diff --git a/.DEREK.yml b/.DEREK.yml index 3fd6789173495..1198da9513bde 100644 --- a/.DEREK.yml +++ b/.DEREK.yml @@ -3,11 +3,14 @@ curators: - alexellis - andrewhsu - anonymuse + - arkodg - chanwit - ehazlett - fntlnz - gianarb + - kolyshkin - mgoelzer + - olljanat - programmerq - rheinwein - ripcurld0 @@ -15,3 +18,5 @@ curators: features: - comments + - pr_description_required + diff --git a/.dockerignore b/.dockerignore index 4a56f2e00c26a..a8eeb4b41f30a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,6 @@ -bundles +.git +.go-pkg-cache .gopath +bundles vendor/pkg -.go-pkg-cache -.git -hack/integration-cli-on-swarm/integration-cli-on-swarm diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 908185496518a..26e94ba4df628 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,17 +4,12 @@ # KEEP THIS FILE SORTED. Order is important. Last match takes precedence. builder/** @tonistiigi -client/** @dnephin contrib/mkimage/** @tianon daemon/graphdriver/devmapper/** @rhvgoyal -daemon/graphdriver/lcow/** @johnstep @jhowardmsft daemon/graphdriver/overlay/** @dmcgowan daemon/graphdriver/overlay2/** @dmcgowan -daemon/graphdriver/windows/** @johnstep @jhowardmsft +daemon/graphdriver/windows/** @johnstep daemon/logger/awslogs/** @samuelkarp hack/** @tianon -hack/integration-cli-on-swarm/** @AkihiroSuda -integration-cli/** @vdemeester -integration/** @vdemeester plugin/** @cpuguy83 project/** @thaJeztah diff --git a/.gitignore b/.gitignore index 392bf963c5e71..1605fa8930fee 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # please consider a global .gitignore https://help.github.com/articles/ignoring-files *.exe *.exe~ +*.gz *.orig test.main .*.swp @@ -16,9 +17,7 @@ autogen/ bundles/ cmd/dockerd/dockerd contrib/builder/rpm/*/changelog -dockerversion/version_autogen.go -dockerversion/version_autogen_unix.go vendor/pkg/ -hack/integration-cli-on-swarm/integration-cli-on-swarm -coverage.txt +go-test-report.json profile.out +junit-report.xml diff --git a/.mailmap b/.mailmap index 55663177c64fc..cc2e8088d3b0b 100644 --- a/.mailmap +++ b/.mailmap @@ -9,7 +9,14 @@ <21551195@zju.edu.cn> Aaron L. Xu -Abhinandan Prativadi +Abhinandan Prativadi +Abhinandan Prativadi +Abhinandan Prativadi abhi +Abhishek Chanda +Abhishek Chanda +Ada Mancini +Adam Dobrawy +Adam Dobrawy Adrien Gallouët Ahmed Kamal Ahmet Alp Balkan @@ -17,12 +24,21 @@ AJ Bowen AJ Bowen AJ Bowen Akihiro Matsushima -Akihiro Suda +Akihiro Suda +Akihiro Suda +Akihiro Suda +Akshay Moghe Aleksa Sarai Aleksa Sarai Aleksa Sarai Aleksandrs Fadins +Alessandro Boch +Alessandro Boch Alessandro Boch +Alessandro Boch +Alessandro Boch +Alex Chan +Alex Chan Alex Chen Alex Ellis Alex Goodman @@ -30,11 +46,17 @@ Alexander Larsson Alexander Morozov Alexander Morozov Alexandre Beslic +Alexis Thomas Alicia Lauerman Allen Sun Allen Sun +Andrea Denisse Gómez +Andrew Kim +Andrew Kim Andrew Weiss Andrew Weiss +Andrey Kolomentsev +Andrey Kolomentsev André Martins Andy Rothfusz Andy Smith @@ -47,28 +69,40 @@ Antonio Murdaca Anuj Bahuguna Anuj Bahuguna Anusha Ragunathan +Arko Dasgupta +Arko Dasgupta Arnaud Porterie Arnaud Porterie +Arnaud Rebillout +Arnaud Rebillout Arthur Gautier Avi Miller Ben Bonnefoy Ben Golub Ben Toews +Benny Ng Benoit Chesneau +Bevisy Zhang Bhiraj Butala Bhumika Bayani Bilal Amarni Bill Wang +Bily Zhang Bin Liu Bin Liu Bingshen Wang Boaz Shuster +Boqin Qin Brandon Philips Brandon Philips Brent Salisbury Brian Goff Brian Goff Brian Goff +Brian Goff +Brian Goff +Cameron Sparr +Carlos de Paula Chander Govindarajan Chao Wang Charles Hooper @@ -77,12 +111,19 @@ Chen Chuanliang Chen Mingjie Chen Qiu Chen Qiu <21321229@zju.edu.cn> +Chengfei Shang Chris Dias Chris McKinnel +Chris Price +Chris Price +Chris Telfer +Chris Telfer Christopher Biscardi Christopher Latham +Christy Norman Chun Chen Corbin Coleman +Cristian Ariza Cristian Staretu Cristian Staretu Cristian Staretu @@ -105,6 +146,7 @@ Daniel Mizyrycki Daniel Norberg Daniel Watkins +Daniel Zhang Danny Yates Darren Shepherd Dattatraya Kumbhar @@ -115,16 +157,27 @@ David M. Karr David Sheets David Sissitka David Williamson +Derek Ch Deshi Xiao Deshi Xiao +Dhilip Kumars Diego Siqueira Diogo Monica +Dmitry Sharshakov +Dmitry Sharshakov +Dmytro Iakovliev +Dominic Yin Dominik Honnef Doug Davis Doug Tangren +Drew Erny +Drew Erny Elan Ruusamäe Elan Ruusamäe Elango Sivanandam +Elango Sivanandam +Eli Uriegas +Eli Uriegas Eric G. Noriega Eric Hanchrow Eric Rosenberg @@ -146,7 +199,9 @@ Feng Yan Fengtu Wang Francisco Carriedo Frank Rosquin +Frank Yang Frederick F. Kautz IV +Fu JinLin Gabriel Nicolas Avellaneda Gaetan de Villele Gang Qiao <1373319223@qq.com> @@ -154,14 +209,19 @@ Geon Kim George Kontridze Gerwim Feiken Giampaolo Mancini +Giovan Isa Musthofa Gopikannan Venugopalsamy Gou Rao +Grant Millar +Grant Millar +Grant Millar Greg Stephens Guillaume J. Charmes Guillaume J. Charmes Guillaume J. Charmes Guillaume J. Charmes Guillaume J. Charmes +Gunadhya S. <6939749+gunadhya@users.noreply.github.com> Guri Gurjeet Singh Gustav Sinder @@ -170,6 +230,7 @@ Hakan Özler Hao Shu Wei Hao Shu Wei Harald Albers +Harald Niesche Harold Cooper Harry Zhang Harry Zhang @@ -177,35 +238,53 @@ Harry Zhang Harry Zhang Harshal Patil Helen Xie +Hiroyuki Sasagawa Hollie Teal Hollie Teal Hollie Teal Hu Keping +Hui Kang +Hui Kang Huu Nguyen Hyzhou Zhy Hyzhou Zhy <1187766782@qq.com> +Ian Campbell +Ian Campbell Ilya Khlopotov +Iskander Sharipov Ivan Markin Jack Laxson Jacob Atzen Jacob Tomlinson Jaivish Kothari +Jake Moshenko +Jakub Drahos +Jakub Drahos +James Nesbitt +James Nesbitt Jamie Hannaford +Jana Radhakrishnan +Jana Radhakrishnan +Jean Rouge Jean-Baptiste Barth Jean-Baptiste Dalido Jean-Tiare Le Bigot Jeff Anderson Jeff Nickoloff Jeroen Franse -Jessica Frazelle -Jessica Frazelle -Jessica Frazelle -Jessica Frazelle -Jessica Frazelle -Jessica Frazelle -Jessica Frazelle -Jessica Frazelle -Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jessica Frazelle +Jian Liao +Jiang Jinyang +Jiang Jinyang Jim Galasyn Jiuyue Ma Joey Geiger @@ -214,19 +293,23 @@ Joffrey F Joffrey F Johan Euphrosine John Harris -John Howard (VM) -John Howard (VM) -John Howard (VM) -John Howard (VM) -John Howard (VM) +John Howard +John Howard <10522484+lowenna@users.noreply.github.com> +John Howard +John Howard +John Howard +John Howard +John Howard John Stephens +Jon Surrell Jonathan Choy Jonathan Choy -Jon Surrell Jordan Arentsen Jordan Jennings Jorit Kleine-Möllhoff -Jose Diaz-Gonzalez +Jose Diaz-Gonzalez +Jose Diaz-Gonzalez +Jose Diaz-Gonzalez Josh Bonczkowski Josh Eveleth Josh Hawn @@ -236,6 +319,7 @@ Josh Wilson Joyce Jang Julien Bordellier Julien Bordellier +Jun Du Justin Cormack Justin Cormack Justin Cormack @@ -249,6 +333,8 @@ Kai Qiang Wu (Kennan) Kai Qiang Wu (Kennan) Kamil Domański Kamjar Gerami +Karthik Nayak +Karthik Nayak Ken Cochrane Ken Herner Ken Reese @@ -263,17 +349,25 @@ Konrad Kleine Konstantin Gribov Konstantin Pelykh Kotaro Yoshimatsu +Kunal Kushwaha +Kunal Kushwaha Kunal Kushwaha +Kyle Squizzato +Kyle Squizzato Lajos Papp +Lei Gong Lei Jitang Lei Jitang +Lei Jitang +Leiiwang Liang Mingqiang Liang-Chi Hsieh Liao Qingwei Linus Heckemann Linus Heckemann Lokesh Mandvekar -Lorenzo Fontana +Lorenzo Fontana +Lorenzo Fontana Louis Opter Louis Opter Luca Favatella @@ -282,8 +376,11 @@ Lyn Lynda O'Leary Lynda O'Leary Ma Müller +Madhan Raj Mookkandy +Madhan Raj Mookkandy Madhan Raj Mookkandy -Madhu Venugopal +Madhu Venugopal +Madhu Venugopal Mageee <21521230.zju.edu.cn> Mansi Nahar Mansi Nahar @@ -296,6 +393,7 @@ Markan Patel Markus Kortlang Martin Redmond Martin Redmond +Maru Newby Mary Anthony Mary Anthony Mary Anthony moxiegirl @@ -309,31 +407,44 @@ Matthew Mosesohn Matthew Mueller Matthias Kühnle Mauricio Garavaglia +Maxwell +Maxwell +Menghui Chen +Michael Beskin Michael Crosby Michael Crosby Michael Crosby -Michał Gryko Michael Hudson-Doyle Michael Huettermann Michael Käufl Michael Nussbaum Michael Nussbaum Michael Spetsiotis +Michael Stapelberg +Michael Stapelberg +Michal Kostrzewa +Michal Kostrzewa Michal Minář +Michał Gryko Michiel de Jong Mickaël Fortunato Miguel Angel Alvarez Cabrerizo <30386061+doncicuto@users.noreply.github.com> Miguel Angel Fernández Mihai Borobocea +Mikael Davranche +Mikael Davranche Mike Casas Mike Goelzer Milind Chawre Misty Stanley-Jones +Mohammad Banikazemi +Mohammad Banikazemi Mohit Soni Moorthy RS Moysés Borges Moysés Borges Nace Oroz +Natasha Jarus Nathan LeClaire Nathan LeClaire Neil Horman @@ -345,6 +456,11 @@ Nolan Darilek O.S. Tezer O.S. Tezer Oh Jinkyun +Oliver Reason +Olli Janatuinen +Olli Janatuinen +Onur Filiz +Onur Filiz Ouyang Liduo Patrick Stapleton Paul Liljenberg @@ -358,22 +474,38 @@ Peter Waller Phil Estes Philip Alexander Etling Philipp Gillé +Prasanna Gautam +Puneet Pruthi +Puneet Pruthi Qiang Huang Qiang Huang +Qin TianHuan Ray Tsang Renaud Gaubert Robert Terhaar Roberto G. Hashioka Roberto Muñoz Fernández +Robin Thoni Roman Dudin +Rong Zhang +Rongxiang Song +Rony Weng Ross Boucher +Rui Cao Runshen Zhu Ryan Stelly +Ryoga Saito +Ryoga Saito +Sainath Grandhi +Sainath Grandhi Sakeven Jiang Sandeep Bansal Sandeep Bansal +Santhosh Manohar Sargun Dhillon Sean Lee +Sebastiaan van Stijn +Sebastiaan van Stijn Sebastiaan van Stijn Sebastiaan van Stijn Shaun Kaasten @@ -382,11 +514,14 @@ Shengbo Song Shengbo Song Shih-Yuan Lee Shishir Mahajan +Shu-Wai Chow Shukui Yang Shuwei Hao Shuwei Hao Sidhartha Mani Sjoerd Langkemper +Smark Meng +Smark Meng Solomon Hykes Solomon Hykes Solomon Hykes @@ -400,9 +535,12 @@ Stefan Berger Stefan Berger Stefan J. Wernli Stefan S. +Stefan Scherer +Stefan Scherer Stephan Spindler -Stephen Day -Stephen Day +Stephen Day +Stephen Day +Stephen Day Steve Desmond Sun Gengze <690388648@qq.com> Sun Jianbo @@ -418,24 +556,40 @@ Sylvain Bellemare Sylvain Bellemare Tangi Colin Tejesh Mehta +Terry Chu +Terry Chu Thatcher Peskens Thatcher Peskens Thatcher Peskens +Thiago Alves Silva +Thiago Alves Silva Thomas Gazagnaire Thomas Léveil Thomas Léveil Tibor Vass Tibor Vass +Till Claassen Tim Bart Tim Bosse +Tim Potter +Tim Potter Tim Ruffles Tim Terhorst +Tim Wagner +Tim Wagner <33624860+herrwagner@users.noreply.github.com> Tim Zju <21651152@zju.edu.cn> Timothy Hobbs Toli Kuznets Tom Barlow +Tom Denham +Tom Denham Tom Sweeney +Tom Wilkie +Tom Wilkie Tõnis Tiigi +Trace Andreason +Trapier Marshall +Trapier Marshall Trishna Guha Tristan Carel Tristan Carel @@ -449,15 +603,23 @@ Victor Vieux Victor Vieux Victor Vieux Victor Vieux +Vikas Choudhary +Vikram bir Singh +Vikram bir Singh Viktor Vojnovski Vincent Batts Vincent Bernat Vincent Bernat +Vincent Boulineau Vincent Demeester Vincent Demeester Vincent Demeester Vishnu Kannan +Vitaly Ostrosablin +Vitaly Ostrosablin Vladimir Rutsky +Vladislav Kolesnikov +Vladislav Kolesnikov Walter Stanish Wang Chao Wang Chao @@ -469,11 +631,23 @@ Wang Yuexiao Wayne Chang Wayne Song Wei Wu cizixs +Wen Cheng Ma Wenjun Tang Wewang Xiaorenfine Will Weaver +Wing-Kam Wong +WuLonghui +Xian Chaobo +Xian Chaobo Xianglin Gao +Xianjie +Xianjie Xianlu Bird +Xiao YongBiao +Xiao Zhang +Xiaodong Liu +Xiaodong Zhang +Xiaohua Ding Xiaoyu Zhang Xuecong Liao Yamasaki Masahide @@ -485,15 +659,25 @@ Yi EungJun Ying Li Ying Li Yong Tang +Yongxin Li Yosef Fertel Yu Changchun Yu Chengxia Yu Peng Yu Peng +Yue Zhang +Zach Gershman +Zach Gershman Zachary Jaffee Zachary Jaffee ZhangHang Zhenkun Bi Zhou Hao +Zhoulin Xie Zhu Kunjia +Ziheng Liu Zou Yu +Zuhayr Elahi +Zuhayr Elahi +정재영 +정재영 <43400316+J-jaeyoung@users.noreply.github.com> diff --git a/AUTHORS b/AUTHORS index c6c8fb40e3989..2ae76d2c2c9c5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,6 +4,7 @@ Aanand Prasad Aaron Davidson Aaron Feng +Aaron Hnatiw Aaron Huslage Aaron L. Xu Aaron Lehmann @@ -11,12 +12,14 @@ Aaron Welch Aaron.L.Xu Abel Muiño Abhijeet Kasurde -Abhinandan Prativadi +Abhinandan Prativadi Abhinav Ajgaonkar Abhishek Chanda Abhishek Sharma Abin Shahab +Ada Mancini Adam Avilla +Adam Dobrawy Adam Eijdenberg Adam Kunk Adam Miller @@ -24,6 +27,7 @@ Adam Mills Adam Pointer Adam Singer Adam Walz +Adam Williams Addam Hardy Aditi Rajagopal Aditya @@ -43,17 +47,22 @@ AJ Bowen Ajey Charantimath ajneu Akash Gupta +Akhil Mohan Akihiro Matsushima -Akihiro Suda +Akihiro Suda Akim Demaille Akira Koyasu Akshay Karle +Akshay Moghe Al Tobey alambike +Alan Hoyle Alan Scherger Alan Thompson Albert Callarisa Albert Zhang +Albin Kerouanton +Alec Benson Alejandro González Hevia Aleksa Sarai Aleksandrs Fadins @@ -75,16 +84,19 @@ Alexander Boyd Alexander Larsson Alexander Midlash Alexander Morozov +Alexander Polakov Alexander Shopov Alexandre Beslic Alexandre Garnier Alexandre González Alexandre Jomin Alexandru Sfirlogea +Alexei Margasov Alexey Guskov Alexey Kotlyarov Alexey Shamrin -Alexis THOMAS +Alexis Ries +Alexis Thomas Alfred Landrum Ali Dehghani Alicia Lauerman @@ -97,6 +109,7 @@ Alvin Deng Alvin Richards amangoel Amen Belayneh +Ameya Gawde Amir Goldstein Amit Bakshi Amit Krishnan @@ -106,11 +119,13 @@ Amy Lindburg Anand Patil AnandkumarPatel Anatoly Borodin +Anca Iordache Anchal Agrawal Anda Xu Anders Janmyr Andre Dublin <81dublin@gmail.com> Andre Granovsky +Andrea Denisse Gómez Andrea Luzzardi Andrea Turli Andreas Elvers @@ -118,6 +133,8 @@ Andreas Köhler Andreas Savvides Andreas Tiefenthaler Andrei Gherzan +Andrei Ushakov +Andrei Vagin Andrew C. Bodine Andrew Clay Shafer Andrew Duckworth @@ -126,6 +143,7 @@ Andrew Gerrand Andrew Guenther Andrew He Andrew Hsu +Andrew Kim Andrew Kuklewicz Andrew Macgregor Andrew Macpherson @@ -137,6 +155,7 @@ Andrew Po Andrew Weiss Andrew Williams Andrews Medina +Andrey Kolomentsev Andrey Petrov Andrey Stolbovsky André Martins @@ -145,12 +164,15 @@ Andy Chambers andy diller Andy Goldstein Andy Kipp +Andy Lindeman Andy Rothfusz Andy Smith Andy Wilson Anes Hasicic +Angel Velazquez Anil Belur Anil Madhavapeddy +Ankit Jain Ankush Agarwal Anonmily Anran Qiao @@ -168,12 +190,15 @@ Antonio Murdaca Antonis Kalipetis Antony Messerli Anuj Bahuguna +Anuj Varma Anusha Ragunathan apocas Arash Deshmeh ArikaChen +Arko Dasgupta Arnaud Lefebvre Arnaud Porterie +Arnaud Rebillout Arthur Barr Arthur Gautier Artur Meyster @@ -182,19 +207,23 @@ Asad Saeeduddin Asbjørn Enge averagehuman Avi Das +Avi Kivity Avi Miller Avi Vaid ayoshitake Azat Khuyiyakhmetov +Bao Yonglei Bardia Keyoumarsi Barnaby Gray Barry Allard Bartłomiej Piotrowski Bastiaan Bakker bdevloed +Bearice Ren Ben Bonnefoy Ben Firshman Ben Golub +Ben Gould Ben Hall Ben Sargent Ben Severson @@ -203,21 +232,28 @@ Ben Wiklund Benjamin Atkin Benjamin Baker Benjamin Boudreau +Benjamin Böhmke Benjamin Yolken +Benny Ng Benoit Chesneau Bernerd Schaefer Bernhard M. Wiedemann Bert Goethals +Bertrand Roussel +Bevisy Zhang Bharath Thiruveedula Bhiraj Butala Bhumika Bayani Bilal Amarni Bill Wang +Billy Ridgway +Bily Zhang Bin Liu Bingshen Wang Blake Geno Boaz Shuster bobby abbott +Boqin Qin Boris Pruessmann Boshi Lian Bouke Haarsma @@ -232,6 +268,7 @@ Brendan Dixon Brent Salisbury Brett Higgins Brett Kochendorfer +Brett Milford Brett Randall Brian (bex) Exelbierd Brian Bland @@ -262,6 +299,7 @@ Byung Kang Caleb Spare Calen Pennington Cameron Boehmer +Cameron Sparr Cameron Spear Campbell Allen Candid Dauth @@ -271,6 +309,7 @@ Carl Loa Odin Carl X. Su Carlo Mion Carlos Alexandro Becker +Carlos de Paula Carlos Sanchez Carol Fager-Higgins Cary @@ -302,6 +341,7 @@ Chen Min Chen Mingjie Chen Qiu Cheng-mean Liu +Chengfei Shang Chengguang Xu chenyuzhu Chetan Birajdar @@ -317,8 +357,10 @@ Chris Fordham Chris Gavin Chris Gibson Chris Khoo +Chris Kreussling (Flatbush Gardener) Chris McKinnel Chris McKinnel +Chris Price Chris Seto Chris Snow Chris St. Pierre @@ -328,6 +370,7 @@ Chris Telfer Chris Wahl Chris Weyl Chris White +Christian Becker Christian Berendt Christian Brauner Christian Böhme @@ -336,6 +379,7 @@ Christian Persson Christian Rotzoll Christian Simon Christian Stefanescu +Christoph Ziebuhr Christophe Mehay Christophe Troestler Christophe Vidal @@ -345,15 +389,18 @@ Christopher Currie Christopher Jones Christopher Latham Christopher Rigor -Christy Perez +Christy Norman Chun Chen Ciro S. Costa Clayton Coleman +Clint Armstrong Clinton Kitson +clubby789 Cody Roseborough Coenraad Loubser Colin Dunklau Colin Hebert +Colin Panisset Colin Rice Colin Walters Collin Guarino @@ -364,10 +411,13 @@ Corey Farrell Cory Forsyth cressie176 CrimsonGlory +Cristian Ariza Cristian Staretu cristiano balducci +Cristina Yenyxe Gonzalez Garcia Cruceru Calin-Cristian CUI Wei +Cuong Manh Le Cyprian Gracz Cyril F Daan van Berkel @@ -389,14 +439,17 @@ Dan Levy Dan McPherson Dan Stine Dan Williams +Dani Hodovic Dani Louca Daniel Antlinger +Daniel Black Daniel Dao Daniel Exner Daniel Farrell Daniel Garcia Daniel Gasienica Daniel Grunwell +Daniel Helfand Daniel Hiltgen Daniel J Walsh Daniel Menet @@ -406,12 +459,14 @@ Daniel Norberg Daniel Nordberg Daniel Robinson Daniel S +Daniel Sweet Daniel Von Fange Daniel Watkins Daniel X Moore Daniel YC Lin Daniel Zhang Danny Berger +Danny Milosavljevic Danny Yates Danyal Khaliq Darren Coxall @@ -425,6 +480,7 @@ Dave Henderson Dave MacDonald Dave Tucker David Anderson +David Bellotti David Calavera David Chung David Corking @@ -439,9 +495,12 @@ David Lawrence David Lechner David M. Karr David Mackey +David Manouchehri David Mat David Mcanulty David McKay +David O'Rourke +David P Hilton David Pelaez David R. Jenni David Röthlisberger @@ -459,6 +518,7 @@ dcylabs Debayan De Deborah Gertrude Digges deed02392 +Deep Debroy Deng Guangxing Deni Bertovic Denis Defreyne @@ -474,15 +534,18 @@ Derek McGowan Deric Crago Deshi Xiao devmeyster +Devon Estes Devvyn Murphy Dharmit Shah Dhawal Yogesh Bhanushali +Dhilip Kumars Diego Romero Diego Siqueira Dieter Reuter Dillon Dixon Dima Stopel Dimitri John Ledkov +Dimitris Mandalidis Dimitris Rozakis Dimitry Andric Dinesh Subhraveti @@ -496,11 +559,16 @@ Dmitri Shuralyov Dmitry Demeshchuk Dmitry Gusev Dmitry Kononenko +Dmitry Sharshakov Dmitry Shyshkin Dmitry Smirnov Dmitry V. Krivenok Dmitry Vorobev +Dmytro Iakovliev +docker-unir[bot] Dolph Mathews +Dominic Tubach +Dominic Yin Dominik Dingel Dominik Finkbeiner Dominik Honnef @@ -519,7 +587,7 @@ Douglas Curtis Dr Nic Williams dragon788 Dražen Lučanin -Drew Erny +Drew Erny Drew Hubl Dustin Sallings Ed Costello @@ -531,7 +599,7 @@ Eivind Uggedal Elan Ruusamäe Elango Sivanandam Elena Morozova -Eli Uriegas +Eli Uriegas Elias Faxö Elias Probst Elijah Zupancic @@ -550,6 +618,7 @@ Eric Curtin Eric G. Noriega Eric Hanchrow Eric Lee +Eric Mountain Eric Myhre Eric Paris Eric Rafaloff @@ -559,6 +628,7 @@ Eric Soderstrom Eric Yang Eric-Olivier Lamey Erica Windisch +Erich Cordoba Erik Bray Erik Dubbelboer Erik Hollensbe @@ -568,7 +638,10 @@ Erik St. Martin Erik Weathers Erno Hopearuoho Erwin van der Koogh +Espen Suenson Ethan Bell +Ethan Mosbaugh +Euan Harris Euan Kemp Eugen Krizo Eugene Yakubovich @@ -580,12 +653,14 @@ Evan Phoenix Evan Wies Evelyn Xu Everett Toews +Evgeniy Makhrov Evgeny Shmarnev Evgeny Vereshchagin Ewa Czechowska Eystein Måløy Stenberg ezbercih Ezra Silvera +Fabian Kramm Fabian Lauer Fabian Raetz Fabiano Rosas @@ -604,6 +679,7 @@ Fareed Dudhia Fathi Boudra Federico Gimenez Felipe Oliveira +Felipe Ruhland Felix Abecassis Felix Geisendörfer Felix Hupfeld @@ -624,12 +700,14 @@ Florian Florian Klein Florian Maier Florian Noeding +Florian Schmaus Florian Weingarten Florin Asavoaie Florin Patan fonglh Foysal Iqbal Francesc Campoy +Francesco Degrassi Francesco Mari Francis Chuang Francisco Carriedo @@ -638,16 +716,20 @@ Frank Groeneveld Frank Herrmann Frank Macreery Frank Rosquin +Frank Yang Fred Lifton Frederick F. Kautz IV +Frederico F. de Oliveira Frederik Loeffert Frederik Nordahl Jul Sabroe Freek Kalter Frieder Bluemle +Fu JinLin Félix Baylac-Jacqué Félix Cantournet Gabe Rosenhouse Gabor Nagy +Gabriel L. Somlo Gabriel Linder Gabriel Monroy Gabriel Nicolas Avellaneda @@ -658,10 +740,11 @@ Gareth Rushgrove Garrett Barboza Gary Schaetz Gaurav -gautam, prasanna +Gaurav Singh Gaël PORTAY Genki Takiuchi GennadySpb +Geoff Levand Geoffrey Bachelet Geon Kim George Kontridze @@ -676,6 +759,7 @@ Ghislain Bourgeois Giampaolo Mancini Gianluca Borello Gildas Cuisinier +Giovan Isa Musthofa gissehel Giuseppe Mazzotta Gleb Fotengauer-Malinovskiy @@ -683,11 +767,12 @@ Gleb M Borisov Glyn Normington GoBella Goffert van Gool +Goldwyn Rodrigues Gopikannan Venugopalsamy Gosuke Miyashita Gou Rao Govinda Fichtner -Grant Millar +Grant Millar Grant Reaber Graydon Hoare Greg Fausak @@ -699,6 +784,7 @@ Guilhem Lettron Guilherme Salgado Guillaume Dufour Guillaume J. Charmes +Gunadhya S. <6939749+gunadhya@users.noreply.github.com> guoxiuyan Guri Gurjeet Singh @@ -706,15 +792,20 @@ Guruprasad Gustav Sinder gwx296173 Günter Zöchbauer +Haichao Yang haikuoliu Hakan Özler +Hamish Hutchings +Hannes Ljungberg Hans Kristian Flaatten Hans Rødtang Hao Shu Wei Hao Zhang <21521210@zju.edu.cn> Harald Albers +Harald Niesche Harley Laue Harold Cooper +Harrison Turton Harry Zhang Harshal Patil Harshal Patil @@ -726,17 +817,24 @@ Hector Castro Helen Xie Henning Sprang Hiroshi Hatake +Hiroyuki Sasagawa Hobofan Hollie Teal Hong Xu Hongbin Lu +Hongxu Jia +Honza Pokorny +Hsing-Hui Hsu hsinko <21551195@zju.edu.cn> Hu Keping Hu Tao +HuanHuan Ye Huanzhong Zhang Huayi Zhang +Hugo Barrera Hugo Duncan Hugo Marisco <0x6875676f@gmail.com> +Hui Kang Hunter Blanks huqun Huu Nguyen @@ -766,8 +864,10 @@ Ilya Khlopotov imre Fitos inglesp Ingo Gottwald +Innovimax Isaac Dupree Isabel Jimenez +Isaiah Grace Isao Jonas Iskander Sharipov Ivan Babrou @@ -783,11 +883,12 @@ Jacob Edelman Jacob Tomlinson Jacob Vallejo Jacob Wen +Jaime Cepeda Jaivish Kothari Jake Champlin Jake Moshenko Jake Sanders -jakedt +Jakub Drahos James Allen James Carey James Carr @@ -797,11 +898,13 @@ James Kyburz James Kyle James Lal James Mills -James Nesbitt +James Nesbitt James Nugent James Turnbull +James Watkins-Harvey Jamie Hannaford Jamshid Afshar +Jan Chren Jan Keromnes Jan Koprowski Jan Pazdziora @@ -816,6 +919,7 @@ Jared Hocutt Jaroslaw Zabiello jaseg Jasmine Hegman +Jason A. Donenfeld Jason Divock Jason Giedymin Jason Green @@ -832,6 +936,7 @@ jaxgeller Jay Jay Jay Kamat +Jean Rouge Jean-Baptiste Barth Jean-Baptiste Dalido Jean-Christophe Berthon @@ -848,12 +953,14 @@ Jeff Minard Jeff Nickoloff Jeff Silberman Jeff Welch +Jeff Zvier Jeffrey Bolle Jeffrey Morgan Jeffrey van Gogh Jenny Gebske Jeremy Chambers Jeremy Grosser +Jeremy Huntwork Jeremy Price Jeremy Qian Jeremy Unruh @@ -862,29 +969,37 @@ Jeroen Franse Jeroen Jacobs Jesse Dearing Jesse Dubay -Jessica Frazelle +Jessica Frazelle Jezeniel Zapanta Jhon Honce Ji.Zhilong +Jian Liao Jian Zhang +Jiang Jinyang +Jianyong Wu Jie Luo +Jie Ma Jihyun Hwang Jilles Oldenbeuving Jim Alateras +Jim Carroll +Jim Ehrismann Jim Galasyn +Jim Lin Jim Minter Jim Perrin Jimmy Cuadra Jimmy Puckett Jimmy Song -jimmyxian Jinsoo Park +Jintao Zhang +Jiri Appl Jiri Popelka Jiuyue Ma Jiří Župka -jjy -jmzwcn +Joakim Roubert Joao Fernandes +Joao Trindade Joe Beda Joe Doliner Joe Ferguson @@ -908,7 +1023,7 @@ John Feminella John Gardiner Myers John Gossman John Harris -John Howard (VM) +John Howard John Laswell John Maguire John Mulhausen @@ -922,7 +1037,10 @@ John Willis Jon Johnson Jon Surrell Jon Wedaman +Jonas Dohse +Jonas Heinrich Jonas Pfenniger +Jonathan A. Schweder Jonathan A. Sternberg Jonathan Boulle Jonathan Camp @@ -941,9 +1059,10 @@ Joost Cassee Jordan Arentsen Jordan Jennings Jordan Sissel +Jordi Massaguer Pla Jorge Marin Jorit Kleine-Möllhoff -Jose Diaz-Gonzalez +Jose Diaz-Gonzalez Joseph Anthony Pasquale Holsten Joseph Hager Joseph Kern @@ -970,10 +1089,15 @@ Julien Dubois Julien Kassar Julien Maitrehenry Julien Pervillé +Julien Pivotto +Julio Guerra Julio Montes +Jun Du Jun-Ru Chang +junxu Jussi Nummelin Justas Brazauskas +Justen Martin Justin Cormack Justin Force Justin Menga @@ -982,11 +1106,13 @@ Justin Simonelis Justin Terry Justyn Temme Jyrki Puttonen +Jérémy Leherpeur Jérôme Petazzoni Jörg Thalheim K. Heller Kai Blin Kai Qiang Wu (Kennan) +Kaijie Chen Kamil Domański Kamjar Gerami Kanstantsin Shautsou @@ -997,7 +1123,7 @@ kargakis Karl Grzeszczak Karol Duleba Karthik Karanth -Karthik Nayak +Karthik Nayak Kasper Fabæch Brandt Kate Heddleston Katie McLaughlin @@ -1007,6 +1133,7 @@ Kawsar Saiyeed Kay Yan kayrus Kazuhiro Sera +Kazuyoshi Kato Ke Li Ke Xu Kei Ohmura @@ -1019,6 +1146,7 @@ Ken Reese Kenfe-Mickaël Laventure Kenjiro Nakayama Kent Johnson +Kenta Tada Kevin "qwazerty" Houdebert Kevin Burke Kevin Clark @@ -1029,6 +1157,7 @@ Kevin Kern Kevin Menard Kevin Meredith Kevin P. Kucharczyk +Kevin Parsons Kevin Richardson Kevin Shi Kevin Wallace @@ -1048,16 +1177,20 @@ Konrad Kleine Konstantin Gribov Konstantin L Konstantin Pelykh +Kostadin Plachkov Krasi Georgiev Krasimir Georgiev Kris-Mikael Krister Kristian Haugene Kristina Zabunova +Krystian Wojcicki Kun Zhang Kunal Kushwaha Kunal Tyagi Kyle Conroy Kyle Linden +Kyle Squizzato +Kyle Wuolle kyu Lachlan Coote Lai Jiangshan @@ -1072,20 +1205,26 @@ Lars R. Damerow Lars-Magnus Skog Laszlo Meszaros Laura Frank +Laurent Bernaille Laurent Erignoux Laurie Voss Leandro Siqueira +Lee Calcote Lee Chao <932819864@qq.com> Lee, Meng-Han leeplay Lei Gong Lei Jitang +Leiiwang Len Weincier Lennie Leo Gallucci +Leonardo Nodari +Leonardo Taccari Leszek Kowalski Levi Blackstone Levi Gross +Levi Harrison Lewis Daly Lewis Marshall Lewis Peckover @@ -1094,8 +1233,12 @@ Liam Macgillavry Liana Lo Liang Mingqiang Liang-Chi Hsieh +liangwei Liao Qingwei +Lifubang +Lihua Tang Lily Guo +limeidan limsy Lin Lu LingFaKe @@ -1113,8 +1256,9 @@ Lloyd Dewolf Lokesh Mandvekar longliqiang88 <394564827@qq.com> Lorenz Leutgeb -Lorenzo Fontana +Lorenzo Fontana Lotus Fenn +Louis Delossantos Louis Opter Luca Favatella Luca Marturana @@ -1123,9 +1267,11 @@ Luca-Bogdan Grigorescu Lucas Chan Lucas Chi Lucas Molas +Lucas Silvestre Luciano Mores Luis Martínez de Bartolomé Izquierdo Luiz Svoboda +Lukas Heeren Lukas Waslowski lukaspustina Lukasz Zajaczkowski @@ -1138,7 +1284,7 @@ Ma Shimiao Mabin Madhan Raj Mookkandy Madhav Puri -Madhu Venugopal +Madhu Venugopal Mageee Mahesh Tiyyagura malnick @@ -1171,11 +1317,13 @@ Marius Gundersen Marius Sturm Marius Voila Mark Allen +Mark Jeromin Mark McGranaghan Mark McKinstry Mark Milstein Mark Oates Mark Parker +Mark Vainomaa Mark West Markan Patel Marko Mikulicic @@ -1189,6 +1337,7 @@ Martin Kelly Martin Mosegaard Amdisen Martin Muzatko Martin Redmond +Maru Newby Mary Anthony Masahito Zembutsu Masato Ohba @@ -1201,6 +1350,7 @@ Mathieu Le Marec - Pasquet Mathieu Parent Matt Apperson Matt Bachmann +Matt Bajor Matt Bentley Matt Haggard Matt Hoyle @@ -1220,25 +1370,33 @@ Matthew Riley Matthias Klumpp Matthias Kühnle Matthias Rampke +Matthieu Fronton Matthieu Hauglustaine +Mattias Jernberg Mauricio Garavaglia mauriyouth +Max Harmathy Max Shytikov +Max Timchenko Maxim Fedchyshyn Maxim Ivanov Maxim Kulkin Maxim Treskin Maxime Petazzoni +Maximiliano Maccanti +Maxwell Meaglith Ma meejah Megan Kostick Mehul Kar Mei ChunTao Mengdi Gao +Menghui Chen Mert Yazıcıoğlu mgniu Micah Zoltu Michael A. Smith +Michael Beskin Michael Bridgen Michael Brown Michael Chiang @@ -1261,22 +1419,29 @@ Michael Stapelberg Michael Steinert Michael Thies Michael West +Michael Zhao Michal Fojtik Michal Gebauer Michal Jemala +Michal Kostrzewa Michal Minář +Michal Rostecki Michal Wieczorek Michaël Pailloncy Michał Czeraszkiewicz Michał Gryko +Michał Kosek Michiel de Jong Mickaël Fortunato Mickaël Remars Miguel Angel Fernández Miguel Morales +Miguel Perez Mihai Borobocea Mihuleacc Sergiu +Mikael Davranche Mike Brown +Mike Bush Mike Casas Mike Chelen Mike Danese @@ -1301,7 +1466,8 @@ Misty Stanley-Jones Mitch Capper Mizuki Urushida mlarcher -Mohammad Banikazemi +Mohammad Banikazemi +Mohammad Nasirifar Mohammed Aaqib Ansari Mohit Soni Moorthy RS @@ -1314,6 +1480,7 @@ Moysés Borges mrfly Mrunal Patel Muayyad Alsadi +Muhammad Zohaib Aslam Mustafa Akın Muthukumar R Máximo Cuadros @@ -1326,9 +1493,12 @@ Nan Monnand Deng Naoki Orii Natalie Parker Natanael Copa +Natasha Jarus Nate Brennand Nate Eagleson Nate Jones +Nathan Carlson +Nathan Herald Nathan Hsieh Nathan Kleyn Nathan LeClaire @@ -1343,6 +1513,7 @@ Neyazul Haque Nghia Tran Niall O'Higgins Nicholas E. Rabenau +Nick Adcock Nick DeCoursin Nick Irvine Nick Neisen @@ -1351,6 +1522,7 @@ Nick Payne Nick Russo Nick Stenning Nick Stinemates +Nick Wood NickrenREN Nicola Kabar Nicolas Borboën @@ -1366,6 +1538,7 @@ Nik Nyby Nikhil Chawla NikolaMandic Nikolas Garofil +Nikolay Edigaryev Nikolay Milovanov Nirmal Mehta Nishant Totla @@ -1375,20 +1548,29 @@ Noah Treuhaft NobodyOnSE noducks Nolan Darilek +Noriki Nakamura nponeccop +Nurahmadie Nuutti Kotivuori nzwsch O.S. Tezer objectified +Odin Ugedal Oguz Bilgic Oh Jinkyun Ohad Schneider ohmystack Ole Reifschneider Oliver Neal +Oliver Reason Olivier Gambier Olle Jonsson +Olli Janatuinen +Olly Pomeroy +Omri Shiv +Onur Filiz Oriol Francès +Oscar Bonilla <6f6231@gmail.com> Oskar Niburski Otto Kekäläinen Ouyang Liduo @@ -1396,14 +1578,17 @@ Ovidio Mallo Panagiotis Moustafellos Paolo G. Giarrusso Pascal +Pascal Bach Pascal Borreli Pascal Hartig Patrick Böänziger Patrick Devine +Patrick Haas Patrick Hemmer Patrick Stapleton Patrik Cyvoct pattichen +Paul "TBBle" Hampson Paul paul Paul Annesley @@ -1418,8 +1603,10 @@ Paul Liljenberg Paul Morie Paul Nasrat Paul Weaver +Paulo Gomes Paulo Ribeiro Pavel Lobashov +Pavel Matěja Pavel Pletenev Pavel Pospisil Pavel Sutyrin @@ -1443,11 +1630,13 @@ Peter Edge Peter Ericson Peter Esbensen Peter Jaffe +Peter Kang Peter Malmgren Peter Salvatore Peter Volpe Peter Waller Petr Švihlík +Petros Angelatos Phil Phil Estes Phil Spitler @@ -1466,26 +1655,31 @@ Pierre Dal-Pra Pierre Wacrenier Pierre-Alain RIVIERE Piotr Bogdan -pixelistik +Piotr Karbowski Porjo Poul Kjeldager Sørensen Pradeep Chhetri Pradip Dhara +Pradipta Kr. Banerjee Prasanna Gautam Pratik Karki Prayag Verma Priya Wadhwa Projjol Banerji Przemek Hejman +Puneet Pruthi Pure White pysqz Qiang Huang +Qin TianHuan Qinglan Peng +Quan Tian qudongfang Quentin Brossard Quentin Perez Quentin Tayssier r0n22 +Radostin Stoyanov Rafal Jeczalik Rafe Colton Raghavendra K T @@ -1499,8 +1693,10 @@ Ralph Bean Ramkumar Ramachandra Ramon Brooker Ramon van Alteren +RaviTeja Pothana Ray Tsang ReadmeCritic +realityone Recursive Madman Reficul Regan McCooey @@ -1528,6 +1724,8 @@ Riku Voipio Riley Guerin Ritesh H Shukla Riyaz Faizullabhoy +Rob Cowsill <42620235+rcowsill@users.noreply.github.com> +Rob Gulewich Rob Vesse Robert Bachmann Robert Bittle @@ -1536,11 +1734,13 @@ Robert Schneider Robert Stern Robert Terhaar Robert Wallis +Robert Wang Roberto G. Hashioka Roberto Muñoz Fernández Robin Naundorf Robin Schneider Robin Speekenbrink +Robin Thoni robpc Rodolfo Carvalho Rodrigo Vaz @@ -1548,15 +1748,21 @@ Roel Van Nyen Roger Peppe Rohit Jnagal Rohit Kadam +Rohit Kapur Rojin George Roland Huß Roland Kammerer Roland Moriz Roma Sokolov Roman Dudin +Roman Mazur Roman Strashkin Ron Smits Ron Williams +Rong Gao +Rong Zhang +Rongxiang Song +Rony Weng root root root @@ -1568,13 +1774,16 @@ Rovanion Luckey Royce Remer Rozhnov Alexandr Rudolph Gottesheim +Rui Cao Rui Lopes +Ruilin Li Runshen Zhu Russ Magee Ryan Abrams Ryan Anderson Ryan Aslett Ryan Belgrave +Ryan Campbell Ryan Detzel Ryan Fowler Ryan Liu @@ -1589,6 +1798,8 @@ Ryan Wallner Ryan Zhang ryancooper7 RyanDeng +Ryo Nakao +Ryoga Saito Rémy Greinhofer s. rannou s00318865 @@ -1606,6 +1817,7 @@ Sam J Sharpe Sam Neirinck Sam Reis Sam Rijs +Sam Whited Sambuddha Basu Sami Wagiaalla Samuel Andaya @@ -1619,12 +1831,15 @@ Santhosh Manohar sapphiredev Sargun Dhillon Sascha Andres +Sascha Grunert +SataQiu Satnam Singh Satoshi Amemiya Satoshi Tagomori Scott Bessler Scott Collier Scott Johnston +Scott Percival Scott Stamp Scott Walls sdreyesg @@ -1637,6 +1852,8 @@ Sean P. Kane Sean Rodman Sebastiaan van Steenis Sebastiaan van Stijn +Sebastian Radloff +Sebastien Goasguen Senthil Kumar Selvaraj Senthil Kumaran SeongJae Park @@ -1645,6 +1862,7 @@ Serge Hallyn Sergey Alekseev Sergey Evstifeev Sergii Kabashniuk +Sergio Lopez Serhat Gülçiçek SeungUkLee Sevki Hasirci @@ -1655,10 +1873,12 @@ shaunol Shawn Landden Shawn Siefkas shawnhe +Shayan Pooya Shayne Wang Shekhar Gulati Sheng Yang Shengbo Song +Shengjing Zhu Shev Yan Shih-Yuan Lee Shijiang Wei @@ -1666,15 +1886,19 @@ Shijun Qin Shishir Mahajan Shoubhik Bose Shourya Sarcar +Shu-Wai Chow shuai-z Shukui Yang Shuwei Hao Sian Lerk Lau +Siarhei Rasiukevich Sidhartha Mani sidharthamani Silas Sewell Silvan Jegen +Simão Reis Simei He +Simon Barendse Simon Eskildsen Simon Ferquel Simon Leinen @@ -1683,29 +1907,36 @@ Simon Taranto Simon Vikstrom Sindhu S Sjoerd Langkemper +skanehira +Smark Meng Solganik Alexander Solomon Hykes Song Gao Soshi Katsuta +Sotiris Salloumis Soulou Spencer Brown Spencer Smith +Spike Curtis Sridatta Thatipamala Sridhar Ratnakumar Srini Brahmaroutu Srinivasan Srivatsan +Staf Wagemakers Stanislav Bondarenko +Stanislav Levin Steeve Morin Stefan Berger Stefan J. Wernli Stefan Praszalowicz Stefan S. -Stefan Scherer +Stefan Scherer Stefan Staudenmeyer Stefan Weil Stephan Spindler +Stephen Benjamin Stephen Crosby -Stephen Day +Stephen Day Stephen Drake Stephen Rust Steve Desmond @@ -1720,10 +1951,13 @@ Steven Iveson Steven Merrill Steven Richards Steven Taylor +Stig Larsson +Su Wang Subhajit Ghosh Sujith Haridasan Sun Gengze <690388648@qq.com> Sun Jianbo +Sune Keller Sunny Gogoi Suryakumar Sudar Sven Dowideit @@ -1747,16 +1981,19 @@ Ted M. Young Tehmasp Chaudhri Tejaswini Duggaraju Tejesh Mehta +Terry Chu terryding77 <550147740@qq.com> tgic Thatcher Peskens theadactyl Thell 'Bo' Fowler Thermionix +Thiago Alves Silva Thijs Terlouw Thomas Bikeev Thomas Frössman Thomas Gazagnaire +Thomas Graf Thomas Grainger Thomas Hansen Thomas Leonard @@ -1774,6 +2011,9 @@ Tianyi Wang Tibor Vass Tiffany Jernigan Tiffany Low +Till Claassen +Till Wegmüller +Tim Tim Bart Tim Bosse Tim Dettrick @@ -1783,11 +2023,14 @@ Tim Potter Tim Ruffles Tim Smith Tim Terhorst +Tim Wagner Tim Wang Tim Waugh Tim Wraight Tim Zju <21651152@zju.edu.cn> +timchenxiaoyu <837829664@qq.com> timfeirg +Timo Rothenpieler Timothy Hobbs tjwebb123 tobe @@ -1796,6 +2039,7 @@ Tobias Bradtke Tobias Gesellchen Tobias Klauser Tobias Munk +Tobias Pfandzelter Tobias Schmidt Tobias Schwab Todd Crane @@ -1809,14 +2053,19 @@ Tom Fotherby Tom Howe Tom Hulihan Tom Maaswinkel +Tom Parker Tom Sweeney Tom Wilkie Tom X. Tobin +Tom Zhao +Tomas Janousek +Tomas Kral Tomas Tomecek Tomasz Kopczynski Tomasz Lipinski Tomasz Nurkiewicz Tommaso Visconti +Tomoya Tabuchi Tomáš Hrčka Tonny Xu Tony Abboud @@ -1824,10 +2073,11 @@ Tony Daws Tony Miller toogley Torstein Husebø +Toshiaki Makita Tõnis Tiigi -tpng +Trace Andreason tracylihui <793912329@qq.com> -Trapier Marshall +Trapier Marshall Travis Cline Travis Thieman Trent Ogren @@ -1837,6 +2087,7 @@ Trevor Sullivan Trishna Guha Tristan Carel Troy Denton +Ty Alexander Tycho Andersen Tyler Brock Tyler Brown @@ -1847,7 +2098,9 @@ Umesh Yadav Utz Bacher vagrant Vaidas Jablonskis +Valentin Kulesh vanderliang +Velko Ivanov Veres Lajos Victor Algaze Victor Coisne @@ -1859,11 +2112,14 @@ Victor Palma Victor Vieux Victoria Bialas Vijaya Kumar K +Vikas Choudhary +Vikram bir Singh Viktor Stanchev Viktor Vojnovski VinayRaghavanKS Vincent Batts Vincent Bernat +Vincent Boulineau Vincent Demeester Vincent Giersch Vincent Mayers @@ -1882,6 +2138,7 @@ Vladimir Pouzanov Vladimir Rutsky Vladimir Varankin VladimirAus +Vladislav Kolesnikov Vlastimil Zeman Vojtech Vitek (V-Teq) waitingkuo @@ -1894,12 +2151,16 @@ Wang Long Wang Ping Wang Xing Wang Yuexiao +Wang Yumu <37442693@qq.com> +wanghuaiqing Ward Vandewege WarheadsSE Wassim Dhif +Wataru Ishida Wayne Chang Wayne Song Weerasak Chongnguluam +Wei Fu Wei Wu Wei-Ting Kuo weipeng @@ -1909,12 +2170,14 @@ Wen Cheng Ma Wendel Fleming Wenjun Tang Wenkai Yin +wenlxie Wentao Zhang Wenxuan Zhao Wenyu You <21551128@zju.edu.cn> Wenzhi Liang Wes Morgan Wewang Xiaorenfine +Wiktor Kwapisiewicz Will Dietz Will Rouesnel Will Weaver @@ -1925,18 +2188,32 @@ William Hubbs William Martin William Riancho William Thurston +Wilson Júnior +Wing-Kam Wong WiseTrem +Wolfgang Nagele Wolfgang Powisch Wonjun Kim +WuLonghui xamyzhao +Xian Chaobo Xianglin Gao +Xianjie Xianlu Bird +Xiao YongBiao +Xiao Zhang XiaoBing Jiang +Xiaodong Liu +Xiaodong Zhang +Xiaohua Ding +Xiaoxi He Xiaoxu Chen Xiaoyu Zhang +xichengliudui <1693291525@qq.com> xiekeyang Ximo Guanter Gonzálbez Xinbo Weng +Xinfeng Liu Xinzi Zhou Xiuming Chen Xuecong Liao @@ -1946,11 +2223,14 @@ Yahya YAMADA Tsuyoshi Yamasaki Masahide Yan Feng +Yan Zhu Yang Bai +Yang Li Yang Pengfei yangchenliang Yanqiang Miao Yao Zaiyong +Yash Murty Yassine Tijani Yasunori Mahata Yazhong Liu @@ -1961,9 +2241,11 @@ Yihang Ho Ying Li Yohei Ueda Yong Tang +Yongxin Li Yongzhi Pan Yosef Fertel You-Sheng Yang (楊有勝) +youcai Youcef YEKHLEF Yu Changchun Yu Chengxia @@ -1971,13 +2253,18 @@ Yu Peng Yu-Ju Hong Yuan Sun Yuanhong Peng +Yue Zhang Yuhao Fang Yuichiro Kaneko +YujiOshima Yunxiang Huang Yurii Rashkovskii +Yusuf Tarık Günaydın +Yves Blusseau <90z7oey02@sneakemail.com> Yves Junqueira Zac Dover Zach Borboa +Zach Gershman Zachary Jaffee Zain Memon Zaiste! @@ -1991,11 +2278,16 @@ ZhangHang zhangxianwei Zhenan Ye <21551168@zju.edu.cn> zhenghenghuo +Zhenhai Gao Zhenkun Bi +ZhiPeng Lu +zhipengzuo Zhou Hao +Zhoulin Xie Zhu Guihua Zhu Kunjia Zhuoyun Wei +Ziheng Liu Zilin Du zimbatm Ziming Dong @@ -2004,13 +2296,15 @@ zmarouf Zoltan Tombol Zou Yu zqh -Zuhayr Elahi +Zuhayr Elahi Zunayed Ali Álex González Álvaro Lázaro Átila Camurça Alves 尹吉峰 +屈骏 徐俊杰 慕陶 搏通 黄艳红00139573 +정재영 diff --git a/CHANGELOG.md b/CHANGELOG.md index 83fb9f4f090b8..8ae5f5493768b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -157,7 +157,7 @@ be found. + Ensure iptables initialization only happens once [docker/libnetwork#1676](https://github.com/docker/libnetwork/pull/1676) * Fix bad order of iptables filter rules [docker/libnetwork#961](https://github.com/docker/libnetwork/pull/961) + Add anonymous container alias to service record on attachable network [docker/libnetwork#1651](https://github.com/docker/libnetwork/pull/1651) -+ Support for `com.docker.network.container_interface_prefix` driver label [docker/libnetwork#1667](https://github.com/docker/libnetwork/pull/1667) ++ Support for `com.docker.network.container_iface_prefix` driver label [docker/libnetwork#1667](https://github.com/docker/libnetwork/pull/1667) + Improve network list performance by omitting network details that are not used [#30673](https://github.com/docker/docker/pull/30673) ### Runtime diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index daefdadaed77c..c47cecb626357 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,10 +27,10 @@ issue, please bring it to their attention right away! Please **DO NOT** file a public issue, instead send your report privately to [security@docker.com](mailto:security@docker.com). -Security reports are greatly appreciated and we will publicly thank you for it. -We also like to send gifts—if you're into schwag, make sure to let -us know. We currently do not offer a paid security bounty program, but are not -ruling it out in the future. +Security reports are greatly appreciated and we will publicly thank you for it, +although we keep your name confidential if you request it. We also like to send +gifts—if you're into schwag, make sure to let us know. We currently do not +offer a paid security bounty program, but are not ruling it out in the future. ## Reporting other issues diff --git a/Dockerfile b/Dockerfile index aac99c5090fe7..5cbbfc61fbdf3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,172 +1,269 @@ -# This file describes the standard way to build Docker, using docker -# -# Usage: -# -# # Use make to build a development environment image and run it in a container. -# # This is slow the first time. -# make BIND_DIR=. shell -# -# The following commands are executed inside the running container. +# syntax=docker/dockerfile:1.3 -# # Make a dockerd binary. -# # hack/make.sh binary -# -# # Install dockerd to /usr/local/bin -# # make install -# -# # Run unit tests -# # hack/test/unit -# -# # Run tests e.g. integration, py -# # hack/make.sh binary test-integration test-docker-py -# -# Note: AppArmor used to mess with privileged mode, but this is no longer -# the case. Therefore, you don't have to disable it anymore. -# +ARG CROSS="false" +ARG SYSTEMD="false" +# IMPORTANT: When updating this please note that stdlib archive/tar pkg is vendored +ARG GO_VERSION=1.17.4 +ARG DEBIAN_FRONTEND=noninteractive +ARG VPNKIT_VERSION=0.5.0 +ARG DOCKER_BUILDTAGS="apparmor seccomp" + +ARG BASE_DEBIAN_DISTRO="bullseye" +ARG GOLANG_IMAGE="golang:${GO_VERSION}-${BASE_DEBIAN_DISTRO}" -FROM golang:1.11.1 AS base -# allow replacing httpredir or deb mirror -ARG APT_MIRROR=deb.debian.org -RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list +FROM ${GOLANG_IMAGE} AS base +RUN echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache +ARG APT_MIRROR +RUN sed -ri "s/(httpredir|deb).debian.org/${APT_MIRROR:-deb.debian.org}/g" /etc/apt/sources.list \ + && sed -ri "s/(security).debian.org/${APT_MIRROR:-security.debian.org}/g" /etc/apt/sources.list +ENV GO111MODULE=off FROM base AS criu -# Install CRIU for checkpoint/restore support -ENV CRIU_VERSION 3.6 -# Install dependency packages specific to criu -RUN apt-get update && apt-get install -y \ - libnet-dev \ - libprotobuf-c0-dev \ - libprotobuf-dev \ - libnl-3-dev \ - libcap-dev \ - protobuf-compiler \ - protobuf-c-compiler \ - python-protobuf \ - && mkdir -p /usr/src/criu \ - && curl -sSL https://github.com/checkpoint-restore/criu/archive/v${CRIU_VERSION}.tar.gz | tar -C /usr/src/criu/ -xz --strip-components=1 \ - && cd /usr/src/criu \ - && make \ - && make PREFIX=/build/ install-criu +ARG DEBIAN_FRONTEND +ADD --chmod=0644 https://download.opensuse.org/repositories/devel:/tools:/criu/Debian_11/Release.key /etc/apt/trusted.gpg.d/criu.gpg.asc +RUN --mount=type=cache,sharing=locked,id=moby-criu-aptlib,target=/var/lib/apt \ + --mount=type=cache,sharing=locked,id=moby-criu-aptcache,target=/var/cache/apt \ + echo 'deb https://download.opensuse.org/repositories/devel:/tools:/criu/Debian_11/ /' > /etc/apt/sources.list.d/criu.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends criu \ + && install -D /usr/sbin/criu /build/criu FROM base AS registry -# Install two versions of the registry. The first is an older version that -# only supports schema1 manifests. The second is a newer version that supports -# both. This allows integration-cli tests to cover push/pull with both schema1 -# and schema2 manifests. -ENV REGISTRY_COMMIT_SCHEMA1 ec87e9b6971d831f0eff752ddb54fb64693e51cd -ENV REGISTRY_COMMIT 47a064d4195a9b56133891bbb13620c3ac83a827 -RUN set -x \ - && export GOPATH="$(mktemp -d)" \ - && git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \ - && (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \ - && GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \ - go build -buildmode=pie -o /build/registry-v2 github.com/docker/distribution/cmd/registry \ - && case $(dpkg --print-architecture) in \ - amd64|ppc64*|s390x) \ - (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1"); \ - GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH"; \ - go build -buildmode=pie -o /build/registry-v2-schema1 github.com/docker/distribution/cmd/registry; \ - ;; \ - esac \ - && rm -rf "$GOPATH" - - - -FROM base AS docker-py -# Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT 8b246db271a85d6541dc458838627e89c683e42f -RUN git clone https://github.com/docker/docker-py.git /build \ - && cd /build \ - && git checkout -q $DOCKER_PY_COMMIT +WORKDIR /go/src/github.com/docker/distribution +# REGISTRY_VERSION specifies the version of the registry to build and install +# from the https://github.com/docker/distribution repository. This version of +# the registry is used to test both schema 1 and schema 2 manifests. Generally, +# the version specified here should match a current release. +ARG REGISTRY_VERSION=v2.3.0 +# REGISTRY_VERSION_SCHEMA1 specifies the version of the regsitry to build and +# install from the https://github.com/docker/distribution repository. This is +# an older (pre v2.3.0) version of the registry that only supports schema1 +# manifests. This version of the registry is not working on arm64, so installation +# is skipped on that architecture. +ARG REGISTRY_VERSION_SCHEMA1=v2.1.0 +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + --mount=type=tmpfs,target=/go/src/ \ + set -x \ + && git clone https://github.com/docker/distribution.git . \ + && git checkout -q "$REGISTRY_VERSION" \ + && GOPATH="/go/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \ + go build -buildmode=pie -o /build/registry-v2 github.com/docker/distribution/cmd/registry \ + && case $(dpkg --print-architecture) in \ + amd64|armhf|ppc64*|s390x) \ + git checkout -q "$REGISTRY_VERSION_SCHEMA1"; \ + GOPATH="/go/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH"; \ + go build -buildmode=pie -o /build/registry-v2-schema1 github.com/docker/distribution/cmd/registry; \ + ;; \ + esac FROM base AS swagger -# Install go-swagger for validating swagger.yaml -ENV GO_SWAGGER_COMMIT c28258affb0b6251755d92489ef685af8d4ff3eb -RUN set -x \ - && export GOPATH="$(mktemp -d)" \ - && git clone https://github.com/go-swagger/go-swagger.git "$GOPATH/src/github.com/go-swagger/go-swagger" \ - && (cd "$GOPATH/src/github.com/go-swagger/go-swagger" && git checkout -q "$GO_SWAGGER_COMMIT") \ - && go build -o /build/swagger github.com/go-swagger/go-swagger/cmd/swagger \ - && rm -rf "$GOPATH" - - -FROM base AS frozen-images -RUN apt-get update && apt-get install -y jq ca-certificates --no-install-recommends +WORKDIR $GOPATH/src/github.com/go-swagger/go-swagger + +# GO_SWAGGER_COMMIT specifies the version of the go-swagger binary to build and +# install. Go-swagger is used in CI for validating swagger.yaml in hack/validate/swagger-gen +# +# Currently uses a fork from https://github.com/kolyshkin/go-swagger/tree/golang-1.13-fix, +# TODO: move to under moby/ or fix upstream go-swagger to work for us. +ENV GO_SWAGGER_COMMIT c56166c036004ba7a3a321e5951ba472b9ae298c +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + --mount=type=tmpfs,target=/go/src/ \ + set -x \ + && git clone https://github.com/kolyshkin/go-swagger.git . \ + && git checkout -q "$GO_SWAGGER_COMMIT" \ + && go build -o /build/swagger github.com/go-swagger/go-swagger/cmd/swagger + +FROM debian:${BASE_DEBIAN_DISTRO} AS frozen-images +ARG DEBIAN_FRONTEND +RUN --mount=type=cache,sharing=locked,id=moby-frozen-images-aptlib,target=/var/lib/apt \ + --mount=type=cache,sharing=locked,id=moby-frozen-images-aptcache,target=/var/cache/apt \ + apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + jq # Get useful and necessary Hub images so we can "docker load" locally instead of pulling COPY contrib/download-frozen-image-v2.sh / +ARG TARGETARCH RUN /download-frozen-image-v2.sh /build \ - buildpack-deps:jessie@sha256:dd86dced7c9cd2a724e779730f0a53f93b7ef42228d4344b25ce9a42a1486251 \ - busybox:latest@sha256:bbc3a03235220b170ba48a157dd097dd1379299370e1ed99ce976df0355d24f0 \ - busybox:glibc@sha256:0b55a30394294ab23b9afd58fab94e61a923f5834fba7ddbae7f8e0c11ba85e6 \ - debian:jessie@sha256:287a20c5f73087ab406e6b364833e3fb7b3ae63ca0eb3486555dc27ed32c6e60 \ - hello-world:latest@sha256:be0cd392e45be79ffeffa6b05338b98ebb16c87b255f48e297ec7f98e123905c -# See also ensureFrozenImagesLinux() in "integration-cli/fixtures_linux_daemon_test.go" (which needs to be updated when adding images to this list) - -# Just a little hack so we don't have to install these deps twice, once for runc and once for dockerd -FROM base AS runtime-dev -RUN apt-get update && apt-get install -y \ - libapparmor-dev \ - libseccomp-dev - - -FROM base AS tomlv -ENV INSTALL_BINARY_NAME=tomlv -COPY hack/dockerfile/install/install.sh ./install.sh -COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ -RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME + busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 \ + busybox:glibc@sha256:1f81263701cddf6402afe9f33fca0266d9fff379e59b1748f33d3072da71ee85 \ + debian:bullseye-slim@sha256:dacf278785a4daa9de07596ec739dbc07131e189942772210709c5c0777e8437 \ + hello-world:latest@sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9 \ + arm32v7/hello-world:latest@sha256:50b8560ad574c779908da71f7ce370c0a2471c098d44d1c8f6b513c5a55eeeb1 +# See also frozenImages in "testutil/environment/protect.go" (which needs to be updated when adding images to this list) + +FROM base AS cross-false + +FROM --platform=linux/amd64 base AS cross-true +ARG DEBIAN_FRONTEND +RUN dpkg --add-architecture arm64 +RUN dpkg --add-architecture armel +RUN dpkg --add-architecture armhf +RUN dpkg --add-architecture ppc64el +RUN dpkg --add-architecture s390x +RUN --mount=type=cache,sharing=locked,id=moby-cross-true-aptlib,target=/var/lib/apt \ + --mount=type=cache,sharing=locked,id=moby-cross-true-aptcache,target=/var/cache/apt \ + apt-get update && apt-get install -y --no-install-recommends \ + crossbuild-essential-arm64 \ + crossbuild-essential-armel \ + crossbuild-essential-armhf \ + crossbuild-essential-ppc64el \ + crossbuild-essential-s390x + +FROM cross-${CROSS} AS dev-base + +FROM dev-base AS runtime-dev-cross-false +ARG DEBIAN_FRONTEND +RUN --mount=type=cache,sharing=locked,id=moby-cross-false-aptlib,target=/var/lib/apt \ + --mount=type=cache,sharing=locked,id=moby-cross-false-aptcache,target=/var/cache/apt \ + apt-get update && apt-get install -y --no-install-recommends \ + binutils-mingw-w64 \ + g++-mingw-w64-x86-64 \ + libapparmor-dev \ + libbtrfs-dev \ + libdevmapper-dev \ + libseccomp-dev \ + libsystemd-dev \ + libudev-dev + +FROM --platform=linux/amd64 runtime-dev-cross-false AS runtime-dev-cross-true +ARG DEBIAN_FRONTEND +# These crossbuild packages rely on gcc-, but this doesn't want to install +# on non-amd64 systems, so other architectures cannnot crossbuild amd64. +RUN --mount=type=cache,sharing=locked,id=moby-cross-true-aptlib,target=/var/lib/apt \ + --mount=type=cache,sharing=locked,id=moby-cross-true-aptcache,target=/var/cache/apt \ + apt-get update && apt-get install -y --no-install-recommends \ + libapparmor-dev:arm64 \ + libapparmor-dev:armel \ + libapparmor-dev:armhf \ + libapparmor-dev:ppc64el \ + libapparmor-dev:s390x \ + libseccomp-dev:arm64 \ + libseccomp-dev:armel \ + libseccomp-dev:armhf \ + libseccomp-dev:ppc64el \ + libseccomp-dev:s390x + +FROM runtime-dev-cross-${CROSS} AS runtime-dev + +FROM base AS tomll +# GOTOML_VERSION specifies the version of the tomll binary to build and install +# from the https://github.com/pelletier/go-toml repository. This binary is used +# in CI in the hack/validate/toml script. +# +# When updating this version, consider updating the github.com/pelletier/go-toml +# dependency in vendor.conf accordingly. +ARG GOTOML_VERSION=v1.8.1 +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + GOBIN=/build/ GO111MODULE=on go install "github.com/pelletier/go-toml/cmd/tomll@${GOTOML_VERSION}" \ + && /build/tomll --help FROM base AS vndr -ENV INSTALL_BINARY_NAME=vndr -COPY hack/dockerfile/install/install.sh ./install.sh -COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ -RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME - -FROM base AS containerd -RUN apt-get update && apt-get install -y btrfs-tools -ENV INSTALL_BINARY_NAME=containerd -COPY hack/dockerfile/install/install.sh ./install.sh -COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ -RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME - -FROM base AS proxy -ENV INSTALL_BINARY_NAME=proxy -COPY hack/dockerfile/install/install.sh ./install.sh -COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ -RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME - -FROM base AS gometalinter -ENV INSTALL_BINARY_NAME=gometalinter -COPY hack/dockerfile/install/install.sh ./install.sh -COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ -RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME - -FROM base AS dockercli -ENV INSTALL_BINARY_NAME=dockercli -COPY hack/dockerfile/install/install.sh ./install.sh -COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ -RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME +# VNDR_VERSION specifies the version of the vndr tool to build and install +# from the https://github.com/LK4D4/vndr repository. +# +# The vndr tool is used to manage vendored go packages in the vendor directory, +# and is pinned to a fixed version because different versions of this tool +# can result in differences in the (go) files that are considered for vendoring. +ARG VNDR_VERSION=v0.1.2 +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + GOBIN=/build/ GO111MODULE=on go install "github.com/LK4D4/vndr@${VNDR_VERSION}" \ + && /build/vndr --help + +FROM dev-base AS containerd +ARG DEBIAN_FRONTEND +RUN --mount=type=cache,sharing=locked,id=moby-containerd-aptlib,target=/var/lib/apt \ + --mount=type=cache,sharing=locked,id=moby-containerd-aptcache,target=/var/cache/apt \ + apt-get update && apt-get install -y --no-install-recommends \ + libbtrfs-dev +ARG CONTAINERD_VERSION +COPY /hack/dockerfile/install/install.sh /hack/dockerfile/install/containerd.installer / +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + PREFIX=/build /install.sh containerd + +FROM base AS golangci_lint +ARG GOLANGCI_LINT_VERSION=v1.23.8 +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + GOBIN=/build/ GO111MODULE=on go install "github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINT_VERSION}" \ + && /build/golangci-lint --version + +FROM base AS gotestsum +ARG GOTESTSUM_VERSION=v1.7.0 +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + GOBIN=/build/ GO111MODULE=on go install "gotest.tools/gotestsum@${GOTESTSUM_VERSION}" \ + && /build/gotestsum --version + +FROM base AS shfmt +ARG SHFMT_VERSION=v3.0.2 +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + GOBIN=/build/ GO111MODULE=on go install "mvdan.cc/sh/v3/cmd/shfmt@${SHFMT_VERSION}" \ + && /build/shfmt --version + +FROM dev-base AS dockercli +ARG DOCKERCLI_CHANNEL +ARG DOCKERCLI_VERSION +COPY /hack/dockerfile/install/install.sh /hack/dockerfile/install/dockercli.installer / +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + PREFIX=/build /install.sh dockercli FROM runtime-dev AS runc -ENV INSTALL_BINARY_NAME=runc -COPY hack/dockerfile/install/install.sh ./install.sh -COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ -RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME +ARG RUNC_VERSION +ARG RUNC_BUILDTAGS +COPY /hack/dockerfile/install/install.sh /hack/dockerfile/install/runc.installer / +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + PREFIX=/build /install.sh runc + +FROM dev-base AS tini +ARG DEBIAN_FRONTEND +ARG TINI_VERSION +RUN --mount=type=cache,sharing=locked,id=moby-tini-aptlib,target=/var/lib/apt \ + --mount=type=cache,sharing=locked,id=moby-tini-aptcache,target=/var/cache/apt \ + apt-get update && apt-get install -y --no-install-recommends \ + cmake \ + vim-common +COPY /hack/dockerfile/install/install.sh /hack/dockerfile/install/tini.installer / +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + PREFIX=/build /install.sh tini + +FROM dev-base AS rootlesskit +ARG ROOTLESSKIT_VERSION +ARG PREFIX=/build +COPY /hack/dockerfile/install/install.sh /hack/dockerfile/install/rootlesskit.installer / +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + /install.sh rootlesskit \ + && "${PREFIX}"/rootlesskit --version \ + && "${PREFIX}"/rootlesskit-docker-proxy --help +COPY ./contrib/dockerd-rootless.sh /build +COPY ./contrib/dockerd-rootless-setuptool.sh /build -FROM base AS tini -RUN apt-get update && apt-get install -y cmake vim-common -COPY hack/dockerfile/install/install.sh ./install.sh -ENV INSTALL_BINARY_NAME=tini -COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ -RUN PREFIX=/build/ ./install.sh $INSTALL_BINARY_NAME +FROM --platform=amd64 djs55/vpnkit:${VPNKIT_VERSION} AS vpnkit-amd64 +FROM --platform=arm64 djs55/vpnkit:${VPNKIT_VERSION} AS vpnkit-arm64 +FROM scratch AS vpnkit +COPY --from=vpnkit-amd64 /vpnkit /build/vpnkit.x86_64 +COPY --from=vpnkit-arm64 /vpnkit /build/vpnkit.aarch64 # TODO: Some of this is only really needed for testing, it would be nice to split this up -FROM runtime-dev AS dev +FROM runtime-dev AS dev-systemd-false +ARG DEBIAN_FRONTEND RUN groupadd -r docker -RUN useradd --create-home --gid docker unprivilegeduser +RUN useradd --create-home --gid docker unprivilegeduser \ + && mkdir -p /home/unprivilegeduser/.local/share/docker \ + && chown -R unprivilegeduser /home/unprivilegeduser # Let us use a .bashrc file RUN ln -sfv /go/src/github.com/docker/docker/.bashrc ~/.bashrc # Activate bash completion and include Docker's completion if mounted with DOCKER_BASH_COMPLETION_PATH @@ -175,65 +272,133 @@ RUN ln -s /usr/local/completion/bash/docker /etc/bash_completion.d/docker RUN ldconfig # This should only install packages that are specifically needed for the dev environment and nothing else # Do you really need to add another package here? Can it be done in a different build stage? -RUN apt-get update && apt-get install -y \ - apparmor \ - aufs-tools \ - bash-completion \ - btrfs-tools \ - iptables \ - jq \ - libdevmapper-dev \ - libudev-dev \ - libsystemd-dev \ - binutils-mingw-w64 \ - g++-mingw-w64-x86-64 \ - net-tools \ - pigz \ - python-backports.ssl-match-hostname \ - python-dev \ - python-mock \ - python-pip \ - python-requests \ - python-setuptools \ - python-websocket \ - python-wheel \ - thin-provisioning-tools \ - vim \ - vim-common \ - xfsprogs \ - zip \ - bzip2 \ - xz-utils \ - --no-install-recommends -COPY --from=swagger /build/swagger* /usr/local/bin/ -COPY --from=frozen-images /build/ /docker-frozen-images -COPY --from=gometalinter /build/ /usr/local/bin/ -COPY --from=tomlv /build/ /usr/local/bin/ -COPY --from=vndr /build/ /usr/local/bin/ -COPY --from=tini /build/ /usr/local/bin/ -COPY --from=runc /build/ /usr/local/bin/ -COPY --from=containerd /build/ /usr/local/bin/ -COPY --from=proxy /build/ /usr/local/bin/ -COPY --from=dockercli /build/ /usr/local/cli -COPY --from=registry /build/registry* /usr/local/bin/ -COPY --from=criu /build/ /usr/local/ -COPY --from=docker-py /build/ /docker-py -# TODO: This is for the docker-py tests, which shouldn't really be needed for -# this image, but currently CI is expecting to run this image. This should be -# split out into a separate image, including all the `python-*` deps installed -# above. -RUN cd /docker-py \ - && pip install docker-pycreds==0.2.1 \ - && pip install yamllint==1.5.0 \ - && pip install -r test-requirements.txt +RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \ + --mount=type=cache,sharing=locked,id=moby-dev-aptcache,target=/var/cache/apt \ + apt-get update && apt-get install -y --no-install-recommends \ + apparmor \ + bash-completion \ + bzip2 \ + inetutils-ping \ + iproute2 \ + iptables \ + jq \ + libcap2-bin \ + libnet1 \ + libnl-3-200 \ + libprotobuf-c1 \ + net-tools \ + patch \ + pigz \ + python3-pip \ + python3-setuptools \ + python3-wheel \ + sudo \ + thin-provisioning-tools \ + uidmap \ + vim \ + vim-common \ + xfsprogs \ + xz-utils \ + zip \ + zstd + + +# Switch to use iptables instead of nftables (to match the CI hosts) +# TODO use some kind of runtime auto-detection instead if/when nftables is supported (https://github.com/moby/moby/issues/26824) +RUN update-alternatives --set iptables /usr/sbin/iptables-legacy || true \ + && update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy || true \ + && update-alternatives --set arptables /usr/sbin/arptables-legacy || true +RUN pip3 install yamllint==1.26.1 + +COPY --from=dockercli /build/ /usr/local/cli +COPY --from=frozen-images /build/ /docker-frozen-images +COPY --from=swagger /build/ /usr/local/bin/ +COPY --from=tomll /build/ /usr/local/bin/ +COPY --from=tini /build/ /usr/local/bin/ +COPY --from=registry /build/ /usr/local/bin/ +COPY --from=criu /build/ /usr/local/bin/ +COPY --from=vndr /build/ /usr/local/bin/ +COPY --from=gotestsum /build/ /usr/local/bin/ +COPY --from=golangci_lint /build/ /usr/local/bin/ +COPY --from=shfmt /build/ /usr/local/bin/ +COPY --from=runc /build/ /usr/local/bin/ +COPY --from=containerd /build/ /usr/local/bin/ +COPY --from=rootlesskit /build/ /usr/local/bin/ +COPY --from=vpnkit /build/ /usr/local/bin/ ENV PATH=/usr/local/cli:$PATH -ENV DOCKER_BUILDTAGS apparmor seccomp selinux -# Options for hack/validate/gometalinter -ENV GOMETALINTER_OPTS="--deadline=2m" +ARG DOCKER_BUILDTAGS +ENV DOCKER_BUILDTAGS="${DOCKER_BUILDTAGS}" WORKDIR /go/src/github.com/docker/docker VOLUME /var/lib/docker +VOLUME /home/unprivilegeduser/.local/share/docker # Wrap all commands in the "docker-in-docker" script to allow nested containers ENTRYPOINT ["hack/dind"] -# Upload docker source + +FROM dev-systemd-false AS dev-systemd-true +RUN --mount=type=cache,sharing=locked,id=moby-dev-aptlib,target=/var/lib/apt \ + --mount=type=cache,sharing=locked,id=moby-dev-aptcache,target=/var/cache/apt \ + apt-get update && apt-get install -y --no-install-recommends \ + dbus \ + dbus-user-session \ + systemd \ + systemd-sysv +RUN mkdir -p hack \ + && curl -o hack/dind-systemd https://raw.githubusercontent.com/AkihiroSuda/containerized-systemd/b70bac0daeea120456764248164c21684ade7d0d/docker-entrypoint.sh \ + && chmod +x hack/dind-systemd +ENTRYPOINT ["hack/dind-systemd"] + +FROM dev-systemd-${SYSTEMD} AS dev + +FROM runtime-dev AS binary-base +ARG DOCKER_GITCOMMIT=HEAD +ENV DOCKER_GITCOMMIT=${DOCKER_GITCOMMIT} +ARG VERSION +ENV VERSION=${VERSION} +ARG PLATFORM +ENV PLATFORM=${PLATFORM} +ARG PRODUCT +ENV PRODUCT=${PRODUCT} +ARG DEFAULT_PRODUCT_LICENSE +ENV DEFAULT_PRODUCT_LICENSE=${DEFAULT_PRODUCT_LICENSE} +ARG DOCKER_BUILDTAGS +ENV DOCKER_BUILDTAGS="${DOCKER_BUILDTAGS}" +ENV PREFIX=/build +# TODO: This is here because hack/make.sh binary copies these extras binaries +# from $PATH into the bundles dir. +# It would be nice to handle this in a different way. +COPY --from=tini /build/ /usr/local/bin/ +COPY --from=runc /build/ /usr/local/bin/ +COPY --from=containerd /build/ /usr/local/bin/ +COPY --from=rootlesskit /build/ /usr/local/bin/ +COPY --from=vpnkit /build/ /usr/local/bin/ +WORKDIR /go/src/github.com/docker/docker + +FROM binary-base AS build-binary +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=bind,target=/go/src/github.com/docker/docker \ + hack/make.sh binary + +FROM binary-base AS build-dynbinary +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=bind,target=/go/src/github.com/docker/docker \ + hack/make.sh dynbinary + +FROM binary-base AS build-cross +ARG DOCKER_CROSSPLATFORMS +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=bind,target=/go/src/github.com/docker/docker \ + --mount=type=tmpfs,target=/go/src/github.com/docker/docker/autogen \ + hack/make.sh cross + +FROM scratch AS binary +COPY --from=build-binary /build/bundles/ / + +FROM scratch AS dynbinary +COPY --from=build-dynbinary /build/bundles/ / + +FROM scratch AS cross +COPY --from=build-cross /build/bundles/ / + +FROM dev AS final COPY . /go/src/github.com/docker/docker diff --git a/Dockerfile.e2e b/Dockerfile.e2e index 48baa3b076bd3..6871a45a9b652 100644 --- a/Dockerfile.e2e +++ b/Dockerfile.e2e @@ -1,6 +1,7 @@ -## Step 1: Build tests -FROM golang:1.11.1-alpine3.7 as builder +ARG GO_VERSION=1.17.4 +FROM golang:${GO_VERSION}-alpine AS base +ENV GO111MODULE=off RUN apk --no-cache add \ bash \ btrfs-progs-dev \ @@ -9,37 +10,55 @@ RUN apk --no-cache add \ lvm2-dev \ jq +RUN mkdir -p /build/ RUN mkdir -p /go/src/github.com/docker/docker/ WORKDIR /go/src/github.com/docker/docker/ -# Generate frozen images -COPY contrib/download-frozen-image-v2.sh contrib/download-frozen-image-v2.sh -RUN contrib/download-frozen-image-v2.sh /output/docker-frozen-images \ - buildpack-deps:jessie@sha256:dd86dced7c9cd2a724e779730f0a53f93b7ef42228d4344b25ce9a42a1486251 \ - busybox:latest@sha256:bbc3a03235220b170ba48a157dd097dd1379299370e1ed99ce976df0355d24f0 \ - busybox:glibc@sha256:0b55a30394294ab23b9afd58fab94e61a923f5834fba7ddbae7f8e0c11ba85e6 \ - debian:jessie@sha256:287a20c5f73087ab406e6b364833e3fb7b3ae63ca0eb3486555dc27ed32c6e60 \ - hello-world:latest@sha256:be0cd392e45be79ffeffa6b05338b98ebb16c87b255f48e297ec7f98e123905c +FROM base AS frozen-images +# Get useful and necessary Hub images so we can "docker load" locally instead of pulling +COPY contrib/download-frozen-image-v2.sh / +RUN /download-frozen-image-v2.sh /build \ + busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 \ + busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 \ + debian:bullseye-slim@sha256:dacf278785a4daa9de07596ec739dbc07131e189942772210709c5c0777e8437 \ + hello-world:latest@sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9 \ + arm32v7/hello-world:latest@sha256:50b8560ad574c779908da71f7ce370c0a2471c098d44d1c8f6b513c5a55eeeb1 +# See also frozenImages in "testutil/environment/protect.go" (which needs to be updated when adding images to this list) -# Install dockercli -# Please edit hack/dockerfile/install/.installer to update them. -COPY hack/dockerfile/install hack/dockerfile/install -RUN ./hack/dockerfile/install/install.sh dockercli - -# Set tag and add sources -ARG DOCKER_GITCOMMIT -ENV DOCKER_GITCOMMIT=${DOCKER_GITCOMMIT:-undefined} -ADD . . +FROM base AS dockercli +ENV INSTALL_BINARY_NAME=dockercli +COPY hack/dockerfile/install/install.sh ./install.sh +COPY hack/dockerfile/install/$INSTALL_BINARY_NAME.installer ./ +RUN PREFIX=/build ./install.sh $INSTALL_BINARY_NAME # Build DockerSuite.TestBuild* dependency -RUN CGO_ENABLED=0 go build -buildmode=pie -o /output/httpserver github.com/docker/docker/contrib/httpserver +FROM base AS contrib +COPY contrib/syscall-test /build/syscall-test +COPY contrib/httpserver/Dockerfile /build/httpserver/Dockerfile +COPY contrib/httpserver contrib/httpserver +RUN CGO_ENABLED=0 go build -buildmode=pie -o /build/httpserver/httpserver github.com/docker/docker/contrib/httpserver -# Build the integration tests and copy the resulting binaries to /output/tests +# Build the integration tests and copy the resulting binaries to /build/tests +FROM base AS builder + +# Set tag and add sources +COPY . . +# Copy test sources tests that use assert can print errors +RUN mkdir -p /build${PWD} && find integration integration-cli -name \*_test.go -exec cp --parents '{}' /build${PWD} \; +# Build and install test binaries +ARG DOCKER_GITCOMMIT=undefined RUN hack/make.sh build-integration-test-binary -RUN mkdir -p /output/tests && find . -name test.main -exec cp --parents '{}' /output/tests \; +RUN mkdir -p /build/tests && find . -name test.main -exec cp --parents '{}' /build/tests \; -## Step 2: Generate testing image -FROM alpine:3.7 as runner +## Generate testing image +FROM alpine:3.10 as runner + +ENV DOCKER_REMOTE_DAEMON=1 +ENV DOCKER_INTEGRATION_DAEMON_DEST=/ +ENTRYPOINT ["/scripts/run.sh"] + +# Add an unprivileged user to be used for tests which need it +RUN addgroup docker && adduser -D -G docker unprivilegeduser -s /bin/ash # GNU tar is used for generating the emptyfs image RUN apk --no-cache add \ @@ -47,26 +66,21 @@ RUN apk --no-cache add \ ca-certificates \ g++ \ git \ + inetutils-ping \ iptables \ + libcap2-bin \ pigz \ tar \ xz -# Add an unprivileged user to be used for tests which need it -RUN addgroup docker && adduser -D -G docker unprivilegeduser -s /bin/ash - -COPY contrib/httpserver/Dockerfile /tests/contrib/httpserver/Dockerfile -COPY contrib/syscall-test /tests/contrib/syscall-test -COPY integration-cli/fixtures /tests/integration-cli/fixtures +COPY hack/test/e2e-run.sh /scripts/run.sh +COPY hack/make/.ensure-emptyfs /scripts/ensure-emptyfs.sh -COPY hack/test/e2e-run.sh /scripts/run.sh -COPY hack/make/.ensure-emptyfs /scripts/ensure-emptyfs.sh +COPY integration/testdata /tests/integration/testdata +COPY integration/build/testdata /tests/integration/build/testdata +COPY integration-cli/fixtures /tests/integration-cli/fixtures -COPY --from=builder /output/docker-frozen-images /docker-frozen-images -COPY --from=builder /output/httpserver /tests/contrib/httpserver/httpserver -COPY --from=builder /output/tests /tests -COPY --from=builder /usr/local/bin/docker /usr/bin/docker - -ENV DOCKER_REMOTE_DAEMON=1 DOCKER_INTEGRATION_DAEMON_DEST=/ - -ENTRYPOINT ["/scripts/run.sh"] +COPY --from=frozen-images /build/ /docker-frozen-images +COPY --from=dockercli /build/ /usr/bin/ +COPY --from=contrib /build/ /tests/contrib/ +COPY --from=builder /build/ / diff --git a/Dockerfile.simple b/Dockerfile.simple index a23d52927faa2..8ccdbe2453f85 100644 --- a/Dockerfile.simple +++ b/Dockerfile.simple @@ -5,7 +5,13 @@ # This represents the bare minimum required to build and test Docker. -FROM golang:1.11.1-stretch +ARG GO_VERSION=1.17.4 + +ARG BASE_DEBIAN_DISTRO="bullseye" +ARG GOLANG_IMAGE="golang:${GO_VERSION}-${BASE_DEBIAN_DISTRO}" + +FROM ${GOLANG_IMAGE} +ENV GO111MODULE=off # allow replacing httpredir or deb mirror ARG APT_MIRROR=deb.debian.org @@ -15,13 +21,13 @@ RUN sed -ri "s/(httpredir|deb).debian.org/$APT_MIRROR/g" /etc/apt/sources.list # https://github.com/docker/docker/blob/master/project/PACKAGERS.md#build-dependencies # https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ - btrfs-tools \ build-essential \ curl \ cmake \ gcc \ git \ libapparmor-dev \ + libbtrfs-dev \ libdevmapper-dev \ libseccomp-dev \ ca-certificates \ @@ -33,7 +39,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ xfsprogs \ xz-utils \ \ - aufs-tools \ vim-common \ && rm -rf /var/lib/apt/lists/* diff --git a/Dockerfile.windows b/Dockerfile.windows index 180b9368d2c1f..da17d5ae60fc1 100644 --- a/Dockerfile.windows +++ b/Dockerfile.windows @@ -45,8 +45,8 @@ # # 1. Clone the sources from github.com: # -# >> git clone https://github.com/docker/docker.git C:\go\src\github.com\docker\docker -# >> Cloning into 'C:\go\src\github.com\docker\docker'... +# >> git clone https://github.com/docker/docker.git C:\gopath\src\github.com\docker\docker +# >> Cloning into 'C:\gopath\src\github.com\docker\docker'... # >> remote: Counting objects: 186216, done. # >> remote: Compressing objects: 100% (21/21), done. # >> remote: Total 186216 (delta 5), reused 0 (delta 0), pack-reused 186195 @@ -59,7 +59,7 @@ # # 2. Change directory to the cloned docker sources: # -# >> cd C:\go\src\github.com\docker\docker +# >> cd C:\gopath\src\github.com\docker\docker # # # 3. Build a docker image with the components required to build the docker binaries from source @@ -79,8 +79,8 @@ # 5. Copy the binaries out of the container, replacing HostPath with an appropriate destination # folder on the host system where you want the binaries to be located. # -# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\docker.exe C:\HostPath\docker.exe -# >> docker cp binaries:C:\go\src\github.com\docker\docker\bundles\dockerd.exe C:\HostPath\dockerd.exe +# >> docker cp binaries:C:\gopath\src\github.com\docker\docker\bundles\docker.exe C:\HostPath\docker.exe +# >> docker cp binaries:C:\gopath\src\github.com\docker\docker\bundles\dockerd.exe C:\HostPath\dockerd.exe # # # 6. (Optional) Remove the interim container holding the built executable binaries: @@ -147,24 +147,39 @@ # The docker integration tests do not currently run in a container on Windows, predominantly # due to Windows not supporting privileged mode, so anything using a volume would fail. # They (along with the rest of the docker CI suite) can be run using -# https://github.com/jhowardmsft/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1. +# https://github.com/kevpar/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1. # # ----------------------------------------------------------------------------------------- # The number of build steps below are explicitly minimised to improve performance. + +# Extremely important - do not change the following line to reference a "specific" image, +# such as `mcr.microsoft.com/windows/servercore:ltsc2019`. If using this Dockerfile in process +# isolated containers, the kernel of the host must match the container image, and hence +# would fail between Windows Server 2016 (aka RS1) and Windows Server 2019 (aka RS5). +# It is expected that the image `microsoft/windowsservercore:latest` is present, and matches +# the hosts kernel version before doing a build. FROM microsoft/windowsservercore # Use PowerShell as the default shell SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] +ARG GO_VERSION=1.17.4 +ARG CONTAINERD_VERSION=1.5.8 +ARG GOTESTSUM_VERSION=v1.7.0 + # Environment variable notes: # - GO_VERSION must be consistent with 'Dockerfile' used by Linux. +# - CONTAINERD_VERSION must be consistent with 'hack/dockerfile/install/containerd.installer' used by Linux. # - FROM_DOCKERFILE is used for detection of building within a container. -ENV GO_VERSION=1.11.1 ` +ENV GO_VERSION=${GO_VERSION} ` + CONTAINERD_VERSION=${CONTAINERD_VERSION} ` GIT_VERSION=2.11.1 ` - GOPATH=C:\go ` - FROM_DOCKERFILE=1 + GOPATH=C:\gopath ` + GO111MODULE=off ` + FROM_DOCKERFILE=1 ` + GOTESTSUM_VERSION=${GOTESTSUM_VERSION} RUN ` Function Test-Nano() { ` @@ -193,28 +208,30 @@ RUN ` Throw ("Failed to download " + $source) ` }` } else { ` + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ` $webClient = New-Object System.Net.WebClient; ` $webClient.DownloadFile($source, $target); ` } ` } ` ` - setx /M PATH $('C:\git\cmd;C:\git\usr\bin;'+$Env:PATH+';C:\gcc\bin;C:\go\bin'); ` + setx /M PATH $('C:\git\cmd;C:\git\usr\bin;'+$Env:PATH+';C:\gcc\bin;C:\go\bin;C:\containerd\bin'); ` ` Write-Host INFO: Downloading git...; ` $location='https://www.nuget.org/api/v2/package/GitForWindows/'+$Env:GIT_VERSION; ` Download-File $location C:\gitsetup.zip; ` ` Write-Host INFO: Downloading go...; ` - Download-File $('https://golang.org/dl/go'+$Env:GO_VERSION+'.windows-amd64.zip') C:\go.zip; ` + $dlGoVersion=$Env:GO_VERSION -replace '\.0$',''; ` + Download-File "https://golang.org/dl/go${dlGoVersion}.windows-amd64.zip" C:\go.zip; ` ` Write-Host INFO: Downloading compiler 1 of 3...; ` - Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/gcc.zip C:\gcc.zip; ` + Download-File https://raw.githubusercontent.com/moby/docker-tdmgcc/master/gcc.zip C:\gcc.zip; ` ` Write-Host INFO: Downloading compiler 2 of 3...; ` - Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/runtime.zip C:\runtime.zip; ` + Download-File https://raw.githubusercontent.com/moby/docker-tdmgcc/master/runtime.zip C:\runtime.zip; ` ` Write-Host INFO: Downloading compiler 3 of 3...; ` - Download-File https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/binutils.zip C:\binutils.zip; ` + Download-File https://raw.githubusercontent.com/moby/docker-tdmgcc/master/binutils.zip C:\binutils.zip; ` ` Write-Host INFO: Extracting git...; ` Expand-Archive C:\gitsetup.zip C:\git-tmp; ` @@ -238,19 +255,45 @@ RUN ` Remove-Item C:\binutils.zip; ` Remove-Item C:\gitsetup.zip; ` ` - Write-Host INFO: Creating source directory...; ` - New-Item -ItemType Directory -Path C:\go\src\github.com\docker\docker | Out-Null; ` + Write-Host INFO: Downloading containerd; ` + Install-Package -Force 7Zip4PowerShell; ` + $location='https://github.com/containerd/containerd/releases/download/v'+$Env:CONTAINERD_VERSION+'/containerd-'+$Env:CONTAINERD_VERSION+'-windows-amd64.tar.gz'; ` + Download-File $location C:\containerd.tar.gz; ` + New-Item -Path C:\containerd -ItemType Directory; ` + Expand-7Zip C:\containerd.tar.gz C:\; ` + Expand-7Zip C:\containerd.tar C:\containerd; ` + Remove-Item C:\containerd.tar.gz; ` + Remove-Item C:\containerd.tar; ` + ` + # Ensure all directories exist that we will require below.... + $srcDir = """$Env:GOPATH`\src\github.com\docker\docker\bundles"""; ` + Write-Host INFO: Ensuring existence of directory $srcDir...; ` + New-Item -Force -ItemType Directory -Path $srcDir | Out-Null; ` ` Write-Host INFO: Configuring git core.autocrlf...; ` - C:\git\cmd\git config --global core.autocrlf true; ` + C:\git\cmd\git config --global core.autocrlf true; + +RUN ` + Function Install-GoTestSum() { ` + $Env:GO111MODULE = 'on'; ` + $tmpGobin = "${Env:GOBIN_TMP}"; ` + $Env:GOBIN = """${Env:GOPATH}`\bin"""; ` + Write-Host "INFO: Installing gotestsum version $Env:GOTESTSUM_VERSION in $Env:GOBIN"; ` + &go install "gotest.tools/gotestsum@${Env:GOTESTSUM_VERSION}"; ` + $Env:GOBIN = "${tmpGobin}"; ` + $Env:GO111MODULE = 'off'; ` + if ($LASTEXITCODE -ne 0) { ` + Throw '"gotestsum install failed..."'; ` + } ` + } ` ` - Write-Host INFO: Completed + Install-GoTestSum # Make PowerShell the default entrypoint ENTRYPOINT ["powershell.exe"] # Set the working directory to the location of the sources -WORKDIR C:\go\src\github.com\docker\docker +WORKDIR ${GOPATH}\src\github.com\docker\docker # Copy the sources into the container COPY . . diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000000..00a6f955e915e --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,1324 @@ +#!groovy +pipeline { + agent none + + options { + buildDiscarder(logRotator(daysToKeepStr: '30')) + timeout(time: 2, unit: 'HOURS') + timestamps() + } + parameters { + booleanParam(name: 'unit_validate', defaultValue: true, description: 'amd64 (x86_64) unit tests and vendor check') + booleanParam(name: 'validate_force', defaultValue: false, description: 'force validation steps to be run, even if no changes were detected') + booleanParam(name: 'amd64', defaultValue: true, description: 'amd64 (x86_64) Build/Test') + booleanParam(name: 'rootless', defaultValue: true, description: 'amd64 (x86_64) Build/Test (Rootless mode)') + booleanParam(name: 'cgroup2', defaultValue: true, description: 'amd64 (x86_64) Build/Test (cgroup v2)') + booleanParam(name: 'arm64', defaultValue: true, description: 'ARM (arm64) Build/Test') + booleanParam(name: 's390x', defaultValue: false, description: 'IBM Z (s390x) Build/Test') + booleanParam(name: 'ppc64le', defaultValue: false, description: 'PowerPC (ppc64le) Build/Test') + booleanParam(name: 'windowsRS1', defaultValue: false, description: 'Windows 2016 (RS1) Build/Test') + booleanParam(name: 'windowsRS5', defaultValue: true, description: 'Windows 2019 (RS5) Build/Test') + booleanParam(name: 'windows2022', defaultValue: true, description: 'Windows 2022 (LTSC) Build/Test') + booleanParam(name: 'windows2022containerd', defaultValue: true, description: 'Windows 2022 (LTSC) with containerd Build/Test') + booleanParam(name: 'dco', defaultValue: true, description: 'Run the DCO check') + } + environment { + DOCKER_BUILDKIT = '1' + DOCKER_EXPERIMENTAL = '1' + DOCKER_GRAPHDRIVER = 'overlay2' + APT_MIRROR = 'cdn-fastly.deb.debian.org' + CHECK_CONFIG_COMMIT = '33a3680e08d1007e72c3b3f1454f823d8e9948ee' + TESTDEBUG = '0' + TIMEOUT = '120m' + } + stages { + stage('pr-hack') { + when { changeRequest() } + steps { + script { + echo "Workaround for PR auto-cancel feature. Borrowed from https://issues.jenkins-ci.org/browse/JENKINS-43353" + def buildNumber = env.BUILD_NUMBER as int + if (buildNumber > 1) milestone(buildNumber - 1) + milestone(buildNumber) + } + } + } + stage('DCO-check') { + when { + beforeAgent true + expression { params.dco } + } + agent { label 'amd64 && ubuntu-1804 && overlay2' } + steps { + sh ''' + docker run --rm \ + -v "$WORKSPACE:/workspace" \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + alpine sh -c 'apk add --no-cache -q bash git openssh-client && cd /workspace && hack/validate/dco' + ''' + } + } + stage('Build') { + parallel { + stage('unit-validate') { + when { + beforeAgent true + expression { params.unit_validate } + } + agent { label 'amd64 && ubuntu-1804 && overlay2' } + environment { + // On master ("non-pull-request"), force running some validation checks (vendor, swagger), + // even if no files were changed. This allows catching problems caused by pull-requests + // that were merged out-of-sequence. + TEST_FORCE_VALIDATE = sh returnStdout: true, script: 'if [ "${BRANCH_NAME%%-*}" != "PR" ] || [ "${CHANGE_TARGET:-master}" != "master" ] || [ "${validate_force}" = "true" ]; then echo "1"; fi' + } + + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + sh ''' + echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}" + curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \ + && bash ${WORKSPACE}/check-config.sh || true + ''' + } + } + stage("Build dev image") { + steps { + sh 'docker build --force-rm --build-arg APT_MIRROR --build-arg CROSS=true -t docker:${GIT_COMMIT} .' + } + } + stage("Validate") { + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + -v "$WORKSPACE/.git:/go/src/github.com/docker/docker/.git" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e TEST_FORCE_VALIDATE \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/validate/default + ''' + } + } + stage("Docker-py") { + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/make.sh \ + dynbinary \ + test-docker-py + ''' + } + post { + always { + junit testResults: 'bundles/test-docker-py/junit-report.xml', allowEmptyResults: true + + sh ''' + echo "Ensuring container killed." + docker rm -vf docker-pr$BUILD_NUMBER || true + ''' + + sh ''' + echo 'Chowning /workspace to jenkins user' + docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace + ''' + + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + sh ''' + bundleName=docker-py + echo "Creating ${bundleName}-bundles.tar.gz" + tar -czf ${bundleName}-bundles.tar.gz bundles/test-docker-py/*.xml bundles/test-docker-py/*.log + ''' + + archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true + } + } + } + } + stage("Static") { + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + docker:${GIT_COMMIT} \ + hack/make.sh binary + ''' + } + } + stage("Cross") { + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + docker:${GIT_COMMIT} \ + hack/make.sh cross + ''' + } + } + // needs to be last stage that calls make.sh for the junit report to work + stage("Unit tests") { + steps { + sh ''' + sudo modprobe ip6table_filter + ''' + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/test/unit + ''' + } + post { + always { + junit testResults: 'bundles/junit-report*.xml', allowEmptyResults: true + } + } + } + stage("Validate vendor") { + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/.git:/go/src/github.com/docker/docker/.git" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e TEST_FORCE_VALIDATE \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/validate/vendor + ''' + } + } + } + + post { + always { + sh ''' + echo 'Ensuring container killed.' + docker rm -vf docker-pr$BUILD_NUMBER || true + ''' + + sh ''' + echo 'Chowning /workspace to jenkins user' + docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace + ''' + + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + sh ''' + bundleName=unit + echo "Creating ${bundleName}-bundles.tar.gz" + tar -czvf ${bundleName}-bundles.tar.gz bundles/junit-report*.xml bundles/go-test-report*.json bundles/profile*.out + ''' + + archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + stage('amd64') { + when { + beforeAgent true + expression { params.amd64 } + } + agent { label 'amd64 && ubuntu-1804 && overlay2' } + + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + sh ''' + echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}" + curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \ + && bash ${WORKSPACE}/check-config.sh || true + ''' + } + } + stage("Build dev image") { + steps { + sh ''' + # todo: include ip_vs in base image + sudo modprobe ip_vs + + docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} . + ''' + } + } + stage("Run tests") { + steps { + sh '''#!/bin/bash + # bash is needed so 'jobs -p' works properly + # it also accepts setting inline envvars for functions without explicitly exporting + set -x + + run_tests() { + [ -n "$TESTDEBUG" ] && rm= || rm=--rm; + docker run $rm -t --privileged \ + -v "$WORKSPACE/bundles/${TEST_INTEGRATION_DEST}:/go/src/github.com/docker/docker/bundles" \ + -v "$WORKSPACE/bundles/dynbinary-daemon:/go/src/github.com/docker/docker/bundles/dynbinary-daemon" \ + -v "$WORKSPACE/.git:/go/src/github.com/docker/docker/.git" \ + --name "$CONTAINER_NAME" \ + -e KEEPBUNDLE=1 \ + -e TESTDEBUG \ + -e TESTFLAGS \ + -e TEST_SKIP_INTEGRATION \ + -e TEST_SKIP_INTEGRATION_CLI \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e TIMEOUT \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/make.sh \ + "$1" \ + test-integration + } + + trap "exit" INT TERM + trap 'pids=$(jobs -p); echo "Remaining pids to kill: [$pids]"; [ -z "$pids" ] || kill $pids' EXIT + + CONTAINER_NAME=docker-pr$BUILD_NUMBER + + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + -v "$WORKSPACE/.git:/go/src/github.com/docker/docker/.git" \ + --name ${CONTAINER_NAME}-build \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + docker:${GIT_COMMIT} \ + hack/make.sh \ + dynbinary + + # flaky + integration + TEST_INTEGRATION_DEST=1 CONTAINER_NAME=${CONTAINER_NAME}-1 TEST_SKIP_INTEGRATION_CLI=1 run_tests test-integration-flaky & + + # integration-cli first set + TEST_INTEGRATION_DEST=2 CONTAINER_NAME=${CONTAINER_NAME}-2 TEST_SKIP_INTEGRATION=1 TESTFLAGS="-test.run Test(DockerSuite|DockerNetworkSuite|DockerHubPullSuite|DockerRegistrySuite|DockerSchema1RegistrySuite|DockerRegistryAuthTokenSuite|DockerRegistryAuthHtpasswdSuite)/" run_tests & + + # integration-cli second set + TEST_INTEGRATION_DEST=3 CONTAINER_NAME=${CONTAINER_NAME}-3 TEST_SKIP_INTEGRATION=1 TESTFLAGS="-test.run Test(DockerSwarmSuite|DockerDaemonSuite|DockerExternalVolumeSuite)/" run_tests & + + c=0 + for job in $(jobs -p); do + wait ${job} || c=$? + done + exit $c + ''' + } + post { + always { + junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true + } + } + } + } + + post { + always { + sh ''' + echo "Ensuring container killed." + cids=$(docker ps -aq -f name=docker-pr${BUILD_NUMBER}-*) + [ -n "$cids" ] && docker rm -vf $cids || true + ''' + + sh ''' + echo "Chowning /workspace to jenkins user" + docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace + ''' + + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + sh ''' + bundleName=amd64 + echo "Creating ${bundleName}-bundles.tar.gz" + # exclude overlay2 directories + find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz + ''' + + archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + stage('rootless') { + when { + beforeAgent true + expression { params.rootless } + } + agent { label 'amd64 && ubuntu-1804 && overlay2' } + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + sh ''' + echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}" + curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \ + && bash ${WORKSPACE}/check-config.sh || true + ''' + } + } + stage("Build dev image") { + steps { + sh ''' + docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} . + ''' + } + } + stage("Integration tests") { + environment { + DOCKER_ROOTLESS = '1' + TEST_SKIP_INTEGRATION_CLI = '1' + } + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_ROOTLESS \ + -e TEST_SKIP_INTEGRATION_CLI \ + -e TIMEOUT \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/make.sh \ + dynbinary \ + test-integration + ''' + } + post { + always { + junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true + } + } + } + } + + post { + always { + sh ''' + echo "Ensuring container killed." + docker rm -vf docker-pr$BUILD_NUMBER || true + ''' + + sh ''' + echo "Chowning /workspace to jenkins user" + docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace + ''' + + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + sh ''' + bundleName=amd64-rootless + echo "Creating ${bundleName}-bundles.tar.gz" + # exclude overlay2 directories + find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz + ''' + + archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + + stage('cgroup2') { + when { + beforeAgent true + expression { params.cgroup2 } + } + agent { label 'amd64 && ubuntu-2004 && cgroup2' } + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + } + } + stage("Build dev image") { + steps { + sh ''' + docker build --force-rm --build-arg APT_MIRROR --build-arg SYSTEMD=true -t docker:${GIT_COMMIT} . + ''' + } + } + stage("Integration tests") { + environment { + DOCKER_SYSTEMD = '1' // recommended cgroup driver for v2 + TEST_SKIP_INTEGRATION_CLI = '1' // CLI tests do not support v2 + } + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_SYSTEMD \ + -e TEST_SKIP_INTEGRATION_CLI \ + -e TIMEOUT \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/make.sh \ + dynbinary \ + test-integration + ''' + } + post { + always { + junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true + } + } + } + } + + post { + always { + sh ''' + echo "Ensuring container killed." + docker rm -vf docker-pr$BUILD_NUMBER || true + ''' + + sh ''' + echo "Chowning /workspace to jenkins user" + docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace + ''' + + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + sh ''' + bundleName=amd64-cgroup2 + echo "Creating ${bundleName}-bundles.tar.gz" + # exclude overlay2 directories + find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz + ''' + + archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + + + stage('s390x') { + when { + beforeAgent true + // Skip this stage on PRs unless the checkbox is selected + anyOf { + not { changeRequest() } + expression { params.s390x } + } + } + agent { label 's390x-ubuntu-2004' } + + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + sh ''' + echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}" + curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \ + && bash ${WORKSPACE}/check-config.sh || true + ''' + } + } + stage("Build dev image") { + steps { + sh ''' + docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} . + ''' + } + } + stage("Unit tests") { + steps { + sh ''' + sudo modprobe ip6table_filter + ''' + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/test/unit + ''' + } + post { + always { + junit testResults: 'bundles/junit-report*.xml', allowEmptyResults: true + } + } + } + stage("Integration tests") { + environment { TEST_SKIP_INTEGRATION_CLI = '1' } + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e TESTDEBUG \ + -e TEST_SKIP_INTEGRATION_CLI \ + -e TIMEOUT \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/make.sh \ + dynbinary \ + test-integration + ''' + } + post { + always { + junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true + } + } + } + } + + post { + always { + sh ''' + echo "Ensuring container killed." + docker rm -vf docker-pr$BUILD_NUMBER || true + ''' + + sh ''' + echo "Chowning /workspace to jenkins user" + docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace + ''' + + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + sh ''' + bundleName=s390x-integration + echo "Creating ${bundleName}-bundles.tar.gz" + # exclude overlay2 directories + find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz + ''' + + archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + stage('s390x integration-cli') { + when { + beforeAgent true + not { changeRequest() } + expression { params.s390x } + } + agent { label 's390x-ubuntu-2004' } + + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + sh ''' + echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}" + curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \ + && bash ${WORKSPACE}/check-config.sh || true + ''' + } + } + stage("Build dev image") { + steps { + sh ''' + docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} . + ''' + } + } + stage("Integration-cli tests") { + environment { TEST_SKIP_INTEGRATION = '1' } + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e TEST_SKIP_INTEGRATION \ + -e TIMEOUT \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/make.sh \ + dynbinary \ + test-integration + ''' + } + post { + always { + junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true + } + } + } + } + + post { + always { + sh ''' + echo "Ensuring container killed." + docker rm -vf docker-pr$BUILD_NUMBER || true + ''' + + sh ''' + echo "Chowning /workspace to jenkins user" + docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace + ''' + + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + sh ''' + bundleName=s390x-integration-cli + echo "Creating ${bundleName}-bundles.tar.gz" + # exclude overlay2 directories + find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz + ''' + + archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + stage('ppc64le') { + when { + beforeAgent true + // Skip this stage on PRs unless the checkbox is selected + anyOf { + not { changeRequest() } + expression { params.ppc64le } + } + } + agent { label 'ppc64le-ubuntu-1604' } + // ppc64le machines run on Docker 18.06, and buildkit has some + // bugs on that version. Build and use buildx instead. + environment { + USE_BUILDX = '1' + DOCKER_BUILDKIT = '0' + } + + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + sh ''' + echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}" + curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \ + && bash ${WORKSPACE}/check-config.sh || true + ''' + } + } + stage("Build dev image") { + steps { + sh ''' + make bundles/buildx + bundles/buildx build --load --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} . + ''' + } + } + stage("Unit tests") { + steps { + sh ''' + sudo modprobe ip6table_filter + ''' + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/test/unit + ''' + } + post { + always { + junit testResults: 'bundles/junit-report*.xml', allowEmptyResults: true + } + } + } + stage("Integration tests") { + environment { TEST_SKIP_INTEGRATION_CLI = '1' } + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e TESTDEBUG \ + -e TEST_SKIP_INTEGRATION_CLI \ + -e TIMEOUT \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/make.sh \ + dynbinary \ + test-integration + ''' + } + post { + always { + junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true + } + } + } + } + + post { + always { + sh ''' + echo "Ensuring container killed." + docker rm -vf docker-pr$BUILD_NUMBER || true + ''' + + sh ''' + echo "Chowning /workspace to jenkins user" + docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace + ''' + + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + sh ''' + bundleName=ppc64le-integration + echo "Creating ${bundleName}-bundles.tar.gz" + # exclude overlay2 directories + find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz + ''' + + archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + stage('ppc64le integration-cli') { + when { + beforeAgent true + not { changeRequest() } + expression { params.ppc64le } + } + agent { label 'ppc64le-ubuntu-1604' } + // ppc64le machines run on Docker 18.06, and buildkit has some + // bugs on that version. Build and use buildx instead. + environment { + USE_BUILDX = '1' + DOCKER_BUILDKIT = '0' + } + + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + sh ''' + echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}" + curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \ + && bash ${WORKSPACE}/check-config.sh || true + ''' + } + } + stage("Build dev image") { + steps { + sh ''' + make bundles/buildx + bundles/buildx build --load --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} . + ''' + } + } + stage("Integration-cli tests") { + environment { TEST_SKIP_INTEGRATION = '1' } + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e TEST_SKIP_INTEGRATION \ + -e TIMEOUT \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/make.sh \ + dynbinary \ + test-integration + ''' + } + post { + always { + junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true + } + } + } + } + + post { + always { + sh ''' + echo "Ensuring container killed." + docker rm -vf docker-pr$BUILD_NUMBER || true + ''' + + sh ''' + echo "Chowning /workspace to jenkins user" + docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace + ''' + + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + sh ''' + bundleName=ppc64le-integration-cli + echo "Creating ${bundleName}-bundles.tar.gz" + # exclude overlay2 directories + find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz + ''' + + archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + stage('arm64') { + when { + beforeAgent true + expression { params.arm64 } + } + agent { label 'arm64 && ubuntu-2004' } + environment { + TEST_SKIP_INTEGRATION_CLI = '1' + } + + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + sh ''' + echo "check-config.sh version: ${CHECK_CONFIG_COMMIT}" + curl -fsSL -o ${WORKSPACE}/check-config.sh "https://raw.githubusercontent.com/moby/moby/${CHECK_CONFIG_COMMIT}/contrib/check-config.sh" \ + && bash ${WORKSPACE}/check-config.sh || true + ''' + } + } + stage("Build dev image") { + steps { + sh 'docker build --force-rm --build-arg APT_MIRROR -t docker:${GIT_COMMIT} .' + } + } + stage("Unit tests") { + steps { + sh ''' + sudo modprobe ip6table_filter + ''' + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/test/unit + ''' + } + post { + always { + junit testResults: 'bundles/junit-report*.xml', allowEmptyResults: true + } + } + } + stage("Integration tests") { + environment { TEST_SKIP_INTEGRATION_CLI = '1' } + steps { + sh ''' + docker run --rm -t --privileged \ + -v "$WORKSPACE/bundles:/go/src/github.com/docker/docker/bundles" \ + --name docker-pr$BUILD_NUMBER \ + -e DOCKER_EXPERIMENTAL \ + -e DOCKER_GITCOMMIT=${GIT_COMMIT} \ + -e DOCKER_GRAPHDRIVER \ + -e TESTDEBUG \ + -e TEST_SKIP_INTEGRATION_CLI \ + -e TIMEOUT \ + -e VALIDATE_REPO=${GIT_URL} \ + -e VALIDATE_BRANCH=${CHANGE_TARGET} \ + docker:${GIT_COMMIT} \ + hack/make.sh \ + dynbinary \ + test-integration + ''' + } + post { + always { + junit testResults: 'bundles/**/*-report.xml', allowEmptyResults: true + } + } + } + } + + post { + always { + sh ''' + echo "Ensuring container killed." + docker rm -vf docker-pr$BUILD_NUMBER || true + ''' + + sh ''' + echo "Chowning /workspace to jenkins user" + docker run --rm -v "$WORKSPACE:/workspace" busybox chown -R "$(id -u):$(id -g)" /workspace + ''' + + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + sh ''' + bundleName=arm64-integration + echo "Creating ${bundleName}-bundles.tar.gz" + # exclude overlay2 directories + find bundles -path '*/root/*overlay2' -prune -o -type f \\( -name '*-report.json' -o -name '*.log' -o -name '*.prof' -o -name '*-report.xml' \\) -print | xargs tar -czf ${bundleName}-bundles.tar.gz + ''' + + archiveArtifacts artifacts: '*-bundles.tar.gz', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + stage('win-RS1') { + when { + beforeAgent true + // Skip this stage on PRs unless the windowsRS1 checkbox is selected + anyOf { + not { changeRequest() } + expression { params.windowsRS1 } + } + } + environment { + DOCKER_BUILDKIT = '0' + DOCKER_DUT_DEBUG = '1' + SKIP_VALIDATION_TESTS = '1' + SOURCES_DRIVE = 'd' + SOURCES_SUBDIR = 'gopath' + TESTRUN_DRIVE = 'd' + TESTRUN_SUBDIR = "CI" + WINDOWS_BASE_IMAGE = 'mcr.microsoft.com/windows/servercore' + WINDOWS_BASE_IMAGE_TAG = 'ltsc2016' + } + agent { + node { + customWorkspace 'd:\\gopath\\src\\github.com\\docker\\docker' + label 'windows-2016' + } + } + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + } + } + stage("Run tests") { + steps { + powershell ''' + $ErrorActionPreference = 'Stop' + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + Invoke-WebRequest https://github.com/moby/docker-ci-zap/blob/master/docker-ci-zap.exe?raw=true -OutFile C:/Windows/System32/docker-ci-zap.exe + ./hack/ci/windows.ps1 + exit $LastExitCode + ''' + } + } + } + post { + always { + junit testResults: 'bundles/junit-report-*.xml', allowEmptyResults: true + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + powershell ''' + cd $env:WORKSPACE + $bundleName="windowsRS1-integration" + Write-Host -ForegroundColor Green "Creating ${bundleName}-bundles.zip" + + # archiveArtifacts does not support env-vars to , so save the artifacts in a fixed location + Compress-Archive -Path "bundles/CIDUT.out", "bundles/CIDUT.err", "bundles/junit-report-*.xml" -CompressionLevel Optimal -DestinationPath "${bundleName}-bundles.zip" + ''' + + archiveArtifacts artifacts: '*-bundles.zip', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + stage('win-RS5') { + when { + beforeAgent true + expression { params.windowsRS5 } + } + environment { + DOCKER_BUILDKIT = '0' + DOCKER_DUT_DEBUG = '1' + SKIP_VALIDATION_TESTS = '1' + SOURCES_DRIVE = 'd' + SOURCES_SUBDIR = 'gopath' + TESTRUN_DRIVE = 'd' + TESTRUN_SUBDIR = "CI" + WINDOWS_BASE_IMAGE = 'mcr.microsoft.com/windows/servercore' + WINDOWS_BASE_IMAGE_TAG = 'ltsc2019' + } + agent { + node { + customWorkspace 'd:\\gopath\\src\\github.com\\docker\\docker' + label 'windows-2019' + } + } + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + } + } + stage("Run tests") { + steps { + powershell ''' + $ErrorActionPreference = 'Stop' + Invoke-WebRequest https://github.com/moby/docker-ci-zap/blob/master/docker-ci-zap.exe?raw=true -OutFile C:/Windows/System32/docker-ci-zap.exe + ./hack/ci/windows.ps1 + exit $LastExitCode + ''' + } + } + } + post { + always { + junit testResults: 'bundles/junit-report-*.xml', allowEmptyResults: true + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.tar.gz') { + powershell ''' + cd $env:WORKSPACE + $bundleName="windowsRS5-integration" + Write-Host -ForegroundColor Green "Creating ${bundleName}-bundles.zip" + + # archiveArtifacts does not support env-vars to , so save the artifacts in a fixed location + Compress-Archive -Path "bundles/CIDUT.out", "bundles/CIDUT.err", "bundles/junit-report-*.xml" -CompressionLevel Optimal -DestinationPath "${bundleName}-bundles.zip" + ''' + + archiveArtifacts artifacts: '*-bundles.zip', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + stage('win-2022') { + when { + beforeAgent true + expression { params.windows2022 } + } + environment { + DOCKER_BUILDKIT = '0' + DOCKER_DUT_DEBUG = '1' + SKIP_VALIDATION_TESTS = '1' + SOURCES_DRIVE = 'd' + SOURCES_SUBDIR = 'gopath' + TESTRUN_DRIVE = 'd' + TESTRUN_SUBDIR = "CI" + WINDOWS_BASE_IMAGE = 'mcr.microsoft.com/windows/servercore' + // Available tags can be found at https://mcr.microsoft.com/v2/windows/servercore/tags/list + WINDOWS_BASE_IMAGE_TAG = 'ltsc2022' + } + agent { + node { + customWorkspace 'd:\\gopath\\src\\github.com\\docker\\docker' + label 'windows-2022' + } + } + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + } + } + stage("Run tests") { + steps { + powershell ''' + $ErrorActionPreference = 'Stop' + Invoke-WebRequest https://github.com/moby/docker-ci-zap/blob/master/docker-ci-zap.exe?raw=true -OutFile C:/Windows/System32/docker-ci-zap.exe + ./hack/ci/windows.ps1 + exit $LastExitCode + ''' + } + } + } + post { + always { + junit testResults: 'bundles/junit-report-*.xml', allowEmptyResults: true + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.zip') { + powershell ''' + cd $env:WORKSPACE + $bundleName="win-2022-integration" + Write-Host -ForegroundColor Green "Creating ${bundleName}-bundles.zip" + + # archiveArtifacts does not support env-vars to , so save the artifacts in a fixed location + Compress-Archive -Path "bundles/CIDUT.out", "bundles/CIDUT.err", "bundles/junit-report-*.xml" -CompressionLevel Optimal -DestinationPath "${bundleName}-bundles.zip" + ''' + + archiveArtifacts artifacts: '*-bundles.zip', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + stage('win-2022-c8d') { + when { + beforeAgent true + expression { params.windows2022containerd } + } + environment { + DOCKER_BUILDKIT = '0' + DOCKER_DUT_DEBUG = '1' + SKIP_VALIDATION_TESTS = '1' + SOURCES_DRIVE = 'd' + SOURCES_SUBDIR = 'gopath' + TESTRUN_DRIVE = 'd' + TESTRUN_SUBDIR = "CI" + WINDOWS_BASE_IMAGE = 'mcr.microsoft.com/windows/servercore' + // Available tags can be found at https://mcr.microsoft.com/v2/windows/servercore/tags/list + WINDOWS_BASE_IMAGE_TAG = 'ltsc2022' + DOCKER_WINDOWS_CONTAINERD_RUNTIME = '1' + } + agent { + node { + customWorkspace 'd:\\gopath\\src\\github.com\\docker\\docker' + label 'windows-2022' + } + } + stages { + stage("Print info") { + steps { + sh 'docker version' + sh 'docker info' + } + } + stage("Run tests") { + steps { + powershell ''' + $ErrorActionPreference = 'Stop' + Invoke-WebRequest https://github.com/moby/docker-ci-zap/blob/master/docker-ci-zap.exe?raw=true -OutFile C:/Windows/System32/docker-ci-zap.exe + ./hack/ci/windows.ps1 + exit $LastExitCode + ''' + } + } + } + post { + always { + junit testResults: 'bundles/junit-report-*.xml', allowEmptyResults: true + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE', message: 'Failed to create bundles.zip') { + powershell ''' + cd $env:WORKSPACE + $bundleName="win-2022-c8d-integration" + Write-Host -ForegroundColor Green "Creating ${bundleName}-bundles.zip" + + # archiveArtifacts does not support env-vars to , so save the artifacts in a fixed location + Compress-Archive -Path "bundles/CIDUT.out", "bundles/CIDUT.err", "bundles/containerd.out", "bundles/containerd.err", "bundles/junit-report-*.xml" -CompressionLevel Optimal -DestinationPath "${bundleName}-bundles.zip" + ''' + + archiveArtifacts artifacts: '*-bundles.zip', allowEmptyArchive: true + } + } + cleanup { + sh 'make clean' + deleteDir() + } + } + } + } + } + } +} diff --git a/MAINTAINERS b/MAINTAINERS index 0aa8562a37997..cf1b17fb03b28 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24,21 +24,17 @@ # subsystem maintainers accountable. If ownership is unclear, they are the de facto owners. people = [ - "aaronlehmann", "akihirosuda", "anusha", "coolljt0725", "cpuguy83", - "crosbymichael", - "dnephin", - "duglin", "estesp", - "jhowardmsft", "johnstep", "justincormack", + "kolyshkin", "mhbauer", - "mlaventure", "runcom", + "samuelkarp", "stevvooe", "thajeztah", "tianon", @@ -50,15 +46,6 @@ "yongtang" ] - [Org."Docs maintainers"] - - # TODO Describe the docs maintainers role. - - people = [ - "misty", - "thajeztah" - ] - [Org.Curators] # The curators help ensure that incoming issues and pull requests are properly triaged and @@ -74,13 +61,12 @@ people = [ "alexellis", "andrewhsu", - "anonymuse", - "chanwit", "fntlnz", "gianarb", + "olljanat", "programmerq", - "rheinwein", "ripcurld", + "samwhited", "thajeztah" ] @@ -91,6 +77,12 @@ # Thank you! people = [ + # Aaron Lehmann was a maintainer for swarmkit, the registry, and the engine, + # and contributed many improvements, features, and bugfixes in those areas, + # among which "automated service rollbacks", templated secrets and configs, + # and resumable image layer downloads. + "aaronlehmann", + # Harald Albers is the mastermind behind the bash completion scripts for the # Docker CLI. The completion scripts moved to the Docker CLI repository, so # you can now find him perform his magic in the https://github.com/docker/cli repository. @@ -110,6 +102,30 @@ # and tweets as @calavera. "calavera", + # Michael Crosby was "chief maintainer" of the Docker project. + # During his time as a maintainer, Michael contributed to many + # milestones of the project; he was release captain of Docker v1.0.0, + # started the development of "libcontainer" (what later became runc) + # and containerd, as well as demoing cool hacks such as live migrating + # a game server container with checkpoint/restore. + # + # Michael is currently a maintainer of containerd, but you may see + # him around in other projects on GitHub. + "crosbymichael", + + # Before becoming a maintainer, Daniel Nephin was a core contributor + # to "Fig" (now known as Docker Compose). As a maintainer for both the + # Engine and Docker CLI, Daniel contributed many features, among which + # the `docker stack` commands, allowing users to deploy their Docker + # Compose projects as a Swarm service. + "dnephin", + + # Doug Davis contributed many features and fixes for the classic builder, + # such as "wildcard" copy, the dockerignore file, custom paths/names + # for the Dockerfile, as well as enhancements to the API and documentation. + # Follow Doug on Twitter, where he tweets as @duginabox. + "duglin", + # As a maintainer, Erik was responsible for the "builder", and # started the first designs for the new networking model in # Docker. Erik is now working on all kinds of plugins for Docker @@ -118,7 +134,7 @@ # still stumble into him in our issue tracker, or on IRC. "erikh", - # Evan Hazlett is the creator of of the Shipyard and Interlock open source projects, + # Evan Hazlett is the creator of the Shipyard and Interlock open source projects, # and the author of "Orca", which became the foundation of Docker Universal Control # Plane (UCP). As a maintainer, Evan helped integrating SwarmKit (secrets, tasks) # into the Docker engine. @@ -160,6 +176,16 @@ # check out her open source projects on GitHub https://github.com/jessfraz (a must-try). "jessfraz", + # As a maintainer, John Howard managed to make the impossible possible; + # to run Docker on Windows. After facing many challenges, teaching + # fellow-maintainers that 'Windows is not Linux', and many changes in + # Windows Server to facilitate containers, native Windows containers + # saw the light of day in 2015. + # + # John is now enjoying life without containers: playing piano, painting, + # and walking his dogs, but you may occasionally see him drop by on GitHub. + "lowenna", + # Alexander Morozov contributed many features to Docker, worked on the premise of # what later became containerd (and worked on that too), and made a "stupid" Go # vendor tool specifically for docker/docker needs: vndr (https://github.com/LK4D4/vndr). @@ -173,6 +199,13 @@ # Swarm mode networking. "mavenugo", + # As a maintainer, Kenfe-Mickaël Laventure worked on the container runtime, + # integrating containerd 1.0 with the daemon, and adding support for custom + # OCI runtimes, as well as implementing the `docker prune` subcommands, + # which was a welcome feature to be added. You can keep up with Mickaél on + # Twitter (@kmlaventure). + "mlaventure", + # As a docs maintainer, Mary Anthony contributed greatly to the Docker # docs. She wrote the Docker Contributor Guide and Getting Started # Guides. She helped create a doc build system independent of @@ -247,7 +280,7 @@ [people.akihirosuda] Name = "Akihiro Suda" - Email = "suda.akihiro@lab.ntt.co.jp" + Email = "akihiro.suda.cz@hco.ntt.co.jp" GitHub = "AkihiroSuda" [people.aluzzardi] @@ -265,11 +298,6 @@ Email = "andrewhsu@docker.com" GitHub = "andrewhsu" - [people.anonymuse] - Name = "Jesse White" - Email = "anonymuse@gmail.com" - GitHub = "anonymuse" - [people.anusha] Name = "Anusha Ragunathan" Email = "anusha@docker.com" @@ -290,11 +318,6 @@ Email = "cpuguy83@gmail.com" GitHub = "cpuguy83" - [people.chanwit] - Name = "Chanwit Kaewkasi" - Email = "chanwit@gmail.com" - GitHub = "chanwit" - [people.crosbymichael] Name = "Michael Crosby" Email = "crosbymichael@gmail.com" @@ -345,11 +368,6 @@ Email = "james@lovedthanlost.net" GitHub = "jamtur01" - [people.jhowardmsft] - Name = "John Howard" - Email = "jhoward@microsoft.com" - GitHub = "jhowardmsft" - [people.jessfraz] Name = "Jessie Frazelle" Email = "jess@linux.com" @@ -365,11 +383,21 @@ Email = "justin.cormack@docker.com" GitHub = "justincormack" + [people.kolyshkin] + Name = "Kir Kolyshkin" + Email = "kolyshkin@gmail.com" + GitHub = "kolyshkin" + [people.lk4d4] Name = "Alexander Morozov" Email = "lk4d4@docker.com" GitHub = "lk4d4" + [people.lowenna] + Name = "John Howard" + Email = "github@lowenna.com" + GitHub = "lowenna" + [people.mavenugo] Name = "Madhu Venugopal" Email = "madhu@docker.com" @@ -380,11 +408,6 @@ Email = "mbauer@us.ibm.com" GitHub = "mhbauer" - [people.misty] - Name = "Misty Stanley-Jones" - Email = "misty@docker.com" - GitHub = "mistyhacks" - [people.mlaventure] Name = "Kenfe-Mickaël Laventure" Email = "mickael.laventure@gmail.com" @@ -400,16 +423,16 @@ Email = "mrjana@docker.com" GitHub = "mrjana" + [people.olljanat] + Name = "Olli Janatuinen" + Email = "olli.janatuinen@gmail.com" + GitHub = "olljanat" + [people.programmerq] Name = "Jeff Anderson" Email = "jeff@docker.com" GitHub = "programmerq" - [people.rheinwein] - Name = "Laura Frank" - Email = "laura@codeship.com" - GitHub = "rheinwein" - [people.ripcurld] Name = "Boaz Shuster" Email = "ripcurld.github@gmail.com" @@ -420,6 +443,16 @@ Email = "runcom@redhat.com" GitHub = "runcom" + [people.samuelkarp] + Name = "Samuel Karp" + Email = "skarp@amazon.com" + GitHub = "samuelkarp" + + [people.samwhited] + Name = "Sam Whited" + Email = "sam@samwhited.com" + GitHub = "samwhited" + [people.shykes] Name = "Solomon Hykes" Email = "solomon@docker.com" diff --git a/Makefile b/Makefile index 0efa32bc10c40..c7cd97f79a4a9 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,22 @@ .PHONY: all binary dynbinary build cross help install manpages run shell test test-docker-py test-integration test-unit validate win +BUILDX_VERSION ?= v0.6.0 + +ifdef USE_BUILDX +BUILDX ?= $(shell command -v buildx) +BUILDX ?= $(shell command -v docker-buildx) +DOCKER_BUILDX_CLI_PLUGIN_PATH ?= ~/.docker/cli-plugins/docker-buildx +BUILDX ?= $(shell if [ -x "$(DOCKER_BUILDX_CLI_PLUGIN_PATH)" ]; then echo $(DOCKER_BUILDX_CLI_PLUGIN_PATH); fi) +endif + +ifndef USE_BUILDX +DOCKER_BUILDKIT := 1 +export DOCKER_BUILDKIT +endif + +BUILDX ?= bundles/buildx +DOCKER ?= docker + # set the graph driver as the current graphdriver if not set DOCKER_GRAPHDRIVER := $(if $(DOCKER_GRAPHDRIVER),$(DOCKER_GRAPHDRIVER),$(shell docker info 2>&1 | grep "Storage Driver" | sed 's/.*: //')) export DOCKER_GRAPHDRIVER @@ -11,6 +28,12 @@ DOCKERFILE := $(shell bash -c 'source hack/make/.detect-daemon-osarch && echo $$ DOCKER_GITCOMMIT := $(shell git rev-parse --short HEAD || echo unsupported) export DOCKER_GITCOMMIT +# allow overriding the repository and branch that validation scripts are running +# against these are used in hack/validate/.validate to check what changed in the PR. +export VALIDATE_REPO +export VALIDATE_BRANCH +export VALIDATE_ORIGIN_BRANCH + # env vars passed through directly to Docker's build scripts # to allow things like `make KEEPBUNDLE=1 binary` easily # `project/PACKAGERS.md` have some limited documentation of some of these @@ -28,6 +51,7 @@ DOCKER_ENVS := \ -e KEEPBUNDLE \ -e DOCKER_BUILD_ARGS \ -e DOCKER_BUILD_GOGC \ + -e DOCKER_BUILD_OPTS \ -e DOCKER_BUILD_PKGS \ -e DOCKER_BUILDKIT \ -e DOCKER_BASH_COMPLETION_PATH \ @@ -39,19 +63,25 @@ DOCKER_ENVS := \ -e DOCKER_LDFLAGS \ -e DOCKER_PORT \ -e DOCKER_REMAP_ROOT \ + -e DOCKER_ROOTLESS \ -e DOCKER_STORAGE_OPTS \ + -e DOCKER_TEST_HOST \ -e DOCKER_USERLANDPROXY \ -e DOCKERD_ARGS \ + -e TEST_FORCE_VALIDATE \ -e TEST_INTEGRATION_DIR \ + -e TEST_SKIP_INTEGRATION \ + -e TEST_SKIP_INTEGRATION_CLI \ + -e TESTDEBUG \ -e TESTDIRS \ -e TESTFLAGS \ + -e TESTFLAGS_INTEGRATION \ + -e TESTFLAGS_INTEGRATION_CLI \ + -e TEST_FILTER \ -e TIMEOUT \ - -e HTTP_PROXY \ - -e HTTPS_PROXY \ - -e NO_PROXY \ - -e http_proxy \ - -e https_proxy \ - -e no_proxy \ + -e VALIDATE_REPO \ + -e VALIDATE_BRANCH \ + -e VALIDATE_ORIGIN_BRANCH \ -e VERSION \ -e PLATFORM \ -e DEFAULT_PRODUCT_LICENSE \ @@ -62,35 +92,35 @@ DOCKER_ENVS := \ # (default to no bind mount if DOCKER_HOST is set) # note: BINDDIR is supported for backwards-compatibility here BIND_DIR := $(if $(BINDDIR),$(BINDDIR),$(if $(DOCKER_HOST),,bundles)) + +# DOCKER_MOUNT can be overriden, but use at your own risk! +ifndef DOCKER_MOUNT DOCKER_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/docker/docker/$(BIND_DIR)") +DOCKER_MOUNT := $(if $(DOCKER_BINDDIR_MOUNT_OPTS),$(DOCKER_MOUNT):$(DOCKER_BINDDIR_MOUNT_OPTS),$(DOCKER_MOUNT)) # This allows the test suite to be able to run without worrying about the underlying fs used by the container running the daemon (e.g. aufs-on-aufs), so long as the host running the container is running a supported fs. # The volume will be cleaned up when the container is removed due to `--rm`. # Note that `BIND_DIR` will already be set to `bundles` if `DOCKER_HOST` is not set (see above BIND_DIR line), in such case this will do nothing since `DOCKER_MOUNT` will already be set. DOCKER_MOUNT := $(if $(DOCKER_MOUNT),$(DOCKER_MOUNT),-v /go/src/github.com/docker/docker/bundles) -v "$(CURDIR)/.git:/go/src/github.com/docker/docker/.git" -# This allows to set the docker-dev container name -DOCKER_CONTAINER_NAME := $(if $(CONTAINER_NAME),--name $(CONTAINER_NAME),) - DOCKER_MOUNT_CACHE := -v docker-dev-cache:/root/.cache DOCKER_MOUNT_CLI := $(if $(DOCKER_CLI_PATH),-v $(shell dirname $(DOCKER_CLI_PATH)):/usr/local/cli,) DOCKER_MOUNT_BASH_COMPLETION := $(if $(DOCKER_BASH_COMPLETION_PATH),-v $(shell dirname $(DOCKER_BASH_COMPLETION_PATH)):/usr/local/completion/bash,) DOCKER_MOUNT := $(DOCKER_MOUNT) $(DOCKER_MOUNT_CACHE) $(DOCKER_MOUNT_CLI) $(DOCKER_MOUNT_BASH_COMPLETION) +endif # ifndef DOCKER_MOUNT + +# This allows to set the docker-dev container name +DOCKER_CONTAINER_NAME := $(if $(CONTAINER_NAME),--name $(CONTAINER_NAME),) -GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null) -GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g") -DOCKER_IMAGE := docker-dev$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN)) +DOCKER_IMAGE := docker-dev DOCKER_PORT_FORWARD := $(if $(DOCKER_PORT),-p "$(DOCKER_PORT)",) -DOCKER_FLAGS := docker run --rm -i --privileged $(DOCKER_CONTAINER_NAME) $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD) +DOCKER_FLAGS := $(DOCKER) run --rm -i --privileged $(DOCKER_CONTAINER_NAME) $(DOCKER_ENVS) $(DOCKER_MOUNT) $(DOCKER_PORT_FORWARD) BUILD_APT_MIRROR := $(if $(DOCKER_BUILD_APT_MIRROR),--build-arg APT_MIRROR=$(DOCKER_BUILD_APT_MIRROR)) export BUILD_APT_MIRROR SWAGGER_DOCS_PORT ?= 9000 -INTEGRATION_CLI_MASTER_IMAGE := $(if $(INTEGRATION_CLI_MASTER_IMAGE), $(INTEGRATION_CLI_MASTER_IMAGE), integration-cli-master) -INTEGRATION_CLI_WORKER_IMAGE := $(if $(INTEGRATION_CLI_WORKER_IMAGE), $(INTEGRATION_CLI_WORKER_IMAGE), integration-cli-worker) - define \n @@ -106,20 +136,51 @@ endif DOCKER_RUN_DOCKER := $(DOCKER_FLAGS) "$(DOCKER_IMAGE)" +DOCKER_BUILD_ARGS += --build-arg=GO_VERSION +ifdef DOCKER_SYSTEMD +DOCKER_BUILD_ARGS += --build-arg=SYSTEMD=true +endif + +BUILD_OPTS := ${BUILD_APT_MIRROR} ${DOCKER_BUILD_ARGS} ${DOCKER_BUILD_OPTS} -f "$(DOCKERFILE)" +ifdef USE_BUILDX +BUILD_OPTS += $(BUILDX_BUILD_EXTRA_OPTS) +BUILD_CMD := $(BUILDX) build +else +BUILD_CMD := $(DOCKER) build +endif + +# This is used for the legacy "build" target and anything still depending on it +BUILD_CROSS = +ifdef DOCKER_CROSS +BUILD_CROSS = --build-arg CROSS=$(DOCKER_CROSS) +endif +ifdef DOCKER_CROSSPLATFORMS +BUILD_CROSS = --build-arg CROSS=true +endif + +VERSION_AUTOGEN_ARGS = --build-arg VERSION --build-arg DOCKER_GITCOMMIT --build-arg PRODUCT --build-arg PLATFORM --build-arg DEFAULT_PRODUCT_LICENSE + default: binary -all: build ## validate all checks, build linux binaries, run all tests\ncross build non-linux binaries and generate archives +all: build ## validate all checks, build linux binaries, run all tests,\ncross build non-linux binaries, and generate archives $(DOCKER_RUN_DOCKER) bash -c 'hack/validate/default && hack/make.sh' -binary: build ## build the linux binaries - $(DOCKER_RUN_DOCKER) hack/make.sh binary +# This is only used to work around read-only bind mounts of the source code into +# binary build targets. We end up mounting a tmpfs over autogen which allows us +# to write build-time generated assets even though the source is mounted read-only +# ...But in order to do so, this dir needs to already exist. +autogen: + mkdir -p autogen -dynbinary: build ## build the linux dynbinaries - $(DOCKER_RUN_DOCKER) hack/make.sh dynbinary +binary: buildx autogen ## build statically linked linux binaries + $(BUILD_CMD) $(BUILD_OPTS) --output=bundles/ --target=$@ $(VERSION_AUTOGEN_ARGS) . -build: bundles - $(warning The docker client CLI has moved to github.com/docker/cli. For a dev-test cycle involving the CLI, run:${\n} DOCKER_CLI_PATH=/host/path/to/cli/binary make shell ${\n} then change the cli and compile into a binary at the same location.${\n}) - docker build ${BUILD_APT_MIRROR} ${DOCKER_BUILD_ARGS} -t "$(DOCKER_IMAGE)" -f "$(DOCKERFILE)" . +dynbinary: buildx autogen ## build dynamically linked linux binaries + $(BUILD_CMD) $(BUILD_OPTS) --output=bundles/ --target=$@ $(VERSION_AUTOGEN_ARGS) . + +cross: BUILD_OPTS += --build-arg CROSS=true --build-arg DOCKER_CROSSPLATFORMS +cross: buildx autogen ## cross build the binaries for darwin, freebsd and\nwindows + $(BUILD_CMD) $(BUILD_OPTS) --output=bundles/ --target=$@ $(VERSION_AUTOGEN_ARGS) . bundles: mkdir bundles @@ -131,19 +192,28 @@ clean: clean-cache clean-cache: docker volume rm -f docker-dev-cache -cross: build ## cross build the binaries for darwin, freebsd and\nwindows - $(DOCKER_RUN_DOCKER) hack/make.sh dynbinary binary cross - help: ## this help - @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {gsub("\\\\n",sprintf("\n%22c",""), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) install: ## install the linux binaries KEEPBUNDLE=1 hack/make.sh install-binary run: build ## run the docker daemon in a container $(DOCKER_RUN_DOCKER) sh -c "KEEPBUNDLE=1 hack/make.sh install-binary run" + +.PHONY: build +ifeq ($(BIND_DIR), .) +build: shell_target := --target=dev +else +build: shell_target := --target=final +endif +ifdef USE_BUILDX +build: buildx_load := --load +endif +build: buildx + $(BUILD_CMD) $(BUILD_OPTS) $(shell_target) $(buildx_load) $(BUILD_CROSS) -t "$(DOCKER_IMAGE)" . -shell: build ## start a shell inside the build env +shell: build ## start a shell inside the build env $(DOCKER_RUN_DOCKER) bash test: build test-unit ## run the unit, integration and docker-py tests @@ -154,8 +224,16 @@ test-docker-py: build ## run the docker-py tests test-integration-cli: test-integration ## (DEPRECATED) use test-integration +ifneq ($(and $(TEST_SKIP_INTEGRATION),$(TEST_SKIP_INTEGRATION_CLI)),) +test-integration: + @echo Both integrations suites skipped per environment variables +else test-integration: build ## run the integration tests $(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration +endif + +test-integration-flaky: build ## run the stress test for all new integration tests + $(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration-flaky test-unit: build ## run the unit tests $(DOCKER_RUN_DOCKER) hack/test/unit @@ -164,7 +242,7 @@ validate: build ## validate DCO, Seccomp profile generation, gofmt,\n./pkg/ isol $(DOCKER_RUN_DOCKER) hack/validate/all win: build ## cross build the binary for windows - $(DOCKER_RUN_DOCKER) hack/make.sh win + $(DOCKER_RUN_DOCKER) DOCKER_CROSSPLATFORMS=windows/amd64 hack/make.sh cross .PHONY: swagger-gen swagger-gen: @@ -180,19 +258,15 @@ swagger-docs: ## preview the API documentation @docker run --rm -v $(PWD)/api/swagger.yaml:/usr/share/nginx/html/swagger.yaml \ -e 'REDOC_OPTIONS=hide-hostname="true" lazy-rendering' \ -p $(SWAGGER_DOCS_PORT):80 \ - bfirsh/redoc:1.6.2 - -build-integration-cli-on-swarm: build ## build images and binary for running integration-cli on Swarm in parallel - @echo "Building hack/integration-cli-on-swarm (if build fails, please refer to hack/integration-cli-on-swarm/README.md)" - go build -buildmode=pie -o ./hack/integration-cli-on-swarm/integration-cli-on-swarm ./hack/integration-cli-on-swarm/host - @echo "Building $(INTEGRATION_CLI_MASTER_IMAGE)" - docker build -t $(INTEGRATION_CLI_MASTER_IMAGE) hack/integration-cli-on-swarm/agent - @echo "Building $(INTEGRATION_CLI_WORKER_IMAGE) from $(DOCKER_IMAGE)" - $(eval tmp := integration-cli-worker-tmp) -# We mount pkgcache, but not bundle (bundle needs to be baked into the image) -# For avoiding bakings DOCKER_GRAPHDRIVER and so on to image, we cannot use $(DOCKER_ENVS) here - docker run -t -d --name $(tmp) -e DOCKER_GITCOMMIT -e BUILDFLAGS --privileged $(DOCKER_IMAGE) top - docker exec $(tmp) hack/make.sh build-integration-test-binary dynbinary - docker exec $(tmp) go build -buildmode=pie -o /worker github.com/docker/docker/hack/integration-cli-on-swarm/agent/worker - docker commit -c 'ENTRYPOINT ["/worker"]' $(tmp) $(INTEGRATION_CLI_WORKER_IMAGE) - docker rm -f $(tmp) + bfirsh/redoc:1.14.0 + +.PHONY: buildx +ifdef USE_BUILDX +ifeq ($(BUILDX), bundles/buildx) +buildx: bundles/buildx ## build buildx cli tool +endif +endif + +bundles/buildx: bundles ## build buildx CLI tool + curl -fsSL https://raw.githubusercontent.com/moby/buildkit/70deac12b5857a1aa4da65e90b262368e2f71500/hack/install-buildx | VERSION="$(BUILDX_VERSION)" BINDIR="$(@D)" bash + $@ version diff --git a/NOTICE b/NOTICE index 0c74e15b057f0..58b19b6d15b99 100644 --- a/NOTICE +++ b/NOTICE @@ -3,7 +3,7 @@ Copyright 2012-2017 Docker, Inc. This product includes software developed at Docker, Inc. (https://www.docker.com). -This product contains software (https://github.com/kr/pty) developed +This product contains software (https://github.com/creack/pty) developed by Keith Rarick, licensed under the MIT License. The following is courtesy of our legal counsel: diff --git a/ROADMAP.md b/ROADMAP.md index e2e6b2b960f80..775d0734ec93d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -35,34 +35,83 @@ issue, in the Slack channel, or in person at the Moby Summits that happen every ## 1.1 Runtime improvements -We introduced [`runC`](https://runc.io) as a standalone low-level tool for container -execution in 2015, the first stage in spinning out parts of the Engine into standalone tools. +Over time we have accumulated a lot of functionality in the container runtime +aspect of Moby while also growing in other areas. Much of the container runtime +pieces are now duplicated work available in other, lower level components such +as [containerd](https://containerd.io). -As runC continued evolving, and the OCI specification along with it, we created -[`containerd`](https://github.com/containerd/containerd), a daemon to control and monitor `runC`. -In late 2016 this was relaunched as the `containerd` 1.0 track, aiming to provide a common runtime -for the whole spectrum of container systems, including Kubernetes, with wide community support. -This change meant that there was an increased scope for `containerd`, including image management -and storage drivers. +Moby currently only utilizes containerd for basic runtime state management, e.g. starting +and stopping a container, which is what the pre-containerd 1.0 daemon provided. +Now that containerd is a full-fledged container runtime which supports full +container life-cycle management, we would like to start relying more on containerd +and removing the bits in Moby which are now duplicated. This will necessitate +a significant effort to refactor and even remove large parts of Moby's codebase. -Moby will rely on a long-running `containerd` companion daemon for all container execution -related operations. This could open the door in the future for Engine restarts without interrupting -running containers. The switch over to containerd 1.0 is an important goal for the project, and -will result in a significant simplification of the functions implemented in this repository. +Tracking issues: -## 1.2 Internal decoupling +- [#38043](https://github.com/moby/moby/issues/38043) Proposal: containerd image integration + +## 1.2 Image Builder + +Work is ongoing to integrate [BuildKit](https://github.com/moby/buildkit) into +Moby and replace the "v0" build implementation. Buildkit offers better cache +management, parallelizable build steps, and better extensibility while also +keeping builds portable, a chief tenent of Moby's builder. + +Upon completion of this effort, users will have a builder that performs better +while also being more extensible, enabling users to provide their own custom +syntax which can be either Dockerfile-like or something completely different. + +See [buildpacks on buildkit](https://github.com/tonistiigi/buildkit-pack) as an +example of this extensibility. + +New features for the builder and Dockerfile should be implemented first in the +BuildKit backend using an external Dockerfile implementation from the container +images. This allows everyone to test and evaluate the feature without upgrading +their daemon. New features should go to the experimental channel first, and can be +part of the `docker/dockerfile:experimental` image. From there they graduate to +`docker/dockerfile:latest` and binary releases. The Dockerfile frontend source +code is temporarily located at +[https://github.com/moby/buildkit/tree/master/frontend/dockerfile](https://github.com/moby/buildkit/tree/master/frontend/dockerfile) +with separate new features defined with go build tags. + +Tracking issues: + +- [#32925](https://github.com/moby/moby/issues/32925) discussion: builder future: buildkit + +## 1.3 Rootless Mode + +Running the daemon requires elevated privileges for many tasks. We would like to +support running the daemon as a normal, unprivileged user without requiring `suid` +binaries. + +Tracking issues: + +- [#37375](https://github.com/moby/moby/issues/37375) Proposal: allow running `dockerd` as an unprivileged user (aka rootless mode) + +## 1.4 Testing + +Moby has many tests, both unit and integration. Moby needs more tests which can +cover the full spectrum functionality and edge cases out there. + +Tests in the `integration-cli` folder should also be migrated into (both in +location and style) the `integration` folder. These newer tests are simpler to +run in isolation, simpler to read, simpler to write, and more fully exercise the +API. Meanwhile tests of the docker CLI should generally live in docker/cli. + +Tracking issues: + +- [#32866](https://github.com/moby/moby/issues/32866) Replace integration-cli suite with API test suite + +## 1.5 Internal decoupling A lot of work has been done in trying to decouple Moby internals. This process of creating standalone projects with a well defined function that attract a dedicated community should continue. As well as integrating `containerd` we would like to integrate [BuildKit](https://github.com/moby/buildkit) as the next standalone component. - We see gRPC as the natural communication layer between decoupled components. -## 1.3 Custom assembly tooling - -We have been prototyping the Moby [assembly tool](https://github.com/moby/tool) which was originally -developed for LinuxKit and intend to turn it into a more generic packaging and assembly mechanism -that can build not only the default version of Moby, as distribution packages or other useful forms, -but can also build very different container systems, themselves built of cooperating daemons built in -and running in containers. We intend to merge this functionality into this repo. +In addition to pushing out large components into other projects, much of the +internal code structure, and in particular the +["Daemon"](https://godoc.org/github.com/docker/docker/daemon#Daemon) object, +should be split into smaller, more manageable, and more testable components. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000..5b2262c282a00 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Reporting security issues + +The Moby maintainers take security seriously. If you discover a security issue, please bring it to their attention right away! + +### Reporting a Vulnerability + +Please **DO NOT** file a public issue, instead send your report privately to security@docker.com. + +Security reports are greatly appreciated and we will publicly thank you for it, although we keep your name confidential if you request it. We also like to send gifts—if you're into schwag, make sure to let us know. We currently do not offer a paid security bounty program, but are not ruling it out in the future. diff --git a/TESTING.md b/TESTING.md index b2c53769ca052..fb0e4e9e6e078 100644 --- a/TESTING.md +++ b/TESTING.md @@ -28,7 +28,7 @@ Most code changes will fall into one of the following categories. ### Writing tests for new features New code should be covered by unit tests. If the code is difficult to test with -a unit tests then that is a good sign that it should be refactored to make it +unit tests, then that is a good sign that it should be refactored to make it easier to reuse and maintain. Consider accepting unexported interfaces instead of structs so that fakes can be provided for dependencies. @@ -44,16 +44,23 @@ case. Error cases should be handled by unit tests. Bugs fixes should include a unit test case which exercises the bug. -A bug fix may also include new assertions in an existing integration tests for the +A bug fix may also include new assertions in existing integration tests for the API endpoint. +### Writing new integration tests + +Note the `integration-cli` tests are deprecated; new tests will be rejected by +the CI. + +Instead, implement new tests under `integration/`. + ### Integration tests environment considerations -When adding new tests or modifying existing test under `integration/`, testing +When adding new tests or modifying existing tests under `integration/`, testing environment should be properly considered. `skip.If` from [gotest.tools/skip](https://godoc.org/gotest.tools/skip) can be used to make the test run conditionally. Full testing environment conditions can be found at -[environment.go](https://github.com/moby/moby/blob/cb37987ee11655ed6bbef663d245e55922354c68/internal/test/environment/environment.go) +[environment.go](https://github.com/moby/moby/blob/6b6eeed03b963a27085ea670f40cd5ff8a61f32e/testutil/environment/environment.go) Here is a quick example. If the test needs to interact with a docker daemon on the same host, the following condition should be checked within the test code @@ -67,6 +74,8 @@ If a remote daemon is detected, the test will be skipped. ## Running tests +### Unit Tests + To run the unit test suite: ``` @@ -82,8 +91,36 @@ The following environment variables may be used to run a subset of tests: * `TESTFLAGS` - flags passed to `go test`, to run tests which match a pattern use `TESTFLAGS="-test.run TestNameOrPrefix"` +### Integration Tests + To run the integration test suite: ``` make test-integration ``` + +This make target runs both the "integration" suite and the "integration-cli" +suite. + +You can specify which integration test dirs to build and run by specifying +the list of dirs in the TEST_INTEGRATION_DIR environment variable. + +You can also explicitly skip either suite by setting (any value) in +TEST_SKIP_INTEGRATION and/or TEST_SKIP_INTEGRATION_CLI environment variables. + +Flags specific to each suite can be set in the TESTFLAGS_INTEGRATION and +TESTFLAGS_INTEGRATION_CLI environment variables. + +If all you want is to specify a test filter to run, you can set the +`TEST_FILTER` environment variable. This ends up getting passed directly to `go +test -run` (or `go test -check-f`, depending on the test suite). It will also +automatically set the other above mentioned environment variables accordingly. + +### Go Version + +You can change a version of golang used for building stuff that is being tested +by setting `GO_VERSION` variable, for example: + +``` +make GO_VERSION=1.12.8 test +``` diff --git a/api/common.go b/api/common.go index 4582eb92e0c32..bee9b875a889b 100644 --- a/api/common.go +++ b/api/common.go @@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api" // Common constants for daemon and client. const ( // DefaultVersion of Current REST API - DefaultVersion = "1.39" + DefaultVersion = "1.42" // NoBaseImageSpecifier is the symbol used by the FROM // command to specify that no base image is to be used. diff --git a/api/common_unix.go b/api/common_unix.go index 504b0c90d7bbf..19fc63d6589aa 100644 --- a/api/common_unix.go +++ b/api/common_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package api // import "github.com/docker/docker/api" diff --git a/api/server/backend/build/backend.go b/api/server/backend/build/backend.go index 33df264cca985..0d81c0138a841 100644 --- a/api/server/backend/build/backend.go +++ b/api/server/backend/build/backend.go @@ -3,17 +3,19 @@ package build // import "github.com/docker/docker/api/server/backend/build" import ( "context" "fmt" + "strconv" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" + "github.com/docker/docker/api/types/events" "github.com/docker/docker/builder" buildkit "github.com/docker/docker/builder/builder-next" - "github.com/docker/docker/builder/fscache" + daemonevents "github.com/docker/docker/daemon/events" "github.com/docker/docker/image" "github.com/docker/docker/pkg/stringid" "github.com/pkg/errors" - "golang.org/x/sync/errgroup" + "google.golang.org/grpc" ) // ImageComponent provides an interface for working with images @@ -30,14 +32,21 @@ type Builder interface { // Backend provides build functionality to the API router type Backend struct { builder Builder - fsCache *fscache.FSCache imageComponent ImageComponent buildkit *buildkit.Builder + eventsService *daemonevents.Events } // NewBackend creates a new build backend from components -func NewBackend(components ImageComponent, builder Builder, fsCache *fscache.FSCache, buildkit *buildkit.Builder) (*Backend, error) { - return &Backend{imageComponent: components, builder: builder, fsCache: fsCache, buildkit: buildkit}, nil +func NewBackend(components ImageComponent, builder Builder, buildkit *buildkit.Builder, es *daemonevents.Events) (*Backend, error) { + return &Backend{imageComponent: components, builder: builder, buildkit: buildkit, eventsService: es}, nil +} + +// RegisterGRPC registers buildkit controller to the grpc server. +func (b *Backend) RegisterGRPC(s *grpc.Server) { + if b.buildkit != nil { + b.buildkit.RegisterGRPC(s) + } } // Build builds an image from a Source @@ -82,6 +91,8 @@ func (b *Backend) Build(ctx context.Context, config backend.BuildConfig) (string if !useBuildKit { stdout := config.ProgressWriter.StdoutFormatter fmt.Fprintf(stdout, "Successfully built %s\n", stringid.TruncateID(imageID)) + } + if imageID != "" { err = tagger.TagImages(image.ID(imageID)) } return imageID, err @@ -89,34 +100,16 @@ func (b *Backend) Build(ctx context.Context, config backend.BuildConfig) (string // PruneCache removes all cached build sources func (b *Backend) PruneCache(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) { - eg, ctx := errgroup.WithContext(ctx) - - var fsCacheSize uint64 - eg.Go(func() error { - var err error - fsCacheSize, err = b.fsCache.Prune(ctx) - if err != nil { - return errors.Wrap(err, "failed to prune fscache") - } - return nil - }) - - var buildCacheSize int64 - var cacheIDs []string - eg.Go(func() error { - var err error - buildCacheSize, cacheIDs, err = b.buildkit.Prune(ctx, opts) - if err != nil { - return errors.Wrap(err, "failed to prune build cache") - } - return nil - }) - - if err := eg.Wait(); err != nil { - return nil, err + buildCacheSize, cacheIDs, err := b.buildkit.Prune(ctx, opts) + if err != nil { + return nil, errors.Wrap(err, "failed to prune build cache") } - - return &types.BuildCachePruneReport{SpaceReclaimed: fsCacheSize + uint64(buildCacheSize), CachesDeleted: cacheIDs}, nil + b.eventsService.Log("prune", events.BuilderEventType, events.Actor{ + Attributes: map[string]string{ + "reclaimed": strconv.FormatInt(buildCacheSize, 10), + }, + }) + return &types.BuildCachePruneReport{SpaceReclaimed: uint64(buildCacheSize), CachesDeleted: cacheIDs}, nil } // Cancel cancels the build by ID diff --git a/api/server/httputils/errors.go b/api/server/httputils/errors.go deleted file mode 100644 index a21affff3439f..0000000000000 --- a/api/server/httputils/errors.go +++ /dev/null @@ -1,131 +0,0 @@ -package httputils // import "github.com/docker/docker/api/server/httputils" - -import ( - "fmt" - "net/http" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/versions" - "github.com/docker/docker/errdefs" - "github.com/gorilla/mux" - "github.com/sirupsen/logrus" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" -) - -type causer interface { - Cause() error -} - -// GetHTTPErrorStatusCode retrieves status code from error message. -func GetHTTPErrorStatusCode(err error) int { - if err == nil { - logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling") - return http.StatusInternalServerError - } - - var statusCode int - - // Stop right there - // Are you sure you should be adding a new error class here? Do one of the existing ones work? - - // Note that the below functions are already checking the error causal chain for matches. - switch { - case errdefs.IsNotFound(err): - statusCode = http.StatusNotFound - case errdefs.IsInvalidParameter(err): - statusCode = http.StatusBadRequest - case errdefs.IsConflict(err) || errdefs.IsAlreadyExists(err): - statusCode = http.StatusConflict - case errdefs.IsUnauthorized(err): - statusCode = http.StatusUnauthorized - case errdefs.IsUnavailable(err): - statusCode = http.StatusServiceUnavailable - case errdefs.IsForbidden(err): - statusCode = http.StatusForbidden - case errdefs.IsNotModified(err): - statusCode = http.StatusNotModified - case errdefs.IsNotImplemented(err): - statusCode = http.StatusNotImplemented - case errdefs.IsSystem(err) || errdefs.IsUnknown(err) || errdefs.IsDataLoss(err) || errdefs.IsDeadline(err) || errdefs.IsCancelled(err): - statusCode = http.StatusInternalServerError - default: - statusCode = statusCodeFromGRPCError(err) - if statusCode != http.StatusInternalServerError { - return statusCode - } - - if e, ok := err.(causer); ok { - return GetHTTPErrorStatusCode(e.Cause()) - } - - logrus.WithFields(logrus.Fields{ - "module": "api", - "error_type": fmt.Sprintf("%T", err), - }).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err) - } - - if statusCode == 0 { - statusCode = http.StatusInternalServerError - } - - return statusCode -} - -func apiVersionSupportsJSONErrors(version string) bool { - const firstAPIVersionWithJSONErrors = "1.23" - return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors) -} - -// MakeErrorHandler makes an HTTP handler that decodes a Docker error and -// returns it in the response. -func MakeErrorHandler(err error) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - statusCode := GetHTTPErrorStatusCode(err) - vars := mux.Vars(r) - if apiVersionSupportsJSONErrors(vars["version"]) { - response := &types.ErrorResponse{ - Message: err.Error(), - } - WriteJSON(w, statusCode, response) - } else { - http.Error(w, grpc.ErrorDesc(err), statusCode) - } - } -} - -// statusCodeFromGRPCError returns status code according to gRPC error -func statusCodeFromGRPCError(err error) int { - switch grpc.Code(err) { - case codes.InvalidArgument: // code 3 - return http.StatusBadRequest - case codes.NotFound: // code 5 - return http.StatusNotFound - case codes.AlreadyExists: // code 6 - return http.StatusConflict - case codes.PermissionDenied: // code 7 - return http.StatusForbidden - case codes.FailedPrecondition: // code 9 - return http.StatusBadRequest - case codes.Unauthenticated: // code 16 - return http.StatusUnauthorized - case codes.OutOfRange: // code 11 - return http.StatusBadRequest - case codes.Unimplemented: // code 12 - return http.StatusNotImplemented - case codes.Unavailable: // code 14 - return http.StatusServiceUnavailable - default: - if e, ok := err.(causer); ok { - return statusCodeFromGRPCError(e.Cause()) - } - // codes.Canceled(1) - // codes.Unknown(2) - // codes.DeadlineExceeded(4) - // codes.ResourceExhausted(8) - // codes.Aborted(10) - // codes.Internal(13) - // codes.DataLoss(15) - return http.StatusInternalServerError - } -} diff --git a/api/server/httputils/errors_deprecated.go b/api/server/httputils/errors_deprecated.go new file mode 100644 index 0000000000000..1263f02f9c60f --- /dev/null +++ b/api/server/httputils/errors_deprecated.go @@ -0,0 +1,9 @@ +package httputils // import "github.com/docker/docker/api/server/httputils" +import "github.com/docker/docker/errdefs" + +// GetHTTPErrorStatusCode retrieves status code from error message. +// +// Deprecated: use errdefs.GetHTTPErrorStatusCode +func GetHTTPErrorStatusCode(err error) int { + return errdefs.GetHTTPErrorStatusCode(err) +} diff --git a/api/server/httputils/form_test.go b/api/server/httputils/form_test.go index e7e2daea20cc8..2721b23e3919f 100644 --- a/api/server/httputils/form_test.go +++ b/api/server/httputils/form_test.go @@ -23,7 +23,7 @@ func TestBoolValue(t *testing.T) { for c, e := range cases { v := url.Values{} v.Set("test", c) - r, _ := http.NewRequest("POST", "", nil) + r, _ := http.NewRequest(http.MethodPost, "", nil) r.Form = v a := BoolValue(r, "test") @@ -34,14 +34,14 @@ func TestBoolValue(t *testing.T) { } func TestBoolValueOrDefault(t *testing.T) { - r, _ := http.NewRequest("GET", "", nil) + r, _ := http.NewRequest(http.MethodGet, "", nil) if !BoolValueOrDefault(r, "queryparam", true) { t.Fatal("Expected to get true default value, got false") } v := url.Values{} v.Set("param", "") - r, _ = http.NewRequest("GET", "", nil) + r, _ = http.NewRequest(http.MethodGet, "", nil) r.Form = v if BoolValueOrDefault(r, "param", true) { t.Fatal("Expected not to get true") @@ -59,7 +59,7 @@ func TestInt64ValueOrZero(t *testing.T) { for c, e := range cases { v := url.Values{} v.Set("test", c) - r, _ := http.NewRequest("POST", "", nil) + r, _ := http.NewRequest(http.MethodPost, "", nil) r.Form = v a := Int64ValueOrZero(r, "test") @@ -79,7 +79,7 @@ func TestInt64ValueOrDefault(t *testing.T) { for c, e := range cases { v := url.Values{} v.Set("test", c) - r, _ := http.NewRequest("POST", "", nil) + r, _ := http.NewRequest(http.MethodPost, "", nil) r.Form = v a, err := Int64ValueOrDefault(r, "test", -1) @@ -95,7 +95,7 @@ func TestInt64ValueOrDefault(t *testing.T) { func TestInt64ValueOrDefaultWithError(t *testing.T) { v := url.Values{} v.Set("test", "invalid") - r, _ := http.NewRequest("POST", "", nil) + r, _ := http.NewRequest(http.MethodPost, "", nil) r.Form = v _, err := Int64ValueOrDefault(r, "test", -1) diff --git a/api/server/httputils/httputils.go b/api/server/httputils/httputils.go index 7303814997d85..accbb06eb3235 100644 --- a/api/server/httputils/httputils.go +++ b/api/server/httputils/httputils.go @@ -7,9 +7,13 @@ import ( "net/http" "strings" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/versions" "github.com/docker/docker/errdefs" + "github.com/gorilla/mux" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "google.golang.org/grpc/status" ) // APIVersionKey is the client's requested API version. @@ -27,7 +31,7 @@ func HijackConnection(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) { return nil, nil, err } // Flush the options to make sure the client sets the raw mode - conn.Write([]byte{}) + _, _ = conn.Write([]byte{}) return conn, conn, nil } @@ -37,9 +41,9 @@ func CloseStreams(streams ...interface{}) { if tcpc, ok := stream.(interface { CloseWrite() error }); ok { - tcpc.CloseWrite() + _ = tcpc.CloseWrite() } else if closer, ok := stream.(io.Closer); ok { - closer.Close() + _ = closer.Close() } } } @@ -88,6 +92,28 @@ func VersionFromContext(ctx context.Context) string { return "" } +// MakeErrorHandler makes an HTTP handler that decodes a Docker error and +// returns it in the response. +func MakeErrorHandler(err error) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + statusCode := errdefs.GetHTTPErrorStatusCode(err) + vars := mux.Vars(r) + if apiVersionSupportsJSONErrors(vars["version"]) { + response := &types.ErrorResponse{ + Message: err.Error(), + } + _ = WriteJSON(w, statusCode, response) + } else { + http.Error(w, status.Convert(err).Message(), statusCode) + } + } +} + +func apiVersionSupportsJSONErrors(version string) bool { + const firstAPIVersionWithJSONErrors = "1.23" + return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors) +} + // matchesContentType validates the content type against the expected one func matchesContentType(contentType, expectedType string) bool { mimetype, _, err := mime.ParseMediaType(contentType) diff --git a/api/server/httputils/write_log_stream.go b/api/server/httputils/write_log_stream.go index 9e769c8b4d583..22be5bb1c3364 100644 --- a/api/server/httputils/write_log_stream.go +++ b/api/server/httputils/write_log_stream.go @@ -51,10 +51,10 @@ func WriteLogStream(_ context.Context, w io.Writer, msgs <-chan *backend.LogMess logLine = append([]byte(msg.Timestamp.Format(jsonmessage.RFC3339NanoFixed)+" "), logLine...) } if msg.Source == "stdout" && config.ShowStdout { - outStream.Write(logLine) + _, _ = outStream.Write(logLine) } if msg.Source == "stderr" && config.ShowStderr { - errStream.Write(logLine) + _, _ = errStream.Write(logLine) } } } diff --git a/api/server/middleware/debug.go b/api/server/middleware/debug.go index 2cef1d46c3d36..1ec62602db145 100644 --- a/api/server/middleware/debug.go +++ b/api/server/middleware/debug.go @@ -18,7 +18,7 @@ func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWri return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { logrus.Debugf("Calling %s %s", r.Method, r.RequestURI) - if r.Method != "POST" { + if r.Method != http.MethodPost { return handler(ctx, w, r, vars) } if err := httputils.CheckForJSON(r); err != nil { @@ -41,7 +41,7 @@ func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWri var postForm map[string]interface{} if err := json.Unmarshal(b, &postForm); err == nil { - maskSecretKeys(postForm, r.RequestURI) + maskSecretKeys(postForm) formStr, errMarshal := json.Marshal(postForm) if errMarshal == nil { logrus.Debugf("form data: %s", string(formStr)) @@ -54,41 +54,37 @@ func DebugRequestMiddleware(handler func(ctx context.Context, w http.ResponseWri } } -func maskSecretKeys(inp interface{}, path string) { - // Remove any query string from the path - idx := strings.Index(path, "?") - if idx != -1 { - path = path[:idx] - } - // Remove trailing / characters - path = strings.TrimRight(path, "/") - +func maskSecretKeys(inp interface{}) { if arr, ok := inp.([]interface{}); ok { for _, f := range arr { - maskSecretKeys(f, path) + maskSecretKeys(f) } return } if form, ok := inp.(map[string]interface{}); ok { + scrub := []string{ + // Note: The Data field contains the base64-encoded secret in 'secret' + // and 'config' create and update requests. Currently, no other POST + // API endpoints use a data field, so we scrub this field unconditionally. + // Change this handling to be conditional if a new endpoint is added + // in future where this field should not be scrubbed. + "data", + "jointoken", + "password", + "secret", + "signingcakey", + "unlockkey", + } loop0: for k, v := range form { - for _, m := range []string{"password", "secret", "jointoken", "unlockkey", "signingcakey"} { + for _, m := range scrub { if strings.EqualFold(m, k) { form[k] = "*****" continue loop0 } } - maskSecretKeys(v, path) - } - - // Route-specific redactions - if strings.HasSuffix(path, "/secrets/create") { - for k := range form { - if k == "Data" { - form[k] = "*****" - } - } + maskSecretKeys(v) } } } diff --git a/api/server/middleware/debug_test.go b/api/server/middleware/debug_test.go index a64b73e0d718b..49b24eec936c6 100644 --- a/api/server/middleware/debug_test.go +++ b/api/server/middleware/debug_test.go @@ -3,37 +3,31 @@ package middleware // import "github.com/docker/docker/api/server/middleware" import ( "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestMaskSecretKeys(t *testing.T) { tests := []struct { - path string + doc string input map[string]interface{} expected map[string]interface{} }{ { - path: "/v1.30/secrets/create", + doc: "secret/config create and update requests", input: map[string]interface{}{"Data": "foo", "Name": "name", "Labels": map[string]interface{}{}}, expected: map[string]interface{}{"Data": "*****", "Name": "name", "Labels": map[string]interface{}{}}, }, { - path: "/v1.30/secrets/create//", - input: map[string]interface{}{"Data": "foo", "Name": "name", "Labels": map[string]interface{}{}}, - expected: map[string]interface{}{"Data": "*****", "Name": "name", "Labels": map[string]interface{}{}}, - }, - - { - path: "/secrets/create?key=val", - input: map[string]interface{}{"Data": "foo", "Name": "name", "Labels": map[string]interface{}{}}, - expected: map[string]interface{}{"Data": "*****", "Name": "name", "Labels": map[string]interface{}{}}, - }, - { - path: "/v1.30/some/other/path", + doc: "masking other fields (recursively)", input: map[string]interface{}{ - "password": "pass", + "password": "pass", + "secret": "secret", + "jointoken": "jointoken", + "unlockkey": "unlockkey", + "signingcakey": "signingcakey", "other": map[string]interface{}{ + "password": "pass", "secret": "secret", "jointoken": "jointoken", "unlockkey": "unlockkey", @@ -41,8 +35,13 @@ func TestMaskSecretKeys(t *testing.T) { }, }, expected: map[string]interface{}{ - "password": "*****", + "password": "*****", + "secret": "*****", + "jointoken": "*****", + "unlockkey": "*****", + "signingcakey": "*****", "other": map[string]interface{}{ + "password": "*****", "secret": "*****", "jointoken": "*****", "unlockkey": "*****", @@ -50,10 +49,27 @@ func TestMaskSecretKeys(t *testing.T) { }, }, }, + { + doc: "case insensitive field matching", + input: map[string]interface{}{ + "PASSWORD": "pass", + "other": map[string]interface{}{ + "PASSWORD": "pass", + }, + }, + expected: map[string]interface{}{ + "PASSWORD": "*****", + "other": map[string]interface{}{ + "PASSWORD": "*****", + }, + }, + }, } for _, testcase := range tests { - maskSecretKeys(testcase.input, testcase.path) - assert.Check(t, is.DeepEqual(testcase.expected, testcase.input)) + t.Run(testcase.doc, func(t *testing.T) { + maskSecretKeys(testcase.input) + assert.Check(t, is.DeepEqual(testcase.expected, testcase.input)) + }) } } diff --git a/api/server/middleware/version_test.go b/api/server/middleware/version_test.go index edbc0bcaa589f..a102f8e6c4daf 100644 --- a/api/server/middleware/version_test.go +++ b/api/server/middleware/version_test.go @@ -8,8 +8,8 @@ import ( "testing" "github.com/docker/docker/api/server/httputils" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestVersionMiddlewareVersion(t *testing.T) { @@ -25,7 +25,7 @@ func TestVersionMiddlewareVersion(t *testing.T) { m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion) h := m.WrapHandler(handler) - req, _ := http.NewRequest("GET", "/containers/json", nil) + req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil) resp := httptest.NewRecorder() ctx := context.Background() @@ -76,7 +76,7 @@ func TestVersionMiddlewareWithErrorsReturnsHeaders(t *testing.T) { m := NewVersionMiddleware(defaultVersion, defaultVersion, minVersion) h := m.WrapHandler(handler) - req, _ := http.NewRequest("GET", "/containers/json", nil) + req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil) resp := httptest.NewRecorder() ctx := context.Background() diff --git a/api/server/router/build/build.go b/api/server/router/build/build.go index e6b42ad163265..8ad89ac2b7629 100644 --- a/api/server/router/build/build.go +++ b/api/server/router/build/build.go @@ -31,8 +31,8 @@ func (r *buildRouter) Routes() []router.Route { func (r *buildRouter) initRoutes() { r.routes = []router.Route{ - router.NewPostRoute("/build", r.postBuild, router.WithCancel), - router.NewPostRoute("/build/prune", r.postPrune, router.WithCancel), + router.NewPostRoute("/build", r.postBuild), + router.NewPostRoute("/build/prune", r.postPrune), router.NewPostRoute("/build/cancel", r.postCancel), } } diff --git a/api/server/router/build/build_routes.go b/api/server/router/build/build_routes.go index f41a6d43e2281..9b57e689638c3 100644 --- a/api/server/router/build/build_routes.go +++ b/api/server/router/build/build_routes.go @@ -38,8 +38,36 @@ func (e invalidIsolationError) Error() string { func (e invalidIsolationError) InvalidParameter() {} func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) { + options := &types.ImageBuildOptions{ + Version: types.BuilderV1, // Builder V1 is the default, but can be overridden + Dockerfile: r.FormValue("dockerfile"), + SuppressOutput: httputils.BoolValue(r, "q"), + NoCache: httputils.BoolValue(r, "nocache"), + ForceRemove: httputils.BoolValue(r, "forcerm"), + MemorySwap: httputils.Int64ValueOrZero(r, "memswap"), + Memory: httputils.Int64ValueOrZero(r, "memory"), + CPUShares: httputils.Int64ValueOrZero(r, "cpushares"), + CPUPeriod: httputils.Int64ValueOrZero(r, "cpuperiod"), + CPUQuota: httputils.Int64ValueOrZero(r, "cpuquota"), + CPUSetCPUs: r.FormValue("cpusetcpus"), + CPUSetMems: r.FormValue("cpusetmems"), + CgroupParent: r.FormValue("cgroupparent"), + NetworkMode: r.FormValue("networkmode"), + Tags: r.Form["t"], + ExtraHosts: r.Form["extrahosts"], + SecurityOpt: r.Form["securityopt"], + Squash: httputils.BoolValue(r, "squash"), + Target: r.FormValue("target"), + RemoteContext: r.FormValue("remote"), + SessionID: r.FormValue("session"), + BuildID: r.FormValue("buildid"), + } + + if runtime.GOOS != "windows" && options.SecurityOpt != nil { + return nil, errdefs.InvalidParameter(errors.New("The daemon on this platform does not support setting security options on build")) + } + version := httputils.VersionFromContext(ctx) - options := &types.ImageBuildOptions{} if httputils.BoolValue(r, "forcerm") && versions.GreaterThanOrEqualTo(version, "1.12") { options.Remove = true } else if r.FormValue("rm") == "" && versions.GreaterThanOrEqualTo(version, "1.12") { @@ -50,52 +78,37 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui if httputils.BoolValue(r, "pull") && versions.GreaterThanOrEqualTo(version, "1.16") { options.PullParent = true } - - options.Dockerfile = r.FormValue("dockerfile") - options.SuppressOutput = httputils.BoolValue(r, "q") - options.NoCache = httputils.BoolValue(r, "nocache") - options.ForceRemove = httputils.BoolValue(r, "forcerm") - options.MemorySwap = httputils.Int64ValueOrZero(r, "memswap") - options.Memory = httputils.Int64ValueOrZero(r, "memory") - options.CPUShares = httputils.Int64ValueOrZero(r, "cpushares") - options.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod") - options.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota") - options.CPUSetCPUs = r.FormValue("cpusetcpus") - options.CPUSetMems = r.FormValue("cpusetmems") - options.CgroupParent = r.FormValue("cgroupparent") - options.NetworkMode = r.FormValue("networkmode") - options.Tags = r.Form["t"] - options.ExtraHosts = r.Form["extrahosts"] - options.SecurityOpt = r.Form["securityopt"] - options.Squash = httputils.BoolValue(r, "squash") - options.Target = r.FormValue("target") - options.RemoteContext = r.FormValue("remote") if versions.GreaterThanOrEqualTo(version, "1.32") { options.Platform = r.FormValue("platform") } + if versions.GreaterThanOrEqualTo(version, "1.40") { + outputsJSON := r.FormValue("outputs") + if outputsJSON != "" { + var outputs []types.ImageBuildOutput + if err := json.Unmarshal([]byte(outputsJSON), &outputs); err != nil { + return nil, err + } + options.Outputs = outputs + } + } - if r.Form.Get("shmsize") != "" { - shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64) + if s := r.Form.Get("shmsize"); s != "" { + shmSize, err := strconv.ParseInt(s, 10, 64) if err != nil { return nil, err } options.ShmSize = shmSize } - if i := container.Isolation(r.FormValue("isolation")); i != "" { - if !container.Isolation.IsValid(i) { - return nil, invalidIsolationError(i) + if i := r.FormValue("isolation"); i != "" { + options.Isolation = container.Isolation(i) + if !options.Isolation.IsValid() { + return nil, invalidIsolationError(options.Isolation) } - options.Isolation = i } - if runtime.GOOS != "windows" && options.SecurityOpt != nil { - return nil, errdefs.InvalidParameter(errors.New("The daemon on this platform does not support setting security options on build")) - } - - var buildUlimits = []*units.Ulimit{} - ulimitsJSON := r.FormValue("ulimits") - if ulimitsJSON != "" { + if ulimitsJSON := r.FormValue("ulimits"); ulimitsJSON != "" { + var buildUlimits = []*units.Ulimit{} if err := json.Unmarshal([]byte(ulimitsJSON), &buildUlimits); err != nil { return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading ulimit settings") } @@ -114,8 +127,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui // the fact they mentioned it, we need to pass that along to the builder // so that it can print a warning about "foo" being unused if there is // no "ARG foo" in the Dockerfile. - buildArgsJSON := r.FormValue("buildargs") - if buildArgsJSON != "" { + if buildArgsJSON := r.FormValue("buildargs"); buildArgsJSON != "" { var buildArgs = map[string]*string{} if err := json.Unmarshal([]byte(buildArgsJSON), &buildArgs); err != nil { return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading build args") @@ -123,8 +135,7 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui options.BuildArgs = buildArgs } - labelsJSON := r.FormValue("labels") - if labelsJSON != "" { + if labelsJSON := r.FormValue("labels"); labelsJSON != "" { var labels = map[string]string{} if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil { return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading labels") @@ -132,40 +143,41 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui options.Labels = labels } - cacheFromJSON := r.FormValue("cachefrom") - if cacheFromJSON != "" { + if cacheFromJSON := r.FormValue("cachefrom"); cacheFromJSON != "" { var cacheFrom = []string{} if err := json.Unmarshal([]byte(cacheFromJSON), &cacheFrom); err != nil { return nil, err } options.CacheFrom = cacheFrom } - options.SessionID = r.FormValue("session") - options.BuildID = r.FormValue("buildid") - builderVersion, err := parseVersion(r.FormValue("version")) - if err != nil { - return nil, err + + if bv := r.FormValue("version"); bv != "" { + v, err := parseVersion(bv) + if err != nil { + return nil, err + } + options.Version = v } - options.Version = builderVersion return options, nil } func parseVersion(s string) (types.BuilderVersion, error) { - if s == "" || s == string(types.BuilderV1) { + switch types.BuilderVersion(s) { + case types.BuilderV1: return types.BuilderV1, nil - } - if s == string(types.BuilderBuildKit) { + case types.BuilderBuildKit: return types.BuilderBuildKit, nil + default: + return "", errors.Errorf("invalid version %q", s) } - return "", errors.Errorf("invalid version %s", s) } func (br *buildRouter) postPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } - filters, err := filters.FromJSON(r.Form.Get("filters")) + fltrs, err := filters.FromJSON(r.Form.Get("filters")) if err != nil { return errors.Wrap(err, "could not parse filters") } @@ -180,7 +192,7 @@ func (br *buildRouter) postPrune(ctx context.Context, w http.ResponseWriter, r * opts := types.BuildCachePruneOptions{ All: httputils.BoolValue(r, "all"), - Filters: filters, + Filters: fltrs, KeepStorage: int64(ks), } @@ -223,12 +235,12 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r * } output := ioutils.NewWriteFlusher(ww) - defer output.Close() + defer func() { _ = output.Close() }() errf := func(err error) error { if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 { - output.Write(notVerboseBuffer.Bytes()) + _, _ = output.Write(notVerboseBuffer.Bytes()) } // Do not write the error in the http output if it's still empty. @@ -279,7 +291,7 @@ func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r * // Everything worked so if -q was provided the output from the daemon // should be just the image ID and we'll print that to stdout. if buildOptions.SuppressOutput { - fmt.Fprintln(streamformatter.NewStdoutWriter(output), imgID) + _, _ = fmt.Fprintln(streamformatter.NewStdoutWriter(output), imgID) } return nil } @@ -295,7 +307,7 @@ func getAuthConfigs(header http.Header) map[string]types.AuthConfig { authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded)) // Pulling an image does not error when no auth is provided so to remain // consistent with the existing api decode errors are ignored - json.NewDecoder(authConfigsJSON).Decode(&authConfigs) + _ = json.NewDecoder(authConfigsJSON).Decode(&authConfigs) return authConfigs } @@ -417,7 +429,7 @@ func (w *wcf) notify() { w.mu.Lock() if !w.ready { if w.buf.Len() > 0 { - io.Copy(w.Writer, w.buf) + _, _ = io.Copy(w.Writer, w.buf) } if w.flushed { w.flusher.Flush() diff --git a/api/server/router/container/container.go b/api/server/router/container/container.go index 358f2bc2c17b5..df68fcb791149 100644 --- a/api/server/router/container/container.go +++ b/api/server/router/container/container.go @@ -10,13 +10,15 @@ type containerRouter struct { backend Backend decoder httputils.ContainerDecoder routes []router.Route + cgroup2 bool } // NewRouter initializes a new container router -func NewRouter(b Backend, decoder httputils.ContainerDecoder) router.Router { +func NewRouter(b Backend, decoder httputils.ContainerDecoder, cgroup2 bool) router.Router { r := &containerRouter{ backend: b, decoder: decoder, + cgroup2: cgroup2, } r.initRoutes() return r @@ -38,8 +40,8 @@ func (r *containerRouter) initRoutes() { router.NewGetRoute("/containers/{name:.*}/changes", r.getContainersChanges), router.NewGetRoute("/containers/{name:.*}/json", r.getContainersByName), router.NewGetRoute("/containers/{name:.*}/top", r.getContainersTop), - router.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs, router.WithCancel), - router.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats, router.WithCancel), + router.NewGetRoute("/containers/{name:.*}/logs", r.getContainersLogs), + router.NewGetRoute("/containers/{name:.*}/stats", r.getContainersStats), router.NewGetRoute("/containers/{name:.*}/attach/ws", r.wsContainersAttach), router.NewGetRoute("/exec/{id:.*}/json", r.getExecByID), router.NewGetRoute("/containers/{name:.*}/archive", r.getContainersArchive), @@ -51,7 +53,7 @@ func (r *containerRouter) initRoutes() { router.NewPostRoute("/containers/{name:.*}/restart", r.postContainersRestart), router.NewPostRoute("/containers/{name:.*}/start", r.postContainersStart), router.NewPostRoute("/containers/{name:.*}/stop", r.postContainersStop), - router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait, router.WithCancel), + router.NewPostRoute("/containers/{name:.*}/wait", r.postContainersWait), router.NewPostRoute("/containers/{name:.*}/resize", r.postContainersResize), router.NewPostRoute("/containers/{name:.*}/attach", r.postContainersAttach), router.NewPostRoute("/containers/{name:.*}/copy", r.postContainersCopy), // Deprecated since 1.8, Errors out since 1.12 @@ -60,7 +62,7 @@ func (r *containerRouter) initRoutes() { router.NewPostRoute("/exec/{name:.*}/resize", r.postContainerExecResize), router.NewPostRoute("/containers/{name:.*}/rename", r.postContainerRename), router.NewPostRoute("/containers/{name:.*}/update", r.postContainerUpdate), - router.NewPostRoute("/containers/prune", r.postContainersPrune, router.WithCancel), + router.NewPostRoute("/containers/prune", r.postContainersPrune), router.NewPostRoute("/commit", r.postCommit), // PUT router.NewPutRoute("/containers/{name:.*}/archive", r.putContainersArchive), diff --git a/api/server/router/container/container_routes.go b/api/server/router/container/container_routes.go index 9282cea09ccc0..c3074aa3a0c0c 100644 --- a/api/server/router/container/container_routes.go +++ b/api/server/router/container/container_routes.go @@ -9,6 +9,7 @@ import ( "strconv" "syscall" + "github.com/containerd/containerd/platforms" "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" @@ -18,7 +19,8 @@ import ( containerpkg "github.com/docker/docker/container" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/signal" + "github.com/moby/sys/signal" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/net/websocket" @@ -41,7 +43,7 @@ func (s *containerRouter) postCommit(ctx context.Context, w http.ResponseWriter, } config, _, _, err := s.decoder.DecodeConfig(r.Body) - if err != nil && err != io.EOF { //Do not fail if body is empty. + if err != nil && err != io.EOF { // Do not fail if body is empty. return err } @@ -105,9 +107,14 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons if !stream { w.Header().Set("Content-Type", "application/json") } + var oneShot bool + if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.41") { + oneShot = httputils.BoolValueOrDefault(r, "one-shot", false) + } config := &backend.ContainerStatsConfig{ Stream: stream, + OneShot: oneShot, OutStream: w, Version: httputils.VersionFromContext(ctx), } @@ -338,9 +345,6 @@ func (s *containerRouter) postContainersWait(ctx context.Context, w http.Respons } } - // Note: the context should get canceled if the client closes the - // connection since this handler has been wrapped by the - // router.WithCancel() wrapper. waitC, err := s.backend.ContainerWait(ctx, vars["name"], waitCondition) if err != nil { return err @@ -428,6 +432,16 @@ func (s *containerRouter) postContainerUpdate(ctx context.Context, w http.Respon if err := decoder.Decode(&updateConfig); err != nil { return err } + if versions.LessThan(httputils.VersionFromContext(ctx), "1.40") { + updateConfig.PidsLimit = nil + } + if updateConfig.PidsLimit != nil && *updateConfig.PidsLimit <= 0 { + // Both `0` and `-1` are accepted to set "unlimited" when updating. + // Historically, any negative value was accepted, so treat them as + // "unlimited" as well. + var unlimited int64 + updateConfig.PidsLimit = &unlimited + } hostConfig := &container.HostConfig{ Resources: updateConfig.Resources, @@ -465,12 +479,54 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo hostConfig.AutoRemove = false } + if hostConfig != nil && versions.LessThan(version, "1.40") { + // Ignore BindOptions.NonRecursive because it was added in API 1.40. + for _, m := range hostConfig.Mounts { + if bo := m.BindOptions; bo != nil { + bo.NonRecursive = false + } + } + // Ignore KernelMemoryTCP because it was added in API 1.40. + hostConfig.KernelMemoryTCP = 0 + + // Older clients (API < 1.40) expects the default to be shareable, make them happy + if hostConfig.IpcMode.IsEmpty() { + hostConfig.IpcMode = container.IPCModeShareable + } + } + if hostConfig != nil && versions.LessThan(version, "1.41") && !s.cgroup2 { + // Older clients expect the default to be "host" on cgroup v1 hosts + if hostConfig.CgroupnsMode.IsEmpty() { + hostConfig.CgroupnsMode = container.CgroupnsModeHost + } + } + + var platform *specs.Platform + if versions.GreaterThanOrEqualTo(version, "1.41") { + if v := r.Form.Get("platform"); v != "" { + p, err := platforms.Parse(v) + if err != nil { + return errdefs.InvalidParameter(err) + } + platform = &p + } + } + + if hostConfig != nil && hostConfig.PidsLimit != nil && *hostConfig.PidsLimit <= 0 { + // Don't set a limit if either no limit was specified, or "unlimited" was + // explicitly set. + // Both `0` and `-1` are accepted as "unlimited", and historically any + // negative value was accepted, so treat those as "unlimited" as well. + hostConfig.PidsLimit = nil + } + ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{ Name: name, Config: config, HostConfig: hostConfig, NetworkingConfig: networkingConfig, AdjustCPUShares: adjustCPUShares, + Platform: platform, }) if err != nil { return err @@ -570,7 +626,7 @@ func (s *containerRouter) postContainersAttach(ctx context.Context, w http.Respo // Remember to close stream if error happens conn, _, errHijack := hijacker.Hijack() if errHijack == nil { - statusCode := httputils.GetHTTPErrorStatusCode(err) + statusCode := errdefs.GetHTTPErrorStatusCode(err) statusText := http.StatusText(statusCode) fmt.Fprintf(conn, "HTTP/1.1 %d %s\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n%s\r\n", statusCode, statusText, err.Error()) httputils.CloseStreams(conn) diff --git a/api/server/router/container/copy.go b/api/server/router/container/copy.go index 837836d001ce0..47de35a455672 100644 --- a/api/server/router/container/copy.go +++ b/api/server/router/container/copy.go @@ -6,12 +6,14 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "io" "net/http" "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions" + "github.com/docker/docker/errdefs" gddohttputil "github.com/golang/gddo/httputil" ) @@ -37,7 +39,10 @@ func (s *containerRouter) postContainersCopy(ctx context.Context, w http.Respons cfg := types.CopyConfig{} if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } if cfg.Resource == "" { diff --git a/api/server/router/container/exec.go b/api/server/router/container/exec.go index 25125edb5f108..cf92be1d2baac 100644 --- a/api/server/router/container/exec.go +++ b/api/server/router/container/exec.go @@ -3,6 +3,7 @@ package container // import "github.com/docker/docker/api/server/router/containe import ( "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -44,7 +45,10 @@ func (s *containerRouter) postContainerExecCreate(ctx context.Context, w http.Re execConfig := &types.ExecConfig{} if err := json.NewDecoder(r.Body).Decode(execConfig); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } if len(execConfig.Cmd) == 0 { @@ -84,7 +88,10 @@ func (s *containerRouter) postContainerExecStart(ctx context.Context, w http.Res execStartCheck := &types.ExecStartCheck{} if err := json.NewDecoder(r.Body).Decode(execStartCheck); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } if exists, err := s.backend.ExecExists(execName); !exists { diff --git a/api/server/router/distribution/backend.go b/api/server/router/distribution/backend.go index 5b881f036b499..6df6ad719a1d9 100644 --- a/api/server/router/distribution/backend.go +++ b/api/server/router/distribution/backend.go @@ -11,5 +11,5 @@ import ( // Backend is all the methods that need to be implemented // to provide image specific functionality. type Backend interface { - GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, bool, error) + GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, error) } diff --git a/api/server/router/distribution/distribution_routes.go b/api/server/router/distribution/distribution_routes.go index 531dba69fcaa5..86b93d3c6290c 100644 --- a/api/server/router/distribution/distribution_routes.go +++ b/api/server/router/distribution/distribution_routes.go @@ -14,7 +14,8 @@ import ( "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" - "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/docker/docker/errdefs" + v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -42,9 +43,10 @@ func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.Res image := vars["name"] + // TODO why is reference.ParseAnyReference() / reference.ParseNormalizedNamed() not using the reference.ErrTagInvalidFormat (and so on) errors? ref, err := reference.ParseAnyReference(image) if err != nil { - return err + return errdefs.InvalidParameter(err) } namedRef, ok := ref.(reference.Named) if !ok { @@ -52,10 +54,10 @@ func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.Res // full image ID return errors.Errorf("no manifest found for full image ID") } - return errors.Errorf("unknown image reference format: %s", image) + return errdefs.InvalidParameter(errors.Errorf("unknown image reference format: %s", image)) } - distrepo, _, err := s.backend.GetRepository(ctx, namedRef, config) + distrepo, err := s.backend.GetRepository(ctx, namedRef, config) if err != nil { return err } @@ -66,7 +68,7 @@ func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.Res taggedRef, ok := namedRef.(reference.NamedTagged) if !ok { - return errors.Errorf("image reference not tagged: %s", image) + return errdefs.InvalidParameter(errors.Errorf("image reference not tagged: %s", image)) } descriptor, err := distrepo.Tags(ctx).Get(ctx, taggedRef.Tag()) @@ -92,6 +94,16 @@ func (s *distributionRouter) getDistributionInfo(ctx context.Context, w http.Res } mnfst, err := mnfstsrvc.Get(ctx, distributionInspect.Descriptor.Digest) if err != nil { + switch err { + case reference.ErrReferenceInvalidFormat, + reference.ErrTagInvalidFormat, + reference.ErrDigestInvalidFormat, + reference.ErrNameContainsUppercase, + reference.ErrNameEmpty, + reference.ErrNameTooLong, + reference.ErrNameNotCanonical: + return errdefs.InvalidParameter(err) + } return err } diff --git a/api/server/router/grpc/backend.go b/api/server/router/grpc/backend.go new file mode 100644 index 0000000000000..52cc37cf2ca7e --- /dev/null +++ b/api/server/router/grpc/backend.go @@ -0,0 +1,8 @@ +package grpc // import "github.com/docker/docker/api/server/router/grpc" + +import "google.golang.org/grpc" + +// Backend abstracts a registerable GRPC service. +type Backend interface { + RegisterGRPC(*grpc.Server) +} diff --git a/api/server/router/grpc/grpc.go b/api/server/router/grpc/grpc.go new file mode 100644 index 0000000000000..713600be4322d --- /dev/null +++ b/api/server/router/grpc/grpc.go @@ -0,0 +1,41 @@ +package grpc // import "github.com/docker/docker/api/server/router/grpc" + +import ( + "github.com/docker/docker/api/server/router" + "github.com/moby/buildkit/util/grpcerrors" + "golang.org/x/net/http2" + "google.golang.org/grpc" +) + +type grpcRouter struct { + routes []router.Route + grpcServer *grpc.Server + h2Server *http2.Server +} + +// NewRouter initializes a new grpc http router +func NewRouter(backends ...Backend) router.Router { + opts := []grpc.ServerOption{grpc.UnaryInterceptor(grpcerrors.UnaryServerInterceptor), grpc.StreamInterceptor(grpcerrors.StreamServerInterceptor)} + server := grpc.NewServer(opts...) + + r := &grpcRouter{ + h2Server: &http2.Server{}, + grpcServer: server, + } + for _, b := range backends { + b.RegisterGRPC(r.grpcServer) + } + r.initRoutes() + return r +} + +// Routes returns the available routers to the session controller +func (r *grpcRouter) Routes() []router.Route { + return r.routes +} + +func (r *grpcRouter) initRoutes() { + r.routes = []router.Route{ + router.NewPostRoute("/grpc", r.serveGRPC), + } +} diff --git a/api/server/router/grpc/grpc_routes.go b/api/server/router/grpc/grpc_routes.go new file mode 100644 index 0000000000000..1fe0da472f6fd --- /dev/null +++ b/api/server/router/grpc/grpc_routes.go @@ -0,0 +1,45 @@ +package grpc // import "github.com/docker/docker/api/server/router/grpc" + +import ( + "context" + "net/http" + + "github.com/pkg/errors" + "golang.org/x/net/http2" +) + +func (gr *grpcRouter) serveGRPC(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + h, ok := w.(http.Hijacker) + if !ok { + return errors.New("handler does not support hijack") + } + proto := r.Header.Get("Upgrade") + if proto == "" { + return errors.New("no upgrade proto in request") + } + if proto != "h2c" { + return errors.Errorf("protocol %s not supported", proto) + } + + conn, _, err := h.Hijack() + if err != nil { + return err + } + resp := &http.Response{ + StatusCode: http.StatusSwitchingProtocols, + ProtoMajor: 1, + ProtoMinor: 1, + Header: http.Header{}, + } + resp.Header.Set("Connection", "Upgrade") + resp.Header.Set("Upgrade", proto) + + // set raw mode + conn.Write([]byte{}) + resp.Write(conn) + + // https://godoc.org/golang.org/x/net/http2#Server.ServeConn + // TODO: is it a problem that conn has already been written to? + gr.h2Server.ServeConn(conn, &http2.ServeConnOpts{Handler: gr.grpcServer}) + return nil +} diff --git a/api/server/router/image/backend.go b/api/server/router/image/backend.go index 5837f9a9bcdfd..624b6c7363ff2 100644 --- a/api/server/router/image/backend.go +++ b/api/server/router/image/backend.go @@ -22,7 +22,7 @@ type Backend interface { type imageBackend interface { ImageDelete(imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error) ImageHistory(imageName string) ([]*image.HistoryResponseItem, error) - Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) + Images(ctx context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error) LookupImage(name string) (*types.ImageInspect, error) TagImage(imageName, repository, tag string) (string, error) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) diff --git a/api/server/router/image/image.go b/api/server/router/image/image.go index 6d5d87f63c977..42f89153b57ff 100644 --- a/api/server/router/image/image.go +++ b/api/server/router/image/image.go @@ -34,10 +34,10 @@ func (r *imageRouter) initRoutes() { router.NewGetRoute("/images/{name:.*}/json", r.getImagesByName), // POST router.NewPostRoute("/images/load", r.postImagesLoad), - router.NewPostRoute("/images/create", r.postImagesCreate, router.WithCancel), - router.NewPostRoute("/images/{name:.*}/push", r.postImagesPush, router.WithCancel), + router.NewPostRoute("/images/create", r.postImagesCreate), + router.NewPostRoute("/images/{name:.*}/push", r.postImagesPush), router.NewPostRoute("/images/{name:.*}/tag", r.postImagesTag), - router.NewPostRoute("/images/prune", r.postImagesPrune, router.WithCancel), + router.NewPostRoute("/images/prune", r.postImagesPrune), // DELETE router.NewDeleteRoute("/images/{name:.*}", r.deleteImages), } diff --git a/api/server/router/image/image_routes.go b/api/server/router/image/image_routes.go index 85707c06d2ce3..9a4648bb49960 100644 --- a/api/server/router/image/image_routes.go +++ b/api/server/router/image/image_routes.go @@ -16,7 +16,6 @@ import ( "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/streamformatter" - "github.com/docker/docker/pkg/system" "github.com/docker/docker/registry" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -50,50 +49,45 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite if err != nil { return err } - if err := system.ValidatePlatform(sp); err != nil { - return err - } platform = &sp } } - if err == nil { - if image != "" { //pull - metaHeaders := map[string][]string{} - for k, v := range r.Header { - if strings.HasPrefix(k, "X-Meta-") { - metaHeaders[k] = v - } + if image != "" { // pull + metaHeaders := map[string][]string{} + for k, v := range r.Header { + if strings.HasPrefix(k, "X-Meta-") { + metaHeaders[k] = v } + } - authEncoded := r.Header.Get("X-Registry-Auth") - authConfig := &types.AuthConfig{} - if authEncoded != "" { - authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) - if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { - // for a pull it is not an error if no auth was given - // to increase compatibility with the existing api it is defaulting to be empty - authConfig = &types.AuthConfig{} - } + authEncoded := r.Header.Get("X-Registry-Auth") + authConfig := &types.AuthConfig{} + if authEncoded != "" { + authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) + if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { + // for a pull it is not an error if no auth was given + // to increase compatibility with the existing api it is defaulting to be empty + authConfig = &types.AuthConfig{} } - err = s.backend.PullImage(ctx, image, tag, platform, metaHeaders, authConfig, output) - } else { //import - src := r.Form.Get("fromSrc") - // 'err' MUST NOT be defined within this block, we need any error - // generated from the download to be available to the output - // stream processing below - os := "" - if platform != nil { - os = platform.OS - } - err = s.backend.ImportImage(src, repo, os, tag, message, r.Body, output, r.Form["changes"]) } + err = s.backend.PullImage(ctx, image, tag, platform, metaHeaders, authConfig, output) + } else { // import + src := r.Form.Get("fromSrc") + // 'err' MUST NOT be defined within this block, we need any error + // generated from the download to be available to the output + // stream processing below + os := "" + if platform != nil { + os = platform.OS + } + err = s.backend.ImportImage(src, repo, os, tag, message, r.Body, output, r.Form["changes"]) } if err != nil { if !output.Flushed() { return err } - output.Write(streamformatter.FormatError(err)) + _, _ = output.Write(streamformatter.FormatError(err)) } return nil @@ -138,7 +132,7 @@ func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, if !output.Flushed() { return err } - output.Write(streamformatter.FormatError(err)) + _, _ = output.Write(streamformatter.FormatError(err)) } return nil } @@ -163,7 +157,7 @@ func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r if !output.Flushed() { return err } - output.Write(streamformatter.FormatError(err)) + _, _ = output.Write(streamformatter.FormatError(err)) } return nil } @@ -179,7 +173,7 @@ func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, output := ioutils.NewWriteFlusher(w) defer output.Close() if err := s.backend.LoadImage(r.Body, output, quiet); err != nil { - output.Write(streamformatter.FormatError(err)) + _, _ = output.Write(streamformatter.FormatError(err)) } return nil } @@ -233,13 +227,26 @@ func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, return err } - filterParam := r.Form.Get("filter") - // FIXME(vdemeester) This has been deprecated in 1.13, and is target for removal for v17.12 - if filterParam != "" { - imageFilters.Add("reference", filterParam) + version := httputils.VersionFromContext(ctx) + if versions.LessThan(version, "1.41") { + // NOTE: filter is a shell glob string applied to repository names. + filterParam := r.Form.Get("filter") + if filterParam != "" { + imageFilters.Add("reference", filterParam) + } + } + + var sharedSize bool + if versions.GreaterThanOrEqualTo(version, "1.42") { + // NOTE: Support for the "shared-size" parameter was added in API 1.42. + sharedSize = httputils.BoolValue(r, "shared-size") } - images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false) + images, err := s.backend.Images(ctx, types.ImageListOptions{ + All: httputils.BoolValue(r, "all"), + Filters: imageFilters, + SharedSize: sharedSize, + }) if err != nil { return err } diff --git a/api/server/router/local.go b/api/server/router/local.go index 79a323928ec10..f85d2f96e7df3 100644 --- a/api/server/router/local.go +++ b/api/server/router/local.go @@ -1,7 +1,6 @@ package router // import "github.com/docker/docker/api/server/router" import ( - "context" "net/http" "github.com/docker/docker/api/server/httputils" @@ -45,60 +44,30 @@ func NewRoute(method, path string, handler httputils.APIFunc, opts ...RouteWrapp // NewGetRoute initializes a new route with the http method GET. func NewGetRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { - return NewRoute("GET", path, handler, opts...) + return NewRoute(http.MethodGet, path, handler, opts...) } // NewPostRoute initializes a new route with the http method POST. func NewPostRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { - return NewRoute("POST", path, handler, opts...) + return NewRoute(http.MethodPost, path, handler, opts...) } // NewPutRoute initializes a new route with the http method PUT. func NewPutRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { - return NewRoute("PUT", path, handler, opts...) + return NewRoute(http.MethodPut, path, handler, opts...) } // NewDeleteRoute initializes a new route with the http method DELETE. func NewDeleteRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { - return NewRoute("DELETE", path, handler, opts...) + return NewRoute(http.MethodDelete, path, handler, opts...) } // NewOptionsRoute initializes a new route with the http method OPTIONS. func NewOptionsRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { - return NewRoute("OPTIONS", path, handler, opts...) + return NewRoute(http.MethodOptions, path, handler, opts...) } // NewHeadRoute initializes a new route with the http method HEAD. func NewHeadRoute(path string, handler httputils.APIFunc, opts ...RouteWrapper) Route { - return NewRoute("HEAD", path, handler, opts...) -} - -func cancellableHandler(h httputils.APIFunc) httputils.APIFunc { - return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - if notifier, ok := w.(http.CloseNotifier); ok { - notify := notifier.CloseNotify() - notifyCtx, cancel := context.WithCancel(ctx) - finished := make(chan struct{}) - defer close(finished) - ctx = notifyCtx - go func() { - select { - case <-notify: - cancel() - case <-finished: - } - }() - } - return h(ctx, w, r, vars) - } -} - -// WithCancel makes new route which embeds http.CloseNotifier feature to -// context.Context of handler. -func WithCancel(r Route) Route { - return localRoute{ - method: r.Method(), - path: r.Path(), - handler: cancellableHandler(r.Handler()), - } + return NewRoute(http.MethodHead, path, handler, opts...) } diff --git a/api/server/router/network/backend.go b/api/server/router/network/backend.go index 42eb950ed83e8..6d63ceb473354 100644 --- a/api/server/router/network/backend.go +++ b/api/server/router/network/backend.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/network" - "github.com/docker/libnetwork" + "github.com/docker/docker/libnetwork" ) // Backend is all the methods that need to be implemented diff --git a/api/server/router/network/network.go b/api/server/router/network/network.go index 4eee970793120..d31883500b68b 100644 --- a/api/server/router/network/network.go +++ b/api/server/router/network/network.go @@ -36,7 +36,7 @@ func (r *networkRouter) initRoutes() { router.NewPostRoute("/networks/create", r.postNetworkCreate), router.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect), router.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect), - router.NewPostRoute("/networks/prune", r.postNetworksPrune, router.WithCancel), + router.NewPostRoute("/networks/prune", r.postNetworksPrune), // DELETE router.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork), } diff --git a/api/server/router/network/network_routes.go b/api/server/router/network/network_routes.go index 8b9382cb4523c..96b65c3777c8e 100644 --- a/api/server/router/network/network_routes.go +++ b/api/server/router/network/network_routes.go @@ -3,6 +3,7 @@ package network // import "github.com/docker/docker/api/server/router/network" import ( "context" "encoding/json" + "io" "net/http" "strconv" "strings" @@ -13,8 +14,8 @@ import ( "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/errdefs" - "github.com/docker/libnetwork" - netconst "github.com/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork" + netconst "github.com/docker/docker/libnetwork/datastore" "github.com/pkg/errors" ) @@ -29,7 +30,7 @@ func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWrit } if err := network.ValidateFilters(filter); err != nil { - return err + return errdefs.InvalidParameter(err) } var list []types.NetworkResource @@ -215,7 +216,10 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr } if err := json.NewDecoder(r.Body).Decode(&create); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } if nws, err := n.cluster.GetNetworksByName(create.Name); err == nil && len(nws) > 0 { @@ -261,7 +265,10 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW } if err := json.NewDecoder(r.Body).Decode(&connect); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } // Unlike other operations, we does not check ambiguity of the name/ID here. @@ -282,7 +289,10 @@ func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.Respon } if err := json.NewDecoder(r.Body).Decode(&disconnect); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } return n.backend.DisconnectContainerFromNetwork(disconnect.Container, vars["id"], disconnect.Force) diff --git a/api/server/router/plugin/plugin.go b/api/server/router/plugin/plugin.go index 7a4f987aa3bfa..96190b3d03293 100644 --- a/api/server/router/plugin/plugin.go +++ b/api/server/router/plugin/plugin.go @@ -28,11 +28,11 @@ func (r *pluginRouter) initRoutes() { router.NewGetRoute("/plugins/{name:.*}/json", r.inspectPlugin), router.NewGetRoute("/plugins/privileges", r.getPrivileges), router.NewDeleteRoute("/plugins/{name:.*}", r.removePlugin), - router.NewPostRoute("/plugins/{name:.*}/enable", r.enablePlugin), // PATCH? + router.NewPostRoute("/plugins/{name:.*}/enable", r.enablePlugin), router.NewPostRoute("/plugins/{name:.*}/disable", r.disablePlugin), - router.NewPostRoute("/plugins/pull", r.pullPlugin, router.WithCancel), - router.NewPostRoute("/plugins/{name:.*}/push", r.pushPlugin, router.WithCancel), - router.NewPostRoute("/plugins/{name:.*}/upgrade", r.upgradePlugin, router.WithCancel), + router.NewPostRoute("/plugins/pull", r.pullPlugin), + router.NewPostRoute("/plugins/{name:.*}/push", r.pushPlugin), + router.NewPostRoute("/plugins/{name:.*}/upgrade", r.upgradePlugin), router.NewPostRoute("/plugins/{name:.*}/set", r.setPlugin), router.NewPostRoute("/plugins/create", r.createPlugin), } diff --git a/api/server/router/plugin/plugin_routes.go b/api/server/router/plugin/plugin_routes.go index 4e816391d143a..8c36b51ecce11 100644 --- a/api/server/router/plugin/plugin_routes.go +++ b/api/server/router/plugin/plugin_routes.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "encoding/json" + "io" "net/http" "strconv" "strings" @@ -12,6 +13,7 @@ import ( "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/streamformatter" "github.com/pkg/errors" @@ -121,7 +123,7 @@ func (pr *pluginRouter) upgradePlugin(ctx context.Context, w http.ResponseWriter if !output.Flushed() { return err } - output.Write(streamformatter.FormatError(err)) + _, _ = output.Write(streamformatter.FormatError(err)) } return nil @@ -160,7 +162,7 @@ func (pr *pluginRouter) pullPlugin(ctx context.Context, w http.ResponseWriter, r if !output.Flushed() { return err } - output.Write(streamformatter.FormatError(err)) + _, _ = output.Write(streamformatter.FormatError(err)) } return nil @@ -209,7 +211,7 @@ func (pr *pluginRouter) createPlugin(ctx context.Context, w http.ResponseWriter, if err := pr.backend.CreateFromContext(ctx, r.Body, options); err != nil { return err } - //TODO: send progress bar + // TODO: send progress bar w.WriteHeader(http.StatusNoContent) return nil } @@ -268,7 +270,7 @@ func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r if !output.Flushed() { return err } - output.Write(streamformatter.FormatError(err)) + _, _ = output.Write(streamformatter.FormatError(err)) } return nil } @@ -276,7 +278,10 @@ func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r func (pr *pluginRouter) setPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var args []string if err := json.NewDecoder(r.Body).Decode(&args); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } if err := pr.backend.Set(vars["name"], args); err != nil { return err diff --git a/api/server/router/swarm/cluster.go b/api/server/router/swarm/cluster.go index 52f950a3a926a..96385d619e1aa 100644 --- a/api/server/router/swarm/cluster.go +++ b/api/server/router/swarm/cluster.go @@ -37,7 +37,7 @@ func (sr *swarmRouter) initRoutes() { router.NewPostRoute("/services/create", sr.createService), router.NewPostRoute("/services/{id}/update", sr.updateService), router.NewDeleteRoute("/services/{id}", sr.removeService), - router.NewGetRoute("/services/{id}/logs", sr.getServiceLogs, router.WithCancel), + router.NewGetRoute("/services/{id}/logs", sr.getServiceLogs), router.NewGetRoute("/nodes", sr.getNodes), router.NewGetRoute("/nodes/{id}", sr.getNode), @@ -46,7 +46,7 @@ func (sr *swarmRouter) initRoutes() { router.NewGetRoute("/tasks", sr.getTasks), router.NewGetRoute("/tasks/{id}", sr.getTask), - router.NewGetRoute("/tasks/{id}/logs", sr.getTaskLogs, router.WithCancel), + router.NewGetRoute("/tasks/{id}/logs", sr.getTaskLogs), router.NewGetRoute("/secrets", sr.getSecrets), router.NewPostRoute("/secrets/create", sr.createSecret), diff --git a/api/server/router/swarm/cluster_routes.go b/api/server/router/swarm/cluster_routes.go index f984328322280..6ab097b4fda21 100644 --- a/api/server/router/swarm/cluster_routes.go +++ b/api/server/router/swarm/cluster_routes.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "io" "net/http" "strconv" @@ -21,7 +22,21 @@ import ( func (sr *swarmRouter) initCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var req types.InitRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) + } + version := httputils.VersionFromContext(ctx) + + // DefaultAddrPool and SubnetSize were added in API 1.39. Ignore on older API versions. + if versions.LessThan(version, "1.39") { + req.DefaultAddrPool = nil + req.SubnetSize = 0 + } + // DataPathPort was added in API 1.40. Ignore this option on older API versions. + if versions.LessThan(version, "1.40") { + req.DataPathPort = 0 } nodeID, err := sr.backend.Init(req) if err != nil { @@ -34,7 +49,10 @@ func (sr *swarmRouter) initCluster(ctx context.Context, w http.ResponseWriter, r func (sr *swarmRouter) joinCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var req types.JoinRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } return sr.backend.Join(req) } @@ -61,7 +79,10 @@ func (sr *swarmRouter) inspectCluster(ctx context.Context, w http.ResponseWriter func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var swarm types.Spec if err := json.NewDecoder(r.Body).Decode(&swarm); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } rawVersion := r.URL.Query().Get("version") @@ -112,7 +133,10 @@ func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter, func (sr *swarmRouter) unlockCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var req types.UnlockRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } if err := sr.backend.UnlockSwarm(req); err != nil { @@ -143,7 +167,19 @@ func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r return errdefs.InvalidParameter(err) } - services, err := sr.backend.GetServices(basictypes.ServiceListOptions{Filters: filter}) + // the status query parameter is only support in API versions >= 1.41. If + // the client is using a lesser version, ignore the parameter. + cliVersion := httputils.VersionFromContext(ctx) + var status bool + if value := r.URL.Query().Get("status"); value != "" && !versions.LessThan(cliVersion, "1.41") { + var err error + status, err = strconv.ParseBool(value) + if err != nil { + return errors.Wrapf(errdefs.InvalidParameter(err), "invalid value for status: %s", value) + } + } + + services, err := sr.backend.GetServices(basictypes.ServiceListOptions{Filters: filter, Status: status}) if err != nil { logrus.Errorf("Error getting services: %v", err) return err @@ -154,15 +190,21 @@ func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var insertDefaults bool + if value := r.URL.Query().Get("insertDefaults"); value != "" { var err error insertDefaults, err = strconv.ParseBool(value) if err != nil { - err := fmt.Errorf("invalid value for insertDefaults: %s", value) return errors.Wrapf(errdefs.InvalidParameter(err), "invalid value for insertDefaults: %s", value) } } + // you may note that there is no code here to handle the "status" query + // parameter, as in getServices. the Status field is not supported when + // retrieving an individual service because the Backend API changes + // required to accommodate it would be too disruptive, and because that + // field is so rarely needed as part of an individual service inspection. + service, err := sr.backend.GetService(vars["id"], insertDefaults) if err != nil { logrus.Errorf("Error getting service %s: %v", vars["id"], err) @@ -175,26 +217,21 @@ func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var service types.ServiceSpec if err := json.NewDecoder(r.Body).Decode(&service); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } // Get returns "" if the header does not exist encodedAuth := r.Header.Get("X-Registry-Auth") - cliVersion := r.Header.Get("version") queryRegistry := false - if cliVersion != "" { - if versions.LessThan(cliVersion, "1.30") { + if v := httputils.VersionFromContext(ctx); v != "" { + if versions.LessThan(v, "1.30") { queryRegistry = true } - if versions.LessThan(cliVersion, "1.39") { - if service.TaskTemplate.ContainerSpec != nil { - // Sysctls for docker swarm services weren't supported before - // API version 1.39 - service.TaskTemplate.ContainerSpec.Sysctls = nil - } - } + adjustForAPIVersion(v, &service) } - resp, err := sr.backend.CreateService(service, encodedAuth, queryRegistry) if err != nil { logrus.Errorf("Error creating service %s: %v", service.Name, err) @@ -207,7 +244,10 @@ func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var service types.ServiceSpec if err := json.NewDecoder(r.Body).Decode(&service); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } rawVersion := r.URL.Query().Get("version") @@ -223,19 +263,12 @@ func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, flags.EncodedRegistryAuth = r.Header.Get("X-Registry-Auth") flags.RegistryAuthFrom = r.URL.Query().Get("registryAuthFrom") flags.Rollback = r.URL.Query().Get("rollback") - cliVersion := r.Header.Get("version") queryRegistry := false - if cliVersion != "" { - if versions.LessThan(cliVersion, "1.30") { + if v := httputils.VersionFromContext(ctx); v != "" { + if versions.LessThan(v, "1.30") { queryRegistry = true } - if versions.LessThan(cliVersion, "1.39") { - if service.TaskTemplate.ContainerSpec != nil { - // Sysctls for docker swarm services weren't supported before - // API version 1.39 - service.TaskTemplate.ContainerSpec.Sysctls = nil - } - } + adjustForAPIVersion(v, &service) } resp, err := sr.backend.UpdateService(vars["id"], version, service, flags, queryRegistry) @@ -309,7 +342,10 @@ func (sr *swarmRouter) getNode(ctx context.Context, w http.ResponseWriter, r *ht func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var node types.NodeSpec if err := json.NewDecoder(r.Body).Decode(&node); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } rawVersion := r.URL.Query().Get("version") @@ -388,7 +424,10 @@ func (sr *swarmRouter) getSecrets(ctx context.Context, w http.ResponseWriter, r func (sr *swarmRouter) createSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var secret types.SecretSpec if err := json.NewDecoder(r.Body).Decode(&secret); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } version := httputils.VersionFromContext(ctx) if secret.Templating != nil && versions.LessThan(version, "1.37") { @@ -426,6 +465,9 @@ func (sr *swarmRouter) getSecret(ctx context.Context, w http.ResponseWriter, r * func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var secret types.SecretSpec if err := json.NewDecoder(r.Body).Decode(&secret); err != nil { + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } return errdefs.InvalidParameter(err) } @@ -459,7 +501,10 @@ func (sr *swarmRouter) getConfigs(ctx context.Context, w http.ResponseWriter, r func (sr *swarmRouter) createConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var config types.ConfigSpec if err := json.NewDecoder(r.Body).Decode(&config); err != nil { - return err + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } + return errdefs.InvalidParameter(err) } version := httputils.VersionFromContext(ctx) @@ -498,6 +543,9 @@ func (sr *swarmRouter) getConfig(ctx context.Context, w http.ResponseWriter, r * func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var config types.ConfigSpec if err := json.NewDecoder(r.Body).Decode(&config); err != nil { + if err == io.EOF { + return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) + } return errdefs.InvalidParameter(err) } diff --git a/api/server/router/swarm/helpers.go b/api/server/router/swarm/helpers.go index 1f57074f92400..969724a1a2a9f 100644 --- a/api/server/router/swarm/helpers.go +++ b/api/server/router/swarm/helpers.go @@ -9,6 +9,8 @@ import ( "github.com/docker/docker/api/server/httputils" basictypes "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/api/types/versions" ) // swarmLogs takes an http response, request, and selector, and writes the logs @@ -64,3 +66,52 @@ func (sr *swarmRouter) swarmLogs(ctx context.Context, w io.Writer, r *http.Reque httputils.WriteLogStream(ctx, w, msgs, logsConfig, !tty) return nil } + +// adjustForAPIVersion takes a version and service spec and removes fields to +// make the spec compatible with the specified version. +func adjustForAPIVersion(cliVersion string, service *swarm.ServiceSpec) { + if cliVersion == "" { + return + } + if versions.LessThan(cliVersion, "1.40") { + if service.TaskTemplate.ContainerSpec != nil { + // Sysctls for docker swarm services weren't supported before + // API version 1.40 + service.TaskTemplate.ContainerSpec.Sysctls = nil + + if service.TaskTemplate.ContainerSpec.Privileges != nil && service.TaskTemplate.ContainerSpec.Privileges.CredentialSpec != nil { + // Support for setting credential-spec through configs was added in API 1.40 + service.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config = "" + } + for _, config := range service.TaskTemplate.ContainerSpec.Configs { + // support for the Runtime target was added in API 1.40 + config.Runtime = nil + } + } + + if service.TaskTemplate.Placement != nil { + // MaxReplicas for docker swarm services weren't supported before + // API version 1.40 + service.TaskTemplate.Placement.MaxReplicas = 0 + } + } + if versions.LessThan(cliVersion, "1.41") { + if service.TaskTemplate.ContainerSpec != nil { + // Capabilities and Ulimits for docker swarm services weren't + // supported before API version 1.41 + service.TaskTemplate.ContainerSpec.CapabilityAdd = nil + service.TaskTemplate.ContainerSpec.CapabilityDrop = nil + service.TaskTemplate.ContainerSpec.Ulimits = nil + } + if service.TaskTemplate.Resources != nil && service.TaskTemplate.Resources.Limits != nil { + // Limits.Pids not supported before API version 1.41 + service.TaskTemplate.Resources.Limits.Pids = 0 + } + + // jobs were only introduced in API version 1.41. Nil out both Job + // modes; if the service is one of these modes and subsequently has no + // mode, then something down the pipe will thrown an error. + service.Mode.ReplicatedJob = nil + service.Mode.GlobalJob = nil + } +} diff --git a/api/server/router/swarm/helpers_test.go b/api/server/router/swarm/helpers_test.go new file mode 100644 index 0000000000000..87fa220125db5 --- /dev/null +++ b/api/server/router/swarm/helpers_test.go @@ -0,0 +1,119 @@ +package swarm // import "github.com/docker/docker/api/server/router/swarm" + +import ( + "reflect" + "testing" + + "github.com/docker/docker/api/types/swarm" + "github.com/docker/go-units" +) + +func TestAdjustForAPIVersion(t *testing.T) { + var ( + expectedSysctls = map[string]string{"foo": "bar"} + ) + // testing the negative -- does this leave everything else alone? -- is + // prohibitively time-consuming to write, because it would need an object + // with literally every field filled in. + spec := &swarm.ServiceSpec{ + TaskTemplate: swarm.TaskSpec{ + ContainerSpec: &swarm.ContainerSpec{ + Sysctls: expectedSysctls, + Privileges: &swarm.Privileges{ + CredentialSpec: &swarm.CredentialSpec{ + Config: "someconfig", + }, + }, + Configs: []*swarm.ConfigReference{ + { + File: &swarm.ConfigReferenceFileTarget{ + Name: "foo", + UID: "bar", + GID: "baz", + }, + ConfigID: "configFile", + ConfigName: "configFile", + }, + { + Runtime: &swarm.ConfigReferenceRuntimeTarget{}, + ConfigID: "configRuntime", + ConfigName: "configRuntime", + }, + }, + Ulimits: []*units.Ulimit{ + { + Name: "nofile", + Soft: 100, + Hard: 200, + }, + }, + }, + Placement: &swarm.Placement{ + MaxReplicas: 222, + }, + Resources: &swarm.ResourceRequirements{ + Limits: &swarm.Limit{ + Pids: 300, + }, + }, + }, + } + + // first, does calling this with a later version correctly NOT strip + // fields? do the later version first, so we can reuse this spec in the + // next test. + adjustForAPIVersion("1.41", spec) + if !reflect.DeepEqual(spec.TaskTemplate.ContainerSpec.Sysctls, expectedSysctls) { + t.Error("Sysctls was stripped from spec") + } + + if spec.TaskTemplate.Resources.Limits.Pids == 0 { + t.Error("PidsLimit was stripped from spec") + } + if spec.TaskTemplate.Resources.Limits.Pids != 300 { + t.Error("PidsLimit did not preserve the value from spec") + } + + if spec.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config != "someconfig" { + t.Error("CredentialSpec.Config field was stripped from spec") + } + + if spec.TaskTemplate.ContainerSpec.Configs[1].Runtime == nil { + t.Error("ConfigReferenceRuntimeTarget was stripped from spec") + } + + if spec.TaskTemplate.Placement.MaxReplicas != 222 { + t.Error("MaxReplicas was stripped from spec") + } + + if len(spec.TaskTemplate.ContainerSpec.Ulimits) == 0 { + t.Error("Ulimits were stripped from spec") + } + + // next, does calling this with an earlier version correctly strip fields? + adjustForAPIVersion("1.29", spec) + if spec.TaskTemplate.ContainerSpec.Sysctls != nil { + t.Error("Sysctls was not stripped from spec") + } + + if spec.TaskTemplate.Resources.Limits.Pids != 0 { + t.Error("PidsLimit was not stripped from spec") + } + + if spec.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config != "" { + t.Error("CredentialSpec.Config field was not stripped from spec") + } + + if spec.TaskTemplate.ContainerSpec.Configs[1].Runtime != nil { + t.Error("ConfigReferenceRuntimeTarget was not stripped from spec") + } + + if spec.TaskTemplate.Placement.MaxReplicas != 0 { + t.Error("MaxReplicas was not stripped from spec") + } + + if len(spec.TaskTemplate.ContainerSpec.Ulimits) != 0 { + t.Error("Ulimits were not stripped from spec") + } + +} diff --git a/api/server/router/system/backend.go b/api/server/router/system/backend.go index f5d2d9810137c..1d704348c4991 100644 --- a/api/server/router/system/backend.go +++ b/api/server/router/system/backend.go @@ -10,12 +10,24 @@ import ( "github.com/docker/docker/api/types/swarm" ) +// DiskUsageOptions holds parameters for system disk usage query. +type DiskUsageOptions struct { + // Containers controls whether container disk usage should be computed. + Containers bool + + // Images controls whether image disk usage should be computed. + Images bool + + // Volumes controls whether volume disk usage should be computed. + Volumes bool +} + // Backend is the methods that need to be implemented to provide // system specific functionality. type Backend interface { - SystemInfo() (*types.Info, error) + SystemInfo() *types.Info SystemVersion() types.Version - SystemDiskUsage(ctx context.Context) (*types.DiskUsage, error) + SystemDiskUsage(ctx context.Context, opts DiskUsageOptions) (*types.DiskUsage, error) SubscribeToEvents(since, until time.Time, ef filters.Args) ([]events.Message, chan interface{}) UnsubscribeFromEvents(chan interface{}) AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error) diff --git a/api/server/router/system/system.go b/api/server/router/system/system.go index bbe32fb4bba33..9624239aaeb83 100644 --- a/api/server/router/system/system.go +++ b/api/server/router/system/system.go @@ -2,8 +2,7 @@ package system // import "github.com/docker/docker/api/server/router/system" import ( "github.com/docker/docker/api/server/router" - "github.com/docker/docker/builder/builder-next" - "github.com/docker/docker/builder/fscache" + buildkit "github.com/docker/docker/builder/builder-next" ) // systemRouter provides information about the Docker system overall. @@ -12,17 +11,15 @@ type systemRouter struct { backend Backend cluster ClusterBackend routes []router.Route - fscache *fscache.FSCache // legacy builder *buildkit.Builder features *map[string]bool } // NewRouter initializes a new system router -func NewRouter(b Backend, c ClusterBackend, fscache *fscache.FSCache, builder *buildkit.Builder, features *map[string]bool) router.Router { +func NewRouter(b Backend, c ClusterBackend, builder *buildkit.Builder, features *map[string]bool) router.Router { r := &systemRouter{ backend: b, cluster: c, - fscache: fscache, builder: builder, features: features, } @@ -30,10 +27,11 @@ func NewRouter(b Backend, c ClusterBackend, fscache *fscache.FSCache, builder *b r.routes = []router.Route{ router.NewOptionsRoute("/{anyroute:.*}", optionsHandler), router.NewGetRoute("/_ping", r.pingHandler), - router.NewGetRoute("/events", r.getEvents, router.WithCancel), + router.NewHeadRoute("/_ping", r.pingHandler), + router.NewGetRoute("/events", r.getEvents), router.NewGetRoute("/info", r.getInfo), router.NewGetRoute("/version", r.getVersion), - router.NewGetRoute("/system/df", r.getDiskUsage, router.WithCancel), + router.NewGetRoute("/system/df", r.getDiskUsage), router.NewPostRoute("/auth", r.postAuth), } diff --git a/api/server/router/system/system_routes.go b/api/server/router/system/system_routes.go index a2ff692de37b9..d25d92204c60c 100644 --- a/api/server/router/system/system_routes.go +++ b/api/server/router/system/system_routes.go @@ -16,7 +16,7 @@ import ( timetypes "github.com/docker/docker/api/types/time" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/pkg/ioutils" - pkgerrors "github.com/pkg/errors" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" ) @@ -27,21 +27,28 @@ func optionsHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, } func (s *systemRouter) pingHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate") + w.Header().Add("Pragma", "no-cache") + builderVersion := build.BuilderVersion(*s.features) if bv := builderVersion; bv != "" { w.Header().Set("Builder-Version", string(bv)) } + if r.Method == http.MethodHead { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.Header().Set("Content-Length", "0") + return nil + } _, err := w.Write([]byte{'O', 'K'}) return err } func (s *systemRouter) getInfo(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - info, err := s.backend.SystemInfo() - if err != nil { - return err - } + info := s.backend.SystemInfo() + if s.cluster != nil { info.Swarm = s.cluster.Info() + info.Warnings = append(info.Warnings, info.Swarm.Warnings...) } if versions.LessThan(httputils.VersionFromContext(ctx), "1.25") { @@ -83,46 +90,86 @@ func (s *systemRouter) getVersion(ctx context.Context, w http.ResponseWriter, r } func (s *systemRouter) getDiskUsage(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - eg, ctx := errgroup.WithContext(ctx) - - var du *types.DiskUsage - eg.Go(func() error { - var err error - du, err = s.backend.SystemDiskUsage(ctx) + if err := httputils.ParseForm(r); err != nil { return err - }) + } - var builderSize int64 // legacy - eg.Go(func() error { - var err error - builderSize, err = s.fscache.DiskUsage(ctx) - if err != nil { - return pkgerrors.Wrap(err, "error getting fscache build cache usage") + version := httputils.VersionFromContext(ctx) + + var getContainers, getImages, getVolumes, getBuildCache bool + typeStrs, ok := r.Form["type"] + if versions.LessThan(version, "1.42") || !ok { + getContainers, getImages, getVolumes, getBuildCache = true, true, true, true + } else { + for _, typ := range typeStrs { + switch types.DiskUsageObject(typ) { + case types.ContainerObject: + getContainers = true + case types.ImageObject: + getImages = true + case types.VolumeObject: + getVolumes = true + case types.BuildCacheObject: + getBuildCache = true + default: + return invalidRequestError{Err: fmt.Errorf("unknown object type: %s", typ)} + } } - return nil - }) + } + + eg, ctx := errgroup.WithContext(ctx) + + var systemDiskUsage *types.DiskUsage + if getContainers || getImages || getVolumes { + eg.Go(func() error { + var err error + systemDiskUsage, err = s.backend.SystemDiskUsage(ctx, DiskUsageOptions{ + Containers: getContainers, + Images: getImages, + Volumes: getVolumes, + }) + return err + }) + } var buildCache []*types.BuildCache - eg.Go(func() error { - var err error - buildCache, err = s.builder.DiskUsage(ctx) - if err != nil { - return pkgerrors.Wrap(err, "error getting build cache usage") - } - return nil - }) + if getBuildCache { + eg.Go(func() error { + var err error + buildCache, err = s.builder.DiskUsage(ctx) + if err != nil { + return errors.Wrap(err, "error getting build cache usage") + } + if buildCache == nil { + // Ensure empty `BuildCache` field is represented as empty JSON array(`[]`) + // instead of `null` to be consistent with `Images`, `Containers` etc. + buildCache = []*types.BuildCache{} + } + return nil + }) + } if err := eg.Wait(); err != nil { return err } - for _, b := range buildCache { - builderSize += b.Size + var builderSize int64 + if versions.LessThan(version, "1.42") { + for _, b := range buildCache { + builderSize += b.Size + } } - du.BuilderSize = builderSize - du.BuildCache = buildCache - + du := types.DiskUsage{ + BuildCache: buildCache, + BuilderSize: builderSize, + } + if systemDiskUsage != nil { + du.LayersSize = systemDiskUsage.LayersSize + du.Images = systemDiskUsage.Images + du.Containers = systemDiskUsage.Containers + du.Volumes = systemDiskUsage.Volumes + } return httputils.WriteJSON(w, http.StatusOK, du) } @@ -165,7 +212,9 @@ func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r * if !onlyPastEvents { dur := until.Sub(now) - timeout = time.After(dur) + timer := time.NewTimer(dur) + defer timer.Stop() + timeout = timer.C } } diff --git a/api/server/router/volume/volume.go b/api/server/router/volume/volume.go index 04f365e370c7b..875497b3eb54f 100644 --- a/api/server/router/volume/volume.go +++ b/api/server/router/volume/volume.go @@ -29,7 +29,7 @@ func (r *volumeRouter) initRoutes() { router.NewGetRoute("/volumes/{name:.*}", r.getVolumeByName), // POST router.NewPostRoute("/volumes/create", r.postVolumesCreate), - router.NewPostRoute("/volumes/prune", r.postVolumesPrune, router.WithCancel), + router.NewPostRoute("/volumes/prune", r.postVolumesPrune), // DELETE router.NewDeleteRoute("/volumes/{name:.*}", r.deleteVolumes), } diff --git a/api/server/router/volume/volume_routes.go b/api/server/router/volume/volume_routes.go index e892d1a52403d..4fa4cb53aab0e 100644 --- a/api/server/router/volume/volume_routes.go +++ b/api/server/router/volume/volume_routes.go @@ -56,7 +56,7 @@ func (v *volumeRouter) postVolumesCreate(ctx context.Context, w http.ResponseWri if err == io.EOF { return errdefs.InvalidParameter(errors.New("got EOF while reading request body")) } - return err + return errdefs.InvalidParameter(err) } volume, err := v.backend.Create(ctx, req.Name, req.Driver, opts.WithCreateOptions(req.DriverOpts), opts.WithCreateLabels(req.Labels)) diff --git a/api/server/router_swapper.go b/api/server/router_swapper.go deleted file mode 100644 index e8087492c475c..0000000000000 --- a/api/server/router_swapper.go +++ /dev/null @@ -1,30 +0,0 @@ -package server // import "github.com/docker/docker/api/server" - -import ( - "net/http" - "sync" - - "github.com/gorilla/mux" -) - -// routerSwapper is an http.Handler that allows you to swap -// mux routers. -type routerSwapper struct { - mu sync.Mutex - router *mux.Router -} - -// Swap changes the old router with the new one. -func (rs *routerSwapper) Swap(newRouter *mux.Router) { - rs.mu.Lock() - rs.router = newRouter - rs.mu.Unlock() -} - -// ServeHTTP makes the routerSwapper to implement the http.Handler interface. -func (rs *routerSwapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { - rs.mu.Lock() - router := rs.router - rs.mu.Unlock() - router.ServeHTTP(w, r) -} diff --git a/api/server/server.go b/api/server/server.go index 3b4e630c41613..a8ce5cb1637ef 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -12,6 +12,7 @@ import ( "github.com/docker/docker/api/server/router" "github.com/docker/docker/api/server/router/debug" "github.com/docker/docker/dockerversion" + "github.com/docker/docker/errdefs" "github.com/gorilla/mux" "github.com/sirupsen/logrus" ) @@ -31,11 +32,10 @@ type Config struct { // Server contains instance details for the server type Server struct { - cfg *Config - servers []*HTTPServer - routers []router.Router - routerSwapper *routerSwapper - middlewares []middleware.Middleware + cfg *Config + servers []*HTTPServer + routers []router.Router + middlewares []middleware.Middleware } // New returns a new instance of the server based on the specified configuration. @@ -79,7 +79,7 @@ func (s *Server) Close() { func (s *Server) serveAPI() error { var chErrors = make(chan error, len(s.servers)) for _, srv := range s.servers { - srv.srv.Handler = s.routerSwapper + srv.srv.Handler = s.createMux() go func(srv *HTTPServer) { var err error logrus.Infof("API listen on %s", srv.l.Addr()) @@ -129,7 +129,8 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc { // use intermediate variable to prevent "should not use basic type // string as key in context.WithValue" golint errors - ctx := context.WithValue(context.Background(), dockerversion.UAStringKey{}, r.Header.Get("User-Agent")) + ctx := context.WithValue(r.Context(), dockerversion.UAStringKey{}, r.Header.Get("User-Agent")) + r = r.WithContext(ctx) handlerFunc := s.handlerWithGlobalMiddlewares(handler) vars := mux.Vars(r) @@ -138,7 +139,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc { } if err := handlerFunc(ctx, w, r, vars); err != nil { - statusCode := httputils.GetHTTPErrorStatusCode(err) + statusCode := errdefs.GetHTTPErrorStatusCode(err) if statusCode >= 500 { logrus.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err) } @@ -151,11 +152,6 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc { // This method also enables the Go profiler. func (s *Server) InitRouter(routers ...router.Router) { s.routers = append(s.routers, routers...) - - m := s.createMux() - s.routerSwapper = &routerSwapper{ - router: m, - } } type pageNotFoundError struct{} @@ -191,6 +187,7 @@ func (s *Server) createMux() *mux.Router { notFoundHandler := httputils.MakeErrorHandler(pageNotFoundError{}) m.HandleFunc(versionMatcher+"/{path:.*}", notFoundHandler) m.NotFoundHandler = notFoundHandler + m.MethodNotAllowedHandler = notFoundHandler return m } diff --git a/api/server/server_test.go b/api/server/server_test.go index e0fac30ab7179..a3e8124a880f8 100644 --- a/api/server/server_test.go +++ b/api/server/server_test.go @@ -22,7 +22,7 @@ func TestMiddlewares(t *testing.T) { srv.UseMiddleware(middleware.NewVersionMiddleware("0.1omega2", api.DefaultVersion, api.MinVersion)) - req, _ := http.NewRequest("GET", "/containers/json", nil) + req, _ := http.NewRequest(http.MethodGet, "/containers/json", nil) resp := httptest.NewRecorder() ctx := context.Background() diff --git a/api/swagger.yaml b/api/swagger.yaml index 1cabb9b516b3c..b8b3ea0946554 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -19,20 +19,26 @@ produces: consumes: - "application/json" - "text/plain" -basePath: "/v1.39" +basePath: "/v1.42" info: title: "Docker Engine API" - version: "1.39" + version: "1.42" x-logo: url: "https://docs.docker.com/images/logo-docker-main.png" description: | - The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + The Engine API is an HTTP API served by Docker Engine. It is the API the + Docker client uses to communicate with the Engine, so everything the Docker + client can do can be done with the API. - Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + Most of the client's commands map directly to API endpoints (e.g. `docker ps` + is `GET /containers/json`). The notable exception is running containers, + which consists of several API calls. # Errors - The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + The API uses standard HTTP status codes to indicate the success or failure + of the API call. The body of the response will be JSON in the following + format: ``` { @@ -49,8 +55,8 @@ info: the URL is not supported by the daemon, a HTTP `400 Bad Request` error message is returned. - If you omit the version-prefix, the current version of the API (v1.39) is used. - For example, calling `/info` is the same as calling `/v1.39/info`. Using the + If you omit the version-prefix, the current version of the API (v1.42) is used. + For example, calling `/info` is the same as calling `/v1.42/info`. Using the API without a version-prefix is deprecated and will be removed in a future release. Engine releases in the near future should support this version of the API, @@ -65,7 +71,11 @@ info: # Authentication - Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + Authentication for registries is handled client side. The client has to send + authentication details to various endpoints that need to communicate with + registries, such as `POST /images/(name)/push`. These are sent as + `X-Registry-Auth` header as a [base64url encoded](https://tools.ietf.org/html/rfc4648#section-5) + (JSON) string with the following structure: ``` { @@ -76,9 +86,11 @@ info: } ``` - The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + The `serveraddress` is a domain/IP without a protocol. Throughout this + structure, double quotes are required. - If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), + you can just pass this instead of credentials: ``` { @@ -104,7 +116,9 @@ tags: - name: "Network" x-displayName: "Networks" description: | - Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/engine/userguide/networking/) for more information. + Networks are user-defined networks that containers can be attached to. + See the [networking documentation](https://docs.docker.com/network/) + for more information. - name: "Volume" x-displayName: "Volumes" description: | @@ -112,34 +126,46 @@ tags: - name: "Exec" x-displayName: "Exec" description: | - Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + Run new commands inside running containers. Refer to the + [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) + for more information. + + To exec a command in a container, you first need to create an exec instance, + then start it. These two API endpoints are wrapped up in a single command-line + command, `docker exec`. - To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. # Swarm things - name: "Swarm" x-displayName: "Swarm" description: | - Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + Engines can be clustered together in a swarm. Refer to the + [swarm mode documentation](https://docs.docker.com/engine/swarm/) + for more information. - name: "Node" x-displayName: "Nodes" description: | - Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + Nodes are instances of the Engine participating in a swarm. Swarm mode + must be enabled for these endpoints to work. - name: "Service" x-displayName: "Services" description: | - Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + Services are the definitions of tasks to run on a swarm. Swarm mode must + be enabled for these endpoints to work. - name: "Task" x-displayName: "Tasks" description: | - A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + A task is a container running on a swarm. It is the atomic scheduling unit + of swarm. Swarm mode must be enabled for these endpoints to work. - name: "Secret" x-displayName: "Secrets" description: | - Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + Secrets are sensitive data that can be used by services. Swarm mode must + be enabled for these endpoints to work. - name: "Config" x-displayName: "Configs" description: | - Configs are application configurations that can be used by services. Swarm mode must be enabled for these endpoints to work. + Configs are application configurations that can be used by services. Swarm + mode must be enabled for these endpoints to work. # System things - name: "Plugin" x-displayName: "Plugins" @@ -210,6 +236,43 @@ definitions: PathInContainer: "/dev/deviceName" CgroupPermissions: "mrw" + DeviceRequest: + type: "object" + description: "A request for devices to be sent to device drivers" + properties: + Driver: + type: "string" + example: "nvidia" + Count: + type: "integer" + example: -1 + DeviceIDs: + type: "array" + items: + type: "string" + example: + - "0" + - "1" + - "GPU-fef8089b-4820-abfc-e83e-94318197576e" + Capabilities: + description: | + A list of capabilities; an OR list of AND lists of capabilities. + type: "array" + items: + type: "array" + items: + type: "string" + example: + # gpu AND nvidia AND compute + - ["gpu", "nvidia", "compute"] + Options: + description: | + Driver-specific options, specified as a key/value pairs. These options + are passed directly to the driver. + type: "object" + additionalProperties: + type: "string" + ThrottleDevice: type: "object" properties: @@ -265,6 +328,10 @@ definitions: - "rshared" - "slave" - "rslave" + NonRecursive: + description: "Disable recursive bind mount." + type: "boolean" + default: false VolumeOptions: description: "Optional configuration for the `volume` type." type: "object" @@ -304,26 +371,31 @@ definitions: RestartPolicy: description: | - The behavior to apply when the container exits. The default is not to restart. + The behavior to apply when the container exits. The default is not to + restart. - An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + An ever increasing delay (double the previous delay, starting at 100ms) is + added before each restart to prevent flooding the server. type: "object" properties: Name: type: "string" description: | - Empty string means not to restart + - `no` Do not automatically restart - `always` Always restart - `unless-stopped` Restart always except when the user has manually stopped the container - `on-failure` Restart only when the container exit code is non-zero enum: - "" + - "no" - "always" - "unless-stopped" - "on-failure" MaximumRetryCount: type: "integer" - description: "If `on-failure` is used, the number of times to retry before giving up" + description: | + If `on-failure` is used, the number of times to retry before giving up. Resources: description: "A container's resources (cgroups config, ulimits, etc)" @@ -331,7 +403,9 @@ definitions: properties: # Applicable to all platforms CpuShares: - description: "An integer value representing this container's relative CPU weight versus other containers." + description: | + An integer value representing this container's relative CPU weight + versus other containers. type: "integer" Memory: description: "Memory limit in bytes." @@ -340,7 +414,11 @@ definitions: default: 0 # Applicable to UNIX platforms CgroupParent: - description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + description: | + Path to `cgroups` under which the container's `cgroup` is created. If + the path is not absolute, the path is considered to be relative to the + `cgroups` path of the init process. Cgroups are created if they do not + already exist. type: "string" BlkioWeight: description: "Block IO weight (relative weight)." @@ -349,7 +427,11 @@ definitions: maximum: 1000 BlkioWeightDevice: description: | - Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + Block IO weight (relative device weight) in the form: + + ``` + [{"Path": "device_path", "Weight": weight}] + ``` type: "array" items: type: "object" @@ -361,25 +443,41 @@ definitions: minimum: 0 BlkioDeviceReadBps: description: | - Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + Limit read rate (bytes per second) from a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` type: "array" items: $ref: "#/definitions/ThrottleDevice" BlkioDeviceWriteBps: description: | - Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + Limit write rate (bytes per second) to a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` type: "array" items: $ref: "#/definitions/ThrottleDevice" BlkioDeviceReadIOps: description: | - Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + Limit read rate (IO per second) from a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` type: "array" items: $ref: "#/definitions/ThrottleDevice" BlkioDeviceWriteIOps: description: | - Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + Limit write rate (IO per second) to a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` type: "array" items: $ref: "#/definitions/ThrottleDevice" @@ -388,23 +486,31 @@ definitions: type: "integer" format: "int64" CpuQuota: - description: "Microseconds of CPU time that the container can get in a CPU period." + description: | + Microseconds of CPU time that the container can get in a CPU period. type: "integer" format: "int64" CpuRealtimePeriod: - description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + description: | + The length of a CPU real-time period in microseconds. Set to 0 to + allocate no time allocated to real-time tasks. type: "integer" format: "int64" CpuRealtimeRuntime: - description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + description: | + The length of a CPU real-time runtime in microseconds. Set to 0 to + allocate no time allocated to real-time tasks. type: "integer" format: "int64" CpusetCpus: - description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + description: | + CPUs in which to allow execution (e.g., `0-3`, `0,1`). type: "string" example: "0-3" CpusetMems: - description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + description: | + Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only + effective on NUMA systems. type: "string" Devices: description: "A list of devices to add to the container." @@ -417,12 +523,25 @@ definitions: items: type: "string" example: "c 13:* rwm" - DiskQuota: - description: "Disk limit (in bytes)." + DeviceRequests: + description: | + A list of requests for devices to be sent to device drivers. + type: "array" + items: + $ref: "#/definitions/DeviceRequest" + KernelMemory: + description: | + Kernel memory limit in bytes. + +


+ + > **Deprecated**: This field is deprecated as the kernel 5.4 deprecated + > `kmem.limit_in_bytes`. type: "integer" format: "int64" - KernelMemory: - description: "Kernel memory limit in bytes." + example: 209715200 + KernelMemoryTCP: + description: "Hard limit for kernel TCP buffer memory (in bytes)." type: "integer" format: "int64" MemoryReservation: @@ -430,16 +549,20 @@ definitions: type: "integer" format: "int64" MemorySwap: - description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + description: | + Total memory limit (memory + swap). Set as `-1` to enable unlimited + swap. type: "integer" format: "int64" MemorySwappiness: - description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + description: | + Tune a container's memory swappiness behavior. Accepts an integer + between 0 and 100. type: "integer" format: "int64" minimum: 0 maximum: 100 - NanoCPUs: + NanoCpus: description: "CPU quota in units of 10-9 CPUs." type: "integer" format: "int64" @@ -447,16 +570,26 @@ definitions: description: "Disable OOM Killer for the container." type: "boolean" Init: - description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used." + description: | + Run an init inside the container that forwards signals and reaps + processes. This field is omitted if empty, and the default (as + configured on the daemon) is used. type: "boolean" x-nullable: true PidsLimit: - description: "Tune a container's pids limit. Set -1 for unlimited." + description: | + Tune a container's PIDs limit. Set `0` or `-1` for unlimited, or `null` + to not change. type: "integer" format: "int64" + x-nullable: true Ulimits: description: | - A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + A list of resource limits to set in the container. For example: + + ``` + {"Name": "nofile", "Soft": 1024, "Hard": 2048} + ``` type: "array" items: type: "object" @@ -475,14 +608,18 @@ definitions: description: | The number of usable CPUs (Windows only). - On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + On Windows Server containers, the processor resource controls are + mutually exclusive. The order of precedence is `CPUCount` first, then + `CPUShares`, and `CPUPercent` last. type: "integer" format: "int64" CpuPercent: description: | The usable percentage of the available CPUs (Windows only). - On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + On Windows Server containers, the processor resource controls are + mutually exclusive. The order of precedence is `CPUCount` first, then + `CPUShares`, and `CPUPercent` last. type: "integer" format: "int64" IOMaximumIOps: @@ -490,12 +627,37 @@ definitions: type: "integer" format: "int64" IOMaximumBandwidth: - description: "Maximum IO in bytes per second for the container system drive (Windows only)" + description: | + Maximum IO in bytes per second for the container system drive + (Windows only). + type: "integer" + format: "int64" + + Limit: + description: | + An object describing a limit on resources which can be requested by a task. + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: type: "integer" format: "int64" + example: 8272408576 + Pids: + description: | + Limits the maximum number of PIDs in the container. Set `0` for unlimited. + type: "integer" + format: "int64" + default: 0 + example: 100 ResourceObject: - description: "An object describing the resources which can be advertised by a node and requested by a task" + description: | + An object describing the resources which can be advertised by a node and + requested by a task. type: "object" properties: NanoCPUs: @@ -510,7 +672,9 @@ definitions: $ref: "#/definitions/GenericResources" GenericResources: - description: "User-defined resources can be either Integer resources (e.g, `SSD=3`) or String resources (e.g, `GPU=UUID1`)" + description: | + User-defined resources can be either Integer resources (e.g, `SSD=3`) or + String resources (e.g, `GPU=UUID1`). type: "array" items: type: "object" @@ -557,17 +721,92 @@ definitions: items: type: "string" Interval: - description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + description: | + The time to wait between checks in nanoseconds. It should be 0 or at + least 1000000 (1 ms). 0 means inherit. type: "integer" Timeout: - description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + description: | + The time to wait before considering the check to have hung. It should + be 0 or at least 1000000 (1 ms). 0 means inherit. type: "integer" Retries: - description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + description: | + The number of consecutive failures needed to consider a container as + unhealthy. 0 means inherit. type: "integer" StartPeriod: - description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + description: | + Start period for the container to initialize before starting + health-retries countdown in nanoseconds. It should be 0 or at least + 1000000 (1 ms). 0 means inherit. + type: "integer" + + Health: + description: | + Health stores information about the container's healthcheck results. + type: "object" + x-nullable: true + properties: + Status: + description: | + Status is one of `none`, `starting`, `healthy` or `unhealthy` + + - "none" Indicates there is no healthcheck + - "starting" Starting indicates that the container is not yet ready + - "healthy" Healthy indicates that the container is running correctly + - "unhealthy" Unhealthy indicates that the container has a problem + type: "string" + enum: + - "none" + - "starting" + - "healthy" + - "unhealthy" + example: "healthy" + FailingStreak: + description: "FailingStreak is the number of consecutive failures" + type: "integer" + example: 0 + Log: + type: "array" + description: | + Log contains the last few results (oldest first) + items: + $ref: "#/definitions/HealthcheckResult" + + HealthcheckResult: + description: | + HealthcheckResult stores information about a single run of a healthcheck probe + type: "object" + x-nullable: true + properties: + Start: + description: | + Date and time at which this check started in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "date-time" + example: "2020-01-04T10:44:24.496525531Z" + End: + description: | + Date and time at which this check ended in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2020-01-04T10:45:21.364524523Z" + ExitCode: + description: | + ExitCode meanings: + + - `0` healthy + - `1` unhealthy + - `2` reserved (considered unhealthy) + - other values: error running probe type: "integer" + example: 0 + Output: + description: "Output from last check" + type: "string" HostConfig: description: "Container configuration that depends on the host we are running on" @@ -579,12 +818,44 @@ definitions: Binds: type: "array" description: | - A list of volume bindings for this container. Each volume binding is a string in one of these forms: - - - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. - - `host-src:container-dest:ro` to make the bind mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. - - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. - - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + A list of volume bindings for this container. Each volume binding + is a string in one of these forms: + + - `host-src:container-dest[:options]` to bind-mount a host path + into the container. Both `host-src`, and `container-dest` must + be an _absolute_ path. + - `volume-name:container-dest[:options]` to bind-mount a volume + managed by a volume driver into the container. `container-dest` + must be an _absolute_ path. + + `options` is an optional, comma-delimited list of: + + - `nocopy` disables automatic copying of data from the container + path to the volume. The `nocopy` flag only applies to named volumes. + - `[ro|rw]` mounts a volume read-only or read-write, respectively. + If omitted or set to `rw`, volumes are mounted read-write. + - `[z|Z]` applies SELinux labels to allow or deny multiple containers + to read and write to the same volume. + - `z`: a _shared_ content label is applied to the content. This + label indicates that multiple containers can share the volume + content, for both reading and writing. + - `Z`: a _private unshared_ label is applied to the content. + This label indicates that only the current container can use + a private volume. Labeling systems such as SELinux require + proper labels to be placed on volume content that is mounted + into a container. Without a label, the security system can + prevent a container's processes from using the content. By + default, the labels set by the host operating system are not + modified. + - `[[r]shared|[r]slave|[r]private]` specifies mount + [propagation behavior](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt). + This only applies to bind-mounted volumes, not internal volumes + or named volumes. Mount propagation requires the source mount + point (the location where the source directory is mounted in the + host operating system) to have the correct propagation properties. + For shared volumes, the source mount point must be set to `shared`. + For slave volumes, the mount must be set to either `shared` or + `slave`. items: type: "string" ContainerIDFile: @@ -612,25 +883,33 @@ definitions: type: "string" NetworkMode: type: "string" - description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken - as a custom network's name to which this container should connect to." + description: | + Network mode to use for this container. Supported standard values + are: `bridge`, `host`, `none`, and `container:`. Any + other value is taken as a custom network's name to which this + container should connect to. PortBindings: $ref: "#/definitions/PortMap" RestartPolicy: $ref: "#/definitions/RestartPolicy" AutoRemove: type: "boolean" - description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + description: | + Automatically remove the container when the container's process + exits. This has no effect if `RestartPolicy` is set. VolumeDriver: type: "string" description: "Driver that this container uses to mount volumes." VolumesFrom: type: "array" - description: "A list of volumes to inherit from another container, specified in the form `[:]`." + description: | + A list of volumes to inherit from another container, specified in + the form `[:]`. items: type: "string" Mounts: - description: "Specification for mounts to be added to the container." + description: | + Specification for mounts to be added to the container. type: "array" items: $ref: "#/definitions/Mount" @@ -638,14 +917,31 @@ definitions: # Applicable to UNIX platforms CapAdd: type: "array" - description: "A list of kernel capabilities to add to the container." + description: | + A list of kernel capabilities to add to the container. Conflicts + with option 'Capabilities'. items: type: "string" CapDrop: type: "array" - description: "A list of kernel capabilities to drop from the container." + description: | + A list of kernel capabilities to drop from the container. Conflicts + with option 'Capabilities'. items: type: "string" + CgroupnsMode: + type: "string" + enum: + - "private" + - "host" + description: | + cgroup namespace mode for the container. Possible values are: + + - `"private"`: the container runs in its own private cgroup namespace + - `"host"`: use the host system's cgroup namespace + + If not specified, the daemon default is used, which can either be `"private"` + or `"host"`, depending on daemon version, kernel support and configuration. Dns: type: "array" description: "A list of DNS servers for the container to use." @@ -664,43 +960,49 @@ definitions: ExtraHosts: type: "array" description: | - A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + A list of hostnames/IP mappings to add to the container's `/etc/hosts` + file. Specified in the form `["hostname:IP"]`. items: type: "string" GroupAdd: type: "array" - description: "A list of additional groups that the container process will run as." + description: | + A list of additional groups that the container process will run as. items: type: "string" IpcMode: type: "string" description: | - IPC sharing mode for the container. Possible values are: + IPC sharing mode for the container. Possible values are: - - `"none"`: own private IPC namespace, with /dev/shm not mounted - - `"private"`: own private IPC namespace - - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers - - `"container:"`: join another (shareable) container's IPC namespace - - `"host"`: use the host system's IPC namespace + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace - If not specified, daemon default is used, which can either be `"private"` - or `"shareable"`, depending on daemon version and configuration. + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. Cgroup: type: "string" description: "Cgroup to use for the container." Links: type: "array" - description: "A list of links for the container in the form `container_name:alias`." + description: | + A list of links for the container in the form `container_name:alias`. items: type: "string" OomScoreAdj: type: "integer" - description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + description: | + An integer value containing the score given to the container in + order to tune OOM killer preferences. example: 500 PidMode: type: "string" description: | - Set the PID (Process) Namespace mode for the container. It can be either: + Set the PID (Process) Namespace mode for the container. It can be + either: - `"container:"`: joins another container's PID namespace - `"host"`: use the host's PID namespace inside the container @@ -713,11 +1015,13 @@ definitions: Allocates an ephemeral host port for all of a container's exposed ports. - Ports are de-allocated when the container stops and allocated when the container starts. - The allocated port might be changed when restarting the container. + Ports are de-allocated when the container stops and allocated when + the container starts. The allocated port might be changed when + restarting the container. - The port is selected from the ephemeral port range that depends on the kernel. - For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`. + The port is selected from the ephemeral port range that depends on + the kernel. For example, on Linux the range is defined by + `/proc/sys/net/ipv4/ip_local_port_range`. ReadonlyRootfs: type: "boolean" description: "Mount the container's root filesystem as read only." @@ -736,7 +1040,12 @@ definitions: Tmpfs: type: "object" description: | - A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + A map of container directories which should be replaced by tmpfs + mounts, and their corresponding mount options. For example: + + ``` + { "/run": "rw,noexec,nosuid,size=65536k" } + ``` additionalProperties: type: "string" UTSMode: @@ -744,15 +1053,23 @@ definitions: description: "UTS namespace to use for the container." UsernsMode: type: "string" - description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + description: | + Sets the usernamespace mode for the container when usernamespace + remapping option is enabled. ShmSize: type: "integer" - description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + description: | + Size of `/dev/shm` in bytes. If omitted, the system uses 64MB. minimum: 0 Sysctls: type: "object" description: | - A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + A list of kernel parameters (sysctls) to set in the container. + For example: + + ``` + {"net.ipv4.ip_forward": "1"} + ``` additionalProperties: type: "string" Runtime: @@ -761,7 +1078,8 @@ definitions: # Applicable to Windows ConsoleSize: type: "array" - description: "Initial console size, as an `[height, width]` array. (Windows only)" + description: | + Initial console size, as an `[height, width]` array. (Windows only) minItems: 2 maxItems: 2 items: @@ -769,19 +1087,24 @@ definitions: minimum: 0 Isolation: type: "string" - description: "Isolation technology of the container. (Windows only)" + description: | + Isolation technology of the container. (Windows only) enum: - "default" - "process" - "hyperv" MaskedPaths: type: "array" - description: "The list of paths to be masked inside the container (this overrides the default set of paths)" + description: | + The list of paths to be masked inside the container (this overrides + the default set of paths). items: type: "string" ReadonlyPaths: type: "array" - description: "The list of paths to be set as read-only inside the container (this overrides the default set of paths)" + description: | + The list of paths to be set as read-only inside the container + (this overrides the default set of paths). items: type: "string" @@ -822,7 +1145,8 @@ definitions: - {} default: {} Tty: - description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + description: | + Attach standard streams to a TTY, including `stdin` if it is not closed. type: "boolean" default: false OpenStdin: @@ -835,12 +1159,15 @@ definitions: default: false Env: description: | - A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + A list of environment variables to set inside the container in the + form `["VAR=value", ...]`. A variable without `=` is removed from the + environment, rather than to have an empty value. type: "array" items: type: "string" Cmd: - description: "Command to run specified as a string or an array of strings." + description: | + Command to run specified as a string or an array of strings. type: "array" items: type: "string" @@ -850,10 +1177,13 @@ definitions: description: "Command is already escaped (Windows only)" type: "boolean" Image: - description: "The name of the image to use when creating the container" + description: | + The name of the image to use when creating the container/ type: "string" Volumes: - description: "An object mapping mount point paths inside the container to empty objects." + description: | + An object mapping mount point paths inside the container to empty + objects. type: "object" additionalProperties: type: "object" @@ -867,7 +1197,9 @@ definitions: description: | The entry point for the container as a string or an array of strings. - If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + If the array consists of exactly one empty string (`[""]`) then the + entry point is reset to system default (i.e., the entry point used by + docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). type: "array" items: type: "string" @@ -878,7 +1210,8 @@ definitions: description: "MAC address of the container." type: "string" OnBuild: - description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + description: | + `ONBUILD` metadata that were defined in the image's `Dockerfile`. type: "array" items: type: "string" @@ -888,7 +1221,8 @@ definitions: additionalProperties: type: "string" StopSignal: - description: "Signal to stop a container as a string or unsigned integer." + description: | + Signal to stop a container as a string or unsigned integer. type: "string" default: "SIGTERM" StopTimeout: @@ -896,17 +1230,54 @@ definitions: type: "integer" default: 10 Shell: - description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + description: | + Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell. type: "array" items: type: "string" + NetworkingConfig: + description: | + NetworkingConfig represents the container's networking configuration for + each of its interfaces. + It is used for the networking configs specified in the `docker create` + and `docker network connect` commands. + type: "object" + properties: + EndpointsConfig: + description: | + A mapping of network name to endpoint configuration for that network. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + # putting an example here, instead of using the example values from + # /definitions/EndpointSettings, because containers/create currently + # does not support attaching to multiple networks, so the example request + # would be confusing if it showed that multiple networks can be contained + # in the EndpointsConfig. + # TODO remove once we support multiple networks on container create (see https://github.com/moby/moby/blob/07e6b843594e061f82baa5fa23c2ff7d536c2a05/daemon/create.go#L323) + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + NetworkSettings: description: "NetworkSettings exposes the network settings in the API" type: "object" properties: Bridge: - description: Name of the network'a bridge (for example, `docker0`). + description: Name of the network's bridge (for example, `docker0`). type: "string" example: "docker0" SandboxID: @@ -1084,6 +1455,7 @@ definitions: type: "object" additionalProperties: type: "array" + x-nullable: true items: $ref: "#/definitions/PortBinding" example: @@ -1108,7 +1480,6 @@ definitions: PortBinding represents a binding between a host IP address and a host port. type: "object" - x-nullable: true properties: HostIp: description: "Host IP address that the container's port is mapped to." @@ -1343,13 +1714,16 @@ definitions: type: "string" Scope: type: "string" - description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + description: | + The level at which the volume exists. Either `global` for cluster-wide, + or `local` for machine level. default: "local" x-nullable: false enum: ["local", "global"] Options: type: "object" - description: "The driver specific options used when creating the volume." + description: | + The driver specific options used when creating the volume. additionalProperties: type: "string" UsageData: @@ -1467,7 +1841,12 @@ definitions: type: "string" default: "default" Config: - description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + description: | + List of IPAM configuration options, specified as a map: + + ``` + {"Subnet": , "IPRange": , "Gateway": , "AuxAddress": } + ``` type: "array" items: type: "object" @@ -1475,11 +1854,9 @@ definitions: type: "string" Options: description: "Driver-specific options, specified as a map." - type: "array" - items: - type: "object" - additionalProperties: - type: "string" + type: "object" + additionalProperties: + type: "string" NetworkContainer: type: "object" @@ -1531,12 +1908,24 @@ definitions: Shared: type: "boolean" Size: + description: | + Amount of disk space used by the build cache (in bytes). type: "integer" CreatedAt: - type: "integer" + description: | + Date and time at which the build cache was created in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" LastUsedAt: - type: "integer" + description: | + Date and time at which the build cache was last used in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" x-nullable: true + example: "2017-08-09T07:09:37.632105588Z" UsageCount: type: "integer" @@ -1802,6 +2191,25 @@ definitions: type: "string" x-nullable: false + PluginPrivilege: + description: | + Describes a permission the user has to accept upon installing + the plugin. + type: "object" + x-go-name: "PluginPrivilege" + properties: + Name: + type: "string" + example: "network" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - "host" + Plugin: description: "A plugin for the Engine API" type: "object" @@ -1815,7 +2223,9 @@ definitions: x-nullable: false example: "tiborvass/sample-volume-plugin" Enabled: - description: "True if the plugin is running. False if the plugin is not running, only installed." + description: + True if the plugin is running. False if the plugin is not running, + only installed. type: "boolean" x-nullable: false example: true @@ -2017,13 +2427,16 @@ definitions: ObjectVersion: description: | - The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. - The client must send the version number along with the modified specification when updating these objects. - This approach ensures safe concurrency and determinism in that the change on the object - may not be applied if the version number has changed from the last read. In other words, - if two update requests specify the same base version, only one of the requests can succeed. - As a result, two separate update requests that happen at the same time will not - unintentionally overwrite each other. + The version number of the object such as node, service, etc. This is needed + to avoid conflicting writes. The client must send the version number along + with the modified specification when updating these objects. + + This approach ensures safe concurrency and determinism in that the change + on the object may not be applied if the version number has changed from the + last read. In other words, if two update requests specify the same base + version, only one of the requests can succeed. As a result, two separate + update requests that happen at the same time will not unintentionally + overwrite each other. type: "object" properties: Index: @@ -2192,17 +2605,23 @@ definitions: Name: "vieux/sshfs:latest" TLSInfo: - description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + description: | + Information about the issuer of leaf TLS certificates and the trusted root + CA certificate. type: "object" properties: TrustRoot: - description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + description: | + The root CA certificate(s) that are used to validate leaf TLS + certificates. type: "string" CertIssuerSubject: - description: "The base64-url-safe-encoded raw subject bytes of the issuer" + description: + The base64-url-safe-encoded raw subject bytes of the issuer. type: "string" CertIssuerPublicKey: - description: "The base64-url-safe-encoded raw public key bytes of the issuer" + description: | + The base64-url-safe-encoded raw public key bytes of the issuer. type: "string" example: TrustRoot: | @@ -2298,7 +2717,9 @@ definitions: x-nullable: true properties: TaskHistoryRetentionLimit: - description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + description: | + The number of historic tasks to keep per instance or node. If + negative, never remove completed or failed tasks. type: "integer" format: "int64" example: 10 @@ -2312,26 +2733,34 @@ definitions: format: "uint64" example: 10000 KeepOldSnapshots: - description: "The number of snapshots to keep beyond the current snapshot." + description: | + The number of snapshots to keep beyond the current snapshot. type: "integer" format: "uint64" LogEntriesForSlowFollowers: - description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + description: | + The number of log entries to keep around to sync up slow followers + after a snapshot is created. type: "integer" format: "uint64" example: 500 ElectionTick: description: | - The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + The number of ticks that a follower will wait for a message from + the leader before becoming a candidate and starting an election. + `ElectionTick` must be greater than `HeartbeatTick`. - A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + A tick currently defaults to one second, so these translate + directly to seconds currently, but this is NOT guaranteed. type: "integer" example: 3 HeartbeatTick: description: | - The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + The number of ticks between heartbeats. Every HeartbeatTick ticks, + the leader will send a heartbeat to the followers. - A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + A tick currently defaults to one second, so these translate + directly to seconds currently, but this is NOT guaranteed. type: "integer" example: 1 Dispatcher: @@ -2340,7 +2769,8 @@ definitions: x-nullable: true properties: HeartbeatPeriod: - description: "The delay for an agent to send a heartbeat to the dispatcher." + description: | + The delay for an agent to send a heartbeat to the dispatcher. type: "integer" format: "int64" example: 5000000000 @@ -2355,36 +2785,53 @@ definitions: format: "int64" example: 7776000000000000 ExternalCAs: - description: "Configuration for forwarding signing requests to an external certificate authority." + description: | + Configuration for forwarding signing requests to an external + certificate authority. type: "array" items: type: "object" properties: Protocol: - description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + description: | + Protocol for communication with the external CA (currently + only `cfssl` is supported). type: "string" enum: - "cfssl" default: "cfssl" URL: - description: "URL where certificate signing requests should be sent." + description: | + URL where certificate signing requests should be sent. type: "string" Options: - description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + description: | + An object with key/value pairs that are interpreted as + protocol-specific options for the external CA driver. type: "object" additionalProperties: type: "string" CACert: - description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)." + description: | + The root CA certificate (in PEM format) this external CA uses + to issue TLS certificates (assumed to be to the current swarm + root CA certificate if not provided). type: "string" SigningCACert: - description: "The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format." + description: | + The desired signing CA certificate for all swarm node TLS leaf + certificates, in PEM format. type: "string" SigningCAKey: - description: "The desired signing CA key for all swarm node TLS leaf certificates, in PEM format." + description: | + The desired signing CA key for all swarm node TLS leaf certificates, + in PEM format. type: "string" ForceRotate: - description: "An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`" + description: | + An integer whose purpose is to force swarm to generate a new + signing CA certificate and key, if none have been specified in + `SigningCACert` and `SigningCAKey` format: "uint64" type: "integer" EncryptionConfig: @@ -2392,7 +2839,9 @@ definitions: type: "object" properties: AutoLockManagers: - description: "If set, generate a key and use it to lock data stored on the managers." + description: | + If set, generate a key and use it to lock data stored on the + managers. type: "boolean" example: false TaskDefaults: @@ -2458,9 +2907,37 @@ definitions: TLSInfo: $ref: "#/definitions/TLSInfo" RootRotationInProgress: - description: "Whether there is currently a root CA rotation in progress for the swarm" + description: | + Whether there is currently a root CA rotation in progress for the swarm type: "boolean" example: false + DataPathPort: + description: | + DataPathPort specifies the data path port number for data traffic. + Acceptable port range is 1024 to 49151. + If no port is set or is set to 0, the default port (4789) is used. + type: "integer" + format: "uint32" + default: 4789 + example: 4789 + DefaultAddrPool: + description: | + Default Address Pool specifies default subnet pools for global scope + networks. + type: "array" + items: + type: "string" + format: "CIDR" + example: ["10.10.0.0/16", "20.20.0.0/16"] + SubnetSize: + description: | + SubnetSize specifies the subnet size of the networks created from the + default subnet pool. + type: "integer" + format: "uint32" + maximum: 29 + default: 24 + example: 24 JoinTokens: description: | @@ -2515,17 +2992,7 @@ definitions: PluginPrivilege: type: "array" items: - description: "Describes a permission accepted by the user upon installing the plugin." - type: "object" - properties: - Name: - type: "string" - Description: - type: "string" - Value: - type: "array" - items: - type: "string" + $ref: "#/definitions/PluginPrivilege" ContainerSpec: type: "object" description: | @@ -2557,10 +3024,13 @@ definitions: items: type: "string" Hostname: - description: "The hostname to use for the container, as a valid RFC 1123 hostname." + description: | + The hostname to use for the container, as a valid + [RFC 1123](https://tools.ietf.org/html/rfc1123) hostname. type: "string" Env: - description: "A list of environment variables in the form `VAR=value`." + description: | + A list of environment variables in the form `VAR=value`. type: "array" items: type: "string" @@ -2572,7 +3042,8 @@ definitions: type: "string" Groups: type: "array" - description: "A list of additional groups that the container process will run as." + description: | + A list of additional groups that the container process will run as. items: type: "string" Privileges: @@ -2583,30 +3054,48 @@ definitions: type: "object" description: "CredentialSpec for managed service account (Windows only)" properties: + Config: + type: "string" + example: "0bt9dmxjvjiqermk6xrop3ekq" + description: | + Load credential spec from a Swarm Config with the given ID. + The specified config must also be present in the Configs + field with the Runtime property set. + +


+ + + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. File: type: "string" + example: "spec.json" description: | - Load credential spec from this file. The file is read by the daemon, and must be present in the - `CredentialSpecs` subdirectory in the docker data directory, which defaults to - `C:\ProgramData\Docker\` on Windows. + Load credential spec from this file. The file is read by + the daemon, and must be present in the `CredentialSpecs` + subdirectory in the docker data directory, which defaults + to `C:\ProgramData\Docker\` on Windows. - For example, specifying `spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + For example, specifying `spec.json` loads + `C:\ProgramData\Docker\CredentialSpecs\spec.json`.


- > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. Registry: type: "string" description: | - Load credential spec from this value in the Windows registry. The specified registry value must be - located in: + Load credential spec from this value in the Windows + registry. The specified registry value must be located in: `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs`


- > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. SELinuxContext: type: "object" description: "SELinux labels of the container" @@ -2636,7 +3125,9 @@ definitions: description: "Mount the container's root filesystem as read only." type: "boolean" Mounts: - description: "Specification for mounts to be added to containers created as part of the service." + description: | + Specification for mounts to be added to containers created as part + of the service. type: "array" items: $ref: "#/definitions/Mount" @@ -2644,7 +3135,9 @@ definitions: description: "Signal to stop the container." type: "string" StopGracePeriod: - description: "Amount of time to wait for the container to terminate before forcefully killing it." + description: | + Amount of time to wait for the container to terminate before + forcefully killing it. type: "integer" format: "int64" HealthCheck: @@ -2661,7 +3154,9 @@ definitions: items: type: "string" DNSConfig: - description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + description: | + Specification for DNS related configurations in resolver configuration + file (`resolv.conf`). type: "object" properties: Nameservers: @@ -2675,22 +3170,28 @@ definitions: items: type: "string" Options: - description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + description: | + A list of internal resolver variables to be modified (e.g., + `debug`, `ndots:3`, etc.). type: "array" items: type: "string" Secrets: - description: "Secrets contains references to zero or more secrets that will be exposed to the service." + description: | + Secrets contains references to zero or more secrets that will be + exposed to the service. type: "array" items: type: "object" properties: File: - description: "File represents a specific target that is backed by a file." + description: | + File represents a specific target that is backed by a file. type: "object" properties: Name: - description: "Name represents the final filename in the filesystem." + description: | + Name represents the final filename in the filesystem. type: "string" UID: description: "UID represents the file UID." @@ -2703,25 +3204,36 @@ definitions: type: "integer" format: "uint32" SecretID: - description: "SecretID represents the ID of the specific secret that we're referencing." + description: | + SecretID represents the ID of the specific secret that we're + referencing. type: "string" SecretName: description: | - SecretName is the name of the secret that this references, but this is just provided for - lookup/display purposes. The secret in the reference will be identified by its ID. + SecretName is the name of the secret that this references, + but this is just provided for lookup/display purposes. The + secret in the reference will be identified by its ID. type: "string" Configs: - description: "Configs contains references to zero or more configs that will be exposed to the service." + description: | + Configs contains references to zero or more configs that will be + exposed to the service. type: "array" items: type: "object" properties: File: - description: "File represents a specific target that is backed by a file." + description: | + File represents a specific target that is backed by a file. + +


+ + > **Note**: `Configs.File` and `Configs.Runtime` are mutually exclusive type: "object" properties: Name: - description: "Name represents the final filename in the filesystem." + description: | + Name represents the final filename in the filesystem. type: "string" UID: description: "UID represents the file UID." @@ -2733,23 +3245,41 @@ definitions: description: "Mode represents the FileMode of the file." type: "integer" format: "uint32" + Runtime: + description: | + Runtime represents a target that is not mounted into the + container but is used by the task + +


+ + > **Note**: `Configs.File` and `Configs.Runtime` are mutually + > exclusive + type: "object" ConfigID: - description: "ConfigID represents the ID of the specific config that we're referencing." + description: | + ConfigID represents the ID of the specific config that we're + referencing. type: "string" ConfigName: description: | - ConfigName is the name of the config that this references, but this is just provided for - lookup/display purposes. The config in the reference will be identified by its ID. + ConfigName is the name of the config that this references, + but this is just provided for lookup/display purposes. The + config in the reference will be identified by its ID. type: "string" Isolation: type: "string" - description: "Isolation technology of the containers running the service. (Windows only)" + description: | + Isolation technology of the containers running the service. + (Windows only) enum: - "default" - "process" - "hyperv" Init: - description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used." + description: | + Run an init inside the container that forwards signals and reaps + processes. This field is omitted if empty, and the default (as + configured on the daemon) is used. type: "boolean" x-nullable: true Sysctls: @@ -2764,6 +3294,44 @@ definitions: type: "object" additionalProperties: type: "string" + # This option is not used by Windows containers + CapabilityAdd: + type: "array" + description: | + A list of kernel capabilities to add to the default set + for the container. + items: + type: "string" + example: + - "CAP_NET_RAW" + - "CAP_SYS_ADMIN" + - "CAP_SYS_CHROOT" + - "CAP_SYSLOG" + CapabilityDrop: + type: "array" + description: | + A list of kernel capabilities to drop from the default set + for the container. + items: + type: "string" + example: + - "CAP_NET_RAW" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" NetworkAttachmentSpec: description: | Read-only spec type for non-swarm containers attached to swarm overlay @@ -2781,17 +3349,21 @@ definitions: description: "ID of the container represented by this task" type: "string" Resources: - description: "Resource requirements which apply to each individual container created as part of the service." + description: | + Resource requirements which apply to each individual container created + as part of the service. type: "object" properties: Limits: description: "Define resources limits." - $ref: "#/definitions/ResourceObject" + $ref: "#/definitions/Limit" Reservation: description: "Define resources reservation." $ref: "#/definitions/ResourceObject" RestartPolicy: - description: "Specification for the restart policy which applies to containers created as part of this service." + description: | + Specification for the restart policy which applies to containers + created as part of this service. type: "object" properties: Condition: @@ -2806,12 +3378,16 @@ definitions: type: "integer" format: "int64" MaxAttempts: - description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + description: | + Maximum attempts to restart a given container before giving up + (default value is 0, which is ignored). type: "integer" format: "int64" default: 0 Window: - description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + description: | + Windows is the time window used to evaluate the restart policy + (default value is 0, which is unbounded). type: "integer" format: "int64" default: 0 @@ -2819,7 +3395,27 @@ definitions: type: "object" properties: Constraints: - description: "An array of constraints." + description: | + An array of constraint expressions to limit the set of nodes where + a task can be scheduled. Constraint expressions can either use a + _match_ (`==`) or _exclude_ (`!=`) rule. Multiple constraints find + nodes that satisfy every expression (AND match). Constraints can + match node or Docker Engine labels as follows: + + node attribute | matches | example + ---------------------|--------------------------------|----------------------------------------------- + `node.id` | Node ID | `node.id==2ivku8v2gvtg4` + `node.hostname` | Node hostname | `node.hostname!=node-2` + `node.role` | Node role (`manager`/`worker`) | `node.role==manager` + `node.platform.os` | Node operating system | `node.platform.os==windows` + `node.platform.arch` | Node architecture | `node.platform.arch==x86_64` + `node.labels` | User-defined node labels | `node.labels.security==high` + `engine.labels` | Docker Engine's labels | `engine.labels.operatingsystem==ubuntu-14.04` + + `engine.labels` apply to Docker Engine labels like operating system, + drivers, etc. Swarm administrators add `node.labels` for operational + purposes by using the [`node update endpoint`](#operation/NodeUpdate). + type: "array" items: type: "string" @@ -2827,8 +3423,13 @@ definitions: - "node.hostname!=node3.corp.example.com" - "node.role!=manager" - "node.labels.type==production" + - "node.platform.os==linux" + - "node.platform.arch==x86_64" Preferences: - description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + description: | + Preferences provide a way to make the scheduler aware of factors + such as topology. They are provided in order from highest to + lowest precedence. type: "array" items: type: "object" @@ -2837,13 +3438,21 @@ definitions: type: "object" properties: SpreadDescriptor: - description: "label descriptor, such as engine.labels.az" + description: | + label descriptor, such as `engine.labels.az`. type: "string" example: - Spread: SpreadDescriptor: "node.labels.datacenter" - Spread: SpreadDescriptor: "node.labels.rack" + MaxReplicas: + description: | + Maximum number of replicas for per node (default value is 0, which + is unlimited) + type: "integer" + format: "int64" + default: 0 Platforms: description: | Platforms stores all the platforms that the service's image can @@ -2854,24 +3463,24 @@ definitions: items: $ref: "#/definitions/Platform" ForceUpdate: - description: "A counter that triggers an update even if no relevant parameters have been changed." + description: | + A counter that triggers an update even if no relevant parameters have + been changed. type: "integer" Runtime: - description: "Runtime is the type of runtime specified for the task executor." + description: | + Runtime is the type of runtime specified for the task executor. type: "string" Networks: + description: "Specifies which networks the service should attach to." type: "array" items: - type: "object" - properties: - Target: - type: "string" - Aliases: - type: "array" - items: - type: "string" + $ref: "#/definitions/NetworkAttachmentConfig" LogDriver: - description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + description: | + Specifies the log driver to use for tasks created from this spec. If + not present, the default one for the swarm will be used, finally + falling back to the engine default if not specified. type: "object" properties: Name: @@ -2957,6 +3566,12 @@ definitions: type: "integer" DesiredState: $ref: "#/definitions/TaskState" + JobIteration: + description: | + If the Service this Task belongs to is a job-mode service, contains + the JobIteration of the Service this Task was created for. Absent if + the Task was created for a Replicated or Global Service. + $ref: "#/definitions/ObjectVersion" example: ID: "0kzzo1i0y4jz6027t0k7aezc7" Version: @@ -3049,12 +3664,37 @@ definitions: format: "int64" Global: type: "object" + ReplicatedJob: + description: | + The mode used for services with a finite number of tasks that run + to a completed state. + type: "object" + properties: + MaxConcurrent: + description: | + The maximum number of replicas to run simultaneously. + type: "integer" + format: "int64" + default: 1 + TotalCompletions: + description: | + The total number of replicas desired to reach the Completed + state. If unset, will default to the value of `MaxConcurrent` + type: "integer" + format: "int64" + GlobalJob: + description: | + The mode used for services which run a task to the completed state + on each valid node. + type: "object" UpdateConfig: description: "Specification for the update strategy of the service." type: "object" properties: Parallelism: - description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + description: | + Maximum number of tasks to be updated in one iteration (0 means + unlimited parallelism). type: "integer" format: "int64" Delay: @@ -3062,22 +3702,32 @@ definitions: type: "integer" format: "int64" FailureAction: - description: "Action to take if an updated task fails to run, or stops running during the update." + description: | + Action to take if an updated task fails to run, or stops running + during the update. type: "string" enum: - "continue" - "pause" - "rollback" Monitor: - description: "Amount of time to monitor each updated task for failures, in nanoseconds." + description: | + Amount of time to monitor each updated task for failures, in + nanoseconds. type: "integer" format: "int64" MaxFailureRatio: - description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + description: | + The fraction of tasks that may fail during an update before the + failure action is invoked, specified as a floating point number + between 0 and 1. type: "number" default: 0 Order: - description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + description: | + The order of operations when rolling out an updated task. Either + the old task is shut down before the new task is started, or the + new task is started before the old task is shut down. type: "string" enum: - "stop-first" @@ -3087,45 +3737,52 @@ definitions: type: "object" properties: Parallelism: - description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + description: | + Maximum number of tasks to be rolled back in one iteration (0 means + unlimited parallelism). type: "integer" format: "int64" Delay: - description: "Amount of time between rollback iterations, in nanoseconds." + description: | + Amount of time between rollback iterations, in nanoseconds. type: "integer" format: "int64" FailureAction: - description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + description: | + Action to take if an rolled back task fails to run, or stops + running during the rollback. type: "string" enum: - "continue" - "pause" Monitor: - description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + description: | + Amount of time to monitor each rolled back task for failures, in + nanoseconds. type: "integer" format: "int64" MaxFailureRatio: - description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + description: | + The fraction of tasks that may fail during a rollback before the + failure action is invoked, specified as a floating point number + between 0 and 1. type: "number" default: 0 Order: - description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + description: | + The order of operations when rolling back a task. Either the old + task is shut down before the new task is started, or the new task + is started before the old task is shut down. type: "string" enum: - "stop-first" - "start-first" Networks: - description: "Array of network names or IDs to attach the service to." + description: "Specifies which networks the service should attach to." type: "array" items: - type: "object" - properties: - Target: - type: "string" - Aliases: - type: "array" - items: - type: "string" + $ref: "#/definitions/NetworkAttachmentConfig" + EndpointSpec: $ref: "#/definitions/EndpointSpec" @@ -3152,7 +3809,7 @@ definitions:


- - "ingress" makes the target port accessible on on every node, + - "ingress" makes the target port accessible on every node, regardless of whether there is a task for the service running on that node or not. - "host" bypasses the routing mesh and publish the port directly on @@ -3170,15 +3827,17 @@ definitions: type: "object" properties: Mode: - description: "The mode of resolution to use for internal load balancing - between tasks." + description: | + The mode of resolution to use for internal load balancing between tasks. type: "string" enum: - "vip" - "dnsrr" default: "vip" Ports: - description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + description: | + List of exposed ports that this service is accessible on from the + outside. Ports can only be provided if `vip` resolution mode is used. type: "array" items: $ref: "#/definitions/EndpointPortConfig" @@ -3234,6 +3893,61 @@ definitions: format: "dateTime" Message: type: "string" + ServiceStatus: + description: | + The status of the service's tasks. Provided only when requested as + part of a ServiceList operation. + type: "object" + properties: + RunningTasks: + description: | + The number of tasks for the service currently in the Running state. + type: "integer" + format: "uint64" + example: 7 + DesiredTasks: + description: | + The number of tasks for the service desired to be running. + For replicated services, this is the replica count from the + service spec. For global services, this is computed by taking + count of all tasks for the service with a Desired State other + than Shutdown. + type: "integer" + format: "uint64" + example: 10 + CompletedTasks: + description: | + The number of tasks for a job that are in the Completed state. + This field must be cross-referenced with the service type, as the + value of 0 may mean the service is not in a job mode, or it may + mean the job-mode service has no tasks yet Completed. + type: "integer" + format: "uint64" + JobStatus: + description: | + The status of the service when it is in one of ReplicatedJob or + GlobalJob modes. Absent on Replicated and Global mode services. The + JobIteration is an ObjectVersion, but unlike the Service's version, + does not need to be sent with an update request. + type: "object" + properties: + JobIteration: + description: | + JobIteration is a value increased each time a Job is executed, + successfully or otherwise. "Executed", in this case, means the + job as a whole has been started, not that an individual Task has + been launched. A job is "Executed" when its ServiceSpec is + updated. JobIteration can be used to disambiguate Tasks belonging + to different executions of a job. Though JobIteration will + increase with each subsequent execution, it may not necessarily + increase by 1, and so JobIteration should not be used to + $ref: "#/definitions/ObjectVersion" + LastExecution: + description: | + The last time, as observed by the server, that this job was + started. + type: "string" + format: "dateTime" example: ID: "9mnpnzenvg8p8tdbtq4wvbkcz" Version: @@ -3318,73 +4032,71 @@ definitions: Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" ContainerSummary: - type: "array" - items: - type: "object" - properties: - Id: - description: "The ID of this container" - type: "string" - x-go-name: "ID" - Names: - description: "The names that this container has been given" - type: "array" - items: - type: "string" - Image: - description: "The name of the image used when creating this container" - type: "string" - ImageID: - description: "The ID of the image that this container was created from" + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: type: "string" - Command: - description: "Command to run when starting the container" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: type: "string" - Created: - description: "When the container was created" - type: "integer" - format: "int64" - Ports: - description: "The ports exposed by this container" - type: "array" - items: - $ref: "#/definitions/Port" - SizeRw: - description: "The size of files that have been created or changed by this container" - type: "integer" - format: "int64" - SizeRootFs: - description: "The total size of all the files in this container" - type: "integer" - format: "int64" - Labels: - description: "User-defined key/value metadata." - type: "object" - additionalProperties: + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: type: "string" - State: - description: "The state of this container (e.g. `Exited`)" - type: "string" - Status: - description: "Additional human-readable status of this container (e.g. `Exit 0`)" - type: "string" - HostConfig: - type: "object" - properties: - NetworkMode: - type: "string" - NetworkSettings: - description: "A summary of the container's network settings" - type: "object" - properties: - Networks: - type: "object" - additionalProperties: - $ref: "#/definitions/EndpointSettings" - Mounts: - type: "array" - items: - $ref: "#/definitions/Mount" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" Driver: description: "Driver represents a driver (network, logging, secrets)." @@ -3422,7 +4134,7 @@ definitions: com.example.some-other-label: "some-other-value" Data: description: | - Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) data to store as secret. This field is only used to _create_ a secret, and is not returned by @@ -3430,7 +4142,9 @@ definitions: type: "string" example: "" Driver: - description: "Name of the secrets driver used to fetch the secret's value from an external secret store" + description: | + Name of the secrets driver used to fetch the secret's value from an + external secret store. $ref: "#/definitions/Driver" Templating: description: | @@ -3472,7 +4186,7 @@ definitions: type: "string" Data: description: | - Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) config data. type: "string" Templating: @@ -3499,6 +4213,167 @@ definitions: Spec: $ref: "#/definitions/ConfigSpec" + ContainerState: + description: | + ContainerState stores container's running state. It's part of ContainerJSONBase + and will be returned by the "inspect" command. + type: "object" + x-nullable: true + properties: + Status: + description: | + String representation of the container state. Can be one of "created", + "running", "paused", "restarting", "removing", "exited", or "dead". + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + example: "running" + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the freezer cgroup is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + example: true + Paused: + description: "Whether this container is paused." + type: "boolean" + example: false + Restarting: + description: "Whether this container is restarting." + type: "boolean" + example: false + OOMKilled: + description: | + Whether this container has been killed because it ran out of memory. + type: "boolean" + example: false + Dead: + type: "boolean" + example: false + Pid: + description: "The process ID of this container" + type: "integer" + example: 1234 + ExitCode: + description: "The last exit code of this container" + type: "integer" + example: 0 + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + example: "2020-01-06T09:06:59.461876391Z" + FinishedAt: + description: "The time when this container last exited." + type: "string" + example: "2020-01-06T09:07:59.461876391Z" + Health: + $ref: "#/definitions/Health" + + SystemVersion: + type: "object" + description: | + Response of Engine API: GET "/version" + properties: + Platform: + type: "object" + required: [Name] + properties: + Name: + type: "string" + Components: + type: "array" + description: | + Information about system components + items: + type: "object" + x-go-name: ComponentVersion + required: [Name, Version] + properties: + Name: + description: | + Name of the component + type: "string" + example: "Engine" + Version: + description: | + Version of the component + type: "string" + x-nullable: false + example: "19.03.12" + Details: + description: | + Key/value pairs of strings with additional information about the + component. These values are intended for informational purposes + only, and their content is not defined, and not part of the API + specification. + + These messages can be printed by the client as information to the user. + type: "object" + x-nullable: true + Version: + description: "The version of the daemon" + type: "string" + example: "19.03.12" + ApiVersion: + description: | + The default (and highest) API version that is supported by the daemon + type: "string" + example: "1.40" + MinAPIVersion: + description: | + The minimum API version that is supported by the daemon + type: "string" + example: "1.12" + GitCommit: + description: | + The Git commit of the source code that was used to build the daemon + type: "string" + example: "48a66213fe" + GoVersion: + description: | + The version Go used to compile the daemon, and the version of the Go + runtime in use. + type: "string" + example: "go1.13.14" + Os: + description: | + The operating system that the daemon is running on ("linux" or "windows") + type: "string" + example: "linux" + Arch: + description: | + The architecture that the daemon is running on + type: "string" + example: "amd64" + KernelVersion: + description: | + The kernel version (`uname -r`) that the daemon is running on. + + This field is omitted when empty. + type: "string" + example: "4.19.76-linuxkit" + Experimental: + description: | + Indicates if the daemon is started with experimental features enabled. + + This field is omitted when empty / false. + type: "boolean" + example: true + BuildTime: + description: | + The date and time that the daemon was compiled. + type: "string" + example: "2020-06-22T15:49:27.000000000+00:00" + SystemInfo: type: "object" properties: @@ -3573,44 +4448,6 @@ definitions: on Windows. type: "string" example: "/var/lib/docker" - SystemStatus: - description: | - Status information about this node (standalone Swarm API). - -


- - > **Note**: The information returned in this field is only propagated - > by the Swarm standalone API, and is empty (`null`) when using - > built-in swarm mode. - type: "array" - items: - type: "array" - items: - type: "string" - example: - - ["Role", "primary"] - - ["State", "Healthy"] - - ["Strategy", "spread"] - - ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"] - - ["Nodes", "2"] - - [" swarm-agent-00", "192.168.99.102:2376"] - - [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"] - - [" └ Status", "Healthy"] - - [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"] - - [" └ Reserved CPUs", "0 / 1"] - - [" └ Reserved Memory", "0 B / 1.021 GiB"] - - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] - - [" └ UpdatedAt", "2017-08-09T10:03:46Z"] - - [" └ ServerVersion", "17.06.0-ce"] - - [" swarm-manager", "192.168.99.101:2376"] - - [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"] - - [" └ Status", "Healthy"] - - [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"] - - [" └ Reserved CPUs", "0 / 1"] - - [" └ Reserved Memory", "0 B / 1.021 GiB"] - - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] - - [" └ UpdatedAt", "2017-08-09T10:04:11Z"] - - [" └ ServerVersion", "17.06.0-ce"] Plugins: $ref: "#/definitions/PluginsInfo" MemoryLimit: @@ -3622,19 +4459,30 @@ definitions: type: "boolean" example: true KernelMemory: - description: "Indicates if the host has kernel memory limit support enabled." + description: | + Indicates if the host has kernel memory limit support enabled. + +


+ + > **Deprecated**: This field is deprecated as the kernel 5.4 deprecated + > `kmem.limit_in_bytes`. type: "boolean" example: true CpuCfsPeriod: - description: "Indicates if CPU CFS(Completely Fair Scheduler) period is supported by the host." + description: | + Indicates if CPU CFS(Completely Fair Scheduler) period is supported by + the host. type: "boolean" example: true CpuCfsQuota: - description: "Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by the host." + description: | + Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by + the host. type: "boolean" example: true CPUShares: - description: "Indicates if CPU Shares limiting is supported by the host." + description: | + Indicates if CPU Shares limiting is supported by the host. type: "boolean" example: true CPUSet: @@ -3644,6 +4492,10 @@ definitions: See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) type: "boolean" example: true + PidsLimit: + description: "Indicates if the host kernel has PID limit support enabled." + type: "boolean" + example: true OomKillDisable: description: "Indicates if OOM killer disable is supported on the host." type: "boolean" @@ -3660,7 +4512,9 @@ definitions: type: "boolean" example: true Debug: - description: "Indicates if the daemon is running in debug-mode / with debug-level logging enabled." + description: | + Indicates if the daemon is running in debug-mode / with debug-level + logging enabled. type: "boolean" example: true NFd: @@ -3691,9 +4545,16 @@ definitions: description: | The driver to use for managing cgroups. type: "string" - enum: ["cgroupfs", "systemd"] + enum: ["cgroupfs", "systemd", "none"] default: "cgroupfs" example: "cgroupfs" + CgroupVersion: + description: | + The version of the cgroup. + type: "string" + enum: ["1", "2"] + default: "1" + example: "1" NEventsListener: description: "Number of event listeners subscribed." type: "integer" @@ -3713,6 +4574,17 @@ definitions: or "Windows Server 2016 Datacenter" type: "string" example: "Alpine Linux v3.5" + OSVersion: + description: | + Version of the host's operating system + +


+ + > **Note**: The information returned in this field, including its + > very existence, and the formatting of values, should not be considered + > stable, and may change without notice. + type: "string" + example: "16.04" OSType: description: | Generic type of the operating system of the host, as returned by the @@ -3741,7 +4613,7 @@ definitions: example: 4 MemTotal: description: | - Total amount of physical memory available on the host, in kilobytes (kB). + Total amount of physical memory available on the host, in bytes. type: "integer" format: "int64" example: 2095882240 @@ -3829,7 +4701,7 @@ definitions:


- > **Note**: This field is only propagated when using standalone Swarm + > **Deprecated**: This field is only propagated when using standalone Swarm > mode, and overlay networking using an external k/v store. Overlay > networks with Swarm mode enabled use the built-in raft store, and > this field will be empty. @@ -3843,7 +4715,7 @@ definitions:


- > **Note**: This field is only propagated when using standalone Swarm + > **Deprecated**: This field is only propagated when using standalone Swarm > mode, and overlay networking using an external k/v store. Overlay > networks with Swarm mode enabled use the built-in raft store, and > this field will be empty. @@ -3926,7 +4798,7 @@ definitions: SecurityOptions: description: | List of security features that are enabled on the daemon, such as - apparmor, seccomp, SELinux, and user-namespaces (userns). + apparmor, seccomp, SELinux, user-namespaces (userns), and rootless. Additional configuration options for each security feature may be present, and are included as a comma-separated list of key/value @@ -3939,6 +4811,7 @@ definitions: - "name=seccomp,profile=default" - "name=selinux" - "name=userns" + - "name=rootless" ProductLicense: description: | Reports a summary of the product license on the daemon. @@ -3947,6 +4820,25 @@ definitions: such as number of nodes, and expiration are included. type: "string" example: "Community Engine" + DefaultAddressPools: + description: | + List of custom default address pools for local networks, which can be + specified in the daemon.json file or dockerd option. + + Example: a Base "10.10.0.0/16" with Size 24 will define the set of 256 + 10.10.[0-255].0/24 address pools. + type: "array" + items: + type: "object" + properties: + Base: + description: "The network address in CIDR format" + type: "string" + example: "10.10.0.0/16" + Size: + description: "The network pool size" + type: "integer" + example: "24" Warnings: description: | List of warnings / informational messages about missing features, or @@ -4292,37 +5184,221 @@ definitions: IP address and ports at which this node can be reached. type: "string" + NetworkAttachmentConfig: + description: | + Specifies how a service should be attached to a particular network. + type: "object" + properties: + Target: + description: | + The target network for attachment. Must be a network name or ID. + type: "string" + Aliases: + description: | + Discoverable alternate names for the service on this network. + type: "array" + items: + type: "string" + DriverOpts: + description: | + Driver attachment options for the network target. + type: "object" + additionalProperties: + type: "string" + + EventActor: + description: | + Actor describes something that generates events, like a container, network, + or a volume. + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + example: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + description: | + Various key/value attributes of the object, depending on its type. + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-label-value" + image: "alpine:latest" + name: "my-container" + + EventMessage: + description: | + EventMessage represents the information an event contains. + type: "object" + title: "SystemEventsResponse" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + enum: ["builder", "config", "container", "daemon", "image", "network", "node", "plugin", "secret", "service", "volume"] + example: "container" + Action: + description: "The type of event" + type: "string" + example: "create" + Actor: + $ref: "#/definitions/EventActor" + scope: + description: | + Scope of the event. Engine events are `local` scope. Cluster (Swarm) + events are `swarm` scope. + type: "string" + enum: ["local", "swarm"] + time: + description: "Timestamp of event" + type: "integer" + format: "int64" + example: 1629574695 + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + example: 1629574695515050031 + + OCIDescriptor: + type: "object" + x-go-name: Descriptor + description: | + A descriptor struct containing digest, media type, and size, as defined in + the [OCI Content Descriptors Specification](https://github.com/opencontainers/image-spec/blob/v1.0.1/descriptor.md). + properties: + mediaType: + description: | + The media type of the object this schema refers to. + type: "string" + example: "application/vnd.docker.distribution.manifest.v2+json" + digest: + description: | + The digest of the targeted content. + type: "string" + example: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + size: + description: | + The size in bytes of the blob. + type: "integer" + format: "int64" + example: 3987495 + # TODO Not yet including these fields for now, as they are nil / omitted in our response. + # urls: + # description: | + # List of URLs from which this object MAY be downloaded. + # type: "array" + # items: + # type: "string" + # format: "uri" + # annotations: + # description: | + # Arbitrary metadata relating to the targeted content. + # type: "object" + # additionalProperties: + # type: "string" + # platform: + # $ref: "#/definitions/OCIPlatform" + + OCIPlatform: + type: "object" + x-go-name: Platform + description: | + Describes the platform which the image in the manifest runs on, as defined + in the [OCI Image Index Specification](https://github.com/opencontainers/image-spec/blob/v1.0.1/image-index.md). + properties: + architecture: + description: | + The CPU architecture, for example `amd64` or `ppc64`. + type: "string" + example: "arm" + os: + description: | + The operating system, for example `linux` or `windows`. + type: "string" + example: "windows" + os.version: + description: | + Optional field specifying the operating system version, for example on + Windows `10.0.19041.1165`. + type: "string" + example: "10.0.19041.1165" + os.features: + description: | + Optional field specifying an array of strings, each listing a required + OS feature (for example on Windows `win32k`). + type: "array" + items: + type: "string" + example: + - "win32k" + variant: + description: | + Optional field specifying a variant of the CPU, for example `v7` to + specify ARMv7 when architecture is `arm`. + type: "string" + example: "v7" + + DistributionInspect: + type: "object" + x-go-name: DistributionInspect + title: "DistributionInspectResponse" + required: [Descriptor, Platforms] + description: | + Describes the result obtained from contacting the registry to retrieve + image metadata. + properties: + Descriptor: + $ref: "#/definitions/OCIDescriptor" + Platforms: + type: "array" + description: | + An array containing all platforms supported by the image. + items: + $ref: "#/definitions/OCIPlatform" + paths: /containers/json: get: summary: "List containers" description: | - Returns a list of containers. For details on the format, see [the inspect endpoint](#operation/ContainerInspect). + Returns a list of containers. For details on the format, see the + [inspect endpoint](#operation/ContainerInspect). - Note that it uses a different, smaller representation of a container than inspecting a single container. For example, - the list of linked containers is not propagated . + Note that it uses a different, smaller representation of a container + than inspecting a single container. For example, the list of linked + containers is not propagated . operationId: "ContainerList" produces: - "application/json" parameters: - name: "all" in: "query" - description: "Return all containers. By default, only running containers are shown" + description: | + Return all containers. By default, only running containers are shown. type: "boolean" default: false - name: "limit" in: "query" - description: "Return this number of most recently created containers, including non-running ones." + description: | + Return this number of most recently created containers, including + non-running ones. type: "integer" - name: "size" in: "query" - description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + description: | + Return the size of container as fields `SizeRw` and `SizeRootFs`. type: "boolean" default: false - name: "filters" in: "query" description: | - Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + Filters to process on the container list, encoded as JSON (a + `map[string][]string`). For example, `{"status": ["paused"]}` will + only return paused containers. + + Available filters: - `ancestor`=(`[:]`, ``, or ``) - `before`=(`` or ``) @@ -4344,7 +5420,9 @@ paths: 200: description: "no error" schema: - $ref: "#/definitions/ContainerSummary" + type: "array" + items: + $ref: "#/definitions/ContainerSummary" examples: application/json: - Id: "8dfafdbc3a40" @@ -4493,9 +5571,11 @@ paths: parameters: - name: "name" in: "query" - description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + description: | + Assign the specified name to the container. Must match + `/?[a-zA-Z0-9][a-zA-Z0-9_.-]+`. type: "string" - pattern: "/?[a-zA-Z0-9_-]+" + pattern: "^/?[a-zA-Z0-9][a-zA-Z0-9_.-]+$" - name: "body" in: "body" description: "Container to create" @@ -4507,14 +5587,7 @@ paths: HostConfig: $ref: "#/definitions/HostConfig" NetworkingConfig: - description: "This container's networking configuration." - type: "object" - properties: - EndpointsConfig: - description: "A mapping of network name to endpoint configuration for that network." - type: "object" - additionalProperties: - $ref: "#/definitions/EndpointSettings" + $ref: "#/definitions/NetworkingConfig" example: Hostname: "" Domainname: "" @@ -4554,7 +5627,7 @@ paths: MemorySwap: 0 MemoryReservation: 0 KernelMemory: 0 - NanoCPUs: 500000 + NanoCpus: 500000 CpuPercent: 80 CpuShares: 512 CpuPeriod: 100000 @@ -4576,11 +5649,19 @@ paths: - {} BlkioDeviceWriteIOps: - {} + DeviceRequests: + - Driver: "nvidia" + Count: -1 + DeviceIDs": ["0", "1", "GPU-fef8089b-4820-abfc-e83e-94318197576e"] + Capabilities: [["gpu", "nvidia", "compute"]] + Options: + property1: "string" + property2: "string" MemorySwappiness: 60 OomKillDisable: false OomScoreAdj: 500 PidMode: "" - PidsLimit: -1 + PidsLimit: 0 PortBindings: 22/tcp: - HostPort: "11022" @@ -4663,12 +5744,12 @@ paths: schema: $ref: "#/definitions/ErrorResponse" 404: - description: "no such container" + description: "no such image" schema: $ref: "#/definitions/ErrorResponse" examples: application/json: - message: "No such container: c2ada9df5af8" + message: "No such image: c2ada9df5af8" 409: description: "conflict" schema: @@ -4707,54 +5788,9 @@ paths: items: type: "string" State: - description: "The state of the container." - type: "object" - properties: - Status: - description: | - The status of the container. For example, `"running"` or `"exited"`. - type: "string" - enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] - Running: - description: | - Whether this container is running. - - Note that a running container can be _paused_. The `Running` and `Paused` - booleans are not mutually exclusive: - - When pausing a container (on Linux), the cgroups freezer is used to suspend - all processes in the container. Freezing the process requires the process to - be running. As a result, paused containers are both `Running` _and_ `Paused`. - - Use the `Status` field instead to determine if a container's state is "running". - type: "boolean" - Paused: - description: "Whether this container is paused." - type: "boolean" - Restarting: - description: "Whether this container is restarting." - type: "boolean" - OOMKilled: - description: "Whether this container has been killed because it ran out of memory." - type: "boolean" - Dead: - type: "boolean" - Pid: - description: "The process ID of this container" - type: "integer" - ExitCode: - description: "The last exit code of this container" - type: "integer" - Error: - type: "string" - StartedAt: - description: "The time when this container was last started." - type: "string" - FinishedAt: - description: "The time when this container last exited." - type: "string" + $ref: "#/definitions/ContainerState" Image: - description: "The container's image" + description: "The container's image ID" type: "string" ResolvConfPath: type: "string" @@ -4764,15 +5800,14 @@ paths: type: "string" LogPath: type: "string" - Node: - description: "TODO" - type: "object" Name: type: "string" RestartCount: type: "integer" Driver: type: "string" + Platform: + type: "string" MountLabel: type: "string" ProcessLabel: @@ -4790,7 +5825,9 @@ paths: GraphDriver: $ref: "#/definitions/GraphDriverData" SizeRw: - description: "The size of files that have been created or changed by this container." + description: | + The size of files that have been created or changed by this + container. type: "integer" format: "int64" SizeRootFs: @@ -4822,6 +5859,8 @@ paths: Domainname: "" Env: - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Healthcheck: + Test: ["CMD-SHELL", "exit 0"] Hostname: "ba033ac44011" Image: "ubuntu" Labels: @@ -4867,6 +5906,14 @@ paths: CpuRealtimePeriod: 1000000 CpuRealtimeRuntime: 10000 Devices: [] + DeviceRequests: + - Driver: "nvidia" + Count: -1 + DeviceIDs": ["0", "1", "GPU-fef8089b-4820-abfc-e83e-94318197576e"] + Capabilities: [["gpu", "nvidia", "compute"]] + Options: + property1: "string" + property2: "string" IpcMode: "" LxcConf: [] Memory: 0 @@ -4933,6 +5980,14 @@ paths: Error: "" ExitCode: 9 FinishedAt: "2015-01-06T15:47:32.080254511Z" + Health: + Status: "healthy" + FailingStreak: 0 + Log: + - Start: "2019-12-22T10:59:05.6385933Z" + End: "2019-12-22T10:59:05.8078452Z" + ExitCode: 0 + Output: "" OOMKilled: false Dead: false Paused: false @@ -4975,7 +6030,9 @@ paths: /containers/{id}/top: get: summary: "List processes running inside a container" - description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + description: | + On Unix systems, this is done by running the `ps` command. This endpoint + is not supported on Windows. operationId: "ContainerTop" responses: 200: @@ -4991,7 +6048,9 @@ paths: items: type: "string" Processes: - description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + description: | + Each process running in the container, where each is process + is an array of values corresponding to the titles. type: "array" items: type: "array" @@ -5056,18 +6115,19 @@ paths: description: | Get `stdout` and `stderr` logs from a container. - Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + Note: This endpoint works only for containers with the `json-file` or + `journald` logging driver. operationId: "ContainerLogs" responses: - 101: - description: "logs returned as a stream" - schema: - type: "string" - format: "binary" 200: - description: "logs returned as a string in response body" + description: | + logs returned as a stream in response body. + For the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + Note that unlike the attach endpoint, the logs endpoint does not + upgrade the connection and does not set Content-Type. schema: type: "string" + format: "binary" 404: description: "no such container" schema: @@ -5087,10 +6147,7 @@ paths: type: "string" - name: "follow" in: "query" - description: | - Return the logs as a stream. - - This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + description: "Keep connection after returning logs." type: "boolean" default: false - name: "stdout" @@ -5120,7 +6177,9 @@ paths: default: false - name: "tail" in: "query" - description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. type: "string" default: "all" tags: ["Container"] @@ -5226,6 +6285,22 @@ paths: If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is nil then for compatibility with older daemons the length of the corresponding `cpu_usage.percpu_usage` array should be used. + + On a cgroup v2 host, the following fields are not set + * `blkio_stats`: all fields other than `io_service_bytes_recursive` + * `cpu_stats`: `cpu_usage.percpu_usage` + * `memory_stats`: `max_usage` and `failcnt` + Also, `memory_stats.stats` fields are incompatible with cgroup v1. + + To calculate the values shown by the `stats` command of the docker cli tool + the following formulas can be used: + * used_memory = `memory_stats.usage - memory_stats.stats.cache` + * available_memory = `memory_stats.limit` + * Memory usage % = `(used_memory / available_memory) * 100.0` + * cpu_delta = `cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage` + * system_cpu_delta = `cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage` + * number_cpus = `lenght(cpu_stats.cpu_usage.percpu_usage)` or `cpu_stats.online_cpus` + * CPU usage % = `(cpu_delta / system_cpu_delta) * number_cpus * 100.0` operationId: "ContainerStats" produces: ["application/json"] responses: @@ -5344,14 +6419,23 @@ paths: type: "string" - name: "stream" in: "query" - description: "Stream the output. If false, the stats will be output once and then it will disconnect." + description: | + Stream the output. If false, the stats will be output once and then + it will disconnect. type: "boolean" default: true + - name: "one-shot" + in: "query" + description: | + Only get a single stat instead of waiting for 2 cycles. Must be used + with `stream=false`. + type: "boolean" + default: false tags: ["Container"] /containers/{id}/resize: post: summary: "Resize a container TTY" - description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + description: "Resize the TTY for a container." operationId: "ContainerResize" consumes: - "application/octet-stream" @@ -5379,11 +6463,11 @@ paths: type: "string" - name: "h" in: "query" - description: "Height of the tty session in characters" + description: "Height of the TTY session in characters" type: "integer" - name: "w" in: "query" - description: "Width of the tty session in characters" + description: "Width of the TTY session in characters" type: "integer" tags: ["Container"] /containers/{id}/start: @@ -5395,8 +6479,6 @@ paths: description: "no error" 304: description: "container already started" - schema: - $ref: "#/definitions/ErrorResponse" 404: description: "no such container" schema: @@ -5416,7 +6498,10 @@ paths: type: "string" - name: "detachKeys" in: "query" - description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + description: | + Override the key sequence for detaching a container. Format is a + single character `[a-Z]` or `ctrl-` where `` is one + of: `a-z`, `@`, `^`, `[`, `,` or `_`. type: "string" tags: ["Container"] /containers/{id}/stop: @@ -5428,8 +6513,6 @@ paths: description: "no error" 304: description: "container already stopped" - schema: - $ref: "#/definitions/ErrorResponse" 404: description: "no such container" schema: @@ -5484,7 +6567,9 @@ paths: /containers/{id}/kill: post: summary: "Kill a container" - description: "Send a POSIX signal to a container, defaulting to killing to the container." + description: | + Send a POSIX signal to a container, defaulting to killing to the + container. operationId: "ContainerKill" responses: 204: @@ -5522,7 +6607,9 @@ paths: /containers/{id}/update: post: summary: "Update a container" - description: "Change various configuration options of a container without having to recreate it." + description: | + Change various configuration options of a container without having to + recreate it. operationId: "ContainerUpdate" consumes: ["application/json"] produces: ["application/json"] @@ -5620,9 +6707,12 @@ paths: post: summary: "Pause a container" description: | - Use the cgroups freezer to suspend all processes in a container. + Use the freezer cgroup to suspend all processes in a container. - Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + Traditionally, when suspending a process the `SIGSTOP` signal is used, + which is observable by the process being suspended. With the freezer + cgroup the process is unaware, and unable to capture, that it is being + suspended, and subsequently resumed. operationId: "ContainerPause" responses: 204: @@ -5675,15 +6765,20 @@ paths: post: summary: "Attach to a container" description: | - Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + Attach to a container to read its output or send it input. You can attach + to the same container multiple times and you can reattach to containers + that have been detached. - Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + Either the `stream` or `logs` parameter must be `true` for this endpoint + to do anything. - See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + See the [documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) + for more details. ### Hijacking - This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, + and `stderr` on the same socket. This is the response from the daemon for an attach request: @@ -5694,9 +6789,11 @@ paths: [STREAM] ``` - After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + After the headers and two new lines, the TCP connection can now be used + for raw, bidirectional communication between the client and server. - To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + To hint potential proxies about connection hijacking, the Docker client + can also optionally send connection upgrade headers. For example, the client sends this request to upgrade the connection: @@ -5706,7 +6803,8 @@ paths: Connection: Upgrade ``` - The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + The Docker daemon will respond with a `101 UPGRADED` response, and will + similarly follow with the raw stream: ``` HTTP/1.1 101 UPGRADED @@ -5719,9 +6817,14 @@ paths: ### Stream format - When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), + the stream over the hijacked connected is multiplexed to separate out + `stdout` and `stderr`. The stream consists of a series of frames, each + containing a header and a payload. - The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + The header contains the information which the stream writes (`stdout` or + `stderr`). It also contains the size of the associated frame encoded in + the last four bytes (`uint32`). It is encoded on the first eight bytes like this: @@ -5735,9 +6838,11 @@ paths: - 1: `stdout` - 2: `stderr` - `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size + encoded as big endian. - Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + Following the header is the payload, which is the specified number of + bytes of `STREAM_TYPE`. The simplest way to implement this protocol is the following: @@ -5749,7 +6854,10 @@ paths: ### Stream format when using a TTY - When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), + the stream is not multiplexed. The data exchanged over the hijacked + connection is simply the raw data from the process PTY and client's + `stdin`. operationId: "ContainerAttach" produces: @@ -5782,21 +6890,28 @@ paths: type: "string" - name: "detachKeys" in: "query" - description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + description: | + Override the key sequence for detaching a container.Format is a single + character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + `@`, `^`, `[`, `,` or `_`. type: "string" - name: "logs" in: "query" description: | Replay previous logs from the container. - This is useful for attaching to a container that has started and you want to output everything since the container started. + This is useful for attaching to a container that has started and you + want to output everything since the container started. - If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + If `stream` is also enabled, once all the previous output has been + returned, it will seamlessly transition into streaming current + output. type: "boolean" default: false - name: "stream" in: "query" - description: "Stream attached streams from the time the request was made onwards" + description: | + Stream attached streams from the time the request was made onwards. type: "boolean" default: false - name: "stdin" @@ -5847,7 +6962,10 @@ paths: type: "string" - name: "detachKeys" in: "query" - description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + description: | + Override the key sequence for detaching a container.Format is a single + character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + `@`, `^`, `[`, `,`, or `_`. type: "string" - name: "logs" in: "query" @@ -5920,7 +7038,9 @@ paths: type: "string" - name: "condition" in: "query" - description: "Wait until a container state reaches the given condition, either 'not-running' (default), 'next-exit', or 'removed'." + description: | + Wait until a container state reaches the given condition, either + 'not-running' (default), 'next-exit', or 'removed'. type: "string" default: "not-running" tags: ["Container"] @@ -5948,7 +7068,9 @@ paths: $ref: "#/definitions/ErrorResponse" examples: application/json: - message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + message: | + You cannot remove a running container: c2ada9df5af8. Stop the + container before attempting removal or force remove 500: description: "server error" schema: @@ -5961,7 +7083,7 @@ paths: type: "string" - name: "v" in: "query" - description: "Remove the volumes associated with the container." + description: "Remove anonymous volumes associated with the container." type: "boolean" default: false - name: "force" @@ -5978,7 +7100,10 @@ paths: /containers/{id}/archive: head: summary: "Get information about files in a container" - description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + description: | + A response header `X-Docker-Container-Path-Stat` is returned, containing + a base64 - encoded JSON object with some filesystem header information + about the path. operationId: "ContainerArchiveInfo" responses: 200: @@ -5986,7 +7111,9 @@ paths: headers: X-Docker-Container-Path-Stat: type: "string" - description: "A base64 - encoded JSON object with some filesystem header information about the path" + description: | + A base64 - encoded JSON object with some filesystem header + information about the path 400: description: "Bad parameter" schema: @@ -5995,7 +7122,10 @@ paths: - type: "object" properties: message: - description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + description: | + The error message. Either "must specify path parameter" + (path cannot be empty) or "not a directory" (path was + asserted to be a directory but exists as a file). type: "string" x-nullable: false 404: @@ -6037,7 +7167,10 @@ paths: - type: "object" properties: message: - description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + description: | + The error message. Either "must specify path parameter" + (path cannot be empty) or "not a directory" (path was + asserted to be a directory but exists as a file). type: "string" x-nullable: false 404: @@ -6101,16 +7234,29 @@ paths: required: true description: "Path to a directory in the container to extract the archive’s contents into. " type: "string" - - name: "noOverwriteDirNonDir" + - name: "noOverwriteDirNonDir" + in: "query" + description: | + If `1`, `true`, or `True` then it will be an error if unpacking the + given content would cause an existing directory to be replaced with + a non-directory and vice versa. + type: "string" + - name: "copyUIDGID" in: "query" - description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + description: | + If `1`, `true`, then it will copy UID/GID maps to the dest file or + dir type: "string" - name: "inputStream" in: "body" required: true - description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + description: | + The input stream must be a tar archive compressed with one of the + following algorithms: `identity` (no compression), `gzip`, `bzip2`, + or `xz`. schema: type: "string" + format: "binary" tags: ["Container"] /containers/prune: post: @@ -6205,7 +7351,10 @@ paths: - name: "filters" in: "query" description: | - A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + A JSON encoded value of the filters (a `map[string][]string`) to + process on the images list. + + Available filters: - `before`=(`[:]`, `` or ``) - `dangling=true` @@ -6213,6 +7362,11 @@ paths: - `reference`=(`[:]`) - `since`=(`[:]`, `` or ``) type: "string" + - name: "shared-size" + in: "query" + description: "Compute and show shared size as a `SharedSize` field on each image." + type: "boolean" + default: false - name: "digests" in: "query" description: "Show digest information as a `RepoDigests` field on each image." @@ -6321,7 +7475,7 @@ paths: For example, the build arg `FOO=bar` would become `{"FOO":"bar"}` in JSON. This would result in the - the query parameter `buildargs={"FOO":"bar"}`. Note that `{"FOO":"bar"}` should be URI component encoded. + query parameter `buildargs={"FOO":"bar"}`. Note that `{"FOO":"bar"}` should be URI component encoded. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg) @@ -6340,10 +7494,11 @@ paths: type: "string" - name: "networkmode" in: "query" - description: "Sets the networking mode for the run commands during - build. Supported standard values are: `bridge`, `host`, `none`, and - `container:`. Any other value is taken as a custom network's - name to which this container should connect to." + description: | + Sets the networking mode for the run commands during build. Supported + standard values are: `bridge`, `host`, `none`, and `container:`. + Any other value is taken as a custom network's name or ID to which this + container should connect to. type: "string" - name: "Content-type" in: "header" @@ -6383,6 +7538,11 @@ paths: description: "Target build stage" type: "string" default: "" + - name: "outputs" + in: "query" + description: "BuildKit output configuration" + type: "string" + default: "" responses: 200: description: "no error" @@ -6415,8 +7575,12 @@ paths: in: "query" type: "string" description: | - A JSON encoded value of the filters (a `map[string][]string`) to process on the list of build cache objects. Available filters: - - `unused-for=`: duration relative to daemon's time, during which build cache was not used, in Go's duration format (e.g., '24h') + A JSON encoded value of the filters (a `map[string][]string`) to + process on the list of build cache objects. + + Available filters: + + - `until=`: duration relative to daemon's time, during which build cache was not used, in Go's duration format (e.g., '24h') - `id=` - `parent=` - `type=` @@ -6483,6 +7647,10 @@ paths: in: "query" description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." type: "string" + - name: "message" + in: "query" + description: "Set commit message for imported image." + type: "string" - name: "inputImage" in: "body" description: "Image content if the value `-` has been specified in fromSrc query parameter" @@ -6491,8 +7659,24 @@ paths: required: false - name: "X-Registry-Auth" in: "header" - description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + description: | + A base64url-encoded auth configuration. + + Refer to the [authentication section](#section/Authentication) for + details. type: "string" + - name: "changes" + in: "query" + description: | + Apply `Dockerfile` instructions to the image that is created, + for example: `changes=ENV DEBUG=true`. + Note that `ENV DEBUG=true` should be URI component encoded. + + Supported `Dockerfile` instructions: + `CMD`|`ENTRYPOINT`|`ENV`|`EXPOSE`|`ONBUILD`|`USER`|`VOLUME`|`WORKDIR` + type: "array" + items: + type: "string" - name: "platform" in: "query" description: "Platform in the format os[/arch[/variant]]" @@ -6690,7 +7874,9 @@ paths: description: | Push an image to a registry. - If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + If you wish to push an image on to a private registry, that image must + already have a tag which references the registry. For example, + `registry.example.com/myimage:latest`. The push is cancelled if the HTTP connection is closed. operationId: "ImagePush" @@ -6719,7 +7905,11 @@ paths: type: "string" - name: "X-Registry-Auth" in: "header" - description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + description: | + A base64url-encoded auth configuration. + + Refer to the [authentication section](#section/Authentication) for + details. type: "string" required: true tags: ["Image"] @@ -6923,7 +8113,9 @@ paths: /auth: post: summary: "Check auth configuration" - description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + description: | + Validate credentials for a registry and, if available, get an identity + token for accessing the registry without password. operationId: "SystemAuth" consumes: ["application/json"] produces: ["application/json"] @@ -6986,63 +8178,7 @@ paths: 200: description: "no error" schema: - type: "object" - title: "SystemVersionResponse" - properties: - Platform: - type: "object" - required: [Name] - properties: - Name: - type: "string" - Components: - type: "array" - items: - type: "object" - x-go-name: ComponentVersion - required: [Name, Version] - properties: - Name: - type: "string" - Version: - type: "string" - x-nullable: false - Details: - type: "object" - x-nullable: true - - Version: - type: "string" - ApiVersion: - type: "string" - MinAPIVersion: - type: "string" - GitCommit: - type: "string" - GoVersion: - type: "string" - Os: - type: "string" - Arch: - type: "string" - KernelVersion: - type: "string" - Experimental: - type: "boolean" - BuildTime: - type: "string" - examples: - application/json: - Version: "17.04.0" - Os: "linux" - KernelVersion: "3.19.0-23-generic" - GoVersion: "go1.7.5" - GitCommit: "deadbee" - Arch: "amd64" - ApiVersion: "1.27" - MinAPIVersion: "1.12" - BuildTime: "2016-06-14T07:09:13.444803460+00:00" - Experimental: true + $ref: "#/definitions/SystemVersion" 500: description: "server error" schema: @@ -7064,12 +8200,57 @@ paths: API-Version: type: "string" description: "Max API Version the server supports" - BuildKit-Version: + Builder-Version: + type: "string" + description: "Default version of docker image builder" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + headers: + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + tags: ["System"] + head: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPingHead" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "(empty)" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Builder-Version: type: "string" description: "Default version of docker image builder" Docker-Experimental: type: "boolean" description: "If the server is running with experimental mode enabled" + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" 500: description: "server error" schema: @@ -7143,13 +8324,13 @@ paths: Various objects within Docker report events when something happens to them. - Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, `update`, and `prune` - Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, `untag`, and `prune` - Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + Volumes report these events: `create`, `mount`, `unmount`, `destroy`, and `prune` - Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, `remove`, and `prune` The Docker daemon reports these events: `reload` @@ -7161,6 +8342,8 @@ paths: Configs report these events: `create`, `update`, and `remove` + The Builder reports `prune` events + operationId: "SystemEvents" produces: - "application/json" @@ -7168,44 +8351,7 @@ paths: 200: description: "no error" schema: - type: "object" - title: "SystemEventsResponse" - properties: - Type: - description: "The type of object emitting the event" - type: "string" - Action: - description: "The type of event" - type: "string" - Actor: - type: "object" - properties: - ID: - description: "The ID of the object emitting the event" - type: "string" - Attributes: - description: "Various key/value attributes of the object, depending on its type" - type: "object" - additionalProperties: - type: "string" - time: - description: "Timestamp of event" - type: "integer" - timeNano: - description: "Timestamp of event, with nanosecond accuracy" - type: "integer" - format: "int64" - examples: - application/json: - Type: "container" - Action: "create" - Actor: - ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" - Attributes: - com.example.some-label: "some-label-value" - image: "alpine" - name: "my-container" - time: 1461943101 + $ref: "#/definitions/EventMessage" 400: description: "bad parameter" schema: @@ -7333,10 +8479,43 @@ paths: UsageData: Size: 10920104 RefCount: 2 + BuildCache: + - + ID: "hw53o5aio51xtltp5xjp8v7fx" + Parent: "" + Type: "regular" + Description: "pulled from docker.io/library/debian@sha256:234cb88d3020898631af0ccbbcca9a66ae7306ecd30c9720690858c1b007d2a0" + InUse: false + Shared: true + Size: 0 + CreatedAt: "2021-06-28T13:31:01.474619385Z" + LastUsedAt: "2021-07-07T22:02:32.738075951Z" + UsageCount: 26 + - + ID: "ndlpt0hhvkqcdfkputsk4cq9c" + Parent: "hw53o5aio51xtltp5xjp8v7fx" + Type: "regular" + Description: "mount / from exec /bin/sh -c echo 'Binary::apt::APT::Keep-Downloaded-Packages \"true\";' > /etc/apt/apt.conf.d/keep-cache" + InUse: false + Shared: true + Size: 51 + CreatedAt: "2021-06-28T13:31:03.002625487Z" + LastUsedAt: "2021-07-07T22:02:32.773909517Z" + UsageCount: 26 500: description: "server error" schema: $ref: "#/definitions/ErrorResponse" + parameters: + - name: "type" + in: "query" + description: | + Object types, for which to compute and return data. + type: "array" + collectionFormat: multi + items: + type: "string" + enum: ["container", "image", "volume", "build-cache"] tags: ["System"] /images/{name}/get: get: @@ -7389,11 +8568,16 @@ paths: get: summary: "Export several images" description: | - Get a tarball containing all images and metadata for several image repositories. + Get a tarball containing all images and metadata for several image + repositories. - For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + For each value of the `names` parameter: if it is a specific name and + tag (e.g. `ubuntu:latest`), then only that image (and its parents) are + returned; if it is an image ID, similarly only that image (and its parents) + are returned and there would be no names referenced in the 'repositories' + file for this image ID. - For details on the format, see [the export image endpoint](#operation/ImageGet). + For details on the format, see the [export image endpoint](#operation/ImageGet). operationId: "ImageGetAll" produces: - "application/x-tar" @@ -7421,7 +8605,7 @@ paths: description: | Load a set of images and tags into a repository. - For details on the format, see [the export image endpoint](#operation/ImageGet). + For details on the format, see the [export image endpoint](#operation/ImageGet). operationId: "ImageLoad" consumes: - "application/x-tar" @@ -7482,6 +8666,7 @@ paths: description: "Exec configuration" schema: type: "object" + title: "ExecConfig" properties: AttachStdin: type: "boolean" @@ -7494,12 +8679,16 @@ paths: description: "Attach to `stderr` of the exec command." DetachKeys: type: "string" - description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + description: | + Override the key sequence for detaching a container. Format is + a single character `[a-Z]` or `ctrl-` where `` + is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. Tty: type: "boolean" description: "Allocate a pseudo-TTY." Env: - description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + description: | + A list of environment variables in the form `["VAR=value", ...]`. type: "array" items: type: "string" @@ -7514,10 +8703,14 @@ paths: default: false User: type: "string" - description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + description: | + The user, and optionally, group to run the exec process inside + the container. Format is one of: `user`, `user:group`, `uid`, + or `uid:gid`. WorkingDir: type: "string" - description: "The working directory for the exec process inside the container." + description: | + The working directory for the exec process inside the container. example: AttachStdin: false AttachStdout: true @@ -7539,7 +8732,10 @@ paths: /exec/{id}/start: post: summary: "Start an exec instance" - description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + description: | + Starts a previously set up exec instance. If detach is true, this endpoint + returns immediately after starting the command. Otherwise, it sets up an + interactive session with the command. operationId: "ExecStart" consumes: - "application/json" @@ -7561,6 +8757,7 @@ paths: in: "body" schema: type: "object" + title: "ExecStartConfig" properties: Detach: type: "boolean" @@ -7580,7 +8777,9 @@ paths: /exec/{id}/resize: post: summary: "Resize an exec instance" - description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + description: | + Resize the TTY session used by an exec instance. This endpoint only works + if `tty` was specified as part of creating and starting the exec instance. operationId: "ExecResize" responses: 201: @@ -7700,7 +8899,8 @@ paths: Warnings: type: "array" x-nullable: false - description: "Warnings that occurred when fetching the list of volumes" + description: | + Warnings that occurred when fetching the list of volumes. items: type: "string" @@ -7769,7 +8969,8 @@ paths: title: "VolumeConfig" properties: Name: - description: "The new volume's name. If not specified, Docker generates a name." + description: | + The new volume's name. If not specified, Docker generates a name. type: "string" x-nullable: false Driver: @@ -7778,7 +8979,9 @@ paths: default: "local" x-nullable: false DriverOpts: - description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + description: | + A mapping of driver options and values. These options are + passed directly to the driver and are driver specific. type: "object" additionalProperties: type: "string" @@ -7892,10 +9095,12 @@ paths: get: summary: "List networks" description: | - Returns a list of networks. For details on the format, see [the network inspect endpoint](#operation/NetworkInspect). + Returns a list of networks. For details on the format, see the + [network inspect endpoint](#operation/NetworkInspect). - Note that it uses a different, smaller representation of a network than inspecting a single network. For example, - the list of containers attached to the network is not propagated in API versions 1.28 and up. + Note that it uses a different, smaller representation of a network than + inspecting a single network. For example, the list of containers attached + to the network is not propagated in API versions 1.28 and up. operationId: "NetworkList" produces: - "application/json" @@ -7965,8 +9170,15 @@ paths: - name: "filters" in: "query" description: | - JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + JSON encoded value of the filters (a `map[string][]string`) to process + on the networks list. + + Available filters: + - `dangling=` When set to `true` (or `1`), returns all + networks that are not in use by a container. When set to `false` + (or `0`), only networks that are in use by one or more + containers are returned. - `driver=` Matches a network's driver. - `id=` Matches all or part of a network ID. - `label=` or `label==` of a network label. @@ -8080,13 +9292,21 @@ paths: required: true schema: type: "object" + title: "NetworkCreateRequest" required: ["Name"] properties: Name: description: "The network's name." type: "string" CheckDuplicate: - description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + description: | + Check for networks with duplicate names. Since Network is + primarily keyed based on a random ID and not on the name, and + network name is strictly a user-friendly alias to the network + which is uniquely identified using ID, there is no guaranteed + way to check for duplicates. CheckDuplicate is there to provide + a best effort checking of any networks which has the same name + but it is not guaranteed to catch all name collisions. type: "boolean" Driver: description: "Name of the network driver plugin to use." @@ -8096,10 +9316,14 @@ paths: description: "Restrict external access to the network." type: "boolean" Attachable: - description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + description: | + Globally scoped network is manually attachable by regular + containers from workers in swarm mode. type: "boolean" Ingress: - description: "Ingress network is the network which provides the routing-mesh in swarm mode." + description: | + Ingress network is the network which provides the routing-mesh + in swarm mode. type: "boolean" IPAM: description: "Optional custom IP scheme for the network." @@ -8179,6 +9403,7 @@ paths: required: true schema: type: "object" + title: "NetworkConnectRequest" properties: Container: type: "string" @@ -8225,13 +9450,16 @@ paths: required: true schema: type: "object" + title: "NetworkDisconnectRequest" properties: Container: type: "string" - description: "The ID or name of the container to disconnect from the network." + description: | + The ID or name of the container to disconnect from the network. Force: type: "boolean" - description: "Force the container to disconnect from the network." + description: | + Force the container to disconnect from the network. tags: ["Network"] /networks/prune: post: @@ -8288,7 +9516,10 @@ paths: in: "query" type: "string" description: | - A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + A JSON encoded value of the filters (a `map[string][]string`) to + process on the plugin list. + + Available filters: - `capability=` - `enable=|` @@ -8304,18 +9535,7 @@ paths: schema: type: "array" items: - description: "Describes a permission the user has to accept upon installing the plugin." - type: "object" - title: "PluginPrivilegeItem" - properties: - Name: - type: "string" - Description: - type: "string" - Value: - type: "array" - items: - type: "string" + $ref: "#/definitions/PluginPrivilege" example: - Name: "network" Description: "" @@ -8336,7 +9556,9 @@ paths: parameters: - name: "remote" in: "query" - description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. required: true type: "string" tags: @@ -8347,7 +9569,8 @@ paths: summary: "Install a plugin" operationId: "PluginPull" description: | - Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + Pulls and installs a plugin. After the plugin is installed, it can be + enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). produces: - "application/json" responses: @@ -8376,24 +9599,19 @@ paths: type: "string" - name: "X-Registry-Auth" in: "header" - description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + description: | + A base64url-encoded auth configuration to use when pulling a plugin + from a registry. + + Refer to the [authentication section](#section/Authentication) for + details. type: "string" - name: "body" in: "body" schema: type: "array" items: - description: "Describes a permission accepted by the user upon installing the plugin." - type: "object" - properties: - Name: - type: "string" - Description: - type: "string" - Value: - type: "array" - items: - type: "string" + $ref: "#/definitions/PluginPrivilege" example: - Name: "network" Description: "" @@ -8428,7 +9646,9 @@ paths: parameters: - name: "name" in: "path" - description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. required: true type: "string" tags: ["Plugin"] @@ -8452,12 +9672,16 @@ paths: parameters: - name: "name" in: "path" - description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. required: true type: "string" - name: "force" in: "query" - description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + description: | + Disable the plugin before removing. This may result in issues if the + plugin is in use by a container. type: "boolean" default: false tags: ["Plugin"] @@ -8479,7 +9703,9 @@ paths: parameters: - name: "name" in: "path" - description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. required: true type: "string" - name: "timeout" @@ -8506,7 +9732,9 @@ paths: parameters: - name: "name" in: "path" - description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. required: true type: "string" tags: ["Plugin"] @@ -8528,7 +9756,9 @@ paths: parameters: - name: "name" in: "path" - description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. required: true type: "string" - name: "remote" @@ -8541,24 +9771,19 @@ paths: type: "string" - name: "X-Registry-Auth" in: "header" - description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + description: | + A base64url-encoded auth configuration to use when pulling a plugin + from a registry. + + Refer to the [authentication section](#section/Authentication) for + details. type: "string" - name: "body" in: "body" schema: type: "array" items: - description: "Describes a permission accepted by the user upon installing the plugin." - type: "object" - properties: - Name: - type: "string" - Description: - type: "string" - Value: - type: "array" - items: - type: "string" + $ref: "#/definitions/PluginPrivilege" example: - Name: "network" Description: "" @@ -8589,7 +9814,9 @@ paths: parameters: - name: "name" in: "query" - description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. required: true type: "string" - name: "tarContext" @@ -8608,7 +9835,9 @@ paths: parameters: - name: "name" in: "path" - description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. required: true type: "string" responses: @@ -8632,7 +9861,9 @@ paths: parameters: - name: "name" in: "path" - description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. required: true type: "string" - name: "body" @@ -8781,7 +10012,9 @@ paths: $ref: "#/definitions/NodeSpec" - name: "version" in: "query" - description: "The version number of the node object being updated. This is required to avoid conflicting writes." + description: | + The version number of the node object being updated. This is required + to avoid conflicting writes. type: "integer" format: "int64" required: true @@ -8840,26 +10073,50 @@ paths: required: true schema: type: "object" + title: "SwarmInitRequest" properties: ListenAddr: - description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + description: | + Listen address used for inter-manager communication, as well + as determining the networking interface used for the VXLAN + Tunnel Endpoint (VTEP). This can either be an address/port + combination in the form `192.168.1.1:4567`, or an interface + followed by a port number, like `eth0:4567`. If the port number + is omitted, the default swarm listening port is used. type: "string" AdvertiseAddr: - description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + description: | + Externally reachable address advertised to other nodes. This + can either be an address/port combination in the form + `192.168.1.1:4567`, or an interface followed by a port number, + like `eth0:4567`. If the port number is omitted, the port + number from the listen address is used. If `AdvertiseAddr` is + not specified, it will be automatically detected when possible. type: "string" DataPathAddr: description: | - Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, - or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` - is used. - - The `DataPathAddr` specifies the address that global scope network drivers will publish towards other - nodes in order to reach the containers running on this node. Using this parameter it is possible to - separate the container data traffic from the management traffic of the cluster. + Address or interface to use for data path traffic (format: + ``), for example, `192.168.1.1`, or an interface, + like `eth0`. If `DataPathAddr` is unspecified, the same address + as `AdvertiseAddr` is used. + + The `DataPathAddr` specifies the address that global scope + network drivers will publish towards other nodes in order to + reach the containers running on this node. Using this parameter + it is possible to separate the container data traffic from the + management traffic of the cluster. type: "string" + DataPathPort: + description: | + DataPathPort specifies the data path port number for data traffic. + Acceptable port range is 1024 to 49151. + if no port is set or is set to 0, default port 4789 will be used. + type: "integer" + format: "uint32" DefaultAddrPool: description: | - Default Address Pool specifies default subnet pools for global scope networks. + Default Address Pool specifies default subnet pools for global + scope networks. type: "array" items: type: "string" @@ -8869,7 +10126,8 @@ paths: type: "boolean" SubnetSize: description: | - SubnetSize specifies the subnet size of the networks created from the default subnet pool + SubnetSize specifies the subnet size of the networks created + from the default subnet pool. type: "integer" format: "uint32" Spec: @@ -8877,6 +10135,7 @@ paths: example: ListenAddr: "0.0.0.0:2377" AdvertiseAddr: "192.168.1.1:2377" + DataPathPort: 4789 DefaultAddrPool: ["10.10.0.0/8", "20.20.0.0/8"] SubnetSize: 24 ForceNewCluster: false @@ -8913,27 +10172,43 @@ paths: required: true schema: type: "object" + title: "SwarmJoinRequest" properties: ListenAddr: - description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + description: | + Listen address used for inter-manager communication if the node + gets promoted to manager, as well as determining the networking + interface used for the VXLAN Tunnel Endpoint (VTEP). type: "string" AdvertiseAddr: - description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + description: | + Externally reachable address advertised to other nodes. This + can either be an address/port combination in the form + `192.168.1.1:4567`, or an interface followed by a port number, + like `eth0:4567`. If the port number is omitted, the port + number from the listen address is used. If `AdvertiseAddr` is + not specified, it will be automatically detected when possible. type: "string" DataPathAddr: description: | - Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, - or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` - is used. + Address or interface to use for data path traffic (format: + ``), for example, `192.168.1.1`, or an interface, + like `eth0`. If `DataPathAddr` is unspecified, the same address + as `AdvertiseAddr` is used. - The `DataPathAddr` specifies the address that global scope network drivers will publish towards other - nodes in order to reach the containers running on this node. Using this parameter it is possible to - separate the container data traffic from the management traffic of the cluster. + The `DataPathAddr` specifies the address that global scope + network drivers will publish towards other nodes in order to + reach the containers running on this node. Using this parameter + it is possible to separate the container data traffic from the + management traffic of the cluster. type: "string" RemoteAddrs: - description: "Addresses of manager nodes already participating in the swarm." - type: "string" + description: | + Addresses of manager nodes already participating in the swarm. + type: "array" + items: + type: "string" JoinToken: description: "Secret token for joining this swarm." type: "string" @@ -8961,7 +10236,9 @@ paths: $ref: "#/definitions/ErrorResponse" parameters: - name: "force" - description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + description: | + Force leave swarm, even if this is the last manager or that it will + break the cluster. in: "query" type: "boolean" default: false @@ -8993,7 +10270,9 @@ paths: $ref: "#/definitions/SwarmSpec" - name: "version" in: "query" - description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + description: | + The version number of the swarm object being updated. This is + required to avoid conflicting writes. type: "integer" format: "int64" required: true @@ -9054,6 +10333,7 @@ paths: required: true schema: type: "object" + title: "SwarmUnlockRequest" properties: UnlockKey: description: "The swarm's unlock key." @@ -9096,12 +10376,20 @@ paths: in: "query" type: "string" description: | - A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + A JSON encoded value of the filters (a `map[string][]string`) to + process on the services list. + + Available filters: - `id=` - `label=` - `mode=["replicated"|"global"]` - `name=` + - name: "status" + in: "query" + type: "boolean" + description: | + Include service status, with count of running and desired tasks. tags: ["Service"] /services/create: post: @@ -9224,7 +10512,12 @@ paths: foo: "bar" - name: "X-Registry-Auth" in: "header" - description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + description: | + A base64url-encoded auth configuration for pulling from private + registries. + + Refer to the [authentication section](#section/Authentication) for + details. type: "string" tags: ["Service"] /services/{id}: @@ -9360,25 +10653,37 @@ paths: - name: "version" in: "query" - description: "The version number of the service object being updated. This is required to avoid conflicting writes." + description: | + The version number of the service object being updated. This is + required to avoid conflicting writes. + This version number should be the value as currently set on the + service *before* the update. You can find the current version by + calling `GET /services/{id}` required: true type: "integer" - name: "registryAuthFrom" in: "query" + description: | + If the `X-Registry-Auth` header is not specified, this parameter + indicates where to find registry authorization credentials. type: "string" - description: "If the X-Registry-Auth header is not specified, this - parameter indicates where to find registry authorization credentials. The - valid values are `spec` and `previous-spec`." + enum: ["spec", "previous-spec"] default: "spec" - name: "rollback" in: "query" + description: | + Set to this parameter to `previous` to cause a server-side rollback + to the previous service spec. The supplied spec will be ignored in + this case. type: "string" - description: "Set to this parameter to `previous` to cause a - server-side rollback to the previous service spec. The supplied spec will be - ignored in this case." - name: "X-Registry-Auth" in: "header" - description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + description: | + A base64url-encoded auth configuration for pulling from private + registries. + + Refer to the [authentication section](#section/Authentication) for + details. type: "string" tags: ["Service"] @@ -9386,23 +10691,18 @@ paths: get: summary: "Get service logs" description: | - Get `stdout` and `stderr` logs from a service. + Get `stdout` and `stderr` logs from a service. See also + [`/containers/{id}/logs`](#operation/ContainerLogs). - **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + **Note**: This endpoint works only for services with the `local`, + `json-file` or `journald` logging drivers. operationId: "ServiceLogs" - produces: - - "application/vnd.docker.raw-stream" - - "application/json" responses: - 101: - description: "logs returned as a stream" - schema: - type: "string" - format: "binary" 200: - description: "logs returned as a string in response body" + description: "logs returned as a stream in response body" schema: type: "string" + format: "binary" 404: description: "no such service" schema: @@ -9431,10 +10731,7 @@ paths: default: false - name: "follow" in: "query" - description: | - Return the logs as a stream. - - This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + description: "Keep connection after returning logs." type: "boolean" default: false - name: "stdout" @@ -9459,7 +10756,9 @@ paths: default: false - name: "tail" in: "query" - description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. type: "string" default: "all" tags: ["Service"] @@ -9600,7 +10899,10 @@ paths: in: "query" type: "string" description: | - A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + A JSON encoded value of the filters (a `map[string][]string`) to + process on the tasks list. + + Available filters: - `desired-state=(running | shutdown | accepted)` - `id=` @@ -9644,22 +10946,17 @@ paths: summary: "Get task logs" description: | Get `stdout` and `stderr` logs from a task. + See also [`/containers/{id}/logs`](#operation/ContainerLogs). - **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + **Note**: This endpoint works only for services with the `local`, + `json-file` or `journald` logging drivers. operationId: "TaskLogs" - produces: - - "application/vnd.docker.raw-stream" - - "application/json" responses: - 101: - description: "logs returned as a stream" - schema: - type: "string" - format: "binary" 200: - description: "logs returned as a string in response body" + description: "logs returned as a stream in response body" schema: type: "string" + format: "binary" 404: description: "no such task" schema: @@ -9688,10 +10985,7 @@ paths: default: false - name: "follow" in: "query" - description: | - Return the logs as a stream. - - This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + description: "Keep connection after returning logs." type: "boolean" default: false - name: "stdout" @@ -9716,9 +11010,12 @@ paths: default: false - name: "tail" in: "query" - description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. type: "string" default: "all" + tags: ["Task"] /secrets: get: summary: "List secrets" @@ -9769,7 +11066,10 @@ paths: in: "query" type: "string" description: | - A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + A JSON encoded value of the filters (a `map[string][]string`) to + process on the secrets list. + + Available filters: - `id=` - `label= or label==value` @@ -9926,10 +11226,15 @@ paths: in: "body" schema: $ref: "#/definitions/SecretSpec" - description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + description: | + The spec of the secret to update. Currently, only the Labels field + can be updated. All other fields must remain unchanged from the + [SecretInspect endpoint](#operation/SecretInspect) response values. - name: "version" in: "query" - description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + description: | + The version number of the secret object being updated. This is + required to avoid conflicting writes. type: "integer" format: "int64" required: true @@ -9968,7 +11273,10 @@ paths: in: "query" type: "string" description: | - A JSON encoded value of the filters (a `map[string][]string`) to process on the configs list. Available filters: + A JSON encoded value of the filters (a `map[string][]string`) to + process on the configs list. + + Available filters: - `id=` - `label= or label==value` @@ -10112,10 +11420,15 @@ paths: in: "body" schema: $ref: "#/definitions/ConfigSpec" - description: "The spec of the config to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [ConfigInspect endpoint](#operation/ConfigInspect) response values." + description: | + The spec of the config to update. Currently, only the Labels field + can be updated. All other fields must remain unchanged from the + [ConfigInspect endpoint](#operation/ConfigInspect) response values. - name: "version" in: "query" - description: "The version number of the config object being updated. This is required to avoid conflicting writes." + description: | + The version number of the config object being updated. This is + required to avoid conflicting writes. type: "integer" format: "int64" required: true @@ -10123,7 +11436,8 @@ paths: /distribution/{name}/json: get: summary: "Get image information from the registry" - description: "Return image digest and platform information by contacting the registry." + description: | + Return image digest and platform information by contacting the registry. operationId: "DistributionInspect" produces: - "application/json" @@ -10131,65 +11445,7 @@ paths: 200: description: "descriptor and platform information" schema: - type: "object" - x-go-name: DistributionInspect - title: "DistributionInspectResponse" - required: [Descriptor, Platforms] - properties: - Descriptor: - type: "object" - description: "A descriptor struct containing digest, media type, and size" - properties: - MediaType: - type: "string" - Size: - type: "integer" - format: "int64" - Digest: - type: "string" - URLs: - type: "array" - items: - type: "string" - Platforms: - type: "array" - description: "An array containing all platforms supported by the image" - items: - type: "object" - properties: - Architecture: - type: "string" - OS: - type: "string" - OSVersion: - type: "string" - OSFeatures: - type: "array" - items: - type: "string" - Variant: - type: "string" - Features: - type: "array" - items: - type: "string" - examples: - application/json: - Descriptor: - MediaType: "application/vnd.docker.distribution.manifest.v2+json" - Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" - Size: 3987495 - URLs: - - "" - Platforms: - - Architecture: "amd64" - OS: "linux" - OSVersion: "" - OSFeatures: - - "" - Variant: "" - Features: - - "" + $ref: "#/definitions/DistributionInspect" 401: description: "Failed authentication or no image found" schema: @@ -10212,14 +11468,13 @@ paths: post: summary: "Initialize interactive session" description: | - Start a new interactive session with a server. Session allows server to call back to the client for advanced capabilities. - - > **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental - > features enabled. The specifications for this endpoint may still change in a future version of the API. + Start a new interactive session with a server. Session allows server to + call back to the client for advanced capabilities. ### Hijacking - This endpoint hijacks the HTTP connection to HTTP2 transport that allows the client to expose gPRC services on that connection. + This endpoint hijacks the HTTP connection to HTTP2 transport that allows + the client to expose gPRC services on that connection. For example, the client sends this request to upgrade the connection: @@ -10229,7 +11484,8 @@ paths: Connection: Upgrade ``` - The Docker daemon will respond with a `101 UPGRADED` response follow with the raw stream: + The Docker daemon responds with a `101 UPGRADED` response follow with + the raw stream: ``` HTTP/1.1 101 UPGRADED @@ -10250,4 +11506,4 @@ paths: description: "server error" schema: $ref: "#/definitions/ErrorResponse" - tags: ["Session (experimental)"] + tags: ["Session"] diff --git a/api/templates/server/operation.gotmpl b/api/templates/server/operation.gotmpl index cf24aacc5f6a1..828deb4735c39 100644 --- a/api/templates/server/operation.gotmpl +++ b/api/templates/server/operation.gotmpl @@ -1,8 +1,7 @@ -package {{ .Package }} +package {{ .Package }} // import "github.com/docker/docker/api/types/{{ .Package }}" // ---------------------------------------------------------------------------- -// DO NOT EDIT THIS FILE -// This file was generated by `swagger generate operation` +// Code generated by `swagger generate operation`. DO NOT EDIT. // // See hack/generate-swagger-api.sh // ---------------------------------------------------------------------------- diff --git a/api/types/backend/backend.go b/api/types/backend/backend.go index ef1e669c396f1..9880c632bdd4b 100644 --- a/api/types/backend/backend.go +++ b/api/types/backend/backend.go @@ -30,7 +30,7 @@ type ContainerAttachConfig struct { // expectation is for the logger endpoints to assemble the chunks using this // metadata. type PartialLogMetaData struct { - Last bool //true if this message is last of a partial + Last bool // true if this message is last of a partial ID string // identifies group of messages comprising a single record Ordinal int // ordering of message in partial group } @@ -73,6 +73,7 @@ type LogSelector struct { // behavior of a backend.ContainerStats() call. type ContainerStatsConfig struct { Stream bool + OneShot bool OutStream io.Writer Version string } diff --git a/api/types/client.go b/api/types/client.go index 3b698c2c240d0..e3c06cef691a8 100644 --- a/api/types/client.go +++ b/api/types/client.go @@ -50,7 +50,7 @@ type ContainerCommitOptions struct { // ContainerExecInspect holds information returned by exec inspect. type ContainerExecInspect struct { - ExecID string + ExecID string `json:"ID"` ContainerID string Running bool ExitCode int @@ -59,7 +59,6 @@ type ContainerExecInspect struct { // ContainerListOptions holds parameters to list containers with. type ContainerListOptions struct { - Quiet bool Size bool All bool Latest bool @@ -187,6 +186,15 @@ type ImageBuildOptions struct { // build request. The same identifier can be used to gracefully cancel the // build with the cancel request. BuildID string + // Outputs defines configurations for exporting build results. Only supported + // in BuildKit mode + Outputs []ImageBuildOutput +} + +// ImageBuildOutput defines configuration for exporting a build result +type ImageBuildOutput struct { + Type string + Attrs map[string]string } // BuilderVersion sets the version of underlying builder to use @@ -196,7 +204,7 @@ const ( // BuilderV1 is the first generation builder in docker daemon BuilderV1 BuilderVersion = "1" // BuilderBuildKit is builder based on moby/buildkit project - BuilderBuildKit = "2" + BuilderBuildKit BuilderVersion = "2" ) // ImageBuildResponse holds information @@ -227,10 +235,20 @@ type ImageImportOptions struct { Platform string // Platform is the target platform of the image } -// ImageListOptions holds parameters to filter the list of images with. +// ImageListOptions holds parameters to list images with. type ImageListOptions struct { - All bool + // All controls whether all images in the graph are filtered, or just + // the heads. + All bool + + // Filters is a JSON-encoded set of filter arguments. Filters filters.Args + + // SharedSize indicates whether the shared size of images should be computed. + SharedSize bool + + // ContainerCount indicates whether container count should be computed. + ContainerCount bool } // ImageLoadResponse returns information to the client about a load process. @@ -256,7 +274,7 @@ type ImagePullOptions struct { // if the privilege request fails. type RequestPrivilegeFunc func() (string, error) -//ImagePushOptions holds information to push images. +// ImagePushOptions holds information to push images. type ImagePushOptions ImagePullOptions // ImageRemoveOptions holds parameters to remove images. @@ -354,6 +372,10 @@ type ServiceUpdateOptions struct { // ServiceListOptions holds parameters to list services with. type ServiceListOptions struct { Filters filters.Args + + // Status indicates whether the server should include the service task + // count of running and desired tasks. + Status bool } // ServiceInspectOptions holds parameters related to the "service inspect" diff --git a/api/types/configs.go b/api/types/configs.go index 178e911a7afc2..3dd133a3a58a4 100644 --- a/api/types/configs.go +++ b/api/types/configs.go @@ -3,6 +3,7 @@ package types // import "github.com/docker/docker/api/types" import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" + specs "github.com/opencontainers/image-spec/specs-go/v1" ) // configs holds structs used for internal communication between the @@ -15,6 +16,7 @@ type ContainerCreateConfig struct { Config *container.Config HostConfig *container.HostConfig NetworkingConfig *network.NetworkingConfig + Platform *specs.Platform AdjustCPUShares bool } diff --git a/api/types/container/config.go b/api/types/container/config.go index 89ad08c23461f..f767195b94b41 100644 --- a/api/types/container/config.go +++ b/api/types/container/config.go @@ -54,7 +54,7 @@ type Config struct { Env []string // List of environment variable to set in the container Cmd strslice.StrSlice // Command to run when starting the container Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy - ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific) + ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (meaning treat as a command line) (Windows specific). Image string // Name of the image as it was passed by the operator (e.g. could be symbolic) Volumes map[string]struct{} // List of volumes (mounts) used for the container WorkingDir string // Current directory (PWD) in the command will be launched diff --git a/api/types/container/container_changes.go b/api/types/container/container_changes.go index c909d6ca3e9e7..16dd5019eef88 100644 --- a/api/types/container/container_changes.go +++ b/api/types/container/container_changes.go @@ -1,8 +1,7 @@ -package container +package container // import "github.com/docker/docker/api/types/container" // ---------------------------------------------------------------------------- -// DO NOT EDIT THIS FILE -// This file was generated by `swagger generate operation` +// Code generated by `swagger generate operation`. DO NOT EDIT. // // See hack/generate-swagger-api.sh // ---------------------------------------------------------------------------- diff --git a/api/types/container/container_create.go b/api/types/container/container_create.go index 49efa0f2c093c..d0c852f84d5c2 100644 --- a/api/types/container/container_create.go +++ b/api/types/container/container_create.go @@ -1,8 +1,7 @@ -package container +package container // import "github.com/docker/docker/api/types/container" // ---------------------------------------------------------------------------- -// DO NOT EDIT THIS FILE -// This file was generated by `swagger generate operation` +// Code generated by `swagger generate operation`. DO NOT EDIT. // // See hack/generate-swagger-api.sh // ---------------------------------------------------------------------------- diff --git a/api/types/container/container_top.go b/api/types/container/container_top.go index ba41edcf3f842..63381da36749a 100644 --- a/api/types/container/container_top.go +++ b/api/types/container/container_top.go @@ -1,8 +1,7 @@ -package container +package container // import "github.com/docker/docker/api/types/container" // ---------------------------------------------------------------------------- -// DO NOT EDIT THIS FILE -// This file was generated by `swagger generate operation` +// Code generated by `swagger generate operation`. DO NOT EDIT. // // See hack/generate-swagger-api.sh // ---------------------------------------------------------------------------- @@ -11,7 +10,9 @@ package container // swagger:model ContainerTopOKBody type ContainerTopOKBody struct { - // Each process running in the container, where each is process is an array of values corresponding to the titles + // Each process running in the container, where each is process + // is an array of values corresponding to the titles. + // // Required: true Processes [][]string `json:"Processes"` diff --git a/api/types/container/container_update.go b/api/types/container/container_update.go index 7630ae54cd6d3..c10f175ea82f7 100644 --- a/api/types/container/container_update.go +++ b/api/types/container/container_update.go @@ -1,8 +1,7 @@ -package container +package container // import "github.com/docker/docker/api/types/container" // ---------------------------------------------------------------------------- -// DO NOT EDIT THIS FILE -// This file was generated by `swagger generate operation` +// Code generated by `swagger generate operation`. DO NOT EDIT. // // See hack/generate-swagger-api.sh // ---------------------------------------------------------------------------- diff --git a/api/types/container/container_wait.go b/api/types/container/container_wait.go index 9e3910a6b42ec..49e05ae669449 100644 --- a/api/types/container/container_wait.go +++ b/api/types/container/container_wait.go @@ -1,8 +1,7 @@ -package container +package container // import "github.com/docker/docker/api/types/container" // ---------------------------------------------------------------------------- -// DO NOT EDIT THIS FILE -// This file was generated by `swagger generate operation` +// Code generated by `swagger generate operation`. DO NOT EDIT. // // See hack/generate-swagger-api.sh // ---------------------------------------------------------------------------- diff --git a/api/types/container/host_config.go b/api/types/container/host_config.go index 4ef26fa6c878b..dcea6c8a5ae66 100644 --- a/api/types/container/host_config.go +++ b/api/types/container/host_config.go @@ -7,67 +7,106 @@ import ( "github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/strslice" "github.com/docker/go-connections/nat" - "github.com/docker/go-units" + units "github.com/docker/go-units" ) +// CgroupnsMode represents the cgroup namespace mode of the container +type CgroupnsMode string + +// cgroup namespace modes for containers +const ( + CgroupnsModeEmpty CgroupnsMode = "" + CgroupnsModePrivate CgroupnsMode = "private" + CgroupnsModeHost CgroupnsMode = "host" +) + +// IsPrivate indicates whether the container uses its own private cgroup namespace +func (c CgroupnsMode) IsPrivate() bool { + return c == CgroupnsModePrivate +} + +// IsHost indicates whether the container shares the host's cgroup namespace +func (c CgroupnsMode) IsHost() bool { + return c == CgroupnsModeHost +} + +// IsEmpty indicates whether the container cgroup namespace mode is unset +func (c CgroupnsMode) IsEmpty() bool { + return c == CgroupnsModeEmpty +} + +// Valid indicates whether the cgroup namespace mode is valid +func (c CgroupnsMode) Valid() bool { + return c.IsEmpty() || c.IsPrivate() || c.IsHost() +} + // Isolation represents the isolation technology of a container. The supported // values are platform specific type Isolation string +// Isolation modes for containers +const ( + IsolationEmpty Isolation = "" // IsolationEmpty is unspecified (same behavior as default) + IsolationDefault Isolation = "default" // IsolationDefault is the default isolation mode on current daemon + IsolationProcess Isolation = "process" // IsolationProcess is process isolation mode + IsolationHyperV Isolation = "hyperv" // IsolationHyperV is HyperV isolation mode +) + // IsDefault indicates the default isolation technology of a container. On Linux this // is the native driver. On Windows, this is a Windows Server Container. func (i Isolation) IsDefault() bool { - return strings.ToLower(string(i)) == "default" || string(i) == "" + // TODO consider making isolation-mode strict (case-sensitive) + v := Isolation(strings.ToLower(string(i))) + return v == IsolationDefault || v == IsolationEmpty } // IsHyperV indicates the use of a Hyper-V partition for isolation func (i Isolation) IsHyperV() bool { - return strings.ToLower(string(i)) == "hyperv" + // TODO consider making isolation-mode strict (case-sensitive) + return Isolation(strings.ToLower(string(i))) == IsolationHyperV } // IsProcess indicates the use of process isolation func (i Isolation) IsProcess() bool { - return strings.ToLower(string(i)) == "process" + // TODO consider making isolation-mode strict (case-sensitive) + return Isolation(strings.ToLower(string(i))) == IsolationProcess } -const ( - // IsolationEmpty is unspecified (same behavior as default) - IsolationEmpty = Isolation("") - // IsolationDefault is the default isolation mode on current daemon - IsolationDefault = Isolation("default") - // IsolationProcess is process isolation mode - IsolationProcess = Isolation("process") - // IsolationHyperV is HyperV isolation mode - IsolationHyperV = Isolation("hyperv") -) - // IpcMode represents the container ipc stack. type IpcMode string +// IpcMode constants +const ( + IPCModeNone IpcMode = "none" + IPCModeHost IpcMode = "host" + IPCModeContainer IpcMode = "container" + IPCModePrivate IpcMode = "private" + IPCModeShareable IpcMode = "shareable" +) + // IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared. func (n IpcMode) IsPrivate() bool { - return n == "private" + return n == IPCModePrivate } // IsHost indicates whether the container shares the host's ipc namespace. func (n IpcMode) IsHost() bool { - return n == "host" + return n == IPCModeHost } // IsShareable indicates whether the container's ipc namespace can be shared with another container. func (n IpcMode) IsShareable() bool { - return n == "shareable" + return n == IPCModeShareable } // IsContainer indicates whether the container uses another container's ipc namespace. func (n IpcMode) IsContainer() bool { - parts := strings.SplitN(string(n), ":", 2) - return len(parts) > 1 && parts[0] == "container" + return strings.HasPrefix(string(n), string(IPCModeContainer)+":") } // IsNone indicates whether container IpcMode is set to "none". func (n IpcMode) IsNone() bool { - return n == "none" + return n == IPCModeNone } // IsEmpty indicates whether container IpcMode is empty @@ -82,9 +121,8 @@ func (n IpcMode) Valid() bool { // Container returns the name of the container ipc stack is going to be used. func (n IpcMode) Container() string { - parts := strings.SplitN(string(n), ":", 2) - if len(parts) > 1 && parts[0] == "container" { - return parts[1] + if n.IsContainer() { + return strings.TrimPrefix(string(n), string(IPCModeContainer)+":") } return "" } @@ -122,7 +160,7 @@ func (n NetworkMode) ConnectedContainer() string { return "" } -//UserDefined indicates user-created network +// UserDefined indicates user-created network func (n NetworkMode) UserDefined() string { if n.IsUserDefined() { return string(n) @@ -244,6 +282,16 @@ func (n PidMode) Container() string { return "" } +// DeviceRequest represents a request for devices from a device driver. +// Used by GPU device drivers. +type DeviceRequest struct { + Driver string // Name of device driver + Count int // Number of devices to request (-1 = All) + DeviceIDs []string // List of device IDs as recognizable by the device driver + Capabilities [][]string // An OR list of AND lists of device capabilities (e.g. "gpu") + Options map[string]string // Options to pass onto the device driver +} + // DeviceMapping represents the device mapping between the host and the container. type DeviceMapping struct { PathOnHost string @@ -293,7 +341,7 @@ type LogMode string // Available logging modes const ( - LogModeUnset = "" + LogModeUnset LogMode = "" LogModeBlocking LogMode = "blocking" LogModeNonBlock LogMode = "non-blocking" ) @@ -327,13 +375,14 @@ type Resources struct { CpusetMems string // CpusetMems 0-2, 0,1 Devices []DeviceMapping // List of devices to map inside the container DeviceCgroupRules []string // List of rule to be added to the device cgroup - DiskQuota int64 // Disk limit (in bytes) - KernelMemory int64 // Kernel memory limit (in bytes) + DeviceRequests []DeviceRequest // List of device requests for device drivers + KernelMemory int64 // Kernel memory limit (in bytes), Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes + KernelMemoryTCP int64 // Hard limit for kernel TCP buffer memory (in bytes) MemoryReservation int64 // Memory soft limit (in bytes) MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap MemorySwappiness *int64 // Tuning container memory swappiness behaviour OomKillDisable *bool // Whether to disable OOM Killer or not - PidsLimit int64 // Setting pids limit for a container + PidsLimit *int64 // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change. Ulimits []*units.Ulimit // List of ulimits to be set in the container // Applicable to Windows @@ -369,6 +418,7 @@ type HostConfig struct { // Applicable to UNIX platforms CapAdd strslice.StrSlice // List of kernel capabilities to add to the container CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container + CgroupnsMode CgroupnsMode // Cgroup namespace mode to use for the container DNS []string `json:"Dns"` // List of DNS server to lookup DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for diff --git a/api/types/container/hostconfig_unix.go b/api/types/container/hostconfig_unix.go index cf6fdf44026ca..24c4fa8d9002e 100644 --- a/api/types/container/hostconfig_unix.go +++ b/api/types/container/hostconfig_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package container // import "github.com/docker/docker/api/types/container" diff --git a/api/types/error_response_ext.go b/api/types/error_response_ext.go new file mode 100644 index 0000000000000..f84f034cd545c --- /dev/null +++ b/api/types/error_response_ext.go @@ -0,0 +1,6 @@ +package types + +// Error returns the error message +func (e ErrorResponse) Error() string { + return e.Message +} diff --git a/api/types/events/events.go b/api/types/events/events.go index 027c6edb7223e..9fe07e26fd2fe 100644 --- a/api/types/events/events.go +++ b/api/types/events/events.go @@ -1,31 +1,26 @@ package events // import "github.com/docker/docker/api/types/events" +// Type is used for event-types. +type Type = string + +// List of known event types. const ( - // ContainerEventType is the event type that containers generate - ContainerEventType = "container" - // DaemonEventType is the event type that daemon generate - DaemonEventType = "daemon" - // ImageEventType is the event type that images generate - ImageEventType = "image" - // NetworkEventType is the event type that networks generate - NetworkEventType = "network" - // PluginEventType is the event type that plugins generate - PluginEventType = "plugin" - // VolumeEventType is the event type that volumes generate - VolumeEventType = "volume" - // ServiceEventType is the event type that services generate - ServiceEventType = "service" - // NodeEventType is the event type that nodes generate - NodeEventType = "node" - // SecretEventType is the event type that secrets generate - SecretEventType = "secret" - // ConfigEventType is the event type that configs generate - ConfigEventType = "config" + BuilderEventType Type = "builder" // BuilderEventType is the event type that the builder generates. + ConfigEventType Type = "config" // ConfigEventType is the event type that configs generate. + ContainerEventType Type = "container" // ContainerEventType is the event type that containers generate. + DaemonEventType Type = "daemon" // DaemonEventType is the event type that daemon generate. + ImageEventType Type = "image" // ImageEventType is the event type that images generate. + NetworkEventType Type = "network" // NetworkEventType is the event type that networks generate. + NodeEventType Type = "node" // NodeEventType is the event type that nodes generate. + PluginEventType Type = "plugin" // PluginEventType is the event type that plugins generate. + SecretEventType Type = "secret" // SecretEventType is the event type that secrets generate. + ServiceEventType Type = "service" // ServiceEventType is the event type that services generate. + VolumeEventType Type = "volume" // VolumeEventType is the event type that volumes generate. ) // Actor describes something that generates events, // like a container, or a network, or a volume. -// It has a defined name and a set or attributes. +// It has a defined name and a set of attributes. // The container attributes are its labels, other actors // can generate these attributes from other properties. type Actor struct { @@ -37,11 +32,11 @@ type Actor struct { type Message struct { // Deprecated information from JSONMessage. // With data only in container events. - Status string `json:"status,omitempty"` - ID string `json:"id,omitempty"` - From string `json:"from,omitempty"` + Status string `json:"status,omitempty"` // Deprecated: use Action instead. + ID string `json:"id,omitempty"` // Deprecated: use Actor.ID instead. + From string `json:"from,omitempty"` // Deprecated: use Actor.Attributes["image"] instead. - Type string + Type Type Action string Actor Actor // Engine events are local scope. Cluster events are swarm scope. diff --git a/api/types/filters/parse.go b/api/types/filters/parse.go index a41e3d8d96ad6..4bc91cffd6e5c 100644 --- a/api/types/filters/parse.go +++ b/api/types/filters/parse.go @@ -5,7 +5,6 @@ package filters // import "github.com/docker/docker/api/types/filters" import ( "encoding/json" - "errors" "regexp" "strings" @@ -37,39 +36,13 @@ func NewArgs(initialArgs ...KeyValuePair) Args { return args } -// ParseFlag parses a key=value string and adds it to an Args. -// -// Deprecated: Use Args.Add() -func ParseFlag(arg string, prev Args) (Args, error) { - filters := prev - if len(arg) == 0 { - return filters, nil - } - - if !strings.Contains(arg, "=") { - return filters, ErrBadFormat +// Keys returns all the keys in list of Args +func (args Args) Keys() []string { + keys := make([]string, 0, len(args.fields)) + for k := range args.fields { + keys = append(keys, k) } - - f := strings.SplitN(arg, "=", 2) - - name := strings.ToLower(strings.TrimSpace(f[0])) - value := strings.TrimSpace(f[1]) - - filters.Add(name, value) - - return filters, nil -} - -// ErrBadFormat is an error returned when a filter is not in the form key=value -// -// Deprecated: this error will be removed in a future version -var ErrBadFormat = errors.New("bad format of filter (expected name=value)") - -// ToParam encodes the Args as args JSON encoded string -// -// Deprecated: use ToJSON -func ToParam(a Args) (string, error) { - return ToJSON(a) + return keys } // MarshalJSON returns a JSON byte representation of the Args @@ -93,7 +66,7 @@ func ToJSON(a Args) (string, error) { // then the encoded format will use an older legacy format where the values are a // list of strings, instead of a set. // -// Deprecated: Use ToJSON +// Deprecated: do not use in any new code; use ToJSON instead func ToParamWithVersion(version string, a Args) (string, error) { if a.Len() == 0 { return "", nil @@ -107,13 +80,6 @@ func ToParamWithVersion(version string, a Args) (string, error) { return ToJSON(a) } -// FromParam decodes a JSON encoded string into Args -// -// Deprecated: use FromJSON -func FromParam(p string) (Args, error) { - return FromJSON(p) -} - // FromJSON decodes a JSON encoded string into Args func FromJSON(p string) (Args, error) { args := NewArgs() @@ -188,7 +154,7 @@ func (args Args) Len() int { func (args Args) MatchKVList(key string, sources map[string]string) bool { fieldValues := args.fields[key] - //do not filter if there is no filter set or cannot determine filter + // do not filter if there is no filter set or cannot determine filter if len(fieldValues) == 0 { return true } @@ -234,7 +200,7 @@ func (args Args) Match(field, source string) bool { // ExactMatch returns true if the source matches exactly one of the values. func (args Args) ExactMatch(key, source string) bool { fieldValues, ok := args.fields[key] - //do not filter if there is no filter set or cannot determine filter + // do not filter if there is no filter set or cannot determine filter if !ok || len(fieldValues) == 0 { return true } @@ -247,7 +213,7 @@ func (args Args) ExactMatch(key, source string) bool { // matches exactly the value. func (args Args) UniqueExactMatch(key, source string) bool { fieldValues := args.fields[key] - //do not filter if there is no filter set or cannot determine filter + // do not filter if there is no filter set or cannot determine filter if len(fieldValues) == 0 { return true } @@ -275,14 +241,6 @@ func (args Args) FuzzyMatch(key, source string) bool { return false } -// Include returns true if the key exists in the mapping -// -// Deprecated: use Contains -func (args Args) Include(field string) bool { - _, ok := args.fields[field] - return ok -} - // Contains returns true if the key exists in the mapping func (args Args) Contains(field string) bool { _, ok := args.fields[field] @@ -323,6 +281,22 @@ func (args Args) WalkValues(field string, op func(value string) error) error { return nil } +// Clone returns a copy of args. +func (args Args) Clone() (newArgs Args) { + newArgs.fields = make(map[string]map[string]bool, len(args.fields)) + for k, m := range args.fields { + var mm map[string]bool + if m != nil { + mm = make(map[string]bool, len(m)) + for kk, v := range m { + mm[kk] = v + } + } + newArgs.fields[k] = mm + } + return newArgs +} + func deprecatedArgs(d map[string][]string) map[string]map[string]bool { m := map[string]map[string]bool{} for k, v := range d { diff --git a/api/types/filters/parse_test.go b/api/types/filters/parse_test.go index e8345a1d5d524..4b68c9685dd2a 100644 --- a/api/types/filters/parse_test.go +++ b/api/types/filters/parse_test.go @@ -4,44 +4,10 @@ import ( "errors" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) -func TestParseArgs(t *testing.T) { - // equivalent of `docker ps -f 'created=today' -f 'image.name=ubuntu*' -f 'image.name=*untu'` - flagArgs := []string{ - "created=today", - "image.name=ubuntu*", - "image.name=*untu", - } - var ( - args = NewArgs() - err error - ) - - for i := range flagArgs { - args, err = ParseFlag(flagArgs[i], args) - assert.NilError(t, err) - } - assert.Check(t, is.Len(args.Get("created"), 1)) - assert.Check(t, is.Len(args.Get("image.name"), 2)) -} - -func TestParseArgsEdgeCase(t *testing.T) { - var args Args - args, err := ParseFlag("", args) - if err != nil { - t.Fatal(err) - } - if args.Len() != 0 { - t.Fatalf("Expected an empty Args (map), got %v", args) - } - if args, err = ParseFlag("anything", args); err == nil || err != ErrBadFormat { - t.Fatalf("Expected ErrBadFormat, got %v", err) - } -} - func TestToJSON(t *testing.T) { fields := map[string]map[string]bool{ "created": {"today": true}, @@ -347,17 +313,6 @@ func TestContains(t *testing.T) { } } -func TestInclude(t *testing.T) { - f := NewArgs() - if f.Include("status") { - t.Fatal("Expected to not include a status key, got true") - } - f.Add("status", "running") - if !f.Include("status") { - t.Fatal("Expected to include a status key, got false") - } -} - func TestValidate(t *testing.T) { f := NewArgs() f.Add("status", "running") @@ -382,14 +337,17 @@ func TestWalkValues(t *testing.T) { f.Add("status", "running") f.Add("status", "paused") - f.WalkValues("status", func(value string) error { + err := f.WalkValues("status", func(value string) error { if value != "running" && value != "paused" { t.Fatalf("Unexpected value %s", value) } return nil }) + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } - err := f.WalkValues("status", func(value string) error { + err = f.WalkValues("status", func(value string) error { return errors.New("return") }) if err == nil { @@ -421,3 +379,11 @@ func TestFuzzyMatch(t *testing.T) { } } } + +func TestClone(t *testing.T) { + f := NewArgs() + f.Add("foo", "bar") + f2 := f.Clone() + f2.Add("baz", "qux") + assert.Check(t, is.Len(f.Get("baz"), 0)) +} diff --git a/api/types/image/image_history.go b/api/types/image/image_history.go index d6b354bcdf389..e302bb0aebbe7 100644 --- a/api/types/image/image_history.go +++ b/api/types/image/image_history.go @@ -1,8 +1,7 @@ -package image +package image // import "github.com/docker/docker/api/types/image" // ---------------------------------------------------------------------------- -// DO NOT EDIT THIS FILE -// This file was generated by `swagger generate operation` +// Code generated by `swagger generate operation`. DO NOT EDIT. // // See hack/generate-swagger-api.sh // ---------------------------------------------------------------------------- diff --git a/api/types/mount/mount.go b/api/types/mount/mount.go index 3fef974df8835..443b8d07a9f3c 100644 --- a/api/types/mount/mount.go +++ b/api/types/mount/mount.go @@ -79,7 +79,8 @@ const ( // BindOptions defines options specific to mounts of type "bind". type BindOptions struct { - Propagation Propagation `json:",omitempty"` + Propagation Propagation `json:",omitempty"` + NonRecursive bool `json:",omitempty"` } // VolumeOptions represents the options for a mount of type volume. @@ -112,7 +113,7 @@ type TmpfsOptions struct { // TODO(stevvooe): There are several more tmpfs flags, specified in the // daemon, that are accepted. Only the most basic are added for now. // - // From docker/docker/pkg/mount/flags.go: + // From https://github.com/moby/sys/blob/mount/v0.1.1/mount/flags.go#L47-L56 // // var validFlags = map[string]bool{ // "": true, diff --git a/api/types/network/network.go b/api/types/network/network.go index ccb448f23a214..437b184c67b5b 100644 --- a/api/types/network/network.go +++ b/api/types/network/network.go @@ -1,7 +1,6 @@ package network // import "github.com/docker/docker/api/types/network" import ( "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/errdefs" ) // Address represents an IP address @@ -13,7 +12,7 @@ type Address struct { // IPAM represents IP Address Management type IPAM struct { Driver string - Options map[string]string //Per network IPAM driver options + Options map[string]string // Per network IPAM driver options Config []IPAMConfig } @@ -112,15 +111,16 @@ type ConfigReference struct { } var acceptedFilters = map[string]bool{ - "driver": true, - "type": true, - "name": true, - "id": true, - "label": true, - "scope": true, + "dangling": true, + "driver": true, + "id": true, + "label": true, + "name": true, + "scope": true, + "type": true, } // ValidateFilters validates the list of filter args with the available filters. func ValidateFilters(filter filters.Args) error { - return errdefs.InvalidParameter(filter.Validate(acceptedFilters)) + return filter.Validate(acceptedFilters) } diff --git a/api/types/registry/registry.go b/api/types/registry/registry.go index 8789ad3b32101..53e47084c8d5e 100644 --- a/api/types/registry/registry.go +++ b/api/types/registry/registry.go @@ -4,7 +4,7 @@ import ( "encoding/json" "net" - "github.com/opencontainers/image-spec/specs-go/v1" + v1 "github.com/opencontainers/image-spec/specs-go/v1" ) // ServiceConfig stores daemon registry services configuration. diff --git a/api/types/seccomp.go b/api/types/seccomp.go deleted file mode 100644 index 67a41e1a89e87..0000000000000 --- a/api/types/seccomp.go +++ /dev/null @@ -1,93 +0,0 @@ -package types // import "github.com/docker/docker/api/types" - -// Seccomp represents the config for a seccomp profile for syscall restriction. -type Seccomp struct { - DefaultAction Action `json:"defaultAction"` - // Architectures is kept to maintain backward compatibility with the old - // seccomp profile. - Architectures []Arch `json:"architectures,omitempty"` - ArchMap []Architecture `json:"archMap,omitempty"` - Syscalls []*Syscall `json:"syscalls"` -} - -// Architecture is used to represent a specific architecture -// and its sub-architectures -type Architecture struct { - Arch Arch `json:"architecture"` - SubArches []Arch `json:"subArchitectures"` -} - -// Arch used for architectures -type Arch string - -// Additional architectures permitted to be used for system calls -// By default only the native architecture of the kernel is permitted -const ( - ArchX86 Arch = "SCMP_ARCH_X86" - ArchX86_64 Arch = "SCMP_ARCH_X86_64" - ArchX32 Arch = "SCMP_ARCH_X32" - ArchARM Arch = "SCMP_ARCH_ARM" - ArchAARCH64 Arch = "SCMP_ARCH_AARCH64" - ArchMIPS Arch = "SCMP_ARCH_MIPS" - ArchMIPS64 Arch = "SCMP_ARCH_MIPS64" - ArchMIPS64N32 Arch = "SCMP_ARCH_MIPS64N32" - ArchMIPSEL Arch = "SCMP_ARCH_MIPSEL" - ArchMIPSEL64 Arch = "SCMP_ARCH_MIPSEL64" - ArchMIPSEL64N32 Arch = "SCMP_ARCH_MIPSEL64N32" - ArchPPC Arch = "SCMP_ARCH_PPC" - ArchPPC64 Arch = "SCMP_ARCH_PPC64" - ArchPPC64LE Arch = "SCMP_ARCH_PPC64LE" - ArchS390 Arch = "SCMP_ARCH_S390" - ArchS390X Arch = "SCMP_ARCH_S390X" -) - -// Action taken upon Seccomp rule match -type Action string - -// Define actions for Seccomp rules -const ( - ActKill Action = "SCMP_ACT_KILL" - ActTrap Action = "SCMP_ACT_TRAP" - ActErrno Action = "SCMP_ACT_ERRNO" - ActTrace Action = "SCMP_ACT_TRACE" - ActAllow Action = "SCMP_ACT_ALLOW" -) - -// Operator used to match syscall arguments in Seccomp -type Operator string - -// Define operators for syscall arguments in Seccomp -const ( - OpNotEqual Operator = "SCMP_CMP_NE" - OpLessThan Operator = "SCMP_CMP_LT" - OpLessEqual Operator = "SCMP_CMP_LE" - OpEqualTo Operator = "SCMP_CMP_EQ" - OpGreaterEqual Operator = "SCMP_CMP_GE" - OpGreaterThan Operator = "SCMP_CMP_GT" - OpMaskedEqual Operator = "SCMP_CMP_MASKED_EQ" -) - -// Arg used for matching specific syscall arguments in Seccomp -type Arg struct { - Index uint `json:"index"` - Value uint64 `json:"value"` - ValueTwo uint64 `json:"valueTwo"` - Op Operator `json:"op"` -} - -// Filter is used to conditionally apply Seccomp rules -type Filter struct { - Caps []string `json:"caps,omitempty"` - Arches []string `json:"arches,omitempty"` -} - -// Syscall is used to match a group of syscalls in Seccomp -type Syscall struct { - Name string `json:"name,omitempty"` - Names []string `json:"names,omitempty"` - Action Action `json:"action"` - Args []*Arg `json:"args"` - Comment string `json:"comment"` - Includes Filter `json:"includes"` - Excludes Filter `json:"excludes"` -} diff --git a/api/types/swarm/config.go b/api/types/swarm/config.go index a1555cf43eeeb..16202ccce6151 100644 --- a/api/types/swarm/config.go +++ b/api/types/swarm/config.go @@ -27,9 +27,14 @@ type ConfigReferenceFileTarget struct { Mode os.FileMode } +// ConfigReferenceRuntimeTarget is a target for a config specifying that it +// isn't mounted into the container but instead has some other purpose. +type ConfigReferenceRuntimeTarget struct{} + // ConfigReference is a reference to a config in swarm type ConfigReference struct { - File *ConfigReferenceFileTarget + File *ConfigReferenceFileTarget `json:",omitempty"` + Runtime *ConfigReferenceRuntimeTarget `json:",omitempty"` ConfigID string ConfigName string } diff --git a/api/types/swarm/container.go b/api/types/swarm/container.go index e12f09837fce8..af5e1c0bc2799 100644 --- a/api/types/swarm/container.go +++ b/api/types/swarm/container.go @@ -5,6 +5,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" + "github.com/docker/go-units" ) // DNSConfig specifies DNS related configurations in resolver configuration file (resolv.conf) @@ -33,6 +34,7 @@ type SELinuxContext struct { // CredentialSpec for managed service account (Windows only) type CredentialSpec struct { + Config string File string Registry string } @@ -66,10 +68,13 @@ type ContainerSpec struct { // The format of extra hosts on swarmkit is specified in: // http://man7.org/linux/man-pages/man5/hosts.5.html // IP_address canonical_hostname [aliases...] - Hosts []string `json:",omitempty"` - DNSConfig *DNSConfig `json:",omitempty"` - Secrets []*SecretReference `json:",omitempty"` - Configs []*ConfigReference `json:",omitempty"` - Isolation container.Isolation `json:",omitempty"` - Sysctls map[string]string `json:",omitempty"` + Hosts []string `json:",omitempty"` + DNSConfig *DNSConfig `json:",omitempty"` + Secrets []*SecretReference `json:",omitempty"` + Configs []*ConfigReference `json:",omitempty"` + Isolation container.Isolation `json:",omitempty"` + Sysctls map[string]string `json:",omitempty"` + CapabilityAdd []string `json:",omitempty"` + CapabilityDrop []string `json:",omitempty"` + Ulimits []*units.Ulimit `json:",omitempty"` } diff --git a/api/types/swarm/runtime/plugin.pb.go b/api/types/swarm/runtime/plugin.pb.go index 1fdc9b0436135..e45045866a6ea 100644 --- a/api/types/swarm/runtime/plugin.pb.go +++ b/api/types/swarm/runtime/plugin.pb.go @@ -1,6 +1,5 @@ -// Code generated by protoc-gen-gogo. +// Code generated by protoc-gen-gogo. DO NOT EDIT. // source: plugin.proto -// DO NOT EDIT! /* Package runtime is a generated protocol buffer package. @@ -38,6 +37,7 @@ type PluginSpec struct { Remote string `protobuf:"bytes,2,opt,name=remote,proto3" json:"remote,omitempty"` Privileges []*PluginPrivilege `protobuf:"bytes,3,rep,name=privileges" json:"privileges,omitempty"` Disabled bool `protobuf:"varint,4,opt,name=disabled,proto3" json:"disabled,omitempty"` + Env []string `protobuf:"bytes,5,rep,name=env" json:"env,omitempty"` } func (m *PluginSpec) Reset() { *m = PluginSpec{} } @@ -73,6 +73,13 @@ func (m *PluginSpec) GetDisabled() bool { return false } +func (m *PluginSpec) GetEnv() []string { + if m != nil { + return m.Env + } + return nil +} + // PluginPrivilege describes a permission the user has to accept // upon installing a plugin. type PluginPrivilege struct { @@ -160,6 +167,21 @@ func (m *PluginSpec) MarshalTo(dAtA []byte) (int, error) { } i++ } + if len(m.Env) > 0 { + for _, s := range m.Env { + dAtA[i] = 0x2a + i++ + l = len(s) + for l >= 1<<7 { + dAtA[i] = uint8(uint64(l)&0x7f | 0x80) + l >>= 7 + i++ + } + dAtA[i] = uint8(l) + i++ + i += copy(dAtA[i:], s) + } + } return i, nil } @@ -208,24 +230,6 @@ func (m *PluginPrivilege) MarshalTo(dAtA []byte) (int, error) { return i, nil } -func encodeFixed64Plugin(dAtA []byte, offset int, v uint64) int { - dAtA[offset] = uint8(v) - dAtA[offset+1] = uint8(v >> 8) - dAtA[offset+2] = uint8(v >> 16) - dAtA[offset+3] = uint8(v >> 24) - dAtA[offset+4] = uint8(v >> 32) - dAtA[offset+5] = uint8(v >> 40) - dAtA[offset+6] = uint8(v >> 48) - dAtA[offset+7] = uint8(v >> 56) - return offset + 8 -} -func encodeFixed32Plugin(dAtA []byte, offset int, v uint32) int { - dAtA[offset] = uint8(v) - dAtA[offset+1] = uint8(v >> 8) - dAtA[offset+2] = uint8(v >> 16) - dAtA[offset+3] = uint8(v >> 24) - return offset + 4 -} func encodeVarintPlugin(dAtA []byte, offset int, v uint64) int { for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) @@ -255,6 +259,12 @@ func (m *PluginSpec) Size() (n int) { if m.Disabled { n += 2 } + if len(m.Env) > 0 { + for _, s := range m.Env { + l = len(s) + n += 1 + l + sovPlugin(uint64(l)) + } + } return n } @@ -429,6 +439,35 @@ func (m *PluginSpec) Unmarshal(dAtA []byte) error { } } m.Disabled = bool(v != 0) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Env", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlugin + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPlugin + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Env = append(m.Env, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPlugin(dAtA[iNdEx:]) @@ -695,18 +734,21 @@ var ( func init() { proto.RegisterFile("plugin.proto", fileDescriptorPlugin) } var fileDescriptorPlugin = []byte{ - // 196 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0xc8, 0x29, 0x4d, - 0xcf, 0xcc, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x6a, 0x63, 0xe4, 0xe2, 0x0a, 0x00, 0x0b, - 0x04, 0x17, 0xa4, 0x26, 0x0b, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, - 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, 0x62, 0x5c, 0x6c, 0x45, 0xa9, 0xb9, 0xf9, 0x25, 0xa9, 0x12, - 0x4c, 0x60, 0x51, 0x28, 0x4f, 0xc8, 0x80, 0x8b, 0xab, 0xa0, 0x28, 0xb3, 0x2c, 0x33, 0x27, 0x35, - 0x3d, 0xb5, 0x58, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x40, 0x0f, 0x62, 0x58, 0x00, 0x4c, - 0x22, 0x08, 0x49, 0x8d, 0x90, 0x14, 0x17, 0x47, 0x4a, 0x66, 0x71, 0x62, 0x52, 0x4e, 0x6a, 0x8a, - 0x04, 0x8b, 0x02, 0xa3, 0x06, 0x47, 0x10, 0x9c, 0xaf, 0x14, 0xcb, 0xc5, 0x8f, 0xa6, 0x15, 0xab, - 0x63, 0x14, 0xb8, 0xb8, 0x53, 0x52, 0x8b, 0x93, 0x8b, 0x32, 0x0b, 0x4a, 0x32, 0xf3, 0xf3, 0xa0, - 0x2e, 0x42, 0x16, 0x12, 0x12, 0xe1, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, 0x05, 0xbb, 0x88, 0x33, - 0x08, 0xc2, 0x71, 0xe2, 0x39, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, - 0x18, 0x93, 0xd8, 0xc0, 0x9e, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x84, 0xad, 0x79, - 0x0c, 0x01, 0x00, 0x00, + // 256 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x4d, 0x4b, 0xc3, 0x30, + 0x18, 0xc7, 0x89, 0xdd, 0xc6, 0xfa, 0x4c, 0x70, 0x04, 0x91, 0xe2, 0xa1, 0x94, 0x9d, 0x7a, 0x6a, + 0x45, 0x2f, 0x82, 0x37, 0x0f, 0x9e, 0x47, 0xbc, 0x09, 0x1e, 0xd2, 0xf6, 0xa1, 0x06, 0x9b, 0x17, + 0x92, 0xb4, 0xe2, 0x37, 0xf1, 0x23, 0x79, 0xf4, 0x23, 0x48, 0x3f, 0x89, 0x98, 0x75, 0x32, 0x64, + 0xa7, 0xff, 0x4b, 0xc2, 0x9f, 0x1f, 0x0f, 0x9c, 0x9a, 0xae, 0x6f, 0x85, 0x2a, 0x8c, 0xd5, 0x5e, + 0x6f, 0x3e, 0x08, 0xc0, 0x36, 0x14, 0x8f, 0x06, 0x6b, 0x4a, 0x61, 0xa6, 0xb8, 0xc4, 0x84, 0x64, + 0x24, 0x8f, 0x59, 0xf0, 0xf4, 0x02, 0x16, 0x16, 0xa5, 0xf6, 0x98, 0x9c, 0x84, 0x76, 0x4a, 0xf4, + 0x0a, 0xc0, 0x58, 0x31, 0x88, 0x0e, 0x5b, 0x74, 0x49, 0x94, 0x45, 0xf9, 0xea, 0x7a, 0x5d, 0xec, + 0xc6, 0xb6, 0xfb, 0x07, 0x76, 0xf0, 0x87, 0x5e, 0xc2, 0xb2, 0x11, 0x8e, 0x57, 0x1d, 0x36, 0xc9, + 0x2c, 0x23, 0xf9, 0x92, 0xfd, 0x65, 0xba, 0x86, 0x08, 0xd5, 0x90, 0xcc, 0xb3, 0x28, 0x8f, 0xd9, + 0xaf, 0xdd, 0x3c, 0xc3, 0xd9, 0xbf, 0xb1, 0xa3, 0x78, 0x19, 0xac, 0x1a, 0x74, 0xb5, 0x15, 0xc6, + 0x0b, 0xad, 0x26, 0xc6, 0xc3, 0x8a, 0x9e, 0xc3, 0x7c, 0xe0, 0x5d, 0x8f, 0x81, 0x31, 0x66, 0xbb, + 0x70, 0xff, 0xf0, 0x39, 0xa6, 0xe4, 0x6b, 0x4c, 0xc9, 0xf7, 0x98, 0x92, 0xa7, 0xdb, 0x56, 0xf8, + 0x97, 0xbe, 0x2a, 0x6a, 0x2d, 0xcb, 0x46, 0xd7, 0xaf, 0x68, 0xf7, 0xc2, 0x8d, 0x28, 0xfd, 0xbb, + 0x41, 0x57, 0xba, 0x37, 0x6e, 0x65, 0x69, 0x7b, 0xe5, 0x85, 0xc4, 0xbb, 0x49, 0xab, 0x45, 0x38, + 0xe4, 0xcd, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x99, 0xa8, 0xd9, 0x9b, 0x58, 0x01, 0x00, 0x00, } diff --git a/api/types/swarm/runtime/plugin.proto b/api/types/swarm/runtime/plugin.proto index 6d63b7783fd9f..9ef169046b4fa 100644 --- a/api/types/swarm/runtime/plugin.proto +++ b/api/types/swarm/runtime/plugin.proto @@ -9,6 +9,7 @@ message PluginSpec { string remote = 2; repeated PluginPrivilege privileges = 3; bool disabled = 4; + repeated string env = 5; } // PluginPrivilege describes a permission the user has to accept diff --git a/api/types/swarm/service.go b/api/types/swarm/service.go index abf192e759414..6eb452d24d122 100644 --- a/api/types/swarm/service.go +++ b/api/types/swarm/service.go @@ -10,6 +10,17 @@ type Service struct { PreviousSpec *ServiceSpec `json:",omitempty"` Endpoint Endpoint `json:",omitempty"` UpdateStatus *UpdateStatus `json:",omitempty"` + + // ServiceStatus is an optional, extra field indicating the number of + // desired and running tasks. It is provided primarily as a shortcut to + // calculating these values client-side, which otherwise would require + // listing all tasks for a service, an operation that could be + // computation and network expensive. + ServiceStatus *ServiceStatus `json:",omitempty"` + + // JobStatus is the status of a Service which is in one of ReplicatedJob or + // GlobalJob modes. It is absent on Replicated and Global services. + JobStatus *JobStatus `json:",omitempty"` } // ServiceSpec represents the spec of a service. @@ -32,8 +43,10 @@ type ServiceSpec struct { // ServiceMode represents the mode of a service. type ServiceMode struct { - Replicated *ReplicatedService `json:",omitempty"` - Global *GlobalService `json:",omitempty"` + Replicated *ReplicatedService `json:",omitempty"` + Global *GlobalService `json:",omitempty"` + ReplicatedJob *ReplicatedJob `json:",omitempty"` + GlobalJob *GlobalJob `json:",omitempty"` } // UpdateState is the state of a service update. @@ -70,6 +83,32 @@ type ReplicatedService struct { // GlobalService is a kind of ServiceMode. type GlobalService struct{} +// ReplicatedJob is the a type of Service which executes a defined Tasks +// in parallel until the specified number of Tasks have succeeded. +type ReplicatedJob struct { + // MaxConcurrent indicates the maximum number of Tasks that should be + // executing simultaneously for this job at any given time. There may be + // fewer Tasks that MaxConcurrent executing simultaneously; for example, if + // there are fewer than MaxConcurrent tasks needed to reach + // TotalCompletions. + // + // If this field is empty, it will default to a max concurrency of 1. + MaxConcurrent *uint64 `json:",omitempty"` + + // TotalCompletions is the total number of Tasks desired to run to + // completion. + // + // If this field is empty, the value of MaxConcurrent will be used. + TotalCompletions *uint64 `json:",omitempty"` +} + +// GlobalJob is the type of a Service which executes a Task on every Node +// matching the Service's placement constraints. These tasks run to completion +// and then exit. +// +// This type is deliberately empty. +type GlobalJob struct{} + const ( // UpdateFailureActionPause PAUSE UpdateFailureActionPause = "pause" @@ -122,3 +161,42 @@ type UpdateConfig struct { // started, or the new task is started before the old task is shut down. Order string } + +// ServiceStatus represents the number of running tasks in a service and the +// number of tasks desired to be running. +type ServiceStatus struct { + // RunningTasks is the number of tasks for the service actually in the + // Running state + RunningTasks uint64 + + // DesiredTasks is the number of tasks desired to be running by the + // service. For replicated services, this is the replica count. For global + // services, this is computed by taking the number of tasks with desired + // state of not-Shutdown. + DesiredTasks uint64 + + // CompletedTasks is the number of tasks in the state Completed, if this + // service is in ReplicatedJob or GlobalJob mode. This field must be + // cross-referenced with the service type, because the default value of 0 + // may mean that a service is not in a job mode, or it may mean that the + // job has yet to complete any tasks. + CompletedTasks uint64 +} + +// JobStatus is the status of a job-type service. +type JobStatus struct { + // JobIteration is a value increased each time a Job is executed, + // successfully or otherwise. "Executed", in this case, means the job as a + // whole has been started, not that an individual Task has been launched. A + // job is "Executed" when its ServiceSpec is updated. JobIteration can be + // used to disambiguate Tasks belonging to different executions of a job. + // + // Though JobIteration will increase with each subsequent execution, it may + // not necessarily increase by 1, and so JobIteration should not be used to + // keep track of the number of times a job has been executed. + JobIteration Version + + // LastExecution is the time that the job was last executed, as observed by + // Swarm manager. + LastExecution time.Time `json:",omitempty"` +} diff --git a/api/types/swarm/swarm.go b/api/types/swarm/swarm.go index b742cf1bfbb31..b25f9996462e0 100644 --- a/api/types/swarm/swarm.go +++ b/api/types/swarm/swarm.go @@ -14,6 +14,7 @@ type ClusterInfo struct { RootRotationInProgress bool DefaultAddrPool []string SubnetSize uint32 + DataPathPort uint32 } // Swarm represents a swarm. @@ -153,6 +154,7 @@ type InitRequest struct { ListenAddr string AdvertiseAddr string DataPathAddr string + DataPathPort uint32 ForceNewCluster bool Spec Spec AutoLockManagers bool @@ -207,6 +209,8 @@ type Info struct { Managers int `json:",omitempty"` Cluster *ClusterInfo `json:",omitempty"` + + Warnings []string `json:",omitempty"` } // Peer represents a peer. diff --git a/api/types/swarm/task.go b/api/types/swarm/task.go index b35605d12fd2a..a6f7ab7b5c790 100644 --- a/api/types/swarm/task.go +++ b/api/types/swarm/task.go @@ -56,6 +56,12 @@ type Task struct { DesiredState TaskState `json:",omitempty"` NetworksAttachments []NetworkAttachment `json:",omitempty"` GenericResources []GenericResource `json:",omitempty"` + + // JobIteration is the JobIteration of the Service that this Task was + // spawned from, if the Service is a ReplicatedJob or GlobalJob. This is + // used to determine which Tasks belong to which run of the job. This field + // is absent if the Service mode is Replicated or Global. + JobIteration *Version `json:",omitempty"` } // TaskSpec represents the spec of a task. @@ -85,13 +91,21 @@ type TaskSpec struct { Runtime RuntimeType `json:",omitempty"` } -// Resources represents resources (CPU/Memory). +// Resources represents resources (CPU/Memory) which can be advertised by a +// node and requested to be reserved for a task. type Resources struct { NanoCPUs int64 `json:",omitempty"` MemoryBytes int64 `json:",omitempty"` GenericResources []GenericResource `json:",omitempty"` } +// Limit describes limits on resources which can be requested by a task. +type Limit struct { + NanoCPUs int64 `json:",omitempty"` + MemoryBytes int64 `json:",omitempty"` + Pids int64 `json:",omitempty"` +} + // GenericResource represents a "user defined" resource which can // be either an integer (e.g: SSD=3) or a string (e.g: SSD=sda1) type GenericResource struct { @@ -119,7 +133,7 @@ type DiscreteGenericResource struct { // ResourceRequirements represents resources requirements. type ResourceRequirements struct { - Limits *Resources `json:",omitempty"` + Limits *Limit `json:",omitempty"` Reservations *Resources `json:",omitempty"` } @@ -127,6 +141,7 @@ type ResourceRequirements struct { type Placement struct { Constraints []string `json:",omitempty"` Preferences []PlacementPreference `json:",omitempty"` + MaxReplicas uint64 `json:",omitempty"` // Platforms stores all the platforms that the image can run on. // This field is used in the platform filter for scheduling. If empty, diff --git a/api/types/types.go b/api/types/types.go index a8fae3ba32d76..a186550d72421 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -39,6 +39,7 @@ type ImageInspect struct { Author string Config *container.Config Architecture string + Variant string `json:",omitempty"` Os string OsVersion string `json:",omitempty"` Size int64 @@ -153,15 +154,17 @@ type Info struct { Images int Driver string DriverStatus [][2]string - SystemStatus [][2]string + SystemStatus [][2]string `json:",omitempty"` // SystemStatus is only propagated by the Swarm standalone API Plugins PluginsInfo MemoryLimit bool SwapLimit bool - KernelMemory bool + KernelMemory bool // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes + KernelMemoryTCP bool CPUCfsPeriod bool `json:"CpuCfsPeriod"` CPUCfsQuota bool `json:"CpuCfsQuota"` CPUShares bool CPUSet bool + PidsLimit bool IPv4Forwarding bool BridgeNfIptables bool BridgeNfIP6tables bool `json:"BridgeNfIp6tables"` @@ -172,9 +175,11 @@ type Info struct { SystemTime string LoggingDriver string CgroupDriver string + CgroupVersion string `json:",omitempty"` NEventsListener int KernelVersion string OperatingSystem string + OSVersion string OSType string Architecture string IndexServerAddress string @@ -190,23 +195,29 @@ type Info struct { Labels []string ExperimentalBuild bool ServerVersion string - ClusterStore string - ClusterAdvertise string + ClusterStore string `json:",omitempty"` // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated + ClusterAdvertise string `json:",omitempty"` // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated Runtimes map[string]Runtime DefaultRuntime string Swarm swarm.Info // LiveRestoreEnabled determines whether containers should be kept // running when the daemon is shutdown or upon daemon start if // running containers are detected - LiveRestoreEnabled bool - Isolation container.Isolation - InitBinary string - ContainerdCommit Commit - RuncCommit Commit - InitCommit Commit - SecurityOptions []string - ProductLicense string `json:",omitempty"` - Warnings []string + LiveRestoreEnabled bool + Isolation container.Isolation + InitBinary string + ContainerdCommit Commit + RuncCommit Commit + InitCommit Commit + SecurityOptions []string + ProductLicense string `json:",omitempty"` + DefaultAddressPools []NetworkAddressPool `json:",omitempty"` + + // Warnings contains a slice of warnings that occurred while collecting + // system information. These warnings are intended to be informational + // messages for the user, and are not intended to be parsed / used for + // other purposes, as they do not have a fixed format. + Warnings []string } // KeyValue holds a key/value pair @@ -214,6 +225,12 @@ type KeyValue struct { Key, Value string } +// NetworkAddressPool is a temp struct used by Info struct +type NetworkAddressPool struct { + Base string + Size int +} + // SecurityOpt contains the name and options of a security option type SecurityOpt struct { Name string @@ -314,7 +331,7 @@ type ContainerState struct { } // ContainerNode stores information about the node that a container -// is running on. It's only available in Docker Swarm +// is running on. It's only used by the Docker Swarm standalone API type ContainerNode struct { ID string IPAddress string `json:"IP"` @@ -338,7 +355,7 @@ type ContainerJSONBase struct { HostnamePath string HostsPath string LogPath string - Node *ContainerNode `json:",omitempty"` + Node *ContainerNode `json:",omitempty"` // Node is only propagated by Docker Swarm standalone API Name string RestartCount int Driver string @@ -506,6 +523,37 @@ type Checkpoint struct { type Runtime struct { Path string `json:"path"` Args []string `json:"runtimeArgs,omitempty"` + + // This is exposed here only for internal use + // It is not currently supported to specify custom shim configs + Shim *ShimConfig `json:"-"` +} + +// ShimConfig is used by runtime to configure containerd shims +type ShimConfig struct { + Binary string + Opts interface{} +} + +// DiskUsageObject represents an object type used for disk usage query filtering. +type DiskUsageObject string + +const ( + // ContainerObject represents a container DiskUsageObject. + ContainerObject DiskUsageObject = "container" + // ImageObject represents an image DiskUsageObject. + ImageObject DiskUsageObject = "image" + // VolumeObject represents a volume DiskUsageObject. + VolumeObject DiskUsageObject = "volume" + // BuildCacheObject represents a build-cache DiskUsageObject. + BuildCacheObject DiskUsageObject = "build-cache" +) + +// DiskUsageOptions holds parameters for system disk usage query. +type DiskUsageOptions struct { + // Types specifies what object types to include in the response. If empty, + // all object types are returned. + Types []DiskUsageObject } // DiskUsage contains response of Engine API: @@ -516,7 +564,7 @@ type DiskUsage struct { Containers []*Container Volumes []*Volume BuildCache []*BuildCache - BuilderSize int64 // deprecated + BuilderSize int64 `json:",omitempty"` // Deprecated: deprecated in API 1.38, and no longer used since API 1.40. } // ContainersPruneReport contains the response for Engine API: diff --git a/api/types/versions/compare.go b/api/types/versions/compare.go index 8ccb0aa92ebe4..489e917ee52a3 100644 --- a/api/types/versions/compare.go +++ b/api/types/versions/compare.go @@ -8,6 +8,9 @@ import ( // compare compares two version strings // returns -1 if v1 < v2, 1 if v1 > v2, 0 otherwise. func compare(v1, v2 string) int { + if v1 == v2 { + return 0 + } var ( currTab = strings.Split(v1, ".") otherTab = strings.Split(v2, ".") diff --git a/api/types/volume.go b/api/types/volume.go index b5ee96a500586..c69b08448df4c 100644 --- a/api/types/volume.go +++ b/api/types/volume.go @@ -27,10 +27,13 @@ type Volume struct { Name string `json:"Name"` // The driver specific options used when creating the volume. + // // Required: true Options map[string]string `json:"Options"` - // The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level. + // The level at which the volume exists. Either `global` for cluster-wide, + // or `local` for machine level. + // // Required: true Scope string `json:"Scope"` diff --git a/api/types/volume/volume_create.go b/api/types/volume/volume_create.go index f12e48612c56c..8538078dd663c 100644 --- a/api/types/volume/volume_create.go +++ b/api/types/volume/volume_create.go @@ -1,8 +1,7 @@ -package volume +package volume // import "github.com/docker/docker/api/types/volume" // ---------------------------------------------------------------------------- -// DO NOT EDIT THIS FILE -// This file was generated by `swagger generate operation` +// Code generated by `swagger generate operation`. DO NOT EDIT. // // See hack/generate-swagger-api.sh // ---------------------------------------------------------------------------- @@ -15,7 +14,9 @@ type VolumeCreateBody struct { // Required: true Driver string `json:"Driver"` - // A mapping of driver options and values. These options are passed directly to the driver and are driver specific. + // A mapping of driver options and values. These options are + // passed directly to the driver and are driver specific. + // // Required: true DriverOpts map[string]string `json:"DriverOpts"` @@ -24,6 +25,7 @@ type VolumeCreateBody struct { Labels map[string]string `json:"Labels"` // The new volume's name. If not specified, Docker generates a name. + // // Required: true Name string `json:"Name"` } diff --git a/api/types/volume/volume_list.go b/api/types/volume/volume_list.go index 020198f7371bc..be06179bf488d 100644 --- a/api/types/volume/volume_list.go +++ b/api/types/volume/volume_list.go @@ -1,8 +1,7 @@ -package volume +package volume // import "github.com/docker/docker/api/types/volume" // ---------------------------------------------------------------------------- -// DO NOT EDIT THIS FILE -// This file was generated by `swagger generate operation` +// Code generated by `swagger generate operation`. DO NOT EDIT. // // See hack/generate-swagger-api.sh // ---------------------------------------------------------------------------- @@ -17,7 +16,8 @@ type VolumeListOKBody struct { // Required: true Volumes []*types.Volume `json:"Volumes"` - // Warnings that occurred when fetching the list of volumes + // Warnings that occurred when fetching the list of volumes. + // // Required: true Warnings []string `json:"Warnings"` } diff --git a/builder/builder-next/adapters/containerimage/pull.go b/builder/builder-next/adapters/containerimage/pull.go index f94b4ebe9eb69..0926b36ade4b7 100644 --- a/builder/builder-next/adapters/containerimage/pull.go +++ b/builder/builder-next/adapters/containerimage/pull.go @@ -5,20 +5,23 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" + "path" "runtime" "sync" "time" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" + containerderrors "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/gc" "github.com/containerd/containerd/images" + "github.com/containerd/containerd/leases" "github.com/containerd/containerd/platforms" ctdreference "github.com/containerd/containerd/reference" "github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes/docker" "github.com/containerd/containerd/remotes/docker/schema1" distreference "github.com/docker/distribution/reference" + dimages "github.com/docker/docker/daemon/images" "github.com/docker/docker/distribution" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" @@ -27,83 +30,54 @@ import ( pkgprogress "github.com/docker/docker/pkg/progress" "github.com/docker/docker/reference" "github.com/moby/buildkit/cache" - gw "github.com/moby/buildkit/frontend/gateway/client" + "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/session" - "github.com/moby/buildkit/session/auth" + "github.com/moby/buildkit/solver" "github.com/moby/buildkit/source" "github.com/moby/buildkit/util/flightcontrol" "github.com/moby/buildkit/util/imageutil" + "github.com/moby/buildkit/util/leaseutil" "github.com/moby/buildkit/util/progress" "github.com/moby/buildkit/util/resolver" - "github.com/moby/buildkit/util/tracing" digest "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/identity" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "golang.org/x/time/rate" ) // SourceOpt is options for creating the image source type SourceOpt struct { - SessionManager *session.Manager ContentStore content.Store CacheAccessor cache.Accessor ReferenceStore reference.Store DownloadManager distribution.RootFSDownloadManager MetadataStore metadata.V2MetadataService ImageStore image.Store - ResolverOpt resolver.ResolveOptionsFunc + RegistryHosts docker.RegistryHosts + LayerStore layer.Store + LeaseManager leases.Manager + GarbageCollect func(ctx context.Context) (gc.Stats, error) } -type imageSource struct { +// Source is the source implementation for accessing container images +type Source struct { SourceOpt g flightcontrol.Group } // NewSource creates a new image source -func NewSource(opt SourceOpt) (source.Source, error) { - is := &imageSource{ - SourceOpt: opt, - } - - return is, nil +func NewSource(opt SourceOpt) (*Source, error) { + return &Source{SourceOpt: opt}, nil } -func (is *imageSource) ID() string { +// ID returns image scheme identifier +func (is *Source) ID() string { return source.DockerImageScheme } -func (is *imageSource) getResolver(ctx context.Context, rfn resolver.ResolveOptionsFunc, ref string) remotes.Resolver { - opt := docker.ResolverOptions{ - Client: tracing.DefaultClient, - Credentials: is.getCredentialsFromSession(ctx), - } - if rfn != nil { - opt = rfn(ref) - } - r := docker.NewResolver(opt) - return r -} - -func (is *imageSource) getCredentialsFromSession(ctx context.Context) func(string) (string, string, error) { - id := session.FromContext(ctx) - if id == "" { - return nil - } - return func(host string) (string, string, error) { - timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - caller, err := is.SessionManager.Get(timeoutCtx, id) - if err != nil { - return "", "", err - } - - return auth.CredentialsFunc(tracing.ContextWithSpanFromContext(context.TODO(), ctx), caller)(host) - } -} - -func (is *imageSource) resolveLocal(refStr string) ([]byte, error) { +func (is *Source) resolveLocal(refStr string) (*image.Image, error) { ref, err := distreference.ParseNormalizedNamed(refStr) if err != nil { return nil, err @@ -116,16 +90,23 @@ func (is *imageSource) resolveLocal(refStr string) ([]byte, error) { if err != nil { return nil, err } - return img.RawJSON(), nil + return img, nil } -func (is *imageSource) resolveRemote(ctx context.Context, ref string, platform *ocispec.Platform) (digest.Digest, []byte, error) { +func (is *Source) resolveRemote(ctx context.Context, ref string, platform *ocispec.Platform, sm *session.Manager, g session.Group) (digest.Digest, []byte, error) { type t struct { dgst digest.Digest dt []byte } - res, err := is.g.Do(ctx, ref, func(ctx context.Context) (interface{}, error) { - dgst, dt, err := imageutil.Config(ctx, ref, is.getResolver(ctx, is.ResolverOpt, ref), is.ContentStore, platform) + p := platforms.DefaultSpec() + if platform != nil { + p = *platform + } + // key is used to synchronize resolutions that can happen in parallel when doing multi-stage. + key := "getconfig::" + ref + "::" + platforms.Format(p) + res, err := is.g.Do(ctx, key, func(ctx context.Context) (interface{}, error) { + res := resolver.DefaultPool.GetResolver(is.RegistryHosts, ref, "pull", sm, g) + dgst, dt, err := imageutil.Config(ctx, ref, res, is.ContentStore, is.LeaseManager, platform) if err != nil { return nil, err } @@ -139,14 +120,15 @@ func (is *imageSource) resolveRemote(ctx context.Context, ref string, platform * return typed.dgst, typed.dt, nil } -func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error) { +// ResolveImageConfig returns image config for an image +func (is *Source) ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt, sm *session.Manager, g session.Group) (digest.Digest, []byte, error) { resolveMode, err := source.ParseImageResolveMode(opt.ResolveMode) if err != nil { return "", nil, err } switch resolveMode { case source.ResolveModeForcePull: - dgst, dt, err := is.resolveRemote(ctx, ref, opt.Platform) + dgst, dt, err := is.resolveRemote(ctx, ref, opt.Platform, sm, g) // TODO: pull should fallback to local in case of failure to allow offline behavior // the fallback doesn't work currently return dgst, dt, err @@ -163,18 +145,26 @@ func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string, opt g // default == prefer local, but in the future could be smarter fallthrough case source.ResolveModePreferLocal: - dt, err := is.resolveLocal(ref) + img, err := is.resolveLocal(ref) if err == nil { - return "", dt, err + if opt.Platform != nil && !platformMatches(img, opt.Platform) { + logrus.WithField("ref", ref).Debugf("Requested build platform %s does not match local image platform %s, checking remote", + path.Join(opt.Platform.OS, opt.Platform.Architecture, opt.Platform.Variant), + path.Join(img.OS, img.Architecture, img.Variant), + ) + } else { + return "", img.RawJSON(), err + } } // fallback to remote - return is.resolveRemote(ctx, ref, opt.Platform) + return is.resolveRemote(ctx, ref, opt.Platform, sm, g) } // should never happen return "", nil, fmt.Errorf("builder cannot resolve image %s: invalid mode %q", ref, opt.ResolveMode) } -func (is *imageSource) Resolve(ctx context.Context, id source.Identifier) (source.SourceInstance, error) { +// Resolve returns access to pulling for an identifier +func (is *Source) Resolve(ctx context.Context, id source.Identifier, sm *session.Manager, vtx solver.Vertex) (source.SourceInstance, error) { imageIdentifier, ok := id.(*source.ImageIdentifier) if !ok { return nil, errors.Errorf("invalid image identifier %v", id) @@ -186,28 +176,32 @@ func (is *imageSource) Resolve(ctx context.Context, id source.Identifier) (sourc } p := &puller{ - src: imageIdentifier, - is: is, - resolver: is.getResolver(ctx, is.ResolverOpt, imageIdentifier.Reference.String()), + src: imageIdentifier, + is: is, + //resolver: is.getResolver(is.RegistryHosts, imageIdentifier.Reference.String(), sm, g), platform: platform, + sm: sm, } return p, nil } type puller struct { - is *imageSource - resolveOnce sync.Once + is *Source resolveLocalOnce sync.Once + g flightcontrol.Group src *source.ImageIdentifier desc ocispec.Descriptor ref string - resolveErr error - resolver remotes.Resolver config []byte platform ocispec.Platform + sm *session.Manager +} + +func (p *puller) resolver(g session.Group) remotes.Resolver { + return resolver.DefaultPool.GetResolver(p.is.RegistryHosts, p.src.Reference.String(), "pull", p.sm, g) } -func (p *puller) mainManifestKey(dgst digest.Digest, platform ocispec.Platform) (digest.Digest, error) { +func (p *puller) mainManifestKey(platform ocispec.Platform) (digest.Digest, error) { dt, err := json.Marshal(struct { Digest digest.Digest OS string @@ -248,31 +242,38 @@ func (p *puller) resolveLocal() { } if p.src.ResolveMode == source.ResolveModeDefault || p.src.ResolveMode == source.ResolveModePreferLocal { - dt, err := p.is.resolveLocal(p.src.Reference.String()) + ref := p.src.Reference.String() + img, err := p.is.resolveLocal(ref) if err == nil { - p.config = dt + if !platformMatches(img, &p.platform) { + logrus.WithField("ref", ref).Debugf("Requested build platform %s does not match local image platform %s, not resolving", + path.Join(p.platform.OS, p.platform.Architecture, p.platform.Variant), + path.Join(img.OS, img.Architecture, img.Variant), + ) + } else { + p.config = img.RawJSON() + } } } }) } -func (p *puller) resolve(ctx context.Context) error { - p.resolveOnce.Do(func() { +func (p *puller) resolve(ctx context.Context, g session.Group) error { + _, err := p.g.Do(ctx, "", func(ctx context.Context) (_ interface{}, err error) { resolveProgressDone := oneOffProgress(ctx, "resolve "+p.src.Reference.String()) + defer func() { + resolveProgressDone(err) + }() ref, err := distreference.ParseNormalizedNamed(p.src.Reference.String()) if err != nil { - p.resolveErr = err - resolveProgressDone(err) - return + return nil, err } if p.desc.Digest == "" && p.config == nil { - origRef, desc, err := p.resolver.Resolve(ctx, ref.String()) + origRef, desc, err := p.resolver(g).Resolve(ctx, ref.String()) if err != nil { - p.resolveErr = err - resolveProgressDone(err) - return + return nil, err } p.desc = desc @@ -287,58 +288,90 @@ func (p *puller) resolve(ctx context.Context) error { if p.config == nil && p.desc.MediaType != images.MediaTypeDockerSchema1Manifest { ref, err := distreference.WithDigest(ref, p.desc.Digest) if err != nil { - p.resolveErr = err - resolveProgressDone(err) - return + return nil, err } - _, dt, err := p.is.ResolveImageConfig(ctx, ref.String(), gw.ResolveImageConfigOpt{Platform: &p.platform, ResolveMode: resolveModeToString(p.src.ResolveMode)}) + _, dt, err := p.is.ResolveImageConfig(ctx, ref.String(), llb.ResolveImageConfigOpt{Platform: &p.platform, ResolveMode: resolveModeToString(p.src.ResolveMode)}, p.sm, g) if err != nil { - p.resolveErr = err - resolveProgressDone(err) - return + return nil, err } p.config = dt } - resolveProgressDone(nil) + return nil, nil }) - return p.resolveErr + return err } -func (p *puller) CacheKey(ctx context.Context, index int) (string, bool, error) { +func (p *puller) CacheKey(ctx context.Context, g session.Group, index int) (string, solver.CacheOpts, bool, error) { p.resolveLocal() if p.desc.Digest != "" && index == 0 { - dgst, err := p.mainManifestKey(p.desc.Digest, p.platform) + dgst, err := p.mainManifestKey(p.platform) if err != nil { - return "", false, err + return "", nil, false, err } - return dgst.String(), false, nil + return dgst.String(), nil, false, nil } if p.config != nil { - return cacheKeyFromConfig(p.config).String(), true, nil + k := cacheKeyFromConfig(p.config).String() + if k == "" { + return digest.FromBytes(p.config).String(), nil, true, nil + } + return k, nil, true, nil } - if err := p.resolve(ctx); err != nil { - return "", false, err + if err := p.resolve(ctx, g); err != nil { + return "", nil, false, err } if p.desc.Digest != "" && index == 0 { - dgst, err := p.mainManifestKey(p.desc.Digest, p.platform) + dgst, err := p.mainManifestKey(p.platform) + if err != nil { + return "", nil, false, err + } + return dgst.String(), nil, false, nil + } + + if len(p.config) == 0 && p.desc.MediaType != images.MediaTypeDockerSchema1Manifest { + return "", nil, false, errors.Errorf("invalid empty config file resolved for %s", p.src.Reference.String()) + } + + k := cacheKeyFromConfig(p.config).String() + if k == "" || p.desc.MediaType == images.MediaTypeDockerSchema1Manifest { + dgst, err := p.mainManifestKey(p.platform) if err != nil { - return "", false, err + return "", nil, false, err } - return dgst.String(), false, nil + return dgst.String(), nil, true, nil } - return cacheKeyFromConfig(p.config).String(), true, nil + return k, nil, true, nil } -func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { +func (p *puller) getRef(ctx context.Context, diffIDs []layer.DiffID, opts ...cache.RefOption) (cache.ImmutableRef, error) { + var parent cache.ImmutableRef + if len(diffIDs) > 1 { + var err error + parent, err = p.getRef(ctx, diffIDs[:len(diffIDs)-1], opts...) + if err != nil { + return nil, err + } + defer parent.Release(context.TODO()) + } + return p.is.CacheAccessor.GetByBlob(ctx, ocispec.Descriptor{ + Annotations: map[string]string{ + "containerd.io/uncompressed": diffIDs[len(diffIDs)-1].String(), + }, + }, parent, opts...) +} + +func (p *puller) Snapshot(ctx context.Context, g session.Group) (cache.ImmutableRef, error) { p.resolveLocal() - if err := p.resolve(ctx); err != nil { - return nil, err + if len(p.config) == 0 { + if err := p.resolve(ctx, g); err != nil { + return nil, err + } } if p.config != nil { @@ -347,16 +380,31 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { if len(img.RootFS.DiffIDs) == 0 { return nil, nil } - ref, err := p.is.CacheAccessor.GetFromSnapshotter(ctx, string(img.RootFS.ChainID()), cache.WithDescription(fmt.Sprintf("from local %s", p.ref))) - if err != nil { - return nil, err + l, err := p.is.LayerStore.Get(img.RootFS.ChainID()) + if err == nil { + layer.ReleaseAndLog(p.is.LayerStore, l) + ref, err := p.getRef(ctx, img.RootFS.DiffIDs, cache.WithDescription(fmt.Sprintf("from local %s", p.ref))) + if err != nil { + return nil, err + } + return ref, nil } - return ref, nil } } ongoing := newJobs(p.ref) + ctx, done, err := leaseutil.WithLease(ctx, p.is.LeaseManager, leases.WithExpiration(5*time.Minute), leaseutil.MakeTemporary) + if err != nil { + return nil, err + } + defer func() { + done(context.TODO()) + if p.is.GarbageCollect != nil { + go p.is.GarbageCollect(context.TODO()) + } + }() + pctx, stopProgress := context.WithCancel(ctx) pw, _, ctx := progress.FromContext(ctx) @@ -371,12 +419,16 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { <-progressDone }() - fetcher, err := p.resolver.Fetcher(ctx, p.ref) + fetcher, err := p.resolver(g).Fetcher(ctx, p.ref) if err != nil { stopProgress() return nil, err } + platform := platforms.Only(p.platform) + + var nonLayers []digest.Digest + var ( schema1Converter *schema1.Converter handlers []images.Handler @@ -397,6 +449,7 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest, images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex, images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig: + nonLayers = append(nonLayers, desc.Digest) default: return nil, images.ErrSkipDesc } @@ -406,10 +459,10 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { // Get all the children for a descriptor childrenHandler := images.ChildrenHandler(p.is.ContentStore) - // Set any children labels for that content - childrenHandler = images.SetChildrenLabels(p.is.ContentStore, childrenHandler) // Filter the children by the platform - childrenHandler = images.FilterPlatforms(childrenHandler, platforms.Default()) + childrenHandler = images.FilterPlatforms(childrenHandler, platform) + // Limit manifests pulled to the best match in an index + childrenHandler = images.LimitManifests(childrenHandler, platform, 1) handlers = append(handlers, remotes.FetchHandler(p.is.ContentStore, fetcher), @@ -417,7 +470,7 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { ) } - if err := images.Dispatch(ctx, images.Handlers(handlers...), p.desc); err != nil { + if err := images.Dispatch(ctx, images.Handlers(handlers...), nil, p.desc); err != nil { stopProgress() return nil, err } @@ -430,12 +483,12 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { } } - mfst, err := images.Manifest(ctx, p.is.ContentStore, p.desc, platforms.Default()) + mfst, err := images.Manifest(ctx, p.is.ContentStore, p.desc, platform) if err != nil { return nil, err } - config, err := images.Config(ctx, p.is.ContentStore, p.desc, platforms.Default()) + config, err := images.Config(ctx, p.is.ContentStore, p.desc, platform) if err != nil { return nil, err } @@ -476,7 +529,7 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { tm := time.Now() end = &tm } - pw.Write("extracting "+p.ID, progress.Status{ + _ = pw.Write("extracting "+p.ID, progress.Status{ Action: "extract", Started: &st.st, Completed: end, @@ -493,6 +546,9 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { layers := make([]xfer.DownloadDescriptor, 0, len(mfst.Layers)) for i, desc := range mfst.Layers { + if err := desc.Digest.Validate(); err != nil { + return nil, errors.Wrap(err, "layer digest could not be validated") + } ongoing.add(desc) layers = append(layers, &layerDescriptor{ desc: desc, @@ -505,24 +561,31 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { defer func() { <-progressDone - for _, desc := range mfst.Layers { - p.is.ContentStore.Delete(context.TODO(), desc.Digest) - } }() r := image.NewRootFS() rootFS, release, err := p.is.DownloadManager.Download(ctx, *r, runtime.GOOS, layers, pkgprogress.ChanOutput(pchan)) + stopProgress() if err != nil { return nil, err } - stopProgress() - ref, err := p.is.CacheAccessor.GetFromSnapshotter(ctx, string(rootFS.ChainID()), cache.WithDescription(fmt.Sprintf("pulled from %s", p.ref))) + ref, err := p.getRef(ctx, rootFS.DiffIDs, cache.WithDescription(fmt.Sprintf("pulled from %s", p.ref))) release() if err != nil { return nil, err } + // keep manifest blobs until ref is alive for cache + for _, nl := range nonLayers { + if err := p.is.LeaseManager.AddResource(ctx, leases.Lease{ID: ref.ID()}, leases.Resource{ + ID: nl.String(), + Type: "content", + }); err != nil { + return nil, err + } + } + // TODO: handle windows layers for cross platform builds if p.src.RecordType != "" && cache.GetRecordType(ref) == "" { @@ -537,7 +600,7 @@ func (p *puller) Snapshot(ctx context.Context) (cache.ImmutableRef, error) { // Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) type layerDescriptor struct { - is *imageSource + is *Source fetcher remotes.Fetcher desc ocispec.Descriptor diffID layer.DiffID @@ -577,7 +640,7 @@ func (ld *layerDescriptor) Download(ctx context.Context, progressOutput pkgprogr return nil, 0, err } - return ioutil.NopCloser(content.NewReader(ra)), ld.desc.Size, nil + return io.NopCloser(content.NewReader(ra)), ld.desc.Size, nil } func (ld *layerDescriptor) Close() { @@ -639,7 +702,7 @@ func showProgress(ctx context.Context, ongoing *jobs, cs content.Store, pw progr refKey := remotes.MakeRefKey(ctx, j.Descriptor) if a, ok := actives[refKey]; ok { started := j.started - pw.Write(j.Digest.String(), progress.Status{ + _ = pw.Write(j.Digest.String(), progress.Status{ Action: a.Status, Total: int(a.Total), Current: int(a.Offset), @@ -651,8 +714,8 @@ func showProgress(ctx context.Context, ongoing *jobs, cs content.Store, pw progr if !j.done { info, err := cs.Info(context.TODO(), j.Digest) if err != nil { - if errdefs.IsNotFound(err) { - // pw.Write(j.Digest.String(), progress.Status{ + if containerderrors.IsNotFound(err) { + // _ = pw.Write(j.Digest.String(), progress.Status{ // Action: "waiting", // }) continue @@ -664,7 +727,7 @@ func showProgress(ctx context.Context, ongoing *jobs, cs content.Store, pw progr if done || j.done { started := j.started createdAt := info.CreatedAt - pw.Write(j.Digest.String(), progress.Status{ + _ = pw.Write(j.Digest.String(), progress.Status{ Action: "done", Current: int(info.Size), Total: int(info.Size), @@ -750,13 +813,13 @@ func oneOffProgress(ctx context.Context, id string) func(err error) error { st := progress.Status{ Started: &now, } - pw.Write(id, st) + _ = pw.Write(id, st) return func(err error) error { // TODO: set error on status now := time.Now() st.Completed = &now - pw.Write(id, st) - pw.Close() + _ = pw.Write(id, st) + _ = pw.Close() return err } } @@ -767,10 +830,11 @@ func cacheKeyFromConfig(dt []byte) digest.Digest { var img ocispec.Image err := json.Unmarshal(dt, &img) if err != nil { + logrus.WithError(err).Errorf("failed to unmarshal image config for cache key %v", err) return digest.FromBytes(dt) } - if img.RootFS.Type != "layers" { - return digest.FromBytes(dt) + if img.RootFS.Type != "layers" || len(img.RootFS.DiffIDs) == 0 { + return "" } return identity.ChainID(img.RootFS.DiffIDs) } @@ -788,3 +852,13 @@ func resolveModeToString(rm source.ResolveMode) string { } return "" } + +func platformMatches(img *image.Image, p *ocispec.Platform) bool { + return dimages.OnlyPlatformWithFallback(*p).Match(ocispec.Platform{ + Architecture: img.Architecture, + OS: img.OS, + OSVersion: img.OSVersion, + OSFeatures: img.OSFeatures, + Variant: img.Variant, + }) +} diff --git a/builder/builder-next/adapters/localinlinecache/inlinecache.go b/builder/builder-next/adapters/localinlinecache/inlinecache.go new file mode 100644 index 0000000000000..c5e19ce2d5d42 --- /dev/null +++ b/builder/builder-next/adapters/localinlinecache/inlinecache.go @@ -0,0 +1,168 @@ +package localinlinecache + +import ( + "context" + "encoding/json" + "time" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/remotes/docker" + distreference "github.com/docker/distribution/reference" + imagestore "github.com/docker/docker/image" + "github.com/docker/docker/reference" + "github.com/moby/buildkit/cache/remotecache" + registryremotecache "github.com/moby/buildkit/cache/remotecache/registry" + v1 "github.com/moby/buildkit/cache/remotecache/v1" + "github.com/moby/buildkit/session" + "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/worker" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +func init() { + // See https://github.com/moby/buildkit/pull/1993. + v1.EmptyLayerRemovalSupported = false +} + +// ResolveCacheImporterFunc returns a resolver function for local inline cache +func ResolveCacheImporterFunc(sm *session.Manager, resolverFunc docker.RegistryHosts, cs content.Store, rs reference.Store, is imagestore.Store) remotecache.ResolveCacheImporterFunc { + + upstream := registryremotecache.ResolveCacheImporterFunc(sm, cs, resolverFunc) + + return func(ctx context.Context, group session.Group, attrs map[string]string) (remotecache.Importer, specs.Descriptor, error) { + if dt, err := tryImportLocal(rs, is, attrs["ref"]); err == nil { + return newLocalImporter(dt), specs.Descriptor{}, nil + } + return upstream(ctx, group, attrs) + } +} + +func tryImportLocal(rs reference.Store, is imagestore.Store, refStr string) ([]byte, error) { + ref, err := distreference.ParseNormalizedNamed(refStr) + if err != nil { + return nil, err + } + dgst, err := rs.Get(ref) + if err != nil { + return nil, err + } + img, err := is.Get(imagestore.ID(dgst)) + if err != nil { + return nil, err + } + + return img.RawJSON(), nil +} + +func newLocalImporter(dt []byte) remotecache.Importer { + return &localImporter{dt: dt} +} + +type localImporter struct { + dt []byte +} + +func (li *localImporter) Resolve(ctx context.Context, _ specs.Descriptor, id string, w worker.Worker) (solver.CacheManager, error) { + cc := v1.NewCacheChains() + if err := li.importInlineCache(ctx, li.dt, cc); err != nil { + return nil, err + } + + keysStorage, resultStorage, err := v1.NewCacheKeyStorage(cc, w) + if err != nil { + return nil, err + } + return solver.NewCacheManager(id, keysStorage, resultStorage), nil +} + +func (li *localImporter) importInlineCache(ctx context.Context, dt []byte, cc solver.CacheExporterTarget) error { + var img image + + if err := json.Unmarshal(dt, &img); err != nil { + return err + } + + if img.Cache == nil { + return nil + } + + var config v1.CacheConfig + if err := json.Unmarshal(img.Cache, &config.Records); err != nil { + return err + } + + createdDates, createdMsg, err := parseCreatedLayerInfo(img) + if err != nil { + return err + } + + layers := v1.DescriptorProvider{} + for i, diffID := range img.Rootfs.DiffIDs { + dgst := digest.Digest(diffID.String()) + desc := specs.Descriptor{ + Digest: dgst, + Size: -1, + MediaType: images.MediaTypeDockerSchema2Layer, + Annotations: map[string]string{}, + } + if createdAt := createdDates[i]; createdAt != "" { + desc.Annotations["buildkit/createdat"] = createdAt + } + if createdBy := createdMsg[i]; createdBy != "" { + desc.Annotations["buildkit/description"] = createdBy + } + desc.Annotations["containerd.io/uncompressed"] = img.Rootfs.DiffIDs[i].String() + layers[dgst] = v1.DescriptorProviderPair{ + Descriptor: desc, + Provider: &emptyProvider{}, + } + config.Layers = append(config.Layers, v1.CacheLayer{ + Blob: dgst, + ParentIndex: i - 1, + }) + } + + return v1.ParseConfig(config, layers, cc) +} + +type image struct { + Rootfs struct { + DiffIDs []digest.Digest `json:"diff_ids"` + } `json:"rootfs"` + Cache []byte `json:"moby.buildkit.cache.v0"` + History []struct { + Created *time.Time `json:"created,omitempty"` + CreatedBy string `json:"created_by,omitempty"` + EmptyLayer bool `json:"empty_layer,omitempty"` + } `json:"history,omitempty"` +} + +func parseCreatedLayerInfo(img image) ([]string, []string, error) { + dates := make([]string, 0, len(img.Rootfs.DiffIDs)) + createdBy := make([]string, 0, len(img.Rootfs.DiffIDs)) + for _, h := range img.History { + if !h.EmptyLayer { + str := "" + if h.Created != nil { + dt, err := h.Created.MarshalText() + if err != nil { + return nil, nil, err + } + str = string(dt) + } + dates = append(dates, str) + createdBy = append(createdBy, h.CreatedBy) + } + } + return dates, createdBy, nil +} + +type emptyProvider struct { +} + +func (p *emptyProvider) ReaderAt(ctx context.Context, dec specs.Descriptor) (content.ReaderAt, error) { + return nil, errors.Errorf("ReaderAt not implemented for empty provider") +} diff --git a/builder/builder-next/adapters/snapshot/layer.go b/builder/builder-next/adapters/snapshot/layer.go index ffde5eec51108..ed13def16fdee 100644 --- a/builder/builder-next/adapters/snapshot/layer.go +++ b/builder/builder-next/adapters/snapshot/layer.go @@ -12,12 +12,22 @@ import ( "golang.org/x/sync/errgroup" ) -func (s *snapshotter) EnsureLayer(ctx context.Context, key string) ([]layer.DiffID, error) { +func (s *snapshotter) GetDiffIDs(ctx context.Context, key string) ([]layer.DiffID, error) { if l, err := s.getLayer(key, true); err != nil { return nil, err } else if l != nil { return getDiffChain(l), nil } + return nil, nil +} + +func (s *snapshotter) EnsureLayer(ctx context.Context, key string) ([]layer.DiffID, error) { + diffIDs, err := s.GetDiffIDs(ctx, key) + if err != nil { + return nil, err + } else if diffIDs != nil { + return diffIDs, nil + } id, committed := s.getGraphDriverID(key) if !committed { diff --git a/builder/builder-next/adapters/snapshot/leasemanager.go b/builder/builder-next/adapters/snapshot/leasemanager.go new file mode 100644 index 0000000000000..713af013a17d7 --- /dev/null +++ b/builder/builder-next/adapters/snapshot/leasemanager.go @@ -0,0 +1,133 @@ +package snapshot + +import ( + "context" + "sync" + + "github.com/containerd/containerd/leases" + "github.com/sirupsen/logrus" + bolt "go.etcd.io/bbolt" +) + +type sLM struct { + manager leases.Manager + s *snapshotter + + mu sync.Mutex + byLease map[string]map[string]struct{} + bySnapshot map[string]map[string]struct{} +} + +func newLeaseManager(s *snapshotter, lm leases.Manager) *sLM { + return &sLM{ + s: s, + manager: lm, + + byLease: map[string]map[string]struct{}{}, + bySnapshot: map[string]map[string]struct{}{}, + } +} + +func (l *sLM) Create(ctx context.Context, opts ...leases.Opt) (leases.Lease, error) { + return l.manager.Create(ctx, opts...) +} + +func (l *sLM) Delete(ctx context.Context, lease leases.Lease, opts ...leases.DeleteOpt) error { + if err := l.manager.Delete(ctx, lease, opts...); err != nil { + return err + } + l.mu.Lock() + if snaps, ok := l.byLease[lease.ID]; ok { + for sID := range snaps { + l.delRef(lease.ID, sID) + } + } + l.mu.Unlock() + return nil +} + +func (l *sLM) List(ctx context.Context, filters ...string) ([]leases.Lease, error) { + return l.manager.List(ctx, filters...) +} + +func (l *sLM) AddResource(ctx context.Context, lease leases.Lease, resource leases.Resource) error { + if err := l.manager.AddResource(ctx, lease, resource); err != nil { + return err + } + if resource.Type == "snapshots/default" { + l.mu.Lock() + l.addRef(lease.ID, resource.ID) + l.mu.Unlock() + } + return nil +} + +func (l *sLM) DeleteResource(ctx context.Context, lease leases.Lease, resource leases.Resource) error { + if err := l.manager.DeleteResource(ctx, lease, resource); err != nil { + return err + } + if resource.Type == "snapshots/default" { + l.mu.Lock() + l.delRef(lease.ID, resource.ID) + l.mu.Unlock() + } + return nil +} + +func (l *sLM) ListResources(ctx context.Context, lease leases.Lease) ([]leases.Resource, error) { + return l.manager.ListResources(ctx, lease) +} + +func (l *sLM) addRef(lID, sID string) { + load := false + snapshots, ok := l.byLease[lID] + if !ok { + snapshots = map[string]struct{}{} + l.byLease[lID] = snapshots + } + if _, ok := snapshots[sID]; !ok { + snapshots[sID] = struct{}{} + } + leases, ok := l.bySnapshot[sID] + if !ok { + leases = map[string]struct{}{} + l.byLease[sID] = leases + load = true + } + if _, ok := leases[lID]; !ok { + leases[lID] = struct{}{} + } + + if load { + l.s.getLayer(sID, true) + if _, ok := l.s.chainID(sID); ok { + l.s.db.Update(func(tx *bolt.Tx) error { + b, err := tx.CreateBucketIfNotExists([]byte(lID)) + if err != nil { + return err + } + return b.Put(keyChainID, []byte(sID)) + }) + } + } +} + +func (l *sLM) delRef(lID, sID string) { + snapshots, ok := l.byLease[lID] + if !ok { + delete(snapshots, sID) + if len(snapshots) == 0 { + delete(l.byLease, lID) + } + } + leases, ok := l.bySnapshot[sID] + if !ok { + delete(leases, lID) + if len(leases) == 0 { + delete(l.bySnapshot, sID) + if err := l.s.remove(context.TODO(), sID); err != nil { + logrus.Warnf("failed to remove snapshot %v", sID) + } + } + } +} diff --git a/builder/builder-next/adapters/snapshot/snapshot.go b/builder/builder-next/adapters/snapshot/snapshot.go index e5e6942653d70..19ee488b8e92f 100644 --- a/builder/builder-next/adapters/snapshot/snapshot.go +++ b/builder/builder-next/adapters/snapshot/snapshot.go @@ -7,10 +7,12 @@ import ( "strings" "sync" + "github.com/containerd/containerd/leases" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/snapshots" "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/layer" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/identity" "github.com/moby/buildkit/snapshot" digest "github.com/opencontainers/go-digest" @@ -20,14 +22,16 @@ import ( var keyParent = []byte("parent") var keyCommitted = []byte("committed") +var keyIsCommitted = []byte("iscommitted") var keyChainID = []byte("chainid") var keySize = []byte("size") // Opt defines options for creating the snapshotter type Opt struct { - GraphDriver graphdriver.Driver - LayerStore layer.Store - Root string + GraphDriver graphdriver.Driver + LayerStore layer.Store + Root string + IdentityMapping *idtools.IdentityMapping } type graphIDRegistrar interface { @@ -49,19 +53,17 @@ type snapshotter struct { reg graphIDRegistrar } -var _ snapshot.SnapshotterBase = &snapshotter{} - // NewSnapshotter creates a new snapshotter -func NewSnapshotter(opt Opt) (snapshot.SnapshotterBase, error) { +func NewSnapshotter(opt Opt, prevLM leases.Manager) (snapshot.Snapshotter, leases.Manager, error) { dbPath := filepath.Join(opt.Root, "snapshots.db") db, err := bolt.Open(dbPath, 0600, nil) if err != nil { - return nil, errors.Wrapf(err, "failed to open database file %s", dbPath) + return nil, nil, errors.Wrapf(err, "failed to open database file %s", dbPath) } reg, ok := opt.LayerStore.(graphIDRegistrar) if !ok { - return nil, errors.Errorf("layerstore doesn't support graphID registration") + return nil, nil, errors.Errorf("layerstore doesn't support graphID registration") } s := &snapshotter{ @@ -70,18 +72,45 @@ func NewSnapshotter(opt Opt) (snapshot.SnapshotterBase, error) { refs: map[string]layer.Layer{}, reg: reg, } - return s, nil + + lm := newLeaseManager(s, prevLM) + + ll, err := lm.List(context.TODO()) + if err != nil { + return nil, nil, err + } + for _, l := range ll { + rr, err := lm.ListResources(context.TODO(), l) + if err != nil { + return nil, nil, err + } + for _, r := range rr { + if r.Type == "snapshots/default" { + lm.addRef(l.ID, r.ID) + } + } + } + + return s, lm, nil +} + +func (s *snapshotter) Name() string { + return "default" +} + +func (s *snapshotter) IdentityMapping() *idtools.IdentityMapping { + return s.opt.IdentityMapping } func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) error { origParent := parent if parent != "" { if l, err := s.getLayer(parent, false); err != nil { - return err + return errors.Wrapf(err, "failed to get parent layer %s", parent) } else if l != nil { parent, err = getGraphID(l) if err != nil { - return err + return errors.Wrapf(err, "failed to get parent graphid %s", l.ChainID()) } } else { parent, _ = s.getGraphDriverID(parent) @@ -136,23 +165,24 @@ func (s *snapshotter) getLayer(key string, withCommitted bool) (layer.Layer, err return nil }); err != nil { s.mu.Unlock() - return nil, err + return nil, errors.WithStack(err) } + s.mu.Unlock() if id == "" { - s.mu.Unlock() return nil, nil } + return s.getLayer(string(id), withCommitted) } var err error l, err = s.opt.LayerStore.Get(id) if err != nil { s.mu.Unlock() - return nil, err + return nil, errors.WithStack(err) } s.refs[key] = l if err := s.db.Update(func(tx *bolt.Tx) error { _, err := tx.CreateBucketIfNotExists([]byte(key)) - return err + return errors.WithStack(err) }); err != nil { s.mu.Unlock() return nil, err @@ -244,24 +274,24 @@ func (s *snapshotter) Mounts(ctx context.Context, key string) (snapshot.Mountabl id := identity.NewID() var rwlayer layer.RWLayer return &mountable{ - acquire: func() ([]mount.Mount, error) { + idmap: s.opt.IdentityMapping, + acquire: func() ([]mount.Mount, func() error, error) { rwlayer, err = s.opt.LayerStore.CreateRWLayer(id, l.ChainID(), nil) if err != nil { - return nil, err + return nil, nil, err } rootfs, err := rwlayer.Mount("") if err != nil { - return nil, err + return nil, nil, err } return []mount.Mount{{ - Source: rootfs.Path(), - Type: "bind", - Options: []string{"rbind"}, - }}, nil - }, - release: func() error { - _, err := s.opt.LayerStore.ReleaseRWLayer(rwlayer) - return err + Source: rootfs.Path(), + Type: "bind", + Options: []string{"rbind"}, + }}, func() error { + _, err := s.opt.LayerStore.ReleaseRWLayer(rwlayer) + return err + }, nil }, }, nil } @@ -269,24 +299,28 @@ func (s *snapshotter) Mounts(ctx context.Context, key string) (snapshot.Mountabl id, _ := s.getGraphDriverID(key) return &mountable{ - acquire: func() ([]mount.Mount, error) { + idmap: s.opt.IdentityMapping, + acquire: func() ([]mount.Mount, func() error, error) { rootfs, err := s.opt.GraphDriver.Get(id, "") if err != nil { - return nil, err + return nil, nil, err } return []mount.Mount{{ - Source: rootfs.Path(), - Type: "bind", - Options: []string{"rbind"}, - }}, nil - }, - release: func() error { - return s.opt.GraphDriver.Put(id) + Source: rootfs.Path(), + Type: "bind", + Options: []string{"rbind"}, + }}, func() error { + return s.opt.GraphDriver.Put(id) + }, nil }, }, nil } func (s *snapshotter) Remove(ctx context.Context, key string) error { + return errors.Errorf("calling snapshot.remove is forbidden") +} + +func (s *snapshotter) remove(ctx context.Context, key string) error { l, err := s.getLayer(key, true) if err != nil { return err @@ -295,8 +329,17 @@ func (s *snapshotter) Remove(ctx context.Context, key string) error { id, _ := s.getGraphDriverID(key) var found bool + var alreadyCommitted bool if err := s.db.Update(func(tx *bolt.Tx) error { - found = tx.Bucket([]byte(key)) != nil + b := tx.Bucket([]byte(key)) + found = b != nil + + if b != nil { + if b.Get(keyIsCommitted) != nil { + alreadyCommitted = true + return nil + } + } if found { tx.DeleteBucket([]byte(key)) if id != key { @@ -308,6 +351,10 @@ func (s *snapshotter) Remove(ctx context.Context, key string) error { return err } + if alreadyCommitted { + return nil + } + if l != nil { s.mu.Lock() delete(s.refs, key) @@ -329,7 +376,14 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap if err != nil { return err } - return b.Put(keyCommitted, []byte(key)) + if err := b.Put(keyCommitted, []byte(key)); err != nil { + return err + } + b, err = tx.CreateBucketIfNotExists([]byte(key)) + if err != nil { + return err + } + return b.Put(keyIsCommitted, []byte{}) }) } @@ -337,8 +391,8 @@ func (s *snapshotter) View(ctx context.Context, key, parent string, opts ...snap return s.Mounts(ctx, parent) } -func (s *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error { - return errors.Errorf("not-implemented") +func (s *snapshotter) Walk(context.Context, snapshots.WalkFunc, ...string) error { + return nil } func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) { @@ -426,36 +480,55 @@ func (s *snapshotter) Close() error { } type mountable struct { - mu sync.Mutex - mounts []mount.Mount - acquire func() ([]mount.Mount, error) - release func() error + mu sync.Mutex + mounts []mount.Mount + acquire func() ([]mount.Mount, func() error, error) + release func() error + refCount int + idmap *idtools.IdentityMapping } -func (m *mountable) Mount() ([]mount.Mount, error) { +func (m *mountable) Mount() ([]mount.Mount, func() error, error) { m.mu.Lock() defer m.mu.Unlock() if m.mounts != nil { - return m.mounts, nil + m.refCount++ + return m.mounts, m.releaseMount, nil } - mounts, err := m.acquire() + mounts, release, err := m.acquire() if err != nil { - return nil, err + return nil, nil, err } m.mounts = mounts + m.release = release + m.refCount = 1 - return m.mounts, nil + return m.mounts, m.releaseMount, nil } -func (m *mountable) Release() error { +func (m *mountable) releaseMount() error { m.mu.Lock() defer m.mu.Unlock() + + if m.refCount > 1 { + m.refCount-- + return nil + } + + m.refCount = 0 if m.release == nil { return nil } m.mounts = nil + defer func() { + m.release = nil + }() return m.release() } + +func (m *mountable) IdentityMapping() *idtools.IdentityMapping { + return m.idmap +} diff --git a/builder/builder-next/builder.go b/builder/builder-next/builder.go index a51d8ec52c79b..5ee9d8e30d20f 100644 --- a/builder/builder-next/builder.go +++ b/builder/builder-next/builder.go @@ -2,37 +2,52 @@ package buildkit import ( "context" + "fmt" "io" "net" + "strconv" "strings" "sync" "time" - "github.com/containerd/containerd/content" "github.com/containerd/containerd/platforms" + "github.com/containerd/containerd/remotes/docker" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/builder" "github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/images" + "github.com/docker/docker/libnetwork" + "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/streamformatter" - "github.com/docker/docker/pkg/system" - "github.com/docker/libnetwork" controlapi "github.com/moby/buildkit/api/services/control" "github.com/moby/buildkit/client" "github.com/moby/buildkit/control" "github.com/moby/buildkit/identity" "github.com/moby/buildkit/session" - "github.com/moby/buildkit/solver/llbsolver" "github.com/moby/buildkit/util/entitlements" - "github.com/moby/buildkit/util/resolver" "github.com/moby/buildkit/util/tracing" "github.com/pkg/errors" "golang.org/x/sync/errgroup" + "google.golang.org/grpc" grpcmetadata "google.golang.org/grpc/metadata" ) -var errMultipleFilterValues = errors.New("filters expect only one value") +type errMultipleFilterValues struct{} + +func (errMultipleFilterValues) Error() string { return "filters expect only one value" } + +func (errMultipleFilterValues) InvalidParameter() {} + +type errConflictFilter struct { + a, b string +} + +func (e errConflictFilter) Error() string { + return fmt.Sprintf("conflicting filters: %q and %q", e.a, e.b) +} + +func (errConflictFilter) InvalidParameter() {} var cacheFields = map[string]bool{ "id": true, @@ -47,10 +62,6 @@ var cacheFields = map[string]bool{ "immutable": false, } -func init() { - llbsolver.AllowNetworkHostUnstable = true -} - // Opt is option struct required for creating the builder type Opt struct { SessionManager *session.Manager @@ -58,8 +69,12 @@ type Opt struct { Dist images.DistributionServices NetworkController libnetwork.NetworkController DefaultCgroupParent string - ResolverOpt resolver.ResolveOptionsFunc + RegistryHosts docker.RegistryHosts BuilderConfig config.BuilderConfig + Rootless bool + IdentityMapping *idtools.IdentityMapping + DNSConfig config.DNSConfig + ApparmorProfile string } // Builder can build using BuildKit backend @@ -75,6 +90,10 @@ type Builder struct { func New(opt Opt) (*Builder, error) { reqHandler := newReqBodyHandler(tracing.DefaultTransport) + if opt.IdentityMapping != nil && opt.IdentityMapping.Empty() { + opt.IdentityMapping = nil + } + c, err := newController(reqHandler, opt) if err != nil { return nil, err @@ -87,6 +106,11 @@ func New(opt Opt) (*Builder, error) { return b, nil } +// RegisterGRPC registers controller to the grpc server. +func (b *Builder) RegisterGRPC(s *grpc.Server) { + b.controller.Register(s) +} + // Cancel cancels a build using ID func (b *Builder) Cancel(ctx context.Context, id string) error { b.mu.Lock() @@ -130,6 +154,9 @@ func (b *Builder) Prune(ctx context.Context, opts types.BuildCachePruneOptions) validFilters := make(map[string]bool, 1+len(cacheFields)) validFilters["unused-for"] = true + validFilters["until"] = true + validFilters["label"] = true // TODO(tiborvass): handle label + validFilters["label!"] = true // TODO(tiborvass): handle label! for k, v := range cacheFields { validFilters[k] = v } @@ -213,7 +240,9 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder. } defer func() { + b.mu.Lock() delete(b.jobs, buildID) + b.mu.Unlock() }() } @@ -269,13 +298,10 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder. if opt.Options.Platform != "" { // same as in newBuilder in builder/dockerfile.builder.go // TODO: remove once opt.Options.Platform is of type specs.Platform - sp, err := platforms.Parse(opt.Options.Platform) + _, err := platforms.Parse(opt.Options.Platform) if err != nil { return nil, err } - if err := system.ValidatePlatform(sp); err != nil { - return nil, err - } frontendAttrs["platform"] = opt.Options.Platform } @@ -293,19 +319,45 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder. } frontendAttrs["add-hosts"] = extraHosts + exporterName := "" exporterAttrs := map[string]string{} - if len(opt.Options.Tags) > 0 { - exporterAttrs["name"] = strings.Join(opt.Options.Tags, ",") + if len(opt.Options.Outputs) > 1 { + return nil, errors.Errorf("multiple outputs not supported") + } else if len(opt.Options.Outputs) == 0 { + exporterName = "moby" + } else { + // cacheonly is a special type for triggering skipping all exporters + if opt.Options.Outputs[0].Type != "cacheonly" { + exporterName = opt.Options.Outputs[0].Type + exporterAttrs = opt.Options.Outputs[0].Attrs + } + } + + if exporterName == "moby" { + if len(opt.Options.Tags) > 0 { + exporterAttrs["name"] = strings.Join(opt.Options.Tags, ",") + } + } + + cache := controlapi.CacheOptions{} + + if inlineCache := opt.Options.BuildArgs["BUILDKIT_INLINE_CACHE"]; inlineCache != nil { + if b, err := strconv.ParseBool(*inlineCache); err == nil && b { + cache.Exports = append(cache.Exports, &controlapi.CacheOptionsEntry{ + Type: "inline", + }) + } } req := &controlapi.SolveRequest{ Ref: id, - Exporter: "moby", + Exporter: exporterName, ExporterAttrs: exporterAttrs, Frontend: "dockerfile.v0", FrontendAttrs: frontendAttrs, Session: opt.Options.SessionID, + Cache: cache, } if opt.Options.NetworkMode == "host" { @@ -321,6 +373,9 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder. if err != nil { return err } + if exporterName != "moby" { + return nil + } id, ok := resp.ExporterResponse["containerimage.digest"] if !ok { return errors.Errorf("missing image id") @@ -411,14 +466,6 @@ func (sp *pruneProxy) SendMsg(m interface{}) error { return nil } -type contentStoreNoLabels struct { - content.Store -} - -func (c *contentStoreNoLabels) Update(ctx context.Context, info content.Info, fieldpaths ...string) (content.Info, error) { - return content.Info{}, nil -} - type wrapRC struct { io.ReadCloser once sync.Once @@ -502,6 +549,7 @@ func toBuildkitExtraHosts(inp []string) (string, error) { hosts := make([]string, 0, len(inp)) for _, h := range inp { parts := strings.Split(h, ":") + if len(parts) != 2 || parts[0] == "" || net.ParseIP(parts[1]) == nil { return "", errors.Errorf("invalid host %s", h) } @@ -511,39 +559,54 @@ func toBuildkitExtraHosts(inp []string) (string, error) { } func toBuildkitPruneInfo(opts types.BuildCachePruneOptions) (client.PruneInfo, error) { - var unusedFor time.Duration - unusedForValues := opts.Filters.Get("unused-for") + var until time.Duration + untilValues := opts.Filters.Get("until") // canonical + unusedForValues := opts.Filters.Get("unused-for") // deprecated synonym for "until" filter - switch len(unusedForValues) { - case 0: + if len(untilValues) > 0 && len(unusedForValues) > 0 { + return client.PruneInfo{}, errConflictFilter{"until", "unused-for"} + } + filterKey := "until" + if len(unusedForValues) > 0 { + filterKey = "unused-for" + } + untilValues = append(untilValues, unusedForValues...) + switch len(untilValues) { + case 0: + // nothing to do case 1: var err error - unusedFor, err = time.ParseDuration(unusedForValues[0]) + until, err = time.ParseDuration(untilValues[0]) if err != nil { - return client.PruneInfo{}, errors.Wrap(err, "unused-for filter expects a duration (e.g., '24h')") + return client.PruneInfo{}, errors.Wrapf(err, "%q filter expects a duration (e.g., '24h')", filterKey) } - default: - return client.PruneInfo{}, errMultipleFilterValues + return client.PruneInfo{}, errMultipleFilterValues{} } bkFilter := make([]string, 0, opts.Filters.Len()) for cacheField := range cacheFields { - values := opts.Filters.Get(cacheField) - switch len(values) { - case 0: - bkFilter = append(bkFilter, cacheField) - case 1: - bkFilter = append(bkFilter, cacheField+"=="+values[0]) - default: - return client.PruneInfo{}, errMultipleFilterValues + if opts.Filters.Contains(cacheField) { + values := opts.Filters.Get(cacheField) + switch len(values) { + case 0: + bkFilter = append(bkFilter, cacheField) + case 1: + if cacheField == "id" { + bkFilter = append(bkFilter, cacheField+"~="+values[0]) + } else { + bkFilter = append(bkFilter, cacheField+"=="+values[0]) + } + default: + return client.PruneInfo{}, errMultipleFilterValues{} + } } } return client.PruneInfo{ All: opts.All, - KeepDuration: unusedFor, + KeepDuration: until, KeepBytes: opts.KeepStorage, - Filter: bkFilter, + Filter: []string{strings.Join(bkFilter, ",")}, }, nil } diff --git a/builder/builder-next/controller.go b/builder/builder-next/controller.go index 5aafba2e4a525..fd086ca90e562 100644 --- a/builder/builder-next/controller.go +++ b/builder/builder-next/controller.go @@ -1,13 +1,19 @@ package buildkit import ( + "context" "net/http" "os" "path/filepath" "github.com/containerd/containerd/content/local" + ctdmetadata "github.com/containerd/containerd/metadata" + "github.com/containerd/containerd/platforms" + "github.com/containerd/containerd/snapshots" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/builder/builder-next/adapters/containerimage" + "github.com/docker/docker/builder/builder-next/adapters/localinlinecache" "github.com/docker/docker/builder/builder-next/adapters/snapshot" containerimageexp "github.com/docker/docker/builder/builder-next/exporter" "github.com/docker/docker/builder/builder-next/imagerefchecker" @@ -17,22 +23,28 @@ import ( units "github.com/docker/go-units" "github.com/moby/buildkit/cache" "github.com/moby/buildkit/cache/metadata" - registryremotecache "github.com/moby/buildkit/cache/remotecache/registry" + "github.com/moby/buildkit/cache/remotecache" + inlineremotecache "github.com/moby/buildkit/cache/remotecache/inline" + localremotecache "github.com/moby/buildkit/cache/remotecache/local" "github.com/moby/buildkit/client" "github.com/moby/buildkit/control" - "github.com/moby/buildkit/exporter" "github.com/moby/buildkit/frontend" dockerfile "github.com/moby/buildkit/frontend/dockerfile/builder" "github.com/moby/buildkit/frontend/gateway" "github.com/moby/buildkit/frontend/gateway/forwarder" - "github.com/moby/buildkit/snapshot/blobmapping" + containerdsnapshot "github.com/moby/buildkit/snapshot/containerd" "github.com/moby/buildkit/solver/bboltcachestorage" + "github.com/moby/buildkit/util/archutil" + "github.com/moby/buildkit/util/entitlements" + "github.com/moby/buildkit/util/leaseutil" "github.com/moby/buildkit/worker" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" + bolt "go.etcd.io/bbolt" ) func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) { - if err := os.MkdirAll(opt.Root, 0700); err != nil { + if err := os.MkdirAll(opt.Root, 0711); err != nil { return nil, err } @@ -48,33 +60,42 @@ func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) { return nil, errors.Errorf("could not access graphdriver") } - sbase, err := snapshot.NewSnapshotter(snapshot.Opt{ - GraphDriver: driver, - LayerStore: dist.LayerStore, - Root: root, - }) + store, err := local.NewStore(filepath.Join(root, "content")) if err != nil { return nil, err } - store, err := local.NewStore(filepath.Join(root, "content")) + db, err := bolt.Open(filepath.Join(root, "containerdmeta.db"), 0644, nil) if err != nil { - return nil, err + return nil, errors.WithStack(err) } - store = &contentStoreNoLabels{store} - md, err := metadata.NewStore(filepath.Join(root, "metadata.db")) + mdb := ctdmetadata.NewDB(db, store, map[string]snapshots.Snapshotter{}) + + store = containerdsnapshot.NewContentStore(mdb.ContentStore(), "buildkit") + + lm := leaseutil.WithNamespace(ctdmetadata.NewLeaseManager(mdb), "buildkit") + + snapshotter, lm, err := snapshot.NewSnapshotter(snapshot.Opt{ + GraphDriver: driver, + LayerStore: dist.LayerStore, + Root: root, + IdentityMapping: opt.IdentityMapping, + }, lm) if err != nil { return nil, err } - snapshotter := blobmapping.NewSnapshotter(blobmapping.Opt{ - Content: store, - Snapshotter: sbase, - MetadataStore: md, - }) + if err := cache.MigrateV2(context.Background(), filepath.Join(root, "metadata.db"), filepath.Join(root, "metadata_v2.db"), store, snapshotter, lm); err != nil { + return nil, err + } - layerGetter, ok := sbase.(imagerefchecker.LayerGetter) + md, err := metadata.NewStore(filepath.Join(root, "metadata_v2.db")) + if err != nil { + return nil, err + } + + layerGetter, ok := snapshotter.(imagerefchecker.LayerGetter) if !ok { return nil, errors.Errorf("snapshotter does not implement layergetter") } @@ -88,31 +109,38 @@ func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) { Snapshotter: snapshotter, MetadataStore: md, PruneRefChecker: refChecker, + LeaseManager: lm, + ContentStore: store, + GarbageCollect: mdb.GarbageCollect, }) if err != nil { return nil, err } src, err := containerimage.NewSource(containerimage.SourceOpt{ - SessionManager: opt.SessionManager, CacheAccessor: cm, ContentStore: store, DownloadManager: dist.DownloadManager, MetadataStore: dist.V2MetadataService, ImageStore: dist.ImageStore, ReferenceStore: dist.ReferenceStore, - ResolverOpt: opt.ResolverOpt, + RegistryHosts: opt.RegistryHosts, + LayerStore: dist.LayerStore, + LeaseManager: lm, + GarbageCollect: mdb.GarbageCollect, }) if err != nil { return nil, err } - exec, err := newExecutor(root, opt.DefaultCgroupParent, opt.NetworkController) + dns := getDNSConfig(opt.DNSConfig) + + exec, err := newExecutor(root, opt.DefaultCgroupParent, opt.NetworkController, dns, opt.Rootless, opt.IdentityMapping, opt.ApparmorProfile) if err != nil { return nil, err } - differ, ok := sbase.(containerimageexp.Differ) + differ, ok := snapshotter.(containerimageexp.Differ) if !ok { return nil, errors.Errorf("snapshotter doesn't support differ") } @@ -136,9 +164,26 @@ func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) { return nil, errors.Wrap(err, "could not get builder GC policy") } + layers, ok := snapshotter.(mobyworker.LayerAccess) + if !ok { + return nil, errors.Errorf("snapshotter doesn't support differ") + } + + p, err := parsePlatforms(archutil.SupportedPlatforms(true)) + if err != nil { + return nil, err + } + + leases, err := lm.List(context.TODO(), "labels.\"buildkit/lease.temporary\"") + if err != nil { + return nil, err + } + for _, l := range leases { + lm.Delete(context.TODO(), l) + } + wopt := mobyworker.Opt{ ID: "moby", - SessionManager: opt.SessionManager, MetadataStore: md, ContentStore: store, CacheManager: cm, @@ -148,10 +193,10 @@ func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) { ImageSource: src, DownloadManager: dist.DownloadManager, V2MetadataService: dist.V2MetadataService, - Exporters: map[string]exporter.Exporter{ - "moby": exp, - }, - Transport: rt, + Exporter: exp, + Transport: rt, + Layers: layers, + Platforms: p, } wc := &worker.Controller{} @@ -167,12 +212,18 @@ func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) { } return control.NewController(control.Opt{ - SessionManager: opt.SessionManager, - WorkerController: wc, - Frontends: frontends, - CacheKeyStorage: cacheStorage, - ResolveCacheImporterFunc: registryremotecache.ResolveCacheImporterFunc(opt.SessionManager, opt.ResolverOpt), - // TODO: set ResolveCacheExporterFunc for exporting cache + SessionManager: opt.SessionManager, + WorkerController: wc, + Frontends: frontends, + CacheKeyStorage: cacheStorage, + ResolveCacheImporterFuncs: map[string]remotecache.ResolveCacheImporterFunc{ + "registry": localinlinecache.ResolveCacheImporterFunc(opt.SessionManager, opt.RegistryHosts, store, dist.ReferenceStore, dist.ImageStore), + "local": localremotecache.ResolveCacheImporterFunc(opt.SessionManager), + }, + ResolveCacheExporterFuncs: map[string]remotecache.ResolveCacheExporterFunc{ + "inline": inlineremotecache.ResolveCacheExporterFunc(), + }, + Entitlements: getEntitlements(opt.BuilderConfig), }) } @@ -206,7 +257,7 @@ func getGCPolicy(conf config.BuilderConfig, root string) ([]client.PruneInfo, er gcPolicy[i], err = toBuildkitPruneInfo(types.BuildCachePruneOptions{ All: p.All, KeepStorage: b, - Filters: p.Filter, + Filters: filters.Args(p.Filter), }) if err != nil { return nil, err @@ -216,3 +267,27 @@ func getGCPolicy(conf config.BuilderConfig, root string) ([]client.PruneInfo, er } return gcPolicy, nil } + +func parsePlatforms(platformsStr []string) ([]specs.Platform, error) { + out := make([]specs.Platform, 0, len(platformsStr)) + for _, s := range platformsStr { + p, err := platforms.Parse(s) + if err != nil { + return nil, err + } + out = append(out, platforms.Normalize(p)) + } + return out, nil +} + +func getEntitlements(conf config.BuilderConfig) []string { + var ents []string + // Incase of no config settings, NetworkHost should be enabled & SecurityInsecure must be disabled. + if conf.Entitlements.NetworkHost == nil || *conf.Entitlements.NetworkHost { + ents = append(ents, string(entitlements.EntitlementNetworkHost)) + } + if conf.Entitlements.SecurityInsecure != nil && *conf.Entitlements.SecurityInsecure { + ents = append(ents, string(entitlements.EntitlementSecurityInsecure)) + } + return ents +} diff --git a/builder/builder-next/executor_unix.go b/builder/builder-next/executor_unix.go index b3ea33c05c71f..468f13052394c 100644 --- a/builder/builder-next/executor_unix.go +++ b/builder/builder-next/executor_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package buildkit @@ -8,32 +9,56 @@ import ( "strconv" "sync" - "github.com/docker/libnetwork" + "github.com/docker/docker/daemon/config" + "github.com/docker/docker/libnetwork" + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/stringid" "github.com/moby/buildkit/executor" + "github.com/moby/buildkit/executor/oci" "github.com/moby/buildkit/executor/runcexecutor" "github.com/moby/buildkit/identity" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/network" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/sirupsen/logrus" ) const networkName = "bridge" -func newExecutor(root, cgroupParent string, net libnetwork.NetworkController) (executor.Executor, error) { +func newExecutor(root, cgroupParent string, net libnetwork.NetworkController, dnsConfig *oci.DNSConfig, rootless bool, idmap *idtools.IdentityMapping, apparmorProfile string) (executor.Executor, error) { + netRoot := filepath.Join(root, "net") networkProviders := map[pb.NetMode]network.Provider{ - pb.NetMode_UNSET: &bridgeProvider{NetworkController: net}, + pb.NetMode_UNSET: &bridgeProvider{NetworkController: net, Root: netRoot}, pb.NetMode_HOST: network.NewHostProvider(), pb.NetMode_NONE: network.NewNoneProvider(), } + + // make sure net state directory is cleared from previous state + fis, err := os.ReadDir(netRoot) + if err == nil { + for _, fi := range fis { + fp := filepath.Join(netRoot, fi.Name()) + if err := os.RemoveAll(fp); err != nil { + logrus.WithError(err).Errorf("failed to delete old network state: %v", fp) + } + } + } + return runcexecutor.New(runcexecutor.Opt{ Root: filepath.Join(root, "executor"), CommandCandidates: []string{"runc"}, DefaultCgroupParent: cgroupParent, + Rootless: rootless, + NoPivot: os.Getenv("DOCKER_RAMDISK") != "", + IdentityMapping: idmap, + DNS: dnsConfig, + ApparmorProfile: apparmorProfile, }, networkProviders) } type bridgeProvider struct { libnetwork.NetworkController + Root string } func (p *bridgeProvider) New() (network.Namespace, error) { @@ -63,13 +88,14 @@ func (iface *lnInterface) init(c libnetwork.NetworkController, n libnetwork.Netw defer close(iface.ready) id := identity.NewID() - ep, err := n.CreateEndpoint(id) + ep, err := n.CreateEndpoint(id, libnetwork.CreateOptionDisableResolution()) if err != nil { iface.err = err return } - sbx, err := c.NewSandbox(id) + sbx, err := c.NewSandbox(id, libnetwork.OptionUseExternalKey(), libnetwork.OptionHostsPath(filepath.Join(iface.provider.Root, id, "hosts")), + libnetwork.OptionResolvConfPath(filepath.Join(iface.provider.Root, id, "resolv.conf"))) if err != nil { iface.err = err return @@ -84,26 +110,45 @@ func (iface *lnInterface) init(c libnetwork.NetworkController, n libnetwork.Netw iface.ep = ep } -func (iface *lnInterface) Set(s *specs.Spec) { +func (iface *lnInterface) Set(s *specs.Spec) error { <-iface.ready if iface.err != nil { - return + logrus.WithError(iface.err).Error("failed to set networking spec") + return iface.err } + shortNetCtlrID := stringid.TruncateID(iface.provider.NetworkController.ID()) // attach netns to bridge within the container namespace, using reexec in a prestart hook s.Hooks = &specs.Hooks{ Prestart: []specs.Hook{{ Path: filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe"), - Args: []string{"libnetwork-setkey", iface.sbx.ContainerID(), iface.provider.NetworkController.ID()}, + Args: []string{"libnetwork-setkey", "-exec-root=" + iface.provider.Config().Daemon.ExecRoot, iface.sbx.ContainerID(), shortNetCtlrID}, }}, } + return nil } func (iface *lnInterface) Close() error { <-iface.ready - err := iface.sbx.Delete() - if iface.err != nil { - // iface.err takes precedence over cleanup errors - return iface.err + if iface.sbx != nil { + go func() { + if err := iface.sbx.Delete(); err != nil { + logrus.WithError(err).Errorf("failed to delete builder network sandbox") + } + if err := os.RemoveAll(filepath.Join(iface.provider.Root, iface.sbx.ContainerID())); err != nil { + logrus.WithError(err).Errorf("failed to delete builder sandbox directory") + } + }() + } + return iface.err +} + +func getDNSConfig(cfg config.DNSConfig) *oci.DNSConfig { + if cfg.DNS != nil || cfg.DNSSearch != nil || cfg.DNSOptions != nil { + return &oci.DNSConfig{ + Nameservers: cfg.DNS, + SearchDomains: cfg.DNSSearch, + Options: cfg.DNSOptions, + } } - return err + return nil } diff --git a/builder/builder-next/executor_windows.go b/builder/builder-next/executor_windows.go index f19bf18655360..77d1fa096d629 100644 --- a/builder/builder-next/executor_windows.go +++ b/builder/builder-next/executor_windows.go @@ -3,20 +3,29 @@ package buildkit import ( "context" "errors" - "io" - "github.com/docker/libnetwork" - "github.com/moby/buildkit/cache" + "github.com/docker/docker/daemon/config" + "github.com/docker/docker/libnetwork" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/executor" + "github.com/moby/buildkit/executor/oci" ) -func newExecutor(_, _ string, _ libnetwork.NetworkController) (executor.Executor, error) { +func newExecutor(_, _ string, _ libnetwork.NetworkController, _ *oci.DNSConfig, _ bool, _ *idtools.IdentityMapping, _ string) (executor.Executor, error) { return &winExecutor{}, nil } type winExecutor struct { } -func (e *winExecutor) Exec(ctx context.Context, meta executor.Meta, rootfs cache.Mountable, mounts []executor.Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error { +func (w *winExecutor) Run(ctx context.Context, id string, root executor.Mount, mounts []executor.Mount, process executor.ProcessInfo, started chan<- struct{}) (err error) { return errors.New("buildkit executor not implemented for windows") } + +func (w *winExecutor) Exec(ctx context.Context, id string, process executor.ProcessInfo) error { + return errors.New("buildkit executor not implemented for windows") +} + +func getDNSConfig(config.DNSConfig) *oci.DNSConfig { + return nil +} diff --git a/builder/builder-next/exporter/export.go b/builder/builder-next/exporter/export.go index e0c7db563c419..46503ad1e5dec 100644 --- a/builder/builder-next/exporter/export.go +++ b/builder/builder-next/exporter/export.go @@ -77,8 +77,7 @@ func (e *imageExporterInstance) Name() string { return "exporting to image" } -func (e *imageExporterInstance) Export(ctx context.Context, inp exporter.Source) (map[string]string, error) { - +func (e *imageExporterInstance) Export(ctx context.Context, inp exporter.Source, sessionID string) (map[string]string, error) { if len(inp.Refs) > 1 { return nil, fmt.Errorf("exporting multiple references to image store is currently unsupported") } @@ -117,12 +116,12 @@ func (e *imageExporterInstance) Export(ctx context.Context, inp exporter.Source) layersDone := oneOffProgress(ctx, "exporting layers") if err := ref.Finalize(ctx, true); err != nil { - return nil, err + return nil, layersDone(err) } diffIDs, err := e.opt.Differ.EnsureLayer(ctx, ref.ID()) if err != nil { - return nil, err + return nil, layersDone(err) } diffs = make([]digest.Digest, len(diffIDs)) @@ -130,7 +129,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, inp exporter.Source) diffs[i] = digest.Digest(diffIDs[i]) } - layersDone(nil) + _ = layersDone(nil) } if len(config) == 0 { @@ -148,7 +147,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, inp exporter.Source) diffs, history = normalizeLayersAndHistory(diffs, history, ref) - config, err = patchImageConfig(config, diffs, history) + config, err = patchImageConfig(config, diffs, history, inp.Metadata[exptypes.ExporterInlineCache]) if err != nil { return nil, err } @@ -160,7 +159,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, inp exporter.Source) if err != nil { return nil, configDone(err) } - configDone(nil) + _ = configDone(nil) if e.opt.ReferenceStore != nil { for _, targetName := range e.targetNames { @@ -169,7 +168,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, inp exporter.Source) if err := e.opt.ReferenceStore.AddTag(targetName, digest.Digest(id), true); err != nil { return nil, tagDone(err) } - tagDone(nil) + _ = tagDone(nil) } } diff --git a/builder/builder-next/exporter/writer.go b/builder/builder-next/exporter/writer.go index e8fa143fc628b..53305caf8543a 100644 --- a/builder/builder-next/exporter/writer.go +++ b/builder/builder-next/exporter/writer.go @@ -26,7 +26,7 @@ func emptyImageConfig() ([]byte, error) { } img.RootFS.Type = "layers" img.Config.WorkingDir = "/" - img.Config.Env = []string{"PATH=" + system.DefaultPathEnv} + img.Config.Env = []string{"PATH=" + system.DefaultPathEnvUnix} dt, err := json.Marshal(img) return dt, errors.Wrap(err, "failed to create empty image config") } @@ -41,7 +41,7 @@ func parseHistoryFromConfig(dt []byte) ([]ocispec.History, error) { return config.History, nil } -func patchImageConfig(dt []byte, dps []digest.Digest, history []ocispec.History) ([]byte, error) { +func patchImageConfig(dt []byte, dps []digest.Digest, history []ocispec.History, cache []byte) ([]byte, error) { m := map[string]json.RawMessage{} if err := json.Unmarshal(dt, &m); err != nil { return nil, errors.Wrap(err, "failed to parse image config for patch") @@ -77,6 +77,14 @@ func patchImageConfig(dt []byte, dps []digest.Digest, history []ocispec.History) m["created"] = dt } + if cache != nil { + dt, err = json.Marshal(cache) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal cache") + } + m["moby.buildkit.cache.v0"] = dt + } + dt, err = json.Marshal(m) return dt, errors.Wrap(err, "failed to marshal config after patch") } @@ -129,6 +137,37 @@ func normalizeLayersAndHistory(diffs []digest.Digest, history []ocispec.History, history[i] = h } + // Find the first new layer time. Otherwise, the history item for a first + // metadata command would be the creation time of a base image layer. + // If there is no such then the last layer with timestamp. + var created *time.Time + var noCreatedTime bool + for _, h := range history { + if h.Created != nil { + created = h.Created + if noCreatedTime { + break + } + } else { + noCreatedTime = true + } + } + + // Fill in created times for all history items to be either the first new + // layer time or the previous layer. + noCreatedTime = false + for i, h := range history { + if h.Created != nil { + if noCreatedTime { + created = h.Created + } + } else { + noCreatedTime = true + h.Created = created + } + history[i] = h + } + return diffs, history } @@ -165,13 +204,13 @@ func oneOffProgress(ctx context.Context, id string) func(err error) error { st := progress.Status{ Started: &now, } - pw.Write(id, st) + _ = pw.Write(id, st) return func(err error) error { // TODO: set error on status now := time.Now() st.Completed = &now - pw.Write(id, st) - pw.Close() + _ = pw.Write(id, st) + _ = pw.Close() return err } } diff --git a/builder/builder-next/imagerefchecker/checker.go b/builder/builder-next/imagerefchecker/checker.go index 052391d5893fc..01dfab37d5a18 100644 --- a/builder/builder-next/imagerefchecker/checker.go +++ b/builder/builder-next/imagerefchecker/checker.go @@ -6,6 +6,7 @@ import ( "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/moby/buildkit/cache" + digest "github.com/opencontainers/go-digest" ) // LayerGetter abstracts away the snapshotter @@ -57,7 +58,7 @@ type checker struct { cache map[string]bool } -func (c *checker) Exists(key string) bool { +func (c *checker) Exists(key string, chain []digest.Digest) bool { if c.opt.ImageStore == nil { return false } diff --git a/builder/builder-next/reqbodyhandler.go b/builder/builder-next/reqbodyhandler.go index 48433908fb30f..91592182959c7 100644 --- a/builder/builder-next/reqbodyhandler.go +++ b/builder/builder-next/reqbodyhandler.go @@ -35,13 +35,14 @@ func (h *reqBodyHandler) newRequest(rc io.ReadCloser) (string, func()) { h.mu.Lock() delete(h.requests, id) h.mu.Unlock() + rc.Close() } } func (h *reqBodyHandler) RoundTrip(req *http.Request) (*http.Response, error) { host := req.URL.Host if strings.HasPrefix(host, urlPrefix) { - if req.Method != "GET" { + if req.Method != http.MethodGet { return nil, errors.Errorf("invalid request") } id := strings.TrimPrefix(host, urlPrefix) @@ -56,7 +57,7 @@ func (h *reqBodyHandler) RoundTrip(req *http.Request) (*http.Response, error) { resp := &http.Response{ Status: "200 OK", - StatusCode: 200, + StatusCode: http.StatusOK, Body: rc, ContentLength: -1, } diff --git a/builder/builder-next/worker/gc_unix.go b/builder/builder-next/worker/gc_unix.go index 7457a06654e60..b25906b828409 100644 --- a/builder/builder-next/worker/gc_unix.go +++ b/builder/builder-next/worker/gc_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package worker @@ -11,7 +12,7 @@ func detectDefaultGCCap(root string) int64 { if err := syscall.Statfs(root, &st); err != nil { return defaultCap } - diskSize := int64(st.Bsize) * int64(st.Blocks) // nolint unconvert + diskSize := int64(st.Bsize) * int64(st.Blocks) //nolint unconvert avail := diskSize / 10 return (avail/(1<<30) + 1) * 1e9 // round up } diff --git a/builder/builder-next/worker/gc_windows.go b/builder/builder-next/worker/gc_windows.go index 1ffcce41dbc11..748be9041d16d 100644 --- a/builder/builder-next/worker/gc_windows.go +++ b/builder/builder-next/worker/gc_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package worker diff --git a/builder/builder-next/worker/worker.go b/builder/builder-next/worker/worker.go index 28d0d8bea645e..9476cb20a92d4 100644 --- a/builder/builder-next/worker/worker.go +++ b/builder/builder-next/worker/worker.go @@ -4,14 +4,16 @@ import ( "context" "fmt" "io" - "io/ioutil" nethttp "net/http" "runtime" + "strings" "time" "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/rootfs" + "github.com/docker/docker/builder/builder-next/adapters/containerimage" "github.com/docker/docker/distribution" distmetadata "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" @@ -21,43 +23,59 @@ import ( "github.com/moby/buildkit/cache" "github.com/moby/buildkit/cache/metadata" "github.com/moby/buildkit/client" + "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/executor" "github.com/moby/buildkit/exporter" + localexporter "github.com/moby/buildkit/exporter/local" + tarexporter "github.com/moby/buildkit/exporter/tar" "github.com/moby/buildkit/frontend" - gw "github.com/moby/buildkit/frontend/gateway/client" "github.com/moby/buildkit/session" "github.com/moby/buildkit/snapshot" "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/solver/llbsolver/mounts" "github.com/moby/buildkit/solver/llbsolver/ops" "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/source" "github.com/moby/buildkit/source/git" "github.com/moby/buildkit/source/http" "github.com/moby/buildkit/source/local" + "github.com/moby/buildkit/util/archutil" + "github.com/moby/buildkit/util/compression" "github.com/moby/buildkit/util/contentutil" "github.com/moby/buildkit/util/progress" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" + bolt "go.etcd.io/bbolt" + "golang.org/x/sync/semaphore" ) +const labelCreatedAt = "buildkit/createdat" + +// LayerAccess provides access to a moby layer from a snapshot +type LayerAccess interface { + GetDiffIDs(ctx context.Context, key string) ([]layer.DiffID, error) + EnsureLayer(ctx context.Context, key string) ([]layer.DiffID, error) +} + // Opt defines a structure for creating a worker. type Opt struct { ID string Labels map[string]string GCPolicy []client.PruneInfo - SessionManager *session.Manager MetadataStore *metadata.Store Executor executor.Executor Snapshotter snapshot.Snapshotter ContentStore content.Store CacheManager cache.Manager - ImageSource source.Source - Exporters map[string]exporter.Exporter + ImageSource *containerimage.Source DownloadManager distribution.RootFSDownloadManager V2MetadataService distmetadata.V2MetadataService Transport nethttp.RoundTripper + Exporter exporter.Exporter + Layers LayerAccess + Platforms []ocispec.Platform } // Worker is a local worker instance with dedicated snapshotter, cache, and so on. @@ -99,9 +117,8 @@ func NewWorker(opt Opt) (*Worker, error) { } ss, err := local.NewSource(local.Opt{ - SessionManager: opt.SessionManager, - CacheAccessor: cm, - MetadataStore: opt.MetadataStore, + CacheAccessor: cm, + MetadataStore: opt.MetadataStore, }) if err == nil { sm.Register(ss) @@ -126,9 +143,23 @@ func (w *Worker) Labels() map[string]string { } // Platforms returns one or more platforms supported by the image. -func (w *Worker) Platforms() []ocispec.Platform { - // does not handle lcow - return []ocispec.Platform{platforms.DefaultSpec()} +func (w *Worker) Platforms(noCache bool) []ocispec.Platform { + if noCache { + pm := make(map[string]struct{}, len(w.Opt.Platforms)) + for _, p := range w.Opt.Platforms { + pm[platforms.Format(p)] = struct{}{} + } + for _, p := range archutil.SupportedPlatforms(noCache) { + if _, ok := pm[p]; !ok { + pp, _ := platforms.Parse(p) + w.Opt.Platforms = append(w.Opt.Platforms, pp) + } + } + } + if len(w.Opt.Platforms) == 0 { + return []ocispec.Platform{platforms.DefaultSpec()} + } + return w.Opt.Platforms } // GCPolicy returns automatic GC Policy @@ -136,23 +167,37 @@ func (w *Worker) GCPolicy() []client.PruneInfo { return w.Opt.GCPolicy } +// ContentStore returns content store +func (w *Worker) ContentStore() content.Store { + return w.Opt.ContentStore +} + +// MetadataStore returns the metadata store +func (w *Worker) MetadataStore() *metadata.Store { + return w.Opt.MetadataStore +} + // LoadRef loads a reference by ID -func (w *Worker) LoadRef(id string, hidden bool) (cache.ImmutableRef, error) { +func (w *Worker) LoadRef(ctx context.Context, id string, hidden bool) (cache.ImmutableRef, error) { var opts []cache.RefOption if hidden { opts = append(opts, cache.NoUpdateLastUsed) } - return w.CacheManager.Get(context.TODO(), id, opts...) + return w.CacheManager().Get(ctx, id, opts...) } // ResolveOp converts a LLB vertex into a LLB operation -func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solver.Op, error) { +func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge, sm *session.Manager) (solver.Op, error) { if baseOp, ok := v.Sys().(*pb.Op); ok { + // TODO do we need to pass a value here? Where should it come from? https://github.com/moby/buildkit/commit/b3cf7c43cfefdfd7a945002c0e76b54e346ab6cf + var parallelism *semaphore.Weighted switch op := baseOp.Op.(type) { case *pb.Op_Source: - return ops.NewSourceOp(v, op, baseOp.Platform, w.SourceManager, w) + return ops.NewSourceOp(v, op, baseOp.Platform, w.SourceManager, parallelism, sm, w) case *pb.Op_Exec: - return ops.NewExecOp(v, op, w.CacheManager, w.Opt.SessionManager, w.MetadataStore, w.Executor, w) + return ops.NewExecOp(v, op, baseOp.Platform, w.CacheManager(), parallelism, sm, w.Opt.MetadataStore, w.Executor(), w) + case *pb.Op_File: + return ops.NewFileOp(v, op, w.CacheManager(), parallelism, w.Opt.MetadataStore, w) case *pb.Op_Build: return ops.NewBuildOp(v, op, s, w) } @@ -161,47 +206,128 @@ func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solve } // ResolveImageConfig returns image config for an image -func (w *Worker) ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error) { - // ImageSource is typically source/containerimage - resolveImageConfig, ok := w.ImageSource.(resolveImageConfig) - if !ok { - return "", nil, errors.Errorf("worker %q does not implement ResolveImageConfig", w.ID()) - } - return resolveImageConfig.ResolveImageConfig(ctx, ref, opt) -} - -// Exec executes a process directly on a worker -func (w *Worker) Exec(ctx context.Context, meta executor.Meta, rootFS cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error { - active, err := w.CacheManager.New(ctx, rootFS) - if err != nil { - return err - } - defer active.Release(context.TODO()) - return w.Executor.Exec(ctx, meta, active, nil, stdin, stdout, stderr) +func (w *Worker) ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt, sm *session.Manager, g session.Group) (digest.Digest, []byte, error) { + return w.ImageSource.ResolveImageConfig(ctx, ref, opt, sm, g) } // DiskUsage returns disk usage report func (w *Worker) DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error) { - return w.CacheManager.DiskUsage(ctx, opt) + return w.CacheManager().DiskUsage(ctx, opt) } // Prune deletes reclaimable build cache func (w *Worker) Prune(ctx context.Context, ch chan client.UsageInfo, info ...client.PruneInfo) error { - return w.CacheManager.Prune(ctx, ch, info...) + return w.CacheManager().Prune(ctx, ch, info...) } // Exporter returns exporter by name -func (w *Worker) Exporter(name string) (exporter.Exporter, error) { - exp, ok := w.Exporters[name] - if !ok { +func (w *Worker) Exporter(name string, sm *session.Manager) (exporter.Exporter, error) { + switch name { + case "moby": + return w.Opt.Exporter, nil + case client.ExporterLocal: + return localexporter.New(localexporter.Opt{ + SessionManager: sm, + }) + case client.ExporterTar: + return tarexporter.New(tarexporter.Opt{ + SessionManager: sm, + }) + default: return nil, errors.Errorf("exporter %q could not be found", name) } - return exp, nil } // GetRemote returns a remote snapshot reference for a local one -func (w *Worker) GetRemote(ctx context.Context, ref cache.ImmutableRef, createIfNeeded bool) (*solver.Remote, error) { - return nil, errors.Errorf("getremote not implemented") +func (w *Worker) GetRemote(ctx context.Context, ref cache.ImmutableRef, createIfNeeded bool, _ compression.Type, _ session.Group) (*solver.Remote, error) { + var diffIDs []layer.DiffID + var err error + if !createIfNeeded { + diffIDs, err = w.Layers.GetDiffIDs(ctx, ref.ID()) + if err != nil { + return nil, err + } + } else { + if err := ref.Finalize(ctx, true); err != nil { + return nil, err + } + diffIDs, err = w.Layers.EnsureLayer(ctx, ref.ID()) + if err != nil { + return nil, err + } + } + + descriptors := make([]ocispec.Descriptor, len(diffIDs)) + for i, dgst := range diffIDs { + descriptors[i] = ocispec.Descriptor{ + MediaType: images.MediaTypeDockerSchema2Layer, + Digest: digest.Digest(dgst), + Size: -1, + } + } + + return &solver.Remote{ + Descriptors: descriptors, + Provider: &emptyProvider{}, + }, nil +} + +// PruneCacheMounts removes the current cache snapshots for specified IDs +func (w *Worker) PruneCacheMounts(ctx context.Context, ids []string) error { + mu := mounts.CacheMountsLocker() + mu.Lock() + defer mu.Unlock() + + for _, id := range ids { + id = "cache-dir:" + id + sis, err := w.Opt.MetadataStore.Search(id) + if err != nil { + return err + } + for _, si := range sis { + for _, k := range si.Indexes() { + if k == id || strings.HasPrefix(k, id+":") { + if siCached := w.CacheManager().Metadata(si.ID()); siCached != nil { + si = siCached + } + if err := cache.CachePolicyDefault(si); err != nil { + return err + } + si.Queue(func(b *bolt.Bucket) error { + return si.SetValue(b, k, nil) + }) + if err := si.Commit(); err != nil { + return err + } + // if ref is unused try to clean it up right away by releasing it + if mref, err := w.CacheManager().GetMutable(ctx, si.ID()); err == nil { + go mref.Release(context.TODO()) + } + break + } + } + } + } + + mounts.ClearActiveCacheMounts() + return nil +} + +func (w *Worker) getRef(ctx context.Context, diffIDs []layer.DiffID, opts ...cache.RefOption) (cache.ImmutableRef, error) { + var parent cache.ImmutableRef + if len(diffIDs) > 1 { + var err error + parent, err = w.getRef(ctx, diffIDs[:len(diffIDs)-1], opts...) + if err != nil { + return nil, err + } + defer parent.Release(context.TODO()) + } + return w.CacheManager().GetByBlob(context.TODO(), ocispec.Descriptor{ + Annotations: map[string]string{ + "containerd.io/uncompressed": diffIDs[len(diffIDs)-1].String(), + }, + }, parent, opts...) } // FromRemote converts a remote snapshot reference to a local one @@ -226,7 +352,7 @@ func (w *Worker) FromRemote(ctx context.Context, remote *solver.Remote) (cache.I defer func() { for _, l := range rootfs { - w.ContentStore.Delete(context.TODO(), l.Blob.Digest) + w.ContentStore().Delete(context.TODO(), l.Blob.Digest) } }() @@ -237,11 +363,42 @@ func (w *Worker) FromRemote(ctx context.Context, remote *solver.Remote) (cache.I } defer release() - ref, err := w.CacheManager.GetFromSnapshotter(ctx, string(rootFS.ChainID()), cache.WithDescription(fmt.Sprintf("imported %s", remote.Descriptors[len(remote.Descriptors)-1].Digest))) - if err != nil { - return nil, err + if len(rootFS.DiffIDs) != len(layers) { + return nil, errors.Errorf("invalid layer count mismatch %d vs %d", len(rootFS.DiffIDs), len(layers)) + } + + for i := range rootFS.DiffIDs { + tm := time.Now() + if tmstr, ok := remote.Descriptors[i].Annotations[labelCreatedAt]; ok { + if err := (&tm).UnmarshalText([]byte(tmstr)); err != nil { + return nil, err + } + } + descr := fmt.Sprintf("imported %s", remote.Descriptors[i].Digest) + if v, ok := remote.Descriptors[i].Annotations["buildkit/description"]; ok { + descr = v + } + ref, err := w.getRef(ctx, rootFS.DiffIDs[:i+1], cache.WithDescription(descr), cache.WithCreationTime(tm)) + if err != nil { + return nil, err + } + if i == len(remote.Descriptors)-1 { + return ref, nil + } + defer ref.Release(context.TODO()) } - return ref, nil + + return nil, errors.Errorf("unreachable") +} + +// Executor returns executor.Executor for running processes +func (w *Worker) Executor() executor.Executor { + return w.Opt.Executor +} + +// CacheManager returns cache.Manager for accessing local storage +func (w *Worker) CacheManager() cache.Manager { + return w.Opt.CacheManager } type discardProgress struct{} @@ -274,21 +431,24 @@ func (ld *layerDescriptor) DiffID() (layer.DiffID, error) { func (ld *layerDescriptor) Download(ctx context.Context, progressOutput pkgprogress.Output) (io.ReadCloser, int64, error) { done := oneOffProgress(ld.pctx, fmt.Sprintf("pulling %s", ld.desc.Digest)) - if err := contentutil.Copy(ctx, ld.w.ContentStore, ld.provider, ld.desc); err != nil { + + // TODO should this write output to progressOutput? Or use something similar to loggerFromContext()? see https://github.com/moby/buildkit/commit/aa29e7729464f3c2a773e27795e584023c751cb8 + discardLogs := func(_ []byte) {} + if err := contentutil.Copy(ctx, ld.w.ContentStore(), ld.provider, ld.desc, discardLogs); err != nil { return nil, 0, done(err) } - done(nil) + _ = done(nil) - ra, err := ld.w.ContentStore.ReaderAt(ctx, ld.desc) + ra, err := ld.w.ContentStore().ReaderAt(ctx, ld.desc) if err != nil { return nil, 0, err } - return ioutil.NopCloser(content.NewReader(ra)), ld.desc.Size, nil + return io.NopCloser(content.NewReader(ra)), ld.desc.Size, nil } func (ld *layerDescriptor) Close() { - // ld.is.ContentStore.Delete(context.TODO(), ld.desc.Digest) + // ld.is.ContentStore().Delete(context.TODO(), ld.desc.Digest) } func (ld *layerDescriptor) Registered(diffID layer.DiffID) { @@ -326,17 +486,20 @@ func oneOffProgress(ctx context.Context, id string) func(err error) error { st := progress.Status{ Started: &now, } - pw.Write(id, st) + _ = pw.Write(id, st) return func(err error) error { // TODO: set error on status now := time.Now() st.Completed = &now - pw.Write(id, st) - pw.Close() + _ = pw.Write(id, st) + _ = pw.Close() return err } } -type resolveImageConfig interface { - ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error) +type emptyProvider struct { +} + +func (p *emptyProvider) ReaderAt(ctx context.Context, dec ocispec.Descriptor) (content.ReaderAt, error) { + return nil, errors.Errorf("ReaderAt not implemented for empty provider") } diff --git a/builder/builder.go b/builder/builder.go index 3eb0341417fe2..cf4d737e2baf7 100644 --- a/builder/builder.go +++ b/builder/builder.go @@ -60,8 +60,8 @@ type ImageBackend interface { type ExecBackend interface { // ContainerAttachRaw attaches to container. ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool, attached chan struct{}) error - // ContainerCreate creates a new Docker container and returns potential warnings - ContainerCreate(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) + // ContainerCreateIgnoreImagesArgsEscaped creates a new Docker container and returns potential warnings + ContainerCreateIgnoreImagesArgsEscaped(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) // ContainerRm removes a container specified by `id`. ContainerRm(name string, config *types.ContainerRmConfig) error // ContainerKill stops the container execution abruptly. diff --git a/builder/dockerfile/buildargs.go b/builder/dockerfile/buildargs.go index aa794fb7bcd71..965d1c6b1c2fe 100644 --- a/builder/dockerfile/buildargs.go +++ b/builder/dockerfile/buildargs.go @@ -19,6 +19,8 @@ var builtinAllowedBuildArgs = map[string]bool{ "ftp_proxy": true, "NO_PROXY": true, "no_proxy": true, + "ALL_PROXY": true, + "all_proxy": true, } // BuildArgs manages arguments used by the builder diff --git a/builder/dockerfile/buildargs_test.go b/builder/dockerfile/buildargs_test.go index c3b82c83f4b60..08318ea310597 100644 --- a/builder/dockerfile/buildargs_test.go +++ b/builder/dockerfile/buildargs_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func strPtr(source string) *string { @@ -19,6 +19,7 @@ func TestGetAllAllowed(t *testing.T) { "ArgOverriddenByOptions": strPtr("fromopt2"), "ArgNoDefaultInDockerfileFromOptions": strPtr("fromopt3"), "HTTP_PROXY": strPtr("theproxy"), + "all_proxy": strPtr("theproxy2"), }) buildArgs.AddMetaArg("ArgFromMeta", strPtr("frommeta1")) @@ -35,6 +36,7 @@ func TestGetAllAllowed(t *testing.T) { all := buildArgs.GetAllAllowed() expected := map[string]string{ "HTTP_PROXY": "theproxy", + "all_proxy": "theproxy2", "ArgOverriddenByOptions": "fromopt2", "ArgWithDefaultInDockerfile": "fromdockerfile1", "ArgNoDefaultInDockerfileFromOptions": "fromopt3", diff --git a/builder/dockerfile/builder.go b/builder/dockerfile/builder.go index a6094a8973f11..ac3ba16c727eb 100644 --- a/builder/dockerfile/builder.go +++ b/builder/dockerfile/builder.go @@ -5,17 +5,14 @@ import ( "context" "fmt" "io" - "io/ioutil" "sort" "strings" - "time" "github.com/containerd/containerd/platforms" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/container" "github.com/docker/docker/builder" - "github.com/docker/docker/builder/fscache" "github.com/docker/docker/builder/remotecontext" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/idtools" @@ -25,7 +22,6 @@ import ( "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/frontend/dockerfile/parser" "github.com/moby/buildkit/frontend/dockerfile/shell" - "github.com/moby/buildkit/session" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -49,31 +45,19 @@ const ( stepFormat = "Step %d/%d : %v" ) -// SessionGetter is object used to get access to a session by uuid -type SessionGetter interface { - Get(ctx context.Context, uuid string) (session.Caller, error) -} - // BuildManager is shared across all Builder objects type BuildManager struct { idMapping *idtools.IdentityMapping backend builder.Backend pathCache pathCache // TODO: make this persistent - sg SessionGetter - fsCache *fscache.FSCache } // NewBuildManager creates a BuildManager -func NewBuildManager(b builder.Backend, sg SessionGetter, fsCache *fscache.FSCache, identityMapping *idtools.IdentityMapping) (*BuildManager, error) { +func NewBuildManager(b builder.Backend, identityMapping *idtools.IdentityMapping) (*BuildManager, error) { bm := &BuildManager{ backend: b, pathCache: &syncmap.Map{}, - sg: sg, idMapping: identityMapping, - fsCache: fsCache, - } - if err := fsCache.RegisterTransport(remotecontext.ClientSessionRemote, NewClientSessionTransport()); err != nil { - return nil, err } return bm, nil } @@ -100,12 +84,6 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) ( ctx, cancel := context.WithCancel(ctx) defer cancel() - if src, err := bm.initializeClientSession(ctx, cancel, config.Options); err != nil { - return nil, err - } else if src != nil { - source = src - } - builderOptions := builderOptions{ Options: config.Options, ProgressWriter: config.ProgressWriter, @@ -120,39 +98,6 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) ( return b.build(source, dockerfile) } -func (bm *BuildManager) initializeClientSession(ctx context.Context, cancel func(), options *types.ImageBuildOptions) (builder.Source, error) { - if options.SessionID == "" || bm.sg == nil { - return nil, nil - } - logrus.Debug("client is session enabled") - - connectCtx, cancelCtx := context.WithTimeout(ctx, sessionConnectTimeout) - defer cancelCtx() - - c, err := bm.sg.Get(connectCtx, options.SessionID) - if err != nil { - return nil, err - } - go func() { - <-c.Context().Done() - cancel() - }() - if options.RemoteContext == remotecontext.ClientSessionRemote { - st := time.Now() - csi, err := NewClientSessionSourceIdentifier(ctx, bm.sg, options.SessionID) - if err != nil { - return nil, err - } - src, err := bm.fsCache.SyncFrom(ctx, csi) - if err != nil { - return nil, err - } - logrus.Debugf("sync-time: %v", time.Since(st)) - return src, nil - } - return nil, nil -} - // builderOptions are the dependencies required by the builder type builderOptions struct { Options *types.ImageBuildOptions @@ -213,9 +158,6 @@ func newBuilder(clientCtx context.Context, options builderOptions) (*Builder, er if err != nil { return nil, err } - if err := system.ValidatePlatform(sp); err != nil { - return nil, err - } b.platform = &sp } @@ -244,7 +186,8 @@ func (b *Builder) build(source builder.Source, dockerfile *parser.Result) (*buil stages, metaArgs, err := instructions.Parse(dockerfile.AST) if err != nil { - if instructions.IsUnknownInstruction(err) { + var uiErr *instructions.UnknownInstruction + if errors.As(err, &uiErr) { buildsFailed.WithValues(metricsUnknownInstructionError).Inc() } return nil, errdefs.InvalidParameter(err) @@ -288,8 +231,10 @@ func processMetaArg(meta instructions.ArgCommand, shlex *shell.Lex, args *BuildA }); err != nil { return err } - args.AddArg(meta.Key, meta.Value) - args.AddMetaArg(meta.Key, meta.Value) + for _, arg := range meta.Args { + args.AddArg(arg.Key, arg.Value) + args.AddMetaArg(arg.Key, arg.Value) + } return nil } @@ -308,10 +253,10 @@ func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions. totalCommands += len(stage.Commands) } shlex := shell.NewLex(escapeToken) - for _, meta := range metaArgs { - currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, &meta) + for i := range metaArgs { + currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, &metaArgs[i]) - err := processMetaArg(meta, shlex, buildArgs) + err := processMetaArg(metaArgs[i], shlex, buildArgs) if err != nil { return nil, err } @@ -319,7 +264,8 @@ func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions. stagesResults := newStagesBuildResults() - for _, stage := range parseResult { + for _, s := range parseResult { + stage := s if err := stagesResults.checkStageNameAvailable(stage.Name); err != nil { return nil, err } @@ -399,8 +345,8 @@ func BuildFromConfig(config *container.Config, changes []string, os string) (*co } } - b.Stdout = ioutil.Discard - b.Stderr = ioutil.Discard + b.Stdout = io.Discard + b.Stderr = io.Discard b.disableCommit = true var commands []instructions.Command diff --git a/builder/dockerfile/builder_unix.go b/builder/dockerfile/builder_unix.go index c4453459b37e1..7d10028575d93 100644 --- a/builder/dockerfile/builder_unix.go +++ b/builder/dockerfile/builder_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package dockerfile // import "github.com/docker/docker/builder/dockerfile" diff --git a/builder/dockerfile/clientsession.go b/builder/dockerfile/clientsession.go deleted file mode 100644 index b48090d7b5092..0000000000000 --- a/builder/dockerfile/clientsession.go +++ /dev/null @@ -1,76 +0,0 @@ -package dockerfile // import "github.com/docker/docker/builder/dockerfile" - -import ( - "context" - "time" - - "github.com/docker/docker/builder/fscache" - "github.com/docker/docker/builder/remotecontext" - "github.com/moby/buildkit/session" - "github.com/moby/buildkit/session/filesync" - "github.com/pkg/errors" -) - -const sessionConnectTimeout = 5 * time.Second - -// ClientSessionTransport is a transport for copying files from docker client -// to the daemon. -type ClientSessionTransport struct{} - -// NewClientSessionTransport returns new ClientSessionTransport instance -func NewClientSessionTransport() *ClientSessionTransport { - return &ClientSessionTransport{} -} - -// Copy data from a remote to a destination directory. -func (cst *ClientSessionTransport) Copy(ctx context.Context, id fscache.RemoteIdentifier, dest string, cu filesync.CacheUpdater) error { - csi, ok := id.(*ClientSessionSourceIdentifier) - if !ok { - return errors.New("invalid identifier for client session") - } - - return filesync.FSSync(ctx, csi.caller, filesync.FSSendRequestOpt{ - IncludePatterns: csi.includePatterns, - DestDir: dest, - CacheUpdater: cu, - }) -} - -// ClientSessionSourceIdentifier is an identifier that can be used for requesting -// files from remote client -type ClientSessionSourceIdentifier struct { - includePatterns []string - caller session.Caller - uuid string -} - -// NewClientSessionSourceIdentifier returns new ClientSessionSourceIdentifier instance -func NewClientSessionSourceIdentifier(ctx context.Context, sg SessionGetter, uuid string) (*ClientSessionSourceIdentifier, error) { - csi := &ClientSessionSourceIdentifier{ - uuid: uuid, - } - caller, err := sg.Get(ctx, uuid) - if err != nil { - return nil, errors.Wrapf(err, "failed to get session for %s", uuid) - } - - csi.caller = caller - return csi, nil -} - -// Transport returns transport identifier for remote identifier -func (csi *ClientSessionSourceIdentifier) Transport() string { - return remotecontext.ClientSessionRemote -} - -// SharedKey returns shared key for remote identifier. Shared key is used -// for finding the base for a repeated transfer. -func (csi *ClientSessionSourceIdentifier) SharedKey() string { - return csi.caller.SharedKey() -} - -// Key returns unique key for remote identifier. Requests with same key return -// same data. -func (csi *ClientSessionSourceIdentifier) Key() string { - return csi.uuid -} diff --git a/builder/dockerfile/containerbackend.go b/builder/dockerfile/containerbackend.go index 54adfb13f7fc0..8a2a70691186a 100644 --- a/builder/dockerfile/containerbackend.go +++ b/builder/dockerfile/containerbackend.go @@ -29,7 +29,7 @@ func newContainerManager(docker builder.ExecBackend) *containerManager { // Create a container func (c *containerManager) Create(runConfig *container.Config, hostConfig *container.HostConfig) (container.ContainerCreateCreatedBody, error) { - container, err := c.backend.ContainerCreate(types.ContainerCreateConfig{ + container, err := c.backend.ContainerCreateIgnoreImagesArgsEscaped(types.ContainerCreateConfig{ Config: runConfig, HostConfig: hostConfig, }) @@ -45,7 +45,7 @@ var errCancelled = errors.New("build cancelled") // Run a container by ID func (c *containerManager) Run(ctx context.Context, cID string, stdout, stderr io.Writer) (err error) { attached := make(chan struct{}) - errCh := make(chan error) + errCh := make(chan error, 1) go func() { errCh <- c.backend.ContainerAttachRaw(cID, nil, stdout, stderr, true, attached) }() diff --git a/builder/dockerfile/copy.go b/builder/dockerfile/copy.go index ad9b08dfefdd6..6d8b19beb15d1 100644 --- a/builder/dockerfile/copy.go +++ b/builder/dockerfile/copy.go @@ -24,6 +24,7 @@ import ( "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/urlutil" + "github.com/moby/buildkit/frontend/dockerfile/instructions" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -64,6 +65,7 @@ type copyInstruction struct { dest string chownStr string allowLocalDecompression bool + preserveOwnership bool } // copier reads a raw COPY or ADD command, fetches remote sources using a downloader, @@ -106,24 +108,16 @@ func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, i } -func (o *copier) createCopyInstruction(args []string, cmdName string) (copyInstruction, error) { - inst := copyInstruction{cmdName: cmdName} - last := len(args) - 1 - - // Work in platform-specific filepath semantics - // TODO: This OS switch for paths is NOT correct and should not be supported. - // Maintained for backwards compatibility - pathOS := runtime.GOOS - if o.platform != nil { - pathOS = o.platform.OS +func (o *copier) createCopyInstruction(sourcesAndDest instructions.SourcesAndDest, cmdName string) (copyInstruction, error) { + inst := copyInstruction{ + cmdName: cmdName, + dest: filepath.FromSlash(sourcesAndDest.DestPath), } - inst.dest = fromSlash(args[last], pathOS) - separator := string(separator(pathOS)) - infos, err := o.getCopyInfosForSourcePaths(args[0:last], inst.dest) + infos, err := o.getCopyInfosForSourcePaths(sourcesAndDest.SourcePaths, inst.dest) if err != nil { return inst, errors.Wrapf(err, "%s failed", cmdName) } - if len(infos) > 1 && !strings.HasSuffix(inst.dest, separator) { + if len(infos) > 1 && !strings.HasSuffix(inst.dest, string(os.PathSeparator)) { return inst, errors.Errorf("When using %s with more than one source file, the destination must be a directory and end with a /", cmdName) } inst.infos = infos @@ -190,6 +184,9 @@ func (o *copier) Cleanup() { // TODO: allowWildcards can probably be removed by refactoring this function further. func (o *copier) calcCopyInfo(origPath string, allowWildcards bool) ([]copyInfo, error) { imageSource := o.imageSource + if err := validateCopySourcePath(imageSource, origPath); err != nil { + return nil, err + } // TODO: do this when creating copier. Requires validateCopySourcePath // (and other below) to be aware of the difference sources. Why is it only @@ -214,20 +211,13 @@ func (o *copier) calcCopyInfo(origPath string, allowWildcards bool) ([]copyInfo, return nil, errors.Errorf("missing build context") } - root := o.source.Root() - - if err := validateCopySourcePath(imageSource, origPath, root.OS()); err != nil { - return nil, err - } - - // Work in source OS specific filepath semantics - // For LCOW, this is NOT the daemon OS. - origPath = root.FromSlash(origPath) - origPath = strings.TrimPrefix(origPath, string(root.Separator())) - origPath = strings.TrimPrefix(origPath, "."+string(root.Separator())) + // Work in daemon-specific OS filepath semantics + origPath = filepath.FromSlash(origPath) + origPath = strings.TrimPrefix(origPath, string(os.PathSeparator)) + origPath = strings.TrimPrefix(origPath, "."+string(os.PathSeparator)) // Deal with wildcards - if allowWildcards && containsWildcards(origPath, root.OS()) { + if allowWildcards && containsWildcards(origPath) { return o.copyWithWildcards(origPath) } @@ -241,6 +231,8 @@ func (o *copier) calcCopyInfo(origPath string, allowWildcards bool) ([]copyInfo, // Deal with the single file case copyInfo, err := copyInfoForFile(o.source, origPath) switch { + case imageSource == nil && errors.Is(err, os.ErrNotExist): + return nil, errors.Wrapf(err, "file not found in build context or excluded by .dockerignore") case err != nil: return nil, err case copyInfo.hash != "": @@ -259,19 +251,6 @@ func (o *copier) calcCopyInfo(origPath string, allowWildcards bool) ([]copyInfo, return newCopyInfos(newCopyInfoFromSource(o.source, origPath, hash)), nil } -func containsWildcards(name, platform string) bool { - isWindows := platform == "windows" - for i := 0; i < len(name); i++ { - ch := name[i] - if ch == '\\' && !isWindows { - i++ - } else if ch == '*' || ch == '?' || ch == '[' { - return true - } - } - return false -} - func (o *copier) storeInPathCache(im *imageMount, path string, hash string) { if im != nil { o.pathCache.Store(im.ImageID()+path, hash) @@ -314,6 +293,10 @@ func (o *copier) copyWithWildcards(origPath string) ([]copyInfo, error) { func copyInfoForFile(source builder.Source, path string) (copyInfo, error) { fi, err := remotecontext.StatAt(source, path) if err != nil { + if errors.Is(err, os.ErrNotExist) { + // return the relative path in the error, which is more user-friendly than the full path to the tmp-dir + return copyInfo{}, errors.WithStack(&os.PathError{Op: "stat", Path: path, Err: os.ErrNotExist}) + } return copyInfo{}, err } @@ -466,7 +449,7 @@ func downloadSource(output io.Writer, stdout io.Writer, srcURL string) (remote b type copyFileOptions struct { decompress bool - identity idtools.Identity + identity *idtools.Identity archiver Archiver } @@ -532,7 +515,7 @@ func isArchivePath(driver containerfs.ContainerFS, path string) bool { return err == nil } -func copyDirectory(archiver Archiver, source, dest *copyEndpoint, identity idtools.Identity) error { +func copyDirectory(archiver Archiver, source, dest *copyEndpoint, identity *idtools.Identity) error { destExists, err := isExistingDirectory(dest) if err != nil { return errors.Wrapf(err, "failed to query destination path") @@ -541,19 +524,23 @@ func copyDirectory(archiver Archiver, source, dest *copyEndpoint, identity idtoo if err := archiver.CopyWithTar(source.path, dest.path); err != nil { return errors.Wrapf(err, "failed to copy directory") } - // TODO: @gupta-ak. Investigate how LCOW permission mappings will work. - return fixPermissions(source.path, dest.path, identity, !destExists) + if identity != nil { + return fixPermissions(source.path, dest.path, *identity, !destExists) + } + return nil } -func copyFile(archiver Archiver, source, dest *copyEndpoint, identity idtools.Identity) error { - if runtime.GOOS == "windows" && dest.driver.OS() == "linux" { - // LCOW - if err := dest.driver.MkdirAll(dest.driver.Dir(dest.path), 0755); err != nil { - return errors.Wrapf(err, "failed to create new directory") +func copyFile(archiver Archiver, source, dest *copyEndpoint, identity *idtools.Identity) error { + if identity == nil { + // Use system.MkdirAll here, which is a custom version of os.MkdirAll + // modified for use on Windows to handle volume GUID paths. These paths + // are of the form \\?\Volume{}\. An example would be: + // \\?\Volume{dae8d3ac-b9a1-11e9-88eb-e8554b2ba1db}\bin\busybox.exe + if err := system.MkdirAll(filepath.Dir(dest.path), 0755); err != nil { + return err } } else { - if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest.path), 0755, identity); err != nil { - // Normal containers + if err := idtools.MkdirAllAndChownNew(filepath.Dir(dest.path), 0755, *identity); err != nil { return errors.Wrapf(err, "failed to create new directory") } } @@ -561,8 +548,10 @@ func copyFile(archiver Archiver, source, dest *copyEndpoint, identity idtools.Id if err := archiver.CopyFileWithTar(source.path, dest.path); err != nil { return errors.Wrapf(err, "failed to copy file") } - // TODO: @gupta-ak. Investigate how LCOW permission mappings will work. - return fixPermissions(source.path, dest.path, identity, false) + if identity != nil { + return fixPermissions(source.path, dest.path, *identity, false) + } + return nil } func endsInSlash(driver containerfs.Driver, path string) bool { @@ -573,7 +562,7 @@ func endsInSlash(driver containerfs.Driver, path string) bool { func isExistingDirectory(point *copyEndpoint) (bool, error) { destStat, err := point.driver.Stat(point.path) switch { - case os.IsNotExist(err): + case errors.Is(err, os.ErrNotExist): return false, nil case err != nil: return false, err diff --git a/builder/dockerfile/copy_test.go b/builder/dockerfile/copy_test.go index f559ff4fd8213..84b2a735f770b 100644 --- a/builder/dockerfile/copy_test.go +++ b/builder/dockerfile/copy_test.go @@ -5,9 +5,9 @@ import ( "testing" "github.com/docker/docker/pkg/containerfs" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/fs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/fs" ) func TestIsExistingDirectory(t *testing.T) { diff --git a/builder/dockerfile/copy_unix.go b/builder/dockerfile/copy_unix.go index 770226444545a..7c5a574ffb3d9 100644 --- a/builder/dockerfile/copy_unix.go +++ b/builder/dockerfile/copy_unix.go @@ -1,10 +1,13 @@ +//go:build !windows // +build !windows package dockerfile // import "github.com/docker/docker/builder/dockerfile" import ( "os" + "path" "path/filepath" + "strings" "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" @@ -25,7 +28,7 @@ func fixPermissions(source, destination string, identity idtools.Identity, overr // We Walk on the source rather than on the destination because we don't // want to change permissions on things we haven't created or modified. - return filepath.Walk(source, func(fullpath string, info os.FileInfo, err error) error { + return filepath.Walk(source, func(fullpath string, _ os.FileInfo, _ error) error { // Do not alter the walk root iff. it existed before, as it doesn't fall under // the domain of "things we should chown". if skipChownRoot && source == fullpath { @@ -43,6 +46,34 @@ func fixPermissions(source, destination string, identity idtools.Identity, overr }) } -func validateCopySourcePath(imageSource *imageMount, origPath, platform string) error { +// normalizeDest normalises the destination of a COPY/ADD command in a +// platform semantically consistent way. +func normalizeDest(workingDir, requested string) (string, error) { + dest := filepath.FromSlash(requested) + endsInSlash := strings.HasSuffix(dest, string(os.PathSeparator)) + + if !path.IsAbs(requested) { + dest = path.Join("/", filepath.ToSlash(workingDir), dest) + // Make sure we preserve any trailing slash + if endsInSlash { + dest += "/" + } + } + return dest, nil +} + +func containsWildcards(name string) bool { + for i := 0; i < len(name); i++ { + ch := name[i] + if ch == '\\' { + i++ + } else if ch == '*' || ch == '?' || ch == '[' { + return true + } + } + return false +} + +func validateCopySourcePath(imageSource *imageMount, origPath string) error { return nil } diff --git a/builder/dockerfile/copy_windows.go b/builder/dockerfile/copy_windows.go index 59c1a4b7c37bb..1a3a488516970 100644 --- a/builder/dockerfile/copy_windows.go +++ b/builder/dockerfile/copy_windows.go @@ -6,7 +6,7 @@ import ( "path/filepath" "strings" - "github.com/Microsoft/go-winio" + winio "github.com/Microsoft/go-winio" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/system" @@ -14,7 +14,7 @@ import ( "golang.org/x/sys/windows" ) -var pathBlacklist = map[string]bool{ +var pathDenyList = map[string]bool{ "c:\\": true, "c:\\windows": true, } @@ -43,8 +43,7 @@ func fixPermissionsReexec() { } func fixPermissionsWindows(source, destination, SID string) error { - - privileges := []string{winio.SeRestorePrivilege, system.SeTakeOwnershipPrivilege} + privileges := []string{winio.SeRestorePrivilege, idtools.SeTakeOwnershipPrivilege} err := winio.EnableProcessPrivileges(privileges) if err != nil { @@ -67,29 +66,79 @@ func fixPermissionsWindows(source, destination, SID string) error { sddlString := system.SddlAdministratorsLocalSystem sddlString += "(A;OICI;GRGWGXRCWDSD;;;" + SID + ")" - securityDescriptor, err := winio.SddlToSecurityDescriptor(sddlString) + securityDescriptor, err := windows.SecurityDescriptorFromString(sddlString) if err != nil { return err } - var daclPresent uint32 - var daclDefaulted uint32 - var dacl *byte - - err = system.GetSecurityDescriptorDacl(&securityDescriptor[0], &daclPresent, &dacl, &daclDefaulted) + dacl, _, err := securityDescriptor.DACL() if err != nil { return err } - return system.SetNamedSecurityInfo(windows.StringToUTF16Ptr(destination), system.SE_FILE_OBJECT, system.OWNER_SECURITY_INFORMATION|system.DACL_SECURITY_INFORMATION, sid, nil, dacl, nil) + return windows.SetNamedSecurityInfo(destination, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION, sid, nil, dacl, nil) } -func validateCopySourcePath(imageSource *imageMount, origPath, platform string) error { - // validate windows paths from other images + LCOW - if imageSource == nil || platform != "windows" { - return nil +// normalizeDest normalises the destination of a COPY/ADD command in a +// platform semantically consistent way. +func normalizeDest(workingDir, requested string) (string, error) { + dest := filepath.FromSlash(requested) + endsInSlash := strings.HasSuffix(dest, string(os.PathSeparator)) + + // We are guaranteed that the working directory is already consistent, + // However, Windows also has, for now, the limitation that ADD/COPY can + // only be done to the system drive, not any drives that might be present + // as a result of a bind mount. + // + // So... if the path requested is Linux-style absolute (/foo or \\foo), + // we assume it is the system drive. If it is a Windows-style absolute + // (DRIVE:\\foo), error if DRIVE is not C. And finally, ensure we + // strip any configured working directories drive letter so that it + // can be subsequently legitimately converted to a Windows volume-style + // pathname. + + // Not a typo - filepath.IsAbs, not system.IsAbs on this next check as + // we only want to validate where the DriveColon part has been supplied. + if filepath.IsAbs(dest) { + if strings.ToUpper(string(dest[0])) != "C" { + return "", fmt.Errorf("Windows does not support destinations not on the system drive (C:)") + } + dest = dest[2:] // Strip the drive letter } + // Cannot handle relative where WorkingDir is not the system drive. + if len(workingDir) > 0 { + if ((len(workingDir) > 1) && !system.IsAbs(workingDir[2:])) || (len(workingDir) == 1) { + return "", fmt.Errorf("Current WorkingDir %s is not platform consistent", workingDir) + } + if !system.IsAbs(dest) { + if string(workingDir[0]) != "C" { + return "", fmt.Errorf("Windows does not support relative paths when WORKDIR is not the system drive") + } + dest = filepath.Join(string(os.PathSeparator), workingDir[2:], dest) + // Make sure we preserve any trailing slash + if endsInSlash { + dest += string(os.PathSeparator) + } + } + } + return dest, nil +} + +func containsWildcards(name string) bool { + for i := 0; i < len(name); i++ { + ch := name[i] + if ch == '*' || ch == '?' || ch == '[' { + return true + } + } + return false +} + +func validateCopySourcePath(imageSource *imageMount, origPath string) error { + if imageSource == nil { + return nil + } origPath = filepath.FromSlash(origPath) p := strings.ToLower(filepath.Clean(origPath)) if !filepath.IsAbs(p) { @@ -97,13 +146,13 @@ func validateCopySourcePath(imageSource *imageMount, origPath, platform string) if p[len(p)-2:] == ":." { // case where clean returns weird c:. paths p = p[:len(p)-1] } - p += "\\" + p += `\` } else { - p = filepath.Join("c:\\", p) + p = filepath.Join(`c:\`, p) } } - if _, blacklisted := pathBlacklist[p]; blacklisted { - return errors.New("copy from c:\\ or c:\\windows is not allowed on windows") + if _, ok := pathDenyList[p]; ok { + return errors.New(`copy from c:\ or c:\windows is not allowed on windows`) } return nil } diff --git a/builder/dockerfile/dispatchers.go b/builder/dockerfile/dispatchers.go index f2da08ed4d6e8..66324a513edb9 100644 --- a/builder/dockerfile/dispatchers.go +++ b/builder/dockerfile/dispatchers.go @@ -16,18 +16,17 @@ import ( "github.com/containerd/containerd/platforms" "github.com/docker/docker/api" - "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/strslice" "github.com/docker/docker/builder" "github.com/docker/docker/errdefs" "github.com/docker/docker/image" "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/system" "github.com/docker/go-connections/nat" "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/frontend/dockerfile/parser" "github.com/moby/buildkit/frontend/dockerfile/shell" + "github.com/moby/sys/signal" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -93,6 +92,9 @@ func dispatchLabel(d dispatchRequest, c *instructions.LabelCommand) error { // exist here. If you do not wish to have this automatic handling, use COPY. // func dispatchAdd(d dispatchRequest, c *instructions.AddCommand) error { + if c.Chmod != "" { + return errors.New("the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled") + } downloader := newRemoteSourceDownloader(d.builder.Output, d.builder.Stdout) copier := copierFromDispatchRequest(d, downloader, nil) defer copier.Cleanup() @@ -112,6 +114,9 @@ func dispatchAdd(d dispatchRequest, c *instructions.AddCommand) error { // Same as 'ADD' but without the tar and remote url handling. // func dispatchCopy(d dispatchRequest, c *instructions.CopyCommand) error { + if c.Chmod != "" { + return errors.New("the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled") + } var im *imageMount var err error if c.From != "" { @@ -127,7 +132,9 @@ func dispatchCopy(d dispatchRequest, c *instructions.CopyCommand) error { return err } copyInstruction.chownStr = c.Chown - + if c.From != "" && copyInstruction.chownStr == "" { + copyInstruction.preserveOwnership = true + } return d.builder.performCopy(d, copyInstruction) } @@ -165,9 +172,6 @@ func initializeStage(d dispatchRequest, cmd *instructions.Stage) error { if err != nil { return errors.Wrapf(err, "failed to parse platform %s", v) } - if err := system.ValidatePlatform(p); err != nil { - return err - } platform = &p } @@ -204,7 +208,8 @@ func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error { } cmd, err := instructions.ParseCommand(ast.AST.Children[0]) if err != nil { - if instructions.IsUnknownInstruction(err) { + var uiErr *instructions.UnknownInstruction + if errors.As(err, &uiErr) { buildsFailed.WithValues(metricsUnknownInstructionError).Inc() } return err @@ -256,10 +261,7 @@ func (d *dispatchRequest) getImageOrStage(name string, platform *specs.Platform) // from it. if runtime.GOOS == "windows" { if platform == nil || platform.OS == "linux" { - if !system.LCOWSupported() { - return nil, errors.New("Linux containers are not supported on this system") - } - imageImage.OS = "linux" + return nil, errors.New("Linux containers are not supported on this system") } else if platform.OS == "windows" { return nil, errors.New("Windows does not support FROM scratch") } else { @@ -330,14 +332,6 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error { return d.builder.commitContainer(d.state, containerID, runConfigWithCommentCmd) } -func resolveCmdLine(cmd instructions.ShellDependantCmdLine, runConfig *container.Config, os string) []string { - result := cmd.CmdLine - if cmd.PrependShell && result != nil { - result = append(getShell(runConfig, os), result...) - } - return result -} - // RUN some command yo // // run a command and commit the image. Args are automatically prepended with @@ -352,8 +346,14 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error { if !system.IsOSSupported(d.state.operatingSystem) { return system.ErrNotSupportedOperatingSystem } + + if len(c.FlagsUsed) > 0 { + // classic builder RUN currently does not support any flags, so fail on the first one + return errors.Errorf("the --%s option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled", c.FlagsUsed[0]) + } + stateRunConfig := d.state.runConfig - cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.state.operatingSystem) + cmdFromArgs, argsEscaped := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.state.operatingSystem, c.Name(), c.String()) buildArgs := d.state.buildArgs.FilterAllowed(stateRunConfig.Env) saveCmd := cmdFromArgs @@ -363,6 +363,7 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error { runConfigForCacheProbe := copyRunConfig(stateRunConfig, withCmd(saveCmd), + withArgsEscaped(argsEscaped), withEntrypointOverride(saveCmd, nil)) if hit, err := d.builder.probeCache(d.state, runConfigForCacheProbe); err != nil || hit { return err @@ -370,13 +371,11 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error { runConfig := copyRunConfig(stateRunConfig, withCmd(cmdFromArgs), + withArgsEscaped(argsEscaped), withEnv(append(stateRunConfig.Env, buildArgs...)), withEntrypointOverride(saveCmd, strslice.StrSlice{""}), withoutHealthcheck()) - // set config as already being escaped, this prevents double escaping on windows - runConfig.ArgsEscaped = true - cID, err := d.builder.create(runConfig) if err != nil { return err @@ -399,6 +398,12 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error { return err } + // Don't persist the argsEscaped value in the committed image. Use the original + // from previous build steps (only CMD and ENTRYPOINT persist this). + if d.state.operatingSystem == "windows" { + runConfigForCacheProbe.ArgsEscaped = stateRunConfig.ArgsEscaped + } + return d.builder.commitContainer(d.state, cID, runConfigForCacheProbe) } @@ -434,15 +439,23 @@ func prependEnvOnCmd(buildArgs *BuildArgs, buildArgVars []string, cmd strslice.S // func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error { runConfig := d.state.runConfig - cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.operatingSystem) + cmd, argsEscaped := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.operatingSystem, c.Name(), c.String()) + + // We warn here as Windows shell processing operates differently to Linux. + // Linux: /bin/sh -c "echo hello" world --> hello + // Windows: cmd /s /c "echo hello" world --> hello world + if d.state.operatingSystem == "windows" && + len(runConfig.Entrypoint) > 0 && + d.state.runConfig.ArgsEscaped != argsEscaped { + fmt.Fprintf(d.builder.Stderr, " ---> [Warning] Shell-form ENTRYPOINT and exec-form CMD may have unexpected results\n") + } + runConfig.Cmd = cmd - // set config as already being escaped, this prevents double escaping on windows - runConfig.ArgsEscaped = true + runConfig.ArgsEscaped = argsEscaped if err := d.builder.commit(d.state, fmt.Sprintf("CMD %q", cmd)); err != nil { return err } - if len(c.ShellDependantCmdLine.CmdLine) != 0 { d.state.cmdSet = true } @@ -477,8 +490,22 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand) // func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) error { runConfig := d.state.runConfig - cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.operatingSystem) + cmd, argsEscaped := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.operatingSystem, c.Name(), c.String()) + + // This warning is a little more complex than in dispatchCmd(), as the Windows base images (similar + // universally to almost every Linux image out there) have a single .Cmd field populated so that + // `docker run --rm image` starts the default shell which would typically be sh on Linux, + // or cmd on Windows. The catch to this is that if a dockerfile had `CMD ["c:\\windows\\system32\\cmd.exe"]`, + // we wouldn't be able to tell the difference. However, that would be highly unlikely, and besides, this + // is only trying to give a helpful warning of possibly unexpected results. + if d.state.operatingSystem == "windows" && + d.state.runConfig.ArgsEscaped != argsEscaped && + ((len(runConfig.Cmd) == 1 && strings.ToLower(runConfig.Cmd[0]) != `c:\windows\system32\cmd.exe` && len(runConfig.Shell) == 0) || (len(runConfig.Cmd) > 1)) { + fmt.Fprintf(d.builder.Stderr, " ---> [Warning] Shell-form CMD and exec-form ENTRYPOINT may have unexpected results\n") + } + runConfig.Entrypoint = cmd + runConfig.ArgsEscaped = argsEscaped if !d.state.cmdSet { runConfig.Cmd = nil } @@ -566,14 +593,21 @@ func dispatchStopSignal(d dispatchRequest, c *instructions.StopSignalCommand) er // to builder using the --build-arg flag for expansion/substitution or passing to 'run'. // Dockerfile author may optionally set a default value of this variable. func dispatchArg(d dispatchRequest, c *instructions.ArgCommand) error { - - commitStr := "ARG " + c.Key - if c.Value != nil { - commitStr += "=" + *c.Value + var commitStr strings.Builder + commitStr.WriteString("ARG ") + for i, arg := range c.Args { + if i > 0 { + commitStr.WriteString(" ") + } + commitStr.WriteString(arg.Key) + if arg.Value != nil { + commitStr.WriteString("=") + commitStr.WriteString(*arg.Value) + } + d.state.buildArgs.AddArg(arg.Key, arg.Value) } - d.state.buildArgs.AddArg(c.Key, c.Value) - return d.builder.commit(d.state, commitStr) + return d.builder.commit(d.state, commitStr.String()) } // SHELL powershell -command diff --git a/builder/dockerfile/dispatchers_test.go b/builder/dockerfile/dispatchers_test.go index d767d318fac28..75ae30b52d9e2 100644 --- a/builder/dockerfile/dispatchers_test.go +++ b/builder/dockerfile/dispatchers_test.go @@ -3,7 +3,9 @@ package dockerfile // import "github.com/docker/docker/builder/dockerfile" import ( "bytes" "context" + "fmt" "runtime" + "strings" "testing" "github.com/docker/docker/api/types" @@ -15,9 +17,10 @@ import ( "github.com/docker/docker/pkg/system" "github.com/docker/go-connections/nat" "github.com/moby/buildkit/frontend/dockerfile/instructions" + "github.com/moby/buildkit/frontend/dockerfile/parser" "github.com/moby/buildkit/frontend/dockerfile/shell" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func newBuilderWithMockBackend() *Builder { @@ -113,7 +116,7 @@ func TestFromScratch(t *testing.T) { } err := initializeStage(sb, cmd) - if runtime.GOOS == "windows" && !system.LCOWSupported() { + if runtime.GOOS == "windows" { assert.Check(t, is.Error(err, "Linux containers are not supported on this system")) return } @@ -137,10 +140,10 @@ func TestFromWithArg(t *testing.T) { args := NewBuildArgs(make(map[string]*string)) val := "sometag" - metaArg := instructions.ArgCommand{KeyValuePairOptional: instructions.KeyValuePairOptional{ + metaArg := instructions.ArgCommand{Args: []instructions.KeyValuePairOptional{{ Key: "THETAG", Value: &val, - }} + }}} cmd := &instructions.Stage{ BaseName: "alpine:${THETAG}", } @@ -393,7 +396,7 @@ func TestArg(t *testing.T) { argName := "foo" argVal := "bar" - cmd := &instructions.ArgCommand{KeyValuePairOptional: instructions.KeyValuePairOptional{Key: argName, Value: &argVal}} + cmd := &instructions.ArgCommand{Args: []instructions.KeyValuePairOptional{{Key: argName, Value: &argVal}}} err := dispatch(sb, cmd) assert.NilError(t, err) @@ -436,7 +439,14 @@ func TestRunWithBuildArgs(t *testing.T) { runConfig := &container.Config{} origCmd := strslice.StrSlice([]string{"cmd", "in", "from", "image"}) - cmdWithShell := strslice.StrSlice(append(getShell(runConfig, runtime.GOOS), "echo foo")) + + var cmdWithShell strslice.StrSlice + if runtime.GOOS == "windows" { + cmdWithShell = strslice.StrSlice([]string{strings.Join(append(getShell(runConfig, runtime.GOOS), []string{"echo foo"}...), " ")}) + } else { + cmdWithShell = strslice.StrSlice(append(getShell(runConfig, runtime.GOOS), "echo foo")) + } + envVars := []string{"|1", "one=two"} cachedCmd := strslice.StrSlice(append(envVars, cmdWithShell...)) @@ -478,13 +488,24 @@ func TestRunWithBuildArgs(t *testing.T) { err := initializeStage(sb, from) assert.NilError(t, err) sb.state.buildArgs.AddArg("one", strPtr("two")) - run := &instructions.RunCommand{ - ShellDependantCmdLine: instructions.ShellDependantCmdLine{ - CmdLine: strslice.StrSlice{"echo foo"}, - PrependShell: true, - }, - } - assert.NilError(t, dispatch(sb, run)) + + // This is hugely annoying. On the Windows side, it relies on the + // RunCommand being able to emit String() and Name() (as implemented by + // withNameAndCode). Unfortunately, that is internal, and no way to directly + // set. However, we can fortunately use ParseInstruction in the instructions + // package to parse a fake node which can be used as our instructions.RunCommand + // instead. + node := &parser.Node{ + Original: `RUN echo foo`, + Value: "run", + } + runint, err := instructions.ParseInstruction(node) + assert.NilError(t, err) + runinst := runint.(*instructions.RunCommand) + runinst.CmdLine = strslice.StrSlice{"echo foo"} + runinst.PrependShell = true + + assert.NilError(t, dispatch(sb, runinst)) // Check that runConfig.Cmd has not been modified by run assert.Check(t, is.DeepEqual(origCmd, sb.state.runConfig.Cmd)) @@ -526,11 +547,19 @@ func TestRunIgnoresHealthcheck(t *testing.T) { assert.NilError(t, err) expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"} - cmd := &instructions.HealthCheckCommand{ - Health: &container.HealthConfig{ - Test: expectedTest, + healthint, err := instructions.ParseInstruction(&parser.Node{ + Original: `HEALTHCHECK CMD curl -f http://localhost/ || exit 1`, + Value: "healthcheck", + Next: &parser.Node{ + Value: "cmd", + Next: &parser.Node{ + Value: `curl -f http://localhost/ || exit 1`, + }, }, - } + }) + assert.NilError(t, err) + cmd := healthint.(*instructions.HealthCheckCommand) + assert.NilError(t, dispatch(sb, cmd)) assert.Assert(t, sb.state.runConfig.Healthcheck != nil) @@ -541,12 +570,57 @@ func TestRunIgnoresHealthcheck(t *testing.T) { } sb.state.buildArgs.AddArg("one", strPtr("two")) - run := &instructions.RunCommand{ - ShellDependantCmdLine: instructions.ShellDependantCmdLine{ - CmdLine: strslice.StrSlice{"echo foo"}, - PrependShell: true, - }, - } + runint, err := instructions.ParseInstruction(&parser.Node{Original: `RUN echo foo`, Value: "run"}) + assert.NilError(t, err) + run := runint.(*instructions.RunCommand) + run.PrependShell = true + assert.NilError(t, dispatch(sb, run)) assert.Check(t, is.DeepEqual(expectedTest, sb.state.runConfig.Healthcheck.Test)) } + +func TestDispatchUnsupportedOptions(t *testing.T) { + b := newBuilderWithMockBackend() + sb := newDispatchRequest(b, '`', nil, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) + sb.state.baseImage = &mockImage{} + sb.state.operatingSystem = runtime.GOOS + + t.Run("ADD with chmod", func(t *testing.T) { + cmd := &instructions.AddCommand{ + SourcesAndDest: instructions.SourcesAndDest{ + SourcePaths: []string{"."}, + DestPath: ".", + }, + Chmod: "0655", + } + err := dispatch(sb, cmd) + assert.Error(t, err, "the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled") + }) + + t.Run("COPY with chmod", func(t *testing.T) { + cmd := &instructions.CopyCommand{ + SourcesAndDest: instructions.SourcesAndDest{ + SourcePaths: []string{"."}, + DestPath: ".", + }, + Chmod: "0655", + } + err := dispatch(sb, cmd) + assert.Error(t, err, "the --chmod option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled") + }) + + t.Run("RUN with unsupported options", func(t *testing.T) { + runint, err := instructions.ParseInstruction(&parser.Node{Original: `RUN echo foo`, Value: "run"}) + assert.NilError(t, err) + cmd := runint.(*instructions.RunCommand) + + // classic builder "RUN" currently doesn't support any flags, but testing + // both "known" flags and "bogus" flags for completeness, and in case + // one or more of these flags will be supported in future + for _, f := range []string{"mount", "network", "security", "any-flag"} { + cmd.FlagsUsed = []string{f} + err := dispatch(sb, cmd) + assert.Error(t, err, fmt.Sprintf("the --%s option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled", f)) + } + }) +} diff --git a/builder/dockerfile/dispatchers_unix.go b/builder/dockerfile/dispatchers_unix.go index b3ba380323abd..87dbe72192927 100644 --- a/builder/dockerfile/dispatchers_unix.go +++ b/builder/dockerfile/dispatchers_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package dockerfile // import "github.com/docker/docker/builder/dockerfile" @@ -6,6 +7,9 @@ import ( "errors" "os" "path/filepath" + + "github.com/docker/docker/api/types/container" + "github.com/moby/buildkit/frontend/dockerfile/instructions" ) // normalizeWorkdir normalizes a user requested working directory in a @@ -21,3 +25,13 @@ func normalizeWorkdir(_ string, current string, requested string) (string, error } return requested, nil } + +// resolveCmdLine takes a command line arg set and optionally prepends a platform-specific +// shell in front of it. +func resolveCmdLine(cmd instructions.ShellDependantCmdLine, runConfig *container.Config, os, _, _ string) ([]string, bool) { + result := cmd.CmdLine + if cmd.PrependShell && result != nil { + result = append(getShell(runConfig, os), result...) + } + return result, false +} diff --git a/builder/dockerfile/dispatchers_unix_test.go b/builder/dockerfile/dispatchers_unix_test.go index c2aebfbb27314..3f39e26929720 100644 --- a/builder/dockerfile/dispatchers_unix_test.go +++ b/builder/dockerfile/dispatchers_unix_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package dockerfile // import "github.com/docker/docker/builder/dockerfile" diff --git a/builder/dockerfile/dispatchers_windows.go b/builder/dockerfile/dispatchers_windows.go index 7824d1169b4f1..4800ec9b8d193 100644 --- a/builder/dockerfile/dispatchers_windows.go +++ b/builder/dockerfile/dispatchers_windows.go @@ -9,7 +9,9 @@ import ( "regexp" "strings" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/system" + "github.com/moby/buildkit/frontend/dockerfile/instructions" ) var pattern = regexp.MustCompile(`^[a-zA-Z]:\.$`) @@ -93,3 +95,47 @@ func normalizeWorkdirWindows(current string, requested string) (string, error) { // Upper-case drive letter return (strings.ToUpper(string(requested[0])) + requested[1:]), nil } + +// resolveCmdLine takes a command line arg set and optionally prepends a platform-specific +// shell in front of it. It returns either an array of arguments and an indication that +// the arguments are not yet escaped; Or, an array containing a single command line element +// along with an indication that the arguments are escaped so the runtime shouldn't escape. +// +// A better solution could be made, but it would be exceptionally invasive throughout +// many parts of the daemon which are coded assuming Linux args array only only, not taking +// account of Windows-natural command line semantics and it's argv handling. Put another way, +// while what is here is good-enough, it could be improved, but would be highly invasive. +// +// The commands when this function is called are RUN, ENTRYPOINT and CMD. +func resolveCmdLine(cmd instructions.ShellDependantCmdLine, runConfig *container.Config, os, command, original string) ([]string, bool) { + + // Make sure we return an empty array if there is no cmd.CmdLine + if len(cmd.CmdLine) == 0 { + return []string{}, runConfig.ArgsEscaped + } + + if os == "windows" { // ie WCOW + if cmd.PrependShell { + // WCOW shell-form. Return a single-element array containing the original command line prepended with the shell. + // Also indicate that it has not been escaped (so will be passed through directly to HCS). Note that + // we go back to the original un-parsed command line in the dockerfile line, strip off both the command part of + // it (RUN/ENTRYPOINT/CMD), and also strip any leading white space. IOW, we deliberately ignore any prior parsing + // so as to ensure it is treated exactly as a command line. For those interested, `RUN mkdir "c:/foo"` is a particularly + // good example of why this is necessary if you fancy debugging how cmd.exe and its builtin mkdir works. (Windows + // doesn't have a mkdir.exe, and I'm guessing cmd.exe has some very long unavoidable and unchangeable historical + // design decisions over how both its built-in echo and mkdir are coded. Probably more too.) + original = original[len(command):] // Strip off the command + original = strings.TrimLeft(original, " \t\v\n") // Strip of leading whitespace + return []string{strings.Join(getShell(runConfig, os), " ") + " " + original}, true + } + + // WCOW JSON/"exec" form. + return cmd.CmdLine, false + } + + // LCOW - use args as an array, same as LCOL. + if cmd.PrependShell && cmd.CmdLine != nil { + return append(getShell(runConfig, os), cmd.CmdLine...), false + } + return cmd.CmdLine, false +} diff --git a/builder/dockerfile/dispatchers_windows_test.go b/builder/dockerfile/dispatchers_windows_test.go index ae72092c4f44b..aef8b047dd99b 100644 --- a/builder/dockerfile/dispatchers_windows_test.go +++ b/builder/dockerfile/dispatchers_windows_test.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package dockerfile // import "github.com/docker/docker/builder/dockerfile" diff --git a/builder/dockerfile/evaluator_test.go b/builder/dockerfile/evaluator_test.go index 28607ead0d220..27e01954d0bf2 100644 --- a/builder/dockerfile/evaluator_test.go +++ b/builder/dockerfile/evaluator_test.go @@ -9,9 +9,9 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" "github.com/moby/buildkit/frontend/dockerfile/instructions" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/skip" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/skip" ) type dispatchTestCase struct { @@ -24,14 +24,16 @@ func init() { reexec.Init() } -func initDispatchTestCases() []dispatchTestCase { - dispatchTestCases := []dispatchTestCase{ +func TestDispatch(t *testing.T) { + if runtime.GOOS != "windows" { + skip.If(t, os.Getuid() != 0, "skipping test that requires root") + } + testCases := []dispatchTestCase{ { name: "ADD multiple files to file", cmd: &instructions.AddCommand{SourcesAndDest: instructions.SourcesAndDest{ - "file1.txt", - "file2.txt", - "test", + SourcePaths: []string{"file1.txt", "file2.txt"}, + DestPath: "test", }}, expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /", files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"}, @@ -39,8 +41,8 @@ func initDispatchTestCases() []dispatchTestCase { { name: "Wildcard ADD multiple files to file", cmd: &instructions.AddCommand{SourcesAndDest: instructions.SourcesAndDest{ - "file*.txt", - "test", + SourcePaths: []string{"file*.txt"}, + DestPath: "test", }}, expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /", files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"}, @@ -48,9 +50,8 @@ func initDispatchTestCases() []dispatchTestCase { { name: "COPY multiple files to file", cmd: &instructions.CopyCommand{SourcesAndDest: instructions.SourcesAndDest{ - "file1.txt", - "file2.txt", - "test", + SourcePaths: []string{"file1.txt", "file2.txt"}, + DestPath: "test", }}, expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /", files: map[string]string{"file1.txt": "test1", "file2.txt": "test2"}, @@ -58,9 +59,8 @@ func initDispatchTestCases() []dispatchTestCase { { name: "ADD multiple files to file with whitespace", cmd: &instructions.AddCommand{SourcesAndDest: instructions.SourcesAndDest{ - "test file1.txt", - "test file2.txt", - "test", + SourcePaths: []string{"test file1.txt", "test file2.txt"}, + DestPath: "test", }}, expectedError: "When using ADD with more than one source file, the destination must be a directory and end with a /", files: map[string]string{"test file1.txt": "test1", "test file2.txt": "test2"}, @@ -68,9 +68,8 @@ func initDispatchTestCases() []dispatchTestCase { { name: "COPY multiple files to file with whitespace", cmd: &instructions.CopyCommand{SourcesAndDest: instructions.SourcesAndDest{ - "test file1.txt", - "test file2.txt", - "test", + SourcePaths: []string{"test file1.txt", "test file2.txt"}, + DestPath: "test", }}, expectedError: "When using COPY with more than one source file, the destination must be a directory and end with a /", files: map[string]string{"test file1.txt": "test1", "test file2.txt": "test2"}, @@ -78,8 +77,8 @@ func initDispatchTestCases() []dispatchTestCase { { name: "COPY wildcard no files", cmd: &instructions.CopyCommand{SourcesAndDest: instructions.SourcesAndDest{ - "file*.txt", - "/tmp/", + SourcePaths: []string{"file*.txt"}, + DestPath: "/tmp/", }}, expectedError: "COPY failed: no source files were specified", files: nil, @@ -87,61 +86,51 @@ func initDispatchTestCases() []dispatchTestCase { { name: "COPY url", cmd: &instructions.CopyCommand{SourcesAndDest: instructions.SourcesAndDest{ - "https://index.docker.io/robots.txt", - "/", + SourcePaths: []string{"https://index.docker.io/robots.txt"}, + DestPath: "/", }}, expectedError: "source can't be a URL for COPY", files: nil, - }} - - return dispatchTestCases -} - -func TestDispatch(t *testing.T) { - if runtime.GOOS != "windows" { - skip.If(t, os.Getuid() != 0, "skipping test that requires root") + }, } - testCases := initDispatchTestCases() - for _, testCase := range testCases { - executeTestCase(t, testCase) - } -} + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") + defer cleanup() -func executeTestCase(t *testing.T, testCase dispatchTestCase) { - contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") - defer cleanup() + for filename, content := range tc.files { + createTestTempFile(t, contextDir, filename, content, 0777) + } - for filename, content := range testCase.files { - createTestTempFile(t, contextDir, filename, content, 0777) - } + tarStream, err := archive.Tar(contextDir, archive.Uncompressed) - tarStream, err := archive.Tar(contextDir, archive.Uncompressed) + if err != nil { + t.Fatalf("Error when creating tar stream: %s", err) + } - if err != nil { - t.Fatalf("Error when creating tar stream: %s", err) - } - - defer func() { - if err = tarStream.Close(); err != nil { - t.Fatalf("Error when closing tar stream: %s", err) - } - }() + defer func() { + if err = tarStream.Close(); err != nil { + t.Fatalf("Error when closing tar stream: %s", err) + } + }() - context, err := remotecontext.FromArchive(tarStream) + context, err := remotecontext.FromArchive(tarStream) - if err != nil { - t.Fatalf("Error when creating tar context: %s", err) - } + if err != nil { + t.Fatalf("Error when creating tar context: %s", err) + } - defer func() { - if err = context.Close(); err != nil { - t.Fatalf("Error when closing tar context: %s", err) - } - }() + defer func() { + if err = context.Close(); err != nil { + t.Fatalf("Error when closing tar context: %s", err) + } + }() - b := newBuilderWithMockBackend() - sb := newDispatchRequest(b, '`', context, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) - err = dispatch(sb, testCase.cmd) - assert.Check(t, is.ErrorContains(err, testCase.expectedError)) + b := newBuilderWithMockBackend() + sb := newDispatchRequest(b, '`', context, NewBuildArgs(make(map[string]*string)), newStagesBuildResults()) + err = dispatch(sb, tc.cmd) + assert.Check(t, is.ErrorContains(err, tc.expectedError)) + }) + } } diff --git a/builder/dockerfile/imagecontext.go b/builder/dockerfile/imagecontext.go index 08cb396a2bdfb..9d9a6c618ce5e 100644 --- a/builder/dockerfile/imagecontext.go +++ b/builder/dockerfile/imagecontext.go @@ -4,6 +4,7 @@ import ( "context" "runtime" + "github.com/containerd/containerd/platforms" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/builder" dockerimage "github.com/docker/docker/image" @@ -56,7 +57,7 @@ func (m *imageSources) Get(idOrRef string, localOnly bool, platform *specs.Platf return nil, err } im := newImageMount(image, layer) - m.Add(im) + m.Add(im, platform) return im, nil } @@ -70,16 +71,26 @@ func (m *imageSources) Unmount() (retErr error) { return } -func (m *imageSources) Add(im *imageMount) { +func (m *imageSources) Add(im *imageMount, platform *specs.Platform) { switch im.image { case nil: - // set the OS for scratch images - os := runtime.GOOS + // Set the platform for scratch images + if platform == nil { + p := platforms.DefaultSpec() + platform = &p + } + // Windows does not support scratch except for LCOW + os := platform.OS if runtime.GOOS == "windows" { os = "linux" } - im.image = &dockerimage.Image{V1Image: dockerimage.V1Image{OS: os}} + + im.image = &dockerimage.Image{V1Image: dockerimage.V1Image{ + OS: os, + Architecture: platform.Architecture, + Variant: platform.Variant, + }} default: m.byImageID[im.image.ImageID()] = im } @@ -88,9 +99,8 @@ func (m *imageSources) Add(im *imageMount) { // imageMount is a reference to an image that can be used as a builder.Source type imageMount struct { - image builder.Image - source builder.Source - layer builder.ROLayer + image builder.Image + layer builder.ROLayer } func newImageMount(image builder.Image, layer builder.ROLayer) *imageMount { diff --git a/builder/dockerfile/imagecontext_test.go b/builder/dockerfile/imagecontext_test.go new file mode 100644 index 0000000000000..ad4de31f2b8f9 --- /dev/null +++ b/builder/dockerfile/imagecontext_test.go @@ -0,0 +1,106 @@ +package dockerfile // import "github.com/docker/docker/builder/dockerfile" + +import ( + "fmt" + "runtime" + "testing" + + "github.com/containerd/containerd/platforms" + "github.com/docker/docker/builder" + "github.com/docker/docker/image" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "gotest.tools/v3/assert" +) + +func getMockImageSource(getImageImage builder.Image, getImageLayer builder.ROLayer, getImageError error) *imageSources { + return &imageSources{ + byImageID: make(map[string]*imageMount), + mounts: []*imageMount{}, + getImage: func(name string, localOnly bool, platform *ocispec.Platform) (builder.Image, builder.ROLayer, error) { + return getImageImage, getImageLayer, getImageError + }, + } +} + +func getMockImageMount() *imageMount { + return &imageMount{ + image: nil, + layer: nil, + } +} + +func TestAddScratchImageAddsToMounts(t *testing.T) { + is := getMockImageSource(nil, nil, fmt.Errorf("getImage is not implemented")) + im := getMockImageMount() + + // We are testing whether the imageMount is added to is.mounts + assert.Equal(t, len(is.mounts), 0) + is.Add(im, nil) + assert.Equal(t, len(is.mounts), 1) +} + +func TestAddFromScratchPopulatesPlatform(t *testing.T) { + is := getMockImageSource(nil, nil, fmt.Errorf("getImage is not implemented")) + + platforms := []*ocispec.Platform{ + { + OS: "linux", + Architecture: "amd64", + }, + { + OS: "linux", + Architecture: "arm64", + Variant: "v8", + }, + } + + for i, platform := range platforms { + im := getMockImageMount() + assert.Equal(t, len(is.mounts), i) + is.Add(im, platform) + image, ok := im.image.(*image.Image) + assert.Assert(t, ok) + assert.Equal(t, image.OS, platform.OS) + assert.Equal(t, image.Architecture, platform.Architecture) + assert.Equal(t, image.Variant, platform.Variant) + } +} + +func TestAddFromScratchDoesNotModifyArgPlatform(t *testing.T) { + is := getMockImageSource(nil, nil, fmt.Errorf("getImage is not implemented")) + im := getMockImageMount() + + platform := &ocispec.Platform{ + OS: "windows", + Architecture: "amd64", + } + argPlatform := *platform + + is.Add(im, &argPlatform) + // The way the code is written right now, this test + // really doesn't do much except on Windows. + assert.DeepEqual(t, *platform, argPlatform) +} + +func TestAddFromScratchPopulatesPlatformIfNil(t *testing.T) { + is := getMockImageSource(nil, nil, fmt.Errorf("getImage is not implemented")) + im := getMockImageMount() + is.Add(im, nil) + image, ok := im.image.(*image.Image) + assert.Assert(t, ok) + + expectedPlatform := platforms.DefaultSpec() + if runtime.GOOS == "windows" { + expectedPlatform.OS = "linux" + } + assert.Equal(t, expectedPlatform.OS, image.OS) + assert.Equal(t, expectedPlatform.Architecture, image.Architecture) + assert.Equal(t, expectedPlatform.Variant, image.Variant) +} + +func TestImageSourceGetAddsToMounts(t *testing.T) { + is := getMockImageSource(nil, nil, nil) + _, err := is.Get("test", false, nil) + assert.NilError(t, err) + assert.Equal(t, len(is.mounts), 1) +} diff --git a/builder/dockerfile/internals.go b/builder/dockerfile/internals.go index 1635981f177a2..0bc9d0e9efaee 100644 --- a/builder/dockerfile/internals.go +++ b/builder/dockerfile/internals.go @@ -8,10 +8,6 @@ import ( "encoding/hex" "fmt" "io" - "os" - "path" - "path/filepath" - "runtime" "strings" "github.com/docker/docker/api/types" @@ -24,8 +20,8 @@ import ( "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/pkg/system" "github.com/docker/go-connections/nat" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -117,15 +113,21 @@ func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, paren return err } - // add an image mount without an image so the layer is properly unmounted - // if there is an error before we can add the full mount with image - b.imageSources.Add(newImageMount(nil, newLayer)) - parentImage, ok := parent.(*image.Image) if !ok { return errors.Errorf("unexpected image type") } + platform := &specs.Platform{ + OS: parentImage.OS, + Architecture: parentImage.Architecture, + Variant: parentImage.Variant, + } + + // add an image mount without an image so the layer is properly unmounted + // if there is an error before we can add the full mount with image + b.imageSources.Add(newImageMount(nil, newLayer), platform) + newImage := image.NewChildImage(parentImage, image.ChildConfig{ Author: state.maintainer, ContainerConfig: runConfig, @@ -146,7 +148,7 @@ func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, paren } state.imageID = exportedImage.ImageID() - b.imageSources.Add(newImageMount(exportedImage, newLayer)) + b.imageSources.Add(newImageMount(exportedImage, newLayer), platform) return nil } @@ -204,7 +206,9 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error { opts := copyFileOptions{ decompress: inst.allowLocalDecompression, archiver: b.getArchiver(info.root, destInfo.root), - identity: identity, + } + if !inst.preserveOwnership { + opts.identity = &identity } if err := performCopyForInfo(destInfo, info, opts); err != nil { return errors.Wrapf(err, "failed to copy files") @@ -216,7 +220,7 @@ func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error { func createDestInfo(workingDir string, inst copyInstruction, rwLayer builder.RWLayer, platform string) (copyInfo, error) { // Twiddle the destination when it's a relative path - meaning, make it // relative to the WORKINGDIR - dest, err := normalizeDest(workingDir, inst.dest, platform) + dest, err := normalizeDest(workingDir, inst.dest) if err != nil { return copyInfo{}, errors.Wrapf(err, "invalid %s", inst.cmdName) } @@ -224,63 +228,6 @@ func createDestInfo(workingDir string, inst copyInstruction, rwLayer builder.RWL return copyInfo{root: rwLayer.Root(), path: dest}, nil } -// normalizeDest normalises the destination of a COPY/ADD command in a -// platform semantically consistent way. -func normalizeDest(workingDir, requested string, platform string) (string, error) { - dest := fromSlash(requested, platform) - endsInSlash := strings.HasSuffix(dest, string(separator(platform))) - - if platform != "windows" { - if !path.IsAbs(requested) { - dest = path.Join("/", filepath.ToSlash(workingDir), dest) - // Make sure we preserve any trailing slash - if endsInSlash { - dest += "/" - } - } - return dest, nil - } - - // We are guaranteed that the working directory is already consistent, - // However, Windows also has, for now, the limitation that ADD/COPY can - // only be done to the system drive, not any drives that might be present - // as a result of a bind mount. - // - // So... if the path requested is Linux-style absolute (/foo or \\foo), - // we assume it is the system drive. If it is a Windows-style absolute - // (DRIVE:\\foo), error if DRIVE is not C. And finally, ensure we - // strip any configured working directories drive letter so that it - // can be subsequently legitimately converted to a Windows volume-style - // pathname. - - // Not a typo - filepath.IsAbs, not system.IsAbs on this next check as - // we only want to validate where the DriveColon part has been supplied. - if filepath.IsAbs(dest) { - if strings.ToUpper(string(dest[0])) != "C" { - return "", fmt.Errorf("Windows does not support destinations not on the system drive (C:)") - } - dest = dest[2:] // Strip the drive letter - } - - // Cannot handle relative where WorkingDir is not the system drive. - if len(workingDir) > 0 { - if ((len(workingDir) > 1) && !system.IsAbs(workingDir[2:])) || (len(workingDir) == 1) { - return "", fmt.Errorf("Current WorkingDir %s is not platform consistent", workingDir) - } - if !system.IsAbs(dest) { - if string(workingDir[0]) != "C" { - return "", fmt.Errorf("Windows does not support relative paths when WORKDIR is not the system drive") - } - dest = filepath.Join(string(os.PathSeparator), workingDir[2:], dest) - // Make sure we preserve any trailing slash - if endsInSlash { - dest += string(os.PathSeparator) - } - } - } - return dest, nil -} - // For backwards compat, if there's just one info then use it as the // cache look-up string, otherwise hash 'em all into one func getSourceHashFromInfos(infos []copyInfo) string { @@ -308,6 +255,12 @@ func withCmd(cmd []string) runConfigModifier { } } +func withArgsEscaped(argsEscaped bool) runConfigModifier { + return func(runConfig *container.Config) { + runConfig.ArgsEscaped = argsEscaped + } +} + // withCmdComment sets Cmd to a nop comment string. See withCmdCommentString for // why there are two almost identical versions of this. func withCmdComment(comment string, platform string) runConfigModifier { @@ -433,8 +386,7 @@ func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *contai func (b *Builder) create(runConfig *container.Config) (string, error) { logrus.Debugf("[BUILDER] Command to be executed: %v", runConfig.Cmd) - isWCOW := runtime.GOOS == "windows" && b.platform != nil && b.platform.OS == "windows" - hostConfig := hostConfigFromOptions(b.options, isWCOW) + hostConfig := hostConfigFromOptions(b.options) container, err := b.containerManager.Create(runConfig, hostConfig) if err != nil { return "", err @@ -447,7 +399,7 @@ func (b *Builder) create(runConfig *container.Config) (string, error) { return container.ID, nil } -func hostConfigFromOptions(options *types.ImageBuildOptions, isWCOW bool) *container.HostConfig { +func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConfig { resources := container.Resources{ CgroupParent: options.CgroupParent, CPUShares: options.CPUShares, @@ -470,31 +422,5 @@ func hostConfigFromOptions(options *types.ImageBuildOptions, isWCOW bool) *conta LogConfig: defaultLogConfig, ExtraHosts: options.ExtraHosts, } - - // For WCOW, the default of 20GB hard-coded in the platform - // is too small for builder scenarios where many users are - // using RUN statements to install large amounts of data. - // Use 127GB as that's the default size of a VHD in Hyper-V. - if isWCOW { - hc.StorageOpt = make(map[string]string) - hc.StorageOpt["size"] = "127GB" - } - return hc } - -// fromSlash works like filepath.FromSlash but with a given OS platform field -func fromSlash(path, platform string) string { - if platform == "windows" { - return strings.Replace(path, "/", "\\", -1) - } - return path -} - -// separator returns a OS path separator for the given OS platform -func separator(platform string) byte { - if platform == "windows" { - return '\\' - } - return '/' -} diff --git a/builder/dockerfile/internals_linux.go b/builder/dockerfile/internals_linux.go index 5dbd86f147009..269f722d61c2c 100644 --- a/builder/dockerfile/internals_linux.go +++ b/builder/dockerfile/internals_linux.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/symlink" + "github.com/moby/sys/symlink" lcUser "github.com/opencontainers/runc/libcontainer/user" "github.com/pkg/errors" ) diff --git a/builder/dockerfile/internals_linux_test.go b/builder/dockerfile/internals_linux_test.go index 28c9cc81a8c86..6a2a8f6a8917d 100644 --- a/builder/dockerfile/internals_linux_test.go +++ b/builder/dockerfile/internals_linux_test.go @@ -7,8 +7,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/idtools" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestChownFlagParsing(t *testing.T) { diff --git a/builder/dockerfile/internals_test.go b/builder/dockerfile/internals_test.go index b1ef6c80d82ec..5353167ad637b 100644 --- a/builder/dockerfile/internals_test.go +++ b/builder/dockerfile/internals_test.go @@ -11,11 +11,15 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/builder" "github.com/docker/docker/builder/remotecontext" + "github.com/docker/docker/image" + "github.com/docker/docker/layer" "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/containerfs" "github.com/docker/go-connections/nat" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/skip" + "github.com/opencontainers/go-digest" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/skip" ) func TestEmptyDockerfile(t *testing.T) { @@ -46,7 +50,7 @@ func TestDockerfileOutsideTheBuildContext(t *testing.T) { contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test") defer cleanup() - expectedError := "Forbidden path outside the build context: ../../Dockerfile ()" + expectedError := "path outside the build context: ../../Dockerfile ()" if runtime.GOOS == "windows" { expectedError = "failed to resolve scoped path ../../Dockerfile ()" } @@ -176,3 +180,45 @@ func TestDeepCopyRunConfig(t *testing.T) { copy.Shell[0] = "sh" assert.Check(t, is.DeepEqual(fullMutableRunConfig(), runConfig)) } + +type MockRWLayer struct{} + +func (l *MockRWLayer) Release() error { return nil } +func (l *MockRWLayer) Root() containerfs.ContainerFS { return nil } +func (l *MockRWLayer) Commit() (builder.ROLayer, error) { + return &MockROLayer{ + diffID: layer.DiffID(digest.Digest("sha256:1234")), + }, nil +} + +type MockROLayer struct { + diffID layer.DiffID +} + +func (l *MockROLayer) Release() error { return nil } +func (l *MockROLayer) NewRWLayer() (builder.RWLayer, error) { return nil, nil } +func (l *MockROLayer) DiffID() layer.DiffID { return l.diffID } + +func getMockBuildBackend() builder.Backend { + return &MockBackend{} +} + +func TestExportImage(t *testing.T) { + ds := newDispatchState(NewBuildArgs(map[string]*string{})) + layer := &MockRWLayer{} + parentImage := &image.Image{ + V1Image: image.V1Image{ + OS: "linux", + Architecture: "arm64", + Variant: "v8", + }, + } + runConfig := &container.Config{} + + b := &Builder{ + imageSources: getMockImageSource(nil, nil, nil), + docker: getMockBuildBackend(), + } + err := b.exportImage(ds, layer, parentImage, runConfig) + assert.NilError(t, err) +} diff --git a/builder/dockerfile/internals_windows.go b/builder/dockerfile/internals_windows.go index 82135a12ae689..0d26e69ef3e8d 100644 --- a/builder/dockerfile/internals_windows.go +++ b/builder/dockerfile/internals_windows.go @@ -11,8 +11,6 @@ import ( "github.com/docker/docker/api/types/mount" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/pkg/system" - "github.com/pkg/errors" "golang.org/x/sys/windows" ) @@ -31,13 +29,7 @@ func getAccountIdentity(builder *Builder, accountName string, ctrRootPath string sid, err := windows.StringToSid(accountName) if err == nil { - accountSid, err := sid.String() - - if err != nil { - return idtools.Identity{SID: ""}, errors.Wrapf(err, "error converting SID to string") - } - - return idtools.Identity{SID: accountSid}, nil + return idtools.Identity{SID: sid.String()}, nil } } @@ -46,21 +38,15 @@ func getAccountIdentity(builder *Builder, accountName string, ctrRootPath string // If this is a SID that is built-in and hence the same across all systems then use that. if err == nil && (accType == windows.SidTypeAlias || accType == windows.SidTypeWellKnownGroup) { - accountSid, err := sid.String() - - if err != nil { - return idtools.Identity{SID: ""}, errors.Wrapf(err, "error converting SID to string") - } - - return idtools.Identity{SID: accountSid}, nil + return idtools.Identity{SID: sid.String()}, nil } // Check if the account name is one unique to containers. if strings.EqualFold(accountName, "ContainerAdministrator") { - return idtools.Identity{SID: system.ContainerAdministratorSidString}, nil + return idtools.Identity{SID: idtools.ContainerAdministratorSidString}, nil } else if strings.EqualFold(accountName, "ContainerUser") { - return idtools.Identity{SID: system.ContainerUserSidString}, nil + return idtools.Identity{SID: idtools.ContainerUserSidString}, nil } // All other lookups failed, so therefore determine if the account in @@ -83,7 +69,6 @@ func lookupNTAccount(builder *Builder, accountName string, state *dispatchState) runConfig := copyRunConfig(state.runConfig, withCmdCommentString("internal run to obtain NT account information.", optionsPlatform.OS)) - runConfig.ArgsEscaped = true runConfig.Cmd = []string{targetExecutable, "getaccountsid", accountName} hostConfig := &container.HostConfig{Mounts: []mount.Mount{ diff --git a/builder/dockerfile/internals_windows_test.go b/builder/dockerfile/internals_windows_test.go index 4f00623404dbb..59d84e5cce018 100644 --- a/builder/dockerfile/internals_windows_test.go +++ b/builder/dockerfile/internals_windows_test.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package dockerfile // import "github.com/docker/docker/builder/dockerfile" @@ -6,8 +7,8 @@ import ( "fmt" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestNormalizeDest(t *testing.T) { @@ -40,7 +41,7 @@ func TestNormalizeDest(t *testing.T) { } for _, testcase := range tests { msg := fmt.Sprintf("Input: %s, %s", testcase.current, testcase.requested) - actual, err := normalizeDest(testcase.current, testcase.requested, "windows") + actual, err := normalizeDest(testcase.current, testcase.requested) if testcase.etext == "" { if !assert.Check(t, err, msg) { continue diff --git a/builder/dockerfile/metrics.go b/builder/dockerfile/metrics.go index ceafa7ad629e5..eb5b5562f3742 100644 --- a/builder/dockerfile/metrics.go +++ b/builder/dockerfile/metrics.go @@ -1,7 +1,7 @@ package dockerfile // import "github.com/docker/docker/builder/dockerfile" import ( - "github.com/docker/go-metrics" + metrics "github.com/docker/go-metrics" ) var ( diff --git a/builder/dockerfile/mockbackend_test.go b/builder/dockerfile/mockbackend_test.go index 45cba00a8c016..81f74175a205a 100644 --- a/builder/dockerfile/mockbackend_test.go +++ b/builder/dockerfile/mockbackend_test.go @@ -28,7 +28,7 @@ func (m *MockBackend) ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout return nil } -func (m *MockBackend) ContainerCreate(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) { +func (m *MockBackend) ContainerCreateIgnoreImagesArgsEscaped(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) { if m.containerCreateFunc != nil { return m.containerCreateFunc(config) } @@ -82,7 +82,7 @@ func (m *MockBackend) MakeImageCache(cacheFrom []string) builder.ImageCache { } func (m *MockBackend) CreateImage(config []byte, parent string) (builder.Image, error) { - return nil, nil + return &mockImage{id: "test"}, nil } type mockImage struct { @@ -104,7 +104,7 @@ func (i *mockImage) OperatingSystem() string { func (i *mockImage) MarshalJSON() ([]byte, error) { type rawImage mockImage - return json.Marshal(rawImage(*i)) + return json.Marshal(rawImage(*i)) //nolint:staticcheck } type mockImageCache struct { diff --git a/builder/dockerfile/utils_test.go b/builder/dockerfile/utils_test.go index 3d615f3460f24..98bdda2a7eb54 100644 --- a/builder/dockerfile/utils_test.go +++ b/builder/dockerfile/utils_test.go @@ -1,7 +1,6 @@ package dockerfile // import "github.com/docker/docker/builder/dockerfile" import ( - "io/ioutil" "os" "path/filepath" "testing" @@ -11,7 +10,7 @@ import ( // It returns the created path and a cleanup function which is meant to be used as deferred call. // When an error occurs, it terminates the test. func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) { - path, err := ioutil.TempDir(dir, prefix) + path, err := os.MkdirTemp(dir, prefix) if err != nil { t.Fatalf("Error when creating directory %s with prefix %s: %s", dir, prefix, err) @@ -30,7 +29,7 @@ func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) { // When an error occurs, it terminates the test func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string { filePath := filepath.Join(dir, filename) - err := ioutil.WriteFile(filePath, []byte(contents), perm) + err := os.WriteFile(filePath, []byte(contents), perm) if err != nil { t.Fatalf("Error when creating %s file: %s", filename, err) diff --git a/builder/dockerignore/deprecated.go b/builder/dockerignore/deprecated.go new file mode 100644 index 0000000000000..e387cc8ed24d9 --- /dev/null +++ b/builder/dockerignore/deprecated.go @@ -0,0 +1,17 @@ +// Package dockerignore is deprecated. Use github.com/moby/buildkit/frontend/dockerfile/dockerignore instead. +package dockerignore + +import ( + "io" + + "github.com/moby/buildkit/frontend/dockerfile/dockerignore" +) + +// ReadAll reads a .dockerignore file and returns the list of file patterns +// to ignore. Note this will trim whitespace from each line as well +// as use GO's "clean" func to get the shortest/cleanest path for each. +// +// Deprecated: use github.com/moby/buildkit/frontend/dockerfile/dockerignore.ReadAll instead. +func ReadAll(reader io.Reader) ([]string, error) { + return dockerignore.ReadAll(reader) +} diff --git a/builder/dockerignore/dockerignore_test.go b/builder/dockerignore/dockerignore_test.go deleted file mode 100644 index 06186cc12082a..0000000000000 --- a/builder/dockerignore/dockerignore_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package dockerignore // import "github.com/docker/docker/builder/dockerignore" - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "testing" -) - -func TestReadAll(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "dockerignore-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) - - di, err := ReadAll(nil) - if err != nil { - t.Fatalf("Expected not to have error, got %v", err) - } - - if diLen := len(di); diLen != 0 { - t.Fatalf("Expected to have zero dockerignore entry, got %d", diLen) - } - - diName := filepath.Join(tmpDir, ".dockerignore") - content := fmt.Sprintf("test1\n/test2\n/a/file/here\n\nlastfile\n# this is a comment\n! /inverted/abs/path\n!\n! \n") - err = ioutil.WriteFile(diName, []byte(content), 0777) - if err != nil { - t.Fatal(err) - } - - diFd, err := os.Open(diName) - if err != nil { - t.Fatal(err) - } - defer diFd.Close() - - di, err = ReadAll(diFd) - if err != nil { - t.Fatal(err) - } - - if len(di) != 7 { - t.Fatalf("Expected 5 entries, got %v", len(di)) - } - if di[0] != "test1" { - t.Fatal("First element is not test1") - } - if di[1] != "test2" { // according to https://docs.docker.com/engine/reference/builder/#dockerignore-file, /foo/bar should be treated as foo/bar - t.Fatal("Second element is not test2") - } - if di[2] != "a/file/here" { // according to https://docs.docker.com/engine/reference/builder/#dockerignore-file, /foo/bar should be treated as foo/bar - t.Fatal("Third element is not a/file/here") - } - if di[3] != "lastfile" { - t.Fatal("Fourth element is not lastfile") - } - if di[4] != "!inverted/abs/path" { - t.Fatal("Fifth element is not !inverted/abs/path") - } - if di[5] != "!" { - t.Fatalf("Sixth element is not !, but %s", di[5]) - } - if di[6] != "!" { - t.Fatalf("Sixth element is not !, but %s", di[6]) - } -} diff --git a/builder/fscache/fscache.go b/builder/fscache/fscache.go deleted file mode 100644 index 8897fe16d89bf..0000000000000 --- a/builder/fscache/fscache.go +++ /dev/null @@ -1,653 +0,0 @@ -package fscache // import "github.com/docker/docker/builder/fscache" - -import ( - "archive/tar" - "context" - "crypto/sha256" - "encoding/json" - "hash" - "os" - "path/filepath" - "sort" - "sync" - "time" - - "github.com/docker/docker/builder" - "github.com/docker/docker/builder/remotecontext" - "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/pkg/directory" - "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/pkg/tarsum" - "github.com/moby/buildkit/session/filesync" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/tonistiigi/fsutil" - fsutiltypes "github.com/tonistiigi/fsutil/types" - bolt "go.etcd.io/bbolt" - "golang.org/x/sync/singleflight" -) - -const dbFile = "fscache.db" -const cacheKey = "cache" -const metaKey = "meta" - -// Backend is a backing implementation for FSCache -type Backend interface { - Get(id string) (string, error) - Remove(id string) error -} - -// FSCache allows syncing remote resources to cached snapshots -type FSCache struct { - opt Opt - transports map[string]Transport - mu sync.Mutex - g singleflight.Group - store *fsCacheStore -} - -// Opt defines options for initializing FSCache -type Opt struct { - Backend Backend - Root string // for storing local metadata - GCPolicy GCPolicy -} - -// GCPolicy defines policy for garbage collection -type GCPolicy struct { - MaxSize uint64 - MaxKeepDuration time.Duration -} - -// NewFSCache returns new FSCache object -func NewFSCache(opt Opt) (*FSCache, error) { - store, err := newFSCacheStore(opt) - if err != nil { - return nil, err - } - return &FSCache{ - store: store, - opt: opt, - transports: make(map[string]Transport), - }, nil -} - -// Transport defines a method for syncing remote data to FSCache -type Transport interface { - Copy(ctx context.Context, id RemoteIdentifier, dest string, cs filesync.CacheUpdater) error -} - -// RemoteIdentifier identifies a transfer request -type RemoteIdentifier interface { - Key() string - SharedKey() string - Transport() string -} - -// RegisterTransport registers a new transport method -func (fsc *FSCache) RegisterTransport(id string, transport Transport) error { - fsc.mu.Lock() - defer fsc.mu.Unlock() - if _, ok := fsc.transports[id]; ok { - return errors.Errorf("transport %v already exists", id) - } - fsc.transports[id] = transport - return nil -} - -// SyncFrom returns a source based on a remote identifier -func (fsc *FSCache) SyncFrom(ctx context.Context, id RemoteIdentifier) (builder.Source, error) { // cacheOpt - trasportID := id.Transport() - fsc.mu.Lock() - transport, ok := fsc.transports[id.Transport()] - if !ok { - fsc.mu.Unlock() - return nil, errors.Errorf("invalid transport %s", trasportID) - } - - logrus.Debugf("SyncFrom %s %s", id.Key(), id.SharedKey()) - fsc.mu.Unlock() - sourceRef, err, _ := fsc.g.Do(id.Key(), func() (interface{}, error) { - var sourceRef *cachedSourceRef - sourceRef, err := fsc.store.Get(id.Key()) - if err == nil { - return sourceRef, nil - } - - // check for unused shared cache - sharedKey := id.SharedKey() - if sharedKey != "" { - r, err := fsc.store.Rebase(sharedKey, id.Key()) - if err == nil { - sourceRef = r - } - } - - if sourceRef == nil { - var err error - sourceRef, err = fsc.store.New(id.Key(), sharedKey) - if err != nil { - return nil, errors.Wrap(err, "failed to create remote context") - } - } - - if err := syncFrom(ctx, sourceRef, transport, id); err != nil { - sourceRef.Release() - return nil, err - } - if err := sourceRef.resetSize(-1); err != nil { - return nil, err - } - return sourceRef, nil - }) - if err != nil { - return nil, err - } - ref := sourceRef.(*cachedSourceRef) - if ref.src == nil { // failsafe - return nil, errors.Errorf("invalid empty pull") - } - wc := &wrappedContext{Source: ref.src, closer: func() error { - ref.Release() - return nil - }} - return wc, nil -} - -// DiskUsage reports how much data is allocated by the cache -func (fsc *FSCache) DiskUsage(ctx context.Context) (int64, error) { - return fsc.store.DiskUsage(ctx) -} - -// Prune allows manually cleaning up the cache -func (fsc *FSCache) Prune(ctx context.Context) (uint64, error) { - return fsc.store.Prune(ctx) -} - -// Close stops the gc and closes the persistent db -func (fsc *FSCache) Close() error { - return fsc.store.Close() -} - -func syncFrom(ctx context.Context, cs *cachedSourceRef, transport Transport, id RemoteIdentifier) (retErr error) { - src := cs.src - if src == nil { - src = remotecontext.NewCachableSource(cs.Dir()) - } - - if !cs.cached { - if err := cs.storage.db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte(id.Key())) - dt := b.Get([]byte(cacheKey)) - if dt != nil { - if err := src.UnmarshalBinary(dt); err != nil { - return err - } - } else { - return errors.Wrap(src.Scan(), "failed to scan cache records") - } - return nil - }); err != nil { - return err - } - } - - dc := &detectChanges{f: src.HandleChange} - - // todo: probably send a bucket to `Copy` and let it return source - // but need to make sure that tx is safe - if err := transport.Copy(ctx, id, cs.Dir(), dc); err != nil { - return errors.Wrapf(err, "failed to copy to %s", cs.Dir()) - } - - if !dc.supported { - if err := src.Scan(); err != nil { - return errors.Wrap(err, "failed to scan cache records after transfer") - } - } - cs.cached = true - cs.src = src - return cs.storage.db.Update(func(tx *bolt.Tx) error { - dt, err := src.MarshalBinary() - if err != nil { - return err - } - b := tx.Bucket([]byte(id.Key())) - return b.Put([]byte(cacheKey), dt) - }) -} - -type fsCacheStore struct { - mu sync.Mutex - sources map[string]*cachedSource - db *bolt.DB - fs Backend - gcTimer *time.Timer - gcPolicy GCPolicy -} - -// CachePolicy defines policy for keeping a resource in cache -type CachePolicy struct { - Priority int - LastUsed time.Time -} - -func defaultCachePolicy() CachePolicy { - return CachePolicy{Priority: 10, LastUsed: time.Now()} -} - -func newFSCacheStore(opt Opt) (*fsCacheStore, error) { - if err := os.MkdirAll(opt.Root, 0700); err != nil { - return nil, err - } - p := filepath.Join(opt.Root, dbFile) - db, err := bolt.Open(p, 0600, nil) - if err != nil { - return nil, errors.Wrap(err, "failed to open database file %s") - } - s := &fsCacheStore{db: db, sources: make(map[string]*cachedSource), fs: opt.Backend, gcPolicy: opt.GCPolicy} - db.View(func(tx *bolt.Tx) error { - return tx.ForEach(func(name []byte, b *bolt.Bucket) error { - dt := b.Get([]byte(metaKey)) - if dt == nil { - return nil - } - var sm sourceMeta - if err := json.Unmarshal(dt, &sm); err != nil { - return err - } - dir, err := s.fs.Get(sm.BackendID) - if err != nil { - return err // TODO: handle gracefully - } - source := &cachedSource{ - refs: make(map[*cachedSourceRef]struct{}), - id: string(name), - dir: dir, - sourceMeta: sm, - storage: s, - } - s.sources[string(name)] = source - return nil - }) - }) - - s.gcTimer = s.startPeriodicGC(5 * time.Minute) - return s, nil -} - -func (s *fsCacheStore) startPeriodicGC(interval time.Duration) *time.Timer { - var t *time.Timer - t = time.AfterFunc(interval, func() { - if err := s.GC(); err != nil { - logrus.Errorf("build gc error: %v", err) - } - t.Reset(interval) - }) - return t -} - -func (s *fsCacheStore) Close() error { - s.gcTimer.Stop() - return s.db.Close() -} - -func (s *fsCacheStore) New(id, sharedKey string) (*cachedSourceRef, error) { - s.mu.Lock() - defer s.mu.Unlock() - var ret *cachedSource - if err := s.db.Update(func(tx *bolt.Tx) error { - b, err := tx.CreateBucket([]byte(id)) - if err != nil { - return err - } - backendID := stringid.GenerateRandomID() - dir, err := s.fs.Get(backendID) - if err != nil { - return err - } - source := &cachedSource{ - refs: make(map[*cachedSourceRef]struct{}), - id: id, - dir: dir, - sourceMeta: sourceMeta{ - BackendID: backendID, - SharedKey: sharedKey, - CachePolicy: defaultCachePolicy(), - }, - storage: s, - } - dt, err := json.Marshal(source.sourceMeta) - if err != nil { - return err - } - if err := b.Put([]byte(metaKey), dt); err != nil { - return err - } - s.sources[id] = source - ret = source - return nil - }); err != nil { - return nil, err - } - return ret.getRef(), nil -} - -func (s *fsCacheStore) Rebase(sharedKey, newid string) (*cachedSourceRef, error) { - s.mu.Lock() - defer s.mu.Unlock() - var ret *cachedSource - for id, snap := range s.sources { - if snap.SharedKey == sharedKey && len(snap.refs) == 0 { - if err := s.db.Update(func(tx *bolt.Tx) error { - if err := tx.DeleteBucket([]byte(id)); err != nil { - return err - } - b, err := tx.CreateBucket([]byte(newid)) - if err != nil { - return err - } - snap.id = newid - snap.CachePolicy = defaultCachePolicy() - dt, err := json.Marshal(snap.sourceMeta) - if err != nil { - return err - } - if err := b.Put([]byte(metaKey), dt); err != nil { - return err - } - delete(s.sources, id) - s.sources[newid] = snap - return nil - }); err != nil { - return nil, err - } - ret = snap - break - } - } - if ret == nil { - return nil, errors.Errorf("no candidate for rebase") - } - return ret.getRef(), nil -} - -func (s *fsCacheStore) Get(id string) (*cachedSourceRef, error) { - s.mu.Lock() - defer s.mu.Unlock() - src, ok := s.sources[id] - if !ok { - return nil, errors.Errorf("not found") - } - return src.getRef(), nil -} - -// DiskUsage reports how much data is allocated by the cache -func (s *fsCacheStore) DiskUsage(ctx context.Context) (int64, error) { - s.mu.Lock() - defer s.mu.Unlock() - var size int64 - - for _, snap := range s.sources { - if len(snap.refs) == 0 { - ss, err := snap.getSize(ctx) - if err != nil { - return 0, err - } - size += ss - } - } - return size, nil -} - -// Prune allows manually cleaning up the cache -func (s *fsCacheStore) Prune(ctx context.Context) (uint64, error) { - s.mu.Lock() - defer s.mu.Unlock() - var size uint64 - - for id, snap := range s.sources { - select { - case <-ctx.Done(): - logrus.Debugf("Cache prune operation cancelled, pruned size: %d", size) - // when the context is cancelled, only return current size and nil - return size, nil - default: - } - if len(snap.refs) == 0 { - ss, err := snap.getSize(ctx) - if err != nil { - return size, err - } - if err := s.delete(id); err != nil { - return size, errors.Wrapf(err, "failed to delete %s", id) - } - size += uint64(ss) - } - } - return size, nil -} - -// GC runs a garbage collector on FSCache -func (s *fsCacheStore) GC() error { - s.mu.Lock() - defer s.mu.Unlock() - var size uint64 - - ctx := context.Background() - cutoff := time.Now().Add(-s.gcPolicy.MaxKeepDuration) - var blacklist []*cachedSource - - for id, snap := range s.sources { - if len(snap.refs) == 0 { - if cutoff.After(snap.CachePolicy.LastUsed) { - if err := s.delete(id); err != nil { - return errors.Wrapf(err, "failed to delete %s", id) - } - } else { - ss, err := snap.getSize(ctx) - if err != nil { - return err - } - size += uint64(ss) - blacklist = append(blacklist, snap) - } - } - } - - sort.Sort(sortableCacheSources(blacklist)) - for _, snap := range blacklist { - if size <= s.gcPolicy.MaxSize { - break - } - ss, err := snap.getSize(ctx) - if err != nil { - return err - } - if err := s.delete(snap.id); err != nil { - return errors.Wrapf(err, "failed to delete %s", snap.id) - } - size -= uint64(ss) - } - return nil -} - -// keep mu while calling this -func (s *fsCacheStore) delete(id string) error { - src, ok := s.sources[id] - if !ok { - return nil - } - if len(src.refs) > 0 { - return errors.Errorf("can't delete %s because it has active references", id) - } - delete(s.sources, id) - if err := s.db.Update(func(tx *bolt.Tx) error { - return tx.DeleteBucket([]byte(id)) - }); err != nil { - return err - } - return s.fs.Remove(src.BackendID) -} - -type sourceMeta struct { - SharedKey string - BackendID string - CachePolicy CachePolicy - Size int64 -} - -type cachedSource struct { - sourceMeta - refs map[*cachedSourceRef]struct{} - id string - dir string - src *remotecontext.CachableSource - storage *fsCacheStore - cached bool // keep track if cache is up to date -} - -type cachedSourceRef struct { - *cachedSource -} - -func (cs *cachedSource) Dir() string { - return cs.dir -} - -// hold storage lock before calling -func (cs *cachedSource) getRef() *cachedSourceRef { - ref := &cachedSourceRef{cachedSource: cs} - cs.refs[ref] = struct{}{} - return ref -} - -// hold storage lock before calling -func (cs *cachedSource) getSize(ctx context.Context) (int64, error) { - if cs.sourceMeta.Size < 0 { - ss, err := directory.Size(ctx, cs.dir) - if err != nil { - return 0, err - } - if err := cs.resetSize(ss); err != nil { - return 0, err - } - return ss, nil - } - return cs.sourceMeta.Size, nil -} - -func (cs *cachedSource) resetSize(val int64) error { - cs.sourceMeta.Size = val - return cs.saveMeta() -} -func (cs *cachedSource) saveMeta() error { - return cs.storage.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte(cs.id)) - dt, err := json.Marshal(cs.sourceMeta) - if err != nil { - return err - } - return b.Put([]byte(metaKey), dt) - }) -} - -func (csr *cachedSourceRef) Release() error { - csr.cachedSource.storage.mu.Lock() - defer csr.cachedSource.storage.mu.Unlock() - delete(csr.cachedSource.refs, csr) - if len(csr.cachedSource.refs) == 0 { - go csr.cachedSource.storage.GC() - } - return nil -} - -type detectChanges struct { - f fsutil.ChangeFunc - supported bool -} - -func (dc *detectChanges) HandleChange(kind fsutil.ChangeKind, path string, fi os.FileInfo, err error) error { - if dc == nil { - return nil - } - return dc.f(kind, path, fi, err) -} - -func (dc *detectChanges) MarkSupported(v bool) { - if dc == nil { - return - } - dc.supported = v -} - -func (dc *detectChanges) ContentHasher() fsutil.ContentHasher { - return newTarsumHash -} - -type wrappedContext struct { - builder.Source - closer func() error -} - -func (wc *wrappedContext) Close() error { - if err := wc.Source.Close(); err != nil { - return err - } - return wc.closer() -} - -type sortableCacheSources []*cachedSource - -// Len is the number of elements in the collection. -func (s sortableCacheSources) Len() int { - return len(s) -} - -// Less reports whether the element with -// index i should sort before the element with index j. -func (s sortableCacheSources) Less(i, j int) bool { - return s[i].CachePolicy.LastUsed.Before(s[j].CachePolicy.LastUsed) -} - -// Swap swaps the elements with indexes i and j. -func (s sortableCacheSources) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func newTarsumHash(stat *fsutiltypes.Stat) (hash.Hash, error) { - fi := &fsutil.StatInfo{Stat: stat} - p := stat.Path - if fi.IsDir() { - p += string(os.PathSeparator) - } - h, err := archive.FileInfoHeader(p, fi, stat.Linkname) - if err != nil { - return nil, err - } - h.Name = p - h.Uid = int(stat.Uid) - h.Gid = int(stat.Gid) - h.Linkname = stat.Linkname - if stat.Xattrs != nil { - h.Xattrs = make(map[string]string) - for k, v := range stat.Xattrs { - h.Xattrs[k] = string(v) - } - } - - tsh := &tarsumHash{h: h, Hash: sha256.New()} - tsh.Reset() - return tsh, nil -} - -// Reset resets the Hash to its initial state. -func (tsh *tarsumHash) Reset() { - tsh.Hash.Reset() - tarsum.WriteV1Header(tsh.h, tsh.Hash) -} - -type tarsumHash struct { - hash.Hash - h *tar.Header -} diff --git a/builder/fscache/fscache_test.go b/builder/fscache/fscache_test.go deleted file mode 100644 index 5108d65df1576..0000000000000 --- a/builder/fscache/fscache_test.go +++ /dev/null @@ -1,132 +0,0 @@ -package fscache // import "github.com/docker/docker/builder/fscache" - -import ( - "context" - "io/ioutil" - "os" - "path/filepath" - "testing" - "time" - - "github.com/moby/buildkit/session/filesync" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" -) - -func TestFSCache(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "fscache") - assert.Check(t, err) - defer os.RemoveAll(tmpDir) - - backend := NewNaiveCacheBackend(filepath.Join(tmpDir, "backend")) - - opt := Opt{ - Root: tmpDir, - Backend: backend, - GCPolicy: GCPolicy{MaxSize: 15, MaxKeepDuration: time.Hour}, - } - - fscache, err := NewFSCache(opt) - assert.Check(t, err) - - defer fscache.Close() - - err = fscache.RegisterTransport("test", &testTransport{}) - assert.Check(t, err) - - src1, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo", "data", "bar"}) - assert.Check(t, err) - - dt, err := ioutil.ReadFile(filepath.Join(src1.Root().Path(), "foo")) - assert.Check(t, err) - assert.Check(t, is.Equal(string(dt), "data")) - - // same id doesn't recalculate anything - src2, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo", "data2", "bar"}) - assert.Check(t, err) - assert.Check(t, is.Equal(src1.Root().Path(), src2.Root().Path())) - - dt, err = ioutil.ReadFile(filepath.Join(src1.Root().Path(), "foo")) - assert.Check(t, err) - assert.Check(t, is.Equal(string(dt), "data")) - assert.Check(t, src2.Close()) - - src3, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo2", "data2", "bar"}) - assert.Check(t, err) - assert.Check(t, src1.Root().Path() != src3.Root().Path()) - - dt, err = ioutil.ReadFile(filepath.Join(src3.Root().Path(), "foo2")) - assert.Check(t, err) - assert.Check(t, is.Equal(string(dt), "data2")) - - s, err := fscache.DiskUsage(context.TODO()) - assert.Check(t, err) - assert.Check(t, is.Equal(s, int64(0))) - - assert.Check(t, src3.Close()) - - s, err = fscache.DiskUsage(context.TODO()) - assert.Check(t, err) - assert.Check(t, is.Equal(s, int64(5))) - - // new upload with the same shared key shoutl overwrite - src4, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo3", "data3", "bar"}) - assert.Check(t, err) - assert.Check(t, src1.Root().Path() != src3.Root().Path()) - - dt, err = ioutil.ReadFile(filepath.Join(src3.Root().Path(), "foo3")) - assert.Check(t, err) - assert.Check(t, is.Equal(string(dt), "data3")) - assert.Check(t, is.Equal(src4.Root().Path(), src3.Root().Path())) - assert.Check(t, src4.Close()) - - s, err = fscache.DiskUsage(context.TODO()) - assert.Check(t, err) - assert.Check(t, is.Equal(s, int64(10))) - - // this one goes over the GC limit - src5, err := fscache.SyncFrom(context.TODO(), &testIdentifier{"foo4", "datadata", "baz"}) - assert.Check(t, err) - assert.Check(t, src5.Close()) - - // GC happens async - time.Sleep(100 * time.Millisecond) - - // only last insertion after GC - s, err = fscache.DiskUsage(context.TODO()) - assert.Check(t, err) - assert.Check(t, is.Equal(s, int64(8))) - - // prune deletes everything - released, err := fscache.Prune(context.TODO()) - assert.Check(t, err) - assert.Check(t, is.Equal(released, uint64(8))) - - s, err = fscache.DiskUsage(context.TODO()) - assert.Check(t, err) - assert.Check(t, is.Equal(s, int64(0))) -} - -type testTransport struct { -} - -func (t *testTransport) Copy(ctx context.Context, id RemoteIdentifier, dest string, cs filesync.CacheUpdater) error { - testid := id.(*testIdentifier) - return ioutil.WriteFile(filepath.Join(dest, testid.filename), []byte(testid.data), 0600) -} - -type testIdentifier struct { - filename string - data string - sharedKey string -} - -func (t *testIdentifier) Key() string { - return t.filename -} -func (t *testIdentifier) SharedKey() string { - return t.sharedKey -} -func (t *testIdentifier) Transport() string { - return "test" -} diff --git a/builder/fscache/naivedriver.go b/builder/fscache/naivedriver.go deleted file mode 100644 index 053509aecffe9..0000000000000 --- a/builder/fscache/naivedriver.go +++ /dev/null @@ -1,28 +0,0 @@ -package fscache // import "github.com/docker/docker/builder/fscache" - -import ( - "os" - "path/filepath" - - "github.com/pkg/errors" -) - -// NewNaiveCacheBackend is a basic backend implementation for fscache -func NewNaiveCacheBackend(root string) Backend { - return &naiveCacheBackend{root: root} -} - -type naiveCacheBackend struct { - root string -} - -func (tcb *naiveCacheBackend) Get(id string) (string, error) { - d := filepath.Join(tcb.root, id) - if err := os.MkdirAll(d, 0700); err != nil { - return "", errors.Wrapf(err, "failed to create tmp dir for %s", d) - } - return d, nil -} -func (tcb *naiveCacheBackend) Remove(id string) error { - return errors.WithStack(os.RemoveAll(filepath.Join(tcb.root, id))) -} diff --git a/builder/remotecontext/detect.go b/builder/remotecontext/detect.go index 144eb570abde0..09f4d28434637 100644 --- a/builder/remotecontext/detect.go +++ b/builder/remotecontext/detect.go @@ -11,9 +11,10 @@ import ( "github.com/containerd/continuity/driver" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/builder" - "github.com/docker/docker/builder/dockerignore" + "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/urlutil" + "github.com/moby/buildkit/frontend/dockerfile/dockerignore" "github.com/moby/buildkit/frontend/dockerfile/parser" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -32,11 +33,7 @@ func Detect(config backend.BuildConfig) (remote builder.Source, dockerfile *pars case remoteURL == "": remote, dockerfile, err = newArchiveRemote(config.Source, dockerfilePath) case remoteURL == ClientSessionRemote: - res, err := parser.Parse(config.Source) - if err != nil { - return nil, nil, err - } - return nil, res, nil + return nil, nil, errdefs.InvalidParameter(errors.New("experimental session with v1 builder is no longer supported, use builder version v2 (BuildKit) instead")) case urlutil.IsGitURL(remoteURL): remote, dockerfile, err = newGitRemote(remoteURL, dockerfilePath) case urlutil.IsURL(remoteURL): @@ -60,7 +57,7 @@ func newArchiveRemote(rc io.ReadCloser, dockerfilePath string) (builder.Source, func withDockerfileFromContext(c modifiableContext, dockerfilePath string) (builder.Source, *parser.Result, error) { df, err := openAt(c, dockerfilePath) if err != nil { - if os.IsNotExist(err) { + if errors.Is(err, os.ErrNotExist) { if dockerfilePath == builder.DefaultDockerfileName { lowercase := strings.ToLower(dockerfilePath) if _, err := StatAt(c, lowercase); err == nil { @@ -106,7 +103,7 @@ func newURLRemote(url string, dockerfilePath string, progressReader func(in io.R switch contentType { case mimeTypes.TextPlain: res, err := parser.Parse(progressReader(content)) - return nil, res, err + return nil, res, errdefs.InvalidParameter(err) default: source, err := FromArchive(progressReader(content)) if err != nil { @@ -133,7 +130,7 @@ func removeDockerfile(c modifiableContext, filesToRemove ...string) error { f.Close() filesToRemove = append([]string{".dockerignore"}, filesToRemove...) for _, fileToRemove := range filesToRemove { - if rm, _ := fileutils.Matches(fileToRemove, excludes); rm { + if rm, _ := fileutils.MatchesOrParentMatches(fileToRemove, excludes); rm { if err := c.Remove(fileToRemove); err != nil { logrus.Errorf("failed to remove %s: %v", fileToRemove, err) } @@ -146,11 +143,17 @@ func readAndParseDockerfile(name string, rc io.Reader) (*parser.Result, error) { br := bufio.NewReader(rc) if _, err := br.Peek(1); err != nil { if err == io.EOF { - return nil, errors.Errorf("the Dockerfile (%s) cannot be empty", name) + return nil, errdefs.InvalidParameter(errors.Errorf("the Dockerfile (%s) cannot be empty", name)) } return nil, errors.Wrap(err, "unexpected error reading Dockerfile") } - return parser.Parse(br) + + dockerfile, err := parser.Parse(br) + if err != nil { + return nil, errdefs.InvalidParameter(errors.Wrapf(err, "failed to parse %s", name)) + } + + return dockerfile, nil } func openAt(remote builder.Source, path string) (driver.File, error) { @@ -177,7 +180,7 @@ func FullPath(remote builder.Source, path string) (string, error) { if runtime.GOOS == "windows" { return "", fmt.Errorf("failed to resolve scoped path %s (%s): %s. Possible cause is a forbidden path outside the build context", path, fullPath, err) } - return "", fmt.Errorf("Forbidden path outside the build context: %s (%s)", path, fullPath) // backwards compat with old error + return "", fmt.Errorf("forbidden path outside the build context: %s (%s)", path, fullPath) // backwards compat with old error } return fullPath, nil } diff --git a/builder/remotecontext/detect_test.go b/builder/remotecontext/detect_test.go index 04b7686c7ad6c..71dfd7bbb9306 100644 --- a/builder/remotecontext/detect_test.go +++ b/builder/remotecontext/detect_test.go @@ -2,7 +2,6 @@ package remotecontext // import "github.com/docker/docker/builder/remotecontext" import ( "errors" - "io/ioutil" "log" "os" "sort" @@ -20,7 +19,7 @@ const ( const shouldStayFilename = "should_stay" -func extractFilenames(files []os.FileInfo) []string { +func extractFilenames(files []os.DirEntry) []string { filenames := make([]string, len(files)) for i, file := range files { @@ -31,7 +30,7 @@ func extractFilenames(files []os.FileInfo) []string { } func checkDirectory(t *testing.T, dir string, expectedFiles []string) { - files, err := ioutil.ReadDir(dir) + files, err := os.ReadDir(dir) if err != nil { t.Fatalf("Could not read directory: %s", err) diff --git a/builder/remotecontext/git/gitutils.go b/builder/remotecontext/git/gitutils.go index 77a45beff31cc..1dd07851ed199 100644 --- a/builder/remotecontext/git/gitutils.go +++ b/builder/remotecontext/git/gitutils.go @@ -1,17 +1,15 @@ package git // import "github.com/docker/docker/builder/remotecontext/git" import ( - "io/ioutil" "net/http" "net/url" "os" - "os/exec" "path/filepath" "strings" - "github.com/docker/docker/pkg/symlink" - "github.com/docker/docker/pkg/urlutil" + "github.com/moby/sys/symlink" "github.com/pkg/errors" + exec "golang.org/x/sys/execabs" ) type gitRepo struct { @@ -35,7 +33,7 @@ func Clone(remoteURL string) (string, error) { func cloneGitRepo(repo gitRepo) (checkoutDir string, err error) { fetch := fetchArgs(repo.remote, repo.ref) - root, err := ioutil.TempDir("", "docker-build-git") + root, err := os.MkdirTemp("", "docker-build-git") if err != nil { return "", err } @@ -102,6 +100,11 @@ func parseRemoteURL(remoteURL string) (gitRepo, error) { u.Fragment = "" repo.remote = u.String() } + + if strings.HasPrefix(repo.ref, "-") { + return gitRepo{}, errors.Errorf("invalid refspec: %s", repo.ref) + } + return repo, nil } @@ -124,22 +127,22 @@ func fetchArgs(remoteURL string, ref string) []string { args = append(args, "--depth", "1") } - return append(args, "origin", ref) + return append(args, "origin", "--", ref) } // Check if a given git URL supports a shallow git clone, // i.e. it is a non-HTTP server or a smart HTTP server. func supportsShallowClone(remoteURL string) bool { - if urlutil.IsURL(remoteURL) { + if scheme := getScheme(remoteURL); scheme == "http" || scheme == "https" { // Check if the HTTP server is smart // Smart servers must correctly respond to a query for the git-upload-pack service serviceURL := remoteURL + "/info/refs?service=git-upload-pack" // Try a HEAD request and fallback to a Get request on error - res, err := http.Head(serviceURL) + res, err := http.Head(serviceURL) // #nosec G107 if err != nil || res.StatusCode != http.StatusOK { - res, err = http.Get(serviceURL) + res, err = http.Get(serviceURL) // #nosec G107 if err == nil { res.Body.Close() } @@ -200,5 +203,24 @@ func git(args ...string) ([]byte, error) { // isGitTransport returns true if the provided str is a git transport by inspecting // the prefix of the string for known protocols used in git. func isGitTransport(str string) bool { - return urlutil.IsURL(str) || strings.HasPrefix(str, "git://") || strings.HasPrefix(str, "git@") + if strings.HasPrefix(str, "git@") { + return true + } + + switch getScheme(str) { + case "git", "http", "https", "ssh": + return true + } + + return false +} + +// getScheme returns addresses' scheme in lowercase, or an empty +// string in case address is an invalid URL. +func getScheme(address string) string { + u, err := url.Parse(address) + if err != nil { + return "" + } + return u.Scheme } diff --git a/builder/remotecontext/git/gitutils_test.go b/builder/remotecontext/git/gitutils_test.go index 8c39679081f1e..17df6fa867e14 100644 --- a/builder/remotecontext/git/gitutils_test.go +++ b/builder/remotecontext/git/gitutils_test.go @@ -2,7 +2,6 @@ package git // import "github.com/docker/docker/builder/remotecontext/git" import ( "fmt" - "io/ioutil" "net/http" "net/http/httptest" "net/url" @@ -14,38 +13,113 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestParseRemoteURL(t *testing.T) { - dir, err := parseRemoteURL("git://github.com/user/repo.git") - assert.NilError(t, err) - assert.Check(t, is.DeepEqual(gitRepo{"git://github.com/user/repo.git", "master", ""}, dir, cmpGitRepoOpt)) - - dir, err = parseRemoteURL("git://github.com/user/repo.git#mybranch:mydir/mysubdir/") - assert.NilError(t, err) - assert.Check(t, is.DeepEqual(gitRepo{"git://github.com/user/repo.git", "mybranch", "mydir/mysubdir/"}, dir, cmpGitRepoOpt)) - - dir, err = parseRemoteURL("https://github.com/user/repo.git") - assert.NilError(t, err) - assert.Check(t, is.DeepEqual(gitRepo{"https://github.com/user/repo.git", "master", ""}, dir, cmpGitRepoOpt)) - - dir, err = parseRemoteURL("https://github.com/user/repo.git#mybranch:mydir/mysubdir/") - assert.NilError(t, err) - assert.Check(t, is.DeepEqual(gitRepo{"https://github.com/user/repo.git", "mybranch", "mydir/mysubdir/"}, dir, cmpGitRepoOpt)) - - dir, err = parseRemoteURL("git@github.com:user/repo.git") - assert.NilError(t, err) - assert.Check(t, is.DeepEqual(gitRepo{"git@github.com:user/repo.git", "master", ""}, dir, cmpGitRepoOpt)) + tests := []struct { + doc string + url string + expected gitRepo + }{ + { + doc: "git scheme uppercase, no url-fragment", + url: "GIT://github.com/user/repo.git", + expected: gitRepo{ + remote: "git://github.com/user/repo.git", + ref: "master", + }, + }, + { + doc: "git scheme, no url-fragment", + url: "git://github.com/user/repo.git", + expected: gitRepo{ + remote: "git://github.com/user/repo.git", + ref: "master", + }, + }, + { + doc: "git scheme, with url-fragment", + url: "git://github.com/user/repo.git#mybranch:mydir/mysubdir/", + expected: gitRepo{ + remote: "git://github.com/user/repo.git", + ref: "mybranch", + subdir: "mydir/mysubdir/", + }, + }, + { + doc: "https scheme, no url-fragment", + url: "https://github.com/user/repo.git", + expected: gitRepo{ + remote: "https://github.com/user/repo.git", + ref: "master", + }, + }, + { + doc: "https scheme, with url-fragment", + url: "https://github.com/user/repo.git#mybranch:mydir/mysubdir/", + expected: gitRepo{ + remote: "https://github.com/user/repo.git", + ref: "mybranch", + subdir: "mydir/mysubdir/", + }, + }, + { + doc: "git@, no url-fragment", + url: "git@github.com:user/repo.git", + expected: gitRepo{ + remote: "git@github.com:user/repo.git", + ref: "master", + }, + }, + { + doc: "git@, with url-fragment", + url: "git@github.com:user/repo.git#mybranch:mydir/mysubdir/", + expected: gitRepo{ + remote: "git@github.com:user/repo.git", + ref: "mybranch", + subdir: "mydir/mysubdir/", + }, + }, + { + doc: "ssh, no url-fragment", + url: "ssh://github.com/user/repo.git", + expected: gitRepo{ + remote: "ssh://github.com/user/repo.git", + ref: "master", + }, + }, + { + doc: "ssh, with url-fragment", + url: "ssh://github.com/user/repo.git#mybranch:mydir/mysubdir/", + expected: gitRepo{ + remote: "ssh://github.com/user/repo.git", + ref: "mybranch", + subdir: "mydir/mysubdir/", + }, + }, + { + doc: "ssh, with url-fragment and user", + url: "ssh://foo%40barcorp.com@github.com/user/repo.git#mybranch:mydir/mysubdir/", + expected: gitRepo{ + remote: "ssh://foo%40barcorp.com@github.com/user/repo.git", + ref: "mybranch", + subdir: "mydir/mysubdir/", + }, + }, + } - dir, err = parseRemoteURL("git@github.com:user/repo.git#mybranch:mydir/mysubdir/") - assert.NilError(t, err) - assert.Check(t, is.DeepEqual(gitRepo{"git@github.com:user/repo.git", "mybranch", "mydir/mysubdir/"}, dir, cmpGitRepoOpt)) + for _, tc := range tests { + tc := tc + t.Run(tc.doc, func(t *testing.T) { + repo, err := parseRemoteURL(tc.url) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(tc.expected, repo, cmp.AllowUnexported(gitRepo{}))) + }) + } } -var cmpGitRepoOpt = cmp.AllowUnexported(gitRepo{}) - func TestCloneArgsSmartHttp(t *testing.T) { mux := http.NewServeMux() server := httptest.NewServer(mux) @@ -59,7 +133,7 @@ func TestCloneArgsSmartHttp(t *testing.T) { }) args := fetchArgs(serverURL.String(), "master") - exp := []string{"fetch", "--depth", "1", "origin", "master"} + exp := []string{"fetch", "--depth", "1", "origin", "--", "master"} assert.Check(t, is.DeepEqual(exp, args)) } @@ -75,13 +149,13 @@ func TestCloneArgsDumbHttp(t *testing.T) { }) args := fetchArgs(serverURL.String(), "master") - exp := []string{"fetch", "origin", "master"} + exp := []string{"fetch", "origin", "--", "master"} assert.Check(t, is.DeepEqual(exp, args)) } func TestCloneArgsGit(t *testing.T) { args := fetchArgs("git://github.com/docker/docker", "master") - exp := []string{"fetch", "--depth", "1", "origin", "master"} + exp := []string{"fetch", "--depth", "1", "origin", "--", "master"} assert.Check(t, is.DeepEqual(exp, args)) } @@ -96,7 +170,7 @@ func gitGetConfig(name string) string { } func TestCheckoutGit(t *testing.T) { - root, err := ioutil.TempDir("", "docker-build-git-checkout") + root, err := os.MkdirTemp("", "docker-build-git-checkout") assert.NilError(t, err) defer os.RemoveAll(root) @@ -120,13 +194,13 @@ func TestCheckoutGit(t *testing.T) { _, err = gitWithinDir(gitDir, "config", "user.name", "Docker test") assert.NilError(t, err) - err = ioutil.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte("FROM scratch"), 0644) + err = os.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte("FROM scratch"), 0644) assert.NilError(t, err) subDir := filepath.Join(gitDir, "subdir") assert.NilError(t, os.Mkdir(subDir, 0755)) - err = ioutil.WriteFile(filepath.Join(subDir, "Dockerfile"), []byte("FROM scratch\nEXPOSE 5000"), 0644) + err = os.WriteFile(filepath.Join(subDir, "Dockerfile"), []byte("FROM scratch\nEXPOSE 5000"), 0644) assert.NilError(t, err) if runtime.GOOS != "windows" { @@ -148,10 +222,10 @@ func TestCheckoutGit(t *testing.T) { _, err = gitWithinDir(gitDir, "checkout", "-b", "test") assert.NilError(t, err) - err = ioutil.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte("FROM scratch\nEXPOSE 3000"), 0644) + err = os.WriteFile(filepath.Join(gitDir, "Dockerfile"), []byte("FROM scratch\nEXPOSE 3000"), 0644) assert.NilError(t, err) - err = ioutil.WriteFile(filepath.Join(subDir, "Dockerfile"), []byte("FROM busybox\nEXPOSE 5000"), 0644) + err = os.WriteFile(filepath.Join(subDir, "Dockerfile"), []byte("FROM busybox\nEXPOSE 5000"), 0644) assert.NilError(t, err) _, err = gitWithinDir(gitDir, "add", "-A") @@ -174,7 +248,7 @@ func TestCheckoutGit(t *testing.T) { _, err = gitWithinDir(subrepoDir, "config", "user.name", "Docker test") assert.NilError(t, err) - err = ioutil.WriteFile(filepath.Join(subrepoDir, "subfile"), []byte("subcontents"), 0644) + err = os.WriteFile(filepath.Join(subrepoDir, "subfile"), []byte("subcontents"), 0644) assert.NilError(t, err) _, err = gitWithinDir(subrepoDir, "add", "-A") @@ -235,7 +309,7 @@ func TestCheckoutGit(t *testing.T) { assert.NilError(t, err) defer os.RemoveAll(r) if c.submodule { - b, err := ioutil.ReadFile(filepath.Join(r, "sub/subfile")) + b, err := os.ReadFile(filepath.Join(r, "sub/subfile")) assert.NilError(t, err) assert.Check(t, is.Equal("subcontents", string(b))) } else { @@ -244,7 +318,7 @@ func TestCheckoutGit(t *testing.T) { assert.Assert(t, os.IsNotExist(err)) } - b, err := ioutil.ReadFile(filepath.Join(r, "Dockerfile")) + b, err := os.ReadFile(filepath.Join(r, "Dockerfile")) assert.NilError(t, err) assert.Check(t, is.Equal(c.exp, string(b))) } @@ -276,3 +350,18 @@ func TestValidGitTransport(t *testing.T) { } } } + +func TestGitInvalidRef(t *testing.T) { + gitUrls := []string{ + "git://github.com/moby/moby#--foo bar", + "git@github.com/moby/moby#--upload-pack=sleep;:", + "git@g.com:a/b.git#-B", + "git@g.com:a/b.git#with space", + } + + for _, url := range gitUrls { + _, err := Clone(url) + assert.Assert(t, err != nil) + assert.Check(t, is.Contains(strings.ToLower(err.Error()), "invalid refspec")) + } +} diff --git a/builder/remotecontext/mimetype_test.go b/builder/remotecontext/mimetype_test.go index df9c37877091f..cbcf31807a44d 100644 --- a/builder/remotecontext/mimetype_test.go +++ b/builder/remotecontext/mimetype_test.go @@ -3,8 +3,8 @@ package remotecontext // import "github.com/docker/docker/builder/remotecontext" import ( "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestDetectContentType(t *testing.T) { diff --git a/builder/remotecontext/remote.go b/builder/remotecontext/remote.go index 1fb80549b8b26..6eeadf521dda5 100644 --- a/builder/remotecontext/remote.go +++ b/builder/remotecontext/remote.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "net" "net/http" "net/url" @@ -45,6 +44,7 @@ func downloadRemote(remoteURL string) (string, io.ReadCloser, error) { // GetWithStatusError does an http.Get() and returns an error if the // status code is 4xx or 5xx. func GetWithStatusError(address string) (resp *http.Response, err error) { + // #nosec G107 if resp, err = http.Get(address); err != nil { if uerr, ok := err.(*url.Error); ok { if derr, ok := uerr.Err.(*net.DNSError); ok && !derr.IsTimeout { @@ -57,7 +57,7 @@ func GetWithStatusError(address string) (resp *http.Response, err error) { return resp, nil } msg := fmt.Sprintf("failed to GET %s with status %s", address, resp.Status) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { return nil, errdefs.System(errors.New(msg + ": error reading body")) diff --git a/builder/remotecontext/remote_test.go b/builder/remotecontext/remote_test.go index a0101f7493853..a945181183187 100644 --- a/builder/remotecontext/remote_test.go +++ b/builder/remotecontext/remote_test.go @@ -3,19 +3,18 @@ package remotecontext // import "github.com/docker/docker/builder/remotecontext" import ( "bytes" "io" - "io/ioutil" "net/http" "net/http/httptest" "net/url" "testing" "github.com/docker/docker/builder" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/fs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/fs" ) -var binaryContext = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00} //xz magic +var binaryContext = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00} // xz magic func TestSelectAcceptableMIME(t *testing.T) { validMimeStrings := []string{ @@ -52,7 +51,7 @@ func TestSelectAcceptableMIME(t *testing.T) { func TestInspectEmptyResponse(t *testing.T) { ct := "application/octet-stream" - br := ioutil.NopCloser(bytes.NewReader([]byte(""))) + br := io.NopCloser(bytes.NewReader([]byte(""))) contentType, bReader, err := inspectResponse(ct, br, 0) if err == nil { t.Fatal("Should have generated an error for an empty response") @@ -60,7 +59,7 @@ func TestInspectEmptyResponse(t *testing.T) { if contentType != "application/octet-stream" { t.Fatalf("Content type should be 'application/octet-stream' but is %q", contentType) } - body, err := ioutil.ReadAll(bReader) + body, err := io.ReadAll(bReader) if err != nil { t.Fatal(err) } @@ -71,7 +70,7 @@ func TestInspectEmptyResponse(t *testing.T) { func TestInspectResponseBinary(t *testing.T) { ct := "application/octet-stream" - br := ioutil.NopCloser(bytes.NewReader(binaryContext)) + br := io.NopCloser(bytes.NewReader(binaryContext)) contentType, bReader, err := inspectResponse(ct, br, int64(len(binaryContext))) if err != nil { t.Fatal(err) @@ -79,7 +78,7 @@ func TestInspectResponseBinary(t *testing.T) { if contentType != "application/octet-stream" { t.Fatalf("Content type should be 'application/octet-stream' but is %q", contentType) } - body, err := ioutil.ReadAll(bReader) + body, err := io.ReadAll(bReader) if err != nil { t.Fatal(err) } @@ -96,7 +95,7 @@ func TestInspectResponseBinary(t *testing.T) { func TestResponseUnsupportedContentType(t *testing.T) { content := []byte(dockerfileContents) ct := "application/json" - br := ioutil.NopCloser(bytes.NewReader(content)) + br := io.NopCloser(bytes.NewReader(content)) contentType, bReader, err := inspectResponse(ct, br, int64(len(dockerfileContents))) if err == nil { @@ -105,7 +104,7 @@ func TestResponseUnsupportedContentType(t *testing.T) { if contentType != ct { t.Fatalf("Should not have altered content-type: orig: %s, altered: %s", ct, contentType) } - body, err := ioutil.ReadAll(bReader) + body, err := io.ReadAll(bReader) if err != nil { t.Fatal(err) } @@ -117,7 +116,7 @@ func TestResponseUnsupportedContentType(t *testing.T) { func TestInspectResponseTextSimple(t *testing.T) { content := []byte(dockerfileContents) ct := "text/plain" - br := ioutil.NopCloser(bytes.NewReader(content)) + br := io.NopCloser(bytes.NewReader(content)) contentType, bReader, err := inspectResponse(ct, br, int64(len(content))) if err != nil { t.Fatal(err) @@ -125,7 +124,7 @@ func TestInspectResponseTextSimple(t *testing.T) { if contentType != "text/plain" { t.Fatalf("Content type should be 'text/plain' but is %q", contentType) } - body, err := ioutil.ReadAll(bReader) + body, err := io.ReadAll(bReader) if err != nil { t.Fatal(err) } @@ -136,7 +135,7 @@ func TestInspectResponseTextSimple(t *testing.T) { func TestInspectResponseEmptyContentType(t *testing.T) { content := []byte(dockerfileContents) - br := ioutil.NopCloser(bytes.NewReader(content)) + br := io.NopCloser(bytes.NewReader(content)) contentType, bodyReader, err := inspectResponse("", br, int64(len(content))) if err != nil { t.Fatal(err) @@ -144,7 +143,7 @@ func TestInspectResponseEmptyContentType(t *testing.T) { if contentType != "text/plain" { t.Fatalf("Content type should be 'text/plain' but is %q", contentType) } - body, err := ioutil.ReadAll(bodyReader) + body, err := io.ReadAll(bodyReader) if err != nil { t.Fatal(err) } @@ -156,7 +155,7 @@ func TestInspectResponseEmptyContentType(t *testing.T) { func TestUnknownContentLength(t *testing.T) { content := []byte(dockerfileContents) ct := "text/plain" - br := ioutil.NopCloser(bytes.NewReader(content)) + br := io.NopCloser(bytes.NewReader(content)) contentType, bReader, err := inspectResponse(ct, br, -1) if err != nil { t.Fatal(err) @@ -164,7 +163,7 @@ func TestUnknownContentLength(t *testing.T) { if contentType != "text/plain" { t.Fatalf("Content type should be 'text/plain' but is %q", contentType) } - body, err := ioutil.ReadAll(bReader) + body, err := io.ReadAll(bReader) if err != nil { t.Fatal(err) } @@ -191,7 +190,7 @@ func TestDownloadRemote(t *testing.T) { assert.NilError(t, err) assert.Check(t, is.Equal(mimeTypes.TextPlain, contentType)) - raw, err := ioutil.ReadAll(content) + raw, err := io.ReadAll(content) assert.NilError(t, err) assert.Check(t, is.Equal(dockerfileContents, string(raw))) } @@ -238,5 +237,5 @@ func TestGetWithStatusError(t *testing.T) { func readBody(b io.ReadCloser) ([]byte, error) { defer b.Close() - return ioutil.ReadAll(b) + return io.ReadAll(b) } diff --git a/builder/remotecontext/tarsum.go b/builder/remotecontext/tarsum.go index b809cfb78b4f8..9e8c7d6072bde 100644 --- a/builder/remotecontext/tarsum.go +++ b/builder/remotecontext/tarsum.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/pkg/containerfs" iradix "github.com/hashicorp/go-immutable-radix" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/tonistiigi/fsutil" ) diff --git a/builder/remotecontext/tarsum_test.go b/builder/remotecontext/tarsum_test.go index 46f128d9f0608..fd5d9edc2f198 100644 --- a/builder/remotecontext/tarsum_test.go +++ b/builder/remotecontext/tarsum_test.go @@ -1,7 +1,6 @@ package remotecontext // import "github.com/docker/docker/builder/remotecontext" import ( - "io/ioutil" "os" "path/filepath" "testing" @@ -10,7 +9,7 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" "github.com/pkg/errors" - "gotest.tools/skip" + "gotest.tools/v3/skip" ) const ( @@ -23,7 +22,7 @@ func init() { } func TestCloseRootDirectory(t *testing.T) { - contextDir, err := ioutil.TempDir("", "builder-tarsum-test") + contextDir, err := os.MkdirTemp("", "builder-tarsum-test") defer os.RemoveAll(contextDir) if err != nil { t.Fatalf("Error with creating temporary directory: %s", err) @@ -38,7 +37,7 @@ func TestCloseRootDirectory(t *testing.T) { _, err = os.Stat(src.Root().Path()) - if !os.IsNotExist(err) { + if !errors.Is(err, os.ErrNotExist) { t.Fatal("Directory should not exist at this point") } } @@ -131,7 +130,7 @@ func TestRemoveDirectory(t *testing.T) { } _, err = src.Root().Stat(src.Root().Join(src.Root().Path(), relativePath)) - if !os.IsNotExist(errors.Cause(err)) { + if !errors.Is(err, os.ErrNotExist) { t.Fatalf("Directory should not exist at this point: %+v ", err) } } diff --git a/builder/remotecontext/utils_test.go b/builder/remotecontext/utils_test.go index 6a4c707a6e1a0..9a44719f8a18e 100644 --- a/builder/remotecontext/utils_test.go +++ b/builder/remotecontext/utils_test.go @@ -1,7 +1,6 @@ package remotecontext // import "github.com/docker/docker/builder/remotecontext" import ( - "io/ioutil" "os" "path/filepath" "testing" @@ -11,7 +10,7 @@ import ( // It returns the created path and a cleanup function which is meant to be used as deferred call. // When an error occurs, it terminates the test. func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) { - path, err := ioutil.TempDir(dir, prefix) + path, err := os.MkdirTemp(dir, prefix) if err != nil { t.Fatalf("Error when creating directory %s with prefix %s: %s", dir, prefix, err) @@ -32,7 +31,7 @@ func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) { // whose parent directories are properly cleaned up. // When an error occurs, it terminates the test. func createTestTempSubdir(t *testing.T, dir, prefix string) string { - path, err := ioutil.TempDir(dir, prefix) + path, err := os.MkdirTemp(dir, prefix) if err != nil { t.Fatalf("Error when creating directory %s with prefix %s: %s", dir, prefix, err) @@ -45,7 +44,7 @@ func createTestTempSubdir(t *testing.T, dir, prefix string) string { // When an error occurs, it terminates the test func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string { filePath := filepath.Join(dir, filename) - err := ioutil.WriteFile(filePath, []byte(contents), perm) + err := os.WriteFile(filePath, []byte(contents), perm) if err != nil { t.Fatalf("Error when creating %s file: %s", filename, err) diff --git a/cli/cobra.go b/cli/cobra.go index 8ed1fddc069f6..ac8e43f309db9 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -3,7 +3,7 @@ package cli // import "github.com/docker/docker/cli" import ( "fmt" - "github.com/docker/docker/pkg/term" + "github.com/moby/term" "github.com/spf13/cobra" ) diff --git a/cli/config/configdir.go b/cli/config/configdir.go index 4bef4e104da48..f0b68ee3879ca 100644 --- a/cli/config/configdir.go +++ b/cli/config/configdir.go @@ -13,6 +13,8 @@ var ( ) // Dir returns the path to the configuration directory as specified by the DOCKER_CONFIG environment variable. +// If DOCKER_CONFIG is unset, Dir returns ~/.docker . +// Dir ignores XDG_CONFIG_HOME (same as the docker client). // TODO: this was copied from cli/config/configfile and should be removed once cmd/dockerd moves func Dir() string { return configDir diff --git a/client/README.md b/client/README.md index 059dfb3ce7b95..992f18117df57 100644 --- a/client/README.md +++ b/client/README.md @@ -16,7 +16,7 @@ import ( ) func main() { - cli, err := client.NewEnvClient() + cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { panic(err) } diff --git a/client/build_cancel.go b/client/build_cancel.go index 74df495089178..b76bf366bb9e4 100644 --- a/client/build_cancel.go +++ b/client/build_cancel.go @@ -5,16 +5,12 @@ import ( "net/url" ) -// BuildCancel requests the daemon to cancel ongoing build request +// BuildCancel requests the daemon to cancel the ongoing build request. func (cli *Client) BuildCancel(ctx context.Context, id string) error { query := url.Values{} query.Set("id", id) serverResp, err := cli.post(ctx, "/build/cancel", query, nil, nil) - if err != nil { - return err - } - defer ensureReaderClosed(serverResp) - - return nil + ensureReaderClosed(serverResp) + return err } diff --git a/client/build_prune.go b/client/build_prune.go index 42bbf99ef1085..397d67cdcf1ac 100644 --- a/client/build_prune.go +++ b/client/build_prune.go @@ -31,11 +31,11 @@ func (cli *Client) BuildCachePrune(ctx context.Context, opts types.BuildCachePru query.Set("filters", filters) serverResp, err := cli.post(ctx, "/build/prune", query, nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return nil, err } - defer ensureReaderClosed(serverResp) if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { return nil, fmt.Errorf("Error retrieving disk usage: %v", err) diff --git a/client/checkpoint_create_test.go b/client/checkpoint_create_test.go index 5703c21904da7..cd2f544307dd3 100644 --- a/client/checkpoint_create_test.go +++ b/client/checkpoint_create_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestCheckpointCreateError(t *testing.T) { @@ -22,8 +23,8 @@ func TestCheckpointCreateError(t *testing.T) { Exit: true, }) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -38,7 +39,7 @@ func TestCheckpointCreate(t *testing.T) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } @@ -57,7 +58,7 @@ func TestCheckpointCreate(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/checkpoint_delete_test.go b/client/checkpoint_delete_test.go index 117630d61ac7a..f132e52b6d293 100644 --- a/client/checkpoint_delete_test.go +++ b/client/checkpoint_delete_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestCheckpointDeleteError(t *testing.T) { @@ -21,8 +22,8 @@ func TestCheckpointDeleteError(t *testing.T) { CheckpointID: "checkpoint_id", }) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -34,12 +35,12 @@ func TestCheckpointDelete(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "DELETE" { + if req.Method != http.MethodDelete { return nil, fmt.Errorf("expected DELETE method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/checkpoint_list.go b/client/checkpoint_list.go index 2b73fb553f206..66d46dd161ba3 100644 --- a/client/checkpoint_list.go +++ b/client/checkpoint_list.go @@ -18,11 +18,11 @@ func (cli *Client) CheckpointList(ctx context.Context, container string, options } resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil) + defer ensureReaderClosed(resp) if err != nil { return checkpoints, wrapResponseError(err, resp, "container", container) } err = json.NewDecoder(resp.body).Decode(&checkpoints) - ensureReaderClosed(resp) return checkpoints, err } diff --git a/client/checkpoint_list_test.go b/client/checkpoint_list_test.go index d5cfcda0e52fe..673956340fcb9 100644 --- a/client/checkpoint_list_test.go +++ b/client/checkpoint_list_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestCheckpointListError(t *testing.T) { @@ -19,8 +20,8 @@ func TestCheckpointListError(t *testing.T) { } _, err := client.CheckpointList(context.Background(), "container_id", types.CheckpointListOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -42,7 +43,7 @@ func TestCheckpointList(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/client.go b/client/client.go index 5031502acf16b..fa511897e0e55 100644 --- a/client/client.go +++ b/client/client.go @@ -2,13 +2,13 @@ Package client is a Go client for the Docker Engine API. For more information about the Engine API, see the documentation: -https://docs.docker.com/engine/reference/api/ +https://docs.docker.com/engine/api/ Usage You use the library by creating a client object and calling methods on it. The -client can be created either from environment variables with NewEnvClient, or -configured manually with NewClient. +client can be created either from environment variables with NewClientWithOpts(client.FromEnv), +or configured manually with NewClient(). For example, to list running containers (the equivalent of "docker ps"): @@ -23,7 +23,7 @@ For example, to list running containers (the equivalent of "docker ps"): ) func main() { - cli, err := client.NewEnvClient() + cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { panic(err) } @@ -47,16 +47,13 @@ import ( "net" "net/http" "net/url" - "os" "path" - "path/filepath" "strings" "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions" "github.com/docker/go-connections/sockets" - "github.com/docker/go-connections/tlsconfig" "github.com/pkg/errors" ) @@ -84,13 +81,22 @@ type Client struct { customHTTPHeaders map[string]string // manualOverride is set to true when the version was set by users. manualOverride bool + + // negotiateVersion indicates if the client should automatically negotiate + // the API version to use when making requests. API version negotiation is + // performed on the first request, after which negotiated is set to "true" + // so that subsequent requests do not re-negotiate. + negotiateVersion bool + + // negotiated indicates that API version negotiation took place + negotiated bool } // CheckRedirect specifies the policy for dealing with redirect responses: // If the request is non-GET return `ErrRedirect`. Otherwise use the last response. // // Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308) in the client . -// The Docker client (and by extension docker API client) can be made to to send a request +// The Docker client (and by extension docker API client) can be made to send a request // like POST /containers//start where what would normally be in the name section of the URL is empty. // This triggers an HTTP 301 from the daemon. // In go 1.8 this 301 will be converted to a GET request, and ends up getting a 404 from the daemon. @@ -103,137 +109,6 @@ func CheckRedirect(req *http.Request, via []*http.Request) error { return ErrRedirect } -// NewEnvClient initializes a new API client based on environment variables. -// See FromEnv for a list of support environment variables. -// -// Deprecated: use NewClientWithOpts(FromEnv) -func NewEnvClient() (*Client, error) { - return NewClientWithOpts(FromEnv) -} - -// FromEnv configures the client with values from environment variables. -// -// Supported environment variables: -// DOCKER_HOST to set the url to the docker server. -// DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest. -// DOCKER_CERT_PATH to load the TLS certificates from. -// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default. -func FromEnv(c *Client) error { - if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" { - options := tlsconfig.Options{ - CAFile: filepath.Join(dockerCertPath, "ca.pem"), - CertFile: filepath.Join(dockerCertPath, "cert.pem"), - KeyFile: filepath.Join(dockerCertPath, "key.pem"), - InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "", - } - tlsc, err := tlsconfig.Client(options) - if err != nil { - return err - } - - c.client = &http.Client{ - Transport: &http.Transport{TLSClientConfig: tlsc}, - CheckRedirect: CheckRedirect, - } - } - - if host := os.Getenv("DOCKER_HOST"); host != "" { - if err := WithHost(host)(c); err != nil { - return err - } - } - - if version := os.Getenv("DOCKER_API_VERSION"); version != "" { - c.version = version - c.manualOverride = true - } - return nil -} - -// WithTLSClientConfig applies a tls config to the client transport. -func WithTLSClientConfig(cacertPath, certPath, keyPath string) func(*Client) error { - return func(c *Client) error { - opts := tlsconfig.Options{ - CAFile: cacertPath, - CertFile: certPath, - KeyFile: keyPath, - ExclusiveRootPools: true, - } - config, err := tlsconfig.Client(opts) - if err != nil { - return errors.Wrap(err, "failed to create tls config") - } - if transport, ok := c.client.Transport.(*http.Transport); ok { - transport.TLSClientConfig = config - return nil - } - return errors.Errorf("cannot apply tls config to transport: %T", c.client.Transport) - } -} - -// WithDialer applies the dialer.DialContext to the client transport. This can be -// used to set the Timeout and KeepAlive settings of the client. -// Deprecated: use WithDialContext -func WithDialer(dialer *net.Dialer) func(*Client) error { - return WithDialContext(dialer.DialContext) -} - -// WithDialContext applies the dialer to the client transport. This can be -// used to set the Timeout and KeepAlive settings of the client. -func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) func(*Client) error { - return func(c *Client) error { - if transport, ok := c.client.Transport.(*http.Transport); ok { - transport.DialContext = dialContext - return nil - } - return errors.Errorf("cannot apply dialer to transport: %T", c.client.Transport) - } -} - -// WithVersion overrides the client version with the specified one -func WithVersion(version string) func(*Client) error { - return func(c *Client) error { - c.version = version - return nil - } -} - -// WithHost overrides the client host with the specified one. -func WithHost(host string) func(*Client) error { - return func(c *Client) error { - hostURL, err := ParseHostURL(host) - if err != nil { - return err - } - c.host = host - c.proto = hostURL.Scheme - c.addr = hostURL.Host - c.basePath = hostURL.Path - if transport, ok := c.client.Transport.(*http.Transport); ok { - return sockets.ConfigureTransport(transport, c.proto, c.addr) - } - return errors.Errorf("cannot apply host to transport: %T", c.client.Transport) - } -} - -// WithHTTPClient overrides the client http client with the specified one -func WithHTTPClient(client *http.Client) func(*Client) error { - return func(c *Client) error { - if client != nil { - c.client = client - } - return nil - } -} - -// WithHTTPHeaders overrides the client default http headers -func WithHTTPHeaders(headers map[string]string) func(*Client) error { - return func(c *Client) error { - c.customHTTPHeaders = headers - return nil - } -} - // NewClientWithOpts initializes a new API client with default values. It takes functors // to modify values when creating it, like `NewClientWithOpts(WithVersion(…))` // It also initializes the custom http headers to add to each request. @@ -241,7 +116,7 @@ func WithHTTPHeaders(headers map[string]string) func(*Client) error { // It won't send any version information if the version number is empty. It is // highly recommended that you set a version or your client may break if the // server is upgraded. -func NewClientWithOpts(ops ...func(*Client) error) (*Client, error) { +func NewClientWithOpts(ops ...Opt) (*Client, error) { client, err := defaultHTTPClient(DefaultDockerHost) if err != nil { return nil, err @@ -249,7 +124,6 @@ func NewClientWithOpts(ops ...func(*Client) error) (*Client, error) { c := &Client{ host: DefaultDockerHost, version: api.DefaultVersion, - scheme: "http", client: client, proto: defaultProto, addr: defaultAddr, @@ -264,14 +138,18 @@ func NewClientWithOpts(ops ...func(*Client) error) (*Client, error) { if _, ok := c.client.Transport.(http.RoundTripper); !ok { return nil, fmt.Errorf("unable to verify TLS configuration, invalid transport %v", c.client.Transport) } - tlsConfig := resolveTLSConfig(c.client.Transport) - if tlsConfig != nil { - // TODO(stevvooe): This isn't really the right way to write clients in Go. - // `NewClient` should probably only take an `*http.Client` and work from there. - // Unfortunately, the model of having a host-ish/url-thingy as the connection - // string has us confusing protocol and transport layers. We continue doing - // this to avoid breaking existing clients but this should be addressed. - c.scheme = "https" + if c.scheme == "" { + c.scheme = "http" + + tlsConfig := resolveTLSConfig(c.client.Transport) + if tlsConfig != nil { + // TODO(stevvooe): This isn't really the right way to write clients in Go. + // `NewClient` should probably only take an `*http.Client` and work from there. + // Unfortunately, the model of having a host-ish/url-thingy as the connection + // string has us confusing protocol and transport layers. We continue doing + // this to avoid breaking existing clients but this should be addressed. + c.scheme = "https" + } } return c, nil @@ -290,18 +168,6 @@ func defaultHTTPClient(host string) (*http.Client, error) { }, nil } -// NewClient initializes a new API client for the given host and API version. -// It uses the given http client as transport. -// It also initializes the custom http headers to add to each request. -// -// It won't send any version information if the version number is empty. It is -// highly recommended that you set a version or your client may break if the -// server is upgraded. -// Deprecated: use NewClientWithOpts -func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) { - return NewClientWithOpts(WithHost(host), WithVersion(version), WithHTTPClient(client), WithHTTPHeaders(httpHeaders)) -} - // Close the transport used by the client func (cli *Client) Close() error { if t, ok := cli.client.Transport.(*http.Transport); ok { @@ -312,8 +178,11 @@ func (cli *Client) Close() error { // getAPIPath returns the versioned request path to call the api. // It appends the query parameters to the path if they are not empty. -func (cli *Client) getAPIPath(p string, query url.Values) string { +func (cli *Client) getAPIPath(ctx context.Context, p string, query url.Values) string { var apiPath string + if cli.negotiateVersion && !cli.negotiated { + cli.NegotiateAPIVersion(ctx) + } if cli.version != "" { v := strings.TrimPrefix(cli.version, "v") apiPath = path.Join(cli.basePath, "/v"+v, p) @@ -329,19 +198,31 @@ func (cli *Client) ClientVersion() string { } // NegotiateAPIVersion queries the API and updates the version to match the -// API version. Any errors are silently ignored. +// API version. Any errors are silently ignored. If a manual override is in place, +// either through the `DOCKER_API_VERSION` environment variable, or if the client +// was initialized with a fixed version (`opts.WithVersion(xx)`), no negotiation +// will be performed. func (cli *Client) NegotiateAPIVersion(ctx context.Context) { - ping, _ := cli.Ping(ctx) - cli.NegotiateAPIVersionPing(ping) + if !cli.manualOverride { + ping, _ := cli.Ping(ctx) + cli.negotiateAPIVersionPing(ping) + } } // NegotiateAPIVersionPing updates the client version to match the Ping.APIVersion -// if the ping version is less than the default version. +// if the ping version is less than the default version. If a manual override is +// in place, either through the `DOCKER_API_VERSION` environment variable, or if +// the client was initialized with a fixed version (`opts.WithVersion(xx)`), no +// negotiation is performed. func (cli *Client) NegotiateAPIVersionPing(p types.Ping) { - if cli.manualOverride { - return + if !cli.manualOverride { + cli.negotiateAPIVersionPing(p) } +} +// negotiateAPIVersionPing queries the API and updates the version to match the +// API version. Any errors are silently ignored. +func (cli *Client) negotiateAPIVersionPing(p types.Ping) { // try the latest version before versioning headers existed if p.APIVersion == "" { p.APIVersion = "1.24" @@ -356,6 +237,12 @@ func (cli *Client) NegotiateAPIVersionPing(p types.Ping) { if versions.LessThan(p.APIVersion, cli.version) { cli.version = p.APIVersion } + + // Store the results, so that automatic API version negotiation (if enabled) + // won't be performed on the next request. + if cli.negotiateVersion { + cli.negotiated = true + } } // DaemonHost returns the host address used by the client @@ -365,7 +252,8 @@ func (cli *Client) DaemonHost() string { // HTTPClient returns a copy of the HTTP client bound to the server func (cli *Client) HTTPClient() *http.Client { - return &*cli.client + c := *cli.client + return &c } // ParseHostURL parses a url string, validates the string is a host url, and @@ -393,21 +281,6 @@ func ParseHostURL(host string) (*url.URL, error) { }, nil } -// CustomHTTPHeaders returns the custom http headers stored by the client. -func (cli *Client) CustomHTTPHeaders() map[string]string { - m := make(map[string]string) - for k, v := range cli.customHTTPHeaders { - m[k] = v - } - return m -} - -// SetCustomHTTPHeaders that will be set on every HTTP request made by the client. -// Deprecated: use WithHTTPHeaders when creating the client. -func (cli *Client) SetCustomHTTPHeaders(headers map[string]string) { - cli.customHTTPHeaders = headers -} - // Dialer returns a dialer for a raw stream connection, with HTTP/1.1 header, that can be used for proxying the daemon connection. // Used by `docker dial-stdio` (docker/cli#889). func (cli *Client) Dialer() func(context.Context) (net.Conn, error) { diff --git a/client/client_deprecated.go b/client/client_deprecated.go new file mode 100644 index 0000000000000..54cdfc29a84b6 --- /dev/null +++ b/client/client_deprecated.go @@ -0,0 +1,23 @@ +package client + +import "net/http" + +// NewClient initializes a new API client for the given host and API version. +// It uses the given http client as transport. +// It also initializes the custom http headers to add to each request. +// +// It won't send any version information if the version number is empty. It is +// highly recommended that you set a version or your client may break if the +// server is upgraded. +// Deprecated: use NewClientWithOpts +func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) { + return NewClientWithOpts(WithHost(host), WithVersion(version), WithHTTPClient(client), WithHTTPHeaders(httpHeaders)) +} + +// NewEnvClient initializes a new API client based on environment variables. +// See FromEnv for a list of support environment variables. +// +// Deprecated: use NewClientWithOpts(FromEnv) +func NewEnvClient() (*Client, error) { + return NewClientWithOpts(FromEnv) +} diff --git a/client/client_mock_test.go b/client/client_mock_test.go index 390a1eed7d65b..c119e59bbbe05 100644 --- a/client/client_mock_test.go +++ b/client/client_mock_test.go @@ -3,7 +3,7 @@ package client // import "github.com/docker/docker/client" import ( "bytes" "encoding/json" - "io/ioutil" + "io" "net/http" "github.com/docker/docker/api/types" @@ -37,7 +37,7 @@ func errorMock(statusCode int, message string) func(req *http.Request) (*http.Re return &http.Response{ StatusCode: statusCode, - Body: ioutil.NopCloser(bytes.NewReader(body)), + Body: io.NopCloser(bytes.NewReader(body)), Header: header, }, nil } @@ -47,7 +47,7 @@ func plainTextErrorMock(statusCode int, message string) func(req *http.Request) return func(req *http.Request) (*http.Response, error) { return &http.Response{ StatusCode: statusCode, - Body: ioutil.NopCloser(bytes.NewReader([]byte(message))), + Body: io.NopCloser(bytes.NewReader([]byte(message))), }, nil } } diff --git a/client/client_test.go b/client/client_test.go index 58bccaa311508..14739f2ca9e79 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -2,21 +2,24 @@ package client // import "github.com/docker/docker/client" import ( "bytes" + "context" + "io" "net/http" "net/url" "os" "runtime" + "strings" "testing" "github.com/docker/docker/api" "github.com/docker/docker/api/types" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/env" - "gotest.tools/skip" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/env" + "gotest.tools/v3/skip" ) -func TestNewEnvClient(t *testing.T) { +func TestNewClientWithOpsFromEnv(t *testing.T) { skip.If(t, runtime.GOOS == "windows") testcases := []struct { @@ -86,7 +89,7 @@ func TestNewEnvClient(t *testing.T) { defer env.PatchAll(t, nil)() for _, c := range testcases { env.PatchAll(t, c.envs) - apiclient, err := NewEnvClient() + apiclient, err := NewClientWithOpts(FromEnv) if c.expectedError != "" { assert.Check(t, is.Error(err, c.expectedError), c.doc) } else { @@ -123,9 +126,10 @@ func TestGetAPIPath(t *testing.T) { {"v1.22", "/networks/kiwl$%^", nil, "/v1.22/networks/kiwl$%25%5E"}, } + ctx := context.TODO() for _, testcase := range testcases { c := Client{version: testcase.version, basePath: "/"} - actual := c.getAPIPath(testcase.path, testcase.query) + actual := c.getAPIPath(ctx, testcase.path, testcase.query) assert.Check(t, is.Equal(actual, testcase.expected)) } } @@ -167,7 +171,7 @@ func TestParseHostURL(t *testing.T) { } } -func TestNewEnvClientSetsDefaultVersion(t *testing.T) { +func TestNewClientWithOpsFromEnvSetsDefaultVersion(t *testing.T) { defer env.PatchAll(t, map[string]string{ "DOCKER_HOST": "", "DOCKER_API_VERSION": "", @@ -175,7 +179,7 @@ func TestNewEnvClientSetsDefaultVersion(t *testing.T) { "DOCKER_CERT_PATH": "", })() - client, err := NewEnvClient() + client, err := NewClientWithOpts(FromEnv) if err != nil { t.Fatal(err) } @@ -183,7 +187,7 @@ func TestNewEnvClientSetsDefaultVersion(t *testing.T) { expected := "1.22" os.Setenv("DOCKER_API_VERSION", expected) - client, err = NewEnvClient() + client, err = NewClientWithOpts(FromEnv) if err != nil { t.Fatal(err) } @@ -195,7 +199,7 @@ func TestNewEnvClientSetsDefaultVersion(t *testing.T) { func TestNegotiateAPIVersionEmpty(t *testing.T) { defer env.PatchAll(t, map[string]string{"DOCKER_API_VERSION": ""})() - client, err := NewEnvClient() + client, err := NewClientWithOpts(FromEnv) assert.NilError(t, err) ping := types.Ping{ @@ -219,7 +223,7 @@ func TestNegotiateAPIVersionEmpty(t *testing.T) { // TestNegotiateAPIVersion asserts that client.Client can // negotiate a compatible APIVersion with the server func TestNegotiateAPIVersion(t *testing.T) { - client, err := NewEnvClient() + client, err := NewClientWithOpts(FromEnv) assert.NilError(t, err) expected := "1.21" @@ -251,7 +255,7 @@ func TestNegotiateAPVersionOverride(t *testing.T) { expected := "9.99" defer env.PatchAll(t, map[string]string{"DOCKER_API_VERSION": expected})() - client, err := NewEnvClient() + client, err := NewClientWithOpts(FromEnv) assert.NilError(t, err) ping := types.Ping{ @@ -265,6 +269,55 @@ func TestNegotiateAPVersionOverride(t *testing.T) { assert.Check(t, is.Equal(expected, client.version)) } +func TestNegotiateAPIVersionAutomatic(t *testing.T) { + var pingVersion string + httpClient := newMockClient(func(req *http.Request) (*http.Response, error) { + resp := &http.Response{StatusCode: http.StatusOK, Header: http.Header{}} + resp.Header.Set("API-Version", pingVersion) + resp.Body = io.NopCloser(strings.NewReader("OK")) + return resp, nil + }) + + client, err := NewClientWithOpts( + WithHTTPClient(httpClient), + WithAPIVersionNegotiation(), + ) + assert.NilError(t, err) + + ctx := context.Background() + assert.Equal(t, client.ClientVersion(), api.DefaultVersion) + + // First request should trigger negotiation + pingVersion = "1.35" + _, _ = client.Info(ctx) + assert.Equal(t, client.ClientVersion(), "1.35") + + // Once successfully negotiated, subsequent requests should not re-negotiate + pingVersion = "1.25" + _, _ = client.Info(ctx) + assert.Equal(t, client.ClientVersion(), "1.35") +} + +// TestNegotiateAPIVersionWithEmptyVersion asserts that initializing a client +// with an empty version string does still allow API-version negotiation +func TestNegotiateAPIVersionWithEmptyVersion(t *testing.T) { + client, err := NewClientWithOpts(WithVersion("")) + assert.NilError(t, err) + + client.NegotiateAPIVersionPing(types.Ping{APIVersion: "1.35"}) + assert.Equal(t, client.version, "1.35") +} + +// TestNegotiateAPIVersionWithFixedVersion asserts that initializing a client +// with an fixed version disables API-version negotiation +func TestNegotiateAPIVersionWithFixedVersion(t *testing.T) { + client, err := NewClientWithOpts(WithVersion("1.35")) + assert.NilError(t, err) + + client.NegotiateAPIVersionPing(types.Ping{APIVersion: "1.31"}) + assert.Equal(t, client.version, "1.35") +} + type roundTripFunc func(*http.Request) (*http.Response, error) func (rtf roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { diff --git a/client/client_unix.go b/client/client_unix.go index 3d24470ba3aae..5846f888fea21 100644 --- a/client/client_unix.go +++ b/client/client_unix.go @@ -1,4 +1,5 @@ -// +build linux freebsd openbsd darwin +//go:build linux || freebsd || openbsd || netbsd || darwin || solaris || illumos || dragonfly +// +build linux freebsd openbsd netbsd darwin solaris illumos dragonfly package client // import "github.com/docker/docker/client" diff --git a/client/config_create.go b/client/config_create.go index c8b802ad356c0..f6b1881fc3691 100644 --- a/client/config_create.go +++ b/client/config_create.go @@ -8,18 +8,18 @@ import ( "github.com/docker/docker/api/types/swarm" ) -// ConfigCreate creates a new Config. +// ConfigCreate creates a new config. func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (types.ConfigCreateResponse, error) { var response types.ConfigCreateResponse if err := cli.NewVersionError("1.30", "config create"); err != nil { return response, err } resp, err := cli.post(ctx, "/configs/create", nil, config, nil) + defer ensureReaderClosed(resp) if err != nil { return response, err } err = json.NewDecoder(resp.body).Decode(&response) - ensureReaderClosed(resp) return response, err } diff --git a/client/config_create_test.go b/client/config_create_test.go index a6408792db931..18b18b5401734 100644 --- a/client/config_create_test.go +++ b/client/config_create_test.go @@ -5,15 +5,16 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestConfigCreateUnsupported(t *testing.T) { @@ -31,8 +32,8 @@ func TestConfigCreateError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ConfigCreate(context.Background(), swarm.ConfigSpec{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -44,7 +45,7 @@ func TestConfigCreate(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } b, err := json.Marshal(types.ConfigCreateResponse{ @@ -55,7 +56,7 @@ func TestConfigCreate(t *testing.T) { } return &http.Response{ StatusCode: http.StatusCreated, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/config_inspect.go b/client/config_inspect.go index 4ac566ad89fc7..f1b0d7f7536ce 100644 --- a/client/config_inspect.go +++ b/client/config_inspect.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" + "io" "github.com/docker/docker/api/types/swarm" ) @@ -18,12 +18,12 @@ func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.C return swarm.Config{}, nil, err } resp, err := cli.get(ctx, "/configs/"+id, nil, nil) + defer ensureReaderClosed(resp) if err != nil { return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id) } - defer ensureReaderClosed(resp) - body, err := ioutil.ReadAll(resp.body) + body, err := io.ReadAll(resp.body) if err != nil { return swarm.Config{}, nil, err } diff --git a/client/config_inspect_test.go b/client/config_inspect_test.go index 76a5dae9e5ebc..139d6022cc8ce 100644 --- a/client/config_inspect_test.go +++ b/client/config_inspect_test.go @@ -5,15 +5,16 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestConfigInspectNotFound(t *testing.T) { @@ -55,8 +56,8 @@ func TestConfigInspectError(t *testing.T) { } _, _, err := client.ConfigInspectWithRaw(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -88,7 +89,7 @@ func TestConfigInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/config_list.go b/client/config_list.go index 2b9d54606bf35..565acc6e27332 100644 --- a/client/config_list.go +++ b/client/config_list.go @@ -27,12 +27,12 @@ func (cli *Client) ConfigList(ctx context.Context, options types.ConfigListOptio } resp, err := cli.get(ctx, "/configs", query, nil) + defer ensureReaderClosed(resp) if err != nil { return nil, err } var configs []swarm.Config err = json.NewDecoder(resp.body).Decode(&configs) - ensureReaderClosed(resp) return configs, err } diff --git a/client/config_list_test.go b/client/config_list_test.go index b35a5929531ef..9c4cf6642b7f8 100644 --- a/client/config_list_test.go +++ b/client/config_list_test.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" @@ -13,8 +13,9 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestConfigListUnsupported(t *testing.T) { @@ -33,8 +34,8 @@ func TestConfigListError(t *testing.T) { } _, err := client.ConfigList(context.Background(), types.ConfigListOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -91,7 +92,7 @@ func TestConfigList(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/config_remove.go b/client/config_remove.go index a96871e98b71d..93de0d8445b55 100644 --- a/client/config_remove.go +++ b/client/config_remove.go @@ -2,12 +2,12 @@ package client // import "github.com/docker/docker/client" import "context" -// ConfigRemove removes a Config. +// ConfigRemove removes a config. func (cli *Client) ConfigRemove(ctx context.Context, id string) error { if err := cli.NewVersionError("1.30", "config remove"); err != nil { return err } resp, err := cli.delete(ctx, "/configs/"+id, nil, nil) - ensureReaderClosed(resp) + defer ensureReaderClosed(resp) return wrapResponseError(err, resp, "config", id) } diff --git a/client/config_remove_test.go b/client/config_remove_test.go index 9c0c0f9337e2c..1573f318ae398 100644 --- a/client/config_remove_test.go +++ b/client/config_remove_test.go @@ -4,13 +4,14 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestConfigRemoveUnsupported(t *testing.T) { @@ -29,8 +30,8 @@ func TestConfigRemoveError(t *testing.T) { } err := client.ConfigRemove(context.Background(), "config_id") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -43,12 +44,12 @@ func TestConfigRemove(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "DELETE" { + if req.Method != http.MethodDelete { return nil, fmt.Errorf("expected DELETE method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), }, nil }), } diff --git a/client/config_update.go b/client/config_update.go index 39e59cf858904..ba79ae64e5920 100644 --- a/client/config_update.go +++ b/client/config_update.go @@ -8,7 +8,7 @@ import ( "github.com/docker/docker/api/types/swarm" ) -// ConfigUpdate attempts to update a Config +// ConfigUpdate attempts to update a config func (cli *Client) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error { if err := cli.NewVersionError("1.30", "config update"); err != nil { return err diff --git a/client/config_update_test.go b/client/config_update_test.go index 1299f8278ca7c..7b6d15be87950 100644 --- a/client/config_update_test.go +++ b/client/config_update_test.go @@ -4,14 +4,15 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestConfigUpdateUnsupported(t *testing.T) { @@ -30,8 +31,8 @@ func TestConfigUpdateError(t *testing.T) { } err := client.ConfigUpdate(context.Background(), "config_id", swarm.Version{}, swarm.ConfigSpec{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -44,12 +45,12 @@ func TestConfigUpdate(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), }, nil }), } diff --git a/client/container_commit.go b/client/container_commit.go index 377a2ea681c12..cd7f7634646f7 100644 --- a/client/container_commit.go +++ b/client/container_commit.go @@ -10,7 +10,7 @@ import ( "github.com/docker/docker/api/types" ) -// ContainerCommit applies changes into a container and creates a new tagged image. +// ContainerCommit applies changes to a container and creates a new tagged image. func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) { var repository, tag string if options.Reference != "" { @@ -45,11 +45,11 @@ func (cli *Client) ContainerCommit(ctx context.Context, container string, option var response types.IDResponse resp, err := cli.post(ctx, "/commit", query, options.Config, nil) + defer ensureReaderClosed(resp) if err != nil { return response, err } err = json.NewDecoder(resp.body).Decode(&response) - ensureReaderClosed(resp) return response, err } diff --git a/client/container_commit_test.go b/client/container_commit_test.go index 8e3fe8b730aeb..80aca7bbb33ba 100644 --- a/client/container_commit_test.go +++ b/client/container_commit_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestContainerCommitError(t *testing.T) { @@ -18,8 +19,8 @@ func TestContainerCommitError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerCommit(context.Background(), "nothing", types.ContainerCommitOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -75,7 +76,7 @@ func TestContainerCommit(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/container_copy.go b/client/container_copy.go index d706260cee0fd..c0a47c14e31b3 100644 --- a/client/container_copy.go +++ b/client/container_copy.go @@ -14,17 +14,17 @@ import ( "github.com/docker/docker/api/types" ) -// ContainerStatPath returns Stat information about a path inside the container filesystem. +// ContainerStatPath returns stat information about a path inside the container filesystem. func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path string) (types.ContainerPathStat, error) { query := url.Values{} query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API. urlStr := "/containers/" + containerID + "/archive" response, err := cli.head(ctx, urlStr, query, nil) + defer ensureReaderClosed(response) if err != nil { return types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+path) } - defer ensureReaderClosed(response) return getContainerPathStatFromHeader(response.header) } @@ -45,11 +45,12 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath str apiPath := "/containers/" + containerID + "/archive" response, err := cli.putRaw(ctx, apiPath, query, content, nil) + defer ensureReaderClosed(response) if err != nil { return wrapResponseError(err, response, "container:path", containerID+":"+dstPath) } - defer ensureReaderClosed(response) + // TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior if response.statusCode != http.StatusOK { return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode) } @@ -69,6 +70,7 @@ func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath s return nil, types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+srcPath) } + // TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior if response.statusCode != http.StatusOK { return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode) } diff --git a/client/container_copy_test.go b/client/container_copy_test.go index efddbef20dba5..c94aa5e1aa3e2 100644 --- a/client/container_copy_test.go +++ b/client/container_copy_test.go @@ -6,12 +6,13 @@ import ( "encoding/base64" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestContainerStatPathError(t *testing.T) { @@ -19,8 +20,8 @@ func TestContainerStatPathError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerStatPath(context.Background(), "container_id", "path") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -39,7 +40,7 @@ func TestContainerStatPathNoHeaderError(t *testing.T) { client: newMockClient(func(req *http.Request) (*http.Response, error) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } @@ -57,7 +58,7 @@ func TestContainerStatPath(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "HEAD" { + if req.Method != http.MethodHead { return nil, fmt.Errorf("expected HEAD method, got %s", req.Method) } query := req.URL.Query() @@ -75,7 +76,7 @@ func TestContainerStatPath(t *testing.T) { base64PathStat := base64.StdEncoding.EncodeToString(content) return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), Header: http.Header{ "X-Docker-Container-Path-Stat": []string{base64PathStat}, }, @@ -99,8 +100,8 @@ func TestCopyToContainerError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } err := client.CopyToContainer(context.Background(), "container_id", "path/to/file", bytes.NewReader([]byte("")), types.CopyToContainerOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -114,6 +115,7 @@ func TestCopyToContainerNotFoundError(t *testing.T) { } } +// TODO TestCopyToContainerNotStatusOKError expects a non-error status-code ("204 No Content") to produce an error; verify if this is the desired behavior func TestCopyToContainerNotStatusOKError(t *testing.T) { client := &Client{ client: newMockClient(errorMock(http.StatusNoContent, "No content")), @@ -132,7 +134,7 @@ func TestCopyToContainer(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "PUT" { + if req.Method != http.MethodPut { return nil, fmt.Errorf("expected PUT method, got %s", req.Method) } query := req.URL.Query() @@ -145,7 +147,7 @@ func TestCopyToContainer(t *testing.T) { return nil, fmt.Errorf("noOverwriteDirNonDir not set in URL query properly, expected true, got %s", noOverwriteDirNonDir) } - content, err := ioutil.ReadAll(req.Body) + content, err := io.ReadAll(req.Body) if err != nil { return nil, err } @@ -158,7 +160,7 @@ func TestCopyToContainer(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } @@ -175,8 +177,8 @@ func TestCopyFromContainerError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, _, err := client.CopyFromContainer(context.Background(), "container_id", "path/to/file") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -190,6 +192,7 @@ func TestCopyFromContainerNotFoundError(t *testing.T) { } } +// TODO TestCopyFromContainerNotStatusOKError expects a non-error status-code ("204 No Content") to produce an error; verify if this is the desired behavior func TestCopyFromContainerNotStatusOKError(t *testing.T) { client := &Client{ client: newMockClient(errorMock(http.StatusNoContent, "No content")), @@ -205,7 +208,7 @@ func TestCopyFromContainerNoHeaderError(t *testing.T) { client: newMockClient(func(req *http.Request) (*http.Response, error) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } @@ -223,7 +226,7 @@ func TestCopyFromContainer(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "GET" { + if req.Method != http.MethodGet { return nil, fmt.Errorf("expected GET method, got %s", req.Method) } query := req.URL.Query() @@ -243,7 +246,7 @@ func TestCopyFromContainer(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("content"))), + Body: io.NopCloser(bytes.NewReader([]byte("content"))), Header: http.Header{ "X-Docker-Container-Path-Stat": []string{base64PathStat}, }, @@ -260,7 +263,7 @@ func TestCopyFromContainer(t *testing.T) { if stat.Mode != 0700 { t.Fatalf("expected container path stat mode to be 0700, got '%v'", stat.Mode) } - content, err := ioutil.ReadAll(r) + content, err := io.ReadAll(r) if err != nil { t.Fatal(err) } diff --git a/client/container_create.go b/client/container_create.go index d269a61894aee..47d15c2bbd6bc 100644 --- a/client/container_create.go +++ b/client/container_create.go @@ -4,11 +4,12 @@ import ( "context" "encoding/json" "net/url" - "strings" + "path" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/versions" + specs "github.com/opencontainers/image-spec/specs-go/v1" ) type configWrapper struct { @@ -17,9 +18,9 @@ type configWrapper struct { NetworkingConfig *network.NetworkingConfig } -// ContainerCreate creates a new container based in the given configuration. +// ContainerCreate creates a new container based on the given configuration. // It can be associated with a name, but it's not mandatory. -func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error) { +func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.ContainerCreateCreatedBody, error) { var response container.ContainerCreateCreatedBody if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil { @@ -31,7 +32,15 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config hostConfig.AutoRemove = false } + if err := cli.NewVersionError("1.41", "specify container image platform"); platform != nil && err != nil { + return response, err + } + query := url.Values{} + if p := formatPlatform(platform); p != "" { + query.Set("platform", p) + } + if containerName != "" { query.Set("name", containerName) } @@ -43,14 +52,23 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config } serverResp, err := cli.post(ctx, "/containers/create", query, body, nil) + defer ensureReaderClosed(serverResp) if err != nil { - if serverResp.statusCode == 404 && strings.Contains(err.Error(), "No such image") { - return response, objectNotFoundError{object: "image", id: config.Image} - } return response, err } err = json.NewDecoder(serverResp.body).Decode(&response) - ensureReaderClosed(serverResp) return response, err } + +// formatPlatform returns a formatted string representing platform (e.g. linux/arm/v7). +// +// Similar to containerd's platforms.Format(), but does allow components to be +// omitted (e.g. pass "architecture" only, without "os": +// https://github.com/containerd/containerd/blob/v1.5.2/platforms/platforms.go#L243-L263 +func formatPlatform(platform *specs.Platform) string { + if platform == nil { + return "" + } + return path.Join(platform.OS, platform.Architecture, platform.Variant) +} diff --git a/client/container_create_test.go b/client/container_create_test.go index d46e70492dbfe..2a8c5eef2b752 100644 --- a/client/container_create_test.go +++ b/client/container_create_test.go @@ -5,30 +5,31 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/errdefs" ) func TestContainerCreateError(t *testing.T) { client := &Client{ client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } - _, err := client.ContainerCreate(context.Background(), nil, nil, nil, "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error while testing StatusInternalServerError, got %v", err) + _, err := client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing") + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error while testing StatusInternalServerError, got %T", err) } // 404 doesn't automatically means an unknown image client = &Client{ client: newMockClient(errorMock(http.StatusNotFound, "Server error")), } - _, err = client.ContainerCreate(context.Background(), nil, nil, nil, "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error while testing StatusNotFound, got %v", err) + _, err = client.ContainerCreate(context.Background(), nil, nil, nil, nil, "nothing") + if err == nil || !IsErrNotFound(err) { + t.Fatalf("expected a Server Error while testing StatusNotFound, got %T", err) } } @@ -36,9 +37,9 @@ func TestContainerCreateImageNotFound(t *testing.T) { client := &Client{ client: newMockClient(errorMock(http.StatusNotFound, "No such image")), } - _, err := client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, "unknown") + _, err := client.ContainerCreate(context.Background(), &container.Config{Image: "unknown_image"}, nil, nil, nil, "unknown") if err == nil || !IsErrNotFound(err) { - t.Fatalf("expected an imageNotFound error, got %v", err) + t.Fatalf("expected an imageNotFound error, got %v, %T", err, err) } } @@ -61,12 +62,12 @@ func TestContainerCreateWithName(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } - r, err := client.ContainerCreate(context.Background(), nil, nil, nil, "container_name") + r, err := client.ContainerCreate(context.Background(), nil, nil, nil, nil, "container_name") if err != nil { t.Fatal(err) } @@ -96,7 +97,7 @@ func TestContainerCreateAutoRemove(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil } } @@ -105,14 +106,14 @@ func TestContainerCreateAutoRemove(t *testing.T) { client: newMockClient(autoRemoveValidator(false)), version: "1.24", } - if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, ""); err != nil { + if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, nil, ""); err != nil { t.Fatal(err) } client = &Client{ client: newMockClient(autoRemoveValidator(true)), version: "1.25", } - if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, ""); err != nil { + if _, err := client.ContainerCreate(context.Background(), nil, &container.HostConfig{AutoRemove: true}, nil, nil, ""); err != nil { t.Fatal(err) } } diff --git a/client/container_diff.go b/client/container_diff.go index 3b7c90c96c95e..29dac8491df51 100644 --- a/client/container_diff.go +++ b/client/container_diff.go @@ -13,11 +13,11 @@ func (cli *Client) ContainerDiff(ctx context.Context, containerID string) ([]con var changes []container.ContainerChangeResponseItem serverResp, err := cli.get(ctx, "/containers/"+containerID+"/changes", url.Values{}, nil) + defer ensureReaderClosed(serverResp) if err != nil { return changes, err } err = json.NewDecoder(serverResp.body).Decode(&changes) - ensureReaderClosed(serverResp) return changes, err } diff --git a/client/container_diff_test.go b/client/container_diff_test.go index ac215f34038fb..14e243343ea54 100644 --- a/client/container_diff_test.go +++ b/client/container_diff_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/errdefs" ) func TestContainerDiffError(t *testing.T) { @@ -18,10 +19,9 @@ func TestContainerDiffError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerDiff(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } - } func TestContainerDiff(t *testing.T) { @@ -46,7 +46,7 @@ func TestContainerDiff(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/container_exec.go b/client/container_exec.go index 535536b1e0ff7..e3ee755b71dc2 100644 --- a/client/container_exec.go +++ b/client/container_exec.go @@ -16,11 +16,11 @@ func (cli *Client) ContainerExecCreate(ctx context.Context, container string, co } resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, config, nil) + defer ensureReaderClosed(resp) if err != nil { return response, err } err = json.NewDecoder(resp.body).Decode(&response) - ensureReaderClosed(resp) return response, err } diff --git a/client/container_exec_test.go b/client/container_exec_test.go index 68b900bf147b1..3daae491d8a7b 100644 --- a/client/container_exec_test.go +++ b/client/container_exec_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestContainerExecCreateError(t *testing.T) { @@ -18,8 +19,8 @@ func TestContainerExecCreateError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerExecCreate(context.Background(), "container_id", types.ExecConfig{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -30,7 +31,7 @@ func TestContainerExecCreate(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } // FIXME validate the content is the given ExecConfig ? @@ -52,7 +53,7 @@ func TestContainerExecCreate(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } @@ -73,8 +74,8 @@ func TestContainerExecStartError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } err := client.ContainerExecStart(context.Background(), "nothing", types.ExecStartCheck{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -98,7 +99,7 @@ func TestContainerExecStart(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } @@ -117,8 +118,8 @@ func TestContainerExecInspectError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerExecInspect(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -138,7 +139,7 @@ func TestContainerExecInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/container_export_test.go b/client/container_export_test.go index 8f6c8dce642c6..1a19aebd93108 100644 --- a/client/container_export_test.go +++ b/client/container_export_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestContainerExportError(t *testing.T) { @@ -15,8 +17,8 @@ func TestContainerExportError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerExport(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -30,7 +32,7 @@ func TestContainerExport(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("response"))), + Body: io.NopCloser(bytes.NewReader([]byte("response"))), }, nil }), } @@ -39,7 +41,7 @@ func TestContainerExport(t *testing.T) { t.Fatal(err) } defer body.Close() - content, err := ioutil.ReadAll(body) + content, err := io.ReadAll(body) if err != nil { t.Fatal(err) } diff --git a/client/container_inspect.go b/client/container_inspect.go index f453064cf8f44..43db32bd973a1 100644 --- a/client/container_inspect.go +++ b/client/container_inspect.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" + "io" "net/url" "github.com/docker/docker/api/types" @@ -16,13 +16,13 @@ func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (ty return types.ContainerJSON{}, objectNotFoundError{object: "container", id: containerID} } serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return types.ContainerJSON{}, wrapResponseError(err, serverResp, "container", containerID) } var response types.ContainerJSON err = json.NewDecoder(serverResp.body).Decode(&response) - ensureReaderClosed(serverResp) return response, err } @@ -36,12 +36,12 @@ func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID stri query.Set("size", "1") } serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil) + defer ensureReaderClosed(serverResp) if err != nil { return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID) } - defer ensureReaderClosed(serverResp) - body, err := ioutil.ReadAll(serverResp.body) + body, err := io.ReadAll(serverResp.body) if err != nil { return types.ContainerJSON{}, nil, err } diff --git a/client/container_inspect_test.go b/client/container_inspect_test.go index 92a77f6aea419..54c70d030491d 100644 --- a/client/container_inspect_test.go +++ b/client/container_inspect_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" ) @@ -20,8 +21,8 @@ func TestContainerInspectError(t *testing.T) { } _, err := client.ContainerInspect(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -67,7 +68,7 @@ func TestContainerInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } @@ -87,6 +88,8 @@ func TestContainerInspect(t *testing.T) { } } +// TestContainerInspectNode tests that the "Node" field is included in the "inspect" +// output. This information is only present when connected to a Swarm standalone API. func TestContainerInspectNode(t *testing.T) { client := &Client{ client: newMockClient(func(req *http.Request) (*http.Response, error) { @@ -107,7 +110,7 @@ func TestContainerInspectNode(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/container_kill_test.go b/client/container_kill_test.go index 85bb5ee559348..6db886d2c52d3 100644 --- a/client/container_kill_test.go +++ b/client/container_kill_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestContainerKillError(t *testing.T) { @@ -15,8 +17,8 @@ func TestContainerKillError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } err := client.ContainerKill(context.Background(), "nothing", "SIGKILL") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -33,7 +35,7 @@ func TestContainerKill(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/container_list.go b/client/container_list.go index 9c218e2218aed..a973de597fdf3 100644 --- a/client/container_list.go +++ b/client/container_list.go @@ -35,6 +35,7 @@ func (cli *Client) ContainerList(ctx context.Context, options types.ContainerLis } if options.Filters.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters) if err != nil { @@ -45,12 +46,12 @@ func (cli *Client) ContainerList(ctx context.Context, options types.ContainerLis } resp, err := cli.get(ctx, "/containers/json", query, nil) + defer ensureReaderClosed(resp) if err != nil { return nil, err } var containers []types.Container err = json.NewDecoder(resp.body).Decode(&containers) - ensureReaderClosed(resp) return containers, err } diff --git a/client/container_list_test.go b/client/container_list_test.go index 809f20f5c7ea2..dfd3494ca6132 100644 --- a/client/container_list_test.go +++ b/client/container_list_test.go @@ -5,13 +5,14 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/errdefs" ) func TestContainerListError(t *testing.T) { @@ -19,8 +20,8 @@ func TestContainerListError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerList(context.Background(), types.ContainerListOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -72,7 +73,7 @@ func TestContainerList(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/container_logs_test.go b/client/container_logs_test.go index 6d6e34e101272..07c3bbd137fb0 100644 --- a/client/container_logs_test.go +++ b/client/container_logs_test.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "log" "net/http" "os" @@ -14,8 +13,9 @@ import ( "time" "github.com/docker/docker/api/types" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestContainerLogsNotFoundError(t *testing.T) { @@ -33,7 +33,9 @@ func TestContainerLogsError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerLogs(context.Background(), "container_id", types.ContainerLogsOptions{}) - assert.Check(t, is.Error(err, "Error response from daemon: Server error")) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) + } _, err = client.ContainerLogs(context.Background(), "container_id", types.ContainerLogsOptions{ Since: "2006-01-02TZ", }) @@ -132,7 +134,7 @@ func TestContainerLogs(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("response"))), + Body: io.NopCloser(bytes.NewReader([]byte("response"))), }, nil }), } @@ -143,7 +145,7 @@ func TestContainerLogs(t *testing.T) { } assert.NilError(t, err) defer body.Close() - content, err := ioutil.ReadAll(body) + content, err := io.ReadAll(body) assert.NilError(t, err) assert.Check(t, is.Contains(string(content), "response")) } @@ -153,7 +155,7 @@ func ExampleClient_ContainerLogs_withTimeout() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - client, _ := NewEnvClient() + client, _ := NewClientWithOpts(FromEnv) reader, err := client.ContainerLogs(ctx, "container_id", types.ContainerLogsOptions{}) if err != nil { log.Fatal(err) diff --git a/client/container_pause_test.go b/client/container_pause_test.go index d1f73a67f34e6..45cbe21498dec 100644 --- a/client/container_pause_test.go +++ b/client/container_pause_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestContainerPauseError(t *testing.T) { @@ -15,8 +17,8 @@ func TestContainerPauseError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } err := client.ContainerPause(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -29,7 +31,7 @@ func TestContainerPause(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/container_prune.go b/client/container_prune.go index 14f88d93bac39..04383deaaffc3 100644 --- a/client/container_prune.go +++ b/client/container_prune.go @@ -23,10 +23,10 @@ func (cli *Client) ContainersPrune(ctx context.Context, pruneFilters filters.Arg } serverResp, err := cli.post(ctx, "/containers/prune", query, nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return report, err } - defer ensureReaderClosed(serverResp) if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { return report, fmt.Errorf("Error retrieving disk usage: %v", err) diff --git a/client/container_prune_test.go b/client/container_prune_test.go index 6a830d01dcacb..9525efc3bc4a9 100644 --- a/client/container_prune_test.go +++ b/client/container_prune_test.go @@ -5,15 +5,16 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestContainersPruneError(t *testing.T) { @@ -25,7 +26,9 @@ func TestContainersPruneError(t *testing.T) { filters := filters.NewArgs() _, err := client.ContainersPrune(context.Background(), filters) - assert.Check(t, is.Error(err, "Error response from daemon: Server error")) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) + } } func TestContainersPrune(t *testing.T) { @@ -111,7 +114,7 @@ func TestContainersPrune(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), version: "1.25", diff --git a/client/container_remove.go b/client/container_remove.go index ab4cfc16f8786..df81461b889c5 100644 --- a/client/container_remove.go +++ b/client/container_remove.go @@ -22,6 +22,6 @@ func (cli *Client) ContainerRemove(ctx context.Context, containerID string, opti } resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil) - ensureReaderClosed(resp) + defer ensureReaderClosed(resp) return wrapResponseError(err, resp, "container", containerID) } diff --git a/client/container_remove_test.go b/client/container_remove_test.go index d94d8313041e8..ffc15eaf7efb1 100644 --- a/client/container_remove_test.go +++ b/client/container_remove_test.go @@ -4,14 +4,15 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestContainerRemoveError(t *testing.T) { @@ -19,7 +20,9 @@ func TestContainerRemoveError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } err := client.ContainerRemove(context.Background(), "container_id", types.ContainerRemoveOptions{}) - assert.Check(t, is.Error(err, "Error response from daemon: Server error")) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) + } } func TestContainerRemoveNotFoundError(t *testing.T) { @@ -53,7 +56,7 @@ func TestContainerRemove(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/container_rename_test.go b/client/container_rename_test.go index 42be60902860d..5ccc18eb3c1a0 100644 --- a/client/container_rename_test.go +++ b/client/container_rename_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestContainerRenameError(t *testing.T) { @@ -15,8 +17,8 @@ func TestContainerRenameError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } err := client.ContainerRename(context.Background(), "nothing", "newNothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -33,7 +35,7 @@ func TestContainerRename(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/container_resize_test.go b/client/container_resize_test.go index 3c10fd7e6905e..0fd63fe3e63ed 100644 --- a/client/container_resize_test.go +++ b/client/container_resize_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestContainerResizeError(t *testing.T) { @@ -17,8 +18,8 @@ func TestContainerResizeError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } err := client.ContainerResize(context.Background(), "container_id", types.ResizeOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -27,8 +28,8 @@ func TestContainerExecResizeError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } err := client.ContainerExecResize(context.Background(), "exec_id", types.ResizeOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -76,7 +77,7 @@ func resizeTransport(expectedURL string) func(req *http.Request) (*http.Response } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil } } diff --git a/client/container_restart.go b/client/container_restart.go index 41e421969f47a..aa0d6485de39b 100644 --- a/client/container_restart.go +++ b/client/container_restart.go @@ -9,7 +9,7 @@ import ( ) // ContainerRestart stops and starts a container again. -// It makes the daemon to wait for the container to be up again for +// It makes the daemon wait for the container to be up again for // a specific amount of time, given the timeout. func (cli *Client) ContainerRestart(ctx context.Context, containerID string, timeout *time.Duration) error { query := url.Values{} diff --git a/client/container_restart_test.go b/client/container_restart_test.go index 27e81da5d8745..085da26b70f95 100644 --- a/client/container_restart_test.go +++ b/client/container_restart_test.go @@ -4,11 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "time" + + "github.com/docker/docker/errdefs" ) func TestContainerRestartError(t *testing.T) { @@ -17,8 +19,8 @@ func TestContainerRestartError(t *testing.T) { } timeout := 0 * time.Second err := client.ContainerRestart(context.Background(), "nothing", &timeout) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -35,7 +37,7 @@ func TestContainerRestart(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/container_start_test.go b/client/container_start_test.go index 277c585caa526..07fce72e5d2a0 100644 --- a/client/container_start_test.go +++ b/client/container_start_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestContainerStartError(t *testing.T) { @@ -18,8 +19,8 @@ func TestContainerStartError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } err := client.ContainerStart(context.Background(), "nothing", types.ContainerStartOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -45,7 +46,7 @@ func TestContainerStart(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/container_stats.go b/client/container_stats.go index 6ef44c77480c1..0a6488dde8266 100644 --- a/client/container_stats.go +++ b/client/container_stats.go @@ -24,3 +24,19 @@ func (cli *Client) ContainerStats(ctx context.Context, containerID string, strea osType := getDockerOS(resp.header.Get("Server")) return types.ContainerStats{Body: resp.body, OSType: osType}, err } + +// ContainerStatsOneShot gets a single stat entry from a container. +// It differs from `ContainerStats` in that the API should not wait to prime the stats +func (cli *Client) ContainerStatsOneShot(ctx context.Context, containerID string) (types.ContainerStats, error) { + query := url.Values{} + query.Set("stream", "0") + query.Set("one-shot", "1") + + resp, err := cli.get(ctx, "/containers/"+containerID+"/stats", query, nil) + if err != nil { + return types.ContainerStats{}, err + } + + osType := getDockerOS(resp.header.Get("Server")) + return types.ContainerStats{Body: resp.body, OSType: osType}, err +} diff --git a/client/container_stats_test.go b/client/container_stats_test.go index d88596315c9df..933b67ca8846a 100644 --- a/client/container_stats_test.go +++ b/client/container_stats_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestContainerStatsError(t *testing.T) { @@ -15,8 +17,8 @@ func TestContainerStatsError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerStats(context.Background(), "nothing", false) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -49,7 +51,7 @@ func TestContainerStats(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("response"))), + Body: io.NopCloser(bytes.NewReader([]byte("response"))), }, nil }), } @@ -58,7 +60,7 @@ func TestContainerStats(t *testing.T) { t.Fatal(err) } defer resp.Body.Close() - content, err := ioutil.ReadAll(resp.Body) + content, err := io.ReadAll(resp.Body) if err != nil { t.Fatal(err) } diff --git a/client/container_stop_test.go b/client/container_stop_test.go index e9af74525af9b..f5df1805cb9be 100644 --- a/client/container_stop_test.go +++ b/client/container_stop_test.go @@ -4,11 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "time" + + "github.com/docker/docker/errdefs" ) func TestContainerStopError(t *testing.T) { @@ -17,8 +19,8 @@ func TestContainerStopError(t *testing.T) { } timeout := 0 * time.Second err := client.ContainerStop(context.Background(), "nothing", &timeout) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -35,7 +37,7 @@ func TestContainerStop(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/container_top.go b/client/container_top.go index 9c9fce7a040ec..a5b78999bf0a7 100644 --- a/client/container_top.go +++ b/client/container_top.go @@ -18,11 +18,11 @@ func (cli *Client) ContainerTop(ctx context.Context, containerID string, argumen } resp, err := cli.get(ctx, "/containers/"+containerID+"/top", query, nil) + defer ensureReaderClosed(resp) if err != nil { return response, err } err = json.NewDecoder(resp.body).Decode(&response) - ensureReaderClosed(resp) return response, err } diff --git a/client/container_top_test.go b/client/container_top_test.go index 48daba7783a8d..35d5c42397844 100644 --- a/client/container_top_test.go +++ b/client/container_top_test.go @@ -5,13 +5,14 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "reflect" "strings" "testing" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/errdefs" ) func TestContainerTopError(t *testing.T) { @@ -19,8 +20,8 @@ func TestContainerTopError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerTop(context.Background(), "nothing", []string{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -56,7 +57,7 @@ func TestContainerTop(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/container_unpause_test.go b/client/container_unpause_test.go index 000699190a20e..42eef30fcacdb 100644 --- a/client/container_unpause_test.go +++ b/client/container_unpause_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestContainerUnpauseError(t *testing.T) { @@ -15,8 +17,8 @@ func TestContainerUnpauseError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } err := client.ContainerUnpause(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -29,7 +31,7 @@ func TestContainerUnpause(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/container_update.go b/client/container_update.go index 14e7f23dfb8aa..bf68a5300e9da 100644 --- a/client/container_update.go +++ b/client/container_update.go @@ -7,16 +7,15 @@ import ( "github.com/docker/docker/api/types/container" ) -// ContainerUpdate updates resources of a container +// ContainerUpdate updates the resources of a container. func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) { var response container.ContainerUpdateOKBody serverResp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil) + defer ensureReaderClosed(serverResp) if err != nil { return response, err } err = json.NewDecoder(serverResp.body).Decode(&response) - - ensureReaderClosed(serverResp) return response, err } diff --git a/client/container_update_test.go b/client/container_update_test.go index 41c6485ec84c4..83ae098be0ba9 100644 --- a/client/container_update_test.go +++ b/client/container_update_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/errdefs" ) func TestContainerUpdateError(t *testing.T) { @@ -18,8 +19,8 @@ func TestContainerUpdateError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerUpdate(context.Background(), "nothing", container.UpdateConfig{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -39,7 +40,7 @@ func TestContainerUpdate(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/container_wait_test.go b/client/container_wait_test.go index 11a9203ddcc24..ab42272887912 100644 --- a/client/container_wait_test.go +++ b/client/container_wait_test.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "log" "net/http" "strings" @@ -13,6 +13,7 @@ import ( "time" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/errdefs" ) func TestContainerWaitError(t *testing.T) { @@ -24,8 +25,8 @@ func TestContainerWaitError(t *testing.T) { case result := <-resultC: t.Fatalf("expected to not get a wait result, got %d", result.StatusCode) case err := <-errC: - if err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } } @@ -45,7 +46,7 @@ func TestContainerWait(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } @@ -65,7 +66,7 @@ func ExampleClient_ContainerWait_withTimeout() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - client, _ := NewEnvClient() + client, _ := NewClientWithOpts(FromEnv) _, errC := client.ContainerWait(ctx, "container_id", "") if err := <-errC; err != nil { log.Fatal(err) diff --git a/client/disk_usage.go b/client/disk_usage.go index 8eb30eb5de226..ba0d92e9e669c 100644 --- a/client/disk_usage.go +++ b/client/disk_usage.go @@ -4,23 +4,30 @@ import ( "context" "encoding/json" "fmt" + "net/url" "github.com/docker/docker/api/types" ) // DiskUsage requests the current data usage from the daemon -func (cli *Client) DiskUsage(ctx context.Context) (types.DiskUsage, error) { - var du types.DiskUsage +func (cli *Client) DiskUsage(ctx context.Context, options types.DiskUsageOptions) (types.DiskUsage, error) { + var query url.Values + if len(options.Types) > 0 { + query = url.Values{} + for _, t := range options.Types { + query.Add("type", string(t)) + } + } - serverResp, err := cli.get(ctx, "/system/df", nil, nil) + serverResp, err := cli.get(ctx, "/system/df", query, nil) + defer ensureReaderClosed(serverResp) if err != nil { - return du, err + return types.DiskUsage{}, err } - defer ensureReaderClosed(serverResp) + var du types.DiskUsage if err := json.NewDecoder(serverResp.body).Decode(&du); err != nil { - return du, fmt.Errorf("Error retrieving disk usage: %v", err) + return types.DiskUsage{}, fmt.Errorf("Error retrieving disk usage: %v", err) } - return du, nil } diff --git a/client/disk_usage_test.go b/client/disk_usage_test.go index 3968f75e62aeb..d64a511db0b8d 100644 --- a/client/disk_usage_test.go +++ b/client/disk_usage_test.go @@ -5,21 +5,22 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestDiskUsageError(t *testing.T) { client := &Client{ client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } - _, err := client.DiskUsage(context.Background()) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + _, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{}) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -45,11 +46,11 @@ func TestDiskUsage(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } - if _, err := client.DiskUsage(context.Background()); err != nil { + if _, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{}); err != nil { t.Fatal(err) } } diff --git a/client/distribution_inspect.go b/client/distribution_inspect.go index 7245bbeeda4d1..7f36c99a01667 100644 --- a/client/distribution_inspect.go +++ b/client/distribution_inspect.go @@ -8,7 +8,7 @@ import ( registrytypes "github.com/docker/docker/api/types/registry" ) -// DistributionInspect returns the image digest with full Manifest +// DistributionInspect returns the image digest with the full manifest. func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registrytypes.DistributionInspect, error) { // Contact the registry to retrieve digest and platform information var distributionInspect registrytypes.DistributionInspect @@ -28,11 +28,11 @@ func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegist } resp, err := cli.get(ctx, "/distribution/"+image+"/json", url.Values{}, headers) + defer ensureReaderClosed(resp) if err != nil { return distributionInspect, err } err = json.NewDecoder(resp.body).Decode(&distributionInspect) - ensureReaderClosed(resp) return distributionInspect, err } diff --git a/client/distribution_inspect_test.go b/client/distribution_inspect_test.go index a23d5f55d59fb..bb5eda4ae0ca7 100644 --- a/client/distribution_inspect_test.go +++ b/client/distribution_inspect_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/pkg/errors" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestDistributionInspectUnsupported(t *testing.T) { diff --git a/client/errors.go b/client/errors.go index 0461af329d5d2..041bc8d49c44c 100644 --- a/client/errors.go +++ b/client/errors.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/docker/docker/api/types/versions" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" ) @@ -23,8 +24,7 @@ func (err errConnectionFailed) Error() string { // IsErrConnectionFailed returns true if the error is caused by connection failed. func IsErrConnectionFailed(err error) bool { - _, ok := errors.Cause(err).(errConnectionFailed) - return ok + return errors.As(err, &errConnectionFailed{}) } // ErrorConnectionFailed returns an error with host in the error message when connection to docker daemon failed. @@ -32,16 +32,20 @@ func ErrorConnectionFailed(host string) error { return errConnectionFailed{host: host} } +// Deprecated: use the errdefs.NotFound() interface instead. Kept for backward compatibility type notFound interface { error - NotFound() bool // Is the error a NotFound error + NotFound() bool } // IsErrNotFound returns true if the error is a NotFound error, which is returned // by the API when some object is not found. func IsErrNotFound(err error) bool { - te, ok := err.(notFound) - return ok && te.NotFound() + var e notFound + if errors.As(err, &e) { + return true + } + return errdefs.IsNotFound(err) } type objectNotFoundError struct { @@ -49,9 +53,7 @@ type objectNotFoundError struct { id string } -func (e objectNotFoundError) NotFound() bool { - return true -} +func (e objectNotFoundError) NotFound() {} func (e objectNotFoundError) Error() string { return fmt.Sprintf("Error: No such %s: %s", e.object, e.id) @@ -64,7 +66,7 @@ func wrapResponseError(err error, resp serverResponse, object, id string) error case resp.statusCode == http.StatusNotFound: return objectNotFoundError{object: object, id: id} case resp.statusCode == http.StatusNotImplemented: - return notImplementedError{message: err.Error()} + return errdefs.NotImplemented(err) default: return err } @@ -83,8 +85,10 @@ func (u unauthorizedError) Error() string { // IsErrUnauthorized returns true if the error is caused // when a remote registry authentication fails func IsErrUnauthorized(err error) bool { - _, ok := err.(unauthorizedError) - return ok + if _, ok := err.(unauthorizedError); ok { + return ok + } + return errdefs.IsUnauthorized(err) } type pluginPermissionDenied struct { @@ -118,8 +122,10 @@ func (e notImplementedError) NotImplemented() bool { // This is returned by the API when a requested feature has not been // implemented. func IsErrNotImplemented(err error) bool { - te, ok := err.(notImplementedError) - return ok && te.NotImplemented() + if _, ok := err.(notImplementedError); ok { + return ok + } + return errdefs.IsNotImplemented(err) } // NewVersionError returns an error if the APIVersion required diff --git a/client/events.go b/client/events.go index 6e56538955ee6..f0dc9d9e12f32 100644 --- a/client/events.go +++ b/client/events.go @@ -90,6 +90,7 @@ func buildEventsQueryParams(cliVersion string, options types.EventsOptions) (url } if options.Filters.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code filterJSON, err := filters.ToParamWithVersion(cliVersion, options.Filters) if err != nil { return nil, err diff --git a/client/events_test.go b/client/events_test.go index 4a39901b45b1f..bcd11ab252d54 100644 --- a/client/events_test.go +++ b/client/events_test.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "strings" "testing" @@ -14,6 +13,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/errdefs" ) func TestEventsErrorInOptions(t *testing.T) { @@ -52,8 +52,8 @@ func TestEventsErrorFromServer(t *testing.T) { } _, errs := client.Events(context.Background(), types.EventsOptions{}) err := <-errs - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -90,17 +90,17 @@ func TestEvents(t *testing.T) { }, events: []events.Message{ { - Type: "container", + Type: events.BuilderEventType, ID: "1", Action: "create", }, { - Type: "container", + Type: events.BuilderEventType, ID: "2", Action: "die", }, { - Type: "container", + Type: events.BuilderEventType, ID: "3", Action: "create", }, @@ -137,7 +137,7 @@ func TestEvents(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(buffer), + Body: io.NopCloser(buffer), }, nil }), } diff --git a/client/hijack.go b/client/hijack.go index 0ac8248f2c7be..e1dc49ef0f66a 100644 --- a/client/hijack.go +++ b/client/hijack.go @@ -23,8 +23,8 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu return types.HijackedResponse{}, err } - apiPath := cli.getAPIPath(path, query) - req, err := http.NewRequest("POST", apiPath, bodyEncoded) + apiPath := cli.getAPIPath(ctx, path, query) + req, err := http.NewRequest(http.MethodPost, apiPath, bodyEncoded) if err != nil { return types.HijackedResponse{}, err } @@ -38,6 +38,17 @@ func (cli *Client) postHijacked(ctx context.Context, path string, query url.Valu return types.HijackedResponse{Conn: conn, Reader: bufio.NewReader(conn)}, err } +// DialHijack returns a hijacked connection with negotiated protocol proto. +func (cli *Client) DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error) { + req, err := http.NewRequest(http.MethodPost, url, nil) + if err != nil { + return nil, err + } + req = cli.addHeaders(req, meta) + + return cli.setupHijackConn(ctx, req, proto) +} + // fallbackDial is used when WithDialer() was not called. // See cli.Dialer(). func fallbackDial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) { @@ -76,6 +87,8 @@ func (cli *Client) setupHijackConn(ctx context.Context, req *http.Request, proto // Server hijacks the connection, error 'connection closed' expected resp, err := clientconn.Do(req) + + //nolint:staticcheck // ignore SA1019 for connecting to old (pre go1.8) daemons if err != httputil.ErrPersistEOF { if err != nil { return nil, err diff --git a/client/hijack_test.go b/client/hijack_test.go index d71dc9ea38cc8..67d3894756021 100644 --- a/client/hijack_test.go +++ b/client/hijack_test.go @@ -3,7 +3,7 @@ package client import ( "context" "fmt" - "io/ioutil" + "io" "net" "net/http" "net/http/httptest" @@ -13,7 +13,7 @@ import ( "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/types" "github.com/pkg/errors" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestTLSCloseWriter(t *testing.T) { @@ -25,13 +25,13 @@ func TestTLSCloseWriter(t *testing.T) { defer close(chErr) if err := httputils.ParseForm(req); err != nil { chErr <- errors.Wrap(err, "error parsing form") - http.Error(w, err.Error(), 500) + http.Error(w, err.Error(), http.StatusInternalServerError) return } r, rw, err := httputils.HijackConnection(w) if err != nil { chErr <- errors.Wrap(err, "error hijacking connection") - http.Error(w, err.Error(), 500) + http.Error(w, err.Error(), http.StatusInternalServerError) return } defer r.Close() @@ -61,7 +61,7 @@ func TestTLSCloseWriter(t *testing.T) { break } } - assert.Assert(t, err) + assert.NilError(t, err) ts.Listener = l defer l.Close() @@ -76,13 +76,13 @@ func TestTLSCloseWriter(t *testing.T) { defer ts.Close() serverURL, err := url.Parse(ts.URL) - assert.Assert(t, err) + assert.NilError(t, err) - client, err := NewClient("tcp://"+serverURL.Host, "", ts.Client(), nil) - assert.Assert(t, err) + client, err := NewClientWithOpts(WithHost("tcp://"+serverURL.Host), WithHTTPClient(ts.Client())) + assert.NilError(t, err) resp, err := client.postHijacked(context.Background(), "/asdf", url.Values{}, nil, map[string][]string{"Content-Type": {"text/plain"}}) - assert.Assert(t, err) + assert.NilError(t, err) defer resp.Close() if _, ok := resp.Conn.(types.CloseWriter); !ok { @@ -90,10 +90,10 @@ func TestTLSCloseWriter(t *testing.T) { } _, err = resp.Conn.Write([]byte("hello")) - assert.Assert(t, err) + assert.NilError(t, err) - b, err := ioutil.ReadAll(resp.Reader) - assert.Assert(t, err) + b, err := io.ReadAll(resp.Reader) + assert.NilError(t, err) assert.Assert(t, string(b) == "hello") assert.Assert(t, resp.CloseWrite()) diff --git a/client/image_build.go b/client/image_build.go index 9add3c10b348b..d16e1d8ea98ae 100644 --- a/client/image_build.go +++ b/client/image_build.go @@ -14,8 +14,8 @@ import ( "github.com/docker/docker/api/types/container" ) -// ImageBuild sends request to the daemon to build images. -// The Body in the response implement an io.ReadCloser and it's up to the caller to +// ImageBuild sends a request to the daemon to build images. +// The Body in the response implements an io.ReadCloser and it's up to the caller to // close it. func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) { query, err := cli.imageBuildOptionsToQuery(options) @@ -134,5 +134,13 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur query.Set("buildid", options.BuildID) } query.Set("version", string(options.Version)) + + if options.Outputs != nil { + outputsJSON, err := json.Marshal(options.Outputs) + if err != nil { + return query, err + } + query.Set("outputs", string(outputsJSON)) + } return query, nil } diff --git a/client/image_build_test.go b/client/image_build_test.go index 95c11bc3e5e9a..3d6c0a972d97c 100644 --- a/client/image_build_test.go +++ b/client/image_build_test.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "reflect" "strings" @@ -12,7 +12,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - "github.com/docker/go-units" + "github.com/docker/docker/errdefs" + units "github.com/docker/go-units" ) func TestImageBuildError(t *testing.T) { @@ -20,8 +21,8 @@ func TestImageBuildError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ImageBuild(context.Background(), nil, types.ImageBuildOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -194,7 +195,7 @@ func TestImageBuild(t *testing.T) { headers.Add("Server", "Docker/v1.23 (MyOS)") return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), Header: headers, }, nil }), @@ -206,7 +207,7 @@ func TestImageBuild(t *testing.T) { if buildResponse.OSType != "MyOS" { t.Fatalf("expected OSType to be 'MyOS', got %s", buildResponse.OSType) } - response, err := ioutil.ReadAll(buildResponse.Body) + response, err := io.ReadAll(buildResponse.Body) if err != nil { t.Fatal(err) } diff --git a/client/image_create.go b/client/image_create.go index 239380474e618..b1c0227775ce5 100644 --- a/client/image_create.go +++ b/client/image_create.go @@ -10,7 +10,7 @@ import ( "github.com/docker/docker/api/types" ) -// ImageCreate creates a new image based in the parent options. +// ImageCreate creates a new image based on the parent options. // It returns the JSON content in the response body. func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) { ref, err := reference.ParseNormalizedNamed(parentReference) diff --git a/client/image_create_test.go b/client/image_create_test.go index b89f16d27b7d6..3095cbc05b8be 100644 --- a/client/image_create_test.go +++ b/client/image_create_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestImageCreateError(t *testing.T) { @@ -17,8 +18,8 @@ func TestImageCreateError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ImageCreate(context.Background(), "reference", types.ImageCreateOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -51,7 +52,7 @@ func TestImageCreate(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), }, nil }), } @@ -62,7 +63,7 @@ func TestImageCreate(t *testing.T) { if err != nil { t.Fatal(err) } - response, err := ioutil.ReadAll(createResponse) + response, err := io.ReadAll(createResponse) if err != nil { t.Fatal(err) } diff --git a/client/image_history.go b/client/image_history.go index 0151b9517f250..b5bea10d8f638 100644 --- a/client/image_history.go +++ b/client/image_history.go @@ -12,11 +12,11 @@ import ( func (cli *Client) ImageHistory(ctx context.Context, imageID string) ([]image.HistoryResponseItem, error) { var history []image.HistoryResponseItem serverResp, err := cli.get(ctx, "/images/"+imageID+"/history", url.Values{}, nil) + defer ensureReaderClosed(serverResp) if err != nil { return history, err } err = json.NewDecoder(serverResp.body).Decode(&history) - ensureReaderClosed(serverResp) return history, err } diff --git a/client/image_history_test.go b/client/image_history_test.go index 0217bf575a34d..bb4a2b11fc2e4 100644 --- a/client/image_history_test.go +++ b/client/image_history_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/image" + "github.com/docker/docker/errdefs" ) func TestImageHistoryError(t *testing.T) { @@ -18,8 +19,8 @@ func TestImageHistoryError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ImageHistory(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -46,7 +47,7 @@ func TestImageHistory(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/image_import.go b/client/image_import.go index c2972ea950eb8..c5de42cb799d3 100644 --- a/client/image_import.go +++ b/client/image_import.go @@ -10,11 +10,11 @@ import ( "github.com/docker/docker/api/types" ) -// ImageImport creates a new image based in the source options. +// ImageImport creates a new image based on the source options. // It returns the JSON content in the response body. func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { if ref != "" { - //Check if the given image name can be resolved + // Check if the given image name can be resolved if _, err := reference.ParseNormalizedNamed(ref); err != nil { return nil, err } diff --git a/client/image_import_test.go b/client/image_import_test.go index 944cd52fecef2..34f1e0642fe8d 100644 --- a/client/image_import_test.go +++ b/client/image_import_test.go @@ -4,13 +4,14 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "reflect" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestImageImportError(t *testing.T) { @@ -18,8 +19,8 @@ func TestImageImportError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ImageImport(context.Background(), types.ImageImportSource{}, "image:tag", types.ImageImportOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -55,7 +56,7 @@ func TestImageImport(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("response"))), + Body: io.NopCloser(bytes.NewReader([]byte("response"))), }, nil }), } @@ -70,7 +71,7 @@ func TestImageImport(t *testing.T) { if err != nil { t.Fatal(err) } - response, err := ioutil.ReadAll(importResponse) + response, err := io.ReadAll(importResponse) if err != nil { t.Fatal(err) } diff --git a/client/image_inspect.go b/client/image_inspect.go index 2f8f6d2f1416c..03aa12d8b4cdd 100644 --- a/client/image_inspect.go +++ b/client/image_inspect.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" + "io" "github.com/docker/docker/api/types" ) @@ -15,12 +15,12 @@ func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (typ return types.ImageInspect{}, nil, objectNotFoundError{object: "image", id: imageID} } serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID) } - defer ensureReaderClosed(serverResp) - body, err := ioutil.ReadAll(serverResp.body) + body, err := io.ReadAll(serverResp.body) if err != nil { return types.ImageInspect{}, nil, err } diff --git a/client/image_inspect_test.go b/client/image_inspect_test.go index a910872d1c726..893552a2e44c4 100644 --- a/client/image_inspect_test.go +++ b/client/image_inspect_test.go @@ -5,13 +5,14 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "reflect" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" ) @@ -21,8 +22,8 @@ func TestImageInspectError(t *testing.T) { } _, _, err := client.ImageInspectWithRaw(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -66,7 +67,7 @@ func TestImageInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/image_list.go b/client/image_list.go index 32fae27b375e4..a4d7505094cd5 100644 --- a/client/image_list.go +++ b/client/image_list.go @@ -24,6 +24,7 @@ func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions } } if optionFilters.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code filterJSON, err := filters.ToParamWithVersion(cli.version, optionFilters) if err != nil { return images, err @@ -35,11 +36,11 @@ func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions } serverResp, err := cli.get(ctx, "/images/json", query, nil) + defer ensureReaderClosed(serverResp) if err != nil { return images, err } err = json.NewDecoder(serverResp.body).Decode(&images) - ensureReaderClosed(serverResp) return images, err } diff --git a/client/image_list_test.go b/client/image_list_test.go index 3ba5239a536fc..4619393ff3cb9 100644 --- a/client/image_list_test.go +++ b/client/image_list_test.go @@ -5,13 +5,14 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/errdefs" ) func TestImageListError(t *testing.T) { @@ -20,8 +21,8 @@ func TestImageListError(t *testing.T) { } _, err := client.ImageList(context.Background(), types.ImageListOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -95,7 +96,7 @@ func TestImageList(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } @@ -136,7 +137,7 @@ func TestImageListApiBefore125(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), version: "1.24", diff --git a/client/image_load_test.go b/client/image_load_test.go index 116317da75f34..4e53cb83a3f2c 100644 --- a/client/image_load_test.go +++ b/client/image_load_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestImageLoadError(t *testing.T) { @@ -16,8 +18,8 @@ func TestImageLoadError(t *testing.T) { } _, err := client.ImageLoad(context.Background(), nil, true) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -69,7 +71,7 @@ func TestImageLoad(t *testing.T) { headers.Add("Content-Type", loadCase.responseContentType) return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(expectedOutput))), + Body: io.NopCloser(bytes.NewReader([]byte(expectedOutput))), Header: headers, }, nil }), @@ -83,7 +85,7 @@ func TestImageLoad(t *testing.T) { if imageLoadResponse.JSON != loadCase.expectedResponseJSON { t.Fatalf("expected a JSON response, was not.") } - body, err := ioutil.ReadAll(imageLoadResponse.Body) + body, err := io.ReadAll(imageLoadResponse.Body) if err != nil { t.Fatal(err) } diff --git a/client/image_prune.go b/client/image_prune.go index 78ee3f6c496f3..56af6d7f98f4f 100644 --- a/client/image_prune.go +++ b/client/image_prune.go @@ -23,10 +23,10 @@ func (cli *Client) ImagesPrune(ctx context.Context, pruneFilters filters.Args) ( } serverResp, err := cli.post(ctx, "/images/prune", query, nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return report, err } - defer ensureReaderClosed(serverResp) if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { return report, fmt.Errorf("Error retrieving disk usage: %v", err) diff --git a/client/image_prune_test.go b/client/image_prune_test.go index 9b0839bb6c12b..a3652a10034f7 100644 --- a/client/image_prune_test.go +++ b/client/image_prune_test.go @@ -5,15 +5,17 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + "github.com/docker/docker/errdefs" + "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestImagesPruneError(t *testing.T) { @@ -22,10 +24,10 @@ func TestImagesPruneError(t *testing.T) { version: "1.25", } - filters := filters.NewArgs() - - _, err := client.ImagesPrune(context.Background(), filters) - assert.Check(t, is.Error(err, "Error response from daemon: Server error")) + _, err := client.ImagesPrune(context.Background(), filters.NewArgs()) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) + } } func TestImagesPrune(t *testing.T) { @@ -106,7 +108,7 @@ func TestImagesPrune(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), version: "1.25", diff --git a/client/image_pull.go b/client/image_pull.go index d97aacf8c5291..a23975591be27 100644 --- a/client/image_pull.go +++ b/client/image_pull.go @@ -3,12 +3,12 @@ package client // import "github.com/docker/docker/client" import ( "context" "io" - "net/http" "net/url" "strings" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) // ImagePull requests the docker host to pull an image from a remote registry. @@ -35,7 +35,7 @@ func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.I } resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth) - if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil { + if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { newAuthHeader, privilegeErr := options.PrivilegeFunc() if privilegeErr != nil { return nil, privilegeErr diff --git a/client/image_pull_test.go b/client/image_pull_test.go index 361c5c2be37a3..92793e1e3994a 100644 --- a/client/image_pull_test.go +++ b/client/image_pull_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestImagePullReferenceParseError(t *testing.T) { @@ -30,8 +31,8 @@ func TestImagePullAnyError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ImagePull(context.Background(), "myimage", types.ImagePullOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -40,8 +41,8 @@ func TestImagePullStatusUnauthorizedError(t *testing.T) { client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")), } _, err := client.ImagePull(context.Background(), "myimage", types.ImagePullOptions{}) - if err == nil || err.Error() != "Error response from daemon: Unauthorized error" { - t.Fatalf("expected an Unauthorized Error, got %v", err) + if !errdefs.IsUnauthorized(err) { + t.Fatalf("expected a Unauthorized Error, got %[1]T: %[1]v", err) } } @@ -70,8 +71,8 @@ func TestImagePullWithUnauthorizedErrorAndAnotherUnauthorizedError(t *testing.T) _, err := client.ImagePull(context.Background(), "myimage", types.ImagePullOptions{ PrivilegeFunc: privilegeFunc, }) - if err == nil || err.Error() != "Error response from daemon: Unauthorized error" { - t.Fatalf("expected an Unauthorized Error, got %v", err) + if !errdefs.IsUnauthorized(err) { + t.Fatalf("expected a Unauthorized Error, got %[1]T: %[1]v", err) } } @@ -86,7 +87,7 @@ func TestImagePullWithPrivilegedFuncNoError(t *testing.T) { if auth == "NotValid" { return &http.Response{ StatusCode: http.StatusUnauthorized, - Body: ioutil.NopCloser(bytes.NewReader([]byte("Invalid credentials"))), + Body: io.NopCloser(bytes.NewReader([]byte("Invalid credentials"))), }, nil } if auth != "IAmValid" { @@ -103,7 +104,7 @@ func TestImagePullWithPrivilegedFuncNoError(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("hello world"))), + Body: io.NopCloser(bytes.NewReader([]byte("hello world"))), }, nil }), } @@ -117,7 +118,7 @@ func TestImagePullWithPrivilegedFuncNoError(t *testing.T) { if err != nil { t.Fatal(err) } - body, err := ioutil.ReadAll(resp) + body, err := io.ReadAll(resp) if err != nil { t.Fatal(err) } @@ -177,7 +178,7 @@ func TestImagePullWithoutErrors(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(expectedOutput))), + Body: io.NopCloser(bytes.NewReader([]byte(expectedOutput))), }, nil }), } @@ -187,7 +188,7 @@ func TestImagePullWithoutErrors(t *testing.T) { if err != nil { t.Fatal(err) } - body, err := ioutil.ReadAll(resp) + body, err := io.ReadAll(resp) if err != nil { t.Fatal(err) } diff --git a/client/image_push.go b/client/image_push.go index a15871c2b4480..845580d4a4cdb 100644 --- a/client/image_push.go +++ b/client/image_push.go @@ -4,11 +4,11 @@ import ( "context" "errors" "io" - "net/http" "net/url" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) // ImagePush requests the docker host to push an image to a remote registry. @@ -25,18 +25,17 @@ func (cli *Client) ImagePush(ctx context.Context, image string, options types.Im return nil, errors.New("cannot push a digest reference") } - tag := "" name := reference.FamiliarName(ref) - - if nameTaggedRef, isNamedTagged := ref.(reference.NamedTagged); isNamedTagged { - tag = nameTaggedRef.Tag() - } - query := url.Values{} - query.Set("tag", tag) + if !options.All { + ref = reference.TagNameOnly(ref) + if tagged, ok := ref.(reference.Tagged); ok { + query.Set("tag", tagged.Tag()) + } + } resp, err := cli.tryImagePush(ctx, name, query, options.RegistryAuth) - if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil { + if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { newAuthHeader, privilegeErr := options.PrivilegeFunc() if privilegeErr != nil { return nil, privilegeErr diff --git a/client/image_push_test.go b/client/image_push_test.go index 0693601af1917..9b161717caac4 100644 --- a/client/image_push_test.go +++ b/client/image_push_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestImagePushReferenceError(t *testing.T) { @@ -35,8 +36,8 @@ func TestImagePushAnyError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ImagePush(context.Background(), "myimage", types.ImagePushOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -45,8 +46,8 @@ func TestImagePushStatusUnauthorizedError(t *testing.T) { client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")), } _, err := client.ImagePush(context.Background(), "myimage", types.ImagePushOptions{}) - if err == nil || err.Error() != "Error response from daemon: Unauthorized error" { - t.Fatalf("expected an Unauthorized Error, got %v", err) + if !errdefs.IsUnauthorized(err) { + t.Fatalf("expected a Unauthorized Error, got %[1]T: %[1]v", err) } } @@ -75,8 +76,8 @@ func TestImagePushWithUnauthorizedErrorAndAnotherUnauthorizedError(t *testing.T) _, err := client.ImagePush(context.Background(), "myimage", types.ImagePushOptions{ PrivilegeFunc: privilegeFunc, }) - if err == nil || err.Error() != "Error response from daemon: Unauthorized error" { - t.Fatalf("expected an Unauthorized Error, got %v", err) + if !errdefs.IsUnauthorized(err) { + t.Fatalf("expected a Unauthorized Error, got %[1]T: %[1]v", err) } } @@ -91,7 +92,7 @@ func TestImagePushWithPrivilegedFuncNoError(t *testing.T) { if auth == "NotValid" { return &http.Response{ StatusCode: http.StatusUnauthorized, - Body: ioutil.NopCloser(bytes.NewReader([]byte("Invalid credentials"))), + Body: io.NopCloser(bytes.NewReader([]byte("Invalid credentials"))), }, nil } if auth != "IAmValid" { @@ -104,7 +105,7 @@ func TestImagePushWithPrivilegedFuncNoError(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("hello world"))), + Body: io.NopCloser(bytes.NewReader([]byte("hello world"))), }, nil }), } @@ -118,7 +119,7 @@ func TestImagePushWithPrivilegedFuncNoError(t *testing.T) { if err != nil { t.Fatal(err) } - body, err := ioutil.ReadAll(resp) + body, err := io.ReadAll(resp) if err != nil { t.Fatal(err) } @@ -130,50 +131,70 @@ func TestImagePushWithPrivilegedFuncNoError(t *testing.T) { func TestImagePushWithoutErrors(t *testing.T) { expectedOutput := "hello world" expectedURLFormat := "/images/%s/push" - pullCases := []struct { + testCases := []struct { + all bool reference string expectedImage string expectedTag string }{ { + all: false, reference: "myimage", expectedImage: "myimage", - expectedTag: "", + expectedTag: "latest", }, { + all: false, reference: "myimage:tag", expectedImage: "myimage", expectedTag: "tag", }, + { + all: true, + reference: "myimage", + expectedImage: "myimage", + expectedTag: "", + }, + { + all: true, + reference: "myimage:anything", + expectedImage: "myimage", + expectedTag: "", + }, } - for _, pullCase := range pullCases { - client := &Client{ - client: newMockClient(func(req *http.Request) (*http.Response, error) { - expectedURL := fmt.Sprintf(expectedURLFormat, pullCase.expectedImage) - if !strings.HasPrefix(req.URL.Path, expectedURL) { - return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) - } - query := req.URL.Query() - tag := query.Get("tag") - if tag != pullCase.expectedTag { - return nil, fmt.Errorf("tag not set in URL query properly. Expected '%s', got %s", pullCase.expectedTag, tag) - } - return &http.Response{ - StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(expectedOutput))), - }, nil - }), - } - resp, err := client.ImagePush(context.Background(), pullCase.reference, types.ImagePushOptions{}) - if err != nil { - t.Fatal(err) - } - body, err := ioutil.ReadAll(resp) - if err != nil { - t.Fatal(err) - } - if string(body) != expectedOutput { - t.Fatalf("expected '%s', got %s", expectedOutput, string(body)) - } + for _, tc := range testCases { + tc := tc + t.Run(fmt.Sprintf("%s,all-tags=%t", tc.reference, tc.all), func(t *testing.T) { + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + expectedURL := fmt.Sprintf(expectedURLFormat, tc.expectedImage) + if !strings.HasPrefix(req.URL.Path, expectedURL) { + return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) + } + query := req.URL.Query() + tag := query.Get("tag") + if tag != tc.expectedTag { + return nil, fmt.Errorf("tag not set in URL query properly. Expected '%s', got %s", tc.expectedTag, tag) + } + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader([]byte(expectedOutput))), + }, nil + }), + } + resp, err := client.ImagePush(context.Background(), tc.reference, types.ImagePushOptions{ + All: tc.all, + }) + if err != nil { + t.Fatal(err) + } + body, err := io.ReadAll(resp) + if err != nil { + t.Fatal(err) + } + if string(body) != expectedOutput { + t.Fatalf("expected '%s', got %s", expectedOutput, string(body)) + } + }) } } diff --git a/client/image_remove.go b/client/image_remove.go index 45d6e6f0dbad2..84a41af0f2ca7 100644 --- a/client/image_remove.go +++ b/client/image_remove.go @@ -21,11 +21,11 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options type var dels []types.ImageDeleteResponseItem resp, err := cli.delete(ctx, "/images/"+imageID, query, nil) + defer ensureReaderClosed(resp) if err != nil { return dels, wrapResponseError(err, resp, "image", imageID) } err = json.NewDecoder(resp.body).Decode(&dels) - ensureReaderClosed(resp) return dels, err } diff --git a/client/image_remove_test.go b/client/image_remove_test.go index acc6bc9177602..919baa2f68434 100644 --- a/client/image_remove_test.go +++ b/client/image_remove_test.go @@ -5,14 +5,15 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestImageRemoveError(t *testing.T) { @@ -21,7 +22,9 @@ func TestImageRemoveError(t *testing.T) { } _, err := client.ImageRemove(context.Background(), "image_id", types.ImageRemoveOptions{}) - assert.Check(t, is.Error(err, "Error response from daemon: Server error")) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) + } } func TestImageRemoveImageNotFound(t *testing.T) { @@ -63,7 +66,7 @@ func TestImageRemove(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "DELETE" { + if req.Method != http.MethodDelete { return nil, fmt.Errorf("expected DELETE method, got %s", req.Method) } query := req.URL.Query() @@ -87,7 +90,7 @@ func TestImageRemove(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/image_save_test.go b/client/image_save_test.go index a40055e583e99..c90a742c4a572 100644 --- a/client/image_save_test.go +++ b/client/image_save_test.go @@ -4,11 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "reflect" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestImageSaveError(t *testing.T) { @@ -16,8 +18,8 @@ func TestImageSaveError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ImageSave(context.Background(), []string{"nothing"}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -37,7 +39,7 @@ func TestImageSave(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("response"))), + Body: io.NopCloser(bytes.NewReader([]byte("response"))), }, nil }), } @@ -45,7 +47,7 @@ func TestImageSave(t *testing.T) { if err != nil { t.Fatal(err) } - response, err := ioutil.ReadAll(saveResponse) + response, err := io.ReadAll(saveResponse) if err != nil { t.Fatal(err) } diff --git a/client/image_search.go b/client/image_search.go index 176de3c582404..5f40a22a964cc 100644 --- a/client/image_search.go +++ b/client/image_search.go @@ -4,15 +4,15 @@ import ( "context" "encoding/json" "fmt" - "net/http" "net/url" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/errdefs" ) -// ImageSearch makes the docker host to search by a term in a remote registry. +// ImageSearch makes the docker host search by a term in a remote registry. // The list of results is not sorted in any fashion. func (cli *Client) ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error) { var results []registry.SearchResult @@ -29,7 +29,8 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options types.I } resp, err := cli.tryImageSearch(ctx, query, options.RegistryAuth) - if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil { + defer ensureReaderClosed(resp) + if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { newAuthHeader, privilegeErr := options.PrivilegeFunc() if privilegeErr != nil { return results, privilegeErr @@ -41,7 +42,6 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options types.I } err = json.NewDecoder(resp.body).Decode(&results) - ensureReaderClosed(resp) return results, err } diff --git a/client/image_search_test.go b/client/image_search_test.go index 1456cd606fbe1..ced6deeb2bdb5 100644 --- a/client/image_search_test.go +++ b/client/image_search_test.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/errdefs" ) func TestImageSearchAnyError(t *testing.T) { @@ -20,8 +21,8 @@ func TestImageSearchAnyError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ImageSearch(context.Background(), "some-image", types.ImageSearchOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -30,8 +31,8 @@ func TestImageSearchStatusUnauthorizedError(t *testing.T) { client: newMockClient(errorMock(http.StatusUnauthorized, "Unauthorized error")), } _, err := client.ImageSearch(context.Background(), "some-image", types.ImageSearchOptions{}) - if err == nil || err.Error() != "Error response from daemon: Unauthorized error" { - t.Fatalf("expected an Unauthorized Error, got %v", err) + if !errdefs.IsUnauthorized(err) { + t.Fatalf("expected a Unauthorized Error, got %[1]T: %[1]v", err) } } @@ -60,8 +61,8 @@ func TestImageSearchWithUnauthorizedErrorAndAnotherUnauthorizedError(t *testing. _, err := client.ImageSearch(context.Background(), "some-image", types.ImageSearchOptions{ PrivilegeFunc: privilegeFunc, }) - if err == nil || err.Error() != "Error response from daemon: Unauthorized error" { - t.Fatalf("expected an Unauthorized Error, got %v", err) + if !errdefs.IsUnauthorized(err) { + t.Fatalf("expected a Unauthorized Error, got %[1]T: %[1]v", err) } } @@ -76,7 +77,7 @@ func TestImageSearchWithPrivilegedFuncNoError(t *testing.T) { if auth == "NotValid" { return &http.Response{ StatusCode: http.StatusUnauthorized, - Body: ioutil.NopCloser(bytes.NewReader([]byte("Invalid credentials"))), + Body: io.NopCloser(bytes.NewReader([]byte("Invalid credentials"))), }, nil } if auth != "IAmValid" { @@ -97,7 +98,7 @@ func TestImageSearchWithPrivilegedFuncNoError(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } @@ -148,7 +149,7 @@ func TestImageSearchWithoutErrors(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/image_tag_test.go b/client/image_tag_test.go index 2923bb995ba12..63653af20744e 100644 --- a/client/image_tag_test.go +++ b/client/image_tag_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestImageTagError(t *testing.T) { @@ -16,8 +18,8 @@ func TestImageTagError(t *testing.T) { } err := client.ImageTag(context.Background(), "image_id", "repo:tag") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -118,7 +120,7 @@ func TestImageTag(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } query := req.URL.Query() @@ -130,7 +132,7 @@ func TestImageTag(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/info.go b/client/info.go index 121f256ab1cce..c856704e23f4f 100644 --- a/client/info.go +++ b/client/info.go @@ -13,10 +13,10 @@ import ( func (cli *Client) Info(ctx context.Context) (types.Info, error) { var info types.Info serverResp, err := cli.get(ctx, "/info", url.Values{}, nil) + defer ensureReaderClosed(serverResp) if err != nil { return info, err } - defer ensureReaderClosed(serverResp) if err := json.NewDecoder(serverResp.body).Decode(&info); err != nil { return info, fmt.Errorf("Error reading remote info: %v", err) diff --git a/client/info_test.go b/client/info_test.go index 866d8e8849b97..f1d538373823a 100644 --- a/client/info_test.go +++ b/client/info_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestInfoServerError(t *testing.T) { @@ -18,8 +19,8 @@ func TestInfoServerError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.Info(context.Background()) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -28,7 +29,7 @@ func TestInfoInvalidResponseJSONError(t *testing.T) { client: newMockClient(func(req *http.Request) (*http.Response, error) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("invalid json"))), + Body: io.NopCloser(bytes.NewReader([]byte("invalid json"))), }, nil }), } @@ -56,7 +57,7 @@ func TestInfo(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/interface.go b/client/interface.go index d190f8e58db8e..7277d1bbdd296 100644 --- a/client/interface.go +++ b/client/interface.go @@ -16,6 +16,7 @@ import ( "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/swarm" volumetypes "github.com/docker/docker/api/types/volume" + specs "github.com/opencontainers/image-spec/specs-go/v1" ) // CommonAPIClient is the common methods between stable and experimental versions of APIClient. @@ -38,7 +39,7 @@ type CommonAPIClient interface { ServerVersion(ctx context.Context) (types.Version, error) NegotiateAPIVersion(ctx context.Context) NegotiateAPIVersionPing(types.Ping) - DialSession(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) + DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error) Dialer() func(context.Context) (net.Conn, error) Close() error } @@ -47,7 +48,7 @@ type CommonAPIClient interface { type ContainerAPIClient interface { ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) - ContainerCreate(ctx context.Context, config *containertypes.Config, hostConfig *containertypes.HostConfig, networkingConfig *networktypes.NetworkingConfig, containerName string) (containertypes.ContainerCreateCreatedBody, error) + ContainerCreate(ctx context.Context, config *containertypes.Config, hostConfig *containertypes.HostConfig, networkingConfig *networktypes.NetworkingConfig, platform *specs.Platform, containerName string) (containertypes.ContainerCreateCreatedBody, error) ContainerDiff(ctx context.Context, container string) ([]containertypes.ContainerChangeResponseItem, error) ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error) @@ -67,6 +68,7 @@ type ContainerAPIClient interface { ContainerRestart(ctx context.Context, container string, timeout *time.Duration) error ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error) ContainerStats(ctx context.Context, container string, stream bool) (types.ContainerStats, error) + ContainerStatsOneShot(ctx context.Context, container string) (types.ContainerStats, error) ContainerStart(ctx context.Context, container string, options types.ContainerStartOptions) error ContainerStop(ctx context.Context, container string, timeout *time.Duration) error ContainerTop(ctx context.Context, container string, arguments []string) (containertypes.ContainerTopOKBody, error) @@ -166,7 +168,7 @@ type SystemAPIClient interface { Events(ctx context.Context, options types.EventsOptions) (<-chan events.Message, <-chan error) Info(ctx context.Context) (types.Info, error) RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error) - DiskUsage(ctx context.Context) (types.DiskUsage, error) + DiskUsage(ctx context.Context, options types.DiskUsageOptions) (types.DiskUsage, error) Ping(ctx context.Context) (types.Ping, error) } diff --git a/client/login.go b/client/login.go index 7d66181900401..f058520638238 100644 --- a/client/login.go +++ b/client/login.go @@ -3,7 +3,6 @@ package client // import "github.com/docker/docker/client" import ( "context" "encoding/json" - "net/http" "net/url" "github.com/docker/docker/api/types" @@ -14,16 +13,13 @@ import ( // It returns unauthorizedError when the authentication fails. func (cli *Client) RegistryLogin(ctx context.Context, auth types.AuthConfig) (registry.AuthenticateOKBody, error) { resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil) + defer ensureReaderClosed(resp) - if resp.statusCode == http.StatusUnauthorized { - return registry.AuthenticateOKBody{}, unauthorizedError{err} - } if err != nil { return registry.AuthenticateOKBody{}, err } var response registry.AuthenticateOKBody err = json.NewDecoder(resp.body).Decode(&response) - ensureReaderClosed(resp) return response, err } diff --git a/client/network_connect_test.go b/client/network_connect_test.go index 07a3ba692ea39..30f2f254d63cd 100644 --- a/client/network_connect_test.go +++ b/client/network_connect_test.go @@ -5,13 +5,14 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" + "github.com/docker/docker/errdefs" ) func TestNetworkConnectError(t *testing.T) { @@ -20,8 +21,8 @@ func TestNetworkConnectError(t *testing.T) { } err := client.NetworkConnect(context.Background(), "network_id", "container_id", nil) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -34,7 +35,7 @@ func TestNetworkConnectEmptyNilEndpointSettings(t *testing.T) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } @@ -53,7 +54,7 @@ func TestNetworkConnectEmptyNilEndpointSettings(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } @@ -73,7 +74,7 @@ func TestNetworkConnect(t *testing.T) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } @@ -96,7 +97,7 @@ func TestNetworkConnect(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/network_create.go b/client/network_create.go index 41da2ac6109ab..278d9383a86b2 100644 --- a/client/network_create.go +++ b/client/network_create.go @@ -15,11 +15,11 @@ func (cli *Client) NetworkCreate(ctx context.Context, name string, options types } var response types.NetworkCreateResponse serverResp, err := cli.post(ctx, "/networks/create", nil, networkCreateRequest, nil) + defer ensureReaderClosed(serverResp) if err != nil { return response, err } - json.NewDecoder(serverResp.body).Decode(&response) - ensureReaderClosed(serverResp) + err = json.NewDecoder(serverResp.body).Decode(&response) return response, err } diff --git a/client/network_create_test.go b/client/network_create_test.go index 894c98ebb3ab1..72969864b7682 100644 --- a/client/network_create_test.go +++ b/client/network_create_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestNetworkCreateError(t *testing.T) { @@ -19,8 +20,8 @@ func TestNetworkCreateError(t *testing.T) { } _, err := client.NetworkCreate(context.Background(), "mynetwork", types.NetworkCreate{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -33,7 +34,7 @@ func TestNetworkCreate(t *testing.T) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } @@ -46,7 +47,7 @@ func TestNetworkCreate(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/network_disconnect_test.go b/client/network_disconnect_test.go index b27b955e2ee03..5a44bcd8e054d 100644 --- a/client/network_disconnect_test.go +++ b/client/network_disconnect_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestNetworkDisconnectError(t *testing.T) { @@ -19,8 +20,8 @@ func TestNetworkDisconnectError(t *testing.T) { } err := client.NetworkDisconnect(context.Background(), "network_id", "container_id", false) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -33,7 +34,7 @@ func TestNetworkDisconnect(t *testing.T) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } @@ -52,7 +53,7 @@ func TestNetworkDisconnect(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/network_inspect.go b/client/network_inspect.go index 025f6d8757dab..ecf20ceb6e46f 100644 --- a/client/network_inspect.go +++ b/client/network_inspect.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" + "io" "net/url" "github.com/docker/docker/api/types" @@ -34,12 +34,12 @@ func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, query.Set("scope", options.Scope) } resp, err = cli.get(ctx, "/networks/"+networkID, query, nil) + defer ensureReaderClosed(resp) if err != nil { return networkResource, nil, wrapResponseError(err, resp, "network", networkID) } - defer ensureReaderClosed(resp) - body, err := ioutil.ReadAll(resp.body) + body, err := io.ReadAll(resp.body) if err != nil { return networkResource, nil, err } diff --git a/client/network_inspect_test.go b/client/network_inspect_test.go index 699bccba675a0..84278fff6522e 100644 --- a/client/network_inspect_test.go +++ b/client/network_inspect_test.go @@ -4,72 +4,45 @@ import ( "bytes" "context" "encoding/json" - "fmt" - "io/ioutil" + "errors" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" - "github.com/pkg/errors" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) -func TestNetworkInspectError(t *testing.T) { - client := &Client{ - client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), - } - - _, err := client.NetworkInspect(context.Background(), "nothing", types.NetworkInspectOptions{}) - assert.Check(t, is.Error(err, "Error response from daemon: Server error")) -} - -func TestNetworkInspectNotFoundError(t *testing.T) { - client := &Client{ - client: newMockClient(errorMock(http.StatusNotFound, "missing")), - } - - _, err := client.NetworkInspect(context.Background(), "unknown", types.NetworkInspectOptions{}) - assert.Check(t, is.Error(err, "Error: No such network: unknown")) - assert.Check(t, IsErrNotFound(err)) -} - -func TestNetworkInspectWithEmptyID(t *testing.T) { - client := &Client{ - client: newMockClient(func(req *http.Request) (*http.Response, error) { - return nil, errors.New("should not make request") - }), - } - _, _, err := client.NetworkInspectWithRaw(context.Background(), "", types.NetworkInspectOptions{}) - if !IsErrNotFound(err) { - t.Fatalf("Expected NotFoundError, got %v", err) - } -} - func TestNetworkInspect(t *testing.T) { - expectedURL := "/networks/network_id" client := &Client{ client: newMockClient(func(req *http.Request) (*http.Response, error) { - if !strings.HasPrefix(req.URL.Path, expectedURL) { - return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) + if req.Method != http.MethodGet { + return nil, errors.New("expected GET method, got " + req.Method) } - if req.Method != "GET" { - return nil, fmt.Errorf("expected GET method, got %s", req.Method) + if req.URL.Path == "/networks/" { + return errorMock(http.StatusInternalServerError, "client should not make a request for empty IDs")(req) + } + if strings.HasPrefix(req.URL.Path, "/networks/unknown") { + return errorMock(http.StatusNotFound, "Error: No such network: unknown")(req) + } + if strings.HasPrefix(req.URL.Path, "/networks/test-500-response") { + return errorMock(http.StatusInternalServerError, "Server error")(req) + } + // other test-cases all use "network_id" + if !strings.HasPrefix(req.URL.Path, "/networks/network_id") { + return nil, errors.New("expected URL '/networks/network_id', got " + req.URL.Path) + } + if strings.Contains(req.URL.RawQuery, "scope=global") { + return errorMock(http.StatusNotFound, "Error: No such network: unknown")(req) } - var ( content []byte err error ) - if strings.Contains(req.URL.RawQuery, "scope=global") { - return &http.Response{ - StatusCode: http.StatusNotFound, - Body: ioutil.NopCloser(bytes.NewReader(content)), - }, nil - } - if strings.Contains(req.URL.RawQuery, "verbose=true") { s := map[string]network.ServiceInfo{ "web": {}, @@ -87,32 +60,45 @@ func TestNetworkInspect(t *testing.T) { return nil, err } return &http.Response{ + Header: http.Header{"Content-Type": []string{"application/json"}}, StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } - r, err := client.NetworkInspect(context.Background(), "network_id", types.NetworkInspectOptions{}) - if err != nil { - t.Fatal(err) - } - if r.Name != "mynetwork" { - t.Fatalf("expected `mynetwork`, got %s", r.Name) - } - - r, err = client.NetworkInspect(context.Background(), "network_id", types.NetworkInspectOptions{Verbose: true}) - if err != nil { - t.Fatal(err) - } - if r.Name != "mynetwork" { - t.Fatalf("expected `mynetwork`, got %s", r.Name) - } - _, ok := r.Services["web"] - if !ok { - t.Fatalf("expected service `web` missing in the verbose output") - } - - _, err = client.NetworkInspect(context.Background(), "network_id", types.NetworkInspectOptions{Scope: "global"}) - assert.Check(t, is.Error(err, "Error: No such network: network_id")) + t.Run("empty ID", func(t *testing.T) { + // verify that the client does not create a request if the network-ID/name is empty. + _, err := client.NetworkInspect(context.Background(), "", types.NetworkInspectOptions{}) + assert.Check(t, IsErrNotFound(err)) + }) + t.Run("no options", func(t *testing.T) { + r, err := client.NetworkInspect(context.Background(), "network_id", types.NetworkInspectOptions{}) + assert.NilError(t, err) + assert.Equal(t, r.Name, "mynetwork") + }) + t.Run("verbose", func(t *testing.T) { + r, err := client.NetworkInspect(context.Background(), "network_id", types.NetworkInspectOptions{Verbose: true}) + assert.NilError(t, err) + assert.Equal(t, r.Name, "mynetwork") + _, ok := r.Services["web"] + if !ok { + t.Fatalf("expected service `web` missing in the verbose output") + } + }) + t.Run("global scope", func(t *testing.T) { + _, err := client.NetworkInspect(context.Background(), "network_id", types.NetworkInspectOptions{Scope: "global"}) + assert.Check(t, is.Error(err, "Error: No such network: network_id")) + assert.Check(t, IsErrNotFound(err)) + }) + t.Run("unknown network", func(t *testing.T) { + _, err := client.NetworkInspect(context.Background(), "unknown", types.NetworkInspectOptions{}) + assert.Check(t, is.Error(err, "Error: No such network: unknown")) + assert.Check(t, IsErrNotFound(err)) + }) + t.Run("server error", func(t *testing.T) { + // Just testing that an internal server error is converted correctly by the client + _, err := client.NetworkInspect(context.Background(), "test-500-response", types.NetworkInspectOptions{}) + assert.Check(t, errdefs.IsSystem(err)) + }) } diff --git a/client/network_list.go b/client/network_list.go index f16b2f5624048..ed2acb55711dd 100644 --- a/client/network_list.go +++ b/client/network_list.go @@ -13,6 +13,7 @@ import ( func (cli *Client) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) { query := url.Values{} if options.Filters.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters) if err != nil { return nil, err @@ -22,10 +23,10 @@ func (cli *Client) NetworkList(ctx context.Context, options types.NetworkListOpt } var networkResources []types.NetworkResource resp, err := cli.get(ctx, "/networks", query, nil) + defer ensureReaderClosed(resp) if err != nil { return networkResources, err } err = json.NewDecoder(resp.body).Decode(&networkResources) - ensureReaderClosed(resp) return networkResources, err } diff --git a/client/network_list_test.go b/client/network_list_test.go index 5263808cfd202..66e15e1290e7c 100644 --- a/client/network_list_test.go +++ b/client/network_list_test.go @@ -5,13 +5,14 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/errdefs" ) func TestNetworkListError(t *testing.T) { @@ -22,8 +23,8 @@ func TestNetworkListError(t *testing.T) { _, err := client.NetworkList(context.Background(), types.NetworkListOptions{ Filters: filters.NewArgs(), }) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -73,7 +74,7 @@ func TestNetworkList(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "GET" { + if req.Method != http.MethodGet { return nil, fmt.Errorf("expected GET method, got %s", req.Method) } query := req.URL.Query() @@ -92,7 +93,7 @@ func TestNetworkList(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/network_prune.go b/client/network_prune.go index 6418b8b607048..cebb18821925e 100644 --- a/client/network_prune.go +++ b/client/network_prune.go @@ -23,10 +23,10 @@ func (cli *Client) NetworksPrune(ctx context.Context, pruneFilters filters.Args) } serverResp, err := cli.post(ctx, "/networks/prune", query, nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return report, err } - defer ensureReaderClosed(serverResp) if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { return report, fmt.Errorf("Error retrieving network prune report: %v", err) diff --git a/client/network_prune_test.go b/client/network_prune_test.go index 7a5d340e51953..d5019efc692ae 100644 --- a/client/network_prune_test.go +++ b/client/network_prune_test.go @@ -5,15 +5,16 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestNetworksPruneError(t *testing.T) { @@ -25,8 +26,8 @@ func TestNetworksPruneError(t *testing.T) { filters := filters.NewArgs() _, err := client.NetworksPrune(context.Background(), filters) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -100,7 +101,7 @@ func TestNetworksPrune(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), version: "1.25", diff --git a/client/network_remove.go b/client/network_remove.go index 12741437be35f..e71b16d86929a 100644 --- a/client/network_remove.go +++ b/client/network_remove.go @@ -5,6 +5,6 @@ import "context" // NetworkRemove removes an existent network from the docker host. func (cli *Client) NetworkRemove(ctx context.Context, networkID string) error { resp, err := cli.delete(ctx, "/networks/"+networkID, nil, nil) - ensureReaderClosed(resp) + defer ensureReaderClosed(resp) return wrapResponseError(err, resp, "network", networkID) } diff --git a/client/network_remove_test.go b/client/network_remove_test.go index ac40af74e6a11..e7dddff86ee1a 100644 --- a/client/network_remove_test.go +++ b/client/network_remove_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestNetworkRemoveError(t *testing.T) { @@ -16,8 +18,8 @@ func TestNetworkRemoveError(t *testing.T) { } err := client.NetworkRemove(context.Background(), "network_id") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -29,12 +31,12 @@ func TestNetworkRemove(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "DELETE" { + if req.Method != http.MethodDelete { return nil, fmt.Errorf("expected DELETE method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), }, nil }), } diff --git a/client/node_inspect.go b/client/node_inspect.go index 593b2e9f0b3f7..b58db528567bf 100644 --- a/client/node_inspect.go +++ b/client/node_inspect.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" + "io" "github.com/docker/docker/api/types/swarm" ) @@ -15,12 +15,12 @@ func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm return swarm.Node{}, nil, objectNotFoundError{object: "node", id: nodeID} } serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return swarm.Node{}, nil, wrapResponseError(err, serverResp, "node", nodeID) } - defer ensureReaderClosed(serverResp) - body, err := ioutil.ReadAll(serverResp.body) + body, err := io.ReadAll(serverResp.body) if err != nil { return swarm.Node{}, nil, err } diff --git a/client/node_inspect_test.go b/client/node_inspect_test.go index d0fdace7fe35e..ae97061a8b999 100644 --- a/client/node_inspect_test.go +++ b/client/node_inspect_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" ) @@ -20,8 +21,8 @@ func TestNodeInspectError(t *testing.T) { } _, _, err := client.NodeInspectWithRaw(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -63,7 +64,7 @@ func TestNodeInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/node_list.go b/client/node_list.go index 9883f6fc52d5f..c212906bc718e 100644 --- a/client/node_list.go +++ b/client/node_list.go @@ -25,12 +25,12 @@ func (cli *Client) NodeList(ctx context.Context, options types.NodeListOptions) } resp, err := cli.get(ctx, "/nodes", query, nil) + defer ensureReaderClosed(resp) if err != nil { return nil, err } var nodes []swarm.Node err = json.NewDecoder(resp.body).Decode(&nodes) - ensureReaderClosed(resp) return nodes, err } diff --git a/client/node_list_test.go b/client/node_list_test.go index 784a754a595d6..5851cd1a77789 100644 --- a/client/node_list_test.go +++ b/client/node_list_test.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" ) func TestNodeListError(t *testing.T) { @@ -21,8 +22,8 @@ func TestNodeListError(t *testing.T) { } _, err := client.NodeList(context.Background(), types.NodeListOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -78,7 +79,7 @@ func TestNodeList(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/node_remove.go b/client/node_remove.go index e7a75057155ba..03ab878097418 100644 --- a/client/node_remove.go +++ b/client/node_remove.go @@ -15,6 +15,6 @@ func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options types. } resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil) - ensureReaderClosed(resp) + defer ensureReaderClosed(resp) return wrapResponseError(err, resp, "node", nodeID) } diff --git a/client/node_remove_test.go b/client/node_remove_test.go index 85f828b849b0e..37b5964be18b5 100644 --- a/client/node_remove_test.go +++ b/client/node_remove_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestNodeRemoveError(t *testing.T) { @@ -18,8 +19,8 @@ func TestNodeRemoveError(t *testing.T) { } err := client.NodeRemove(context.Background(), "node_id", types.NodeRemoveOptions{Force: false}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -45,7 +46,7 @@ func TestNodeRemove(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "DELETE" { + if req.Method != http.MethodDelete { return nil, fmt.Errorf("expected DELETE method, got %s", req.Method) } force := req.URL.Query().Get("force") @@ -55,7 +56,7 @@ func TestNodeRemove(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), }, nil }), } diff --git a/client/node_update_test.go b/client/node_update_test.go index d89e1ed85892f..772c6fbc89246 100644 --- a/client/node_update_test.go +++ b/client/node_update_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" ) func TestNodeUpdateError(t *testing.T) { @@ -18,8 +19,8 @@ func TestNodeUpdateError(t *testing.T) { } err := client.NodeUpdate(context.Background(), "node_id", swarm.Version{}, swarm.NodeSpec{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -31,12 +32,12 @@ func TestNodeUpdate(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), }, nil }), } diff --git a/client/options.go b/client/options.go new file mode 100644 index 0000000000000..6f77f0955f698 --- /dev/null +++ b/client/options.go @@ -0,0 +1,172 @@ +package client + +import ( + "context" + "net" + "net/http" + "os" + "path/filepath" + "time" + + "github.com/docker/go-connections/sockets" + "github.com/docker/go-connections/tlsconfig" + "github.com/pkg/errors" +) + +// Opt is a configuration option to initialize a client +type Opt func(*Client) error + +// FromEnv configures the client with values from environment variables. +// +// Supported environment variables: +// DOCKER_HOST to set the url to the docker server. +// DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest. +// DOCKER_CERT_PATH to load the TLS certificates from. +// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default. +func FromEnv(c *Client) error { + if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" { + options := tlsconfig.Options{ + CAFile: filepath.Join(dockerCertPath, "ca.pem"), + CertFile: filepath.Join(dockerCertPath, "cert.pem"), + KeyFile: filepath.Join(dockerCertPath, "key.pem"), + InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "", + } + tlsc, err := tlsconfig.Client(options) + if err != nil { + return err + } + + c.client = &http.Client{ + Transport: &http.Transport{TLSClientConfig: tlsc}, + CheckRedirect: CheckRedirect, + } + } + + if host := os.Getenv("DOCKER_HOST"); host != "" { + if err := WithHost(host)(c); err != nil { + return err + } + } + + if version := os.Getenv("DOCKER_API_VERSION"); version != "" { + if err := WithVersion(version)(c); err != nil { + return err + } + } + return nil +} + +// WithDialer applies the dialer.DialContext to the client transport. This can be +// used to set the Timeout and KeepAlive settings of the client. +// Deprecated: use WithDialContext +func WithDialer(dialer *net.Dialer) Opt { + return WithDialContext(dialer.DialContext) +} + +// WithDialContext applies the dialer to the client transport. This can be +// used to set the Timeout and KeepAlive settings of the client. +func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) Opt { + return func(c *Client) error { + if transport, ok := c.client.Transport.(*http.Transport); ok { + transport.DialContext = dialContext + return nil + } + return errors.Errorf("cannot apply dialer to transport: %T", c.client.Transport) + } +} + +// WithHost overrides the client host with the specified one. +func WithHost(host string) Opt { + return func(c *Client) error { + hostURL, err := ParseHostURL(host) + if err != nil { + return err + } + c.host = host + c.proto = hostURL.Scheme + c.addr = hostURL.Host + c.basePath = hostURL.Path + if transport, ok := c.client.Transport.(*http.Transport); ok { + return sockets.ConfigureTransport(transport, c.proto, c.addr) + } + return errors.Errorf("cannot apply host to transport: %T", c.client.Transport) + } +} + +// WithHTTPClient overrides the client http client with the specified one +func WithHTTPClient(client *http.Client) Opt { + return func(c *Client) error { + if client != nil { + c.client = client + } + return nil + } +} + +// WithTimeout configures the time limit for requests made by the HTTP client +func WithTimeout(timeout time.Duration) Opt { + return func(c *Client) error { + c.client.Timeout = timeout + return nil + } +} + +// WithHTTPHeaders overrides the client default http headers +func WithHTTPHeaders(headers map[string]string) Opt { + return func(c *Client) error { + c.customHTTPHeaders = headers + return nil + } +} + +// WithScheme overrides the client scheme with the specified one +func WithScheme(scheme string) Opt { + return func(c *Client) error { + c.scheme = scheme + return nil + } +} + +// WithTLSClientConfig applies a tls config to the client transport. +func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt { + return func(c *Client) error { + opts := tlsconfig.Options{ + CAFile: cacertPath, + CertFile: certPath, + KeyFile: keyPath, + ExclusiveRootPools: true, + } + config, err := tlsconfig.Client(opts) + if err != nil { + return errors.Wrap(err, "failed to create tls config") + } + if transport, ok := c.client.Transport.(*http.Transport); ok { + transport.TLSClientConfig = config + return nil + } + return errors.Errorf("cannot apply tls config to transport: %T", c.client.Transport) + } +} + +// WithVersion overrides the client version with the specified one. If an empty +// version is specified, the value will be ignored to allow version negotiation. +func WithVersion(version string) Opt { + return func(c *Client) error { + if version != "" { + c.version = version + c.manualOverride = true + } + return nil + } +} + +// WithAPIVersionNegotiation enables automatic API version negotiation for the client. +// With this option enabled, the client automatically negotiates the API version +// to use when making requests. API version negotiation is performed on the first +// request; subsequent requests will not re-negotiate. +func WithAPIVersionNegotiation() Opt { + return func(c *Client) error { + c.negotiateVersion = true + return nil + } +} diff --git a/client/options_test.go b/client/options_test.go new file mode 100644 index 0000000000000..e853357123c4b --- /dev/null +++ b/client/options_test.go @@ -0,0 +1,16 @@ +package client + +import ( + "testing" + "time" + + "gotest.tools/v3/assert" +) + +func TestOptionWithTimeout(t *testing.T) { + timeout := 10 * time.Second + c, err := NewClientWithOpts(WithTimeout(timeout)) + assert.NilError(t, err) + assert.Check(t, c.client != nil) + assert.Equal(t, c.client.Timeout, timeout) +} diff --git a/client/ping.go b/client/ping.go index dec1423e38911..a9af001ef46b5 100644 --- a/client/ping.go +++ b/client/ping.go @@ -2,34 +2,65 @@ package client // import "github.com/docker/docker/client" import ( "context" + "net/http" "path" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) -// Ping pings the server and returns the value of the "Docker-Experimental", "Builder-Version", "OS-Type" & "API-Version" headers +// Ping pings the server and returns the value of the "Docker-Experimental", +// "Builder-Version", "OS-Type" & "API-Version" headers. It attempts to use +// a HEAD request on the endpoint, but falls back to GET if HEAD is not supported +// by the daemon. func (cli *Client) Ping(ctx context.Context) (types.Ping, error) { var ping types.Ping - req, err := cli.buildRequest("GET", path.Join(cli.basePath, "/_ping"), nil, nil) + + // Using cli.buildRequest() + cli.doRequest() instead of cli.sendRequest() + // because ping requests are used during API version negotiation, so we want + // to hit the non-versioned /_ping endpoint, not /v1.xx/_ping + req, err := cli.buildRequest(http.MethodHead, path.Join(cli.basePath, "/_ping"), nil, nil) if err != nil { return ping, err } serverResp, err := cli.doRequest(ctx, req) + if err == nil { + defer ensureReaderClosed(serverResp) + switch serverResp.statusCode { + case http.StatusOK, http.StatusInternalServerError: + // Server handled the request, so parse the response + return parsePingResponse(cli, serverResp) + } + } else if IsErrConnectionFailed(err) { + return ping, err + } + + req, err = cli.buildRequest(http.MethodGet, path.Join(cli.basePath, "/_ping"), nil, nil) if err != nil { return ping, err } + serverResp, err = cli.doRequest(ctx, req) defer ensureReaderClosed(serverResp) + if err != nil { + return ping, err + } + return parsePingResponse(cli, serverResp) +} - if serverResp.header != nil { - ping.APIVersion = serverResp.header.Get("API-Version") - - if serverResp.header.Get("Docker-Experimental") == "true" { - ping.Experimental = true - } - ping.OSType = serverResp.header.Get("OSType") - if bv := serverResp.header.Get("Builder-Version"); bv != "" { - ping.BuilderVersion = types.BuilderVersion(bv) - } +func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) { + var ping types.Ping + if resp.header == nil { + err := cli.checkResponseErr(resp) + return ping, errdefs.FromStatusCode(err, resp.statusCode) + } + ping.APIVersion = resp.header.Get("API-Version") + ping.OSType = resp.header.Get("OSType") + if resp.header.Get("Docker-Experimental") == "true" { + ping.Experimental = true + } + if bv := resp.header.Get("Builder-Version"); bv != "" { + ping.BuilderVersion = types.BuilderVersion(bv) } - return ping, cli.checkResponseErr(serverResp) + err := cli.checkResponseErr(resp) + return ping, errdefs.FromStatusCode(err, resp.statusCode) } diff --git a/client/ping_test.go b/client/ping_test.go index 10bbbe811de62..ae3a1f986cdee 100644 --- a/client/ping_test.go +++ b/client/ping_test.go @@ -3,13 +3,13 @@ package client // import "github.com/docker/docker/client" import ( "context" "errors" - "io/ioutil" + "io" "net/http" "strings" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) // TestPingFail tests that when a server sends a non-successful response that we @@ -26,19 +26,19 @@ func TestPingFail(t *testing.T) { resp.Header.Set("API-Version", "awesome") resp.Header.Set("Docker-Experimental", "true") } - resp.Body = ioutil.NopCloser(strings.NewReader("some error with the server")) + resp.Body = io.NopCloser(strings.NewReader("some error with the server")) return resp, nil }), } ping, err := client.Ping(context.Background()) - assert.Check(t, is.ErrorContains(err, "")) + assert.ErrorContains(t, err, "some error with the server") assert.Check(t, is.Equal(false, ping.Experimental)) assert.Check(t, is.Equal("", ping.APIVersion)) withHeader = true ping2, err := client.Ping(context.Background()) - assert.Check(t, is.ErrorContains(err, "")) + assert.ErrorContains(t, err, "some error with the server") assert.Check(t, is.Equal(true, ping2.Experimental)) assert.Check(t, is.Equal("awesome", ping2.APIVersion)) } @@ -52,13 +52,13 @@ func TestPingWithError(t *testing.T) { resp.Header = http.Header{} resp.Header.Set("API-Version", "awesome") resp.Header.Set("Docker-Experimental", "true") - resp.Body = ioutil.NopCloser(strings.NewReader("some error with the server")) + resp.Body = io.NopCloser(strings.NewReader("some error with the server")) return resp, errors.New("some error") }), } ping, err := client.Ping(context.Background()) - assert.Check(t, is.ErrorContains(err, "")) + assert.ErrorContains(t, err, "some error") assert.Check(t, is.Equal(false, ping.Experimental)) assert.Check(t, is.Equal("", ping.APIVersion)) } @@ -68,16 +68,62 @@ func TestPingWithError(t *testing.T) { func TestPingSuccess(t *testing.T) { client := &Client{ client: newMockClient(func(req *http.Request) (*http.Response, error) { - resp := &http.Response{StatusCode: http.StatusInternalServerError} + resp := &http.Response{StatusCode: http.StatusOK} resp.Header = http.Header{} resp.Header.Set("API-Version", "awesome") resp.Header.Set("Docker-Experimental", "true") - resp.Body = ioutil.NopCloser(strings.NewReader("some error with the server")) + resp.Body = io.NopCloser(strings.NewReader("OK")) return resp, nil }), } ping, err := client.Ping(context.Background()) - assert.Check(t, is.ErrorContains(err, "")) + assert.NilError(t, err) assert.Check(t, is.Equal(true, ping.Experimental)) assert.Check(t, is.Equal("awesome", ping.APIVersion)) } + +// TestPingHeadFallback tests that the client falls back to GET if HEAD fails. +func TestPingHeadFallback(t *testing.T) { + tests := []struct { + status int + expected string + }{ + { + status: http.StatusOK, + expected: http.MethodHead, + }, + { + status: http.StatusInternalServerError, + expected: http.MethodHead, + }, + { + status: http.StatusNotFound, + expected: "HEAD, GET", + }, + { + status: http.StatusMethodNotAllowed, + expected: "HEAD, GET", + }, + } + + for _, tc := range tests { + tc := tc + t.Run(http.StatusText(tc.status), func(t *testing.T) { + var reqs []string + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + reqs = append(reqs, req.Method) + resp := &http.Response{StatusCode: http.StatusOK} + if req.Method == http.MethodHead { + resp.StatusCode = tc.status + } + resp.Header = http.Header{} + resp.Header.Add("API-Version", strings.Join(reqs, ", ")) + return resp, nil + }), + } + ping, _ := client.Ping(context.Background()) + assert.Check(t, is.Equal(ping.APIVersion, tc.expected)) + }) + } +} diff --git a/client/plugin_create.go b/client/plugin_create.go index 4591db50fda59..b95dbaf68633b 100644 --- a/client/plugin_create.go +++ b/client/plugin_create.go @@ -18,9 +18,6 @@ func (cli *Client) PluginCreate(ctx context.Context, createContext io.Reader, cr query.Set("name", createOptions.RepoName) resp, err := cli.postRaw(ctx, "/plugins/create", query, createContext, headers) - if err != nil { - return err - } ensureReaderClosed(resp) return err } diff --git a/client/plugin_disable_test.go b/client/plugin_disable_test.go index ac2413d6c5358..f042adc3208d7 100644 --- a/client/plugin_disable_test.go +++ b/client/plugin_disable_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestPluginDisableError(t *testing.T) { @@ -18,8 +19,8 @@ func TestPluginDisableError(t *testing.T) { } err := client.PluginDisable(context.Background(), "plugin_name", types.PluginDisableOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -31,12 +32,12 @@ func TestPluginDisable(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/plugin_enable_test.go b/client/plugin_enable_test.go index 911ccaf1e9181..c7f7a1be7b5bf 100644 --- a/client/plugin_enable_test.go +++ b/client/plugin_enable_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestPluginEnableError(t *testing.T) { @@ -18,8 +19,8 @@ func TestPluginEnableError(t *testing.T) { } err := client.PluginEnable(context.Background(), "plugin_name", types.PluginEnableOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -31,12 +32,12 @@ func TestPluginEnable(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/plugin_inspect.go b/client/plugin_inspect.go index 0ab7beaee8fbb..4a90bec51a0cb 100644 --- a/client/plugin_inspect.go +++ b/client/plugin_inspect.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" + "io" "github.com/docker/docker/api/types" ) @@ -15,12 +15,12 @@ func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*type return nil, nil, objectNotFoundError{object: "plugin", id: name} } resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil) + defer ensureReaderClosed(resp) if err != nil { return nil, nil, wrapResponseError(err, resp, "plugin", name) } - defer ensureReaderClosed(resp) - body, err := ioutil.ReadAll(resp.body) + body, err := io.ReadAll(resp.body) if err != nil { return nil, nil, err } diff --git a/client/plugin_inspect_test.go b/client/plugin_inspect_test.go index 74ca0f0fc0cd2..898d02e5432fa 100644 --- a/client/plugin_inspect_test.go +++ b/client/plugin_inspect_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" ) @@ -20,8 +21,8 @@ func TestPluginInspectError(t *testing.T) { } _, _, err := client.PluginInspectWithRaw(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -52,7 +53,7 @@ func TestPluginInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/plugin_install.go b/client/plugin_install.go index 13baa40a9b063..012afe61cacf0 100644 --- a/client/plugin_install.go +++ b/client/plugin_install.go @@ -4,11 +4,11 @@ import ( "context" "encoding/json" "io" - "net/http" "net/url" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" ) @@ -78,7 +78,7 @@ func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileg func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values, options types.PluginInstallOptions) (types.PluginPrivileges, error) { resp, err := cli.tryPluginPrivileges(ctx, query, options.RegistryAuth) - if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil { + if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { // todo: do inspect before to check existing name before checking privileges newAuthHeader, privilegeErr := options.PrivilegeFunc() if privilegeErr != nil { diff --git a/client/plugin_list.go b/client/plugin_list.go index ade1051a97009..cf1935e2f5ee5 100644 --- a/client/plugin_list.go +++ b/client/plugin_list.go @@ -15,6 +15,7 @@ func (cli *Client) PluginList(ctx context.Context, filter filters.Args) (types.P query := url.Values{} if filter.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code filterJSON, err := filters.ToParamWithVersion(cli.version, filter) if err != nil { return plugins, err @@ -22,11 +23,11 @@ func (cli *Client) PluginList(ctx context.Context, filter filters.Args) (types.P query.Set("filters", filterJSON) } resp, err := cli.get(ctx, "/plugins", query, nil) + defer ensureReaderClosed(resp) if err != nil { return plugins, wrapResponseError(err, resp, "plugin", "") } err = json.NewDecoder(resp.body).Decode(&plugins) - ensureReaderClosed(resp) return plugins, err } diff --git a/client/plugin_list_test.go b/client/plugin_list_test.go index 7dc351dceb7a4..c2643c3663946 100644 --- a/client/plugin_list_test.go +++ b/client/plugin_list_test.go @@ -5,13 +5,14 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/errdefs" ) func TestPluginListError(t *testing.T) { @@ -20,8 +21,8 @@ func TestPluginListError(t *testing.T) { } _, err := client.PluginList(context.Background(), filters.NewArgs()) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -91,7 +92,7 @@ func TestPluginList(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/plugin_push_test.go b/client/plugin_push_test.go index 20b23a1173cba..4b5a0e2e30b88 100644 --- a/client/plugin_push_test.go +++ b/client/plugin_push_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestPluginPushError(t *testing.T) { @@ -16,8 +18,8 @@ func TestPluginPushError(t *testing.T) { } _, err := client.PluginPush(context.Background(), "plugin_name", "") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -29,7 +31,7 @@ func TestPluginPush(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } auth := req.Header.Get("X-Registry-Auth") @@ -38,7 +40,7 @@ func TestPluginPush(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/plugin_remove.go b/client/plugin_remove.go index 8563bab0dbcf2..51ca1040d6d29 100644 --- a/client/plugin_remove.go +++ b/client/plugin_remove.go @@ -15,6 +15,6 @@ func (cli *Client) PluginRemove(ctx context.Context, name string, options types. } resp, err := cli.delete(ctx, "/plugins/"+name, query, nil) - ensureReaderClosed(resp) + defer ensureReaderClosed(resp) return wrapResponseError(err, resp, "plugin", name) } diff --git a/client/plugin_remove_test.go b/client/plugin_remove_test.go index e6c76342ee548..210a92d73c696 100644 --- a/client/plugin_remove_test.go +++ b/client/plugin_remove_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" ) func TestPluginRemoveError(t *testing.T) { @@ -18,8 +19,8 @@ func TestPluginRemoveError(t *testing.T) { } err := client.PluginRemove(context.Background(), "plugin_name", types.PluginRemoveOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -31,12 +32,12 @@ func TestPluginRemove(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "DELETE" { + if req.Method != http.MethodDelete { return nil, fmt.Errorf("expected DELETE method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/plugin_set_test.go b/client/plugin_set_test.go index 2e97904b86feb..6b54023988956 100644 --- a/client/plugin_set_test.go +++ b/client/plugin_set_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestPluginSetError(t *testing.T) { @@ -16,8 +18,8 @@ func TestPluginSetError(t *testing.T) { } err := client.PluginSet(context.Background(), "plugin_name", []string{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -29,12 +31,12 @@ func TestPluginSet(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/request.go b/client/request.go index 9b8639a1e5faf..475ce4bd40454 100644 --- a/client/request.go +++ b/client/request.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net" "net/http" "net/url" @@ -15,8 +14,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" - "golang.org/x/net/context/ctxhttp" ) // serverResponse is a wrapper for http API responses. @@ -29,12 +28,12 @@ type serverResponse struct { // head sends an http request to the docker API using the method HEAD. func (cli *Client) head(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) { - return cli.sendRequest(ctx, "HEAD", path, query, nil, headers) + return cli.sendRequest(ctx, http.MethodHead, path, query, nil, headers) } // get sends an http request to the docker API using the method GET with a specific Go context. func (cli *Client) get(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) { - return cli.sendRequest(ctx, "GET", path, query, nil, headers) + return cli.sendRequest(ctx, http.MethodGet, path, query, nil, headers) } // post sends an http request to the docker API using the method POST with a specific Go context. @@ -43,30 +42,21 @@ func (cli *Client) post(ctx context.Context, path string, query url.Values, obj if err != nil { return serverResponse{}, err } - return cli.sendRequest(ctx, "POST", path, query, body, headers) + return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers) } func (cli *Client) postRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) { - return cli.sendRequest(ctx, "POST", path, query, body, headers) -} - -// put sends an http request to the docker API using the method PUT. -func (cli *Client) put(ctx context.Context, path string, query url.Values, obj interface{}, headers map[string][]string) (serverResponse, error) { - body, headers, err := encodeBody(obj, headers) - if err != nil { - return serverResponse{}, err - } - return cli.sendRequest(ctx, "PUT", path, query, body, headers) + return cli.sendRequest(ctx, http.MethodPost, path, query, body, headers) } // putRaw sends an http request to the docker API using the method PUT. func (cli *Client) putRaw(ctx context.Context, path string, query url.Values, body io.Reader, headers map[string][]string) (serverResponse, error) { - return cli.sendRequest(ctx, "PUT", path, query, body, headers) + return cli.sendRequest(ctx, http.MethodPut, path, query, body, headers) } // delete sends an http request to the docker API using the method DELETE. func (cli *Client) delete(ctx context.Context, path string, query url.Values, headers map[string][]string) (serverResponse, error) { - return cli.sendRequest(ctx, "DELETE", path, query, nil, headers) + return cli.sendRequest(ctx, http.MethodDelete, path, query, nil, headers) } type headers map[string][]string @@ -88,7 +78,7 @@ func encodeBody(obj interface{}, headers headers) (io.Reader, headers, error) { } func (cli *Client) buildRequest(method, path string, body io.Reader, headers headers) (*http.Request, error) { - expectedPayload := (method == "POST" || method == "PUT") + expectedPayload := (method == http.MethodPost || method == http.MethodPut) if expectedPayload && body == nil { body = bytes.NewReader([]byte{}) } @@ -115,34 +105,40 @@ func (cli *Client) buildRequest(method, path string, body io.Reader, headers hea } func (cli *Client) sendRequest(ctx context.Context, method, path string, query url.Values, body io.Reader, headers headers) (serverResponse, error) { - req, err := cli.buildRequest(method, cli.getAPIPath(path, query), body, headers) + req, err := cli.buildRequest(method, cli.getAPIPath(ctx, path, query), body, headers) if err != nil { return serverResponse{}, err } + resp, err := cli.doRequest(ctx, req) - if err != nil { - return resp, err + switch { + case errors.Is(err, context.Canceled): + return serverResponse{}, errdefs.Cancelled(err) + case errors.Is(err, context.DeadlineExceeded): + return serverResponse{}, errdefs.Deadline(err) + case err == nil: + err = cli.checkResponseErr(resp) } - return resp, cli.checkResponseErr(resp) + return resp, errdefs.FromStatusCode(err, resp.statusCode) } func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResponse, error) { serverResp := serverResponse{statusCode: -1, reqURL: req.URL} - resp, err := ctxhttp.Do(ctx, cli.client, req) + req = req.WithContext(ctx) + resp, err := cli.client.Do(req) if err != nil { if cli.scheme != "https" && strings.Contains(err.Error(), "malformed HTTP response") { return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err) } if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") { - return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err) + return serverResp, errors.Wrap(err, "The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings") } // Don't decorate context sentinel errors; users may be comparing to // them directly. - switch err { - case context.Canceled, context.DeadlineExceeded: + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return serverResp, err } @@ -176,7 +172,13 @@ func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResp // this is localised - for example in French the error would be // `open //./pipe/docker_engine: Le fichier spécifié est introuvable.` if strings.Contains(err.Error(), `open //./pipe/docker_engine`) { - err = errors.New(err.Error() + " In the default daemon configuration on Windows, the docker client must be run elevated to connect. This error may also indicate that the docker daemon is not running.") + // Checks if client is running with elevated privileges + if f, elevatedErr := os.Open("\\\\.\\PHYSICALDRIVE0"); elevatedErr == nil { + err = errors.Wrap(err, "In the default daemon configuration on Windows, the docker client must be run with elevated privileges to connect.") + } else { + f.Close() + err = errors.Wrap(err, "This error may indicate that the docker daemon is not running.") + } } return serverResp, errors.Wrap(err, "error during connect") @@ -195,17 +197,21 @@ func (cli *Client) checkResponseErr(serverResp serverResponse) error { return nil } - bodyMax := 1 * 1024 * 1024 // 1 MiB - bodyR := &io.LimitedReader{ - R: serverResp.body, - N: int64(bodyMax), - } - body, err := ioutil.ReadAll(bodyR) - if err != nil { - return err - } - if bodyR.N == 0 { - return fmt.Errorf("request returned %s with a message (> %d bytes) for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), bodyMax, serverResp.reqURL) + var body []byte + var err error + if serverResp.body != nil { + bodyMax := 1 * 1024 * 1024 // 1 MiB + bodyR := &io.LimitedReader{ + R: serverResp.body, + N: int64(bodyMax), + } + body, err = io.ReadAll(bodyR) + if err != nil { + return err + } + if bodyR.N == 0 { + return fmt.Errorf("request returned %s with a message (> %d bytes) for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), bodyMax, serverResp.reqURL) + } } if len(body) == 0 { return fmt.Errorf("request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), serverResp.reqURL) @@ -220,14 +226,14 @@ func (cli *Client) checkResponseErr(serverResp serverResponse) error { if (cli.version == "" || versions.GreaterThan(cli.version, "1.23")) && ct == "application/json" { var errorResponse types.ErrorResponse if err := json.Unmarshal(body, &errorResponse); err != nil { - return fmt.Errorf("Error reading JSON: %v", err) + return errors.Wrap(err, "Error reading JSON") } - errorMessage = errorResponse.Message + errorMessage = strings.TrimSpace(errorResponse.Message) } else { - errorMessage = string(body) + errorMessage = strings.TrimSpace(string(body)) } - return fmt.Errorf("Error response from daemon: %s", strings.TrimSpace(errorMessage)) + return errors.Wrap(errors.New(errorMessage), "Error response from daemon") } func (cli *Client) addHeaders(req *http.Request, headers headers) *http.Request { @@ -240,10 +246,8 @@ func (cli *Client) addHeaders(req *http.Request, headers headers) *http.Request req.Header.Set(k, v) } - if headers != nil { - for k, v := range headers { - req.Header[k] = v - } + for k, v := range headers { + req.Header[k] = v } return req } @@ -261,7 +265,7 @@ func encodeData(data interface{}) (*bytes.Buffer, error) { func ensureReaderClosed(response serverResponse) { if response.body != nil { // Drain up to 512 bytes and close the body to let the Transport reuse the connection - io.CopyN(ioutil.Discard, response.body, 512) + io.CopyN(io.Discard, response.body, 512) response.body.Close() } } diff --git a/client/request_test.go b/client/request_test.go index 2847a9a4a3f8d..6e5a6e81f21c1 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -3,16 +3,19 @@ package client // import "github.com/docker/docker/client" import ( "bytes" "context" + "errors" "fmt" - "io/ioutil" + "io" "math/rand" "net/http" "strings" "testing" + "time" "github.com/docker/docker/api/types" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) // TestSetHostHeader should set fake host for local communications, set real host @@ -63,7 +66,7 @@ func TestSetHostHeader(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), @@ -72,7 +75,7 @@ func TestSetHostHeader(t *testing.T) { basePath: hostURL.Path, } - _, err = client.sendRequest(context.Background(), "GET", testURL, nil, nil, nil) + _, err = client.sendRequest(context.Background(), http.MethodGet, testURL, nil, nil, nil) assert.NilError(t, err) } } @@ -85,8 +88,8 @@ func TestPlainTextError(t *testing.T) { client: newMockClient(plainTextErrorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ContainerList(context.Background(), types.ContainerListOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -96,7 +99,7 @@ func TestInfiniteError(t *testing.T) { client: newMockClient(func(req *http.Request) (*http.Response, error) { resp := &http.Response{StatusCode: http.StatusInternalServerError} resp.Header = http.Header{} - resp.Body = ioutil.NopCloser(infinitR) + resp.Body = io.NopCloser(infinitR) return resp, nil }), } @@ -104,3 +107,43 @@ func TestInfiniteError(t *testing.T) { _, err := client.Ping(context.Background()) assert.Check(t, is.ErrorContains(err, "request returned Internal Server Error")) } + +func TestCanceledContext(t *testing.T) { + testURL := "/test" + + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + assert.Equal(t, req.Context().Err(), context.Canceled) + + return &http.Response{}, context.Canceled + }), + } + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + _, err := client.sendRequest(ctx, http.MethodGet, testURL, nil, nil, nil) + assert.Equal(t, true, errdefs.IsCancelled(err)) + assert.Equal(t, true, errors.Is(err, context.Canceled)) +} + +func TestDeadlineExceededContext(t *testing.T) { + testURL := "/test" + + client := &Client{ + client: newMockClient(func(req *http.Request) (*http.Response, error) { + assert.Equal(t, req.Context().Err(), context.DeadlineExceeded) + + return &http.Response{}, context.DeadlineExceeded + }), + } + + ctx, cancel := context.WithDeadline(context.Background(), time.Now()) + defer cancel() + + <-ctx.Done() + + _, err := client.sendRequest(ctx, http.MethodGet, testURL, nil, nil, nil) + assert.Equal(t, true, errdefs.IsDeadline(err)) + assert.Equal(t, true, errors.Is(err, context.DeadlineExceeded)) +} diff --git a/client/secret_create.go b/client/secret_create.go index 09fae82f2a1bb..c65d38a191f45 100644 --- a/client/secret_create.go +++ b/client/secret_create.go @@ -8,18 +8,18 @@ import ( "github.com/docker/docker/api/types/swarm" ) -// SecretCreate creates a new Secret. +// SecretCreate creates a new secret. func (cli *Client) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) { var response types.SecretCreateResponse if err := cli.NewVersionError("1.25", "secret create"); err != nil { return response, err } resp, err := cli.post(ctx, "/secrets/create", nil, secret, nil) + defer ensureReaderClosed(resp) if err != nil { return response, err } err = json.NewDecoder(resp.body).Decode(&response) - ensureReaderClosed(resp) return response, err } diff --git a/client/secret_create_test.go b/client/secret_create_test.go index 419bdbcbc6213..1b30825e203d1 100644 --- a/client/secret_create_test.go +++ b/client/secret_create_test.go @@ -5,15 +5,16 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestSecretCreateUnsupported(t *testing.T) { @@ -31,8 +32,8 @@ func TestSecretCreateError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.SecretCreate(context.Background(), swarm.SecretSpec{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -44,7 +45,7 @@ func TestSecretCreate(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } b, err := json.Marshal(types.SecretCreateResponse{ @@ -55,7 +56,7 @@ func TestSecretCreate(t *testing.T) { } return &http.Response{ StatusCode: http.StatusCreated, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/secret_inspect.go b/client/secret_inspect.go index e8322f4589108..c07c9550d448d 100644 --- a/client/secret_inspect.go +++ b/client/secret_inspect.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" + "io" "github.com/docker/docker/api/types/swarm" ) @@ -18,12 +18,12 @@ func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.S return swarm.Secret{}, nil, objectNotFoundError{object: "secret", id: id} } resp, err := cli.get(ctx, "/secrets/"+id, nil, nil) + defer ensureReaderClosed(resp) if err != nil { return swarm.Secret{}, nil, wrapResponseError(err, resp, "secret", id) } - defer ensureReaderClosed(resp) - body, err := ioutil.ReadAll(resp.body) + body, err := io.ReadAll(resp.body) if err != nil { return swarm.Secret{}, nil, err } diff --git a/client/secret_inspect_test.go b/client/secret_inspect_test.go index 6c84799b1767a..094a609206aeb 100644 --- a/client/secret_inspect_test.go +++ b/client/secret_inspect_test.go @@ -5,15 +5,16 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestSecretInspectUnsupported(t *testing.T) { @@ -32,8 +33,8 @@ func TestSecretInspectError(t *testing.T) { } _, _, err := client.SecretInspectWithRaw(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -77,7 +78,7 @@ func TestSecretInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/secret_list.go b/client/secret_list.go index f6bf7ba470de1..a0289c9f440f2 100644 --- a/client/secret_list.go +++ b/client/secret_list.go @@ -27,12 +27,12 @@ func (cli *Client) SecretList(ctx context.Context, options types.SecretListOptio } resp, err := cli.get(ctx, "/secrets", query, nil) + defer ensureReaderClosed(resp) if err != nil { return nil, err } var secrets []swarm.Secret err = json.NewDecoder(resp.body).Decode(&secrets) - ensureReaderClosed(resp) return secrets, err } diff --git a/client/secret_list_test.go b/client/secret_list_test.go index 72323b055faa0..df330aa7253c9 100644 --- a/client/secret_list_test.go +++ b/client/secret_list_test.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" @@ -13,8 +13,9 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestSecretListUnsupported(t *testing.T) { @@ -33,8 +34,8 @@ func TestSecretListError(t *testing.T) { } _, err := client.SecretList(context.Background(), types.SecretListOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -91,7 +92,7 @@ func TestSecretList(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/secret_remove.go b/client/secret_remove.go index e9d5218293205..f6c69e57f8504 100644 --- a/client/secret_remove.go +++ b/client/secret_remove.go @@ -2,12 +2,12 @@ package client // import "github.com/docker/docker/client" import "context" -// SecretRemove removes a Secret. +// SecretRemove removes a secret. func (cli *Client) SecretRemove(ctx context.Context, id string) error { if err := cli.NewVersionError("1.25", "secret remove"); err != nil { return err } resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil) - ensureReaderClosed(resp) + defer ensureReaderClosed(resp) return wrapResponseError(err, resp, "secret", id) } diff --git a/client/secret_remove_test.go b/client/secret_remove_test.go index bdfccf6be86d6..64057e6d74f75 100644 --- a/client/secret_remove_test.go +++ b/client/secret_remove_test.go @@ -4,13 +4,14 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestSecretRemoveUnsupported(t *testing.T) { @@ -29,8 +30,8 @@ func TestSecretRemoveError(t *testing.T) { } err := client.SecretRemove(context.Background(), "secret_id") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -43,12 +44,12 @@ func TestSecretRemove(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "DELETE" { + if req.Method != http.MethodDelete { return nil, fmt.Errorf("expected DELETE method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), }, nil }), } diff --git a/client/secret_update.go b/client/secret_update.go index 164256bbc15b5..d082dcef75ccc 100644 --- a/client/secret_update.go +++ b/client/secret_update.go @@ -8,7 +8,7 @@ import ( "github.com/docker/docker/api/types/swarm" ) -// SecretUpdate attempts to update a Secret +// SecretUpdate attempts to update a secret. func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error { if err := cli.NewVersionError("1.25", "secret update"); err != nil { return err diff --git a/client/secret_update_test.go b/client/secret_update_test.go index c7670b440c37b..75ccdc979000e 100644 --- a/client/secret_update_test.go +++ b/client/secret_update_test.go @@ -4,14 +4,15 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestSecretUpdateUnsupported(t *testing.T) { @@ -30,8 +31,8 @@ func TestSecretUpdateError(t *testing.T) { } err := client.SecretUpdate(context.Background(), "secret_id", swarm.Version{}, swarm.SecretSpec{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -44,12 +45,12 @@ func TestSecretUpdate(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), }, nil }), } diff --git a/client/service_create.go b/client/service_create.go index 8fadda4a9064d..a07315f71fe28 100644 --- a/client/service_create.go +++ b/client/service_create.go @@ -9,14 +9,13 @@ import ( "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" ) -// ServiceCreate creates a new Service. +// ServiceCreate creates a new service. func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error) { - var distErr error - + var response types.ServiceCreateResponse headers := map[string][]string{ "version": {cli.version}, } @@ -31,61 +30,74 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, } if err := validateServiceSpec(service); err != nil { - return types.ServiceCreateResponse{}, err + return response, err } // ensure that the image is tagged - var imgPlatforms []swarm.Platform - if service.TaskTemplate.ContainerSpec != nil { + var resolveWarning string + switch { + case service.TaskTemplate.ContainerSpec != nil: if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" { service.TaskTemplate.ContainerSpec.Image = taggedImg } if options.QueryRegistry { - var img string - img, imgPlatforms, distErr = imageDigestAndPlatforms(ctx, cli, service.TaskTemplate.ContainerSpec.Image, options.EncodedRegistryAuth) - if img != "" { - service.TaskTemplate.ContainerSpec.Image = img - } + resolveWarning = resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth) } - } - - // ensure that the image is tagged - if service.TaskTemplate.PluginSpec != nil { + case service.TaskTemplate.PluginSpec != nil: if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" { service.TaskTemplate.PluginSpec.Remote = taggedImg } if options.QueryRegistry { - var img string - img, imgPlatforms, distErr = imageDigestAndPlatforms(ctx, cli, service.TaskTemplate.PluginSpec.Remote, options.EncodedRegistryAuth) - if img != "" { - service.TaskTemplate.PluginSpec.Remote = img - } + resolveWarning = resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth) } } - if service.TaskTemplate.Placement == nil && len(imgPlatforms) > 0 { - service.TaskTemplate.Placement = &swarm.Placement{} - } - if len(imgPlatforms) > 0 { - service.TaskTemplate.Placement.Platforms = imgPlatforms - } - - var response types.ServiceCreateResponse resp, err := cli.post(ctx, "/services/create", nil, service, headers) + defer ensureReaderClosed(resp) if err != nil { return response, err } err = json.NewDecoder(resp.body).Decode(&response) - - if distErr != nil { - response.Warnings = append(response.Warnings, digestWarning(service.TaskTemplate.ContainerSpec.Image)) + if resolveWarning != "" { + response.Warnings = append(response.Warnings, resolveWarning) } - ensureReaderClosed(resp) return response, err } +func resolveContainerSpecImage(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string { + var warning string + if img, imgPlatforms, err := imageDigestAndPlatforms(ctx, cli, taskSpec.ContainerSpec.Image, encodedAuth); err != nil { + warning = digestWarning(taskSpec.ContainerSpec.Image) + } else { + taskSpec.ContainerSpec.Image = img + if len(imgPlatforms) > 0 { + if taskSpec.Placement == nil { + taskSpec.Placement = &swarm.Placement{} + } + taskSpec.Placement.Platforms = imgPlatforms + } + } + return warning +} + +func resolvePluginSpecRemote(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string { + var warning string + if img, imgPlatforms, err := imageDigestAndPlatforms(ctx, cli, taskSpec.PluginSpec.Remote, encodedAuth); err != nil { + warning = digestWarning(taskSpec.PluginSpec.Remote) + } else { + taskSpec.PluginSpec.Remote = img + if len(imgPlatforms) > 0 { + if taskSpec.Placement == nil { + taskSpec.Placement = &swarm.Placement{} + } + taskSpec.Placement.Platforms = imgPlatforms + } + } + return warning +} + func imageDigestAndPlatforms(ctx context.Context, cli DistributionAPIClient, image, encodedAuth string) (string, []swarm.Platform, error) { distributionInspect, err := cli.DistributionInspect(ctx, image, encodedAuth) var platforms []swarm.Platform @@ -119,7 +131,7 @@ func imageDigestAndPlatforms(ctx context.Context, cli DistributionAPIClient, ima // imageWithDigestString takes an image string and a digest, and updates // the image string if it didn't originally contain a digest. It returns -// an empty string if there are no updates. +// image unmodified in other situations. func imageWithDigestString(image string, dgst digest.Digest) string { namedRef, err := reference.ParseNormalizedNamed(image) if err == nil { @@ -131,7 +143,7 @@ func imageWithDigestString(image string, dgst digest.Digest) string { } } } - return "" + return image } // imageWithTagString takes an image string, and returns a tagged image diff --git a/client/service_create_test.go b/client/service_create_test.go index 9f51c18223b8a..af6dd65e7f01c 100644 --- a/client/service_create_test.go +++ b/client/service_create_test.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" @@ -13,10 +13,11 @@ import ( "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/swarm" - "github.com/opencontainers/go-digest" - "github.com/opencontainers/image-spec/specs-go/v1" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + digest "github.com/opencontainers/go-digest" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestServiceCreateError(t *testing.T) { @@ -24,8 +25,8 @@ func TestServiceCreateError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ServiceCreate(context.Background(), swarm.ServiceSpec{}, types.ServiceCreateOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -36,7 +37,7 @@ func TestServiceCreate(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } b, err := json.Marshal(types.ServiceCreateResponse{ @@ -47,7 +48,7 @@ func TestServiceCreate(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } @@ -86,7 +87,7 @@ func TestServiceCreateCompatiblePlatforms(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil } else if strings.HasPrefix(req.URL.Path, "/v1.30/distribution/") { b, err := json.Marshal(registrytypes.DistributionInspect{ @@ -105,7 +106,7 @@ func TestServiceCreateCompatiblePlatforms(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil } else { return nil, fmt.Errorf("unexpected URL '%s'", req.URL.Path) @@ -162,7 +163,7 @@ func TestServiceCreateDigestPinning(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil } else if strings.HasPrefix(req.URL.Path, "/v1.30/distribution/cannotresolve") { // unresolvable image @@ -179,7 +180,7 @@ func TestServiceCreateDigestPinning(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil } return nil, fmt.Errorf("unexpected URL '%s'", req.URL.Path) diff --git a/client/service_inspect.go b/client/service_inspect.go index de6aa22de76d8..c5368bab1e3c8 100644 --- a/client/service_inspect.go +++ b/client/service_inspect.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/url" "github.com/docker/docker/api/types" @@ -20,12 +20,12 @@ func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string, query := url.Values{} query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults)) serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil) + defer ensureReaderClosed(serverResp) if err != nil { return swarm.Service{}, nil, wrapResponseError(err, serverResp, "service", serviceID) } - defer ensureReaderClosed(serverResp) - body, err := ioutil.ReadAll(serverResp.body) + body, err := io.ReadAll(serverResp.body) if err != nil { return swarm.Service{}, nil, err } diff --git a/client/service_inspect_test.go b/client/service_inspect_test.go index b69332ccc6a23..96b337c71715e 100644 --- a/client/service_inspect_test.go +++ b/client/service_inspect_test.go @@ -5,13 +5,14 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" ) @@ -21,8 +22,8 @@ func TestServiceInspectError(t *testing.T) { } _, _, err := client.ServiceInspectWithRaw(context.Background(), "nothing", types.ServiceInspectOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -64,7 +65,7 @@ func TestServiceInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/service_list.go b/client/service_list.go index 7d53e2b9b9e06..f97ec75a5cb76 100644 --- a/client/service_list.go +++ b/client/service_list.go @@ -23,13 +23,17 @@ func (cli *Client) ServiceList(ctx context.Context, options types.ServiceListOpt query.Set("filters", filterJSON) } + if options.Status { + query.Set("status", "true") + } + resp, err := cli.get(ctx, "/services", query, nil) + defer ensureReaderClosed(resp) if err != nil { return nil, err } var services []swarm.Service err = json.NewDecoder(resp.body).Decode(&services) - ensureReaderClosed(resp) return services, err } diff --git a/client/service_list_test.go b/client/service_list_test.go index 9903f9e71cfb6..4021f810bb8a1 100644 --- a/client/service_list_test.go +++ b/client/service_list_test.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" ) func TestServiceListError(t *testing.T) { @@ -21,8 +22,8 @@ func TestServiceListError(t *testing.T) { } _, err := client.ServiceList(context.Background(), types.ServiceListOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -78,7 +79,7 @@ func TestServiceList(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/service_logs_test.go b/client/service_logs_test.go index 28f3ab5c6b669..ee2592c7bc480 100644 --- a/client/service_logs_test.go +++ b/client/service_logs_test.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "log" "net/http" "os" @@ -14,8 +13,9 @@ import ( "time" "github.com/docker/docker/api/types" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestServiceLogsError(t *testing.T) { @@ -23,7 +23,9 @@ func TestServiceLogsError(t *testing.T) { client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), } _, err := client.ServiceLogs(context.Background(), "service_id", types.ContainerLogsOptions{}) - assert.Check(t, is.Error(err, "Error response from daemon: Server error")) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) + } _, err = client.ServiceLogs(context.Background(), "service_id", types.ContainerLogsOptions{ Since: "2006-01-02TZ", }) @@ -101,7 +103,7 @@ func TestServiceLogs(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("response"))), + Body: io.NopCloser(bytes.NewReader([]byte("response"))), }, nil }), } @@ -112,7 +114,7 @@ func TestServiceLogs(t *testing.T) { } assert.NilError(t, err) defer body.Close() - content, err := ioutil.ReadAll(body) + content, err := io.ReadAll(body) assert.NilError(t, err) assert.Check(t, is.Contains(string(content), "response")) } @@ -122,7 +124,7 @@ func ExampleClient_ServiceLogs_withTimeout() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - client, _ := NewEnvClient() + client, _ := NewClientWithOpts(FromEnv) reader, err := client.ServiceLogs(ctx, "service_id", types.ContainerLogsOptions{}) if err != nil { log.Fatal(err) diff --git a/client/service_remove.go b/client/service_remove.go index fe3421bec82f0..953a2adf5aec4 100644 --- a/client/service_remove.go +++ b/client/service_remove.go @@ -5,6 +5,6 @@ import "context" // ServiceRemove kills and removes a service. func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error { resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil) - ensureReaderClosed(resp) + defer ensureReaderClosed(resp) return wrapResponseError(err, resp, "service", serviceID) } diff --git a/client/service_remove_test.go b/client/service_remove_test.go index d2379a1366392..a6d6f37cb4727 100644 --- a/client/service_remove_test.go +++ b/client/service_remove_test.go @@ -4,13 +4,14 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestServiceRemoveError(t *testing.T) { @@ -19,7 +20,9 @@ func TestServiceRemoveError(t *testing.T) { } err := client.ServiceRemove(context.Background(), "service_id") - assert.Check(t, is.Error(err, "Error response from daemon: Server error")) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) + } } func TestServiceRemoveNotFoundError(t *testing.T) { @@ -40,12 +43,12 @@ func TestServiceRemove(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "DELETE" { + if req.Method != http.MethodDelete { return nil, fmt.Errorf("expected DELETE method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), }, nil }), } diff --git a/client/service_update.go b/client/service_update.go index 5a7a61b01ffd1..c63895f74f252 100644 --- a/client/service_update.go +++ b/client/service_update.go @@ -10,11 +10,13 @@ import ( "github.com/docker/docker/api/types/swarm" ) -// ServiceUpdate updates a Service. +// ServiceUpdate updates a Service. The version number is required to avoid conflicting writes. +// It should be the value as set *before* the update. You can find this value in the Meta field +// of swarm.Service, which can be found using ServiceInspectWithRaw. func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options types.ServiceUpdateOptions) (types.ServiceUpdateResponse, error) { var ( - query = url.Values{} - distErr error + query = url.Values{} + response = types.ServiceUpdateResponse{} ) headers := map[string][]string{ @@ -36,57 +38,38 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version query.Set("version", strconv.FormatUint(version.Index, 10)) if err := validateServiceSpec(service); err != nil { - return types.ServiceUpdateResponse{}, err + return response, err } - var imgPlatforms []swarm.Platform // ensure that the image is tagged - if service.TaskTemplate.ContainerSpec != nil { + var resolveWarning string + switch { + case service.TaskTemplate.ContainerSpec != nil: if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" { service.TaskTemplate.ContainerSpec.Image = taggedImg } if options.QueryRegistry { - var img string - img, imgPlatforms, distErr = imageDigestAndPlatforms(ctx, cli, service.TaskTemplate.ContainerSpec.Image, options.EncodedRegistryAuth) - if img != "" { - service.TaskTemplate.ContainerSpec.Image = img - } + resolveWarning = resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth) } - } - - // ensure that the image is tagged - if service.TaskTemplate.PluginSpec != nil { + case service.TaskTemplate.PluginSpec != nil: if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" { service.TaskTemplate.PluginSpec.Remote = taggedImg } if options.QueryRegistry { - var img string - img, imgPlatforms, distErr = imageDigestAndPlatforms(ctx, cli, service.TaskTemplate.PluginSpec.Remote, options.EncodedRegistryAuth) - if img != "" { - service.TaskTemplate.PluginSpec.Remote = img - } + resolveWarning = resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth) } } - if service.TaskTemplate.Placement == nil && len(imgPlatforms) > 0 { - service.TaskTemplate.Placement = &swarm.Placement{} - } - if len(imgPlatforms) > 0 { - service.TaskTemplate.Placement.Platforms = imgPlatforms - } - - var response types.ServiceUpdateResponse resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers) + defer ensureReaderClosed(resp) if err != nil { return response, err } err = json.NewDecoder(resp.body).Decode(&response) - - if distErr != nil { - response.Warnings = append(response.Warnings, digestWarning(service.TaskTemplate.ContainerSpec.Image)) + if resolveWarning != "" { + response.Warnings = append(response.Warnings, resolveWarning) } - ensureReaderClosed(resp) return response, err } diff --git a/client/service_update_test.go b/client/service_update_test.go index 9a0a9ce0ddb5d..b1801bf0b60fa 100644 --- a/client/service_update_test.go +++ b/client/service_update_test.go @@ -4,13 +4,14 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" ) func TestServiceUpdateError(t *testing.T) { @@ -19,8 +20,8 @@ func TestServiceUpdateError(t *testing.T) { } _, err := client.ServiceUpdate(context.Background(), "service_id", swarm.Version{}, swarm.ServiceSpec{}, types.ServiceUpdateOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -54,7 +55,7 @@ func TestServiceUpdate(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } version := req.URL.Query().Get("version") @@ -63,7 +64,7 @@ func TestServiceUpdate(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + Body: io.NopCloser(bytes.NewReader([]byte("{}"))), }, nil }), } diff --git a/client/session.go b/client/session.go deleted file mode 100644 index df199f3d03ab3..0000000000000 --- a/client/session.go +++ /dev/null @@ -1,18 +0,0 @@ -package client // import "github.com/docker/docker/client" - -import ( - "context" - "net" - "net/http" -) - -// DialSession returns a connection that can be used communication with daemon -func (cli *Client) DialSession(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) { - req, err := http.NewRequest("POST", "/session", nil) - if err != nil { - return nil, err - } - req = cli.addHeaders(req, meta) - - return cli.setupHijackConn(ctx, req, proto) -} diff --git a/client/swarm_get_unlock_key.go b/client/swarm_get_unlock_key.go index 0c50c01a8c094..19f59dd582a90 100644 --- a/client/swarm_get_unlock_key.go +++ b/client/swarm_get_unlock_key.go @@ -10,12 +10,12 @@ import ( // SwarmGetUnlockKey retrieves the swarm's unlock key. func (cli *Client) SwarmGetUnlockKey(ctx context.Context) (types.SwarmUnlockKeyResponse, error) { serverResp, err := cli.get(ctx, "/swarm/unlockkey", nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return types.SwarmUnlockKeyResponse{}, err } var response types.SwarmUnlockKeyResponse err = json.NewDecoder(serverResp.body).Decode(&response) - ensureReaderClosed(serverResp) return response, err } diff --git a/client/swarm_get_unlock_key_test.go b/client/swarm_get_unlock_key_test.go index a1e460c1dc51d..3bfbcde529fdf 100644 --- a/client/swarm_get_unlock_key_test.go +++ b/client/swarm_get_unlock_key_test.go @@ -5,14 +5,15 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/errdefs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestSwarmGetUnlockKeyError(t *testing.T) { @@ -21,7 +22,9 @@ func TestSwarmGetUnlockKeyError(t *testing.T) { } _, err := client.SwarmGetUnlockKey(context.Background()) - assert.Check(t, is.ErrorContains(err, "Error response from daemon: Server error")) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) + } } func TestSwarmGetUnlockKey(t *testing.T) { @@ -33,7 +36,7 @@ func TestSwarmGetUnlockKey(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "GET" { + if req.Method != http.MethodGet { return nil, fmt.Errorf("expected GET method, got %s", req.Method) } @@ -48,7 +51,7 @@ func TestSwarmGetUnlockKey(t *testing.T) { return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(b)), + Body: io.NopCloser(bytes.NewReader(b)), }, nil }), } diff --git a/client/swarm_init.go b/client/swarm_init.go index 742ca0f0416a3..da3c1637ef049 100644 --- a/client/swarm_init.go +++ b/client/swarm_init.go @@ -10,12 +10,12 @@ import ( // SwarmInit initializes the swarm. func (cli *Client) SwarmInit(ctx context.Context, req swarm.InitRequest) (string, error) { serverResp, err := cli.post(ctx, "/swarm/init", nil, req, nil) + defer ensureReaderClosed(serverResp) if err != nil { return "", err } var response string err = json.NewDecoder(serverResp.body).Decode(&response) - ensureReaderClosed(serverResp) return response, err } diff --git a/client/swarm_init_test.go b/client/swarm_init_test.go index 1abadc75e92ca..3579af3e819a6 100644 --- a/client/swarm_init_test.go +++ b/client/swarm_init_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" ) func TestSwarmInitError(t *testing.T) { @@ -18,8 +19,8 @@ func TestSwarmInitError(t *testing.T) { } _, err := client.SwarmInit(context.Background(), swarm.InitRequest{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -31,12 +32,12 @@ func TestSwarmInit(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(`"body"`))), + Body: io.NopCloser(bytes.NewReader([]byte(`"body"`))), }, nil }), } diff --git a/client/swarm_inspect.go b/client/swarm_inspect.go index cfaabb25b1ef1..b52b67a8849bc 100644 --- a/client/swarm_inspect.go +++ b/client/swarm_inspect.go @@ -10,12 +10,12 @@ import ( // SwarmInspect inspects the swarm. func (cli *Client) SwarmInspect(ctx context.Context) (swarm.Swarm, error) { serverResp, err := cli.get(ctx, "/swarm", nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return swarm.Swarm{}, err } var response swarm.Swarm err = json.NewDecoder(serverResp.body).Decode(&response) - ensureReaderClosed(serverResp) return response, err } diff --git a/client/swarm_inspect_test.go b/client/swarm_inspect_test.go index 954adc94c6493..4c56b34fcc41f 100644 --- a/client/swarm_inspect_test.go +++ b/client/swarm_inspect_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" ) func TestSwarmInspectError(t *testing.T) { @@ -19,8 +20,8 @@ func TestSwarmInspectError(t *testing.T) { } _, err := client.SwarmInspect(context.Background()) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -41,7 +42,7 @@ func TestSwarmInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/swarm_join_test.go b/client/swarm_join_test.go index e67f2bdecfc19..933ad9380ecac 100644 --- a/client/swarm_join_test.go +++ b/client/swarm_join_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" ) func TestSwarmJoinError(t *testing.T) { @@ -18,8 +19,8 @@ func TestSwarmJoinError(t *testing.T) { } err := client.SwarmJoin(context.Background(), swarm.JoinRequest{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -31,12 +32,12 @@ func TestSwarmJoin(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/swarm_leave_test.go b/client/swarm_leave_test.go index 3dd3711d04519..89fcac8cf873b 100644 --- a/client/swarm_leave_test.go +++ b/client/swarm_leave_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestSwarmLeaveError(t *testing.T) { @@ -16,8 +18,8 @@ func TestSwarmLeaveError(t *testing.T) { } err := client.SwarmLeave(context.Background(), false) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -43,7 +45,7 @@ func TestSwarmLeave(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } force := req.URL.Query().Get("force") @@ -52,7 +54,7 @@ func TestSwarmLeave(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/swarm_unlock_test.go b/client/swarm_unlock_test.go index b3bcc5d922cc2..b308c9052da2f 100644 --- a/client/swarm_unlock_test.go +++ b/client/swarm_unlock_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" ) func TestSwarmUnlockError(t *testing.T) { @@ -18,8 +19,8 @@ func TestSwarmUnlockError(t *testing.T) { } err := client.SwarmUnlock(context.Background(), swarm.UnlockRequest{UnlockKey: "SWMKEY-1-y6guTZNTwpQeTL5RhUfOsdBdXoQjiB2GADHSRJvbXeU"}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -31,12 +32,12 @@ func TestSwarmUnlock(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/swarm_update_test.go b/client/swarm_update_test.go index e908bf7860aab..66fc6b4e28a3e 100644 --- a/client/swarm_update_test.go +++ b/client/swarm_update_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" ) func TestSwarmUpdateError(t *testing.T) { @@ -18,8 +19,8 @@ func TestSwarmUpdateError(t *testing.T) { } err := client.SwarmUpdate(context.Background(), swarm.Version{}, swarm.Spec{}, swarm.UpdateFlags{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -31,12 +32,12 @@ func TestSwarmUpdate(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte(""))), + Body: io.NopCloser(bytes.NewReader([]byte(""))), }, nil }), } diff --git a/client/task_inspect.go b/client/task_inspect.go index e1c0a736da589..410fc526e268e 100644 --- a/client/task_inspect.go +++ b/client/task_inspect.go @@ -4,23 +4,23 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" + "io" "github.com/docker/docker/api/types/swarm" ) -// TaskInspectWithRaw returns the task information and its raw representation.. +// TaskInspectWithRaw returns the task information and its raw representation. func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) { if taskID == "" { return swarm.Task{}, nil, objectNotFoundError{object: "task", id: taskID} } serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return swarm.Task{}, nil, wrapResponseError(err, serverResp, "task", taskID) } - defer ensureReaderClosed(serverResp) - body, err := ioutil.ReadAll(serverResp.body) + body, err := io.ReadAll(serverResp.body) if err != nil { return swarm.Task{}, nil, err } diff --git a/client/task_inspect_test.go b/client/task_inspect_test.go index fe5c5bd778af4..f8ed67cf192c1 100644 --- a/client/task_inspect_test.go +++ b/client/task_inspect_test.go @@ -5,12 +5,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" ) @@ -20,8 +21,8 @@ func TestTaskInspectError(t *testing.T) { } _, _, err := client.TaskInspectWithRaw(context.Background(), "nothing") - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -52,7 +53,7 @@ func TestTaskInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/task_list.go b/client/task_list.go index 42d20c1b8d1ab..4869b44493b1e 100644 --- a/client/task_list.go +++ b/client/task_list.go @@ -24,12 +24,12 @@ func (cli *Client) TaskList(ctx context.Context, options types.TaskListOptions) } resp, err := cli.get(ctx, "/tasks", query, nil) + defer ensureReaderClosed(resp) if err != nil { return nil, err } var tasks []swarm.Task err = json.NewDecoder(resp.body).Decode(&tasks) - ensureReaderClosed(resp) return tasks, err } diff --git a/client/task_list_test.go b/client/task_list_test.go index 16d0edaa0ac63..939319fffd562 100644 --- a/client/task_list_test.go +++ b/client/task_list_test.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/errdefs" ) func TestTaskListError(t *testing.T) { @@ -21,8 +22,8 @@ func TestTaskListError(t *testing.T) { } _, err := client.TaskList(context.Background(), types.TaskListOptions{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -78,7 +79,7 @@ func TestTaskList(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/version.go b/client/version.go index 1989f6d6d2baf..8f17ff4e87af1 100644 --- a/client/version.go +++ b/client/version.go @@ -10,12 +10,12 @@ import ( // ServerVersion returns information of the docker client and server host. func (cli *Client) ServerVersion(ctx context.Context) (types.Version, error) { resp, err := cli.get(ctx, "/version", nil, nil) + defer ensureReaderClosed(resp) if err != nil { return types.Version{}, err } var server types.Version err = json.NewDecoder(resp.body).Decode(&server) - ensureReaderClosed(resp) return server, err } diff --git a/client/volume_create.go b/client/volume_create.go index f1f6fcdc4a2da..92761b3c639ea 100644 --- a/client/volume_create.go +++ b/client/volume_create.go @@ -12,10 +12,10 @@ import ( func (cli *Client) VolumeCreate(ctx context.Context, options volumetypes.VolumeCreateBody) (types.Volume, error) { var volume types.Volume resp, err := cli.post(ctx, "/volumes/create", nil, options, nil) + defer ensureReaderClosed(resp) if err != nil { return volume, err } err = json.NewDecoder(resp.body).Decode(&volume) - ensureReaderClosed(resp) return volume, err } diff --git a/client/volume_create_test.go b/client/volume_create_test.go index cfab1918454c4..9f8624a2923d5 100644 --- a/client/volume_create_test.go +++ b/client/volume_create_test.go @@ -5,13 +5,14 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" volumetypes "github.com/docker/docker/api/types/volume" + "github.com/docker/docker/errdefs" ) func TestVolumeCreateError(t *testing.T) { @@ -20,8 +21,8 @@ func TestVolumeCreateError(t *testing.T) { } _, err := client.VolumeCreate(context.Background(), volumetypes.VolumeCreateBody{}) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -34,7 +35,7 @@ func TestVolumeCreate(t *testing.T) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "POST" { + if req.Method != http.MethodPost { return nil, fmt.Errorf("expected POST method, got %s", req.Method) } @@ -48,7 +49,7 @@ func TestVolumeCreate(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/volume_inspect.go b/client/volume_inspect.go index f840682d2ec7f..5c5b3f905c54c 100644 --- a/client/volume_inspect.go +++ b/client/volume_inspect.go @@ -4,7 +4,7 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" + "io" "github.com/docker/docker/api/types" ) @@ -23,12 +23,12 @@ func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (t var volume types.Volume resp, err := cli.get(ctx, "/volumes/"+volumeID, nil, nil) + defer ensureReaderClosed(resp) if err != nil { return volume, nil, wrapResponseError(err, resp, "volume", volumeID) } - defer ensureReaderClosed(resp) - body, err := ioutil.ReadAll(resp.body) + body, err := io.ReadAll(resp.body) if err != nil { return volume, nil, err } diff --git a/client/volume_inspect_test.go b/client/volume_inspect_test.go index 04f00129b7142..1aa2a54448016 100644 --- a/client/volume_inspect_test.go +++ b/client/volume_inspect_test.go @@ -5,15 +5,16 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" "github.com/docker/docker/api/types" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestVolumeInspectError(t *testing.T) { @@ -22,7 +23,9 @@ func TestVolumeInspectError(t *testing.T) { } _, err := client.VolumeInspect(context.Background(), "nothing") - assert.Check(t, is.ErrorContains(err, "Error response from daemon: Server error")) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) + } } func TestVolumeInspectNotFound(t *testing.T) { @@ -59,7 +62,7 @@ func TestVolumeInspect(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "GET" { + if req.Method != http.MethodGet { return nil, fmt.Errorf("expected GET method, got %s", req.Method) } content, err := json.Marshal(expected) @@ -68,7 +71,7 @@ func TestVolumeInspect(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/volume_list.go b/client/volume_list.go index 284554d67c036..942498dde2c74 100644 --- a/client/volume_list.go +++ b/client/volume_list.go @@ -15,6 +15,7 @@ func (cli *Client) VolumeList(ctx context.Context, filter filters.Args) (volumet query := url.Values{} if filter.Len() > 0 { + //nolint:staticcheck // ignore SA1019 for old code filterJSON, err := filters.ToParamWithVersion(cli.version, filter) if err != nil { return volumes, err @@ -22,11 +23,11 @@ func (cli *Client) VolumeList(ctx context.Context, filter filters.Args) (volumet query.Set("filters", filterJSON) } resp, err := cli.get(ctx, "/volumes", query, nil) + defer ensureReaderClosed(resp) if err != nil { return volumes, err } err = json.NewDecoder(resp.body).Decode(&volumes) - ensureReaderClosed(resp) return volumes, err } diff --git a/client/volume_list_test.go b/client/volume_list_test.go index 2a83823f7ef9f..d26bb51405e36 100644 --- a/client/volume_list_test.go +++ b/client/volume_list_test.go @@ -5,7 +5,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" @@ -13,6 +13,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" volumetypes "github.com/docker/docker/api/types/volume" + "github.com/docker/docker/errdefs" ) func TestVolumeListError(t *testing.T) { @@ -21,8 +22,8 @@ func TestVolumeListError(t *testing.T) { } _, err := client.VolumeList(context.Background(), filters.NewArgs()) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -82,7 +83,7 @@ func TestVolumeList(t *testing.T) { } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader(content)), + Body: io.NopCloser(bytes.NewReader(content)), }, nil }), } diff --git a/client/volume_prune.go b/client/volume_prune.go index 70041efed8b1b..6e324708f2b13 100644 --- a/client/volume_prune.go +++ b/client/volume_prune.go @@ -23,10 +23,10 @@ func (cli *Client) VolumesPrune(ctx context.Context, pruneFilters filters.Args) } serverResp, err := cli.post(ctx, "/volumes/prune", query, nil, nil) + defer ensureReaderClosed(serverResp) if err != nil { return report, err } - defer ensureReaderClosed(serverResp) if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil { return report, fmt.Errorf("Error retrieving volume prune report: %v", err) diff --git a/client/volume_remove.go b/client/volume_remove.go index fc5a71d3349e1..79decdafab88d 100644 --- a/client/volume_remove.go +++ b/client/volume_remove.go @@ -16,6 +16,6 @@ func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool } } resp, err := cli.delete(ctx, "/volumes/"+volumeID, query, nil) - ensureReaderClosed(resp) + defer ensureReaderClosed(resp) return wrapResponseError(err, resp, "volume", volumeID) } diff --git a/client/volume_remove_test.go b/client/volume_remove_test.go index 31fb3d71aa6b9..7581f72e8f5bc 100644 --- a/client/volume_remove_test.go +++ b/client/volume_remove_test.go @@ -4,10 +4,12 @@ import ( "bytes" "context" "fmt" - "io/ioutil" + "io" "net/http" "strings" "testing" + + "github.com/docker/docker/errdefs" ) func TestVolumeRemoveError(t *testing.T) { @@ -16,8 +18,8 @@ func TestVolumeRemoveError(t *testing.T) { } err := client.VolumeRemove(context.Background(), "volume_id", false) - if err == nil || err.Error() != "Error response from daemon: Server error" { - t.Fatalf("expected a Server Error, got %v", err) + if !errdefs.IsSystem(err) { + t.Fatalf("expected a Server Error, got %[1]T: %[1]v", err) } } @@ -29,12 +31,12 @@ func TestVolumeRemove(t *testing.T) { if !strings.HasPrefix(req.URL.Path, expectedURL) { return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) } - if req.Method != "DELETE" { + if req.Method != http.MethodDelete { return nil, fmt.Errorf("expected DELETE method, got %s", req.Method) } return &http.Response{ StatusCode: http.StatusOK, - Body: ioutil.NopCloser(bytes.NewReader([]byte("body"))), + Body: io.NopCloser(bytes.NewReader([]byte("body"))), }, nil }), } diff --git a/cmd/docker-proxy/main.go b/cmd/docker-proxy/main.go new file mode 100644 index 0000000000000..c2832b3fe3e66 --- /dev/null +++ b/cmd/docker-proxy/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "flag" + "fmt" + "log" + "net" + "os" + "os/signal" + "syscall" + + "github.com/ishidawataru/sctp" +) + +func main() { + f := os.NewFile(3, "signal-parent") + host, container := parseHostContainerAddrs() + + p, err := NewProxy(host, container) + if err != nil { + fmt.Fprintf(f, "1\n%s", err) + f.Close() + os.Exit(1) + } + go handleStopSignals(p) + fmt.Fprint(f, "0\n") + f.Close() + + // Run will block until the proxy stops + p.Run() +} + +// parseHostContainerAddrs parses the flags passed on reexec to create the TCP/UDP/SCTP +// net.Addrs to map the host and container ports +func parseHostContainerAddrs() (host net.Addr, container net.Addr) { + var ( + proto = flag.String("proto", "tcp", "proxy protocol") + hostIP = flag.String("host-ip", "", "host ip") + hostPort = flag.Int("host-port", -1, "host port") + containerIP = flag.String("container-ip", "", "container ip") + containerPort = flag.Int("container-port", -1, "container port") + ) + + flag.Parse() + + switch *proto { + case "tcp": + host = &net.TCPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort} + container = &net.TCPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort} + case "udp": + host = &net.UDPAddr{IP: net.ParseIP(*hostIP), Port: *hostPort} + container = &net.UDPAddr{IP: net.ParseIP(*containerIP), Port: *containerPort} + case "sctp": + host = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*hostIP)}}, Port: *hostPort} + container = &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.ParseIP(*containerIP)}}, Port: *containerPort} + default: + log.Fatalf("unsupported protocol %s", *proto) + } + + return host, container +} + +func handleStopSignals(p Proxy) { + s := make(chan os.Signal, 10) + signal.Notify(s, os.Interrupt, syscall.SIGTERM) + + for range s { + p.Close() + + os.Exit(0) + } +} diff --git a/cmd/docker-proxy/network_proxy_test.go b/cmd/docker-proxy/network_proxy_test.go new file mode 100644 index 0000000000000..7f6fb2c5bdf87 --- /dev/null +++ b/cmd/docker-proxy/network_proxy_test.go @@ -0,0 +1,310 @@ +package main + +import ( + "bytes" + "fmt" + "io" + "net" + "runtime" + "strings" + "testing" + "time" + + "github.com/ishidawataru/sctp" + "gotest.tools/v3/skip" +) + +var testBuf = []byte("Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo") +var testBufSize = len(testBuf) + +type EchoServer interface { + Run() + Close() + LocalAddr() net.Addr +} + +type EchoServerOptions struct { + TCPHalfClose bool +} + +type StreamEchoServer struct { + listener net.Listener + testCtx *testing.T + opts EchoServerOptions +} + +type UDPEchoServer struct { + conn net.PacketConn + testCtx *testing.T +} + +func NewEchoServer(t *testing.T, proto, address string, opts EchoServerOptions) EchoServer { + var server EchoServer + if !strings.HasPrefix(proto, "tcp") && opts.TCPHalfClose { + t.Fatalf("TCPHalfClose is not supported for %s", proto) + } + + switch { + case strings.HasPrefix(proto, "tcp"): + listener, err := net.Listen(proto, address) + if err != nil { + t.Fatal(err) + } + server = &StreamEchoServer{listener: listener, testCtx: t, opts: opts} + case strings.HasPrefix(proto, "udp"): + socket, err := net.ListenPacket(proto, address) + if err != nil { + t.Fatal(err) + } + server = &UDPEchoServer{conn: socket, testCtx: t} + case strings.HasPrefix(proto, "sctp"): + addr, err := sctp.ResolveSCTPAddr(proto, address) + if err != nil { + t.Fatal(err) + } + listener, err := sctp.ListenSCTP(proto, addr) + if err != nil { + t.Fatal(err) + } + server = &StreamEchoServer{listener: listener, testCtx: t} + default: + t.Fatalf("unknown protocol: %s", proto) + } + return server +} + +func (server *StreamEchoServer) Run() { + go func() { + for { + client, err := server.listener.Accept() + if err != nil { + return + } + go func(client net.Conn) { + if server.opts.TCPHalfClose { + data, err := io.ReadAll(client) + if err != nil { + server.testCtx.Logf("io.ReadAll() failed for the client: %v\n", err.Error()) + } + if _, err := client.Write(data); err != nil { + server.testCtx.Logf("can't echo to the client: %v\n", err.Error()) + } + client.(*net.TCPConn).CloseWrite() + } else { + if _, err := io.Copy(client, client); err != nil { + server.testCtx.Logf("can't echo to the client: %v\n", err.Error()) + } + client.Close() + } + }(client) + } + }() +} + +func (server *StreamEchoServer) LocalAddr() net.Addr { return server.listener.Addr() } +func (server *StreamEchoServer) Close() { server.listener.Close() } + +func (server *UDPEchoServer) Run() { + go func() { + readBuf := make([]byte, 1024) + for { + read, from, err := server.conn.ReadFrom(readBuf) + if err != nil { + return + } + for i := 0; i != read; { + written, err := server.conn.WriteTo(readBuf[i:read], from) + if err != nil { + break + } + i += written + } + } + }() +} + +func (server *UDPEchoServer) LocalAddr() net.Addr { return server.conn.LocalAddr() } +func (server *UDPEchoServer) Close() { server.conn.Close() } + +func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string, halfClose bool) { + defer proxy.Close() + go proxy.Run() + var client net.Conn + var err error + if strings.HasPrefix(proto, "sctp") { + var a *sctp.SCTPAddr + a, err = sctp.ResolveSCTPAddr(proto, addr) + if err != nil { + t.Fatal(err) + } + client, err = sctp.DialSCTP(proto, nil, a) + } else { + client, err = net.Dial(proto, addr) + } + + if err != nil { + t.Fatalf("Can't connect to the proxy: %v", err) + } + defer client.Close() + client.SetDeadline(time.Now().Add(10 * time.Second)) + if _, err = client.Write(testBuf); err != nil { + t.Fatal(err) + } + if halfClose { + if proto != "tcp" { + t.Fatalf("halfClose is not supported for %s", proto) + } + client.(*net.TCPConn).CloseWrite() + } + recvBuf := make([]byte, testBufSize) + if _, err = client.Read(recvBuf); err != nil { + t.Fatal(err) + } + if !bytes.Equal(testBuf, recvBuf) { + t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf)) + } +} + +func testProxy(t *testing.T, proto string, proxy Proxy, halfClose bool) { + testProxyAt(t, proto, proxy, proxy.FrontendAddr().String(), halfClose) +} + +func testTCP4Proxy(t *testing.T, halfClose bool) { + backend := NewEchoServer(t, "tcp", "127.0.0.1:0", EchoServerOptions{TCPHalfClose: halfClose}) + defer backend.Close() + backend.Run() + frontendAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} + proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) + if err != nil { + t.Fatal(err) + } + testProxy(t, "tcp", proxy, halfClose) +} + +func TestTCP4Proxy(t *testing.T) { + testTCP4Proxy(t, false) +} + +func TestTCP4ProxyHalfClose(t *testing.T) { + testTCP4Proxy(t, true) +} + +func TestTCP6Proxy(t *testing.T) { + t.Skip("Need to start CI docker with --ipv6") + backend := NewEchoServer(t, "tcp", "[::1]:0", EchoServerOptions{}) + defer backend.Close() + backend.Run() + frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0} + proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) + if err != nil { + t.Fatal(err) + } + testProxy(t, "tcp", proxy, false) +} + +func TestTCPDualStackProxy(t *testing.T) { + // If I understand `godoc -src net favoriteAddrFamily` (used by the + // net.Listen* functions) correctly this should work, but it doesn't. + t.Skip("No support for dual stack yet") + backend := NewEchoServer(t, "tcp", "[::1]:0", EchoServerOptions{}) + defer backend.Close() + backend.Run() + frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0} + proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) + if err != nil { + t.Fatal(err) + } + ipv4ProxyAddr := &net.TCPAddr{ + IP: net.IPv4(127, 0, 0, 1), + Port: proxy.FrontendAddr().(*net.TCPAddr).Port, + } + testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String(), false) +} + +func TestUDP4Proxy(t *testing.T) { + backend := NewEchoServer(t, "udp", "127.0.0.1:0", EchoServerOptions{}) + defer backend.Close() + backend.Run() + frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} + proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) + if err != nil { + t.Fatal(err) + } + testProxy(t, "udp", proxy, false) +} + +func TestUDP6Proxy(t *testing.T) { + t.Skip("Need to start CI docker with --ipv6") + backend := NewEchoServer(t, "udp", "[::1]:0", EchoServerOptions{}) + defer backend.Close() + backend.Run() + frontendAddr := &net.UDPAddr{IP: net.IPv6loopback, Port: 0} + proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) + if err != nil { + t.Fatal(err) + } + testProxy(t, "udp", proxy, false) +} + +func TestUDPWriteError(t *testing.T) { + frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} + // Hopefully, this port will be free: */ + backendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 25587} + proxy, err := NewProxy(frontendAddr, backendAddr) + if err != nil { + t.Fatal(err) + } + defer proxy.Close() + go proxy.Run() + client, err := net.Dial("udp", "127.0.0.1:25587") + if err != nil { + t.Fatalf("Can't connect to the proxy: %v", err) + } + defer client.Close() + // Make sure the proxy doesn't stop when there is no actual backend: + client.Write(testBuf) + client.Write(testBuf) + backend := NewEchoServer(t, "udp", "127.0.0.1:25587", EchoServerOptions{}) + defer backend.Close() + backend.Run() + client.SetDeadline(time.Now().Add(10 * time.Second)) + if _, err = client.Write(testBuf); err != nil { + t.Fatal(err) + } + recvBuf := make([]byte, testBufSize) + if _, err = client.Read(recvBuf); err != nil { + t.Fatal(err) + } + if !bytes.Equal(testBuf, recvBuf) { + t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf)) + } +} + +func TestSCTP4Proxy(t *testing.T) { + skip.If(t, runtime.GOOS == "windows", "sctp is not supported on windows") + + backend := NewEchoServer(t, "sctp", "127.0.0.1:0", EchoServerOptions{}) + defer backend.Close() + backend.Run() + frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv4(127, 0, 0, 1)}}, Port: 0} + proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) + if err != nil { + t.Fatal(err) + } + testProxy(t, "sctp", proxy, false) +} + +func TestSCTP6Proxy(t *testing.T) { + t.Skip("Need to start CI docker with --ipv6") + skip.If(t, runtime.GOOS == "windows", "sctp is not supported on windows") + + backend := NewEchoServer(t, "sctp", "[::1]:0", EchoServerOptions{}) + defer backend.Close() + backend.Run() + frontendAddr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: net.IPv6loopback}}, Port: 0} + proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) + if err != nil { + t.Fatal(err) + } + testProxy(t, "sctp", proxy, false) +} diff --git a/cmd/docker-proxy/proxy.go b/cmd/docker-proxy/proxy.go new file mode 100644 index 0000000000000..1bd8f9d8e1b36 --- /dev/null +++ b/cmd/docker-proxy/proxy.go @@ -0,0 +1,50 @@ +// docker-proxy provides a network Proxy interface and implementations for TCP +// and UDP. +package main + +import ( + "net" + + "github.com/ishidawataru/sctp" +) + +// ipVersion refers to IP version - v4 or v6 +type ipVersion string + +const ( + // IPv4 is version 4 + ipv4 ipVersion = "4" + // IPv4 is version 6 + ipv6 ipVersion = "6" +) + +// Proxy defines the behavior of a proxy. It forwards traffic back and forth +// between two endpoints : the frontend and the backend. +// It can be used to do software port-mapping between two addresses. +// e.g. forward all traffic between the frontend (host) 127.0.0.1:3000 +// to the backend (container) at 172.17.42.108:4000. +type Proxy interface { + // Run starts forwarding traffic back and forth between the front + // and back-end addresses. + Run() + // Close stops forwarding traffic and close both ends of the Proxy. + Close() + // FrontendAddr returns the address on which the proxy is listening. + FrontendAddr() net.Addr + // BackendAddr returns the proxied address. + BackendAddr() net.Addr +} + +// NewProxy creates a Proxy according to the specified frontendAddr and backendAddr. +func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) { + switch frontendAddr.(type) { + case *net.UDPAddr: + return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr)) + case *net.TCPAddr: + return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr)) + case *sctp.SCTPAddr: + return NewSCTPProxy(frontendAddr.(*sctp.SCTPAddr), backendAddr.(*sctp.SCTPAddr)) + default: + panic("Unsupported protocol") + } +} diff --git a/cmd/docker-proxy/sctp_proxy.go b/cmd/docker-proxy/sctp_proxy.go new file mode 100644 index 0000000000000..9b18686341a01 --- /dev/null +++ b/cmd/docker-proxy/sctp_proxy.go @@ -0,0 +1,98 @@ +package main + +import ( + "io" + "log" + "net" + "sync" + + "github.com/ishidawataru/sctp" +) + +// SCTPProxy is a proxy for SCTP connections. It implements the Proxy interface to +// handle SCTP traffic forwarding between the frontend and backend addresses. +type SCTPProxy struct { + listener *sctp.SCTPListener + frontendAddr *sctp.SCTPAddr + backendAddr *sctp.SCTPAddr +} + +// NewSCTPProxy creates a new SCTPProxy. +func NewSCTPProxy(frontendAddr, backendAddr *sctp.SCTPAddr) (*SCTPProxy, error) { + // detect version of hostIP to bind only to correct version + ipVersion := ipv4 + if frontendAddr.IPAddrs[0].IP.To4() == nil { + ipVersion = ipv6 + } + listener, err := sctp.ListenSCTP("sctp"+string(ipVersion), frontendAddr) + if err != nil { + return nil, err + } + // If the port in frontendAddr was 0 then ListenSCTP will have a picked + // a port to listen on, hence the call to Addr to get that actual port: + return &SCTPProxy{ + listener: listener, + frontendAddr: listener.Addr().(*sctp.SCTPAddr), + backendAddr: backendAddr, + }, nil +} + +func (proxy *SCTPProxy) clientLoop(client *sctp.SCTPConn, quit chan bool) { + backend, err := sctp.DialSCTP("sctp", nil, proxy.backendAddr) + if err != nil { + log.Printf("Can't forward traffic to backend sctp/%v: %s\n", proxy.backendAddr, err) + client.Close() + return + } + clientC := sctp.NewSCTPSndRcvInfoWrappedConn(client) + backendC := sctp.NewSCTPSndRcvInfoWrappedConn(backend) + + var wg sync.WaitGroup + var broker = func(to, from net.Conn) { + io.Copy(to, from) + from.Close() + to.Close() + wg.Done() + } + + wg.Add(2) + go broker(clientC, backendC) + go broker(backendC, clientC) + + finish := make(chan struct{}) + go func() { + wg.Wait() + close(finish) + }() + + select { + case <-quit: + case <-finish: + } + clientC.Close() + backendC.Close() + <-finish +} + +// Run starts forwarding the traffic using SCTP. +func (proxy *SCTPProxy) Run() { + quit := make(chan bool) + defer close(quit) + for { + client, err := proxy.listener.Accept() + if err != nil { + log.Printf("Stopping proxy on sctp/%v for sctp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err) + return + } + go proxy.clientLoop(client.(*sctp.SCTPConn), quit) + } +} + +// Close stops forwarding the traffic. +func (proxy *SCTPProxy) Close() { proxy.listener.Close() } + +// FrontendAddr returns the SCTP address on which the proxy is listening. +func (proxy *SCTPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } + +// BackendAddr returns the SCTP proxied address. +func (proxy *SCTPProxy) BackendAddr() net.Addr { return proxy.backendAddr } diff --git a/cmd/docker-proxy/tcp_proxy.go b/cmd/docker-proxy/tcp_proxy.go new file mode 100644 index 0000000000000..fe8c8fac9d5c5 --- /dev/null +++ b/cmd/docker-proxy/tcp_proxy.go @@ -0,0 +1,94 @@ +package main + +import ( + "io" + "log" + "net" + "sync" +) + +// TCPProxy is a proxy for TCP connections. It implements the Proxy interface to +// handle TCP traffic forwarding between the frontend and backend addresses. +type TCPProxy struct { + listener *net.TCPListener + frontendAddr *net.TCPAddr + backendAddr *net.TCPAddr +} + +// NewTCPProxy creates a new TCPProxy. +func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) { + // detect version of hostIP to bind only to correct version + ipVersion := ipv4 + if frontendAddr.IP.To4() == nil { + ipVersion = ipv6 + } + listener, err := net.ListenTCP("tcp"+string(ipVersion), frontendAddr) + if err != nil { + return nil, err + } + // If the port in frontendAddr was 0 then ListenTCP will have a picked + // a port to listen on, hence the call to Addr to get that actual port: + return &TCPProxy{ + listener: listener, + frontendAddr: listener.Addr().(*net.TCPAddr), + backendAddr: backendAddr, + }, nil +} + +func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) { + backend, err := net.DialTCP("tcp", nil, proxy.backendAddr) + if err != nil { + log.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err) + client.Close() + return + } + + var wg sync.WaitGroup + var broker = func(to, from *net.TCPConn) { + io.Copy(to, from) + from.CloseRead() + to.CloseWrite() + wg.Done() + } + + wg.Add(2) + go broker(client, backend) + go broker(backend, client) + + finish := make(chan struct{}) + go func() { + wg.Wait() + close(finish) + }() + + select { + case <-quit: + case <-finish: + } + client.Close() + backend.Close() + <-finish +} + +// Run starts forwarding the traffic using TCP. +func (proxy *TCPProxy) Run() { + quit := make(chan bool) + defer close(quit) + for { + client, err := proxy.listener.Accept() + if err != nil { + log.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err) + return + } + go proxy.clientLoop(client.(*net.TCPConn), quit) + } +} + +// Close stops forwarding the traffic. +func (proxy *TCPProxy) Close() { proxy.listener.Close() } + +// FrontendAddr returns the TCP address on which the proxy is listening. +func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } + +// BackendAddr returns the TCP proxied address. +func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr } diff --git a/cmd/docker-proxy/udp_proxy.go b/cmd/docker-proxy/udp_proxy.go new file mode 100644 index 0000000000000..66bacafa4febf --- /dev/null +++ b/cmd/docker-proxy/udp_proxy.go @@ -0,0 +1,173 @@ +package main + +import ( + "encoding/binary" + "log" + "net" + "strings" + "sync" + "syscall" + "time" +) + +const ( + // UDPConnTrackTimeout is the timeout used for UDP connection tracking + UDPConnTrackTimeout = 90 * time.Second + // UDPBufSize is the buffer size for the UDP proxy + UDPBufSize = 65507 +) + +// A net.Addr where the IP is split into two fields so you can use it as a key +// in a map: +type connTrackKey struct { + IPHigh uint64 + IPLow uint64 + Port int +} + +func newConnTrackKey(addr *net.UDPAddr) *connTrackKey { + if len(addr.IP) == net.IPv4len { + return &connTrackKey{ + IPHigh: 0, + IPLow: uint64(binary.BigEndian.Uint32(addr.IP)), + Port: addr.Port, + } + } + return &connTrackKey{ + IPHigh: binary.BigEndian.Uint64(addr.IP[:8]), + IPLow: binary.BigEndian.Uint64(addr.IP[8:]), + Port: addr.Port, + } +} + +type connTrackMap map[connTrackKey]*net.UDPConn + +// UDPProxy is proxy for which handles UDP datagrams. It implements the Proxy +// interface to handle UDP traffic forwarding between the frontend and backend +// addresses. +type UDPProxy struct { + listener *net.UDPConn + frontendAddr *net.UDPAddr + backendAddr *net.UDPAddr + connTrackTable connTrackMap + connTrackLock sync.Mutex +} + +// NewUDPProxy creates a new UDPProxy. +func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) { + // detect version of hostIP to bind only to correct version + ipVersion := ipv4 + if frontendAddr.IP.To4() == nil { + ipVersion = ipv6 + } + listener, err := net.ListenUDP("udp"+string(ipVersion), frontendAddr) + if err != nil { + return nil, err + } + return &UDPProxy{ + listener: listener, + frontendAddr: listener.LocalAddr().(*net.UDPAddr), + backendAddr: backendAddr, + connTrackTable: make(connTrackMap), + }, nil +} + +func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) { + defer func() { + proxy.connTrackLock.Lock() + delete(proxy.connTrackTable, *clientKey) + proxy.connTrackLock.Unlock() + proxyConn.Close() + }() + + readBuf := make([]byte, UDPBufSize) + for { + proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout)) + again: + read, err := proxyConn.Read(readBuf) + if err != nil { + if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED { + // This will happen if the last write failed + // (e.g: nothing is actually listening on the + // proxied port on the container), ignore it + // and continue until UDPConnTrackTimeout + // expires: + goto again + } + return + } + for i := 0; i != read; { + written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr) + if err != nil { + return + } + i += written + } + } +} + +// Run starts forwarding the traffic using UDP. +func (proxy *UDPProxy) Run() { + readBuf := make([]byte, UDPBufSize) + for { + read, from, err := proxy.listener.ReadFromUDP(readBuf) + if err != nil { + // NOTE: Apparently ReadFrom doesn't return + // ECONNREFUSED like Read do (see comment in + // UDPProxy.replyLoop) + if !isClosedError(err) { + log.Printf("Stopping proxy on udp/%v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err) + } + break + } + + fromKey := newConnTrackKey(from) + proxy.connTrackLock.Lock() + proxyConn, hit := proxy.connTrackTable[*fromKey] + if !hit { + proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr) + if err != nil { + log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err) + proxy.connTrackLock.Unlock() + continue + } + proxy.connTrackTable[*fromKey] = proxyConn + go proxy.replyLoop(proxyConn, from, fromKey) + } + proxy.connTrackLock.Unlock() + for i := 0; i != read; { + written, err := proxyConn.Write(readBuf[i:read]) + if err != nil { + log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err) + break + } + i += written + } + } +} + +// Close stops forwarding the traffic. +func (proxy *UDPProxy) Close() { + proxy.listener.Close() + proxy.connTrackLock.Lock() + defer proxy.connTrackLock.Unlock() + for _, conn := range proxy.connTrackTable { + conn.Close() + } +} + +// FrontendAddr returns the UDP address on which the proxy is listening. +func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } + +// BackendAddr returns the proxied UDP address. +func (proxy *UDPProxy) BackendAddr() net.Addr { return proxy.backendAddr } + +func isClosedError(err error) bool { + /* This comparison is ugly, but unfortunately, net.go doesn't export errClosing. + * See: + * http://golang.org/src/pkg/net/net.go + * https://code.google.com/p/go/issues/detail?id=4337 + * https://groups.google.com/forum/#!msg/golang-nuts/0_aaCvBmOcM/SptmDyX1XJMJ + */ + return strings.HasSuffix(err.Error(), "use of closed network connection") +} diff --git a/cmd/dockerd/config.go b/cmd/dockerd/config.go index 2c8ed8edb4a94..0f1dff4b8e3c1 100644 --- a/cmd/dockerd/config.go +++ b/cmd/dockerd/config.go @@ -3,8 +3,10 @@ package main import ( "runtime" + "github.com/docker/docker/daemon" "github.com/docker/docker/daemon/config" "github.com/docker/docker/opts" + "github.com/docker/docker/plugin/executor/containerd" "github.com/docker/docker/registry" "github.com/spf13/pflag" ) @@ -17,8 +19,20 @@ const ( ) // installCommonConfigFlags adds flags to the pflag.FlagSet to configure the daemon -func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) { - var maxConcurrentDownloads, maxConcurrentUploads int +func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) error { + var maxConcurrentDownloads, maxConcurrentUploads, maxDownloadAttempts int + defaultPidFile, err := getDefaultPidFile() + if err != nil { + return err + } + defaultDataRoot, err := getDefaultDataRoot() + if err != nil { + return err + } + defaultExecRoot, err := getDefaultExecRoot() + if err != nil { + return err + } installRegistryServiceFlags(&conf.ServiceOptions, flags) @@ -33,12 +47,12 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) { // "--graph" is "soft-deprecated" in favor of "data-root". This flag was added // before Docker 1.0, so won't be removed, only hidden, to discourage its usage. - flags.MarkHidden("graph") + _ = flags.MarkHidden("graph") flags.StringVar(&conf.Root, "data-root", defaultDataRoot, "Root directory of persistent Docker state") flags.BoolVarP(&conf.AutoRestart, "restart", "r", true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run") - flags.MarkDeprecated("restart", "Please use a restart policy on docker run") + _ = flags.MarkDeprecated("restart", "Please use a restart policy on docker run") // Windows doesn't support setting the storage driver - there is no choice as to which ones to use. if runtime.GOOS != "windows" { @@ -50,18 +64,25 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) { flags.Var(opts.NewListOptsRef(&conf.DNS, opts.ValidateIPAddress), "dns", "DNS server to use") flags.Var(opts.NewNamedListOptsRef("dns-opts", &conf.DNSOptions, nil), "dns-opt", "DNS options to use") flags.Var(opts.NewListOptsRef(&conf.DNSSearch, opts.ValidateDNSSearch), "dns-search", "DNS search domains to use") + flags.Var(opts.NewIPOpt(&conf.HostGatewayIP, ""), "host-gateway-ip", "IP address that the special 'host-gateway' string in --add-host resolves to. Defaults to the IP address of the default bridge") flags.Var(opts.NewNamedListOptsRef("labels", &conf.Labels, opts.ValidateLabel), "label", "Set key=value labels to the daemon") flags.StringVar(&conf.LogConfig.Type, "log-driver", "json-file", "Default driver for container logs") flags.Var(opts.NewNamedMapOpts("log-opts", conf.LogConfig.Config, nil), "log-opt", "Default log driver options for containers") + flags.StringVar(&conf.ClusterAdvertise, "cluster-advertise", "", "Address or interface name to advertise") + _ = flags.MarkDeprecated("cluster-advertise", "Swarm classic is deprecated. Please use Swarm-mode (docker swarm init)") flags.StringVar(&conf.ClusterStore, "cluster-store", "", "URL of the distributed storage backend") + _ = flags.MarkDeprecated("cluster-store", "Swarm classic is deprecated. Please use Swarm-mode (docker swarm init)") flags.Var(opts.NewNamedMapOpts("cluster-store-opts", conf.ClusterOpts, nil), "cluster-store-opt", "Set cluster store options") + _ = flags.MarkDeprecated("cluster-store-opt", "Swarm classic is deprecated. Please use Swarm-mode (docker swarm init)") + flags.StringVar(&conf.CorsHeaders, "api-cors-header", "", "Set CORS headers in the Engine API") flags.IntVar(&maxConcurrentDownloads, "max-concurrent-downloads", config.DefaultMaxConcurrentDownloads, "Set the max concurrent downloads for each pull") flags.IntVar(&maxConcurrentUploads, "max-concurrent-uploads", config.DefaultMaxConcurrentUploads, "Set the max concurrent uploads for each push") + flags.IntVar(&maxDownloadAttempts, "max-download-attempts", config.DefaultDownloadAttempts, "Set the max download attempts for each pull") flags.IntVar(&conf.ShutdownTimeout, "shutdown-timeout", defaultShutdownTimeout, "Set the default shutdown timeout") flags.IntVar(&conf.NetworkDiagnosticPort, "network-diagnostic-port", 0, "TCP port number of the network diagnostic server") - flags.MarkHidden("network-diagnostic-port") + _ = flags.MarkHidden("network-diagnostic-port") flags.StringVar(&conf.SwarmDefaultAdvertiseAddr, "swarm-default-advertise-addr", "", "Set default address or interface for swarm advertised address") flags.BoolVar(&conf.Experimental, "experimental", false, "Enable experimental features") @@ -71,15 +92,20 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) { flags.IntVar(&conf.NetworkControlPlaneMTU, "network-control-plane-mtu", config.DefaultNetworkMtu, "Network Control plane MTU") - // "--deprecated-key-path" is to allow configuration of the key used - // for the daemon ID and the deprecated image signing. It was never - // exposed as a command line option but is added here to allow - // overriding the default path in configuration. - flags.Var(opts.NewQuotedString(&conf.TrustKeyPath), "deprecated-key-path", "Path to key file for ID and image signing") - flags.MarkHidden("deprecated-key-path") - conf.MaxConcurrentDownloads = &maxConcurrentDownloads conf.MaxConcurrentUploads = &maxConcurrentUploads + conf.MaxDownloadAttempts = &maxDownloadAttempts + + flags.StringVar(&conf.ContainerdNamespace, "containerd-namespace", daemon.ContainersNamespace, "Containerd namespace to use") + flags.StringVar(&conf.ContainerdPluginNamespace, "containerd-plugins-namespace", containerd.PluginNamespace, "Containerd namespace to use for plugins") + + flags.StringVar(&conf.DefaultRuntime, "default-runtime", config.StockRuntimeName, "Default OCI runtime for containers") + + flags.StringVar(&conf.HTTPProxy, "http-proxy", "", "HTTP proxy URL to use for outgoing traffic") + flags.StringVar(&conf.HTTPSProxy, "https-proxy", "", "HTTPS proxy URL to use for outgoing traffic") + flags.StringVar(&conf.NoProxy, "no-proxy", "", "Comma-separated list of hosts or IP addresses for which the proxy is skipped") + + return nil } func installRegistryServiceFlags(options *registry.ServiceOptions, flags *pflag.FlagSet) { @@ -90,10 +116,4 @@ func installRegistryServiceFlags(options *registry.ServiceOptions, flags *pflag. flags.Var(ana, "allow-nondistributable-artifacts", "Allow push of nondistributable artifacts to registry") flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror") flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication") - - if runtime.GOOS != "windows" { - // TODO: Remove this flag after 3 release cycles (18.03) - flags.BoolVar(&options.V2Only, "disable-legacy-registry", true, "Disable contacting legacy registries") - flags.MarkHidden("disable-legacy-registry") - } } diff --git a/cmd/dockerd/config_common_unix.go b/cmd/dockerd/config_common_unix.go index febf30ae9fca6..dfbc3e2e3d0e3 100644 --- a/cmd/dockerd/config_common_unix.go +++ b/cmd/dockerd/config_common_unix.go @@ -1,19 +1,50 @@ +//go:build linux || freebsd // +build linux freebsd package main import ( + "path/filepath" + "github.com/docker/docker/api/types" "github.com/docker/docker/daemon/config" "github.com/docker/docker/opts" + "github.com/docker/docker/pkg/homedir" "github.com/spf13/pflag" ) -var ( - defaultPidFile = "/var/run/docker.pid" - defaultDataRoot = "/var/lib/docker" - defaultExecRoot = "/var/run/docker" -) +func getDefaultPidFile() (string, error) { + if !honorXDG { + return "/var/run/docker.pid", nil + } + runtimeDir, err := homedir.GetRuntimeDir() + if err != nil { + return "", err + } + return filepath.Join(runtimeDir, "docker.pid"), nil +} + +func getDefaultDataRoot() (string, error) { + if !honorXDG { + return "/var/lib/docker", nil + } + dataHome, err := homedir.GetDataHome() + if err != nil { + return "", err + } + return filepath.Join(dataHome, "docker"), nil +} + +func getDefaultExecRoot() (string, error) { + if !honorXDG { + return "/var/run/docker", nil + } + runtimeDir, err := homedir.GetRuntimeDir() + if err != nil { + return "", err + } + return filepath.Join(runtimeDir, "docker"), nil +} // installUnixConfigFlags adds command-line options to the top-level flag parser for // the current process that are common across Unix platforms. @@ -29,6 +60,4 @@ func installUnixConfigFlags(conf *config.Config, flags *pflag.FlagSet) { flags.BoolVar(&conf.BridgeConfig.InterContainerCommunication, "icc", true, "Enable inter-container communication") flags.Var(opts.NewIPOpt(&conf.BridgeConfig.DefaultIP, "0.0.0.0"), "ip", "Default IP when binding container ports") flags.Var(opts.NewNamedRuntimeOpt("runtimes", &conf.Runtimes, config.StockRuntimeName), "add-runtime", "Register an additional OCI compatible runtime") - flags.StringVar(&conf.DefaultRuntime, "default-runtime", config.StockRuntimeName, "Default OCI runtime for containers") - } diff --git a/cmd/dockerd/config_unix.go b/cmd/dockerd/config_unix.go index 2dbd84b1db079..354d5da1968f9 100644 --- a/cmd/dockerd/config_unix.go +++ b/cmd/dockerd/config_unix.go @@ -1,18 +1,26 @@ +//go:build linux || freebsd // +build linux freebsd package main import ( + "os/exec" + + "github.com/containerd/cgroups" "github.com/docker/docker/daemon/config" "github.com/docker/docker/opts" - "github.com/docker/go-units" + "github.com/docker/docker/rootless" + units "github.com/docker/go-units" + "github.com/pkg/errors" "github.com/spf13/pflag" ) // installConfigFlags adds flags to the pflag.FlagSet to configure the daemon -func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) { +func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) error { // First handle install flags which are consistent cross-platform - installCommonConfigFlags(conf, flags) + if err := installCommonConfigFlags(conf, flags); err != nil { + return err + } // Then install flags common to unix platforms installUnixConfigFlags(conf, flags) @@ -27,24 +35,42 @@ func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) { flags.BoolVar(&conf.EnableSelinuxSupport, "selinux-enabled", false, "Enable selinux support") flags.Var(opts.NewNamedUlimitOpt("default-ulimits", &conf.Ulimits), "default-ulimit", "Default ulimits for containers") flags.BoolVar(&conf.BridgeConfig.EnableIPTables, "iptables", true, "Enable addition of iptables rules") + flags.BoolVar(&conf.BridgeConfig.EnableIP6Tables, "ip6tables", false, "Enable addition of ip6tables rules") flags.BoolVar(&conf.BridgeConfig.EnableIPForward, "ip-forward", true, "Enable net.ipv4.ip_forward") flags.BoolVar(&conf.BridgeConfig.EnableIPMasq, "ip-masq", true, "Enable IP masquerading") flags.BoolVar(&conf.BridgeConfig.EnableIPv6, "ipv6", false, "Enable IPv6 networking") flags.StringVar(&conf.BridgeConfig.FixedCIDRv6, "fixed-cidr-v6", "", "IPv6 subnet for fixed IPs") flags.BoolVar(&conf.BridgeConfig.EnableUserlandProxy, "userland-proxy", true, "Use userland proxy for loopback traffic") - flags.StringVar(&conf.BridgeConfig.UserlandProxyPath, "userland-proxy-path", "", "Path to the userland proxy binary") + defaultUserlandProxyPath := "" + if rootless.RunningWithRootlessKit() { + var err error + // use rootlesskit-docker-proxy for exposing the ports in RootlessKit netns to the initial namespace. + defaultUserlandProxyPath, err = exec.LookPath(rootless.RootlessKitDockerProxyBinary) + if err != nil { + return errors.Wrapf(err, "running with RootlessKit, but %s not installed", rootless.RootlessKitDockerProxyBinary) + } + } + flags.StringVar(&conf.BridgeConfig.UserlandProxyPath, "userland-proxy-path", defaultUserlandProxyPath, "Path to the userland proxy binary") flags.StringVar(&conf.CgroupParent, "cgroup-parent", "", "Set parent cgroup for all containers") flags.StringVar(&conf.RemappedRoot, "userns-remap", "", "User/Group setting for user namespaces") flags.BoolVar(&conf.LiveRestoreEnabled, "live-restore", false, "Enable live restore of docker when containers are still running") - flags.IntVar(&conf.OOMScoreAdjust, "oom-score-adjust", -500, "Set the oom_score_adj for the daemon") + flags.IntVar(&conf.OOMScoreAdjust, "oom-score-adjust", 0, "Set the oom_score_adj for the daemon") flags.BoolVar(&conf.Init, "init", false, "Run an init in the container to forward signals and reap processes") flags.StringVar(&conf.InitPath, "init-path", "", "Path to the docker-init binary") - flags.Int64Var(&conf.CPURealtimePeriod, "cpu-rt-period", 0, "Limit the CPU real-time period in microseconds") - flags.Int64Var(&conf.CPURealtimeRuntime, "cpu-rt-runtime", 0, "Limit the CPU real-time runtime in microseconds") - flags.StringVar(&conf.SeccompProfile, "seccomp-profile", "", "Path to seccomp profile") + flags.Int64Var(&conf.CPURealtimePeriod, "cpu-rt-period", 0, "Limit the CPU real-time period in microseconds for the parent cgroup for all containers") + flags.Int64Var(&conf.CPURealtimeRuntime, "cpu-rt-runtime", 0, "Limit the CPU real-time runtime in microseconds for the parent cgroup for all containers") + flags.StringVar(&conf.SeccompProfile, "seccomp-profile", config.SeccompProfileDefault, `Path to seccomp profile. Use "unconfined" to disable the default seccomp profile`) flags.Var(&conf.ShmSize, "default-shm-size", "Default shm size for containers") flags.BoolVar(&conf.NoNewPrivileges, "no-new-privileges", false, "Set no-new-privileges by default for new containers") - flags.StringVar(&conf.IpcMode, "default-ipc-mode", config.DefaultIpcMode, `Default mode for containers ipc ("shareable" | "private")`) + flags.StringVar(&conf.IpcMode, "default-ipc-mode", string(config.DefaultIpcMode), `Default mode for containers ipc ("shareable" | "private")`) flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "Default address pools for node specific local networks") - + // rootless needs to be explicitly specified for running "rootful" dockerd in rootless dockerd (#38702) + // Note that defaultUserlandProxyPath and honorXDG are configured according to the value of rootless.RunningWithRootlessKit, not the value of --rootless. + flags.BoolVar(&conf.Rootless, "rootless", rootless.RunningWithRootlessKit(), "Enable rootless mode; typically used with RootlessKit") + defaultCgroupNamespaceMode := config.DefaultCgroupNamespaceMode + if cgroups.Mode() != cgroups.Unified { + defaultCgroupNamespaceMode = config.DefaultCgroupV1NamespaceMode + } + flags.StringVar(&conf.CgroupNamespaceMode, "default-cgroupns-mode", string(defaultCgroupNamespaceMode), `Default mode for containers cgroup namespace ("host" | "private")`) + return nil } diff --git a/cmd/dockerd/config_unix_test.go b/cmd/dockerd/config_unix_test.go index d7dbf4b4cc5d7..1ffe4c1fbf2b8 100644 --- a/cmd/dockerd/config_unix_test.go +++ b/cmd/dockerd/config_unix_test.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package main @@ -7,15 +8,16 @@ import ( "github.com/docker/docker/daemon/config" "github.com/spf13/pflag" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestDaemonParseShmSize(t *testing.T) { flags := pflag.NewFlagSet("test", pflag.ContinueOnError) conf := &config.Config{} - installConfigFlags(conf, flags) + err := installConfigFlags(conf, flags) + assert.NilError(t, err) // By default `--default-shm-size=64M` assert.Check(t, is.Equal(int64(64*1024*1024), conf.ShmSize.Value())) assert.Check(t, flags.Set("default-shm-size", "128M")) diff --git a/cmd/dockerd/config_windows.go b/cmd/dockerd/config_windows.go index 36af76645ff95..88b76196dc34c 100644 --- a/cmd/dockerd/config_windows.go +++ b/cmd/dockerd/config_windows.go @@ -8,19 +8,28 @@ import ( "github.com/spf13/pflag" ) -var ( - defaultPidFile string - defaultDataRoot = filepath.Join(os.Getenv("programdata"), "docker") - defaultExecRoot = filepath.Join(os.Getenv("programdata"), "docker", "exec-root") -) +func getDefaultPidFile() (string, error) { + return "", nil +} + +func getDefaultDataRoot() (string, error) { + return filepath.Join(os.Getenv("programdata"), "docker"), nil +} + +func getDefaultExecRoot() (string, error) { + return filepath.Join(os.Getenv("programdata"), "docker", "exec-root"), nil +} // installConfigFlags adds flags to the pflag.FlagSet to configure the daemon -func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) { +func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) error { // First handle install flags which are consistent cross-platform - installCommonConfigFlags(conf, flags) + if err := installCommonConfigFlags(conf, flags); err != nil { + return err + } // Then platform-specific install flags. flags.StringVar(&conf.BridgeConfig.FixedCIDR, "fixed-cidr", "", "IPv4 subnet for fixed IPs") flags.StringVarP(&conf.BridgeConfig.Iface, "bridge", "b", "", "Attach containers to a virtual switch") flags.StringVarP(&conf.SocketGroup, "group", "G", "", "Users or groups that can access the named pipe") + return nil } diff --git a/cmd/dockerd/daemon.go b/cmd/dockerd/daemon.go index 2efa60b5c58db..2c9ae2951b916 100644 --- a/cmd/dockerd/daemon.go +++ b/cmd/dockerd/daemon.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "fmt" + "net" "os" "path/filepath" "runtime" @@ -11,7 +12,6 @@ import ( "time" containerddefaults "github.com/containerd/containerd/defaults" - "github.com/docker/distribution/uuid" "github.com/docker/docker/api" apiserver "github.com/docker/docker/api/server" buildbackend "github.com/docker/docker/api/server/backend/build" @@ -21,6 +21,7 @@ import ( checkpointrouter "github.com/docker/docker/api/server/router/checkpoint" "github.com/docker/docker/api/server/router/container" distributionrouter "github.com/docker/docker/api/server/router/distribution" + grpcrouter "github.com/docker/docker/api/server/router/grpc" "github.com/docker/docker/api/server/router/image" "github.com/docker/docker/api/server/router/network" pluginrouter "github.com/docker/docker/api/server/router/plugin" @@ -30,8 +31,8 @@ import ( "github.com/docker/docker/api/server/router/volume" buildkit "github.com/docker/docker/builder/builder-next" "github.com/docker/docker/builder/dockerfile" - "github.com/docker/docker/builder/fscache" "github.com/docker/docker/cli/debug" + "github.com/docker/docker/cmd/dockerd/trap" "github.com/docker/docker/daemon" "github.com/docker/docker/daemon/cluster" "github.com/docker/docker/daemon/config" @@ -40,12 +41,14 @@ import ( "github.com/docker/docker/libcontainerd/supervisor" dopts "github.com/docker/docker/opts" "github.com/docker/docker/pkg/authorization" + "github.com/docker/docker/pkg/homedir" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/pidfile" "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/docker/pkg/signal" + "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" "github.com/docker/docker/plugin" + "github.com/docker/docker/rootless" "github.com/docker/docker/runconfig" "github.com/docker/go-connections/tlsconfig" swarmapi "github.com/docker/swarmkit/api" @@ -72,17 +75,28 @@ func NewDaemonCli() *DaemonCli { } func (cli *DaemonCli) start(opts *daemonOptions) (err error) { - stopc := make(chan bool) - defer close(stopc) - - // warn from uuid package when running the daemon - uuid.Loggerf = logrus.Warnf - opts.SetDefaultOptions(opts.flags) if cli.Config, err = loadDaemonCliConfig(opts); err != nil { return err } + + if opts.Validate { + // If config wasn't OK we wouldn't have made it this far. + fmt.Fprintln(os.Stderr, "configuration OK") + return nil + } + + configureProxyEnv(cli.Config) + + warnOnDeprecatedConfigOptions(cli.Config) + + if err := configureDaemonLogs(cli.Config); err != nil { + return err + } + + logrus.Info("Starting up") + cli.configFile = &opts.configFile cli.flags = opts.flags @@ -94,13 +108,20 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { logrus.Warn("Running experimental build") } - logrus.SetFormatter(&logrus.TextFormatter{ - TimestampFormat: jsonmessage.RFC3339NanoFixed, - DisableColors: cli.Config.RawLogs, - FullTimestamp: true, - }) + if cli.Config.IsRootless() { + logrus.Warn("Running in rootless mode. This mode has feature limitations.") + } + if rootless.RunningWithRootlessKit() { + logrus.Info("Running with RootlessKit integration") + if !cli.Config.IsRootless() { + return fmt.Errorf("rootless mode needs to be enabled for running with RootlessKit") + } + } - system.InitLCOW(cli.Config.Experimental) + // return human-friendly error before creating files + if runtime.GOOS == "linux" && os.Geteuid() != 0 { + return fmt.Errorf("dockerd needs to be started with root privileges. To run dockerd in rootless mode as an unprivileged user, see https://docs.docker.com/go/rootless/") + } if err := setDefaultUmask(); err != nil { return err @@ -112,15 +133,18 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { return err } - if err := system.MkdirAll(cli.Config.ExecRoot, 0700, ""); err != nil { + if err := system.MkdirAll(cli.Config.ExecRoot, 0700); err != nil { return err } + potentiallyUnderRuntimeDir := []string{cli.Config.ExecRoot} + if cli.Pidfile != "" { pf, err := pidfile.New(cli.Pidfile) if err != nil { return errors.Wrap(err, "failed to start daemon") } + potentiallyUnderRuntimeDir = append(potentiallyUnderRuntimeDir, cli.Pidfile) defer func() { if err := pf.Remove(); err != nil { logrus.Error(err) @@ -128,6 +152,14 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { }() } + if cli.Config.IsRootless() { + // Set sticky bit if XDG_RUNTIME_DIR is set && the file is actually under XDG_RUNTIME_DIR + if _, err := homedir.StickRuntimeDirContents(potentiallyUnderRuntimeDir); err != nil { + // StickRuntimeDirContents returns nil error if XDG_RUNTIME_DIR is just unset + logrus.WithError(err).Warn("cannot set sticky bit on files under XDG_RUNTIME_DIR") + } + } + serverConfig, err := newAPIServerConfig(cli) if err != nil { return errors.Wrap(err, "failed to create API server") @@ -140,36 +172,26 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { } ctx, cancel := context.WithCancel(context.Background()) - if cli.Config.ContainerdAddr == "" && runtime.GOOS != "windows" { - if !systemContainerdRunning() { - opts, err := cli.getContainerdDaemonOpts() - if err != nil { - cancel() - return errors.Wrap(err, "failed to generate containerd options") - } - - r, err := supervisor.Start(ctx, filepath.Join(cli.Config.Root, "containerd"), filepath.Join(cli.Config.ExecRoot, "containerd"), opts...) - if err != nil { - cancel() - return errors.Wrap(err, "failed to start containerd") - } - cli.Config.ContainerdAddr = r.Address() - - // Try to wait for containerd to shutdown - defer r.WaitTimeout(10 * time.Second) - } else { - cli.Config.ContainerdAddr = containerddefaults.DefaultAddress - } + waitForContainerDShutdown, err := cli.initContainerD(ctx) + if waitForContainerDShutdown != nil { + defer waitForContainerDShutdown(10 * time.Second) + } + if err != nil { + cancel() + return err } defer cancel() - signal.Trap(func() { + stopc := make(chan bool) + defer close(stopc) + + trap.Trap(func() { cli.stop() <-stopc // wait for daemonCli.start() to return }, logrus.StandardLogger()) // Notify that the API is active, but before daemon is set up. - preNotifySystem() + preNotifyReady() pluginStore := plugin.NewStore() @@ -189,14 +211,10 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { return errors.Wrap(err, "failed to validate authorization plugin") } - // TODO: move into startMetricsServer() - if cli.Config.MetricsAddress != "" { - if !d.HasExperimental() { - return errors.Wrap(err, "metrics-addr is only supported when experimental is enabled") - } - if err := startMetricsServer(cli.Config.MetricsAddress); err != nil { - return err - } + cli.d = d + + if err := startMetricsServer(cli.Config.MetricsAddress); err != nil { + return errors.Wrap(err, "failed to start metrics server") } c, err := createAndStartCluster(cli, d) @@ -211,8 +229,6 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { logrus.Info("Daemon has completed initialization") - cli.d = d - routerOptions, err := newRouterOptions(cli.Config, d) if err != nil { return err @@ -233,13 +249,15 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { go cli.api.Wait(serveAPIWait) // after the daemon is done setting up we can notify systemd api - notifySystem() + notifyReady() // Daemon is fully initialized and handling API traffic // Wait for serve API to complete errAPI := <-serveAPIWait c.Cleanup() + // notify systemd that we're shutting down + notifyStopping() shutdownDaemon(d) // Stop notification processing and any background processes @@ -249,13 +267,13 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) { return errors.Wrap(errAPI, "shutting down due to ServeAPI error") } + logrus.Info("Daemon shutdown complete") return nil } type routerOptions struct { sessionManager *session.Manager buildBackend *buildbackend.Backend - buildCache *fscache.FSCache // legacy features *map[string]bool buildkit *buildkit.Builder daemon *daemon.Daemon @@ -270,21 +288,7 @@ func newRouterOptions(config *config.Config, d *daemon.Daemon) (routerOptions, e return opts, errors.Wrap(err, "failed to create sessionmanager") } - builderStateDir := filepath.Join(config.Root, "builder") - - buildCache, err := fscache.NewFSCache(fscache.Opt{ - Backend: fscache.NewNaiveCacheBackend(builderStateDir), - Root: builderStateDir, - GCPolicy: fscache.GCPolicy{ // TODO: expose this in config - MaxSize: 1024 * 1024 * 512, // 512MB - MaxKeepDuration: 7 * 24 * time.Hour, // 1 week - }, - }) - if err != nil { - return opts, errors.Wrap(err, "failed to create fscache") - } - - manager, err := dockerfile.NewBuildManager(d.BuilderBackend(), sm, buildCache, d.IdentityMapping()) + manager, err := dockerfile.NewBuildManager(d.BuilderBackend(), d.IdentityMapping()) if err != nil { return opts, err } @@ -295,21 +299,24 @@ func newRouterOptions(config *config.Config, d *daemon.Daemon) (routerOptions, e Dist: d.DistributionServices(), NetworkController: d.NetworkController(), DefaultCgroupParent: cgroupParent, - ResolverOpt: d.NewResolveOptionsFunc(), + RegistryHosts: d.RegistryHosts(), BuilderConfig: config.Builder, + Rootless: d.Rootless(), + IdentityMapping: d.IdentityMapping(), + DNSConfig: config.DNSConfig, + ApparmorProfile: daemon.DefaultApparmorProfile(), }) if err != nil { return opts, err } - bb, err := buildbackend.NewBackend(d.ImageService(), manager, buildCache, bk) + bb, err := buildbackend.NewBackend(d.ImageService(), manager, bk, d.EventsService) if err != nil { return opts, errors.Wrap(err, "failed to create buildmanager") } return routerOptions{ sessionManager: sm, buildBackend: bb, - buildCache: buildCache, buildkit: bk, features: d.Features(), daemon: d, @@ -326,19 +333,6 @@ func (cli *DaemonCli) reloadConfig() { } cli.authzMiddleware.SetPlugins(c.AuthorizationPlugins) - // The namespaces com.docker.*, io.docker.*, org.dockerproject.* have been documented - // to be reserved for Docker's internal use, but this was never enforced. Allowing - // configured labels to use these namespaces are deprecated for 18.05. - // - // The following will check the usage of such labels, and report a warning for deprecation. - // - // TODO: At the next stable release, the validation should be folded into the other - // configuration validation functions and an error will be returned instead, and this - // block should be deleted. - if err := config.ValidateReservedNamespaceLabels(c.Labels); err != nil { - logrus.Warnf("Configured labels using reserved namespaces is deprecated: %s", err) - } - if err := cli.d.Reload(c); err != nil { logrus.Errorf("Error reconfiguring the daemon: %v", err) return @@ -379,10 +373,14 @@ func shutdownDaemon(d *daemon.Daemon) { logrus.Debug("Clean shutdown succeeded") return } + + timeout := time.NewTimer(time.Duration(shutdownTimeout) * time.Second) + defer timeout.Stop() + select { case <-ch: logrus.Debug("Clean shutdown succeeded") - case <-time.After(time.Duration(shutdownTimeout) * time.Second): + case <-timeout.C: logrus.Error("Force shutdown daemon") } } @@ -393,8 +391,16 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) { conf.Debug = opts.Debug conf.Hosts = opts.Hosts conf.LogLevel = opts.LogLevel - conf.TLS = opts.TLS - conf.TLSVerify = opts.TLSVerify + + if opts.flags.Changed(FlagTLS) { + conf.TLS = &opts.TLS + } + if opts.flags.Changed(FlagTLSVerify) { + conf.TLSVerify = &opts.TLSVerify + v := true + conf.TLS = &v + } + conf.CommonTLSOptions = config.CommonTLSOptions{} if opts.TLSOptions != nil { @@ -404,9 +410,11 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) { } if conf.TrustKeyPath == "" { - conf.TrustKeyPath = filepath.Join( - getDaemonConfDir(conf.Root), - defaultTrustKeyFile) + daemonConfDir, err := getDaemonConfDir(conf.Root) + if err != nil { + return nil, err + } + conf.TrustKeyPath = filepath.Join(daemonConfDir, defaultTrustKeyFile) } if flags.Changed("graph") && flags.Changed("data-root") { @@ -420,6 +428,7 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) { return nil, errors.Wrapf(err, "unable to configure the Docker daemon with file %s", opts.configFile) } } + // the merged configuration can be nil if the config file didn't exist. // leave the current configuration as it is if when that happens. if c != nil { @@ -431,17 +440,6 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) { return nil, err } - if runtime.GOOS != "windows" { - if flags.Changed("disable-legacy-registry") { - // TODO: Remove this error after 3 release cycles (18.03) - return nil, errors.New("ERROR: The '--disable-legacy-registry' flag has been removed. Interacting with legacy (v1) registries is no longer supported") - } - if !conf.V2Only { - // TODO: Remove this error after 3 release cycles (18.03) - return nil, errors.New("ERROR: The 'disable-legacy-registry' configuration option has been removed. Interacting with legacy (v1) registries is no longer supported") - } - } - if flags.Changed("graph") { logrus.Warnf(`The "-g / --graph" flag is deprecated. Please use "--data-root" instead`) } @@ -451,41 +449,47 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) { if err != nil { return nil, err } - // The namespaces com.docker.*, io.docker.*, org.dockerproject.* have been documented - // to be reserved for Docker's internal use, but this was never enforced. Allowing - // configured labels to use these namespaces are deprecated for 18.05. - // - // The following will check the usage of such labels, and report a warning for deprecation. - // - // TODO: At the next stable release, the validation should be folded into the other - // configuration validation functions and an error will be returned instead, and this - // block should be deleted. - if err := config.ValidateReservedNamespaceLabels(newLabels); err != nil { - logrus.Warnf("Configured labels using reserved namespaces is deprecated: %s", err) - } conf.Labels = newLabels // Regardless of whether the user sets it to true or false, if they // specify TLSVerify at all then we need to turn on TLS if conf.IsValueSet(FlagTLSVerify) { - conf.TLS = true + v := true + conf.TLS = &v } - // ensure that the log level is the one set after merging configurations - setLogLevel(conf.LogLevel) + if conf.TLSVerify == nil && conf.TLS != nil { + conf.TLSVerify = conf.TLS + } return conf, nil } +func warnOnDeprecatedConfigOptions(config *config.Config) { + if config.ClusterAdvertise != "" { + logrus.Warn(`The "cluster-advertise" option is deprecated. To be removed soon.`) + } + if config.ClusterStore != "" { + logrus.Warn(`The "cluster-store" option is deprecated. To be removed soon.`) + } + if len(config.ClusterOpts) > 0 { + logrus.Warn(`The "cluster-store-opt" option is deprecated. To be removed soon.`) + } +} + func initRouter(opts routerOptions) { - decoder := runconfig.ContainerDecoder{} + decoder := runconfig.ContainerDecoder{ + GetSysInfo: func() *sysinfo.SysInfo { + return opts.daemon.RawSysInfo() + }, + } routers := []router.Router{ // we need to add the checkpoint router before the container router or the DELETE gets masked checkpointrouter.NewRouter(opts.daemon, decoder), - container.NewRouter(opts.daemon, decoder), + container.NewRouter(opts.daemon, decoder, opts.daemon.RawSysInfo().CgroupUnified), image.NewRouter(opts.daemon.ImageService()), - systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildCache, opts.buildkit, opts.features), + systemrouter.NewRouter(opts.daemon, opts.cluster, opts.buildkit, opts.features), volume.NewRouter(opts.daemon.VolumesService()), build.NewRouter(opts.buildBackend, opts.daemon, opts.features), sessionrouter.NewRouter(opts.sessionManager), @@ -494,6 +498,16 @@ func initRouter(opts routerOptions) { distributionrouter.NewRouter(opts.daemon.ImageService()), } + grpcBackends := []grpcrouter.Backend{} + for _, b := range []interface{}{opts.daemon, opts.buildBackend} { + if b, ok := b.(grpcrouter.Backend); ok { + grpcBackends = append(grpcBackends, b) + } + } + if len(grpcBackends) > 0 { + routers = append(routers, grpcrouter.NewRouter(grpcBackends...)) + } + if opts.daemon.NetworkControllerEnabled() { routers = append(routers, network.NewRouter(opts.daemon, opts.cluster)) } @@ -559,7 +573,7 @@ func newAPIServerConfig(cli *DaemonCli) (*apiserver.Config, error) { CorsHeaders: cli.Config.CorsHeaders, } - if cli.Config.TLS { + if cli.Config.TLS != nil && *cli.Config.TLS { tlsOptions := tlsconfig.Options{ CAFile: cli.Config.CommonTLSOptions.CAFile, CertFile: cli.Config.CommonTLSOptions.CertFile, @@ -567,7 +581,7 @@ func newAPIServerConfig(cli *DaemonCli) (*apiserver.Config, error) { ExclusiveRootPools: true, } - if cli.Config.TLSVerify { + if cli.Config.TLSVerify == nil || *cli.Config.TLSVerify { // server requires and verifies client's certificate tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert } @@ -585,13 +599,49 @@ func newAPIServerConfig(cli *DaemonCli) (*apiserver.Config, error) { return serverConfig, nil } +// checkTLSAuthOK checks basically for an explicitly disabled TLS/TLSVerify +// Going forward we do not want to support a scenario where dockerd listens +// on TCP without either TLS client auth (or an explicit opt-in to disable it) +func checkTLSAuthOK(c *config.Config) bool { + if c.TLS == nil { + // Either TLS is enabled by default, in which case TLS verification should be enabled by default, or explicitly disabled + // Or TLS is disabled by default... in any of these cases, we can just take the default value as to how to proceed + return DefaultTLSValue + } + + if !*c.TLS { + // TLS is explicitly disabled, which is supported + return true + } + + if c.TLSVerify == nil { + // this actually shouldn't happen since we set TLSVerify on the config object anyway + // But in case it does get here, be cautious and assume this is not supported. + return false + } + + // Either TLSVerify is explicitly enabled or disabled, both cases are supported + return true +} + func loadListeners(cli *DaemonCli, serverConfig *apiserver.Config) ([]string, error) { var hosts []string + seen := make(map[string]struct{}, len(cli.Config.Hosts)) + + useTLS := DefaultTLSValue + if cli.Config.TLS != nil { + useTLS = *cli.Config.TLS + } + for i := 0; i < len(cli.Config.Hosts); i++ { var err error - if cli.Config.Hosts[i], err = dopts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil { + if cli.Config.Hosts[i], err = dopts.ParseHost(useTLS, honorXDG, cli.Config.Hosts[i]); err != nil { return nil, errors.Wrapf(err, "error parsing -H %s", cli.Config.Hosts[i]) } + if _, ok := seen[cli.Config.Hosts[i]]; ok { + continue + } + seen[cli.Config.Hosts[i]] = struct{}{} protoAddr := cli.Config.Hosts[i] protoAddrParts := strings.SplitN(protoAddr, "://", 2) @@ -603,14 +653,48 @@ func loadListeners(cli *DaemonCli, serverConfig *apiserver.Config) ([]string, er addr := protoAddrParts[1] // It's a bad idea to bind to TCP without tlsverify. - if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) { - logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting --tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]") + authEnabled := serverConfig.TLSConfig != nil && serverConfig.TLSConfig.ClientAuth == tls.RequireAndVerifyClientCert + if proto == "tcp" && !authEnabled { + logrus.WithField("host", protoAddr).Warn("Binding to IP address without --tlsverify is insecure and gives root access on this machine to everyone who has access to your network.") + logrus.WithField("host", protoAddr).Warn("Binding to an IP address, even on localhost, can also give access to scripts run in a browser. Be safe out there!") + time.Sleep(time.Second) + + // If TLSVerify is explicitly set to false we'll take that as "Please let me shoot myself in the foot" + // We do not want to continue to support a default mode where tls verification is disabled, so we do some extra warnings here and eventually remove support + if !checkTLSAuthOK(cli.Config) { + ipAddr, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, errors.Wrap(err, "error parsing tcp address") + } + + // shortcut all this extra stuff for literal "localhost" + // -H supports specifying hostnames, since we want to bypass this on loopback interfaces we'll look it up here. + if ipAddr != "localhost" { + ip := net.ParseIP(ipAddr) + if ip == nil { + ipA, err := net.ResolveIPAddr("ip", ipAddr) + if err != nil { + logrus.WithError(err).WithField("host", ipAddr).Error("Error looking up specified host address") + } + if ipA != nil { + ip = ipA.IP + } + } + if ip == nil || !ip.IsLoopback() { + logrus.WithField("host", protoAddr).Warn("Binding to an IP address without --tlsverify is deprecated. Startup is intentionally being slowed down to show this message") + logrus.WithField("host", protoAddr).Warn("Please consider generating tls certificates with client validation to prevent exposing unauthenticated root access to your network") + logrus.WithField("host", protoAddr).Warnf("You can override this by explicitly specifying '--%s=false' or '--%s=false'", FlagTLS, FlagTLSVerify) + logrus.WithField("host", protoAddr).Warnf("Support for listening on TCP without authentication or explicit intent to run without authentication will be removed in the next release") + + time.Sleep(15 * time.Second) + } + } + } } ls, err := listeners.Init(proto, addr, serverConfig.SocketGroup, serverConfig.TLSConfig) if err != nil { return nil, err } - ls = wrapListeners(proto, ls) // If we're binding to a TCP port, make sure that a container doesn't try to use it. if proto == "tcp" { if err := allocateDaemonPort(addr); err != nil { @@ -666,7 +750,60 @@ func validateAuthzPlugins(requestedPlugins []string, pg plugingetter.PluginGette return nil } -func systemContainerdRunning() bool { - _, err := os.Lstat(containerddefaults.DefaultAddress) - return err == nil +func systemContainerdRunning(honorXDG bool) (string, bool, error) { + addr := containerddefaults.DefaultAddress + if honorXDG { + runtimeDir, err := homedir.GetRuntimeDir() + if err != nil { + return "", false, err + } + addr = filepath.Join(runtimeDir, "containerd", "containerd.sock") + } + _, err := os.Lstat(addr) + return addr, err == nil, nil +} + +// configureDaemonLogs sets the logrus logging level and formatting +func configureDaemonLogs(conf *config.Config) error { + if conf.LogLevel != "" { + lvl, err := logrus.ParseLevel(conf.LogLevel) + if err != nil { + return fmt.Errorf("unable to parse logging level: %s", conf.LogLevel) + } + logrus.SetLevel(lvl) + } else { + logrus.SetLevel(logrus.InfoLevel) + } + logrus.SetFormatter(&logrus.TextFormatter{ + TimestampFormat: jsonmessage.RFC3339NanoFixed, + DisableColors: conf.RawLogs, + FullTimestamp: true, + }) + return nil +} + +func configureProxyEnv(conf *config.Config) { + if p := conf.HTTPProxy; p != "" { + overrideProxyEnv("HTTP_PROXY", p) + overrideProxyEnv("http_proxy", p) + } + if p := conf.HTTPSProxy; p != "" { + overrideProxyEnv("HTTPS_PROXY", p) + overrideProxyEnv("https_proxy", p) + } + if p := conf.NoProxy; p != "" { + overrideProxyEnv("NO_PROXY", p) + overrideProxyEnv("no_proxy", p) + } +} + +func overrideProxyEnv(name, val string) { + if oldVal := os.Getenv(name); oldVal != "" && oldVal != val { + logrus.WithFields(logrus.Fields{ + "name": name, + "old-value": config.MaskCredentials(oldVal), + "new-value": config.MaskCredentials(val), + }).Warn("overriding existing proxy variable with value from configuration") + } + _ = os.Setenv(name, val) } diff --git a/cmd/dockerd/daemon_freebsd.go b/cmd/dockerd/daemon_freebsd.go index 6d013b8103990..1bb49047c71ab 100644 --- a/cmd/dockerd/daemon_freebsd.go +++ b/cmd/dockerd/daemon_freebsd.go @@ -1,9 +1,13 @@ package main -// preNotifySystem sends a message to the host when the API is active, but before the daemon is -func preNotifySystem() { +// preNotifyReady sends a message to the host when the API is active, but before the daemon is +func preNotifyReady() { } -// notifySystem sends a message to the host when the server is ready to be used -func notifySystem() { +// notifyReady sends a message to the host when the server is ready to be used +func notifyReady() { +} + +// notifyStopping sends a message to the host when the server is shutting down +func notifyStopping() { } diff --git a/cmd/dockerd/daemon_linux.go b/cmd/dockerd/daemon_linux.go index cf2d65275f625..aade57a8d3389 100644 --- a/cmd/dockerd/daemon_linux.go +++ b/cmd/dockerd/daemon_linux.go @@ -1,13 +1,18 @@ package main -import systemdDaemon "github.com/coreos/go-systemd/daemon" +import systemdDaemon "github.com/coreos/go-systemd/v22/daemon" -// preNotifySystem sends a message to the host when the API is active, but before the daemon is -func preNotifySystem() { +// preNotifyReady sends a message to the host when the API is active, but before the daemon is +func preNotifyReady() { } -// notifySystem sends a message to the host when the server is ready to be used -func notifySystem() { +// notifyReady sends a message to the host when the server is ready to be used +func notifyReady() { // Tell the init daemon we are accepting requests go systemdDaemon.SdNotify(false, systemdDaemon.SdNotifyReady) } + +// notifyStopping sends a message to the host when the server is shutting down +func notifyStopping() { + go systemdDaemon.SdNotify(false, systemdDaemon.SdNotifyStopping) +} diff --git a/cmd/dockerd/daemon_test.go b/cmd/dockerd/daemon_test.go index ad447e3b90b5d..7628f40c87fe5 100644 --- a/cmd/dockerd/daemon_test.go +++ b/cmd/dockerd/daemon_test.go @@ -6,23 +6,27 @@ import ( "github.com/docker/docker/daemon/config" "github.com/sirupsen/logrus" "github.com/spf13/pflag" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/fs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/fs" ) -func defaultOptions(configFile string) *daemonOptions { +func defaultOptions(t *testing.T, configFile string) *daemonOptions { opts := newDaemonOptions(&config.Config{}) opts.flags = &pflag.FlagSet{} opts.InstallFlags(opts.flags) - installConfigFlags(opts.daemonConfig, opts.flags) + if err := installConfigFlags(opts.daemonConfig, opts.flags); err != nil { + t.Fatal(err) + } + defaultDaemonConfigFile, err := getDefaultDaemonConfigFile() + assert.NilError(t, err) opts.flags.StringVar(&opts.configFile, "config-file", defaultDaemonConfigFile, "") opts.configFile = configFile return opts } func TestLoadDaemonCliConfigWithoutOverriding(t *testing.T) { - opts := defaultOptions("") + opts := defaultOptions(t, "") opts.Debug = true loadedConfig, err := loadDaemonCliConfig(opts) @@ -34,7 +38,7 @@ func TestLoadDaemonCliConfigWithoutOverriding(t *testing.T) { } func TestLoadDaemonCliConfigWithTLS(t *testing.T) { - opts := defaultOptions("") + opts := defaultOptions(t, "") opts.TLSOptions.CAFile = "/tmp/ca.pem" opts.TLS = true @@ -49,7 +53,7 @@ func TestLoadDaemonCliConfigWithConflicts(t *testing.T) { defer tempFile.Remove() configFile := tempFile.Path() - opts := defaultOptions(configFile) + opts := defaultOptions(t, configFile) flags := opts.flags assert.Check(t, flags.Set("config-file", configFile)) @@ -65,7 +69,7 @@ func TestLoadDaemonCliWithConflictingNodeGenericResources(t *testing.T) { defer tempFile.Remove() configFile := tempFile.Path() - opts := defaultOptions(configFile) + opts := defaultOptions(t, configFile) flags := opts.flags assert.Check(t, flags.Set("config-file", configFile)) @@ -77,7 +81,7 @@ func TestLoadDaemonCliWithConflictingNodeGenericResources(t *testing.T) { } func TestLoadDaemonCliWithConflictingLabels(t *testing.T) { - opts := defaultOptions("") + opts := defaultOptions(t, "") flags := opts.flags assert.Check(t, flags.Set("label", "foo=bar")) @@ -88,7 +92,7 @@ func TestLoadDaemonCliWithConflictingLabels(t *testing.T) { } func TestLoadDaemonCliWithDuplicateLabels(t *testing.T) { - opts := defaultOptions("") + opts := defaultOptions(t, "") flags := opts.flags assert.Check(t, flags.Set("label", "foo=the-same")) @@ -102,51 +106,50 @@ func TestLoadDaemonCliConfigWithTLSVerify(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(`{"tlsverify": true}`)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) opts.TLSOptions.CAFile = "/tmp/ca.pem" loadedConfig, err := loadDaemonCliConfig(opts) assert.NilError(t, err) assert.Assert(t, loadedConfig != nil) - assert.Check(t, is.Equal(loadedConfig.TLS, true)) + assert.Check(t, is.Equal(*loadedConfig.TLS, true)) } func TestLoadDaemonCliConfigWithExplicitTLSVerifyFalse(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(`{"tlsverify": false}`)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) opts.TLSOptions.CAFile = "/tmp/ca.pem" loadedConfig, err := loadDaemonCliConfig(opts) assert.NilError(t, err) assert.Assert(t, loadedConfig != nil) - assert.Check(t, loadedConfig.TLS) + assert.Check(t, *loadedConfig.TLS) } func TestLoadDaemonCliConfigWithoutTLSVerify(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(`{}`)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) opts.TLSOptions.CAFile = "/tmp/ca.pem" loadedConfig, err := loadDaemonCliConfig(opts) assert.NilError(t, err) assert.Assert(t, loadedConfig != nil) - assert.Check(t, !loadedConfig.TLS) + assert.Check(t, loadedConfig.TLS == nil) } func TestLoadDaemonCliConfigWithLogLevel(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(`{"log-level": "warn"}`)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) loadedConfig, err := loadDaemonCliConfig(opts) assert.NilError(t, err) assert.Assert(t, loadedConfig != nil) assert.Check(t, is.Equal("warn", loadedConfig.LogLevel)) - assert.Check(t, is.Equal(logrus.WarnLevel, logrus.GetLevel())) } func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) { @@ -154,7 +157,7 @@ func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(content)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) loadedConfig, err := loadDaemonCliConfig(opts) assert.NilError(t, err) assert.Assert(t, loadedConfig != nil) @@ -164,14 +167,14 @@ func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) { func TestLoadDaemonConfigWithRegistryOptions(t *testing.T) { content := `{ - "allow-nondistributable-artifacts": ["allow-nondistributable-artifacts.com"], - "registry-mirrors": ["https://mirrors.docker.com"], - "insecure-registries": ["https://insecure.docker.com"] + "allow-nondistributable-artifacts": ["allow-nondistributable-artifacts.example.com"], + "registry-mirrors": ["https://mirrors.example.com"], + "insecure-registries": ["https://insecure-registry.example.com"] }` tempFile := fs.NewFile(t, "config", fs.WithContent(content)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) loadedConfig, err := loadDaemonCliConfig(opts) assert.NilError(t, err) assert.Assert(t, loadedConfig != nil) @@ -180,3 +183,22 @@ func TestLoadDaemonConfigWithRegistryOptions(t *testing.T) { assert.Check(t, is.Len(loadedConfig.Mirrors, 1)) assert.Check(t, is.Len(loadedConfig.InsecureRegistries, 1)) } + +func TestConfigureDaemonLogs(t *testing.T) { + conf := &config.Config{} + err := configureDaemonLogs(conf) + assert.NilError(t, err) + assert.Check(t, is.Equal(logrus.InfoLevel, logrus.GetLevel())) + + conf.LogLevel = "warn" + err = configureDaemonLogs(conf) + assert.NilError(t, err) + assert.Check(t, is.Equal(logrus.WarnLevel, logrus.GetLevel())) + + conf.LogLevel = "foobar" + err = configureDaemonLogs(conf) + assert.Error(t, err, "unable to parse logging level: foobar") + + // log level should not be changed after a failure + assert.Check(t, is.Equal(logrus.WarnLevel, logrus.GetLevel())) +} diff --git a/cmd/dockerd/daemon_unix.go b/cmd/dockerd/daemon_unix.go index 7b03e28594b2d..89e74ee8b6949 100644 --- a/cmd/dockerd/daemon_unix.go +++ b/cmd/dockerd/daemon_unix.go @@ -1,25 +1,50 @@ +//go:build !windows // +build !windows package main import ( + "context" "fmt" "net" "os" "os/signal" "path/filepath" "strconv" + "time" "github.com/containerd/containerd/runtime/v1/linux" - "github.com/docker/docker/cmd/dockerd/hack" "github.com/docker/docker/daemon" "github.com/docker/docker/daemon/config" "github.com/docker/docker/libcontainerd/supervisor" - "github.com/docker/libnetwork/portallocator" + "github.com/docker/docker/libnetwork/portallocator" + "github.com/docker/docker/pkg/homedir" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) -const defaultDaemonConfigFile = "/etc/docker/daemon.json" +func getDefaultDaemonConfigDir() (string, error) { + if !honorXDG { + return "/etc/docker", nil + } + // NOTE: CLI uses ~/.docker while the daemon uses ~/.config/docker, because + // ~/.docker was not designed to store daemon configurations. + // In future, the daemon directory may be renamed to ~/.config/moby-engine (?). + configHome, err := homedir.GetConfigHome() + if err != nil { + return "", nil + } + return filepath.Join(configHome, "docker"), nil +} + +func getDefaultDaemonConfigFile() (string, error) { + dir, err := getDefaultDaemonConfigDir() + if err != nil { + return "", err + } + return filepath.Join(dir, "daemon.json"), nil +} // setDefaultUmask sets the umask to 0022 to avoid problems // caused by custom umask @@ -33,16 +58,16 @@ func setDefaultUmask() error { return nil } -func getDaemonConfDir(_ string) string { - return "/etc/docker" +func getDaemonConfDir(_ string) (string, error) { + return getDefaultDaemonConfigDir() } func (cli *DaemonCli) getPlatformContainerdDaemonOpts() ([]supervisor.DaemonOpt, error) { opts := []supervisor.DaemonOpt{ supervisor.WithOOMScore(cli.Config.OOMScoreAdjust), supervisor.WithPlugin("linux", &linux.Config{ - Shim: daemon.DefaultShimBinary, - Runtime: daemon.DefaultRuntimeBinary, + Shim: config.DefaultShimBinary, + Runtime: config.DefaultRuntimeBinary, RuntimeRoot: filepath.Join(cli.Config.Root, "runc"), ShimDebug: cli.Config.Debug, }), @@ -51,7 +76,7 @@ func (cli *DaemonCli) getPlatformContainerdDaemonOpts() ([]supervisor.DaemonOpt, return opts, nil } -// setupConfigReloadTrap configures the USR2 signal to reload the configuration. +// setupConfigReloadTrap configures the SIGHUP signal to reload the configuration. func (cli *DaemonCli) setupConfigReloadTrap() { c := make(chan os.Signal, 1) signal.Notify(c, unix.SIGHUP) @@ -97,18 +122,6 @@ func allocateDaemonPort(addr string) error { return nil } -func wrapListeners(proto string, ls []net.Listener) []net.Listener { - switch proto { - case "unix": - ls[0] = &hack.MalformedHostHeaderOverride{Listener: ls[0]} - case "fd": - for i := range ls { - ls[i] = &hack.MalformedHostHeaderOverride{Listener: ls[i]} - } - } - return ls -} - func newCgroupParent(config *config.Config) string { cgroupParent := "docker" useSystemd := daemon.UsingSystemd(config) @@ -123,3 +136,34 @@ func newCgroupParent(config *config.Config) string { } return cgroupParent } + +func (cli *DaemonCli) initContainerD(ctx context.Context) (func(time.Duration) error, error) { + var waitForShutdown func(time.Duration) error + if cli.Config.ContainerdAddr == "" { + systemContainerdAddr, ok, err := systemContainerdRunning(honorXDG) + if err != nil { + return nil, errors.Wrap(err, "could not determine whether the system containerd is running") + } + if !ok { + logrus.Debug("Containerd not running, starting daemon managed containerd") + opts, err := cli.getContainerdDaemonOpts() + if err != nil { + return nil, errors.Wrap(err, "failed to generate containerd options") + } + + r, err := supervisor.Start(ctx, filepath.Join(cli.Config.Root, "containerd"), filepath.Join(cli.Config.ExecRoot, "containerd"), opts...) + if err != nil { + return nil, errors.Wrap(err, "failed to start containerd") + } + logrus.Debug("Started daemon managed containerd") + cli.Config.ContainerdAddr = r.Address() + + // Try to wait for containerd to shutdown + waitForShutdown = r.WaitTimeout + } else { + cli.Config.ContainerdAddr = systemContainerdAddr + } + } + + return waitForShutdown, nil +} diff --git a/cmd/dockerd/daemon_unix_test.go b/cmd/dockerd/daemon_unix_test.go index 692d0328c4167..4ead3d2d3d540 100644 --- a/cmd/dockerd/daemon_unix_test.go +++ b/cmd/dockerd/daemon_unix_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package main @@ -6,9 +7,9 @@ import ( "testing" "github.com/docker/docker/daemon/config" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/fs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/fs" ) func TestLoadDaemonCliConfigWithDaemonFlags(t *testing.T) { @@ -16,7 +17,7 @@ func TestLoadDaemonCliConfigWithDaemonFlags(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(content)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) opts.Debug = true opts.LogLevel = "info" assert.Check(t, opts.flags.Set("selinux-enabled", "true")) @@ -37,7 +38,7 @@ func TestLoadDaemonConfigWithNetwork(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(content)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) loadedConfig, err := loadDaemonCliConfig(opts) assert.NilError(t, err) assert.Assert(t, loadedConfig != nil) @@ -54,7 +55,7 @@ func TestLoadDaemonConfigWithMapOptions(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(content)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) loadedConfig, err := loadDaemonCliConfig(opts) assert.NilError(t, err) assert.Assert(t, loadedConfig != nil) @@ -71,7 +72,7 @@ func TestLoadDaemonConfigWithTrueDefaultValues(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(content)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) loadedConfig, err := loadDaemonCliConfig(opts) assert.NilError(t, err) assert.Assert(t, loadedConfig != nil) @@ -90,7 +91,7 @@ func TestLoadDaemonConfigWithTrueDefaultValuesLeaveDefaults(t *testing.T) { tempFile := fs.NewFile(t, "config", fs.WithContent(`{}`)) defer tempFile.Remove() - opts := defaultOptions(tempFile.Path()) + opts := defaultOptions(t, tempFile.Path()) loadedConfig, err := loadDaemonCliConfig(opts) assert.NilError(t, err) assert.Assert(t, loadedConfig != nil) diff --git a/cmd/dockerd/daemon_windows.go b/cmd/dockerd/daemon_windows.go index 3b9ed9551f48b..ef06b0eb6da10 100644 --- a/cmd/dockerd/daemon_windows.go +++ b/cmd/dockerd/daemon_windows.go @@ -1,30 +1,34 @@ package main import ( + "context" "fmt" - "net" "os" "path/filepath" + "time" "github.com/docker/docker/daemon/config" "github.com/docker/docker/libcontainerd/supervisor" + "github.com/docker/docker/pkg/system" "github.com/sirupsen/logrus" "golang.org/x/sys/windows" ) -var defaultDaemonConfigFile = "" +func getDefaultDaemonConfigFile() (string, error) { + return "", nil +} // setDefaultUmask doesn't do anything on windows func setDefaultUmask() error { return nil } -func getDaemonConfDir(root string) string { - return filepath.Join(root, `\config`) +func getDaemonConfDir(root string) (string, error) { + return filepath.Join(root, `\config`), nil } -// preNotifySystem sends a message to the host when the API is active, but before the daemon is -func preNotifySystem() { +// preNotifyReady sends a message to the host when the API is active, but before the daemon is +func preNotifyReady() { // start the service now to prevent timeouts waiting for daemon to start // but still (eventually) complete all requests that are sent after this if service != nil { @@ -35,8 +39,12 @@ func preNotifySystem() { } } -// notifySystem sends a message to the host when the server is ready to be used -func notifySystem() { +// notifyReady sends a message to the host when the server is ready to be used +func notifyReady() { +} + +// notifyStopping sends a message to the host when the server is shutting down +func notifyStopping() { } // notifyShutdown is called after the daemon shuts down but before the process exits. @@ -81,10 +89,11 @@ func allocateDaemonPort(addr string) error { return nil } -func wrapListeners(proto string, ls []net.Listener) []net.Listener { - return ls -} - func newCgroupParent(config *config.Config) string { return "" } + +func (cli *DaemonCli) initContainerD(_ context.Context) (func(time.Duration) error, error) { + system.InitContainerdRuntime(cli.Config.ContainerdAddr) + return nil, nil +} diff --git a/cmd/dockerd/docker.go b/cmd/dockerd/docker.go index 197bb49c92e12..647da5a4dcafa 100644 --- a/cmd/dockerd/docker.go +++ b/cmd/dockerd/docker.go @@ -3,19 +3,24 @@ package main import ( "fmt" "os" - "runtime" "github.com/docker/docker/cli" "github.com/docker/docker/daemon/config" "github.com/docker/docker/dockerversion" + "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/reexec" - "github.com/docker/docker/pkg/term" + "github.com/docker/docker/rootless" "github.com/moby/buildkit/util/apicaps" + "github.com/moby/term" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) -func newDaemonCommand() *cobra.Command { +var ( + honorXDG bool +) + +func newDaemonCommand() (*cobra.Command, error) { opts := newDaemonOptions(config.New()) cmd := &cobra.Command{ @@ -35,18 +40,32 @@ func newDaemonCommand() *cobra.Command { flags := cmd.Flags() flags.BoolP("version", "v", false, "Print version information and quit") + defaultDaemonConfigFile, err := getDefaultDaemonConfigFile() + if err != nil { + return nil, err + } flags.StringVar(&opts.configFile, "config-file", defaultDaemonConfigFile, "Daemon configuration file") opts.InstallFlags(flags) - installConfigFlags(opts.daemonConfig, flags) + if err := installConfigFlags(opts.daemonConfig, flags); err != nil { + return nil, err + } installServiceFlags(flags) - return cmd + return cmd, nil } func init() { if dockerversion.ProductName != "" { apicaps.ExportedProduct = dockerversion.ProductName } + // When running with RootlessKit, $XDG_RUNTIME_DIR, $XDG_DATA_HOME, and $XDG_CONFIG_HOME needs to be + // honored as the default dirs, because we are unlikely to have permissions to access the system-wide + // directories. + // + // Note that even running with --rootless, when not running with RootlessKit, honorXDG needs to be kept false, + // because the system-wide directories in the current mount namespace are expected to be accessible. + // ("rootful" dockerd in rootless dockerd, #38702) + honorXDG = rootless.RunningWithRootlessKit() } func main() { @@ -54,21 +73,28 @@ func main() { return } + // initial log formatting; this setting is updated after the daemon configuration is loaded. + logrus.SetFormatter(&logrus.TextFormatter{ + TimestampFormat: jsonmessage.RFC3339NanoFixed, + FullTimestamp: true, + }) + // Set terminal emulation based on platform as required. _, stdout, stderr := term.StdStreams() - // @jhowardmsft - maybe there is a historic reason why on non-Windows, stderr is used - // here. However, on Windows it makes no sense and there is no need. - if runtime.GOOS == "windows" { - logrus.SetOutput(stdout) - } else { - logrus.SetOutput(stderr) - } + initLogging(stdout, stderr) - cmd := newDaemonCommand() - cmd.SetOutput(stdout) - if err := cmd.Execute(); err != nil { + onError := func(err error) { fmt.Fprintf(stderr, "%s\n", err) os.Exit(1) } + + cmd, err := newDaemonCommand() + if err != nil { + onError(err) + } + cmd.SetOut(stdout) + if err := cmd.Execute(); err != nil { + onError(err) + } } diff --git a/cmd/dockerd/docker_unix.go b/cmd/dockerd/docker_unix.go index 0dec48663d536..b7e30350ea67a 100644 --- a/cmd/dockerd/docker_unix.go +++ b/cmd/dockerd/docker_unix.go @@ -1,8 +1,19 @@ +//go:build !windows // +build !windows package main +import ( + "io" + + "github.com/sirupsen/logrus" +) + func runDaemon(opts *daemonOptions) error { daemonCli := NewDaemonCli() return daemonCli.start(opts) } + +func initLogging(_, stderr io.Writer) { + logrus.SetOutput(stderr) +} diff --git a/cmd/dockerd/docker_windows.go b/cmd/dockerd/docker_windows.go index bd8bc5a58eb56..a9e942a38bc05 100644 --- a/cmd/dockerd/docker_windows.go +++ b/cmd/dockerd/docker_windows.go @@ -1,8 +1,10 @@ package main import ( + "io" "path/filepath" + "github.com/Microsoft/go-winio/pkg/etwlogrus" _ "github.com/docker/docker/autogen/winresources/dockerd" "github.com/sirupsen/logrus" ) @@ -36,3 +38,17 @@ func runDaemon(opts *daemonOptions) error { notifyShutdown(err) return err } + +func initLogging(stdout, _ io.Writer) { + // Maybe there is a historic reason why on non-Windows, stderr is used + // for output. However, on Windows it makes no sense and there is no need. + logrus.SetOutput(stdout) + + // Provider ID: {6996f090-c5de-5082-a81e-5841acc3a635} + // Hook isn't closed explicitly, as it will exist until process exit. + // GUID is generated based on name - see Microsoft/go-winio/tools/etw-provider-gen. + if hook, err := etwlogrus.NewHook("Moby"); err == nil { + logrus.AddHook(hook) + } + return +} diff --git a/cmd/dockerd/hack/malformed_host_override.go b/cmd/dockerd/hack/malformed_host_override.go deleted file mode 100644 index ddd5eb9d8be91..0000000000000 --- a/cmd/dockerd/hack/malformed_host_override.go +++ /dev/null @@ -1,121 +0,0 @@ -// +build !windows - -package hack // import "github.com/docker/docker/cmd/dockerd/hack" - -import "net" - -// MalformedHostHeaderOverride is a wrapper to be able -// to overcome the 400 Bad request coming from old docker -// clients that send an invalid Host header. -type MalformedHostHeaderOverride struct { - net.Listener -} - -// MalformedHostHeaderOverrideConn wraps the underlying unix -// connection and keeps track of the first read from http.Server -// which just reads the headers. -type MalformedHostHeaderOverrideConn struct { - net.Conn - first bool -} - -var closeConnHeader = []byte("\r\nConnection: close\r") - -// Read reads the first *read* request from http.Server to inspect -// the Host header. If the Host starts with / then we're talking to -// an old docker client which send an invalid Host header. To not -// error out in http.Server we rewrite the first bytes of the request -// to sanitize the Host header itself. -// In case we're not dealing with old docker clients the data is just passed -// to the server w/o modification. -func (l *MalformedHostHeaderOverrideConn) Read(b []byte) (n int, err error) { - // http.Server uses a 4k buffer - if l.first && len(b) == 4096 { - // This keeps track of the first read from http.Server which just reads - // the headers - l.first = false - // The first read of the connection by http.Server is done limited to - // DefaultMaxHeaderBytes (usually 1 << 20) + 4096. - // Here we do the first read which gets us all the http headers to - // be inspected and modified below. - c, err := l.Conn.Read(b) - if err != nil { - return c, err - } - - var ( - start, end int - firstLineFeed = -1 - buf []byte - ) - for i := 0; i <= c-1-7; i++ { - if b[i] == '\n' && firstLineFeed == -1 { - firstLineFeed = i - } - if b[i] != '\n' { - continue - } - - if b[i+1] == '\r' && b[i+2] == '\n' { - return c, nil - } - - if b[i+1] != 'H' { - continue - } - if b[i+2] != 'o' { - continue - } - if b[i+3] != 's' { - continue - } - if b[i+4] != 't' { - continue - } - if b[i+5] != ':' { - continue - } - if b[i+6] != ' ' { - continue - } - if b[i+7] != '/' { - continue - } - // ensure clients other than the docker clients do not get this hack - if i != firstLineFeed { - return c, nil - } - start = i + 7 - // now find where the value ends - for ii, bbb := range b[start:c] { - if bbb == '\n' { - end = start + ii - break - } - } - buf = make([]byte, 0, c+len(closeConnHeader)-(end-start)) - // strip the value of the host header and - // inject `Connection: close` to ensure we don't reuse this connection - buf = append(buf, b[:start]...) - buf = append(buf, closeConnHeader...) - buf = append(buf, b[end:c]...) - copy(b, buf) - break - } - if len(buf) == 0 { - return c, nil - } - return len(buf), nil - } - return l.Conn.Read(b) -} - -// Accept makes the listener accepts connections and wraps the connection -// in a MalformedHostHeaderOverrideConn initializing first to true. -func (l *MalformedHostHeaderOverride) Accept() (net.Conn, error) { - c, err := l.Listener.Accept() - if err != nil { - return c, err - } - return &MalformedHostHeaderOverrideConn{c, true}, nil -} diff --git a/cmd/dockerd/hack/malformed_host_override_test.go b/cmd/dockerd/hack/malformed_host_override_test.go deleted file mode 100644 index 6874b059bea87..0000000000000 --- a/cmd/dockerd/hack/malformed_host_override_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// +build !windows - -package hack // import "github.com/docker/docker/cmd/dockerd/hack" - -import ( - "bytes" - "io" - "net" - "strings" - "testing" -) - -type bufConn struct { - net.Conn - buf *bytes.Buffer -} - -func (bc *bufConn) Read(b []byte) (int, error) { - return bc.buf.Read(b) -} - -func TestHeaderOverrideHack(t *testing.T) { - tests := [][2][]byte{ - { - []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"), - []byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\n"), - }, - { - []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\nFoo: Bar\r\n"), - []byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\nFoo: Bar\r\n"), - }, - { - []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\ntest something!"), - []byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\ntest something!"), - }, - { - []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\ntest something! " + strings.Repeat("test", 15000)), - []byte("GET /foo\nHost: \r\nConnection: close\r\nUser-Agent: Docker\r\n\r\ntest something! " + strings.Repeat("test", 15000)), - }, - { - []byte("GET /foo\nFoo: Bar\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"), - []byte("GET /foo\nFoo: Bar\nHost: /var/run/docker.sock\nUser-Agent: Docker\r\n\r\n"), - }, - } - - // Test for https://github.com/docker/docker/issues/23045 - h0 := "GET /foo\nUser-Agent: Docker\r\n\r\n" - h0 = h0 + strings.Repeat("a", 4096-len(h0)-1) + "\n" - tests = append(tests, [2][]byte{[]byte(h0), []byte(h0)}) - - for _, pair := range tests { - read := make([]byte, 4096) - client := &bufConn{ - buf: bytes.NewBuffer(pair[0]), - } - l := MalformedHostHeaderOverrideConn{client, true} - - n, err := l.Read(read) - if err != nil && err != io.EOF { - t.Fatalf("read: %d - %d, err: %v\n%s", n, len(pair[0]), err, string(read[:n])) - } - if !bytes.Equal(read[:n], pair[1][:n]) { - t.Fatalf("\n%s\n%s\n", read[:n], pair[1][:n]) - } - } -} - -func BenchmarkWithHack(b *testing.B) { - client, srv := net.Pipe() - done := make(chan struct{}) - req := []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\n") - read := make([]byte, 4096) - b.SetBytes(int64(len(req) * 30)) - - l := MalformedHostHeaderOverrideConn{client, true} - go func() { - for { - if _, err := srv.Write(req); err != nil { - srv.Close() - break - } - l.first = true // make sure each subsequent run uses the hack parsing - } - close(done) - }() - - for i := 0; i < b.N; i++ { - for i := 0; i < 30; i++ { - if n, err := l.Read(read); err != nil && err != io.EOF { - b.Fatalf("read: %d - %d, err: %v\n%s", n, len(req), err, string(read[:n])) - } - } - } - l.Close() - <-done -} - -func BenchmarkNoHack(b *testing.B) { - client, srv := net.Pipe() - done := make(chan struct{}) - req := []byte("GET /foo\nHost: /var/run/docker.sock\nUser-Agent: Docker\n") - read := make([]byte, 4096) - b.SetBytes(int64(len(req) * 30)) - - go func() { - for { - if _, err := srv.Write(req); err != nil { - srv.Close() - break - } - } - close(done) - }() - - for i := 0; i < b.N; i++ { - for i := 0; i < 30; i++ { - if _, err := client.Read(read); err != nil && err != io.EOF { - b.Fatal(err) - } - } - } - client.Close() - <-done -} diff --git a/cmd/dockerd/metrics.go b/cmd/dockerd/metrics.go index 20ceaf8466612..4ea8321b5d7a7 100644 --- a/cmd/dockerd/metrics.go +++ b/cmd/dockerd/metrics.go @@ -3,12 +3,16 @@ package main import ( "net" "net/http" + "strings" - "github.com/docker/go-metrics" + metrics "github.com/docker/go-metrics" "github.com/sirupsen/logrus" ) func startMetricsServer(addr string) error { + if addr == "" { + return nil + } if err := allocateDaemonPort(addr); err != nil { return err } @@ -19,8 +23,9 @@ func startMetricsServer(addr string) error { mux := http.NewServeMux() mux.Handle("/metrics", metrics.Handler()) go func() { - if err := http.Serve(l, mux); err != nil { - logrus.Errorf("serve metrics api: %s", err) + logrus.Infof("metrics API listening on %s", l.Addr()) + if err := http.Serve(l, mux); err != nil && !strings.Contains(err.Error(), "use of closed network connection") { + logrus.WithError(err).Error("error serving metrics API") } }() return nil diff --git a/cmd/dockerd/options.go b/cmd/dockerd/options.go index a6276add59c44..56f00059632a6 100644 --- a/cmd/dockerd/options.go +++ b/cmd/dockerd/options.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" "path/filepath" @@ -9,7 +8,6 @@ import ( "github.com/docker/docker/daemon/config" "github.com/docker/docker/opts" "github.com/docker/go-connections/tlsconfig" - "github.com/sirupsen/logrus" "github.com/spf13/pflag" ) @@ -22,6 +20,10 @@ const ( DefaultCertFile = "cert.pem" // FlagTLSVerify is the flag name for the TLS verification option FlagTLSVerify = "tlsverify" + // FlagTLS is the flag name for the TLS option + FlagTLS = "tls" + // DefaultTLSValue is the default value used for setting the tls option for tcp connections + DefaultTLSValue = false ) var ( @@ -39,6 +41,7 @@ type daemonOptions struct { TLS bool TLSVerify bool TLSOptions *tlsconfig.Options + Validate bool } // newDaemonOptions returns a new daemonFlags @@ -51,13 +54,16 @@ func newDaemonOptions(config *config.Config) *daemonOptions { // InstallFlags adds flags for the common options on the FlagSet func (o *daemonOptions) InstallFlags(flags *pflag.FlagSet) { if dockerCertPath == "" { + // cliconfig.Dir returns $DOCKER_CONFIG or ~/.docker. + // cliconfig.Dir does not look up $XDG_CONFIG_HOME dockerCertPath = cliconfig.Dir() } flags.BoolVarP(&o.Debug, "debug", "D", false, "Enable debug mode") + flags.BoolVar(&o.Validate, "validate", false, "Validate daemon configuration and exit") flags.StringVarP(&o.LogLevel, "log-level", "l", "info", `Set the logging level ("debug"|"info"|"warn"|"error"|"fatal")`) - flags.BoolVar(&o.TLS, "tls", false, "Use TLS; implied by --tlsverify") - flags.BoolVar(&o.TLSVerify, FlagTLSVerify, dockerTLSVerify, "Use TLS and verify the remote") + flags.BoolVar(&o.TLS, FlagTLS, DefaultTLSValue, "Use TLS; implied by --tlsverify") + flags.BoolVar(&o.TLSVerify, FlagTLSVerify, dockerTLSVerify || DefaultTLSValue, "Use TLS and verify the remote") // TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file") @@ -86,6 +92,11 @@ func (o *daemonOptions) SetDefaultOptions(flags *pflag.FlagSet) { o.TLS = true } + if o.TLS && !flags.Changed(FlagTLSVerify) { + // Enable tls verification unless explicitly disabled + o.TLSVerify = true + } + if !o.TLS { o.TLSOptions = nil } else { @@ -106,17 +117,3 @@ func (o *daemonOptions) SetDefaultOptions(flags *pflag.FlagSet) { } } } - -// setLogLevel sets the logrus logging level -func setLogLevel(logLevel string) { - if logLevel != "" { - lvl, err := logrus.ParseLevel(logLevel) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", logLevel) - os.Exit(1) - } - logrus.SetLevel(lvl) - } else { - logrus.SetLevel(logrus.InfoLevel) - } -} diff --git a/cmd/dockerd/options_test.go b/cmd/dockerd/options_test.go index 691118f08fbe1..604a902bb52d1 100644 --- a/cmd/dockerd/options_test.go +++ b/cmd/dockerd/options_test.go @@ -7,8 +7,8 @@ import ( cliconfig "github.com/docker/docker/cli/config" "github.com/docker/docker/daemon/config" "github.com/spf13/pflag" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestCommonOptionsInstallFlags(t *testing.T) { diff --git a/cmd/dockerd/service_unsupported.go b/cmd/dockerd/service_unsupported.go index bbcb7f3f3b954..907fd6ebcafe6 100644 --- a/cmd/dockerd/service_unsupported.go +++ b/cmd/dockerd/service_unsupported.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package main diff --git a/cmd/dockerd/service_windows.go b/cmd/dockerd/service_windows.go index 00432af643160..e41906c17f0b3 100644 --- a/cmd/dockerd/service_windows.go +++ b/cmd/dockerd/service_windows.go @@ -4,7 +4,7 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" + "io" "log" "os" "os/exec" @@ -12,7 +12,7 @@ import ( "time" "unsafe" - "github.com/docker/docker/pkg/system" + "github.com/Microsoft/hcsshim/osversion" "github.com/sirupsen/logrus" "github.com/spf13/pflag" "golang.org/x/sys/windows" @@ -52,7 +52,7 @@ func installServiceFlags(flags *pflag.FlagSet) { flRegisterService = flags.Bool("register-service", false, "Register the service and exit") flUnregisterService = flags.Bool("unregister-service", false, "Unregister the service and exit") flRunService = flags.Bool("run-service", false, "") - flags.MarkHidden("run-service") + _ = flags.MarkHidden("run-service") } type handler struct { @@ -171,7 +171,7 @@ func registerService() error { // This dependency is required on build 14393 (RS1) // it is added to the platform in newer builds - if system.GetOSVersion().Build == 14393 { + if osversion.Build() == osversion.RS1 { depends = append(depends, "ConDrv") } @@ -293,7 +293,7 @@ func initService(daemonCli *DaemonCli) (bool, bool, error) { } logrus.AddHook(&etwHook{log}) - logrus.SetOutput(ioutil.Discard) + logrus.SetOutput(io.Discard) service = h go func() { @@ -372,7 +372,7 @@ Loop: func initPanicFile(path string) error { var err error - panicFile, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0) + panicFile, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o200) if err != nil { return err } @@ -396,8 +396,8 @@ func initPanicFile(path string) error { // Update STD_ERROR_HANDLE to point to the panic file so that Go writes to // it when it panics. Remember the old stderr to restore it before removing // the panic file. - sh := windows.STD_ERROR_HANDLE - h, err := windows.GetStdHandle(uint32(sh)) + sh := uint32(windows.STD_ERROR_HANDLE) + h, err := windows.GetStdHandle(sh) if err != nil { return err } @@ -421,7 +421,7 @@ func initPanicFile(path string) error { func removePanicFile() { if st, err := panicFile.Stat(); err == nil { if st.Size() == 0 { - sh := windows.STD_ERROR_HANDLE + sh := uint32(windows.STD_ERROR_HANDLE) setStdHandle.Call(uintptr(sh), uintptr(oldStderr)) panicFile.Close() os.Remove(panicFile.Name()) diff --git a/cmd/dockerd/trap/testfiles/main.go b/cmd/dockerd/trap/testfiles/main.go new file mode 100644 index 0000000000000..22cb60bd00952 --- /dev/null +++ b/cmd/dockerd/trap/testfiles/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "os" + "syscall" + "time" + + "github.com/docker/docker/cmd/dockerd/trap" + "github.com/sirupsen/logrus" +) + +func main() { + sigmap := map[string]os.Signal{ + "TERM": syscall.SIGTERM, + "QUIT": syscall.SIGQUIT, + "INT": os.Interrupt, + } + trap.Trap(func() { + time.Sleep(time.Second) + os.Exit(99) + }, logrus.StandardLogger()) + go func() { + p, err := os.FindProcess(os.Getpid()) + if err != nil { + panic(err) + } + s := os.Getenv("SIGNAL_TYPE") + multiple := os.Getenv("IF_MULTIPLE") + switch s { + case "TERM", "INT": + if multiple == "1" { + for { + p.Signal(sigmap[s]) + } + } else { + p.Signal(sigmap[s]) + } + case "QUIT": + p.Signal(sigmap[s]) + } + }() + time.Sleep(2 * time.Second) +} diff --git a/cmd/dockerd/trap/trap.go b/cmd/dockerd/trap/trap.go new file mode 100644 index 0000000000000..fa21d99d04554 --- /dev/null +++ b/cmd/dockerd/trap/trap.go @@ -0,0 +1,65 @@ +package trap // import "github.com/docker/docker/cmd/dockerd/trap" + +import ( + "fmt" + "os" + gosignal "os/signal" + "sync/atomic" + "syscall" + + "github.com/docker/docker/pkg/stack" +) + +// Trap sets up a simplified signal "trap", appropriate for common +// behavior expected from a vanilla unix command-line tool in general +// (and the Docker engine in particular). +// +// * If SIGINT or SIGTERM are received, `cleanup` is called, then the process is terminated. +// * If SIGINT or SIGTERM are received 3 times before cleanup is complete, then cleanup is +// skipped and the process is terminated immediately (allows force quit of stuck daemon) +// * A SIGQUIT always causes an exit without cleanup, with a goroutine dump preceding exit. +// * Ignore SIGPIPE events. These are generated by systemd when journald is restarted while +// the docker daemon is not restarted and also running under systemd. +// Fixes https://github.com/docker/docker/issues/19728 +// +func Trap(cleanup func(), logger interface { + Info(args ...interface{}) +}) { + c := make(chan os.Signal, 1) + // we will handle INT, TERM, QUIT, SIGPIPE here + signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGPIPE} + gosignal.Notify(c, signals...) + go func() { + interruptCount := uint32(0) + for sig := range c { + if sig == syscall.SIGPIPE { + continue + } + + go func(sig os.Signal) { + logger.Info(fmt.Sprintf("Processing signal '%v'", sig)) + switch sig { + case os.Interrupt, syscall.SIGTERM: + if atomic.LoadUint32(&interruptCount) < 3 { + // Initiate the cleanup only once + if atomic.AddUint32(&interruptCount, 1) == 1 { + // Call the provided cleanup handler + cleanup() + os.Exit(0) + } else { + return + } + } else { + // 3 SIGTERM/INT signals received; force exit without cleanup + logger.Info("Forcing docker daemon shutdown without cleanup; 3 interrupts received") + } + case syscall.SIGQUIT: + stack.Dump() + logger.Info("Forcing docker daemon shutdown without cleanup on SIGQUIT") + } + // for the SIGINT/TERM, and SIGQUIT non-clean shutdown case, exit with 128 + signal # + os.Exit(128 + int(sig.(syscall.Signal))) + }(sig) + } + }() +} diff --git a/cmd/dockerd/trap/trap_linux_test.go b/cmd/dockerd/trap/trap_linux_test.go new file mode 100644 index 0000000000000..b213516492864 --- /dev/null +++ b/cmd/dockerd/trap/trap_linux_test.go @@ -0,0 +1,66 @@ +//go:build linux +// +build linux + +package trap // import "github.com/docker/docker/cmd/dockerd/trap" + +import ( + "os" + "os/exec" + "syscall" + "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func buildTestBinary(t *testing.T, tmpdir string, prefix string) (string, string) { + t.Helper() + tmpDir, err := os.MkdirTemp(tmpdir, prefix) + assert.NilError(t, err) + exePath := tmpDir + "/" + prefix + wd, _ := os.Getwd() + testHelperCode := wd + "/testfiles/main.go" + cmd := exec.Command("go", "build", "-o", exePath, testHelperCode) + err = cmd.Run() + assert.NilError(t, err) + return exePath, tmpDir +} + +func TestTrap(t *testing.T) { + var sigmap = []struct { + name string + signal os.Signal + multiple bool + }{ + {"TERM", syscall.SIGTERM, false}, + {"QUIT", syscall.SIGQUIT, true}, + {"INT", os.Interrupt, false}, + {"TERM", syscall.SIGTERM, true}, + {"INT", os.Interrupt, true}, + } + exePath, tmpDir := buildTestBinary(t, "", "main") + defer os.RemoveAll(tmpDir) + + for _, v := range sigmap { + t.Run(v.name, func(t *testing.T) { + cmd := exec.Command(exePath) + cmd.Env = append(os.Environ(), "SIGNAL_TYPE="+v.name) + if v.multiple { + cmd.Env = append(cmd.Env, "IF_MULTIPLE=1") + } + err := cmd.Start() + assert.NilError(t, err) + err = cmd.Wait() + e, ok := err.(*exec.ExitError) + assert.Assert(t, ok, "expected exec.ExitError, got %T", e) + + code := e.Sys().(syscall.WaitStatus).ExitStatus() + if v.multiple { + assert.Check(t, is.DeepEqual(128+int(v.signal.(syscall.Signal)), code)) + } else { + assert.Check(t, is.Equal(99, code)) + } + }) + } + +} diff --git a/container/container.go b/container/container.go index 6a5907c34b4dd..a8f267fb5912c 100644 --- a/container/container.go +++ b/container/container.go @@ -23,6 +23,7 @@ import ( "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/jsonfilelog" "github.com/docker/docker/daemon/logger/local" + "github.com/docker/docker/daemon/logger/loggerutils/cache" "github.com/docker/docker/daemon/network" "github.com/docker/docker/errdefs" "github.com/docker/docker/image" @@ -30,19 +31,22 @@ import ( "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/signal" - "github.com/docker/docker/pkg/symlink" "github.com/docker/docker/pkg/system" "github.com/docker/docker/restartmanager" "github.com/docker/docker/volume" volumemounts "github.com/docker/docker/volume/mounts" - "github.com/docker/go-units" + units "github.com/docker/go-units" agentexec "github.com/docker/swarmkit/agent/exec" + "github.com/moby/sys/signal" + "github.com/moby/sys/symlink" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -const configFileName = "config.v2.json" +const ( + configFileName = "config.v2.json" + hostConfigFileName = "hostconfig.json" +) // ExitStatus provides exit reasons for a container. type ExitStatus struct { @@ -104,8 +108,13 @@ type Container struct { NoNewPrivileges bool // Fields here are specific to Windows - NetworkSharedContainerID string `json:"-"` - SharedEndpointList []string `json:"-"` + NetworkSharedContainerID string `json:"-"` + SharedEndpointList []string `json:"-"` + LocalLogCacheMeta localLogCacheMeta `json:",omitempty"` +} + +type localLogCacheMeta struct { + HaveNotifyEnabled bool } // NewBaseContainer creates a new container with its @@ -152,12 +161,9 @@ func (container *Container) FromDisk() error { return container.readHostConfig() } -// toDisk saves the container configuration on disk and returns a deep copy. +// toDisk writes the container's configuration (config.v2.json, hostconfig.json) +// to disk and returns a deep copy. func (container *Container) toDisk() (*Container, error) { - var ( - buf bytes.Buffer - deepCopy Container - ) pth, err := container.ConfigPath() if err != nil { return nil, err @@ -170,11 +176,13 @@ func (container *Container) toDisk() (*Container, error) { } defer f.Close() + var buf bytes.Buffer w := io.MultiWriter(&buf, f) if err := json.NewEncoder(w).Encode(container); err != nil { return nil, err } + var deepCopy Container if err := json.NewDecoder(&buf).Decode(&deepCopy); err != nil { return nil, err } @@ -182,7 +190,6 @@ func (container *Container) toDisk() (*Container, error) { if err != nil { return nil, err } - return &deepCopy, nil } @@ -238,7 +245,7 @@ func (container *Container) WriteHostConfig() (*containertypes.HostConfig, error return nil, err } - f, err := ioutils.NewAtomicFileWriter(pth, 0644) + f, err := ioutils.NewAtomicFileWriter(pth, 0600) if err != nil { return nil, err } @@ -257,12 +264,6 @@ func (container *Container) WriteHostConfig() (*containertypes.HostConfig, error // SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir func (container *Container) SetupWorkingDirectory(rootIdentity idtools.Identity) error { - // TODO @jhowardmsft, @gupta-ak LCOW Support. This will need revisiting. - // We will need to do remote filesystem operations here. - if container.OS != runtime.GOOS { - return nil - } - if container.Config.WorkingDir == "" { return nil } @@ -342,7 +343,7 @@ func (container *Container) ExitOnNext() { // HostConfigPath returns the path to the container's JSON hostconfig func (container *Container) HostConfigPath() (string, error) { - return container.GetRootResourcePath("hostconfig.json") + return container.GetRootResourcePath(hostConfigFileName) } // ConfigPath returns the path to the container's JSON config @@ -415,6 +416,25 @@ func (container *Container) StartLogger() (logger.Logger, error) { } l = logger.NewRingLogger(l, info, bufferSize) } + + if _, ok := l.(logger.LogReader); !ok { + if cache.ShouldUseCache(cfg.Config) { + logPath, err := container.GetRootResourcePath("container-cached.log") + if err != nil { + return nil, err + } + + if !container.LocalLogCacheMeta.HaveNotifyEnabled { + logrus.WithField("container", container.ID).WithField("driver", container.HostConfig.LogConfig.Type).Info("Configured log driver does not support reads, enabling local file cache for container logs") + container.LocalLogCacheMeta.HaveNotifyEnabled = true + } + info.LogPath = logPath + l, err = cache.WithLocalCache(l, info) + if err != nil { + return nil, errors.Wrap(err, "error setting up local container log cache") + } + } + } return l, nil } @@ -448,11 +468,7 @@ func (container *Container) ShouldRestart() bool { // AddMountPointWithVolume adds a new mount point configured with a volume to the container. func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) { - operatingSystem := container.OS - if operatingSystem == "" { - operatingSystem = runtime.GOOS - } - volumeParser := volumemounts.NewParser(operatingSystem) + volumeParser := volumemounts.NewParser() container.MountPoints[destination] = &volumemounts.MountPoint{ Type: mounttypes.TypeVolume, Name: vol.Name(), @@ -502,7 +518,7 @@ func (container *Container) StopSignal() int { } if int(stopSignal) == 0 { - stopSignal, _ = signal.ParseSignal(signal.DefaultStopSignal) + stopSignal, _ = signal.ParseSignal(defaultStopSignal) } return int(stopSignal) } @@ -512,7 +528,7 @@ func (container *Container) StopTimeout() int { if container.Config.StopTimeout != nil { return *container.Config.StopTimeout } - return DefaultStopTimeout + return defaultStopTimeout } // InitDNSHostConfig ensures that the dns fields are never nil. @@ -691,19 +707,38 @@ func getSecretTargetPath(r *swarmtypes.SecretReference) string { return filepath.Join(containerSecretMountPath, r.File.Name) } +// getConfigTargetPath makes sure that config paths inside the container are +// absolute, as required by the runtime spec, and enforced by runc >= 1.0.0-rc94. +// see https://github.com/opencontainers/runc/issues/2928 +func getConfigTargetPath(r *swarmtypes.ConfigReference) string { + if filepath.IsAbs(r.File.Name) { + return r.File.Name + } + + return filepath.Join(containerConfigMountPath, r.File.Name) +} + // CreateDaemonEnvironment creates a new environment variable slice for this container. func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string) []string { // Setup environment - os := container.OS - if os == "" { - os = runtime.GOOS - } - env := []string{} - if runtime.GOOS != "windows" || (runtime.GOOS == "windows" && os == "linux") { - env = []string{ - "PATH=" + system.DefaultPathEnv(os), - "HOSTNAME=" + container.Config.Hostname, - } + ctrOS := container.OS + if ctrOS == "" { + ctrOS = runtime.GOOS + } + + // Figure out what size slice we need so we can allocate this all at once. + envSize := len(container.Config.Env) + if runtime.GOOS != "windows" { + envSize += 2 + len(linkedEnv) + } + if tty { + envSize++ + } + + env := make([]string, 0, envSize) + if runtime.GOOS != "windows" { + env = append(env, "PATH="+system.DefaultPathEnv(ctrOS)) + env = append(env, "HOSTNAME="+container.Config.Hostname) if tty { env = append(env, "TERM=xterm") } @@ -730,7 +765,7 @@ func (i *rio) Close() error { } func (i *rio) Wait() { - i.sc.Wait() + i.sc.Wait(context.Background()) i.IO.Wait() } diff --git a/container/container_unit_test.go b/container/container_unit_test.go index 82b5864760fca..030583039df18 100644 --- a/container/container_unit_test.go +++ b/container/container_unit_test.go @@ -2,7 +2,6 @@ package container // import "github.com/docker/docker/container" import ( "fmt" - "io/ioutil" "os" "path/filepath" "testing" @@ -10,8 +9,8 @@ import ( "github.com/docker/docker/api/types/container" swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/daemon/logger/jsonfilelog" - "github.com/docker/docker/pkg/signal" - "gotest.tools/assert" + "github.com/moby/sys/signal" + "gotest.tools/v3/assert" ) func TestContainerStopSignal(t *testing.T) { @@ -19,7 +18,7 @@ func TestContainerStopSignal(t *testing.T) { Config: &container.Config{}, } - def, err := signal.ParseSignal(signal.DefaultStopSignal) + def, err := signal.ParseSignal(defaultStopSignal) if err != nil { t.Fatal(err) } @@ -44,8 +43,8 @@ func TestContainerStopTimeout(t *testing.T) { } s := c.StopTimeout() - if s != DefaultStopTimeout { - t.Fatalf("Expected %v, got %v", DefaultStopTimeout, s) + if s != defaultStopTimeout { + t.Fatalf("Expected %v, got %v", defaultStopTimeout, s) } stopTimeout := 15 @@ -73,7 +72,7 @@ func TestContainerSecretReferenceDestTarget(t *testing.T) { } func TestContainerLogPathSetForJSONFileLogger(t *testing.T) { - containerRoot, err := ioutil.TempDir("", "TestContainerLogPathSetForJSONFileLogger") + containerRoot, err := os.MkdirTemp("", "TestContainerLogPathSetForJSONFileLogger") assert.NilError(t, err) defer os.RemoveAll(containerRoot) @@ -98,7 +97,7 @@ func TestContainerLogPathSetForJSONFileLogger(t *testing.T) { } func TestContainerLogPathSetForRingLogger(t *testing.T) { - containerRoot, err := ioutil.TempDir("", "TestContainerLogPathSetForRingLogger") + containerRoot, err := os.MkdirTemp("", "TestContainerLogPathSetForRingLogger") assert.NilError(t, err) defer os.RemoveAll(containerRoot) diff --git a/container/container_unix.go b/container/container_unix.go index ed664f3eecc59..3dced869c438f 100644 --- a/container/container_unix.go +++ b/container/container_unix.go @@ -1,32 +1,36 @@ +//go:build !windows // +build !windows package container // import "github.com/docker/docker/container" import ( - "io/ioutil" "os" "path/filepath" + "syscall" "github.com/containerd/continuity/fs" "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" mounttypes "github.com/docker/docker/api/types/mount" swarmtypes "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/volume" volumemounts "github.com/docker/docker/volume/mounts" + "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" ) const ( - // DefaultStopTimeout sets the default time, in seconds, to wait + // defaultStopSignal is the default syscall signal used to stop a container. + defaultStopSignal = "SIGTERM" + + // defaultStopTimeout sets the default time, in seconds, to wait // for the graceful container stop before forcefully terminating it. - DefaultStopTimeout = 10 + defaultStopTimeout = 10 + containerConfigMountPath = "/" containerSecretMountPath = "/run/secrets" ) @@ -56,14 +60,14 @@ func (container *Container) BuildHostnameFile() error { return err } container.HostnamePath = hostnamePath - return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) + return os.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) } // NetworkMounts returns the list of network mounts. func (container *Container) NetworkMounts() []Mount { var mounts []Mount shared := container.HostConfig.NetworkMode.IsContainer() - parser := volumemounts.NewParser(container.OS) + parser := volumemounts.NewParser() if container.ResolvConfPath != "" { if _, err := os.Stat(container.ResolvConfPath); err != nil { logrus.Warnf("ResolvConfPath set to %q, but can't stat this filename (err = %v); skipping", container.ResolvConfPath, err) @@ -135,7 +139,7 @@ func (container *Container) CopyImagePathContent(v volume.Volume, destination st return err } - id := stringid.GenerateNonCryptoID() + id := stringid.GenerateRandomID() path, err := v.Mount(id) if err != nil { return err @@ -146,7 +150,7 @@ func (container *Container) CopyImagePathContent(v volume.Volume, destination st logrus.Warnf("error while unmounting volume %s: %v", v.Name(), err) } }() - if err := label.Relabel(path, container.MountLabel, true); err != nil && err != unix.ENOTSUP { + if err := label.Relabel(path, container.MountLabel, true); err != nil && !errors.Is(err, syscall.ENOTSUP) { return err } return copyExistingContents(rootfs, path) @@ -174,8 +178,8 @@ func (container *Container) HasMountFor(path string) bool { return false } -// UnmountIpcMount uses the provided unmount function to unmount shm if it was mounted -func (container *Container) UnmountIpcMount(unmount func(pth string) error) error { +// UnmountIpcMount unmounts shm if it was mounted +func (container *Container) UnmountIpcMount() error { if container.HasMountFor("/dev/shm") { return nil } @@ -189,10 +193,8 @@ func (container *Container) UnmountIpcMount(unmount func(pth string) error) erro if shmPath == "" { return nil } - if err = unmount(shmPath); err != nil && !os.IsNotExist(err) { - if mounted, mErr := mount.Mounted(shmPath); mounted || mErr != nil { - return errors.Wrapf(err, "umount %s", shmPath) - } + if err = mount.Unmount(shmPath); err != nil && !errors.Is(err, os.ErrNotExist) { + return err } return nil } @@ -200,7 +202,7 @@ func (container *Container) UnmountIpcMount(unmount func(pth string) error) erro // IpcMounts returns the list of IPC mounts func (container *Container) IpcMounts() []Mount { var mounts []Mount - parser := volumemounts.NewParser(container.OS) + parser := volumemounts.NewParser() if container.HasMountFor("/dev/shm") { return mounts @@ -244,7 +246,7 @@ func (container *Container) SecretMounts() ([]Mount, error) { } mounts = append(mounts, Mount{ Source: fPath, - Destination: r.File.Name, + Destination: getConfigTargetPath(r), Writable: false, }) } @@ -343,6 +345,9 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi if resources.CPURealtimeRuntime != 0 { cResources.CPURealtimeRuntime = resources.CPURealtimeRuntime } + if resources.PidsLimit != nil { + cResources.PidsLimit = resources.PidsLimit + } // update HostConfig of container if hostConfig.RestartPolicy.Name != "" { @@ -382,16 +387,29 @@ func (container *Container) DetachAndUnmount(volumeEventLog func(name, action st for _, mountPath := range mountPaths { if err := mount.Unmount(mountPath); err != nil { - logrus.Warnf("%s unmountVolumes: Failed to do lazy umount fo volume '%s': %v", container.ID, mountPath, err) + logrus.WithError(err).WithField("container", container.ID). + Warn("Unable to unmount") } } return container.UnmountVolumes(volumeEventLog) } +// ignoreUnsupportedXAttrs ignores errors when extended attributes +// are not supported +func ignoreUnsupportedXAttrs() fs.CopyDirOpt { + xeh := func(dst, src, xattrKey string, err error) error { + if !errors.Is(err, syscall.ENOTSUP) { + return err + } + return nil + } + return fs.WithXAttrErrorHandler(xeh) +} + // copyExistingContents copies from the source to the destination and // ensures the ownership is appropriately set. func copyExistingContents(source, destination string) error { - dstList, err := ioutil.ReadDir(destination) + dstList, err := os.ReadDir(destination) if err != nil { return err } @@ -399,12 +417,11 @@ func copyExistingContents(source, destination string) error { // destination is not empty, do not copy return nil } - return fs.CopyDir(destination, source) + return fs.CopyDir(destination, source, ignoreUnsupportedXAttrs()) } // TmpfsMounts returns the list of tmpfs mounts func (container *Container) TmpfsMounts() ([]Mount, error) { - parser := volumemounts.NewParser(container.OS) var mounts []Mount for dest, data := range container.HostConfig.Tmpfs { mounts = append(mounts, Mount{ @@ -413,6 +430,7 @@ func (container *Container) TmpfsMounts() ([]Mount, error) { Data: data, }) } + parser := volumemounts.NewParser() for dest, mnt := range container.MountPoints { if mnt.Type == mounttypes.TypeTmpfs { data, err := parser.ConvertTmpfsOptions(mnt.Spec.TmpfsOptions, mnt.Spec.ReadOnly) @@ -429,11 +447,6 @@ func (container *Container) TmpfsMounts() ([]Mount, error) { return mounts, nil } -// EnableServiceDiscoveryOnDefaultNetwork Enable service discovery on default network -func (container *Container) EnableServiceDiscoveryOnDefaultNetwork() bool { - return false -} - // GetMountPoints gives a platform specific transformation to types.MountPoint. Callers must hold a Container lock. func (container *Container) GetMountPoints() []types.MountPoint { mountPoints := make([]types.MountPoint, 0, len(container.MountPoints)) diff --git a/container/container_windows.go b/container/container_windows.go index b5bdb5bc34363..8229480df762f 100644 --- a/container/container_windows.go +++ b/container/container_windows.go @@ -12,17 +12,21 @@ import ( ) const ( + containerConfigMountPath = `C:\` containerSecretMountPath = `C:\ProgramData\Docker\secrets` containerInternalSecretMountPath = `C:\ProgramData\Docker\internal\secrets` containerInternalConfigsDirPath = `C:\ProgramData\Docker\internal\configs` - // DefaultStopTimeout is the timeout (in seconds) for the shutdown call on a container - DefaultStopTimeout = 30 + // defaultStopSignal is the default syscall signal used to stop a container. + defaultStopSignal = "SIGTERM" + + // defaultStopTimeout is the timeout (in seconds) for the shutdown call on a container + defaultStopTimeout = 30 ) // UnmountIpcMount unmounts Ipc related mounts. // This is a NOOP on windows. -func (container *Container) UnmountIpcMount(unmount func(pth string) error) error { +func (container *Container) UnmountIpcMount() error { return nil } @@ -41,7 +45,7 @@ func (container *Container) CreateSecretSymlinks() error { if err != nil { return err } - if err := system.MkdirAll(filepath.Dir(resolvedPath), 0, ""); err != nil { + if err := system.MkdirAll(filepath.Dir(resolvedPath), 0); err != nil { return err } if err := os.Symlink(filepath.Join(containerInternalSecretMountPath, r.SecretID), resolvedPath); err != nil { @@ -87,11 +91,11 @@ func (container *Container) CreateConfigSymlinks() error { if configRef.File == nil { continue } - resolvedPath, _, err := container.ResolvePath(configRef.File.Name) + resolvedPath, _, err := container.ResolvePath(getConfigTargetPath(configRef)) if err != nil { return err } - if err := system.MkdirAll(filepath.Dir(resolvedPath), 0, ""); err != nil { + if err := system.MkdirAll(filepath.Dir(resolvedPath), 0); err != nil { return err } if err := os.Symlink(filepath.Join(containerInternalConfigsDirPath, configRef.ConfigID), resolvedPath); err != nil { @@ -153,13 +157,12 @@ func (container *Container) UpdateContainer(hostConfig *containertypes.HostConfi resources.CpusetMems != "" || len(resources.Devices) != 0 || len(resources.DeviceCgroupRules) != 0 || - resources.DiskQuota != 0 || resources.KernelMemory != 0 || resources.MemoryReservation != 0 || resources.MemorySwap != 0 || resources.MemorySwappiness != nil || resources.OomKillDisable != nil || - resources.PidsLimit != 0 || + (resources.PidsLimit != nil && *resources.PidsLimit != 0) || len(resources.Ulimits) != 0 || resources.CPUCount != 0 || resources.CPUPercent != 0 || @@ -182,11 +185,6 @@ func (container *Container) BuildHostnameFile() error { return nil } -// EnableServiceDiscoveryOnDefaultNetwork Enable service discovery on default network -func (container *Container) EnableServiceDiscoveryOnDefaultNetwork() bool { - return true -} - // GetMountPoints gives a platform specific transformation to types.MountPoint. Callers must hold a Container lock. func (container *Container) GetMountPoints() []types.MountPoint { mountPoints := make([]types.MountPoint, 0, len(container.MountPoints)) @@ -208,6 +206,6 @@ func (container *Container) ConfigsDirPath() string { } // ConfigFilePath returns the path to the on-disk location of a config. -func (container *Container) ConfigFilePath(configRef swarmtypes.ConfigReference) string { - return filepath.Join(container.ConfigsDirPath(), configRef.ConfigID) +func (container *Container) ConfigFilePath(configRef swarmtypes.ConfigReference) (string, error) { + return filepath.Join(container.ConfigsDirPath(), configRef.ConfigID), nil } diff --git a/container/env.go b/container/env.go index d225fd14714e1..e31088c88558d 100644 --- a/container/env.go +++ b/container/env.go @@ -9,22 +9,22 @@ import ( func ReplaceOrAppendEnvValues(defaults, overrides []string) []string { cache := make(map[string]int, len(defaults)) for i, e := range defaults { - parts := strings.SplitN(e, "=", 2) - cache[parts[0]] = i + index := strings.Index(e, "=") + cache[e[:index]] = i } for _, value := range overrides { // Values w/o = means they want this env to be removed/unset. - if !strings.Contains(value, "=") { + index := strings.Index(value, "=") + if index < 0 { + // no "=" in value if i, exists := cache[value]; exists { defaults[i] = "" // Used to indicate it should be removed } continue } - // Just do a normal set/update - parts := strings.SplitN(value, "=", 2) - if i, exists := cache[parts[0]]; exists { + if i, exists := cache[value[:index]]; exists { defaults[i] = value } else { defaults = append(defaults, value) diff --git a/container/env_test.go b/container/env_test.go index 77856284c251a..432d43a4b2857 100644 --- a/container/env_test.go +++ b/container/env_test.go @@ -1,6 +1,11 @@ package container // import "github.com/docker/docker/container" -import "testing" +import ( + "crypto/rand" + "testing" + + "gotest.tools/v3/assert" +) func TestReplaceAndAppendEnvVars(t *testing.T) { var ( @@ -22,3 +27,47 @@ func TestReplaceAndAppendEnvVars(t *testing.T) { t.Fatalf("expected TERM=xterm got '%s'", env[1]) } } + +func BenchmarkReplaceOrAppendEnvValues(b *testing.B) { + b.Run("0", func(b *testing.B) { + benchmarkReplaceOrAppendEnvValues(b, 0) + }) + b.Run("100", func(b *testing.B) { + benchmarkReplaceOrAppendEnvValues(b, 100) + }) + b.Run("1000", func(b *testing.B) { + benchmarkReplaceOrAppendEnvValues(b, 1000) + }) + b.Run("10000", func(b *testing.B) { + benchmarkReplaceOrAppendEnvValues(b, 10000) + }) +} + +func benchmarkReplaceOrAppendEnvValues(b *testing.B, extraEnv int) { + b.StopTimer() + // remove FOO from env + // remove BAR from env (nop) + o := []string{"HOME=/root", "TERM=xterm", "FOO", "BAR"} + + if extraEnv > 0 { + buf := make([]byte, 5) + for i := 0; i < extraEnv; i++ { + n, err := rand.Read(buf) + assert.NilError(b, err) + key := string(buf[:n]) + + n, err = rand.Read(buf) + assert.NilError(b, err) + val := string(buf[:n]) + + o = append(o, key+"="+val) + } + } + d := make([]string, 0, len(o)+2) + d = append(d, []string{"HOME=/", "FOO=foo_default"}...) + + b.StartTimer() + for i := 0; i < b.N; i++ { + _ = ReplaceOrAppendEnvValues(d, o) + } +} diff --git a/container/health.go b/container/health.go index 167ee9b476700..3e93142b98599 100644 --- a/container/health.go +++ b/container/health.go @@ -22,7 +22,7 @@ func (s *Health) String() string { case types.Starting: return "health: starting" default: // Healthy and Unhealthy are clear on their own - return s.Health.Status + return status } } diff --git a/container/monitor.go b/container/monitor.go index 1735e3487e2a1..ff4b3439e5395 100644 --- a/container/monitor.go +++ b/container/monitor.go @@ -33,8 +33,11 @@ func (container *Container) Reset(lock bool) { container.LogCopier.Wait() close(exit) }() + + timer := time.NewTimer(loggerCloseTimeout) + defer timer.Stop() select { - case <-time.After(loggerCloseTimeout): + case <-timer.C: logrus.Warn("Logger didn't exit in time: logs may be truncated") case <-exit: } diff --git a/container/mounts_unix.go b/container/mounts_unix.go index 62f4441dce0cf..168286889a011 100644 --- a/container/mounts_unix.go +++ b/container/mounts_unix.go @@ -1,12 +1,14 @@ +//go:build !windows // +build !windows package container // import "github.com/docker/docker/container" // Mount contains information for a mount operation. type Mount struct { - Source string `json:"source"` - Destination string `json:"destination"` - Writable bool `json:"writable"` - Data string `json:"data"` - Propagation string `json:"mountpropagation"` + Source string `json:"source"` + Destination string `json:"destination"` + Writable bool `json:"writable"` + Data string `json:"data"` + Propagation string `json:"mountpropagation"` + NonRecursive bool `json:"nonrecursive"` } diff --git a/container/state.go b/container/state.go index 7c2a1ec81cdab..fe2b060d72dc5 100644 --- a/container/state.go +++ b/container/state.go @@ -8,7 +8,7 @@ import ( "time" "github.com/docker/docker/api/types" - "github.com/docker/go-units" + units "github.com/docker/go-units" ) // State holds the current container state, and has methods to get and @@ -17,7 +17,7 @@ import ( type State struct { sync.Mutex // Note that `Running` and `Paused` are not mutually exclusive: - // When pausing a container (on Linux), the cgroups freezer is used to suspend + // When pausing a container (on Linux), the freezer cgroup is used to suspend // all processes in the container. Freezing the process requires the process to // be running. As a result, paused containers are both `Running` _and_ `Paused`. Running bool @@ -207,7 +207,7 @@ func (s *State) Wait(ctx context.Context, condition WaitCondition) <-chan StateS // actually stopped. waitRemove := s.waitRemove - resultC := make(chan StateStatus) + resultC := make(chan StateStatus, 1) go func() { select { @@ -366,13 +366,6 @@ func (s *State) IsRemovalInProgress() bool { return res } -// SetDead sets the container state to "dead" -func (s *State) SetDead() { - s.Lock() - s.Dead = true - s.Unlock() -} - // IsDead returns whether the Dead flag is set. Used by Container to check whether a container is dead. func (s *State) IsDead() bool { s.Lock() diff --git a/container/state_test.go b/container/state_test.go index 4ad3c805edead..bf114ea1aafcd 100644 --- a/container/state_test.go +++ b/container/state_test.go @@ -109,7 +109,9 @@ func TestStateRunStop(t *testing.T) { } // Set the state to dead and removed. - s.SetDead() + s.Lock() + s.Dead = true + s.Unlock() s.SetRemoved() // Wait for removed status or timeout. diff --git a/container/stream/attach.go b/container/stream/attach.go index 1366dcb4994c4..0269a226b1bd6 100644 --- a/container/stream/attach.go +++ b/container/stream/attach.go @@ -5,7 +5,7 @@ import ( "io" "github.com/docker/docker/pkg/pools" - "github.com/docker/docker/pkg/term" + "github.com/moby/term" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" diff --git a/container/stream/streams.go b/container/stream/streams.go index d81867c1daae8..83e6ded611db8 100644 --- a/container/stream/streams.go +++ b/container/stream/streams.go @@ -1,9 +1,9 @@ package stream // import "github.com/docker/docker/container/stream" import ( + "context" "fmt" "io" - "io/ioutil" "strings" "sync" @@ -24,11 +24,12 @@ import ( // copied and delivered to all StdoutPipe and StderrPipe consumers, using // a kind of "broadcaster". type Config struct { - sync.WaitGroup + wg sync.WaitGroup stdout *broadcaster.Unbuffered stderr *broadcaster.Unbuffered stdin io.ReadCloser stdinPipe io.WriteCloser + dio *cio.DirectIO } // NewConfig creates a stream config and initializes @@ -85,7 +86,7 @@ func (c *Config) NewInputPipes() { // NewNopInputPipe creates a new input pipe that will silently drop all messages in the input. func (c *Config) NewNopInputPipe() { - c.stdinPipe = ioutils.NopWriteCloser(ioutil.Discard) + c.stdinPipe = ioutils.NopWriteCloser(io.Discard) } // CloseStreams ensures that the configured streams are properly closed. @@ -115,14 +116,15 @@ func (c *Config) CloseStreams() error { // CopyToPipe connects streamconfig with a libcontainerd.IOPipe func (c *Config) CopyToPipe(iop *cio.DirectIO) { + c.dio = iop copyFunc := func(w io.Writer, r io.ReadCloser) { - c.Add(1) + c.wg.Add(1) go func() { if _, err := pools.Copy(w, r); err != nil { logrus.Errorf("stream copy error: %v", err) } r.Close() - c.Done() + c.wg.Done() }() } @@ -144,3 +146,23 @@ func (c *Config) CopyToPipe(iop *cio.DirectIO) { } } } + +// Wait for the stream to close +// Wait supports timeouts via the context to unblock and forcefully +// close the io streams +func (c *Config) Wait(ctx context.Context) { + done := make(chan struct{}, 1) + go func() { + c.wg.Wait() + close(done) + }() + select { + case <-done: + case <-ctx.Done(): + if c.dio != nil { + c.dio.Cancel() + c.dio.Wait() + c.dio.Close() + } + } +} diff --git a/container/view.go b/container/view.go index b631499412868..962a20b9e1cd6 100644 --- a/container/view.go +++ b/container/view.go @@ -9,7 +9,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" "github.com/docker/go-connections/nat" - "github.com/hashicorp/go-memdb" + memdb "github.com/hashicorp/go-memdb" "github.com/sirupsen/logrus" ) @@ -473,7 +473,7 @@ type namesByContainerIDIndexer struct{} func (e *namesByContainerIDIndexer) FromObject(obj interface{}) (bool, []byte, error) { n, ok := obj.(nameAssociation) if !ok { - return false, nil, fmt.Errorf(`%T does not have type "nameAssocation"`, obj) + return false, nil, fmt.Errorf(`%T does not have type "nameAssociation"`, obj) } // Add the null character as a terminator diff --git a/container/view_test.go b/container/view_test.go index 434b7c618d53b..290282c907dc0 100644 --- a/container/view_test.go +++ b/container/view_test.go @@ -1,23 +1,22 @@ package container // import "github.com/docker/docker/container" import ( - "io/ioutil" "os" "path/filepath" "testing" "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" - "github.com/pborman/uuid" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/google/uuid" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) var root string func TestMain(m *testing.M) { var err error - root, err = ioutil.TempDir("", "docker-container-test-") + root, err = os.MkdirTemp("", "docker-container-test-") if err != nil { panic(err) } @@ -28,7 +27,7 @@ func TestMain(m *testing.M) { func newContainer(t *testing.T) *Container { var ( - id = uuid.New() + id = uuid.New().String() cRoot = filepath.Join(root, id) ) if err := os.MkdirAll(cRoot, 0755); err != nil { diff --git a/contrib/REVIEWERS b/contrib/REVIEWERS deleted file mode 100644 index 18e05a307018f..0000000000000 --- a/contrib/REVIEWERS +++ /dev/null @@ -1 +0,0 @@ -Tianon Gravi (@tianon) diff --git a/contrib/apparmor/template.go b/contrib/apparmor/template.go index e5e1c8bed6df3..e6d0b6d37c58c 100644 --- a/contrib/apparmor/template.go +++ b/contrib/apparmor/template.go @@ -29,10 +29,11 @@ profile /usr/bin/docker (attach_disconnected, complain) { capability, owner /** rw, @{DOCKER_GRAPH_PATH}/** rwl, - @{DOCKER_GRAPH_PATH}/linkgraph.db k, @{DOCKER_GRAPH_PATH}/network/files/boltdb.db k, @{DOCKER_GRAPH_PATH}/network/files/local-kv.db k, - @{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/linkgraph.db k, + # For user namespaces: + @{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/network/files/boltdb.db k, + @{DOCKER_GRAPH_PATH}/[0-9]*.[0-9]*/network/files/local-kv.db k, # For non-root client use: /dev/urandom r, diff --git a/contrib/busybox/Dockerfile b/contrib/busybox/Dockerfile new file mode 100644 index 0000000000000..b2f40d53cd7ac --- /dev/null +++ b/contrib/busybox/Dockerfile @@ -0,0 +1,30 @@ +# Source: https://frippery.org/busybox/ +# This Dockerfile builds a (32-bit) busybox images which is suitable for +# running many of the integration-cli tests for Docker against a Windows +# daemon. It will not run on nanoserver as that is 64-bit only. +# +# Based on https://github.com/jhowardmsft/busybox +# John Howard (IRC jhowardmsft, Email john.howard@microsoft.com) +# +# To build: docker build -t busybox . +# To publish: Needs someone with publishing rights +ARG WINDOWS_BASE_IMAGE=mcr.microsoft.com/windows/servercore +ARG WINDOWS_BASE_IMAGE_TAG=ltsc2019 +ARG BUSYBOX_VERSION=FRP-3329-gcf0fa4d13 + +# Checksum taken from https://frippery.org/files/busybox/SHA256SUM +ARG BUSYBOX_SHA256SUM=bfaeb88638e580fc522a68e69072e305308f9747563e51fa085eec60ca39a5ae + +FROM ${WINDOWS_BASE_IMAGE}:${WINDOWS_BASE_IMAGE_TAG} +RUN mkdir C:\tmp && mkdir C:\bin +ARG BUSYBOX_VERSION +ARG BUSYBOX_SHA256SUM +ADD https://frippery.org/files/busybox/busybox-w32-${BUSYBOX_VERSION}.exe /bin/busybox.exe +RUN powershell \ + if ((Get-FileHash -Path /bin/busybox.exe -Algorithm SHA256).Hash -ne $Env:BUSYBOX_SHA256SUM) { \ + Throw \"Checksum validation failed\" \ + } + +RUN setx /M PATH "C:\bin;%PATH%" +RUN powershell busybox.exe --list ^|%{$nul = cmd /c mklink C:\bin\$_.exe busybox.exe} +CMD ["sh"] diff --git a/contrib/busybox/LICENSE b/contrib/busybox/LICENSE new file mode 100644 index 0000000000000..178d7990937e7 --- /dev/null +++ b/contrib/busybox/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 John Howard (Microsoft) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/contrib/check-config.sh b/contrib/check-config.sh index 88eb8aa753494..617eb938938d4 100755 --- a/contrib/check-config.sh +++ b/contrib/check-config.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh set -e EXITCODE=0 @@ -6,20 +6,20 @@ EXITCODE=0 # bits of this were adapted from lxc-checkconfig # see also https://github.com/lxc/lxc/blob/lxc-1.0.2/src/lxc/lxc-checkconfig.in -possibleConfigs=( - '/proc/config.gz' - "/boot/config-$(uname -r)" - "/usr/src/linux-$(uname -r)/.config" - '/usr/src/linux/.config' -) +possibleConfigs=" + /proc/config.gz + /boot/config-$(uname -r) + /usr/src/linux-$(uname -r)/.config + /usr/src/linux/.config +" if [ $# -gt 0 ]; then CONFIG="$1" else - : ${CONFIG:="${possibleConfigs[0]}"} + : "${CONFIG:=/proc/config.gz}" fi -if ! command -v zgrep &> /dev/null; then +if ! command -v zgrep > /dev/null 2>&1; then zgrep() { zcat "$2" | grep "$1" } @@ -41,13 +41,13 @@ is_set_as_module() { } color() { - local codes=() + codes= if [ "$1" = 'bold' ]; then - codes=( "${codes[@]}" '1' ) + codes='1' shift fi if [ "$#" -gt 0 ]; then - local code= + code= case "$1" in # see https://en.wikipedia.org/wiki/ANSI_escape_code#Colors black) code=30 ;; @@ -60,17 +60,16 @@ color() { white) code=37 ;; esac if [ "$code" ]; then - codes=( "${codes[@]}" "$code" ) + codes="${codes:+$codes;}$code" fi fi - local IFS=';' - echo -en '\033['"${codes[*]}"'m' + printf '\033[%sm' "$codes" } wrap_color() { text="$1" shift color "$@" - echo -n "$text" + printf '%s' "$text" color reset echo } @@ -98,12 +97,13 @@ check_flag() { check_flags() { for flag in "$@"; do - echo -n "- "; check_flag "$flag" + printf -- '- ' + check_flag "$flag" done } check_command() { - if command -v "$1" >/dev/null 2>&1; then + if command -v "$1" > /dev/null 2>&1; then wrap_good "$1 command" 'available' else wrap_bad "$1 command" 'missing' @@ -121,20 +121,26 @@ check_device() { } check_distro_userns() { - source /etc/os-release 2>/dev/null || /bin/true - if [[ "${ID}" =~ ^(centos|rhel)$ && "${VERSION_ID}" =~ ^7 ]]; then - # this is a CentOS7 or RHEL7 system - grep -q "user_namespace.enable=1" /proc/cmdline || { - # no user namespace support enabled - wrap_bad " (RHEL7/CentOS7" "User namespaces disabled; add 'user_namespace.enable=1' to boot command line)" - EXITCODE=1 - } - fi + . /etc/os-release 2> /dev/null || /bin/true + case "$ID" in + centos | rhel) + case "$VERSION_ID" in + 7*) + # this is a CentOS7 or RHEL7 system + grep -q 'user_namespace.enable=1' /proc/cmdline || { + # no user namespace support enabled + wrap_bad " (RHEL7/CentOS7" "User namespaces disabled; add 'user_namespace.enable=1' to boot command line)" + EXITCODE=1 + } + ;; + esac + ;; + esac } if [ ! -e "$CONFIG" ]; then wrap_warning "warning: $CONFIG does not exist, searching other paths for kernel config ..." - for tryConfig in "${possibleConfigs[@]}"; do + for tryConfig in $possibleConfigs; do if [ -e "$tryConfig" ]; then CONFIG="$tryConfig" break @@ -153,54 +159,85 @@ echo echo 'Generally Necessary:' -echo -n '- ' -cgroupSubsystemDir="$(awk '/[, ](cpu|cpuacct|cpuset|devices|freezer|memory)[, ]/ && $3 == "cgroup" { print $2 }' /proc/mounts | head -n1)" -cgroupDir="$(dirname "$cgroupSubsystemDir")" -if [ -d "$cgroupDir/cpu" -o -d "$cgroupDir/cpuacct" -o -d "$cgroupDir/cpuset" -o -d "$cgroupDir/devices" -o -d "$cgroupDir/freezer" -o -d "$cgroupDir/memory" ]; then - echo "$(wrap_good 'cgroup hierarchy' 'properly mounted') [$cgroupDir]" +printf -- '- ' +if [ "$(stat -f -c %t /sys/fs/cgroup 2> /dev/null)" = '63677270' ]; then + wrap_good 'cgroup hierarchy' 'cgroupv2' + cgroupv2ControllerFile='/sys/fs/cgroup/cgroup.controllers' + if [ -f "$cgroupv2ControllerFile" ]; then + echo ' Controllers:' + for controller in cpu cpuset io memory pids; do + if grep -qE '(^| )'"$controller"'($| )' "$cgroupv2ControllerFile"; then + echo " - $(wrap_good "$controller" 'available')" + else + echo " - $(wrap_bad "$controller" 'missing')" + fi + done + else + wrap_bad "$cgroupv2ControllerFile" 'nonexistent??' + fi + # TODO find an efficient way to check if cgroup.freeze exists in subdir else - if [ "$cgroupSubsystemDir" ]; then - echo "$(wrap_bad 'cgroup hierarchy' 'single mountpoint!') [$cgroupSubsystemDir]" + cgroupSubsystemDir="$(sed -rne '/^[^ ]+ ([^ ]+) cgroup ([^ ]*,)?(cpu|cpuacct|cpuset|devices|freezer|memory)[, ].*$/ { s//\1/p; q }' /proc/mounts)" + cgroupDir="$(dirname "$cgroupSubsystemDir")" + if [ -d "$cgroupDir/cpu" ] || [ -d "$cgroupDir/cpuacct" ] || [ -d "$cgroupDir/cpuset" ] || [ -d "$cgroupDir/devices" ] || [ -d "$cgroupDir/freezer" ] || [ -d "$cgroupDir/memory" ]; then + echo "$(wrap_good 'cgroup hierarchy' 'properly mounted') [$cgroupDir]" else - echo "$(wrap_bad 'cgroup hierarchy' 'nonexistent??')" + if [ "$cgroupSubsystemDir" ]; then + echo "$(wrap_bad 'cgroup hierarchy' 'single mountpoint!') [$cgroupSubsystemDir]" + else + wrap_bad 'cgroup hierarchy' 'nonexistent??' + fi + EXITCODE=1 + echo " $(wrap_color '(see https://github.com/tianon/cgroupfs-mount)' yellow)" fi - EXITCODE=1 - echo " $(wrap_color '(see https://github.com/tianon/cgroupfs-mount)' yellow)" fi -if [ "$(cat /sys/module/apparmor/parameters/enabled 2>/dev/null)" = 'Y' ]; then - echo -n '- ' - if command -v apparmor_parser &> /dev/null; then - echo "$(wrap_good 'apparmor' 'enabled and tools installed')" +if [ "$(cat /sys/module/apparmor/parameters/enabled 2> /dev/null)" = 'Y' ]; then + printf -- '- ' + if command -v apparmor_parser > /dev/null 2>&1; then + wrap_good 'apparmor' 'enabled and tools installed' else - echo "$(wrap_bad 'apparmor' 'enabled, but apparmor_parser missing')" - echo -n ' ' - if command -v apt-get &> /dev/null; then - echo "$(wrap_color '(use "apt-get install apparmor" to fix this)')" - elif command -v yum &> /dev/null; then - echo "$(wrap_color '(your best bet is "yum install apparmor-parser")')" + wrap_bad 'apparmor' 'enabled, but apparmor_parser missing' + printf ' ' + if command -v apt-get > /dev/null 2>&1; then + wrap_color '(use "apt-get install apparmor" to fix this)' + elif command -v yum > /dev/null 2>&1; then + wrap_color '(your best bet is "yum install apparmor-parser")' else - echo "$(wrap_color '(look for an "apparmor" package for your distribution)')" + wrap_color '(look for an "apparmor" package for your distribution)' fi EXITCODE=1 fi fi -flags=( - NAMESPACES {NET,PID,IPC,UTS}_NS - CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED CPUSETS MEMCG - KEYS - VETH BRIDGE BRIDGE_NETFILTER - NF_NAT_IPV4 IP_NF_FILTER IP_NF_TARGET_MASQUERADE - NETFILTER_XT_MATCH_{ADDRTYPE,CONNTRACK,IPVS} - IP_NF_NAT NF_NAT NF_NAT_NEEDED - - # required for bind-mounting /dev/mqueue into containers +check_flags \ + NAMESPACES NET_NS PID_NS IPC_NS UTS_NS \ + CGROUPS CGROUP_CPUACCT CGROUP_DEVICE CGROUP_FREEZER CGROUP_SCHED CPUSETS MEMCG \ + KEYS \ + VETH BRIDGE BRIDGE_NETFILTER \ + IP_NF_FILTER IP_NF_TARGET_MASQUERADE \ + NETFILTER_XT_MATCH_ADDRTYPE \ + NETFILTER_XT_MATCH_CONNTRACK \ + NETFILTER_XT_MATCH_IPVS \ + NETFILTER_XT_MARK \ + IP_NF_NAT NF_NAT \ POSIX_MQUEUE -) -check_flags "${flags[@]}" -if [ "$kernelMajor" -lt 4 ] || [ "$kernelMajor" -eq 4 -a "$kernelMinor" -lt 8 ]; then - check_flags DEVPTS_MULTIPLE_INSTANCES +# (POSIX_MQUEUE is required for bind-mounting /dev/mqueue into containers) + +if [ "$kernelMajor" -lt 4 ] || ([ "$kernelMajor" -eq 4 ] && [ "$kernelMinor" -lt 8 ]); then + check_flags DEVPTS_MULTIPLE_INSTANCES +fi + +if [ "$kernelMajor" -lt 5 ] || [ "$kernelMajor" -eq 5 -a "$kernelMinor" -le 1 ]; then + check_flags NF_NAT_IPV4 +fi + +if [ "$kernelMajor" -lt 5 ] || [ "$kernelMajor" -eq 5 -a "$kernelMinor" -le 2 ]; then + check_flags NF_NAT_NEEDED +fi +# check availability of BPF_CGROUP_DEVICE support +if [ "$kernelMajor" -ge 5 ] || ([ "$kernelMajor" -eq 4 ] && [ "$kernelMinor" -ge 15 ]); then + check_flags CGROUP_BPF fi echo @@ -212,28 +249,40 @@ echo 'Optional Features:' } { check_flags SECCOMP + check_flags SECCOMP_FILTER } { check_flags CGROUP_PIDS } { - CODE=${EXITCODE} - check_flags MEMCG_SWAP MEMCG_SWAP_ENABLED - if [ -e /sys/fs/cgroup/memory/memory.memsw.limit_in_bytes ]; then + check_flags MEMCG_SWAP + # Kernel v5.8+ removes MEMCG_SWAP_ENABLED. + if [ "$kernelMajor" -lt 5 ] || [ "$kernelMajor" -eq 5 -a "$kernelMinor" -le 8 ]; then + CODE=${EXITCODE} + check_flags MEMCG_SWAP_ENABLED + # FIXME this check is cgroupv1-specific + if [ -e /sys/fs/cgroup/memory/memory.memsw.limit_in_bytes ]; then + echo " $(wrap_color '(cgroup swap accounting is currently enabled)' bold black)" + EXITCODE=${CODE} + elif is_set MEMCG_SWAP && ! is_set MEMCG_SWAP_ENABLED; then + echo " $(wrap_color '(cgroup swap accounting is currently not enabled, you can enable it by setting boot option "swapaccount=1")' bold black)" + fi + else + # Kernel v5.8+ enables swap accounting by default. echo " $(wrap_color '(cgroup swap accounting is currently enabled)' bold black)" - EXITCODE=${CODE} - elif is_set MEMCG_SWAP && ! is_set MEMCG_SWAP_ENABLED; then - echo " $(wrap_color '(cgroup swap accounting is currently not enabled, you can enable it by setting boot option "swapaccount=1")' bold black)" fi } { if is_set LEGACY_VSYSCALL_NATIVE; then - echo -n "- "; wrap_bad "CONFIG_LEGACY_VSYSCALL_NATIVE" 'enabled' + printf -- '- ' + wrap_bad "CONFIG_LEGACY_VSYSCALL_NATIVE" 'enabled' echo " $(wrap_color '(dangerous, provides an ASLR-bypassing target with usable ROP gadgets.)' bold black)" elif is_set LEGACY_VSYSCALL_EMULATE; then - echo -n "- "; wrap_good "CONFIG_LEGACY_VSYSCALL_EMULATE" 'enabled' + printf -- '- ' + wrap_good "CONFIG_LEGACY_VSYSCALL_EMULATE" 'enabled' elif is_set LEGACY_VSYSCALL_NONE; then - echo -n "- "; wrap_bad "CONFIG_LEGACY_VSYSCALL_NONE" 'enabled' + printf -- '- ' + wrap_bad "CONFIG_LEGACY_VSYSCALL_NONE" 'enabled' echo " $(wrap_color '(containers using eglibc <= 2.13 will not work. Switch to' bold black)" echo " $(wrap_color ' "CONFIG_VSYSCALL_[NATIVE|EMULATE]" or use "vsyscall=[native|emulate]"' bold black)" echo " $(wrap_color ' on kernel command line. Note that this will disable ASLR for the,' bold black)" @@ -245,31 +294,38 @@ echo 'Optional Features:' fi } -if [ "$kernelMajor" -lt 4 ] || [ "$kernelMajor" -eq 4 -a "$kernelMinor" -le 5 ]; then +if [ "$kernelMajor" -lt 4 ] || ([ "$kernelMajor" -eq 4 ] && [ "$kernelMinor" -le 5 ]); then check_flags MEMCG_KMEM fi -if [ "$kernelMajor" -lt 3 ] || [ "$kernelMajor" -eq 3 -a "$kernelMinor" -le 18 ]; then +if [ "$kernelMajor" -lt 3 ] || ([ "$kernelMajor" -eq 3 ] && [ "$kernelMinor" -le 18 ]); then check_flags RESOURCE_COUNTERS fi -if [ "$kernelMajor" -lt 3 ] || [ "$kernelMajor" -eq 3 -a "$kernelMinor" -le 13 ]; then +if [ "$kernelMajor" -lt 3 ] || ([ "$kernelMajor" -eq 3 ] && [ "$kernelMinor" -le 13 ]); then netprio=NETPRIO_CGROUP else netprio=CGROUP_NET_PRIO fi -flags=( - BLK_CGROUP BLK_DEV_THROTTLING IOSCHED_CFQ CFQ_GROUP_IOSCHED - CGROUP_PERF - CGROUP_HUGETLB - NET_CLS_CGROUP $netprio - CFS_BANDWIDTH FAIR_GROUP_SCHED RT_GROUP_SCHED - IP_VS - IP_VS_NFCT - IP_VS_RR -) -check_flags "${flags[@]}" +if [ "$kernelMajor" -lt 5 ]; then + check_flags IOSCHED_CFQ CFQ_GROUP_IOSCHED +fi + +check_flags \ + BLK_CGROUP BLK_DEV_THROTTLING \ + CGROUP_PERF \ + CGROUP_HUGETLB \ + NET_CLS_CGROUP $netprio \ + CFS_BANDWIDTH FAIR_GROUP_SCHED RT_GROUP_SCHED \ + IP_NF_TARGET_REDIRECT \ + IP_VS \ + IP_VS_NFCT \ + IP_VS_PROTO_TCP \ + IP_VS_PROTO_UDP \ + IP_VS_RR \ + SECURITY_SELINUX \ + SECURITY_APPARMOR if ! is_set EXT4_USE_FOR_EXT2; then check_flags EXT3_FS EXT3_FS_XATTR EXT3_FS_POSIX_ACL EXT3_FS_SECURITY @@ -288,16 +344,19 @@ if ! is_set EXT4_FS || ! is_set EXT4_FS_POSIX_ACL || ! is_set EXT4_FS_SECURITY; fi echo '- Network Drivers:' -echo ' - "'$(wrap_color 'overlay' blue)'":' -check_flags VXLAN | sed 's/^/ /' +echo " - \"$(wrap_color 'overlay' blue)\":" +check_flags VXLAN BRIDGE_VLAN_FILTERING | sed 's/^/ /' echo ' Optional (for encrypted networks):' check_flags CRYPTO CRYPTO_AEAD CRYPTO_GCM CRYPTO_SEQIV CRYPTO_GHASH \ - XFRM XFRM_USER XFRM_ALGO INET_ESP INET_XFRM_MODE_TRANSPORT | sed 's/^/ /' -echo ' - "'$(wrap_color 'ipvlan' blue)'":' + XFRM XFRM_USER XFRM_ALGO INET_ESP | sed 's/^/ /' +if [ "$kernelMajor" -lt 5 ] || [ "$kernelMajor" -eq 5 -a "$kernelMinor" -le 3 ]; then + check_flags INET_XFRM_MODE_TRANSPORT | sed 's/^/ /' +fi +echo " - \"$(wrap_color 'ipvlan' blue)\":" check_flags IPVLAN | sed 's/^/ /' -echo ' - "'$(wrap_color 'macvlan' blue)'":' +echo " - \"$(wrap_color 'macvlan' blue)\":" check_flags MACVLAN DUMMY | sed 's/^/ /' -echo ' - "'$(wrap_color 'ftp,tftp client in container' blue)'":' +echo " - \"$(wrap_color 'ftp,tftp client in container' blue)\":" check_flags NF_NAT_FTP NF_CONNTRACK_FTP NF_NAT_TFTP NF_CONNTRACK_TFTP | sed 's/^/ /' # only fail if no storage drivers available @@ -306,7 +365,7 @@ EXITCODE=0 STORAGE=1 echo '- Storage Drivers:' -echo ' - "'$(wrap_color 'aufs' blue)'":' +echo " - \"$(wrap_color 'aufs' blue)\":" check_flags AUFS_FS | sed 's/^/ /' if ! is_set AUFS_FS && grep -q aufs /proc/filesystems; then echo " $(wrap_color '(note that some kernels include AUFS patches but not the AUFS_FS flag)' bold black)" @@ -314,26 +373,29 @@ fi [ "$EXITCODE" = 0 ] && STORAGE=0 EXITCODE=0 -echo ' - "'$(wrap_color 'btrfs' blue)'":' +echo " - \"$(wrap_color 'btrfs' blue)\":" check_flags BTRFS_FS | sed 's/^/ /' check_flags BTRFS_FS_POSIX_ACL | sed 's/^/ /' [ "$EXITCODE" = 0 ] && STORAGE=0 EXITCODE=0 -echo ' - "'$(wrap_color 'devicemapper' blue)'":' +echo " - \"$(wrap_color 'devicemapper' blue)\":" check_flags BLK_DEV_DM DM_THIN_PROVISIONING | sed 's/^/ /' [ "$EXITCODE" = 0 ] && STORAGE=0 EXITCODE=0 -echo ' - "'$(wrap_color 'overlay' blue)'":' +echo " - \"$(wrap_color 'overlay' blue)\":" check_flags OVERLAY_FS | sed 's/^/ /' [ "$EXITCODE" = 0 ] && STORAGE=0 EXITCODE=0 -echo ' - "'$(wrap_color 'zfs' blue)'":' -echo -n " - "; check_device /dev/zfs -echo -n " - "; check_command zfs -echo -n " - "; check_command zpool +echo " - \"$(wrap_color 'zfs' blue)\":" +printf ' - ' +check_device /dev/zfs +printf ' - ' +check_command zfs +printf ' - ' +check_command zpool [ "$EXITCODE" = 0 ] && STORAGE=0 EXITCODE=0 @@ -342,14 +404,13 @@ EXITCODE=$CODE echo -check_limit_over() -{ - if [ $(cat "$1") -le "$2" ]; then - wrap_bad "- $1" "$(cat $1)" +check_limit_over() { + if [ "$(cat "$1")" -le "$2" ]; then + wrap_bad "- $1" "$(cat "$1")" wrap_color " This should be set to at least $2, for example set: sysctl -w kernel/keys/root_maxkeys=1000000" bold black EXITCODE=1 else - wrap_good "- $1" "$(cat $1)" + wrap_good "- $1" "$(cat "$1")" fi } diff --git a/contrib/desktop-integration/README.md b/contrib/desktop-integration/README.md deleted file mode 100644 index 85a01b9ee90fb..0000000000000 --- a/contrib/desktop-integration/README.md +++ /dev/null @@ -1,11 +0,0 @@ -Desktop Integration -=================== - -The ./contrib/desktop-integration contains examples of typical dockerized -desktop applications. - -Examples -======== - -* Chromium: ./chromium/Dockerfile shows a way to dockerize a common application -* Gparted: ./gparted/Dockerfile shows a way to dockerize a common application w devices diff --git a/contrib/desktop-integration/chromium/Dockerfile b/contrib/desktop-integration/chromium/Dockerfile deleted file mode 100644 index 187281644ff54..0000000000000 --- a/contrib/desktop-integration/chromium/Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -# VERSION: 0.1 -# DESCRIPTION: Create chromium container with its dependencies -# AUTHOR: Jessica Frazelle -# COMMENTS: -# This file describes how to build a Chromium container with all -# dependencies installed. It uses native X11 unix socket. -# Tested on Debian Jessie -# USAGE: -# # Download Chromium Dockerfile -# wget http://raw.githubusercontent.com/docker/docker/master/contrib/desktop-integration/chromium/Dockerfile -# -# # Build chromium image -# docker build -t chromium . -# -# # Run stateful data-on-host chromium. For ephemeral, remove -v /data/chromium:/data -# docker run -v /data/chromium:/data -v /tmp/.X11-unix:/tmp/.X11-unix \ -# -e DISPLAY=unix$DISPLAY chromium - -# # To run stateful dockerized data containers -# docker run --volumes-from chromium-data -v /tmp/.X11-unix:/tmp/.X11-unix \ -# -e DISPLAY=unix$DISPLAY chromium - -# Base docker image -FROM debian:jessie -LABEL maintainer Jessica Frazelle - -# Install Chromium -RUN apt-get update && apt-get install -y \ - chromium \ - chromium-l10n \ - libcanberra-gtk-module \ - libexif-dev \ - --no-install-recommends - -# Autorun chromium -CMD ["/usr/bin/chromium", "--no-sandbox", "--user-data-dir=/data"] diff --git a/contrib/desktop-integration/gparted/Dockerfile b/contrib/desktop-integration/gparted/Dockerfile deleted file mode 100644 index 8a9b646ee4b4f..0000000000000 --- a/contrib/desktop-integration/gparted/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -# VERSION: 0.1 -# DESCRIPTION: Create gparted container with its dependencies -# AUTHOR: Jessica Frazelle -# COMMENTS: -# This file describes how to build a gparted container with all -# dependencies installed. It uses native X11 unix socket. -# Tested on Debian Jessie -# USAGE: -# # Download gparted Dockerfile -# wget http://raw.githubusercontent.com/docker/docker/master/contrib/desktop-integration/gparted/Dockerfile -# -# # Build gparted image -# docker build -t gparted . -# -# docker run -v /tmp/.X11-unix:/tmp/.X11-unix \ -# --device=/dev/sda:/dev/sda \ -# -e DISPLAY=unix$DISPLAY gparted -# - -# Base docker image -FROM debian:jessie -LABEL maintainer Jessica Frazelle - -# Install Gparted and its dependencies -RUN apt-get update && apt-get install -y \ - gparted \ - libcanberra-gtk-module \ - --no-install-recommends - -# Autorun gparted -CMD ["/usr/sbin/gparted"] diff --git a/contrib/docker-device-tool/device_tool.go b/contrib/docker-device-tool/device_tool.go index d3ec46a8b4d2d..a9f3d720c3f17 100644 --- a/contrib/docker-device-tool/device_tool.go +++ b/contrib/docker-device-tool/device_tool.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package main @@ -38,7 +39,7 @@ func byteSizeFromString(arg string) (int64, error) { rest = strings.ToLower(strings.TrimSpace(rest)) - var multiplier int64 = 1 + var multiplier int64 switch rest { case "": multiplier = 1 diff --git a/contrib/docker-machine-install-bundle.sh b/contrib/docker-machine-install-bundle.sh deleted file mode 100755 index eff821799c716..0000000000000 --- a/contrib/docker-machine-install-bundle.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env bash -# -# This script installs the bundle to Docker Machine instances, for the purpose -# of testing the latest Docker with Swarm mode enabled. -# Do not use in production. -# -# Requirements (on host to run this script) -# - bash is installed -# - Docker Machine is installed -# - GNU tar is installed -# -# Requirements (on Docker machine instances) -# - Docker can be managed via one of `systemctl`, `service`, or `/etc/init.d/docker` -# -set -e -set -o pipefail - -errexit() { - echo "$1" - exit 1 -} - -BUNDLE="bundles/$(cat VERSION)" - -bundle_files(){ - # prefer dynbinary if exists - for f in dockerd docker-proxy; do - if [ -d $BUNDLE/dynbinary-daemon ]; then - echo $BUNDLE/dynbinary-daemon/$f - else - echo $BUNDLE/binary-daemon/$f - fi - done - for f in containerd ctr containerd-shim docker-init runc; do - echo $BUNDLE/binary-daemon/$f - done - if [ -d $BUNDLE/dynbinary-client ]; then - echo $BUNDLE/dynbinary-client/docker - else - echo $BUNDLE/binary-client/docker - fi -} - -control_docker(){ - m=$1; op=$2 - # NOTE: `docker-machine ssh $m sh -c "foo bar"` does not work - # (but `docker-machine ssh $m sh -c "foo\ bar"` works) - # Anyway we avoid using `sh -c` here for avoiding confusion - cat < /dev/null; then - systemctl $op docker -elif command -v service > /dev/null; then - service docker $op -elif [ -x /etc/init.d/docker ]; then - /etc/init.d/docker $op -else - echo "not sure how to control the docker daemon" - exit 1 -fi -EOF -} - -detect_prefix(){ - m=$1 - script='dirname $(dirname $(which dockerd))' - echo $script | docker-machine ssh $m sh -} - -install_to(){ - m=$1; shift; files=$@ - echo "$m: detecting docker" - prefix=$(detect_prefix $m) - echo "$m: detected docker on $prefix" - echo "$m: stopping docker" - control_docker $m stop - echo "$m: installing docker" - # NOTE: GNU tar is required because we use --transform here - # TODO: compression (should not be default) - tar ch --transform 's/.*\///' $files | docker-machine ssh $m sudo tar Cx $prefix/bin - echo "$m: starting docker" - control_docker $m start - echo "$m: done" -} - -check_prereq(){ - command -v docker-machine > /dev/null || errexit "docker-machine not installed" - ( tar --version | grep GNU > /dev/null ) || errexit "GNU tar not installed" -} - -case "$1" in - "install") - shift; machines=$@ - check_prereq - files=$(bundle_files) - echo "Files to be installed:" - for f in $files; do echo $f; done - pids=() - for m in $machines; do - install_to $m $files & - pids+=($!) - done - status=0 - for pid in ${pids[@]}; do - wait $pid || { status=$?; echo "background process $pid failed with exit status $status"; } - done - exit $status - ;; - *) - errexit "Usage: $0 install MACHINES" - ;; -esac diff --git a/contrib/dockerd-rootless-setuptool.sh b/contrib/dockerd-rootless-setuptool.sh new file mode 100755 index 0000000000000..6324a59507126 --- /dev/null +++ b/contrib/dockerd-rootless-setuptool.sh @@ -0,0 +1,491 @@ +#!/bin/sh +# dockerd-rootless-setuptool.sh: setup tool for dockerd-rootless.sh +# Needs to be executed as a non-root user. +# +# Typical usage: dockerd-rootless-setuptool.sh install --force +# +# Documentation: https://docs.docker.com/go/rootless/ +set -eu + +# utility functions +INFO() { + /bin/echo -e "\e[104m\e[97m[INFO]\e[49m\e[39m $@" +} + +WARNING() { + /bin/echo >&2 -e "\e[101m\e[97m[WARNING]\e[49m\e[39m $@" +} + +ERROR() { + /bin/echo >&2 -e "\e[101m\e[97m[ERROR]\e[49m\e[39m $@" +} + +# constants +DOCKERD_ROOTLESS_SH="dockerd-rootless.sh" +SYSTEMD_UNIT="docker.service" +CLI_CONTEXT="rootless" + +# CLI opt: --force +OPT_FORCE="" +# CLI opt: --skip-iptables +OPT_SKIP_IPTABLES="" + +# global vars +ARG0="$0" +DOCKERD_ROOTLESS_SH_FLAGS="" +BIN="" +SYSTEMD="" +CFG_DIR="" +XDG_RUNTIME_DIR_CREATED="" + +# run checks and also initialize global vars +init() { + # OS verification: Linux only + case "$(uname)" in + Linux) ;; + + *) + ERROR "Rootless Docker cannot be installed on $(uname)" + exit 1 + ;; + esac + + # User verification: deny running as root + if [ "$(id -u)" = "0" ]; then + ERROR "Refusing to install rootless Docker as the root user" + exit 1 + fi + + # set BIN + if ! BIN="$(command -v "$DOCKERD_ROOTLESS_SH" 2> /dev/null)"; then + ERROR "$DOCKERD_ROOTLESS_SH needs to be present under \$PATH" + exit 1 + fi + BIN=$(dirname "$BIN") + + # set SYSTEMD + if systemctl --user show-environment > /dev/null 2>&1; then + SYSTEMD=1 + fi + + # HOME verification + if [ -z "${HOME:-}" ] || [ ! -d "$HOME" ]; then + ERROR "HOME needs to be set" + exit 1 + fi + if [ ! -w "$HOME" ]; then + ERROR "HOME needs to be writable" + exit 1 + fi + + # set CFG_DIR + CFG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}" + + # Existing rootful docker verification + if [ -w /var/run/docker.sock ] && [ -z "$OPT_FORCE" ]; then + ERROR "Aborting because rootful Docker (/var/run/docker.sock) is running and accessible. Set --force to ignore." + exit 1 + fi + + # Validate XDG_RUNTIME_DIR and set XDG_RUNTIME_DIR_CREATED + if [ -z "${XDG_RUNTIME_DIR:-}" ] || [ ! -w "$XDG_RUNTIME_DIR" ]; then + if [ -n "$SYSTEMD" ]; then + ERROR "Aborting because systemd was detected but XDG_RUNTIME_DIR (\"$XDG_RUNTIME_DIR\") is not set, does not exist, or is not writable" + ERROR "Hint: this could happen if you changed users with 'su' or 'sudo'. To work around this:" + ERROR "- try again by first running with root privileges 'loginctl enable-linger ' where is the unprivileged user and export XDG_RUNTIME_DIR to the value of RuntimePath as shown by 'loginctl show-user '" + ERROR "- or simply log back in as the desired unprivileged user (ssh works for remote machines, machinectl shell works for local machines)" + exit 1 + fi + export XDG_RUNTIME_DIR="$HOME/.docker/run" + mkdir -p -m 700 "$XDG_RUNTIME_DIR" + XDG_RUNTIME_DIR_CREATED=1 + fi + + instructions="" + # instruction: uidmap dependency check + if ! command -v newuidmap > /dev/null 2>&1; then + if command -v apt-get > /dev/null 2>&1; then + instructions=$( + cat <<- EOI + ${instructions} + # Install newuidmap & newgidmap binaries + apt-get install -y uidmap + EOI + ) + elif command -v dnf > /dev/null 2>&1; then + instructions=$( + cat <<- EOI + ${instructions} + # Install newuidmap & newgidmap binaries + dnf install -y shadow-utils + EOI + ) + elif command -v yum > /dev/null 2>&1; then + instructions=$( + cat <<- EOI + ${instructions} + # Install newuidmap & newgidmap binaries + yum install -y shadow-utils + EOI + ) + else + ERROR "newuidmap binary not found. Please install with a package manager." + exit 1 + fi + fi + + # instruction: iptables dependency check + faced_iptables_error="" + if ! command -v iptables > /dev/null 2>&1 && [ ! -f /sbin/iptables ] && [ ! -f /usr/sbin/iptables ]; then + faced_iptables_error=1 + if [ -z "$OPT_SKIP_IPTABLES" ]; then + if command -v apt-get > /dev/null 2>&1; then + instructions=$( + cat <<- EOI + ${instructions} + # Install iptables + apt-get install -y iptables + EOI + ) + elif command -v dnf > /dev/null 2>&1; then + instructions=$( + cat <<- EOI + ${instructions} + # Install iptables + dnf install -y iptables + EOI + ) + elif command -v yum > /dev/null 2>&1; then + instructions=$( + cat <<- EOI + ${instructions} + # Install iptables + yum install -y iptables + EOI + ) + else + ERROR "iptables binary not found. Please install with a package manager." + exit 1 + fi + fi + fi + + # instruction: ip_tables module dependency check + if ! grep -q ip_tables /proc/modules 2> /dev/null && ! grep -q ip_tables /lib/modules/$(uname -r)/modules.builtin 2> /dev/null; then + faced_iptables_error=1 + if [ -z "$OPT_SKIP_IPTABLES" ]; then + instructions=$( + cat <<- EOI + ${instructions} + # Load ip_tables module + modprobe ip_tables + EOI + ) + fi + fi + + # set DOCKERD_ROOTLESS_SH_FLAGS + if [ -n "$faced_iptables_error" ] && [ -n "$OPT_SKIP_IPTABLES" ]; then + DOCKERD_ROOTLESS_SH_FLAGS="${DOCKERD_ROOTLESS_SH_FLAGS} --iptables=false" + fi + + # instruction: Debian and Arch require setting unprivileged_userns_clone + if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then + if [ "1" != "$(cat /proc/sys/kernel/unprivileged_userns_clone)" ]; then + instructions=$( + cat <<- EOI + ${instructions} + # Set kernel.unprivileged_userns_clone + cat < /etc/sysctl.d/50-rootless.conf + kernel.unprivileged_userns_clone = 1 + EOT + sysctl --system + EOI + ) + fi + fi + + # instruction: RHEL/CentOS 7 requires setting max_user_namespaces + if [ -f /proc/sys/user/max_user_namespaces ]; then + if [ "0" = "$(cat /proc/sys/user/max_user_namespaces)" ]; then + instructions=$( + cat <<- EOI + ${instructions} + # Set user.max_user_namespaces + cat < /etc/sysctl.d/51-rootless.conf + user.max_user_namespaces = 28633 + EOT + sysctl --system + EOI + ) + fi + fi + + # instructions: validate subuid/subgid files for current user + if ! grep -q "^$(id -un):\|^$(id -u):" /etc/subuid 2> /dev/null; then + instructions=$( + cat <<- EOI + ${instructions} + # Add subuid entry for $(id -un) + echo "$(id -un):100000:65536" >> /etc/subuid + EOI + ) + fi + if ! grep -q "^$(id -un):\|^$(id -u):" /etc/subgid 2> /dev/null; then + instructions=$( + cat <<- EOI + ${instructions} + # Add subgid entry for $(id -un) + echo "$(id -un):100000:65536" >> /etc/subgid + EOI + ) + fi + + # fail with instructions if requirements are not satisfied. + if [ -n "$instructions" ]; then + ERROR "Missing system requirements. Run the following commands to" + ERROR "install the requirements and run this tool again." + if [ -n "$faced_iptables_error" ] && [ -z "$OPT_SKIP_IPTABLES" ]; then + ERROR "Alternatively iptables checks can be disabled with --skip-iptables ." + fi + echo + echo "########## BEGIN ##########" + echo "sudo sh -eux < "${unit_file}" + [Unit] + Description=Docker Application Container Engine (Rootless) + Documentation=https://docs.docker.com/go/rootless/ + + [Service] + Environment=PATH=$BIN:/sbin:/usr/sbin:$PATH + ExecStart=$BIN/dockerd-rootless.sh $DOCKERD_ROOTLESS_SH_FLAGS + ExecReload=/bin/kill -s HUP \$MAINPID + TimeoutSec=0 + RestartSec=2 + Restart=always + StartLimitBurst=3 + StartLimitInterval=60s + LimitNOFILE=infinity + LimitNPROC=infinity + LimitCORE=infinity + TasksMax=infinity + Delegate=yes + Type=notify + NotifyAccess=all + KillMode=mixed + + [Install] + WantedBy=default.target + EOT + systemctl --user daemon-reload + fi + if ! systemctl --user --no-pager status "${SYSTEMD_UNIT}" > /dev/null 2>&1; then + INFO "starting systemd service ${SYSTEMD_UNIT}" + ( + set -x + if ! systemctl --user start "${SYSTEMD_UNIT}"; then + set +x + show_systemd_error + exit 1 + fi + sleep 3 + ) + fi + ( + set -x + if ! systemctl --user --no-pager --full status "${SYSTEMD_UNIT}"; then + set +x + show_systemd_error + exit 1 + fi + DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock" $BIN/docker version + systemctl --user enable "${SYSTEMD_UNIT}" + ) + INFO "Installed ${SYSTEMD_UNIT} successfully." + INFO "To control ${SYSTEMD_UNIT}, run: \`systemctl --user (start|stop|restart) ${SYSTEMD_UNIT}\`" + INFO "To run ${SYSTEMD_UNIT} on system startup, run: \`sudo loginctl enable-linger $(id -un)\`" + echo +} + +# install (non-systemd) +install_nonsystemd() { + INFO "systemd not detected, ${DOCKERD_ROOTLESS_SH} needs to be started manually:" + echo + echo "PATH=$BIN:/sbin:/usr/sbin:\$PATH ${DOCKERD_ROOTLESS_SH} ${DOCKERD_ROOTLESS_SH_FLAGS}" + echo +} + +cli_ctx_exists() { + name="$1" + "${BIN}/docker" context inspect -f "{{.Name}}" "${name}" > /dev/null 2>&1 +} + +cli_ctx_create() { + name="$1" + host="$2" + description="$3" + "${BIN}/docker" context create "${name}" --docker "host=${host}" --description "${description}" > /dev/null +} + +cli_ctx_rm() { + name="$1" + "${BIN}/docker" context rm -f "${name}" > /dev/null +} + +# CLI subcommand: "install" +cmd_entrypoint_install() { + # requirements are already checked in init() + if [ -z "$SYSTEMD" ]; then + install_nonsystemd + else + install_systemd + fi + + if cli_ctx_exists "${CLI_CONTEXT}"; then + INFO "CLI context \"${CLI_CONTEXT}\" already exists" + else + INFO "Creating CLI context \"${CLI_CONTEXT}\"" + cli_ctx_create "${CLI_CONTEXT}" "unix://${XDG_RUNTIME_DIR}/docker.sock" "Rootless mode" + fi + + echo + INFO "Make sure the following environment variables are set (or add them to ~/.bashrc):" + echo + if [ -n "$XDG_RUNTIME_DIR_CREATED" ]; then + echo "# WARNING: systemd not found. You have to remove XDG_RUNTIME_DIR manually on every logout." + echo "export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR}" + fi + echo "export PATH=${BIN}:\$PATH" + echo "export DOCKER_HOST=unix://${XDG_RUNTIME_DIR}/docker.sock" + echo + +} + +# CLI subcommand: "uninstall" +cmd_entrypoint_uninstall() { + # requirements are already checked in init() + if [ -z "$SYSTEMD" ]; then + INFO "systemd not detected, ${DOCKERD_ROOTLESS_SH} needs to be stopped manually:" + else + unit_file="${CFG_DIR}/systemd/user/${SYSTEMD_UNIT}" + ( + set -x + systemctl --user stop "${SYSTEMD_UNIT}" + ) || : + ( + set -x + systemctl --user disable "${SYSTEMD_UNIT}" + ) || : + rm -f "${unit_file}" + INFO "Uninstalled ${SYSTEMD_UNIT}" + fi + + if cli_ctx_exists "${CLI_CONTEXT}"; then + cli_ctx_rm "${CLI_CONTEXT}" + INFO "Deleted CLI context \"${CLI_CONTEXT}\"" + fi + + INFO "This uninstallation tool does NOT remove Docker binaries and data." + INFO "To remove data, run: \`$BIN/rootlesskit rm -rf $HOME/.local/share/docker\`" +} + +# text for --help +usage() { + echo "Usage: ${ARG0} [OPTIONS] COMMAND" + echo + echo "A setup tool for Rootless Docker (${DOCKERD_ROOTLESS_SH})." + echo + echo "Documentation: https://docs.docker.com/go/rootless/" + echo + echo "Options:" + echo " -f, --force Ignore rootful Docker (/var/run/docker.sock)" + echo " --skip-iptables Ignore missing iptables" + echo + echo "Commands:" + echo " check Check prerequisites" + echo " install Install systemd unit (if systemd is available) and show how to manage the service" + echo " uninstall Uninstall systemd unit" +} + +# parse CLI args +if ! args="$(getopt -o hf --long help,force,skip-iptables -n "$ARG0" -- "$@")"; then + usage + exit 1 +fi +eval set -- "$args" +while [ "$#" -gt 0 ]; do + arg="$1" + shift + case "$arg" in + -h | --help) + usage + exit 0 + ;; + -f | --force) + OPT_FORCE=1 + ;; + --skip-iptables) + OPT_SKIP_IPTABLES=1 + ;; + --) + break + ;; + *) + # XXX this means we missed something in our "getopt" arguments above! + ERROR "Scripting error, unknown argument '$arg' when parsing script arguments." + exit 1 + ;; + esac +done + +command="${1:-}" +if [ -z "$command" ]; then + ERROR "No command was specified. Run with --help to see the usage. Maybe you want to run \`$ARG0 install\`?" + exit 1 +fi + +if ! command -v "cmd_entrypoint_${command}" > /dev/null 2>&1; then + ERROR "Unknown command: ${command}. Run with --help to see the usage." + exit 1 +fi + +# main +init +"cmd_entrypoint_${command}" diff --git a/contrib/dockerd-rootless.sh b/contrib/dockerd-rootless.sh new file mode 100755 index 0000000000000..7f31016f6aef9 --- /dev/null +++ b/contrib/dockerd-rootless.sh @@ -0,0 +1,132 @@ +#!/bin/sh +# dockerd-rootless.sh executes dockerd in rootless mode. +# +# Usage: dockerd-rootless.sh [DOCKERD_OPTIONS] +# +# External dependencies: +# * newuidmap and newgidmap needs to be installed. +# * /etc/subuid and /etc/subgid needs to be configured for the current user. +# * Either one of slirp4netns (>= v0.4.0), VPNKit, lxc-user-nic needs to be installed. +# +# Recognized environment variables: +# * DOCKERD_ROOTLESS_ROOTLESSKIT_NET=(slirp4netns|vpnkit|lxc-user-nic): the rootlesskit network driver. Defaults to "slirp4netns" if slirp4netns (>= v0.4.0) is installed. Otherwise defaults to "vpnkit". +# * DOCKERD_ROOTLESS_ROOTLESSKIT_MTU=NUM: the MTU value for the rootlesskit network driver. Defaults to 65520 for slirp4netns, 1500 for other drivers. +# * DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=(builtin|slirp4netns): the rootlesskit port driver. Defaults to "builtin". +# * DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SANDBOX=(auto|true|false): whether to protect slirp4netns with a dedicated mount namespace. Defaults to "auto". +# * DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SECCOMP=(auto|true|false): whether to protect slirp4netns with seccomp. Defaults to "auto". +# +# See the documentation for the further information: https://docs.docker.com/go/rootless/ + +set -e -x +case "$1" in + "check" | "install" | "uninstall") + echo "Did you mean 'dockerd-rootless-setuptool.sh $@' ?" + exit 1 + ;; +esac +if ! [ -w "$XDG_RUNTIME_DIR" ]; then + echo "XDG_RUNTIME_DIR needs to be set and writable" + exit 1 +fi +if ! [ -d "$HOME" ]; then + echo "HOME needs to be set and exist." + exit 1 +fi + +rootlesskit="" +for f in docker-rootlesskit rootlesskit; do + if command -v $f > /dev/null 2>&1; then + rootlesskit=$f + break + fi +done +if [ -z "$rootlesskit" ]; then + echo "rootlesskit needs to be installed" + exit 1 +fi + +: "${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:=}" +: "${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:=}" +: "${DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER:=builtin}" +: "${DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SANDBOX:=auto}" +: "${DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SECCOMP:=auto}" +net=$DOCKERD_ROOTLESS_ROOTLESSKIT_NET +mtu=$DOCKERD_ROOTLESS_ROOTLESSKIT_MTU +if [ -z "$net" ]; then + if command -v slirp4netns > /dev/null 2>&1; then + # If --netns-type is present in --help, slirp4netns is >= v0.4.0. + if slirp4netns --help | grep -qw -- --netns-type; then + net=slirp4netns + if [ -z "$mtu" ]; then + mtu=65520 + fi + else + echo "slirp4netns found but seems older than v0.4.0. Falling back to VPNKit." + fi + fi + if [ -z "$net" ]; then + if command -v vpnkit > /dev/null 2>&1; then + net=vpnkit + else + echo "Either slirp4netns (>= v0.4.0) or vpnkit needs to be installed" + exit 1 + fi + fi +fi +if [ -z "$mtu" ]; then + mtu=1500 +fi + +if [ -z "$_DOCKERD_ROOTLESS_CHILD" ]; then + _DOCKERD_ROOTLESS_CHILD=1 + export _DOCKERD_ROOTLESS_CHILD + if [ "$(id -u)" = "0" ]; then + echo "This script must be executed as a non-privileged user" + exit 1 + fi + # `selinuxenabled` always returns false in RootlessKit child, so we execute `selinuxenabled` in the parent. + # https://github.com/rootless-containers/rootlesskit/issues/94 + if command -v selinuxenabled > /dev/null 2>&1 && selinuxenabled; then + _DOCKERD_ROOTLESS_SELINUX=1 + export _DOCKERD_ROOTLESS_SELINUX + fi + # Re-exec the script via RootlessKit, so as to create unprivileged {user,mount,network} namespaces. + # + # --copy-up allows removing/creating files in the directories by creating tmpfs and symlinks + # * /etc: copy-up is required so as to prevent `/etc/resolv.conf` in the + # namespace from being unexpectedly unmounted when `/etc/resolv.conf` is recreated on the host + # (by either systemd-networkd or NetworkManager) + # * /run: copy-up is required so that we can create /run/docker (hardcoded for plugins) in our namespace + exec $rootlesskit \ + --net=$net --mtu=$mtu \ + --slirp4netns-sandbox=$DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SANDBOX \ + --slirp4netns-seccomp=$DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SECCOMP \ + --disable-host-loopback --port-driver=$DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER \ + --copy-up=/etc --copy-up=/run \ + --propagation=rslave \ + $DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS \ + $0 $@ +else + [ "$_DOCKERD_ROOTLESS_CHILD" = 1 ] + # remove the symlinks for the existing files in the parent namespace if any, + # so that we can create our own files in our mount namespace. + rm -f /run/docker /run/containerd /run/xtables.lock + + if [ -n "$_DOCKERD_ROOTLESS_SELINUX" ]; then + # iptables requires /run in the child to be relabeled. The actual /run in the parent is unaffected. + # https://github.com/containers/podman/blob/e6fc34b71aa9d876b1218efe90e14f8b912b0603/libpod/networking_linux.go#L396-L401 + # https://github.com/moby/moby/issues/41230 + chcon system_u:object_r:iptables_var_run_t:s0 /run + fi + + if [ "$(stat -c %T -f /etc)" = "tmpfs" ] && [ -L "/etc/ssl" ]; then + # Workaround for "x509: certificate signed by unknown authority" on openSUSE Tumbleweed. + # https://github.com/rootless-containers/rootlesskit/issues/225 + realpath_etc_ssl=$(realpath /etc/ssl) + rm -f /etc/ssl + mkdir /etc/ssl + mount --rbind ${realpath_etc_ssl} /etc/ssl + fi + + exec dockerd $@ +fi diff --git a/contrib/dockerize-disk.sh b/contrib/dockerize-disk.sh index 444e243abe9d2..744fc79d0f2f4 100755 --- a/contrib/dockerize-disk.sh +++ b/contrib/dockerize-disk.sh @@ -2,21 +2,21 @@ set -e if ! command -v qemu-nbd &> /dev/null; then - echo >&2 'error: "qemu-nbd" not found!' - exit 1 + echo >&2 'error: "qemu-nbd" not found!' + exit 1 fi usage() { - echo "Convert disk image to docker image" - echo "" - echo "usage: $0 image-name disk-image-file [ base-image ]" - echo " ie: $0 cirros:0.3.3 cirros-0.3.3-x86_64-disk.img" - echo " $0 ubuntu:cloud ubuntu-14.04-server-cloudimg-amd64-disk1.img ubuntu:14.04" + echo "Convert disk image to docker image" + echo "" + echo "usage: $0 image-name disk-image-file [ base-image ]" + echo " ie: $0 cirros:0.3.3 cirros-0.3.3-x86_64-disk.img" + echo " $0 ubuntu:cloud ubuntu-14.04-server-cloudimg-amd64-disk1.img ubuntu:14.04" } if [ "$#" -lt 2 ]; then - usage - exit 1 + usage + exit 1 fi CURDIR=$(pwd) @@ -24,7 +24,7 @@ CURDIR=$(pwd) image_name="${1%:*}" image_tag="${1#*:}" if [ "$image_tag" == "$1" ]; then - image_tag="latest" + image_tag="latest" fi disk_image_file="$2" @@ -35,10 +35,10 @@ block_device=/dev/nbd0 builddir=$(mktemp -d) cleanup() { - umount "$builddir/disk_image" || true - umount "$builddir/workdir" || true - qemu-nbd -d $block_device &> /dev/null || true - rm -rf $builddir + umount "$builddir/disk_image" || true + umount "$builddir/workdir" || true + qemu-nbd -d $block_device &> /dev/null || true + rm -rf $builddir } trap cleanup EXIT @@ -55,18 +55,18 @@ base_image_mounts="" # Unpack base image if [ -n "$docker_base_image" ]; then - mkdir -p "$builddir/base" - docker pull "$docker_base_image" - docker save "$docker_base_image" | tar -xC "$builddir/base" - - image_id=$(docker inspect -f "{{.Id}}" "$docker_base_image") - while [ -n "$image_id" ]; do - mkdir -p "$builddir/base/$image_id/layer" - tar -xf "$builddir/base/$image_id/layer.tar" -C "$builddir/base/$image_id/layer" - - base_image_mounts="${base_image_mounts}:$builddir/base/$image_id/layer=ro+wh" - image_id=$(docker inspect -f "{{.Parent}}" "$image_id") - done + mkdir -p "$builddir/base" + docker pull "$docker_base_image" + docker save "$docker_base_image" | tar -xC "$builddir/base" + + image_id=$(docker inspect -f "{{.Id}}" "$docker_base_image") + while [ -n "$image_id" ]; do + mkdir -p "$builddir/base/$image_id/layer" + tar -xf "$builddir/base/$image_id/layer.tar" -C "$builddir/base/$image_id/layer" + + base_image_mounts="${base_image_mounts}:$builddir/base/$image_id/layer=ro+wh" + image_id=$(docker inspect -f "{{.Parent}}" "$image_id") + done fi # Mount work directory @@ -75,20 +75,20 @@ mount -t aufs -o "br=$builddir/diff=rw${base_image_mounts},dio,xino=/dev/shm/auf # Update files cd $builddir LC_ALL=C diff -rq disk_image workdir \ - | sed -re "s|Only in workdir(.*?): |DEL \1/|g;s|Only in disk_image(.*?): |ADD \1/|g;s|Files disk_image/(.+) and workdir/(.+) differ|UPDATE /\1|g" \ - | while read action entry; do - case "$action" in - ADD|UPDATE) - cp -a "disk_image$entry" "workdir$entry" - ;; - DEL) - rm -rf "workdir$entry" - ;; - *) - echo "Error: unknown diff line: $action $entry" >&2 - ;; - esac - done + | sed -re "s|Only in workdir(.*?): |DEL \1/|g;s|Only in disk_image(.*?): |ADD \1/|g;s|Files disk_image/(.+) and workdir/(.+) differ|UPDATE /\1|g" \ + | while read action entry; do + case "$action" in + ADD | UPDATE) + cp -a "disk_image$entry" "workdir$entry" + ;; + DEL) + rm -rf "workdir$entry" + ;; + *) + echo "Error: unknown diff line: $action $entry" >&2 + ;; + esac + done # Pack new image new_image_id="$(for i in $(seq 1 32); do printf "%02x" $(($RANDOM % 256)); done)" @@ -96,15 +96,15 @@ mkdir -p $builddir/result/$new_image_id cd diff tar -cf $builddir/result/$new_image_id/layer.tar * echo "1.0" > $builddir/result/$new_image_id/VERSION -cat > $builddir/result/$new_image_id/json <<-EOS -{ "docker_version": "1.4.1" -, "id": "$new_image_id" -, "created": "$(date -u +%Y-%m-%dT%H:%M:%S.%NZ)" +cat > $builddir/result/$new_image_id/json <<- EOS + { "docker_version": "1.4.1" + , "id": "$new_image_id" + , "created": "$(date -u +%Y-%m-%dT%H:%M:%S.%NZ)" EOS if [ -n "$docker_base_image" ]; then - image_id=$(docker inspect -f "{{.Id}}" "$docker_base_image") - echo ", \"parent\": \"$image_id\"" >> $builddir/result/$new_image_id/json + image_id=$(docker inspect -f "{{.Id}}" "$docker_base_image") + echo ", \"parent\": \"$image_id\"" >> $builddir/result/$new_image_id/json fi echo "}" >> $builddir/result/$new_image_id/json diff --git a/contrib/download-frozen-image-v1.sh b/contrib/download-frozen-image-v1.sh deleted file mode 100755 index 77c91d1f1b374..0000000000000 --- a/contrib/download-frozen-image-v1.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env bash -set -e - -# hello-world latest ef872312fe1b 3 months ago 910 B -# hello-world latest ef872312fe1bbc5e05aae626791a47ee9b032efa8f3bda39cc0be7b56bfe59b9 3 months ago 910 B - -# debian latest f6fab3b798be 10 weeks ago 85.1 MB -# debian latest f6fab3b798be3174f45aa1eb731f8182705555f89c9026d8c1ef230cbf8301dd 10 weeks ago 85.1 MB - -if ! command -v curl &> /dev/null; then - echo >&2 'error: "curl" not found!' - exit 1 -fi - -usage() { - echo "usage: $0 dir image[:tag][@image-id] ..." - echo " ie: $0 /tmp/hello-world hello-world" - echo " $0 /tmp/debian-jessie debian:jessie" - echo " $0 /tmp/old-hello-world hello-world@ef872312fe1bbc5e05aae626791a47ee9b032efa8f3bda39cc0be7b56bfe59b9" - echo " $0 /tmp/old-debian debian:latest@f6fab3b798be3174f45aa1eb731f8182705555f89c9026d8c1ef230cbf8301dd" - [ -z "$1" ] || exit "$1" -} - -dir="$1" # dir for building tar in -shift || usage 1 >&2 - -[ $# -gt 0 -a "$dir" ] || usage 2 >&2 -mkdir -p "$dir" - -# hacky workarounds for Bash 3 support (no associative arrays) -images=() -rm -f "$dir"/tags-*.tmp -# repositories[busybox]='"latest": "...", "ubuntu-14.04": "..."' - -while [ $# -gt 0 ]; do - imageTag="$1" - shift - image="${imageTag%%[:@]*}" - tag="${imageTag#*:}" - imageId="${tag##*@}" - [ "$imageId" != "$tag" ] || imageId= - [ "$tag" != "$imageTag" ] || tag='latest' - tag="${tag%@*}" - - imageFile="${image//\//_}" # "/" can't be in filenames :) - - token="$(curl -sSL -o /dev/null -D- -H 'X-Docker-Token: true' "https://index.docker.io/v1/repositories/$image/images" | tr -d '\r' | awk -F ': *' '$1 == "X-Docker-Token" { print $2 }')" - - if [ -z "$imageId" ]; then - imageId="$(curl -sSL -H "Authorization: Token $token" "https://registry-1.docker.io/v1/repositories/$image/tags/$tag")" - imageId="${imageId//\"/}" - fi - - ancestryJson="$(curl -sSL -H "Authorization: Token $token" "https://registry-1.docker.io/v1/images/$imageId/ancestry")" - if [ "${ancestryJson:0:1}" != '[' ]; then - echo >&2 "error: /v1/images/$imageId/ancestry returned something unexpected:" - echo >&2 " $ancestryJson" - exit 1 - fi - - IFS=',' - ancestry=( ${ancestryJson//[\[\] \"]/} ) - unset IFS - - if [ -s "$dir/tags-$imageFile.tmp" ]; then - echo -n ', ' >> "$dir/tags-$imageFile.tmp" - else - images=( "${images[@]}" "$image" ) - fi - echo -n '"'"$tag"'": "'"$imageId"'"' >> "$dir/tags-$imageFile.tmp" - - echo "Downloading '$imageTag' (${#ancestry[@]} layers)..." - for imageId in "${ancestry[@]}"; do - mkdir -p "$dir/$imageId" - echo '1.0' > "$dir/$imageId/VERSION" - - curl -sSL -H "Authorization: Token $token" "https://registry-1.docker.io/v1/images/$imageId/json" -o "$dir/$imageId/json" - - # TODO figure out why "-C -" doesn't work here - # "curl: (33) HTTP server doesn't seem to support byte ranges. Cannot resume." - # "HTTP/1.1 416 Requested Range Not Satisfiable" - if [ -f "$dir/$imageId/layer.tar" ]; then - # TODO hackpatch for no -C support :'( - echo "skipping existing ${imageId:0:12}" - continue - fi - curl -SL --progress -H "Authorization: Token $token" "https://registry-1.docker.io/v1/images/$imageId/layer" -o "$dir/$imageId/layer.tar" # -C - - done - echo -done - -echo -n '{' > "$dir/repositories" -firstImage=1 -for image in "${images[@]}"; do - imageFile="${image//\//_}" # "/" can't be in filenames :) - - [ "$firstImage" ] || echo -n ',' >> "$dir/repositories" - firstImage= - echo -n $'\n\t' >> "$dir/repositories" - echo -n '"'"$image"'": { '"$(cat "$dir/tags-$imageFile.tmp")"' }' >> "$dir/repositories" -done -echo -n $'\n}\n' >> "$dir/repositories" - -rm -f "$dir"/tags-*.tmp - -echo "Download of images into '$dir' complete." -echo "Use something like the following to load the result into a Docker daemon:" -echo " tar -cC '$dir' . | docker load" diff --git a/contrib/download-frozen-image-v2.sh b/contrib/download-frozen-image-v2.sh index 54b592307f88a..27a9b6c44bac7 100755 --- a/contrib/download-frozen-image-v2.sh +++ b/contrib/download-frozen-image-v2.sh @@ -6,14 +6,14 @@ set -eo pipefail # debian latest f6fab3b798be 10 weeks ago 85.1 MB # debian latest f6fab3b798be3174f45aa1eb731f8182705555f89c9026d8c1ef230cbf8301dd 10 weeks ago 85.1 MB -if ! command -v curl &> /dev/null; then - echo >&2 'error: "curl" not found!' - exit 1 -fi -if ! command -v jq &> /dev/null; then - echo >&2 'error: "jq" not found!' - exit 1 -fi + +# check if essential commands are in our PATH +for cmd in curl jq; do + if ! command -v $cmd &> /dev/null; then + echo >&2 "error: \"$cmd\" not found!" + exit 1 + fi +done usage() { echo "usage: $0 dir image[:tag][@digest] ..." @@ -24,7 +24,9 @@ usage() { dir="$1" # dir for building tar in shift || usage 1 >&2 -[ $# -gt 0 -a "$dir" ] || usage 2 >&2 +if ! [ $# -gt 0 ] && [ "$dir" ]; then + usage 2 >&2 +fi mkdir -p "$dir" # hacky workarounds for Bash 3 support (no associative arrays) @@ -34,13 +36,11 @@ manifestJsonEntries=() doNotGenerateManifestJson= # repositories[busybox]='"latest": "...", "ubuntu-14.04": "..."' -# bash v4 on Windows CI requires CRLF separator +# bash v4 on Windows CI requires CRLF separator... and linux doesn't seem to care either way newlineIFS=$'\n' -if [ "$(go env GOHOSTOS)" = 'windows' ]; then - major=$(echo ${BASH_VERSION%%[^0.9]} | cut -d. -f1) - if [ "$major" -ge 4 ]; then - newlineIFS=$'\r\n' - fi +major=$(echo "${BASH_VERSION%%[^0.9]}" | cut -d. -f1) +if [ "$major" -ge 4 ]; then + newlineIFS=$'\r\n' fi registryBase='https://registry-1.docker.io' @@ -49,13 +49,18 @@ authService='registry.docker.io' # https://github.com/moby/moby/issues/33700 fetch_blob() { - local token="$1"; shift - local image="$1"; shift - local digest="$1"; shift - local targetFile="$1"; shift - local curlArgs=( "$@" ) + local token="$1" + shift + local image="$1" + shift + local digest="$1" + shift + local targetFile="$1" + shift + local curlArgs=("$@") - local curlHeaders="$( + local curlHeaders + curlHeaders="$( curl -S "${curlArgs[@]}" \ -H "Authorization: Bearer $token" \ "$registryBase/v2/$image/blobs/$digest" \ @@ -63,10 +68,11 @@ fetch_blob() { -D- )" curlHeaders="$(echo "$curlHeaders" | tr -d '\r')" - if grep -qE "^HTTP/[0-9].[0-9] 3" <<<"$curlHeaders"; then + if grep -qE "^HTTP/[0-9].[0-9] 3" <<< "$curlHeaders"; then rm -f "$targetFile" - local blobRedirect="$(echo "$curlHeaders" | awk -F ': ' 'tolower($1) == "location" { print $2; exit }')" + local blobRedirect + blobRedirect="$(echo "$curlHeaders" | awk -F ': ' 'tolower($1) == "location" { print $2; exit }')" if [ -z "$blobRedirect" ]; then echo >&2 "error: failed fetching '$image' blob '$digest'" echo "$curlHeaders" | head -1 >&2 @@ -81,17 +87,21 @@ fetch_blob() { # handle 'application/vnd.docker.distribution.manifest.v2+json' manifest handle_single_manifest_v2() { - local manifestJson="$1"; shift + local manifestJson="$1" + shift - local configDigest="$(echo "$manifestJson" | jq --raw-output '.config.digest')" + local configDigest + configDigest="$(echo "$manifestJson" | jq --raw-output '.config.digest')" local imageId="${configDigest#*:}" # strip off "sha256:" local configFile="$imageId.json" fetch_blob "$token" "$image" "$configDigest" "$dir/$configFile" -s - local layersFs="$(echo "$manifestJson" | jq --raw-output --compact-output '.layers[]')" + local layersFs + layersFs="$(echo "$manifestJson" | jq --raw-output --compact-output '.layers[]')" local IFS="$newlineIFS" - local layers=( $layersFs ) + local layers + mapfile -t layers <<< "$layersFs" unset IFS echo "Downloading '$imageIdentifier' (${#layers[@]} layers)..." @@ -100,8 +110,10 @@ handle_single_manifest_v2() { for i in "${!layers[@]}"; do local layerMeta="${layers[$i]}" - local layerMediaType="$(echo "$layerMeta" | jq --raw-output '.mediaType')" - local layerDigest="$(echo "$layerMeta" | jq --raw-output '.digest')" + local layerMediaType + layerMediaType="$(echo "$layerMeta" | jq --raw-output '.mediaType')" + local layerDigest + layerDigest="$(echo "$layerMeta" | jq --raw-output '.digest')" # save the previous layer's ID local parentId="$layerId" @@ -113,10 +125,12 @@ handle_single_manifest_v2() { echo '1.0' > "$dir/$layerId/VERSION" if [ ! -s "$dir/$layerId/json" ]; then - local parentJson="$(printf ', parent: "%s"' "$parentId")" - local addJson="$(printf '{ id: "%s"%s }' "$layerId" "${parentId:+$parentJson}")" + local parentJson + parentJson="$(printf ', parent: "%s"' "$parentId")" + local addJson + addJson="$(printf '{ id: "%s"%s }' "$layerId" "${parentId:+$parentJson}")" # this starter JSON is taken directly from Docker's own "docker save" output for unimportant layers - jq "$addJson + ." > "$dir/$layerId/json" <<-'EOJSON' + jq "$addJson + ." > "$dir/$layerId/json" <<- 'EOJSON' { "created": "0001-01-01T00:00:00Z", "container_config": { @@ -145,7 +159,7 @@ handle_single_manifest_v2() { case "$layerMediaType" in application/vnd.docker.image.rootfs.diff.tar.gzip) local layerTar="$layerId/layer.tar" - layerFiles=( "${layerFiles[@]}" "$layerTar" ) + layerFiles=("${layerFiles[@]}" "$layerTar") # TODO figure out why "-C -" doesn't work here # "curl: (33) HTTP server doesn't seem to support byte ranges. Cannot resume." # "HTTP/1.1 416 Requested Range Not Satisfiable" @@ -154,8 +168,9 @@ handle_single_manifest_v2() { echo "skipping existing ${layerId:0:12}" continue fi - local token="$(curl -fsSL "$authBase/token?service=$authService&scope=repository:$image:pull" | jq --raw-output '.token')" - fetch_blob "$token" "$image" "$layerDigest" "$dir/$layerTar" --progress + local token + token="$(curl -fsSL "$authBase/token?service=$authService&scope=repository:$image:pull" | jq --raw-output '.token')" + fetch_blob "$token" "$image" "$layerDigest" "$dir/$layerTar" --progress-bar ;; *) @@ -169,17 +184,81 @@ handle_single_manifest_v2() { imageId="$layerId" # munge the top layer image manifest to have the appropriate image configuration for older daemons - local imageOldConfig="$(jq --raw-output --compact-output '{ id: .id } + if .parent then { parent: .parent } else {} end' "$dir/$imageId/json")" + local imageOldConfig + imageOldConfig="$(jq --raw-output --compact-output '{ id: .id } + if .parent then { parent: .parent } else {} end' "$dir/$imageId/json")" jq --raw-output "$imageOldConfig + del(.history, .rootfs)" "$dir/$configFile" > "$dir/$imageId/json" - local manifestJsonEntry="$( + local manifestJsonEntry + manifestJsonEntry="$( echo '{}' | jq --raw-output '. + { Config: "'"$configFile"'", RepoTags: ["'"${image#library\/}:$tag"'"], Layers: '"$(echo '[]' | jq --raw-output ".$(for layerFile in "${layerFiles[@]}"; do echo " + [ \"$layerFile\" ]"; done)")"' }' )" - manifestJsonEntries=( "${manifestJsonEntries[@]}" "$manifestJsonEntry" ) + manifestJsonEntries=("${manifestJsonEntries[@]}" "$manifestJsonEntry") +} + +get_target_arch() { + if [ -n "${TARGETARCH:-}" ]; then + echo "${TARGETARCH}" + return 0 + fi + + if type go > /dev/null; then + go env GOARCH + return 0 + fi + + if type dpkg > /dev/null; then + debArch="$(dpkg --print-architecture)" + case "${debArch}" in + armel | armhf) + echo "arm" + return 0 + ;; + *64el) + echo "${debArch%el}le" + return 0 + ;; + *) + echo "${debArch}" + return 0 + ;; + esac + fi + + if type uname > /dev/null; then + uArch="$(uname -m)" + case "${uArch}" in + x86_64) + echo amd64 + return 0 + ;; + arm | armv[0-9]*) + echo arm + return 0 + ;; + aarch64) + echo arm64 + return 0 + ;; + mips*) + echo >&2 "I see you are running on mips but I don't know how to determine endianness yet, so I cannot select a correct arch to fetch." + echo >&2 "Consider installing \"go\" on the system which I can use to determine the correct arch or specify it explictly by setting TARGETARCH" + exit 1 + ;; + *) + echo "${uArch}" + return 0 + ;; + esac + + fi + + # default value + echo >&2 "Unable to determine CPU arch, falling back to amd64. You can specify a target arch by setting TARGETARCH" + echo amd64 } while [ $# -gt 0 ]; do @@ -227,15 +306,16 @@ while [ $# -gt 0 ]; do application/vnd.docker.distribution.manifest.list.v2+json) layersFs="$(echo "$manifestJson" | jq --raw-output --compact-output '.manifests[]')" IFS="$newlineIFS" - layers=( $layersFs ) + mapfile -t layers <<< "$layersFs" unset IFS found="" + targetArch="$(get_target_arch)" # parse first level multi-arch manifest for i in "${!layers[@]}"; do layerMeta="${layers[$i]}" maniArch="$(echo "$layerMeta" | jq --raw-output '.platform.architecture')" - if [ "$maniArch" = "$(go env GOARCH)" ]; then + if [ "$maniArch" = "${targetArch}" ]; then digest="$(echo "$layerMeta" | jq --raw-output '.digest')" # get second level single manifest submanifestJson="$( @@ -273,7 +353,7 @@ while [ $# -gt 0 ]; do layersFs="$(echo "$manifestJson" | jq --raw-output '.fsLayers | .[] | .blobSum')" IFS="$newlineIFS" - layers=( $layersFs ) + mapfile -t layers <<< "$layersFs" unset IFS history="$(echo "$manifestJson" | jq '.history | [.[] | .v1Compatibility]')" @@ -299,7 +379,7 @@ while [ $# -gt 0 ]; do continue fi token="$(curl -fsSL "$authBase/token?service=$authService&scope=repository:$image:pull" | jq --raw-output '.token')" - fetch_blob "$token" "$image" "$imageLayer" "$dir/$layerId/layer.tar" --progress + fetch_blob "$token" "$image" "$imageLayer" "$dir/$layerId/layer.tar" --progress-bar done ;; @@ -314,7 +394,7 @@ while [ $# -gt 0 ]; do if [ -s "$dir/tags-$imageFile.tmp" ]; then echo -n ', ' >> "$dir/tags-$imageFile.tmp" else - images=( "${images[@]}" "$image" ) + images=("${images[@]}" "$image") fi echo -n '"'"$tag"'": "'"$imageId"'"' >> "$dir/tags-$imageFile.tmp" done diff --git a/contrib/gitdm/generate_aliases.sh b/contrib/gitdm/generate_aliases.sh index dfff5ff2048f6..f61b0ef8431c0 100755 --- a/contrib/gitdm/generate_aliases.sh +++ b/contrib/gitdm/generate_aliases.sh @@ -8,9 +8,9 @@ # $> ./generate_aliases > aliases # -cat $1 | \ - grep -v '^#' | \ - sed 's/^[^<]*<\([^>]*\)>/\1/' | \ - grep '<.*>' | sed -e 's/[<>]/ /g' | \ - awk '{if ($3 != "") { print $3" "$1 } else {print $2" "$1}}' | \ - sort | uniq +cat $1 \ + | grep -v '^#' \ + | sed 's/^[^<]*<\([^>]*\)>/\1/' \ + | grep '<.*>' | sed -e 's/[<>]/ /g' \ + | awk '{if ($3 != "") { print $3" "$1 } else {print $2" "$1}}' \ + | sort | uniq diff --git a/contrib/init/openrc/docker.confd b/contrib/init/openrc/docker.confd index 89183de46b9e7..cc599e6da4cce 100644 --- a/contrib/init/openrc/docker.confd +++ b/contrib/init/openrc/docker.confd @@ -16,6 +16,12 @@ # where docker's pid get stored #DOCKER_PIDFILE="/run/docker.pid" +# Settings for process limits (ulimit) +#DOCKER_ULIMIT="-c unlimited -n 1048576 -u unlimited" + +# seconds to wait for sending SIGTERM and SIGKILL signals when stopping docker +#DOCKER_RETRY="TERM/60/KILL/10" + # where the docker daemon itself is run from #DOCKERD_BINARY="/usr/bin/dockerd" diff --git a/contrib/init/openrc/docker.initd b/contrib/init/openrc/docker.initd index 6c968f607e9d2..3229223bad720 100644 --- a/contrib/init/openrc/docker.initd +++ b/contrib/init/openrc/docker.initd @@ -11,14 +11,18 @@ DOCKER_OUTFILE="${DOCKER_OUTFILE:-${DOCKER_LOGFILE}}" start_stop_daemon_args="--background \ --stderr \"${DOCKER_ERRFILE}\" --stdout \"${DOCKER_OUTFILE}\"" -start_pre() { - checkpath -f -m 0644 -o root:docker "$DOCKER_LOGFILE" +extra_started_commands="reload" + +rc_ulimit="${DOCKER_ULIMIT:--c unlimited -n 1048576 -u unlimited}" - ulimit -n 1048576 +retry="${DOCKER_RETRY:-TERM/60/KILL/10}" - # Having non-zero limits causes performance problems due to accounting overhead - # in the kernel. We recommend using cgroups to do container-local accounting. - ulimit -u unlimited +start_pre() { + checkpath -f -m 0644 -o root:docker "$DOCKER_LOGFILE" +} - return 0 +reload() { + ebegin "Reloading ${RC_SVCNAME}" + start-stop-daemon --signal HUP --pidfile "${pidfile}" + eend $? "Failed to stop ${RC_SVCNAME}" } diff --git a/contrib/init/systemd/REVIEWERS b/contrib/init/systemd/REVIEWERS deleted file mode 100644 index b9ba55b3fba91..0000000000000 --- a/contrib/init/systemd/REVIEWERS +++ /dev/null @@ -1,3 +0,0 @@ -Lokesh Mandvekar (@lsm5) -Brandon Philips (@philips) -Jessie Frazelle (@jfrazelle) diff --git a/contrib/init/systemd/docker.service b/contrib/init/systemd/docker.service index 517463172b36e..fab73b8632db9 100644 --- a/contrib/init/systemd/docker.service +++ b/contrib/init/systemd/docker.service @@ -1,8 +1,8 @@ [Unit] Description=Docker Application Container Engine Documentation=https://docs.docker.com -After=network-online.target docker.socket firewalld.service -Wants=network-online.target +After=network-online.target docker.socket firewalld.service containerd.service +Wants=network-online.target containerd.service Requires=docker.socket [Service] @@ -10,25 +10,38 @@ Type=notify # the default is not to use systemd for cgroups because the delegate issues still # exists and systemd currently does not support the cgroup feature set required # for containers run by docker -ExecStart=/usr/bin/dockerd -H fd:// +ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID -LimitNOFILE=1048576 +TimeoutStartSec=0 +RestartSec=2 +Restart=always + +# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229. +# Both the old, and new location are accepted by systemd 229 and up, so using the old location +# to make them work for either version of systemd. +StartLimitBurst=3 + +# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230. +# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make +# this option work for either version of systemd. +StartLimitInterval=60s + # Having non-zero Limit*s causes performance problems due to accounting overhead # in the kernel. We recommend using cgroups to do container-local accounting. +LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity -# Uncomment TasksMax if your systemd version supports it. -# Only systemd 226 and above support this version. -#TasksMax=infinity -TimeoutStartSec=0 + +# Comment TasksMax if your systemd version does not support it. +# Only systemd 226 and above support this option. +TasksMax=infinity + # set delegate yes so that systemd does not reset the cgroups of docker containers Delegate=yes + # kill only the docker process, not all processes in the cgroup KillMode=process -# restart the docker process if it exits prematurely -Restart=on-failure -StartLimitBurst=3 -StartLimitInterval=60s +OOMScoreAdjust=-500 [Install] WantedBy=multi-user.target diff --git a/contrib/init/systemd/docker.service.rpm b/contrib/init/systemd/docker.service.rpm deleted file mode 100644 index 6c60646b5680a..0000000000000 --- a/contrib/init/systemd/docker.service.rpm +++ /dev/null @@ -1,33 +0,0 @@ -[Unit] -Description=Docker Application Container Engine -Documentation=https://docs.docker.com -After=network-online.target firewalld.service -Wants=network-online.target - -[Service] -Type=notify -# the default is not to use systemd for cgroups because the delegate issues still -# exists and systemd currently does not support the cgroup feature set required -# for containers run by docker -ExecStart=/usr/bin/dockerd -ExecReload=/bin/kill -s HUP $MAINPID -# Having non-zero Limit*s causes performance problems due to accounting overhead -# in the kernel. We recommend using cgroups to do container-local accounting. -LimitNOFILE=infinity -LimitNPROC=infinity -LimitCORE=infinity -# Uncomment TasksMax if your systemd version supports it. -# Only systemd 226 and above support this version. -#TasksMax=infinity -TimeoutStartSec=0 -# set delegate yes so that systemd does not reset the cgroups of docker containers -Delegate=yes -# kill only the docker process, not all processes in the cgroup -KillMode=process -# restart the docker process if it exits prematurely -Restart=on-failure -StartLimitBurst=3 -StartLimitInterval=60s - -[Install] -WantedBy=multi-user.target diff --git a/contrib/init/systemd/docker.socket b/contrib/init/systemd/docker.socket index 7dd95098e4ad7..c76a23296ec0e 100644 --- a/contrib/init/systemd/docker.socket +++ b/contrib/init/systemd/docker.socket @@ -1,9 +1,10 @@ [Unit] Description=Docker Socket for the API -PartOf=docker.service [Socket] -ListenStream=/var/run/docker.sock +# If /var/run is not implemented as a symlink to /run, you may need to +# specify ListenStream=/var/run/docker.sock instead. +ListenStream=/run/docker.sock SocketMode=0660 SocketUser=root SocketGroup=docker diff --git a/contrib/init/sysvinit-debian/docker b/contrib/init/sysvinit-debian/docker index 9c8fa6be73e31..a2ba4f4b7d31a 100755 --- a/contrib/init/sysvinit-debian/docker +++ b/contrib/init/sysvinit-debian/docker @@ -46,7 +46,7 @@ fi check_init() { # see also init_is_upstart in /lib/lsb/init-functions (which isn't available in Ubuntu 12.04, or we'd use it directly) - if [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | grep -q upstart; then + if [ -x /sbin/initctl ] && /sbin/initctl version 2> /dev/null | grep -q upstart; then log_failure_msg "$DOCKER_DESC is managed via upstart, try using service $BASE $1" exit 1 fi @@ -85,7 +85,7 @@ cgroupfs_mount() { case "$1" in start) check_init - + fail_unless_root cgroupfs_mount @@ -110,9 +110,9 @@ case "$1" in --pidfile "$DOCKER_SSD_PIDFILE" \ --make-pidfile \ -- \ - -p "$DOCKER_PIDFILE" \ - $DOCKER_OPTS \ - >> "$DOCKER_LOGFILE" 2>&1 + -p "$DOCKER_PIDFILE" \ + $DOCKER_OPTS \ + >> "$DOCKER_LOGFILE" 2>&1 log_end_msg $? ;; @@ -131,7 +131,7 @@ case "$1" in restart) check_init fail_unless_root - docker_pid=`cat "$DOCKER_SSD_PIDFILE" 2>/dev/null` + docker_pid=$(cat "$DOCKER_SSD_PIDFILE" 2> /dev/null) [ -n "$docker_pid" ] \ && ps -p $docker_pid > /dev/null 2>&1 \ && $0 stop diff --git a/contrib/init/sysvinit-redhat/docker b/contrib/init/sysvinit-redhat/docker index df9b02a2a451f..7bcb2d88adf18 100755 --- a/contrib/init/sysvinit-redhat/docker +++ b/contrib/init/sysvinit-redhat/docker @@ -109,10 +109,9 @@ rh_status() { } rh_status_q() { - rh_status >/dev/null 2>&1 + rh_status > /dev/null 2>&1 } - check_for_cleanup() { if [ -f ${pidfile} ]; then /bin/ps -fp $(cat ${pidfile}) > /dev/null || rm ${pidfile} @@ -141,13 +140,14 @@ case "$1" in status) rh_status ;; - condrestart|try-restart) + condrestart | try-restart) rh_status_q || exit 0 restart ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" exit 2 + ;; esac exit $? diff --git a/contrib/init/upstart/REVIEWERS b/contrib/init/upstart/REVIEWERS deleted file mode 100644 index 03ee2dde3dae6..0000000000000 --- a/contrib/init/upstart/REVIEWERS +++ /dev/null @@ -1,2 +0,0 @@ -Tianon Gravi (@tianon) -Jessie Frazelle (@jfrazelle) diff --git a/contrib/mac-install-bundle.sh b/contrib/mac-install-bundle.sh index 2110d044d01f7..f51546e116fc5 100755 --- a/contrib/mac-install-bundle.sh +++ b/contrib/mac-install-bundle.sh @@ -20,26 +20,26 @@ DATABASE_KEY="$DATABASE/com.docker.driver.amd64-linux/bundle" [ -d "$DATABASE" ] || errexit "Docker for Mac must be installed for this script" case "$1" in -"install") - [ -d "$BUNDLE" ] || errexit "cannot find bundle $BUNDLE" - [ -e "$CLIENT_PATH" ] || errexit "you need to run make cross first" - [ -e "$BUNDLE/binary-daemon/dockerd" ] || errexit "you need to build binaries first" - [ -f "$BUNDLE/binary-client/docker" ] || errexit "you need to build binaries first" - git -C "$DATABASE" reset --hard >/dev/null - echo "$BUNDLE_PATH" > "$DATABASE_KEY" - git -C "$DATABASE" add "$DATABASE_KEY" - git -C "$DATABASE" commit -m "update bundle to $BUNDLE_PATH" - rm -f /usr/local/bin/docker - cp "$CLIENT_PATH" /usr/local/bin - echo "Bundle installed. Restart Docker to use. To uninstall, reset Docker to factory defaults." - ;; -"undo") - git -C "$DATABASE" reset --hard >/dev/null - [ -f "$DATABASE_KEY" ] || errexit "bundle not set" - git -C "$DATABASE" rm "$DATABASE_KEY" - git -C "$DATABASE" commit -m "remove bundle" - rm -f /usr/local/bin/docker - ln -s "$HOME/Library/Group Containers/group.com.docker/bin/docker" /usr/local/bin - echo "Bundle removed. Using dev versions may cause issues, a reset to factory defaults is recommended." - ;; + "install") + [ -d "$BUNDLE" ] || errexit "cannot find bundle $BUNDLE" + [ -e "$CLIENT_PATH" ] || errexit "you need to run make cross first" + [ -e "$BUNDLE/binary-daemon/dockerd" ] || errexit "you need to build binaries first" + [ -f "$BUNDLE/binary-client/docker" ] || errexit "you need to build binaries first" + git -C "$DATABASE" reset --hard > /dev/null + echo "$BUNDLE_PATH" > "$DATABASE_KEY" + git -C "$DATABASE" add "$DATABASE_KEY" + git -C "$DATABASE" commit -m "update bundle to $BUNDLE_PATH" + rm -f /usr/local/bin/docker + cp "$CLIENT_PATH" /usr/local/bin + echo "Bundle installed. Restart Docker to use. To uninstall, reset Docker to factory defaults." + ;; + "undo") + git -C "$DATABASE" reset --hard > /dev/null + [ -f "$DATABASE_KEY" ] || errexit "bundle not set" + git -C "$DATABASE" rm "$DATABASE_KEY" + git -C "$DATABASE" commit -m "remove bundle" + rm -f /usr/local/bin/docker + ln -s "$HOME/Library/Group Containers/group.com.docker/bin/docker" /usr/local/bin + echo "Bundle removed. Using dev versions may cause issues, a reset to factory defaults is recommended." + ;; esac diff --git a/contrib/mkimage-alpine.sh b/contrib/mkimage-alpine.sh index 16791a758d375..60bfc80dd9e16 100755 --- a/contrib/mkimage-alpine.sh +++ b/contrib/mkimage-alpine.sh @@ -19,13 +19,13 @@ tmp() { } apkv() { - curl -sSL $MAINREPO/$ARCH/APKINDEX.tar.gz | tar -Oxz | - grep --text '^P:apk-tools-static$' -A1 | tail -n1 | cut -d: -f2 + curl -sSL $MAINREPO/$ARCH/APKINDEX.tar.gz | tar -Oxz \ + | grep --text '^P:apk-tools-static$' -A1 | tail -n1 | cut -d: -f2 } getapk() { - curl -sSL $MAINREPO/$ARCH/apk-tools-static-$(apkv).apk | - tar -xz -C $TMP sbin/apk.static + curl -sSL $MAINREPO/$ARCH/apk-tools-static-$(apkv).apk \ + | tar -xz -C $TMP sbin/apk.static } mkbase() { diff --git a/contrib/mkimage-arch.sh b/contrib/mkimage-arch.sh index f941177122be1..a618f2ccab45f 100755 --- a/contrib/mkimage-arch.sh +++ b/contrib/mkimage-arch.sh @@ -4,54 +4,82 @@ # requires root set -e -hash pacstrap &>/dev/null || { +# reset umask to default +umask 022 + +hash pacstrap &> /dev/null || { echo "Could not find pacstrap. Run pacman -S arch-install-scripts" exit 1 } -hash expect &>/dev/null || { +hash expect &> /dev/null || { echo "Could not find expect. Run pacman -S expect" exit 1 } - export LANG="C.UTF-8" ROOTFS=$(mktemp -d ${TMPDIR:-/var/tmp}/rootfs-archlinux-XXXXXXXXXX) chmod 755 $ROOTFS +# required packages +PKGREQUIRED=( + bash + haveged + pacman + pacman-mirrorlist +) + # packages to ignore for space savings PKGIGNORE=( - cryptsetup - device-mapper - dhcpcd - iproute2 - jfsutils - linux - lvm2 - man-db - man-pages - mdadm - nano - netctl - openresolv - pciutils - pcmciautils - reiserfsprogs - s-nail - systemd-sysvcompat - usbutils - vi - xfsprogs + dhcpcd + diffutils + file + inetutils + iproute2 + iputils + jfsutils + licenses + linux + linux-firmware + lvm2 + man-db + man-pages + mdadm + nano + netctl + openresolv + pciutils + pcmciautils + psmisc + reiserfsprogs + s-nail + sysfsutils + systemd-sysvcompat + usbutils + vi + which + xfsprogs ) + +PKGREMOVE=( + gawk + haveged + less + linux-libre + linux-libre-firmware +) + +PKGREQUIRED="${PKGREQUIRED[*]}" IFS=',' PKGIGNORE="${PKGIGNORE[*]}" unset IFS +PKGREMOVE="${PKGREMOVE[*]}" arch="$(uname -m)" case "$arch" in armv*) - if pacman -Q archlinuxarm-keyring >/dev/null 2>&1; then + if pacman -Q archlinuxarm-keyring > /dev/null 2>&1; then pacman-key --init pacman-key --populate archlinuxarm else @@ -79,7 +107,7 @@ esac export PACMAN_MIRRORLIST -expect < /dev/null 2>&1; then pacman -Rs --noconfirm \$pkg; fi; done" echo 'en_US.UTF-8 UTF-8' > $ROOTFS/etc/locale.gen arch-chroot $ROOTFS locale-gen -arch-chroot $ROOTFS /bin/sh -c 'echo $PACMAN_MIRRORLIST > /etc/pacman.d/mirrorlist' # udev doesn't work in containers, rebuild /dev DEV=$ROOTFS/dev diff --git a/contrib/mkimage-crux.sh b/contrib/mkimage-crux.sh index 3f0bdcae3c564..cb4fc23b0c261 100755 --- a/contrib/mkimage-crux.sh +++ b/contrib/mkimage-crux.sh @@ -5,9 +5,9 @@ set -e -die () { - echo >&2 "$@" - exit 1 +die() { + echo >&2 "$@" + exit 1 } [ "$#" -eq 1 ] || die "1 argument(s) required, $# provided. Usage: ./mkimage-crux.sh /path/to/iso" @@ -33,15 +33,15 @@ export PATH="$TMP/usr/bin:$PATH" mkdir -p $ROOTFS/var/lib/pkg touch $ROOTFS/var/lib/pkg/db for pkg in $CRUX/crux/core/*; do - pkgadd -r $ROOTFS $pkg + pkgadd -r $ROOTFS $pkg done # Remove agetty and inittab config if (grep agetty ${ROOTFS}/etc/inittab 2>&1 > /dev/null); then - echo "Removing agetty from /etc/inittab ..." - chroot ${ROOTFS} sed -i -e "/agetty/d" /etc/inittab - chroot ${ROOTFS} sed -i -e "/shutdown/d" /etc/inittab - chroot ${ROOTFS} sed -i -e "/^$/N;/^\n$/d" /etc/inittab + echo "Removing agetty from /etc/inittab ..." + chroot ${ROOTFS} sed -i -e "/agetty/d" /etc/inittab + chroot ${ROOTFS} sed -i -e "/shutdown/d" /etc/inittab + chroot ${ROOTFS} sed -i -e "/^$/N;/^\n$/d" /etc/inittab fi # Remove kernel source diff --git a/contrib/mkimage-yum.sh b/contrib/mkimage-yum.sh index 901280451b211..f36c82fbfe348 100755 --- a/contrib/mkimage-yum.sh +++ b/contrib/mkimage-yum.sh @@ -3,23 +3,24 @@ # Create a base CentOS Docker image. # # This script is useful on systems with yum installed (e.g., building -# a CentOS image on CentOS). See contrib/mkimage-rinse.sh for a way -# to build CentOS images on other systems. +# a CentOS image on CentOS). set -e usage() { - cat < OPTIONS: -p "" The list of packages to install in the container. - The default is blank. + The default is blank. Can use multiple times. -g "" The groups of packages to install in the container. - The default is "Core". + The default is "Core". Can use multiple times. -y The path to the yum config to install packages from. The default is /etc/yum.conf for Centos/RHEL and /etc/dnf/dnf.conf for Fedora + -t Specify Tag information. + default is reffered at /etc/{redhat,system}-release EOOPTS - exit 1 + exit 1 } # option defaults @@ -28,32 +29,43 @@ if [ -f /etc/dnf/dnf.conf ] && command -v dnf &> /dev/null; then yum_config=/etc/dnf/dnf.conf alias yum=dnf fi -install_groups="Core" -while getopts ":y:p:g:h" opt; do - case $opt in - y) - yum_config=$OPTARG - ;; - h) - usage - ;; - p) - install_packages="$OPTARG" - ;; - g) - install_groups="$OPTARG" - ;; - \?) - echo "Invalid option: -$OPTARG" - usage - ;; - esac +# for names with spaces, use double quotes (") as install_groups=('Core' '"Compute Node"') +install_groups=() +install_packages=() +version= +while getopts ":y:p:g:t:h" opt; do + case $opt in + y) + yum_config=$OPTARG + ;; + h) + usage + ;; + p) + install_packages+=("$OPTARG") + ;; + g) + install_groups+=("$OPTARG") + ;; + t) + version="$OPTARG" + ;; + \?) + echo "Invalid option: -$OPTARG" + usage + ;; + esac done shift $((OPTIND - 1)) name=$1 if [[ -z $name ]]; then - usage + usage +fi + +# default to Core group if not specified otherwise +if [ ${#install_groups[*]} -eq 0 ]; then + install_groups=('Core') fi target=$(mktemp -d --tmpdir $(basename $0).XXXXXX) @@ -78,21 +90,19 @@ if [ -d /etc/yum/vars ]; then cp -a /etc/yum/vars "$target"/etc/yum/ fi -if [[ -n "$install_groups" ]]; -then - yum -c "$yum_config" --installroot="$target" --releasever=/ --setopt=tsflags=nodocs \ - --setopt=group_package_types=mandatory -y groupinstall "$install_groups" +if [[ -n "$install_groups" ]]; then + yum -c "$yum_config" --installroot="$target" --releasever=/ --setopt=tsflags=nodocs \ + --setopt=group_package_types=mandatory -y groupinstall "${install_groups[@]}" fi -if [[ -n "$install_packages" ]]; -then - yum -c "$yum_config" --installroot="$target" --releasever=/ --setopt=tsflags=nodocs \ - --setopt=group_package_types=mandatory -y install "$install_packages" +if [[ -n "$install_packages" ]]; then + yum -c "$yum_config" --installroot="$target" --releasever=/ --setopt=tsflags=nodocs \ + --setopt=group_package_types=mandatory -y install "${install_packages[@]}" fi yum -c "$yum_config" --installroot="$target" -y clean all -cat > "$target"/etc/sysconfig/network < "$target"/etc/sysconfig/network << EOF NETWORKING=yes HOSTNAME=localhost.localdomain EOF @@ -115,18 +125,18 @@ rm -rf "$target"/sbin/sln rm -rf "$target"/etc/ld.so.cache "$target"/var/cache/ldconfig mkdir -p --mode=0755 "$target"/var/cache/ldconfig -version= -for file in "$target"/etc/{redhat,system}-release -do - if [ -r "$file" ]; then - version="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' "$file")" - break - fi -done +if [ -z "$version" ]; then + for file in "$target"/etc/{redhat,system}-release; do + if [ -r "$file" ]; then + version="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' "$file")" + break + fi + done +fi if [ -z "$version" ]; then - echo >&2 "warning: cannot autodetect OS version, using '$name' as tag" - version=$name + echo >&2 "warning: cannot autodetect OS version, using '$name' as tag" + version=$name fi tar --numeric-owner -c -C "$target" . | docker import - $name:$version diff --git a/contrib/mkimage.sh b/contrib/mkimage.sh deleted file mode 100755 index ae05d139c3043..0000000000000 --- a/contrib/mkimage.sh +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env bash -set -e - -mkimg="$(basename "$0")" - -usage() { - echo >&2 "usage: $mkimg [-d dir] [-t tag] [--compression algo| --no-compression] script [script-args]" - echo >&2 " ie: $mkimg -t someuser/debian debootstrap --variant=minbase jessie" - echo >&2 " $mkimg -t someuser/ubuntu debootstrap --include=ubuntu-minimal --components=main,universe trusty" - echo >&2 " $mkimg -t someuser/busybox busybox-static" - echo >&2 " $mkimg -t someuser/centos:5 rinse --distribution centos-5" - echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4" - echo >&2 " $mkimg -t someuser/mageia:4 mageia-urpmi --version=4 --mirror=http://somemirror/" - exit 1 -} - -scriptDir="$(dirname "$(readlink -f "$BASH_SOURCE")")/mkimage" - -os= -os=$(uname -o) - -optTemp=$(getopt --options '+d:t:c:hC' --longoptions 'dir:,tag:,compression:,no-compression,help' --name "$mkimg" -- "$@") -eval set -- "$optTemp" -unset optTemp - -dir= -tag= -compression="auto" -while true; do - case "$1" in - -d|--dir) dir="$2" ; shift 2 ;; - -t|--tag) tag="$2" ; shift 2 ;; - --compression) compression="$2" ; shift 2 ;; - --no-compression) compression="none" ; shift 1 ;; - -h|--help) usage ;; - --) shift ; break ;; - esac -done - -script="$1" -[ "$script" ] || usage -shift - -if [ "$compression" == 'auto' ] || [ -z "$compression" ] -then - compression='xz' -fi - -[ "$compression" == 'none' ] && compression='' - -if [ ! -x "$scriptDir/$script" ]; then - echo >&2 "error: $script does not exist or is not executable" - echo >&2 " see $scriptDir for possible scripts" - exit 1 -fi - -# don't mistake common scripts like .febootstrap-minimize as image-creators -if [[ "$script" == .* ]]; then - echo >&2 "error: $script is a script helper, not a script" - echo >&2 " see $scriptDir for possible scripts" - exit 1 -fi - -delDir= -if [ -z "$dir" ]; then - dir="$(mktemp -d ${TMPDIR:-/var/tmp}/docker-mkimage.XXXXXXXXXX)" - delDir=1 -fi - -rootfsDir="$dir/rootfs" -( set -x; mkdir -p "$rootfsDir" ) - -# pass all remaining arguments to $script -"$scriptDir/$script" "$rootfsDir" "$@" - -# Docker mounts tmpfs at /dev and procfs at /proc so we can remove them -rm -rf "$rootfsDir/dev" "$rootfsDir/proc" -mkdir -p "$rootfsDir/dev" "$rootfsDir/proc" - -# make sure /etc/resolv.conf has something useful in it -mkdir -p "$rootfsDir/etc" -cat > "$rootfsDir/etc/resolv.conf" <<'EOF' -nameserver 8.8.8.8 -nameserver 8.8.4.4 -EOF - -tarFile="$dir/rootfs.tar${compression:+.$compression}" -touch "$tarFile" - -( - set -x - tar --numeric-owner --create --auto-compress --file "$tarFile" --directory "$rootfsDir" --transform='s,^./,,' . -) - -echo >&2 "+ cat > '$dir/Dockerfile'" -cat > "$dir/Dockerfile" <> "$dir/Dockerfile" ) - break - fi -done - -( set -x; rm -rf "$rootfsDir" ) - -if [ "$tag" ]; then - ( set -x; docker build -t "$tag" "$dir" ) -elif [ "$delDir" ]; then - # if we didn't specify a tag and we're going to delete our dir, let's just build an untagged image so that we did _something_ - ( set -x; docker build "$dir" ) -fi - -if [ "$delDir" ]; then - ( set -x; rm -rf "$dir" ) -fi diff --git a/contrib/mkimage/.febootstrap-minimize b/contrib/mkimage/.febootstrap-minimize deleted file mode 100755 index 7749e63fb0828..0000000000000 --- a/contrib/mkimage/.febootstrap-minimize +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -set -e - -rootfsDir="$1" -shift - -( - cd "$rootfsDir" - - # effectively: febootstrap-minimize --keep-zoneinfo --keep-rpmdb --keep-services "$target" - # locales - rm -rf usr/{{lib,share}/locale,{lib,lib64}/gconv,bin/localedef,sbin/build-locale-archive} - # docs and man pages - rm -rf usr/share/{man,doc,info,gnome/help} - # cracklib - rm -rf usr/share/cracklib - # i18n - rm -rf usr/share/i18n - # yum cache - rm -rf var/cache/yum - mkdir -p --mode=0755 var/cache/yum - # sln - rm -rf sbin/sln - # ldconfig - #rm -rf sbin/ldconfig - rm -rf etc/ld.so.cache var/cache/ldconfig - mkdir -p --mode=0755 var/cache/ldconfig -) diff --git a/contrib/mkimage/busybox-static b/contrib/mkimage/busybox-static deleted file mode 100755 index e15322b49dba4..0000000000000 --- a/contrib/mkimage/busybox-static +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -set -e - -rootfsDir="$1" -shift - -busybox="$(which busybox 2>/dev/null || true)" -if [ -z "$busybox" ]; then - echo >&2 'error: busybox: not found' - echo >&2 ' install it with your distribution "busybox-static" package' - exit 1 -fi -if ! ldd "$busybox" 2>&1 | grep -q 'not a dynamic executable'; then - echo >&2 "error: '$busybox' appears to be a dynamic executable" - echo >&2 ' you should install your distribution "busybox-static" package instead' - exit 1 -fi - -mkdir -p "$rootfsDir/bin" -rm -f "$rootfsDir/bin/busybox" # just in case -cp "$busybox" "$rootfsDir/bin/busybox" - -( - cd "$rootfsDir" - - IFS=$'\n' - modules=( $(bin/busybox --list-modules) ) - unset IFS - - for module in "${modules[@]}"; do - mkdir -p "$(dirname "$module")" - ln -sf /bin/busybox "$module" - done -) diff --git a/contrib/mkimage/debootstrap b/contrib/mkimage/debootstrap deleted file mode 100755 index 9f7d8987ad2a0..0000000000000 --- a/contrib/mkimage/debootstrap +++ /dev/null @@ -1,251 +0,0 @@ -#!/usr/bin/env bash -set -e - -mkimgdeb="$(basename "$0")" -mkimg="$(dirname "$0").sh" - -usage() { - echo >&2 "usage: $mkimgdeb rootfsDir suite [debootstrap-args]" - echo >&2 " note: $mkimgdeb meant to be used from $mkimg" - exit 1 -} - -rootfsDir="$1" -if [ -z "$rootfsDir" ]; then - echo >&2 "error: rootfsDir is missing" - echo >&2 - usage -fi -shift - -# we have to do a little fancy footwork to make sure "rootfsDir" becomes the second non-option argument to debootstrap - -before=() -while [ $# -gt 0 ] && [[ "$1" == -* ]]; do - before+=( "$1" ) - shift -done - -suite="$1" -if [ -z "$suite" ]; then - echo >&2 "error: suite is missing" - echo >&2 - usage -fi -shift - -# get path to "chroot" in our current PATH -chrootPath="$(type -P chroot || :)" -if [ -z "$chrootPath" ]; then - echo >&2 "error: chroot not found. Are you root?" - echo >&2 - usage -fi - -rootfs_chroot() { - # "chroot" doesn't set PATH, so we need to set it explicitly to something our new debootstrap chroot can use appropriately! - - # set PATH and chroot away! - PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' \ - "$chrootPath" "$rootfsDir" "$@" -} - -# allow for DEBOOTSTRAP=qemu-debootstrap ./mkimage.sh ... -: ${DEBOOTSTRAP:=debootstrap} - -( - set -x - $DEBOOTSTRAP "${before[@]}" "$suite" "$rootfsDir" "$@" -) - -# now for some Docker-specific tweaks - -# prevent init scripts from running during install/update -echo >&2 "+ echo exit 101 > '$rootfsDir/usr/sbin/policy-rc.d'" -cat > "$rootfsDir/usr/sbin/policy-rc.d" <<-'EOF' - #!/bin/sh - - # For most Docker users, "apt-get install" only happens during "docker build", - # where starting services doesn't work and often fails in humorous ways. This - # prevents those failures by stopping the services from attempting to start. - - exit 101 -EOF -chmod +x "$rootfsDir/usr/sbin/policy-rc.d" - -# prevent upstart scripts from running during install/update -( - set -x - rootfs_chroot dpkg-divert --local --rename --add /sbin/initctl - cp -a "$rootfsDir/usr/sbin/policy-rc.d" "$rootfsDir/sbin/initctl" - sed -i 's/^exit.*/exit 0/' "$rootfsDir/sbin/initctl" -) - -# shrink a little, since apt makes us cache-fat (wheezy: ~157.5MB vs ~120MB) -( set -x; rootfs_chroot apt-get clean ) - -# this file is one APT creates to make sure we don't "autoremove" our currently -# in-use kernel, which doesn't really apply to debootstraps/Docker images that -# don't even have kernels installed -rm -f "$rootfsDir/etc/apt/apt.conf.d/01autoremove-kernels" - -# Ubuntu 10.04 sucks... :) -if strings "$rootfsDir/usr/bin/dpkg" | grep -q unsafe-io; then - # force dpkg not to call sync() after package extraction (speeding up installs) - echo >&2 "+ echo force-unsafe-io > '$rootfsDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup'" - cat > "$rootfsDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup" <<-'EOF' - # For most Docker users, package installs happen during "docker build", which - # doesn't survive power loss and gets restarted clean afterwards anyhow, so - # this minor tweak gives us a nice speedup (much nicer on spinning disks, - # obviously). - - force-unsafe-io - EOF -fi - -if [ -d "$rootfsDir/etc/apt/apt.conf.d" ]; then - # _keep_ us lean by effectively running "apt-get clean" after every install - aptGetClean='"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true";' - echo >&2 "+ cat > '$rootfsDir/etc/apt/apt.conf.d/docker-clean'" - cat > "$rootfsDir/etc/apt/apt.conf.d/docker-clean" <<-EOF - # Since for most Docker users, package installs happen in "docker build" steps, - # they essentially become individual layers due to the way Docker handles - # layering, especially using CoW filesystems. What this means for us is that - # the caches that APT keeps end up just wasting space in those layers, making - # our layers unnecessarily large (especially since we'll normally never use - # these caches again and will instead just "docker build" again and make a brand - # new image). - - # Ideally, these would just be invoking "apt-get clean", but in our testing, - # that ended up being cyclic and we got stuck on APT's lock, so we get this fun - # creation that's essentially just "apt-get clean". - DPkg::Post-Invoke { ${aptGetClean} }; - APT::Update::Post-Invoke { ${aptGetClean} }; - - Dir::Cache::pkgcache ""; - Dir::Cache::srcpkgcache ""; - - # Note that we do realize this isn't the ideal way to do this, and are always - # open to better suggestions (https://github.com/docker/docker/issues). - EOF - - # remove apt-cache translations for fast "apt-get update" - echo >&2 "+ echo Acquire::Languages 'none' > '$rootfsDir/etc/apt/apt.conf.d/docker-no-languages'" - cat > "$rootfsDir/etc/apt/apt.conf.d/docker-no-languages" <<-'EOF' - # In Docker, we don't often need the "Translations" files, so we're just wasting - # time and space by downloading them, and this inhibits that. For users that do - # need them, it's a simple matter to delete this file and "apt-get update". :) - - Acquire::Languages "none"; - EOF - - echo >&2 "+ echo Acquire::GzipIndexes 'true' > '$rootfsDir/etc/apt/apt.conf.d/docker-gzip-indexes'" - cat > "$rootfsDir/etc/apt/apt.conf.d/docker-gzip-indexes" <<-'EOF' - # Since Docker users using "RUN apt-get update && apt-get install -y ..." in - # their Dockerfiles don't go delete the lists files afterwards, we want them to - # be as small as possible on-disk, so we explicitly request "gz" versions and - # tell Apt to keep them gzipped on-disk. - - # For comparison, an "apt-get update" layer without this on a pristine - # "debian:wheezy" base image was "29.88 MB", where with this it was only - # "8.273 MB". - - Acquire::GzipIndexes "true"; - Acquire::CompressionTypes::Order:: "gz"; - EOF - - # update "autoremove" configuration to be aggressive about removing suggests deps that weren't manually installed - echo >&2 "+ echo Apt::AutoRemove::SuggestsImportant 'false' > '$rootfsDir/etc/apt/apt.conf.d/docker-autoremove-suggests'" - cat > "$rootfsDir/etc/apt/apt.conf.d/docker-autoremove-suggests" <<-'EOF' - # Since Docker users are looking for the smallest possible final images, the - # following emerges as a very common pattern: - - # RUN apt-get update \ - # && apt-get install -y \ - # && \ - # && apt-get purge -y --auto-remove - - # By default, APT will actually _keep_ packages installed via Recommends or - # Depends if another package Suggests them, even and including if the package - # that originally caused them to be installed is removed. Setting this to - # "false" ensures that APT is appropriately aggressive about removing the - # packages it added. - - # https://aptitude.alioth.debian.org/doc/en/ch02s05s05.html#configApt-AutoRemove-SuggestsImportant - Apt::AutoRemove::SuggestsImportant "false"; - EOF -fi - -if [ -z "$DONT_TOUCH_SOURCES_LIST" ]; then - # tweak sources.list, where appropriate - lsbDist= - if [ -z "$lsbDist" -a -r "$rootfsDir/etc/os-release" ]; then - lsbDist="$(. "$rootfsDir/etc/os-release" && echo "$ID")" - fi - if [ -z "$lsbDist" -a -r "$rootfsDir/etc/lsb-release" ]; then - lsbDist="$(. "$rootfsDir/etc/lsb-release" && echo "$DISTRIB_ID")" - fi - if [ -z "$lsbDist" -a -r "$rootfsDir/etc/debian_version" ]; then - lsbDist='Debian' - fi - # normalize to lowercase for easier matching - lsbDist="$(echo "$lsbDist" | tr '[:upper:]' '[:lower:]')" - case "$lsbDist" in - debian) - # updates and security! - if curl -o /dev/null -s --head --fail "http://security.debian.org/dists/$suite/updates/main/binary-$(rootfs_chroot dpkg --print-architecture)/Packages.gz"; then - ( - set -x - sed -i " - p; - s/ $suite / ${suite}-updates / - " "$rootfsDir/etc/apt/sources.list" - echo "deb http://security.debian.org $suite/updates main" >> "$rootfsDir/etc/apt/sources.list" - ) - fi - ;; - ubuntu) - # add the updates and security repositories - ( - set -x - sed -i " - p; - s/ $suite / ${suite}-updates /; p; - s/ $suite-updates / ${suite}-security / - " "$rootfsDir/etc/apt/sources.list" - ) - ;; - tanglu) - # add the updates repository - if [ "$suite" != 'devel' ]; then - ( - set -x - sed -i " - p; - s/ $suite / ${suite}-updates / - " "$rootfsDir/etc/apt/sources.list" - ) - fi - ;; - steamos) - # add contrib and non-free if "main" is the only component - ( - set -x - sed -i "s/ $suite main$/ $suite main contrib non-free/" "$rootfsDir/etc/apt/sources.list" - ) - ;; - esac -fi - -( - set -x - - # make sure we're fully up-to-date - rootfs_chroot sh -xc 'apt-get update && apt-get dist-upgrade -y' - - # delete all the apt list files since they're big and get stale quickly - rm -rf "$rootfsDir/var/lib/apt/lists"/* - # this forces "apt-get update" in dependent images, which is also good - - mkdir "$rootfsDir/var/lib/apt/lists/partial" # Lucid... "E: Lists directory /var/lib/apt/lists/partial is missing." -) diff --git a/contrib/mkimage/mageia-urpmi b/contrib/mkimage/mageia-urpmi deleted file mode 100755 index 93fb289cac394..0000000000000 --- a/contrib/mkimage/mageia-urpmi +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -# -# Needs to be run from Mageia 4 or greater for kernel support for docker. -# -# Mageia 4 does not have docker available in official repos, so please -# install and run the docker binary manually. -# -# Tested working versions are for Mageia 2 onwards (inc. cauldron). -# -set -e - -rootfsDir="$1" -shift - -optTemp=$(getopt --options '+v:,m:' --longoptions 'version:,mirror:' --name mageia-urpmi -- "$@") -eval set -- "$optTemp" -unset optTemp - -installversion= -mirror= -while true; do - case "$1" in - -v|--version) installversion="$2" ; shift 2 ;; - -m|--mirror) mirror="$2" ; shift 2 ;; - --) shift ; break ;; - esac -done - -if [ -z $installversion ]; then - # Attempt to match host version - if [ -r /etc/mageia-release ]; then - installversion="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' /etc/mageia-release)" - else - echo "Error: no version supplied and unable to detect host mageia version" - exit 1 - fi -fi - -if [ -z $mirror ]; then - # No mirror provided, default to mirrorlist - mirror="--mirrorlist https://mirrors.mageia.org/api/mageia.$installversion.x86_64.list" -fi - -( - set -x - urpmi.addmedia --distrib \ - $mirror \ - --urpmi-root "$rootfsDir" - urpmi basesystem-minimal urpmi \ - --auto \ - --no-suggests \ - --urpmi-root "$rootfsDir" \ - --root "$rootfsDir" -) - -"$(dirname "$BASH_SOURCE")/.febootstrap-minimize" "$rootfsDir" - -if [ -d "$rootfsDir/etc/sysconfig" ]; then - # allow networking init scripts inside the container to work without extra steps - echo 'NETWORKING=yes' > "$rootfsDir/etc/sysconfig/network" -fi diff --git a/contrib/mkimage/rinse b/contrib/mkimage/rinse deleted file mode 100755 index 75eb4f0d9dfdc..0000000000000 --- a/contrib/mkimage/rinse +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -set -e - -rootfsDir="$1" -shift - -# specifying --arch below is safe because "$@" can override it and the "latest" one wins :) - -( - set -x - rinse --directory "$rootfsDir" --arch amd64 "$@" -) - -"$(dirname "$BASH_SOURCE")/.febootstrap-minimize" "$rootfsDir" - -if [ -d "$rootfsDir/etc/sysconfig" ]; then - # allow networking init scripts inside the container to work without extra steps - echo 'NETWORKING=yes' > "$rootfsDir/etc/sysconfig/network" -fi - -# make sure we're fully up-to-date, too -( - set -x - chroot "$rootfsDir" yum update -y -) diff --git a/contrib/nnp-test/Dockerfile b/contrib/nnp-test/Dockerfile index 026d86954f6e0..833c5c76c25e5 100644 --- a/contrib/nnp-test/Dockerfile +++ b/contrib/nnp-test/Dockerfile @@ -1,4 +1,5 @@ -FROM buildpack-deps:jessie +FROM debian:bullseye-slim +RUN apt-get update && apt-get install -y gcc libc6-dev --no-install-recommends COPY . /usr/src/ diff --git a/contrib/nuke-graph-directory.sh b/contrib/nuke-graph-directory.sh index 3d2f49e869315..c0f807cd71ca2 100755 --- a/contrib/nuke-graph-directory.sh +++ b/contrib/nuke-graph-directory.sh @@ -31,7 +31,10 @@ echo "Nuking $dir ..." echo ' (if this is wrong, press Ctrl+C NOW!)' echo -( set -x; sleep 10 ) +( + set -x + sleep 10 +) echo dir_in_dir() { @@ -45,7 +48,10 @@ dir_in_dir() { for mount in $(awk '{ print $5 }' /proc/self/mountinfo); do mount="$(readlink -f "$mount" || true)" if [ "$dir" != "$mount" ] && dir_in_dir "$mount" "$dir"; then - ( set -x; umount -f "$mount" ) + ( + set -x + umount -f "$mount" + ) fi done @@ -54,11 +60,18 @@ if command -v btrfs > /dev/null 2>&1; then # Find btrfs subvolumes under $dir checking for inode 256 # Source: http://stackoverflow.com/a/32865333 for subvol in $(find "$dir" -type d -inum 256 | sort -r); do - if [ "$dir" != "$subvol" ]; then - ( set -x; btrfs subvolume delete "$subvol" ) + if [ "$dir" != "$subvol" ] && subvolType="$(stat -f --format=%T "$subvol")" && [ "$subvolType" = "btrfs" ]; then + ( + set -x + btrfs subvolume delete "$subvol" + ) fi done fi # finally, DESTROY ALL THINGS -( shopt -s dotglob; set -x; rm -rf "$dir"/* ) +( + shopt -s dotglob + set -x + rm -rf "$dir"/* +) diff --git a/contrib/report-issue.sh b/contrib/report-issue.sh deleted file mode 100755 index cb54f1a5bc005..0000000000000 --- a/contrib/report-issue.sh +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/sh - -# This is a convenience script for reporting issues that include a base -# template of information. See https://github.com/docker/docker/pull/8845 - -set -e - -DOCKER_ISSUE_URL=${DOCKER_ISSUE_URL:-"https://github.com/docker/docker/issues/new"} -DOCKER_ISSUE_NAME_PREFIX=${DOCKER_ISSUE_NAME_PREFIX:-"Report: "} -DOCKER=${DOCKER:-"docker"} -DOCKER_COMMAND="${DOCKER}" -export DOCKER_COMMAND - -# pulled from https://gist.github.com/cdown/1163649 -function urlencode() { - # urlencode - - local length="${#1}" - for (( i = 0; i < length; i++ )); do - local c="${1:i:1}" - case $c in - [a-zA-Z0-9.~_-]) printf "$c" ;; - *) printf '%%%02X' "'$c" - esac - done -} - -function template() { -# this should always match the template from CONTRIBUTING.md - cat <<- EOM - Description of problem: - - - \`docker version\`: - `${DOCKER_COMMAND} -D version` - - - \`docker info\`: - `${DOCKER_COMMAND} -D info` - - - \`uname -a\`: - `uname -a` - - - Environment details (AWS, VirtualBox, physical, etc.): - - - How reproducible: - - - Steps to Reproduce: - 1. - 2. - 3. - - - Actual Results: - - - Expected Results: - - - Additional info: - - - EOM -} - -function format_issue_url() { - if [ ${#@} -ne 2 ] ; then - return 1 - fi - local issue_name=$(urlencode "${DOCKER_ISSUE_NAME_PREFIX}${1}") - local issue_body=$(urlencode "${2}") - echo "${DOCKER_ISSUE_URL}?title=${issue_name}&body=${issue_body}" -} - - -echo -ne "Do you use \`sudo\` to call docker? [y|N]: " -read -r -n 1 use_sudo -echo "" - -if [ "x${use_sudo}" = "xy" -o "x${use_sudo}" = "xY" ]; then - export DOCKER_COMMAND="sudo ${DOCKER}" -fi - -echo -ne "Title of new issue?: " -read -r issue_title -echo "" - -issue_url=$(format_issue_url "${issue_title}" "$(template)") - -if which xdg-open 2>/dev/null >/dev/null ; then - echo -ne "Would like to launch this report in your browser? [Y|n]: " - read -r -n 1 launch_now - echo "" - - if [ "${launch_now}" != "n" -a "${launch_now}" != "N" ]; then - xdg-open "${issue_url}" - fi -fi - -echo "If you would like to manually open the url, you can open this link if your browser: ${issue_url}" - diff --git a/contrib/syntax/vim/LICENSE b/contrib/syntax/vim/LICENSE deleted file mode 100644 index e67cdabd22e5f..0000000000000 --- a/contrib/syntax/vim/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2013 Honza Pokorny -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/contrib/syntax/vim/README.md b/contrib/syntax/vim/README.md index b73a30bea328a..684d11f47ae6d 100644 --- a/contrib/syntax/vim/README.md +++ b/contrib/syntax/vim/README.md @@ -3,28 +3,9 @@ dockerfile.vim Syntax highlighting for Dockerfiles -Installation ------------- -With [pathogen](https://github.com/tpope/vim-pathogen), the usual way... +------------------------------------------------------------------------------- -With [Vundle](https://github.com/gmarik/Vundle.vim) - - Plugin 'docker/docker' , {'rtp': '/contrib/syntax/vim/'} +dockerfile.vim has been merged into upstream vim, and has therefore been deleted +from here. You can find the canonical version [here][1]. -With [vim-plug](https://github.com/junegunn/vim-plug) - - Plug 'docker/docker' , {'rtp': '/contrib/syntax/vim/'} - -Features --------- - -The syntax highlighting includes: - -* The directives (e.g. `FROM`) -* Strings -* Comments - -License -------- - -BSD, short and sweet +[1]: https://github.com/vim/vim/blob/master/runtime/syntax/dockerfile.vim diff --git a/contrib/syntax/vim/doc/dockerfile.txt b/contrib/syntax/vim/doc/dockerfile.txt deleted file mode 100644 index e69e2b7b30fca..0000000000000 --- a/contrib/syntax/vim/doc/dockerfile.txt +++ /dev/null @@ -1,18 +0,0 @@ -*dockerfile.txt* Syntax highlighting for Dockerfiles - -Author: Honza Pokorny -License: BSD - -INSTALLATION *installation* - -Drop it on your Pathogen path and you're all set. - -FEATURES *features* - -The syntax highlighting includes: - -* The directives (e.g. FROM) -* Strings -* Comments - - vim:tw=78:et:ft=help:norl: diff --git a/contrib/syntax/vim/ftdetect/dockerfile.vim b/contrib/syntax/vim/ftdetect/dockerfile.vim deleted file mode 100644 index a21dd14095ccf..0000000000000 --- a/contrib/syntax/vim/ftdetect/dockerfile.vim +++ /dev/null @@ -1 +0,0 @@ -au BufNewFile,BufRead [Dd]ockerfile,[Dd]ockerfile.*,*.[Dd]ockerfile set filetype=dockerfile diff --git a/contrib/syntax/vim/syntax/dockerfile.vim b/contrib/syntax/vim/syntax/dockerfile.vim deleted file mode 100644 index a067e6ad4c510..0000000000000 --- a/contrib/syntax/vim/syntax/dockerfile.vim +++ /dev/null @@ -1,31 +0,0 @@ -" dockerfile.vim - Syntax highlighting for Dockerfiles -" Maintainer: Honza Pokorny -" Version: 0.5 - - -if exists("b:current_syntax") - finish -endif - -let b:current_syntax = "dockerfile" - -syntax case ignore - -syntax match dockerfileKeyword /\v^\s*(ONBUILD\s+)?(ADD|ARG|CMD|COPY|ENTRYPOINT|ENV|EXPOSE|FROM|HEALTHCHECK|LABEL|MAINTAINER|RUN|SHELL|STOPSIGNAL|USER|VOLUME|WORKDIR)\s/ -highlight link dockerfileKeyword Keyword - -syntax region dockerfileString start=/\v"/ skip=/\v\\./ end=/\v"/ -highlight link dockerfileString String - -syntax match dockerfileComment "\v^\s*#.*$" -highlight link dockerfileComment Comment - -set commentstring=#\ %s - -" match "RUN", "CMD", and "ENTRYPOINT" lines, and parse them as shell -let s:current_syntax = b:current_syntax -unlet b:current_syntax -syntax include @SH syntax/sh.vim -let b:current_syntax = s:current_syntax -syntax region shLine matchgroup=dockerfileKeyword start=/\v^\s*(RUN|CMD|ENTRYPOINT)\s/ end=/\v$/ contains=@SH -" since @SH will handle "\" as part of the same line automatically, this "just works" for line continuation too, but with the caveat that it will highlight "RUN echo '" followed by a newline as if it were a block because the "'" is shell line continuation... not sure how to fix that just yet (TODO) diff --git a/contrib/syscall-test/Dockerfile b/contrib/syscall-test/Dockerfile index f95f1758c0b24..8281a06776caa 100644 --- a/contrib/syscall-test/Dockerfile +++ b/contrib/syscall-test/Dockerfile @@ -1,4 +1,5 @@ -FROM buildpack-deps:jessie +FROM debian:bullseye-slim +RUN apt-get update && apt-get install -y gcc libc6-dev --no-install-recommends COPY . /usr/src/ diff --git a/contrib/vagrant-docker/README.md b/contrib/vagrant-docker/README.md deleted file mode 100644 index 736c7899984c5..0000000000000 --- a/contrib/vagrant-docker/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Vagrant integration - -Currently there are at least 4 different projects that we are aware of that deals -with integration with [Vagrant](http://vagrantup.com/) at different levels. One -approach is to use Docker as a [provisioner](http://docs.vagrantup.com/v2/provisioning/index.html) -which means you can create containers and pull base images on VMs using Docker's -CLI and the other is to use Docker as a [provider](http://docs.vagrantup.com/v2/providers/index.html), -meaning you can use Vagrant to control Docker containers. - - -### Provisioners - -* [Vocker](https://github.com/fgrehm/vocker) -* [Ventriloquist](https://github.com/fgrehm/ventriloquist) - -### Providers - -* [docker-provider](https://github.com/fgrehm/docker-provider) -* [vagrant-shell](https://github.com/destructuring/vagrant-shell) - -## Setting up Vagrant-docker with the Engine API - -The initial Docker upstart script will not work because it runs on `127.0.0.1`, which is not accessible to the host machine. Instead, we need to change the script to connect to `0.0.0.0`. To do this, modify `/etc/init/docker.conf` to look like this: - -``` -description "Docker daemon" - -start on filesystem -stop on runlevel [!2345] - -respawn - -script - /usr/bin/dockerd -H=tcp://0.0.0.0:2375 -end script -``` - -Once that's done, you need to set up an SSH tunnel between your host machine and the vagrant machine that's running Docker. This can be done by running the following command in a host terminal: - -``` -ssh -L 2375:localhost:2375 -p 2222 vagrant@localhost -``` - -(The first 2375 is what your host can connect to, the second 2375 is what port Docker is running on in the vagrant machine, and the 2222 is the port Vagrant is providing for SSH. If VirtualBox is the VM you're using, you can see what value "2222" should be by going to: Network > Adapter 1 > Advanced > Port Forwarding in the VirtualBox GUI.) - -Note that because the port has been changed, to run docker commands from within the command line you must run them like this: - -``` -sudo docker -H 0.0.0.0:2375 < commands for docker > -``` diff --git a/daemon/apparmor_default.go b/daemon/apparmor_default.go index 461f5c7f96b28..6376001613f7e 100644 --- a/daemon/apparmor_default.go +++ b/daemon/apparmor_default.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package daemon // import "github.com/docker/docker/daemon" @@ -5,20 +6,29 @@ package daemon // import "github.com/docker/docker/daemon" import ( "fmt" + "github.com/containerd/containerd/pkg/apparmor" aaprofile "github.com/docker/docker/profiles/apparmor" - "github.com/opencontainers/runc/libcontainer/apparmor" ) // Define constants for native driver const ( - defaultApparmorProfile = "docker-default" + unconfinedAppArmorProfile = "unconfined" + defaultAppArmorProfile = "docker-default" ) +// DefaultApparmorProfile returns the name of the default apparmor profile +func DefaultApparmorProfile() string { + if apparmor.HostSupports() { + return defaultAppArmorProfile + } + return "" +} + func ensureDefaultAppArmorProfile() error { - if apparmor.IsEnabled() { - loaded, err := aaprofile.IsLoaded(defaultApparmorProfile) + if apparmor.HostSupports() { + loaded, err := aaprofile.IsLoaded(defaultAppArmorProfile) if err != nil { - return fmt.Errorf("Could not check if %s AppArmor profile was loaded: %s", defaultApparmorProfile, err) + return fmt.Errorf("Could not check if %s AppArmor profile was loaded: %s", defaultAppArmorProfile, err) } // Nothing to do. @@ -27,8 +37,8 @@ func ensureDefaultAppArmorProfile() error { } // Load the profile. - if err := aaprofile.InstallDefault(defaultApparmorProfile); err != nil { - return fmt.Errorf("AppArmor enabled on system but the %s profile could not be loaded: %s", defaultApparmorProfile, err) + if err := aaprofile.InstallDefault(defaultAppArmorProfile); err != nil { + return fmt.Errorf("AppArmor enabled on system but the %s profile could not be loaded: %s", defaultAppArmorProfile, err) } } diff --git a/daemon/apparmor_default_unsupported.go b/daemon/apparmor_default_unsupported.go index 51f9c526b3505..e3dc18b32b5ef 100644 --- a/daemon/apparmor_default_unsupported.go +++ b/daemon/apparmor_default_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package daemon // import "github.com/docker/docker/daemon" @@ -5,3 +6,8 @@ package daemon // import "github.com/docker/docker/daemon" func ensureDefaultAppArmorProfile() error { return nil } + +// DefaultApparmorProfile returns an empty string. +func DefaultApparmorProfile() string { + return "" +} diff --git a/daemon/archive.go b/daemon/archive.go index 9c7971b56ea3e..f443d12117e8c 100644 --- a/daemon/archive.go +++ b/daemon/archive.go @@ -31,34 +31,35 @@ type archiver interface { } // helper functions to extract or archive -func extractArchive(i interface{}, src io.Reader, dst string, opts *archive.TarOptions) error { +func extractArchive(i interface{}, src io.Reader, dst string, opts *archive.TarOptions, root string) error { if ea, ok := i.(extractor); ok { return ea.ExtractArchive(src, dst, opts) } - return chrootarchive.Untar(src, dst, opts) + + return chrootarchive.UntarWithRoot(src, dst, opts, root) } -func archivePath(i interface{}, src string, opts *archive.TarOptions) (io.ReadCloser, error) { +func archivePath(i interface{}, src string, opts *archive.TarOptions, root string) (io.ReadCloser, error) { if ap, ok := i.(archiver); ok { return ap.ArchivePath(src, opts) } - return archive.TarWithOptions(src, opts) + return chrootarchive.Tar(src, opts, root) } // ContainerCopy performs a deprecated operation of archiving the resource at // the specified path in the container identified by the given name. func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, error) { - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return nil, err } // Make sure an online file-system operation is permitted. - if err := daemon.isOnlineFSOperationPermitted(container); err != nil { + if err := daemon.isOnlineFSOperationPermitted(ctr); err != nil { return nil, errdefs.System(err) } - data, err := daemon.containerCopy(container, res) + data, err := daemon.containerCopy(ctr, res) if err == nil { return data, nil } @@ -72,17 +73,17 @@ func (daemon *Daemon) ContainerCopy(name string, res string) (io.ReadCloser, err // ContainerStatPath stats the filesystem resource at the specified path in the // container identified by the given name. func (daemon *Daemon) ContainerStatPath(name string, path string) (stat *types.ContainerPathStat, err error) { - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return nil, err } // Make sure an online file-system operation is permitted. - if err := daemon.isOnlineFSOperationPermitted(container); err != nil { + if err := daemon.isOnlineFSOperationPermitted(ctr); err != nil { return nil, errdefs.System(err) } - stat, err = daemon.containerStatPath(container, path) + stat, err = daemon.containerStatPath(ctr, path) if err == nil { return stat, nil } @@ -97,17 +98,17 @@ func (daemon *Daemon) ContainerStatPath(name string, path string) (stat *types.C // specified path in the container identified by the given name. Returns a // tar archive of the resource and whether it was a directory or a single file. func (daemon *Daemon) ContainerArchivePath(name string, path string) (content io.ReadCloser, stat *types.ContainerPathStat, err error) { - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return nil, nil, err } // Make sure an online file-system operation is permitted. - if err := daemon.isOnlineFSOperationPermitted(container); err != nil { + if err := daemon.isOnlineFSOperationPermitted(ctr); err != nil { return nil, nil, errdefs.System(err) } - content, stat, err = daemon.containerArchivePath(container, path) + content, stat, err = daemon.containerArchivePath(ctr, path) if err == nil { return content, stat, nil } @@ -125,17 +126,17 @@ func (daemon *Daemon) ContainerArchivePath(name string, path string) (content io // be an error if unpacking the given content would cause an existing directory // to be replaced with a non-directory and vice versa. func (daemon *Daemon) ContainerExtractToDir(name, path string, copyUIDGID, noOverwriteDirNonDir bool, content io.Reader) error { - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return err } // Make sure an online file-system operation is permitted. - if err := daemon.isOnlineFSOperationPermitted(container); err != nil { + if err := daemon.isOnlineFSOperationPermitted(ctr); err != nil { return errdefs.System(err) } - err = daemon.containerExtractToDir(container, path, copyUIDGID, noOverwriteDirNonDir, content) + err = daemon.containerExtractToDir(ctr, path, copyUIDGID, noOverwriteDirNonDir, content) if err == nil { return nil } @@ -235,10 +236,16 @@ func (daemon *Daemon) containerArchivePath(container *container.Container, path if driver.Base(resolvedPath) == "." { resolvedPath += string(driver.Separator()) + "." } - sourceDir, sourceBase := driver.Dir(resolvedPath), driver.Base(resolvedPath) + + sourceDir := resolvedPath + sourceBase := "." + + if stat.Mode&os.ModeDir == 0 { // not dir + sourceDir, sourceBase = driver.Split(resolvedPath) + } opts := archive.TarResourceRebaseOpts(sourceBase, driver.Base(absPath)) - data, err := archivePath(driver, sourceDir, opts) + data, err := archivePath(driver, sourceDir, opts, container.BaseFS.Path()) if err != nil { return nil, nil, err } @@ -367,7 +374,7 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path } } - if err := extractArchive(driver, content, resolvedPath, options); err != nil { + if err := extractArchive(driver, content, resolvedPath, options, container.BaseFS.Path()); err != nil { return err } @@ -425,20 +432,17 @@ func (daemon *Daemon) containerCopy(container *container.Container, resource str d, f := driver.Split(basePath) basePath = d filter = []string{f} - } else { - filter = []string{driver.Base(basePath)} - basePath = driver.Dir(basePath) } - archive, err := archivePath(driver, basePath, &archive.TarOptions{ + archv, err := archivePath(driver, basePath, &archive.TarOptions{ Compression: archive.Uncompressed, IncludeFiles: filter, - }) + }, container.BaseFS.Path()) if err != nil { return nil, err } - reader := ioutils.NewReadCloserWrapper(archive, func() error { - err := archive.Close() + reader := ioutils.NewReadCloserWrapper(archv, func() error { + err := archv.Close() container.DetachAndUnmount(daemon.LogVolumeEvent) daemon.Unmount(container) container.Unlock() diff --git a/daemon/archive_tarcopyoptions_unix.go b/daemon/archive_tarcopyoptions_unix.go index c456b410dd537..52f1ce7dbe340 100644 --- a/daemon/archive_tarcopyoptions_unix.go +++ b/daemon/archive_tarcopyoptions_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" diff --git a/daemon/archive_unix.go b/daemon/archive_unix.go index 50e6fe24be374..863788d72d8b4 100644 --- a/daemon/archive_unix.go +++ b/daemon/archive_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" @@ -12,7 +13,7 @@ import ( // cannot be configured with a read-only rootfs. func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) { var toVolume bool - parser := volumemounts.NewParser(container.OS) + parser := volumemounts.NewParser() for _, mnt := range container.MountPoints { if toVolume = parser.HasResource(mnt, absPath); toVolume { if mnt.RW { diff --git a/daemon/attach.go b/daemon/attach.go index 0e12441ad1e2e..c6553f79d3919 100644 --- a/daemon/attach.go +++ b/daemon/attach.go @@ -11,7 +11,7 @@ import ( "github.com/docker/docker/daemon/logger" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/docker/pkg/term" + "github.com/moby/term" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -27,15 +27,15 @@ func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerA } } - container, err := daemon.GetContainer(prefixOrName) + ctr, err := daemon.GetContainer(prefixOrName) if err != nil { return err } - if container.IsPaused() { + if ctr.IsPaused() { err := fmt.Errorf("container %s is paused, unpause the container before attach", prefixOrName) return errdefs.Conflict(err) } - if container.IsRestarting() { + if ctr.IsRestarting() { err := fmt.Errorf("container %s is restarting, wait until the container is running", prefixOrName) return errdefs.Conflict(err) } @@ -44,11 +44,11 @@ func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerA UseStdin: c.UseStdin, UseStdout: c.UseStdout, UseStderr: c.UseStderr, - TTY: container.Config.Tty, - CloseStdin: container.Config.StdinOnce, + TTY: ctr.Config.Tty, + CloseStdin: ctr.Config.StdinOnce, DetachKeys: keys, } - container.StreamConfig.AttachStreams(&cfg) + ctr.StreamConfig.AttachStreams(&cfg) inStream, outStream, errStream, err := c.GetStreams() if err != nil { @@ -56,7 +56,7 @@ func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerA } defer inStream.Close() - if !container.Config.Tty && c.MuxStreams { + if !ctr.Config.Tty && c.MuxStreams { errStream = stdcopy.NewStdWriter(errStream, stdcopy.Stderr) outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout) } @@ -71,7 +71,7 @@ func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerA cfg.Stderr = errStream } - if err := daemon.containerAttach(container, &cfg, c.Logs, c.Stream); err != nil { + if err := daemon.containerAttach(ctr, &cfg, c.Logs, c.Stream); err != nil { fmt.Fprintf(outStream, "Error attaching: %s\n", err) } return nil @@ -79,7 +79,7 @@ func (daemon *Daemon) ContainerAttach(prefixOrName string, c *backend.ContainerA // ContainerAttachRaw attaches the provided streams to the container's stdio func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadCloser, stdout, stderr io.Writer, doStream bool, attached chan struct{}) error { - container, err := daemon.GetContainer(prefixOrName) + ctr, err := daemon.GetContainer(prefixOrName) if err != nil { return err } @@ -87,10 +87,10 @@ func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadClose UseStdin: stdin != nil, UseStdout: stdout != nil, UseStderr: stderr != nil, - TTY: container.Config.Tty, - CloseStdin: container.Config.StdinOnce, + TTY: ctr.Config.Tty, + CloseStdin: ctr.Config.StdinOnce, } - container.StreamConfig.AttachStreams(&cfg) + ctr.StreamConfig.AttachStreams(&cfg) close(attached) if cfg.UseStdin { cfg.Stdin = stdin @@ -102,7 +102,7 @@ func (daemon *Daemon) ContainerAttachRaw(prefixOrName string, stdin io.ReadClose cfg.Stderr = stderr } - return daemon.containerAttach(container, &cfg, false, doStream) + return daemon.containerAttach(ctr, &cfg, false, doStream) } func (daemon *Daemon) containerAttach(c *container.Container, cfg *stream.AttachConfig, logs, doStream bool) error { @@ -176,7 +176,8 @@ func (daemon *Daemon) containerAttach(c *container.Container, cfg *stream.Attach ctx := c.InitAttachContext() err := <-c.StreamConfig.CopyStreams(ctx, cfg) if err != nil { - if _, ok := errors.Cause(err).(term.EscapeError); ok || err == context.Canceled { + var ierr term.EscapeError + if errors.Is(err, context.Canceled) || errors.As(err, &ierr) { daemon.LogContainerEvent(c, "detach") } else { logrus.Errorf("attach failed with error: %v", err) diff --git a/daemon/bindmount_unix.go b/daemon/bindmount_unix.go deleted file mode 100644 index 028e300b06777..0000000000000 --- a/daemon/bindmount_unix.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build linux freebsd - -package daemon // import "github.com/docker/docker/daemon" - -const bindMountType = "bind" diff --git a/daemon/caps/utils.go b/daemon/caps/utils.go deleted file mode 100644 index c5ded542eff7a..0000000000000 --- a/daemon/caps/utils.go +++ /dev/null @@ -1,139 +0,0 @@ -package caps // import "github.com/docker/docker/daemon/caps" - -import ( - "fmt" - "strings" - - "github.com/syndtr/gocapability/capability" -) - -var capabilityList Capabilities - -func init() { - last := capability.CAP_LAST_CAP - // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap - if last == capability.Cap(63) { - last = capability.CAP_BLOCK_SUSPEND - } - for _, cap := range capability.List() { - if cap > last { - continue - } - capabilityList = append(capabilityList, - &CapabilityMapping{ - Key: "CAP_" + strings.ToUpper(cap.String()), - Value: cap, - }, - ) - } -} - -type ( - // CapabilityMapping maps linux capability name to its value of capability.Cap type - // Capabilities is one of the security systems in Linux Security Module (LSM) - // framework provided by the kernel. - // For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html - CapabilityMapping struct { - Key string `json:"key,omitempty"` - Value capability.Cap `json:"value,omitempty"` - } - // Capabilities contains all CapabilityMapping - Capabilities []*CapabilityMapping -) - -// String returns of CapabilityMapping -func (c *CapabilityMapping) String() string { - return c.Key -} - -// GetCapability returns CapabilityMapping which contains specific key -func GetCapability(key string) *CapabilityMapping { - for _, capp := range capabilityList { - if capp.Key == key { - cpy := *capp - return &cpy - } - } - return nil -} - -// GetAllCapabilities returns all of the capabilities -func GetAllCapabilities() []string { - output := make([]string, len(capabilityList)) - for i, capability := range capabilityList { - output[i] = capability.String() - } - return output -} - -// inSlice tests whether a string is contained in a slice of strings or not. -// Comparison is case insensitive -func inSlice(slice []string, s string) bool { - for _, ss := range slice { - if strings.ToLower(s) == strings.ToLower(ss) { - return true - } - } - return false -} - -// TweakCapabilities can tweak capabilities by adding or dropping capabilities -// based on the basics capabilities. -func TweakCapabilities(basics, adds, drops []string) ([]string, error) { - var ( - newCaps []string - allCaps = GetAllCapabilities() - ) - - // FIXME(tonistiigi): docker format is without CAP_ prefix, oci is with prefix - // Currently they are mixed in here. We should do conversion in one place. - - // look for invalid cap in the drop list - for _, cap := range drops { - if strings.ToLower(cap) == "all" { - continue - } - - if !inSlice(allCaps, "CAP_"+cap) { - return nil, fmt.Errorf("Unknown capability drop: %q", cap) - } - } - - // handle --cap-add=all - if inSlice(adds, "all") { - basics = allCaps - } - - if !inSlice(drops, "all") { - for _, cap := range basics { - // skip `all` already handled above - if strings.ToLower(cap) == "all" { - continue - } - - // if we don't drop `all`, add back all the non-dropped caps - if !inSlice(drops, cap[4:]) { - newCaps = append(newCaps, strings.ToUpper(cap)) - } - } - } - - for _, cap := range adds { - // skip `all` already handled above - if strings.ToLower(cap) == "all" { - continue - } - - cap = "CAP_" + cap - - if !inSlice(allCaps, cap) { - return nil, fmt.Errorf("Unknown capability to add: %q", cap) - } - - // add cap if not already in the list - if !inSlice(newCaps, cap) { - newCaps = append(newCaps, strings.ToUpper(cap)) - } - } - return newCaps, nil -} diff --git a/daemon/changes.go b/daemon/changes.go index 70b3f6b943c80..09f27b2161c1f 100644 --- a/daemon/changes.go +++ b/daemon/changes.go @@ -2,7 +2,6 @@ package daemon // import "github.com/docker/docker/daemon" import ( "errors" - "runtime" "time" "github.com/docker/docker/pkg/archive" @@ -16,7 +15,7 @@ func (daemon *Daemon) ContainerChanges(name string) ([]archive.Change, error) { return nil, err } - if runtime.GOOS == "windows" && container.IsRunning() { + if isWindows && container.IsRunning() { return nil, errors.New("Windows does not support diff of a running container") } diff --git a/daemon/checkpoint.go b/daemon/checkpoint.go index 4a1cb0e10ebe0..5cbe8574abd3f 100644 --- a/daemon/checkpoint.go +++ b/daemon/checkpoint.go @@ -2,9 +2,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( "context" - "encoding/json" "fmt" - "io/ioutil" "os" "path/filepath" @@ -36,16 +34,16 @@ func getCheckpointDir(checkDir, checkpointID, ctrName, ctrID, ctrCheckpointDir s err2 = os.MkdirAll(checkpointAbsDir, 0700) case err != nil: err2 = err - case err == nil: + default: err2 = fmt.Errorf("%s exists and is not a directory", checkpointAbsDir) } } else { switch { case err != nil: - err2 = fmt.Errorf("checkpoint %s does not exists for container %s", checkpointID, ctrName) - case err == nil && stat.IsDir(): + err2 = fmt.Errorf("checkpoint %s does not exist for container %s", checkpointID, ctrName) + case stat.IsDir(): err2 = nil - case err == nil: + default: err2 = fmt.Errorf("%s exists and is not a directory", checkpointAbsDir) } } @@ -63,10 +61,6 @@ func (daemon *Daemon) CheckpointCreate(name string, config types.CheckpointCreat return fmt.Errorf("Container %s not running", name) } - if container.Config.Tty { - return fmt.Errorf("checkpoint not support on containers with tty") - } - if !validCheckpointNamePattern.MatchString(config.CheckpointID) { return fmt.Errorf("Invalid checkpoint ID (%s), only %s are allowed", config.CheckpointID, validCheckpointNameChars) } @@ -95,7 +89,7 @@ func (daemon *Daemon) CheckpointDelete(name string, config types.CheckpointDelet } checkpointDir, err := getCheckpointDir(config.CheckpointDir, config.CheckpointID, name, container.ID, container.CheckpointDir(), false) if err == nil { - return os.RemoveAll(filepath.Join(checkpointDir, config.CheckpointID)) + return os.RemoveAll(checkpointDir) } return err } @@ -118,7 +112,7 @@ func (daemon *Daemon) CheckpointList(name string, config types.CheckpointListOpt return nil, err } - dirs, err := ioutil.ReadDir(checkpointDir) + dirs, err := os.ReadDir(checkpointDir) if err != nil { return nil, err } @@ -127,15 +121,7 @@ func (daemon *Daemon) CheckpointList(name string, config types.CheckpointListOpt if !d.IsDir() { continue } - path := filepath.Join(checkpointDir, d.Name(), "config.json") - data, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - var cpt types.Checkpoint - if err := json.Unmarshal(data, &cpt); err != nil { - return nil, err - } + cpt := types.Checkpoint{Name: d.Name()} out = append(out, cpt) } diff --git a/daemon/cluster.go b/daemon/cluster.go index 62eb2a2e5957d..4f4dff185ea99 100644 --- a/daemon/cluster.go +++ b/daemon/cluster.go @@ -3,7 +3,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( apitypes "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" - lncluster "github.com/docker/libnetwork/cluster" + lncluster "github.com/docker/docker/libnetwork/cluster" ) // Cluster is the interface for github.com/docker/docker/daemon/cluster.(*Cluster). diff --git a/daemon/cluster/cluster.go b/daemon/cluster/cluster.go index 35ba5a93789e3..38a38223621c2 100644 --- a/daemon/cluster/cluster.go +++ b/daemon/cluster/cluster.go @@ -41,9 +41,11 @@ package cluster // import "github.com/docker/docker/daemon/cluster" import ( "context" "fmt" + "math" "net" "os" "path/filepath" + "runtime" "sync" "time" @@ -51,25 +53,27 @@ import ( types "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/daemon/cluster/controllers/plugin" executorpkg "github.com/docker/docker/daemon/cluster/executor" - "github.com/docker/docker/pkg/signal" - lncluster "github.com/docker/libnetwork/cluster" + lncluster "github.com/docker/docker/libnetwork/cluster" + "github.com/docker/docker/pkg/stack" swarmapi "github.com/docker/swarmkit/api" swarmnode "github.com/docker/swarmkit/node" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "google.golang.org/grpc" ) -const swarmDirName = "swarm" -const controlSocket = "control.sock" -const swarmConnectTimeout = 20 * time.Second -const swarmRequestTimeout = 20 * time.Second -const stateFile = "docker-state.json" -const defaultAddr = "0.0.0.0:2377" - const ( - initialReconnectDelay = 100 * time.Millisecond - maxReconnectDelay = 30 * time.Second - contextPrefix = "com.docker.swarm" + swarmDirName = "swarm" + controlSocket = "control.sock" + swarmConnectTimeout = 20 * time.Second + swarmRequestTimeout = 20 * time.Second + stateFile = "docker-state.json" + defaultAddr = "0.0.0.0:2377" + isWindows = runtime.GOOS == "windows" + initialReconnectDelay = 100 * time.Millisecond + maxReconnectDelay = 30 * time.Second + contextPrefix = "com.docker.swarm" + defaultRecvSizeForListResponse = math.MaxInt32 // the max recv limit grpc <1.4.0 ) // NetworkSubnetsProvider exposes functions for retrieving the subnets @@ -184,8 +188,11 @@ func (c *Cluster) Start() error { } c.nr = nr + timer := time.NewTimer(swarmConnectTimeout) + defer timer.Stop() + select { - case <-time.After(swarmConnectTimeout): + case <-timer.C: logrus.Error("swarm component could not be started before timeout was reached") case err := <-nr.Ready(): if err != nil { @@ -346,7 +353,7 @@ func (c *Cluster) currentNodeState() nodeState { // Call with read lock. func (c *Cluster) errNoManager(st nodeState) error { if st.swarmNode == nil { - if errors.Cause(st.err) == errSwarmLocked { + if errors.Is(st.err, errSwarmLocked) { return errSwarmLocked } if st.err == errSwarmCertificatesExpired { @@ -386,7 +393,7 @@ func (c *Cluster) Cleanup() { if err := node.Stop(); err != nil { logrus.Errorf("failed to shut down cluster node: %v", err) - signal.DumpStacks("") + stack.Dump() } c.mu.Lock() @@ -397,7 +404,10 @@ func (c *Cluster) Cleanup() { func managerStats(client swarmapi.ControlClient, currentNodeID string) (current bool, reachable int, unreachable int, err error) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - nodes, err := client.ListNodes(ctx, &swarmapi.ListNodesRequest{}) + nodes, err := client.ListNodes( + ctx, &swarmapi.ListNodesRequest{}, + grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse), + ) if err != nil { return false, 0, 0, err } diff --git a/daemon/cluster/configs.go b/daemon/cluster/configs.go index 6b373e618b111..f44adb284e92b 100644 --- a/daemon/cluster/configs.go +++ b/daemon/cluster/configs.go @@ -7,6 +7,7 @@ import ( types "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/daemon/cluster/convert" swarmapi "github.com/docker/swarmkit/api" + "google.golang.org/grpc" ) // GetConfig returns a config from a managed swarm cluster @@ -44,7 +45,8 @@ func (c *Cluster) GetConfigs(options apitypes.ConfigListOptions) ([]types.Config defer cancel() r, err := state.controlClient.ListConfigs(ctx, - &swarmapi.ListConfigsRequest{Filters: filters}) + &swarmapi.ListConfigsRequest{Filters: filters}, + grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse)) if err != nil { return nil, err } diff --git a/daemon/cluster/controllers/plugin/controller.go b/daemon/cluster/controllers/plugin/controller.go index 371f918d39964..7d4b64a65d5d4 100644 --- a/daemon/cluster/controllers/plugin/controller.go +++ b/daemon/cluster/controllers/plugin/controller.go @@ -3,7 +3,6 @@ package plugin // import "github.com/docker/docker/daemon/cluster/controllers/pl import ( "context" "io" - "io/ioutil" "net/http" "github.com/docker/distribution/reference" @@ -11,7 +10,7 @@ import ( "github.com/docker/docker/api/types/swarm/runtime" "github.com/docker/docker/errdefs" "github.com/docker/docker/plugin" - "github.com/docker/docker/plugin/v2" + v2 "github.com/docker/docker/plugin/v2" "github.com/docker/swarmkit/api" "github.com/gogo/protobuf/proto" "github.com/pkg/errors" @@ -34,7 +33,6 @@ type Controller struct { pluginID string serviceID string - taskID string // hook used to signal tests that `Wait()` is actually ready and waiting signalWaitReady func() @@ -119,10 +117,10 @@ func (p *Controller) Prepare(ctx context.Context) (err error) { } } p.pluginID = pl.GetID() - return p.backend.Upgrade(ctx, remote, p.spec.Name, nil, &authConfig, privs, ioutil.Discard) + return p.backend.Upgrade(ctx, remote, p.spec.Name, nil, &authConfig, privs, io.Discard) } - if err := p.backend.Pull(ctx, remote, p.spec.Name, nil, &authConfig, privs, ioutil.Discard, plugin.WithSwarmService(p.serviceID)); err != nil { + if err := p.backend.Pull(ctx, remote, p.spec.Name, nil, &authConfig, privs, io.Discard, plugin.WithSwarmService(p.serviceID), plugin.WithEnv(p.spec.Env)); err != nil { return err } pl, err = p.backend.Get(p.spec.Name) diff --git a/daemon/cluster/controllers/plugin/controller_test.go b/daemon/cluster/controllers/plugin/controller_test.go index 8329d44766d18..8130e6f48b42e 100644 --- a/daemon/cluster/controllers/plugin/controller_test.go +++ b/daemon/cluster/controllers/plugin/controller_test.go @@ -4,7 +4,6 @@ import ( "context" "errors" "io" - "io/ioutil" "net/http" "strings" "testing" @@ -15,7 +14,7 @@ import ( "github.com/docker/docker/api/types/swarm/runtime" "github.com/docker/docker/pkg/pubsub" "github.com/docker/docker/plugin" - "github.com/docker/docker/plugin/v2" + v2 "github.com/docker/docker/plugin/v2" "github.com/sirupsen/logrus" ) @@ -108,7 +107,7 @@ func TestWaitCancel(t *testing.T) { } ctxCancel, cancel := context.WithCancel(ctx) - chErr := make(chan error) + chErr := make(chan error, 1) go func() { chErr <- c.Wait(ctxCancel) }() @@ -134,7 +133,7 @@ func TestWaitDisabled(t *testing.T) { t.Fatal(err) } - chErr := make(chan error) + chErr := make(chan error, 1) go func() { chErr <- c.Wait(ctx) }() @@ -215,7 +214,7 @@ func TestWaitEnabled(t *testing.T) { t.Fatal(err) } - chErr := make(chan error) + chErr := make(chan error, 1) go func() { chErr <- c.Wait(ctx) }() @@ -321,7 +320,7 @@ func TestRemove(t *testing.T) { func newTestController(b Backend, disabled bool) *Controller { return &Controller{ - logger: &logrus.Entry{Logger: &logrus.Logger{Out: ioutil.Discard}}, + logger: &logrus.Entry{Logger: &logrus.Logger{Out: io.Discard}}, backend: b, spec: runtime.PluginSpec{ Name: pluginTestName, diff --git a/daemon/cluster/convert/container.go b/daemon/cluster/convert/container.go index effd362a12992..a743442331ff2 100644 --- a/daemon/cluster/convert/container.go +++ b/daemon/cluster/convert/container.go @@ -1,15 +1,16 @@ package convert // import "github.com/docker/docker/daemon/cluster/convert" import ( - "errors" "fmt" "strings" "github.com/docker/docker/api/types/container" mounttypes "github.com/docker/docker/api/types/mount" types "github.com/docker/docker/api/types/swarm" + "github.com/docker/go-units" swarmapi "github.com/docker/swarmkit/api" gogotypes "github.com/gogo/protobuf/types" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -18,25 +19,28 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec { return nil } containerSpec := &types.ContainerSpec{ - Image: c.Image, - Labels: c.Labels, - Command: c.Command, - Args: c.Args, - Hostname: c.Hostname, - Env: c.Env, - Dir: c.Dir, - User: c.User, - Groups: c.Groups, - StopSignal: c.StopSignal, - TTY: c.TTY, - OpenStdin: c.OpenStdin, - ReadOnly: c.ReadOnly, - Hosts: c.Hosts, - Secrets: secretReferencesFromGRPC(c.Secrets), - Configs: configReferencesFromGRPC(c.Configs), - Isolation: IsolationFromGRPC(c.Isolation), - Init: initFromGRPC(c.Init), - Sysctls: c.Sysctls, + Image: c.Image, + Labels: c.Labels, + Command: c.Command, + Args: c.Args, + Hostname: c.Hostname, + Env: c.Env, + Dir: c.Dir, + User: c.User, + Groups: c.Groups, + StopSignal: c.StopSignal, + TTY: c.TTY, + OpenStdin: c.OpenStdin, + ReadOnly: c.ReadOnly, + Hosts: c.Hosts, + Secrets: secretReferencesFromGRPC(c.Secrets), + Configs: configReferencesFromGRPC(c.Configs), + Isolation: IsolationFromGRPC(c.Isolation), + Init: initFromGRPC(c.Init), + Sysctls: c.Sysctls, + CapabilityAdd: c.CapabilityAdd, + CapabilityDrop: c.CapabilityDrop, + Ulimits: ulimitsFromGRPC(c.Ulimits), } if c.DNSConfig != nil { @@ -52,13 +56,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec { containerSpec.Privileges = &types.Privileges{} if c.Privileges.CredentialSpec != nil { - containerSpec.Privileges.CredentialSpec = &types.CredentialSpec{} - switch c.Privileges.CredentialSpec.Source.(type) { - case *swarmapi.Privileges_CredentialSpec_File: - containerSpec.Privileges.CredentialSpec.File = c.Privileges.CredentialSpec.GetFile() - case *swarmapi.Privileges_CredentialSpec_Registry: - containerSpec.Privileges.CredentialSpec.Registry = c.Privileges.CredentialSpec.GetRegistry() - } + containerSpec.Privileges.CredentialSpec = credentialSpecFromGRPC(c.Privileges.CredentialSpec) } if c.Privileges.SELinuxContext != nil { @@ -83,7 +81,8 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec { if m.BindOptions != nil { mount.BindOptions = &mounttypes.BindOptions{ - Propagation: mounttypes.Propagation(strings.ToLower(swarmapi.Mount_BindOptions_MountPropagation_name[int32(m.BindOptions.Propagation)])), + Propagation: mounttypes.Propagation(strings.ToLower(swarmapi.Mount_BindOptions_MountPropagation_name[int32(m.BindOptions.Propagation)])), + NonRecursive: m.BindOptions.NonRecursive, } } @@ -184,14 +183,26 @@ func secretReferencesFromGRPC(sr []*swarmapi.SecretReference) []*types.SecretRef return refs } -func configReferencesToGRPC(sr []*types.ConfigReference) []*swarmapi.ConfigReference { +func configReferencesToGRPC(sr []*types.ConfigReference) ([]*swarmapi.ConfigReference, error) { refs := make([]*swarmapi.ConfigReference, 0, len(sr)) for _, s := range sr { ref := &swarmapi.ConfigReference{ ConfigID: s.ConfigID, ConfigName: s.ConfigName, } - if s.File != nil { + switch { + case s.Runtime == nil && s.File == nil: + return nil, errors.New("either File or Runtime should be set") + case s.Runtime != nil && s.File != nil: + return nil, errors.New("cannot specify both File and Runtime") + case s.Runtime != nil: + // Runtime target was added in API v1.40 and takes precedence over + // File target. However, File and Runtime targets are mutually exclusive, + // so we should never have both. + ref.Target = &swarmapi.ConfigReference_Runtime{ + Runtime: &swarmapi.RuntimeTarget{}, + } + case s.File != nil: ref.Target = &swarmapi.ConfigReference_File{ File: &swarmapi.FileTarget{ Name: s.File.Name, @@ -205,28 +216,32 @@ func configReferencesToGRPC(sr []*types.ConfigReference) []*swarmapi.ConfigRefer refs = append(refs, ref) } - return refs + return refs, nil } func configReferencesFromGRPC(sr []*swarmapi.ConfigReference) []*types.ConfigReference { refs := make([]*types.ConfigReference, 0, len(sr)) for _, s := range sr { - target := s.GetFile() - if target == nil { - // not a file target - logrus.Warnf("config target not a file: config=%s", s.ConfigID) - continue + + r := &types.ConfigReference{ + ConfigID: s.ConfigID, + ConfigName: s.ConfigName, } - refs = append(refs, &types.ConfigReference{ - File: &types.ConfigReferenceFileTarget{ + if target := s.GetRuntime(); target != nil { + r.Runtime = &types.ConfigReferenceRuntimeTarget{} + } else if target := s.GetFile(); target != nil { + r.File = &types.ConfigReferenceFileTarget{ Name: target.Name, UID: target.UID, GID: target.GID, Mode: target.Mode, - }, - ConfigID: s.ConfigID, - ConfigName: s.ConfigName, - }) + } + } else { + // not a file target + logrus.Warnf("config target not known: config=%s", s.ConfigID) + continue + } + refs = append(refs, r) } return refs @@ -234,25 +249,27 @@ func configReferencesFromGRPC(sr []*swarmapi.ConfigReference) []*types.ConfigRef func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) { containerSpec := &swarmapi.ContainerSpec{ - Image: c.Image, - Labels: c.Labels, - Command: c.Command, - Args: c.Args, - Hostname: c.Hostname, - Env: c.Env, - Dir: c.Dir, - User: c.User, - Groups: c.Groups, - StopSignal: c.StopSignal, - TTY: c.TTY, - OpenStdin: c.OpenStdin, - ReadOnly: c.ReadOnly, - Hosts: c.Hosts, - Secrets: secretReferencesToGRPC(c.Secrets), - Configs: configReferencesToGRPC(c.Configs), - Isolation: isolationToGRPC(c.Isolation), - Init: initToGRPC(c.Init), - Sysctls: c.Sysctls, + Image: c.Image, + Labels: c.Labels, + Command: c.Command, + Args: c.Args, + Hostname: c.Hostname, + Env: c.Env, + Dir: c.Dir, + User: c.User, + Groups: c.Groups, + StopSignal: c.StopSignal, + TTY: c.TTY, + OpenStdin: c.OpenStdin, + ReadOnly: c.ReadOnly, + Hosts: c.Hosts, + Secrets: secretReferencesToGRPC(c.Secrets), + Isolation: isolationToGRPC(c.Isolation), + Init: initToGRPC(c.Init), + Sysctls: c.Sysctls, + CapabilityAdd: c.CapabilityAdd, + CapabilityDrop: c.CapabilityDrop, + Ulimits: ulimitsToGRPC(c.Ulimits), } if c.DNSConfig != nil { @@ -272,22 +289,11 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) { containerSpec.Privileges = &swarmapi.Privileges{} if c.Privileges.CredentialSpec != nil { - containerSpec.Privileges.CredentialSpec = &swarmapi.Privileges_CredentialSpec{} - - if c.Privileges.CredentialSpec.File != "" && c.Privileges.CredentialSpec.Registry != "" { - return nil, errors.New("cannot specify both \"file\" and \"registry\" credential specs") - } - if c.Privileges.CredentialSpec.File != "" { - containerSpec.Privileges.CredentialSpec.Source = &swarmapi.Privileges_CredentialSpec_File{ - File: c.Privileges.CredentialSpec.File, - } - } else if c.Privileges.CredentialSpec.Registry != "" { - containerSpec.Privileges.CredentialSpec.Source = &swarmapi.Privileges_CredentialSpec_Registry{ - Registry: c.Privileges.CredentialSpec.Registry, - } - } else { - return nil, errors.New("must either provide \"file\" or \"registry\" for credential spec") + cs, err := credentialSpecToGRPC(c.Privileges.CredentialSpec) + if err != nil { + return nil, errors.Wrap(err, "invalid CredentialSpec") } + containerSpec.Privileges.CredentialSpec = cs } if c.Privileges.SELinuxContext != nil { @@ -301,6 +307,14 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) { } } + if c.Configs != nil { + configs, err := configReferencesToGRPC(c.Configs) + if err != nil { + return nil, errors.Wrap(err, "invalid Config") + } + containerSpec.Configs = configs + } + // Mounts for _, m := range c.Mounts { mount := swarmapi.Mount{ @@ -321,6 +335,14 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) { } else if string(m.BindOptions.Propagation) != "" { return nil, fmt.Errorf("invalid MountPropagation: %q", m.BindOptions.Propagation) } + + if m.BindOptions.NonRecursive { + if mount.BindOptions == nil { + // the propagation defaults to rprivate + mount.BindOptions = &swarmapi.Mount_BindOptions{} + } + mount.BindOptions.NonRecursive = m.BindOptions.NonRecursive + } } if m.VolumeOptions != nil { @@ -353,6 +375,60 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) { return containerSpec, nil } +func credentialSpecFromGRPC(c *swarmapi.Privileges_CredentialSpec) *types.CredentialSpec { + cs := &types.CredentialSpec{} + switch c.Source.(type) { + case *swarmapi.Privileges_CredentialSpec_Config: + cs.Config = c.GetConfig() + case *swarmapi.Privileges_CredentialSpec_File: + cs.File = c.GetFile() + case *swarmapi.Privileges_CredentialSpec_Registry: + cs.Registry = c.GetRegistry() + } + return cs +} + +func credentialSpecToGRPC(c *types.CredentialSpec) (*swarmapi.Privileges_CredentialSpec, error) { + var opts []string + + if c.Config != "" { + opts = append(opts, `"config"`) + } + if c.File != "" { + opts = append(opts, `"file"`) + } + if c.Registry != "" { + opts = append(opts, `"registry"`) + } + l := len(opts) + switch { + case l == 0: + return nil, errors.New(`must either provide "file", "registry", or "config" for credential spec`) + case l == 2: + return nil, fmt.Errorf("cannot specify both %s and %s credential specs", opts[0], opts[1]) + case l > 2: + return nil, fmt.Errorf("cannot specify both %s, and %s credential specs", strings.Join(opts[:l-1], ", "), opts[l-1]) + } + + spec := &swarmapi.Privileges_CredentialSpec{} + switch { + case c.Config != "": + spec.Source = &swarmapi.Privileges_CredentialSpec_Config{ + Config: c.Config, + } + case c.File != "": + spec.Source = &swarmapi.Privileges_CredentialSpec_File{ + File: c.File, + } + case c.Registry != "": + spec.Source = &swarmapi.Privileges_CredentialSpec_Registry{ + Registry: c.Registry, + } + } + + return spec, nil +} + func healthConfigFromGRPC(h *swarmapi.HealthConfig) *container.HealthConfig { interval, _ := gogotypes.DurationFromProto(h.Interval) timeout, _ := gogotypes.DurationFromProto(h.Timeout) @@ -398,3 +474,31 @@ func isolationToGRPC(i container.Isolation) swarmapi.ContainerSpec_Isolation { } return swarmapi.ContainerIsolationDefault } + +func ulimitsFromGRPC(u []*swarmapi.ContainerSpec_Ulimit) []*units.Ulimit { + ulimits := make([]*units.Ulimit, len(u)) + + for i, ulimit := range u { + ulimits[i] = &units.Ulimit{ + Name: ulimit.Name, + Soft: ulimit.Soft, + Hard: ulimit.Hard, + } + } + + return ulimits +} + +func ulimitsToGRPC(u []*units.Ulimit) []*swarmapi.ContainerSpec_Ulimit { + ulimits := make([]*swarmapi.ContainerSpec_Ulimit, len(u)) + + for i, ulimit := range u { + ulimits[i] = &swarmapi.ContainerSpec_Ulimit{ + Name: ulimit.Name, + Soft: ulimit.Soft, + Hard: ulimit.Hard, + } + } + + return ulimits +} diff --git a/daemon/cluster/convert/network.go b/daemon/cluster/convert/network.go index 34660fc4ffc9d..356e7b7397c65 100644 --- a/daemon/cluster/convert/network.go +++ b/daemon/cluster/convert/network.go @@ -6,7 +6,7 @@ import ( basictypes "github.com/docker/docker/api/types" networktypes "github.com/docker/docker/api/types/network" types "github.com/docker/docker/api/types/swarm" - netconst "github.com/docker/libnetwork/datastore" + netconst "github.com/docker/docker/libnetwork/datastore" swarmapi "github.com/docker/swarmkit/api" gogotypes "github.com/gogo/protobuf/types" ) @@ -47,10 +47,10 @@ func networkFromGRPC(n *swarmapi.Network) types.Network { network.CreatedAt, _ = gogotypes.TimestampFromProto(n.Meta.CreatedAt) network.UpdatedAt, _ = gogotypes.TimestampFromProto(n.Meta.UpdatedAt) - //Annotations + // Annotations network.Spec.Annotations = annotationsFromGRPC(n.Spec.Annotations) - //DriverConfiguration + // DriverConfiguration if n.Spec.DriverConfig != nil { network.Spec.DriverConfiguration = &types.Driver{ Name: n.Spec.DriverConfig.Name, @@ -58,7 +58,7 @@ func networkFromGRPC(n *swarmapi.Network) types.Network { } } - //DriverState + // DriverState if n.DriverState != nil { network.DriverState = types.Driver{ Name: n.DriverState.Name, diff --git a/daemon/cluster/convert/node.go b/daemon/cluster/convert/node.go index 00636b6ab42b1..488fb909e69e7 100644 --- a/daemon/cluster/convert/node.go +++ b/daemon/cluster/convert/node.go @@ -29,10 +29,10 @@ func NodeFromGRPC(n swarmapi.Node) types.Node { node.CreatedAt, _ = gogotypes.TimestampFromProto(n.Meta.CreatedAt) node.UpdatedAt, _ = gogotypes.TimestampFromProto(n.Meta.UpdatedAt) - //Annotations + // Annotations node.Spec.Annotations = annotationsFromGRPC(n.Spec.Annotations) - //Description + // Description if n.Description != nil { node.Description.Hostname = n.Description.Hostname if n.Description.Platform != nil { @@ -58,7 +58,7 @@ func NodeFromGRPC(n swarmapi.Node) types.Node { } } - //Manager + // Manager if n.ManagerStatus != nil { node.ManagerStatus = &types.ManagerStatus{ Leader: n.ManagerStatus.Leader, diff --git a/daemon/cluster/convert/service.go b/daemon/cluster/convert/service.go index 09b023c9d7d3f..4161f89479598 100644 --- a/daemon/cluster/convert/service.go +++ b/daemon/cluster/convert/service.go @@ -44,6 +44,15 @@ func ServiceFromGRPC(s swarmapi.Service) (types.Service, error) { service.CreatedAt, _ = gogotypes.TimestampFromProto(s.Meta.CreatedAt) service.UpdatedAt, _ = gogotypes.TimestampFromProto(s.Meta.UpdatedAt) + if s.JobStatus != nil { + service.JobStatus = &types.JobStatus{ + JobIteration: types.Version{ + Index: s.JobStatus.JobIteration.Index, + }, + } + service.JobStatus.LastExecution, _ = gogotypes.TimestampFromProto(s.JobStatus.LastExecution) + } + // UpdateStatus if s.UpdateStatus != nil { service.UpdateStatus = &types.UpdateStatus{} @@ -131,6 +140,13 @@ func serviceSpecFromGRPC(spec *swarmapi.ServiceSpec) (*types.ServiceSpec, error) convertedSpec.Mode.Replicated = &types.ReplicatedService{ Replicas: &t.Replicated.Replicas, } + case *swarmapi.ServiceSpec_ReplicatedJob: + convertedSpec.Mode.ReplicatedJob = &types.ReplicatedJob{ + MaxConcurrent: &t.ReplicatedJob.MaxConcurrent, + TotalCompletions: &t.ReplicatedJob.TotalCompletions, + } + case *swarmapi.ServiceSpec_GlobalJob: + convertedSpec.Mode.GlobalJob = &types.GlobalJob{} } return convertedSpec, nil @@ -177,6 +193,10 @@ func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) { if err != nil { return swarmapi.ServiceSpec{}, err } + if s.TaskTemplate.Resources != nil && s.TaskTemplate.Resources.Limits != nil { + // TODO remove this (or keep for backward compat) once SwarmKit API moved PidsLimit into Resources + containerSpec.PidsLimit = s.TaskTemplate.Resources.Limits.Pids + } spec.Task.Runtime = &swarmapi.TaskSpec_Container{Container: containerSpec} } else { // If the ContainerSpec is nil, we can't set the task runtime @@ -246,6 +266,7 @@ func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) { spec.Task.Placement = &swarmapi.Placement{ Constraints: s.TaskTemplate.Placement.Constraints, Preferences: preferences, + MaxReplicas: s.TaskTemplate.Placement.MaxReplicas, Platforms: platforms, } } @@ -282,14 +303,52 @@ func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) { } // Mode - if s.Mode.Global != nil && s.Mode.Replicated != nil { - return swarmapi.ServiceSpec{}, fmt.Errorf("cannot specify both replicated mode and global mode") + numModes := 0 + if s.Mode.Global != nil { + numModes++ + } + if s.Mode.Replicated != nil { + numModes++ + } + if s.Mode.ReplicatedJob != nil { + numModes++ + } + if s.Mode.GlobalJob != nil { + numModes++ + } + + if numModes > 1 { + return swarmapi.ServiceSpec{}, fmt.Errorf("must specify only one service mode") } if s.Mode.Global != nil { spec.Mode = &swarmapi.ServiceSpec_Global{ Global: &swarmapi.GlobalService{}, } + } else if s.Mode.GlobalJob != nil { + spec.Mode = &swarmapi.ServiceSpec_GlobalJob{ + GlobalJob: &swarmapi.GlobalJob{}, + } + } else if s.Mode.ReplicatedJob != nil { + // if the service is a replicated job, we have two different kinds of + // values that might need to be defaulted. + + r := &swarmapi.ReplicatedJob{} + if s.Mode.ReplicatedJob.MaxConcurrent != nil { + r.MaxConcurrent = *s.Mode.ReplicatedJob.MaxConcurrent + } else { + r.MaxConcurrent = 1 + } + + if s.Mode.ReplicatedJob.TotalCompletions != nil { + r.TotalCompletions = *s.Mode.ReplicatedJob.TotalCompletions + } else { + r.TotalCompletions = r.MaxConcurrent + } + + spec.Mode = &swarmapi.ServiceSpec_ReplicatedJob{ + ReplicatedJob: r, + } } else if s.Mode.Replicated != nil && s.Mode.Replicated.Replicas != nil { spec.Mode = &swarmapi.ServiceSpec_Replicated{ Replicated: &swarmapi.ReplicatedService{Replicas: *s.Mode.Replicated.Replicas}, @@ -341,15 +400,31 @@ func GenericResourcesFromGRPC(genericRes []*swarmapi.GenericResource) []types.Ge return generic } -func resourcesFromGRPC(res *swarmapi.ResourceRequirements) *types.ResourceRequirements { +// resourcesFromGRPC creates a ResourceRequirements from the GRPC TaskSpec. +// We currently require the whole TaskSpec to be passed, because PidsLimit +// is returned as part of the container spec, instead of Resources +// TODO move PidsLimit to Resources in the Swarm API +func resourcesFromGRPC(ts *swarmapi.TaskSpec) *types.ResourceRequirements { var resources *types.ResourceRequirements - if res != nil { - resources = &types.ResourceRequirements{} + + if cs := ts.GetContainer(); cs != nil && cs.PidsLimit != 0 { + resources = &types.ResourceRequirements{ + Limits: &types.Limit{ + Pids: cs.PidsLimit, + }, + } + } + if ts.Resources != nil { + if resources == nil { + resources = &types.ResourceRequirements{} + } + res := ts.Resources if res.Limits != nil { - resources.Limits = &types.Resources{ - NanoCPUs: res.Limits.NanoCPUs, - MemoryBytes: res.Limits.MemoryBytes, + if resources.Limits == nil { + resources.Limits = &types.Limit{} } + resources.Limits.NanoCPUs = res.Limits.NanoCPUs + resources.Limits.MemoryBytes = res.Limits.MemoryBytes } if res.Reservations != nil { resources.Reservations = &types.Resources{ @@ -386,6 +461,7 @@ func resourcesToGRPC(res *types.ResourceRequirements) *swarmapi.ResourceRequirem if res != nil { reqs = &swarmapi.ResourceRequirements{} if res.Limits != nil { + // TODO add PidsLimit once Swarm API has been updated to move it into Limits reqs.Limits = &swarmapi.Resources{ NanoCPUs: res.Limits.NanoCPUs, MemoryBytes: res.Limits.MemoryBytes, @@ -472,6 +548,7 @@ func placementFromGRPC(p *swarmapi.Placement) *types.Placement { } r := &types.Placement{ Constraints: p.Constraints, + MaxReplicas: p.MaxReplicas, } for _, pref := range p.Preferences { @@ -601,7 +678,7 @@ func taskSpecFromGRPC(taskSpec swarmapi.TaskSpec) (types.TaskSpec, error) { } t := types.TaskSpec{ - Resources: resourcesFromGRPC(taskSpec.Resources), + Resources: resourcesFromGRPC(&taskSpec), RestartPolicy: restartPolicyFromGRPC(taskSpec.Restart), Placement: placementFromGRPC(taskSpec.Placement), LogDriver: driverFromGRPC(taskSpec.LogDriver), diff --git a/daemon/cluster/convert/service_test.go b/daemon/cluster/convert/service_test.go index ad5f0d4494246..2b4ef94eee8e9 100644 --- a/daemon/cluster/convert/service_test.go +++ b/daemon/cluster/convert/service_test.go @@ -8,7 +8,7 @@ import ( "github.com/docker/docker/api/types/swarm/runtime" swarmapi "github.com/docker/swarmkit/api" google_protobuf3 "github.com/gogo/protobuf/types" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestServiceConvertFromGRPCRuntimeContainer(t *testing.T) { @@ -233,6 +233,167 @@ func TestServiceConvertFromGRPCIsolation(t *testing.T) { } } +func TestServiceConvertToGRPCCredentialSpec(t *testing.T) { + cases := []struct { + name string + from swarmtypes.CredentialSpec + to swarmapi.Privileges_CredentialSpec + expectedErr string + }{ + { + name: "empty credential spec", + from: swarmtypes.CredentialSpec{}, + to: swarmapi.Privileges_CredentialSpec{}, + expectedErr: `invalid CredentialSpec: must either provide "file", "registry", or "config" for credential spec`, + }, + { + name: "config and file credential spec", + from: swarmtypes.CredentialSpec{ + Config: "0bt9dmxjvjiqermk6xrop3ekq", + File: "spec.json", + }, + to: swarmapi.Privileges_CredentialSpec{}, + expectedErr: `invalid CredentialSpec: cannot specify both "config" and "file" credential specs`, + }, + { + name: "config and registry credential spec", + from: swarmtypes.CredentialSpec{ + Config: "0bt9dmxjvjiqermk6xrop3ekq", + Registry: "testing", + }, + to: swarmapi.Privileges_CredentialSpec{}, + expectedErr: `invalid CredentialSpec: cannot specify both "config" and "registry" credential specs`, + }, + { + name: "file and registry credential spec", + from: swarmtypes.CredentialSpec{ + File: "spec.json", + Registry: "testing", + }, + to: swarmapi.Privileges_CredentialSpec{}, + expectedErr: `invalid CredentialSpec: cannot specify both "file" and "registry" credential specs`, + }, + { + name: "config and file and registry credential spec", + from: swarmtypes.CredentialSpec{ + Config: "0bt9dmxjvjiqermk6xrop3ekq", + File: "spec.json", + Registry: "testing", + }, + to: swarmapi.Privileges_CredentialSpec{}, + expectedErr: `invalid CredentialSpec: cannot specify both "config", "file", and "registry" credential specs`, + }, + { + name: "config credential spec", + from: swarmtypes.CredentialSpec{Config: "0bt9dmxjvjiqermk6xrop3ekq"}, + to: swarmapi.Privileges_CredentialSpec{ + Source: &swarmapi.Privileges_CredentialSpec_Config{Config: "0bt9dmxjvjiqermk6xrop3ekq"}, + }, + }, + { + name: "file credential spec", + from: swarmtypes.CredentialSpec{File: "foo.json"}, + to: swarmapi.Privileges_CredentialSpec{ + Source: &swarmapi.Privileges_CredentialSpec_File{File: "foo.json"}, + }, + }, + { + name: "registry credential spec", + from: swarmtypes.CredentialSpec{Registry: "testing"}, + to: swarmapi.Privileges_CredentialSpec{ + Source: &swarmapi.Privileges_CredentialSpec_Registry{Registry: "testing"}, + }, + }, + } + + for _, c := range cases { + c := c + t.Run(c.name, func(t *testing.T) { + s := swarmtypes.ServiceSpec{ + TaskTemplate: swarmtypes.TaskSpec{ + ContainerSpec: &swarmtypes.ContainerSpec{ + Privileges: &swarmtypes.Privileges{ + CredentialSpec: &c.from, + }, + }, + }, + } + + res, err := ServiceSpecToGRPC(s) + if c.expectedErr != "" { + assert.Error(t, err, c.expectedErr) + return + } + + assert.NilError(t, err) + v, ok := res.Task.Runtime.(*swarmapi.TaskSpec_Container) + if !ok { + t.Fatal("expected type swarmapi.TaskSpec_Container") + } + assert.DeepEqual(t, c.to, *v.Container.Privileges.CredentialSpec) + }) + } +} + +func TestServiceConvertFromGRPCCredentialSpec(t *testing.T) { + cases := []struct { + name string + from swarmapi.Privileges_CredentialSpec + to *swarmtypes.CredentialSpec + }{ + { + name: "empty credential spec", + from: swarmapi.Privileges_CredentialSpec{}, + to: &swarmtypes.CredentialSpec{}, + }, + { + name: "config credential spec", + from: swarmapi.Privileges_CredentialSpec{ + Source: &swarmapi.Privileges_CredentialSpec_Config{Config: "0bt9dmxjvjiqermk6xrop3ekq"}, + }, + to: &swarmtypes.CredentialSpec{Config: "0bt9dmxjvjiqermk6xrop3ekq"}, + }, + { + name: "file credential spec", + from: swarmapi.Privileges_CredentialSpec{ + Source: &swarmapi.Privileges_CredentialSpec_File{File: "foo.json"}, + }, + to: &swarmtypes.CredentialSpec{File: "foo.json"}, + }, + { + name: "registry credential spec", + from: swarmapi.Privileges_CredentialSpec{ + Source: &swarmapi.Privileges_CredentialSpec_Registry{Registry: "testing"}, + }, + to: &swarmtypes.CredentialSpec{Registry: "testing"}, + }, + } + + for _, tc := range cases { + tc := tc + + t.Run(tc.name, func(t *testing.T) { + gs := swarmapi.Service{ + Spec: swarmapi.ServiceSpec{ + Task: swarmapi.TaskSpec{ + Runtime: &swarmapi.TaskSpec_Container{ + Container: &swarmapi.ContainerSpec{ + Privileges: &swarmapi.Privileges{ + CredentialSpec: &tc.from, + }, + }, + }, + }, + }, + } + + svc, err := ServiceFromGRPC(gs) + assert.NilError(t, err) + assert.DeepEqual(t, svc.Spec.TaskTemplate.ContainerSpec.Privileges.CredentialSpec, tc.to) + }) + } +} + func TestServiceConvertToGRPCNetworkAtachmentRuntime(t *testing.T) { someid := "asfjkl" s := swarmtypes.ServiceSpec{ @@ -306,3 +467,147 @@ func TestTaskConvertFromGRPCNetworkAttachment(t *testing.T) { t.Fatalf("expected Runtime to be %v", swarmtypes.RuntimeNetworkAttachment) } } + +// TestServiceConvertFromGRPCConfigs tests that converting config references +// from GRPC is correct +func TestServiceConvertFromGRPCConfigs(t *testing.T) { + cases := []struct { + name string + from *swarmapi.ConfigReference + to *swarmtypes.ConfigReference + }{ + { + name: "file", + from: &swarmapi.ConfigReference{ + ConfigID: "configFile", + ConfigName: "configFile", + Target: &swarmapi.ConfigReference_File{ + // skip mode, if everything else here works mode will too. otherwise we'd need to import os. + File: &swarmapi.FileTarget{Name: "foo", UID: "bar", GID: "baz"}, + }, + }, + to: &swarmtypes.ConfigReference{ + ConfigID: "configFile", + ConfigName: "configFile", + File: &swarmtypes.ConfigReferenceFileTarget{Name: "foo", UID: "bar", GID: "baz"}, + }, + }, + { + name: "runtime", + from: &swarmapi.ConfigReference{ + ConfigID: "configRuntime", + ConfigName: "configRuntime", + Target: &swarmapi.ConfigReference_Runtime{Runtime: &swarmapi.RuntimeTarget{}}, + }, + to: &swarmtypes.ConfigReference{ + ConfigID: "configRuntime", + ConfigName: "configRuntime", + Runtime: &swarmtypes.ConfigReferenceRuntimeTarget{}, + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + grpcService := swarmapi.Service{ + Spec: swarmapi.ServiceSpec{ + Task: swarmapi.TaskSpec{ + Runtime: &swarmapi.TaskSpec_Container{ + Container: &swarmapi.ContainerSpec{ + Configs: []*swarmapi.ConfigReference{tc.from}, + }, + }, + }, + }, + } + + engineService, err := ServiceFromGRPC(grpcService) + assert.NilError(t, err) + assert.DeepEqual(t, + engineService.Spec.TaskTemplate.ContainerSpec.Configs[0], + tc.to, + ) + }) + } +} + +// TestServiceConvertToGRPCConfigs tests that converting config references to +// GRPC is correct +func TestServiceConvertToGRPCConfigs(t *testing.T) { + cases := []struct { + name string + from *swarmtypes.ConfigReference + to *swarmapi.ConfigReference + expectedErr string + }{ + { + name: "file", + from: &swarmtypes.ConfigReference{ + ConfigID: "configFile", + ConfigName: "configFile", + File: &swarmtypes.ConfigReferenceFileTarget{Name: "foo", UID: "bar", GID: "baz"}, + }, + to: &swarmapi.ConfigReference{ + ConfigID: "configFile", + ConfigName: "configFile", + Target: &swarmapi.ConfigReference_File{ + // skip mode, if everything else here works mode will too. otherwise we'd need to import os. + File: &swarmapi.FileTarget{Name: "foo", UID: "bar", GID: "baz"}, + }, + }, + }, + { + name: "runtime", + from: &swarmtypes.ConfigReference{ + ConfigID: "configRuntime", + ConfigName: "configRuntime", + Runtime: &swarmtypes.ConfigReferenceRuntimeTarget{}, + }, + to: &swarmapi.ConfigReference{ + ConfigID: "configRuntime", + ConfigName: "configRuntime", + Target: &swarmapi.ConfigReference_Runtime{Runtime: &swarmapi.RuntimeTarget{}}, + }, + }, + { + name: "file and runtime", + from: &swarmtypes.ConfigReference{ + ConfigID: "fileAndRuntime", + ConfigName: "fileAndRuntime", + File: &swarmtypes.ConfigReferenceFileTarget{}, + Runtime: &swarmtypes.ConfigReferenceRuntimeTarget{}, + }, + expectedErr: "invalid Config: cannot specify both File and Runtime", + }, + { + name: "none", + from: &swarmtypes.ConfigReference{ + ConfigID: "none", + ConfigName: "none", + }, + expectedErr: "invalid Config: either File or Runtime should be set", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + engineServiceSpec := swarmtypes.ServiceSpec{ + TaskTemplate: swarmtypes.TaskSpec{ + ContainerSpec: &swarmtypes.ContainerSpec{ + Configs: []*swarmtypes.ConfigReference{tc.from}, + }, + }, + } + + grpcServiceSpec, err := ServiceSpecToGRPC(engineServiceSpec) + if tc.expectedErr != "" { + assert.Error(t, err, tc.expectedErr) + return + } + + assert.NilError(t, err) + taskRuntime := grpcServiceSpec.Task.Runtime.(*swarmapi.TaskSpec_Container) + assert.DeepEqual(t, taskRuntime.Container.Configs[0], tc.to) + }) + } +} diff --git a/daemon/cluster/convert/swarm.go b/daemon/cluster/convert/swarm.go index 643505badf61f..bdad2a5ea711c 100644 --- a/daemon/cluster/convert/swarm.go +++ b/daemon/cluster/convert/swarm.go @@ -42,6 +42,7 @@ func SwarmFromGRPC(c swarmapi.Cluster) types.Swarm { RootRotationInProgress: c.RootCA.RootRotation != nil, DefaultAddrPool: c.DefaultAddressPool, SubnetSize: c.SubnetSize, + DataPathPort: c.VXLANUDPPort, }, JoinTokens: types.JoinTokens{ Worker: c.RootCA.JoinTokens.Worker, diff --git a/daemon/cluster/convert/task.go b/daemon/cluster/convert/task.go index 72e2805e1eecb..b29cf4871fa91 100644 --- a/daemon/cluster/convert/task.go +++ b/daemon/cluster/convert/task.go @@ -51,6 +51,12 @@ func TaskFromGRPC(t swarmapi.Task) (types.Task, error) { task.NetworksAttachments = append(task.NetworksAttachments, networkAttachmentFromGRPC(na)) } + if t.JobIteration != nil { + task.JobIteration = &types.Version{ + Index: t.JobIteration.Index, + } + } + if t.Status.PortStatus == nil { return task, nil } diff --git a/daemon/cluster/executor/backend.go b/daemon/cluster/executor/backend.go index cfbc86ce366da..f342d522fcade 100644 --- a/daemon/cluster/executor/backend.go +++ b/daemon/cluster/executor/backend.go @@ -17,11 +17,11 @@ import ( containerpkg "github.com/docker/docker/container" clustertypes "github.com/docker/docker/daemon/cluster/provider" networkSettings "github.com/docker/docker/daemon/network" + "github.com/docker/docker/libnetwork" + "github.com/docker/docker/libnetwork/cluster" + networktypes "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/plugin" volumeopts "github.com/docker/docker/volume/service/opts" - "github.com/docker/libnetwork" - "github.com/docker/libnetwork/cluster" - networktypes "github.com/docker/libnetwork/types" "github.com/docker/swarmkit/agent/exec" specs "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -48,7 +48,7 @@ type Backend interface { SetContainerDependencyStore(name string, store exec.DependencyGetter) error SetContainerSecretReferences(name string, refs []*swarmtypes.SecretReference) error SetContainerConfigReferences(name string, refs []*swarmtypes.ConfigReference) error - SystemInfo() (*types.Info, error) + SystemInfo() *types.Info Containers(config *types.ContainerListOptions) ([]*types.Container, error) SetNetworkBootstrapKeys([]*networktypes.EncryptionKey) error DaemonJoinsCluster(provider cluster.Provider) @@ -61,6 +61,7 @@ type Backend interface { PluginManager() *plugin.Manager PluginGetter() *plugin.Store GetAttachmentStore() *networkSettings.AttachmentStore + HasExperimental() bool } // VolumeBackend is used by an executor to perform volume operations @@ -71,6 +72,6 @@ type VolumeBackend interface { // ImageBackend is used by an executor to perform image operations type ImageBackend interface { PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error - GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, bool, error) + GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, error) LookupImage(name string) (*types.ImageInspect, error) } diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index 720b8447fc803..00b47e77ce228 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/daemon/cluster/executor/container/adapter.go @@ -20,13 +20,13 @@ import ( "github.com/docker/docker/daemon" "github.com/docker/docker/daemon/cluster/convert" executorpkg "github.com/docker/docker/daemon/cluster/executor" + "github.com/docker/docker/libnetwork" volumeopts "github.com/docker/docker/volume/service/opts" - "github.com/docker/libnetwork" "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/log" gogotypes "github.com/gogo/protobuf/types" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/time/rate" @@ -97,7 +97,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error { pr, pw := io.Pipe() metaHeaders := map[string][]string{} go func() { - // TODO @jhowardmsft LCOW Support: This will need revisiting as + // TODO LCOW Support: This will need revisiting as // the stack is built up to include LCOW support for swarm. err := c.imageBackend.PullImage(ctx, c.container.image(), "", nil, metaHeaders, authConfig, pw) pw.CloseWithError(err) @@ -222,12 +222,17 @@ func (c *containerAdapter) createNetworks(ctx context.Context) error { } func (c *containerAdapter) removeNetworks(ctx context.Context) error { + var ( + activeEndpointsError *libnetwork.ActiveEndpointsError + errNoSuchNetwork libnetwork.ErrNoSuchNetwork + ) + for name, v := range c.container.networksAttachments { if err := c.backend.DeleteManagedNetwork(v.Network.ID); err != nil { - switch errors.Cause(err).(type) { - case *libnetwork.ActiveEndpointsError: + switch { + case errors.As(err, &activeEndpointsError): continue - case libnetwork.ErrNoSuchNetwork: + case errors.As(err, &errNoSuchNetwork): continue default: log.G(ctx).Errorf("network %s remove failed: %v", name, err) @@ -426,6 +431,7 @@ func (c *containerAdapter) remove(ctx context.Context) error { func (c *containerAdapter) createVolumes(ctx context.Context) error { // Create plugin volumes that are embedded inside a Mount for _, mount := range c.container.task.Spec.GetContainer().Mounts { + mount := mount if mount.Type != api.MountTypeVolume { continue } diff --git a/daemon/cluster/executor/container/container.go b/daemon/cluster/executor/container/container.go index b26076bcd8c8c..7ee0b0dee5694 100644 --- a/daemon/cluster/executor/container/container.go +++ b/daemon/cluster/executor/container/container.go @@ -6,7 +6,6 @@ import ( "net" "strconv" "strings" - "time" "github.com/sirupsen/logrus" @@ -21,8 +20,9 @@ import ( "github.com/docker/docker/daemon/cluster/convert" executorpkg "github.com/docker/docker/daemon/cluster/executor" clustertypes "github.com/docker/docker/daemon/cluster/provider" + netconst "github.com/docker/docker/libnetwork/datastore" "github.com/docker/go-connections/nat" - netconst "github.com/docker/libnetwork/datastore" + "github.com/docker/go-units" "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/api/genericresource" @@ -31,10 +31,6 @@ import ( ) const ( - // Explicitly use the kernel's default setting for CPU quota of 100ms. - // https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt - cpuQuotaPeriod = 100 * time.Millisecond - // systemLabelPrefix represents the reserved namespace for system labels. systemLabelPrefix = "com.docker.swarm" ) @@ -103,10 +99,6 @@ func (c *containerConfig) taskID() string { return c.task.ID } -func (c *containerConfig) endpoint() *api.Endpoint { - return c.task.Endpoint -} - func (c *containerConfig) spec() *api.ContainerSpec { return c.task.Spec.GetContainer() } @@ -290,7 +282,9 @@ func convertMount(m api.Mount) enginemount.Mount { } if m.BindOptions != nil { - mount.BindOptions = &enginemount.BindOptions{} + mount.BindOptions = &enginemount.BindOptions{ + NonRecursive: m.BindOptions.NonRecursive, + } switch m.BindOptions.Propagation { case api.MountPropagationRPrivate: mount.BindOptions.Propagation = enginemount.PropagationRPrivate @@ -367,6 +361,8 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig { Isolation: c.isolation(), Init: c.init(), Sysctls: c.spec().Sysctls, + CapAdd: c.spec().CapabilityAdd, + CapDrop: c.spec().CapabilityDrop, } if c.spec().DNSConfig != nil { @@ -437,6 +433,21 @@ func (c *containerConfig) volumeCreateRequest(mount *api.Mount) *volumetypes.Vol func (c *containerConfig) resources() enginecontainer.Resources { resources := enginecontainer.Resources{} + // set pids limit + pidsLimit := c.spec().PidsLimit + if pidsLimit > 0 { + resources.PidsLimit = &pidsLimit + } + + resources.Ulimits = make([]*units.Ulimit, len(c.spec().Ulimits)) + for i, ulimit := range c.spec().Ulimits { + resources.Ulimits[i] = &units.Ulimit{ + Name: ulimit.Name, + Soft: ulimit.Soft, + Hard: ulimit.Hard, + } + } + // If no limits are specified let the engine use its defaults. // // TODO(aluzzardi): We might want to set some limits anyway otherwise @@ -451,9 +462,7 @@ func (c *containerConfig) resources() enginecontainer.Resources { } if r.Limits.NanoCPUs > 0 { - // CPU Period must be set in microseconds. - resources.CPUPeriod = int64(cpuQuotaPeriod / time.Microsecond) - resources.CPUQuota = r.Limits.NanoCPUs * resources.CPUPeriod / 1e9 + resources.NanoCPUs = r.Limits.NanoCPUs } return resources @@ -651,6 +660,8 @@ func (c *containerConfig) applyPrivileges(hc *enginecontainer.HostConfig) { hc.SecurityOpt = append(hc.SecurityOpt, "credentialspec=file://"+credentials.GetFile()) case *api.Privileges_CredentialSpec_Registry: hc.SecurityOpt = append(hc.SecurityOpt, "credentialspec=registry://"+credentials.GetRegistry()) + case *api.Privileges_CredentialSpec_Config: + hc.SecurityOpt = append(hc.SecurityOpt, "credentialspec=config://"+credentials.GetConfig()) } } diff --git a/daemon/cluster/executor/container/container_test.go b/daemon/cluster/executor/container/container_test.go index 5f967c2f771c8..88ba8beb1ca54 100644 --- a/daemon/cluster/executor/container/container_test.go +++ b/daemon/cluster/executor/container/container_test.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/api/types/container" swarmapi "github.com/docker/swarmkit/api" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestIsolationConversion(t *testing.T) { @@ -80,3 +80,56 @@ func TestContainerLabels(t *testing.T) { labels := c.labels() assert.DeepEqual(t, expected, labels) } + +func TestCredentialSpecConversion(t *testing.T) { + cases := []struct { + name string + from swarmapi.Privileges_CredentialSpec + to []string + }{ + { + name: "none", + from: swarmapi.Privileges_CredentialSpec{}, + to: nil, + }, + { + name: "config", + from: swarmapi.Privileges_CredentialSpec{ + Source: &swarmapi.Privileges_CredentialSpec_Config{Config: "0bt9dmxjvjiqermk6xrop3ekq"}, + }, + to: []string{"credentialspec=config://0bt9dmxjvjiqermk6xrop3ekq"}, + }, + { + name: "file", + from: swarmapi.Privileges_CredentialSpec{ + Source: &swarmapi.Privileges_CredentialSpec_File{File: "foo.json"}, + }, + to: []string{"credentialspec=file://foo.json"}, + }, + { + name: "registry", + from: swarmapi.Privileges_CredentialSpec{ + Source: &swarmapi.Privileges_CredentialSpec_Registry{Registry: "testing"}, + }, + to: []string{"credentialspec=registry://testing"}, + }, + } + for _, c := range cases { + c := c + t.Run(c.name, func(t *testing.T) { + task := swarmapi.Task{ + Spec: swarmapi.TaskSpec{ + Runtime: &swarmapi.TaskSpec_Container{ + Container: &swarmapi.ContainerSpec{ + Privileges: &swarmapi.Privileges{ + CredentialSpec: &c.from, + }, + }, + }, + }, + } + config := containerConfig{task: &task} + assert.DeepEqual(t, c.to, config.hostConfig().SecurityOpt) + }) + } +} diff --git a/daemon/cluster/executor/container/controller.go b/daemon/cluster/executor/container/controller.go index 5552b13328951..e3491a4b95e81 100644 --- a/daemon/cluster/executor/container/controller.go +++ b/daemon/cluster/executor/container/controller.go @@ -11,8 +11,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/events" executorpkg "github.com/docker/docker/daemon/cluster/executor" + "github.com/docker/docker/libnetwork" "github.com/docker/go-connections/nat" - "github.com/docker/libnetwork" "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/log" @@ -204,9 +204,10 @@ func (r *controller) Start(ctx context.Context) error { return exec.ErrTaskStarted } + var lnErr libnetwork.ErrNoSuchNetwork for { if err := r.adapter.start(ctx); err != nil { - if _, ok := errors.Cause(err).(libnetwork.ErrNoSuchNetwork); ok { + if errors.As(err, &lnErr) { // Retry network creation again if we // failed because some of the networks // were not found. @@ -369,11 +370,17 @@ func (r *controller) Shutdown(ctx context.Context) error { } if err := r.adapter.shutdown(ctx); err != nil { - if isUnknownContainer(err) || isStoppedContainer(err) { - return nil + if !(isUnknownContainer(err) || isStoppedContainer(err)) { + return err } + } - return err + // Try removing networks referenced in this task in case this + // task is the last one referencing it + if err := r.adapter.removeNetworks(ctx); err != nil { + if !isUnknownContainer(err) { + return err + } } return nil @@ -419,15 +426,6 @@ func (r *controller) Remove(ctx context.Context) error { log.G(ctx).WithError(err).Debug("shutdown failed on removal") } - // Try removing networks referenced in this task in case this - // task is the last one referencing it - if err := r.adapter.removeNetworks(ctx); err != nil { - if isUnknownContainer(err) { - return nil - } - return err - } - if err := r.adapter.remove(ctx); err != nil { if isUnknownContainer(err) { return nil @@ -511,7 +509,9 @@ func (r *controller) Logs(ctx context.Context, publisher exec.LogPublisher, opti var ( // use a rate limiter to keep things under control but also provides some // ability coalesce messages. - limiter = rate.NewLimiter(rate.Every(time.Second), 10<<20) // 10 MB/s + // this will implement a "token bucket" of size 10 MB, initially full and refilled + // at rate 10 MB tokens per second. + limiter = rate.NewLimiter(10<<20, 10<<20) // 10 MB/s msgctx = api.LogContext{ NodeID: r.task.NodeID, ServiceID: r.task.ServiceID, @@ -638,7 +638,7 @@ func parsePortMap(portMap nat.PortMap) ([]*api.PortConfig, error) { return nil, err } - protocol := api.ProtocolTCP + var protocol api.PortConfig_Protocol switch strings.ToLower(parts[1]) { case "tcp": protocol = api.ProtocolTCP diff --git a/daemon/cluster/executor/container/executor.go b/daemon/cluster/executor/container/executor.go index 940a943e4f791..66f21e8c7b768 100644 --- a/daemon/cluster/executor/container/executor.go +++ b/daemon/cluster/executor/container/executor.go @@ -15,12 +15,15 @@ import ( "github.com/docker/docker/daemon/cluster/convert" executorpkg "github.com/docker/docker/daemon/cluster/executor" clustertypes "github.com/docker/docker/daemon/cluster/provider" - networktypes "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork" + networktypes "github.com/docker/docker/libnetwork/types" "github.com/docker/swarmkit/agent" "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/api/naming" + "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/template" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -32,6 +35,14 @@ type executor struct { dependencies exec.DependencyManager mutex sync.Mutex // This mutex protects the following node field node *api.NodeDescription + + // nodeObj holds a copy of the swarmkit Node object from the time of the + // last call to executor.Configure. This allows us to discover which + // network attachments the node previously had, which further allows us to + // determine which, if any, need to be removed. nodeObj is not protected by + // a mutex, because it is only written to in the method (Configure) that it + // is read from. If that changes, it may need to be guarded. + nodeObj *api.Node } // NewExecutor returns an executor from the docker client. @@ -47,10 +58,7 @@ func NewExecutor(b executorpkg.Backend, p plugin.Backend, i executorpkg.ImageBac // Describe returns the underlying node description from the docker client. func (e *executor) Describe(ctx context.Context) (*api.NodeDescription, error) { - info, err := e.backend.SystemInfo() - if err != nil { - return nil, err - } + info := e.backend.SystemInfo() plugins := map[api.PluginDescription]struct{}{} addPlugins := func(typ string, names []string) { @@ -160,6 +168,40 @@ func (e *executor) Configure(ctx context.Context, node *api.Node) error { attachments[na.Network.ID] = na.Addresses[0] } + // discover which, if any, attachments have been removed. + // + // we aren't responsible directly for creating these networks. that is + // handled indirectly when a container using that network is created. + // however, when it comes time to remove the network, none of the relevant + // tasks may exist anymore. this means we should go ahead and try to remove + // any network we know to no longer be in use. + + // removeAttachments maps the network ID to a boolean. This boolean + // indicates whether the attachment in question is totally removed (true), + // or has just had its IP changed (false) + removeAttachments := make(map[string]bool) + + // the first time we Configure, nodeObj wil be nil, because it will not be + // set yet. in that case, skip this check. + if e.nodeObj != nil { + for _, na := range e.nodeObj.Attachments { + // same thing as above, check sanity of the attachments so we don't + // get a panic. + if na == nil || na.Network == nil || len(na.Addresses) == 0 { + logrus.WithField("NetworkAttachment", fmt.Sprintf("%#v", na)). + Warnf("skipping nil or malformed node network attachment entry") + continue + } + + // now, check if the attachment exists and shares the same IP address. + if ip, ok := attachments[na.Network.ID]; !ok || na.Addresses[0] != ip { + // if the map entry exists, then the network still exists, and the + // IP must be what has changed + removeAttachments[na.Network.ID] = !ok + } + } + } + if (ingressNA == nil) && (node.Attachment != nil) && (len(node.Attachment.Addresses) > 0) { ingressNA = node.Attachment attachments[ingressNA.Network.ID] = ingressNA.Addresses[0] @@ -200,6 +242,42 @@ func (e *executor) Configure(ctx context.Context, node *api.Node) error { return err } + var ( + activeEndpointsError *libnetwork.ActiveEndpointsError + errNoSuchNetwork libnetwork.ErrNoSuchNetwork + ) + + // now, finally, remove any network LB attachments that we no longer have. + for nw, gone := range removeAttachments { + err := e.backend.DeleteManagedNetwork(nw) + switch { + case err == nil: + continue + case errors.As(err, &activeEndpointsError): + // this is the purpose of the boolean in the map. it's literally + // just to log an appropriate, informative error. i'm unsure if + // this can ever actually occur, but we need to know if it does. + if gone { + log.G(ctx).Warnf("network %s should be removed, but still has active attachments", nw) + } else { + log.G(ctx).Warnf( + "network %s should have its node LB IP changed, but cannot be removed because of active attachments", + nw, + ) + } + continue + case errors.As(err, &errNoSuchNetwork): + // NoSuchNetworkError indicates the network is already gone. + continue + default: + log.G(ctx).Errorf("network %s remove failed: %v", nw, err) + } + } + + // now update our copy of the node object, reset the attachment store, and + // return + e.nodeObj = node + return e.backend.GetAttachmentStore().ResetAttachments(attachments) } @@ -229,8 +307,7 @@ func (e *executor) Controller(t *api.Task) (exec.Controller, error) { } switch runtimeKind { case string(swarmtypes.RuntimePlugin): - info, _ := e.backend.SystemInfo() - if !info.ExperimentalBuild { + if !e.backend.HasExperimental() { return ctlr, fmt.Errorf("runtime type %q only supported in experimental", swarmtypes.RuntimePlugin) } c, err := plugin.NewController(e.pluginBackend, t) diff --git a/daemon/cluster/executor/container/health_test.go b/daemon/cluster/executor/container/health_test.go index 03d6273635548..3c2aeb6d1714e 100644 --- a/daemon/cluster/executor/container/health_test.go +++ b/daemon/cluster/executor/container/health_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package container // import "github.com/docker/docker/daemon/cluster/executor/container" diff --git a/daemon/cluster/executor/container/validate_test.go b/daemon/cluster/executor/container/validate_test.go index 5e4694ff1bb8f..a7fa27099874f 100644 --- a/daemon/cluster/executor/container/validate_test.go +++ b/daemon/cluster/executor/container/validate_test.go @@ -1,7 +1,6 @@ package container // import "github.com/docker/docker/daemon/cluster/executor/container" import ( - "io/ioutil" "os" "strings" "testing" @@ -50,7 +49,7 @@ func TestControllerValidateMountBind(t *testing.T) { } // with proper source - tmpdir, err := ioutil.TempDir("", "TestControllerValidateMountBind") + tmpdir, err := os.MkdirTemp("", "TestControllerValidateMountBind") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } @@ -86,7 +85,7 @@ func TestControllerValidateMountVolume(t *testing.T) { } func TestControllerValidateMountTarget(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestControllerValidateMountTarget") + tmpdir, err := os.MkdirTemp("", "TestControllerValidateMountTarget") if err != nil { t.Fatalf("failed to create temp dir: %v", err) } diff --git a/daemon/cluster/executor/container/validate_unix_test.go b/daemon/cluster/executor/container/validate_unix_test.go index 7a3f053621310..bf57b9c7ffaf0 100644 --- a/daemon/cluster/executor/container/validate_unix_test.go +++ b/daemon/cluster/executor/container/validate_unix_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package container // import "github.com/docker/docker/daemon/cluster/executor/container" diff --git a/daemon/cluster/executor/container/validate_windows_test.go b/daemon/cluster/executor/container/validate_windows_test.go index e0d5bc2e60714..09f3ff4a1e7a2 100644 --- a/daemon/cluster/executor/container/validate_windows_test.go +++ b/daemon/cluster/executor/container/validate_windows_test.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package container // import "github.com/docker/docker/daemon/cluster/executor/container" diff --git a/daemon/cluster/listen_addr.go b/daemon/cluster/listen_addr.go index 44ea5fce42f31..ca49a93568d39 100644 --- a/daemon/cluster/listen_addr.go +++ b/daemon/cluster/listen_addr.go @@ -95,7 +95,7 @@ func validateDefaultAddrPool(defaultAddrPool []string, size uint32) error { // defaultAddrPool is not defined return nil } - //if size is not set, then we use default value 24 + // if size is not set, then we use default value 24 if size == 0 { size = 24 } @@ -123,6 +123,25 @@ func validateDefaultAddrPool(defaultAddrPool []string, size uint32) error { return nil } +// getDataPathPort validates vxlan udp port (data path port) number. +// if no port is set, the default (4789) is returned +// valid port numbers are between 1024 and 49151 +func getDataPathPort(portNum uint32) (uint32, error) { + // if the value comes as 0 by any reason we set it to default value 4789 + if portNum == 0 { + portNum = 4789 + return portNum, nil + } + // IANA procedures for each range in detail + // The Well Known Ports, aka the System Ports, from 0-1023 + // The Registered Ports, aka the User Ports, from 1024-49151 + // The Dynamic Ports, aka the Private Ports, from 49152-65535 + // So we can allow range between 1024 to 49151 + if portNum < 1024 || portNum > 49151 { + return 0, fmt.Errorf("Datapath port number is not in valid range (1024-49151) : %d", portNum) + } + return portNum, nil +} func resolveDataPathAddr(dataPathAddr string) (string, error) { if dataPathAddr == "" { // dataPathAddr is not defined diff --git a/daemon/cluster/listen_addr_others.go b/daemon/cluster/listen_addr_others.go index fe75848e57f79..de3d25381b05e 100644 --- a/daemon/cluster/listen_addr_others.go +++ b/daemon/cluster/listen_addr_others.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package cluster // import "github.com/docker/docker/daemon/cluster" diff --git a/daemon/cluster/noderunner.go b/daemon/cluster/noderunner.go index aa905d6780410..6bb2f47448349 100644 --- a/daemon/cluster/noderunner.go +++ b/daemon/cluster/noderunner.go @@ -4,14 +4,13 @@ import ( "context" "fmt" "path/filepath" - "runtime" "strings" "sync" "time" types "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/daemon/cluster/executor/container" - lncluster "github.com/docker/libnetwork/cluster" + lncluster "github.com/docker/docker/libnetwork/cluster" swarmapi "github.com/docker/swarmkit/api" swarmallocator "github.com/docker/swarmkit/manager/allocator/cnmallocator" swarmnode "github.com/docker/swarmkit/node" @@ -57,6 +56,8 @@ type nodeStartConfig struct { DefaultAddressPool []string // SubnetSize contains subnet size of DefaultAddressPool SubnetSize uint32 + // DataPathPort contains Data path port (VXLAN UDP port) number that is used for data traffic. + DataPathPort uint32 // JoinInProgress is set to true if a join operation has started, but // not completed yet. JoinInProgress bool @@ -102,7 +103,7 @@ func (n *nodeRunner) Start(conf nodeStartConfig) error { func (n *nodeRunner) start(conf nodeStartConfig) error { var control string - if runtime.GOOS == "windows" { + if isWindows { control = `\\.\pipe\` + controlSocket } else { control = filepath.Join(n.cluster.runtimeRoot, controlSocket) @@ -125,6 +126,7 @@ func (n *nodeRunner) start(conf nodeStartConfig) error { NetworkConfig: &swarmallocator.NetworkConfig{ DefaultAddrPool: conf.DefaultAddressPool, SubnetSize: conf.SubnetSize, + VXLANUDPPort: conf.DataPathPort, }, JoinAddr: joinAddr, StateDir: n.cluster.root, @@ -295,6 +297,11 @@ func (n *nodeRunner) Stop() error { n.cancelReconnect = nil } if n.swarmNode == nil { + // even though the swarm node is nil we still may need + // to send a node leave event to perform any cleanup required. + if n.cluster != nil { + n.cluster.SendClusterEvent(lncluster.EventNodeLeave) + } n.mu.Unlock() return nil } @@ -320,7 +327,7 @@ func (n *nodeRunner) State() nodeState { ns := n.nodeState if ns.err != nil || n.cancelReconnect != nil { - if errors.Cause(ns.err) == errSwarmLocked { + if errors.Is(ns.err, errSwarmLocked) { ns.status = types.LocalNodeStateLocked } else { ns.status = types.LocalNodeStateError diff --git a/daemon/cluster/nodes.go b/daemon/cluster/nodes.go index 3c073b0bacc86..f0d70fe1ff6c6 100644 --- a/daemon/cluster/nodes.go +++ b/daemon/cluster/nodes.go @@ -8,6 +8,7 @@ import ( "github.com/docker/docker/daemon/cluster/convert" "github.com/docker/docker/errdefs" swarmapi "github.com/docker/swarmkit/api" + "google.golang.org/grpc" ) // GetNodes returns a list of all nodes known to a cluster. @@ -30,7 +31,9 @@ func (c *Cluster) GetNodes(options apitypes.NodeListOptions) ([]types.Node, erro r, err := state.controlClient.ListNodes( ctx, - &swarmapi.ListNodesRequest{Filters: filters}) + &swarmapi.ListNodesRequest{Filters: filters}, + grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse), + ) if err != nil { return nil, err } @@ -63,7 +66,7 @@ func (c *Cluster) GetNode(input string) (types.Node, error) { // UpdateNode updates existing nodes properties. func (c *Cluster) UpdateNode(input string, version uint64, spec types.NodeSpec) error { - return c.lockedManagerAction(func(ctx context.Context, state nodeState) error { + return c.lockedManagerAction(func(_ context.Context, state nodeState) error { nodeSpec, err := convert.NodeSpecToGRPC(spec) if err != nil { return errdefs.InvalidParameter(err) diff --git a/daemon/cluster/secrets.go b/daemon/cluster/secrets.go index c6fd842081678..6f652eb54d6b0 100644 --- a/daemon/cluster/secrets.go +++ b/daemon/cluster/secrets.go @@ -7,6 +7,7 @@ import ( types "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/daemon/cluster/convert" swarmapi "github.com/docker/swarmkit/api" + "google.golang.org/grpc" ) // GetSecret returns a secret from a managed swarm cluster @@ -44,7 +45,9 @@ func (c *Cluster) GetSecrets(options apitypes.SecretListOptions) ([]types.Secret defer cancel() r, err := state.controlClient.ListSecrets(ctx, - &swarmapi.ListSecretsRequest{Filters: filters}) + &swarmapi.ListSecretsRequest{Filters: filters}, + grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse), + ) if err != nil { return nil, err } diff --git a/daemon/cluster/services.go b/daemon/cluster/services.go index c14037645c915..5b7536b0e2265 100644 --- a/daemon/cluster/services.go +++ b/daemon/cluster/services.go @@ -23,6 +23,7 @@ import ( gogotypes "github.com/gogo/protobuf/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "google.golang.org/grpc" ) // GetServices returns all services of a managed swarm cluster. @@ -67,13 +68,21 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv r, err := state.controlClient.ListServices( ctx, - &swarmapi.ListServicesRequest{Filters: filters}) + &swarmapi.ListServicesRequest{Filters: filters}, + grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse), + ) if err != nil { return nil, err } services := make([]types.Service, 0, len(r.Services)) + // if the user requests the service statuses, we'll store the IDs needed + // in this slice + var serviceIDs []string + if options.Status { + serviceIDs = make([]string, 0, len(r.Services)) + } for _, service := range r.Services { if options.Filters.Contains("mode") { var mode string @@ -82,12 +91,19 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv mode = "global" case *swarmapi.ServiceSpec_Replicated: mode = "replicated" + case *swarmapi.ServiceSpec_ReplicatedJob: + mode = "replicated-job" + case *swarmapi.ServiceSpec_GlobalJob: + mode = "global-job" } if !options.Filters.ExactMatch("mode", mode) { continue } } + if options.Status { + serviceIDs = append(serviceIDs, service.ID) + } svcs, err := convert.ServiceFromGRPC(*service) if err != nil { return nil, err @@ -95,6 +111,50 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv services = append(services, svcs) } + if options.Status { + // Listing service statuses is a separate call because, while it is the + // most common UI operation, it is still just a UI operation, and it + // would be improper to include this data in swarm's Service object. + // We pay the cost with some complexity here, but this is still way + // more efficient than marshalling and unmarshalling all the JSON + // needed to list tasks and get this data otherwise client-side + resp, err := state.controlClient.ListServiceStatuses( + ctx, + &swarmapi.ListServiceStatusesRequest{Services: serviceIDs}, + grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse), + ) + if err != nil { + return nil, err + } + + // we'll need to match up statuses in the response with the services in + // the list operation. if we did this by operating on two lists, the + // result would be quadratic. instead, make a mapping of service IDs to + // service statuses so that this is roughly linear. additionally, + // convert the status response to an engine api service status here. + serviceMap := map[string]*types.ServiceStatus{} + for _, status := range resp.Statuses { + serviceMap[status.ServiceID] = &types.ServiceStatus{ + RunningTasks: status.RunningTasks, + DesiredTasks: status.DesiredTasks, + CompletedTasks: status.CompletedTasks, + } + } + + // because this is a list of values and not pointers, make sure we + // actually alter the value when iterating. + for i, service := range services { + // the return value of the ListServiceStatuses operation is + // guaranteed to contain a value in the response for every argument + // in the request, so we can safely do this assignment. and even if + // it wasn't, and the service ID was for some reason absent from + // this map, the resulting value of service.Status would just be + // nil -- the same thing it was before + service.ServiceStatus = serviceMap[service.ID] + services[i] = service + } + } + return services, nil } @@ -141,8 +201,7 @@ func (c *Cluster) CreateService(s types.ServiceSpec, encodedAuth string, queryRe case *swarmapi.TaskSpec_Generic: switch serviceSpec.Task.GetGeneric().Kind { case string(types.RuntimePlugin): - info, _ := c.config.Backend.SystemInfo() - if !info.ExperimentalBuild { + if !c.config.Backend.HasExperimental() { return fmt.Errorf("runtime type %q only supported in experimental", types.RuntimePlugin) } if s.TaskTemplate.PluginSpec == nil { @@ -574,7 +633,7 @@ func (c *Cluster) imageWithDigestString(ctx context.Context, image string, authC return "", errors.Errorf("image reference not tagged: %s", image) } - repo, _, err := c.config.ImageBackend.GetRepository(ctx, taggedRef, authConfig) + repo, err := c.config.ImageBackend.GetRepository(ctx, taggedRef, authConfig) if err != nil { return "", err } diff --git a/daemon/cluster/swarm.go b/daemon/cluster/swarm.go index 65dfe9eb45a05..e7bb7cc21d572 100644 --- a/daemon/cluster/swarm.go +++ b/daemon/cluster/swarm.go @@ -13,12 +13,13 @@ import ( "github.com/docker/docker/daemon/cluster/convert" "github.com/docker/docker/errdefs" "github.com/docker/docker/opts" - "github.com/docker/docker/pkg/signal" + "github.com/docker/docker/pkg/stack" swarmapi "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/manager/encryption" swarmnode "github.com/docker/swarmkit/node" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "google.golang.org/grpc" ) // Init initializes new cluster from user provided request. @@ -32,6 +33,7 @@ func (c *Cluster) Init(req types.InitRequest) (string, error) { // API handlers to finish before shutting down the node. c.mu.Lock() if !c.nr.nodeState.IsManager() { + c.mu.Unlock() return "", errSwarmNotManager } c.mu.Unlock() @@ -92,10 +94,15 @@ func (c *Cluster) Init(req types.InitRequest) (string, error) { } } - //Validate Default Address Pool input if err := validateDefaultAddrPool(req.DefaultAddrPool, req.SubnetSize); err != nil { return "", err } + + port, err := getDataPathPort(req.DataPathPort) + if err != nil { + return "", err + } + nr, err := c.newNodeRunner(nodeStartConfig{ forceNewCluster: req.ForceNewCluster, autolock: req.AutoLockManagers, @@ -106,6 +113,7 @@ func (c *Cluster) Init(req types.InitRequest) (string, error) { DefaultAddressPool: req.DefaultAddrPool, SubnetSize: req.SubnetSize, availability: req.Availability, + DataPathPort: port, }) if err != nil { return "", err @@ -187,8 +195,11 @@ func (c *Cluster) Join(req types.JoinRequest) error { c.nr = nr c.mu.Unlock() + timeout := time.NewTimer(swarmConnectTimeout) + defer timeout.Stop() + select { - case <-time.After(swarmConnectTimeout): + case <-timeout.C: return errSwarmJoinTimeoutReached case err := <-nr.Ready(): if err != nil { @@ -336,7 +347,7 @@ func (c *Cluster) UnlockSwarm(req types.UnlockRequest) error { c.mu.Unlock() if err := <-nr.Ready(); err != nil { - if errors.Cause(err) == errSwarmLocked { + if errors.Is(err, errSwarmLocked) { return invalidUnlockKey{} } return errors.Errorf("swarm component could not be started: %v", err) @@ -360,7 +371,7 @@ func (c *Cluster) Leave(force bool) error { c.mu.Unlock() - if errors.Cause(state.err) == errSwarmLocked && !force { + if errors.Is(state.err, errSwarmLocked) && !force { // leave a locked swarm without --force is not allowed return errors.WithStack(notAvailableError("Swarm is encrypted and locked. Please unlock it first or use `--force` to ignore this message.")) } @@ -388,7 +399,7 @@ func (c *Cluster) Leave(force bool) error { // release readers in here if err := nr.Stop(); err != nil { logrus.Errorf("failed to shut down cluster node: %v", err) - signal.DumpStacks("") + stack.Dump() return err } @@ -442,7 +453,10 @@ func (c *Cluster) Info() types.Info { info.Cluster = &swarm.ClusterInfo - if r, err := state.controlClient.ListNodes(ctx, &swarmapi.ListNodesRequest{}); err != nil { + if r, err := state.controlClient.ListNodes( + ctx, &swarmapi.ListNodesRequest{}, + grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse), + ); err != nil { info.Error = err.Error() } else { info.Nodes = len(r.Nodes) @@ -452,6 +466,20 @@ func (c *Cluster) Info() types.Info { } } } + + switch info.LocalNodeState { + case types.LocalNodeStateInactive, types.LocalNodeStateLocked, types.LocalNodeStateError: + // nothing to do + default: + if info.Managers == 2 { + const warn string = `WARNING: Running Swarm in a two-manager configuration. This configuration provides + no fault tolerance, and poses a high risk to lose control over the cluster. + Refer to https://docs.docker.com/engine/swarm/admin_guide/ to configure the + Swarm for fault-tolerance.` + + info.Warnings = append(info.Warnings, warn) + } + } } if state.swarmNode != nil { diff --git a/daemon/cluster/tasks.go b/daemon/cluster/tasks.go index de1240dfe89e1..f1001ea0bac3c 100644 --- a/daemon/cluster/tasks.go +++ b/daemon/cluster/tasks.go @@ -8,6 +8,7 @@ import ( types "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/daemon/cluster/convert" swarmapi "github.com/docker/swarmkit/api" + "google.golang.org/grpc" ) // GetTasks returns a list of tasks matching the filter options. @@ -53,7 +54,9 @@ func (c *Cluster) GetTasks(options apitypes.TaskListOptions) ([]types.Task, erro r, err = state.controlClient.ListTasks( ctx, - &swarmapi.ListTasksRequest{Filters: filters}) + &swarmapi.ListTasksRequest{Filters: filters}, + grpc.MaxCallRecvMsgSize(defaultRecvSizeForListResponse), + ) return err }); err != nil { return nil, err diff --git a/daemon/cluster/utils.go b/daemon/cluster/utils.go index d55e0012b7558..e1493de413e29 100644 --- a/daemon/cluster/utils.go +++ b/daemon/cluster/utils.go @@ -2,7 +2,6 @@ package cluster // import "github.com/docker/docker/daemon/cluster" import ( "encoding/json" - "io/ioutil" "os" "path/filepath" @@ -10,7 +9,7 @@ import ( ) func loadPersistentState(root string) (*nodeStartConfig, error) { - dt, err := ioutil.ReadFile(filepath.Join(root, stateFile)) + dt, err := os.ReadFile(filepath.Join(root, stateFile)) if err != nil { return nil, err } @@ -40,7 +39,7 @@ func clearPersistentState(root string) error { // todo: backup this data instead of removing? // rather than delete the entire swarm directory, delete the contents in order to preserve the inode // (for example, allowing it to be bind-mounted) - files, err := ioutil.ReadDir(root) + files, err := os.ReadDir(root) if err != nil { return err } diff --git a/daemon/commit.go b/daemon/commit.go index 0f6f440514b9f..302e9a95d6fe5 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -40,7 +40,7 @@ func merge(userConf, imageConf *containertypes.Config) error { imageEnvKey := strings.Split(imageEnv, "=")[0] for _, userEnv := range userConf.Env { userEnvKey := strings.Split(userEnv, "=")[0] - if runtime.GOOS == "windows" { + if isWindows { // Case insensitive environment variables on Windows imageEnvKey = strings.ToUpper(imageEnvKey) userEnvKey = strings.ToUpper(userEnvKey) @@ -68,7 +68,6 @@ func merge(userConf, imageConf *containertypes.Config) error { if len(userConf.Entrypoint) == 0 { if len(userConf.Cmd) == 0 { userConf.Cmd = imageConf.Cmd - userConf.ArgsEscaped = imageConf.ArgsEscaped } if userConf.Entrypoint == nil { @@ -125,7 +124,7 @@ func (daemon *Daemon) CreateImageFromContainer(name string, c *backend.CreateIma } // It is not possible to commit a running container on Windows - if (runtime.GOOS == "windows") && container.IsRunning() { + if isWindows && container.IsRunning() { return "", errors.Errorf("%+v does not support commit of a running container", runtime.GOOS) } diff --git a/daemon/config/builder.go b/daemon/config/builder.go index ac85e76b303d7..07eb5ced20bc8 100644 --- a/daemon/config/builder.go +++ b/daemon/config/builder.go @@ -1,12 +1,57 @@ package config -import "github.com/docker/docker/api/types/filters" +import ( + "encoding/json" + "fmt" + "sort" + "strings" + + "github.com/docker/docker/api/types/filters" +) // BuilderGCRule represents a GC rule for buildkit cache type BuilderGCRule struct { - All bool `json:",omitempty"` - Filter filters.Args `json:",omitempty"` - KeepStorage string `json:",omitempty"` + All bool `json:",omitempty"` + Filter BuilderGCFilter `json:",omitempty"` + KeepStorage string `json:",omitempty"` +} + +// BuilderGCFilter contains garbage-collection filter rules for a BuildKit builder +type BuilderGCFilter filters.Args + +// MarshalJSON returns a JSON byte representation of the BuilderGCFilter +func (x *BuilderGCFilter) MarshalJSON() ([]byte, error) { + f := filters.Args(*x) + keys := f.Keys() + sort.Strings(keys) + arr := make([]string, 0, len(keys)) + for _, k := range keys { + values := f.Get(k) + for _, v := range values { + arr = append(arr, fmt.Sprintf("%s=%s", k, v)) + } + } + return json.Marshal(arr) +} + +// UnmarshalJSON fills the BuilderGCFilter values structure from JSON input +func (x *BuilderGCFilter) UnmarshalJSON(data []byte) error { + var arr []string + f := filters.NewArgs() + if err := json.Unmarshal(data, &arr); err != nil { + // backwards compat for deprecated buggy form + err := json.Unmarshal(data, &f) + *x = BuilderGCFilter(f) + return err + } + for _, s := range arr { + fields := strings.SplitN(s, "=", 2) + name := strings.ToLower(strings.TrimSpace(fields[0])) + value := strings.TrimSpace(fields[1]) + f.Add(name, value) + } + *x = BuilderGCFilter(f) + return nil } // BuilderGCConfig contains GC config for a buildkit builder @@ -16,7 +61,14 @@ type BuilderGCConfig struct { DefaultKeepStorage string `json:",omitempty"` } +// BuilderEntitlements contains settings to enable/disable entitlements +type BuilderEntitlements struct { + NetworkHost *bool `json:"network-host,omitempty"` + SecurityInsecure *bool `json:"security-insecure,omitempty"` +} + // BuilderConfig contains config for the builder type BuilderConfig struct { - GC BuilderGCConfig `json:",omitempty"` + GC BuilderGCConfig `json:",omitempty"` + Entitlements BuilderEntitlements `json:",omitempty"` } diff --git a/daemon/config/builder_test.go b/daemon/config/builder_test.go new file mode 100644 index 0000000000000..6b4576446b493 --- /dev/null +++ b/daemon/config/builder_test.go @@ -0,0 +1,44 @@ +package config + +import ( + "testing" + + "github.com/docker/docker/api/types/filters" + "github.com/google/go-cmp/cmp" + "gotest.tools/v3/assert" + "gotest.tools/v3/fs" +) + +func TestBuilderGC(t *testing.T) { + tempFile := fs.NewFile(t, "config", fs.WithContent(`{ + "builder": { + "gc": { + "enabled": true, + "policy": [ + {"keepStorage": "10GB", "filter": ["unused-for=2200h"]}, + {"keepStorage": "50GB", "filter": {"unused-for": {"3300h": true}}}, + {"keepStorage": "100GB", "all": true} + ] + } + } +}`)) + defer tempFile.Remove() + configFile := tempFile.Path() + + cfg, err := MergeDaemonConfigurations(&Config{}, nil, configFile) + assert.NilError(t, err) + assert.Assert(t, cfg.Builder.GC.Enabled) + f1 := filters.NewArgs() + f1.Add("unused-for", "2200h") + f2 := filters.NewArgs() + f2.Add("unused-for", "3300h") + expectedPolicy := []BuilderGCRule{ + {KeepStorage: "10GB", Filter: BuilderGCFilter(f1)}, + {KeepStorage: "50GB", Filter: BuilderGCFilter(f2)}, /* parsed from deprecated form */ + {KeepStorage: "100GB", All: true}, + } + assert.DeepEqual(t, cfg.Builder.GC.Policy, expectedPolicy, cmp.AllowUnexported(BuilderGCFilter{})) + // double check to please the skeptics + assert.Assert(t, filters.Args(cfg.Builder.GC.Policy[0].Filter).UniqueExactMatch("unused-for", "2200h")) + assert.Assert(t, filters.Args(cfg.Builder.GC.Policy[1].Filter).UniqueExactMatch("unused-for", "3300h")) +} diff --git a/daemon/config/config.go b/daemon/config/config.go index 8b2c844a579f0..fa534f3249b9d 100644 --- a/daemon/config/config.go +++ b/daemon/config/config.go @@ -3,13 +3,11 @@ package config // import "github.com/docker/docker/daemon/config" import ( "bytes" "encoding/json" - "errors" "fmt" - "io" - "io/ioutil" + "net" + "net/url" "os" "reflect" - "runtime" "strings" "sync" @@ -19,6 +17,7 @@ import ( "github.com/docker/docker/pkg/discovery" "github.com/docker/docker/registry" "github.com/imdario/mergo" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/pflag" ) @@ -32,9 +31,10 @@ const ( // maximum number of uploads that // may take place at a time for each push. DefaultMaxConcurrentUploads = 5 - // StockRuntimeName is the reserved name/alias used to represent the - // OCI runtime being shipped with the docker daemon package. - StockRuntimeName = "runc" + // DefaultDownloadAttempts is the default value for + // maximum number of attempts that + // may take place at a time for each pull when the connection is lost. + DefaultDownloadAttempts = 5 // DefaultShmSize is the default value for container's shm size DefaultShmSize = int64(67108864) // DefaultNetworkMtu is the default value for network MTU @@ -43,8 +43,33 @@ const ( DisableNetworkBridge = "none" // DefaultInitBinary is the name of the default init binary DefaultInitBinary = "docker-init" + // DefaultShimBinary is the default shim to be used by containerd if none + // is specified + DefaultShimBinary = "containerd-shim" + // DefaultRuntimeBinary is the default runtime to be used by + // containerd if none is specified + DefaultRuntimeBinary = "runc" + + // LinuxV1RuntimeName is the runtime used to specify the containerd v1 shim with the runc binary + // Note this is different than io.containerd.runc.v1 which would be the v1 shim using the v2 shim API. + // This is specifically for the v1 shim using the v1 shim API. + LinuxV1RuntimeName = "io.containerd.runtime.v1.linux" + // LinuxV2RuntimeName is the runtime used to specify the containerd v2 runc shim + LinuxV2RuntimeName = "io.containerd.runc.v2" + + // SeccompProfileDefault is the built-in default seccomp profile. + SeccompProfileDefault = "builtin" + // SeccompProfileUnconfined is a special profile name for seccomp to use an + // "unconfined" seccomp profile. + SeccompProfileUnconfined = "unconfined" ) +var builtinRuntimes = map[string]bool{ + StockRuntimeName: true, + LinuxV1RuntimeName: true, + LinuxV2RuntimeName: true, +} + // flatOptions contains configuration keys // that MUST NOT be parsed as deep structures. // Use this to differentiate these options @@ -64,6 +89,8 @@ var flatOptions = map[string]bool{ var skipValidateOptions = map[string]bool{ "features": true, "builder": true, + // Corresponding flag has been removed because it was already unusable + "deprecated-key-path": true, } // skipDuplicates contains configuration keys that @@ -108,6 +135,14 @@ type CommonTLSOptions struct { KeyFile string `json:"tlskey,omitempty"` } +// DNSConfig defines the DNS configurations. +type DNSConfig struct { + DNS []string `json:"dns,omitempty"` + DNSOptions []string `json:"dns-opts,omitempty"` + DNSSearch []string `json:"dns-search,omitempty"` + HostGatewayIP net.IP `json:"host-gateway-ip,omitempty"` +} + // CommonConfig defines the configuration of a docker daemon which is // common across platforms. // It includes json tags to deserialize configuration from a file @@ -118,9 +153,6 @@ type CommonConfig struct { AutoRestart bool `json:"-"` Context map[string][]string `json:"-"` DisableBridge bool `json:"-"` - DNS []string `json:"dns,omitempty"` - DNSOptions []string `json:"dns-opts,omitempty"` - DNSSearch []string `json:"dns-search,omitempty"` ExecOptions []string `json:"exec-opts,omitempty"` GraphDriver string `json:"storage-driver,omitempty"` GraphOptions []string `json:"storage-opts,omitempty"` @@ -134,6 +166,7 @@ type CommonConfig struct { ExecRoot string `json:"exec-root,omitempty"` SocketGroup string `json:"group,omitempty"` CorsHeaders string `json:"api-cors-header,omitempty"` + ProxyConfig // TrustKeyPath is used to generate the daemon ID and for signing schema 1 manifests // when pushing to a registry which does not support schema 2. This field is marked as @@ -148,15 +181,18 @@ type CommonConfig struct { // ClusterStore is the storage backend used for the cluster information. It is used by both // multihost networking (to store networks and endpoints information) and by the node discovery // mechanism. + // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated ClusterStore string `json:"cluster-store,omitempty"` // ClusterOpts is used to pass options to the discovery package for tuning libkv settings, such // as TLS configuration settings. + // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated ClusterOpts map[string]string `json:"cluster-store-opts,omitempty"` // ClusterAdvertise is the network endpoint that the Engine advertises for the purpose of node // discovery. This should be a 'host:port' combination on which that daemon instance is // reachable by other hosts. + // Deprecated: host-discovery and overlay networks with external k/v stores are deprecated ClusterAdvertise string `json:"cluster-advertise,omitempty"` // MaxConcurrentDownloads is the maximum number of downloads that @@ -167,6 +203,10 @@ type CommonConfig struct { // may take place at a time for each push. MaxConcurrentUploads *int `json:"max-concurrent-uploads,omitempty"` + // MaxDownloadAttempts is the maximum number of attempts that + // may take place at a time for each push. + MaxDownloadAttempts *int `json:"max-download-attempts,omitempty"` + // ShutdownTimeout is the timeout value (in seconds) the daemon will wait for the container // to stop when daemon is being shutdown ShutdownTimeout int `json:"shutdown-timeout,omitempty"` @@ -174,8 +214,8 @@ type CommonConfig struct { Debug bool `json:"debug,omitempty"` Hosts []string `json:"hosts,omitempty"` LogLevel string `json:"log-level,omitempty"` - TLS bool `json:"tls,omitempty"` - TLSVerify bool `json:"tlsverify,omitempty"` + TLS *bool `json:"tls,omitempty"` + TLSVerify *bool `json:"tlsverify,omitempty"` // Embedded structs that allow config // deserialization without the full struct. @@ -199,6 +239,7 @@ type CommonConfig struct { MetricsAddress string `json:"metrics-addr"` + DNSConfig LogConfig BridgeConfig // bridgeConfig holds bridge network specific configuration. NetworkConfig @@ -229,6 +270,18 @@ type CommonConfig struct { Features map[string]bool `json:"features,omitempty"` Builder BuilderConfig `json:"builder,omitempty"` + + ContainerdNamespace string `json:"containerd-namespace,omitempty"` + ContainerdPluginNamespace string `json:"containerd-plugin-namespace,omitempty"` + + DefaultRuntime string `json:"default-runtime,omitempty"` +} + +// ProxyConfig holds the proxy-configuration for the daemon. +type ProxyConfig struct { + HTTPProxy string `json:"http-proxy,omitempty"` + HTTPSProxy string `json:"https-proxy,omitempty"` + NoProxy string `json:"no-proxy,omitempty"` } // IsValueSet returns true if a configuration value @@ -243,14 +296,14 @@ func (conf *Config) IsValueSet(name string) bool { // New returns a new fully initialized Config struct func New() *Config { - config := Config{} - config.LogConfig.Config = make(map[string]string) - config.ClusterOpts = make(map[string]string) - - if runtime.GOOS != "linux" { - config.V2Only = true + return &Config{ + CommonConfig: CommonConfig{ + LogConfig: LogConfig{ + Config: make(map[string]string), + }, + ClusterOpts: make(map[string]string), + }, } - return &config } // ParseClusterAdvertiseSettings parses the specified advertise settings @@ -264,7 +317,7 @@ func ParseClusterAdvertiseSettings(clusterStore, clusterAdvertise string) (strin advertise, err := discovery.ParseAdvertise(clusterAdvertise) if err != nil { - return "", fmt.Errorf("discovery advertise parsing failed (%v)", err) + return "", errors.Wrap(err, "discovery advertise parsing failed") } return advertise, nil } @@ -293,38 +346,19 @@ func GetConflictFreeLabels(labels []string) ([]string, error) { return newLabels, nil } -// ValidateReservedNamespaceLabels errors if the reserved namespaces com.docker.*, -// io.docker.*, org.dockerproject.* are used in a configured engine label. -// -// TODO: This is a separate function because we need to warn users first of the -// deprecation. When we return an error, this logic can be added to Validate -// or GetConflictFreeLabels instead of being here. -func ValidateReservedNamespaceLabels(labels []string) error { - for _, label := range labels { - lowered := strings.ToLower(label) - if strings.HasPrefix(lowered, "com.docker.") || strings.HasPrefix(lowered, "io.docker.") || - strings.HasPrefix(lowered, "org.dockerproject.") { - return fmt.Errorf( - "label %s not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for Docker's internal use", - label) - } - } - return nil -} - // Reload reads the configuration in the host and reloads the daemon and server. func Reload(configFile string, flags *pflag.FlagSet, reload func(*Config)) error { logrus.Infof("Got signal to reload configuration, reloading from: %s", configFile) newConfig, err := getConflictFreeConfiguration(configFile, flags) if err != nil { if flags.Changed("config-file") || !os.IsNotExist(err) { - return fmt.Errorf("unable to configure the Docker daemon with file %s: %v", configFile, err) + return errors.Wrapf(err, "unable to configure the Docker daemon with file %s", configFile) } newConfig = New() } if err := Validate(newConfig); err != nil { - return fmt.Errorf("file configuration validation failed (%v)", err) + return errors.Wrap(err, "file configuration validation failed") } // Check if duplicate label-keys with different values are found @@ -355,7 +389,7 @@ func MergeDaemonConfigurations(flagsConfig *Config, flags *pflag.FlagSet, config } if err := Validate(fileConfig); err != nil { - return nil, fmt.Errorf("configuration validation from file failed (%v)", err) + return nil, errors.Wrap(err, "configuration validation from file failed") } // merge flags configuration on top of the file configuration @@ -366,7 +400,7 @@ func MergeDaemonConfigurations(flagsConfig *Config, flags *pflag.FlagSet, config // We need to validate again once both fileConfig and flagsConfig // have been merged if err := Validate(fileConfig); err != nil { - return nil, fmt.Errorf("merged configuration validation from file and command line flags failed (%v)", err) + return nil, errors.Wrap(err, "merged configuration validation from file and command line flags failed") } return fileConfig, nil @@ -376,17 +410,22 @@ func MergeDaemonConfigurations(flagsConfig *Config, flags *pflag.FlagSet, config // It compares that configuration with the one provided by the flags, // and returns an error if there are conflicts. func getConflictFreeConfiguration(configFile string, flags *pflag.FlagSet) (*Config, error) { - b, err := ioutil.ReadFile(configFile) + b, err := os.ReadFile(configFile) if err != nil { return nil, err } var config Config - var reader io.Reader + + b = bytes.TrimSpace(b) + if len(b) == 0 { + // empty config file + return &config, nil + } + if flags != nil { var jsonConfig map[string]interface{} - reader = bytes.NewReader(b) - if err := json.NewDecoder(reader).Decode(&jsonConfig); err != nil { + if err := json.Unmarshal(b, &jsonConfig); err != nil { return nil, err } @@ -429,8 +468,7 @@ func getConflictFreeConfiguration(configFile string, flags *pflag.FlagSet) (*Con config.ValuesSet = configSet } - reader = bytes.NewReader(b) - if err := json.NewDecoder(reader).Decode(&config); err != nil { + if err := json.Unmarshal(b, &config); err != nil { return nil, err } @@ -438,7 +476,7 @@ func getConflictFreeConfiguration(configFile string, flags *pflag.FlagSet) (*Con logrus.Warn(`The "graph" config file option is deprecated. Please use "data-root" instead.`) if config.Root != "" { - return nil, fmt.Errorf(`cannot specify both "graph" and "data-root" config file options`) + return nil, errors.New(`cannot specify both "graph" and "data-root" config file options`) } config.Root = config.RootDeprecated @@ -480,9 +518,7 @@ func findConfigurationConflicts(config map[string]interface{}, flags *pflag.Flag if len(unknownKeys) > 0 { unknownNamedConflicts := func(f *pflag.Flag) { if namedOption, ok := f.Value.(opts.NamedOption); ok { - if _, valid := unknownKeys[namedOption.Name()]; valid { - delete(unknownKeys, namedOption.Name()) - } + delete(unknownKeys, namedOption.Name()) } } flags.VisitAll(unknownNamedConflicts) @@ -498,6 +534,11 @@ func findConfigurationConflicts(config map[string]interface{}, flags *pflag.Flag var conflicts []string printConflict := func(name string, flagValue, fileValue interface{}) string { + switch name { + case "http-proxy", "https-proxy": + flagValue = MaskCredentials(flagValue.(string)) + fileValue = MaskCredentials(fileValue.(string)) + } return fmt.Sprintf("%s: (from flag: %v, from file: %v)", name, flagValue, fileValue) } @@ -529,7 +570,7 @@ func findConfigurationConflicts(config map[string]interface{}, flags *pflag.Flag // Validate validates some specific configs. // such as config.DNS, config.Labels, config.DNSSearch, -// as well as config.MaxConcurrentDownloads, config.MaxConcurrentUploads. +// as well as config.MaxConcurrentDownloads, config.MaxConcurrentUploads and config.MaxDownloadAttempts. func Validate(config *Config) error { // validate DNS for _, dns := range config.DNS { @@ -559,6 +600,9 @@ func Validate(config *Config) error { if config.MaxConcurrentUploads != nil && *config.MaxConcurrentUploads < 0 { return fmt.Errorf("invalid max concurrent uploads: %d", *config.MaxConcurrentUploads) } + if err := ValidateMaxDownloadAttempts(config); err != nil { + return err + } // validate that "default" runtime is not reset if runtimes := config.GetAllRuntimes(); len(runtimes) > 0 { @@ -571,10 +615,12 @@ func Validate(config *Config) error { return err } - if defaultRuntime := config.GetDefaultRuntimeName(); defaultRuntime != "" && defaultRuntime != StockRuntimeName { - runtimes := config.GetAllRuntimes() - if _, ok := runtimes[defaultRuntime]; !ok { - return fmt.Errorf("specified default runtime '%s' does not exist", defaultRuntime) + if defaultRuntime := config.GetDefaultRuntimeName(); defaultRuntime != "" { + if !builtinRuntimes[defaultRuntime] { + runtimes := config.GetAllRuntimes() + if _, ok := runtimes[defaultRuntime]; !ok { + return fmt.Errorf("specified default runtime '%s' does not exist", defaultRuntime) + } } } @@ -582,6 +628,14 @@ func Validate(config *Config) error { return config.ValidatePlatformConfig() } +// ValidateMaxDownloadAttempts validates if the max-download-attempts is within the valid range +func ValidateMaxDownloadAttempts(config *Config) error { + if config.MaxDownloadAttempts != nil && *config.MaxDownloadAttempts <= 0 { + return fmt.Errorf("invalid max download attempts: %d", *config.MaxDownloadAttempts) + } + return nil +} + // ModifiedDiscoverySettings returns whether the discovery configuration has been modified or not. func ModifiedDiscoverySettings(config *Config, backendType, advertise string, clusterOpts map[string]string) bool { if config.ClusterStore != backendType || config.ClusterAdvertise != advertise { @@ -596,3 +650,22 @@ func ModifiedDiscoverySettings(config *Config, backendType, advertise string, cl return !reflect.DeepEqual(config.ClusterOpts, clusterOpts) } + +// GetDefaultRuntimeName returns the current default runtime +func (conf *Config) GetDefaultRuntimeName() string { + conf.Lock() + rt := conf.DefaultRuntime + conf.Unlock() + + return rt +} + +// MaskCredentials masks credentials that are in an URL. +func MaskCredentials(rawURL string) string { + parsedURL, err := url.Parse(rawURL) + if err != nil || parsedURL.User == nil { + return rawURL + } + parsedURL.User = url.UserPassword("xxxxx", "xxxxx") + return parsedURL.String() +} diff --git a/daemon/config/config_common_unix.go b/daemon/config/config_common_unix.go deleted file mode 100644 index 0a862d3b50a9b..0000000000000 --- a/daemon/config/config_common_unix.go +++ /dev/null @@ -1,77 +0,0 @@ -// +build linux freebsd - -package config // import "github.com/docker/docker/daemon/config" - -import ( - "net" - - "github.com/docker/docker/api/types" -) - -// CommonUnixConfig defines configuration of a docker daemon that is -// common across Unix platforms. -type CommonUnixConfig struct { - Runtimes map[string]types.Runtime `json:"runtimes,omitempty"` - DefaultRuntime string `json:"default-runtime,omitempty"` - DefaultInitBinary string `json:"default-init,omitempty"` -} - -type commonUnixBridgeConfig struct { - DefaultIP net.IP `json:"ip,omitempty"` - IP string `json:"bip,omitempty"` - DefaultGatewayIPv4 net.IP `json:"default-gateway,omitempty"` - DefaultGatewayIPv6 net.IP `json:"default-gateway-v6,omitempty"` - InterContainerCommunication bool `json:"icc,omitempty"` -} - -// GetRuntime returns the runtime path and arguments for a given -// runtime name -func (conf *Config) GetRuntime(name string) *types.Runtime { - conf.Lock() - defer conf.Unlock() - if rt, ok := conf.Runtimes[name]; ok { - return &rt - } - return nil -} - -// GetDefaultRuntimeName returns the current default runtime -func (conf *Config) GetDefaultRuntimeName() string { - conf.Lock() - rt := conf.DefaultRuntime - conf.Unlock() - - return rt -} - -// GetAllRuntimes returns a copy of the runtimes map -func (conf *Config) GetAllRuntimes() map[string]types.Runtime { - conf.Lock() - rts := conf.Runtimes - conf.Unlock() - return rts -} - -// GetExecRoot returns the user configured Exec-root -func (conf *Config) GetExecRoot() string { - return conf.ExecRoot -} - -// GetInitPath returns the configured docker-init path -func (conf *Config) GetInitPath() string { - conf.Lock() - defer conf.Unlock() - if conf.InitPath != "" { - return conf.InitPath - } - if conf.DefaultInitBinary != "" { - return conf.DefaultInitBinary - } - return DefaultInitBinary -} - -// GetResolvConf returns the appropriate resolv.conf -// Check setupResolvConf on how this is selected -func (conf *Config) GetResolvConf() string { - return conf.ResolvConf -} diff --git a/daemon/config/config_common_unix_test.go b/daemon/config/config_common_unix_test.go deleted file mode 100644 index 47774a8ec1a18..0000000000000 --- a/daemon/config/config_common_unix_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// +build !windows - -package config // import "github.com/docker/docker/daemon/config" - -import ( - "testing" - - "github.com/docker/docker/api/types" -) - -func TestCommonUnixValidateConfigurationErrors(t *testing.T) { - testCases := []struct { - config *Config - }{ - // Can't override the stock runtime - { - config: &Config{ - CommonUnixConfig: CommonUnixConfig{ - Runtimes: map[string]types.Runtime{ - StockRuntimeName: {}, - }, - }, - }, - }, - // Default runtime should be present in runtimes - { - config: &Config{ - CommonUnixConfig: CommonUnixConfig{ - Runtimes: map[string]types.Runtime{ - "foo": {}, - }, - DefaultRuntime: "bar", - }, - }, - }, - } - for _, tc := range testCases { - err := Validate(tc.config) - if err == nil { - t.Fatalf("expected error, got nil for config %v", tc.config) - } - } -} - -func TestCommonUnixGetInitPath(t *testing.T) { - testCases := []struct { - config *Config - expectedInitPath string - }{ - { - config: &Config{ - InitPath: "some-init-path", - }, - expectedInitPath: "some-init-path", - }, - { - config: &Config{ - CommonUnixConfig: CommonUnixConfig{ - DefaultInitBinary: "foo-init-bin", - }, - }, - expectedInitPath: "foo-init-bin", - }, - { - config: &Config{ - InitPath: "init-path-A", - CommonUnixConfig: CommonUnixConfig{ - DefaultInitBinary: "init-path-B", - }, - }, - expectedInitPath: "init-path-A", - }, - { - config: &Config{}, - expectedInitPath: "docker-init", - }, - } - for _, tc := range testCases { - initPath := tc.config.GetInitPath() - if initPath != tc.expectedInitPath { - t.Fatalf("expected initPath to be %v, got %v", tc.expectedInitPath, initPath) - } - } -} diff --git a/daemon/config/config_linux.go b/daemon/config/config_linux.go new file mode 100644 index 0000000000000..498e9e74fb511 --- /dev/null +++ b/daemon/config/config_linux.go @@ -0,0 +1,166 @@ +package config // import "github.com/docker/docker/daemon/config" + +import ( + "fmt" + "net" + + "github.com/docker/docker/api/types" + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/opts" + units "github.com/docker/go-units" +) + +const ( + // DefaultIpcMode is default for container's IpcMode, if not set otherwise + DefaultIpcMode = containertypes.IPCModePrivate + + // DefaultCgroupNamespaceMode is the default mode for containers cgroup namespace when using cgroups v2. + DefaultCgroupNamespaceMode = containertypes.CgroupnsModePrivate + + // DefaultCgroupV1NamespaceMode is the default mode for containers cgroup namespace when using cgroups v1. + DefaultCgroupV1NamespaceMode = containertypes.CgroupnsModeHost + + // StockRuntimeName is the reserved name/alias used to represent the + // OCI runtime being shipped with the docker daemon package. + StockRuntimeName = "runc" +) + +// BridgeConfig stores all the bridge driver specific +// configuration. +type BridgeConfig struct { + commonBridgeConfig + + // Fields below here are platform specific. + DefaultIP net.IP `json:"ip,omitempty"` + IP string `json:"bip,omitempty"` + DefaultGatewayIPv4 net.IP `json:"default-gateway,omitempty"` + DefaultGatewayIPv6 net.IP `json:"default-gateway-v6,omitempty"` + InterContainerCommunication bool `json:"icc,omitempty"` + + EnableIPv6 bool `json:"ipv6,omitempty"` + EnableIPTables bool `json:"iptables,omitempty"` + EnableIP6Tables bool `json:"ip6tables,omitempty"` + EnableIPForward bool `json:"ip-forward,omitempty"` + EnableIPMasq bool `json:"ip-masq,omitempty"` + EnableUserlandProxy bool `json:"userland-proxy,omitempty"` + UserlandProxyPath string `json:"userland-proxy-path,omitempty"` + FixedCIDRv6 string `json:"fixed-cidr-v6,omitempty"` +} + +// Config defines the configuration of a docker daemon. +// It includes json tags to deserialize configuration from a file +// using the same names that the flags in the command line uses. +type Config struct { + CommonConfig + + // Fields below here are platform specific. + Runtimes map[string]types.Runtime `json:"runtimes,omitempty"` + DefaultInitBinary string `json:"default-init,omitempty"` + CgroupParent string `json:"cgroup-parent,omitempty"` + EnableSelinuxSupport bool `json:"selinux-enabled,omitempty"` + RemappedRoot string `json:"userns-remap,omitempty"` + Ulimits map[string]*units.Ulimit `json:"default-ulimits,omitempty"` + CPURealtimePeriod int64 `json:"cpu-rt-period,omitempty"` + CPURealtimeRuntime int64 `json:"cpu-rt-runtime,omitempty"` + OOMScoreAdjust int `json:"oom-score-adjust,omitempty"` + Init bool `json:"init,omitempty"` + InitPath string `json:"init-path,omitempty"` + SeccompProfile string `json:"seccomp-profile,omitempty"` + ShmSize opts.MemBytes `json:"default-shm-size,omitempty"` + NoNewPrivileges bool `json:"no-new-privileges,omitempty"` + IpcMode string `json:"default-ipc-mode,omitempty"` + CgroupNamespaceMode string `json:"default-cgroupns-mode,omitempty"` + // ResolvConf is the path to the configuration of the host resolver + ResolvConf string `json:"resolv-conf,omitempty"` + Rootless bool `json:"rootless,omitempty"` +} + +// GetRuntime returns the runtime path and arguments for a given +// runtime name +func (conf *Config) GetRuntime(name string) *types.Runtime { + conf.Lock() + defer conf.Unlock() + if rt, ok := conf.Runtimes[name]; ok { + return &rt + } + return nil +} + +// GetAllRuntimes returns a copy of the runtimes map +func (conf *Config) GetAllRuntimes() map[string]types.Runtime { + conf.Lock() + rts := conf.Runtimes + conf.Unlock() + return rts +} + +// GetExecRoot returns the user configured Exec-root +func (conf *Config) GetExecRoot() string { + return conf.ExecRoot +} + +// GetInitPath returns the configured docker-init path +func (conf *Config) GetInitPath() string { + conf.Lock() + defer conf.Unlock() + if conf.InitPath != "" { + return conf.InitPath + } + if conf.DefaultInitBinary != "" { + return conf.DefaultInitBinary + } + return DefaultInitBinary +} + +// GetResolvConf returns the appropriate resolv.conf +// Check setupResolvConf on how this is selected +func (conf *Config) GetResolvConf() string { + return conf.ResolvConf +} + +// IsSwarmCompatible defines if swarm mode can be enabled in this config +func (conf *Config) IsSwarmCompatible() error { + if conf.ClusterStore != "" || conf.ClusterAdvertise != "" { + return fmt.Errorf("--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode") + } + if conf.LiveRestoreEnabled { + return fmt.Errorf("--live-restore daemon configuration is incompatible with swarm mode") + } + return nil +} + +func verifyDefaultIpcMode(mode string) error { + const hint = `use "shareable" or "private"` + + dm := containertypes.IpcMode(mode) + if !dm.Valid() { + return fmt.Errorf("default IPC mode setting (%v) is invalid; "+hint, dm) + } + if dm != "" && !dm.IsPrivate() && !dm.IsShareable() { + return fmt.Errorf(`IPC mode "%v" is not supported as default value; `+hint, dm) + } + return nil +} + +func verifyDefaultCgroupNsMode(mode string) error { + cm := containertypes.CgroupnsMode(mode) + if !cm.Valid() { + return fmt.Errorf(`default cgroup namespace mode (%v) is invalid; use "host" or "private"`, cm) + } + + return nil +} + +// ValidatePlatformConfig checks if any platform-specific configuration settings are invalid. +func (conf *Config) ValidatePlatformConfig() error { + if err := verifyDefaultIpcMode(conf.IpcMode); err != nil { + return err + } + + return verifyDefaultCgroupNsMode(conf.CgroupNamespaceMode) +} + +// IsRootless returns conf.Rootless on Linux but false on Windows +func (conf *Config) IsRootless() bool { + return conf.Rootless +} diff --git a/daemon/config/config_linux_test.go b/daemon/config/config_linux_test.go new file mode 100644 index 0000000000000..cfc635567dfd4 --- /dev/null +++ b/daemon/config/config_linux_test.go @@ -0,0 +1,202 @@ +package config // import "github.com/docker/docker/daemon/config" + +import ( + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/opts" + units "github.com/docker/go-units" + "github.com/spf13/pflag" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/fs" +) + +func TestGetConflictFreeConfiguration(t *testing.T) { + configFileData := ` + { + "debug": true, + "default-ulimits": { + "nofile": { + "Name": "nofile", + "Hard": 2048, + "Soft": 1024 + } + }, + "log-opts": { + "tag": "test_tag" + } + }` + + file := fs.NewFile(t, "docker-config", fs.WithContent(configFileData)) + defer file.Remove() + + flags := pflag.NewFlagSet("test", pflag.ContinueOnError) + var debug bool + flags.BoolVarP(&debug, "debug", "D", false, "") + flags.Var(opts.NewNamedUlimitOpt("default-ulimits", nil), "default-ulimit", "") + flags.Var(opts.NewNamedMapOpts("log-opts", nil, nil), "log-opt", "") + + cc, err := getConflictFreeConfiguration(file.Path(), flags) + assert.NilError(t, err) + + assert.Check(t, cc.Debug) + + expectedUlimits := map[string]*units.Ulimit{ + "nofile": { + Name: "nofile", + Hard: 2048, + Soft: 1024, + }, + } + + assert.Check(t, is.DeepEqual(expectedUlimits, cc.Ulimits)) +} + +func TestDaemonConfigurationMerge(t *testing.T) { + configFileData := ` + { + "debug": true, + "default-ulimits": { + "nofile": { + "Name": "nofile", + "Hard": 2048, + "Soft": 1024 + } + }, + "log-opts": { + "tag": "test_tag" + } + }` + + file := fs.NewFile(t, "docker-config", fs.WithContent(configFileData)) + defer file.Remove() + + c := &Config{ + CommonConfig: CommonConfig{ + AutoRestart: true, + LogConfig: LogConfig{ + Type: "syslog", + Config: map[string]string{"tag": "test"}, + }, + }, + } + + flags := pflag.NewFlagSet("test", pflag.ContinueOnError) + + var debug bool + flags.BoolVarP(&debug, "debug", "D", false, "") + flags.Var(opts.NewNamedUlimitOpt("default-ulimits", nil), "default-ulimit", "") + flags.Var(opts.NewNamedMapOpts("log-opts", nil, nil), "log-opt", "") + + cc, err := MergeDaemonConfigurations(c, flags, file.Path()) + assert.NilError(t, err) + + assert.Check(t, cc.Debug) + assert.Check(t, cc.AutoRestart) + + expectedLogConfig := LogConfig{ + Type: "syslog", + Config: map[string]string{"tag": "test_tag"}, + } + + assert.Check(t, is.DeepEqual(expectedLogConfig, cc.LogConfig)) + + expectedUlimits := map[string]*units.Ulimit{ + "nofile": { + Name: "nofile", + Hard: 2048, + Soft: 1024, + }, + } + + assert.Check(t, is.DeepEqual(expectedUlimits, cc.Ulimits)) +} + +func TestDaemonConfigurationMergeShmSize(t *testing.T) { + data := `{"default-shm-size": "1g"}` + + file := fs.NewFile(t, "docker-config", fs.WithContent(data)) + defer file.Remove() + + c := &Config{} + + flags := pflag.NewFlagSet("test", pflag.ContinueOnError) + shmSize := opts.MemBytes(DefaultShmSize) + flags.Var(&shmSize, "default-shm-size", "") + + cc, err := MergeDaemonConfigurations(c, flags, file.Path()) + assert.NilError(t, err) + + expectedValue := 1 * 1024 * 1024 * 1024 + assert.Check(t, is.Equal(int64(expectedValue), cc.ShmSize.Value())) +} + +func TestUnixValidateConfigurationErrors(t *testing.T) { + testCases := []struct { + config *Config + }{ + // Can't override the stock runtime + { + config: &Config{ + Runtimes: map[string]types.Runtime{ + StockRuntimeName: {}, + }, + }, + }, + // Default runtime should be present in runtimes + { + config: &Config{ + Runtimes: map[string]types.Runtime{ + "foo": {}, + }, + CommonConfig: CommonConfig{ + DefaultRuntime: "bar", + }, + }, + }, + } + for _, tc := range testCases { + err := Validate(tc.config) + if err == nil { + t.Fatalf("expected error, got nil for config %v", tc.config) + } + } +} + +func TestUnixGetInitPath(t *testing.T) { + testCases := []struct { + config *Config + expectedInitPath string + }{ + { + config: &Config{ + InitPath: "some-init-path", + }, + expectedInitPath: "some-init-path", + }, + { + config: &Config{ + DefaultInitBinary: "foo-init-bin", + }, + expectedInitPath: "foo-init-bin", + }, + { + config: &Config{ + InitPath: "init-path-A", + DefaultInitBinary: "init-path-B", + }, + expectedInitPath: "init-path-A", + }, + { + config: &Config{}, + expectedInitPath: "docker-init", + }, + } + for _, tc := range testCases { + initPath := tc.config.GetInitPath() + if initPath != tc.expectedInitPath { + t.Fatalf("expected initPath to be %v, got %v", tc.expectedInitPath, initPath) + } + } +} diff --git a/daemon/config/config_test.go b/daemon/config/config_test.go index 6998ed3312342..35ac9a0147074 100644 --- a/daemon/config/config_test.go +++ b/daemon/config/config_test.go @@ -1,18 +1,18 @@ package config // import "github.com/docker/docker/daemon/config" import ( - "io/ioutil" "os" "strings" "testing" "github.com/docker/docker/daemon/discovery" + "github.com/docker/docker/libnetwork/ipamutils" "github.com/docker/docker/opts" "github.com/spf13/pflag" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/fs" - "gotest.tools/skip" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/fs" + "gotest.tools/v3/skip" ) func TestDaemonConfigurationNotFound(t *testing.T) { @@ -23,7 +23,7 @@ func TestDaemonConfigurationNotFound(t *testing.T) { } func TestDaemonBrokenConfiguration(t *testing.T) { - f, err := ioutil.TempFile("", "docker-config-") + f, err := os.CreateTemp("", "docker-config-") if err != nil { t.Fatal(err) } @@ -76,7 +76,7 @@ func TestFindConfigurationConflictsWithNamedOptions(t *testing.T) { } func TestDaemonConfigurationMergeConflicts(t *testing.T) { - f, err := ioutil.TempFile("", "docker-config-") + f, err := os.CreateTemp("", "docker-config-") if err != nil { t.Fatal(err) } @@ -99,7 +99,7 @@ func TestDaemonConfigurationMergeConflicts(t *testing.T) { } func TestDaemonConfigurationMergeConcurrent(t *testing.T) { - f, err := ioutil.TempFile("", "docker-config-") + f, err := os.CreateTemp("", "docker-config-") if err != nil { t.Fatal(err) } @@ -115,7 +115,7 @@ func TestDaemonConfigurationMergeConcurrent(t *testing.T) { } func TestDaemonConfigurationMergeConcurrentError(t *testing.T) { - f, err := ioutil.TempFile("", "docker-config-") + f, err := os.CreateTemp("", "docker-config-") if err != nil { t.Fatal(err) } @@ -131,7 +131,7 @@ func TestDaemonConfigurationMergeConcurrentError(t *testing.T) { } func TestDaemonConfigurationMergeConflictsWithInnerStructs(t *testing.T) { - f, err := ioutil.TempFile("", "docker-config-") + f, err := os.CreateTemp("", "docker-config-") if err != nil { t.Fatal(err) } @@ -153,6 +153,48 @@ func TestDaemonConfigurationMergeConflictsWithInnerStructs(t *testing.T) { } } +// Test for #40711 +func TestDaemonConfigurationMergeDefaultAddressPools(t *testing.T) { + emptyConfigFile := fs.NewFile(t, "config", fs.WithContent(`{}`)) + defer emptyConfigFile.Remove() + configFile := fs.NewFile(t, "config", fs.WithContent(`{"default-address-pools":[{"base": "10.123.0.0/16", "size": 24 }]}`)) + defer configFile.Remove() + + expected := []*ipamutils.NetworkToSplit{{Base: "10.123.0.0/16", Size: 24}} + + t.Run("empty config file", func(t *testing.T) { + var conf = Config{} + flags := pflag.NewFlagSet("test", pflag.ContinueOnError) + flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "") + flags.Set("default-address-pool", "base=10.123.0.0/16,size=24") + + config, err := MergeDaemonConfigurations(&conf, flags, emptyConfigFile.Path()) + assert.NilError(t, err) + assert.DeepEqual(t, config.DefaultAddressPools.Value(), expected) + }) + + t.Run("config file", func(t *testing.T) { + var conf = Config{} + flags := pflag.NewFlagSet("test", pflag.ContinueOnError) + flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "") + + config, err := MergeDaemonConfigurations(&conf, flags, configFile.Path()) + assert.NilError(t, err) + assert.DeepEqual(t, config.DefaultAddressPools.Value(), expected) + }) + + t.Run("with conflicting options", func(t *testing.T) { + var conf = Config{} + flags := pflag.NewFlagSet("test", pflag.ContinueOnError) + flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "") + flags.Set("default-address-pool", "base=10.123.0.0/16,size=24") + + _, err := MergeDaemonConfigurations(&conf, flags, configFile.Path()) + assert.ErrorContains(t, err, "the following directives are specified both as a flag and in the configuration file") + assert.ErrorContains(t, err, "default-address-pools") + }) +} + func TestFindConfigurationConflictsWithUnknownKeys(t *testing.T) { config := map[string]interface{}{"tls-verify": "true"} flags := pflag.NewFlagSet("test", pflag.ContinueOnError) @@ -188,138 +230,148 @@ func TestFindConfigurationConflictsWithMergedValues(t *testing.T) { } } -func TestValidateReservedNamespaceLabels(t *testing.T) { - for _, validLabels := range [][]string{ - nil, // no error if there are no labels - { // no error if there aren't any reserved namespace labels - "hello=world", - "label=me", - }, - { // only reserved namespaces that end with a dot are invalid - "com.dockerpsychnotreserved.label=value", - "io.dockerproject.not=reserved", - "org.docker.not=reserved", - }, - } { - assert.Check(t, ValidateReservedNamespaceLabels(validLabels)) - } - - for _, invalidLabel := range []string{ - "com.docker.feature=enabled", - "io.docker.configuration=0", - "org.dockerproject.setting=on", - // casing doesn't matter - "COM.docker.feature=enabled", - "io.DOCKER.CONFIGURATION=0", - "Org.Dockerproject.Setting=on", - } { - err := ValidateReservedNamespaceLabels([]string{ - "valid=label", - invalidLabel, - "another=valid", - }) - assert.Check(t, is.ErrorContains(err, invalidLabel)) - } -} - func TestValidateConfigurationErrors(t *testing.T) { - minusNumber := -10 + intPtr := func(i int) *int { return &i } + testCases := []struct { - config *Config + name string + config *Config + expectedErr string }{ { + name: "single label without value", config: &Config{ CommonConfig: CommonConfig{ Labels: []string{"one"}, }, }, + expectedErr: "bad attribute format: one", }, { + name: "multiple label without value", config: &Config{ CommonConfig: CommonConfig{ Labels: []string{"foo=bar", "one"}, }, }, + expectedErr: "bad attribute format: one", }, { + name: "single DNS, invalid IP-address", config: &Config{ CommonConfig: CommonConfig{ - DNS: []string{"1.1.1.1o"}, + DNSConfig: DNSConfig{ + DNS: []string{"1.1.1.1o"}, + }, }, }, + expectedErr: "1.1.1.1o is not an ip address", }, { + name: "multiple DNS, invalid IP-address", config: &Config{ CommonConfig: CommonConfig{ - DNS: []string{"2.2.2.2", "1.1.1.1o"}, + DNSConfig: DNSConfig{ + DNS: []string{"2.2.2.2", "1.1.1.1o"}, + }, }, }, + expectedErr: "1.1.1.1o is not an ip address", }, { + name: "single DNSSearch", config: &Config{ CommonConfig: CommonConfig{ - DNSSearch: []string{"123456"}, + DNSConfig: DNSConfig{ + DNSSearch: []string{"123456"}, + }, }, }, + expectedErr: "123456 is not a valid domain", }, { + name: "multiple DNSSearch", config: &Config{ CommonConfig: CommonConfig{ - DNSSearch: []string{"a.b.c", "123456"}, + DNSConfig: DNSConfig{ + DNSSearch: []string{"a.b.c", "123456"}, + }, }, }, + expectedErr: "123456 is not a valid domain", }, { + name: "negative max-concurrent-downloads", config: &Config{ CommonConfig: CommonConfig{ - MaxConcurrentDownloads: &minusNumber, - // This is weird... - ValuesSet: map[string]interface{}{ - "max-concurrent-downloads": -1, - }, + MaxConcurrentDownloads: intPtr(-10), }, }, + expectedErr: "invalid max concurrent downloads: -10", }, { + name: "negative max-concurrent-uploads", config: &Config{ CommonConfig: CommonConfig{ - MaxConcurrentUploads: &minusNumber, - // This is weird... - ValuesSet: map[string]interface{}{ - "max-concurrent-uploads": -1, - }, + MaxConcurrentUploads: intPtr(-10), + }, + }, + expectedErr: "invalid max concurrent uploads: -10", + }, + { + name: "negative max-download-attempts", + config: &Config{ + CommonConfig: CommonConfig{ + MaxDownloadAttempts: intPtr(-10), + }, + }, + expectedErr: "invalid max download attempts: -10", + }, + { + name: "zero max-download-attempts", + config: &Config{ + CommonConfig: CommonConfig{ + MaxDownloadAttempts: intPtr(0), }, }, + expectedErr: "invalid max download attempts: 0", }, { + name: "generic resource without =", config: &Config{ CommonConfig: CommonConfig{ NodeGenericResources: []string{"foo"}, }, }, + expectedErr: "could not parse GenericResource: incorrect term foo, missing '=' or malformed expression", }, { + name: "generic resource mixed named and discrete", config: &Config{ CommonConfig: CommonConfig{ NodeGenericResources: []string{"foo=bar", "foo=1"}, }, }, + expectedErr: "could not parse GenericResource: mixed discrete and named resources in expression 'foo=[bar 1]'", }, } for _, tc := range testCases { - err := Validate(tc.config) - if err == nil { - t.Fatalf("expected error, got nil for config %v", tc.config) - } + t.Run(tc.name, func(t *testing.T) { + err := Validate(tc.config) + assert.Error(t, err, tc.expectedErr) + }) } } func TestValidateConfiguration(t *testing.T) { - minusNumber := 4 + intPtr := func(i int) *int { return &i } + testCases := []struct { + name string config *Config }{ { + name: "with label", config: &Config{ CommonConfig: CommonConfig{ Labels: []string{"one=two"}, @@ -327,42 +379,51 @@ func TestValidateConfiguration(t *testing.T) { }, }, { + name: "with dns", config: &Config{ CommonConfig: CommonConfig{ - DNS: []string{"1.1.1.1"}, + DNSConfig: DNSConfig{ + DNS: []string{"1.1.1.1"}, + }, }, }, }, { + name: "with dns-search", config: &Config{ CommonConfig: CommonConfig{ - DNSSearch: []string{"a.b.c"}, + DNSConfig: DNSConfig{ + DNSSearch: []string{"a.b.c"}, + }, }, }, }, { + name: "with max-concurrent-downloads", config: &Config{ CommonConfig: CommonConfig{ - MaxConcurrentDownloads: &minusNumber, - // This is weird... - ValuesSet: map[string]interface{}{ - "max-concurrent-downloads": -1, - }, + MaxConcurrentDownloads: intPtr(4), }, }, }, { + name: "with max-concurrent-uploads", config: &Config{ CommonConfig: CommonConfig{ - MaxConcurrentUploads: &minusNumber, - // This is weird... - ValuesSet: map[string]interface{}{ - "max-concurrent-uploads": -1, - }, + MaxConcurrentUploads: intPtr(4), }, }, }, { + name: "with max-download-attempts", + config: &Config{ + CommonConfig: CommonConfig{ + MaxDownloadAttempts: intPtr(4), + }, + }, + }, + { + name: "with multiple node generic resources", config: &Config{ CommonConfig: CommonConfig{ NodeGenericResources: []string{"foo=bar", "foo=baz"}, @@ -370,6 +431,7 @@ func TestValidateConfiguration(t *testing.T) { }, }, { + name: "with node generic resources", config: &Config{ CommonConfig: CommonConfig{ NodeGenericResources: []string{"foo=1"}, @@ -378,10 +440,10 @@ func TestValidateConfiguration(t *testing.T) { }, } for _, tc := range testCases { - err := Validate(tc.config) - if err != nil { - t.Fatalf("expected no error, got error %v", err) - } + t.Run(tc.name, func(t *testing.T) { + err := Validate(tc.config) + assert.NilError(t, err) + }) } } @@ -476,7 +538,7 @@ func TestReloadDefaultConfigNotExist(t *testing.T) { // TestReloadBadDefaultConfig tests that when `--config-file` is not set // and the default configuration file exists and is bad return an error func TestReloadBadDefaultConfig(t *testing.T) { - f, err := ioutil.TempFile("", "docker-config-") + f, err := os.CreateTemp("", "docker-config-") if err != nil { t.Fatal(err) } @@ -516,3 +578,49 @@ func TestReloadWithDuplicateLabels(t *testing.T) { err := Reload(configFile, flags, func(c *Config) {}) assert.Check(t, err) } + +func TestMaskURLCredentials(t *testing.T) { + tests := []struct { + rawURL string + maskedURL string + }{ + { + rawURL: "", + maskedURL: "", + }, { + rawURL: "invalidURL", + maskedURL: "invalidURL", + }, { + rawURL: "http://proxy.example.com:80/", + maskedURL: "http://proxy.example.com:80/", + }, { + rawURL: "http://USER:PASSWORD@proxy.example.com:80/", + maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", + }, { + rawURL: "http://PASSWORD:PASSWORD@proxy.example.com:80/", + maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", + }, { + rawURL: "http://USER:@proxy.example.com:80/", + maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", + }, { + rawURL: "http://:PASSWORD@proxy.example.com:80/", + maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", + }, { + rawURL: "http://USER@docker:password@proxy.example.com:80/", + maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", + }, { + rawURL: "http://USER%40docker:password@proxy.example.com:80/", + maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", + }, { + rawURL: "http://USER%40docker:pa%3Fsword@proxy.example.com:80/", + maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", + }, { + rawURL: "http://USER%40docker:pa%3Fsword@proxy.example.com:80/hello%20world", + maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/hello%20world", + }, + } + for _, test := range tests { + maskedURL := MaskCredentials(test.rawURL) + assert.Equal(t, maskedURL, test.maskedURL) + } +} diff --git a/daemon/config/config_unix.go b/daemon/config/config_unix.go deleted file mode 100644 index 5ed6abd89e8fd..0000000000000 --- a/daemon/config/config_unix.go +++ /dev/null @@ -1,89 +0,0 @@ -// +build linux freebsd - -package config // import "github.com/docker/docker/daemon/config" - -import ( - "fmt" - - containertypes "github.com/docker/docker/api/types/container" - "github.com/docker/docker/opts" - "github.com/docker/go-units" -) - -const ( - // DefaultIpcMode is default for container's IpcMode, if not set otherwise - DefaultIpcMode = "shareable" // TODO: change to private -) - -// Config defines the configuration of a docker daemon. -// It includes json tags to deserialize configuration from a file -// using the same names that the flags in the command line uses. -type Config struct { - CommonConfig - - // These fields are common to all unix platforms. - CommonUnixConfig - // Fields below here are platform specific. - CgroupParent string `json:"cgroup-parent,omitempty"` - EnableSelinuxSupport bool `json:"selinux-enabled,omitempty"` - RemappedRoot string `json:"userns-remap,omitempty"` - Ulimits map[string]*units.Ulimit `json:"default-ulimits,omitempty"` - CPURealtimePeriod int64 `json:"cpu-rt-period,omitempty"` - CPURealtimeRuntime int64 `json:"cpu-rt-runtime,omitempty"` - OOMScoreAdjust int `json:"oom-score-adjust,omitempty"` - Init bool `json:"init,omitempty"` - InitPath string `json:"init-path,omitempty"` - SeccompProfile string `json:"seccomp-profile,omitempty"` - ShmSize opts.MemBytes `json:"default-shm-size,omitempty"` - NoNewPrivileges bool `json:"no-new-privileges,omitempty"` - IpcMode string `json:"default-ipc-mode,omitempty"` - // ResolvConf is the path to the configuration of the host resolver - ResolvConf string `json:"resolv-conf,omitempty"` -} - -// BridgeConfig stores all the bridge driver specific -// configuration. -type BridgeConfig struct { - commonBridgeConfig - - // These fields are common to all unix platforms. - commonUnixBridgeConfig - - // Fields below here are platform specific. - EnableIPv6 bool `json:"ipv6,omitempty"` - EnableIPTables bool `json:"iptables,omitempty"` - EnableIPForward bool `json:"ip-forward,omitempty"` - EnableIPMasq bool `json:"ip-masq,omitempty"` - EnableUserlandProxy bool `json:"userland-proxy,omitempty"` - UserlandProxyPath string `json:"userland-proxy-path,omitempty"` - FixedCIDRv6 string `json:"fixed-cidr-v6,omitempty"` -} - -// IsSwarmCompatible defines if swarm mode can be enabled in this config -func (conf *Config) IsSwarmCompatible() error { - if conf.ClusterStore != "" || conf.ClusterAdvertise != "" { - return fmt.Errorf("--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode") - } - if conf.LiveRestoreEnabled { - return fmt.Errorf("--live-restore daemon configuration is incompatible with swarm mode") - } - return nil -} - -func verifyDefaultIpcMode(mode string) error { - const hint = "Use \"shareable\" or \"private\"." - - dm := containertypes.IpcMode(mode) - if !dm.Valid() { - return fmt.Errorf("Default IPC mode setting (%v) is invalid. "+hint, dm) - } - if dm != "" && !dm.IsPrivate() && !dm.IsShareable() { - return fmt.Errorf("IPC mode \"%v\" is not supported as default value. "+hint, dm) - } - return nil -} - -// ValidatePlatformConfig checks if any platform-specific configuration settings are invalid. -func (conf *Config) ValidatePlatformConfig() error { - return verifyDefaultIpcMode(conf.IpcMode) -} diff --git a/daemon/config/config_unix_test.go b/daemon/config/config_unix_test.go deleted file mode 100644 index 529b6777050e7..0000000000000 --- a/daemon/config/config_unix_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// +build !windows - -package config // import "github.com/docker/docker/daemon/config" - -import ( - "testing" - - "github.com/docker/docker/opts" - "github.com/docker/go-units" - "github.com/spf13/pflag" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/fs" -) - -func TestGetConflictFreeConfiguration(t *testing.T) { - configFileData := ` - { - "debug": true, - "default-ulimits": { - "nofile": { - "Name": "nofile", - "Hard": 2048, - "Soft": 1024 - } - }, - "log-opts": { - "tag": "test_tag" - } - }` - - file := fs.NewFile(t, "docker-config", fs.WithContent(configFileData)) - defer file.Remove() - - flags := pflag.NewFlagSet("test", pflag.ContinueOnError) - var debug bool - flags.BoolVarP(&debug, "debug", "D", false, "") - flags.Var(opts.NewNamedUlimitOpt("default-ulimits", nil), "default-ulimit", "") - flags.Var(opts.NewNamedMapOpts("log-opts", nil, nil), "log-opt", "") - - cc, err := getConflictFreeConfiguration(file.Path(), flags) - assert.NilError(t, err) - - assert.Check(t, cc.Debug) - - expectedUlimits := map[string]*units.Ulimit{ - "nofile": { - Name: "nofile", - Hard: 2048, - Soft: 1024, - }, - } - - assert.Check(t, is.DeepEqual(expectedUlimits, cc.Ulimits)) -} - -func TestDaemonConfigurationMerge(t *testing.T) { - configFileData := ` - { - "debug": true, - "default-ulimits": { - "nofile": { - "Name": "nofile", - "Hard": 2048, - "Soft": 1024 - } - }, - "log-opts": { - "tag": "test_tag" - } - }` - - file := fs.NewFile(t, "docker-config", fs.WithContent(configFileData)) - defer file.Remove() - - c := &Config{ - CommonConfig: CommonConfig{ - AutoRestart: true, - LogConfig: LogConfig{ - Type: "syslog", - Config: map[string]string{"tag": "test"}, - }, - }, - } - - flags := pflag.NewFlagSet("test", pflag.ContinueOnError) - - var debug bool - flags.BoolVarP(&debug, "debug", "D", false, "") - flags.Var(opts.NewNamedUlimitOpt("default-ulimits", nil), "default-ulimit", "") - flags.Var(opts.NewNamedMapOpts("log-opts", nil, nil), "log-opt", "") - - cc, err := MergeDaemonConfigurations(c, flags, file.Path()) - assert.NilError(t, err) - - assert.Check(t, cc.Debug) - assert.Check(t, cc.AutoRestart) - - expectedLogConfig := LogConfig{ - Type: "syslog", - Config: map[string]string{"tag": "test_tag"}, - } - - assert.Check(t, is.DeepEqual(expectedLogConfig, cc.LogConfig)) - - expectedUlimits := map[string]*units.Ulimit{ - "nofile": { - Name: "nofile", - Hard: 2048, - Soft: 1024, - }, - } - - assert.Check(t, is.DeepEqual(expectedUlimits, cc.Ulimits)) -} - -func TestDaemonConfigurationMergeShmSize(t *testing.T) { - data := `{"default-shm-size": "1g"}` - - file := fs.NewFile(t, "docker-config", fs.WithContent(data)) - defer file.Remove() - - c := &Config{} - - flags := pflag.NewFlagSet("test", pflag.ContinueOnError) - shmSize := opts.MemBytes(DefaultShmSize) - flags.Var(&shmSize, "default-shm-size", "") - - cc, err := MergeDaemonConfigurations(c, flags, file.Path()) - assert.NilError(t, err) - - expectedValue := 1 * 1024 * 1024 * 1024 - assert.Check(t, is.Equal(int64(expectedValue), cc.ShmSize.Value())) -} diff --git a/daemon/config/config_windows.go b/daemon/config/config_windows.go index 0aa7d54bf29a2..29d7dbaa16c58 100644 --- a/daemon/config/config_windows.go +++ b/daemon/config/config_windows.go @@ -4,6 +4,12 @@ import ( "github.com/docker/docker/api/types" ) +const ( + // This is used by the `default-runtime` flag in dockerd as the default value. + // On windows we'd prefer to keep this empty so the value is auto-detected based on other options. + StockRuntimeName = "" +) + // BridgeConfig stores all the bridge driver specific // configuration. type BridgeConfig struct { @@ -11,8 +17,8 @@ type BridgeConfig struct { } // Config defines the configuration of a docker daemon. -// These are the configuration settings that you pass -// to the docker daemon when you launch it with say: `dockerd -e windows` +// It includes json tags to deserialize configuration from a file +// using the same names that the flags in the command line uses. type Config struct { CommonConfig @@ -26,16 +32,6 @@ func (conf *Config) GetRuntime(name string) *types.Runtime { return nil } -// GetInitPath returns the configure docker-init path -func (conf *Config) GetInitPath() string { - return "" -} - -// GetDefaultRuntimeName returns the current default runtime -func (conf *Config) GetDefaultRuntimeName() string { - return StockRuntimeName -} - // GetAllRuntimes returns a copy of the runtimes map func (conf *Config) GetAllRuntimes() map[string]types.Runtime { return map[string]types.Runtime{} @@ -46,6 +42,11 @@ func (conf *Config) GetExecRoot() string { return "" } +// GetInitPath returns the configured docker-init path +func (conf *Config) GetInitPath() string { + return "" +} + // IsSwarmCompatible defines if swarm mode can be enabled in this config func (conf *Config) IsSwarmCompatible() error { return nil @@ -55,3 +56,8 @@ func (conf *Config) IsSwarmCompatible() error { func (conf *Config) ValidatePlatformConfig() error { return nil } + +// IsRootless returns conf.Rootless on Linux but false on Windows +func (conf *Config) IsRootless() bool { + return false +} diff --git a/daemon/config/config_windows_test.go b/daemon/config/config_windows_test.go index 09417ee388307..95cfaf255aa00 100644 --- a/daemon/config/config_windows_test.go +++ b/daemon/config/config_windows_test.go @@ -1,19 +1,17 @@ -// +build windows - package config // import "github.com/docker/docker/daemon/config" import ( - "io/ioutil" + "os" "testing" "github.com/docker/docker/opts" "github.com/spf13/pflag" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestDaemonConfigurationMerge(t *testing.T) { - f, err := ioutil.TempFile("", "docker-config-") + f, err := os.CreateTemp("", "docker-config-") if err != nil { t.Fatal(err) } diff --git a/daemon/configs_unsupported.go b/daemon/configs_unsupported.go index ae6f14f54e691..ce98148ee71bd 100644 --- a/daemon/configs_unsupported.go +++ b/daemon/configs_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows // +build !linux,!windows package daemon // import "github.com/docker/docker/daemon" diff --git a/daemon/container.go b/daemon/container.go index c8e2053970a72..5ac4865a04f88 100644 --- a/daemon/container.go +++ b/daemon/container.go @@ -3,10 +3,8 @@ package daemon // import "github.com/docker/docker/daemon" import ( "fmt" "os" - "path" "path/filepath" "runtime" - "strings" "time" containertypes "github.com/docker/docker/api/types/container" @@ -15,15 +13,17 @@ import ( "github.com/docker/docker/daemon/network" "github.com/docker/docker/errdefs" "github.com/docker/docker/image" + "github.com/docker/docker/oci/caps" "github.com/docker/docker/opts" - "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" "github.com/docker/docker/runconfig" volumemounts "github.com/docker/docker/volume/mounts" "github.com/docker/go-connections/nat" - "github.com/opencontainers/selinux/go-selinux/label" + "github.com/moby/sys/signal" + "github.com/opencontainers/selinux/go-selinux" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // GetContainer looks for a container using the provided information, which could be @@ -90,20 +90,18 @@ func (daemon *Daemon) containerRoot(id string) string { // Load reads the contents of a container from disk // This is typically done at startup. func (daemon *Daemon) load(id string) (*container.Container, error) { - container := daemon.newBaseContainer(id) + ctr := daemon.newBaseContainer(id) - if err := container.FromDisk(); err != nil { - return nil, err - } - if err := label.ReserveLabel(container.ProcessLabel); err != nil { + if err := ctr.FromDisk(); err != nil { return nil, err } + selinux.ReserveLabel(ctr.ProcessLabel) - if container.ID != id { - return container, fmt.Errorf("Container %s is stored at %s", container.ID, id) + if ctr.ID != id { + return ctr, fmt.Errorf("Container %s is stored at %s", ctr.ID, id) } - return container, nil + return ctr, nil } // Register makes a container object usable by the daemon as @@ -152,13 +150,13 @@ func (daemon *Daemon) newContainer(name string, operatingSystem string, config * base.Created = time.Now().UTC() base.Managed = managed base.Path = entrypoint - base.Args = args //FIXME: de-duplicate from config + base.Args = args // FIXME: de-duplicate from config base.Config = config base.HostConfig = &containertypes.HostConfig{} base.ImageID = imgID base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName} base.Name = name - base.Driver = daemon.imageService.GraphDriverForOS(operatingSystem) + base.Driver = daemon.imageService.GraphDriverName() base.OS = operatingSystem return base, err } @@ -231,128 +229,156 @@ func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig * // verifyContainerSettings performs validation of the hostconfig and config // structures. -func (daemon *Daemon) verifyContainerSettings(platform string, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { +func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) (warnings []string, err error) { // First perform verification of settings common across all platforms. - if config != nil { - if config.WorkingDir != "" { - wdInvalid := false - if runtime.GOOS == platform { - config.WorkingDir = filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics - if !system.IsAbs(config.WorkingDir) { - wdInvalid = true - } - } else { - // LCOW. Force Unix semantics - config.WorkingDir = strings.Replace(config.WorkingDir, string(os.PathSeparator), "/", -1) - if !path.IsAbs(config.WorkingDir) { - wdInvalid = true - } - } - if wdInvalid { - return nil, fmt.Errorf("the working directory '%s' is invalid, it needs to be an absolute path", config.WorkingDir) - } - } + if err = validateContainerConfig(config); err != nil { + return warnings, err + } + if err := validateHostConfig(hostConfig); err != nil { + return warnings, err + } - if len(config.StopSignal) > 0 { - _, err := signal.ParseSignal(config.StopSignal) - if err != nil { - return nil, err - } - } + // Now do platform-specific verification + warnings, err = verifyPlatformContainerSettings(daemon, hostConfig, update) + for _, w := range warnings { + logrus.Warn(w) + } + return warnings, err +} - // Validate if Env contains empty variable or not (e.g., ``, `=foo`) - for _, env := range config.Env { - if _, err := opts.ValidateEnv(env); err != nil { - return nil, err - } +func validateContainerConfig(config *containertypes.Config) error { + if config == nil { + return nil + } + if err := translateWorkingDir(config); err != nil { + return err + } + if len(config.StopSignal) > 0 { + if _, err := signal.ParseSignal(config.StopSignal); err != nil { + return err } - - // Validate the healthcheck params of Config - if config.Healthcheck != nil { - if config.Healthcheck.Interval != 0 && config.Healthcheck.Interval < containertypes.MinimumDuration { - return nil, errors.Errorf("Interval in Healthcheck cannot be less than %s", containertypes.MinimumDuration) - } - - if config.Healthcheck.Timeout != 0 && config.Healthcheck.Timeout < containertypes.MinimumDuration { - return nil, errors.Errorf("Timeout in Healthcheck cannot be less than %s", containertypes.MinimumDuration) - } - - if config.Healthcheck.Retries < 0 { - return nil, errors.Errorf("Retries in Healthcheck cannot be negative") - } - - if config.Healthcheck.StartPeriod != 0 && config.Healthcheck.StartPeriod < containertypes.MinimumDuration { - return nil, errors.Errorf("StartPeriod in Healthcheck cannot be less than %s", containertypes.MinimumDuration) - } + } + // Validate if Env contains empty variable or not (e.g., ``, `=foo`) + for _, env := range config.Env { + if _, err := opts.ValidateEnv(env); err != nil { + return err } } + return validateHealthCheck(config.Healthcheck) +} +func validateHostConfig(hostConfig *containertypes.HostConfig) error { if hostConfig == nil { - return nil, nil + return nil } if hostConfig.AutoRemove && !hostConfig.RestartPolicy.IsNone() { - return nil, errors.Errorf("can't create 'AutoRemove' container with restart policy") + return errors.Errorf("can't create 'AutoRemove' container with restart policy") } - // Validate mounts; check if host directories still exist - parser := volumemounts.NewParser(platform) - for _, cfg := range hostConfig.Mounts { + parser := volumemounts.NewParser() + for _, c := range hostConfig.Mounts { + cfg := c if err := parser.ValidateMountConfig(&cfg); err != nil { - return nil, err + return err } } - for _, extraHost := range hostConfig.ExtraHosts { if _, err := opts.ValidateExtraHost(extraHost); err != nil { - return nil, err + return err } } + if err := validatePortBindings(hostConfig.PortBindings); err != nil { + return err + } + if err := validateRestartPolicy(hostConfig.RestartPolicy); err != nil { + return err + } + if err := validateCapabilities(hostConfig); err != nil { + return err + } + if !hostConfig.Isolation.IsValid() { + return errors.Errorf("invalid isolation '%s' on %s", hostConfig.Isolation, runtime.GOOS) + } + return nil +} + +func validateCapabilities(hostConfig *containertypes.HostConfig) error { + if _, err := caps.NormalizeLegacyCapabilities(hostConfig.CapAdd); err != nil { + return errors.Wrap(err, "invalid CapAdd") + } + if _, err := caps.NormalizeLegacyCapabilities(hostConfig.CapDrop); err != nil { + return errors.Wrap(err, "invalid CapDrop") + } + // TODO consider returning warnings if "Privileged" is combined with Capabilities, CapAdd and/or CapDrop + return nil +} + +// validateHealthCheck validates the healthcheck params of Config +func validateHealthCheck(healthConfig *containertypes.HealthConfig) error { + if healthConfig == nil { + return nil + } + if healthConfig.Interval != 0 && healthConfig.Interval < containertypes.MinimumDuration { + return errors.Errorf("Interval in Healthcheck cannot be less than %s", containertypes.MinimumDuration) + } + if healthConfig.Timeout != 0 && healthConfig.Timeout < containertypes.MinimumDuration { + return errors.Errorf("Timeout in Healthcheck cannot be less than %s", containertypes.MinimumDuration) + } + if healthConfig.Retries < 0 { + return errors.Errorf("Retries in Healthcheck cannot be negative") + } + if healthConfig.StartPeriod != 0 && healthConfig.StartPeriod < containertypes.MinimumDuration { + return errors.Errorf("StartPeriod in Healthcheck cannot be less than %s", containertypes.MinimumDuration) + } + return nil +} - for port := range hostConfig.PortBindings { +func validatePortBindings(ports nat.PortMap) error { + for port := range ports { _, portStr := nat.SplitProtoPort(string(port)) if _, err := nat.ParsePort(portStr); err != nil { - return nil, errors.Errorf("invalid port specification: %q", portStr) + return errors.Errorf("invalid port specification: %q", portStr) } - for _, pb := range hostConfig.PortBindings[port] { + for _, pb := range ports[port] { _, err := nat.NewPort(nat.SplitProtoPort(pb.HostPort)) if err != nil { - return nil, errors.Errorf("invalid port specification: %q", pb.HostPort) + return errors.Errorf("invalid port specification: %q", pb.HostPort) } } } + return nil +} - p := hostConfig.RestartPolicy - - switch p.Name { +func validateRestartPolicy(policy containertypes.RestartPolicy) error { + switch policy.Name { case "always", "unless-stopped", "no": - if p.MaximumRetryCount != 0 { - return nil, errors.Errorf("maximum retry count cannot be used with restart policy '%s'", p.Name) + if policy.MaximumRetryCount != 0 { + return errors.Errorf("maximum retry count cannot be used with restart policy '%s'", policy.Name) } case "on-failure": - if p.MaximumRetryCount < 0 { - return nil, errors.Errorf("maximum retry count cannot be negative") + if policy.MaximumRetryCount < 0 { + return errors.Errorf("maximum retry count cannot be negative") } case "": // do nothing + return nil default: - return nil, errors.Errorf("invalid restart policy '%s'", p.Name) - } - - if !hostConfig.Isolation.IsValid() { - return nil, errors.Errorf("invalid isolation '%s' on %s", hostConfig.Isolation, runtime.GOOS) + return errors.Errorf("invalid restart policy '%s'", policy.Name) } + return nil +} - var ( - err error - warnings []string - ) - // Now do platform-specific verification - if warnings, err = verifyPlatformContainerSettings(daemon, hostConfig, config, update); err != nil { - return warnings, err +// translateWorkingDir translates the working-dir for the target platform, +// and returns an error if the given path is not an absolute path. +func translateWorkingDir(config *containertypes.Config) error { + if config.WorkingDir == "" { + return nil } - if hostConfig.NetworkMode.IsHost() && len(hostConfig.PortBindings) > 0 { - warnings = append(warnings, "Published ports are discarded when using host network mode") + wd := filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics + if !system.IsAbs(wd) { + return fmt.Errorf("the working directory '%s' is invalid, it needs to be an absolute path", config.WorkingDir) } - return warnings, err + config.WorkingDir = wd + return nil } diff --git a/daemon/container_linux.go b/daemon/container_linux.go index e6f5bf2cccb96..8d449f700bbe1 100644 --- a/daemon/container_linux.go +++ b/daemon/container_linux.go @@ -1,4 +1,5 @@ -//+build !windows +//go:build !windows +// +build !windows package daemon // import "github.com/docker/docker/daemon" @@ -7,8 +8,8 @@ import ( "github.com/docker/docker/errdefs" ) -func (daemon *Daemon) saveApparmorConfig(container *container.Container) error { - container.AppArmorProfile = "" //we don't care about the previous value. +func (daemon *Daemon) saveAppArmorConfig(container *container.Container) error { + container.AppArmorProfile = "" // we don't care about the previous value. if !daemon.apparmorEnabled { return nil // if apparmor is disabled there is nothing to do here. @@ -20,11 +21,11 @@ func (daemon *Daemon) saveApparmorConfig(container *container.Container) error { if !container.HostConfig.Privileged { if container.AppArmorProfile == "" { - container.AppArmorProfile = defaultApparmorProfile + container.AppArmorProfile = defaultAppArmorProfile } } else { - container.AppArmorProfile = "unconfined" + container.AppArmorProfile = unconfinedAppArmorProfile } return nil } diff --git a/daemon/container_operations.go b/daemon/container_operations.go index 909c7ccb275c6..4c3d37468a864 100644 --- a/daemon/container_operations.go +++ b/daemon/container_operations.go @@ -6,7 +6,6 @@ import ( "net" "os" "path" - "runtime" "strings" "time" @@ -15,15 +14,15 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/daemon/network" "github.com/docker/docker/errdefs" + "github.com/docker/docker/libnetwork" + netconst "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/runconfig" "github.com/docker/go-connections/nat" - "github.com/docker/libnetwork" - netconst "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/options" - "github.com/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -116,6 +115,16 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib return nil, err } parts := strings.SplitN(extraHost, ":", 2) + // If the IP Address is a string called "host-gateway", replace this + // value with the IP address stored in the daemon level HostGatewayIP + // config variable + if parts[1] == opts.HostGatewayName { + gateway := daemon.configStore.HostGatewayIP.String() + if gateway == "" { + return nil, fmt.Errorf("unable to derive the IP value for host-gateway") + } + parts[1] = gateway + } sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1])) } @@ -201,7 +210,14 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib if alias != child.Name[1:] { aliasList = aliasList + " " + child.Name[1:] } - sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.Networks[defaultNetName].IPAddress)) + ipv4 := child.NetworkSettings.Networks[defaultNetName].IPAddress + ipv6 := child.NetworkSettings.Networks[defaultNetName].GlobalIPv6Address + if ipv4 != "" { + sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, ipv4)) + } + if ipv6 != "" { + sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, ipv6)) + } cEndpointID = child.NetworkSettings.Networks[defaultNetName].EndpointID if cEndpointID != "" { childEndpoints = append(childEndpoints, cEndpointID) @@ -238,7 +254,10 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib func (daemon *Daemon) updateNetworkSettings(container *container.Container, n libnetwork.Network, endpointConfig *networktypes.EndpointSettings) error { if container.NetworkSettings == nil { - container.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)} + container.NetworkSettings = &network.Settings{} + } + if container.NetworkSettings.Networks == nil { + container.NetworkSettings.Networks = make(map[string]*network.EndpointSettings) } if !container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() { @@ -323,12 +342,12 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error { return nil } - options, err := daemon.buildSandboxOptions(container) + sbOptions, err := daemon.buildSandboxOptions(container) if err != nil { return fmt.Errorf("Update network failed: %v", err) } - if err := sb.Refresh(options...); err != nil { + if err := sb.Refresh(sbOptions...); err != nil { return fmt.Errorf("Update network failed: Failure in refresh sandbox %s: %v", sid, err) } @@ -355,6 +374,15 @@ func (daemon *Daemon) findAndAttachNetwork(container *container.Container, idOrN if container.Managed || !n.Info().Dynamic() { return n, nil, nil } + // Throw an error if the container is already attached to the network + if container.NetworkSettings.Networks != nil { + networkName := n.Name() + containerName := strings.TrimPrefix(container.Name, "/") + if nw, ok := container.NetworkSettings.Networks[networkName]; ok && nw.EndpointID != "" { + err := fmt.Errorf("%s is already attached to network %s", containerName, networkName) + return n, nil, errdefs.Conflict(err) + } + } } var addresses []string @@ -498,17 +526,19 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai } } -func (daemon *Daemon) allocateNetwork(container *container.Container) error { - start := time.Now() - controller := daemon.netController - +func (daemon *Daemon) allocateNetwork(container *container.Container) (retErr error) { if daemon.netController == nil { return nil } + var ( + start = time.Now() + controller = daemon.netController + ) + // Cleanup any stale sandbox left over due to ungraceful daemon shutdown if err := controller.SandboxDestroy(container.ID); err != nil { - logrus.Errorf("failed to cleanup up stale network sandbox for container %s", container.ID) + logrus.WithError(err).Errorf("failed to cleanup up stale network sandbox for container %s", container.ID) } if container.Config.NetworkDisabled || container.HostConfig.NetworkMode.IsContainer() { @@ -556,17 +586,17 @@ func (daemon *Daemon) allocateNetwork(container *container.Container) error { // create its network sandbox now if not present if len(networks) == 0 { if nil == daemon.getNetworkSandbox(container) { - options, err := daemon.buildSandboxOptions(container) + sbOptions, err := daemon.buildSandboxOptions(container) if err != nil { return err } - sb, err := daemon.netController.NewSandbox(container.ID, options...) + sb, err := daemon.netController.NewSandbox(container.ID, sbOptions...) if err != nil { return err } updateSandboxNetworkSettings(container, sb) defer func() { - if err != nil { + if retErr != nil { sb.Delete() } }() @@ -593,9 +623,9 @@ func (daemon *Daemon) getNetworkSandbox(container *container.Container) libnetwo return sb } -// hasUserDefinedIPAddress returns whether the passed endpoint configuration contains IP address configuration -func hasUserDefinedIPAddress(epConfig *networktypes.EndpointSettings) bool { - return epConfig != nil && epConfig.IPAMConfig != nil && (len(epConfig.IPAMConfig.IPv4Address) > 0 || len(epConfig.IPAMConfig.IPv6Address) > 0) +// hasUserDefinedIPAddress returns whether the passed IPAM configuration contains IP address configuration +func hasUserDefinedIPAddress(ipamConfig *networktypes.EndpointIPAMConfig) bool { + return ipamConfig != nil && (len(ipamConfig.IPv4Address) > 0 || len(ipamConfig.IPv6Address) > 0) } // User specified ip address is acceptable only for networks with user specified subnets. @@ -603,9 +633,18 @@ func validateNetworkingConfig(n libnetwork.Network, epConfig *networktypes.Endpo if n == nil || epConfig == nil { return nil } - if !hasUserDefinedIPAddress(epConfig) { + if !containertypes.NetworkMode(n.Name()).IsUserDefined() { + if hasUserDefinedIPAddress(epConfig.IPAMConfig) && !enableIPOnPredefinedNetwork() { + return runconfig.ErrUnsupportedNetworkAndIP + } + if len(epConfig.Aliases) > 0 && !serviceDiscoveryOnDefaultNetwork() { + return runconfig.ErrUnsupportedNetworkAndAlias + } + } + if !hasUserDefinedIPAddress(epConfig.IPAMConfig) { return nil } + _, _, nwIPv4Configs, nwIPv6Configs := n.Info().IpamConfig() for _, s := range []struct { ipConfigured bool @@ -654,14 +693,7 @@ func cleanOperationalData(es *network.EndpointSettings) { func (daemon *Daemon) updateNetworkConfig(container *container.Container, n libnetwork.Network, endpointConfig *networktypes.EndpointSettings, updateSettings bool) error { - if !containertypes.NetworkMode(n.Name()).IsUserDefined() { - if hasUserDefinedIPAddress(endpointConfig) && !enableIPOnPredefinedNetwork() { - return runconfig.ErrUnsupportedNetworkAndIP - } - if endpointConfig != nil && len(endpointConfig.Aliases) > 0 && !container.EnableServiceDiscoveryOnDefaultNetwork() { - return runconfig.ErrUnsupportedNetworkAndAlias - } - } else { + if containertypes.NetworkMode(n.Name()).IsUserDefined() { addShortID := true shortID := stringid.TruncateID(container.ID) for _, alias := range endpointConfig.Aliases { @@ -673,6 +705,18 @@ func (daemon *Daemon) updateNetworkConfig(container *container.Container, n libn if addShortID { endpointConfig.Aliases = append(endpointConfig.Aliases, shortID) } + if container.Name != container.Config.Hostname { + addHostname := true + for _, alias := range endpointConfig.Aliases { + if alias == container.Config.Hostname { + addHostname = false + break + } + } + if addHostname { + endpointConfig.Aliases = append(endpointConfig.Aliases, container.Config.Hostname) + } + } } if err := validateNetworkingConfig(n, endpointConfig); err != nil { @@ -725,8 +769,7 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName } } - err = daemon.updateNetworkConfig(container, n, endpointConfig, updateSettings) - if err != nil { + if err := daemon.updateNetworkConfig(container, n, endpointConfig, updateSettings); err != nil { return err } @@ -753,20 +796,19 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName EndpointSettings: endpointConfig, IPAMOperational: operIPAM, } - if _, ok := container.NetworkSettings.Networks[n.ID()]; ok { - delete(container.NetworkSettings.Networks, n.ID()) - } + + delete(container.NetworkSettings.Networks, n.ID()) if err := daemon.updateEndpointNetworkSettings(container, n, ep); err != nil { return err } if sb == nil { - options, err := daemon.buildSandboxOptions(container) + sbOptions, err := daemon.buildSandboxOptions(container) if err != nil { return err } - sb, err = controller.NewSandbox(container.ID, options...) + sb, err = controller.NewSandbox(container.ID, sbOptions...) if err != nil { return err } @@ -801,7 +843,7 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName return nil } -func updateJoinInfo(networkSettings *network.Settings, n libnetwork.Network, ep libnetwork.Endpoint) error { // nolint: interfacer +func updateJoinInfo(networkSettings *network.Settings, n libnetwork.Network, ep libnetwork.Endpoint) error { if ep == nil { return errors.New("invalid enppoint whhile building portmap info") } @@ -1040,8 +1082,6 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName EndpointSettings: endpointConfig, } } - } else if !daemon.isNetworkHotPluggable() { - return fmt.Errorf(runtime.GOOS + " does not support connecting a running container to a network") } else { if err := daemon.connectToNetwork(container, idOrName, endpointConfig, true); err != nil { return err @@ -1070,8 +1110,6 @@ func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, netw return fmt.Errorf("container %s is not connected to the network %s", container.ID, networkName) } delete(container.NetworkSettings.Networks, networkName) - } else if err == nil && !daemon.isNetworkHotPluggable() { - return fmt.Errorf(runtime.GOOS + " does not support connecting a running container to a network") } else if err == nil { if container.HostConfig.NetworkMode.IsHost() && containertypes.NetworkMode(n.Type()).IsHost() { return runconfig.ErrConflictHostNetwork @@ -1099,11 +1137,11 @@ func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, netw // ActivateContainerServiceBinding puts this container into load balancer active rotation and DNS response func (daemon *Daemon) ActivateContainerServiceBinding(containerName string) error { - container, err := daemon.GetContainer(containerName) + ctr, err := daemon.GetContainer(containerName) if err != nil { return err } - sb := daemon.getNetworkSandbox(container) + sb := daemon.getNetworkSandbox(ctr) if sb == nil { return fmt.Errorf("network sandbox does not exist for container %s", containerName) } @@ -1112,11 +1150,11 @@ func (daemon *Daemon) ActivateContainerServiceBinding(containerName string) erro // DeactivateContainerServiceBinding removes this container from load balancer active rotation, and DNS response func (daemon *Daemon) DeactivateContainerServiceBinding(containerName string) error { - container, err := daemon.GetContainer(containerName) + ctr, err := daemon.GetContainer(containerName) if err != nil { return err } - sb := daemon.getNetworkSandbox(container) + sb := daemon.getNetworkSandbox(ctr) if sb == nil { // If the network sandbox is not found, then there is nothing to deactivate logrus.Debugf("Could not find network sandbox for container %s on service binding deactivation request", containerName) diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index 9953c7f3fddc5..039a61b1c3047 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -1,24 +1,23 @@ +//go:build linux || freebsd // +build linux freebsd package daemon // import "github.com/docker/docker/daemon" import ( - "context" "fmt" - "io/ioutil" "os" "path/filepath" "strconv" - "time" "github.com/docker/docker/container" "github.com/docker/docker/daemon/links" "github.com/docker/docker/errdefs" + "github.com/docker/docker/libnetwork" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/pkg/system" "github.com/docker/docker/runconfig" - "github.com/docker/libnetwork" + "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -61,33 +60,33 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s func (daemon *Daemon) getIpcContainer(id string) (*container.Container, error) { errMsg := "can't join IPC of container " + id // Check the container exists - container, err := daemon.GetContainer(id) + ctr, err := daemon.GetContainer(id) if err != nil { return nil, errors.Wrap(err, errMsg) } // Check the container is running and not restarting - if err := daemon.checkContainer(container, containerIsRunning, containerIsNotRestarting); err != nil { + if err := daemon.checkContainer(ctr, containerIsRunning, containerIsNotRestarting); err != nil { return nil, errors.Wrap(err, errMsg) } // Check the container ipc is shareable - if st, err := os.Stat(container.ShmPath); err != nil || !st.IsDir() { + if st, err := os.Stat(ctr.ShmPath); err != nil || !st.IsDir() { if err == nil || os.IsNotExist(err) { - return nil, errors.New(errMsg + ": non-shareable IPC") + return nil, errors.New(errMsg + ": non-shareable IPC (hint: use IpcMode:shareable for the donor container)") } // stat() failed? - return nil, errors.Wrap(err, errMsg+": unexpected error from stat "+container.ShmPath) + return nil, errors.Wrap(err, errMsg+": unexpected error from stat "+ctr.ShmPath) } - return container, nil + return ctr, nil } -func (daemon *Daemon) getPidContainer(container *container.Container) (*container.Container, error) { - containerID := container.HostConfig.PidMode.Container() - container, err := daemon.GetContainer(containerID) +func (daemon *Daemon) getPidContainer(ctr *container.Container) (*container.Container, error) { + containerID := ctr.HostConfig.PidMode.Container() + ctr, err := daemon.GetContainer(containerID) if err != nil { return nil, errors.Wrapf(err, "cannot join PID of a non running container: %s", containerID) } - return container, daemon.checkContainer(container, containerIsRunning, containerIsNotRestarting) + return ctr, daemon.checkContainer(ctr, containerIsRunning, containerIsNotRestarting) } func containerIsRunning(c *container.Container) error { @@ -206,7 +205,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { if err != nil { return errors.Wrap(err, "unable to get secret from secret store") } - if err := ioutil.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil { + if err := os.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil { return errors.Wrap(err, "error injecting secret") } @@ -227,14 +226,21 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { } } - for _, ref := range c.ConfigReferences { + for _, configRef := range c.ConfigReferences { // TODO (ehazlett): use type switch when more are supported - if ref.File == nil { - logrus.Error("config target type is not a file target") + if configRef.File == nil { + // Runtime configs are not mounted into the container, but they're + // a valid type of config so we should not error when we encounter + // one. + if configRef.Runtime == nil { + logrus.Error("config target type is not a file or runtime target") + } + // However, in any case, this isn't a file config, so we have no + // further work to do continue } - fPath, err := c.ConfigFilePath(*ref) + fPath, err := c.ConfigFilePath(*configRef) if err != nil { return errors.Wrap(err, "error getting config file path for container") } @@ -243,22 +249,22 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { } logrus.WithFields(logrus.Fields{ - "name": ref.File.Name, + "name": configRef.File.Name, "path": fPath, }).Debug("injecting config") - config, err := c.DependencyStore.Configs().Get(ref.ConfigID) + config, err := c.DependencyStore.Configs().Get(configRef.ConfigID) if err != nil { return errors.Wrap(err, "unable to get config from config store") } - if err := ioutil.WriteFile(fPath, config.Spec.Data, ref.File.Mode); err != nil { + if err := os.WriteFile(fPath, config.Spec.Data, configRef.File.Mode); err != nil { return errors.Wrap(err, "error injecting config") } - uid, err := strconv.Atoi(ref.File.UID) + uid, err := strconv.Atoi(configRef.File.UID) if err != nil { return err } - gid, err := strconv.Atoi(ref.File.GID) + gid, err := strconv.Atoi(configRef.File.GID) if err != nil { return err } @@ -266,7 +272,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { if err := os.Chown(fPath, rootIDs.UID+uid, rootIDs.GID+gid); err != nil { return errors.Wrap(err, "error setting ownership for config") } - if err := os.Chmod(fPath, ref.File.Mode); err != nil { + if err := os.Chmod(fPath, configRef.File.Mode); err != nil { return errors.Wrap(err, "error setting file mode for config") } } @@ -323,36 +329,40 @@ func (daemon *Daemon) cleanupSecretDir(c *container.Container) { if err := mount.RecursiveUnmount(dir); err != nil { logrus.WithField("dir", dir).WithError(err).Warn("Error while attempting to unmount dir, this may prevent removal of container.") } - if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) { + if err := os.RemoveAll(dir); err != nil { logrus.WithField("dir", dir).WithError(err).Error("Error removing dir.") } } -func killProcessDirectly(cntr *container.Container) error { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - // Block until the container to stops or timeout. - status := <-cntr.Wait(ctx, container.WaitConditionNotRunning) - if status.Err() != nil { - // Ensure that we don't kill ourselves - if pid := cntr.GetPID(); pid != 0 { - logrus.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", stringid.TruncateID(cntr.ID)) - if err := unix.Kill(pid, 9); err != nil { - if err != unix.ESRCH { - return err - } - e := errNoSuchProcess{pid, 9} - logrus.Debug(e) - return e - } +func killProcessDirectly(container *container.Container) error { + pid := container.GetPID() + // Ensure that we don't kill ourselves + if pid == 0 { + return nil + } + + if err := unix.Kill(pid, 9); err != nil { + if err != unix.ESRCH { + return err } + e := errNoSuchProcess{pid, 9} + logrus.WithError(e).WithField("container", container.ID).Debug("no such process") + return e } - return nil -} -func detachMounted(path string) error { - return unix.Unmount(path, unix.MNT_DETACH) + // In case there were some exceptions(e.g., state of zombie and D) + if system.IsProcessAlive(pid) { + // Since we can not kill a zombie pid, add zombie check here + isZombie, err := system.IsProcessZombie(pid) + if err != nil { + logrus.WithError(err).WithField("container", container.ID).Warn("Container state is invalid") + return err + } + if isZombie { + return errdefs.System(errors.Errorf("container %s PID %d is zombie and can not be killed. Use the --init option when creating containers to run an init inside the container that forwards signals and reaps processes", stringid.TruncateID(container.ID), pid)) + } + } + return nil } func isLinkable(child *container.Container) bool { @@ -365,19 +375,60 @@ func enableIPOnPredefinedNetwork() bool { return false } -func (daemon *Daemon) isNetworkHotPluggable() bool { - return true +// serviceDiscoveryOnDefaultNetwork indicates if service discovery is supported on the default network +func serviceDiscoveryOnDefaultNetwork() bool { + return false } func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error { var err error - if container.HostConfig.NetworkMode.IsHost() { - // Point to the host files, so that will be copied into the container running in host mode - *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) - *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) - } else { - *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf())) + // Set the correct paths for /etc/hosts and /etc/resolv.conf, based on the + // networking-mode of the container. Note that containers with "container" + // networking are already handled in "initializeNetworking()" before we reach + // this function, so do not have to be accounted for here. + switch { + case container.HostConfig.NetworkMode.IsHost(): + // In host-mode networking, the container does not have its own networking + // namespace, so both `/etc/hosts` and `/etc/resolv.conf` should be the same + // as on the host itself. The container gets a copy of these files. + *sboxOptions = append( + *sboxOptions, + libnetwork.OptionOriginHostsPath("/etc/hosts"), + libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"), + ) + case container.HostConfig.NetworkMode.IsUserDefined(): + // The container uses a user-defined network. We use the embedded DNS + // server for container name resolution and to act as a DNS forwarder + // for external DNS resolution. + // We parse the DNS server(s) that are defined in /etc/resolv.conf on + // the host, which may be a local DNS server (for example, if DNSMasq or + // systemd-resolvd are in use). The embedded DNS server forwards DNS + // resolution to the DNS server configured on the host, which in itself + // may act as a forwarder for external DNS servers. + // If systemd-resolvd is used, the "upstream" DNS servers can be found in + // /run/systemd/resolve/resolv.conf. We do not query those DNS servers + // directly, as they can be dynamically reconfigured. + *sboxOptions = append( + *sboxOptions, + libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"), + ) + default: + // For other situations, such as the default bridge network, container + // discovery / name resolution is handled through /etc/hosts, and no + // embedded DNS server is available. Without the embedded DNS, we + // cannot use local DNS servers on the host (for example, if DNSMasq or + // systemd-resolvd is used). If systemd-resolvd is used, we try to + // determine the external DNS servers that are used on the host. + // This situation is not ideal, because DNS servers configured in the + // container are not updated after the container is created, but the + // DNS servers on the host can be dynamically updated. + // + // Copy the host's resolv.conf for the container (/run/systemd/resolve/resolv.conf or /etc/resolv.conf) + *sboxOptions = append( + *sboxOptions, + libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf()), + ) } container.HostsPath, err = container.GetRootResourcePath("hosts") @@ -407,5 +458,5 @@ func (daemon *Daemon) setupContainerMountsRoot(c *container.Container) error { if err != nil { return err } - return idtools.MkdirAllAndChown(p, 0700, daemon.idMapping.RootPair()) + return idtools.MkdirAllAndChown(p, 0710, idtools.Identity{UID: idtools.CurrentIdentity().UID, GID: daemon.IdentityMapping().RootPair().GID}) } diff --git a/daemon/container_operations_windows.go b/daemon/container_operations_windows.go index 349d3a1566fad..424536f239c1e 100644 --- a/daemon/container_operations_windows.go +++ b/daemon/container_operations_windows.go @@ -2,12 +2,11 @@ package daemon // import "github.com/docker/docker/daemon" import ( "fmt" - "io/ioutil" "os" "github.com/docker/docker/container" + "github.com/docker/docker/libnetwork" "github.com/docker/docker/pkg/system" - "github.com/docker/libnetwork" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -44,11 +43,21 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) { for _, configRef := range c.ConfigReferences { // TODO (ehazlett): use type switch when more are supported if configRef.File == nil { - logrus.Error("config target type is not a file target") + // Runtime configs are not mounted into the container, but they're + // a valid type of config so we should not error when we encounter + // one. + if configRef.Runtime == nil { + logrus.Error("config target type is not a file or runtime target") + } + // However, in any case, this isn't a file config, so we have no + // further work to do continue } - fPath := c.ConfigFilePath(*configRef) + fPath, err := c.ConfigFilePath(*configRef) + if err != nil { + return errors.Wrap(err, "error getting config file path for container") + } log := logrus.WithFields(logrus.Fields{"name": configRef.File.Name, "path": fPath}) log.Debug("injecting config") @@ -56,7 +65,7 @@ func (daemon *Daemon) setupConfigDir(c *container.Container) (setupErr error) { if err != nil { return errors.Wrap(err, "unable to get config from config store") } - if err := ioutil.WriteFile(fPath, config.Spec.Data, configRef.File.Mode); err != nil { + if err := os.WriteFile(fPath, config.Spec.Data, configRef.File.Mode); err != nil { return errors.Wrap(err, "error injecting config") } } @@ -78,10 +87,6 @@ func (daemon *Daemon) mountVolumes(container *container.Container) error { return nil } -func detachMounted(path string) error { - return nil -} - func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { if len(c.SecretReferences) == 0 { return nil @@ -131,7 +136,7 @@ func (daemon *Daemon) setupSecretDir(c *container.Container) (setupErr error) { if err != nil { return errors.Wrap(err, "unable to get secret from secret store") } - if err := ioutil.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil { + if err := os.WriteFile(fPath, secret.Spec.Data, s.File.Mode); err != nil { return errors.Wrap(err, "error injecting secret") } } @@ -151,7 +156,8 @@ func enableIPOnPredefinedNetwork() bool { return true } -func (daemon *Daemon) isNetworkHotPluggable() bool { +// serviceDiscoveryOnDefaultNetwork indicates if service discovery is supported on the default network +func serviceDiscoveryOnDefaultNetwork() bool { return true } diff --git a/daemon/container_unix_test.go b/daemon/container_unix_test.go index b4c5f84c7e794..da85f8f608153 100644 --- a/daemon/container_unix_test.go +++ b/daemon/container_unix_test.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package daemon @@ -9,7 +10,7 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/daemon/config" "github.com/docker/go-connections/nat" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) // TestContainerWarningHostAndPublishPorts that a warning is returned when setting network mode to host and specifying published ports. @@ -24,6 +25,7 @@ func TestContainerWarningHostAndPublishPorts(t *testing.T) { "8080": []nat.PortBinding{{HostPort: "8989"}}, }, warnings: []string{"Published ports are discarded when using host network mode"}}, } + muteLogs() for _, tc := range testCases { hostConfig := &containertypes.HostConfig{ @@ -32,12 +34,10 @@ func TestContainerWarningHostAndPublishPorts(t *testing.T) { PortBindings: tc.ports, } cs := &config.Config{ - CommonUnixConfig: config.CommonUnixConfig{ - Runtimes: map[string]types.Runtime{"runc": {}}, - }, + Runtimes: map[string]types.Runtime{"runc": {}}, } d := &Daemon{configStore: cs} - wrns, err := d.verifyContainerSettings("", hostConfig, &containertypes.Config{}, false) + wrns, err := d.verifyContainerSettings(hostConfig, &containertypes.Config{}, false) assert.NilError(t, err) assert.DeepEqual(t, tc.warnings, wrns) } diff --git a/daemon/container_windows.go b/daemon/container_windows.go index 0ca8039dd6014..fa9ae50f8fb52 100644 --- a/daemon/container_windows.go +++ b/daemon/container_windows.go @@ -4,6 +4,6 @@ import ( "github.com/docker/docker/container" ) -func (daemon *Daemon) saveApparmorConfig(container *container.Container) error { +func (daemon *Daemon) saveAppArmorConfig(container *container.Container) error { return nil } diff --git a/daemon/content.go b/daemon/content.go new file mode 100644 index 0000000000000..01d1e2343d135 --- /dev/null +++ b/daemon/content.go @@ -0,0 +1,30 @@ +package daemon + +import ( + "os" + "path/filepath" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/content/local" + "github.com/containerd/containerd/leases" + "github.com/containerd/containerd/metadata" + "github.com/pkg/errors" + "go.etcd.io/bbolt" +) + +func (d *Daemon) configureLocalContentStore() (content.Store, leases.Manager, error) { + if err := os.MkdirAll(filepath.Join(d.root, "content"), 0700); err != nil { + return nil, nil, errors.Wrap(err, "error creating dir for content store") + } + db, err := bbolt.Open(filepath.Join(d.root, "content", "metadata.db"), 0600, nil) + if err != nil { + return nil, nil, errors.Wrap(err, "error opening bolt db for content metadata store") + } + cs, err := local.NewStore(filepath.Join(d.root, "content", "data")) + if err != nil { + return nil, nil, errors.Wrap(err, "error setting up content store") + } + md := metadata.NewDB(db, cs, nil) + d.mdDB = db + return md.ContentStore(), metadata.NewLeaseManager(md), nil +} diff --git a/daemon/create.go b/daemon/create.go index 1afb1bebea328..70c1ac94ca1cb 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -7,192 +7,201 @@ import ( "strings" "time" + "github.com/containerd/containerd/platforms" "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" networktypes "github.com/docker/docker/api/types/network" "github.com/docker/docker/container" + "github.com/docker/docker/daemon/images" "github.com/docker/docker/errdefs" "github.com/docker/docker/image" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/system" "github.com/docker/docker/runconfig" - "github.com/opencontainers/selinux/go-selinux/label" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/opencontainers/selinux/go-selinux" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) +type createOpts struct { + params types.ContainerCreateConfig + managed bool + ignoreImagesArgsEscaped bool +} + // CreateManagedContainer creates a container that is managed by a Service func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) { - return daemon.containerCreate(params, true) + return daemon.containerCreate(createOpts{ + params: params, + managed: true, + ignoreImagesArgsEscaped: false}) } // ContainerCreate creates a regular container func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) { - return daemon.containerCreate(params, false) + return daemon.containerCreate(createOpts{ + params: params, + managed: false, + ignoreImagesArgsEscaped: false}) +} + +// ContainerCreateIgnoreImagesArgsEscaped creates a regular container. This is called from the builder RUN case +// and ensures that we do not take the images ArgsEscaped +func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) { + return daemon.containerCreate(createOpts{ + params: params, + managed: false, + ignoreImagesArgsEscaped: true}) } -func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (containertypes.ContainerCreateCreatedBody, error) { +func (daemon *Daemon) containerCreate(opts createOpts) (containertypes.ContainerCreateCreatedBody, error) { start := time.Now() - if params.Config == nil { + if opts.params.Config == nil { return containertypes.ContainerCreateCreatedBody{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container")) } - os := runtime.GOOS - if params.Config.Image != "" { - img, err := daemon.imageService.GetImage(params.Config.Image) - if err == nil { - os = img.OS - } - } else { - // This mean scratch. On Windows, we can safely assume that this is a linux - // container. On other platforms, it's the host OS (which it already is) - if runtime.GOOS == "windows" && system.LCOWSupported() { - os = "linux" - } - } - - warnings, err := daemon.verifyContainerSettings(os, params.HostConfig, params.Config, false) + warnings, err := daemon.verifyContainerSettings(opts.params.HostConfig, opts.params.Config, false) if err != nil { return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) } - err = verifyNetworkingConfig(params.NetworkingConfig) + if opts.params.Platform == nil && opts.params.Config.Image != "" { + if img, _ := daemon.imageService.GetImage(opts.params.Config.Image, opts.params.Platform); img != nil { + p := platforms.DefaultSpec() + imgPlat := v1.Platform{ + OS: img.OS, + Architecture: img.Architecture, + Variant: img.Variant, + } + + if !images.OnlyPlatformWithFallback(p).Match(imgPlat) { + warnings = append(warnings, fmt.Sprintf("The requested image's platform (%s) does not match the detected host platform (%s) and no specific platform was requested", platforms.Format(imgPlat), platforms.Format(p))) + } + } + } + + err = verifyNetworkingConfig(opts.params.NetworkingConfig) if err != nil { return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) } - if params.HostConfig == nil { - params.HostConfig = &containertypes.HostConfig{} + if opts.params.HostConfig == nil { + opts.params.HostConfig = &containertypes.HostConfig{} } - err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares) + err = daemon.adaptContainerSettings(opts.params.HostConfig, opts.params.AdjustCPUShares) if err != nil { return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) } - container, err := daemon.create(params, managed) + ctr, err := daemon.create(opts) if err != nil { return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err } containerActions.WithValues("create").UpdateSince(start) - return containertypes.ContainerCreateCreatedBody{ID: container.ID, Warnings: warnings}, nil + if warnings == nil { + warnings = make([]string, 0) // Create an empty slice to avoid https://github.com/moby/moby/issues/38222 + } + + return containertypes.ContainerCreateCreatedBody{ID: ctr.ID, Warnings: warnings}, nil } // Create creates a new container from the given configuration with a given name. -func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (retC *container.Container, retErr error) { +func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr error) { var ( - container *container.Container - img *image.Image - imgID image.ID - err error + ctr *container.Container + img *image.Image + imgID image.ID + err error + os = runtime.GOOS ) - os := runtime.GOOS - if params.Config.Image != "" { - img, err = daemon.imageService.GetImage(params.Config.Image) + if opts.params.Config.Image != "" { + img, err = daemon.imageService.GetImage(opts.params.Config.Image, opts.params.Platform) if err != nil { return nil, err } - if img.OS != "" { - os = img.OS - } else { - // default to the host OS except on Windows with LCOW - if runtime.GOOS == "windows" && system.LCOWSupported() { - os = "linux" - } - } + os = img.OperatingSystem() imgID = img.ID() - - if runtime.GOOS == "windows" && img.OS == "linux" && !system.LCOWSupported() { + if !system.IsOSSupported(os) { return nil, errors.New("operating system on which parent image was created is not Windows") } - } else { - if runtime.GOOS == "windows" { - os = "linux" // 'scratch' case. - } + } else if isWindows { + os = "linux" // 'scratch' case. + } + + // On WCOW, if are not being invoked by the builder to create this container (where + // ignoreImagesArgEscaped will be true) - if the image already has its arguments escaped, + // ensure that this is replicated across to the created container to avoid double-escaping + // of the arguments/command line when the runtime attempts to run the container. + if os == "windows" && !opts.ignoreImagesArgsEscaped && img != nil && img.RunConfig().ArgsEscaped { + opts.params.Config.ArgsEscaped = true } - if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil { + if err := daemon.mergeAndVerifyConfig(opts.params.Config, img); err != nil { return nil, errdefs.InvalidParameter(err) } - if err := daemon.mergeAndVerifyLogConfig(¶ms.HostConfig.LogConfig); err != nil { + if err := daemon.mergeAndVerifyLogConfig(&opts.params.HostConfig.LogConfig); err != nil { return nil, errdefs.InvalidParameter(err) } - if container, err = daemon.newContainer(params.Name, os, params.Config, params.HostConfig, imgID, managed); err != nil { + if ctr, err = daemon.newContainer(opts.params.Name, os, opts.params.Config, opts.params.HostConfig, imgID, opts.managed); err != nil { return nil, err } defer func() { if retErr != nil { - if err := daemon.cleanupContainer(container, true, true); err != nil { + if err := daemon.cleanupContainer(ctr, true, true); err != nil { logrus.Errorf("failed to cleanup container on create error: %v", err) } } }() - if err := daemon.setSecurityOptions(container, params.HostConfig); err != nil { + if err := daemon.setSecurityOptions(ctr, opts.params.HostConfig); err != nil { return nil, err } - container.HostConfig.StorageOpt = params.HostConfig.StorageOpt - - // Fixes: https://github.com/moby/moby/issues/34074 and - // https://github.com/docker/for-win/issues/999. - // Merge the daemon's storage options if they aren't already present. We only - // do this on Windows as there's no effective sandbox size limit other than - // physical on Linux. - if runtime.GOOS == "windows" { - if container.HostConfig.StorageOpt == nil { - container.HostConfig.StorageOpt = make(map[string]string) - } - for _, v := range daemon.configStore.GraphOptions { - opt := strings.SplitN(v, "=", 2) - if _, ok := container.HostConfig.StorageOpt[opt[0]]; !ok { - container.HostConfig.StorageOpt[opt[0]] = opt[1] - } - } - } + ctr.HostConfig.StorageOpt = opts.params.HostConfig.StorageOpt // Set RWLayer for container after mount labels have been set - rwLayer, err := daemon.imageService.CreateLayer(container, setupInitLayer(daemon.idMapping)) + rwLayer, err := daemon.imageService.CreateLayer(ctr, setupInitLayer(daemon.idMapping)) if err != nil { return nil, errdefs.System(err) } - container.RWLayer = rwLayer + ctr.RWLayer = rwLayer - rootIDs := daemon.idMapping.RootPair() - - if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil { + current := idtools.CurrentIdentity() + if err := idtools.MkdirAndChown(ctr.Root, 0710, idtools.Identity{UID: current.UID, GID: daemon.IdentityMapping().RootPair().GID}); err != nil { return nil, err } - if err := idtools.MkdirAndChown(container.CheckpointDir(), 0700, rootIDs); err != nil { + if err := idtools.MkdirAndChown(ctr.CheckpointDir(), 0700, current); err != nil { return nil, err } - if err := daemon.setHostConfig(container, params.HostConfig); err != nil { + if err := daemon.setHostConfig(ctr, opts.params.HostConfig); err != nil { return nil, err } - if err := daemon.createContainerOSSpecificSettings(container, params.Config, params.HostConfig); err != nil { + if err := daemon.createContainerOSSpecificSettings(ctr, opts.params.Config, opts.params.HostConfig); err != nil { return nil, err } var endpointsConfigs map[string]*networktypes.EndpointSettings - if params.NetworkingConfig != nil { - endpointsConfigs = params.NetworkingConfig.EndpointsConfig + if opts.params.NetworkingConfig != nil { + endpointsConfigs = opts.params.NetworkingConfig.EndpointsConfig } // Make sure NetworkMode has an acceptable value. We do this to ensure // backwards API compatibility. - runconfig.SetDefaultNetModeIfBlank(container.HostConfig) + runconfig.SetDefaultNetModeIfBlank(ctr.HostConfig) - daemon.updateContainerNetworkSettings(container, endpointsConfigs) - if err := daemon.Register(container); err != nil { + daemon.updateContainerNetworkSettings(ctr, endpointsConfigs) + if err := daemon.Register(ctr); err != nil { return nil, err } - stateCtr.set(container.ID, "stopped") - daemon.LogContainerEvent(container, "create") - return container, nil + stateCtr.set(ctr.ID, "stopped") + daemon.LogContainerEvent(ctr, "create") + return ctr, nil } func toHostConfigSelinuxLabels(labels []string) []string { @@ -214,7 +223,7 @@ func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig) pidMode := hostConfig.PidMode privileged := hostConfig.Privileged if ipcMode.IsHost() || pidMode.IsHost() || privileged { - return toHostConfigSelinuxLabels(label.DisableSecOpt()), nil + return toHostConfigSelinuxLabels(selinux.DisableSecOpt()), nil } var ipcLabel []string @@ -226,7 +235,10 @@ func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig) if err != nil { return nil, err } - ipcLabel = label.DupSecOpt(c.ProcessLabel) + ipcLabel, err = selinux.DupSecOpt(c.ProcessLabel) + if err != nil { + return nil, err + } if pidContainer == "" { return toHostConfigSelinuxLabels(ipcLabel), err } @@ -237,7 +249,10 @@ func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig) return nil, err } - pidLabel = label.DupSecOpt(c.ProcessLabel) + pidLabel, err = selinux.DupSecOpt(c.ProcessLabel) + if err != nil { + return nil, err + } if ipcContainer == "" { return toHostConfigSelinuxLabels(pidLabel), err } @@ -276,30 +291,31 @@ func verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error { if nwConfig == nil || len(nwConfig.EndpointsConfig) == 0 { return nil } - if len(nwConfig.EndpointsConfig) == 1 { - for k, v := range nwConfig.EndpointsConfig { - if v == nil { - return errdefs.InvalidParameter(errors.Errorf("no EndpointSettings for %s", k)) + if len(nwConfig.EndpointsConfig) > 1 { + l := make([]string, 0, len(nwConfig.EndpointsConfig)) + for k := range nwConfig.EndpointsConfig { + l = append(l, k) + } + return errors.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) + } + + for k, v := range nwConfig.EndpointsConfig { + if v == nil { + return errdefs.InvalidParameter(errors.Errorf("no EndpointSettings for %s", k)) + } + if v.IPAMConfig != nil { + if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil { + return errors.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address) } - if v.IPAMConfig != nil { - if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil { - return errors.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address) - } - if v.IPAMConfig.IPv6Address != "" { - n := net.ParseIP(v.IPAMConfig.IPv6Address) - // if the address is an invalid network address (ParseIP == nil) or if it is - // an IPv4 address (To4() != nil), then it is an invalid IPv6 address - if n == nil || n.To4() != nil { - return errors.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address) - } + if v.IPAMConfig.IPv6Address != "" { + n := net.ParseIP(v.IPAMConfig.IPv6Address) + // if the address is an invalid network address (ParseIP == nil) or if it is + // an IPv4 address (To4() != nil), then it is an invalid IPv6 address + if n == nil || n.To4() != nil { + return errors.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address) } } } - return nil } - l := make([]string, 0, len(nwConfig.EndpointsConfig)) - for k := range nwConfig.EndpointsConfig { - l = append(l, k) - } - return errors.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) + return nil } diff --git a/daemon/create_test.go b/daemon/create_test.go index 3dba847d46b3d..5abe24d2cfb0f 100644 --- a/daemon/create_test.go +++ b/daemon/create_test.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/api/types/network" "github.com/docker/docker/errdefs" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) // Test case for 35752 diff --git a/daemon/create_unix.go b/daemon/create_unix.go index 13857bab069a1..f6f9649eb5f5c 100644 --- a/daemon/create_unix.go +++ b/daemon/create_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" @@ -41,12 +42,14 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con } for spec := range config.Volumes { - name := stringid.GenerateNonCryptoID() + name := stringid.GenerateRandomID() destination := filepath.Clean(spec) // Skip volumes for which we already have something mounted on that // destination because of a --volume-from. - if container.IsDestinationMounted(destination) { + if container.HasMountFor(destination) { + logrus.WithField("container", container.ID).WithField("destination", spec).Debug("mountpoint already exists, skipping anonymous volume") + // Not an error, this could easily have come from the image config. continue } path, err := container.GetResourcePath(destination) diff --git a/daemon/create_windows.go b/daemon/create_windows.go index 37e425a014af7..f47b732fbd3cd 100644 --- a/daemon/create_windows.go +++ b/daemon/create_windows.go @@ -3,7 +3,6 @@ package daemon // import "github.com/docker/docker/daemon" import ( "context" "fmt" - "runtime" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" @@ -14,21 +13,11 @@ import ( // createContainerOSSpecificSettings performs host-OS specific container create functionality func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error { - - if container.OS == runtime.GOOS { + if containertypes.Isolation.IsDefault(hostConfig.Isolation) { // Make sure the host config has the default daemon isolation if not specified by caller. - if containertypes.Isolation.IsDefault(containertypes.Isolation(hostConfig.Isolation)) { - hostConfig.Isolation = daemon.defaultIsolation - } - } else { - // LCOW must be a Hyper-V container as you can't run a shared kernel when one - // is a Windows kernel, the other is a Linux kernel. - if containertypes.Isolation.IsProcess(containertypes.Isolation(hostConfig.Isolation)) { - return fmt.Errorf("process isolation is invalid for Linux containers on Windows") - } - hostConfig.Isolation = "hyperv" + hostConfig.Isolation = daemon.defaultIsolation } - parser := volumemounts.NewParser(container.OS) + parser := volumemounts.NewParser() for spec := range config.Volumes { mp, err := parser.ParseMountRaw(spec, hostConfig.VolumeDriver) @@ -38,7 +27,7 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con // If the mountpoint doesn't have a name, generate one. if len(mp.Name) == 0 { - mp.Name = stringid.GenerateNonCryptoID() + mp.Name = stringid.GenerateRandomID() } // Skip volumes for which we already have something mounted on that diff --git a/daemon/daemon.go b/daemon/daemon.go index 14b31e76677c6..34bcb53768c17 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -8,9 +8,8 @@ package daemon // import "github.com/docker/docker/daemon" import ( "context" "fmt" - "io/ioutil" - "math/rand" "net" + "net/url" "os" "path" "path/filepath" @@ -19,13 +18,11 @@ import ( "sync" "time" - "google.golang.org/grpc" - "github.com/containerd/containerd" "github.com/containerd/containerd/defaults" "github.com/containerd/containerd/pkg/dialer" + "github.com/containerd/containerd/pkg/userns" "github.com/containerd/containerd/remotes/docker" - "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/swarm" @@ -35,26 +32,23 @@ import ( "github.com/docker/docker/daemon/discovery" "github.com/docker/docker/daemon/events" "github.com/docker/docker/daemon/exec" + _ "github.com/docker/docker/daemon/graphdriver/register" // register graph drivers "github.com/docker/docker/daemon/images" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/network" - "github.com/docker/docker/errdefs" - "github.com/moby/buildkit/util/resolver" - "github.com/moby/buildkit/util/tracing" - "github.com/sirupsen/logrus" - // register graph drivers - _ "github.com/docker/docker/daemon/graphdriver/register" "github.com/docker/docker/daemon/stats" dmetadata "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/dockerversion" + "github.com/docker/docker/errdefs" "github.com/docker/docker/image" "github.com/docker/docker/layer" - "github.com/docker/docker/libcontainerd" - "github.com/docker/docker/migrate/v1" + libcontainerdtypes "github.com/docker/docker/libcontainerd/types" + "github.com/docker/docker/libnetwork" + "github.com/docker/docker/libnetwork/cluster" + nwconfig "github.com/docker/docker/libnetwork/config" + "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/truncindex" "github.com/docker/docker/plugin" @@ -63,14 +57,21 @@ import ( "github.com/docker/docker/registry" "github.com/docker/docker/runconfig" volumesservice "github.com/docker/docker/volume/service" - "github.com/docker/libnetwork" - "github.com/docker/libnetwork/cluster" - nwconfig "github.com/docker/libnetwork/config" + "github.com/moby/buildkit/util/resolver" + "github.com/moby/locker" "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "go.etcd.io/bbolt" + "golang.org/x/sync/semaphore" + "golang.org/x/sync/singleflight" + "google.golang.org/grpc" + "google.golang.org/grpc/backoff" ) // ContainersNamespace is the name of the namespace used for users containers -const ContainersNamespace = "moby" +const ( + ContainersNamespace = "moby" +) var ( errSystemNotSupported = errors.New("the Docker daemon is not supported on this platform") @@ -78,34 +79,32 @@ var ( // Daemon holds information about the Docker daemon. type Daemon struct { - ID string - repository string - containers container.Store - containersReplica container.ViewDB - execCommands *exec.Store - imageService *images.ImageService - idIndex *truncindex.TruncIndex - configStore *config.Config - statsCollector *stats.Collector - defaultLogConfig containertypes.LogConfig - RegistryService registry.Service - EventsService *events.Events - netController libnetwork.NetworkController - volumes *volumesservice.VolumesService - discoveryWatcher discovery.Reloader - root string - seccompEnabled bool - apparmorEnabled bool - shutdown bool - idMapping *idtools.IdentityMapping - // TODO: move graphDrivers field to an InfoService - graphDrivers map[string]string // By operating system - - PluginStore *plugin.Store // todo: remove + ID string + repository string + containers container.Store + containersReplica container.ViewDB + execCommands *exec.Store + imageService *images.ImageService + idIndex *truncindex.TruncIndex + configStore *config.Config + statsCollector *stats.Collector + defaultLogConfig containertypes.LogConfig + RegistryService registry.Service + EventsService *events.Events + netController libnetwork.NetworkController + volumes *volumesservice.VolumesService + discoveryWatcher discovery.Reloader + root string + seccompEnabled bool + apparmorEnabled bool + shutdown bool + idMapping *idtools.IdentityMapping + graphDriver string // TODO: move graphDriver field to an InfoService + PluginStore *plugin.Store // TODO: remove pluginManager *plugin.Manager linkIndex *linkIndex containerdCli *containerd.Client - containerd libcontainerd.Client + containerd libcontainerdtypes.Client defaultIsolation containertypes.Isolation // Default isolation mode on Windows clusterProvider cluster.Provider cluster Cluster @@ -117,13 +116,19 @@ type Daemon struct { seccompProfile []byte seccompProfilePath string - diskUsageRunning int32 - pruneRunning int32 - hosts map[string]bool // hosts stores the addresses the daemon is listening on - startupDone chan struct{} + usage singleflight.Group + + pruneRunning int32 + hosts map[string]bool // hosts stores the addresses the daemon is listening on + startupDone chan struct{} attachmentStore network.AttachmentStore attachableNetworkLock *locker.Locker + + // This is used for Windows which doesn't currently support running on containerd + // It stores metadata for the content store (used for manifest caching) + // This needs to be closed on daemon exit + mdDB *bbolt.DB } // StoreHosts stores the addresses the daemon is listening on @@ -146,208 +151,262 @@ func (daemon *Daemon) Features() *map[string]bool { return &daemon.configStore.Features } -// NewResolveOptionsFunc returns a call back function to resolve "registry-mirrors" and -// "insecure-registries" for buildkit -func (daemon *Daemon) NewResolveOptionsFunc() resolver.ResolveOptionsFunc { - return func(ref string) docker.ResolverOptions { - var ( - registryKey = "docker.io" - mirrors = make([]string, len(daemon.configStore.Mirrors)) - m = map[string]resolver.RegistryConf{} - ) - // must trim "https://" or "http://" prefix - for i, v := range daemon.configStore.Mirrors { - v = strings.TrimPrefix(v, "https://") - v = strings.TrimPrefix(v, "http://") - mirrors[i] = v +// RegistryHosts returns registry configuration in containerd resolvers format +func (daemon *Daemon) RegistryHosts() docker.RegistryHosts { + var ( + registryKey = "docker.io" + mirrors = make([]string, len(daemon.configStore.Mirrors)) + m = map[string]resolver.RegistryConfig{} + ) + // must trim "https://" or "http://" prefix + for i, v := range daemon.configStore.Mirrors { + if uri, err := url.Parse(v); err == nil { + v = uri.Host } - // set "registry-mirrors" - m[registryKey] = resolver.RegistryConf{Mirrors: mirrors} - // set "insecure-registries" - for _, v := range daemon.configStore.InsecureRegistries { - v = strings.TrimPrefix(v, "http://") - m[v] = resolver.RegistryConf{ - PlainHTTP: true, + mirrors[i] = v + } + // set mirrors for default registry + m[registryKey] = resolver.RegistryConfig{Mirrors: mirrors} + + for _, v := range daemon.configStore.InsecureRegistries { + u, err := url.Parse(v) + c := resolver.RegistryConfig{} + if err == nil { + v = u.Host + t := true + if u.Scheme == "http" { + c.PlainHTTP = &t + } else { + c.Insecure = &t } } - def := docker.ResolverOptions{ - Client: tracing.DefaultClient, - } - - parsed, err := reference.ParseNormalizedNamed(ref) - if err != nil { - return def - } - host := reference.Domain(parsed) + m[v] = c + } - c, ok := m[host] - if !ok { - return def + for k, v := range m { + if d, err := registry.HostCertsDir(k); err == nil { + v.TLSConfigDir = []string{d} + m[k] = v } + } - if len(c.Mirrors) > 0 { - def.Host = func(string) (string, error) { - return c.Mirrors[rand.Intn(len(c.Mirrors))], nil + certsDir := registry.CertsDir() + if fis, err := os.ReadDir(certsDir); err == nil { + for _, fi := range fis { + if _, ok := m[fi.Name()]; !ok { + m[fi.Name()] = resolver.RegistryConfig{ + TLSConfigDir: []string{filepath.Join(certsDir, fi.Name())}, + } } } - - def.PlainHTTP = c.PlainHTTP - - return def } + + return resolver.NewRegistryConfig(m) } func (daemon *Daemon) restore() error { + var mapLock sync.Mutex containers := make(map[string]*container.Container) logrus.Info("Loading containers: start.") - dir, err := ioutil.ReadDir(daemon.repository) + dir, err := os.ReadDir(daemon.repository) if err != nil { return err } + // parallelLimit is the maximum number of parallel startup jobs that we + // allow (this is the limited used for all startup semaphores). The multipler + // (128) was chosen after some fairly significant benchmarking -- don't change + // it unless you've tested it significantly (this value is adjusted if + // RLIMIT_NOFILE is small to avoid EMFILE). + parallelLimit := adjustParallelLimit(len(dir), 128*runtime.NumCPU()) + + // Re-used for all parallel startup jobs. + var group sync.WaitGroup + sem := semaphore.NewWeighted(int64(parallelLimit)) + for _, v := range dir { - id := v.Name() - container, err := daemon.load(id) - if err != nil { - logrus.Errorf("Failed to load container %v: %v", id, err) - continue - } - if !system.IsOSSupported(container.OS) { - logrus.Errorf("Failed to load container %v: %s (%q)", id, system.ErrNotSupportedOperatingSystem, container.OS) - continue - } - // Ignore the container if it does not support the current driver being used by the graph - currentDriverForContainerOS := daemon.graphDrivers[container.OS] - if (container.Driver == "" && currentDriverForContainerOS == "aufs") || container.Driver == currentDriverForContainerOS { - rwlayer, err := daemon.imageService.GetLayerByID(container.ID, container.OS) + group.Add(1) + go func(id string) { + defer group.Done() + _ = sem.Acquire(context.Background(), 1) + defer sem.Release(1) + + log := logrus.WithField("container", id) + + c, err := daemon.load(id) if err != nil { - logrus.Errorf("Failed to load container mount %v: %v", id, err) - continue + log.WithError(err).Error("failed to load container") + return + } + if !system.IsOSSupported(c.OS) { + log.Errorf("failed to load container: %s (%q)", system.ErrNotSupportedOperatingSystem, c.OS) + return } - container.RWLayer = rwlayer - logrus.Debugf("Loaded container %v, isRunning: %v", container.ID, container.IsRunning()) + // Ignore the container if it does not support the current driver being used by the graph + if (c.Driver == "" && daemon.graphDriver == "aufs") || c.Driver == daemon.graphDriver { + rwlayer, err := daemon.imageService.GetLayerByID(c.ID) + if err != nil { + log.WithError(err).Error("failed to load container mount") + return + } + c.RWLayer = rwlayer + log.WithFields(logrus.Fields{ + "running": c.IsRunning(), + "paused": c.IsPaused(), + }).Debug("loaded container") - containers[container.ID] = container - } else { - logrus.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID) - } + mapLock.Lock() + containers[c.ID] = c + mapLock.Unlock() + } else { + log.Debugf("cannot load container because it was created with another storage driver") + } + }(v.Name()) } + group.Wait() removeContainers := make(map[string]*container.Container) restartContainers := make(map[*container.Container]chan struct{}) activeSandboxes := make(map[string]interface{}) - for id, c := range containers { - if err := daemon.registerName(c); err != nil { - logrus.Errorf("Failed to register container name %s: %s", c.ID, err) - delete(containers, id) - continue - } - if err := daemon.Register(c); err != nil { - logrus.Errorf("Failed to register container %s: %s", c.ID, err) - delete(containers, id) - continue - } - // The LogConfig.Type is empty if the container was created before docker 1.12 with default log driver. - // We should rewrite it to use the daemon defaults. - // Fixes https://github.com/docker/docker/issues/22536 - if c.HostConfig.LogConfig.Type == "" { - if err := daemon.mergeAndVerifyLogConfig(&c.HostConfig.LogConfig); err != nil { - logrus.Errorf("Failed to verify log config for container %s: %q", c.ID, err) - continue + for _, c := range containers { + group.Add(1) + go func(c *container.Container) { + defer group.Done() + _ = sem.Acquire(context.Background(), 1) + defer sem.Release(1) + + log := logrus.WithField("container", c.ID) + + if err := daemon.registerName(c); err != nil { + log.WithError(err).Errorf("failed to register container name: %s", c.Name) + mapLock.Lock() + delete(containers, c.ID) + mapLock.Unlock() + return } - } + if err := daemon.Register(c); err != nil { + log.WithError(err).Error("failed to register container") + mapLock.Lock() + delete(containers, c.ID) + mapLock.Unlock() + return + } + }(c) } + group.Wait() - var ( - wg sync.WaitGroup - mapLock sync.Mutex - ) for _, c := range containers { - wg.Add(1) + group.Add(1) go func(c *container.Container) { - defer wg.Done() + defer group.Done() + _ = sem.Acquire(context.Background(), 1) + defer sem.Release(1) + + log := logrus.WithField("container", c.ID) + daemon.backportMountSpec(c) if err := daemon.checkpointAndSave(c); err != nil { - logrus.WithError(err).WithField("container", c.ID).Error("error saving backported mountspec to disk") + log.WithError(err).Error("error saving backported mountspec to disk") } daemon.setStateCounter(c) - logrus.WithFields(logrus.Fields{ - "container": c.ID, - "running": c.IsRunning(), - "paused": c.IsPaused(), - }).Debug("restoring container") + logger := func(c *container.Container) *logrus.Entry { + return log.WithFields(logrus.Fields{ + "running": c.IsRunning(), + "paused": c.IsPaused(), + "restarting": c.IsRestarting(), + }) + } + + logger(c).Debug("restoring container") var ( err error alive bool ec uint32 exitedAt time.Time + process libcontainerdtypes.Process ) - alive, _, err = daemon.containerd.Restore(context.Background(), c.ID, c.InitializeStdio) + alive, _, process, err = daemon.containerd.Restore(context.Background(), c.ID, c.InitializeStdio) if err != nil && !errdefs.IsNotFound(err) { - logrus.Errorf("Failed to restore container %s with containerd: %s", c.ID, err) + logger(c).WithError(err).Error("failed to restore container with containerd") return } + logger(c).Debugf("alive: %v", alive) if !alive { - ec, exitedAt, err = daemon.containerd.DeleteTask(context.Background(), c.ID) - if err != nil && !errdefs.IsNotFound(err) { - logrus.WithError(err).Errorf("Failed to delete container %s from containerd", c.ID) - return + // If process is not nil, cleanup dead container from containerd. + // If process is nil then the above `containerd.Restore` returned an errdefs.NotFoundError, + // and docker's view of the container state will be updated accorrdingly via SetStopped further down. + if process != nil { + logger(c).Debug("cleaning up dead container process") + ec, exitedAt, err = process.Delete(context.Background()) + if err != nil && !errdefs.IsNotFound(err) { + logger(c).WithError(err).Error("failed to delete container from containerd") + return + } } } else if !daemon.configStore.LiveRestoreEnabled { - if err := daemon.kill(c, c.StopSignal()); err != nil && !errdefs.IsNotFound(err) { - logrus.WithError(err).WithField("container", c.ID).Error("error shutting down container") + logger(c).Debug("shutting down container considered alive by containerd") + if err := daemon.shutdownContainer(c); err != nil && !errdefs.IsNotFound(err) { + log.WithError(err).Error("error shutting down container") return } + c.ResetRestartManager(false) } if c.IsRunning() || c.IsPaused() { + logger(c).Debug("syncing container on disk state with real state") + c.RestartManager().Cancel() // manually start containers because some need to wait for swarm networking - if c.IsPaused() && alive { + switch { + case c.IsPaused() && alive: s, err := daemon.containerd.Status(context.Background(), c.ID) if err != nil { - logrus.WithError(err).WithField("container", c.ID). - Errorf("Failed to get container status") + logger(c).WithError(err).Error("failed to get container status") } else { - logrus.WithField("container", c.ID).WithField("state", s). - Info("restored container paused") + logger(c).WithField("state", s).Info("restored container paused") switch s { - case libcontainerd.StatusPaused, libcontainerd.StatusPausing: + case containerd.Paused, containerd.Pausing: // nothing to do - case libcontainerd.StatusStopped: + case containerd.Stopped: alive = false - case libcontainerd.StatusUnknown: - logrus.WithField("container", c.ID). - Error("Unknown status for container during restore") + case containerd.Unknown: + log.Error("unknown status for paused container during restore") default: // running c.Lock() c.Paused = false daemon.setStateCounter(c) + daemon.updateHealthMonitor(c) if err := c.CheckpointTo(daemon.containersReplica); err != nil { - logrus.WithError(err).WithField("container", c.ID). - Error("Failed to update stopped container state") + log.WithError(err).Error("failed to update paused container state") } c.Unlock() } } + case !c.IsPaused() && alive: + logger(c).Debug("restoring healthcheck") + c.Lock() + daemon.updateHealthMonitor(c) + c.Unlock() } if !alive { + logger(c).Debug("setting stopped state") c.Lock() c.SetStopped(&container.ExitStatus{ExitCode: int(ec), ExitedAt: exitedAt}) daemon.Cleanup(c) if err := c.CheckpointTo(daemon.containersReplica); err != nil { - logrus.Errorf("Failed to update stopped container %s state: %v", c.ID, err) + log.WithError(err).Error("failed to update stopped container state") } c.Unlock() + logger(c).Debug("set stopped state") } // we call Mount and then Unmount to get BaseFs of the container @@ -358,10 +417,10 @@ func (daemon *Daemon) restore() error { // stopped/restarted/removed. // See #29365 for related information. // The error is only logged here. - logrus.Warnf("Failed to mount container on getting BaseFs path %v: %v", c.ID, err) + logger(c).WithError(err).Warn("failed to mount container to get BaseFs path") } else { if err := daemon.Unmount(c); err != nil { - logrus.Warnf("Failed to umount container on getting BaseFs path %v: %v", c.ID, err) + logger(c).WithError(err).Warn("failed to umount container to get BaseFs path") } } @@ -369,7 +428,7 @@ func (daemon *Daemon) restore() error { if !c.HostConfig.NetworkMode.IsContainer() && c.IsRunning() { options, err := daemon.buildSandboxOptions(c) if err != nil { - logrus.Warnf("Failed build sandbox option to restore container %s: %v", c.ID, err) + logger(c).WithError(err).Warn("failed to build sandbox option to restore container") } mapLock.Lock() activeSandboxes[c.NetworkSettings.SandboxID] = options @@ -405,17 +464,20 @@ func (daemon *Daemon) restore() error { // associated volumes, network links or both to also // be removed. So we put the container in the "dead" // state and leave further processing up to them. - logrus.Debugf("Resetting RemovalInProgress flag from %v", c.ID) c.RemovalInProgress = false c.Dead = true if err := c.CheckpointTo(daemon.containersReplica); err != nil { - logrus.Errorf("Failed to update RemovalInProgress container %s state: %v", c.ID, err) + log.WithError(err).Error("failed to update RemovalInProgress container state") + } else { + log.Debugf("reset RemovalInProgress state for container") } } c.Unlock() + logger(c).Debug("done restoring container") }(c) } - wg.Wait() + group.Wait() + daemon.netController, err = daemon.initNetworkController(daemon.configStore, activeSandboxes) if err != nil { return fmt.Errorf("Error initializing network controller: %v", err) @@ -423,29 +485,40 @@ func (daemon *Daemon) restore() error { // Now that all the containers are registered, register the links for _, c := range containers { - if err := daemon.registerLinks(c, c.HostConfig); err != nil { - logrus.Errorf("failed to register link for container %s: %v", c.ID, err) - } + group.Add(1) + go func(c *container.Container) { + _ = sem.Acquire(context.Background(), 1) + + if err := daemon.registerLinks(c, c.HostConfig); err != nil { + logrus.WithField("container", c.ID).WithError(err).Error("failed to register link for container") + } + + sem.Release(1) + group.Done() + }(c) } + group.Wait() - group := sync.WaitGroup{} for c, notifier := range restartContainers { group.Add(1) - go func(c *container.Container, chNotify chan struct{}) { - defer group.Done() + _ = sem.Acquire(context.Background(), 1) + + log := logrus.WithField("container", c.ID) - logrus.Debugf("Starting container %s", c.ID) + log.Debug("starting container") // ignore errors here as this is a best effort to wait for children to be // running before we try to start the container children := daemon.children(c) - timeout := time.After(5 * time.Second) + timeout := time.NewTimer(5 * time.Second) + defer timeout.Stop() + for _, child := range children { if notifier, exists := restartContainers[child]; exists { select { case <-notifier: - case <-timeout: + case <-timeout.C: } } } @@ -453,25 +526,30 @@ func (daemon *Daemon) restore() error { // Make sure networks are available before starting daemon.waitForNetworks(c) if err := daemon.containerStart(c, "", "", true); err != nil { - logrus.Errorf("Failed to start container %s: %s", c.ID, err) + log.WithError(err).Error("failed to start container") } close(chNotify) - }(c, notifier) + sem.Release(1) + group.Done() + }(c, notifier) } group.Wait() - removeGroup := sync.WaitGroup{} for id := range removeContainers { - removeGroup.Add(1) + group.Add(1) go func(cid string) { + _ = sem.Acquire(context.Background(), 1) + if err := daemon.ContainerRm(cid, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil { - logrus.Errorf("Failed to remove container %s: %s", cid, err) + logrus.WithField("container", cid).WithError(err).Error("failed to remove container") } - removeGroup.Done() + + sem.Release(1) + group.Done() }(id) } - removeGroup.Wait() + group.Wait() // any containers that were started above would already have had this done, // however we need to now prepare the mountpoints for the rest of the containers as well. @@ -492,13 +570,16 @@ func (daemon *Daemon) restore() error { group.Add(1) go func(c *container.Container) { - defer group.Done() + _ = sem.Acquire(context.Background(), 1) + if err := daemon.prepareMountPoints(c); err != nil { - logrus.Error(err) + logrus.WithField("container", c.ID).WithError(err).Error("failed to prepare mountpoints for container") } + + sem.Release(1) + group.Done() }(c) } - group.Wait() logrus.Info("Loading containers: done.") @@ -509,7 +590,18 @@ func (daemon *Daemon) restore() error { // RestartSwarmContainers restarts any autostart container which has a // swarm endpoint. func (daemon *Daemon) RestartSwarmContainers() { - group := sync.WaitGroup{} + ctx := context.Background() + + // parallelLimit is the maximum number of parallel startup jobs that we + // allow (this is the limited used for all startup semaphores). The multipler + // (128) was chosen after some fairly significant benchmarking -- don't change + // it unless you've tested it significantly (this value is adjusted if + // RLIMIT_NOFILE is small to avoid EMFILE). + parallelLimit := adjustParallelLimit(len(daemon.List()), 128*runtime.NumCPU()) + + var group sync.WaitGroup + sem := semaphore.NewWeighted(int64(parallelLimit)) + for _, c := range daemon.List() { if !c.IsRunning() && !c.IsPaused() { // Autostart all the containers which has a @@ -518,14 +610,21 @@ func (daemon *Daemon) RestartSwarmContainers() { if daemon.configStore.AutoRestart && c.ShouldRestart() && c.NetworkSettings.HasSwarmEndpoint && c.HasBeenStartedBefore { group.Add(1) go func(c *container.Container) { - defer group.Done() + if err := sem.Acquire(ctx, 1); err != nil { + // ctx is done. + group.Done() + return + } + if err := daemon.containerStart(c, "", "", true); err != nil { - logrus.Error(err) + logrus.WithField("container", c.ID).WithError(err).Error("failed to start swarm container") } + + sem.Release(1) + group.Done() }(c) } } - } group.Wait() } @@ -537,6 +636,7 @@ func (daemon *Daemon) waitForNetworks(c *container.Container) { if daemon.discoveryWatcher == nil { return } + // Make sure if the container has a network that requires discovery that the discovery service is available before starting for netName := range c.NetworkSettings.Networks { // If we get `ErrNoSuchNetwork` here, we can assume that it is due to discovery not being ready @@ -545,13 +645,19 @@ func (daemon *Daemon) waitForNetworks(c *container.Container) { if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok { continue } + // use a longish timeout here due to some slowdowns in libnetwork if the k/v store is on anything other than --net=host // FIXME: why is this slow??? - logrus.Debugf("Container %s waiting for network to be ready", c.Name) + dur := 60 * time.Second + timer := time.NewTimer(dur) + + logrus.WithField("container", c.ID).Debugf("Container %s waiting for network to be ready", c.Name) select { case <-daemon.discoveryWatcher.ReadyCh(): - case <-time.After(60 * time.Second): + case <-timer.C: } + timer.Stop() + return } } @@ -601,10 +707,14 @@ func (daemon *Daemon) DaemonLeavesCluster() { // This is called also on graceful daemon shutdown. We need to // wait, because the ingress release has to happen before the // network controller is stopped. + if done, err := daemon.ReleaseIngress(); err == nil { + timeout := time.NewTimer(5 * time.Second) + defer timeout.Stop() + select { case <-done: - case <-time.After(5 * time.Second): + case <-timeout.C: logrus.Warn("timeout while waiting for ingress network removal") } } else { @@ -641,7 +751,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S } // Ensure that we have a correct root key limit for launching containers. - if err := ModifyRootKeyLimit(); err != nil { + if err := modifyRootKeyLimit(); err != nil { logrus.Warnf("unable to modify root key limit, number of containers could be limited by this quota: %v", err) } @@ -676,17 +786,17 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S } // set up the tmpDir to use a canonical path - tmp, err := prepareTempDir(config.Root, rootIDs) + tmp, err := prepareTempDir(config.Root) if err != nil { return nil, fmt.Errorf("Unable to get the TempDir under %s: %s", config.Root, err) } - realTmp, err := getRealPath(tmp) + realTmp, err := fileutils.ReadSymlinkedDirectory(tmp) if err != nil { return nil, fmt.Errorf("Unable to get the full path to the TempDir (%s): %s", tmp, err) } - if runtime.GOOS == "windows" { + if isWindows { if _, err := os.Stat(realTmp); err != nil && os.IsNotExist(err) { - if err := system.MkdirAll(realTmp, 0700, ""); err != nil { + if err := system.MkdirAll(realTmp, 0700); err != nil { return nil, fmt.Errorf("Unable to create the TempDir (%s): %s", realTmp, err) } } @@ -701,6 +811,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S PluginStore: pluginStore, startupDone: make(chan struct{}), } + // Ensure the daemon is properly shutdown if there is a failure during // initialization defer func() { @@ -735,52 +846,48 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S logrus.Warnf("Failed to configure golang's threads limit: %v", err) } + // ensureDefaultAppArmorProfile does nothing if apparmor is disabled if err := ensureDefaultAppArmorProfile(); err != nil { logrus.Errorf(err.Error()) } daemonRepo := filepath.Join(config.Root, "containers") - if err := idtools.MkdirAllAndChown(daemonRepo, 0700, rootIDs); err != nil { + if err := idtools.MkdirAllAndChown(daemonRepo, 0710, idtools.Identity{ + UID: idtools.CurrentIdentity().UID, + GID: rootIDs.GID, + }); err != nil { return nil, err } // Create the directory where we'll store the runtime scripts (i.e. in // order to support runtimeArgs) daemonRuntimes := filepath.Join(config.Root, "runtimes") - if err := system.MkdirAll(daemonRuntimes, 0700, ""); err != nil { + if err := system.MkdirAll(daemonRuntimes, 0700); err != nil { return nil, err } if err := d.loadRuntimes(); err != nil { return nil, err } - if runtime.GOOS == "windows" { - if err := system.MkdirAll(filepath.Join(config.Root, "credentialspecs"), 0, ""); err != nil { + if isWindows { + if err := system.MkdirAll(filepath.Join(config.Root, "credentialspecs"), 0); err != nil { return nil, err } } - // On Windows we don't support the environment variable, or a user supplied graphdriver - // as Windows has no choice in terms of which graphdrivers to use. It's a case of - // running Windows containers on Windows - windowsfilter, running Linux containers on Windows, - // lcow. Unix platforms however run a single graphdriver for all containers, and it can - // be set through an environment variable, a daemon start parameter, or chosen through - // initialization of the layerstore through driver priority order for example. - d.graphDrivers = make(map[string]string) - layerStores := make(map[string]layer.Store) - if runtime.GOOS == "windows" { - d.graphDrivers[runtime.GOOS] = "windowsfilter" - if system.LCOWSupported() { - d.graphDrivers["linux"] = "lcow" - } + if isWindows { + // On Windows we don't support the environment variable, or a user supplied graphdriver + d.graphDriver = "windowsfilter" } else { - driverName := os.Getenv("DOCKER_DRIVER") - if driverName == "" { - driverName = config.GraphDriver + // Unix platforms however run a single graphdriver for all containers, and it can + // be set through an environment variable, a daemon start parameter, or chosen through + // initialization of the layerstore through driver priority order for example. + if drv := os.Getenv("DOCKER_DRIVER"); drv != "" { + d.graphDriver = drv + logrus.Infof("Setting the storage driver from the $DOCKER_DRIVER environment variable (%s)", drv) } else { - logrus.Infof("Setting the storage driver from the $DOCKER_DRIVER environment variable (%s)", driverName) + d.graphDriver = config.GraphDriver // May still be empty. Layerstore init determines instead. } - d.graphDrivers[runtime.GOOS] = driverName // May still be empty. Layerstore init determines instead. } d.RegistryService = registryService @@ -792,17 +899,41 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S } registerMetricsPluginCallback(d.PluginStore, metricsSockPath) + backoffConfig := backoff.DefaultConfig + backoffConfig.MaxDelay = 3 * time.Second + connParams := grpc.ConnectParams{ + Backoff: backoffConfig, + } gopts := []grpc.DialOption{ + // WithBlock makes sure that the following containerd request + // is reliable. + // + // NOTE: In one edge case with high load pressure, kernel kills + // dockerd, containerd and containerd-shims caused by OOM. + // When both dockerd and containerd restart, but containerd + // will take time to recover all the existing containers. Before + // containerd serving, dockerd will failed with gRPC error. + // That bad thing is that restore action will still ignore the + // any non-NotFound errors and returns running state for + // already stopped container. It is unexpected behavior. And + // we need to restart dockerd to make sure that anything is OK. + // + // It is painful. Add WithBlock can prevent the edge case. And + // n common case, the containerd will be serving in shortly. + // It is not harm to add WithBlock for containerd connection. + grpc.WithBlock(), + grpc.WithInsecure(), - grpc.WithBackoffMaxDelay(3 * time.Second), - grpc.WithDialer(dialer.Dialer), + grpc.WithConnectParams(connParams), + grpc.WithContextDialer(dialer.ContextDialer), // TODO(stevvooe): We may need to allow configuration of this on the client. grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)), grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)), } + if config.ContainerdAddr != "" { - d.containerdCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(ContainersNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second)) + d.containerdCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(config.ContainerdNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second)) if err != nil { return nil, errors.Wrapf(err, "failed to dial %q", config.ContainerdAddr) } @@ -811,16 +942,22 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S createPluginExec := func(m *plugin.Manager) (plugin.Executor, error) { var pluginCli *containerd.Client - // Windows is not currently using containerd, keep the - // client as nil if config.ContainerdAddr != "" { - pluginCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(pluginexec.PluginNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second)) + pluginCli, err = containerd.New(config.ContainerdAddr, containerd.WithDefaultNamespace(config.ContainerdPluginNamespace), containerd.WithDialOpts(gopts), containerd.WithTimeout(60*time.Second)) if err != nil { return nil, errors.Wrapf(err, "failed to dial %q", config.ContainerdAddr) } } - return pluginexec.New(ctx, getPluginExecRoot(config.Root), pluginCli, m) + var rt types.Runtime + if runtime.GOOS != "windows" { + rtPtr, err := d.getRuntime(config.GetDefaultRuntimeName()) + if err != nil { + return nil, err + } + rt = *rtPtr + } + return pluginexec.New(ctx, getPluginExecRoot(config.Root), pluginCli, config.ContainerdPluginNamespace, m, rt) } // Plugin system initialization should happen before restore. Do not change order. @@ -842,44 +979,36 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S return nil, err } - for operatingSystem, gd := range d.graphDrivers { - layerStores[operatingSystem], err = layer.NewStoreFromOptions(layer.StoreOptions{ - Root: config.Root, - MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"), - GraphDriver: gd, - GraphDriverOptions: config.GraphOptions, - IDMapping: idMapping, - PluginGetter: d.PluginStore, - ExperimentalEnabled: config.Experimental, - OS: operatingSystem, - }) - if err != nil { - return nil, err - } + layerStore, err := layer.NewStoreFromOptions(layer.StoreOptions{ + Root: config.Root, + MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"), + GraphDriver: d.graphDriver, + GraphDriverOptions: config.GraphOptions, + IDMapping: idMapping, + PluginGetter: d.PluginStore, + ExperimentalEnabled: config.Experimental, + OS: runtime.GOOS, + }) + if err != nil { + return nil, err } // As layerstore initialization may set the driver - for os := range d.graphDrivers { - d.graphDrivers[os] = layerStores[os].DriverName() - } + d.graphDriver = layerStore.DriverName() // Configure and validate the kernels security support. Note this is a Linux/FreeBSD // operation only, so it is safe to pass *just* the runtime OS graphdriver. - if err := configureKernelSecuritySupport(config, d.graphDrivers[runtime.GOOS]); err != nil { + if err := configureKernelSecuritySupport(config, d.graphDriver); err != nil { return nil, err } - imageRoot := filepath.Join(config.Root, "image", d.graphDrivers[runtime.GOOS]) + imageRoot := filepath.Join(config.Root, "image", d.graphDriver) ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb")) if err != nil { return nil, err } - lgrMap := make(map[string]image.LayerGetReleaser) - for os, ls := range layerStores { - lgrMap[os] = ls - } - imageStore, err := image.NewImageStore(ifs, lgrMap) + imageStore, err := image.NewImageStore(ifs, layerStore) if err != nil { return nil, err } @@ -896,7 +1025,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S trustDir := filepath.Join(config.Root, "trust") - if err := system.MkdirAll(trustDir, 0700, ""); err != nil { + if err := system.MkdirAll(trustDir, 0700); err != nil { return nil, err } @@ -921,25 +1050,19 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S return nil, err } - // No content-addressability migration on Windows as it never supported pre-CA - if runtime.GOOS != "windows" { - migrationStart := time.Now() - if err := v1.Migrate(config.Root, d.graphDrivers[runtime.GOOS], layerStores[runtime.GOOS], imageStore, rs, distributionMetadataStore); err != nil { - logrus.Errorf("Graph migration failed: %q. Your old graph data was found to be too inconsistent for upgrading to content-addressable storage. Some of the old data was probably not upgraded. We recommend starting over with a clean storage directory if possible.", err) - } - logrus.Infof("Graph migration to content-addressability took %.2f seconds", time.Since(migrationStart).Seconds()) - } - // Discovery is only enabled when the daemon is launched with an address to advertise. When // initialized, the daemon is registered and we can store the discovery backend as it's read-only if err := d.initDiscovery(config); err != nil { return nil, err } - sysInfo := sysinfo.New(false) + sysInfo := d.RawSysInfo() + for _, w := range sysInfo.Warnings { + logrus.Warn(w) + } // Check if Devices cgroup is mounted, it is hard requirement for container security, // on Linux. - if runtime.GOOS == "linux" && !sysInfo.CgroupDevicesEnabled { + if runtime.GOOS == "linux" && !sysInfo.CgroupDevicesEnabled && !userns.RunningInUserNS() { return nil, errors.New("Devices cgroup isn't mounted") } @@ -961,26 +1084,44 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S d.linkIndex = newLinkIndex() - // TODO: imageStore, distributionMetadataStore, and ReferenceStore are only - // used above to run migration. They could be initialized in ImageService - // if migration is called from daemon/images. layerStore might move as well. - d.imageService = images.NewImageService(images.ImageServiceConfig{ + imgSvcConfig := images.ImageServiceConfig{ ContainerStore: d.containers, DistributionMetadataStore: distributionMetadataStore, EventsService: d.EventsService, ImageStore: imageStore, - LayerStores: layerStores, + LayerStore: layerStore, MaxConcurrentDownloads: *config.MaxConcurrentDownloads, MaxConcurrentUploads: *config.MaxConcurrentUploads, + MaxDownloadAttempts: *config.MaxDownloadAttempts, ReferenceStore: rs, RegistryService: registryService, TrustKey: trustKey, - }) + ContentNamespace: config.ContainerdNamespace, + } + + // containerd is not currently supported with Windows. + // So sometimes d.containerdCli will be nil + // In that case we'll create a local content store... but otherwise we'll use containerd + if d.containerdCli != nil { + imgSvcConfig.Leases = d.containerdCli.LeasesService() + imgSvcConfig.ContentStore = d.containerdCli.ContentStore() + } else { + cs, lm, err := d.configureLocalContentStore() + if err != nil { + return nil, err + } + imgSvcConfig.ContentStore = cs + imgSvcConfig.Leases = lm + } + + // TODO: imageStore, distributionMetadataStore, and ReferenceStore are only + // used above to run migration. They could be initialized in ImageService + // if migration is called from daemon/images. layerStore might move as well. + d.imageService = images.NewImageService(imgSvcConfig) go d.execCommandGC() - d.containerd, err = libcontainerd.NewClient(ctx, d.containerdCli, filepath.Join(config.ExecRoot, "containerd"), ContainersNamespace, d) - if err != nil { + if err := d.initLibcontainerd(ctx); err != nil { return nil, err } @@ -989,8 +1130,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S } close(d.startupDone) - // FIXME: this method never returns an error - info, _ := d.SystemInfo() + info := d.SystemInfo() engineInfo.WithValues( dockerversion.Version, @@ -1000,25 +1140,16 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S info.KernelVersion, info.OperatingSystem, info.OSType, + info.OSVersion, info.ID, ).Set(1) engineCpus.Set(float64(info.NCPU)) engineMemory.Set(float64(info.MemTotal)) - gd := "" - for os, driver := range d.graphDrivers { - if len(gd) > 0 { - gd += ", " - } - gd += driver - if len(d.graphDrivers) > 1 { - gd = fmt.Sprintf("%s (%s)", gd, os) - } - } logrus.WithFields(logrus.Fields{ - "version": dockerversion.Version, - "commit": dockerversion.GitCommit, - "graphdriver(s)": gd, + "version": dockerversion.Version, + "commit": dockerversion.GitCommit, + "graphdriver": d.graphDriver, }).Info("Docker daemon") return d, nil @@ -1098,15 +1229,16 @@ func (daemon *Daemon) Shutdown() error { if !c.IsRunning() { return } - logrus.Debugf("stopping %s", c.ID) + log := logrus.WithField("container", c.ID) + log.Debug("shutting down container") if err := daemon.shutdownContainer(c); err != nil { - logrus.Errorf("Stop container error: %v", err) + log.WithError(err).Error("failed to shut down container") return } - if mountid, err := daemon.imageService.GetLayerMountID(c.ID, c.OS); err == nil { + if mountid, err := daemon.imageService.GetLayerMountID(c.ID); err == nil { daemon.cleanupMountsByID(mountid) } - logrus.Debugf("container stopped %s", c.ID) + log.Debugf("shut down container") }) } @@ -1140,6 +1272,10 @@ func (daemon *Daemon) Shutdown() error { daemon.containerdCli.Close() } + if daemon.mdDB != nil { + daemon.mdDB.Close() + } + return daemon.cleanupMounts() } @@ -1153,7 +1289,7 @@ func (daemon *Daemon) Mount(container *container.Container) error { if err != nil { return err } - logrus.Debugf("container mounted via layerStore: %v", dir) + logrus.WithField("container", container.ID).Debugf("container mounted via layerStore: %v", dir) if container.BaseFS != nil && container.BaseFS.Path() != dir.Path() { // The mount path reported by the graph driver should always be trusted on Windows, since the @@ -1162,7 +1298,7 @@ func (daemon *Daemon) Mount(container *container.Container) error { if runtime.GOOS != "windows" { daemon.Unmount(container) return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')", - daemon.imageService.GraphDriverForOS(container.OS), container.ID, container.BaseFS, dir) + daemon.imageService.GraphDriverName(), container.ID, container.BaseFS, dir) } } container.BaseFS = dir // TODO: combine these fields @@ -1175,7 +1311,7 @@ func (daemon *Daemon) Unmount(container *container.Container) error { return errors.New("RWLayer of container " + container.ID + " is unexpectedly nil") } if err := container.RWLayer.Unmount(); err != nil { - logrus.Errorf("Error unmounting container %s: %s", container.ID, err) + logrus.WithField("container", container.ID).WithError(err).Error("error unmounting container") return err } @@ -1209,7 +1345,7 @@ func (daemon *Daemon) Subnets() ([]net.IPNet, []net.IPNet) { // prepareTempDir prepares and returns the default directory to use // for temporary files. // If it doesn't exist, it is created. If it exists, its content is removed. -func prepareTempDir(rootDir string, rootIdentity idtools.Identity) (string, error) { +func prepareTempDir(rootDir string) (string, error) { var tmpDir string if tmpDir = os.Getenv("DOCKER_TMPDIR"); tmpDir == "" { tmpDir = filepath.Join(rootDir, "tmp") @@ -1227,9 +1363,7 @@ func prepareTempDir(rootDir string, rootIdentity idtools.Identity) (string, erro } } } - // We don't remove the content of tmpdir if it's not the default, - // it may hold things that do not belong to us. - return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, rootIdentity) + return tmpDir, idtools.MkdirAllAndChown(tmpDir, 0700, idtools.CurrentIdentity()) } func (daemon *Daemon) setGenericResources(conf *config.Config) error { @@ -1316,7 +1450,7 @@ func (daemon *Daemon) networkOptions(dconfig *config.Config, pg plugingetter.Plu } options = append(options, nwconfig.OptionLabels(dconfig.Labels)) - options = append(options, driverOptions(dconfig)...) + options = append(options, driverOptions(dconfig)) if len(dconfig.NetworkConfig.DefaultAddressPools.Value()) > 0 { options = append(options, nwconfig.OptionDefaultAddressPoolConfig(dconfig.NetworkConfig.DefaultAddressPools.Value())) @@ -1371,7 +1505,7 @@ func CreateDaemonRoot(config *config.Config) error { if _, err := os.Stat(config.Root); err != nil && os.IsNotExist(err) { realRoot = config.Root } else { - realRoot, err = getRealPath(config.Root) + realRoot, err = fileutils.ReadSymlinkedDirectory(config.Root) if err != nil { return fmt.Errorf("Unable to get the full path to root (%s): %s", config.Root, err) } diff --git a/daemon/daemon_linux.go b/daemon/daemon_linux.go index 6a5790b4fc94e..120387c1435e8 100644 --- a/daemon/daemon_linux.go +++ b/daemon/daemon_linux.go @@ -9,18 +9,13 @@ import ( "strings" "github.com/docker/docker/daemon/config" - "github.com/docker/docker/internal/procfs" - "github.com/docker/docker/pkg/fileutils" - "github.com/docker/docker/pkg/mount" + "github.com/docker/docker/libnetwork/resolvconf" + "github.com/moby/sys/mount" + "github.com/moby/sys/mountinfo" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -const ( - defaultResolvConf = "/etc/resolv.conf" - alternateResolvConf = "/run/systemd/resolve/resolv.conf" -) - // On Linux, plugins use a static path for storing execution state, // instead of deriving path from daemon's exec-root. This is because // plugin socket files are created here and they cannot exceed max @@ -44,18 +39,18 @@ func (daemon *Daemon) cleanupMountsFromReaderByID(reader io.Reader, id string, u if daemon.root == "" { return nil } - var errors []string + var errs []string regexps := getCleanPatterns(id) sc := bufio.NewScanner(reader) for sc.Scan() { - if fields := strings.Fields(sc.Text()); len(fields) >= 4 { + if fields := strings.Fields(sc.Text()); len(fields) > 4 { if mnt := fields[4]; strings.HasPrefix(mnt, daemon.root) { for _, p := range regexps { if p.MatchString(mnt) { if err := unmount(mnt); err != nil { logrus.Error(err) - errors = append(errors, err.Error()) + errs = append(errs, err.Error()) } } } @@ -67,8 +62,8 @@ func (daemon *Daemon) cleanupMountsFromReaderByID(reader io.Reader, id string, u return err } - if len(errors) > 0 { - return fmt.Errorf("Error cleaning up mounts:\n%v", strings.Join(errors, "\n")) + if len(errs) > 0 { + return fmt.Errorf("Error cleaning up mounts:\n%v", strings.Join(errs, "\n")) } logrus.Debugf("Cleaning up old mountid %v: done.", id) @@ -81,7 +76,7 @@ func (daemon *Daemon) cleanupMounts() error { return err } - info, err := mount.GetMounts(mount.SingleEntryFilter(daemon.root)) + info, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(daemon.root)) if err != nil { return errors.Wrap(err, "error reading mount table for cleanup") } @@ -128,15 +123,11 @@ func getCleanPatterns(id string) (regexps []*regexp.Regexp) { return } -func getRealPath(path string) (string, error) { - return fileutils.ReadSymlinkedDirectory(path) -} - -func shouldUnmountRoot(root string, info *mount.Info) bool { +func shouldUnmountRoot(root string, info *mountinfo.Info) bool { if !strings.HasSuffix(root, info.Root) { return false } - return hasMountinfoOption(info.Optional, sharedPropagationOption) + return hasMountInfoOption(info.Optional, sharedPropagationOption) } // setupResolvConf sets the appropriate resolv.conf file if not specified @@ -148,20 +139,5 @@ func setupResolvConf(config *config.Config) { if config.ResolvConf != "" { return } - - config.ResolvConf = defaultResolvConf - pids, err := procfs.PidOf("systemd-resolved") - if err != nil { - logrus.Errorf("unable to check systemd-resolved status: %s", err) - return - } - if len(pids) > 0 && pids[0] > 0 { - _, err := os.Stat(alternateResolvConf) - if err == nil { - logrus.Infof("systemd-resolved is running, so using resolvconf: %s", alternateResolvConf) - config.ResolvConf = alternateResolvConf - return - } - logrus.Infof("systemd-resolved is running, but %s is not present, fallback to %s", alternateResolvConf, defaultResolvConf) - } + config.ResolvConf = resolvconf.Path() } diff --git a/daemon/daemon_linux_test.go b/daemon/daemon_linux_test.go index 66d6aa20d8fb0..41b725ad85f68 100644 --- a/daemon/daemon_linux_test.go +++ b/daemon/daemon_linux_test.go @@ -1,22 +1,20 @@ +//go:build linux // +build linux package daemon // import "github.com/docker/docker/daemon" import ( - "io/ioutil" "os" "path/filepath" "strings" "testing" containertypes "github.com/docker/docker/api/types/container" - "github.com/docker/docker/container" "github.com/docker/docker/daemon/config" - "github.com/docker/docker/oci" - "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/mount" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/moby/sys/mount" + "github.com/moby/sys/mountinfo" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) const mountsFixture = `142 78 0:38 / / rw,relatime - aufs none rw,si=573b861da0b3a05b,dio @@ -115,58 +113,10 @@ func TestNotCleanupMounts(t *testing.T) { } } -// TestTmpfsDevShmSizeOverride checks that user-specified /dev/tmpfs mount -// size is not overridden by the default shmsize (that should only be used -// for default /dev/shm (as in "shareable" and "private" ipc modes). -// https://github.com/moby/moby/issues/35271 -func TestTmpfsDevShmSizeOverride(t *testing.T) { - size := "777m" - mnt := "/dev/shm" - - d := Daemon{ - idMapping: &idtools.IdentityMapping{}, - } - c := &container.Container{ - HostConfig: &containertypes.HostConfig{ - ShmSize: 48 * 1024, // size we should NOT end up with - }, - } - ms := []container.Mount{ - { - Source: "tmpfs", - Destination: mnt, - Data: "size=" + size, - }, - } - - // convert ms to spec - spec := oci.DefaultSpec() - err := setMounts(&d, &spec, c, ms) - assert.Check(t, err) - - // Check the resulting spec for the correct size - found := false - for _, m := range spec.Mounts { - if m.Destination == mnt { - for _, o := range m.Options { - if !strings.HasPrefix(o, "size=") { - continue - } - t.Logf("%+v\n", m.Options) - assert.Check(t, is.Equal("size="+size, o)) - found = true - } - } - } - if !found { - t.Fatal("/dev/shm not found in spec, or size option missing") - } -} - func TestValidateContainerIsolationLinux(t *testing.T) { d := Daemon{} - _, err := d.verifyContainerSettings("linux", &containertypes.HostConfig{Isolation: containertypes.IsolationHyperV}, nil, false) + _, err := d.verifyContainerSettings(&containertypes.HostConfig{Isolation: containertypes.IsolationHyperV}, nil, false) assert.Check(t, is.Error(err, "invalid isolation 'hyperv' on linux")) } @@ -174,25 +124,25 @@ func TestShouldUnmountRoot(t *testing.T) { for _, test := range []struct { desc string root string - info *mount.Info + info *mountinfo.Info expect bool }{ { desc: "root is at /", root: "/docker", - info: &mount.Info{Root: "/docker", Mountpoint: "/docker"}, + info: &mountinfo.Info{Root: "/docker", Mountpoint: "/docker"}, expect: true, }, { desc: "root is at in a submount from `/`", root: "/foo/docker", - info: &mount.Info{Root: "/docker", Mountpoint: "/foo/docker"}, + info: &mountinfo.Info{Root: "/docker", Mountpoint: "/foo/docker"}, expect: true, }, { desc: "root is mounted in from a parent mount namespace same root dir", // dind is an example of this root: "/docker", - info: &mount.Info{Root: "/docker/volumes/1234657/_data", Mountpoint: "/docker"}, + info: &mountinfo.Info{Root: "/docker/volumes/1234657/_data", Mountpoint: "/docker"}, expect: false, }, } { @@ -223,7 +173,7 @@ func TestShouldUnmountRoot(t *testing.T) { func checkMounted(t *testing.T, p string, expect bool) { t.Helper() - mounted, err := mount.Mounted(p) + mounted, err := mountinfo.Mounted(p) assert.Check(t, err) assert.Check(t, mounted == expect, "expected %v, actual %v", expect, mounted) } @@ -235,31 +185,31 @@ func TestRootMountCleanup(t *testing.T) { t.Parallel() - testRoot, err := ioutil.TempDir("", t.Name()) - assert.Assert(t, err) + testRoot, err := os.MkdirTemp("", t.Name()) + assert.NilError(t, err) defer os.RemoveAll(testRoot) cfg := &config.Config{} err = mount.MakePrivate(testRoot) - assert.Assert(t, err) + assert.NilError(t, err) defer mount.Unmount(testRoot) cfg.ExecRoot = filepath.Join(testRoot, "exec") cfg.Root = filepath.Join(testRoot, "daemon") err = os.Mkdir(cfg.ExecRoot, 0755) - assert.Assert(t, err) + assert.NilError(t, err) err = os.Mkdir(cfg.Root, 0755) - assert.Assert(t, err) + assert.NilError(t, err) d := &Daemon{configStore: cfg, root: cfg.Root} unmountFile := getUnmountOnShutdownPath(cfg) t.Run("regular dir no mountpoint", func(t *testing.T) { err = setupDaemonRootPropagation(cfg) - assert.Assert(t, err) + assert.NilError(t, err) _, err = os.Stat(unmountFile) - assert.Assert(t, err) + assert.NilError(t, err) checkMounted(t, cfg.Root, true) assert.Assert(t, d.cleanupMounts()) @@ -271,11 +221,11 @@ func TestRootMountCleanup(t *testing.T) { t.Run("root is a private mountpoint", func(t *testing.T) { err = mount.MakePrivate(cfg.Root) - assert.Assert(t, err) + assert.NilError(t, err) defer mount.Unmount(cfg.Root) err = setupDaemonRootPropagation(cfg) - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, ensureShared(cfg.Root)) _, err = os.Stat(unmountFile) @@ -287,11 +237,11 @@ func TestRootMountCleanup(t *testing.T) { // mount is pre-configured with a shared mount t.Run("root is a shared mountpoint", func(t *testing.T) { err = mount.MakeShared(cfg.Root) - assert.Assert(t, err) + assert.NilError(t, err) defer mount.Unmount(cfg.Root) err = setupDaemonRootPropagation(cfg) - assert.Assert(t, err) + assert.NilError(t, err) if _, err := os.Stat(unmountFile); err == nil { t.Fatal("unmount file should not exist") @@ -305,13 +255,13 @@ func TestRootMountCleanup(t *testing.T) { // does not need mount but unmount file exists from previous run t.Run("old mount file is cleaned up on setup if not needed", func(t *testing.T) { err = mount.MakeShared(testRoot) - assert.Assert(t, err) + assert.NilError(t, err) defer mount.MakePrivate(testRoot) - err = ioutil.WriteFile(unmountFile, nil, 0644) - assert.Assert(t, err) + err = os.WriteFile(unmountFile, nil, 0644) + assert.NilError(t, err) err = setupDaemonRootPropagation(cfg) - assert.Assert(t, err) + assert.NilError(t, err) _, err = os.Stat(unmountFile) assert.Check(t, os.IsNotExist(err), err) diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go index d89b0077fd6e2..1975f3cde3911 100644 --- a/daemon/daemon_test.go +++ b/daemon/daemon_test.go @@ -1,7 +1,6 @@ package daemon // import "github.com/docker/docker/daemon" import ( - "io/ioutil" "os" "path/filepath" "runtime" @@ -10,15 +9,15 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/errdefs" + "github.com/docker/docker/libnetwork" _ "github.com/docker/docker/pkg/discovery/memory" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/truncindex" volumesservice "github.com/docker/docker/volume/service" "github.com/docker/go-connections/nat" - "github.com/docker/libnetwork" "github.com/pkg/errors" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) // @@ -82,15 +81,15 @@ func TestGetContainer(t *testing.T) { daemon.reserveName(c4.ID, c4.Name) daemon.reserveName(c5.ID, c5.Name) - if container, _ := daemon.GetContainer("3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de"); container != c2 { + if ctr, _ := daemon.GetContainer("3cdbd1aa394fd68559fd1441d6eff2ab7c1e6363582c82febfaa8045df3bd8de"); ctr != c2 { t.Fatal("Should explicitly match full container IDs") } - if container, _ := daemon.GetContainer("75fb0b8009"); container != c4 { + if ctr, _ := daemon.GetContainer("75fb0b8009"); ctr != c4 { t.Fatal("Should match a partial ID") } - if container, _ := daemon.GetContainer("drunk_hawking"); container != c2 { + if ctr, _ := daemon.GetContainer("drunk_hawking"); ctr != c2 { t.Fatal("Should match a full name") } @@ -99,7 +98,7 @@ func TestGetContainer(t *testing.T) { t.Fatal("Should match a full name even though it collides with another container's ID") } - if container, _ := daemon.GetContainer("d22d69a2b896"); container != c5 { + if ctr, _ := daemon.GetContainer("d22d69a2b896"); ctr != c5 { t.Fatal("Should match a container where the provided prefix is an exact match to the its name, and is also a prefix for its ID") } @@ -147,7 +146,7 @@ func TestContainerInitDNS(t *testing.T) { t.Skip("root required") // for chown } - tmp, err := ioutil.TempDir("", "docker-container-test-") + tmp, err := os.MkdirTemp("", "docker-container-test-") if err != nil { t.Fatal(err) } @@ -176,12 +175,12 @@ func TestContainerInitDNS(t *testing.T) { "UpdateDns":false,"Volumes":{},"VolumesRW":{},"AppliedVolumesFrom":null}` // Container struct only used to retrieve path to config file - container := &container.Container{Root: containerPath} - configPath, err := container.ConfigPath() + ctr := &container.Container{Root: containerPath} + configPath, err := ctr.ConfigPath() if err != nil { t.Fatal(err) } - if err = ioutil.WriteFile(configPath, []byte(config), 0644); err != nil { + if err = os.WriteFile(configPath, []byte(config), 0644); err != nil { t.Fatal(err) } @@ -190,11 +189,11 @@ func TestContainerInitDNS(t *testing.T) { "Devices":[],"NetworkMode":"bridge","IpcMode":"","PidMode":"","CapAdd":null,"CapDrop":null,"RestartPolicy":{"Name":"no","MaximumRetryCount":0}, "SecurityOpt":null,"ReadonlyRootfs":false,"Ulimits":null,"LogConfig":{"Type":"","Config":null},"CgroupParent":""}` - hostConfigPath, err := container.HostConfigPath() + hostConfigPath, err := ctr.HostConfigPath() if err != nil { t.Fatal(err) } - if err = ioutil.WriteFile(hostConfigPath, []byte(hostConfig), 0644); err != nil { + if err = os.WriteFile(hostConfigPath, []byte(hostConfig), 0644); err != nil { t.Fatal(err) } @@ -305,14 +304,15 @@ func TestMerge(t *testing.T) { func TestValidateContainerIsolation(t *testing.T) { d := Daemon{} - _, err := d.verifyContainerSettings(runtime.GOOS, &containertypes.HostConfig{Isolation: containertypes.Isolation("invalid")}, nil, false) + _, err := d.verifyContainerSettings(&containertypes.HostConfig{Isolation: containertypes.Isolation("invalid")}, nil, false) assert.Check(t, is.Error(err, "invalid isolation 'invalid' on "+runtime.GOOS)) } func TestFindNetworkErrorType(t *testing.T) { d := Daemon{} _, err := d.FindNetwork("fakeNet") - _, ok := errors.Cause(err).(libnetwork.ErrNoSuchNetwork) + var nsn libnetwork.ErrNoSuchNetwork + ok := errors.As(err, &nsn) if !errdefs.IsNotFound(err) || !ok { t.Error("The FindNetwork method MUST always return an error that implements the NotFound interface and is ErrNoSuchNetwork") } diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index b69eede21c44f..19f8ad8ca8b6a 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package daemon // import "github.com/docker/docker/daemon" @@ -6,7 +7,6 @@ import ( "bufio" "context" "fmt" - "io/ioutil" "net" "os" "path/filepath" @@ -14,9 +14,13 @@ import ( "runtime/debug" "strconv" "strings" + "sync" "time" - containerd_cgroups "github.com/containerd/cgroups" + "github.com/containerd/cgroups" + statsV1 "github.com/containerd/cgroups/stats/v1" + statsV2 "github.com/containerd/cgroups/v2/stats" + "github.com/containerd/containerd/pkg/userns" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/blkiodev" pblkiodev "github.com/docker/docker/api/types/blkiodev" @@ -24,26 +28,26 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/initlayer" + "github.com/docker/docker/errdefs" + "github.com/docker/docker/libcontainerd/remote" + "github.com/docker/docker/libnetwork" + nwconfig "github.com/docker/docker/libnetwork/config" + "github.com/docker/docker/libnetwork/drivers/bridge" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/options" + lntypes "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/runconfig" volumemounts "github.com/docker/docker/volume/mounts" - "github.com/docker/libnetwork" - nwconfig "github.com/docker/libnetwork/config" - "github.com/docker/libnetwork/drivers/bridge" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/options" - lntypes "github.com/docker/libnetwork/types" - "github.com/opencontainers/runc/libcontainer/cgroups" - rsystem "github.com/opencontainers/runc/libcontainer/system" - "github.com/opencontainers/runtime-spec/specs-go" + "github.com/moby/sys/mount" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -52,20 +56,14 @@ import ( ) const ( - // DefaultShimBinary is the default shim to be used by containerd if none - // is specified - DefaultShimBinary = "containerd-shim" - - // DefaultRuntimeBinary is the default runtime to be used by - // containerd if none is specified - DefaultRuntimeBinary = "runc" + isWindows = false // See https://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/tree/kernel/sched/sched.h?id=8cd9234c64c584432f6992fe944ca9e46ca8ea76#n269 linuxMinCPUShares = 2 linuxMaxCPUShares = 262144 platformSupported = true - // It's not kernel limit, we want this 4M limit to supply a reasonable functional container - linuxMinMemory = 4194304 + // It's not kernel limit, we want this 6M limit to account for overhead during startup, and to supply a reasonable functional container + linuxMinMemory = 6291456 // constants for remapped root settings defaultIDSpecifier = "default" defaultRemappedID = "dockremap" @@ -73,10 +71,7 @@ const ( // constant for cgroup drivers cgroupFsDriver = "cgroupfs" cgroupSystemdDriver = "systemd" - - // DefaultRuntimeName is the default runtime to be used by - // containerd if none is specified - DefaultRuntimeName = "runc" + cgroupNoneDriver = "none" ) type containerGetter interface { @@ -111,9 +106,26 @@ func getMemoryResources(config containertypes.Resources) *specs.LinuxMemory { memory.Kernel = &config.KernelMemory } + if config.KernelMemoryTCP != 0 { + memory.KernelTCP = &config.KernelMemoryTCP + } + return &memory } +func getPidsLimit(config containertypes.Resources) *specs.LinuxPids { + if config.PidsLimit == nil { + return nil + } + if *config.PidsLimit <= 0 { + // docker API allows 0 and negative values to unset this to be consistent + // with default values. When updating values, runc requires -1 to unset + // the previous limit. + return &specs.LinuxPids{Limit: -1} + } + return &specs.LinuxPids{Limit: *config.PidsLimit} +} + func getCPUResources(config containertypes.Resources) (*specs.LinuxCPU, error) { cpu := specs.LinuxCPU{} @@ -170,12 +182,13 @@ func getBlkioWeightDevices(config containertypes.Resources) ([]specs.LinuxWeight for _, weightDevice := range config.BlkioWeightDevice { if err := unix.Stat(weightDevice.Path, &stat); err != nil { - return nil, err + return nil, errors.WithStack(&os.PathError{Op: "stat", Path: weightDevice.Path, Err: err}) } weight := weightDevice.Weight d := specs.LinuxWeightDevice{Weight: &weight} - d.Major = int64(stat.Rdev / 256) - d.Minor = int64(stat.Rdev % 256) + // The type is 32bit on mips. + d.Major = int64(unix.Major(uint64(stat.Rdev))) //nolint: unconvert + d.Minor = int64(unix.Minor(uint64(stat.Rdev))) //nolint: unconvert blkioWeightDevices = append(blkioWeightDevices, d) } @@ -242,17 +255,53 @@ func getBlkioThrottleDevices(devs []*blkiodev.ThrottleDevice) ([]specs.LinuxThro for _, d := range devs { if err := unix.Stat(d.Path, &stat); err != nil { - return nil, err + return nil, errors.WithStack(&os.PathError{Op: "stat", Path: d.Path, Err: err}) } d := specs.LinuxThrottleDevice{Rate: d.Rate} - d.Major = int64(stat.Rdev / 256) - d.Minor = int64(stat.Rdev % 256) + // the type is 32bit on mips + d.Major = int64(unix.Major(uint64(stat.Rdev))) //nolint: unconvert + d.Minor = int64(unix.Minor(uint64(stat.Rdev))) //nolint: unconvert throttleDevices = append(throttleDevices, d) } return throttleDevices, nil } +// adjustParallelLimit takes a number of objects and a proposed limit and +// figures out if it's reasonable (and adjusts it accordingly). This is only +// used for daemon startup, which does a lot of parallel loading of containers +// (and if we exceed RLIMIT_NOFILE then we're in trouble). +func adjustParallelLimit(n int, limit int) int { + // Rule-of-thumb overhead factor (how many files will each goroutine open + // simultaneously). Yes, this is ugly but to be frank this whole thing is + // ugly. + const overhead = 2 + + // On Linux, we need to ensure that parallelStartupJobs doesn't cause us to + // exceed RLIMIT_NOFILE. If parallelStartupJobs is too large, we reduce it + // and give a warning (since in theory the user should increase their + // ulimits to the largest possible value for dockerd). + var rlim unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlim); err != nil { + logrus.Warnf("Couldn't find dockerd's RLIMIT_NOFILE to double-check startup parallelism factor: %v", err) + return limit + } + softRlimit := int(rlim.Cur) + + // Much fewer containers than RLIMIT_NOFILE. No need to adjust anything. + if softRlimit > overhead*n { + return limit + } + + // RLIMIT_NOFILE big enough, no need to adjust anything. + if softRlimit > overhead*limit { + return limit + } + + logrus.Warnf("Found dockerd's open file ulimit (%v) is far too small -- consider increasing it significantly (at least %v)", softRlimit, overhead*limit) + return softRlimit / overhead +} + func checkKernel() error { // Check for unsupported kernel versions // FIXME: it would be cleaner to not test for specific versions, but rather @@ -299,19 +348,37 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf if hostConfig.IpcMode.IsEmpty() { m := config.DefaultIpcMode if daemon.configStore != nil { - m = daemon.configStore.IpcMode + m = containertypes.IpcMode(daemon.configStore.IpcMode) + } + hostConfig.IpcMode = m + } + + // Set default cgroup namespace mode, if unset for container + if hostConfig.CgroupnsMode.IsEmpty() { + // for cgroup v2: unshare cgroupns even for privileged containers + // https://github.com/containers/libpod/pull/4374#issuecomment-549776387 + if hostConfig.Privileged && cgroups.Mode() != cgroups.Unified { + hostConfig.CgroupnsMode = containertypes.CgroupnsModeHost + } else { + m := containertypes.CgroupnsModeHost + if cgroups.Mode() == cgroups.Unified { + m = containertypes.CgroupnsModePrivate + } + if daemon.configStore != nil { + m = containertypes.CgroupnsMode(daemon.configStore.CgroupNamespaceMode) + } + hostConfig.CgroupnsMode = m } - hostConfig.IpcMode = containertypes.IpcMode(m) } adaptSharedNamespaceContainer(daemon, hostConfig) var err error - opts, err := daemon.generateSecurityOpt(hostConfig) + secOpts, err := daemon.generateSecurityOpt(hostConfig) if err != nil { return err } - hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, opts...) + hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, secOpts...) if hostConfig.OomKillDisable == nil { defaultOomKillDisable := false hostConfig.OomKillDisable = &defaultOomKillDisable @@ -350,23 +417,21 @@ func adaptSharedNamespaceContainer(daemon containerGetter, hostConfig *container } } -func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysinfo.SysInfo, update bool) ([]string, error) { - warnings := []string{} +// verifyPlatformContainerResources performs platform-specific validation of the container's resource-configuration +func verifyPlatformContainerResources(resources *containertypes.Resources, sysInfo *sysinfo.SysInfo, update bool) (warnings []string, err error) { fixMemorySwappiness(resources) // memory subsystem checks and adjustments if resources.Memory != 0 && resources.Memory < linuxMinMemory { - return warnings, fmt.Errorf("Minimum memory limit allowed is 4MB") + return warnings, fmt.Errorf("Minimum memory limit allowed is 6MB") } if resources.Memory > 0 && !sysInfo.MemoryLimit { warnings = append(warnings, "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.") - logrus.Warn("Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.") resources.Memory = 0 resources.MemorySwap = -1 } if resources.Memory > 0 && resources.MemorySwap != -1 && !sysInfo.SwapLimit { warnings = append(warnings, "Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.") - logrus.Warn("Your kernel does not support swap limit capabilities,or the cgroup is not mounted. Memory limited without swap.") resources.MemorySwap = -1 } if resources.Memory > 0 && resources.MemorySwap > 0 && resources.MemorySwap < resources.Memory { @@ -377,7 +442,6 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi } if resources.MemorySwappiness != nil && !sysInfo.MemorySwappiness { warnings = append(warnings, "Your kernel does not support memory swappiness capabilities or the cgroup is not mounted. Memory swappiness discarded.") - logrus.Warn("Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded.") resources.MemorySwappiness = nil } if resources.MemorySwappiness != nil { @@ -388,18 +452,22 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi } if resources.MemoryReservation > 0 && !sysInfo.MemoryReservation { warnings = append(warnings, "Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.") - logrus.Warn("Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.") resources.MemoryReservation = 0 } if resources.MemoryReservation > 0 && resources.MemoryReservation < linuxMinMemory { - return warnings, fmt.Errorf("Minimum memory reservation allowed is 4MB") + return warnings, fmt.Errorf("Minimum memory reservation allowed is 6MB") } if resources.Memory > 0 && resources.MemoryReservation > 0 && resources.Memory < resources.MemoryReservation { return warnings, fmt.Errorf("Minimum memory limit can not be less than memory reservation limit, see usage") } + if resources.KernelMemory > 0 { + // Kernel memory limit is not supported on cgroup v2. + // Even on cgroup v1, kernel memory limit (`kmem.limit_in_bytes`) has been deprecated since kernel 5.4. + // https://github.com/torvalds/linux/commit/0158115f702b0ba208ab0b5adf44cae99b3ebcc7 + warnings = append(warnings, "Specifying a kernel memory limit is deprecated and will be removed in a future release.") + } if resources.KernelMemory > 0 && !sysInfo.KernelMemory { warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.") - logrus.Warn("Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.") resources.KernelMemory = 0 } if resources.KernelMemory > 0 && resources.KernelMemory < linuxMinMemory { @@ -407,22 +475,23 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi } if resources.KernelMemory > 0 && !kernel.CheckKernelVersion(4, 0, 0) { warnings = append(warnings, "You specified a kernel memory limit on a kernel older than 4.0. Kernel memory limits are experimental on older kernels, it won't work as expected and can cause your system to be unstable.") - logrus.Warn("You specified a kernel memory limit on a kernel older than 4.0. Kernel memory limits are experimental on older kernels, it won't work as expected and can cause your system to be unstable.") } if resources.OomKillDisable != nil && !sysInfo.OomKillDisable { // only produce warnings if the setting wasn't to *disable* the OOM Kill; no point // warning the caller if they already wanted the feature to be off if *resources.OomKillDisable { warnings = append(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.") - logrus.Warn("Your kernel does not support OomKillDisable. OomKillDisable discarded.") } resources.OomKillDisable = nil } - - if resources.PidsLimit != 0 && !sysInfo.PidsLimit { - warnings = append(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.") - logrus.Warn("Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.") - resources.PidsLimit = 0 + if resources.OomKillDisable != nil && *resources.OomKillDisable && resources.Memory == 0 { + warnings = append(warnings, "OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.") + } + if resources.PidsLimit != nil && !sysInfo.PidsLimit { + if *resources.PidsLimit > 0 { + warnings = append(warnings, "Your kernel does not support PIDs limit capabilities or the cgroup is not mounted. PIDs limit discarded.") + } + resources.PidsLimit = nil } // cpu subsystem checks and adjustments @@ -432,8 +501,8 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi if resources.NanoCPUs > 0 && resources.CPUQuota > 0 { return warnings, fmt.Errorf("Conflicting options: Nano CPUs and CPU Quota cannot both be set") } - if resources.NanoCPUs > 0 && (!sysInfo.CPUCfsPeriod || !sysInfo.CPUCfsQuota) { - return warnings, fmt.Errorf("NanoCPUs can not be set, as your kernel does not support CPU cfs period/quota or the cgroup is not mounted") + if resources.NanoCPUs > 0 && !sysInfo.CPUCfs { + return warnings, fmt.Errorf("NanoCPUs can not be set, as your kernel does not support CPU CFS scheduler or the cgroup is not mounted") } // The highest precision we could get on Linux is 0.001, by setting // cpu.cfs_period_us=1000ms @@ -448,35 +517,27 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi if resources.CPUShares > 0 && !sysInfo.CPUShares { warnings = append(warnings, "Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.") - logrus.Warn("Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.") resources.CPUShares = 0 } - if resources.CPUPeriod > 0 && !sysInfo.CPUCfsPeriod { - warnings = append(warnings, "Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.") - logrus.Warn("Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.") + if (resources.CPUPeriod != 0 || resources.CPUQuota != 0) && !sysInfo.CPUCfs { + warnings = append(warnings, "Your kernel does not support CPU CFS scheduler. CPU period/quota discarded.") resources.CPUPeriod = 0 + resources.CPUQuota = 0 } if resources.CPUPeriod != 0 && (resources.CPUPeriod < 1000 || resources.CPUPeriod > 1000000) { return warnings, fmt.Errorf("CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)") } - if resources.CPUQuota > 0 && !sysInfo.CPUCfsQuota { - warnings = append(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.") - logrus.Warn("Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.") - resources.CPUQuota = 0 - } if resources.CPUQuota > 0 && resources.CPUQuota < 1000 { return warnings, fmt.Errorf("CPU cfs quota can not be less than 1ms (i.e. 1000)") } if resources.CPUPercent > 0 { warnings = append(warnings, fmt.Sprintf("%s does not support CPU percent. Percent discarded.", runtime.GOOS)) - logrus.Warnf("%s does not support CPU percent. Percent discarded.", runtime.GOOS) resources.CPUPercent = 0 } // cpuset subsystem checks and adjustments if (resources.CpusetCpus != "" || resources.CpusetMems != "") && !sysInfo.Cpuset { warnings = append(warnings, "Your kernel does not support cpuset or the cgroup is not mounted. Cpuset discarded.") - logrus.Warn("Your kernel does not support cpuset or the cgroup is not mounted. Cpuset discarded.") resources.CpusetCpus = "" resources.CpusetMems = "" } @@ -498,7 +559,6 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi // blkio subsystem checks and adjustments if resources.BlkioWeight > 0 && !sysInfo.BlkioWeight { warnings = append(warnings, "Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.") - logrus.Warn("Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.") resources.BlkioWeight = 0 } if resources.BlkioWeight > 0 && (resources.BlkioWeight < 10 || resources.BlkioWeight > 1000) { @@ -509,28 +569,23 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi } if len(resources.BlkioWeightDevice) > 0 && !sysInfo.BlkioWeightDevice { warnings = append(warnings, "Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.") - logrus.Warn("Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.") resources.BlkioWeightDevice = []*pblkiodev.WeightDevice{} } if len(resources.BlkioDeviceReadBps) > 0 && !sysInfo.BlkioReadBpsDevice { warnings = append(warnings, "Your kernel does not support BPS Block I/O read limit or the cgroup is not mounted. Block I/O BPS read limit discarded.") - logrus.Warn("Your kernel does not support BPS Block I/O read limit or the cgroup is not mounted. Block I/O BPS read limit discarded") resources.BlkioDeviceReadBps = []*pblkiodev.ThrottleDevice{} } if len(resources.BlkioDeviceWriteBps) > 0 && !sysInfo.BlkioWriteBpsDevice { warnings = append(warnings, "Your kernel does not support BPS Block I/O write limit or the cgroup is not mounted. Block I/O BPS write limit discarded.") - logrus.Warn("Your kernel does not support BPS Block I/O write limit or the cgroup is not mounted. Block I/O BPS write limit discarded.") resources.BlkioDeviceWriteBps = []*pblkiodev.ThrottleDevice{} } if len(resources.BlkioDeviceReadIOps) > 0 && !sysInfo.BlkioReadIOpsDevice { warnings = append(warnings, "Your kernel does not support IOPS Block read limit or the cgroup is not mounted. Block I/O IOPS read limit discarded.") - logrus.Warn("Your kernel does not support IOPS Block I/O read limit in IO or the cgroup is not mounted. Block I/O IOPS read limit discarded.") resources.BlkioDeviceReadIOps = []*pblkiodev.ThrottleDevice{} } if len(resources.BlkioDeviceWriteIOps) > 0 && !sysInfo.BlkioWriteIOpsDevice { warnings = append(warnings, "Your kernel does not support IOPS Block write limit or the cgroup is not mounted. Block I/O IOPS write limit discarded.") - logrus.Warn("Your kernel does not support IOPS Block I/O write limit or the cgroup is not mounted. Block I/O IOPS write limit discarded.") resources.BlkioDeviceWriteIOps = []*pblkiodev.ThrottleDevice{} } @@ -538,12 +593,13 @@ func verifyContainerResources(resources *containertypes.Resources, sysInfo *sysi } func (daemon *Daemon) getCgroupDriver() string { - cgroupDriver := cgroupFsDriver - if UsingSystemd(daemon.configStore) { - cgroupDriver = cgroupSystemdDriver + return cgroupSystemdDriver + } + if daemon.Rootless() { + return cgroupNoneDriver } - return cgroupDriver + return cgroupFsDriver } // getCD gets the raw value of the native.cgroupdriver option, if set. @@ -558,27 +614,64 @@ func getCD(config *config.Config) string { return "" } -// VerifyCgroupDriver validates native.cgroupdriver -func VerifyCgroupDriver(config *config.Config) error { +// verifyCgroupDriver validates native.cgroupdriver +func verifyCgroupDriver(config *config.Config) error { cd := getCD(config) if cd == "" || cd == cgroupFsDriver || cd == cgroupSystemdDriver { return nil } + if cd == cgroupNoneDriver { + return fmt.Errorf("native.cgroupdriver option %s is internally used and cannot be specified manually", cd) + } return fmt.Errorf("native.cgroupdriver option %s not supported", cd) } // UsingSystemd returns true if cli option includes native.cgroupdriver=systemd func UsingSystemd(config *config.Config) bool { - return getCD(config) == cgroupSystemdDriver + cd := getCD(config) + + if cd == cgroupSystemdDriver { + return true + } + // On cgroup v2 hosts, default to systemd driver + if cd == "" && cgroups.Mode() == cgroups.Unified && isRunningSystemd() { + return true + } + return false +} + +var ( + runningSystemd bool + detectSystemd sync.Once +) + +// isRunningSystemd checks whether the host was booted with systemd as its init +// system. This functions similarly to systemd's `sd_booted(3)`: internally, it +// checks whether /run/systemd/system/ exists and is a directory. +// http://www.freedesktop.org/software/systemd/man/sd_booted.html +// +// NOTE: This function comes from package github.com/coreos/go-systemd/util +// It was borrowed here to avoid a dependency on cgo. +func isRunningSystemd() bool { + detectSystemd.Do(func() { + fi, err := os.Lstat("/run/systemd/system") + if err != nil { + return + } + runningSystemd = fi.IsDir() + }) + return runningSystemd } // verifyPlatformContainerSettings performs platform-specific validation of the // hostconfig and config structures. -func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { - var warnings []string - sysInfo := sysinfo.New(true) +func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) { + if hostConfig == nil { + return nil, nil + } + sysInfo := daemon.RawSysInfo() - w, err := verifyContainerResources(&hostConfig.Resources, sysInfo, update) + w, err := verifyPlatformContainerResources(&hostConfig.Resources, sysInfo, update) // no matter err is nil or not, w could have data in itself. warnings = append(warnings, w...) @@ -598,8 +691,11 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes. // ip-forwarding does not affect container with '--net=host' (or '--net=none') if sysInfo.IPv4ForwardingDisabled && !(hostConfig.NetworkMode.IsHost() || hostConfig.NetworkMode.IsNone()) { warnings = append(warnings, "IPv4 forwarding is disabled. Networking will not work.") - logrus.Warn("IPv4 forwarding is disabled. Networking will not work") } + if hostConfig.NetworkMode.IsHost() && len(hostConfig.PortBindings) > 0 { + warnings = append(warnings, "Published ports are discarded when using host network mode") + } + // check for various conflicting options with user namespaces if daemon.configStore.RemappedRoot != "" && hostConfig.UsernsMode.IsPrivate() { if hostConfig.Privileged { @@ -626,66 +722,34 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes. return warnings, fmt.Errorf("Unknown runtime specified %s", hostConfig.Runtime) } - parser := volumemounts.NewParser(runtime.GOOS) + parser := volumemounts.NewParser() for dest := range hostConfig.Tmpfs { if err := parser.ValidateTmpfsMountDestination(dest); err != nil { return warnings, err } } - return warnings, nil -} - -func (daemon *Daemon) loadRuntimes() error { - return daemon.initRuntimes(daemon.configStore.Runtimes) -} - -func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error) { - runtimeDir := filepath.Join(daemon.configStore.Root, "runtimes") - // Remove old temp directory if any - os.RemoveAll(runtimeDir + "-old") - tmpDir, err := ioutils.TempDir(daemon.configStore.Root, "gen-runtimes") - if err != nil { - return errors.Wrap(err, "failed to get temp dir to generate runtime scripts") + if !hostConfig.CgroupnsMode.Valid() { + return warnings, fmt.Errorf("invalid cgroup namespace mode: %v", hostConfig.CgroupnsMode) } - defer func() { - if err != nil { - if err1 := os.RemoveAll(tmpDir); err1 != nil { - logrus.WithError(err1).WithField("dir", tmpDir). - Warn("failed to remove tmp dir") - } - return - } - - if err = os.Rename(runtimeDir, runtimeDir+"-old"); err != nil { - return - } - if err = os.Rename(tmpDir, runtimeDir); err != nil { - err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start") - return - } - if err = os.RemoveAll(runtimeDir + "-old"); err != nil { - logrus.WithError(err).WithField("dir", tmpDir). - Warn("failed to remove old runtimes dir") - } - }() - - for name, rt := range runtimes { - if len(rt.Args) == 0 { - continue + if hostConfig.CgroupnsMode.IsPrivate() { + if !sysInfo.CgroupNamespaces { + warnings = append(warnings, "Your kernel does not support cgroup namespaces. Cgroup namespace setting discarded.") } + } - script := filepath.Join(tmpDir, name) - content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " ")) - if err := ioutil.WriteFile(script, []byte(content), 0700); err != nil { - return err - } + if hostConfig.Runtime == config.LinuxV1RuntimeName || (hostConfig.Runtime == "" && daemon.configStore.DefaultRuntime == config.LinuxV1RuntimeName) { + warnings = append(warnings, fmt.Sprintf("Configured runtime %q is deprecated and will be removed in the next release.", config.LinuxV1RuntimeName)) } - return nil + + return warnings, nil } // verifyDaemonSettings performs validation of daemon config struct func verifyDaemonSettings(conf *config.Config) error { + if conf.ContainerdNamespace == conf.ContainerdPluginNamespace { + return errors.New("containers namespace and plugins namespace cannot be the same") + } // Check for mutually incompatible config options if conf.BridgeConfig.Iface != "" && conf.BridgeConfig.IP != "" { return fmt.Errorf("You specified -b & --bip, mutually exclusive options. Please specify only one") @@ -693,10 +757,13 @@ func verifyDaemonSettings(conf *config.Config) error { if !conf.BridgeConfig.EnableIPTables && !conf.BridgeConfig.InterContainerCommunication { return fmt.Errorf("You specified --iptables=false with --icc=false. ICC=false uses iptables to function. Please set --icc or --iptables to true") } + if conf.BridgeConfig.EnableIP6Tables && !conf.Experimental { + return fmt.Errorf("ip6tables rules are only available if experimental features are enabled") + } if !conf.BridgeConfig.EnableIPTables && conf.BridgeConfig.EnableIPMasq { conf.BridgeConfig.EnableIPMasq = false } - if err := VerifyCgroupDriver(conf); err != nil { + if err := verifyCgroupDriver(conf); err != nil { return err } if conf.CgroupParent != "" && UsingSystemd(conf) { @@ -705,29 +772,31 @@ func verifyDaemonSettings(conf *config.Config) error { } } - if conf.DefaultRuntime == "" { - conf.DefaultRuntime = config.StockRuntimeName - } - if conf.Runtimes == nil { - conf.Runtimes = make(map[string]types.Runtime) + if conf.Rootless && UsingSystemd(conf) && cgroups.Mode() != cgroups.Unified { + return fmt.Errorf("exec-opt native.cgroupdriver=systemd requires cgroup v2 for rootless mode") } - conf.Runtimes[config.StockRuntimeName] = types.Runtime{Path: DefaultRuntimeName} + configureRuntimes(conf) + if rtName := conf.GetDefaultRuntimeName(); rtName != "" { + if conf.GetRuntime(rtName) == nil { + return fmt.Errorf("specified default runtime '%s' does not exist", rtName) + } + if rtName == config.LinuxV1RuntimeName { + logrus.Warnf("Configured default runtime %q is deprecated and will be removed in the next release.", config.LinuxV1RuntimeName) + } + } return nil } // checkSystem validates platform-specific requirements func checkSystem() error { - if os.Geteuid() != 0 { - return fmt.Errorf("The Docker daemon needs to be run as root") - } return checkKernel() } // configureMaxThreads sets the Go runtime max threads threshold // which is 90% of the kernel setting from /proc/sys/kernel/threads-max func configureMaxThreads(config *config.Config) error { - mt, err := ioutil.ReadFile("/proc/sys/kernel/threads-max") + mt, err := os.ReadFile("/proc/sys/kernel/threads-max") if err != nil { return err } @@ -751,31 +820,20 @@ func overlaySupportsSelinux() (bool, error) { } defer f.Close() - var symAddr, symType, symName, text string - s := bufio.NewScanner(f) for s.Scan() { - if err := s.Err(); err != nil { - return false, err - } - - text = s.Text() - if _, err := fmt.Sscanf(text, "%s %s %s", &symAddr, &symType, &symName); err != nil { - return false, fmt.Errorf("Scanning '%s' failed: %s", text, err) - } - - // Check for presence of symbol security_inode_copy_up. - if symName == "security_inode_copy_up" { + if strings.HasSuffix(s.Text(), " security_inode_copy_up") { return true, nil } } - return false, nil + + return false, s.Err() } // configureKernelSecuritySupport configures and validates security support for the kernel func configureKernelSecuritySupport(config *config.Config, driverName string) error { if config.EnableSelinuxSupport { - if !selinuxEnabled() { + if !selinux.GetEnabled() { logrus.Warn("Docker could not enable SELinux on the host system") return nil } @@ -793,7 +851,7 @@ func configureKernelSecuritySupport(config *config.Config, driverName string) er } } } else { - selinuxSetDisabled() + selinux.SetDisabled() } return nil } @@ -811,6 +869,7 @@ func (daemon *Daemon) initNetworkController(config *config.Config, activeSandbox if len(activeSandboxes) > 0 { logrus.Info("There are old running containers, the network config will not take affect") + setHostGatewayIP(daemon.configStore, controller) return controller, nil } @@ -847,20 +906,39 @@ func (daemon *Daemon) initNetworkController(config *config.Config, activeSandbox removeDefaultBridgeInterface() } + // Set HostGatewayIP to the default bridge's IP if it is empty + setHostGatewayIP(daemon.configStore, controller) + return controller, nil } -func driverOptions(config *config.Config) []nwconfig.Option { - bridgeConfig := options.Generic{ - "EnableIPForwarding": config.BridgeConfig.EnableIPForward, - "EnableIPTables": config.BridgeConfig.EnableIPTables, - "EnableUserlandProxy": config.BridgeConfig.EnableUserlandProxy, - "UserlandProxyPath": config.BridgeConfig.UserlandProxyPath} - bridgeOption := options.Generic{netlabel.GenericData: bridgeConfig} - - dOptions := []nwconfig.Option{} - dOptions = append(dOptions, nwconfig.OptionDriverConfig("bridge", bridgeOption)) - return dOptions +// setHostGatewayIP sets cfg.HostGatewayIP to the default bridge's IP if it is empty. +func setHostGatewayIP(config *config.Config, controller libnetwork.NetworkController) { + if config.HostGatewayIP != nil { + return + } + if n, err := controller.NetworkByName("bridge"); err == nil { + v4Info, v6Info := n.Info().IpamInfo() + var gateway net.IP + if len(v4Info) > 0 { + gateway = v4Info[0].Gateway.IP + } else if len(v6Info) > 0 { + gateway = v6Info[0].Gateway.IP + } + config.HostGatewayIP = gateway + } +} + +func driverOptions(config *config.Config) nwconfig.Option { + return nwconfig.OptionDriverConfig("bridge", options.Generic{ + netlabel.GenericData: options.Generic{ + "EnableIPForwarding": config.BridgeConfig.EnableIPForward, + "EnableIPTables": config.BridgeConfig.EnableIPTables, + "EnableIP6Tables": config.BridgeConfig.EnableIP6Tables, + "EnableUserlandProxy": config.BridgeConfig.EnableUserlandProxy, + "UserlandProxyPath": config.BridgeConfig.UserlandProxyPath, + }, + }) } func initBridgeDriver(controller libnetwork.NetworkController, config *config.Config) error { @@ -881,12 +959,7 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *config.Co netOption[bridge.DefaultBindingIP] = config.BridgeConfig.DefaultIP.String() } - var ( - ipamV4Conf *libnetwork.IpamConf - ipamV6Conf *libnetwork.IpamConf - ) - - ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} + ipamV4Conf := &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} nwList, nw6List, err := netutils.ElectInterfaceAddresses(bridgeName) if err != nil { @@ -915,11 +988,11 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *config.Co } if config.BridgeConfig.IP != "" { - ipamV4Conf.PreferredPool = config.BridgeConfig.IP - ip, _, err := net.ParseCIDR(config.BridgeConfig.IP) + ip, ipNet, err := net.ParseCIDR(config.BridgeConfig.IP) if err != nil { return err } + ipamV4Conf.PreferredPool = ipNet.String() ipamV4Conf.Gateway = ip.String() } else if bridgeName == bridge.DefaultBridgeName && ipamV4Conf.PreferredPool != "" { logrus.Infof("Default bridge (%s) is assigned with an IP address %s. Daemon option --bip can be used to set a preferred IP address", bridgeName, ipamV4Conf.PreferredPool) @@ -938,8 +1011,14 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *config.Co ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.BridgeConfig.DefaultGatewayIPv4.String() } - var deferIPv6Alloc bool - if config.BridgeConfig.FixedCIDRv6 != "" { + var ( + deferIPv6Alloc bool + ipamV6Conf *libnetwork.IpamConf + ) + + if config.BridgeConfig.EnableIPv6 && config.BridgeConfig.FixedCIDRv6 == "" { + return errdefs.InvalidParameter(errors.New("IPv6 is enabled for the default bridge, but no subnet is configured. Specify an IPv6 subnet using --fixed-cidr-v6")) + } else if config.BridgeConfig.FixedCIDRv6 != "" { _, fCIDRv6, err := net.ParseCIDR(config.BridgeConfig.FixedCIDRv6) if err != nil { return err @@ -954,10 +1033,10 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *config.Co ones, _ := fCIDRv6.Mask.Size() deferIPv6Alloc = ones <= 80 - if ipamV6Conf == nil { - ipamV6Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)} + ipamV6Conf = &libnetwork.IpamConf{ + AuxAddresses: make(map[string]string), + PreferredPool: fCIDRv6.String(), } - ipamV6Conf.PreferredPool = fCIDRv6.String() // In case the --fixed-cidr-v6 is specified and the current docker0 bridge IPv6 // address belongs to the same network, we need to inform libnetwork about it, so @@ -1120,11 +1199,11 @@ func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) logrus.Warn("User namespaces: root cannot be remapped with itself; user namespaces are OFF") return &idtools.IdentityMapping{}, nil } - logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname) + logrus.Infof("User namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s", username) // update remapped root setting now that we have resolved them to actual names config.RemappedRoot = fmt.Sprintf("%s:%s", username, groupname) - mappings, err := idtools.NewIdentityMapping(username, groupname) + mappings, err := idtools.NewIdentityMapping(username) if err != nil { return nil, errors.Wrap(err, "Can't create ID mappings") } @@ -1133,7 +1212,7 @@ func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) return &idtools.IdentityMapping{}, nil } -func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools.Identity) error { +func setupDaemonRoot(config *config.Config, rootDir string, remappedRoot idtools.Identity) error { config.Root = rootDir // the docker root metadata directory needs to have execute permissions for all users (g+x,o+x) // so that syscalls executing as non-root, operating on subdirectories of the graph root @@ -1153,15 +1232,21 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools } } + id := idtools.Identity{UID: idtools.CurrentIdentity().UID, GID: remappedRoot.GID} + // First make sure the current root dir has the correct perms. + if err := idtools.MkdirAllAndChown(config.Root, 0710, id); err != nil { + return errors.Wrapf(err, "could not create or set daemon root permissions: %s", config.Root) + } + // if user namespaces are enabled we will create a subtree underneath the specified root // with any/all specified remapped root uid/gid options on the daemon creating // a new subdirectory with ownership set to the remapped uid/gid (so as to allow // `chdir()` to work for containers namespaced to that uid/gid) if config.RemappedRoot != "" { - config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", rootIdentity.UID, rootIdentity.GID)) + config.Root = filepath.Join(rootDir, fmt.Sprintf("%d.%d", remappedRoot.UID, remappedRoot.GID)) logrus.Debugf("Creating user namespaced daemon root: %s", config.Root) // Create the root directory if it doesn't exist - if err := idtools.MkdirAllAndChown(config.Root, 0700, rootIdentity); err != nil { + if err := idtools.MkdirAllAndChown(config.Root, 0710, id); err != nil { return fmt.Errorf("Cannot create daemon root: %s: %v", config.Root, err) } // we also need to verify that any pre-existing directories in the path to @@ -1174,7 +1259,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools if dirPath == "/" { break } - if !idtools.CanAccess(dirPath, rootIdentity) { + if !idtools.CanAccess(dirPath, remappedRoot) { return fmt.Errorf("a subdirectory in your graphroot path (%s) restricts access to the remapped root uid/gid; please fix by allowing 'o+x' permissions on existing directories", config.Root) } } @@ -1187,7 +1272,7 @@ func setupDaemonRoot(config *config.Config, rootDir string, rootIdentity idtools } func setupDaemonRootPropagation(cfg *config.Config) error { - rootParentMount, options, err := getSourceMount(cfg.Root) + rootParentMount, mountOptions, err := getSourceMount(cfg.Root) if err != nil { return errors.Wrap(err, "error getting daemon root's parent mount") } @@ -1203,7 +1288,7 @@ func setupDaemonRootPropagation(cfg *config.Config) error { } }() - if hasMountinfoOption(options, sharedPropagationOption, slavePropagationOption) { + if hasMountInfoOption(mountOptions, sharedPropagationOption, slavePropagationOption) { cleanupOldFile = true return nil } @@ -1219,7 +1304,11 @@ func setupDaemonRootPropagation(cfg *config.Config) error { return nil } - if err := ioutil.WriteFile(cleanupFile, nil, 0600); err != nil { + if err := os.MkdirAll(filepath.Dir(cleanupFile), 0700); err != nil { + return errors.Wrap(err, "error creating dir to store mount cleanup file") + } + + if err := os.WriteFile(cleanupFile, nil, 0600); err != nil { return errors.Wrap(err, "error writing file to signal mount cleanup on shutdown") } return nil @@ -1244,12 +1333,26 @@ func (daemon *Daemon) registerLinks(container *container.Container, hostConfig * } child, err := daemon.GetContainer(name) if err != nil { + if errdefs.IsNotFound(err) { + // Trying to link to a non-existing container is not valid, and + // should return an "invalid parameter" error. Returning a "not + // found" error here would make the client report the container's + // image could not be found (see moby/moby#39823) + err = errdefs.InvalidParameter(err) + } return errors.Wrapf(err, "could not get container for %s", name) } for child.HostConfig.NetworkMode.IsContainer() { parts := strings.SplitN(string(child.HostConfig.NetworkMode), ":", 2) child, err = daemon.GetContainer(parts[1]) if err != nil { + if errdefs.IsNotFound(err) { + // Trying to link to a non-existing container is not valid, and + // should return an "invalid parameter" error. Returning a "not + // found" error here would make the client report the container's + // image could not be found (see moby/moby#39823) + err = errdefs.InvalidParameter(err) + } return errors.Wrapf(err, "Could not get container for %s", parts[1]) } } @@ -1279,7 +1382,7 @@ func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container return daemon.Unmount(container) } -func copyBlkioEntry(entries []*containerd_cgroups.BlkIOEntry) []types.BlkioStatEntry { +func copyBlkioEntry(entries []*statsV1.BlkIOEntry) []types.BlkioStatEntry { out := make([]types.BlkioStatEntry, len(entries)) for i, re := range entries { out[i] = types.BlkioStatEntry{ @@ -1306,6 +1409,17 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { s := &types.StatsJSON{} s.Read = cs.Read stats := cs.Metrics + switch t := stats.(type) { + case *statsV1.Metrics: + return daemon.statsV1(s, t) + case *statsV2.Metrics: + return daemon.statsV2(s, t) + default: + return nil, errors.Errorf("unexpected type of metrics %+v", t) + } +} + +func (daemon *Daemon) statsV1(s *types.StatsJSON, stats *statsV1.Metrics) (*types.StatsJSON, error) { if stats.Blkio != nil { s.BlkioStats = types.BlkioStats{ IoServiceBytesRecursive: copyBlkioEntry(stats.Blkio.IoServiceBytesRecursive), @@ -1335,40 +1449,40 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { } if stats.Memory != nil { - raw := make(map[string]uint64) - raw["cache"] = stats.Memory.Cache - raw["rss"] = stats.Memory.RSS - raw["rss_huge"] = stats.Memory.RSSHuge - raw["mapped_file"] = stats.Memory.MappedFile - raw["dirty"] = stats.Memory.Dirty - raw["writeback"] = stats.Memory.Writeback - raw["pgpgin"] = stats.Memory.PgPgIn - raw["pgpgout"] = stats.Memory.PgPgOut - raw["pgfault"] = stats.Memory.PgFault - raw["pgmajfault"] = stats.Memory.PgMajFault - raw["inactive_anon"] = stats.Memory.InactiveAnon - raw["active_anon"] = stats.Memory.ActiveAnon - raw["inactive_file"] = stats.Memory.InactiveFile - raw["active_file"] = stats.Memory.ActiveFile - raw["unevictable"] = stats.Memory.Unevictable - raw["hierarchical_memory_limit"] = stats.Memory.HierarchicalMemoryLimit - raw["hierarchical_memsw_limit"] = stats.Memory.HierarchicalSwapLimit - raw["total_cache"] = stats.Memory.TotalCache - raw["total_rss"] = stats.Memory.TotalRSS - raw["total_rss_huge"] = stats.Memory.TotalRSSHuge - raw["total_mapped_file"] = stats.Memory.TotalMappedFile - raw["total_dirty"] = stats.Memory.TotalDirty - raw["total_writeback"] = stats.Memory.TotalWriteback - raw["total_pgpgin"] = stats.Memory.TotalPgPgIn - raw["total_pgpgout"] = stats.Memory.TotalPgPgOut - raw["total_pgfault"] = stats.Memory.TotalPgFault - raw["total_pgmajfault"] = stats.Memory.TotalPgMajFault - raw["total_inactive_anon"] = stats.Memory.TotalInactiveAnon - raw["total_active_anon"] = stats.Memory.TotalActiveAnon - raw["total_inactive_file"] = stats.Memory.TotalInactiveFile - raw["total_active_file"] = stats.Memory.TotalActiveFile - raw["total_unevictable"] = stats.Memory.TotalUnevictable - + raw := map[string]uint64{ + "cache": stats.Memory.Cache, + "rss": stats.Memory.RSS, + "rss_huge": stats.Memory.RSSHuge, + "mapped_file": stats.Memory.MappedFile, + "dirty": stats.Memory.Dirty, + "writeback": stats.Memory.Writeback, + "pgpgin": stats.Memory.PgPgIn, + "pgpgout": stats.Memory.PgPgOut, + "pgfault": stats.Memory.PgFault, + "pgmajfault": stats.Memory.PgMajFault, + "inactive_anon": stats.Memory.InactiveAnon, + "active_anon": stats.Memory.ActiveAnon, + "inactive_file": stats.Memory.InactiveFile, + "active_file": stats.Memory.ActiveFile, + "unevictable": stats.Memory.Unevictable, + "hierarchical_memory_limit": stats.Memory.HierarchicalMemoryLimit, + "hierarchical_memsw_limit": stats.Memory.HierarchicalSwapLimit, + "total_cache": stats.Memory.TotalCache, + "total_rss": stats.Memory.TotalRSS, + "total_rss_huge": stats.Memory.TotalRSSHuge, + "total_mapped_file": stats.Memory.TotalMappedFile, + "total_dirty": stats.Memory.TotalDirty, + "total_writeback": stats.Memory.TotalWriteback, + "total_pgpgin": stats.Memory.TotalPgPgIn, + "total_pgpgout": stats.Memory.TotalPgPgOut, + "total_pgfault": stats.Memory.TotalPgFault, + "total_pgmajfault": stats.Memory.TotalPgMajFault, + "total_inactive_anon": stats.Memory.TotalInactiveAnon, + "total_active_anon": stats.Memory.TotalActiveAnon, + "total_inactive_file": stats.Memory.TotalInactiveFile, + "total_active_file": stats.Memory.TotalActiveFile, + "total_unevictable": stats.Memory.TotalUnevictable, + } if stats.Memory.Usage != nil { s.MemoryStats = types.MemoryStats{ Stats: raw, @@ -1399,6 +1513,108 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { return s, nil } +func (daemon *Daemon) statsV2(s *types.StatsJSON, stats *statsV2.Metrics) (*types.StatsJSON, error) { + if stats.Io != nil { + var isbr []types.BlkioStatEntry + for _, re := range stats.Io.Usage { + isbr = append(isbr, + types.BlkioStatEntry{ + Major: re.Major, + Minor: re.Minor, + Op: "read", + Value: re.Rbytes, + }, + types.BlkioStatEntry{ + Major: re.Major, + Minor: re.Minor, + Op: "write", + Value: re.Wbytes, + }, + ) + } + s.BlkioStats = types.BlkioStats{ + IoServiceBytesRecursive: isbr, + // Other fields are unsupported + } + } + + if stats.CPU != nil { + s.CPUStats = types.CPUStats{ + CPUUsage: types.CPUUsage{ + TotalUsage: stats.CPU.UsageUsec * 1000, + // PercpuUsage is not supported + UsageInKernelmode: stats.CPU.SystemUsec * 1000, + UsageInUsermode: stats.CPU.UserUsec * 1000, + }, + ThrottlingData: types.ThrottlingData{ + Periods: stats.CPU.NrPeriods, + ThrottledPeriods: stats.CPU.NrThrottled, + ThrottledTime: stats.CPU.ThrottledUsec * 1000, + }, + } + } + + if stats.Memory != nil { + s.MemoryStats = types.MemoryStats{ + // Stats is not compatible with v1 + Stats: map[string]uint64{ + "anon": stats.Memory.Anon, + "file": stats.Memory.File, + "kernel_stack": stats.Memory.KernelStack, + "slab": stats.Memory.Slab, + "sock": stats.Memory.Sock, + "shmem": stats.Memory.Shmem, + "file_mapped": stats.Memory.FileMapped, + "file_dirty": stats.Memory.FileDirty, + "file_writeback": stats.Memory.FileWriteback, + "anon_thp": stats.Memory.AnonThp, + "inactive_anon": stats.Memory.InactiveAnon, + "active_anon": stats.Memory.ActiveAnon, + "inactive_file": stats.Memory.InactiveFile, + "active_file": stats.Memory.ActiveFile, + "unevictable": stats.Memory.Unevictable, + "slab_reclaimable": stats.Memory.SlabReclaimable, + "slab_unreclaimable": stats.Memory.SlabUnreclaimable, + "pgfault": stats.Memory.Pgfault, + "pgmajfault": stats.Memory.Pgmajfault, + "workingset_refault": stats.Memory.WorkingsetRefault, + "workingset_activate": stats.Memory.WorkingsetActivate, + "workingset_nodereclaim": stats.Memory.WorkingsetNodereclaim, + "pgrefill": stats.Memory.Pgrefill, + "pgscan": stats.Memory.Pgscan, + "pgsteal": stats.Memory.Pgsteal, + "pgactivate": stats.Memory.Pgactivate, + "pgdeactivate": stats.Memory.Pgdeactivate, + "pglazyfree": stats.Memory.Pglazyfree, + "pglazyfreed": stats.Memory.Pglazyfreed, + "thp_fault_alloc": stats.Memory.ThpFaultAlloc, + "thp_collapse_alloc": stats.Memory.ThpCollapseAlloc, + }, + Usage: stats.Memory.Usage, + // MaxUsage is not supported + Limit: stats.Memory.UsageLimit, + } + // if the container does not set memory limit, use the machineMemory + if s.MemoryStats.Limit > daemon.machineMemory && daemon.machineMemory > 0 { + s.MemoryStats.Limit = daemon.machineMemory + } + if stats.MemoryEvents != nil { + // Failcnt is set to the "oom" field of the "memory.events" file. + // See https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html + s.MemoryStats.Failcnt = stats.MemoryEvents.Oom + } + } + + if stats.Pids != nil { + s.PidsStats = types.PidsStats{ + Current: stats.Pids.Current, + Limit: stats.Pids.Limit, + } + } + + return s, nil +} + // setDefaultIsolation determines the default isolation mode for the // daemon to run in. This is only applicable on Windows func (daemon *Daemon) setDefaultIsolation() error { @@ -1438,7 +1654,7 @@ func setMayDetachMounts() error { // Setting may_detach_mounts does not work in an // unprivileged container. Ignore the error, but log // it if we appear not to be in that situation. - if !rsystem.RunningInUserNS() { + if !userns.RunningInUserNS() { logrus.Debugf("Permission denied writing %q to /proc/sys/fs/may_detach_mounts", "1") } return nil @@ -1447,6 +1663,9 @@ func setMayDetachMounts() error { } func setupOOMScoreAdj(score int) error { + if score == 0 { + return nil + } f, err := os.OpenFile("/proc/self/oom_score_adj", os.O_WRONLY, 0) if err != nil { return err @@ -1458,7 +1677,7 @@ func setupOOMScoreAdj(score int) error { // Setting oom_score_adj does not work in an // unprivileged container. Ignore the error, but log // it if we appear not to be in that situation. - if !rsystem.RunningInUserNS() { + if !userns.RunningInUserNS() { logrus.Debugf("Permission denied writing %q to /proc/self/oom_score_adj", stringScore) } return nil @@ -1467,57 +1686,74 @@ func setupOOMScoreAdj(score int) error { return err } -func (daemon *Daemon) initCgroupsPath(path string) error { +func (daemon *Daemon) initCPURtController(mnt, path string) error { if path == "/" || path == "." { return nil } - if daemon.configStore.CPURealtimePeriod == 0 && daemon.configStore.CPURealtimeRuntime == 0 { - return nil - } - // Recursively create cgroup to ensure that the system and all parent cgroups have values set // for the period and runtime as this limits what the children can be set to. - daemon.initCgroupsPath(filepath.Dir(path)) - - mnt, root, err := cgroups.FindCgroupMountpointAndRoot("cpu") - if err != nil { + if err := daemon.initCPURtController(mnt, filepath.Dir(path)); err != nil { return err } - // When docker is run inside docker, the root is based of the host cgroup. - // Should this be handled in runc/libcontainer/cgroups ? - if strings.HasPrefix(root, "/docker/") { - root = "/" - } - path = filepath.Join(mnt, root, path) - sysinfo := sysinfo.New(true) - if err := maybeCreateCPURealTimeFile(sysinfo.CPURealtimePeriod, daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil { + path = filepath.Join(mnt, path) + if err := os.MkdirAll(path, 0755); err != nil { return err } - return maybeCreateCPURealTimeFile(sysinfo.CPURealtimeRuntime, daemon.configStore.CPURealtimeRuntime, "cpu.rt_runtime_us", path) + if err := maybeCreateCPURealTimeFile(daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil { + return err + } + return maybeCreateCPURealTimeFile(daemon.configStore.CPURealtimeRuntime, "cpu.rt_runtime_us", path) } -func maybeCreateCPURealTimeFile(sysinfoPresent bool, configValue int64, file string, path string) error { - if sysinfoPresent && configValue != 0 { - if err := os.MkdirAll(path, 0755); err != nil { - return err - } - if err := ioutil.WriteFile(filepath.Join(path, file), []byte(strconv.FormatInt(configValue, 10)), 0700); err != nil { - return err - } +func maybeCreateCPURealTimeFile(configValue int64, file string, path string) error { + if configValue == 0 { + return nil } - return nil + return os.WriteFile(filepath.Join(path, file), []byte(strconv.FormatInt(configValue, 10)), 0700) } func (daemon *Daemon) setupSeccompProfile() error { - if daemon.configStore.SeccompProfile != "" { - daemon.seccompProfilePath = daemon.configStore.SeccompProfile - b, err := ioutil.ReadFile(daemon.configStore.SeccompProfile) + switch profile := daemon.configStore.SeccompProfile; profile { + case "", config.SeccompProfileDefault: + daemon.seccompProfilePath = config.SeccompProfileDefault + case config.SeccompProfileUnconfined: + daemon.seccompProfilePath = config.SeccompProfileUnconfined + default: + daemon.seccompProfilePath = profile + b, err := os.ReadFile(profile) if err != nil { - return fmt.Errorf("opening seccomp profile (%s) failed: %v", daemon.configStore.SeccompProfile, err) + return fmt.Errorf("opening seccomp profile (%s) failed: %v", profile, err) } daemon.seccompProfile = b } return nil } + +// RawSysInfo returns *sysinfo.SysInfo . +func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo { + var siOpts []sysinfo.Opt + if daemon.getCgroupDriver() == cgroupSystemdDriver { + if euid := os.Getenv("ROOTLESSKIT_PARENT_EUID"); euid != "" { + siOpts = append(siOpts, sysinfo.WithCgroup2GroupPath("/user.slice/user-"+euid+".slice")) + } + } + return sysinfo.New(siOpts...) +} + +func recursiveUnmount(target string) error { + return mount.RecursiveUnmount(target) +} + +func (daemon *Daemon) initLibcontainerd(ctx context.Context) error { + var err error + daemon.containerd, err = remote.NewClient( + ctx, + daemon.containerdCli, + filepath.Join(daemon.configStore.ExecRoot, "containerd"), + daemon.configStore.ContainerdNamespace, + daemon, + ) + return err +} diff --git a/daemon/daemon_unix_test.go b/daemon/daemon_unix_test.go index 36c6030988572..9c1b146ca19bb 100644 --- a/daemon/daemon_unix_test.go +++ b/daemon/daemon_unix_test.go @@ -1,16 +1,22 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" import ( "errors" - "io/ioutil" "os" + "path/filepath" "testing" + "github.com/docker/docker/api/types/blkiodev" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/daemon/config" + "github.com/docker/docker/pkg/sysinfo" + "golang.org/x/sys/unix" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) type fakeContainerGetter struct { @@ -18,11 +24,11 @@ type fakeContainerGetter struct { } func (f *fakeContainerGetter) GetContainer(cid string) (*container.Container, error) { - container, ok := f.containers[cid] + ctr, ok := f.containers[cid] if !ok { return nil, errors.New("container not found") } - return container, nil + return ctr, nil } // Unix test as uses settings which are not available on Windows @@ -53,7 +59,7 @@ func TestAdjustSharedNamespaceContainerName(t *testing.T) { // Unix test as uses settings which are not available on Windows func TestAdjustCPUShares(t *testing.T) { - tmp, err := ioutil.TempDir("", "docker-daemon-unix-test-") + tmp, err := os.MkdirTemp("", "docker-daemon-unix-test-") if err != nil { t.Fatal(err) } @@ -62,6 +68,7 @@ func TestAdjustCPUShares(t *testing.T) { repository: tmp, root: tmp, } + muteLogs() hostConfig := &containertypes.HostConfig{ Resources: containertypes.Resources{CPUShares: linuxMinCPUShares - 1}, @@ -92,7 +99,7 @@ func TestAdjustCPUShares(t *testing.T) { // Unix test as uses settings which are not available on Windows func TestAdjustCPUSharesNoAdjustment(t *testing.T) { - tmp, err := ioutil.TempDir("", "docker-daemon-unix-test-") + tmp, err := os.MkdirTemp("", "docker-daemon-unix-test-") if err != nil { t.Fatal(err) } @@ -131,85 +138,85 @@ func TestAdjustCPUSharesNoAdjustment(t *testing.T) { // Unix test as uses settings which are not available on Windows func TestParseSecurityOptWithDeprecatedColon(t *testing.T) { - container := &container.Container{} - config := &containertypes.HostConfig{} + ctr := &container.Container{} + cfg := &containertypes.HostConfig{} // test apparmor - config.SecurityOpt = []string{"apparmor=test_profile"} - if err := parseSecurityOpt(container, config); err != nil { + cfg.SecurityOpt = []string{"apparmor=test_profile"} + if err := parseSecurityOpt(ctr, cfg); err != nil { t.Fatalf("Unexpected parseSecurityOpt error: %v", err) } - if container.AppArmorProfile != "test_profile" { - t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile) + if ctr.AppArmorProfile != "test_profile" { + t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", ctr.AppArmorProfile) } // test seccomp sp := "/path/to/seccomp_test.json" - config.SecurityOpt = []string{"seccomp=" + sp} - if err := parseSecurityOpt(container, config); err != nil { + cfg.SecurityOpt = []string{"seccomp=" + sp} + if err := parseSecurityOpt(ctr, cfg); err != nil { t.Fatalf("Unexpected parseSecurityOpt error: %v", err) } - if container.SeccompProfile != sp { - t.Fatalf("Unexpected AppArmorProfile, expected: %q, got %q", sp, container.SeccompProfile) + if ctr.SeccompProfile != sp { + t.Fatalf("Unexpected AppArmorProfile, expected: %q, got %q", sp, ctr.SeccompProfile) } // test valid label - config.SecurityOpt = []string{"label=user:USER"} - if err := parseSecurityOpt(container, config); err != nil { + cfg.SecurityOpt = []string{"label=user:USER"} + if err := parseSecurityOpt(ctr, cfg); err != nil { t.Fatalf("Unexpected parseSecurityOpt error: %v", err) } // test invalid label - config.SecurityOpt = []string{"label"} - if err := parseSecurityOpt(container, config); err == nil { + cfg.SecurityOpt = []string{"label"} + if err := parseSecurityOpt(ctr, cfg); err == nil { t.Fatal("Expected parseSecurityOpt error, got nil") } // test invalid opt - config.SecurityOpt = []string{"test"} - if err := parseSecurityOpt(container, config); err == nil { + cfg.SecurityOpt = []string{"test"} + if err := parseSecurityOpt(ctr, cfg); err == nil { t.Fatal("Expected parseSecurityOpt error, got nil") } } func TestParseSecurityOpt(t *testing.T) { - container := &container.Container{} - config := &containertypes.HostConfig{} + ctr := &container.Container{} + cfg := &containertypes.HostConfig{} // test apparmor - config.SecurityOpt = []string{"apparmor=test_profile"} - if err := parseSecurityOpt(container, config); err != nil { + cfg.SecurityOpt = []string{"apparmor=test_profile"} + if err := parseSecurityOpt(ctr, cfg); err != nil { t.Fatalf("Unexpected parseSecurityOpt error: %v", err) } - if container.AppArmorProfile != "test_profile" { - t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile) + if ctr.AppArmorProfile != "test_profile" { + t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", ctr.AppArmorProfile) } // test seccomp sp := "/path/to/seccomp_test.json" - config.SecurityOpt = []string{"seccomp=" + sp} - if err := parseSecurityOpt(container, config); err != nil { + cfg.SecurityOpt = []string{"seccomp=" + sp} + if err := parseSecurityOpt(ctr, cfg); err != nil { t.Fatalf("Unexpected parseSecurityOpt error: %v", err) } - if container.SeccompProfile != sp { - t.Fatalf("Unexpected SeccompProfile, expected: %q, got %q", sp, container.SeccompProfile) + if ctr.SeccompProfile != sp { + t.Fatalf("Unexpected SeccompProfile, expected: %q, got %q", sp, ctr.SeccompProfile) } // test valid label - config.SecurityOpt = []string{"label=user:USER"} - if err := parseSecurityOpt(container, config); err != nil { + cfg.SecurityOpt = []string{"label=user:USER"} + if err := parseSecurityOpt(ctr, cfg); err != nil { t.Fatalf("Unexpected parseSecurityOpt error: %v", err) } // test invalid label - config.SecurityOpt = []string{"label"} - if err := parseSecurityOpt(container, config); err == nil { + cfg.SecurityOpt = []string{"label"} + if err := parseSecurityOpt(ctr, cfg); err == nil { t.Fatal("Expected parseSecurityOpt error, got nil") } // test invalid opt - config.SecurityOpt = []string{"test"} - if err := parseSecurityOpt(container, config); err == nil { + cfg.SecurityOpt = []string{"test"} + if err := parseSecurityOpt(ctr, cfg); err == nil { t.Fatal("Expected parseSecurityOpt error, got nil") } } @@ -218,28 +225,28 @@ func TestParseNNPSecurityOptions(t *testing.T) { daemon := &Daemon{ configStore: &config.Config{NoNewPrivileges: true}, } - container := &container.Container{} - config := &containertypes.HostConfig{} + ctr := &container.Container{} + cfg := &containertypes.HostConfig{} // test NNP when "daemon:true" and "no-new-privileges=false"" - config.SecurityOpt = []string{"no-new-privileges=false"} + cfg.SecurityOpt = []string{"no-new-privileges=false"} - if err := daemon.parseSecurityOpt(container, config); err != nil { + if err := daemon.parseSecurityOpt(ctr, cfg); err != nil { t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err) } - if container.NoNewPrivileges { - t.Fatalf("container.NoNewPrivileges should be FALSE: %v", container.NoNewPrivileges) + if ctr.NoNewPrivileges { + t.Fatalf("container.NoNewPrivileges should be FALSE: %v", ctr.NoNewPrivileges) } // test NNP when "daemon:false" and "no-new-privileges=true"" daemon.configStore.NoNewPrivileges = false - config.SecurityOpt = []string{"no-new-privileges=true"} + cfg.SecurityOpt = []string{"no-new-privileges=true"} - if err := daemon.parseSecurityOpt(container, config); err != nil { + if err := daemon.parseSecurityOpt(ctr, cfg); err != nil { t.Fatalf("Unexpected daemon.parseSecurityOpt error: %v", err) } - if !container.NoNewPrivileges { - t.Fatalf("container.NoNewPrivileges should be TRUE: %v", container.NoNewPrivileges) + if !ctr.NoNewPrivileges { + t.Fatalf("container.NoNewPrivileges should be TRUE: %v", ctr.NoNewPrivileges) } } @@ -266,3 +273,169 @@ func TestNetworkOptions(t *testing.T) { t.Fatal("Expected networkOptions error, got nil") } } + +func TestVerifyPlatformContainerResources(t *testing.T) { + t.Parallel() + var ( + no = false + yes = true + ) + + withMemoryLimit := func(si *sysinfo.SysInfo) { + si.MemoryLimit = true + } + withSwapLimit := func(si *sysinfo.SysInfo) { + si.SwapLimit = true + } + withOomKillDisable := func(si *sysinfo.SysInfo) { + si.OomKillDisable = true + } + + tests := []struct { + name string + resources containertypes.Resources + sysInfo sysinfo.SysInfo + update bool + expectedWarnings []string + }{ + { + name: "no-oom-kill-disable", + resources: containertypes.Resources{}, + sysInfo: sysInfo(t, withMemoryLimit), + expectedWarnings: []string{}, + }, + { + name: "oom-kill-disable-disabled", + resources: containertypes.Resources{ + OomKillDisable: &no, + }, + sysInfo: sysInfo(t, withMemoryLimit), + expectedWarnings: []string{}, + }, + { + name: "oom-kill-disable-not-supported", + resources: containertypes.Resources{ + OomKillDisable: &yes, + }, + sysInfo: sysInfo(t, withMemoryLimit), + expectedWarnings: []string{ + "Your kernel does not support OomKillDisable. OomKillDisable discarded.", + }, + }, + { + name: "oom-kill-disable-without-memory-constraints", + resources: containertypes.Resources{ + OomKillDisable: &yes, + Memory: 0, + }, + sysInfo: sysInfo(t, withMemoryLimit, withOomKillDisable, withSwapLimit), + expectedWarnings: []string{ + "OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.", + }, + }, + { + name: "oom-kill-disable-with-memory-constraints-but-no-memory-limit-support", + resources: containertypes.Resources{ + OomKillDisable: &yes, + Memory: linuxMinMemory, + }, + sysInfo: sysInfo(t, withOomKillDisable), + expectedWarnings: []string{ + "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.", + "OOM killer is disabled for the container, but no memory limit is set, this can result in the system running out of resources.", + }, + }, + { + name: "oom-kill-disable-with-memory-constraints", + resources: containertypes.Resources{ + OomKillDisable: &yes, + Memory: linuxMinMemory, + }, + sysInfo: sysInfo(t, withMemoryLimit, withOomKillDisable, withSwapLimit), + expectedWarnings: []string{}, + }, + } + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + warnings, err := verifyPlatformContainerResources(&tc.resources, &tc.sysInfo, tc.update) + assert.NilError(t, err) + for _, w := range tc.expectedWarnings { + assert.Assert(t, is.Contains(warnings, w)) + } + }) + } +} + +func sysInfo(t *testing.T, opts ...func(*sysinfo.SysInfo)) sysinfo.SysInfo { + t.Helper() + si := sysinfo.SysInfo{} + + for _, opt := range opts { + opt(&si) + } + + if si.OomKillDisable { + t.Log(t.Name(), "OOM disable supported") + } + return si +} + +const ( + // prepare major 0x1FD(509 in decimal) and minor 0x130(304) + DEVNO = 0x11FD30 + MAJOR = 509 + MINOR = 304 + WEIGHT = 1024 +) + +func deviceTypeMock(t *testing.T, testAndCheck func(string)) { + if os.Getuid() != 0 { + t.Skip("root required") // for mknod + } + + t.Parallel() + + tempDir, err := os.MkdirTemp("", "tempDevDir"+t.Name()) + assert.NilError(t, err, "create temp file") + tempFile := filepath.Join(tempDir, "dev") + + defer os.RemoveAll(tempDir) + + if err = unix.Mknod(tempFile, unix.S_IFCHR, DEVNO); err != nil { + t.Fatalf("mknod error %s(%x): %v", tempFile, DEVNO, err) + } + + testAndCheck(tempFile) +} + +func TestGetBlkioWeightDevices(t *testing.T) { + deviceTypeMock(t, func(tempFile string) { + mockResource := containertypes.Resources{ + BlkioWeightDevice: []*blkiodev.WeightDevice{{Path: tempFile, Weight: WEIGHT}}, + } + + weightDevs, err := getBlkioWeightDevices(mockResource) + + assert.NilError(t, err, "getBlkioWeightDevices") + assert.Check(t, is.Len(weightDevs, 1), "getBlkioWeightDevices") + assert.Check(t, weightDevs[0].Major == MAJOR, "get major device type") + assert.Check(t, weightDevs[0].Minor == MINOR, "get minor device type") + assert.Check(t, *weightDevs[0].Weight == WEIGHT, "get device weight") + }) +} + +func TestGetBlkioThrottleDevices(t *testing.T) { + deviceTypeMock(t, func(tempFile string) { + mockDevs := []*blkiodev.ThrottleDevice{{Path: tempFile, Rate: WEIGHT}} + + retDevs, err := getBlkioThrottleDevices(mockDevs) + + assert.NilError(t, err, "getBlkioThrottleDevices") + assert.Check(t, is.Len(retDevs, 1), "getBlkioThrottleDevices") + assert.Check(t, retDevs[0].Major == MAJOR, "get major device type") + assert.Check(t, retDevs[0].Minor == MINOR, "get minor device type") + assert.Check(t, retDevs[0].Rate == WEIGHT, "get device rate") + }) +} diff --git a/daemon/daemon_unsupported.go b/daemon/daemon_unsupported.go index 6d8ac6224bd05..7666d14f6ee60 100644 --- a/daemon/daemon_unsupported.go +++ b/daemon/daemon_unsupported.go @@ -1,9 +1,19 @@ +//go:build !linux && !freebsd && !windows // +build !linux,!freebsd,!windows package daemon // import "github.com/docker/docker/daemon" -import "github.com/docker/docker/daemon/config" + +import ( + "github.com/docker/docker/daemon/config" + "github.com/docker/docker/pkg/sysinfo" +) const platformSupported = false func setupResolvConf(config *config.Config) { } + +// RawSysInfo returns *sysinfo.SysInfo . +func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo { + return sysinfo.New() +} diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index e534d7ecccbf6..b9e932f0ec1b5 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -3,28 +3,32 @@ package daemon // import "github.com/docker/docker/daemon" import ( "context" "fmt" + "math" "path/filepath" + "runtime" "strings" "github.com/Microsoft/hcsshim" + "github.com/Microsoft/hcsshim/osversion" "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/daemon/config" + "github.com/docker/docker/libcontainerd/local" + "github.com/docker/docker/libcontainerd/remote" + "github.com/docker/docker/libnetwork" + nwconfig "github.com/docker/docker/libnetwork/config" + "github.com/docker/docker/libnetwork/datastore" + winlibnetwork "github.com/docker/docker/libnetwork/drivers/windows" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/options" "github.com/docker/docker/pkg/containerfs" - "github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/platform" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" "github.com/docker/docker/runconfig" - "github.com/docker/libnetwork" - nwconfig "github.com/docker/libnetwork/config" - "github.com/docker/libnetwork/datastore" - winlibnetwork "github.com/docker/libnetwork/drivers/windows" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/options" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/windows" @@ -32,14 +36,23 @@ import ( ) const ( - defaultNetworkSpace = "172.16.0.0/12" + isWindows = true platformSupported = true windowsMinCPUShares = 1 windowsMaxCPUShares = 10000 windowsMinCPUPercent = 1 windowsMaxCPUPercent = 100 + + windowsV1RuntimeName = "com.docker.hcsshim.v1" + windowsV2RuntimeName = "io.containerd.runhcs.v1" ) +// Windows containers are much larger than Linux containers and each of them +// have > 20 system processes which why we use much smaller parallelism value. +func adjustParallelLimit(n int, limit int) int { + return int(math.Max(1, math.Floor(float64(runtime.NumCPU())*.8))) +} + // Windows has no concept of an execution state directory. So use config.Root here. func getPluginExecRoot(root string) string { return filepath.Join(root, "plugins") @@ -75,8 +88,8 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf return nil } -func verifyContainerResources(resources *containertypes.Resources, isHyperv bool) ([]string, error) { - warnings := []string{} +// verifyPlatformContainerResources performs platform-specific validation of the container's resource-configuration +func verifyPlatformContainerResources(resources *containertypes.Resources, isHyperv bool) (warnings []string, err error) { fixMemorySwappiness(resources) if !isHyperv { // The processor resource controls are mutually exclusive on @@ -85,18 +98,15 @@ func verifyContainerResources(resources *containertypes.Resources, isHyperv bool if resources.CPUCount > 0 { if resources.CPUShares > 0 { warnings = append(warnings, "Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded") - logrus.Warn("Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded") resources.CPUShares = 0 } if resources.CPUPercent > 0 { warnings = append(warnings, "Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded") - logrus.Warn("Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded") resources.CPUPercent = 0 } } else if resources.CPUShares > 0 { if resources.CPUPercent > 0 { warnings = append(warnings, "Conflicting options: CPU shares takes priority over CPU percent on Windows Server Containers. CPU percent discarded") - logrus.Warn("Conflicting options: CPU shares takes priority over CPU percent on Windows Server Containers. CPU percent discarded") resources.CPUPercent = 0 } } @@ -124,14 +134,12 @@ func verifyContainerResources(resources *containertypes.Resources, isHyperv bool return warnings, fmt.Errorf("range of CPUs is from 0.01 to %d.00, as there are only %d CPUs available", sysinfo.NumCPU(), sysinfo.NumCPU()) } - osv := system.GetOSVersion() - if resources.NanoCPUs > 0 && isHyperv && osv.Build < 16175 { + if resources.NanoCPUs > 0 && isHyperv && osversion.Build() < osversion.RS3 { leftoverNanoCPUs := resources.NanoCPUs % 1e9 if leftoverNanoCPUs != 0 && resources.NanoCPUs > 1e9 { resources.NanoCPUs = ((resources.NanoCPUs + 1e9/2) / 1e9) * 1e9 warningString := fmt.Sprintf("Your current OS version does not support Hyper-V containers with NanoCPUs greater than 1000000000 but not divisible by 1000000000. NanoCPUs rounded to %d", resources.NanoCPUs) warnings = append(warnings, warningString) - logrus.Warn(warningString) } } @@ -180,7 +188,7 @@ func verifyContainerResources(resources *containertypes.Resources, isHyperv bool if resources.OomKillDisable != nil && *resources.OomKillDisable { return warnings, fmt.Errorf("invalid option: Windows does not support OomKillDisable") } - if resources.PidsLimit != 0 { + if resources.PidsLimit != nil && *resources.PidsLimit != 0 { return warnings, fmt.Errorf("invalid option: Windows does not support PidsLimit") } if len(resources.Ulimits) != 0 { @@ -191,20 +199,21 @@ func verifyContainerResources(resources *containertypes.Resources, isHyperv bool // verifyPlatformContainerSettings performs platform-specific validation of the // hostconfig and config structures. -func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { - warnings := []string{} - osv := system.GetOSVersion() +func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.HostConfig, update bool) (warnings []string, err error) { + if hostConfig == nil { + return nil, nil + } hyperv := daemon.runAsHyperVContainer(hostConfig) // On RS5, we allow (but don't strictly support) process isolation on Client SKUs. // Prior to RS5, we don't allow process isolation on Client SKUs. // @engine maintainers. This block should not be removed. It partially enforces licensing - // restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this. - if !hyperv && system.IsWindowsClient() && osv.Build < 17763 { + // restrictions on Windows. Ping Microsoft folks if there are concerns or PRs to change this. + if !hyperv && system.IsWindowsClient() && osversion.Build() < osversion.RS5 { return warnings, fmt.Errorf("Windows client operating systems earlier than version 1809 can only run Hyper-V containers") } - w, err := verifyContainerResources(&hostConfig.Resources, hyperv) + w, err := verifyPlatformContainerResources(&hostConfig.Resources, hyperv) warnings = append(warnings, w...) return warnings, err } @@ -216,13 +225,12 @@ func verifyDaemonSettings(config *config.Config) error { // checkSystem validates platform-specific requirements func checkSystem() error { - // Validate the OS version. Note that docker.exe must be manifested for this + // Validate the OS version. Note that dockerd.exe must be manifested for this // call to return the correct version. - osv := system.GetOSVersion() - if osv.MajorVersion < 10 { + if osversion.Get().MajorVersion < 10 { return fmt.Errorf("This version of Windows does not support the docker daemon") } - if osv.Build < 14393 { + if osversion.Build() < osversion.RS1 { return fmt.Errorf("The docker daemon requires build 14393 or later of Windows Server 2016 or Windows 10") } @@ -343,8 +351,10 @@ func (daemon *Daemon) initNetworkController(config *config.Config, activeSandbox controller.WalkNetworks(s) drvOptions := make(map[string]string) - + nid := "" if n != nil { + nid = n.ID() + // global networks should not be deleted by local HNS if n.Info().Scope() == datastore.GlobalScope { continue @@ -389,7 +399,7 @@ func (daemon *Daemon) initNetworkController(config *config.Config, activeSandbox } v6Conf := []*libnetwork.IpamConf{} - _, err := controller.NewNetwork(strings.ToLower(v.Type), name, "", + _, err := controller.NewNetwork(strings.ToLower(v.Type), name, nid, libnetwork.NetworkOptionGeneric(options.Generic{ netlabel.GenericData: netOption, }), @@ -425,17 +435,10 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *config.Co if config.BridgeConfig.FixedCIDR != "" { subnetPrefix = config.BridgeConfig.FixedCIDR - } else { - // TP5 doesn't support properly detecting subnet - osv := system.GetOSVersion() - if osv.Build < 14360 { - subnetPrefix = defaultNetworkSpace - } } if subnetPrefix != "" { - ipamV4Conf := libnetwork.IpamConf{} - ipamV4Conf.PreferredPool = subnetPrefix + ipamV4Conf := libnetwork.IpamConf{PreferredPool: subnetPrefix} v4Conf := []*libnetwork.IpamConf{&ipamV4Conf} v6Conf := []*libnetwork.IpamConf{} ipamOption = libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil) @@ -469,6 +472,10 @@ func (daemon *Daemon) cleanupMounts() error { return nil } +func recursiveUnmount(_ string) error { + return nil +} + func setupRemappedRoot(config *config.Config) (*idtools.IdentityMapping, error) { return &idtools.IdentityMapping{}, nil } @@ -497,37 +504,26 @@ func (daemon *Daemon) runAsHyperVContainer(hostConfig *containertypes.HostConfig // conditionalMountOnStart is a platform specific helper function during the // container start to call mount. func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { - // Bail out now for Linux containers. We cannot mount the containers filesystem on the - // host as it is a non-Windows filesystem. - if system.LCOWSupported() && container.OS != "windows" { + if daemon.runAsHyperVContainer(container.HostConfig) { + // We do not mount if a Hyper-V container as it needs to be mounted inside the + // utility VM, not the host. return nil } - - // We do not mount if a Hyper-V container as it needs to be mounted inside the - // utility VM, not the host. - if !daemon.runAsHyperVContainer(container.HostConfig) { - return daemon.Mount(container) - } - return nil + return daemon.Mount(container) } // conditionalUnmountOnCleanup is a platform specific helper function called // during the cleanup of a container to unmount. func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error { - // Bail out now for Linux containers - if system.LCOWSupported() && container.OS != "windows" { + if daemon.runAsHyperVContainer(container.HostConfig) { + // We do not unmount if a Hyper-V container return nil } - - // We do not unmount if a Hyper-V container - if !daemon.runAsHyperVContainer(container.HostConfig) { - return daemon.Unmount(container) - } - return nil + return daemon.Unmount(container) } -func driverOptions(config *config.Config) []nwconfig.Option { - return []nwconfig.Option{} +func driverOptions(_ *config.Config) nwconfig.Option { + return nil } func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { @@ -556,7 +552,7 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { CPUUsage: types.CPUUsage{ TotalUsage: hcss.Processor.TotalRuntime100ns, UsageInKernelmode: hcss.Processor.RuntimeKernel100ns, - UsageInUsermode: hcss.Processor.RuntimeKernel100ns, + UsageInUsermode: hcss.Processor.RuntimeUser100ns, }, } @@ -595,10 +591,9 @@ func (daemon *Daemon) stats(c *container.Container) (*types.StatsJSON, error) { // daemon to run in. This is only applicable on Windows func (daemon *Daemon) setDefaultIsolation() error { daemon.defaultIsolation = containertypes.Isolation("process") - osv := system.GetOSVersion() // On client SKUs, default to Hyper-V. @engine maintainers. This - // should not be removed. Ping @jhowardmsft is there are PRs to + // should not be removed. Ping Microsoft folks is there are PRs to // to change this. if system.IsWindowsClient() { daemon.defaultIsolation = containertypes.Isolation("hyperv") @@ -619,10 +614,10 @@ func (daemon *Daemon) setDefaultIsolation() error { daemon.defaultIsolation = containertypes.Isolation("hyperv") } if containertypes.Isolation(val).IsProcess() { - if system.IsWindowsClient() && osv.Build < 17763 { + if system.IsWindowsClient() && osversion.Build() < osversion.RS5 { // On RS5, we allow (but don't strictly support) process isolation on Client SKUs. // @engine maintainers. This block should not be removed. It partially enforces licensing - // restrictions on Windows. Ping @jhowardmsft if there are concerns or PRs to change this. + // restrictions on Windows. Ping Microsoft folks if there are concerns or PRs to change this. return fmt.Errorf("Windows client operating systems earlier than version 1809 can only run Hyper-V containers") } daemon.defaultIsolation = containertypes.Isolation("process") @@ -644,16 +639,6 @@ func (daemon *Daemon) setupSeccompProfile() error { return nil } -func getRealPath(path string) (string, error) { - if system.IsIoTCore() { - // Due to https://github.com/golang/go/issues/20506, path expansion - // does not work correctly on the default IoT Core configuration. - // TODO @darrenstahlmsft remove this once golang/go/20506 is fixed - return path, nil - } - return fileutils.ReadSymlinkedDirectory(path) -} - func (daemon *Daemon) loadRuntimes() error { return nil } @@ -664,3 +649,47 @@ func (daemon *Daemon) initRuntimes(_ map[string]types.Runtime) error { func setupResolvConf(config *config.Config) { } + +// RawSysInfo returns *sysinfo.SysInfo . +func (daemon *Daemon) RawSysInfo() *sysinfo.SysInfo { + return sysinfo.New() +} + +func (daemon *Daemon) initLibcontainerd(ctx context.Context) error { + var err error + + rt := daemon.configStore.GetDefaultRuntimeName() + if rt == "" { + if daemon.configStore.ContainerdAddr == "" { + rt = windowsV1RuntimeName + } else { + rt = windowsV2RuntimeName + } + } + + switch rt { + case windowsV1RuntimeName: + daemon.containerd, err = local.NewClient( + ctx, + daemon.containerdCli, + filepath.Join(daemon.configStore.ExecRoot, "containerd"), + daemon.configStore.ContainerdNamespace, + daemon, + ) + case windowsV2RuntimeName: + if daemon.configStore.ContainerdAddr == "" { + return fmt.Errorf("cannot use the specified runtime %q without containerd", rt) + } + daemon.containerd, err = remote.NewClient( + ctx, + daemon.containerdCli, + filepath.Join(daemon.configStore.ExecRoot, "containerd"), + daemon.configStore.ContainerdNamespace, + daemon, + ) + default: + return fmt.Errorf("unknown windows runtime %s", rt) + } + + return err +} diff --git a/daemon/daemon_windows_test.go b/daemon/daemon_windows_test.go index a4d8b6a20a45a..32ee182588168 100644 --- a/daemon/daemon_windows_test.go +++ b/daemon/daemon_windows_test.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package daemon // import "github.com/docker/docker/daemon" diff --git a/daemon/debugtrap_unix.go b/daemon/debugtrap_unix.go index c8abe69bb6cba..4ed710cb47166 100644 --- a/daemon/debugtrap_unix.go +++ b/daemon/debugtrap_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" @@ -6,17 +7,17 @@ import ( "os" "os/signal" - stackdump "github.com/docker/docker/pkg/signal" + "github.com/docker/docker/pkg/stack" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) -func (d *Daemon) setupDumpStackTrap(root string) { +func (daemon *Daemon) setupDumpStackTrap(root string) { c := make(chan os.Signal, 1) signal.Notify(c, unix.SIGUSR1) go func() { for range c { - path, err := stackdump.DumpStacks(root) + path, err := stack.DumpToFile(root) if err != nil { logrus.WithError(err).Error("failed to write goroutines dump") } else { diff --git a/daemon/debugtrap_unsupported.go b/daemon/debugtrap_unsupported.go index e83d51f59783c..79e27ba6edf62 100644 --- a/daemon/debugtrap_unsupported.go +++ b/daemon/debugtrap_unsupported.go @@ -1,7 +1,8 @@ +//go:build !linux && !darwin && !freebsd && !windows // +build !linux,!darwin,!freebsd,!windows package daemon // import "github.com/docker/docker/daemon" -func (d *Daemon) setupDumpStackTrap(_ string) { +func (daemon *Daemon) setupDumpStackTrap(_ string) { return } diff --git a/daemon/debugtrap_windows.go b/daemon/debugtrap_windows.go index b438d03812857..56e505e49bb24 100644 --- a/daemon/debugtrap_windows.go +++ b/daemon/debugtrap_windows.go @@ -5,19 +5,18 @@ import ( "os" "unsafe" - winio "github.com/Microsoft/go-winio" - "github.com/docker/docker/pkg/signal" + "github.com/docker/docker/pkg/stack" "github.com/sirupsen/logrus" "golang.org/x/sys/windows" ) -func (d *Daemon) setupDumpStackTrap(root string) { +func (daemon *Daemon) setupDumpStackTrap(root string) { // Windows does not support signals like *nix systems. So instead of // trapping on SIGUSR1 to dump stacks, we wait on a Win32 event to be // signaled. ACL'd to builtin administrators and local system - event := "Global\\docker-daemon-" + fmt.Sprint(os.Getpid()) + event := "Global\\stackdump-" + fmt.Sprint(os.Getpid()) ev, _ := windows.UTF16PtrFromString(event) - sd, err := winio.SddlToSecurityDescriptor("D:P(A;;GA;;;BA)(A;;GA;;;SY)") + sd, err := windows.SecurityDescriptorFromString("D:P(A;;GA;;;BA)(A;;GA;;;SY)") if err != nil { logrus.Errorf("failed to get security descriptor for debug stackdump event %s: %s", event, err.Error()) return @@ -25,7 +24,7 @@ func (d *Daemon) setupDumpStackTrap(root string) { var sa windows.SecurityAttributes sa.Length = uint32(unsafe.Sizeof(sa)) sa.InheritHandle = 1 - sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0])) + sa.SecurityDescriptor = sd h, err := windows.CreateEvent(&sa, 0, 0, ev) if h == 0 || err != nil { logrus.Errorf("failed to create debug stackdump event %s: %s", event, err.Error()) @@ -35,7 +34,7 @@ func (d *Daemon) setupDumpStackTrap(root string) { logrus.Debugf("Stackdump - waiting signal at %s", event) for { windows.WaitForSingleObject(h, windows.INFINITE) - path, err := signal.DumpStacks(root) + path, err := stack.DumpToFile(root) if err != nil { logrus.WithError(err).Error("failed to write goroutines dump") } else { diff --git a/daemon/delete.go b/daemon/delete.go index 2ccbff05fb4d5..668f232d1c2b3 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/system" + "github.com/opencontainers/selinux/go-selinux" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -54,13 +55,13 @@ func (daemon *Daemon) rmLink(container *container.Container, name string) error } parent, n := path.Split(name) if parent == "/" { - return fmt.Errorf("Conflict, cannot remove the default name of the container") + return fmt.Errorf("Conflict, cannot remove the default link name of the container") } parent = strings.TrimSuffix(parent, "/") pe, err := daemon.containersReplica.Snapshot().GetID(parent) if err != nil { - return fmt.Errorf("Cannot get parent %s for name %s", parent, name) + return fmt.Errorf("Cannot get parent %s for link name %s", parent, name) } daemon.releaseName(name) @@ -134,7 +135,7 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo } linkNames := daemon.linkIndex.delete(container) - selinuxFreeLxcContexts(container.ProcessLabel) + selinux.ReleaseLabel(container.ProcessLabel) daemon.idIndex.Delete(container.ID) daemon.containers.Delete(container.ID) daemon.containersReplica.Delete(container) diff --git a/daemon/delete_test.go b/daemon/delete_test.go index d600917b0c188..c95309e0121bc 100644 --- a/daemon/delete_test.go +++ b/daemon/delete_test.go @@ -2,19 +2,18 @@ package daemon // import "github.com/docker/docker/daemon" import ( "fmt" - "io/ioutil" "os" "testing" "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func newDaemonWithTmpRoot(t *testing.T) (*Daemon, func()) { - tmp, err := ioutil.TempDir("", "docker-daemon-unix-test-") + tmp, err := os.MkdirTemp("", "docker-daemon-unix-test-") assert.NilError(t, err) d := &Daemon{ repository: tmp, diff --git a/daemon/devices_linux.go b/daemon/devices_linux.go new file mode 100644 index 0000000000000..a7b76eacafc8f --- /dev/null +++ b/daemon/devices_linux.go @@ -0,0 +1,38 @@ +package daemon // import "github.com/docker/docker/daemon" + +import ( + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/pkg/capabilities" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +var deviceDrivers = map[string]*deviceDriver{} + +type deviceDriver struct { + capset capabilities.Set + updateSpec func(*specs.Spec, *deviceInstance) error +} + +type deviceInstance struct { + req container.DeviceRequest + selectedCaps []string +} + +func registerDeviceDriver(name string, d *deviceDriver) { + deviceDrivers[name] = d +} + +func (daemon *Daemon) handleDevice(req container.DeviceRequest, spec *specs.Spec) error { + if req.Driver == "" { + for _, dd := range deviceDrivers { + if selected := dd.capset.Match(req.Capabilities); selected != nil { + return dd.updateSpec(spec, &deviceInstance{req: req, selectedCaps: selected}) + } + } + } else if dd := deviceDrivers[req.Driver]; dd != nil { + if selected := dd.capset.Match(req.Capabilities); selected != nil { + return dd.updateSpec(spec, &deviceInstance{req: req, selectedCaps: selected}) + } + } + return incompatibleDeviceRequest{req.Driver, req.Capabilities} +} diff --git a/daemon/discovery/discovery.go b/daemon/discovery/discovery.go index 092c57638a18d..985ab6a3a7635 100644 --- a/daemon/discovery/discovery.go +++ b/daemon/discovery/discovery.go @@ -148,12 +148,14 @@ func (d *daemonDiscoveryReloader) initHeartbeat(address string) error { // Setup a short ticker until the first heartbeat has succeeded t := time.NewTicker(500 * time.Millisecond) defer t.Stop() + // timeout makes sure that after a period of time we stop being so aggressive trying to reach the discovery service - timeout := time.After(60 * time.Second) + timeout := time.NewTimer(60 * time.Second) + defer timeout.Stop() for { select { - case <-timeout: + case <-timeout.C: return errors.New("timeout waiting for initial discovery") case <-d.term: return errors.New("terminated") diff --git a/daemon/discovery/discovery_test.go b/daemon/discovery/discovery_test.go index c354a2918d646..cc5f34a718214 100644 --- a/daemon/discovery/discovery_test.go +++ b/daemon/discovery/discovery_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestDiscoveryOptsErrors(t *testing.T) { diff --git a/daemon/disk_usage.go b/daemon/disk_usage.go index 5bec60d174142..b96e626f45288 100644 --- a/daemon/disk_usage.go +++ b/daemon/disk_usage.go @@ -3,48 +3,83 @@ package daemon // import "github.com/docker/docker/daemon" import ( "context" "fmt" - "sync/atomic" + "github.com/docker/docker/api/server/router/system" "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" + "golang.org/x/sync/errgroup" ) -// SystemDiskUsage returns information about the daemon data disk usage -func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, error) { - if !atomic.CompareAndSwapInt32(&daemon.diskUsageRunning, 0, 1) { - return nil, fmt.Errorf("a disk usage operation is already running") +// ContainerDiskUsage returns information about container data disk usage. +func (daemon *Daemon) ContainerDiskUsage(ctx context.Context) ([]*types.Container, error) { + ch := daemon.usage.DoChan("ContainerDiskUsage", func() (interface{}, error) { + // Retrieve container list + containers, err := daemon.Containers(&types.ContainerListOptions{ + Size: true, + All: true, + }) + if err != nil { + return nil, fmt.Errorf("failed to retrieve container list: %v", err) + } + return containers, nil + }) + select { + case <-ctx.Done(): + return nil, ctx.Err() + case res := <-ch: + if res.Err != nil { + return nil, res.Err + } + return res.Val.([]*types.Container), nil } - defer atomic.StoreInt32(&daemon.diskUsageRunning, 0) +} - // Retrieve container list - allContainers, err := daemon.Containers(&types.ContainerListOptions{ - Size: true, - All: true, - }) - if err != nil { - return nil, fmt.Errorf("failed to retrieve container list: %v", err) +// SystemDiskUsage returns information about the daemon data disk usage. +// Callers must not mutate contents of the returned fields. +func (daemon *Daemon) SystemDiskUsage(ctx context.Context, opts system.DiskUsageOptions) (*types.DiskUsage, error) { + eg, ctx := errgroup.WithContext(ctx) + + var containers []*types.Container + if opts.Containers { + eg.Go(func() error { + var err error + containers, err = daemon.ContainerDiskUsage(ctx) + return err + }) } - // Get all top images with extra attributes - allImages, err := daemon.imageService.Images(filters.NewArgs(), false, true) - if err != nil { - return nil, fmt.Errorf("failed to retrieve image list: %v", err) + var ( + images []*types.ImageSummary + layersSize int64 + ) + if opts.Images { + eg.Go(func() error { + var err error + images, err = daemon.imageService.ImageDiskUsage(ctx) + return err + }) + eg.Go(func() error { + var err error + layersSize, err = daemon.imageService.LayerDiskUsage(ctx) + return err + }) } - localVolumes, err := daemon.volumes.LocalVolumesSize(ctx) - if err != nil { - return nil, err + var volumes []*types.Volume + if opts.Volumes { + eg.Go(func() error { + var err error + volumes, err = daemon.volumes.LocalVolumesSize(ctx) + return err + }) } - allLayersSize, err := daemon.imageService.LayerDiskUsage(ctx) - if err != nil { + if err := eg.Wait(); err != nil { return nil, err } - return &types.DiskUsage{ - LayersSize: allLayersSize, - Containers: allContainers, - Volumes: localVolumes, - Images: allImages, + LayersSize: layersSize, + Containers: containers, + Volumes: volumes, + Images: images, }, nil } diff --git a/daemon/errors.go b/daemon/errors.go index 6d02af3d547cf..6f9eb54039351 100644 --- a/daemon/errors.go +++ b/daemon/errors.go @@ -7,7 +7,7 @@ import ( "github.com/docker/docker/errdefs" "github.com/pkg/errors" - "google.golang.org/grpc" + "google.golang.org/grpc/status" ) func errNotRunning(id string) error { @@ -80,6 +80,17 @@ func (e invalidIdentifier) Error() string { func (invalidIdentifier) InvalidParameter() {} +type incompatibleDeviceRequest struct { + driver string + caps [][]string +} + +func (i incompatibleDeviceRequest) Error() string { + return fmt.Sprintf("could not select device driver %q with capabilities: %v", i.driver, i.caps) +} + +func (incompatibleDeviceRequest) InvalidParameter() {} + type duplicateMountPointError string func (e duplicateMountPointError) Error() string { @@ -122,7 +133,7 @@ func (e startInvalidConfigError) Error() string { func (e startInvalidConfigError) InvalidParameter() {} // Is this right??? func translateContainerdStartErr(cmd string, setExitCode func(int), err error) error { - errDesc := grpc.ErrorDesc(err) + errDesc := status.Convert(err).Message() contains := func(s1, s2 string) bool { return strings.Contains(strings.ToLower(s1), s2) } @@ -130,10 +141,10 @@ func translateContainerdStartErr(cmd string, setExitCode func(int), err error) e // if we receive an internal error from the initial start of a container then lets // return it instead of entering the restart loop // set to 127 for container cmd not found/does not exist) - if contains(errDesc, cmd) && - (contains(errDesc, "executable file not found") || - contains(errDesc, "no such file or directory") || - contains(errDesc, "system cannot find the file specified")) { + if contains(errDesc, "executable file not found") || + contains(errDesc, "no such file or directory") || + contains(errDesc, "system cannot find the file specified") || + contains(errDesc, "failed to run runc create/exec call") { setExitCode(127) retErr = startInvalidConfigError(errDesc) } diff --git a/daemon/events.go b/daemon/events.go index cf1634a198818..9b76fdbb97c29 100644 --- a/daemon/events.go +++ b/daemon/events.go @@ -10,7 +10,7 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/container" daemonevents "github.com/docker/docker/daemon/events" - "github.com/docker/libnetwork" + "github.com/docker/docker/libnetwork" swarmapi "github.com/docker/swarmkit/api" gogotypes "github.com/gogo/protobuf/types" "github.com/sirupsen/logrus" @@ -87,7 +87,7 @@ func (daemon *Daemon) LogNetworkEventWithAttributes(nw libnetwork.Network, actio // LogDaemonEventWithAttributes generates an event related to the daemon itself with specific given attributes. func (daemon *Daemon) LogDaemonEventWithAttributes(action string, attributes map[string]string) { if daemon.EventsService != nil { - if info, err := daemon.SystemInfo(); err == nil && info.Name != "" { + if info := daemon.SystemInfo(); info.Name != "" { attributes["name"] = info.Name } actor := events.Actor{ diff --git a/daemon/events/events.go b/daemon/events/events.go index 31af271fe628e..77f197c7bc714 100644 --- a/daemon/events/events.go +++ b/daemon/events/events.go @@ -79,7 +79,7 @@ func (e *Events) Evict(l chan interface{}) { } // Log creates a local scope message and publishes it -func (e *Events) Log(action, eventType string, actor eventtypes.Actor) { +func (e *Events) Log(action string, eventType eventtypes.Type, actor eventtypes.Actor) { now := time.Now().UTC() jm := eventtypes.Message{ Action: action, diff --git a/daemon/events/events_test.go b/daemon/events/events_test.go index d11521567f412..6a193fc08459d 100644 --- a/daemon/events/events_test.go +++ b/daemon/events/events_test.go @@ -163,9 +163,9 @@ func TestLogEvents(t *testing.T) { // https://github.com/docker/docker/issues/20999 // Fixtures: // -//2016-03-07T17:28:03.022433271+02:00 container die 0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079 (image=ubuntu, name=small_hoover) -//2016-03-07T17:28:03.091719377+02:00 network disconnect 19c5ed41acb798f26b751e0035cd7821741ab79e2bbd59a66b5fd8abf954eaa0 (type=bridge, container=0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079, name=bridge) -//2016-03-07T17:28:03.129014751+02:00 container destroy 0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079 (image=ubuntu, name=small_hoover) +// 2016-03-07T17:28:03.022433271+02:00 container die 0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079 (image=ubuntu, name=small_hoover) +// 2016-03-07T17:28:03.091719377+02:00 network disconnect 19c5ed41acb798f26b751e0035cd7821741ab79e2bbd59a66b5fd8abf954eaa0 (type=bridge, container=0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079, name=bridge) +// 2016-03-07T17:28:03.129014751+02:00 container destroy 0b863f2a26c18557fc6cdadda007c459f9ec81b874780808138aea78a3595079 (image=ubuntu, name=small_hoover) func TestLoadBufferedEvents(t *testing.T) { now := time.Now() f, err := timetypes.GetTimestamp("2016-03-07T17:28:03.100000000+02:00", now) diff --git a/daemon/events/filter.go b/daemon/events/filter.go index da06f18b0648c..f6856f6bc3e8e 100644 --- a/daemon/events/filter.go +++ b/daemon/events/filter.go @@ -102,7 +102,7 @@ func (ef *Filter) matchConfig(ev events.Message) bool { return ef.fuzzyMatchName(ev, events.ConfigEventType) } -func (ef *Filter) fuzzyMatchName(ev events.Message, eventType string) bool { +func (ef *Filter) fuzzyMatchName(ev events.Message, eventType events.Type) bool { return ef.filter.FuzzyMatch(eventType, ev.Actor.ID) || ef.filter.FuzzyMatch(eventType, ev.Actor.Attributes["name"]) } diff --git a/daemon/events/metrics.go b/daemon/events/metrics.go index 199858d6e0bdc..bf939004bf618 100644 --- a/daemon/events/metrics.go +++ b/daemon/events/metrics.go @@ -1,6 +1,6 @@ package events // import "github.com/docker/docker/daemon/events" -import "github.com/docker/go-metrics" +import metrics "github.com/docker/go-metrics" var ( eventsCounter metrics.Counter diff --git a/daemon/events/testutils/testutils.go b/daemon/events/testutils/testutils.go index b6766adb90daf..c9ef45da2ac07 100644 --- a/daemon/events/testutils/testutils.go +++ b/daemon/events/testutils/testutils.go @@ -62,10 +62,9 @@ func Scan(text string) (*events.Message, error) { attrs[kv[0]] = kv[1] } - tu := time.Unix(t, tn) return &events.Message{ Time: t, - TimeNano: tu.UnixNano(), + TimeNano: time.Unix(t, tn).UnixNano(), Type: md["eventType"], Action: md["action"], Actor: events.Actor{ diff --git a/daemon/events_test.go b/daemon/events_test.go index df089976f27f2..85759cb1f64ee 100644 --- a/daemon/events_test.go +++ b/daemon/events_test.go @@ -15,7 +15,7 @@ func TestLogContainerEventCopyLabels(t *testing.T) { _, l, _ := e.Subscribe() defer e.Evict(l) - container := &container.Container{ + ctr := &container.Container{ ID: "container_id", Name: "container_name", Config: &containertypes.Config{ @@ -29,10 +29,10 @@ func TestLogContainerEventCopyLabels(t *testing.T) { daemon := &Daemon{ EventsService: e, } - daemon.LogContainerEvent(container, "create") + daemon.LogContainerEvent(ctr, "create") - if _, mutated := container.Config.Labels["image"]; mutated { - t.Fatalf("Expected to not mutate the container labels, got %q", container.Config.Labels) + if _, mutated := ctr.Config.Labels["image"]; mutated { + t.Fatalf("Expected to not mutate the container labels, got %q", ctr.Config.Labels) } validateTestAttributes(t, l, map[string]string{ @@ -46,7 +46,7 @@ func TestLogContainerEventWithAttributes(t *testing.T) { _, l, _ := e.Subscribe() defer e.Evict(l) - container := &container.Container{ + ctr := &container.Container{ ID: "container_id", Name: "container_name", Config: &containertypes.Config{ @@ -63,7 +63,7 @@ func TestLogContainerEventWithAttributes(t *testing.T) { "node": "2", "foo": "bar", } - daemon.LogContainerEventWithAttributes(container, "create", attributes) + daemon.LogContainerEventWithAttributes(ctr, "create", attributes) validateTestAttributes(t, l, map[string]string{ "node": "1", diff --git a/daemon/exec.go b/daemon/exec.go index f0b43d725352c..a0b0623b8ffba 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "runtime" "strings" "time" @@ -14,27 +15,27 @@ import ( "github.com/docker/docker/daemon/exec" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/pools" - "github.com/docker/docker/pkg/signal" - "github.com/docker/docker/pkg/term" + "github.com/moby/sys/signal" + "github.com/moby/term" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) // Seconds to wait after sending TERM before trying KILL -const termProcessTimeout = 10 +const termProcessTimeout = 10 * time.Second -func (d *Daemon) registerExecCommand(container *container.Container, config *exec.Config) { +func (daemon *Daemon) registerExecCommand(container *container.Container, config *exec.Config) { // Storing execs in container in order to kill them gracefully whenever the container is stopped or removed. container.ExecCommands.Add(config.ID, config) // Storing execs in daemon for easy access via Engine API. - d.execCommands.Add(config.ID, config) + daemon.execCommands.Add(config.ID, config) } // ExecExists looks up the exec instance and returns a bool if it exists or not. // It will also return the error produced by `getConfig` -func (d *Daemon) ExecExists(name string) (bool, error) { - if _, err := d.getExecConfig(name); err != nil { +func (daemon *Daemon) ExecExists(name string) (bool, error) { + if _, err := daemon.getExecConfig(name); err != nil { return false, err } return true, nil @@ -42,8 +43,8 @@ func (d *Daemon) ExecExists(name string) (bool, error) { // getExecConfig looks up the exec instance by name. If the container associated // with the exec instance is stopped or paused, it will return an error. -func (d *Daemon) getExecConfig(name string) (*exec.Config, error) { - ec := d.execCommands.Get(name) +func (daemon *Daemon) getExecConfig(name string) (*exec.Config, error) { + ec := daemon.execCommands.Get(name) if ec == nil { return nil, errExecNotFound(name) } @@ -53,54 +54,54 @@ func (d *Daemon) getExecConfig(name string) (*exec.Config, error) { // saying the container isn't running, we should return a 404 so that // the user sees the same error now that they will after the // 5 minute clean-up loop is run which erases old/dead execs. - container := d.containers.Get(ec.ContainerID) - if container == nil { + ctr := daemon.containers.Get(ec.ContainerID) + if ctr == nil { return nil, containerNotFound(name) } - if !container.IsRunning() { - return nil, fmt.Errorf("Container %s is not running: %s", container.ID, container.State.String()) + if !ctr.IsRunning() { + return nil, errNotRunning(ctr.ID) } - if container.IsPaused() { - return nil, errExecPaused(container.ID) + if ctr.IsPaused() { + return nil, errExecPaused(ctr.ID) } - if container.IsRestarting() { - return nil, errContainerIsRestarting(container.ID) + if ctr.IsRestarting() { + return nil, errContainerIsRestarting(ctr.ID) } return ec, nil } -func (d *Daemon) unregisterExecCommand(container *container.Container, execConfig *exec.Config) { +func (daemon *Daemon) unregisterExecCommand(container *container.Container, execConfig *exec.Config) { container.ExecCommands.Delete(execConfig.ID, execConfig.Pid) - d.execCommands.Delete(execConfig.ID, execConfig.Pid) + daemon.execCommands.Delete(execConfig.ID, execConfig.Pid) } -func (d *Daemon) getActiveContainer(name string) (*container.Container, error) { - container, err := d.GetContainer(name) +func (daemon *Daemon) getActiveContainer(name string) (*container.Container, error) { + ctr, err := daemon.GetContainer(name) if err != nil { return nil, err } - if !container.IsRunning() { - return nil, errNotRunning(container.ID) + if !ctr.IsRunning() { + return nil, errNotRunning(ctr.ID) } - if container.IsPaused() { + if ctr.IsPaused() { return nil, errExecPaused(name) } - if container.IsRestarting() { - return nil, errContainerIsRestarting(container.ID) + if ctr.IsRestarting() { + return nil, errContainerIsRestarting(ctr.ID) } - return container, nil + return ctr, nil } // ContainerExecCreate sets up an exec in a running container. -func (d *Daemon) ContainerExecCreate(name string, config *types.ExecConfig) (string, error) { - cntr, err := d.getActiveContainer(name) +func (daemon *Daemon) ContainerExecCreate(name string, config *types.ExecConfig) (string, error) { + cntr, err := daemon.getActiveContainer(name) if err != nil { return "", err } cmd := strslice.StrSlice(config.Cmd) - entrypoint, args := d.getEntrypointAndArgs(strslice.StrSlice{}, cmd) + entrypoint, args := daemon.getEntrypointAndArgs(strslice.StrSlice{}, cmd) keys := []byte{} if config.DetachKeys != "" { @@ -124,7 +125,7 @@ func (d *Daemon) ContainerExecCreate(name string, config *types.ExecConfig) (str execConfig.User = config.User execConfig.WorkingDir = config.WorkingDir - linkedEnv, err := d.setupLinkedContainers(cntr) + linkedEnv, err := daemon.setupLinkedContainers(cntr) if err != nil { return "", err } @@ -136,12 +137,12 @@ func (d *Daemon) ContainerExecCreate(name string, config *types.ExecConfig) (str execConfig.WorkingDir = cntr.Config.WorkingDir } - d.registerExecCommand(cntr, execConfig) + daemon.registerExecCommand(cntr, execConfig) attributes := map[string]string{ "execID": execConfig.ID, } - d.LogContainerEventWithAttributes(cntr, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " "), attributes) + daemon.LogContainerEventWithAttributes(cntr, "exec_create: "+execConfig.Entrypoint+" "+strings.Join(execConfig.Args, " "), attributes) return execConfig.ID, nil } @@ -149,15 +150,15 @@ func (d *Daemon) ContainerExecCreate(name string, config *types.ExecConfig) (str // ContainerExecStart starts a previously set up exec instance. The // std streams are set up. // If ctx is cancelled, the process is terminated. -func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (err error) { +func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (err error) { var ( cStdin io.ReadCloser cStdout, cStderr io.Writer ) - ec, err := d.getExecConfig(name) + ec, err := daemon.getExecConfig(name) if err != nil { - return errExecNotFound(name) + return err } ec.Lock() @@ -174,12 +175,15 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R ec.Running = true ec.Unlock() - c := d.containers.Get(ec.ContainerID) + c := daemon.containers.Get(ec.ContainerID) + if c == nil { + return containerNotFound(ec.ContainerID) + } logrus.Debugf("starting exec command %s in container %s", ec.ID, c.ID) attributes := map[string]string{ "execID": ec.ID, } - d.LogContainerEventWithAttributes(c, "exec_start: "+ec.Entrypoint+" "+strings.Join(ec.Args, " "), attributes) + daemon.LogContainerEventWithAttributes(c, "exec_start: "+ec.Entrypoint+" "+strings.Join(ec.Args, " "), attributes) defer func() { if err != nil { @@ -217,17 +221,28 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R ec.StreamConfig.NewNopInputPipe() } - p := &specs.Process{ - Args: append([]string{ec.Entrypoint}, ec.Args...), - Env: ec.Env, - Terminal: ec.Tty, - Cwd: ec.WorkingDir, + p := &specs.Process{} + if runtime.GOOS != "windows" { + ctr, err := daemon.containerdCli.LoadContainer(ctx, ec.ContainerID) + if err != nil { + return err + } + spec, err := ctr.Spec(ctx) + if err != nil { + return err + } + p = spec.Process } + p.Args = append([]string{ec.Entrypoint}, ec.Args...) + p.Env = ec.Env + p.Cwd = ec.WorkingDir + p.Terminal = ec.Tty + if p.Cwd == "" { p.Cwd = "/" } - if err := d.execSetPlatformOpt(c, ec, p); err != nil { + if err := daemon.execSetPlatformOpt(c, ec, p); err != nil { return err } @@ -248,7 +263,7 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R // Synchronize with libcontainerd event loop ec.Lock() c.ExecCommands.Lock() - systemPid, err := d.containerd.Exec(ctx, c.ID, ec.ID, p, cStdin != nil, ec.InitializeStdio) + systemPid, err := daemon.containerd.Exec(ctx, c.ID, ec.ID, p, cStdin != nil, ec.InitializeStdio) // the exec context should be ready, or error happened. // close the chan to notify readiness close(ec.Started) @@ -264,11 +279,15 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R select { case <-ctx.Done(): logrus.Debugf("Sending TERM signal to process %v in container %v", name, c.ID) - d.containerd.SignalProcess(ctx, c.ID, name, int(signal.SignalMap["TERM"])) + daemon.containerd.SignalProcess(ctx, c.ID, name, int(signal.SignalMap["TERM"])) + + timeout := time.NewTimer(termProcessTimeout) + defer timeout.Stop() + select { - case <-time.After(termProcessTimeout * time.Second): - logrus.Infof("Container %v, process %v failed to exit within %d seconds of signal TERM - using the force", c.ID, name, termProcessTimeout) - d.containerd.SignalProcess(ctx, c.ID, name, int(signal.SignalMap["KILL"])) + case <-timeout.C: + logrus.Infof("Container %v, process %v failed to exit within %v of signal TERM - using the force", c.ID, name, termProcessTimeout) + daemon.containerd.SignalProcess(ctx, c.ID, name, int(signal.SignalMap["KILL"])) case <-attachErr: // TERM signal worked } @@ -281,7 +300,7 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R attributes := map[string]string{ "execID": ec.ID, } - d.LogContainerEventWithAttributes(c, "exec_detach", attributes) + daemon.LogContainerEventWithAttributes(c, "exec_detach", attributes) } } return nil @@ -289,16 +308,16 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R // execCommandGC runs a ticker to clean up the daemon references // of exec configs that are no longer part of the container. -func (d *Daemon) execCommandGC() { +func (daemon *Daemon) execCommandGC() { for range time.Tick(5 * time.Minute) { var ( cleaned int - liveExecCommands = d.containerExecIds() + liveExecCommands = daemon.containerExecIds() ) - for id, config := range d.execCommands.Commands() { + for id, config := range daemon.execCommands.Commands() { if config.CanRemove { cleaned++ - d.execCommands.Delete(id, config.Pid) + daemon.execCommands.Delete(id, config.Pid) } else { if _, exists := liveExecCommands[id]; !exists { config.CanRemove = true @@ -313,9 +332,9 @@ func (d *Daemon) execCommandGC() { // containerExecIds returns a list of all the current exec ids that are in use // and running inside a container. -func (d *Daemon) containerExecIds() map[string]struct{} { +func (daemon *Daemon) containerExecIds() map[string]struct{} { ids := map[string]struct{}{} - for _, c := range d.containers.List() { + for _, c := range daemon.containers.List() { for _, id := range c.ExecCommands.List() { ids[id] = struct{}{} } diff --git a/daemon/exec/exec.go b/daemon/exec/exec.go index c036c46a0c8eb..f1b52598545dc 100644 --- a/daemon/exec/exec.go +++ b/daemon/exec/exec.go @@ -1,6 +1,7 @@ package exec // import "github.com/docker/docker/daemon/exec" import ( + "context" "runtime" "sync" @@ -39,7 +40,7 @@ type Config struct { // NewConfig initializes the a new exec configuration func NewConfig() *Config { return &Config{ - ID: stringid.GenerateNonCryptoID(), + ID: stringid.GenerateRandomID(), StreamConfig: stream.NewConfig(), Started: make(chan struct{}), } @@ -58,7 +59,7 @@ func (i *rio) Close() error { } func (i *rio) Wait() { - i.sc.Wait() + i.sc.Wait(context.Background()) i.IO.Wait() } diff --git a/daemon/exec_linux.go b/daemon/exec_linux.go index cd52f4886f84b..b683fef7de3ed 100644 --- a/daemon/exec_linux.go +++ b/daemon/exec_linux.go @@ -1,24 +1,22 @@ package daemon // import "github.com/docker/docker/daemon" import ( + "context" + + "github.com/containerd/containerd/pkg/apparmor" "github.com/docker/docker/container" - "github.com/docker/docker/daemon/caps" "github.com/docker/docker/daemon/exec" - "github.com/opencontainers/runc/libcontainer/apparmor" - "github.com/opencontainers/runtime-spec/specs-go" + "github.com/docker/docker/oci/caps" + specs "github.com/opencontainers/runtime-spec/specs-go" ) func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config, p *specs.Process) error { if len(ec.User) > 0 { - uid, gid, additionalGids, err := getUser(c, ec.User) + var err error + p.User, err = getUser(c, ec.User) if err != nil { return err } - p.User = specs.User{ - UID: uid, - GID: gid, - AdditionalGids: additionalGids, - } } if ec.Privileged { if p.Capabilities == nil { @@ -29,19 +27,19 @@ func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config p.Capabilities.Inheritable = p.Capabilities.Bounding p.Capabilities.Effective = p.Capabilities.Bounding } - if apparmor.IsEnabled() { + if apparmor.HostSupports() { var appArmorProfile string if c.AppArmorProfile != "" { appArmorProfile = c.AppArmorProfile } else if c.HostConfig.Privileged { // `docker exec --privileged` does not currently disable AppArmor // profiles. Privileged configuration of the container is inherited - appArmorProfile = "unconfined" + appArmorProfile = unconfinedAppArmorProfile } else { - appArmorProfile = "docker-default" + appArmorProfile = defaultAppArmorProfile } - if appArmorProfile == "docker-default" { + if appArmorProfile == defaultAppArmorProfile { // Unattended upgrades and other fun services can unload AppArmor // profiles inadvertently. Since we cannot store our profile in // /etc/apparmor.d, nor can we practically add other ways of @@ -54,6 +52,6 @@ func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config } p.ApparmorProfile = appArmorProfile } - daemon.setRlimits(&specs.Spec{Process: p}, c) - return nil + s := &specs.Spec{Process: p} + return WithRlimits(daemon, c)(context.Background(), nil, nil, s) } diff --git a/daemon/exec_linux_test.go b/daemon/exec_linux_test.go index 0db7f080db106..ffef343898e79 100644 --- a/daemon/exec_linux_test.go +++ b/daemon/exec_linux_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package daemon @@ -5,49 +6,86 @@ package daemon import ( "testing" + "github.com/containerd/containerd/pkg/apparmor" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" + "github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/exec" - "github.com/opencontainers/runc/libcontainer/apparmor" - "github.com/opencontainers/runtime-spec/specs-go" - "gotest.tools/assert" + specs "github.com/opencontainers/runtime-spec/specs-go" + "gotest.tools/v3/assert" ) -func TestExecSetPlatformOpt(t *testing.T) { - if !apparmor.IsEnabled() { - t.Skip("requires AppArmor to be enabled") +func TestExecSetPlatformOptAppArmor(t *testing.T) { + appArmorEnabled := apparmor.HostSupports() + + tests := []struct { + doc string + privileged bool + appArmorProfile string + expectedProfile string + }{ + { + doc: "default options", + expectedProfile: defaultAppArmorProfile, + }, + { + doc: "custom profile", + appArmorProfile: "my-custom-profile", + expectedProfile: "my-custom-profile", + }, + { + doc: "privileged container", + privileged: true, + expectedProfile: unconfinedAppArmorProfile, + }, + { + doc: "privileged container, custom profile", + privileged: true, + appArmorProfile: "my-custom-profile", + expectedProfile: "my-custom-profile", + // FIXME: execSetPlatformOpts prefers custom profiles over "privileged", + // which looks like a bug (--privileged on the container should + // disable apparmor, seccomp, and selinux); see the code at: + // https://github.com/moby/moby/blob/46cdcd206c56172b95ba5c77b827a722dab426c5/daemon/exec_linux.go#L32-L40 + // expectedProfile: unconfinedAppArmorProfile, + }, } - d := &Daemon{} - c := &container.Container{AppArmorProfile: "my-custom-profile"} - ec := &exec.Config{} - p := &specs.Process{} - - err := d.execSetPlatformOpt(c, ec, p) - assert.NilError(t, err) - assert.Equal(t, "my-custom-profile", p.ApparmorProfile) -} -// TestExecSetPlatformOptPrivileged verifies that `docker exec --privileged` -// does not disable AppArmor profiles. Exec currently inherits the `Privileged` -// configuration of the container. See https://github.com/moby/moby/pull/31773#discussion_r105586900 -// -// This behavior may change in future, but test for the behavior to prevent it -// from being changed accidentally. -func TestExecSetPlatformOptPrivileged(t *testing.T) { - if !apparmor.IsEnabled() { - t.Skip("requires AppArmor to be enabled") + d := &Daemon{configStore: &config.Config{}} + + // Currently, `docker exec --privileged` inherits the Privileged configuration + // of the container, and does not disable AppArmor. + // See https://github.com/moby/moby/pull/31773#discussion_r105586900 + // + // This behavior may change in future, but to verify the current behavior, + // we run the test both with "exec" and "exec --privileged", which should + // both give the same result. + for _, execPrivileged := range []bool{false, true} { + for _, tc := range tests { + tc := tc + doc := tc.doc + if !appArmorEnabled { + // no profile should be set if the host does not support AppArmor + doc += " (apparmor disabled)" + tc.expectedProfile = "" + } + if execPrivileged { + doc += " (exec privileged)" + } + t.Run(doc, func(t *testing.T) { + c := &container.Container{ + AppArmorProfile: tc.appArmorProfile, + HostConfig: &containertypes.HostConfig{ + Privileged: tc.privileged, + }, + } + ec := &exec.Config{Privileged: execPrivileged} + p := &specs.Process{} + + err := d.execSetPlatformOpt(c, ec, p) + assert.NilError(t, err) + assert.Equal(t, p.ApparmorProfile, tc.expectedProfile) + }) + } } - d := &Daemon{} - c := &container.Container{AppArmorProfile: "my-custom-profile"} - ec := &exec.Config{Privileged: true} - p := &specs.Process{} - - err := d.execSetPlatformOpt(c, ec, p) - assert.NilError(t, err) - assert.Equal(t, "my-custom-profile", p.ApparmorProfile) - - c.HostConfig = &containertypes.HostConfig{Privileged: true} - err = d.execSetPlatformOpt(c, ec, p) - assert.NilError(t, err) - assert.Equal(t, "unconfined", p.ApparmorProfile) } diff --git a/daemon/exec_windows.go b/daemon/exec_windows.go index c37ea9f31ae79..32f16e9282c2e 100644 --- a/daemon/exec_windows.go +++ b/daemon/exec_windows.go @@ -7,9 +7,7 @@ import ( ) func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config, p *specs.Process) error { - // Process arguments need to be escaped before sending to OCI. if c.OS == "windows" { - p.Args = escapeArgs(p.Args) p.User.Username = ec.User } return nil diff --git a/daemon/export.go b/daemon/export.go index 27bc35967d220..75d1a22dac315 100644 --- a/daemon/export.go +++ b/daemon/export.go @@ -3,7 +3,6 @@ package daemon // import "github.com/docker/docker/daemon" import ( "fmt" "io" - "runtime" "github.com/docker/docker/container" "github.com/docker/docker/errdefs" @@ -15,26 +14,26 @@ import ( // ContainerExport writes the contents of the container to the given // writer. An error is returned if the container cannot be found. func (daemon *Daemon) ContainerExport(name string, out io.Writer) error { - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return err } - if runtime.GOOS == "windows" && container.OS == "windows" { + if isWindows && ctr.OS == "windows" { return fmt.Errorf("the daemon on this operating system does not support exporting Windows containers") } - if container.IsDead() { - err := fmt.Errorf("You cannot export container %s which is Dead", container.ID) + if ctr.IsDead() { + err := fmt.Errorf("You cannot export container %s which is Dead", ctr.ID) return errdefs.Conflict(err) } - if container.IsRemovalInProgress() { - err := fmt.Errorf("You cannot export container %s which is being removed", container.ID) + if ctr.IsRemovalInProgress() { + err := fmt.Errorf("You cannot export container %s which is being removed", ctr.ID) return errdefs.Conflict(err) } - data, err := daemon.containerExport(container) + data, err := daemon.containerExport(ctr) if err != nil { return fmt.Errorf("Error exporting container %s: %v", name, err) } @@ -51,7 +50,7 @@ func (daemon *Daemon) containerExport(container *container.Container) (arch io.R if !system.IsOSSupported(container.OS) { return nil, fmt.Errorf("cannot export %s: %s ", container.ID, system.ErrNotSupportedOperatingSystem) } - rwlayer, err := daemon.imageService.GetLayerByID(container.ID, container.OS) + rwlayer, err := daemon.imageService.GetLayerByID(container.ID) if err != nil { return nil, err } @@ -66,17 +65,17 @@ func (daemon *Daemon) containerExport(container *container.Container) (arch io.R return nil, err } - archive, err := archivePath(basefs, basefs.Path(), &archive.TarOptions{ + archv, err := archivePath(basefs, basefs.Path(), &archive.TarOptions{ Compression: archive.Uncompressed, UIDMaps: daemon.idMapping.UIDs(), GIDMaps: daemon.idMapping.GIDs(), - }) + }, basefs.Path()) if err != nil { rwlayer.Unmount() return nil, err } - arch = ioutils.NewReadCloserWrapper(archive, func() error { - err := archive.Close() + arch = ioutils.NewReadCloserWrapper(archv, func() error { + err := archv.Close() rwlayer.Unmount() daemon.imageService.ReleaseLayer(rwlayer, container.OS) return err diff --git a/daemon/graphdriver/aufs/aufs.go b/daemon/graphdriver/aufs/aufs.go index 114aa9a615e09..0935e447c0747 100644 --- a/daemon/graphdriver/aufs/aufs.go +++ b/daemon/graphdriver/aufs/aufs.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux /* @@ -27,25 +28,23 @@ import ( "context" "fmt" "io" - "io/ioutil" "os" "os/exec" "path" "path/filepath" "strings" "sync" - "time" + "github.com/containerd/containerd/pkg/userns" "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/directory" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" - mountpk "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/system" - rsystem "github.com/opencontainers/runc/libcontainer/system" + "github.com/moby/locker" + "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -72,7 +71,6 @@ func init() { // Driver contains information about the filesystem mounted. type Driver struct { - sync.Mutex root string uidMaps []idtools.IDMap gidMaps []idtools.IDMap @@ -81,6 +79,7 @@ type Driver struct { pathCache map[string]string naiveDiff graphdriver.DiffDriver locker *locker.Locker + mntL sync.Mutex } // Init returns a new AUFS driver. @@ -130,25 +129,31 @@ func Init(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap locker: locker.New(), } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) + currentID := idtools.CurrentIdentity() + _, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } + dirID := idtools.Identity{ + UID: currentID.UID, + GID: rootGID, + } + // Create the root aufs driver dir - if err := idtools.MkdirAllAndChown(root, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(root, 0710, dirID); err != nil { return nil, err } // Populate the dir structure for _, p := range paths { - if err := idtools.MkdirAllAndChown(path.Join(root, p), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(path.Join(root, p), 0710, dirID); err != nil { return nil, err } } for _, path := range []string{"mnt", "diff"} { p := filepath.Join(root, path) - entries, err := ioutil.ReadDir(p) + entries, err := os.ReadDir(p) if err != nil { logger.WithError(err).WithField("dir", p).Error("error reading dir entries") continue @@ -178,7 +183,7 @@ func supportsAufs() error { // proc/filesystems for when aufs is supported exec.Command("modprobe", "aufs").Run() - if rsystem.RunningInUserNS() { + if userns.RunningInUserNS() { return ErrAufsNested } @@ -307,36 +312,9 @@ func (a *Driver) Remove(id string) error { mountpoint = a.getMountpoint(id) } - logger := logger.WithField("layer", id) - - var retries int - for { - mounted, err := a.mounted(mountpoint) - if err != nil { - if os.IsNotExist(err) { - break - } - return err - } - if !mounted { - break - } - - err = a.unmount(mountpoint) - if err == nil { - break - } - - if err != unix.EBUSY { - return errors.Wrapf(err, "aufs: unmount error: %s", mountpoint) - } - if retries >= 5 { - return errors.Wrapf(err, "aufs: unmount error after retries: %s", mountpoint) - } - // If unmount returns EBUSY, it could be a transient error. Sleep and retry. - retries++ - logger.Warnf("unmount failed due to EBUSY: retry count: %d", retries) - time.Sleep(100 * time.Millisecond) + if err := a.unmount(mountpoint); err != nil { + logger.WithError(err).WithField("method", "Remove()").Warn() + return err } // Remove the layers file for the id @@ -352,7 +330,7 @@ func (a *Driver) Remove(id string) error { // way (so that docker doesn't find it anymore) before doing removal of // the whole tree. if err := atomicRemove(mountpoint); err != nil { - if errors.Cause(err) == unix.EBUSY { + if errors.Is(err, unix.EBUSY) { logger.WithField("dir", mountpoint).WithError(err).Warn("error performing atomic remove due to EBUSY") } return errors.Wrapf(err, "could not remove mountpoint for id %s", id) @@ -437,7 +415,7 @@ func (a *Driver) Put(id string) error { err := a.unmount(m) if err != nil { - logger.Debugf("Failed to unmount %s aufs: %v", id, err) + logger.WithError(err).WithField("method", "Put()").Warn() } return err } @@ -547,9 +525,6 @@ func (a *Driver) getParentLayerPaths(id string) ([]string, error) { } func (a *Driver) mount(id string, target string, mountLabel string, layers []string) error { - a.Lock() - defer a.Unlock() - // If the id is mounted or we get an error return if mounted, err := a.mounted(target); err != nil || mounted { return err @@ -564,9 +539,6 @@ func (a *Driver) mount(id string, target string, mountLabel string, layers []str } func (a *Driver) unmount(mountPath string) error { - a.Lock() - defer a.Unlock() - if mounted, err := a.mounted(mountPath); err != nil || !mounted { return err } @@ -579,32 +551,29 @@ func (a *Driver) mounted(mountpoint string) (bool, error) { // Cleanup aufs and unmount all mountpoints func (a *Driver) Cleanup() error { - var dirs []string - if err := filepath.Walk(a.mntPath(), func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() { - return nil - } - dirs = append(dirs, path) - return nil - }); err != nil { - return err + dir := a.mntPath() + files, err := os.ReadDir(dir) + if err != nil { + return errors.Wrap(err, "aufs readdir error") } + for _, f := range files { + if !f.IsDir() { + continue + } + + m := path.Join(dir, f.Name()) - for _, m := range dirs { if err := a.unmount(m); err != nil { - logger.Debugf("error unmounting %s: %s", m, err) + logger.WithError(err).WithField("method", "Cleanup()").Warn() } } - return mountpk.RecursiveUnmount(a.root) + return mount.RecursiveUnmount(a.root) } func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err error) { defer func() { if err != nil { - Unmount(target) + mount.Unmount(target) } }() @@ -632,14 +601,29 @@ func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err erro opts += ",dirperm1" } data := label.FormatMountLabel(fmt.Sprintf("%s,%s", string(b[:bp]), opts), mountLabel) - if err = mount("none", target, "aufs", 0, data); err != nil { + a.mntL.Lock() + err = unix.Mount("none", target, "aufs", 0, data) + a.mntL.Unlock() + if err != nil { + err = errors.Wrap(err, "mount target="+target+" data="+data) return } - for ; index < len(ro); index++ { - layer := fmt.Sprintf(":%s=ro+wh", ro[index]) - data := label.FormatMountLabel(fmt.Sprintf("append%s", layer), mountLabel) - if err = mount("none", target, "aufs", unix.MS_REMOUNT, data); err != nil { + for index < len(ro) { + bp = 0 + for ; index < len(ro); index++ { + layer := fmt.Sprintf("append:%s=ro+wh,", ro[index]) + if bp+len(layer) > len(b) { + break + } + bp += copy(b[bp:], layer) + } + data := label.FormatMountLabel(string(b[:bp]), mountLabel) + a.mntL.Lock() + err = unix.Mount("none", target, "aufs", unix.MS_REMOUNT, data) + a.mntL.Unlock() + if err != nil { + err = errors.Wrap(err, "mount target="+target+" flags=MS_REMOUNT data="+data) return } } @@ -651,14 +635,14 @@ func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err erro // version of aufs. func useDirperm() bool { enableDirpermLock.Do(func() { - base, err := ioutil.TempDir("", "docker-aufs-base") + base, err := os.MkdirTemp("", "docker-aufs-base") if err != nil { logger.Errorf("error checking dirperm1: %v", err) return } defer os.RemoveAll(base) - union, err := ioutil.TempDir("", "docker-aufs-union") + union, err := os.MkdirTemp("", "docker-aufs-union") if err != nil { logger.Errorf("error checking dirperm1: %v", err) return @@ -666,7 +650,7 @@ func useDirperm() bool { defer os.RemoveAll(union) opts := fmt.Sprintf("br:%s,dirperm1,xino=/dev/shm/aufs.xino", base) - if err := mount("none", union, "aufs", 0, opts); err != nil { + if err := unix.Mount("none", union, "aufs", 0, opts); err != nil { return } enableDirperm = true diff --git a/daemon/graphdriver/aufs/aufs_test.go b/daemon/graphdriver/aufs/aufs_test.go index fdc502ba65341..4c902f7848735 100644 --- a/daemon/graphdriver/aufs/aufs_test.go +++ b/daemon/graphdriver/aufs/aufs_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package aufs // import "github.com/docker/docker/daemon/graphdriver/aufs" @@ -6,7 +7,6 @@ import ( "crypto/sha256" "encoding/hex" "fmt" - "io/ioutil" "os" "path" "path/filepath" @@ -17,8 +17,8 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/stringid" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) var ( @@ -690,7 +690,7 @@ func testMountMoreThan42Layers(t *testing.T, mountPath string) { // Perform the actual mount for the top most image point, err := driverGet(d, last, "") assert.NilError(t, err) - files, err := ioutil.ReadDir(point) + files, err := os.ReadDir(point) assert.NilError(t, err) assert.Check(t, is.Len(files, expected)) } @@ -729,9 +729,9 @@ func BenchmarkConcurrentAccess(b *testing.B) { numConcurrent := 256 // create a bunch of ids - var ids []string + ids := make([]string, numConcurrent) for i := 0; i < numConcurrent; i++ { - ids = append(ids, stringid.GenerateNonCryptoID()) + ids[i] = stringid.GenerateRandomID() } if err := d.Create(ids[0], "", nil); err != nil { @@ -743,7 +743,7 @@ func BenchmarkConcurrentAccess(b *testing.B) { } parent := ids[1] - ids = append(ids[2:]) + ids = ids[2:] chErr := make(chan error, numConcurrent) var outerGroup sync.WaitGroup diff --git a/daemon/graphdriver/aufs/dirs.go b/daemon/graphdriver/aufs/dirs.go index e60be5e3c9897..006c556c076d5 100644 --- a/daemon/graphdriver/aufs/dirs.go +++ b/daemon/graphdriver/aufs/dirs.go @@ -1,17 +1,17 @@ +//go:build linux // +build linux package aufs // import "github.com/docker/docker/daemon/graphdriver/aufs" import ( "bufio" - "io/ioutil" "os" "path" ) // Return all the directories func loadIds(root string) ([]string, error) { - dirs, err := ioutil.ReadDir(root) + dirs, err := os.ReadDir(root) if err != nil { return nil, err } diff --git a/daemon/graphdriver/aufs/mount.go b/daemon/graphdriver/aufs/mount.go index 9f5510380cf88..33ee5a6400599 100644 --- a/daemon/graphdriver/aufs/mount.go +++ b/daemon/graphdriver/aufs/mount.go @@ -1,17 +1,56 @@ +//go:build linux // +build linux package aufs // import "github.com/docker/docker/daemon/graphdriver/aufs" import ( "os/exec" + "syscall" + "time" + "github.com/moby/sys/mount" + "github.com/pkg/errors" "golang.org/x/sys/unix" ) // Unmount the target specified. func Unmount(target string) error { - if err := exec.Command("auplink", target, "flush").Run(); err != nil { - logger.WithError(err).Warnf("Couldn't run auplink before unmount %s", target) + const retries = 5 + + // auplink flush + for i := 0; ; i++ { + out, err := exec.Command("auplink", target, "flush").CombinedOutput() + if err == nil { + break + } + rc := 0 + if exiterr, ok := err.(*exec.ExitError); ok { + if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { + rc = status.ExitStatus() + } + } + if i >= retries || rc != int(unix.EINVAL) { + logger.WithError(err).WithField("method", "Unmount").Warnf("auplink flush failed: %s", out) + break + } + // auplink failed to find target in /proc/self/mounts because + // kernel can't guarantee continuity while reading from it + // while mounts table is being changed + logger.Debugf("auplink flush error (retrying %d/%d): %s", i+1, retries, out) + } + + // unmount + var err error + for i := 0; i < retries; i++ { + err = mount.Unmount(target) + if err != nil && errors.Is(err, unix.EBUSY) { + logger.Debugf("aufs unmount %s failed with EBUSY (retrying %d/%d)", target, i+1, retries) + time.Sleep(100 * time.Millisecond) + continue // try again + } + break } - return unix.Unmount(target, 0) + + // either no error occurred, or another error + return err } diff --git a/daemon/graphdriver/aufs/mount_linux.go b/daemon/graphdriver/aufs/mount_linux.go deleted file mode 100644 index 8d5ad8f32d681..0000000000000 --- a/daemon/graphdriver/aufs/mount_linux.go +++ /dev/null @@ -1,7 +0,0 @@ -package aufs // import "github.com/docker/docker/daemon/graphdriver/aufs" - -import "golang.org/x/sys/unix" - -func mount(source string, target string, fstype string, flags uintptr, data string) error { - return unix.Mount(source, target, fstype, flags, data) -} diff --git a/daemon/graphdriver/aufs/mount_unsupported.go b/daemon/graphdriver/aufs/mount_unsupported.go deleted file mode 100644 index cf7f58c29ec0b..0000000000000 --- a/daemon/graphdriver/aufs/mount_unsupported.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build !linux - -package aufs // import "github.com/docker/docker/daemon/graphdriver/aufs" - -import "errors" - -// MsRemount declared to specify a non-linux system mount. -const MsRemount = 0 - -func mount(source string, target string, fstype string, flags uintptr, data string) (err error) { - return errors.New("mount is not implemented on this platform") -} diff --git a/daemon/graphdriver/btrfs/btrfs.go b/daemon/graphdriver/btrfs/btrfs.go index 7ce7edef36e91..ee842cce09004 100644 --- a/daemon/graphdriver/btrfs/btrfs.go +++ b/daemon/graphdriver/btrfs/btrfs.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package btrfs // import "github.com/docker/docker/daemon/graphdriver/btrfs" @@ -16,7 +17,6 @@ import "C" import ( "fmt" - "io/ioutil" "math" "os" "path" @@ -26,13 +26,14 @@ import ( "sync" "unsafe" + "github.com/containerd/containerd/pkg/userns" "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/system" - "github.com/docker/go-units" + units "github.com/docker/go-units" + "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -70,11 +71,14 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, graphdriver.ErrPrerequisites } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) - if err != nil { - return nil, err + remappedRoot := idtools.NewIDMappingsFromMaps(uidMaps, gidMaps) + currentID := idtools.CurrentIdentity() + dirID := idtools.Identity{ + UID: currentID.UID, + GID: remappedRoot.RootPair().GID, } - if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + + if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil { return nil, err } @@ -100,7 +104,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap } if userDiskQuota { - if err := driver.subvolEnableQuota(); err != nil { + if err := driver.enableQuota(); err != nil { return nil, err } } @@ -134,7 +138,7 @@ func parseOptions(opt []string) (btrfsOptions, bool, error) { // Driver contains information about the filesystem mounted. type Driver struct { - //root of the file system + // root of the file system home string uidMaps []idtools.IDMap gidMaps []idtools.IDMap @@ -169,18 +173,10 @@ func (d *Driver) GetMetadata(id string) (map[string]string, error) { // Cleanup unmounts the home directory. func (d *Driver) Cleanup() error { - err := d.subvolDisableQuota() - umountErr := mount.Unmount(d.home) - - // in case we have two errors, prefer the one from disableQuota() - if err != nil { + if err := mount.Unmount(d.home); err != nil { return err } - if umountErr != nil { - return errors.Wrapf(umountErr, "error unmounting %s", d.home) - } - return nil } @@ -338,7 +334,7 @@ func (d *Driver) updateQuotaStatus() { d.once.Do(func() { if !d.quotaEnabled { // In case quotaEnabled is not set, check qgroup and update quotaEnabled as needed - if err := subvolQgroupStatus(d.home); err != nil { + if err := qgroupStatus(d.home); err != nil { // quota is still not enabled return } @@ -347,7 +343,7 @@ func (d *Driver) updateQuotaStatus() { }) } -func (d *Driver) subvolEnableQuota() error { +func (d *Driver) enableQuota() error { d.updateQuotaStatus() if d.quotaEnabled { @@ -373,32 +369,6 @@ func (d *Driver) subvolEnableQuota() error { return nil } -func (d *Driver) subvolDisableQuota() error { - d.updateQuotaStatus() - - if !d.quotaEnabled { - return nil - } - - dir, err := openDir(d.home) - if err != nil { - return err - } - defer closeDir(dir) - - var args C.struct_btrfs_ioctl_quota_ctl_args - args.cmd = C.BTRFS_QUOTA_CTL_DISABLE - _, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.BTRFS_IOC_QUOTA_CTL, - uintptr(unsafe.Pointer(&args))) - if errno != 0 { - return fmt.Errorf("Failed to disable btrfs quota for %s: %v", dir, errno.Error()) - } - - d.quotaEnabled = false - - return nil -} - func (d *Driver) subvolRescanQuota() error { d.updateQuotaStatus() @@ -441,11 +411,11 @@ func subvolLimitQgroup(path string, size uint64) error { return nil } -// subvolQgroupStatus performs a BTRFS_IOC_TREE_SEARCH on the root path +// qgroupStatus performs a BTRFS_IOC_TREE_SEARCH on the root path // with search key of BTRFS_QGROUP_STATUS_KEY. // In case qgroup is enabled, the retuned key type will match BTRFS_QGROUP_STATUS_KEY. // For more details please see https://github.com/kdave/btrfs-progs/blob/v4.9/qgroup.c#L1035 -func subvolQgroupStatus(path string) error { +func qgroupStatus(path string) error { dir, err := openDir(path) if err != nil { return err @@ -525,7 +495,14 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { if err != nil { return err } - if err := idtools.MkdirAllAndChown(subvolumes, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + + currentID := idtools.CurrentIdentity() + dirID := idtools.Identity{ + UID: currentID.UID, + GID: rootGID, + } + + if err := idtools.MkdirAllAndChown(subvolumes, 0710, dirID); err != nil { return err } if parent == "" { @@ -560,10 +537,10 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { if err := d.setStorageSize(path.Join(subvolumes, id), driver); err != nil { return err } - if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(quotas, 0700, idtools.CurrentIdentity()); err != nil { return err } - if err := ioutil.WriteFile(path.Join(quotas, id), []byte(fmt.Sprint(driver.options.size)), 0644); err != nil { + if err := os.WriteFile(path.Join(quotas, id), []byte(fmt.Sprint(driver.options.size)), 0644); err != nil { return err } } @@ -606,13 +583,13 @@ func (d *Driver) parseStorageOpt(storageOpt map[string]string, driver *Driver) e // Set btrfs storage size func (d *Driver) setStorageSize(dir string, driver *Driver) error { - if driver.options.size <= 0 { + if driver.options.size == 0 { return fmt.Errorf("btrfs: invalid storage size: %s", units.HumanSize(float64(driver.options.size))) } if d.options.minSpace > 0 && driver.options.size < d.options.minSpace { return fmt.Errorf("btrfs: storage size cannot be less than %s", units.HumanSize(float64(d.options.minSpace))) } - if err := d.subvolEnableQuota(); err != nil { + if err := d.enableQuota(); err != nil { return err } return subvolLimitQgroup(dir, driver.options.size) @@ -637,7 +614,18 @@ func (d *Driver) Remove(id string) error { d.updateQuotaStatus() if err := subvolDelete(d.subvolumesDir(), id, d.quotaEnabled); err != nil { - return err + if d.quotaEnabled { + // use strings.Contains() rather than errors.Is(), because subvolDelete() does not use %w yet + if userns.RunningInUserNS() && strings.Contains(err.Error(), "operation not permitted") { + err = errors.Wrap(err, `failed to delete subvolume without root (hint: remount btrfs on "user_subvol_rm_allowed" option, or update the kernel to >= 4.18, or change the storage driver to "fuse-overlayfs")`) + } + return err + } + // If quota is not enabled, fallback to rmdir syscall to delete subvolumes. + // This would allow unprivileged user to delete their owned subvolumes + // in kernel >= 4.18 without user_subvol_rm_allowed mount option. + // + // From https://github.com/containers/storage/pull/508/commits/831e32b6bdcb530acc4c1cb9059d3c6dba14208c } if err := system.EnsureRemoveAll(dir); err != nil { return err @@ -657,9 +645,9 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) { return nil, fmt.Errorf("%s: not a directory", dir) } - if quota, err := ioutil.ReadFile(d.quotasDirID(id)); err == nil { + if quota, err := os.ReadFile(d.quotasDirID(id)); err == nil { if size, err := strconv.ParseUint(string(quota), 10, 64); err == nil && size >= d.options.minSpace { - if err := d.subvolEnableQuota(); err != nil { + if err := d.enableQuota(); err != nil { return nil, err } if err := subvolLimitQgroup(dir, size); err != nil { diff --git a/daemon/graphdriver/btrfs/btrfs_test.go b/daemon/graphdriver/btrfs/btrfs_test.go index b70e93bc2d328..63c3adbe1c813 100644 --- a/daemon/graphdriver/btrfs/btrfs_test.go +++ b/daemon/graphdriver/btrfs/btrfs_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package btrfs // import "github.com/docker/docker/daemon/graphdriver/btrfs" diff --git a/daemon/graphdriver/btrfs/dummy_unsupported.go b/daemon/graphdriver/btrfs/dummy_unsupported.go index d7793f879484c..490ba4c901668 100644 --- a/daemon/graphdriver/btrfs/dummy_unsupported.go +++ b/daemon/graphdriver/btrfs/dummy_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux || !cgo // +build !linux !cgo package btrfs // import "github.com/docker/docker/daemon/graphdriver/btrfs" diff --git a/daemon/graphdriver/btrfs/version.go b/daemon/graphdriver/btrfs/version.go index 2fb5c73555bdd..635e97681316c 100644 --- a/daemon/graphdriver/btrfs/version.go +++ b/daemon/graphdriver/btrfs/version.go @@ -1,4 +1,5 @@ -// +build linux,!btrfs_noversion +//go:build linux +// +build linux package btrfs // import "github.com/docker/docker/daemon/graphdriver/btrfs" diff --git a/daemon/graphdriver/btrfs/version_none.go b/daemon/graphdriver/btrfs/version_none.go deleted file mode 100644 index 5c755f8177773..0000000000000 --- a/daemon/graphdriver/btrfs/version_none.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build linux,btrfs_noversion - -package btrfs // import "github.com/docker/docker/daemon/graphdriver/btrfs" - -// TODO(vbatts) remove this work-around once supported linux distros are on -// btrfs utilities of >= 3.16.1 - -func btrfsBuildVersion() string { - return "-" -} - -func btrfsLibVersion() int { - return -1 -} diff --git a/daemon/graphdriver/btrfs/version_test.go b/daemon/graphdriver/btrfs/version_test.go index 465daadb0d7e7..0f8652f0defdc 100644 --- a/daemon/graphdriver/btrfs/version_test.go +++ b/daemon/graphdriver/btrfs/version_test.go @@ -1,4 +1,5 @@ -// +build linux,!btrfs_noversion +//go:build linux +// +build linux package btrfs // import "github.com/docker/docker/daemon/graphdriver/btrfs" diff --git a/daemon/graphdriver/copy/copy.go b/daemon/graphdriver/copy/copy.go index 86316fdfe7418..9ae30a3696e54 100644 --- a/daemon/graphdriver/copy/copy.go +++ b/daemon/graphdriver/copy/copy.go @@ -1,15 +1,8 @@ +//go:build linux // +build linux package copy // import "github.com/docker/docker/daemon/graphdriver/copy" -/* -#include - -#ifndef FICLONE -#define FICLONE _IOW(0x94, 9, int) -#endif -*/ -import "C" import ( "container/list" "fmt" @@ -19,9 +12,9 @@ import ( "syscall" "time" + "github.com/containerd/containerd/pkg/userns" "github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/system" - rsystem "github.com/opencontainers/runc/libcontainer/system" "golang.org/x/sys/unix" ) @@ -50,7 +43,7 @@ func copyRegular(srcPath, dstPath string, fileinfo os.FileInfo, copyWithFileRang defer dstFile.Close() if *copyWithFileClone { - _, _, err = unix.Syscall(unix.SYS_IOCTL, dstFile.Fd(), C.FICLONE, srcFile.Fd()) + err = unix.IoctlFileClone(int(dstFile.Fd()), int(srcFile.Fd())) if err == nil { return nil } @@ -141,9 +134,6 @@ func DirCopy(srcDir, dstDir string, copyMode Mode, copyXattrs bool) error { } dstPath := filepath.Join(dstDir, relPath) - if err != nil { - return err - } stat, ok := f.Sys().(*syscall.Stat_t) if !ok { @@ -152,9 +142,10 @@ func DirCopy(srcDir, dstDir string, copyMode Mode, copyXattrs bool) error { isHardlink := false - switch f.Mode() & os.ModeType { - case 0: // Regular file - id := fileID{dev: stat.Dev, ino: stat.Ino} + switch mode := f.Mode(); { + case mode.IsRegular(): + // the type is 32bit on mips + id := fileID{dev: uint64(stat.Dev), ino: stat.Ino} //nolint: unconvert if copyMode == Hardlink { isHardlink = true if err2 := os.Link(srcPath, dstPath); err2 != nil { @@ -171,12 +162,12 @@ func DirCopy(srcDir, dstDir string, copyMode Mode, copyXattrs bool) error { copiedFiles[id] = dstPath } - case os.ModeDir: + case mode.IsDir(): if err := os.Mkdir(dstPath, f.Mode()); err != nil && !os.IsExist(err) { return err } - case os.ModeSymlink: + case mode&os.ModeSymlink != 0: link, err := os.Readlink(srcPath) if err != nil { return err @@ -186,15 +177,15 @@ func DirCopy(srcDir, dstDir string, copyMode Mode, copyXattrs bool) error { return err } - case os.ModeNamedPipe: + case mode&os.ModeNamedPipe != 0: fallthrough - case os.ModeSocket: + case mode&os.ModeSocket != 0: if err := unix.Mkfifo(dstPath, stat.Mode); err != nil { return err } - case os.ModeDevice: - if rsystem.RunningInUserNS() { + case mode&os.ModeDevice != 0: + if userns.RunningInUserNS() { // cannot create a device if running in user namespace return nil } @@ -203,7 +194,7 @@ func DirCopy(srcDir, dstDir string, copyMode Mode, copyXattrs bool) error { } default: - return fmt.Errorf("unknown file type for %s", srcPath) + return fmt.Errorf("unknown file type (%d / %s) for %s", f.Mode(), f.Mode().String(), srcPath) } // Everything below is copying metadata from src to dst. All this metadata @@ -233,7 +224,7 @@ func DirCopy(srcDir, dstDir string, copyMode Mode, copyXattrs bool) error { } // system.Chtimes doesn't support a NOFOLLOW flag atm - // nolint: unconvert + //nolint: unconvert if f.IsDir() { dirsToSetMtimes.PushFront(&dirMtimeInfo{dstPath: &dstPath, stat: stat}) } else if !isSymlink { diff --git a/daemon/graphdriver/copy/copy_test.go b/daemon/graphdriver/copy/copy_test.go index 0f3b1670f741a..8dcd8d9d560e2 100644 --- a/daemon/graphdriver/copy/copy_test.go +++ b/daemon/graphdriver/copy/copy_test.go @@ -1,10 +1,10 @@ +//go:build linux // +build linux package copy // import "github.com/docker/docker/daemon/graphdriver/copy" import ( "fmt" - "io/ioutil" "math/rand" "os" "path/filepath" @@ -14,8 +14,8 @@ import ( "github.com/docker/docker/pkg/system" "golang.org/x/sys/unix" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestCopy(t *testing.T) { @@ -31,11 +31,12 @@ func TestCopyWithoutRange(t *testing.T) { } func TestCopyDir(t *testing.T) { - srcDir, err := ioutil.TempDir("", "srcDir") + srcDir, err := os.MkdirTemp("", "srcDir") assert.NilError(t, err) + defer os.RemoveAll(srcDir) populateSrcDir(t, srcDir, 3) - dstDir, err := ioutil.TempDir("", "testdst") + dstDir, err := os.MkdirTemp("", "testdst") assert.NilError(t, err) defer os.RemoveAll(dstDir) @@ -53,7 +54,6 @@ func TestCopyDir(t *testing.T) { } dstPath := filepath.Join(dstDir, relPath) - assert.NilError(t, err) // If we add non-regular dirs and files to the test // then we need to add more checks here. @@ -67,8 +67,7 @@ func TestCopyDir(t *testing.T) { if srcFileSys.Dev == dstFileSys.Dev { assert.Check(t, srcFileSys.Ino != dstFileSys.Ino) } - // Todo: check size, and ctim is not equal - /// on filesystems that have granular ctimes + // Todo: check size, and ctim is not equal on filesystems that have granular ctimes assert.Check(t, is.DeepEqual(srcFileSys.Mode, dstFileSys.Mode)) assert.Check(t, is.DeepEqual(srcFileSys.Uid, dstFileSys.Uid)) assert.Check(t, is.DeepEqual(srcFileSys.Gid, dstFileSys.Gid)) @@ -103,28 +102,28 @@ func populateSrcDir(t *testing.T, srcDir string, remainingDepth int) { for i := 0; i < 10; i++ { fileName := filepath.Join(srcDir, fmt.Sprintf("srcfile-%d", i)) // Owner read bit set - assert.NilError(t, ioutil.WriteFile(fileName, []byte{}, randomMode(0400))) + assert.NilError(t, os.WriteFile(fileName, []byte{}, randomMode(0400))) assert.NilError(t, system.Chtimes(fileName, aTime, mTime)) } } func doCopyTest(t *testing.T, copyWithFileRange, copyWithFileClone *bool) { - dir, err := ioutil.TempDir("", "docker-copy-check") + dir, err := os.MkdirTemp("", "docker-copy-check") assert.NilError(t, err) defer os.RemoveAll(dir) srcFilename := filepath.Join(dir, "srcFilename") - dstFilename := filepath.Join(dir, "dstilename") + dstFilename := filepath.Join(dir, "dstFilename") r := rand.New(rand.NewSource(0)) buf := make([]byte, 1024) _, err = r.Read(buf) assert.NilError(t, err) - assert.NilError(t, ioutil.WriteFile(srcFilename, buf, 0777)) + assert.NilError(t, os.WriteFile(srcFilename, buf, 0777)) fileinfo, err := os.Stat(srcFilename) assert.NilError(t, err) assert.NilError(t, copyRegular(srcFilename, dstFilename, fileinfo, copyWithFileRange, copyWithFileClone)) - readBuf, err := ioutil.ReadFile(dstFilename) + readBuf, err := os.ReadFile(dstFilename) assert.NilError(t, err) assert.Check(t, is.DeepEqual(buf, readBuf)) } @@ -132,11 +131,11 @@ func doCopyTest(t *testing.T, copyWithFileRange, copyWithFileClone *bool) { func TestCopyHardlink(t *testing.T) { var srcFile1FileInfo, srcFile2FileInfo, dstFile1FileInfo, dstFile2FileInfo unix.Stat_t - srcDir, err := ioutil.TempDir("", "srcDir") + srcDir, err := os.MkdirTemp("", "srcDir") assert.NilError(t, err) defer os.RemoveAll(srcDir) - dstDir, err := ioutil.TempDir("", "dstDir") + dstDir, err := os.MkdirTemp("", "dstDir") assert.NilError(t, err) defer os.RemoveAll(dstDir) @@ -144,7 +143,7 @@ func TestCopyHardlink(t *testing.T) { srcFile2 := filepath.Join(srcDir, "file2") dstFile1 := filepath.Join(dstDir, "file1") dstFile2 := filepath.Join(dstDir, "file2") - assert.NilError(t, ioutil.WriteFile(srcFile1, []byte{}, 0777)) + assert.NilError(t, os.WriteFile(srcFile1, []byte{}, 0777)) assert.NilError(t, os.Link(srcFile1, srcFile2)) assert.Check(t, DirCopy(srcDir, dstDir, Content, false)) diff --git a/daemon/graphdriver/devmapper/device_setup.go b/daemon/graphdriver/devmapper/device_setup.go index e03e23c92f6ed..ec3d3aa93cab5 100644 --- a/daemon/graphdriver/devmapper/device_setup.go +++ b/daemon/graphdriver/devmapper/device_setup.go @@ -5,7 +5,6 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -139,7 +138,7 @@ func readLVMConfig(root string) (directLVMConfig, error) { var cfg directLVMConfig p := filepath.Join(root, "setup-config.json") - b, err := ioutil.ReadFile(p) + b, err := os.ReadFile(p) if err != nil { if os.IsNotExist(err) { return cfg, nil @@ -162,7 +161,7 @@ func writeLVMConfig(root string, cfg directLVMConfig) error { if err != nil { return errors.Wrap(err, "error marshalling direct lvm config") } - err = ioutil.WriteFile(p, b, 0600) + err = os.WriteFile(p, b, 0600) return errors.Wrap(err, "error writing direct lvm config to file") } @@ -221,7 +220,7 @@ func setupDirectLVM(cfg directLVMConfig) error { } profile := fmt.Sprintf("activation{\nthin_pool_autoextend_threshold=%d\nthin_pool_autoextend_percent=%d\n}", cfg.AutoExtendThreshold, cfg.AutoExtendPercent) - err = ioutil.WriteFile(lvmProfileDir+"/docker-thinpool.profile", []byte(profile), 0600) + err = os.WriteFile(lvmProfileDir+"/docker-thinpool.profile", []byte(profile), 0600) if err != nil { return errors.Wrap(err, "error writing docker thinp autoextend profile") } diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 5dc01d71d9c52..e9c4634b67fe5 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" @@ -7,7 +8,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "os/exec" "path" @@ -24,10 +24,10 @@ import ( "github.com/docker/docker/pkg/dmesg" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/loopback" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/go-units" + units "github.com/docker/go-units" + "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -119,7 +119,7 @@ type DeviceSet struct { deletionWorkerTicker *time.Ticker uidMaps []idtools.IDMap gidMaps []idtools.IDMap - minFreeSpacePercent uint32 //min free space percentage in thinpool + minFreeSpacePercent uint32 // min free space percentage in thinpool xfsNospaceRetries string // max retries when xfs receives ENOSPC lvmSetupConfig directLVMConfig } @@ -325,7 +325,7 @@ func (devices *DeviceSet) removeMetadata(info *devInfo) error { // Given json data and file path, write it to disk func (devices *DeviceSet) writeMetaFile(jsonData []byte, filePath string) error { - tmpFile, err := ioutil.TempFile(devices.metadataDir(), ".tmp") + tmpFile, err := os.CreateTemp(devices.metadataDir(), ".tmp") if err != nil { return fmt.Errorf("devmapper: Error creating metadata file: %s", err) } @@ -540,8 +540,14 @@ func xfsSupported() error { return err // error text is descriptive enough } - // Check if kernel supports xfs filesystem or not. - exec.Command("modprobe", "xfs").Run() + mountTarget, err := os.MkdirTemp("", "supportsXFS") + if err != nil { + return errors.Wrapf(err, "error checking for xfs support") + } + + /* The mounting will fail--after the module has been loaded.*/ + defer os.RemoveAll(mountTarget) + unix.Mount("none", mountTarget, "xfs", 0, "") f, err := os.Open("/proc/filesystems") if err != nil { @@ -627,7 +633,7 @@ func (devices *DeviceSet) createFilesystem(info *devInfo) (err error) { func (devices *DeviceSet) migrateOldMetaData() error { // Migrate old metadata file - jsonData, err := ioutil.ReadFile(devices.oldMetadataFile()) + jsonData, err := os.ReadFile(devices.oldMetadataFile()) if err != nil && !os.IsNotExist(err) { return err } @@ -949,7 +955,7 @@ func (devices *DeviceSet) loadMetadata(hash string) *devInfo { info := &devInfo{Hash: hash, devices: devices} logger := logrus.WithField("storage-driver", "devicemapper") - jsonData, err := ioutil.ReadFile(devices.metadataFile(info)) + jsonData, err := os.ReadFile(devices.metadataFile(info)) if err != nil { logger.Debugf("Failed to read %s with err: %v", devices.metadataFile(info), err) return nil @@ -1200,7 +1206,7 @@ func (devices *DeviceSet) growFS(info *devInfo) error { options = joinMountOptions(options, devices.mountOptions) if err := mount.Mount(info.DevName(), fsMountPoint, devices.BaseDeviceFilesystem, options); err != nil { - return fmt.Errorf("Error mounting '%s' on '%s' (fstype='%s' options='%s'): %s\n%v", info.DevName(), fsMountPoint, devices.BaseDeviceFilesystem, options, err, string(dmesg.Dmesg(256))) + return errors.Wrapf(err, "Failed to mount; dmesg: %s", string(dmesg.Dmesg(256))) } defer unix.Unmount(fsMountPoint, unix.MNT_DETACH) @@ -1257,7 +1263,7 @@ func (devices *DeviceSet) setupBaseImage() error { } func setCloseOnExec(name string) { - fileInfos, _ := ioutil.ReadDir("/proc/self/fd") + fileInfos, _ := os.ReadDir("/proc/self/fd") for _, i := range fileInfos { link, _ := os.Readlink(filepath.Join("/proc/self/fd", i.Name())) if link == name { @@ -1351,7 +1357,7 @@ func (devices *DeviceSet) ResizePool(size int64) error { } func (devices *DeviceSet) loadTransactionMetaData() error { - jsonData, err := ioutil.ReadFile(devices.transactionMetaFile()) + jsonData, err := os.ReadFile(devices.transactionMetaFile()) if err != nil { // There is no active transaction. This will be the case // during upgrade. @@ -1434,7 +1440,7 @@ func (devices *DeviceSet) processPendingTransaction() error { } func (devices *DeviceSet) loadDeviceSetMetaData() error { - jsonData, err := ioutil.ReadFile(devices.deviceSetMetaFile()) + jsonData, err := os.ReadFile(devices.deviceSetMetaFile()) if err != nil { // For backward compatibility return success if file does // not exist. @@ -1527,7 +1533,8 @@ func getDeviceMajorMinor(file *os.File) (uint64, uint64, error) { return 0, 0, err } - dev := stat.Rdev + // the type is 32bit on mips + dev := uint64(stat.Rdev) //nolint: unconvert majorNum := major(dev) minorNum := minor(dev) @@ -1685,8 +1692,8 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { } } - //create the root dir of the devmapper driver ownership to match this - //daemon's remapped root uid/gid so containers can start properly + // create the root dir of the devmapper driver ownership to match this + // daemon's remapped root uid/gid so containers can start properly uid, gid, err := idtools.GetRootUIDGID(devices.uidMaps, devices.gidMaps) if err != nil { return err @@ -1738,7 +1745,8 @@ func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) { // - Managed by docker // - The target of this device is at major and minor // - If is defined, use that file inside the device as a loopback image. Otherwise use the device itself. - devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(st.Dev), minor(st.Dev), st.Ino) + // The type Dev in Stat_t is 32bit on mips. + devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(uint64(st.Dev)), minor(uint64(st.Dev)), st.Ino) //nolint: unconvert logger.Debugf("Generated prefix: %s", devices.devicePrefix) // Check for the existence of the thin-pool device @@ -2237,7 +2245,7 @@ func (devices *DeviceSet) cancelDeferredRemoval(info *devInfo) error { func (devices *DeviceSet) unmountAndDeactivateAll(dir string) { logger := logrus.WithField("storage-driver", "devicemapper") - files, err := ioutil.ReadDir(dir) + files, err := os.ReadDir(dir) if err != nil { logger.Warnf("unmountAndDeactivate: %s", err) return @@ -2381,7 +2389,7 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error { options = joinMountOptions(options, label.FormatMountLabel("", mountLabel)) if err := mount.Mount(info.DevName(), path, fstype, options); err != nil { - return fmt.Errorf("devmapper: Error mounting '%s' on '%s' (fstype='%s' options='%s'): %s\n%v", info.DevName(), path, fstype, options, err, string(dmesg.Dmesg(256))) + return errors.Wrapf(err, "Failed to mount; dmesg: %s", string(dmesg.Dmesg(256))) } if fstype == "xfs" && devices.xfsNospaceRetries != "" { diff --git a/daemon/graphdriver/devmapper/devmapper_test.go b/daemon/graphdriver/devmapper/devmapper_test.go index bda907a5d6748..7eb1795d17ca8 100644 --- a/daemon/graphdriver/devmapper/devmapper_test.go +++ b/daemon/graphdriver/devmapper/devmapper_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" @@ -34,13 +35,14 @@ func initLoopbacks() error { if err != nil { return err } - // create at least 8 loopback files, ya, that is a good number - for i := 0; i < 8; i++ { + // create at least 128 loopback files, since a few first ones + // might be already in use by the host OS + for i := 0; i < 128; i++ { loopPath := fmt.Sprintf("/dev/loop%d", i) // only create new loopback files if they don't exist if _, err := os.Stat(loopPath); err != nil { if mkerr := syscall.Mknod(loopPath, - uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil { + uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil { //nolint: unconvert return mkerr } os.Chown(loopPath, int(statT.Uid), int(statT.Gid)) @@ -109,7 +111,7 @@ func testChangeLoopBackSize(t *testing.T, delta, expectDataSize, expectMetaDataS if err := driver.Cleanup(); err != nil { t.Fatal(err) } - //Reload + // Reload d, err := Init(driver.home, []string{ fmt.Sprintf("dm.loopdatasize=%d", defaultDataLoopbackSize+delta), fmt.Sprintf("dm.loopmetadatasize=%d", defaultMetaDataLoopbackSize+delta), @@ -135,7 +137,7 @@ func TestDevmapperLockReleasedDeviceDeletion(t *testing.T) { // DeviceSet Lock. If lock has not been released, this will hang. driver.DeviceSet.cleanupDeletedDevices() - doneChan := make(chan bool) + doneChan := make(chan bool, 1) go func() { driver.DeviceSet.Lock() diff --git a/daemon/graphdriver/devmapper/driver.go b/daemon/graphdriver/devmapper/driver.go index 899b1f8670979..7abdbe6163f51 100644 --- a/daemon/graphdriver/devmapper/driver.go +++ b/daemon/graphdriver/devmapper/driver.go @@ -1,10 +1,10 @@ +//go:build linux // +build linux package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" import ( "fmt" - "io/ioutil" "os" "path" "strconv" @@ -13,10 +13,9 @@ import ( "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/devicemapper" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" - "github.com/docker/docker/pkg/mount" - "github.com/docker/go-units" - "github.com/pkg/errors" + units "github.com/docker/go-units" + "github.com/moby/locker" + "github.com/moby/sys/mount" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -129,11 +128,7 @@ func (d *Driver) Cleanup() error { return err } - if umountErr != nil { - return errors.Wrapf(umountErr, "error unmounting %s", d.home) - } - - return nil + return umountErr } // CreateReadWrite creates a layer that is writable for use as a container @@ -225,7 +220,7 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) { if _, err := os.Stat(idFile); err != nil && os.IsNotExist(err) { // Create an "id" file with the container/image id in it to help reconstruct this in case // of later problems - if err := ioutil.WriteFile(idFile, []byte(id), 0600); err != nil { + if err := os.WriteFile(idFile, []byte(id), 0600); err != nil { d.ctr.Decrement(mp) d.DeviceSet.UnmountDevice(id, mp) return nil, err diff --git a/daemon/graphdriver/devmapper/mount.go b/daemon/graphdriver/devmapper/mount.go index 78d05b079218e..724f64cd2b1c1 100644 --- a/daemon/graphdriver/devmapper/mount.go +++ b/daemon/graphdriver/devmapper/mount.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package devmapper // import "github.com/docker/docker/daemon/graphdriver/devmapper" diff --git a/daemon/graphdriver/driver.go b/daemon/graphdriver/driver.go index 109e00b3d83d3..a9e8ce4c919af 100644 --- a/daemon/graphdriver/driver.go +++ b/daemon/graphdriver/driver.go @@ -29,7 +29,7 @@ var ( drivers map[string]InitFunc ) -//CreateOpts contains optional arguments for Create() and CreateReadWrite() +// CreateOpts contains optional arguments for Create() and CreateReadWrite() // methods. type CreateOpts struct { MountLabel string @@ -319,7 +319,7 @@ func isEmptyDir(name string) bool { func isDeprecated(name string) bool { switch name { // NOTE: when deprecating a driver, update daemon.fillDriverInfo() accordingly - case "devicemapper", "overlay": + case "aufs", "devicemapper", "overlay": return true } return false diff --git a/daemon/graphdriver/driver_linux.go b/daemon/graphdriver/driver_linux.go index 61c6b24a9cb44..c4cfe8e891538 100644 --- a/daemon/graphdriver/driver_linux.go +++ b/daemon/graphdriver/driver_linux.go @@ -1,7 +1,7 @@ package graphdriver // import "github.com/docker/docker/daemon/graphdriver" import ( - "github.com/docker/docker/pkg/mount" + "github.com/moby/sys/mountinfo" "golang.org/x/sys/unix" ) @@ -44,11 +44,13 @@ const ( FsMagicZfs = FsMagic(0x2fc12fc1) // FsMagicOverlay filesystem id for overlay FsMagicOverlay = FsMagic(0x794C7630) + // FsMagicFUSE filesystem id for FUSE + FsMagicFUSE = FsMagic(0x65735546) ) var ( // List of drivers that should be used in an order - priority = "btrfs,zfs,overlay2,aufs,overlay,devicemapper,vfs" + priority = "overlay2,fuse-overlayfs,btrfs,zfs,aufs,overlay,devicemapper,vfs" // FsNames maps filesystem id to name of the filesystem. FsNames = map[FsMagic]string{ @@ -58,6 +60,7 @@ var ( FsMagicEcryptfs: "ecryptfs", FsMagicExtfs: "extfs", FsMagicF2fs: "f2fs", + FsMagicFUSE: "fuse", FsMagicGPFS: "gpfs", FsMagicJffs2Fs: "jffs2", FsMagicJfs: "jfs", @@ -110,7 +113,7 @@ type defaultChecker struct { } func (c *defaultChecker) IsMounted(path string) bool { - m, _ := mount.Mounted(path) + m, _ := mountinfo.Mounted(path) return m } @@ -118,6 +121,9 @@ func (c *defaultChecker) IsMounted(path string) bool { func Mounted(fsType FsMagic, mountPath string) (bool, error) { var buf unix.Statfs_t if err := unix.Statfs(mountPath, &buf); err != nil { + if err == unix.ENOENT { // not exist, thus not mounted + err = nil + } return false, err } return FsMagic(buf.Type) == fsType, nil diff --git a/daemon/graphdriver/driver_test.go b/daemon/graphdriver/driver_test.go index e6f973c397e87..474370203f4cc 100644 --- a/daemon/graphdriver/driver_test.go +++ b/daemon/graphdriver/driver_test.go @@ -1,16 +1,15 @@ package graphdriver // import "github.com/docker/docker/daemon/graphdriver" import ( - "io/ioutil" "os" "path/filepath" "testing" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestIsEmptyDir(t *testing.T) { - tmp, err := ioutil.TempDir("", "test-is-empty-dir") + tmp, err := os.MkdirTemp("", "test-is-empty-dir") assert.NilError(t, err) defer os.RemoveAll(tmp) @@ -29,7 +28,7 @@ func TestIsEmptyDir(t *testing.T) { d = filepath.Join(tmp, "dir-with-empty-file") err = os.Mkdir(d, 0755) assert.NilError(t, err) - _, err = ioutil.TempFile(d, "file") + _, err = os.CreateTemp(d, "file") assert.NilError(t, err) empty = isEmptyDir(d) assert.Check(t, !empty) diff --git a/daemon/graphdriver/driver_unsupported.go b/daemon/graphdriver/driver_unsupported.go index 1f2e8f071b67f..60aea63b9ccad 100644 --- a/daemon/graphdriver/driver_unsupported.go +++ b/daemon/graphdriver/driver_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows && !freebsd // +build !linux,!windows,!freebsd package graphdriver // import "github.com/docker/docker/daemon/graphdriver" diff --git a/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go b/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go new file mode 100644 index 0000000000000..b31931a8ca733 --- /dev/null +++ b/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs.go @@ -0,0 +1,544 @@ +//go:build linux +// +build linux + +package fuseoverlayfs // import "github.com/docker/docker/daemon/graphdriver/fuse-overlayfs" + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "os/exec" + "path" + "path/filepath" + "strings" + + "github.com/containerd/containerd/pkg/userns" + "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/daemon/graphdriver/overlayutils" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/chrootarchive" + "github.com/docker/docker/pkg/containerfs" + "github.com/docker/docker/pkg/directory" + "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/parsers/kernel" + "github.com/docker/docker/pkg/system" + "github.com/moby/locker" + "github.com/moby/sys/mount" + "github.com/opencontainers/selinux/go-selinux/label" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +) + +var ( + // untar defines the untar method + untar = chrootarchive.UntarUncompressed +) + +const ( + driverName = "fuse-overlayfs" + binary = "fuse-overlayfs" + linkDir = "l" + diffDirName = "diff" + workDirName = "work" + mergedDirName = "merged" + lowerFile = "lower" + maxDepth = 128 + + // idLength represents the number of random characters + // which can be used to create the unique link identifier + // for every layer. If this value is too long then the + // page size limit for the mount command may be exceeded. + // The idLength should be selected such that following equation + // is true (512 is a buffer for label metadata). + // ((idLength + len(linkDir) + 1) * maxDepth) <= (pageSize - 512) + idLength = 26 +) + +// Driver contains information about the home directory and the list of active +// mounts that are created using this driver. +type Driver struct { + home string + uidMaps []idtools.IDMap + gidMaps []idtools.IDMap + ctr *graphdriver.RefCounter + naiveDiff graphdriver.DiffDriver + locker *locker.Locker +} + +var ( + logger = logrus.WithField("storage-driver", driverName) +) + +func init() { + graphdriver.Register(driverName, Init) +} + +// Init returns the naive diff driver for fuse-overlayfs. +// If fuse-overlayfs is not supported on the host, the error +// graphdriver.ErrNotSupported is returned. +func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) { + if _, err := exec.LookPath(binary); err != nil { + logger.Error(err) + return nil, graphdriver.ErrNotSupported + } + if !kernel.CheckKernelVersion(4, 18, 0) { + return nil, graphdriver.ErrNotSupported + } + + remappedRoot := idtools.NewIDMappingsFromMaps(uidMaps, gidMaps) + currentID := idtools.CurrentIdentity() + dirID := idtools.Identity{ + UID: currentID.UID, + GID: remappedRoot.RootPair().GID, + } + + if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil { + return nil, err + } + if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 700, currentID); err != nil { + return nil, err + } + + d := &Driver{ + home: home, + uidMaps: uidMaps, + gidMaps: gidMaps, + ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicFUSE)), + locker: locker.New(), + } + + d.naiveDiff = graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps) + + return d, nil +} + +func (d *Driver) String() string { + return driverName +} + +// Status returns current driver information in a two dimensional string array. +func (d *Driver) Status() [][2]string { + return [][2]string{} +} + +// GetMetadata returns metadata about the overlay driver such as the LowerDir, +// UpperDir, WorkDir, and MergeDir used to store data. +func (d *Driver) GetMetadata(id string) (map[string]string, error) { + dir := d.dir(id) + if _, err := os.Stat(dir); err != nil { + return nil, err + } + + metadata := map[string]string{ + "WorkDir": path.Join(dir, workDirName), + "MergedDir": path.Join(dir, mergedDirName), + "UpperDir": path.Join(dir, diffDirName), + } + + lowerDirs, err := d.getLowerDirs(id) + if err != nil { + return nil, err + } + if len(lowerDirs) > 0 { + metadata["LowerDir"] = strings.Join(lowerDirs, ":") + } + + return metadata, nil +} + +// Cleanup any state created by overlay which should be cleaned when daemon +// is being shutdown. For now, we just have to unmount the bind mounted +// we had created. +func (d *Driver) Cleanup() error { + return mount.RecursiveUnmount(d.home) +} + +// CreateReadWrite creates a layer that is writable for use as a container +// file system. +func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { + if opts != nil && len(opts.StorageOpt) != 0 { + return fmt.Errorf("--storage-opt is not supported") + } + return d.create(id, parent, opts) +} + +// Create is used to create the upper, lower, and merge directories required for overlay fs for a given id. +// The parent filesystem is used to configure these directories for the overlay. +func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { + if opts != nil && len(opts.StorageOpt) != 0 { + return fmt.Errorf("--storage-opt is not supported") + } + return d.create(id, parent, opts) +} + +func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr error) { + dir := d.dir(id) + + rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) + if err != nil { + return err + } + root := idtools.Identity{UID: rootUID, GID: rootGID} + + dirID := idtools.Identity{ + UID: rootUID, + GID: rootGID, + } + + if err := idtools.MkdirAllAndChown(path.Dir(dir), 0710, dirID); err != nil { + return err + } + if err := idtools.MkdirAndChown(dir, 0710, dirID); err != nil { + return err + } + + defer func() { + // Clean up on failure + if retErr != nil { + os.RemoveAll(dir) + } + }() + + if opts != nil && len(opts.StorageOpt) > 0 { + return fmt.Errorf("--storage-opt is not supported") + } + + if err := idtools.MkdirAndChown(path.Join(dir, diffDirName), 0755, root); err != nil { + return err + } + + lid := overlayutils.GenerateID(idLength, logger) + if err := os.Symlink(path.Join("..", id, diffDirName), path.Join(d.home, linkDir, lid)); err != nil { + return err + } + + // Write link id to link file + if err := os.WriteFile(path.Join(dir, "link"), []byte(lid), 0644); err != nil { + return err + } + + // if no parent directory, done + if parent == "" { + return nil + } + + if err := idtools.MkdirAndChown(path.Join(dir, workDirName), 0710, dirID); err != nil { + return err + } + + if err := os.WriteFile(path.Join(d.dir(parent), "committed"), []byte{}, 0600); err != nil { + return err + } + + lower, err := d.getLower(parent) + if err != nil { + return err + } + if lower != "" { + if err := os.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0666); err != nil { + return err + } + } + + return nil +} + +func (d *Driver) getLower(parent string) (string, error) { + parentDir := d.dir(parent) + + // Ensure parent exists + if _, err := os.Lstat(parentDir); err != nil { + return "", err + } + + // Read Parent link fileA + parentLink, err := os.ReadFile(path.Join(parentDir, "link")) + if err != nil { + return "", err + } + lowers := []string{path.Join(linkDir, string(parentLink))} + + parentLower, err := os.ReadFile(path.Join(parentDir, lowerFile)) + if err == nil { + parentLowers := strings.Split(string(parentLower), ":") + lowers = append(lowers, parentLowers...) + } + if len(lowers) > maxDepth { + return "", errors.New("max depth exceeded") + } + return strings.Join(lowers, ":"), nil +} + +func (d *Driver) dir(id string) string { + return path.Join(d.home, id) +} + +func (d *Driver) getLowerDirs(id string) ([]string, error) { + var lowersArray []string + lowers, err := os.ReadFile(path.Join(d.dir(id), lowerFile)) + if err == nil { + for _, s := range strings.Split(string(lowers), ":") { + lp, err := os.Readlink(path.Join(d.home, s)) + if err != nil { + return nil, err + } + lowersArray = append(lowersArray, path.Clean(path.Join(d.home, linkDir, lp))) + } + } else if !os.IsNotExist(err) { + return nil, err + } + return lowersArray, nil +} + +// Remove cleans the directories that are created for this id. +func (d *Driver) Remove(id string) error { + if id == "" { + return fmt.Errorf("refusing to remove the directories: id is empty") + } + d.locker.Lock(id) + defer d.locker.Unlock(id) + dir := d.dir(id) + lid, err := os.ReadFile(path.Join(dir, "link")) + if err == nil { + if len(lid) == 0 { + logger.Errorf("refusing to remove empty link for layer %v", id) + } else if err := os.RemoveAll(path.Join(d.home, linkDir, string(lid))); err != nil { + logger.Debugf("Failed to remove link: %v", err) + } + } + + if err := system.EnsureRemoveAll(dir); err != nil && !os.IsNotExist(err) { + return err + } + return nil +} + +// Get creates and mounts the required file system for the given id and returns the mount path. +func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr error) { + d.locker.Lock(id) + defer d.locker.Unlock(id) + dir := d.dir(id) + if _, err := os.Stat(dir); err != nil { + return nil, err + } + + diffDir := path.Join(dir, diffDirName) + lowers, err := os.ReadFile(path.Join(dir, lowerFile)) + if err != nil { + // If no lower, just return diff directory + if os.IsNotExist(err) { + return containerfs.NewLocalContainerFS(diffDir), nil + } + return nil, err + } + + mergedDir := path.Join(dir, mergedDirName) + if count := d.ctr.Increment(mergedDir); count > 1 { + return containerfs.NewLocalContainerFS(mergedDir), nil + } + defer func() { + if retErr != nil { + if c := d.ctr.Decrement(mergedDir); c <= 0 { + if unmounted := fusermountU(mergedDir); !unmounted { + if mntErr := unix.Unmount(mergedDir, 0); mntErr != nil { + logger.Errorf("error unmounting %v: %v", mergedDir, mntErr) + } + } + // Cleanup the created merged directory; see the comment in Put's rmdir + if rmErr := unix.Rmdir(mergedDir); rmErr != nil && !os.IsNotExist(rmErr) { + logger.Debugf("Failed to remove %s: %v: %v", id, rmErr, err) + } + } + } + }() + + workDir := path.Join(dir, workDirName) + splitLowers := strings.Split(string(lowers), ":") + absLowers := make([]string, len(splitLowers)) + for i, s := range splitLowers { + absLowers[i] = path.Join(d.home, s) + } + var readonly bool + if _, err := os.Stat(path.Join(dir, "committed")); err == nil { + readonly = true + } else if !os.IsNotExist(err) { + return nil, err + } + + var opts string + if readonly { + opts = "lowerdir=" + diffDir + ":" + strings.Join(absLowers, ":") + } else { + opts = "lowerdir=" + strings.Join(absLowers, ":") + ",upperdir=" + diffDir + ",workdir=" + workDir + } + + mountData := label.FormatMountLabel(opts, mountLabel) + mountTarget := mergedDir + + rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) + if err != nil { + return nil, err + } + if err := idtools.MkdirAndChown(mergedDir, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + return nil, err + } + + mountProgram := exec.Command(binary, "-o", mountData, mountTarget) + mountProgram.Dir = d.home + var b bytes.Buffer + mountProgram.Stderr = &b + if err = mountProgram.Run(); err != nil { + output := b.String() + if output == "" { + output = "" + } + return nil, errors.Wrapf(err, "using mount program %s: %s", binary, output) + } + + return containerfs.NewLocalContainerFS(mergedDir), nil +} + +// Put unmounts the mount path created for the give id. +// It also removes the 'merged' directory to force the kernel to unmount the +// overlay mount in other namespaces. +func (d *Driver) Put(id string) error { + d.locker.Lock(id) + defer d.locker.Unlock(id) + dir := d.dir(id) + _, err := os.ReadFile(path.Join(dir, lowerFile)) + if err != nil { + // If no lower, no mount happened and just return directly + if os.IsNotExist(err) { + return nil + } + return err + } + + mountpoint := path.Join(dir, mergedDirName) + if count := d.ctr.Decrement(mountpoint); count > 0 { + return nil + } + if unmounted := fusermountU(mountpoint); !unmounted { + if err := unix.Unmount(mountpoint, unix.MNT_DETACH); err != nil { + logger.Debugf("Failed to unmount %s overlay: %s - %v", id, mountpoint, err) + } + } + // Remove the mountpoint here. Removing the mountpoint (in newer kernels) + // will cause all other instances of this mount in other mount namespaces + // to be unmounted. This is necessary to avoid cases where an overlay mount + // that is present in another namespace will cause subsequent mounts + // operations to fail with ebusy. We ignore any errors here because this may + // fail on older kernels which don't have + // torvalds/linux@8ed936b5671bfb33d89bc60bdcc7cf0470ba52fe applied. + if err := unix.Rmdir(mountpoint); err != nil && !os.IsNotExist(err) { + logger.Debugf("Failed to remove %s overlay: %v", id, err) + } + return nil +} + +// Exists checks to see if the id is already mounted. +func (d *Driver) Exists(id string) bool { + _, err := os.Stat(d.dir(id)) + return err == nil +} + +// isParent determines whether the given parent is the direct parent of the +// given layer id +func (d *Driver) isParent(id, parent string) bool { + lowers, err := d.getLowerDirs(id) + if err != nil { + return false + } + if parent == "" && len(lowers) > 0 { + return false + } + + parentDir := d.dir(parent) + var ld string + if len(lowers) > 0 { + ld = filepath.Dir(lowers[0]) + } + if ld == "" && parent == "" { + return true + } + return ld == parentDir +} + +// ApplyDiff applies the new layer into a root +func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) { + if !d.isParent(id, parent) { + return d.naiveDiff.ApplyDiff(id, parent, diff) + } + + applyDir := d.getDiffPath(id) + + logger.Debugf("Applying tar in %s", applyDir) + // Overlay doesn't need the parent id to apply the diff + if err := untar(diff, applyDir, &archive.TarOptions{ + UIDMaps: d.uidMaps, + GIDMaps: d.gidMaps, + // Use AUFS whiteout format: https://github.com/containers/storage/blob/39a8d5ed9843844eafb5d2ba6e6a7510e0126f40/drivers/overlay/overlay.go#L1084-L1089 + WhiteoutFormat: archive.AUFSWhiteoutFormat, + InUserNS: userns.RunningInUserNS(), + }); err != nil { + return 0, err + } + + return directory.Size(context.TODO(), applyDir) +} + +func (d *Driver) getDiffPath(id string) string { + dir := d.dir(id) + + return path.Join(dir, diffDirName) +} + +// DiffSize calculates the changes between the specified id +// and its parent and returns the size in bytes of the changes +// relative to its base filesystem directory. +func (d *Driver) DiffSize(id, parent string) (size int64, err error) { + return d.naiveDiff.DiffSize(id, parent) +} + +// Diff produces an archive of the changes between the specified +// layer and its parent layer which may be "". +func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) { + return d.naiveDiff.Diff(id, parent) +} + +// Changes produces a list of changes between the specified layer and its +// parent layer. If parent is "", then all changes will be ADD changes. +func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { + return d.naiveDiff.Changes(id, parent) +} + +// fusermountU is from https://github.com/containers/storage/blob/39a8d5ed9843844eafb5d2ba6e6a7510e0126f40/drivers/overlay/overlay.go#L1016-L1040 +func fusermountU(mountpoint string) (unmounted bool) { + // Attempt to unmount the FUSE mount using either fusermount or fusermount3. + // If they fail, fallback to unix.Unmount + for _, v := range []string{"fusermount3", "fusermount"} { + err := exec.Command(v, "-u", mountpoint).Run() + if err != nil && !os.IsNotExist(err) { + logrus.Debugf("Error unmounting %s with %s - %v", mountpoint, v, err) + } + if err == nil { + unmounted = true + break + } + } + // If fusermount|fusermount3 failed to unmount the FUSE file system, make sure all + // pending changes are propagated to the file system + if !unmounted { + fd, err := unix.Open(mountpoint, unix.O_DIRECTORY, 0) + if err == nil { + if err := unix.Syncfs(fd); err != nil { + logrus.Debugf("Error Syncfs(%s) - %v", mountpoint, err) + } + unix.Close(fd) + } + } + return +} diff --git a/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs_test.go b/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs_test.go new file mode 100644 index 0000000000000..0d5b29e53846c --- /dev/null +++ b/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs_test.go @@ -0,0 +1,86 @@ +//go:build linux +// +build linux + +package fuseoverlayfs // import "github.com/docker/docker/daemon/graphdriver/fuse-overlayfs" + +import ( + "testing" + + "github.com/docker/docker/daemon/graphdriver" + "github.com/docker/docker/daemon/graphdriver/graphtest" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/pkg/reexec" +) + +func init() { + // Do not sure chroot to speed run time and allow archive + // errors or hangs to be debugged directly from the test process. + untar = archive.UntarUncompressed + graphdriver.ApplyUncompressedLayer = archive.ApplyUncompressedLayer + + reexec.Init() +} + +// This avoids creating a new driver for each test if all tests are run +// Make sure to put new tests between TestFUSEOverlayFSSetup and TestFUSEOverlayFSTeardown +func TestFUSEOverlayFSSetup(t *testing.T) { + graphtest.GetDriver(t, driverName) +} + +func TestFUSEOverlayFSCreateEmpty(t *testing.T) { + graphtest.DriverTestCreateEmpty(t, driverName) +} + +func TestFUSEOverlayFSCreateBase(t *testing.T) { + graphtest.DriverTestCreateBase(t, driverName) +} + +func TestFUSEOverlayFSCreateSnap(t *testing.T) { + graphtest.DriverTestCreateSnap(t, driverName) +} + +func TestFUSEOverlayFS128LayerRead(t *testing.T) { + graphtest.DriverTestDeepLayerRead(t, 128, driverName) +} + +func TestFUSEOverlayFSTeardown(t *testing.T) { + graphtest.PutDriver(t) +} + +// Benchmarks should always setup new driver + +func BenchmarkExists(b *testing.B) { + graphtest.DriverBenchExists(b, driverName) +} + +func BenchmarkGetEmpty(b *testing.B) { + graphtest.DriverBenchGetEmpty(b, driverName) +} + +func BenchmarkDiffBase(b *testing.B) { + graphtest.DriverBenchDiffBase(b, driverName) +} + +func BenchmarkDiffSmallUpper(b *testing.B) { + graphtest.DriverBenchDiffN(b, 10, 10, driverName) +} + +func BenchmarkDiff10KFileUpper(b *testing.B) { + graphtest.DriverBenchDiffN(b, 10, 10000, driverName) +} + +func BenchmarkDiff10KFilesBottom(b *testing.B) { + graphtest.DriverBenchDiffN(b, 10000, 10, driverName) +} + +func BenchmarkDiffApply100(b *testing.B) { + graphtest.DriverBenchDiffApplyN(b, 100, driverName) +} + +func BenchmarkDiff20Layers(b *testing.B) { + graphtest.DriverBenchDeepLayerDiff(b, 20, driverName) +} + +func BenchmarkRead20Layers(b *testing.B) { + graphtest.DriverBenchDeepLayerRead(b, 20, driverName) +} diff --git a/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs_unsupported.go b/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs_unsupported.go new file mode 100644 index 0000000000000..1b115345e9cc8 --- /dev/null +++ b/daemon/graphdriver/fuse-overlayfs/fuseoverlayfs_unsupported.go @@ -0,0 +1,4 @@ +//go:build !linux +// +build !linux + +package fuseoverlayfs // import "github.com/docker/docker/daemon/graphdriver/fuse-overlayfs" diff --git a/daemon/graphdriver/graphtest/graphbench_unix.go b/daemon/graphdriver/graphtest/graphbench_unix.go index 22de8d17813ad..378794a5310d0 100644 --- a/daemon/graphdriver/graphtest/graphbench_unix.go +++ b/daemon/graphdriver/graphtest/graphbench_unix.go @@ -1,15 +1,15 @@ +//go:build linux || freebsd // +build linux freebsd package graphtest // import "github.com/docker/docker/daemon/graphdriver/graphtest" import ( "io" - "io/ioutil" "testing" contdriver "github.com/containerd/continuity/driver" "github.com/docker/docker/pkg/stringid" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) // DriverBenchExists benchmarks calls to exist @@ -76,7 +76,7 @@ func DriverBenchDiffBase(b *testing.B, drivername string, driveroptions ...strin if err != nil { b.Fatal(err) } - _, err = io.Copy(ioutil.Discard, arch) + _, err = io.Copy(io.Discard, arch) if err != nil { b.Fatalf("Error copying archive: %s", err) } @@ -112,7 +112,7 @@ func DriverBenchDiffN(b *testing.B, bottom, top int, drivername string, driverop if err != nil { b.Fatal(err) } - _, err = io.Copy(ioutil.Discard, arch) + _, err = io.Copy(io.Discard, arch) if err != nil { b.Fatalf("Error copying archive: %s", err) } @@ -172,6 +172,10 @@ func DriverBenchDiffApplyN(b *testing.B, fileCount int, drivername string, drive b.StopTimer() arch.Close() + // suppressing "SA9003: empty branch (staticcheck)" instead of commenting-out/removing + // these lines because removing/commenting these lines causes a ripple effect + // of changes, and there's still a to-do below + //nolint:staticcheck if applyDiffSize != diffSize { // TODO: enforce this //b.Fatalf("Apply diff size different, got %d, expected %s", applyDiffSize, diffSize) @@ -207,7 +211,7 @@ func DriverBenchDeepLayerDiff(b *testing.B, layerCount int, drivername string, d if err != nil { b.Fatal(err) } - _, err = io.Copy(ioutil.Discard, arch) + _, err = io.Copy(io.Discard, arch) if err != nil { b.Fatalf("Error copying archive: %s", err) } diff --git a/daemon/graphdriver/graphtest/graphtest_unix.go b/daemon/graphdriver/graphtest/graphtest_unix.go index e83d0bb2ad77e..8b6a6bf971505 100644 --- a/daemon/graphdriver/graphtest/graphtest_unix.go +++ b/daemon/graphdriver/graphtest/graphtest_unix.go @@ -1,24 +1,22 @@ +//go:build linux || freebsd // +build linux freebsd package graphtest // import "github.com/docker/docker/daemon/graphdriver/graphtest" import ( "bytes" - "io/ioutil" - "math/rand" + "crypto/rand" "os" "path" - "reflect" "testing" - "unsafe" "github.com/docker/docker/daemon/graphdriver" - "github.com/docker/docker/daemon/graphdriver/quota" "github.com/docker/docker/pkg/stringid" - "github.com/docker/go-units" + "github.com/docker/docker/quota" + units "github.com/docker/go-units" "golang.org/x/sys/unix" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) var ( @@ -35,7 +33,7 @@ type Driver struct { } func newDriver(t testing.TB, name string, options []string) *Driver { - root, err := ioutil.TempDir("", "docker-graphtest-") + root, err := os.MkdirTemp("", "docker-graphtest-") assert.NilError(t, err) assert.NilError(t, os.MkdirAll(root, 0755)) @@ -294,20 +292,12 @@ func DriverTestChanges(t testing.TB, drivername string, driverOptions ...string) } func writeRandomFile(path string, size uint64) error { - buf := make([]int64, size/8) - - r := rand.NewSource(0) - for i := range buf { - buf[i] = r.Int63() + data := make([]byte, size) + _, err := rand.Read(data) + if err != nil { + return err } - - // Cast to []byte - header := *(*reflect.SliceHeader)(unsafe.Pointer(&buf)) - header.Len *= 8 - header.Cap *= 8 - data := *(*[]byte)(unsafe.Pointer(&header)) - - return ioutil.WriteFile(path, data, 0700) + return os.WriteFile(path, data, 0700) } // DriverTestSetQuota Create a driver and test setting quota. diff --git a/daemon/graphdriver/graphtest/testutil_unix.go b/daemon/graphdriver/graphtest/testutil_unix.go index 6871dca09a690..1c3037c8aa06c 100644 --- a/daemon/graphdriver/graphtest/testutil_unix.go +++ b/daemon/graphdriver/graphtest/testutil_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package graphtest // import "github.com/docker/docker/daemon/graphdriver/graphtest" @@ -10,8 +11,8 @@ import ( contdriver "github.com/containerd/continuity/driver" "github.com/docker/docker/daemon/graphdriver" "golang.org/x/sys/unix" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func verifyFile(t testing.TB, path string, mode os.FileMode, uid, gid uint32) { diff --git a/daemon/graphdriver/lcow/lcow.go b/daemon/graphdriver/lcow/lcow.go deleted file mode 100644 index a26d13ede1f8b..0000000000000 --- a/daemon/graphdriver/lcow/lcow.go +++ /dev/null @@ -1,1100 +0,0 @@ -// +build windows - -// Maintainer: jhowardmsft -// Locale: en-gb -// About: Graph-driver for Linux Containers On Windows (LCOW) -// -// This graphdriver runs in two modes. Yet to be determined which one will -// be the shipping mode. The global mode is where a single utility VM -// is used for all service VM tool operations. This isn't safe security-wise -// as it's attaching a sandbox of multiple containers to it, containing -// untrusted data. This may be fine for client devops scenarios. In -// safe mode, a unique utility VM is instantiated for all service VM tool -// operations. The downside of safe-mode is that operations are slower as -// a new service utility VM has to be started and torn-down when needed. -// -// Options: -// -// The following options are read by the graphdriver itself: -// -// * lcow.globalmode - Enables global service VM Mode -// -- Possible values: true/false -// -- Default if omitted: false -// -// * lcow.sandboxsize - Specifies a custom sandbox size in GB for starting a container -// -- Possible values: >= default sandbox size (opengcs defined, currently 20) -// -- Default if omitted: 20 -// -// The following options are read by opengcs: -// -// * lcow.kirdpath - Specifies a custom path to a kernel/initrd pair -// -- Possible values: Any local path that is not a mapped drive -// -- Default if omitted: %ProgramFiles%\Linux Containers -// -// * lcow.kernel - Specifies a custom kernel file located in the `lcow.kirdpath` path -// -- Possible values: Any valid filename -// -- Default if omitted: bootx64.efi -// -// * lcow.initrd - Specifies a custom initrd file located in the `lcow.kirdpath` path -// -- Possible values: Any valid filename -// -- Default if omitted: initrd.img -// -// * lcow.bootparameters - Specifies additional boot parameters for booting in kernel+initrd mode -// -- Possible values: Any valid linux kernel boot options -// -- Default if omitted: -// -// * lcow.vhdx - Specifies a custom vhdx file to boot (instead of a kernel+initrd) -// -- Possible values: Any valid filename -// -- Default if omitted: uvm.vhdx under `lcow.kirdpath` -// -// * lcow.timeout - Specifies a timeout for utility VM operations in seconds -// -- Possible values: >=0 -// -- Default if omitted: 300 - -// TODO: Grab logs from SVM at terminate or errors - -package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow" - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path" - "path/filepath" - "strconv" - "strings" - "sync" - "syscall" - "time" - - "github.com/Microsoft/hcsshim" - "github.com/Microsoft/opengcs/client" - "github.com/docker/docker/daemon/graphdriver" - "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/pkg/containerfs" - "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/system" - "github.com/sirupsen/logrus" -) - -// init registers this driver to the register. It gets initialised by the -// function passed in the second parameter, implemented in this file. -func init() { - graphdriver.Register("lcow", InitDriver) -} - -const ( - // sandboxFilename is the name of the file containing a layer's sandbox (read-write layer). - sandboxFilename = "sandbox.vhdx" - - // scratchFilename is the name of the scratch-space used by an SVM to avoid running out of memory. - scratchFilename = "scratch.vhdx" - - // layerFilename is the name of the file containing a layer's read-only contents. - // Note this really is VHD format, not VHDX. - layerFilename = "layer.vhd" - - // toolsScratchPath is a location in a service utility VM that the tools can use as a - // scratch space to avoid running out of memory. - toolsScratchPath = "/tmp/scratch" - - // svmGlobalID is the ID used in the serviceVMs map for the global service VM when running in "global" mode. - svmGlobalID = "_lcow_global_svm_" - - // cacheDirectory is the sub-folder under the driver's data-root used to cache blank sandbox and scratch VHDs. - cacheDirectory = "cache" - - // scratchDirectory is the sub-folder under the driver's data-root used for scratch VHDs in service VMs - scratchDirectory = "scratch" - - // errOperationPending is the HRESULT returned by the HCS when the VM termination operation is still pending. - errOperationPending syscall.Errno = 0xc0370103 -) - -// Driver represents an LCOW graph driver. -type Driver struct { - dataRoot string // Root path on the host where we are storing everything. - cachedSandboxFile string // Location of the local default-sized cached sandbox. - cachedSandboxMutex sync.Mutex // Protects race conditions from multiple threads creating the cached sandbox. - cachedScratchFile string // Location of the local cached empty scratch space. - cachedScratchMutex sync.Mutex // Protects race conditions from multiple threads creating the cached scratch. - options []string // Graphdriver options we are initialised with. - globalMode bool // Indicates if running in an unsafe/global service VM mode. - - // NOTE: It is OK to use a cache here because Windows does not support - // restoring containers when the daemon dies. - serviceVms *serviceVMMap // Map of the configs representing the service VM(s) we are running. -} - -// layerDetails is the structure returned by a helper function `getLayerDetails` -// for getting information about a layer folder -type layerDetails struct { - filename string // \path\to\sandbox.vhdx or \path\to\layer.vhd - size int64 // size of the above file - isSandbox bool // true if sandbox.vhdx -} - -// deletefiles is a helper function for initialisation where we delete any -// left-over scratch files in case we were previously forcibly terminated. -func deletefiles(path string, f os.FileInfo, err error) error { - if strings.HasSuffix(f.Name(), ".vhdx") { - logrus.Warnf("lcowdriver: init: deleting stale scratch file %s", path) - return os.Remove(path) - } - return nil -} - -// InitDriver returns a new LCOW storage driver. -func InitDriver(dataRoot string, options []string, _, _ []idtools.IDMap) (graphdriver.Driver, error) { - title := "lcowdriver: init:" - - cd := filepath.Join(dataRoot, cacheDirectory) - sd := filepath.Join(dataRoot, scratchDirectory) - - d := &Driver{ - dataRoot: dataRoot, - options: options, - cachedSandboxFile: filepath.Join(cd, sandboxFilename), - cachedScratchFile: filepath.Join(cd, scratchFilename), - serviceVms: &serviceVMMap{ - svms: make(map[string]*serviceVMMapItem), - }, - globalMode: false, - } - - // Looks for relevant options - for _, v := range options { - opt := strings.SplitN(v, "=", 2) - if len(opt) == 2 { - switch strings.ToLower(opt[0]) { - case "lcow.globalmode": - var err error - d.globalMode, err = strconv.ParseBool(opt[1]) - if err != nil { - return nil, fmt.Errorf("%s failed to parse value for 'lcow.globalmode' - must be 'true' or 'false'", title) - } - break - } - } - } - - // Make sure the dataRoot directory is created - if err := idtools.MkdirAllAndChown(dataRoot, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil { - return nil, fmt.Errorf("%s failed to create '%s': %v", title, dataRoot, err) - } - - // Make sure the cache directory is created under dataRoot - if err := idtools.MkdirAllAndChown(cd, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil { - return nil, fmt.Errorf("%s failed to create '%s': %v", title, cd, err) - } - - // Make sure the scratch directory is created under dataRoot - if err := idtools.MkdirAllAndChown(sd, 0700, idtools.Identity{UID: 0, GID: 0}); err != nil { - return nil, fmt.Errorf("%s failed to create '%s': %v", title, sd, err) - } - - // Delete any items in the scratch directory - filepath.Walk(sd, deletefiles) - - logrus.Infof("%s dataRoot: %s globalMode: %t", title, dataRoot, d.globalMode) - - return d, nil -} - -func (d *Driver) getVMID(id string) string { - if d.globalMode { - return svmGlobalID - } - return id -} - -// remapLongToShortContainerPath does the mapping of a long container path for a -// SCSI attached disk, to a short container path where it's actually mounted. -func remapLongToShortContainerPath(longContainerPath string, attachCounter uint64, svmName string) string { - shortContainerPath := longContainerPath - if shortContainerPath != "" && shortContainerPath != toolsScratchPath { - shortContainerPath = fmt.Sprintf("/tmp/d%d", attachCounter) - logrus.Debugf("lcowdriver: UVM %s: remapping %s --> %s", svmName, longContainerPath, shortContainerPath) - } - return shortContainerPath -} - -// startServiceVMIfNotRunning starts a service utility VM if it is not currently running. -// It can optionally be started with a mapped virtual disk. Returns a opengcs config structure -// representing the VM. -func (d *Driver) startServiceVMIfNotRunning(id string, mvdToAdd []hcsshim.MappedVirtualDisk, context string) (_ *serviceVM, err error) { - // Use the global ID if in global mode - id = d.getVMID(id) - - title := "lcowdriver: startServiceVMIfNotRunning " + id - - // Attempt to add ID to the service vm map - logrus.Debugf("%s: adding entry to service vm map", title) - svm, exists, err := d.serviceVms.add(id) - if err != nil && err == errVMisTerminating { - // VM is in the process of terminating. Wait until it's done and then try again - logrus.Debugf("%s: VM with current ID still in the process of terminating", title) - if err := svm.getStopError(); err != nil { - logrus.Debugf("%s: VM did not stop successfully: %s", title, err) - return nil, err - } - return d.startServiceVMIfNotRunning(id, mvdToAdd, context) - } else if err != nil { - logrus.Debugf("%s: failed to add service vm to map: %s", title, err) - return nil, fmt.Errorf("%s: failed to add to service vm map: %s", title, err) - } - - if exists { - // Service VM is already up and running. In this case, just hot add the vhds. - // Note that hotAddVHDs will remap long to short container paths, so no need - // for us to that here. - logrus.Debugf("%s: service vm already exists. Just hot adding: %+v", title, mvdToAdd) - if err := svm.hotAddVHDs(mvdToAdd...); err != nil { - logrus.Debugf("%s: failed to hot add vhds on service vm creation: %s", title, err) - return nil, fmt.Errorf("%s: failed to hot add vhds on service vm: %s", title, err) - } - return svm, nil - } - - // We are the first service for this id, so we need to start it - logrus.Debugf("%s: service vm doesn't exist. Now starting it up", title) - - defer func() { - // Signal that start has finished, passing in the error if any. - svm.signalStartFinished(err) - if err != nil { - // We added a ref to the VM, since we failed, we should delete the ref. - d.terminateServiceVM(id, "error path on startServiceVMIfNotRunning", false) - } - }() - - // Generate a default configuration - if err := svm.config.GenerateDefault(d.options); err != nil { - return nil, fmt.Errorf("%s: failed to generate default gogcs configuration for global svm (%s): %s", title, context, err) - } - - // For the name, we deliberately suffix if safe-mode to ensure that it doesn't - // clash with another utility VM which may be running for the container itself. - // This also makes it easier to correlate through Get-ComputeProcess. - if id == svmGlobalID { - svm.config.Name = svmGlobalID - } else { - svm.config.Name = fmt.Sprintf("%s_svm", id) - } - - // Ensure we take the cached scratch mutex around the check to ensure the file is complete - // and not in the process of being created by another thread. - scratchTargetFile := filepath.Join(d.dataRoot, scratchDirectory, fmt.Sprintf("%s.vhdx", id)) - - logrus.Debugf("%s: locking cachedScratchMutex", title) - d.cachedScratchMutex.Lock() - if _, err := os.Stat(d.cachedScratchFile); err == nil { - // Make a copy of cached scratch to the scratch directory - logrus.Debugf("%s: (%s) cloning cached scratch for mvd", title, context) - if err := client.CopyFile(d.cachedScratchFile, scratchTargetFile, true); err != nil { - logrus.Debugf("%s: releasing cachedScratchMutex on err: %s", title, err) - d.cachedScratchMutex.Unlock() - return nil, err - } - - // Add the cached clone as a mapped virtual disk - logrus.Debugf("%s: (%s) adding cloned scratch as mvd", title, context) - mvd := hcsshim.MappedVirtualDisk{ - HostPath: scratchTargetFile, - ContainerPath: toolsScratchPath, - CreateInUtilityVM: true, - } - svm.config.MappedVirtualDisks = append(svm.config.MappedVirtualDisks, mvd) - svm.scratchAttached = true - } - - logrus.Debugf("%s: releasing cachedScratchMutex", title) - d.cachedScratchMutex.Unlock() - - // Add mapped virtual disks. First those that are already in the configuration. Generally, - // the only one that will be here is the service VMs scratch. The exception is when invoked - // via the graphdrivers DiffGetter implementation. - for i, mvd := range svm.config.MappedVirtualDisks { - svm.attachCounter++ - svm.attachedVHDs[mvd.HostPath] = &attachedVHD{refCount: 1, attachCounter: svm.attachCounter} - - // No-op for the service VMs scratch disk. Only applicable in the DiffGetter interface invocation. - svm.config.MappedVirtualDisks[i].ContainerPath = remapLongToShortContainerPath(mvd.ContainerPath, svm.attachCounter, svm.config.Name) - } - - // Then the remaining ones to add, and adding them to the startup configuration. - for _, mvd := range mvdToAdd { - svm.attachCounter++ - svm.attachedVHDs[mvd.HostPath] = &attachedVHD{refCount: 1, attachCounter: svm.attachCounter} - mvd.ContainerPath = remapLongToShortContainerPath(mvd.ContainerPath, svm.attachCounter, svm.config.Name) - svm.config.MappedVirtualDisks = append(svm.config.MappedVirtualDisks, mvd) - } - - // Start it. - logrus.Debugf("%s: (%s) starting %s", title, context, svm.config.Name) - if err := svm.config.StartUtilityVM(); err != nil { - return nil, fmt.Errorf("failed to start service utility VM (%s): %s", context, err) - } - - // defer function to terminate the VM if the next steps fail - defer func() { - if err != nil { - waitTerminate(svm, fmt.Sprintf("%s: (%s)", title, context)) - } - }() - - // Now we have a running service VM, we can create the cached scratch file if it doesn't exist. - logrus.Debugf("%s: locking cachedScratchMutex", title) - d.cachedScratchMutex.Lock() - if _, err := os.Stat(d.cachedScratchFile); err != nil { - logrus.Debugf("%s: (%s) creating an SVM scratch", title, context) - - // Don't use svm.CreateExt4Vhdx since that only works when the service vm is setup, - // but we're still in that process right now. - if err := svm.config.CreateExt4Vhdx(scratchTargetFile, client.DefaultVhdxSizeGB, d.cachedScratchFile); err != nil { - logrus.Debugf("%s: (%s) releasing cachedScratchMutex on error path", title, context) - d.cachedScratchMutex.Unlock() - logrus.Debugf("%s: failed to create vm scratch %s: %s", title, scratchTargetFile, err) - return nil, fmt.Errorf("failed to create SVM scratch VHDX (%s): %s", context, err) - } - } - logrus.Debugf("%s: (%s) releasing cachedScratchMutex", title, context) - d.cachedScratchMutex.Unlock() - - // Hot-add the scratch-space if not already attached - if !svm.scratchAttached { - logrus.Debugf("%s: (%s) hot-adding scratch %s", title, context, scratchTargetFile) - if err := svm.hotAddVHDsAtStart(hcsshim.MappedVirtualDisk{ - HostPath: scratchTargetFile, - ContainerPath: toolsScratchPath, - CreateInUtilityVM: true, - }); err != nil { - logrus.Debugf("%s: failed to hot-add scratch %s: %s", title, scratchTargetFile, err) - return nil, fmt.Errorf("failed to hot-add %s failed: %s", scratchTargetFile, err) - } - svm.scratchAttached = true - // Don't need to ref-count here as it will be done via hotAddVHDsAtStart() call above. - } - - logrus.Debugf("%s: (%s) success", title, context) - return svm, nil -} - -// terminateServiceVM terminates a service utility VM if its running if it's, -// not being used by any goroutine, but does nothing when in global mode as it's -// lifetime is limited to that of the daemon. If the force flag is set, then -// the VM will be killed regardless of the ref count or if it's global. -func (d *Driver) terminateServiceVM(id, context string, force bool) (err error) { - // We don't do anything in safe mode unless the force flag has been passed, which - // is only the case for cleanup at driver termination. - if d.globalMode && !force { - logrus.Debugf("lcowdriver: terminateservicevm: %s (%s) - doing nothing as in global mode", id, context) - return nil - } - - id = d.getVMID(id) - - var svm *serviceVM - var lastRef bool - if !force { - // In the not force case, we ref count - svm, lastRef, err = d.serviceVms.decrementRefCount(id) - } else { - // In the force case, we ignore the ref count and just set it to 0 - svm, err = d.serviceVms.setRefCountZero(id) - lastRef = true - } - - if err == errVMUnknown { - return nil - } else if err == errVMisTerminating { - return svm.getStopError() - } else if !lastRef { - return nil - } - - // We run the deletion of the scratch as a deferred function to at least attempt - // clean-up in case of errors. - defer func() { - if svm.scratchAttached { - scratchTargetFile := filepath.Join(d.dataRoot, scratchDirectory, fmt.Sprintf("%s.vhdx", id)) - logrus.Debugf("lcowdriver: terminateservicevm: %s (%s) - deleting scratch %s", id, context, scratchTargetFile) - if errRemove := os.Remove(scratchTargetFile); errRemove != nil { - logrus.Warnf("failed to remove scratch file %s (%s): %s", scratchTargetFile, context, errRemove) - err = errRemove - } - } - - // This function shouldn't actually return error unless there is a bug - if errDelete := d.serviceVms.deleteID(id); errDelete != nil { - logrus.Warnf("failed to service vm from svm map %s (%s): %s", id, context, errDelete) - } - - // Signal that this VM has stopped - svm.signalStopFinished(err) - }() - - // Now it's possible that the service VM failed to start and now we are trying to terminate it. - // In this case, we will relay the error to the goroutines waiting for this vm to stop. - if err := svm.getStartError(); err != nil { - logrus.Debugf("lcowdriver: terminateservicevm: %s had failed to start up: %s", id, err) - return err - } - - if err := waitTerminate(svm, fmt.Sprintf("terminateservicevm: %s (%s)", id, context)); err != nil { - return err - } - - logrus.Debugf("lcowdriver: terminateservicevm: %s (%s) - success", id, context) - return nil -} - -func waitTerminate(svm *serviceVM, context string) error { - if svm.config == nil { - return fmt.Errorf("lcowdriver: waitTermiante: Nil utility VM. %s", context) - } - - logrus.Debugf("lcowdriver: waitTerminate: Calling terminate: %s", context) - if err := svm.config.Uvm.Terminate(); err != nil { - // We might get operation still pending from the HCS. In that case, we shouldn't return - // an error since we call wait right after. - underlyingError := err - if conterr, ok := err.(*hcsshim.ContainerError); ok { - underlyingError = conterr.Err - } - - if syscallErr, ok := underlyingError.(syscall.Errno); ok { - underlyingError = syscallErr - } - - if underlyingError != errOperationPending { - return fmt.Errorf("failed to terminate utility VM (%s): %s", context, err) - } - logrus.Debugf("lcowdriver: waitTerminate: uvm.Terminate() returned operation pending (%s)", context) - } - - logrus.Debugf("lcowdriver: waitTerminate: (%s) - waiting for utility VM to terminate", context) - if err := svm.config.Uvm.WaitTimeout(time.Duration(svm.config.UvmTimeoutSeconds) * time.Second); err != nil { - return fmt.Errorf("failed waiting for utility VM to terminate (%s): %s", context, err) - } - return nil -} - -// String returns the string representation of a driver. This should match -// the name the graph driver has been registered with. -func (d *Driver) String() string { - return "lcow" -} - -// Status returns the status of the driver. -func (d *Driver) Status() [][2]string { - return [][2]string{ - {"LCOW", ""}, - // TODO: Add some more info here - mode, home, .... - } -} - -// Exists returns true if the given id is registered with this driver. -func (d *Driver) Exists(id string) bool { - _, err := os.Lstat(d.dir(id)) - logrus.Debugf("lcowdriver: exists: id %s %t", id, err == nil) - return err == nil -} - -// CreateReadWrite creates a layer that is writable for use as a container -// file system. That equates to creating a sandbox. -func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { - title := fmt.Sprintf("lcowdriver: createreadwrite: id %s", id) - logrus.Debugf(title) - - // First we need to create the folder - if err := d.Create(id, parent, opts); err != nil { - return err - } - - // Look for an explicit sandbox size option. - sandboxSize := uint64(client.DefaultVhdxSizeGB) - for k, v := range opts.StorageOpt { - switch strings.ToLower(k) { - case "lcow.sandboxsize": - var err error - sandboxSize, err = strconv.ParseUint(v, 10, 32) - if err != nil { - return fmt.Errorf("%s failed to parse value '%s' for 'lcow.sandboxsize'", title, v) - } - if sandboxSize < client.DefaultVhdxSizeGB { - return fmt.Errorf("%s 'lcow.sandboxsize' option cannot be less than %d", title, client.DefaultVhdxSizeGB) - } - break - } - } - - // Massive perf optimisation here. If we know that the RW layer is the default size, - // and that the cached sandbox already exists, and we are running in safe mode, we - // can just do a simple copy into the layers sandbox file without needing to start a - // unique service VM. For a global service VM, it doesn't really matter. Of course, - // this is only the case where the sandbox is the default size. - // - // Make sure we have the sandbox mutex taken while we are examining it. - if sandboxSize == client.DefaultVhdxSizeGB { - logrus.Debugf("%s: locking cachedSandboxMutex", title) - d.cachedSandboxMutex.Lock() - _, err := os.Stat(d.cachedSandboxFile) - logrus.Debugf("%s: releasing cachedSandboxMutex", title) - d.cachedSandboxMutex.Unlock() - if err == nil { - logrus.Debugf("%s: using cached sandbox to populate", title) - if err := client.CopyFile(d.cachedSandboxFile, filepath.Join(d.dir(id), sandboxFilename), true); err != nil { - return err - } - return nil - } - } - - logrus.Debugf("%s: creating SVM to create sandbox", title) - svm, err := d.startServiceVMIfNotRunning(id, nil, "createreadwrite") - if err != nil { - return err - } - defer d.terminateServiceVM(id, "createreadwrite", false) - - // So the sandbox needs creating. If default size ensure we are the only thread populating the cache. - // Non-default size we don't store, just create them one-off so no need to lock the cachedSandboxMutex. - if sandboxSize == client.DefaultVhdxSizeGB { - logrus.Debugf("%s: locking cachedSandboxMutex for creation", title) - d.cachedSandboxMutex.Lock() - defer func() { - logrus.Debugf("%s: releasing cachedSandboxMutex for creation", title) - d.cachedSandboxMutex.Unlock() - }() - } - - // Make sure we don't write to our local cached copy if this is for a non-default size request. - targetCacheFile := d.cachedSandboxFile - if sandboxSize != client.DefaultVhdxSizeGB { - targetCacheFile = "" - } - - // Create the ext4 vhdx - logrus.Debugf("%s: creating sandbox ext4 vhdx", title) - if err := svm.createExt4VHDX(filepath.Join(d.dir(id), sandboxFilename), uint32(sandboxSize), targetCacheFile); err != nil { - logrus.Debugf("%s: failed to create sandbox vhdx for %s: %s", title, id, err) - return err - } - return nil -} - -// Create creates the folder for the layer with the given id, and -// adds it to the layer chain. -func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { - logrus.Debugf("lcowdriver: create: id %s parent: %s", id, parent) - - parentChain, err := d.getLayerChain(parent) - if err != nil { - return err - } - - var layerChain []string - if parent != "" { - if !d.Exists(parent) { - return fmt.Errorf("lcowdriver: cannot create layer folder with missing parent %s", parent) - } - layerChain = []string{d.dir(parent)} - } - layerChain = append(layerChain, parentChain...) - - // Make sure layers are created with the correct ACL so that VMs can access them. - layerPath := d.dir(id) - logrus.Debugf("lcowdriver: create: id %s: creating %s", id, layerPath) - if err := system.MkdirAllWithACL(layerPath, 755, system.SddlNtvmAdministratorsLocalSystem); err != nil { - return err - } - - if err := d.setLayerChain(id, layerChain); err != nil { - if err2 := os.RemoveAll(layerPath); err2 != nil { - logrus.Warnf("failed to remove layer %s: %s", layerPath, err2) - } - return err - } - logrus.Debugf("lcowdriver: create: id %s: success", id) - - return nil -} - -// Remove unmounts and removes the dir information. -func (d *Driver) Remove(id string) error { - logrus.Debugf("lcowdriver: remove: id %s", id) - tmpID := fmt.Sprintf("%s-removing", id) - tmpLayerPath := d.dir(tmpID) - layerPath := d.dir(id) - - logrus.Debugf("lcowdriver: remove: id %s: layerPath %s", id, layerPath) - - // Unmount all the layers - err := d.Put(id) - if err != nil { - logrus.Debugf("lcowdriver: remove id %s: failed to unmount: %s", id, err) - return err - } - - // for non-global case just kill the vm - if !d.globalMode { - if err := d.terminateServiceVM(id, fmt.Sprintf("Remove %s", id), true); err != nil { - return err - } - } - - if err := os.Rename(layerPath, tmpLayerPath); err != nil && !os.IsNotExist(err) { - return err - } - - if err := os.RemoveAll(tmpLayerPath); err != nil { - return err - } - - logrus.Debugf("lcowdriver: remove: id %s: layerPath %s succeeded", id, layerPath) - return nil -} - -// Get returns the rootfs path for the id. It is reference counted and -// effectively can be thought of as a "mount the layer into the utility -// vm if it isn't already". The contract from the caller of this is that -// all Gets and Puts are matched. It -should- be the case that on cleanup, -// nothing is mounted. -// -// For optimisation, we don't actually mount the filesystem (which in our -// case means [hot-]adding it to a service VM. But we track that and defer -// the actual adding to the point we need to access it. -func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) { - title := fmt.Sprintf("lcowdriver: get: %s", id) - logrus.Debugf(title) - - // Generate the mounts needed for the deferred operation. - disks, err := d.getAllMounts(id) - if err != nil { - logrus.Debugf("%s failed to get all layer details for %s: %s", title, d.dir(id), err) - return nil, fmt.Errorf("%s failed to get layer details for %s: %s", title, d.dir(id), err) - } - - logrus.Debugf("%s: got layer mounts: %+v", title, disks) - return &lcowfs{ - root: unionMountName(disks), - d: d, - mappedDisks: disks, - vmID: d.getVMID(id), - }, nil -} - -// Put does the reverse of get. If there are no more references to -// the layer, it unmounts it from the utility VM. -func (d *Driver) Put(id string) error { - title := fmt.Sprintf("lcowdriver: put: %s", id) - - // Get the service VM that we need to remove from - svm, err := d.serviceVms.get(d.getVMID(id)) - if err == errVMUnknown { - return nil - } else if err == errVMisTerminating { - return svm.getStopError() - } - - // Generate the mounts that Get() might have mounted - disks, err := d.getAllMounts(id) - if err != nil { - logrus.Debugf("%s failed to get all layer details for %s: %s", title, d.dir(id), err) - return fmt.Errorf("%s failed to get layer details for %s: %s", title, d.dir(id), err) - } - - // Now, we want to perform the unmounts, hot-remove and stop the service vm. - // We want to go though all the steps even if we have an error to clean up properly - err = svm.deleteUnionMount(unionMountName(disks), disks...) - if err != nil { - logrus.Debugf("%s failed to delete union mount %s: %s", title, id, err) - } - - err1 := svm.hotRemoveVHDs(disks...) - if err1 != nil { - logrus.Debugf("%s failed to hot remove vhds %s: %s", title, id, err) - if err == nil { - err = err1 - } - } - - err1 = d.terminateServiceVM(id, fmt.Sprintf("Put %s", id), false) - if err1 != nil { - logrus.Debugf("%s failed to terminate service vm %s: %s", title, id, err1) - if err == nil { - err = err1 - } - } - logrus.Debugf("Put succeeded on id %s", id) - return err -} - -// Cleanup ensures the information the driver stores is properly removed. -// We use this opportunity to cleanup any -removing folders which may be -// still left if the daemon was killed while it was removing a layer. -func (d *Driver) Cleanup() error { - title := "lcowdriver: cleanup" - - items, err := ioutil.ReadDir(d.dataRoot) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - - // Note we don't return an error below - it's possible the files - // are locked. However, next time around after the daemon exits, - // we likely will be able to cleanup successfully. Instead we log - // warnings if there are errors. - for _, item := range items { - if item.IsDir() && strings.HasSuffix(item.Name(), "-removing") { - if err := os.RemoveAll(filepath.Join(d.dataRoot, item.Name())); err != nil { - logrus.Warnf("%s failed to cleanup %s: %s", title, item.Name(), err) - } else { - logrus.Infof("%s cleaned up %s", title, item.Name()) - } - } - } - - // Cleanup any service VMs we have running, along with their scratch spaces. - // We don't take the lock for this as it's taken in terminateServiceVm. - for k, v := range d.serviceVms.svms { - logrus.Debugf("%s svm entry: %s: %+v", title, k, v) - d.terminateServiceVM(k, "cleanup", true) - } - - return nil -} - -// Diff takes a layer (and it's parent layer which may be null, but -// is ignored by this implementation below) and returns a reader for -// a tarstream representing the layers contents. The id could be -// a read-only "layer.vhd" or a read-write "sandbox.vhdx". The semantics -// of this function dictate that the layer is already mounted. -// However, as we do lazy mounting as a performance optimisation, -// this will likely not be the case. -func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) { - title := fmt.Sprintf("lcowdriver: diff: %s", id) - - // Get VHDX info - ld, err := getLayerDetails(d.dir(id)) - if err != nil { - logrus.Debugf("%s: failed to get vhdx information of %s: %s", title, d.dir(id), err) - return nil, err - } - - // Start the SVM with a mapped virtual disk. Note that if the SVM is - // already running and we are in global mode, this will be - // hot-added. - mvd := hcsshim.MappedVirtualDisk{ - HostPath: ld.filename, - ContainerPath: hostToGuest(ld.filename), - CreateInUtilityVM: true, - ReadOnly: true, - } - - logrus.Debugf("%s: starting service VM", title) - svm, err := d.startServiceVMIfNotRunning(id, []hcsshim.MappedVirtualDisk{mvd}, fmt.Sprintf("diff %s", id)) - if err != nil { - return nil, err - } - - logrus.Debugf("lcowdriver: diff: waiting for svm to finish booting") - err = svm.getStartError() - if err != nil { - d.terminateServiceVM(id, fmt.Sprintf("diff %s", id), false) - return nil, fmt.Errorf("lcowdriver: diff: svm failed to boot: %s", err) - } - - // Obtain the tar stream for it - // The actual container path will have be remapped to a short name, so use that. - actualContainerPath := svm.getShortContainerPath(&mvd) - if actualContainerPath == "" { - return nil, fmt.Errorf("failed to get short container path for %+v in SVM %s", mvd, svm.config.Name) - } - logrus.Debugf("%s: %s %s, size %d, ReadOnly %t", title, ld.filename, actualContainerPath, ld.size, ld.isSandbox) - tarReadCloser, err := svm.config.VhdToTar(mvd.HostPath, actualContainerPath, ld.isSandbox, ld.size) - if err != nil { - svm.hotRemoveVHDs(mvd) - d.terminateServiceVM(id, fmt.Sprintf("diff %s", id), false) - return nil, fmt.Errorf("%s failed to export layer to tar stream for id: %s, parent: %s : %s", title, id, parent, err) - } - - logrus.Debugf("%s id %s parent %s completed successfully", title, id, parent) - - // In safe/non-global mode, we can't tear down the service VM until things have been read. - return ioutils.NewReadCloserWrapper(tarReadCloser, func() error { - tarReadCloser.Close() - svm.hotRemoveVHDs(mvd) - d.terminateServiceVM(id, fmt.Sprintf("diff %s", id), false) - return nil - }), nil -} - -// ApplyDiff extracts the changeset from the given diff into the -// layer with the specified id and parent, returning the size of the -// new layer in bytes. The layer should not be mounted when calling -// this function. Another way of describing this is that ApplyDiff writes -// to a new layer (a VHD in LCOW) the contents of a tarstream it's given. -func (d *Driver) ApplyDiff(id, parent string, diff io.Reader) (int64, error) { - logrus.Debugf("lcowdriver: applydiff: id %s", id) - - svm, err := d.startServiceVMIfNotRunning(id, nil, fmt.Sprintf("applydiff %s", id)) - if err != nil { - return 0, err - } - defer d.terminateServiceVM(id, fmt.Sprintf("applydiff %s", id), false) - - logrus.Debugf("lcowdriver: applydiff: waiting for svm to finish booting") - err = svm.getStartError() - if err != nil { - return 0, fmt.Errorf("lcowdriver: applydiff: svm failed to boot: %s", err) - } - - // TODO @jhowardmsft - the retries are temporary to overcome platform reliability issues. - // Obviously this will be removed as platform bugs are fixed. - retries := 0 - for { - retries++ - size, err := svm.config.TarToVhd(filepath.Join(d.dataRoot, id, layerFilename), diff) - if err != nil { - if retries <= 10 { - continue - } - return 0, err - } - return size, err - } -} - -// Changes produces a list of changes between the specified layer -// and its parent layer. If parent is "", then all changes will be ADD changes. -// The layer should not be mounted when calling this function. -func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { - logrus.Debugf("lcowdriver: changes: id %s parent %s", id, parent) - // TODO @gupta-ak. Needs implementation with assistance from service VM - return nil, nil -} - -// DiffSize calculates the changes between the specified layer -// and its parent and returns the size in bytes of the changes -// relative to its base filesystem directory. -func (d *Driver) DiffSize(id, parent string) (size int64, err error) { - logrus.Debugf("lcowdriver: diffsize: id %s", id) - // TODO @gupta-ak. Needs implementation with assistance from service VM - return 0, nil -} - -// GetMetadata returns custom driver information. -func (d *Driver) GetMetadata(id string) (map[string]string, error) { - logrus.Debugf("lcowdriver: getmetadata: id %s", id) - m := make(map[string]string) - m["dir"] = d.dir(id) - return m, nil -} - -// GetLayerPath gets the layer path on host (path to VHD/VHDX) -func (d *Driver) GetLayerPath(id string) (string, error) { - return d.dir(id), nil -} - -// dir returns the absolute path to the layer. -func (d *Driver) dir(id string) string { - return filepath.Join(d.dataRoot, filepath.Base(id)) -} - -// getLayerChain returns the layer chain information. -func (d *Driver) getLayerChain(id string) ([]string, error) { - jPath := filepath.Join(d.dir(id), "layerchain.json") - logrus.Debugf("lcowdriver: getlayerchain: id %s json %s", id, jPath) - content, err := ioutil.ReadFile(jPath) - if os.IsNotExist(err) { - return nil, nil - } else if err != nil { - return nil, fmt.Errorf("lcowdriver: getlayerchain: %s unable to read layerchain file %s: %s", id, jPath, err) - } - - var layerChain []string - err = json.Unmarshal(content, &layerChain) - if err != nil { - return nil, fmt.Errorf("lcowdriver: getlayerchain: %s failed to unmarshall layerchain file %s: %s", id, jPath, err) - } - return layerChain, nil -} - -// setLayerChain stores the layer chain information on disk. -func (d *Driver) setLayerChain(id string, chain []string) error { - content, err := json.Marshal(&chain) - if err != nil { - return fmt.Errorf("lcowdriver: setlayerchain: %s failed to marshall layerchain json: %s", id, err) - } - - jPath := filepath.Join(d.dir(id), "layerchain.json") - logrus.Debugf("lcowdriver: setlayerchain: id %s json %s", id, jPath) - err = ioutil.WriteFile(jPath, content, 0600) - if err != nil { - return fmt.Errorf("lcowdriver: setlayerchain: %s failed to write layerchain file: %s", id, err) - } - return nil -} - -// getLayerDetails is a utility for getting a file name, size and indication of -// sandbox for a VHD(x) in a folder. A read-only layer will be layer.vhd. A -// read-write layer will be sandbox.vhdx. -func getLayerDetails(folder string) (*layerDetails, error) { - var fileInfo os.FileInfo - ld := &layerDetails{ - isSandbox: false, - filename: filepath.Join(folder, layerFilename), - } - - fileInfo, err := os.Stat(ld.filename) - if err != nil { - ld.filename = filepath.Join(folder, sandboxFilename) - if fileInfo, err = os.Stat(ld.filename); err != nil { - return nil, fmt.Errorf("failed to locate layer or sandbox in %s", folder) - } - ld.isSandbox = true - } - ld.size = fileInfo.Size() - - return ld, nil -} - -func (d *Driver) getAllMounts(id string) ([]hcsshim.MappedVirtualDisk, error) { - layerChain, err := d.getLayerChain(id) - if err != nil { - return nil, err - } - layerChain = append([]string{d.dir(id)}, layerChain...) - - logrus.Debugf("getting all layers: %v", layerChain) - disks := make([]hcsshim.MappedVirtualDisk, len(layerChain), len(layerChain)) - for i := range layerChain { - ld, err := getLayerDetails(layerChain[i]) - if err != nil { - logrus.Debugf("Failed to get LayerVhdDetails from %s: %s", layerChain[i], err) - return nil, err - } - disks[i].HostPath = ld.filename - disks[i].ContainerPath = hostToGuest(ld.filename) - disks[i].CreateInUtilityVM = true - disks[i].ReadOnly = !ld.isSandbox - } - return disks, nil -} - -func hostToGuest(hostpath string) string { - // This is the "long" container path. At the point of which we are - // calculating this, we don't know which service VM we're going to be - // using, so we can't translate this to a short path yet, instead - // deferring until the point of which it's added to an SVM. We don't - // use long container paths in SVMs for SCSI disks, otherwise it can cause - // command line operations that we invoke to fail due to being over ~4200 - // characters when there are ~47 layers involved. An example of this is - // the mount call to create the overlay across multiple SCSI-attached disks. - // It doesn't affect VPMem attached layers during container creation as - // these get mapped by openGCS to /tmp/N/M where N is a container instance - // number, and M is a layer number. - return fmt.Sprintf("/tmp/%s", filepath.Base(filepath.Dir(hostpath))) -} - -func unionMountName(disks []hcsshim.MappedVirtualDisk) string { - return fmt.Sprintf("%s-mount", disks[0].ContainerPath) -} - -type nopCloser struct { - io.Reader -} - -func (nopCloser) Close() error { - return nil -} - -type fileGetCloserFromSVM struct { - id string - svm *serviceVM - mvd *hcsshim.MappedVirtualDisk - d *Driver -} - -func (fgc *fileGetCloserFromSVM) Close() error { - if fgc.svm != nil { - if fgc.mvd != nil { - if err := fgc.svm.hotRemoveVHDs(*fgc.mvd); err != nil { - // We just log this as we're going to tear down the SVM imminently unless in global mode - logrus.Errorf("failed to remove mvd %s: %s", fgc.mvd.ContainerPath, err) - } - } - } - if fgc.d != nil && fgc.svm != nil && fgc.id != "" { - if err := fgc.d.terminateServiceVM(fgc.id, fmt.Sprintf("diffgetter %s", fgc.id), false); err != nil { - return err - } - } - return nil -} - -func (fgc *fileGetCloserFromSVM) Get(filename string) (io.ReadCloser, error) { - errOut := &bytes.Buffer{} - outOut := &bytes.Buffer{} - // Must map to the actual "short" container path where the SCSI disk was mounted - actualContainerPath := fgc.svm.getShortContainerPath(fgc.mvd) - if actualContainerPath == "" { - return nil, fmt.Errorf("inconsistency detected: couldn't get short container path for %+v in utility VM %s", fgc.mvd, fgc.svm.config.Name) - } - file := path.Join(actualContainerPath, filename) - if err := fgc.svm.runProcess(fmt.Sprintf("cat %s", file), nil, outOut, errOut); err != nil { - logrus.Debugf("cat %s failed: %s", file, errOut.String()) - return nil, err - } - return nopCloser{bytes.NewReader(outOut.Bytes())}, nil -} - -// DiffGetter returns a FileGetCloser that can read files from the directory that -// contains files for the layer differences. Used for direct access for tar-split. -func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { - title := fmt.Sprintf("lcowdriver: diffgetter: %s", id) - logrus.Debugf(title) - - ld, err := getLayerDetails(d.dir(id)) - if err != nil { - logrus.Debugf("%s: failed to get vhdx information of %s: %s", title, d.dir(id), err) - return nil, err - } - - // Start the SVM with a mapped virtual disk. Note that if the SVM is - // already running and we are in global mode, this will be hot-added. - mvd := hcsshim.MappedVirtualDisk{ - HostPath: ld.filename, - ContainerPath: hostToGuest(ld.filename), - CreateInUtilityVM: true, - ReadOnly: true, - } - - logrus.Debugf("%s: starting service VM", title) - svm, err := d.startServiceVMIfNotRunning(id, []hcsshim.MappedVirtualDisk{mvd}, fmt.Sprintf("diffgetter %s", id)) - if err != nil { - return nil, err - } - - logrus.Debugf("%s: waiting for svm to finish booting", title) - err = svm.getStartError() - if err != nil { - d.terminateServiceVM(id, fmt.Sprintf("diff %s", id), false) - return nil, fmt.Errorf("%s: svm failed to boot: %s", title, err) - } - - return &fileGetCloserFromSVM{ - id: id, - svm: svm, - mvd: &mvd, - d: d}, nil -} diff --git a/daemon/graphdriver/lcow/lcow_svm.go b/daemon/graphdriver/lcow/lcow_svm.go deleted file mode 100644 index a70e1b2486e03..0000000000000 --- a/daemon/graphdriver/lcow/lcow_svm.go +++ /dev/null @@ -1,421 +0,0 @@ -// +build windows - -package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow" - -import ( - "bytes" - "fmt" - "io" - "strings" - "sync" - "time" - - "github.com/Microsoft/hcsshim" - "github.com/Microsoft/opengcs/client" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// Code for all the service VM management for the LCOW graphdriver - -var errVMisTerminating = errors.New("service VM is shutting down") -var errVMUnknown = errors.New("service vm id is unknown") -var errVMStillHasReference = errors.New("Attemping to delete a VM that is still being used") - -// serviceVMMap is the struct representing the id -> service VM mapping. -type serviceVMMap struct { - sync.Mutex - svms map[string]*serviceVMMapItem -} - -// serviceVMMapItem is our internal structure representing an item in our -// map of service VMs we are maintaining. -type serviceVMMapItem struct { - svm *serviceVM // actual service vm object - refCount int // refcount for VM -} - -// attachedVHD is for reference counting SCSI disks attached to a service VM, -// and for a counter used to generate a short path name for the container path. -type attachedVHD struct { - refCount int - attachCounter uint64 -} - -type serviceVM struct { - sync.Mutex // Serialises operations being performed in this service VM. - scratchAttached bool // Has a scratch been attached? - config *client.Config // Represents the service VM item. - - // Indicates that the vm is started - startStatus chan interface{} - startError error - - // Indicates that the vm is stopped - stopStatus chan interface{} - stopError error - - attachCounter uint64 // Increasing counter for each add - attachedVHDs map[string]*attachedVHD // Map ref counting all the VHDS we've hot-added/hot-removed. - unionMounts map[string]int // Map ref counting all the union filesystems we mounted. -} - -// add will add an id to the service vm map. There are three cases: -// - entry doesn't exist: -// - add id to map and return a new vm that the caller can manually configure+start -// - entry does exist -// - return vm in map and increment ref count -// - entry does exist but the ref count is 0 -// - return the svm and errVMisTerminating. Caller can call svm.getStopError() to wait for stop -func (svmMap *serviceVMMap) add(id string) (svm *serviceVM, alreadyExists bool, err error) { - svmMap.Lock() - defer svmMap.Unlock() - if svm, ok := svmMap.svms[id]; ok { - if svm.refCount == 0 { - return svm.svm, true, errVMisTerminating - } - svm.refCount++ - return svm.svm, true, nil - } - - // Doesn't exist, so create an empty svm to put into map and return - newSVM := &serviceVM{ - startStatus: make(chan interface{}), - stopStatus: make(chan interface{}), - attachedVHDs: make(map[string]*attachedVHD), - unionMounts: make(map[string]int), - config: &client.Config{}, - } - svmMap.svms[id] = &serviceVMMapItem{ - svm: newSVM, - refCount: 1, - } - return newSVM, false, nil -} - -// get will get the service vm from the map. There are three cases: -// - entry doesn't exist: -// - return errVMUnknown -// - entry does exist -// - return vm with no error -// - entry does exist but the ref count is 0 -// - return the svm and errVMisTerminating. Caller can call svm.getStopError() to wait for stop -func (svmMap *serviceVMMap) get(id string) (*serviceVM, error) { - svmMap.Lock() - defer svmMap.Unlock() - svm, ok := svmMap.svms[id] - if !ok { - return nil, errVMUnknown - } - if svm.refCount == 0 { - return svm.svm, errVMisTerminating - } - return svm.svm, nil -} - -// decrementRefCount decrements the ref count of the given ID from the map. There are four cases: -// - entry doesn't exist: -// - return errVMUnknown -// - entry does exist but the ref count is 0 -// - return the svm and errVMisTerminating. Caller can call svm.getStopError() to wait for stop -// - entry does exist but ref count is 1 -// - return vm and set lastRef to true. The caller can then stop the vm, delete the id from this map -// - and execute svm.signalStopFinished to signal the threads that the svm has been terminated. -// - entry does exist and ref count > 1 -// - just reduce ref count and return svm -func (svmMap *serviceVMMap) decrementRefCount(id string) (_ *serviceVM, lastRef bool, _ error) { - svmMap.Lock() - defer svmMap.Unlock() - - svm, ok := svmMap.svms[id] - if !ok { - return nil, false, errVMUnknown - } - if svm.refCount == 0 { - return svm.svm, false, errVMisTerminating - } - svm.refCount-- - return svm.svm, svm.refCount == 0, nil -} - -// setRefCountZero works the same way as decrementRefCount, but sets ref count to 0 instead of decrementing it. -func (svmMap *serviceVMMap) setRefCountZero(id string) (*serviceVM, error) { - svmMap.Lock() - defer svmMap.Unlock() - - svm, ok := svmMap.svms[id] - if !ok { - return nil, errVMUnknown - } - if svm.refCount == 0 { - return svm.svm, errVMisTerminating - } - svm.refCount = 0 - return svm.svm, nil -} - -// deleteID deletes the given ID from the map. If the refcount is not 0 or the -// VM does not exist, then this function returns an error. -func (svmMap *serviceVMMap) deleteID(id string) error { - svmMap.Lock() - defer svmMap.Unlock() - svm, ok := svmMap.svms[id] - if !ok { - return errVMUnknown - } - if svm.refCount != 0 { - return errVMStillHasReference - } - delete(svmMap.svms, id) - return nil -} - -func (svm *serviceVM) signalStartFinished(err error) { - svm.Lock() - svm.startError = err - svm.Unlock() - close(svm.startStatus) -} - -func (svm *serviceVM) getStartError() error { - <-svm.startStatus - svm.Lock() - defer svm.Unlock() - return svm.startError -} - -func (svm *serviceVM) signalStopFinished(err error) { - svm.Lock() - svm.stopError = err - svm.Unlock() - close(svm.stopStatus) -} - -func (svm *serviceVM) getStopError() error { - <-svm.stopStatus - svm.Lock() - defer svm.Unlock() - return svm.stopError -} - -// hotAddVHDs waits for the service vm to start and then attaches the vhds. -func (svm *serviceVM) hotAddVHDs(mvds ...hcsshim.MappedVirtualDisk) error { - if err := svm.getStartError(); err != nil { - return err - } - return svm.hotAddVHDsAtStart(mvds...) -} - -// hotAddVHDsAtStart works the same way as hotAddVHDs but does not wait for the VM to start. -func (svm *serviceVM) hotAddVHDsAtStart(mvds ...hcsshim.MappedVirtualDisk) error { - svm.Lock() - defer svm.Unlock() - for i, mvd := range mvds { - if _, ok := svm.attachedVHDs[mvd.HostPath]; ok { - svm.attachedVHDs[mvd.HostPath].refCount++ - logrus.Debugf("lcowdriver: UVM %s: %s already present, refCount now %d", svm.config.Name, mvd.HostPath, svm.attachedVHDs[mvd.HostPath].refCount) - continue - } - - svm.attachCounter++ - shortContainerPath := remapLongToShortContainerPath(mvd.ContainerPath, svm.attachCounter, svm.config.Name) - if err := svm.config.HotAddVhd(mvd.HostPath, shortContainerPath, mvd.ReadOnly, !mvd.AttachOnly); err != nil { - svm.hotRemoveVHDsNoLock(mvds[:i]...) - return err - } - svm.attachedVHDs[mvd.HostPath] = &attachedVHD{refCount: 1, attachCounter: svm.attachCounter} - } - return nil -} - -// hotRemoveVHDs waits for the service vm to start and then removes the vhds. -// The service VM must not be locked when calling this function. -func (svm *serviceVM) hotRemoveVHDs(mvds ...hcsshim.MappedVirtualDisk) error { - if err := svm.getStartError(); err != nil { - return err - } - svm.Lock() - defer svm.Unlock() - return svm.hotRemoveVHDsNoLock(mvds...) -} - -// hotRemoveVHDsNoLock removes VHDs from a service VM. When calling this function, -// the contract is the service VM lock must be held. -func (svm *serviceVM) hotRemoveVHDsNoLock(mvds ...hcsshim.MappedVirtualDisk) error { - var retErr error - for _, mvd := range mvds { - if _, ok := svm.attachedVHDs[mvd.HostPath]; !ok { - // We continue instead of returning an error if we try to hot remove a non-existent VHD. - // This is because one of the callers of the function is graphdriver.Put(). Since graphdriver.Get() - // defers the VM start to the first operation, it's possible that nothing have been hot-added - // when Put() is called. To avoid Put returning an error in that case, we simply continue if we - // don't find the vhd attached. - logrus.Debugf("lcowdriver: UVM %s: %s is not attached, not doing anything", svm.config.Name, mvd.HostPath) - continue - } - - if svm.attachedVHDs[mvd.HostPath].refCount > 1 { - svm.attachedVHDs[mvd.HostPath].refCount-- - logrus.Debugf("lcowdriver: UVM %s: %s refCount dropped to %d. not removing from UVM", svm.config.Name, mvd.HostPath, svm.attachedVHDs[mvd.HostPath].refCount) - continue - } - - // last reference to VHD, so remove from VM and map - if err := svm.config.HotRemoveVhd(mvd.HostPath); err == nil { - delete(svm.attachedVHDs, mvd.HostPath) - } else { - // Take note of the error, but still continue to remove the other VHDs - logrus.Warnf("Failed to hot remove %s: %s", mvd.HostPath, err) - if retErr == nil { - retErr = err - } - } - } - return retErr -} - -func (svm *serviceVM) createExt4VHDX(destFile string, sizeGB uint32, cacheFile string) error { - if err := svm.getStartError(); err != nil { - return err - } - - svm.Lock() - defer svm.Unlock() - return svm.config.CreateExt4Vhdx(destFile, sizeGB, cacheFile) -} - -// getShortContainerPath looks up where a SCSI disk was actually mounted -// in a service VM when we remapped a long path name to a short name. -func (svm *serviceVM) getShortContainerPath(mvd *hcsshim.MappedVirtualDisk) string { - if mvd.ContainerPath == "" { - return "" - } - avhd, ok := svm.attachedVHDs[mvd.HostPath] - if !ok { - return "" - } - return fmt.Sprintf("/tmp/d%d", avhd.attachCounter) -} - -func (svm *serviceVM) createUnionMount(mountName string, mvds ...hcsshim.MappedVirtualDisk) (err error) { - if len(mvds) == 0 { - return fmt.Errorf("createUnionMount: error must have at least 1 layer") - } - - if err = svm.getStartError(); err != nil { - return err - } - - svm.Lock() - defer svm.Unlock() - if _, ok := svm.unionMounts[mountName]; ok { - svm.unionMounts[mountName]++ - return nil - } - - var lowerLayers []string - if mvds[0].ReadOnly { - lowerLayers = append(lowerLayers, svm.getShortContainerPath(&mvds[0])) - } - - for i := 1; i < len(mvds); i++ { - lowerLayers = append(lowerLayers, svm.getShortContainerPath(&mvds[i])) - } - - logrus.Debugf("Doing the overlay mount with union directory=%s", mountName) - errOut := &bytes.Buffer{} - if err = svm.runProcess(fmt.Sprintf("mkdir -p %s", mountName), nil, nil, errOut); err != nil { - return errors.Wrapf(err, "mkdir -p %s failed (%s)", mountName, errOut.String()) - } - - var cmd string - if len(mvds) == 1 { - // `FROM SCRATCH` case and the only layer. No overlay required. - cmd = fmt.Sprintf("mount %s %s", svm.getShortContainerPath(&mvds[0]), mountName) - } else if mvds[0].ReadOnly { - // Readonly overlay - cmd = fmt.Sprintf("mount -t overlay overlay -olowerdir=%s %s", - strings.Join(lowerLayers, ","), - mountName) - } else { - upper := fmt.Sprintf("%s/upper", svm.getShortContainerPath(&mvds[0])) - work := fmt.Sprintf("%s/work", svm.getShortContainerPath(&mvds[0])) - - errOut := &bytes.Buffer{} - if err = svm.runProcess(fmt.Sprintf("mkdir -p %s %s", upper, work), nil, nil, errOut); err != nil { - return errors.Wrapf(err, "mkdir -p %s failed (%s)", mountName, errOut.String()) - } - - cmd = fmt.Sprintf("mount -t overlay overlay -olowerdir=%s,upperdir=%s,workdir=%s %s", - strings.Join(lowerLayers, ":"), - upper, - work, - mountName) - } - - logrus.Debugf("createUnionMount: Executing mount=%s", cmd) - errOut = &bytes.Buffer{} - if err = svm.runProcess(cmd, nil, nil, errOut); err != nil { - return errors.Wrapf(err, "%s failed (%s)", cmd, errOut.String()) - } - - svm.unionMounts[mountName] = 1 - return nil -} - -func (svm *serviceVM) deleteUnionMount(mountName string, disks ...hcsshim.MappedVirtualDisk) error { - if err := svm.getStartError(); err != nil { - return err - } - - svm.Lock() - defer svm.Unlock() - if _, ok := svm.unionMounts[mountName]; !ok { - return nil - } - - if svm.unionMounts[mountName] > 1 { - svm.unionMounts[mountName]-- - return nil - } - - logrus.Debugf("Removing union mount %s", mountName) - if err := svm.runProcess(fmt.Sprintf("umount %s", mountName), nil, nil, nil); err != nil { - return err - } - - delete(svm.unionMounts, mountName) - return nil -} - -func (svm *serviceVM) runProcess(command string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { - var process hcsshim.Process - var err error - errOut := &bytes.Buffer{} - - if stderr != nil { - process, err = svm.config.RunProcess(command, stdin, stdout, stderr) - } else { - process, err = svm.config.RunProcess(command, stdin, stdout, errOut) - } - if err != nil { - return err - } - defer process.Close() - - process.WaitTimeout(time.Duration(int(time.Second) * svm.config.UvmTimeoutSeconds)) - exitCode, err := process.ExitCode() - if err != nil { - return err - } - - if exitCode != 0 { - // If the caller isn't explicitly capturing stderr output, then capture it here instead. - e := fmt.Sprintf("svm.runProcess: command %s failed with exit code %d", command, exitCode) - if stderr == nil { - e = fmt.Sprintf("%s. (%s)", e, errOut.String()) - } - return fmt.Errorf(e) - } - return nil -} diff --git a/daemon/graphdriver/lcow/remotefs.go b/daemon/graphdriver/lcow/remotefs.go deleted file mode 100644 index 29f15fd24cf66..0000000000000 --- a/daemon/graphdriver/lcow/remotefs.go +++ /dev/null @@ -1,139 +0,0 @@ -// +build windows - -package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow" - -import ( - "bytes" - "fmt" - "io" - "runtime" - "strings" - "sync" - - "github.com/Microsoft/hcsshim" - "github.com/Microsoft/opengcs/service/gcsutils/remotefs" - "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/pkg/containerfs" - "github.com/sirupsen/logrus" -) - -type lcowfs struct { - root string - d *Driver - mappedDisks []hcsshim.MappedVirtualDisk - vmID string - currentSVM *serviceVM - sync.Mutex -} - -var _ containerfs.ContainerFS = &lcowfs{} - -// ErrNotSupported is an error for unsupported operations in the remotefs -var ErrNotSupported = fmt.Errorf("not supported") - -// Functions to implement the ContainerFS interface -func (l *lcowfs) Path() string { - return l.root -} - -func (l *lcowfs) ResolveScopedPath(path string, rawPath bool) (string, error) { - logrus.Debugf("remotefs.resolvescopedpath inputs: %s %s ", path, l.root) - - arg1 := l.Join(l.root, path) - if !rawPath { - // The l.Join("/", path) will make path an absolute path and then clean it - // so if path = ../../X, it will become /X. - arg1 = l.Join(l.root, l.Join("/", path)) - } - arg2 := l.root - - output := &bytes.Buffer{} - if err := l.runRemoteFSProcess(nil, output, remotefs.ResolvePathCmd, arg1, arg2); err != nil { - return "", err - } - - logrus.Debugf("remotefs.resolvescopedpath success. Output: %s\n", output.String()) - return output.String(), nil -} - -func (l *lcowfs) OS() string { - return "linux" -} - -func (l *lcowfs) Architecture() string { - return runtime.GOARCH -} - -// Other functions that are used by docker like the daemon Archiver/Extractor -func (l *lcowfs) ExtractArchive(src io.Reader, dst string, opts *archive.TarOptions) error { - logrus.Debugf("remotefs.ExtractArchve inputs: %s %+v", dst, opts) - - tarBuf := &bytes.Buffer{} - if err := remotefs.WriteTarOptions(tarBuf, opts); err != nil { - return fmt.Errorf("failed to marshall tar opts: %s", err) - } - - input := io.MultiReader(tarBuf, src) - if err := l.runRemoteFSProcess(input, nil, remotefs.ExtractArchiveCmd, dst); err != nil { - return fmt.Errorf("failed to extract archive to %s: %s", dst, err) - } - return nil -} - -func (l *lcowfs) ArchivePath(src string, opts *archive.TarOptions) (io.ReadCloser, error) { - logrus.Debugf("remotefs.ArchivePath: %s %+v", src, opts) - - tarBuf := &bytes.Buffer{} - if err := remotefs.WriteTarOptions(tarBuf, opts); err != nil { - return nil, fmt.Errorf("failed to marshall tar opts: %s", err) - } - - r, w := io.Pipe() - go func() { - defer w.Close() - if err := l.runRemoteFSProcess(tarBuf, w, remotefs.ArchivePathCmd, src); err != nil { - logrus.Debugf("REMOTEFS: Failed to extract archive: %s %+v %s", src, opts, err) - } - }() - return r, nil -} - -// Helper functions -func (l *lcowfs) startVM() error { - l.Lock() - defer l.Unlock() - if l.currentSVM != nil { - return nil - } - - svm, err := l.d.startServiceVMIfNotRunning(l.vmID, l.mappedDisks, fmt.Sprintf("lcowfs.startVM")) - if err != nil { - return err - } - - if err = svm.createUnionMount(l.root, l.mappedDisks...); err != nil { - return err - } - l.currentSVM = svm - return nil -} - -func (l *lcowfs) runRemoteFSProcess(stdin io.Reader, stdout io.Writer, args ...string) error { - if err := l.startVM(); err != nil { - return err - } - - // Append remotefs prefix and setup as a command line string - cmd := fmt.Sprintf("%s %s", remotefs.RemotefsCmd, strings.Join(args, " ")) - stderr := &bytes.Buffer{} - if err := l.currentSVM.runProcess(cmd, stdin, stdout, stderr); err != nil { - return err - } - - eerr, err := remotefs.ReadError(stderr) - if eerr != nil { - // Process returned an error so return that. - return remotefs.ExportedToError(eerr) - } - return err -} diff --git a/daemon/graphdriver/lcow/remotefs_file.go b/daemon/graphdriver/lcow/remotefs_file.go deleted file mode 100644 index 1f00bfff46da5..0000000000000 --- a/daemon/graphdriver/lcow/remotefs_file.go +++ /dev/null @@ -1,211 +0,0 @@ -// +build windows - -package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow" - -import ( - "bytes" - "encoding/binary" - "encoding/json" - "fmt" - "io" - "os" - "strconv" - - "github.com/Microsoft/hcsshim" - "github.com/Microsoft/opengcs/service/gcsutils/remotefs" - "github.com/containerd/continuity/driver" -) - -type lcowfile struct { - process hcsshim.Process - stdin io.WriteCloser - stdout io.ReadCloser - stderr io.ReadCloser - fs *lcowfs - guestPath string -} - -func (l *lcowfs) Open(path string) (driver.File, error) { - return l.OpenFile(path, os.O_RDONLY, 0) -} - -func (l *lcowfs) OpenFile(path string, flag int, perm os.FileMode) (_ driver.File, err error) { - flagStr := strconv.FormatInt(int64(flag), 10) - permStr := strconv.FormatUint(uint64(perm), 8) - - commandLine := fmt.Sprintf("%s %s %s %s %s", remotefs.RemotefsCmd, remotefs.OpenFileCmd, path, flagStr, permStr) - env := make(map[string]string) - env["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:" - processConfig := &hcsshim.ProcessConfig{ - EmulateConsole: false, - CreateStdInPipe: true, - CreateStdOutPipe: true, - CreateStdErrPipe: true, - CreateInUtilityVm: true, - WorkingDirectory: "/bin", - Environment: env, - CommandLine: commandLine, - } - - process, err := l.currentSVM.config.Uvm.CreateProcess(processConfig) - if err != nil { - return nil, fmt.Errorf("failed to open file %s: %s", path, err) - } - - stdin, stdout, stderr, err := process.Stdio() - if err != nil { - process.Kill() - process.Close() - return nil, fmt.Errorf("failed to open file pipes %s: %s", path, err) - } - - lf := &lcowfile{ - process: process, - stdin: stdin, - stdout: stdout, - stderr: stderr, - fs: l, - guestPath: path, - } - - if _, err := lf.getResponse(); err != nil { - return nil, fmt.Errorf("failed to open file %s: %s", path, err) - } - return lf, nil -} - -func (l *lcowfile) Read(b []byte) (int, error) { - hdr := &remotefs.FileHeader{ - Cmd: remotefs.Read, - Size: uint64(len(b)), - } - - if err := remotefs.WriteFileHeader(l.stdin, hdr, nil); err != nil { - return 0, err - } - - buf, err := l.getResponse() - if err != nil { - return 0, err - } - - n := copy(b, buf) - return n, nil -} - -func (l *lcowfile) Write(b []byte) (int, error) { - hdr := &remotefs.FileHeader{ - Cmd: remotefs.Write, - Size: uint64(len(b)), - } - - if err := remotefs.WriteFileHeader(l.stdin, hdr, b); err != nil { - return 0, err - } - - _, err := l.getResponse() - if err != nil { - return 0, err - } - - return len(b), nil -} - -func (l *lcowfile) Seek(offset int64, whence int) (int64, error) { - seekHdr := &remotefs.SeekHeader{ - Offset: offset, - Whence: int32(whence), - } - - buf := &bytes.Buffer{} - if err := binary.Write(buf, binary.BigEndian, seekHdr); err != nil { - return 0, err - } - - hdr := &remotefs.FileHeader{ - Cmd: remotefs.Write, - Size: uint64(buf.Len()), - } - if err := remotefs.WriteFileHeader(l.stdin, hdr, buf.Bytes()); err != nil { - return 0, err - } - - resBuf, err := l.getResponse() - if err != nil { - return 0, err - } - - var res int64 - if err := binary.Read(bytes.NewBuffer(resBuf), binary.BigEndian, &res); err != nil { - return 0, err - } - return res, nil -} - -func (l *lcowfile) Close() error { - hdr := &remotefs.FileHeader{ - Cmd: remotefs.Close, - Size: 0, - } - - if err := remotefs.WriteFileHeader(l.stdin, hdr, nil); err != nil { - return err - } - - _, err := l.getResponse() - return err -} - -func (l *lcowfile) Readdir(n int) ([]os.FileInfo, error) { - nStr := strconv.FormatInt(int64(n), 10) - - // Unlike the other File functions, this one can just be run without maintaining state, - // so just do the normal runRemoteFSProcess way. - buf := &bytes.Buffer{} - if err := l.fs.runRemoteFSProcess(nil, buf, remotefs.ReadDirCmd, l.guestPath, nStr); err != nil { - return nil, err - } - - var info []remotefs.FileInfo - if err := json.Unmarshal(buf.Bytes(), &info); err != nil { - return nil, err - } - - osInfo := make([]os.FileInfo, len(info)) - for i := range info { - osInfo[i] = &info[i] - } - return osInfo, nil -} - -func (l *lcowfile) getResponse() ([]byte, error) { - hdr, err := remotefs.ReadFileHeader(l.stdout) - if err != nil { - return nil, err - } - - if hdr.Cmd != remotefs.CmdOK { - // Something went wrong during the openfile in the server. - // Parse stderr and return that as an error - eerr, err := remotefs.ReadError(l.stderr) - if eerr != nil { - return nil, remotefs.ExportedToError(eerr) - } - - // Maybe the parsing went wrong? - if err != nil { - return nil, err - } - - // At this point, we know something went wrong in the remotefs program, but - // we we don't know why. - return nil, fmt.Errorf("unknown error") - } - - // Successful command, we might have some data to read (for Read + Seek) - buf := make([]byte, hdr.Size, hdr.Size) - if _, err := io.ReadFull(l.stdout, buf); err != nil { - return nil, err - } - return buf, nil -} diff --git a/daemon/graphdriver/lcow/remotefs_filedriver.go b/daemon/graphdriver/lcow/remotefs_filedriver.go deleted file mode 100644 index f335868af6242..0000000000000 --- a/daemon/graphdriver/lcow/remotefs_filedriver.go +++ /dev/null @@ -1,123 +0,0 @@ -// +build windows - -package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow" - -import ( - "bytes" - "encoding/json" - "os" - "strconv" - - "github.com/Microsoft/opengcs/service/gcsutils/remotefs" - - "github.com/containerd/continuity/driver" - "github.com/sirupsen/logrus" -) - -var _ driver.Driver = &lcowfs{} - -func (l *lcowfs) Readlink(p string) (string, error) { - logrus.Debugf("removefs.readlink args: %s", p) - - result := &bytes.Buffer{} - if err := l.runRemoteFSProcess(nil, result, remotefs.ReadlinkCmd, p); err != nil { - return "", err - } - return result.String(), nil -} - -func (l *lcowfs) Mkdir(path string, mode os.FileMode) error { - return l.mkdir(path, mode, remotefs.MkdirCmd) -} - -func (l *lcowfs) MkdirAll(path string, mode os.FileMode) error { - return l.mkdir(path, mode, remotefs.MkdirAllCmd) -} - -func (l *lcowfs) mkdir(path string, mode os.FileMode, cmd string) error { - modeStr := strconv.FormatUint(uint64(mode), 8) - logrus.Debugf("remotefs.%s args: %s %s", cmd, path, modeStr) - return l.runRemoteFSProcess(nil, nil, cmd, path, modeStr) -} - -func (l *lcowfs) Remove(path string) error { - return l.remove(path, remotefs.RemoveCmd) -} - -func (l *lcowfs) RemoveAll(path string) error { - return l.remove(path, remotefs.RemoveAllCmd) -} - -func (l *lcowfs) remove(path string, cmd string) error { - logrus.Debugf("remotefs.%s args: %s", cmd, path) - return l.runRemoteFSProcess(nil, nil, cmd, path) -} - -func (l *lcowfs) Link(oldname, newname string) error { - return l.link(oldname, newname, remotefs.LinkCmd) -} - -func (l *lcowfs) Symlink(oldname, newname string) error { - return l.link(oldname, newname, remotefs.SymlinkCmd) -} - -func (l *lcowfs) link(oldname, newname, cmd string) error { - logrus.Debugf("remotefs.%s args: %s %s", cmd, oldname, newname) - return l.runRemoteFSProcess(nil, nil, cmd, oldname, newname) -} - -func (l *lcowfs) Lchown(name string, uid, gid int64) error { - uidStr := strconv.FormatInt(uid, 10) - gidStr := strconv.FormatInt(gid, 10) - - logrus.Debugf("remotefs.lchown args: %s %s %s", name, uidStr, gidStr) - return l.runRemoteFSProcess(nil, nil, remotefs.LchownCmd, name, uidStr, gidStr) -} - -// Lchmod changes the mode of an file not following symlinks. -func (l *lcowfs) Lchmod(path string, mode os.FileMode) error { - modeStr := strconv.FormatUint(uint64(mode), 8) - logrus.Debugf("remotefs.lchmod args: %s %s", path, modeStr) - return l.runRemoteFSProcess(nil, nil, remotefs.LchmodCmd, path, modeStr) -} - -func (l *lcowfs) Mknod(path string, mode os.FileMode, major, minor int) error { - modeStr := strconv.FormatUint(uint64(mode), 8) - majorStr := strconv.FormatUint(uint64(major), 10) - minorStr := strconv.FormatUint(uint64(minor), 10) - - logrus.Debugf("remotefs.mknod args: %s %s %s %s", path, modeStr, majorStr, minorStr) - return l.runRemoteFSProcess(nil, nil, remotefs.MknodCmd, path, modeStr, majorStr, minorStr) -} - -func (l *lcowfs) Mkfifo(path string, mode os.FileMode) error { - modeStr := strconv.FormatUint(uint64(mode), 8) - logrus.Debugf("remotefs.mkfifo args: %s %s", path, modeStr) - return l.runRemoteFSProcess(nil, nil, remotefs.MkfifoCmd, path, modeStr) -} - -func (l *lcowfs) Stat(p string) (os.FileInfo, error) { - return l.stat(p, remotefs.StatCmd) -} - -func (l *lcowfs) Lstat(p string) (os.FileInfo, error) { - return l.stat(p, remotefs.LstatCmd) -} - -func (l *lcowfs) stat(path string, cmd string) (os.FileInfo, error) { - logrus.Debugf("remotefs.stat inputs: %s %s", cmd, path) - - output := &bytes.Buffer{} - err := l.runRemoteFSProcess(nil, output, cmd, path) - if err != nil { - return nil, err - } - - var fi remotefs.FileInfo - if err := json.Unmarshal(output.Bytes(), &fi); err != nil { - return nil, err - } - - logrus.Debugf("remotefs.stat success. got: %v\n", fi) - return &fi, nil -} diff --git a/daemon/graphdriver/lcow/remotefs_pathdriver.go b/daemon/graphdriver/lcow/remotefs_pathdriver.go deleted file mode 100644 index 74895b0465428..0000000000000 --- a/daemon/graphdriver/lcow/remotefs_pathdriver.go +++ /dev/null @@ -1,212 +0,0 @@ -// +build windows - -package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow" - -import ( - "errors" - "os" - pathpkg "path" - "path/filepath" - "sort" - "strings" - - "github.com/containerd/continuity/pathdriver" -) - -var _ pathdriver.PathDriver = &lcowfs{} - -// Continuity Path functions can be done locally -func (l *lcowfs) Join(path ...string) string { - return pathpkg.Join(path...) -} - -func (l *lcowfs) IsAbs(path string) bool { - return pathpkg.IsAbs(path) -} - -func sameWord(a, b string) bool { - return a == b -} - -// Implementation taken from the Go standard library -func (l *lcowfs) Rel(basepath, targpath string) (string, error) { - baseVol := "" - targVol := "" - base := l.Clean(basepath) - targ := l.Clean(targpath) - if sameWord(targ, base) { - return ".", nil - } - base = base[len(baseVol):] - targ = targ[len(targVol):] - if base == "." { - base = "" - } - // Can't use IsAbs - `\a` and `a` are both relative in Windows. - baseSlashed := len(base) > 0 && base[0] == l.Separator() - targSlashed := len(targ) > 0 && targ[0] == l.Separator() - if baseSlashed != targSlashed || !sameWord(baseVol, targVol) { - return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath) - } - // Position base[b0:bi] and targ[t0:ti] at the first differing elements. - bl := len(base) - tl := len(targ) - var b0, bi, t0, ti int - for { - for bi < bl && base[bi] != l.Separator() { - bi++ - } - for ti < tl && targ[ti] != l.Separator() { - ti++ - } - if !sameWord(targ[t0:ti], base[b0:bi]) { - break - } - if bi < bl { - bi++ - } - if ti < tl { - ti++ - } - b0 = bi - t0 = ti - } - if base[b0:bi] == ".." { - return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath) - } - if b0 != bl { - // Base elements left. Must go up before going down. - seps := strings.Count(base[b0:bl], string(l.Separator())) - size := 2 + seps*3 - if tl != t0 { - size += 1 + tl - t0 - } - buf := make([]byte, size) - n := copy(buf, "..") - for i := 0; i < seps; i++ { - buf[n] = l.Separator() - copy(buf[n+1:], "..") - n += 3 - } - if t0 != tl { - buf[n] = l.Separator() - copy(buf[n+1:], targ[t0:]) - } - return string(buf), nil - } - return targ[t0:], nil -} - -func (l *lcowfs) Base(path string) string { - return pathpkg.Base(path) -} - -func (l *lcowfs) Dir(path string) string { - return pathpkg.Dir(path) -} - -func (l *lcowfs) Clean(path string) string { - return pathpkg.Clean(path) -} - -func (l *lcowfs) Split(path string) (dir, file string) { - return pathpkg.Split(path) -} - -func (l *lcowfs) Separator() byte { - return '/' -} - -func (l *lcowfs) Abs(path string) (string, error) { - // Abs is supposed to add the current working directory, which is meaningless in lcow. - // So, return an error. - return "", ErrNotSupported -} - -// Implementation taken from the Go standard library -func (l *lcowfs) Walk(root string, walkFn filepath.WalkFunc) error { - info, err := l.Lstat(root) - if err != nil { - err = walkFn(root, nil, err) - } else { - err = l.walk(root, info, walkFn) - } - if err == filepath.SkipDir { - return nil - } - return err -} - -// walk recursively descends path, calling w. -func (l *lcowfs) walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { - err := walkFn(path, info, nil) - if err != nil { - if info.IsDir() && err == filepath.SkipDir { - return nil - } - return err - } - - if !info.IsDir() { - return nil - } - - names, err := l.readDirNames(path) - if err != nil { - return walkFn(path, info, err) - } - - for _, name := range names { - filename := l.Join(path, name) - fileInfo, err := l.Lstat(filename) - if err != nil { - if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { - return err - } - } else { - err = l.walk(filename, fileInfo, walkFn) - if err != nil { - if !fileInfo.IsDir() || err != filepath.SkipDir { - return err - } - } - } - } - return nil -} - -// readDirNames reads the directory named by dirname and returns -// a sorted list of directory entries. -func (l *lcowfs) readDirNames(dirname string) ([]string, error) { - f, err := l.Open(dirname) - if err != nil { - return nil, err - } - files, err := f.Readdir(-1) - f.Close() - if err != nil { - return nil, err - } - - names := make([]string, len(files), len(files)) - for i := range files { - names[i] = files[i].Name() - } - - sort.Strings(names) - return names, nil -} - -// Note that Go's filepath.FromSlash/ToSlash convert between OS paths and '/'. Since the path separator -// for LCOW (and Unix) is '/', they are no-ops. -func (l *lcowfs) FromSlash(path string) string { - return path -} - -func (l *lcowfs) ToSlash(path string) string { - return path -} - -func (l *lcowfs) Match(pattern, name string) (matched bool, err error) { - return pathpkg.Match(pattern, name) -} diff --git a/daemon/graphdriver/overlay/overlay.go b/daemon/graphdriver/overlay/overlay.go index 08c05e192f61c..6a51f59d55c4c 100644 --- a/daemon/graphdriver/overlay/overlay.go +++ b/daemon/graphdriver/overlay/overlay.go @@ -1,14 +1,12 @@ +//go:build linux // +build linux package overlay // import "github.com/docker/docker/daemon/graphdriver/overlay" import ( - "bufio" "fmt" "io" - "io/ioutil" "os" - "os/exec" "path" "path/filepath" "strconv" @@ -21,10 +19,10 @@ import ( "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/fsutils" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/system" + "github.com/moby/locker" + "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -124,10 +122,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, err } - if err := supportsOverlay(); err != nil { - return nil, graphdriver.ErrNotSupported - } - // Perform feature detection on /var/lib/docker/overlay if it's an existing directory. // This covers situations where /var/lib/docker/overlay is a mount, and on a different // filesystem than /var/lib/docker. @@ -137,6 +131,11 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap testdir = filepath.Dir(testdir) } + if err := overlayutils.SupportsOverlay(testdir, false); err != nil { + logrus.WithField("storage-driver", "overlay").Error(err) + return nil, graphdriver.ErrNotSupported + } + fsMagic, err := graphdriver.GetFSMagic(testdir) if err != nil { return nil, err @@ -145,12 +144,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap backingFs = fsName } - switch fsMagic { - case graphdriver.FsMagicAufs, graphdriver.FsMagicBtrfs, graphdriver.FsMagicEcryptfs, graphdriver.FsMagicNfsFs, graphdriver.FsMagicOverlay, graphdriver.FsMagicZfs: - logrus.WithField("storage-driver", "overlay").Errorf("'overlay' is not supported over %s", backingFs) - return nil, graphdriver.ErrIncompatibleFS - } - supportsDType, err := fsutils.SupportsDType(testdir) if err != nil { return nil, err @@ -163,15 +156,20 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap logrus.WithField("storage-driver", "overlay").Warn(overlayutils.ErrDTypeNotSupported("overlay", backingFs)) } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) + currentID := idtools.CurrentIdentity() + _, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } + dirID := idtools.Identity{ + UID: currentID.UID, + GID: rootGID, + } + // Create the driver home dir - if err := idtools.MkdirAllAndChown(home, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil { return nil, err } - d := &Driver{ home: home, uidMaps: uidMaps, @@ -200,27 +198,6 @@ func parseOptions(options []string) (*overlayOptions, error) { return o, nil } -func supportsOverlay() error { - // We can try to modprobe overlay first before looking at - // proc/filesystems for when overlay is supported - exec.Command("modprobe", "overlay").Run() - - f, err := os.Open("/proc/filesystems") - if err != nil { - return err - } - defer f.Close() - - s := bufio.NewScanner(f) - for s.Scan() { - if s.Text() == "nodev\toverlay" { - return nil - } - } - logrus.WithField("storage-driver", "overlay").Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") - return graphdriver.ErrNotSupported -} - func (d *Driver) String() string { return "overlay" } @@ -251,7 +228,7 @@ func (d *Driver) GetMetadata(id string) (map[string]string, error) { return metadata, nil } - lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) + lowerID, err := os.ReadFile(path.Join(dir, "lower-id")) if err != nil { return nil, err } @@ -293,10 +270,12 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr } root := idtools.Identity{UID: rootUID, GID: rootGID} - if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { - return err + currentID := idtools.CurrentIdentity() + dirID := idtools.Identity{ + UID: currentID.UID, + GID: rootGID, } - if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { + if err := idtools.MkdirAndChown(dir, 0710, dirID); err != nil { return err } @@ -309,6 +288,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr // Toplevel images are just a "root" dir if parent == "" { + // This must be 0755 otherwise unprivileged users will in the container will not be able to read / in the container return idtools.MkdirAndChown(path.Join(dir, "root"), 0755, root) } @@ -329,17 +309,17 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { return err } - return ioutil.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0666) + return os.WriteFile(path.Join(dir, "lower-id"), []byte(parent), 0600) } // Otherwise, copy the upper and the lower-id from the parent - lowerID, err := ioutil.ReadFile(path.Join(parentDir, "lower-id")) + lowerID, err := os.ReadFile(path.Join(parentDir, "lower-id")) if err != nil { return err } - if err := ioutil.WriteFile(path.Join(dir, "lower-id"), lowerID, 0666); err != nil { + if err := os.WriteFile(path.Join(dir, "lower-id"), lowerID, 0600); err != nil { return err } @@ -405,7 +385,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, err erro } } }() - lowerID, err := ioutil.ReadFile(path.Join(dir, "lower-id")) + lowerID, err := os.ReadFile(path.Join(dir, "lower-id")) if err != nil { return nil, err } @@ -485,7 +465,7 @@ func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64 // 2) ApplyDiff doesn't do any in-place writes to files (would break hardlinks) // These are all currently true and are not expected to break - tmpRootDir, err := ioutil.TempDir(dir, "tmproot") + tmpRootDir, err := os.MkdirTemp(dir, "tmproot") if err != nil { return 0, err } diff --git a/daemon/graphdriver/overlay/overlay_test.go b/daemon/graphdriver/overlay/overlay_test.go index b270122c63f1f..414d5f97c4470 100644 --- a/daemon/graphdriver/overlay/overlay_test.go +++ b/daemon/graphdriver/overlay/overlay_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package overlay // import "github.com/docker/docker/daemon/graphdriver/overlay" diff --git a/daemon/graphdriver/overlay/overlay_unsupported.go b/daemon/graphdriver/overlay/overlay_unsupported.go index 8fc06ffecfa91..73128b58cd770 100644 --- a/daemon/graphdriver/overlay/overlay_unsupported.go +++ b/daemon/graphdriver/overlay/overlay_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package overlay // import "github.com/docker/docker/daemon/graphdriver/overlay" diff --git a/daemon/graphdriver/overlay2/check.go b/daemon/graphdriver/overlay2/check.go index 1703b7a00c8a8..af5bc34650dac 100644 --- a/daemon/graphdriver/overlay2/check.go +++ b/daemon/graphdriver/overlay2/check.go @@ -1,15 +1,16 @@ +//go:build linux // +build linux package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2" import ( "fmt" - "io/ioutil" "os" "path" "path/filepath" "syscall" + "github.com/containerd/containerd/pkg/userns" "github.com/docker/docker/pkg/system" "github.com/pkg/errors" "golang.org/x/sys/unix" @@ -19,8 +20,15 @@ import ( // which copies up the opaque flag when copying up an opaque // directory or the kernel enable CONFIG_OVERLAY_FS_REDIRECT_DIR. // When these exist naive diff should be used. +// +// When running in a user namespace, returns errRunningInUserNS +// immediately. func doesSupportNativeDiff(d string) error { - td, err := ioutil.TempDir(d, "opaque-bug-check") + if userns.RunningInUserNS() { + return errors.New("running in a user namespace") + } + + td, err := os.MkdirTemp(d, "opaque-bug-check") if err != nil { return err } @@ -43,10 +51,10 @@ func doesSupportNativeDiff(d string) error { if err := os.Mkdir(filepath.Join(td, "l3"), 0755); err != nil { return err } - if err := os.Mkdir(filepath.Join(td, "work"), 0755); err != nil { + if err := os.Mkdir(filepath.Join(td, workDirName), 0755); err != nil { return err } - if err := os.Mkdir(filepath.Join(td, "merged"), 0755); err != nil { + if err := os.Mkdir(filepath.Join(td, mergedDirName), 0755); err != nil { return err } @@ -55,18 +63,18 @@ func doesSupportNativeDiff(d string) error { return errors.Wrap(err, "failed to set opaque flag on middle layer") } - opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "l2"), path.Join(td, "l1"), path.Join(td, "l3"), path.Join(td, "work")) - if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", 0, opts); err != nil { + opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "l2"), path.Join(td, "l1"), path.Join(td, "l3"), path.Join(td, workDirName)) + if err := unix.Mount("overlay", filepath.Join(td, mergedDirName), "overlay", 0, opts); err != nil { return errors.Wrap(err, "failed to mount overlay") } defer func() { - if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil { - logger.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err) + if err := unix.Unmount(filepath.Join(td, mergedDirName), 0); err != nil { + logger.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, mergedDirName), err) } }() // Touch file in d to force copy up of opaque directory "d" from "l2" to "l3" - if err := ioutil.WriteFile(filepath.Join(td, "merged", "d", "f"), []byte{}, 0644); err != nil { + if err := os.WriteFile(filepath.Join(td, mergedDirName, "d", "f"), []byte{}, 0644); err != nil { return errors.Wrap(err, "failed to write to merged directory") } @@ -80,7 +88,7 @@ func doesSupportNativeDiff(d string) error { } // rename "d1" to "d2" - if err := os.Rename(filepath.Join(td, "merged", "d1"), filepath.Join(td, "merged", "d2")); err != nil { + if err := os.Rename(filepath.Join(td, mergedDirName, "d1"), filepath.Join(td, mergedDirName, "d2")); err != nil { // if rename failed with syscall.EXDEV, the kernel doesn't have CONFIG_OVERLAY_FS_REDIRECT_DIR enabled if err.(*os.LinkError).Err == syscall.EXDEV { return nil @@ -99,35 +107,3 @@ func doesSupportNativeDiff(d string) error { return nil } - -// supportsMultipleLowerDir checks if the system supports multiple lowerdirs, -// which is required for the overlay2 driver. On 4.x kernels, multiple lowerdirs -// are always available (so this check isn't needed), and backported to RHEL and -// CentOS 3.x kernels (3.10.0-693.el7.x86_64 and up). This function is to detect -// support on those kernels, without doing a kernel version compare. -func supportsMultipleLowerDir(d string) error { - td, err := ioutil.TempDir(d, "multiple-lowerdir-check") - if err != nil { - return err - } - defer func() { - if err := os.RemoveAll(td); err != nil { - logger.Warnf("Failed to remove check directory %v: %v", td, err) - } - }() - - for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} { - if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil { - return err - } - } - - opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "lower2"), path.Join(td, "lower1"), path.Join(td, "upper"), path.Join(td, "work")) - if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", 0, opts); err != nil { - return errors.Wrap(err, "failed to mount overlay") - } - if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil { - logger.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err) - } - return nil -} diff --git a/daemon/graphdriver/overlay2/mount.go b/daemon/graphdriver/overlay2/mount.go index da409fc81a5e1..dcd7c0149009d 100644 --- a/daemon/graphdriver/overlay2/mount.go +++ b/daemon/graphdriver/overlay2/mount.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2" @@ -53,7 +54,7 @@ func mountFrom(dir, device, target, mType string, flags uintptr, label string) e w.Close() return fmt.Errorf("mountfrom error on re-exec cmd: %v", err) } - //write the options to the pipe for the untar exec to read + // write the options to the pipe for the untar exec to read if err := json.NewEncoder(w).Encode(options); err != nil { w.Close() return fmt.Errorf("mountfrom json encode to pipe failed: %v", err) diff --git a/daemon/graphdriver/overlay2/overlay.go b/daemon/graphdriver/overlay2/overlay.go index 6da1f25a34e62..444f600f556a4 100644 --- a/daemon/graphdriver/overlay2/overlay.go +++ b/daemon/graphdriver/overlay2/overlay.go @@ -1,16 +1,14 @@ +//go:build linux // +build linux package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2" import ( - "bufio" "context" "errors" "fmt" "io" - "io/ioutil" "os" - "os/exec" "path" "path/filepath" "strconv" @@ -19,20 +17,18 @@ import ( "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/daemon/graphdriver/overlayutils" - "github.com/docker/docker/daemon/graphdriver/quota" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/chrootarchive" "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/directory" "github.com/docker/docker/pkg/fsutils" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/locker" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/parsers" - "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/system" - "github.com/docker/go-units" - rsystem "github.com/opencontainers/runc/libcontainer/system" + "github.com/docker/docker/quota" + units "github.com/docker/go-units" + "github.com/moby/locker" + "github.com/moby/sys/mount" "github.com/opencontainers/selinux/go-selinux/label" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -71,10 +67,13 @@ var ( // that mounts do not fail due to length. const ( - driverName = "overlay2" - linkDir = "l" - lowerFile = "lower" - maxDepth = 128 + driverName = "overlay2" + linkDir = "l" + diffDirName = "diff" + workDirName = "work" + mergedDirName = "merged" + lowerFile = "lower" + maxDepth = 128 // idLength represents the number of random characters // which can be used to create the unique link identifier @@ -113,7 +112,8 @@ var ( useNaiveDiffLock sync.Once useNaiveDiffOnly bool - indexOff string + indexOff string + userxattr string ) func init() { @@ -131,16 +131,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap return nil, err } - if err := supportsOverlay(); err != nil { - return nil, graphdriver.ErrNotSupported - } - - // require kernel 4.0.0 to ensure multiple lower dirs are supported - v, err := kernel.GetKernelVersion() - if err != nil { - return nil, err - } - // Perform feature detection on /var/lib/docker/overlay2 if it's an existing directory. // This covers situations where /var/lib/docker/overlay2 is a mount, and on a different // filesystem than /var/lib/docker. @@ -150,6 +140,11 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap testdir = filepath.Dir(testdir) } + if err := overlayutils.SupportsOverlay(testdir, true); err != nil { + logger.Error(err) + return nil, graphdriver.ErrNotSupported + } + fsMagic, err := graphdriver.GetFSMagic(testdir) if err != nil { return nil, err @@ -158,32 +153,6 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap backingFs = fsName } - switch fsMagic { - case graphdriver.FsMagicAufs, graphdriver.FsMagicEcryptfs, graphdriver.FsMagicNfsFs, graphdriver.FsMagicOverlay, graphdriver.FsMagicZfs: - logger.Errorf("'overlay2' is not supported over %s", backingFs) - return nil, graphdriver.ErrIncompatibleFS - case graphdriver.FsMagicBtrfs: - // Support for OverlayFS on BTRFS was added in kernel 4.7 - // See https://btrfs.wiki.kernel.org/index.php/Changelog - if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 7, Minor: 0}) < 0 { - if !opts.overrideKernelCheck { - logger.Errorf("'overlay2' requires kernel 4.7 to use on %s", backingFs) - return nil, graphdriver.ErrIncompatibleFS - } - logger.Warn("Using pre-4.7.0 kernel for overlay2 on btrfs, may require kernel update") - } - } - - if kernel.CompareKernelVersion(*v, kernel.VersionInfo{Kernel: 4, Major: 0, Minor: 0}) < 0 { - if opts.overrideKernelCheck { - logger.Warn("Using pre-4.0.0 kernel for overlay2, mount failures may require kernel update") - } else { - if err := supportsMultipleLowerDir(testdir); err != nil { - logger.Debugf("Multiple lower dirs not supported: %v", err) - return nil, graphdriver.ErrNotSupported - } - } - } supportsDType, err := fsutils.SupportsDType(testdir) if err != nil { return nil, err @@ -196,12 +165,20 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap logger.Warn(overlayutils.ErrDTypeNotSupported("overlay2", backingFs)) } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) + _, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { return nil, err } - // Create the driver home dir - if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + + cur := idtools.CurrentIdentity() + dirID := idtools.Identity{ + UID: cur.UID, + GID: rootGID, + } + if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil { + return nil, err + } + if err := idtools.MkdirAllAndChown(path.Join(home, linkDir), 0700, cur); err != nil { return nil, err } @@ -240,7 +217,16 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap logger.Warnf("Unable to detect whether overlay kernel module supports index parameter: %s", err) } - logger.Debugf("backingFs=%s, projectQuotaSupported=%v, indexOff=%q", backingFs, projectQuotaSupported, indexOff) + needsUserXattr, err := overlayutils.NeedsUserXAttr(home) + if err != nil { + logger.Warnf("Unable to detect whether overlay kernel module needs \"userxattr\" parameter: %s", err) + } + if needsUserXattr { + userxattr = "userxattr," + } + + logger.Debugf("backingFs=%s, projectQuotaSupported=%v, indexOff=%q, userxattr=%q", + backingFs, projectQuotaSupported, indexOff, userxattr) return d, nil } @@ -272,27 +258,6 @@ func parseOptions(options []string) (*overlayOptions, error) { return o, nil } -func supportsOverlay() error { - // We can try to modprobe overlay first before looking at - // proc/filesystems for when overlay is supported - exec.Command("modprobe", "overlay").Run() - - f, err := os.Open("/proc/filesystems") - if err != nil { - return err - } - defer f.Close() - - s := bufio.NewScanner(f) - for s.Scan() { - if s.Text() == "nodev\toverlay" { - return nil - } - } - logger.Error("'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") - return graphdriver.ErrNotSupported -} - func useNaiveDiff(home string) bool { useNaiveDiffLock.Do(func() { if err := doesSupportNativeDiff(home); err != nil { @@ -314,6 +279,7 @@ func (d *Driver) Status() [][2]string { {"Backing Filesystem", backingFs}, {"Supports d_type", strconv.FormatBool(d.supportsDType)}, {"Native Overlay Diff", strconv.FormatBool(!useNaiveDiff(d.home))}, + {"userxattr", strconv.FormatBool(userxattr != "")}, } } @@ -326,9 +292,9 @@ func (d *Driver) GetMetadata(id string) (map[string]string, error) { } metadata := map[string]string{ - "WorkDir": path.Join(dir, "work"), - "MergedDir": path.Join(dir, "merged"), - "UpperDir": path.Join(dir, "diff"), + "WorkDir": path.Join(dir, workDirName), + "MergedDir": path.Join(dir, mergedDirName), + "UpperDir": path.Join(dir, diffDirName), } lowerDirs, err := d.getLowerDirs(id) @@ -352,23 +318,23 @@ func (d *Driver) Cleanup() error { // CreateReadWrite creates a layer that is writable for use as a container // file system. func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { - if opts != nil && len(opts.StorageOpt) != 0 && !projectQuotaSupported { - return fmt.Errorf("--storage-opt is supported only for overlay over xfs with 'pquota' mount option") - } - if opts == nil { opts = &graphdriver.CreateOpts{ - StorageOpt: map[string]string{}, + StorageOpt: make(map[string]string), } + } else if opts.StorageOpt == nil { + opts.StorageOpt = make(map[string]string) } - if _, ok := opts.StorageOpt["size"]; !ok { - if opts.StorageOpt == nil { - opts.StorageOpt = map[string]string{} - } + // Merge daemon default config. + if _, ok := opts.StorageOpt["size"]; !ok && d.options.quota.Size != 0 { opts.StorageOpt["size"] = strconv.FormatUint(d.options.quota.Size, 10) } + if _, ok := opts.StorageOpt["size"]; ok && !projectQuotaSupported { + return fmt.Errorf("--storage-opt is supported only for overlay over xfs with 'pquota' mount option") + } + return d.create(id, parent, opts) } @@ -391,11 +357,15 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr return err } root := idtools.Identity{UID: rootUID, GID: rootGID} + dirID := idtools.Identity{ + UID: idtools.CurrentIdentity().UID, + GID: rootGID, + } - if err := idtools.MkdirAllAndChown(path.Dir(dir), 0700, root); err != nil { + if err := idtools.MkdirAllAndChown(path.Dir(dir), 0710, dirID); err != nil { return err } - if err := idtools.MkdirAndChown(dir, 0700, root); err != nil { + if err := idtools.MkdirAndChown(dir, 0710, dirID); err != nil { return err } @@ -420,17 +390,17 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr } } - if err := idtools.MkdirAndChown(path.Join(dir, "diff"), 0755, root); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, diffDirName), 0755, root); err != nil { return err } - lid := generateID(idLength) - if err := os.Symlink(path.Join("..", id, "diff"), path.Join(d.home, linkDir, lid)); err != nil { + lid := overlayutils.GenerateID(idLength, logger) + if err := os.Symlink(path.Join("..", id, diffDirName), path.Join(d.home, linkDir, lid)); err != nil { return err } // Write link id to link file - if err := ioutil.WriteFile(path.Join(dir, "link"), []byte(lid), 0644); err != nil { + if err := os.WriteFile(path.Join(dir, "link"), []byte(lid), 0644); err != nil { return err } @@ -439,7 +409,11 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr return nil } - if err := idtools.MkdirAndChown(path.Join(dir, "work"), 0700, root); err != nil { + if err := idtools.MkdirAndChown(path.Join(dir, workDirName), 0700, root); err != nil { + return err + } + + if err := os.WriteFile(path.Join(d.dir(parent), "committed"), []byte{}, 0600); err != nil { return err } @@ -448,7 +422,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr return err } if lower != "" { - if err := ioutil.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0666); err != nil { + if err := os.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0666); err != nil { return err } } @@ -485,13 +459,13 @@ func (d *Driver) getLower(parent string) (string, error) { } // Read Parent link fileA - parentLink, err := ioutil.ReadFile(path.Join(parentDir, "link")) + parentLink, err := os.ReadFile(path.Join(parentDir, "link")) if err != nil { return "", err } lowers := []string{path.Join(linkDir, string(parentLink))} - parentLower, err := ioutil.ReadFile(path.Join(parentDir, lowerFile)) + parentLower, err := os.ReadFile(path.Join(parentDir, lowerFile)) if err == nil { parentLowers := strings.Split(string(parentLower), ":") lowers = append(lowers, parentLowers...) @@ -508,7 +482,7 @@ func (d *Driver) dir(id string) string { func (d *Driver) getLowerDirs(id string) ([]string, error) { var lowersArray []string - lowers, err := ioutil.ReadFile(path.Join(d.dir(id), lowerFile)) + lowers, err := os.ReadFile(path.Join(d.dir(id), lowerFile)) if err == nil { for _, s := range strings.Split(string(lowers), ":") { lp, err := os.Readlink(path.Join(d.home, s)) @@ -531,7 +505,7 @@ func (d *Driver) Remove(id string) error { d.locker.Lock(id) defer d.locker.Unlock(id) dir := d.dir(id) - lid, err := ioutil.ReadFile(path.Join(dir, "link")) + lid, err := os.ReadFile(path.Join(dir, "link")) if err == nil { if len(lid) == 0 { logger.Errorf("refusing to remove empty link for layer %v", id) @@ -555,8 +529,8 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e return nil, err } - diffDir := path.Join(dir, "diff") - lowers, err := ioutil.ReadFile(path.Join(dir, lowerFile)) + diffDir := path.Join(dir, diffDirName) + lowers, err := os.ReadFile(path.Join(dir, lowerFile)) if err != nil { // If no lower, just return diff directory if os.IsNotExist(err) { @@ -565,7 +539,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e return nil, err } - mergedDir := path.Join(dir, "merged") + mergedDir := path.Join(dir, mergedDirName) if count := d.ctr.Increment(mergedDir); count > 1 { return containerfs.NewLocalContainerFS(mergedDir), nil } @@ -583,13 +557,26 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e } }() - workDir := path.Join(dir, "work") + workDir := path.Join(dir, workDirName) splitLowers := strings.Split(string(lowers), ":") absLowers := make([]string, len(splitLowers)) for i, s := range splitLowers { absLowers[i] = path.Join(d.home, s) } - opts := indexOff + "lowerdir=" + strings.Join(absLowers, ":") + ",upperdir=" + path.Join(dir, "diff") + ",workdir=" + path.Join(dir, "work") + var readonly bool + if _, err := os.Stat(path.Join(dir, "committed")); err == nil { + readonly = true + } else if !os.IsNotExist(err) { + return nil, err + } + + var opts string + if readonly { + opts = indexOff + userxattr + "lowerdir=" + diffDir + ":" + strings.Join(absLowers, ":") + } else { + opts = indexOff + userxattr + "lowerdir=" + strings.Join(absLowers, ":") + ",upperdir=" + diffDir + ",workdir=" + workDir + } + mountData := label.FormatMountLabel(opts, mountLabel) mount := unix.Mount mountTarget := mergedDir @@ -604,40 +591,37 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e pageSize := unix.Getpagesize() - // Go can return a larger page size than supported by the system - // as of go 1.7. This will be fixed in 1.8 and this block can be - // removed when building with 1.8. - // See https://github.com/golang/go/commit/1b9499b06989d2831e5b156161d6c07642926ee1 - // See https://github.com/docker/docker/issues/27384 - if pageSize > 4096 { - pageSize = 4096 - } - // Use relative paths and mountFrom when the mount data has exceeded // the page size. The mount syscall fails if the mount data cannot // fit within a page and relative links make the mount data much // smaller at the expense of requiring a fork exec to chroot. - if len(mountData) > pageSize { - opts = indexOff + "lowerdir=" + string(lowers) + ",upperdir=" + path.Join(id, "diff") + ",workdir=" + path.Join(id, "work") + if len(mountData) > pageSize-1 { + if readonly { + opts = indexOff + userxattr + "lowerdir=" + path.Join(id, diffDirName) + ":" + string(lowers) + } else { + opts = indexOff + userxattr + "lowerdir=" + string(lowers) + ",upperdir=" + path.Join(id, diffDirName) + ",workdir=" + path.Join(id, workDirName) + } mountData = label.FormatMountLabel(opts, mountLabel) - if len(mountData) > pageSize { + if len(mountData) > pageSize-1 { return nil, fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData)) } mount = func(source string, target string, mType string, flags uintptr, label string) error { return mountFrom(d.home, source, target, mType, flags, label) } - mountTarget = path.Join(id, "merged") + mountTarget = path.Join(id, mergedDirName) } if err := mount("overlay", mountTarget, "overlay", 0, mountData); err != nil { return nil, fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err) } - // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a - // user namespace requires this to move a directory from lower to upper. - if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil { - return nil, err + if !readonly { + // chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a + // user namespace requires this to move a directory from lower to upper. + if err := os.Chown(path.Join(workDir, workDirName), rootUID, rootGID); err != nil { + return nil, err + } } return containerfs.NewLocalContainerFS(mergedDir), nil @@ -650,7 +634,7 @@ func (d *Driver) Put(id string) error { d.locker.Lock(id) defer d.locker.Unlock(id) dir := d.dir(id) - _, err := ioutil.ReadFile(path.Join(dir, lowerFile)) + _, err := os.ReadFile(path.Join(dir, lowerFile)) if err != nil { // If no lower, no mount happened and just return directly if os.IsNotExist(err) { @@ -659,7 +643,7 @@ func (d *Driver) Put(id string) error { return err } - mountpoint := path.Join(dir, "merged") + mountpoint := path.Join(dir, mergedDirName) if count := d.ctr.Decrement(mountpoint); count > 0 { return nil } @@ -709,10 +693,11 @@ func (d *Driver) isParent(id, parent string) bool { // ApplyDiff applies the new layer into a root func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64, err error) { - if !d.isParent(id, parent) { + if useNaiveDiff(d.home) || !d.isParent(id, parent) { return d.naiveDiff.ApplyDiff(id, parent, diff) } + // never reach here if we are running in UserNS applyDir := d.getDiffPath(id) logger.Debugf("Applying tar in %s", applyDir) @@ -721,7 +706,6 @@ func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64 UIDMaps: d.uidMaps, GIDMaps: d.gidMaps, WhiteoutFormat: archive.OverlayWhiteoutFormat, - InUserNS: rsystem.RunningInUserNS(), }); err != nil { return 0, err } @@ -732,7 +716,7 @@ func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64 func (d *Driver) getDiffPath(id string) string { dir := d.dir(id) - return path.Join(dir, "diff") + return path.Join(dir, diffDirName) } // DiffSize calculates the changes between the specified id @@ -752,6 +736,7 @@ func (d *Driver) Diff(id, parent string) (io.ReadCloser, error) { return d.naiveDiff.Diff(id, parent) } + // never reach here if we are running in UserNS diffPath := d.getDiffPath(id) logger.Debugf("Tar with options on %s", diffPath) return archive.TarWithOptions(diffPath, &archive.TarOptions{ diff --git a/daemon/graphdriver/overlay2/overlay_test.go b/daemon/graphdriver/overlay2/overlay_test.go index 6befa7db57d4e..47f3f11005dfa 100644 --- a/daemon/graphdriver/overlay2/overlay_test.go +++ b/daemon/graphdriver/overlay2/overlay_test.go @@ -1,9 +1,9 @@ +//go:build linux // +build linux package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2" import ( - "io/ioutil" "os" "testing" @@ -23,7 +23,7 @@ func init() { } func skipIfNaive(t *testing.T) { - td, err := ioutil.TempDir("", "naive-check-") + td, err := os.MkdirTemp("", "naive-check-") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) } diff --git a/daemon/graphdriver/overlay2/overlay_unsupported.go b/daemon/graphdriver/overlay2/overlay_unsupported.go index 68b75a366af31..e34c13df60ae8 100644 --- a/daemon/graphdriver/overlay2/overlay_unsupported.go +++ b/daemon/graphdriver/overlay2/overlay_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2" diff --git a/daemon/graphdriver/overlayutils/overlayutils.go b/daemon/graphdriver/overlayutils/overlayutils.go index 71f6d2d46077f..5100c87bf9b9e 100644 --- a/daemon/graphdriver/overlayutils/overlayutils.go +++ b/daemon/graphdriver/overlayutils/overlayutils.go @@ -1,11 +1,18 @@ +//go:build linux // +build linux package overlayutils // import "github.com/docker/docker/daemon/graphdriver/overlayutils" import ( "fmt" + "os" + "path" + "path/filepath" "github.com/docker/docker/daemon/graphdriver" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) // ErrDTypeNotSupported denotes that the backing filesystem doesn't support d_type. @@ -23,3 +30,50 @@ func ErrDTypeNotSupported(driver, backingFs string) error { return graphdriver.NotSupportedError(msg) } + +// SupportsOverlay checks if the system supports overlay filesystem +// by performing an actual overlay mount. +// +// checkMultipleLowers parameter enables check for multiple lowerdirs, +// which is required for the overlay2 driver. +func SupportsOverlay(d string, checkMultipleLowers bool) error { + // We can't rely on go-selinux.GetEnabled() to detect whether SELinux is enabled, + // because RootlessKit doesn't mount /sys/fs/selinux in the child: https://github.com/rootless-containers/rootlesskit/issues/94 + // So we check $_DOCKERD_ROOTLESS_SELINUX, which is set by dockerd-rootless.sh . + if os.Getenv("_DOCKERD_ROOTLESS_SELINUX") == "1" { + // Kernel 5.11 introduced support for rootless overlayfs, but incompatible with SELinux, + // so fallback to fuse-overlayfs. + // https://github.com/moby/moby/issues/42333 + return errors.New("overlay is not supported for Rootless with SELinux") + } + + td, err := os.MkdirTemp(d, "check-overlayfs-support") + if err != nil { + return err + } + defer func() { + if err := os.RemoveAll(td); err != nil { + logrus.Warnf("Failed to remove check directory %v: %v", td, err) + } + }() + + for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} { + if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil { + return err + } + } + + mnt := filepath.Join(td, "merged") + lowerDir := path.Join(td, "lower2") + if checkMultipleLowers { + lowerDir += ":" + path.Join(td, "lower1") + } + opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, path.Join(td, "upper"), path.Join(td, "work")) + if err := unix.Mount("overlay", mnt, "overlay", 0, opts); err != nil { + return errors.Wrap(err, "failed to mount overlay") + } + if err := unix.Unmount(mnt, 0); err != nil { + logrus.Warnf("Failed to unmount check directory %v: %v", mnt, err) + } + return nil +} diff --git a/daemon/graphdriver/overlay2/randomid.go b/daemon/graphdriver/overlayutils/randomid.go similarity index 87% rename from daemon/graphdriver/overlay2/randomid.go rename to daemon/graphdriver/overlayutils/randomid.go index 8f3f4627886c6..2c6706b38870c 100644 --- a/daemon/graphdriver/overlay2/randomid.go +++ b/daemon/graphdriver/overlayutils/randomid.go @@ -1,6 +1,7 @@ +//go:build linux // +build linux -package overlay2 // import "github.com/docker/docker/daemon/graphdriver/overlay2" +package overlayutils // import "github.com/docker/docker/daemon/graphdriver/overlayutils" import ( "crypto/rand" @@ -11,11 +12,12 @@ import ( "syscall" "time" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) -// generateID creates a new random string identifier with the given length -func generateID(l int) string { +// GenerateID creates a new random string identifier with the given length +func GenerateID(l int, logger *logrus.Entry) string { const ( // ensures we backoff for less than 450ms total. Use the following to // select new value, in units of 10ms: diff --git a/daemon/graphdriver/overlayutils/userxattr.go b/daemon/graphdriver/overlayutils/userxattr.go new file mode 100644 index 0000000000000..f5176c450da5d --- /dev/null +++ b/daemon/graphdriver/overlayutils/userxattr.go @@ -0,0 +1,116 @@ +//go:build linux +// +build linux + +// Forked from https://github.com/containerd/containerd/blob/9ade247b38b5a685244e1391c86ff41ab109556e/snapshots/overlay/check.go +/* + Copyright The containerd Authors. + + 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. +*/ + +package overlayutils + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/pkg/userns" + "github.com/docker/docker/pkg/parsers/kernel" + "github.com/sirupsen/logrus" +) + +// NeedsUserXAttr returns whether overlayfs should be mounted with the "userxattr" mount option. +// +// The "userxattr" option is needed for mounting overlayfs inside a user namespace with kernel >= 5.11. +// +// The "userxattr" option is NOT needed for the initial user namespace (aka "the host"). +// +// Also, Ubuntu (since circa 2015) and Debian (since 10) with kernel < 5.11 can mount +// the overlayfs in a user namespace without the "userxattr" option. +// +// The corresponding kernel commit: https://github.com/torvalds/linux/commit/2d2f2d7322ff43e0fe92bf8cccdc0b09449bf2e1 +// > ovl: user xattr +// > +// > Optionally allow using "user.overlay." namespace instead of "trusted.overlay." +// > ... +// > Disable redirect_dir and metacopy options, because these would allow privilege escalation through direct manipulation of the +// > "user.overlay.redirect" or "user.overlay.metacopy" xattrs. +// > ... +// +// The "userxattr" support is not exposed in "/sys/module/overlay/parameters". +func NeedsUserXAttr(d string) (bool, error) { + if !userns.RunningInUserNS() { + // we are the real root (i.e., the root in the initial user NS), + // so we do never need "userxattr" opt. + return false, nil + } + + // Fast path for kernel >= 5.11 . + // + // Keep in mind that distro vendors might be going to backport the patch to older kernels. + // So we can't completely remove the "slow path". + if kernel.CheckKernelVersion(5, 11, 0) { + return true, nil + } + + tdRoot := filepath.Join(d, "userxattr-check") + if err := os.RemoveAll(tdRoot); err != nil { + logrus.WithError(err).Warnf("Failed to remove check directory %v", tdRoot) + } + + if err := os.MkdirAll(tdRoot, 0700); err != nil { + return false, err + } + + defer func() { + if err := os.RemoveAll(tdRoot); err != nil { + logrus.WithError(err).Warnf("Failed to remove check directory %v", tdRoot) + } + }() + + td, err := os.MkdirTemp(tdRoot, "") + if err != nil { + return false, err + } + + for _, dir := range []string{"lower1", "lower2", "upper", "work", "merged"} { + if err := os.Mkdir(filepath.Join(td, dir), 0755); err != nil { + return false, err + } + } + + opts := []string{ + fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", filepath.Join(td, "lower2"), filepath.Join(td, "lower1"), filepath.Join(td, "upper"), filepath.Join(td, "work")), + "userxattr", + } + + m := mount.Mount{ + Type: "overlay", + Source: "overlay", + Options: opts, + } + + dest := filepath.Join(td, "merged") + if err := m.Mount(dest); err != nil { + // Probably the host is running Ubuntu/Debian kernel (< 5.11) with the userns patch but without the userxattr patch. + // Return false without error. + logrus.WithError(err).Debugf("cannot mount overlay with \"userxattr\", probably the kernel does not support userxattr") + return false, nil + } + if err := mount.UnmountAll(dest, 0); err != nil { + logrus.WithError(err).Warnf("Failed to unmount check directory %v", dest) + } + return true, nil +} diff --git a/daemon/graphdriver/plugin.go b/daemon/graphdriver/plugin.go index b0983c566797c..00f1dc9b6df1b 100644 --- a/daemon/graphdriver/plugin.go +++ b/daemon/graphdriver/plugin.go @@ -7,7 +7,7 @@ import ( "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" - "github.com/docker/docker/plugin/v2" + v2 "github.com/docker/docker/plugin/v2" "github.com/pkg/errors" ) diff --git a/daemon/graphdriver/quota/errors.go b/daemon/graphdriver/quota/errors.go deleted file mode 100644 index 68e797470dfe7..0000000000000 --- a/daemon/graphdriver/quota/errors.go +++ /dev/null @@ -1,19 +0,0 @@ -package quota // import "github.com/docker/docker/daemon/graphdriver/quota" - -import "github.com/docker/docker/errdefs" - -var ( - _ errdefs.ErrNotImplemented = (*errQuotaNotSupported)(nil) -) - -// ErrQuotaNotSupported indicates if were found the FS didn't have projects quotas available -var ErrQuotaNotSupported = errQuotaNotSupported{} - -type errQuotaNotSupported struct { -} - -func (e errQuotaNotSupported) NotImplemented() {} - -func (e errQuotaNotSupported) Error() string { - return "Filesystem does not support, or has not enabled quotas" -} diff --git a/daemon/graphdriver/quota/projectquota.go b/daemon/graphdriver/quota/projectquota.go deleted file mode 100644 index 93e85823af845..0000000000000 --- a/daemon/graphdriver/quota/projectquota.go +++ /dev/null @@ -1,384 +0,0 @@ -// +build linux - -// -// projectquota.go - implements XFS project quota controls -// for setting quota limits on a newly created directory. -// It currently supports the legacy XFS specific ioctls. -// -// TODO: use generic quota control ioctl FS_IOC_FS{GET,SET}XATTR -// for both xfs/ext4 for kernel version >= v4.5 -// - -package quota // import "github.com/docker/docker/daemon/graphdriver/quota" - -/* -#include -#include -#include -#include -#include - -#ifndef FS_XFLAG_PROJINHERIT -struct fsxattr { - __u32 fsx_xflags; - __u32 fsx_extsize; - __u32 fsx_nextents; - __u32 fsx_projid; - unsigned char fsx_pad[12]; -}; -#define FS_XFLAG_PROJINHERIT 0x00000200 -#endif -#ifndef FS_IOC_FSGETXATTR -#define FS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr) -#endif -#ifndef FS_IOC_FSSETXATTR -#define FS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr) -#endif - -#ifndef PRJQUOTA -#define PRJQUOTA 2 -#endif -#ifndef XFS_PROJ_QUOTA -#define XFS_PROJ_QUOTA 2 -#endif -#ifndef Q_XSETPQLIM -#define Q_XSETPQLIM QCMD(Q_XSETQLIM, PRJQUOTA) -#endif -#ifndef Q_XGETPQUOTA -#define Q_XGETPQUOTA QCMD(Q_XGETQUOTA, PRJQUOTA) -#endif - -const int Q_XGETQSTAT_PRJQUOTA = QCMD(Q_XGETQSTAT, PRJQUOTA); -*/ -import "C" -import ( - "fmt" - "io/ioutil" - "path" - "path/filepath" - "unsafe" - - rsystem "github.com/opencontainers/runc/libcontainer/system" - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" -) - -// Quota limit params - currently we only control blocks hard limit -type Quota struct { - Size uint64 -} - -// Control - Context to be used by storage driver (e.g. overlay) -// who wants to apply project quotas to container dirs -type Control struct { - backingFsBlockDev string - nextProjectID uint32 - quotas map[string]uint32 -} - -// NewControl - initialize project quota support. -// Test to make sure that quota can be set on a test dir and find -// the first project id to be used for the next container create. -// -// Returns nil (and error) if project quota is not supported. -// -// First get the project id of the home directory. -// This test will fail if the backing fs is not xfs. -// -// xfs_quota tool can be used to assign a project id to the driver home directory, e.g.: -// echo 999:/var/lib/docker/overlay2 >> /etc/projects -// echo docker:999 >> /etc/projid -// xfs_quota -x -c 'project -s docker' / -// -// In that case, the home directory project id will be used as a "start offset" -// and all containers will be assigned larger project ids (e.g. >= 1000). -// This is a way to prevent xfs_quota management from conflicting with docker. -// -// Then try to create a test directory with the next project id and set a quota -// on it. If that works, continue to scan existing containers to map allocated -// project ids. -// -func NewControl(basePath string) (*Control, error) { - // - // If we are running in a user namespace quota won't be supported for - // now since makeBackingFsDev() will try to mknod(). - // - if rsystem.RunningInUserNS() { - return nil, ErrQuotaNotSupported - } - - // - // create backing filesystem device node - // - backingFsBlockDev, err := makeBackingFsDev(basePath) - if err != nil { - return nil, err - } - - // check if we can call quotactl with project quotas - // as a mechanism to determine (early) if we have support - hasQuotaSupport, err := hasQuotaSupport(backingFsBlockDev) - if err != nil { - return nil, err - } - if !hasQuotaSupport { - return nil, ErrQuotaNotSupported - } - - // - // Get project id of parent dir as minimal id to be used by driver - // - minProjectID, err := getProjectID(basePath) - if err != nil { - return nil, err - } - minProjectID++ - - // - // Test if filesystem supports project quotas by trying to set - // a quota on the first available project id - // - quota := Quota{ - Size: 0, - } - if err := setProjectQuota(backingFsBlockDev, minProjectID, quota); err != nil { - return nil, err - } - - q := Control{ - backingFsBlockDev: backingFsBlockDev, - nextProjectID: minProjectID + 1, - quotas: make(map[string]uint32), - } - - // - // get first project id to be used for next container - // - err = q.findNextProjectID(basePath) - if err != nil { - return nil, err - } - - logrus.Debugf("NewControl(%s): nextProjectID = %d", basePath, q.nextProjectID) - return &q, nil -} - -// SetQuota - assign a unique project id to directory and set the quota limits -// for that project id -func (q *Control) SetQuota(targetPath string, quota Quota) error { - - projectID, ok := q.quotas[targetPath] - if !ok { - projectID = q.nextProjectID - - // - // assign project id to new container directory - // - err := setProjectID(targetPath, projectID) - if err != nil { - return err - } - - q.quotas[targetPath] = projectID - q.nextProjectID++ - } - - // - // set the quota limit for the container's project id - // - logrus.Debugf("SetQuota(%s, %d): projectID=%d", targetPath, quota.Size, projectID) - return setProjectQuota(q.backingFsBlockDev, projectID, quota) -} - -// setProjectQuota - set the quota for project id on xfs block device -func setProjectQuota(backingFsBlockDev string, projectID uint32, quota Quota) error { - var d C.fs_disk_quota_t - d.d_version = C.FS_DQUOT_VERSION - d.d_id = C.__u32(projectID) - d.d_flags = C.XFS_PROJ_QUOTA - - d.d_fieldmask = C.FS_DQ_BHARD | C.FS_DQ_BSOFT - d.d_blk_hardlimit = C.__u64(quota.Size / 512) - d.d_blk_softlimit = d.d_blk_hardlimit - - var cs = C.CString(backingFsBlockDev) - defer C.free(unsafe.Pointer(cs)) - - _, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, C.Q_XSETPQLIM, - uintptr(unsafe.Pointer(cs)), uintptr(d.d_id), - uintptr(unsafe.Pointer(&d)), 0, 0) - if errno != 0 { - return fmt.Errorf("Failed to set quota limit for projid %d on %s: %v", - projectID, backingFsBlockDev, errno.Error()) - } - - return nil -} - -// GetQuota - get the quota limits of a directory that was configured with SetQuota -func (q *Control) GetQuota(targetPath string, quota *Quota) error { - - projectID, ok := q.quotas[targetPath] - if !ok { - return fmt.Errorf("quota not found for path : %s", targetPath) - } - - // - // get the quota limit for the container's project id - // - var d C.fs_disk_quota_t - - var cs = C.CString(q.backingFsBlockDev) - defer C.free(unsafe.Pointer(cs)) - - _, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, C.Q_XGETPQUOTA, - uintptr(unsafe.Pointer(cs)), uintptr(C.__u32(projectID)), - uintptr(unsafe.Pointer(&d)), 0, 0) - if errno != 0 { - return fmt.Errorf("Failed to get quota limit for projid %d on %s: %v", - projectID, q.backingFsBlockDev, errno.Error()) - } - quota.Size = uint64(d.d_blk_hardlimit) * 512 - - return nil -} - -// getProjectID - get the project id of path on xfs -func getProjectID(targetPath string) (uint32, error) { - dir, err := openDir(targetPath) - if err != nil { - return 0, err - } - defer closeDir(dir) - - var fsx C.struct_fsxattr - _, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.FS_IOC_FSGETXATTR, - uintptr(unsafe.Pointer(&fsx))) - if errno != 0 { - return 0, fmt.Errorf("Failed to get projid for %s: %v", targetPath, errno.Error()) - } - - return uint32(fsx.fsx_projid), nil -} - -// setProjectID - set the project id of path on xfs -func setProjectID(targetPath string, projectID uint32) error { - dir, err := openDir(targetPath) - if err != nil { - return err - } - defer closeDir(dir) - - var fsx C.struct_fsxattr - _, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.FS_IOC_FSGETXATTR, - uintptr(unsafe.Pointer(&fsx))) - if errno != 0 { - return fmt.Errorf("Failed to get projid for %s: %v", targetPath, errno.Error()) - } - fsx.fsx_projid = C.__u32(projectID) - fsx.fsx_xflags |= C.FS_XFLAG_PROJINHERIT - _, _, errno = unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.FS_IOC_FSSETXATTR, - uintptr(unsafe.Pointer(&fsx))) - if errno != 0 { - return fmt.Errorf("Failed to set projid for %s: %v", targetPath, errno.Error()) - } - - return nil -} - -// findNextProjectID - find the next project id to be used for containers -// by scanning driver home directory to find used project ids -func (q *Control) findNextProjectID(home string) error { - files, err := ioutil.ReadDir(home) - if err != nil { - return fmt.Errorf("read directory failed : %s", home) - } - for _, file := range files { - if !file.IsDir() { - continue - } - path := filepath.Join(home, file.Name()) - projid, err := getProjectID(path) - if err != nil { - return err - } - if projid > 0 { - q.quotas[path] = projid - } - if q.nextProjectID <= projid { - q.nextProjectID = projid + 1 - } - } - - return nil -} - -func free(p *C.char) { - C.free(unsafe.Pointer(p)) -} - -func openDir(path string) (*C.DIR, error) { - Cpath := C.CString(path) - defer free(Cpath) - - dir := C.opendir(Cpath) - if dir == nil { - return nil, fmt.Errorf("Can't open dir") - } - return dir, nil -} - -func closeDir(dir *C.DIR) { - if dir != nil { - C.closedir(dir) - } -} - -func getDirFd(dir *C.DIR) uintptr { - return uintptr(C.dirfd(dir)) -} - -// Get the backing block device of the driver home directory -// and create a block device node under the home directory -// to be used by quotactl commands -func makeBackingFsDev(home string) (string, error) { - var stat unix.Stat_t - if err := unix.Stat(home, &stat); err != nil { - return "", err - } - - backingFsBlockDev := path.Join(home, "backingFsBlockDev") - // Re-create just in case someone copied the home directory over to a new device - unix.Unlink(backingFsBlockDev) - err := unix.Mknod(backingFsBlockDev, unix.S_IFBLK|0600, int(stat.Dev)) - switch err { - case nil: - return backingFsBlockDev, nil - - case unix.ENOSYS: - return "", ErrQuotaNotSupported - - default: - return "", fmt.Errorf("Failed to mknod %s: %v", backingFsBlockDev, err) - } -} - -func hasQuotaSupport(backingFsBlockDev string) (bool, error) { - var cs = C.CString(backingFsBlockDev) - defer free(cs) - var qstat C.fs_quota_stat_t - - _, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, uintptr(C.Q_XGETQSTAT_PRJQUOTA), uintptr(unsafe.Pointer(cs)), 0, uintptr(unsafe.Pointer(&qstat)), 0, 0) - if errno == 0 && qstat.qs_flags&C.FS_QUOTA_PDQ_ENFD > 0 && qstat.qs_flags&C.FS_QUOTA_PDQ_ACCT > 0 { - return true, nil - } - - switch errno { - // These are the known fatal errors, consider all other errors (ENOTTY, etc.. not supporting quota) - case unix.EFAULT, unix.ENOENT, unix.ENOTBLK, unix.EPERM: - default: - return false, nil - } - - return false, errno -} diff --git a/daemon/graphdriver/quota/projectquota_test.go b/daemon/graphdriver/quota/projectquota_test.go deleted file mode 100644 index aa164cc4198a1..0000000000000 --- a/daemon/graphdriver/quota/projectquota_test.go +++ /dev/null @@ -1,152 +0,0 @@ -// +build linux - -package quota // import "github.com/docker/docker/daemon/graphdriver/quota" - -import ( - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "testing" - - "golang.org/x/sys/unix" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/fs" -) - -// 10MB -const testQuotaSize = 10 * 1024 * 1024 -const imageSize = 64 * 1024 * 1024 - -func TestBlockDev(t *testing.T) { - mkfs, err := exec.LookPath("mkfs.xfs") - if err != nil { - t.Skip("mkfs.xfs not found in PATH") - } - - // create a sparse image - imageFile, err := ioutil.TempFile("", "xfs-image") - if err != nil { - t.Fatal(err) - } - imageFileName := imageFile.Name() - defer os.Remove(imageFileName) - if _, err = imageFile.Seek(imageSize-1, 0); err != nil { - t.Fatal(err) - } - if _, err = imageFile.Write([]byte{0}); err != nil { - t.Fatal(err) - } - if err = imageFile.Close(); err != nil { - t.Fatal(err) - } - - // The reason for disabling these options is sometimes people run with a newer userspace - // than kernelspace - out, err := exec.Command(mkfs, "-m", "crc=0,finobt=0", imageFileName).CombinedOutput() - if len(out) > 0 { - t.Log(string(out)) - } - if err != nil { - t.Fatal(err) - } - - t.Run("testBlockDevQuotaDisabled", wrapMountTest(imageFileName, false, testBlockDevQuotaDisabled)) - t.Run("testBlockDevQuotaEnabled", wrapMountTest(imageFileName, true, testBlockDevQuotaEnabled)) - t.Run("testSmallerThanQuota", wrapMountTest(imageFileName, true, wrapQuotaTest(testSmallerThanQuota))) - t.Run("testBiggerThanQuota", wrapMountTest(imageFileName, true, wrapQuotaTest(testBiggerThanQuota))) - t.Run("testRetrieveQuota", wrapMountTest(imageFileName, true, wrapQuotaTest(testRetrieveQuota))) -} - -func wrapMountTest(imageFileName string, enableQuota bool, testFunc func(t *testing.T, mountPoint, backingFsDev string)) func(*testing.T) { - return func(t *testing.T) { - mountOptions := "loop" - - if enableQuota { - mountOptions = mountOptions + ",prjquota" - } - - mountPointDir := fs.NewDir(t, "xfs-mountPoint") - defer mountPointDir.Remove() - mountPoint := mountPointDir.Path() - - out, err := exec.Command("mount", "-o", mountOptions, imageFileName, mountPoint).CombinedOutput() - if err != nil { - _, err := os.Stat("/proc/fs/xfs") - if os.IsNotExist(err) { - t.Skip("no /proc/fs/xfs") - } - } - - assert.NilError(t, err, "mount failed: %s", out) - - defer func() { - assert.NilError(t, unix.Unmount(mountPoint, 0)) - }() - - backingFsDev, err := makeBackingFsDev(mountPoint) - assert.NilError(t, err) - - testFunc(t, mountPoint, backingFsDev) - } -} - -func testBlockDevQuotaDisabled(t *testing.T, mountPoint, backingFsDev string) { - hasSupport, err := hasQuotaSupport(backingFsDev) - assert.NilError(t, err) - assert.Check(t, !hasSupport) -} - -func testBlockDevQuotaEnabled(t *testing.T, mountPoint, backingFsDev string) { - hasSupport, err := hasQuotaSupport(backingFsDev) - assert.NilError(t, err) - assert.Check(t, hasSupport) -} - -func wrapQuotaTest(testFunc func(t *testing.T, ctrl *Control, mountPoint, testDir, testSubDir string)) func(t *testing.T, mountPoint, backingFsDev string) { - return func(t *testing.T, mountPoint, backingFsDev string) { - testDir, err := ioutil.TempDir(mountPoint, "per-test") - assert.NilError(t, err) - defer os.RemoveAll(testDir) - - ctrl, err := NewControl(testDir) - assert.NilError(t, err) - - testSubDir, err := ioutil.TempDir(testDir, "quota-test") - assert.NilError(t, err) - testFunc(t, ctrl, mountPoint, testDir, testSubDir) - } - -} - -func testSmallerThanQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) { - assert.NilError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize})) - smallerThanQuotaFile := filepath.Join(testSubDir, "smaller-than-quota") - assert.NilError(t, ioutil.WriteFile(smallerThanQuotaFile, make([]byte, testQuotaSize/2), 0644)) - assert.NilError(t, os.Remove(smallerThanQuotaFile)) -} - -func testBiggerThanQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) { - // Make sure the quota is being enforced - // TODO: When we implement this under EXT4, we need to shed CAP_SYS_RESOURCE, otherwise - // we're able to violate quota without issue - assert.NilError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize})) - - biggerThanQuotaFile := filepath.Join(testSubDir, "bigger-than-quota") - err := ioutil.WriteFile(biggerThanQuotaFile, make([]byte, testQuotaSize+1), 0644) - assert.Assert(t, is.ErrorContains(err, "")) - if err == io.ErrShortWrite { - assert.NilError(t, os.Remove(biggerThanQuotaFile)) - } -} - -func testRetrieveQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) { - // Validate that we can retrieve quota - assert.NilError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize})) - - var q Quota - assert.NilError(t, ctrl.GetQuota(testSubDir, &q)) - assert.Check(t, is.Equal(uint64(testQuotaSize), q.Size)) -} diff --git a/daemon/graphdriver/register/register_aufs.go b/daemon/graphdriver/register/register_aufs.go index ec18d1d377c11..4c028f72ef747 100644 --- a/daemon/graphdriver/register/register_aufs.go +++ b/daemon/graphdriver/register/register_aufs.go @@ -1,3 +1,4 @@ +//go:build !exclude_graphdriver_aufs && linux // +build !exclude_graphdriver_aufs,linux package register // import "github.com/docker/docker/daemon/graphdriver/register" diff --git a/daemon/graphdriver/register/register_btrfs.go b/daemon/graphdriver/register/register_btrfs.go index 2f8c67056b591..5167f8553db14 100644 --- a/daemon/graphdriver/register/register_btrfs.go +++ b/daemon/graphdriver/register/register_btrfs.go @@ -1,3 +1,4 @@ +//go:build !exclude_graphdriver_btrfs && linux // +build !exclude_graphdriver_btrfs,linux package register // import "github.com/docker/docker/daemon/graphdriver/register" diff --git a/daemon/graphdriver/register/register_devicemapper.go b/daemon/graphdriver/register/register_devicemapper.go index ccbb8bfabe85e..ce16e36cca302 100644 --- a/daemon/graphdriver/register/register_devicemapper.go +++ b/daemon/graphdriver/register/register_devicemapper.go @@ -1,3 +1,4 @@ +//go:build !exclude_graphdriver_devicemapper && !static_build && linux // +build !exclude_graphdriver_devicemapper,!static_build,linux package register // import "github.com/docker/docker/daemon/graphdriver/register" diff --git a/daemon/graphdriver/register/register_fuseoverlayfs.go b/daemon/graphdriver/register/register_fuseoverlayfs.go new file mode 100644 index 0000000000000..c4ebdf262825b --- /dev/null +++ b/daemon/graphdriver/register/register_fuseoverlayfs.go @@ -0,0 +1,9 @@ +//go:build !exclude_graphdriver_fuseoverlayfs && linux +// +build !exclude_graphdriver_fuseoverlayfs,linux + +package register // import "github.com/docker/docker/daemon/graphdriver/register" + +import ( + // register the fuse-overlayfs graphdriver + _ "github.com/docker/docker/daemon/graphdriver/fuse-overlayfs" +) diff --git a/daemon/graphdriver/register/register_overlay.go b/daemon/graphdriver/register/register_overlay.go index a2e384d5480d6..9b6c8c36db4e8 100644 --- a/daemon/graphdriver/register/register_overlay.go +++ b/daemon/graphdriver/register/register_overlay.go @@ -1,3 +1,4 @@ +//go:build !exclude_graphdriver_overlay && linux // +build !exclude_graphdriver_overlay,linux package register // import "github.com/docker/docker/daemon/graphdriver/register" diff --git a/daemon/graphdriver/register/register_overlay2.go b/daemon/graphdriver/register/register_overlay2.go index bcd2cee20e1a5..53233c50096d9 100644 --- a/daemon/graphdriver/register/register_overlay2.go +++ b/daemon/graphdriver/register/register_overlay2.go @@ -1,3 +1,4 @@ +//go:build !exclude_graphdriver_overlay2 && linux // +build !exclude_graphdriver_overlay2,linux package register // import "github.com/docker/docker/daemon/graphdriver/register" diff --git a/daemon/graphdriver/register/register_windows.go b/daemon/graphdriver/register/register_windows.go index cd612cbea96d6..2b6fb6911d6f9 100644 --- a/daemon/graphdriver/register/register_windows.go +++ b/daemon/graphdriver/register/register_windows.go @@ -2,6 +2,5 @@ package register // import "github.com/docker/docker/daemon/graphdriver/register import ( // register the windows graph drivers - _ "github.com/docker/docker/daemon/graphdriver/lcow" _ "github.com/docker/docker/daemon/graphdriver/windows" ) diff --git a/daemon/graphdriver/register/register_zfs.go b/daemon/graphdriver/register/register_zfs.go index b137ad25b7c06..2632864b4e0af 100644 --- a/daemon/graphdriver/register/register_zfs.go +++ b/daemon/graphdriver/register/register_zfs.go @@ -1,3 +1,4 @@ +//go:build (!exclude_graphdriver_zfs && linux) || (!exclude_graphdriver_zfs && freebsd) // +build !exclude_graphdriver_zfs,linux !exclude_graphdriver_zfs,freebsd package register // import "github.com/docker/docker/daemon/graphdriver/register" diff --git a/daemon/graphdriver/vfs/copy_unsupported.go b/daemon/graphdriver/vfs/copy_unsupported.go index 894ff02f020ad..1b0185fedb0b5 100644 --- a/daemon/graphdriver/vfs/copy_unsupported.go +++ b/daemon/graphdriver/vfs/copy_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package vfs // import "github.com/docker/docker/daemon/graphdriver/vfs" diff --git a/daemon/graphdriver/vfs/driver.go b/daemon/graphdriver/vfs/driver.go index 33e6bf6cc9c2f..f903393da2b03 100644 --- a/daemon/graphdriver/vfs/driver.go +++ b/daemon/graphdriver/vfs/driver.go @@ -6,12 +6,15 @@ import ( "path/filepath" "github.com/docker/docker/daemon/graphdriver" - "github.com/docker/docker/daemon/graphdriver/quota" + "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" + "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/system" - "github.com/docker/go-units" + "github.com/docker/docker/quota" + units "github.com/docker/go-units" "github.com/opencontainers/selinux/go-selinux/label" + "github.com/pkg/errors" ) var ( @@ -30,13 +33,29 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap home: home, idMapping: idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), } - rootIDs := d.idMapping.RootPair() - if err := idtools.MkdirAllAndChown(home, 0700, rootIDs); err != nil { + + if err := d.parseOptions(options); err != nil { + return nil, err + } + _, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) + if err != nil { + return nil, err + } + + dirID := idtools.Identity{ + UID: idtools.CurrentIdentity().UID, + GID: rootGID, + } + if err := idtools.MkdirAllAndChown(home, 0710, dirID); err != nil { return nil, err } setupDriverQuota(d) + if size := d.getQuotaOpt(); !d.quotaSupported() && size > 0 { + return nil, quota.ErrQuotaNotSupported + } + return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil } @@ -69,11 +88,32 @@ func (d *Driver) Cleanup() error { return nil } +func (d *Driver) parseOptions(options []string) error { + for _, option := range options { + key, val, err := parsers.ParseKeyValueOpt(option) + if err != nil { + return errdefs.InvalidParameter(err) + } + switch key { + case "size": + size, err := units.RAMInBytes(val) + if err != nil { + return errdefs.InvalidParameter(err) + } + if err = d.setQuotaOpt(uint64(size)); err != nil { + return errdefs.InvalidParameter(errors.Wrap(err, "failed to set option size for vfs")) + } + default: + return errdefs.InvalidParameter(errors.Errorf("unknown option %s for vfs", key)) + } + } + return nil +} + // CreateReadWrite creates a layer that is writable for use as a container // file system. func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { - var err error - var size int64 + quotaSize := d.getQuotaOpt() if opts != nil { for key, val := range opts.StorageOpt { @@ -82,16 +122,18 @@ func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts if !d.quotaSupported() { return quota.ErrQuotaNotSupported } - if size, err = units.RAMInBytes(val); err != nil { - return err + size, err := units.RAMInBytes(val) + if err != nil { + return errdefs.InvalidParameter(err) } + quotaSize = uint64(size) default: - return fmt.Errorf("Storage opt %s not supported", key) + return errdefs.InvalidParameter(errors.Errorf("Storage opt %s not supported", key)) } } } - return d.create(id, parent, uint64(size)) + return d.create(id, parent, quotaSize) } // Create prepares the filesystem for the VFS driver and copies the directory for the given id under the parent. @@ -106,7 +148,12 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { func (d *Driver) create(id, parent string, size uint64) error { dir := d.dir(id) rootIDs := d.idMapping.RootPair() - if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0700, rootIDs); err != nil { + + dirID := idtools.Identity{ + UID: idtools.CurrentIdentity().UID, + GID: rootIDs.GID, + } + if err := idtools.MkdirAllAndChown(filepath.Dir(dir), 0710, dirID); err != nil { return err } if err := idtools.MkdirAndChown(dir, 0755, rootIDs); err != nil { diff --git a/daemon/graphdriver/vfs/quota_linux.go b/daemon/graphdriver/vfs/quota_linux.go index 0d5c3a7b98fb2..372cbbb769b94 100644 --- a/daemon/graphdriver/vfs/quota_linux.go +++ b/daemon/graphdriver/vfs/quota_linux.go @@ -1,12 +1,14 @@ package vfs // import "github.com/docker/docker/daemon/graphdriver/vfs" import ( - "github.com/docker/docker/daemon/graphdriver/quota" + "github.com/docker/docker/quota" "github.com/sirupsen/logrus" ) +//nolint:structcheck type driverQuota struct { quotaCtl *quota.Control + quotaOpt quota.Quota } func setupDriverQuota(driver *Driver) { @@ -17,6 +19,15 @@ func setupDriverQuota(driver *Driver) { } } +func (d *Driver) setQuotaOpt(size uint64) error { + d.quotaOpt.Size = size + return nil +} + +func (d *Driver) getQuotaOpt() uint64 { + return d.quotaOpt.Size +} + func (d *Driver) setupQuota(dir string, size uint64) error { return d.quotaCtl.SetQuota(dir, quota.Quota{Size: size}) } diff --git a/daemon/graphdriver/vfs/quota_unsupported.go b/daemon/graphdriver/vfs/quota_unsupported.go index 3ae60ac07cdde..ecd16ebdda9a0 100644 --- a/daemon/graphdriver/vfs/quota_unsupported.go +++ b/daemon/graphdriver/vfs/quota_unsupported.go @@ -1,8 +1,9 @@ +//go:build !linux // +build !linux package vfs // import "github.com/docker/docker/daemon/graphdriver/vfs" -import "github.com/docker/docker/daemon/graphdriver/quota" +import "github.com/docker/docker/quota" type driverQuota struct { } @@ -11,6 +12,14 @@ func setupDriverQuota(driver *Driver) error { return nil } +func (d *Driver) setQuotaOpt(size uint64) error { + return quota.ErrQuotaNotSupported +} + +func (d *Driver) getQuotaOpt() uint64 { + return 0 +} + func (d *Driver) setupQuota(dir string, size uint64) error { return quota.ErrQuotaNotSupported } diff --git a/daemon/graphdriver/vfs/vfs_test.go b/daemon/graphdriver/vfs/vfs_test.go index 7c59ec32e26be..63db564518e6c 100644 --- a/daemon/graphdriver/vfs/vfs_test.go +++ b/daemon/graphdriver/vfs/vfs_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package vfs // import "github.com/docker/docker/daemon/graphdriver/vfs" diff --git a/daemon/graphdriver/windows/windows.go b/daemon/graphdriver/windows/windows.go index 347231b1044ac..a384f8cde1911 100644 --- a/daemon/graphdriver/windows/windows.go +++ b/daemon/graphdriver/windows/windows.go @@ -1,14 +1,15 @@ -//+build windows +//go:build windows +// +build windows package windows // import "github.com/docker/docker/daemon/graphdriver/windows" import ( + "archive/tar" "bufio" "bytes" "encoding/json" "fmt" "io" - "io/ioutil" "os" "path" "path/filepath" @@ -19,11 +20,11 @@ import ( "time" "unsafe" - "github.com/Microsoft/go-winio" - "github.com/Microsoft/go-winio/archive/tar" + winio "github.com/Microsoft/go-winio" "github.com/Microsoft/go-winio/backuptar" "github.com/Microsoft/go-winio/vhd" "github.com/Microsoft/hcsshim" + "github.com/Microsoft/hcsshim/osversion" "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/containerfs" @@ -31,15 +32,21 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/longpath" "github.com/docker/docker/pkg/reexec" - "github.com/docker/docker/pkg/system" units "github.com/docker/go-units" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/windows" ) -// filterDriver is an HCSShim driver type for the Windows Filter driver. -const filterDriver = 1 +const ( + // filterDriver is an HCSShim driver type for the Windows Filter driver. + filterDriver = 1 + // For WCOW, the default of 20GB hard-coded in the platform + // is too small for builder scenarios where many users are + // using RUN or COPY statements to install large amounts of data. + // Use 127GB as that's the default size of a VHD in Hyper-V. + defaultSandboxSize = "127GB" +) var ( // mutatedFiles is a list of files that are mutated by the import process @@ -73,6 +80,10 @@ func (c *checker) IsMounted(path string) bool { return false } +type storageOptions struct { + size uint64 +} + // Driver represents a windows graph driver. type Driver struct { // info stores the shim driver information @@ -80,8 +91,9 @@ type Driver struct { ctr *graphdriver.RefCounter // it is safe for windows to use a cache here because it does not support // restoring containers when the daemon dies. - cacheMu sync.Mutex - cache map[string]string + cacheMu sync.Mutex + cache map[string]string + defaultStorageOpts *storageOptions } // InitFilter returns a new Windows storage filter driver. @@ -100,13 +112,27 @@ func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) return nil, fmt.Errorf("windowsfilter failed to create '%s': %v", home, err) } + storageOpt := make(map[string]string) + storageOpt["size"] = defaultSandboxSize + + for _, v := range options { + opt := strings.SplitN(v, "=", 2) + storageOpt[strings.ToLower(opt[0])] = opt[1] + } + + storageOptions, err := parseStorageOpt(storageOpt) + if err != nil { + return nil, fmt.Errorf("windowsfilter failed to parse default storage options - %s", err) + } + d := &Driver{ info: hcsshim.DriverInfo{ HomeDir: home, Flavour: filterDriver, }, - cache: make(map[string]string), - ctr: graphdriver.NewRefCounter(&checker{}), + cache: make(map[string]string), + ctr: graphdriver.NewRefCounter(&checker{}), + defaultStorageOpts: storageOptions, } return d, nil } @@ -231,8 +257,13 @@ func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt return fmt.Errorf("Failed to parse storage options - %s", err) } + sandboxSize := d.defaultStorageOpts.size if storageOptions.size != 0 { - if err := hcsshim.ExpandSandboxSize(d.info, id, storageOptions.size); err != nil { + sandboxSize = storageOptions.size + } + + if sandboxSize != 0 { + if err := hcsshim.ExpandSandboxSize(d.info, id, sandboxSize); err != nil { return err } } @@ -272,7 +303,6 @@ func (d *Driver) Remove(id string) error { // it is a transient error. Retry until it succeeds. var computeSystems []hcsshim.ContainerProperties retryCount := 0 - osv := system.GetOSVersion() for { // Get and terminate any template VMs that are currently using the layer. // Note: It is unfortunate that we end up in the graphdrivers Remove() call @@ -286,7 +316,7 @@ func (d *Driver) Remove(id string) error { // in RS1 and RS2 building during enumeration when a silo is going away // for example under it, in HCS. AccessIsDenied added to fix 30278. // - // TODO @jhowardmsft - For RS3, we can remove the retries. Also consider + // TODO: For RS3, we can remove the retries. Also consider // using platform APIs (if available) to get this more succinctly. Also // consider enhancing the Remove() interface to have context of why // the remove is being called - that could improve efficiency by not @@ -294,8 +324,10 @@ func (d *Driver) Remove(id string) error { // not required. computeSystems, err = hcsshim.GetContainers(hcsshim.ComputeSystemQuery{}) if err != nil { - if (osv.Build < 15139) && - ((err == hcsshim.ErrVmcomputeOperationInvalidState) || (err == hcsshim.ErrVmcomputeOperationAccessIsDenied)) { + if osversion.Build() >= osversion.RS3 { + return err + } + if (err == hcsshim.ErrVmcomputeOperationInvalidState) || (err == hcsshim.ErrVmcomputeOperationAccessIsDenied) { if retryCount >= 500 { break } @@ -338,11 +370,14 @@ func (d *Driver) Remove(id string) error { // If permission denied, it's possible that the scratch is still mounted, an // artifact after a hard daemon crash for example. Worth a shot to try detaching it // before retrying the rename. - if detachErr := vhd.DetachVhd(filepath.Join(layerPath, "sandbox.vhdx")); detachErr != nil { - return errors.Wrapf(err, "failed to detach VHD: %s", detachErr) - } - if renameErr := os.Rename(layerPath, tmpLayerPath); renameErr != nil && !os.IsNotExist(renameErr) { - return errors.Wrapf(err, "second rename attempt following detach failed: %s", renameErr) + sandbox := filepath.Join(layerPath, "sandbox.vhdx") + if _, statErr := os.Stat(sandbox); statErr == nil { + if detachErr := vhd.DetachVhd(sandbox); detachErr != nil { + return errors.Wrapf(err, "failed to detach VHD: %s", detachErr) + } + if renameErr := os.Rename(layerPath, tmpLayerPath); renameErr != nil && !os.IsNotExist(renameErr) { + return errors.Wrapf(err, "second rename attempt following detach failed: %s", renameErr) + } } } if err := hcsshim.DestroyLayer(d.info, tmpID); err != nil { @@ -446,7 +481,7 @@ func (d *Driver) Put(id string) error { // We use this opportunity to cleanup any -removing folders which may be // still left if the daemon was killed while it was removing a layer. func (d *Driver) Cleanup() error { - items, err := ioutil.ReadDir(d.info.HomeDir) + items, err := os.ReadDir(d.info.HomeDir) if err != nil { if os.IsNotExist(err) { return nil @@ -797,13 +832,13 @@ func writeLayerReexec() { // writeLayer writes a layer from a tar file. func writeLayer(layerData io.Reader, home string, id string, parentLayerPaths ...string) (size int64, retErr error) { - err := winio.EnableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}) + err := winio.EnableProcessPrivileges([]string{winio.SeSecurityPrivilege, winio.SeBackupPrivilege, winio.SeRestorePrivilege}) if err != nil { return 0, err } if noreexec { defer func() { - if err := winio.DisableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege}); err != nil { + if err := winio.DisableProcessPrivileges([]string{winio.SeSecurityPrivilege, winio.SeBackupPrivilege, winio.SeRestorePrivilege}); err != nil { // This should never happen, but just in case when in debugging mode. // See https://github.com/docker/docker/pull/28002#discussion_r86259241 for rationale. panic("Failed to disabled process privileges while in non re-exec mode") @@ -836,7 +871,7 @@ func writeLayer(layerData io.Reader, home string, id string, parentLayerPaths .. // resolveID computes the layerID information based on the given id. func (d *Driver) resolveID(id string) (string, error) { - content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerID")) + content, err := os.ReadFile(filepath.Join(d.dir(id), "layerID")) if os.IsNotExist(err) { return id, nil } else if err != nil { @@ -847,13 +882,13 @@ func (d *Driver) resolveID(id string) (string, error) { // setID stores the layerId in disk. func (d *Driver) setID(id, altID string) error { - return ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altID), 0600) + return os.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altID), 0600) } // getLayerChain returns the layer chain information. func (d *Driver) getLayerChain(id string) ([]string, error) { jPath := filepath.Join(d.dir(id), "layerchain.json") - content, err := ioutil.ReadFile(jPath) + content, err := os.ReadFile(jPath) if os.IsNotExist(err) { return nil, nil } else if err != nil { @@ -877,7 +912,7 @@ func (d *Driver) setLayerChain(id string, chain []string) error { } jPath := filepath.Join(d.dir(id), "layerchain.json") - err = ioutil.WriteFile(jPath, content, 0600) + err = os.WriteFile(jPath, content, 0600) if err != nil { return fmt.Errorf("Unable to write layerchain file - %s", err) } @@ -931,10 +966,6 @@ func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { return &fileGetCloserWithBackupPrivileges{d.dir(id)}, nil } -type storageOptions struct { - size uint64 -} - func parseStorageOpt(storageOpt map[string]string) (*storageOptions, error) { options := storageOptions{} diff --git a/daemon/graphdriver/zfs/zfs.go b/daemon/graphdriver/zfs/zfs.go index 8a798778d2914..90c080a228c19 100644 --- a/daemon/graphdriver/zfs/zfs.go +++ b/daemon/graphdriver/zfs/zfs.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package zfs // import "github.com/docker/docker/daemon/graphdriver/zfs" @@ -15,10 +16,12 @@ import ( "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/parsers" - "github.com/mistifyio/go-zfs" + zfs "github.com/mistifyio/go-zfs" + "github.com/moby/sys/mount" + "github.com/moby/sys/mountinfo" "github.com/opencontainers/selinux/go-selinux/label" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -53,7 +56,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri return nil, graphdriver.ErrPrerequisites } - file, err := os.OpenFile("/dev/zfs", os.O_RDWR, 600) + file, err := os.OpenFile("/dev/zfs", os.O_RDWR, 0600) if err != nil { logger.Debugf("cannot open /dev/zfs: %v", err) return nil, graphdriver.ErrPrerequisites @@ -102,11 +105,16 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri return nil, fmt.Errorf("BUG: zfs get all -t filesystem -rHp '%s' should contain '%s'", options.fsName, options.fsName) } - rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) + _, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) if err != nil { - return nil, fmt.Errorf("Failed to get root uid/guid: %v", err) + return nil, err + } + + dirID := idtools.Identity{ + UID: idtools.CurrentIdentity().UID, + GID: rootGID, } - if err := idtools.MkdirAllAndChown(base, 0700, idtools.Identity{UID: rootUID, GID: rootGID}); err != nil { + if err := idtools.MkdirAllAndChown(base, 0710, dirID); err != nil { return nil, fmt.Errorf("Failed to create '%s': %v", base, err) } @@ -147,7 +155,7 @@ func lookupZfsDataset(rootdir string) (string, error) { } wantedDev := stat.Dev - mounts, err := mount.GetMounts(nil) + mounts, err := mountinfo.GetMounts(nil) if err != nil { return "", err } @@ -157,7 +165,7 @@ func lookupZfsDataset(rootdir string) (string, error) { continue // may fail on fuse file systems } - if stat.Dev == wantedDev && m.Fstype == "zfs" { + if stat.Dev == wantedDev && m.FSType == "zfs" { return m.Source, nil } } @@ -390,7 +398,7 @@ func (d *Driver) Get(id, mountLabel string) (_ containerfs.ContainerFS, retErr e } if err := mount.Mount(filesystem, mountpoint, "zfs", options); err != nil { - return nil, fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err) + return nil, errors.Wrap(err, "error creating zfs mount") } // this could be our first mount after creation of the filesystem, and the root dir may still have root diff --git a/daemon/graphdriver/zfs/zfs_test.go b/daemon/graphdriver/zfs/zfs_test.go index b5d6cb18c7971..f8bcdaf524057 100644 --- a/daemon/graphdriver/zfs/zfs_test.go +++ b/daemon/graphdriver/zfs/zfs_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package zfs // import "github.com/docker/docker/daemon/graphdriver/zfs" diff --git a/daemon/graphdriver/zfs/zfs_unsupported.go b/daemon/graphdriver/zfs/zfs_unsupported.go index 1b77030684dbe..a1eda73e6973c 100644 --- a/daemon/graphdriver/zfs/zfs_unsupported.go +++ b/daemon/graphdriver/zfs/zfs_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !freebsd // +build !linux,!freebsd package zfs // import "github.com/docker/docker/daemon/graphdriver/zfs" diff --git a/daemon/health.go b/daemon/health.go index ae0d7f8921bc1..d6ae1d1e292b0 100644 --- a/daemon/health.go +++ b/daemon/health.go @@ -10,7 +10,6 @@ import ( "time" "github.com/docker/docker/api/types" - containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/strslice" "github.com/docker/docker/container" "github.com/docker/docker/daemon/exec" @@ -65,7 +64,7 @@ type cmdProbe struct { func (p *cmdProbe) run(ctx context.Context, d *Daemon, cntr *container.Container) (*types.HealthcheckResult, error) { cmdSlice := strslice.StrSlice(cntr.Config.Healthcheck.Test)[1:] if p.shell { - cmdSlice = append(getShell(cntr.Config), cmdSlice...) + cmdSlice = append(getShell(cntr), cmdSlice...) } entrypoint, args := d.getEntrypointAndArgs(strslice.StrSlice{}, cmdSlice) execConfig := exec.NewConfig() @@ -187,12 +186,18 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch func monitor(d *Daemon, c *container.Container, stop chan struct{}, probe probe) { probeTimeout := timeoutWithDefault(c.Config.Healthcheck.Timeout, defaultProbeTimeout) probeInterval := timeoutWithDefault(c.Config.Healthcheck.Interval, defaultProbeInterval) + + intervalTimer := time.NewTimer(probeInterval) + defer intervalTimer.Stop() + for { + intervalTimer.Reset(probeInterval) + select { case <-stop: logrus.Debugf("Stop healthcheck monitoring for container %s (received while idle)", c.ID) return - case <-time.After(probeInterval): + case <-intervalTimer.C: logrus.Debugf("Running health check for container %s ...", c.ID) startTime := time.Now() ctx, cancelProbe := context.WithTimeout(context.Background(), probeTimeout) @@ -268,7 +273,7 @@ func getProbe(c *container.Container) probe { // Ensure the health-check monitor is running or not, depending on the current // state of the container. // Called from monitor.go, with c locked. -func (d *Daemon) updateHealthMonitor(c *container.Container) { +func (daemon *Daemon) updateHealthMonitor(c *container.Container) { h := c.State.Health if h == nil { return // No healthcheck configured @@ -278,7 +283,7 @@ func (d *Daemon) updateHealthMonitor(c *container.Container) { wantRunning := c.Running && !c.Paused && probe != nil if wantRunning { if stop := h.OpenMonitorChannel(); stop != nil { - go monitor(d, c, stop, probe) + go monitor(daemon, c, stop, probe) } } else { h.CloseMonitorChannel() @@ -289,14 +294,14 @@ func (d *Daemon) updateHealthMonitor(c *container.Container) { // initHealthMonitor is called from monitor.go and we should never be running // two instances at once. // Called with c locked. -func (d *Daemon) initHealthMonitor(c *container.Container) { +func (daemon *Daemon) initHealthMonitor(c *container.Container) { // If no healthcheck is setup then don't init the monitor if getProbe(c) == nil { return } // This is needed in case we're auto-restarting - d.stopHealthchecks(c) + daemon.stopHealthchecks(c) if h := c.State.Health; h != nil { h.SetStatus(types.Starting) @@ -307,12 +312,12 @@ func (d *Daemon) initHealthMonitor(c *container.Container) { c.State.Health = h } - d.updateHealthMonitor(c) + daemon.updateHealthMonitor(c) } // Called when the container is being stopped (whether because the health check is // failing or for any other reason). -func (d *Daemon) stopHealthchecks(c *container.Container) { +func (daemon *Daemon) stopHealthchecks(c *container.Container) { h := c.State.Health if h != nil { h.CloseMonitorChannel() @@ -370,12 +375,15 @@ func min(x, y int) int { return y } -func getShell(config *containertypes.Config) []string { - if len(config.Shell) != 0 { - return config.Shell +func getShell(cntr *container.Container) []string { + if len(cntr.Config.Shell) != 0 { + return cntr.Config.Shell } if runtime.GOOS != "windows" { return []string{"/bin/sh", "-c"} } + if cntr.OS != runtime.GOOS { + return []string{"/bin/sh", "-c"} + } return []string{"cmd", "/S", "/C"} } diff --git a/daemon/health_test.go b/daemon/health_test.go index db166317fd588..d761dbc2a8d80 100644 --- a/daemon/health_test.go +++ b/daemon/health_test.go @@ -78,6 +78,7 @@ func TestHealthStates(t *testing.T) { EventsService: e, containersReplica: store, } + muteLogs() c.Config.Healthcheck = &containertypes.HealthConfig{ Retries: 1, diff --git a/daemon/images/cache.go b/daemon/images/cache.go index 3b433106e8a8b..445b1b9261831 100644 --- a/daemon/images/cache.go +++ b/daemon/images/cache.go @@ -15,7 +15,7 @@ func (i *ImageService) MakeImageCache(sourceRefs []string) builder.ImageCache { cache := cache.New(i.imageStore) for _, ref := range sourceRefs { - img, err := i.GetImage(ref) + img, err := i.GetImage(ref, nil) if err != nil { logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err) continue diff --git a/daemon/images/image.go b/daemon/images/image.go index 79cc07c4fda46..8b966047811ed 100644 --- a/daemon/images/image.go +++ b/daemon/images/image.go @@ -1,11 +1,23 @@ package images // import "github.com/docker/docker/daemon/images" import ( + "context" + "encoding/json" "fmt" + "io" + "github.com/containerd/containerd/content" + c8derrdefs "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/leases" + "github.com/containerd/containerd/platforms" "github.com/docker/distribution/reference" "github.com/docker/docker/errdefs" "github.com/docker/docker/image" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // ErrImageDoesNotExist is error returned when no image can be found for a reference. @@ -24,8 +36,151 @@ func (e ErrImageDoesNotExist) Error() string { // NotFound implements the NotFound interface func (e ErrImageDoesNotExist) NotFound() {} +type manifestList struct { + Manifests []specs.Descriptor `json:"manifests"` +} + +type manifest struct { + Config specs.Descriptor `json:"config"` +} + +func (i *ImageService) manifestMatchesPlatform(img *image.Image, platform specs.Platform) bool { + ctx := context.TODO() + logger := logrus.WithField("image", img.ID).WithField("desiredPlatform", platforms.Format(platform)) + + ls, leaseErr := i.leases.ListResources(context.TODO(), leases.Lease{ID: imageKey(img.ID().Digest())}) + if leaseErr != nil { + logger.WithError(leaseErr).Error("Error looking up image leases") + return false + } + + // Note we are comparing against manifest lists here, which we expect to always have a CPU variant set (where applicable). + // So there is no need for the fallback matcher here. + comparer := platforms.Only(platform) + + var ( + ml manifestList + m manifest + ) + + makeRdr := func(ra content.ReaderAt) io.Reader { + return io.LimitReader(io.NewSectionReader(ra, 0, ra.Size()), 1e6) + } + + for _, r := range ls { + logger := logger.WithField("resourceID", r.ID).WithField("resourceType", r.Type) + logger.Debug("Checking lease resource for platform match") + if r.Type != "content" { + continue + } + + ra, err := i.content.ReaderAt(ctx, specs.Descriptor{Digest: digest.Digest(r.ID)}) + if err != nil { + if c8derrdefs.IsNotFound(err) { + continue + } + logger.WithError(err).Error("Error looking up referenced manifest list for image") + continue + } + + data, err := io.ReadAll(makeRdr(ra)) + ra.Close() + + if err != nil { + logger.WithError(err).Error("Error reading manifest list for image") + continue + } + + ml.Manifests = nil + + if err := json.Unmarshal(data, &ml); err != nil { + logger.WithError(err).Error("Error unmarshalling content") + continue + } + + for _, md := range ml.Manifests { + switch md.MediaType { + case specs.MediaTypeImageManifest, images.MediaTypeDockerSchema2Manifest: + default: + continue + } + + p := specs.Platform{ + Architecture: md.Platform.Architecture, + OS: md.Platform.OS, + Variant: md.Platform.Variant, + } + if !comparer.Match(p) { + logger.WithField("otherPlatform", platforms.Format(p)).Debug("Manifest is not a match") + continue + } + + // Here we have a platform match for the referenced manifest, let's make sure the manifest is actually for the image config we are using. + + ra, err := i.content.ReaderAt(ctx, specs.Descriptor{Digest: md.Digest}) + if err != nil { + logger.WithField("otherDigest", md.Digest).WithError(err).Error("Could not get reader for manifest") + continue + } + + data, err := io.ReadAll(makeRdr(ra)) + ra.Close() + if err != nil { + logger.WithError(err).Error("Error reading manifest for image") + continue + } + + if err := json.Unmarshal(data, &m); err != nil { + logger.WithError(err).Error("Error desserializing manifest") + continue + } + + if m.Config.Digest == img.ID().Digest() { + logger.WithField("manifestDigest", md.Digest).Debug("Found matching manifest for image") + return true + } + + logger.WithField("otherDigest", md.Digest).Debug("Skipping non-matching manifest") + } + } + + return false +} + // GetImage returns an image corresponding to the image referred to by refOrID. -func (i *ImageService) GetImage(refOrID string) (*image.Image, error) { +func (i *ImageService) GetImage(refOrID string, platform *specs.Platform) (retImg *image.Image, retErr error) { + defer func() { + if retErr != nil || retImg == nil || platform == nil { + return + } + + imgPlat := specs.Platform{ + OS: retImg.OS, + Architecture: retImg.Architecture, + Variant: retImg.Variant, + } + p := *platform + // Note that `platforms.Only` will fuzzy match this for us + // For example: an armv6 image will run just fine an an armv7 CPU, without emulation or anything. + if OnlyPlatformWithFallback(p).Match(imgPlat) { + return + } + // In some cases the image config can actually be wrong (e.g. classic `docker build` may not handle `--platform` correctly) + // So we'll look up the manifest list that coresponds to this imaage to check if at least the manifest list says it is the correct image. + if i.manifestMatchesPlatform(retImg, p) { + return + } + + // This allows us to tell clients that we don't have the image they asked for + // Where this gets hairy is the image store does not currently support multi-arch images, e.g.: + // An image `foo` may have a multi-arch manifest, but the image store only fetches the image for a specific platform + // The image store does not store the manifest list and image tags are assigned to architecture specific images. + // So we can have a `foo` image that is amd64 but the user requested armv7. If the user looks at the list of images. + // This may be confusing. + // The alternative to this is to return a errdefs.Conflict error with a helpful message, but clients will not be + // able to automatically tell what causes the conflict. + retErr = errdefs.NotFound(errors.Errorf("image with reference %s was found but does not match the specified platform: wanted %s, actual: %s", refOrID, platforms.Format(p), platforms.Format(imgPlat))) + }() ref, err := reference.ParseAnyReference(refOrID) if err != nil { return nil, errdefs.InvalidParameter(err) @@ -62,3 +217,34 @@ func (i *ImageService) GetImage(refOrID string) (*image.Image, error) { return nil, ErrImageDoesNotExist{ref} } + +// OnlyPlatformWithFallback uses `platforms.Only` with a fallback to handle the case where the platform +// being matched does not have a CPU variant. +// +// The reason for this is that CPU variant is not even if the official image config spec as of this writing. +// See: https://github.com/opencontainers/image-spec/pull/809 +// Since Docker tends to compare platforms from the image config, we need to handle this case. +func OnlyPlatformWithFallback(p specs.Platform) platforms.Matcher { + return &onlyFallbackMatcher{only: platforms.Only(p), p: platforms.Normalize(p)} +} + +type onlyFallbackMatcher struct { + only platforms.Matcher + p specs.Platform +} + +func (m *onlyFallbackMatcher) Match(other specs.Platform) bool { + if m.only.Match(other) { + // It matches, no reason to fallback + return true + } + if other.Variant != "" { + // If there is a variant then this fallback does not apply, and there is no match + return false + } + otherN := platforms.Normalize(other) + otherN.Variant = "" // normalization adds a default variant... which is the whole problem with `platforms.Only` + + return m.p.OS == otherN.OS && + m.p.Architecture == otherN.Architecture +} diff --git a/daemon/images/image_builder.go b/daemon/images/image_builder.go index cdf951c6f5649..52cd3d88ee60e 100644 --- a/daemon/images/image_builder.go +++ b/daemon/images/image_builder.go @@ -5,18 +5,23 @@ import ( "io" "runtime" + "github.com/containerd/containerd/platforms" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/builder" + "github.com/docker/docker/errdefs" "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/containerfs" + "github.com/docker/docker/pkg/progress" + "github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/system" "github.com/docker/docker/registry" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) type roLayer struct { @@ -161,7 +166,29 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf if err := i.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil { return nil, err } - return i.GetImage(name) + + img, err := i.GetImage(name, platform) + if errdefs.IsNotFound(err) && img != nil && platform != nil { + imgPlat := specs.Platform{ + OS: img.OS, + Architecture: img.BaseImgArch(), + Variant: img.BaseImgVariant(), + } + + p := *platform + if !platforms.Only(p).Match(imgPlat) { + po := streamformatter.NewJSONProgressOutput(output, false) + progress.Messagef(po, "", ` +WARNING: Pulled image with specified platform (%s), but the resulting image's configured platform (%s) does not match. +This is most likely caused by a bug in the build system that created the fetched image (%s). +Please notify the image author to correct the configuration.`, + platforms.Format(p), platforms.Format(imgPlat), name, + ) + logrus.WithError(err).WithField("image", name).Warn("Ignoring error about platform mismatch where the manifest list points to an image whose configuration does not match the platform in the manifest.") + err = nil + } + } + return img, err } // GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID. @@ -170,18 +197,21 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) { if refOrID == "" { // ie FROM scratch os := runtime.GOOS + if runtime.GOOS == "windows" { + os = "linux" + } if opts.Platform != nil { os = opts.Platform.OS } if !system.IsOSSupported(os) { return nil, nil, system.ErrNotSupportedOperatingSystem } - layer, err := newROLayerForImage(nil, i.layerStores[os]) + layer, err := newROLayerForImage(nil, i.layerStore) return nil, layer, err } if opts.PullOption != backend.PullOptionForcePull { - image, err := i.GetImage(refOrID) + image, err := i.GetImage(refOrID, opts.Platform) if err != nil && opts.PullOption == backend.PullOptionNoPull { return nil, nil, err } @@ -190,7 +220,7 @@ func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID s if !system.IsOSSupported(image.OperatingSystem()) { return nil, nil, system.ErrNotSupportedOperatingSystem } - layer, err := newROLayerForImage(image, i.layerStores[image.OperatingSystem()]) + layer, err := newROLayerForImage(image, i.layerStore) return image, layer, err } } @@ -202,7 +232,7 @@ func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID s if !system.IsOSSupported(image.OperatingSystem()) { return nil, nil, system.ErrNotSupportedOperatingSystem } - layer, err := newROLayerForImage(image, i.layerStores[image.OperatingSystem()]) + layer, err := newROLayerForImage(image, i.layerStore) return image, layer, err } diff --git a/daemon/images/image_commit.go b/daemon/images/image_commit.go index 4caba9f27b761..58854bdd6142c 100644 --- a/daemon/images/image_commit.go +++ b/daemon/images/image_commit.go @@ -8,17 +8,12 @@ import ( "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/system" "github.com/pkg/errors" ) // CommitImage creates a new image from a commit config func (i *ImageService) CommitImage(c backend.CommitConfig) (image.ID, error) { - layerStore, ok := i.layerStores[c.ContainerOS] - if !ok { - return "", system.ErrNotSupportedOperatingSystem - } - rwTar, err := exportContainerRw(layerStore, c.ContainerID, c.ContainerMountLabel) + rwTar, err := exportContainerRw(i.layerStore, c.ContainerID, c.ContainerMountLabel) if err != nil { return "", err } @@ -39,11 +34,11 @@ func (i *ImageService) CommitImage(c backend.CommitConfig) (image.ID, error) { } } - l, err := layerStore.Register(rwTar, parent.RootFS.ChainID()) + l, err := i.layerStore.Register(rwTar, parent.RootFS.ChainID()) if err != nil { return "", err } - defer layer.ReleaseAndLog(layerStore, l) + defer layer.ReleaseAndLog(i.layerStore, l) cc := image.ChildConfig{ ContainerID: c.ContainerID, diff --git a/daemon/images/image_delete.go b/daemon/images/image_delete.go index 94d6f872dda54..c4acf89999e99 100644 --- a/daemon/images/image_delete.go +++ b/daemon/images/image_delete.go @@ -64,7 +64,7 @@ func (i *ImageService) ImageDelete(imageRef string, force, prune bool) ([]types. start := time.Now() records := []types.ImageDeleteResponseItem{} - img, err := i.GetImage(imageRef) + img, err := i.GetImage(imageRef, nil) if err != nil { return nil, err } @@ -369,7 +369,7 @@ func (i *ImageService) checkImageDeleteConflict(imgID image.ID, mask conflictTyp if mask&conflictRunningContainer != 0 { // Check if any running container is using the image. running := func(c *container.Container) bool { - return c.IsRunning() && c.ImageID == imgID + return c.ImageID == imgID && c.IsRunning() } if container := i.containers.First(running); container != nil { return &imageDeleteConflict{ diff --git a/daemon/images/image_events.go b/daemon/images/image_events.go index d0b3064d7043a..1d8cfcd914dc8 100644 --- a/daemon/images/image_events.go +++ b/daemon/images/image_events.go @@ -11,7 +11,7 @@ func (i *ImageService) LogImageEvent(imageID, refName, action string) { // LogImageEventWithAttributes generates an event related to an image with specific given attributes. func (i *ImageService) LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string) { - img, err := i.GetImage(imageID) + img, err := i.GetImage(imageID, nil) if err == nil && img.Config != nil { // image has not been removed yet. // it could be missing if the event is `delete`. diff --git a/daemon/images/image_exporter.go b/daemon/images/image_exporter.go index 58105dcb710cc..037a694b45597 100644 --- a/daemon/images/image_exporter.go +++ b/daemon/images/image_exporter.go @@ -12,14 +12,14 @@ import ( // the same tag are exported. names is the set of tags to export, and // outStream is the writer which the images are written to. func (i *ImageService) ExportImage(names []string, outStream io.Writer) error { - imageExporter := tarexport.NewTarExporter(i.imageStore, i.layerStores, i.referenceStore, i) + imageExporter := tarexport.NewTarExporter(i.imageStore, i.layerStore, i.referenceStore, i) return imageExporter.Save(names, outStream) } // LoadImage uploads a set of images into the repository. This is the -// complement of ImageExport. The input stream is an uncompressed tar +// complement of ExportImage. The input stream is an uncompressed tar // ball containing images and metadata. func (i *ImageService) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error { - imageExporter := tarexport.NewTarExporter(i.imageStore, i.layerStores, i.referenceStore, i) + imageExporter := tarexport.NewTarExporter(i.imageStore, i.layerStore, i.referenceStore, i) return imageExporter.Load(inTar, outStream, quiet) } diff --git a/daemon/images/image_history.go b/daemon/images/image_history.go index b4ca25b1b652b..373f979335ea9 100644 --- a/daemon/images/image_history.go +++ b/daemon/images/image_history.go @@ -14,7 +14,7 @@ import ( // name by walking the image lineage. func (i *ImageService) ImageHistory(name string) ([]*image.HistoryResponseItem, error) { start := time.Now() - img, err := i.GetImage(name) + img, err := i.GetImage(name, nil) if err != nil { return nil, err } @@ -36,12 +36,12 @@ func (i *ImageService) ImageHistory(name string) ([]*image.HistoryResponseItem, return nil, system.ErrNotSupportedOperatingSystem } rootFS.Append(img.RootFS.DiffIDs[layerCounter]) - l, err := i.layerStores[img.OperatingSystem()].Get(rootFS.ChainID()) + l, err := i.layerStore.Get(rootFS.ChainID()) if err != nil { return nil, err } layerSize, err = l.DiffSize() - layer.ReleaseAndLog(i.layerStores[img.OperatingSystem()], l) + layer.ReleaseAndLog(i.layerStore, l) if err != nil { return nil, err } @@ -77,7 +77,7 @@ func (i *ImageService) ImageHistory(name string) ([]*image.HistoryResponseItem, if id == "" { break } - histImg, err = i.GetImage(id.String()) + histImg, err = i.GetImage(id.String(), nil) if err != nil { break } diff --git a/daemon/images/image_import.go b/daemon/images/image_import.go index 8d54e0704fe77..bd0f60c674f1e 100644 --- a/daemon/images/image_import.go +++ b/daemon/images/image_import.go @@ -91,11 +91,11 @@ func (i *ImageService) ImportImage(src string, repository, os string, tag string if err != nil { return err } - l, err := i.layerStores[os].Register(inflatedLayerData, "") + l, err := i.layerStore.Register(inflatedLayerData, "") if err != nil { return err } - defer layer.ReleaseAndLog(i.layerStores[os], l) + defer layer.ReleaseAndLog(i.layerStore, l) created := time.Now().UTC() imgConfig, err := json.Marshal(&image.Image{ diff --git a/daemon/images/image_inspect.go b/daemon/images/image_inspect.go index 16c4c9b2dc950..f19d4ad514cdb 100644 --- a/daemon/images/image_inspect.go +++ b/daemon/images/image_inspect.go @@ -14,7 +14,7 @@ import ( // LookupImage looks up an image by name and returns it as an ImageInspect // structure. func (i *ImageService) LookupImage(name string) (*types.ImageInspect, error) { - img, err := i.GetImage(name) + img, err := i.GetImage(name, nil) if err != nil { return nil, errors.Wrapf(err, "no such image: %s", name) } @@ -37,11 +37,11 @@ func (i *ImageService) LookupImage(name string) (*types.ImageInspect, error) { var layerMetadata map[string]string layerID := img.RootFS.ChainID() if layerID != "" { - l, err := i.layerStores[img.OperatingSystem()].Get(layerID) + l, err := i.layerStore.Get(layerID) if err != nil { return nil, err } - defer layer.ReleaseAndLog(i.layerStores[img.OperatingSystem()], l) + defer layer.ReleaseAndLog(i.layerStore, l) size, err = l.Size() if err != nil { return nil, err @@ -76,6 +76,7 @@ func (i *ImageService) LookupImage(name string) (*types.ImageInspect, error) { Author: img.Author, Config: img.Config, Architecture: img.Architecture, + Variant: img.Variant, Os: img.OperatingSystem(), OsVersion: img.OSVersion, Size: size, @@ -86,7 +87,7 @@ func (i *ImageService) LookupImage(name string) (*types.ImageInspect, error) { }, } - imageInspect.GraphDriver.Name = i.layerStores[img.OperatingSystem()].DriverName() + imageInspect.GraphDriver.Name = i.layerStore.DriverName() imageInspect.GraphDriver.Data = layerMetadata return imageInspect, nil diff --git a/daemon/images/image_prune.go b/daemon/images/image_prune.go index 313494f2f48d0..55332020feaed 100644 --- a/daemon/images/image_prune.go +++ b/daemon/images/image_prune.go @@ -3,17 +3,19 @@ package images // import "github.com/docker/docker/daemon/images" import ( "context" "fmt" + "strconv" "sync/atomic" "time" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/filters" timetypes "github.com/docker/docker/api/types/time" "github.com/docker/docker/errdefs" "github.com/docker/docker/image" "github.com/docker/docker/layer" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -66,12 +68,7 @@ func (i *ImageService) ImagesPrune(ctx context.Context, pruneFilters filters.Arg } // Filter intermediary images and get their unique size - allLayers := make(map[layer.ChainID]layer.Layer) - for _, ls := range i.layerStores { - for k, v := range ls.Map() { - allLayers[k] = v - } - } + allLayers := i.layerStore.Map() topImages := map[image.ID]*image.Image{} for id, img := range allImages { select { @@ -159,7 +156,11 @@ deleteImagesLoop: if canceled { logrus.Debugf("ImagesPrune operation cancelled: %#v", *rep) } - + i.eventsService.Log("prune", events.ImageEventType, events.Actor{ + Attributes: map[string]string{ + "reclaimed": strconv.FormatUint(rep.SpaceReclaimed, 10), + }, + }) return rep, nil } diff --git a/daemon/images/image_pull.go b/daemon/images/image_pull.go index 3e6b4330373b9..bb8bce5aaa106 100644 --- a/daemon/images/image_pull.go +++ b/daemon/images/image_pull.go @@ -6,6 +6,8 @@ import ( "strings" "time" + "github.com/containerd/containerd/leases" + "github.com/containerd/containerd/namespaces" dist "github.com/docker/distribution" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" @@ -13,9 +15,11 @@ import ( progressutils "github.com/docker/docker/distribution/utils" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/progress" - "github.com/docker/docker/registry" - "github.com/opencontainers/go-digest" + "github.com/docker/docker/pkg/streamformatter" + digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // PullImage initiates a pull operation. image is the repository name to pull, and @@ -48,7 +52,29 @@ func (i *ImageService) PullImage(ctx context.Context, image, tag string, platfor err = i.pullImageWithReference(ctx, ref, platform, metaHeaders, authConfig, outStream) imageActions.WithValues("pull").UpdateSince(start) - return err + if err != nil { + return err + } + + if platform != nil { + // If --platform was specified, check that the image we pulled matches + // the expected platform. This check is for situations where the image + // is a single-arch image, in which case (for backward compatibility), + // we allow the image to have a non-matching architecture. The code + // below checks for this situation, and returns a warning to the client, + // as well as logging it to the daemon logs. + img, err := i.GetImage(image, platform) + + // Note that this is a special case where GetImage returns both an image + // and an error: https://github.com/docker/docker/blob/v20.10.7/daemon/images/image.go#L175-L183 + if errdefs.IsNotFound(err) && img != nil { + po := streamformatter.NewJSONProgressOutput(outStream, false) + progress.Messagef(po, "", `WARNING: %s`, err.Error()) + logrus.WithError(err).WithField("image", image).Warn("ignoring platform mismatch on single-arch image") + } + } + + return nil } func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference.Named, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { @@ -65,6 +91,25 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference close(writesDone) }() + ctx = namespaces.WithNamespace(ctx, i.contentNamespace) + // Take out a temporary lease for everything that gets persisted to the content store. + // Before the lease is cancelled, any content we want to keep should have it's own lease applied. + ctx, done, err := tempLease(ctx, i.leases) + if err != nil { + return err + } + defer done(ctx) + + cs := &contentStoreForPull{ + ContentStore: i.content, + leases: i.leases, + } + imageStore := &imageStoreForPull{ + ImageConfigStore: distribution.NewImageConfigStoreFromStore(i.imageStore), + ingested: cs, + leases: i.leases, + } + imagePullConfig := &distribution.ImagePullConfig{ Config: distribution.Config{ MetaHeaders: metaHeaders, @@ -73,7 +118,7 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference RegistryService: i.registryService, ImageEventLogger: i.LogImageEvent, MetadataStore: i.distributionMetadataStore, - ImageStore: distribution.NewImageConfigStoreFromStore(i.imageStore), + ImageStore: imageStore, ReferenceStore: i.referenceStore, }, DownloadManager: i.downloadManager, @@ -81,46 +126,67 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference Platform: platform, } - err := distribution.Pull(ctx, ref, imagePullConfig) + err = distribution.Pull(ctx, ref, imagePullConfig, cs) close(progressChan) <-writesDone return err } // GetRepository returns a repository from the registry. -func (i *ImageService) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, bool, error) { +func (i *ImageService) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, error) { // get repository info repoInfo, err := i.registryService.ResolveRepository(ref) if err != nil { - return nil, false, err + return nil, errdefs.InvalidParameter(err) } // makes sure name is not empty or `scratch` if err := distribution.ValidateRepoName(repoInfo.Name); err != nil { - return nil, false, errdefs.InvalidParameter(err) + return nil, errdefs.InvalidParameter(err) } // get endpoints endpoints, err := i.registryService.LookupPullEndpoints(reference.Domain(repoInfo.Name)) if err != nil { - return nil, false, err + return nil, err } // retrieve repository var ( - confirmedV2 bool - repository dist.Repository - lastError error + repository dist.Repository + lastError error ) for _, endpoint := range endpoints { - if endpoint.Version == registry.APIVersion1 { - continue - } - - repository, confirmedV2, lastError = distribution.NewV2Repository(ctx, repoInfo, endpoint, nil, authConfig, "pull") - if lastError == nil && confirmedV2 { + repository, lastError = distribution.NewV2Repository(ctx, repoInfo, endpoint, nil, authConfig, "pull") + if lastError == nil { break } } - return repository, confirmedV2, lastError + return repository, lastError +} + +func tempLease(ctx context.Context, mgr leases.Manager) (context.Context, func(context.Context) error, error) { + nop := func(context.Context) error { return nil } + _, ok := leases.FromContext(ctx) + if ok { + return ctx, nop, nil + } + + // Use an expiration that ensures the lease is cleaned up at some point if there is a crash, SIGKILL, etc. + opts := []leases.Opt{ + leases.WithRandomID(), + leases.WithExpiration(24 * time.Hour), + leases.WithLabels(map[string]string{ + "moby.lease/temporary": time.Now().UTC().Format(time.RFC3339Nano), + }), + } + l, err := mgr.Create(ctx, opts...) + if err != nil { + return ctx, nop, errors.Wrap(err, "error creating temporary lease") + } + + ctx = leases.WithLease(ctx, l.ID) + return ctx, func(ctx context.Context) error { + return mgr.Delete(ctx, l) + }, nil } diff --git a/daemon/images/image_push.go b/daemon/images/image_push.go index 4c7be8d2e9749..302ffd8eece1a 100644 --- a/daemon/images/image_push.go +++ b/daemon/images/image_push.go @@ -53,7 +53,7 @@ func (i *ImageService) PushImage(ctx context.Context, image, tag string, metaHea ReferenceStore: i.referenceStore, }, ConfigMediaType: schema2.MediaTypeImageConfig, - LayerStores: distribution.NewLayerProvidersFromStores(i.layerStores), + LayerStores: distribution.NewLayerProvidersFromStore(i.layerStore), TrustKey: i.trustKey, UploadManager: i.uploadManager, } diff --git a/daemon/images/image_tag.go b/daemon/images/image_tag.go index 4693611c3a199..becd2e2df3d77 100644 --- a/daemon/images/image_tag.go +++ b/daemon/images/image_tag.go @@ -8,7 +8,7 @@ import ( // TagImage creates the tag specified by newTag, pointing to the image named // imageName (alternatively, imageName can also be an image ID). func (i *ImageService) TagImage(imageName, repository, tag string) (string, error) { - img, err := i.GetImage(imageName) + img, err := i.GetImage(imageName, nil) if err != nil { return "", err } diff --git a/daemon/images/image_unix.go b/daemon/images/image_unix.go index 3f577271a2585..f28008488ee85 100644 --- a/daemon/images/image_unix.go +++ b/daemon/images/image_unix.go @@ -1,10 +1,9 @@ +//go:build linux || freebsd // +build linux freebsd package images // import "github.com/docker/docker/daemon/images" import ( - "runtime" - "github.com/sirupsen/logrus" ) @@ -17,17 +16,17 @@ func (i *ImageService) GetContainerLayerSize(containerID string) (int64, int64) // Safe to index by runtime.GOOS as Unix hosts don't support multiple // container operating systems. - rwlayer, err := i.layerStores[runtime.GOOS].GetRWLayer(containerID) + rwlayer, err := i.layerStore.GetRWLayer(containerID) if err != nil { logrus.Errorf("Failed to compute size of container rootfs %v: %v", containerID, err) return sizeRw, sizeRootfs } - defer i.layerStores[runtime.GOOS].ReleaseRWLayer(rwlayer) + defer i.layerStore.ReleaseRWLayer(rwlayer) sizeRw, err = rwlayer.Size() if err != nil { logrus.Errorf("Driver %s couldn't return diff size of container %s: %s", - i.layerStores[runtime.GOOS].DriverName(), containerID, err) + i.layerStore.DriverName(), containerID, err) // FIXME: GetSize should return an error. Not changing it now in case // there is a side-effect. sizeRw = -1 diff --git a/daemon/images/image_windows.go b/daemon/images/image_windows.go index 6f4be49736701..035d7b7139a45 100644 --- a/daemon/images/image_windows.go +++ b/daemon/images/image_windows.go @@ -23,9 +23,9 @@ func (i *ImageService) GetLayerFolders(img *image.Image, rwLayer layer.RWLayer) if !system.IsOSSupported(img.OperatingSystem()) { return nil, errors.Wrapf(system.ErrNotSupportedOperatingSystem, "cannot get layerpath for ImageID %s", img.RootFS.ChainID()) } - layerPath, err := layer.GetLayerPath(i.layerStores[img.OperatingSystem()], img.RootFS.ChainID()) + layerPath, err := layer.GetLayerPath(i.layerStore, img.RootFS.ChainID()) if err != nil { - return nil, errors.Wrapf(err, "failed to get layer path from graphdriver %s for ImageID %s", i.layerStores[img.OperatingSystem()], img.RootFS.ChainID()) + return nil, errors.Wrapf(err, "failed to get layer path from graphdriver %s for ImageID %s", i.layerStore, img.RootFS.ChainID()) } // Reverse order, expecting parent first folders = append([]string{layerPath}, folders...) diff --git a/daemon/images/images.go b/daemon/images/images.go index d1ca978f3b131..e756bc334d399 100644 --- a/daemon/images/images.go +++ b/daemon/images/images.go @@ -1,6 +1,7 @@ package images // import "github.com/docker/docker/daemon/images" import ( + "context" "encoding/json" "fmt" "sort" @@ -10,7 +11,6 @@ import ( "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" "github.com/docker/docker/container" "github.com/docker/docker/image" "github.com/docker/docker/layer" @@ -38,59 +38,54 @@ func (i *ImageService) Map() map[image.ID]*image.Image { return i.imageStore.Map() } -// Images returns a filtered list of images. filterArgs is a JSON-encoded set -// of filter arguments which will be interpreted by api/types/filters. -// filter is a shell glob string applied to repository names. The argument -// named all controls whether all images in the graph are filtered, or just -// the heads. -func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) { - var ( - allImages map[image.ID]*image.Image - err error - danglingOnly = false - ) - - if err := imageFilters.Validate(acceptedImageFilterTags); err != nil { +// Images returns a filtered list of images. +func (i *ImageService) Images(_ context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error) { + if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil { return nil, err } - if imageFilters.Contains("dangling") { - if imageFilters.ExactMatch("dangling", "true") { + var danglingOnly bool + if opts.Filters.Contains("dangling") { + if opts.Filters.ExactMatch("dangling", "true") { danglingOnly = true - } else if !imageFilters.ExactMatch("dangling", "false") { - return nil, invalidFilter{"dangling", imageFilters.Get("dangling")} + } else if !opts.Filters.ExactMatch("dangling", "false") { + return nil, invalidFilter{"dangling", opts.Filters.Get("dangling")} } } - if danglingOnly { - allImages = i.imageStore.Heads() - } else { - allImages = i.imageStore.Map() - } - var beforeFilter, sinceFilter *image.Image - err = imageFilters.WalkValues("before", func(value string) error { - beforeFilter, err = i.GetImage(value) + var ( + beforeFilter, sinceFilter *image.Image + err error + ) + err = opts.Filters.WalkValues("before", func(value string) error { + beforeFilter, err = i.GetImage(value, nil) return err }) if err != nil { return nil, err } - err = imageFilters.WalkValues("since", func(value string) error { - sinceFilter, err = i.GetImage(value) + err = opts.Filters.WalkValues("since", func(value string) error { + sinceFilter, err = i.GetImage(value, nil) return err }) if err != nil { return nil, err } - images := []*types.ImageSummary{} - var imagesMap map[*image.Image]*types.ImageSummary - var layerRefs map[layer.ChainID]int - var allLayers map[layer.ChainID]layer.Layer - var allContainers []*container.Container + var selectedImages map[image.ID]*image.Image + if danglingOnly { + selectedImages = i.imageStore.Heads() + } else { + selectedImages = i.imageStore.Map() + } - for id, img := range allImages { + var ( + summaries = make([]*types.ImageSummary, 0, len(selectedImages)) + summaryMap map[*image.Image]*types.ImageSummary + allContainers []*container.Container + ) + for id, img := range selectedImages { if beforeFilter != nil { if img.Created.Equal(beforeFilter.Created) || img.Created.After(beforeFilter.Created) { continue @@ -103,13 +98,13 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr } } - if imageFilters.Contains("label") { + if opts.Filters.Contains("label") { // Very old image that do not have image.Config (or even labels) if img.Config == nil { continue } // We are now sure image.Config is not nil - if !imageFilters.MatchKVList("label", img.Config.Labels) { + if !opts.Filters.MatchKVList("label", img.Config.Labels) { continue } } @@ -121,10 +116,9 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr continue } - layerID := img.RootFS.ChainID() var size int64 - if layerID != "" { - l, err := i.layerStores[img.OperatingSystem()].Get(layerID) + if layerID := img.RootFS.ChainID(); layerID != "" { + l, err := i.layerStore.Get(layerID) if err != nil { // The layer may have been deleted between the call to `Map()` or // `Heads()` and the call to `Get()`, so we just ignore this error @@ -135,122 +129,134 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr } size, err = l.Size() - layer.ReleaseAndLog(i.layerStores[img.OperatingSystem()], l) + layer.ReleaseAndLog(i.layerStore, l) if err != nil { return nil, err } } - newImage := newImage(img, size) + summary := newImageSummary(img, size) for _, ref := range i.referenceStore.References(id.Digest()) { - if imageFilters.Contains("reference") { + if opts.Filters.Contains("reference") { var found bool var matchErr error - for _, pattern := range imageFilters.Get("reference") { + for _, pattern := range opts.Filters.Get("reference") { found, matchErr = reference.FamiliarMatch(pattern, ref) if matchErr != nil { return nil, matchErr } + if found { + break + } } if !found { continue } } if _, ok := ref.(reference.Canonical); ok { - newImage.RepoDigests = append(newImage.RepoDigests, reference.FamiliarString(ref)) + summary.RepoDigests = append(summary.RepoDigests, reference.FamiliarString(ref)) } if _, ok := ref.(reference.NamedTagged); ok { - newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref)) + summary.RepoTags = append(summary.RepoTags, reference.FamiliarString(ref)) } } - if newImage.RepoDigests == nil && newImage.RepoTags == nil { - if all || len(i.imageStore.Children(id)) == 0 { + if summary.RepoDigests == nil && summary.RepoTags == nil { + if opts.All || len(i.imageStore.Children(id)) == 0 { - if imageFilters.Contains("dangling") && !danglingOnly { - //dangling=false case, so dangling image is not needed + if opts.Filters.Contains("dangling") && !danglingOnly { + // dangling=false case, so dangling image is not needed continue } - if imageFilters.Contains("reference") { // skip images with no references if filtering by reference + if opts.Filters.Contains("reference") { // skip images with no references if filtering by reference continue } - newImage.RepoDigests = []string{"@"} - newImage.RepoTags = []string{":"} + summary.RepoDigests = []string{"@"} + summary.RepoTags = []string{":"} } else { continue } - } else if danglingOnly && len(newImage.RepoTags) > 0 { + } else if danglingOnly && len(summary.RepoTags) > 0 { continue } - if withExtraAttrs { - // lazily init variables - if imagesMap == nil { + if opts.ContainerCount { + // Lazily init allContainers. + if allContainers == nil { allContainers = i.containers.List() - - // allLayers is built from all layerstores combined - allLayers = make(map[layer.ChainID]layer.Layer) - for _, ls := range i.layerStores { - layers := ls.Map() - for k, v := range layers { - allLayers[k] = v - } - } - imagesMap = make(map[*image.Image]*types.ImageSummary) - layerRefs = make(map[layer.ChainID]int) } // Get container count - newImage.Containers = 0 + var containers int64 for _, c := range allContainers { if c.ImageID == id { - newImage.Containers++ + containers++ } } + // NOTE: By default, Containers is -1, or "not set" + summary.Containers = containers + } + + if opts.ContainerCount || opts.SharedSize { + // Lazily init summaryMap. + if summaryMap == nil { + summaryMap = make(map[*image.Image]*types.ImageSummary, len(selectedImages)) + } + summaryMap[img] = summary + } + summaries = append(summaries, summary) + } - // count layer references + if opts.SharedSize { + allLayers := i.layerStore.Map() + layerRefs := make(map[layer.ChainID]int, len(allLayers)) + + allImages := selectedImages + if danglingOnly { + // If danglingOnly is true, then selectedImages include only dangling images, + // but we need to consider all existing images to correctly perform reference counting. + // If danglingOnly is false, selectedImages (and, hence, allImages) is already equal to i.imageStore.Map() + // and we can avoid performing an otherwise redundant method call. + allImages = i.imageStore.Map() + } + // Count layer references across all known images + for _, img := range allImages { rootFS := *img.RootFS rootFS.DiffIDs = nil for _, id := range img.RootFS.DiffIDs { rootFS.Append(id) - chid := rootFS.ChainID() - layerRefs[chid]++ - if _, ok := allLayers[chid]; !ok { - return nil, fmt.Errorf("layer %v was not found (corruption?)", chid) - } + layerRefs[rootFS.ChainID()]++ } - imagesMap[img] = newImage } - images = append(images, newImage) - } - - if withExtraAttrs { // Get Shared sizes - for img, newImage := range imagesMap { + for img, summary := range summaryMap { rootFS := *img.RootFS rootFS.DiffIDs = nil - newImage.SharedSize = 0 + // Indicate that we collected shared size information (default is -1, or "not set") + summary.SharedSize = 0 for _, id := range img.RootFS.DiffIDs { rootFS.Append(id) chid := rootFS.ChainID() - diffSize, err := allLayers[chid].DiffSize() - if err != nil { - return nil, err - } - if layerRefs[chid] > 1 { - newImage.SharedSize += diffSize + if _, ok := allLayers[chid]; !ok { + return nil, fmt.Errorf("layer %v was not found (corruption?)", chid) + } + diffSize, err := allLayers[chid].DiffSize() + if err != nil { + return nil, err + } + summary.SharedSize += diffSize } } } } - sort.Sort(sort.Reverse(byCreated(images))) + sort.Sort(sort.Reverse(byCreated(summaries))) - return images, nil + return summaries, nil } // SquashImage creates a new image with the diff of the specified image and the specified parent. @@ -282,11 +288,11 @@ func (i *ImageService) SquashImage(id, parent string) (string, error) { if !system.IsOSSupported(img.OperatingSystem()) { return "", errors.Wrap(err, system.ErrNotSupportedOperatingSystem.Error()) } - l, err := i.layerStores[img.OperatingSystem()].Get(img.RootFS.ChainID()) + l, err := i.layerStore.Get(img.RootFS.ChainID()) if err != nil { return "", errors.Wrap(err, "error getting image layer") } - defer i.layerStores[img.OperatingSystem()].Release(l) + defer i.layerStore.Release(l) ts, err := l.TarStreamFrom(parentChainID) if err != nil { @@ -294,11 +300,11 @@ func (i *ImageService) SquashImage(id, parent string) (string, error) { } defer ts.Close() - newL, err := i.layerStores[img.OperatingSystem()].Register(ts, parentChainID) + newL, err := i.layerStore.Register(ts, parentChainID) if err != nil { return "", errors.Wrap(err, "error registering layer") } - defer i.layerStores[img.OperatingSystem()].Release(newL) + defer i.layerStore.Release(newL) newImage := *img newImage.RootFS = nil @@ -340,17 +346,22 @@ func (i *ImageService) SquashImage(id, parent string) (string, error) { return string(newImgID), nil } -func newImage(image *image.Image, size int64) *types.ImageSummary { - newImage := new(types.ImageSummary) - newImage.ParentID = image.Parent.String() - newImage.ID = image.ID().String() - newImage.Created = image.Created.Unix() - newImage.Size = size - newImage.VirtualSize = size - newImage.SharedSize = -1 - newImage.Containers = -1 +func newImageSummary(image *image.Image, size int64) *types.ImageSummary { + summary := &types.ImageSummary{ + ParentID: image.Parent.String(), + ID: image.ID().String(), + Created: image.Created.Unix(), + Size: size, + VirtualSize: size, + // -1 indicates that the value has not been set (avoids ambiguity + // between 0 (default) and "not set". We cannot use a pointer (nil) + // for this, as the JSON representation uses "omitempty", which would + // consider both "0" and "nil" to be "empty". + SharedSize: -1, + Containers: -1, + } if image.Config != nil { - newImage.Labels = image.Config.Labels + summary.Labels = image.Config.Labels } - return newImage + return summary } diff --git a/daemon/images/images_test.go b/daemon/images/images_test.go new file mode 100644 index 0000000000000..2608c0b4ed819 --- /dev/null +++ b/daemon/images/images_test.go @@ -0,0 +1,33 @@ +package images + +import ( + "testing" + + specs "github.com/opencontainers/image-spec/specs-go/v1" + "gotest.tools/v3/assert" +) + +func TestOnlyPlatformWithFallback(t *testing.T) { + p := specs.Platform{ + OS: "linux", + Architecture: "arm", + Variant: "v8", + } + + // Check no variant + assert.Assert(t, OnlyPlatformWithFallback(p).Match(specs.Platform{ + OS: p.OS, + Architecture: p.Architecture, + })) + // check with variant + assert.Assert(t, OnlyPlatformWithFallback(p).Match(specs.Platform{ + OS: p.OS, + Architecture: p.Architecture, + Variant: p.Variant, + })) + // Make sure non-matches are false. + assert.Assert(t, !OnlyPlatformWithFallback(p).Match(specs.Platform{ + OS: p.OS, + Architecture: "amd64", + })) +} diff --git a/daemon/images/locals.go b/daemon/images/locals.go index 5ffc460a09e9a..a57ea2da60957 100644 --- a/daemon/images/locals.go +++ b/daemon/images/locals.go @@ -3,7 +3,7 @@ package images // import "github.com/docker/docker/daemon/images" import ( "fmt" - "github.com/docker/go-metrics" + metrics "github.com/docker/go-metrics" ) type invalidFilter struct { diff --git a/daemon/images/service.go b/daemon/images/service.go index e8df5cb649858..85c3bd5d407ea 100644 --- a/daemon/images/service.go +++ b/daemon/images/service.go @@ -2,9 +2,13 @@ package images // import "github.com/docker/docker/daemon/images" import ( "context" + "fmt" "os" - "runtime" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/leases" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/container" daemonevents "github.com/docker/docker/daemon/events" "github.com/docker/docker/distribution" @@ -15,9 +19,10 @@ import ( dockerreference "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/libtrust" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/sync/singleflight" ) type containerStore interface { @@ -35,29 +40,37 @@ type ImageServiceConfig struct { DistributionMetadataStore metadata.Store EventsService *daemonevents.Events ImageStore image.Store - LayerStores map[string]layer.Store + LayerStore layer.Store MaxConcurrentDownloads int MaxConcurrentUploads int + MaxDownloadAttempts int ReferenceStore dockerreference.Store RegistryService registry.Service TrustKey libtrust.PrivateKey + ContentStore content.Store + Leases leases.Manager + ContentNamespace string } // NewImageService returns a new ImageService from a configuration func NewImageService(config ImageServiceConfig) *ImageService { logrus.Debugf("Max Concurrent Downloads: %d", config.MaxConcurrentDownloads) logrus.Debugf("Max Concurrent Uploads: %d", config.MaxConcurrentUploads) + logrus.Debugf("Max Download Attempts: %d", config.MaxDownloadAttempts) return &ImageService{ containers: config.ContainerStore, distributionMetadataStore: config.DistributionMetadataStore, - downloadManager: xfer.NewLayerDownloadManager(config.LayerStores, config.MaxConcurrentDownloads), + downloadManager: xfer.NewLayerDownloadManager(config.LayerStore, config.MaxConcurrentDownloads, xfer.WithMaxDownloadAttempts(config.MaxDownloadAttempts)), eventsService: config.EventsService, - imageStore: config.ImageStore, - layerStores: config.LayerStores, + imageStore: &imageStoreWithLease{Store: config.ImageStore, leases: config.Leases, ns: config.ContentNamespace}, + layerStore: config.LayerStore, referenceStore: config.ReferenceStore, registryService: config.RegistryService, trustKey: config.TrustKey, uploadManager: xfer.NewLayerUploadManager(config.MaxConcurrentUploads), + leases: config.Leases, + content: config.ContentStore, + contentNamespace: config.ContentNamespace, } } @@ -68,19 +81,23 @@ type ImageService struct { downloadManager *xfer.LayerDownloadManager eventsService *daemonevents.Events imageStore image.Store - layerStores map[string]layer.Store // By operating system + layerStore layer.Store pruneRunning int32 referenceStore dockerreference.Store registryService registry.Service trustKey libtrust.PrivateKey uploadManager *xfer.LayerUploadManager + leases leases.Manager + content content.Store + contentNamespace string + usage singleflight.Group } // DistributionServices provides daemon image storage services type DistributionServices struct { DownloadManager distribution.RootFSDownloadManager V2MetadataService metadata.V2MetadataService - LayerStore layer.Store // TODO: lcow + LayerStore layer.Store ImageStore image.Store ReferenceStore dockerreference.Store } @@ -90,7 +107,7 @@ func (i *ImageService) DistributionServices() DistributionServices { return DistributionServices{ DownloadManager: i.downloadManager, V2MetadataService: metadata.NewV2MetadataService(i.distributionMetadataStore), - LayerStore: i.layerStores[runtime.GOOS], + LayerStore: i.layerStore, ImageStore: i.imageStore, ReferenceStore: i.referenceStore, } @@ -130,61 +147,52 @@ func (i *ImageService) CreateLayer(container *container.Container, initFunc laye // Indexing by OS is safe here as validation of OS has already been performed in create() (the only // caller), and guaranteed non-nil - return i.layerStores[container.OS].CreateRWLayer(container.ID, layerID, rwLayerOpts) + return i.layerStore.CreateRWLayer(container.ID, layerID, rwLayerOpts) } -// GetLayerByID returns a layer by ID and operating system +// GetLayerByID returns a layer by ID // called from daemon.go Daemon.restore(), and Daemon.containerExport() -func (i *ImageService) GetLayerByID(cid string, os string) (layer.RWLayer, error) { - return i.layerStores[os].GetRWLayer(cid) +func (i *ImageService) GetLayerByID(cid string) (layer.RWLayer, error) { + return i.layerStore.GetRWLayer(cid) } // LayerStoreStatus returns the status for each layer store // called from info.go -func (i *ImageService) LayerStoreStatus() map[string][][2]string { - result := make(map[string][][2]string) - for os, store := range i.layerStores { - result[os] = store.DriverStatus() - } - return result +func (i *ImageService) LayerStoreStatus() [][2]string { + return i.layerStore.DriverStatus() } // GetLayerMountID returns the mount ID for a layer // called from daemon.go Daemon.Shutdown(), and Daemon.Cleanup() (cleanup is actually continerCleanup) -// TODO: needs to be refactored to Unmount (see callers), or removed and replaced -// with GetLayerByID -func (i *ImageService) GetLayerMountID(cid string, os string) (string, error) { - return i.layerStores[os].GetMountID(cid) +// TODO: needs to be refactored to Unmount (see callers), or removed and replaced with GetLayerByID +func (i *ImageService) GetLayerMountID(cid string) (string, error) { + return i.layerStore.GetMountID(cid) } // Cleanup resources before the process is shutdown. // called from daemon.go Daemon.Shutdown() func (i *ImageService) Cleanup() { - for os, ls := range i.layerStores { - if ls != nil { - if err := ls.Cleanup(); err != nil { - logrus.Errorf("Error during layer Store.Cleanup(): %v %s", err, os) - } - } + if err := i.layerStore.Cleanup(); err != nil { + logrus.Errorf("Error during layer Store.Cleanup(): %v", err) } } -// GraphDriverForOS returns the name of the graph drvier +// GraphDriverName returns the name of the graph drvier // moved from Daemon.GraphDriverName, used by: // - newContainer // - to report an error in Daemon.Mount(container) -func (i *ImageService) GraphDriverForOS(os string) string { - return i.layerStores[os].DriverName() +func (i *ImageService) GraphDriverName() string { + return i.layerStore.DriverName() } // ReleaseLayer releases a layer allowing it to be removed // called from delete.go Daemon.cleanupContainer(), and Daemon.containerExport() func (i *ImageService) ReleaseLayer(rwlayer layer.RWLayer, containerOS string) error { - metadata, err := i.layerStores[containerOS].ReleaseRWLayer(rwlayer) + metadata, err := i.layerStore.ReleaseRWLayer(rwlayer) layer.LogReleaseMetadata(metadata) - if err != nil && err != layer.ErrMountDoesNotExist && !os.IsNotExist(errors.Cause(err)) { + if err != nil && !errors.Is(err, layer.ErrMountDoesNotExist) && !errors.Is(err, os.ErrNotExist) { return errors.Wrapf(err, "driver %q failed to remove root filesystem", - i.layerStores[containerOS].DriverName()) + i.layerStore.DriverName()) } return nil } @@ -192,10 +200,10 @@ func (i *ImageService) ReleaseLayer(rwlayer layer.RWLayer, containerOS string) e // LayerDiskUsage returns the number of bytes used by layer stores // called from disk_usage.go func (i *ImageService) LayerDiskUsage(ctx context.Context) (int64, error) { - var allLayersSize int64 - layerRefs := i.getLayerRefs() - for _, ls := range i.layerStores { - allLayers := ls.Map() + ch := i.usage.DoChan("LayerDiskUsage", func() (interface{}, error) { + var allLayersSize int64 + layerRefs := i.getLayerRefs() + allLayers := i.layerStore.Map() for _, l := range allLayers { select { case <-ctx.Done(): @@ -211,8 +219,17 @@ func (i *ImageService) LayerDiskUsage(ctx context.Context) (int64, error) { } } } + return allLayersSize, nil + }) + select { + case <-ctx.Done(): + return 0, ctx.Err() + case res := <-ch: + if res.Err != nil { + return 0, res.Err + } + return res.Val.(int64), nil } - return allLayersSize, nil } func (i *ImageService) getLayerRefs() map[layer.ChainID]int { @@ -236,6 +253,31 @@ func (i *ImageService) getLayerRefs() map[layer.ChainID]int { return layerRefs } +// ImageDiskUsage returns information about image data disk usage. +func (i *ImageService) ImageDiskUsage(ctx context.Context) ([]*types.ImageSummary, error) { + ch := i.usage.DoChan("ImageDiskUsage", func() (interface{}, error) { + // Get all top images with extra attributes + images, err := i.Images(ctx, types.ImageListOptions{ + Filters: filters.NewArgs(), + SharedSize: true, + ContainerCount: true, + }) + if err != nil { + return nil, fmt.Errorf("failed to retrieve image list: %v", err) + } + return images, nil + }) + select { + case <-ctx.Done(): + return nil, ctx.Err() + case res := <-ch: + if res.Err != nil { + return nil, res.Err + } + return res.Val.([]*types.ImageSummary), nil + } +} + // UpdateConfig values // // called from reload.go diff --git a/daemon/images/store.go b/daemon/images/store.go new file mode 100644 index 0000000000000..2c8d481c50a95 --- /dev/null +++ b/daemon/images/store.go @@ -0,0 +1,155 @@ +package images + +import ( + "context" + "sync" + + "github.com/containerd/containerd/content" + c8derrdefs "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/leases" + "github.com/containerd/containerd/log" + "github.com/containerd/containerd/namespaces" + "github.com/docker/docker/distribution" + "github.com/docker/docker/image" + "github.com/docker/docker/layer" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func imageKey(dgst digest.Digest) string { + return "moby-image-" + dgst.String() +} + +// imageStoreWithLease wraps the configured image store with one that deletes the lease +// reigstered for a given image ID, if one exists +// +// This is used by the main image service to wrap delete calls to the real image store. +type imageStoreWithLease struct { + image.Store + leases leases.Manager + + // Normally we'd pass namespace down through a context.Context, however... + // The interface for image store doesn't allow this, so we store it here. + ns string +} + +func (s *imageStoreWithLease) Delete(id image.ID) ([]layer.Metadata, error) { + ctx := namespaces.WithNamespace(context.TODO(), s.ns) + if err := s.leases.Delete(ctx, leases.Lease{ID: imageKey(digest.Digest(id))}); err != nil && !c8derrdefs.IsNotFound(err) { + return nil, errors.Wrap(err, "error deleting lease") + } + return s.Store.Delete(id) +} + +// iamgeStoreForPull is created for each pull It wraps an underlying image store +// to handle registering leases for content fetched in a single image pull. +type imageStoreForPull struct { + distribution.ImageConfigStore + leases leases.Manager + ingested *contentStoreForPull +} + +func (s *imageStoreForPull) Put(ctx context.Context, config []byte) (digest.Digest, error) { + id, err := s.ImageConfigStore.Put(ctx, config) + if err != nil { + return "", err + } + return id, s.updateLease(ctx, id) +} + +func (s *imageStoreForPull) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) { + id, err := s.ImageConfigStore.Get(ctx, dgst) + if err != nil { + return nil, err + } + return id, s.updateLease(ctx, dgst) +} + +func (s *imageStoreForPull) updateLease(ctx context.Context, dgst digest.Digest) error { + leaseID := imageKey(dgst) + lease, err := s.leases.Create(ctx, leases.WithID(leaseID)) + if err != nil { + if !c8derrdefs.IsAlreadyExists(err) { + return errors.Wrap(err, "error creating lease") + } + lease = leases.Lease{ID: leaseID} + } + + digested := s.ingested.getDigested() + resource := leases.Resource{ + Type: "content", + } + for _, dgst := range digested { + log.G(ctx).WithFields(logrus.Fields{ + "digest": dgst, + "lease": lease.ID, + }).Debug("Adding content digest to lease") + + resource.ID = dgst.String() + if err := s.leases.AddResource(ctx, lease, resource); err != nil { + return errors.Wrapf(err, "error adding content digest to lease: %s", dgst) + } + } + return nil +} + +// contentStoreForPull is used to wrap the configured content store to +// add lease management for a single `pull` +// It stores all committed digests so that `imageStoreForPull` can add +// the digsted resources to the lease for an image. +type contentStoreForPull struct { + distribution.ContentStore + leases leases.Manager + + mu sync.Mutex + digested []digest.Digest +} + +func (c *contentStoreForPull) addDigested(dgst digest.Digest) { + c.mu.Lock() + c.digested = append(c.digested, dgst) + c.mu.Unlock() +} + +func (c *contentStoreForPull) getDigested() []digest.Digest { + c.mu.Lock() + digested := make([]digest.Digest, len(c.digested)) + copy(digested, c.digested) + c.mu.Unlock() + return digested +} + +func (c *contentStoreForPull) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) { + w, err := c.ContentStore.Writer(ctx, opts...) + if err != nil { + if c8derrdefs.IsAlreadyExists(err) { + var cfg content.WriterOpts + for _, o := range opts { + if err := o(&cfg); err != nil { + return nil, err + } + + } + c.addDigested(cfg.Desc.Digest) + } + return nil, err + } + return &contentWriter{ + cs: c, + Writer: w, + }, nil +} + +type contentWriter struct { + cs *contentStoreForPull + content.Writer +} + +func (w *contentWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error { + err := w.Writer.Commit(ctx, size, expected, opts...) + if err == nil || c8derrdefs.IsAlreadyExists(err) { + w.cs.addDigested(expected) + } + return err +} diff --git a/daemon/images/store_test.go b/daemon/images/store_test.go new file mode 100644 index 0000000000000..cbb723b4a23b8 --- /dev/null +++ b/daemon/images/store_test.go @@ -0,0 +1,123 @@ +package images + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/content/local" + c8derrdefs "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/leases" + "github.com/containerd/containerd/metadata" + "github.com/containerd/containerd/namespaces" + "github.com/docker/docker/image" + digest "github.com/opencontainers/go-digest" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "go.etcd.io/bbolt" + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" +) + +func setupTestStores(t *testing.T) (context.Context, content.Store, *imageStoreWithLease, func(t *testing.T)) { + dir, err := os.MkdirTemp("", t.Name()) + assert.NilError(t, err) + + backend, err := image.NewFSStoreBackend(filepath.Join(dir, "images")) + assert.NilError(t, err) + is, err := image.NewImageStore(backend, nil) + assert.NilError(t, err) + + db, err := bbolt.Open(filepath.Join(dir, "metadata.db"), 0600, nil) + assert.NilError(t, err) + + cs, err := local.NewStore(filepath.Join(dir, "content")) + assert.NilError(t, err) + mdb := metadata.NewDB(db, cs, nil) + + cleanup := func(t *testing.T) { + assert.Check(t, db.Close()) + assert.Check(t, os.RemoveAll(dir)) + } + ctx := namespaces.WithNamespace(context.Background(), t.Name()) + images := &imageStoreWithLease{Store: is, ns: t.Name(), leases: metadata.NewLeaseManager(mdb)} + + return ctx, cs, images, cleanup +} + +func TestImageDelete(t *testing.T) { + ctx, _, images, cleanup := setupTestStores(t) + defer cleanup(t) + + t.Run("no lease", func(t *testing.T) { + id, err := images.Create([]byte(`{"rootFS": {}}`)) + assert.NilError(t, err) + defer images.Delete(id) + + ls, err := images.leases.List(ctx) + assert.NilError(t, err) + assert.Equal(t, len(ls), 0, ls) + + _, err = images.Delete(id) + assert.NilError(t, err, "should not error when there is no lease") + }) + + t.Run("lease exists", func(t *testing.T) { + id, err := images.Create([]byte(`{"rootFS": {}}`)) + assert.NilError(t, err) + defer images.Delete(id) + + leaseID := imageKey(digest.Digest(id)) + _, err = images.leases.Create(ctx, leases.WithID(leaseID)) + assert.NilError(t, err) + defer images.leases.Delete(ctx, leases.Lease{ID: leaseID}) + + ls, err := images.leases.List(ctx) + assert.NilError(t, err) + assert.Check(t, cmp.Equal(len(ls), 1), ls) + + _, err = images.Delete(id) + assert.NilError(t, err) + + ls, err = images.leases.List(ctx) + assert.NilError(t, err) + assert.Check(t, cmp.Equal(len(ls), 0), ls) + }) +} + +func TestContentStoreForPull(t *testing.T) { + ctx, cs, is, cleanup := setupTestStores(t) + defer cleanup(t) + + csP := &contentStoreForPull{ + ContentStore: cs, + leases: is.leases, + } + + data := []byte(`{}`) + desc := v1.Descriptor{ + Digest: digest.Canonical.FromBytes(data), + Size: int64(len(data)), + } + + w, err := csP.Writer(ctx, content.WithRef(t.Name()), content.WithDescriptor(desc)) + assert.NilError(t, err) + + _, err = w.Write(data) + assert.NilError(t, err) + defer w.Close() + + err = w.Commit(ctx, desc.Size, desc.Digest) + assert.NilError(t, err) + + assert.Equal(t, len(csP.digested), 1) + assert.Check(t, cmp.Equal(csP.digested[0], desc.Digest)) + + // Test already exists + csP.digested = nil + _, err = csP.Writer(ctx, content.WithRef(t.Name()), content.WithDescriptor(desc)) + assert.Check(t, c8derrdefs.IsAlreadyExists(err)) + assert.Equal(t, len(csP.digested), 1) + assert.Check(t, cmp.Equal(csP.digested[0], desc.Digest)) +} diff --git a/daemon/info.go b/daemon/info.go index 603474ab970b2..f6e090063ef87 100644 --- a/daemon/info.go +++ b/daemon/info.go @@ -2,7 +2,6 @@ package daemon // import "github.com/docker/docker/daemon" import ( "fmt" - "net/url" "os" "runtime" "strings" @@ -11,6 +10,7 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/cli/debug" + "github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/dockerversion" "github.com/docker/docker/pkg/fileutils" @@ -20,13 +20,16 @@ import ( "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" "github.com/docker/docker/registry" - "github.com/docker/go-connections/sockets" + metrics "github.com/docker/go-metrics" + "github.com/opencontainers/selinux/go-selinux" "github.com/sirupsen/logrus" ) // SystemInfo returns information about the host server the daemon is running on. -func (daemon *Daemon) SystemInfo() (*types.Info, error) { - sysInfo := sysinfo.New(true) +func (daemon *Daemon) SystemInfo() *types.Info { + defer metrics.StartTimer(hostInfoFunctions.WithValues("system_info"))() + + sysInfo := daemon.RawSysInfo() cRunning, cPaused, cStopped := stateCtr.get() v := &types.Info{ @@ -45,10 +48,10 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { NGoroutines: runtime.NumGoroutine(), SystemTime: time.Now().Format(time.RFC3339Nano), LoggingDriver: daemon.defaultLogConfig.Type, - CgroupDriver: daemon.getCgroupDriver(), NEventsListener: daemon.EventsService.SubscribersCount(), KernelVersion: kernelVersion(), OperatingSystem: operatingSystem(), + OSVersion: osVersion(), IndexServerAddress: registry.IndexServer, OSType: platform.OSType, Architecture: platform.Architecture, @@ -60,15 +63,14 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { Labels: daemon.configStore.Labels, ExperimentalBuild: daemon.configStore.Experimental, ServerVersion: dockerversion.Version, - ClusterStore: daemon.configStore.ClusterStore, - ClusterAdvertise: daemon.configStore.ClusterAdvertise, - HTTPProxy: maskCredentials(sockets.GetProxyEnv("http_proxy")), - HTTPSProxy: maskCredentials(sockets.GetProxyEnv("https_proxy")), - NoProxy: sockets.GetProxyEnv("no_proxy"), + HTTPProxy: config.MaskCredentials(getConfigOrEnv(daemon.configStore.HTTPProxy, "HTTP_PROXY", "http_proxy")), + HTTPSProxy: config.MaskCredentials(getConfigOrEnv(daemon.configStore.HTTPSProxy, "HTTPS_PROXY", "https_proxy")), + NoProxy: getConfigOrEnv(daemon.configStore.NoProxy, "NO_PROXY", "no_proxy"), LiveRestoreEnabled: daemon.configStore.LiveRestoreEnabled, Isolation: daemon.defaultIsolation, } + daemon.fillClusterInfo(v) daemon.fillAPIInfo(v) // Retrieve platform specific info daemon.fillPlatformInfo(v, sysInfo) @@ -76,12 +78,19 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { daemon.fillPluginsInfo(v) daemon.fillSecurityOptions(v, sysInfo) daemon.fillLicense(v) + daemon.fillDefaultAddressPools(v) + + if v.DefaultRuntime == config.LinuxV1RuntimeName { + v.Warnings = append(v.Warnings, fmt.Sprintf("Configured default runtime %q is deprecated and will be removed in the next release.", config.LinuxV1RuntimeName)) + } - return v, nil + return v } // SystemVersion returns version information about the daemon. func (daemon *Daemon) SystemVersion() types.Version { + defer metrics.StartTimer(hostInfoFunctions.WithValues("system_version"))() + kernelVersion := kernelVersion() v := types.Version{ @@ -118,28 +127,28 @@ func (daemon *Daemon) SystemVersion() types.Version { v.Platform.Name = dockerversion.PlatformName + daemon.fillPlatformVersion(&v) return v } +func (daemon *Daemon) fillClusterInfo(v *types.Info) { + v.ClusterAdvertise = daemon.configStore.ClusterAdvertise + v.ClusterStore = daemon.configStore.ClusterStore + + if v.ClusterAdvertise != "" || v.ClusterStore != "" { + v.Warnings = append(v.Warnings, `WARNING: node discovery and overlay networks with an external k/v store (cluster-advertise, + cluster-store, cluster-store-opt) are deprecated and will be removed in a future release.`) + } +} + func (daemon *Daemon) fillDriverInfo(v *types.Info) { - var ds [][2]string - drivers := "" - statuses := daemon.imageService.LayerStoreStatus() - for os, gd := range daemon.graphDrivers { - ds = append(ds, statuses[os]...) - drivers += gd - if len(daemon.graphDrivers) > 1 { - drivers += fmt.Sprintf(" (%s) ", os) - } - switch gd { - case "devicemapper", "overlay": - v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: the %s storage-driver is deprecated, and will be removed in a future release.", gd)) - } + switch daemon.graphDriver { + case "aufs", "devicemapper", "overlay": + v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: the %s storage-driver is deprecated, and will be removed in a future release.", daemon.graphDriver)) } - drivers = strings.TrimSpace(drivers) - v.Driver = drivers - v.DriverStatus = ds + v.Driver = daemon.graphDriver + v.DriverStatus = daemon.imageService.LayerStoreStatus() fillDriverWarnings(v) } @@ -162,18 +171,24 @@ func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInf securityOptions = append(securityOptions, "name=apparmor") } if sysInfo.Seccomp && supportsSeccomp { - profile := daemon.seccompProfilePath - if profile == "" { - profile = "default" + if daemon.seccompProfilePath != config.SeccompProfileDefault { + v.Warnings = append(v.Warnings, "WARNING: daemon is not using the default seccomp profile") } - securityOptions = append(securityOptions, fmt.Sprintf("name=seccomp,profile=%s", profile)) + securityOptions = append(securityOptions, "name=seccomp,profile="+daemon.seccompProfilePath) } - if selinuxEnabled() { + if selinux.GetEnabled() { securityOptions = append(securityOptions, "name=selinux") } if rootIDs := daemon.idMapping.RootPair(); rootIDs.UID != 0 || rootIDs.GID != 0 { securityOptions = append(securityOptions, "name=userns") } + if daemon.Rootless() { + securityOptions = append(securityOptions, "name=rootless") + } + if daemon.cgroupNamespacesEnabled(sysInfo) { + securityOptions = append(securityOptions, "name=cgroupns") + } + v.SecurityOptions = securityOptions } @@ -181,7 +196,7 @@ func (daemon *Daemon) fillAPIInfo(v *types.Info) { const warn string = ` Access to the remote API is equivalent to root access on the host. Refer to the 'Docker daemon attack surface' section in the documentation for - more information: https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface` + more information: https://docs.docker.com/go/attack-surface/` cfg := daemon.configStore for _, host := range cfg.Hosts { @@ -192,17 +207,26 @@ func (daemon *Daemon) fillAPIInfo(v *types.Info) { if proto != "tcp" { continue } - if !cfg.TLS { + if cfg.TLS == nil || !*cfg.TLS { v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: API is accessible on http://%s without encryption.%s", addr, warn)) continue } - if !cfg.TLSVerify { + if cfg.TLSVerify == nil || !*cfg.TLSVerify { v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: API is accessible on https://%s without TLS client verification.%s", addr, warn)) continue } } } +func (daemon *Daemon) fillDefaultAddressPools(v *types.Info) { + for _, pool := range daemon.configStore.DefaultAddressPools.Value() { + v.DefaultAddressPools = append(v.DefaultAddressPools, types.NetworkAddressPool{ + Base: pool.Base, + Size: pool.Size, + }) + } +} + func hostName() string { hostname := "" if hn, err := os.Hostname(); err != nil { @@ -232,8 +256,9 @@ func memInfo() *system.MemInfo { return memInfo } -func operatingSystem() string { - var operatingSystem string +func operatingSystem() (operatingSystem string) { + defer metrics.StartTimer(hostInfoFunctions.WithValues("operating_system"))() + if s, err := operatingsystem.GetOperatingSystem(); err != nil { logrus.Warnf("Could not get operating system name: %v", err) } else { @@ -248,15 +273,33 @@ func operatingSystem() string { operatingSystem += " (containerized)" } } + return operatingSystem } -func maskCredentials(rawURL string) string { - parsedURL, err := url.Parse(rawURL) - if err != nil || parsedURL.User == nil { - return rawURL +func osVersion() (version string) { + defer metrics.StartTimer(hostInfoFunctions.WithValues("os_version"))() + + version, err := operatingsystem.GetOperatingSystemVersion() + if err != nil { + logrus.Warnf("Could not get operating system version: %v", err) + } + + return version +} + +func getEnvAny(names ...string) string { + for _, n := range names { + if val := os.Getenv(n); val != "" { + return val + } + } + return "" +} + +func getConfigOrEnv(config string, env ...string) string { + if config != "" { + return config } - parsedURL.User = url.UserPassword("xxxxx", "xxxxx") - maskedURL := parsedURL.String() - return maskedURL + return getEnvAny(env...) } diff --git a/daemon/info_test.go b/daemon/info_test.go deleted file mode 100644 index 076373cba234e..0000000000000 --- a/daemon/info_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package daemon - -import ( - "testing" - - "gotest.tools/assert" -) - -func TestMaskURLCredentials(t *testing.T) { - tests := []struct { - rawURL string - maskedURL string - }{ - { - rawURL: "", - maskedURL: "", - }, { - rawURL: "invalidURL", - maskedURL: "invalidURL", - }, { - rawURL: "http://proxy.example.com:80/", - maskedURL: "http://proxy.example.com:80/", - }, { - rawURL: "http://USER:PASSWORD@proxy.example.com:80/", - maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", - }, { - rawURL: "http://PASSWORD:PASSWORD@proxy.example.com:80/", - maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", - }, { - rawURL: "http://USER:@proxy.example.com:80/", - maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", - }, { - rawURL: "http://:PASSWORD@proxy.example.com:80/", - maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", - }, { - rawURL: "http://USER@docker:password@proxy.example.com:80/", - maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", - }, { - rawURL: "http://USER%40docker:password@proxy.example.com:80/", - maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", - }, { - rawURL: "http://USER%40docker:pa%3Fsword@proxy.example.com:80/", - maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/", - }, { - rawURL: "http://USER%40docker:pa%3Fsword@proxy.example.com:80/hello%20world", - maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/hello%20world", - }, - } - for _, test := range tests { - maskedURL := maskCredentials(test.rawURL) - assert.Equal(t, maskedURL, test.maskedURL) - } -} diff --git a/daemon/info_unix.go b/daemon/info_unix.go index 60b2f99870b21..be554844d0999 100644 --- a/daemon/info_unix.go +++ b/daemon/info_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" @@ -6,10 +7,11 @@ import ( "context" "fmt" "os/exec" + "path/filepath" "strings" "github.com/docker/docker/api/types" - "github.com/docker/docker/dockerversion" + containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/sysinfo" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -17,88 +19,138 @@ import ( // fillPlatformInfo fills the platform related info. func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) { - v.MemoryLimit = sysInfo.MemoryLimit - v.SwapLimit = sysInfo.SwapLimit - v.KernelMemory = sysInfo.KernelMemory - v.OomKillDisable = sysInfo.OomKillDisable - v.CPUCfsPeriod = sysInfo.CPUCfsPeriod - v.CPUCfsQuota = sysInfo.CPUCfsQuota - v.CPUShares = sysInfo.CPUShares - v.CPUSet = sysInfo.Cpuset + v.CgroupDriver = daemon.getCgroupDriver() + v.CgroupVersion = "1" + if sysInfo.CgroupUnified { + v.CgroupVersion = "2" + } + + if v.CgroupDriver != cgroupNoneDriver { + v.MemoryLimit = sysInfo.MemoryLimit + v.SwapLimit = sysInfo.SwapLimit + v.KernelMemory = sysInfo.KernelMemory + v.KernelMemoryTCP = sysInfo.KernelMemoryTCP + v.OomKillDisable = sysInfo.OomKillDisable + v.CPUCfsPeriod = sysInfo.CPUCfs + v.CPUCfsQuota = sysInfo.CPUCfs + v.CPUShares = sysInfo.CPUShares + v.CPUSet = sysInfo.Cpuset + v.PidsLimit = sysInfo.PidsLimit + } v.Runtimes = daemon.configStore.GetAllRuntimes() v.DefaultRuntime = daemon.configStore.GetDefaultRuntimeName() v.InitBinary = daemon.configStore.GetInitPath() + v.RuncCommit.ID = "N/A" + v.ContainerdCommit.ID = "N/A" + v.InitCommit.ID = "N/A" defaultRuntimeBinary := daemon.configStore.GetRuntime(v.DefaultRuntime).Path if rv, err := exec.Command(defaultRuntimeBinary, "--version").Output(); err == nil { - parts := strings.Split(strings.TrimSpace(string(rv)), "\n") - if len(parts) == 3 { - parts = strings.Split(parts[1], ": ") - if len(parts) == 2 { - v.RuncCommit.ID = strings.TrimSpace(parts[1]) - } - } - - if v.RuncCommit.ID == "" { - logrus.Warnf("failed to retrieve %s version: unknown output format: %s", defaultRuntimeBinary, string(rv)) - v.RuncCommit.ID = "N/A" + if _, _, commit, err := parseRuntimeVersion(string(rv)); err != nil { + logrus.Warnf("failed to parse %s version: %v", defaultRuntimeBinary, err) + } else { + v.RuncCommit.ID = commit } } else { logrus.Warnf("failed to retrieve %s version: %v", defaultRuntimeBinary, err) - v.RuncCommit.ID = "N/A" } - // runc is now shipped as a separate package. Set "expected" to same value - // as "ID" to prevent clients from reporting a version-mismatch - v.RuncCommit.Expected = v.RuncCommit.ID - if rv, err := daemon.containerd.Version(context.Background()); err == nil { v.ContainerdCommit.ID = rv.Revision } else { logrus.Warnf("failed to retrieve containerd version: %v", err) - v.ContainerdCommit.ID = "N/A" } - // containerd is now shipped as a separate package. Set "expected" to same - // value as "ID" to prevent clients from reporting a version-mismatch - v.ContainerdCommit.Expected = v.ContainerdCommit.ID - defaultInitBinary := daemon.configStore.GetInitPath() if rv, err := exec.Command(defaultInitBinary, "--version").Output(); err == nil { - ver, err := parseInitVersion(string(rv)) - - if err != nil { - logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err) + if _, commit, err := parseInitVersion(string(rv)); err != nil { + logrus.Warnf("failed to parse %s version: %s", defaultInitBinary, err) + } else { + v.InitCommit.ID = commit } - v.InitCommit = ver } else { logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err) - v.InitCommit.ID = "N/A" } - if !v.MemoryLimit { - v.Warnings = append(v.Warnings, "WARNING: No memory limit support") - } - if !v.SwapLimit { - v.Warnings = append(v.Warnings, "WARNING: No swap limit support") - } - if !v.KernelMemory { - v.Warnings = append(v.Warnings, "WARNING: No kernel memory limit support") - } - if !v.OomKillDisable { - v.Warnings = append(v.Warnings, "WARNING: No oom kill disable support") - } - if !v.CPUCfsQuota { - v.Warnings = append(v.Warnings, "WARNING: No cpu cfs quota support") - } - if !v.CPUCfsPeriod { - v.Warnings = append(v.Warnings, "WARNING: No cpu cfs period support") - } - if !v.CPUShares { - v.Warnings = append(v.Warnings, "WARNING: No cpu shares support") - } - if !v.CPUSet { - v.Warnings = append(v.Warnings, "WARNING: No cpuset support") + // Set expected and actual commits to the same value to prevent the client + // showing that the version does not match the "expected" version/commit. + v.RuncCommit.Expected = v.RuncCommit.ID + v.ContainerdCommit.Expected = v.ContainerdCommit.ID + v.InitCommit.Expected = v.InitCommit.ID + + if v.CgroupDriver == cgroupNoneDriver { + if v.CgroupVersion == "2" { + v.Warnings = append(v.Warnings, "WARNING: Running in rootless-mode without cgroups. Systemd is required to enable cgroups in rootless-mode.") + } else { + v.Warnings = append(v.Warnings, "WARNING: Running in rootless-mode without cgroups. To enable cgroups in rootless-mode, you need to boot the system in cgroup v2 mode.") + } + } else { + if !v.MemoryLimit { + v.Warnings = append(v.Warnings, "WARNING: No memory limit support") + } + if !v.SwapLimit { + v.Warnings = append(v.Warnings, "WARNING: No swap limit support") + } + if !v.KernelMemoryTCP && v.CgroupVersion == "1" { + // kernel memory is not available for cgroup v2. + // Warning is not printed on cgroup v2, because there is no action user can take. + v.Warnings = append(v.Warnings, "WARNING: No kernel memory TCP limit support") + } + if !v.OomKillDisable && v.CgroupVersion == "1" { + // oom kill disable is not available for cgroup v2. + // Warning is not printed on cgroup v2, because there is no action user can take. + v.Warnings = append(v.Warnings, "WARNING: No oom kill disable support") + } + if !v.CPUCfsQuota { + v.Warnings = append(v.Warnings, "WARNING: No cpu cfs quota support") + } + if !v.CPUCfsPeriod { + v.Warnings = append(v.Warnings, "WARNING: No cpu cfs period support") + } + if !v.CPUShares { + v.Warnings = append(v.Warnings, "WARNING: No cpu shares support") + } + if !v.CPUSet { + v.Warnings = append(v.Warnings, "WARNING: No cpuset support") + } + // TODO add fields for these options in types.Info + if !sysInfo.BlkioWeight && v.CgroupVersion == "2" { + // blkio weight is not available on cgroup v1 since kernel 5.0. + // Warning is not printed on cgroup v1, because there is no action user can take. + // On cgroup v2, blkio weight is implemented using io.weight + v.Warnings = append(v.Warnings, "WARNING: No io.weight support") + } + if !sysInfo.BlkioWeightDevice && v.CgroupVersion == "2" { + v.Warnings = append(v.Warnings, "WARNING: No io.weight (per device) support") + } + if !sysInfo.BlkioReadBpsDevice { + if v.CgroupVersion == "2" { + v.Warnings = append(v.Warnings, "WARNING: No io.max (rbps) support") + } else { + v.Warnings = append(v.Warnings, "WARNING: No blkio throttle.read_bps_device support") + } + } + if !sysInfo.BlkioWriteBpsDevice { + if v.CgroupVersion == "2" { + v.Warnings = append(v.Warnings, "WARNING: No io.max (wbps) support") + } else { + v.Warnings = append(v.Warnings, "WARNING: No blkio throttle.write_bps_device support") + } + } + if !sysInfo.BlkioReadIOpsDevice { + if v.CgroupVersion == "2" { + v.Warnings = append(v.Warnings, "WARNING: No io.max (riops) support") + } else { + v.Warnings = append(v.Warnings, "WARNING: No blkio throttle.read_iops_device support") + } + } + if !sysInfo.BlkioWriteIOpsDevice { + if v.CgroupVersion == "2" { + v.Warnings = append(v.Warnings, "WARNING: No io.max (wiops) support") + } else { + v.Warnings = append(v.Warnings, "WARNING: No blkio throttle.write_iops_device support") + } + } } if !v.IPv4Forwarding { v.Warnings = append(v.Warnings, "WARNING: IPv4 forwarding is disabled") @@ -111,6 +163,53 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) } } +func (daemon *Daemon) fillPlatformVersion(v *types.Version) { + if rv, err := daemon.containerd.Version(context.Background()); err == nil { + v.Components = append(v.Components, types.ComponentVersion{ + Name: "containerd", + Version: rv.Version, + Details: map[string]string{ + "GitCommit": rv.Revision, + }, + }) + } + + defaultRuntime := daemon.configStore.GetDefaultRuntimeName() + defaultRuntimeBinary := daemon.configStore.GetRuntime(defaultRuntime).Path + if rv, err := exec.Command(defaultRuntimeBinary, "--version").Output(); err == nil { + if _, ver, commit, err := parseRuntimeVersion(string(rv)); err != nil { + logrus.Warnf("failed to parse %s version: %v", defaultRuntimeBinary, err) + } else { + v.Components = append(v.Components, types.ComponentVersion{ + Name: defaultRuntime, + Version: ver, + Details: map[string]string{ + "GitCommit": commit, + }, + }) + } + } else { + logrus.Warnf("failed to retrieve %s version: %v", defaultRuntimeBinary, err) + } + + defaultInitBinary := daemon.configStore.GetInitPath() + if rv, err := exec.Command(defaultInitBinary, "--version").Output(); err == nil { + if ver, commit, err := parseInitVersion(string(rv)); err != nil { + logrus.Warnf("failed to parse %s version: %s", defaultInitBinary, err) + } else { + v.Components = append(v.Components, types.ComponentVersion{ + Name: filepath.Base(defaultInitBinary), + Version: ver, + Details: map[string]string{ + "GitCommit": commit, + }, + }) + } + } else { + logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err) + } +} + func fillDriverWarnings(v *types.Info) { for _, pair := range v.DriverStatus { if pair[0] == "Data loop file" { @@ -145,24 +244,64 @@ func getBackingFs(v *types.Info) string { return "" } -// parseInitVersion parses a Tini version string, and extracts the version. -func parseInitVersion(v string) (types.Commit, error) { - version := types.Commit{ID: "", Expected: dockerversion.InitCommitID} - parts := strings.Split(strings.TrimSpace(v), " - ") +// parseInitVersion parses a Tini version string, and extracts the "version" +// and "git commit" from the output. +// +// Output example from `docker-init --version`: +// +// tini version 0.18.0 - git.fec3683 +func parseInitVersion(v string) (version string, commit string, err error) { + parts := strings.Split(v, " - ") if len(parts) >= 2 { - gitParts := strings.Split(parts[1], ".") + gitParts := strings.Split(strings.TrimSpace(parts[1]), ".") if len(gitParts) == 2 && gitParts[0] == "git" { - version.ID = gitParts[1] - version.Expected = dockerversion.InitCommitID[0:len(version.ID)] + commit = gitParts[1] } } - if version.ID == "" && strings.HasPrefix(parts[0], "tini version ") { - version.ID = "v" + strings.TrimPrefix(parts[0], "tini version ") + parts[0] = strings.TrimSpace(parts[0]) + if strings.HasPrefix(parts[0], "tini version ") { + version = strings.TrimPrefix(parts[0], "tini version ") } - if version.ID == "" { - version.ID = "N/A" - return version, errors.Errorf("unknown output format: %s", v) + if version == "" && commit == "" { + err = errors.Errorf("unknown output format: %s", v) } - return version, nil + return version, commit, err +} + +// parseRuntimeVersion parses the output of `[runtime] --version` and extracts the +// "name", "version" and "git commit" from the output. +// +// Output example from `runc --version`: +// +// runc version 1.0.0-rc5+dev +// commit: 69663f0bd4b60df09991c08812a60108003fa340 +// spec: 1.0.0 +func parseRuntimeVersion(v string) (runtime string, version string, commit string, err error) { + lines := strings.Split(strings.TrimSpace(v), "\n") + for _, line := range lines { + if strings.Contains(line, "version") { + s := strings.Split(line, "version") + runtime = strings.TrimSpace(s[0]) + version = strings.TrimSpace(s[len(s)-1]) + continue + } + if strings.HasPrefix(line, "commit:") { + commit = strings.TrimSpace(strings.TrimPrefix(line, "commit:")) + continue + } + } + if version == "" && commit == "" { + err = errors.Errorf("unknown output format: %s", v) + } + return runtime, version, commit, err +} + +func (daemon *Daemon) cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo) bool { + return sysInfo.CgroupNamespaces && containertypes.CgroupnsMode(daemon.configStore.CgroupNamespaceMode).IsPrivate() +} + +// Rootless returns true if daemon is running in rootless mode +func (daemon *Daemon) Rootless() bool { + return daemon.configStore.Rootless } diff --git a/daemon/info_unix_test.go b/daemon/info_unix_test.go index a5a4e06f98451..4dfe6e4efafc6 100644 --- a/daemon/info_unix_test.go +++ b/daemon/info_unix_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" @@ -5,49 +6,127 @@ package daemon // import "github.com/docker/docker/daemon" import ( "testing" - "github.com/docker/docker/api/types" - "github.com/docker/docker/dockerversion" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestParseInitVersion(t *testing.T) { tests := []struct { + output string version string - result types.Commit + commit string invalid bool }{ { - version: "tini version 0.13.0 - git.949e6fa", - result: types.Commit{ID: "949e6fa", Expected: dockerversion.InitCommitID[0:7]}, + output: "tini version 0.13.0 - git.949e6fa", + version: "0.13.0", + commit: "949e6fa", }, { - version: "tini version 0.13.0\n", - result: types.Commit{ID: "v0.13.0", Expected: dockerversion.InitCommitID}, + output: "tini version 0.13.0\n", + version: "0.13.0", }, { - version: "tini version 0.13.2", - result: types.Commit{ID: "v0.13.2", Expected: dockerversion.InitCommitID}, + output: "tini version 0.13.2", + version: "0.13.2", }, { - version: "tini version0.13.2", - result: types.Commit{ID: "N/A", Expected: dockerversion.InitCommitID}, + output: "tini version 0.13.2 - ", + version: "0.13.2", + }, { + output: " - git.949e6fa", + commit: "949e6fa", + }, { + output: "tini version0.13.2", + invalid: true, + }, { + output: "version 0.13.0", invalid: true, }, { - version: "", - result: types.Commit{ID: "N/A", Expected: dockerversion.InitCommitID}, + output: "", invalid: true, }, { - version: "hello world", - result: types.Commit{ID: "N/A", Expected: dockerversion.InitCommitID}, + output: " - ", + invalid: true, + }, { + output: "hello world", + invalid: true, + }, + } + + for _, test := range tests { + test := test + t.Run(test.output, func(t *testing.T) { + version, commit, err := parseInitVersion(test.output) + if test.invalid { + assert.Check(t, is.ErrorContains(err, "")) + } else { + assert.Check(t, err) + } + assert.Equal(t, test.version, version) + assert.Equal(t, test.commit, commit) + }) + } +} + +func TestParseRuntimeVersion(t *testing.T) { + tests := []struct { + output string + runtime string + version string + commit string + invalid bool + }{ + { + output: ` +runc version 1.0.0-rc5+dev +commit: 69663f0bd4b60df09991c08812a60108003fa340 +spec: 1.0.0 +`, + runtime: "runc", + version: "1.0.0-rc5+dev", + commit: "69663f0bd4b60df09991c08812a60108003fa340", + }, + { + output: ` +runc version 1.0.0-rc5+dev +spec: 1.0.0 +`, + runtime: "runc", + version: "1.0.0-rc5+dev", + }, + { + output: ` +commit: 69663f0bd4b60df09991c08812a60108003fa340 +spec: 1.0.0 +`, + commit: "69663f0bd4b60df09991c08812a60108003fa340", + }, + { + output: ` +crun version 0.7 +spec: 1.0.0 ++SYSTEMD +SELINUX +CAP +SECCOMP +EBPF +YAJL +`, + runtime: "crun", + version: "0.7", + }, + { + output: "", + invalid: true, + }, + { + output: "hello world", invalid: true, }, } for _, test := range tests { - ver, err := parseInitVersion(string(test.version)) + runtime, version, commit, err := parseRuntimeVersion(test.output) if test.invalid { assert.Check(t, is.ErrorContains(err, "")) } else { assert.Check(t, err) } - assert.Check(t, is.DeepEqual(test.result, ver)) + assert.Equal(t, test.runtime, runtime) + assert.Equal(t, test.version, version) + assert.Equal(t, test.commit, commit) } } diff --git a/daemon/info_windows.go b/daemon/info_windows.go index 2c1ff460c37f3..49fb32ce213a4 100644 --- a/daemon/info_windows.go +++ b/daemon/info_windows.go @@ -9,5 +9,16 @@ import ( func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) { } +func (daemon *Daemon) fillPlatformVersion(v *types.Version) {} + func fillDriverWarnings(v *types.Info) { } + +func (daemon *Daemon) cgroupNamespacesEnabled(sysInfo *sysinfo.SysInfo) bool { + return false +} + +// Rootless returns true if daemon is running in rootless mode +func (daemon *Daemon) Rootless() bool { + return false +} diff --git a/daemon/initlayer/setup_unix.go b/daemon/initlayer/setup_unix.go index 4af7eafc679a8..1a971897b4a3b 100644 --- a/daemon/initlayer/setup_unix.go +++ b/daemon/initlayer/setup_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package initlayer // import "github.com/docker/docker/daemon/initlayer" diff --git a/daemon/inspect.go b/daemon/inspect.go index 45a21542549d8..17331be232ca1 100644 --- a/daemon/inspect.go +++ b/daemon/inspect.go @@ -32,50 +32,50 @@ func (daemon *Daemon) ContainerInspect(name string, size bool, version string) ( // ContainerInspectCurrent returns low-level information about a // container in a most recent api version. func (daemon *Daemon) ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error) { - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return nil, err } - container.Lock() + ctr.Lock() - base, err := daemon.getInspectData(container) + base, err := daemon.getInspectData(ctr) if err != nil { - container.Unlock() + ctr.Unlock() return nil, err } apiNetworks := make(map[string]*networktypes.EndpointSettings) - for name, epConf := range container.NetworkSettings.Networks { + for name, epConf := range ctr.NetworkSettings.Networks { if epConf.EndpointSettings != nil { // We must make a copy of this pointer object otherwise it can race with other operations apiNetworks[name] = epConf.EndpointSettings.Copy() } } - mountPoints := container.GetMountPoints() + mountPoints := ctr.GetMountPoints() networkSettings := &types.NetworkSettings{ NetworkSettingsBase: types.NetworkSettingsBase{ - Bridge: container.NetworkSettings.Bridge, - SandboxID: container.NetworkSettings.SandboxID, - HairpinMode: container.NetworkSettings.HairpinMode, - LinkLocalIPv6Address: container.NetworkSettings.LinkLocalIPv6Address, - LinkLocalIPv6PrefixLen: container.NetworkSettings.LinkLocalIPv6PrefixLen, - SandboxKey: container.NetworkSettings.SandboxKey, - SecondaryIPAddresses: container.NetworkSettings.SecondaryIPAddresses, - SecondaryIPv6Addresses: container.NetworkSettings.SecondaryIPv6Addresses, + Bridge: ctr.NetworkSettings.Bridge, + SandboxID: ctr.NetworkSettings.SandboxID, + HairpinMode: ctr.NetworkSettings.HairpinMode, + LinkLocalIPv6Address: ctr.NetworkSettings.LinkLocalIPv6Address, + LinkLocalIPv6PrefixLen: ctr.NetworkSettings.LinkLocalIPv6PrefixLen, + SandboxKey: ctr.NetworkSettings.SandboxKey, + SecondaryIPAddresses: ctr.NetworkSettings.SecondaryIPAddresses, + SecondaryIPv6Addresses: ctr.NetworkSettings.SecondaryIPv6Addresses, }, - DefaultNetworkSettings: daemon.getDefaultNetworkSettings(container.NetworkSettings.Networks), + DefaultNetworkSettings: daemon.getDefaultNetworkSettings(ctr.NetworkSettings.Networks), Networks: apiNetworks, } - ports := make(nat.PortMap, len(container.NetworkSettings.Ports)) - for k, pm := range container.NetworkSettings.Ports { + ports := make(nat.PortMap, len(ctr.NetworkSettings.Ports)) + for k, pm := range ctr.NetworkSettings.Ports { ports[k] = pm } networkSettings.NetworkSettingsBase.Ports = ports - container.Unlock() + ctr.Unlock() if size { sizeRw, sizeRootFs := daemon.imageService.GetContainerLayerSize(base.ID) @@ -86,7 +86,7 @@ func (daemon *Daemon) ContainerInspectCurrent(name string, size bool) (*types.Co return &types.ContainerJSON{ ContainerJSONBase: base, Mounts: mountPoints, - Config: container.Config, + Config: ctr.Config, NetworkSettings: networkSettings, }, nil } @@ -214,7 +214,7 @@ func (daemon *Daemon) ContainerExecInspect(id string) (*backend.ExecInspect, err return nil, errExecNotFound(id) } - if container := daemon.containers.Get(e.ContainerID); container == nil { + if ctr := daemon.containers.Get(e.ContainerID); ctr == nil { return nil, errExecNotFound(id) } diff --git a/daemon/inspect_linux.go b/daemon/inspect_linux.go index 77a4c44d794a6..049a7f743f21e 100644 --- a/daemon/inspect_linux.go +++ b/daemon/inspect_linux.go @@ -20,38 +20,38 @@ func setPlatformSpecificContainerFields(container *container.Container, contJSON // containerInspectPre120 gets containers for pre 1.20 APIs. func (daemon *Daemon) containerInspectPre120(name string) (*v1p19.ContainerJSON, error) { - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return nil, err } - container.Lock() - defer container.Unlock() + ctr.Lock() + defer ctr.Unlock() - base, err := daemon.getInspectData(container) + base, err := daemon.getInspectData(ctr) if err != nil { return nil, err } volumes := make(map[string]string) volumesRW := make(map[string]bool) - for _, m := range container.MountPoints { + for _, m := range ctr.MountPoints { volumes[m.Destination] = m.Path() volumesRW[m.Destination] = m.RW } config := &v1p19.ContainerConfig{ - Config: container.Config, - MacAddress: container.Config.MacAddress, - NetworkDisabled: container.Config.NetworkDisabled, - ExposedPorts: container.Config.ExposedPorts, - VolumeDriver: container.HostConfig.VolumeDriver, - Memory: container.HostConfig.Memory, - MemorySwap: container.HostConfig.MemorySwap, - CPUShares: container.HostConfig.CPUShares, - CPUSet: container.HostConfig.CpusetCpus, + Config: ctr.Config, + MacAddress: ctr.Config.MacAddress, + NetworkDisabled: ctr.Config.NetworkDisabled, + ExposedPorts: ctr.Config.ExposedPorts, + VolumeDriver: ctr.HostConfig.VolumeDriver, + Memory: ctr.HostConfig.Memory, + MemorySwap: ctr.HostConfig.MemorySwap, + CPUShares: ctr.HostConfig.CPUShares, + CPUSet: ctr.HostConfig.CpusetCpus, } - networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings) + networkSettings := daemon.getBackwardsCompatibleNetworkSettings(ctr.NetworkSettings) return &v1p19.ContainerJSON{ ContainerJSONBase: base, diff --git a/daemon/inspect_test.go b/daemon/inspect_test.go index f402a7af99b6e..07c026b723290 100644 --- a/daemon/inspect_test.go +++ b/daemon/inspect_test.go @@ -7,8 +7,8 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/exec" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestGetInspectData(t *testing.T) { diff --git a/daemon/keys.go b/daemon/keys.go index 946eaaab1c08f..3a494fef226d8 100644 --- a/daemon/keys.go +++ b/daemon/keys.go @@ -1,10 +1,10 @@ +//go:build linux // +build linux package daemon // import "github.com/docker/docker/daemon" import ( "fmt" - "io/ioutil" "os" "strconv" "strings" @@ -18,10 +18,10 @@ const ( rootKeyByteMultiplier = 25 ) -// ModifyRootKeyLimit checks to see if the root key limit is set to +// modifyRootKeyLimit checks to see if the root key limit is set to // at least 1000000 and changes it to that limit along with the maxbytes // allocated to the keys at a 25 to 1 multiplier. -func ModifyRootKeyLimit() error { +func modifyRootKeyLimit() error { value, err := readRootKeyLimit(rootKeyFile) if err != nil { return err @@ -51,7 +51,7 @@ func setRootKeyLimit(limit int) error { } func readRootKeyLimit(path string) (int, error) { - data, err := ioutil.ReadFile(path) + data, err := os.ReadFile(path) if err != nil { return -1, err } diff --git a/daemon/keys_unsupported.go b/daemon/keys_unsupported.go index 2ccdb576d713a..917f94192cd14 100644 --- a/daemon/keys_unsupported.go +++ b/daemon/keys_unsupported.go @@ -1,8 +1,9 @@ +//go:build !linux // +build !linux package daemon // import "github.com/docker/docker/daemon" -// ModifyRootKeyLimit is a noop on unsupported platforms. -func ModifyRootKeyLimit() error { +// modifyRootKeyLimit is a noop on unsupported platforms. +func modifyRootKeyLimit() error { return nil } diff --git a/daemon/kill.go b/daemon/kill.go index 3e6457e9522dd..98c44abb226ca 100644 --- a/daemon/kill.go +++ b/daemon/kill.go @@ -9,8 +9,8 @@ import ( containerpkg "github.com/docker/docker/container" "github.com/docker/docker/errdefs" - "github.com/docker/docker/libcontainerd" - "github.com/docker/docker/pkg/signal" + libcontainerdtypes "github.com/docker/docker/libcontainerd/types" + "github.com/moby/sys/signal" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -64,8 +64,6 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int) container.Lock() defer container.Unlock() - daemon.stopHealthchecks(container) - if !container.Running { return errNotRunning(container.ID) } @@ -87,6 +85,7 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int) if !daemon.IsShuttingDown() { container.HasBeenManuallyStopped = true + container.CheckpointTo(daemon.containersReplica) } // if the container is currently restarting we do not need to send the signal @@ -100,6 +99,19 @@ func (daemon *Daemon) killWithSignal(container *containerpkg.Container, sig int) if errdefs.IsNotFound(err) { unpause = false logrus.WithError(err).WithField("container", container.ID).WithField("action", "kill").Debug("container kill failed because of 'container not found' or 'no such process'") + go func() { + // We need to clean up this container but it is possible there is a case where we hit here before the exit event is processed + // but after it was fired off. + // So let's wait the container's stop timeout amount of time to see if the event is eventually processed. + // Doing this has the side effect that if no event was ever going to come we are waiting a a longer period of time uneccessarily. + // But this prevents race conditions in processing the container. + ctx, cancel := context.WithTimeout(context.TODO(), time.Duration(container.StopTimeout())*time.Second) + defer cancel() + s := <-container.Wait(ctx, containerpkg.WaitConditionNotRunning) + if s.Err() != nil { + daemon.handleContainerExit(container, nil) + } + }() } else { return errors.Wrapf(err, "Cannot kill container %s", container.ID) } @@ -127,29 +139,22 @@ func (daemon *Daemon) Kill(container *containerpkg.Container) error { // 1. Send SIGKILL if err := daemon.killPossiblyDeadProcess(container, int(syscall.SIGKILL)); err != nil { - // While normally we might "return err" here we're not going to - // because if we can't stop the container by this point then - // it's probably because it's already stopped. Meaning, between - // the time of the IsRunning() call above and now it stopped. - // Also, since the err return will be environment specific we can't - // look for any particular (common) error that would indicate - // that the process is already dead vs something else going wrong. - // So, instead we'll give it up to 2 more seconds to complete and if - // by that time the container is still running, then the error - // we got is probably valid and so we return it to the caller. + // kill failed, check if process is no longer running. if isErrNoSuchProcess(err) { return nil } + } - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() - if status := <-container.Wait(ctx, containerpkg.WaitConditionNotRunning); status.Err() != nil { - return err - } + status := <-container.Wait(ctx, containerpkg.WaitConditionNotRunning) + if status.Err() == nil { + return nil } - // 2. Wait for the process to die, in last resort, try to kill the process directly + logrus.WithError(status.Err()).WithField("container", container.ID).Error("Container failed to exit within 10 seconds of kill - trying direct SIGKILL") + if err := killProcessDirectly(container); err != nil { if isErrNoSuchProcess(err) { return nil @@ -157,10 +162,13 @@ func (daemon *Daemon) Kill(container *containerpkg.Container) error { return err } - // Wait for exit with no timeout. - // Ignore returned status. - <-container.Wait(context.Background(), containerpkg.WaitConditionNotRunning) + // wait for container to exit one last time, if it doesn't then kill didnt work, so return error + ctx2, cancel2 := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel2() + if status := <-container.Wait(ctx2, containerpkg.WaitConditionNotRunning); status.Err() != nil { + return errors.New("tried to kill container, but did not receive an exit event") + } return nil } @@ -176,5 +184,5 @@ func (daemon *Daemon) killPossiblyDeadProcess(container *containerpkg.Container, } func (daemon *Daemon) kill(c *containerpkg.Container, sig int) error { - return daemon.containerd.SignalProcess(context.Background(), c.ID, libcontainerd.InitProcessName, sig) + return daemon.containerd.SignalProcess(context.Background(), c.ID, libcontainerdtypes.InitProcessName, sig) } diff --git a/daemon/licensing_test.go b/daemon/licensing_test.go index 5a97ce8fa2914..902b3c166ebad 100644 --- a/daemon/licensing_test.go +++ b/daemon/licensing_test.go @@ -5,10 +5,10 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/dockerversion" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) -func TestfillLicense(t *testing.T) { +func TestFillLicense(t *testing.T) { v := &types.Info{} d := &Daemon{ root: "/var/lib/docker/", diff --git a/daemon/links/links.go b/daemon/links/links.go index be153f6bb012a..911a5aca12f4d 100644 --- a/daemon/links/links.go +++ b/daemon/links/links.go @@ -56,7 +56,7 @@ func (l *Link) ToEnv() []string { env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port())) } - //sort the ports so that we can bulk the continuous ports together + // sort the ports so that we can bulk the continuous ports together nat.Sort(l.Ports, func(ip, jp nat.Port) bool { // If the two ports have the same number, tcp takes priority // Sort in desc order diff --git a/daemon/list.go b/daemon/list.go index 69fb69a90c65f..e3c4153582e52 100644 --- a/daemon/list.go +++ b/daemon/list.go @@ -317,7 +317,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis if psFilters.Contains("ancestor") { ancestorFilter = true psFilters.WalkValues("ancestor", func(ancestor string) error { - img, err := daemon.imageService.GetImage(ancestor) + img, err := daemon.imageService.GetImage(ancestor, nil) if err != nil { logrus.Warnf("Error while looking up for image %v", ancestor) return nil @@ -388,7 +388,7 @@ func portOp(key string, filter map[nat.Port]bool) func(value string) error { if strings.Contains(value, ":") { return fmt.Errorf("filter for '%s' should not contain ':': %s", key, value) } - //support two formats, original format /[] or /[] + // support two formats, original format /[] or /[] proto, port := nat.SplitProtoPort(value) start, end, err := nat.ParsePortRange(port) if err != nil { @@ -551,23 +551,19 @@ func includeContainerInList(container *container.Snapshot, ctx *listContext) ite } } - if len(ctx.publish) > 0 { - shouldSkip := true - for port := range ctx.publish { - if _, ok := container.PortBindings[port]; ok { + if len(ctx.expose) > 0 || len(ctx.publish) > 0 { + var ( + shouldSkip = true + publishedPort nat.Port + exposedPort nat.Port + ) + for _, port := range container.Ports { + publishedPort = nat.Port(fmt.Sprintf("%d/%s", port.PublicPort, port.Type)) + exposedPort = nat.Port(fmt.Sprintf("%d/%s", port.PrivatePort, port.Type)) + if ok := ctx.publish[publishedPort]; ok { shouldSkip = false break - } - } - if shouldSkip { - return excludeContainer - } - } - - if len(ctx.expose) > 0 { - shouldSkip := true - for port := range ctx.expose { - if _, ok := container.ExposedPorts[port]; ok { + } else if ok := ctx.expose[exposedPort]; ok { shouldSkip = false break } @@ -585,7 +581,7 @@ func (daemon *Daemon) refreshImage(s *container.Snapshot, ctx *listContext) (*ty c := s.Container image := s.Image // keep the original ref if still valid (hasn't changed) if image != s.ImageID { - img, err := daemon.imageService.GetImage(image) + img, err := daemon.imageService.GetImage(image, nil) if _, isDNE := err.(images.ErrImageDoesNotExist); err != nil && !isDNE { return nil, err } diff --git a/daemon/list_test.go b/daemon/list_test.go index 2fdb266c4ab65..e6a7382bff294 100644 --- a/daemon/list_test.go +++ b/daemon/list_test.go @@ -1,7 +1,6 @@ package daemon import ( - "io/ioutil" "os" "path/filepath" "testing" @@ -11,17 +10,17 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/container" "github.com/docker/docker/image" - "github.com/opencontainers/go-digest" - "github.com/pborman/uuid" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/google/uuid" + digest "github.com/opencontainers/go-digest" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) var root string func TestMain(m *testing.M) { var err error - root, err = ioutil.TempDir("", "docker-container-test-") + root, err = os.MkdirTemp("", "docker-container-test-") if err != nil { panic(err) } @@ -36,7 +35,7 @@ func TestMain(m *testing.M) { func setupContainerWithName(t *testing.T, name string, daemon *Daemon) *container.Container { t.Helper() var ( - id = uuid.New() + id = uuid.New().String() computedImageID = digest.FromString(id) cRoot = filepath.Join(root, id) ) @@ -69,8 +68,8 @@ func setupContainerWithName(t *testing.T, name string, daemon *Daemon) *containe } func containerListContainsName(containers []*types.Container, name string) bool { - for _, container := range containers { - for _, containerName := range container.Names { + for _, ctr := range containers { + for _, containerName := range ctr.Names { if containerName == name { return true } diff --git a/daemon/list_unix.go b/daemon/list_unix.go index 4f9e453bc2198..af86834a10a0d 100644 --- a/daemon/list_unix.go +++ b/daemon/list_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package daemon // import "github.com/docker/docker/daemon" diff --git a/daemon/listeners/group_unix.go b/daemon/listeners/group_unix.go index 9cc17eba7b8d9..546871ed87402 100644 --- a/daemon/listeners/group_unix.go +++ b/daemon/listeners/group_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package listeners // import "github.com/docker/docker/daemon/listeners" @@ -6,25 +7,15 @@ import ( "fmt" "strconv" - "github.com/opencontainers/runc/libcontainer/user" - "github.com/pkg/errors" + "github.com/docker/docker/pkg/idtools" ) const defaultSocketGroup = "docker" func lookupGID(name string) (int, error) { - groupFile, err := user.GetGroupPath() - if err != nil { - return -1, errors.Wrap(err, "error looking up groups") - } - groups, err := user.ParseGroupFileFilter(groupFile, func(g user.Group) bool { - return g.Name == name || strconv.Itoa(g.Gid) == name - }) - if err != nil { - return -1, errors.Wrapf(err, "error parsing groups for %s", name) - } - if len(groups) > 0 { - return groups[0].Gid, nil + group, err := idtools.LookupGroup(name) + if err == nil { + return group.Gid, nil } gid, err := strconv.Atoi(name) if err == nil { diff --git a/daemon/listeners/listeners_linux.go b/daemon/listeners/listeners_linux.go index c8956db258fdc..515fa548b9820 100644 --- a/daemon/listeners/listeners_linux.go +++ b/daemon/listeners/listeners_linux.go @@ -2,13 +2,14 @@ package listeners // import "github.com/docker/docker/daemon/listeners" import ( "crypto/tls" - "fmt" "net" "os" "strconv" - "github.com/coreos/go-systemd/activation" + "github.com/coreos/go-systemd/v22/activation" + "github.com/docker/docker/pkg/homedir" "github.com/docker/go-connections/sockets" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -43,11 +44,15 @@ func Init(proto, addr, socketGroup string, tlsConfig *tls.Config) ([]net.Listene } l, err := sockets.NewUnixSocket(addr, gid) if err != nil { - return nil, fmt.Errorf("can't create unix socket %s: %v", addr, err) + return nil, errors.Wrapf(err, "can't create unix socket %s", addr) + } + if _, err := homedir.StickRuntimeDirContents([]string{addr}); err != nil { + // StickRuntimeDirContents returns nil error if XDG_RUNTIME_DIR is just unset + logrus.WithError(err).Warnf("cannot set sticky bit on socket %s under XDG_RUNTIME_DIR", addr) } ls = append(ls, l) default: - return nil, fmt.Errorf("invalid protocol format: %q", proto) + return nil, errors.Errorf("invalid protocol format: %q", proto) } return ls, nil @@ -71,7 +76,7 @@ func listenFD(addr string, tlsConfig *tls.Config) ([]net.Listener, error) { } if len(listeners) == 0 { - return nil, fmt.Errorf("no sockets found via socket activation: make sure the service was started by systemd") + return nil, errors.New("no sockets found via socket activation: make sure the service was started by systemd") } // default to all fds just like unix:// and tcp:// @@ -81,21 +86,21 @@ func listenFD(addr string, tlsConfig *tls.Config) ([]net.Listener, error) { fdNum, err := strconv.Atoi(addr) if err != nil { - return nil, fmt.Errorf("failed to parse systemd fd address: should be a number: %v", addr) + return nil, errors.Errorf("failed to parse systemd fd address: should be a number: %v", addr) } fdOffset := fdNum - 3 if len(listeners) < fdOffset+1 { - return nil, fmt.Errorf("too few socket activated files passed in by systemd") + return nil, errors.New("too few socket activated files passed in by systemd") } if listeners[fdOffset] == nil { - return nil, fmt.Errorf("failed to listen on systemd activated file: fd %d", fdOffset+3) + return nil, errors.Errorf("failed to listen on systemd activated file: fd %d", fdOffset+3) } for i, ls := range listeners { if i == fdOffset || ls == nil { continue } if err := ls.Close(); err != nil { - return nil, fmt.Errorf("failed to close systemd activated file: fd %d: %v", fdOffset+3, err) + return nil, errors.Wrapf(err, "failed to close systemd activated file: fd %d", fdOffset+3) } } return []net.Listener{listeners[fdOffset]}, nil diff --git a/daemon/listeners/listeners_windows.go b/daemon/listeners/listeners_windows.go index 73f5f79e4b18c..b6d630445a714 100644 --- a/daemon/listeners/listeners_windows.go +++ b/daemon/listeners/listeners_windows.go @@ -6,7 +6,7 @@ import ( "net" "strings" - "github.com/Microsoft/go-winio" + winio "github.com/Microsoft/go-winio" "github.com/docker/go-connections/sockets" ) diff --git a/daemon/logdrivers_linux.go b/daemon/logdrivers_linux.go index 67154a7a98b1c..425f412b200eb 100644 --- a/daemon/logdrivers_linux.go +++ b/daemon/logdrivers_linux.go @@ -11,6 +11,7 @@ import ( _ "github.com/docker/docker/daemon/logger/jsonfilelog" _ "github.com/docker/docker/daemon/logger/local" _ "github.com/docker/docker/daemon/logger/logentries" + _ "github.com/docker/docker/daemon/logger/loggerutils/cache" _ "github.com/docker/docker/daemon/logger/splunk" _ "github.com/docker/docker/daemon/logger/syslog" ) diff --git a/daemon/logdrivers_windows.go b/daemon/logdrivers_windows.go index 425a4d89b6e09..6c9d97f785821 100644 --- a/daemon/logdrivers_windows.go +++ b/daemon/logdrivers_windows.go @@ -10,6 +10,7 @@ import ( _ "github.com/docker/docker/daemon/logger/gelf" _ "github.com/docker/docker/daemon/logger/jsonfilelog" _ "github.com/docker/docker/daemon/logger/logentries" + _ "github.com/docker/docker/daemon/logger/loggerutils/cache" _ "github.com/docker/docker/daemon/logger/splunk" _ "github.com/docker/docker/daemon/logger/syslog" ) diff --git a/daemon/logger/adapter.go b/daemon/logger/adapter.go index d9370352c5735..97d59be5e0ebc 100644 --- a/daemon/logger/adapter.go +++ b/daemon/logger/adapter.go @@ -39,6 +39,13 @@ func (a *pluginAdapter) Log(msg *Message) error { a.buf.TimeNano = msg.Timestamp.UnixNano() a.buf.Partial = msg.PLogMetaData != nil a.buf.Source = msg.Source + if msg.PLogMetaData != nil { + a.buf.PartialLogMetadata = &logdriver.PartialLogEntryMetadata{ + Id: msg.PLogMetaData.ID, + Last: msg.PLogMetaData.Last, + Ordinal: int32(msg.PLogMetaData.Ordinal), + } + } err := a.enc.Encode(&a.buf) a.buf.Reset() diff --git a/daemon/logger/adapter_test.go b/daemon/logger/adapter_test.go index d14a48e477627..1ca28de6d9057 100644 --- a/daemon/logger/adapter_test.go +++ b/daemon/logger/adapter_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/api/types/plugins/logdriver" protoio "github.com/gogo/protobuf/io" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) // mockLoggingPlugin implements the loggingPlugin interface for testing purposes diff --git a/daemon/logger/awslogs/cloudwatchlogs.go b/daemon/logger/awslogs/cloudwatchlogs.go index 0f989af379838..0ed42e66510fe 100644 --- a/daemon/logger/awslogs/cloudwatchlogs.go +++ b/daemon/logger/awslogs/cloudwatchlogs.go @@ -11,6 +11,7 @@ import ( "strings" "sync" "time" + "unicode/utf8" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" @@ -34,11 +35,17 @@ const ( logGroupKey = "awslogs-group" logStreamKey = "awslogs-stream" logCreateGroupKey = "awslogs-create-group" + logCreateStreamKey = "awslogs-create-stream" tagKey = "tag" datetimeFormatKey = "awslogs-datetime-format" multilinePatternKey = "awslogs-multiline-pattern" credentialsEndpointKey = "awslogs-credentials-endpoint" - batchPublishFrequency = 5 * time.Second + forceFlushIntervalKey = "awslogs-force-flush-interval-seconds" + maxBufferedEventsKey = "awslogs-max-buffered-events" + logFormatKey = "awslogs-format" + + defaultForceFlushInterval = 5 * time.Second + defaultMaxBufferedEvents = 4096 // See: http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html perEventBytes = 26 @@ -46,6 +53,10 @@ const ( maximumLogEventsPerPut = 10000 // See: http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/cloudwatch_limits.html + // Because the events are interpreted as UTF-8 encoded Unicode, invalid UTF-8 byte sequences are replaced with the + // Unicode replacement character (U+FFFD), which is a 3-byte sequence in UTF-8. To compensate for that and to avoid + // splitting valid UTF-8 characters into invalid byte sequences, we calculate the length of each event assuming that + // this replacement happens. maximumBytesPerEvent = 262144 - perEventBytes resourceAlreadyExistsCode = "ResourceAlreadyExistsException" @@ -56,19 +67,36 @@ const ( credentialsEndpoint = "http://169.254.170.2" userAgentHeader = "User-Agent" + + // See: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html + logsFormatHeader = "x-amzn-logs-format" + jsonEmfLogFormat = "json/emf" ) type logStream struct { - logStreamName string - logGroupName string - logCreateGroup bool - logNonBlocking bool - multilinePattern *regexp.Regexp - client api - messages chan *logger.Message - lock sync.RWMutex - closed bool - sequenceToken *string + logStreamName string + logGroupName string + logCreateGroup bool + logCreateStream bool + logNonBlocking bool + forceFlushInterval time.Duration + multilinePattern *regexp.Regexp + client api + messages chan *logger.Message + lock sync.RWMutex + closed bool + sequenceToken *string +} + +type logStreamConfig struct { + logStreamName string + logGroupName string + logCreateGroup bool + logCreateStream bool + logNonBlocking bool + forceFlushInterval time.Duration + maxBufferedEvents int + multilinePattern *regexp.Regexp } var _ logger.SizedLogger = &logStream{} @@ -118,47 +146,29 @@ type eventBatch struct { // AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, the shared credentials // file (~/.aws/credentials), and the EC2 Instance Metadata Service. func New(info logger.Info) (logger.Logger, error) { - logGroupName := info.Config[logGroupKey] - logStreamName, err := loggerutils.ParseLogTag(info, "{{.FullID}}") + containerStreamConfig, err := newStreamConfig(info) if err != nil { return nil, err } - logCreateGroup := false - if info.Config[logCreateGroupKey] != "" { - logCreateGroup, err = strconv.ParseBool(info.Config[logCreateGroupKey]) - if err != nil { - return nil, err - } - } - - logNonBlocking := info.Config["mode"] == "non-blocking" - - if info.Config[logStreamKey] != "" { - logStreamName = info.Config[logStreamKey] - } - - multilinePattern, err := parseMultilineOptions(info) - if err != nil { - return nil, err - } - client, err := newAWSLogsClient(info) if err != nil { return nil, err } containerStream := &logStream{ - logStreamName: logStreamName, - logGroupName: logGroupName, - logCreateGroup: logCreateGroup, - logNonBlocking: logNonBlocking, - multilinePattern: multilinePattern, - client: client, - messages: make(chan *logger.Message, 4096), + logStreamName: containerStreamConfig.logStreamName, + logGroupName: containerStreamConfig.logGroupName, + logCreateGroup: containerStreamConfig.logCreateGroup, + logCreateStream: containerStreamConfig.logCreateStream, + logNonBlocking: containerStreamConfig.logNonBlocking, + forceFlushInterval: containerStreamConfig.forceFlushInterval, + multilinePattern: containerStreamConfig.multilinePattern, + client: client, + messages: make(chan *logger.Message, containerStreamConfig.maxBufferedEvents), } creationDone := make(chan bool) - if logNonBlocking { + if containerStream.logNonBlocking { go func() { backoff := 1 maxBackoff := 32 @@ -198,6 +208,71 @@ func New(info logger.Info) (logger.Logger, error) { return containerStream, nil } +// Parses most of the awslogs- options and prepares a config object to be used for newing the actual stream +// It has been formed out to ease Utest of the New above +func newStreamConfig(info logger.Info) (*logStreamConfig, error) { + logGroupName := info.Config[logGroupKey] + logStreamName, err := loggerutils.ParseLogTag(info, "{{.FullID}}") + if err != nil { + return nil, err + } + logCreateGroup := false + if info.Config[logCreateGroupKey] != "" { + logCreateGroup, err = strconv.ParseBool(info.Config[logCreateGroupKey]) + if err != nil { + return nil, err + } + } + + logNonBlocking := info.Config["mode"] == "non-blocking" + + forceFlushInterval := defaultForceFlushInterval + if info.Config[forceFlushIntervalKey] != "" { + forceFlushIntervalAsInt, err := strconv.Atoi(info.Config[forceFlushIntervalKey]) + if err != nil { + return nil, err + } + forceFlushInterval = time.Duration(forceFlushIntervalAsInt) * time.Second + } + + maxBufferedEvents := int(defaultMaxBufferedEvents) + if info.Config[maxBufferedEventsKey] != "" { + maxBufferedEvents, err = strconv.Atoi(info.Config[maxBufferedEventsKey]) + if err != nil { + return nil, err + } + } + + if info.Config[logStreamKey] != "" { + logStreamName = info.Config[logStreamKey] + } + logCreateStream := true + if info.Config[logCreateStreamKey] != "" { + logCreateStream, err = strconv.ParseBool(info.Config[logCreateStreamKey]) + if err != nil { + return nil, err + } + } + + multilinePattern, err := parseMultilineOptions(info) + if err != nil { + return nil, err + } + + containerStreamConfig := &logStreamConfig{ + logStreamName: logStreamName, + logGroupName: logGroupName, + logCreateGroup: logCreateGroup, + logCreateStream: logCreateStream, + logNonBlocking: logNonBlocking, + forceFlushInterval: forceFlushInterval, + maxBufferedEvents: maxBufferedEvents, + multilinePattern: multilinePattern, + } + + return containerStreamConfig, nil +} + // Parses awslogs-multiline-pattern and awslogs-datetime-format options // If awslogs-datetime-format is present, convert the format from strftime // to regexp and return. @@ -250,8 +325,12 @@ var strftimeToRegex = map[string]string{ // newRegionFinder is a variable such that the implementation // can be swapped out for unit tests. -var newRegionFinder = func() regionFinder { - return ec2metadata.New(session.New()) +var newRegionFinder = func() (regionFinder, error) { + s, err := session.NewSession() + if err != nil { + return nil, err + } + return ec2metadata.New(s), nil } // newSDKEndpoint is a variable such that the implementation @@ -275,12 +354,15 @@ func newAWSLogsClient(info logger.Info) (api, error) { } if region == nil || *region == "" { logrus.Info("Trying to get region from EC2 Metadata") - ec2MetadataClient := newRegionFinder() + ec2MetadataClient, err := newRegionFinder() + if err != nil { + logrus.WithError(err).Error("could not create EC2 metadata client") + return nil, errors.Wrap(err, "could not create EC2 metadata client") + } + r, err := ec2MetadataClient.Region() if err != nil { - logrus.WithFields(logrus.Fields{ - "error": err, - }).Error("Could not get region from EC2 metadata, environment, or log option") + logrus.WithError(err).Error("Could not get region from EC2 metadata, environment, or log option") return nil, errors.New("Cannot determine region for awslogs driver") } region = &r @@ -288,7 +370,7 @@ func newAWSLogsClient(info logger.Info) (api, error) { sess, err := session.NewSession() if err != nil { - return nil, errors.New("Failed to create a service client session for for awslogs driver") + return nil, errors.New("Failed to create a service client session for awslogs driver") } // attach region to cloudwatchlogs config @@ -327,6 +409,16 @@ func newAWSLogsClient(info logger.Info) (api, error) { dockerversion.Version, runtime.GOOS, currentAgent)) }, }) + + if info.Config[logFormatKey] != "" { + client.Handlers.Build.PushBackNamed(request.NamedHandler{ + Name: "LogFormatHeaderHandler", + Fn: func(req *request.Request) { + req.HTTPRequest.Header.Set(logsFormatHeader, info.Config[logFormatKey]) + }, + }) + } + return client, nil } @@ -335,6 +427,7 @@ func (l *logStream) Name() string { return name } +// BufSize returns the maximum bytes CloudWatch can handle. func (l *logStream) BufSize() int { return maximumBytesPerEvent } @@ -371,21 +464,20 @@ func (l *logStream) Close() error { // create creates log group and log stream for the instance of the awslogs logging driver func (l *logStream) create() error { - if err := l.createLogStream(); err != nil { - if l.logCreateGroup { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == resourceNotFoundCode { - if err := l.createLogGroup(); err != nil { - return err - } - return l.createLogStream() - } + err := l.createLogStream() + if err == nil { + return nil + } + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == resourceNotFoundCode && l.logCreateGroup { + if err := l.createLogGroup(); err != nil { + return errors.Wrap(err, "failed to create Cloudwatch log group") } - if err != nil { - return err + err = l.createLogStream() + if err == nil { + return nil } } - - return nil + return errors.Wrap(err, "failed to create Cloudwatch log stream") } // createLogGroup creates a log group for the instance of the awslogs logging driver @@ -415,6 +507,16 @@ func (l *logStream) createLogGroup() error { // createLogStream creates a log stream for the instance of the awslogs logging driver func (l *logStream) createLogStream() error { + // Directly return if we do not want to create log stream. + if !l.logCreateStream { + logrus.WithFields(logrus.Fields{ + "logGroupName": l.logGroupName, + "logStreamName": l.logStreamName, + "logCreateStream": l.logCreateStream, + }).Info("Skipping creating log stream") + return nil + } + input := &cloudwatchlogs.CreateLogStreamInput{ LogGroupName: aws.String(l.logGroupName), LogStreamName: aws.String(l.logStreamName), @@ -462,7 +564,11 @@ var newTicker = func(freq time.Duration) *time.Ticker { func (l *logStream) collectBatch(created chan bool) { // Wait for the logstream/group to be created <-created - ticker := newTicker(batchPublishFrequency) + flushInterval := l.forceFlushInterval + if flushInterval <= 0 { + flushInterval = defaultForceFlushInterval + } + ticker := newTicker(flushInterval) var eventBuffer []byte var eventBufferTimestamp int64 var batch = newEventBatch() @@ -472,7 +578,7 @@ func (l *logStream) collectBatch(created chan bool) { // If event buffer is older than batch publish frequency flush the event buffer if eventBufferTimestamp > 0 && len(eventBuffer) > 0 { eventBufferAge := t.UnixNano()/int64(time.Millisecond) - eventBufferTimestamp - eventBufferExpired := eventBufferAge >= int64(batchPublishFrequency)/int64(time.Millisecond) + eventBufferExpired := eventBufferAge >= int64(flushInterval)/int64(time.Millisecond) eventBufferNegative := eventBufferAge < 0 if eventBufferExpired || eventBufferNegative { l.processEvent(batch, eventBuffer, eventBufferTimestamp) @@ -485,7 +591,6 @@ func (l *logStream) collectBatch(created chan bool) { if !more { // Flush event buffer and release resources l.processEvent(batch, eventBuffer, eventBufferTimestamp) - eventBuffer = eventBuffer[:0] l.publishBatch(batch) batch.reset() return @@ -495,15 +600,16 @@ func (l *logStream) collectBatch(created chan bool) { } line := msg.Line if l.multilinePattern != nil { - if l.multilinePattern.Match(line) || len(eventBuffer)+len(line) > maximumBytesPerEvent { + lineEffectiveLen := effectiveLen(string(line)) + if l.multilinePattern.Match(line) || effectiveLen(string(eventBuffer))+lineEffectiveLen > maximumBytesPerEvent { // This is a new log event or we will exceed max bytes per event // so flush the current eventBuffer to events and reset timestamp l.processEvent(batch, eventBuffer, eventBufferTimestamp) eventBufferTimestamp = msg.Timestamp.UnixNano() / int64(time.Millisecond) eventBuffer = eventBuffer[:0] } - // Append new line if event is less than max event size - if len(line) < maximumBytesPerEvent { + // Append newline if event is less than max event size + if lineEffectiveLen < maximumBytesPerEvent { line = append(line, "\n"...) } eventBuffer = append(eventBuffer, line...) @@ -524,16 +630,17 @@ func (l *logStream) collectBatch(created chan bool) { // batch (defined in maximumBytesPerPut). Log messages are split by the maximum // bytes per event (defined in maximumBytesPerEvent). There is a fixed per-event // byte overhead (defined in perEventBytes) which is accounted for in split- and -// batch-calculations. -func (l *logStream) processEvent(batch *eventBatch, events []byte, timestamp int64) { - for len(events) > 0 { +// batch-calculations. Because the events are interpreted as UTF-8 encoded +// Unicode, invalid UTF-8 byte sequences are replaced with the Unicode +// replacement character (U+FFFD), which is a 3-byte sequence in UTF-8. To +// compensate for that and to avoid splitting valid UTF-8 characters into +// invalid byte sequences, we calculate the length of each event assuming that +// this replacement happens. +func (l *logStream) processEvent(batch *eventBatch, bytes []byte, timestamp int64) { + for len(bytes) > 0 { // Split line length so it does not exceed the maximum - lineBytes := len(events) - if lineBytes > maximumBytesPerEvent { - lineBytes = maximumBytesPerEvent - } - line := events[:lineBytes] - + splitOffset, lineBytes := findValidSplit(string(bytes), maximumBytesPerEvent) + line := bytes[:splitOffset] event := wrappedEvent{ inputLogEvent: &cloudwatchlogs.InputLogEvent{ Message: aws.String(string(line)), @@ -544,7 +651,7 @@ func (l *logStream) processEvent(batch *eventBatch, events []byte, timestamp int added := batch.add(event, lineBytes) if added { - events = events[lineBytes:] + bytes = bytes[splitOffset:] } else { l.publishBatch(batch) batch.reset() @@ -552,6 +659,37 @@ func (l *logStream) processEvent(batch *eventBatch, events []byte, timestamp int } } +// effectiveLen counts the effective number of bytes in the string, after +// UTF-8 normalization. UTF-8 normalization includes replacing bytes that do +// not constitute valid UTF-8 encoded Unicode codepoints with the Unicode +// replacement codepoint U+FFFD (a 3-byte UTF-8 sequence, represented in Go as +// utf8.RuneError) +func effectiveLen(line string) int { + effectiveBytes := 0 + for _, rune := range line { + effectiveBytes += utf8.RuneLen(rune) + } + return effectiveBytes +} + +// findValidSplit finds the byte offset to split a string without breaking valid +// Unicode codepoints given a maximum number of total bytes. findValidSplit +// returns the byte offset for splitting a string or []byte, as well as the +// effective number of bytes if the string were normalized to replace invalid +// UTF-8 encoded bytes with the Unicode replacement character (a 3-byte UTF-8 +// sequence, represented in Go as utf8.RuneError) +func findValidSplit(line string, maxBytes int) (splitOffset, effectiveBytes int) { + for offset, rune := range line { + splitOffset = offset + if effectiveBytes+utf8.RuneLen(rune) > maxBytes { + return splitOffset, effectiveBytes + } + effectiveBytes += utf8.RuneLen(rune) + } + splitOffset = len(line) + return +} + // publishBatch calls PutLogEvents for a given set of InputLogEvents, // accounting for sequencing requirements (each request must reference the // sequence token returned by the previous request). @@ -630,6 +768,9 @@ func ValidateLogOpt(cfg map[string]string) error { case datetimeFormatKey: case multilinePatternKey: case credentialsEndpointKey: + case forceFlushIntervalKey: + case maxBufferedEventsKey: + case logFormatKey: default: return fmt.Errorf("unknown log opt '%s' for %s log driver", key, name) } @@ -642,11 +783,32 @@ func ValidateLogOpt(cfg map[string]string) error { return fmt.Errorf("must specify valid value for log opt '%s': %v", logCreateGroupKey, err) } } + if cfg[forceFlushIntervalKey] != "" { + if value, err := strconv.Atoi(cfg[forceFlushIntervalKey]); err != nil || value <= 0 { + return fmt.Errorf("must specify a positive integer for log opt '%s': %v", forceFlushIntervalKey, cfg[forceFlushIntervalKey]) + } + } + if cfg[maxBufferedEventsKey] != "" { + if value, err := strconv.Atoi(cfg[maxBufferedEventsKey]); err != nil || value <= 0 { + return fmt.Errorf("must specify a positive integer for log opt '%s': %v", maxBufferedEventsKey, cfg[maxBufferedEventsKey]) + } + } _, datetimeFormatKeyExists := cfg[datetimeFormatKey] _, multilinePatternKeyExists := cfg[multilinePatternKey] if datetimeFormatKeyExists && multilinePatternKeyExists { return fmt.Errorf("you cannot configure log opt '%s' and '%s' at the same time", datetimeFormatKey, multilinePatternKey) } + + if cfg[logFormatKey] != "" { + // For now, only the "json/emf" log format is supported + if cfg[logFormatKey] != jsonEmfLogFormat { + return fmt.Errorf("unsupported log format '%s'", cfg[logFormatKey]) + } + if datetimeFormatKeyExists || multilinePatternKeyExists { + return fmt.Errorf("you cannot configure log opt '%s' or '%s' when log opt '%s' is set to '%s'", datetimeFormatKey, multilinePatternKey, logFormatKey, jsonEmfLogFormat) + } + } + return nil } diff --git a/daemon/logger/awslogs/cloudwatchlogs_test.go b/daemon/logger/awslogs/cloudwatchlogs_test.go index 172be51a307f5..28b521d1fa511 100644 --- a/daemon/logger/awslogs/cloudwatchlogs_test.go +++ b/daemon/logger/awslogs/cloudwatchlogs_test.go @@ -3,13 +3,13 @@ package awslogs // import "github.com/docker/docker/daemon/logger/awslogs" import ( "errors" "fmt" - "io/ioutil" "net/http" "net/http/httptest" "os" "reflect" "regexp" "runtime" + "strconv" "strings" "testing" "time" @@ -21,8 +21,8 @@ import ( "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/loggerutils" "github.com/docker/docker/dockerversion" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) const ( @@ -59,6 +59,65 @@ func testEventBatch(events []wrappedEvent) *eventBatch { return batch } +func TestNewStreamConfig(t *testing.T) { + tests := []struct { + logStreamName string + logGroupName string + logCreateGroup string + logCreateStream string + logNonBlocking string + forceFlushInterval string + maxBufferedEvents string + datetimeFormat string + multilinePattern string + shouldErr bool + testName string + }{ + {"", groupName, "", "", "", "", "", "", "", false, "defaults"}, + {"", groupName, "invalid create group", "", "", "", "", "", "", true, "invalid create group"}, + {"", groupName, "", "", "", "invalid flush interval", "", "", "", true, "invalid flush interval"}, + {"", groupName, "", "", "", "", "invalid max buffered events", "", "", true, "invalid max buffered events"}, + {"", groupName, "", "", "", "", "", "", "n{1001}", true, "invalid multiline pattern"}, + {"", groupName, "", "", "", "15", "", "", "", false, "flush interval at 15"}, + {"", groupName, "", "", "", "", "1024", "", "", false, "max buffered events at 1024"}, + } + + for _, tc := range tests { + t.Run(tc.testName, func(t *testing.T) { + cfg := map[string]string{ + logGroupKey: tc.logGroupName, + logCreateGroupKey: tc.logCreateGroup, + "mode": tc.logNonBlocking, + forceFlushIntervalKey: tc.forceFlushInterval, + maxBufferedEventsKey: tc.maxBufferedEvents, + logStreamKey: tc.logStreamName, + logCreateStreamKey: tc.logCreateStream, + datetimeFormatKey: tc.datetimeFormat, + multilinePatternKey: tc.multilinePattern, + } + + info := logger.Info{ + Config: cfg, + } + logStreamConfig, err := newStreamConfig(info) + if tc.shouldErr { + assert.Check(t, err != nil, "Expected an error") + } else { + assert.Check(t, err == nil, "Unexpected error") + assert.Check(t, logStreamConfig.logGroupName == tc.logGroupName, "Unexpected logGroupName") + if tc.forceFlushInterval != "" { + forceFlushIntervalAsInt, _ := strconv.Atoi(info.Config[forceFlushIntervalKey]) + assert.Check(t, logStreamConfig.forceFlushInterval == time.Duration(forceFlushIntervalAsInt)*time.Second, "Unexpected forceFlushInterval") + } + if tc.maxBufferedEvents != "" { + maxBufferedEvents, _ := strconv.Atoi(info.Config[maxBufferedEventsKey]) + assert.Check(t, logStreamConfig.maxBufferedEvents == maxBufferedEvents, "Unexpected maxBufferedEvents") + } + } + }) + } +} + func TestNewAWSLogsClientUserAgentHandler(t *testing.T) { info := logger.Info{ Config: map[string]string{ @@ -88,6 +147,48 @@ func TestNewAWSLogsClientUserAgentHandler(t *testing.T) { } } +func TestNewAWSLogsClientLogFormatHeaderHandler(t *testing.T) { + tests := []struct { + logFormat string + expectedHeaderValue string + }{ + { + logFormat: jsonEmfLogFormat, + expectedHeaderValue: "json/emf", + }, + { + logFormat: "", + expectedHeaderValue: "", + }, + } + for _, tc := range tests { + t.Run(tc.logFormat, func(t *testing.T) { + info := logger.Info{ + Config: map[string]string{ + regionKey: "us-east-1", + logFormatKey: tc.logFormat, + }, + } + + client, err := newAWSLogsClient(info) + assert.NilError(t, err) + + realClient, ok := client.(*cloudwatchlogs.CloudWatchLogs) + assert.Check(t, ok, "Could not cast client to cloudwatchlogs.CloudWatchLogs") + + buildHandlerList := realClient.Handlers.Build + request := &request.Request{ + HTTPRequest: &http.Request{ + Header: http.Header{}, + }, + } + buildHandlerList.Run(request) + logFormatHeaderVal := request.HTTPRequest.Header.Get("x-amzn-logs-format") + assert.Equal(t, tc.expectedHeaderValue, logFormatHeaderVal) + }) + } +} + func TestNewAWSLogsClientAWSLogsEndpoint(t *testing.T) { endpoint := "mock-endpoint" info := logger.Info{ @@ -114,8 +215,8 @@ func TestNewAWSLogsClientRegionDetect(t *testing.T) { } mockMetadata := newMockMetadataClient() - newRegionFinder = func() regionFinder { - return mockMetadata + newRegionFinder = func() (regionFinder, error) { + return mockMetadata, nil } mockMetadata.regionResult <- ®ionResult{ successResult: "us-east-1", @@ -128,9 +229,10 @@ func TestNewAWSLogsClientRegionDetect(t *testing.T) { func TestCreateSuccess(t *testing.T) { mockClient := newMockClient() stream := &logStream{ - client: mockClient, - logGroupName: groupName, - logStreamName: streamName, + client: mockClient, + logGroupName: groupName, + logStreamName: streamName, + logCreateStream: true, } mockClient.createLogStreamResult <- &createLogStreamResult{} @@ -154,13 +256,28 @@ func TestCreateSuccess(t *testing.T) { } } +func TestCreateStreamSkipped(t *testing.T) { + stream := &logStream{ + logGroupName: groupName, + logStreamName: streamName, + logCreateStream: false, + } + + err := stream.create() + + if err != nil { + t.Errorf("Received unexpected err: %v\n", err) + } +} + func TestCreateLogGroupSuccess(t *testing.T) { mockClient := newMockClient() stream := &logStream{ - client: mockClient, - logGroupName: groupName, - logStreamName: streamName, - logCreateGroup: true, + client: mockClient, + logGroupName: groupName, + logStreamName: streamName, + logCreateGroup: true, + logCreateStream: true, } mockClient.createLogGroupResult <- &createLogGroupResult{} mockClient.createLogStreamResult <- &createLogStreamResult{} @@ -188,7 +305,8 @@ func TestCreateLogGroupSuccess(t *testing.T) { func TestCreateError(t *testing.T) { mockClient := newMockClient() stream := &logStream{ - client: mockClient, + client: mockClient, + logCreateStream: true, } mockClient.createLogStreamResult <- &createLogStreamResult{ errorResult: errors.New("Error"), @@ -204,7 +322,8 @@ func TestCreateError(t *testing.T) { func TestCreateAlreadyExists(t *testing.T) { mockClient := newMockClient() stream := &logStream{ - client: mockClient, + client: mockClient, + logCreateStream: true, } mockClient.createLogStreamResult <- &createLogStreamResult{ errorResult: awserr.New(resourceAlreadyExistsCode, "", nil), @@ -227,6 +346,9 @@ func TestLogClosed(t *testing.T) { } } +// TestLogBlocking tests that the Log method blocks appropriately when +// non-blocking behavior is not enabled. Blocking is achieved through an +// internal channel that must be drained for Log to return. func TestLogBlocking(t *testing.T) { mockClient := newMockClient() stream := &logStream{ @@ -241,18 +363,20 @@ func TestLogBlocking(t *testing.T) { err := stream.Log(&logger.Message{}) errorCh <- err }() + // block until the goroutine above has started <-started select { case err := <-errorCh: t.Fatal("Expected stream.Log to block: ", err) default: - break } + // assuming it is blocked, we can now try to drain the internal channel and + // unblock it select { - case <-stream.messages: - break - default: + case <-time.After(10 * time.Millisecond): + // if we're unable to drain the channel within 10ms, something seems broken t.Fatal("Expected to be able to read from stream.messages but was unable to") + case <-stream.messages: } select { case err := <-errorCh: @@ -282,7 +406,7 @@ func TestLogNonBlockingBufferFull(t *testing.T) { logNonBlocking: true, } stream.messages <- &logger.Message{} - errorCh := make(chan error) + errorCh := make(chan error, 1) started := make(chan bool) go func() { started <- true @@ -762,10 +886,10 @@ func TestCollectBatchMultilinePatternMaxEventAge(t *testing.T) { Timestamp: time.Now().Add(time.Second), }) - // Fire ticker batchPublishFrequency seconds later - ticks <- time.Now().Add(batchPublishFrequency + time.Second) + // Fire ticker defaultForceFlushInterval seconds later + ticks <- time.Now().Add(defaultForceFlushInterval + time.Second) - // Verify single multiline event is flushed after maximum event buffer age (batchPublishFrequency) + // Verify single multiline event is flushed after maximum event buffer age (defaultForceFlushInterval) argument := <-mockClient.putLogEventsArgument assert.Check(t, argument != nil, "Expected non-nil PutLogEventsInput") assert.Check(t, is.Equal(1, len(argument.LogEvents)), "Expected single multiline event") @@ -777,8 +901,8 @@ func TestCollectBatchMultilinePatternMaxEventAge(t *testing.T) { Timestamp: time.Now().Add(time.Second), }) - // Fire ticker another batchPublishFrequency seconds later - ticks <- time.Now().Add(2*batchPublishFrequency + time.Second) + // Fire ticker another defaultForceFlushInterval seconds later + ticks <- time.Now().Add(2*defaultForceFlushInterval + time.Second) // Verify the event buffer is truly flushed - we should only receive a single event argument = <-mockClient.putLogEventsArgument @@ -880,7 +1004,7 @@ func TestCollectBatchMultilinePatternMaxEventSize(t *testing.T) { }) // Fire ticker - ticks <- time.Now().Add(batchPublishFrequency) + ticks <- time.Now().Add(defaultForceFlushInterval) // Verify multiline events // We expect a maximum sized event with no new line characters and a @@ -938,6 +1062,62 @@ func TestCollectBatchClose(t *testing.T) { } } +func TestEffectiveLen(t *testing.T) { + tests := []struct { + str string + effectiveBytes int + }{ + {"Hello", 5}, + {string([]byte{1, 2, 3, 4}), 4}, + {"🙃", 4}, + {string([]byte{0xFF, 0xFF, 0xFF, 0xFF}), 12}, + {"He\xff\xffo", 9}, + {"", 0}, + } + for i, tc := range tests { + t.Run(fmt.Sprintf("%d/%s", i, tc.str), func(t *testing.T) { + assert.Equal(t, tc.effectiveBytes, effectiveLen(tc.str)) + }) + } +} + +func TestFindValidSplit(t *testing.T) { + tests := []struct { + str string + maxEffectiveBytes int + splitOffset int + effectiveBytes int + }{ + {"", 10, 0, 0}, + {"Hello", 6, 5, 5}, + {"Hello", 2, 2, 2}, + {"Hello", 0, 0, 0}, + {"🙃", 3, 0, 0}, + {"🙃", 4, 4, 4}, + {string([]byte{'a', 0xFF}), 2, 1, 1}, + {string([]byte{'a', 0xFF}), 4, 2, 4}, + } + for i, tc := range tests { + t.Run(fmt.Sprintf("%d/%s", i, tc.str), func(t *testing.T) { + splitOffset, effectiveBytes := findValidSplit(tc.str, tc.maxEffectiveBytes) + assert.Equal(t, tc.splitOffset, splitOffset, "splitOffset") + assert.Equal(t, tc.effectiveBytes, effectiveBytes, "effectiveBytes") + t.Log(tc.str[:tc.splitOffset]) + t.Log(tc.str[tc.splitOffset:]) + }) + } +} + +func TestProcessEventEmoji(t *testing.T) { + stream := &logStream{} + batch := &eventBatch{} + bytes := []byte(strings.Repeat("🙃", maximumBytesPerEvent/4+1)) + stream.processEvent(batch, bytes, 0) + assert.Equal(t, 2, len(batch.batch), "should be two events in the batch") + assert.Equal(t, strings.Repeat("🙃", maximumBytesPerEvent/4), aws.StringValue(batch.batch[0].inputLogEvent.Message)) + assert.Equal(t, "🙃", aws.StringValue(batch.batch[1].inputLogEvent.Message)) +} + func TestCollectBatchLineSplit(t *testing.T) { mockClient := newMockClient() stream := &logStream{ @@ -987,6 +1167,55 @@ func TestCollectBatchLineSplit(t *testing.T) { } } +func TestCollectBatchLineSplitWithBinary(t *testing.T) { + mockClient := newMockClient() + stream := &logStream{ + client: mockClient, + logGroupName: groupName, + logStreamName: streamName, + sequenceToken: aws.String(sequenceToken), + messages: make(chan *logger.Message), + } + mockClient.putLogEventsResult <- &putLogEventsResult{ + successResult: &cloudwatchlogs.PutLogEventsOutput{ + NextSequenceToken: aws.String(nextSequenceToken), + }, + } + var ticks = make(chan time.Time) + newTicker = func(_ time.Duration) *time.Ticker { + return &time.Ticker{ + C: ticks, + } + } + + d := make(chan bool) + close(d) + go stream.collectBatch(d) + + longline := strings.Repeat("\xFF", maximumBytesPerEvent/3) // 0xFF is counted as the 3-byte utf8.RuneError + stream.Log(&logger.Message{ + Line: []byte(longline + "\xFD"), + Timestamp: time.Time{}, + }) + + // no ticks + stream.Close() + + argument := <-mockClient.putLogEventsArgument + if argument == nil { + t.Fatal("Expected non-nil PutLogEventsInput") + } + if len(argument.LogEvents) != 2 { + t.Errorf("Expected LogEvents to contain 2 elements, but contains %d", len(argument.LogEvents)) + } + if *argument.LogEvents[0].Message != longline { + t.Errorf("Expected message to be %s but was %s", longline, *argument.LogEvents[0].Message) + } + if *argument.LogEvents[1].Message != "\xFD" { + t.Errorf("Expected message to be %s but was %s", "\xFD", *argument.LogEvents[1].Message) + } +} + func TestCollectBatchMaxEvents(t *testing.T) { mockClient := newMockClientBuffered(1) stream := &logStream{ @@ -1125,6 +1354,83 @@ func TestCollectBatchMaxTotalBytes(t *testing.T) { } } +func TestCollectBatchMaxTotalBytesWithBinary(t *testing.T) { + expectedPuts := 2 + mockClient := newMockClientBuffered(expectedPuts) + stream := &logStream{ + client: mockClient, + logGroupName: groupName, + logStreamName: streamName, + sequenceToken: aws.String(sequenceToken), + messages: make(chan *logger.Message), + } + for i := 0; i < expectedPuts; i++ { + mockClient.putLogEventsResult <- &putLogEventsResult{ + successResult: &cloudwatchlogs.PutLogEventsOutput{ + NextSequenceToken: aws.String(nextSequenceToken), + }, + } + } + + var ticks = make(chan time.Time) + newTicker = func(_ time.Duration) *time.Ticker { + return &time.Ticker{ + C: ticks, + } + } + + d := make(chan bool) + close(d) + go stream.collectBatch(d) + + // maxline is the maximum line that could be submitted after + // accounting for its overhead. + maxline := strings.Repeat("\xFF", (maximumBytesPerPut-perEventBytes)/3) // 0xFF is counted as the 3-byte utf8.RuneError + // This will be split and batched up to the `maximumBytesPerPut' + // (+/- `maximumBytesPerEvent'). This /should/ be aligned, but + // should also tolerate an offset within that range. + stream.Log(&logger.Message{ + Line: []byte(maxline), + Timestamp: time.Time{}, + }) + stream.Log(&logger.Message{ + Line: []byte("B"), + Timestamp: time.Time{}, + }) + + // no ticks, guarantee batch by size (and chan close) + stream.Close() + + argument := <-mockClient.putLogEventsArgument + if argument == nil { + t.Fatal("Expected non-nil PutLogEventsInput") + } + + // Should total to the maximum allowed bytes. + eventBytes := 0 + for _, event := range argument.LogEvents { + eventBytes += effectiveLen(*event.Message) + } + eventsOverhead := len(argument.LogEvents) * perEventBytes + payloadTotal := eventBytes + eventsOverhead + // lowestMaxBatch allows the payload to be offset if the messages + // don't lend themselves to align with the maximum event size. + lowestMaxBatch := maximumBytesPerPut - maximumBytesPerEvent + + if payloadTotal > maximumBytesPerPut { + t.Errorf("Expected <= %d bytes but was %d", maximumBytesPerPut, payloadTotal) + } + if payloadTotal < lowestMaxBatch { + t.Errorf("Batch to be no less than %d but was %d", lowestMaxBatch, payloadTotal) + } + + argument = <-mockClient.putLogEventsArgument + message := *argument.LogEvents[len(argument.LogEvents)-1].Message + if message[len(message)-1:] != "B" { + t.Errorf("Expected message to be %s but was %s", "B", message[len(message)-1:]) + } +} + func TestCollectBatchWithDuplicateTimestamps(t *testing.T) { mockClient := newMockClient() stream := &logStream{ @@ -1237,6 +1543,101 @@ func TestValidateLogOptionsDatetimeFormatAndMultilinePattern(t *testing.T) { assert.Check(t, is.Equal(err.Error(), conflictingLogOptionsError), "Received invalid error") } +func TestValidateLogOptionsForceFlushIntervalSeconds(t *testing.T) { + tests := []struct { + input string + shouldErr bool + }{ + {"0", true}, + {"-1", true}, + {"a", true}, + {"10", false}, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + cfg := map[string]string{ + forceFlushIntervalKey: tc.input, + logGroupKey: groupName, + } + + err := ValidateLogOpt(cfg) + if tc.shouldErr { + expectedErr := "must specify a positive integer for log opt 'awslogs-force-flush-interval-seconds': " + tc.input + assert.Error(t, err, expectedErr) + } else { + assert.NilError(t, err) + } + }) + } +} + +func TestValidateLogOptionsMaxBufferedEvents(t *testing.T) { + tests := []struct { + input string + shouldErr bool + }{ + {"0", true}, + {"-1", true}, + {"a", true}, + {"10", false}, + } + + for _, tc := range tests { + t.Run(tc.input, func(t *testing.T) { + cfg := map[string]string{ + maxBufferedEventsKey: tc.input, + logGroupKey: groupName, + } + + err := ValidateLogOpt(cfg) + if tc.shouldErr { + expectedErr := "must specify a positive integer for log opt 'awslogs-max-buffered-events': " + tc.input + assert.Error(t, err, expectedErr) + } else { + assert.NilError(t, err) + } + }) + } +} + +func TestValidateLogOptionsFormat(t *testing.T) { + tests := []struct { + format string + multiLinePattern string + datetimeFormat string + expErrMsg string + }{ + {"json/emf", "", "", ""}, + {"random", "", "", "unsupported log format 'random'"}, + {"", "", "", ""}, + {"json/emf", "---", "", "you cannot configure log opt 'awslogs-datetime-format' or 'awslogs-multiline-pattern' when log opt 'awslogs-format' is set to 'json/emf'"}, + {"json/emf", "", "yyyy-dd-mm", "you cannot configure log opt 'awslogs-datetime-format' or 'awslogs-multiline-pattern' when log opt 'awslogs-format' is set to 'json/emf'"}, + } + + for i, tc := range tests { + t.Run(fmt.Sprintf("%d/%s", i, tc.format), func(t *testing.T) { + cfg := map[string]string{ + logGroupKey: groupName, + logFormatKey: tc.format, + } + if tc.multiLinePattern != "" { + cfg[multilinePatternKey] = tc.multiLinePattern + } + if tc.datetimeFormat != "" { + cfg[datetimeFormatKey] = tc.datetimeFormat + } + + err := ValidateLogOpt(cfg) + if tc.expErrMsg != "" { + assert.Error(t, err, tc.expErrMsg) + } else { + assert.NilError(t, err) + } + }) + } +} + func TestCreateTagSuccess(t *testing.T) { mockClient := newMockClient() info := logger.Info{ @@ -1249,9 +1650,10 @@ func TestCreateTagSuccess(t *testing.T) { t.Errorf("Error generating tag: %q", e) } stream := &logStream{ - client: mockClient, - logGroupName: groupName, - logStreamName: logStreamName, + client: mockClient, + logGroupName: groupName, + logStreamName: logStreamName, + logCreateStream: true, } mockClient.createLogStreamResult <- &createLogStreamResult{} @@ -1366,7 +1768,7 @@ func TestNewAWSLogsClientCredentialSharedFile(t *testing.T) { ` content := []byte(contentStr) - tmpfile, err := ioutil.TempFile("", "example") + tmpfile, err := os.CreateTemp("", "example") defer os.Remove(tmpfile.Name()) // clean up assert.Check(t, err) diff --git a/daemon/logger/copier.go b/daemon/logger/copier.go index e24272fa6dff5..30c68ea364d2b 100644 --- a/daemon/logger/copier.go +++ b/daemon/logger/copier.go @@ -54,7 +54,12 @@ func (c *Copier) copySrc(name string, src io.Reader) { bufSize := defaultBufSize if sizedLogger, ok := c.dst.(SizedLogger); ok { - bufSize = sizedLogger.BufSize() + size := sizedLogger.BufSize() + // Loggers that wrap another loggers would have BufSize(), but cannot return the size + // when the wrapped loggers doesn't have BufSize(). + if size > 0 { + bufSize = size + } } buf := make([]byte, bufSize) @@ -121,8 +126,7 @@ func (c *Copier) copySrc(name string, src io.Reader) { } if logErr := c.dst.Log(msg); logErr != nil { - logWritesFailedCount.Inc(1) - logrus.Errorf("Failed to log msg %q for logger %s: %s", msg.Line, c.dst.Name(), logErr) + logDriverError(c.dst.Name(), string(msg.Line), logErr) } } p += q + 1 @@ -154,8 +158,7 @@ func (c *Copier) copySrc(name string, src io.Reader) { hasMorePartial = true if logErr := c.dst.Log(msg); logErr != nil { - logWritesFailedCount.Inc(1) - logrus.Errorf("Failed to log msg %q for logger %s: %s", msg.Line, c.dst.Name(), logErr) + logDriverError(c.dst.Name(), string(msg.Line), logErr) } p = 0 n = 0 diff --git a/daemon/logger/copier_test.go b/daemon/logger/copier_test.go index d09450bd199b6..db674b32a3dd7 100644 --- a/daemon/logger/copier_test.go +++ b/daemon/logger/copier_test.go @@ -3,7 +3,6 @@ package logger // import "github.com/docker/docker/daemon/logger" import ( "bytes" "encoding/json" - "fmt" "io" "os" "strings" @@ -204,7 +203,7 @@ func TestCopierSlow(t *testing.T) { } var jsonBuf bytes.Buffer - //encoder := &encodeCloser{Encoder: json.NewEncoder(&jsonBuf)} + // encoder := &encodeCloser{Encoder: json.NewEncoder(&jsonBuf)} jsonLog := &TestLoggerJSON{Encoder: json.NewEncoder(&jsonBuf), delay: 100 * time.Millisecond} c := NewCopier(map[string]io.Reader{"stdout": &stdout}, jsonLog) @@ -224,10 +223,28 @@ func TestCopierSlow(t *testing.T) { } func TestCopierWithSized(t *testing.T) { + t.Run("as is", func(t *testing.T) { + testCopierWithSized(t, func(l SizedLogger) SizedLogger { + return l + }) + }) + t.Run("With RingLogger", func(t *testing.T) { + testCopierWithSized(t, func(l SizedLogger) SizedLogger { + return newRingLogger(l, Info{}, defaultRingMaxSize) + }) + }) +} + +func testCopierWithSized(t *testing.T, loggerFactory func(SizedLogger) SizedLogger) { var jsonBuf bytes.Buffer expectedMsgs := 2 - sizedLogger := &TestSizedLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)} - logbuf := bytes.NewBufferString(strings.Repeat(".", sizedLogger.BufSize()*expectedMsgs)) + sizedLogger := loggerFactory(&TestSizedLoggerJSON{Encoder: json.NewEncoder(&jsonBuf)}) + + size := sizedLogger.BufSize() + if size < 0 { + size = 100 + } + logbuf := bytes.NewBufferString(strings.Repeat(".", size*expectedMsgs)) c := NewCopier(map[string]io.Reader{"stdout": logbuf}, sizedLogger) c.Run() @@ -235,6 +252,8 @@ func TestCopierWithSized(t *testing.T) { c.Wait() c.Close() + sizedLogger.Close() + recvdMsgs := 0 dec := json.NewDecoder(&jsonBuf) for { @@ -254,7 +273,7 @@ func TestCopierWithSized(t *testing.T) { recvdMsgs++ } if recvdMsgs != expectedMsgs { - t.Fatalf("expected to receive %d messages, actually received %d", expectedMsgs, recvdMsgs) + t.Fatalf("expected to receive %d messages, actually received %d %q", expectedMsgs, recvdMsgs, jsonBuf.String()) } } @@ -453,9 +472,9 @@ func piped(b *testing.B, iterations int, delay time.Duration, buf []byte) io.Rea time.Sleep(delay) if n, err := w.Write(buf); err != nil || n != len(buf) { if err != nil { - b.Fatal(err) + b.Error(err) } - b.Fatal(fmt.Errorf("short write")) + b.Error("short write") } } w.Close() diff --git a/daemon/logger/factory.go b/daemon/logger/factory.go index 84b54b2794cb6..933ea0884e6f7 100644 --- a/daemon/logger/factory.go +++ b/daemon/logger/factory.go @@ -7,7 +7,7 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/go-units" + units "github.com/docker/go-units" "github.com/pkg/errors" ) @@ -41,7 +41,11 @@ func ListDrivers() []string { } func (lf *logdriverFactory) register(name string, c Creator) error { - if lf.driverRegistered(name) { + registered, err := lf.driverRegistered(name) + if err != nil { + return err + } + if registered { return fmt.Errorf("logger: log driver named '%s' is already registered", name) } @@ -51,18 +55,22 @@ func (lf *logdriverFactory) register(name string, c Creator) error { return nil } -func (lf *logdriverFactory) driverRegistered(name string) bool { +func (lf *logdriverFactory) driverRegistered(name string) (bool, error) { lf.m.Lock() _, ok := lf.registry[name] lf.m.Unlock() if !ok { if pluginGetter != nil { // this can be nil when the init functions are running - if l, _ := getPlugin(name, plugingetter.Lookup); l != nil { - return true + l, err := getPlugin(name, plugingetter.Lookup) + if err != nil { + return false, err + } + if l != nil { + return true, nil } } } - return ok + return ok, nil } func (lf *logdriverFactory) registerLogOptValidator(name string, l LogOptValidator) error { @@ -143,7 +151,15 @@ func ValidateLogOpts(name string, cfg map[string]string) error { } } - if !factory.driverRegistered(name) { + if err := validateExternal(cfg); err != nil { + return err + } + + registered, err := factory.driverRegistered(name) + if err != nil { + return err + } + if !registered { return fmt.Errorf("logger: no log driver named '%s' is registered", name) } diff --git a/daemon/logger/fluentd/fluentd.go b/daemon/logger/fluentd/fluentd.go index 907261f41f17d..997c01c65257e 100644 --- a/daemon/logger/fluentd/fluentd.go +++ b/daemon/logger/fluentd/fluentd.go @@ -3,7 +3,6 @@ package fluentd // import "github.com/docker/docker/daemon/logger/fluentd" import ( - "fmt" "math" "net" "net/url" @@ -13,8 +12,9 @@ import ( "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/loggerutils" + "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/urlutil" - "github.com/docker/go-units" + units "github.com/docker/go-units" "github.com/fluent/fluent-logger-golang/fluent" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -38,21 +38,23 @@ type location struct { const ( name = "fluentd" - defaultProtocol = "tcp" + defaultBufferLimit = 1024 * 1024 defaultHost = "127.0.0.1" defaultPort = 24224 - defaultBufferLimit = 1024 * 1024 + defaultProtocol = "tcp" // logger tries to reconnect 2**32 - 1 times // failed (and panic) after 204 years [ 1.5 ** (2**32 - 1) - 1 seconds] - defaultRetryWait = 1000 defaultMaxRetries = math.MaxInt32 + defaultRetryWait = 1000 addressKey = "fluentd-address" + asyncKey = "fluentd-async" + asyncConnectKey = "fluentd-async-connect" // deprecated option (use fluent-async instead) bufferLimitKey = "fluentd-buffer-limit" - retryWaitKey = "fluentd-retry-wait" maxRetriesKey = "fluentd-max-retries" - asyncConnectKey = "fluentd-async-connect" + requestAckKey = "fluentd-request-ack" + retryWaitKey = "fluentd-retry-wait" subSecondPrecisionKey = "fluentd-sub-second-precision" ) @@ -69,72 +71,19 @@ func init() { // the context. The supported context configuration variable is // fluentd-address. func New(info logger.Info) (logger.Logger, error) { - loc, err := parseAddress(info.Config[addressKey]) + fluentConfig, err := parseConfig(info.Config) if err != nil { - return nil, err + return nil, errdefs.InvalidParameter(err) } tag, err := loggerutils.ParseLogTag(info, loggerutils.DefaultTemplate) if err != nil { - return nil, err + return nil, errdefs.InvalidParameter(err) } extra, err := info.ExtraAttributes(nil) if err != nil { - return nil, err - } - - bufferLimit := defaultBufferLimit - if info.Config[bufferLimitKey] != "" { - bl64, err := units.RAMInBytes(info.Config[bufferLimitKey]) - if err != nil { - return nil, err - } - bufferLimit = int(bl64) - } - - retryWait := defaultRetryWait - if info.Config[retryWaitKey] != "" { - rwd, err := time.ParseDuration(info.Config[retryWaitKey]) - if err != nil { - return nil, err - } - retryWait = int(rwd.Seconds() * 1000) - } - - maxRetries := defaultMaxRetries - if info.Config[maxRetriesKey] != "" { - mr64, err := strconv.ParseUint(info.Config[maxRetriesKey], 10, strconv.IntSize) - if err != nil { - return nil, err - } - maxRetries = int(mr64) - } - - asyncConnect := false - if info.Config[asyncConnectKey] != "" { - if asyncConnect, err = strconv.ParseBool(info.Config[asyncConnectKey]); err != nil { - return nil, err - } - } - - subSecondPrecision := false - if info.Config[subSecondPrecisionKey] != "" { - if subSecondPrecision, err = strconv.ParseBool(info.Config[subSecondPrecisionKey]); err != nil { - return nil, err - } - } - - fluentConfig := fluent.Config{ - FluentPort: loc.port, - FluentHost: loc.host, - FluentNetwork: loc.protocol, - FluentSocketPath: loc.path, - BufferLimit: bufferLimit, - RetryWait: retryWait, - MaxRetry: maxRetries, - AsyncConnect: asyncConnect, - SubSecondPrecision: subSecondPrecision, + return nil, errdefs.InvalidParameter(err) } logrus.WithField("container", info.ContainerID).WithField("config", fluentConfig). @@ -165,6 +114,9 @@ func (f *fluentd) Log(msg *logger.Message) error { } if msg.PLogMetaData != nil { data["partial_message"] = "true" + data["partial_id"] = msg.PLogMetaData.ID + data["partial_ordinal"] = strconv.Itoa(msg.PLogMetaData.Ordinal) + data["partial_last"] = strconv.FormatBool(msg.PLogMetaData.Last) } ts := msg.Timestamp @@ -189,23 +141,113 @@ func ValidateLogOpt(cfg map[string]string) error { case "env": case "env-regex": case "labels": + case "labels-regex": case "tag": + case addressKey: + case asyncKey: + case asyncConnectKey: case bufferLimitKey: - case retryWaitKey: case maxRetriesKey: - case asyncConnectKey: + case requestAckKey: + case retryWaitKey: case subSecondPrecisionKey: // Accepted default: - return fmt.Errorf("unknown log opt '%s' for fluentd log driver", key) + return errors.Errorf("unknown log opt '%s' for fluentd log driver", key) } } - _, err := parseAddress(cfg[addressKey]) + _, err := parseConfig(cfg) return err } +func parseConfig(cfg map[string]string) (fluent.Config, error) { + var config fluent.Config + + loc, err := parseAddress(cfg[addressKey]) + if err != nil { + return config, err + } + + bufferLimit := defaultBufferLimit + if cfg[bufferLimitKey] != "" { + bl64, err := units.RAMInBytes(cfg[bufferLimitKey]) + if err != nil { + return config, err + } + bufferLimit = int(bl64) + } + + retryWait := defaultRetryWait + if cfg[retryWaitKey] != "" { + rwd, err := time.ParseDuration(cfg[retryWaitKey]) + if err != nil { + return config, err + } + retryWait = int(rwd.Seconds() * 1000) + } + + maxRetries := defaultMaxRetries + if cfg[maxRetriesKey] != "" { + mr64, err := strconv.ParseUint(cfg[maxRetriesKey], 10, strconv.IntSize) + if err != nil { + return config, err + } + maxRetries = int(mr64) + } + + if cfg[asyncKey] != "" && cfg[asyncConnectKey] != "" { + return config, errors.Errorf("conflicting options: cannot specify both '%s' and '%s", asyncKey, asyncConnectKey) + } + + async := false + if cfg[asyncKey] != "" { + if async, err = strconv.ParseBool(cfg[asyncKey]); err != nil { + return config, err + } + } + + // TODO fluentd-async-connect is deprecated in driver v1.4.0. Remove after two stable releases + asyncConnect := false + if cfg[asyncConnectKey] != "" { + if asyncConnect, err = strconv.ParseBool(cfg[asyncConnectKey]); err != nil { + return config, err + } + } + + subSecondPrecision := false + if cfg[subSecondPrecisionKey] != "" { + if subSecondPrecision, err = strconv.ParseBool(cfg[subSecondPrecisionKey]); err != nil { + return config, err + } + } + + requestAck := false + if cfg[requestAckKey] != "" { + if requestAck, err = strconv.ParseBool(cfg[requestAckKey]); err != nil { + return config, err + } + } + + config = fluent.Config{ + FluentPort: loc.port, + FluentHost: loc.host, + FluentNetwork: loc.protocol, + FluentSocketPath: loc.path, + BufferLimit: bufferLimit, + RetryWait: retryWait, + MaxRetry: maxRetries, + Async: async, + AsyncConnect: asyncConnect, + SubSecondPrecision: subSecondPrecision, + RequestAck: requestAck, + ForceStopAsyncSend: async || asyncConnect, + } + + return config, nil +} + func parseAddress(address string) (*location, error) { if address == "" { return &location{ diff --git a/daemon/logger/gcplogs/gcplogging.go b/daemon/logger/gcplogs/gcplogging.go index 1699f67a2d5be..4d1e74152f91b 100644 --- a/daemon/logger/gcplogs/gcplogging.go +++ b/daemon/logger/gcplogs/gcplogging.go @@ -18,14 +18,15 @@ import ( const ( name = "gcplogs" - projectOptKey = "gcp-project" - logLabelsKey = "labels" - logEnvKey = "env" - logEnvRegexKey = "env-regex" - logCmdKey = "gcp-log-cmd" - logZoneKey = "gcp-meta-zone" - logNameKey = "gcp-meta-name" - logIDKey = "gcp-meta-id" + projectOptKey = "gcp-project" + logLabelsKey = "labels" + logLabelsRegexKey = "labels-regex" + logEnvKey = "env" + logEnvRegexKey = "env-regex" + logCmdKey = "gcp-log-cmd" + logZoneKey = "gcp-meta-zone" + logNameKey = "gcp-meta-name" + logIDKey = "gcp-meta-id" ) var ( @@ -53,6 +54,7 @@ func init() { } type gcplogs struct { + client *logging.Client logger *logging.Logger instance *instanceInfo container *containerInfo @@ -169,6 +171,7 @@ func New(info logger.Info) (logger.Logger, error) { } l := &gcplogs{ + client: c, logger: lg, container: &containerInfo{ Name: info.ContainerName, @@ -210,7 +213,7 @@ func New(info logger.Info) (logger.Logger, error) { func ValidateLogOpts(cfg map[string]string) error { for k := range cfg { switch k { - case projectOptKey, logLabelsKey, logEnvKey, logEnvRegexKey, logCmdKey, logZoneKey, logNameKey, logIDKey: + case projectOptKey, logLabelsKey, logLabelsRegexKey, logEnvKey, logEnvRegexKey, logCmdKey, logZoneKey, logNameKey, logIDKey: default: return fmt.Errorf("%q is not a valid option for the gcplogs driver", k) } @@ -236,7 +239,7 @@ func (l *gcplogs) Log(m *logger.Message) error { func (l *gcplogs) Close() error { l.logger.Flush() - return nil + return l.client.Close() } func (l *gcplogs) Name() string { diff --git a/daemon/logger/gcplogs/gcplogging_linux.go b/daemon/logger/gcplogs/gcplogging_linux.go index 27f8ef32f5cd8..1c2160c41c5f5 100644 --- a/daemon/logger/gcplogs/gcplogging_linux.go +++ b/daemon/logger/gcplogs/gcplogging_linux.go @@ -11,17 +11,15 @@ import ( // ensureHomeIfIAmStatic ensure $HOME to be set if dockerversion.IAmStatic is "true". // See issue #29344: gcplogs segfaults (static binary) // If HOME is not set, logging.NewClient() will call os/user.Current() via oauth2/google. -// However, in static binary, os/user.Current() leads to segfault due to a glibc issue that won't be fixed -// in a short term. (golang/go#13470, https://sourceware.org/bugzilla/show_bug.cgi?id=19341) +// If compiling statically, make sure osusergo build tag is also used to prevent a segfault +// due to a glibc issue that won't be fixed in a short term +// (see golang/go#13470, https://sourceware.org/bugzilla/show_bug.cgi?id=19341). // So we forcibly set HOME so as to avoid call to os/user/Current() func ensureHomeIfIAmStatic() error { - // Note: dockerversion.IAmStatic and homedir.GetStatic() is only available for linux. + // Note: dockerversion.IAmStatic is only available for linux. // So we need to use them in this gcplogging_linux.go rather than in gcplogging.go if dockerversion.IAmStatic == "true" && os.Getenv("HOME") == "" { - home, err := homedir.GetStatic() - if err != nil { - return err - } + home := homedir.Get() logrus.Warnf("gcplogs requires HOME to be set for static daemon binary. Forcibly setting HOME to %s.", home) os.Setenv("HOME", home) } diff --git a/daemon/logger/gcplogs/gcplogging_others.go b/daemon/logger/gcplogs/gcplogging_others.go index 10a2cdc8cd7cb..55f43b0c0c712 100644 --- a/daemon/logger/gcplogs/gcplogging_others.go +++ b/daemon/logger/gcplogs/gcplogging_others.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package gcplogs // import "github.com/docker/docker/daemon/logger/gcplogs" diff --git a/daemon/logger/gelf/gelf.go b/daemon/logger/gelf/gelf.go index e9c860406a641..5492e5a4cebe8 100644 --- a/daemon/logger/gelf/gelf.go +++ b/daemon/logger/gelf/gelf.go @@ -166,6 +166,10 @@ func newGELFUDPWriter(address string, info logger.Info) (gelf.Writer, error) { } func (s *gelfLogger) Log(msg *logger.Message) error { + if len(msg.Line) == 0 { + return nil + } + level := gelf.LOG_INFO if msg.Source == "stderr" { level = gelf.LOG_ERR @@ -207,6 +211,7 @@ func ValidateLogOpt(cfg map[string]string) error { case "gelf-address": case "tag": case "labels": + case "labels-regex": case "env": case "env-regex": case "gelf-compression-level": diff --git a/daemon/logger/gelf/gelf_test.go b/daemon/logger/gelf/gelf_test.go index 3610bc6d3ac60..2b6e5f0f82563 100644 --- a/daemon/logger/gelf/gelf_test.go +++ b/daemon/logger/gelf/gelf_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package gelf // import "github.com/docker/docker/daemon/logger/gelf" @@ -98,6 +99,7 @@ func TestUDPValidateLogOpt(t *testing.T) { "gelf-address": "udp://127.0.0.1:12201", "tag": "testtag", "labels": "testlabel", + "labels-regex": "testlabel-regex", "env": "testenv", "env-regex": "testenv-regex", "gelf-compression-level": "9", diff --git a/daemon/logger/journald/journald.go b/daemon/logger/journald/journald.go index 113ed585c9964..e809ca722c121 100644 --- a/daemon/logger/journald/journald.go +++ b/daemon/logger/journald/journald.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux // Package journald provides the log driver for forwarding server logs @@ -6,10 +7,11 @@ package journald // import "github.com/docker/docker/daemon/logger/journald" import ( "fmt" + "strconv" "sync" "unicode" - "github.com/coreos/go-systemd/journal" + "github.com/coreos/go-systemd/v22/journal" "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/loggerutils" "github.com/sirupsen/logrus" @@ -18,10 +20,9 @@ import ( const name = "journald" type journald struct { - mu sync.Mutex + mu sync.Mutex //nolint:structcheck,unused vars map[string]string // additional variables and values to send to the journal along with the log message readers map[*logger.LogWatcher]struct{} - closed bool } func init() { @@ -71,6 +72,7 @@ func New(info logger.Info) (logger.Logger, error) { "CONTAINER_ID_FULL": info.ContainerID, "CONTAINER_NAME": info.Name(), "CONTAINER_TAG": tag, + "IMAGE_NAME": info.ImageName(), "SYSLOG_IDENTIFIER": tag, } extraAttrs, err := info.ExtraAttributes(sanitizeKeyMod) @@ -89,6 +91,7 @@ func validateLogOpt(cfg map[string]string) error { for key := range cfg { switch key { case "labels": + case "labels-regex": case "env": case "env-regex": case "tag": @@ -105,7 +108,12 @@ func (s *journald) Log(msg *logger.Message) error { vars[k] = v } if msg.PLogMetaData != nil { - vars["CONTAINER_PARTIAL_MESSAGE"] = "true" + vars["CONTAINER_PARTIAL_ID"] = msg.PLogMetaData.ID + vars["CONTAINER_PARTIAL_ORDINAL"] = strconv.Itoa(msg.PLogMetaData.Ordinal) + vars["CONTAINER_PARTIAL_LAST"] = strconv.FormatBool(msg.PLogMetaData.Last) + if !msg.PLogMetaData.Last { + vars["CONTAINER_PARTIAL_MESSAGE"] = "true" + } } line := string(msg.Line) diff --git a/daemon/logger/journald/journald_test.go b/daemon/logger/journald/journald_test.go index bd7bf7a3b382f..385e1db93df71 100644 --- a/daemon/logger/journald/journald_test.go +++ b/daemon/logger/journald/journald_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package journald // import "github.com/docker/docker/daemon/logger/journald" diff --git a/daemon/logger/journald/journald_unsupported.go b/daemon/logger/journald/journald_unsupported.go index 7899fc1214792..2dddc9e05387b 100644 --- a/daemon/logger/journald/journald_unsupported.go +++ b/daemon/logger/journald/journald_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package journald // import "github.com/docker/docker/daemon/logger/journald" diff --git a/daemon/logger/journald/read.go b/daemon/logger/journald/read.go index 4bddfd5781c7d..5a43ecf4ac393 100644 --- a/daemon/logger/journald/read.go +++ b/daemon/logger/journald/read.go @@ -1,3 +1,4 @@ +//go:build linux && cgo && !static_build && journald // +build linux,cgo,!static_build,journald package journald // import "github.com/docker/docker/daemon/logger/journald" @@ -101,61 +102,15 @@ package journald // import "github.com/docker/docker/daemon/logger/journald" // } // return rc; //} -//static int wait_for_data_cancelable(sd_journal *j, int pipefd) -//{ -// struct pollfd fds[2]; -// uint64_t when = 0; -// int timeout, jevents, i; -// struct timespec ts; -// uint64_t now; -// -// memset(&fds, 0, sizeof(fds)); -// fds[0].fd = pipefd; -// fds[0].events = POLLHUP; -// fds[1].fd = sd_journal_get_fd(j); -// if (fds[1].fd < 0) { -// return fds[1].fd; -// } -// -// do { -// jevents = sd_journal_get_events(j); -// if (jevents < 0) { -// return jevents; -// } -// fds[1].events = jevents; -// sd_journal_get_timeout(j, &when); -// if (when == -1) { -// timeout = -1; -// } else { -// clock_gettime(CLOCK_MONOTONIC, &ts); -// now = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; -// timeout = when > now ? (int) ((when - now + 999) / 1000) : 0; -// } -// i = poll(fds, 2, timeout); -// if ((i == -1) && (errno != EINTR)) { -// /* An unexpected error. */ -// return (errno != 0) ? -errno : -EINTR; -// } -// if (fds[0].revents & POLLHUP) { -// /* The close notification pipe was closed. */ -// return 0; -// } -// if (sd_journal_process(j) == SD_JOURNAL_APPEND) { -// /* Data, which we might care about, was appended. */ -// return 1; -// } -// } while ((fds[0].revents & POLLHUP) == 0); -// return 0; -//} import "C" import ( - "fmt" + "errors" "strings" "time" "unsafe" - "github.com/coreos/go-systemd/journal" + "github.com/coreos/go-systemd/v22/journal" "github.com/docker/docker/api/types/backend" "github.com/docker/docker/daemon/logger" "github.com/sirupsen/logrus" @@ -163,22 +118,29 @@ import ( func (s *journald) Close() error { s.mu.Lock() - s.closed = true for r := range s.readers { r.ProducerGone() delete(s.readers, r) - } s.mu.Unlock() return nil } -func (s *journald) drainJournal(logWatcher *logger.LogWatcher, j *C.sd_journal, oldCursor *C.char, untilUnixMicro uint64) (*C.char, bool) { - var msg, data, cursor *C.char - var length C.size_t - var stamp C.uint64_t - var priority, partial C.int - var done bool +// CErr converts error code returned from a sd_journal_* function +// (which returns -errno) to a string +func CErr(ret C.int) string { + return C.GoString(C.strerror(-ret)) +} + +func (s *journald) drainJournal(logWatcher *logger.LogWatcher, j *C.sd_journal, oldCursor *C.char, untilUnixMicro uint64) (*C.char, bool, int) { + var ( + msg, data, cursor *C.char + length C.size_t + stamp C.uint64_t + priority, partial C.int + done bool + shown int + ) // Walk the journal from here forward until we run out of new entries // or we reach the until value (if provided). @@ -216,12 +178,12 @@ drain: // the stream that we would have // assigned that value. source := "" - if C.get_priority(j, &priority) != 0 { - source = "" - } else if priority == C.int(journal.PriErr) { - source = "stderr" - } else if priority == C.int(journal.PriInfo) { - source = "stdout" + if C.get_priority(j, &priority) == 0 { + if priority == C.int(journal.PriErr) { + source = "stderr" + } else if priority == C.int(journal.PriInfo) { + source = "stdout" + } } // Retrieve the values of any variables we're adding to the journal. var attrs []backend.LogAttr @@ -230,12 +192,29 @@ drain: kv := strings.SplitN(C.GoStringN(data, C.int(length)), "=", 2) attrs = append(attrs, backend.LogAttr{Key: kv[0], Value: kv[1]}) } - // Send the log message. - logWatcher.Msg <- &logger.Message{ + + // Send the log message, unless the consumer is gone + select { + case <-logWatcher.WatchConsumerGone(): + done = true // we won't be able to write anything anymore + break drain + case logWatcher.Msg <- &logger.Message{ Line: line, Source: source, Timestamp: timestamp.In(time.UTC), Attrs: attrs, + }: + shown++ + } + // Call sd_journal_process() periodically during the processing loop + // to close any opened file descriptors for rotated (deleted) journal files. + if shown%1024 == 0 { + if ret := C.sd_journal_process(j); ret < 0 { + // log a warning but ignore it for now + logrus.WithField("container", s.vars["CONTAINER_ID_FULL"]). + WithField("error", CErr(ret)). + Warn("journald: error processing journal") + } } } // If we're at the end of the journal, we're done (for now). @@ -250,104 +229,93 @@ drain: // ensure that we won't be freeing an address that's invalid cursor = nil } - return cursor, done + return cursor, done, shown } -func (s *journald) followJournal(logWatcher *logger.LogWatcher, j *C.sd_journal, pfd [2]C.int, cursor *C.char, untilUnixMicro uint64) *C.char { +func (s *journald) followJournal(logWatcher *logger.LogWatcher, j *C.sd_journal, cursor *C.char, untilUnixMicro uint64) *C.char { s.mu.Lock() s.readers[logWatcher] = struct{}{} - if s.closed { - // the journald Logger is closed, presumably because the container has been - // reset. So we shouldn't follow, because we'll never be woken up. But we - // should make one more drainJournal call to be sure we've got all the logs. - // Close pfd[1] so that one drainJournal happens, then cleanup, then return. - C.close(pfd[1]) - } s.mu.Unlock() - newCursor := make(chan *C.char) - - go func() { - for { - // Keep copying journal data out until we're notified to stop - // or we hit an error. - status := C.wait_for_data_cancelable(j, pfd[0]) - if status < 0 { - cerrstr := C.strerror(C.int(-status)) - errstr := C.GoString(cerrstr) - fmtstr := "error %q while attempting to follow journal for container %q" - logrus.Errorf(fmtstr, errstr, s.vars["CONTAINER_ID_FULL"]) - break - } + waitTimeout := C.uint64_t(250000) // 0.25s - var done bool - cursor, done = s.drainJournal(logWatcher, j, cursor, untilUnixMicro) - - if status != 1 || done { - // We were notified to stop - break + for { + status := C.sd_journal_wait(j, waitTimeout) + if status < 0 { + logWatcher.Err <- errors.New("error waiting for journal: " + CErr(status)) + goto cleanup + } + select { + case <-logWatcher.WatchConsumerGone(): + goto cleanup // won't be able to write anything anymore + case <-logWatcher.WatchProducerGone(): + // container is gone, drain journal + default: + // container is still alive + if status == C.SD_JOURNAL_NOP { + // no new data -- keep waiting + continue } } - - // Clean up. - C.close(pfd[0]) - s.mu.Lock() - delete(s.readers, logWatcher) - s.mu.Unlock() - close(logWatcher.Msg) - newCursor <- cursor - }() - - // Wait until we're told to stop. - select { - case cursor = <-newCursor: - case <-logWatcher.WatchConsumerGone(): - // Notify the other goroutine that its work is done. - C.close(pfd[1]) - cursor = <-newCursor + newCursor, done, recv := s.drainJournal(logWatcher, j, cursor, untilUnixMicro) + cursor = newCursor + if done || (status == C.SD_JOURNAL_NOP && recv == 0) { + break + } } +cleanup: + s.mu.Lock() + delete(s.readers, logWatcher) + s.mu.Unlock() + close(logWatcher.Msg) return cursor } func (s *journald) readLogs(logWatcher *logger.LogWatcher, config logger.ReadConfig) { - var j *C.sd_journal - var cmatch, cursor *C.char - var stamp C.uint64_t - var sinceUnixMicro uint64 - var untilUnixMicro uint64 - var pipes [2]C.int + var ( + j *C.sd_journal + cmatch, cursor *C.char + stamp C.uint64_t + sinceUnixMicro uint64 + untilUnixMicro uint64 + ) // Get a handle to the journal. - rc := C.sd_journal_open(&j, C.int(0)) - if rc != 0 { - logWatcher.Err <- fmt.Errorf("error opening journal") + if rc := C.sd_journal_open(&j, C.int(0)); rc != 0 { + logWatcher.Err <- errors.New("error opening journal: " + CErr(rc)) close(logWatcher.Msg) return } + if config.Follow { + // Initialize library inotify watches early + if rc := C.sd_journal_get_fd(j); rc < 0 { + logWatcher.Err <- errors.New("error getting journald fd: " + CErr(rc)) + close(logWatcher.Msg) + return + } + } // If we end up following the log, we can set the journal context // pointer and the channel pointer to nil so that we won't close them // here, potentially while the goroutine that uses them is still // running. Otherwise, close them when we return from this function. following := false - defer func(pfollowing *bool) { - if !*pfollowing { + defer func() { + if !following { close(logWatcher.Msg) } C.sd_journal_close(j) - }(&following) + }() // Remove limits on the size of data items that we'll retrieve. - rc = C.sd_journal_set_data_threshold(j, C.size_t(0)) - if rc != 0 { - logWatcher.Err <- fmt.Errorf("error setting journal data threshold") + if rc := C.sd_journal_set_data_threshold(j, C.size_t(0)); rc != 0 { + logWatcher.Err <- errors.New("error setting journal data threshold: " + CErr(rc)) return } // Add a match to have the library do the searching for us. cmatch = C.CString("CONTAINER_ID_FULL=" + s.vars["CONTAINER_ID_FULL"]) defer C.free(unsafe.Pointer(cmatch)) - rc = C.sd_journal_add_match(j, unsafe.Pointer(cmatch), C.strlen(cmatch)) - if rc != 0 { - logWatcher.Err <- fmt.Errorf("error setting journal match") + if rc := C.sd_journal_add_match(j, unsafe.Pointer(cmatch), C.strlen(cmatch)); rc != 0 { + logWatcher.Err <- errors.New("error setting journal match: " + CErr(rc)) return } // If we have a cutoff time, convert it to Unix time once. @@ -360,80 +328,56 @@ func (s *journald) readLogs(logWatcher *logger.LogWatcher, config logger.ReadCon nano := config.Until.UnixNano() untilUnixMicro = uint64(nano / 1000) } - if config.Tail > 0 { - lines := config.Tail + if config.Tail >= 0 { // If until time provided, start from there. // Otherwise start at the end of the journal. - if untilUnixMicro != 0 && C.sd_journal_seek_realtime_usec(j, C.uint64_t(untilUnixMicro)) < 0 { - logWatcher.Err <- fmt.Errorf("error seeking provided until value") - return - } else if C.sd_journal_seek_tail(j) < 0 { - logWatcher.Err <- fmt.Errorf("error seeking to end of journal") - return - } - if C.sd_journal_previous(j) < 0 { - logWatcher.Err <- fmt.Errorf("error backtracking to previous journal entry") + if untilUnixMicro != 0 { + if rc := C.sd_journal_seek_realtime_usec(j, C.uint64_t(untilUnixMicro)); rc != 0 { + logWatcher.Err <- errors.New("error seeking provided until value: " + CErr(rc)) + return + } + } else if rc := C.sd_journal_seek_tail(j); rc != 0 { + logWatcher.Err <- errors.New("error seeking to end of journal: " + CErr(rc)) return } - // Walk backward. - for lines > 0 { - // Stop if the entry time is before our cutoff. - // We'll need the entry time if it isn't, so go - // ahead and parse it now. - if C.sd_journal_get_realtime_usec(j, &stamp) != 0 { - break - } else { - // Compare the timestamp on the entry to our threshold value. - if sinceUnixMicro != 0 && sinceUnixMicro > uint64(stamp) { - break - } - } - lines-- - // If we're at the start of the journal, or - // don't need to back up past any more entries, - // stop. - if lines == 0 || C.sd_journal_previous(j) <= 0 { - break + // (Try to) skip backwards by the requested number of lines... + if C.sd_journal_previous_skip(j, C.uint64_t(config.Tail)) >= 0 { + // ...but not before "since" + if sinceUnixMicro != 0 && + C.sd_journal_get_realtime_usec(j, &stamp) == 0 && + uint64(stamp) < sinceUnixMicro { + C.sd_journal_seek_realtime_usec(j, C.uint64_t(sinceUnixMicro)) } } } else { // Start at the beginning of the journal. - if C.sd_journal_seek_head(j) < 0 { - logWatcher.Err <- fmt.Errorf("error seeking to start of journal") + if rc := C.sd_journal_seek_head(j); rc != 0 { + logWatcher.Err <- errors.New("error seeking to start of journal: " + CErr(rc)) return } // If we have a cutoff date, fast-forward to it. - if sinceUnixMicro != 0 && C.sd_journal_seek_realtime_usec(j, C.uint64_t(sinceUnixMicro)) != 0 { - logWatcher.Err <- fmt.Errorf("error seeking to start time in journal") - return + if sinceUnixMicro != 0 { + if rc := C.sd_journal_seek_realtime_usec(j, C.uint64_t(sinceUnixMicro)); rc != 0 { + logWatcher.Err <- errors.New("error seeking to start time in journal: " + CErr(rc)) + return + } } - if C.sd_journal_next(j) < 0 { - logWatcher.Err <- fmt.Errorf("error skipping to next journal entry") + if rc := C.sd_journal_next(j); rc < 0 { + logWatcher.Err <- errors.New("error skipping to next journal entry: " + CErr(rc)) return } } - cursor, _ = s.drainJournal(logWatcher, j, nil, untilUnixMicro) + if config.Tail != 0 { // special case for --tail 0 + cursor, _, _ = s.drainJournal(logWatcher, j, nil, untilUnixMicro) + } if config.Follow { - // Allocate a descriptor for following the journal, if we'll - // need one. Do it here so that we can report if it fails. - if fd := C.sd_journal_get_fd(j); fd < C.int(0) { - logWatcher.Err <- fmt.Errorf("error opening journald follow descriptor: %q", C.GoString(C.strerror(-fd))) - } else { - // Create a pipe that we can poll at the same time as - // the journald descriptor. - if C.pipe(&pipes[0]) == C.int(-1) { - logWatcher.Err <- fmt.Errorf("error opening journald close notification pipe") - } else { - cursor = s.followJournal(logWatcher, j, pipes, cursor, untilUnixMicro) - // Let followJournal handle freeing the journal context - // object and closing the channel. - following = true - } - } + cursor = s.followJournal(logWatcher, j, cursor, untilUnixMicro) + // Let followJournal handle freeing the journal context + // object and closing the channel. + following = true } C.free(unsafe.Pointer(cursor)) - return } func (s *journald) ReadLogs(config logger.ReadConfig) *logger.LogWatcher { diff --git a/daemon/logger/journald/read_native.go b/daemon/logger/journald/read_native.go index ab68cf4ba7524..ce1de3fbde578 100644 --- a/daemon/logger/journald/read_native.go +++ b/daemon/logger/journald/read_native.go @@ -1,3 +1,4 @@ +//go:build linux && cgo && !static_build && journald && !journald_compat // +build linux,cgo,!static_build,journald,!journald_compat package journald // import "github.com/docker/docker/daemon/logger/journald" diff --git a/daemon/logger/journald/read_native_compat.go b/daemon/logger/journald/read_native_compat.go index 4806e130ef962..98765911315b0 100644 --- a/daemon/logger/journald/read_native_compat.go +++ b/daemon/logger/journald/read_native_compat.go @@ -1,3 +1,4 @@ +//go:build linux && cgo && !static_build && journald && journald_compat // +build linux,cgo,!static_build,journald,journald_compat package journald // import "github.com/docker/docker/daemon/logger/journald" diff --git a/daemon/logger/journald/read_unsupported.go b/daemon/logger/journald/read_unsupported.go index a66b666659068..199d7683a6b74 100644 --- a/daemon/logger/journald/read_unsupported.go +++ b/daemon/logger/journald/read_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux || !cgo || static_build || !journald // +build !linux !cgo static_build !journald package journald // import "github.com/docker/docker/daemon/logger/journald" diff --git a/daemon/logger/jsonfilelog/jsonfilelog.go b/daemon/logger/jsonfilelog/jsonfilelog.go index bbb8eeb7ec3a9..e66a6b8266f43 100644 --- a/daemon/logger/jsonfilelog/jsonfilelog.go +++ b/daemon/logger/jsonfilelog/jsonfilelog.go @@ -13,7 +13,7 @@ import ( "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/jsonfilelog/jsonlog" "github.com/docker/docker/daemon/logger/loggerutils" - "github.com/docker/go-units" + units "github.com/docker/go-units" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -156,6 +156,7 @@ func ValidateLogOpt(cfg map[string]string) error { case "max-size": case "compress": case "labels": + case "labels-regex": case "env": case "env-regex": case "tag": diff --git a/daemon/logger/jsonfilelog/jsonfilelog_test.go b/daemon/logger/jsonfilelog/jsonfilelog_test.go index 8e66e6455a4b5..3ccf1d1a96995 100644 --- a/daemon/logger/jsonfilelog/jsonfilelog_test.go +++ b/daemon/logger/jsonfilelog/jsonfilelog_test.go @@ -5,7 +5,7 @@ import ( "compress/gzip" "encoding/json" "fmt" - "io/ioutil" + "io" "os" "path/filepath" "reflect" @@ -15,14 +15,14 @@ import ( "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/jsonfilelog/jsonlog" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/fs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/fs" ) func TestJSONFileLogger(t *testing.T) { cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657" - tmp, err := ioutil.TempDir("", "docker-logger-") + tmp, err := os.MkdirTemp("", "docker-logger-") if err != nil { t.Fatal(err) } @@ -46,7 +46,7 @@ func TestJSONFileLogger(t *testing.T) { if err := l.Log(&logger.Message{Line: []byte("line3"), Source: "src3"}); err != nil { t.Fatal(err) } - res, err := ioutil.ReadFile(filename) + res, err := os.ReadFile(filename) if err != nil { t.Fatal(err) } @@ -63,7 +63,7 @@ func TestJSONFileLogger(t *testing.T) { func TestJSONFileLoggerWithTags(t *testing.T) { cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657" cname := "test-container" - tmp, err := ioutil.TempDir("", "docker-logger-") + tmp, err := os.MkdirTemp("", "docker-logger-") assert.NilError(t, err) @@ -90,7 +90,7 @@ func TestJSONFileLoggerWithTags(t *testing.T) { err = l.Log(&logger.Message{Line: []byte("line3"), Source: "src3"}) assert.NilError(t, err) - res, err := ioutil.ReadFile(filename) + res, err := os.ReadFile(filename) assert.NilError(t, err) expected := `{"log":"line1\n","stream":"src1","attrs":{"tag":"a7317399f3f8/test-container"},"time":"0001-01-01T00:00:00Z"} @@ -154,7 +154,7 @@ func BenchmarkJSONFileLoggerLog(b *testing.B) { func TestJSONFileLoggerWithOpts(t *testing.T) { cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657" - tmp, err := ioutil.TempDir("", "docker-logger-") + tmp, err := os.MkdirTemp("", "docker-logger-") if err != nil { t.Fatal(err) } @@ -176,44 +176,44 @@ func TestJSONFileLoggerWithOpts(t *testing.T) { } } - res, err := ioutil.ReadFile(filename) + res, err := os.ReadFile(filename) if err != nil { t.Fatal(err) } - penUlt, err := ioutil.ReadFile(filename + ".1") + penUlt, err := os.ReadFile(filename + ".1") if err != nil { if !os.IsNotExist(err) { t.Fatal(err) } file, err := os.Open(filename + ".1.gz") - defer file.Close() if err != nil { t.Fatal(err) } + defer file.Close() zipReader, err := gzip.NewReader(file) - defer zipReader.Close() if err != nil { t.Fatal(err) } - penUlt, err = ioutil.ReadAll(zipReader) + defer zipReader.Close() + penUlt, err = io.ReadAll(zipReader) if err != nil { t.Fatal(err) } } file, err := os.Open(filename + ".2.gz") - defer file.Close() if err != nil { t.Fatal(err) } + defer file.Close() zipReader, err := gzip.NewReader(file) - defer zipReader.Close() if err != nil { t.Fatal(err) } - antepenult, err := ioutil.ReadAll(zipReader) + defer zipReader.Close() + antepenult, err := io.ReadAll(zipReader) if err != nil { t.Fatal(err) } @@ -271,18 +271,18 @@ func TestJSONFileLoggerWithOpts(t *testing.T) { func TestJSONFileLoggerWithLabelsEnv(t *testing.T) { cid := "a7317399f3f857173c6179d44823594f8294678dea9999662e5c625b5a1c7657" - tmp, err := ioutil.TempDir("", "docker-logger-") + tmp, err := os.MkdirTemp("", "docker-logger-") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmp) filename := filepath.Join(tmp, "container.log") - config := map[string]string{"labels": "rack,dc", "env": "environ,debug,ssl", "env-regex": "^dc"} + config := map[string]string{"labels": "rack,dc", "labels-regex": "^loc", "env": "environ,debug,ssl", "env-regex": "^dc"} l, err := New(logger.Info{ ContainerID: cid, LogPath: filename, Config: config, - ContainerLabels: map[string]string{"rack": "101", "dc": "lhr"}, + ContainerLabels: map[string]string{"rack": "101", "dc": "lhr", "location": "here"}, ContainerEnv: []string{"environ=production", "debug=false", "port=10001", "ssl=true", "dc_region=west"}, }) if err != nil { @@ -292,7 +292,7 @@ func TestJSONFileLoggerWithLabelsEnv(t *testing.T) { if err := l.Log(&logger.Message{Line: []byte("line"), Source: "src1"}); err != nil { t.Fatal(err) } - res, err := ioutil.ReadFile(filename) + res, err := os.ReadFile(filename) if err != nil { t.Fatal(err) } @@ -308,6 +308,7 @@ func TestJSONFileLoggerWithLabelsEnv(t *testing.T) { expected := map[string]string{ "rack": "101", "dc": "lhr", + "location": "here", "environ": "production", "debug": "false", "ssl": "true", diff --git a/daemon/logger/jsonfilelog/jsonlog/jsonlog.go b/daemon/logger/jsonfilelog/jsonlog/jsonlog.go index 74be8e7da0a19..050900197f04f 100644 --- a/daemon/logger/jsonfilelog/jsonlog/jsonlog.go +++ b/daemon/logger/jsonfilelog/jsonlog/jsonlog.go @@ -21,5 +21,7 @@ func (jl *JSONLog) Reset() { jl.Log = "" jl.Stream = "" jl.Created = time.Time{} - jl.Attrs = make(map[string]string) + for k := range jl.Attrs { + delete(jl.Attrs, k) + } } diff --git a/daemon/logger/jsonfilelog/jsonlog/jsonlogbytes_test.go b/daemon/logger/jsonfilelog/jsonlog/jsonlogbytes_test.go index d268db4df0c97..125c2b057635b 100644 --- a/daemon/logger/jsonfilelog/jsonlog/jsonlogbytes_test.go +++ b/daemon/logger/jsonfilelog/jsonlog/jsonlogbytes_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestJSONLogsMarshalJSONBuf(t *testing.T) { diff --git a/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go b/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go index b3959b0467a50..ee2a2709fd177 100644 --- a/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go +++ b/daemon/logger/jsonfilelog/jsonlog/time_marshalling_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestFastTimeMarshalJSONWithInvalidYear(t *testing.T) { diff --git a/daemon/logger/jsonfilelog/read.go b/daemon/logger/jsonfilelog/read.go index 12f676bb1a31c..5099a0b947b8f 100644 --- a/daemon/logger/jsonfilelog/read.go +++ b/daemon/logger/jsonfilelog/read.go @@ -60,35 +60,110 @@ func decodeLogLine(dec *json.Decoder, l *jsonlog.JSONLog) (*logger.Message, erro return msg, nil } -// decodeFunc is used to create a decoder for the log file reader -func decodeFunc(rdr io.Reader) func() (*logger.Message, error) { - l := &jsonlog.JSONLog{} - dec := json.NewDecoder(rdr) - return func() (msg *logger.Message, err error) { - for retries := 0; retries < maxJSONDecodeRetry; retries++ { - msg, err = decodeLogLine(dec, l) - if err == nil || err == io.EOF { - break - } +type decoder struct { + rdr io.Reader + dec *json.Decoder + jl *jsonlog.JSONLog + maxRetry int +} - logrus.WithError(err).WithField("retries", retries).Warn("got error while decoding json") - // try again, could be due to a an incomplete json object as we read - if _, ok := err.(*json.SyntaxError); ok { - dec = json.NewDecoder(rdr) - continue - } +func (d *decoder) Reset(rdr io.Reader) { + d.rdr = rdr + d.dec = nil + if d.jl != nil { + d.jl.Reset() + } +} + +func (d *decoder) Close() { + d.dec = nil + d.rdr = nil + d.jl = nil +} + +func (d *decoder) Decode() (msg *logger.Message, err error) { + if d.dec == nil { + d.dec = json.NewDecoder(d.rdr) + } + if d.jl == nil { + d.jl = &jsonlog.JSONLog{} + } + if d.maxRetry == 0 { + // We aren't using maxJSONDecodeRetry directly so we can give a custom value for testing. + d.maxRetry = maxJSONDecodeRetry + } + for retries := 0; retries < d.maxRetry; retries++ { + msg, err = decodeLogLine(d.dec, d.jl) + if err == nil || err == io.EOF { + break + } + + logrus.WithError(err).WithField("retries", retries).Warn("got error while decoding json") + // try again, could be due to a an incomplete json object as we read + if _, ok := err.(*json.SyntaxError); ok { + d.dec = json.NewDecoder(d.rdr) + continue + } + + // io.ErrUnexpectedEOF is returned from json.Decoder when there is + // remaining data in the parser's buffer while an io.EOF occurs. + // If the json logger writes a partial json log entry to the disk + // while at the same time the decoder tries to decode it, the race condition happens. + if err == io.ErrUnexpectedEOF { + d.rdr = combineReaders(d.dec.Buffered(), d.rdr) + d.dec = json.NewDecoder(d.rdr) + continue + } + } + return msg, err +} + +func combineReaders(pre, rdr io.Reader) io.Reader { + return &combinedReader{pre: pre, rdr: rdr} +} + +// combinedReader is a reader which is like `io.MultiReader` where except it does not cache a full EOF. +// Once `io.MultiReader` returns EOF, it is always EOF. +// +// For this usecase we have an underlying reader which is a file which may reach EOF but have more data written to it later. +// As such, io.MultiReader does not work for us. +type combinedReader struct { + pre io.Reader + rdr io.Reader +} - // io.ErrUnexpectedEOF is returned from json.Decoder when there is - // remaining data in the parser's buffer while an io.EOF occurs. - // If the json logger writes a partial json log entry to the disk - // while at the same time the decoder tries to decode it, the race condition happens. - if err == io.ErrUnexpectedEOF { - reader := io.MultiReader(dec.Buffered(), rdr) - dec = json.NewDecoder(reader) - continue +func (r *combinedReader) Read(p []byte) (int, error) { + var read int + if r.pre != nil { + n, err := r.pre.Read(p) + if err != nil { + if err != io.EOF { + return n, err } + r.pre = nil + } + read = n + } + + if read < len(p) { + n, err := r.rdr.Read(p[read:]) + if n > 0 { + read += n } - return msg, err + if err != nil { + return read, err + } + } + + return read, nil +} + +// decodeFunc is used to create a decoder for the log file reader +func decodeFunc(rdr io.Reader) loggerutils.Decoder { + return &decoder{ + rdr: rdr, + dec: nil, + jl: nil, } } diff --git a/daemon/logger/jsonfilelog/read_test.go b/daemon/logger/jsonfilelog/read_test.go index cfa8694b19ef9..1f0a295f63bad 100644 --- a/daemon/logger/jsonfilelog/read_test.go +++ b/daemon/logger/jsonfilelog/read_test.go @@ -2,13 +2,14 @@ package jsonfilelog // import "github.com/docker/docker/daemon/logger/jsonfilelo import ( "bytes" + "encoding/json" "io" "testing" "time" "github.com/docker/docker/daemon/logger" - "gotest.tools/assert" - "gotest.tools/fs" + "gotest.tools/v3/assert" + "gotest.tools/v3/fs" ) func BenchmarkJSONFileLoggerReadLogs(b *testing.B) { @@ -75,19 +76,79 @@ func TestEncodeDecode(t *testing.T) { assert.Assert(t, marshalMessage(m2, nil, buf)) assert.Assert(t, marshalMessage(m3, nil, buf)) - decode := decodeFunc(buf) - msg, err := decode() - assert.Assert(t, err) + dec := decodeFunc(buf) + defer dec.Close() + + msg, err := dec.Decode() + assert.NilError(t, err) assert.Assert(t, string(msg.Line) == "hello 1\n", string(msg.Line)) - msg, err = decode() - assert.Assert(t, err) + msg, err = dec.Decode() + assert.NilError(t, err) assert.Assert(t, string(msg.Line) == "hello 2\n") - msg, err = decode() - assert.Assert(t, err) + msg, err = dec.Decode() + assert.NilError(t, err) assert.Assert(t, string(msg.Line) == "hello 3\n") - _, err = decode() + _, err = dec.Decode() assert.Assert(t, err == io.EOF) } + +func TestUnexpectedEOF(t *testing.T) { + buf := bytes.NewBuffer(nil) + msg1 := &logger.Message{Timestamp: time.Now(), Line: []byte("hello1")} + msg2 := &logger.Message{Timestamp: time.Now(), Line: []byte("hello2")} + + err := marshalMessage(msg1, json.RawMessage{}, buf) + assert.NilError(t, err) + err = marshalMessage(msg2, json.RawMessage{}, buf) + assert.NilError(t, err) + + r := &readerWithErr{ + err: io.EOF, + after: buf.Len() / 4, + r: buf, + } + dec := &decoder{rdr: r, maxRetry: 1} + + _, err = dec.Decode() + assert.Error(t, err, io.ErrUnexpectedEOF.Error()) + // again just to check + _, err = dec.Decode() + assert.Error(t, err, io.ErrUnexpectedEOF.Error()) + + // reset the error + // from here all reads should succeed until we get EOF on the underlying reader + r.err = nil + + msg, err := dec.Decode() + assert.NilError(t, err) + assert.Equal(t, string(msg1.Line)+"\n", string(msg.Line)) + + msg, err = dec.Decode() + assert.NilError(t, err) + assert.Equal(t, string(msg2.Line)+"\n", string(msg.Line)) + + _, err = dec.Decode() + assert.Error(t, err, io.EOF.Error()) +} + +type readerWithErr struct { + err error + after int + r io.Reader + read int +} + +func (r *readerWithErr) Read(p []byte) (int, error) { + if r.err != nil && r.read > r.after { + return 0, r.err + } + + n, err := r.r.Read(p[:1]) + if n > 0 { + r.read += n + } + return n, err +} diff --git a/daemon/logger/local/local.go b/daemon/logger/local/local.go index ba4aa096f7104..f4411d087ab89 100644 --- a/daemon/logger/local/local.go +++ b/daemon/logger/local/local.go @@ -12,7 +12,7 @@ import ( "github.com/docker/docker/daemon/logger" "github.com/docker/docker/daemon/logger/loggerutils" "github.com/docker/docker/errdefs" - "github.com/docker/go-units" + units "github.com/docker/go-units" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -109,7 +109,7 @@ func makeMarshaller() func(m *logger.Message) ([]byte, error) { messageToProto(m, proto, md) protoSize := proto.Size() - writeLen := protoSize + (2 * encodeBinaryLen) //+ len(messageDelimiter) + writeLen := protoSize + (2 * encodeBinaryLen) // + len(messageDelimiter) if writeLen > len(buf) { buf = make([]byte, writeLen) diff --git a/daemon/logger/local/local_test.go b/daemon/logger/local/local_test.go index 2da67bc6fb59d..ca313184991d7 100644 --- a/daemon/logger/local/local_test.go +++ b/daemon/logger/local/local_test.go @@ -1,40 +1,36 @@ package local import ( + "bytes" "context" "encoding/binary" - "io/ioutil" + "fmt" + "io" "os" "path/filepath" + "strings" "testing" "time" - "bytes" - "fmt" - - "strings" - - "io" - "github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/plugins/logdriver" "github.com/docker/docker/daemon/logger" protoio "github.com/gogo/protobuf/io" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestWriteLog(t *testing.T) { t.Parallel() - dir, err := ioutil.TempDir("", t.Name()) - assert.Assert(t, err) + dir, err := os.MkdirTemp("", t.Name()) + assert.NilError(t, err) defer os.RemoveAll(dir) logPath := filepath.Join(dir, "test.log") l, err := New(logger.Info{LogPath: logPath}) - assert.Assert(t, err) + assert.NilError(t, err) defer l.Close() m1 := logger.Message{Source: "stdout", Timestamp: time.Now().Add(-1 * 30 * time.Minute), Line: []byte("message 1")} @@ -43,14 +39,14 @@ func TestWriteLog(t *testing.T) { // copy the log message because the underying log writer resets the log message and returns it to a buffer pool err = l.Log(copyLogMessage(&m1)) - assert.Assert(t, err) + assert.NilError(t, err) err = l.Log(copyLogMessage(&m2)) - assert.Assert(t, err) + assert.NilError(t, err) err = l.Log(copyLogMessage(&m3)) - assert.Assert(t, err) + assert.NilError(t, err) f, err := os.Open(logPath) - assert.Assert(t, err) + assert.NilError(t, err) defer f.Close() dec := protoio.NewUint32DelimitedReader(f, binary.BigEndian, 1e6) @@ -66,19 +62,19 @@ func TestWriteLog(t *testing.T) { } err = dec.ReadMsg(&proto) - assert.Assert(t, err) + assert.NilError(t, err) messageToProto(&m1, &testProto, &partial) assert.Check(t, is.DeepEqual(testProto, proto), "expected:\n%+v\ngot:\n%+v", testProto, proto) seekMsgLen() err = dec.ReadMsg(&proto) - assert.Assert(t, err) + assert.NilError(t, err) messageToProto(&m2, &testProto, &partial) assert.Check(t, is.DeepEqual(testProto, proto)) seekMsgLen() err = dec.ReadMsg(&proto) - assert.Assert(t, err) + assert.NilError(t, err) messageToProto(&m3, &testProto, &partial) assert.Check(t, is.DeepEqual(testProto, proto), "expected:\n%+v\ngot:\n%+v", testProto, proto) } @@ -86,13 +82,13 @@ func TestWriteLog(t *testing.T) { func TestReadLog(t *testing.T) { t.Parallel() - dir, err := ioutil.TempDir("", t.Name()) - assert.Assert(t, err) + dir, err := os.MkdirTemp("", t.Name()) + assert.NilError(t, err) defer os.RemoveAll(dir) logPath := filepath.Join(dir, "test.log") l, err := New(logger.Info{LogPath: logPath}) - assert.Assert(t, err) + assert.NilError(t, err) defer l.Close() m1 := logger.Message{Source: "stdout", Timestamp: time.Now().Add(-1 * 30 * time.Minute), Line: []byte("a message")} @@ -103,13 +99,13 @@ func TestReadLog(t *testing.T) { // copy the log message because the underlying log writer resets the log message and returns it to a buffer pool err = l.Log(copyLogMessage(&m1)) - assert.Assert(t, err) + assert.NilError(t, err) err = l.Log(copyLogMessage(&m2)) - assert.Assert(t, err) + assert.NilError(t, err) err = l.Log(copyLogMessage(&m3)) - assert.Assert(t, err) + assert.NilError(t, err) err = l.Log(copyLogMessage(&m4)) - assert.Assert(t, err) + assert.NilError(t, err) lr := l.(logger.LogReader) @@ -121,12 +117,12 @@ func TestReadLog(t *testing.T) { case <-ctx.Done(): assert.Assert(t, ctx.Err()) case err := <-lw.Err: - assert.Assert(t, err) + assert.NilError(t, err) case msg, open := <-lw.Msg: if !open { select { case err := <-lw.Err: - assert.Assert(t, err) + assert.NilError(t, err) default: assert.Assert(t, m == nil) return @@ -173,7 +169,7 @@ func TestReadLog(t *testing.T) { } func BenchmarkLogWrite(b *testing.B) { - f, err := ioutil.TempFile("", b.Name()) + f, err := os.CreateTemp("", b.Name()) assert.Assert(b, err) defer os.Remove(f.Name()) f.Close() @@ -214,7 +210,8 @@ func copyLogMessage(src *logger.Message) *logger.Message { dst.Err = src.Err dst.Line = append(dst.Line, src.Line...) if src.PLogMetaData != nil { - dst.PLogMetaData = &(*src.PLogMetaData) + lmd := *src.PLogMetaData + dst.PLogMetaData = &lmd } return dst } diff --git a/daemon/logger/local/read.go b/daemon/logger/local/read.go index a752de2a8d541..b28aa90651bc3 100644 --- a/daemon/logger/local/read.go +++ b/daemon/logger/local/read.go @@ -96,51 +96,81 @@ func getTailReader(ctx context.Context, r loggerutils.SizeReaderAt, req int) (io return io.NewSectionReader(r, offset, size), found, nil } -func decodeFunc(rdr io.Reader) func() (*logger.Message, error) { - proto := &logdriver.LogEntry{} - buf := make([]byte, initialBufSize) - - return func() (*logger.Message, error) { - var ( - read int - err error - ) - - resetProto(proto) - - for i := 0; i < maxDecodeRetry; i++ { - var n int - n, err = io.ReadFull(rdr, buf[read:encodeBinaryLen]) - if err != nil { - if err != io.ErrUnexpectedEOF { - return nil, errors.Wrap(err, "error reading log message length") - } - read += n - continue +type decoder struct { + rdr io.Reader + proto *logdriver.LogEntry + buf []byte +} + +func (d *decoder) Decode() (*logger.Message, error) { + if d.proto == nil { + d.proto = &logdriver.LogEntry{} + } else { + resetProto(d.proto) + } + if d.buf == nil { + d.buf = make([]byte, initialBufSize) + } + var ( + read int + err error + ) + + for i := 0; i < maxDecodeRetry; i++ { + var n int + n, err = io.ReadFull(d.rdr, d.buf[read:encodeBinaryLen]) + if err != nil { + if err != io.ErrUnexpectedEOF { + return nil, errors.Wrap(err, "error reading log message length") } read += n - break - } - if err != nil { - return nil, errors.Wrapf(err, "could not read log message length: read: %d, expected: %d", read, encodeBinaryLen) + continue } + read += n + break + } + if err != nil { + return nil, errors.Wrapf(err, "could not read log message length: read: %d, expected: %d", read, encodeBinaryLen) + } - msgLen := int(binary.BigEndian.Uint32(buf[:read])) + msgLen := int(binary.BigEndian.Uint32(d.buf[:read])) - if len(buf) < msgLen+encodeBinaryLen { - buf = make([]byte, msgLen+encodeBinaryLen) + if len(d.buf) < msgLen+encodeBinaryLen { + d.buf = make([]byte, msgLen+encodeBinaryLen) + } else { + if msgLen <= initialBufSize { + d.buf = d.buf[:initialBufSize] } else { - if msgLen <= initialBufSize { - buf = buf[:initialBufSize] - } else { - buf = buf[:msgLen+encodeBinaryLen] - } + d.buf = d.buf[:msgLen+encodeBinaryLen] } + } - return decodeLogEntry(rdr, proto, buf, msgLen) + return decodeLogEntry(d.rdr, d.proto, d.buf, msgLen) +} + +func (d *decoder) Reset(rdr io.Reader) { + d.rdr = rdr + if d.proto != nil { + resetProto(d.proto) + } + if d.buf != nil { + d.buf = d.buf[:initialBufSize] } } +func (d *decoder) Close() { + d.buf = d.buf[:0] + d.buf = nil + if d.proto != nil { + resetProto(d.proto) + } + d.rdr = nil +} + +func decodeFunc(rdr io.Reader) loggerutils.Decoder { + return &decoder{rdr: rdr} +} + func decodeLogEntry(rdr io.Reader, proto *logdriver.LogEntry, buf []byte, msgLen int) (*logger.Message, error) { var ( read int diff --git a/daemon/logger/log_cache_opts.go b/daemon/logger/log_cache_opts.go new file mode 100644 index 0000000000000..8d09c489edfb0 --- /dev/null +++ b/daemon/logger/log_cache_opts.go @@ -0,0 +1,29 @@ +package logger + +var externalValidators []LogOptValidator + +// RegisterExternalValidator adds the validator to the list of external validators. +// External validators are used by packages outside this package that need to add their own validation logic. +// This should only be called on package initialization. +func RegisterExternalValidator(v LogOptValidator) { + externalValidators = append(externalValidators, v) +} + +// AddBuiltinLogOpts updates the list of built-in log opts. This allows other packages to supplement additional log options +// without having to register an actual log driver. This is used by things that are more proxy log drivers and should +// not be exposed as a usable log driver to the API. +// This should only be called on package initialization. +func AddBuiltinLogOpts(opts map[string]bool) { + for k, v := range opts { + builtInLogOpts[k] = v + } +} + +func validateExternal(cfg map[string]string) error { + for _, v := range externalValidators { + if err := v(cfg); err != nil { + return err + } + } + return nil +} diff --git a/daemon/logger/logentries/logentries.go b/daemon/logger/logentries/logentries.go index 70a8baf66ee70..013b182d825e6 100644 --- a/daemon/logger/logentries/logentries.go +++ b/daemon/logger/logentries/logentries.go @@ -100,6 +100,7 @@ func ValidateLogOpt(cfg map[string]string) error { case "env": case "env-regex": case "labels": + case "labels-regex": case "tag": case key: default: diff --git a/daemon/logger/logger_error.go b/daemon/logger/logger_error.go new file mode 100644 index 0000000000000..70f4311979c49 --- /dev/null +++ b/daemon/logger/logger_error.go @@ -0,0 +1,24 @@ +package logger + +import ( + "github.com/sirupsen/logrus" + "golang.org/x/time/rate" +) + +// Rates based on journald defaults of 10,000 messages in 30s. +// reference: https://www.freedesktop.org/software/systemd/man/journald.conf.html#RateLimitIntervalSec= +var logErrorLimiter = rate.NewLimiter(333, 333) + +// logDriverError logs errors produced by log drivers to the daemon logs. It also increments the logWritesFailedCount +// metric. +// Logging to the daemon logs is limited to 333 operations per second at most. If this limit is exceeded, the +// logWritesFailedCount is still counted, but logging to the daemon logs is omitted in order to prevent disk saturation. +func logDriverError(loggerName, msgLine string, logErr error) { + logWritesFailedCount.Inc(1) + if logErrorLimiter.Allow() { + logrus.WithError(logErr). + WithField("driver", loggerName). + WithField("message", msgLine). + Errorf("Error writing log message") + } +} diff --git a/daemon/logger/loggerutils/cache/local_cache.go b/daemon/logger/loggerutils/cache/local_cache.go new file mode 100644 index 0000000000000..c5e8fc2caced5 --- /dev/null +++ b/daemon/logger/loggerutils/cache/local_cache.go @@ -0,0 +1,121 @@ +package cache // import "github.com/docker/docker/daemon/logger/loggerutils/cache" + +import ( + "strconv" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/daemon/logger" + "github.com/docker/docker/daemon/logger/local" + units "github.com/docker/go-units" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + // DriverName is the name of the driver used for local log caching + DriverName = local.Name + + cachePrefix = "cache-" + cacheDisabledKey = cachePrefix + "disabled" +) + +var builtInCacheLogOpts = map[string]bool{ + cacheDisabledKey: true, +} + +// WithLocalCache wraps the passed in logger with a logger caches all writes locally +// in addition to writing to the passed in logger. +func WithLocalCache(l logger.Logger, info logger.Info) (logger.Logger, error) { + initLogger, err := logger.GetLogDriver(DriverName) + if err != nil { + return nil, err + } + + cacher, err := initLogger(info) + if err != nil { + return nil, errors.Wrap(err, "error initializing local log cache driver") + } + + if container.LogMode(info.Config["mode"]) == container.LogModeUnset || container.LogMode(info.Config["mode"]) == container.LogModeNonBlock { + var size int64 = -1 + if s, exists := info.Config["max-buffer-size"]; exists { + size, err = units.RAMInBytes(s) + if err != nil { + return nil, err + } + } + cacher = logger.NewRingLogger(cacher, info, size) + } + + return &loggerWithCache{ + l: l, + cache: cacher, + }, nil +} + +type loggerWithCache struct { + l logger.Logger + cache logger.Logger +} + +var _ logger.SizedLogger = &loggerWithCache{} + +// BufSize returns the buffer size of the underlying logger. +// Returns -1 if the logger doesn't match SizedLogger interface. +func (l *loggerWithCache) BufSize() int { + if sl, ok := l.l.(logger.SizedLogger); ok { + return sl.BufSize() + } + return -1 +} + +func (l *loggerWithCache) Log(msg *logger.Message) error { + // copy the message as the original will be reset once the call to `Log` is complete + dup := logger.NewMessage() + dumbCopyMessage(dup, msg) + + if err := l.l.Log(msg); err != nil { + return err + } + return l.cache.Log(dup) +} + +func (l *loggerWithCache) Name() string { + return l.l.Name() +} + +func (l *loggerWithCache) ReadLogs(config logger.ReadConfig) *logger.LogWatcher { + return l.cache.(logger.LogReader).ReadLogs(config) +} + +func (l *loggerWithCache) Close() error { + err := l.l.Close() + if err := l.cache.Close(); err != nil { + logrus.WithError(err).Warn("error while shutting cache logger") + } + return err +} + +// ShouldUseCache reads the log opts to determine if caching should be enabled +func ShouldUseCache(cfg map[string]string) bool { + if cfg[cacheDisabledKey] == "" { + return true + } + b, err := strconv.ParseBool(cfg[cacheDisabledKey]) + if err != nil { + // This shouldn't happen since the values are validated before hand. + return false + } + return !b +} + +// dumbCopyMessage is a bit of a fake copy but avoids extra allocations which +// are not necessary for this use case. +func dumbCopyMessage(dst, src *logger.Message) { + dst.Source = src.Source + dst.Timestamp = src.Timestamp + dst.PLogMetaData = src.PLogMetaData + dst.Err = src.Err + dst.Attrs = src.Attrs + dst.Line = append(dst.Line[:0], src.Line...) +} diff --git a/daemon/logger/loggerutils/cache/log_cache_test.go b/daemon/logger/loggerutils/cache/log_cache_test.go new file mode 100644 index 0000000000000..ef4be26f6f03f --- /dev/null +++ b/daemon/logger/loggerutils/cache/log_cache_test.go @@ -0,0 +1,81 @@ +package cache + +import ( + "context" + "testing" + + "time" + + "bytes" + + "github.com/docker/docker/daemon/logger" + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" +) + +type fakeLogger struct { + messages chan logger.Message + close chan struct{} +} + +func (l *fakeLogger) Log(msg *logger.Message) error { + select { + case l.messages <- *msg: + case <-l.close: + } + logger.PutMessage(msg) + return nil +} + +func (l *fakeLogger) Name() string { + return "fake" +} + +func (l *fakeLogger) Close() error { + close(l.close) + return nil +} + +func TestLog(t *testing.T) { + cacher := &fakeLogger{make(chan logger.Message), make(chan struct{})} + l := &loggerWithCache{ + l: &fakeLogger{make(chan logger.Message, 100), make(chan struct{})}, + cache: cacher, + } + defer l.Close() + + var messages []logger.Message + for i := 0; i < 100; i++ { + messages = append(messages, logger.Message{ + Timestamp: time.Now(), + Line: append(bytes.Repeat([]byte("a"), 100), '\n'), + }) + } + + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + go func() { + for _, msg := range messages { + select { + case <-ctx.Done(): + return + default: + } + + m := logger.NewMessage() + dumbCopyMessage(m, &msg) + l.Log(m) + } + }() + + for _, m := range messages { + var msg logger.Message + select { + case <-ctx.Done(): + t.Fatal("timed out waiting for messages... this is probably a test implementation error") + case msg = <-cacher.messages: + assert.Assert(t, cmp.DeepEqual(msg, m)) + } + } +} diff --git a/daemon/logger/loggerutils/cache/validate.go b/daemon/logger/loggerutils/cache/validate.go new file mode 100644 index 0000000000000..70bd8810d0443 --- /dev/null +++ b/daemon/logger/loggerutils/cache/validate.go @@ -0,0 +1,40 @@ +package cache + +import ( + "strconv" + + "github.com/docker/docker/daemon/logger" + "github.com/docker/docker/daemon/logger/local" + "github.com/pkg/errors" +) + +func init() { + for k, v := range local.LogOptKeys { + builtInCacheLogOpts[cachePrefix+k] = v + } + logger.AddBuiltinLogOpts(builtInCacheLogOpts) + logger.RegisterExternalValidator(validateLogCacheOpts) +} + +func validateLogCacheOpts(cfg map[string]string) error { + if v := cfg[cacheDisabledKey]; v != "" { + _, err := strconv.ParseBool(v) + if err != nil { + return errors.Errorf("invalid value for option %s: %s", cacheDisabledKey, cfg[cacheDisabledKey]) + } + } + return nil +} + +// MergeDefaultLogConfig reads the default log opts and makes sure that any caching related keys that exist there are +// added to dst. +func MergeDefaultLogConfig(dst, defaults map[string]string) { + for k, v := range defaults { + if !builtInCacheLogOpts[k] { + continue + } + if _, exists := dst[k]; !exists { + dst[k] = v + } + } +} diff --git a/daemon/logger/loggerutils/file_unix.go b/daemon/logger/loggerutils/file_unix.go new file mode 100644 index 0000000000000..1d2553c2800ed --- /dev/null +++ b/daemon/logger/loggerutils/file_unix.go @@ -0,0 +1,14 @@ +//go:build !windows +// +build !windows + +package loggerutils + +import "os" + +func openFile(name string, flag int, perm os.FileMode) (*os.File, error) { + return os.OpenFile(name, flag, perm) +} + +func open(name string) (*os.File, error) { + return os.Open(name) +} diff --git a/daemon/logger/loggerutils/file_windows.go b/daemon/logger/loggerutils/file_windows.go new file mode 100644 index 0000000000000..b16bf01d70b5d --- /dev/null +++ b/daemon/logger/loggerutils/file_windows.go @@ -0,0 +1,226 @@ +package loggerutils + +import ( + "os" + "syscall" + "unsafe" +) + +func open(name string) (*os.File, error) { + return openFile(name, os.O_RDONLY, 0) +} + +func openFile(name string, flag int, perm os.FileMode) (*os.File, error) { + if name == "" { + return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} + } + h, err := syscallOpen(fixLongPath(name), flag|syscall.O_CLOEXEC, syscallMode(perm)) + if err != nil { + return nil, &os.PathError{Op: "open", Path: name, Err: err} + } + return os.NewFile(uintptr(h), name), nil +} + +// syscallOpen is copied from syscall.Open but is modified to +// always open a file with FILE_SHARE_DELETE +func syscallOpen(path string, mode int, perm uint32) (fd syscall.Handle, err error) { + if len(path) == 0 { + return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND + } + + pathp, err := syscall.UTF16PtrFromString(path) + if err != nil { + return syscall.InvalidHandle, err + } + var access uint32 + switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) { + case syscall.O_RDONLY: + access = syscall.GENERIC_READ + case syscall.O_WRONLY: + access = syscall.GENERIC_WRITE + case syscall.O_RDWR: + access = syscall.GENERIC_READ | syscall.GENERIC_WRITE + } + if mode&syscall.O_CREAT != 0 { + access |= syscall.GENERIC_WRITE + } + if mode&syscall.O_APPEND != 0 { + access &^= syscall.GENERIC_WRITE + access |= syscall.FILE_APPEND_DATA + } + sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE | syscall.FILE_SHARE_DELETE) + var sa *syscall.SecurityAttributes + if mode&syscall.O_CLOEXEC == 0 { + sa = makeInheritSa() + } + var createmode uint32 + switch { + case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL): + createmode = syscall.CREATE_NEW + case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC): + createmode = syscall.CREATE_ALWAYS + case mode&syscall.O_CREAT == syscall.O_CREAT: + createmode = syscall.OPEN_ALWAYS + case mode&syscall.O_TRUNC == syscall.O_TRUNC: + createmode = syscall.TRUNCATE_EXISTING + default: + createmode = syscall.OPEN_EXISTING + } + h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, syscall.FILE_ATTRIBUTE_NORMAL, 0) + return h, e +} + +func makeInheritSa() *syscall.SecurityAttributes { + var sa syscall.SecurityAttributes + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + return &sa +} + +// fixLongPath returns the extended-length (\\?\-prefixed) form of +// path when needed, in order to avoid the default 260 character file +// path limit imposed by Windows. If path is not easily converted to +// the extended-length form (for example, if path is a relative path +// or contains .. elements), or is short enough, fixLongPath returns +// path unmodified. +// +// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath +// +// Copied from os.OpenFile +func fixLongPath(path string) string { + // Do nothing (and don't allocate) if the path is "short". + // Empirically (at least on the Windows Server 2013 builder), + // the kernel is arbitrarily okay with < 248 bytes. That + // matches what the docs above say: + // "When using an API to create a directory, the specified + // path cannot be so long that you cannot append an 8.3 file + // name (that is, the directory name cannot exceed MAX_PATH + // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248. + // + // The MSDN docs appear to say that a normal path that is 248 bytes long + // will work; empirically the path must be less then 248 bytes long. + if len(path) < 248 { + // Don't fix. (This is how Go 1.7 and earlier worked, + // not automatically generating the \\?\ form) + return path + } + + // The extended form begins with \\?\, as in + // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt. + // The extended form disables evaluation of . and .. path + // elements and disables the interpretation of / as equivalent + // to \. The conversion here rewrites / to \ and elides + // . elements as well as trailing or duplicate separators. For + // simplicity it avoids the conversion entirely for relative + // paths or paths containing .. elements. For now, + // \\server\share paths are not converted to + // \\?\UNC\server\share paths because the rules for doing so + // are less well-specified. + if len(path) >= 2 && path[:2] == `\\` { + // Don't canonicalize UNC paths. + return path + } + if !isAbs(path) { + // Relative path + return path + } + + const prefix = `\\?` + + pathbuf := make([]byte, len(prefix)+len(path)+len(`\`)) + copy(pathbuf, prefix) + n := len(path) + r, w := 0, len(prefix) + for r < n { + switch { + case os.IsPathSeparator(path[r]): + // empty block + r++ + case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): + // /./ + r++ + case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): + // /../ is currently unhandled + return path + default: + pathbuf[w] = '\\' + w++ + for ; r < n && !os.IsPathSeparator(path[r]); r++ { + pathbuf[w] = path[r] + w++ + } + } + } + // A drive's root directory needs a trailing \ + if w == len(`\\?\c:`) { + pathbuf[w] = '\\' + w++ + } + return string(pathbuf[:w]) +} + +// copied from os package for os.OpenFile +func syscallMode(i os.FileMode) (o uint32) { + o |= uint32(i.Perm()) + if i&os.ModeSetuid != 0 { + o |= syscall.S_ISUID + } + if i&os.ModeSetgid != 0 { + o |= syscall.S_ISGID + } + if i&os.ModeSticky != 0 { + o |= syscall.S_ISVTX + } + // No mapping for Go's ModeTemporary (plan9 only). + return +} + +func isAbs(path string) (b bool) { + v := volumeName(path) + if v == "" { + return false + } + path = path[len(v):] + if path == "" { + return false + } + return os.IsPathSeparator(path[0]) +} + +func volumeName(path string) (v string) { + if len(path) < 2 { + return "" + } + // with drive letter + c := path[0] + if path[1] == ':' && + ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' || + 'A' <= c && c <= 'Z') { + return path[:2] + } + // is it UNC + if l := len(path); l >= 5 && os.IsPathSeparator(path[0]) && os.IsPathSeparator(path[1]) && + !os.IsPathSeparator(path[2]) && path[2] != '.' { + // first, leading `\\` and next shouldn't be `\`. its server name. + for n := 3; n < l-1; n++ { + // second, next '\' shouldn't be repeated. + if os.IsPathSeparator(path[n]) { + n++ + // third, following something characters. its share name. + if !os.IsPathSeparator(path[n]) { + if path[n] == '.' { + break + } + for ; n < l; n++ { + if os.IsPathSeparator(path[n]) { + break + } + } + return path[:n] + } + break + } + } + } + return "" +} diff --git a/daemon/logger/loggerutils/file_windows_test.go b/daemon/logger/loggerutils/file_windows_test.go new file mode 100644 index 0000000000000..9978c942194da --- /dev/null +++ b/daemon/logger/loggerutils/file_windows_test.go @@ -0,0 +1,33 @@ +package loggerutils + +import ( + "os" + "path/filepath" + "testing" + + "gotest.tools/v3/assert" +) + +func TestOpenFileDelete(t *testing.T) { + tmpDir, err := os.MkdirTemp("", t.Name()) + assert.NilError(t, err) + defer os.RemoveAll(tmpDir) + + f, err := openFile(filepath.Join(tmpDir, "test.txt"), os.O_CREATE|os.O_RDWR, 644) + assert.NilError(t, err) + defer f.Close() + + assert.NilError(t, os.RemoveAll(f.Name())) +} + +func TestOpenFileRename(t *testing.T) { + tmpDir, err := os.MkdirTemp("", t.Name()) + assert.NilError(t, err) + defer os.RemoveAll(tmpDir) + + f, err := openFile(filepath.Join(tmpDir, "test.txt"), os.O_CREATE|os.O_RDWR, 0644) + assert.NilError(t, err) + defer f.Close() + + assert.NilError(t, os.Rename(f.Name(), f.Name()+"renamed")) +} diff --git a/daemon/logger/loggerutils/logfile.go b/daemon/logger/loggerutils/logfile.go index fa45154ec9765..6b42d9dd30d72 100644 --- a/daemon/logger/loggerutils/logfile.go +++ b/daemon/logger/loggerutils/logfile.go @@ -68,7 +68,7 @@ func (rc *refCounter) Dereference(fileName string) error { if rc.counter[fileName] <= 0 { delete(rc.counter, fileName) err := os.Remove(fileName) - if err != nil { + if err != nil && !os.IsNotExist(err) { return err } } @@ -87,14 +87,27 @@ type LogFile struct { compress bool // whether old versions of log files are compressed lastTimestamp time.Time // timestamp of the last log filesRefCounter refCounter // keep reference-counted of decompressed files - notifyRotate *pubsub.Publisher + notifyReaders *pubsub.Publisher marshal logger.MarshalFunc - createDecoder makeDecoderFunc + createDecoder MakeDecoderFn getTailReader GetTailReaderFunc perms os.FileMode } -type makeDecoderFunc func(rdr io.Reader) func() (*logger.Message, error) +// MakeDecoderFn creates a decoder +type MakeDecoderFn func(rdr io.Reader) Decoder + +// Decoder is for reading logs +// It is created by the log reader by calling the `MakeDecoderFunc` +type Decoder interface { + // Reset resets the decoder + // Reset is called for certain events, such as log rotations + Reset(io.Reader) + // Decode decodes the next log messeage from the stream + Decode() (*logger.Message, error) + // Close signals to the decoder that it can release whatever resources it was using. + Close() +} // SizeReaderAt defines a ReaderAt that also reports its size. // This is used for tailing log files. @@ -110,13 +123,13 @@ type SizeReaderAt interface { type GetTailReaderFunc func(ctx context.Context, f SizeReaderAt, nLogLines int) (rdr io.Reader, nLines int, err error) // NewLogFile creates new LogFile -func NewLogFile(logPath string, capacity int64, maxFiles int, compress bool, marshaller logger.MarshalFunc, decodeFunc makeDecoderFunc, perms os.FileMode, getTailReader GetTailReaderFunc) (*LogFile, error) { - log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, perms) +func NewLogFile(logPath string, capacity int64, maxFiles int, compress bool, marshaller logger.MarshalFunc, decodeFunc MakeDecoderFn, perms os.FileMode, getTailReader GetTailReaderFunc) (*LogFile, error) { + log, err := openFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, perms) if err != nil { return nil, err } - size, err := log.Seek(0, os.SEEK_END) + size, err := log.Seek(0, io.SeekEnd) if err != nil { return nil, err } @@ -128,7 +141,7 @@ func NewLogFile(logPath string, capacity int64, maxFiles int, compress bool, mar maxFiles: maxFiles, compress: compress, filesRefCounter: refCounter{counter: make(map[string]int)}, - notifyRotate: pubsub.NewPublisher(0, 1), + notifyReaders: pubsub.NewPublisher(0, 1), marshal: marshaller, createDecoder: decodeFunc, perms: perms, @@ -154,7 +167,7 @@ func (w *LogFile) WriteLogEntry(msg *logger.Message) error { if err := w.checkCapacityAndRotate(); err != nil { w.mu.Unlock() - return err + return errors.Wrap(err, "error rotating log file") } n, err := w.f.Write(b) @@ -162,46 +175,77 @@ func (w *LogFile) WriteLogEntry(msg *logger.Message) error { w.currentSize += int64(n) w.lastTimestamp = msg.Timestamp } + w.mu.Unlock() - return err + return errors.Wrap(err, "error writing log entry") } -func (w *LogFile) checkCapacityAndRotate() error { +func (w *LogFile) checkCapacityAndRotate() (retErr error) { if w.capacity == -1 { return nil } + if w.currentSize < w.capacity { + return nil + } - if w.currentSize >= w.capacity { - w.rotateMu.Lock() - fname := w.f.Name() - if err := w.f.Close(); err != nil { + w.rotateMu.Lock() + noCompress := w.maxFiles <= 1 || !w.compress + defer func() { + // If we aren't going to run the goroutine to compress the log file, then we need to unlock in this function. + // Otherwise the lock will be released in the goroutine that handles compression. + if retErr != nil || noCompress { w.rotateMu.Unlock() + } + }() + + fname := w.f.Name() + if err := w.f.Close(); err != nil { + // if there was an error during a prior rotate, the file could already be closed + if !errors.Is(err, os.ErrClosed) { return errors.Wrap(err, "error closing file") } - if err := rotate(fname, w.maxFiles, w.compress); err != nil { - w.rotateMu.Unlock() - return err + } + + if err := rotate(fname, w.maxFiles, w.compress); err != nil { + logrus.WithError(err).Warn("Error rotating log file, log data may have been lost") + } else { + var renameErr error + for i := 0; i < 10; i++ { + if renameErr = os.Rename(fname, fname+".1"); renameErr != nil && !os.IsNotExist(renameErr) { + logrus.WithError(renameErr).WithField("file", fname).Debug("Error rotating current container log file, evicting readers and retrying") + w.notifyReaders.Publish(renameErr) + time.Sleep(100 * time.Millisecond) + continue + } + break } - file, err := os.OpenFile(fname, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, w.perms) - if err != nil { - w.rotateMu.Unlock() - return err + if renameErr != nil { + logrus.WithError(renameErr).Error("Error renaming current log file") } - w.f = file - w.currentSize = 0 - w.notifyRotate.Publish(struct{}{}) + } - if w.maxFiles <= 1 || !w.compress { - w.rotateMu.Unlock() - return nil - } + file, err := openFile(fname, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, w.perms) + if err != nil { + return err + } + w.f = file + w.currentSize = 0 - go func() { - compressFile(fname+".1", w.lastTimestamp) - w.rotateMu.Unlock() - }() + w.notifyReaders.Publish(struct{}{}) + + if noCompress { + return nil } + ts := w.lastTimestamp + + go func() { + if err := compressFile(fname+".1", ts); err != nil { + logrus.WithError(err).Error("Error compressing log file after rotation") + } + w.rotateMu.Unlock() + }() + return nil } @@ -224,48 +268,51 @@ func rotate(name string, maxFiles int, compress bool) error { for i := maxFiles - 1; i > 1; i-- { toPath := name + "." + strconv.Itoa(i) + extension fromPath := name + "." + strconv.Itoa(i-1) + extension + logrus.WithField("source", fromPath).WithField("target", toPath).Trace("Rotating log file") if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) { return err } } - if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) { - return err - } - return nil } -func compressFile(fileName string, lastTimestamp time.Time) { - file, err := os.Open(fileName) +func compressFile(fileName string, lastTimestamp time.Time) (retErr error) { + file, err := open(fileName) if err != nil { - logrus.Errorf("Failed to open log file: %v", err) - return + if os.IsNotExist(err) { + logrus.WithField("file", fileName).WithError(err).Debug("Could not open log file to compress") + return nil + } + return errors.Wrap(err, "failed to open log file") } defer func() { file.Close() - err := os.Remove(fileName) - if err != nil { - logrus.Errorf("Failed to remove source log file: %v", err) + if retErr == nil { + err := os.Remove(fileName) + if err != nil && !os.IsNotExist(err) { + retErr = errors.Wrap(err, "failed to remove source log file") + } } }() - outFile, err := os.OpenFile(fileName+".gz", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0640) + outFile, err := openFile(fileName+".gz", os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0640) if err != nil { - logrus.Errorf("Failed to open or create gzip log file: %v", err) - return + return errors.Wrap(err, "failed to open or create gzip log file") } defer func() { outFile.Close() - if err != nil { - os.Remove(fileName + ".gz") + if retErr != nil { + if err := os.Remove(fileName + ".gz"); err != nil && !os.IsExist(err) { + logrus.WithError(err).Error("Error cleaning up after failed log compression") + } } }() compressWriter := gzip.NewWriter(outFile) defer compressWriter.Close() - // Add the last log entry timestramp to the gzip header + // Add the last log entry timestamp to the gzip header extra := rotateFileMetadata{} extra.LastTime = lastTimestamp compressWriter.Header.Extra, err = json.Marshal(&extra) @@ -276,9 +323,10 @@ func compressFile(fileName string, lastTimestamp time.Time) { _, err = pools.Copy(compressWriter, file) if err != nil { - logrus.WithError(err).WithField("module", "container.logs").WithField("file", fileName).Error("Error compressing log file") - return + return errors.Wrapf(err, "error compressing log file %s", fileName) } + + return nil } // MaxFiles return maximum number of files @@ -293,7 +341,7 @@ func (w *LogFile) Close() error { if w.closed { return nil } - if err := w.f.Close(); err != nil { + if err := w.f.Close(); err != nil && !errors.Is(err, os.ErrClosed) { return err } w.closed = true @@ -306,7 +354,7 @@ func (w *LogFile) Close() error { // TODO: Consider a different implementation which can effectively follow logs under frequent rotations. func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) { w.mu.RLock() - currentFile, err := os.Open(w.f.Name()) + currentFile, err := open(w.f.Name()) if err != nil { w.mu.RUnlock() watcher.Err <- err @@ -314,6 +362,9 @@ func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) } defer currentFile.Close() + dec := w.createDecoder(nil) + defer dec.Close() + currentChunk, err := newSectionReader(currentFile) if err != nil { w.mu.RUnlock() @@ -321,6 +372,12 @@ func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) return } + notifyEvict := w.notifyReaders.SubscribeTopicWithBuffer(func(i interface{}) bool { + _, ok := i.(error) + return ok + }, 1) + defer w.notifyReaders.Evict(notifyEvict) + if config.Tail != 0 { // TODO(@cpuguy83): Instead of opening every file, only get the files which // are needed to tail. @@ -339,7 +396,7 @@ func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) if strings.HasSuffix(fileName, tmpLogfileSuffix) { err := w.filesRefCounter.Dereference(fileName) if err != nil { - logrus.Errorf("Failed to dereference the log file %q: %v", fileName, err) + logrus.WithError(err).WithField("file", fileName).Error("Failed to dereference the log file") } } } @@ -359,9 +416,11 @@ func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) readers = append(readers, currentChunk) } - tailFiles(readers, watcher, w.createDecoder, w.getTailReader, config) + ok := tailFiles(readers, watcher, dec, w.getTailReader, config, notifyEvict) closeFiles() - + if !ok { + return + } w.mu.RLock() } @@ -371,9 +430,13 @@ func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) } w.mu.RUnlock() - notifyRotate := w.notifyRotate.Subscribe() - defer w.notifyRotate.Evict(notifyRotate) - followLogs(currentFile, watcher, notifyRotate, w.createDecoder, config.Since, config.Until) + notifyRotate := w.notifyReaders.SubscribeTopic(func(i interface{}) bool { + _, ok := i.(struct{}) + return ok + }) + defer w.notifyReaders.Evict(notifyRotate) + + followLogs(currentFile, watcher, notifyRotate, notifyEvict, dec, config.Since, config.Until) } func (w *LogFile) openRotatedFiles(config logger.ReadConfig) (files []*os.File, err error) { @@ -396,7 +459,7 @@ func (w *LogFile) openRotatedFiles(config logger.ReadConfig) (files []*os.File, }() for i := w.maxFiles; i > 1; i-- { - f, err := os.Open(fmt.Sprintf("%s.%d", w.f.Name(), i-1)) + f, err := open(fmt.Sprintf("%s.%d", w.f.Name(), i-1)) if err != nil { if !os.IsNotExist(err) { return nil, errors.Wrap(err, "error opening rotated log file") @@ -406,13 +469,13 @@ func (w *LogFile) openRotatedFiles(config logger.ReadConfig) (files []*os.File, decompressedFileName := fileName + tmpLogfileSuffix tmpFile, err := w.filesRefCounter.GetReference(decompressedFileName, func(refFileName string, exists bool) (*os.File, error) { if exists { - return os.Open(refFileName) + return open(refFileName) } return decompressfile(fileName, refFileName, config.Since) }) if err != nil { - if !os.IsNotExist(errors.Cause(err)) { + if !errors.Is(err, os.ErrNotExist) { return nil, errors.Wrap(err, "error getting reference to decompressed log file") } continue @@ -432,7 +495,7 @@ func (w *LogFile) openRotatedFiles(config logger.ReadConfig) (files []*os.File, } func decompressfile(fileName, destFileName string, since time.Time) (*os.File, error) { - cf, err := os.Open(fileName) + cf, err := open(fileName) if err != nil { return nil, errors.Wrap(err, "error opening file for decompression") } @@ -451,7 +514,7 @@ func decompressfile(fileName, destFileName string, since time.Time) (*os.File, e return nil, nil } - rs, err := os.OpenFile(destFileName, os.O_CREATE|os.O_RDWR, 0640) + rs, err := openFile(destFileName, os.O_CREATE|os.O_RDWR, 0640) if err != nil { return nil, errors.Wrap(err, "error creating file for copying decompressed log stream") } @@ -472,23 +535,32 @@ func decompressfile(fileName, destFileName string, since time.Time) (*os.File, e func newSectionReader(f *os.File) (*io.SectionReader, error) { // seek to the end to get the size // we'll leave this at the end of the file since section reader does not advance the reader - size, err := f.Seek(0, os.SEEK_END) + size, err := f.Seek(0, io.SeekEnd) if err != nil { return nil, errors.Wrap(err, "error getting current file size") } return io.NewSectionReader(f, 0, size), nil } -func tailFiles(files []SizeReaderAt, watcher *logger.LogWatcher, createDecoder makeDecoderFunc, getTailReader GetTailReaderFunc, config logger.ReadConfig) { +func tailFiles(files []SizeReaderAt, watcher *logger.LogWatcher, dec Decoder, getTailReader GetTailReaderFunc, config logger.ReadConfig, notifyEvict <-chan interface{}) (cont bool) { nLines := config.Tail ctx, cancel := context.WithCancel(context.Background()) defer cancel() + + cont = true // TODO(@cpuguy83): we should plumb a context through instead of dealing with `WatchClose()` here. go func() { select { + case err := <-notifyEvict: + if err != nil { + watcher.Err <- err.(error) + cont = false + cancel() + } case <-ctx.Done(): case <-watcher.WatchConsumerGone(): + cont = false cancel() } }() @@ -512,11 +584,12 @@ func tailFiles(files []SizeReaderAt, watcher *logger.LogWatcher, createDecoder m } rdr := io.MultiReader(readers...) - decodeLogLine := createDecoder(rdr) + dec.Reset(rdr) + for { - msg, err := decodeLogLine() + msg, err := dec.Decode() if err != nil { - if errors.Cause(err) != io.EOF { + if !errors.Is(err, io.EOF) { watcher.Err <- err } return @@ -535,8 +608,8 @@ func tailFiles(files []SizeReaderAt, watcher *logger.LogWatcher, createDecoder m } } -func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan interface{}, createDecoder makeDecoderFunc, since, until time.Time) { - decodeLogLine := createDecoder(f) +func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate, notifyEvict chan interface{}, dec Decoder, since, until time.Time) { + dec.Reset(f) name := f.Name() fileWatcher, err := watchFile(name) @@ -546,6 +619,7 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int } defer func() { f.Close() + dec.Close() fileWatcher.Close() }() @@ -556,7 +630,7 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int // retry when the file doesn't exist for retries := 0; retries <= 5; retries++ { - f, err = os.Open(name) + f, err = open(name) if err == nil || !os.IsNotExist(err) { break } @@ -567,18 +641,32 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int if err := fileWatcher.Add(name); err != nil { return err } - decodeLogLine = createDecoder(f) + dec.Reset(f) return nil } errRetry := errors.New("retry") errDone := errors.New("done") + + handleMustClose := func(evictErr error) { + f.Close() + dec.Close() + logWatcher.Err <- errors.Wrap(err, "log reader evicted due to errors") + logrus.WithField("file", f.Name()).Error("Log reader notified that it must re-open log file, some log data may not be streamed to the client.") + } + waitRead := func() error { select { + case e := <-notifyEvict: + if e != nil { + err := e.(error) + handleMustClose(err) + } + return errDone case e := <-fileWatcher.Events(): switch e.Op { case fsnotify.Write: - decodeLogLine = createDecoder(f) + dec.Reset(f) return nil case fsnotify.Rename, fsnotify.Remove: select { @@ -614,11 +702,25 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int } } + oldSize := int64(-1) handleDecodeErr := func(err error) error { - if errors.Cause(err) != io.EOF { + if !errors.Is(err, io.EOF) { return err } + // Handle special case (#39235): max-file=1 and file was truncated + st, stErr := f.Stat() + if stErr == nil { + size := st.Size() + defer func() { oldSize = size }() + if size < oldSize { // truncated + f.Seek(0, 0) + return nil + } + } else { + logrus.WithError(stErr).Warn("logger: stat error") + } + for { err := waitRead() if err == nil { @@ -634,7 +736,15 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int // main loop for { - msg, err := decodeLogLine() + select { + case err := <-notifyEvict: + if err != nil { + handleMustClose(err.(error)) + } + return + default: + } + msg, err := dec.Decode() if err != nil { if err := handleDecodeErr(err); err != nil { if err == errDone { @@ -657,6 +767,13 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int } // send the message, unless the consumer is gone select { + case e := <-notifyEvict: + if e != nil { + err := e.(error) + logrus.WithError(err).Debug("Reader evicted while sending log message") + logWatcher.Err <- err + } + return case logWatcher.Msg <- msg: case <-logWatcher.WatchConsumerGone(): return diff --git a/daemon/logger/loggerutils/logfile_test.go b/daemon/logger/loggerutils/logfile_test.go index e3e63210fc0d9..01f13ac9f29ff 100644 --- a/daemon/logger/loggerutils/logfile_test.go +++ b/daemon/logger/loggerutils/logfile_test.go @@ -1,20 +1,52 @@ -package loggerutils +package loggerutils // import "github.com/docker/docker/daemon/logger/loggerutils" import ( "bufio" + "bytes" "context" + "fmt" "io" - "io/ioutil" "os" + "path/filepath" "strings" + "sync/atomic" "testing" + "text/tabwriter" "time" "github.com/docker/docker/daemon/logger" + "github.com/docker/docker/pkg/pubsub" "github.com/docker/docker/pkg/tailfile" - "gotest.tools/assert" + "gotest.tools/v3/assert" + "gotest.tools/v3/poll" ) +type testDecoder struct { + rdr io.Reader + scanner *bufio.Scanner +} + +func (d *testDecoder) Decode() (*logger.Message, error) { + if d.scanner == nil { + d.scanner = bufio.NewScanner(d.rdr) + } + if !d.scanner.Scan() { + return nil, d.scanner.Err() + } + // some comment + return &logger.Message{Line: d.scanner.Bytes(), Timestamp: time.Now()}, nil +} + +func (d *testDecoder) Reset(rdr io.Reader) { + d.rdr = rdr + d.scanner = bufio.NewScanner(rdr) +} + +func (d *testDecoder) Close() { + d.rdr = nil + d.scanner = nil +} + func TestTailFiles(t *testing.T) { s1 := strings.NewReader("Hello.\nMy name is Inigo Montoya.\n") s2 := strings.NewReader("I'm serious.\nDon't call me Shirley!\n") @@ -22,27 +54,19 @@ func TestTailFiles(t *testing.T) { files := []SizeReaderAt{s1, s2, s3} watcher := logger.NewLogWatcher() - createDecoder := func(r io.Reader) func() (*logger.Message, error) { - scanner := bufio.NewScanner(r) - return func() (*logger.Message, error) { - if !scanner.Scan() { - return nil, scanner.Err() - } - // some comment - return &logger.Message{Line: scanner.Bytes(), Timestamp: time.Now()}, nil - } - } + defer watcher.ConsumerGone() tailReader := func(ctx context.Context, r SizeReaderAt, lines int) (io.Reader, int, error) { return tailfile.NewTailReader(ctx, r, lines) } + dec := &testDecoder{} for desc, config := range map[string]logger.ReadConfig{} { t.Run(desc, func(t *testing.T) { started := make(chan struct{}) go func() { close(started) - tailFiles(files, watcher, createDecoder, tailReader, config) + tailFiles(files, watcher, dec, tailReader, config, make(chan interface{})) }() <-started }) @@ -52,7 +76,7 @@ func TestTailFiles(t *testing.T) { started := make(chan struct{}) go func() { close(started) - tailFiles(files, watcher, createDecoder, tailReader, config) + tailFiles(files, watcher, dec, tailReader, config, make(chan interface{})) }() <-started @@ -60,7 +84,7 @@ func TestTailFiles(t *testing.T) { case <-time.After(60 * time.Second): t.Fatal("timeout waiting for tail line") case err := <-watcher.Err: - assert.Assert(t, err) + assert.NilError(t, err) case msg := <-watcher.Msg: assert.Assert(t, msg != nil) assert.Assert(t, string(msg.Line) == "Roads?", string(msg.Line)) @@ -70,33 +94,38 @@ func TestTailFiles(t *testing.T) { case <-time.After(60 * time.Second): t.Fatal("timeout waiting for tail line") case err := <-watcher.Err: - assert.Assert(t, err) + assert.NilError(t, err) case msg := <-watcher.Msg: assert.Assert(t, msg != nil) assert.Assert(t, string(msg.Line) == "Where we're going we don't need roads.", string(msg.Line)) } } +type dummyDecoder struct{} + +func (dummyDecoder) Decode() (*logger.Message, error) { + return &logger.Message{}, nil +} + +func (dummyDecoder) Close() {} +func (dummyDecoder) Reset(io.Reader) {} + func TestFollowLogsConsumerGone(t *testing.T) { lw := logger.NewLogWatcher() - f, err := ioutil.TempFile("", t.Name()) + f, err := os.CreateTemp("", t.Name()) assert.NilError(t, err) defer func() { f.Close() os.Remove(f.Name()) }() - makeDecoder := func(rdr io.Reader) func() (*logger.Message, error) { - return func() (*logger.Message, error) { - return &logger.Message{}, nil - } - } + dec := dummyDecoder{} followLogsDone := make(chan struct{}) var since, until time.Time go func() { - followLogs(f, lw, make(chan interface{}), makeDecoder, since, until) + followLogs(f, lw, make(chan interface{}), make(chan interface{}), dec, since, until) close(followLogsDone) }() @@ -118,33 +147,46 @@ func TestFollowLogsConsumerGone(t *testing.T) { } } +type dummyWrapper struct { + dummyDecoder + fn func() error +} + +func (d *dummyWrapper) Decode() (*logger.Message, error) { + if err := d.fn(); err != nil { + return nil, err + } + return d.dummyDecoder.Decode() +} + func TestFollowLogsProducerGone(t *testing.T) { lw := logger.NewLogWatcher() + defer lw.ConsumerGone() - f, err := ioutil.TempFile("", t.Name()) + f, err := os.CreateTemp("", t.Name()) assert.NilError(t, err) defer os.Remove(f.Name()) - var sent, received, closed int - makeDecoder := func(rdr io.Reader) func() (*logger.Message, error) { - return func() (*logger.Message, error) { - if closed == 1 { - closed++ - t.Logf("logDecode() closed after sending %d messages\n", sent) - return nil, io.EOF - } else if closed > 1 { - t.Fatal("logDecode() called after closing!") - return nil, io.EOF - } - sent++ - return &logger.Message{}, nil + var sent, received, closed int32 + dec := &dummyWrapper{fn: func() error { + switch atomic.LoadInt32(&closed) { + case 0: + atomic.AddInt32(&sent, 1) + return nil + case 1: + atomic.AddInt32(&closed, 1) + t.Logf("logDecode() closed after sending %d messages\n", sent) + return io.EOF + default: + t.Fatal("logDecode() called after closing!") + return io.EOF } - } + }} var since, until time.Time followLogsDone := make(chan struct{}) go func() { - followLogs(f, lw, make(chan interface{}), makeDecoder, since, until) + followLogs(f, lw, make(chan interface{}), make(chan interface{}), dec, since, until) close(followLogsDone) }() @@ -161,7 +203,7 @@ func TestFollowLogsProducerGone(t *testing.T) { } // "stop" the "container" - closed = 1 + atomic.StoreInt32(&closed, 1) lw.ProducerGone() // should receive all the messages sent @@ -172,7 +214,7 @@ func TestFollowLogsProducerGone(t *testing.T) { select { case <-lw.Msg: received++ - if received == sent { + if received == atomic.LoadInt32(&sent) { return } case err := <-lw.Err: @@ -186,7 +228,7 @@ func TestFollowLogsProducerGone(t *testing.T) { t.Fatalf("timeout waiting for log messages to be read (sent: %d, received: %d", sent, received) } - t.Logf("messages sent: %d, received: %d", sent, received) + t.Logf("messages sent: %d, received: %d", atomic.LoadInt32(&sent), received) // followLogs() should be done by now select { @@ -201,3 +243,131 @@ func TestFollowLogsProducerGone(t *testing.T) { default: } } + +func TestCheckCapacityAndRotate(t *testing.T) { + dir, err := os.MkdirTemp("", t.Name()) + assert.NilError(t, err) + defer os.RemoveAll(dir) + + f, err := os.CreateTemp(dir, "log") + assert.NilError(t, err) + + l := &LogFile{ + f: f, + capacity: 5, + maxFiles: 3, + compress: true, + notifyReaders: pubsub.NewPublisher(0, 1), + perms: 0600, + filesRefCounter: refCounter{counter: make(map[string]int)}, + getTailReader: func(ctx context.Context, r SizeReaderAt, lines int) (io.Reader, int, error) { + return tailfile.NewTailReader(ctx, r, lines) + }, + createDecoder: func(io.Reader) Decoder { + return dummyDecoder{} + }, + marshal: func(msg *logger.Message) ([]byte, error) { + return msg.Line, nil + }, + } + defer l.Close() + + ls := dirStringer{dir} + + assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")})) + _, err = os.Stat(f.Name() + ".1") + assert.Assert(t, os.IsNotExist(err), ls) + + assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")})) + poll.WaitOn(t, checkFileExists(f.Name()+".1.gz"), poll.WithDelay(time.Millisecond), poll.WithTimeout(30*time.Second)) + + assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")})) + poll.WaitOn(t, checkFileExists(f.Name()+".1.gz"), poll.WithDelay(time.Millisecond), poll.WithTimeout(30*time.Second)) + poll.WaitOn(t, checkFileExists(f.Name()+".2.gz"), poll.WithDelay(time.Millisecond), poll.WithTimeout(30*time.Second)) + + t.Run("closed log file", func(t *testing.T) { + // Now let's simulate a failed rotation where the file was able to be closed but something else happened elsewhere + // down the line. + // We want to make sure that we can recover in the case that `l.f` was closed while attempting a rotation. + l.f.Close() + assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")})) + assert.NilError(t, os.Remove(f.Name()+".2.gz")) + }) + + t.Run("with log reader", func(t *testing.T) { + // Make sure rotate works with an active reader + lw := logger.NewLogWatcher() + defer lw.ConsumerGone() + go l.ReadLogs(logger.ReadConfig{Follow: true, Tail: 1000}, lw) + + assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world 0!")}), ls) + // make sure the log reader is primed + waitForMsg(t, lw, 30*time.Second) + + assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world 1!")}), ls) + assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world 2!")}), ls) + assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world 3!")}), ls) + assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world 4!")}), ls) + poll.WaitOn(t, checkFileExists(f.Name()+".2.gz"), poll.WithDelay(time.Millisecond), poll.WithTimeout(30*time.Second)) + }) +} + +func waitForMsg(t *testing.T, lw *logger.LogWatcher, timeout time.Duration) { + t.Helper() + + timer := time.NewTimer(timeout) + defer timer.Stop() + + select { + case <-lw.Msg: + case <-lw.WatchProducerGone(): + t.Fatal("log producer gone before log message arrived") + case err := <-lw.Err: + assert.NilError(t, err) + case <-timer.C: + t.Fatal("timeout waiting for log message") + } +} + +type dirStringer struct { + d string +} + +func (d dirStringer) String() string { + ls, err := os.ReadDir(d.d) + if err != nil { + return "" + } + buf := bytes.NewBuffer(nil) + tw := tabwriter.NewWriter(buf, 1, 8, 1, '\t', 0) + buf.WriteString("\n") + + btw := bufio.NewWriter(tw) + + for _, entry := range ls { + fi, err := entry.Info() + if err != nil { + return "" + } + + btw.WriteString(fmt.Sprintf("%s\t%s\t%dB\t%s\n", fi.Name(), fi.Mode(), fi.Size(), fi.ModTime())) + } + btw.Flush() + tw.Flush() + return buf.String() +} + +func checkFileExists(name string) poll.Check { + return func(t poll.LogT) poll.Result { + _, err := os.Stat(name) + switch { + case err == nil: + return poll.Success() + case os.IsNotExist(err): + return poll.Continue("waiting for %s to exist", name) + default: + t.Logf("waiting for %s: %v: %s", name, err, dirStringer{filepath.Dir(name)}) + return poll.Error(err) + } + } +} diff --git a/daemon/logger/loginfo.go b/daemon/logger/loginfo.go index 4c48235f5c73b..12034421fc984 100644 --- a/daemon/logger/loginfo.go +++ b/daemon/logger/loginfo.go @@ -41,6 +41,22 @@ func (info *Info) ExtraAttributes(keyMod func(string) string) (map[string]string } } + labelsRegex, ok := info.Config["labels-regex"] + if ok && len(labelsRegex) > 0 { + re, err := regexp.Compile(labelsRegex) + if err != nil { + return nil, err + } + for k, v := range info.ContainerLabels { + if re.MatchString(k) { + if keyMod != nil { + k = keyMod(k) + } + extra[k] = v + } + } + } + envMapping := make(map[string]string) for _, e := range info.ContainerEnv { if kv := strings.SplitN(e, "=", 2); len(kv) == 2 { diff --git a/daemon/logger/metrics.go b/daemon/logger/metrics.go index b7dfd38ec2061..05b557f8625ed 100644 --- a/daemon/logger/metrics.go +++ b/daemon/logger/metrics.go @@ -1,7 +1,7 @@ package logger // import "github.com/docker/docker/daemon/logger" import ( - "github.com/docker/go-metrics" + metrics "github.com/docker/go-metrics" ) var ( diff --git a/daemon/logger/plugin.go b/daemon/logger/plugin.go index c66540ce52d1a..8c155b0ddb46c 100644 --- a/daemon/logger/plugin.go +++ b/daemon/logger/plugin.go @@ -81,7 +81,7 @@ func makePluginCreator(name string, l logPlugin, scopePath func(s string) string return nil, err } - id := stringid.GenerateNonCryptoID() + id := stringid.GenerateRandomID() a := &pluginAdapter{ driverName: name, id: id, diff --git a/daemon/logger/plugin_unix.go b/daemon/logger/plugin_unix.go index e9a16af9b1f35..a59fda860a59e 100644 --- a/daemon/logger/plugin_unix.go +++ b/daemon/logger/plugin_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package logger // import "github.com/docker/docker/daemon/logger" diff --git a/daemon/logger/plugin_unsupported.go b/daemon/logger/plugin_unsupported.go index 2ad47cc07798c..fbbeba0c21706 100644 --- a/daemon/logger/plugin_unsupported.go +++ b/daemon/logger/plugin_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !freebsd // +build !linux,!freebsd package logger // import "github.com/docker/docker/daemon/logger" diff --git a/daemon/logger/ring.go b/daemon/logger/ring.go index c675c1e83c591..ff43baac2fbd2 100644 --- a/daemon/logger/ring.go +++ b/daemon/logger/ring.go @@ -4,8 +4,6 @@ import ( "errors" "sync" "sync/atomic" - - "github.com/sirupsen/logrus" ) const ( @@ -19,8 +17,11 @@ type RingLogger struct { l Logger logInfo Info closeFlag int32 + wg sync.WaitGroup } +var _ SizedLogger = &RingLogger{} + type ringWithReader struct { *RingLogger } @@ -40,6 +41,7 @@ func newRingLogger(driver Logger, logInfo Info, maxSize int64) *RingLogger { l: driver, logInfo: logInfo, } + l.wg.Add(1) go l.run() return l } @@ -57,6 +59,15 @@ func NewRingLogger(driver Logger, logInfo Info, maxSize int64) Logger { return l } +// BufSize returns the buffer size of the underlying logger. +// Returns -1 if the logger doesn't match SizedLogger interface. +func (r *RingLogger) BufSize() int { + if sl, ok := r.l.(SizedLogger); ok { + return sl.BufSize() + } + return -1 +} + // Log queues messages into the ring buffer func (r *RingLogger) Log(msg *Message) error { if r.closed() { @@ -82,6 +93,7 @@ func (r *RingLogger) setClosed() { func (r *RingLogger) Close() error { r.setClosed() r.buffer.Close() + r.wg.Wait() // empty out the queue var logErr bool for _, msg := range r.buffer.Drain() { @@ -93,10 +105,7 @@ func (r *RingLogger) Close() error { } if err := r.l.Log(msg); err != nil { - logrus.WithField("driver", r.l.Name()). - WithField("container", r.logInfo.ContainerID). - WithError(err). - Errorf("Error writing log message") + logDriverError(r.l.Name(), string(msg.Line), err) logErr = true } } @@ -107,6 +116,7 @@ func (r *RingLogger) Close() error { // logger. // This is run in a goroutine when the RingLogger is created func (r *RingLogger) run() { + defer r.wg.Done() for { if r.closed() { return @@ -117,10 +127,7 @@ func (r *RingLogger) run() { return } if err := r.l.Log(msg); err != nil { - logrus.WithField("driver", r.l.Name()). - WithField("container", r.logInfo.ContainerID). - WithError(err). - Errorf("Error writing log message") + logDriverError(r.l.Name(), string(msg.Line), err) } } } diff --git a/daemon/logger/splunk/splunk.go b/daemon/logger/splunk/splunk.go index 8756ffa3b2928..5d76506dcbc8d 100644 --- a/daemon/logger/splunk/splunk.go +++ b/daemon/logger/splunk/splunk.go @@ -11,7 +11,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" @@ -24,6 +23,7 @@ import ( "github.com/docker/docker/daemon/logger/loggerutils" "github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/urlutil" + "github.com/google/uuid" "github.com/sirupsen/logrus" ) @@ -41,9 +41,11 @@ const ( splunkVerifyConnectionKey = "splunk-verify-connection" splunkGzipCompressionKey = "splunk-gzip" splunkGzipCompressionLevelKey = "splunk-gzip-level" + splunkIndexAcknowledgment = "splunk-index-acknowledgment" envKey = "env" envRegexKey = "env-regex" labelsKey = "labels" + labelsRegexKey = "labels-regex" tagKey = "tag" ) @@ -90,6 +92,7 @@ type splunkLogger struct { postMessagesFrequency time.Duration postMessagesBatchSize int bufferMaximum int + indexAck bool // For synchronization between background worker and logger. // We use channel to send messages to worker go routine. @@ -166,7 +169,8 @@ func New(info logger.Info) (logger.Logger, error) { return nil, fmt.Errorf("%s: %s is expected", driverName, splunkTokenKey) } - tlsConfig := &tls.Config{} + // FIXME set minimum TLS version for splunk (see https://github.com/moby/moby/issues/42443) + tlsConfig := &tls.Config{} //nolint: gosec // G402: TLS MinVersion too low. // Splunk is using autogenerated certificates by default, // allow users to trust them with skipping verification @@ -180,7 +184,7 @@ func New(info logger.Info) (logger.Logger, error) { // If path to the root certificate is provided - load it if caPath, ok := info.Config[splunkCAPathKey]; ok { - caCert, err := ioutil.ReadFile(caPath) + caCert, err := os.ReadFile(caPath) if err != nil { return nil, err } @@ -216,6 +220,14 @@ func New(info logger.Info) (logger.Logger, error) { } } + indexAck := false + if indexAckStr, ok := info.Config[splunkIndexAcknowledgment]; ok { + indexAck, err = strconv.ParseBool(indexAckStr) + if err != nil { + return nil, err + } + } + transport := &http.Transport{ TLSClientConfig: tlsConfig, Proxy: http.ProxyFromEnvironment, @@ -268,6 +280,7 @@ func New(info logger.Info) (logger.Logger, error) { postMessagesFrequency: postMessagesFrequency, postMessagesBatchSize: postMessagesBatchSize, bufferMaximum: bufferMaximum, + indexAck: indexAck, } // By default we verify connection, but we allow use to skip that @@ -494,7 +507,7 @@ func (l *splunkLogger) tryPostMessages(ctx context.Context, messages []*splunkMe return err } } - req, err := http.NewRequest("POST", l.url, bytes.NewBuffer(buffer.Bytes())) + req, err := http.NewRequest(http.MethodPost, l.url, bytes.NewBuffer(buffer.Bytes())) if err != nil { return err } @@ -504,17 +517,25 @@ func (l *splunkLogger) tryPostMessages(ctx context.Context, messages []*splunkMe if l.gzipCompression { req.Header.Set("Content-Encoding", "gzip") } + // Set the correct header if index acknowledgment is enabled + if l.indexAck { + requestChannel, err := uuid.NewRandom() + if err != nil { + return err + } + req.Header.Set("X-Splunk-Request-Channel", requestChannel.String()) + } resp, err := l.client.Do(req) if err != nil { return err } defer func() { - pools.Copy(ioutil.Discard, resp.Body) + pools.Copy(io.Discard, resp.Body) resp.Body.Close() }() if resp.StatusCode != http.StatusOK { rdr := io.LimitReader(resp.Body, maxResponseSize) - body, err := ioutil.ReadAll(rdr) + body, err := io.ReadAll(rdr) if err != nil { return err } @@ -562,9 +583,11 @@ func ValidateLogOpt(cfg map[string]string) error { case splunkVerifyConnectionKey: case splunkGzipCompressionKey: case splunkGzipCompressionLevelKey: + case splunkIndexAcknowledgment: case envKey: case envRegexKey: case labelsKey: + case labelsRegexKey: case tagKey: default: return fmt.Errorf("unknown log opt '%s' for %s log driver", key, driverName) @@ -607,13 +630,13 @@ func verifySplunkConnection(l *splunkLogger) error { return err } defer func() { - pools.Copy(ioutil.Discard, resp.Body) + pools.Copy(io.Discard, resp.Body) resp.Body.Close() }() if resp.StatusCode != http.StatusOK { rdr := io.LimitReader(resp.Body, maxResponseSize) - body, err := ioutil.ReadAll(rdr) + body, err := io.ReadAll(rdr) if err != nil { return err } diff --git a/daemon/logger/splunk/splunk_test.go b/daemon/logger/splunk/splunk_test.go index 654e96a309648..c5f3164d75506 100644 --- a/daemon/logger/splunk/splunk_test.go +++ b/daemon/logger/splunk/splunk_test.go @@ -11,8 +11,8 @@ import ( "time" "github.com/docker/docker/daemon/logger" - "gotest.tools/assert" - "gotest.tools/env" + "gotest.tools/v3/assert" + "gotest.tools/v3/env" ) // Validate options @@ -105,7 +105,7 @@ func TestNewWithProxy(t *testing.T) { proxyFunc := splunkLogger.transport.Proxy assert.Assert(t, proxyFunc != nil) - req, err := http.NewRequest("GET", splunkURL, nil) + req, err := http.NewRequest(http.MethodGet, splunkURL, nil) assert.NilError(t, err) proxyURL, err := proxyFunc(req) @@ -925,7 +925,12 @@ func TestFrequency(t *testing.T) { // 1 to verify connection and 10 to verify that we have sent messages with required frequency, // but because frequency is too small (to keep test quick), instead of 11, use 9 if context switches will be slow - if hec.numOfRequests < 9 { + expectedRequests := 9 + if runtime.GOOS == "windows" { + // sometimes in Windows, this test fails with number of requests showing 8. So be more conservative. + expectedRequests = 7 + } + if hec.numOfRequests < expectedRequests { t.Fatalf("Unexpected number of requests %d", hec.numOfRequests) } diff --git a/daemon/logger/splunk/splunkhecmock_test.go b/daemon/logger/splunk/splunkhecmock_test.go index a3a83ac103b7c..f592abe22865a 100644 --- a/daemon/logger/splunk/splunkhecmock_test.go +++ b/daemon/logger/splunk/splunkhecmock_test.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net" "net/http" "sync" @@ -150,7 +149,7 @@ func (hec *HTTPEventCollectorMock) ServeHTTP(writer http.ResponseWriter, request // Read body var body []byte - body, err = ioutil.ReadAll(reader) + body, err = io.ReadAll(reader) if err != nil { hec.test.Fatal(err) } diff --git a/daemon/logger/syslog/syslog.go b/daemon/logger/syslog/syslog.go index 94bdee364a6ae..fb20cf4def772 100644 --- a/daemon/logger/syslog/syslog.go +++ b/daemon/logger/syslog/syslog.go @@ -77,7 +77,7 @@ func rfc5424formatterWithAppNameAsTag(p syslog.Priority, hostname, tag, content // for multiple syntaxes, there are further restrictions in rfc5424, i.e., the maximum // resolution is limited to "TIME-SECFRAC" which is 6 (microsecond resolution) func rfc5424microformatterWithAppNameAsTag(p syslog.Priority, hostname, tag, content string) string { - timestamp := time.Now().Format("2006-01-02T15:04:05.999999Z07:00") + timestamp := time.Now().Format("2006-01-02T15:04:05.000000Z07:00") pid := os.Getpid() msg := fmt.Sprintf("<%d>%d %s %s %s %d %s - %s", p, 1, timestamp, hostname, tag, pid, tag, content) @@ -132,6 +132,10 @@ func New(info logger.Info) (logger.Logger, error) { } func (s *syslogger) Log(msg *logger.Message) error { + if len(msg.Line) == 0 { + return nil + } + line := string(msg.Line) source := msg.Source logger.PutMessage(msg) @@ -189,6 +193,7 @@ func ValidateLogOpt(cfg map[string]string) error { case "env": case "env-regex": case "labels": + case "labels-regex": case "syslog-address": case "syslog-facility": case "syslog-tls-ca-cert": diff --git a/daemon/logger/syslog/syslog_test.go b/daemon/logger/syslog/syslog_test.go index 4631788fbbbc2..fbba836e1157c 100644 --- a/daemon/logger/syslog/syslog_test.go +++ b/daemon/logger/syslog/syslog_test.go @@ -1,6 +1,7 @@ package syslog // import "github.com/docker/docker/daemon/logger/syslog" import ( + "net" "reflect" "testing" @@ -60,3 +61,100 @@ func TestValidateLogOptEmpty(t *testing.T) { t.Fatal("Failed to parse empty config", err) } } + +func TestValidateSyslogAddress(t *testing.T) { + err := ValidateLogOpt(map[string]string{ + "syslog-address": "this is not an uri", + }) + if err == nil { + t.Fatal("Expected error with invalid uri") + } + + // File exists + err = ValidateLogOpt(map[string]string{ + "syslog-address": "unix:///", + }) + if err != nil { + t.Fatal(err) + } + + // File does not exist + err = ValidateLogOpt(map[string]string{ + "syslog-address": "unix:///does_not_exist", + }) + if err == nil { + t.Fatal("Expected error when address is non existing file") + } + + // accepts udp and tcp URIs + err = ValidateLogOpt(map[string]string{ + "syslog-address": "udp://1.2.3.4", + }) + if err != nil { + t.Fatal(err) + } + + err = ValidateLogOpt(map[string]string{ + "syslog-address": "tcp://1.2.3.4", + }) + if err != nil { + t.Fatal(err) + } +} + +func TestParseAddressDefaultPort(t *testing.T) { + _, address, err := parseAddress("tcp://1.2.3.4") + if err != nil { + t.Fatal(err) + } + + _, port, _ := net.SplitHostPort(address) + if port != "514" { + t.Fatalf("Expected to default to port 514. It used port %s", port) + } +} + +func TestValidateSyslogFacility(t *testing.T) { + err := ValidateLogOpt(map[string]string{ + "syslog-facility": "Invalid facility", + }) + if err == nil { + t.Fatal("Expected error if facility level is invalid") + } +} + +func TestValidateLogOptSyslogFormat(t *testing.T) { + err := ValidateLogOpt(map[string]string{ + "syslog-format": "Invalid format", + }) + if err == nil { + t.Fatal("Expected error if format is invalid") + } +} + +func TestValidateLogOpt(t *testing.T) { + err := ValidateLogOpt(map[string]string{ + "env": "http://127.0.0.1", + "env-regex": "abc", + "labels": "labelA", + "labels-regex": "def", + "syslog-address": "udp://1.2.3.4:1111", + "syslog-facility": "daemon", + "syslog-tls-ca-cert": "/etc/ca-certificates/custom/ca.pem", + "syslog-tls-cert": "/etc/ca-certificates/custom/cert.pem", + "syslog-tls-key": "/etc/ca-certificates/custom/key.pem", + "syslog-tls-skip-verify": "true", + "tag": "true", + "syslog-format": "rfc3164", + }) + if err != nil { + t.Fatal(err) + } + + err = ValidateLogOpt(map[string]string{ + "not-supported-option": "a", + }) + if err == nil { + t.Fatal("Expecting error on unsupported options") + } +} diff --git a/daemon/logger/templates/templates_test.go b/daemon/logger/templates/templates_test.go index 25e7c88750781..2f8fad10d0623 100644 --- a/daemon/logger/templates/templates_test.go +++ b/daemon/logger/templates/templates_test.go @@ -4,8 +4,8 @@ import ( "bytes" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestNewParse(t *testing.T) { diff --git a/daemon/logs.go b/daemon/logs.go index 668a75c778306..567e3c7155aed 100644 --- a/daemon/logs.go +++ b/daemon/logs.go @@ -11,6 +11,7 @@ import ( timetypes "github.com/docker/docker/api/types/time" "github.com/docker/docker/container" "github.com/docker/docker/daemon/logger" + logcache "github.com/docker/docker/daemon/logger/loggerutils/cache" "github.com/docker/docker/errdefs" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -32,20 +33,20 @@ func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, c if !(config.ShowStdout || config.ShowStderr) { return nil, false, errdefs.InvalidParameter(errors.New("You must choose at least one stream")) } - container, err := daemon.GetContainer(containerName) + ctr, err := daemon.GetContainer(containerName) if err != nil { return nil, false, err } - if container.RemovalInProgress || container.Dead { + if ctr.RemovalInProgress || ctr.Dead { return nil, false, errdefs.Conflict(errors.New("can not get logs from container which is dead or marked for removal")) } - if container.HostConfig.LogConfig.Type == "none" { + if ctr.HostConfig.LogConfig.Type == "none" { return nil, false, logger.ErrReadLogsNotSupported{} } - cLog, cLogCreated, err := daemon.getLogger(container) + cLog, cLogCreated, err := daemon.getLogger(ctr) if err != nil { return nil, false, err } @@ -156,7 +157,7 @@ func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, c } } }() - return messageChan, container.Config.Tty, nil + return messageChan, ctr.Config.Tty, nil } func (daemon *Daemon) getLogger(container *container.Container) (l logger.Logger, created bool, err error) { @@ -190,6 +191,8 @@ func (daemon *Daemon) mergeAndVerifyLogConfig(cfg *containertypes.LogConfig) err } } + logcache.MergeDefaultLogConfig(cfg.Config, daemon.defaultLogConfig.Config) + return logger.ValidateLogOpts(cfg.Type, cfg.Config) } @@ -204,6 +207,7 @@ func (daemon *Daemon) setupDefaultLogConfig() error { Type: config.LogConfig.Type, Config: config.LogConfig.Config, } + logrus.Debugf("Using default logging driver %s", daemon.defaultLogConfig.Type) return nil } diff --git a/daemon/metrics.go b/daemon/metrics.go index 20030c427056f..ebe5b75a6e9bc 100644 --- a/daemon/metrics.go +++ b/daemon/metrics.go @@ -6,7 +6,7 @@ import ( "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" - "github.com/docker/go-metrics" + metrics "github.com/docker/go-metrics" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" @@ -17,6 +17,7 @@ const metricsPluginType = "MetricsCollector" var ( containerActions metrics.LabeledTimer networkActions metrics.LabeledTimer + hostInfoFunctions metrics.LabeledTimer engineInfo metrics.LabeledGauge engineCpus metrics.Gauge engineMemory metrics.Gauge @@ -38,6 +39,7 @@ func init() { } { containerActions.WithValues(a).Update(0) } + hostInfoFunctions = ns.NewLabeledTimer("host_info_functions", "The number of seconds it takes to call functions gathering info about the host", "function") networkActions = ns.NewLabeledTimer("network_actions", "The number of seconds it takes to process each network action", "action") engineInfo = ns.NewLabeledGauge("engine", "The information related to the engine and the OS it is running on", metrics.Unit("info"), @@ -45,8 +47,10 @@ func init() { "commit", "architecture", "graphdriver", - "kernel", "os", + "kernel", + "os", "os_type", + "os_version", "daemon_id", // ID is a randomly generated unique identifier (e.g. UUID4) ) engineCpus = ns.NewGauge("engine_cpus", "The number of cpus that the host system of the engine has", metrics.Unit("cpus")) @@ -111,8 +115,8 @@ func (ctr *stateCounter) Collect(ch chan<- prometheus.Metric) { ch <- prometheus.MustNewConstMetric(ctr.desc, prometheus.GaugeValue, float64(stopped), "stopped") } -func (d *Daemon) cleanupMetricsPlugins() { - ls := d.PluginStore.GetAllManagedPluginsByCap(metricsPluginType) +func (daemon *Daemon) cleanupMetricsPlugins() { + ls := daemon.PluginStore.GetAllManagedPluginsByCap(metricsPluginType) var wg sync.WaitGroup wg.Add(len(ls)) @@ -133,8 +137,8 @@ func (d *Daemon) cleanupMetricsPlugins() { } wg.Wait() - if d.metricsPluginListener != nil { - d.metricsPluginListener.Close() + if daemon.metricsPluginListener != nil { + daemon.metricsPluginListener.Close() } } @@ -143,7 +147,7 @@ type metricsPlugin interface { StopMetrics() error } -func makePluginAdapter(p plugingetter.CompatPlugin) (metricsPlugin, error) { // nolint: interfacer +func makePluginAdapter(p plugingetter.CompatPlugin) (metricsPlugin, error) { if pc, ok := p.(plugingetter.PluginWithV1Client); ok { return &metricsPluginAdapter{pc.Client(), p.Name()}, nil } diff --git a/daemon/metrics_unix.go b/daemon/metrics_unix.go index a9b5f810b0638..7869712541380 100644 --- a/daemon/metrics_unix.go +++ b/daemon/metrics_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" @@ -6,12 +7,13 @@ import ( "net" "net/http" "path/filepath" + "strings" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" "github.com/docker/docker/plugin" - "github.com/docker/go-metrics" - "github.com/opencontainers/runtime-spec/specs-go" + metrics "github.com/docker/go-metrics" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -28,7 +30,10 @@ func (daemon *Daemon) listenMetricsSock() (string, error) { mux := http.NewServeMux() mux.Handle("/metrics", metrics.Handler()) go func() { - http.Serve(l, mux) + logrus.Debugf("metrics API listening on %s", l.Addr()) + if err := http.Serve(l, mux); err != nil && !strings.Contains(err.Error(), "use of closed network connection") { + logrus.WithError(err).Error("error serving metrics API") + } }() daemon.metricsPluginListener = l return path, nil diff --git a/daemon/metrics_unsupported.go b/daemon/metrics_unsupported.go index 653c77fc32578..2a25b73d94959 100644 --- a/daemon/metrics_unsupported.go +++ b/daemon/metrics_unsupported.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package daemon // import "github.com/docker/docker/daemon" diff --git a/daemon/monitor.go b/daemon/monitor.go index 9b4452d7efc0b..36b82f17a7d60 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -2,16 +2,14 @@ package daemon // import "github.com/docker/docker/daemon" import ( "context" - "errors" - "fmt" - "runtime" "strconv" "time" "github.com/docker/docker/api/types" "github.com/docker/docker/container" - "github.com/docker/docker/libcontainerd" + libcontainerdtypes "github.com/docker/docker/libcontainerd/types" "github.com/docker/docker/restartmanager" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -26,17 +24,98 @@ func (daemon *Daemon) setStateCounter(c *container.Container) { } } +func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontainerdtypes.EventInfo) error { + c.Lock() + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + ec, et, err := daemon.containerd.DeleteTask(ctx, c.ID) + cancel() + if err != nil { + logrus.WithError(err).WithField("container", c.ID).Warnf("failed to delete container from containerd") + } + + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) + c.StreamConfig.Wait(ctx) + cancel() + + c.Reset(false) + + exitStatus := container.ExitStatus{ + ExitCode: int(ec), + ExitedAt: et, + } + if e != nil { + exitStatus.ExitCode = int(e.ExitCode) + exitStatus.ExitedAt = e.ExitedAt + exitStatus.OOMKilled = e.OOMKilled + if e.Error != nil { + c.SetError(e.Error) + } + } + + restart, wait, err := c.RestartManager().ShouldRestart(ec, daemon.IsShuttingDown() || c.HasBeenManuallyStopped, time.Since(c.StartedAt)) + + // cancel healthcheck here, they will be automatically + // restarted if/when the container is started again + daemon.stopHealthchecks(c) + attributes := map[string]string{ + "exitCode": strconv.Itoa(int(ec)), + } + daemon.Cleanup(c) + + if err == nil && restart { + c.RestartCount++ + c.SetRestarting(&exitStatus) + } else { + c.SetStopped(&exitStatus) + defer daemon.autoRemove(c) + } + defer c.Unlock() // needs to be called before autoRemove + + daemon.setStateCounter(c) + cpErr := c.CheckpointTo(daemon.containersReplica) + + daemon.LogContainerEventWithAttributes(c, "die", attributes) + + if err == nil && restart { + go func() { + err := <-wait + if err == nil { + // daemon.netController is initialized when daemon is restoring containers. + // But containerStart will use daemon.netController segment. + // So to avoid panic at startup process, here must wait util daemon restore done. + daemon.waitForStartupDone() + if err = daemon.containerStart(c, "", "", false); err != nil { + logrus.Debugf("failed to restart container: %+v", err) + } + } + if err != nil { + c.Lock() + c.SetStopped(&exitStatus) + daemon.setStateCounter(c) + c.CheckpointTo(daemon.containersReplica) + c.Unlock() + defer daemon.autoRemove(c) + if err != restartmanager.ErrRestartCanceled { + logrus.Errorf("restartmanger wait error: %+v", err) + } + } + }() + } + + return cpErr +} + // ProcessEvent is called by libcontainerd whenever an event occurs -func (daemon *Daemon) ProcessEvent(id string, e libcontainerd.EventType, ei libcontainerd.EventInfo) error { +func (daemon *Daemon) ProcessEvent(id string, e libcontainerdtypes.EventType, ei libcontainerdtypes.EventInfo) error { c, err := daemon.GetContainer(id) - if c == nil || err != nil { - return fmt.Errorf("no such container: %s", id) + if err != nil { + return errors.Wrapf(err, "could not find container %s", id) } switch e { - case libcontainerd.EventOOM: + case libcontainerdtypes.EventOOM: // StateOOM is Linux specific and should never be hit on Windows - if runtime.GOOS == "windows" { + if isWindows { return errors.New("received StateOOM from libcontainerd on Windows. This should never happen") } @@ -48,79 +127,23 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerd.EventType, ei libc } daemon.LogContainerEvent(c, "oom") - case libcontainerd.EventExit: + case libcontainerdtypes.EventExit: if int(ei.Pid) == c.Pid { - c.Lock() - _, _, err := daemon.containerd.DeleteTask(context.Background(), c.ID) - if err != nil { - logrus.WithError(err).Warnf("failed to delete container %s from containerd", c.ID) - } - - c.StreamConfig.Wait() - c.Reset(false) - - exitStatus := container.ExitStatus{ - ExitCode: int(ei.ExitCode), - ExitedAt: ei.ExitedAt, - OOMKilled: ei.OOMKilled, - } - restart, wait, err := c.RestartManager().ShouldRestart(ei.ExitCode, daemon.IsShuttingDown() || c.HasBeenManuallyStopped, time.Since(c.StartedAt)) - if err == nil && restart { - c.RestartCount++ - c.SetRestarting(&exitStatus) - } else { - if ei.Error != nil { - c.SetError(ei.Error) - } - c.SetStopped(&exitStatus) - defer daemon.autoRemove(c) - } - defer c.Unlock() // needs to be called before autoRemove - - // cancel healthcheck here, they will be automatically - // restarted if/when the container is started again - daemon.stopHealthchecks(c) - attributes := map[string]string{ - "exitCode": strconv.Itoa(int(ei.ExitCode)), - } - daemon.LogContainerEventWithAttributes(c, "die", attributes) - daemon.Cleanup(c) - - if err == nil && restart { - go func() { - err := <-wait - if err == nil { - // daemon.netController is initialized when daemon is restoring containers. - // But containerStart will use daemon.netController segment. - // So to avoid panic at startup process, here must wait util daemon restore done. - daemon.waitForStartupDone() - if err = daemon.containerStart(c, "", "", false); err != nil { - logrus.Debugf("failed to restart container: %+v", err) - } - } - if err != nil { - c.Lock() - c.SetStopped(&exitStatus) - c.Unlock() - defer daemon.autoRemove(c) - if err != restartmanager.ErrRestartCanceled { - logrus.Errorf("restartmanger wait error: %+v", err) - } - } - }() - } - - daemon.setStateCounter(c) - return c.CheckpointTo(daemon.containersReplica) + return daemon.handleContainerExit(c, &ei) } + exitCode := 127 if execConfig := c.ExecCommands.Get(ei.ProcessID); execConfig != nil { ec := int(ei.ExitCode) execConfig.Lock() defer execConfig.Unlock() execConfig.ExitCode = &ec execConfig.Running = false - execConfig.StreamConfig.Wait() + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + execConfig.StreamConfig.Wait(ctx) + cancel() + if err := execConfig.CloseStreams(); err != nil { logrus.Errorf("failed to cleanup exec %s streams: %s", c.ID, err) } @@ -128,19 +151,15 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerd.EventType, ei libc // remove the exec command from the container's store only and not the // daemon's store so that the exec command can be inspected. c.ExecCommands.Delete(execConfig.ID, execConfig.Pid) - attributes := map[string]string{ - "execID": execConfig.ID, - "exitCode": strconv.Itoa(ec), - } - daemon.LogContainerEventWithAttributes(c, "exec_die", attributes) - } else { - logrus.WithFields(logrus.Fields{ - "container": c.ID, - "exec-id": ei.ProcessID, - "exec-pid": ei.Pid, - }).Warn("Ignoring Exit Event, no such exec command found") + + exitCode = ec + } + attributes := map[string]string{ + "execID": ei.ProcessID, + "exitCode": strconv.Itoa(exitCode), } - case libcontainerd.EventStart: + daemon.LogContainerEventWithAttributes(c, "exec_die", attributes) + case libcontainerdtypes.EventStart: c.Lock() defer c.Unlock() @@ -159,7 +178,7 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerd.EventType, ei libc daemon.LogContainerEvent(c, "start") } - case libcontainerd.EventPaused: + case libcontainerdtypes.EventPaused: c.Lock() defer c.Unlock() @@ -172,7 +191,7 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerd.EventType, ei libc } daemon.LogContainerEvent(c, "pause") } - case libcontainerd.EventResumed: + case libcontainerdtypes.EventResumed: c.Lock() defer c.Unlock() @@ -198,15 +217,13 @@ func (daemon *Daemon) autoRemove(c *container.Container) { return } - var err error - if err = daemon.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err == nil { + err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}) + if err == nil { return } if c := daemon.containers.Get(c.ID); c == nil { return } - if err != nil { - logrus.WithError(err).WithField("container", c.ID).Error("error removing container") - } + logrus.WithError(err).WithField("container", c.ID).Error("error removing container") } diff --git a/daemon/names.go b/daemon/names.go index 6c319497772bb..4fa39af2eeb5e 100644 --- a/daemon/names.go +++ b/daemon/names.go @@ -38,7 +38,7 @@ func (daemon *Daemon) registerName(container *container.Container) error { func (daemon *Daemon) generateIDAndName(name string) (string, string, error) { var ( err error - id = stringid.GenerateNonCryptoID() + id = stringid.GenerateRandomID() ) if name == "" { diff --git a/daemon/network.go b/daemon/network.go index f7675a65680cb..fc84eaa164ce8 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -17,18 +17,18 @@ import ( clustertypes "github.com/docker/docker/daemon/cluster/provider" internalnetwork "github.com/docker/docker/daemon/network" "github.com/docker/docker/errdefs" + "github.com/docker/docker/libnetwork" + lncluster "github.com/docker/docker/libnetwork/cluster" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/networkdb" + "github.com/docker/docker/libnetwork/options" + networktypes "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/runconfig" "github.com/docker/go-connections/nat" - "github.com/docker/libnetwork" - lncluster "github.com/docker/libnetwork/cluster" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/networkdb" - "github.com/docker/libnetwork/options" - networktypes "github.com/docker/libnetwork/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -155,7 +155,7 @@ var ( func (daemon *Daemon) startIngressWorker() { ingressJobsChannel = make(chan *ingressJob, 100) go func() { - // nolint: gosimple + //nolint: gosimple for { select { case r := <-ingressJobsChannel: @@ -365,7 +365,7 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string n, err := c.NewNetwork(driver, create.Name, id, nwOptions...) if err != nil { if _, ok := err.(libnetwork.ErrDataStoreNotInitialized); ok { - // nolint: golint + //nolint: golint return nil, errors.New("This node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again.") } return nil, err @@ -430,12 +430,12 @@ func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnet // UpdateContainerServiceConfig updates a service configuration. func (daemon *Daemon) UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error { - container, err := daemon.GetContainer(containerName) + ctr, err := daemon.GetContainer(containerName) if err != nil { return err } - container.NetworkSettings.Service = serviceConfig + ctr.NetworkSettings.Service = serviceConfig return nil } @@ -443,24 +443,24 @@ func (daemon *Daemon) UpdateContainerServiceConfig(containerName string, service // network. If either cannot be found, an err is returned. If the // network cannot be set up, an err is returned. func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error { - container, err := daemon.GetContainer(containerName) + ctr, err := daemon.GetContainer(containerName) if err != nil { return err } - return daemon.ConnectToNetwork(container, networkName, endpointConfig) + return daemon.ConnectToNetwork(ctr, networkName, endpointConfig) } // DisconnectContainerFromNetwork disconnects the given container from // the given network. If either cannot be found, an err is returned. func (daemon *Daemon) DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error { - container, err := daemon.GetContainer(containerName) + ctr, err := daemon.GetContainer(containerName) if err != nil { if force { return daemon.ForceEndpointDelete(containerName, networkName) } return err } - return daemon.DisconnectFromNetwork(container, networkName, force) + return daemon.DisconnectFromNetwork(ctr, networkName, force) } // GetNetworkDriverList returns the list of plugins drivers @@ -485,10 +485,10 @@ func (daemon *Daemon) GetNetworkDriverList() []string { networks := daemon.netController.Networks() - for _, network := range networks { - if !pluginMap[network.Type()] { - pluginList = append(pluginList, network.Type()) - pluginMap[network.Type()] = true + for _, nw := range networks { + if !pluginMap[nw.Type()] { + pluginList = append(pluginList, nw.Type()) + pluginMap[nw.Type()] = true } } @@ -573,9 +573,9 @@ func (daemon *Daemon) GetNetworks(filter filters.Args, config types.NetworkListC } if config.Detailed { - for i, n := range list { - np := &n - buildDetailedNetworkResources(np, idx[n.ID], config.Verbose) + for i := range list { + np := &list[i] + buildDetailedNetworkResources(np, idx[np.ID], config.Verbose) list[i] = *np } } @@ -796,7 +796,7 @@ func buildCreateEndpointOptions(c *container.Container, n libnetwork.Network, ep defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() - if (!c.EnableServiceDiscoveryOnDefaultNetwork() && n.Name() == defaultNetName) || + if (!serviceDiscoveryOnDefaultNetwork() && n.Name() == defaultNetName) || c.NetworkSettings.IsAnonymousEndpoint { createOptions = append(createOptions, libnetwork.CreateOptionAnonymous()) } @@ -960,12 +960,6 @@ func buildCreateEndpointOptions(c *container.Container, n libnetwork.Network, ep return createOptions, nil } -// getEndpointInNetwork returns the container's endpoint to the provided network. -func getEndpointInNetwork(name string, n libnetwork.Network) (libnetwork.Endpoint, error) { - endpointName := strings.TrimPrefix(name, "/") - return n.EndpointByName(endpointName) -} - // getSandboxPortMapInfo retrieves the current port-mapping programmed for the given sandbox func getSandboxPortMapInfo(sb libnetwork.Sandbox) nat.PortMap { pm := nat.PortMap{} diff --git a/daemon/network/filter.go b/daemon/network/filter.go index 01a7d2dd81451..258a0f4446797 100644 --- a/daemon/network/filter.go +++ b/daemon/network/filter.go @@ -3,6 +3,7 @@ package network // import "github.com/docker/docker/daemon/network" import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/errdefs" "github.com/docker/docker/runconfig" "github.com/pkg/errors" ) @@ -51,6 +52,24 @@ func FilterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.N displayNet = append(displayNet, nw) } + if values := filter.Get("dangling"); len(values) > 0 { + if len(values) > 1 { + return nil, errdefs.InvalidParameter(errors.New(`got more than one value for filter key "dangling"`)) + } + + var danglingOnly bool + switch values[0] { + case "0", "false": + // dangling is false already + case "1", "true": + danglingOnly = true + default: + return nil, errdefs.InvalidParameter(errors.New(`invalid value for filter 'dangling', must be "true" (or "1"), or "false" (or "0")`)) + } + + displayNet = filterNetworkByUse(displayNet, danglingOnly) + } + if filter.Contains("type") { typeNet := []types.NetworkResource{} errFilter := filter.WalkValues("type", func(fval string) error { @@ -70,6 +89,25 @@ func FilterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.N return displayNet, nil } +func filterNetworkByUse(nws []types.NetworkResource, danglingOnly bool) []types.NetworkResource { + retNws := []types.NetworkResource{} + + filterFunc := func(nw types.NetworkResource) bool { + if danglingOnly { + return !runconfig.IsPreDefinedNetwork(nw.Name) && len(nw.Containers) == 0 && len(nw.Services) == 0 + } + return runconfig.IsPreDefinedNetwork(nw.Name) || len(nw.Containers) > 0 || len(nw.Services) > 0 + } + + for _, nw := range nws { + if filterFunc(nw) { + retNws = append(retNws, nw) + } + } + + return retNws +} + func filterNetworkByType(nws []types.NetworkResource, netType string) ([]types.NetworkResource, error) { retNws := []types.NetworkResource{} switch netType { diff --git a/daemon/network/filter_test.go b/daemon/network/filter_test.go index d3180a05a93f8..40634f80e1040 100644 --- a/daemon/network/filter_test.go +++ b/daemon/network/filter_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package network // import "github.com/docker/docker/daemon/network" @@ -42,6 +43,16 @@ func TestFilterNetworks(t *testing.T) { Driver: "mykvdriver", Scope: "global", }, + { + Name: "networkwithcontainer", + Driver: "nwc", + Scope: "local", + Containers: map[string]types.EndpointResource{ + "customcontainer": { + Name: "customendpoint", + }, + }, + }, } bridgeDriverFilters := filters.NewArgs() @@ -71,11 +82,18 @@ func TestFilterNetworks(t *testing.T) { globalScopeFilters := filters.NewArgs() globalScopeFilters.Add("scope", "global") + trueDanglingFilters := filters.NewArgs() + trueDanglingFilters.Add("dangling", "true") + + falseDanglingFilters := filters.NewArgs() + falseDanglingFilters.Add("dangling", "false") + testCases := []struct { filter filters.Args resultCount int err string name string + results []string }{ { filter: bridgeDriverFilters, @@ -97,7 +115,7 @@ func TestFilterNetworks(t *testing.T) { }, { filter: customDriverFilters, - resultCount: 3, + resultCount: 4, err: "", name: "custom driver filters", }, @@ -115,7 +133,7 @@ func TestFilterNetworks(t *testing.T) { }, { filter: localScopeFilters, - resultCount: 4, + resultCount: 5, err: "", name: "local scope filters", }, @@ -131,6 +149,20 @@ func TestFilterNetworks(t *testing.T) { err: "", name: "global scope filters", }, + { + filter: trueDanglingFilters, + resultCount: 3, + err: "", + name: "dangling filter is 'True'", + results: []string{"myoverlay", "mydrivernet", "mykvnet"}, + }, + { + filter: falseDanglingFilters, + resultCount: 4, + err: "", + name: "dangling filter is 'False'", + results: []string{"host", "bridge", "none", "networkwithcontainer"}, + }, } for _, testCase := range testCases { @@ -157,6 +189,18 @@ func TestFilterNetworks(t *testing.T) { if len(result) != testCase.resultCount { t.Fatalf("expect '%d' networks, got '%d' networks", testCase.resultCount, len(result)) } + + if len(testCase.results) > 0 { + resultMap := make(map[string]bool) + for _, r := range result { + resultMap[r.Name] = true + } + for _, r := range testCase.results { + if _, ok := resultMap[r]; !ok { + t.Fatalf("expected result: '%s' not found", r) + } + } + } } }) } diff --git a/daemon/network/settings.go b/daemon/network/settings.go index 7696d402018c8..39646c45ee36a 100644 --- a/daemon/network/settings.go +++ b/daemon/network/settings.go @@ -39,8 +39,8 @@ type EndpointSettings struct { // AttachmentStore stores the load balancer IP address for a network id. type AttachmentStore struct { sync.Mutex - //key: networkd id - //value: load balancer ip address + // key: networkd id + // value: load balancer ip address networkToNodeLBIP map[string]net.IP } diff --git a/daemon/network_windows.go b/daemon/network_windows.go new file mode 100644 index 0000000000000..75064cebd8cbe --- /dev/null +++ b/daemon/network_windows.go @@ -0,0 +1,13 @@ +package daemon + +import ( + "strings" + + "github.com/docker/docker/libnetwork" +) + +// getEndpointInNetwork returns the container's endpoint to the provided network. +func getEndpointInNetwork(name string, n libnetwork.Network) (libnetwork.Endpoint, error) { + endpointName := strings.TrimPrefix(name, "/") + return n.EndpointByName(endpointName) +} diff --git a/daemon/nvidia_linux.go b/daemon/nvidia_linux.go new file mode 100644 index 0000000000000..9f1c9141465f4 --- /dev/null +++ b/daemon/nvidia_linux.go @@ -0,0 +1,105 @@ +package daemon + +import ( + "os" + "os/exec" + "strconv" + "strings" + + "github.com/containerd/containerd/contrib/nvidia" + "github.com/docker/docker/pkg/capabilities" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +// TODO: nvidia should not be hard-coded, and should be a device plugin instead on the daemon object. +// TODO: add list of device capabilities in daemon/node info + +var errConflictCountDeviceIDs = errors.New("cannot set both Count and DeviceIDs on device request") + +const nvidiaHook = "nvidia-container-runtime-hook" + +// These are NVIDIA-specific capabilities stolen from github.com/containerd/containerd/contrib/nvidia.allCaps +var allNvidiaCaps = map[nvidia.Capability]struct{}{ + nvidia.Compute: {}, + nvidia.Compat32: {}, + nvidia.Graphics: {}, + nvidia.Utility: {}, + nvidia.Video: {}, + nvidia.Display: {}, +} + +func init() { + if _, err := exec.LookPath(nvidiaHook); err != nil { + // do not register Nvidia driver if helper binary is not present. + return + } + capset := capabilities.Set{"gpu": struct{}{}, "nvidia": struct{}{}} + nvidiaDriver := &deviceDriver{ + capset: capset, + updateSpec: setNvidiaGPUs, + } + for c := range allNvidiaCaps { + nvidiaDriver.capset[string(c)] = struct{}{} + } + registerDeviceDriver("nvidia", nvidiaDriver) +} + +func setNvidiaGPUs(s *specs.Spec, dev *deviceInstance) error { + req := dev.req + if req.Count != 0 && len(req.DeviceIDs) > 0 { + return errConflictCountDeviceIDs + } + + if len(req.DeviceIDs) > 0 { + s.Process.Env = append(s.Process.Env, "NVIDIA_VISIBLE_DEVICES="+strings.Join(req.DeviceIDs, ",")) + } else if req.Count > 0 { + s.Process.Env = append(s.Process.Env, "NVIDIA_VISIBLE_DEVICES="+countToDevices(req.Count)) + } else if req.Count < 0 { + s.Process.Env = append(s.Process.Env, "NVIDIA_VISIBLE_DEVICES=all") + } + + var nvidiaCaps []string + // req.Capabilities contains device capabilities, some but not all are NVIDIA driver capabilities. + for _, c := range dev.selectedCaps { + nvcap := nvidia.Capability(c) + if _, isNvidiaCap := allNvidiaCaps[nvcap]; isNvidiaCap { + nvidiaCaps = append(nvidiaCaps, c) + continue + } + // TODO: nvidia.WithRequiredCUDAVersion + // for now we let the prestart hook verify cuda versions but errors are not pretty. + } + + if nvidiaCaps != nil { + s.Process.Env = append(s.Process.Env, "NVIDIA_DRIVER_CAPABILITIES="+strings.Join(nvidiaCaps, ",")) + } + + path, err := exec.LookPath(nvidiaHook) + if err != nil { + return err + } + + if s.Hooks == nil { + s.Hooks = &specs.Hooks{} + } + s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{ + Path: path, + Args: []string{ + nvidiaHook, + "prestart", + }, + Env: os.Environ(), + }) + + return nil +} + +// countToDevices returns the list 0, 1, ... count-1 of deviceIDs. +func countToDevices(count int) string { + devices := make([]string, count) + for i := range devices { + devices[i] = strconv.Itoa(i) + } + return strings.Join(devices, ",") +} diff --git a/daemon/oci.go b/daemon/oci.go deleted file mode 100644 index 52050e24faee7..0000000000000 --- a/daemon/oci.go +++ /dev/null @@ -1,78 +0,0 @@ -package daemon // import "github.com/docker/docker/daemon" - -import ( - "fmt" - "regexp" - "strconv" - - "github.com/docker/docker/container" - "github.com/docker/docker/daemon/caps" - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -// nolint: gosimple -var ( - deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\*) ([rwm]{1,3})$") -) - -func setCapabilities(s *specs.Spec, c *container.Container) error { - var caplist []string - var err error - if c.HostConfig.Privileged { - caplist = caps.GetAllCapabilities() - } else { - caplist, err = caps.TweakCapabilities(s.Process.Capabilities.Bounding, c.HostConfig.CapAdd, c.HostConfig.CapDrop) - if err != nil { - return err - } - } - s.Process.Capabilities.Effective = caplist - s.Process.Capabilities.Bounding = caplist - s.Process.Capabilities.Permitted = caplist - s.Process.Capabilities.Inheritable = caplist - // setUser has already been executed here - // if non root drop capabilities in the way execve does - if s.Process.User.UID != 0 { - s.Process.Capabilities.Effective = []string{} - s.Process.Capabilities.Permitted = []string{} - } - return nil -} - -func appendDevicePermissionsFromCgroupRules(devPermissions []specs.LinuxDeviceCgroup, rules []string) ([]specs.LinuxDeviceCgroup, error) { - for _, deviceCgroupRule := range rules { - ss := deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1) - if len(ss[0]) != 5 { - return nil, fmt.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule) - } - matches := ss[0] - - dPermissions := specs.LinuxDeviceCgroup{ - Allow: true, - Type: matches[1], - Access: matches[4], - } - if matches[2] == "*" { - major := int64(-1) - dPermissions.Major = &major - } else { - major, err := strconv.ParseInt(matches[2], 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule) - } - dPermissions.Major = &major - } - if matches[3] == "*" { - minor := int64(-1) - dPermissions.Minor = &minor - } else { - minor, err := strconv.ParseInt(matches[3], 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid minor value in device cgroup rule format: '%s'", deviceCgroupRule) - } - dPermissions.Minor = &minor - } - devPermissions = append(devPermissions, dPermissions) - } - return devPermissions, nil -} diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index dfe02796a5083..5136b4097cc84 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -1,8 +1,8 @@ package daemon // import "github.com/docker/docker/daemon" import ( + "context" "fmt" - "io" "os" "os/exec" "path/filepath" @@ -10,201 +10,206 @@ import ( "strconv" "strings" + cdcgroups "github.com/containerd/cgroups" + "github.com/containerd/containerd/containers" + coci "github.com/containerd/containerd/oci" + "github.com/containerd/containerd/pkg/apparmor" + "github.com/containerd/containerd/pkg/userns" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" daemonconfig "github.com/docker/docker/daemon/config" "github.com/docker/docker/oci" + "github.com/docker/docker/oci/caps" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/mount" + "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/rootless/specconv" volumemounts "github.com/docker/docker/volume/mounts" - "github.com/opencontainers/runc/libcontainer/apparmor" + "github.com/moby/sys/mount" + "github.com/moby/sys/mountinfo" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/devices" "github.com/opencontainers/runc/libcontainer/user" - "github.com/opencontainers/runtime-spec/specs-go" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) -const ( - inContainerInitPath = "/sbin/" + daemonconfig.DefaultInitBinary -) - -func setResources(s *specs.Spec, r containertypes.Resources) error { - weightDevices, err := getBlkioWeightDevices(r) - if err != nil { - return err - } - readBpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceReadBps) - if err != nil { - return err - } - writeBpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceWriteBps) - if err != nil { - return err - } - readIOpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceReadIOps) - if err != nil { - return err - } - writeIOpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceWriteIOps) - if err != nil { - return err - } - - memoryRes := getMemoryResources(r) - cpuRes, err := getCPUResources(r) - if err != nil { - return err - } - blkioWeight := r.BlkioWeight - - specResources := &specs.LinuxResources{ - Memory: memoryRes, - CPU: cpuRes, - BlockIO: &specs.LinuxBlockIO{ - Weight: &blkioWeight, - WeightDevice: weightDevices, - ThrottleReadBpsDevice: readBpsDevice, - ThrottleWriteBpsDevice: writeBpsDevice, - ThrottleReadIOPSDevice: readIOpsDevice, - ThrottleWriteIOPSDevice: writeIOpsDevice, - }, - Pids: &specs.LinuxPids{ - Limit: r.PidsLimit, - }, - } +const inContainerInitPath = "/sbin/" + daemonconfig.DefaultInitBinary + +// WithRlimits sets the container's rlimits along with merging the daemon's rlimits +func WithRlimits(daemon *Daemon, c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + var rlimits []specs.POSIXRlimit + + // We want to leave the original HostConfig alone so make a copy here + hostConfig := *c.HostConfig + // Merge with the daemon defaults + daemon.mergeUlimits(&hostConfig) + for _, ul := range hostConfig.Ulimits { + rlimits = append(rlimits, specs.POSIXRlimit{ + Type: "RLIMIT_" + strings.ToUpper(ul.Name), + Soft: uint64(ul.Soft), + Hard: uint64(ul.Hard), + }) + } - if s.Linux.Resources != nil && len(s.Linux.Resources.Devices) > 0 { - specResources.Devices = s.Linux.Resources.Devices + s.Process.Rlimits = rlimits + return nil } - - s.Linux.Resources = specResources - return nil } -func setDevices(s *specs.Spec, c *container.Container) error { - // Build lists of devices allowed and created within the container. - var devs []specs.LinuxDevice - devPermissions := s.Linux.Resources.Devices - if c.HostConfig.Privileged { - hostDevices, err := devices.HostDevices() - if err != nil { - return err +// WithLibnetwork sets the libnetwork hook +func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + if s.Hooks == nil { + s.Hooks = &specs.Hooks{} } - for _, d := range hostDevices { - devs = append(devs, oci.Device(d)) - } - devPermissions = []specs.LinuxDeviceCgroup{ - { - Allow: true, - Access: "rwm", - }, + for _, ns := range s.Linux.Namespaces { + if ns.Type == "network" && ns.Path == "" && !c.Config.NetworkDisabled { + target := filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe") + shortNetCtlrID := stringid.TruncateID(daemon.netController.ID()) + s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{ + Path: target, + Args: []string{ + "libnetwork-setkey", + "-exec-root=" + daemon.configStore.GetExecRoot(), + c.ID, + shortNetCtlrID, + }, + }) + } } - } else { - for _, deviceMapping := range c.HostConfig.Devices { - d, dPermissions, err := oci.DevicesFromPath(deviceMapping.PathOnHost, deviceMapping.PathInContainer, deviceMapping.CgroupPermissions) + return nil + } +} + +// WithRootless sets the spec to the rootless configuration +func WithRootless(daemon *Daemon) coci.SpecOpts { + return func(_ context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + var v2Controllers []string + if daemon.getCgroupDriver() == cgroupSystemdDriver { + if cdcgroups.Mode() != cdcgroups.Unified { + return errors.New("rootless systemd driver doesn't support cgroup v1") + } + rootlesskitParentEUID := os.Getenv("ROOTLESSKIT_PARENT_EUID") + if rootlesskitParentEUID == "" { + return errors.New("$ROOTLESSKIT_PARENT_EUID is not set (requires RootlessKit v0.8.0)") + } + euid, err := strconv.Atoi(rootlesskitParentEUID) + if err != nil { + return errors.Wrap(err, "invalid $ROOTLESSKIT_PARENT_EUID: must be a numeric value") + } + controllersPath := fmt.Sprintf("/sys/fs/cgroup/user.slice/user-%d.slice/cgroup.controllers", euid) + controllersFile, err := os.ReadFile(controllersPath) if err != nil { return err } - devs = append(devs, d...) - devPermissions = append(devPermissions, dPermissions...) - } - - var err error - devPermissions, err = appendDevicePermissionsFromCgroupRules(devPermissions, c.HostConfig.DeviceCgroupRules) - if err != nil { - return err + v2Controllers = strings.Fields(string(controllersFile)) } + return specconv.ToRootless(s, v2Controllers) } +} - s.Linux.Devices = append(s.Linux.Devices, devs...) - s.Linux.Resources.Devices = devPermissions - return nil +// WithOOMScore sets the oom score +func WithOOMScore(score *int) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + s.Process.OOMScoreAdj = score + return nil + } } -func (daemon *Daemon) setRlimits(s *specs.Spec, c *container.Container) error { - var rlimits []specs.POSIXRlimit - - // We want to leave the original HostConfig alone so make a copy here - hostConfig := *c.HostConfig - // Merge with the daemon defaults - daemon.mergeUlimits(&hostConfig) - for _, ul := range hostConfig.Ulimits { - rlimits = append(rlimits, specs.POSIXRlimit{ - Type: "RLIMIT_" + strings.ToUpper(ul.Name), - Soft: uint64(ul.Soft), - Hard: uint64(ul.Hard), - }) +// WithSelinux sets the selinux labels +func WithSelinux(c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + s.Process.SelinuxLabel = c.GetProcessLabel() + s.Linux.MountLabel = c.MountLabel + return nil } +} - s.Process.Rlimits = rlimits - return nil +// WithApparmor sets the apparmor profile +func WithApparmor(c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + if apparmor.HostSupports() { + var appArmorProfile string + if c.AppArmorProfile != "" { + appArmorProfile = c.AppArmorProfile + } else if c.HostConfig.Privileged { + appArmorProfile = unconfinedAppArmorProfile + } else { + appArmorProfile = defaultAppArmorProfile + } + + if appArmorProfile == defaultAppArmorProfile { + // Unattended upgrades and other fun services can unload AppArmor + // profiles inadvertently. Since we cannot store our profile in + // /etc/apparmor.d, nor can we practically add other ways of + // telling the system to keep our profile loaded, in order to make + // sure that we keep the default profile enabled we dynamically + // reload it if necessary. + if err := ensureDefaultAppArmorProfile(); err != nil { + return err + } + } + s.Process.ApparmorProfile = appArmorProfile + } + return nil + } } -func setUser(s *specs.Spec, c *container.Container) error { - uid, gid, additionalGids, err := getUser(c, c.Config.User) - if err != nil { - return err +// WithCapabilities sets the container's capabilties +func WithCapabilities(c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + capabilities, err := caps.TweakCapabilities( + caps.DefaultCapabilities(), + c.HostConfig.CapAdd, + c.HostConfig.CapDrop, + c.HostConfig.Privileged, + ) + if err != nil { + return err + } + return oci.SetCapabilities(s, capabilities) } - s.Process.User.UID = uid - s.Process.User.GID = gid - s.Process.User.AdditionalGids = additionalGids - return nil } -func readUserFile(c *container.Container, p string) (io.ReadCloser, error) { - fp, err := c.GetResourcePath(p) +func resourcePath(c *container.Container, getPath func() (string, error)) (string, error) { + p, err := getPath() if err != nil { - return nil, err + return "", err } - return os.Open(fp) + return c.GetResourcePath(p) } -func getUser(c *container.Container, username string) (uint32, uint32, []uint32, error) { - passwdPath, err := user.GetPasswdPath() +func getUser(c *container.Container, username string) (specs.User, error) { + var usr specs.User + passwdPath, err := resourcePath(c, user.GetPasswdPath) if err != nil { - return 0, 0, nil, err + return usr, err } - groupPath, err := user.GetGroupPath() + groupPath, err := resourcePath(c, user.GetGroupPath) if err != nil { - return 0, 0, nil, err - } - passwdFile, err := readUserFile(c, passwdPath) - if err == nil { - defer passwdFile.Close() + return usr, err } - groupFile, err := readUserFile(c, groupPath) - if err == nil { - defer groupFile.Close() - } - - execUser, err := user.GetExecUser(username, nil, passwdFile, groupFile) + execUser, err := user.GetExecUserPath(username, nil, passwdPath, groupPath) if err != nil { - return 0, 0, nil, err + return usr, err } + usr.UID = uint32(execUser.Uid) + usr.GID = uint32(execUser.Gid) - // todo: fix this double read by a change to libcontainer/user pkg - groupFile, err = readUserFile(c, groupPath) - if err == nil { - defer groupFile.Close() - } var addGroups []int if len(c.HostConfig.GroupAdd) > 0 { - addGroups, err = user.GetAdditionalGroups(c.HostConfig.GroupAdd, groupFile) + addGroups, err = user.GetAdditionalGroupsPath(c.HostConfig.GroupAdd, groupPath) if err != nil { - return 0, 0, nil, err + return usr, err } } - uid := uint32(execUser.Uid) - gid := uint32(execUser.Gid) - sgids := append(execUser.Sgids, addGroups...) - var additionalGids []uint32 - for _, g := range sgids { - additionalGids = append(additionalGids, uint32(g)) + for _, g := range append(execUser.Sgids, addGroups...) { + usr.AdditionalGids = append(usr.AdditionalGids, uint32(g)) } - return uid, gid, additionalGids, nil + return usr, nil } func setNamespace(s *specs.Spec, ns specs.LinuxNamespace) { @@ -217,99 +222,118 @@ func setNamespace(s *specs.Spec, ns specs.LinuxNamespace) { s.Linux.Namespaces = append(s.Linux.Namespaces, ns) } -func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error { - userNS := false - // user - if c.HostConfig.UsernsMode.IsPrivate() { - uidMap := daemon.idMapping.UIDs() - if uidMap != nil { - userNS = true - ns := specs.LinuxNamespace{Type: "user"} +// WithNamespaces sets the container's namespaces +func WithNamespaces(daemon *Daemon, c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + userNS := false + // user + if c.HostConfig.UsernsMode.IsPrivate() { + uidMap := daemon.idMapping.UIDs() + if uidMap != nil { + userNS = true + ns := specs.LinuxNamespace{Type: "user"} + setNamespace(s, ns) + s.Linux.UIDMappings = specMapping(uidMap) + s.Linux.GIDMappings = specMapping(daemon.idMapping.GIDs()) + } + } + // network + if !c.Config.NetworkDisabled { + ns := specs.LinuxNamespace{Type: "network"} + parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2) + if parts[0] == "container" { + nc, err := daemon.getNetworkedContainer(c.ID, c.HostConfig.NetworkMode.ConnectedContainer()) + if err != nil { + return err + } + ns.Path = fmt.Sprintf("/proc/%d/ns/net", nc.State.GetPID()) + if userNS { + // to share a net namespace, they must also share a user namespace + nsUser := specs.LinuxNamespace{Type: "user"} + nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", nc.State.GetPID()) + setNamespace(s, nsUser) + } + } else if c.HostConfig.NetworkMode.IsHost() { + ns.Path = c.NetworkSettings.SandboxKey + } setNamespace(s, ns) - s.Linux.UIDMappings = specMapping(uidMap) - s.Linux.GIDMappings = specMapping(daemon.idMapping.GIDs()) } - } - // network - if !c.Config.NetworkDisabled { - ns := specs.LinuxNamespace{Type: "network"} - parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2) - if parts[0] == "container" { - nc, err := daemon.getNetworkedContainer(c.ID, c.HostConfig.NetworkMode.ConnectedContainer()) + + // ipc + ipcMode := c.HostConfig.IpcMode + switch { + case ipcMode.IsContainer(): + ns := specs.LinuxNamespace{Type: "ipc"} + ic, err := daemon.getIpcContainer(ipcMode.Container()) if err != nil { return err } - ns.Path = fmt.Sprintf("/proc/%d/ns/net", nc.State.GetPID()) + ns.Path = fmt.Sprintf("/proc/%d/ns/ipc", ic.State.GetPID()) + setNamespace(s, ns) if userNS { - // to share a net namespace, they must also share a user namespace + // to share an IPC namespace, they must also share a user namespace nsUser := specs.LinuxNamespace{Type: "user"} - nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", nc.State.GetPID()) + nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", ic.State.GetPID()) setNamespace(s, nsUser) } - } else if c.HostConfig.NetworkMode.IsHost() { - ns.Path = c.NetworkSettings.SandboxKey + case ipcMode.IsHost(): + oci.RemoveNamespace(s, "ipc") + case ipcMode.IsEmpty(): + // A container was created by an older version of the daemon. + // The default behavior used to be what is now called "shareable". + fallthrough + case ipcMode.IsPrivate(), ipcMode.IsShareable(), ipcMode.IsNone(): + ns := specs.LinuxNamespace{Type: "ipc"} + setNamespace(s, ns) + default: + return fmt.Errorf("Invalid IPC mode: %v", ipcMode) } - setNamespace(s, ns) - } - // ipc - ipcMode := c.HostConfig.IpcMode - switch { - case ipcMode.IsContainer(): - ns := specs.LinuxNamespace{Type: "ipc"} - ic, err := daemon.getIpcContainer(ipcMode.Container()) - if err != nil { - return err + // pid + if c.HostConfig.PidMode.IsContainer() { + pc, err := daemon.getPidContainer(c) + if err != nil { + return err + } + ns := specs.LinuxNamespace{ + Type: "pid", + Path: fmt.Sprintf("/proc/%d/ns/pid", pc.State.GetPID()), + } + setNamespace(s, ns) + if userNS { + // to share a PID namespace, they must also share a user namespace + nsUser := specs.LinuxNamespace{ + Type: "user", + Path: fmt.Sprintf("/proc/%d/ns/user", pc.State.GetPID()), + } + setNamespace(s, nsUser) + } + } else if c.HostConfig.PidMode.IsHost() { + oci.RemoveNamespace(s, "pid") + } else { + ns := specs.LinuxNamespace{Type: "pid"} + setNamespace(s, ns) } - ns.Path = fmt.Sprintf("/proc/%d/ns/ipc", ic.State.GetPID()) - setNamespace(s, ns) - if userNS { - // to share an IPC namespace, they must also share a user namespace - nsUser := specs.LinuxNamespace{Type: "user"} - nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", ic.State.GetPID()) - setNamespace(s, nsUser) - } - case ipcMode.IsHost(): - oci.RemoveNamespace(s, specs.LinuxNamespaceType("ipc")) - case ipcMode.IsEmpty(): - // A container was created by an older version of the daemon. - // The default behavior used to be what is now called "shareable". - fallthrough - case ipcMode.IsPrivate(), ipcMode.IsShareable(), ipcMode.IsNone(): - ns := specs.LinuxNamespace{Type: "ipc"} - setNamespace(s, ns) - default: - return fmt.Errorf("Invalid IPC mode: %v", ipcMode) - } - - // pid - if c.HostConfig.PidMode.IsContainer() { - ns := specs.LinuxNamespace{Type: "pid"} - pc, err := daemon.getPidContainer(c) - if err != nil { - return err + // uts + if c.HostConfig.UTSMode.IsHost() { + oci.RemoveNamespace(s, "uts") + s.Hostname = "" } - ns.Path = fmt.Sprintf("/proc/%d/ns/pid", pc.State.GetPID()) - setNamespace(s, ns) - if userNS { - // to share a PID namespace, they must also share a user namespace - nsUser := specs.LinuxNamespace{Type: "user"} - nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", pc.State.GetPID()) - setNamespace(s, nsUser) + + // cgroup + if !c.HostConfig.CgroupnsMode.IsEmpty() { + cgroupNsMode := c.HostConfig.CgroupnsMode + if !cgroupNsMode.Valid() { + return fmt.Errorf("invalid cgroup namespace mode: %v", cgroupNsMode) + } + if cgroupNsMode.IsPrivate() { + nsCgroup := specs.LinuxNamespace{Type: "cgroup"} + setNamespace(s, nsCgroup) + } } - } else if c.HostConfig.PidMode.IsHost() { - oci.RemoveNamespace(s, specs.LinuxNamespaceType("pid")) - } else { - ns := specs.LinuxNamespace{Type: "pid"} - setNamespace(s, ns) - } - // uts - if c.HostConfig.UTSMode.IsHost() { - oci.RemoveNamespace(s, specs.LinuxNamespaceType("uts")) - s.Hostname = "" - } - return nil + return nil + } } func specMapping(s []idtools.IDMap) []specs.LinuxIDMapping { @@ -333,7 +357,7 @@ func getSourceMount(source string) (string, string, error) { return "", "", err } - mi, err := mount.GetMounts(mount.ParentsFilter(sourcePath)) + mi, err := mountinfo.GetMounts(mountinfo.ParentsFilter(sourcePath)) if err != nil { return "", "", err } @@ -357,9 +381,9 @@ const ( slavePropagationOption = "master:" ) -// hasMountinfoOption checks if any of the passed any of the given option values +// hasMountInfoOption checks if any of the passed any of the given option values // are set in the passed in option string. -func hasMountinfoOption(opts string, vals ...string) bool { +func hasMountInfoOption(opts string, vals ...string) bool { for _, opt := range strings.Split(opts, " ") { for _, val := range vals { if strings.HasPrefix(opt, val) { @@ -377,7 +401,7 @@ func ensureShared(path string) error { return err } // Make sure source mount point is shared. - if !hasMountinfoOption(optionalOpts, sharedPropagationOption) { + if !hasMountInfoOption(optionalOpts, sharedPropagationOption) { return errors.Errorf("path %s is mounted on %s but it is not a shared mount", path, sourceMount) } return nil @@ -390,7 +414,7 @@ func ensureSharedOrSlave(path string) error { return err } - if !hasMountinfoOption(optionalOpts, sharedPropagationOption, slavePropagationOption) { + if !hasMountInfoOption(optionalOpts, sharedPropagationOption, slavePropagationOption) { return errors.Errorf("path %s is mounted on %s but it is not a shared or slave mount", path, sourceMount) } return nil @@ -459,394 +483,573 @@ func inSlice(slice []string, s string) bool { return false } -func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []container.Mount) error { - userMounts := make(map[string]struct{}) - for _, m := range mounts { - userMounts[m.Destination] = struct{}{} - } - - // Copy all mounts from spec to defaultMounts, except for - // - mounts overridden by a user supplied mount; - // - all mounts under /dev if a user supplied /dev is present; - // - /dev/shm, in case IpcMode is none. - // While at it, also - // - set size for /dev/shm from shmsize. - defaultMounts := s.Mounts[:0] - _, mountDev := userMounts["/dev"] - for _, m := range s.Mounts { - if _, ok := userMounts[m.Destination]; ok { - // filter out mount overridden by a user supplied mount - continue - } - if mountDev && strings.HasPrefix(m.Destination, "/dev/") { - // filter out everything under /dev if /dev is user-mounted - continue - } - - if m.Destination == "/dev/shm" { - if c.HostConfig.IpcMode.IsNone() { - // filter out /dev/shm for "none" IpcMode - continue +// WithMounts sets the container's mounts +func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) (err error) { + if err := daemon.setupContainerMountsRoot(c); err != nil { + return err + } + + if err := daemon.setupIpcDirs(c); err != nil { + return err + } + + defer func() { + if err != nil { + daemon.cleanupSecretDir(c) } - // set size for /dev/shm mount from spec - sizeOpt := "size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10) - m.Options = append(m.Options, sizeOpt) + }() + + if err := daemon.setupSecretDir(c); err != nil { + return err } - defaultMounts = append(defaultMounts, m) - } + ms, err := daemon.setupMounts(c) + if err != nil { + return err + } + + if !c.HostConfig.IpcMode.IsPrivate() && !c.HostConfig.IpcMode.IsEmpty() { + ms = append(ms, c.IpcMounts()...) + } + + tmpfsMounts, err := c.TmpfsMounts() + if err != nil { + return err + } + ms = append(ms, tmpfsMounts...) - s.Mounts = defaultMounts - for _, m := range mounts { - if m.Source == "tmpfs" { - data := m.Data - parser := volumemounts.NewParser("linux") - options := []string{"noexec", "nosuid", "nodev", string(parser.DefaultPropagationMode())} - if data != "" { - options = append(options, strings.Split(data, ",")...) + secretMounts, err := c.SecretMounts() + if err != nil { + return err + } + ms = append(ms, secretMounts...) + + sort.Sort(mounts(ms)) + + mounts := ms + + userMounts := make(map[string]struct{}) + for _, m := range mounts { + userMounts[m.Destination] = struct{}{} + } + + // Copy all mounts from spec to defaultMounts, except for + // - mounts overridden by a user supplied mount; + // - all mounts under /dev if a user supplied /dev is present; + // - /dev/shm, in case IpcMode is none. + // While at it, also + // - set size for /dev/shm from shmsize. + defaultMounts := s.Mounts[:0] + _, mountDev := userMounts["/dev"] + for _, m := range s.Mounts { + if _, ok := userMounts[m.Destination]; ok { + // filter out mount overridden by a user supplied mount + continue + } + if mountDev && strings.HasPrefix(m.Destination, "/dev/") { + // filter out everything under /dev if /dev is user-mounted + continue } - merged, err := mount.MergeTmpfsOptions(options) - if err != nil { - return err + if m.Destination == "/dev/shm" { + if c.HostConfig.IpcMode.IsNone() { + // filter out /dev/shm for "none" IpcMode + continue + } + // set size for /dev/shm mount from spec + sizeOpt := "size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10) + m.Options = append(m.Options, sizeOpt) } - s.Mounts = append(s.Mounts, specs.Mount{Destination: m.Destination, Source: m.Source, Type: "tmpfs", Options: merged}) - continue + defaultMounts = append(defaultMounts, m) } - mt := specs.Mount{Destination: m.Destination, Source: m.Source, Type: "bind"} + s.Mounts = defaultMounts + for _, m := range mounts { + if m.Source == "tmpfs" { + data := m.Data + parser := volumemounts.NewParser() + options := []string{"noexec", "nosuid", "nodev", string(parser.DefaultPropagationMode())} + if data != "" { + options = append(options, strings.Split(data, ",")...) + } - // Determine property of RootPropagation based on volume - // properties. If a volume is shared, then keep root propagation - // shared. This should work for slave and private volumes too. - // - // For slave volumes, it can be either [r]shared/[r]slave. - // - // For private volumes any root propagation value should work. - pFlag := mountPropagationMap[m.Propagation] - switch pFlag { - case mount.SHARED, mount.RSHARED: - if err := ensureShared(m.Source); err != nil { - return err - } - rootpg := mountPropagationMap[s.Linux.RootfsPropagation] - if rootpg != mount.SHARED && rootpg != mount.RSHARED { - s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.SHARED] - } - case mount.SLAVE, mount.RSLAVE: - var fallback bool - if err := ensureSharedOrSlave(m.Source); err != nil { - // For backwards compatibility purposes, treat mounts from the daemon root - // as special since we automatically add rslave propagation to these mounts - // when the user did not set anything, so we should fallback to the old - // behavior which is to use private propagation which is normally the - // default. - if !strings.HasPrefix(m.Source, daemon.root) && !strings.HasPrefix(daemon.root, m.Source) { + merged, err := mount.MergeTmpfsOptions(options) + if err != nil { return err } - cm, ok := c.MountPoints[m.Destination] - if !ok { + s.Mounts = append(s.Mounts, specs.Mount{Destination: m.Destination, Source: m.Source, Type: "tmpfs", Options: merged}) + continue + } + + mt := specs.Mount{Destination: m.Destination, Source: m.Source, Type: "bind"} + + // Determine property of RootPropagation based on volume + // properties. If a volume is shared, then keep root propagation + // shared. This should work for slave and private volumes too. + // + // For slave volumes, it can be either [r]shared/[r]slave. + // + // For private volumes any root propagation value should work. + pFlag := mountPropagationMap[m.Propagation] + switch pFlag { + case mount.SHARED, mount.RSHARED: + if err := ensureShared(m.Source); err != nil { return err } - if cm.Spec.BindOptions != nil && cm.Spec.BindOptions.Propagation != "" { - // This means the user explicitly set a propagation, do not fallback in that case. - return err + rootpg := mountPropagationMap[s.Linux.RootfsPropagation] + if rootpg != mount.SHARED && rootpg != mount.RSHARED { + s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.SHARED] + } + case mount.SLAVE, mount.RSLAVE: + var fallback bool + if err := ensureSharedOrSlave(m.Source); err != nil { + // For backwards compatibility purposes, treat mounts from the daemon root + // as special since we automatically add rslave propagation to these mounts + // when the user did not set anything, so we should fallback to the old + // behavior which is to use private propagation which is normally the + // default. + if !strings.HasPrefix(m.Source, daemon.root) && !strings.HasPrefix(daemon.root, m.Source) { + return err + } + + cm, ok := c.MountPoints[m.Destination] + if !ok { + return err + } + if cm.Spec.BindOptions != nil && cm.Spec.BindOptions.Propagation != "" { + // This means the user explicitly set a propagation, do not fallback in that case. + return err + } + fallback = true + logrus.WithField("container", c.ID).WithField("source", m.Source).Warn("Falling back to default propagation for bind source in daemon root") + } + if !fallback { + rootpg := mountPropagationMap[s.Linux.RootfsPropagation] + if rootpg != mount.SHARED && rootpg != mount.RSHARED && rootpg != mount.SLAVE && rootpg != mount.RSLAVE { + s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.RSLAVE] + } } - fallback = true - logrus.WithField("container", c.ID).WithField("source", m.Source).Warn("Falling back to default propagation for bind source in daemon root") } - if !fallback { - rootpg := mountPropagationMap[s.Linux.RootfsPropagation] - if rootpg != mount.SHARED && rootpg != mount.RSHARED && rootpg != mount.SLAVE && rootpg != mount.RSLAVE { - s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.RSLAVE] + + bindMode := "rbind" + if m.NonRecursive { + bindMode = "bind" + } + opts := []string{bindMode} + if !m.Writable { + opts = append(opts, "ro") + } + if pFlag != 0 { + opts = append(opts, mountPropagationReverseMap[pFlag]) + } + + // If we are using user namespaces, then we must make sure that we + // don't drop any of the CL_UNPRIVILEGED "locked" flags of the source + // "mount" when we bind-mount. The reason for this is that at the point + // when runc sets up the root filesystem, it is already inside a user + // namespace, and thus cannot change any flags that are locked. + if daemon.configStore.RemappedRoot != "" || userns.RunningInUserNS() { + unprivOpts, err := getUnprivilegedMountFlags(m.Source) + if err != nil { + return err } + opts = append(opts, unprivOpts...) } + + mt.Options = opts + s.Mounts = append(s.Mounts, mt) } - opts := []string{"rbind"} - if !m.Writable { - opts = append(opts, "ro") + if s.Root.Readonly { + for i, m := range s.Mounts { + switch m.Destination { + case "/proc", "/dev/pts", "/dev/shm", "/dev/mqueue", "/dev": + continue + } + if _, ok := userMounts[m.Destination]; !ok { + if !inSlice(m.Options, "ro") { + s.Mounts[i].Options = append(s.Mounts[i].Options, "ro") + } + } + } } - if pFlag != 0 { - opts = append(opts, mountPropagationReverseMap[pFlag]) + + if c.HostConfig.Privileged { + // clear readonly for /sys + for i := range s.Mounts { + if s.Mounts[i].Destination == "/sys" { + clearReadOnly(&s.Mounts[i]) + } + } + s.Linux.ReadonlyPaths = nil + s.Linux.MaskedPaths = nil } - // If we are using user namespaces, then we must make sure that we - // don't drop any of the CL_UNPRIVILEGED "locked" flags of the source - // "mount" when we bind-mount. The reason for this is that at the point - // when runc sets up the root filesystem, it is already inside a user - // namespace, and thus cannot change any flags that are locked. - if daemon.configStore.RemappedRoot != "" { - unprivOpts, err := getUnprivilegedMountFlags(m.Source) - if err != nil { - return err + // TODO: until a kernel/mount solution exists for handling remount in a user namespace, + // we must clear the readonly flag for the cgroups mount (@mrunalp concurs) + if uidMap := daemon.idMapping.UIDs(); uidMap != nil || c.HostConfig.Privileged { + for i, m := range s.Mounts { + if m.Type == "cgroup" { + clearReadOnly(&s.Mounts[i]) + } } - opts = append(opts, unprivOpts...) } - mt.Options = opts - s.Mounts = append(s.Mounts, mt) + return nil + } +} - if s.Root.Readonly { - for i, m := range s.Mounts { - switch m.Destination { - case "/proc", "/dev/pts", "/dev/shm", "/dev/mqueue", "/dev": - continue - } - if _, ok := userMounts[m.Destination]; !ok { - if !inSlice(m.Options, "ro") { - s.Mounts[i].Options = append(s.Mounts[i].Options, "ro") +// sysctlExists checks if a sysctl exists; runc will error if we add any that do not actually +// exist, so do not add the default ones if running on an old kernel. +func sysctlExists(s string) bool { + f := filepath.Join("/proc", "sys", strings.Replace(s, ".", "/", -1)) + _, err := os.Stat(f) + return err == nil +} + +// WithCommonOptions sets common docker options +func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + if c.BaseFS == nil { + return errors.New("populateCommonSpec: BaseFS of container " + c.ID + " is unexpectedly nil") + } + linkedEnv, err := daemon.setupLinkedContainers(c) + if err != nil { + return err + } + s.Root = &specs.Root{ + Path: c.BaseFS.Path(), + Readonly: c.HostConfig.ReadonlyRootfs, + } + if err := c.SetupWorkingDirectory(daemon.idMapping.RootPair()); err != nil { + return err + } + cwd := c.Config.WorkingDir + if len(cwd) == 0 { + cwd = "/" + } + s.Process.Args = append([]string{c.Path}, c.Args...) + + // only add the custom init if it is specified and the container is running in its + // own private pid namespace. It does not make sense to add if it is running in the + // host namespace or another container's pid namespace where we already have an init + if c.HostConfig.PidMode.IsPrivate() { + if (c.HostConfig.Init != nil && *c.HostConfig.Init) || + (c.HostConfig.Init == nil && daemon.configStore.Init) { + s.Process.Args = append([]string{inContainerInitPath, "--", c.Path}, c.Args...) + path := daemon.configStore.InitPath + if path == "" { + path, err = exec.LookPath(daemonconfig.DefaultInitBinary) + if err != nil { + return err + } } + s.Mounts = append(s.Mounts, specs.Mount{ + Destination: inContainerInitPath, + Type: "bind", + Source: path, + Options: []string{"bind", "ro"}, + }) } } - } - - if c.HostConfig.Privileged { - // clear readonly for /sys - for i := range s.Mounts { - if s.Mounts[i].Destination == "/sys" { - clearReadOnly(&s.Mounts[i]) + s.Process.Cwd = cwd + s.Process.Env = c.CreateDaemonEnvironment(c.Config.Tty, linkedEnv) + s.Process.Terminal = c.Config.Tty + + s.Hostname = c.Config.Hostname + setLinuxDomainname(c, s) + + // Add default sysctls that are generally safe and useful; currently we + // grant the capabilities to allow these anyway. You can override if + // you want to restore the original behaviour. + // We do not set network sysctls if network namespace is host, or if we are + // joining an existing namespace, only if we create a new net namespace. + if c.HostConfig.NetworkMode.IsPrivate() { + // We cannot set up ping socket support in a user namespace + userNS := daemon.configStore.RemappedRoot != "" && c.HostConfig.UsernsMode.IsPrivate() + if !userNS && !userns.RunningInUserNS() && sysctlExists("net.ipv4.ping_group_range") { + // allow unprivileged ICMP echo sockets without CAP_NET_RAW + s.Linux.Sysctl["net.ipv4.ping_group_range"] = "0 2147483647" + } + // allow opening any port less than 1024 without CAP_NET_BIND_SERVICE + if sysctlExists("net.ipv4.ip_unprivileged_port_start") { + s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"] = "0" } } - s.Linux.ReadonlyPaths = nil - s.Linux.MaskedPaths = nil + + return nil } +} - // TODO: until a kernel/mount solution exists for handling remount in a user namespace, - // we must clear the readonly flag for the cgroups mount (@mrunalp concurs) - if uidMap := daemon.idMapping.UIDs(); uidMap != nil || c.HostConfig.Privileged { - for i, m := range s.Mounts { - if m.Type == "cgroup" { - clearReadOnly(&s.Mounts[i]) +// WithCgroups sets the container's cgroups +func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + var cgroupsPath string + scopePrefix := "docker" + parent := "/docker" + useSystemd := UsingSystemd(daemon.configStore) + if useSystemd { + parent = "system.slice" + if daemon.configStore.Rootless { + parent = "user.slice" } } - } - return nil -} + if c.HostConfig.CgroupParent != "" { + parent = c.HostConfig.CgroupParent + } else if daemon.configStore.CgroupParent != "" { + parent = daemon.configStore.CgroupParent + } -func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container) error { - if c.BaseFS == nil { - return errors.New("populateCommonSpec: BaseFS of container " + c.ID + " is unexpectedly nil") - } - linkedEnv, err := daemon.setupLinkedContainers(c) - if err != nil { - return err - } - s.Root = &specs.Root{ - Path: c.BaseFS.Path(), - Readonly: c.HostConfig.ReadonlyRootfs, - } - if err := c.SetupWorkingDirectory(daemon.idMapping.RootPair()); err != nil { - return err - } - cwd := c.Config.WorkingDir - if len(cwd) == 0 { - cwd = "/" - } - s.Process.Args = append([]string{c.Path}, c.Args...) - - // only add the custom init if it is specified and the container is running in its - // own private pid namespace. It does not make sense to add if it is running in the - // host namespace or another container's pid namespace where we already have an init - if c.HostConfig.PidMode.IsPrivate() { - if (c.HostConfig.Init != nil && *c.HostConfig.Init) || - (c.HostConfig.Init == nil && daemon.configStore.Init) { - s.Process.Args = append([]string{inContainerInitPath, "--", c.Path}, c.Args...) - path := daemon.configStore.InitPath - if path == "" { - path, err = exec.LookPath(daemonconfig.DefaultInitBinary) - if err != nil { - return err - } - } - s.Mounts = append(s.Mounts, specs.Mount{ - Destination: inContainerInitPath, - Type: "bind", - Source: path, - Options: []string{"bind", "ro"}, - }) + if useSystemd { + cgroupsPath = parent + ":" + scopePrefix + ":" + c.ID + logrus.Debugf("createSpec: cgroupsPath: %s", cgroupsPath) + } else { + cgroupsPath = filepath.Join(parent, c.ID) } - } - s.Process.Cwd = cwd - s.Process.Env = c.CreateDaemonEnvironment(c.Config.Tty, linkedEnv) - s.Process.Terminal = c.Config.Tty - s.Hostname = c.FullHostname() + s.Linux.CgroupsPath = cgroupsPath - return nil -} + // the rest is only needed for CPU RT controller -func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, err error) { - s := oci.DefaultSpec() - if err := daemon.populateCommonSpec(&s, c); err != nil { - return nil, err - } + if daemon.configStore.CPURealtimePeriod == 0 && daemon.configStore.CPURealtimeRuntime == 0 { + return nil + } - var cgroupsPath string - scopePrefix := "docker" - parent := "/docker" - useSystemd := UsingSystemd(daemon.configStore) - if useSystemd { - parent = "system.slice" - } + if cdcgroups.Mode() == cdcgroups.Unified { + return errors.New("daemon-scoped cpu-rt-period and cpu-rt-runtime are not implemented for cgroup v2") + } - if c.HostConfig.CgroupParent != "" { - parent = c.HostConfig.CgroupParent - } else if daemon.configStore.CgroupParent != "" { - parent = daemon.configStore.CgroupParent - } + // FIXME this is very expensive way to check if cpu rt is supported + sysInfo := daemon.RawSysInfo() + if !sysInfo.CPURealtime { + return errors.New("daemon-scoped cpu-rt-period and cpu-rt-runtime are not supported by the kernel") + } - if useSystemd { - cgroupsPath = parent + ":" + scopePrefix + ":" + c.ID - logrus.Debugf("createSpec: cgroupsPath: %s", cgroupsPath) - } else { - cgroupsPath = filepath.Join(parent, c.ID) - } - s.Linux.CgroupsPath = cgroupsPath + p := cgroupsPath + if useSystemd { + initPath, err := cgroups.GetInitCgroup("cpu") + if err != nil { + return errors.Wrap(err, "unable to init CPU RT controller") + } + _, err = cgroups.GetOwnCgroup("cpu") + if err != nil { + return errors.Wrap(err, "unable to init CPU RT controller") + } + p = filepath.Join(initPath, s.Linux.CgroupsPath) + } - if err := setResources(&s, c.HostConfig.Resources); err != nil { - return nil, fmt.Errorf("linux runtime spec resources: %v", err) - } - s.Linux.Sysctl = c.HostConfig.Sysctls + // Clean path to guard against things like ../../../BAD + parentPath := filepath.Dir(p) + if !filepath.IsAbs(parentPath) { + parentPath = filepath.Clean("/" + parentPath) + } - p := s.Linux.CgroupsPath - if useSystemd { - initPath, err := cgroups.GetInitCgroup("cpu") + mnt, root, err := cgroups.FindCgroupMountpointAndRoot("", "cpu") if err != nil { - return nil, err + return errors.Wrap(err, "unable to init CPU RT controller") } - _, err = cgroups.GetOwnCgroup("cpu") - if err != nil { - return nil, err + // When docker is run inside docker, the root is based of the host cgroup. + // Should this be handled in runc/libcontainer/cgroups ? + if strings.HasPrefix(root, "/docker/") { + root = "/" } - p = filepath.Join(initPath, s.Linux.CgroupsPath) - } + mnt = filepath.Join(mnt, root) - // Clean path to guard against things like ../../../BAD - parentPath := filepath.Dir(p) - if !filepath.IsAbs(parentPath) { - parentPath = filepath.Clean("/" + parentPath) + if err := daemon.initCPURtController(mnt, parentPath); err != nil { + return errors.Wrap(err, "unable to init CPU RT controller") + } + return nil } +} - if err := daemon.initCgroupsPath(parentPath); err != nil { - return nil, fmt.Errorf("linux init cgroups path: %v", err) - } - if err := setDevices(&s, c); err != nil { - return nil, fmt.Errorf("linux runtime spec devices: %v", err) - } - if err := daemon.setRlimits(&s, c); err != nil { - return nil, fmt.Errorf("linux runtime spec rlimits: %v", err) - } - if err := setUser(&s, c); err != nil { - return nil, fmt.Errorf("linux spec user: %v", err) - } - if err := setNamespaces(daemon, &s, c); err != nil { - return nil, fmt.Errorf("linux spec namespaces: %v", err) - } - if err := setCapabilities(&s, c); err != nil { - return nil, fmt.Errorf("linux spec capabilities: %v", err) - } - if err := setSeccomp(daemon, &s, c); err != nil { - return nil, fmt.Errorf("linux seccomp: %v", err) - } +// WithDevices sets the container's devices +func WithDevices(daemon *Daemon, c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + // Build lists of devices allowed and created within the container. + var devs []specs.LinuxDevice + devPermissions := s.Linux.Resources.Devices - if err := daemon.setupContainerMountsRoot(c); err != nil { - return nil, err - } + if c.HostConfig.Privileged && !userns.RunningInUserNS() { + hostDevices, err := devices.HostDevices() + if err != nil { + return err + } + for _, d := range hostDevices { + devs = append(devs, oci.Device(d)) + } - if err := daemon.setupIpcDirs(c); err != nil { - return nil, err - } + // adding device mappings in privileged containers + for _, deviceMapping := range c.HostConfig.Devices { + // issue a warning that custom cgroup permissions are ignored in privileged mode + if deviceMapping.CgroupPermissions != "rwm" { + logrus.WithField("container", c.ID).Warnf("custom %s permissions for device %s are ignored in privileged mode", deviceMapping.CgroupPermissions, deviceMapping.PathOnHost) + } + // issue a warning that the device path already exists via /dev mounting in privileged mode + if deviceMapping.PathOnHost == deviceMapping.PathInContainer { + logrus.WithField("container", c.ID).Warnf("path in container %s already exists in privileged mode", deviceMapping.PathInContainer) + continue + } + d, _, err := oci.DevicesFromPath(deviceMapping.PathOnHost, deviceMapping.PathInContainer, "rwm") + if err != nil { + return err + } + devs = append(devs, d...) + } - defer func() { - if err != nil { - daemon.cleanupSecretDir(c) + devPermissions = []specs.LinuxDeviceCgroup{ + { + Allow: true, + Access: "rwm", + }, + } + } else { + for _, deviceMapping := range c.HostConfig.Devices { + d, dPermissions, err := oci.DevicesFromPath(deviceMapping.PathOnHost, deviceMapping.PathInContainer, deviceMapping.CgroupPermissions) + if err != nil { + return err + } + devs = append(devs, d...) + devPermissions = append(devPermissions, dPermissions...) + } + + var err error + devPermissions, err = oci.AppendDevicePermissionsFromCgroupRules(devPermissions, c.HostConfig.DeviceCgroupRules) + if err != nil { + return err + } } - }() - if err := daemon.setupSecretDir(c); err != nil { - return nil, err - } + s.Linux.Devices = append(s.Linux.Devices, devs...) + s.Linux.Resources.Devices = devPermissions - ms, err := daemon.setupMounts(c) - if err != nil { - return nil, err + for _, req := range c.HostConfig.DeviceRequests { + if err := daemon.handleDevice(req, s); err != nil { + return err + } + } + return nil } +} - if !c.HostConfig.IpcMode.IsPrivate() && !c.HostConfig.IpcMode.IsEmpty() { - ms = append(ms, c.IpcMounts()...) - } +// WithResources applies the container resources +func WithResources(c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + r := c.HostConfig.Resources + weightDevices, err := getBlkioWeightDevices(r) + if err != nil { + return err + } + readBpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceReadBps) + if err != nil { + return err + } + writeBpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceWriteBps) + if err != nil { + return err + } + readIOpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceReadIOps) + if err != nil { + return err + } + writeIOpsDevice, err := getBlkioThrottleDevices(r.BlkioDeviceWriteIOps) + if err != nil { + return err + } - tmpfsMounts, err := c.TmpfsMounts() - if err != nil { - return nil, err - } - ms = append(ms, tmpfsMounts...) + memoryRes := getMemoryResources(r) + cpuRes, err := getCPUResources(r) + if err != nil { + return err + } + blkioWeight := r.BlkioWeight + + specResources := &specs.LinuxResources{ + Memory: memoryRes, + CPU: cpuRes, + BlockIO: &specs.LinuxBlockIO{ + Weight: &blkioWeight, + WeightDevice: weightDevices, + ThrottleReadBpsDevice: readBpsDevice, + ThrottleWriteBpsDevice: writeBpsDevice, + ThrottleReadIOPSDevice: readIOpsDevice, + ThrottleWriteIOPSDevice: writeIOpsDevice, + }, + Pids: getPidsLimit(r), + } - secretMounts, err := c.SecretMounts() - if err != nil { - return nil, err - } - ms = append(ms, secretMounts...) + if s.Linux.Resources != nil && len(s.Linux.Resources.Devices) > 0 { + specResources.Devices = s.Linux.Resources.Devices + } - sort.Sort(mounts(ms)) - if err := setMounts(daemon, &s, c, ms); err != nil { - return nil, fmt.Errorf("linux mounts: %v", err) + s.Linux.Resources = specResources + return nil } +} - for _, ns := range s.Linux.Namespaces { - if ns.Type == "network" && ns.Path == "" && !c.Config.NetworkDisabled { - target := filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe") - s.Hooks = &specs.Hooks{ - Prestart: []specs.Hook{{ - Path: target, - Args: []string{"libnetwork-setkey", "-exec-root=" + daemon.configStore.GetExecRoot(), c.ID, daemon.netController.ID()}, - }}, - } +// WithSysctls sets the container's sysctls +func WithSysctls(c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + // We merge the sysctls injected above with the HostConfig (latter takes + // precedence for backwards-compatibility reasons). + for k, v := range c.HostConfig.Sysctls { + s.Linux.Sysctl[k] = v } + return nil } +} - if apparmor.IsEnabled() { - var appArmorProfile string - if c.AppArmorProfile != "" { - appArmorProfile = c.AppArmorProfile - } else if c.HostConfig.Privileged { - appArmorProfile = "unconfined" - } else { - appArmorProfile = "docker-default" - } - - if appArmorProfile == "docker-default" { - // Unattended upgrades and other fun services can unload AppArmor - // profiles inadvertently. Since we cannot store our profile in - // /etc/apparmor.d, nor can we practically add other ways of - // telling the system to keep our profile loaded, in order to make - // sure that we keep the default profile enabled we dynamically - // reload it if necessary. - if err := ensureDefaultAppArmorProfile(); err != nil { - return nil, err - } - } +// WithUser sets the container's user +func WithUser(c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + var err error + s.Process.User, err = getUser(c, c.Config.User) + return err + } +} - s.Process.ApparmorProfile = appArmorProfile +func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, err error) { + var ( + opts []coci.SpecOpts + s = oci.DefaultSpec() + ) + opts = append(opts, + WithCommonOptions(daemon, c), + WithCgroups(daemon, c), + WithResources(c), + WithSysctls(c), + WithDevices(daemon, c), + WithUser(c), + WithRlimits(daemon, c), + WithNamespaces(daemon, c), + WithCapabilities(c), + WithSeccomp(daemon, c), + WithMounts(daemon, c), + WithLibnetwork(daemon, c), + WithApparmor(c), + WithSelinux(c), + WithOOMScore(&c.HostConfig.OomScoreAdj), + ) + if c.NoNewPrivileges { + opts = append(opts, coci.WithNoNewPrivileges) } - s.Process.SelinuxLabel = c.GetProcessLabel() - s.Process.NoNewPrivileges = c.NoNewPrivileges - s.Process.OOMScoreAdj = &c.HostConfig.OomScoreAdj - s.Linux.MountLabel = c.MountLabel // Set the masked and readonly paths with regard to the host config options if they are set. if c.HostConfig.MaskedPaths != nil { - s.Linux.MaskedPaths = c.HostConfig.MaskedPaths + opts = append(opts, coci.WithMaskedPaths(c.HostConfig.MaskedPaths)) } if c.HostConfig.ReadonlyPaths != nil { - s.Linux.ReadonlyPaths = c.HostConfig.ReadonlyPaths + opts = append(opts, coci.WithReadonlyPaths(c.HostConfig.ReadonlyPaths)) } - - return &s, nil + if daemon.configStore.Rootless { + opts = append(opts, WithRootless(daemon)) + } + return &s, coci.ApplyOpts(context.Background(), nil, &containers.Container{ + ID: c.ID, + }, &s, opts...) } func clearReadOnly(m *specs.Mount) { diff --git a/daemon/oci_linux_test.go b/daemon/oci_linux_test.go index 7b31970aae8e3..6e60d07f7cf0f 100644 --- a/daemon/oci_linux_test.go +++ b/daemon/oci_linux_test.go @@ -2,51 +2,81 @@ package daemon // import "github.com/docker/docker/daemon" import ( "os" + "path/filepath" "testing" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/daemon/config" - "github.com/docker/docker/oci" + "github.com/docker/docker/daemon/network" + "github.com/docker/docker/libnetwork" + "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/skip" ) +func setupFakeDaemon(t *testing.T, c *container.Container) *Daemon { + root, err := os.MkdirTemp("", "oci_linux_test-root") + assert.NilError(t, err) + + rootfs := filepath.Join(root, "rootfs") + err = os.MkdirAll(rootfs, 0755) + assert.NilError(t, err) + + netController, err := libnetwork.New() + assert.NilError(t, err) + + d := &Daemon{ + // some empty structs to avoid getting a panic + // caused by a null pointer dereference + idMapping: &idtools.IdentityMapping{}, + configStore: &config.Config{}, + linkIndex: newLinkIndex(), + netController: netController, + } + + c.Root = root + c.BaseFS = containerfs.NewLocalContainerFS(rootfs) + + if c.Config == nil { + c.Config = new(containertypes.Config) + } + if c.HostConfig == nil { + c.HostConfig = new(containertypes.HostConfig) + } + if c.NetworkSettings == nil { + c.NetworkSettings = &network.Settings{Networks: make(map[string]*network.EndpointSettings)} + } + + return d +} + +func cleanupFakeContainer(c *container.Container) { + _ = os.RemoveAll(c.Root) +} + // TestTmpfsDevShmNoDupMount checks that a user-specified /dev/shm tmpfs // mount (as in "docker run --tmpfs /dev/shm:rw,size=NNN") does not result // in "Duplicate mount point" error from the engine. // https://github.com/moby/moby/issues/35455 func TestTmpfsDevShmNoDupMount(t *testing.T) { - d := Daemon{ - // some empty structs to avoid getting a panic - // caused by a null pointer dereference - idMapping: &idtools.IdentityMapping{}, - configStore: &config.Config{}, - } + skip.If(t, os.Getuid() != 0, "skipping test that requires root") c := &container.Container{ ShmPath: "foobar", // non-empty, for c.IpcMounts() to work HostConfig: &containertypes.HostConfig{ - IpcMode: containertypes.IpcMode("shareable"), // default mode + IpcMode: containertypes.IPCModeShareable, // default mode // --tmpfs /dev/shm:rw,exec,size=NNN Tmpfs: map[string]string{ "/dev/shm": "rw,exec,size=1g", }, }, } + d := setupFakeDaemon(t, c) + defer cleanupFakeContainer(c) - // Mimic the code flow of daemon.createSpec(), enough to reproduce the issue - ms, err := d.setupMounts(c) - assert.Check(t, err) - - ms = append(ms, c.IpcMounts()...) - - tmpfsMounts, err := c.TmpfsMounts() - assert.Check(t, err) - ms = append(ms, tmpfsMounts...) - - s := oci.DefaultSpec() - err = setMounts(&d, &s, c, ms) + _, err := d.createSpec(c) assert.Check(t, err) } @@ -55,28 +85,17 @@ func TestTmpfsDevShmNoDupMount(t *testing.T) { // the resulting /dev/shm mount is NOT made read-only. // https://github.com/moby/moby/issues/36503 func TestIpcPrivateVsReadonly(t *testing.T) { - d := Daemon{ - // some empty structs to avoid getting a panic - // caused by a null pointer dereference - idMapping: &idtools.IdentityMapping{}, - configStore: &config.Config{}, - } + skip.If(t, os.Getuid() != 0, "skipping test that requires root") c := &container.Container{ HostConfig: &containertypes.HostConfig{ - IpcMode: containertypes.IpcMode("private"), + IpcMode: containertypes.IPCModePrivate, ReadonlyRootfs: true, }, } + d := setupFakeDaemon(t, c) + defer cleanupFakeContainer(c) - // We can't call createSpec() so mimick the minimal part - // of its code flow, just enough to reproduce the issue. - ms, err := d.setupMounts(c) - assert.Check(t, err) - - s := oci.DefaultSpec() - s.Root.Readonly = c.HostConfig.ReadonlyRootfs - - err = setMounts(&d, &s, c, ms) + s, err := d.createSpec(c) assert.Check(t, err) // Find the /dev/shm mount in ms, check it does not have ro @@ -88,6 +107,89 @@ func TestIpcPrivateVsReadonly(t *testing.T) { } } +// TestSysctlOverride ensures that any implicit sysctls (such as +// Config.Domainname) are overridden by an explicit sysctl in the HostConfig. +func TestSysctlOverride(t *testing.T) { + skip.If(t, os.Getuid() != 0, "skipping test that requires root") + c := &container.Container{ + Config: &containertypes.Config{ + Hostname: "foobar", + Domainname: "baz.cyphar.com", + }, + HostConfig: &containertypes.HostConfig{ + NetworkMode: "bridge", + Sysctls: map[string]string{}, + }, + } + d := setupFakeDaemon(t, c) + defer cleanupFakeContainer(c) + + // Ensure that the implicit sysctl is set correctly. + s, err := d.createSpec(c) + assert.NilError(t, err) + assert.Equal(t, s.Hostname, "foobar") + assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.Config.Domainname) + if sysctlExists("net.ipv4.ip_unprivileged_port_start") { + assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "0") + } + if sysctlExists("net.ipv4.ping_group_range") { + assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647") + } + + // Set an explicit sysctl. + c.HostConfig.Sysctls["kernel.domainname"] = "foobar.net" + assert.Assert(t, c.HostConfig.Sysctls["kernel.domainname"] != c.Config.Domainname) + c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024" + + s, err = d.createSpec(c) + assert.NilError(t, err) + assert.Equal(t, s.Hostname, "foobar") + assert.Equal(t, s.Linux.Sysctl["kernel.domainname"], c.HostConfig.Sysctls["kernel.domainname"]) + assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"]) + + // Ensure the ping_group_range is not set on a daemon with user-namespaces enabled + d.configStore.RemappedRoot = "dummy:dummy" + s, err = d.createSpec(c) + assert.NilError(t, err) + _, ok := s.Linux.Sysctl["net.ipv4.ping_group_range"] + assert.Assert(t, !ok) + + // Ensure the ping_group_range is set on a container in "host" userns mode + // on a daemon with user-namespaces enabled + c.HostConfig.UsernsMode = "host" + s, err = d.createSpec(c) + assert.NilError(t, err) + assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "0 2147483647") +} + +// TestSysctlOverrideHost ensures that any implicit network sysctls are not set +// with host networking +func TestSysctlOverrideHost(t *testing.T) { + skip.If(t, os.Getuid() != 0, "skipping test that requires root") + c := &container.Container{ + Config: &containertypes.Config{}, + HostConfig: &containertypes.HostConfig{ + NetworkMode: "host", + Sysctls: map[string]string{}, + }, + } + d := setupFakeDaemon(t, c) + defer cleanupFakeContainer(c) + + // Ensure that the implicit sysctl is not set + s, err := d.createSpec(c) + assert.NilError(t, err) + assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], "") + assert.Equal(t, s.Linux.Sysctl["net.ipv4.ping_group_range"], "") + + // Set an explicit sysctl. + c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"] = "1024" + + s, err = d.createSpec(c) + assert.NilError(t, err) + assert.Equal(t, s.Linux.Sysctl["net.ipv4.ip_unprivileged_port_start"], c.HostConfig.Sysctls["net.ipv4.ip_unprivileged_port_start"]) +} + func TestGetSourceMount(t *testing.T) { // must be able to find source mount for / mnt, _, err := getSourceMount("/") diff --git a/daemon/oci_utils.go b/daemon/oci_utils.go new file mode 100644 index 0000000000000..2d833502bd3a9 --- /dev/null +++ b/daemon/oci_utils.go @@ -0,0 +1,16 @@ +package daemon // import "github.com/docker/docker/daemon" + +import ( + "github.com/docker/docker/container" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +func setLinuxDomainname(c *container.Container, s *specs.Spec) { + // There isn't a field in the OCI for the NIS domainname, but luckily there + // is a sysctl which has an identical effect to setdomainname(2) so there's + // no explicit need for runtime support. + s.Linux.Sysctl = make(map[string]string) + if c.Config.Domainname != "" { + s.Linux.Sysctl["kernel.domainname"] = c.Config.Domainname + } +} diff --git a/daemon/oci_windows.go b/daemon/oci_windows.go index 6279d7dd20ab0..bede371f69985 100644 --- a/daemon/oci_windows.go +++ b/daemon/oci_windows.go @@ -1,20 +1,22 @@ package daemon // import "github.com/docker/docker/daemon" import ( + "encoding/json" "fmt" - "io/ioutil" + "os" "path/filepath" - "runtime" "strings" + "github.com/Microsoft/hcsshim/osversion" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" + "github.com/docker/docker/errdefs" "github.com/docker/docker/oci" "github.com/docker/docker/pkg/sysinfo" "github.com/docker/docker/pkg/system" - "github.com/opencontainers/runtime-spec/specs-go" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "golang.org/x/sys/windows" + "github.com/sirupsen/logrus" "golang.org/x/sys/windows/registry" ) @@ -24,12 +26,16 @@ const ( ) func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { - img, err := daemon.imageService.GetImage(string(c.ImageID)) + + img, err := daemon.imageService.GetImage(string(c.ImageID), nil) if err != nil { return nil, err } + if !system.IsOSSupported(img.OperatingSystem()) { + return nil, system.ErrNotSupportedOperatingSystem + } - s := oci.DefaultOSSpec(img.OS) + s := oci.DefaultSpec() linkedEnv, err := daemon.setupLinkedContainers(c) if err != nil { @@ -40,9 +46,6 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { // this is done in VMCompute. Further, we couldn't do it for Hyper-V // containers anyway. - // In base spec - s.Hostname = c.FullHostname() - if err := daemon.setupSecretDir(c); err != nil { return nil, err } @@ -114,24 +117,15 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { if !mount.Writable { m.Options = append(m.Options, "ro") } - if img.OS != runtime.GOOS { - m.Type = "bind" - m.Options = append(m.Options, "rbind") - m.Options = append(m.Options, fmt.Sprintf("uvmpath=/tmp/gcs/%s/binds", c.ID)) - } s.Mounts = append(s.Mounts, m) } // In s.Process - s.Process.Args = append([]string{c.Path}, c.Args...) - if !c.Config.ArgsEscaped && img.OS == "windows" { - s.Process.Args = escapeArgs(s.Process.Args) - } - s.Process.Cwd = c.Config.WorkingDir s.Process.Env = c.CreateDaemonEnvironment(c.Config.Tty, linkedEnv) + s.Process.Terminal = c.Config.Tty + if c.Config.Tty { - s.Process.Terminal = c.Config.Tty s.Process.ConsoleSize = &specs.Box{ Height: c.HostConfig.ConsoleSize[0], Width: c.HostConfig.ConsoleSize[1], @@ -202,27 +196,24 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) { NetworkSharedContainerName: networkSharedContainerID, } - switch img.OS { - case "windows": - if err := daemon.createSpecWindowsFields(c, &s, isHyperV); err != nil { - return nil, err - } - case "linux": - if !system.LCOWSupported() { - return nil, fmt.Errorf("Linux containers on Windows are not supported") - } - if err := daemon.createSpecLinuxFields(c, &s); err != nil { - return nil, err + if err := daemon.createSpecWindowsFields(c, &s, isHyperV); err != nil { + return nil, err + } + + if logrus.IsLevelEnabled(logrus.DebugLevel) { + if b, err := json.Marshal(&s); err == nil { + logrus.Debugf("Generated spec: %s", string(b)) } - default: - return nil, fmt.Errorf("Unsupported platform %q", img.OS) } - return (*specs.Spec)(&s), nil + return &s, nil } // Sets the Windows-specific fields of the OCI spec func (daemon *Daemon) createSpecWindowsFields(c *container.Container, s *specs.Spec, isHyperV bool) error { + + s.Hostname = c.FullHostname() + if len(s.Process.Cwd) == 0 { // We default to C:\ to workaround the oddity of the case that the // default directory for cmd running as LocalSystem (or @@ -234,6 +225,14 @@ func (daemon *Daemon) createSpecWindowsFields(c *container.Container, s *specs.S s.Process.Cwd = `C:\` } + if c.Config.ArgsEscaped { + s.Process.CommandLine = c.Path + if len(c.Args) > 0 { + s.Process.CommandLine += " " + system.EscapeArgs(c.Args) + } + } else { + s.Process.Args = append([]string{c.Path}, c.Args...) + } s.Root.Readonly = false // Windows does not support a read-only root filesystem if !isHyperV { if c.BaseFS == nil { @@ -249,6 +248,113 @@ func (daemon *Daemon) createSpecWindowsFields(c *container.Container, s *specs.S // First boot optimization s.Windows.IgnoreFlushesDuringBoot = !c.HasBeenStartedBefore + setResourcesInSpec(c, s, isHyperV) + + // Read and add credentials from the security options if a credential spec has been provided. + if err := daemon.setWindowsCredentialSpec(c, s); err != nil { + return err + } + + // Do we have any assigned devices? + if len(c.HostConfig.Devices) > 0 { + if isHyperV { + return errors.New("device assignment is not supported for HyperV containers") + } + if osversion.Build() < osversion.RS5 { + return errors.New("device assignment requires Windows builds RS5 (17763+) or later") + } + for _, deviceMapping := range c.HostConfig.Devices { + srcParts := strings.SplitN(deviceMapping.PathOnHost, "/", 2) + if len(srcParts) != 2 { + return errors.New("invalid device assignment path") + } + if srcParts[0] != "class" { + return errors.Errorf("invalid device assignment type: '%s' should be 'class'", srcParts[0]) + } + wd := specs.WindowsDevice{ + ID: srcParts[1], + IDType: srcParts[0], + } + s.Windows.Devices = append(s.Windows.Devices, wd) + } + } + + return nil +} + +var errInvalidCredentialSpecSecOpt = errdefs.InvalidParameter(fmt.Errorf("invalid credential spec security option - value must be prefixed by 'file://', 'registry://', or 'raw://' followed by a non-empty value")) + +// setWindowsCredentialSpec sets the spec's `Windows.CredentialSpec` +// field if relevant +func (daemon *Daemon) setWindowsCredentialSpec(c *container.Container, s *specs.Spec) error { + if c.HostConfig == nil || c.HostConfig.SecurityOpt == nil { + return nil + } + + // TODO (jrouge/wk8): if provided with several security options, we silently ignore + // all but the last one (provided they're all valid, otherwise we do return an error); + // this doesn't seem like a great idea? + credentialSpec := "" + + for _, secOpt := range c.HostConfig.SecurityOpt { + optSplits := strings.SplitN(secOpt, "=", 2) + if len(optSplits) != 2 { + return errdefs.InvalidParameter(fmt.Errorf("invalid security option: no equals sign in supplied value %s", secOpt)) + } + if !strings.EqualFold(optSplits[0], "credentialspec") { + return errdefs.InvalidParameter(fmt.Errorf("security option not supported: %s", optSplits[0])) + } + + credSpecSplits := strings.SplitN(optSplits[1], "://", 2) + if len(credSpecSplits) != 2 || credSpecSplits[1] == "" { + return errInvalidCredentialSpecSecOpt + } + value := credSpecSplits[1] + + var err error + switch strings.ToLower(credSpecSplits[0]) { + case "file": + if credentialSpec, err = readCredentialSpecFile(c.ID, daemon.root, filepath.Clean(value)); err != nil { + return errdefs.InvalidParameter(err) + } + case "registry": + if credentialSpec, err = readCredentialSpecRegistry(c.ID, value); err != nil { + return errdefs.InvalidParameter(err) + } + case "config": + // if the container does not have a DependencyStore, then it + // isn't swarmkit managed. In order to avoid creating any + // impression that `config://` is a valid API, return the same + // error as if you'd passed any other random word. + if c.DependencyStore == nil { + return errInvalidCredentialSpecSecOpt + } + + csConfig, err := c.DependencyStore.Configs().Get(value) + if err != nil { + return errdefs.System(errors.Wrap(err, "error getting value from config store")) + } + // stuff the resulting secret data into a string to use as the + // CredentialSpec + credentialSpec = string(csConfig.Spec.Data) + case "raw": + credentialSpec = value + default: + return errInvalidCredentialSpecSecOpt + } + } + + if credentialSpec != "" { + if s.Windows == nil { + s.Windows = &specs.Windows{} + } + s.Windows.CredentialSpec = credentialSpec + } + + return nil +} + +func setResourcesInSpec(c *container.Container, s *specs.Spec, isHyperV bool) { // In s.Windows.Resources cpuShares := uint16(c.HostConfig.CPUShares) cpuMaximum := uint16(c.HostConfig.CPUPercent) * 100 @@ -273,94 +379,37 @@ func (daemon *Daemon) createSpecWindowsFields(c *container.Container, s *specs.S } } } - memoryLimit := uint64(c.HostConfig.Memory) - s.Windows.Resources = &specs.WindowsResources{ - CPU: &specs.WindowsCPUResources{ + + if cpuMaximum != 0 || cpuShares != 0 || cpuCount != 0 { + if s.Windows.Resources == nil { + s.Windows.Resources = &specs.WindowsResources{} + } + s.Windows.Resources.CPU = &specs.WindowsCPUResources{ Maximum: &cpuMaximum, Shares: &cpuShares, Count: &cpuCount, - }, - Memory: &specs.WindowsMemoryResources{ - Limit: &memoryLimit, - }, - Storage: &specs.WindowsStorageResources{ - Bps: &c.HostConfig.IOMaximumBandwidth, - Iops: &c.HostConfig.IOMaximumIOps, - }, - } - - // Read and add credentials from the security options if a credential spec has been provided. - if c.HostConfig.SecurityOpt != nil { - cs := "" - for _, sOpt := range c.HostConfig.SecurityOpt { - sOpt = strings.ToLower(sOpt) - if !strings.Contains(sOpt, "=") { - return fmt.Errorf("invalid security option: no equals sign in supplied value %s", sOpt) - } - var splitsOpt []string - splitsOpt = strings.SplitN(sOpt, "=", 2) - if len(splitsOpt) != 2 { - return fmt.Errorf("invalid security option: %s", sOpt) - } - if splitsOpt[0] != "credentialspec" { - return fmt.Errorf("security option not supported: %s", splitsOpt[0]) - } - - var ( - match bool - csValue string - err error - ) - if match, csValue = getCredentialSpec("file://", splitsOpt[1]); match { - if csValue == "" { - return fmt.Errorf("no value supplied for file:// credential spec security option") - } - if cs, err = readCredentialSpecFile(c.ID, daemon.root, filepath.Clean(csValue)); err != nil { - return err - } - } else if match, csValue = getCredentialSpec("registry://", splitsOpt[1]); match { - if csValue == "" { - return fmt.Errorf("no value supplied for registry:// credential spec security option") - } - if cs, err = readCredentialSpecRegistry(c.ID, csValue); err != nil { - return err - } - } else { - return fmt.Errorf("invalid credential spec security option - value must be prefixed file:// or registry:// followed by a value") - } } - s.Windows.CredentialSpec = cs } - return nil -} - -// Sets the Linux-specific fields of the OCI spec -// TODO: @jhowardmsft LCOW Support. We need to do a lot more pulling in what can -// be pulled in from oci_linux.go. -func (daemon *Daemon) createSpecLinuxFields(c *container.Container, s *specs.Spec) error { - if len(s.Process.Cwd) == 0 { - s.Process.Cwd = `/` - } - s.Root.Path = "rootfs" - s.Root.Readonly = c.HostConfig.ReadonlyRootfs - if err := setCapabilities(s, c); err != nil { - return fmt.Errorf("linux spec capabilities: %v", err) - } - devPermissions, err := appendDevicePermissionsFromCgroupRules(nil, c.HostConfig.DeviceCgroupRules) - if err != nil { - return fmt.Errorf("linux runtime spec devices: %v", err) + memoryLimit := uint64(c.HostConfig.Memory) + if memoryLimit != 0 { + if s.Windows.Resources == nil { + s.Windows.Resources = &specs.WindowsResources{} + } + s.Windows.Resources.Memory = &specs.WindowsMemoryResources{ + Limit: &memoryLimit, + } } - s.Linux.Resources.Devices = devPermissions - return nil -} -func escapeArgs(args []string) []string { - escapedArgs := make([]string, len(args)) - for i, a := range args { - escapedArgs[i] = windows.EscapeArg(a) + if c.HostConfig.IOMaximumBandwidth != 0 || c.HostConfig.IOMaximumIOps != 0 { + if s.Windows.Resources == nil { + s.Windows.Resources = &specs.WindowsResources{} + } + s.Windows.Resources.Storage = &specs.WindowsStorageResources{ + Bps: &c.HostConfig.IOMaximumBandwidth, + Iops: &c.HostConfig.IOMaximumIOps, + } } - return escapedArgs } // mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig @@ -369,34 +418,37 @@ func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) { return } -// getCredentialSpec is a helper function to get the value of a credential spec supplied -// on the CLI, stripping the prefix -func getCredentialSpec(prefix, value string) (bool, string) { - if strings.HasPrefix(value, prefix) { - return true, strings.TrimPrefix(value, prefix) - } - return false, "" +// registryKey is an interface wrapper around `registry.Key`, +// listing only the methods we care about here. +// It's mainly useful to easily allow mocking the registry in tests. +type registryKey interface { + GetStringValue(name string) (val string, valtype uint32, err error) + Close() error +} + +var registryOpenKeyFunc = func(baseKey registry.Key, path string, access uint32) (registryKey, error) { + return registry.OpenKey(baseKey, path, access) } // readCredentialSpecRegistry is a helper function to read a credential spec from // the registry. If not found, we return an empty string and warn in the log. // This allows for staging on machines which do not have the necessary components. func readCredentialSpecRegistry(id, name string) (string, error) { - var ( - k registry.Key - err error - val string - ) - if k, err = registry.OpenKey(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.QUERY_VALUE); err != nil { - return "", fmt.Errorf("failed handling spec %q for container %s - %s could not be opened", name, id, credentialSpecRegistryLocation) - } - if val, _, err = k.GetStringValue(name); err != nil { + key, err := registryOpenKeyFunc(registry.LOCAL_MACHINE, credentialSpecRegistryLocation, registry.QUERY_VALUE) + if err != nil { + return "", errors.Wrapf(err, "failed handling spec %q for container %s - registry key %s could not be opened", name, id, credentialSpecRegistryLocation) + } + defer key.Close() + + value, _, err := key.GetStringValue(name) + if err != nil { if err == registry.ErrNotExist { - return "", fmt.Errorf("credential spec %q for container %s as it was not found", name, id) + return "", fmt.Errorf("registry credential spec %q for container %s was not found", name, id) } - return "", fmt.Errorf("error %v reading credential spec %q from registry for container %s", err, name, id) + return "", errors.Wrapf(err, "error reading credential spec %q from registry for container %s", name, id) } - return val, nil + + return value, nil } // readCredentialSpecFile is a helper function to read a credential spec from @@ -411,9 +463,9 @@ func readCredentialSpecFile(id, root, location string) (string, error) { if !strings.HasPrefix(full, base) { return "", fmt.Errorf("invalid credential spec - file:// path must be under %s", base) } - bcontents, err := ioutil.ReadFile(full) + bcontents, err := os.ReadFile(full) if err != nil { - return "", fmt.Errorf("credential spec '%s' for container %s as the file could not be read: %q", full, id, err) + return "", errors.Wrapf(err, "credential spec for container %s could not be read from file %q", id, full) } return string(bcontents[:]), nil } diff --git a/daemon/oci_windows_test.go b/daemon/oci_windows_test.go new file mode 100644 index 0000000000000..5503eff813113 --- /dev/null +++ b/daemon/oci_windows_test.go @@ -0,0 +1,313 @@ +package daemon + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + "gotest.tools/v3/fs" + + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/container" + swarmagent "github.com/docker/swarmkit/agent" + swarmapi "github.com/docker/swarmkit/api" + specs "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/windows/registry" + "gotest.tools/v3/assert" +) + +func TestSetWindowsCredentialSpecInSpec(t *testing.T) { + // we need a temp directory to act as the daemon's root + tmpDaemonRoot := fs.NewDir(t, t.Name()).Path() + defer func() { + assert.NilError(t, os.RemoveAll(tmpDaemonRoot)) + }() + + daemon := &Daemon{ + root: tmpDaemonRoot, + } + + t.Run("it does nothing if there are no security options", func(t *testing.T) { + spec := &specs.Spec{} + + err := daemon.setWindowsCredentialSpec(&container.Container{}, spec) + assert.NilError(t, err) + assert.Check(t, spec.Windows == nil) + + err = daemon.setWindowsCredentialSpec(&container.Container{HostConfig: &containertypes.HostConfig{}}, spec) + assert.NilError(t, err) + assert.Check(t, spec.Windows == nil) + + err = daemon.setWindowsCredentialSpec(&container.Container{HostConfig: &containertypes.HostConfig{SecurityOpt: []string{}}}, spec) + assert.NilError(t, err) + assert.Check(t, spec.Windows == nil) + }) + + dummyContainerID := "dummy-container-ID" + containerFactory := func(secOpt string) *container.Container { + if !strings.Contains(secOpt, "=") { + secOpt = "credentialspec=" + secOpt + } + return &container.Container{ + ID: dummyContainerID, + HostConfig: &containertypes.HostConfig{ + SecurityOpt: []string{secOpt}, + }, + } + } + + credSpecsDir := filepath.Join(tmpDaemonRoot, credentialSpecFileLocation) + dummyCredFileContents := `{"We don't need no": "education"}` + + t.Run("happy path with a 'file://' option", func(t *testing.T) { + spec := &specs.Spec{} + + // let's render a dummy cred file + err := os.Mkdir(credSpecsDir, os.ModePerm) + assert.NilError(t, err) + dummyCredFileName := "dummy-cred-spec.json" + dummyCredFilePath := filepath.Join(credSpecsDir, dummyCredFileName) + err = os.WriteFile(dummyCredFilePath, []byte(dummyCredFileContents), 0644) + defer func() { + assert.NilError(t, os.Remove(dummyCredFilePath)) + }() + assert.NilError(t, err) + + err = daemon.setWindowsCredentialSpec(containerFactory("file://"+dummyCredFileName), spec) + assert.NilError(t, err) + + if assert.Check(t, spec.Windows != nil) { + assert.Equal(t, dummyCredFileContents, spec.Windows.CredentialSpec) + } + }) + + t.Run("it's not allowed to use a 'file://' option with an absolute path", func(t *testing.T) { + spec := &specs.Spec{} + + err := daemon.setWindowsCredentialSpec(containerFactory(`file://C:\path\to\my\credspec.json`), spec) + assert.ErrorContains(t, err, "invalid credential spec - file:// path cannot be absolute") + + assert.Check(t, spec.Windows == nil) + }) + + t.Run("it's not allowed to use a 'file://' option breaking out of the cred specs' directory", func(t *testing.T) { + spec := &specs.Spec{} + + err := daemon.setWindowsCredentialSpec(containerFactory(`file://..\credspec.json`), spec) + assert.ErrorContains(t, err, fmt.Sprintf("invalid credential spec - file:// path must be under %s", credSpecsDir)) + + assert.Check(t, spec.Windows == nil) + }) + + t.Run("when using a 'file://' option pointing to a file that doesn't exist, it fails gracefully", func(t *testing.T) { + spec := &specs.Spec{} + + err := daemon.setWindowsCredentialSpec(containerFactory("file://i-dont-exist.json"), spec) + assert.ErrorContains(t, err, fmt.Sprintf("credential spec for container %s could not be read from file", dummyContainerID)) + assert.ErrorContains(t, err, "The system cannot find") + + assert.Check(t, spec.Windows == nil) + }) + + t.Run("happy path with a 'registry://' option", func(t *testing.T) { + valueName := "my-cred-spec" + key := &dummyRegistryKey{ + getStringValueFunc: func(name string) (val string, valtype uint32, err error) { + assert.Equal(t, valueName, name) + return dummyCredFileContents, 0, nil + }, + } + defer setRegistryOpenKeyFunc(t, key)() + + spec := &specs.Spec{} + assert.NilError(t, daemon.setWindowsCredentialSpec(containerFactory("registry://"+valueName), spec)) + + if assert.Check(t, spec.Windows != nil) { + assert.Equal(t, dummyCredFileContents, spec.Windows.CredentialSpec) + } + assert.Check(t, key.closed) + }) + + t.Run("when using a 'registry://' option and opening the registry key fails, it fails gracefully", func(t *testing.T) { + dummyError := fmt.Errorf("dummy error") + defer setRegistryOpenKeyFunc(t, &dummyRegistryKey{}, dummyError)() + + spec := &specs.Spec{} + err := daemon.setWindowsCredentialSpec(containerFactory("registry://my-cred-spec"), spec) + assert.ErrorContains(t, err, fmt.Sprintf("registry key %s could not be opened: %v", credentialSpecRegistryLocation, dummyError)) + + assert.Check(t, spec.Windows == nil) + }) + + t.Run("when using a 'registry://' option pointing to a value that doesn't exist, it fails gracefully", func(t *testing.T) { + valueName := "my-cred-spec" + key := &dummyRegistryKey{ + getStringValueFunc: func(name string) (val string, valtype uint32, err error) { + assert.Equal(t, valueName, name) + return "", 0, registry.ErrNotExist + }, + } + defer setRegistryOpenKeyFunc(t, key)() + + spec := &specs.Spec{} + err := daemon.setWindowsCredentialSpec(containerFactory("registry://"+valueName), spec) + assert.ErrorContains(t, err, fmt.Sprintf("registry credential spec %q for container %s was not found", valueName, dummyContainerID)) + + assert.Check(t, key.closed) + }) + + t.Run("when using a 'registry://' option and reading the registry value fails, it fails gracefully", func(t *testing.T) { + dummyError := fmt.Errorf("dummy error") + valueName := "my-cred-spec" + key := &dummyRegistryKey{ + getStringValueFunc: func(name string) (val string, valtype uint32, err error) { + assert.Equal(t, valueName, name) + return "", 0, dummyError + }, + } + defer setRegistryOpenKeyFunc(t, key)() + + spec := &specs.Spec{} + err := daemon.setWindowsCredentialSpec(containerFactory("registry://"+valueName), spec) + assert.ErrorContains(t, err, fmt.Sprintf("error reading credential spec %q from registry for container %s: %v", valueName, dummyContainerID, dummyError)) + + assert.Check(t, key.closed) + }) + + t.Run("happy path with a 'config://' option", func(t *testing.T) { + configID := "my-cred-spec" + + dependencyManager := swarmagent.NewDependencyManager() + dependencyManager.Configs().Add(swarmapi.Config{ + ID: configID, + Spec: swarmapi.ConfigSpec{ + Data: []byte(dummyCredFileContents), + }, + }) + + task := &swarmapi.Task{ + Spec: swarmapi.TaskSpec{ + Runtime: &swarmapi.TaskSpec_Container{ + Container: &swarmapi.ContainerSpec{ + Configs: []*swarmapi.ConfigReference{ + { + ConfigID: configID, + }, + }, + }, + }, + }, + } + + cntr := containerFactory("config://" + configID) + cntr.DependencyStore = swarmagent.Restrict(dependencyManager, task) + + spec := &specs.Spec{} + err := daemon.setWindowsCredentialSpec(cntr, spec) + assert.NilError(t, err) + + if assert.Check(t, spec.Windows != nil) { + assert.Equal(t, dummyCredFileContents, spec.Windows.CredentialSpec) + } + }) + + t.Run("using a 'config://' option on a container not managed by swarmkit is not allowed, and results in a generic error message to hide that purely internal API", func(t *testing.T) { + spec := &specs.Spec{} + + err := daemon.setWindowsCredentialSpec(containerFactory("config://whatever"), spec) + assert.Equal(t, errInvalidCredentialSpecSecOpt, err) + + assert.Check(t, spec.Windows == nil) + }) + + t.Run("happy path with a 'raw://' option", func(t *testing.T) { + spec := &specs.Spec{} + + err := daemon.setWindowsCredentialSpec(containerFactory("raw://"+dummyCredFileContents), spec) + assert.NilError(t, err) + + if assert.Check(t, spec.Windows != nil) { + assert.Equal(t, dummyCredFileContents, spec.Windows.CredentialSpec) + } + }) + + t.Run("it's not case sensitive in the option names", func(t *testing.T) { + spec := &specs.Spec{} + + err := daemon.setWindowsCredentialSpec(containerFactory("CreDENtiaLSPeC=rAw://"+dummyCredFileContents), spec) + assert.NilError(t, err) + + if assert.Check(t, spec.Windows != nil) { + assert.Equal(t, dummyCredFileContents, spec.Windows.CredentialSpec) + } + }) + + t.Run("it rejects unknown options", func(t *testing.T) { + spec := &specs.Spec{} + + err := daemon.setWindowsCredentialSpec(containerFactory("credentialspe=config://whatever"), spec) + assert.ErrorContains(t, err, "security option not supported: credentialspe") + + assert.Check(t, spec.Windows == nil) + }) + + t.Run("it rejects unsupported credentialspec options", func(t *testing.T) { + spec := &specs.Spec{} + + err := daemon.setWindowsCredentialSpec(containerFactory("idontexist://whatever"), spec) + assert.Equal(t, errInvalidCredentialSpecSecOpt, err) + + assert.Check(t, spec.Windows == nil) + }) + + for _, option := range []string{"file", "registry", "config", "raw"} { + t.Run(fmt.Sprintf("it rejects empty values for %s", option), func(t *testing.T) { + spec := &specs.Spec{} + + err := daemon.setWindowsCredentialSpec(containerFactory(option+"://"), spec) + assert.Equal(t, errInvalidCredentialSpecSecOpt, err) + + assert.Check(t, spec.Windows == nil) + }) + } +} + +/* Helpers below */ + +type dummyRegistryKey struct { + getStringValueFunc func(name string) (val string, valtype uint32, err error) + closed bool +} + +func (k *dummyRegistryKey) GetStringValue(name string) (val string, valtype uint32, err error) { + return k.getStringValueFunc(name) +} + +func (k *dummyRegistryKey) Close() error { + k.closed = true + return nil +} + +// setRegistryOpenKeyFunc replaces the registryOpenKeyFunc package variable, and returns a function +// to be called to revert the change when done with testing. +func setRegistryOpenKeyFunc(t *testing.T, key *dummyRegistryKey, err ...error) func() { + previousRegistryOpenKeyFunc := registryOpenKeyFunc + + registryOpenKeyFunc = func(baseKey registry.Key, path string, access uint32) (registryKey, error) { + // this should always be called with exactly the same arguments + assert.Equal(t, registry.LOCAL_MACHINE, baseKey) + assert.Equal(t, credentialSpecRegistryLocation, path) + assert.Equal(t, uint32(registry.QUERY_VALUE), access) + + if len(err) > 0 { + return nil, err[0] + } + return key, nil + } + + return func() { + registryOpenKeyFunc = previousRegistryOpenKeyFunc + } +} diff --git a/daemon/pause.go b/daemon/pause.go index be6ec1b92a7a0..51004e6c15094 100644 --- a/daemon/pause.go +++ b/daemon/pause.go @@ -10,11 +10,11 @@ import ( // ContainerPause pauses a container func (daemon *Daemon) ContainerPause(name string) error { - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return err } - return daemon.containerPause(container) + return daemon.containerPause(ctr) } // containerPause pauses the container execution without stopping the process. diff --git a/daemon/prune.go b/daemon/prune.go index b1bcd5b79c048..e3ef4668bee75 100644 --- a/daemon/prune.go +++ b/daemon/prune.go @@ -4,15 +4,17 @@ import ( "context" "fmt" "regexp" + "strconv" "sync/atomic" "time" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/filters" timetypes "github.com/docker/docker/api/types/time" "github.com/docker/docker/errdefs" + "github.com/docker/docker/libnetwork" "github.com/docker/docker/runconfig" - "github.com/docker/libnetwork" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -84,7 +86,9 @@ func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters. rep.ContainersDeleted = append(rep.ContainersDeleted, c.ID) } } - + daemon.EventsService.Log("prune", events.ContainerEventType, events.Actor{ + Attributes: map[string]string{"reclaimed": strconv.FormatUint(rep.SpaceReclaimed, 10)}, + }) return rep, nil } @@ -210,7 +214,9 @@ func (daemon *Daemon) NetworksPrune(ctx context.Context, pruneFilters filters.Ar return rep, nil default: } - + daemon.EventsService.Log("prune", events.NetworkEventType, events.Actor{ + Attributes: map[string]string{"reclaimed": "0"}, + }) return rep, nil } diff --git a/daemon/reload.go b/daemon/reload.go index be31f4ef6c3ae..661619626e929 100644 --- a/daemon/reload.go +++ b/daemon/reload.go @@ -16,6 +16,7 @@ import ( // - Daemon debug log level // - Daemon max concurrent downloads // - Daemon max concurrent uploads +// - Daemon max download attempts // - Daemon shutdown timeout (in seconds) // - Cluster discovery (reconfigure and restart) // - Daemon labels @@ -27,14 +28,26 @@ func (daemon *Daemon) Reload(conf *config.Config) (err error) { attributes := map[string]string{} defer func() { - jsonString, _ := json.Marshal(daemon.configStore) + if err == nil { + jsonString, _ := json.Marshal(&struct { + *config.Config + config.ProxyConfig + }{ + Config: daemon.configStore, + ProxyConfig: config.ProxyConfig{ + HTTPProxy: config.MaskCredentials(daemon.configStore.HTTPProxy), + HTTPSProxy: config.MaskCredentials(daemon.configStore.HTTPSProxy), + NoProxy: config.MaskCredentials(daemon.configStore.NoProxy), + }, + }) + logrus.Infof("Reloaded configuration: %s", jsonString) + } // we're unlocking here, because // LogDaemonEventWithAttributes() -> SystemInfo() -> GetAllRuntimes() // holds that lock too. daemon.configStore.Unlock() if err == nil { - logrus.Infof("Reloaded configuration: %s", jsonString) daemon.LogDaemonEventWithAttributes("reload", attributes) } }() @@ -44,6 +57,9 @@ func (daemon *Daemon) Reload(conf *config.Config) (err error) { } daemon.reloadDebug(conf, attributes) daemon.reloadMaxConcurrentDownloadsAndUploads(conf, attributes) + if err := daemon.reloadMaxDownloadAttempts(conf, attributes); err != nil { + return err + } daemon.reloadShutdownTimeout(conf, attributes) daemon.reloadFeatures(conf, attributes) @@ -84,31 +100,53 @@ func (daemon *Daemon) reloadDebug(conf *config.Config, attributes map[string]str func (daemon *Daemon) reloadMaxConcurrentDownloadsAndUploads(conf *config.Config, attributes map[string]string) { // If no value is set for max-concurrent-downloads we assume it is the default value // We always "reset" as the cost is lightweight and easy to maintain. + maxConcurrentDownloads := config.DefaultMaxConcurrentDownloads if conf.IsValueSet("max-concurrent-downloads") && conf.MaxConcurrentDownloads != nil { - *daemon.configStore.MaxConcurrentDownloads = *conf.MaxConcurrentDownloads - } else { - maxConcurrentDownloads := config.DefaultMaxConcurrentDownloads - daemon.configStore.MaxConcurrentDownloads = &maxConcurrentDownloads + maxConcurrentDownloads = *conf.MaxConcurrentDownloads } + daemon.configStore.MaxConcurrentDownloads = &maxConcurrentDownloads logrus.Debugf("Reset Max Concurrent Downloads: %d", *daemon.configStore.MaxConcurrentDownloads) // If no value is set for max-concurrent-upload we assume it is the default value // We always "reset" as the cost is lightweight and easy to maintain. + maxConcurrentUploads := config.DefaultMaxConcurrentUploads if conf.IsValueSet("max-concurrent-uploads") && conf.MaxConcurrentUploads != nil { - *daemon.configStore.MaxConcurrentUploads = *conf.MaxConcurrentUploads - } else { - maxConcurrentUploads := config.DefaultMaxConcurrentUploads - daemon.configStore.MaxConcurrentUploads = &maxConcurrentUploads + maxConcurrentUploads = *conf.MaxConcurrentUploads } + daemon.configStore.MaxConcurrentUploads = &maxConcurrentUploads logrus.Debugf("Reset Max Concurrent Uploads: %d", *daemon.configStore.MaxConcurrentUploads) - daemon.imageService.UpdateConfig(conf.MaxConcurrentDownloads, conf.MaxConcurrentUploads) + if daemon.imageService != nil { + daemon.imageService.UpdateConfig(&maxConcurrentDownloads, &maxConcurrentUploads) + } + // prepare reload event attributes with updatable configurations attributes["max-concurrent-downloads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentDownloads) // prepare reload event attributes with updatable configurations attributes["max-concurrent-uploads"] = fmt.Sprintf("%d", *daemon.configStore.MaxConcurrentUploads) } +// reloadMaxDownloadAttempts updates configuration with max concurrent +// download attempts when a connection is lost and updates the passed attributes +func (daemon *Daemon) reloadMaxDownloadAttempts(conf *config.Config, attributes map[string]string) error { + if err := config.ValidateMaxDownloadAttempts(conf); err != nil { + return err + } + + // If no value is set for max-download-attempts we assume it is the default value + // We always "reset" as the cost is lightweight and easy to maintain. + maxDownloadAttempts := config.DefaultDownloadAttempts + if conf.IsValueSet("max-download-attempts") && conf.MaxDownloadAttempts != nil { + maxDownloadAttempts = *conf.MaxDownloadAttempts + } + daemon.configStore.MaxDownloadAttempts = &maxDownloadAttempts + logrus.Debugf("Reset Max Download Attempts: %d", *daemon.configStore.MaxDownloadAttempts) + + // prepare reload event attributes with updatable configurations + attributes["max-download-attempts"] = fmt.Sprintf("%d", *daemon.configStore.MaxDownloadAttempts) + return nil +} + // reloadShutdownTimeout updates configuration with daemon shutdown timeout option // and updates the passed attributes func (daemon *Daemon) reloadShutdownTimeout(conf *config.Config, attributes map[string]string) { diff --git a/daemon/reload_test.go b/daemon/reload_test.go index ffad297f71b74..6e8866ebb699f 100644 --- a/daemon/reload_test.go +++ b/daemon/reload_test.go @@ -9,14 +9,20 @@ import ( "github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/images" + "github.com/docker/docker/libnetwork" "github.com/docker/docker/pkg/discovery" _ "github.com/docker/docker/pkg/discovery/memory" "github.com/docker/docker/registry" - "github.com/docker/libnetwork" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/sirupsen/logrus" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) +// muteLogs suppresses logs that are generated during the test +func muteLogs() { + logrus.SetLevel(logrus.ErrorLevel) +} + func TestDaemonReloadLabels(t *testing.T) { daemon := &Daemon{ configStore: &config.Config{ @@ -26,6 +32,7 @@ func TestDaemonReloadLabels(t *testing.T) { }, imageService: images.NewImageService(images.ImageServiceConfig{}), } + muteLogs() valuesSets := make(map[string]interface{}) valuesSets["labels"] = "foo:baz" @@ -51,6 +58,7 @@ func TestDaemonReloadAllowNondistributableArtifacts(t *testing.T) { configStore: &config.Config{}, imageService: images.NewImageService(images.ImageServiceConfig{}), } + muteLogs() var err error // Initialize daemon with some registries. @@ -106,13 +114,15 @@ func TestDaemonReloadMirrors(t *testing.T) { daemon := &Daemon{ imageService: images.NewImageService(images.ImageServiceConfig{}), } + muteLogs() + var err error daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{ InsecureRegistries: []string{}, Mirrors: []string{ - "https://mirror.test1.com", - "https://mirror.test2.com", // this will be removed when reloading - "https://mirror.test3.com", // this will be removed when reloading + "https://mirror.test1.example.com", + "https://mirror.test2.example.com", // this will be removed when reloading + "https://mirror.test3.example.com", // this will be removed when reloading }, }) if err != nil { @@ -140,13 +150,13 @@ func TestDaemonReloadMirrors(t *testing.T) { }, { valid: false, - mirrors: []string{"10.10.1.11:5000", "mirror.test1.com"}, // mirrors are invalid + mirrors: []string{"10.10.1.11:5000", "mirror.test1.example.com"}, // mirrors are invalid after: []string{}, }, { valid: true, - mirrors: []string{"https://mirror.test1.com", "https://mirror.test4.com"}, - after: []string{"https://mirror.test1.com/", "https://mirror.test4.com/"}, + mirrors: []string{"https://mirror.test1.example.com", "https://mirror.test4.example.com"}, + after: []string{"https://mirror.test1.example.com/", "https://mirror.test4.example.com/"}, }, } @@ -205,6 +215,8 @@ func TestDaemonReloadInsecureRegistries(t *testing.T) { daemon := &Daemon{ imageService: images.NewImageService(images.ImageServiceConfig{}), } + muteLogs() + var err error // initialize daemon with existing insecure registries: "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000" daemon.RegistryService, err = registry.NewService(registry.ServiceOptions{ @@ -212,8 +224,8 @@ func TestDaemonReloadInsecureRegistries(t *testing.T) { "127.0.0.0/8", "10.10.1.11:5000", "10.10.1.22:5000", // this will be removed when reloading - "docker1.com", - "docker2.com", // this will be removed when reloading + "docker1.example.com", + "docker2.example.com", // this will be removed when reloading }, }) if err != nil { @@ -223,11 +235,11 @@ func TestDaemonReloadInsecureRegistries(t *testing.T) { daemon.configStore = &config.Config{} insecureRegistries := []string{ - "127.0.0.0/8", // this will be kept - "10.10.1.11:5000", // this will be kept - "10.10.1.33:5000", // this will be newly added - "docker1.com", // this will be kept - "docker3.com", // this will be newly added + "127.0.0.0/8", // this will be kept + "10.10.1.11:5000", // this will be kept + "10.10.1.33:5000", // this will be newly added + "docker1.example.com", // this will be kept + "docker3.example.com", // this will be newly added } valuesSets := make(map[string]interface{}) @@ -288,7 +300,7 @@ func TestDaemonReloadInsecureRegistries(t *testing.T) { } // assert if "docker2.com" is removed when reloading - if value, ok := dataMap["docker2.com"]; ok { + if value, ok := dataMap["docker2.example.com"]; ok { t.Fatalf("Expected no insecure registry of docker2.com, got %d", value) } } @@ -297,6 +309,8 @@ func TestDaemonReloadNotAffectOthers(t *testing.T) { daemon := &Daemon{ imageService: images.NewImageService(images.ImageServiceConfig{}), } + muteLogs() + daemon.configStore = &config.Config{ CommonConfig: config.CommonConfig{ Labels: []string{"foo:bar"}, @@ -331,6 +345,7 @@ func TestDaemonDiscoveryReload(t *testing.T) { daemon := &Daemon{ imageService: images.NewImageService(images.ImageServiceConfig{}), } + muteLogs() daemon.configStore = &config.Config{ CommonConfig: config.CommonConfig{ ClusterStore: "memory://127.0.0.1", @@ -411,6 +426,7 @@ func TestDaemonDiscoveryReloadFromEmptyDiscovery(t *testing.T) { imageService: images.NewImageService(images.ImageServiceConfig{}), } daemon.configStore = &config.Config{} + muteLogs() valuesSet := make(map[string]interface{}) valuesSet["cluster-store"] = "memory://127.0.0.1:2222" diff --git a/daemon/reload_unix.go b/daemon/reload_unix.go index 9c1bb992af0ff..590267c48449a 100644 --- a/daemon/reload_unix.go +++ b/daemon/reload_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package daemon // import "github.com/docker/docker/daemon" @@ -19,7 +20,7 @@ func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string] if conf.IsValueSet("runtimes") { // Always set the default one - conf.Runtimes[config.StockRuntimeName] = types.Runtime{Path: DefaultRuntimeBinary} + conf.Runtimes[config.StockRuntimeName] = types.Runtime{Path: config.DefaultRuntimeBinary} if err := daemon.initRuntimes(conf.Runtimes); err != nil { return err } @@ -34,6 +35,10 @@ func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string] daemon.configStore.ShmSize = conf.ShmSize } + if conf.CgroupNamespaceMode != "" { + daemon.configStore.CgroupNamespaceMode = conf.CgroupNamespaceMode + } + if conf.IpcMode != "" { daemon.configStore.IpcMode = conf.IpcMode } @@ -44,13 +49,14 @@ func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string] if runtimeList.Len() > 0 { runtimeList.WriteRune(' ') } - runtimeList.WriteString(fmt.Sprintf("%s:%s", name, rt)) + runtimeList.WriteString(fmt.Sprintf("%s:%s", name, rt.Path)) } attributes["runtimes"] = runtimeList.String() attributes["default-runtime"] = daemon.configStore.DefaultRuntime attributes["default-shm-size"] = fmt.Sprintf("%d", daemon.configStore.ShmSize) attributes["default-ipc-mode"] = daemon.configStore.IpcMode + attributes["default-cgroupns-mode"] = daemon.configStore.CgroupNamespaceMode return nil } diff --git a/daemon/rename.go b/daemon/rename.go index 6b099018f5ae1..55a2488529948 100644 --- a/daemon/rename.go +++ b/daemon/rename.go @@ -5,7 +5,7 @@ import ( dockercontainer "github.com/docker/docker/container" "github.com/docker/docker/errdefs" - "github.com/docker/libnetwork" + "github.com/docker/docker/libnetwork" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) diff --git a/daemon/resize.go b/daemon/resize.go index 21240650f85a8..ac9395379ba0c 100644 --- a/daemon/resize.go +++ b/daemon/resize.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/docker/docker/libcontainerd" + libcontainerdtypes "github.com/docker/docker/libcontainerd/types" ) // ContainerResize changes the size of the TTY of the process running @@ -20,7 +20,7 @@ func (daemon *Daemon) ContainerResize(name string, height, width int) error { return errNotRunning(container.ID) } - if err = daemon.containerd.ResizeTerminal(context.Background(), container.ID, libcontainerd.InitProcessName, width, height); err == nil { + if err = daemon.containerd.ResizeTerminal(context.Background(), container.ID, libcontainerdtypes.InitProcessName, width, height); err == nil { attributes := map[string]string{ "height": fmt.Sprintf("%d", height), "width": fmt.Sprintf("%d", width), @@ -38,13 +38,16 @@ func (daemon *Daemon) ContainerExecResize(name string, height, width int) error if err != nil { return err } + // TODO: the timeout is hardcoded here, it would be more flexible to make it // a parameter in resize request context, which would need API changes. - timeout := 10 * time.Second + timeout := time.NewTimer(10 * time.Second) + defer timeout.Stop() + select { case <-ec.Started: return daemon.containerd.ResizeTerminal(context.Background(), ec.ContainerID, ec.ID, width, height) - case <-time.After(timeout): + case <-timeout.C: return fmt.Errorf("timeout waiting for exec session ready") } } diff --git a/daemon/resize_test.go b/daemon/resize_test.go index edfe9d3ed1771..50a96778a7df1 100644 --- a/daemon/resize_test.go +++ b/daemon/resize_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package daemon @@ -8,7 +9,7 @@ import ( "github.com/docker/docker/container" "github.com/docker/docker/daemon/exec" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) // This test simply verify that when a wrong ID used, a specific error should be returned for exec resize. diff --git a/daemon/restart.go b/daemon/restart.go index 0f06dea26713e..da3deec4bfd73 100644 --- a/daemon/restart.go +++ b/daemon/restart.go @@ -3,6 +3,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( "fmt" + containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/sirupsen/logrus" ) @@ -14,15 +15,15 @@ import ( // stop. Returns an error if the container cannot be found, or if // there is an underlying error at any stage of the restart. func (daemon *Daemon) ContainerRestart(name string, seconds *int) error { - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return err } if seconds == nil { - stopTimeout := container.StopTimeout() + stopTimeout := ctr.StopTimeout() seconds = &stopTimeout } - if err := daemon.containerRestart(container, *seconds); err != nil { + if err := daemon.containerRestart(ctr, *seconds); err != nil { return fmt.Errorf("Cannot restart container %s: %v", name, err) } return nil @@ -34,11 +35,23 @@ func (daemon *Daemon) ContainerRestart(name string, seconds *int) error { // gracefully stop, before forcefully terminating the container. If // given a negative duration, wait forever for a graceful stop. func (daemon *Daemon) containerRestart(container *container.Container, seconds int) error { + + // Determine isolation. If not specified in the hostconfig, use daemon default. + actualIsolation := container.HostConfig.Isolation + if containertypes.Isolation.IsDefault(actualIsolation) { + actualIsolation = daemon.defaultIsolation + } + // Avoid unnecessarily unmounting and then directly mounting // the container when the container stops and then starts - // again - if err := daemon.Mount(container); err == nil { - defer daemon.Unmount(container) + // again. We do not do this for Hyper-V isolated containers + // (implying also on Windows) as the HCS must have exclusive + // access to mount the containers filesystem inside the utility + // VM. + if !containertypes.Isolation.IsHyperV(actualIsolation) { + if err := daemon.Mount(container); err == nil { + defer daemon.Unmount(container) + } } if container.IsRunning() { diff --git a/daemon/runtime_unix.go b/daemon/runtime_unix.go new file mode 100644 index 0000000000000..1478735b838c9 --- /dev/null +++ b/daemon/runtime_unix.go @@ -0,0 +1,158 @@ +//go:build !windows +// +build !windows + +package daemon + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/containerd/cgroups" + "github.com/containerd/containerd/runtime/linux/runctypes" + v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options" + "github.com/docker/docker/api/types" + "github.com/docker/docker/daemon/config" + "github.com/docker/docker/errdefs" + "github.com/docker/docker/pkg/ioutils" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + defaultRuntimeName = "runc" + + linuxShimV1 = "io.containerd.runtime.v1.linux" + linuxShimV2 = "io.containerd.runc.v2" +) + +func configureRuntimes(conf *config.Config) { + if conf.DefaultRuntime == "" { + conf.DefaultRuntime = config.StockRuntimeName + } + if conf.Runtimes == nil { + conf.Runtimes = make(map[string]types.Runtime) + } + conf.Runtimes[config.LinuxV1RuntimeName] = types.Runtime{Path: defaultRuntimeName, Shim: defaultV1ShimConfig(conf, defaultRuntimeName)} + conf.Runtimes[config.LinuxV2RuntimeName] = types.Runtime{Path: defaultRuntimeName, Shim: defaultV2ShimConfig(conf, defaultRuntimeName)} + conf.Runtimes[config.StockRuntimeName] = conf.Runtimes[config.LinuxV2RuntimeName] +} + +func defaultV2ShimConfig(conf *config.Config, runtimePath string) *types.ShimConfig { + return &types.ShimConfig{ + Binary: linuxShimV2, + Opts: &v2runcoptions.Options{ + BinaryName: runtimePath, + Root: filepath.Join(conf.ExecRoot, "runtime-"+defaultRuntimeName), + SystemdCgroup: UsingSystemd(conf), + NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "", + }, + } +} + +func defaultV1ShimConfig(conf *config.Config, runtimePath string) *types.ShimConfig { + return &types.ShimConfig{ + Binary: linuxShimV1, + Opts: &runctypes.RuncOptions{ + Runtime: runtimePath, + RuntimeRoot: filepath.Join(conf.ExecRoot, "runtime-"+defaultRuntimeName), + SystemdCgroup: UsingSystemd(conf), + }, + } +} + +func (daemon *Daemon) loadRuntimes() error { + return daemon.initRuntimes(daemon.configStore.Runtimes) +} + +func (daemon *Daemon) initRuntimes(runtimes map[string]types.Runtime) (err error) { + runtimeDir := filepath.Join(daemon.configStore.Root, "runtimes") + // Remove old temp directory if any + os.RemoveAll(runtimeDir + "-old") + tmpDir, err := ioutils.TempDir(daemon.configStore.Root, "gen-runtimes") + if err != nil { + return errors.Wrap(err, "failed to get temp dir to generate runtime scripts") + } + defer func() { + if err != nil { + if err1 := os.RemoveAll(tmpDir); err1 != nil { + logrus.WithError(err1).WithField("dir", tmpDir). + Warn("failed to remove tmp dir") + } + return + } + + if err = os.Rename(runtimeDir, runtimeDir+"-old"); err != nil { + return + } + if err = os.Rename(tmpDir, runtimeDir); err != nil { + err = errors.Wrap(err, "failed to setup runtimes dir, new containers may not start") + return + } + if err = os.RemoveAll(runtimeDir + "-old"); err != nil { + logrus.WithError(err).WithField("dir", tmpDir). + Warn("failed to remove old runtimes dir") + } + }() + + for name, rt := range runtimes { + if len(rt.Args) > 0 { + script := filepath.Join(tmpDir, name) + content := fmt.Sprintf("#!/bin/sh\n%s %s $@\n", rt.Path, strings.Join(rt.Args, " ")) + if err := os.WriteFile(script, []byte(content), 0700); err != nil { + return err + } + } + if rt.Shim == nil { + rt.Shim = defaultV2ShimConfig(daemon.configStore, rt.Path) + } + } + return nil +} + +// rewriteRuntimePath is used for runtimes which have custom arguments supplied. +// This is needed because the containerd API only calls the OCI runtime binary, there is no options for extra arguments. +// To support this case, the daemon wraps the specified runtime in a script that passes through those arguments. +func (daemon *Daemon) rewriteRuntimePath(name, p string, args []string) (string, error) { + if len(args) == 0 { + return p, nil + } + + // Check that the runtime path actually exists here so that we can return a well known error. + if _, err := exec.LookPath(p); err != nil { + return "", errors.Wrap(err, "error while looking up the specified runtime path") + } + + return filepath.Join(daemon.configStore.Root, "runtimes", name), nil +} + +func (daemon *Daemon) getRuntime(name string) (*types.Runtime, error) { + rt := daemon.configStore.GetRuntime(name) + if rt == nil { + return nil, errdefs.InvalidParameter(errors.Errorf("runtime not found in config: %s", name)) + } + + if len(rt.Args) > 0 { + p, err := daemon.rewriteRuntimePath(name, rt.Path, rt.Args) + if err != nil { + return nil, err + } + rt.Path = p + rt.Args = nil + } + + if rt.Shim == nil { + rt.Shim = defaultV2ShimConfig(daemon.configStore, rt.Path) + } + + if rt.Shim.Binary == linuxShimV1 { + if cgroups.Mode() == cgroups.Unified { + return nil, errdefs.InvalidParameter(errors.Errorf("runtime %q is not supported while cgroups v2 (unified hierarchy) is being used", name)) + } + logrus.Warnf("Configured runtime %q is deprecated and will be removed in the next release", name) + } + + return rt, nil +} diff --git a/daemon/runtime_windows.go b/daemon/runtime_windows.go new file mode 100644 index 0000000000000..0787cb115563e --- /dev/null +++ b/daemon/runtime_windows.go @@ -0,0 +1,10 @@ +package daemon + +import ( + "github.com/docker/docker/api/types" + "github.com/pkg/errors" +) + +func (daemon *Daemon) getRuntime(name string) (*types.Runtime, error) { + return nil, errors.New("not implemented") +} diff --git a/daemon/seccomp_disabled.go b/daemon/seccomp_disabled.go index 3855c7830e69a..5cfe585e16c1d 100644 --- a/daemon/seccomp_disabled.go +++ b/daemon/seccomp_disabled.go @@ -1,19 +1,26 @@ +//go:build linux && !seccomp // +build linux,!seccomp package daemon // import "github.com/docker/docker/daemon" import ( + "context" "fmt" + "github.com/containerd/containerd/containers" + coci "github.com/containerd/containerd/oci" "github.com/docker/docker/container" - "github.com/opencontainers/runtime-spec/specs-go" + dconfig "github.com/docker/docker/daemon/config" ) -var supportsSeccomp = false +const supportsSeccomp = false -func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error { - if c.SeccompProfile != "" && c.SeccompProfile != "unconfined" { - return fmt.Errorf("seccomp profiles are not supported on this daemon, you cannot specify a custom seccomp profile") +// WithSeccomp sets the seccomp profile +func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + if c.SeccompProfile != "" && c.SeccompProfile != dconfig.SeccompProfileUnconfined { + return fmt.Errorf("seccomp profiles are not supported on this daemon, you cannot specify a custom seccomp profile") + } + return nil } - return nil } diff --git a/daemon/seccomp_linux.go b/daemon/seccomp_linux.go index 66ab8c768cd81..1f4ecb576d615 100644 --- a/daemon/seccomp_linux.go +++ b/daemon/seccomp_linux.go @@ -1,55 +1,52 @@ +//go:build linux && seccomp // +build linux,seccomp package daemon // import "github.com/docker/docker/daemon" import ( + "context" "fmt" + "github.com/containerd/containerd/containers" + coci "github.com/containerd/containerd/oci" "github.com/docker/docker/container" + dconfig "github.com/docker/docker/daemon/config" "github.com/docker/docker/profiles/seccomp" - "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" ) -var supportsSeccomp = true +const supportsSeccomp = true -func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error { - var profile *specs.LinuxSeccomp - var err error - - if c.HostConfig.Privileged { - return nil - } - - if !daemon.seccompEnabled { - if c.SeccompProfile != "" && c.SeccompProfile != "unconfined" { - return fmt.Errorf("Seccomp is not enabled in your kernel, cannot run a custom seccomp profile.") +// WithSeccomp sets the seccomp profile +func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + if c.SeccompProfile == dconfig.SeccompProfileUnconfined { + return nil } - logrus.Warn("Seccomp is not enabled in your kernel, running container without default profile.") - c.SeccompProfile = "unconfined" - } - if c.SeccompProfile == "unconfined" { - return nil - } - if c.SeccompProfile != "" { - profile, err = seccomp.LoadProfile(c.SeccompProfile, rs) - if err != nil { - return err + if c.HostConfig.Privileged { + return nil } - } else { - if daemon.seccompProfile != nil { - profile, err = seccomp.LoadProfile(string(daemon.seccompProfile), rs) - if err != nil { - return err - } - } else { - profile, err = seccomp.GetDefaultProfile(rs) - if err != nil { - return err + if !daemon.seccompEnabled { + if c.SeccompProfile != "" && c.SeccompProfile != dconfig.SeccompProfileDefault { + return fmt.Errorf("seccomp is not enabled in your kernel, cannot run a custom seccomp profile") } + logrus.Warn("seccomp is not enabled in your kernel, running container without default profile") + c.SeccompProfile = dconfig.SeccompProfileUnconfined + return nil } + var err error + switch { + case c.SeccompProfile == dconfig.SeccompProfileDefault: + s.Linux.Seccomp, err = seccomp.GetDefaultProfile(s) + case c.SeccompProfile != "": + s.Linux.Seccomp, err = seccomp.LoadProfile(c.SeccompProfile, s) + case daemon.seccompProfile != nil: + s.Linux.Seccomp, err = seccomp.LoadProfile(string(daemon.seccompProfile), s) + case daemon.seccompProfilePath == dconfig.SeccompProfileUnconfined: + c.SeccompProfile = dconfig.SeccompProfileUnconfined + default: + s.Linux.Seccomp, err = seccomp.GetDefaultProfile(s) + } + return err } - - rs.Linux.Seccomp = profile - return nil } diff --git a/daemon/seccomp_linux_test.go b/daemon/seccomp_linux_test.go new file mode 100644 index 0000000000000..bb5331da37d1f --- /dev/null +++ b/daemon/seccomp_linux_test.go @@ -0,0 +1,200 @@ +//go:build linux && seccomp +// +build linux,seccomp + +package daemon // import "github.com/docker/docker/daemon" + +import ( + "testing" + + coci "github.com/containerd/containerd/oci" + config "github.com/docker/docker/api/types/container" + "github.com/docker/docker/container" + dconfig "github.com/docker/docker/daemon/config" + doci "github.com/docker/docker/oci" + "github.com/docker/docker/profiles/seccomp" + specs "github.com/opencontainers/runtime-spec/specs-go" + "gotest.tools/v3/assert" +) + +func TestWithSeccomp(t *testing.T) { + + type expected struct { + daemon *Daemon + c *container.Container + inSpec coci.Spec + outSpec coci.Spec + err string + comment string + } + + for _, x := range []expected{ + { + comment: "unconfined seccompProfile runs unconfined", + daemon: &Daemon{ + seccompEnabled: true, + }, + c: &container.Container{ + SeccompProfile: dconfig.SeccompProfileUnconfined, + HostConfig: &config.HostConfig{ + Privileged: false, + }, + }, + inSpec: doci.DefaultLinuxSpec(), + outSpec: doci.DefaultLinuxSpec(), + }, + { + comment: "privileged container w/ custom profile runs unconfined", + daemon: &Daemon{ + seccompEnabled: true, + }, + c: &container.Container{ + SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_LOG\" }", + HostConfig: &config.HostConfig{ + Privileged: true, + }, + }, + inSpec: doci.DefaultLinuxSpec(), + outSpec: doci.DefaultLinuxSpec(), + }, + { + comment: "privileged container w/ default runs unconfined", + daemon: &Daemon{ + seccompEnabled: true, + }, + c: &container.Container{ + SeccompProfile: "", + HostConfig: &config.HostConfig{ + Privileged: true, + }, + }, + inSpec: doci.DefaultLinuxSpec(), + outSpec: doci.DefaultLinuxSpec(), + }, + { + comment: "privileged container w/ daemon profile runs unconfined", + daemon: &Daemon{ + seccompEnabled: true, + seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"), + }, + c: &container.Container{ + SeccompProfile: "", + HostConfig: &config.HostConfig{ + Privileged: true, + }, + }, + inSpec: doci.DefaultLinuxSpec(), + outSpec: doci.DefaultLinuxSpec(), + }, + { + comment: "custom profile when seccomp is disabled returns error", + daemon: &Daemon{ + seccompEnabled: false, + }, + c: &container.Container{ + SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }", + HostConfig: &config.HostConfig{ + Privileged: false, + }, + }, + inSpec: doci.DefaultLinuxSpec(), + outSpec: doci.DefaultLinuxSpec(), + err: "seccomp is not enabled in your kernel, cannot run a custom seccomp profile", + }, + { + comment: "empty profile name loads default profile", + daemon: &Daemon{ + seccompEnabled: true, + }, + c: &container.Container{ + SeccompProfile: "", + HostConfig: &config.HostConfig{ + Privileged: false, + }, + }, + inSpec: doci.DefaultLinuxSpec(), + outSpec: func() coci.Spec { + s := doci.DefaultLinuxSpec() + profile, _ := seccomp.GetDefaultProfile(&s) + s.Linux.Seccomp = profile + return s + }(), + }, + { + comment: "load container's profile", + daemon: &Daemon{ + seccompEnabled: true, + }, + c: &container.Container{ + SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }", + HostConfig: &config.HostConfig{ + Privileged: false, + }, + }, + inSpec: doci.DefaultLinuxSpec(), + outSpec: func() coci.Spec { + s := doci.DefaultLinuxSpec() + profile := &specs.LinuxSeccomp{ + DefaultAction: specs.LinuxSeccompAction("SCMP_ACT_ERRNO"), + } + s.Linux.Seccomp = profile + return s + }(), + }, + { + comment: "load daemon's profile", + daemon: &Daemon{ + seccompEnabled: true, + seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"), + }, + c: &container.Container{ + SeccompProfile: "", + HostConfig: &config.HostConfig{ + Privileged: false, + }, + }, + inSpec: doci.DefaultLinuxSpec(), + outSpec: func() coci.Spec { + s := doci.DefaultLinuxSpec() + profile := &specs.LinuxSeccomp{ + DefaultAction: specs.LinuxSeccompAction("SCMP_ACT_ERRNO"), + } + s.Linux.Seccomp = profile + return s + }(), + }, + { + comment: "load prioritise container profile over daemon's", + daemon: &Daemon{ + seccompEnabled: true, + seccompProfile: []byte("{ \"defaultAction\": \"SCMP_ACT_ERRNO\" }"), + }, + c: &container.Container{ + SeccompProfile: "{ \"defaultAction\": \"SCMP_ACT_LOG\" }", + HostConfig: &config.HostConfig{ + Privileged: false, + }, + }, + inSpec: doci.DefaultLinuxSpec(), + outSpec: func() coci.Spec { + s := doci.DefaultLinuxSpec() + profile := &specs.LinuxSeccomp{ + DefaultAction: specs.LinuxSeccompAction("SCMP_ACT_LOG"), + } + s.Linux.Seccomp = profile + return s + }(), + }, + } { + t.Run(x.comment, func(t *testing.T) { + opts := WithSeccomp(x.daemon, x.c) + err := opts(nil, nil, nil, &x.inSpec) + + assert.DeepEqual(t, x.inSpec, x.outSpec) + if x.err != "" { + assert.Error(t, err, x.err) + } else { + assert.NilError(t, err) + } + }) + } +} diff --git a/daemon/seccomp_unsupported.go b/daemon/seccomp_unsupported.go index a323fe0be15db..97fe70b30a915 100644 --- a/daemon/seccomp_unsupported.go +++ b/daemon/seccomp_unsupported.go @@ -1,5 +1,21 @@ +//go:build !linux // +build !linux package daemon // import "github.com/docker/docker/daemon" -var supportsSeccomp = false +import ( + "context" + + "github.com/containerd/containerd/containers" + coci "github.com/containerd/containerd/oci" + "github.com/docker/docker/container" +) + +const supportsSeccomp = false + +// WithSeccomp sets the seccomp profile +func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts { + return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error { + return nil + } +} diff --git a/daemon/secrets_unsupported.go b/daemon/secrets_unsupported.go index edad69c56959f..678b7c34c0da1 100644 --- a/daemon/secrets_unsupported.go +++ b/daemon/secrets_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows // +build !linux,!windows package daemon // import "github.com/docker/docker/daemon" diff --git a/daemon/selinux_linux.go b/daemon/selinux_linux.go deleted file mode 100644 index f87b30b7387ce..0000000000000 --- a/daemon/selinux_linux.go +++ /dev/null @@ -1,15 +0,0 @@ -package daemon // import "github.com/docker/docker/daemon" - -import "github.com/opencontainers/selinux/go-selinux" - -func selinuxSetDisabled() { - selinux.SetDisabled() -} - -func selinuxFreeLxcContexts(label string) { - selinux.ReleaseLabel(label) -} - -func selinuxEnabled() bool { - return selinux.GetEnabled() -} diff --git a/daemon/selinux_unsupported.go b/daemon/selinux_unsupported.go deleted file mode 100644 index 49d0d13bce932..0000000000000 --- a/daemon/selinux_unsupported.go +++ /dev/null @@ -1,13 +0,0 @@ -// +build !linux - -package daemon // import "github.com/docker/docker/daemon" - -func selinuxSetDisabled() { -} - -func selinuxFreeLxcContexts(label string) { -} - -func selinuxEnabled() bool { - return false -} diff --git a/daemon/start.go b/daemon/start.go index c00bd9ceb22be..fc5aa5b55f5eb 100644 --- a/daemon/start.go +++ b/daemon/start.go @@ -9,7 +9,6 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/errdefs" - "github.com/docker/docker/pkg/mount" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -20,24 +19,24 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode")) } - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return err } validateState := func() error { - container.Lock() - defer container.Unlock() + ctr.Lock() + defer ctr.Unlock() - if container.Paused { + if ctr.Paused { return errdefs.Conflict(errors.New("cannot start a paused container, try unpause instead")) } - if container.Running { + if ctr.Running { return containerNotModifiedError{running: true} } - if container.RemovalInProgress || container.Dead { + if ctr.RemovalInProgress || ctr.Dead { return errdefs.Conflict(errors.New("container is marked for removal and cannot be started")) } return nil @@ -53,26 +52,26 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos // creating a container, not during start. if hostConfig != nil { logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12") - oldNetworkMode := container.HostConfig.NetworkMode - if err := daemon.setSecurityOptions(container, hostConfig); err != nil { + oldNetworkMode := ctr.HostConfig.NetworkMode + if err := daemon.setSecurityOptions(ctr, hostConfig); err != nil { return errdefs.InvalidParameter(err) } if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil { return errdefs.InvalidParameter(err) } - if err := daemon.setHostConfig(container, hostConfig); err != nil { + if err := daemon.setHostConfig(ctr, hostConfig); err != nil { return errdefs.InvalidParameter(err) } - newNetworkMode := container.HostConfig.NetworkMode + newNetworkMode := ctr.HostConfig.NetworkMode if string(oldNetworkMode) != string(newNetworkMode) { // if user has change the network mode on starting, clean up the // old networks. It is a deprecated feature and has been removed in Docker 1.12 - container.NetworkSettings.Networks = nil - if err := container.CheckpointTo(daemon.containersReplica); err != nil { + ctr.NetworkSettings.Networks = nil + if err := ctr.CheckpointTo(daemon.containersReplica); err != nil { return errdefs.System(err) } } - container.InitDNSHostConfig() + ctr.InitDNSHostConfig() } } else { if hostConfig != nil { @@ -82,17 +81,17 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos // check if hostConfig is in line with the current system settings. // It may happen cgroups are umounted or the like. - if _, err = daemon.verifyContainerSettings(container.OS, container.HostConfig, nil, false); err != nil { + if _, err = daemon.verifyContainerSettings(ctr.HostConfig, nil, false); err != nil { return errdefs.InvalidParameter(err) } // Adapt for old containers in case we have updates in this function and // old containers never have chance to call the new function in create stage. if hostConfig != nil { - if err := daemon.adaptContainerSettings(container.HostConfig, false); err != nil { + if err := daemon.adaptContainerSettings(ctr.HostConfig, false); err != nil { return errdefs.InvalidParameter(err) } } - return daemon.containerStart(container, checkpoint, checkpointDir, true) + return daemon.containerStart(ctr, checkpoint, checkpointDir, true) } // containerStart prepares the container to run by setting up everything the @@ -158,9 +157,10 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint if resetRestartManager { container.ResetRestartManager(true) + container.HasBeenManuallyStopped = false } - if daemon.saveApparmorConfig(container); err != nil { + if err := daemon.saveAppArmorConfig(container); err != nil { return err } @@ -171,14 +171,27 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint } } - createOptions, err := daemon.getLibcontainerdCreateOptions(container) + shim, createOptions, err := daemon.getLibcontainerdCreateOptions(container) if err != nil { return err } - err = daemon.containerd.Create(context.Background(), container.ID, spec, createOptions) + ctx := context.TODO() + + err = daemon.containerd.Create(ctx, container.ID, spec, shim, createOptions) if err != nil { - return translateContainerdStartErr(container.Path, container.SetExitCode, err) + if errdefs.IsConflict(err) { + logrus.WithError(err).WithField("container", container.ID).Error("Container not cleaned up from containerd from previous run") + // best effort to clean up old container object + daemon.containerd.DeleteTask(ctx, container.ID) + if err := daemon.containerd.Delete(ctx, container.ID); err != nil && !errdefs.IsNotFound(err) { + logrus.WithError(err).WithField("container", container.ID).Error("Error cleaning up stale containerd container object") + } + err = daemon.containerd.Create(ctx, container.ID, spec, shim, createOptions) + } + if err != nil { + return translateContainerdStartErr(container.Path, container.SetExitCode, err) + } } // TODO(mlaventure): we need to specify checkpoint options here @@ -194,7 +207,6 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint } container.SetRunning(pid, true) - container.HasBeenManuallyStopped = false container.HasBeenStartedBefore = true daemon.setStateCounter(container) @@ -216,14 +228,14 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint func (daemon *Daemon) Cleanup(container *container.Container) { daemon.releaseNetwork(container) - if err := container.UnmountIpcMount(detachMounted); err != nil { + if err := container.UnmountIpcMount(); err != nil { logrus.Warnf("%s cleanup: failed to unmount IPC: %s", container.ID, err) } if err := daemon.conditionalUnmountOnCleanup(container); err != nil { // FIXME: remove once reference counting for graphdrivers has been refactored // Ensure that all the mounts are gone - if mountid, err := daemon.imageService.GetLayerMountID(container.ID, container.OS); err == nil { + if mountid, err := daemon.imageService.GetLayerMountID(container.ID); err == nil { daemon.cleanupMountsByID(mountid) } } @@ -232,7 +244,7 @@ func (daemon *Daemon) Cleanup(container *container.Container) { logrus.Warnf("%s cleanup: failed to unmount secrets: %s", container.ID, err) } - if err := mount.RecursiveUnmount(container.Root); err != nil { + if err := recursiveUnmount(container.Root); err != nil { logrus.WithError(err).WithField("container", container.ID).Warn("Error while cleaning up container resource mounts.") } diff --git a/daemon/start_unix.go b/daemon/start_unix.go index e680b95f42149..2f66c0010501c 100644 --- a/daemon/start_unix.go +++ b/daemon/start_unix.go @@ -1,57 +1,24 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" import ( - "fmt" - "os/exec" - "path/filepath" - - "github.com/containerd/containerd/runtime/linux/runctypes" "github.com/docker/docker/container" - "github.com/docker/docker/errdefs" - "github.com/pkg/errors" ) -func (daemon *Daemon) getRuntimeScript(container *container.Container) (string, error) { - name := container.HostConfig.Runtime - rt := daemon.configStore.GetRuntime(name) - if rt == nil { - return "", errdefs.InvalidParameter(errors.Errorf("no such runtime '%s'", name)) - } - - if len(rt.Args) > 0 { - // First check that the target exist, as using it in a script won't - // give us the right error - if _, err := exec.LookPath(rt.Path); err != nil { - return "", translateContainerdStartErr(container.Path, container.SetExitCode, err) - } - return filepath.Join(daemon.configStore.Root, "runtimes", name), nil - } - return rt.Path, nil -} - // getLibcontainerdCreateOptions callers must hold a lock on the container -func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (interface{}, error) { +func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (string, interface{}, error) { // Ensure a runtime has been assigned to this container if container.HostConfig.Runtime == "" { container.HostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName() container.CheckpointTo(daemon.containersReplica) } - path, err := daemon.getRuntimeScript(container) + rt, err := daemon.getRuntime(container.HostConfig.Runtime) if err != nil { - return nil, err - } - opts := &runctypes.RuncOptions{ - Runtime: path, - RuntimeRoot: filepath.Join(daemon.configStore.ExecRoot, - fmt.Sprintf("runtime-%s", container.HostConfig.Runtime)), - } - - if UsingSystemd(daemon.configStore) { - opts.SystemdCgroup = true + return "", nil, translateContainerdStartErr(container.Path, container.SetExitCode, err) } - return opts, nil + return rt.Shim.Binary, rt.Shim.Opts, nil } diff --git a/daemon/start_windows.go b/daemon/start_windows.go index f4606f7a60b18..cbdffb3466652 100644 --- a/daemon/start_windows.go +++ b/daemon/start_windows.go @@ -1,38 +1,15 @@ package daemon // import "github.com/docker/docker/daemon" import ( - "github.com/Microsoft/opengcs/client" + "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options" "github.com/docker/docker/container" + "github.com/docker/docker/pkg/system" ) -func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (interface{}, error) { - // LCOW options. - if container.OS == "linux" { - config := &client.Config{} - if err := config.GenerateDefault(daemon.configStore.GraphOptions); err != nil { - return nil, err - } - // Override from user-supplied options. - for k, v := range container.HostConfig.StorageOpt { - switch k { - case "lcow.kirdpath": - config.KirdPath = v - case "lcow.kernel": - config.KernelFile = v - case "lcow.initrd": - config.InitrdFile = v - case "lcow.vhdx": - config.Vhdx = v - case "lcow.bootparameters": - config.BootParameters = v - } - } - if err := config.Validate(); err != nil { - return nil, err - } - - return config, nil +func (daemon *Daemon) getLibcontainerdCreateOptions(_ *container.Container) (string, interface{}, error) { + if system.ContainerdRuntimeSupported() { + opts := &options.Options{} + return "io.containerd.runhcs.v1", opts, nil } - - return nil, nil + return "", nil, nil } diff --git a/daemon/stats.go b/daemon/stats.go index eb23e272ae51c..c380f4e5292f0 100644 --- a/daemon/stats.go +++ b/daemon/stats.go @@ -12,6 +12,7 @@ import ( "github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions/v1p20" "github.com/docker/docker/container" + "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/ioutils" ) @@ -21,16 +22,25 @@ func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, c // Engine API version (used for backwards compatibility) apiVersion := config.Version - container, err := daemon.GetContainer(prefixOrName) + if isWindows && versions.LessThan(apiVersion, "1.21") { + return errors.New("API versions pre v1.21 do not support stats on Windows") + } + + ctr, err := daemon.GetContainer(prefixOrName) if err != nil { return err } + if config.Stream && config.OneShot { + return errdefs.InvalidParameter(errors.New("cannot have stream=true and one-shot=true")) + } + // If the container is either not running or restarting and requires no stream, return an empty stats. - if (!container.IsRunning() || container.IsRestarting()) && !config.Stream { + if (!ctr.IsRunning() || ctr.IsRestarting()) && !config.Stream { return json.NewEncoder(config.OutStream).Encode(&types.StatsJSON{ - Name: container.Name, - ID: container.ID}) + Name: ctr.Name, + ID: ctr.ID, + }) } outStream := config.OutStream @@ -45,8 +55,8 @@ func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, c var preRead time.Time getStatJSON := func(v interface{}) *types.StatsJSON { ss := v.(types.StatsJSON) - ss.Name = container.Name - ss.ID = container.ID + ss.Name = ctr.Name + ss.ID = ctr.ID ss.PreCPUStats = preCPUStats ss.PreRead = preRead preCPUStats = ss.CPUStats @@ -56,10 +66,10 @@ func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, c enc := json.NewEncoder(outStream) - updates := daemon.subscribeToContainerStats(container) - defer daemon.unsubscribeToContainerStats(container, updates) + updates := daemon.subscribeToContainerStats(ctr) + defer daemon.unsubscribeToContainerStats(ctr, updates) - noStreamFirstFrame := true + noStreamFirstFrame := !config.OneShot for { select { case v, ok := <-updates: @@ -70,9 +80,6 @@ func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, c var statsJSON interface{} statsJSONPost120 := getStatJSON(v) if versions.LessThan(apiVersion, "1.21") { - if runtime.GOOS == "windows" { - return errors.New("API versions pre v1.21 do not support stats on Windows") - } var ( rxBytes uint64 rxPackets uint64 diff --git a/daemon/stats/collector.go b/daemon/stats/collector.go index 88e20984bc7a1..4356661c40237 100644 --- a/daemon/stats/collector.go +++ b/daemon/stats/collector.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/container" + "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/pubsub" "github.com/sirupsen/logrus" ) @@ -14,13 +15,11 @@ import ( // Collector manages and provides container resource stats type Collector struct { m sync.Mutex + cond *sync.Cond supervisor supervisor interval time.Duration publishers map[*container.Container]*pubsub.Publisher bufReader *bufio.Reader - - // The following fields are not set on Windows currently. - clockTicksPerSecond uint64 } // NewCollector creates a stats collector that will poll the supervisor with the specified interval @@ -31,9 +30,7 @@ func NewCollector(supervisor supervisor, interval time.Duration) *Collector { publishers: make(map[*container.Container]*pubsub.Publisher), bufReader: bufio.NewReaderSize(nil, 128), } - - platformNewStatsCollector(s) - + s.cond = sync.NewCond(&s.m) return s } @@ -46,13 +43,16 @@ type supervisor interface { // the event loop for collection on the specified interval returning // a channel for the subscriber to receive on. func (s *Collector) Collect(c *container.Container) chan interface{} { - s.m.Lock() - defer s.m.Unlock() + s.cond.L.Lock() + defer s.cond.L.Unlock() + publisher, exists := s.publishers[c] if !exists { publisher = pubsub.NewPublisher(100*time.Millisecond, 1024) s.publishers[c] = publisher } + + s.cond.Broadcast() return publisher.Subscribe() } @@ -91,23 +91,21 @@ func (s *Collector) Run() { var pairs []publishersPair for { - // Put sleep at the start so that it will always be hit, - // preventing a tight loop if no stats are collected. - time.Sleep(s.interval) + s.cond.L.Lock() + for len(s.publishers) == 0 { + s.cond.Wait() + } // it does not make sense in the first iteration, // but saves allocations in further iterations pairs = pairs[:0] - s.m.Lock() for container, publisher := range s.publishers { // copy pointers here to release the lock ASAP pairs = append(pairs, publishersPair{container, publisher}) } - s.m.Unlock() - if len(pairs) == 0 { - continue - } + + s.cond.L.Unlock() onlineCPUs, err := s.getNumberOnlineCPUs() if err != nil { @@ -134,7 +132,7 @@ func (s *Collector) Run() { pair.publisher.Publish(*stats) - case notRunningErr, notFoundErr: + case errdefs.ErrConflict, errdefs.ErrNotFound: // publish empty stats containing only name and ID if not running or not found pair.publisher.Publish(types.StatsJSON{ Name: pair.container.Name, @@ -143,17 +141,13 @@ func (s *Collector) Run() { default: logrus.Errorf("collecting stats for %s: %v", pair.container.ID, err) + pair.publisher.Publish(types.StatsJSON{ + Name: pair.container.Name, + ID: pair.container.ID, + }) } } - } -} - -type notRunningErr interface { - error - Conflict() -} -type notFoundErr interface { - error - NotFound() + time.Sleep(s.interval) + } } diff --git a/daemon/stats/collector_unix.go b/daemon/stats/collector_unix.go index 2480aceb51b7b..215fe26e46e23 100644 --- a/daemon/stats/collector_unix.go +++ b/daemon/stats/collector_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package stats // import "github.com/docker/docker/daemon/stats" @@ -8,21 +9,17 @@ import ( "strconv" "strings" - "github.com/opencontainers/runc/libcontainer/system" + "golang.org/x/sys/unix" ) -/* -#include -*/ -import "C" - -// platformNewStatsCollector performs platform specific initialisation of the -// Collector structure. -func platformNewStatsCollector(s *Collector) { - s.clockTicksPerSecond = uint64(system.GetClockTicks()) -} - -const nanoSecondsPerSecond = 1e9 +const ( + // The value comes from `C.sysconf(C._SC_CLK_TCK)`, and + // on Linux it's a constant which is safe to be hard coded, + // so we can avoid using cgo here. For details, see: + // https://github.com/containerd/cgroups/pull/12 + clockTicksPerSecond = 100 + nanoSecondsPerSecond = 1e9 +) // getSystemCPUUsage returns the host system's cpu usage in // nanoseconds. An error is returned if the format of the underlying @@ -33,7 +30,6 @@ const nanoSecondsPerSecond = 1e9 // provided. See `man 5 proc` for details on specific field // information. func (s *Collector) getSystemCPUUsage() (uint64, error) { - var line string f, err := os.Open("/proc/stat") if err != nil { return 0, err @@ -43,9 +39,9 @@ func (s *Collector) getSystemCPUUsage() (uint64, error) { f.Close() }() s.bufReader.Reset(f) - err = nil - for err == nil { - line, err = s.bufReader.ReadString('\n') + + for { + line, err := s.bufReader.ReadString('\n') if err != nil { break } @@ -64,20 +60,17 @@ func (s *Collector) getSystemCPUUsage() (uint64, error) { totalClockTicks += v } return (totalClockTicks * nanoSecondsPerSecond) / - s.clockTicksPerSecond, nil + clockTicksPerSecond, nil } } return 0, fmt.Errorf("invalid stat format. Error trying to parse the '/proc/stat' file") } func (s *Collector) getNumberOnlineCPUs() (uint32, error) { - i, err := C.sysconf(C._SC_NPROCESSORS_ONLN) - // According to POSIX - errno is undefined after successful - // sysconf, and can be non-zero in several cases, so look for - // error in returned value not in errno. - // (https://sourceware.org/bugzilla/show_bug.cgi?id=21536) - if i == -1 { + var cpuset unix.CPUSet + err := unix.SchedGetaffinity(0, &cpuset) + if err != nil { return 0, err } - return uint32(i), nil + return uint32(cpuset.Count()), nil } diff --git a/daemon/stats/collector_windows.go b/daemon/stats/collector_windows.go index 018e9065f1f49..d8e4b37507b48 100644 --- a/daemon/stats/collector_windows.go +++ b/daemon/stats/collector_windows.go @@ -1,10 +1,5 @@ package stats // import "github.com/docker/docker/daemon/stats" -// platformNewStatsCollector performs platform specific initialisation of the -// Collector structure. This is a no-op on Windows. -func platformNewStatsCollector(s *Collector) { -} - // getSystemCPUUsage returns the host system's cpu usage in // nanoseconds. An error is returned if the format of the underlying // file does not match. This is a no-op on Windows. diff --git a/daemon/stats_unix.go b/daemon/stats_unix.go index ee78ca688bbd6..0afc953266ffe 100644 --- a/daemon/stats_unix.go +++ b/daemon/stats_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" diff --git a/daemon/stop.go b/daemon/stop.go index c3ac09056a970..5d4c214004c66 100644 --- a/daemon/stop.go +++ b/daemon/stop.go @@ -38,52 +38,64 @@ func (daemon *Daemon) ContainerStop(name string, timeout *int) error { // containerStop sends a stop signal, waits, sends a kill signal. func (daemon *Daemon) containerStop(container *containerpkg.Container, seconds int) error { + // TODO propagate a context down to this function + ctx := context.TODO() if !container.IsRunning() { return nil } - + var wait time.Duration + if seconds >= 0 { + wait = time.Duration(seconds) * time.Second + } + success := func() error { + daemon.LogContainerEvent(container, "stop") + return nil + } stopSignal := container.StopSignal() - // 1. Send a stop signal - if err := daemon.killPossiblyDeadProcess(container, stopSignal); err != nil { - // While normally we might "return err" here we're not going to - // because if we can't stop the container by this point then - // it's probably because it's already stopped. Meaning, between - // the time of the IsRunning() call above and now it stopped. - // Also, since the err return will be environment specific we can't - // look for any particular (common) error that would indicate - // that the process is already dead vs something else going wrong. - // So, instead we'll give it up to 2 more seconds to complete and if - // by that time the container is still running, then the error - // we got is probably valid and so we force kill it. - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - if status := <-container.Wait(ctx, containerpkg.WaitConditionNotRunning); status.Err() != nil { - logrus.Infof("Container failed to stop after sending signal %d to the process, force killing", stopSignal) - if err := daemon.killPossiblyDeadProcess(container, 9); err != nil { - return err - } - } + // 1. Send a stop signal + err := daemon.killPossiblyDeadProcess(container, stopSignal) + if err != nil { + wait = 2 * time.Second } - // 2. Wait for the process to exit on its own - ctx := context.Background() + var subCtx context.Context + var cancel context.CancelFunc if seconds >= 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Duration(seconds)*time.Second) - defer cancel() + subCtx, cancel = context.WithTimeout(ctx, wait) + } else { + subCtx, cancel = context.WithCancel(ctx) } + defer cancel() - if status := <-container.Wait(ctx, containerpkg.WaitConditionNotRunning); status.Err() != nil { - logrus.Infof("Container %v failed to exit within %d seconds of signal %d - using the force", container.ID, seconds, stopSignal) - // 3. If it doesn't, then send SIGKILL - if err := daemon.Kill(container); err != nil { - // Wait without a timeout, ignore result. - <-container.Wait(context.Background(), containerpkg.WaitConditionNotRunning) - logrus.Warn(err) // Don't return error because we only care that container is stopped, not what function stopped it + if status := <-container.Wait(subCtx, containerpkg.WaitConditionNotRunning); status.Err() == nil { + // container did exit, so ignore any previous errors and return + return success() + } + + if err != nil { + // the container has still not exited, and the kill function errored, so log the error here: + logrus.WithError(err).WithField("container", container.ID).Errorf("Error sending stop (signal %d) to container", stopSignal) + } + if seconds < 0 { + // if the client requested that we never kill / wait forever, but container.Wait was still + // interrupted (parent context cancelled, for example), we should propagate the signal failure + return err + } + + logrus.WithField("container", container.ID).Infof("Container failed to exit within %s of signal %d - using the force", wait, stopSignal) + // Stop either failed or container didnt exit, so fallback to kill. + if err := daemon.Kill(container); err != nil { + // got a kill error, but give container 2 more seconds to exit just in case + subCtx, cancel := context.WithTimeout(ctx, 2*time.Second) + defer cancel() + if status := <-container.Wait(subCtx, containerpkg.WaitConditionNotRunning); status.Err() == nil { + // container did exit, so ignore error and return + return success() } + logrus.WithError(err).WithField("container", container.ID).Error("Error killing the container") + return err } - daemon.LogContainerEvent(container, "stop") - return nil + return success() } diff --git a/daemon/top_unix.go b/daemon/top_unix.go index 99ca56f0f452e..0287acaf7a7cd 100644 --- a/daemon/top_unix.go +++ b/daemon/top_unix.go @@ -1,4 +1,5 @@ -//+build !windows +//go:build !windows +// +build !windows package daemon // import "github.com/docker/docker/daemon" @@ -20,7 +21,7 @@ func validatePSArgs(psArgs string) error { // NOTE: \\s does not detect unicode whitespaces. // So we use fieldsASCII instead of strings.Fields in parsePSOutput. // See https://github.com/docker/docker/pull/24358 - // nolint: gosimple + //nolint: gosimple re := regexp.MustCompile("\\s+([^\\s]*)=\\s*(PID[^\\s]*)") for _, group := range re.FindAllStringSubmatch(psArgs, -1) { if len(group) >= 3 { @@ -144,20 +145,20 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*container.Conta return nil, err } - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return nil, err } - if !container.IsRunning() { - return nil, errNotRunning(container.ID) + if !ctr.IsRunning() { + return nil, errNotRunning(ctr.ID) } - if container.IsRestarting() { - return nil, errContainerIsRestarting(container.ID) + if ctr.IsRestarting() { + return nil, errContainerIsRestarting(ctr.ID) } - procs, err := daemon.containerd.ListPids(context.Background(), container.ID) + procs, err := daemon.containerd.ListPids(context.Background(), ctr.ID) if err != nil { return nil, err } @@ -184,6 +185,6 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*container.Conta if err != nil { return nil, err } - daemon.LogContainerEvent(container, "top") + daemon.LogContainerEvent(ctr, "top") return procList, nil } diff --git a/daemon/top_unix_test.go b/daemon/top_unix_test.go index 41cb3e1cd97a4..a663323b67549 100644 --- a/daemon/top_unix_test.go +++ b/daemon/top_unix_test.go @@ -1,9 +1,12 @@ -//+build !windows +//go:build !windows +// +build !windows package daemon // import "github.com/docker/docker/daemon" import ( "testing" + + "gotest.tools/v3/assert" ) func TestContainerTopValidatePSArgs(t *testing.T) { @@ -22,14 +25,14 @@ func TestContainerTopValidatePSArgs(t *testing.T) { "": false, } for psArgs, errExpected := range tests { - err := validatePSArgs(psArgs) - t.Logf("tested %q, got err=%v", psArgs, err) - if errExpected && err == nil { - t.Fatalf("expected error, got %v (%q)", err, psArgs) - } - if !errExpected && err != nil { - t.Fatalf("expected nil, got %v (%q)", err, psArgs) - } + t.Run(psArgs, func(t *testing.T) { + err := validatePSArgs(psArgs) + if errExpected { + assert.ErrorContains(t, err, "", "psArgs: %q", psArgs) + } else { + assert.NilError(t, err, "psArgs: %q", psArgs) + } + }) } } @@ -39,41 +42,59 @@ func TestContainerTopParsePSOutput(t *testing.T) { pids []uint32 errExpected bool }{ - {[]byte(` PID COMMAND + { + output: []byte(` PID COMMAND 42 foo 43 bar - - 100 baz -`), []uint32{42, 43}, false}, - {[]byte(` UID COMMAND +`), + pids: []uint32{42, 43}, + errExpected: false, + }, + { + output: []byte(` UID COMMAND 42 foo 43 bar - - 100 baz -`), []uint32{42, 43}, true}, +`), + pids: []uint32{42, 43}, + errExpected: true, + }, // unicode space (U+2003, 0xe2 0x80 0x83) - {[]byte(` PID COMMAND + { + output: []byte(` PID COMMAND 42 foo 43 bar - - 100 baz -`), []uint32{42, 43}, true}, +`), + pids: []uint32{42, 43}, + errExpected: true, + }, // the first space is U+2003, the second one is ascii. - {[]byte(` PID COMMAND + { + output: []byte(` PID COMMAND 42 foo 43 bar 100 baz -`), []uint32{42, 43}, true}, +`), + pids: []uint32{42, 43}, + errExpected: true, + }, } - for _, f := range tests { - _, err := parsePSOutput(f.output, f.pids) - t.Logf("tested %q, got err=%v", string(f.output), err) - if f.errExpected && err == nil { - t.Fatalf("expected error, got %v (%q)", err, string(f.output)) - } - if !f.errExpected && err != nil { - t.Fatalf("expected nil, got %v (%q)", err, string(f.output)) - } + for _, tc := range tests { + tc := tc + t.Run(string(tc.output), func(t *testing.T) { + _, err := parsePSOutput(tc.output, tc.pids) + if tc.errExpected && err == nil { + t.Fatalf("expected error, got %v (%q)", err, string(tc.output)) + } + if !tc.errExpected && err != nil { + t.Fatalf("expected nil, got %v (%q)", err, string(tc.output)) + } + }) } } diff --git a/daemon/top_windows.go b/daemon/top_windows.go index 1b3f84396282b..010b426d3f297 100644 --- a/daemon/top_windows.go +++ b/daemon/top_windows.go @@ -7,7 +7,7 @@ import ( "time" containertypes "github.com/docker/docker/api/types/container" - "github.com/docker/go-units" + units "github.com/docker/go-units" ) // ContainerTop handles `docker top` client requests. @@ -51,10 +51,10 @@ func (daemon *Daemon) ContainerTop(name string, psArgs string) (*containertypes. procList.Titles = []string{"Name", "PID", "CPU", "Private Working Set"} for _, j := range s { - d := time.Duration((j.KernelTime100ns + j.UserTime100ns) * 100) // Combined time in nanoseconds + d := time.Duration((j.KernelTime_100Ns + j.UserTime_100Ns) * 100) // Combined time in nanoseconds procList.Processes = append(procList.Processes, []string{ j.ImageName, - fmt.Sprint(j.ProcessId), + fmt.Sprint(j.ProcessID), fmt.Sprintf("%02d:%02d:%02d.%03d", int(d.Hours()), int(d.Minutes())%60, int(d.Seconds())%60, int(d.Nanoseconds()/1000000)%1000), units.HumanSize(float64(j.MemoryWorkingSetPrivateBytes))}) } diff --git a/daemon/trustkey.go b/daemon/trustkey.go index 4d72c932f1485..a6b662d7c9960 100644 --- a/daemon/trustkey.go +++ b/daemon/trustkey.go @@ -17,7 +17,7 @@ import ( // TODO: this should use more of libtrust.LoadOrCreateTrustKey which may need // a refactor or this function to be moved into libtrust func loadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) { - err := system.MkdirAll(filepath.Dir(trustKeyPath), 0755, "") + err := system.MkdirAll(filepath.Dir(trustKeyPath), 0755) if err != nil { return nil, err } diff --git a/daemon/trustkey_test.go b/daemon/trustkey_test.go index e49e76aa3eeec..b30c2ebeae688 100644 --- a/daemon/trustkey_test.go +++ b/daemon/trustkey_test.go @@ -1,23 +1,22 @@ package daemon // import "github.com/docker/docker/daemon" import ( - "io/ioutil" "os" "path/filepath" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/fs" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/fs" ) // LoadOrCreateTrustKey func TestLoadOrCreateTrustKeyInvalidKeyFile(t *testing.T) { - tmpKeyFolderPath, err := ioutil.TempDir("", "api-trustkey-test") + tmpKeyFolderPath, err := os.MkdirTemp("", "api-trustkey-test") assert.NilError(t, err) defer os.RemoveAll(tmpKeyFolderPath) - tmpKeyFile, err := ioutil.TempFile(tmpKeyFolderPath, "keyfile") + tmpKeyFile, err := os.CreateTemp(tmpKeyFolderPath, "keyfile") assert.NilError(t, err) _, err = loadOrCreateTrustKey(tmpKeyFile.Name()) diff --git a/daemon/unpause.go b/daemon/unpause.go index 27648ae72e3c8..fbcf7a589ea96 100644 --- a/daemon/unpause.go +++ b/daemon/unpause.go @@ -10,33 +10,33 @@ import ( // ContainerUnpause unpauses a container func (daemon *Daemon) ContainerUnpause(name string) error { - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return err } - return daemon.containerUnpause(container) + return daemon.containerUnpause(ctr) } // containerUnpause resumes the container execution after the container is paused. -func (daemon *Daemon) containerUnpause(container *container.Container) error { - container.Lock() - defer container.Unlock() +func (daemon *Daemon) containerUnpause(ctr *container.Container) error { + ctr.Lock() + defer ctr.Unlock() // We cannot unpause the container which is not paused - if !container.Paused { - return fmt.Errorf("Container %s is not paused", container.ID) + if !ctr.Paused { + return fmt.Errorf("Container %s is not paused", ctr.ID) } - if err := daemon.containerd.Resume(context.Background(), container.ID); err != nil { - return fmt.Errorf("Cannot unpause container %s: %s", container.ID, err) + if err := daemon.containerd.Resume(context.Background(), ctr.ID); err != nil { + return fmt.Errorf("Cannot unpause container %s: %s", ctr.ID, err) } - container.Paused = false - daemon.setStateCounter(container) - daemon.updateHealthMonitor(container) - daemon.LogContainerEvent(container, "unpause") + ctr.Paused = false + daemon.setStateCounter(ctr) + daemon.updateHealthMonitor(ctr) + daemon.LogContainerEvent(ctr, "unpause") - if err := container.CheckpointTo(daemon.containersReplica); err != nil { + if err := ctr.CheckpointTo(daemon.containersReplica); err != nil { logrus.WithError(err).Warn("could not save container to disk") } diff --git a/daemon/update.go b/daemon/update.go index 0ebb139d3dafc..bd8479fc05a76 100644 --- a/daemon/update.go +++ b/daemon/update.go @@ -13,12 +13,7 @@ import ( func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostConfig) (container.ContainerUpdateOKBody, error) { var warnings []string - c, err := daemon.GetContainer(name) - if err != nil { - return container.ContainerUpdateOKBody{Warnings: warnings}, err - } - - warnings, err = daemon.verifyContainerSettings(c.OS, hostConfig, nil, true) + warnings, err := daemon.verifyContainerSettings(hostConfig, nil, true) if err != nil { return container.ContainerUpdateOKBody{Warnings: warnings}, errdefs.InvalidParameter(err) } @@ -35,57 +30,63 @@ func (daemon *Daemon) update(name string, hostConfig *container.HostConfig) erro return nil } - container, err := daemon.GetContainer(name) + ctr, err := daemon.GetContainer(name) if err != nil { return err } restoreConfig := false - backupHostConfig := *container.HostConfig + backupHostConfig := *ctr.HostConfig + defer func() { if restoreConfig { - container.Lock() - container.HostConfig = &backupHostConfig - container.CheckpointTo(daemon.containersReplica) - container.Unlock() + ctr.Lock() + if !ctr.RemovalInProgress && !ctr.Dead { + ctr.HostConfig = &backupHostConfig + ctr.CheckpointTo(daemon.containersReplica) + } + ctr.Unlock() } }() - if container.RemovalInProgress || container.Dead { - return errCannotUpdate(container.ID, fmt.Errorf("container is marked for removal and cannot be \"update\"")) + ctr.Lock() + + if ctr.RemovalInProgress || ctr.Dead { + ctr.Unlock() + return errCannotUpdate(ctr.ID, fmt.Errorf("container is marked for removal and cannot be \"update\"")) } - container.Lock() - if err := container.UpdateContainer(hostConfig); err != nil { + if err := ctr.UpdateContainer(hostConfig); err != nil { restoreConfig = true - container.Unlock() - return errCannotUpdate(container.ID, err) + ctr.Unlock() + return errCannotUpdate(ctr.ID, err) } - if err := container.CheckpointTo(daemon.containersReplica); err != nil { + if err := ctr.CheckpointTo(daemon.containersReplica); err != nil { restoreConfig = true - container.Unlock() - return errCannotUpdate(container.ID, err) + ctr.Unlock() + return errCannotUpdate(ctr.ID, err) } - container.Unlock() + + ctr.Unlock() // if Restart Policy changed, we need to update container monitor if hostConfig.RestartPolicy.Name != "" { - container.UpdateMonitor(hostConfig.RestartPolicy) + ctr.UpdateMonitor(hostConfig.RestartPolicy) } // If container is not running, update hostConfig struct is enough, // resources will be updated when the container is started again. // If container is running (including paused), we need to update configs // to the real world. - if container.IsRunning() && !container.IsRestarting() { - if err := daemon.containerd.UpdateResources(context.Background(), container.ID, toContainerdResources(hostConfig.Resources)); err != nil { + if ctr.IsRunning() && !ctr.IsRestarting() { + if err := daemon.containerd.UpdateResources(context.Background(), ctr.ID, toContainerdResources(hostConfig.Resources)); err != nil { restoreConfig = true // TODO: it would be nice if containerd responded with better errors here so we can classify this better. - return errCannotUpdate(container.ID, errdefs.System(err)) + return errCannotUpdate(ctr.ID, errdefs.System(err)) } } - daemon.LogContainerEvent(container, "update") + daemon.LogContainerEvent(ctr, "update") return nil } diff --git a/daemon/update_linux.go b/daemon/update_linux.go index 6a307eabc5a93..c1d3684868db1 100644 --- a/daemon/update_linux.go +++ b/daemon/update_linux.go @@ -4,12 +4,12 @@ import ( "time" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/libcontainerd" - "github.com/opencontainers/runtime-spec/specs-go" + libcontainerdtypes "github.com/docker/docker/libcontainerd/types" + specs "github.com/opencontainers/runtime-spec/specs-go" ) -func toContainerdResources(resources container.Resources) *libcontainerd.Resources { - var r libcontainerd.Resources +func toContainerdResources(resources container.Resources) *libcontainerdtypes.Resources { + var r libcontainerdtypes.Resources r.BlockIO = &specs.LinuxBlockIO{ Weight: &resources.BlkioWeight, @@ -50,5 +50,6 @@ func toContainerdResources(resources container.Resources) *libcontainerd.Resourc r.Memory.Swap = &resources.MemorySwap } + r.Pids = getPidsLimit(resources) return &r } diff --git a/daemon/update_windows.go b/daemon/update_windows.go index fada3c1c0b557..cefbe919a5407 100644 --- a/daemon/update_windows.go +++ b/daemon/update_windows.go @@ -2,10 +2,10 @@ package daemon // import "github.com/docker/docker/daemon" import ( "github.com/docker/docker/api/types/container" - "github.com/docker/docker/libcontainerd" + libcontainerdtypes "github.com/docker/docker/libcontainerd/types" ) -func toContainerdResources(resources container.Resources) *libcontainerd.Resources { +func toContainerdResources(resources container.Resources) *libcontainerdtypes.Resources { // We don't support update, so do nothing return nil } diff --git a/daemon/util_test.go b/daemon/util_test.go index b2c464f737fac..64a28be865c8b 100644 --- a/daemon/util_test.go +++ b/daemon/util_test.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package daemon @@ -7,10 +8,17 @@ import ( "time" "github.com/containerd/containerd" - "github.com/docker/docker/libcontainerd" + libcontainerdtypes "github.com/docker/docker/libcontainerd/types" specs "github.com/opencontainers/runtime-spec/specs-go" ) +type mockProcess struct { +} + +func (m *mockProcess) Delete(_ context.Context) (uint32, time.Time, error) { + return 0, time.Time{}, nil +} + // Mock containerd client implementation, for unit tests. type MockContainerdClient struct { } @@ -18,19 +26,19 @@ type MockContainerdClient struct { func (c *MockContainerdClient) Version(ctx context.Context) (containerd.Version, error) { return containerd.Version{}, nil } -func (c *MockContainerdClient) Restore(ctx context.Context, containerID string, attachStdio libcontainerd.StdioCallback) (alive bool, pid int, err error) { - return false, 0, nil +func (c *MockContainerdClient) Restore(ctx context.Context, containerID string, attachStdio libcontainerdtypes.StdioCallback) (alive bool, pid int, p libcontainerdtypes.Process, err error) { + return false, 0, &mockProcess{}, nil } -func (c *MockContainerdClient) Create(ctx context.Context, containerID string, spec *specs.Spec, runtimeOptions interface{}) error { +func (c *MockContainerdClient) Create(ctx context.Context, containerID string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error { return nil } -func (c *MockContainerdClient) Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerd.StdioCallback) (pid int, err error) { +func (c *MockContainerdClient) Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (pid int, err error) { return 0, nil } func (c *MockContainerdClient) SignalProcess(ctx context.Context, containerID, processID string, signal int) error { return nil } -func (c *MockContainerdClient) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerd.StdioCallback) (int, error) { +func (c *MockContainerdClient) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) { return 0, nil } func (c *MockContainerdClient) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error { @@ -41,23 +49,23 @@ func (c *MockContainerdClient) CloseStdin(ctx context.Context, containerID, proc } func (c *MockContainerdClient) Pause(ctx context.Context, containerID string) error { return nil } func (c *MockContainerdClient) Resume(ctx context.Context, containerID string) error { return nil } -func (c *MockContainerdClient) Stats(ctx context.Context, containerID string) (*libcontainerd.Stats, error) { +func (c *MockContainerdClient) Stats(ctx context.Context, containerID string) (*libcontainerdtypes.Stats, error) { return nil, nil } func (c *MockContainerdClient) ListPids(ctx context.Context, containerID string) ([]uint32, error) { return nil, nil } -func (c *MockContainerdClient) Summary(ctx context.Context, containerID string) ([]libcontainerd.Summary, error) { +func (c *MockContainerdClient) Summary(ctx context.Context, containerID string) ([]libcontainerdtypes.Summary, error) { return nil, nil } func (c *MockContainerdClient) DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) { return 0, time.Time{}, nil } func (c *MockContainerdClient) Delete(ctx context.Context, containerID string) error { return nil } -func (c *MockContainerdClient) Status(ctx context.Context, containerID string) (libcontainerd.Status, error) { +func (c *MockContainerdClient) Status(ctx context.Context, containerID string) (containerd.ProcessStatus, error) { return "null", nil } -func (c *MockContainerdClient) UpdateResources(ctx context.Context, containerID string, resources *libcontainerd.Resources) error { +func (c *MockContainerdClient) UpdateResources(ctx context.Context, containerID string, resources *libcontainerdtypes.Resources) error { return nil } func (c *MockContainerdClient) CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error { diff --git a/daemon/volumes.go b/daemon/volumes.go index ad3c96a945991..7327fe35f2985 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -62,7 +62,7 @@ func (m mounts) parts(i int) int { func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) { binds := map[string]bool{} mountPoints := map[string]*volumemounts.MountPoint{} - parser := volumemounts.NewParser(container.OS) + parser := volumemounts.NewParser() ctx := context.TODO() defer func() { @@ -95,12 +95,12 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo for _, v := range hostConfig.VolumesFrom { containerID, mode, err := parser.ParseVolumesFrom(v) if err != nil { - return err + return errdefs.InvalidParameter(err) } c, err := daemon.GetContainer(containerID) if err != nil { - return err + return errdefs.InvalidParameter(err) } for _, m := range c.MountPoints { @@ -265,8 +265,6 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { container.Lock() defer container.Unlock() - parser := volumemounts.NewParser(container.OS) - maybeUpdate := make(map[string]bool) for _, mp := range container.MountPoints { if mp.Spec.Source != "" && mp.Type != "" { @@ -283,6 +281,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) { mountSpecs[m.Target] = true } + parser := volumemounts.NewParser() binds := make(map[string]*volumemounts.MountPoint, len(container.HostConfig.Binds)) for _, rawSpec := range container.HostConfig.Binds { mp, err := parser.ParseMountRaw(rawSpec, container.HostConfig.VolumeDriver) diff --git a/daemon/volumes_linux_test.go b/daemon/volumes_linux_test.go index 72830c3e81b2f..3abff3e454332 100644 --- a/daemon/volumes_linux_test.go +++ b/daemon/volumes_linux_test.go @@ -34,12 +34,12 @@ func TestBindDaemonRoot(t *testing.T) { "source is /": "/", } { t.Run(desc, func(t *testing.T) { - mount := mount.Mount{ + mnt := mount.Mount{ Type: mount.TypeBind, Source: source, BindOptions: test.opts, } - needsProp, err := d.validateBindDaemonRoot(mount) + needsProp, err := d.validateBindDaemonRoot(mnt) if (err != nil) != test.err { t.Fatalf("expected err=%v, got: %v", test.err, err) } diff --git a/daemon/volumes_unit_test.go b/daemon/volumes_unit_test.go index 6bdebe467cbb3..9a572215f2de7 100644 --- a/daemon/volumes_unit_test.go +++ b/daemon/volumes_unit_test.go @@ -1,7 +1,6 @@ package daemon // import "github.com/docker/docker/daemon" import ( - "runtime" "testing" volumemounts "github.com/docker/docker/volume/mounts" @@ -21,7 +20,7 @@ func TestParseVolumesFrom(t *testing.T) { {"foobar:baz", "", "", true}, } - parser := volumemounts.NewParser(runtime.GOOS) + parser := volumemounts.NewParser() for _, c := range cases { id, mode, err := parser.ParseVolumesFrom(c.spec) diff --git a/daemon/volumes_unix.go b/daemon/volumes_unix.go index 5ddb926de74a5..59a95c239a787 100644 --- a/daemon/volumes_unix.go +++ b/daemon/volumes_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" @@ -9,10 +10,11 @@ import ( "strconv" "strings" + mounttypes "github.com/docker/docker/api/types/mount" "github.com/docker/docker/container" "github.com/docker/docker/pkg/fileutils" - "github.com/docker/docker/pkg/mount" volumemounts "github.com/docker/docker/volume/mounts" + "github.com/moby/sys/mount" ) // setupMounts iterates through each of the mount points for a container and @@ -58,6 +60,9 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er Writable: m.RW, Propagation: string(m.Propagation), } + if m.Spec.Type == mounttypes.TypeBind && m.Spec.BindOptions != nil { + mnt.NonRecursive = m.Spec.BindOptions.NonRecursive + } if m.Volume != nil { attributes := map[string]string{ "driver": m.Volume.DriverName(), @@ -78,12 +83,12 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, er // metadata, the ownership must be set properly for potential container // remapped root (user namespaces) rootIDs := daemon.idMapping.RootPair() - for _, mount := range netMounts { + for _, mnt := range netMounts { // we should only modify ownership of network files within our own container // metadata repository. If the user specifies a mount path external, it is // up to the user to make sure the file has proper ownership for userns - if strings.Index(mount.Source, daemon.repository) == 0 { - if err := os.Chown(mount.Source, rootIDs.UID, rootIDs.GID); err != nil { + if strings.Index(mnt.Source, daemon.repository) == 0 { + if err := os.Chown(mnt.Source, rootIDs.UID, rootIDs.GID); err != nil { return nil, err } } @@ -129,13 +134,13 @@ func (daemon *Daemon) mountVolumes(container *container.Container) error { return err } - opts := "rbind,ro" - if m.Writable { - opts = "rbind,rw" + bindMode := "rbind" + if m.NonRecursive { + bindMode = "bind" } - - if err := mount.Mount(m.Source, dest, bindMountType, opts); err != nil { - return err + writeMode := "ro" + if m.Writable { + writeMode = "rw" } // mountVolumes() seems to be called for temporary mounts @@ -146,8 +151,9 @@ func (daemon *Daemon) mountVolumes(container *container.Container) error { // then these unmounts will propagate and unmount original // mount as well. So make all these mounts rprivate. // Do not use propagation property of volume as that should - // apply only when mounting happen inside the container. - if err := mount.MakeRPrivate(dest); err != nil { + // apply only when mounting happens inside the container. + opts := strings.Join([]string{bindMode, writeMode, "rprivate"}, ",") + if err := mount.Mount(m.Source, dest, "", opts); err != nil { return err } } diff --git a/daemon/volumes_unix_test.go b/daemon/volumes_unix_test.go index 36e19110d1605..a566d19b4a604 100644 --- a/daemon/volumes_unix_test.go +++ b/daemon/volumes_unix_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package daemon // import "github.com/docker/docker/daemon" @@ -96,6 +97,8 @@ func TestBackportMountSpec(t *testing.T) { return string(b) } + mpc := *c.MountPoints["/jambolan"] + for _, x := range []expected{ { mp: &volumemounts.MountPoint{ @@ -225,7 +228,7 @@ func TestBackportMountSpec(t *testing.T) { comment: "partially configured named volume caused by #32613", }, { - mp: &(*c.MountPoints["/jambolan"]), // copy the mountpoint, expect no changes + mp: &mpc, comment: "volume defined in mounts API", }, { diff --git a/distribution/config.go b/distribution/config.go index 438051c296e27..48b0b46e26a43 100644 --- a/distribution/config.go +++ b/distribution/config.go @@ -3,7 +3,6 @@ package distribution // import "github.com/docker/docker/distribution" import ( "context" "encoding/json" - "fmt" "io" "runtime" @@ -19,8 +18,9 @@ import ( refstore "github.com/docker/docker/reference" "github.com/docker/docker/registry" "github.com/docker/libtrust" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" ) // Config stores configuration for communicating @@ -71,8 +71,8 @@ type ImagePushConfig struct { // ConfigMediaType is the configuration media type for // schema2 manifests. ConfigMediaType string - // LayerStores (indexed by operating system) manages layers. - LayerStores map[string]PushLayerProvider + // LayerStores manages layers. + LayerStores PushLayerProvider // TrustKey is the private key for legacy signatures. This is typically // an ephemeral key, since these signatures are no longer verified. TrustKey libtrust.PrivateKey @@ -84,8 +84,8 @@ type ImagePushConfig struct { // by digest. Allows getting an image configurations rootfs from the // configuration. type ImageConfigStore interface { - Put([]byte) (digest.Digest, error) - Get(digest.Digest) ([]byte, error) + Put(context.Context, []byte) (digest.Digest, error) + Get(context.Context, digest.Digest) ([]byte, error) RootFSFromConfig([]byte) (*image.RootFS, error) PlatformFromConfig([]byte) (*specs.Platform, error) } @@ -128,12 +128,12 @@ func NewImageConfigStoreFromStore(is image.Store) ImageConfigStore { } } -func (s *imageConfigStore) Put(c []byte) (digest.Digest, error) { +func (s *imageConfigStore) Put(_ context.Context, c []byte) (digest.Digest, error) { id, err := s.Store.Create(c) return digest.Digest(id), err } -func (s *imageConfigStore) Get(d digest.Digest) ([]byte, error) { +func (s *imageConfigStore) Get(_ context.Context, d digest.Digest) ([]byte, error) { img, err := s.Store.Get(image.IDFromDigest(d)) if err != nil { return nil, err @@ -155,37 +155,25 @@ func (s *imageConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error) return nil, err } - // fail immediately on Windows when downloading a non-Windows image - // and vice versa. Exception on Windows if Linux Containers are enabled. - if runtime.GOOS == "windows" && unmarshalledConfig.OS == "linux" && !system.LCOWSupported() { - return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS) - } else if runtime.GOOS != "windows" && unmarshalledConfig.OS == "windows" { - return nil, fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS) - } - os := unmarshalledConfig.OS if os == "" { os = runtime.GOOS } if !system.IsOSSupported(os) { - return nil, system.ErrNotSupportedOperatingSystem + return nil, errors.Wrapf(system.ErrNotSupportedOperatingSystem, "image operating system %q cannot be used on this platform", os) } - return &specs.Platform{OS: os, Architecture: unmarshalledConfig.Architecture, OSVersion: unmarshalledConfig.OSVersion}, nil + return &specs.Platform{OS: os, Architecture: unmarshalledConfig.Architecture, Variant: unmarshalledConfig.Variant, OSVersion: unmarshalledConfig.OSVersion}, nil } type storeLayerProvider struct { ls layer.Store } -// NewLayerProvidersFromStores returns layer providers backed by +// NewLayerProvidersFromStore returns layer providers backed by // an instance of LayerStore. Only getting layers as gzipped // tars is supported. -func NewLayerProvidersFromStores(lss map[string]layer.Store) map[string]PushLayerProvider { - plps := make(map[string]PushLayerProvider) - for os, ls := range lss { - plps[os] = &storeLayerProvider{ls: ls} - } - return plps +func NewLayerProvidersFromStore(ls layer.Store) PushLayerProvider { + return &storeLayerProvider{ls: ls} } func (p *storeLayerProvider) Get(lid layer.ChainID) (PushLayer, error) { diff --git a/distribution/errors.go b/distribution/errors.go index 9dc5955f26c7d..6b67d53ff4294 100644 --- a/distribution/errors.go +++ b/distribution/errors.go @@ -9,7 +9,7 @@ import ( "github.com/docker/distribution" "github.com/docker/distribution/reference" "github.com/docker/distribution/registry/api/errcode" - "github.com/docker/distribution/registry/api/v2" + v2 "github.com/docker/distribution/registry/api/v2" "github.com/docker/distribution/registry/client" "github.com/docker/distribution/registry/client/auth" "github.com/docker/docker/distribution/xfer" @@ -34,10 +34,6 @@ func (e ErrNoSupport) Error() string { type fallbackError struct { // err is the error being wrapped. err error - // confirmedV2 is set to true if it was confirmed that the registry - // supports the v2 protocol. This is used to limit fallbacks to the v1 - // protocol. - confirmedV2 bool // transportOK is set to true if we managed to speak HTTP with the // registry. This confirms that we're using appropriate TLS settings // (or lack of TLS). @@ -53,15 +49,6 @@ func (f fallbackError) Cause() error { return f.err } -// shouldV2Fallback returns true if this error is a reason to fall back to v1. -func shouldV2Fallback(err errcode.Error) bool { - switch err.Code { - case errcode.ErrorCodeUnauthorized, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown: - return true - } - return false -} - type notFoundError struct { cause errcode.Error ref reference.Named @@ -112,6 +99,23 @@ func TranslatePullError(err error, ref reference.Named) error { return errdefs.Unknown(err) } +func isNotFound(err error) bool { + switch v := err.(type) { + case errcode.Errors: + for _, e := range v { + if isNotFound(e) { + return true + } + } + case errcode.Error: + switch v.Code { + case errcode.ErrorCodeDenied, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown: + return true + } + } + return false +} + // continueOnError returns true if we should fallback to the next endpoint // as a result of this error. func continueOnError(err error, mirrorEndpoint bool) bool { @@ -124,7 +128,7 @@ func continueOnError(err error, mirrorEndpoint bool) bool { case ErrNoSupport: return continueOnError(v.Err, mirrorEndpoint) case errcode.Error: - return mirrorEndpoint || shouldV2Fallback(v) + return mirrorEndpoint case *client.UnexpectedHTTPResponseError: return true case ImageConfigPullError: diff --git a/distribution/errors_test.go b/distribution/errors_test.go index 7105bdb4d6079..164c57a650fd7 100644 --- a/distribution/errors_test.go +++ b/distribution/errors_test.go @@ -7,26 +7,26 @@ import ( "testing" "github.com/docker/distribution/registry/api/errcode" - "github.com/docker/distribution/registry/api/v2" "github.com/docker/distribution/registry/client" ) +var errUnexpected = errors.New("some totally unexpected error") + var alwaysContinue = []error{ &client.UnexpectedHTTPResponseError{}, - - // Some errcode.Errors that don't disprove the existence of a V1 image - errcode.Error{Code: errcode.ErrorCodeUnauthorized}, - errcode.Error{Code: v2.ErrorCodeManifestUnknown}, - errcode.Error{Code: v2.ErrorCodeNameUnknown}, - - errors.New("some totally unexpected error"), + errcode.Errors{}, + errUnexpected, + // nested + errcode.Errors{errUnexpected}, + ErrNoSupport{Err: errUnexpected}, } var continueFromMirrorEndpoint = []error{ ImageConfigPullError{}, - - // Some other errcode.Error that doesn't indicate we should search for a V1 image. - errcode.Error{Code: errcode.ErrorCodeTooManyRequests}, + errcode.Error{}, + // nested + errcode.Errors{errcode.Error{}}, + ErrNoSupport{Err: errcode.Error{}}, } var neverContinue = []error{ @@ -67,19 +67,3 @@ func TestContinueOnError_NeverContinue(t *testing.T) { } } } - -func TestContinueOnError_UnnestsErrors(t *testing.T) { - // ContinueOnError should evaluate nested errcode.Errors. - - // Assumes that v2.ErrorCodeNameUnknown is a continueable error code. - err := errcode.Errors{errcode.Error{Code: v2.ErrorCodeNameUnknown}} - if !continueOnError(err, false) { - t.Fatal("ContinueOnError should unnest, base return value on errcode.Errors") - } - - // Assumes that errcode.ErrorCodeTooManyRequests is not a V1-fallback indication - err = errcode.Errors{errcode.Error{Code: errcode.ErrorCodeTooManyRequests}} - if continueOnError(err, false) { - t.Fatal("ContinueOnError should unnest, base return value on errcode.Errors") - } -} diff --git a/distribution/manifest.go b/distribution/manifest.go new file mode 100644 index 0000000000000..ff6cf4bcf342b --- /dev/null +++ b/distribution/manifest.go @@ -0,0 +1,221 @@ +package distribution + +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/log" + "github.com/containerd/containerd/remotes" + "github.com/docker/distribution" + "github.com/docker/distribution/manifest/manifestlist" + "github.com/docker/distribution/manifest/schema1" + "github.com/docker/distribution/manifest/schema2" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +// This is used by manifestStore to pare down the requirements to implement a +// full distribution.ManifestService, since `Get` is all we use here. +type manifestGetter interface { + Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) +} + +type manifestStore struct { + local ContentStore + remote manifestGetter +} + +// ContentStore is the interface used to persist registry blobs +// +// Currently this is only used to persist manifests and manifest lists. +// It is exported because `distribution.Pull` takes one as an argument. +type ContentStore interface { + content.Ingester + content.Provider + Info(ctx context.Context, dgst digest.Digest) (content.Info, error) + Abort(ctx context.Context, ref string) error +} + +func (m *manifestStore) getLocal(ctx context.Context, desc specs.Descriptor) (distribution.Manifest, error) { + ra, err := m.local.ReaderAt(ctx, desc) + if err != nil { + return nil, errors.Wrap(err, "error getting content store reader") + } + defer ra.Close() + + r := io.NewSectionReader(ra, 0, ra.Size()) + data, err := io.ReadAll(r) + if err != nil { + return nil, errors.Wrap(err, "error reading manifest from content store") + } + + manifest, _, err := distribution.UnmarshalManifest(desc.MediaType, data) + if err != nil { + return nil, errors.Wrap(err, "error unmarshaling manifest from content store") + } + return manifest, nil +} + +func (m *manifestStore) getMediaType(ctx context.Context, desc specs.Descriptor) (string, error) { + ra, err := m.local.ReaderAt(ctx, desc) + if err != nil { + return "", errors.Wrap(err, "error getting reader to detect media type") + } + defer ra.Close() + + mt, err := detectManifestMediaType(ra) + if err != nil { + return "", errors.Wrap(err, "error detecting media type") + } + return mt, nil +} + +func (m *manifestStore) Get(ctx context.Context, desc specs.Descriptor) (distribution.Manifest, error) { + l := log.G(ctx) + + if desc.MediaType == "" { + // When pulling by digest we will not have the media type on the + // descriptor since we have not made a request to the registry yet + // + // We already have the digest, so we only lookup locally... by digest. + // + // Let's try to detect the media type so we can have a good ref key + // here. We may not even have the content locally, and this is fine, but + // if we do we should determine that. + mt, err := m.getMediaType(ctx, desc) + if err != nil && !errdefs.IsNotFound(err) { + l.WithError(err).Warn("Error looking up media type of content") + } + desc.MediaType = mt + } + + key := remotes.MakeRefKey(ctx, desc) + + // Here we open a writer to the requested content. This both gives us a + // reference to write to if indeed we need to persist it and increments the + // ref count on the content. + w, err := m.local.Writer(ctx, content.WithDescriptor(desc), content.WithRef(key)) + if err != nil { + if errdefs.IsAlreadyExists(err) { + var manifest distribution.Manifest + if manifest, err = m.getLocal(ctx, desc); err == nil { + return manifest, nil + } + } + // always fallback to the remote if there is an error with the local store + } + if w != nil { + defer w.Close() + } + + l.WithError(err).Debug("Fetching manifest from remote") + + manifest, err := m.remote.Get(ctx, desc.Digest) + if err != nil { + if err := m.local.Abort(ctx, key); err != nil { + l.WithError(err).Warn("Error while attempting to abort content ingest") + } + return nil, err + } + + if w != nil { + // if `w` is nil here, something happened with the content store, so don't bother trying to persist. + if err := m.Put(ctx, manifest, desc, w); err != nil { + if err := m.local.Abort(ctx, key); err != nil { + l.WithError(err).Warn("error aborting content ingest") + } + l.WithError(err).Warn("Error persisting manifest") + } + } + return manifest, nil +} + +func (m *manifestStore) Put(ctx context.Context, manifest distribution.Manifest, desc specs.Descriptor, w content.Writer) error { + mt, payload, err := manifest.Payload() + if err != nil { + return err + } + desc.Size = int64(len(payload)) + desc.MediaType = mt + + if _, err = w.Write(payload); err != nil { + return errors.Wrap(err, "error writing manifest to content store") + } + + if err := w.Commit(ctx, desc.Size, desc.Digest); err != nil { + return errors.Wrap(err, "error committing manifest to content store") + } + return nil +} + +func detectManifestMediaType(ra content.ReaderAt) (string, error) { + dt := make([]byte, ra.Size()) + if _, err := ra.ReadAt(dt, 0); err != nil { + return "", err + } + + return detectManifestBlobMediaType(dt) +} + +// This is used when the manifest store does not know the media type of a sha it +// was told to get. This would currently only happen when pulling by digest. +// The media type is needed so the blob can be unmarshalled properly. +func detectManifestBlobMediaType(dt []byte) (string, error) { + var mfst struct { + MediaType string `json:"mediaType"` + Manifests json.RawMessage `json:"manifests"` // oci index, manifest list + Config json.RawMessage `json:"config"` // schema2 Manifest + Layers json.RawMessage `json:"layers"` // schema2 Manifest + FSLayers json.RawMessage `json:"fsLayers"` // schema1 Manifest + } + + if err := json.Unmarshal(dt, &mfst); err != nil { + return "", err + } + + // We may have a media type specified in the json, in which case that should be used. + // Docker types should generally have a media type set. + // OCI (golang) types do not have a `mediaType` defined, and it is optional in the spec. + // + // `distribution.UnmarshalManifest`, which is used to unmarshal this for real, checks these media type values. + // If the specified media type does not match it will error, and in some cases (docker media types) it is required. + // So pretty much if we don't have a media type we can fall back to OCI. + // This does have a special fallback for schema1 manifests just because it is easy to detect. + switch mfst.MediaType { + case schema2.MediaTypeManifest, specs.MediaTypeImageManifest: + if mfst.Manifests != nil || mfst.FSLayers != nil { + return "", fmt.Errorf(`media-type: %q should not have "manifests" or "fsLayers"`, mfst.MediaType) + } + return mfst.MediaType, nil + case manifestlist.MediaTypeManifestList, specs.MediaTypeImageIndex: + if mfst.Config != nil || mfst.Layers != nil || mfst.FSLayers != nil { + return "", fmt.Errorf(`media-type: %q should not have "config", "layers", or "fsLayers"`, mfst.MediaType) + } + return mfst.MediaType, nil + case schema1.MediaTypeManifest: + if mfst.Manifests != nil || mfst.Layers != nil { + return "", fmt.Errorf(`media-type: %q should not have "manifests" or "layers"`, mfst.MediaType) + } + return mfst.MediaType, nil + default: + if mfst.MediaType != "" { + return mfst.MediaType, nil + } + } + switch { + case mfst.FSLayers != nil && mfst.Manifests == nil && mfst.Layers == nil && mfst.Config == nil: + return schema1.MediaTypeManifest, nil + case mfst.Config != nil && mfst.Manifests == nil && mfst.FSLayers == nil, + mfst.Layers != nil && mfst.Manifests == nil && mfst.FSLayers == nil: + return specs.MediaTypeImageManifest, nil + case mfst.Config == nil && mfst.Layers == nil && mfst.FSLayers == nil: + // fallback to index + return specs.MediaTypeImageIndex, nil + } + return "", errors.New("media-type: cannot determine") +} diff --git a/distribution/manifest_test.go b/distribution/manifest_test.go new file mode 100644 index 0000000000000..3aac85c5fe44a --- /dev/null +++ b/distribution/manifest_test.go @@ -0,0 +1,422 @@ +package distribution + +import ( + "context" + "encoding/json" + "os" + "strings" + "sync" + "testing" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/content/local" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/remotes" + "github.com/docker/distribution" + "github.com/docker/distribution/manifest/manifestlist" + "github.com/docker/distribution/manifest/ocischema" + "github.com/docker/distribution/manifest/schema1" + "github.com/docker/distribution/manifest/schema2" + "github.com/google/go-cmp/cmp/cmpopts" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" +) + +type mockManifestGetter struct { + manifests map[digest.Digest]distribution.Manifest + gets int +} + +func (m *mockManifestGetter) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) { + m.gets++ + manifest, ok := m.manifests[dgst] + if !ok { + return nil, distribution.ErrManifestUnknown{Tag: dgst.String()} + } + return manifest, nil +} + +type memoryLabelStore struct { + mu sync.Mutex + labels map[digest.Digest]map[string]string +} + +// Get returns all the labels for the given digest +func (s *memoryLabelStore) Get(dgst digest.Digest) (map[string]string, error) { + s.mu.Lock() + labels := s.labels[dgst] + s.mu.Unlock() + return labels, nil +} + +// Set sets all the labels for a given digest +func (s *memoryLabelStore) Set(dgst digest.Digest, labels map[string]string) error { + s.mu.Lock() + if s.labels == nil { + s.labels = make(map[digest.Digest]map[string]string) + } + s.labels[dgst] = labels + s.mu.Unlock() + return nil +} + +// Update replaces the given labels for a digest, +// a key with an empty value removes a label. +func (s *memoryLabelStore) Update(dgst digest.Digest, update map[string]string) (map[string]string, error) { + s.mu.Lock() + defer s.mu.Unlock() + + labels, ok := s.labels[dgst] + if !ok { + labels = map[string]string{} + } + for k, v := range update { + labels[k] = v + } + + s.labels[dgst] = labels + + return labels, nil +} + +type testingContentStoreWrapper struct { + ContentStore + errorOnWriter error + errorOnCommit error +} + +func (s *testingContentStoreWrapper) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) { + if s.errorOnWriter != nil { + return nil, s.errorOnWriter + } + + w, err := s.ContentStore.Writer(ctx, opts...) + if err != nil { + return nil, err + } + + if s.errorOnCommit != nil { + w = &testingContentWriterWrapper{w, s.errorOnCommit} + } + return w, nil +} + +type testingContentWriterWrapper struct { + content.Writer + err error +} + +func (w *testingContentWriterWrapper) Commit(ctx context.Context, size int64, dgst digest.Digest, opts ...content.Opt) error { + if w.err != nil { + // The contract for `Commit` is to always close. + // Since this is returning early before hitting the real `Commit`, we should close it here. + w.Close() + return w.err + } + return w.Writer.Commit(ctx, size, dgst, opts...) +} + +func TestManifestStore(t *testing.T) { + ociManifest := &specs.Manifest{} + serialized, err := json.Marshal(ociManifest) + assert.NilError(t, err) + dgst := digest.Canonical.FromBytes(serialized) + + setupTest := func(t *testing.T) (specs.Descriptor, *mockManifestGetter, *manifestStore, content.Store, func(*testing.T)) { + root, err := os.MkdirTemp("", strings.Replace(t.Name(), "/", "_", -1)) + assert.NilError(t, err) + defer func() { + if t.Failed() { + os.RemoveAll(root) + } + }() + + cs, err := local.NewLabeledStore(root, &memoryLabelStore{}) + assert.NilError(t, err) + + mg := &mockManifestGetter{manifests: make(map[digest.Digest]distribution.Manifest)} + store := &manifestStore{local: cs, remote: mg} + desc := specs.Descriptor{Digest: dgst, MediaType: specs.MediaTypeImageManifest, Size: int64(len(serialized))} + + return desc, mg, store, cs, func(t *testing.T) { + assert.Check(t, os.RemoveAll(root)) + } + } + + ctx := context.Background() + + m, _, err := distribution.UnmarshalManifest(specs.MediaTypeImageManifest, serialized) + assert.NilError(t, err) + + writeManifest := func(t *testing.T, cs ContentStore, desc specs.Descriptor, opts ...content.Opt) { + ingestKey := remotes.MakeRefKey(ctx, desc) + w, err := cs.Writer(ctx, content.WithDescriptor(desc), content.WithRef(ingestKey)) + assert.NilError(t, err) + defer func() { + if err := w.Close(); err != nil { + t.Log(err) + } + if t.Failed() { + if err := cs.Abort(ctx, ingestKey); err != nil { + t.Log(err) + } + } + }() + + _, err = w.Write(serialized) + assert.NilError(t, err) + + err = w.Commit(ctx, desc.Size, desc.Digest, opts...) + assert.NilError(t, err) + + } + + // All tests should end up with no active ingest + checkIngest := func(t *testing.T, cs content.Store, desc specs.Descriptor) { + ingestKey := remotes.MakeRefKey(ctx, desc) + _, err := cs.Status(ctx, ingestKey) + assert.Check(t, errdefs.IsNotFound(err), err) + } + + t.Run("no remote or local", func(t *testing.T) { + desc, _, store, cs, teardown := setupTest(t) + defer teardown(t) + + _, err = store.Get(ctx, desc) + checkIngest(t, cs, desc) + // This error is what our digest getter returns when it doesn't know about the manifest + assert.Error(t, err, distribution.ErrManifestUnknown{Tag: dgst.String()}.Error()) + }) + + t.Run("no local cache", func(t *testing.T) { + desc, mg, store, cs, teardown := setupTest(t) + defer teardown(t) + + mg.manifests[desc.Digest] = m + + m2, err := store.Get(ctx, desc) + checkIngest(t, cs, desc) + assert.NilError(t, err) + assert.Check(t, cmp.DeepEqual(m, m2, cmpopts.IgnoreUnexported(ocischema.DeserializedManifest{}))) + assert.Check(t, cmp.Equal(mg.gets, 1)) + + i, err := cs.Info(ctx, desc.Digest) + assert.NilError(t, err) + assert.Check(t, cmp.Equal(i.Digest, desc.Digest)) + + // Now check again, this should not hit the remote + m2, err = store.Get(ctx, desc) + checkIngest(t, cs, desc) + assert.NilError(t, err) + assert.Check(t, cmp.DeepEqual(m, m2, cmpopts.IgnoreUnexported(ocischema.DeserializedManifest{}))) + assert.Check(t, cmp.Equal(mg.gets, 1)) + }) + + t.Run("with local cache", func(t *testing.T) { + desc, mg, store, cs, teardown := setupTest(t) + defer teardown(t) + + // first add the manifest to the coontent store + writeManifest(t, cs, desc) + + // now do the get + m2, err := store.Get(ctx, desc) + checkIngest(t, cs, desc) + assert.NilError(t, err) + assert.Check(t, cmp.DeepEqual(m, m2, cmpopts.IgnoreUnexported(ocischema.DeserializedManifest{}))) + assert.Check(t, cmp.Equal(mg.gets, 0)) + + i, err := cs.Info(ctx, desc.Digest) + assert.NilError(t, err) + assert.Check(t, cmp.Equal(i.Digest, desc.Digest)) + }) + + // This is for the case of pull by digest where we don't know the media type of the manifest until it's actually pulled. + t.Run("unknown media type", func(t *testing.T) { + t.Run("no cache", func(t *testing.T) { + desc, mg, store, cs, teardown := setupTest(t) + defer teardown(t) + + mg.manifests[desc.Digest] = m + desc.MediaType = "" + + m2, err := store.Get(ctx, desc) + checkIngest(t, cs, desc) + assert.NilError(t, err) + assert.Check(t, cmp.DeepEqual(m, m2, cmpopts.IgnoreUnexported(ocischema.DeserializedManifest{}))) + assert.Check(t, cmp.Equal(mg.gets, 1)) + }) + + t.Run("with cache", func(t *testing.T) { + t.Run("cached manifest has media type", func(t *testing.T) { + desc, mg, store, cs, teardown := setupTest(t) + defer teardown(t) + + writeManifest(t, cs, desc) + desc.MediaType = "" + + m2, err := store.Get(ctx, desc) + checkIngest(t, cs, desc) + assert.NilError(t, err) + assert.Check(t, cmp.DeepEqual(m, m2, cmpopts.IgnoreUnexported(ocischema.DeserializedManifest{}))) + assert.Check(t, cmp.Equal(mg.gets, 0)) + }) + + t.Run("cached manifest has no media type", func(t *testing.T) { + desc, mg, store, cs, teardown := setupTest(t) + defer teardown(t) + + desc.MediaType = "" + writeManifest(t, cs, desc) + + m2, err := store.Get(ctx, desc) + checkIngest(t, cs, desc) + assert.NilError(t, err) + assert.Check(t, cmp.DeepEqual(m, m2, cmpopts.IgnoreUnexported(ocischema.DeserializedManifest{}))) + assert.Check(t, cmp.Equal(mg.gets, 0)) + }) + }) + }) + + // Test that if there is an error with the content store, for whatever + // reason, that doesn't stop us from getting the manifest. + // + // Also makes sure the ingests are aborted. + t.Run("error persisting manifest", func(t *testing.T) { + t.Run("error on writer", func(t *testing.T) { + desc, mg, store, cs, teardown := setupTest(t) + defer teardown(t) + mg.manifests[desc.Digest] = m + + csW := &testingContentStoreWrapper{ContentStore: store.local, errorOnWriter: errors.New("random error")} + store.local = csW + + m2, err := store.Get(ctx, desc) + checkIngest(t, cs, desc) + assert.NilError(t, err) + assert.Check(t, cmp.DeepEqual(m, m2, cmpopts.IgnoreUnexported(ocischema.DeserializedManifest{}))) + assert.Check(t, cmp.Equal(mg.gets, 1)) + + _, err = cs.Info(ctx, desc.Digest) + // Nothing here since we couldn't persist + assert.Check(t, errdefs.IsNotFound(err), err) + }) + + t.Run("error on commit", func(t *testing.T) { + desc, mg, store, cs, teardown := setupTest(t) + defer teardown(t) + mg.manifests[desc.Digest] = m + + csW := &testingContentStoreWrapper{ContentStore: store.local, errorOnCommit: errors.New("random error")} + store.local = csW + + m2, err := store.Get(ctx, desc) + checkIngest(t, cs, desc) + assert.NilError(t, err) + assert.Check(t, cmp.DeepEqual(m, m2, cmpopts.IgnoreUnexported(ocischema.DeserializedManifest{}))) + assert.Check(t, cmp.Equal(mg.gets, 1)) + + _, err = cs.Info(ctx, desc.Digest) + // Nothing here since we couldn't persist + assert.Check(t, errdefs.IsNotFound(err), err) + }) + }) +} + +func TestDetectManifestBlobMediaType(t *testing.T) { + type testCase struct { + json []byte + expected string + } + cases := map[string]testCase{ + "mediaType is set": {[]byte(`{"mediaType": "bananas"}`), "bananas"}, + "oci manifest": {[]byte(`{"config": {}}`), specs.MediaTypeImageManifest}, + "schema1": {[]byte(`{"fsLayers": []}`), schema1.MediaTypeManifest}, + "oci index fallback": {[]byte(`{}`), specs.MediaTypeImageIndex}, + // Make sure we prefer mediaType + "mediaType and config set": {[]byte(`{"mediaType": "bananas", "config": {}}`), "bananas"}, + "mediaType and fsLayers set": {[]byte(`{"mediaType": "bananas", "fsLayers": []}`), "bananas"}, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + mt, err := detectManifestBlobMediaType(tc.json) + assert.NilError(t, err) + assert.Equal(t, mt, tc.expected) + }) + } + +} + +func TestDetectManifestBlobMediaTypeInvalid(t *testing.T) { + type testCase struct { + json []byte + expected string + } + cases := map[string]testCase{ + "schema 1 mediaType with manifests": { + []byte(`{"mediaType": "` + schema1.MediaTypeManifest + `","manifests":[]}`), + `media-type: "application/vnd.docker.distribution.manifest.v1+json" should not have "manifests" or "layers"`, + }, + "schema 1 mediaType with layers": { + []byte(`{"mediaType": "` + schema1.MediaTypeManifest + `","layers":[]}`), + `media-type: "application/vnd.docker.distribution.manifest.v1+json" should not have "manifests" or "layers"`, + }, + "schema 2 mediaType with manifests": { + []byte(`{"mediaType": "` + schema2.MediaTypeManifest + `","manifests":[]}`), + `media-type: "application/vnd.docker.distribution.manifest.v2+json" should not have "manifests" or "fsLayers"`, + }, + "schema 2 mediaType with fsLayers": { + []byte(`{"mediaType": "` + schema2.MediaTypeManifest + `","fsLayers":[]}`), + `media-type: "application/vnd.docker.distribution.manifest.v2+json" should not have "manifests" or "fsLayers"`, + }, + "oci manifest mediaType with manifests": { + []byte(`{"mediaType": "` + specs.MediaTypeImageManifest + `","manifests":[]}`), + `media-type: "application/vnd.oci.image.manifest.v1+json" should not have "manifests" or "fsLayers"`, + }, + "manifest list mediaType with fsLayers": { + []byte(`{"mediaType": "` + manifestlist.MediaTypeManifestList + `","fsLayers":[]}`), + `media-type: "application/vnd.docker.distribution.manifest.list.v2+json" should not have "config", "layers", or "fsLayers"`, + }, + "index mediaType with layers": { + []byte(`{"mediaType": "` + specs.MediaTypeImageIndex + `","layers":[]}`), + `media-type: "application/vnd.oci.image.index.v1+json" should not have "config", "layers", or "fsLayers"`, + }, + "index mediaType with config": { + []byte(`{"mediaType": "` + specs.MediaTypeImageIndex + `","config":{}}`), + `media-type: "application/vnd.oci.image.index.v1+json" should not have "config", "layers", or "fsLayers"`, + }, + "config and manifests": { + []byte(`{"config":{}, "manifests":[]}`), + `media-type: cannot determine`, + }, + "layers and manifests": { + []byte(`{"layers":[], "manifests":[]}`), + `media-type: cannot determine`, + }, + "layers and fsLayers": { + []byte(`{"layers":[], "fsLayers":[]}`), + `media-type: cannot determine`, + }, + "fsLayers and manifests": { + []byte(`{"fsLayers":[], "manifests":[]}`), + `media-type: cannot determine`, + }, + "config and fsLayers": { + []byte(`{"config":{}, "fsLayers":[]}`), + `media-type: cannot determine`, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + mt, err := detectManifestBlobMediaType(tc.json) + assert.Error(t, err, tc.expected) + assert.Equal(t, mt, "") + }) + } + +} diff --git a/distribution/metadata/metadata.go b/distribution/metadata/metadata.go index 4ae8223bd0541..88c859572dfaa 100644 --- a/distribution/metadata/metadata.go +++ b/distribution/metadata/metadata.go @@ -1,7 +1,6 @@ package metadata // import "github.com/docker/docker/distribution/metadata" import ( - "io/ioutil" "os" "path/filepath" "sync" @@ -48,7 +47,7 @@ func (store *FSMetadataStore) Get(namespace string, key string) ([]byte, error) store.RLock() defer store.RUnlock() - return ioutil.ReadFile(store.path(namespace, key)) + return os.ReadFile(store.path(namespace, key)) } // Set writes data indexed by namespace and key. The data is written to a file diff --git a/distribution/metadata/v1_id_service.go b/distribution/metadata/v1_id_service.go index 5575c59b0e9a7..86c83f62ebf11 100644 --- a/distribution/metadata/v1_id_service.go +++ b/distribution/metadata/v1_id_service.go @@ -1,7 +1,7 @@ package metadata // import "github.com/docker/docker/distribution/metadata" import ( - "github.com/docker/docker/image/v1" + v1 "github.com/docker/docker/image/v1" "github.com/docker/docker/layer" "github.com/pkg/errors" ) diff --git a/distribution/metadata/v1_id_service_test.go b/distribution/metadata/v1_id_service_test.go index 5003897cbb92d..a548733e153f7 100644 --- a/distribution/metadata/v1_id_service_test.go +++ b/distribution/metadata/v1_id_service_test.go @@ -1,16 +1,15 @@ package metadata // import "github.com/docker/docker/distribution/metadata" import ( - "io/ioutil" "os" "testing" "github.com/docker/docker/layer" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestV1IDService(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "v1-id-service-test") + tmpDir, err := os.MkdirTemp("", "v1-id-service-test") if err != nil { t.Fatalf("could not create temp dir: %v", err) } diff --git a/distribution/metadata/v2_metadata_service.go b/distribution/metadata/v2_metadata_service.go index fe33498554148..482d2224ea54c 100644 --- a/distribution/metadata/v2_metadata_service.go +++ b/distribution/metadata/v2_metadata_service.go @@ -9,7 +9,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/layer" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" ) // V2MetadataService maps layer IDs to a set of known metadata for diff --git a/distribution/metadata/v2_metadata_service_test.go b/distribution/metadata/v2_metadata_service_test.go index cf24e0d85b637..ca54eaed98574 100644 --- a/distribution/metadata/v2_metadata_service_test.go +++ b/distribution/metadata/v2_metadata_service_test.go @@ -2,18 +2,17 @@ package metadata // import "github.com/docker/docker/distribution/metadata" import ( "encoding/hex" - "io/ioutil" "math/rand" "os" "reflect" "testing" "github.com/docker/docker/layer" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" ) func TestV2MetadataService(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "blobsum-storage-service-test") + tmpDir, err := os.MkdirTemp("", "blobsum-storage-service-test") if err != nil { t.Fatalf("could not create temp dir: %v", err) } diff --git a/distribution/oci.go b/distribution/oci.go deleted file mode 100644 index 7366546d09611..0000000000000 --- a/distribution/oci.go +++ /dev/null @@ -1,45 +0,0 @@ -package distribution - -import ( - "fmt" - - "github.com/docker/distribution" - "github.com/docker/distribution/manifest/manifestlist" - "github.com/docker/distribution/manifest/schema2" - digest "github.com/opencontainers/go-digest" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" -) - -func init() { - // TODO: Remove this registration if distribution is included with OCI support - - ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) { - m := new(schema2.DeserializedManifest) - err := m.UnmarshalJSON(b) - if err != nil { - return nil, distribution.Descriptor{}, err - } - - dgst := digest.FromBytes(b) - return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: ocispec.MediaTypeImageManifest}, err - } - err := distribution.RegisterManifestSchema(ocispec.MediaTypeImageManifest, ocischemaFunc) - if err != nil { - panic(fmt.Sprintf("Unable to register manifest: %s", err)) - } - - manifestListFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) { - m := new(manifestlist.DeserializedManifestList) - err := m.UnmarshalJSON(b) - if err != nil { - return nil, distribution.Descriptor{}, err - } - - dgst := digest.FromBytes(b) - return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: ocispec.MediaTypeImageIndex}, err - } - err = distribution.RegisterManifestSchema(ocispec.MediaTypeImageIndex, manifestListFunc) - if err != nil { - panic(fmt.Sprintf("Unable to register manifest: %s", err)) - } -} diff --git a/distribution/pull.go b/distribution/pull.go index 5de73ae99ac39..829c5fed6a3a2 100644 --- a/distribution/pull.go +++ b/distribution/pull.go @@ -10,8 +10,7 @@ import ( "github.com/docker/docker/pkg/progress" refstore "github.com/docker/docker/reference" "github.com/docker/docker/registry" - "github.com/opencontainers/go-digest" - specs "github.com/opencontainers/image-spec/specs-go/v1" + digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -21,15 +20,11 @@ type Puller interface { // Pull tries to pull the image referenced by `tag` // Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint. // - Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) error + Pull(ctx context.Context, ref reference.Named) error } -// newPuller returns a Puller interface that will pull from either a v1 or v2 -// registry. The endpoint argument contains a Version field that determines -// whether a v1 or v2 puller will be created. The other parameters are passed -// through to the underlying puller implementation for use during the actual -// pull operation. -func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig) (Puller, error) { +// newPuller returns a Puller interface that will pull from a v2 registry. +func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, imagePullConfig *ImagePullConfig, local ContentStore) (Puller, error) { switch endpoint.Version { case registry.APIVersion2: return &v2Puller{ @@ -37,21 +32,19 @@ func newPuller(endpoint registry.APIEndpoint, repoInfo *registry.RepositoryInfo, endpoint: endpoint, config: imagePullConfig, repoInfo: repoInfo, + manifestStore: &manifestStore{ + local: local, + }, }, nil case registry.APIVersion1: - return &v1Puller{ - v1IDService: metadata.NewV1IDService(imagePullConfig.MetadataStore), - endpoint: endpoint, - config: imagePullConfig, - repoInfo: repoInfo, - }, nil + return nil, fmt.Errorf("protocol version %d no longer supported. Please contact admins of registry %s", endpoint.Version, endpoint.URL) } return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL) } // Pull initiates a pull operation. image is the repository name to pull, and // tag may be either empty, or indicate a specific tag to pull. -func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullConfig) error { +func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullConfig, local ContentStore) error { // Resolve the Repository name from fqn to RepositoryInfo repoInfo, err := imagePullConfig.RegistryService.ResolveRepository(ref) if err != nil { @@ -80,26 +73,12 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo // error is the ones from v2 endpoints not v1. discardNoSupportErrors bool - // confirmedV2 is set to true if a pull attempt managed to - // confirm that it was talking to a v2 registry. This will - // prevent fallback to the v1 protocol. - confirmedV2 bool - // confirmedTLSRegistries is a map indicating which registries // are known to be using TLS. There should never be a plaintext // retry for any of these. confirmedTLSRegistries = make(map[string]struct{}) ) for _, endpoint := range endpoints { - if imagePullConfig.RequireSchema2 && endpoint.Version == registry.APIVersion1 { - continue - } - - if confirmedV2 && endpoint.Version == registry.APIVersion1 { - logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL) - continue - } - if endpoint.URL.Scheme != "https" { if _, confirmedTLS := confirmedTLSRegistries[endpoint.URL.Host]; confirmedTLS { logrus.Debugf("Skipping non-TLS endpoint %s for host/port that appears to use TLS", endpoint.URL) @@ -109,13 +88,13 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo logrus.Debugf("Trying to pull %s from %s %s", reference.FamiliarName(repoInfo.Name), endpoint.URL, endpoint.Version) - puller, err := newPuller(endpoint, repoInfo, imagePullConfig) + puller, err := newPuller(endpoint, repoInfo, imagePullConfig, local) if err != nil { lastErr = err continue } - if err := puller.Pull(ctx, ref, imagePullConfig.Platform); err != nil { + if err := puller.Pull(ctx, ref); err != nil { // Was this pull cancelled? If so, don't try to fall // back. fallback := false @@ -124,7 +103,6 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo default: if fallbackErr, ok := err.(fallbackError); ok { fallback = true - confirmedV2 = confirmedV2 || fallbackErr.confirmedV2 if fallbackErr.transportOK && endpoint.URL.Scheme == "https" { confirmedTLSRegistries[endpoint.URL.Host] = struct{}{} } diff --git a/distribution/pull_v1.go b/distribution/pull_v1.go deleted file mode 100644 index c2c897dc1c31c..0000000000000 --- a/distribution/pull_v1.go +++ /dev/null @@ -1,368 +0,0 @@ -package distribution // import "github.com/docker/docker/distribution" - -import ( - "context" - "errors" - "fmt" - "io" - "io/ioutil" - "net" - "net/url" - "os" - "strings" - "time" - - "github.com/docker/distribution/reference" - "github.com/docker/distribution/registry/client/auth" - "github.com/docker/distribution/registry/client/transport" - "github.com/docker/docker/distribution/metadata" - "github.com/docker/docker/distribution/xfer" - "github.com/docker/docker/dockerversion" - "github.com/docker/docker/image" - "github.com/docker/docker/image/v1" - "github.com/docker/docker/layer" - "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/progress" - "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/registry" - specs "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/sirupsen/logrus" -) - -type v1Puller struct { - v1IDService *metadata.V1IDService - endpoint registry.APIEndpoint - config *ImagePullConfig - repoInfo *registry.RepositoryInfo - session *registry.Session -} - -func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, _ *specs.Platform) error { - if _, isCanonical := ref.(reference.Canonical); isCanonical { - // Allowing fallback, because HTTPS v1 is before HTTP v2 - return fallbackError{err: ErrNoSupport{Err: errors.New("Cannot pull by digest with v1 registry")}} - } - - tlsConfig, err := p.config.RegistryService.TLSConfig(p.repoInfo.Index.Name) - if err != nil { - return err - } - // Adds Docker-specific headers as well as user-specified headers (metaHeaders) - tr := transport.NewTransport( - // TODO(tiborvass): was ReceiveTimeout - registry.NewTransport(tlsConfig), - registry.Headers(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)..., - ) - client := registry.HTTPClient(tr) - v1Endpoint := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders) - p.session, err = registry.NewSession(client, p.config.AuthConfig, v1Endpoint) - if err != nil { - // TODO(dmcgowan): Check if should fallback - logrus.Debugf("Fallback from error: %s", err) - return fallbackError{err: err} - } - if err := p.pullRepository(ctx, ref); err != nil { - // TODO(dmcgowan): Check if should fallback - return err - } - progress.Message(p.config.ProgressOutput, "", p.repoInfo.Name.Name()+": this image was pulled from a legacy registry. Important: This registry version will not be supported in future versions of docker.") - - return nil -} - -// Note use auth.Scope rather than reference.Named due to this warning causing Jenkins CI to fail: -// warning: ref can be github.com/docker/docker/vendor/github.com/docker/distribution/registry/client/auth.Scope (interfacer) -func (p *v1Puller) pullRepository(ctx context.Context, ref auth.Scope) error { - progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.Name.Name()) - - tagged, isTagged := ref.(reference.NamedTagged) - - repoData, err := p.session.GetRepositoryData(p.repoInfo.Name) - if err != nil { - if strings.Contains(err.Error(), "HTTP code: 404") { - if isTagged { - return fmt.Errorf("Error: image %s:%s not found", reference.Path(p.repoInfo.Name), tagged.Tag()) - } - return fmt.Errorf("Error: image %s not found", reference.Path(p.repoInfo.Name)) - } - // Unexpected HTTP error - return err - } - - logrus.Debug("Retrieving the tag list") - var tagsList map[string]string - if !isTagged { - tagsList, err = p.session.GetRemoteTags(repoData.Endpoints, p.repoInfo.Name) - } else { - var tagID string - tagsList = make(map[string]string) - tagID, err = p.session.GetRemoteTag(repoData.Endpoints, p.repoInfo.Name, tagged.Tag()) - if err == registry.ErrRepoNotFound { - return fmt.Errorf("Tag %s not found in repository %s", tagged.Tag(), p.repoInfo.Name.Name()) - } - tagsList[tagged.Tag()] = tagID - } - if err != nil { - logrus.Errorf("unable to get remote tags: %s", err) - return err - } - - for tag, id := range tagsList { - repoData.ImgList[id] = ®istry.ImgData{ - ID: id, - Tag: tag, - Checksum: "", - } - } - - layersDownloaded := false - for _, imgData := range repoData.ImgList { - if isTagged && imgData.Tag != tagged.Tag() { - continue - } - - err := p.downloadImage(ctx, repoData, imgData, &layersDownloaded) - if err != nil { - return err - } - } - - writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded) - return nil -} - -func (p *v1Puller) downloadImage(ctx context.Context, repoData *registry.RepositoryData, img *registry.ImgData, layersDownloaded *bool) error { - if img.Tag == "" { - logrus.Debugf("Image (id: %s) present in this repository but untagged, skipping", img.ID) - return nil - } - - localNameRef, err := reference.WithTag(p.repoInfo.Name, img.Tag) - if err != nil { - retErr := fmt.Errorf("Image (id: %s) has invalid tag: %s", img.ID, img.Tag) - logrus.Debug(retErr.Error()) - return retErr - } - - if err := v1.ValidateID(img.ID); err != nil { - return err - } - - progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s", img.Tag, p.repoInfo.Name.Name()) - success := false - var lastErr error - for _, ep := range p.repoInfo.Index.Mirrors { - ep += "v1/" - progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, p.repoInfo.Name.Name(), ep)) - if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil { - // Don't report errors when pulling from mirrors. - logrus.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err) - continue - } - success = true - break - } - if !success { - for _, ep := range repoData.Endpoints { - progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Pulling image (%s) from %s, endpoint: %s", img.Tag, p.repoInfo.Name.Name(), ep) - if err = p.pullImage(ctx, img.ID, ep, localNameRef, layersDownloaded); err != nil { - // It's not ideal that only the last error is returned, it would be better to concatenate the errors. - // As the error is also given to the output stream the user will see the error. - lastErr = err - progress.Updatef(p.config.ProgressOutput, stringid.TruncateID(img.ID), "Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, p.repoInfo.Name.Name(), ep, err) - continue - } - success = true - break - } - } - if !success { - err := fmt.Errorf("Error pulling image (%s) from %s, %v", img.Tag, p.repoInfo.Name.Name(), lastErr) - progress.Update(p.config.ProgressOutput, stringid.TruncateID(img.ID), err.Error()) - return err - } - return nil -} - -func (p *v1Puller) pullImage(ctx context.Context, v1ID, endpoint string, localNameRef reference.Named, layersDownloaded *bool) (err error) { - var history []string - history, err = p.session.GetRemoteHistory(v1ID, endpoint) - if err != nil { - return err - } - if len(history) < 1 { - return fmt.Errorf("empty history for image %s", v1ID) - } - progress.Update(p.config.ProgressOutput, stringid.TruncateID(v1ID), "Pulling dependent layers") - - var ( - descriptors []xfer.DownloadDescriptor - newHistory []image.History - imgJSON []byte - imgSize int64 - ) - - // Iterate over layers, in order from bottom-most to top-most. Download - // config for all layers and create descriptors. - for i := len(history) - 1; i >= 0; i-- { - v1LayerID := history[i] - imgJSON, imgSize, err = p.downloadLayerConfig(v1LayerID, endpoint) - if err != nil { - return err - } - - // Create a new-style config from the legacy configs - h, err := v1.HistoryFromConfig(imgJSON, false) - if err != nil { - return err - } - newHistory = append(newHistory, h) - - layerDescriptor := &v1LayerDescriptor{ - v1LayerID: v1LayerID, - indexName: p.repoInfo.Index.Name, - endpoint: endpoint, - v1IDService: p.v1IDService, - layersDownloaded: layersDownloaded, - layerSize: imgSize, - session: p.session, - } - - descriptors = append(descriptors, layerDescriptor) - } - - rootFS := image.NewRootFS() - resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, "", descriptors, p.config.ProgressOutput) - if err != nil { - return err - } - defer release() - - config, err := v1.MakeConfigFromV1Config(imgJSON, &resultRootFS, newHistory) - if err != nil { - return err - } - - imageID, err := p.config.ImageStore.Put(config) - if err != nil { - return err - } - - if p.config.ReferenceStore != nil { - if err := p.config.ReferenceStore.AddTag(localNameRef, imageID, true); err != nil { - return err - } - } - - return nil -} - -func (p *v1Puller) downloadLayerConfig(v1LayerID, endpoint string) (imgJSON []byte, imgSize int64, err error) { - progress.Update(p.config.ProgressOutput, stringid.TruncateID(v1LayerID), "Pulling metadata") - - retries := 5 - for j := 1; j <= retries; j++ { - imgJSON, imgSize, err := p.session.GetRemoteImageJSON(v1LayerID, endpoint) - if err != nil && j == retries { - progress.Update(p.config.ProgressOutput, stringid.TruncateID(v1LayerID), "Error pulling layer metadata") - return nil, 0, err - } else if err != nil { - time.Sleep(time.Duration(j) * 500 * time.Millisecond) - continue - } - - return imgJSON, imgSize, nil - } - - // not reached - return nil, 0, nil -} - -type v1LayerDescriptor struct { - v1LayerID string - indexName string - endpoint string - v1IDService *metadata.V1IDService - layersDownloaded *bool - layerSize int64 - session *registry.Session - tmpFile *os.File -} - -func (ld *v1LayerDescriptor) Key() string { - return "v1:" + ld.v1LayerID -} - -func (ld *v1LayerDescriptor) ID() string { - return stringid.TruncateID(ld.v1LayerID) -} - -func (ld *v1LayerDescriptor) DiffID() (layer.DiffID, error) { - return ld.v1IDService.Get(ld.v1LayerID, ld.indexName) -} - -func (ld *v1LayerDescriptor) Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) { - progress.Update(progressOutput, ld.ID(), "Pulling fs layer") - layerReader, err := ld.session.GetRemoteImageLayer(ld.v1LayerID, ld.endpoint, ld.layerSize) - if err != nil { - progress.Update(progressOutput, ld.ID(), "Error pulling dependent layers") - if uerr, ok := err.(*url.Error); ok { - err = uerr.Err - } - if terr, ok := err.(net.Error); ok && terr.Timeout() { - return nil, 0, err - } - return nil, 0, xfer.DoNotRetry{Err: err} - } - *ld.layersDownloaded = true - - ld.tmpFile, err = ioutil.TempFile("", "GetImageBlob") - if err != nil { - layerReader.Close() - return nil, 0, err - } - - reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(ctx, layerReader), progressOutput, ld.layerSize, ld.ID(), "Downloading") - defer reader.Close() - - _, err = io.Copy(ld.tmpFile, reader) - if err != nil { - ld.Close() - return nil, 0, err - } - - progress.Update(progressOutput, ld.ID(), "Download complete") - - logrus.Debugf("Downloaded %s to tempfile %s", ld.ID(), ld.tmpFile.Name()) - - ld.tmpFile.Seek(0, 0) - - // hand off the temporary file to the download manager, so it will only - // be closed once - tmpFile := ld.tmpFile - ld.tmpFile = nil - - return ioutils.NewReadCloserWrapper(tmpFile, func() error { - tmpFile.Close() - err := os.RemoveAll(tmpFile.Name()) - if err != nil { - logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name()) - } - return err - }), ld.layerSize, nil -} - -func (ld *v1LayerDescriptor) Close() { - if ld.tmpFile != nil { - ld.tmpFile.Close() - if err := os.RemoveAll(ld.tmpFile.Name()); err != nil { - logrus.Errorf("Failed to remove temp file: %s", ld.tmpFile.Name()) - } - ld.tmpFile = nil - } -} - -func (ld *v1LayerDescriptor) Registered(diffID layer.DiffID) { - // Cache mapping from this layer's DiffID to the blobsum - ld.v1IDService.Set(ld.v1LayerID, ld.indexName, diffID) -} diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 8f05cfa0b289b..296f47372be07 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -5,25 +5,22 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" - "net/url" "os" "runtime" - "strings" + "github.com/containerd/containerd/log" "github.com/containerd/containerd/platforms" "github.com/docker/distribution" "github.com/docker/distribution/manifest/manifestlist" + "github.com/docker/distribution/manifest/ocischema" "github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/reference" - "github.com/docker/distribution/registry/api/errcode" - "github.com/docker/distribution/registry/client/auth" "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/distribution/metadata" "github.com/docker/docker/distribution/xfer" "github.com/docker/docker/image" - "github.com/docker/docker/image/v1" + v1 "github.com/docker/docker/image/v1" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/progress" @@ -31,7 +28,7 @@ import ( "github.com/docker/docker/pkg/system" refstore "github.com/docker/docker/reference" "github.com/docker/docker/registry" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -59,27 +56,29 @@ type v2Puller struct { config *ImagePullConfig repoInfo *registry.RepositoryInfo repo distribution.Repository - // confirmedV2 is set to true if we confirm we're talking to a v2 - // registry. This is used to limit fallbacks to the v1 protocol. - confirmedV2 bool + manifestStore *manifestStore } -func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) { +func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) { // TODO(tiborvass): was ReceiveTimeout - p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") + p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { logrus.Warnf("Error getting v2 registry: %v", err) return err } - if err = p.pullV2Repository(ctx, ref, platform); err != nil { + p.manifestStore.remote, err = p.repo.Manifests(ctx) + if err != nil { + return err + } + + if err = p.pullV2Repository(ctx, ref); err != nil { if _, ok := err.(fallbackError); ok { return err } if continueOnError(err, p.endpoint.Mirror) { return fallbackError{ err: err, - confirmedV2: p.confirmedV2, transportOK: true, } } @@ -87,32 +86,25 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform *spec return err } -func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) { +func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) { var layersDownloaded bool if !reference.IsNameOnly(ref) { - layersDownloaded, err = p.pullV2Tag(ctx, ref, platform) + layersDownloaded, err = p.pullV2Tag(ctx, ref, p.config.Platform) if err != nil { return err } } else { tags, err := p.repo.Tags(ctx).All(ctx) if err != nil { - // If this repository doesn't exist on V2, we should - // permit a fallback to V1. - return allowV1Fallback(err) + return err } - // The v2 registry knows about this repository, so we will not - // allow fallback to the v1 protocol even if we encounter an - // error later on. - p.confirmedV2 = true - for _, tag := range tags { tagRef, err := reference.WithTag(ref, tag) if err != nil { return err } - pulledNew, err := p.pullV2Tag(ctx, tagRef, platform) + pulledNew, err := p.pullV2Tag(ctx, tagRef, p.config.Platform) if err != nil { // Since this is the pull-all-tags case, don't // allow an error pulling a particular tag to @@ -173,7 +165,7 @@ func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progre return nil, 0, xfer.DoNotRetry{Err: err} } } else { - offset, err = ld.tmpFile.Seek(0, os.SEEK_END) + offset, err = ld.tmpFile.Seek(0, io.SeekEnd) if err != nil { logrus.Debugf("error seeking to end of download file: %v", err) offset = 0 @@ -200,7 +192,7 @@ func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progre } if offset != 0 { - _, err := layerDownload.Seek(offset, os.SEEK_SET) + _, err := layerDownload.Seek(offset, io.SeekStart) if err != nil { if err := ld.truncateDownloadFile(); err != nil { return nil, 0, xfer.DoNotRetry{Err: err} @@ -208,7 +200,7 @@ func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progre return nil, 0, err } } - size, err := layerDownload.Seek(0, os.SEEK_END) + size, err := layerDownload.Seek(0, io.SeekEnd) if err != nil { // Seek failed, perhaps because there was no Content-Length // header. This shouldn't fail the download, because we can @@ -226,7 +218,7 @@ func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progre // Restore the seek offset either at the beginning of the // stream, or just after the last byte we have from previous // attempts. - _, err = layerDownload.Seek(offset, os.SEEK_SET) + _, err = layerDownload.Seek(offset, io.SeekStart) if err != nil { return nil, 0, err } @@ -272,7 +264,7 @@ func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progre logrus.Debugf("Downloaded %s to tempfile %s", ld.ID(), tmpFile.Name()) - _, err = tmpFile.Seek(0, os.SEEK_SET) + _, err = tmpFile.Seek(0, io.SeekStart) if err != nil { tmpFile.Close() if err := os.Remove(tmpFile.Name()); err != nil { @@ -310,7 +302,7 @@ func (ld *v2LayerDescriptor) truncateDownloadFile() error { // Need a new hash context since we will be redoing the download ld.verifier = nil - if _, err := ld.tmpFile.Seek(0, os.SEEK_SET); err != nil { + if _, err := ld.tmpFile.Seek(0, io.SeekStart); err != nil { logrus.Errorf("error seeking to beginning of download file: %v", err) return err } @@ -329,31 +321,75 @@ func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) { } func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform *specs.Platform) (tagUpdated bool, err error) { - manSvc, err := p.repo.Manifests(ctx) - if err != nil { - return false, err - } var ( - manifest distribution.Manifest tagOrDigest string // Used for logging/progress only + dgst digest.Digest + mt string + size int64 + tagged reference.NamedTagged + isTagged bool ) if digested, isDigested := ref.(reference.Canonical); isDigested { - manifest, err = manSvc.Get(ctx, digested.Digest()) + dgst = digested.Digest() + tagOrDigest = digested.String() + } else if tagged, isTagged = ref.(reference.NamedTagged); isTagged { + tagService := p.repo.Tags(ctx) + desc, err := tagService.Get(ctx, tagged.Tag()) if err != nil { return false, err } - tagOrDigest = digested.Digest().String() - } else if tagged, isTagged := ref.(reference.NamedTagged); isTagged { - manifest, err = manSvc.Get(ctx, "", distribution.WithTag(tagged.Tag())) - if err != nil { - return false, allowV1Fallback(err) - } + + dgst = desc.Digest tagOrDigest = tagged.Tag() + mt = desc.MediaType + size = desc.Size } else { return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", reference.FamiliarString(ref)) } + ctx = log.WithLogger(ctx, logrus.WithFields( + logrus.Fields{ + "digest": dgst, + "remote": ref, + })) + + desc := specs.Descriptor{ + MediaType: mt, + Digest: dgst, + Size: size, + } + manifest, err := p.manifestStore.Get(ctx, desc) + if err != nil { + if isTagged && isNotFound(errors.Cause(err)) { + logrus.WithField("ref", ref).WithError(err).Debug("Falling back to pull manifest by tag") + + msg := `%s Failed to pull manifest by the resolved digest. This registry does not + appear to conform to the distribution registry specification; falling back to + pull by tag. This fallback is DEPRECATED, and will be removed in a future + release. Please contact admins of %s. %s +` + + warnEmoji := "\U000026A0\U0000FE0F" + progress.Messagef(p.config.ProgressOutput, "WARNING", msg, warnEmoji, p.endpoint.URL, warnEmoji) + + // Fetch by tag worked, but fetch by digest didn't. + // This is a broken registry implementation. + // We'll fallback to the old behavior and get the manifest by tag. + var ms distribution.ManifestService + ms, err = p.repo.Manifests(ctx) + if err != nil { + return false, err + } + + manifest, err = ms.Get(ctx, "", distribution.WithTag(tagged.Tag())) + err = errors.Wrap(err, "error after falling back to get manifest by tag") + } + if err != nil { + return false, err + } + } + if manifest == nil { return false, fmt.Errorf("image manifest does not exist for tag or digest %q", tagOrDigest) } @@ -375,10 +411,6 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform } } - // If manSvc.Get succeeded, we can be confident that the registry on - // the other side speaks the v2 protocol. - p.confirmedV2 = true - logrus.Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref)) progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named())) @@ -392,6 +424,15 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform if p.config.RequireSchema2 { return false, fmt.Errorf("invalid manifest: not schema2") } + + // give registries time to upgrade to schema2 and only warn if we know a registry has been upgraded long time ago + // TODO: condition to be removed + if reference.Domain(ref) == "docker.io" { + msg := fmt.Sprintf("Image %s uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/", ref) + logrus.Warn(msg) + progress.Message(p.config.ProgressOutput, "", msg) + } + id, manifestDigest, err = p.pullSchema1(ctx, ref, v, platform) if err != nil { return false, err @@ -401,6 +442,11 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform if err != nil { return false, err } + case *ocischema.DeserializedManifest: + id, manifestDigest, err = p.pullOCI(ctx, ref, v, platform) + if err != nil { + return false, err + } case *manifestlist.DeserializedManifestList: id, manifestDigest, err = p.pullManifestList(ctx, ref, v, platform) if err != nil { @@ -439,6 +485,14 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform } func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { + if platform != nil { + // Early bath if the requested OS doesn't match that of the configuration. + // This avoids doing the download, only to potentially fail later. + if !system.IsOSSupported(platform.OS) { + return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", runtime.GOOS, platform.OS) + } + } + var verifiedManifest *schema1.Manifest verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref) if err != nil { @@ -462,6 +516,9 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv // to top-most, so that the downloads slice gets ordered correctly. for i := len(verifiedManifest.FSLayers) - 1; i >= 0; i-- { blobSum := verifiedManifest.FSLayers[i].BlobSum + if err = blobSum.Validate(); err != nil { + return "", "", errors.Wrapf(err, "could not validate layer digest %q", blobSum) + } var throwAway struct { ThrowAway bool `json:"throwaway,omitempty"` @@ -490,44 +547,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv descriptors = append(descriptors, layerDescriptor) } - // The v1 manifest itself doesn't directly contain an OS. However, - // the history does, but unfortunately that's a string, so search through - // all the history until hopefully we find one which indicates the OS. - // supertest2014/nyan is an example of a registry image with schemav1. - configOS := runtime.GOOS - if system.LCOWSupported() { - type config struct { - Os string `json:"os,omitempty"` - } - for _, v := range verifiedManifest.History { - var c config - if err := json.Unmarshal([]byte(v.V1Compatibility), &c); err == nil { - if c.Os != "" { - configOS = c.Os - break - } - } - } - } - - // In the situation that the API call didn't specify an OS explicitly, but - // we support the operating system, switch to that operating system. - // eg FROM supertest2014/nyan with no platform specifier, and docker build - // with no --platform= flag under LCOW. - requestedOS := "" - if platform != nil { - requestedOS = platform.OS - } else if system.IsOSSupported(configOS) { - requestedOS = configOS - } - - // Early bath if the requested OS doesn't match that of the configuration. - // This avoids doing the download, only to potentially fail later. - if !strings.EqualFold(configOS, requestedOS) { - return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS) - } - - resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, configOS, descriptors, p.config.ProgressOutput) + resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, runtime.GOOS, descriptors, p.config.ProgressOutput) if err != nil { return "", "", err } @@ -538,7 +558,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv return "", "", err } - imageID, err := p.config.ImageStore.Put(config) + imageID, err := p.config.ImageStore.Put(ctx, config) if err != nil { return "", "", err } @@ -548,24 +568,21 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv return imageID, manifestDigest, nil } -func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { - manifestDigest, err = schema2ManifestDigest(ref, mfst) - if err != nil { - return "", "", err - } - - target := mfst.Target() - if _, err := p.config.ImageStore.Get(target.Digest); err == nil { +func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.Descriptor, layers []distribution.Descriptor, platform *specs.Platform) (id digest.Digest, err error) { + if _, err := p.config.ImageStore.Get(ctx, target.Digest); err == nil { // If the image already exists locally, no need to pull // anything. - return target.Digest, manifestDigest, nil + return target.Digest, nil } var descriptors []xfer.DownloadDescriptor // Note that the order of this loop is in the direction of bottom-most // to top-most, so that the downloads slice gets ordered correctly. - for _, d := range mfst.Layers { + for _, d := range layers { + if err := d.Digest.Validate(); err != nil { + return "", errors.Wrapf(err, "could not validate layer digest %q", d.Digest) + } layerDescriptor := &v2LayerDescriptor{ digest: d.Digest, repo: p.repo, @@ -620,23 +637,23 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s if runtime.GOOS == "windows" { configJSON, configRootFS, configPlatform, err = receiveConfig(p.config.ImageStore, configChan, configErrChan) if err != nil { - return "", "", err + return "", err } if configRootFS == nil { - return "", "", errRootFSInvalid + return "", errRootFSInvalid } if err := checkImageCompatibility(configPlatform.OS, configPlatform.OSVersion); err != nil { - return "", "", err + return "", err } if len(descriptors) != len(configRootFS.DiffIDs) { - return "", "", errRootFSMismatch + return "", errRootFSMismatch } if platform == nil { // Early bath if the requested OS doesn't match that of the configuration. // This avoids doing the download, only to potentially fail later. if !system.IsOSSupported(configPlatform.OS) { - return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, layerStoreOS) + return "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, layerStoreOS) } layerStoreOS = configPlatform.OS } @@ -683,14 +700,14 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s case <-downloadsDone: case <-layerErrChan: } - return "", "", err + return "", err } } select { case <-downloadsDone: case err = <-layerErrChan: - return "", "", err + return "", err } if release != nil { @@ -702,22 +719,40 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s // Otherwise the image config could be referencing layers that aren't // included in the manifest. if len(downloadedRootFS.DiffIDs) != len(configRootFS.DiffIDs) { - return "", "", errRootFSMismatch + return "", errRootFSMismatch } for i := range downloadedRootFS.DiffIDs { if downloadedRootFS.DiffIDs[i] != configRootFS.DiffIDs[i] { - return "", "", errRootFSMismatch + return "", errRootFSMismatch } } } - imageID, err := p.config.ImageStore.Put(configJSON) + imageID, err := p.config.ImageStore.Put(ctx, configJSON) + if err != nil { + return "", err + } + + return imageID, nil +} + +func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { + manifestDigest, err = schema2ManifestDigest(ref, mfst) if err != nil { return "", "", err } + id, err = p.pullSchema2Layers(ctx, mfst.Target(), mfst.Layers, platform) + return id, manifestDigest, err +} - return imageID, manifestDigest, nil +func (p *v2Puller) pullOCI(ctx context.Context, ref reference.Named, mfst *ocischema.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) { + manifestDigest, err = schema2ManifestDigest(ref, mfst) + if err != nil { + return "", "", err + } + id, err = p.pullSchema2Layers(ctx, mfst.Target(), mfst.Layers, platform) + return id, manifestDigest, err } func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, *specs.Platform, error) { @@ -756,7 +791,7 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf manifestMatches := filterManifests(mfstList.Manifests, platform) if len(manifestMatches) == 0 { - errMsg := fmt.Sprintf("no matching manifest for %s in the manifest list entries", platforms.Format(platform)) + errMsg := fmt.Sprintf("no matching manifest for %s in the manifest list entries", formatPlatform(platform)) logrus.Debugf(errMsg) return "", "", errors.New(errMsg) } @@ -764,29 +799,33 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf if len(manifestMatches) > 1 { logrus.Debugf("found multiple matches in manifest list, choosing best match %s", manifestMatches[0].Digest.String()) } - manifestDigest := manifestMatches[0].Digest + match := manifestMatches[0] - if err := checkImageCompatibility(manifestMatches[0].Platform.OS, manifestMatches[0].Platform.OSVersion); err != nil { + if err := checkImageCompatibility(match.Platform.OS, match.Platform.OSVersion); err != nil { return "", "", err } - manSvc, err := p.repo.Manifests(ctx) - if err != nil { - return "", "", err + desc := specs.Descriptor{ + Digest: match.Digest, + Size: match.Size, + MediaType: match.MediaType, } - - manifest, err := manSvc.Get(ctx, manifestDigest) + manifest, err := p.manifestStore.Get(ctx, desc) if err != nil { return "", "", err } - manifestRef, err := reference.WithDigest(reference.TrimNamed(ref), manifestDigest) + manifestRef, err := reference.WithDigest(reference.TrimNamed(ref), match.Digest) if err != nil { return "", "", err } switch v := manifest.(type) { case *schema1.SignedManifest: + msg := fmt.Sprintf("[DEPRECATION NOTICE] v2 schema1 manifests in manifest lists are not supported and will break in a future release. Suggest author of %s to upgrade to v2 schema2. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/", ref) + logrus.Warn(msg) + progress.Message(p.config.ProgressOutput, "", msg) + platform := toOCIPlatform(manifestMatches[0].Platform) id, _, err = p.pullSchema1(ctx, manifestRef, v, &platform) if err != nil { @@ -798,6 +837,12 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf if err != nil { return "", "", err } + case *ocischema.DeserializedManifest: + platform := toOCIPlatform(manifestMatches[0].Platform) + id, _, err = p.pullOCI(ctx, manifestRef, v, &platform) + if err != nil { + return "", "", err + } default: return "", "", errors.New("unsupported manifest format") } @@ -851,39 +896,6 @@ func schema2ManifestDigest(ref reference.Named, mfst distribution.Manifest) (dig return digest.FromBytes(canonical), nil } -// allowV1Fallback checks if the error is a possible reason to fallback to v1 -// (even if confirmedV2 has been set already), and if so, wraps the error in -// a fallbackError with confirmedV2 set to false. Otherwise, it returns the -// error unmodified. -func allowV1Fallback(err error) error { - switch v := err.(type) { - case errcode.Errors: - if len(v) != 0 { - if v0, ok := v[0].(errcode.Error); ok && shouldV2Fallback(v0) { - return fallbackError{ - err: err, - confirmedV2: false, - transportOK: true, - } - } - } - case errcode.Error: - if shouldV2Fallback(v) { - return fallbackError{ - err: err, - confirmedV2: false, - transportOK: true, - } - } - case *url.Error: - if v.Err == auth.ErrNoBasicAuthCredentials { - return fallbackError{err: err, confirmedV2: false} - } - } - - return err -} - func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Reference) (m *schema1.Manifest, err error) { // If pull by digest, then verify the manifest digest. NOTE: It is // important to do this first, before any other content validation. If the @@ -962,7 +974,7 @@ func fixManifestLayers(m *schema1.Manifest) error { } func createDownloadFile() (*os.File, error) { - return ioutil.TempFile("", "GetImageBlob") + return os.CreateTemp("", "GetImageBlob") } func toOCIPlatform(p manifestlist.PlatformSpec) specs.Platform { diff --git a/distribution/pull_v2_test.go b/distribution/pull_v2_test.go index ca3470c8cfe8d..ac42858a7a349 100644 --- a/distribution/pull_v2_test.go +++ b/distribution/pull_v2_test.go @@ -2,17 +2,20 @@ package distribution // import "github.com/docker/docker/distribution" import ( "encoding/json" - "io/ioutil" + "fmt" + "os" "reflect" + "regexp" "runtime" "strings" "testing" "github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/reference" - "github.com/opencontainers/go-digest" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) // TestFixManifestLayers checks that fixManifestLayers removes a duplicate @@ -122,7 +125,7 @@ func TestValidateManifest(t *testing.T) { // Good manifest - goodManifestBytes, err := ioutil.ReadFile("fixtures/validate_manifest/good_manifest") + goodManifestBytes, err := os.ReadFile("fixtures/validate_manifest/good_manifest") if err != nil { t.Fatal("error reading fixture:", err) } @@ -144,7 +147,7 @@ func TestValidateManifest(t *testing.T) { // "Extra data" manifest - extraDataManifestBytes, err := ioutil.ReadFile("fixtures/validate_manifest/extra_data_manifest") + extraDataManifestBytes, err := os.ReadFile("fixtures/validate_manifest/extra_data_manifest") if err != nil { t.Fatal("error reading fixture:", err) } @@ -166,7 +169,7 @@ func TestValidateManifest(t *testing.T) { // Bad manifest - badManifestBytes, err := ioutil.ReadFile("fixtures/validate_manifest/bad_manifest") + badManifestBytes, err := os.ReadFile("fixtures/validate_manifest/bad_manifest") if err != nil { t.Fatal("error reading fixture:", err) } @@ -177,8 +180,28 @@ func TestValidateManifest(t *testing.T) { t.Fatal("error unmarshaling manifest:", err) } - verifiedManifest, err = verifySchema1Manifest(&badSignedManifest, expectedDigest) + _, err = verifySchema1Manifest(&badSignedManifest, expectedDigest) if err == nil || !strings.HasPrefix(err.Error(), "image verification failed for digest") { t.Fatal("expected validateManifest to fail with digest error") } } + +func TestFormatPlatform(t *testing.T) { + var platform specs.Platform + var result = formatPlatform(platform) + if strings.HasPrefix(result, "unknown") { + t.Fatal("expected formatPlatform to show a known platform") + } + if !strings.HasPrefix(result, runtime.GOOS) { + t.Fatal("expected formatPlatform to show the current platform") + } + if runtime.GOOS == "windows" { + if !strings.HasPrefix(result, "windows") { + t.Fatal("expected formatPlatform to show windows platform") + } + matches, _ := regexp.MatchString("windows.* [0-9]", result) + if !matches { + t.Fatal(fmt.Sprintf("expected formatPlatform to show windows platform with a version, but got '%s'", result)) + } + } +} diff --git a/distribution/pull_v2_unix.go b/distribution/pull_v2_unix.go index adbaf41451280..e922292883e96 100644 --- a/distribution/pull_v2_unix.go +++ b/distribution/pull_v2_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package distribution // import "github.com/docker/docker/distribution" @@ -58,3 +59,10 @@ func withDefault(p specs.Platform) specs.Platform { } return p } + +func formatPlatform(platform specs.Platform) string { + if platform.OS == "" { + platform = platforms.DefaultSpec() + } + return platforms.Format(platform) +} diff --git a/distribution/pull_v2_windows.go b/distribution/pull_v2_windows.go index 1ae167ef777d0..a6db4b9b9eac3 100644 --- a/distribution/pull_v2_windows.go +++ b/distribution/pull_v2_windows.go @@ -4,13 +4,15 @@ import ( "context" "errors" "fmt" + "io" "net/http" - "os" "runtime" "sort" "strconv" "strings" + "github.com/Microsoft/hcsshim/osversion" + "github.com/containerd/containerd/platforms" "github.com/docker/distribution" "github.com/docker/distribution/manifest/manifestlist" "github.com/docker/distribution/manifest/schema2" @@ -40,7 +42,7 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo // We're done if the registry has this blob. if err == nil { // Seek does an HTTP GET. If it succeeds, the blob really is accessible. - if _, err = rsc.Seek(0, os.SEEK_SET); err == nil { + if _, err = rsc.Seek(0, io.SeekStart); err == nil { return rsc, nil } rsc.Close() @@ -52,7 +54,7 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo rsc = transport.NewHTTPReadSeeker(http.DefaultClient, url, nil) // Seek does an HTTP GET. If it succeeds, the blob really is accessible. - _, err = rsc.Seek(0, os.SEEK_SET) + _, err = rsc.Seek(0, io.SeekStart) if err == nil { break } @@ -64,7 +66,7 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo } func filterManifests(manifests []manifestlist.ManifestDescriptor, p specs.Platform) []manifestlist.ManifestDescriptor { - version := system.GetOSVersion() + version := osversion.Get() osVersion := fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build) logrus.Debugf("will prefer Windows entries with version %s", osVersion) @@ -122,7 +124,7 @@ func (mbv manifestsByVersion) Swap(i, j int) { // Fixes https://github.com/moby/moby/issues/36184. func checkImageCompatibility(imageOS, imageOSVersion string) error { if imageOS == "windows" { - hostOSV := system.GetOSVersion() + hostOSV := osversion.Get() splitImageOSVersion := strings.Split(imageOSVersion, ".") // eg 10.0.16299.nnnn if len(splitImageOSVersion) >= 3 { if imageOSBuild, err := strconv.Atoi(splitImageOSVersion[2]); err == nil { @@ -136,3 +138,10 @@ func checkImageCompatibility(imageOS, imageOSVersion string) error { } return nil } + +func formatPlatform(platform specs.Platform) string { + if platform.OS == "" { + platform = platforms.DefaultSpec() + } + return fmt.Sprintf("%s %s", platforms.Format(platform), osversion.Get().ToString()) +} diff --git a/distribution/push.go b/distribution/push.go index eb3bc5597462a..f64d0f822c080 100644 --- a/distribution/push.go +++ b/distribution/push.go @@ -41,13 +41,7 @@ func NewPusher(ref reference.Named, endpoint registry.APIEndpoint, repoInfo *reg config: imagePushConfig, }, nil case registry.APIVersion1: - return &v1Pusher{ - v1IDService: metadata.NewV1IDService(imagePushConfig.MetadataStore), - ref: ref, - endpoint: endpoint, - repoInfo: repoInfo, - config: imagePushConfig, - }, nil + return nil, fmt.Errorf("protocol version %d no longer supported. Please contact admins of registry %s", endpoint.Version, endpoint.URL) } return nil, fmt.Errorf("unknown version %d for registry %s", endpoint.Version, endpoint.URL) } @@ -79,11 +73,6 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo var ( lastErr error - // confirmedV2 is set to true if a push attempt managed to - // confirm that it was talking to a v2 registry. This will - // prevent fallback to the v1 protocol. - confirmedV2 bool - // confirmedTLSRegistries is a map indicating which registries // are known to be using TLS. There should never be a plaintext // retry for any of these. @@ -91,14 +80,6 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo ) for _, endpoint := range endpoints { - if imagePushConfig.RequireSchema2 && endpoint.Version == registry.APIVersion1 { - continue - } - if confirmedV2 && endpoint.Version == registry.APIVersion1 { - logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL) - continue - } - if endpoint.URL.Scheme != "https" { if _, confirmedTLS := confirmedTLSRegistries[endpoint.URL.Host]; confirmedTLS { logrus.Debugf("Skipping non-TLS endpoint %s for host/port that appears to use TLS", endpoint.URL) @@ -120,7 +101,6 @@ func Push(ctx context.Context, ref reference.Named, imagePushConfig *ImagePushCo case <-ctx.Done(): default: if fallbackErr, ok := err.(fallbackError); ok { - confirmedV2 = confirmedV2 || fallbackErr.confirmedV2 if fallbackErr.transportOK && endpoint.URL.Scheme == "https" { confirmedTLSRegistries[endpoint.URL.Host] = struct{}{} } diff --git a/distribution/push_v1.go b/distribution/push_v1.go deleted file mode 100644 index 7bd75e9fe6c03..0000000000000 --- a/distribution/push_v1.go +++ /dev/null @@ -1,457 +0,0 @@ -package distribution // import "github.com/docker/docker/distribution" - -import ( - "context" - "fmt" - "sync" - - "github.com/docker/distribution/reference" - "github.com/docker/distribution/registry/client/transport" - "github.com/docker/docker/distribution/metadata" - "github.com/docker/docker/dockerversion" - "github.com/docker/docker/image" - "github.com/docker/docker/image/v1" - "github.com/docker/docker/layer" - "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/progress" - "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/pkg/system" - "github.com/docker/docker/registry" - "github.com/opencontainers/go-digest" - "github.com/sirupsen/logrus" -) - -type v1Pusher struct { - v1IDService *metadata.V1IDService - endpoint registry.APIEndpoint - ref reference.Named - repoInfo *registry.RepositoryInfo - config *ImagePushConfig - session *registry.Session -} - -func (p *v1Pusher) Push(ctx context.Context) error { - tlsConfig, err := p.config.RegistryService.TLSConfig(p.repoInfo.Index.Name) - if err != nil { - return err - } - // Adds Docker-specific headers as well as user-specified headers (metaHeaders) - tr := transport.NewTransport( - // TODO(tiborvass): was NoTimeout - registry.NewTransport(tlsConfig), - registry.Headers(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders)..., - ) - client := registry.HTTPClient(tr) - v1Endpoint := p.endpoint.ToV1Endpoint(dockerversion.DockerUserAgent(ctx), p.config.MetaHeaders) - p.session, err = registry.NewSession(client, p.config.AuthConfig, v1Endpoint) - if err != nil { - // TODO(dmcgowan): Check if should fallback - return fallbackError{err: err} - } - if err := p.pushRepository(ctx); err != nil { - // TODO(dmcgowan): Check if should fallback - return err - } - return nil -} - -// v1Image exposes the configuration, filesystem layer ID, and a v1 ID for an -// image being pushed to a v1 registry. -type v1Image interface { - Config() []byte - Layer() layer.Layer - V1ID() string -} - -type v1ImageCommon struct { - layer layer.Layer - config []byte - v1ID string -} - -func (common *v1ImageCommon) Config() []byte { - return common.config -} - -func (common *v1ImageCommon) V1ID() string { - return common.v1ID -} - -func (common *v1ImageCommon) Layer() layer.Layer { - return common.layer -} - -// v1TopImage defines a runnable (top layer) image being pushed to a v1 -// registry. -type v1TopImage struct { - v1ImageCommon - imageID image.ID -} - -func newV1TopImage(imageID image.ID, img *image.Image, l layer.Layer, parent *v1DependencyImage) (*v1TopImage, error) { - v1ID := imageID.Digest().Hex() - parentV1ID := "" - if parent != nil { - parentV1ID = parent.V1ID() - } - - config, err := v1.MakeV1ConfigFromConfig(img, v1ID, parentV1ID, false) - if err != nil { - return nil, err - } - - return &v1TopImage{ - v1ImageCommon: v1ImageCommon{ - v1ID: v1ID, - config: config, - layer: l, - }, - imageID: imageID, - }, nil -} - -// v1DependencyImage defines a dependency layer being pushed to a v1 registry. -type v1DependencyImage struct { - v1ImageCommon -} - -func newV1DependencyImage(l layer.Layer, parent *v1DependencyImage) *v1DependencyImage { - v1ID := digest.Digest(l.ChainID()).Hex() - - var config string - if parent != nil { - config = fmt.Sprintf(`{"id":"%s","parent":"%s"}`, v1ID, parent.V1ID()) - } else { - config = fmt.Sprintf(`{"id":"%s"}`, v1ID) - } - return &v1DependencyImage{ - v1ImageCommon: v1ImageCommon{ - v1ID: v1ID, - config: []byte(config), - layer: l, - }, - } -} - -// Retrieve the all the images to be uploaded in the correct order -func (p *v1Pusher) getImageList() (imageList []v1Image, tagsByImage map[image.ID][]string, referencedLayers []PushLayer, err error) { - tagsByImage = make(map[image.ID][]string) - - // Ignore digest references - if _, isCanonical := p.ref.(reference.Canonical); isCanonical { - return - } - - tagged, isTagged := p.ref.(reference.NamedTagged) - if isTagged { - // Push a specific tag - var imgID image.ID - var dgst digest.Digest - dgst, err = p.config.ReferenceStore.Get(p.ref) - if err != nil { - return - } - imgID = image.IDFromDigest(dgst) - - imageList, err = p.imageListForTag(imgID, nil, &referencedLayers) - if err != nil { - return - } - - tagsByImage[imgID] = []string{tagged.Tag()} - - return - } - - imagesSeen := make(map[digest.Digest]struct{}) - dependenciesSeen := make(map[layer.ChainID]*v1DependencyImage) - - associations := p.config.ReferenceStore.ReferencesByName(p.ref) - for _, association := range associations { - if tagged, isTagged = association.Ref.(reference.NamedTagged); !isTagged { - // Ignore digest references. - continue - } - - imgID := image.IDFromDigest(association.ID) - tagsByImage[imgID] = append(tagsByImage[imgID], tagged.Tag()) - - if _, present := imagesSeen[association.ID]; present { - // Skip generating image list for already-seen image - continue - } - imagesSeen[association.ID] = struct{}{} - - imageListForThisTag, err := p.imageListForTag(imgID, dependenciesSeen, &referencedLayers) - if err != nil { - return nil, nil, nil, err - } - - // append to main image list - imageList = append(imageList, imageListForThisTag...) - } - if len(imageList) == 0 { - return nil, nil, nil, fmt.Errorf("No images found for the requested repository / tag") - } - logrus.Debugf("Image list: %v", imageList) - logrus.Debugf("Tags by image: %v", tagsByImage) - - return -} - -func (p *v1Pusher) imageListForTag(imgID image.ID, dependenciesSeen map[layer.ChainID]*v1DependencyImage, referencedLayers *[]PushLayer) (imageListForThisTag []v1Image, err error) { - ics, ok := p.config.ImageStore.(*imageConfigStore) - if !ok { - return nil, fmt.Errorf("only image store images supported for v1 push") - } - img, err := ics.Store.Get(imgID) - if err != nil { - return nil, err - } - - topLayerID := img.RootFS.ChainID() - - if !system.IsOSSupported(img.OperatingSystem()) { - return nil, system.ErrNotSupportedOperatingSystem - } - pl, err := p.config.LayerStores[img.OperatingSystem()].Get(topLayerID) - *referencedLayers = append(*referencedLayers, pl) - if err != nil { - return nil, fmt.Errorf("failed to get top layer from image: %v", err) - } - - // V1 push is deprecated, only support existing layerstore layers - lsl, ok := pl.(*storeLayer) - if !ok { - return nil, fmt.Errorf("only layer store layers supported for v1 push") - } - l := lsl.Layer - - dependencyImages, parent := generateDependencyImages(l.Parent(), dependenciesSeen) - - topImage, err := newV1TopImage(imgID, img, l, parent) - if err != nil { - return nil, err - } - - imageListForThisTag = append(dependencyImages, topImage) - - return -} - -func generateDependencyImages(l layer.Layer, dependenciesSeen map[layer.ChainID]*v1DependencyImage) (imageListForThisTag []v1Image, parent *v1DependencyImage) { - if l == nil { - return nil, nil - } - - imageListForThisTag, parent = generateDependencyImages(l.Parent(), dependenciesSeen) - - if dependenciesSeen != nil { - if dependencyImage, present := dependenciesSeen[l.ChainID()]; present { - // This layer is already on the list, we can ignore it - // and all its parents. - return imageListForThisTag, dependencyImage - } - } - - dependencyImage := newV1DependencyImage(l, parent) - imageListForThisTag = append(imageListForThisTag, dependencyImage) - - if dependenciesSeen != nil { - dependenciesSeen[l.ChainID()] = dependencyImage - } - - return imageListForThisTag, dependencyImage -} - -// createImageIndex returns an index of an image's layer IDs and tags. -func createImageIndex(images []v1Image, tags map[image.ID][]string) []*registry.ImgData { - var imageIndex []*registry.ImgData - for _, img := range images { - v1ID := img.V1ID() - - if topImage, isTopImage := img.(*v1TopImage); isTopImage { - if tags, hasTags := tags[topImage.imageID]; hasTags { - // If an image has tags you must add an entry in the image index - // for each tag - for _, tag := range tags { - imageIndex = append(imageIndex, ®istry.ImgData{ - ID: v1ID, - Tag: tag, - }) - } - continue - } - } - - // If the image does not have a tag it still needs to be sent to the - // registry with an empty tag so that it is associated with the repository - imageIndex = append(imageIndex, ®istry.ImgData{ - ID: v1ID, - Tag: "", - }) - } - return imageIndex -} - -// lookupImageOnEndpoint checks the specified endpoint to see if an image exists -// and if it is absent then it sends the image id to the channel to be pushed. -func (p *v1Pusher) lookupImageOnEndpoint(wg *sync.WaitGroup, endpoint string, images chan v1Image, imagesToPush chan string) { - defer wg.Done() - for image := range images { - v1ID := image.V1ID() - truncID := stringid.TruncateID(image.Layer().DiffID().String()) - if err := p.session.LookupRemoteImage(v1ID, endpoint); err != nil { - logrus.Errorf("Error in LookupRemoteImage: %s", err) - imagesToPush <- v1ID - progress.Update(p.config.ProgressOutput, truncID, "Waiting") - } else { - progress.Update(p.config.ProgressOutput, truncID, "Already exists") - } - } -} - -func (p *v1Pusher) pushImageToEndpoint(ctx context.Context, endpoint string, imageList []v1Image, tags map[image.ID][]string, repo *registry.RepositoryData) error { - workerCount := len(imageList) - // start a maximum of 5 workers to check if images exist on the specified endpoint. - if workerCount > 5 { - workerCount = 5 - } - var ( - wg = &sync.WaitGroup{} - imageData = make(chan v1Image, workerCount*2) - imagesToPush = make(chan string, workerCount*2) - pushes = make(chan map[string]struct{}, 1) - ) - for i := 0; i < workerCount; i++ { - wg.Add(1) - go p.lookupImageOnEndpoint(wg, endpoint, imageData, imagesToPush) - } - // start a go routine that consumes the images to push - go func() { - shouldPush := make(map[string]struct{}) - for id := range imagesToPush { - shouldPush[id] = struct{}{} - } - pushes <- shouldPush - }() - for _, v1Image := range imageList { - imageData <- v1Image - } - // close the channel to notify the workers that there will be no more images to check. - close(imageData) - wg.Wait() - close(imagesToPush) - // wait for all the images that require pushes to be collected into a consumable map. - shouldPush := <-pushes - // finish by pushing any images and tags to the endpoint. The order that the images are pushed - // is very important that is why we are still iterating over the ordered list of imageIDs. - for _, img := range imageList { - v1ID := img.V1ID() - if _, push := shouldPush[v1ID]; push { - if _, err := p.pushImage(ctx, img, endpoint); err != nil { - // FIXME: Continue on error? - return err - } - } - if topImage, isTopImage := img.(*v1TopImage); isTopImage { - for _, tag := range tags[topImage.imageID] { - progress.Messagef(p.config.ProgressOutput, "", "Pushing tag for rev [%s] on {%s}", stringid.TruncateID(v1ID), endpoint+"repositories/"+reference.Path(p.repoInfo.Name)+"/tags/"+tag) - if err := p.session.PushRegistryTag(p.repoInfo.Name, v1ID, tag, endpoint); err != nil { - return err - } - } - } - } - return nil -} - -// pushRepository pushes layers that do not already exist on the registry. -func (p *v1Pusher) pushRepository(ctx context.Context) error { - imgList, tags, referencedLayers, err := p.getImageList() - defer func() { - for _, l := range referencedLayers { - l.Release() - } - }() - if err != nil { - return err - } - - imageIndex := createImageIndex(imgList, tags) - for _, data := range imageIndex { - logrus.Debugf("Pushing ID: %s with Tag: %s", data.ID, data.Tag) - } - - // Register all the images in a repository with the registry - // If an image is not in this list it will not be associated with the repository - repoData, err := p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, false, nil) - if err != nil { - return err - } - // push the repository to each of the endpoints only if it does not exist. - for _, endpoint := range repoData.Endpoints { - if err := p.pushImageToEndpoint(ctx, endpoint, imgList, tags, repoData); err != nil { - return err - } - } - _, err = p.session.PushImageJSONIndex(p.repoInfo.Name, imageIndex, true, repoData.Endpoints) - return err -} - -func (p *v1Pusher) pushImage(ctx context.Context, v1Image v1Image, ep string) (checksum string, err error) { - l := v1Image.Layer() - v1ID := v1Image.V1ID() - truncID := stringid.TruncateID(l.DiffID().String()) - - jsonRaw := v1Image.Config() - progress.Update(p.config.ProgressOutput, truncID, "Pushing") - - // General rule is to use ID for graph accesses and compatibilityID for - // calls to session.registry() - imgData := ®istry.ImgData{ - ID: v1ID, - } - - // Send the json - if err := p.session.PushImageJSONRegistry(imgData, jsonRaw, ep); err != nil { - if err == registry.ErrAlreadyExists { - progress.Update(p.config.ProgressOutput, truncID, "Image already pushed, skipping") - return "", nil - } - return "", err - } - - arch, err := l.TarStream() - if err != nil { - return "", err - } - defer arch.Close() - - // don't care if this fails; best effort - size, _ := l.DiffSize() - - // Send the layer - logrus.Debugf("rendered layer for %s of [%d] size", v1ID, size) - - reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(ctx, arch), p.config.ProgressOutput, size, truncID, "Pushing") - defer reader.Close() - - checksum, checksumPayload, err := p.session.PushImageLayerRegistry(v1ID, reader, ep, jsonRaw) - if err != nil { - return "", err - } - imgData.Checksum = checksum - imgData.ChecksumPayload = checksumPayload - // Send the checksum - if err := p.session.PushImageChecksumRegistry(imgData, ep); err != nil { - return "", err - } - - if err := p.v1IDService.Set(v1ID, p.repoInfo.Index.Name, l.DiffID()); err != nil { - logrus.Warnf("Could not set v1 ID mapping: %v", err) - } - - progress.Update(p.config.ProgressOutput, truncID, "Image successfully pushed") - return imgData.Checksum, nil -} diff --git a/distribution/push_v2.go b/distribution/push_v2.go index 9dc3e7a2a663f..60171de773b0b 100644 --- a/distribution/push_v2.go +++ b/distribution/push_v2.go @@ -2,9 +2,9 @@ package distribution // import "github.com/docker/docker/distribution" import ( "context" - "errors" "fmt" "io" + "os" "runtime" "sort" "strings" @@ -24,7 +24,8 @@ import ( "github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/registry" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -52,16 +53,13 @@ type pushState struct { // involve the same layers. It is also used to fill in digest and size // information when building the manifest. remoteLayers map[layer.DiffID]distribution.Descriptor - // confirmedV2 is set to true if we confirm we're talking to a v2 - // registry. This is used to limit fallbacks to the v1 protocol. - confirmedV2 bool - hasAuthInfo bool + hasAuthInfo bool } func (p *v2Pusher) Push(ctx context.Context) (err error) { p.pushState.remoteLayers = make(map[layer.DiffID]distribution.Descriptor) - p.repo, p.pushState.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "push", "pull") + p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "push", "pull") p.pushState.hasAuthInfo = p.config.AuthConfig.RegistryToken != "" || (p.config.AuthConfig.Username != "" && p.config.AuthConfig.Password != "") if err != nil { logrus.Debugf("Error getting v2 registry: %v", err) @@ -72,7 +70,6 @@ func (p *v2Pusher) Push(ctx context.Context) (err error) { if continueOnError(err, p.endpoint.Mirror) { return fallbackError{ err: err, - confirmedV2: p.pushState.confirmedV2, transportOK: true, } } @@ -94,7 +91,7 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) { return errors.New("cannot push a digest reference") } - // Pull all tags + // Push all tags pushed := 0 for _, association := range p.config.ReferenceStore.ReferencesByName(p.ref) { if namedTagged, isNamedTagged := association.Ref.(reference.NamedTagged); isNamedTagged { @@ -115,7 +112,7 @@ func (p *v2Pusher) pushV2Repository(ctx context.Context) (err error) { func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id digest.Digest) error { logrus.Debugf("Pushing repository: %s", reference.FamiliarString(ref)) - imgConfig, err := p.config.ImageStore.Get(id) + imgConfig, err := p.config.ImageStore.Get(ctx, id) if err != nil { return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err) } @@ -125,12 +122,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err) } - platform, err := p.config.ImageStore.PlatformFromConfig(imgConfig) - if err != nil { - return fmt.Errorf("unable to get platform for image %s: %s", reference.FamiliarString(ref), err) - } - - l, err := p.config.LayerStores[platform.OS].Get(rootfs.ChainID()) + l, err := p.config.LayerStores.Get(rootfs.ChainID()) if err != nil { return fmt.Errorf("failed to get top layer from image: %v", err) } @@ -186,8 +178,21 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id return err } + // This is a temporary environment variables used in CI to allow pushing + // manifest v2 schema 1 images to test-registries used for testing *pulling* + // these images. + if os.Getenv("DOCKER_ALLOW_SCHEMA1_PUSH_DONOTUSE") == "" { + if err.Error() == "tag invalid" { + msg := "[DEPRECATED] support for pushing manifest v2 schema1 images has been removed. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/" + logrus.WithError(err).Error(msg) + return errors.Wrap(err, msg) + } + return err + } + logrus.Warnf("failed to upload schema2 manifest: %v - falling back to schema1", err) + // Note: this fallback is deprecated, see log messages below manifestRef, err := reference.WithTag(p.repo.Named(), ref.Tag()) if err != nil { return err @@ -201,6 +206,11 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id if _, err = manSvc.Put(ctx, manifest, putOptions...); err != nil { return err } + + // schema2 failed but schema1 succeeded + msg := fmt.Sprintf("[DEPRECATION NOTICE] support for pushing manifest v2 schema1 images will be removed in an upcoming release. Please contact admins of the %s registry NOW to avoid future disruption. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/", reference.Domain(ref)) + logrus.Warn(msg) + progress.Message(p.config.ProgressOutput, "", msg) } var canonicalManifest []byte @@ -311,7 +321,8 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. // Attempt to find another repository in the same registry to mount the layer from to avoid an unnecessary upload candidates := getRepositoryMountCandidates(pd.repoInfo, pd.hmacKey, maxMountAttempts, v2Metadata) isUnauthorizedError := false - for _, mountCandidate := range candidates { + for _, mc := range candidates { + mountCandidate := mc logrus.Debugf("attempting to mount layer %s (%s) from %s", diffID, mountCandidate.Digest, mountCandidate.SourceRepository) createOpts := []distribution.BlobCreateOption{} @@ -351,7 +362,6 @@ func (pd *v2PushDescriptor) Upload(ctx context.Context, progressOutput progress. err.Descriptor.MediaType = schema2.MediaTypeLayer pd.pushState.Lock() - pd.pushState.confirmedV2 = true pd.pushState.remoteLayers[diffID] = err.Descriptor pd.pushState.Unlock() @@ -456,7 +466,7 @@ func (pd *v2PushDescriptor) uploadUsingSession( case schema2.MediaTypeLayer: default: reader.Close() - return distribution.Descriptor{}, fmt.Errorf("unsupported layer media type %s", m) + return distribution.Descriptor{}, xfer.DoNotRetry{Err: fmt.Errorf("unsupported layer media type %s", m)} } digester := digest.Canonical.Digester() @@ -491,8 +501,6 @@ func (pd *v2PushDescriptor) uploadUsingSession( } pd.pushState.Lock() - // If Commit succeeded, that's an indication that the remote registry speaks the v2 protocol. - pd.pushState.confirmedV2 = true pd.pushState.remoteLayers[diffID] = desc pd.pushState.Unlock() @@ -668,7 +676,6 @@ func (bla byLikeness) Swap(i, j int) { } func (bla byLikeness) Len() int { return len(bla.arr) } -// nolint: interfacer func sortV2MetadataByLikenessAndAge(repoInfo reference.Named, hmacKey []byte, marr []metadata.V2Metadata) { // reverse the metadata array to shift the newest entries to the beginning for i := 0; i < len(marr)/2; i++ { diff --git a/distribution/push_v2_test.go b/distribution/push_v2_test.go index 436b4a1797fc0..99d29f0802669 100644 --- a/distribution/push_v2_test.go +++ b/distribution/push_v2_test.go @@ -17,7 +17,7 @@ import ( "github.com/docker/docker/pkg/progress" refstore "github.com/docker/docker/reference" "github.com/docker/docker/registry" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" ) func TestGetRepositoryMountCandidates(t *testing.T) { @@ -532,7 +532,7 @@ func TestWhenEmptyAuthConfig(t *testing.T) { Scheme: "https", Host: "index.docker.io", }, - Version: registry.APIVersion1, + Version: registry.APIVersion2, TrimHostname: true, }, } @@ -549,7 +549,7 @@ type mockBlobStoreWithCreate struct { } func (blob *mockBlobStoreWithCreate) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) { - return nil, errcode.Errors(append([]error{errcode.ErrorCodeUnauthorized.WithMessage("unauthorized")})) + return nil, errcode.Errors([]error{errcode.ErrorCodeUnauthorized.WithMessage("unauthorized")}) } type mockRepoWithBlob struct { diff --git a/distribution/registry.go b/distribution/registry.go index d81530b75cbbf..651ae54249c34 100644 --- a/distribution/registry.go +++ b/distribution/registry.go @@ -16,7 +16,6 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/dockerversion" "github.com/docker/docker/registry" - "github.com/docker/go-connections/sockets" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -56,7 +55,10 @@ func init() { // NewV2Repository returns a repository (v2 only). It creates an HTTP transport // providing timeout settings and authentication support, and also verifies the // remote API version. -func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string) (repo distribution.Repository, foundVersion bool, err error) { +func NewV2Repository( + ctx context.Context, repoInfo *registry.RepositoryInfo, endpoint registry.APIEndpoint, + metaHeaders http.Header, authConfig *types.AuthConfig, actions ...string, +) (repo distribution.Repository, err error) { repoName := repoInfo.Name.Name() // If endpoint does not support CanonicalName, use the RemoteName instead if endpoint.TrimHostname { @@ -72,31 +74,25 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end // TODO(dmcgowan): Call close idle connections when complete, use keep alive base := &http.Transport{ Proxy: http.ProxyFromEnvironment, - Dial: direct.Dial, + DialContext: direct.DialContext, TLSHandshakeTimeout: 10 * time.Second, TLSClientConfig: endpoint.TLSConfig, // TODO(dmcgowan): Call close idle connections when complete and use keep alive DisableKeepAlives: true, } - proxyDialer, err := sockets.DialerFromEnvironment(direct) - if err == nil { - base.Dial = proxyDialer.Dial - } - modifiers := registry.Headers(dockerversion.DockerUserAgent(ctx), metaHeaders) authTransport := transport.NewTransport(base, modifiers...) - challengeManager, foundVersion, err := registry.PingV2Registry(endpoint.URL, authTransport) + challengeManager, err := registry.PingV2Registry(endpoint.URL, authTransport) if err != nil { transportOK := false if responseErr, ok := err.(registry.PingResponseError); ok { transportOK = true err = responseErr.Err } - return nil, foundVersion, fallbackError{ + return nil, fallbackError{ err: err, - confirmedV2: foundVersion, transportOK: transportOK, } } @@ -126,9 +122,8 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end repoNameRef, err := reference.WithName(repoName) if err != nil { - return nil, foundVersion, fallbackError{ + return nil, fallbackError{ err: err, - confirmedV2: foundVersion, transportOK: true, } } @@ -137,7 +132,6 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end if err != nil { err = fallbackError{ err: err, - confirmedV2: foundVersion, transportOK: true, } } diff --git a/distribution/registry_unit_test.go b/distribution/registry_unit_test.go index 3651c4688bde5..3cbd09f5621bb 100644 --- a/distribution/registry_unit_test.go +++ b/distribution/registry_unit_test.go @@ -69,13 +69,13 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) { }, Schema2Types: ImageTypes, } - puller, err := newPuller(endpoint, repoInfo, imagePullConfig) + puller, err := newPuller(endpoint, repoInfo, imagePullConfig, nil) if err != nil { t.Fatal(err) } p := puller.(*v2Puller) ctx := context.Background() - p.repo, _, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") + p.repo, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") if err != nil { t.Fatal(err) } @@ -83,7 +83,7 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) { logrus.Debug("About to pull") // We expect it to fail, since we haven't mock'd the full registry exchange in our handler above tag, _ := reference.WithTag(n, "tag_goes_here") - _ = p.pullV2Repository(ctx, tag, nil) + _ = p.pullV2Repository(ctx, tag) } func TestTokenPassThru(t *testing.T) { diff --git a/distribution/xfer/download.go b/distribution/xfer/download.go index e8cda93628b16..847152d081b8b 100644 --- a/distribution/xfer/download.go +++ b/distribution/xfer/download.go @@ -24,9 +24,10 @@ const maxDownloadAttempts = 5 // registers and downloads those, taking into account dependencies between // layers. type LayerDownloadManager struct { - layerStores map[string]layer.Store - tm TransferManager - waitDuration time.Duration + layerStore layer.Store + tm TransferManager + waitDuration time.Duration + maxDownloadAttempts int } // SetConcurrency sets the max concurrent downloads for each pull @@ -35,11 +36,12 @@ func (ldm *LayerDownloadManager) SetConcurrency(concurrency int) { } // NewLayerDownloadManager returns a new LayerDownloadManager. -func NewLayerDownloadManager(layerStores map[string]layer.Store, concurrencyLimit int, options ...func(*LayerDownloadManager)) *LayerDownloadManager { +func NewLayerDownloadManager(layerStore layer.Store, concurrencyLimit int, options ...func(*LayerDownloadManager)) *LayerDownloadManager { manager := LayerDownloadManager{ - layerStores: layerStores, - tm: NewTransferManager(concurrencyLimit), - waitDuration: time.Second, + layerStore: layerStore, + tm: NewTransferManager(concurrencyLimit), + waitDuration: time.Second, + maxDownloadAttempts: maxDownloadAttempts, } for _, option := range options { option(&manager) @@ -47,6 +49,14 @@ func NewLayerDownloadManager(layerStores map[string]layer.Store, concurrencyLimi return &manager } +// WithMaxDownloadAttempts configures the maximum number of download +// attempts for a download manager. +func WithMaxDownloadAttempts(max int) func(*LayerDownloadManager) { + return func(dlm *LayerDownloadManager) { + dlm.maxDownloadAttempts = max + } +} + type downloadTransfer struct { Transfer @@ -108,6 +118,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima // Assume that the operating system is the host OS if blank, and validate it // to ensure we don't cause a panic by an invalid index into the layerstores. + // TODO remove now that LCOW is no longer a thing if os == "" { os = runtime.GOOS } @@ -126,13 +137,13 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima if err == nil { getRootFS := rootFS getRootFS.Append(diffID) - l, err := ldm.layerStores[os].Get(getRootFS.ChainID()) + l, err := ldm.layerStore.Get(getRootFS.ChainID()) if err == nil { // Layer already exists. logrus.Debugf("Layer already exists: %s", descriptor.ID()) progress.Update(progressOutput, descriptor.ID(), "Already exists") if topLayer != nil { - layer.ReleaseAndLog(ldm.layerStores[os], topLayer) + layer.ReleaseAndLog(ldm.layerStore, topLayer) } topLayer = l missingLayer = false @@ -151,7 +162,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima // the stack? If so, avoid downloading it more than once. var topDownloadUncasted Transfer if existingDownload, ok := downloadsByKey[key]; ok { - xferFunc := ldm.makeDownloadFuncFromDownload(descriptor, existingDownload, topDownload, os) + xferFunc := ldm.makeDownloadFuncFromDownload(descriptor, existingDownload, topDownload) defer topDownload.Transfer.Release(watcher) topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput) topDownload = topDownloadUncasted.(*downloadTransfer) @@ -163,10 +174,10 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima var xferFunc DoFunc if topDownload != nil { - xferFunc = ldm.makeDownloadFunc(descriptor, "", topDownload, os) + xferFunc = ldm.makeDownloadFunc(descriptor, "", topDownload) defer topDownload.Transfer.Release(watcher) } else { - xferFunc = ldm.makeDownloadFunc(descriptor, rootFS.ChainID(), nil, os) + xferFunc = ldm.makeDownloadFunc(descriptor, rootFS.ChainID(), nil) } topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput) topDownload = topDownloadUncasted.(*downloadTransfer) @@ -176,7 +187,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima if topDownload == nil { return rootFS, func() { if topLayer != nil { - layer.ReleaseAndLog(ldm.layerStores[os], topLayer) + layer.ReleaseAndLog(ldm.layerStore, topLayer) } }, nil } @@ -187,7 +198,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima defer func() { if topLayer != nil { - layer.ReleaseAndLog(ldm.layerStores[os], topLayer) + layer.ReleaseAndLog(ldm.layerStore, topLayer) } }() @@ -223,11 +234,11 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima // complete before the registration step, and registers the downloaded data // on top of parentDownload's resulting layer. Otherwise, it registers the // layer on top of the ChainID given by parentLayer. -func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer, os string) DoFunc { +func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer) DoFunc { return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { d := &downloadTransfer{ Transfer: NewTransfer(), - layerStore: ldm.layerStores[os], + layerStore: ldm.layerStore, } go func() { @@ -283,13 +294,13 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, } retries++ - if _, isDNR := err.(DoNotRetry); isDNR || retries == maxDownloadAttempts { - logrus.Errorf("Download failed: %v", err) + if _, isDNR := err.(DoNotRetry); isDNR || retries > ldm.maxDownloadAttempts { + logrus.Errorf("Download failed after %d attempts: %v", retries, err) d.err = err return } - logrus.Errorf("Download failed, retrying: %v", err) + logrus.Infof("Download failed, retrying (%d/%d): %v", retries, ldm.maxDownloadAttempts, err) delay := retries * 5 ticker := time.NewTicker(ldm.waitDuration) @@ -387,11 +398,11 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, // parentDownload. This function does not log progress output because it would // interfere with the progress reporting for sourceDownload, which has the same // Key. -func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer, os string) DoFunc { +func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer) DoFunc { return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { d := &downloadTransfer{ Transfer: NewTransfer(), - layerStore: ldm.layerStores[os], + layerStore: ldm.layerStore, } go func() { diff --git a/distribution/xfer/download_test.go b/distribution/xfer/download_test.go index 4ab3705af6a1f..fbf7f541afac6 100644 --- a/distribution/xfer/download_test.go +++ b/distribution/xfer/download_test.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "runtime" "sync/atomic" "testing" @@ -16,7 +15,8 @@ import ( "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/progress" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" + "gotest.tools/v3/assert" ) const maxDownloadConcurrency = 3 @@ -26,11 +26,10 @@ type mockLayer struct { diffID layer.DiffID chainID layer.ChainID parent layer.Layer - os string } func (ml *mockLayer) TarStream() (io.ReadCloser, error) { - return ioutil.NopCloser(bytes.NewBuffer(ml.layerData.Bytes())), nil + return io.NopCloser(bytes.NewBuffer(ml.layerData.Bytes())), nil } func (ml *mockLayer) TarStreamFrom(layer.ChainID) (io.ReadCloser, error) { @@ -161,6 +160,7 @@ type mockDownloadDescriptor struct { registeredDiffID layer.DiffID expectedDiffID layer.DiffID simulateRetries int + retries int } // Key returns the key used to deduplicate downloads. @@ -191,7 +191,7 @@ func (d *mockDownloadDescriptor) mockTarStream() io.ReadCloser { // The mock implementation returns the ID repeated 5 times as a tar // stream instead of actual tar data. The data is ignored except for // computing IDs. - return ioutil.NopCloser(bytes.NewBuffer([]byte(d.id + d.id + d.id + d.id + d.id))) + return io.NopCloser(bytes.NewBuffer([]byte(d.id + d.id + d.id + d.id + d.id))) } // Download is called to perform the download. @@ -214,9 +214,9 @@ func (d *mockDownloadDescriptor) Download(ctx context.Context, progressOutput pr } } - if d.simulateRetries != 0 { - d.simulateRetries-- - return nil, 0, errors.New("simulating retry") + if d.retries < d.simulateRetries { + d.retries++ + return nil, 0, fmt.Errorf("simulating download attempt %d/%d", d.retries, d.simulateRetries) } return d.mockTarStream(), 0, nil @@ -268,9 +268,7 @@ func TestSuccessfulDownload(t *testing.T) { } layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)} - lsMap := make(map[string]layer.Store) - lsMap[runtime.GOOS] = layerStore - ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) + ldm := NewLayerDownloadManager(layerStore, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) progressChan := make(chan progress.Progress) progressDone := make(chan struct{}) @@ -332,9 +330,7 @@ func TestSuccessfulDownload(t *testing.T) { func TestCancelledDownload(t *testing.T) { layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)} - lsMap := make(map[string]layer.Store) - lsMap[runtime.GOOS] = layerStore - ldm := NewLayerDownloadManager(lsMap, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) + ldm := NewLayerDownloadManager(layerStore, maxDownloadConcurrency, func(m *LayerDownloadManager) { m.waitDuration = time.Millisecond }) progressChan := make(chan progress.Progress) progressDone := make(chan struct{}) @@ -360,3 +356,68 @@ func TestCancelledDownload(t *testing.T) { close(progressChan) <-progressDone } + +func TestMaxDownloadAttempts(t *testing.T) { + tests := []struct { + name string + simulateRetries int + maxDownloadAttempts int + expectedErr string + }{ + { + name: "max-attempts=5, succeed at 2nd attempt", + simulateRetries: 2, + maxDownloadAttempts: 5, + }, + { + name: "max-attempts=5, succeed at 5th attempt", + simulateRetries: 5, + maxDownloadAttempts: 5, + }, + { + name: "max-attempts=5, fail at 6th attempt", + simulateRetries: 6, + maxDownloadAttempts: 5, + expectedErr: "simulating download attempt 5/6", + }, + { + name: "max-attempts=0, fail after 1 attempt", + simulateRetries: 1, + maxDownloadAttempts: 0, + expectedErr: "simulating download attempt 1/1", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + layerStore := &mockLayerStore{make(map[layer.ChainID]*mockLayer)} + ldm := NewLayerDownloadManager( + layerStore, + maxDownloadConcurrency, + func(m *LayerDownloadManager) { + m.waitDuration = time.Millisecond + m.maxDownloadAttempts = tc.maxDownloadAttempts + }) + + progressChan := make(chan progress.Progress) + progressDone := make(chan struct{}) + + go func() { + for range progressChan { + } + close(progressDone) + }() + + var currentDownloads int32 + descriptors := downloadDescriptors(¤tDownloads) + descriptors[4].(*mockDownloadDescriptor).simulateRetries = tc.simulateRetries + + _, _, err := ldm.Download(context.Background(), *image.NewRootFS(), runtime.GOOS, descriptors, progress.ChanOutput(progressChan)) + if tc.expectedErr == "" { + assert.NilError(t, err) + } else { + assert.Error(t, err, tc.expectedErr) + } + }) + } +} diff --git a/distribution/xfer/transfer.go b/distribution/xfer/transfer.go index c356fde8d3b10..82df793ebe43f 100644 --- a/distribution/xfer/transfer.go +++ b/distribution/xfer/transfer.go @@ -42,7 +42,7 @@ type Transfer interface { Close() Done() <-chan struct{} Released() <-chan struct{} - Broadcast(masterProgressChan <-chan progress.Progress) + Broadcast(mainProgressChan <-chan progress.Progress) } type transfer struct { @@ -66,7 +66,7 @@ type transfer struct { // the transfer is no longer tracked by the transfer manager. released chan struct{} - // broadcastDone is true if the master progress channel has closed. + // broadcastDone is true if the main progress channel has closed. broadcastDone bool // closed is true if Close has been called closed bool @@ -95,14 +95,14 @@ func NewTransfer() Transfer { } // Broadcast copies the progress and error output to all viewers. -func (t *transfer) Broadcast(masterProgressChan <-chan progress.Progress) { +func (t *transfer) Broadcast(mainProgressChan <-chan progress.Progress) { for { var ( p progress.Progress ok bool ) select { - case p, ok = <-masterProgressChan: + case p, ok = <-mainProgressChan: default: // We've depleted the channel, so now we can handle // reads on broadcastSyncChan to let detaching watchers @@ -110,7 +110,7 @@ func (t *transfer) Broadcast(masterProgressChan <-chan progress.Progress) { select { case <-t.broadcastSyncChan: continue - case p, ok = <-masterProgressChan: + case p, ok = <-mainProgressChan: } } @@ -353,10 +353,10 @@ func (tm *transferManager) Transfer(key string, xferFunc DoFunc, progressOutput tm.waitingTransfers = append(tm.waitingTransfers, start) } - masterProgressChan := make(chan progress.Progress) - xfer := xferFunc(masterProgressChan, start, inactive) + mainProgressChan := make(chan progress.Progress) + xfer := xferFunc(mainProgressChan, start, inactive) watcher := xfer.Watch(progressOutput) - go xfer.Broadcast(masterProgressChan) + go xfer.Broadcast(mainProgressChan) tm.transfers[key] = xfer // When the transfer is finished, remove from the map. diff --git a/distribution/xfer/transfer_test.go b/distribution/xfer/transfer_test.go index a86e27959ea08..f8f8e96fe0fa3 100644 --- a/distribution/xfer/transfer_test.go +++ b/distribution/xfer/transfer_test.go @@ -10,11 +10,11 @@ import ( func TestTransfer(t *testing.T) { makeXferFunc := func(id string) DoFunc { - return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { + return func(progressChan chan<- progress.Progress, start <-chan struct{}, _ chan<- struct{}) Transfer { select { case <-start: default: - t.Fatalf("transfer function not started even though concurrency limit not reached") + t.Errorf("%s: transfer function not started even though concurrency limit not reached", id) } xfer := NewTransfer() @@ -38,7 +38,7 @@ func TestTransfer(t *testing.T) { for p := range progressChan { val, present := receivedProgress[p.ID] if present && p.Current <= val { - t.Fatalf("got unexpected progress value: %d (expected %d)", p.Current, val+1) + t.Errorf("%s: got unexpected progress value: %d (expected <= %d)", p.ID, p.Current, val) } receivedProgress[p.ID] = p.Current } @@ -72,13 +72,13 @@ func TestConcurrencyLimit(t *testing.T) { var runningJobs int32 makeXferFunc := func(id string) DoFunc { - return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { + return func(progressChan chan<- progress.Progress, start <-chan struct{}, _ chan<- struct{}) Transfer { xfer := NewTransfer() go func() { <-start totalJobs := atomic.AddInt32(&runningJobs, 1) if int(totalJobs) > concurrencyLimit { - t.Fatalf("too many jobs running") + t.Errorf("%s: too many jobs running (%d > %d)", id, totalJobs, concurrencyLimit) } for i := 0; i <= 10; i++ { progressChan <- progress.Progress{ID: id, Action: "testing", Current: int64(i), Total: 10} @@ -137,7 +137,7 @@ func TestInactiveJobs(t *testing.T) { <-start totalJobs := atomic.AddInt32(&runningJobs, 1) if int(totalJobs) > concurrencyLimit { - t.Fatalf("too many jobs running") + t.Errorf("%s: too many jobs running (%d > %d)", id, totalJobs, concurrencyLimit) } for i := 0; i <= 10; i++ { progressChan <- progress.Progress{ID: id, Action: "testing", Current: int64(i), Total: 10} @@ -191,7 +191,7 @@ func TestWatchRelease(t *testing.T) { ready := make(chan struct{}) makeXferFunc := func(id string) DoFunc { - return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { + return func(progressChan chan<- progress.Progress, start <-chan struct{}, _ chan<- struct{}) Transfer { xfer := NewTransfer() go func() { defer func() { @@ -280,7 +280,7 @@ func TestWatchRelease(t *testing.T) { func TestWatchFinishedTransfer(t *testing.T) { makeXferFunc := func(id string) DoFunc { - return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { + return func(progressChan chan<- progress.Progress, _ <-chan struct{}, _ chan<- struct{}) Transfer { xfer := NewTransfer() go func() { // Finish immediately @@ -322,7 +322,7 @@ func TestDuplicateTransfer(t *testing.T) { var xferFuncCalls int32 makeXferFunc := func(id string) DoFunc { - return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { + return func(progressChan chan<- progress.Progress, _ <-chan struct{}, _ chan<- struct{}) Transfer { atomic.AddInt32(&xferFuncCalls, 1) xfer := NewTransfer() go func() { diff --git a/dockerversion/version_lib.go b/dockerversion/version_lib.go index b7d46504445c7..96954560cd9b7 100644 --- a/dockerversion/version_lib.go +++ b/dockerversion/version_lib.go @@ -1,16 +1,16 @@ +//go:build !autogen // +build !autogen // Package dockerversion is auto-generated at build-time package dockerversion // import "github.com/docker/docker/dockerversion" // Default build-time variable for library-import. -// This file is overridden on build with build-time information. -const ( +// These variables are overridden on build with build-time information. +var ( GitCommit = "library-import" Version = "library-import" BuildTime = "library-import" IAmStatic = "library-import" - InitCommitID = "library-import" PlatformName = "" ProductName = "" DefaultProductLicense = "" diff --git a/docs/api/v1.18.md b/docs/api/v1.18.md index 327701427a1eb..dc5970125f808 100644 --- a/docs/api/v1.18.md +++ b/docs/api/v1.18.md @@ -19,7 +19,7 @@ redirect_from: ## 1. Brief introduction - The daemon listens on `unix:///var/run/docker.sock` but you can - [Bind Docker to another host/port or a Unix socket](../reference/commandline/dockerd.md#bind-docker-to-another-host-port-or-a-unix-socket). + [Bind Docker to another host/port or a Unix socket](https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-host-port-or-a-unix-socket). - The API tends to be REST, but for some complex commands, like `attach` or `pull`, the HTTP connection is hijacked to transport `stdout`, `stdin` and `stderr`. @@ -317,7 +317,7 @@ Create a container - **201** – no error - **400** – bad parameter -- **404** – no such container +- **404** – no such image - **406** – impossible to attach (container not running) - **409** – conflict - **500** – server error @@ -1234,7 +1234,7 @@ the path to the alternate build instructions file to use. The archive may include any number of other files, which are accessible in the build context (See the [*ADD build -command*](../reference/builder.md#add)). +command*](https://docs.docker.com/engine/reference/builder/#add)). The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, diff --git a/docs/api/v1.19.md b/docs/api/v1.19.md index f3d44555a527d..85207d52f4af6 100644 --- a/docs/api/v1.19.md +++ b/docs/api/v1.19.md @@ -19,7 +19,7 @@ redirect_from: ## 1. Brief introduction - The daemon listens on `unix:///var/run/docker.sock` but you can - [Bind Docker to another host/port or a Unix socket](../reference/commandline/dockerd.md#bind-docker-to-another-host-port-or-a-unix-socket). + [Bind Docker to another host/port or a Unix socket](https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-host-port-or-a-unix-socket). - The API tends to be REST. However, for some complex commands, like `attach` or `pull`, the HTTP connection is hijacked to transport `stdout`, `stdin` and `stderr`. @@ -327,7 +327,7 @@ Create a container - **201** – no error - **400** – bad parameter -- **404** – no such container +- **404** – no such image - **406** – impossible to attach (container not running) - **409** – conflict - **500** – server error @@ -1276,7 +1276,7 @@ the path to the alternate build instructions file to use. The archive may include any number of other files, which are accessible in the build context (See the [*ADD build -command*](../reference/builder.md#add)). +command*](https://docs.docker.com/engine/reference/builder/#add)). The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, diff --git a/docs/api/v1.20.md b/docs/api/v1.20.md index 199428121bc64..27d99995e6899 100644 --- a/docs/api/v1.20.md +++ b/docs/api/v1.20.md @@ -19,7 +19,7 @@ redirect_from: ## 1. Brief introduction - The daemon listens on `unix:///var/run/docker.sock` but you can - [Bind Docker to another host/port or a Unix socket](../reference/commandline/dockerd.md#bind-docker-to-another-host-port-or-a-unix-socket). + [Bind Docker to another host/port or a Unix socket](https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-host-port-or-a-unix-socket). - The API tends to be REST. However, for some complex commands, like `attach` or `pull`, the HTTP connection is hijacked to transport `stdout`, `stdin` and `stderr`. @@ -331,7 +331,7 @@ Create a container - **201** – no error - **400** – bad parameter -- **404** – no such container +- **404** – no such image - **406** – impossible to attach (container not running) - **409** – conflict - **500** – server error @@ -1407,7 +1407,7 @@ the path to the alternate build instructions file to use. The archive may include any number of other files, which are accessible in the build context (See the [*ADD build -command*](../reference/builder.md#add)). +command*](https://docs.docker.com/engine/reference/builder/#add)). The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, diff --git a/docs/api/v1.21.md b/docs/api/v1.21.md index 3ecfd3b9f9e33..3866da3bf6f66 100644 --- a/docs/api/v1.21.md +++ b/docs/api/v1.21.md @@ -19,7 +19,7 @@ redirect_from: ## 1. Brief introduction - The daemon listens on `unix:///var/run/docker.sock` but you can - [Bind Docker to another host/port or a Unix socket](../reference/commandline/dockerd.md#bind-docker-to-another-host-port-or-a-unix-socket). + [Bind Docker to another host/port or a Unix socket](https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-host-port-or-a-unix-socket). - The API tends to be REST. However, for some complex commands, like `attach` or `pull`, the HTTP connection is hijacked to transport `stdout`, `stdin` and `stderr`. @@ -352,7 +352,7 @@ Create a container - **201** – no error - **400** – bad parameter -- **404** – no such container +- **404** – no such image - **406** – impossible to attach (container not running) - **409** – conflict - **500** – server error @@ -1488,7 +1488,7 @@ the path to the alternate build instructions file to use. The archive may include any number of other files, which are accessible in the build context (See the [*ADD build -command*](../reference/builder.md#add)). +command*](https://docs.docker.com/engine/reference/builder/#add)). The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, @@ -1526,7 +1526,7 @@ or being killed. these values at build-time. Docker uses the `buildargs` as the environment context for command(s) run via the Dockerfile's `RUN` instruction or for variable expansion in other Dockerfile instructions. This is not meant for - passing secret values. [Read more about the buildargs instruction](../reference/builder.md#arg) + passing secret values. [Read more about the buildargs instruction](https://docs.docker.com/engine/reference/builder/#arg) **Request Headers**: diff --git a/docs/api/v1.22.md b/docs/api/v1.22.md index fc19c9f0afc07..b0f0bc88bba2f 100644 --- a/docs/api/v1.22.md +++ b/docs/api/v1.22.md @@ -19,7 +19,7 @@ redirect_from: ## 1. Brief introduction - The daemon listens on `unix:///var/run/docker.sock` but you can - [Bind Docker to another host/port or a Unix socket](../reference/commandline/dockerd.md#bind-docker-to-another-host-port-or-a-unix-socket). + [Bind Docker to another host/port or a Unix socket](https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-host-port-or-a-unix-socket). - The API tends to be REST. However, for some complex commands, like `attach` or `pull`, the HTTP connection is hijacked to transport `stdout`, `stdin` and `stderr`. @@ -467,7 +467,7 @@ Create a container - **201** – no error - **400** – bad parameter -- **404** – no such container +- **404** – no such image - **406** – impossible to attach (container not running) - **409** – conflict - **500** – server error @@ -1669,7 +1669,7 @@ the path to the alternate build instructions file to use. The archive may include any number of other files, which are accessible in the build context (See the [*ADD build -command*](../reference/builder.md#add)). +command*](https://docs.docker.com/engine/reference/builder/#add)). The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, @@ -1707,7 +1707,7 @@ or being killed. these values at build-time. Docker uses the `buildargs` as the environment context for command(s) run via the Dockerfile's `RUN` instruction or for variable expansion in other Dockerfile instructions. This is not meant for - passing secret values. [Read more about the buildargs instruction](../reference/builder.md#arg) + passing secret values. [Read more about the buildargs instruction](https://docs.docker.com/engine/reference/builder/#arg) - **shmsize** - Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB. **Request Headers**: diff --git a/docs/api/v1.23.md b/docs/api/v1.23.md index 218734aea60aa..f0859fcf814d6 100644 --- a/docs/api/v1.23.md +++ b/docs/api/v1.23.md @@ -19,7 +19,7 @@ redirect_from: ## 1. Brief introduction - The daemon listens on `unix:///var/run/docker.sock` but you can - [Bind Docker to another host/port or a Unix socket](../reference/commandline/dockerd.md#bind-docker-to-another-host-port-or-a-unix-socket). + [Bind Docker to another host/port or a Unix socket](https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-host-port-or-a-unix-socket). - The API tends to be REST. However, for some complex commands, like `attach` or `pull`, the HTTP connection is hijacked to transport `stdout`, `stdin` and `stderr`. @@ -493,7 +493,7 @@ Create a container - **201** – no error - **400** – bad parameter -- **404** – no such container +- **404** – no such image - **406** – impossible to attach (container not running) - **409** – conflict - **500** – server error @@ -1703,7 +1703,7 @@ the path to the alternate build instructions file to use. The archive may include any number of other files, which are accessible in the build context (See the [*ADD build -command*](../reference/builder.md#add)). +command*](https://docs.docker.com/engine/reference/builder/#add)). The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, @@ -1741,7 +1741,7 @@ or being killed. these values at build-time. Docker uses the `buildargs` as the environment context for command(s) run via the Dockerfile's `RUN` instruction or for variable expansion in other Dockerfile instructions. This is not meant for - passing secret values. [Read more about the buildargs instruction](../reference/builder.md#arg) + passing secret values. [Read more about the buildargs instruction](https://docs.docker.com/engine/reference/builder/#arg) - **shmsize** - Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB. - **labels** – JSON map of string pairs for labels to set on the image. diff --git a/docs/api/v1.24.md b/docs/api/v1.24.md index e320020ed6d68..5ee0600c04ea7 100644 --- a/docs/api/v1.24.md +++ b/docs/api/v1.24.md @@ -19,7 +19,7 @@ redirect_from: ## 1. Brief introduction - The daemon listens on `unix:///var/run/docker.sock` but you can - [Bind Docker to another host/port or a Unix socket](../reference/commandline/dockerd.md#bind-docker-to-another-host-port-or-a-unix-socket). + [Bind Docker to another host/port or a Unix socket](https://docs.docker.com/engine/reference/commandline/dockerd/#bind-docker-to-another-host-port-or-a-unix-socket). - The API tends to be REST. However, for some complex commands, like `attach` or `pull`, the HTTP connection is hijacked to transport `stdout`, `stdin` and `stderr`. @@ -535,7 +535,7 @@ Create a container - **201** – no error - **400** – bad parameter -- **404** – no such container +- **404** – no such image - **406** – impossible to attach (container not running) - **409** – conflict - **500** – server error @@ -1718,7 +1718,7 @@ the path to the alternate build instructions file to use. The archive may include any number of other files, which are accessible in the build context (See the [*ADD build -command*](../reference/builder.md#add)). +command*](https://docs.docker.com/engine/reference/builder/#add)). The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, @@ -1756,7 +1756,7 @@ or being killed. these values at build-time. Docker uses the `buildargs` as the environment context for command(s) run via the Dockerfile's `RUN` instruction or for variable expansion in other Dockerfile instructions. This is not meant for - passing secret values. [Read more about the buildargs instruction](../reference/builder.md#arg) + passing secret values. [Read more about the buildargs instruction](https://docs.docker.com/engine/reference/builder/#arg) - **shmsize** - Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB. - **labels** – JSON map of string pairs for labels to set on the image. diff --git a/docs/api/v1.25.yaml b/docs/api/v1.25.yaml new file mode 100644 index 0000000000000..cbcf726ab87e8 --- /dev/null +++ b/docs/api/v1.25.yaml @@ -0,0 +1,7680 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.25" +info: + title: "Docker Engine API" + version: "1.25" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + + For Docker Engine 1.13, the API version is 1.25. To lock to this version, you prefix the URL with `/v1.25`. For example, calling `/info` is the same as calling `/v1.25/info`. + + Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + + In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + + The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. + + This documentation is for version 1.25 of the API, which was introduced with Docker 1.13. Use this table to find documentation for previous versions of the API: + + Docker version | API version | Changes + ----------------|-------------|--------- + 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) + 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) + 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) + 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) + 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) + 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) + 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldly. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + - name: "Secret" + x-displayName: "Secrets" + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + default: {} + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind-mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + type: "object" + description: "A map of exposed container ports and the host port they should map to." + additionalProperties: + type: "object" + properties: + HostIp: + type: "string" + description: "The host IP address" + HostPort: + type: "string" + description: "The host port number, as a string" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: "IPC namespace to use for the container." + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: "Allocates a random host port for all of a container's exposed ports." + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + Config: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]` + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: + - "array" + - "string" + items: + type: "string" + Healthcheck: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `{}` inherit healthcheck from image or parent image + - `{"NONE"}` disable healthcheck + - `{"CMD", args...}` exec arguments directly + - `{"CMD-SHELL", command}` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: + - "array" + - "string" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkConfig: + description: "TODO: check is correct" + type: "object" + properties: + Bridge: + type: "string" + Gateway: + type: "string" + Address: + type: "string" + IPPrefixLen: + type: "integer" + MacAddress: + type: "string" + PortMapping: + type: "string" + Ports: + type: "array" + items: + $ref: "#/definitions/Port" + + GraphDriver: + description: "Information about this container's graph driver." + type: "object" + properties: + Name: + type: "string" + Data: + type: "object" + additionalProperties: + type: "string" + + Image: + type: "object" + properties: + Id: + type: "string" + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + Comment: + type: "string" + Created: + type: "string" + Container: + type: "string" + ContainerConfig: + $ref: "#/definitions/Config" + DockerVersion: + type: "string" + Author: + type: "string" + Config: + $ref: "#/definitions/Config" + Architecture: + type: "string" + Os: + type: "string" + Size: + type: "integer" + format: "int64" + VirtualSize: + type: "integer" + format: "int64" + GraphDriver: + $ref: "#/definitions/GraphDriver" + RootFS: + type: "object" + properties: + Type: + type: "string" + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + required: [Size, RefCount] + properties: + Size: + type: "integer" + description: "The disk space used by the volume (local driver only)" + default: -1 + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: "The number of containers referencing this volume." + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + NetworkContainer: + type: "object" + properties: + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + IPAMConfig: + description: "IPAM configurations for the endpoint" + type: "object" + properties: + IPv4Address: + type: "string" + IPv6Address: + type: "string" + LinkLocalIPs: + type: "array" + items: + type: "string" + Links: + type: "array" + items: + type: "string" + Aliases: + type: "array" + items: + type: "string" + NetworkID: + type: "string" + EndpointID: + type: "string" + Gateway: + type: "string" + IPAddress: + type: "string" + IPPrefixLen: + type: "integer" + IPv6Gateway: + type: "string" + GlobalIPv6Address: + type: "string" + GlobalIPv6PrefixLen: + type: "integer" + format: "int64" + MacAddress: + type: "string" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + Destination: + type: "string" + x-nullable: false + Type: + type: "string" + x-nullable: false + Options: + type: "array" + items: + type: "string" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + Name: + type: "string" + x-nullable: false + Enabled: + description: "True when the plugin is running. False when the plugin is not running, only installed." + type: "boolean" + x-nullable: false + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PropagatedMount + - Mounts + - Env + - Args + properties: + Description: + type: "string" + x-nullable: false + Documentation: + type: "string" + x-nullable: false + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + Socket: + type: "string" + x-nullable: false + Entrypoint: + type: "array" + items: + type: "string" + WorkDir: + type: "string" + x-nullable: false + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + GID: + type: "integer" + format: "uint32" + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + AllowAllDevices: + type: "boolean" + x-nullable: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + diff_ids: + type: "array" + items: + type: "string" + example: + Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: "tiborvass/sample-volume-plugin" + Tag: "latest" + Active: true + Settings: + Env: + - "DEBUG=0" + Args: null + Devices: null + Config: + Description: "A sample volume plugin for Docker" + Documentation: "https://docs.docker.com/engine/extend/plugins/" + Interface: + Types: + - "docker.volumedriver/1.0" + Socket: "plugins.sock" + Entrypoint: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: "" + User: {} + Network: + Type: "" + Linux: + Capabilities: null + AllowAllDevices: false + Devices: null + Mounts: null + PropagatedMount: "/data" + Env: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + Name: "args" + Description: "command line arguments" + Settable: null + Value: [] + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + Node: + type: "object" + properties: + ID: + type: "string" + Version: + type: "object" + properties: + Index: + type: "integer" + format: "int64" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + type: "object" + properties: + Hostname: + type: "string" + Platform: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + Resources: + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + MemoryBytes: + type: "integer" + format: "int64" + Engine: + type: "object" + properties: + EngineVersion: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + ID: "24ifsmvkjbyhk" + Version: + Index: 8 + CreatedAt: "2016-06-07T20:31:11.853781916Z" + UpdatedAt: "2016-06-07T20:31:11.999868824Z" + Spec: + Name: "my-node" + Role: "manager" + Availability: "active" + Labels: + foo: "bar" + Description: + Hostname: "bf3067039e47" + Platform: + Architecture: "x86_64" + OS: "linux" + Resources: + NanoCPUs: 4000000000 + MemoryBytes: 8272408576 + Engine: + EngineVersion: "1.13.0" + Labels: + foo: "bar" + Plugins: + - Type: "Volume" + Name: "local" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + Status: + State: "ready" + Addr: "172.17.0.2" + ManagerStatus: + Leader: true + Reachability: "reachable" + Addr: "172.17.0.2:2377" + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Orchestration: + description: "Orchestration configuration." + type: "object" + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "int64" + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "int64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "int64" + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + Dispatcher: + description: "Dispatcher configuration." + type: "object" + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + CAConfig: + description: "CA configuration." + type: "object" + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if unspecified by a service. + + Updating this value will only have an affect on new tasks. Old tasks will continue use their previously configured log driver until recreated. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + example: + Name: "default" + Orchestration: + TaskHistoryRetentionLimit: 10 + Raft: + SnapshotInterval: 10000 + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + ElectionTick: 3 + Dispatcher: + HeartbeatPeriod: 5000000000 + CAConfig: + NodeCertExpiry: 7776000000000000 + JoinTokens: + Worker: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + EncryptionConfig: + AutoLockManagers: false + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + Version: + type: "object" + properties: + Index: + type: "integer" + format: "int64" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/SwarmSpec" + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + ContainerSpec: + type: "object" + properties: + Image: + description: "The image name to use for the container." + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + type: "object" + properties: + NanoCPUs: + description: "CPU limit in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory limit in Bytes." + type: "integer" + format: "int64" + Reservation: + description: "Define resources reservation." + properties: + NanoCPUs: + description: "CPU reservation in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory reservation in Bytes." + type: "integer" + format: "int64" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + type: "object" + properties: + Index: + type: "integer" + format: "int64" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + Service: + type: "object" + properties: + ID: + type: "string" + Version: + type: "object" + properties: + Index: + type: "integer" + format: "int64" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + ImageDeleteResponse: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: "Base64-url-safe-encoded secret data" + type: "array" + items: + type: "string" + Secret: + type: "object" + properties: + ID: + type: "string" + Version: + type: "object" + properties: + Index: + type: "integer" + format: "int64" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" +paths: + /containers/json: + get: + summary: "List containers" + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. + + Available filters: + - `exited=` containers with exit code of `` + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `label=key` or `label="key=value"` of a container label + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `id=` a container's ID + - `name=` a container's name + - `is-task=`(`true`|`false`) + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `since`=(`` or ``) + - `volume`=(`` or ``) + - `network`=(`` or ``) + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/Config" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 406: + description: "impossible to attach" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: "The status of the container. For example, `running` or `exited`." + type: "string" + Running: + description: "Whether this container is running." + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriver" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/Config" + NetworkSettings: + $ref: "#/definitions/NetworkConfig" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + SecondaryIPAddresses: null + SecondaryIPv6Addresses: null + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + type: "object" + properties: + Path: + description: "Path to file that has changed" + type: "string" + Kind: + description: "Kind of change" + type: "integer" + enum: + - 0 + - 1 + - 2 + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used for calculating the CPU usage percentage. It is not the same as the `cpu_stats` field. + operationId: "ContainerStats" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveHead" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get an tar archive of a resource in the filesystem of container id." + operationId: "ContainerGetArchive" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "ContainerPutArchive" + consumes: + - "application/x-tar" + - "application/octet-stream" + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. + + Available filters: + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `before`=(`[:]`, `` or ``) + - `since`=(`[:]`, `` or ``) + - `reference`=(`[:]`) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/tar" + default: "application/tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + Id: + type: "string" + Created: + type: "integer" + format: "int64" + CreatedBy: + type: "string" + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + Comment: + type: "string" + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were referenced by that image. + + Images can't be removed if they have descendant images, are being used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponse" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `stars=` + - `is-automated=(true|false)` + - `is-official=(true|false)` + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponse" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + Architecture: + type: "string" + Containers: + type: "integer" + ContainersRunning: + type: "integer" + ContainersStopped: + type: "integer" + ContainersPaused: + type: "integer" + CpuCfsPeriod: + type: "boolean" + CpuCfsQuota: + type: "boolean" + Debug: + type: "boolean" + DiscoveryBackend: + type: "string" + DockerRootDir: + type: "string" + Driver: + type: "string" + DriverStatus: + type: "array" + items: + type: "array" + items: + type: "string" + SystemStatus: + type: "array" + items: + type: "array" + items: + type: "string" + Plugins: + type: "object" + properties: + Volume: + type: "array" + items: + type: "string" + Network: + type: "array" + items: + type: "string" + ExperimentalBuild: + type: "boolean" + HttpProxy: + type: "string" + HttpsProxy: + type: "string" + ID: + type: "string" + IPv4Forwarding: + type: "boolean" + Images: + type: "integer" + IndexServerAddress: + type: "string" + InitPath: + type: "string" + InitSha1: + type: "string" + KernelVersion: + type: "string" + Labels: + type: "array" + items: + type: "string" + MemTotal: + type: "integer" + MemoryLimit: + type: "boolean" + NCPU: + type: "integer" + NEventsListener: + type: "integer" + NFd: + type: "integer" + NGoroutines: + type: "integer" + Name: + type: "string" + NoProxy: + type: "string" + OomKillDisable: + type: "boolean" + OSType: + type: "string" + OomScoreAdj: + type: "integer" + OperatingSystem: + type: "string" + RegistryConfig: + type: "object" + properties: + IndexConfigs: + type: "object" + additionalProperties: + type: "object" + properties: + Mirrors: + type: "array" + items: + type: "string" + Name: + type: "string" + Official: + type: "boolean" + Secure: + type: "boolean" + InsecureRegistryCIDRs: + type: "array" + items: + type: "string" + SwapLimit: + type: "boolean" + SystemTime: + type: "string" + ServerVersion: + type: "string" + examples: + application/json: + Architecture: "x86_64" + ClusterStore: "etcd://localhost:2379" + CgroupDriver: "cgroupfs" + Containers: 11 + ContainersRunning: 7 + ContainersStopped: 3 + ContainersPaused: 1 + CpuCfsPeriod: true + CpuCfsQuota: true + Debug: false + DockerRootDir: "/var/lib/docker" + Driver: "btrfs" + DriverStatus: + - + - "" + ExperimentalBuild: false + HttpProxy: "http://test:test@localhost:8080" + HttpsProxy: "https://test:test@localhost:8080" + ID: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + IPv4Forwarding: true + Images: 16 + IndexServerAddress: "https://index.docker.io/v1/" + InitPath: "/usr/bin/docker" + InitSha1: "" + KernelMemory: true + KernelVersion: "3.12.0-1-amd64" + Labels: + - "storage=ssd" + MemTotal: 2099236864 + MemoryLimit: true + NCPU: 1 + NEventsListener: 0 + NFd: 11 + NGoroutines: 21 + Name: "prod-server-42" + NoProxy: "9.81.1.160" + OomKillDisable: true + OSType: "linux" + OperatingSystem: "Boot2Docker" + Plugins: + Volume: + - "local" + Network: + - "null" + - "host" + - "bridge" + RegistryConfig: + IndexConfigs: + docker.io: + Name: "docker.io" + Official: true + Secure: true + InsecureRegistryCIDRs: + - "127.0.0.0/8" + SecurityOptions: + - Key: "Name" + Value: "seccomp" + - Key: "Profile" + Value: "default" + - Key: "Name" + Value: "apparmor" + - Key: "Name" + Value: "selinux" + - Key: "Name" + Value: "userns" + ServerVersion: "1.9.0" + SwapLimit: false + SystemStatus: + - + - "State" + - "Healthy" + SystemTime: "2015-03-10T11:11:23.730591467-07:00" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "1.13.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.6.3" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.25" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: + - "text/plain" + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/Config" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach, commit, copy, create, destroy, detach, die, exec_create, exec_detach, exec_start, export, kill, oom, pause, rename, resize, restart, start, stop, top, unpause, update` + + Images report these events: `delete, import, load, pull, push, save, tag, untag` + + Volumes report these events: `create, mount, unmount, destroy` + + Networks report these events: `create, connect, disconnect, destroy` + + The Docker daemon reports these events: `reload` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `container=` container name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, or `daemon` + - `volume=` volume name or ID + - `network=` network name or ID + - `daemon=` daemon name or ID + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "" + Labels: null + Scope: "" + Options: null + UsageData: + Size: 0 + RefCount: 0 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `name=` Matches all or part of a volume name. + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches all or part of a volume + driver name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Containers: + 39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867: + EndpointID: "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda" + MacAddress: "02:42:ac:11:00:02" + IPv4Address: "172.17.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + consumes: + - "application/json" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + example: + - Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: "tiborvass/sample-volume-plugin" + Tag: "latest" + Active: true + Settings: + Env: + - "DEBUG=0" + Args: null + Devices: null + Config: + Description: "A sample volume plugin for Docker" + Documentation: "https://docs.docker.com/engine/extend/plugins/" + Interface: + Types: + - "docker.volumedriver/1.0" + Socket: "plugins.sock" + Entrypoint: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: "" + User: {} + Network: + Type: "" + Linux: + Capabilities: null + AllowAllDevices: false + Devices: null + Mounts: null + PropagatedMount: "/data" + Env: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + Name: "args" + Description: "command line arguments" + Settable: null + Value: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + description: "The tokens workers and managers need to join the swarm." + type: "object" + properties: + Worker: + description: "The token workers can use to join the swarm." + type: "string" + Manager: + description: "The token managers can use to join the swarm." + type: "string" + example: + CreatedAt: "2016-08-15T16:00:20.349727406Z" + Spec: + Dispatcher: + HeartbeatPeriod: 5000000000 + Orchestration: + TaskHistoryRetentionLimit: 10 + CAConfig: + NodeCertExpiry: 7776000000000000 + Raft: + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + SnapshotInterval: 10000 + ElectionTick: 3 + TaskDefaults: {} + EncryptionConfig: + AutoLockManagers: false + Name: "default" + JoinTokens: + Worker: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-6qmn92w6bu3jdvnglku58u11a" + Manager: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-8llk83c4wm9lwioey2s316r9l" + ID: "70ilmkj2f6sp2137c753w2nmt" + UpdatedAt: "2016-08-15T16:32:09.623207604Z" + Version: + Index: 51 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 406: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `name=` + - `label=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "server error or node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Delay: 30000000000 + Parallelism: 2 + FailureAction: "pause" + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ImageDeleteResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "details" + in: "query" + description: "Show extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `id=` + - `name=` + - `service=` + - `node=` + - `label=key` or `label="key=value"` + - `desired-state=(running | shutdown | accepted)` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 406: + description: "server error or node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 406: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] diff --git a/docs/api/v1.26.yaml b/docs/api/v1.26.yaml new file mode 100644 index 0000000000000..22d4cc4e06530 --- /dev/null +++ b/docs/api/v1.26.yaml @@ -0,0 +1,7785 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.26" +info: + title: "Docker Engine API" + version: "1.26" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + + For Docker Engine >= 1.13.1, the API version is 1.26. To lock to this version, you prefix the URL with `/v1.26`. For example, calling `/info` is the same as calling `/v1.26/info`. + + Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + + In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + + The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. + + This documentation is for version 1.26 of the API, which was introduced with Docker 1.13.1. Use this table to find documentation for previous versions of the API: + + Docker version | API version | Changes + ----------------|-------------|--------- + 1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes) + 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) + 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) + 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) + 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) + 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) + 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) + 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldly. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + - name: "Secret" + x-displayName: "Secrets" + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + default: {} + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind-mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + type: "object" + description: "A map of exposed container ports and the host port they should map to." + additionalProperties: + type: "object" + properties: + HostIp: + type: "string" + description: "The host IP address" + HostPort: + type: "string" + description: "The host port number, as a string" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: "IPC namespace to use for the container." + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: "Allocates a random host port for all of a container's exposed ports." + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + Config: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]` + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: + - "array" + - "string" + items: + type: "string" + Healthcheck: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `{}` inherit healthcheck from image or parent image + - `{"NONE"}` disable healthcheck + - `{"CMD", args...}` exec arguments directly + - `{"CMD-SHELL", command}` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: + - "array" + - "string" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkConfig: + description: "TODO: check is correct" + type: "object" + properties: + Bridge: + type: "string" + Gateway: + type: "string" + Address: + type: "string" + IPPrefixLen: + type: "integer" + MacAddress: + type: "string" + PortMapping: + type: "string" + Ports: + type: "array" + items: + $ref: "#/definitions/Port" + + GraphDriver: + description: "Information about this container's graph driver." + type: "object" + properties: + Name: + type: "string" + Data: + type: "object" + additionalProperties: + type: "string" + + Image: + type: "object" + properties: + Id: + type: "string" + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + Comment: + type: "string" + Created: + type: "string" + Container: + type: "string" + ContainerConfig: + $ref: "#/definitions/Config" + DockerVersion: + type: "string" + Author: + type: "string" + Config: + $ref: "#/definitions/Config" + Architecture: + type: "string" + Os: + type: "string" + Size: + type: "integer" + format: "int64" + VirtualSize: + type: "integer" + format: "int64" + GraphDriver: + $ref: "#/definitions/GraphDriver" + RootFS: + type: "object" + properties: + Type: + type: "string" + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + required: [Size, RefCount] + properties: + Size: + type: "integer" + description: "The disk space used by the volume (local driver only)" + default: -1 + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: "The number of containers referencing this volume." + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + NetworkContainer: + type: "object" + properties: + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + IPAMConfig: + description: "IPAM configurations for the endpoint" + type: "object" + properties: + IPv4Address: + type: "string" + IPv6Address: + type: "string" + LinkLocalIPs: + type: "array" + items: + type: "string" + Links: + type: "array" + items: + type: "string" + Aliases: + type: "array" + items: + type: "string" + NetworkID: + type: "string" + EndpointID: + type: "string" + Gateway: + type: "string" + IPAddress: + type: "string" + IPPrefixLen: + type: "integer" + IPv6Gateway: + type: "string" + GlobalIPv6Address: + type: "string" + GlobalIPv6PrefixLen: + type: "integer" + format: "int64" + MacAddress: + type: "string" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + Destination: + type: "string" + x-nullable: false + Type: + type: "string" + x-nullable: false + Options: + type: "array" + items: + type: "string" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + Name: + type: "string" + x-nullable: false + Enabled: + description: "True when the plugin is running. False when the plugin is not running, only installed." + type: "boolean" + x-nullable: false + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PropagatedMount + - Mounts + - Env + - Args + properties: + Description: + type: "string" + x-nullable: false + Documentation: + type: "string" + x-nullable: false + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + Socket: + type: "string" + x-nullable: false + Entrypoint: + type: "array" + items: + type: "string" + WorkDir: + type: "string" + x-nullable: false + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + GID: + type: "integer" + format: "uint32" + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + AllowAllDevices: + type: "boolean" + x-nullable: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + diff_ids: + type: "array" + items: + type: "string" + example: + Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: "tiborvass/sample-volume-plugin" + Tag: "latest" + Active: true + Settings: + Env: + - "DEBUG=0" + Args: null + Devices: null + Config: + Description: "A sample volume plugin for Docker" + Documentation: "https://docs.docker.com/engine/extend/plugins/" + Interface: + Types: + - "docker.volumedriver/1.0" + Socket: "plugins.sock" + Entrypoint: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: "" + User: {} + Network: + Type: "" + Linux: + Capabilities: null + AllowAllDevices: false + Devices: null + Mounts: null + PropagatedMount: "/data" + Env: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + Name: "args" + Description: "command line arguments" + Settable: null + Value: [] + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + Node: + type: "object" + properties: + ID: + type: "string" + Version: + type: "object" + properties: + Index: + type: "integer" + format: "int64" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + type: "object" + properties: + Hostname: + type: "string" + Platform: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + Resources: + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + MemoryBytes: + type: "integer" + format: "int64" + Engine: + type: "object" + properties: + EngineVersion: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + ID: "24ifsmvkjbyhk" + Version: + Index: 8 + CreatedAt: "2016-06-07T20:31:11.853781916Z" + UpdatedAt: "2016-06-07T20:31:11.999868824Z" + Spec: + Name: "my-node" + Role: "manager" + Availability: "active" + Labels: + foo: "bar" + Description: + Hostname: "bf3067039e47" + Platform: + Architecture: "x86_64" + OS: "linux" + Resources: + NanoCPUs: 4000000000 + MemoryBytes: 8272408576 + Engine: + EngineVersion: "1.13.0" + Labels: + foo: "bar" + Plugins: + - Type: "Volume" + Name: "local" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + Status: + State: "ready" + Addr: "172.17.0.2" + ManagerStatus: + Leader: true + Reachability: "reachable" + Addr: "172.17.0.2:2377" + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Orchestration: + description: "Orchestration configuration." + type: "object" + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "int64" + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "int64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "int64" + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + Dispatcher: + description: "Dispatcher configuration." + type: "object" + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + CAConfig: + description: "CA configuration." + type: "object" + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if unspecified by a service. + + Updating this value will only have an affect on new tasks. Old tasks will continue use their previously configured log driver until recreated. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + example: + Name: "default" + Orchestration: + TaskHistoryRetentionLimit: 10 + Raft: + SnapshotInterval: 10000 + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + ElectionTick: 3 + Dispatcher: + HeartbeatPeriod: 5000000000 + CAConfig: + NodeCertExpiry: 7776000000000000 + JoinTokens: + Worker: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + EncryptionConfig: + AutoLockManagers: false + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + Version: + type: "object" + properties: + Index: + type: "integer" + format: "int64" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/SwarmSpec" + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + ContainerSpec: + type: "object" + properties: + Image: + description: "The image name to use for the container." + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + type: "object" + properties: + NanoCPUs: + description: "CPU limit in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory limit in Bytes." + type: "integer" + format: "int64" + Reservation: + description: "Define resources reservation." + properties: + NanoCPUs: + description: "CPU reservation in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory reservation in Bytes." + type: "integer" + format: "int64" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + type: "object" + properties: + Index: + type: "integer" + format: "int64" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + Service: + type: "object" + properties: + ID: + type: "string" + Version: + type: "object" + properties: + Index: + type: "integer" + format: "int64" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + ImageDeleteResponse: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: "Base64-url-safe-encoded secret data" + type: "array" + items: + type: "string" + Secret: + type: "object" + properties: + ID: + type: "string" + Version: + type: "object" + properties: + Index: + type: "integer" + format: "int64" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" +paths: + /containers/json: + get: + summary: "List containers" + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. + + Available filters: + - `exited=` containers with exit code of `` + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `label=key` or `label="key=value"` of a container label + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `id=` a container's ID + - `name=` a container's name + - `is-task=`(`true`|`false`) + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `since`=(`` or ``) + - `volume`=(`` or ``) + - `network`=(`` or ``) + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/Config" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 406: + description: "impossible to attach" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: "The status of the container. For example, `running` or `exited`." + type: "string" + Running: + description: "Whether this container is running." + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriver" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/Config" + NetworkSettings: + $ref: "#/definitions/NetworkConfig" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + SecondaryIPAddresses: null + SecondaryIPv6Addresses: null + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + type: "object" + properties: + Path: + description: "Path to file that has changed" + type: "string" + Kind: + description: "Kind of change" + type: "integer" + enum: + - 0 + - 1 + - 2 + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used for calculating the CPU usage percentage. It is not the same as the `cpu_stats` field. + operationId: "ContainerStats" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveHead" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get an tar archive of a resource in the filesystem of container id." + operationId: "ContainerGetArchive" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "ContainerPutArchive" + consumes: + - "application/x-tar" + - "application/octet-stream" + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. + + Available filters: + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `before`=(`[:]`, `` or ``) + - `since`=(`[:]`, `` or ``) + - `reference`=(`[:]`) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/tar" + default: "application/tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + Id: + type: "string" + Created: + type: "integer" + format: "int64" + CreatedBy: + type: "string" + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + Comment: + type: "string" + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were referenced by that image. + + Images can't be removed if they have descendant images, are being used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponse" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `stars=` + - `is-automated=(true|false)` + - `is-official=(true|false)` + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponse" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + Architecture: + type: "string" + Containers: + type: "integer" + ContainersRunning: + type: "integer" + ContainersStopped: + type: "integer" + ContainersPaused: + type: "integer" + CpuCfsPeriod: + type: "boolean" + CpuCfsQuota: + type: "boolean" + Debug: + type: "boolean" + DiscoveryBackend: + type: "string" + DockerRootDir: + type: "string" + Driver: + type: "string" + DriverStatus: + type: "array" + items: + type: "array" + items: + type: "string" + SystemStatus: + type: "array" + items: + type: "array" + items: + type: "string" + Plugins: + type: "object" + properties: + Volume: + type: "array" + items: + type: "string" + Network: + type: "array" + items: + type: "string" + ExperimentalBuild: + type: "boolean" + HttpProxy: + type: "string" + HttpsProxy: + type: "string" + ID: + type: "string" + IPv4Forwarding: + type: "boolean" + Images: + type: "integer" + IndexServerAddress: + type: "string" + InitPath: + type: "string" + InitSha1: + type: "string" + KernelVersion: + type: "string" + Labels: + type: "array" + items: + type: "string" + MemTotal: + type: "integer" + MemoryLimit: + type: "boolean" + NCPU: + type: "integer" + NEventsListener: + type: "integer" + NFd: + type: "integer" + NGoroutines: + type: "integer" + Name: + type: "string" + NoProxy: + type: "string" + OomKillDisable: + type: "boolean" + OSType: + type: "string" + OomScoreAdj: + type: "integer" + OperatingSystem: + type: "string" + RegistryConfig: + type: "object" + properties: + IndexConfigs: + type: "object" + additionalProperties: + type: "object" + properties: + Mirrors: + type: "array" + items: + type: "string" + Name: + type: "string" + Official: + type: "boolean" + Secure: + type: "boolean" + InsecureRegistryCIDRs: + type: "array" + items: + type: "string" + SwapLimit: + type: "boolean" + SystemTime: + type: "string" + ServerVersion: + type: "string" + examples: + application/json: + Architecture: "x86_64" + ClusterStore: "etcd://localhost:2379" + CgroupDriver: "cgroupfs" + Containers: 11 + ContainersRunning: 7 + ContainersStopped: 3 + ContainersPaused: 1 + CpuCfsPeriod: true + CpuCfsQuota: true + Debug: false + DockerRootDir: "/var/lib/docker" + Driver: "btrfs" + DriverStatus: + - + - "" + ExperimentalBuild: false + HttpProxy: "http://test:test@localhost:8080" + HttpsProxy: "https://test:test@localhost:8080" + ID: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + IPv4Forwarding: true + Images: 16 + IndexServerAddress: "https://index.docker.io/v1/" + InitPath: "/usr/bin/docker" + InitSha1: "" + KernelMemory: true + KernelVersion: "3.12.0-1-amd64" + Labels: + - "storage=ssd" + MemTotal: 2099236864 + MemoryLimit: true + NCPU: 1 + NEventsListener: 0 + NFd: 11 + NGoroutines: 21 + Name: "prod-server-42" + NoProxy: "9.81.1.160" + OomKillDisable: true + OSType: "linux" + OperatingSystem: "Boot2Docker" + Plugins: + Volume: + - "local" + Network: + - "null" + - "host" + - "bridge" + RegistryConfig: + IndexConfigs: + docker.io: + Name: "docker.io" + Official: true + Secure: true + InsecureRegistryCIDRs: + - "127.0.0.0/8" + SecurityOptions: + - Key: "Name" + Value: "seccomp" + - Key: "Profile" + Value: "default" + - Key: "Name" + Value: "apparmor" + - Key: "Name" + Value: "selinux" + - Key: "Name" + Value: "userns" + ServerVersion: "1.9.0" + SwapLimit: false + SystemStatus: + - + - "State" + - "Healthy" + SystemTime: "2015-03-10T11:11:23.730591467-07:00" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "1.13.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.6.3" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.25" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: + - "text/plain" + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/Config" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach, commit, copy, create, destroy, detach, die, exec_create, exec_detach, exec_start, export, kill, oom, pause, rename, resize, restart, start, stop, top, unpause, update` + + Images report these events: `delete, import, load, pull, push, save, tag, untag` + + Volumes report these events: `create, mount, unmount, destroy` + + Networks report these events: `create, connect, disconnect, destroy` + + The Docker daemon reports these events: `reload` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `container=` container name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, or `daemon` + - `volume=` volume name or ID + - `network=` network name or ID + - `daemon=` daemon name or ID + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "" + Labels: null + Scope: "" + Options: null + UsageData: + Size: 0 + RefCount: 0 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `name=` Matches all or part of a volume name. + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches all or part of a volume + driver name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Containers: + 39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867: + EndpointID: "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda" + MacAddress: "02:42:ac:11:00:02" + IPv4Address: "172.17.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + consumes: + - "application/json" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + example: + - Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: "tiborvass/sample-volume-plugin" + Tag: "latest" + Active: true + Settings: + Env: + - "DEBUG=0" + Args: null + Devices: null + Config: + Description: "A sample volume plugin for Docker" + Documentation: "https://docs.docker.com/engine/extend/plugins/" + Interface: + Types: + - "docker.volumedriver/1.0" + Socket: "plugins.sock" + Entrypoint: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: "" + User: {} + Network: + Type: "" + Linux: + Capabilities: null + AllowAllDevices: false + Devices: null + Mounts: null + PropagatedMount: "/data" + Env: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + Name: "args" + Description: "command line arguments" + Settable: null + Value: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + description: "The tokens workers and managers need to join the swarm." + type: "object" + properties: + Worker: + description: "The token workers can use to join the swarm." + type: "string" + Manager: + description: "The token managers can use to join the swarm." + type: "string" + example: + CreatedAt: "2016-08-15T16:00:20.349727406Z" + Spec: + Dispatcher: + HeartbeatPeriod: 5000000000 + Orchestration: + TaskHistoryRetentionLimit: 10 + CAConfig: + NodeCertExpiry: 7776000000000000 + Raft: + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + SnapshotInterval: 10000 + ElectionTick: 3 + TaskDefaults: {} + EncryptionConfig: + AutoLockManagers: false + Name: "default" + JoinTokens: + Worker: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-6qmn92w6bu3jdvnglku58u11a" + Manager: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-8llk83c4wm9lwioey2s316r9l" + ID: "70ilmkj2f6sp2137c753w2nmt" + UpdatedAt: "2016-08-15T16:32:09.623207604Z" + Version: + Index: 51 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 406: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `name=` + - `label=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "server error or node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Delay: 30000000000 + Parallelism: 2 + FailureAction: "pause" + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ImageDeleteResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "details" + in: "query" + description: "Show extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `id=` + - `name=` + - `service=` + - `node=` + - `label=key` or `label="key=value"` + - `desired-state=(running | shutdown | accepted)` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 406: + description: "server error or node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 406: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] diff --git a/docs/api/v1.27.yaml b/docs/api/v1.27.yaml new file mode 100644 index 0000000000000..542251110caf1 --- /dev/null +++ b/docs/api/v1.27.yaml @@ -0,0 +1,7939 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.27" +info: + title: "Docker Engine API" + version: "1.27" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + + For Docker Engine >= 17.03.1, the API version is 1.27. To lock to this version, you prefix the URL with `/v1.27`. For example, calling `/info` is the same as calling `/v1.27/info`. + + Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + + In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + + The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. + + This documentation is for version 1.27 of the API, which was introduced with Docker 17.03.1. Use this table to find documentation for previous versions of the API: + + Docker version | API version | Changes + ----------------|-------------|--------- + 1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes) + 1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes) + 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) + 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) + 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) + 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) + 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) + 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) + 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldly. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + default: {} + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCPUs: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind-mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + type: "object" + description: "A map of exposed container ports and the host port they should map to." + additionalProperties: + type: "object" + properties: + HostIp: + type: "string" + description: "The host IP address" + HostPort: + type: "string" + description: "The host port number, as a string" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: "IPC namespace to use for the container." + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: "Allocates a random host port for all of a container's exposed ports." + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + Config: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]` + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: + - "array" + - "string" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: + - "array" + - "string" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkConfig: + description: "TODO: check is correct" + type: "object" + properties: + Bridge: + type: "string" + Gateway: + type: "string" + Address: + type: "string" + IPPrefixLen: + type: "integer" + MacAddress: + type: "string" + PortMapping: + type: "string" + Ports: + type: "array" + items: + $ref: "#/definitions/Port" + + GraphDriver: + description: "Information about this container's graph driver." + type: "object" + properties: + Name: + type: "string" + Data: + type: "object" + additionalProperties: + type: "string" + + Image: + type: "object" + properties: + Id: + type: "string" + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + Comment: + type: "string" + Created: + type: "string" + Container: + type: "string" + ContainerConfig: + $ref: "#/definitions/Config" + DockerVersion: + type: "string" + Author: + type: "string" + Config: + $ref: "#/definitions/Config" + Architecture: + type: "string" + Os: + type: "string" + Size: + type: "integer" + format: "int64" + VirtualSize: + type: "integer" + format: "int64" + GraphDriver: + $ref: "#/definitions/GraphDriver" + RootFS: + type: "object" + properties: + Type: + type: "string" + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + required: [Size, RefCount] + properties: + Size: + type: "integer" + description: "The disk space used by the volume (local driver only)" + default: -1 + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: "The number of containers referencing this volume." + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + NetworkContainer: + type: "object" + properties: + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + IPAMConfig: + description: "IPAM configurations for the endpoint" + type: "object" + properties: + IPv4Address: + type: "string" + IPv6Address: + type: "string" + LinkLocalIPs: + type: "array" + items: + type: "string" + Links: + type: "array" + items: + type: "string" + Aliases: + type: "array" + items: + type: "string" + NetworkID: + type: "string" + EndpointID: + type: "string" + Gateway: + type: "string" + IPAddress: + type: "string" + IPPrefixLen: + type: "integer" + IPv6Gateway: + type: "string" + GlobalIPv6Address: + type: "string" + GlobalIPv6PrefixLen: + type: "integer" + format: "int64" + MacAddress: + type: "string" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + Destination: + type: "string" + x-nullable: false + Type: + type: "string" + x-nullable: false + Options: + type: "array" + items: + type: "string" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + Name: + type: "string" + x-nullable: false + Enabled: + description: "True when the plugin is running. False when the plugin is not running, only installed." + type: "boolean" + x-nullable: false + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PropagatedMount + - Mounts + - Env + - Args + properties: + Description: + type: "string" + x-nullable: false + Documentation: + type: "string" + x-nullable: false + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + Socket: + type: "string" + x-nullable: false + Entrypoint: + type: "array" + items: + type: "string" + WorkDir: + type: "string" + x-nullable: false + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + GID: + type: "integer" + format: "uint32" + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + AllowAllDevices: + type: "boolean" + x-nullable: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + diff_ids: + type: "array" + items: + type: "string" + example: + Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: "tiborvass/sample-volume-plugin" + Tag: "latest" + Active: true + Settings: + Env: + - "DEBUG=0" + Args: null + Devices: null + Config: + Description: "A sample volume plugin for Docker" + Documentation: "https://docs.docker.com/engine/extend/plugins/" + Interface: + Types: + - "docker.volumedriver/1.0" + Socket: "plugins.sock" + Entrypoint: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: "" + User: {} + Network: + Type: "" + Linux: + Capabilities: null + AllowAllDevices: false + Devices: null + Mounts: null + PropagatedMount: "/data" + Env: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + Name: "args" + Description: "command line arguments" + Settable: null + Value: [] + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentially overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "int64" + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + Node: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + type: "object" + properties: + Hostname: + type: "string" + Platform: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + Resources: + type: "object" + properties: + NanoCpus: + type: "integer" + format: "int64" + MemoryBytes: + type: "integer" + format: "int64" + Engine: + type: "object" + properties: + EngineVersion: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + ID: "24ifsmvkjbyhk" + Version: + Index: 8 + CreatedAt: "2016-06-07T20:31:11.853781916Z" + UpdatedAt: "2016-06-07T20:31:11.999868824Z" + Spec: + Name: "my-node" + Role: "manager" + Availability: "active" + Labels: + foo: "bar" + Description: + Hostname: "bf3067039e47" + Platform: + Architecture: "x86_64" + OS: "linux" + Resources: + NanoCPUs: 4000000000 + MemoryBytes: 8272408576 + Engine: + EngineVersion: "1.13.0" + Labels: + foo: "bar" + Plugins: + - Type: "Volume" + Name: "local" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + Status: + State: "ready" + Addr: "172.17.0.2" + ManagerStatus: + Leader: true + Reachability: "reachable" + Addr: "172.17.0.2:2377" + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Orchestration: + description: "Orchestration configuration." + type: "object" + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "int64" + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "int64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "int64" + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + Dispatcher: + description: "Dispatcher configuration." + type: "object" + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + CAConfig: + description: "CA configuration." + type: "object" + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if unspecified by a service. + + Updating this value will only have an affect on new tasks. Old tasks will continue use their previously configured log driver until recreated. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + example: + Name: "default" + Orchestration: + TaskHistoryRetentionLimit: 10 + Raft: + SnapshotInterval: 10000 + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + ElectionTick: 3 + Dispatcher: + HeartbeatPeriod: 5000000000 + CAConfig: + NodeCertExpiry: 7776000000000000 + JoinTokens: + Worker: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + EncryptionConfig: + AutoLockManagers: false + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/SwarmSpec" + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + ContainerSpec: + type: "object" + properties: + Image: + description: "The image name to use for the container." + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. + The format of extra hosts on swarmkit is specified in: + http://man7.org/linux/man-pages/man5/hosts.5.html + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + type: "object" + properties: + NanoCPUs: + description: "CPU limit in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory limit in Bytes." + type: "integer" + format: "int64" + Reservation: + description: "Define resources reservation." + properties: + NanoCPUs: + description: "CPU reservation in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory reservation in Bytes." + type: "integer" + format: "int64" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + ImageDeleteResponse: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: "Base64-url-safe-encoded secret data" + type: "array" + items: + type: "string" + Secret: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" +paths: + /containers/json: + get: + summary: "List containers" + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/Config" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 406: + description: "impossible to attach" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: "The status of the container. For example, `running` or `exited`." + type: "string" + Running: + description: "Whether this container is running." + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriver" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/Config" + NetworkSettings: + $ref: "#/definitions/NetworkConfig" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + SecondaryIPAddresses: null + SecondaryIPv6Addresses: null + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + type: "object" + properties: + Path: + description: "Path to file that has changed" + type: "string" + Kind: + description: "Kind of change" + type: "integer" + enum: + - 0 + - 1 + - 2 + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or use -f" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveHead" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get an tar archive of a resource in the filesystem of container id." + operationId: "ContainerGetArchive" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "ContainerPutArchive" + consumes: + - "application/x-tar" + - "application/octet-stream" + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/tar" + default: "application/tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + Id: + type: "string" + Created: + type: "integer" + format: "int64" + CreatedBy: + type: "string" + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + Comment: + type: "string" + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were referenced by that image. + + Images can't be removed if they have descendant images, are being used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponse" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponse" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + Architecture: + type: "string" + Containers: + type: "integer" + ContainersRunning: + type: "integer" + ContainersStopped: + type: "integer" + ContainersPaused: + type: "integer" + CpuCfsPeriod: + type: "boolean" + CpuCfsQuota: + type: "boolean" + Debug: + type: "boolean" + DiscoveryBackend: + type: "string" + DockerRootDir: + type: "string" + Driver: + type: "string" + DriverStatus: + type: "array" + items: + type: "array" + items: + type: "string" + SystemStatus: + type: "array" + items: + type: "array" + items: + type: "string" + Plugins: + type: "object" + properties: + Volume: + type: "array" + items: + type: "string" + Network: + type: "array" + items: + type: "string" + ExperimentalBuild: + type: "boolean" + HttpProxy: + type: "string" + HttpsProxy: + type: "string" + ID: + type: "string" + IPv4Forwarding: + type: "boolean" + Images: + type: "integer" + IndexServerAddress: + type: "string" + InitPath: + type: "string" + InitSha1: + type: "string" + KernelVersion: + type: "string" + Labels: + type: "array" + items: + type: "string" + MemTotal: + type: "integer" + MemoryLimit: + type: "boolean" + NCPU: + type: "integer" + NEventsListener: + type: "integer" + NFd: + type: "integer" + NGoroutines: + type: "integer" + Name: + type: "string" + NoProxy: + type: "string" + OomKillDisable: + type: "boolean" + OSType: + type: "string" + OomScoreAdj: + type: "integer" + OperatingSystem: + type: "string" + RegistryConfig: + type: "object" + properties: + IndexConfigs: + type: "object" + additionalProperties: + type: "object" + properties: + Mirrors: + type: "array" + items: + type: "string" + Name: + type: "string" + Official: + type: "boolean" + Secure: + type: "boolean" + InsecureRegistryCIDRs: + type: "array" + items: + type: "string" + SwapLimit: + type: "boolean" + SystemTime: + type: "string" + ServerVersion: + type: "string" + examples: + application/json: + Architecture: "x86_64" + ClusterStore: "etcd://localhost:2379" + CgroupDriver: "cgroupfs" + Containers: 11 + ContainersRunning: 7 + ContainersStopped: 3 + ContainersPaused: 1 + CpuCfsPeriod: true + CpuCfsQuota: true + Debug: false + DockerRootDir: "/var/lib/docker" + Driver: "btrfs" + DriverStatus: + - + - "" + ExperimentalBuild: false + HttpProxy: "http://test:test@localhost:8080" + HttpsProxy: "https://test:test@localhost:8080" + ID: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + IPv4Forwarding: true + Images: 16 + IndexServerAddress: "https://index.docker.io/v1/" + InitPath: "/usr/bin/docker" + InitSha1: "" + KernelMemory: true + KernelVersion: "3.12.0-1-amd64" + Labels: + - "storage=ssd" + MemTotal: 2099236864 + MemoryLimit: true + NCPU: 1 + NEventsListener: 0 + NFd: 11 + NGoroutines: 21 + Name: "prod-server-42" + NoProxy: "9.81.1.160" + OomKillDisable: true + OSType: "linux" + OperatingSystem: "Boot2Docker" + Plugins: + Volume: + - "local" + Network: + - "null" + - "host" + - "bridge" + RegistryConfig: + IndexConfigs: + docker.io: + Name: "docker.io" + Official: true + Secure: true + InsecureRegistryCIDRs: + - "127.0.0.0/8" + SecurityOptions: + - Key: "Name" + Value: "seccomp" + - Key: "Profile" + Value: "default" + - Key: "Name" + Value: "apparmor" + - Key: "Name" + Value: "selinux" + - Key: "Name" + Value: "userns" + ServerVersion: "1.9.0" + SwapLimit: false + SystemStatus: + - + - "State" + - "Healthy" + SystemTime: "2015-03-10T11:11:23.730591467-07:00" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "1.13.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.6.3" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.25" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: + - "text/plain" + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/Config" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach, commit, copy, create, destroy, detach, die, exec_create, exec_detach, exec_start, export, health_status, kill, oom, pause, rename, resize, restart, start, stop, top, unpause, update` + + Images report these events: `delete, import, load, pull, push, save, tag, untag` + + Volumes report these events: `create, mount, unmount, destroy` + + Networks report these events: `create, connect, disconnect, destroy` + + The Docker daemon reports these events: `reload` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `plugin`= plugin name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, or `daemon` + - `volume=` volume name or ID + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "" + Labels: null + Scope: "" + Options: null + UsageData: + Size: 0 + RefCount: 0 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Containers: + 39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867: + EndpointID: "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda" + MacAddress: "02:42:ac:11:00:02" + IPv4Address: "172.17.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + consumes: + - "application/json" + produces: + - "application/json" + operationId: "NetworkPrune" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + example: + - Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: "tiborvass/sample-volume-plugin" + Tag: "latest" + Active: true + Settings: + Env: + - "DEBUG=0" + Args: null + Devices: null + Config: + Description: "A sample volume plugin for Docker" + Documentation: "https://docs.docker.com/engine/extend/plugins/" + Interface: + Types: + - "docker.volumedriver/1.0" + Socket: "plugins.sock" + Entrypoint: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: "" + User: {} + Network: + Type: "" + Linux: + Capabilities: null + AllowAllDevices: false + Devices: null + Mounts: null + PropagatedMount: "/data" + Env: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + Name: "args" + Description: "command line arguments" + Settable: null + Value: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + description: "The tokens workers and managers need to join the swarm." + type: "object" + properties: + Worker: + description: "The token workers can use to join the swarm." + type: "string" + Manager: + description: "The token managers can use to join the swarm." + type: "string" + example: + CreatedAt: "2016-08-15T16:00:20.349727406Z" + Spec: + Dispatcher: + HeartbeatPeriod: 5000000000 + Orchestration: + TaskHistoryRetentionLimit: 10 + CAConfig: + NodeCertExpiry: 7776000000000000 + Raft: + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + SnapshotInterval: 10000 + ElectionTick: 3 + TaskDefaults: {} + EncryptionConfig: + AutoLockManagers: false + Name: "default" + JoinTokens: + Worker: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-6qmn92w6bu3jdvnglku58u11a" + Manager: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-8llk83c4wm9lwioey2s316r9l" + ID: "70ilmkj2f6sp2137c753w2nmt" + UpdatedAt: "2016-08-15T16:32:09.623207604Z" + Version: + Index: 51 + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Delay: 30000000000 + Parallelism: 2 + FailureAction: "pause" + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ImageDeleteResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "details" + in: "query" + description: "Show extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] diff --git a/docs/api/v1.28.yaml b/docs/api/v1.28.yaml new file mode 100644 index 0000000000000..ed96d079c914b --- /dev/null +++ b/docs/api/v1.28.yaml @@ -0,0 +1,8106 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.28" +info: + title: "Docker Engine API" + version: "1.28" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + + For Docker Engine 17.04, the API version is 1.28. To lock to this version, you prefix the URL with `/v1.28`. For example, calling `/info` is the same as calling `/v1.28/info`. + + Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + + In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + + The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. + + This documentation is for version 1.28 of the API, which was introduced with Docker 17.04. Use this table to find documentation for previous versions of the API: + + Docker version | API version | Changes + ----------------|-------------|--------- + 17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes) + 1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes) + 1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes) + 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) + 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) + 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) + 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) + 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) + 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) + 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + default: {} + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or not less than 1000000000(1s). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or not less than 1000000000(1s). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind-mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + type: "object" + description: "A map of exposed container ports and the host port they should map to." + additionalProperties: + type: "object" + properties: + HostIp: + type: "string" + description: "The host IP address" + HostPort: + type: "string" + description: "The host port number, as a string" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: "IPC namespace to use for the container." + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: "Allocates a random host port for all of a container's exposed ports." + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + Config: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]` + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: + - "array" + - "string" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: + - "array" + - "string" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkConfig: + description: "TODO: check is correct" + type: "object" + properties: + Bridge: + type: "string" + Gateway: + type: "string" + Address: + type: "string" + IPPrefixLen: + type: "integer" + MacAddress: + type: "string" + PortMapping: + type: "string" + Ports: + type: "array" + items: + $ref: "#/definitions/Port" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/Config" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/Config" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + required: [Size, RefCount] + properties: + Size: + type: "integer" + description: "The disk space used by the volume (local driver only)" + default: -1 + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: "The number of containers referencing this volume." + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + NetworkContainer: + type: "object" + properties: + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + IPAMConfig: + description: "IPAM configurations for the endpoint" + type: "object" + properties: + IPv4Address: + type: "string" + IPv6Address: + type: "string" + LinkLocalIPs: + type: "array" + items: + type: "string" + Links: + type: "array" + items: + type: "string" + Aliases: + type: "array" + items: + type: "string" + NetworkID: + type: "string" + EndpointID: + type: "string" + Gateway: + type: "string" + IPAddress: + type: "string" + IPPrefixLen: + type: "integer" + IPv6Gateway: + type: "string" + GlobalIPv6Address: + type: "string" + GlobalIPv6PrefixLen: + type: "integer" + format: "int64" + MacAddress: + type: "string" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + Destination: + type: "string" + x-nullable: false + Type: + type: "string" + x-nullable: false + Options: + type: "array" + items: + type: "string" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + Name: + type: "string" + x-nullable: false + Enabled: + description: "True when the plugin is running. False when the plugin is not running, only installed." + type: "boolean" + x-nullable: false + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PropagatedMount + - Mounts + - Env + - Args + properties: + Description: + type: "string" + x-nullable: false + Documentation: + type: "string" + x-nullable: false + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + Socket: + type: "string" + x-nullable: false + Entrypoint: + type: "array" + items: + type: "string" + WorkDir: + type: "string" + x-nullable: false + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + GID: + type: "integer" + format: "uint32" + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + AllowAllDevices: + type: "boolean" + x-nullable: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + diff_ids: + type: "array" + items: + type: "string" + example: + Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: "tiborvass/sample-volume-plugin" + Tag: "latest" + Active: true + Settings: + Env: + - "DEBUG=0" + Args: null + Devices: null + Config: + Description: "A sample volume plugin for Docker" + Documentation: "https://docs.docker.com/engine/extend/plugins/" + Interface: + Types: + - "docker.volumedriver/1.0" + Socket: "plugins.sock" + Entrypoint: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: "" + User: {} + Network: + Type: "" + Linux: + Capabilities: null + AllowAllDevices: false + Devices: null + Mounts: null + PropagatedMount: "/data" + Env: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + Name: "args" + Description: "command line arguments" + Settable: null + Value: [] + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentially overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "int64" + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + Node: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + type: "object" + properties: + Hostname: + type: "string" + Platform: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + Resources: + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + MemoryBytes: + type: "integer" + format: "int64" + Engine: + type: "object" + properties: + EngineVersion: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + ID: "24ifsmvkjbyhk" + Version: + Index: 8 + CreatedAt: "2016-06-07T20:31:11.853781916Z" + UpdatedAt: "2016-06-07T20:31:11.999868824Z" + Spec: + Name: "my-node" + Role: "manager" + Availability: "active" + Labels: + foo: "bar" + Description: + Hostname: "bf3067039e47" + Platform: + Architecture: "x86_64" + OS: "linux" + Resources: + NanoCPUs: 4000000000 + MemoryBytes: 8272408576 + Engine: + EngineVersion: "17.04.0" + Labels: + foo: "bar" + Plugins: + - Type: "Volume" + Name: "local" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + Status: + State: "ready" + Addr: "172.17.0.2" + ManagerStatus: + Leader: true + Reachability: "reachable" + Addr: "172.17.0.2:2377" + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Orchestration: + description: "Orchestration configuration." + type: "object" + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "int64" + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "int64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "int64" + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + Dispatcher: + description: "Dispatcher configuration." + type: "object" + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + CAConfig: + description: "CA configuration." + type: "object" + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if unspecified by a service. + + Updating this value will only have an affect on new tasks. Old tasks will continue use their previously configured log driver until recreated. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + example: + Name: "default" + Orchestration: + TaskHistoryRetentionLimit: 10 + Raft: + SnapshotInterval: 10000 + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + ElectionTick: 3 + Dispatcher: + HeartbeatPeriod: 5000000000 + CAConfig: + NodeCertExpiry: 7776000000000000 + JoinTokens: + Worker: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + EncryptionConfig: + AutoLockManagers: false + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/SwarmSpec" + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + ContainerSpec: + type: "object" + properties: + Image: + description: "The image name to use for the container." + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. + The format of extra hosts on swarmkit is specified in: + http://man7.org/linux/man-pages/man5/hosts.5.html + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + type: "object" + properties: + NanoCPUs: + description: "CPU limit in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory limit in Bytes." + type: "integer" + format: "int64" + Reservation: + description: "Define resources reservation." + properties: + NanoCPUs: + description: "CPU reservation in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory reservation in Bytes." + type: "integer" + format: "int64" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: "Base64-url-safe-encoded secret data" + type: "array" + items: + type: "string" + Secret: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" +paths: + /containers/json: + get: + summary: "List containers" + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/Config" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 406: + description: "impossible to attach" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: "The status of the container. For example, `running` or `exited`." + type: "string" + Running: + description: "Whether this container is running." + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/Config" + NetworkSettings: + $ref: "#/definitions/NetworkConfig" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + SecondaryIPAddresses: null + SecondaryIPv6Addresses: null + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + Architecture: + type: "string" + Containers: + type: "integer" + ContainersRunning: + type: "integer" + ContainersStopped: + type: "integer" + ContainersPaused: + type: "integer" + CpuCfsPeriod: + type: "boolean" + CpuCfsQuota: + type: "boolean" + Debug: + type: "boolean" + DiscoveryBackend: + type: "string" + DockerRootDir: + type: "string" + Driver: + type: "string" + DriverStatus: + type: "array" + items: + type: "array" + items: + type: "string" + SystemStatus: + type: "array" + items: + type: "array" + items: + type: "string" + Plugins: + type: "object" + properties: + Volume: + type: "array" + items: + type: "string" + Network: + type: "array" + items: + type: "string" + ExperimentalBuild: + type: "boolean" + HttpProxy: + type: "string" + HttpsProxy: + type: "string" + ID: + type: "string" + IPv4Forwarding: + type: "boolean" + Images: + type: "integer" + IndexServerAddress: + type: "string" + InitPath: + type: "string" + InitSha1: + type: "string" + KernelVersion: + type: "string" + Labels: + type: "array" + items: + type: "string" + MemTotal: + type: "integer" + MemoryLimit: + type: "boolean" + NCPU: + type: "integer" + NEventsListener: + type: "integer" + NFd: + type: "integer" + NGoroutines: + type: "integer" + Name: + type: "string" + NoProxy: + type: "string" + OomKillDisable: + type: "boolean" + OSType: + type: "string" + OomScoreAdj: + type: "integer" + OperatingSystem: + type: "string" + RegistryConfig: + type: "object" + properties: + IndexConfigs: + type: "object" + additionalProperties: + type: "object" + properties: + Mirrors: + type: "array" + items: + type: "string" + Name: + type: "string" + Official: + type: "boolean" + Secure: + type: "boolean" + InsecureRegistryCIDRs: + type: "array" + items: + type: "string" + SwapLimit: + type: "boolean" + SystemTime: + type: "string" + ServerVersion: + type: "string" + examples: + application/json: + Architecture: "x86_64" + ClusterStore: "etcd://localhost:2379" + CgroupDriver: "cgroupfs" + Containers: 11 + ContainersRunning: 7 + ContainersStopped: 3 + ContainersPaused: 1 + CpuCfsPeriod: true + CpuCfsQuota: true + Debug: false + DockerRootDir: "/var/lib/docker" + Driver: "btrfs" + DriverStatus: + - + - "" + ExperimentalBuild: false + HttpProxy: "http://test:test@localhost:8080" + HttpsProxy: "https://test:test@localhost:8080" + ID: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + IPv4Forwarding: true + Images: 16 + IndexServerAddress: "https://index.docker.io/v1/" + InitPath: "/usr/bin/docker" + InitSha1: "" + KernelMemory: true + KernelVersion: "3.12.0-1-amd64" + Labels: + - "storage=ssd" + MemTotal: 2099236864 + MemoryLimit: true + NCPU: 1 + NEventsListener: 0 + NFd: 11 + NGoroutines: 21 + Name: "prod-server-42" + NoProxy: "9.81.1.160" + OomKillDisable: true + OSType: "linux" + OperatingSystem: "Boot2Docker" + Plugins: + Volume: + - "local" + Network: + - "null" + - "host" + - "bridge" + RegistryConfig: + IndexConfigs: + docker.io: + Name: "docker.io" + Official: true + Secure: true + InsecureRegistryCIDRs: + - "127.0.0.0/8" + SecurityOptions: + - Key: "Name" + Value: "seccomp" + - Key: "Profile" + Value: "default" + - Key: "Name" + Value: "apparmor" + - Key: "Name" + Value: "selinux" + - Key: "Name" + Value: "userns" + ServerVersion: "1.9.0" + SwapLimit: false + SystemStatus: + - + - "State" + - "Healthy" + SystemTime: "2015-03-10T11:11:23.730591467-07:00" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/Config" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach, commit, copy, create, destroy, detach, die, exec_create, exec_detach, exec_start, export, health_status, kill, oom, pause, rename, resize, restart, start, stop, top, unpause, update` + + Images report these events: `delete, import, load, pull, push, save, tag, untag` + + Volumes report these events: `create, mount, unmount, destroy` + + Networks report these events: `create, connect, disconnect, destroy` + + The Docker daemon reports these events: `reload` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `plugin`= plugin name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, or `daemon` + - `volume=` volume name or ID + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "" + Labels: null + Scope: "" + Options: null + UsageData: + Size: 0 + RefCount: 0 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Containers: + 39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867: + EndpointID: "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda" + MacAddress: "02:42:ac:11:00:02" + IPv4Address: "172.17.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + example: + - Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: "tiborvass/sample-volume-plugin" + Tag: "latest" + Active: true + Settings: + Env: + - "DEBUG=0" + Args: null + Devices: null + Config: + Description: "A sample volume plugin for Docker" + Documentation: "https://docs.docker.com/engine/extend/plugins/" + Interface: + Types: + - "docker.volumedriver/1.0" + Socket: "plugins.sock" + Entrypoint: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: "" + User: {} + Network: + Type: "" + Linux: + Capabilities: null + AllowAllDevices: false + Devices: null + Mounts: null + PropagatedMount: "/data" + Env: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + Name: "args" + Description: "command line arguments" + Settable: null + Value: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + description: "The tokens workers and managers need to join the swarm." + type: "object" + properties: + Worker: + description: "The token workers can use to join the swarm." + type: "string" + Manager: + description: "The token managers can use to join the swarm." + type: "string" + example: + CreatedAt: "2016-08-15T16:00:20.349727406Z" + Spec: + Dispatcher: + HeartbeatPeriod: 5000000000 + Orchestration: + TaskHistoryRetentionLimit: 10 + CAConfig: + NodeCertExpiry: 7776000000000000 + Raft: + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + SnapshotInterval: 10000 + ElectionTick: 3 + TaskDefaults: {} + EncryptionConfig: + AutoLockManagers: false + Name: "default" + JoinTokens: + Worker: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-6qmn92w6bu3jdvnglku58u11a" + Manager: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-8llk83c4wm9lwioey2s316r9l" + ID: "70ilmkj2f6sp2137c753w2nmt" + UpdatedAt: "2016-08-15T16:32:09.623207604Z" + Version: + Index: 51 + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "details" + in: "query" + description: "Show extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] diff --git a/docs/api/v1.29.yaml b/docs/api/v1.29.yaml new file mode 100644 index 0000000000000..77f6cbea12576 --- /dev/null +++ b/docs/api/v1.29.yaml @@ -0,0 +1,8238 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.29" +info: + title: "Docker Engine API" + version: "1.29" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + + For Docker Engine 17.05, the API version is 1.29. To lock to this version, you prefix the URL with `/v1.29`. For example, calling `/info` is the same as calling `/v1.29/info`. + + Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + + In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + + The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. + + This documentation is for version 1.29 of the API, which was introduced with Docker 17.05. Use this table to find documentation for previous versions of the API: + + Docker version | API version | Changes + ----------------|-------------|--------- + 17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes) + 17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes) + 1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes) + 1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes) + 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) + 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) + 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) + 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) + 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) + 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) + 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + default: {} + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or not less than 1000000000(1s). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or not less than 1000000000(1s). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + StartPeriod: + description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind-mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + type: "object" + description: "A map of exposed container ports and the host port they should map to." + additionalProperties: + type: "object" + properties: + HostIp: + type: "string" + description: "The host IP address" + HostPort: + type: "string" + description: "The host port number, as a string" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: "IPC namespace to use for the container." + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: "Allocates a random host port for all of a container's exposed ports." + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + Config: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: + - "array" + - "string" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: + - "array" + - "string" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkConfig: + description: "TODO: check is correct" + type: "object" + properties: + Bridge: + type: "string" + Gateway: + type: "string" + Address: + type: "string" + IPPrefixLen: + type: "integer" + MacAddress: + type: "string" + PortMapping: + type: "string" + Ports: + type: "array" + items: + $ref: "#/definitions/Port" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/Config" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/Config" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + required: [Size, RefCount] + properties: + Size: + type: "integer" + description: "The disk space used by the volume (local driver only)" + default: -1 + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: "The number of containers referencing this volume." + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + NetworkContainer: + type: "object" + properties: + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + IPAMConfig: + description: "IPAM configurations for the endpoint" + type: "object" + properties: + IPv4Address: + type: "string" + IPv6Address: + type: "string" + LinkLocalIPs: + type: "array" + items: + type: "string" + Links: + type: "array" + items: + type: "string" + Aliases: + type: "array" + items: + type: "string" + NetworkID: + type: "string" + EndpointID: + type: "string" + Gateway: + type: "string" + IPAddress: + type: "string" + IPPrefixLen: + type: "integer" + IPv6Gateway: + type: "string" + GlobalIPv6Address: + type: "string" + GlobalIPv6PrefixLen: + type: "integer" + format: "int64" + MacAddress: + type: "string" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + Destination: + type: "string" + x-nullable: false + Type: + type: "string" + x-nullable: false + Options: + type: "array" + items: + type: "string" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + Name: + type: "string" + x-nullable: false + Enabled: + description: "True when the plugin is running. False when the plugin is not running, only installed." + type: "boolean" + x-nullable: false + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Documentation: + type: "string" + x-nullable: false + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + Socket: + type: "string" + x-nullable: false + Entrypoint: + type: "array" + items: + type: "string" + WorkDir: + type: "string" + x-nullable: false + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + GID: + type: "integer" + format: "uint32" + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + AllowAllDevices: + type: "boolean" + x-nullable: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + IpcHost: + type: "boolean" + x-nullable: false + PidHost: + type: "boolean" + x-nullable: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + diff_ids: + type: "array" + items: + type: "string" + example: + Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: "tiborvass/sample-volume-plugin" + Tag: "latest" + Active: true + Settings: + Env: + - "DEBUG=0" + Args: null + Devices: null + Config: + Description: "A sample volume plugin for Docker" + Documentation: "https://docs.docker.com/engine/extend/plugins/" + Interface: + Types: + - "docker.volumedriver/1.0" + Socket: "plugins.sock" + Entrypoint: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: "" + User: {} + Network: + Type: "" + Linux: + Capabilities: null + AllowAllDevices: false + Devices: null + Mounts: null + PropagatedMount: "/data" + Env: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + Name: "args" + Description: "command line arguments" + Settable: null + Value: [] + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentially overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "int64" + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + Node: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + type: "object" + properties: + Hostname: + type: "string" + Platform: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + Resources: + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + MemoryBytes: + type: "integer" + format: "int64" + Engine: + type: "object" + properties: + EngineVersion: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + ID: "24ifsmvkjbyhk" + Version: + Index: 8 + CreatedAt: "2016-06-07T20:31:11.853781916Z" + UpdatedAt: "2016-06-07T20:31:11.999868824Z" + Spec: + Name: "my-node" + Role: "manager" + Availability: "active" + Labels: + foo: "bar" + Description: + Hostname: "bf3067039e47" + Platform: + Architecture: "x86_64" + OS: "linux" + Resources: + NanoCPUs: 4000000000 + MemoryBytes: 8272408576 + Engine: + EngineVersion: "17.04.0" + Labels: + foo: "bar" + Plugins: + - Type: "Volume" + Name: "local" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + Status: + State: "ready" + Addr: "172.17.0.2" + ManagerStatus: + Leader: true + Reachability: "reachable" + Addr: "172.17.0.2:2377" + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Orchestration: + description: "Orchestration configuration." + type: "object" + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "int64" + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "int64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "int64" + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + Dispatcher: + description: "Dispatcher configuration." + type: "object" + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + CAConfig: + description: "CA configuration." + type: "object" + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if unspecified by a service. + + Updating this value will only have an affect on new tasks. Old tasks will continue use their previously configured log driver until recreated. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + example: + Name: "default" + Orchestration: + TaskHistoryRetentionLimit: 10 + Raft: + SnapshotInterval: 10000 + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + ElectionTick: 3 + Dispatcher: + HeartbeatPeriod: 5000000000 + CAConfig: + NodeCertExpiry: 7776000000000000 + JoinTokens: + Worker: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + EncryptionConfig: + AutoLockManagers: false + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/SwarmSpec" + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + ContainerSpec: + type: "object" + properties: + Image: + description: "The image name to use for the container." + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. + The format of extra hosts on swarmkit is specified in: + http://man7.org/linux/man-pages/man5/hosts.5.html + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + type: "object" + properties: + NanoCPUs: + description: "CPU limit in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory limit in Bytes." + type: "integer" + format: "int64" + Reservation: + description: "Define resources reservation." + properties: + NanoCPUs: + description: "CPU reservation in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory reservation in Bytes." + type: "integer" + format: "int64" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: "Base64-url-safe-encoded secret data" + type: "array" + items: + type: "string" + Secret: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" +paths: + /containers/json: + get: + summary: "List containers" + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/Config" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 406: + description: "impossible to attach" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: "The status of the container. For example, `running` or `exited`." + type: "string" + Running: + description: "Whether this container is running." + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/Config" + NetworkSettings: + $ref: "#/definitions/NetworkConfig" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + SecondaryIPAddresses: null + SecondaryIPv6Addresses: null + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + Architecture: + type: "string" + Containers: + type: "integer" + ContainersRunning: + type: "integer" + ContainersStopped: + type: "integer" + ContainersPaused: + type: "integer" + CpuCfsPeriod: + type: "boolean" + CpuCfsQuota: + type: "boolean" + Debug: + type: "boolean" + DiscoveryBackend: + type: "string" + DockerRootDir: + type: "string" + Driver: + type: "string" + DriverStatus: + type: "array" + items: + type: "array" + items: + type: "string" + SystemStatus: + type: "array" + items: + type: "array" + items: + type: "string" + Plugins: + type: "object" + properties: + Volume: + type: "array" + items: + type: "string" + Network: + type: "array" + items: + type: "string" + ExperimentalBuild: + type: "boolean" + HttpProxy: + type: "string" + HttpsProxy: + type: "string" + ID: + type: "string" + IPv4Forwarding: + type: "boolean" + Images: + type: "integer" + IndexServerAddress: + type: "string" + InitPath: + type: "string" + InitSha1: + type: "string" + KernelVersion: + type: "string" + Labels: + type: "array" + items: + type: "string" + MemTotal: + type: "integer" + MemoryLimit: + type: "boolean" + NCPU: + type: "integer" + NEventsListener: + type: "integer" + NFd: + type: "integer" + NGoroutines: + type: "integer" + Name: + type: "string" + NoProxy: + type: "string" + OomKillDisable: + type: "boolean" + OSType: + type: "string" + OomScoreAdj: + type: "integer" + OperatingSystem: + type: "string" + RegistryConfig: + type: "object" + properties: + IndexConfigs: + type: "object" + additionalProperties: + type: "object" + properties: + Mirrors: + type: "array" + items: + type: "string" + Name: + type: "string" + Official: + type: "boolean" + Secure: + type: "boolean" + InsecureRegistryCIDRs: + type: "array" + items: + type: "string" + SwapLimit: + type: "boolean" + SystemTime: + type: "string" + ServerVersion: + type: "string" + examples: + application/json: + Architecture: "x86_64" + ClusterStore: "etcd://localhost:2379" + CgroupDriver: "cgroupfs" + Containers: 11 + ContainersRunning: 7 + ContainersStopped: 3 + ContainersPaused: 1 + CpuCfsPeriod: true + CpuCfsQuota: true + Debug: false + DockerRootDir: "/var/lib/docker" + Driver: "btrfs" + DriverStatus: + - + - "" + ExperimentalBuild: false + HttpProxy: "http://test:test@localhost:8080" + HttpsProxy: "https://test:test@localhost:8080" + ID: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + IPv4Forwarding: true + Images: 16 + IndexServerAddress: "https://index.docker.io/v1/" + InitPath: "/usr/bin/docker" + InitSha1: "" + KernelMemory: true + KernelVersion: "3.12.0-1-amd64" + Labels: + - "storage=ssd" + MemTotal: 2099236864 + MemoryLimit: true + NCPU: 1 + NEventsListener: 0 + NFd: 11 + NGoroutines: 21 + Name: "prod-server-42" + NoProxy: "9.81.1.160" + OomKillDisable: true + OSType: "linux" + OperatingSystem: "Boot2Docker" + Plugins: + Volume: + - "local" + Network: + - "null" + - "host" + - "bridge" + RegistryConfig: + IndexConfigs: + docker.io: + Name: "docker.io" + Official: true + Secure: true + InsecureRegistryCIDRs: + - "127.0.0.0/8" + SecurityOptions: + - Key: "Name" + Value: "seccomp" + - Key: "Profile" + Value: "default" + - Key: "Name" + Value: "apparmor" + - Key: "Name" + Value: "selinux" + - Key: "Name" + Value: "userns" + ServerVersion: "1.9.0" + SwapLimit: false + SystemStatus: + - + - "State" + - "Healthy" + SystemTime: "2015-03-10T11:11:23.730591467-07:00" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/Config" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach, commit, copy, create, destroy, detach, die, exec_create, exec_detach, exec_start, export, health_status, kill, oom, pause, rename, resize, restart, start, stop, top, unpause, update` + + Images report these events: `delete, import, load, pull, push, save, tag, untag` + + Volumes report these events: `create, mount, unmount, destroy` + + Networks report these events: `create, connect, disconnect, destroy` + + The Docker daemon reports these events: `reload` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `plugin`= plugin name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, or `daemon` + - `volume=` volume name or ID + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "" + Labels: null + Scope: "" + Options: null + UsageData: + Size: 0 + RefCount: 0 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Containers: + 39b69226f9d79f5634485fb236a23b2fe4e96a0a94128390a7fbbcc167065867: + EndpointID: "ed2419a97c1d9954d05b46e462e7002ea552f216e9b136b80a7db8d98b442eda" + MacAddress: "02:42:ac:11:00:02" + IPv4Address: "172.17.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + Ingress: + description: "Ingress network is the network which provides the routing-mesh in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + example: + - Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: "tiborvass/sample-volume-plugin" + Tag: "latest" + Active: true + Settings: + Env: + - "DEBUG=0" + Args: null + Devices: null + Config: + Description: "A sample volume plugin for Docker" + Documentation: "https://docs.docker.com/engine/extend/plugins/" + Interface: + Types: + - "docker.volumedriver/1.0" + Socket: "plugins.sock" + Entrypoint: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: "" + User: {} + Network: + Type: "" + Linux: + Capabilities: null + AllowAllDevices: false + Devices: null + Mounts: null + PropagatedMount: "/data" + Env: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + Name: "args" + Description: "command line arguments" + Settable: null + Value: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + description: "The tokens workers and managers need to join the swarm." + type: "object" + properties: + Worker: + description: "The token workers can use to join the swarm." + type: "string" + Manager: + description: "The token managers can use to join the swarm." + type: "string" + example: + CreatedAt: "2016-08-15T16:00:20.349727406Z" + Spec: + Dispatcher: + HeartbeatPeriod: 5000000000 + Orchestration: + TaskHistoryRetentionLimit: 10 + CAConfig: + NodeCertExpiry: 7776000000000000 + Raft: + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + SnapshotInterval: 10000 + ElectionTick: 3 + TaskDefaults: {} + EncryptionConfig: + AutoLockManagers: false + Name: "default" + JoinTokens: + Worker: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-6qmn92w6bu3jdvnglku58u11a" + Manager: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-8llk83c4wm9lwioey2s316r9l" + ID: "70ilmkj2f6sp2137c753w2nmt" + UpdatedAt: "2016-08-15T16:32:09.623207604Z" + Version: + Index: 51 + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] diff --git a/docs/api/v1.30.yaml b/docs/api/v1.30.yaml new file mode 100644 index 0000000000000..007564d3529a9 --- /dev/null +++ b/docs/api/v1.30.yaml @@ -0,0 +1,8776 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.30" +info: + title: "Docker Engine API" + version: "1.30" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + + For Docker Engine 17.06, the API version is 1.30. To lock to this version, you prefix the URL with `/v1.30`. For example, calling `/info` is the same as calling `/v1.30/info`. + + Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + + In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + + The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. + + This documentation is for version 1.30 of the API, which was introduced with Docker 17.06. Use this table to find documentation for previous versions of the API: + + Docker version | API version | Changes + ----------------|-------------|--------- + 17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes) + 17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes) + 17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes) + 1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes) + 1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes) + 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) + 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) + 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) + 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) + 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) + 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) + 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + StartPeriod: + description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind-mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + type: "object" + description: "A map of exposed container ports and the host port they should map to." + additionalProperties: + type: "object" + properties: + HostIp: + type: "string" + description: "The host IP address" + HostPort: + type: "string" + description: "The host port number, as a string" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: "IPC namespace to use for the container." + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: "Allocates a random host port for all of a container's exposed ports." + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: + - "array" + - "string" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: + - "array" + - "string" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkConfig: + description: "TODO: check is correct" + type: "object" + properties: + Bridge: + type: "string" + Gateway: + type: "string" + Address: + type: "string" + IPPrefixLen: + type: "integer" + MacAddress: + type: "string" + PortMapping: + type: "string" + Ports: + type: "array" + items: + $ref: "#/definitions/Port" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + IPAMConfig: + description: "IPAM configurations for the endpoint" + type: "object" + properties: + IPv4Address: + type: "string" + IPv6Address: + type: "string" + LinkLocalIPs: + type: "array" + items: + type: "string" + Links: + type: "array" + items: + type: "string" + Aliases: + type: "array" + items: + type: "string" + NetworkID: + type: "string" + EndpointID: + type: "string" + Gateway: + type: "string" + IPAddress: + type: "string" + IPPrefixLen: + type: "integer" + IPv6Gateway: + type: "string" + GlobalIPv6Address: + type: "string" + GlobalIPv6PrefixLen: + type: "integer" + format: "int64" + MacAddress: + type: "string" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: "True if the plugin is running. False if the plugin is not running, only installed." + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentially overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "int64" + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + Node: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + type: "object" + properties: + Hostname: + type: "string" + Platform: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + Resources: + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + MemoryBytes: + type: "integer" + format: "int64" + Engine: + type: "object" + properties: + EngineVersion: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + TLSInfo: + $ref: "#/definitions/SwarmSpec" + example: + ID: "24ifsmvkjbyhk" + Version: + Index: 8 + CreatedAt: "2016-06-07T20:31:11.853781916Z" + UpdatedAt: "2016-06-07T20:31:11.999868824Z" + Spec: + Name: "my-node" + Role: "manager" + Availability: "active" + Labels: + foo: "bar" + Description: + Hostname: "bf3067039e47" + Platform: + Architecture: "x86_64" + OS: "linux" + Resources: + NanoCPUs: 4000000000 + MemoryBytes: 8272408576 + Engine: + EngineVersion: "17.04.0" + Labels: + foo: "bar" + Plugins: + - Type: "Volume" + Name: "local" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + Status: + State: "ready" + Addr: "172.17.0.2" + ManagerStatus: + Leader: true + Reachability: "reachable" + Addr: "172.17.0.2:2377" + TLSInfo: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + TLSInfo: + description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + type: "object" + properties: + TrustRoot: + description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + type: "string" + CertIssuerSubject: + description: "The base64-url-safe-encoded raw subject bytes of the issuer" + type: "string" + CertIssuerPublicKey: + description: "The base64-url-safe-encoded raw public key bytes of the issuer" + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Orchestration: + description: "Orchestration configuration." + type: "object" + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "int64" + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "int64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "int64" + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + Dispatcher: + description: "Dispatcher configuration." + type: "object" + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + CAConfig: + description: "CA configuration." + type: "object" + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + CACert: + description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)." + type: "string" + SigningCACert: + description: "The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format." + type: "string" + SigningCAKey: + description: "The desired signing CA key for all swarm node TLS leaf certificates, in PEM format." + type: "string" + ForceRotate: + description: "An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if unspecified by a service. + + Updating this value will only have an affect on new tasks. Old tasks will continue use their previously configured log driver until recreated. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + example: + Name: "default" + Orchestration: + TaskHistoryRetentionLimit: 10 + Raft: + SnapshotInterval: 10000 + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + ElectionTick: 3 + Dispatcher: + HeartbeatPeriod: 5000000000 + CAConfig: + NodeCertExpiry: 7776000000000000 + JoinTokens: + Worker: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + EncryptionConfig: + AutoLockManagers: false + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: "Whether there is currently a root CA rotation in progress for the swarm" + type: "boolean" + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + ContainerSpec: + type: "object" + properties: + Image: + description: "The image name to use for the container." + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + File: + type: "string" + description: | + Load credential spec from this file. The file is read by the daemon, and must be present in the + `CredentialSpecs` subdirectory in the docker data directory, which defaults to + `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows registry. The specified registry value must be + located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. + The format of extra hosts on swarmkit is specified in: + http://man7.org/linux/man-pages/man5/hosts.5.html + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + Configs: + description: "Configs contains references to zero or more configs that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + ConfigID: + description: "ConfigID represents the ID of the specific config that we're referencing." + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, but this is just provided for + lookup/display purposes. The config in the reference will be identified by its ID. + type: "string" + + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + type: "object" + properties: + NanoCPUs: + description: "CPU limit in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory limit in Bytes." + type: "integer" + format: "int64" + Reservation: + description: "Define resources reservation." + properties: + NanoCPUs: + description: "CPU reservation in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory reservation in Bytes." + type: "integer" + format: "int64" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + Platforms: + description: "An array of supported platforms." + type: "array" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Runtime: + description: "Runtime is the type of runtime specified for the task executor." + type: "string" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: "Base64-url-safe-encoded secret data" + type: "array" + items: + type: "string" + Secret: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/SecretSpec" + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: "Base64-url-safe-encoded config data" + type: "array" + items: + type: "string" + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + +paths: + /containers/json: + get: + summary: "List containers" + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 406: + description: "impossible to attach" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: | + The status of the container. For example, `"running"` or `"exited"`. + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the cgroups freezer is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkConfig" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + SecondaryIPAddresses: null + SecondaryIPv6Addresses: null + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: "Wait until a container state reaches the given condition, either 'not-running' (default), 'next-exit', or 'removed'." + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + Architecture: + type: "string" + Containers: + type: "integer" + ContainersRunning: + type: "integer" + ContainersStopped: + type: "integer" + ContainersPaused: + type: "integer" + CpuCfsPeriod: + type: "boolean" + CpuCfsQuota: + type: "boolean" + Debug: + type: "boolean" + DiscoveryBackend: + type: "string" + DockerRootDir: + type: "string" + Driver: + type: "string" + DriverStatus: + type: "array" + items: + type: "array" + items: + type: "string" + SystemStatus: + type: "array" + items: + type: "array" + items: + type: "string" + Plugins: + type: "object" + properties: + Volume: + type: "array" + items: + type: "string" + Network: + type: "array" + items: + type: "string" + Log: + type: "array" + items: + type: "string" + ExperimentalBuild: + type: "boolean" + HttpProxy: + type: "string" + HttpsProxy: + type: "string" + ID: + type: "string" + IPv4Forwarding: + type: "boolean" + Images: + type: "integer" + IndexServerAddress: + type: "string" + InitPath: + type: "string" + InitSha1: + type: "string" + KernelVersion: + type: "string" + Labels: + type: "array" + items: + type: "string" + MemTotal: + type: "integer" + MemoryLimit: + type: "boolean" + NCPU: + type: "integer" + NEventsListener: + type: "integer" + NFd: + type: "integer" + NGoroutines: + type: "integer" + Name: + type: "string" + NoProxy: + type: "string" + OomKillDisable: + type: "boolean" + OSType: + type: "string" + OomScoreAdj: + type: "integer" + OperatingSystem: + type: "string" + RegistryConfig: + type: "object" + properties: + IndexConfigs: + type: "object" + additionalProperties: + type: "object" + properties: + Mirrors: + type: "array" + items: + type: "string" + Name: + type: "string" + Official: + type: "boolean" + Secure: + type: "boolean" + InsecureRegistryCIDRs: + type: "array" + items: + type: "string" + SwapLimit: + type: "boolean" + SystemTime: + type: "string" + ServerVersion: + type: "string" + examples: + application/json: + Architecture: "x86_64" + ClusterStore: "etcd://localhost:2379" + CgroupDriver: "cgroupfs" + Containers: 11 + ContainersRunning: 7 + ContainersStopped: 3 + ContainersPaused: 1 + CpuCfsPeriod: true + CpuCfsQuota: true + Debug: false + DockerRootDir: "/var/lib/docker" + Driver: "btrfs" + DriverStatus: + - + - "" + ExperimentalBuild: false + HttpProxy: "http://test:test@localhost:8080" + HttpsProxy: "https://test:test@localhost:8080" + ID: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + IPv4Forwarding: true + Images: 16 + IndexServerAddress: "https://index.docker.io/v1/" + InitPath: "/usr/bin/docker" + InitSha1: "" + KernelMemory: true + KernelVersion: "3.12.0-1-amd64" + Labels: + - "storage=ssd" + MemTotal: 2099236864 + MemoryLimit: true + NCPU: 1 + NEventsListener: 0 + NFd: 11 + NGoroutines: 21 + Name: "prod-server-42" + NoProxy: "9.81.1.160" + OomKillDisable: true + OSType: "linux" + OperatingSystem: "Boot2Docker" + Plugins: + Volume: + - "local" + Network: + - "null" + - "host" + - "bridge" + RegistryConfig: + IndexConfigs: + docker.io: + Name: "docker.io" + Official: true + Secure: true + InsecureRegistryCIDRs: + - "127.0.0.0/8" + SecurityOptions: + - Key: "Name" + Value: "seccomp" + - Key: "Profile" + Value: "default" + - Key: "Name" + Value: "apparmor" + - Key: "Name" + Value: "selinux" + - Key: "Name" + Value: "userns" + ServerVersion: "1.9.0" + SwapLimit: false + SystemStatus: + - + - "State" + - "Healthy" + SystemTime: "2015-03-10T11:11:23.730591467-07:00" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service` or `secret` + - `volume=` volume name or ID + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see [the network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than inspecting a single network. For example, + the list of containers attached to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + Ingress: + description: "Ingress network is the network which provides the routing-mesh in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + description: "The tokens workers and managers need to join the swarm." + type: "object" + properties: + Worker: + description: "The token workers can use to join the swarm." + type: "string" + Manager: + description: "The token managers can use to join the swarm." + type: "string" + example: + CreatedAt: "2016-08-15T16:00:20.349727406Z" + Spec: + Dispatcher: + HeartbeatPeriod: 5000000000 + Orchestration: + TaskHistoryRetentionLimit: 10 + CAConfig: + NodeCertExpiry: 7776000000000000 + Raft: + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + SnapshotInterval: 10000 + ElectionTick: 3 + TaskDefaults: {} + EncryptionConfig: + AutoLockManagers: false + Name: "default" + JoinTokens: + Worker: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-6qmn92w6bu3jdvnglku58u11a" + Manager: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-8llk83c4wm9lwioey2s316r9l" + ID: "70ilmkj2f6sp2137c753w2nmt" + UpdatedAt: "2016-08-15T16:32:09.623207604Z" + Version: + Index: 51 + RootRotationInProgress: false + TLSInfo: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the configs list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created config." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: "The spec of the config to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [ConfigInspect endpoint](#operation/ConfigInspect) response values." + - name: "version" + in: "query" + description: "The version number of the config object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: "Return image digest and platform information by contacting the registry." + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: "A descriptor struct containing digest, media type, and size" + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: "An array containing all platforms supported by the image" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] diff --git a/docs/api/v1.31.yaml b/docs/api/v1.31.yaml new file mode 100644 index 0000000000000..77518554fca6c --- /dev/null +++ b/docs/api/v1.31.yaml @@ -0,0 +1,8946 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.31" +info: + title: "Docker Engine API" + version: "1.31" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + + For Docker Engine 17.06, the API version is 1.30. To lock to this version, you prefix the URL with `/v1.30`. For example, calling `/info` is the same as calling `/v1.30/info`. + + Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + + In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + + The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. + + This documentation is for version 1.31 of the API. Use this table to find documentation for previous versions of the API: + + Docker version | API version | Changes + ----------------|-------------|--------- + 17.06.x | [1.30](https://docs.docker.com/engine/api/v1.30/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-30-api-changes) + 17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes) + 17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes) + 17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes) + 1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes) + 1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes) + 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) + 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) + 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) + 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) + 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) + 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) + 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + StartPeriod: + description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind-mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + type: "object" + description: "A map of exposed container ports and the host port they should map to." + additionalProperties: + type: "object" + properties: + HostIp: + type: "string" + description: "The host IP address" + HostPort: + type: "string" + description: "The host port number, as a string" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: "IPC namespace to use for the container." + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: "Allocates a random host port for all of a container's exposed ports." + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: + - "array" + - "string" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: + - "array" + - "string" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkConfig: + description: "TODO: check is correct" + type: "object" + properties: + Bridge: + type: "string" + Gateway: + type: "string" + Address: + type: "string" + IPPrefixLen: + type: "integer" + MacAddress: + type: "string" + PortMapping: + type: "string" + Ports: + type: "array" + items: + $ref: "#/definitions/Port" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + required: [Size, RefCount] + properties: + Size: + type: "integer" + description: "The disk space used by the volume (local driver only)" + default: -1 + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: "The number of containers referencing this volume." + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + IPAMConfig: + description: "IPAM configurations for the endpoint" + type: "object" + properties: + IPv4Address: + type: "string" + IPv6Address: + type: "string" + LinkLocalIPs: + type: "array" + items: + type: "string" + Links: + type: "array" + items: + type: "string" + Aliases: + type: "array" + items: + type: "string" + NetworkID: + type: "string" + EndpointID: + type: "string" + Gateway: + type: "string" + IPAddress: + type: "string" + IPPrefixLen: + type: "integer" + IPv6Gateway: + type: "string" + GlobalIPv6Address: + type: "string" + GlobalIPv6PrefixLen: + type: "integer" + format: "int64" + MacAddress: + type: "string" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: "True if the plugin is running. False if the plugin is not running, only installed." + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentionally overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "int64" + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + Node: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + type: "object" + properties: + Hostname: + type: "string" + Platform: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + Resources: + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + MemoryBytes: + type: "integer" + format: "int64" + Engine: + type: "object" + properties: + EngineVersion: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + TLSInfo: + $ref: "#/definitions/SwarmSpec" + example: + ID: "24ifsmvkjbyhk" + Version: + Index: 8 + CreatedAt: "2016-06-07T20:31:11.853781916Z" + UpdatedAt: "2016-06-07T20:31:11.999868824Z" + Spec: + Name: "my-node" + Role: "manager" + Availability: "active" + Labels: + foo: "bar" + Description: + Hostname: "bf3067039e47" + Platform: + Architecture: "x86_64" + OS: "linux" + Resources: + NanoCPUs: 4000000000 + MemoryBytes: 8272408576 + Engine: + EngineVersion: "17.04.0" + Labels: + foo: "bar" + Plugins: + - Type: "Volume" + Name: "local" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + Status: + State: "ready" + Addr: "172.17.0.2" + ManagerStatus: + Leader: true + Reachability: "reachable" + Addr: "172.17.0.2:2377" + TLSInfo: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + TLSInfo: + description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + type: "object" + properties: + TrustRoot: + description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + type: "string" + CertIssuerSubject: + description: "The base64-url-safe-encoded raw subject bytes of the issuer" + type: "string" + CertIssuerPublicKey: + description: "The base64-url-safe-encoded raw public key bytes of the issuer" + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Orchestration: + description: "Orchestration configuration." + type: "object" + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "int64" + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "int64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "int64" + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + Dispatcher: + description: "Dispatcher configuration." + type: "object" + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + CAConfig: + description: "CA configuration." + type: "object" + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + CACert: + description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)." + type: "string" + SigningCACert: + description: "The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format." + type: "string" + SigningCAKey: + description: "The desired signing CA key for all swarm node TLS leaf certificates, in PEM format." + type: "string" + ForceRotate: + description: "An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if unspecified by a service. + + Updating this value will only have an affect on new tasks. Old tasks will continue use their previously configured log driver until recreated. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + example: + Name: "default" + Orchestration: + TaskHistoryRetentionLimit: 10 + Raft: + SnapshotInterval: 10000 + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + ElectionTick: 3 + Dispatcher: + HeartbeatPeriod: 5000000000 + CAConfig: + NodeCertExpiry: 7776000000000000 + JoinTokens: + Worker: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + EncryptionConfig: + AutoLockManagers: false + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: "Whether there is currently a root CA rotation in progress for the swarm" + type: "boolean" + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: "Invalid when specified with `ContainerSpec`. *(Experimental release only.)*" + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: "Invalid when specified with `PluginSpec`." + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + File: + type: "string" + description: | + Load credential spec from this file. The file is read by the daemon, and must be present in the + `CredentialSpecs` subdirectory in the docker data directory, which defaults to + `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows registry. The specified registry value must be + located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. + The format of extra hosts on swarmkit is specified in: + http://man7.org/linux/man-pages/man5/hosts.5.html + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + Configs: + description: "Configs contains references to zero or more configs that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + ConfigID: + description: "ConfigID represents the ID of the specific config that we're referencing." + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, but this is just provided for + lookup/display purposes. The config in the reference will be identified by its ID. + type: "string" + + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + type: "object" + properties: + NanoCPUs: + description: "CPU limit in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory limit in Bytes." + type: "integer" + format: "int64" + Reservation: + description: "Define resources reservation." + properties: + NanoCPUs: + description: "CPU reservation in units of 10-9 CPU shares." + type: "integer" + format: "int64" + MemoryBytes: + description: "Memory reservation in Bytes." + type: "integer" + format: "int64" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + Platforms: + description: "An array of supported platforms." + type: "array" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Runtime: + description: "Runtime is the type of runtime specified for the task executor." + type: "string" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: "Name of the secrets driver used to fetch the secret's value from an external secret store" + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + config data. + type: "string" + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see [the inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container than inspecting a single container. For example, + the list of linked containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 406: + description: "impossible to attach" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: | + The status of the container. For example, `"running"` or `"exited"`. + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the cgroups freezer is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkConfig" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + SecondaryIPAddresses: null + SecondaryIPv6Addresses: null + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: "Wait until a container state reaches the given condition, either 'not-running' (default), 'next-exit', or 'removed'." + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + Architecture: + type: "string" + Containers: + type: "integer" + ContainersRunning: + type: "integer" + ContainersStopped: + type: "integer" + ContainersPaused: + type: "integer" + CpuCfsPeriod: + type: "boolean" + CpuCfsQuota: + type: "boolean" + Debug: + type: "boolean" + DiscoveryBackend: + type: "string" + DockerRootDir: + type: "string" + Driver: + type: "string" + DriverStatus: + type: "array" + items: + type: "array" + items: + type: "string" + SystemStatus: + type: "array" + items: + type: "array" + items: + type: "string" + Plugins: + type: "object" + properties: + Volume: + type: "array" + items: + type: "string" + Network: + type: "array" + items: + type: "string" + Log: + type: "array" + items: + type: "string" + ExperimentalBuild: + type: "boolean" + HttpProxy: + type: "string" + HttpsProxy: + type: "string" + ID: + type: "string" + IPv4Forwarding: + type: "boolean" + Images: + type: "integer" + IndexServerAddress: + type: "string" + InitPath: + type: "string" + InitSha1: + type: "string" + KernelVersion: + type: "string" + Labels: + type: "array" + items: + type: "string" + MemTotal: + type: "integer" + MemoryLimit: + type: "boolean" + NCPU: + type: "integer" + NEventsListener: + type: "integer" + NFd: + type: "integer" + NGoroutines: + type: "integer" + Name: + type: "string" + NoProxy: + type: "string" + OomKillDisable: + type: "boolean" + OSType: + type: "string" + OomScoreAdj: + type: "integer" + OperatingSystem: + type: "string" + RegistryConfig: + type: "object" + properties: + IndexConfigs: + type: "object" + additionalProperties: + type: "object" + properties: + Mirrors: + type: "array" + items: + type: "string" + Name: + type: "string" + Official: + type: "boolean" + Secure: + type: "boolean" + InsecureRegistryCIDRs: + type: "array" + items: + type: "string" + SwapLimit: + type: "boolean" + SystemTime: + type: "string" + ServerVersion: + type: "string" + examples: + application/json: + Architecture: "x86_64" + ClusterStore: "etcd://localhost:2379" + CgroupDriver: "cgroupfs" + Containers: 11 + ContainersRunning: 7 + ContainersStopped: 3 + ContainersPaused: 1 + CpuCfsPeriod: true + CpuCfsQuota: true + Debug: false + DockerRootDir: "/var/lib/docker" + Driver: "btrfs" + DriverStatus: + - + - "" + ExperimentalBuild: false + HttpProxy: "http://test:test@localhost:8080" + HttpsProxy: "https://test:test@localhost:8080" + ID: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + IPv4Forwarding: true + Images: 16 + IndexServerAddress: "https://index.docker.io/v1/" + InitPath: "/usr/bin/docker" + InitSha1: "" + KernelMemory: true + KernelVersion: "3.12.0-1-amd64" + Labels: + - "storage=ssd" + MemTotal: 2099236864 + MemoryLimit: true + NCPU: 1 + NEventsListener: 0 + NFd: 11 + NGoroutines: 21 + Name: "prod-server-42" + NoProxy: "9.81.1.160" + OomKillDisable: true + OSType: "linux" + OperatingSystem: "Boot2Docker" + Plugins: + Volume: + - "local" + Network: + - "null" + - "host" + - "bridge" + RegistryConfig: + IndexConfigs: + docker.io: + Name: "docker.io" + Official: true + Secure: true + InsecureRegistryCIDRs: + - "127.0.0.0/8" + SecurityOptions: + - Key: "Name" + Value: "seccomp" + - Key: "Profile" + Value: "default" + - Key: "Name" + Value: "apparmor" + - Key: "Name" + Value: "selinux" + - Key: "Name" + Value: "userns" + ServerVersion: "1.9.0" + SwapLimit: false + SystemStatus: + - + - "State" + - "Healthy" + SystemTime: "2015-03-10T11:11:23.730591467-07:00" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service` or `secret` + - `volume=` volume name or ID + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "" + Labels: null + Scope: "" + Options: null + UsageData: + Size: 0 + RefCount: 0 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see [the network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than inspecting a single network. For example, + the list of containers attached to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + Ingress: + description: "Ingress network is the network which provides the routing-mesh in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + description: "The tokens workers and managers need to join the swarm." + type: "object" + properties: + Worker: + description: "The token workers can use to join the swarm." + type: "string" + Manager: + description: "The token managers can use to join the swarm." + type: "string" + example: + CreatedAt: "2016-08-15T16:00:20.349727406Z" + Spec: + Dispatcher: + HeartbeatPeriod: 5000000000 + Orchestration: + TaskHistoryRetentionLimit: 10 + CAConfig: + NodeCertExpiry: 7776000000000000 + Raft: + LogEntriesForSlowFollowers: 500 + HeartbeatTick: 1 + SnapshotInterval: 10000 + ElectionTick: 3 + TaskDefaults: {} + EncryptionConfig: + AutoLockManagers: false + Name: "default" + JoinTokens: + Worker: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-6qmn92w6bu3jdvnglku58u11a" + Manager: "SWMTKN-1-1h8aps2yszaiqmz2l3oc5392pgk8e49qhx2aj3nyv0ui0hez2a-8llk83c4wm9lwioey2s316r9l" + ID: "70ilmkj2f6sp2137c753w2nmt" + UpdatedAt: "2016-08-15T16:32:09.623207604Z" + Version: + Index: 51 + RootRotationInProgress: false + TLSInfo: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the configs list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created config." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: "The spec of the config to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [ConfigInspect endpoint](#operation/ConfigInspect) response values." + - name: "version" + in: "query" + description: "The version number of the config object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: "Return image digest and platform information by contacting the registry." + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: "A descriptor struct containing digest, media type, and size" + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: "An array containing all platforms supported by the image" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to call back to the client for advanced capabilities. + + > **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental + > features enabled. The specifications for this endpoint may still change in a future version of the API. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session (experimental)"] diff --git a/docs/api/v1.32.yaml b/docs/api/v1.32.yaml new file mode 100644 index 0000000000000..834b2eb0f553a --- /dev/null +++ b/docs/api/v1.32.yaml @@ -0,0 +1,9930 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.32" +info: + title: "Docker Engine API" + version: "1.32" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + + For Docker Engine 17.07, the API version is 1.31. To lock to this version, you prefix the URL with `/v1.31`. For example, calling `/info` is the same as calling `/v1.31/info`. + + Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + + In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + + The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. + + This documentation is for version 1.32 of the API. Use this table to find documentation for previous versions of the API: + + Docker version | API version | Changes + ----------------|-------------|--------- + 17.07.x | [1.31](https://docs.docker.com/engine/api/v1.31/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-31-api-changes) + 17.06.x | [1.30](https://docs.docker.com/engine/api/v1.30/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-30-api-changes) + 17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes) + 17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes) + 17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes) + 1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes) + 1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes) + 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) + 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) + 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) + 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) + 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) + 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) + 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + example: "0-3" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + ResourceObject: + description: "An object describing the resources which can be advertised by a node and requested by a task" + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: "User-defined resources can be either Integer resources (e.g, `SSD=3`) or String resources (e.g, `GPU=UUID1`)" + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + StartPeriod: + description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + type: "object" + description: "A map of exposed container ports and the host port they should map to." + additionalProperties: + type: "object" + properties: + HostIp: + type: "string" + description: "The host IP address" + HostPort: + type: "string" + description: "The host port number, as a string" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: "Allocates a random host port for all of a container's exposed ports." + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: + - "array" + - "string" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: + - "array" + - "string" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network's bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for both `tcp` and `udp`, two separate + entries are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + x-nullable: true + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: "True if the plugin is running. False if the plugin is not running, only installed." + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentionally overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + type: "object" + properties: + TrustRoot: + description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + type: "string" + CertIssuerSubject: + description: "The base64-url-safe-encoded raw subject bytes of the issuer" + type: "string" + CertIssuerPublicKey: + description: "The base64-url-safe-encoded raw public key bytes of the issuer" + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + CACert: + description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)." + type: "string" + SigningCACert: + description: "The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format." + type: "string" + SigningCAKey: + description: "The desired signing CA key for all swarm node TLS leaf certificates, in PEM format." + type: "string" + ForceRotate: + description: "An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`" + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: "Whether there is currently a root CA rotation in progress for the swarm" + type: "boolean" + example: false + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: "Invalid when specified with `ContainerSpec`. *(Experimental release only.)*" + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: "Invalid when specified with `PluginSpec`." + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + File: + type: "string" + description: | + Load credential spec from this file. The file is read by the daemon, and must be present in the + `CredentialSpecs` subdirectory in the docker data directory, which defaults to + `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows registry. The specified registry value must be + located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + Configs: + description: "Configs contains references to zero or more configs that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + ConfigID: + description: "ConfigID represents the ID of the specific config that we're referencing." + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, but this is just provided for + lookup/display purposes. The config in the reference will be identified by its ID. + type: "string" + + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/ResourceObject" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Runtime: + description: "Runtime is the type of runtime specified for the task executor." + type: "string" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: "Name of the secrets driver used to fetch the secret's value from an external secret store" + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + config data. + type: "string" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + SystemStatus: + description: | + Status information about this node (standalone Swarm API). + +


+ + > **Note**: The information returned in this field is only propagated + > by the Swarm standalone API, and is empty (`null`) when using + > built-in swarm mode. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Role", "primary"] + - ["State", "Healthy"] + - ["Strategy", "spread"] + - ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"] + - ["Nodes", "2"] + - [" swarm-agent-00", "192.168.99.102:2376"] + - [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:03:46Z"] + - [" └ ServerVersion", "17.06.0-ce"] + - [" swarm-manager", "192.168.99.101:2376"] + - [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:04:11Z"] + - [" └ ServerVersion", "17.06.0-ce"] + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: "Indicates if the host has kernel memory limit support enabled." + type: "boolean" + example: true + CpuCfsPeriod: + description: "Indicates if CPU CFS(Completely Fair Scheduler) period is supported by the host." + type: "boolean" + example: true + CpuCfsQuota: + description: "Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by the host." + type: "boolean" + example: true + CPUShares: + description: "Indicates if CPU Shares limiting is supported by the host." + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: "Indicates if the daemon is running in debug-mode / with debug-level logging enabled." + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd"] + default: "cgroupfs" + example: "cgroupfs" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://user:pass@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://user:pass@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "docker-runc" + example: + runc: + path: "docker-runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, and user-namespaces (userns). + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see [the inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container than inspecting a single container. For example, + the list of linked containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: | + The status of the container. For example, `"running"` or `"exited"`. + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the cgroups freezer is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: "Wait until a container state reaches the given condition, either 'not-running' (default), 'next-exit', or 'removed'." + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service` or `secret` + - `volume=` volume name or ID + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see [the network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than inspecting a single network. For example, + the list of containers attached to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + Ingress: + description: "Ingress network is the network which provides the routing-mesh in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the configs list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created config." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: "The spec of the config to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [ConfigInspect endpoint](#operation/ConfigInspect) response values." + - name: "version" + in: "query" + description: "The version number of the config object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: "Return image digest and platform information by contacting the registry." + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: "A descriptor struct containing digest, media type, and size" + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: "An array containing all platforms supported by the image" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to call back to the client for advanced capabilities. + + > **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental + > features enabled. The specifications for this endpoint may still change in a future version of the API. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session (experimental)"] diff --git a/docs/api/v1.33.yaml b/docs/api/v1.33.yaml new file mode 100644 index 0000000000000..09f551d205ec5 --- /dev/null +++ b/docs/api/v1.33.yaml @@ -0,0 +1,9939 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.33" +info: + title: "Docker Engine API" + version: "1.33" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + + For Docker Engine 17.09, the API version is 1.32. To lock to this version, you prefix the URL with `/v1.32`. For example, calling `/info` is the same as calling `/v1.32/info`. + + Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + + In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + + The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. + + This documentation is for version 1.33 of the API. Use this table to find documentation for previous versions of the API: + + Docker version | API version | Changes + ----------------|-------------|--------- + 17.09.x | [1.31](https://docs.docker.com/engine/api/v1.32/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-32-api-changes) + 17.07.x | [1.31](https://docs.docker.com/engine/api/v1.31/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-31-api-changes) + 17.06.x | [1.30](https://docs.docker.com/engine/api/v1.30/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-30-api-changes) + 17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes) + 17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes) + 17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes) + 1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes) + 1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes) + 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) + 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) + 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) + 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) + 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) + 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) + 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + - name: "Config" + x-displayName: "Configs" + description: | + Configs are application configurations that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + example: "0-3" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + ResourceObject: + description: "An object describing the resources which can be advertised by a node and requested by a task" + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: "User-defined resources can be either Integer resources (e.g, `SSD=3`) or String resources (e.g, `GPU=UUID1`)" + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + StartPeriod: + description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + type: "object" + description: "A map of exposed container ports and the host port they should map to." + additionalProperties: + type: "object" + properties: + HostIp: + type: "string" + description: "The host IP address" + HostPort: + type: "string" + description: "The host port number, as a string" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: "Allocates a random host port for all of a container's exposed ports." + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: + - "array" + - "string" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: + - "array" + - "string" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network's bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for both `tcp` and `udp`, two separate + entries are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + x-nullable: true + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: "True if the plugin is running. False if the plugin is not running, only installed." + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentionally overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + type: "object" + properties: + TrustRoot: + description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + type: "string" + CertIssuerSubject: + description: "The base64-url-safe-encoded raw subject bytes of the issuer" + type: "string" + CertIssuerPublicKey: + description: "The base64-url-safe-encoded raw public key bytes of the issuer" + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + CACert: + description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)." + type: "string" + SigningCACert: + description: "The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format." + type: "string" + SigningCAKey: + description: "The desired signing CA key for all swarm node TLS leaf certificates, in PEM format." + type: "string" + ForceRotate: + description: "An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`" + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: "Whether there is currently a root CA rotation in progress for the swarm" + type: "boolean" + example: false + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: "Invalid when specified with `ContainerSpec`. *(Experimental release only.)*" + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: "Invalid when specified with `PluginSpec`." + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + File: + type: "string" + description: | + Load credential spec from this file. The file is read by the daemon, and must be present in the + `CredentialSpecs` subdirectory in the docker data directory, which defaults to + `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows registry. The specified registry value must be + located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + Configs: + description: "Configs contains references to zero or more configs that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + ConfigID: + description: "ConfigID represents the ID of the specific config that we're referencing." + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, but this is just provided for + lookup/display purposes. The config in the reference will be identified by its ID. + type: "string" + + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/ResourceObject" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Runtime: + description: "Runtime is the type of runtime specified for the task executor." + type: "string" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: "Name of the secrets driver used to fetch the secret's value from an external secret store" + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + config data. + type: "string" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + SystemStatus: + description: | + Status information about this node (standalone Swarm API). + +


+ + > **Note**: The information returned in this field is only propagated + > by the Swarm standalone API, and is empty (`null`) when using + > built-in swarm mode. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Role", "primary"] + - ["State", "Healthy"] + - ["Strategy", "spread"] + - ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"] + - ["Nodes", "2"] + - [" swarm-agent-00", "192.168.99.102:2376"] + - [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:03:46Z"] + - [" └ ServerVersion", "17.06.0-ce"] + - [" swarm-manager", "192.168.99.101:2376"] + - [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:04:11Z"] + - [" └ ServerVersion", "17.06.0-ce"] + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: "Indicates if the host has kernel memory limit support enabled." + type: "boolean" + example: true + CpuCfsPeriod: + description: "Indicates if CPU CFS(Completely Fair Scheduler) period is supported by the host." + type: "boolean" + example: true + CpuCfsQuota: + description: "Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by the host." + type: "boolean" + example: true + CPUShares: + description: "Indicates if CPU Shares limiting is supported by the host." + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: "Indicates if the daemon is running in debug-mode / with debug-level logging enabled." + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd"] + default: "cgroupfs" + example: "cgroupfs" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://user:pass@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://user:pass@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "docker-runc" + example: + runc: + path: "docker-runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, and user-namespaces (userns). + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see [the inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container than inspecting a single container. For example, + the list of linked containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: | + The status of the container. For example, `"running"` or `"exited"`. + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the cgroups freezer is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: "Wait until a container state reaches the given condition, either 'not-running' (default), 'next-exit', or 'removed'." + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `config=` config name or ID + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `node=` node ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `secret=` secret name or ID + - `service=` service name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + - `volume=` volume name + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see [the network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than inspecting a single network. For example, + the list of containers attached to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + Ingress: + description: "Ingress network is the network which provides the routing-mesh in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the configs list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created config." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: "The spec of the config to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [ConfigInspect endpoint](#operation/ConfigInspect) response values." + - name: "version" + in: "query" + description: "The version number of the config object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: "Return image digest and platform information by contacting the registry." + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: "A descriptor struct containing digest, media type, and size" + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: "An array containing all platforms supported by the image" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to call back to the client for advanced capabilities. + + > **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental + > features enabled. The specifications for this endpoint may still change in a future version of the API. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session (experimental)"] diff --git a/docs/api/v1.34.yaml b/docs/api/v1.34.yaml new file mode 100644 index 0000000000000..1c6602d214d22 --- /dev/null +++ b/docs/api/v1.34.yaml @@ -0,0 +1,9985 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.34" +info: + title: "Docker Engine API" + version: "1.34" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release of Docker, so API calls are versioned to ensure that clients don't break. + + For Docker Engine 17.10, the API version is 1.33. To lock to this version, you prefix the URL with `/v1.33`. For example, calling `/info` is the same as calling `/v1.33/info`. + + Engine releases in the near future should support this version of the API, so your client will continue to work even if it is talking to a newer Engine. + + In previous versions of Docker, it was possible to access the API without providing a version. This behaviour is now deprecated will be removed in a future version of Docker. + + If the API version specified in the URL is not supported by the daemon, a HTTP `400 Bad Request` error message is returned. + + The API uses an open schema model, which means server may add extra properties to responses. Likewise, the server will ignore any extra query parameters and request body properties. When you write clients, you need to ignore additional properties in responses to ensure they do not break when talking to newer Docker daemons. + + This documentation is for version 1.34 of the API. Use this table to find documentation for previous versions of the API: + + Docker version | API version | Changes + ----------------|-------------|--------- + 17.10.x | [1.33](https://docs.docker.com/engine/api/v1.33/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-33-api-changes) + 17.09.x | [1.32](https://docs.docker.com/engine/api/v1.32/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-32-api-changes) + 17.07.x | [1.31](https://docs.docker.com/engine/api/v1.31/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-31-api-changes) + 17.06.x | [1.30](https://docs.docker.com/engine/api/v1.30/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-30-api-changes) + 17.05.x | [1.29](https://docs.docker.com/engine/api/v1.29/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-29-api-changes) + 17.04.x | [1.28](https://docs.docker.com/engine/api/v1.28/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-28-api-changes) + 17.03.1 | [1.27](https://docs.docker.com/engine/api/v1.27/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-27-api-changes) + 1.13.1 & 17.03.0 | [1.26](https://docs.docker.com/engine/api/v1.26/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-26-api-changes) + 1.13.0 | [1.25](https://docs.docker.com/engine/api/v1.25/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-25-api-changes) + 1.12.x | [1.24](https://docs.docker.com/engine/api/v1.24/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-24-api-changes) + 1.11.x | [1.23](https://docs.docker.com/engine/api/v1.23/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-23-api-changes) + 1.10.x | [1.22](https://docs.docker.com/engine/api/v1.22/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-22-api-changes) + 1.9.x | [1.21](https://docs.docker.com/engine/api/v1.21/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-21-api-changes) + 1.8.x | [1.20](https://docs.docker.com/engine/api/v1.20/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-20-api-changes) + 1.7.x | [1.19](https://docs.docker.com/engine/api/v1.19/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-19-api-changes) + 1.6.x | [1.18](https://docs.docker.com/engine/api/v1.18/) | [API changes](https://docs.docker.com/engine/api/version-history/#v1-18-api-changes) + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + - name: "Config" + x-displayName: "Configs" + description: | + Configs are application configurations that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + example: "0-3" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + ResourceObject: + description: "An object describing the resources which can be advertised by a node and requested by a task" + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: "User-defined resources can be either Integer resources (e.g, `SSD=3`) or String resources (e.g, `GPU=UUID1`)" + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + StartPeriod: + description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + type: "object" + description: "A map of exposed container ports and the host port they should map to." + additionalProperties: + type: "object" + properties: + HostIp: + type: "string" + description: "The host IP address" + HostPort: + type: "string" + description: "The host port number, as a string" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: | + Allocates an ephemeral host port for all of a container's + exposed ports. + + Ports are de-allocated when the container stops and allocated when the container starts. + The allocated port might be changed when restarting the container. + + The port is selected from the ephemeral port range that depends on the kernel. + For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`. + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: + - "array" + - "string" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: + - "array" + - "string" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network's bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for both `tcp` and `udp`, two separate + entries are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + x-nullable: true + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: "True if the plugin is running. False if the plugin is not running, only installed." + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentionally overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + type: "object" + properties: + TrustRoot: + description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + type: "string" + CertIssuerSubject: + description: "The base64-url-safe-encoded raw subject bytes of the issuer" + type: "string" + CertIssuerPublicKey: + description: "The base64-url-safe-encoded raw public key bytes of the issuer" + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + CACert: + description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)." + type: "string" + SigningCACert: + description: "The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format." + type: "string" + SigningCAKey: + description: "The desired signing CA key for all swarm node TLS leaf certificates, in PEM format." + type: "string" + ForceRotate: + description: "An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`" + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: "Whether there is currently a root CA rotation in progress for the swarm" + type: "boolean" + example: false + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: "Invalid when specified with `ContainerSpec`. *(Experimental release only.)*" + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: "Invalid when specified with `PluginSpec`." + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + File: + type: "string" + description: | + Load credential spec from this file. The file is read by the daemon, and must be present in the + `CredentialSpecs` subdirectory in the docker data directory, which defaults to + `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows registry. The specified registry value must be + located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + Configs: + description: "Configs contains references to zero or more configs that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + ConfigID: + description: "ConfigID represents the ID of the specific config that we're referencing." + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, but this is just provided for + lookup/display purposes. The config in the reference will be identified by its ID. + type: "string" + + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/ResourceObject" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Runtime: + description: "Runtime is the type of runtime specified for the task executor." + type: "string" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + PublishMode: + description: | + The mode in which port is published. + +


+ + - "ingress" makes the target port accessible on on every node, + regardless of whether there is a task for the service running on + that node or not. + - "host" bypasses the routing mesh and publish the port directly on + the swarm node where that service is running. + + type: "string" + enum: + - "ingress" + - "host" + default: "ingress" + example: "ingress" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: "Name of the secrets driver used to fetch the secret's value from an external secret store" + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + config data. + type: "string" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + SystemStatus: + description: | + Status information about this node (standalone Swarm API). + +


+ + > **Note**: The information returned in this field is only propagated + > by the Swarm standalone API, and is empty (`null`) when using + > built-in swarm mode. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Role", "primary"] + - ["State", "Healthy"] + - ["Strategy", "spread"] + - ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"] + - ["Nodes", "2"] + - [" swarm-agent-00", "192.168.99.102:2376"] + - [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:03:46Z"] + - [" └ ServerVersion", "17.06.0-ce"] + - [" swarm-manager", "192.168.99.101:2376"] + - [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:04:11Z"] + - [" └ ServerVersion", "17.06.0-ce"] + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: "Indicates if the host has kernel memory limit support enabled." + type: "boolean" + example: true + CpuCfsPeriod: + description: "Indicates if CPU CFS(Completely Fair Scheduler) period is supported by the host." + type: "boolean" + example: true + CpuCfsQuota: + description: "Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by the host." + type: "boolean" + example: true + CPUShares: + description: "Indicates if CPU Shares limiting is supported by the host." + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: "Indicates if the daemon is running in debug-mode / with debug-level logging enabled." + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd"] + default: "cgroupfs" + example: "cgroupfs" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://user:pass@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://user:pass@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "docker-runc" + example: + runc: + path: "docker-runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, and user-namespaces (userns). + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see [the inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container than inspecting a single container. For example, + the list of linked containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: | + The status of the container. For example, `"running"` or `"exited"`. + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the cgroups freezer is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + Error: + description: "container waiting error, if any" + type: "object" + properties: + Message: + description: "Details of an error" + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: "Wait until a container state reaches the given condition, either 'not-running' (default), 'next-exit', or 'removed'." + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `config=` config name or ID + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `node=` node ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `secret=` secret name or ID + - `service=` service name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + - `volume=` volume name + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see [the network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than inspecting a single network. For example, + the list of containers attached to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + Ingress: + description: "Ingress network is the network which provides the routing-mesh in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the configs list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created config." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: "The spec of the config to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [ConfigInspect endpoint](#operation/ConfigInspect) response values." + - name: "version" + in: "query" + description: "The version number of the config object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: "Return image digest and platform information by contacting the registry." + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: "A descriptor struct containing digest, media type, and size" + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: "An array containing all platforms supported by the image" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to call back to the client for advanced capabilities. + + > **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental + > features enabled. The specifications for this endpoint may still change in a future version of the API. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session (experimental)"] diff --git a/docs/api/v1.35.yaml b/docs/api/v1.35.yaml new file mode 100644 index 0000000000000..e4de8e6162d65 --- /dev/null +++ b/docs/api/v1.35.yaml @@ -0,0 +1,9997 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.35" +info: + title: "Docker Engine API" + version: "1.35" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release, so API calls are versioned to + ensure that clients don't break. To lock to a specific version of the API, + you prefix the URL with its version, for example, call `/v1.30/info` to use + the v1.30 version of the `/info` endpoint. If the API version specified in + the URL is not supported by the daemon, a HTTP `400 Bad Request` error message + is returned. + + If you omit the version-prefix, the current version of the API (v1.35) is used. + For example, calling `/info` is the same as calling `/v1.35/info`. Using the + API without a version-prefix is deprecated and will be removed in a future release. + + Engine releases in the near future should support this version of the API, + so your client will continue to work even if it is talking to a newer Engine. + + The API uses an open schema model, which means server may add extra properties + to responses. Likewise, the server will ignore any extra query parameters and + request body properties. When you write clients, you need to ignore additional + properties in responses to ensure they do not break when talking to newer + daemons. + + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + - name: "Config" + x-displayName: "Configs" + description: | + Configs are application configurations that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + type: "string" + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + format: "int64" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + example: "0-3" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + ResourceObject: + description: "An object describing the resources which can be advertised by a node and requested by a task" + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: "User-defined resources can be either Integer resources (e.g, `SSD=3`) or String resources (e.g, `GPU=UUID1`)" + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + StartPeriod: + description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + $ref: "#/definitions/PortMap" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: | + Allocates an ephemeral host port for all of a container's + exposed ports. + + Ports are de-allocated when the container stops and allocated when the container starts. + The allocated port might be changed when restarting the container. + + The port is selected from the ephemeral port range that depends on the kernel. + For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`. + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: "array" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: "array" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network's bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for both `tcp` and `udp`, two separate + entries are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + x-nullable: true + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + CreateImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: "True if the plugin is running. False if the plugin is not running, only installed." + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentionally overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + type: "object" + properties: + TrustRoot: + description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + type: "string" + CertIssuerSubject: + description: "The base64-url-safe-encoded raw subject bytes of the issuer" + type: "string" + CertIssuerPublicKey: + description: "The base64-url-safe-encoded raw public key bytes of the issuer" + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + CACert: + description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)." + type: "string" + SigningCACert: + description: "The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format." + type: "string" + SigningCAKey: + description: "The desired signing CA key for all swarm node TLS leaf certificates, in PEM format." + type: "string" + ForceRotate: + description: "An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`" + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: "Whether there is currently a root CA rotation in progress for the swarm" + type: "boolean" + example: false + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: "Invalid when specified with `ContainerSpec`. *(Experimental release only.)*" + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: "Invalid when specified with `PluginSpec`." + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + File: + type: "string" + description: | + Load credential spec from this file. The file is read by the daemon, and must be present in the + `CredentialSpecs` subdirectory in the docker data directory, which defaults to + `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows registry. The specified registry value must be + located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + Configs: + description: "Configs contains references to zero or more configs that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + ConfigID: + description: "ConfigID represents the ID of the specific config that we're referencing." + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, but this is just provided for + lookup/display purposes. The config in the reference will be identified by its ID. + type: "string" + Isolation: + type: "string" + description: "Isolation technology of the containers running the service. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/ResourceObject" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Runtime: + description: "Runtime is the type of runtime specified for the task executor." + type: "string" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + PublishMode: + description: | + The mode in which port is published. + +


+ + - "ingress" makes the target port accessible on on every node, + regardless of whether there is a task for the service running on + that node or not. + - "host" bypasses the routing mesh and publish the port directly on + the swarm node where that service is running. + + type: "string" + enum: + - "ingress" + - "host" + default: "ingress" + example: "ingress" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: "Name of the secrets driver used to fetch the secret's value from an external secret store" + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + config data. + type: "string" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + SystemStatus: + description: | + Status information about this node (standalone Swarm API). + +


+ + > **Note**: The information returned in this field is only propagated + > by the Swarm standalone API, and is empty (`null`) when using + > built-in swarm mode. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Role", "primary"] + - ["State", "Healthy"] + - ["Strategy", "spread"] + - ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"] + - ["Nodes", "2"] + - [" swarm-agent-00", "192.168.99.102:2376"] + - [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:03:46Z"] + - [" └ ServerVersion", "17.06.0-ce"] + - [" swarm-manager", "192.168.99.101:2376"] + - [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:04:11Z"] + - [" └ ServerVersion", "17.06.0-ce"] + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: "Indicates if the host has kernel memory limit support enabled." + type: "boolean" + example: true + CpuCfsPeriod: + description: "Indicates if CPU CFS(Completely Fair Scheduler) period is supported by the host." + type: "boolean" + example: true + CpuCfsQuota: + description: "Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by the host." + type: "boolean" + example: true + CPUShares: + description: "Indicates if CPU Shares limiting is supported by the host." + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: "Indicates if the daemon is running in debug-mode / with debug-level logging enabled." + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd"] + default: "cgroupfs" + example: "cgroupfs" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://user:pass@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://user:pass@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "docker-runc" + example: + runc: + path: "docker-runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, and user-namespaces (userns). + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see [the inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container than inspecting a single container. For example, + the list of linked containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: | + The status of the container. For example, `"running"` or `"exited"`. + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the cgroups freezer is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "until" + in: "query" + description: "Only return logs before this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + Error: + description: "container waiting error, if any" + type: "object" + properties: + Message: + description: "Details of an error" + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: "Wait until a container state reaches the given condition, either 'not-running' (default), 'next-exit', or 'removed'." + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Platform: + type: "object" + required: [Name] + properties: + Name: + type: "string" + Components: + type: "array" + items: + type: "object" + x-go-name: ComponentVersion + required: [Name, Version] + properties: + Name: + type: "string" + Version: + type: "string" + x-nullable: false + Details: + type: "object" + x-nullable: true + + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `config=` config name or ID + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `node=` node ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `secret=` secret name or ID + - `service=` service name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + - `volume=` volume name + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + WorkingDir: + type: "string" + description: "The working directory for the exec process inside the container." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see [the network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than inspecting a single network. For example, + the list of containers attached to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + Ingress: + description: "Ingress network is the network which provides the routing-mesh in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created secret." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the configs list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + properties: + ID: + description: "The ID of the created config." + type: "string" + example: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: "The spec of the config to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [ConfigInspect endpoint](#operation/ConfigInspect) response values." + - name: "version" + in: "query" + description: "The version number of the config object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: "Return image digest and platform information by contacting the registry." + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: "A descriptor struct containing digest, media type, and size" + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: "An array containing all platforms supported by the image" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to call back to the client for advanced capabilities. + + > **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental + > features enabled. The specifications for this endpoint may still change in a future version of the API. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session (experimental)"] diff --git a/docs/api/v1.36.yaml b/docs/api/v1.36.yaml new file mode 100644 index 0000000000000..2ca85965f4b6b --- /dev/null +++ b/docs/api/v1.36.yaml @@ -0,0 +1,10032 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.36" +info: + title: "Docker Engine API" + version: "1.36" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release, so API calls are versioned to + ensure that clients don't break. To lock to a specific version of the API, + you prefix the URL with its version, for example, call `/v1.30/info` to use + the v1.30 version of the `/info` endpoint. If the API version specified in + the URL is not supported by the daemon, a HTTP `400 Bad Request` error message + is returned. + + If you omit the version-prefix, the current version of the API (v1.36) is used. + For example, calling `/info` is the same as calling `/v1.36/info`. Using the + API without a version-prefix is deprecated and will be removed in a future release. + + Engine releases in the near future should support this version of the API, + so your client will continue to work even if it is talking to a newer Engine. + + The API uses an open schema model, which means server may add extra properties + to responses. Likewise, the server will ignore any extra query parameters and + request body properties. When you write clients, you need to ignore additional + properties in responses to ensure they do not break when talking to newer + daemons. + + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + - name: "Config" + x-displayName: "Configs" + description: | + Configs are application configurations that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + type: "string" + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + format: "int64" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + example: "0-3" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + ResourceObject: + description: "An object describing the resources which can be advertised by a node and requested by a task" + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: "User-defined resources can be either Integer resources (e.g, `SSD=3`) or String resources (e.g, `GPU=UUID1`)" + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + StartPeriod: + description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + $ref: "#/definitions/PortMap" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: | + Allocates an ephemeral host port for all of a container's + exposed ports. + + Ports are de-allocated when the container stops and allocated when the container starts. + The allocated port might be changed when restarting the container. + + The port is selected from the ephemeral port range that depends on the kernel. + For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`. + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: "array" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + properties: + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: "array" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network's bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for both `tcp` and `udp`, two separate + entries are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + x-nullable: true + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + aux: + $ref: "#/definitions/ImageID" + + ImageID: + type: "object" + description: "Image ID or Digest" + properties: + ID: + type: "string" + example: + ID: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + + CreateImageInfo: + type: "object" + properties: + id: + type: "string" + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + current: + type: "integer" + total: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: "True if the plugin is running. False if the plugin is not running, only installed." + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentionally overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + type: "object" + properties: + TrustRoot: + description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + type: "string" + CertIssuerSubject: + description: "The base64-url-safe-encoded raw subject bytes of the issuer" + type: "string" + CertIssuerPublicKey: + description: "The base64-url-safe-encoded raw public key bytes of the issuer" + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + CACert: + description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)." + type: "string" + SigningCACert: + description: "The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format." + type: "string" + SigningCAKey: + description: "The desired signing CA key for all swarm node TLS leaf certificates, in PEM format." + type: "string" + ForceRotate: + description: "An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`" + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: "Whether there is currently a root CA rotation in progress for the swarm" + type: "boolean" + example: false + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: "Invalid when specified with `ContainerSpec`. *(Experimental release only.)*" + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: "Invalid when specified with `PluginSpec`." + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + File: + type: "string" + description: | + Load credential spec from this file. The file is read by the daemon, and must be present in the + `CredentialSpecs` subdirectory in the docker data directory, which defaults to + `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows registry. The specified registry value must be + located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + Configs: + description: "Configs contains references to zero or more configs that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + ConfigID: + description: "ConfigID represents the ID of the specific config that we're referencing." + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, but this is just provided for + lookup/display purposes. The config in the reference will be identified by its ID. + type: "string" + Isolation: + type: "string" + description: "Isolation technology of the containers running the service. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/ResourceObject" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Runtime: + description: "Runtime is the type of runtime specified for the task executor." + type: "string" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + PublishMode: + description: | + The mode in which port is published. + +


+ + - "ingress" makes the target port accessible on on every node, + regardless of whether there is a task for the service running on + that node or not. + - "host" bypasses the routing mesh and publish the port directly on + the swarm node where that service is running. + + type: "string" + enum: + - "ingress" + - "host" + default: "ingress" + example: "ingress" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: "Name of the secrets driver used to fetch the secret's value from an external secret store" + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + config data. + type: "string" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + SystemStatus: + description: | + Status information about this node (standalone Swarm API). + +


+ + > **Note**: The information returned in this field is only propagated + > by the Swarm standalone API, and is empty (`null`) when using + > built-in swarm mode. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Role", "primary"] + - ["State", "Healthy"] + - ["Strategy", "spread"] + - ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"] + - ["Nodes", "2"] + - [" swarm-agent-00", "192.168.99.102:2376"] + - [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:03:46Z"] + - [" └ ServerVersion", "17.06.0-ce"] + - [" swarm-manager", "192.168.99.101:2376"] + - [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:04:11Z"] + - [" └ ServerVersion", "17.06.0-ce"] + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: "Indicates if the host has kernel memory limit support enabled." + type: "boolean" + example: true + CpuCfsPeriod: + description: "Indicates if CPU CFS(Completely Fair Scheduler) period is supported by the host." + type: "boolean" + example: true + CpuCfsQuota: + description: "Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by the host." + type: "boolean" + example: true + CPUShares: + description: "Indicates if CPU Shares limiting is supported by the host." + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: "Indicates if the daemon is running in debug-mode / with debug-level logging enabled." + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd"] + default: "cgroupfs" + example: "cgroupfs" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://user:pass@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://user:pass@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "docker-runc" + example: + runc: + path: "docker-runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, and user-namespaces (userns). + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see [the inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container than inspecting a single container. For example, + the list of linked containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + title: "ContainerCreateResponse" + description: "OK response to ContainerCreate operation" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerInspectResponse" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: | + The status of the container. For example, `"running"` or `"exited"`. + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the cgroups freezer is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerTopResponse" + description: "OK response to ContainerTop operation" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "until" + in: "query" + description: "Only return logs before this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + title: "ContainerChangeResponseItem" + description: "change item in response to ContainerChanges operation" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + title: "ContainerUpdateResponse" + description: "OK response to ContainerUpdate operation" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + title: "ContainerWaitResponse" + description: "OK response to ContainerWait operation" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + Error: + description: "container waiting error, if any" + type: "object" + properties: + Message: + description: "Details of an error" + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: "Wait until a container state reaches the given condition, either 'not-running' (default), 'next-exit', or 'removed'." + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ContainerPruneResponse" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: "JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker uses the buildargs as the environment context for commands run via the `Dockerfile` RUN instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for passing secret values. [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg)" + type: "integer" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "BuildPruneResponse" + properties: + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + title: "HistoryResponseItem" + description: "individual image layer information in response to ImageHistory operation" + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + title: "ImageSearchResponseItem" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ImagePruneResponse" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + title: "SystemAuthResponse" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemVersionResponse" + properties: + Platform: + type: "object" + required: [Name] + properties: + Name: + type: "string" + Components: + type: "array" + items: + type: "object" + x-go-name: ComponentVersion + required: [Name, Version] + properties: + Name: + type: "string" + Version: + type: "string" + x-nullable: false + Details: + type: "object" + x-nullable: true + + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemEventsResponse" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `config=` config name or ID + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `node=` node ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `secret=` secret name or ID + - `service=` service name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + - `volume=` volume name + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemDataUsageResponse" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + WorkingDir: + type: "string" + description: "The working directory for the exec process inside the container." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ExecInspectResponse" + properties: + CanRemove: + type: "boolean" + DetachKeys: + type: "string" + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + title: "VolumeListResponse" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "VolumePruneResponse" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see [the network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than inspecting a single network. For example, + the list of containers attached to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + title: "NetworkCreateResponse" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + Ingress: + description: "Ingress network is the network which provides the routing-mesh in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "NetworkPruneResponse" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + title: "PluginPrivilegeItem" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "UnlockKeyResponse" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + title: "ServiceCreateResponse" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the configs list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: "The spec of the config to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [ConfigInspect endpoint](#operation/ConfigInspect) response values." + - name: "version" + in: "query" + description: "The version number of the config object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: "Return image digest and platform information by contacting the registry." + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + title: "DistributionInspectResponse" + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: "A descriptor struct containing digest, media type, and size" + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: "An array containing all platforms supported by the image" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to call back to the client for advanced capabilities. + + > **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental + > features enabled. The specifications for this endpoint may still change in a future version of the API. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session (experimental)"] diff --git a/docs/api/v1.37.yaml b/docs/api/v1.37.yaml new file mode 100644 index 0000000000000..7e1e5bd523a83 --- /dev/null +++ b/docs/api/v1.37.yaml @@ -0,0 +1,10075 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.37" +info: + title: "Docker Engine API" + version: "1.37" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release, so API calls are versioned to + ensure that clients don't break. To lock to a specific version of the API, + you prefix the URL with its version, for example, call `/v1.30/info` to use + the v1.30 version of the `/info` endpoint. If the API version specified in + the URL is not supported by the daemon, a HTTP `400 Bad Request` error message + is returned. + + If you omit the version-prefix, the current version of the API (v1.37) is used. + For example, calling `/info` is the same as calling `/v1.37/info`. Using the + API without a version-prefix is deprecated and will be removed in a future release. + + Engine releases in the near future should support this version of the API, + so your client will continue to work even if it is talking to a newer Engine. + + The API uses an open schema model, which means server may add extra properties + to responses. Likewise, the server will ignore any extra query parameters and + request body properties. When you write clients, you need to ignore additional + properties in responses to ensure they do not break when talking to newer + daemons. + + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + - name: "Config" + x-displayName: "Configs" + description: | + Configs are application configurations that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp", "sctp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + type: "string" + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + format: "int64" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + example: "0-3" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + Init: + description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used." + type: "boolean" + x-nullable: true + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + ResourceObject: + description: "An object describing the resources which can be advertised by a node and requested by a task" + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: "User-defined resources can be either Integer resources (e.g, `SSD=3`) or String resources (e.g, `GPU=UUID1`)" + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + StartPeriod: + description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + $ref: "#/definitions/PortMap" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: | + Allocates an ephemeral host port for all of a container's + exposed ports. + + Ports are de-allocated when the container stops and allocated when the container starts. + The allocated port might be changed when restarting the container. + + The port is selected from the ephemeral port range that depends on the kernel. + For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`. + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: "array" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: "array" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network's bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for multiple protocols, separate entries + are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + x-nullable: true + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + aux: + $ref: "#/definitions/ImageID" + + ImageID: + type: "object" + description: "Image ID or Digest" + properties: + ID: + type: "string" + example: + ID: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + + CreateImageInfo: + type: "object" + properties: + id: + type: "string" + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + current: + type: "integer" + total: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: "True if the plugin is running. False if the plugin is not running, only installed." + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentionally overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + type: "object" + properties: + TrustRoot: + description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + type: "string" + CertIssuerSubject: + description: "The base64-url-safe-encoded raw subject bytes of the issuer" + type: "string" + CertIssuerPublicKey: + description: "The base64-url-safe-encoded raw public key bytes of the issuer" + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + CACert: + description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)." + type: "string" + SigningCACert: + description: "The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format." + type: "string" + SigningCAKey: + description: "The desired signing CA key for all swarm node TLS leaf certificates, in PEM format." + type: "string" + ForceRotate: + description: "An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`" + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: "Whether there is currently a root CA rotation in progress for the swarm" + type: "boolean" + example: false + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: "Invalid when specified with `ContainerSpec`. *(Experimental release only.)*" + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: "Invalid when specified with `PluginSpec`." + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + File: + type: "string" + description: | + Load credential spec from this file. The file is read by the daemon, and must be present in the + `CredentialSpecs` subdirectory in the docker data directory, which defaults to + `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows registry. The specified registry value must be + located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + Configs: + description: "Configs contains references to zero or more configs that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + ConfigID: + description: "ConfigID represents the ID of the specific config that we're referencing." + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, but this is just provided for + lookup/display purposes. The config in the reference will be identified by its ID. + type: "string" + Isolation: + type: "string" + description: "Isolation technology of the containers running the service. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/ResourceObject" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Runtime: + description: "Runtime is the type of runtime specified for the task executor." + type: "string" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + - "remove" + - "orphaned" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + - "sctp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + PublishMode: + description: | + The mode in which port is published. + +


+ + - "ingress" makes the target port accessible on on every node, + regardless of whether there is a task for the service running on + that node or not. + - "host" bypasses the routing mesh and publish the port directly on + the swarm node where that service is running. + + type: "string" + enum: + - "ingress" + - "host" + default: "ingress" + example: "ingress" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: "Name of the secrets driver used to fetch the secret's value from an external secret store" + $ref: "#/definitions/Driver" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + config data. + type: "string" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + SystemStatus: + description: | + Status information about this node (standalone Swarm API). + +


+ + > **Note**: The information returned in this field is only propagated + > by the Swarm standalone API, and is empty (`null`) when using + > built-in swarm mode. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Role", "primary"] + - ["State", "Healthy"] + - ["Strategy", "spread"] + - ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"] + - ["Nodes", "2"] + - [" swarm-agent-00", "192.168.99.102:2376"] + - [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:03:46Z"] + - [" └ ServerVersion", "17.06.0-ce"] + - [" swarm-manager", "192.168.99.101:2376"] + - [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:04:11Z"] + - [" └ ServerVersion", "17.06.0-ce"] + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: "Indicates if the host has kernel memory limit support enabled." + type: "boolean" + example: true + CpuCfsPeriod: + description: "Indicates if CPU CFS(Completely Fair Scheduler) period is supported by the host." + type: "boolean" + example: true + CpuCfsQuota: + description: "Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by the host." + type: "boolean" + example: true + CPUShares: + description: "Indicates if CPU Shares limiting is supported by the host." + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: "Indicates if the daemon is running in debug-mode / with debug-level logging enabled." + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd"] + default: "cgroupfs" + example: "cgroupfs" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://user:pass@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://user:pass@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "docker-runc" + example: + runc: + path: "docker-runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, and user-namespaces (userns). + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see [the inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container than inspecting a single container. For example, + the list of linked containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + title: "ContainerCreateResponse" + description: "OK response to ContainerCreate operation" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerInspectResponse" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: | + The status of the container. For example, `"running"` or `"exited"`. + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the cgroups freezer is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + type: "string" + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerTopResponse" + description: "OK response to ContainerTop operation" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "until" + in: "query" + description: "Only return logs before this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + title: "ContainerChangeResponseItem" + description: "change item in response to ContainerChanges operation" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of last read, which is used + for calculating the CPU usage percentage. It is not the same as the + `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is not running" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "Container d37cde0fe4ad63c3a7252023b2f9800282894247d145cb5933ddf6e52cc03a28 is not running" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + title: "ContainerUpdateResponse" + description: "OK response to ContainerUpdate operation" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + title: "ContainerWaitResponse" + description: "OK response to ContainerWait operation" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + Error: + description: "container waiting error, if any" + type: "object" + properties: + Message: + description: "Details of an error" + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: "Wait until a container state reaches the given condition, either 'not-running' (default), 'next-exit', or 'removed'." + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ContainerPruneResponse" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: > + JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker + uses the buildargs as the environment context for commands run via the `Dockerfile` RUN + instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for + passing secret values. + + + For example, the build arg `FOO=bar` would become `{"FOO":"bar"}` in JSON. This would result in the + query parameter `buildargs={"FOO":"bar"}`. Note that `{"FOO":"bar"}` should be URI component encoded. + + + [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg) + type: "string" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + - name: "target" + in: "query" + description: "Target build stage" + type: "string" + default: "" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "BuildPruneResponse" + properties: + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + title: "HistoryResponseItem" + description: "individual image layer information in response to ImageHistory operation" + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + title: "ImageSearchResponseItem" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ImagePruneResponse" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + title: "SystemAuthResponse" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemVersionResponse" + properties: + Platform: + type: "object" + required: [Name] + properties: + Name: + type: "string" + Components: + type: "array" + items: + type: "object" + x-go-name: ComponentVersion + required: [Name, Version] + properties: + Name: + type: "string" + Version: + type: "string" + x-nullable: false + Details: + type: "object" + x-nullable: true + + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemEventsResponse" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `config=` config name or ID + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `node=` node ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `secret=` secret name or ID + - `service=` service name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + - `volume=` volume name + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemDataUsageResponse" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + WorkingDir: + type: "string" + description: "The working directory for the exec process inside the container." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ExecInspectResponse" + properties: + CanRemove: + type: "boolean" + DetachKeys: + type: "string" + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + title: "VolumeListResponse" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "VolumePruneResponse" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see [the network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than inspecting a single network. For example, + the list of containers attached to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + title: "NetworkCreateResponse" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + Ingress: + description: "Ingress network is the network which provides the routing-mesh in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "NetworkPruneResponse" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + title: "PluginPrivilegeItem" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "UnlockKeyResponse" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + title: "ServiceCreateResponse" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the configs list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: "The spec of the config to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [ConfigInspect endpoint](#operation/ConfigInspect) response values." + - name: "version" + in: "query" + description: "The version number of the config object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: "Return image digest and platform information by contacting the registry." + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + title: "DistributionInspectResponse" + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: "A descriptor struct containing digest, media type, and size" + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: "An array containing all platforms supported by the image" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to call back to the client for advanced capabilities. + + > **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental + > features enabled. The specifications for this endpoint may still change in a future version of the API. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session (experimental)"] diff --git a/docs/api/v1.38.yaml b/docs/api/v1.38.yaml new file mode 100644 index 0000000000000..f593e09c2b8ff --- /dev/null +++ b/docs/api/v1.38.yaml @@ -0,0 +1,10136 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.38" +info: + title: "Docker Engine API" + version: "1.38" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the Docker client uses to communicate with the Engine, so everything the Docker client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` is `GET /containers/json`). The notable exception is running containers, which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure of the API call. The body of the response will be JSON in the following format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release, so API calls are versioned to + ensure that clients don't break. To lock to a specific version of the API, + you prefix the URL with its version, for example, call `/v1.30/info` to use + the v1.30 version of the `/info` endpoint. If the API version specified in + the URL is not supported by the daemon, a HTTP `400 Bad Request` error message + is returned. + + If you omit the version-prefix, the current version of the API (v1.38) is used. + For example, calling `/info` is the same as calling `/v1.38/info`. Using the + API without a version-prefix is deprecated and will be removed in a future release. + + Engine releases in the near future should support this version of the API, + so your client will continue to work even if it is talking to a newer Engine. + + The API uses an open schema model, which means server may add extra properties + to responses. Likewise, the server will ignore any extra query parameters and + request body properties. When you write clients, you need to ignore additional + properties in responses to ensure they do not break when talking to newer + daemons. + + + # Authentication + + Authentication for registries is handled client side. The client has to send authentication details to various endpoints that need to communicate with registries, such as `POST /images/(name)/push`. These are sent as `X-Registry-Auth` header as a Base64 encoded (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. See the [networking documentation](https://docs.docker.com/network/) for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. See the [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) for more information. + + To exec a command in a container, you first need to create an exec instance, then start it. These two API endpoints are wrapped up in a single command-line command, `docker exec`. + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. See [the swarm mode documentation](https://docs.docker.com/engine/swarm/) for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must be enabled for these endpoints to work. + - name: "Config" + x-displayName: "Configs" + description: | + Configs are application configurations that can be used by services. Swarm mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + description: "Host IP address that the container's port is mapped to" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp", "sctp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + type: "string" + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: "If `on-failure` is used, the number of times to retry before giving up" + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: "An integer value representing this container's relative CPU weight versus other containers." + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + format: "int64" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: "Path to `cgroups` under which the container's `cgroup` is created. If the path is not absolute, the path is considered to be relative to the `cgroups` path of the init process. Cgroups are created if they do not already exist." + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form `[{"Path": "device_path", "Weight": weight}]`. + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form `[{"Path": "device_path", "Rate": rate}]`. + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: "The length of a CPU real-time period in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: "The length of a CPU real-time runtime in microseconds. Set to 0 to allocate no time allocated to real-time tasks." + type: "integer" + format: "int64" + CpusetCpus: + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)" + type: "string" + example: "0-3" + CpusetMems: + description: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems." + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: "Total memory limit (memory + swap). Set as `-1` to enable unlimited swap." + type: "integer" + format: "int64" + MemorySwappiness: + description: "Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100." + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + Init: + description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used." + type: "boolean" + x-nullable: true + PidsLimit: + description: "Tune a container's pids limit. Set -1 for unlimited." + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are mutually exclusive. The order of precedence is `CPUCount` first, then `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: "Maximum IO in bytes per second for the container system drive (Windows only)" + type: "integer" + format: "int64" + + ResourceObject: + description: "An object describing the resources which can be advertised by a node and requested by a task" + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: "User-defined resources can be either Integer resources (e.g, `SSD=3`) or String resources (e.g, `GPU=UUID1`)" + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Timeout: + description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + Retries: + description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." + type: "integer" + StartPeriod: + description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." + type: "integer" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding is a string in one of these forms: + + - `host-src:container-dest` to bind-mount a host path into the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `host-src:container-dest:ro` to make the bind mount read-only inside the container. Both `host-src`, and `container-dest` must be an _absolute_ path. + - `volume-name:container-dest` to bind-mount a volume managed by a volume driver into the container. `container-dest` must be an _absolute_ path. + - `volume-name:container-dest:ro` to mount the volume read-only inside the container. `container-dest` must be an _absolute_ path. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: "Network mode to use for this container. Supported standard values are: `bridge`, `host`, `none`, and `container:`. Any other value is taken + as a custom network's name to which this container should connect to." + PortBindings: + $ref: "#/definitions/PortMap" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: "Automatically remove the container when the container's process exits. This has no effect if `RestartPolicy` is set." + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: "A list of volumes to inherit from another container, specified in the form `[:]`." + items: + type: "string" + Mounts: + description: "Specification for mounts to be added to the container." + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: "A list of kernel capabilities to add to the container." + items: + type: "string" + CapDrop: + type: "array" + description: "A list of kernel capabilities to drop from the container." + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: "A list of links for the container in the form `container_name:alias`." + items: + type: "string" + OomScoreAdj: + type: "integer" + description: "An integer value containing the score given to the container in order to tune OOM killer preferences." + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: | + Allocates an ephemeral host port for all of a container's + exposed ports. + + Ports are de-allocated when the container stops and allocated when the container starts. + The allocated port might be changed when restarting the container. + + The port is selected from the ephemeral port range that depends on the kernel. + For example, on Linux the range is defined by `/proc/sys/net/ipv4/ip_local_port_range`. + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs mounts, and their corresponding mount options. For example: `{ "/run": "rw,noexec,nosuid,size=65536k" }`. + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: "Sets the usernamespace mode for the container when usernamespace remapping option is enabled." + ShmSize: + type: "integer" + description: "Size of `/dev/shm` in bytes. If omitted, the system uses 64MB." + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. For example: `{"net.ipv4.ip_forward": "1"}` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: "Initial console size, as an `[height, width]` array. (Windows only)" + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: "Isolation technology of the container. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + MaskedPaths: + type: "array" + description: "The list of paths to be masked inside the container (this overrides the default set of paths)" + items: + type: "string" + ReadonlyPaths: + type: "array" + description: "The list of paths to be set as read-only inside the container (this overrides the default set of paths)" + items: + type: "string" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: "Attach standard streams to a TTY, including `stdin` if it is not closed." + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the form `["VAR=value", ...]`. A variable without `=` is removed from the environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: "Command to run specified as a string or an array of strings." + type: "array" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: "The name of the image to use when creating the container" + type: "string" + Volumes: + description: "An object mapping mount point paths inside the container to empty objects." + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the entry point is reset to system default (i.e., the entry point used by docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: "array" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: "`ONBUILD` metadata that were defined in the image's `Dockerfile`." + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: "Signal to stop a container as a string or unsigned integer." + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: "Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell." + type: "array" + items: + type: "string" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network's bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for multiple protocols, separate entries + are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + x-nullable: true + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: "The level at which the volume exists. Either `global` for cluster-wide, or `local` for machine level." + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: "The driver specific options used when creating the volume." + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: "List of IPAM configuration options, specified as a map: `{\"Subnet\": , \"IPRange\": , \"Gateway\": , \"AuxAddress\": }`" + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + aux: + $ref: "#/definitions/ImageID" + + ImageID: + type: "object" + description: "Image ID or Digest" + properties: + ID: + type: "string" + example: + ID: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + + CreateImageInfo: + type: "object" + properties: + id: + type: "string" + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + current: + type: "integer" + total: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: "True if the plugin is running. False if the plugin is not running, only installed." + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + ProtocolScheme: + type: "string" + example: "some.protocol/v1.0" + description: "Protocol to use for clients connecting to the plugin." + enum: + - "" + - "moby.plugins.http/v1" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed to avoid conflicting writes. + The client must send the version number along with the modified specification when updating these objects. + This approach ensures safe concurrency and determinism in that the change on the object + may not be applied if the version number has changed from the last read. In other words, + if two update requests specify the same base version, only one of the requests can succeed. + As a result, two separate update requests that happen at the same time will not + unintentionally overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: "Information about the issuer of leaf TLS certificates and the trusted root CA certificate" + type: "object" + properties: + TrustRoot: + description: "The root CA certificate(s) that are used to validate leaf TLS certificates" + type: "string" + CertIssuerSubject: + description: "The base64-url-safe-encoded raw subject bytes of the issuer" + type: "string" + CertIssuerPublicKey: + description: "The base64-url-safe-encoded raw public key bytes of the issuer" + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: "The number of historic tasks to keep per instance or node. If negative, never remove completed or failed tasks." + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: "The number of snapshots to keep beyond the current snapshot." + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: "The number of log entries to keep around to sync up slow followers after a snapshot is created." + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: "The delay for an agent to send a heartbeat to the dispatcher." + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: "Configuration for forwarding signing requests to an external certificate authority." + type: "array" + items: + type: "object" + properties: + Protocol: + description: "Protocol for communication with the external CA (currently only `cfssl` is supported)." + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: "URL where certificate signing requests should be sent." + type: "string" + Options: + description: "An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver." + type: "object" + additionalProperties: + type: "string" + CACert: + description: "The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided)." + type: "string" + SigningCACert: + description: "The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format." + type: "string" + SigningCAKey: + description: "The desired signing CA key for all swarm node TLS leaf certificates, in PEM format." + type: "string" + ForceRotate: + description: "An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`" + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: "If set, generate a key and use it to lock data stored on the managers." + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: "Whether there is currently a root CA rotation in progress for the swarm" + type: "boolean" + example: false + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: | + Plugin spec for the service. *(Experimental release only.)* + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: | + Container spec for the service. + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Env: + description: "A list of environment variables in the form `VAR=value`." + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: "A list of additional groups that the container process will run as." + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + File: + type: "string" + description: | + Load credential spec from this file. The file is read by the daemon, and must be present in the + `CredentialSpecs` subdirectory in the docker data directory, which defaults to + `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows registry. The specified registry value must be + located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: "Specification for mounts to be added to containers created as part of the service." + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: "Amount of time to wait for the container to terminate before forcefully killing it." + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: "Specification for DNS related configurations in resolver configuration file (`resolv.conf`)." + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: "A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.)." + type: "array" + items: + type: "string" + Secrets: + description: "Secrets contains references to zero or more secrets that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: "SecretID represents the ID of the specific secret that we're referencing." + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, but this is just provided for + lookup/display purposes. The secret in the reference will be identified by its ID. + type: "string" + Configs: + description: "Configs contains references to zero or more configs that will be exposed to the service." + type: "array" + items: + type: "object" + properties: + File: + description: "File represents a specific target that is backed by a file." + type: "object" + properties: + Name: + description: "Name represents the final filename in the filesystem." + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + ConfigID: + description: "ConfigID represents the ID of the specific config that we're referencing." + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, but this is just provided for + lookup/display purposes. The config in the reference will be identified by its ID. + type: "string" + Isolation: + type: "string" + description: "Isolation technology of the containers running the service. (Windows only)" + enum: + - "default" + - "process" + - "hyperv" + Init: + description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used." + type: "boolean" + x-nullable: true + NetworkAttachmentSpec: + description: | + Read-only spec type for non-swarm containers attached to swarm overlay + networks. + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + type: "object" + properties: + ContainerID: + description: "ID of the container represented by this task" + type: "string" + Resources: + description: "Resource requirements which apply to each individual container created as part of the service." + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/ResourceObject" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: "Specification for the restart policy which applies to containers created as part of this service." + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: "Maximum attempts to restart a given container before giving up (default value is 0, which is ignored)." + type: "integer" + format: "int64" + default: 0 + Window: + description: "Windows is the time window used to evaluate the restart policy (default value is 0, which is unbounded)." + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: "An array of constraints." + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + Preferences: + description: "Preferences provide a way to make the scheduler aware of factors such as topology. They are provided in order from highest to lowest precedence." + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: "label descriptor, such as engine.labels.az" + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: "A counter that triggers an update even if no relevant parameters have been changed." + type: "integer" + Runtime: + description: "Runtime is the type of runtime specified for the task executor." + type: "string" + Networks: + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + LogDriver: + description: "Specifies the log driver to use for tasks created from this spec. If not present, the default one for the swarm will be used, finally falling back to the engine default if not specified." + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + - "remove" + - "orphaned" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be updated in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an updated task fails to run, or stops running during the update." + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: "Amount of time to monitor each updated task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during an update before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling out an updated task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: "Maximum number of tasks to be rolled back in one iteration (0 means unlimited parallelism)." + type: "integer" + format: "int64" + Delay: + description: "Amount of time between rollback iterations, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: "Action to take if an rolled back task fails to run, or stops running during the rollback." + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: "Amount of time to monitor each rolled back task for failures, in nanoseconds." + type: "integer" + format: "int64" + MaxFailureRatio: + description: "The fraction of tasks that may fail during a rollback before the failure action is invoked, specified as a floating point number between 0 and 1." + type: "number" + default: 0 + Order: + description: "The order of operations when rolling back a task. Either the old task is shut down before the new task is started, or the new task is started before the old task is shut down." + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Array of network names or IDs to attach the service to." + type: "array" + items: + type: "object" + properties: + Target: + type: "string" + Aliases: + type: "array" + items: + type: "string" + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + - "sctp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + PublishMode: + description: | + The mode in which port is published. + +


+ + - "ingress" makes the target port accessible on on every node, + regardless of whether there is a task for the service running on + that node or not. + - "host" bypasses the routing mesh and publish the port directly on + the swarm node where that service is running. + + type: "string" + enum: + - "ingress" + - "host" + default: "ingress" + example: "ingress" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: "The mode of resolution to use for internal load balancing + between tasks." + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: "List of exposed ports that this service is accessible on from the outside. Ports can only be provided if `vip` resolution mode is used." + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: "Name of the secrets driver used to fetch the secret's value from an external secret store" + $ref: "#/definitions/Driver" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-3.2)) + config data. + type: "string" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + SystemStatus: + description: | + Status information about this node (standalone Swarm API). + +


+ + > **Note**: The information returned in this field is only propagated + > by the Swarm standalone API, and is empty (`null`) when using + > built-in swarm mode. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Role", "primary"] + - ["State", "Healthy"] + - ["Strategy", "spread"] + - ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"] + - ["Nodes", "2"] + - [" swarm-agent-00", "192.168.99.102:2376"] + - [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:03:46Z"] + - [" └ ServerVersion", "17.06.0-ce"] + - [" swarm-manager", "192.168.99.101:2376"] + - [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:04:11Z"] + - [" └ ServerVersion", "17.06.0-ce"] + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: "Indicates if the host has kernel memory limit support enabled." + type: "boolean" + example: true + CpuCfsPeriod: + description: "Indicates if CPU CFS(Completely Fair Scheduler) period is supported by the host." + type: "boolean" + example: true + CpuCfsQuota: + description: "Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by the host." + type: "boolean" + example: true + CPUShares: + description: "Indicates if CPU Shares limiting is supported by the host." + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: "Indicates if the daemon is running in debug-mode / with debug-level logging enabled." + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd"] + default: "cgroupfs" + example: "cgroupfs" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://user:pass@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://user:pass@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "docker-runc" + example: + runc: + path: "docker-runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, and user-namespaces (userns). + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see [the inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container than inspecting a single container. For example, + the list of linked containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: "Return all containers. By default, only running containers are shown" + type: "boolean" + default: false + - name: "limit" + in: "query" + description: "Return this number of most recently created containers, including non-running ones." + type: "integer" + - name: "size" + in: "query" + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a `map[string][]string`). For example, `{"status": ["paused"]}` will only return paused containers. Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: "Assign the specified name to the container. Must match `/?[a-zA-Z0-9_-]+`." + type: "string" + pattern: "/?[a-zA-Z0-9_-]+" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + description: "This container's networking configuration." + type: "object" + properties: + EndpointsConfig: + description: "A mapping of network name to endpoint configuration for that network." + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + title: "ContainerCreateResponse" + description: "OK response to ContainerCreate operation" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerInspectResponse" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + description: "The state of the container." + type: "object" + properties: + Status: + description: | + The status of the container. For example, `"running"` or `"exited"`. + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the cgroups freezer is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + Paused: + description: "Whether this container is paused." + type: "boolean" + Restarting: + description: "Whether this container is restarting." + type: "boolean" + OOMKilled: + description: "Whether this container has been killed because it ran out of memory." + type: "boolean" + Dead: + type: "boolean" + Pid: + description: "The process ID of this container" + type: "integer" + ExitCode: + description: "The last exit code of this container" + type: "integer" + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + FinishedAt: + description: "The time when this container last exited." + type: "string" + Image: + description: "The container's image" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + description: "IDs of exec instances that are running in the container." + type: "array" + items: + type: "string" + x-nullable: true + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: "The size of files that have been created or changed by this container." + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + ExecIDs: + - "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca" + - "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: "On Unix systems, this is done by running the `ps` command. This endpoint is not supported on Windows." + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerTopResponse" + description: "OK response to ContainerTop operation" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: "Each process running in the container, where each is process is an array of values corresponding to the titles" + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "until" + in: "query" + description: "Only return logs before this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + title: "ContainerChangeResponseItem" + description: "change item in response to ContainerChanges operation" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of the *previous* read, and is + used to calculate the CPU usage percentage. It is not an exact copy + of the `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: "Stream the output. If false, the stats will be output once and then it will disconnect." + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container. You must restart the container for the resize to take effect." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the tty session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the tty session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: "Send a POSIX signal to a container, defaulting to killing to the container." + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is not running" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "Container d37cde0fe4ad63c3a7252023b2f9800282894247d145cb5933ddf6e52cc03a28 is not running" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: "Change various configuration options of a container without having to recreate it." + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + title: "ContainerUpdateResponse" + description: "OK response to ContainerUpdate operation" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the cgroups freezer to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, which is observable by the process being suspended. With the cgroups freezer the process is unaware, and unable to capture, that it is being suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach to the same container multiple times and you can reattach to containers that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint to do anything. + + See [the documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), the stream over the hijacked connected is multiplexed to separate out `stdout` and `stderr`. The stream consists of a series of frames, each containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or `stderr`). It also contains the size of the associated frame encoded in the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size encoded as big endian. + + Following the header is the payload, which is the specified number of bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), the stream is not multiplexed. The data exchanged over the hijacked connection is simply the raw data from the process PTY and client's `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been returned, it will seamlessly transition into streaming current output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Stream attached streams from the time the request was made onwards" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: "Override the key sequence for detaching a container.Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,`, or `_`." + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + title: "ContainerWaitResponse" + description: "OK response to ContainerWait operation" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + Error: + description: "container waiting error, if any" + type: "object" + properties: + Message: + description: "Details of an error" + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: "Wait until a container state reaches the given condition, either 'not-running' (default), 'next-exit', or 'removed'." + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "You cannot remove a running container: c2ada9df5af8. Stop the container before attempting removal or force remove" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: "A response header `X-Docker-Container-Path-Stat` is return containing a base64 - encoded JSON object with some filesystem header information about the path." + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: "TODO" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: "The error message. Either \"must specify path parameter\" (path cannot be empty) or \"not a directory\" (path was asserted to be a directory but exists as a file)." + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: "If “1”, “true”, or “True” then it will be an error if unpacking the given content would cause an existing directory to be replaced with a non-directory and vice versa." + type: "string" + - name: "inputStream" + in: "body" + required: true + description: "The input stream must be a tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ContainerPruneResponse" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: > + JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker + uses the buildargs as the environment context for commands run via the `Dockerfile` RUN + instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for + passing secret values. + + + For example, the build arg `FOO=bar` would become `{"FOO":"bar"}` in JSON. This would result in the + query parameter `buildargs={"FOO":"bar"}`. Note that `{"FOO":"bar"}` should be URI component encoded. + + + [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg) + type: "string" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: "Sets the networking mode for the run commands during + build. Supported standard values are: `bridge`, `host`, `none`, and + `container:`. Any other value is taken as a custom network's + name to which this container should connect to." + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + - name: "target" + in: "query" + description: "Target build stage" + type: "string" + default: "" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "BuildPruneResponse" + properties: + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + title: "HistoryResponseItem" + description: "individual image layer information in response to ImageHistory operation" + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must already have a tag which references the registry. For example, `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + title: "ImageSearchResponseItem" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ImagePruneResponse" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: "Validate credentials for a registry and, if available, get an identity token for accessing the registry without password." + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + title: "SystemAuthResponse" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemVersionResponse" + properties: + Platform: + type: "object" + required: [Name] + properties: + Name: + type: "string" + Components: + type: "array" + items: + type: "object" + x-go-name: ComponentVersion + required: [Name, Version] + properties: + Name: + type: "string" + Version: + type: "string" + x-nullable: false + Details: + type: "object" + x-nullable: true + + Version: + type: "string" + ApiVersion: + type: "string" + MinAPIVersion: + type: "string" + GitCommit: + type: "string" + GoVersion: + type: "string" + Os: + type: "string" + Arch: + type: "string" + KernelVersion: + type: "string" + Experimental: + type: "boolean" + BuildTime: + type: "string" + examples: + application/json: + Version: "17.04.0" + Os: "linux" + KernelVersion: "3.19.0-23-generic" + GoVersion: "go1.7.5" + GitCommit: "deadbee" + Arch: "amd64" + ApiVersion: "1.27" + MinAPIVersion: "1.12" + BuildTime: "2016-06-14T07:09:13.444803460+00:00" + Experimental: true + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemEventsResponse" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `config=` config name or ID + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `node=` node ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `secret=` secret name or ID + - `service=` service name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + - `volume=` volume name + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemDataUsageResponse" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image repositories. + + For each value of the `names` parameter: if it is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned; if it is an image ID, similarly only that image (and its parents) are returned and there would be no names referenced in the 'repositories' file for this image ID. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see [the export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: "A list of environment variables in the form `[\"VAR=value\", ...]`." + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: "The user, and optionally, group to run the exec process inside the container. Format is one of: `user`, `user:group`, `uid`, or `uid:gid`." + WorkingDir: + type: "string" + description: "The working directory for the exec process inside the container." + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: "Starts a previously set up exec instance. If detach is true, this endpoint returns immediately after starting the command. Otherwise, it sets up an interactive session with the command." + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: "Resize the TTY session used by an exec instance. This endpoint only works if `tty` was specified as part of creating and starting the exec instance." + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ExecInspectResponse" + properties: + CanRemove: + type: "boolean" + DetachKeys: + type: "string" + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + title: "VolumeListResponse" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: "Warnings that occurred when fetching the list of volumes" + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + properties: + Name: + description: "The new volume's name. If not specified, Docker generates a name." + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: "A mapping of driver options and values. These options are passed directly to the driver and are driver specific." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "VolumePruneResponse" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see [the network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than inspecting a single network. For example, + the list of containers attached to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process on the networks list. Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + title: "NetworkCreateResponse" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: "Check for networks with duplicate names. Since Network is primarily keyed based on a random ID and not on the name, and network name is strictly a user-friendly alias to the network which is uniquely identified using ID, there is no guaranteed way to check for duplicates. CheckDuplicate is there to provide a best effort checking of any networks which has the same name but it is not guaranteed to catch all name collisions." + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: "Globally scoped network is manually attachable by regular containers from workers in swarm mode." + type: "boolean" + Ingress: + description: "Ingress network is the network which provides the routing-mesh in swarm mode." + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to disconnect from the network." + Force: + type: "boolean" + description: "Force the container to disconnect from the network." + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "NetworkPruneResponse" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the plugin list. Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: "Describes a permission the user has to accept upon installing the plugin." + type: "object" + title: "PluginPrivilegeItem" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "force" + in: "query" + description: "Disable the plugin before removing. This may result in issues if the plugin is in use by a container." + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration to use when pulling a plugin from a registry. [See the authentication section for details.](#section/Authentication)" + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: "Describes a permission accepted by the user upon installing the plugin." + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: "The name of the plugin. The `:latest` tag is optional, and is the default if omitted." + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: "The version number of the node object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP). This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the default swarm listening port is used." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + type: "string" + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: "Listen address used for inter-manager communication if the node gets promoted to manager, as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP)." + type: "string" + AdvertiseAddr: + description: "Externally reachable address advertised to other nodes. This can either be an address/port combination in the form `192.168.1.1:4567`, or an interface followed by a port number, like `eth0:4567`. If the port number is omitted, the port number from the listen address is used. If `AdvertiseAddr` is not specified, it will be automatically detected when possible." + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: ``), for example, `192.168.1.1`, + or an interface, like `eth0`. If `DataPathAddr` is unspecified, the same address as `AdvertiseAddr` + is used. + + The `DataPathAddr` specifies the address that global scope network drivers will publish towards other + nodes in order to reach the containers running on this node. Using this parameter it is possible to + separate the container data traffic from the management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: "Addresses of manager nodes already participating in the swarm." + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: "Force leave swarm, even if this is the last manager or that it will break the cluster." + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: "The version number of the swarm object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "UnlockKeyResponse" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the services list. Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + title: "ServiceCreateResponse" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: "The version number of the service object being updated. This is required to avoid conflicting writes." + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + type: "string" + description: "If the X-Registry-Auth header is not specified, this + parameter indicates where to find registry authorization credentials. The + valid values are `spec` and `previous-spec`." + default: "spec" + - name: "rollback" + in: "query" + type: "string" + description: "Set to this parameter to `previous` to cause a + server-side rollback to the previous service spec. The supplied spec will be + ignored in this case." + - name: "X-Registry-Auth" + in: "header" + description: "A base64-encoded auth configuration for pulling from private registries. [See the authentication section for details.](#section/Authentication)" + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the tasks list. Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + + **Note**: This endpoint works only for services with the `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: "Only return this number of log lines from the end of the logs. Specify as an integer or `all` to output all log lines." + type: "string" + default: "all" + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: "The spec of the secret to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [SecretInspect endpoint](#operation/SecretInspect) response values." + - name: "version" + in: "query" + description: "The version number of the secret object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the configs list. Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: "The spec of the config to update. Currently, only the Labels field can be updated. All other fields must remain unchanged from the [ConfigInspect endpoint](#operation/ConfigInspect) response values." + - name: "version" + in: "query" + description: "The version number of the config object being updated. This is required to avoid conflicting writes." + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: "Return image digest and platform information by contacting the registry." + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + title: "DistributionInspectResponse" + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: "A descriptor struct containing digest, media type, and size" + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: "An array containing all platforms supported by the image" + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to call back to the client for advanced capabilities. + + > **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental + > features enabled. The specifications for this endpoint may still change in a future version of the API. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session (experimental)"] diff --git a/docs/api/v1.39.yaml b/docs/api/v1.39.yaml new file mode 100644 index 0000000000000..1cf462a7e6f2b --- /dev/null +++ b/docs/api/v1.39.yaml @@ -0,0 +1,11072 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.39" +info: + title: "Docker Engine API" + version: "1.39" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the + Docker client uses to communicate with the Engine, so everything the Docker + client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` + is `GET /containers/json`). The notable exception is running containers, + which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure + of the API call. The body of the response will be JSON in the following + format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release, so API calls are versioned to + ensure that clients don't break. To lock to a specific version of the API, + you prefix the URL with its version, for example, call `/v1.30/info` to use + the v1.30 version of the `/info` endpoint. If the API version specified in + the URL is not supported by the daemon, a HTTP `400 Bad Request` error message + is returned. + + If you omit the version-prefix, the current version of the API (v1.39) is used. + For example, calling `/info` is the same as calling `/v1.39/info`. Using the + API without a version-prefix is deprecated and will be removed in a future release. + + Engine releases in the near future should support this version of the API, + so your client will continue to work even if it is talking to a newer Engine. + + The API uses an open schema model, which means server may add extra properties + to responses. Likewise, the server will ignore any extra query parameters and + request body properties. When you write clients, you need to ignore additional + properties in responses to ensure they do not break when talking to newer + daemons. + + + # Authentication + + Authentication for registries is handled client side. The client has to send + authentication details to various endpoints that need to communicate with + registries, such as `POST /images/(name)/push`. These are sent as + `X-Registry-Auth` header as a [base64url encoded](https://tools.ietf.org/html/rfc4648#section-5) + (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this + structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), + you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. + See the [networking documentation](https://docs.docker.com/network/) + for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. Refer to the + [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) + for more information. + + To exec a command in a container, you first need to create an exec instance, + then start it. These two API endpoints are wrapped up in a single command-line + command, `docker exec`. + + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. Refer to the + [swarm mode documentation](https://docs.docker.com/engine/swarm/) + for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode + must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must + be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit + of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must + be enabled for these endpoints to work. + - name: "Config" + x-displayName: "Configs" + description: | + Configs are application configurations that can be used by services. Swarm + mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + description: "Host IP address that the container's port is mapped to" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp", "sctp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + type: "string" + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to + restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is + added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: | + If `on-failure` is used, the number of times to retry before giving up. + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: | + An integer value representing this container's relative CPU weight + versus other containers. + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + format: "int64" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: | + Path to `cgroups` under which the container's `cgroup` is created. If + the path is not absolute, the path is considered to be relative to the + `cgroups` path of the init process. Cgroups are created if they do not + already exist. + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form: + + ``` + [{"Path": "device_path", "Weight": weight}] + ``` + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: | + Microseconds of CPU time that the container can get in a CPU period. + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: | + The length of a CPU real-time period in microseconds. Set to 0 to + allocate no time allocated to real-time tasks. + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: | + The length of a CPU real-time runtime in microseconds. Set to 0 to + allocate no time allocated to real-time tasks. + type: "integer" + format: "int64" + CpusetCpus: + description: | + CPUs in which to allow execution (e.g., `0-3`, `0,1`). + type: "string" + example: "0-3" + CpusetMems: + description: | + Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only + effective on NUMA systems. + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DiskQuota: + description: "Disk limit (in bytes)." + type: "integer" + format: "int64" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + example: 209715200 + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: | + Total memory limit (memory + swap). Set as `-1` to enable unlimited + swap. + type: "integer" + format: "int64" + MemorySwappiness: + description: | + Tune a container's memory swappiness behavior. Accepts an integer + between 0 and 100. + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + Init: + description: | + Run an init inside the container that forwards signals and reaps + processes. This field is omitted if empty, and the default (as + configured on the daemon) is used. + type: "boolean" + x-nullable: true + PidsLimit: + description: | + Tune a container's PIDs limit. Set `0` or `-1` for unlimited, or `null` + to not change. + type: "integer" + format: "int64" + Ulimits: + description: | + A list of resource limits to set in the container. For example: + + ``` + {"Name": "nofile", "Soft": 1024, "Hard": 2048} + ``` + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are + mutually exclusive. The order of precedence is `CPUCount` first, then + `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are + mutually exclusive. The order of precedence is `CPUCount` first, then + `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: | + Maximum IO in bytes per second for the container system drive + (Windows only). + type: "integer" + format: "int64" + + ResourceObject: + description: | + An object describing the resources which can be advertised by a node and + requested by a task. + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: | + User-defined resources can be either Integer resources (e.g, `SSD=3`) or + String resources (e.g, `GPU=UUID1`). + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: | + The time to wait between checks in nanoseconds. It should be 0 or at + least 1000000 (1 ms). 0 means inherit. + type: "integer" + Timeout: + description: | + The time to wait before considering the check to have hung. It should + be 0 or at least 1000000 (1 ms). 0 means inherit. + type: "integer" + Retries: + description: | + The number of consecutive failures needed to consider a container as + unhealthy. 0 means inherit. + type: "integer" + StartPeriod: + description: | + Start period for the container to initialize before starting + health-retries countdown in nanoseconds. It should be 0 or at least + 1000000 (1 ms). 0 means inherit. + type: "integer" + + Health: + description: | + Health stores information about the container's healthcheck results. + type: "object" + properties: + Status: + description: | + Status is one of `none`, `starting`, `healthy` or `unhealthy` + + - "none" Indicates there is no healthcheck + - "starting" Starting indicates that the container is not yet ready + - "healthy" Healthy indicates that the container is running correctly + - "unhealthy" Unhealthy indicates that the container has a problem + type: "string" + enum: + - "none" + - "starting" + - "healthy" + - "unhealthy" + example: "healthy" + FailingStreak: + description: "FailingStreak is the number of consecutive failures" + type: "integer" + example: 0 + Log: + type: "array" + description: | + Log contains the last few results (oldest first) + items: + x-nullable: true + $ref: "#/definitions/HealthcheckResult" + + HealthcheckResult: + description: | + HealthcheckResult stores information about a single run of a healthcheck probe + type: "object" + properties: + Start: + description: | + Date and time at which this check started in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "date-time" + example: "2020-01-04T10:44:24.496525531Z" + End: + description: | + Date and time at which this check ended in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2020-01-04T10:45:21.364524523Z" + ExitCode: + description: | + ExitCode meanings: + + - `0` healthy + - `1` unhealthy + - `2` reserved (considered unhealthy) + - other values: error running probe + type: "integer" + example: 0 + Output: + description: "Output from last check" + type: "string" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding + is a string in one of these forms: + + - `host-src:container-dest[:options]` to bind-mount a host path + into the container. Both `host-src`, and `container-dest` must + be an _absolute_ path. + - `volume-name:container-dest[:options]` to bind-mount a volume + managed by a volume driver into the container. `container-dest` + must be an _absolute_ path. + + `options` is an optional, comma-delimited list of: + + - `nocopy` disables automatic copying of data from the container + path to the volume. The `nocopy` flag only applies to named volumes. + - `[ro|rw]` mounts a volume read-only or read-write, respectively. + If omitted or set to `rw`, volumes are mounted read-write. + - `[z|Z]` applies SELinux labels to allow or deny multiple containers + to read and write to the same volume. + - `z`: a _shared_ content label is applied to the content. This + label indicates that multiple containers can share the volume + content, for both reading and writing. + - `Z`: a _private unshared_ label is applied to the content. + This label indicates that only the current container can use + a private volume. Labeling systems such as SELinux require + proper labels to be placed on volume content that is mounted + into a container. Without a label, the security system can + prevent a container's processes from using the content. By + default, the labels set by the host operating system are not + modified. + - `[[r]shared|[r]slave|[r]private]` specifies mount + [propagation behavior](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt). + This only applies to bind-mounted volumes, not internal volumes + or named volumes. Mount propagation requires the source mount + point (the location where the source directory is mounted in the + host operating system) to have the correct propagation properties. + For shared volumes, the source mount point must be set to `shared`. + For slave volumes, the mount must be set to either `shared` or + `slave`. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: | + Network mode to use for this container. Supported standard values + are: `bridge`, `host`, `none`, and `container:`. Any + other value is taken as a custom network's name to which this + container should connect to. + PortBindings: + $ref: "#/definitions/PortMap" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: | + Automatically remove the container when the container's process + exits. This has no effect if `RestartPolicy` is set. + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: | + A list of volumes to inherit from another container, specified in + the form `[:]`. + items: + type: "string" + Mounts: + description: | + Specification for mounts to be added to the container. + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: | + A list of kernel capabilities to add to the container. Conflicts + with option 'Capabilities'. + items: + type: "string" + CapDrop: + type: "array" + description: | + A list of kernel capabilities to drop from the container. Conflicts + with option 'Capabilities'. + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` + file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: | + A list of additional groups that the container process will run as. + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: | + A list of links for the container in the form `container_name:alias`. + items: + type: "string" + OomScoreAdj: + type: "integer" + description: | + An integer value containing the score given to the container in + order to tune OOM killer preferences. + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be + either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: | + Allocates an ephemeral host port for all of a container's + exposed ports. + + Ports are de-allocated when the container stops and allocated when + the container starts. The allocated port might be changed when + restarting the container. + + The port is selected from the ephemeral port range that depends on + the kernel. For example, on Linux the range is defined by + `/proc/sys/net/ipv4/ip_local_port_range`. + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs + mounts, and their corresponding mount options. For example: + + ``` + { "/run": "rw,noexec,nosuid,size=65536k" } + ``` + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: | + Sets the usernamespace mode for the container when usernamespace + remapping option is enabled. + ShmSize: + type: "integer" + description: | + Size of `/dev/shm` in bytes. If omitted, the system uses 64MB. + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. + For example: + + ``` + {"net.ipv4.ip_forward": "1"} + ``` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: | + Initial console size, as an `[height, width]` array. (Windows only) + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: | + Isolation technology of the container. (Windows only) + enum: + - "default" + - "process" + - "hyperv" + MaskedPaths: + type: "array" + description: | + The list of paths to be masked inside the container (this overrides + the default set of paths). + items: + type: "string" + ReadonlyPaths: + type: "array" + description: | + The list of paths to be set as read-only inside the container + (this overrides the default set of paths). + items: + type: "string" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: | + Attach standard streams to a TTY, including `stdin` if it is not closed. + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the + form `["VAR=value", ...]`. A variable without `=` is removed from the + environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: | + Command to run specified as a string or an array of strings. + type: "array" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: | + The name of the image to use when creating the container/ + type: "string" + Volumes: + description: | + An object mapping mount point paths inside the container to empty + objects. + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the + entry point is reset to system default (i.e., the entry point used by + docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: "array" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: | + `ONBUILD` metadata that were defined in the image's `Dockerfile`. + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: | + Signal to stop a container as a string or unsigned integer. + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: | + Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell. + type: "array" + items: + type: "string" + + NetworkingConfig: + description: | + NetworkingConfig represents the container's networking configuration for + each of its interfaces. + It is used for the networking configs specified in the `docker create` + and `docker network connect` commands. + type: "object" + properties: + EndpointsConfig: + description: | + A mapping of network name to endpoint configuration for that network. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + # putting an example here, instead of using the example values from + # /definitions/EndpointSettings, because containers/create currently + # does not support attaching to multiple networks, so the example request + # would be confusing if it showed that multiple networks can be contained + # in the EndpointsConfig. + # TODO remove once we support multiple networks on container create (see https://github.com/moby/moby/blob/07e6b843594e061f82baa5fa23c2ff7d536c2a05/daemon/create.go#L323) + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network's bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for multiple protocols, separate entries + are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + x-nullable: true + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: | + The level at which the volume exists. Either `global` for cluster-wide, + or `local` for machine level. + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: | + The driver specific options used when creating the volume. + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: | + List of IPAM configuration options, specified as a map: + + ``` + {"Subnet": , "IPRange": , "Gateway": , "AuxAddress": } + ``` + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + aux: + $ref: "#/definitions/ImageID" + + BuildCache: + type: "object" + properties: + ID: + type: "string" + Parent: + type: "string" + Type: + type: "string" + Description: + type: "string" + InUse: + type: "boolean" + Shared: + type: "boolean" + Size: + description: | + Amount of disk space used by the build cache (in bytes). + type: "integer" + CreatedAt: + description: | + Date and time at which the build cache was created in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + LastUsedAt: + description: | + Date and time at which the build cache was last used in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + x-nullable: true + example: "2017-08-09T07:09:37.632105588Z" + UsageCount: + type: "integer" + + ImageID: + type: "object" + description: "Image ID or Digest" + properties: + ID: + type: "string" + example: + ID: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + + CreateImageInfo: + type: "object" + properties: + id: + type: "string" + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + current: + type: "integer" + total: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: + True if the plugin is running. False if the plugin is not running, + only installed. + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + ProtocolScheme: + type: "string" + example: "some.protocol/v1.0" + description: "Protocol to use for clients connecting to the plugin." + enum: + - "" + - "moby.plugins.http/v1" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed + to avoid conflicting writes. The client must send the version number along + with the modified specification when updating these objects. + + This approach ensures safe concurrency and determinism in that the change + on the object may not be applied if the version number has changed from the + last read. In other words, if two update requests specify the same base + version, only one of the requests can succeed. As a result, two separate + update requests that happen at the same time will not unintentionally + overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: | + Information about the issuer of leaf TLS certificates and the trusted root + CA certificate. + type: "object" + properties: + TrustRoot: + description: | + The root CA certificate(s) that are used to validate leaf TLS + certificates. + type: "string" + CertIssuerSubject: + description: + The base64-url-safe-encoded raw subject bytes of the issuer. + type: "string" + CertIssuerPublicKey: + description: | + The base64-url-safe-encoded raw public key bytes of the issuer. + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: | + The number of historic tasks to keep per instance or node. If + negative, never remove completed or failed tasks. + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: | + The number of snapshots to keep beyond the current snapshot. + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: | + The number of log entries to keep around to sync up slow followers + after a snapshot is created. + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from + the leader before becoming a candidate and starting an election. + `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate + directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, + the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate + directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: | + The delay for an agent to send a heartbeat to the dispatcher. + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: | + Configuration for forwarding signing requests to an external + certificate authority. + type: "array" + items: + type: "object" + properties: + Protocol: + description: | + Protocol for communication with the external CA (currently + only `cfssl` is supported). + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: | + URL where certificate signing requests should be sent. + type: "string" + Options: + description: | + An object with key/value pairs that are interpreted as + protocol-specific options for the external CA driver. + type: "object" + additionalProperties: + type: "string" + CACert: + description: | + The root CA certificate (in PEM format) this external CA uses + to issue TLS certificates (assumed to be to the current swarm + root CA certificate if not provided). + type: "string" + SigningCACert: + description: | + The desired signing CA certificate for all swarm node TLS leaf + certificates, in PEM format. + type: "string" + SigningCAKey: + description: | + The desired signing CA key for all swarm node TLS leaf certificates, + in PEM format. + type: "string" + ForceRotate: + description: | + An integer whose purpose is to force swarm to generate a new + signing CA certificate and key, if none have been specified in + `SigningCACert` and `SigningCAKey` + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: | + If set, generate a key and use it to lock data stored on the + managers. + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: | + Whether there is currently a root CA rotation in progress for the swarm + type: "boolean" + example: false + DefaultAddrPool: + description: | + Default Address Pool specifies default subnet pools for global scope + networks. + type: "array" + items: + type: "string" + format: "CIDR" + example: ["10.10.0.0/16", "20.20.0.0/16"] + SubnetSize: + description: | + SubnetSize specifies the subnet size of the networks created from the + default subnet pool. + type: "integer" + format: "uint32" + maximum: 29 + default: 24 + example: 24 + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: | + Plugin spec for the service. *(Experimental release only.)* + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: | + Container spec for the service. + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: | + The hostname to use for the container, as a valid + [RFC 1123](https://tools.ietf.org/html/rfc1123) hostname. + type: "string" + Env: + description: | + A list of environment variables in the form `VAR=value`. + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: | + A list of additional groups that the container process will run as. + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + File: + type: "string" + example: "spec.json" + description: | + Load credential spec from this file. The file is read by + the daemon, and must be present in the `CredentialSpecs` + subdirectory in the docker data directory, which defaults + to `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads + `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` + > are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows + registry. The specified registry value must be located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File` and `CredentialSpec.Registry` + > are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: | + Specification for mounts to be added to containers created as part + of the service. + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: | + Amount of time to wait for the container to terminate before + forcefully killing it. + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: | + Specification for DNS related configurations in resolver configuration + file (`resolv.conf`). + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: | + A list of internal resolver variables to be modified (e.g., + `debug`, `ndots:3`, etc.). + type: "array" + items: + type: "string" + Secrets: + description: | + Secrets contains references to zero or more secrets that will be + exposed to the service. + type: "array" + items: + type: "object" + properties: + File: + description: | + File represents a specific target that is backed by a file. + type: "object" + properties: + Name: + description: | + Name represents the final filename in the filesystem. + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: | + SecretID represents the ID of the specific secret that we're + referencing. + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, + but this is just provided for lookup/display purposes. The + secret in the reference will be identified by its ID. + type: "string" + Configs: + description: | + Configs contains references to zero or more configs that will be + exposed to the service. + type: "array" + items: + type: "object" + properties: + File: + description: | + File represents a specific target that is backed by a file. + type: "object" + properties: + Name: + description: | + Name represents the final filename in the filesystem. + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + ConfigID: + description: | + ConfigID represents the ID of the specific config that we're + referencing. + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, + but this is just provided for lookup/display purposes. The + config in the reference will be identified by its ID. + type: "string" + Isolation: + type: "string" + description: | + Isolation technology of the containers running the service. + (Windows only) + enum: + - "default" + - "process" + - "hyperv" + Init: + description: | + Run an init inside the container that forwards signals and reaps + processes. This field is omitted if empty, and the default (as + configured on the daemon) is used. + type: "boolean" + x-nullable: true + NetworkAttachmentSpec: + description: | + Read-only spec type for non-swarm containers attached to swarm overlay + networks. + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + type: "object" + properties: + ContainerID: + description: "ID of the container represented by this task" + type: "string" + Resources: + description: | + Resource requirements which apply to each individual container created + as part of the service. + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/ResourceObject" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: | + Specification for the restart policy which applies to containers + created as part of this service. + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: | + Maximum attempts to restart a given container before giving up + (default value is 0, which is ignored). + type: "integer" + format: "int64" + default: 0 + Window: + description: | + Windows is the time window used to evaluate the restart policy + (default value is 0, which is unbounded). + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: | + An array of constraint expressions to limit the set of nodes where + a task can be scheduled. Constraint expressions can either use a + _match_ (`==`) or _exclude_ (`!=`) rule. Multiple constraints find + nodes that satisfy every expression (AND match). Constraints can + match node or Docker Engine labels as follows: + + node attribute | matches | example + ---------------------|--------------------------------|----------------------------------------------- + `node.id` | Node ID | `node.id==2ivku8v2gvtg4` + `node.hostname` | Node hostname | `node.hostname!=node-2` + `node.role` | Node role (`manager`/`worker`) | `node.role==manager` + `node.labels` | User-defined node labels | `node.labels.security==high` + `engine.labels` | Docker Engine's labels | `engine.labels.operatingsystem==ubuntu-14.04` + + `engine.labels` apply to Docker Engine labels like operating system, + drivers, etc. Swarm administrators add `node.labels` for operational + purposes by using the [`node update endpoint`](#operation/NodeUpdate). + + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + Preferences: + description: | + Preferences provide a way to make the scheduler aware of factors + such as topology. They are provided in order from highest to + lowest precedence. + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: | + label descriptor, such as `engine.labels.az`. + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: | + A counter that triggers an update even if no relevant parameters have + been changed. + type: "integer" + Runtime: + description: | + Runtime is the type of runtime specified for the task executor. + type: "string" + Networks: + description: "Specifies which networks the service should attach to." + type: "array" + items: + $ref: "#/definitions/NetworkAttachmentConfig" + LogDriver: + description: | + Specifies the log driver to use for tasks created from this spec. If + not present, the default one for the swarm will be used, finally + falling back to the engine default if not specified. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + - "remove" + - "orphaned" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: | + Maximum number of tasks to be updated in one iteration (0 means + unlimited parallelism). + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: | + Action to take if an updated task fails to run, or stops running + during the update. + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: | + Amount of time to monitor each updated task for failures, in + nanoseconds. + type: "integer" + format: "int64" + MaxFailureRatio: + description: | + The fraction of tasks that may fail during an update before the + failure action is invoked, specified as a floating point number + between 0 and 1. + type: "number" + default: 0 + Order: + description: | + The order of operations when rolling out an updated task. Either + the old task is shut down before the new task is started, or the + new task is started before the old task is shut down. + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: | + Maximum number of tasks to be rolled back in one iteration (0 means + unlimited parallelism). + type: "integer" + format: "int64" + Delay: + description: | + Amount of time between rollback iterations, in nanoseconds. + type: "integer" + format: "int64" + FailureAction: + description: | + Action to take if an rolled back task fails to run, or stops + running during the rollback. + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: | + Amount of time to monitor each rolled back task for failures, in + nanoseconds. + type: "integer" + format: "int64" + MaxFailureRatio: + description: | + The fraction of tasks that may fail during a rollback before the + failure action is invoked, specified as a floating point number + between 0 and 1. + type: "number" + default: 0 + Order: + description: | + The order of operations when rolling back a task. Either the old + task is shut down before the new task is started, or the new task + is started before the old task is shut down. + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Specifies which networks the service should attach to." + type: "array" + items: + $ref: "#/definitions/NetworkAttachmentConfig" + + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + - "sctp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + PublishMode: + description: | + The mode in which port is published. + +


+ + - "ingress" makes the target port accessible on every node, + regardless of whether there is a task for the service running on + that node or not. + - "host" bypasses the routing mesh and publish the port directly on + the swarm node where that service is running. + + type: "string" + enum: + - "ingress" + - "host" + default: "ingress" + example: "ingress" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: | + The mode of resolution to use for internal load balancing between tasks. + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: | + List of exposed ports that this service is accessible on from the + outside. Ports can only be provided if `vip` resolution mode is used. + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: | + Name of the secrets driver used to fetch the secret's value from an + external secret store. + $ref: "#/definitions/Driver" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) + config data. + type: "string" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + ContainerState: + description: | + ContainerState stores container's running state. It's part of ContainerJSONBase + and will be returned by the "inspect" command. + type: "object" + properties: + Status: + description: | + String representation of the container state. Can be one of "created", + "running", "paused", "restarting", "removing", "exited", or "dead". + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + example: "running" + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the freezer cgroup is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + example: true + Paused: + description: "Whether this container is paused." + type: "boolean" + example: false + Restarting: + description: "Whether this container is restarting." + type: "boolean" + example: false + OOMKilled: + description: | + Whether this container has been killed because it ran out of memory. + type: "boolean" + example: false + Dead: + type: "boolean" + example: false + Pid: + description: "The process ID of this container" + type: "integer" + example: 1234 + ExitCode: + description: "The last exit code of this container" + type: "integer" + example: 0 + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + example: "2020-01-06T09:06:59.461876391Z" + FinishedAt: + description: "The time when this container last exited." + type: "string" + example: "2020-01-06T09:07:59.461876391Z" + Health: + x-nullable: true + $ref: "#/definitions/Health" + + SystemVersion: + type: "object" + description: | + Response of Engine API: GET "/version" + properties: + Platform: + type: "object" + required: [Name] + properties: + Name: + type: "string" + Components: + type: "array" + description: | + Information about system components + items: + type: "object" + x-go-name: ComponentVersion + required: [Name, Version] + properties: + Name: + description: | + Name of the component + type: "string" + example: "Engine" + Version: + description: | + Version of the component + type: "string" + x-nullable: false + example: "19.03.12" + Details: + description: | + Key/value pairs of strings with additional information about the + component. These values are intended for informational purposes + only, and their content is not defined, and not part of the API + specification. + + These messages can be printed by the client as information to the user. + type: "object" + x-nullable: true + Version: + description: "The version of the daemon" + type: "string" + example: "19.03.12" + ApiVersion: + description: | + The default (and highest) API version that is supported by the daemon + type: "string" + example: "1.40" + MinAPIVersion: + description: | + The minimum API version that is supported by the daemon + type: "string" + example: "1.12" + GitCommit: + description: | + The Git commit of the source code that was used to build the daemon + type: "string" + example: "48a66213fe" + GoVersion: + description: | + The version Go used to compile the daemon, and the version of the Go + runtime in use. + type: "string" + example: "go1.13.14" + Os: + description: | + The operating system that the daemon is running on ("linux" or "windows") + type: "string" + example: "linux" + Arch: + description: | + The architecture that the daemon is running on + type: "string" + example: "amd64" + KernelVersion: + description: | + The kernel version (`uname -r`) that the daemon is running on. + + This field is omitted when empty. + type: "string" + example: "4.19.76-linuxkit" + Experimental: + description: | + Indicates if the daemon is started with experimental features enabled. + + This field is omitted when empty / false. + type: "boolean" + example: true + BuildTime: + description: | + The date and time that the daemon was compiled. + type: "string" + example: "2020-06-22T15:49:27.000000000+00:00" + + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + SystemStatus: + description: | + Status information about this node (standalone Swarm API). + +


+ + > **Note**: The information returned in this field is only propagated + > by the Swarm standalone API, and is empty (`null`) when using + > built-in swarm mode. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Role", "primary"] + - ["State", "Healthy"] + - ["Strategy", "spread"] + - ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"] + - ["Nodes", "2"] + - [" swarm-agent-00", "192.168.99.102:2376"] + - [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:03:46Z"] + - [" └ ServerVersion", "17.06.0-ce"] + - [" swarm-manager", "192.168.99.101:2376"] + - [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:04:11Z"] + - [" └ ServerVersion", "17.06.0-ce"] + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: "Indicates if the host has kernel memory limit support enabled." + type: "boolean" + example: true + CpuCfsPeriod: + description: | + Indicates if CPU CFS(Completely Fair Scheduler) period is supported by + the host. + type: "boolean" + example: true + CpuCfsQuota: + description: | + Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by + the host. + type: "boolean" + example: true + CPUShares: + description: | + Indicates if CPU Shares limiting is supported by the host. + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: | + Indicates if the daemon is running in debug-mode / with debug-level + logging enabled. + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd"] + default: "cgroupfs" + example: "cgroupfs" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL + are masked in the API response. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://xxxxx:xxxxx@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL + are masked in the API response. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://xxxxx:xxxxx@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "runc" + example: + runc: + path: "runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, and user-namespaces (userns). + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + ProductLicense: + description: | + Reports a summary of the product license on the daemon. + + If a commercial license has been applied to the daemon, information + such as number of nodes, and expiration are included. + type: "string" + example: "Community Engine" + Warnings: + description: | + List of warnings / informational messages about missing features, or + issues related to the daemon configuration. + + These messages can be printed by the client as information to the user. + type: "array" + items: + type: "string" + example: + - "WARNING: No memory limit support" + - "WARNING: bridge-nf-call-iptables is disabled" + - "WARNING: bridge-nf-call-ip6tables is disabled" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + + NetworkAttachmentConfig: + description: | + Specifies how a service should be attached to a particular network. + type: "object" + properties: + Target: + description: | + The target network for attachment. Must be a network name or ID. + type: "string" + Aliases: + description: | + Discoverable alternate names for the service on this network. + type: "array" + items: + type: "string" + DriverOpts: + description: | + Driver attachment options for the network target. + type: "object" + additionalProperties: + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see the + [inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container + than inspecting a single container. For example, the list of linked + containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: | + Return all containers. By default, only running containers are shown. + type: "boolean" + default: false + - name: "limit" + in: "query" + description: | + Return this number of most recently created containers, including + non-running ones. + type: "integer" + - name: "size" + in: "query" + description: | + Return the size of container as fields `SizeRw` and `SizeRootFs`. + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a + `map[string][]string`). For example, `{"status": ["paused"]}` will + only return paused containers. + + Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: | + Assign the specified name to the container. Must match + `/?[a-zA-Z0-9][a-zA-Z0-9_.-]+`. + type: "string" + pattern: "^/?[a-zA-Z0-9][a-zA-Z0-9_.-]+$" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + $ref: "#/definitions/NetworkingConfig" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: -1 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + title: "ContainerCreateResponse" + description: "OK response to ContainerCreate operation" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerInspectResponse" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + x-nullable: true + $ref: "#/definitions/ContainerState" + Image: + description: "The container's image ID" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + description: "IDs of exec instances that are running in the container." + type: "array" + items: + type: "string" + x-nullable: true + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: | + The size of files that have been created or changed by this + container. + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Healthcheck: + Test: ["CMD-SHELL", "exit 0"] + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + ExecIDs: + - "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca" + - "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + Health: + Status: "healthy" + FailingStreak: 0 + Log: + - Start: "2019-12-22T10:59:05.6385933Z" + End: "2019-12-22T10:59:05.8078452Z" + ExitCode: 0 + Output: "" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: | + On Unix systems, this is done by running the `ps` command. This endpoint + is not supported on Windows. + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerTopResponse" + description: "OK response to ContainerTop operation" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: | + Each process running in the container, where each is process + is an array of values corresponding to the titles. + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or + `journald` logging driver. + operationId: "ContainerLogs" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "until" + in: "query" + description: "Only return logs before this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + title: "ContainerChangeResponseItem" + description: "change item in response to ContainerChanges operation" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of the *previous* read, and is + used to calculate the CPU usage percentage. It is not an exact copy + of the `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + + To calculate the values shown by the `stats` command of the docker cli tool + the following formulas can be used: + * used_memory = `memory_stats.usage - memory_stats.stats.cache` + * available_memory = `memory_stats.limit` + * Memory usage % = `(used_memory / available_memory) * 100.0` + * cpu_delta = `cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage` + * system_cpu_delta = `cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage` + * number_cpus = `lenght(cpu_stats.cpu_usage.percpu_usage)` or `cpu_stats.online_cpus` + * CPU usage % = `(cpu_delta / system_cpu_delta) * number_cpus * 100.0` + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: | + Stream the output. If false, the stats will be output once and then + it will disconnect. + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container. Format is a + single character `[a-Z]` or `ctrl-` where `` is one + of: `a-z`, `@`, `^`, `[`, `,` or `_`. + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: | + Send a POSIX signal to a container, defaulting to killing to the + container. + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is not running" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "Container d37cde0fe4ad63c3a7252023b2f9800282894247d145cb5933ddf6e52cc03a28 is not running" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: | + Change various configuration options of a container without having to + recreate it. + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + title: "ContainerUpdateResponse" + description: "OK response to ContainerUpdate operation" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the freezer cgroup to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, + which is observable by the process being suspended. With the freezer + cgroup the process is unaware, and unable to capture, that it is being + suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach + to the same container multiple times and you can reattach to containers + that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint + to do anything. + + See the [documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) + for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, + and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used + for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client + can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will + similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), + the stream over the hijacked connected is multiplexed to separate out + `stdout` and `stderr`. The stream consists of a series of frames, each + containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or + `stderr`). It also contains the size of the associated frame encoded in + the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size + encoded as big endian. + + Following the header is the payload, which is the specified number of + bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), + the stream is not multiplexed. The data exchanged over the hijacked + connection is simply the raw data from the process PTY and client's + `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container.Format is a single + character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + `@`, `^`, `[`, `,` or `_`. + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you + want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been + returned, it will seamlessly transition into streaming current + output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: | + Stream attached streams from the time the request was made onwards. + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container.Format is a single + character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + `@`, `^`, `[`, `,`, or `_`. + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + title: "ContainerWaitResponse" + description: "OK response to ContainerWait operation" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + Error: + description: "container waiting error, if any" + type: "object" + properties: + Message: + description: "Details of an error" + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: | + Wait until a container state reaches the given condition, either + 'not-running' (default), 'next-exit', or 'removed'. + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: | + You cannot remove a running container: c2ada9df5af8. Stop the + container before attempting removal or force remove + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: | + A response header `X-Docker-Container-Path-Stat` is returned, containing + a base64 - encoded JSON object with some filesystem header information + about the path. + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: | + A base64 - encoded JSON object with some filesystem header + information about the path + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: | + The error message. Either "must specify path parameter" + (path cannot be empty) or "not a directory" (path was + asserted to be a directory but exists as a file). + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: | + The error message. Either "must specify path parameter" + (path cannot be empty) or "not a directory" (path was + asserted to be a directory but exists as a file). + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: | + If `1`, `true`, or `True` then it will be an error if unpacking the + given content would cause an existing directory to be replaced with + a non-directory and vice versa. + type: "string" + - name: "copyUIDGID" + in: "query" + description: | + If `1`, `true`, then it will copy UID/GID maps to the dest file or + dir + type: "string" + - name: "inputStream" + in: "body" + required: true + description: | + The input stream must be a tar archive compressed with one of the + following algorithms: `identity` (no compression), `gzip`, `bzip2`, + or `xz`. + schema: + type: "string" + format: "binary" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ContainerPruneResponse" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the images list. + + Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: > + JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker + uses the buildargs as the environment context for commands run via the `Dockerfile` RUN + instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for + passing secret values. + + + For example, the build arg `FOO=bar` would become `{"FOO":"bar"}` in JSON. This would result in the + query parameter `buildargs={"FOO":"bar"}`. Note that `{"FOO":"bar"}` should be URI component encoded. + + + [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg) + type: "string" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: | + Sets the networking mode for the run commands during build. Supported + standard values are: `bridge`, `host`, `none`, and `container:`. + Any other value is taken as a custom network's name or ID to which this + container should connect to. + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + - name: "target" + in: "query" + description: "Target build stage" + type: "string" + default: "" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + parameters: + - name: "keep-storage" + in: "query" + description: "Amount of disk space in bytes to keep for cache" + type: "integer" + format: "int64" + - name: "all" + in: "query" + type: "boolean" + description: "Remove all types of build cache" + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the list of build cache objects. + + Available filters: + + - `until=`: duration relative to daemon's time, during which build cache was not used, in Go's duration format (e.g., '24h') + - `id=` + - `parent=` + - `type=` + - `description=` + - `inuse` + - `shared` + - `private` + responses: + 200: + description: "No error" + schema: + type: "object" + title: "BuildPruneResponse" + properties: + CachesDeleted: + type: "array" + items: + description: "ID of build cache object" + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "message" + in: "query" + description: "Set commit message for imported image." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + title: "HistoryResponseItem" + description: "individual image layer information in response to ImageHistory operation" + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must + already have a tag which references the registry. For example, + `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + title: "ImageSearchResponseItem" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ImagePruneResponse" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: | + Validate credentials for a registry and, if available, get an identity + token for accessing the registry without password. + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + title: "SystemAuthResponse" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/SystemVersion" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Builder-Version: + type: "string" + description: "Default version of docker image builder" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemEventsResponse" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `config=` config name or ID + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `node=` node ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `secret=` secret name or ID + - `service=` service name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + - `volume=` volume name + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemDataUsageResponse" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + BuildCache: + type: "array" + items: + $ref: "#/definitions/BuildCache" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image + repositories. + + For each value of the `names` parameter: if it is a specific name and + tag (e.g. `ubuntu:latest`), then only that image (and its parents) are + returned; if it is an image ID, similarly only that image (and its parents) + are returned and there would be no names referenced in the 'repositories' + file for this image ID. + + For details on the format, see the [export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see the [export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: | + Override the key sequence for detaching a container. Format is + a single character `[a-Z]` or `ctrl-` where `` + is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: | + A list of environment variables in the form `["VAR=value", ...]`. + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: | + The user, and optionally, group to run the exec process inside + the container. Format is one of: `user`, `user:group`, `uid`, + or `uid:gid`. + WorkingDir: + type: "string" + description: | + The working directory for the exec process inside the container. + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: | + Starts a previously set up exec instance. If detach is true, this endpoint + returns immediately after starting the command. Otherwise, it sets up an + interactive session with the command. + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: | + Resize the TTY session used by an exec instance. This endpoint only works + if `tty` was specified as part of creating and starting the exec instance. + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ExecInspectResponse" + properties: + CanRemove: + type: "boolean" + DetachKeys: + type: "string" + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + title: "VolumeListResponse" + description: "Volume list response" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: | + Warnings that occurred when fetching the list of volumes. + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + description: "Volume configuration" + title: "VolumeConfig" + properties: + Name: + description: | + The new volume's name. If not specified, Docker generates a name. + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: | + A mapping of driver options and values. These options are + passed directly to the driver and are driver specific. + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "VolumePruneResponse" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see the + [network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than + inspecting a single network. For example, the list of containers attached + to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process + on the networks list. + + Available filters: + + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + title: "NetworkCreateResponse" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: | + Check for networks with duplicate names. Since Network is + primarily keyed based on a random ID and not on the name, and + network name is strictly a user-friendly alias to the network + which is uniquely identified using ID, there is no guaranteed + way to check for duplicates. CheckDuplicate is there to provide + a best effort checking of any networks which has the same name + but it is not guaranteed to catch all name collisions. + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: | + Globally scoped network is manually attachable by regular + containers from workers in swarm mode. + type: "boolean" + Ingress: + description: | + Ingress network is the network which provides the routing-mesh + in swarm mode. + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: | + The ID or name of the container to disconnect from the network. + Force: + type: "boolean" + description: | + Force the container to disconnect from the network. + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "NetworkPruneResponse" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the plugin list. + + Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: | + Describes a permission the user has to accept upon installing + the plugin. + type: "object" + title: "PluginPrivilegeItem" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be + enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration to use when pulling a plugin + from a registry. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "force" + in: "query" + description: | + Disable the plugin before removing. This may result in issues if the + plugin is in use by a container. + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration to use when pulling a plugin + from a registry. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: | + The version number of the node object being updated. This is required + to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: | + Listen address used for inter-manager communication, as well + as determining the networking interface used for the VXLAN + Tunnel Endpoint (VTEP). This can either be an address/port + combination in the form `192.168.1.1:4567`, or an interface + followed by a port number, like `eth0:4567`. If the port number + is omitted, the default swarm listening port is used. + type: "string" + AdvertiseAddr: + description: | + Externally reachable address advertised to other nodes. This + can either be an address/port combination in the form + `192.168.1.1:4567`, or an interface followed by a port number, + like `eth0:4567`. If the port number is omitted, the port + number from the listen address is used. If `AdvertiseAddr` is + not specified, it will be automatically detected when possible. + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: + ``), for example, `192.168.1.1`, or an interface, + like `eth0`. If `DataPathAddr` is unspecified, the same address + as `AdvertiseAddr` is used. + + The `DataPathAddr` specifies the address that global scope + network drivers will publish towards other nodes in order to + reach the containers running on this node. Using this parameter + it is possible to separate the container data traffic from the + management traffic of the cluster. + type: "string" + DefaultAddrPool: + description: | + Default Address Pool specifies default subnet pools for global + scope networks. + type: "array" + items: + type: "string" + example: ["10.10.0.0/16", "20.20.0.0/16"] + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + SubnetSize: + description: | + SubnetSize specifies the subnet size of the networks created + from the default subnet pool. + type: "integer" + format: "uint32" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + DefaultAddrPool: ["10.10.0.0/8", "20.20.0.0/8"] + SubnetSize: 24 + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: | + Listen address used for inter-manager communication if the node + gets promoted to manager, as well as determining the networking + interface used for the VXLAN Tunnel Endpoint (VTEP). + type: "string" + AdvertiseAddr: + description: | + Externally reachable address advertised to other nodes. This + can either be an address/port combination in the form + `192.168.1.1:4567`, or an interface followed by a port number, + like `eth0:4567`. If the port number is omitted, the port + number from the listen address is used. If `AdvertiseAddr` is + not specified, it will be automatically detected when possible. + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: + ``), for example, `192.168.1.1`, or an interface, + like `eth0`. If `DataPathAddr` is unspecified, the same address + as `AdvertiseAddr` is used. + + The `DataPathAddr` specifies the address that global scope + network drivers will publish towards other nodes in order to + reach the containers running on this node. Using this parameter + it is possible to separate the container data traffic from the + management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: | + Addresses of manager nodes already participating in the swarm. + type: "array" + items: + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: | + Force leave swarm, even if this is the last manager or that it will + break the cluster. + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: | + The version number of the swarm object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "UnlockKeyResponse" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the services list. + + Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + title: "ServiceCreateResponse" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration for pulling from private + registries. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: | + The version number of the service object being updated. This is + required to avoid conflicting writes. + This version number should be the value as currently set on the + service *before* the update. You can find the current version by + calling `GET /services/{id}` + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + description: | + If the `X-Registry-Auth` header is not specified, this parameter + indicates where to find registry authorization credentials. + type: "string" + enum: ["spec", "previous-spec"] + default: "spec" + - name: "rollback" + in: "query" + description: | + Set to this parameter to `previous` to cause a server-side rollback + to the previous service spec. The supplied spec will be ignored in + this case. + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration for pulling from private + registries. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. See also + [`/containers/{id}/logs`](#operation/ContainerLogs). + + **Note**: This endpoint works only for services with the `json-file` or + `journald` logging drivers. + operationId: "ServiceLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the tasks list. + + Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + See also [`/containers/{id}/logs`](#operation/ContainerLogs). + + **Note**: This endpoint works only for services with the `json-file` or + `journald` logging drivers. + operationId: "TaskLogs" + produces: + - "application/vnd.docker.raw-stream" + - "application/json" + responses: + 101: + description: "logs returned as a stream" + schema: + type: "string" + format: "binary" + 200: + description: "logs returned as a string in response body" + schema: + type: "string" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: | + Return the logs as a stream. + + This will return a `101` HTTP response with a `Connection: upgrade` header, then hijack the HTTP connection to send raw output. For more information about hijacking and the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Task"] + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the secrets list. + + Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: | + The spec of the secret to update. Currently, only the Labels field + can be updated. All other fields must remain unchanged from the + [SecretInspect endpoint](#operation/SecretInspect) response values. + - name: "version" + in: "query" + description: | + The version number of the secret object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the configs list. + + Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: | + The spec of the config to update. Currently, only the Labels field + can be updated. All other fields must remain unchanged from the + [ConfigInspect endpoint](#operation/ConfigInspect) response values. + - name: "version" + in: "query" + description: | + The version number of the config object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: | + Return image digest and platform information by contacting the registry. + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + title: "DistributionInspectResponse" + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: | + A descriptor struct containing digest, media type, and size. + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: | + An array containing all platforms supported by the image. + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to + call back to the client for advanced capabilities. + + > **Note**: This endpoint is *experimental* and only available if the daemon is started with experimental + > features enabled. The specifications for this endpoint may still change in a future version of the API. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows + the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon responds with a `101 UPGRADED` response follow with + the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session (experimental)"] diff --git a/docs/api/v1.40.yaml b/docs/api/v1.40.yaml new file mode 100644 index 0000000000000..c09f76387767c --- /dev/null +++ b/docs/api/v1.40.yaml @@ -0,0 +1,11250 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.40" +info: + title: "Docker Engine API" + version: "1.40" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the + Docker client uses to communicate with the Engine, so everything the Docker + client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` + is `GET /containers/json`). The notable exception is running containers, + which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure + of the API call. The body of the response will be JSON in the following + format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release, so API calls are versioned to + ensure that clients don't break. To lock to a specific version of the API, + you prefix the URL with its version, for example, call `/v1.30/info` to use + the v1.30 version of the `/info` endpoint. If the API version specified in + the URL is not supported by the daemon, a HTTP `400 Bad Request` error message + is returned. + + If you omit the version-prefix, the current version of the API (v1.40) is used. + For example, calling `/info` is the same as calling `/v1.40/info`. Using the + API without a version-prefix is deprecated and will be removed in a future release. + + Engine releases in the near future should support this version of the API, + so your client will continue to work even if it is talking to a newer Engine. + + The API uses an open schema model, which means server may add extra properties + to responses. Likewise, the server will ignore any extra query parameters and + request body properties. When you write clients, you need to ignore additional + properties in responses to ensure they do not break when talking to newer + daemons. + + + # Authentication + + Authentication for registries is handled client side. The client has to send + authentication details to various endpoints that need to communicate with + registries, such as `POST /images/(name)/push`. These are sent as + `X-Registry-Auth` header as a [base64url encoded](https://tools.ietf.org/html/rfc4648#section-5) + (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this + structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), + you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. + See the [networking documentation](https://docs.docker.com/network/) + for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. Refer to the + [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) + for more information. + + To exec a command in a container, you first need to create an exec instance, + then start it. These two API endpoints are wrapped up in a single command-line + command, `docker exec`. + + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. Refer to the + [swarm mode documentation](https://docs.docker.com/engine/swarm/) + for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode + must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must + be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit + of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must + be enabled for these endpoints to work. + - name: "Config" + x-displayName: "Configs" + description: | + Configs are application configurations that can be used by services. Swarm + mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + description: "Host IP address that the container's port is mapped to" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp", "sctp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + DeviceRequest: + type: "object" + description: "A request for devices to be sent to device drivers" + properties: + Driver: + type: "string" + example: "nvidia" + Count: + type: "integer" + example: -1 + DeviceIDs: + type: "array" + items: + type: "string" + example: + - "0" + - "1" + - "GPU-fef8089b-4820-abfc-e83e-94318197576e" + Capabilities: + description: | + A list of capabilities; an OR list of AND lists of capabilities. + type: "array" + items: + type: "array" + items: + type: "string" + example: + # gpu AND nvidia AND compute + - ["gpu", "nvidia", "compute"] + Options: + description: | + Driver-specific options, specified as a key/value pairs. These options + are passed directly to the driver. + type: "object" + additionalProperties: + type: "string" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + - `npipe` Mounts a named pipe from the host into the container. Must exist prior to creating the container. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + - "npipe" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + type: "string" + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + NonRecursive: + description: "Disable recursive bind mount." + type: "boolean" + default: false + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to + restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is + added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: | + If `on-failure` is used, the number of times to retry before giving up. + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: | + An integer value representing this container's relative CPU weight + versus other containers. + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + format: "int64" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: | + Path to `cgroups` under which the container's `cgroup` is created. If + the path is not absolute, the path is considered to be relative to the + `cgroups` path of the init process. Cgroups are created if they do not + already exist. + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form: + + ``` + [{"Path": "device_path", "Weight": weight}] + ``` + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: | + Microseconds of CPU time that the container can get in a CPU period. + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: | + The length of a CPU real-time period in microseconds. Set to 0 to + allocate no time allocated to real-time tasks. + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: | + The length of a CPU real-time runtime in microseconds. Set to 0 to + allocate no time allocated to real-time tasks. + type: "integer" + format: "int64" + CpusetCpus: + description: | + CPUs in which to allow execution (e.g., `0-3`, `0,1`). + type: "string" + example: "0-3" + CpusetMems: + description: | + Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only + effective on NUMA systems. + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DeviceRequests: + description: | + A list of requests for devices to be sent to device drivers. + type: "array" + items: + $ref: "#/definitions/DeviceRequest" + KernelMemory: + description: "Kernel memory limit in bytes." + type: "integer" + format: "int64" + example: 209715200 + KernelMemoryTCP: + description: "Hard limit for kernel TCP buffer memory (in bytes)." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: | + Total memory limit (memory + swap). Set as `-1` to enable unlimited + swap. + type: "integer" + format: "int64" + MemorySwappiness: + description: | + Tune a container's memory swappiness behavior. Accepts an integer + between 0 and 100. + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + Init: + description: | + Run an init inside the container that forwards signals and reaps + processes. This field is omitted if empty, and the default (as + configured on the daemon) is used. + type: "boolean" + x-nullable: true + PidsLimit: + description: | + Tune a container's PIDs limit. Set `0` or `-1` for unlimited, or `null` + to not change. + type: "integer" + format: "int64" + x-nullable: true + Ulimits: + description: | + A list of resource limits to set in the container. For example: + + ``` + {"Name": "nofile", "Soft": 1024, "Hard": 2048} + ``` + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are + mutually exclusive. The order of precedence is `CPUCount` first, then + `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are + mutually exclusive. The order of precedence is `CPUCount` first, then + `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: | + Maximum IO in bytes per second for the container system drive + (Windows only). + type: "integer" + format: "int64" + + ResourceObject: + description: | + An object describing the resources which can be advertised by a node and + requested by a task. + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: | + User-defined resources can be either Integer resources (e.g, `SSD=3`) or + String resources (e.g, `GPU=UUID1`). + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: | + The time to wait between checks in nanoseconds. It should be 0 or at + least 1000000 (1 ms). 0 means inherit. + type: "integer" + Timeout: + description: | + The time to wait before considering the check to have hung. It should + be 0 or at least 1000000 (1 ms). 0 means inherit. + type: "integer" + Retries: + description: | + The number of consecutive failures needed to consider a container as + unhealthy. 0 means inherit. + type: "integer" + StartPeriod: + description: | + Start period for the container to initialize before starting + health-retries countdown in nanoseconds. It should be 0 or at least + 1000000 (1 ms). 0 means inherit. + type: "integer" + + Health: + description: | + Health stores information about the container's healthcheck results. + type: "object" + properties: + Status: + description: | + Status is one of `none`, `starting`, `healthy` or `unhealthy` + + - "none" Indicates there is no healthcheck + - "starting" Starting indicates that the container is not yet ready + - "healthy" Healthy indicates that the container is running correctly + - "unhealthy" Unhealthy indicates that the container has a problem + type: "string" + enum: + - "none" + - "starting" + - "healthy" + - "unhealthy" + example: "healthy" + FailingStreak: + description: "FailingStreak is the number of consecutive failures" + type: "integer" + example: 0 + Log: + type: "array" + description: | + Log contains the last few results (oldest first) + items: + x-nullable: true + $ref: "#/definitions/HealthcheckResult" + + HealthcheckResult: + description: | + HealthcheckResult stores information about a single run of a healthcheck probe + type: "object" + properties: + Start: + description: | + Date and time at which this check started in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "date-time" + example: "2020-01-04T10:44:24.496525531Z" + End: + description: | + Date and time at which this check ended in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2020-01-04T10:45:21.364524523Z" + ExitCode: + description: | + ExitCode meanings: + + - `0` healthy + - `1` unhealthy + - `2` reserved (considered unhealthy) + - other values: error running probe + type: "integer" + example: 0 + Output: + description: "Output from last check" + type: "string" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding + is a string in one of these forms: + + - `host-src:container-dest[:options]` to bind-mount a host path + into the container. Both `host-src`, and `container-dest` must + be an _absolute_ path. + - `volume-name:container-dest[:options]` to bind-mount a volume + managed by a volume driver into the container. `container-dest` + must be an _absolute_ path. + + `options` is an optional, comma-delimited list of: + + - `nocopy` disables automatic copying of data from the container + path to the volume. The `nocopy` flag only applies to named volumes. + - `[ro|rw]` mounts a volume read-only or read-write, respectively. + If omitted or set to `rw`, volumes are mounted read-write. + - `[z|Z]` applies SELinux labels to allow or deny multiple containers + to read and write to the same volume. + - `z`: a _shared_ content label is applied to the content. This + label indicates that multiple containers can share the volume + content, for both reading and writing. + - `Z`: a _private unshared_ label is applied to the content. + This label indicates that only the current container can use + a private volume. Labeling systems such as SELinux require + proper labels to be placed on volume content that is mounted + into a container. Without a label, the security system can + prevent a container's processes from using the content. By + default, the labels set by the host operating system are not + modified. + - `[[r]shared|[r]slave|[r]private]` specifies mount + [propagation behavior](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt). + This only applies to bind-mounted volumes, not internal volumes + or named volumes. Mount propagation requires the source mount + point (the location where the source directory is mounted in the + host operating system) to have the correct propagation properties. + For shared volumes, the source mount point must be set to `shared`. + For slave volumes, the mount must be set to either `shared` or + `slave`. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: | + Network mode to use for this container. Supported standard values + are: `bridge`, `host`, `none`, and `container:`. Any + other value is taken as a custom network's name to which this + container should connect to. + PortBindings: + $ref: "#/definitions/PortMap" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: | + Automatically remove the container when the container's process + exits. This has no effect if `RestartPolicy` is set. + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: | + A list of volumes to inherit from another container, specified in + the form `[:]`. + items: + type: "string" + Mounts: + description: | + Specification for mounts to be added to the container. + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + Capabilities: + type: "array" + description: | + A list of kernel capabilities to be available for container (this + overrides the default set). + + Conflicts with options 'CapAdd' and 'CapDrop'" + items: + type: "string" + CapAdd: + type: "array" + description: | + A list of kernel capabilities to add to the container. Conflicts + with option 'Capabilities'. + items: + type: "string" + CapDrop: + type: "array" + description: | + A list of kernel capabilities to drop from the container. Conflicts + with option 'Capabilities'. + items: + type: "string" + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` + file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: | + A list of additional groups that the container process will run as. + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: | + A list of links for the container in the form `container_name:alias`. + items: + type: "string" + OomScoreAdj: + type: "integer" + description: | + An integer value containing the score given to the container in + order to tune OOM killer preferences. + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be + either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: | + Allocates an ephemeral host port for all of a container's + exposed ports. + + Ports are de-allocated when the container stops and allocated when + the container starts. The allocated port might be changed when + restarting the container. + + The port is selected from the ephemeral port range that depends on + the kernel. For example, on Linux the range is defined by + `/proc/sys/net/ipv4/ip_local_port_range`. + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs + mounts, and their corresponding mount options. For example: + + ``` + { "/run": "rw,noexec,nosuid,size=65536k" } + ``` + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: | + Sets the usernamespace mode for the container when usernamespace + remapping option is enabled. + ShmSize: + type: "integer" + description: | + Size of `/dev/shm` in bytes. If omitted, the system uses 64MB. + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. + For example: + + ``` + {"net.ipv4.ip_forward": "1"} + ``` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: | + Initial console size, as an `[height, width]` array. (Windows only) + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: | + Isolation technology of the container. (Windows only) + enum: + - "default" + - "process" + - "hyperv" + MaskedPaths: + type: "array" + description: | + The list of paths to be masked inside the container (this overrides + the default set of paths). + items: + type: "string" + ReadonlyPaths: + type: "array" + description: | + The list of paths to be set as read-only inside the container + (this overrides the default set of paths). + items: + type: "string" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: | + Attach standard streams to a TTY, including `stdin` if it is not closed. + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the + form `["VAR=value", ...]`. A variable without `=` is removed from the + environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: | + Command to run specified as a string or an array of strings. + type: "array" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: | + The name of the image to use when creating the container/ + type: "string" + Volumes: + description: | + An object mapping mount point paths inside the container to empty + objects. + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the + entry point is reset to system default (i.e., the entry point used by + docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: "array" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: | + `ONBUILD` metadata that were defined in the image's `Dockerfile`. + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: | + Signal to stop a container as a string or unsigned integer. + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: | + Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell. + type: "array" + items: + type: "string" + + NetworkingConfig: + description: | + NetworkingConfig represents the container's networking configuration for + each of its interfaces. + It is used for the networking configs specified in the `docker create` + and `docker network connect` commands. + type: "object" + properties: + EndpointsConfig: + description: | + A mapping of network name to endpoint configuration for that network. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + # putting an example here, instead of using the example values from + # /definitions/EndpointSettings, because containers/create currently + # does not support attaching to multiple networks, so the example request + # would be confusing if it showed that multiple networks can be contained + # in the EndpointsConfig. + # TODO remove once we support multiple networks on container create (see https://github.com/moby/moby/blob/07e6b843594e061f82baa5fa23c2ff7d536c2a05/daemon/create.go#L323) + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network's bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for multiple protocols, separate entries + are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + x-nullable: true + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: | + The level at which the volume exists. Either `global` for cluster-wide, + or `local` for machine level. + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: | + The driver specific options used when creating the volume. + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: | + List of IPAM configuration options, specified as a map: + + ``` + {"Subnet": , "IPRange": , "Gateway": , "AuxAddress": } + ``` + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + aux: + $ref: "#/definitions/ImageID" + + BuildCache: + type: "object" + properties: + ID: + type: "string" + Parent: + type: "string" + Type: + type: "string" + Description: + type: "string" + InUse: + type: "boolean" + Shared: + type: "boolean" + Size: + description: | + Amount of disk space used by the build cache (in bytes). + type: "integer" + CreatedAt: + description: | + Date and time at which the build cache was created in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + LastUsedAt: + description: | + Date and time at which the build cache was last used in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + x-nullable: true + example: "2017-08-09T07:09:37.632105588Z" + UsageCount: + type: "integer" + + ImageID: + type: "object" + description: "Image ID or Digest" + properties: + ID: + type: "string" + example: + ID: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + + CreateImageInfo: + type: "object" + properties: + id: + type: "string" + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + current: + type: "integer" + total: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: + True if the plugin is running. False if the plugin is not running, + only installed. + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + ProtocolScheme: + type: "string" + example: "some.protocol/v1.0" + description: "Protocol to use for clients connecting to the plugin." + enum: + - "" + - "moby.plugins.http/v1" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed + to avoid conflicting writes. The client must send the version number along + with the modified specification when updating these objects. + + This approach ensures safe concurrency and determinism in that the change + on the object may not be applied if the version number has changed from the + last read. In other words, if two update requests specify the same base + version, only one of the requests can succeed. As a result, two separate + update requests that happen at the same time will not unintentionally + overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: | + Information about the issuer of leaf TLS certificates and the trusted root + CA certificate. + type: "object" + properties: + TrustRoot: + description: | + The root CA certificate(s) that are used to validate leaf TLS + certificates. + type: "string" + CertIssuerSubject: + description: + The base64-url-safe-encoded raw subject bytes of the issuer. + type: "string" + CertIssuerPublicKey: + description: | + The base64-url-safe-encoded raw public key bytes of the issuer. + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: | + The number of historic tasks to keep per instance or node. If + negative, never remove completed or failed tasks. + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: | + The number of snapshots to keep beyond the current snapshot. + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: | + The number of log entries to keep around to sync up slow followers + after a snapshot is created. + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from + the leader before becoming a candidate and starting an election. + `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate + directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, + the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate + directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: | + The delay for an agent to send a heartbeat to the dispatcher. + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: | + Configuration for forwarding signing requests to an external + certificate authority. + type: "array" + items: + type: "object" + properties: + Protocol: + description: | + Protocol for communication with the external CA (currently + only `cfssl` is supported). + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: | + URL where certificate signing requests should be sent. + type: "string" + Options: + description: | + An object with key/value pairs that are interpreted as + protocol-specific options for the external CA driver. + type: "object" + additionalProperties: + type: "string" + CACert: + description: | + The root CA certificate (in PEM format) this external CA uses + to issue TLS certificates (assumed to be to the current swarm + root CA certificate if not provided). + type: "string" + SigningCACert: + description: | + The desired signing CA certificate for all swarm node TLS leaf + certificates, in PEM format. + type: "string" + SigningCAKey: + description: | + The desired signing CA key for all swarm node TLS leaf certificates, + in PEM format. + type: "string" + ForceRotate: + description: | + An integer whose purpose is to force swarm to generate a new + signing CA certificate and key, if none have been specified in + `SigningCACert` and `SigningCAKey` + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: | + If set, generate a key and use it to lock data stored on the + managers. + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: | + Whether there is currently a root CA rotation in progress for the swarm + type: "boolean" + example: false + DataPathPort: + description: | + DataPathPort specifies the data path port number for data traffic. + Acceptable port range is 1024 to 49151. + If no port is set or is set to 0, the default port (4789) is used. + type: "integer" + format: "uint32" + default: 4789 + example: 4789 + DefaultAddrPool: + description: | + Default Address Pool specifies default subnet pools for global scope + networks. + type: "array" + items: + type: "string" + format: "CIDR" + example: ["10.10.0.0/16", "20.20.0.0/16"] + SubnetSize: + description: | + SubnetSize specifies the subnet size of the networks created from the + default subnet pool. + type: "integer" + format: "uint32" + maximum: 29 + default: 24 + example: 24 + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: | + Plugin spec for the service. *(Experimental release only.)* + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: | + Container spec for the service. + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: | + The hostname to use for the container, as a valid + [RFC 1123](https://tools.ietf.org/html/rfc1123) hostname. + type: "string" + Env: + description: | + A list of environment variables in the form `VAR=value`. + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: | + A list of additional groups that the container process will run as. + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + Config: + type: "string" + example: "0bt9dmxjvjiqermk6xrop3ekq" + description: | + Load credential spec from a Swarm Config with the given ID. + The specified config must also be present in the Configs + field with the Runtime property set. + +


+ + + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. + File: + type: "string" + example: "spec.json" + description: | + Load credential spec from this file. The file is read by + the daemon, and must be present in the `CredentialSpecs` + subdirectory in the docker data directory, which defaults + to `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads + `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows + registry. The specified registry value must be located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: | + Specification for mounts to be added to containers created as part + of the service. + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: | + Amount of time to wait for the container to terminate before + forcefully killing it. + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: | + Specification for DNS related configurations in resolver configuration + file (`resolv.conf`). + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: | + A list of internal resolver variables to be modified (e.g., + `debug`, `ndots:3`, etc.). + type: "array" + items: + type: "string" + Secrets: + description: | + Secrets contains references to zero or more secrets that will be + exposed to the service. + type: "array" + items: + type: "object" + properties: + File: + description: | + File represents a specific target that is backed by a file. + type: "object" + properties: + Name: + description: | + Name represents the final filename in the filesystem. + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: | + SecretID represents the ID of the specific secret that we're + referencing. + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, + but this is just provided for lookup/display purposes. The + secret in the reference will be identified by its ID. + type: "string" + Configs: + description: | + Configs contains references to zero or more configs that will be + exposed to the service. + type: "array" + items: + type: "object" + properties: + File: + description: | + File represents a specific target that is backed by a file. + +


+ + > **Note**: `Configs.File` and `Configs.Runtime` are mutually exclusive + type: "object" + properties: + Name: + description: | + Name represents the final filename in the filesystem. + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + Runtime: + description: | + Runtime represents a target that is not mounted into the + container but is used by the task + +


+ + > **Note**: `Configs.File` and `Configs.Runtime` are mutually + > exclusive + type: "object" + ConfigID: + description: | + ConfigID represents the ID of the specific config that we're + referencing. + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, + but this is just provided for lookup/display purposes. The + config in the reference will be identified by its ID. + type: "string" + Isolation: + type: "string" + description: | + Isolation technology of the containers running the service. + (Windows only) + enum: + - "default" + - "process" + - "hyperv" + Init: + description: | + Run an init inside the container that forwards signals and reaps + processes. This field is omitted if empty, and the default (as + configured on the daemon) is used. + type: "boolean" + x-nullable: true + Sysctls: + description: | + Set kernel namedspaced parameters (sysctls) in the container. + The Sysctls option on services accepts the same sysctls as the + are supported on containers. Note that while the same sysctls are + supported, no guarantees or checks are made about their + suitability for a clustered environment, and it's up to the user + to determine whether a given sysctl will work properly in a + Service. + type: "object" + additionalProperties: + type: "string" + NetworkAttachmentSpec: + description: | + Read-only spec type for non-swarm containers attached to swarm overlay + networks. + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + type: "object" + properties: + ContainerID: + description: "ID of the container represented by this task" + type: "string" + Resources: + description: | + Resource requirements which apply to each individual container created + as part of the service. + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/ResourceObject" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: | + Specification for the restart policy which applies to containers + created as part of this service. + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: | + Maximum attempts to restart a given container before giving up + (default value is 0, which is ignored). + type: "integer" + format: "int64" + default: 0 + Window: + description: | + Windows is the time window used to evaluate the restart policy + (default value is 0, which is unbounded). + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: | + An array of constraint expressions to limit the set of nodes where + a task can be scheduled. Constraint expressions can either use a + _match_ (`==`) or _exclude_ (`!=`) rule. Multiple constraints find + nodes that satisfy every expression (AND match). Constraints can + match node or Docker Engine labels as follows: + + node attribute | matches | example + ---------------------|--------------------------------|----------------------------------------------- + `node.id` | Node ID | `node.id==2ivku8v2gvtg4` + `node.hostname` | Node hostname | `node.hostname!=node-2` + `node.role` | Node role (`manager`/`worker`) | `node.role==manager` + `node.platform.os` | Node operating system | `node.platform.os==windows` + `node.platform.arch` | Node architecture | `node.platform.arch==x86_64` + `node.labels` | User-defined node labels | `node.labels.security==high` + `engine.labels` | Docker Engine's labels | `engine.labels.operatingsystem==ubuntu-14.04` + + `engine.labels` apply to Docker Engine labels like operating system, + drivers, etc. Swarm administrators add `node.labels` for operational + purposes by using the [`node update endpoint`](#operation/NodeUpdate). + + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + - "node.platform.os==linux" + - "node.platform.arch==x86_64" + Preferences: + description: | + Preferences provide a way to make the scheduler aware of factors + such as topology. They are provided in order from highest to + lowest precedence. + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: | + label descriptor, such as `engine.labels.az`. + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + MaxReplicas: + description: | + Maximum number of replicas for per node (default value is 0, which + is unlimited) + type: "integer" + format: "int64" + default: 0 + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: | + A counter that triggers an update even if no relevant parameters have + been changed. + type: "integer" + Runtime: + description: | + Runtime is the type of runtime specified for the task executor. + type: "string" + Networks: + description: "Specifies which networks the service should attach to." + type: "array" + items: + $ref: "#/definitions/NetworkAttachmentConfig" + LogDriver: + description: | + Specifies the log driver to use for tasks created from this spec. If + not present, the default one for the swarm will be used, finally + falling back to the engine default if not specified. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + - "remove" + - "orphaned" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: | + Maximum number of tasks to be updated in one iteration (0 means + unlimited parallelism). + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: | + Action to take if an updated task fails to run, or stops running + during the update. + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: | + Amount of time to monitor each updated task for failures, in + nanoseconds. + type: "integer" + format: "int64" + MaxFailureRatio: + description: | + The fraction of tasks that may fail during an update before the + failure action is invoked, specified as a floating point number + between 0 and 1. + type: "number" + default: 0 + Order: + description: | + The order of operations when rolling out an updated task. Either + the old task is shut down before the new task is started, or the + new task is started before the old task is shut down. + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: | + Maximum number of tasks to be rolled back in one iteration (0 means + unlimited parallelism). + type: "integer" + format: "int64" + Delay: + description: | + Amount of time between rollback iterations, in nanoseconds. + type: "integer" + format: "int64" + FailureAction: + description: | + Action to take if an rolled back task fails to run, or stops + running during the rollback. + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: | + Amount of time to monitor each rolled back task for failures, in + nanoseconds. + type: "integer" + format: "int64" + MaxFailureRatio: + description: | + The fraction of tasks that may fail during a rollback before the + failure action is invoked, specified as a floating point number + between 0 and 1. + type: "number" + default: 0 + Order: + description: | + The order of operations when rolling back a task. Either the old + task is shut down before the new task is started, or the new task + is started before the old task is shut down. + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Specifies which networks the service should attach to." + type: "array" + items: + $ref: "#/definitions/NetworkAttachmentConfig" + + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + - "sctp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + PublishMode: + description: | + The mode in which port is published. + +


+ + - "ingress" makes the target port accessible on every node, + regardless of whether there is a task for the service running on + that node or not. + - "host" bypasses the routing mesh and publish the port directly on + the swarm node where that service is running. + + type: "string" + enum: + - "ingress" + - "host" + default: "ingress" + example: "ingress" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: | + The mode of resolution to use for internal load balancing between tasks. + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: | + List of exposed ports that this service is accessible on from the + outside. Ports can only be provided if `vip` resolution mode is used. + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: | + Name of the secrets driver used to fetch the secret's value from an + external secret store. + $ref: "#/definitions/Driver" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) + config data. + type: "string" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + ContainerState: + description: | + ContainerState stores container's running state. It's part of ContainerJSONBase + and will be returned by the "inspect" command. + type: "object" + properties: + Status: + description: | + String representation of the container state. Can be one of "created", + "running", "paused", "restarting", "removing", "exited", or "dead". + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + example: "running" + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the freezer cgroup is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + example: true + Paused: + description: "Whether this container is paused." + type: "boolean" + example: false + Restarting: + description: "Whether this container is restarting." + type: "boolean" + example: false + OOMKilled: + description: | + Whether this container has been killed because it ran out of memory. + type: "boolean" + example: false + Dead: + type: "boolean" + example: false + Pid: + description: "The process ID of this container" + type: "integer" + example: 1234 + ExitCode: + description: "The last exit code of this container" + type: "integer" + example: 0 + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + example: "2020-01-06T09:06:59.461876391Z" + FinishedAt: + description: "The time when this container last exited." + type: "string" + example: "2020-01-06T09:07:59.461876391Z" + Health: + x-nullable: true + $ref: "#/definitions/Health" + + SystemVersion: + type: "object" + description: | + Response of Engine API: GET "/version" + properties: + Platform: + type: "object" + required: [Name] + properties: + Name: + type: "string" + Components: + type: "array" + description: | + Information about system components + items: + type: "object" + x-go-name: ComponentVersion + required: [Name, Version] + properties: + Name: + description: | + Name of the component + type: "string" + example: "Engine" + Version: + description: | + Version of the component + type: "string" + x-nullable: false + example: "19.03.12" + Details: + description: | + Key/value pairs of strings with additional information about the + component. These values are intended for informational purposes + only, and their content is not defined, and not part of the API + specification. + + These messages can be printed by the client as information to the user. + type: "object" + x-nullable: true + Version: + description: "The version of the daemon" + type: "string" + example: "19.03.12" + ApiVersion: + description: | + The default (and highest) API version that is supported by the daemon + type: "string" + example: "1.40" + MinAPIVersion: + description: | + The minimum API version that is supported by the daemon + type: "string" + example: "1.12" + GitCommit: + description: | + The Git commit of the source code that was used to build the daemon + type: "string" + example: "48a66213fe" + GoVersion: + description: | + The version Go used to compile the daemon, and the version of the Go + runtime in use. + type: "string" + example: "go1.13.14" + Os: + description: | + The operating system that the daemon is running on ("linux" or "windows") + type: "string" + example: "linux" + Arch: + description: | + The architecture that the daemon is running on + type: "string" + example: "amd64" + KernelVersion: + description: | + The kernel version (`uname -r`) that the daemon is running on. + + This field is omitted when empty. + type: "string" + example: "4.19.76-linuxkit" + Experimental: + description: | + Indicates if the daemon is started with experimental features enabled. + + This field is omitted when empty / false. + type: "boolean" + example: true + BuildTime: + description: | + The date and time that the daemon was compiled. + type: "string" + example: "2020-06-22T15:49:27.000000000+00:00" + + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + SystemStatus: + description: | + Status information about this node (standalone Swarm API). + +


+ + > **Note**: The information returned in this field is only propagated + > by the Swarm standalone API, and is empty (`null`) when using + > built-in swarm mode. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Role", "primary"] + - ["State", "Healthy"] + - ["Strategy", "spread"] + - ["Filters", "health, port, containerslots, dependency, affinity, constraint, whitelist"] + - ["Nodes", "2"] + - [" swarm-agent-00", "192.168.99.102:2376"] + - [" └ ID", "5CT6:FBGO:RVGO:CZL4:PB2K:WCYN:2JSV:KSHH:GGFW:QOPG:6J5Q:IOZ2|192.168.99.102:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "1 (1 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:03:46Z"] + - [" └ ServerVersion", "17.06.0-ce"] + - [" swarm-manager", "192.168.99.101:2376"] + - [" └ ID", "TAMD:7LL3:SEF7:LW2W:4Q2X:WVFH:RTXX:JSYS:XY2P:JEHL:ZMJK:JGIW|192.168.99.101:2376"] + - [" └ Status", "Healthy"] + - [" └ Containers", "2 (2 Running, 0 Paused, 0 Stopped)"] + - [" └ Reserved CPUs", "0 / 1"] + - [" └ Reserved Memory", "0 B / 1.021 GiB"] + - [" └ Labels", "kernelversion=4.4.74-boot2docker, operatingsystem=Boot2Docker 17.06.0-ce (TCL 7.2); HEAD : 0672754 - Thu Jun 29 00:06:31 UTC 2017, ostype=linux, provider=virtualbox, storagedriver=aufs"] + - [" └ UpdatedAt", "2017-08-09T10:04:11Z"] + - [" └ ServerVersion", "17.06.0-ce"] + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: "Indicates if the host has kernel memory limit support enabled." + type: "boolean" + example: true + CpuCfsPeriod: + description: | + Indicates if CPU CFS(Completely Fair Scheduler) period is supported by + the host. + type: "boolean" + example: true + CpuCfsQuota: + description: | + Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by + the host. + type: "boolean" + example: true + CPUShares: + description: | + Indicates if CPU Shares limiting is supported by the host. + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + PidsLimit: + description: "Indicates if the host kernel has PID limit support enabled." + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: | + Indicates if the daemon is running in debug-mode / with debug-level + logging enabled. + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd", "none"] + default: "cgroupfs" + example: "cgroupfs" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL + are masked in the API response. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://xxxxx:xxxxx@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL + are masked in the API response. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://xxxxx:xxxxx@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Note**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "runc" + example: + runc: + path: "runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, user-namespaces (userns), and rootless. + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + - "name=rootless" + ProductLicense: + description: | + Reports a summary of the product license on the daemon. + + If a commercial license has been applied to the daemon, information + such as number of nodes, and expiration are included. + type: "string" + example: "Community Engine" + Warnings: + description: | + List of warnings / informational messages about missing features, or + issues related to the daemon configuration. + + These messages can be printed by the client as information to the user. + type: "array" + items: + type: "string" + example: + - "WARNING: No memory limit support" + - "WARNING: bridge-nf-call-iptables is disabled" + - "WARNING: bridge-nf-call-ip6tables is disabled" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + + NetworkAttachmentConfig: + description: | + Specifies how a service should be attached to a particular network. + type: "object" + properties: + Target: + description: | + The target network for attachment. Must be a network name or ID. + type: "string" + Aliases: + description: | + Discoverable alternate names for the service on this network. + type: "array" + items: + type: "string" + DriverOpts: + description: | + Driver attachment options for the network target. + type: "object" + additionalProperties: + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see the + [inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container + than inspecting a single container. For example, the list of linked + containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: | + Return all containers. By default, only running containers are shown. + type: "boolean" + default: false + - name: "limit" + in: "query" + description: | + Return this number of most recently created containers, including + non-running ones. + type: "integer" + - name: "size" + in: "query" + description: | + Return the size of container as fields `SizeRw` and `SizeRootFs`. + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a + `map[string][]string`). For example, `{"status": ["paused"]}` will + only return paused containers. + + Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: | + Assign the specified name to the container. Must match + `/?[a-zA-Z0-9][a-zA-Z0-9_.-]+`. + type: "string" + pattern: "^/?[a-zA-Z0-9][a-zA-Z0-9_.-]+$" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + $ref: "#/definitions/NetworkingConfig" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + DeviceRequests: + - Driver: "nvidia" + Count: -1 + DeviceIDs": ["0", "1", "GPU-fef8089b-4820-abfc-e83e-94318197576e"] + Capabilities: [["gpu", "nvidia", "compute"]] + Options: + property1: "string" + property2: "string" + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: 0 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + title: "ContainerCreateResponse" + description: "OK response to ContainerCreate operation" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerInspectResponse" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + x-nullable: true + $ref: "#/definitions/ContainerState" + Image: + description: "The container's image ID" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Node: + description: "TODO" + type: "object" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + Platform: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + description: "IDs of exec instances that are running in the container." + type: "array" + items: + type: "string" + x-nullable: true + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: | + The size of files that have been created or changed by this + container. + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Healthcheck: + Test: ["CMD-SHELL", "exit 0"] + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + ExecIDs: + - "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca" + - "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + DeviceRequests: + - Driver: "nvidia" + Count: -1 + DeviceIDs": ["0", "1", "GPU-fef8089b-4820-abfc-e83e-94318197576e"] + Capabilities: [["gpu", "nvidia", "compute"]] + Options: + property1: "string" + property2: "string" + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + Health: + Status: "healthy" + FailingStreak: 0 + Log: + - Start: "2019-12-22T10:59:05.6385933Z" + End: "2019-12-22T10:59:05.8078452Z" + ExitCode: 0 + Output: "" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: | + On Unix systems, this is done by running the `ps` command. This endpoint + is not supported on Windows. + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerTopResponse" + description: "OK response to ContainerTop operation" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: | + Each process running in the container, where each is process + is an array of values corresponding to the titles. + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or + `journald` logging driver. + operationId: "ContainerLogs" + responses: + 200: + description: | + logs returned as a stream in response body. + For the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + Note that unlike the attach endpoint, the logs endpoint does not + upgrade the connection and does not set Content-Type. + schema: + type: "string" + format: "binary" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: "Keep connection after returning logs." + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "until" + in: "query" + description: "Only return logs before this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + title: "ContainerChangeResponseItem" + description: "change item in response to ContainerChanges operation" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of the *previous* read, and is + used to calculate the CPU usage percentage. It is not an exact copy + of the `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + + To calculate the values shown by the `stats` command of the docker cli tool + the following formulas can be used: + * used_memory = `memory_stats.usage - memory_stats.stats.cache` + * available_memory = `memory_stats.limit` + * Memory usage % = `(used_memory / available_memory) * 100.0` + * cpu_delta = `cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage` + * system_cpu_delta = `cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage` + * number_cpus = `lenght(cpu_stats.cpu_usage.percpu_usage)` or `cpu_stats.online_cpus` + * CPU usage % = `(cpu_delta / system_cpu_delta) * number_cpus * 100.0` + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: | + Stream the output. If false, the stats will be output once and then + it will disconnect. + type: "boolean" + default: true + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container. Format is a + single character `[a-Z]` or `ctrl-` where `` is one + of: `a-z`, `@`, `^`, `[`, `,` or `_`. + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: | + Send a POSIX signal to a container, defaulting to killing to the + container. + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is not running" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "Container d37cde0fe4ad63c3a7252023b2f9800282894247d145cb5933ddf6e52cc03a28 is not running" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: | + Change various configuration options of a container without having to + recreate it. + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + title: "ContainerUpdateResponse" + description: "OK response to ContainerUpdate operation" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the freezer cgroup to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, + which is observable by the process being suspended. With the freezer + cgroup the process is unaware, and unable to capture, that it is being + suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach + to the same container multiple times and you can reattach to containers + that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint + to do anything. + + See the [documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) + for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, + and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used + for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client + can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will + similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), + the stream over the hijacked connected is multiplexed to separate out + `stdout` and `stderr`. The stream consists of a series of frames, each + containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or + `stderr`). It also contains the size of the associated frame encoded in + the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size + encoded as big endian. + + Following the header is the payload, which is the specified number of + bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), + the stream is not multiplexed. The data exchanged over the hijacked + connection is simply the raw data from the process PTY and client's + `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container.Format is a single + character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + `@`, `^`, `[`, `,` or `_`. + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you + want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been + returned, it will seamlessly transition into streaming current + output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: | + Stream attached streams from the time the request was made onwards. + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container.Format is a single + character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + `@`, `^`, `[`, `,`, or `_`. + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + title: "ContainerWaitResponse" + description: "OK response to ContainerWait operation" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + Error: + description: "container waiting error, if any" + type: "object" + properties: + Message: + description: "Details of an error" + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: | + Wait until a container state reaches the given condition, either + 'not-running' (default), 'next-exit', or 'removed'. + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: | + You cannot remove a running container: c2ada9df5af8. Stop the + container before attempting removal or force remove + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: | + A response header `X-Docker-Container-Path-Stat` is returned, containing + a base64 - encoded JSON object with some filesystem header information + about the path. + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: | + A base64 - encoded JSON object with some filesystem header + information about the path + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: | + The error message. Either "must specify path parameter" + (path cannot be empty) or "not a directory" (path was + asserted to be a directory but exists as a file). + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: | + The error message. Either "must specify path parameter" + (path cannot be empty) or "not a directory" (path was + asserted to be a directory but exists as a file). + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: | + If `1`, `true`, or `True` then it will be an error if unpacking the + given content would cause an existing directory to be replaced with + a non-directory and vice versa. + type: "string" + - name: "copyUIDGID" + in: "query" + description: | + If `1`, `true`, then it will copy UID/GID maps to the dest file or + dir + type: "string" + - name: "inputStream" + in: "body" + required: true + description: | + The input stream must be a tar archive compressed with one of the + following algorithms: `identity` (no compression), `gzip`, `bzip2`, + or `xz`. + schema: + type: "string" + format: "binary" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ContainerPruneResponse" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the images list. + + Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: > + JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker + uses the buildargs as the environment context for commands run via the `Dockerfile` RUN + instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for + passing secret values. + + + For example, the build arg `FOO=bar` would become `{"FOO":"bar"}` in JSON. This would result in the + query parameter `buildargs={"FOO":"bar"}`. Note that `{"FOO":"bar"}` should be URI component encoded. + + + [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg) + type: "string" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: | + Sets the networking mode for the run commands during build. Supported + standard values are: `bridge`, `host`, `none`, and `container:`. + Any other value is taken as a custom network's name or ID to which this + container should connect to. + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + - name: "target" + in: "query" + description: "Target build stage" + type: "string" + default: "" + - name: "outputs" + in: "query" + description: "BuildKit output configuration" + type: "string" + default: "" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + parameters: + - name: "keep-storage" + in: "query" + description: "Amount of disk space in bytes to keep for cache" + type: "integer" + format: "int64" + - name: "all" + in: "query" + type: "boolean" + description: "Remove all types of build cache" + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the list of build cache objects. + + Available filters: + + - `until=`: duration relative to daemon's time, during which build cache was not used, in Go's duration format (e.g., '24h') + - `id=` + - `parent=` + - `type=` + - `description=` + - `inuse` + - `shared` + - `private` + responses: + 200: + description: "No error" + schema: + type: "object" + title: "BuildPruneResponse" + properties: + CachesDeleted: + type: "array" + items: + description: "ID of build cache object" + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "message" + in: "query" + description: "Set commit message for imported image." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + title: "HistoryResponseItem" + description: "individual image layer information in response to ImageHistory operation" + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must + already have a tag which references the registry. For example, + `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + title: "ImageSearchResponseItem" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ImagePruneResponse" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: | + Validate credentials for a registry and, if available, get an identity + token for accessing the registry without password. + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + title: "SystemAuthResponse" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/SystemVersion" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Builder-Version: + type: "string" + description: "Default version of docker image builder" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + headers: + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + tags: ["System"] + head: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPingHead" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "(empty)" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Builder-Version: + type: "string" + description: "Default version of docker image builder" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, and `update` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, and `untag` + + Volumes report these events: `create`, `mount`, `unmount`, and `destroy` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, and `remove` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemEventsResponse" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `config=` config name or ID + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `node=` node ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `secret=` secret name or ID + - `service=` service name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + - `volume=` volume name + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemDataUsageResponse" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + BuildCache: + type: "array" + items: + $ref: "#/definitions/BuildCache" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image + repositories. + + For each value of the `names` parameter: if it is a specific name and + tag (e.g. `ubuntu:latest`), then only that image (and its parents) are + returned; if it is an image ID, similarly only that image (and its parents) + are returned and there would be no names referenced in the 'repositories' + file for this image ID. + + For details on the format, see the [export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see the [export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: | + Override the key sequence for detaching a container. Format is + a single character `[a-Z]` or `ctrl-` where `` + is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: | + A list of environment variables in the form `["VAR=value", ...]`. + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: | + The user, and optionally, group to run the exec process inside + the container. Format is one of: `user`, `user:group`, `uid`, + or `uid:gid`. + WorkingDir: + type: "string" + description: | + The working directory for the exec process inside the container. + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: | + Starts a previously set up exec instance. If detach is true, this endpoint + returns immediately after starting the command. Otherwise, it sets up an + interactive session with the command. + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: | + Resize the TTY session used by an exec instance. This endpoint only works + if `tty` was specified as part of creating and starting the exec instance. + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ExecInspectResponse" + properties: + CanRemove: + type: "boolean" + DetachKeys: + type: "string" + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + title: "VolumeListResponse" + description: "Volume list response" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: | + Warnings that occurred when fetching the list of volumes. + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + description: "Volume configuration" + title: "VolumeConfig" + properties: + Name: + description: | + The new volume's name. If not specified, Docker generates a name. + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: | + A mapping of driver options and values. These options are + passed directly to the driver and are driver specific. + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "VolumePruneResponse" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see the + [network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than + inspecting a single network. For example, the list of containers attached + to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process + on the networks list. + + Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + networks that are not in use by a container. When set to `false` + (or `0`), only networks that are in use by one or more + containers are returned. + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + title: "NetworkCreateResponse" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: | + Check for networks with duplicate names. Since Network is + primarily keyed based on a random ID and not on the name, and + network name is strictly a user-friendly alias to the network + which is uniquely identified using ID, there is no guaranteed + way to check for duplicates. CheckDuplicate is there to provide + a best effort checking of any networks which has the same name + but it is not guaranteed to catch all name collisions. + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: | + Globally scoped network is manually attachable by regular + containers from workers in swarm mode. + type: "boolean" + Ingress: + description: | + Ingress network is the network which provides the routing-mesh + in swarm mode. + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: | + The ID or name of the container to disconnect from the network. + Force: + type: "boolean" + description: | + Force the container to disconnect from the network. + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "NetworkPruneResponse" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the plugin list. + + Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: | + Describes a permission the user has to accept upon installing + the plugin. + type: "object" + title: "PluginPrivilegeItem" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be + enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration to use when pulling a plugin + from a registry. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "force" + in: "query" + description: | + Disable the plugin before removing. This may result in issues if the + plugin is in use by a container. + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration to use when pulling a plugin + from a registry. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `node.label=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: | + The version number of the node object being updated. This is required + to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: | + Listen address used for inter-manager communication, as well + as determining the networking interface used for the VXLAN + Tunnel Endpoint (VTEP). This can either be an address/port + combination in the form `192.168.1.1:4567`, or an interface + followed by a port number, like `eth0:4567`. If the port number + is omitted, the default swarm listening port is used. + type: "string" + AdvertiseAddr: + description: | + Externally reachable address advertised to other nodes. This + can either be an address/port combination in the form + `192.168.1.1:4567`, or an interface followed by a port number, + like `eth0:4567`. If the port number is omitted, the port + number from the listen address is used. If `AdvertiseAddr` is + not specified, it will be automatically detected when possible. + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: + ``), for example, `192.168.1.1`, or an interface, + like `eth0`. If `DataPathAddr` is unspecified, the same address + as `AdvertiseAddr` is used. + + The `DataPathAddr` specifies the address that global scope + network drivers will publish towards other nodes in order to + reach the containers running on this node. Using this parameter + it is possible to separate the container data traffic from the + management traffic of the cluster. + type: "string" + DataPathPort: + description: | + DataPathPort specifies the data path port number for data traffic. + Acceptable port range is 1024 to 49151. + if no port is set or is set to 0, default port 4789 will be used. + type: "integer" + format: "uint32" + DefaultAddrPool: + description: | + Default Address Pool specifies default subnet pools for global + scope networks. + type: "array" + items: + type: "string" + example: ["10.10.0.0/16", "20.20.0.0/16"] + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + SubnetSize: + description: | + SubnetSize specifies the subnet size of the networks created + from the default subnet pool. + type: "integer" + format: "uint32" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + DataPathPort: 4789 + DefaultAddrPool: ["10.10.0.0/8", "20.20.0.0/8"] + SubnetSize: 24 + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: | + Listen address used for inter-manager communication if the node + gets promoted to manager, as well as determining the networking + interface used for the VXLAN Tunnel Endpoint (VTEP). + type: "string" + AdvertiseAddr: + description: | + Externally reachable address advertised to other nodes. This + can either be an address/port combination in the form + `192.168.1.1:4567`, or an interface followed by a port number, + like `eth0:4567`. If the port number is omitted, the port + number from the listen address is used. If `AdvertiseAddr` is + not specified, it will be automatically detected when possible. + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: + ``), for example, `192.168.1.1`, or an interface, + like `eth0`. If `DataPathAddr` is unspecified, the same address + as `AdvertiseAddr` is used. + + The `DataPathAddr` specifies the address that global scope + network drivers will publish towards other nodes in order to + reach the containers running on this node. Using this parameter + it is possible to separate the container data traffic from the + management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: | + Addresses of manager nodes already participating in the swarm. + type: "array" + items: + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: | + Force leave swarm, even if this is the last manager or that it will + break the cluster. + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: | + The version number of the swarm object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "UnlockKeyResponse" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the services list. + + Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + title: "ServiceCreateResponse" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration for pulling from private + registries. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: | + The version number of the service object being updated. This is + required to avoid conflicting writes. + This version number should be the value as currently set on the + service *before* the update. You can find the current version by + calling `GET /services/{id}` + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + description: | + If the `X-Registry-Auth` header is not specified, this parameter + indicates where to find registry authorization credentials. + type: "string" + enum: ["spec", "previous-spec"] + default: "spec" + - name: "rollback" + in: "query" + description: | + Set to this parameter to `previous` to cause a server-side rollback + to the previous service spec. The supplied spec will be ignored in + this case. + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration for pulling from private + registries. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. See also + [`/containers/{id}/logs`](#operation/ContainerLogs). + + **Note**: This endpoint works only for services with the `local`, + `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + responses: + 200: + description: "logs returned as a stream in response body" + schema: + type: "string" + format: "binary" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: "Keep connection after returning logs." + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the tasks list. + + Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + See also [`/containers/{id}/logs`](#operation/ContainerLogs). + + **Note**: This endpoint works only for services with the `local`, + `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + responses: + 200: + description: "logs returned as a stream in response body" + schema: + type: "string" + format: "binary" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: "Keep connection after returning logs." + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Task"] + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the secrets list. + + Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: | + The spec of the secret to update. Currently, only the Labels field + can be updated. All other fields must remain unchanged from the + [SecretInspect endpoint](#operation/SecretInspect) response values. + - name: "version" + in: "query" + description: | + The version number of the secret object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the configs list. + + Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: | + The spec of the config to update. Currently, only the Labels field + can be updated. All other fields must remain unchanged from the + [ConfigInspect endpoint](#operation/ConfigInspect) response values. + - name: "version" + in: "query" + description: | + The version number of the config object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: | + Return image digest and platform information by contacting the registry. + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + title: "DistributionInspectResponse" + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: | + A descriptor struct containing digest, media type, and size. + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: | + An array containing all platforms supported by the image. + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to + call back to the client for advanced capabilities. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows + the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon responds with a `101 UPGRADED` response follow with + the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session"] diff --git a/docs/api/v1.41.yaml b/docs/api/v1.41.yaml new file mode 100644 index 0000000000000..fba45b6b15a55 --- /dev/null +++ b/docs/api/v1.41.yaml @@ -0,0 +1,11425 @@ +# A Swagger 2.0 (a.k.a. OpenAPI) definition of the Engine API. +# +# This is used for generating API documentation and the types used by the +# client/server. See api/README.md for more information. +# +# Some style notes: +# - This file is used by ReDoc, which allows GitHub Flavored Markdown in +# descriptions. +# - There is no maximum line length, for ease of editing and pretty diffs. +# - operationIds are in the format "NounVerb", with a singular noun. + +swagger: "2.0" +schemes: + - "http" + - "https" +produces: + - "application/json" + - "text/plain" +consumes: + - "application/json" + - "text/plain" +basePath: "/v1.41" +info: + title: "Docker Engine API" + version: "1.41" + x-logo: + url: "https://docs.docker.com/images/logo-docker-main.png" + description: | + The Engine API is an HTTP API served by Docker Engine. It is the API the + Docker client uses to communicate with the Engine, so everything the Docker + client can do can be done with the API. + + Most of the client's commands map directly to API endpoints (e.g. `docker ps` + is `GET /containers/json`). The notable exception is running containers, + which consists of several API calls. + + # Errors + + The API uses standard HTTP status codes to indicate the success or failure + of the API call. The body of the response will be JSON in the following + format: + + ``` + { + "message": "page not found" + } + ``` + + # Versioning + + The API is usually changed in each release, so API calls are versioned to + ensure that clients don't break. To lock to a specific version of the API, + you prefix the URL with its version, for example, call `/v1.30/info` to use + the v1.30 version of the `/info` endpoint. If the API version specified in + the URL is not supported by the daemon, a HTTP `400 Bad Request` error message + is returned. + + If you omit the version-prefix, the current version of the API (v1.41) is used. + For example, calling `/info` is the same as calling `/v1.41/info`. Using the + API without a version-prefix is deprecated and will be removed in a future release. + + Engine releases in the near future should support this version of the API, + so your client will continue to work even if it is talking to a newer Engine. + + The API uses an open schema model, which means server may add extra properties + to responses. Likewise, the server will ignore any extra query parameters and + request body properties. When you write clients, you need to ignore additional + properties in responses to ensure they do not break when talking to newer + daemons. + + + # Authentication + + Authentication for registries is handled client side. The client has to send + authentication details to various endpoints that need to communicate with + registries, such as `POST /images/(name)/push`. These are sent as + `X-Registry-Auth` header as a [base64url encoded](https://tools.ietf.org/html/rfc4648#section-5) + (JSON) string with the following structure: + + ``` + { + "username": "string", + "password": "string", + "email": "string", + "serveraddress": "string" + } + ``` + + The `serveraddress` is a domain/IP without a protocol. Throughout this + structure, double quotes are required. + + If you have already got an identity token from the [`/auth` endpoint](#operation/SystemAuth), + you can just pass this instead of credentials: + + ``` + { + "identitytoken": "9cbaf023786cd7..." + } + ``` + +# The tags on paths define the menu sections in the ReDoc documentation, so +# the usage of tags must make sense for that: +# - They should be singular, not plural. +# - There should not be too many tags, or the menu becomes unwieldy. For +# example, it is preferable to add a path to the "System" tag instead of +# creating a tag with a single path in it. +# - The order of tags in this list defines the order in the menu. +tags: + # Primary objects + - name: "Container" + x-displayName: "Containers" + description: | + Create and manage containers. + - name: "Image" + x-displayName: "Images" + - name: "Network" + x-displayName: "Networks" + description: | + Networks are user-defined networks that containers can be attached to. + See the [networking documentation](https://docs.docker.com/network/) + for more information. + - name: "Volume" + x-displayName: "Volumes" + description: | + Create and manage persistent storage that can be attached to containers. + - name: "Exec" + x-displayName: "Exec" + description: | + Run new commands inside running containers. Refer to the + [command-line reference](https://docs.docker.com/engine/reference/commandline/exec/) + for more information. + + To exec a command in a container, you first need to create an exec instance, + then start it. These two API endpoints are wrapped up in a single command-line + command, `docker exec`. + + # Swarm things + - name: "Swarm" + x-displayName: "Swarm" + description: | + Engines can be clustered together in a swarm. Refer to the + [swarm mode documentation](https://docs.docker.com/engine/swarm/) + for more information. + - name: "Node" + x-displayName: "Nodes" + description: | + Nodes are instances of the Engine participating in a swarm. Swarm mode + must be enabled for these endpoints to work. + - name: "Service" + x-displayName: "Services" + description: | + Services are the definitions of tasks to run on a swarm. Swarm mode must + be enabled for these endpoints to work. + - name: "Task" + x-displayName: "Tasks" + description: | + A task is a container running on a swarm. It is the atomic scheduling unit + of swarm. Swarm mode must be enabled for these endpoints to work. + - name: "Secret" + x-displayName: "Secrets" + description: | + Secrets are sensitive data that can be used by services. Swarm mode must + be enabled for these endpoints to work. + - name: "Config" + x-displayName: "Configs" + description: | + Configs are application configurations that can be used by services. Swarm + mode must be enabled for these endpoints to work. + # System things + - name: "Plugin" + x-displayName: "Plugins" + - name: "System" + x-displayName: "System" + +definitions: + Port: + type: "object" + description: "An open port on a container" + required: [PrivatePort, Type] + properties: + IP: + type: "string" + format: "ip-address" + description: "Host IP address that the container's port is mapped to" + PrivatePort: + type: "integer" + format: "uint16" + x-nullable: false + description: "Port on the container" + PublicPort: + type: "integer" + format: "uint16" + description: "Port exposed on the host" + Type: + type: "string" + x-nullable: false + enum: ["tcp", "udp", "sctp"] + example: + PrivatePort: 8080 + PublicPort: 80 + Type: "tcp" + + MountPoint: + type: "object" + description: "A mount point inside a container" + properties: + Type: + type: "string" + Name: + type: "string" + Source: + type: "string" + Destination: + type: "string" + Driver: + type: "string" + Mode: + type: "string" + RW: + type: "boolean" + Propagation: + type: "string" + + DeviceMapping: + type: "object" + description: "A device mapping between the host and container" + properties: + PathOnHost: + type: "string" + PathInContainer: + type: "string" + CgroupPermissions: + type: "string" + example: + PathOnHost: "/dev/deviceName" + PathInContainer: "/dev/deviceName" + CgroupPermissions: "mrw" + + DeviceRequest: + type: "object" + description: "A request for devices to be sent to device drivers" + properties: + Driver: + type: "string" + example: "nvidia" + Count: + type: "integer" + example: -1 + DeviceIDs: + type: "array" + items: + type: "string" + example: + - "0" + - "1" + - "GPU-fef8089b-4820-abfc-e83e-94318197576e" + Capabilities: + description: | + A list of capabilities; an OR list of AND lists of capabilities. + type: "array" + items: + type: "array" + items: + type: "string" + example: + # gpu AND nvidia AND compute + - ["gpu", "nvidia", "compute"] + Options: + description: | + Driver-specific options, specified as a key/value pairs. These options + are passed directly to the driver. + type: "object" + additionalProperties: + type: "string" + + ThrottleDevice: + type: "object" + properties: + Path: + description: "Device path" + type: "string" + Rate: + description: "Rate" + type: "integer" + format: "int64" + minimum: 0 + + Mount: + type: "object" + properties: + Target: + description: "Container path." + type: "string" + Source: + description: "Mount source (e.g. a volume name, a host path)." + type: "string" + Type: + description: | + The mount type. Available types: + + - `bind` Mounts a file or directory from the host into the container. Must exist prior to creating the container. + - `volume` Creates a volume with the given name and options (or uses a pre-existing volume with the same name and options). These are **not** removed when the container is removed. + - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. + - `npipe` Mounts a named pipe from the host into the container. Must exist prior to creating the container. + type: "string" + enum: + - "bind" + - "volume" + - "tmpfs" + - "npipe" + ReadOnly: + description: "Whether the mount should be read-only." + type: "boolean" + Consistency: + description: "The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`." + type: "string" + BindOptions: + description: "Optional configuration for the `bind` type." + type: "object" + properties: + Propagation: + description: "A propagation mode with the value `[r]private`, `[r]shared`, or `[r]slave`." + type: "string" + enum: + - "private" + - "rprivate" + - "shared" + - "rshared" + - "slave" + - "rslave" + NonRecursive: + description: "Disable recursive bind mount." + type: "boolean" + default: false + VolumeOptions: + description: "Optional configuration for the `volume` type." + type: "object" + properties: + NoCopy: + description: "Populate volume with data from the target." + type: "boolean" + default: false + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + DriverConfig: + description: "Map of driver specific options" + type: "object" + properties: + Name: + description: "Name of the driver to use to create the volume." + type: "string" + Options: + description: "key/value map of driver specific options." + type: "object" + additionalProperties: + type: "string" + TmpfsOptions: + description: "Optional configuration for the `tmpfs` type." + type: "object" + properties: + SizeBytes: + description: "The size for the tmpfs mount in bytes." + type: "integer" + format: "int64" + Mode: + description: "The permission mode for the tmpfs mount in an integer." + type: "integer" + + RestartPolicy: + description: | + The behavior to apply when the container exits. The default is not to + restart. + + An ever increasing delay (double the previous delay, starting at 100ms) is + added before each restart to prevent flooding the server. + type: "object" + properties: + Name: + type: "string" + description: | + - Empty string means not to restart + - `always` Always restart + - `unless-stopped` Restart always except when the user has manually stopped the container + - `on-failure` Restart only when the container exit code is non-zero + enum: + - "" + - "always" + - "unless-stopped" + - "on-failure" + MaximumRetryCount: + type: "integer" + description: | + If `on-failure` is used, the number of times to retry before giving up. + + Resources: + description: "A container's resources (cgroups config, ulimits, etc)" + type: "object" + properties: + # Applicable to all platforms + CpuShares: + description: | + An integer value representing this container's relative CPU weight + versus other containers. + type: "integer" + Memory: + description: "Memory limit in bytes." + type: "integer" + format: "int64" + default: 0 + # Applicable to UNIX platforms + CgroupParent: + description: | + Path to `cgroups` under which the container's `cgroup` is created. If + the path is not absolute, the path is considered to be relative to the + `cgroups` path of the init process. Cgroups are created if they do not + already exist. + type: "string" + BlkioWeight: + description: "Block IO weight (relative weight)." + type: "integer" + minimum: 0 + maximum: 1000 + BlkioWeightDevice: + description: | + Block IO weight (relative device weight) in the form: + + ``` + [{"Path": "device_path", "Weight": weight}] + ``` + type: "array" + items: + type: "object" + properties: + Path: + type: "string" + Weight: + type: "integer" + minimum: 0 + BlkioDeviceReadBps: + description: | + Limit read rate (bytes per second) from a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteBps: + description: | + Limit write rate (bytes per second) to a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceReadIOps: + description: | + Limit read rate (IO per second) from a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + BlkioDeviceWriteIOps: + description: | + Limit write rate (IO per second) to a device, in the form: + + ``` + [{"Path": "device_path", "Rate": rate}] + ``` + type: "array" + items: + $ref: "#/definitions/ThrottleDevice" + CpuPeriod: + description: "The length of a CPU period in microseconds." + type: "integer" + format: "int64" + CpuQuota: + description: | + Microseconds of CPU time that the container can get in a CPU period. + type: "integer" + format: "int64" + CpuRealtimePeriod: + description: | + The length of a CPU real-time period in microseconds. Set to 0 to + allocate no time allocated to real-time tasks. + type: "integer" + format: "int64" + CpuRealtimeRuntime: + description: | + The length of a CPU real-time runtime in microseconds. Set to 0 to + allocate no time allocated to real-time tasks. + type: "integer" + format: "int64" + CpusetCpus: + description: | + CPUs in which to allow execution (e.g., `0-3`, `0,1`). + type: "string" + example: "0-3" + CpusetMems: + description: | + Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only + effective on NUMA systems. + type: "string" + Devices: + description: "A list of devices to add to the container." + type: "array" + items: + $ref: "#/definitions/DeviceMapping" + DeviceCgroupRules: + description: "a list of cgroup rules to apply to the container" + type: "array" + items: + type: "string" + example: "c 13:* rwm" + DeviceRequests: + description: | + A list of requests for devices to be sent to device drivers. + type: "array" + items: + $ref: "#/definitions/DeviceRequest" + KernelMemory: + description: | + Kernel memory limit in bytes. + +


+ + > **Deprecated**: This field is deprecated as the kernel 5.4 deprecated + > `kmem.limit_in_bytes`. + type: "integer" + format: "int64" + example: 209715200 + KernelMemoryTCP: + description: "Hard limit for kernel TCP buffer memory (in bytes)." + type: "integer" + format: "int64" + MemoryReservation: + description: "Memory soft limit in bytes." + type: "integer" + format: "int64" + MemorySwap: + description: | + Total memory limit (memory + swap). Set as `-1` to enable unlimited + swap. + type: "integer" + format: "int64" + MemorySwappiness: + description: | + Tune a container's memory swappiness behavior. Accepts an integer + between 0 and 100. + type: "integer" + format: "int64" + minimum: 0 + maximum: 100 + NanoCpus: + description: "CPU quota in units of 10-9 CPUs." + type: "integer" + format: "int64" + OomKillDisable: + description: "Disable OOM Killer for the container." + type: "boolean" + Init: + description: | + Run an init inside the container that forwards signals and reaps + processes. This field is omitted if empty, and the default (as + configured on the daemon) is used. + type: "boolean" + x-nullable: true + PidsLimit: + description: | + Tune a container's PIDs limit. Set `0` or `-1` for unlimited, or `null` + to not change. + type: "integer" + format: "int64" + x-nullable: true + Ulimits: + description: | + A list of resource limits to set in the container. For example: + + ``` + {"Name": "nofile", "Soft": 1024, "Hard": 2048} + ``` + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + # Applicable to Windows + CpuCount: + description: | + The number of usable CPUs (Windows only). + + On Windows Server containers, the processor resource controls are + mutually exclusive. The order of precedence is `CPUCount` first, then + `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + CpuPercent: + description: | + The usable percentage of the available CPUs (Windows only). + + On Windows Server containers, the processor resource controls are + mutually exclusive. The order of precedence is `CPUCount` first, then + `CPUShares`, and `CPUPercent` last. + type: "integer" + format: "int64" + IOMaximumIOps: + description: "Maximum IOps for the container system drive (Windows only)" + type: "integer" + format: "int64" + IOMaximumBandwidth: + description: | + Maximum IO in bytes per second for the container system drive + (Windows only). + type: "integer" + format: "int64" + + Limit: + description: | + An object describing a limit on resources which can be requested by a task. + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + Pids: + description: | + Limits the maximum number of PIDs in the container. Set `0` for unlimited. + type: "integer" + format: "int64" + default: 0 + example: 100 + + ResourceObject: + description: | + An object describing the resources which can be advertised by a node and + requested by a task. + type: "object" + properties: + NanoCPUs: + type: "integer" + format: "int64" + example: 4000000000 + MemoryBytes: + type: "integer" + format: "int64" + example: 8272408576 + GenericResources: + $ref: "#/definitions/GenericResources" + + GenericResources: + description: | + User-defined resources can be either Integer resources (e.g, `SSD=3`) or + String resources (e.g, `GPU=UUID1`). + type: "array" + items: + type: "object" + properties: + NamedResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "string" + DiscreteResourceSpec: + type: "object" + properties: + Kind: + type: "string" + Value: + type: "integer" + format: "int64" + example: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + HealthConfig: + description: "A test to perform to check that the container is healthy." + type: "object" + properties: + Test: + description: | + The test to perform. Possible values are: + + - `[]` inherit healthcheck from image or parent image + - `["NONE"]` disable healthcheck + - `["CMD", args...]` exec arguments directly + - `["CMD-SHELL", command]` run command with system's default shell + type: "array" + items: + type: "string" + Interval: + description: | + The time to wait between checks in nanoseconds. It should be 0 or at + least 1000000 (1 ms). 0 means inherit. + type: "integer" + Timeout: + description: | + The time to wait before considering the check to have hung. It should + be 0 or at least 1000000 (1 ms). 0 means inherit. + type: "integer" + Retries: + description: | + The number of consecutive failures needed to consider a container as + unhealthy. 0 means inherit. + type: "integer" + StartPeriod: + description: | + Start period for the container to initialize before starting + health-retries countdown in nanoseconds. It should be 0 or at least + 1000000 (1 ms). 0 means inherit. + type: "integer" + + Health: + description: | + Health stores information about the container's healthcheck results. + type: "object" + properties: + Status: + description: | + Status is one of `none`, `starting`, `healthy` or `unhealthy` + + - "none" Indicates there is no healthcheck + - "starting" Starting indicates that the container is not yet ready + - "healthy" Healthy indicates that the container is running correctly + - "unhealthy" Unhealthy indicates that the container has a problem + type: "string" + enum: + - "none" + - "starting" + - "healthy" + - "unhealthy" + example: "healthy" + FailingStreak: + description: "FailingStreak is the number of consecutive failures" + type: "integer" + example: 0 + Log: + type: "array" + description: | + Log contains the last few results (oldest first) + items: + x-nullable: true + $ref: "#/definitions/HealthcheckResult" + + HealthcheckResult: + description: | + HealthcheckResult stores information about a single run of a healthcheck probe + type: "object" + properties: + Start: + description: | + Date and time at which this check started in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "date-time" + example: "2020-01-04T10:44:24.496525531Z" + End: + description: | + Date and time at which this check ended in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2020-01-04T10:45:21.364524523Z" + ExitCode: + description: | + ExitCode meanings: + + - `0` healthy + - `1` unhealthy + - `2` reserved (considered unhealthy) + - other values: error running probe + type: "integer" + example: 0 + Output: + description: "Output from last check" + type: "string" + + HostConfig: + description: "Container configuration that depends on the host we are running on" + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + # Applicable to all platforms + Binds: + type: "array" + description: | + A list of volume bindings for this container. Each volume binding + is a string in one of these forms: + + - `host-src:container-dest[:options]` to bind-mount a host path + into the container. Both `host-src`, and `container-dest` must + be an _absolute_ path. + - `volume-name:container-dest[:options]` to bind-mount a volume + managed by a volume driver into the container. `container-dest` + must be an _absolute_ path. + + `options` is an optional, comma-delimited list of: + + - `nocopy` disables automatic copying of data from the container + path to the volume. The `nocopy` flag only applies to named volumes. + - `[ro|rw]` mounts a volume read-only or read-write, respectively. + If omitted or set to `rw`, volumes are mounted read-write. + - `[z|Z]` applies SELinux labels to allow or deny multiple containers + to read and write to the same volume. + - `z`: a _shared_ content label is applied to the content. This + label indicates that multiple containers can share the volume + content, for both reading and writing. + - `Z`: a _private unshared_ label is applied to the content. + This label indicates that only the current container can use + a private volume. Labeling systems such as SELinux require + proper labels to be placed on volume content that is mounted + into a container. Without a label, the security system can + prevent a container's processes from using the content. By + default, the labels set by the host operating system are not + modified. + - `[[r]shared|[r]slave|[r]private]` specifies mount + [propagation behavior](https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt). + This only applies to bind-mounted volumes, not internal volumes + or named volumes. Mount propagation requires the source mount + point (the location where the source directory is mounted in the + host operating system) to have the correct propagation properties. + For shared volumes, the source mount point must be set to `shared`. + For slave volumes, the mount must be set to either `shared` or + `slave`. + items: + type: "string" + ContainerIDFile: + type: "string" + description: "Path to a file where the container ID is written" + LogConfig: + type: "object" + description: "The logging configuration for this container" + properties: + Type: + type: "string" + enum: + - "json-file" + - "syslog" + - "journald" + - "gelf" + - "fluentd" + - "awslogs" + - "splunk" + - "etwlogs" + - "none" + Config: + type: "object" + additionalProperties: + type: "string" + NetworkMode: + type: "string" + description: | + Network mode to use for this container. Supported standard values + are: `bridge`, `host`, `none`, and `container:`. Any + other value is taken as a custom network's name to which this + container should connect to. + PortBindings: + $ref: "#/definitions/PortMap" + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + AutoRemove: + type: "boolean" + description: | + Automatically remove the container when the container's process + exits. This has no effect if `RestartPolicy` is set. + VolumeDriver: + type: "string" + description: "Driver that this container uses to mount volumes." + VolumesFrom: + type: "array" + description: | + A list of volumes to inherit from another container, specified in + the form `[:]`. + items: + type: "string" + Mounts: + description: | + Specification for mounts to be added to the container. + type: "array" + items: + $ref: "#/definitions/Mount" + + # Applicable to UNIX platforms + CapAdd: + type: "array" + description: | + A list of kernel capabilities to add to the container. Conflicts + with option 'Capabilities'. + items: + type: "string" + CapDrop: + type: "array" + description: | + A list of kernel capabilities to drop from the container. Conflicts + with option 'Capabilities'. + items: + type: "string" + CgroupnsMode: + type: "string" + enum: + - "private" + - "host" + description: | + cgroup namespace mode for the container. Possible values are: + + - `"private"`: the container runs in its own private cgroup namespace + - `"host"`: use the host system's cgroup namespace + + If not specified, the daemon default is used, which can either be `"private"` + or `"host"`, depending on daemon version, kernel support and configuration. + Dns: + type: "array" + description: "A list of DNS servers for the container to use." + items: + type: "string" + DnsOptions: + type: "array" + description: "A list of DNS options." + items: + type: "string" + DnsSearch: + type: "array" + description: "A list of DNS search domains." + items: + type: "string" + ExtraHosts: + type: "array" + description: | + A list of hostnames/IP mappings to add to the container's `/etc/hosts` + file. Specified in the form `["hostname:IP"]`. + items: + type: "string" + GroupAdd: + type: "array" + description: | + A list of additional groups that the container process will run as. + items: + type: "string" + IpcMode: + type: "string" + description: | + IPC sharing mode for the container. Possible values are: + + - `"none"`: own private IPC namespace, with /dev/shm not mounted + - `"private"`: own private IPC namespace + - `"shareable"`: own private IPC namespace, with a possibility to share it with other containers + - `"container:"`: join another (shareable) container's IPC namespace + - `"host"`: use the host system's IPC namespace + + If not specified, daemon default is used, which can either be `"private"` + or `"shareable"`, depending on daemon version and configuration. + Cgroup: + type: "string" + description: "Cgroup to use for the container." + Links: + type: "array" + description: | + A list of links for the container in the form `container_name:alias`. + items: + type: "string" + OomScoreAdj: + type: "integer" + description: | + An integer value containing the score given to the container in + order to tune OOM killer preferences. + example: 500 + PidMode: + type: "string" + description: | + Set the PID (Process) Namespace mode for the container. It can be + either: + + - `"container:"`: joins another container's PID namespace + - `"host"`: use the host's PID namespace inside the container + Privileged: + type: "boolean" + description: "Gives the container full access to the host." + PublishAllPorts: + type: "boolean" + description: | + Allocates an ephemeral host port for all of a container's + exposed ports. + + Ports are de-allocated when the container stops and allocated when + the container starts. The allocated port might be changed when + restarting the container. + + The port is selected from the ephemeral port range that depends on + the kernel. For example, on Linux the range is defined by + `/proc/sys/net/ipv4/ip_local_port_range`. + ReadonlyRootfs: + type: "boolean" + description: "Mount the container's root filesystem as read only." + SecurityOpt: + type: "array" + description: "A list of string values to customize labels for MLS + systems, such as SELinux." + items: + type: "string" + StorageOpt: + type: "object" + description: | + Storage driver options for this container, in the form `{"size": "120G"}`. + additionalProperties: + type: "string" + Tmpfs: + type: "object" + description: | + A map of container directories which should be replaced by tmpfs + mounts, and their corresponding mount options. For example: + + ``` + { "/run": "rw,noexec,nosuid,size=65536k" } + ``` + additionalProperties: + type: "string" + UTSMode: + type: "string" + description: "UTS namespace to use for the container." + UsernsMode: + type: "string" + description: | + Sets the usernamespace mode for the container when usernamespace + remapping option is enabled. + ShmSize: + type: "integer" + description: | + Size of `/dev/shm` in bytes. If omitted, the system uses 64MB. + minimum: 0 + Sysctls: + type: "object" + description: | + A list of kernel parameters (sysctls) to set in the container. + For example: + + ``` + {"net.ipv4.ip_forward": "1"} + ``` + additionalProperties: + type: "string" + Runtime: + type: "string" + description: "Runtime to use with this container." + # Applicable to Windows + ConsoleSize: + type: "array" + description: | + Initial console size, as an `[height, width]` array. (Windows only) + minItems: 2 + maxItems: 2 + items: + type: "integer" + minimum: 0 + Isolation: + type: "string" + description: | + Isolation technology of the container. (Windows only) + enum: + - "default" + - "process" + - "hyperv" + MaskedPaths: + type: "array" + description: | + The list of paths to be masked inside the container (this overrides + the default set of paths). + items: + type: "string" + ReadonlyPaths: + type: "array" + description: | + The list of paths to be set as read-only inside the container + (this overrides the default set of paths). + items: + type: "string" + + ContainerConfig: + description: "Configuration for a container that is portable between hosts" + type: "object" + properties: + Hostname: + description: "The hostname to use for the container, as a valid RFC 1123 hostname." + type: "string" + Domainname: + description: "The domain name to use for the container." + type: "string" + User: + description: "The user that commands are run as inside the container." + type: "string" + AttachStdin: + description: "Whether to attach to `stdin`." + type: "boolean" + default: false + AttachStdout: + description: "Whether to attach to `stdout`." + type: "boolean" + default: true + AttachStderr: + description: "Whether to attach to `stderr`." + type: "boolean" + default: true + ExposedPorts: + description: | + An object mapping ports to an empty object in the form: + + `{"/": {}}` + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + Tty: + description: | + Attach standard streams to a TTY, including `stdin` if it is not closed. + type: "boolean" + default: false + OpenStdin: + description: "Open `stdin`" + type: "boolean" + default: false + StdinOnce: + description: "Close `stdin` after one attached client disconnects" + type: "boolean" + default: false + Env: + description: | + A list of environment variables to set inside the container in the + form `["VAR=value", ...]`. A variable without `=` is removed from the + environment, rather than to have an empty value. + type: "array" + items: + type: "string" + Cmd: + description: | + Command to run specified as a string or an array of strings. + type: "array" + items: + type: "string" + Healthcheck: + $ref: "#/definitions/HealthConfig" + ArgsEscaped: + description: "Command is already escaped (Windows only)" + type: "boolean" + Image: + description: | + The name of the image to use when creating the container/ + type: "string" + Volumes: + description: | + An object mapping mount point paths inside the container to empty + objects. + type: "object" + additionalProperties: + type: "object" + enum: + - {} + default: {} + WorkingDir: + description: "The working directory for commands to run in." + type: "string" + Entrypoint: + description: | + The entry point for the container as a string or an array of strings. + + If the array consists of exactly one empty string (`[""]`) then the + entry point is reset to system default (i.e., the entry point used by + docker when there is no `ENTRYPOINT` instruction in the `Dockerfile`). + type: "array" + items: + type: "string" + NetworkDisabled: + description: "Disable networking for the container." + type: "boolean" + MacAddress: + description: "MAC address of the container." + type: "string" + OnBuild: + description: | + `ONBUILD` metadata that were defined in the image's `Dockerfile`. + type: "array" + items: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + StopSignal: + description: | + Signal to stop a container as a string or unsigned integer. + type: "string" + default: "SIGTERM" + StopTimeout: + description: "Timeout to stop a container in seconds." + type: "integer" + default: 10 + Shell: + description: | + Shell for when `RUN`, `CMD`, and `ENTRYPOINT` uses a shell. + type: "array" + items: + type: "string" + + NetworkingConfig: + description: | + NetworkingConfig represents the container's networking configuration for + each of its interfaces. + It is used for the networking configs specified in the `docker create` + and `docker network connect` commands. + type: "object" + properties: + EndpointsConfig: + description: | + A mapping of network name to endpoint configuration for that network. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + example: + # putting an example here, instead of using the example values from + # /definitions/EndpointSettings, because containers/create currently + # does not support attaching to multiple networks, so the example request + # would be confusing if it showed that multiple networks can be contained + # in the EndpointsConfig. + # TODO remove once we support multiple networks on container create (see https://github.com/moby/moby/blob/07e6b843594e061f82baa5fa23c2ff7d536c2a05/daemon/create.go#L323) + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + NetworkSettings: + description: "NetworkSettings exposes the network settings in the API" + type: "object" + properties: + Bridge: + description: Name of the network's bridge (for example, `docker0`). + type: "string" + example: "docker0" + SandboxID: + description: SandboxID uniquely represents a container's network stack. + type: "string" + example: "9d12daf2c33f5959c8bf90aa513e4f65b561738661003029ec84830cd503a0c3" + HairpinMode: + description: | + Indicates if hairpin NAT should be enabled on the virtual interface. + type: "boolean" + example: false + LinkLocalIPv6Address: + description: IPv6 unicast address using the link-local prefix. + type: "string" + example: "fe80::42:acff:fe11:1" + LinkLocalIPv6PrefixLen: + description: Prefix length of the IPv6 unicast address. + type: "integer" + example: "64" + Ports: + $ref: "#/definitions/PortMap" + SandboxKey: + description: SandboxKey identifies the sandbox + type: "string" + example: "/var/run/docker/netns/8ab54b426c38" + + # TODO is SecondaryIPAddresses actually used? + SecondaryIPAddresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO is SecondaryIPv6Addresses actually used? + SecondaryIPv6Addresses: + description: "" + type: "array" + items: + $ref: "#/definitions/Address" + x-nullable: true + + # TODO properties below are part of DefaultNetworkSettings, which is + # marked as deprecated since Docker 1.9 and to be removed in Docker v17.12 + EndpointID: + description: | + EndpointID uniquely represents a service endpoint in a Sandbox. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.1" + GlobalIPv6Address: + description: | + Global IPv6 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 64 + IPAddress: + description: | + IPv4 address for the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address for this network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "2001:db8:2::100" + MacAddress: + description: | + MAC address for the container on the default "bridge" network. + +


+ + > **Deprecated**: This field is only propagated when attached to the + > default "bridge" network. Use the information from the "bridge" + > network inside the `Networks` map instead, which contains the same + > information. This field was deprecated in Docker 1.9 and is scheduled + > to be removed in Docker 17.12.0 + type: "string" + example: "02:42:ac:11:00:04" + Networks: + description: | + Information about all networks that the container is connected to. + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + + Address: + description: Address represents an IPv4 or IPv6 IP address. + type: "object" + properties: + Addr: + description: IP address. + type: "string" + PrefixLen: + description: Mask length of the IP address. + type: "integer" + + PortMap: + description: | + PortMap describes the mapping of container ports to host ports, using the + container's port-number and protocol as key in the format `/`, + for example, `80/udp`. + + If a container's port is mapped for multiple protocols, separate entries + are added to the mapping table. + type: "object" + additionalProperties: + type: "array" + x-nullable: true + items: + $ref: "#/definitions/PortBinding" + example: + "443/tcp": + - HostIp: "127.0.0.1" + HostPort: "4443" + "80/tcp": + - HostIp: "0.0.0.0" + HostPort: "80" + - HostIp: "0.0.0.0" + HostPort: "8080" + "80/udp": + - HostIp: "0.0.0.0" + HostPort: "80" + "53/udp": + - HostIp: "0.0.0.0" + HostPort: "53" + "2377/tcp": null + + PortBinding: + description: | + PortBinding represents a binding between a host IP address and a host + port. + type: "object" + properties: + HostIp: + description: "Host IP address that the container's port is mapped to." + type: "string" + example: "127.0.0.1" + HostPort: + description: "Host port number that the container's port is mapped to." + type: "string" + example: "4443" + + GraphDriverData: + description: "Information about a container's graph driver." + type: "object" + required: [Name, Data] + properties: + Name: + type: "string" + x-nullable: false + Data: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + + Image: + type: "object" + required: + - Id + - Parent + - Comment + - Created + - Container + - DockerVersion + - Author + - Architecture + - Os + - Size + - VirtualSize + - GraphDriver + - RootFS + properties: + Id: + type: "string" + x-nullable: false + RepoTags: + type: "array" + items: + type: "string" + RepoDigests: + type: "array" + items: + type: "string" + Parent: + type: "string" + x-nullable: false + Comment: + type: "string" + x-nullable: false + Created: + type: "string" + x-nullable: false + Container: + type: "string" + x-nullable: false + ContainerConfig: + $ref: "#/definitions/ContainerConfig" + DockerVersion: + type: "string" + x-nullable: false + Author: + type: "string" + x-nullable: false + Config: + $ref: "#/definitions/ContainerConfig" + Architecture: + type: "string" + x-nullable: false + Os: + type: "string" + x-nullable: false + OsVersion: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + VirtualSize: + type: "integer" + format: "int64" + x-nullable: false + GraphDriver: + $ref: "#/definitions/GraphDriverData" + RootFS: + type: "object" + required: [Type] + properties: + Type: + type: "string" + x-nullable: false + Layers: + type: "array" + items: + type: "string" + BaseLayer: + type: "string" + Metadata: + type: "object" + properties: + LastTagTime: + type: "string" + format: "dateTime" + + ImageSummary: + type: "object" + required: + - Id + - ParentId + - RepoTags + - RepoDigests + - Created + - Size + - SharedSize + - VirtualSize + - Labels + - Containers + properties: + Id: + type: "string" + x-nullable: false + ParentId: + type: "string" + x-nullable: false + RepoTags: + type: "array" + x-nullable: false + items: + type: "string" + RepoDigests: + type: "array" + x-nullable: false + items: + type: "string" + Created: + type: "integer" + x-nullable: false + Size: + type: "integer" + x-nullable: false + SharedSize: + type: "integer" + x-nullable: false + VirtualSize: + type: "integer" + x-nullable: false + Labels: + type: "object" + x-nullable: false + additionalProperties: + type: "string" + Containers: + x-nullable: false + type: "integer" + + AuthConfig: + type: "object" + properties: + username: + type: "string" + password: + type: "string" + email: + type: "string" + serveraddress: + type: "string" + example: + username: "hannibal" + password: "xxxx" + serveraddress: "https://index.docker.io/v1/" + + ProcessConfig: + type: "object" + properties: + privileged: + type: "boolean" + user: + type: "string" + tty: + type: "boolean" + entrypoint: + type: "string" + arguments: + type: "array" + items: + type: "string" + + Volume: + type: "object" + required: [Name, Driver, Mountpoint, Labels, Scope, Options] + properties: + Name: + type: "string" + description: "Name of the volume." + x-nullable: false + Driver: + type: "string" + description: "Name of the volume driver used by the volume." + x-nullable: false + Mountpoint: + type: "string" + description: "Mount path of the volume on the host." + x-nullable: false + CreatedAt: + type: "string" + format: "dateTime" + description: "Date/Time the volume was created." + Status: + type: "object" + description: | + Low-level details about the volume, provided by the volume driver. + Details are returned as a map with key/value pairs: + `{"key":"value","key2":"value2"}`. + + The `Status` field is optional, and is omitted if the volume driver + does not support this feature. + additionalProperties: + type: "object" + Labels: + type: "object" + description: "User-defined key/value metadata." + x-nullable: false + additionalProperties: + type: "string" + Scope: + type: "string" + description: | + The level at which the volume exists. Either `global` for cluster-wide, + or `local` for machine level. + default: "local" + x-nullable: false + enum: ["local", "global"] + Options: + type: "object" + description: | + The driver specific options used when creating the volume. + additionalProperties: + type: "string" + UsageData: + type: "object" + x-nullable: true + required: [Size, RefCount] + description: | + Usage details about the volume. This information is used by the + `GET /system/df` endpoint, and omitted in other endpoints. + properties: + Size: + type: "integer" + default: -1 + description: | + Amount of disk space used by the volume (in bytes). This information + is only available for volumes created with the `"local"` volume + driver. For volumes created with other volume drivers, this field + is set to `-1` ("not available") + x-nullable: false + RefCount: + type: "integer" + default: -1 + description: | + The number of containers referencing this volume. This field + is set to `-1` if the reference-count is not available. + x-nullable: false + + example: + Name: "tardis" + Driver: "custom" + Mountpoint: "/var/lib/docker/volumes/tardis" + Status: + hello: "world" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + CreatedAt: "2016-06-07T20:31:11.853781916Z" + + Network: + type: "object" + properties: + Name: + type: "string" + Id: + type: "string" + Created: + type: "string" + format: "dateTime" + Scope: + type: "string" + Driver: + type: "string" + EnableIPv6: + type: "boolean" + IPAM: + $ref: "#/definitions/IPAM" + Internal: + type: "boolean" + Attachable: + type: "boolean" + Ingress: + type: "boolean" + Containers: + type: "object" + additionalProperties: + $ref: "#/definitions/NetworkContainer" + Options: + type: "object" + additionalProperties: + type: "string" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + Name: "net01" + Id: "7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99" + Created: "2016-10-19T04:33:30.360899459Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + IPAM: + Driver: "default" + Config: + - Subnet: "172.19.0.0/16" + Gateway: "172.19.0.1" + Options: + foo: "bar" + Internal: false + Attachable: false + Ingress: false + Containers: + 19a4d5d687db25203351ed79d478946f861258f018fe384f229f2efa4b23513c: + Name: "test" + EndpointID: "628cadb8bcb92de107b2a1e516cbffe463e321f548feb37697cce00ad694f21a" + MacAddress: "02:42:ac:13:00:02" + IPv4Address: "172.19.0.2/16" + IPv6Address: "" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + IPAM: + type: "object" + properties: + Driver: + description: "Name of the IPAM driver to use." + type: "string" + default: "default" + Config: + description: | + List of IPAM configuration options, specified as a map: + + ``` + {"Subnet": , "IPRange": , "Gateway": , "AuxAddress": } + ``` + type: "array" + items: + type: "object" + additionalProperties: + type: "string" + Options: + description: "Driver-specific options, specified as a map." + type: "object" + additionalProperties: + type: "string" + + NetworkContainer: + type: "object" + properties: + Name: + type: "string" + EndpointID: + type: "string" + MacAddress: + type: "string" + IPv4Address: + type: "string" + IPv6Address: + type: "string" + + BuildInfo: + type: "object" + properties: + id: + type: "string" + stream: + type: "string" + error: + type: "string" + errorDetail: + $ref: "#/definitions/ErrorDetail" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + aux: + $ref: "#/definitions/ImageID" + + BuildCache: + type: "object" + properties: + ID: + type: "string" + Parent: + type: "string" + Type: + type: "string" + Description: + type: "string" + InUse: + type: "boolean" + Shared: + type: "boolean" + Size: + description: | + Amount of disk space used by the build cache (in bytes). + type: "integer" + CreatedAt: + description: | + Date and time at which the build cache was created in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + LastUsedAt: + description: | + Date and time at which the build cache was last used in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + x-nullable: true + example: "2017-08-09T07:09:37.632105588Z" + UsageCount: + type: "integer" + + ImageID: + type: "object" + description: "Image ID or Digest" + properties: + ID: + type: "string" + example: + ID: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + + CreateImageInfo: + type: "object" + properties: + id: + type: "string" + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + PushImageInfo: + type: "object" + properties: + error: + type: "string" + status: + type: "string" + progress: + type: "string" + progressDetail: + $ref: "#/definitions/ProgressDetail" + + ErrorDetail: + type: "object" + properties: + code: + type: "integer" + message: + type: "string" + + ProgressDetail: + type: "object" + properties: + current: + type: "integer" + total: + type: "integer" + + ErrorResponse: + description: "Represents an error." + type: "object" + required: ["message"] + properties: + message: + description: "The error message." + type: "string" + x-nullable: false + example: + message: "Something went wrong." + + IdResponse: + description: "Response to an API call that returns just an Id" + type: "object" + required: ["Id"] + properties: + Id: + description: "The id of the newly created object." + type: "string" + x-nullable: false + + EndpointSettings: + description: "Configuration for a network endpoint." + type: "object" + properties: + # Configurations + IPAMConfig: + $ref: "#/definitions/EndpointIPAMConfig" + Links: + type: "array" + items: + type: "string" + example: + - "container_1" + - "container_2" + Aliases: + type: "array" + items: + type: "string" + example: + - "server_x" + - "server_y" + + # Operational data + NetworkID: + description: | + Unique ID of the network. + type: "string" + example: "08754567f1f40222263eab4102e1c733ae697e8e354aa9cd6e18d7402835292a" + EndpointID: + description: | + Unique ID for the service endpoint in a Sandbox. + type: "string" + example: "b88f5b905aabf2893f3cbc4ee42d1ea7980bbc0a92e2c8922b1e1795298afb0b" + Gateway: + description: | + Gateway address for this network. + type: "string" + example: "172.17.0.1" + IPAddress: + description: | + IPv4 address. + type: "string" + example: "172.17.0.4" + IPPrefixLen: + description: | + Mask length of the IPv4 address. + type: "integer" + example: 16 + IPv6Gateway: + description: | + IPv6 gateway address. + type: "string" + example: "2001:db8:2::100" + GlobalIPv6Address: + description: | + Global IPv6 address. + type: "string" + example: "2001:db8::5689" + GlobalIPv6PrefixLen: + description: | + Mask length of the global IPv6 address. + type: "integer" + format: "int64" + example: 64 + MacAddress: + description: | + MAC address for the endpoint on this network. + type: "string" + example: "02:42:ac:11:00:04" + DriverOpts: + description: | + DriverOpts is a mapping of driver options and values. These options + are passed directly to the driver and are driver specific. + type: "object" + x-nullable: true + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + + EndpointIPAMConfig: + description: | + EndpointIPAMConfig represents an endpoint's IPAM configuration. + type: "object" + x-nullable: true + properties: + IPv4Address: + type: "string" + example: "172.20.30.33" + IPv6Address: + type: "string" + example: "2001:db8:abcd::3033" + LinkLocalIPs: + type: "array" + items: + type: "string" + example: + - "169.254.34.68" + - "fe80::3468" + + PluginMount: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Source, Destination, Type, Options] + properties: + Name: + type: "string" + x-nullable: false + example: "some-mount" + Description: + type: "string" + x-nullable: false + example: "This is a mount that's used by the plugin." + Settable: + type: "array" + items: + type: "string" + Source: + type: "string" + example: "/var/lib/docker/plugins/" + Destination: + type: "string" + x-nullable: false + example: "/mnt/state" + Type: + type: "string" + x-nullable: false + example: "bind" + Options: + type: "array" + items: + type: "string" + example: + - "rbind" + - "rw" + + PluginDevice: + type: "object" + required: [Name, Description, Settable, Path] + x-nullable: false + properties: + Name: + type: "string" + x-nullable: false + Description: + type: "string" + x-nullable: false + Settable: + type: "array" + items: + type: "string" + Path: + type: "string" + example: "/dev/fuse" + + PluginEnv: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + Description: + x-nullable: false + type: "string" + Settable: + type: "array" + items: + type: "string" + Value: + type: "string" + + PluginInterfaceType: + type: "object" + x-nullable: false + required: [Prefix, Capability, Version] + properties: + Prefix: + type: "string" + x-nullable: false + Capability: + type: "string" + x-nullable: false + Version: + type: "string" + x-nullable: false + + Plugin: + description: "A plugin for the Engine API" + type: "object" + required: [Settings, Enabled, Config, Name] + properties: + Id: + type: "string" + example: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078" + Name: + type: "string" + x-nullable: false + example: "tiborvass/sample-volume-plugin" + Enabled: + description: + True if the plugin is running. False if the plugin is not running, + only installed. + type: "boolean" + x-nullable: false + example: true + Settings: + description: "Settings that can be modified by users." + type: "object" + x-nullable: false + required: [Args, Devices, Env, Mounts] + properties: + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + type: "string" + example: + - "DEBUG=0" + Args: + type: "array" + items: + type: "string" + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PluginReference: + description: "plugin remote reference used to push/pull the plugin" + type: "string" + x-nullable: false + example: "localhost:5000/tiborvass/sample-volume-plugin:latest" + Config: + description: "The config of a plugin." + type: "object" + x-nullable: false + required: + - Description + - Documentation + - Interface + - Entrypoint + - WorkDir + - Network + - Linux + - PidHost + - PropagatedMount + - IpcHost + - Mounts + - Env + - Args + properties: + DockerVersion: + description: "Docker Version used to create the plugin" + type: "string" + x-nullable: false + example: "17.06.0-ce" + Description: + type: "string" + x-nullable: false + example: "A sample volume plugin for Docker" + Documentation: + type: "string" + x-nullable: false + example: "https://docs.docker.com/engine/extend/plugins/" + Interface: + description: "The interface between Docker and the plugin" + x-nullable: false + type: "object" + required: [Types, Socket] + properties: + Types: + type: "array" + items: + $ref: "#/definitions/PluginInterfaceType" + example: + - "docker.volumedriver/1.0" + Socket: + type: "string" + x-nullable: false + example: "plugins.sock" + ProtocolScheme: + type: "string" + example: "some.protocol/v1.0" + description: "Protocol to use for clients connecting to the plugin." + enum: + - "" + - "moby.plugins.http/v1" + Entrypoint: + type: "array" + items: + type: "string" + example: + - "/usr/bin/sample-volume-plugin" + - "/data" + WorkDir: + type: "string" + x-nullable: false + example: "/bin/" + User: + type: "object" + x-nullable: false + properties: + UID: + type: "integer" + format: "uint32" + example: 1000 + GID: + type: "integer" + format: "uint32" + example: 1000 + Network: + type: "object" + x-nullable: false + required: [Type] + properties: + Type: + x-nullable: false + type: "string" + example: "host" + Linux: + type: "object" + x-nullable: false + required: [Capabilities, AllowAllDevices, Devices] + properties: + Capabilities: + type: "array" + items: + type: "string" + example: + - "CAP_SYS_ADMIN" + - "CAP_SYSLOG" + AllowAllDevices: + type: "boolean" + x-nullable: false + example: false + Devices: + type: "array" + items: + $ref: "#/definitions/PluginDevice" + PropagatedMount: + type: "string" + x-nullable: false + example: "/mnt/volumes" + IpcHost: + type: "boolean" + x-nullable: false + example: false + PidHost: + type: "boolean" + x-nullable: false + example: false + Mounts: + type: "array" + items: + $ref: "#/definitions/PluginMount" + Env: + type: "array" + items: + $ref: "#/definitions/PluginEnv" + example: + - Name: "DEBUG" + Description: "If set, prints debug messages" + Settable: null + Value: "0" + Args: + type: "object" + x-nullable: false + required: [Name, Description, Settable, Value] + properties: + Name: + x-nullable: false + type: "string" + example: "args" + Description: + x-nullable: false + type: "string" + example: "command line arguments" + Settable: + type: "array" + items: + type: "string" + Value: + type: "array" + items: + type: "string" + rootfs: + type: "object" + properties: + type: + type: "string" + example: "layers" + diff_ids: + type: "array" + items: + type: "string" + example: + - "sha256:675532206fbf3030b8458f88d6e26d4eb1577688a25efec97154c94e8b6b4887" + - "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + + ObjectVersion: + description: | + The version number of the object such as node, service, etc. This is needed + to avoid conflicting writes. The client must send the version number along + with the modified specification when updating these objects. + + This approach ensures safe concurrency and determinism in that the change + on the object may not be applied if the version number has changed from the + last read. In other words, if two update requests specify the same base + version, only one of the requests can succeed. As a result, two separate + update requests that happen at the same time will not unintentionally + overwrite each other. + type: "object" + properties: + Index: + type: "integer" + format: "uint64" + example: 373531 + + NodeSpec: + type: "object" + properties: + Name: + description: "Name for the node." + type: "string" + example: "my-node" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Role: + description: "Role of the node." + type: "string" + enum: + - "worker" + - "manager" + example: "manager" + Availability: + description: "Availability of the node." + type: "string" + enum: + - "active" + - "pause" + - "drain" + example: "active" + example: + Availability: "active" + Name: "node-name" + Role: "manager" + Labels: + foo: "bar" + + Node: + type: "object" + properties: + ID: + type: "string" + example: "24ifsmvkjbyhk" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the node was added to the swarm in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the node was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/NodeSpec" + Description: + $ref: "#/definitions/NodeDescription" + Status: + $ref: "#/definitions/NodeStatus" + ManagerStatus: + $ref: "#/definitions/ManagerStatus" + + NodeDescription: + description: | + NodeDescription encapsulates the properties of the Node as reported by the + agent. + type: "object" + properties: + Hostname: + type: "string" + example: "bf3067039e47" + Platform: + $ref: "#/definitions/Platform" + Resources: + $ref: "#/definitions/ResourceObject" + Engine: + $ref: "#/definitions/EngineDescription" + TLSInfo: + $ref: "#/definitions/TLSInfo" + + Platform: + description: | + Platform represents the platform (Arch/OS). + type: "object" + properties: + Architecture: + description: | + Architecture represents the hardware architecture (for example, + `x86_64`). + type: "string" + example: "x86_64" + OS: + description: | + OS represents the Operating System (for example, `linux` or `windows`). + type: "string" + example: "linux" + + EngineDescription: + description: "EngineDescription provides information about an engine." + type: "object" + properties: + EngineVersion: + type: "string" + example: "17.06.0" + Labels: + type: "object" + additionalProperties: + type: "string" + example: + foo: "bar" + Plugins: + type: "array" + items: + type: "object" + properties: + Type: + type: "string" + Name: + type: "string" + example: + - Type: "Log" + Name: "awslogs" + - Type: "Log" + Name: "fluentd" + - Type: "Log" + Name: "gcplogs" + - Type: "Log" + Name: "gelf" + - Type: "Log" + Name: "journald" + - Type: "Log" + Name: "json-file" + - Type: "Log" + Name: "logentries" + - Type: "Log" + Name: "splunk" + - Type: "Log" + Name: "syslog" + - Type: "Network" + Name: "bridge" + - Type: "Network" + Name: "host" + - Type: "Network" + Name: "ipvlan" + - Type: "Network" + Name: "macvlan" + - Type: "Network" + Name: "null" + - Type: "Network" + Name: "overlay" + - Type: "Volume" + Name: "local" + - Type: "Volume" + Name: "localhost:5000/vieux/sshfs:latest" + - Type: "Volume" + Name: "vieux/sshfs:latest" + + TLSInfo: + description: | + Information about the issuer of leaf TLS certificates and the trusted root + CA certificate. + type: "object" + properties: + TrustRoot: + description: | + The root CA certificate(s) that are used to validate leaf TLS + certificates. + type: "string" + CertIssuerSubject: + description: + The base64-url-safe-encoded raw subject bytes of the issuer. + type: "string" + CertIssuerPublicKey: + description: | + The base64-url-safe-encoded raw public key bytes of the issuer. + type: "string" + example: + TrustRoot: | + -----BEGIN CERTIFICATE----- + MIIBajCCARCgAwIBAgIUbYqrLSOSQHoxD8CwG6Bi2PJi9c8wCgYIKoZIzj0EAwIw + EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwNDI0MjE0MzAwWhcNMzcwNDE5MjE0 + MzAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH + A0IABJk/VyMPYdaqDXJb/VXh5n/1Yuv7iNrxV3Qb3l06XD46seovcDWs3IZNV1lf + 3Skyr0ofcchipoiHkXBODojJydSjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB + Af8EBTADAQH/MB0GA1UdDgQWBBRUXxuRcnFjDfR/RIAUQab8ZV/n4jAKBggqhkjO + PQQDAgNIADBFAiAy+JTe6Uc3KyLCMiqGl2GyWGQqQDEcO3/YG36x7om65AIhAJvz + pxv6zFeVEkAEEkqIYi0omA9+CjanB/6Bz4n1uw8H + -----END CERTIFICATE----- + CertIssuerSubject: "MBMxETAPBgNVBAMTCHN3YXJtLWNh" + CertIssuerPublicKey: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmT9XIw9h1qoNclv9VeHmf/Vi6/uI2vFXdBveXTpcPjqx6i9wNazchk1XWV/dKTKvSh9xyGKmiIeRcE4OiMnJ1A==" + + NodeStatus: + description: | + NodeStatus represents the status of a node. + + It provides the current status of the node, as seen by the manager. + type: "object" + properties: + State: + $ref: "#/definitions/NodeState" + Message: + type: "string" + example: "" + Addr: + description: "IP address of the node." + type: "string" + example: "172.17.0.2" + + NodeState: + description: "NodeState represents the state of a node." + type: "string" + enum: + - "unknown" + - "down" + - "ready" + - "disconnected" + example: "ready" + + ManagerStatus: + description: | + ManagerStatus represents the status of a manager. + + It provides the current status of a node's manager component, if the node + is a manager. + x-nullable: true + type: "object" + properties: + Leader: + type: "boolean" + default: false + example: true + Reachability: + $ref: "#/definitions/Reachability" + Addr: + description: | + The IP address and port at which the manager is reachable. + type: "string" + example: "10.0.0.46:2377" + + Reachability: + description: "Reachability represents the reachability of a node." + type: "string" + enum: + - "unknown" + - "unreachable" + - "reachable" + example: "reachable" + + SwarmSpec: + description: "User modifiable swarm configuration." + type: "object" + properties: + Name: + description: "Name of the swarm." + type: "string" + example: "default" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.corp.type: "production" + com.example.corp.department: "engineering" + Orchestration: + description: "Orchestration configuration." + type: "object" + x-nullable: true + properties: + TaskHistoryRetentionLimit: + description: | + The number of historic tasks to keep per instance or node. If + negative, never remove completed or failed tasks. + type: "integer" + format: "int64" + example: 10 + Raft: + description: "Raft configuration." + type: "object" + properties: + SnapshotInterval: + description: "The number of log entries between snapshots." + type: "integer" + format: "uint64" + example: 10000 + KeepOldSnapshots: + description: | + The number of snapshots to keep beyond the current snapshot. + type: "integer" + format: "uint64" + LogEntriesForSlowFollowers: + description: | + The number of log entries to keep around to sync up slow followers + after a snapshot is created. + type: "integer" + format: "uint64" + example: 500 + ElectionTick: + description: | + The number of ticks that a follower will wait for a message from + the leader before becoming a candidate and starting an election. + `ElectionTick` must be greater than `HeartbeatTick`. + + A tick currently defaults to one second, so these translate + directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 3 + HeartbeatTick: + description: | + The number of ticks between heartbeats. Every HeartbeatTick ticks, + the leader will send a heartbeat to the followers. + + A tick currently defaults to one second, so these translate + directly to seconds currently, but this is NOT guaranteed. + type: "integer" + example: 1 + Dispatcher: + description: "Dispatcher configuration." + type: "object" + x-nullable: true + properties: + HeartbeatPeriod: + description: | + The delay for an agent to send a heartbeat to the dispatcher. + type: "integer" + format: "int64" + example: 5000000000 + CAConfig: + description: "CA configuration." + type: "object" + x-nullable: true + properties: + NodeCertExpiry: + description: "The duration node certificates are issued for." + type: "integer" + format: "int64" + example: 7776000000000000 + ExternalCAs: + description: | + Configuration for forwarding signing requests to an external + certificate authority. + type: "array" + items: + type: "object" + properties: + Protocol: + description: | + Protocol for communication with the external CA (currently + only `cfssl` is supported). + type: "string" + enum: + - "cfssl" + default: "cfssl" + URL: + description: | + URL where certificate signing requests should be sent. + type: "string" + Options: + description: | + An object with key/value pairs that are interpreted as + protocol-specific options for the external CA driver. + type: "object" + additionalProperties: + type: "string" + CACert: + description: | + The root CA certificate (in PEM format) this external CA uses + to issue TLS certificates (assumed to be to the current swarm + root CA certificate if not provided). + type: "string" + SigningCACert: + description: | + The desired signing CA certificate for all swarm node TLS leaf + certificates, in PEM format. + type: "string" + SigningCAKey: + description: | + The desired signing CA key for all swarm node TLS leaf certificates, + in PEM format. + type: "string" + ForceRotate: + description: | + An integer whose purpose is to force swarm to generate a new + signing CA certificate and key, if none have been specified in + `SigningCACert` and `SigningCAKey` + format: "uint64" + type: "integer" + EncryptionConfig: + description: "Parameters related to encryption-at-rest." + type: "object" + properties: + AutoLockManagers: + description: | + If set, generate a key and use it to lock data stored on the + managers. + type: "boolean" + example: false + TaskDefaults: + description: "Defaults for creating tasks in this cluster." + type: "object" + properties: + LogDriver: + description: | + The log driver to use for tasks created in the orchestrator if + unspecified by a service. + + Updating this value only affects new tasks. Existing tasks continue + to use their previously configured log driver until recreated. + type: "object" + properties: + Name: + description: | + The log driver to use as a default for new tasks. + type: "string" + example: "json-file" + Options: + description: | + Driver-specific options for the selectd log driver, specified + as key/value pairs. + type: "object" + additionalProperties: + type: "string" + example: + "max-file": "10" + "max-size": "100m" + + # The Swarm information for `GET /info`. It is the same as `GET /swarm`, but + # without `JoinTokens`. + ClusterInfo: + description: | + ClusterInfo represents information about the swarm as is returned by the + "/info" endpoint. Join-tokens are not included. + x-nullable: true + type: "object" + properties: + ID: + description: "The ID of the swarm." + type: "string" + example: "abajmipo7b4xz5ip2nrla6b11" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + description: | + Date and time at which the swarm was initialised in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2016-08-18T10:44:24.496525531Z" + UpdatedAt: + description: | + Date and time at which the swarm was last updated in + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. + type: "string" + format: "dateTime" + example: "2017-08-09T07:09:37.632105588Z" + Spec: + $ref: "#/definitions/SwarmSpec" + TLSInfo: + $ref: "#/definitions/TLSInfo" + RootRotationInProgress: + description: | + Whether there is currently a root CA rotation in progress for the swarm + type: "boolean" + example: false + DataPathPort: + description: | + DataPathPort specifies the data path port number for data traffic. + Acceptable port range is 1024 to 49151. + If no port is set or is set to 0, the default port (4789) is used. + type: "integer" + format: "uint32" + default: 4789 + example: 4789 + DefaultAddrPool: + description: | + Default Address Pool specifies default subnet pools for global scope + networks. + type: "array" + items: + type: "string" + format: "CIDR" + example: ["10.10.0.0/16", "20.20.0.0/16"] + SubnetSize: + description: | + SubnetSize specifies the subnet size of the networks created from the + default subnet pool. + type: "integer" + format: "uint32" + maximum: 29 + default: 24 + example: 24 + + JoinTokens: + description: | + JoinTokens contains the tokens workers and managers need to join the swarm. + type: "object" + properties: + Worker: + description: | + The token workers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx" + Manager: + description: | + The token managers can use to join the swarm. + type: "string" + example: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + + Swarm: + type: "object" + allOf: + - $ref: "#/definitions/ClusterInfo" + - type: "object" + properties: + JoinTokens: + $ref: "#/definitions/JoinTokens" + + TaskSpec: + description: "User modifiable task configuration." + type: "object" + properties: + PluginSpec: + type: "object" + description: | + Plugin spec for the service. *(Experimental release only.)* + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + properties: + Name: + description: "The name or 'alias' to use for the plugin." + type: "string" + Remote: + description: "The plugin image reference to use." + type: "string" + Disabled: + description: "Disable the plugin once scheduled." + type: "boolean" + PluginPrivilege: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + ContainerSpec: + type: "object" + description: | + Container spec for the service. + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + properties: + Image: + description: "The image name to use for the container" + type: "string" + Labels: + description: "User-defined key/value data." + type: "object" + additionalProperties: + type: "string" + Command: + description: "The command to be run in the image." + type: "array" + items: + type: "string" + Args: + description: "Arguments to the command." + type: "array" + items: + type: "string" + Hostname: + description: | + The hostname to use for the container, as a valid + [RFC 1123](https://tools.ietf.org/html/rfc1123) hostname. + type: "string" + Env: + description: | + A list of environment variables in the form `VAR=value`. + type: "array" + items: + type: "string" + Dir: + description: "The working directory for commands to run in." + type: "string" + User: + description: "The user inside the container." + type: "string" + Groups: + type: "array" + description: | + A list of additional groups that the container process will run as. + items: + type: "string" + Privileges: + type: "object" + description: "Security options for the container" + properties: + CredentialSpec: + type: "object" + description: "CredentialSpec for managed service account (Windows only)" + properties: + Config: + type: "string" + example: "0bt9dmxjvjiqermk6xrop3ekq" + description: | + Load credential spec from a Swarm Config with the given ID. + The specified config must also be present in the Configs + field with the Runtime property set. + +


+ + + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. + File: + type: "string" + example: "spec.json" + description: | + Load credential spec from this file. The file is read by + the daemon, and must be present in the `CredentialSpecs` + subdirectory in the docker data directory, which defaults + to `C:\ProgramData\Docker\` on Windows. + + For example, specifying `spec.json` loads + `C:\ProgramData\Docker\CredentialSpecs\spec.json`. + +


+ + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. + Registry: + type: "string" + description: | + Load credential spec from this value in the Windows + registry. The specified registry value must be located in: + + `HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs` + +


+ + + > **Note**: `CredentialSpec.File`, `CredentialSpec.Registry`, + > and `CredentialSpec.Config` are mutually exclusive. + SELinuxContext: + type: "object" + description: "SELinux labels of the container" + properties: + Disable: + type: "boolean" + description: "Disable SELinux" + User: + type: "string" + description: "SELinux user label" + Role: + type: "string" + description: "SELinux role label" + Type: + type: "string" + description: "SELinux type label" + Level: + type: "string" + description: "SELinux level label" + TTY: + description: "Whether a pseudo-TTY should be allocated." + type: "boolean" + OpenStdin: + description: "Open `stdin`" + type: "boolean" + ReadOnly: + description: "Mount the container's root filesystem as read only." + type: "boolean" + Mounts: + description: | + Specification for mounts to be added to containers created as part + of the service. + type: "array" + items: + $ref: "#/definitions/Mount" + StopSignal: + description: "Signal to stop the container." + type: "string" + StopGracePeriod: + description: | + Amount of time to wait for the container to terminate before + forcefully killing it. + type: "integer" + format: "int64" + HealthCheck: + $ref: "#/definitions/HealthConfig" + Hosts: + type: "array" + description: | + A list of hostname/IP mappings to add to the container's `hosts` + file. The format of extra hosts is specified in the + [hosts(5)](http://man7.org/linux/man-pages/man5/hosts.5.html) + man page: + + IP_address canonical_hostname [aliases...] + items: + type: "string" + DNSConfig: + description: | + Specification for DNS related configurations in resolver configuration + file (`resolv.conf`). + type: "object" + properties: + Nameservers: + description: "The IP addresses of the name servers." + type: "array" + items: + type: "string" + Search: + description: "A search list for host-name lookup." + type: "array" + items: + type: "string" + Options: + description: | + A list of internal resolver variables to be modified (e.g., + `debug`, `ndots:3`, etc.). + type: "array" + items: + type: "string" + Secrets: + description: | + Secrets contains references to zero or more secrets that will be + exposed to the service. + type: "array" + items: + type: "object" + properties: + File: + description: | + File represents a specific target that is backed by a file. + type: "object" + properties: + Name: + description: | + Name represents the final filename in the filesystem. + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + SecretID: + description: | + SecretID represents the ID of the specific secret that we're + referencing. + type: "string" + SecretName: + description: | + SecretName is the name of the secret that this references, + but this is just provided for lookup/display purposes. The + secret in the reference will be identified by its ID. + type: "string" + Configs: + description: | + Configs contains references to zero or more configs that will be + exposed to the service. + type: "array" + items: + type: "object" + properties: + File: + description: | + File represents a specific target that is backed by a file. + +


+ + > **Note**: `Configs.File` and `Configs.Runtime` are mutually exclusive + type: "object" + properties: + Name: + description: | + Name represents the final filename in the filesystem. + type: "string" + UID: + description: "UID represents the file UID." + type: "string" + GID: + description: "GID represents the file GID." + type: "string" + Mode: + description: "Mode represents the FileMode of the file." + type: "integer" + format: "uint32" + Runtime: + description: | + Runtime represents a target that is not mounted into the + container but is used by the task + +


+ + > **Note**: `Configs.File` and `Configs.Runtime` are mutually + > exclusive + type: "object" + ConfigID: + description: | + ConfigID represents the ID of the specific config that we're + referencing. + type: "string" + ConfigName: + description: | + ConfigName is the name of the config that this references, + but this is just provided for lookup/display purposes. The + config in the reference will be identified by its ID. + type: "string" + Isolation: + type: "string" + description: | + Isolation technology of the containers running the service. + (Windows only) + enum: + - "default" + - "process" + - "hyperv" + Init: + description: | + Run an init inside the container that forwards signals and reaps + processes. This field is omitted if empty, and the default (as + configured on the daemon) is used. + type: "boolean" + x-nullable: true + Sysctls: + description: | + Set kernel namedspaced parameters (sysctls) in the container. + The Sysctls option on services accepts the same sysctls as the + are supported on containers. Note that while the same sysctls are + supported, no guarantees or checks are made about their + suitability for a clustered environment, and it's up to the user + to determine whether a given sysctl will work properly in a + Service. + type: "object" + additionalProperties: + type: "string" + # This option is not used by Windows containers + CapabilityAdd: + type: "array" + description: | + A list of kernel capabilities to add to the default set + for the container. + items: + type: "string" + example: + - "CAP_NET_RAW" + - "CAP_SYS_ADMIN" + - "CAP_SYS_CHROOT" + - "CAP_SYSLOG" + CapabilityDrop: + type: "array" + description: | + A list of kernel capabilities to drop from the default set + for the container. + items: + type: "string" + example: + - "CAP_NET_RAW" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" + NetworkAttachmentSpec: + description: | + Read-only spec type for non-swarm containers attached to swarm overlay + networks. + +


+ + > **Note**: ContainerSpec, NetworkAttachmentSpec, and PluginSpec are + > mutually exclusive. PluginSpec is only used when the Runtime field + > is set to `plugin`. NetworkAttachmentSpec is used when the Runtime + > field is set to `attachment`. + type: "object" + properties: + ContainerID: + description: "ID of the container represented by this task" + type: "string" + Resources: + description: | + Resource requirements which apply to each individual container created + as part of the service. + type: "object" + properties: + Limits: + description: "Define resources limits." + $ref: "#/definitions/Limit" + Reservation: + description: "Define resources reservation." + $ref: "#/definitions/ResourceObject" + RestartPolicy: + description: | + Specification for the restart policy which applies to containers + created as part of this service. + type: "object" + properties: + Condition: + description: "Condition for restart." + type: "string" + enum: + - "none" + - "on-failure" + - "any" + Delay: + description: "Delay between restart attempts." + type: "integer" + format: "int64" + MaxAttempts: + description: | + Maximum attempts to restart a given container before giving up + (default value is 0, which is ignored). + type: "integer" + format: "int64" + default: 0 + Window: + description: | + Windows is the time window used to evaluate the restart policy + (default value is 0, which is unbounded). + type: "integer" + format: "int64" + default: 0 + Placement: + type: "object" + properties: + Constraints: + description: | + An array of constraint expressions to limit the set of nodes where + a task can be scheduled. Constraint expressions can either use a + _match_ (`==`) or _exclude_ (`!=`) rule. Multiple constraints find + nodes that satisfy every expression (AND match). Constraints can + match node or Docker Engine labels as follows: + + node attribute | matches | example + ---------------------|--------------------------------|----------------------------------------------- + `node.id` | Node ID | `node.id==2ivku8v2gvtg4` + `node.hostname` | Node hostname | `node.hostname!=node-2` + `node.role` | Node role (`manager`/`worker`) | `node.role==manager` + `node.platform.os` | Node operating system | `node.platform.os==windows` + `node.platform.arch` | Node architecture | `node.platform.arch==x86_64` + `node.labels` | User-defined node labels | `node.labels.security==high` + `engine.labels` | Docker Engine's labels | `engine.labels.operatingsystem==ubuntu-14.04` + + `engine.labels` apply to Docker Engine labels like operating system, + drivers, etc. Swarm administrators add `node.labels` for operational + purposes by using the [`node update endpoint`](#operation/NodeUpdate). + + type: "array" + items: + type: "string" + example: + - "node.hostname!=node3.corp.example.com" + - "node.role!=manager" + - "node.labels.type==production" + - "node.platform.os==linux" + - "node.platform.arch==x86_64" + Preferences: + description: | + Preferences provide a way to make the scheduler aware of factors + such as topology. They are provided in order from highest to + lowest precedence. + type: "array" + items: + type: "object" + properties: + Spread: + type: "object" + properties: + SpreadDescriptor: + description: | + label descriptor, such as `engine.labels.az`. + type: "string" + example: + - Spread: + SpreadDescriptor: "node.labels.datacenter" + - Spread: + SpreadDescriptor: "node.labels.rack" + MaxReplicas: + description: | + Maximum number of replicas for per node (default value is 0, which + is unlimited) + type: "integer" + format: "int64" + default: 0 + Platforms: + description: | + Platforms stores all the platforms that the service's image can + run on. This field is used in the platform filter for scheduling. + If empty, then the platform filter is off, meaning there are no + scheduling restrictions. + type: "array" + items: + $ref: "#/definitions/Platform" + ForceUpdate: + description: | + A counter that triggers an update even if no relevant parameters have + been changed. + type: "integer" + Runtime: + description: | + Runtime is the type of runtime specified for the task executor. + type: "string" + Networks: + description: "Specifies which networks the service should attach to." + type: "array" + items: + $ref: "#/definitions/NetworkAttachmentConfig" + LogDriver: + description: | + Specifies the log driver to use for tasks created from this spec. If + not present, the default one for the swarm will be used, finally + falling back to the engine default if not specified. + type: "object" + properties: + Name: + type: "string" + Options: + type: "object" + additionalProperties: + type: "string" + + TaskState: + type: "string" + enum: + - "new" + - "allocated" + - "pending" + - "assigned" + - "accepted" + - "preparing" + - "ready" + - "starting" + - "running" + - "complete" + - "shutdown" + - "failed" + - "rejected" + - "remove" + - "orphaned" + + Task: + type: "object" + properties: + ID: + description: "The ID of the task." + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Name: + description: "Name of the task." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Spec: + $ref: "#/definitions/TaskSpec" + ServiceID: + description: "The ID of the service this task is part of." + type: "string" + Slot: + type: "integer" + NodeID: + description: "The ID of the node that this task is on." + type: "string" + AssignedGenericResources: + $ref: "#/definitions/GenericResources" + Status: + type: "object" + properties: + Timestamp: + type: "string" + format: "dateTime" + State: + $ref: "#/definitions/TaskState" + Message: + type: "string" + Err: + type: "string" + ContainerStatus: + type: "object" + properties: + ContainerID: + type: "string" + PID: + type: "integer" + ExitCode: + type: "integer" + DesiredState: + $ref: "#/definitions/TaskState" + JobIteration: + description: | + If the Service this Task belongs to is a job-mode service, contains + the JobIteration of the Service this Task was created for. Absent if + the Task was created for a Replicated or Global Service. + $ref: "#/definitions/ObjectVersion" + example: + ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + AssignedGenericResources: + - DiscreteResourceSpec: + Kind: "SSD" + Value: 3 + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID1" + - NamedResourceSpec: + Kind: "GPU" + Value: "UUID2" + + ServiceSpec: + description: "User modifiable configuration for a service." + properties: + Name: + description: "Name of the service." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + TaskTemplate: + $ref: "#/definitions/TaskSpec" + Mode: + description: "Scheduling mode for the service." + type: "object" + properties: + Replicated: + type: "object" + properties: + Replicas: + type: "integer" + format: "int64" + Global: + type: "object" + ReplicatedJob: + description: | + The mode used for services with a finite number of tasks that run + to a completed state. + type: "object" + properties: + MaxConcurrent: + description: | + The maximum number of replicas to run simultaneously. + type: "integer" + format: "int64" + default: 1 + TotalCompletions: + description: | + The total number of replicas desired to reach the Completed + state. If unset, will default to the value of `MaxConcurrent` + type: "integer" + format: "int64" + GlobalJob: + description: | + The mode used for services which run a task to the completed state + on each valid node. + type: "object" + UpdateConfig: + description: "Specification for the update strategy of the service." + type: "object" + properties: + Parallelism: + description: | + Maximum number of tasks to be updated in one iteration (0 means + unlimited parallelism). + type: "integer" + format: "int64" + Delay: + description: "Amount of time between updates, in nanoseconds." + type: "integer" + format: "int64" + FailureAction: + description: | + Action to take if an updated task fails to run, or stops running + during the update. + type: "string" + enum: + - "continue" + - "pause" + - "rollback" + Monitor: + description: | + Amount of time to monitor each updated task for failures, in + nanoseconds. + type: "integer" + format: "int64" + MaxFailureRatio: + description: | + The fraction of tasks that may fail during an update before the + failure action is invoked, specified as a floating point number + between 0 and 1. + type: "number" + default: 0 + Order: + description: | + The order of operations when rolling out an updated task. Either + the old task is shut down before the new task is started, or the + new task is started before the old task is shut down. + type: "string" + enum: + - "stop-first" + - "start-first" + RollbackConfig: + description: "Specification for the rollback strategy of the service." + type: "object" + properties: + Parallelism: + description: | + Maximum number of tasks to be rolled back in one iteration (0 means + unlimited parallelism). + type: "integer" + format: "int64" + Delay: + description: | + Amount of time between rollback iterations, in nanoseconds. + type: "integer" + format: "int64" + FailureAction: + description: | + Action to take if an rolled back task fails to run, or stops + running during the rollback. + type: "string" + enum: + - "continue" + - "pause" + Monitor: + description: | + Amount of time to monitor each rolled back task for failures, in + nanoseconds. + type: "integer" + format: "int64" + MaxFailureRatio: + description: | + The fraction of tasks that may fail during a rollback before the + failure action is invoked, specified as a floating point number + between 0 and 1. + type: "number" + default: 0 + Order: + description: | + The order of operations when rolling back a task. Either the old + task is shut down before the new task is started, or the new task + is started before the old task is shut down. + type: "string" + enum: + - "stop-first" + - "start-first" + Networks: + description: "Specifies which networks the service should attach to." + type: "array" + items: + $ref: "#/definitions/NetworkAttachmentConfig" + + EndpointSpec: + $ref: "#/definitions/EndpointSpec" + + EndpointPortConfig: + type: "object" + properties: + Name: + type: "string" + Protocol: + type: "string" + enum: + - "tcp" + - "udp" + - "sctp" + TargetPort: + description: "The port inside the container." + type: "integer" + PublishedPort: + description: "The port on the swarm hosts." + type: "integer" + PublishMode: + description: | + The mode in which port is published. + +


+ + - "ingress" makes the target port accessible on every node, + regardless of whether there is a task for the service running on + that node or not. + - "host" bypasses the routing mesh and publish the port directly on + the swarm node where that service is running. + + type: "string" + enum: + - "ingress" + - "host" + default: "ingress" + example: "ingress" + + EndpointSpec: + description: "Properties that can be configured to access and load balance a service." + type: "object" + properties: + Mode: + description: | + The mode of resolution to use for internal load balancing between tasks. + type: "string" + enum: + - "vip" + - "dnsrr" + default: "vip" + Ports: + description: | + List of exposed ports that this service is accessible on from the + outside. Ports can only be provided if `vip` resolution mode is used. + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + + Service: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ServiceSpec" + Endpoint: + type: "object" + properties: + Spec: + $ref: "#/definitions/EndpointSpec" + Ports: + type: "array" + items: + $ref: "#/definitions/EndpointPortConfig" + VirtualIPs: + type: "array" + items: + type: "object" + properties: + NetworkID: + type: "string" + Addr: + type: "string" + UpdateStatus: + description: "The status of a service update." + type: "object" + properties: + State: + type: "string" + enum: + - "updating" + - "paused" + - "completed" + StartedAt: + type: "string" + format: "dateTime" + CompletedAt: + type: "string" + format: "dateTime" + Message: + type: "string" + ServiceStatus: + description: | + The status of the service's tasks. Provided only when requested as + part of a ServiceList operation. + type: "object" + properties: + RunningTasks: + description: | + The number of tasks for the service currently in the Running state. + type: "integer" + format: "uint64" + example: 7 + DesiredTasks: + description: | + The number of tasks for the service desired to be running. + For replicated services, this is the replica count from the + service spec. For global services, this is computed by taking + count of all tasks for the service with a Desired State other + than Shutdown. + type: "integer" + format: "uint64" + example: 10 + CompletedTasks: + description: | + The number of tasks for a job that are in the Completed state. + This field must be cross-referenced with the service type, as the + value of 0 may mean the service is not in a job mode, or it may + mean the job-mode service has no tasks yet Completed. + type: "integer" + format: "uint64" + JobStatus: + description: | + The status of the service when it is in one of ReplicatedJob or + GlobalJob modes. Absent on Replicated and Global mode services. The + JobIteration is an ObjectVersion, but unlike the Service's version, + does not need to be sent with an update request. + type: "object" + properties: + JobIteration: + description: | + JobIteration is a value increased each time a Job is executed, + successfully or otherwise. "Executed", in this case, means the + job as a whole has been started, not that an individual Task has + been launched. A job is "Executed" when its ServiceSpec is + updated. JobIteration can be used to disambiguate Tasks belonging + to different executions of a job. Though JobIteration will + increase with each subsequent execution, it may not necessarily + increase by 1, and so JobIteration should not be used to + $ref: "#/definitions/ObjectVersion" + LastExecution: + description: | + The last time, as observed by the server, that this job was + started. + type: "string" + format: "dateTime" + example: + ID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Version: + Index: 19 + CreatedAt: "2016-06-07T21:05:51.880065305Z" + UpdatedAt: "2016-06-07T21:07:29.962229872Z" + Spec: + Name: "hopeful_cori" + TaskTemplate: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Endpoint: + Spec: + Mode: "vip" + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + Ports: + - + Protocol: "tcp" + TargetPort: 6379 + PublishedPort: 30001 + VirtualIPs: + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.2/16" + - + NetworkID: "4qvuz4ko70xaltuqbt8956gd1" + Addr: "10.255.0.3/16" + + ImageDeleteResponseItem: + type: "object" + properties: + Untagged: + description: "The image ID of an image that was untagged" + type: "string" + Deleted: + description: "The image ID of an image that was deleted" + type: "string" + + ServiceUpdateResponse: + type: "object" + properties: + Warnings: + description: "Optional warning messages" + type: "array" + items: + type: "string" + example: + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + + ContainerSummary: + type: "array" + items: + type: "object" + properties: + Id: + description: "The ID of this container" + type: "string" + x-go-name: "ID" + Names: + description: "The names that this container has been given" + type: "array" + items: + type: "string" + Image: + description: "The name of the image used when creating this container" + type: "string" + ImageID: + description: "The ID of the image that this container was created from" + type: "string" + Command: + description: "Command to run when starting the container" + type: "string" + Created: + description: "When the container was created" + type: "integer" + format: "int64" + Ports: + description: "The ports exposed by this container" + type: "array" + items: + $ref: "#/definitions/Port" + SizeRw: + description: "The size of files that have been created or changed by this container" + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container" + type: "integer" + format: "int64" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + State: + description: "The state of this container (e.g. `Exited`)" + type: "string" + Status: + description: "Additional human-readable status of this container (e.g. `Exit 0`)" + type: "string" + HostConfig: + type: "object" + properties: + NetworkMode: + type: "string" + NetworkSettings: + description: "A summary of the container's network settings" + type: "object" + properties: + Networks: + type: "object" + additionalProperties: + $ref: "#/definitions/EndpointSettings" + Mounts: + type: "array" + items: + $ref: "#/definitions/Mount" + + Driver: + description: "Driver represents a driver (network, logging, secrets)." + type: "object" + required: [Name] + properties: + Name: + description: "Name of the driver." + type: "string" + x-nullable: false + example: "some-driver" + Options: + description: "Key/value map of driver-specific options." + type: "object" + x-nullable: false + additionalProperties: + type: "string" + example: + OptionA: "value for driver-specific option A" + OptionB: "value for driver-specific option B" + + SecretSpec: + type: "object" + properties: + Name: + description: "User-defined name of the secret." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) + data to store as secret. + + This field is only used to _create_ a secret, and is not returned by + other endpoints. + type: "string" + example: "" + Driver: + description: | + Name of the secrets driver used to fetch the secret's value from an + external secret store. + $ref: "#/definitions/Driver" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Secret: + type: "object" + properties: + ID: + type: "string" + example: "blt1owaxmitz71s9v5zh81zun" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: + type: "string" + format: "dateTime" + example: "2017-07-20T13:55:28.678958722Z" + Spec: + $ref: "#/definitions/SecretSpec" + + ConfigSpec: + type: "object" + properties: + Name: + description: "User-defined name of the config." + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + Data: + description: | + Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) + config data. + type: "string" + Templating: + description: | + Templating driver, if applicable + + Templating controls whether and how to evaluate the config payload as + a template. If no driver is set, no templating is used. + $ref: "#/definitions/Driver" + + Config: + type: "object" + properties: + ID: + type: "string" + Version: + $ref: "#/definitions/ObjectVersion" + CreatedAt: + type: "string" + format: "dateTime" + UpdatedAt: + type: "string" + format: "dateTime" + Spec: + $ref: "#/definitions/ConfigSpec" + + ContainerState: + description: | + ContainerState stores container's running state. It's part of ContainerJSONBase + and will be returned by the "inspect" command. + type: "object" + properties: + Status: + description: | + String representation of the container state. Can be one of "created", + "running", "paused", "restarting", "removing", "exited", or "dead". + type: "string" + enum: ["created", "running", "paused", "restarting", "removing", "exited", "dead"] + example: "running" + Running: + description: | + Whether this container is running. + + Note that a running container can be _paused_. The `Running` and `Paused` + booleans are not mutually exclusive: + + When pausing a container (on Linux), the freezer cgroup is used to suspend + all processes in the container. Freezing the process requires the process to + be running. As a result, paused containers are both `Running` _and_ `Paused`. + + Use the `Status` field instead to determine if a container's state is "running". + type: "boolean" + example: true + Paused: + description: "Whether this container is paused." + type: "boolean" + example: false + Restarting: + description: "Whether this container is restarting." + type: "boolean" + example: false + OOMKilled: + description: | + Whether this container has been killed because it ran out of memory. + type: "boolean" + example: false + Dead: + type: "boolean" + example: false + Pid: + description: "The process ID of this container" + type: "integer" + example: 1234 + ExitCode: + description: "The last exit code of this container" + type: "integer" + example: 0 + Error: + type: "string" + StartedAt: + description: "The time when this container was last started." + type: "string" + example: "2020-01-06T09:06:59.461876391Z" + FinishedAt: + description: "The time when this container last exited." + type: "string" + example: "2020-01-06T09:07:59.461876391Z" + Health: + x-nullable: true + $ref: "#/definitions/Health" + + SystemVersion: + type: "object" + description: | + Response of Engine API: GET "/version" + properties: + Platform: + type: "object" + required: [Name] + properties: + Name: + type: "string" + Components: + type: "array" + description: | + Information about system components + items: + type: "object" + x-go-name: ComponentVersion + required: [Name, Version] + properties: + Name: + description: | + Name of the component + type: "string" + example: "Engine" + Version: + description: | + Version of the component + type: "string" + x-nullable: false + example: "19.03.12" + Details: + description: | + Key/value pairs of strings with additional information about the + component. These values are intended for informational purposes + only, and their content is not defined, and not part of the API + specification. + + These messages can be printed by the client as information to the user. + type: "object" + x-nullable: true + Version: + description: "The version of the daemon" + type: "string" + example: "19.03.12" + ApiVersion: + description: | + The default (and highest) API version that is supported by the daemon + type: "string" + example: "1.40" + MinAPIVersion: + description: | + The minimum API version that is supported by the daemon + type: "string" + example: "1.12" + GitCommit: + description: | + The Git commit of the source code that was used to build the daemon + type: "string" + example: "48a66213fe" + GoVersion: + description: | + The version Go used to compile the daemon, and the version of the Go + runtime in use. + type: "string" + example: "go1.13.14" + Os: + description: | + The operating system that the daemon is running on ("linux" or "windows") + type: "string" + example: "linux" + Arch: + description: | + The architecture that the daemon is running on + type: "string" + example: "amd64" + KernelVersion: + description: | + The kernel version (`uname -r`) that the daemon is running on. + + This field is omitted when empty. + type: "string" + example: "4.19.76-linuxkit" + Experimental: + description: | + Indicates if the daemon is started with experimental features enabled. + + This field is omitted when empty / false. + type: "boolean" + example: true + BuildTime: + description: | + The date and time that the daemon was compiled. + type: "string" + example: "2020-06-22T15:49:27.000000000+00:00" + + + SystemInfo: + type: "object" + properties: + ID: + description: | + Unique identifier of the daemon. + +


+ + > **Note**: The format of the ID itself is not part of the API, and + > should not be considered stable. + type: "string" + example: "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS" + Containers: + description: "Total number of containers on the host." + type: "integer" + example: 14 + ContainersRunning: + description: | + Number of containers with status `"running"`. + type: "integer" + example: 3 + ContainersPaused: + description: | + Number of containers with status `"paused"`. + type: "integer" + example: 1 + ContainersStopped: + description: | + Number of containers with status `"stopped"`. + type: "integer" + example: 10 + Images: + description: | + Total number of images on the host. + + Both _tagged_ and _untagged_ (dangling) images are counted. + type: "integer" + example: 508 + Driver: + description: "Name of the storage driver in use." + type: "string" + example: "overlay2" + DriverStatus: + description: | + Information specific to the storage driver, provided as + "label" / "value" pairs. + + This information is provided by the storage driver, and formatted + in a way consistent with the output of `docker info` on the command + line. + +


+ + > **Note**: The information returned in this field, including the + > formatting of values and labels, should not be considered stable, + > and may change without notice. + type: "array" + items: + type: "array" + items: + type: "string" + example: + - ["Backing Filesystem", "extfs"] + - ["Supports d_type", "true"] + - ["Native Overlay Diff", "true"] + DockerRootDir: + description: | + Root directory of persistent Docker state. + + Defaults to `/var/lib/docker` on Linux, and `C:\ProgramData\docker` + on Windows. + type: "string" + example: "/var/lib/docker" + Plugins: + $ref: "#/definitions/PluginsInfo" + MemoryLimit: + description: "Indicates if the host has memory limit support enabled." + type: "boolean" + example: true + SwapLimit: + description: "Indicates if the host has memory swap limit support enabled." + type: "boolean" + example: true + KernelMemory: + description: | + Indicates if the host has kernel memory limit support enabled. + +


+ + > **Deprecated**: This field is deprecated as the kernel 5.4 deprecated + > `kmem.limit_in_bytes`. + type: "boolean" + example: true + CpuCfsPeriod: + description: | + Indicates if CPU CFS(Completely Fair Scheduler) period is supported by + the host. + type: "boolean" + example: true + CpuCfsQuota: + description: | + Indicates if CPU CFS(Completely Fair Scheduler) quota is supported by + the host. + type: "boolean" + example: true + CPUShares: + description: | + Indicates if CPU Shares limiting is supported by the host. + type: "boolean" + example: true + CPUSet: + description: | + Indicates if CPUsets (cpuset.cpus, cpuset.mems) are supported by the host. + + See [cpuset(7)](https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt) + type: "boolean" + example: true + PidsLimit: + description: "Indicates if the host kernel has PID limit support enabled." + type: "boolean" + example: true + OomKillDisable: + description: "Indicates if OOM killer disable is supported on the host." + type: "boolean" + IPv4Forwarding: + description: "Indicates IPv4 forwarding is enabled." + type: "boolean" + example: true + BridgeNfIptables: + description: "Indicates if `bridge-nf-call-iptables` is available on the host." + type: "boolean" + example: true + BridgeNfIp6tables: + description: "Indicates if `bridge-nf-call-ip6tables` is available on the host." + type: "boolean" + example: true + Debug: + description: | + Indicates if the daemon is running in debug-mode / with debug-level + logging enabled. + type: "boolean" + example: true + NFd: + description: | + The total number of file Descriptors in use by the daemon process. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 64 + NGoroutines: + description: | + The number of goroutines that currently exist. + + This information is only returned if debug-mode is enabled. + type: "integer" + example: 174 + SystemTime: + description: | + Current system-time in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) + format with nano-seconds. + type: "string" + example: "2017-08-08T20:28:29.06202363Z" + LoggingDriver: + description: | + The logging driver to use as a default for new containers. + type: "string" + CgroupDriver: + description: | + The driver to use for managing cgroups. + type: "string" + enum: ["cgroupfs", "systemd", "none"] + default: "cgroupfs" + example: "cgroupfs" + CgroupVersion: + description: | + The version of the cgroup. + type: "string" + enum: ["1", "2"] + default: "1" + example: "1" + NEventsListener: + description: "Number of event listeners subscribed." + type: "integer" + example: 30 + KernelVersion: + description: | + Kernel version of the host. + + On Linux, this information obtained from `uname`. On Windows this + information is queried from the HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ + registry value, for example _"10.0 14393 (14393.1198.amd64fre.rs1_release_sec.170427-1353)"_. + type: "string" + example: "4.9.38-moby" + OperatingSystem: + description: | + Name of the host's operating system, for example: "Ubuntu 16.04.2 LTS" + or "Windows Server 2016 Datacenter" + type: "string" + example: "Alpine Linux v3.5" + OSVersion: + description: | + Version of the host's operating system + +


+ + > **Note**: The information returned in this field, including its + > very existence, and the formatting of values, should not be considered + > stable, and may change without notice. + type: "string" + example: "16.04" + OSType: + description: | + Generic type of the operating system of the host, as returned by the + Go runtime (`GOOS`). + + Currently returned values are "linux" and "windows". A full list of + possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "linux" + Architecture: + description: | + Hardware architecture of the host, as returned by the Go runtime + (`GOARCH`). + + A full list of possible values can be found in the [Go documentation](https://golang.org/doc/install/source#environment). + type: "string" + example: "x86_64" + NCPU: + description: | + The number of logical CPUs usable by the daemon. + + The number of available CPUs is checked by querying the operating + system when the daemon starts. Changes to operating system CPU + allocation after the daemon is started are not reflected. + type: "integer" + example: 4 + MemTotal: + description: | + Total amount of physical memory available on the host, in bytes. + type: "integer" + format: "int64" + example: 2095882240 + + IndexServerAddress: + description: | + Address / URL of the index server that is used for image search, + and as a default for user authentication for Docker Hub and Docker Cloud. + default: "https://index.docker.io/v1/" + type: "string" + example: "https://index.docker.io/v1/" + RegistryConfig: + $ref: "#/definitions/RegistryServiceConfig" + GenericResources: + $ref: "#/definitions/GenericResources" + HttpProxy: + description: | + HTTP-proxy configured for the daemon. This value is obtained from the + [`HTTP_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL + are masked in the API response. + + Containers do not automatically inherit this configuration. + type: "string" + example: "http://xxxxx:xxxxx@proxy.corp.example.com:8080" + HttpsProxy: + description: | + HTTPS-proxy configured for the daemon. This value is obtained from the + [`HTTPS_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) environment variable. + Credentials ([user info component](https://tools.ietf.org/html/rfc3986#section-3.2.1)) in the proxy URL + are masked in the API response. + + Containers do not automatically inherit this configuration. + type: "string" + example: "https://xxxxx:xxxxx@proxy.corp.example.com:4443" + NoProxy: + description: | + Comma-separated list of domain extensions for which no proxy should be + used. This value is obtained from the [`NO_PROXY`](https://www.gnu.org/software/wget/manual/html_node/Proxies.html) + environment variable. + + Containers do not automatically inherit this configuration. + type: "string" + example: "*.local, 169.254/16" + Name: + description: "Hostname of the host." + type: "string" + example: "node5.corp.example.com" + Labels: + description: | + User-defined labels (key/value metadata) as set on the daemon. + +


+ + > **Note**: When part of a Swarm, nodes can both have _daemon_ labels, + > set through the daemon configuration, and _node_ labels, set from a + > manager node in the Swarm. Node labels are not included in this + > field. Node labels can be retrieved using the `/nodes/(id)` endpoint + > on a manager node in the Swarm. + type: "array" + items: + type: "string" + example: ["storage=ssd", "production"] + ExperimentalBuild: + description: | + Indicates if experimental features are enabled on the daemon. + type: "boolean" + example: true + ServerVersion: + description: | + Version string of the daemon. + + > **Note**: the [standalone Swarm API](https://docs.docker.com/swarm/swarm-api/) + > returns the Swarm version instead of the daemon version, for example + > `swarm/1.2.8`. + type: "string" + example: "17.06.0-ce" + ClusterStore: + description: | + URL of the distributed storage backend. + + + The storage backend is used for multihost networking (to store + network and endpoint information) and by the node discovery mechanism. + +


+ + > **Deprecated**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "consul://consul.corp.example.com:8600/some/path" + ClusterAdvertise: + description: | + The network endpoint that the Engine advertises for the purpose of + node discovery. ClusterAdvertise is a `host:port` combination on which + the daemon is reachable by other hosts. + +


+ + > **Deprecated**: This field is only propagated when using standalone Swarm + > mode, and overlay networking using an external k/v store. Overlay + > networks with Swarm mode enabled use the built-in raft store, and + > this field will be empty. + type: "string" + example: "node5.corp.example.com:8000" + Runtimes: + description: | + List of [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtimes configured on the daemon. Keys hold the "name" used to + reference the runtime. + + The Docker daemon relies on an OCI compliant runtime (invoked via the + `containerd` daemon) as its interface to the Linux kernel namespaces, + cgroups, and SELinux. + + The default runtime is `runc`, and automatically configured. Additional + runtimes can be configured by the user and will be listed here. + type: "object" + additionalProperties: + $ref: "#/definitions/Runtime" + default: + runc: + path: "runc" + example: + runc: + path: "runc" + runc-master: + path: "/go/bin/runc" + custom: + path: "/usr/local/bin/my-oci-runtime" + runtimeArgs: ["--debug", "--systemd-cgroup=false"] + DefaultRuntime: + description: | + Name of the default OCI runtime that is used when starting containers. + + The default can be overridden per-container at create time. + type: "string" + default: "runc" + example: "runc" + Swarm: + $ref: "#/definitions/SwarmInfo" + LiveRestoreEnabled: + description: | + Indicates if live restore is enabled. + + If enabled, containers are kept running when the daemon is shutdown + or upon daemon start if running containers are detected. + type: "boolean" + default: false + example: false + Isolation: + description: | + Represents the isolation technology to use as a default for containers. + The supported values are platform-specific. + + If no isolation value is specified on daemon start, on Windows client, + the default is `hyperv`, and on Windows server, the default is `process`. + + This option is currently not used on other platforms. + default: "default" + type: "string" + enum: + - "default" + - "hyperv" + - "process" + InitBinary: + description: | + Name and, optional, path of the `docker-init` binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "docker-init" + ContainerdCommit: + $ref: "#/definitions/Commit" + RuncCommit: + $ref: "#/definitions/Commit" + InitCommit: + $ref: "#/definitions/Commit" + SecurityOptions: + description: | + List of security features that are enabled on the daemon, such as + apparmor, seccomp, SELinux, user-namespaces (userns), and rootless. + + Additional configuration options for each security feature may + be present, and are included as a comma-separated list of key/value + pairs. + type: "array" + items: + type: "string" + example: + - "name=apparmor" + - "name=seccomp,profile=default" + - "name=selinux" + - "name=userns" + - "name=rootless" + ProductLicense: + description: | + Reports a summary of the product license on the daemon. + + If a commercial license has been applied to the daemon, information + such as number of nodes, and expiration are included. + type: "string" + example: "Community Engine" + DefaultAddressPools: + description: | + List of custom default address pools for local networks, which can be + specified in the daemon.json file or dockerd option. + + Example: a Base "10.10.0.0/16" with Size 24 will define the set of 256 + 10.10.[0-255].0/24 address pools. + type: "array" + items: + type: "object" + properties: + Base: + description: "The network address in CIDR format" + type: "string" + example: "10.10.0.0/16" + Size: + description: "The network pool size" + type: "integer" + example: "24" + Warnings: + description: | + List of warnings / informational messages about missing features, or + issues related to the daemon configuration. + + These messages can be printed by the client as information to the user. + type: "array" + items: + type: "string" + example: + - "WARNING: No memory limit support" + - "WARNING: bridge-nf-call-iptables is disabled" + - "WARNING: bridge-nf-call-ip6tables is disabled" + + + # PluginsInfo is a temp struct holding Plugins name + # registered with docker daemon. It is used by Info struct + PluginsInfo: + description: | + Available plugins per type. + +


+ + > **Note**: Only unmanaged (V1) plugins are included in this list. + > V1 plugins are "lazily" loaded, and are not returned in this list + > if there is no resource using the plugin. + type: "object" + properties: + Volume: + description: "Names of available volume-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["local"] + Network: + description: "Names of available network-drivers, and network-driver plugins." + type: "array" + items: + type: "string" + example: ["bridge", "host", "ipvlan", "macvlan", "null", "overlay"] + Authorization: + description: "Names of available authorization plugins." + type: "array" + items: + type: "string" + example: ["img-authz-plugin", "hbm"] + Log: + description: "Names of available logging-drivers, and logging-driver plugins." + type: "array" + items: + type: "string" + example: ["awslogs", "fluentd", "gcplogs", "gelf", "journald", "json-file", "logentries", "splunk", "syslog"] + + + RegistryServiceConfig: + description: | + RegistryServiceConfig stores daemon registry services configuration. + type: "object" + x-nullable: true + properties: + AllowNondistributableArtifactsCIDRs: + description: | + List of IP ranges to which nondistributable artifacts can be pushed, + using the CIDR syntax [RFC 4632](https://tools.ietf.org/html/4632). + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior, and enables the daemon to + push nondistributable artifacts to all registries whose resolved IP + address is within the subnet described by the CIDR syntax. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + AllowNondistributableArtifactsHostnames: + description: | + List of registry hostnames to which nondistributable artifacts can be + pushed, using the format `[:]` or `[:]`. + + Some images (for example, Windows base images) contain artifacts + whose distribution is restricted by license. When these images are + pushed to a registry, restricted artifacts are not included. + + This configuration override this behavior for the specified + registries. + + This option is useful when pushing images containing + nondistributable artifacts to a registry on an air-gapped network so + hosts on that network can pull the images without connecting to + another server. + + > **Warning**: Nondistributable artifacts typically have restrictions + > on how and where they can be distributed and shared. Only use this + > feature to push artifacts to private registries and ensure that you + > are in compliance with any terms that cover redistributing + > nondistributable artifacts. + type: "array" + items: + type: "string" + example: ["registry.internal.corp.example.com:3000", "[2001:db8:a0b:12f0::1]:443"] + InsecureRegistryCIDRs: + description: | + List of IP ranges of insecure registries, using the CIDR syntax + ([RFC 4632](https://tools.ietf.org/html/4632)). Insecure registries + accept un-encrypted (HTTP) and/or untrusted (HTTPS with certificates + from unknown CAs) communication. + + By default, local registries (`127.0.0.0/8`) are configured as + insecure. All other registries are secure. Communicating with an + insecure registry is not possible if the daemon assumes that registry + is secure. + + This configuration override this behavior, insecure communication with + registries whose resolved IP address is within the subnet described by + the CIDR syntax. + + Registries can also be marked insecure by hostname. Those registries + are listed under `IndexConfigs` and have their `Secure` field set to + `false`. + + > **Warning**: Using this option can be useful when running a local + > registry, but introduces security vulnerabilities. This option + > should therefore ONLY be used for testing purposes. For increased + > security, users should add their CA to their system's list of trusted + > CAs instead of enabling this option. + type: "array" + items: + type: "string" + example: ["::1/128", "127.0.0.0/8"] + IndexConfigs: + type: "object" + additionalProperties: + $ref: "#/definitions/IndexInfo" + example: + "127.0.0.1:5000": + "Name": "127.0.0.1:5000" + "Mirrors": [] + "Secure": false + "Official": false + "[2001:db8:a0b:12f0::1]:80": + "Name": "[2001:db8:a0b:12f0::1]:80" + "Mirrors": [] + "Secure": false + "Official": false + "docker.io": + Name: "docker.io" + Mirrors: ["https://hub-mirror.corp.example.com:5000/"] + Secure: true + Official: true + "registry.internal.corp.example.com:3000": + Name: "registry.internal.corp.example.com:3000" + Mirrors: [] + Secure: false + Official: false + Mirrors: + description: | + List of registry URLs that act as a mirror for the official + (`docker.io`) registry. + + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://[2001:db8:a0b:12f0::1]/" + + IndexInfo: + description: + IndexInfo contains information about a registry. + type: "object" + x-nullable: true + properties: + Name: + description: | + Name of the registry, such as "docker.io". + type: "string" + example: "docker.io" + Mirrors: + description: | + List of mirrors, expressed as URIs. + type: "array" + items: + type: "string" + example: + - "https://hub-mirror.corp.example.com:5000/" + - "https://registry-2.docker.io/" + - "https://registry-3.docker.io/" + Secure: + description: | + Indicates if the registry is part of the list of insecure + registries. + + If `false`, the registry is insecure. Insecure registries accept + un-encrypted (HTTP) and/or untrusted (HTTPS with certificates from + unknown CAs) communication. + + > **Warning**: Insecure registries can be useful when running a local + > registry. However, because its use creates security vulnerabilities + > it should ONLY be enabled for testing purposes. For increased + > security, users should add their CA to their system's list of + > trusted CAs instead of enabling this option. + type: "boolean" + example: true + Official: + description: | + Indicates whether this is an official registry (i.e., Docker Hub / docker.io) + type: "boolean" + example: true + + Runtime: + description: | + Runtime describes an [OCI compliant](https://github.com/opencontainers/runtime-spec) + runtime. + + The runtime is invoked by the daemon via the `containerd` daemon. OCI + runtimes act as an interface to the Linux kernel namespaces, cgroups, + and SELinux. + type: "object" + properties: + path: + description: | + Name and, optional, path, of the OCI executable binary. + + If the path is omitted, the daemon searches the host's `$PATH` for the + binary and uses the first result. + type: "string" + example: "/usr/local/bin/my-oci-runtime" + runtimeArgs: + description: | + List of command-line arguments to pass to the runtime when invoked. + type: "array" + x-nullable: true + items: + type: "string" + example: ["--debug", "--systemd-cgroup=false"] + + Commit: + description: | + Commit holds the Git-commit (SHA1) that a binary was built from, as + reported in the version-string of external tools, such as `containerd`, + or `runC`. + type: "object" + properties: + ID: + description: "Actual commit ID of external tool." + type: "string" + example: "cfb82a876ecc11b5ca0977d1733adbe58599088a" + Expected: + description: | + Commit ID of external tool expected by dockerd as set at build time. + type: "string" + example: "2d41c047c83e09a6d61d464906feb2a2f3c52aa4" + + SwarmInfo: + description: | + Represents generic information about swarm. + type: "object" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + default: "" + example: "k67qz4598weg5unwwffg6z1m1" + NodeAddr: + description: | + IP address at which this node can be reached by other nodes in the + swarm. + type: "string" + default: "" + example: "10.0.0.46" + LocalNodeState: + $ref: "#/definitions/LocalNodeState" + ControlAvailable: + type: "boolean" + default: false + example: true + Error: + type: "string" + default: "" + RemoteManagers: + description: | + List of ID's and addresses of other managers in the swarm. + type: "array" + default: null + x-nullable: true + items: + $ref: "#/definitions/PeerNode" + example: + - NodeID: "71izy0goik036k48jg985xnds" + Addr: "10.0.0.158:2377" + - NodeID: "79y6h1o4gv8n120drcprv5nmc" + Addr: "10.0.0.159:2377" + - NodeID: "k67qz4598weg5unwwffg6z1m1" + Addr: "10.0.0.46:2377" + Nodes: + description: "Total number of nodes in the swarm." + type: "integer" + x-nullable: true + example: 4 + Managers: + description: "Total number of managers in the swarm." + type: "integer" + x-nullable: true + example: 3 + Cluster: + $ref: "#/definitions/ClusterInfo" + + LocalNodeState: + description: "Current local status of this node." + type: "string" + default: "" + enum: + - "" + - "inactive" + - "pending" + - "active" + - "error" + - "locked" + example: "active" + + PeerNode: + description: "Represents a peer-node in the swarm" + properties: + NodeID: + description: "Unique identifier of for this node in the swarm." + type: "string" + Addr: + description: | + IP address and ports at which this node can be reached. + type: "string" + + NetworkAttachmentConfig: + description: | + Specifies how a service should be attached to a particular network. + type: "object" + properties: + Target: + description: | + The target network for attachment. Must be a network name or ID. + type: "string" + Aliases: + description: | + Discoverable alternate names for the service on this network. + type: "array" + items: + type: "string" + DriverOpts: + description: | + Driver attachment options for the network target. + type: "object" + additionalProperties: + type: "string" + +paths: + /containers/json: + get: + summary: "List containers" + description: | + Returns a list of containers. For details on the format, see the + [inspect endpoint](#operation/ContainerInspect). + + Note that it uses a different, smaller representation of a container + than inspecting a single container. For example, the list of linked + containers is not propagated . + operationId: "ContainerList" + produces: + - "application/json" + parameters: + - name: "all" + in: "query" + description: | + Return all containers. By default, only running containers are shown. + type: "boolean" + default: false + - name: "limit" + in: "query" + description: | + Return this number of most recently created containers, including + non-running ones. + type: "integer" + - name: "size" + in: "query" + description: | + Return the size of container as fields `SizeRw` and `SizeRootFs`. + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + Filters to process on the container list, encoded as JSON (a + `map[string][]string`). For example, `{"status": ["paused"]}` will + only return paused containers. + + Available filters: + + - `ancestor`=(`[:]`, ``, or ``) + - `before`=(`` or ``) + - `expose`=(`[/]`|`/[]`) + - `exited=` containers with exit code of `` + - `health`=(`starting`|`healthy`|`unhealthy`|`none`) + - `id=` a container's ID + - `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) + - `is-task=`(`true`|`false`) + - `label=key` or `label="key=value"` of a container label + - `name=` a container's name + - `network`=(`` or ``) + - `publish`=(`[/]`|`/[]`) + - `since`=(`` or ``) + - `status=`(`created`|`restarting`|`running`|`removing`|`paused`|`exited`|`dead`) + - `volume`=(`` or ``) + type: "string" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ContainerSummary" + examples: + application/json: + - Id: "8dfafdbc3a40" + Names: + - "/boring_feynman" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 1" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: + - PrivatePort: 2222 + PublicPort: 3333 + Type: "tcp" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "2cdc4edb1ded3631c81f57966563e5c8525b81121bb3706a9a9a3ae102711f3f" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:02" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + - Id: "9cd87474be90" + Names: + - "/coolName" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 222222" + Created: 1367854155 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "88eaed7b37b38c2a3f0c4bc796494fdf51b270c2d22656412a2ca5d559a64d7a" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.8" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:08" + Mounts: [] + - Id: "3176a2479c92" + Names: + - "/sleepy_dog" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 3333333333333333" + Created: 1367854154 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "8b27c041c30326d59cd6e6f510d4f8d1d570a228466f956edf7815508f78e30d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.6" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:06" + Mounts: [] + - Id: "4cb07b47f9fb" + Names: + - "/running_cat" + Image: "ubuntu:latest" + ImageID: "d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82" + Command: "echo 444444444444444444444444444444444" + Created: 1367854152 + State: "Exited" + Status: "Exit 0" + Ports: [] + Labels: {} + SizeRw: 12288 + SizeRootFs: 0 + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "d91c7b2f0644403d7ef3095985ea0e2370325cd2332ff3a3225c4247328e66e9" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.5" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:11:00:05" + Mounts: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/create: + post: + summary: "Create a container" + operationId: "ContainerCreate" + consumes: + - "application/json" + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "name" + in: "query" + description: | + Assign the specified name to the container. Must match + `/?[a-zA-Z0-9][a-zA-Z0-9_.-]+`. + type: "string" + pattern: "^/?[a-zA-Z0-9][a-zA-Z0-9_.-]+$" + - name: "body" + in: "body" + description: "Container to create" + schema: + allOf: + - $ref: "#/definitions/ContainerConfig" + - type: "object" + properties: + HostConfig: + $ref: "#/definitions/HostConfig" + NetworkingConfig: + $ref: "#/definitions/NetworkingConfig" + example: + Hostname: "" + Domainname: "" + User: "" + AttachStdin: false + AttachStdout: true + AttachStderr: true + Tty: false + OpenStdin: false + StdinOnce: false + Env: + - "FOO=bar" + - "BAZ=quux" + Cmd: + - "date" + Entrypoint: "" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + Volumes: + /volumes/data: {} + WorkingDir: "" + NetworkDisabled: false + MacAddress: "12:34:56:78:9a:bc" + ExposedPorts: + 22/tcp: {} + StopSignal: "SIGTERM" + StopTimeout: 10 + HostConfig: + Binds: + - "/tmp:/tmp" + Links: + - "redis3:redis" + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + NanoCpus: 500000 + CpuPercent: 80 + CpuShares: 512 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpuQuota: 50000 + CpusetCpus: "0,1" + CpusetMems: "0,1" + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 300 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceWriteIOps: + - {} + DeviceRequests: + - Driver: "nvidia" + Count: -1 + DeviceIDs": ["0", "1", "GPU-fef8089b-4820-abfc-e83e-94318197576e"] + Capabilities: [["gpu", "nvidia", "compute"]] + Options: + property1: "string" + property2: "string" + MemorySwappiness: 60 + OomKillDisable: false + OomScoreAdj: 500 + PidMode: "" + PidsLimit: 0 + PortBindings: + 22/tcp: + - HostPort: "11022" + PublishAllPorts: false + Privileged: false + ReadonlyRootfs: false + Dns: + - "8.8.8.8" + DnsOptions: + - "" + DnsSearch: + - "" + VolumesFrom: + - "parent" + - "other:ro" + CapAdd: + - "NET_ADMIN" + CapDrop: + - "MKNOD" + GroupAdd: + - "newgroup" + RestartPolicy: + Name: "" + MaximumRetryCount: 0 + AutoRemove: true + NetworkMode: "bridge" + Devices: [] + Ulimits: + - {} + LogConfig: + Type: "json-file" + Config: {} + SecurityOpt: [] + StorageOpt: {} + CgroupParent: "" + VolumeDriver: "" + ShmSize: 67108864 + NetworkingConfig: + EndpointsConfig: + isolated_nw: + IPAMConfig: + IPv4Address: "172.20.30.33" + IPv6Address: "2001:db8:abcd::3033" + LinkLocalIPs: + - "169.254.34.68" + - "fe80::3468" + Links: + - "container_1" + - "container_2" + Aliases: + - "server_x" + - "server_y" + + required: true + responses: + 201: + description: "Container created successfully" + schema: + type: "object" + title: "ContainerCreateResponse" + description: "OK response to ContainerCreate operation" + required: [Id, Warnings] + properties: + Id: + description: "The ID of the created container" + type: "string" + x-nullable: false + Warnings: + description: "Warnings encountered when creating the container" + type: "array" + x-nullable: false + items: + type: "string" + examples: + application/json: + Id: "e90e34656806" + Warnings: [] + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /containers/{id}/json: + get: + summary: "Inspect a container" + description: "Return low-level information about a container." + operationId: "ContainerInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerInspectResponse" + properties: + Id: + description: "The ID of the container" + type: "string" + Created: + description: "The time the container was created" + type: "string" + Path: + description: "The path to the command being run" + type: "string" + Args: + description: "The arguments to the command being run" + type: "array" + items: + type: "string" + State: + x-nullable: true + $ref: "#/definitions/ContainerState" + Image: + description: "The container's image ID" + type: "string" + ResolvConfPath: + type: "string" + HostnamePath: + type: "string" + HostsPath: + type: "string" + LogPath: + type: "string" + Name: + type: "string" + RestartCount: + type: "integer" + Driver: + type: "string" + Platform: + type: "string" + MountLabel: + type: "string" + ProcessLabel: + type: "string" + AppArmorProfile: + type: "string" + ExecIDs: + description: "IDs of exec instances that are running in the container." + type: "array" + items: + type: "string" + x-nullable: true + HostConfig: + $ref: "#/definitions/HostConfig" + GraphDriver: + $ref: "#/definitions/GraphDriverData" + SizeRw: + description: | + The size of files that have been created or changed by this + container. + type: "integer" + format: "int64" + SizeRootFs: + description: "The total size of all the files in this container." + type: "integer" + format: "int64" + Mounts: + type: "array" + items: + $ref: "#/definitions/MountPoint" + Config: + $ref: "#/definitions/ContainerConfig" + NetworkSettings: + $ref: "#/definitions/NetworkSettings" + examples: + application/json: + AppArmorProfile: "" + Args: + - "-c" + - "exit 9" + Config: + AttachStderr: true + AttachStdin: false + AttachStdout: true + Cmd: + - "/bin/sh" + - "-c" + - "exit 9" + Domainname: "" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Healthcheck: + Test: ["CMD-SHELL", "exit 0"] + Hostname: "ba033ac44011" + Image: "ubuntu" + Labels: + com.example.vendor: "Acme" + com.example.license: "GPL" + com.example.version: "1.0" + MacAddress: "" + NetworkDisabled: false + OpenStdin: false + StdinOnce: false + Tty: false + User: "" + Volumes: + /volumes/data: {} + WorkingDir: "" + StopSignal: "SIGTERM" + StopTimeout: 10 + Created: "2015-01-06T15:47:31.485331387Z" + Driver: "devicemapper" + ExecIDs: + - "b35395de42bc8abd327f9dd65d913b9ba28c74d2f0734eeeae84fa1c616a0fca" + - "3fc1232e5cd20c8de182ed81178503dc6437f4e7ef12b52cc5e8de020652f1c4" + HostConfig: + MaximumIOps: 0 + MaximumIOBps: 0 + BlkioWeight: 0 + BlkioWeightDevice: + - {} + BlkioDeviceReadBps: + - {} + BlkioDeviceWriteBps: + - {} + BlkioDeviceReadIOps: + - {} + BlkioDeviceWriteIOps: + - {} + ContainerIDFile: "" + CpusetCpus: "" + CpusetMems: "" + CpuPercent: 80 + CpuShares: 0 + CpuPeriod: 100000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + Devices: [] + DeviceRequests: + - Driver: "nvidia" + Count: -1 + DeviceIDs": ["0", "1", "GPU-fef8089b-4820-abfc-e83e-94318197576e"] + Capabilities: [["gpu", "nvidia", "compute"]] + Options: + property1: "string" + property2: "string" + IpcMode: "" + LxcConf: [] + Memory: 0 + MemorySwap: 0 + MemoryReservation: 0 + KernelMemory: 0 + OomKillDisable: false + OomScoreAdj: 500 + NetworkMode: "bridge" + PidMode: "" + PortBindings: {} + Privileged: false + ReadonlyRootfs: false + PublishAllPorts: false + RestartPolicy: + MaximumRetryCount: 2 + Name: "on-failure" + LogConfig: + Type: "json-file" + Sysctls: + net.ipv4.ip_forward: "1" + Ulimits: + - {} + VolumeDriver: "" + ShmSize: 67108864 + HostnamePath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hostname" + HostsPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/hosts" + LogPath: "/var/lib/docker/containers/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b/1eb5fabf5a03807136561b3c00adcd2992b535d624d5e18b6cdc6a6844d9767b-json.log" + Id: "ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39" + Image: "04c5d3b7b0656168630d3ba35d8889bd0e9caafcaeb3004d2bfbc47e7c5d35d2" + MountLabel: "" + Name: "/boring_euclid" + NetworkSettings: + Bridge: "" + SandboxID: "" + HairpinMode: false + LinkLocalIPv6Address: "" + LinkLocalIPv6PrefixLen: 0 + SandboxKey: "" + EndpointID: "" + Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + IPAddress: "" + IPPrefixLen: 0 + IPv6Gateway: "" + MacAddress: "" + Networks: + bridge: + NetworkID: "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812" + EndpointID: "7587b82f0dada3656fda26588aee72630c6fab1536d36e394b2bfbcf898c971d" + Gateway: "172.17.0.1" + IPAddress: "172.17.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Path: "/bin/sh" + ProcessLabel: "" + ResolvConfPath: "/var/lib/docker/containers/ba033ac4401106a3b513bc9d639eee123ad78ca3616b921167cd74b20e25ed39/resolv.conf" + RestartCount: 1 + State: + Error: "" + ExitCode: 9 + FinishedAt: "2015-01-06T15:47:32.080254511Z" + Health: + Status: "healthy" + FailingStreak: 0 + Log: + - Start: "2019-12-22T10:59:05.6385933Z" + End: "2019-12-22T10:59:05.8078452Z" + ExitCode: 0 + Output: "" + OOMKilled: false + Dead: false + Paused: false + Pid: 0 + Restarting: false + Running: true + StartedAt: "2015-01-06T15:47:32.072697474Z" + Status: "running" + Mounts: + - Name: "fac362...80535" + Source: "/data" + Destination: "/data" + Driver: "local" + Mode: "ro,Z" + RW: false + Propagation: "" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "size" + in: "query" + type: "boolean" + default: false + description: "Return the size of container as fields `SizeRw` and `SizeRootFs`" + tags: ["Container"] + /containers/{id}/top: + get: + summary: "List processes running inside a container" + description: | + On Unix systems, this is done by running the `ps` command. This endpoint + is not supported on Windows. + operationId: "ContainerTop" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "ContainerTopResponse" + description: "OK response to ContainerTop operation" + properties: + Titles: + description: "The ps column titles" + type: "array" + items: + type: "string" + Processes: + description: | + Each process running in the container, where each is process + is an array of values corresponding to the titles. + type: "array" + items: + type: "array" + items: + type: "string" + examples: + application/json: + Titles: + - "UID" + - "PID" + - "PPID" + - "C" + - "STIME" + - "TTY" + - "TIME" + - "CMD" + Processes: + - + - "root" + - "13642" + - "882" + - "0" + - "17:03" + - "pts/0" + - "00:00:00" + - "/bin/bash" + - + - "root" + - "13735" + - "13642" + - "0" + - "17:06" + - "pts/0" + - "00:00:00" + - "sleep 10" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "ps_args" + in: "query" + description: "The arguments to pass to `ps`. For example, `aux`" + type: "string" + default: "-ef" + tags: ["Container"] + /containers/{id}/logs: + get: + summary: "Get container logs" + description: | + Get `stdout` and `stderr` logs from a container. + + Note: This endpoint works only for containers with the `json-file` or + `journald` logging driver. + operationId: "ContainerLogs" + responses: + 200: + description: | + logs returned as a stream in response body. + For the stream format, [see the documentation for the attach endpoint](#operation/ContainerAttach). + Note that unlike the attach endpoint, the logs endpoint does not + upgrade the connection and does not set Content-Type. + schema: + type: "string" + format: "binary" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "follow" + in: "query" + description: "Keep connection after returning logs." + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "until" + in: "query" + description: "Only return logs before this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Container"] + /containers/{id}/changes: + get: + summary: "Get changes on a container’s filesystem" + description: | + Returns which files in a container's filesystem have been added, deleted, + or modified. The `Kind` of modification can be one of: + + - `0`: Modified + - `1`: Added + - `2`: Deleted + operationId: "ContainerChanges" + produces: ["application/json"] + responses: + 200: + description: "The list of changes" + schema: + type: "array" + items: + type: "object" + x-go-name: "ContainerChangeResponseItem" + title: "ContainerChangeResponseItem" + description: "change item in response to ContainerChanges operation" + required: [Path, Kind] + properties: + Path: + description: "Path to file that has changed" + type: "string" + x-nullable: false + Kind: + description: "Kind of change" + type: "integer" + format: "uint8" + enum: [0, 1, 2] + x-nullable: false + examples: + application/json: + - Path: "/dev" + Kind: 0 + - Path: "/dev/kmsg" + Kind: 1 + - Path: "/test" + Kind: 1 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/export: + get: + summary: "Export a container" + description: "Export the contents of a container as a tarball." + operationId: "ContainerExport" + produces: + - "application/octet-stream" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/stats: + get: + summary: "Get container stats based on resource usage" + description: | + This endpoint returns a live stream of a container’s resource usage + statistics. + + The `precpu_stats` is the CPU statistic of the *previous* read, and is + used to calculate the CPU usage percentage. It is not an exact copy + of the `cpu_stats` field. + + If either `precpu_stats.online_cpus` or `cpu_stats.online_cpus` is + nil then for compatibility with older daemons the length of the + corresponding `cpu_usage.percpu_usage` array should be used. + + On a cgroup v2 host, the following fields are not set + * `blkio_stats`: all fields other than `io_service_bytes_recursive` + * `cpu_stats`: `cpu_usage.percpu_usage` + * `memory_stats`: `max_usage` and `failcnt` + Also, `memory_stats.stats` fields are incompatible with cgroup v1. + + To calculate the values shown by the `stats` command of the docker cli tool + the following formulas can be used: + * used_memory = `memory_stats.usage - memory_stats.stats.cache` + * available_memory = `memory_stats.limit` + * Memory usage % = `(used_memory / available_memory) * 100.0` + * cpu_delta = `cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage` + * system_cpu_delta = `cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage` + * number_cpus = `lenght(cpu_stats.cpu_usage.percpu_usage)` or `cpu_stats.online_cpus` + * CPU usage % = `(cpu_delta / system_cpu_delta) * number_cpus * 100.0` + operationId: "ContainerStats" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + type: "object" + examples: + application/json: + read: "2015-01-08T22:57:31.547920715Z" + pids_stats: + current: 3 + networks: + eth0: + rx_bytes: 5338 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 36 + tx_bytes: 648 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 8 + eth5: + rx_bytes: 4641 + rx_dropped: 0 + rx_errors: 0 + rx_packets: 26 + tx_bytes: 690 + tx_dropped: 0 + tx_errors: 0 + tx_packets: 9 + memory_stats: + stats: + total_pgmajfault: 0 + cache: 0 + mapped_file: 0 + total_inactive_file: 0 + pgpgout: 414 + rss: 6537216 + total_mapped_file: 0 + writeback: 0 + unevictable: 0 + pgpgin: 477 + total_unevictable: 0 + pgmajfault: 0 + total_rss: 6537216 + total_rss_huge: 6291456 + total_writeback: 0 + total_inactive_anon: 0 + rss_huge: 6291456 + hierarchical_memory_limit: 67108864 + total_pgfault: 964 + total_active_file: 0 + active_anon: 6537216 + total_active_anon: 6537216 + total_pgpgout: 414 + total_cache: 0 + inactive_anon: 0 + active_file: 0 + pgfault: 964 + inactive_file: 0 + total_pgpgin: 477 + max_usage: 6651904 + usage: 6537216 + failcnt: 0 + limit: 67108864 + blkio_stats: {} + cpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24472255 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100215355 + usage_in_kernelmode: 30000000 + system_cpu_usage: 739306590000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + precpu_stats: + cpu_usage: + percpu_usage: + - 8646879 + - 24350896 + - 36438778 + - 30657443 + usage_in_usermode: 50000000 + total_usage: 100093996 + usage_in_kernelmode: 30000000 + system_cpu_usage: 9492140000000 + online_cpus: 4 + throttling_data: + periods: 0 + throttled_periods: 0 + throttled_time: 0 + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "stream" + in: "query" + description: | + Stream the output. If false, the stats will be output once and then + it will disconnect. + type: "boolean" + default: true + - name: "one-shot" + in: "query" + description: | + Only get a single stat instead of waiting for 2 cycles. Must be used + with `stream=false`. + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/resize: + post: + summary: "Resize a container TTY" + description: "Resize the TTY for a container." + operationId: "ContainerResize" + consumes: + - "application/octet-stream" + produces: + - "text/plain" + responses: + 200: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "cannot resize container" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Container"] + /containers/{id}/start: + post: + summary: "Start a container" + operationId: "ContainerStart" + responses: + 204: + description: "no error" + 304: + description: "container already started" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container. Format is a + single character `[a-Z]` or `ctrl-` where `` is one + of: `a-z`, `@`, `^`, `[`, `,` or `_`. + type: "string" + tags: ["Container"] + /containers/{id}/stop: + post: + summary: "Stop a container" + operationId: "ContainerStop" + responses: + 204: + description: "no error" + 304: + description: "container already stopped" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/restart: + post: + summary: "Restart a container" + operationId: "ContainerRestart" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "t" + in: "query" + description: "Number of seconds to wait before killing the container" + type: "integer" + tags: ["Container"] + /containers/{id}/kill: + post: + summary: "Kill a container" + description: | + Send a POSIX signal to a container, defaulting to killing to the + container. + operationId: "ContainerKill" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is not running" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "Container d37cde0fe4ad63c3a7252023b2f9800282894247d145cb5933ddf6e52cc03a28 is not running" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "signal" + in: "query" + description: "Signal to send to the container as an integer or string (e.g. `SIGINT`)" + type: "string" + default: "SIGKILL" + tags: ["Container"] + /containers/{id}/update: + post: + summary: "Update a container" + description: | + Change various configuration options of a container without having to + recreate it. + operationId: "ContainerUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "The container has been updated." + schema: + type: "object" + title: "ContainerUpdateResponse" + description: "OK response to ContainerUpdate operation" + properties: + Warnings: + type: "array" + items: + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "update" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/Resources" + - type: "object" + properties: + RestartPolicy: + $ref: "#/definitions/RestartPolicy" + example: + BlkioWeight: 300 + CpuShares: 512 + CpuPeriod: 100000 + CpuQuota: 50000 + CpuRealtimePeriod: 1000000 + CpuRealtimeRuntime: 10000 + CpusetCpus: "0,1" + CpusetMems: "0" + Memory: 314572800 + MemorySwap: 514288000 + MemoryReservation: 209715200 + KernelMemory: 52428800 + RestartPolicy: + MaximumRetryCount: 4 + Name: "on-failure" + tags: ["Container"] + /containers/{id}/rename: + post: + summary: "Rename a container" + operationId: "ContainerRename" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "name already in use" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "name" + in: "query" + required: true + description: "New name for the container" + type: "string" + tags: ["Container"] + /containers/{id}/pause: + post: + summary: "Pause a container" + description: | + Use the freezer cgroup to suspend all processes in a container. + + Traditionally, when suspending a process the `SIGSTOP` signal is used, + which is observable by the process being suspended. With the freezer + cgroup the process is unaware, and unable to capture, that it is being + suspended, and subsequently resumed. + operationId: "ContainerPause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/unpause: + post: + summary: "Unpause a container" + description: "Resume a container which has been paused." + operationId: "ContainerUnpause" + responses: + 204: + description: "no error" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + tags: ["Container"] + /containers/{id}/attach: + post: + summary: "Attach to a container" + description: | + Attach to a container to read its output or send it input. You can attach + to the same container multiple times and you can reattach to containers + that have been detached. + + Either the `stream` or `logs` parameter must be `true` for this endpoint + to do anything. + + See the [documentation for the `docker attach` command](https://docs.docker.com/engine/reference/commandline/attach/) + for more details. + + ### Hijacking + + This endpoint hijacks the HTTP connection to transport `stdin`, `stdout`, + and `stderr` on the same socket. + + This is the response from the daemon for an attach request: + + ``` + HTTP/1.1 200 OK + Content-Type: application/vnd.docker.raw-stream + + [STREAM] + ``` + + After the headers and two new lines, the TCP connection can now be used + for raw, bidirectional communication between the client and server. + + To hint potential proxies about connection hijacking, the Docker client + can also optionally send connection upgrade headers. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /containers/16253994b7c4/attach?stream=1&stdout=1 HTTP/1.1 + Upgrade: tcp + Connection: Upgrade + ``` + + The Docker daemon will respond with a `101 UPGRADED` response, and will + similarly follow with the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Content-Type: application/vnd.docker.raw-stream + Connection: Upgrade + Upgrade: tcp + + [STREAM] + ``` + + ### Stream format + + When the TTY setting is disabled in [`POST /containers/create`](#operation/ContainerCreate), + the stream over the hijacked connected is multiplexed to separate out + `stdout` and `stderr`. The stream consists of a series of frames, each + containing a header and a payload. + + The header contains the information which the stream writes (`stdout` or + `stderr`). It also contains the size of the associated frame encoded in + the last four bytes (`uint32`). + + It is encoded on the first eight bytes like this: + + ```go + header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4} + ``` + + `STREAM_TYPE` can be: + + - 0: `stdin` (is written on `stdout`) + - 1: `stdout` + - 2: `stderr` + + `SIZE1, SIZE2, SIZE3, SIZE4` are the four bytes of the `uint32` size + encoded as big endian. + + Following the header is the payload, which is the specified number of + bytes of `STREAM_TYPE`. + + The simplest way to implement this protocol is the following: + + 1. Read 8 bytes. + 2. Choose `stdout` or `stderr` depending on the first byte. + 3. Extract the frame size from the last four bytes. + 4. Read the extracted size and output it on the correct output. + 5. Goto 1. + + ### Stream format when using a TTY + + When the TTY setting is enabled in [`POST /containers/create`](#operation/ContainerCreate), + the stream is not multiplexed. The data exchanged over the hijacked + connection is simply the raw data from the process PTY and client's + `stdin`. + + operationId: "ContainerAttach" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container.Format is a single + character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + `@`, `^`, `[`, `,` or `_`. + type: "string" + - name: "logs" + in: "query" + description: | + Replay previous logs from the container. + + This is useful for attaching to a container that has started and you + want to output everything since the container started. + + If `stream` is also enabled, once all the previous output has been + returned, it will seamlessly transition into streaming current + output. + type: "boolean" + default: false + - name: "stream" + in: "query" + description: | + Stream attached streams from the time the request was made onwards. + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/attach/ws: + get: + summary: "Attach to a container via a websocket" + operationId: "ContainerAttachWebsocket" + responses: + 101: + description: "no error, hints proxy about hijacking" + 200: + description: "no error, no upgrade header found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "detachKeys" + in: "query" + description: | + Override the key sequence for detaching a container.Format is a single + character `[a-Z]` or `ctrl-` where `` is one of: `a-z`, + `@`, `^`, `[`, `,`, or `_`. + type: "string" + - name: "logs" + in: "query" + description: "Return logs" + type: "boolean" + default: false + - name: "stream" + in: "query" + description: "Return stream" + type: "boolean" + default: false + - name: "stdin" + in: "query" + description: "Attach to `stdin`" + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Attach to `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Attach to `stderr`" + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/wait: + post: + summary: "Wait for a container" + description: "Block until a container stops, then returns the exit code." + operationId: "ContainerWait" + produces: ["application/json"] + responses: + 200: + description: "The container has exit." + schema: + type: "object" + title: "ContainerWaitResponse" + description: "OK response to ContainerWait operation" + required: [StatusCode] + properties: + StatusCode: + description: "Exit code of the container" + type: "integer" + x-nullable: false + Error: + description: "container waiting error, if any" + type: "object" + properties: + Message: + description: "Details of an error" + type: "string" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "condition" + in: "query" + description: | + Wait until a container state reaches the given condition, either + 'not-running' (default), 'next-exit', or 'removed'. + type: "string" + default: "not-running" + tags: ["Container"] + /containers/{id}: + delete: + summary: "Remove a container" + operationId: "ContainerDelete" + responses: + 204: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "conflict" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: | + You cannot remove a running container: c2ada9df5af8. Stop the + container before attempting removal or force remove + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "v" + in: "query" + description: "Remove anonymous volumes associated with the container." + type: "boolean" + default: false + - name: "force" + in: "query" + description: "If the container is running, kill it before removing it." + type: "boolean" + default: false + - name: "link" + in: "query" + description: "Remove the specified link associated with the container." + type: "boolean" + default: false + tags: ["Container"] + /containers/{id}/archive: + head: + summary: "Get information about files in a container" + description: | + A response header `X-Docker-Container-Path-Stat` is returned, containing + a base64 - encoded JSON object with some filesystem header information + about the path. + operationId: "ContainerArchiveInfo" + responses: + 200: + description: "no error" + headers: + X-Docker-Container-Path-Stat: + type: "string" + description: | + A base64 - encoded JSON object with some filesystem header + information about the path + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: | + The error message. Either "must specify path parameter" + (path cannot be empty) or "not a directory" (path was + asserted to be a directory but exists as a file). + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + get: + summary: "Get an archive of a filesystem resource in a container" + description: "Get a tar archive of a resource in the filesystem of container id." + operationId: "ContainerArchive" + produces: ["application/x-tar"] + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + allOf: + - $ref: "#/definitions/ErrorResponse" + - type: "object" + properties: + message: + description: | + The error message. Either "must specify path parameter" + (path cannot be empty) or "not a directory" (path was + asserted to be a directory but exists as a file). + type: "string" + x-nullable: false + 404: + description: "Container or path does not exist" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Resource in the container’s filesystem to archive." + type: "string" + tags: ["Container"] + put: + summary: "Extract an archive of files or folders to a directory in a container" + description: "Upload a tar archive to be extracted to a path in the filesystem of container id." + operationId: "PutContainerArchive" + consumes: ["application/x-tar", "application/octet-stream"] + responses: + 200: + description: "The content was extracted successfully" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "Permission denied, the volume or container rootfs is marked as read-only." + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such container or path does not exist inside the container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the container" + type: "string" + - name: "path" + in: "query" + required: true + description: "Path to a directory in the container to extract the archive’s contents into. " + type: "string" + - name: "noOverwriteDirNonDir" + in: "query" + description: | + If `1`, `true`, or `True` then it will be an error if unpacking the + given content would cause an existing directory to be replaced with + a non-directory and vice versa. + type: "string" + - name: "copyUIDGID" + in: "query" + description: | + If `1`, `true`, then it will copy UID/GID maps to the dest file or + dir + type: "string" + - name: "inputStream" + in: "body" + required: true + description: | + The input stream must be a tar archive compressed with one of the + following algorithms: `identity` (no compression), `gzip`, `bzip2`, + or `xz`. + schema: + type: "string" + format: "binary" + tags: ["Container"] + /containers/prune: + post: + summary: "Delete stopped containers" + produces: + - "application/json" + operationId: "ContainerPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune containers created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune containers with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ContainerPruneResponse" + properties: + ContainersDeleted: + description: "Container IDs that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Container"] + /images/json: + get: + summary: "List Images" + description: "Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image." + operationId: "ImageList" + produces: + - "application/json" + responses: + 200: + description: "Summary image data for the images matching the query" + schema: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + examples: + application/json: + - Id: "sha256:e216a057b1cb1efc11f8a268f37ef62083e70b1b38323ba252e25ac88904a7e8" + ParentId: "" + RepoTags: + - "ubuntu:12.04" + - "ubuntu:precise" + RepoDigests: + - "ubuntu@sha256:992069aee4016783df6345315302fa59681aae51a8eeb2f889dea59290f21787" + Created: 1474925151 + Size: 103579269 + VirtualSize: 103579269 + SharedSize: 0 + Labels: {} + Containers: 2 + - Id: "sha256:3e314f95dcace0f5e4fd37b10862fe8398e3c60ed36600bc0ca5fda78b087175" + ParentId: "" + RepoTags: + - "ubuntu:12.10" + - "ubuntu:quantal" + RepoDigests: + - "ubuntu@sha256:002fba3e3255af10be97ea26e476692a7ebed0bb074a9ab960b2e7a1526b15d7" + - "ubuntu@sha256:68ea0200f0b90df725d99d823905b04cf844f6039ef60c60bf3e019915017bd3" + Created: 1403128455 + Size: 172064416 + VirtualSize: 172064416 + SharedSize: 0 + Labels: {} + Containers: 5 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "all" + in: "query" + description: "Show all images. Only images from a final layer (no children) are shown by default." + type: "boolean" + default: false + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the images list. + + Available filters: + + - `before`=(`[:]`, `` or ``) + - `dangling=true` + - `label=key` or `label="key=value"` of an image label + - `reference`=(`[:]`) + - `since`=(`[:]`, `` or ``) + type: "string" + - name: "digests" + in: "query" + description: "Show digest information as a `RepoDigests` field on each image." + type: "boolean" + default: false + tags: ["Image"] + /build: + post: + summary: "Build an image" + description: | + Build an image from a tar archive with a `Dockerfile` in it. + + The `Dockerfile` specifies how the image is built from the tar archive. It is typically in the archive's root, but can be at a different path or have a different name by specifying the `dockerfile` parameter. [See the `Dockerfile` reference for more information](https://docs.docker.com/engine/reference/builder/). + + The Docker daemon performs a preliminary validation of the `Dockerfile` before starting the build, and returns an error if the syntax is incorrect. After that, each instruction is run one-by-one until the ID of the new image is output. + + The build is canceled if the client drops the connection by quitting or being killed. + operationId: "ImageBuild" + consumes: + - "application/octet-stream" + produces: + - "application/json" + parameters: + - name: "inputStream" + in: "body" + description: "A tar archive compressed with one of the following algorithms: identity (no compression), gzip, bzip2, xz." + schema: + type: "string" + format: "binary" + - name: "dockerfile" + in: "query" + description: "Path within the build context to the `Dockerfile`. This is ignored if `remote` is specified and points to an external `Dockerfile`." + type: "string" + default: "Dockerfile" + - name: "t" + in: "query" + description: "A name and optional tag to apply to the image in the `name:tag` format. If you omit the tag the default `latest` value is assumed. You can provide several `t` parameters." + type: "string" + - name: "extrahosts" + in: "query" + description: "Extra hosts to add to /etc/hosts" + type: "string" + - name: "remote" + in: "query" + description: "A Git repository URI or HTTP/HTTPS context URI. If the URI points to a single text file, the file’s contents are placed into a file called `Dockerfile` and the image is built from that file. If the URI points to a tarball, the file is downloaded by the daemon and the contents therein used as the context for the build. If the URI points to a tarball and the `dockerfile` parameter is also specified, there must be a file with the corresponding path inside the tarball." + type: "string" + - name: "q" + in: "query" + description: "Suppress verbose build output." + type: "boolean" + default: false + - name: "nocache" + in: "query" + description: "Do not use the cache when building the image." + type: "boolean" + default: false + - name: "cachefrom" + in: "query" + description: "JSON array of images used for build cache resolution." + type: "string" + - name: "pull" + in: "query" + description: "Attempt to pull the image even if an older image exists locally." + type: "string" + - name: "rm" + in: "query" + description: "Remove intermediate containers after a successful build." + type: "boolean" + default: true + - name: "forcerm" + in: "query" + description: "Always remove intermediate containers, even upon failure." + type: "boolean" + default: false + - name: "memory" + in: "query" + description: "Set memory limit for build." + type: "integer" + - name: "memswap" + in: "query" + description: "Total memory (memory + swap). Set as `-1` to disable swap." + type: "integer" + - name: "cpushares" + in: "query" + description: "CPU shares (relative weight)." + type: "integer" + - name: "cpusetcpus" + in: "query" + description: "CPUs in which to allow execution (e.g., `0-3`, `0,1`)." + type: "string" + - name: "cpuperiod" + in: "query" + description: "The length of a CPU period in microseconds." + type: "integer" + - name: "cpuquota" + in: "query" + description: "Microseconds of CPU time that the container can get in a CPU period." + type: "integer" + - name: "buildargs" + in: "query" + description: > + JSON map of string pairs for build-time variables. Users pass these values at build-time. Docker + uses the buildargs as the environment context for commands run via the `Dockerfile` RUN + instruction, or for variable expansion in other `Dockerfile` instructions. This is not meant for + passing secret values. + + + For example, the build arg `FOO=bar` would become `{"FOO":"bar"}` in JSON. This would result in the + query parameter `buildargs={"FOO":"bar"}`. Note that `{"FOO":"bar"}` should be URI component encoded. + + + [Read more about the buildargs instruction.](https://docs.docker.com/engine/reference/builder/#arg) + type: "string" + - name: "shmsize" + in: "query" + description: "Size of `/dev/shm` in bytes. The size must be greater than 0. If omitted the system uses 64MB." + type: "integer" + - name: "squash" + in: "query" + description: "Squash the resulting images layers into a single layer. *(Experimental release only.)*" + type: "boolean" + - name: "labels" + in: "query" + description: "Arbitrary key/value labels to set on the image, as a JSON map of string pairs." + type: "string" + - name: "networkmode" + in: "query" + description: | + Sets the networking mode for the run commands during build. Supported + standard values are: `bridge`, `host`, `none`, and `container:`. + Any other value is taken as a custom network's name or ID to which this + container should connect to. + type: "string" + - name: "Content-type" + in: "header" + type: "string" + enum: + - "application/x-tar" + default: "application/x-tar" + - name: "X-Registry-Config" + in: "header" + description: | + This is a base64-encoded JSON object with auth configurations for multiple registries that a build may refer to. + + The key is a registry URL, and the value is an auth configuration object, [as described in the authentication section](#section/Authentication). For example: + + ``` + { + "docker.example.com": { + "username": "janedoe", + "password": "hunter2" + }, + "https://index.docker.io/v1/": { + "username": "mobydock", + "password": "conta1n3rize14" + } + } + ``` + + Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + - name: "target" + in: "query" + description: "Target build stage" + type: "string" + default: "" + - name: "outputs" + in: "query" + description: "BuildKit output configuration" + type: "string" + default: "" + responses: + 200: + description: "no error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /build/prune: + post: + summary: "Delete builder cache" + produces: + - "application/json" + operationId: "BuildPrune" + parameters: + - name: "keep-storage" + in: "query" + description: "Amount of disk space in bytes to keep for cache" + type: "integer" + format: "int64" + - name: "all" + in: "query" + type: "boolean" + description: "Remove all types of build cache" + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the list of build cache objects. + + Available filters: + + - `until=`: duration relative to daemon's time, during which build cache was not used, in Go's duration format (e.g., '24h') + - `id=` + - `parent=` + - `type=` + - `description=` + - `inuse` + - `shared` + - `private` + responses: + 200: + description: "No error" + schema: + type: "object" + title: "BuildPruneResponse" + properties: + CachesDeleted: + type: "array" + items: + description: "ID of build cache object" + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /images/create: + post: + summary: "Create an image" + description: "Create an image by either pulling it from a registry or importing it." + operationId: "ImageCreate" + consumes: + - "text/plain" + - "application/octet-stream" + produces: + - "application/json" + responses: + 200: + description: "no error" + 404: + description: "repository does not exist or no read access" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "fromImage" + in: "query" + description: "Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed." + type: "string" + - name: "fromSrc" + in: "query" + description: "Source to import. The value may be a URL from which the image can be retrieved or `-` to read the image from the request body. This parameter may only be used when importing an image." + type: "string" + - name: "repo" + in: "query" + description: "Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image." + type: "string" + - name: "tag" + in: "query" + description: "Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled." + type: "string" + - name: "message" + in: "query" + description: "Set commit message for imported image." + type: "string" + - name: "inputImage" + in: "body" + description: "Image content if the value `-` has been specified in fromSrc query parameter" + schema: + type: "string" + required: false + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "platform" + in: "query" + description: "Platform in the format os[/arch[/variant]]" + type: "string" + default: "" + tags: ["Image"] + /images/{name}/json: + get: + summary: "Inspect an image" + description: "Return low-level information about an image." + operationId: "ImageInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Image" + examples: + application/json: + Id: "sha256:85f05633ddc1c50679be2b16a0479ab6f7637f8884e0cfe0f4d20e1ebb3d6e7c" + Container: "cb91e48a60d01f1e27028b4fc6819f4f290b3cf12496c8176ec714d0d390984a" + Comment: "" + Os: "linux" + Architecture: "amd64" + Parent: "sha256:91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + ContainerConfig: + Tty: false + Hostname: "e611e15f9c9d" + Domainname: "" + AttachStdout: false + PublishService: "" + AttachStdin: false + OpenStdin: false + StdinOnce: false + NetworkDisabled: false + OnBuild: [] + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + User: "" + WorkingDir: "" + MacAddress: "" + AttachStderr: false + Labels: + com.example.license: "GPL" + com.example.version: "1.0" + com.example.vendor: "Acme" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Cmd: + - "/bin/sh" + - "-c" + - "#(nop) LABEL com.example.vendor=Acme com.example.license=GPL com.example.version=1.0" + DockerVersion: "1.9.0-dev" + VirtualSize: 188359297 + Size: 0 + Author: "" + Created: "2015-09-10T08:30:53.26995814Z" + GraphDriver: + Name: "aufs" + Data: {} + RepoDigests: + - "localhost:5000/test/busybox/example@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf" + RepoTags: + - "example:1.0" + - "example:latest" + - "example:stable" + Config: + Image: "91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c" + NetworkDisabled: false + OnBuild: [] + StdinOnce: false + PublishService: "" + AttachStdin: false + OpenStdin: false + Domainname: "" + AttachStdout: false + Tty: false + Hostname: "e611e15f9c9d" + Cmd: + - "/bin/bash" + Env: + - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + Labels: + com.example.vendor: "Acme" + com.example.version: "1.0" + com.example.license: "GPL" + MacAddress: "" + AttachStderr: false + WorkingDir: "" + User: "" + RootFS: + Type: "layers" + Layers: + - "sha256:1834950e52ce4d5a88a1bbd131c537f4d0e56d10ff0dd69e66be3b7dfa9df7e6" + - "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Image"] + /images/{name}/history: + get: + summary: "Get the history of an image" + description: "Return parent layers of an image." + operationId: "ImageHistory" + produces: ["application/json"] + responses: + 200: + description: "List of image layers" + schema: + type: "array" + items: + type: "object" + x-go-name: HistoryResponseItem + title: "HistoryResponseItem" + description: "individual image layer information in response to ImageHistory operation" + required: [Id, Created, CreatedBy, Tags, Size, Comment] + properties: + Id: + type: "string" + x-nullable: false + Created: + type: "integer" + format: "int64" + x-nullable: false + CreatedBy: + type: "string" + x-nullable: false + Tags: + type: "array" + items: + type: "string" + Size: + type: "integer" + format: "int64" + x-nullable: false + Comment: + type: "string" + x-nullable: false + examples: + application/json: + - Id: "3db9c44f45209632d6050b35958829c3a2aa256d81b9a7be45b362ff85c54710" + Created: 1398108230 + CreatedBy: "/bin/sh -c #(nop) ADD file:eb15dbd63394e063b805a3c32ca7bf0266ef64676d5a6fab4801f2e81e2a5148 in /" + Tags: + - "ubuntu:lucid" + - "ubuntu:10.04" + Size: 182964289 + Comment: "" + - Id: "6cfa4d1f33fb861d4d114f43b25abd0ac737509268065cdfd69d544a59c85ab8" + Created: 1398108222 + CreatedBy: "/bin/sh -c #(nop) MAINTAINER Tianon Gravi - mkimage-debootstrap.sh -i iproute,iputils-ping,ubuntu-minimal -t lucid.tar.xz lucid http://archive.ubuntu.com/ubuntu/" + Tags: [] + Size: 0 + Comment: "" + - Id: "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158" + Created: 1371157430 + CreatedBy: "" + Tags: + - "scratch12:latest" + - "scratch:latest" + Size: 0 + Comment: "Imported from -" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/{name}/push: + post: + summary: "Push an image" + description: | + Push an image to a registry. + + If you wish to push an image on to a private registry, that image must + already have a tag which references the registry. For example, + `registry.example.com/myimage:latest`. + + The push is cancelled if the HTTP connection is closed. + operationId: "ImagePush" + consumes: + - "application/octet-stream" + responses: + 200: + description: "No error" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID." + type: "string" + required: true + - name: "tag" + in: "query" + description: "The tag to associate with the image on the registry." + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + required: true + tags: ["Image"] + /images/{name}/tag: + post: + summary: "Tag an image" + description: "Tag an image so that it becomes part of a repository." + operationId: "ImageTag" + responses: + 201: + description: "No error" + 400: + description: "Bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID to tag." + type: "string" + required: true + - name: "repo" + in: "query" + description: "The repository to tag in. For example, `someuser/someimage`." + type: "string" + - name: "tag" + in: "query" + description: "The name of the new tag." + type: "string" + tags: ["Image"] + /images/{name}: + delete: + summary: "Remove an image" + description: | + Remove an image, along with any untagged parent images that were + referenced by that image. + + Images can't be removed if they have descendant images, are being + used by a running container or are being used by a build. + operationId: "ImageDelete" + produces: ["application/json"] + responses: + 200: + description: "The image was deleted successfully" + schema: + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + examples: + application/json: + - Untagged: "3e2f21a89f" + - Deleted: "3e2f21a89f" + - Deleted: "53b4f83ac9" + 404: + description: "No such image" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Conflict" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + - name: "force" + in: "query" + description: "Remove the image even if it is being used by stopped containers or has other tags" + type: "boolean" + default: false + - name: "noprune" + in: "query" + description: "Do not delete untagged parent images" + type: "boolean" + default: false + tags: ["Image"] + /images/search: + get: + summary: "Search images" + description: "Search for an image on Docker Hub." + operationId: "ImageSearch" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + type: "object" + title: "ImageSearchResponseItem" + properties: + description: + type: "string" + is_official: + type: "boolean" + is_automated: + type: "boolean" + name: + type: "string" + star_count: + type: "integer" + examples: + application/json: + - description: "" + is_official: false + is_automated: false + name: "wma55/u1210sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "jdswinbank/sshd" + star_count: 0 + - description: "" + is_official: false + is_automated: false + name: "vgauthier/sshd" + star_count: 0 + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "term" + in: "query" + description: "Term to search" + type: "string" + required: true + - name: "limit" + in: "query" + description: "Maximum number of results to return" + type: "integer" + - name: "filters" + in: "query" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + + - `is-automated=(true|false)` + - `is-official=(true|false)` + - `stars=` Matches images that has at least 'number' stars. + type: "string" + tags: ["Image"] + /images/prune: + post: + summary: "Delete unused images" + produces: + - "application/json" + operationId: "ImagePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + + - `dangling=` When set to `true` (or `1`), prune only + unused *and* untagged images. When set to `false` + (or `0`), all unused images are pruned. + - `until=` Prune images created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune images with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ImagePruneResponse" + properties: + ImagesDeleted: + description: "Images that were deleted" + type: "array" + items: + $ref: "#/definitions/ImageDeleteResponseItem" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Image"] + /auth: + post: + summary: "Check auth configuration" + description: | + Validate credentials for a registry and, if available, get an identity + token for accessing the registry without password. + operationId: "SystemAuth" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "An identity token was generated successfully." + schema: + type: "object" + title: "SystemAuthResponse" + required: [Status] + properties: + Status: + description: "The status of the authentication" + type: "string" + x-nullable: false + IdentityToken: + description: "An opaque token used to authenticate a user after a successful login" + type: "string" + x-nullable: false + examples: + application/json: + Status: "Login Succeeded" + IdentityToken: "9cbaf023786cd7..." + 204: + description: "No error" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "authConfig" + in: "body" + description: "Authentication to check" + schema: + $ref: "#/definitions/AuthConfig" + tags: ["System"] + /info: + get: + summary: "Get system information" + operationId: "SystemInfo" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/SystemInfo" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /version: + get: + summary: "Get version" + description: "Returns the version of Docker that is running and various information about the system that Docker is running on." + operationId: "SystemVersion" + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/SystemVersion" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /_ping: + get: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPing" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "OK" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Builder-Version: + type: "string" + description: "Default version of docker image builder" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + headers: + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + tags: ["System"] + head: + summary: "Ping" + description: "This is a dummy endpoint you can use to test if the server is accessible." + operationId: "SystemPingHead" + produces: ["text/plain"] + responses: + 200: + description: "no error" + schema: + type: "string" + example: "(empty)" + headers: + API-Version: + type: "string" + description: "Max API Version the server supports" + Builder-Version: + type: "string" + description: "Default version of docker image builder" + Docker-Experimental: + type: "boolean" + description: "If the server is running with experimental mode enabled" + Cache-Control: + type: "string" + default: "no-cache, no-store, must-revalidate" + Pragma: + type: "string" + default: "no-cache" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /commit: + post: + summary: "Create a new image from a container" + operationId: "ImageCommit" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "containerConfig" + in: "body" + description: "The container configuration" + schema: + $ref: "#/definitions/ContainerConfig" + - name: "container" + in: "query" + description: "The ID or name of the container to commit" + type: "string" + - name: "repo" + in: "query" + description: "Repository name for the created image" + type: "string" + - name: "tag" + in: "query" + description: "Tag name for the create image" + type: "string" + - name: "comment" + in: "query" + description: "Commit message" + type: "string" + - name: "author" + in: "query" + description: "Author of the image (e.g., `John Hannibal Smith `)" + type: "string" + - name: "pause" + in: "query" + description: "Whether to pause the container before committing" + type: "boolean" + default: true + - name: "changes" + in: "query" + description: "`Dockerfile` instructions to apply while committing" + type: "string" + tags: ["Image"] + /events: + get: + summary: "Monitor events" + description: | + Stream real-time events from the server. + + Various objects within Docker report events when something happens to them. + + Containers report these events: `attach`, `commit`, `copy`, `create`, `destroy`, `detach`, `die`, `exec_create`, `exec_detach`, `exec_start`, `exec_die`, `export`, `health_status`, `kill`, `oom`, `pause`, `rename`, `resize`, `restart`, `start`, `stop`, `top`, `unpause`, `update`, and `prune` + + Images report these events: `delete`, `import`, `load`, `pull`, `push`, `save`, `tag`, `untag`, and `prune` + + Volumes report these events: `create`, `mount`, `unmount`, `destroy`, and `prune` + + Networks report these events: `create`, `connect`, `disconnect`, `destroy`, `update`, `remove`, and `prune` + + The Docker daemon reports these events: `reload` + + Services report these events: `create`, `update`, and `remove` + + Nodes report these events: `create`, `update`, and `remove` + + Secrets report these events: `create`, `update`, and `remove` + + Configs report these events: `create`, `update`, and `remove` + + The Builder reports `prune` events + + operationId: "SystemEvents" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemEventsResponse" + properties: + Type: + description: "The type of object emitting the event" + type: "string" + Action: + description: "The type of event" + type: "string" + Actor: + type: "object" + properties: + ID: + description: "The ID of the object emitting the event" + type: "string" + Attributes: + description: "Various key/value attributes of the object, depending on its type" + type: "object" + additionalProperties: + type: "string" + time: + description: "Timestamp of event" + type: "integer" + timeNano: + description: "Timestamp of event, with nanosecond accuracy" + type: "integer" + format: "int64" + examples: + application/json: + Type: "container" + Action: "create" + Actor: + ID: "ede54ee1afda366ab42f824e8a5ffd195155d853ceaec74a927f249ea270c743" + Attributes: + com.example.some-label: "some-label-value" + image: "alpine" + name: "my-container" + time: 1461943101 + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "since" + in: "query" + description: "Show events created since this timestamp then stream new events." + type: "string" + - name: "until" + in: "query" + description: "Show events created until this timestamp then stop streaming." + type: "string" + - name: "filters" + in: "query" + description: | + A JSON encoded value of filters (a `map[string][]string`) to process on the event list. Available filters: + + - `config=` config name or ID + - `container=` container name or ID + - `daemon=` daemon name or ID + - `event=` event type + - `image=` image name or ID + - `label=` image or container label + - `network=` network name or ID + - `node=` node ID + - `plugin`= plugin name or ID + - `scope`= local or swarm + - `secret=` secret name or ID + - `service=` service name or ID + - `type=` object to filter by, one of `container`, `image`, `volume`, `network`, `daemon`, `plugin`, `node`, `service`, `secret` or `config` + - `volume=` volume name + type: "string" + tags: ["System"] + /system/df: + get: + summary: "Get data usage information" + operationId: "SystemDataUsage" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "SystemDataUsageResponse" + properties: + LayersSize: + type: "integer" + format: "int64" + Images: + type: "array" + items: + $ref: "#/definitions/ImageSummary" + Containers: + type: "array" + items: + $ref: "#/definitions/ContainerSummary" + Volumes: + type: "array" + items: + $ref: "#/definitions/Volume" + BuildCache: + type: "array" + items: + $ref: "#/definitions/BuildCache" + example: + LayersSize: 1092588 + Images: + - + Id: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + ParentId: "" + RepoTags: + - "busybox:latest" + RepoDigests: + - "busybox@sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6" + Created: 1466724217 + Size: 1092588 + SharedSize: 0 + VirtualSize: 1092588 + Labels: {} + Containers: 1 + Containers: + - + Id: "e575172ed11dc01bfce087fb27bee502db149e1a0fad7c296ad300bbff178148" + Names: + - "/top" + Image: "busybox" + ImageID: "sha256:2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749" + Command: "top" + Created: 1472592424 + Ports: [] + SizeRootFs: 1092588 + Labels: {} + State: "exited" + Status: "Exited (0) 56 minutes ago" + HostConfig: + NetworkMode: "default" + NetworkSettings: + Networks: + bridge: + IPAMConfig: null + Links: null + Aliases: null + NetworkID: "d687bc59335f0e5c9ee8193e5612e8aee000c8c62ea170cfb99c098f95899d92" + EndpointID: "8ed5115aeaad9abb174f68dcf135b49f11daf597678315231a32ca28441dec6a" + Gateway: "172.18.0.1" + IPAddress: "172.18.0.2" + IPPrefixLen: 16 + IPv6Gateway: "" + GlobalIPv6Address: "" + GlobalIPv6PrefixLen: 0 + MacAddress: "02:42:ac:12:00:02" + Mounts: [] + Volumes: + - + Name: "my-volume" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/my-volume/_data" + Labels: null + Scope: "local" + Options: null + UsageData: + Size: 10920104 + RefCount: 2 + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["System"] + /images/{name}/get: + get: + summary: "Export an image" + description: | + Get a tarball containing all images and metadata for a repository. + + If `name` is a specific name and tag (e.g. `ubuntu:latest`), then only that image (and its parents) are returned. If `name` is an image ID, similarly only that image (and its parents) are returned, but with the exclusion of the `repositories` file in the tarball, as there were no image names referenced. + + ### Image tarball format + + An image tarball contains one directory per image layer (named using its long ID), each containing these files: + + - `VERSION`: currently `1.0` - the file format version + - `json`: detailed layer information, similar to `docker inspect layer_id` + - `layer.tar`: A tarfile containing the filesystem changes in this layer + + The `layer.tar` file contains `aufs` style `.wh..wh.aufs` files and directories for storing attribute changes and deletions. + + If the tarball defines a repository, the tarball should also include a `repositories` file at the root that contains a list of repository and tag names mapped to layer IDs. + + ```json + { + "hello-world": { + "latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1" + } + } + ``` + operationId: "ImageGet" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or ID" + type: "string" + required: true + tags: ["Image"] + /images/get: + get: + summary: "Export several images" + description: | + Get a tarball containing all images and metadata for several image + repositories. + + For each value of the `names` parameter: if it is a specific name and + tag (e.g. `ubuntu:latest`), then only that image (and its parents) are + returned; if it is an image ID, similarly only that image (and its parents) + are returned and there would be no names referenced in the 'repositories' + file for this image ID. + + For details on the format, see the [export image endpoint](#operation/ImageGet). + operationId: "ImageGetAll" + produces: + - "application/x-tar" + responses: + 200: + description: "no error" + schema: + type: "string" + format: "binary" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "names" + in: "query" + description: "Image names to filter by" + type: "array" + items: + type: "string" + tags: ["Image"] + /images/load: + post: + summary: "Import images" + description: | + Load a set of images and tags into a repository. + + For details on the format, see the [export image endpoint](#operation/ImageGet). + operationId: "ImageLoad" + consumes: + - "application/x-tar" + produces: + - "application/json" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "imagesTarball" + in: "body" + description: "Tar archive containing images" + schema: + type: "string" + format: "binary" + - name: "quiet" + in: "query" + description: "Suppress progress details during load." + type: "boolean" + default: false + tags: ["Image"] + /containers/{id}/exec: + post: + summary: "Create an exec instance" + description: "Run a command inside a running container." + operationId: "ContainerExec" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 404: + description: "no such container" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such container: c2ada9df5af8" + 409: + description: "container is paused" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execConfig" + in: "body" + description: "Exec configuration" + schema: + type: "object" + properties: + AttachStdin: + type: "boolean" + description: "Attach to `stdin` of the exec command." + AttachStdout: + type: "boolean" + description: "Attach to `stdout` of the exec command." + AttachStderr: + type: "boolean" + description: "Attach to `stderr` of the exec command." + DetachKeys: + type: "string" + description: | + Override the key sequence for detaching a container. Format is + a single character `[a-Z]` or `ctrl-` where `` + is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + Env: + description: | + A list of environment variables in the form `["VAR=value", ...]`. + type: "array" + items: + type: "string" + Cmd: + type: "array" + description: "Command to run, as a string or array of strings." + items: + type: "string" + Privileged: + type: "boolean" + description: "Runs the exec process with extended privileges." + default: false + User: + type: "string" + description: | + The user, and optionally, group to run the exec process inside + the container. Format is one of: `user`, `user:group`, `uid`, + or `uid:gid`. + WorkingDir: + type: "string" + description: | + The working directory for the exec process inside the container. + example: + AttachStdin: false + AttachStdout: true + AttachStderr: true + DetachKeys: "ctrl-p,ctrl-q" + Tty: false + Cmd: + - "date" + Env: + - "FOO=bar" + - "BAZ=quux" + required: true + - name: "id" + in: "path" + description: "ID or name of container" + type: "string" + required: true + tags: ["Exec"] + /exec/{id}/start: + post: + summary: "Start an exec instance" + description: | + Starts a previously set up exec instance. If detach is true, this endpoint + returns immediately after starting the command. Otherwise, it sets up an + interactive session with the command. + operationId: "ExecStart" + consumes: + - "application/json" + produces: + - "application/vnd.docker.raw-stream" + responses: + 200: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Container is stopped or paused" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "execStartConfig" + in: "body" + schema: + type: "object" + properties: + Detach: + type: "boolean" + description: "Detach from the command." + Tty: + type: "boolean" + description: "Allocate a pseudo-TTY." + example: + Detach: false + Tty: false + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + /exec/{id}/resize: + post: + summary: "Resize an exec instance" + description: | + Resize the TTY session used by an exec instance. This endpoint only works + if `tty` was specified as part of creating and starting the exec instance. + operationId: "ExecResize" + responses: + 201: + description: "No error" + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + - name: "h" + in: "query" + description: "Height of the TTY session in characters" + type: "integer" + - name: "w" + in: "query" + description: "Width of the TTY session in characters" + type: "integer" + tags: ["Exec"] + /exec/{id}/json: + get: + summary: "Inspect an exec instance" + description: "Return low-level information about an exec instance." + operationId: "ExecInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "ExecInspectResponse" + properties: + CanRemove: + type: "boolean" + DetachKeys: + type: "string" + ID: + type: "string" + Running: + type: "boolean" + ExitCode: + type: "integer" + ProcessConfig: + $ref: "#/definitions/ProcessConfig" + OpenStdin: + type: "boolean" + OpenStderr: + type: "boolean" + OpenStdout: + type: "boolean" + ContainerID: + type: "string" + Pid: + type: "integer" + description: "The system process ID for the exec process." + examples: + application/json: + CanRemove: false + ContainerID: "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126" + DetachKeys: "" + ExitCode: 2 + ID: "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b" + OpenStderr: true + OpenStdin: true + OpenStdout: true + ProcessConfig: + arguments: + - "-c" + - "exit 2" + entrypoint: "sh" + privileged: false + tty: true + user: "1000" + Running: false + Pid: 42000 + 404: + description: "No such exec instance" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Exec instance ID" + required: true + type: "string" + tags: ["Exec"] + + /volumes: + get: + summary: "List volumes" + operationId: "VolumeList" + produces: ["application/json"] + responses: + 200: + description: "Summary volume data that matches the query" + schema: + type: "object" + title: "VolumeListResponse" + description: "Volume list response" + required: [Volumes, Warnings] + properties: + Volumes: + type: "array" + x-nullable: false + description: "List of volumes" + items: + $ref: "#/definitions/Volume" + Warnings: + type: "array" + x-nullable: false + description: | + Warnings that occurred when fetching the list of volumes. + items: + type: "string" + + examples: + application/json: + Volumes: + - CreatedAt: "2017-07-19T12:00:26Z" + Name: "tardis" + Driver: "local" + Mountpoint: "/var/lib/docker/volumes/tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Scope: "local" + Options: + device: "tmpfs" + o: "size=100m,uid=1000" + type: "tmpfs" + Warnings: [] + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to + process on the volumes list. Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + volumes that are not in use by a container. When set to `false` + (or `0`), only volumes that are in use by one or more + containers are returned. + - `driver=` Matches volumes based on their driver. + - `label=` or `label=:` Matches volumes based on + the presence of a `label` alone or a `label` and a value. + - `name=` Matches all or part of a volume name. + type: "string" + format: "json" + tags: ["Volume"] + + /volumes/create: + post: + summary: "Create a volume" + operationId: "VolumeCreate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 201: + description: "The volume was created successfully" + schema: + $ref: "#/definitions/Volume" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "volumeConfig" + in: "body" + required: true + description: "Volume configuration" + schema: + type: "object" + description: "Volume configuration" + title: "VolumeConfig" + properties: + Name: + description: | + The new volume's name. If not specified, Docker generates a name. + type: "string" + x-nullable: false + Driver: + description: "Name of the volume driver to use." + type: "string" + default: "local" + x-nullable: false + DriverOpts: + description: | + A mapping of driver options and values. These options are + passed directly to the driver and are driver specific. + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "tardis" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + Driver: "custom" + tags: ["Volume"] + + /volumes/{name}: + get: + summary: "Inspect a volume" + operationId: "VolumeInspect" + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Volume" + 404: + description: "No such volume" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + tags: ["Volume"] + + delete: + summary: "Remove a volume" + description: "Instruct the driver to remove the volume." + operationId: "VolumeDelete" + responses: + 204: + description: "The volume was removed" + 404: + description: "No such volume or volume driver" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "Volume is in use and cannot be removed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + required: true + description: "Volume name or ID" + type: "string" + - name: "force" + in: "query" + description: "Force the removal of the volume" + type: "boolean" + default: false + tags: ["Volume"] + /volumes/prune: + post: + summary: "Delete unused volumes" + produces: + - "application/json" + operationId: "VolumePrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "VolumePruneResponse" + properties: + VolumesDeleted: + description: "Volumes that were deleted" + type: "array" + items: + type: "string" + SpaceReclaimed: + description: "Disk space reclaimed in bytes" + type: "integer" + format: "int64" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Volume"] + /networks: + get: + summary: "List networks" + description: | + Returns a list of networks. For details on the format, see the + [network inspect endpoint](#operation/NetworkInspect). + + Note that it uses a different, smaller representation of a network than + inspecting a single network. For example, the list of containers attached + to the network is not propagated in API versions 1.28 and up. + operationId: "NetworkList" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Network" + examples: + application/json: + - Name: "bridge" + Id: "f2de39df4171b0dc801e8002d1d999b77256983dfc63041c0f34030aa3977566" + Created: "2016-10-19T06:21:00.416543526Z" + Scope: "local" + Driver: "bridge" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: + - + Subnet: "172.17.0.0/16" + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + - Name: "none" + Id: "e086a3893b05ab69242d3c44e49483a3bbbd3a26b46baa8f61ab797c1088d794" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "null" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + - Name: "host" + Id: "13e871235c677f196c4e1ecebb9dc733b9b2d2ab589e30c539efeda84a24215e" + Created: "0001-01-01T00:00:00Z" + Scope: "local" + Driver: "host" + EnableIPv6: false + Internal: false + Attachable: false + Ingress: false + IPAM: + Driver: "default" + Config: [] + Containers: {} + Options: {} + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + JSON encoded value of the filters (a `map[string][]string`) to process + on the networks list. + + Available filters: + + - `dangling=` When set to `true` (or `1`), returns all + networks that are not in use by a container. When set to `false` + (or `0`), only networks that are in use by one or more + containers are returned. + - `driver=` Matches a network's driver. + - `id=` Matches all or part of a network ID. + - `label=` or `label==` of a network label. + - `name=` Matches all or part of a network name. + - `scope=["swarm"|"global"|"local"]` Filters networks by scope (`swarm`, `global`, or `local`). + - `type=["custom"|"builtin"]` Filters networks by type. The `custom` keyword returns all user-defined networks. + type: "string" + tags: ["Network"] + + /networks/{id}: + get: + summary: "Inspect a network" + operationId: "NetworkInspect" + produces: + - "application/json" + responses: + 200: + description: "No error" + schema: + $ref: "#/definitions/Network" + 404: + description: "Network not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "verbose" + in: "query" + description: "Detailed inspect output for troubleshooting" + type: "boolean" + default: false + - name: "scope" + in: "query" + description: "Filter the network by scope (swarm, global, or local)" + type: "string" + tags: ["Network"] + + delete: + summary: "Remove a network" + operationId: "NetworkDelete" + responses: + 204: + description: "No error" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such network" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + tags: ["Network"] + + /networks/create: + post: + summary: "Create a network" + operationId: "NetworkCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "No error" + schema: + type: "object" + title: "NetworkCreateResponse" + properties: + Id: + description: "The ID of the created network." + type: "string" + Warning: + type: "string" + example: + Id: "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30" + Warning: "" + 403: + description: "operation not supported for pre-defined networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "plugin not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "networkConfig" + in: "body" + description: "Network configuration" + required: true + schema: + type: "object" + required: ["Name"] + properties: + Name: + description: "The network's name." + type: "string" + CheckDuplicate: + description: | + Check for networks with duplicate names. Since Network is + primarily keyed based on a random ID and not on the name, and + network name is strictly a user-friendly alias to the network + which is uniquely identified using ID, there is no guaranteed + way to check for duplicates. CheckDuplicate is there to provide + a best effort checking of any networks which has the same name + but it is not guaranteed to catch all name collisions. + type: "boolean" + Driver: + description: "Name of the network driver plugin to use." + type: "string" + default: "bridge" + Internal: + description: "Restrict external access to the network." + type: "boolean" + Attachable: + description: | + Globally scoped network is manually attachable by regular + containers from workers in swarm mode. + type: "boolean" + Ingress: + description: | + Ingress network is the network which provides the routing-mesh + in swarm mode. + type: "boolean" + IPAM: + description: "Optional custom IP scheme for the network." + $ref: "#/definitions/IPAM" + EnableIPv6: + description: "Enable IPv6 on the network." + type: "boolean" + Options: + description: "Network specific options to be used by the drivers." + type: "object" + additionalProperties: + type: "string" + Labels: + description: "User-defined key/value metadata." + type: "object" + additionalProperties: + type: "string" + example: + Name: "isolated_nw" + CheckDuplicate: false + Driver: "bridge" + EnableIPv6: true + IPAM: + Driver: "default" + Config: + - Subnet: "172.20.0.0/16" + IPRange: "172.20.10.0/24" + Gateway: "172.20.10.11" + - Subnet: "2001:db8:abcd::/64" + Gateway: "2001:db8:abcd::1011" + Options: + foo: "bar" + Internal: true + Attachable: false + Ingress: false + Options: + com.docker.network.bridge.default_bridge: "true" + com.docker.network.bridge.enable_icc: "true" + com.docker.network.bridge.enable_ip_masquerade: "true" + com.docker.network.bridge.host_binding_ipv4: "0.0.0.0" + com.docker.network.bridge.name: "docker0" + com.docker.network.driver.mtu: "1500" + Labels: + com.example.some-label: "some-value" + com.example.some-other-label: "some-other-value" + tags: ["Network"] + + /networks/{id}/connect: + post: + summary: "Connect a container to a network" + operationId: "NetworkConnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: "The ID or name of the container to connect to the network." + EndpointConfig: + $ref: "#/definitions/EndpointSettings" + example: + Container: "3613f73ba0e4" + EndpointConfig: + IPAMConfig: + IPv4Address: "172.24.56.89" + IPv6Address: "2001:db8::5689" + tags: ["Network"] + + /networks/{id}/disconnect: + post: + summary: "Disconnect a container from a network" + operationId: "NetworkDisconnect" + consumes: + - "application/json" + responses: + 200: + description: "No error" + 403: + description: "Operation not supported for swarm scoped networks" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "Network or container not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "Network ID or name" + required: true + type: "string" + - name: "container" + in: "body" + required: true + schema: + type: "object" + properties: + Container: + type: "string" + description: | + The ID or name of the container to disconnect from the network. + Force: + type: "boolean" + description: | + Force the container to disconnect from the network. + tags: ["Network"] + /networks/prune: + post: + summary: "Delete unused networks" + produces: + - "application/json" + operationId: "NetworkPrune" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the prune list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. + type: "string" + responses: + 200: + description: "No error" + schema: + type: "object" + title: "NetworkPruneResponse" + properties: + NetworksDeleted: + description: "Networks that were deleted" + type: "array" + items: + type: "string" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Network"] + /plugins: + get: + summary: "List plugins" + operationId: "PluginList" + description: "Returns information about installed plugins." + produces: ["application/json"] + responses: + 200: + description: "No error" + schema: + type: "array" + items: + $ref: "#/definitions/Plugin" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the plugin list. + + Available filters: + + - `capability=` + - `enable=|` + tags: ["Plugin"] + + /plugins/privileges: + get: + summary: "Get plugin privileges" + operationId: "GetPluginPrivileges" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + description: | + Describes a permission the user has to accept upon installing + the plugin. + type: "object" + title: "PluginPrivilegeItem" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: + - "Plugin" + + /plugins/pull: + post: + summary: "Install a plugin" + operationId: "PluginPull" + description: | + Pulls and installs a plugin. After the plugin is installed, it can be + enabled using the [`POST /plugins/{name}/enable` endpoint](#operation/PostPluginsEnable). + produces: + - "application/json" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "remote" + in: "query" + description: | + Remote reference for plugin to install. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "name" + in: "query" + description: | + Local name for the pulled plugin. + + The `:latest` tag is optional, and is used as the default if omitted. + required: false + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration to use when pulling a plugin + from a registry. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/{name}/json: + get: + summary: "Inspect a plugin" + operationId: "PluginInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}: + delete: + summary: "Remove a plugin" + operationId: "PluginDelete" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Plugin" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "force" + in: "query" + description: | + Disable the plugin before removing. This may result in issues if the + plugin is in use by a container. + type: "boolean" + default: false + tags: ["Plugin"] + /plugins/{name}/enable: + post: + summary: "Enable a plugin" + operationId: "PluginEnable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "timeout" + in: "query" + description: "Set the HTTP client timeout (in seconds)" + type: "integer" + default: 0 + tags: ["Plugin"] + /plugins/{name}/disable: + post: + summary: "Disable a plugin" + operationId: "PluginDisable" + responses: + 200: + description: "no error" + 404: + description: "plugin is not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + tags: ["Plugin"] + /plugins/{name}/upgrade: + post: + summary: "Upgrade a plugin" + operationId: "PluginUpgrade" + responses: + 204: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "remote" + in: "query" + description: | + Remote reference to upgrade to. + + The `:latest` tag is optional, and is used as the default if omitted. + required: true + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration to use when pulling a plugin + from a registry. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + description: | + Describes a permission accepted by the user upon installing the + plugin. + type: "object" + properties: + Name: + type: "string" + Description: + type: "string" + Value: + type: "array" + items: + type: "string" + example: + - Name: "network" + Description: "" + Value: + - "host" + - Name: "mount" + Description: "" + Value: + - "/data" + - Name: "device" + Description: "" + Value: + - "/dev/cpu_dma_latency" + tags: ["Plugin"] + /plugins/create: + post: + summary: "Create a plugin" + operationId: "PluginCreate" + consumes: + - "application/x-tar" + responses: + 204: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "query" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "tarContext" + in: "body" + description: "Path to tar containing plugin rootfs and manifest" + schema: + type: "string" + format: "binary" + tags: ["Plugin"] + /plugins/{name}/push: + post: + summary: "Push a plugin" + operationId: "PluginPush" + description: | + Push a plugin to the registry. + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + responses: + 200: + description: "no error" + 404: + description: "plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /plugins/{name}/set: + post: + summary: "Configure a plugin" + operationId: "PluginSet" + consumes: + - "application/json" + parameters: + - name: "name" + in: "path" + description: | + The name of the plugin. The `:latest` tag is optional, and is the + default if omitted. + required: true + type: "string" + - name: "body" + in: "body" + schema: + type: "array" + items: + type: "string" + example: ["DEBUG=1"] + responses: + 204: + description: "No error" + 404: + description: "Plugin not installed" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Plugin"] + /nodes: + get: + summary: "List nodes" + operationId: "NodeList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Node" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + description: | + Filters to process on the nodes list, encoded as JSON (a `map[string][]string`). + + Available filters: + - `id=` + - `label=` + - `membership=`(`accepted`|`pending`)` + - `name=` + - `node.label=` + - `role=`(`manager`|`worker`)` + type: "string" + tags: ["Node"] + /nodes/{id}: + get: + summary: "Inspect a node" + operationId: "NodeInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Node" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + tags: ["Node"] + delete: + summary: "Delete a node" + operationId: "NodeDelete" + responses: + 200: + description: "no error" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the node" + type: "string" + required: true + - name: "force" + in: "query" + description: "Force remove a node from the swarm" + default: false + type: "boolean" + tags: ["Node"] + /nodes/{id}/update: + post: + summary: "Update a node" + operationId: "NodeUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such node" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID of the node" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/NodeSpec" + - name: "version" + in: "query" + description: | + The version number of the node object being updated. This is required + to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Node"] + /swarm: + get: + summary: "Inspect swarm" + operationId: "SwarmInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Swarm" + 404: + description: "no such swarm" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/init: + post: + summary: "Initialize a new swarm" + operationId: "SwarmInit" + produces: + - "application/json" + - "text/plain" + responses: + 200: + description: "no error" + schema: + description: "The node ID" + type: "string" + example: "7v2t30z9blmxuhnyo6s4cpenp" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: | + Listen address used for inter-manager communication, as well + as determining the networking interface used for the VXLAN + Tunnel Endpoint (VTEP). This can either be an address/port + combination in the form `192.168.1.1:4567`, or an interface + followed by a port number, like `eth0:4567`. If the port number + is omitted, the default swarm listening port is used. + type: "string" + AdvertiseAddr: + description: | + Externally reachable address advertised to other nodes. This + can either be an address/port combination in the form + `192.168.1.1:4567`, or an interface followed by a port number, + like `eth0:4567`. If the port number is omitted, the port + number from the listen address is used. If `AdvertiseAddr` is + not specified, it will be automatically detected when possible. + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: + ``), for example, `192.168.1.1`, or an interface, + like `eth0`. If `DataPathAddr` is unspecified, the same address + as `AdvertiseAddr` is used. + + The `DataPathAddr` specifies the address that global scope + network drivers will publish towards other nodes in order to + reach the containers running on this node. Using this parameter + it is possible to separate the container data traffic from the + management traffic of the cluster. + type: "string" + DataPathPort: + description: | + DataPathPort specifies the data path port number for data traffic. + Acceptable port range is 1024 to 49151. + if no port is set or is set to 0, default port 4789 will be used. + type: "integer" + format: "uint32" + DefaultAddrPool: + description: | + Default Address Pool specifies default subnet pools for global + scope networks. + type: "array" + items: + type: "string" + example: ["10.10.0.0/16", "20.20.0.0/16"] + ForceNewCluster: + description: "Force creation of a new swarm." + type: "boolean" + SubnetSize: + description: | + SubnetSize specifies the subnet size of the networks created + from the default subnet pool. + type: "integer" + format: "uint32" + Spec: + $ref: "#/definitions/SwarmSpec" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + DataPathPort: 4789 + DefaultAddrPool: ["10.10.0.0/8", "20.20.0.0/8"] + SubnetSize: 24 + ForceNewCluster: false + Spec: + Orchestration: {} + Raft: {} + Dispatcher: {} + CAConfig: {} + EncryptionConfig: + AutoLockManagers: false + tags: ["Swarm"] + /swarm/join: + post: + summary: "Join an existing swarm" + operationId: "SwarmJoin" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is already part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + ListenAddr: + description: | + Listen address used for inter-manager communication if the node + gets promoted to manager, as well as determining the networking + interface used for the VXLAN Tunnel Endpoint (VTEP). + type: "string" + AdvertiseAddr: + description: | + Externally reachable address advertised to other nodes. This + can either be an address/port combination in the form + `192.168.1.1:4567`, or an interface followed by a port number, + like `eth0:4567`. If the port number is omitted, the port + number from the listen address is used. If `AdvertiseAddr` is + not specified, it will be automatically detected when possible. + type: "string" + DataPathAddr: + description: | + Address or interface to use for data path traffic (format: + ``), for example, `192.168.1.1`, or an interface, + like `eth0`. If `DataPathAddr` is unspecified, the same address + as `AdvertiseAddr` is used. + + The `DataPathAddr` specifies the address that global scope + network drivers will publish towards other nodes in order to + reach the containers running on this node. Using this parameter + it is possible to separate the container data traffic from the + management traffic of the cluster. + + type: "string" + RemoteAddrs: + description: | + Addresses of manager nodes already participating in the swarm. + type: "array" + items: + type: "string" + JoinToken: + description: "Secret token for joining this swarm." + type: "string" + example: + ListenAddr: "0.0.0.0:2377" + AdvertiseAddr: "192.168.1.1:2377" + RemoteAddrs: + - "node1:2377" + JoinToken: "SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-7p73s1dx5in4tatdymyhg9hu2" + tags: ["Swarm"] + /swarm/leave: + post: + summary: "Leave a swarm" + operationId: "SwarmLeave" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "force" + description: | + Force leave swarm, even if this is the last manager or that it will + break the cluster. + in: "query" + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/update: + post: + summary: "Update a swarm" + operationId: "SwarmUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + $ref: "#/definitions/SwarmSpec" + - name: "version" + in: "query" + description: | + The version number of the swarm object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + - name: "rotateWorkerToken" + in: "query" + description: "Rotate the worker join token." + type: "boolean" + default: false + - name: "rotateManagerToken" + in: "query" + description: "Rotate the manager join token." + type: "boolean" + default: false + - name: "rotateManagerUnlockKey" + in: "query" + description: "Rotate the manager unlock key." + type: "boolean" + default: false + tags: ["Swarm"] + /swarm/unlockkey: + get: + summary: "Get the unlock key" + operationId: "SwarmUnlockkey" + consumes: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "object" + title: "UnlockKeyResponse" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /swarm/unlock: + post: + summary: "Unlock a locked manager" + operationId: "SwarmUnlock" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - name: "body" + in: "body" + required: true + schema: + type: "object" + properties: + UnlockKey: + description: "The swarm's unlock key." + type: "string" + example: + UnlockKey: "SWMKEY-1-7c37Cc8654o6p38HnroywCi19pllOnGtbdZEgtKxZu8" + responses: + 200: + description: "no error" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Swarm"] + /services: + get: + summary: "List services" + operationId: "ServiceList" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Service" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the services list. + + Available filters: + + - `id=` + - `label=` + - `mode=["replicated"|"global"]` + - `name=` + - name: "status" + in: "query" + type: "boolean" + description: | + Include service status, with count of running and desired tasks. + tags: ["Service"] + /services/create: + post: + summary: "Create a service" + operationId: "ServiceCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + type: "object" + title: "ServiceCreateResponse" + properties: + ID: + description: "The ID of the created service." + type: "string" + Warning: + description: "Optional warning message" + type: "string" + example: + ID: "ak7w3gjqoa3kuz8xcpnyy0pvl" + Warning: "unable to pin image doesnotexist:latest to digest: image library/doesnotexist:latest not found" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 403: + description: "network is not eligible for services" + schema: + $ref: "#/definitions/ErrorResponse" + 409: + description: "name conflicts with an existing service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "web" + TaskTemplate: + ContainerSpec: + Image: "nginx:alpine" + Mounts: + - + ReadOnly: true + Source: "web-data" + Target: "/usr/share/nginx/html" + Type: "volume" + VolumeOptions: + DriverConfig: {} + Labels: + com.example.something: "something-value" + Hosts: ["10.10.10.10 host1", "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 host2"] + User: "33" + DNSConfig: + Nameservers: ["8.8.8.8"] + Search: ["example.org"] + Options: ["timeout:3"] + Secrets: + - + File: + Name: "www.example.org.key" + UID: "33" + GID: "33" + Mode: 384 + SecretID: "fpjqlhnwb19zds35k8wn80lq9" + SecretName: "example_org_domain_key" + LogDriver: + Name: "json-file" + Options: + max-file: "3" + max-size: "10M" + Placement: {} + Resources: + Limits: + MemoryBytes: 104857600 + Reservations: {} + RestartPolicy: + Condition: "on-failure" + Delay: 10000000000 + MaxAttempts: 10 + Mode: + Replicated: + Replicas: 4 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Ports: + - + Protocol: "tcp" + PublishedPort: 8080 + TargetPort: 80 + Labels: + foo: "bar" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration for pulling from private + registries. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + tags: ["Service"] + /services/{id}: + get: + summary: "Inspect a service" + operationId: "ServiceInspect" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Service" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "insertDefaults" + in: "query" + description: "Fill empty fields with default values." + type: "boolean" + default: false + tags: ["Service"] + delete: + summary: "Delete a service" + operationId: "ServiceDelete" + responses: + 200: + description: "no error" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + tags: ["Service"] + /services/{id}/update: + post: + summary: "Update a service" + operationId: "ServiceUpdate" + consumes: ["application/json"] + produces: ["application/json"] + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/ServiceUpdateResponse" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID or name of service." + required: true + type: "string" + - name: "body" + in: "body" + required: true + schema: + allOf: + - $ref: "#/definitions/ServiceSpec" + - type: "object" + example: + Name: "top" + TaskTemplate: + ContainerSpec: + Image: "busybox" + Args: + - "top" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ForceUpdate: 0 + Mode: + Replicated: + Replicas: 1 + UpdateConfig: + Parallelism: 2 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + RollbackConfig: + Parallelism: 1 + Delay: 1000000000 + FailureAction: "pause" + Monitor: 15000000000 + MaxFailureRatio: 0.15 + EndpointSpec: + Mode: "vip" + + - name: "version" + in: "query" + description: | + The version number of the service object being updated. This is + required to avoid conflicting writes. + This version number should be the value as currently set on the + service *before* the update. You can find the current version by + calling `GET /services/{id}` + required: true + type: "integer" + - name: "registryAuthFrom" + in: "query" + description: | + If the `X-Registry-Auth` header is not specified, this parameter + indicates where to find registry authorization credentials. + type: "string" + enum: ["spec", "previous-spec"] + default: "spec" + - name: "rollback" + in: "query" + description: | + Set to this parameter to `previous` to cause a server-side rollback + to the previous service spec. The supplied spec will be ignored in + this case. + type: "string" + - name: "X-Registry-Auth" + in: "header" + description: | + A base64url-encoded auth configuration for pulling from private + registries. + + Refer to the [authentication section](#section/Authentication) for + details. + type: "string" + + tags: ["Service"] + /services/{id}/logs: + get: + summary: "Get service logs" + description: | + Get `stdout` and `stderr` logs from a service. See also + [`/containers/{id}/logs`](#operation/ContainerLogs). + + **Note**: This endpoint works only for services with the `local`, + `json-file` or `journald` logging drivers. + operationId: "ServiceLogs" + responses: + 200: + description: "logs returned as a stream in response body" + schema: + type: "string" + format: "binary" + 404: + description: "no such service" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such service: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID or name of the service" + type: "string" + - name: "details" + in: "query" + description: "Show service context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: "Keep connection after returning logs." + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Service"] + /tasks: + get: + summary: "List tasks" + operationId: "TaskList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Task" + example: + - ID: "0kzzo1i0y4jz6027t0k7aezc7" + Version: + Index: 71 + CreatedAt: "2016-06-07T21:07:31.171892745Z" + UpdatedAt: "2016-06-07T21:07:31.376370513Z" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:31.290032978Z" + State: "running" + Message: "started" + ContainerStatus: + ContainerID: "e5d62702a1b48d01c3e02ca1e0212a250801fa8d67caca0b6f35919ebc12f035" + PID: 677 + DesiredState: "running" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.10/16" + - ID: "1yljwbmlr8er2waf8orvqpwms" + Version: + Index: 30 + CreatedAt: "2016-06-07T21:07:30.019104782Z" + UpdatedAt: "2016-06-07T21:07:30.231958098Z" + Name: "hopeful_cori" + Spec: + ContainerSpec: + Image: "redis" + Resources: + Limits: {} + Reservations: {} + RestartPolicy: + Condition: "any" + MaxAttempts: 0 + Placement: {} + ServiceID: "9mnpnzenvg8p8tdbtq4wvbkcz" + Slot: 1 + NodeID: "60gvrl6tm78dmak4yl7srz94v" + Status: + Timestamp: "2016-06-07T21:07:30.202183143Z" + State: "shutdown" + Message: "shutdown" + ContainerStatus: + ContainerID: "1cf8d63d18e79668b0004a4be4c6ee58cddfad2dae29506d8781581d0688a213" + DesiredState: "shutdown" + NetworksAttachments: + - Network: + ID: "4qvuz4ko70xaltuqbt8956gd1" + Version: + Index: 18 + CreatedAt: "2016-06-07T20:31:11.912919752Z" + UpdatedAt: "2016-06-07T21:07:29.955277358Z" + Spec: + Name: "ingress" + Labels: + com.docker.swarm.internal: "true" + DriverConfiguration: {} + IPAMOptions: + Driver: {} + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + DriverState: + Name: "overlay" + Options: + com.docker.network.driver.overlay.vxlanid_list: "256" + IPAMOptions: + Driver: + Name: "default" + Configs: + - Subnet: "10.255.0.0/16" + Gateway: "10.255.0.1" + Addresses: + - "10.255.0.5/16" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the tasks list. + + Available filters: + + - `desired-state=(running | shutdown | accepted)` + - `id=` + - `label=key` or `label="key=value"` + - `name=` + - `node=` + - `service=` + tags: ["Task"] + /tasks/{id}: + get: + summary: "Inspect a task" + operationId: "TaskInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Task" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "ID of the task" + required: true + type: "string" + tags: ["Task"] + /tasks/{id}/logs: + get: + summary: "Get task logs" + description: | + Get `stdout` and `stderr` logs from a task. + See also [`/containers/{id}/logs`](#operation/ContainerLogs). + + **Note**: This endpoint works only for services with the `local`, + `json-file` or `journald` logging drivers. + operationId: "TaskLogs" + responses: + 200: + description: "logs returned as a stream in response body" + schema: + type: "string" + format: "binary" + 404: + description: "no such task" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such task: c2ada9df5af8" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + description: "ID of the task" + type: "string" + - name: "details" + in: "query" + description: "Show task context and extra details provided to logs." + type: "boolean" + default: false + - name: "follow" + in: "query" + description: "Keep connection after returning logs." + type: "boolean" + default: false + - name: "stdout" + in: "query" + description: "Return logs from `stdout`" + type: "boolean" + default: false + - name: "stderr" + in: "query" + description: "Return logs from `stderr`" + type: "boolean" + default: false + - name: "since" + in: "query" + description: "Only return logs since this time, as a UNIX timestamp" + type: "integer" + default: 0 + - name: "timestamps" + in: "query" + description: "Add timestamps to every log line" + type: "boolean" + default: false + - name: "tail" + in: "query" + description: | + Only return this number of log lines from the end of the logs. + Specify as an integer or `all` to output all log lines. + type: "string" + default: "all" + tags: ["Task"] + /secrets: + get: + summary: "List secrets" + operationId: "SecretList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Secret" + example: + - ID: "blt1owaxmitz71s9v5zh81zun" + Version: + Index: 85 + CreatedAt: "2017-07-20T13:55:28.678958722Z" + UpdatedAt: "2017-07-20T13:55:28.678958722Z" + Spec: + Name: "mysql-passwd" + Labels: + some.label: "some.value" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the secrets list. + + Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Secret"] + /secrets/create: + post: + summary: "Create a secret" + operationId: "SecretCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/SecretSpec" + - type: "object" + example: + Name: "app-key.crt" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + tags: ["Secret"] + /secrets/{id}: + get: + summary: "Inspect a secret" + operationId: "SecretInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Secret" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + Labels: + foo: "bar" + Driver: + Name: "secret-bucket" + Options: + OptionA: "value for driver option A" + OptionB: "value for driver option B" + + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + delete: + summary: "Delete a secret" + operationId: "SecretDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "secret not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the secret" + tags: ["Secret"] + /secrets/{id}/update: + post: + summary: "Update a Secret" + operationId: "SecretUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such secret" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the secret" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/SecretSpec" + description: | + The spec of the secret to update. Currently, only the Labels field + can be updated. All other fields must remain unchanged from the + [SecretInspect endpoint](#operation/SecretInspect) response values. + - name: "version" + in: "query" + description: | + The version number of the secret object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Secret"] + /configs: + get: + summary: "List configs" + operationId: "ConfigList" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + type: "array" + items: + $ref: "#/definitions/Config" + example: + - ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "server.conf" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "filters" + in: "query" + type: "string" + description: | + A JSON encoded value of the filters (a `map[string][]string`) to + process on the configs list. + + Available filters: + + - `id=` + - `label= or label==value` + - `name=` + - `names=` + tags: ["Config"] + /configs/create: + post: + summary: "Create a config" + operationId: "ConfigCreate" + consumes: + - "application/json" + produces: + - "application/json" + responses: + 201: + description: "no error" + schema: + $ref: "#/definitions/IdResponse" + 409: + description: "name conflicts with an existing object" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "body" + in: "body" + schema: + allOf: + - $ref: "#/definitions/ConfigSpec" + - type: "object" + example: + Name: "server.conf" + Labels: + foo: "bar" + Data: "VEhJUyBJUyBOT1QgQSBSRUFMIENFUlRJRklDQVRFCg==" + tags: ["Config"] + /configs/{id}: + get: + summary: "Inspect a config" + operationId: "ConfigInspect" + produces: + - "application/json" + responses: + 200: + description: "no error" + schema: + $ref: "#/definitions/Config" + examples: + application/json: + ID: "ktnbjxoalbkvbvedmg1urrz8h" + Version: + Index: 11 + CreatedAt: "2016-11-05T01:20:17.327670065Z" + UpdatedAt: "2016-11-05T01:20:17.327670065Z" + Spec: + Name: "app-dev.crt" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + delete: + summary: "Delete a config" + operationId: "ConfigDelete" + produces: + - "application/json" + responses: + 204: + description: "no error" + 404: + description: "config not found" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + required: true + type: "string" + description: "ID of the config" + tags: ["Config"] + /configs/{id}/update: + post: + summary: "Update a Config" + operationId: "ConfigUpdate" + responses: + 200: + description: "no error" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 404: + description: "no such config" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + 503: + description: "node is not part of a swarm" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "id" + in: "path" + description: "The ID or name of the config" + type: "string" + required: true + - name: "body" + in: "body" + schema: + $ref: "#/definitions/ConfigSpec" + description: | + The spec of the config to update. Currently, only the Labels field + can be updated. All other fields must remain unchanged from the + [ConfigInspect endpoint](#operation/ConfigInspect) response values. + - name: "version" + in: "query" + description: | + The version number of the config object being updated. This is + required to avoid conflicting writes. + type: "integer" + format: "int64" + required: true + tags: ["Config"] + /distribution/{name}/json: + get: + summary: "Get image information from the registry" + description: | + Return image digest and platform information by contacting the registry. + operationId: "DistributionInspect" + produces: + - "application/json" + responses: + 200: + description: "descriptor and platform information" + schema: + type: "object" + x-go-name: DistributionInspect + title: "DistributionInspectResponse" + required: [Descriptor, Platforms] + properties: + Descriptor: + type: "object" + description: | + A descriptor struct containing digest, media type, and size. + properties: + MediaType: + type: "string" + Size: + type: "integer" + format: "int64" + Digest: + type: "string" + URLs: + type: "array" + items: + type: "string" + Platforms: + type: "array" + description: | + An array containing all platforms supported by the image. + items: + type: "object" + properties: + Architecture: + type: "string" + OS: + type: "string" + OSVersion: + type: "string" + OSFeatures: + type: "array" + items: + type: "string" + Variant: + type: "string" + Features: + type: "array" + items: + type: "string" + examples: + application/json: + Descriptor: + MediaType: "application/vnd.docker.distribution.manifest.v2+json" + Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" + Size: 3987495 + URLs: + - "" + Platforms: + - Architecture: "amd64" + OS: "linux" + OSVersion: "" + OSFeatures: + - "" + Variant: "" + Features: + - "" + 401: + description: "Failed authentication or no image found" + schema: + $ref: "#/definitions/ErrorResponse" + examples: + application/json: + message: "No such image: someimage (tag: latest)" + 500: + description: "Server error" + schema: + $ref: "#/definitions/ErrorResponse" + parameters: + - name: "name" + in: "path" + description: "Image name or id" + type: "string" + required: true + tags: ["Distribution"] + /session: + post: + summary: "Initialize interactive session" + description: | + Start a new interactive session with a server. Session allows server to + call back to the client for advanced capabilities. + + ### Hijacking + + This endpoint hijacks the HTTP connection to HTTP2 transport that allows + the client to expose gPRC services on that connection. + + For example, the client sends this request to upgrade the connection: + + ``` + POST /session HTTP/1.1 + Upgrade: h2c + Connection: Upgrade + ``` + + The Docker daemon responds with a `101 UPGRADED` response follow with + the raw stream: + + ``` + HTTP/1.1 101 UPGRADED + Connection: Upgrade + Upgrade: h2c + ``` + operationId: "Session" + produces: + - "application/vnd.docker.raw-stream" + responses: + 101: + description: "no error, hijacking successful" + 400: + description: "bad parameter" + schema: + $ref: "#/definitions/ErrorResponse" + 500: + description: "server error" + schema: + $ref: "#/definitions/ErrorResponse" + tags: ["Session"] diff --git a/docs/api/version-history.md b/docs/api/version-history.md index 9c9d2f954a11a..baf90efda2d73 100644 --- a/docs/api/version-history.md +++ b/docs/api/version-history.md @@ -13,6 +13,167 @@ keywords: "API, Docker, rcli, REST, documentation" will be rejected. --> +## v1.42 API changes + +[Docker Engine API v1.42](https://docs.docker.com/engine/api/v1.42/) documentation + +* Removed the `BuilderSize` field on the `GET /system/df` endpoint. This field + was introduced in API 1.31 as part of an experimental feature, and no longer + used since API 1.40. + Use field `BuildCache` instead to track storage used by the builder component. +* `GET /images/json` now accepts query parameter `shared-size`. When set `true`, + images returned will include `SharedSize`, which provides the size on disk shared + with other images present on the system. +* `GET /system/df` now accepts query parameter `type`. When set, + computes and returns data only for the specified object type. + The parameter can be specified multiple times to select several object types. + Supported values are: `container`, `image`, `volume`, `build-cache`. +* `GET /system/df` can now be used concurrently. If a request is made while a + previous request is still being processed, the request will receive the result + of the already running calculation, once completed. Previously, an error + (`a disk usage operation is already running`) would be returned in this + situation. This change is not versioned, and affects all API versions if the + daemon has this patch. + +## v1.41 API changes + +[Docker Engine API v1.41](https://docs.docker.com/engine/api/v1.41/) documentation + +* `GET /events` now returns `prune` events after pruning resources have completed. + Prune events are returned for `container`, `network`, `volume`, `image`, and + `builder`, and have a `reclaimed` attribute, indicating the amount of space + reclaimed (in bytes). +* `GET /info` now returns a `CgroupVersion` field, containing the cgroup version. +* `GET /info` now returns a `DefaultAddressPools` field, containing a list of + custom default address pools for local networks, which can be specified in the + `daemon.json` file or `--default-address-pool` dockerd option. +* `POST /services/create` and `POST /services/{id}/update` now supports `BindOptions.NonRecursive`. +* The `ClusterStore` and `ClusterAdvertise` fields in `GET /info` are deprecated + and are now omitted if they contain an empty value. This change is not versioned, + and affects all API versions if the daemon has this patch. +* The `filter` (singular) query parameter, which was deprecated in favor of the + `filters` option in Docker 1.13, has now been removed from the `GET /images/json` + endpoint. The parameter remains available when using API version 1.40 or below. +* `GET /services` now returns `CapAdd` and `CapDrop` as part of the `ContainerSpec`. +* `GET /services/{id}` now returns `CapAdd` and `CapDrop` as part of the `ContainerSpec`. +* `POST /services/create` now accepts `CapAdd` and `CapDrop` as part of the `ContainerSpec`. +* `POST /services/{id}/update` now accepts `CapAdd` and `CapDrop` as part of the `ContainerSpec`. +* `GET /tasks` now returns `CapAdd` and `CapDrop` as part of the `ContainerSpec`. +* `GET /tasks/{id}` now returns `CapAdd` and `CapDrop` as part of the `ContainerSpec`. +* `GET /services` now returns `Pids` in `TaskTemplate.Resources.Limits`. +* `GET /services/{id}` now returns `Pids` in `TaskTemplate.Resources.Limits`. +* `POST /services/create` now accepts `Pids` in `TaskTemplate.Resources.Limits`. +* `POST /services/{id}/update` now accepts `Pids` in `TaskTemplate.Resources.Limits` + to limit the maximum number of PIDs. +* `GET /tasks` now returns `Pids` in `TaskTemplate.Resources.Limits`. +* `GET /tasks/{id}` now returns `Pids` in `TaskTemplate.Resources.Limits`. +* `POST /containers/create` on Linux now accepts the `HostConfig.CgroupnsMode` property. + Set the property to `host` to create the container in the daemon's cgroup namespace, or + `private` to create the container in its own private cgroup namespace. The per-daemon + default is `host`, and can be changed by using the`CgroupNamespaceMode` daemon configuration + parameter. +* `GET /info` now returns an `OSVersion` field, containing the operating system's + version. This change is not versioned, and affects all API versions if the daemon + has this patch. +* `GET /info` no longer returns the `SystemStatus` field if it does not have a + value set. This change is not versioned, and affects all API versions if the + daemon has this patch. +* `GET /services` now accepts query parameter `status`. When set `true`, + services returned will include `ServiceStatus`, which provides Desired, + Running, and Completed task counts for the service. +* `GET /services` may now include `ReplicatedJob` or `GlobalJob` as the `Mode` + in a `ServiceSpec`. +* `GET /services/{id}` may now include `ReplicatedJob` or `GlobalJob` as the + `Mode` in a `ServiceSpec`. +* `POST /services/create` now accepts `ReplicatedJob or `GlobalJob` as the `Mode` + in the `ServiceSpec. +* `POST /services/{id}/update` accepts updating the fields of the + `ReplicatedJob` object in the `ServiceSpec.Mode`. The service mode still + cannot be changed, however. +* `GET /services` now includes `JobStatus` on Services with mode + `ReplicatedJob` or `GlobalJob`. +* `GET /services/{id}` now includes `JobStatus` on Services with mode + `ReplicatedJob` or `GlobalJob`. +* `GET /tasks` now includes `JobIteration` on Tasks spawned from a job-mode + service. +* `GET /tasks/{id}` now includes `JobIteration` on the task if spawned from a + job-mode service. +* `GET /containers/{id}/stats` now accepts a query param (`one-shot`) which, when used with `stream=false` fetches a + single set of stats instead of waiting for two collection cycles to have 2 CPU stats over a 1 second period. +* The `KernelMemory` field in `HostConfig.Resources` is now deprecated. +* The `KernelMemory` field in `Info` is now deprecated. +* `GET /services` now returns `Ulimits` as part of `ContainerSpec`. +* `GET /services/{id}` now returns `Ulimits` as part of `ContainerSpec`. +* `POST /services/create` now accepts `Ulimits` as part of `ContainerSpec`. +* `POST /services/{id}/update` now accepts `Ulimits` as part of `ContainerSpec`. + +## v1.40 API changes + +[Docker Engine API v1.40](https://docs.docker.com/engine/api/v1.40/) documentation + +* The `/_ping` endpoint can now be accessed both using `GET` or `HEAD` requests. + when accessed using a `HEAD` request, all headers are returned, but the body + is empty (`Content-Length: 0`). This change is not versioned, and affects all + API versions if the daemon has this patch. Clients are recommended to try + using `HEAD`, but fallback to `GET` if the `HEAD` requests fails. +* `GET /_ping` and `HEAD /_ping` now set `Cache-Control` and `Pragma` headers to + prevent the result from being cached. This change is not versioned, and affects + all API versions if the daemon has this patch. +* `GET /services` now returns `Sysctls` as part of the `ContainerSpec`. +* `GET /services/{id}` now returns `Sysctls` as part of the `ContainerSpec`. +* `POST /services/create` now accepts `Sysctls` as part of the `ContainerSpec`. +* `POST /services/{id}/update` now accepts `Sysctls` as part of the `ContainerSpec`. +* `POST /services/create` now accepts `Config` as part of `ContainerSpec.Privileges.CredentialSpec`. +* `POST /services/{id}/update` now accepts `Config` as part of `ContainerSpec.Privileges.CredentialSpec`. +* `POST /services/create` now includes `Runtime` as an option in `ContainerSpec.Configs` +* `POST /services/{id}/update` now includes `Runtime` as an option in `ContainerSpec.Configs` +* `GET /tasks` now returns `Sysctls` as part of the `ContainerSpec`. +* `GET /tasks/{id}` now returns `Sysctls` as part of the `ContainerSpec`. +* `GET /networks` now supports a `dangling` filter type. When set to `true` (or + `1`), the endpoint returns all networks that are not in use by a container. When + set to `false` (or `0`), only networks that are in use by one or more containers + are returned. +* `GET /nodes` now supports a filter type `node.label` filter to filter nodes based + on the node.label. The format of the label filter is `node.label=`/`node.label==` + to return those with the specified labels, or `node.label!=`/`node.label!==` + to return those without the specified labels. +* `POST /containers/create` now accepts a `fluentd-async` option in `HostConfig.LogConfig.Config` + when using the Fluentd logging driver. This option deprecates the `fluentd-async-connect` + option, which remains funtional, but will be removed in a future release. Users + are encouraged to use the `fluentd-async` option going forward. This change is + not versioned, and affects all API versions if the daemon has this patch. +* `POST /containers/create` now accepts a `fluentd-request-ack` option in + `HostConfig.LogConfig.Config` when using the Fluentd logging driver. If enabled, + the Fluentd logging driver sends the chunk option with a unique ID. The server + will respond with an acknowledgement. This option improves the reliability of + the message transmission. This change is not versioned, and affects all API + versions if the daemon has this patch. +* `POST /containers/create`, `GET /containers/{id}/json`, and `GET /containers/json` now supports + `BindOptions.NonRecursive`. +* `POST /swarm/init` now accepts a `DataPathPort` property to set data path port number. +* `GET /info` now returns information about `DataPathPort` that is currently used in swarm +* `GET /info` now returns `PidsLimit` boolean to indicate if the host kernel has + PID limit support enabled. +* `GET /info` now includes `name=rootless` in `SecurityOptions` when the daemon is running in + rootless mode. This change is not versioned, and affects all API versions if the daemon has + this patch. +* `GET /info` now returns `none` as `CgroupDriver` when the daemon is running in rootless mode. + This change is not versioned, and affects all API versions if the daemon has this patch. +* `POST /containers/create` now accepts `DeviceRequests` as part of `HostConfig`. + Can be used to set Nvidia GPUs. +* `GET /swarm` endpoint now returns DataPathPort info +* `POST /containers/create` now takes `KernelMemoryTCP` field to set hard limit for kernel TCP buffer memory. +* `GET /service` now returns `MaxReplicas` as part of the `Placement`. +* `GET /service/{id}` now returns `MaxReplicas` as part of the `Placement`. +* `POST /service/create` and `POST /services/(id or name)/update` now take the field `MaxReplicas` + as part of the service `Placement`, allowing to specify maximum replicas per node for the service. +* `POST /containers/create` on Linux now creates a container with `HostConfig.IpcMode=private` + by default, if IpcMode is not explicitly specified. The per-daemon default can be changed + back to `shareable` by using `DefaultIpcMode` daemon configuration parameter. +* `POST /containers/{id}/update` now accepts a `PidsLimit` field to tune a container's + PID limit. Set `0` or `-1` for unlimited. Leave `null` to not change the current value. +* `POST /build` now accepts `outputs` key for configuring build outputs when using BuildKit mode. + ## V1.39 API changes [Docker Engine API v1.39](https://docs.docker.com/engine/api/v1.39/) documentation @@ -26,16 +187,9 @@ keywords: "API, Docker, rcli, REST, documentation" * `POST /swarm/init` now accepts a `DefaultAddrPool` property to set global scope default address pool * `POST /swarm/init` now accepts a `SubnetSize` property to set global scope networks by giving the length of the subnet masks for every such network -* `GET /nodes` now supports a filter type `node.label` filter to filter nodes based - on the node.label. The format of the label filter is `node.label=`/`node.label==` - to return those with the specified labels, or `node.label!=`/`node.label!==` - to return those without the specified labels. -* `GET /services` now returns `Sysctls` as part of the `ContainerSpec`. -* `GET /services/{id}` now returns `Sysctls` as part of the `ContainerSpec`. -* `POST /services/create` now accepts `Sysctls` as part of the `ContainerSpec`. -* `POST /services/{id}/update` now accepts `Sysctls` as part of the `ContainerSpec`. -* `GET /tasks` now returns `Sysctls` as part of the `ContainerSpec`. -* `GET /tasks/{id}` now returns `Sysctls` as part of the `ContainerSpec`. +* `POST /session` (added in [V1.31](#v131-api-changes) is no longer experimental. + This endpoint can be used to run interactive long-running protocols between the + client and the daemon. ## V1.38 API changes @@ -169,6 +323,7 @@ keywords: "API, Docker, rcli, REST, documentation" * `GET /events` now supports service, node and secret events which are emitted when users create, update and remove service, node and secret * `GET /events` now supports network remove event which is emitted when users remove a swarm scoped network * `GET /events` now supports a filter type `scope` in which supported value could be swarm and local +* `PUT /containers/(name)/archive` now accepts a `copyUIDGID` parameter to allow copy UID/GID maps to dest file or dir. ## v1.29 API changes @@ -192,14 +347,14 @@ keywords: "API, Docker, rcli, REST, documentation" * `GET /containers/create` now takes a `DeviceCgroupRules` field in `HostConfig` allowing to set custom device cgroup rules for the created container. * Optional query parameter `verbose` for `GET /networks/(id or name)` will now list all services with all the tasks, including the non-local tasks on the given network. * `GET /containers/(id or name)/attach/ws` now returns WebSocket in binary frame format for API version >= v1.28, and returns WebSocket in text frame format for API version< v1.28, for the purpose of backward-compatibility. -* `GET /networks` is optimised only to return list of all networks and network specific information. List of all containers attached to a specific network is removed from this API and is only available using the network specific `GET /networks/{network-id}. +* `GET /networks` is optimised only to return list of all networks and network specific information. List of all containers attached to a specific network is removed from this API and is only available using the network specific `GET /networks/{network-id}`. * `GET /containers/json` now supports `publish` and `expose` filters to filter containers that expose or publish certain ports. * `POST /services/create` and `POST /services/(id or name)/update` now accept the `ReadOnly` parameter, which mounts the container's root filesystem as read only. * `POST /build` now accepts `extrahosts` parameter to specify a host to ip mapping to use during the build. * `POST /services/create` and `POST /services/(id or name)/update` now accept a `rollback` value for `FailureAction`. * `POST /services/create` and `POST /services/(id or name)/update` now accept an optional `RollbackConfig` object which specifies rollback options. * `GET /services` now supports a `mode` filter to filter services based on the service mode (either `global` or `replicated`). -* `POST /containers/(name)/update` now supports updating `NanoCPUs` that represents CPU quota in units of 10-9 CPUs. +* `POST /containers/(name)/update` now supports updating `NanoCpus` that represents CPU quota in units of 10-9 CPUs. ## v1.27 API changes @@ -255,11 +410,14 @@ keywords: "API, Docker, rcli, REST, documentation" * The `hostConfig` option now accepts the fields `CpuRealtimePeriod` and `CpuRtRuntime` to allocate cpu runtime to rt tasks when `CONFIG_RT_GROUP_SCHED` is enabled in the kernel. * The `SecurityOptions` field within the `GET /info` response now includes `userns` if user namespaces are enabled in the daemon. * `GET /nodes` and `GET /node/(id or name)` now return `Addr` as part of a node's `Status`, which is the address that that node connects to the manager from. -* The `HostConfig` field now includes `NanoCPUs` that represents CPU quota in units of 10-9 CPUs. +* The `HostConfig` field now includes `NanoCpus` that represents CPU quota in units of 10-9 CPUs. * `GET /info` now returns more structured information about security options. * The `HostConfig` field now includes `CpuCount` that represents the number of CPUs available for execution by the container. Windows daemon only. * `POST /services/create` and `POST /services/(id or name)/update` now accept the `TTY` parameter, which allocate a pseudo-TTY in container. * `POST /services/create` and `POST /services/(id or name)/update` now accept the `DNSConfig` parameter, which specifies DNS related configurations in resolver configuration file (resolv.conf) through `Nameservers`, `Search`, and `Options`. +* `POST /services/create` and `POST /services/(id or name)/update` now support + `node.platform.arch` and `node.platform.os` constraints in the services + `TaskSpec.Placement.Constraints` field. * `GET /networks/(id or name)` now includes IP and name of all peers nodes for swarm mode overlay networks. * `GET /plugins` list plugins. * `POST /plugins/pull?name=` pulls a plugin. diff --git a/docs/contributing/software-req-win.md b/docs/contributing/software-req-win.md index 3070d34e8381e..55b370003f964 100644 --- a/docs/contributing/software-req-win.md +++ b/docs/contributing/software-req-win.md @@ -26,7 +26,7 @@ https://git-scm.com/download/win. ### 3. The machine must be configured to run containers For example, by following the quick start guidance at -https://msdn.microsoft.com/en-us/virtualization/windowscontainers/quick_start/quick_start or https://github.com/docker/labs/blob/master/windows/windows-containers/Setup.md +https://msdn.microsoft.com/en-us/virtualization/windowscontainers/quick_start/quick_start or https://github.com/docker/labs/blob/master/windows/windows-containers/README.md ### 4. If building in a Hyper-V VM @@ -51,7 +51,7 @@ To test and run the Windows Moby engine, you need a system that supports Windows - Windows 10 Anniversary Edition - Windows Server 2016 running in a VM, on bare metal or in the cloud -Check out the [getting started documentation](https://github.com/docker/labs/blob/master/windows/windows-containers/Setup.md) for details. +Check out the [getting started documentation](https://github.com/docker/labs/blob/master/windows/windows-containers/README.md) for details. ### 2. GitHub account @@ -100,8 +100,8 @@ To build Moby, run: Copy out the resulting Windows Moby Engine binary to `dockerd.exe` in the current directory: - docker cp binaries:C:\go\src\github.com\docker\docker\bundles\docker.exe docker.exe - docker cp binaries:C:\go\src\github.com\docker\docker\bundles\dockerd.exe dockerd.exe + docker cp binaries:C:\gopath\src\github.com\docker\docker\bundles\docker.exe docker.exe + docker cp binaries:C:\gopath\src\github.com\docker\docker\bundles\dockerd.exe dockerd.exe To test it, stop the system Docker daemon and start the one you just built: @@ -109,7 +109,7 @@ To test it, stop the system Docker daemon and start the one you just built: .\dockerd.exe -D The other make targets work too, to run unit tests try: -`docker run --rm docker-builder sh -c 'cd /c/go/src/github.com/docker/docker; hack/make.sh test-unit'`. +`docker run --rm docker-builder sh -c 'cd /c/gopath/src/github.com/docker/docker; hack/make.sh test-unit'`. ### 6. Remove the interim binaries container @@ -169,7 +169,7 @@ quit due to the use of console hooks which are not available. The docker integration tests do not currently run in a container on Windows, predominantly due to Windows not supporting privileged mode, so anything using a volume would fail. They (along with the rest of the docker CI suite) can be run using -https://github.com/jhowardmsft/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1. +https://github.com/kevpar/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1. ## Where to go next diff --git a/docs/contributing/test.md b/docs/contributing/test.md index 8a50b0678646a..5b66ba78a420e 100644 --- a/docs/contributing/test.md +++ b/docs/contributing/test.md @@ -83,7 +83,7 @@ hour. To run the test suite, do the following: * cross-compiles all the binaries for the various operating systems * runs all the tests in the system - It can take approximate one hour to run all the tests. The time depends + It can take approximately one hour to run all the tests. The time depends on your host performance. The default timeout is 60 minutes, which is defined in `hack/make.sh` (`${TIMEOUT:=60m}`). You can modify the timeout value on the basis of your host performance. When they complete @@ -108,10 +108,10 @@ Try this now. 2. Start a Moby development image. If you are following along with this guide, you should have a - `dry-run-test` image. + `docker-dev:dry-run-test` image. ```bash - $ docker run --privileged --rm -ti -v `pwd`:/go/src/github.com/docker/docker dry-run-test /bin/bash + $ docker run --privileged --rm -ti -v `pwd`:/go/src/github.com/docker/docker docker-dev:dry-run-test /bin/bash ``` 3. Run the unit tests using the `hack/test/unit` script. @@ -148,7 +148,7 @@ You can use the `TESTDIRS` environment variable to run unit tests for a single package. ```bash -$ TESTDIRS='opts' make test-unit +$ TESTDIRS='github.com/docker/docker/opts' make test-unit ``` You can also use the `TESTFLAGS` environment variable to run a single test. The @@ -163,7 +163,7 @@ On unit tests, it's better to use `TESTFLAGS` in combination with `TESTDIRS` to make it quicker to run a specific test. ```bash -$ TESTDIRS='opts' TESTFLAGS='-test.run ^TestValidateIPAddress$' make test-unit +$ TESTDIRS='github.com/docker/docker/opts' TESTFLAGS='-test.run ^TestValidateIPAddress$' make test-unit ``` ## Run integration tests @@ -174,13 +174,13 @@ flag's value is passed as arguments to the `go test` command. For example, from your local host you can run the `TestBuild` test with this command: ```bash -$ TESTFLAGS='-check.f DockerSuite.TestBuild*' make test-integration +$ TESTFLAGS='-test.run TestDockerSuite/TestBuild*' make test-integration ``` To run the same test inside your Docker development container, you do this: ```bash -# TESTFLAGS='-check.f TestBuild*' hack/make.sh binary test-integration +# TESTFLAGS='-test.run TestDockerSuite/TestBuild*' hack/make.sh binary test-integration ``` ## Test the Windows binary against a Linux daemon @@ -208,7 +208,7 @@ run a Bash terminal on Windows. ``` 4. Set `DOCKER_TEST_HOST` to the `tcp://IP_ADDRESS:2376` value; substitute your - Linux machines actual IP address. For example: + Linux machine's actual IP address. For example: ```bash $ export DOCKER_TEST_HOST=tcp://213.124.23.200:2376 @@ -228,16 +228,24 @@ run a Bash terminal on Windows. ``` Should you wish to run a single test such as one with the name - 'TestExample', you can pass in `TESTFLAGS='-check.f TestExample'`. For + 'TestExample', you can pass in `TESTFLAGS='-test.run /TestExample'`. For example ```bash - $ TESTFLAGS='-check.f TestExample' hack/make.sh binary test-integration + $ TESTFLAGS='-test.run /TestExample' hack/make.sh binary test-integration ``` You can now choose to make changes to the Moby source or the tests. If you make any changes, just run these commands again. +## [Public CI infrastructure](ci.docker.com/public) + +The current infrastructure is maintained here: [Moby ci job](https://ci.docker.com/public/job/moby). The Jenkins infrastructure is for the Moby project is maintained and +managed by Docker Inc. All contributions against the Jenkinsfile are +appreciated and welcomed! However we might not be able to fully provide the +infrastructure to test against various architectures in our CI pipelines. All +jobs can be triggered and re-ran by the Moby maintainers + ## Where to go next Congratulations, you have successfully completed the basics you need to diff --git a/docs/rootless.md b/docs/rootless.md new file mode 100644 index 0000000000000..4eada9d03e2d4 --- /dev/null +++ b/docs/rootless.md @@ -0,0 +1,3 @@ +Moved to https://docs.docker.com/go/rootless/ + + diff --git a/errdefs/defs.go b/errdefs/defs.go index e6a2275b2d7a8..61e7456b4ebfe 100644 --- a/errdefs/defs.go +++ b/errdefs/defs.go @@ -43,11 +43,6 @@ type ErrNotModified interface { NotModified() } -// ErrAlreadyExists is a special case of ErrConflict which signals that the desired object already exists -type ErrAlreadyExists interface { - AlreadyExists() -} - // ErrNotImplemented signals that the requested action/feature is not implemented on the system as configured. type ErrNotImplemented interface { NotImplemented() diff --git a/errdefs/helpers.go b/errdefs/helpers.go index 6169c2bc62a59..fe06fb6f703b1 100644 --- a/errdefs/helpers.go +++ b/errdefs/helpers.go @@ -10,10 +10,14 @@ func (e errNotFound) Cause() error { return e.error } +func (e errNotFound) Unwrap() error { + return e.error +} + // NotFound is a helper to create an error of the class with the same name from any error type func NotFound(err error) error { - if err == nil { - return nil + if err == nil || IsNotFound(err) { + return err } return errNotFound{err} } @@ -26,10 +30,14 @@ func (e errInvalidParameter) Cause() error { return e.error } +func (e errInvalidParameter) Unwrap() error { + return e.error +} + // InvalidParameter is a helper to create an error of the class with the same name from any error type func InvalidParameter(err error) error { - if err == nil { - return nil + if err == nil || IsInvalidParameter(err) { + return err } return errInvalidParameter{err} } @@ -42,10 +50,14 @@ func (e errConflict) Cause() error { return e.error } +func (e errConflict) Unwrap() error { + return e.error +} + // Conflict is a helper to create an error of the class with the same name from any error type func Conflict(err error) error { - if err == nil { - return nil + if err == nil || IsConflict(err) { + return err } return errConflict{err} } @@ -58,10 +70,14 @@ func (e errUnauthorized) Cause() error { return e.error } +func (e errUnauthorized) Unwrap() error { + return e.error +} + // Unauthorized is a helper to create an error of the class with the same name from any error type func Unauthorized(err error) error { - if err == nil { - return nil + if err == nil || IsUnauthorized(err) { + return err } return errUnauthorized{err} } @@ -74,8 +90,15 @@ func (e errUnavailable) Cause() error { return e.error } +func (e errUnavailable) Unwrap() error { + return e.error +} + // Unavailable is a helper to create an error of the class with the same name from any error type func Unavailable(err error) error { + if err == nil || IsUnavailable(err) { + return err + } return errUnavailable{err} } @@ -87,10 +110,14 @@ func (e errForbidden) Cause() error { return e.error } +func (e errForbidden) Unwrap() error { + return e.error +} + // Forbidden is a helper to create an error of the class with the same name from any error type func Forbidden(err error) error { - if err == nil { - return nil + if err == nil || IsForbidden(err) { + return err } return errForbidden{err} } @@ -103,10 +130,14 @@ func (e errSystem) Cause() error { return e.error } +func (e errSystem) Unwrap() error { + return e.error +} + // System is a helper to create an error of the class with the same name from any error type func System(err error) error { - if err == nil { - return nil + if err == nil || IsSystem(err) { + return err } return errSystem{err} } @@ -119,28 +150,16 @@ func (e errNotModified) Cause() error { return e.error } -// NotModified is a helper to create an error of the class with the same name from any error type -func NotModified(err error) error { - if err == nil { - return nil - } - return errNotModified{err} -} - -type errAlreadyExists struct{ error } - -func (errAlreadyExists) AlreadyExists() {} - -func (e errAlreadyExists) Cause() error { +func (e errNotModified) Unwrap() error { return e.error } -// AlreadyExists is a helper to create an error of the class with the same name from any error type -func AlreadyExists(err error) error { - if err == nil { - return nil +// NotModified is a helper to create an error of the class with the same name from any error type +func NotModified(err error) error { + if err == nil || IsNotModified(err) { + return err } - return errAlreadyExists{err} + return errNotModified{err} } type errNotImplemented struct{ error } @@ -151,10 +170,14 @@ func (e errNotImplemented) Cause() error { return e.error } +func (e errNotImplemented) Unwrap() error { + return e.error +} + // NotImplemented is a helper to create an error of the class with the same name from any error type func NotImplemented(err error) error { - if err == nil { - return nil + if err == nil || IsNotImplemented(err) { + return err } return errNotImplemented{err} } @@ -167,10 +190,14 @@ func (e errUnknown) Cause() error { return e.error } +func (e errUnknown) Unwrap() error { + return e.error +} + // Unknown is a helper to create an error of the class with the same name from any error type func Unknown(err error) error { - if err == nil { - return nil + if err == nil || IsUnknown(err) { + return err } return errUnknown{err} } @@ -183,10 +210,14 @@ func (e errCancelled) Cause() error { return e.error } +func (e errCancelled) Unwrap() error { + return e.error +} + // Cancelled is a helper to create an error of the class with the same name from any error type func Cancelled(err error) error { - if err == nil { - return nil + if err == nil || IsCancelled(err) { + return err } return errCancelled{err} } @@ -199,10 +230,14 @@ func (e errDeadline) Cause() error { return e.error } +func (e errDeadline) Unwrap() error { + return e.error +} + // Deadline is a helper to create an error of the class with the same name from any error type func Deadline(err error) error { - if err == nil { - return nil + if err == nil || IsDeadline(err) { + return err } return errDeadline{err} } @@ -215,10 +250,14 @@ func (e errDataLoss) Cause() error { return e.error } +func (e errDataLoss) Unwrap() error { + return e.error +} + // DataLoss is a helper to create an error of the class with the same name from any error type func DataLoss(err error) error { - if err == nil { - return nil + if err == nil || IsDataLoss(err) { + return err } return errDataLoss{err} } diff --git a/errdefs/helpers_test.go b/errdefs/helpers_test.go index 8766ba80a88ef..4ae27c717fd9d 100644 --- a/errdefs/helpers_test.go +++ b/errdefs/helpers_test.go @@ -22,6 +22,9 @@ func TestNotFound(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected not found error to match errTest") + } } func TestConflict(t *testing.T) { @@ -35,6 +38,9 @@ func TestConflict(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected conflict error to match errTest") + } } func TestForbidden(t *testing.T) { @@ -48,6 +54,9 @@ func TestForbidden(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected forbidden error to match errTest") + } } func TestInvalidParameter(t *testing.T) { @@ -61,6 +70,9 @@ func TestInvalidParameter(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected invalid argument error to match errTest") + } } func TestNotImplemented(t *testing.T) { @@ -74,6 +86,9 @@ func TestNotImplemented(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected not implemented error to match errTest") + } } func TestNotModified(t *testing.T) { @@ -87,18 +102,8 @@ func TestNotModified(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } -} - -func TestAlreadyExists(t *testing.T) { - if IsAlreadyExists(errTest) { - t.Fatalf("did not expect already exists error, got %T", errTest) - } - e := AlreadyExists(errTest) - if !IsAlreadyExists(e) { - t.Fatalf("expected already exists error, got %T", e) - } - if cause := e.(causal).Cause(); cause != errTest { - t.Fatalf("causual should be errTest, got: %v", cause) + if !errors.Is(e, errTest) { + t.Fatalf("expected not modified error to match errTest") } } @@ -113,6 +118,9 @@ func TestUnauthorized(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected unauthorized error to match errTest") + } } func TestUnknown(t *testing.T) { @@ -126,6 +134,9 @@ func TestUnknown(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected unknown error to match errTest") + } } func TestCancelled(t *testing.T) { @@ -139,6 +150,9 @@ func TestCancelled(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected cancelled error to match errTest") + } } func TestDeadline(t *testing.T) { @@ -152,6 +166,9 @@ func TestDeadline(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected deadline error to match errTest") + } } func TestDataLoss(t *testing.T) { @@ -165,6 +182,9 @@ func TestDataLoss(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected data loss error to match errTest") + } } func TestUnavailable(t *testing.T) { @@ -178,6 +198,9 @@ func TestUnavailable(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected unavaillable error to match errTest") + } } func TestSystem(t *testing.T) { @@ -191,4 +214,7 @@ func TestSystem(t *testing.T) { if cause := e.(causal).Cause(); cause != errTest { t.Fatalf("causual should be errTest, got: %v", cause) } + if !errors.Is(e, errTest) { + t.Fatalf("expected system error to match errTest") + } } diff --git a/errdefs/http_helpers.go b/errdefs/http_helpers.go new file mode 100644 index 0000000000000..73576f1c6e449 --- /dev/null +++ b/errdefs/http_helpers.go @@ -0,0 +1,191 @@ +package errdefs // import "github.com/docker/docker/errdefs" + +import ( + "fmt" + "net/http" + + containerderrors "github.com/containerd/containerd/errdefs" + "github.com/docker/distribution/registry/api/errcode" + "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// GetHTTPErrorStatusCode retrieves status code from error message. +func GetHTTPErrorStatusCode(err error) int { + if err == nil { + logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling") + return http.StatusInternalServerError + } + + var statusCode int + + // Stop right there + // Are you sure you should be adding a new error class here? Do one of the existing ones work? + + // Note that the below functions are already checking the error causal chain for matches. + switch { + case IsNotFound(err): + statusCode = http.StatusNotFound + case IsInvalidParameter(err): + statusCode = http.StatusBadRequest + case IsConflict(err): + statusCode = http.StatusConflict + case IsUnauthorized(err): + statusCode = http.StatusUnauthorized + case IsUnavailable(err): + statusCode = http.StatusServiceUnavailable + case IsForbidden(err): + statusCode = http.StatusForbidden + case IsNotModified(err): + statusCode = http.StatusNotModified + case IsNotImplemented(err): + statusCode = http.StatusNotImplemented + case IsSystem(err) || IsUnknown(err) || IsDataLoss(err) || IsDeadline(err) || IsCancelled(err): + statusCode = http.StatusInternalServerError + default: + statusCode = statusCodeFromGRPCError(err) + if statusCode != http.StatusInternalServerError { + return statusCode + } + statusCode = statusCodeFromContainerdError(err) + if statusCode != http.StatusInternalServerError { + return statusCode + } + statusCode = statusCodeFromDistributionError(err) + if statusCode != http.StatusInternalServerError { + return statusCode + } + if e, ok := err.(causer); ok { + return GetHTTPErrorStatusCode(e.Cause()) + } + + logrus.WithFields(logrus.Fields{ + "module": "api", + "error_type": fmt.Sprintf("%T", err), + }).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err) + } + + if statusCode == 0 { + statusCode = http.StatusInternalServerError + } + + return statusCode +} + +// FromStatusCode creates an errdef error, based on the provided HTTP status-code +func FromStatusCode(err error, statusCode int) error { + if err == nil { + return err + } + switch statusCode { + case http.StatusNotFound: + err = NotFound(err) + case http.StatusBadRequest: + err = InvalidParameter(err) + case http.StatusConflict: + err = Conflict(err) + case http.StatusUnauthorized: + err = Unauthorized(err) + case http.StatusServiceUnavailable: + err = Unavailable(err) + case http.StatusForbidden: + err = Forbidden(err) + case http.StatusNotModified: + err = NotModified(err) + case http.StatusNotImplemented: + err = NotImplemented(err) + case http.StatusInternalServerError: + if !IsSystem(err) && !IsUnknown(err) && !IsDataLoss(err) && !IsDeadline(err) && !IsCancelled(err) { + err = System(err) + } + default: + logrus.WithError(err).WithFields(logrus.Fields{ + "module": "api", + "status_code": statusCode, + }).Debug("FIXME: Got an status-code for which error does not match any expected type!!!") + + switch { + case statusCode >= 200 && statusCode < 400: + // it's a client error + case statusCode >= 400 && statusCode < 500: + err = InvalidParameter(err) + case statusCode >= 500 && statusCode < 600: + err = System(err) + default: + err = Unknown(err) + } + } + return err +} + +// statusCodeFromGRPCError returns status code according to gRPC error +func statusCodeFromGRPCError(err error) int { + switch status.Code(err) { + case codes.InvalidArgument: // code 3 + return http.StatusBadRequest + case codes.NotFound: // code 5 + return http.StatusNotFound + case codes.AlreadyExists: // code 6 + return http.StatusConflict + case codes.PermissionDenied: // code 7 + return http.StatusForbidden + case codes.FailedPrecondition: // code 9 + return http.StatusBadRequest + case codes.Unauthenticated: // code 16 + return http.StatusUnauthorized + case codes.OutOfRange: // code 11 + return http.StatusBadRequest + case codes.Unimplemented: // code 12 + return http.StatusNotImplemented + case codes.Unavailable: // code 14 + return http.StatusServiceUnavailable + default: + // codes.Canceled(1) + // codes.Unknown(2) + // codes.DeadlineExceeded(4) + // codes.ResourceExhausted(8) + // codes.Aborted(10) + // codes.Internal(13) + // codes.DataLoss(15) + return http.StatusInternalServerError + } +} + +// statusCodeFromDistributionError returns status code according to registry errcode +// code is loosely based on errcode.ServeJSON() in docker/distribution +func statusCodeFromDistributionError(err error) int { + switch errs := err.(type) { + case errcode.Errors: + if len(errs) < 1 { + return http.StatusInternalServerError + } + if _, ok := errs[0].(errcode.ErrorCoder); ok { + return statusCodeFromDistributionError(errs[0]) + } + case errcode.ErrorCoder: + return errs.ErrorCode().Descriptor().HTTPStatusCode + } + return http.StatusInternalServerError +} + +// statusCodeFromContainerdError returns status code for containerd errors when +// consumed directly (not through gRPC) +func statusCodeFromContainerdError(err error) int { + switch { + case containerderrors.IsInvalidArgument(err): + return http.StatusBadRequest + case containerderrors.IsNotFound(err): + return http.StatusNotFound + case containerderrors.IsAlreadyExists(err): + return http.StatusConflict + case containerderrors.IsFailedPrecondition(err): + return http.StatusPreconditionFailed + case containerderrors.IsUnavailable(err): + return http.StatusServiceUnavailable + case containerderrors.IsNotImplemented(err): + return http.StatusNotImplemented + default: + return http.StatusInternalServerError + } +} diff --git a/errdefs/http_helpers_test.go b/errdefs/http_helpers_test.go new file mode 100644 index 0000000000000..7806d283901ee --- /dev/null +++ b/errdefs/http_helpers_test.go @@ -0,0 +1,92 @@ +package errdefs + +import ( + "fmt" + "net/http" + "testing" + + "gotest.tools/v3/assert" +) + +func TestFromStatusCode(t *testing.T) { + testErr := fmt.Errorf("some error occurred") + + testCases := []struct { + err error + status int + check func(error) bool + }{ + { + err: testErr, + status: http.StatusNotFound, + check: IsNotFound, + }, + { + err: testErr, + status: http.StatusBadRequest, + check: IsInvalidParameter, + }, + { + err: testErr, + status: http.StatusConflict, + check: IsConflict, + }, + { + err: testErr, + status: http.StatusUnauthorized, + check: IsUnauthorized, + }, + { + err: testErr, + status: http.StatusServiceUnavailable, + check: IsUnavailable, + }, + { + err: testErr, + status: http.StatusForbidden, + check: IsForbidden, + }, + { + err: testErr, + status: http.StatusNotModified, + check: IsNotModified, + }, + { + err: testErr, + status: http.StatusNotImplemented, + check: IsNotImplemented, + }, + { + err: testErr, + status: http.StatusInternalServerError, + check: IsSystem, + }, + { + err: Unknown(testErr), + status: http.StatusInternalServerError, + check: IsUnknown, + }, + { + err: DataLoss(testErr), + status: http.StatusInternalServerError, + check: IsDataLoss, + }, + { + err: Deadline(testErr), + status: http.StatusInternalServerError, + check: IsDeadline, + }, + { + err: Cancelled(testErr), + status: http.StatusInternalServerError, + check: IsCancelled, + }, + } + + for _, tc := range testCases { + t.Run(http.StatusText(tc.status), func(t *testing.T) { + err := FromStatusCode(tc.err, tc.status) + assert.Check(t, tc.check(err), "unexpected error-type %T", err) + }) + } +} diff --git a/errdefs/is.go b/errdefs/is.go index e0513331bbd68..3abf07d0c3570 100644 --- a/errdefs/is.go +++ b/errdefs/is.go @@ -15,7 +15,6 @@ func getImplementer(err error) error { ErrForbidden, ErrSystem, ErrNotModified, - ErrAlreadyExists, ErrNotImplemented, ErrCancelled, ErrDeadline, @@ -77,12 +76,6 @@ func IsNotModified(err error) bool { return ok } -// IsAlreadyExists returns if the passed in error is a AlreadyExists error -func IsAlreadyExists(err error) bool { - _, ok := getImplementer(err).(ErrAlreadyExists) - return ok -} - // IsNotImplemented returns if the passed in error is an ErrNotImplemented func IsNotImplemented(err error) bool { _, ok := getImplementer(err).(ErrNotImplemented) diff --git a/hack/ci/arm b/hack/ci/arm index e60332a60827b..2c7109b8b6397 100755 --- a/hack/ci/arm +++ b/hack/ci/arm @@ -4,7 +4,7 @@ set -eu -o pipefail hack/test/unit -hack/make.sh \ +TEST_SKIP_INTEGRATION_CLI=1 hack/make.sh \ binary-daemon \ dynbinary \ test-integration diff --git a/hack/ci/experimental b/hack/ci/experimental index 9ccbc8425f699..10297c7dc9209 100755 --- a/hack/ci/experimental +++ b/hack/ci/experimental @@ -2,7 +2,7 @@ # Entrypoint for jenkins experimental CI set -eu -o pipefail -export DOCKER_EXPERIMENTAL=y +export DOCKER_EXPERIMENTAL=1 hack/make.sh \ binary-daemon \ diff --git a/hack/ci/janky b/hack/ci/janky index f2bdfbf326f00..ec48165896833 100755 --- a/hack/ci/janky +++ b/hack/ci/janky @@ -4,14 +4,11 @@ set -eu -o pipefail hack/validate/default hack/test/unit -bash <(curl -s https://codecov.io/bash) \ - -f coverage.txt \ - -C "$GIT_SHA1" || \ - echo 'Codecov failed to upload' hack/make.sh \ binary-daemon \ dynbinary \ test-docker-py \ + test-integration-flaky \ test-integration \ cross diff --git a/hack/ci/master b/hack/ci/master new file mode 100755 index 0000000000000..83182c69ec435 --- /dev/null +++ b/hack/ci/master @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Entrypoint for jenkins master CI build +set -eu -o pipefail + +hack/validate/default +hack/test/unit + +hack/make.sh \ + binary-daemon \ + dynbinary \ + test-docker-py \ + test-integration \ + cross diff --git a/hack/ci/windows.ps1 b/hack/ci/windows.ps1 index 393bcbdc2fc20..69b9e461920d7 100644 --- a/hack/ci/windows.ps1 +++ b/hack/ci/windows.ps1 @@ -1,7 +1,7 @@ -# WARNING WARNING WARNING - DO NOT EDIT THIS FILE IN JENKINS DIRECTLY. -# SUBMIT A PR TO https://github.com/jhowardmsft/docker-w2wCIScripts/blob/master/runCI/executeCI.ps1, -# AND MAKE SURE https://github.com/jhowardmsft/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1 -# ISN'T BROKEN!!!!!!! VALIDATE USING A TEST CONTEXT IN JENKINS. THEN COPY/PASTE INTO JENKINS PRODUCTION. +# WARNING: When editing this file, consider submitting a PR to +# https://github.com/kevpar/docker-w2wCIScripts/blob/master/runCI/executeCI.ps1, and make sure that +# https://github.com/kevpar/docker-w2wCIScripts/blob/master/runCI/Invoke-DockerCI.ps1 isn't broken. +# Validate using a test context in Jenkins, then copy/paste into Jenkins production. # # Jenkins CI scripts for Windows to Windows CI (Powershell Version) # By John Howard (@jhowardmsft) January 2016 - bash version; July 2016 Ported to PowerShell @@ -9,11 +9,10 @@ $ErrorActionPreference = 'Stop' $StartTime=Get-Date -# Put up top to be blindingly obvious. The production jenkins.dockerproject.org Linux-container -# CI job is "Docker-PRs-LoW-RS3". Force into LCOW mode for this run, or not. -if ($env:BUILD_TAG -match "-LoW") { $env:LCOW_MODE=1 } -if ($env:BUILD_TAG -match "-WoW") { $env:LCOW_MODE="" } - +Write-Host -ForegroundColor Red "DEBUG: print all environment variables to check how Jenkins runs this script" +$allArgs = [Environment]::GetCommandLineArgs() +Write-Host -ForegroundColor Red $allArgs +Write-Host -ForegroundColor Red "----------------------------------------------------------------------------" # ------------------------------------------------------------------------------------------- # When executed, we rely on four variables being set in the environment: @@ -46,10 +45,23 @@ if ($env:BUILD_TAG -match "-WoW") { $env:LCOW_MODE="" } # TESTRUN_DRIVE\TESTRUN_SUBDIR\CI- or # d:\CI\CI- # +# Optional environment variables help in CI: +# +# BUILD_NUMBER + BRANCH_NAME are optional variables to be added to the directory below TESTRUN_SUBDIR +# to have individual folder per CI build. If some files couldn't be +# cleaned up and we want to re-run the build in CI. +# Hence, the daemon under test is run under +# TESTRUN_DRIVE\TESTRUN_SUBDIR\PR-\ or +# d:\CI\PR-\ +# # In addition, the following variables can control the run configuration: # # DOCKER_DUT_DEBUG if defined starts the daemon under test in debug mode. # +# DOCKER_STORAGE_OPTS comma-separated list of optional storage driver options for the daemon under test +# examples: +# DOCKER_STORAGE_OPTS="size=40G" +# # SKIP_VALIDATION_TESTS if defined skips the validation tests # # SKIP_UNIT_TESTS if defined skips the unit tests @@ -78,17 +90,15 @@ if ($env:BUILD_TAG -match "-WoW") { $env:LCOW_MODE="" } # docker integration tests are also coded to use the same # environment variable, and if no set, defaults to microsoft/windowsservercore # -# LCOW_BASIC_MODE if defined, does very basic LCOW verification. Ultimately we -# want to run the entire CI suite from docker, but that's a way off. -# -# LCOW_MODE if defined, runs the entire CI suite -# +# WINDOWS_BASE_IMAGE_TAG if defined, uses that as the tag name for the base image. +# if no set, defaults to latest +# # ------------------------------------------------------------------------------------------- # # Jenkins Integration. Add a Windows Powershell build step as follows: # # Write-Host -ForegroundColor green "INFO: Jenkins build step starting" -# $CISCRIPT_DEFAULT_LOCATION = "https://raw.githubusercontent.com/jhowardmsft/docker-w2wCIScripts/master/runCI/executeCI.ps1" +# $CISCRIPT_DEFAULT_LOCATION = "https://raw.githubusercontent.com/moby/moby/master/hack/ci/windows.ps1" # $CISCRIPT_LOCAL_LOCATION = "$env:TEMP\executeCI.ps1" # Write-Host -ForegroundColor green "INFO: Removing cached execution script" # Remove-Item $CISCRIPT_LOCAL_LOCATION -Force -ErrorAction SilentlyContinue 2>&1 | Out-Null @@ -105,7 +115,7 @@ if ($env:BUILD_TAG -match "-WoW") { $env:LCOW_MODE="" } # ------------------------------------------------------------------------------------------- -$SCRIPT_VER="28-Aug-2018 09:33 PDT" +$SCRIPT_VER="05-Feb-2019 09:03 PDT" $FinallyColour="Cyan" #$env:DOCKER_DUT_DEBUG="yes" # Comment out to not be in debug mode @@ -119,13 +129,14 @@ $FinallyColour="Cyan" #$env:INTEGRATION_IN_CONTAINER="yes" #$env:WINDOWS_BASE_IMAGE="" #$env:SKIP_COPY_GO="yes" +#$env:INTEGRATION_TESTFLAGS="-test.v" Function Nuke-Everything { $ErrorActionPreference = 'SilentlyContinue' try { - if ($env:SKIP_ALL_CLEANUP -eq $null) { + if ($null -eq $env:SKIP_ALL_CLEANUP) { Write-Host -ForegroundColor green "INFO: Nuke-Everything..." $containerCount = ($(docker ps -aq | Measure-Object -line).Lines) if (-not $LastExitCode -eq 0) { @@ -136,18 +147,14 @@ Function Nuke-Everything { if ($(docker ps -aq | Measure-Object -line).Lines -gt 0) { docker rm -f $(docker ps -aq) } - $imageCount=($(docker images --format "{{.Repository}}:{{.ID}}" | ` - select-string -NotMatch "windowsservercore" | ` - select-string -NotMatch "nanoserver" | ` - select-string -NotMatch "docker" | ` - Measure-Object -line).Lines) + + $allImages = $(docker images --format "{{.Repository}}#{{.ID}}") + $toRemove = ($allImages | Select-String -NotMatch "servercore","nanoserver","docker","busybox") + $imageCount = ($toRemove | Measure-Object -line).Lines + if ($imageCount -gt 0) { Write-Host -Foregroundcolor green "INFO: Non-base image count on control daemon to delete is $imageCount" - docker rmi -f ` - $(docker images --format "{{.Repository}}:{{.ID}}" | ` - select-string -NotMatch "windowsservercore" | ` - select-string -NotMatch "nanoserver" | ` - select-string -NotMatch "docker").ToString().Split(":")[1] + docker rmi -f ($toRemove | Foreach-Object { $_.ToString().Split("#")[1] }) } } else { Write-Host -ForegroundColor Magenta "WARN: Skipping cleanup of images and containers" @@ -160,11 +167,11 @@ Function Nuke-Everything { Stop-Process -Id $p -Force -ErrorAction SilentlyContinue } - if ($pidFile -ne $Null) { + if ($null -ne $pidFile) { Write-Host "INFO: Tidying pidfile $pidfile" if (Test-Path $pidFile) { $p=Get-Content $pidFile -raw - if ($p -ne $null){ + if ($null -ne $p){ Write-Host -ForegroundColor green "INFO: Stopping possible daemon pid $p" taskkill -f -t -pid $p } @@ -172,6 +179,13 @@ Function Nuke-Everything { } } + # Kill any spurious containerd. + $pids=$(get-process | where-object {$_.ProcessName -like 'containerd'}).id + foreach ($p in $pids) { + Write-Host "INFO: Killing containerd with PID $p" + Stop-Process -Id $p -Force -ErrorAction SilentlyContinue + } + Stop-Process -name "cc1" -Force -ErrorAction SilentlyContinue 2>&1 | Out-Null Stop-Process -name "link" -Force -ErrorAction SilentlyContinue 2>&1 | Out-Null Stop-Process -name "compile" -Force -ErrorAction SilentlyContinue 2>&1 | Out-Null @@ -183,14 +197,14 @@ Function Nuke-Everything { Stop-Process -name "tail" -Force -ErrorAction SilentlyContinue 2>&1 | Out-Null # Detach any VHDs - gwmi msvm_mountedstorageimage -namespace root/virtualization/v2 -ErrorAction SilentlyContinue | foreach-object {$_.DetachVirtualHardDisk() } + gwmi msvm_mountedstorageimage -namespace root/virtualization/v2 -ErrorAction SilentlyContinue | foreach-Object {$_.DetachVirtualHardDisk() } # Stop any compute processes Get-ComputeProcess | Stop-ComputeProcess -Force # Delete the directory using our dangerous utility unless told not to if (Test-Path "$env:TESTRUN_DRIVE`:\$env:TESTRUN_SUBDIR") { - if (($env:SKIP_ZAP_DUT -ne $null) -or ($env:SKIP_ALL_CLEANUP -eq $null)) { + if (($null -ne $env:SKIP_ZAP_DUT) -or ($null -eq $env:SKIP_ALL_CLEANUP)) { Write-Host -ForegroundColor Green "INFO: Nuking $env:TESTRUN_DRIVE`:\$env:TESTRUN_SUBDIR" docker-ci-zap "-folder=$env:TESTRUN_DRIVE`:\$env:TESTRUN_SUBDIR" } else { @@ -203,12 +217,8 @@ Function Nuke-Everything { $count=(Get-ChildItem $reg | Measure-Object).Count if ($count -gt 0) { Write-Warning "There are $count NdisAdapters leaked under Psched\Parameters" - if ($env:COMPUTERNAME -match "jenkins-rs1-") { - Write-Warning "Cleaning Psched..." - Get-ChildItem $reg | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue | Out-Null - } else { - Write-Warning "Not cleaning as not a production RS1 server" - } + Write-Warning "Cleaning Psched..." + Get-ChildItem $reg | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue | Out-Null } # TODO: This should be able to be removed in August 2017 update. Only needed for RS1 @@ -216,12 +226,8 @@ Function Nuke-Everything { $count=(Get-ChildItem $reg | Measure-Object).Count if ($count -gt 0) { Write-Warning "There are $count NdisAdapters leaked under WFPLWFS\Parameters" - if ($env:COMPUTERNAME -match "jenkins-rs1-") { - Write-Warning "Cleaning WFPLWFS..." - Get-ChildItem $reg | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue | Out-Null - } else { - Write-Warning "Not cleaning as not a production RS1 server" - } + Write-Warning "Cleaning WFPLWFS..." + Get-ChildItem $reg | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue | Out-Null } } catch { # Don't throw any errors onwards Throw $_ @@ -256,25 +262,37 @@ Try { Get-ChildItem Env: | Out-String # PR - if (-not ($env:PR -eq $Null)) { echo "INFO: PR#$env:PR (https://github.com/docker/docker/pull/$env:PR)" } + if (-not ($null -eq $env:PR)) { Write-Output "INFO: PR#$env:PR (https://github.com/docker/docker/pull/$env:PR)" } # Make sure docker is installed - if ((Get-Command "docker" -ErrorAction SilentlyContinue) -eq $null) { Throw "ERROR: docker is not installed or not found on path" } + if ($null -eq (Get-Command "docker" -ErrorAction SilentlyContinue)) { Throw "ERROR: docker is not installed or not found on path" } # Make sure docker-ci-zap is installed - if ((Get-Command "docker-ci-zap" -ErrorAction SilentlyContinue) -eq $null) { Throw "ERROR: docker-ci-zap is not installed or not found on path" } + if ($null -eq (Get-Command "docker-ci-zap" -ErrorAction SilentlyContinue)) { Throw "ERROR: docker-ci-zap is not installed or not found on path" } + + # Make sure Windows Defender is disabled + $defender = $false + Try { + $status = Get-MpComputerStatus + if ($status) { + if ($status.RealTimeProtectionEnabled) { + $defender = $true + } + } + } Catch {} + if ($defender) { Write-Host -ForegroundColor Magenta "WARN: Windows Defender real time protection is enabled, which may cause some integration tests to fail" } # Make sure SOURCES_DRIVE is set - if ($env:SOURCES_DRIVE -eq $Null) { Throw "ERROR: Environment variable SOURCES_DRIVE is not set" } + if ($null -eq $env:SOURCES_DRIVE) { Throw "ERROR: Environment variable SOURCES_DRIVE is not set" } # Make sure TESTRUN_DRIVE is set - if ($env:TESTRUN_DRIVE -eq $Null) { Throw "ERROR: Environment variable TESTRUN_DRIVE is not set" } + if ($null -eq $env:TESTRUN_DRIVE) { Throw "ERROR: Environment variable TESTRUN_DRIVE is not set" } # Make sure SOURCES_SUBDIR is set - if ($env:SOURCES_SUBDIR -eq $Null) { Throw "ERROR: Environment variable SOURCES_SUBDIR is not set" } + if ($null -eq $env:SOURCES_SUBDIR) { Throw "ERROR: Environment variable SOURCES_SUBDIR is not set" } # Make sure TESTRUN_SUBDIR is set - if ($env:TESTRUN_SUBDIR -eq $Null) { Throw "ERROR: Environment variable TESTRUN_SUBDIR is not set" } + if ($null -eq $env:TESTRUN_SUBDIR) { Throw "ERROR: Environment variable TESTRUN_SUBDIR is not set" } # SOURCES_DRIVE\SOURCES_SUBDIR must be a directory and exist if (-not (Test-Path -PathType Container "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR")) { Throw "ERROR: $env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR must be an existing directory" } @@ -291,12 +309,12 @@ Try { } # Make sure we start at the root of the sources - cd "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR\src\github.com\docker\docker" - Write-Host -ForegroundColor Green "INFO: Running in $(pwd)" + Set-Location "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR\src\github.com\docker\docker" + Write-Host -ForegroundColor Green "INFO: Running in $(Get-Location)" # Make sure we are in repo if (-not (Test-Path -PathType Leaf -Path ".\Dockerfile.windows")) { - Throw "$(pwd) does not contain Dockerfile.windows!" + Throw "$(Get-Location) does not contain Dockerfile.windows!" } Write-Host -ForegroundColor Green "INFO: docker/docker repository was found" @@ -318,28 +336,26 @@ Try { # Try the internal azure CI image version or Microsoft internal corpnet where the base image is already pre-prepared on the disk, # either through Invoke-DockerCI or, in the case of Azure CI servers, baked into the VHD at the same location. if (Test-Path $("$env:SOURCES_DRIVE`:\baseimages\"+$ControlDaemonBaseImage+".tar")) { - - # An optimization for CI servers to copy it to the D: drive which is an SSD. - if ($env:SOURCES_DRIVE -ne $env:TESTRUN_DRIVE) { - $readBaseFrom=$env:TESTRUN_DRIVE - if (!(Test-Path "$env:TESTRUN_DRIVE`:\baseimages")) { - New-Item "$env:TESTRUN_DRIVE`:\baseimages" -type directory | Out-Null - } - if (!(Test-Path "$env:TESTRUN_DRIVE`:\baseimages\windowsservercore.tar")) { - if (Test-Path "$env:SOURCES_DRIVE`:\baseimages\windowsservercore.tar") { - Write-Host -ForegroundColor Green "INFO: Optimisation - copying $env:SOURCES_DRIVE`:\baseimages\windowsservercore.tar to $env:TESTRUN_DRIVE`:\baseimages" - Copy-Item "$env:SOURCES_DRIVE`:\baseimages\windowsservercore.tar" "$env:TESTRUN_DRIVE`:\baseimages" - } - } - if (!(Test-Path "$env:TESTRUN_DRIVE`:\baseimages\nanoserver.tar")) { - if (Test-Path "$env:SOURCES_DRIVE`:\baseimages\nanoserver.tar") { - Write-Host -ForegroundColor Green "INFO: Optimisation - copying $env:SOURCES_DRIVE`:\baseimages\nanoserver.tar to $env:TESTRUN_DRIVE`:\baseimages" - Copy-Item "$env:SOURCES_DRIVE`:\baseimages\nanoserver.tar" "$env:TESTRUN_DRIVE`:\baseimages" - } - } - $readBaseFrom=$env:TESTRUN_DRIVE - } - + # An optimization for CI servers to copy it to the D: drive which is an SSD. + if ($env:SOURCES_DRIVE -ne $env:TESTRUN_DRIVE) { + $readBaseFrom=$env:TESTRUN_DRIVE + if (!(Test-Path "$env:TESTRUN_DRIVE`:\baseimages")) { + New-Item "$env:TESTRUN_DRIVE`:\baseimages" -type directory | Out-Null + } + if (!(Test-Path "$env:TESTRUN_DRIVE`:\baseimages\windowsservercore.tar")) { + if (Test-Path "$env:SOURCES_DRIVE`:\baseimages\windowsservercore.tar") { + Write-Host -ForegroundColor Green "INFO: Optimisation - copying $env:SOURCES_DRIVE`:\baseimages\windowsservercore.tar to $env:TESTRUN_DRIVE`:\baseimages" + Copy-Item "$env:SOURCES_DRIVE`:\baseimages\windowsservercore.tar" "$env:TESTRUN_DRIVE`:\baseimages" + } + } + if (!(Test-Path "$env:TESTRUN_DRIVE`:\baseimages\nanoserver.tar")) { + if (Test-Path "$env:SOURCES_DRIVE`:\baseimages\nanoserver.tar") { + Write-Host -ForegroundColor Green "INFO: Optimisation - copying $env:SOURCES_DRIVE`:\baseimages\nanoserver.tar to $env:TESTRUN_DRIVE`:\baseimages" + Copy-Item "$env:SOURCES_DRIVE`:\baseimages\nanoserver.tar" "$env:TESTRUN_DRIVE`:\baseimages" + } + } + $readBaseFrom=$env:TESTRUN_DRIVE + } Write-Host -ForegroundColor Green "INFO: Loading"$ControlDaemonBaseImage".tar from disk. This may take some time..." $ErrorActionPreference = "SilentlyContinue" docker load -i $("$readBaseFrom`:\baseimages\"+$ControlDaemonBaseImage+".tar") @@ -350,14 +366,16 @@ Try { Write-Host -ForegroundColor Green "INFO: docker load of"$ControlDaemonBaseImage" completed successfully" } else { # We need to docker pull it instead. It will come in directly as microsoft/imagename:latest - Write-Host -ForegroundColor Green $("INFO: Pulling microsoft/"+$ControlDaemonBaseImage+":latest from docker hub. This may take some time...") + Write-Host -ForegroundColor Green $("INFO: Pulling $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG from docker hub. This may take some time...") $ErrorActionPreference = "SilentlyContinue" - docker pull $("microsoft/"+$ControlDaemonBaseImage) + docker pull "$($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG" $ErrorActionPreference = "Stop" if (-not $LastExitCode -eq 0) { - Throw $("ERROR: Failed to docker pull microsoft/"+$ControlDaemonBaseImage+":latest.") + Throw $("ERROR: Failed to docker pull $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG.") } - Write-Host -ForegroundColor Green $("INFO: docker pull of microsoft/"+$ControlDaemonBaseImage+":latest completed successfully") + Write-Host -ForegroundColor Green $("INFO: docker pull of $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG completed successfully") + Write-Host -ForegroundColor Green $("INFO: Tagging $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG as microsoft/$ControlDaemonBaseImage") + docker tag "$($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG" microsoft/$ControlDaemonBaseImage } } else { Write-Host -ForegroundColor Green "INFO: Image"$("microsoft/"+$ControlDaemonBaseImage+":latest")"is already loaded in the control daemon" @@ -380,7 +398,7 @@ Try { Write-Host -ForegroundColor Green "---------------------------------------------------------------------------" Write-Host -ForegroundColor Green " Failed to get a response from the control daemon. It may be down." Write-Host -ForegroundColor Green " Try re-running this CI job, or ask on #docker-maintainers on docker slack" - Write-Host -ForegroundColor Green " to see if the the daemon is running. Also check the service configuration." + Write-Host -ForegroundColor Green " to see if the daemon is running. Also check the service configuration." Write-Host -ForegroundColor Green " DOCKER_HOST is set to $DOCKER_HOST." Write-Host -ForegroundColor Green "---------------------------------------------------------------------------" Write-Host @@ -414,15 +432,22 @@ Try { # Redirect to a temporary location. $TEMPORIG=$env:TEMP - $env:TEMP="$env:TESTRUN_DRIVE`:\$env:TESTRUN_SUBDIR\CI-$COMMITHASH" - $env:LOCALAPPDATA="$TEMP\localappdata" + if ($null -eq $env:BUILD_NUMBER) { + $env:TEMP="$env:TESTRUN_DRIVE`:\$env:TESTRUN_SUBDIR\CI-$COMMITHASH" + } else { + # individual temporary location per CI build that better matches the BUILD_URL + $env:TEMP="$env:TESTRUN_DRIVE`:\$env:TESTRUN_SUBDIR\$env:BRANCH_NAME\$env:BUILD_NUMBER" + } + $env:LOCALAPPDATA="$env:TEMP\localappdata" $errorActionPreference='Stop' New-Item -ItemType Directory "$env:TEMP" -ErrorAction SilentlyContinue | Out-Null New-Item -ItemType Directory "$env:TEMP\userprofile" -ErrorAction SilentlyContinue | Out-Null + New-Item -ItemType Directory "$env:TEMP\testresults" -ErrorAction SilentlyContinue | Out-Null + New-Item -ItemType Directory "$env:TEMP\testresults\unittests" -ErrorAction SilentlyContinue | Out-Null New-Item -ItemType Directory "$env:TEMP\localappdata" -ErrorAction SilentlyContinue | Out-Null New-Item -ItemType Directory "$env:TEMP\binary" -ErrorAction SilentlyContinue | Out-Null New-Item -ItemType Directory "$env:TEMP\installer" -ErrorAction SilentlyContinue | Out-Null - if ($env:SKIP_COPY_GO -eq $null) { + if ($null -eq $env:SKIP_COPY_GO) { # Wipe the previous version of GO - we're going to get it out of the image if (Test-Path "$env:TEMP\go") { Remove-Item "$env:TEMP\go" -Recurse -Force -ErrorAction SilentlyContinue | Out-Null } New-Item -ItemType Directory "$env:TEMP\go" -ErrorAction SilentlyContinue | Out-Null @@ -431,21 +456,10 @@ Try { Write-Host -ForegroundColor Green "INFO: Location for testing is $env:TEMP" # CI Integrity check - ensure Dockerfile.windows and Dockerfile go versions match - $goVersionDockerfileWindows=$(Get-Content ".\Dockerfile.windows" | Select-String "^ENV GO_VERSION").ToString().Replace("ENV GO_VERSION=","").Replace("\","").Replace("``","").Trim() - $goVersionDockerfile=$(Get-Content ".\Dockerfile" | Select-String "^ENV GO_VERSION") - - # As of go 1.11, Dockerfile changed to be in the format like "FROM golang:1.11.0 AS base". - # If a version number ends with .0 (as in 1.11.0, a convention used in golang docker - # image versions), it needs to be removed (i.e. "1.11.0" becomes "1.11"). - if ($goVersionDockerfile -eq $Null) { - $goVersionDockerfile=$(Get-Content ".\Dockerfile" | Select-String "^FROM golang:") - if ($goVersionDockerfile -ne $Null) { - $goVersionDockerfile = $goVersionDockerfile.ToString().Split(" ")[1].Split(":")[1] -replace '\.0$','' - } - } else { - $goVersionDockerfile = $goVersionDockerfile.ToString().Split(" ")[2] - } - if ($goVersionDockerfile -eq $Null) { + $goVersionDockerfileWindows=(Select-String -Path ".\Dockerfile.windows" -Pattern "^ARG[\s]+GO_VERSION=(.*)$").Matches.groups[1].Value + $goVersionDockerfile=(Select-String -Path ".\Dockerfile" -Pattern "^ARG[\s]+GO_VERSION=(.*)$").Matches.groups[1].Value + + if ($null -eq $goVersionDockerfile) { Throw "ERROR: Failed to extract golang version from Dockerfile" } Write-Host -ForegroundColor Green "INFO: Validating GOLang consistency in Dockerfile.windows..." @@ -454,11 +468,11 @@ Try { } # Build the image - if ($env:SKIP_IMAGE_BUILD -eq $null) { + if ($null -eq $env:SKIP_IMAGE_BUILD) { Write-Host -ForegroundColor Cyan "`n`nINFO: Building the image from Dockerfile.windows at $(Get-Date)..." Write-Host $ErrorActionPreference = "SilentlyContinue" - $Duration=$(Measure-Command { docker build -t docker -f Dockerfile.windows . | Out-Host }) + $Duration=$(Measure-Command { docker build --build-arg=GO_VERSION -t docker -f Dockerfile.windows . | Out-Host }) $ErrorActionPreference = "Stop" if (-not($LastExitCode -eq 0)) { Throw "ERROR: Failed to build image from Dockerfile.windows" @@ -469,7 +483,7 @@ Try { } # Following at the moment must be docker\docker as it's dictated by dockerfile.Windows - $contPath="$COMMITHASH`:c`:\go\src\github.com\docker\docker\bundles" + $contPath="$COMMITHASH`:c`:\gopath\src\github.com\docker\docker\bundles" # After https://github.com/docker/docker/pull/30290, .git was added to .dockerignore. Therefore # we have to calculate unsupported outside of the container, and pass the commit ID in through @@ -480,7 +494,7 @@ Try { } # Build the binary in a container unless asked to skip it. - if ($env:SKIP_BINARY_BUILD -eq $null) { + if ($null -eq $env:SKIP_BINARY_BUILD) { Write-Host -ForegroundColor Cyan "`n`nINFO: Building the test binaries at $(Get-Date)..." $ErrorActionPreference = "SilentlyContinue" docker rm -f $COMMITHASH 2>&1 | Out-Null @@ -508,6 +522,21 @@ Try { if (-not($LastExitCode -eq 0)) { Throw "ERROR: Failed to docker cp the daemon binary (dockerd.exe) to $env:TEMP\binary" } + + docker cp "$COMMITHASH`:c`:\gopath\bin\gotestsum.exe" $env:TEMP\binary\ + if (-not (Test-Path "$env:TEMP\binary\gotestsum.exe")) { + Throw "ERROR: gotestsum.exe not found...." ` + } + + docker cp "$COMMITHASH`:c`:\containerd\bin\containerd.exe" $env:TEMP\binary\ + if (-not (Test-Path "$env:TEMP\binary\containerd.exe")) { + Throw "ERROR: containerd.exe not found...." ` + } + docker cp "$COMMITHASH`:c`:\containerd\bin\containerd-shim-runhcs-v1.exe" $env:TEMP\binary\ + if (-not (Test-Path "$env:TEMP\binary\containerd-shim-runhcs-v1.exe")) { + Throw "ERROR: containerd-shim-runhcs-v1.exe not found...." ` + } + $ErrorActionPreference = "Stop" # Copy the built dockerd.exe to dockerd-$COMMITHASH.exe so that easily spotted in task manager. @@ -532,7 +561,7 @@ Try { # Grab the golang installer out of the built image. That way, we know we are consistent once extracted and paths set, # so there's no need to re-deploy on account of an upgrade to the version of GO being used in docker. - if ($env:SKIP_COPY_GO -eq $null) { + if ($null -eq $env:SKIP_COPY_GO) { Write-Host -ForegroundColor Green "INFO: Copying the golang package from the container to $env:TEMP\installer\go.zip..." docker cp "$COMMITHASH`:c`:\go.zip" $env:TEMP\installer\ if (-not($LastExitCode -eq 0)) { @@ -553,14 +582,16 @@ Try { $env:GOPATH="$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR" Write-Host -ForegroundColor Green "INFO: GOPATH=$env:GOPATH" - # Set the path to have the version of go from the image at the front - $env:PATH="$env:TEMP\go\bin;$env:PATH" + # Set the path to have: + # - the version of go from the image at the front + # - the test binaries, not the host ones. + $env:PATH="$env:TEMP\go\bin;$env:TEMP\binary;$env:PATH" # Set the GOROOT to be our copy of go from the image $env:GOROOT="$env:TEMP\go" Write-Host -ForegroundColor Green "INFO: $(go version)" - # Work out the the -H parameter for the daemon under test (DASHH_DUT) and client under test (DASHH_CUT) + # Work out the -H parameter for the daemon under test (DASHH_DUT) and client under test (DASHH_CUT) #$DASHH_DUT="npipe:////./pipe/$COMMITHASH" # Can't do remote named pipe #$ip = (resolve-dnsname $env:COMPUTERNAME -type A -NoHostsFile -LlmnrNetbiosOnly).IPAddress # Useful to tie down $DASHH_CUT="tcp://127.0.0.1`:2357" # Not a typo for 2375! @@ -581,12 +612,27 @@ Try { $dutArgs += "-D" } + # Arguments: Are we starting the daemon under test in containerd mode? + if (-not ("$env:DOCKER_WINDOWS_CONTAINERD_RUNTIME" -eq "")) { + Write-Host -ForegroundColor Green "INFO: Running the daemon under test in containerd mode" + $dutArgs += "--containerd \\.\pipe\containerd-containerd" + } + # Arguments: Are we starting the daemon under test with Hyper-V containers as the default isolation? if (-not ("$env:DOCKER_DUT_HYPERV" -eq "")) { Write-Host -ForegroundColor Green "INFO: Running the daemon under test with Hyper-V containers as the default" $dutArgs += "--exec-opt isolation=hyperv" } + # Arguments: Allow setting optional storage-driver options + # example usage: DDOCKER_STORAGE_OPTS="size=40G" + if (-not ("$env:DOCKER_STORAGE_OPTS" -eq "")) { + Write-Host -ForegroundColor Green "INFO: Running the daemon under test with storage-driver options ${env:DOCKER_STORAGE_OPTS}" + $env:DOCKER_STORAGE_OPTS.Split(",") | ForEach-Object { + $dutArgs += "--storage-opt $_" + } + } + # Start the daemon under test, ensuring everything is redirected to folders under $TEMP. # Important - we launch the -$COMMITHASH version so that we can kill it without # killing the control daemon. @@ -594,9 +640,13 @@ Try { Write-Host -ForegroundColor Green "INFO: Args: $dutArgs" New-Item -ItemType Directory $env:TEMP\daemon -ErrorAction SilentlyContinue | Out-Null - # In LCOW mode, for now we need to set an environment variable before starting the daemon under test - if (($env:LCOW_MODE -ne $Null) -or ($env:LCOW_BASIC_MODE -ne $Null)) { - $env:LCOW_SUPPORTED=1 + # Start containerd first + if (-not ("$env:DOCKER_WINDOWS_CONTAINERD_RUNTIME" -eq "")) { + Start-Process "$env:TEMP\binary\containerd.exe" ` + -ArgumentList "--log-level debug" ` + -RedirectStandardOutput "$env:TEMP\containerd.out" ` + -RedirectStandardError "$env:TEMP\containerd.err" + Write-Host -ForegroundColor Green "INFO: Containerd started successfully." } # Cannot fathom why, but always writes to stderr.... @@ -607,15 +657,10 @@ Try { Write-Host -ForegroundColor Green "INFO: Process started successfully." $daemonStarted=1 - # In LCOW mode, turn off that variable - if (($env:LCOW_MODE -ne $Null) -or ($env:LCOW_BASIC_MODE -ne $Null)) { - $env:LCOW_SUPPORTED="" - } - - # Start tailing the daemon under test if the command is installed - if ((Get-Command "tail" -ErrorAction SilentlyContinue) -ne $null) { - $tail = start-process "tail" -ArgumentList "-f $env:TEMP\dut.out" -ErrorAction SilentlyContinue + if ($null -ne (Get-Command "tail" -ErrorAction SilentlyContinue)) { + Write-Host -ForegroundColor green "INFO: Start tailing logs of the daemon under tests" + $tail = Start-Process "tail" -ArgumentList "-f $env:TEMP\dut.out" -PassThru -ErrorAction SilentlyContinue } # Verify we can get the daemon under test to respond @@ -631,7 +676,6 @@ Try { $tries-- if ($tries -le 0) { - $DumpDaemonLog=1 Throw "ERROR: Failed to get a response from the daemon under test" } Write-Host -NoNewline "." @@ -647,7 +691,6 @@ Try { $ErrorActionPreference = "Stop" if ($LastExitCode -ne 0) { Throw "ERROR: The daemon under test does not appear to be running." - $DumpDaemonLog=1 } Write-Host @@ -659,7 +702,6 @@ Try { $ErrorActionPreference = "Stop" if ($LastExitCode -ne 0) { Throw "ERROR: The daemon under test does not appear to be running." - $DumpDaemonLog=1 } Write-Host @@ -671,65 +713,65 @@ Try { $ErrorActionPreference = "Stop" if ($LastExitCode -ne 0) { Throw "ERROR: The daemon under test does not appear to be running." - $DumpDaemonLog=1 } Write-Host - # Don't need Windows images when in LCOW mode. - if (($env:LCOW_MODE -eq $Null) -and ($env:LCOW_BASIC_MODE -eq $Null)) { - - # Default to windowsservercore for the base image used for the tests. The "docker" image - # and the control daemon use microsoft/windowsservercore regardless. This is *JUST* for the tests. - if ($env:WINDOWS_BASE_IMAGE -eq $Null) { - $env:WINDOWS_BASE_IMAGE="microsoft/windowsservercore" - } + # Default to windowsservercore for the base image used for the tests. The "docker" image + # and the control daemon use microsoft/windowsservercore regardless. This is *JUST* for the tests. + if ($null -eq $env:WINDOWS_BASE_IMAGE) { + $env:WINDOWS_BASE_IMAGE="microsoft/windowsservercore" + } + if ($null -eq $env:WINDOWS_BASE_IMAGE_TAG) { + $env:WINDOWS_BASE_IMAGE_TAG="latest" + } - # Lowercase and make sure it has a microsoft/ prefix - $env:WINDOWS_BASE_IMAGE = $env:WINDOWS_BASE_IMAGE.ToLower() - if ($($env:WINDOWS_BASE_IMAGE -Split "/")[0] -ne "microsoft") { - Throw "ERROR: WINDOWS_BASE_IMAGE should start microsoft/" - } + # Lowercase and make sure it has a microsoft/ prefix + $env:WINDOWS_BASE_IMAGE = $env:WINDOWS_BASE_IMAGE.ToLower() + if (! $($env:WINDOWS_BASE_IMAGE -Split "/")[0] -match "microsoft") { + Throw "ERROR: WINDOWS_BASE_IMAGE should start microsoft/ or mcr.microsoft.com/" + } - Write-Host -ForegroundColor Green "INFO: Base image for tests is $env:WINDOWS_BASE_IMAGE" + Write-Host -ForegroundColor Green "INFO: Base image for tests is $env:WINDOWS_BASE_IMAGE" - $ErrorActionPreference = "SilentlyContinue" - if ($((& "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" images --format "{{.Repository}}:{{.Tag}}" | Select-String $($env:WINDOWS_BASE_IMAGE+":latest") | Measure-Object -Line).Lines) -eq 0) { - # Try the internal azure CI image version or Microsoft internal corpnet where the base image is already pre-prepared on the disk, - # either through Invoke-DockerCI or, in the case of Azure CI servers, baked into the VHD at the same location. - if (Test-Path $("c:\baseimages\"+$($env:WINDOWS_BASE_IMAGE -Split "/")[1]+".tar")) { - Write-Host -ForegroundColor Green "INFO: Loading"$($env:WINDOWS_BASE_IMAGE -Split "/")[1]".tar from disk into the daemon under test. This may take some time..." - $ErrorActionPreference = "SilentlyContinue" - & "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" load -i $("$readBaseFrom`:\baseimages\"+$($env:WINDOWS_BASE_IMAGE -Split "/")[1]+".tar") - $ErrorActionPreference = "Stop" - if (-not $LastExitCode -eq 0) { - Throw $("ERROR: Failed to load $readBaseFrom`:\baseimages\"+$($env:WINDOWS_BASE_IMAGE -Split "/")[1]+".tar into daemon under test") - } - Write-Host -ForegroundColor Green "INFO: docker load of"$($env:WINDOWS_BASE_IMAGE -Split "/")[1]" into daemon under test completed successfully" - } else { - # We need to docker pull it instead. It will come in directly as microsoft/imagename:latest - Write-Host -ForegroundColor Green $("INFO: Pulling "+$env:WINDOWS_BASE_IMAGE+":latest from docker hub into daemon under test. This may take some time...") - $ErrorActionPreference = "SilentlyContinue" - & "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" pull $($env:WINDOWS_BASE_IMAGE) - $ErrorActionPreference = "Stop" - if (-not $LastExitCode -eq 0) { - Throw $("ERROR: Failed to docker pull "+$env:WINDOWS_BASE_IMAGE+":latest into daemon under test.") - } - Write-Host -ForegroundColor Green $("INFO: docker pull of "+$env:WINDOWS_BASE_IMAGE+":latest into daemon under test completed successfully") + $ErrorActionPreference = "SilentlyContinue" + if ($((& "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" images --format "{{.Repository}}:{{.Tag}}" | Select-String "$($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG" | Measure-Object -Line).Lines) -eq 0) { + # Try the internal azure CI image version or Microsoft internal corpnet where the base image is already pre-prepared on the disk, + # either through Invoke-DockerCI or, in the case of Azure CI servers, baked into the VHD at the same location. + if (Test-Path $("c:\baseimages\"+$($env:WINDOWS_BASE_IMAGE -Split "/")[1]+".tar")) { + Write-Host -ForegroundColor Green "INFO: Loading"$($env:WINDOWS_BASE_IMAGE -Split "/")[1]".tar from disk into the daemon under test. This may take some time..." + $ErrorActionPreference = "SilentlyContinue" + & "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" load -i $("$readBaseFrom`:\baseimages\"+$($env:WINDOWS_BASE_IMAGE -Split "/")[1]+".tar") + $ErrorActionPreference = "Stop" + if (-not $LastExitCode -eq 0) { + Throw $("ERROR: Failed to load $readBaseFrom`:\baseimages\"+$($env:WINDOWS_BASE_IMAGE -Split "/")[1]+".tar into daemon under test") } + Write-Host -ForegroundColor Green "INFO: docker load of"$($env:WINDOWS_BASE_IMAGE -Split "/")[1]" into daemon under test completed successfully" } else { - Write-Host -ForegroundColor Green "INFO: Image"$($env:WINDOWS_BASE_IMAGE+":latest")"is already loaded in the daemon under test" + # We need to docker pull it instead. It will come in directly as microsoft/imagename:tagname + Write-Host -ForegroundColor Green $("INFO: Pulling "+$env:WINDOWS_BASE_IMAGE+":"+$env:WINDOWS_BASE_IMAGE_TAG+" from docker hub into daemon under test. This may take some time...") + $ErrorActionPreference = "SilentlyContinue" + & "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" pull "$($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG" + $ErrorActionPreference = "Stop" + if (-not $LastExitCode -eq 0) { + Throw $("ERROR: Failed to docker pull $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG into daemon under test.") + } + Write-Host -ForegroundColor Green $("INFO: docker pull of $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG into daemon under test completed successfully") + Write-Host -ForegroundColor Green $("INFO: Tagging $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG as microsoft/$ControlDaemonBaseImage in daemon under test") + & "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" tag "$($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG" microsoft/$ControlDaemonBaseImage } - - - # Inspect the pulled or loaded image to get the version directly - $ErrorActionPreference = "SilentlyContinue" - $dutimgVersion = $(&"$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" inspect $($env:WINDOWS_BASE_IMAGE) --format "{{.OsVersion}}") - $ErrorActionPreference = "Stop" - Write-Host -ForegroundColor Green $("INFO: Version of "+$env:WINDOWS_BASE_IMAGE+":latest is '"+$dutimgVersion+"'") + } else { + Write-Host -ForegroundColor Green "INFO: Image $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG is already loaded in the daemon under test" } + + # Inspect the pulled or loaded image to get the version directly + $ErrorActionPreference = "SilentlyContinue" + $dutimgVersion = $(&"$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" inspect "$($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG" --format "{{.OsVersion}}") + $ErrorActionPreference = "Stop" + Write-Host -ForegroundColor Green $("INFO: Version of $($env:WINDOWS_BASE_IMAGE):$env:WINDOWS_BASE_IMAGE_TAG is '"+$dutimgVersion+"'") + # Run the validation tests unless SKIP_VALIDATION_TESTS is defined. - if ($env:SKIP_VALIDATION_TESTS -eq $null) { + if ($null -eq $env:SKIP_VALIDATION_TESTS) { Write-Host -ForegroundColor Cyan "INFO: Running validation tests at $(Get-Date)..." $ErrorActionPreference = "SilentlyContinue" $Duration=$(Measure-Command { hack\make.ps1 -DCO -GoFormat -PkgImports | Out-Host }) @@ -742,182 +784,121 @@ Try { Write-Host -ForegroundColor Magenta "WARN: Skipping validation tests" } - # Note the unit tests won't work in LCOW mode as I turned off loading the base images above. # Run the unit tests inside a container unless SKIP_UNIT_TESTS is defined - if (($env:LCOW_MODE -eq $Null) -and ($env:LCOW_BASIC_MODE -eq $Null)) { - if ($env:SKIP_UNIT_TESTS -eq $null) { - Write-Host -ForegroundColor Cyan "INFO: Running unit tests at $(Get-Date)..." - $ErrorActionPreference = "SilentlyContinue" - $Duration=$(Measure-Command {docker run -e DOCKER_GITCOMMIT=$COMMITHASH$CommitUnsupported docker hack\make.ps1 -TestUnit | Out-Host }) - $ErrorActionPreference = "Stop" - if (-not($LastExitCode -eq 0)) { - Throw "ERROR: Unit tests failed" - } - Write-Host -ForegroundColor Green "INFO: Unit tests ended at $(Get-Date). Duration`:$Duration" + if ($null -eq $env:SKIP_UNIT_TESTS) { + $ContainerNameForUnitTests = $COMMITHASH + "_UnitTests" + Write-Host -ForegroundColor Cyan "INFO: Running unit tests at $(Get-Date)..." + $ErrorActionPreference = "SilentlyContinue" + $Duration=$(Measure-Command {docker run --name $ContainerNameForUnitTests -e DOCKER_GITCOMMIT=$COMMITHASH$CommitUnsupported docker hack\make.ps1 -TestUnit | Out-Host }) + $TestRunExitCode = $LastExitCode + $ErrorActionPreference = "Stop" + + # Saving where jenkins will take a look at..... + New-Item -Force -ItemType Directory bundles | Out-Null + $unitTestsContPath="$ContainerNameForUnitTests`:c`:\gopath\src\github.com\docker\docker\bundles" + $JunitExpectedContFilePath = "$unitTestsContPath\junit-report-unit-tests.xml" + docker cp $JunitExpectedContFilePath "bundles" + if (-not($LastExitCode -eq 0)) { + Throw "ERROR: Failed to docker cp the unit tests report ($JunitExpectedContFilePath) to bundles" + } + + if (Test-Path "bundles\junit-report-unit-tests.xml") { + Write-Host -ForegroundColor Magenta "INFO: Unit tests results(bundles\junit-report-unit-tests.xml) exist. pwd=$pwd" } else { - Write-Host -ForegroundColor Magenta "WARN: Skipping unit tests" + Write-Host -ForegroundColor Magenta "ERROR: Unit tests results(bundles\junit-report-unit-tests.xml) do not exist. pwd=$pwd" + } + + if (-not($TestRunExitCode -eq 0)) { + Throw "ERROR: Unit tests failed" } + Write-Host -ForegroundColor Green "INFO: Unit tests ended at $(Get-Date). Duration`:$Duration" + } else { + Write-Host -ForegroundColor Magenta "WARN: Skipping unit tests" } # Add the Windows busybox image. Needed for WCOW integration tests - if (($env:LCOW_MODE -eq $Null) -and ($env:LCOW_BASIC_MODE -eq $Null)) { - if ($env:SKIP_INTEGRATION_TESTS -eq $null) { - $ErrorActionPreference = "SilentlyContinue" - # Build it regardless while switching between nanoserver and windowsservercore - #$bbCount = $(& "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" images | Select-String "busybox" | Measure-Object -line).Lines - #$ErrorActionPreference = "Stop" - #if (-not($LastExitCode -eq 0)) { - # Throw "ERROR: Could not determine if busybox image is present" - #} - #if ($bbCount -eq 0) { - Write-Host -ForegroundColor Green "INFO: Building busybox" - $ErrorActionPreference = "SilentlyContinue" - - # This is a temporary hack for nanoserver - if ($env:WINDOWS_BASE_IMAGE -ne "microsoft/windowsservercore") { - Write-Host -ForegroundColor Red "HACK HACK HACK - Building 64-bit nanoserver busybox image" - $(& "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" build -t busybox https://raw.githubusercontent.com/jhowardmsft/busybox64/master/Dockerfile | Out-Host) - } else { - $(& "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" build -t busybox https://raw.githubusercontent.com/jhowardmsft/busybox/master/Dockerfile | Out-Host) - } - $ErrorActionPreference = "Stop" - if (-not($LastExitCode -eq 0)) { - Throw "ERROR: Failed to build busybox image" - } - #} - + if ($null -eq $env:SKIP_INTEGRATION_TESTS) { + Write-Host -ForegroundColor Green "INFO: Building busybox" + $ErrorActionPreference = "SilentlyContinue" + $(& "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" build -t busybox --build-arg WINDOWS_BASE_IMAGE --build-arg WINDOWS_BASE_IMAGE_TAG "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR\src\github.com\docker\docker\contrib\busybox\" | Out-Host) + $ErrorActionPreference = "Stop" + if (-not($LastExitCode -eq 0)) { + Throw "ERROR: Failed to build busybox image" + } - Write-Host -ForegroundColor Green "INFO: Docker images of the daemon under test" - Write-Host - $ErrorActionPreference = "SilentlyContinue" - & "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" images - $ErrorActionPreference = "Stop" - if ($LastExitCode -ne 0) { - Throw "ERROR: The daemon under test does not appear to be running." - $DumpDaemonLog=1 - } - Write-Host + Write-Host -ForegroundColor Green "INFO: Docker images of the daemon under test" + Write-Host + $ErrorActionPreference = "SilentlyContinue" + & "$env:TEMP\binary\docker-$COMMITHASH" "-H=$($DASHH_CUT)" images + $ErrorActionPreference = "Stop" + if ($LastExitCode -ne 0) { + Throw "ERROR: The daemon under test does not appear to be running." } + Write-Host } # Run the WCOW integration tests unless SKIP_INTEGRATION_TESTS is defined - if (($env:LCOW_MODE -eq $Null) -and ($env:LCOW_BASIC_MODE -eq $Null)) { - if ($env:SKIP_INTEGRATION_TESTS -eq $null) { - Write-Host -ForegroundColor Cyan "INFO: Running integration tests at $(Get-Date)..." - $ErrorActionPreference = "SilentlyContinue" - - # Location of the daemon under test. - $env:OrigDOCKER_HOST="$env:DOCKER_HOST" - - #https://blogs.technet.microsoft.com/heyscriptingguy/2011/09/20/solve-problems-with-external-command-lines-in-powershell/ is useful to see tokenising - $c = "go test " - $c += "`"-check.v`" " - if ($env:INTEGRATION_TEST_NAME -ne $null) { # Makes is quicker for debugging to be able to run only a subset of the integration tests - $c += "`"-check.f`" " - $c += "`"$env:INTEGRATION_TEST_NAME`" " - Write-Host -ForegroundColor Magenta "WARN: Only running integration tests matching $env:INTEGRATION_TEST_NAME" - } - $c += "`"-tags`" " + "`"autogen`" " - $c += "`"-check.timeout`" " + "`"10m`" " - $c += "`"-test.timeout`" " + "`"200m`" " - - if ($env:INTEGRATION_IN_CONTAINER -ne $null) { - Write-Host -ForegroundColor Green "INFO: Integration tests being run inside a container" - # Note we talk back through the containers gateway address - # And the ridiculous lengths we have to go to to get the default gateway address... (GetNetIPConfiguration doesn't work in nanoserver) - # I just could not get the escaping to work in a single command, so output $c to a file and run that in the container instead... - # Not the prettiest, but it works. - $c | Out-File -Force "$env:TEMP\binary\runIntegrationCLI.ps1" - $Duration= $(Measure-Command { & docker run ` - --rm ` - -e c=$c ` - --workdir "c`:\go\src\github.com\docker\docker\integration-cli" ` - -v "$env:TEMP\binary`:c:\target" ` - docker ` - "`$env`:PATH`='c`:\target;'+`$env:PATH`; `$env:DOCKER_HOST`='tcp`://'+(ipconfig | select -last 1).Substring(39)+'`:2357'; c:\target\runIntegrationCLI.ps1" | Out-Host } ) - } else { - Write-Host -ForegroundColor Green "INFO: Integration tests being run from the host:" - cd "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR\src\github.com\docker\docker\integration-cli" - $env:DOCKER_HOST=$DASHH_CUT - $env:PATH="$env:TEMP\binary;$env:PATH;" # Force to use the test binaries, not the host ones. - Write-Host -ForegroundColor Green "INFO: $c" - Write-Host -ForegroundColor Green "INFO: DOCKER_HOST at $DASHH_CUT" - # Explicit to not use measure-command otherwise don't get output as it goes - $start=(Get-Date); Invoke-Expression $c; $Duration=New-Timespan -Start $start -End (Get-Date) - } - $ErrorActionPreference = "Stop" - if (-not($LastExitCode -eq 0)) { - Throw "ERROR: Integration tests failed at $(Get-Date). Duration`:$Duration" - } - Write-Host -ForegroundColor Green "INFO: Integration tests ended at $(Get-Date). Duration`:$Duration" - } else { - Write-Host -ForegroundColor Magenta "WARN: Skipping integration tests" - } - } else { - # The LCOW version of the tests here - if ($env:SKIP_INTEGRATION_TESTS -eq $null) { - Write-Host -ForegroundColor Cyan "INFO: Running LCOW tests at $(Get-Date)..." - - $ErrorActionPreference = "SilentlyContinue" - - # Location of the daemon under test. - $env:OrigDOCKER_HOST="$env:DOCKER_HOST" + if ($null -eq $env:SKIP_INTEGRATION_TESTS) { + Write-Host -ForegroundColor Cyan "INFO: Running integration tests at $(Get-Date)..." + $ErrorActionPreference = "SilentlyContinue" - # Make sure we are pointing at the DUT - $env:DOCKER_HOST=$DASHH_CUT + # Location of the daemon under test. + $env:OrigDOCKER_HOST="$env:DOCKER_HOST" + + #https://blogs.technet.microsoft.com/heyscriptingguy/2011/09/20/solve-problems-with-external-command-lines-in-powershell/ is useful to see tokenising + $jsonFilePath = "..\\bundles\\go-test-report-intcli-tests.json" + $xmlFilePath = "..\\bundles\\junit-report-intcli-tests.xml" + $c = "gotestsum --format=standard-verbose --jsonfile=$jsonFilePath --junitfile=$xmlFilePath -- " + if ($null -ne $env:INTEGRATION_TEST_NAME) { # Makes is quicker for debugging to be able to run only a subset of the integration tests + $c += "`"-test.run`" " + $c += "`"$env:INTEGRATION_TEST_NAME`" " + Write-Host -ForegroundColor Magenta "WARN: Only running integration tests matching $env:INTEGRATION_TEST_NAME" + } + $c += "`"-tags`" " + "`"autogen`" " + $c += "`"-test.timeout`" " + "`"200m`" " + + if ($null -ne $env:INTEGRATION_IN_CONTAINER) { + Write-Host -ForegroundColor Green "INFO: Integration tests being run inside a container" + # Note we talk back through the containers gateway address + # And the ridiculous lengths we have to go to get the default gateway address... (GetNetIPConfiguration doesn't work in nanoserver) + # I just could not get the escaping to work in a single command, so output $c to a file and run that in the container instead... + # Not the prettiest, but it works. + $c | Out-File -Force "$env:TEMP\binary\runIntegrationCLI.ps1" + $Duration= $(Measure-Command { & docker run ` + --rm ` + -e c=$c ` + --workdir "c`:\gopath\src\github.com\docker\docker\integration-cli" ` + -v "$env:TEMP\binary`:c:\target" ` + docker ` + "`$env`:PATH`='c`:\target;'+`$env:PATH`; `$env:DOCKER_HOST`='tcp`://'+(ipconfig | select -last 1).Substring(39)+'`:2357'; c:\target\runIntegrationCLI.ps1" | Out-Host } ) + } else { + $env:DOCKER_HOST=$DASHH_CUT + $env:GO111MODULE="off" Write-Host -ForegroundColor Green "INFO: DOCKER_HOST at $DASHH_CUT" - # Force to use the test binaries, not the host ones. - $env:PATH="$env:TEMP\binary;$env:PATH;" - - if ($env:LCOW_BASIC_MODE -ne $null) { - $wc = New-Object net.webclient - try { - Write-Host -ForegroundColor green "INFO: Downloading latest execution script..." - $wc.Downloadfile("https://raw.githubusercontent.com/jhowardmsft/docker-w2wCIScripts/master/runCI/lcowbasicvalidation.ps1", "$env:TEMP\binary\lcowbasicvalidation.ps1") - } - catch [System.Net.WebException] - { - Throw ("Failed to download: $_") - } - - # Explicit to not use measure-command otherwise don't get output as it goes - $ErrorActionPreference = "Stop" - $start=(Get-Date); Invoke-Expression "powershell $env:TEMP\binary\lcowbasicvalidation.ps1"; $lec=$lastExitCode; $Duration=New-Timespan -Start $start -End (Get-Date) - $Duration=New-Timespan -Start $start -End (Get-Date) - Write-Host -ForegroundColor Green "INFO: LCOW tests ended at $(Get-Date). Duration`:$Duration" - if ($lec -ne 0) { - Throw "LCOW validation tests failed" - } - } else { - #https://blogs.technet.microsoft.com/heyscriptingguy/2011/09/20/solve-problems-with-external-command-lines-in-powershell/ is useful to see tokenising - $c = "go test " - $c += "`"-check.v`" " - if ($env:INTEGRATION_TEST_NAME -ne $null) { # Makes is quicker for debugging to be able to run only a subset of the integration tests - $c += "`"-check.f`" " - $c += "`"$env:INTEGRATION_TEST_NAME`" " - Write-Host -ForegroundColor Magenta "WARN: Only running LCOW integration tests matching $env:INTEGRATION_TEST_NAME" - } - $c += "`"-tags`" " + "`"autogen`" " - $c += "`"-check.timeout`" " + "`"10m`" " - $c += "`"-test.timeout`" " + "`"200m`" " - - Write-Host -ForegroundColor Green "INFO: LCOW Integration tests being run from the host:" - cd "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR\src\github.com\docker\docker\integration-cli" - Write-Host -ForegroundColor Green "INFO: $c" - Write-Host -ForegroundColor Green "INFO: DOCKER_HOST at $DASHH_CUT" - # Explicit to not use measure-command otherwise don't get output as it goes - $start=(Get-Date); Invoke-Expression $c; $Duration=New-Timespan -Start $start -End (Get-Date) - - } + $ErrorActionPreference = "SilentlyContinue" + Write-Host -ForegroundColor Cyan "INFO: Integration API tests being run from the host:" + $start=(Get-Date); Invoke-Expression ".\hack\make.ps1 -TestIntegration"; $Duration=New-Timespan -Start $start -End (Get-Date) + $IntTestsRunResult = $LastExitCode $ErrorActionPreference = "Stop" - if (-not($LastExitCode -eq 0)) { - Throw "ERROR: Integration tests failed at $(Get-Date). Duration`:$Duration" + if (-not($IntTestsRunResult -eq 0)) { + Throw "ERROR: Integration API tests failed at $(Get-Date). Duration`:$Duration" } - Write-Host -ForegroundColor Green "INFO: Integration tests ended at $(Get-Date). Duration`:$Duration" - } else { - Write-Host -ForegroundColor Magenta "WARN: Skipping LCOW tests" + + $ErrorActionPreference = "SilentlyContinue" + Write-Host -ForegroundColor Green "INFO: Integration CLI tests being run from the host:" + Write-Host -ForegroundColor Green "INFO: $c" + Set-Location "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR\src\github.com\docker\docker\integration-cli" + # Explicit to not use measure-command otherwise don't get output as it goes + $start=(Get-Date); Invoke-Expression $c; $Duration=New-Timespan -Start $start -End (Get-Date) } + $ErrorActionPreference = "Stop" + if (-not($LastExitCode -eq 0)) { + Throw "ERROR: Integration CLI tests failed at $(Get-Date). Duration`:$Duration" + } + Write-Host -ForegroundColor Green "INFO: Integration tests ended at $(Get-Date). Duration`:$Duration" + } else { + Write-Host -ForegroundColor Magenta "WARN: Skipping integration tests" } # Docker info now to get counts (after or if jjh/containercounts is merged) @@ -929,7 +910,6 @@ Try { $ErrorActionPreference = "Stop" if ($LastExitCode -ne 0) { Throw "ERROR: The daemon under test does not appear to be running." - $DumpDaemonLog=1 } Write-Host } @@ -937,7 +917,7 @@ Try { # Stop the daemon under test if (Test-Path "$env:TEMP\docker.pid") { $p=Get-Content "$env:TEMP\docker.pid" -raw - if (($p -ne $null) -and ($daemonStarted -eq 1)) { + if (($null -ne $p) -and ($daemonStarted -eq 1)) { Write-Host -ForegroundColor green "INFO: Stopping daemon under test" taskkill -f -t -pid $p #sleep 5 @@ -945,6 +925,12 @@ Try { Remove-Item "$env:TEMP\docker.pid" -force -ErrorAction SilentlyContinue } + # Stop the tail process (if started) + if ($null -ne $tail) { + Write-Host -ForegroundColor green "INFO: Stop tailing logs of the daemon under tests" + Stop-Process -InputObject $tail -Force + } + Write-Host -ForegroundColor Green "INFO: executeCI.ps1 Completed successfully at $(Get-Date)." } Catch [Exception] { @@ -962,36 +948,50 @@ Catch [Exception] { } Finally { $ErrorActionPreference="SilentlyContinue" - $global:ProgressPreference=$origProgressPreference + $global:ProgressPreference=$origProgressPreference Write-Host -ForegroundColor Green "INFO: Tidying up at end of run" # Restore the path - if ($origPath -ne $null) { $env:PATH=$origPath } + if ($null -ne $origPath) { $env:PATH=$origPath } # Restore the DOCKER_HOST - if ($origDOCKER_HOST -ne $null) { $env:DOCKER_HOST=$origDOCKER_HOST } + if ($null -ne $origDOCKER_HOST) { $env:DOCKER_HOST=$origDOCKER_HOST } # Restore the GOROOT and GOPATH variables - if ($origGOROOT -ne $null) { $env:GOROOT=$origGOROOT } - if ($origGOPATH -ne $null) { $env:GOPATH=$origGOPATH } + if ($null -ne $origGOROOT) { $env:GOROOT=$origGOROOT } + if ($null -ne $origGOPATH) { $env:GOPATH=$origGOPATH } - # Dump the daemon log if asked to - if ($daemonStarted -eq 1) { - if ($dumpDaemonLog -eq 1) { - Write-Host -ForegroundColor Cyan "----------- DAEMON LOG ------------" - Get-Content "$env:TEMP\dut.err" -ErrorAction SilentlyContinue | Write-Host -ForegroundColor Cyan - Write-Host -ForegroundColor Cyan "----------- END DAEMON LOG --------" - } + # Dump the daemon log. This will include any possible panic stack in the .err. + if (($daemonStarted -eq 1) -and ($(Get-Item "$env:TEMP\dut.err").Length -gt 0)) { + Write-Host -ForegroundColor Cyan "----------- DAEMON LOG ------------" + Get-Content "$env:TEMP\dut.err" -ErrorAction SilentlyContinue | Write-Host -ForegroundColor Cyan + Write-Host -ForegroundColor Cyan "----------- END DAEMON LOG --------" } # Save the daemon under test log if ($daemonStarted -eq 1) { - Write-Host -ForegroundColor Green "INFO: Saving daemon under test log ($env:TEMP\dut.err) to $TEMPORIG\CIDUT.log" - Copy-Item "$env:TEMP\dut.err" "$TEMPORIG\CIDUT.log" -Force -ErrorAction SilentlyContinue + Set-Location "$env:SOURCES_DRIVE`:\$env:SOURCES_SUBDIR\src\github.com\docker\docker" + Write-Host -ForegroundColor Green "INFO: Saving daemon under test log ($env:TEMP\dut.out) to bundles\CIDUT.out" + Copy-Item "$env:TEMP\dut.out" "bundles\CIDUT.out" -Force -ErrorAction SilentlyContinue + Write-Host -ForegroundColor Green "INFO: Saving daemon under test log ($env:TEMP\dut.err) to bundles\CIDUT.err" + Copy-Item "$env:TEMP\dut.err" "bundles\CIDUT.err" -Force -ErrorAction SilentlyContinue + + Write-Host -ForegroundColor Green "INFO: Saving containerd logs to bundles" + if (Test-Path -Path "$env:TEMP\containerd.out") { + Copy-Item "$env:TEMP\containerd.out" "bundles\containerd.out" -Force -ErrorAction SilentlyContinue + Copy-Item "$env:TEMP\containerd.err" "bundles\containerd.err" -Force -ErrorAction SilentlyContinue + } else { + "" | Out-File -FilePath "bundles\containerd.out" + "" | Out-File -FilePath "bundles\containerd.err" + } } - cd "$env:SOURCES_DRIVE\$env:SOURCES_SUBDIR" -ErrorAction SilentlyContinue + Set-Location "$env:SOURCES_DRIVE\$env:SOURCES_SUBDIR" -ErrorAction SilentlyContinue Nuke-Everything + + # Restore the TEMP path + if ($null -ne $TEMPORIG) { $env:TEMP="$TEMPORIG" } + $Dur=New-TimeSpan -Start $StartTime -End $(Get-Date) Write-Host -ForegroundColor $FinallyColour "`nINFO: executeCI.ps1 exiting at $(date). Duration $dur`n" } diff --git a/hack/dind b/hack/dind index 3254f9dbe7b44..087270a7a8367 100755 --- a/hack/dind +++ b/hack/dind @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh set -e # DinD: a wrapper script which allows docker to be run inside a docker container. @@ -25,6 +25,18 @@ if ! mountpoint -q /tmp; then mount -t tmpfs none /tmp fi +# cgroup v2: enable nesting +if [ -f /sys/fs/cgroup/cgroup.controllers ]; then + # move the processes from the root group to the /init group, + # otherwise writing subtree_control fails with EBUSY. + # An error during moving non-existent process (i.e., "cat") is ignored. + mkdir -p /sys/fs/cgroup/init + xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || : + # enable controllers + sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \ + > /sys/fs/cgroup/cgroup.subtree_control +fi + if [ $# -gt 0 ]; then exec "$@" fi diff --git a/hack/dockerfile/install/containerd.installer b/hack/dockerfile/install/containerd.installer index 811348fce2e65..ca5cc7cce039c 100755 --- a/hack/dockerfile/install/containerd.installer +++ b/hack/dockerfile/install/containerd.installer @@ -1,36 +1,42 @@ #!/bin/sh - - -# containerd is also pinned in vendor.conf. When updating the binary -# version you may also need to update the vendor version to pick up bug -# fixes or new APIs. -CONTAINERD_COMMIT=9f2e07b1fc1342d1c48fe4d7bbb94cb6d1bf278b # v1.1.4 - -install_containerd() { - echo "Install containerd version $CONTAINERD_COMMIT" +set -e + +# CONTAINERD_VERSION specifies the version of the containerd runtime binary +# to install from the https://github.com/containerd/containerd repository. +# +# This version is used to build statically compiled containerd binaries, and +# used for the integration tests. The distributed docker .deb and .rpm packages +# depend on a separate (containerd.io) package, which may be a different version +# as is specified here. +# +# Generally, the commit specified here should match a tagged release. +# +# The containerd golang package is also pinned in vendor.conf. When updating +# the binary version you may also need to update the vendor version to pick up +# bug fixes or new APIs, however, usually the Go packages are built from a +# commit from the master branch. +: "${CONTAINERD_VERSION:=v1.5.8}" + +install_containerd() ( + echo "Install containerd version $CONTAINERD_VERSION" git clone https://github.com/containerd/containerd.git "$GOPATH/src/github.com/containerd/containerd" cd "$GOPATH/src/github.com/containerd/containerd" - git checkout -q "$CONTAINERD_COMMIT" - - ( - - export BUILDTAGS='netgo osusergo static_build' - export EXTRA_FLAGS='-buildmode=pie' - export EXTRA_LDFLAGS='-extldflags "-fno-PIC -static"' - - # Reset build flags to nothing if we want a dynbinary - if [ "$1" == "dynamic" ]; then - export BUILDTAGS='' - export EXTRA_FLAGS='' - export EXTRA_LDFLAGS='' - fi - - make - ) - - mkdir -p ${PREFIX} - - cp bin/containerd ${PREFIX}/containerd - cp bin/containerd-shim ${PREFIX}/containerd-shim - cp bin/ctr ${PREFIX}/ctr -} + git checkout -q "$CONTAINERD_VERSION" + + export BUILDTAGS='netgo osusergo static_build' + export EXTRA_FLAGS=${GO_BUILDMODE} + export EXTRA_LDFLAGS='-extldflags "-fno-PIC -static"' + + # Reset build flags to nothing if we want a dynbinary + if [ "$1" = "dynamic" ]; then + export BUILDTAGS='' + export EXTRA_FLAGS='' + export EXTRA_LDFLAGS='' + fi + make + + install -D bin/containerd "${PREFIX}/containerd" + install -D bin/containerd-shim "${PREFIX}/containerd-shim" + install -D bin/containerd-shim-runc-v2 "${PREFIX}/containerd-shim-runc-v2" + install -D bin/ctr "${PREFIX}/ctr" +) diff --git a/hack/dockerfile/install/dockercli.installer b/hack/dockerfile/install/dockercli.installer index ae3aa0dd458b6..6e26bf3181d28 100755 --- a/hack/dockerfile/install/dockercli.installer +++ b/hack/dockerfile/install/dockercli.installer @@ -1,23 +1,22 @@ #!/bin/sh -DOCKERCLI_CHANNEL=${DOCKERCLI_CHANNEL:-edge} -DOCKERCLI_VERSION=${DOCKERCLI_VERSION:-17.06.0-ce} +: ${DOCKERCLI_CHANNEL:=stable} +: ${DOCKERCLI_VERSION:=17.06.2-ce} install_dockercli() { echo "Install docker/cli version $DOCKERCLI_VERSION from $DOCKERCLI_CHANNEL" arch=$(uname -m) # No official release of these platforms - if [[ "$arch" != "x86_64" ]] && [[ "$arch" != "s390x" ]]; then + if [ "$arch" != "x86_64" ] && [ "$arch" != "s390x" ] && [ "$arch" != "armhf" ]; then build_dockercli return fi url=https://download.docker.com/linux/static - curl -Ls $url/$DOCKERCLI_CHANNEL/$arch/docker-$DOCKERCLI_VERSION.tgz | \ - tar -xz docker/docker - mkdir -p ${PREFIX} - mv docker/docker ${PREFIX}/ + curl -Ls "${url}/${DOCKERCLI_CHANNEL}/${arch}/docker-${DOCKERCLI_VERSION}.tgz" | tar -xz docker/docker + mkdir -p "${PREFIX}" + mv docker/docker "${PREFIX}/" rmdir docker } @@ -27,5 +26,5 @@ build_dockercli() { git checkout -q "v$DOCKERCLI_VERSION" mkdir -p "$GOPATH/src/github.com/docker" mv components/cli "$GOPATH/src/github.com/docker/cli" - go build -buildmode=pie -o ${PREFIX}/docker github.com/docker/cli/cmd/docker + go build ${GO_BUILDMODE} -o "${PREFIX}/docker" "github.com/docker/cli/cmd/docker" } diff --git a/hack/dockerfile/install/gometalinter.installer b/hack/dockerfile/install/gometalinter.installer deleted file mode 100755 index d921fd739add3..0000000000000 --- a/hack/dockerfile/install/gometalinter.installer +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -GOMETALINTER_COMMIT=v2.0.6 - -install_gometalinter() { - echo "Installing gometalinter version $GOMETALINTER_COMMIT" - go get -d github.com/alecthomas/gometalinter - cd "$GOPATH/src/github.com/alecthomas/gometalinter" - git checkout -q "$GOMETALINTER_COMMIT" - go build -buildmode=pie -o ${PREFIX}/gometalinter github.com/alecthomas/gometalinter - GOBIN=${PREFIX} ${PREFIX}/gometalinter --install -} diff --git a/hack/dockerfile/install/install.sh b/hack/dockerfile/install/install.sh index a0ff09da559da..7629acd8003b1 100755 --- a/hack/dockerfile/install/install.sh +++ b/hack/dockerfile/install/install.sh @@ -15,6 +15,15 @@ if [ -z "$TMP_GOPATH" ]; then else export GOPATH="$TMP_GOPATH" fi +case "$(go env GOARCH)" in + mips* | ppc64) + # pie build mode is not supported on mips architectures + export GO_BUILDMODE="" + ;; + *) + export GO_BUILDMODE="-buildmode=pie" + ;; +esac dir="$(dirname $0)" @@ -26,5 +35,5 @@ if [ ! -f "${dir}/${bin}.installer" ]; then exit 1 fi -. $dir/$bin.installer -install_$bin "$@" +. ${dir}/${bin}.installer +install_${bin} "$@" diff --git a/hack/dockerfile/install/proxy.installer b/hack/dockerfile/install/proxy.installer deleted file mode 100755 index 859336ee54913..0000000000000 --- a/hack/dockerfile/install/proxy.installer +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh - -# LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When -# updating the binary version, consider updating github.com/docker/libnetwork -# in vendor.conf accordingly -LIBNETWORK_COMMIT=d7b61745d16675c9f548b19f06fda80d422a74f0 - -install_proxy() { - case "$1" in - "dynamic") - install_proxy_dynamic - return - ;; - "") - export CGO_ENABLED=0 - _install_proxy - ;; - *) - echo 'Usage: $0 [dynamic]' - ;; - esac -} - -install_proxy_dynamic() { - export PROXY_LDFLAGS="-linkmode=external" install_proxy - export BUILD_MODE="-buildmode=pie" - _install_proxy -} - -_install_proxy() { - echo "Install docker-proxy version $LIBNETWORK_COMMIT" - git clone https://github.com/docker/libnetwork.git "$GOPATH/src/github.com/docker/libnetwork" - cd "$GOPATH/src/github.com/docker/libnetwork" - git checkout -q "$LIBNETWORK_COMMIT" - go build $BUILD_MODE -ldflags="$PROXY_LDFLAGS" -o ${PREFIX}/docker-proxy github.com/docker/libnetwork/cmd/proxy -} - - diff --git a/hack/dockerfile/install/rootlesskit.installer b/hack/dockerfile/install/rootlesskit.installer new file mode 100755 index 0000000000000..665e2dbc50c1e --- /dev/null +++ b/hack/dockerfile/install/rootlesskit.installer @@ -0,0 +1,32 @@ +#!/bin/sh + +: "${ROOTLESSKIT_VERSION:=v0.14.6}" + +install_rootlesskit() { + case "$1" in + "dynamic") + install_rootlesskit_dynamic + return + ;; + "") + export CGO_ENABLED=0 + _install_rootlesskit + ;; + *) + echo 'Usage: $0 [dynamic]' + ;; + esac +} + +install_rootlesskit_dynamic() { + export ROOTLESSKIT_LDFLAGS="-linkmode=external" install_rootlesskit + export BUILD_MODE=${GO_BUILDMODE} + _install_rootlesskit +} + +_install_rootlesskit() ( + echo "Install rootlesskit version ${ROOTLESSKIT_VERSION}" + for f in rootlesskit rootlesskit-docker-proxy; do + GOBIN="${PREFIX}" GO111MODULE=on go install ${BUILD_MODE} -ldflags="$ROOTLESSKIT_LDFLAGS" "github.com/rootless-containers/rootlesskit/cmd/${f}@${ROOTLESSKIT_VERSION}" + done +) diff --git a/hack/dockerfile/install/runc.installer b/hack/dockerfile/install/runc.installer index ed483e0f40c65..bd07d74e82dd8 100755 --- a/hack/dockerfile/install/runc.installer +++ b/hack/dockerfile/install/runc.installer @@ -1,22 +1,29 @@ #!/bin/sh +set -e -# When updating RUNC_COMMIT, also update runc in vendor.conf accordingly -RUNC_COMMIT=69663f0bd4b60df09991c08812a60108003fa340 +# RUNC_VERSION specifies the version of runc to install from the +# https://github.com/opencontainers/runc repository. +# +# The version of runc should match the version that is used by the containerd +# version that is used. If you need to update runc, open a pull request in +# the containerd project first, and update both after that is merged. +# +# When updating RUNC_VERSION, consider updating runc in vendor.conf accordingly +: "${RUNC_VERSION:=v1.0.3}" install_runc() { - # Do not build with ambient capabilities support - RUNC_BUILDTAGS="${RUNC_BUILDTAGS:-"seccomp apparmor selinux"}" + RUNC_BUILDTAGS="${RUNC_BUILDTAGS:-"seccomp"}" - echo "Install runc version $RUNC_COMMIT" + echo "Install runc version $RUNC_VERSION (build tags: $RUNC_BUILDTAGS)" git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" cd "$GOPATH/src/github.com/opencontainers/runc" - git checkout -q "$RUNC_COMMIT" + git checkout -q "$RUNC_VERSION" if [ -z "$1" ]; then target=static else target="$1" fi make BUILDTAGS="$RUNC_BUILDTAGS" "$target" - mkdir -p ${PREFIX} - cp runc ${PREFIX}/runc + mkdir -p "${PREFIX}" + cp runc "${PREFIX}/runc" } diff --git a/hack/dockerfile/install/tini.installer b/hack/dockerfile/install/tini.installer index 34f43f15f4a23..febceb15eb2a0 100755 --- a/hack/dockerfile/install/tini.installer +++ b/hack/dockerfile/install/tini.installer @@ -1,14 +1,17 @@ #!/bin/sh -TINI_COMMIT=fec3683b971d9c3ef73f284f176672c44b448662 # v0.18.0 +# TINI_VERSION specifies the version of tini (docker-init) to build, and install +# from the https://github.com/krallin/tini repository. This binary is used +# when starting containers with the `--init` option. +: "${TINI_VERSION:=v0.19.0}" install_tini() { - echo "Install tini version $TINI_COMMIT" + echo "Install tini version $TINI_VERSION" git clone https://github.com/krallin/tini.git "$GOPATH/tini" cd "$GOPATH/tini" - git checkout -q "$TINI_COMMIT" + git checkout -q "$TINI_VERSION" cmake . make tini-static - mkdir -p ${PREFIX} - cp tini-static ${PREFIX}/docker-init + mkdir -p "${PREFIX}" + cp tini-static "${PREFIX}/docker-init" } diff --git a/hack/dockerfile/install/tomlv.installer b/hack/dockerfile/install/tomlv.installer deleted file mode 100755 index c926454f22ed4..0000000000000 --- a/hack/dockerfile/install/tomlv.installer +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -# When updating TOMLV_COMMIT, consider updating github.com/BurntSushi/toml -# in vendor.conf accordingly -TOMLV_COMMIT=a368813c5e648fee92e5f6c30e3944ff9d5e8895 - -install_tomlv() { - echo "Install tomlv version $TOMLV_COMMIT" - git clone https://github.com/BurntSushi/toml.git "$GOPATH/src/github.com/BurntSushi/toml" - cd "$GOPATH/src/github.com/BurntSushi/toml" && git checkout -q "$TOMLV_COMMIT" - go build -v -buildmode=pie -o ${PREFIX}/tomlv github.com/BurntSushi/toml/cmd/tomlv -} diff --git a/hack/dockerfile/install/vndr.installer b/hack/dockerfile/install/vndr.installer deleted file mode 100755 index e6a94e7071dab..0000000000000 --- a/hack/dockerfile/install/vndr.installer +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -VNDR_COMMIT=81cb8916aad3c8d06193f008dba3e16f82851f52 - -install_vndr() { - echo "Install vndr version $VNDR_COMMIT" - git clone https://github.com/LK4D4/vndr.git "$GOPATH/src/github.com/LK4D4/vndr" - cd "$GOPATH/src/github.com/LK4D4/vndr" - git checkout -q "$VNDR_COMMIT" - go build -buildmode=pie -v -o ${PREFIX}/vndr . -} diff --git a/hack/generate-authors.sh b/hack/generate-authors.sh index 680bdb7b3f746..dc42294052dcd 100755 --- a/hack/generate-authors.sh +++ b/hack/generate-authors.sh @@ -6,9 +6,9 @@ cd "$(dirname "$(readlink -f "$BASH_SOURCE")")/.." # see also ".mailmap" for how email addresses and names are deduplicated { - cat <<-'EOH' - # This file lists all individuals having contributed content to the repository. - # For how it is generated, see `hack/generate-authors.sh`. + cat <<- 'EOH' + # This file lists all individuals having contributed content to the repository. + # For how it is generated, see `hack/generate-authors.sh`. EOH echo git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf diff --git a/hack/generate-swagger-api.sh b/hack/generate-swagger-api.sh index a01a57387aad9..90a01df02757b 100755 --- a/hack/generate-swagger-api.sh +++ b/hack/generate-swagger-api.sh @@ -2,26 +2,26 @@ set -eu swagger generate model -f api/swagger.yaml \ - -t api -m types --skip-validator -C api/swagger-gen.yaml \ - -n ErrorResponse \ - -n GraphDriverData \ - -n IdResponse \ - -n ImageDeleteResponseItem \ - -n ImageSummary \ - -n Plugin -n PluginDevice -n PluginMount -n PluginEnv -n PluginInterfaceType \ - -n Port \ - -n ServiceUpdateResponse \ - -n Volume + -t api -m types --skip-validator -C api/swagger-gen.yaml \ + -n ErrorResponse \ + -n GraphDriverData \ + -n IdResponse \ + -n ImageDeleteResponseItem \ + -n ImageSummary \ + -n Plugin -n PluginDevice -n PluginMount -n PluginEnv -n PluginInterfaceType \ + -n Port \ + -n ServiceUpdateResponse \ + -n Volume swagger generate operation -f api/swagger.yaml \ - -t api -a types -m types -C api/swagger-gen.yaml \ - -T api/templates --skip-responses --skip-parameters --skip-validator \ - -n Authenticate \ - -n ContainerChanges \ - -n ContainerCreate \ - -n ContainerTop \ - -n ContainerUpdate \ - -n ContainerWait \ - -n ImageHistory \ - -n VolumeCreate \ - -n VolumeList + -t api -a types -m types -C api/swagger-gen.yaml \ + -T api/templates --skip-responses --skip-parameters --skip-validator \ + -n Authenticate \ + -n ContainerChanges \ + -n ContainerCreate \ + -n ContainerTop \ + -n ContainerUpdate \ + -n ContainerWait \ + -n ImageHistory \ + -n VolumeCreate \ + -n VolumeList diff --git a/hack/generate-test-certs.sh b/hack/generate-test-certs.sh new file mode 100755 index 0000000000000..2a5347903940c --- /dev/null +++ b/hack/generate-test-certs.sh @@ -0,0 +1,87 @@ +#!/bin/bash +set -eu + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" + +# integration/testdata/https (and integration-cli/fixtures/https, which has symlinks to these files) +OUT_DIR="${SCRIPT_DIR}/../integration/testdata/https" + +# generate CA +echo 01 > "${OUT_DIR}/ca.srl" +openssl genrsa -out "${OUT_DIR}/ca-key.pem" + +openssl req \ + -new \ + -x509 \ + -days 3652 \ + -subj "/C=US/ST=CA/L=SanFrancisco/O=Moby-project/OU=ci/CN=moby-ci/name=moby/emailAddress=moby@example.org" \ + -nameopt compat \ + -text \ + -key "${OUT_DIR}/ca-key.pem" \ + -out "${OUT_DIR}/ca.pem" + +# Now that we have a CA, create a server key and certificate signing request. +# Make sure that `"Common Name (e.g. server FQDN or YOUR name)"` matches the hostname you will use +# to connect or just use '*' for a certificate valid for any hostname: + +openssl genrsa -out server-key.pem +openssl req -new \ + -subj "/C=US/ST=CA/L=SanFrancisco/O=Moby-project/OU=ci/CN=server/name=moby/emailAddress=moby@example.org" \ + -text \ + -key "${OUT_DIR}/server-key.pem" \ + -out "${OUT_DIR}/server.csr" + +# Options for server certificate +cat > "${OUT_DIR}/server-options.cfg" << 'EOF' +basicConstraints=CA:FALSE +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer +extendedKeyUsage=serverAuth +subjectAltName=DNS:*,DNS:localhost,IP:127.0.0.1,IP:::1 +EOF + +# Generate the certificate and sign with our CA +openssl x509 \ + -req \ + -days 3652 \ + -extfile "${OUT_DIR}/server-options.cfg" \ + -CA "${OUT_DIR}/ca.pem" \ + -CAkey "${OUT_DIR}/ca-key.pem" \ + -nameopt compat \ + -text \ + -in "${OUT_DIR}/server.csr" \ + -out "${OUT_DIR}/server-cert.pem" + +# For client authentication, create a client key and certificate signing request +openssl genrsa -out "${OUT_DIR}/client-key.pem" +openssl req -new \ + -subj "/C=US/ST=CA/L=SanFrancisco/O=Moby-project/OU=ci/CN=client/name=moby/emailAddress=moby@example.org" \ + -text \ + -key "${OUT_DIR}/client-key.pem" \ + -out "${OUT_DIR}/client.csr" + +# Options for client certificate +cat > "${OUT_DIR}/client-options.cfg" << 'EOF' +basicConstraints=CA:FALSE +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer +extendedKeyUsage=clientAuth +subjectAltName=DNS:*,DNS:localhost,IP:127.0.0.1,IP:::1 +EOF + +# Generate the certificate and sign with our CA: +openssl x509 \ + -req \ + -days 3652 \ + -extfile "${OUT_DIR}/client-options.cfg" \ + -CA "${OUT_DIR}/ca.pem" \ + -CAkey "${OUT_DIR}/ca-key.pem" \ + -nameopt compat \ + -text \ + -in "${OUT_DIR}/client.csr" \ + -out "${OUT_DIR}/client-cert.pem" + +rm "${OUT_DIR}/ca.srl" +rm "${OUT_DIR}/ca-key.pem" +rm "${OUT_DIR}"/*.cfg +rm "${OUT_DIR}"/*.csr diff --git a/hack/integration-cli-on-swarm/README.md b/hack/integration-cli-on-swarm/README.md deleted file mode 100644 index 852b36cea16ab..0000000000000 --- a/hack/integration-cli-on-swarm/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# Integration Testing on Swarm - -IT on Swarm allows you to execute integration test in parallel across a Docker Swarm cluster - -## Architecture - -### Master service - - - Works as a funker caller - - Calls a worker funker (`-worker-service`) with a chunk of `-check.f` filter strings (passed as a file via `-input` flag, typically `/mnt/input`) - -### Worker service - - - Works as a funker callee - - Executes an equivalent of `TESTFLAGS=-check.f TestFoo|TestBar|TestBaz ... make test-integration` using the bind-mounted API socket (`docker.sock`) - -### Client - - - Controls master and workers via `docker stack` - - No need to have a local daemon - -Typically, the master and workers are supposed to be running on a cloud environment, -while the client is supposed to be running on a laptop, e.g. Docker for Mac/Windows. - -## Requirement - - - Docker daemon 1.13 or later - - Private registry for distributed execution with multiple nodes - -## Usage - -### Step 1: Prepare images - - $ make build-integration-cli-on-swarm - -Following environment variables are known to work in this step: - - - `BUILDFLAGS` - -Note: during the transition into Moby Project, you might need to create a symbolic link `$GOPATH/src/github.com/docker/docker` to `$GOPATH/src/github.com/moby/moby`. - -### Step 2: Execute tests - - $ ./hack/integration-cli-on-swarm/integration-cli-on-swarm -replicas 40 -push-worker-image YOUR_REGISTRY.EXAMPLE.COM/integration-cli-worker:latest - -Following environment variables are known to work in this step: - - - `DOCKER_GRAPHDRIVER` - - `DOCKER_EXPERIMENTAL` - -#### Flags - -Basic flags: - - - `-replicas N`: the number of worker service replicas. i.e. degree of parallelism. - - `-chunks N`: the number of chunks. By default, `chunks` == `replicas`. - - `-push-worker-image REGISTRY/IMAGE:TAG`: push the worker image to the registry. Note that if you have only single node and hence you do not need a private registry, you do not need to specify `-push-worker-image`. - -Experimental flags for mitigating makespan nonuniformity: - - - `-shuffle`: Shuffle the test filter strings - -Flags for debugging IT on Swarm itself: - - - `-rand-seed N`: the random seed. This flag is useful for deterministic replaying. By default(0), the timestamp is used. - - `-filters-file FILE`: the file contains `-check.f` strings. By default, the file is automatically generated. - - `-dry-run`: skip the actual workload - - `keep-executor`: do not auto-remove executor containers, which is used for running privileged programs on Swarm diff --git a/hack/integration-cli-on-swarm/agent/Dockerfile b/hack/integration-cli-on-swarm/agent/Dockerfile deleted file mode 100644 index 1ae228f6ef105..0000000000000 --- a/hack/integration-cli-on-swarm/agent/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -# this Dockerfile is solely used for the master image. -# Please refer to the top-level Makefile for the worker image. -FROM golang:1.7 -ADD . /go/src/github.com/docker/docker/hack/integration-cli-on-swarm/agent -RUN go build -buildmode=pie -o /master github.com/docker/docker/hack/integration-cli-on-swarm/agent/master -ENTRYPOINT ["/master"] diff --git a/hack/integration-cli-on-swarm/agent/master/call.go b/hack/integration-cli-on-swarm/agent/master/call.go deleted file mode 100644 index dab9c6707778d..0000000000000 --- a/hack/integration-cli-on-swarm/agent/master/call.go +++ /dev/null @@ -1,132 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/bfirsh/funker-go" - "github.com/docker/docker/hack/integration-cli-on-swarm/agent/types" -) - -const ( - // funkerRetryTimeout is for the issue https://github.com/bfirsh/funker/issues/3 - // When all the funker replicas are busy in their own job, we cannot connect to funker. - funkerRetryTimeout = 1 * time.Hour - funkerRetryDuration = 1 * time.Second -) - -// ticker is needed for some CI (e.g., on Travis, job is aborted when no output emitted for 10 minutes) -func ticker(d time.Duration) chan struct{} { - t := time.NewTicker(d) - stop := make(chan struct{}) - go func() { - for { - select { - case <-t.C: - log.Printf("tick (just for keeping CI job active) per %s", d.String()) - case <-stop: - t.Stop() - } - } - }() - return stop -} - -func executeTests(funkerName string, testChunks [][]string) error { - tickerStopper := ticker(9*time.Minute + 55*time.Second) - defer func() { - close(tickerStopper) - }() - begin := time.Now() - log.Printf("Executing %d chunks in parallel, against %q", len(testChunks), funkerName) - var wg sync.WaitGroup - var passed, failed uint32 - for chunkID, tests := range testChunks { - log.Printf("Executing chunk %d (contains %d test filters)", chunkID, len(tests)) - wg.Add(1) - go func(chunkID int, tests []string) { - defer wg.Done() - chunkBegin := time.Now() - result, err := executeTestChunkWithRetry(funkerName, types.Args{ - ChunkID: chunkID, - Tests: tests, - }) - if result.RawLog != "" { - for _, s := range strings.Split(result.RawLog, "\n") { - log.Printf("Log (chunk %d): %s", chunkID, s) - } - } - if err != nil { - log.Printf("Error while executing chunk %d: %v", - chunkID, err) - atomic.AddUint32(&failed, 1) - } else { - if result.Code == 0 { - atomic.AddUint32(&passed, 1) - } else { - atomic.AddUint32(&failed, 1) - } - log.Printf("Finished chunk %d [%d/%d] with %d test filters in %s, code=%d.", - chunkID, passed+failed, len(testChunks), len(tests), - time.Since(chunkBegin), result.Code) - } - }(chunkID, tests) - } - wg.Wait() - // TODO: print actual tests rather than chunks - log.Printf("Executed %d chunks in %s. PASS: %d, FAIL: %d.", - len(testChunks), time.Since(begin), passed, failed) - if failed > 0 { - return fmt.Errorf("%d chunks failed", failed) - } - return nil -} - -func executeTestChunk(funkerName string, args types.Args) (types.Result, error) { - ret, err := funker.Call(funkerName, args) - if err != nil { - return types.Result{}, err - } - tmp, err := json.Marshal(ret) - if err != nil { - return types.Result{}, err - } - var result types.Result - err = json.Unmarshal(tmp, &result) - return result, err -} - -func executeTestChunkWithRetry(funkerName string, args types.Args) (types.Result, error) { - begin := time.Now() - for i := 0; time.Since(begin) < funkerRetryTimeout; i++ { - result, err := executeTestChunk(funkerName, args) - if err == nil { - log.Printf("executeTestChunk(%q, %d) returned code %d in trial %d", funkerName, args.ChunkID, result.Code, i) - return result, nil - } - if errorSeemsInteresting(err) { - log.Printf("Error while calling executeTestChunk(%q, %d), will retry (trial %d): %v", - funkerName, args.ChunkID, i, err) - } - // TODO: non-constant sleep - time.Sleep(funkerRetryDuration) - } - return types.Result{}, fmt.Errorf("could not call executeTestChunk(%q, %d) in %v", funkerName, args.ChunkID, funkerRetryTimeout) -} - -// errorSeemsInteresting returns true if err does not seem about https://github.com/bfirsh/funker/issues/3 -func errorSeemsInteresting(err error) bool { - boringSubstrs := []string{"connection refused", "connection reset by peer", "no such host", "transport endpoint is not connected", "no route to host"} - errS := err.Error() - for _, boringS := range boringSubstrs { - if strings.Contains(errS, boringS) { - return false - } - } - return true -} diff --git a/hack/integration-cli-on-swarm/agent/master/master.go b/hack/integration-cli-on-swarm/agent/master/master.go deleted file mode 100644 index a0d9a0d38131b..0000000000000 --- a/hack/integration-cli-on-swarm/agent/master/master.go +++ /dev/null @@ -1,65 +0,0 @@ -package main - -import ( - "errors" - "flag" - "io/ioutil" - "log" - "strings" -) - -func main() { - if err := xmain(); err != nil { - log.Fatalf("fatal error: %v", err) - } -} - -func xmain() error { - workerService := flag.String("worker-service", "", "Name of worker service") - chunks := flag.Int("chunks", 0, "Number of chunks") - input := flag.String("input", "", "Path to input file") - randSeed := flag.Int64("rand-seed", int64(0), "Random seed") - shuffle := flag.Bool("shuffle", false, "Shuffle the input so as to mitigate makespan nonuniformity") - flag.Parse() - if *workerService == "" { - return errors.New("worker-service unset") - } - if *chunks == 0 { - return errors.New("chunks unset") - } - if *input == "" { - return errors.New("input unset") - } - - tests, err := loadTests(*input) - if err != nil { - return err - } - testChunks := chunkTests(tests, *chunks, *shuffle, *randSeed) - log.Printf("Loaded %d tests (%d chunks)", len(tests), len(testChunks)) - return executeTests(*workerService, testChunks) -} - -func chunkTests(tests []string, numChunks int, shuffle bool, randSeed int64) [][]string { - // shuffling (experimental) mitigates makespan nonuniformity - // Not sure this can cause some locality problem.. - if shuffle { - shuffleStrings(tests, randSeed) - } - return chunkStrings(tests, numChunks) -} - -func loadTests(filename string) ([]string, error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - var tests []string - for _, line := range strings.Split(string(b), "\n") { - s := strings.TrimSpace(line) - if s != "" { - tests = append(tests, s) - } - } - return tests, nil -} diff --git a/hack/integration-cli-on-swarm/agent/master/set.go b/hack/integration-cli-on-swarm/agent/master/set.go deleted file mode 100644 index d28c41da7f107..0000000000000 --- a/hack/integration-cli-on-swarm/agent/master/set.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "math/rand" -) - -// chunkStrings chunks the string slice -func chunkStrings(x []string, numChunks int) [][]string { - var result [][]string - chunkSize := (len(x) + numChunks - 1) / numChunks - for i := 0; i < len(x); i += chunkSize { - ub := i + chunkSize - if ub > len(x) { - ub = len(x) - } - result = append(result, x[i:ub]) - } - return result -} - -// shuffleStrings shuffles strings -func shuffleStrings(x []string, seed int64) { - r := rand.New(rand.NewSource(seed)) - for i := range x { - j := r.Intn(i + 1) - x[i], x[j] = x[j], x[i] - } -} diff --git a/hack/integration-cli-on-swarm/agent/master/set_test.go b/hack/integration-cli-on-swarm/agent/master/set_test.go deleted file mode 100644 index c172562b1b0ab..0000000000000 --- a/hack/integration-cli-on-swarm/agent/master/set_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package main - -import ( - "fmt" - "reflect" - "testing" - "time" -) - -func generateInput(inputLen int) []string { - var input []string - for i := 0; i < inputLen; i++ { - input = append(input, fmt.Sprintf("s%d", i)) - } - - return input -} - -func testChunkStrings(t *testing.T, inputLen, numChunks int) { - t.Logf("inputLen=%d, numChunks=%d", inputLen, numChunks) - input := generateInput(inputLen) - result := chunkStrings(input, numChunks) - t.Logf("result has %d chunks", len(result)) - var inputReconstructedFromResult []string - for i, chunk := range result { - t.Logf("chunk %d has %d elements", i, len(chunk)) - inputReconstructedFromResult = append(inputReconstructedFromResult, chunk...) - } - if !reflect.DeepEqual(input, inputReconstructedFromResult) { - t.Fatal("input != inputReconstructedFromResult") - } -} - -func TestChunkStrings_4_4(t *testing.T) { - testChunkStrings(t, 4, 4) -} - -func TestChunkStrings_4_1(t *testing.T) { - testChunkStrings(t, 4, 1) -} - -func TestChunkStrings_1_4(t *testing.T) { - testChunkStrings(t, 1, 4) -} - -func TestChunkStrings_1000_8(t *testing.T) { - testChunkStrings(t, 1000, 8) -} - -func TestChunkStrings_1000_9(t *testing.T) { - testChunkStrings(t, 1000, 9) -} - -func testShuffleStrings(t *testing.T, inputLen int, seed int64) { - t.Logf("inputLen=%d, seed=%d", inputLen, seed) - x := generateInput(inputLen) - shuffleStrings(x, seed) - t.Logf("shuffled: %v", x) -} - -func TestShuffleStrings_100(t *testing.T) { - testShuffleStrings(t, 100, time.Now().UnixNano()) -} diff --git a/hack/integration-cli-on-swarm/agent/types/types.go b/hack/integration-cli-on-swarm/agent/types/types.go deleted file mode 100644 index fc598f0330bcb..0000000000000 --- a/hack/integration-cli-on-swarm/agent/types/types.go +++ /dev/null @@ -1,18 +0,0 @@ -package types - -// Args is the type for funker args -type Args struct { - // ChunkID is an unique number of the chunk - ChunkID int `json:"chunk_id"` - // Tests is the set of the strings that are passed as `-check.f` filters - Tests []string `json:"tests"` -} - -// Result is the type for funker result -type Result struct { - // ChunkID corresponds to Args.ChunkID - ChunkID int `json:"chunk_id"` - // Code is the exit code - Code int `json:"code"` - RawLog string `json:"raw_log"` -} diff --git a/hack/integration-cli-on-swarm/agent/vendor.conf b/hack/integration-cli-on-swarm/agent/vendor.conf deleted file mode 100644 index efd6d6d049d3f..0000000000000 --- a/hack/integration-cli-on-swarm/agent/vendor.conf +++ /dev/null @@ -1,2 +0,0 @@ -# dependencies specific to worker (i.e. github.com/docker/docker/...) are not vendored here -github.com/bfirsh/funker-go eaa0a2e06f30e72c9a0b7f858951e581e26ef773 diff --git a/hack/integration-cli-on-swarm/agent/vendor/github.com/bfirsh/funker-go/LICENSE b/hack/integration-cli-on-swarm/agent/vendor/github.com/bfirsh/funker-go/LICENSE deleted file mode 100644 index 75191a4dc711f..0000000000000 --- a/hack/integration-cli-on-swarm/agent/vendor/github.com/bfirsh/funker-go/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - Copyright 2016 Docker, Inc. - - 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. diff --git a/hack/integration-cli-on-swarm/agent/vendor/github.com/bfirsh/funker-go/call.go b/hack/integration-cli-on-swarm/agent/vendor/github.com/bfirsh/funker-go/call.go deleted file mode 100644 index ac0338d2d7ae4..0000000000000 --- a/hack/integration-cli-on-swarm/agent/vendor/github.com/bfirsh/funker-go/call.go +++ /dev/null @@ -1,50 +0,0 @@ -package funker - -import ( - "encoding/json" - "io/ioutil" - "net" - "time" -) - -// Call a Funker function -func Call(name string, args interface{}) (interface{}, error) { - argsJSON, err := json.Marshal(args) - if err != nil { - return nil, err - } - - addr, err := net.ResolveTCPAddr("tcp", name+":9999") - if err != nil { - return nil, err - } - - conn, err := net.DialTCP("tcp", nil, addr) - if err != nil { - return nil, err - } - // Keepalive is a workaround for docker/docker#29655 . - // The implementation of FIN_WAIT2 seems weird on Swarm-mode. - // It seems always refuseing any packet after 60 seconds. - // - // TODO: remove this workaround if the issue gets resolved on the Docker side - if err := conn.SetKeepAlive(true); err != nil { - return nil, err - } - if err := conn.SetKeepAlivePeriod(30 * time.Second); err != nil { - return nil, err - } - if _, err = conn.Write(argsJSON); err != nil { - return nil, err - } - if err = conn.CloseWrite(); err != nil { - return nil, err - } - retJSON, err := ioutil.ReadAll(conn) - if err != nil { - return nil, err - } - var ret interface{} - err = json.Unmarshal(retJSON, &ret) - return ret, err -} diff --git a/hack/integration-cli-on-swarm/agent/vendor/github.com/bfirsh/funker-go/handle.go b/hack/integration-cli-on-swarm/agent/vendor/github.com/bfirsh/funker-go/handle.go deleted file mode 100644 index 89878994dc999..0000000000000 --- a/hack/integration-cli-on-swarm/agent/vendor/github.com/bfirsh/funker-go/handle.go +++ /dev/null @@ -1,54 +0,0 @@ -package funker - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net" - "reflect" -) - -// Handle a Funker function. -func Handle(handler interface{}) error { - handlerValue := reflect.ValueOf(handler) - handlerType := handlerValue.Type() - if handlerType.Kind() != reflect.Func || handlerType.NumIn() != 1 || handlerType.NumOut() != 1 { - return fmt.Errorf("Handler must be a function with a single parameter and single return value.") - } - argsValue := reflect.New(handlerType.In(0)) - - listener, err := net.Listen("tcp", ":9999") - if err != nil { - return err - } - conn, err := listener.Accept() - if err != nil { - return err - } - // We close listener, because we only allow single request. - // Note that TCP "backlog" cannot be used for that purpose. - // http://www.perlmonks.org/?node_id=940662 - if err = listener.Close(); err != nil { - return err - } - argsJSON, err := ioutil.ReadAll(conn) - if err != nil { - return err - } - err = json.Unmarshal(argsJSON, argsValue.Interface()) - if err != nil { - return err - } - - ret := handlerValue.Call([]reflect.Value{argsValue.Elem()})[0].Interface() - retJSON, err := json.Marshal(ret) - if err != nil { - return err - } - - if _, err = conn.Write(retJSON); err != nil { - return err - } - - return conn.Close() -} diff --git a/hack/integration-cli-on-swarm/agent/worker/executor.go b/hack/integration-cli-on-swarm/agent/worker/executor.go deleted file mode 100644 index eef80d461edb8..0000000000000 --- a/hack/integration-cli-on-swarm/agent/worker/executor.go +++ /dev/null @@ -1,118 +0,0 @@ -package main - -import ( - "bytes" - "context" - "fmt" - "io" - "os" - "strings" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/mount" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/stdcopy" -) - -// testChunkExecutor executes integration-cli binary. -// image needs to be the worker image itself. testFlags are OR-set of regexp for filtering tests. -type testChunkExecutor func(image string, tests []string) (int64, string, error) - -func dryTestChunkExecutor() testChunkExecutor { - return func(image string, tests []string) (int64, string, error) { - return 0, fmt.Sprintf("DRY RUN (image=%q, tests=%v)", image, tests), nil - } -} - -// privilegedTestChunkExecutor invokes a privileged container from the worker -// service via bind-mounted API socket so as to execute the test chunk -func privilegedTestChunkExecutor(autoRemove bool) testChunkExecutor { - return func(image string, tests []string) (int64, string, error) { - cli, err := client.NewEnvClient() - if err != nil { - return 0, "", err - } - // propagate variables from the host (needs to be defined in the compose file) - experimental := os.Getenv("DOCKER_EXPERIMENTAL") - graphdriver := os.Getenv("DOCKER_GRAPHDRIVER") - if graphdriver == "" { - info, err := cli.Info(context.Background()) - if err != nil { - return 0, "", err - } - graphdriver = info.Driver - } - // `daemon_dest` is similar to `$DEST` (e.g. `bundles/VERSION/test-integration`) - // but it exists outside of `bundles` so as to make `$DOCKER_GRAPHDRIVER` work. - // - // Without this hack, `$DOCKER_GRAPHDRIVER` fails because of (e.g.) `overlay2 is not supported over overlayfs` - // - // see integration-cli/daemon/daemon.go - daemonDest := "/daemon_dest" - config := container.Config{ - Image: image, - Env: []string{ - "TESTFLAGS=-check.f " + strings.Join(tests, "|"), - "KEEPBUNDLE=1", - "DOCKER_INTEGRATION_TESTS_VERIFIED=1", // for avoiding rebuilding integration-cli - "DOCKER_EXPERIMENTAL=" + experimental, - "DOCKER_GRAPHDRIVER=" + graphdriver, - "DOCKER_INTEGRATION_DAEMON_DEST=" + daemonDest, - }, - Labels: map[string]string{ - "org.dockerproject.integration-cli-on-swarm": "", - "org.dockerproject.integration-cli-on-swarm.comment": "this non-service container is created for running privileged programs on Swarm. you can remove this container manually if the corresponding service is already stopped.", - }, - Entrypoint: []string{"hack/dind"}, - Cmd: []string{"hack/make.sh", "test-integration"}, - } - hostConfig := container.HostConfig{ - AutoRemove: autoRemove, - Privileged: true, - Mounts: []mount.Mount{ - { - Type: mount.TypeVolume, - Target: daemonDest, - }, - }, - } - id, stream, err := runContainer(context.Background(), cli, config, hostConfig) - if err != nil { - return 0, "", err - } - var b bytes.Buffer - teeContainerStream(&b, os.Stdout, os.Stderr, stream) - resultC, errC := cli.ContainerWait(context.Background(), id, "") - select { - case err := <-errC: - return 0, "", err - case result := <-resultC: - return result.StatusCode, b.String(), nil - } - } -} - -func runContainer(ctx context.Context, cli *client.Client, config container.Config, hostConfig container.HostConfig) (string, io.ReadCloser, error) { - created, err := cli.ContainerCreate(context.Background(), - &config, &hostConfig, nil, "") - if err != nil { - return "", nil, err - } - if err = cli.ContainerStart(ctx, created.ID, types.ContainerStartOptions{}); err != nil { - return "", nil, err - } - stream, err := cli.ContainerLogs(ctx, - created.ID, - types.ContainerLogsOptions{ - ShowStdout: true, - ShowStderr: true, - Follow: true, - }) - return created.ID, stream, err -} - -func teeContainerStream(w, stdout, stderr io.Writer, stream io.ReadCloser) { - stdcopy.StdCopy(io.MultiWriter(w, stdout), io.MultiWriter(w, stderr), stream) - stream.Close() -} diff --git a/hack/integration-cli-on-swarm/agent/worker/worker.go b/hack/integration-cli-on-swarm/agent/worker/worker.go deleted file mode 100644 index ea8bb3fe27f99..0000000000000 --- a/hack/integration-cli-on-swarm/agent/worker/worker.go +++ /dev/null @@ -1,69 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "time" - - "github.com/bfirsh/funker-go" - "github.com/docker/distribution/reference" - "github.com/docker/docker/hack/integration-cli-on-swarm/agent/types" -) - -func main() { - if err := xmain(); err != nil { - log.Fatalf("fatal error: %v", err) - } -} - -func validImageDigest(s string) bool { - return reference.DigestRegexp.FindString(s) != "" -} - -func xmain() error { - workerImageDigest := flag.String("worker-image-digest", "", "Needs to be the digest of this worker image itself") - dryRun := flag.Bool("dry-run", false, "Dry run") - keepExecutor := flag.Bool("keep-executor", false, "Do not auto-remove executor containers, which is used for running privileged programs on Swarm") - flag.Parse() - if !validImageDigest(*workerImageDigest) { - // Because of issue #29582. - // `docker service create localregistry.example.com/blahblah:latest` pulls the image data to local, but not a tag. - // So, `docker run localregistry.example.com/blahblah:latest` fails: `Unable to find image 'localregistry.example.com/blahblah:latest' locally` - return fmt.Errorf("worker-image-digest must be a digest, got %q", *workerImageDigest) - } - executor := privilegedTestChunkExecutor(!*keepExecutor) - if *dryRun { - executor = dryTestChunkExecutor() - } - return handle(*workerImageDigest, executor) -} - -func handle(workerImageDigest string, executor testChunkExecutor) error { - log.Printf("Waiting for a funker request") - return funker.Handle(func(args *types.Args) types.Result { - log.Printf("Executing chunk %d, contains %d test filters", - args.ChunkID, len(args.Tests)) - begin := time.Now() - code, rawLog, err := executor(workerImageDigest, args.Tests) - if err != nil { - log.Printf("Error while executing chunk %d: %v", args.ChunkID, err) - if code == 0 { - // Make sure this is a failure - code = 1 - } - return types.Result{ - ChunkID: args.ChunkID, - Code: int(code), - RawLog: rawLog, - } - } - elapsed := time.Since(begin) - log.Printf("Finished chunk %d, code=%d, elapsed=%v", args.ChunkID, code, elapsed) - return types.Result{ - ChunkID: args.ChunkID, - Code: int(code), - RawLog: rawLog, - } - }) -} diff --git a/hack/integration-cli-on-swarm/host/compose.go b/hack/integration-cli-on-swarm/host/compose.go deleted file mode 100644 index a92282a1a027e..0000000000000 --- a/hack/integration-cli-on-swarm/host/compose.go +++ /dev/null @@ -1,122 +0,0 @@ -package main - -import ( - "context" - "io/ioutil" - "os" - "path/filepath" - "text/template" - - "github.com/docker/docker/client" -) - -const composeTemplate = `# generated by integration-cli-on-swarm -version: "3" - -services: - worker: - image: "{{.WorkerImage}}" - command: ["-worker-image-digest={{.WorkerImageDigest}}", "-dry-run={{.DryRun}}", "-keep-executor={{.KeepExecutor}}"] - networks: - - net - volumes: -# Bind-mount the API socket so that we can invoke "docker run --privileged" within the service containers - - /var/run/docker.sock:/var/run/docker.sock - environment: - - DOCKER_GRAPHDRIVER={{.EnvDockerGraphDriver}} - - DOCKER_EXPERIMENTAL={{.EnvDockerExperimental}} - deploy: - mode: replicated - replicas: {{.Replicas}} - restart_policy: -# The restart condition needs to be any for funker function - condition: any - - master: - image: "{{.MasterImage}}" - command: ["-worker-service=worker", "-input=/mnt/input", "-chunks={{.Chunks}}", "-shuffle={{.Shuffle}}", "-rand-seed={{.RandSeed}}"] - networks: - - net - volumes: - - {{.Volume}}:/mnt - deploy: - mode: replicated - replicas: 1 - restart_policy: - condition: none - placement: -# Make sure the master can access the volume - constraints: [node.id == {{.SelfNodeID}}] - -networks: - net: - -volumes: - {{.Volume}}: - external: true -` - -type composeOptions struct { - Replicas int - Chunks int - MasterImage string - WorkerImage string - Volume string - Shuffle bool - RandSeed int64 - DryRun bool - KeepExecutor bool -} - -type composeTemplateOptions struct { - composeOptions - WorkerImageDigest string - SelfNodeID string - EnvDockerGraphDriver string - EnvDockerExperimental string -} - -// createCompose creates "dir/docker-compose.yml". -// If dir is empty, TempDir() is used. -func createCompose(dir string, cli *client.Client, opts composeOptions) (string, error) { - if dir == "" { - var err error - dir, err = ioutil.TempDir("", "integration-cli-on-swarm-") - if err != nil { - return "", err - } - } - resolved := composeTemplateOptions{} - resolved.composeOptions = opts - workerImageInspect, _, err := cli.ImageInspectWithRaw(context.Background(), defaultWorkerImageName) - if err != nil { - return "", err - } - if len(workerImageInspect.RepoDigests) > 0 { - resolved.WorkerImageDigest = workerImageInspect.RepoDigests[0] - } else { - // fall back for non-pushed image - resolved.WorkerImageDigest = workerImageInspect.ID - } - info, err := cli.Info(context.Background()) - if err != nil { - return "", err - } - resolved.SelfNodeID = info.Swarm.NodeID - resolved.EnvDockerGraphDriver = os.Getenv("DOCKER_GRAPHDRIVER") - resolved.EnvDockerExperimental = os.Getenv("DOCKER_EXPERIMENTAL") - composeFilePath := filepath.Join(dir, "docker-compose.yml") - tmpl, err := template.New("").Parse(composeTemplate) - if err != nil { - return "", err - } - f, err := os.Create(composeFilePath) - if err != nil { - return "", err - } - defer f.Close() - if err = tmpl.Execute(f, resolved); err != nil { - return "", err - } - return composeFilePath, nil -} diff --git a/hack/integration-cli-on-swarm/host/dockercmd.go b/hack/integration-cli-on-swarm/host/dockercmd.go deleted file mode 100644 index c08b763a2ba80..0000000000000 --- a/hack/integration-cli-on-swarm/host/dockercmd.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "fmt" - "os" - "os/exec" - "strings" - "time" - - "github.com/docker/docker/client" -) - -func system(commands [][]string) error { - for _, c := range commands { - cmd := exec.Command(c[0], c[1:]...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Env = os.Environ() - if err := cmd.Run(); err != nil { - return err - } - } - return nil -} - -func pushImage(unusedCli *client.Client, remote, local string) error { - // FIXME: eliminate os/exec (but it is hard to pass auth without os/exec ...) - return system([][]string{ - {"docker", "image", "tag", local, remote}, - {"docker", "image", "push", remote}, - }) -} - -func deployStack(unusedCli *client.Client, stackName, composeFilePath string) error { - // FIXME: eliminate os/exec (but stack is implemented in CLI ...) - return system([][]string{ - {"docker", "stack", "deploy", - "--compose-file", composeFilePath, - "--with-registry-auth", - stackName}, - }) -} - -func hasStack(unusedCli *client.Client, stackName string) bool { - // FIXME: eliminate os/exec (but stack is implemented in CLI ...) - out, err := exec.Command("docker", "stack", "ls").CombinedOutput() - if err != nil { - panic(fmt.Errorf("`docker stack ls` failed with: %s", string(out))) - } - // FIXME: not accurate - return strings.Contains(string(out), stackName) -} - -func removeStack(unusedCli *client.Client, stackName string) error { - // FIXME: eliminate os/exec (but stack is implemented in CLI ...) - if err := system([][]string{ - {"docker", "stack", "rm", stackName}, - }); err != nil { - return err - } - // FIXME - time.Sleep(10 * time.Second) - return nil -} diff --git a/hack/integration-cli-on-swarm/host/enumerate.go b/hack/integration-cli-on-swarm/host/enumerate.go deleted file mode 100644 index 3354c23c07b54..0000000000000 --- a/hack/integration-cli-on-swarm/host/enumerate.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "path/filepath" - "regexp" -) - -var testFuncRegexp *regexp.Regexp - -func init() { - testFuncRegexp = regexp.MustCompile(`(?m)^\s*func\s+\(\w*\s*\*(\w+Suite)\)\s+(Test\w+)`) -} - -func enumerateTestsForBytes(b []byte) ([]string, error) { - var tests []string - submatches := testFuncRegexp.FindAllSubmatch(b, -1) - for _, submatch := range submatches { - if len(submatch) == 3 { - tests = append(tests, fmt.Sprintf("%s.%s$", submatch[1], submatch[2])) - } - } - return tests, nil -} - -// enumerateTests enumerates valid `-check.f` strings for all the test functions. -// Note that we use regexp rather than parsing Go files for performance reason. -// (Try `TESTFLAGS=-check.list make test-integration` to see the slowness of parsing) -// The files needs to be `gofmt`-ed -// -// The result will be as follows, but unsorted ('$' is appended because they are regexp for `-check.f`): -// "DockerAuthzSuite.TestAuthZPluginAPIDenyResponse$" -// "DockerAuthzSuite.TestAuthZPluginAllowEventStream$" -// ... -// "DockerTrustedSwarmSuite.TestTrustedServiceUpdate$" -func enumerateTests(wd string) ([]string, error) { - testGoFiles, err := filepath.Glob(filepath.Join(wd, "integration-cli", "*_test.go")) - if err != nil { - return nil, err - } - var allTests []string - for _, testGoFile := range testGoFiles { - b, err := ioutil.ReadFile(testGoFile) - if err != nil { - return nil, err - } - tests, err := enumerateTestsForBytes(b) - if err != nil { - return nil, err - } - allTests = append(allTests, tests...) - } - return allTests, nil -} diff --git a/hack/integration-cli-on-swarm/host/enumerate_test.go b/hack/integration-cli-on-swarm/host/enumerate_test.go deleted file mode 100644 index d6049ae52e9b9..0000000000000 --- a/hack/integration-cli-on-swarm/host/enumerate_test.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "os" - "path/filepath" - "reflect" - "sort" - "strings" - "testing" -) - -func getRepoTopDir(t *testing.T) string { - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - wd = filepath.Clean(wd) - suffix := "hack/integration-cli-on-swarm/host" - if !strings.HasSuffix(wd, suffix) { - t.Skipf("cwd seems strange (needs to have suffix %s): %v", suffix, wd) - } - return filepath.Clean(filepath.Join(wd, "../../..")) -} - -func TestEnumerateTests(t *testing.T) { - if testing.Short() { - t.Skip("skipping in short mode") - } - tests, err := enumerateTests(getRepoTopDir(t)) - if err != nil { - t.Fatal(err) - } - sort.Strings(tests) - t.Logf("enumerated %d test filter strings:", len(tests)) - for _, s := range tests { - t.Logf("- %q", s) - } -} - -func TestEnumerateTestsForBytes(t *testing.T) { - b := []byte(`package main -import ( - "github.com/go-check/check" -) - -func (s *FooSuite) TestA(c *check.C) { -} - -func (s *FooSuite) TestAAA(c *check.C) { -} - -func (s *BarSuite) TestBar(c *check.C) { -} - -func (x *FooSuite) TestC(c *check.C) { -} - -func (*FooSuite) TestD(c *check.C) { -} - -// should not be counted -func (s *FooSuite) testE(c *check.C) { -} - -// counted, although we don't support ungofmt file - func (s *FooSuite) TestF (c *check.C){} -`) - expected := []string{ - "FooSuite.TestA$", - "FooSuite.TestAAA$", - "BarSuite.TestBar$", - "FooSuite.TestC$", - "FooSuite.TestD$", - "FooSuite.TestF$", - } - - actual, err := enumerateTestsForBytes(b) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(expected, actual) { - t.Fatalf("expected %q, got %q", expected, actual) - } -} diff --git a/hack/integration-cli-on-swarm/host/host.go b/hack/integration-cli-on-swarm/host/host.go deleted file mode 100644 index fdc2a83e7f009..0000000000000 --- a/hack/integration-cli-on-swarm/host/host.go +++ /dev/null @@ -1,198 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "io" - "io/ioutil" - "os" - "strings" - "time" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/stdcopy" - "github.com/sirupsen/logrus" -) - -const ( - defaultStackName = "integration-cli-on-swarm" - defaultVolumeName = "integration-cli-on-swarm" - defaultMasterImageName = "integration-cli-master" - defaultWorkerImageName = "integration-cli-worker" -) - -func main() { - rc, err := xmain() - if err != nil { - logrus.Fatalf("fatal error: %v", err) - } - os.Exit(rc) -} - -func xmain() (int, error) { - // Should we use cobra maybe? - replicas := flag.Int("replicas", 1, "Number of worker service replica") - chunks := flag.Int("chunks", 0, "Number of test chunks executed in batch (0 == replicas)") - pushWorkerImage := flag.String("push-worker-image", "", "Push the worker image to the registry. Required for distributed execution. (empty == not to push)") - shuffle := flag.Bool("shuffle", false, "Shuffle the input so as to mitigate makespan nonuniformity") - // flags below are rarely used - randSeed := flag.Int64("rand-seed", int64(0), "Random seed used for shuffling (0 == current time)") - filtersFile := flag.String("filters-file", "", "Path to optional file composed of `-check.f` filter strings") - dryRun := flag.Bool("dry-run", false, "Dry run") - keepExecutor := flag.Bool("keep-executor", false, "Do not auto-remove executor containers, which is used for running privileged programs on Swarm") - flag.Parse() - if *chunks == 0 { - *chunks = *replicas - } - if *randSeed == int64(0) { - *randSeed = time.Now().UnixNano() - } - cli, err := client.NewEnvClient() - if err != nil { - return 1, err - } - if hasStack(cli, defaultStackName) { - logrus.Infof("Removing stack %s", defaultStackName) - removeStack(cli, defaultStackName) - } - if hasVolume(cli, defaultVolumeName) { - logrus.Infof("Removing volume %s", defaultVolumeName) - removeVolume(cli, defaultVolumeName) - } - if err = ensureImages(cli, []string{defaultWorkerImageName, defaultMasterImageName}); err != nil { - return 1, err - } - workerImageForStack := defaultWorkerImageName - if *pushWorkerImage != "" { - logrus.Infof("Pushing %s to %s", defaultWorkerImageName, *pushWorkerImage) - if err = pushImage(cli, *pushWorkerImage, defaultWorkerImageName); err != nil { - return 1, err - } - workerImageForStack = *pushWorkerImage - } - compose, err := createCompose("", cli, composeOptions{ - Replicas: *replicas, - Chunks: *chunks, - MasterImage: defaultMasterImageName, - WorkerImage: workerImageForStack, - Volume: defaultVolumeName, - Shuffle: *shuffle, - RandSeed: *randSeed, - DryRun: *dryRun, - KeepExecutor: *keepExecutor, - }) - if err != nil { - return 1, err - } - filters, err := filtersBytes(*filtersFile) - if err != nil { - return 1, err - } - logrus.Infof("Creating volume %s with input data", defaultVolumeName) - if err = createVolumeWithData(cli, - defaultVolumeName, - map[string][]byte{"/input": filters}, - defaultMasterImageName); err != nil { - return 1, err - } - logrus.Infof("Deploying stack %s from %s", defaultStackName, compose) - defer func() { - logrus.Infof("NOTE: You may want to inspect or clean up following resources:") - logrus.Infof(" - Stack: %s", defaultStackName) - logrus.Infof(" - Volume: %s", defaultVolumeName) - logrus.Infof(" - Compose file: %s", compose) - logrus.Infof(" - Master image: %s", defaultMasterImageName) - logrus.Infof(" - Worker image: %s", workerImageForStack) - }() - if err = deployStack(cli, defaultStackName, compose); err != nil { - return 1, err - } - logrus.Infof("The log will be displayed here after some duration."+ - "You can watch the live status via `docker service logs %s_worker`", - defaultStackName) - masterContainerID, err := waitForMasterUp(cli, defaultStackName) - if err != nil { - return 1, err - } - rc, err := waitForContainerCompletion(cli, os.Stdout, os.Stderr, masterContainerID) - if err != nil { - return 1, err - } - logrus.Infof("Exit status: %d", rc) - return int(rc), nil -} - -func ensureImages(cli *client.Client, images []string) error { - for _, image := range images { - _, _, err := cli.ImageInspectWithRaw(context.Background(), image) - if err != nil { - return fmt.Errorf("could not find image %s, please run `make build-integration-cli-on-swarm`: %v", - image, err) - } - } - return nil -} - -func filtersBytes(optionalFiltersFile string) ([]byte, error) { - var b []byte - if optionalFiltersFile == "" { - tests, err := enumerateTests(".") - if err != nil { - return b, err - } - b = []byte(strings.Join(tests, "\n") + "\n") - } else { - var err error - b, err = ioutil.ReadFile(optionalFiltersFile) - if err != nil { - return b, err - } - } - return b, nil -} - -func waitForMasterUp(cli *client.Client, stackName string) (string, error) { - // FIXME(AkihiroSuda): it should retry until master is up, rather than pre-sleeping - time.Sleep(10 * time.Second) - - fil := filters.NewArgs() - fil.Add("label", "com.docker.stack.namespace="+stackName) - // FIXME(AkihiroSuda): we should not rely on internal service naming convention - fil.Add("label", "com.docker.swarm.service.name="+stackName+"_master") - masters, err := cli.ContainerList(context.Background(), types.ContainerListOptions{ - All: true, - Filters: fil, - }) - if err != nil { - return "", err - } - if len(masters) == 0 { - return "", fmt.Errorf("master not running in stack %s?", stackName) - } - return masters[0].ID, nil -} - -func waitForContainerCompletion(cli *client.Client, stdout, stderr io.Writer, containerID string) (int64, error) { - stream, err := cli.ContainerLogs(context.Background(), - containerID, - types.ContainerLogsOptions{ - ShowStdout: true, - ShowStderr: true, - Follow: true, - }) - if err != nil { - return 1, err - } - stdcopy.StdCopy(stdout, stderr, stream) - stream.Close() - resultC, errC := cli.ContainerWait(context.Background(), containerID, "") - select { - case err := <-errC: - return 1, err - case result := <-resultC: - return result.StatusCode, nil - } -} diff --git a/hack/integration-cli-on-swarm/host/volume.go b/hack/integration-cli-on-swarm/host/volume.go deleted file mode 100644 index a6ddc6fae75da..0000000000000 --- a/hack/integration-cli-on-swarm/host/volume.go +++ /dev/null @@ -1,88 +0,0 @@ -package main - -import ( - "archive/tar" - "bytes" - "context" - "io" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/mount" - "github.com/docker/docker/api/types/volume" - "github.com/docker/docker/client" -) - -func createTar(data map[string][]byte) (io.Reader, error) { - var b bytes.Buffer - tw := tar.NewWriter(&b) - for path, datum := range data { - hdr := tar.Header{ - Name: path, - Mode: 0644, - Size: int64(len(datum)), - } - if err := tw.WriteHeader(&hdr); err != nil { - return nil, err - } - _, err := tw.Write(datum) - if err != nil { - return nil, err - } - } - if err := tw.Close(); err != nil { - return nil, err - } - return &b, nil -} - -// createVolumeWithData creates a volume with the given data (e.g. data["/foo"] = []byte("bar")) -// Internally, a container is created from the image so as to provision the data to the volume, -// which is attached to the container. -func createVolumeWithData(cli *client.Client, volumeName string, data map[string][]byte, image string) error { - _, err := cli.VolumeCreate(context.Background(), - volume.VolumeCreateBody{ - Driver: "local", - Name: volumeName, - }) - if err != nil { - return err - } - mnt := "/mnt" - miniContainer, err := cli.ContainerCreate(context.Background(), - &container.Config{ - Image: image, - }, - &container.HostConfig{ - Mounts: []mount.Mount{ - { - Type: mount.TypeVolume, - Source: volumeName, - Target: mnt, - }, - }, - }, nil, "") - if err != nil { - return err - } - tr, err := createTar(data) - if err != nil { - return err - } - if cli.CopyToContainer(context.Background(), - miniContainer.ID, mnt, tr, types.CopyToContainerOptions{}); err != nil { - return err - } - return cli.ContainerRemove(context.Background(), - miniContainer.ID, - types.ContainerRemoveOptions{}) -} - -func hasVolume(cli *client.Client, volumeName string) bool { - _, err := cli.VolumeInspect(context.Background(), volumeName) - return err == nil -} - -func removeVolume(cli *client.Client, volumeName string) error { - return cli.VolumeRemove(context.Background(), volumeName, true) -} diff --git a/hack/make.ps1 b/hack/make.ps1 index 4ee47ba847c2a..ea6d5269b3ee0 100644 --- a/hack/make.ps1 +++ b/hack/make.ps1 @@ -60,6 +60,9 @@ .PARAMETER TestUnit Runs unit tests. +.PARAMETER TestIntegration + Runs integration tests. + .PARAMETER All Runs everything this script knows about that can run in a container. @@ -84,12 +87,14 @@ param( [Parameter(Mandatory=$False)][switch]$PkgImports, [Parameter(Mandatory=$False)][switch]$GoFormat, [Parameter(Mandatory=$False)][switch]$TestUnit, + [Parameter(Mandatory=$False)][switch]$TestIntegration, [Parameter(Mandatory=$False)][switch]$All ) $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $pushed=$False # To restore the directory if we have temporarily pushed to one. +Set-Variable GOTESTSUM_LOCATION -option Constant -value "$env:GOPATH/bin/" # Utility function to get the commit ID of the repository Function Get-GitCommit() { @@ -130,7 +135,7 @@ Function Check-InContainer() { # outside of a container where it may be out of date with master. Function Verify-GoVersion() { Try { - $goVersionDockerfile=(Select-String -Path ".\Dockerfile" -Pattern "^FROM golang:").ToString().Split(" ")[1].SubString(7) -replace '\.0$','' + $goVersionDockerfile=(Select-String -Path ".\Dockerfile" -Pattern "^ARG[\s]+GO_VERSION=(.*)$").Matches.groups[1].Value -replace '\.0$','' $goVersionInstalled=(go version).ToString().Split(" ")[2].SubString(2) } Catch [Exception] { @@ -218,7 +223,7 @@ Function Validate-DCO($headCommit, $upstreamCommit) { if ($LASTEXITCODE -ne 0) { Throw "Failed git log --format" } $commits = $($commits -split '\s+' -match '\S') $badCommits=@() - $commits | %{ + $commits | ForEach-Object{ # Skip commits with no content such as merge commits etc if ($(git log -1 --format=format: --name-status $_).Length -gt 0) { # Ignore exit code on next call - always process regardless @@ -244,7 +249,7 @@ Function Validate-PkgImports($headCommit, $upstreamCommit) { # Get a list of go source-code files which have changed under pkg\. Ignore exit code on next call - always process regardless $files=@(); $files = Invoke-Expression "git diff $upstreamCommit...$headCommit --diff-filter=ACMR --name-only -- `'pkg\*.go`'" - $badFiles=@(); $files | %{ + $badFiles=@(); $files | ForEach-Object{ $file=$_ # For the current changed file, get its list of dependencies, sorted and uniqued. $imports = Invoke-Expression "go list -e -f `'{{ .Deps }}`' $file" @@ -255,13 +260,13 @@ Function Validate-PkgImports($headCommit, $upstreamCommit) { -NotMatch "^github.com/docker/docker/vendor" ` -Match "^github.com/docker/docker" ` -Replace "`n", "" - $imports | % { $badFiles+="$file imports $_`n" } + $imports | ForEach-Object{ $badFiles+="$file imports $_`n" } } if ($badFiles.Length -eq 0) { Write-Host 'Congratulations! ".\pkg\*.go" is safely isolated from internal code.' } else { $e = "`nThese files import internal code: (either directly or indirectly)`n" - $badFiles | %{ $e+=" - $_"} + $badFiles | ForEach-Object{ $e+=" - $_"} Throw $e } } @@ -297,7 +302,7 @@ Function Validate-GoFormat($headCommit, $upstreamCommit) { Write-Host 'Congratulations! All Go source files are properly formatted.' } else { $e = "`nThese files are not properly gofmt`'d:`n" - $badFiles | %{ $e+=" - $_`n"} + $badFiles | ForEach-Object{ $e+=" - $_`n"} $e+= "`nPlease reformat the above files using `"gofmt -s -w`" and commit the result." Throw $e } @@ -315,9 +320,56 @@ Function Run-UnitTests() { $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/man" $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/integration" $pkgList = $pkgList -replace "`r`n", " " - $goTestCommand = "go test" + $raceParm + " -cover -ldflags -w -tags """ + "autogen daemon" + """ -a """ + "-test.timeout=10m" + """ $pkgList" - Invoke-Expression $goTestCommand - if ($LASTEXITCODE -ne 0) { Throw "Unit tests failed" } + + $goTestArg = "--format=standard-verbose --jsonfile=bundles\go-test-report-unit-tests.json --junitfile=bundles\junit-report-unit-tests.xml -- " + $raceParm + " -cover -ldflags -w -a """ + "-test.timeout=10m" + """ $pkgList" + Write-Host "INFO: Invoking unit tests run with $GOTESTSUM_LOCATION\gotestsum.exe $goTestArg" + $pinfo = New-Object System.Diagnostics.ProcessStartInfo + $pinfo.FileName = "$GOTESTSUM_LOCATION\gotestsum.exe" + $pinfo.WorkingDirectory = "$($PWD.Path)" + $pinfo.UseShellExecute = $false + $pinfo.Arguments = $goTestArg + $p = New-Object System.Diagnostics.Process + $p.StartInfo = $pinfo + $p.Start() | Out-Null + $p.WaitForExit() + if ($p.ExitCode -ne 0) { Throw "Unit tests failed" } +} + +# Run the integration tests +Function Run-IntegrationTests() { + $escRoot = [Regex]::Escape($root) + $env:DOCKER_INTEGRATION_DAEMON_DEST = $bundlesDir + "\tmp" + $dirs = go list -test -f '{{- if ne .ForTest `"`" -}}{{- .Dir -}}{{- end -}}' .\integration\... + ForEach($dir in $dirs) { + # Normalize directory name for using in the test results files. + $normDir = $dir.Trim() + $normDir = $normDir -replace $escRoot, "" + $normDir = $normDir -replace "\\", "-" + $normDir = $normDir -replace "\/", "-" + $normDir = $normDir -replace "\.", "-" + if ($normDir.StartsWith("-")) + { + $normDir = $normDir.TrimStart("-") + } + if ($normDir.EndsWith("-")) + { + $normDir = $normDir.TrimEnd("-") + } + $jsonFilePath = $bundlesDir + "\go-test-report-int-tests-$normDir" + ".json" + $xmlFilePath = $bundlesDir + "\junit-report-int-tests-$normDir" + ".xml" + Set-Location $dir + Write-Host "Running $($PWD.Path)" + $pinfo = New-Object System.Diagnostics.ProcessStartInfo + $pinfo.FileName = "gotestsum.exe" + $pinfo.WorkingDirectory = "$($PWD.Path)" + $pinfo.UseShellExecute = $false + $pinfo.Arguments = "--format=standard-verbose --jsonfile=$jsonFilePath --junitfile=$xmlFilePath -- -test.timeout=60m $env:INTEGRATION_TESTFLAGS" + $p = New-Object System.Diagnostics.Process + $p.StartInfo = $pinfo + $p.Start() | Out-Null + $p.WaitForExit() + if ($p.ExitCode -ne 0) { Throw "Integration tests failed" } + } } # Start of main code. @@ -328,16 +380,21 @@ Try { $root = $(Split-Path $MyInvocation.MyCommand.Definition -Parent | Split-Path -Parent) Push-Location $root + # Ensure the bundles directory exists + $bundlesDir = $root + "\bundles" + Set-Variable bundlesDir -option ReadOnly + New-Item -Force $bundlesDir -ItemType Directory | Out-Null + # Handle the "-All" shortcut to turn on all things we can handle. # Note we expressly only include the items which can run in a container - the validations tests cannot # as they require the .git directory which is excluded from the image by .dockerignore - if ($All) { $Client=$True; $Daemon=$True; $TestUnit=$True } + if ($All) { $Client=$True; $Daemon=$True; $TestUnit=$True; } # Handle the "-Binary" shortcut to build both client and daemon. if ($Binary) { $Client = $True; $Daemon = $True } # Default to building the daemon if not asked for anything explicitly. - if (-not($Client) -and -not($Daemon) -and -not($DCO) -and -not($PkgImports) -and -not($GoFormat) -and -not($TestUnit)) { $Daemon=$True } + if (-not($Client) -and -not($Daemon) -and -not($DCO) -and -not($PkgImports) -and -not($GoFormat) -and -not($TestUnit) -and -not($TestIntegration)) { $Daemon=$True } # Verify git is installed if ($(Get-Command git -ErrorAction SilentlyContinue) -eq $nil) { Throw "Git does not appear to be installed" } @@ -351,6 +408,8 @@ Try { # Get the version of docker (eg 17.04.0-dev) $dockerVersion="0.0.0-dev" + # Overwrite dockerVersion if VERSION Environment variable is available + if (Test-Path Env:\VERSION) { $dockerVersion=$env:VERSION } # Give a warning if we are not running in a container and are building binaries or running unit tests. # Not relevant for validation tests as these are fine to run outside of a container. @@ -387,15 +446,13 @@ Try { # Build the binaries if ($Client -or $Daemon) { - # Create the bundles directory if it doesn't exist - if (-not (Test-Path ".\bundles")) { New-Item ".\bundles" -ItemType Directory | Out-Null } # Perform the actual build if ($Daemon) { Execute-Build "daemon" "daemon" "dockerd" } if ($Client) { # Get the Docker channel and version from the environment, or use the defaults. - if (-not ($channel = $env:DOCKERCLI_CHANNEL)) { $channel = "edge" } - if (-not ($version = $env:DOCKERCLI_VERSION)) { $version = "17.06.0-ce" } + if (-not ($channel = $env:DOCKERCLI_CHANNEL)) { $channel = "stable" } + if (-not ($version = $env:DOCKERCLI_VERSION)) { $version = "17.06.2-ce" } # Download the zip file and extract the client executable. Write-Host "INFO: Downloading docker/cli version $version from $channel..." @@ -423,6 +480,9 @@ Try { # Run unit tests if ($TestUnit) { Run-UnitTests } + # Run integration tests + if ($TestIntegration) { Run-IntegrationTests } + # Gratuitous ASCII art. if ($Daemon -or $Client) { Write-Host @@ -449,7 +509,7 @@ Catch [Exception] { Write-Host -ForegroundColor Red " \/ \/ \/ \/ " Write-Host - Throw $_ + exit 1 } Finally { Pop-Location # As we pushed to the root of the repo as the very first thing diff --git a/hack/make.sh b/hack/make.sh index 62c72a09e2305..427def3aca3b6 100755 --- a/hack/make.sh +++ b/hack/make.sh @@ -24,44 +24,18 @@ set -e set -o pipefail export DOCKER_PKG='github.com/docker/docker' -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" export MAKEDIR="$SCRIPTDIR/make" export PKG_CONFIG=${PKG_CONFIG:-pkg-config} -# We're a nice, sexy, little shell script, and people might try to run us; -# but really, they shouldn't. We want to be in a container! -inContainer="AssumeSoInitially" -if [ "$(go env GOHOSTOS)" = 'windows' ]; then - if [ -z "$FROM_DOCKERFILE" ]; then - unset inContainer - fi -else - if [ "$PWD" != "/go/src/$DOCKER_PKG" ]; then - unset inContainer - fi -fi - -if [ -z "$inContainer" ]; then - { - echo "# WARNING! I don't seem to be running in a Docker container." - echo "# The result of this command might be an incorrect build, and will not be" - echo "# officially supported." - echo "#" - echo "# Try this instead: make all" - echo "#" - } >&2 -fi - echo # List of bundles to create when no argument is passed DEFAULT_BUNDLES=( binary-daemon dynbinary - test-integration test-docker-py - cross ) @@ -109,28 +83,21 @@ add_buildtag() { [[ " $DOCKER_BUILDTAGS" == *" $1_"* ]] || DOCKER_BUILDTAGS+=" $1_$2" } -if ${PKG_CONFIG} 'libsystemd >= 209' 2> /dev/null ; then +if ${PKG_CONFIG} 'libsystemd >= 209' 2> /dev/null; then DOCKER_BUILDTAGS+=" journald" -elif ${PKG_CONFIG} 'libsystemd-journal' 2> /dev/null ; then +elif ${PKG_CONFIG} 'libsystemd-journal' 2> /dev/null; then DOCKER_BUILDTAGS+=" journald journald_compat" fi -# test whether "btrfs/version.h" exists and apply btrfs_noversion appropriately -if \ - command -v gcc &> /dev/null \ - && ! gcc -E - -o /dev/null &> /dev/null <<<'#include ' \ -; then - DOCKER_BUILDTAGS+=' btrfs_noversion' -fi - # test whether "libdevmapper.h" is new enough to support deferred remove # functionality. We favour libdm_dlsym_deferred_remove over # libdm_no_deferred_remove in dynamic cases because the binary could be shipped # with a newer libdevmapper than the one it was built with. -if \ +if command -v gcc &> /dev/null \ - && ! ( echo -e '#include \nint main() { dm_task_deferred_remove(NULL); }'| gcc -xc - -o /dev/null $(pkg-config --libs devmapper) &> /dev/null ) \ -; then + && ! (echo -e '#include \nint main() { dm_task_deferred_remove(NULL); }' | gcc -xc - -o /dev/null $(pkg-config --libs devmapper) &> /dev/null) \ + ; +then add_buildtag libdm dlsym_deferred_remove fi @@ -145,19 +112,10 @@ LDFLAGS_STATIC='' EXTLDFLAGS_STATIC='-static' # ORIG_BUILDFLAGS is necessary for the cross target which cannot always build # with options like -race. -ORIG_BUILDFLAGS=( -tags "autogen netgo osusergo static_build $DOCKER_BUILDTAGS" -installsuffix netgo ) +ORIG_BUILDFLAGS=(-tags "netgo osusergo static_build $DOCKER_BUILDTAGS" -installsuffix netgo) # see https://github.com/golang/go/issues/9369#issuecomment-69864440 for why -installsuffix is necessary here -BUILDFLAGS=( $BUILDFLAGS "${ORIG_BUILDFLAGS[@]}" ) - -# Test timeout. -if [ "${DOCKER_ENGINE_GOARCH}" == "arm64" ] || [ "${DOCKER_ENGINE_GOARCH}" == "arm" ]; then - : ${TIMEOUT:=10m} -elif [ "${DOCKER_ENGINE_GOARCH}" == "windows" ]; then - : ${TIMEOUT:=8m} -else - : ${TIMEOUT:=5m} -fi +BUILDFLAGS=(${BUILDFLAGS} "${ORIG_BUILDFLAGS[@]}") LDFLAGS_STATIC_DOCKER=" $LDFLAGS_STATIC @@ -175,25 +133,24 @@ if [ "$(uname -s)" = 'FreeBSD' ]; then fi bundle() { - local bundle="$1"; shift + local bundle="$1" + shift echo "---> Making bundle: $(basename "$bundle") (in $DEST)" source "$SCRIPTDIR/make/$bundle" "$@" } main() { - if [ -z "${KEEPBUNDLE-}" ]; then - echo "Removing bundles/" - rm -rf "bundles/*" - echo + bundle_dir="bundles" + if [ -n "${PREFIX}" ]; then + bundle_dir="${PREFIX}/${bundle_dir}" fi - mkdir -p bundles - # Windows and symlinks don't get along well - if [ "$(go env GOHOSTOS)" != 'windows' ]; then - rm -f bundles/latest - # preserve latest symlink for backward compatibility - ln -sf . bundles/latest + if [ -z "${KEEPBUNDLE-}" ]; then + echo "Removing ${bundle_dir}/" + rm -rf "${bundle_dir}"/* + echo fi + mkdir -p "${bundle_dir}" if [ $# -lt 1 ]; then bundles=(${DEFAULT_BUNDLES[@]}) @@ -201,7 +158,7 @@ main() { bundles=($@) fi for bundle in ${bundles[@]}; do - export DEST="bundles/$(basename "$bundle")" + export DEST="${bundle_dir}/$(basename "$bundle")" # Cygdrive paths don't play well with go build -o. if [[ "$(uname -s)" == CYGWIN* ]]; then export DEST="$(cygpath -mw "$DEST")" diff --git a/hack/make/.binary b/hack/make/.binary index 010c2c11daf1d..371af7536bbbd 100644 --- a/hack/make/.binary +++ b/hack/make/.binary @@ -8,8 +8,6 @@ binary_extension() { fi } -GO_PACKAGE='github.com/docker/docker/cmd/dockerd' -BINARY_SHORT_NAME='dockerd' BINARY_NAME="$BINARY_SHORT_NAME-$VERSION" BINARY_EXTENSION="$(binary_extension)" BINARY_FULLNAME="$BINARY_NAME$BINARY_EXTENSION" @@ -38,37 +36,69 @@ hash_files() { } ( -export GOGC=${DOCKER_BUILD_GOGC:-1000} + export GOGC=${DOCKER_BUILD_GOGC:-1000} -if [ "$(go env GOOS)/$(go env GOARCH)" != "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" ]; then - # must be cross-compiling! + if [ "$(go env GOOS)/$(go env GOARCH)" != "$(go env GOHOSTOS)/$(go env GOHOSTARCH)" ]; then + # must be cross-compiling! + case "$(go env GOOS)/$(go env GOARCH)" in + windows/amd64) + export CC="${CC:-x86_64-w64-mingw32-gcc}" + export CGO_ENABLED=1 + ;; + linux/arm) + case "${GOARM}" in + 5 | "") + export CC="${CC:-arm-linux-gnueabi-gcc}" + export CGO_ENABLED=1 + ;; + 7) + export CC="${CC:-arm-linux-gnueabihf-gcc}" + export CGO_ENABLED=1 + ;; + esac + ;; + linux/arm64) + export CC="${CC:-aarch64-linux-gnu-gcc}" + export CGO_ENABLED=1 + ;; + linux/amd64) + export CC="${CC:-x86_64-linux-gnu-gcc}" + export CGO_ENABLED=1 + ;; + linux/ppc64le) + export CC="${CC:-powerpc64le-linux-gnu-gcc}" + export CGO_ENABLED=1 + ;; + linux/s390x) + export CC="${CC:-s390x-linux-gnu-gcc}" + export CGO_ENABLED=1 + ;; + esac + fi + + # -buildmode=pie is not supported on Windows and Linux on mips, riscv64 and ppc64be. + # https://github.com/golang/go/blob/77aa209b386a184e7f4b44938f2a05a1b5c5a3cf/src/cmd/internal/sys/supported.go#L89-L99 case "$(go env GOOS)/$(go env GOARCH)" in - windows/amd64) - export CC=x86_64-w64-mingw32-gcc - export CGO_ENABLED=1 + windows/* | linux/mips* | linux/riscv* | linux/ppc64) ;; + # TODO remove windows in Go 1.15+: https://github.com/golang/go/commit/95f382139043059a2a0780ba577b53893408f7e4 + # TODO remove riscv64 in Go 1.16+: https://github.com/golang/go/commit/8eb846fd37eb7bded8a1cf6932be2c59069863e5 + + *) + BUILDFLAGS+=("-buildmode=pie") ;; esac -fi - -# -buildmode=pie is not supported on Windows and Linux on mips. -case "$(go env GOOS)/$(go env GOARCH)" in - windows/*|linux/mips*) - ;; - *) - BUILDFLAGS+=( "-buildmode=pie" ) - ;; -esac -echo "Building: $DEST/$BINARY_FULLNAME" -go build \ - -o "$DEST/$BINARY_FULLNAME" \ - "${BUILDFLAGS[@]}" \ - -ldflags " + echo "Building: $DEST/$BINARY_FULLNAME" + echo "GOOS=\"${GOOS}\" GOARCH=\"${GOARCH}\" GOARM=\"${GOARM}\"" + go build \ + -o "$DEST/$BINARY_FULLNAME" \ + "${BUILDFLAGS[@]}" \ + -ldflags " $LDFLAGS $LDFLAGS_STATIC_DOCKER $DOCKER_LDFLAGS " \ - $GO_PACKAGE + ${GO_PACKAGE} ) echo "Created binary: $DEST/$BINARY_FULLNAME" diff --git a/hack/make/.binary-setup b/hack/make/.binary-setup deleted file mode 100644 index 69bb39b364c6e..0000000000000 --- a/hack/make/.binary-setup +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -DOCKER_DAEMON_BINARY_NAME='dockerd' -DOCKER_RUNC_BINARY_NAME='runc' -DOCKER_CONTAINERD_BINARY_NAME='containerd' -DOCKER_CONTAINERD_CTR_BINARY_NAME='ctr' -DOCKER_CONTAINERD_SHIM_BINARY_NAME='containerd-shim' -DOCKER_PROXY_BINARY_NAME='docker-proxy' -DOCKER_INIT_BINARY_NAME='docker-init' diff --git a/hack/make/.detect-daemon-osarch b/hack/make/.detect-daemon-osarch index 91e2c53c7565a..9190cd0264bae 100644 --- a/hack/make/.detect-daemon-osarch +++ b/hack/make/.detect-daemon-osarch @@ -2,13 +2,13 @@ set -e docker-version-osarch() { - if ! type docker &>/dev/null; then + if ! type docker &> /dev/null; then # docker is not installed return fi local target="$1" # "Client" or "Server" local fmtStr="{{.${target}.Os}}/{{.${target}.Arch}}" - if docker version -f "$fmtStr" 2>/dev/null; then + if docker version -f "$fmtStr" 2> /dev/null; then # if "docker version -f" works, let's just use that! return fi diff --git a/hack/make/.go-autogen b/hack/make/.go-autogen index 6397cdc2d3414..5fe33555a5346 100644 --- a/hack/make/.go-autogen +++ b/hack/make/.go-autogen @@ -1,51 +1,24 @@ #!/usr/bin/env bash -rm -rf autogen +rm -rf autogen/* source hack/dockerfile/install/runc.installer source hack/dockerfile/install/tini.installer source hack/dockerfile/install/containerd.installer -cat > dockerversion/version_autogen.go < dockerversion/version_autogen_unix.go <&1 42>&2 +# Allow pushing manifest v2 schema 1 images, as they're used to push +# images to our test-registries for testing _pulling_ schema 2v1 images. +export DOCKER_ALLOW_SCHEMA1_PUSH_DONOTUSE=1 export DOCKER_GRAPHDRIVER=${DOCKER_GRAPHDRIVER:-vfs} export DOCKER_USERLANDPROXY=${DOCKER_USERLANDPROXY:-true} @@ -58,11 +61,34 @@ if [ "$DOCKER_REMAP_ROOT" ]; then fi # example usage: DOCKER_EXPERIMENTAL=1 -if [ "$DOCKER_EXPERIMENTAL" ]; then +if [ "$DOCKER_EXPERIMENTAL" ]; then echo >&2 '# DOCKER_EXPERIMENTAL is set: starting daemon with experimental features enabled! ' extra_params="$extra_params --experimental" fi +dockerd="dockerd" +if [ -f "/sys/fs/cgroup/cgroup.controllers" ]; then + if [ -z "$TEST_SKIP_INTEGRATION_CLI" ]; then + echo >&2 '# cgroup v2 requires TEST_SKIP_INTEGRATION_CLI to be set' + exit 1 + fi +fi + +if [ -n "$DOCKER_ROOTLESS" ]; then + if [ -z "$TEST_SKIP_INTEGRATION_CLI" ]; then + echo >&2 '# DOCKER_ROOTLESS requires TEST_SKIP_INTEGRATION_CLI to be set' + exit 1 + fi + ln -sf "$(command -v vpnkit."$(uname -m)")" /usr/local/bin/vpnkit + user="unprivilegeduser" + uid=$(id -u $user) + # shellcheck disable=SC2174 + mkdir -p -m 700 "/tmp/docker-${uid}" + chown "$user" "/tmp/docker-${uid}" + chmod -R o+w "$DEST" + dockerd="sudo -u $user -E -E XDG_RUNTIME_DIR=/tmp/docker-${uid} -E HOME=/home/${user} -E PATH=$PATH -- dockerd-rootless.sh" +fi + if [ -z "$DOCKER_TEST_HOST" ]; then # Start apparmor if it is enabled if [ -e "/sys/module/apparmor/parameters/enabled" ] && [ "$(cat /sys/module/apparmor/parameters/enabled)" == "Y" ]; then @@ -81,14 +107,14 @@ if [ -z "$DOCKER_TEST_HOST" ]; then echo "Starting dockerd" [ -n "$TESTDEBUG" ] && set -x exec \ - dockerd --debug \ + ${dockerd} --debug \ --host "$DOCKER_HOST" \ --storage-driver "$DOCKER_GRAPHDRIVER" \ --pidfile "$DEST/docker.pid" \ --userland-proxy="$DOCKER_USERLANDPROXY" \ - $storage_params \ - $extra_params \ - &> "$DEST/docker.log" + ${storage_params} \ + ${extra_params} \ + &> "$DEST/docker.log" ) & else export DOCKER_HOST="$DOCKER_TEST_HOST" @@ -97,8 +123,8 @@ fi # give it a little time to come up so it's "ready" tries=60 echo "INFO: Waiting for daemon to start..." -while ! $TEST_CLIENT_BINARY version &> /dev/null; do - (( tries-- )) +while ! ${TEST_CLIENT_BINARY} version &> /dev/null; do + ((tries--)) if [ $tries -le 0 ]; then printf "\n" if [ -z "$DOCKER_HOST" ]; then @@ -106,7 +132,7 @@ while ! $TEST_CLIENT_BINARY version &> /dev/null; do echo >&2 " check $DEST/docker.log for details" else echo >&2 "error: daemon at $DOCKER_HOST fails to '$TEST_CLIENT_BINARY version':" - $TEST_CLIENT_BINARY version >&2 || true + ${TEST_CLIENT_BINARY} version >&2 || true # Additional Windows CI debugging as this is a common error as of # January 2016 if [ "$(go env GOOS)" = 'windows' ]; then diff --git a/hack/make/.integration-daemon-stop b/hack/make/.integration-daemon-stop index c1d43e1a5e771..59f62aa593774 100644 --- a/hack/make/.integration-daemon-stop +++ b/hack/make/.integration-daemon-stop @@ -2,7 +2,10 @@ if [ ! "$(go env GOOS)" = 'windows' ]; then for pidFile in $(find "$DEST" -name docker.pid); do - pid=$([ -n "$TESTDEBUG" ] && set -x; cat "$pidFile") + pid=$( + [ -n "$TESTDEBUG" ] && set -x + cat "$pidFile" + ) ( [ -n "$TESTDEBUG" ] && set -x kill "$pid" @@ -10,6 +13,10 @@ if [ ! "$(go env GOOS)" = 'windows' ]; then if ! wait "$pid"; then echo >&2 "warning: PID $pid from $pidFile had a nonzero exit code" fi + root=$(dirname "$pidFile")/root + if [ -d "$root" ]; then + umount "$root" || true + fi done if [ -z "$DOCKER_TEST_HOST" ]; then @@ -17,12 +24,12 @@ if [ ! "$(go env GOOS)" = 'windows' ]; then if [ -e "/sys/module/apparmor/parameters/enabled" ] && [ "$(cat /sys/module/apparmor/parameters/enabled)" == "Y" ]; then ( [ -n "$TESTDEBUG" ] && set -x - /etc/init.d/apparmor stop + /etc/init.d/apparmor stop || true ) fi fi else - # Note this script is not actionable on Windows to Linux CI. Instead the + # Note this script is not actionable on Windows to Linux CI. Instead the # DIND daemon under test is torn down by the Jenkins tear-down script echo "INFO: Not stopping daemon on Windows CI" fi diff --git a/hack/make/.integration-test-helpers b/hack/make/.integration-test-helpers index 149b6538004c2..adc9c0c0a7f34 100644 --- a/hack/make/.integration-test-helpers +++ b/hack/make/.integration-test-helpers @@ -3,56 +3,109 @@ # For integration-cli test, we use [gocheck](https://labix.org/gocheck), if you want # to run certain tests on your local host, you should run with command: # -# TESTFLAGS='-check.f DockerSuite.TestBuild*' ./hack/make.sh binary test-integration +# TESTFLAGS='-test.run TestDockerSuite/TestBuild*' ./hack/make.sh binary test-integration # -if [ -z $MAKEDIR ]; then - export MAKEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +if [ -z "${MAKEDIR}" ]; then + MAKEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + export MAKEDIR fi -source "$MAKEDIR/.go-autogen" +source "${MAKEDIR}/.go-autogen" # Set defaults -: ${TEST_REPEAT:=1} -: ${TESTFLAGS:=} -: ${TESTDEBUG:=} +: "${TEST_REPEAT:=1}" +: "${TESTFLAGS:=}" +: "${TESTDEBUG:=}" +: "${GOCACHE:=$(go env GOCACHE)}" -integration_api_dirs=${TEST_INTEGRATION_DIR:-"$( - find ./integration -type d | - grep -vE '(^./integration($|/internal)|/testdata)')"} +setup_integration_test_filter() { + if [ -z "${TEST_FILTER}" ]; then + return + fi + + local dirs + dirs=$(grep -rIlE --include '*_test.go' "func .*${TEST_FILTER}.*\(. \*testing\.T\)" ./integration*/ | xargs -I file dirname file | uniq) + if [ -z "${TEST_SKIP_INTEGRATION}" ]; then + : "${TEST_INTEGRATION_DIR:=$(echo "$dirs" | grep -v '^\./integration-cli$')}" + if [ -z "${TEST_INTEGRATION_DIR}" ]; then + echo "Skipping integration tests since the supplied filter \"${TEST_FILTER}\" omits all integration tests" + TEST_SKIP_INTEGRATION=1 + else + TESTFLAGS+=" -test.run ${TEST_FILTER}" + fi + fi + + if [ -z "${TEST_SKIP_INTEGRATION_CLI}" ]; then + if echo "$dirs" | grep -vq '^./integration-cli$'; then + TEST_SKIP_INTEGRATION_CLI=1 + echo "Skipping integration-cli tests since the supplied filter \"${TEST_FILTER}\" omits all integration-cli tests" + else + TESTFLAGS+=" -test.run /${TEST_FILTER}" + fi + fi +} + +setup_integration_test_filter +if [ -z "${TEST_SKIP_INTEGRATION}" ] && [ -z "${TEST_INTEGRATION_DIR}" ]; then + integration_api_dirs="$(go list -test -f '{{- if ne .ForTest "" -}}{{- .Dir -}}{{- end -}}' ./integration/...)" +else + integration_api_dirs="${TEST_INTEGRATION_DIR}" +fi run_test_integration() { - [[ "$TESTFLAGS" != *-check.f* ]] && run_test_integration_suites - run_test_integration_legacy_suites + set_platform_timeout + if [ -z "${TEST_SKIP_INTEGRATION}" ]; then + run_test_integration_suites "${integration_api_dirs}" + fi + if [ -z "${TEST_SKIP_INTEGRATION_CLI}" ]; then + TIMEOUT=360m run_test_integration_suites integration-cli + fi } run_test_integration_suites() { local flags="-test.v -test.timeout=${TIMEOUT} $TESTFLAGS" - for dir in $integration_api_dirs; do + local dirs="$1" + for dir in ${dirs}; do if ! ( - cd $dir - echo "Running $PWD" - test_env ./test.main $flags + cd "$dir" + # Create a useful package name based on the tests's $dir. We need to take + # into account that "$dir" can be either an absolute (/go/src/github.com/docker/docker/integration/foo) + # or relative (./integration/foo) path. To account for both, first we strip + # the absolute path, then remove any leading periods and slashes. + pkgname="${dir}" + pkgname="${pkgname#*${GOPATH}/src/${DOCKER_PKG}}" + pkgname="${pkgname#*.}" + pkgname="${pkgname#*\/}" + + # Finally, we use periods as separator (instead of slashes) to be more + # in line with Java package names (which is what junit.xml was designed for) + pkgname="$(go env GOARCH).${pkgname//\//.}" + echo "Running $PWD (${pkgname}) flags=${flags}" + [ -n "$TESTDEBUG" ] && set -x + # shellcheck disable=SC2086 + test_env gotestsum \ + --format=standard-verbose \ + --jsonfile="${ABS_DEST}/${pkgname//./-}-go-test-report.json" \ + --junitfile="${ABS_DEST}/${pkgname//./-}-junit-report.xml" \ + --raw-command \ + -- go tool test2json -p "${pkgname}" -t ./test.main ${flags} ); then exit 1; fi done } -run_test_integration_legacy_suites() { - ( - flags="-check.v -check.timeout=${TIMEOUT} -test.timeout=360m $TESTFLAGS" - cd integration-cli - echo "Running $PWD" - test_env ./test.main $flags - ) -} - build_test_suite_binaries() { - if [ ${DOCKER_INTEGRATION_TESTS_VERIFIED-} ]; then + if [ -n "${DOCKER_INTEGRATION_TESTS_VERIFIED}" ]; then echo "Skipping building test binaries; as DOCKER_INTEGRATION_TESTS_VERIFIED is set" return fi - build_test_suite_binary ./integration-cli "test.main" - for dir in $integration_api_dirs; do - build_test_suite_binary "$dir" "test.main" - done + if [ -z "${TEST_SKIP_INTEGRATION_CLI}" ]; then + build_test_suite_binary ./integration-cli "test.main" + fi + if [ -z "${TEST_SKIP_INTEGRATION}" ]; then + for dir in ${integration_api_dirs}; do + build_test_suite_binary "$dir" "test.main" + done + fi } # Build a binary for a test suite package @@ -66,11 +119,12 @@ build_test_suite_binary() { cleanup_test_suite_binaries() { [ -n "$TESTDEBUG" ] && return echo "Removing test suite binaries" + # shellcheck disable=SC2038 find integration* -name test.main | xargs -r rm } repeat() { - for i in $(seq 1 $TEST_REPEAT); do + for i in $(seq 1 ${TEST_REPEAT}); do echo "Running integration-test (iteration $i)" $@ done @@ -94,7 +148,9 @@ test_env() { DOCKER_HOST="$DOCKER_HOST" \ DOCKER_REMAP_ROOT="$DOCKER_REMAP_ROOT" \ DOCKER_REMOTE_DAEMON="$DOCKER_REMOTE_DAEMON" \ + DOCKER_ROOTLESS="$DOCKER_ROOTLESS" \ DOCKERFILE="$DOCKERFILE" \ + GOCACHE="$GOCACHE" \ GOPATH="$GOPATH" \ GOTRACEBACK=all \ HOME="$ABS_DEST/fake-HOME" \ @@ -104,19 +160,39 @@ test_env() { "$@" ) } - error_on_leaked_containerd_shims() { - if [ "$(go env GOOS)" == 'windows' ]; then + if [ "$(go env GOOS)" = 'windows' ]; then return fi - leftovers=$(ps -ax -o pid,cmd | - awk '$2 == "containerd-shim" && $4 ~ /.*\/bundles\/.*\/test-integration/ { print $1 }') + leftovers=$(ps -ax -o pid,cmd \ + | awk '$2 == "containerd-shim" && $4 ~ /.*\/bundles\/.*\/test-integration/ { print $1 }') if [ -n "$leftovers" ]; then ps aux - kill -9 $leftovers 2> /dev/null + # shellcheck disable=SC2086 + kill -9 ${leftovers} 2> /dev/null echo "!!!! WARNING you have left over shim(s), Cleanup your test !!!!" exit 1 fi } + +set_platform_timeout() { + # Test timeout. + if [ "${DOCKER_ENGINE_GOARCH}" = "arm64" ] || [ "${DOCKER_ENGINE_GOARCH}" = "arm" ]; then + : "${TIMEOUT:=10m}" + elif [ "${DOCKER_ENGINE_GOARCH}" = "windows" ]; then + : "${TIMEOUT:=8m}" + else + : "${TIMEOUT:=5m}" + fi + + if [ "${TEST_REPEAT}" -gt 1 ]; then + # TIMEOUT needs to take TEST_REPEAT into account, or a premature time out may happen. + # The following ugliness will: + # - remove last character (usually 'm' from '10m') + # - multiply by testcount + # - add last character back + TIMEOUT=$((${TIMEOUT::-1} * ${TEST_REPEAT}))${TIMEOUT:$((${#TIMEOUT} - 1)):1} + fi +} diff --git a/hack/make/.resources-windows/common.rc b/hack/make/.resources-windows/common.rc index 000fb353670a2..3524d9bf483fb 100644 --- a/hack/make/.resources-windows/common.rc +++ b/hack/make/.resources-windows/common.rc @@ -1,8 +1,8 @@ // Application icon -1 ICON "docker.ico" +1 ICON "dockerd.ico" // Windows executable manifest -1 24 /* RT_MANIFEST */ "docker.exe.manifest" +1 24 /* RT_MANIFEST */ "dockerd.exe.manifest" // Version information 1 VERSIONINFO diff --git a/hack/make/.resources-windows/docker.ico b/hack/make/.resources-windows/docker.ico deleted file mode 100644 index c6506ec8dbd8e..0000000000000 Binary files a/hack/make/.resources-windows/docker.ico and /dev/null differ diff --git a/hack/make/.resources-windows/docker.png b/hack/make/.resources-windows/docker.png deleted file mode 100644 index 88df0b66dfcf0..0000000000000 Binary files a/hack/make/.resources-windows/docker.png and /dev/null differ diff --git a/hack/make/.resources-windows/docker.rc b/hack/make/.resources-windows/docker.rc deleted file mode 100644 index 40c645ad1da0a..0000000000000 --- a/hack/make/.resources-windows/docker.rc +++ /dev/null @@ -1,3 +0,0 @@ -#define DOCKER_NAME "Docker Client" - -#include "common.rc" diff --git a/hack/make/.resources-windows/docker.exe.manifest b/hack/make/.resources-windows/dockerd.exe.manifest similarity index 100% rename from hack/make/.resources-windows/docker.exe.manifest rename to hack/make/.resources-windows/dockerd.exe.manifest diff --git a/hack/make/.resources-windows/dockerd.ico b/hack/make/.resources-windows/dockerd.ico new file mode 100644 index 0000000000000..1d5ab4f0a9bd1 Binary files /dev/null and b/hack/make/.resources-windows/dockerd.ico differ diff --git a/hack/make/.resources-windows/resources.go b/hack/make/.resources-windows/resources.go index b171259f83358..3ade4eadd96c7 100644 --- a/hack/make/.resources-windows/resources.go +++ b/hack/make/.resources-windows/resources.go @@ -1,6 +1,6 @@ /* -Package winresources is used to embed Windows resources into docker.exe. +Package winresources is used to embed Windows resources into dockerd.exe. These resources are used to provide * Version information diff --git a/hack/make/binary b/hack/make/binary index eab69bb0650bf..9e24410adb715 100644 --- a/hack/make/binary +++ b/hack/make/binary @@ -7,4 +7,5 @@ rm -rf "$DEST" DEST="${DEST}-daemon" ABS_DEST="${ABS_DEST}-daemon" . hack/make/binary-daemon + . hack/make/binary-proxy ) diff --git a/hack/make/binary-daemon b/hack/make/binary-daemon index c1a6e6f9ed5fd..fffceabc06386 100644 --- a/hack/make/binary-daemon +++ b/hack/make/binary-daemon @@ -14,14 +14,28 @@ copy_binaries() { return fi echo "Copying nested executables into $dir" - for file in containerd containerd-shim ctr runc docker-init docker-proxy; do - cp -f `which "$file"` "$dir/" - if [ "$hash" == "hash" ]; then + for file in containerd containerd-shim containerd-shim-runc-v2 ctr runc docker-init rootlesskit rootlesskit-docker-proxy dockerd-rootless.sh dockerd-rootless-setuptool.sh; do + cp -f "$(command -v "$file")" "$dir/" + if [ "$hash" = "hash" ]; then hash_files "$dir/$file" fi done + + # vpnkit is available for x86_64 and aarch64 + if command -v "vpnkit.$(uname -m)" 2>&1 > /dev/null; then + cp -f "$(command -v "vpnkit.$(uname -m)")" "$dir/vpnkit" + if [ "$hash" = "hash" ]; then + hash_files "$dir/vpnkit" + fi + fi } [ -z "$KEEPDEST" ] && rm -rf "$DEST" -source "${MAKEDIR}/.binary" -copy_binaries "$DEST" 'hash' + +( + GO_PACKAGE='github.com/docker/docker/cmd/dockerd' + BINARY_SHORT_NAME='dockerd' + + source "${MAKEDIR}/.binary" + copy_binaries "$DEST" 'hash' +) diff --git a/hack/make/binary-proxy b/hack/make/binary-proxy new file mode 100644 index 0000000000000..287a86e09de6b --- /dev/null +++ b/hack/make/binary-proxy @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +( + GO_PACKAGE='github.com/docker/docker/cmd/docker-proxy' + BINARY_SHORT_NAME='docker-proxy' + + source "${MAKEDIR}/.binary" +) diff --git a/hack/make/build-integration-test-binary b/hack/make/build-integration-test-binary index bbd5a22bccc1c..698717f0f5640 100755 --- a/hack/make/build-integration-test-binary +++ b/hack/make/build-integration-test-binary @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# required by `make build-integration-cli-on-swarm` +# required by https://github.com/AkihiroSuda/kube-moby-integration set -e source hack/make/.integration-test-helpers diff --git a/hack/make/containerutility b/hack/make/containerutility index 8aefc00ff08d6..8525d971f6053 100644 --- a/hack/make/containerutility +++ b/hack/make/containerutility @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -e -CONTAINER_UTILITY_COMMIT=e004a1415a433447369e315b9d7df357102be0d2 # v0.9.0 +: "${CONTAINER_UTILITY_COMMIT:=aa1ba87e99b68e0113bd27ec26c60b88f9d4ccd9}" ( git clone https://github.com/docker/windows-container-utility.git "$GOPATH/src/github.com/docker/windows-container-utility" diff --git a/hack/make/cross b/hack/make/cross index 497f02ae4ba3c..a20148b1fbaec 100644 --- a/hack/make/cross +++ b/hack/make/cross @@ -2,29 +2,35 @@ set -e # if we have our linux/amd64 version compiled, let's symlink it in -if [ -x "$DEST/../binary-daemon/dockerd-$VERSION" ]; then +if [ -x "${DEST}/../binary-daemon/dockerd-${VERSION}" ]; then arch=$(go env GOHOSTARCH) mkdir -p "$DEST/linux/${arch}" ( - cd "$DEST/linux/${arch}" + cd "${DEST}/linux/${arch}" ln -sf ../../../binary-daemon/* ./ ) - echo "Created symlinks:" "$DEST/linux/${arch}/"* + echo "Created symlinks:" "${DEST}/linux/${arch}/"* fi -DOCKER_CROSSPLATFORMS=${DOCKER_CROSSPLATFORMS:-"linux/amd64 windows/amd64"} +DOCKER_CROSSPLATFORMS=${DOCKER_CROSSPLATFORMS:-"linux/amd64 windows/amd64 linux/ppc64le linux/s390x"} -for platform in $DOCKER_CROSSPLATFORMS; do +for platform in ${DOCKER_CROSSPLATFORMS}; do ( export KEEPDEST=1 - export DEST="$DEST/$platform" # bundles/VERSION/cross/GOOS/GOARCH/docker-VERSION - export GOOS=${platform%/*} - export GOARCH=${platform##*/} + export DEST="${DEST}/${platform}" # bundles/VERSION/cross/GOOS/GOARCH/docker-VERSION + export GOOS=${platform%%/*} + export GOARCH=${platform#*/} - echo "Cross building: $DEST" - mkdir -p "$DEST" - ABS_DEST="$(cd "$DEST" && pwd -P)" - source "${MAKEDIR}/binary-daemon" + if [[ "${GOARCH}" = "arm/"* ]]; then + GOARM=${GOARCH##*/v} + GOARCH=${GOARCH%/v*} + export GOARM + fi + + echo "Cross building: ${DEST}" + mkdir -p "${DEST}" + ABS_DEST="$(cd "${DEST}" && pwd -P)" + source "${MAKEDIR}/binary" source "${MAKEDIR}/cross-platform-dependent" ) diff --git a/hack/make/cross-platform-dependent b/hack/make/cross-platform-dependent index 52632c30366bb..21824ed7c96e4 100644 --- a/hack/make/cross-platform-dependent +++ b/hack/make/cross-platform-dependent @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -e -if [ $platform == "windows/amd64" ]; then +if [ ${platform} == "windows/amd64" ]; then source "${MAKEDIR}/containerutility" fi diff --git a/hack/make/dynbinary b/hack/make/dynbinary index 981e505e9fef6..08ce8b5725e99 100644 --- a/hack/make/dynbinary +++ b/hack/make/dynbinary @@ -1,10 +1,11 @@ #!/usr/bin/env bash set -e +rm -rf "$DEST" # This script exists as backwards compatibility for CI ( - - DEST="${DEST}-daemon" - ABS_DEST="${ABS_DEST}-daemon" - . hack/make/dynbinary-daemon + DEST="${DEST}-daemon" + ABS_DEST="${ABS_DEST}-daemon" + . hack/make/dynbinary-daemon + . hack/make/dynbinary-proxy ) diff --git a/hack/make/dynbinary-daemon b/hack/make/dynbinary-daemon index c712bfefc281b..fe2413d73417c 100644 --- a/hack/make/dynbinary-daemon +++ b/hack/make/dynbinary-daemon @@ -1,11 +1,16 @@ #!/usr/bin/env bash set -e +[ -z "$KEEPDEST" ] && rm -rf "$DEST" + ( export IAMSTATIC='false' export LDFLAGS_STATIC_DOCKER='' - export BUILDFLAGS=( "${BUILDFLAGS[@]/netgo /}" ) # disable netgo, since we don't need it for a dynamic binary - export BUILDFLAGS=( "${BUILDFLAGS[@]/osusergo /}" ) # ditto for osusergo - export BUILDFLAGS=( "${BUILDFLAGS[@]/static_build /}" ) # we're not building a "static" binary here + export BUILDFLAGS=("${BUILDFLAGS[@]/netgo /}") # disable netgo, since we don't need it for a dynamic binary + export BUILDFLAGS=("${BUILDFLAGS[@]/osusergo /}") # ditto for osusergo + export BUILDFLAGS=("${BUILDFLAGS[@]/static_build /}") # we're not building a "static" binary here + + GO_PACKAGE='github.com/docker/docker/cmd/dockerd' + BINARY_SHORT_NAME='dockerd' source "${MAKEDIR}/.binary" ) diff --git a/hack/make/dynbinary-proxy b/hack/make/dynbinary-proxy new file mode 100644 index 0000000000000..3e16676061ab9 --- /dev/null +++ b/hack/make/dynbinary-proxy @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -e + +( + export IAMSTATIC='false' + export LDFLAGS_STATIC_DOCKER='' + export BUILDFLAGS=("${BUILDFLAGS[@]/netgo /}") # disable netgo, since we don't need it for a dynamic binary + export BUILDFLAGS=("${BUILDFLAGS[@]/osusergo /}") # ditto for osusergo + export BUILDFLAGS=("${BUILDFLAGS[@]/static_build /}") # we're not building a "static" binary here + + GO_PACKAGE='github.com/docker/docker/cmd/docker-proxy' + BINARY_SHORT_NAME='docker-proxy' + source "${MAKEDIR}/.binary" +) diff --git a/hack/make/install-binary b/hack/make/install-binary index f6a4361fdb0ea..d643c3251bb43 100644 --- a/hack/make/install-binary +++ b/hack/make/install-binary @@ -3,27 +3,23 @@ set -e rm -rf "$DEST" -install_binary() { - local file="$1" - local target="${DOCKER_MAKE_INSTALL_PREFIX:=/usr/local}/bin/" - if [ "$(go env GOOS)" == "linux" ]; then - echo "Installing $(basename $file) to ${target}" - mkdir -p "$target" - cp -f -L "$file" "$target" - else - echo "Install is only supported on linux" - return 1 - fi -} +source "${MAKEDIR}/.install" ( DEST="$(dirname $DEST)/binary-daemon" - source "${MAKEDIR}/.binary-setup" - install_binary "${DEST}/${DOCKER_DAEMON_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_RUNC_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_CONTAINERD_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_CONTAINERD_CTR_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_CONTAINERD_SHIM_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_PROXY_BINARY_NAME}" - install_binary "${DEST}/${DOCKER_INIT_BINARY_NAME}" + install_binary "${DEST}/dockerd" + install_binary "${DEST}/runc" + install_binary "${DEST}/containerd" + install_binary "${DEST}/ctr" + install_binary "${DEST}/containerd-shim" + install_binary "${DEST}/containerd-shim-runc-v2" + install_binary "${DEST}/docker-proxy" + install_binary "${DEST}/docker-init" + install_binary "${DEST}/rootlesskit" + install_binary "${DEST}/rootlesskit-docker-proxy" + install_binary "${DEST}/dockerd-rootless.sh" + install_binary "${DEST}/dockerd-rootless-setuptool.sh" + if [ -f "${DEST}/vpnkit" ]; then + install_binary "${DEST}/vpnkit" + fi ) diff --git a/hack/make/install-proxy b/hack/make/install-proxy new file mode 100644 index 0000000000000..04ea1bf88efdc --- /dev/null +++ b/hack/make/install-proxy @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -e +rm -rf "$DEST" + +source "${MAKEDIR}/.install" + +( + DEST="$(dirname $DEST)/binary-proxy" + install_binary "${DEST}/docker-proxy" +) diff --git a/hack/make/run b/hack/make/run index 3254280260643..1c433dd6a0415 100644 --- a/hack/make/run +++ b/hack/make/run @@ -21,7 +21,6 @@ if [ -n "$DOCKER_STORAGE_OPTS" ]; then unset IFS fi - listen_port=2375 if [ -n "$DOCKER_PORT" ]; then IFS=':' read -r -a ports <<< "$DOCKER_PORT" @@ -33,12 +32,29 @@ if [ "$DOCKER_REMAP_ROOT" ]; then extra_params="$extra_params --userns-remap $DOCKER_REMAP_ROOT" fi +if [ -n "$DOCKER_EXPERIMENTAL" ]; then + extra_params="$extra_params --experimental" +fi + +dockerd="dockerd" +socket=/var/run/docker.sock +if [ -n "$DOCKER_ROOTLESS" ]; then + user="unprivilegeduser" + uid=$(id -u $user) + # shellcheck disable=SC2174 + mkdir -p -m 700 "/tmp/docker-${uid}" + chown $user "/tmp/docker-${uid}" + dockerd="sudo -u $user -E XDG_RUNTIME_DIR=/tmp/docker-${uid} -E HOME=/home/${user} -- dockerd-rootless.sh" + socket=/tmp/docker-${uid}/docker.sock +fi + args="--debug \ - --host tcp://0.0.0.0:${listen_port} --host unix:///var/run/docker.sock \ - --storage-driver "$DOCKER_GRAPHDRIVER" \ - --userland-proxy="$DOCKER_USERLANDPROXY" \ + --host "tcp://0.0.0.0:${listen_port}" --host "unix://${socket}" \ + --storage-driver "${DOCKER_GRAPHDRIVER}" \ + --userland-proxy="${DOCKER_USERLANDPROXY}" \ $storage_params \ $extra_params" -echo dockerd $args -exec dockerd $args +echo "${dockerd} ${args}" +# shellcheck disable=SC2086 +exec "${dockerd}" ${args} diff --git a/hack/make/test-docker-py b/hack/make/test-docker-py index b30879e3a0e40..436262037ca5b 100644 --- a/hack/make/test-docker-py +++ b/hack/make/test-docker-py @@ -3,18 +3,60 @@ set -e source hack/make/.integration-test-helpers -# subshell so that we can export PATH without breaking other things +# The commit or tag to use for testing +# TODO docker 17.06 cli client used in CI fails to build using a sha; +# unable to prepare context: unable to 'git clone' to temporary context directory: error fetching: error: no such remote ref ead0bb9e08c13dd3d1712759491eee06bf5a5602 +#: exit status 128 +: "${DOCKER_PY_COMMIT:=5.0.3}" + +# custom options to pass py.test +# +# This option can be used to temporarily skip flaky tests (using the `--deselect` +# flag) until they are fixed upstream. For example: +# --deselect=tests/integration/api_container_test.py::AttachContainerTest::test_attach_no_stream +# TODO re-enable test after https://github.com/docker/docker-py/issues/2513 has been resolved +: "${PY_TEST_OPTIONS:=--junitxml=${DEST}/junit-report.xml --deselect=tests/integration/api_container_test.py::AttachContainerTest::test_attach_no_stream}" ( bundle .integration-daemon-start - dockerPy='/docker-py' - [ -d "$dockerPy" ] || { - dockerPy="$DEST/docker-py" - git clone https://github.com/docker/docker-py.git "$dockerPy" - } + docker_host_scheme=$(echo "${DOCKER_HOST}" | cut -d: -f1 -) + + case "${docker_host_scheme}" in + unix) + # trim the tcp:// scheme, and bind-mount the docker socket into the container + run_opts="--mount type=bind,src=${DOCKER_HOST#unix://},dst=/var/run/docker.sock" + ;; + + tcp) + # run container in host-mode networking so that it can connect to the + # daemon from the current networking namespace (e.g., to connect to localhost) + run_opts="--network=host -e DOCKER_HOST=${DOCKER_HOST}" + ;; + + *) + echo "WARN: Skipping test-docker-py: connecting to docker daemon using ${docker_host_scheme} (${DOCKER_HOST}) not supported" + bundle .integration-daemon-stop + return 0 + ;; + esac - # exporting PYTHONPATH to import "docker" from our local docker-py - test_env PYTHONPATH="$dockerPy" py.test --junitxml="$DEST/results.xml" "$dockerPy/tests/integration" + docker_py_image="docker-sdk-python3:${DOCKER_PY_COMMIT}" + if ! docker image inspect "dockerPyImage" &> /dev/null; then + echo INFO: Building ${docker_py_image}... + ( + [ -n "${TESTDEBUG}" ] && set -x + [ -z "${TESTDEBUG}" ] && build_opts="--quiet" + [ -f /.dockerenv ] || build_opts="${build_opts} --network=host" + # shellcheck disable=SC2086 + exec docker build ${build_opts} -t "${docker_py_image}" -f tests/Dockerfile "https://github.com/docker/docker-py.git#${DOCKER_PY_COMMIT}" + ) + fi + echo INFO: Starting docker-py tests... + ( + [ -n "${TESTDEBUG}" ] && set -x + # shellcheck disable=SC2086,SC2140 + exec docker run --rm ${run_opts} --mount type=bind,"src=${ABS_DEST}","dst=/src/${DEST}" "${docker_py_image}" pytest ${PY_TEST_OPTIONS} tests/integration + ) bundle .integration-daemon-stop ) 2>&1 | tee -a "$DEST/test.log" diff --git a/hack/make/test-integration b/hack/make/test-integration index c807cd4978d35..1545cc49eedd8 100755 --- a/hack/make/test-integration +++ b/hack/make/test-integration @@ -3,19 +3,27 @@ set -e -o pipefail source hack/make/.integration-test-helpers +if [ ! -z "${TEST_SKIP_INTEGRATION}" ] && [ ! -z "${TEST_SKIP_INTEGRATION_CLI}" ]; then + echo integration and integration-cli skipped according to env vars + exit 0 +fi + ( + env build_test_suite_binaries bundle .integration-daemon-start bundle .integration-daemon-setup - local testexit=0 - ( repeat run_test_integration ) || testexit=$? + testexit=0 + (repeat run_test_integration) || testexit=$? # Always run cleanup, even if the subshell fails bundle .integration-daemon-stop cleanup_test_suite_binaries error_on_leaked_containerd_shims - exit $testexit + echo exiting test-integration + set -x + exit ${testexit} ) 2>&1 | tee -a "$DEST/test.log" diff --git a/hack/make/test-integration-flaky b/hack/make/test-integration-flaky new file mode 100644 index 0000000000000..eef203b96c62f --- /dev/null +++ b/hack/make/test-integration-flaky @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -e -o pipefail + +source hack/validate/.validate + +run_integration_flaky() { + new_tests=$( + validate_diff --diff-filter=ACMR --unified=0 -- 'integration/*_test.go' \ + | grep -E '^(\+func Test)(.*)(\*testing\.T\))' || true + ) + + if [ -z "$new_tests" ]; then + echo 'No new tests added to integration.' + return + fi + + echo + echo "Found new integrations tests:" + echo "$new_tests" + echo "Running stress test for them." + + ( + TESTARRAY=$(echo "$new_tests" | sed 's/+func //' | awk -F'\\(' '{print $1}' | tr '\n' '|') + # Note: TEST_REPEAT will make the test suite run 5 times, restarting the daemon + # and each test will run 5 times in a row under the same daemon. + # This will make a total of 25 runs for each test in TESTARRAY. + export TEST_REPEAT=5 + export TESTFLAGS="-test.count ${TEST_REPEAT} -test.run ${TESTARRAY%?}" + echo "Using test flags: $TESTFLAGS" + source hack/make/test-integration + ) +} + +run_integration_flaky diff --git a/hack/test/e2e-run.sh b/hack/test/e2e-run.sh index 122d58f8ef9df..57127c0d18080 100755 --- a/hack/test/e2e-run.sh +++ b/hack/test/e2e-run.sh @@ -2,8 +2,8 @@ set -e -u -o pipefail ARCH=$(uname -m) -if [ "$ARCH" == "x86_64" ]; then - ARCH="amd64" +if [ "$ARCH" = "x86_64" ]; then + ARCH="amd64" fi export DOCKER_ENGINE_GOARCH=${DOCKER_ENGINE_GOARCH:-${ARCH}} @@ -13,11 +13,13 @@ export DOCKER_ENGINE_GOARCH=${DOCKER_ENGINE_GOARCH:-${ARCH}} : ${TESTDEBUG:=} integration_api_dirs=${TEST_INTEGRATION_DIR:-"$( - find /tests/integration -type d | - grep -vE '(^/tests/integration($|/internal)|/testdata)')"} + find /tests/integration -type d \ + | grep -vE '(^/tests/integration($|/internal)|/testdata)' +)"} run_test_integration() { - [[ "$TESTFLAGS" != *-check.f* ]] && run_test_integration_suites + set_platform_timeout + run_test_integration_suites run_test_integration_legacy_suites } @@ -34,7 +36,7 @@ run_test_integration_suites() { run_test_integration_legacy_suites() { ( - flags="-check.v -check.timeout=${TIMEOUT:-200m} -test.timeout=360m $TESTFLAGS" + flags="-test.v -test.timeout=360m $TESTFLAGS" cd /tests/integration-cli echo "Running $PWD" test_env ./test.main $flags @@ -68,5 +70,16 @@ test_env() { ) } +set_platform_timeout() { + # Test timeout. + if [ "${DOCKER_ENGINE_GOARCH}" = "arm64" ] || [ "${DOCKER_ENGINE_GOARCH}" = "arm" ]; then + : ${TIMEOUT:=10m} + elif [ "${DOCKER_ENGINE_GOARCH}" = "windows" ]; then + : ${TIMEOUT:=8m} + else + : ${TIMEOUT:=5m} + fi +} + sh /scripts/ensure-emptyfs.sh run_test_integration diff --git a/hack/test/unit b/hack/test/unit index ac27f68c309da..7c8ff53adce80 100755 --- a/hack/test/unit +++ b/hack/test/unit @@ -1,34 +1,52 @@ #!/usr/bin/env bash # -# Run unit tests +# Run unit tests and create report # # TESTFLAGS - add additional test flags. Ex: # -# TESTFLAGS="-v -run TestBuild" hack/test/unit +# TESTFLAGS='-v -run TestBuild' hack/test/unit # # TESTDIRS - run tests for specified packages. Ex: # -# TESTDIRS="./pkg/term" hack/test/unit +# TESTDIRS='./pkg/term' hack/test/unit # -set -eu -o pipefail +set -eux -o pipefail +BUILDFLAGS=(-tags 'netgo seccomp libdm_no_deferred_remove') TESTFLAGS+=" -test.timeout=${TIMEOUT:-5m}" -BUILDFLAGS=( -tags "netgo seccomp libdm_no_deferred_remove" ) -TESTDIRS="${TESTDIRS:-"./..."}" - -exclude_paths="/vendor/|/integration" +TESTDIRS="${TESTDIRS:-./...}" +exclude_paths='/vendor/|/integration' pkg_list=$(go list $TESTDIRS | grep -vE "($exclude_paths)") -for pkg in $pkg_list; do - go test "${BUILDFLAGS[@]}" \ - -cover \ - -coverprofile=profile.out \ - -covermode=atomic \ - $TESTFLAGS \ - "${pkg}" +base_pkg_list=$(echo "${pkg_list}" | grep --fixed-strings -v "/libnetwork" || :) +libnetwork_pkg_list=$(echo "${pkg_list}" | grep --fixed-strings "/libnetwork" || :) + +echo "${libnetwork_pkg_list}" | grep --fixed-strings "libnetwork/drivers/bridge" \ + && if ! type docker-proxy; then + hack/make.sh binary-proxy install-proxy + fi + +mkdir -p bundles - if test -f profile.out; then - cat profile.out >> coverage.txt - rm profile.out - fi -done +if [ -n "${base_pkg_list}" ]; then + gotestsum --format=standard-quiet --jsonfile=bundles/go-test-report.json --junitfile=bundles/junit-report.xml -- \ + "${BUILDFLAGS[@]}" \ + -cover \ + -coverprofile=bundles/profile.out \ + -covermode=atomic \ + ${TESTFLAGS} \ + ${base_pkg_list} +fi +if [ -n "${libnetwork_pkg_list}" ]; then + # libnetwork tests invoke iptables, and cannot be run in parallel. Execute + # tests within /libnetwork with '-p=1' to run them sequentially. See + # https://github.com/moby/moby/issues/42458#issuecomment-873216754 for details. + gotestsum --format=standard-quiet --jsonfile=bundles/go-test-report-libnetwork.json --junitfile=bundles/junit-report-libnetwork.xml -- \ + "${BUILDFLAGS[@]}" \ + -cover \ + -coverprofile=bundles/profile-libnetwork.out \ + -covermode=atomic \ + -p=1 \ + ${TESTFLAGS} \ + ${libnetwork_pkg_list} +fi diff --git a/hack/validate/.validate b/hack/validate/.validate index 32cb6b6d64fe4..6ed3a59a736d8 100644 --- a/hack/validate/.validate +++ b/hack/validate/.validate @@ -6,13 +6,16 @@ if [ -z "$VALIDATE_UPSTREAM" ]; then # this is kind of an expensive check, so let's not do this twice if we # are running more than one validate bundlescript - VALIDATE_REPO='https://github.com/docker/docker.git' - VALIDATE_BRANCH='master' + VALIDATE_REPO="${VALIDATE_REPO:-https://github.com/docker/docker.git}" + VALIDATE_BRANCH="${VALIDATE_BRANCH:-master}" VALIDATE_HEAD="$(git rev-parse --verify HEAD)" - git fetch -q "$VALIDATE_REPO" "refs/heads/$VALIDATE_BRANCH" - VALIDATE_UPSTREAM="$(git rev-parse --verify FETCH_HEAD)" + if [ -z "$VALIDATE_ORIGIN_BRANCH" ]; then + git fetch -q "$VALIDATE_REPO" "refs/heads/$VALIDATE_BRANCH" + VALIDATE_ORIGIN_BRANCH=FETCH_HEAD + fi + VALIDATE_UPSTREAM="$(git rev-parse --verify $VALIDATE_ORIGIN_BRANCH)" VALIDATE_COMMIT_LOG="$VALIDATE_UPSTREAM..$VALIDATE_HEAD" VALIDATE_COMMIT_DIFF="$VALIDATE_UPSTREAM...$VALIDATE_HEAD" diff --git a/hack/validate/all b/hack/validate/all index 9d95c2d2fd25c..3224fe57c2dc0 100755 --- a/hack/validate/all +++ b/hack/validate/all @@ -2,7 +2,7 @@ # # Run all validation -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -. $SCRIPTDIR/default -. $SCRIPTDIR/vendor +. ${SCRIPTDIR}/default +. ${SCRIPTDIR}/vendor diff --git a/hack/validate/changelog-date-descending b/hack/validate/changelog-date-descending index b9c3368ca66ad..301f9ba0b5a06 100755 --- a/hack/validate/changelog-date-descending +++ b/hack/validate/changelog-date-descending @@ -3,8 +3,8 @@ changelogFile=${1:-CHANGELOG.md} if [ ! -r "$changelogFile" ]; then - echo "Unable to read file $changelogFile" >&2 - exit 1 + echo "Unable to read file $changelogFile" >&2 + exit 1 fi grep -e '^## ' "$changelogFile" | awk '{print$3}' | sort -c -r || exit 2 diff --git a/hack/validate/changelog-well-formed b/hack/validate/changelog-well-formed index 6c7ce1a1c045f..ea7ef0ff5d9fa 100755 --- a/hack/validate/changelog-well-formed +++ b/hack/validate/changelog-well-formed @@ -3,8 +3,8 @@ changelogFile=${1:-CHANGELOG.md} if [ ! -r "$changelogFile" ]; then - echo "Unable to read file $changelogFile" >&2 - exit 1 + echo "Unable to read file $changelogFile" >&2 + exit 1 fi changelogWellFormed=1 @@ -12,14 +12,14 @@ changelogWellFormed=1 # e.g. "## 1.12.3 (2016-10-26)" VER_LINE_REGEX='^## [0-9]+\.[0-9]+\.[0-9]+(-ce)? \([0-9]+-[0-9]+-[0-9]+\)$' while read -r line; do - if ! [[ "$line" =~ $VER_LINE_REGEX ]]; then - echo "Malformed changelog $changelogFile line \"$line\"" >&2 - changelogWellFormed=0 - fi + if ! [[ "$line" =~ $VER_LINE_REGEX ]]; then + echo "Malformed changelog $changelogFile line \"$line\"" >&2 + changelogWellFormed=0 + fi done < <(grep '^## ' $changelogFile) if [[ "$changelogWellFormed" == "1" ]]; then - echo "Congratulations! Changelog $changelogFile is well-formed." + echo "Congratulations! Changelog $changelogFile is well-formed." else - exit 2 + exit 2 fi diff --git a/hack/validate/dco b/hack/validate/dco index f391001601f84..0e61ccca5d4cd 100755 --- a/hack/validate/dco +++ b/hack/validate/dco @@ -1,6 +1,6 @@ #!/usr/bin/env bash -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPTDIR}/.validate" adds=$(validate_diff --numstat | awk '{ s += $1 } END { print s }') @@ -21,10 +21,10 @@ check_dco() { grep -qE "$dcoRegex" } -if [ $adds -eq 0 -a $dels -eq 0 ]; then +if [ ${adds} -eq 0 -a ${dels} -eq 0 ]; then echo '0 adds, 0 deletions; nothing to validate! :)' else - commits=( $(validate_log --format='format:%H%n') ) + commits=($(validate_log --format='format:%H%n')) badCommits=() for commit in "${commits[@]}"; do if [ -z "$(git log -1 --format='format:' --name-status "$commit")" ]; then @@ -32,7 +32,7 @@ else continue fi if ! git log -1 --format='format:%B' "$commit" | check_dco; then - badCommits+=( "$commit" ) + badCommits+=("$commit") fi done if [ ${#badCommits[@]} -eq 0 ]; then diff --git a/hack/validate/default b/hack/validate/default index 8ec978876dd6d..cb85489190e08 100755 --- a/hack/validate/default +++ b/hack/validate/default @@ -2,16 +2,17 @@ # # Run default validation, exclude vendor because it's slow -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -. $SCRIPTDIR/dco -. $SCRIPTDIR/default-seccomp -. $SCRIPTDIR/gometalinter -. $SCRIPTDIR/pkg-imports -. $SCRIPTDIR/swagger -. $SCRIPTDIR/swagger-gen -. $SCRIPTDIR/test-imports -. $SCRIPTDIR/toml -. $SCRIPTDIR/changelog-well-formed -. $SCRIPTDIR/changelog-date-descending -. $SCRIPTDIR/deprecate-integration-cli +# Skip DCO check here, as it's already checked in a separate stage in the Jenkinsfile +#. "${SCRIPTDIR}"/dco +. "${SCRIPTDIR}"/default-seccomp +. "${SCRIPTDIR}"/pkg-imports +. "${SCRIPTDIR}"/swagger +. "${SCRIPTDIR}"/swagger-gen +. "${SCRIPTDIR}"/toml +. "${SCRIPTDIR}"/changelog-well-formed +. "${SCRIPTDIR}"/changelog-date-descending +. "${SCRIPTDIR}"/deprecate-integration-cli +. "${SCRIPTDIR}"/golangci-lint +. "${SCRIPTDIR}"/shfmt diff --git a/hack/validate/default-seccomp b/hack/validate/default-seccomp index 24cbf00d24b48..c5f8c7e3c8e1b 100755 --- a/hack/validate/default-seccomp +++ b/hack/validate/default-seccomp @@ -1,17 +1,17 @@ #!/usr/bin/env bash -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPTDIR}/.validate" IFS=$'\n' -files=( $(validate_diff --diff-filter=ACMR --name-only -- 'profiles/seccomp' || true) ) +files=($(validate_diff --diff-filter=ACMR --name-only -- 'profiles/seccomp' || true)) unset IFS -if [ ${#files[@]} -gt 0 ]; then +if [ -n "${TEST_FORCE_VALIDATE:-}" ] || [ ${#files[@]} -gt 0 ]; then # We run 'go generate' and see if we have a diff afterwards - go generate ./profiles/seccomp/ >/dev/null + go generate ./profiles/seccomp/ > /dev/null # Let see if the working directory is clean - diffs="$(git status --porcelain -- profiles/seccomp 2>/dev/null)" + diffs="$(git status --porcelain -- profiles/seccomp 2> /dev/null)" if [ "$diffs" ]; then { echo 'The result of go generate ./profiles/seccomp/ differs' @@ -23,6 +23,6 @@ if [ ${#files[@]} -gt 0 ]; then } >&2 false else - echo 'Congratulations! Seccomp profile generation is done correctly.' + echo 'Congratulations! Seccomp profile generation is done correctly.' fi fi diff --git a/hack/validate/deprecate-integration-cli b/hack/validate/deprecate-integration-cli index da6f8310f406b..f53b48eedc489 100755 --- a/hack/validate/deprecate-integration-cli +++ b/hack/validate/deprecate-integration-cli @@ -1,25 +1,25 @@ #!/usr/bin/env bash # Check that no new tests are being added to integration-cli -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPTDIR}/.validate" new_tests=$( - validate_diff --diff-filter=ACMR --unified=0 -- 'integration-cli/*_cli_*.go' | - grep -E '^\+func (.*) Test' || true + validate_diff --diff-filter=ACMR --unified=0 -- 'integration-cli/*_api_*.go' 'integration-cli/*_cli_*.go' \ + | grep -E '^\+func (.*) Test' || true ) -if [ -z "$new_tests" ]; then - echo 'Congratulations! No new tests added to integration-cli.' - exit +if [ -n "$new_tests" ]; then + { + echo "The following new tests were added to integration-cli:" + echo + echo "$new_tests" + echo + echo "integration-cli is deprecated. Please add an API integration test to" + echo "./integration/COMPONENT/. See ./TESTING.md for more details." + echo + } >&2 + false +else + echo 'Congratulations! No new tests were added to integration-cli.' fi - -echo "The following new tests were added to integration-cli:" -echo -echo "$new_tests" -echo -echo "integration-cli is deprecated. Please add an API integration test to" -echo "./integration/COMPONENT/. See ./TESTING.md for more details." -echo - -exit 1 diff --git a/hack/validate/golangci-lint b/hack/validate/golangci-lint new file mode 100755 index 0000000000000..fd14690ae1fd6 --- /dev/null +++ b/hack/validate/golangci-lint @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -e -o pipefail + +SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# CI platforms differ, so per-platform GOLANGCI_LINT_OPTS can be set +# from a platform-specific Dockerfile, otherwise let's just set +# (somewhat pessimistic) default of 10 minutes. +: "${GOLANGCI_LINT_OPTS=--timeout=10m}" + +[ -n "${TESTDEBUG}" ] && set -x + +# TODO find a way to share this code with hack/make.sh +if ${PKG_CONFIG} 'libsystemd >= 209' 2> /dev/null; then + DOCKER_BUILDTAGS+=" journald" +elif ${PKG_CONFIG} 'libsystemd-journal' 2> /dev/null; then + DOCKER_BUILDTAGS+=" journald journald_compat" +fi + +echo -e "\n\033[0;36mINFO\033[0m Start validation with golang-ci-lint" + +# TODO use --out-format=junit-xml and store artifacts +# shellcheck disable=SC2086 +GOGC=75 golangci-lint run \ + ${GOLANGCI_LINT_OPTS} \ + --print-resources-usage \ + --build-tags="${DOCKER_BUILDTAGS}" \ + --verbose \ + --config "${SCRIPTDIR}/golangci-lint.yml" diff --git a/hack/validate/golangci-lint.yml b/hack/validate/golangci-lint.yml new file mode 100644 index 0000000000000..c2081ed83fd71 --- /dev/null +++ b/hack/validate/golangci-lint.yml @@ -0,0 +1,123 @@ +linters: + enable: + - deadcode + - goimports + - golint + - gosec + - gosimple + - govet + - ineffassign + - misspell + - staticcheck + - structcheck + - typecheck + - unconvert + - unused + - varcheck + + disable: + - errcheck + + run: + concurrency: 2 + modules-download-mode: vendor + + skip-dirs: + - bundles + - docs + # TODO: This package should be completely removed + - libnetwork/client/mflag + +linters-settings: + govet: + check-shadowing: false + +issues: + # The default exclusion rules are a bit too permissive, so copying the relevant ones below + exclude-use-default: false + + exclude-rules: + # These are copied from the default exclude rules, except for "ineffective break statement" + # and GoDoc checks. + # https://github.com/golangci/golangci-lint/blob/0cc87df732aaf1d5ad9ce9ca538d38d916918b36/pkg/config/config.go#L36 + - text: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked" + linters: + - errcheck + - text: "func name will be used as test\\.Test.* by other packages, and that stutters; consider calling this" + linters: + - golint + - text: "G103: Use of unsafe calls should be audited" + linters: + - gosec + - text: "G104: Errors unhandled" + linters: + - gosec + - text: "G204: Subprocess launch(ed with (variable|function call)|ing should be audited)" + linters: + - gosec + - text: "(G301|G302): (Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)" + linters: + - gosec + - text: "G304: Potential file inclusion via variable" + linters: + - gosec + - text: "G306: Expect WriteFile permissions to be 0600 or less" + linters: + - gosec + - text: 'G307: Deferring unsafe method "Close" on type "*os.File"' + linters: gosec + + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - errcheck + - gosec + + # Suppress golint complaining about generated types in api/types/ + - text: "type name will be used as (container|volume)\\.(Container|Volume).* by other packages, and that stutters; consider calling this" + path: "api/types/(volume|container)/" + linters: + - golint + - text: "(G201|G202): SQL string (formatting|concatenation)" + linters: + - gosec + # FIXME: evaluate these and fix where needed: G307: Deferring unsafe method "*os.File" on type "Close" (gosec) + - text: "G307: Deferring unsafe method" + linters: + - gosec + # FIXME temporarily suppress these. See #39924 + - text: "SA1019: .*\\.Xattrs is deprecated: Use PAXRecords instead" + linters: + - staticcheck + # FIXME temporarily suppress these. See #39926 + - text: "SA1019: httputil.NewClientConn" + linters: + - staticcheck + # FIXME temporarily suppress these (related to the ones above) + - text: "SA1019: httputil.ErrPersistEOF" + linters: + - staticcheck + # FIXME temporarily suppress these for false positives in tests (see https://github.com/dominikh/go-tools/issues/1022) + - text: "SA5011" + path: _test\.go + linters: + - staticcheck + # This code is doing some fun stuff with reflect and it trips up the linter. + - text: "field `foo` is unused" + path: "libnetwork/options/options_test.go" + linters: + - structcheck + - unused + # This field is only used on windows but is defined in a platform agnostic file. + # The linter doesn't understand that the field is used. + - text: "`resolverOnce` is unused" + path: libnetwork/network.go + linters: + - structcheck + - unused + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-issues-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 diff --git a/hack/validate/gometalinter b/hack/validate/gometalinter deleted file mode 100755 index 8f42597fce772..0000000000000 --- a/hack/validate/gometalinter +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -e -o pipefail - -SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -# CI platforms differ, so per-platform GOMETALINTER_OPTS can be set -# from a platform-specific Dockerfile, otherwise let's just set -# (somewhat pessimistic) default of 10 minutes. -: ${GOMETALINTER_OPTS=--deadline=10m} - -gometalinter \ - ${GOMETALINTER_OPTS} \ - --config $SCRIPTDIR/gometalinter.json ./... diff --git a/hack/validate/gometalinter.json b/hack/validate/gometalinter.json deleted file mode 100644 index 81eb1017cb2b0..0000000000000 --- a/hack/validate/gometalinter.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "Vendor": true, - "EnableGC": true, - "Sort": ["linter", "severity", "path"], - "Exclude": [ - ".*\\.pb\\.go", - "dockerversion/version_autogen.go", - "api/types/container/container_.*", - "api/types/volume/volume_.*", - "integration-cli/" - ], - "Skip": ["integration-cli/"], - - "Enable": [ - "deadcode", - "gofmt", - "goimports", - "golint", - "gosimple", - "ineffassign", - "interfacer", - "unconvert", - "vet" - ], - - "LineLength": 200 -} diff --git a/hack/validate/pkg-imports b/hack/validate/pkg-imports index a9aab6456f767..f82e315f83c01 100755 --- a/hack/validate/pkg-imports +++ b/hack/validate/pkg-imports @@ -1,26 +1,26 @@ #!/usr/bin/env bash set -e -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPTDIR}/.validate" IFS=$'\n' -files=( $(validate_diff --diff-filter=ACMR --name-only -- 'pkg/*.go' || true) ) +files=($(validate_diff --diff-filter=ACMR --name-only -- 'pkg/*.go' || true)) unset IFS badFiles=() for f in "${files[@]}"; do IFS=$'\n' - badImports=( $(go list -e -f '{{ join .Deps "\n" }}' "$f" | sort -u | grep -vE '^github.com/docker/docker/pkg/' | grep -vE '^github.com/docker/docker/vendor' | grep -E '^github.com/docker/docker' || true) ) + badImports=($(go list -e -f '{{ join .Deps "\n" }}' "$f" | sort -u | grep -vE '^github.com/docker/docker/pkg/' | grep -vE '^github.com/docker/docker/vendor' | grep -E '^github.com/docker/docker' || true)) unset IFS for import in "${badImports[@]}"; do - badFiles+=( "$f imports $import" ) + badFiles+=("$f imports $import") done done if [ ${#badFiles[@]} -eq 0 ]; then - echo 'Congratulations! "./pkg/..." is safely isolated from internal code.' + echo 'Congratulations! Packages in "./pkg/..." are safely isolated from internal code.' else { echo 'These files import internal code: (either directly or indirectly)' diff --git a/hack/validate/shfmt b/hack/validate/shfmt new file mode 100755 index 0000000000000..748e774e16585 --- /dev/null +++ b/hack/validate/shfmt @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -e -o pipefail + +shfmtflags="-bn -ci -sr" +# NOTE: `git grep '^#!'` may also pick up non-shell script files. +# Add exceptional files to `egrep -v` if any false-positive is detected. +if git grep --name-only '^#!' | egrep -v '(vendor|\.go|Jenkinsfile|\.py|\.bats)' \ + | xargs shfmt -d $shfmtflags; then + echo 'Congratulations! The shell scripts are properly formatted.' +else + echo "Please reformat the shell scripts with \`shfmt -w ${shfmtflags}\`." + exit 1 +fi diff --git a/hack/validate/swagger b/hack/validate/swagger index 0b3c2719d8667..5bf9c1cbcc151 100755 --- a/hack/validate/swagger +++ b/hack/validate/swagger @@ -1,13 +1,18 @@ #!/usr/bin/env bash set -e -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPTDIR}/.validate" IFS=$'\n' -files=( $(validate_diff --diff-filter=ACMR --name-only -- 'api/swagger.yaml' || true) ) +files=($(validate_diff --diff-filter=ACMR --name-only -- 'api/swagger.yaml' || true)) unset IFS -if [ ${#files[@]} -gt 0 ]; then - yamllint -c ${SCRIPTDIR}/.swagger-yamllint api/swagger.yaml - swagger validate api/swagger.yaml +if [ -n "${TEST_FORCE_VALIDATE:-}" ] || [ ${#files[@]} -gt 0 ]; then + LANG=C.UTF-8 yamllint -c "${SCRIPTDIR}"/.swagger-yamllint api/swagger.yaml + if out=$(swagger validate api/swagger.yaml); then + echo "Congratulations! ${out}" + else + echo "${out}" >&2 + false + fi fi diff --git a/hack/validate/swagger-gen b/hack/validate/swagger-gen index 07c22b5a62d3a..0d000ea39b173 100755 --- a/hack/validate/swagger-gen +++ b/hack/validate/swagger-gen @@ -1,14 +1,14 @@ #!/usr/bin/env bash -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPTDIR}/.validate" IFS=$'\n' -files=( $(validate_diff --diff-filter=ACMR --name-only -- 'api/types/' 'api/swagger.yaml' || true) ) +files=($(validate_diff --diff-filter=ACMR --name-only -- 'api/types/' 'api/swagger.yaml' || true)) unset IFS -if [ ${#files[@]} -gt 0 ]; then - ${SCRIPTDIR}/../generate-swagger-api.sh 2> /dev/null +if [ -n "${TEST_FORCE_VALIDATE:-}" ] || [ ${#files[@]} -gt 0 ]; then + "${SCRIPTDIR}"/../generate-swagger-api.sh 2> /dev/null # Let see if the working directory is clean diffs="$(git diff -- api/types/)" if [ "$diffs" ]; then @@ -17,13 +17,13 @@ if [ ${#files[@]} -gt 0 ]; then echo echo "$diffs" echo - echo 'Please update api/swagger.yaml with any api changes, then ' - echo 'run `hack/generate-swagger-api.sh`.' + echo 'Please update api/swagger.yaml with any API changes, then ' + echo 'run hack/generate-swagger-api.sh.' } >&2 false else - echo 'Congratulations! All api changes are done the right way.' + echo 'Congratulations! All API changes are done the right way.' fi else - echo 'No api/types/ or api/swagger.yaml changes in diff.' + echo 'No api/types/ or api/swagger.yaml changes in diff.' fi diff --git a/hack/validate/test-imports b/hack/validate/test-imports deleted file mode 100755 index 0e836a31c0754..0000000000000 --- a/hack/validate/test-imports +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -# Make sure we're not using gos' Testing package any more in integration-cli - -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -source "${SCRIPTDIR}/.validate" - -IFS=$'\n' -files=( $(validate_diff --diff-filter=ACMR --name-only -- 'integration-cli/*.go' || true) ) -unset IFS - -badFiles=() -for f in "${files[@]}"; do - # skip check_test.go since it *does* use the testing package - if [ "$f" = "integration-cli/check_test.go" ]; then - continue - fi - - # we use "git show" here to validate that what's committed doesn't contain golang built-in testing - if git show "$VALIDATE_HEAD:$f" | grep -q testing.T; then - if [ "$(echo $f | grep '_test')" ]; then - # allow testing.T for non- _test files - badFiles+=( "$f" ) - fi - fi -done - -if [ ${#badFiles[@]} -eq 0 ]; then - echo 'Congratulations! No testing.T found.' -else - { - echo "These files use the wrong testing infrastructure:" - for f in "${badFiles[@]}"; do - echo " - $f" - done - echo - } >&2 - false -fi diff --git a/hack/validate/toml b/hack/validate/toml index d5b2ce1c293f2..7f1739ab84339 100755 --- a/hack/validate/toml +++ b/hack/validate/toml @@ -1,30 +1,30 @@ #!/usr/bin/env bash -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPTDIR}/.validate" IFS=$'\n' -files=( $(validate_diff --diff-filter=ACMR --name-only -- 'MAINTAINERS' || true) ) +files=($(validate_diff --diff-filter=ACMR --name-only -- 'MAINTAINERS' || true)) unset IFS badFiles=() for f in "${files[@]}"; do - # we use "git show" here to validate that what's committed has valid toml syntax - if ! git show "$VALIDATE_HEAD:$f" | tomlv /proc/self/fd/0 ; then - badFiles+=( "$f" ) + # we use "git show" here to validate that what's committed has valid TOML syntax + if ! git show "$VALIDATE_HEAD:$f" | tomll /proc/self/fd/0; then + badFiles+=("$f") fi done if [ ${#badFiles[@]} -eq 0 ]; then - echo 'Congratulations! All toml source files changed here have valid syntax.' + echo 'Congratulations! All TOML source files changed here have valid syntax.' else { - echo "These files are not valid toml:" + echo "These files are not valid TOML:" for f in "${badFiles[@]}"; do echo " - $f" done echo - echo 'Please reformat the above files as valid toml' + echo 'Please reformat the above files as valid TOML' echo } >&2 false diff --git a/hack/validate/vendor b/hack/validate/vendor index 41676fed53558..5d3121b4c996b 100755 --- a/hack/validate/vendor +++ b/hack/validate/vendor @@ -1,26 +1,31 @@ #!/usr/bin/env bash -export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source "${SCRIPTDIR}/.validate" validate_vendor_diff(){ IFS=$'\n' + # shellcheck disable=SC2207 files=( $(validate_diff --diff-filter=ACMR --name-only -- 'vendor.conf' 'vendor/' || true) ) unset IFS - if [ ${#files[@]} -gt 0 ]; then + if [ -n "${TEST_FORCE_VALIDATE:-}" ] || [ ${#files[@]} -gt 0 ]; then # recreate vendor/ - vndr + ./hack/vendor.sh # check if any files have changed diffs="$(git status --porcelain -- vendor 2>/dev/null)" + mfiles="$(echo "$diffs" | awk '/^ M / {print $2}')" if [ "$diffs" ]; then { echo 'The result of vndr differs' echo echo "$diffs" echo - echo 'Please vendor your package with github.com/LK4D4/vndr.' + echo 'Please vendor your package with hack/vendor.sh.' echo + if [ -n "$mfiles" ] ; then + git diff -- "$mfiles" + fi } >&2 false else @@ -34,11 +39,9 @@ validate_vendor_diff(){ # 1. make sure all the vendored packages are used # 2. make sure all the packages contain license information (just warning, because it can cause false-positive) validate_vendor_used() { - pkgs=$(mawk '/^[a-zA-Z0-9]/ { print $1 }' < vendor.conf) - for f in $pkgs; do - if ls -d vendor/$f > /dev/null 2>&1; then - found=$(find vendor/$f -iregex '.*LICENSE.*' -or -iregex '.*COPYRIGHT.*' -or -iregex '.*COPYING.*' | wc -l) - if [ $found -eq 0 ]; then + for f in $(mawk '/^[a-zA-Z0-9]/ { print $1 }' vendor.conf); do + if [ -d "vendor/$f" ]; then + if ! echo "vendor/$f"/* | grep -qiEc '/(LICENSE|COPYING)'; then echo "WARNING: could not find copyright information for $f" fi else diff --git a/hack/vendor.sh b/hack/vendor.sh index a7a571e7b7c5e..893b5b7cd0c46 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -12,4 +12,17 @@ if ! hash vndr; then exit 1 fi -vndr "$@" +if [ $# -eq 0 ] || [ "$1" = "archive/tar" ]; then + echo "update vendored copy of archive/tar" + : "${GO_VERSION:=$(awk -F '[ =]' '$1 == "ARG" && $2 == "GO_VERSION" { print $3; exit }' ./Dockerfile)}" + rm -rf vendor/archive + mkdir -p ./vendor/archive/tar + echo "downloading: https://golang.org/dl/go${GO_VERSION%.0}.src.tar.gz" + curl -fsSL "https://golang.org/dl/go${GO_VERSION%.0}.src.tar.gz" \ + | tar --extract --gzip --directory=vendor/archive/tar --strip-components=4 go/src/archive/tar + patch --strip=4 --directory=vendor/archive/tar --input="$PWD/patches/0001-archive-tar-do-not-populate-user-group-names.patch" +fi + +if [ $# -eq 0 ] || [ "$1" != "archive/tar" ]; then + vndr -whitelist='^archive[/\\]tar' "$@" +fi diff --git a/image/fs.go b/image/fs.go index 7080c8c0155f6..d996501cc6db7 100644 --- a/image/fs.go +++ b/image/fs.go @@ -2,13 +2,12 @@ package image // import "github.com/docker/docker/image" import ( "fmt" - "io/ioutil" "os" "path/filepath" "sync" "github.com/docker/docker/pkg/ioutils" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -68,7 +67,7 @@ func (s *fs) metadataDir(dgst digest.Digest) string { func (s *fs) Walk(f DigestWalkFunc) error { // Only Canonical digest (sha256) is currently supported s.RLock() - dir, err := ioutil.ReadDir(filepath.Join(s.root, contentDirName, string(digest.Canonical))) + dir, err := os.ReadDir(filepath.Join(s.root, contentDirName, string(digest.Canonical))) s.RUnlock() if err != nil { return err @@ -95,7 +94,7 @@ func (s *fs) Get(dgst digest.Digest) ([]byte, error) { } func (s *fs) get(dgst digest.Digest) ([]byte, error) { - content, err := ioutil.ReadFile(s.contentFile(dgst)) + content, err := os.ReadFile(s.contentFile(dgst)) if err != nil { return nil, errors.Wrapf(err, "failed to get digest %s", dgst) } @@ -159,7 +158,7 @@ func (s *fs) GetMetadata(dgst digest.Digest, key string) ([]byte, error) { if _, err := s.get(dgst); err != nil { return nil, err } - bytes, err := ioutil.ReadFile(filepath.Join(s.metadataDir(dgst), key)) + bytes, err := os.ReadFile(filepath.Join(s.metadataDir(dgst), key)) if err != nil { return nil, errors.Wrap(err, "failed to read metadata") } diff --git a/image/fs_test.go b/image/fs_test.go index 6290c2b66e841..dc2f52cc3811d 100644 --- a/image/fs_test.go +++ b/image/fs_test.go @@ -5,18 +5,17 @@ import ( "crypto/sha256" "encoding/hex" "errors" - "io/ioutil" "os" "path/filepath" "testing" - "github.com/opencontainers/go-digest" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + digest "github.com/opencontainers/go-digest" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func defaultFSStoreBackend(t *testing.T) (StoreBackend, func()) { - tmpdir, err := ioutil.TempDir("", "images-fs-store") + tmpdir, err := os.MkdirTemp("", "images-fs-store") assert.Check(t, err) fsBackend, err := NewFSStoreBackend(tmpdir) @@ -29,15 +28,13 @@ func TestFSGetInvalidData(t *testing.T) { store, cleanup := defaultFSStoreBackend(t) defer cleanup() - id, err := store.Set([]byte("foobar")) + dgst, err := store.Set([]byte("foobar")) assert.Check(t, err) - dgst := digest.Digest(id) - - err = ioutil.WriteFile(filepath.Join(store.(*fs).root, contentDirName, string(dgst.Algorithm()), dgst.Hex()), []byte("foobar2"), 0600) + err = os.WriteFile(filepath.Join(store.(*fs).root, contentDirName, string(dgst.Algorithm()), dgst.Hex()), []byte("foobar2"), 0600) assert.Check(t, err) - _, err = store.Get(id) + _, err = store.Get(dgst) assert.Check(t, is.ErrorContains(err, "failed to verify")) } @@ -54,7 +51,7 @@ func TestFSInvalidSet(t *testing.T) { } func TestFSInvalidRoot(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "images-fs-store") + tmpdir, err := os.MkdirTemp("", "images-fs-store") assert.Check(t, err) defer os.RemoveAll(tmpdir) @@ -132,7 +129,7 @@ func TestFSInvalidWalker(t *testing.T) { fooID, err := store.Set([]byte("foo")) assert.Check(t, err) - err = ioutil.WriteFile(filepath.Join(store.(*fs).root, contentDirName, "sha256/foobar"), []byte("foobar"), 0600) + err = os.WriteFile(filepath.Join(store.(*fs).root, contentDirName, "sha256/foobar"), []byte("foobar"), 0600) assert.Check(t, err) n := 0 @@ -172,7 +169,7 @@ func TestFSGetSet(t *testing.T) { }) for _, tc := range tcases { - id, err := store.Set([]byte(tc.input)) + id, err := store.Set(tc.input) assert.Check(t, err) assert.Check(t, is.Equal(tc.expected, id)) } diff --git a/image/image.go b/image/image.go index 7e0646f0724ed..a9db58fd65be2 100644 --- a/image/image.go +++ b/image/image.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "io" + "reflect" "runtime" "strings" "time" @@ -11,7 +12,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/dockerversion" "github.com/docker/docker/layer" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" ) // ID is the content-addressable ID of an image. @@ -53,6 +54,8 @@ type V1Image struct { Config *container.Config `json:"config,omitempty"` // Architecture is the hardware that the image is built and runs on Architecture string `json:"architecture,omitempty"` + // Variant is the CPU architecture variant (presently ARM-only) + Variant string `json:"variant,omitempty"` // OS is the operating system used to build and run the image OS string `json:"os,omitempty"` // Size is the total size of the image including all layers it is composed of @@ -62,7 +65,7 @@ type V1Image struct { // Image stores the image configuration type Image struct { V1Image - Parent ID `json:"parent,omitempty"` + Parent ID `json:"parent,omitempty"` //nolint:govet RootFS *RootFS `json:"rootfs,omitempty"` History []History `json:"history,omitempty"` OSVersion string `json:"os.version,omitempty"` @@ -105,6 +108,13 @@ func (img *Image) BaseImgArch() string { return arch } +// BaseImgVariant returns the image's variant, whether populated or not. +// This avoids creating an inconsistency where the stored image variant +// is "greater than" (i.e. v8 vs v6) the actual image variant. +func (img *Image) BaseImgVariant() string { + return img.Variant +} + // OperatingSystem returns the image's operating system. If not populated, defaults to the host runtime OS. func (img *Image) OperatingSystem() string { os := img.OS @@ -144,7 +154,7 @@ type ChildConfig struct { } // NewChildImage creates a new Image as a child of this image. -func NewChildImage(img *Image, child ChildConfig, platform string) *Image { +func NewChildImage(img *Image, child ChildConfig, os string) *Image { isEmptyLayer := layer.IsEmpty(child.DiffID) var rootFS *RootFS if img.RootFS != nil { @@ -167,7 +177,8 @@ func NewChildImage(img *Image, child ChildConfig, platform string) *Image { DockerVersion: dockerversion.Version, Config: child.Config, Architecture: img.BaseImgArch(), - OS: platform, + Variant: img.BaseImgVariant(), + OS: os, Container: child.ContainerID, ContainerConfig: *child.ContainerConfig, Author: child.Author, @@ -208,6 +219,16 @@ func NewHistory(author, comment, createdBy string, isEmptyLayer bool) History { } } +// Equal compares two history structs for equality +func (h History) Equal(i History) bool { + if !h.Created.Equal(i.Created) { + return false + } + i.Created = h.Created + + return reflect.DeepEqual(h, i) +} + // Exporter provides interface for loading and saving images type Exporter interface { Load(io.ReadCloser, io.Writer, bool) error diff --git a/image/image_test.go b/image/image_test.go index 981be0b68c698..21f81c768d30e 100644 --- a/image/image_test.go +++ b/image/image_test.go @@ -10,8 +10,8 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/layer" "github.com/google/go-cmp/cmp" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) const sampleImageJSON = `{ @@ -56,6 +56,29 @@ func TestMarshalKeyOrder(t *testing.T) { } } +const sampleHistoryJSON = `{ + "created": "2021-01-13T09:35:56Z", + "created_by": "image_test.go" +}` + +func TestHistoryEqual(t *testing.T) { + h := historyFromJSON(t, sampleHistoryJSON) + hCopy := h + assert.Check(t, h.Equal(hCopy)) + + hUTC := historyFromJSON(t, `{"created": "2021-01-13T14:00:00Z"}`) + hOffset0 := historyFromJSON(t, `{"created": "2021-01-13T14:00:00+00:00"}`) + assert.Check(t, hUTC.Created != hOffset0.Created) + assert.Check(t, hUTC.Equal(hOffset0)) +} + +func historyFromJSON(t *testing.T, historyJSON string) History { + var h History + err := json.Unmarshal([]byte(historyJSON), &h) + assert.Check(t, err) + return h +} + func TestImage(t *testing.T) { cid := "50a16564e727" config := &container.Config{ diff --git a/image/rootfs.go b/image/rootfs.go index 84843e10c6d5d..f73a0660fa593 100644 --- a/image/rootfs.go +++ b/image/rootfs.go @@ -38,7 +38,8 @@ func (r *RootFS) Append(id layer.DiffID) { func (r *RootFS) Clone() *RootFS { newRoot := NewRootFS() newRoot.Type = r.Type - newRoot.DiffIDs = append(r.DiffIDs) + newRoot.DiffIDs = make([]layer.DiffID, len(r.DiffIDs)) + copy(newRoot.DiffIDs, r.DiffIDs) return newRoot } diff --git a/image/spec/v1.1.md b/image/spec/v1.1.md index de74d91a199fb..5d8c7e9d806f7 100644 --- a/image/spec/v1.1.md +++ b/image/spec/v1.1.md @@ -144,7 +144,7 @@ Here is an example image JSON file: "/var/job-result-data": {}, "/var/log/my-app-logs": {}, }, - "WorkingDir": "/home/alice", + "WorkingDir": "/home/alice" }, "rootfs": { "diff_ids": [ @@ -405,9 +405,7 @@ whitespace. It has been added to this example for clarity. filesystem). - Here is an example history section: -
"history": [
   {
     "created": "2015-10-31T22:22:54.690851953Z",
diff --git a/image/spec/v1.2.md b/image/spec/v1.2.md
index 2ea3feec92cc0..1400e183f092f 100644
--- a/image/spec/v1.2.md
+++ b/image/spec/v1.2.md
@@ -144,7 +144,7 @@ Here is an example image JSON file:
             "/var/job-result-data": {},
             "/var/log/my-app-logs": {},
         },
-        "WorkingDir": "/home/alice",
+        "WorkingDir": "/home/alice"
     },
     "rootfs": {
       "diff_ids": [
diff --git a/image/spec/v1.md b/image/spec/v1.md
index c1415947f1cbf..7bf85cedd240f 100644
--- a/image/spec/v1.md
+++ b/image/spec/v1.md
@@ -151,7 +151,7 @@ Here is an example image JSON file:
             "/var/job-result-data": {},
             "/var/log/my-app-logs": {},
         },
-        "WorkingDir": "/home/alice",
+        "WorkingDir": "/home/alice"
     }
 }
 ```
diff --git a/image/store.go b/image/store.go
index 1a8a8a24516c2..187644f51093b 100644
--- a/image/store.go
+++ b/image/store.go
@@ -1,7 +1,6 @@
 package image // import "github.com/docker/docker/image"
 
 import (
-	"encoding/json"
 	"fmt"
 	"sync"
 	"time"
@@ -9,7 +8,7 @@ import (
 	"github.com/docker/distribution/digestset"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/system"
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
@@ -43,14 +42,14 @@ type imageMeta struct {
 
 type store struct {
 	sync.RWMutex
-	lss       map[string]LayerGetReleaser
+	lss       LayerGetReleaser
 	images    map[ID]*imageMeta
 	fs        StoreBackend
 	digestSet *digestset.Set
 }
 
 // NewImageStore returns new store object for given set of layer stores
-func NewImageStore(fs StoreBackend, lss map[string]LayerGetReleaser) (Store, error) {
+func NewImageStore(fs StoreBackend, lss LayerGetReleaser) (Store, error) {
 	is := &store{
 		lss:       lss,
 		images:    make(map[ID]*imageMeta),
@@ -79,7 +78,7 @@ func (is *store) restore() error {
 				logrus.Errorf("not restoring image with unsupported operating system %v, %v, %s", dgst, chainID, img.OperatingSystem())
 				return nil
 			}
-			l, err = is.lss[img.OperatingSystem()].Get(chainID)
+			l, err = is.lss.Get(chainID)
 			if err != nil {
 				if err == layer.ErrLayerDoesNotExist {
 					logrus.Errorf("layer does not exist, not restoring image %v, %v, %s", dgst, chainID, img.OperatingSystem())
@@ -118,8 +117,8 @@ func (is *store) restore() error {
 }
 
 func (is *store) Create(config []byte) (ID, error) {
-	var img Image
-	err := json.Unmarshal(config, &img)
+	var img *Image
+	img, err := NewFromJSON(config)
 	if err != nil {
 		return "", err
 	}
@@ -161,7 +160,7 @@ func (is *store) Create(config []byte) (ID, error) {
 		if !system.IsOSSupported(img.OperatingSystem()) {
 			return "", system.ErrNotSupportedOperatingSystem
 		}
-		l, err = is.lss[img.OperatingSystem()].Get(layerID)
+		l, err = is.lss.Get(layerID)
 		if err != nil {
 			return "", errors.Wrapf(err, "failed to get layer %s", layerID)
 		}
@@ -251,7 +250,7 @@ func (is *store) Delete(id ID) ([]layer.Metadata, error) {
 	is.fs.Delete(id.Digest())
 
 	if imageMeta.layer != nil {
-		return is.lss[img.OperatingSystem()].Release(imageMeta.layer)
+		return is.lss.Release(imageMeta.layer)
 	}
 	return nil, nil
 }
diff --git a/image/store_test.go b/image/store_test.go
index 0edf3282af471..4919faa306bae 100644
--- a/image/store_test.go
+++ b/image/store_test.go
@@ -2,15 +2,21 @@ package image // import "github.com/docker/docker/image"
 
 import (
 	"fmt"
-	"runtime"
 	"testing"
 
 	"github.com/docker/docker/layer"
-	"github.com/opencontainers/go-digest"
-	"gotest.tools/assert"
-	"gotest.tools/assert/cmp"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/assert/cmp"
 )
 
+func TestCreate(t *testing.T) {
+	is, cleanup := defaultImageStore(t)
+	defer cleanup()
+
+	_, err := is.Create([]byte(`{}`))
+	assert.Check(t, cmp.Error(err, "invalid image JSON, no RootFS key"))
+}
+
 func TestRestore(t *testing.T) {
 	fs, cleanup := defaultFSStoreBackend(t)
 	defer cleanup()
@@ -27,9 +33,7 @@ func TestRestore(t *testing.T) {
 	err = fs.SetMetadata(id2, "parent", []byte(id1))
 	assert.NilError(t, err)
 
-	mlgrMap := make(map[string]LayerGetReleaser)
-	mlgrMap[runtime.GOOS] = &mockLayerGetReleaser{}
-	is, err := NewImageStore(fs, mlgrMap)
+	is, err := NewImageStore(fs, &mockLayerGetReleaser{})
 	assert.NilError(t, err)
 
 	assert.Check(t, cmp.Len(is.Map(), 2))
@@ -60,11 +64,11 @@ func TestRestore(t *testing.T) {
 	assert.NilError(t, err)
 	assert.Check(t, cmp.Equal(ID(id1), sid1))
 
-	sid1, err = is.Search(digest.Digest(id1).Hex()[:6])
+	sid1, err = is.Search(id1.Hex()[:6])
 	assert.NilError(t, err)
 	assert.Check(t, cmp.Equal(ID(id1), sid1))
 
-	invalidPattern := digest.Digest(id1).Hex()[1:6]
+	invalidPattern := id1.Hex()[1:6]
 	_, err = is.Search(invalidPattern)
 	assert.ErrorContains(t, err, "No such image")
 }
@@ -146,9 +150,7 @@ func TestParentReset(t *testing.T) {
 func defaultImageStore(t *testing.T) (Store, func()) {
 	fsBackend, cleanup := defaultFSStoreBackend(t)
 
-	mlgrMap := make(map[string]LayerGetReleaser)
-	mlgrMap[runtime.GOOS] = &mockLayerGetReleaser{}
-	store, err := NewImageStore(fsBackend, mlgrMap)
+	store, err := NewImageStore(fsBackend, &mockLayerGetReleaser{})
 	assert.NilError(t, err)
 
 	return store, cleanup
diff --git a/image/tarexport/load.go b/image/tarexport/load.go
index 786214383eaf3..fa7a745cc03be 100644
--- a/image/tarexport/load.go
+++ b/image/tarexport/load.go
@@ -5,26 +5,23 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"path/filepath"
-	"reflect"
 	"runtime"
 
-	"github.com/containerd/containerd/platforms"
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/image"
-	"github.com/docker/docker/image/v1"
+	v1 "github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/chrootarchive"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/streamformatter"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/system"
-	"github.com/opencontainers/go-digest"
+	"github.com/moby/sys/symlink"
+	digest "github.com/opencontainers/go-digest"
 	"github.com/sirupsen/logrus"
 )
 
@@ -35,7 +32,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 	}
 	outStream = streamformatter.NewStdoutWriter(outStream)
 
-	tmpDir, err := ioutil.TempDir("", "docker-import-")
+	tmpDir, err := os.MkdirTemp("", "docker-import-")
 	if err != nil {
 		return err
 	}
@@ -63,6 +60,10 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 		return err
 	}
 
+	if err := validateManifest(manifest); err != nil {
+		return err
+	}
+
 	var parentLinks []parentLink
 	var imageIDsStr string
 	var imageRefCount int
@@ -72,7 +73,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 		if err != nil {
 			return err
 		}
-		config, err := ioutil.ReadFile(configPath)
+		config, err := os.ReadFile(configPath)
 		if err != nil {
 			return err
 		}
@@ -80,8 +81,8 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 		if err != nil {
 			return err
 		}
-		if err := checkCompatibleOS(img.OS); err != nil {
-			return err
+		if !system.IsOSSupported(img.OperatingSystem()) {
+			return fmt.Errorf("cannot load %s image on %s", img.OperatingSystem(), runtime.GOOS)
 		}
 		rootFS := *img.RootFS
 		rootFS.DiffIDs = nil
@@ -90,17 +91,6 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 			return fmt.Errorf("invalid manifest, layers length mismatch: expected %d, got %d", expected, actual)
 		}
 
-		// On Windows, validate the platform, defaulting to windows if not present.
-		os := img.OS
-		if os == "" {
-			os = runtime.GOOS
-		}
-		if runtime.GOOS == "windows" {
-			if (os != "windows") && (os != "linux") {
-				return fmt.Errorf("configuration for this image has an unsupported operating system: %s", os)
-			}
-		}
-
 		for i, diffID := range img.RootFS.DiffIDs {
 			layerPath, err := safePath(tmpDir, m.Layers[i])
 			if err != nil {
@@ -108,14 +98,14 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
 			}
 			r := rootFS
 			r.Append(diffID)
-			newLayer, err := l.lss[os].Get(r.ChainID())
+			newLayer, err := l.lss.Get(r.ChainID())
 			if err != nil {
-				newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), os, m.LayerSources[diffID], progressOutput)
+				newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), m.LayerSources[diffID], progressOutput)
 				if err != nil {
 					return err
 				}
 			}
-			defer layer.ReleaseAndLog(l.lss[os], newLayer)
+			defer layer.ReleaseAndLog(l.lss, newLayer)
 			if expected, actual := diffID, newLayer.DiffID(); expected != actual {
 				return fmt.Errorf("invalid diffID for layer %d: expected %q, got %q", i, expected, actual)
 			}
@@ -177,7 +167,7 @@ func (l *tarexporter) setParentID(id, parentID image.ID) error {
 	return l.is.SetParent(id, parentID)
 }
 
-func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, os string, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
+func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
 	// We use system.OpenSequential to use sequential file access on Windows, avoiding
 	// depleting the standby list. On Linux, this equates to a regular os.Open.
 	rawTar, err := system.OpenSequential(filename)
@@ -206,10 +196,10 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string,
 	}
 	defer inflatedLayerData.Close()
 
-	if ds, ok := l.lss[os].(layer.DescribableStore); ok {
+	if ds, ok := l.lss.(layer.DescribableStore); ok {
 		return ds.RegisterWithDescriptor(inflatedLayerData, rootFS.ChainID(), foreignSrc)
 	}
-	return l.lss[os].Register(inflatedLayerData, rootFS.ChainID())
+	return l.lss.Register(inflatedLayerData, rootFS.ChainID())
 }
 
 func (l *tarexporter) setLoadedTag(ref reference.Named, imgID digest.Digest, outStream io.Writer) error {
@@ -227,7 +217,7 @@ func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOut
 
 	legacyLoadedMap := make(map[string]image.ID)
 
-	dirs, err := ioutil.ReadDir(tmpDir)
+	dirs, err := os.ReadDir(tmpDir)
 	if err != nil {
 		return err
 	}
@@ -286,7 +276,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str
 	if err != nil {
 		return err
 	}
-	imageJSON, err := ioutil.ReadFile(configPath)
+	imageJSON, err := os.ReadFile(configPath)
 	if err != nil {
 		logrus.Debugf("Error reading json: %v", err)
 		return err
@@ -300,12 +290,12 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str
 		return err
 	}
 
-	if err := checkCompatibleOS(img.OS); err != nil {
-		return err
-	}
 	if img.OS == "" {
 		img.OS = runtime.GOOS
 	}
+	if !system.IsOSSupported(img.OS) {
+		return fmt.Errorf("cannot load %s image on %s", img.OS, runtime.GOOS)
+	}
 
 	var parentID image.ID
 	if img.Parent != "" {
@@ -339,7 +329,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str
 	if err != nil {
 		return err
 	}
-	newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, img.OS, distribution.Descriptor{}, progressOutput)
+	newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, distribution.Descriptor{}, progressOutput)
 	if err != nil {
 		return err
 	}
@@ -360,7 +350,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str
 		return err
 	}
 
-	metadata, err := l.lss[img.OS].Release(newLayer)
+	metadata, err := l.lss.Release(newLayer)
 	layer.LogReleaseMetadata(metadata)
 	if err != nil {
 		return err
@@ -406,27 +396,19 @@ func checkValidParent(img, parent *image.Image) bool {
 		return false
 	}
 	for i, h := range parent.History {
-		if !reflect.DeepEqual(h, img.History[i]) {
+		if !h.Equal(img.History[i]) {
 			return false
 		}
 	}
 	return true
 }
 
-func checkCompatibleOS(imageOS string) error {
-	// always compatible if the images OS matches the host OS; also match an empty image OS
-	if imageOS == runtime.GOOS || imageOS == "" {
-		return nil
-	}
-	// On non-Windows hosts, for compatibility, fail if the image is Windows.
-	if runtime.GOOS != "windows" && imageOS == "windows" {
-		return fmt.Errorf("cannot load %s image on %s", imageOS, runtime.GOOS)
+func validateManifest(manifest []manifestItem) error {
+	// a nil manifest usually indicates a bug, so don't just silently fail.
+	// if someone really needs to pass an empty manifest, they can pass [].
+	if manifest == nil {
+		return errors.New("invalid manifest, manifest cannot be null (but can be [])")
 	}
 
-	p, err := platforms.Parse(imageOS)
-	if err != nil {
-		return err
-	}
-
-	return system.ValidatePlatform(p)
+	return nil
 }
diff --git a/image/tarexport/load_test.go b/image/tarexport/load_test.go
new file mode 100644
index 0000000000000..3121e1f2c09ea
--- /dev/null
+++ b/image/tarexport/load_test.go
@@ -0,0 +1,37 @@
+package tarexport
+
+import (
+	"testing"
+
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+)
+
+func TestValidateManifest(t *testing.T) {
+	cases := map[string]struct {
+		manifest    []manifestItem
+		valid       bool
+		errContains string
+	}{
+		"nil": {
+			manifest:    nil,
+			valid:       false,
+			errContains: "manifest cannot be null",
+		},
+		"non-nil": {
+			manifest: []manifestItem{},
+			valid:    true,
+		},
+	}
+
+	for name, tc := range cases {
+		t.Run(name, func(t *testing.T) {
+			err := validateManifest(tc.manifest)
+			if tc.valid {
+				assert.Check(t, is.Nil(err))
+			} else {
+				assert.Check(t, is.ErrorContains(err, tc.errContains))
+			}
+		})
+	}
+}
diff --git a/image/tarexport/save.go b/image/tarexport/save.go
index 4e734b35038aa..926d44d68b312 100644
--- a/image/tarexport/save.go
+++ b/image/tarexport/save.go
@@ -4,7 +4,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"path"
 	"path/filepath"
@@ -14,11 +13,11 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/image"
-	"github.com/docker/docker/image/v1"
+	v1 "github.com/docker/docker/image/v1"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/system"
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 )
 
@@ -162,7 +161,7 @@ func (l *tarexporter) takeLayerReference(id image.ID, imgDescr *imageDescriptor)
 	if !system.IsOSSupported(os) {
 		return fmt.Errorf("os %q is not supported", os)
 	}
-	layer, err := l.lss[os].Get(topLayerID)
+	layer, err := l.lss.Get(topLayerID)
 	if err != nil {
 		return err
 	}
@@ -174,11 +173,7 @@ func (l *tarexporter) takeLayerReference(id image.ID, imgDescr *imageDescriptor)
 func (l *tarexporter) releaseLayerReferences(imgDescr map[image.ID]*imageDescriptor) error {
 	for _, descr := range imgDescr {
 		if descr.layerRef != nil {
-			os := descr.image.OS
-			if os == "" {
-				os = runtime.GOOS
-			}
-			l.lss[os].Release(descr.layerRef)
+			l.lss.Release(descr.layerRef)
 		}
 	}
 	return nil
@@ -189,7 +184,7 @@ func (s *saveSession) save(outStream io.Writer) error {
 	s.diffIDPaths = make(map[layer.DiffID]string)
 
 	// get image json
-	tempDir, err := ioutil.TempDir("", "docker-export-")
+	tempDir, err := os.MkdirTemp("", "docker-export-")
 	if err != nil {
 		return err
 	}
@@ -221,9 +216,7 @@ func (s *saveSession) save(outStream io.Writer) error {
 
 		for _, l := range imageDescr.layers {
 			// IMPORTANT: We use path, not filepath here to ensure the layers
-			// in the manifest use Unix-style forward-slashes. Otherwise, a
-			// Linux image saved from LCOW won't be able to be imported on
-			// LCOL.
+			// in the manifest use Unix-style forward-slashes.
 			layers = append(layers, path.Join(l, legacyLayerFileName))
 		}
 
@@ -337,7 +330,7 @@ func (s *saveSession) saveImage(id image.ID) (map[layer.DiffID]distribution.Desc
 	}
 
 	configFile := filepath.Join(s.outDir, id.Digest().Hex()+".json")
-	if err := ioutil.WriteFile(configFile, img.RawJSON(), 0644); err != nil {
+	if err := os.WriteFile(configFile, img.RawJSON(), 0644); err != nil {
 		return nil, err
 	}
 	if err := system.Chtimes(configFile, img.Created, img.Created); err != nil {
@@ -359,7 +352,7 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat
 	}
 
 	// todo: why is this version file here?
-	if err := ioutil.WriteFile(filepath.Join(outDir, legacyVersionFileName), []byte("1.0"), 0644); err != nil {
+	if err := os.WriteFile(filepath.Join(outDir, legacyVersionFileName), []byte("1.0"), 0644); err != nil {
 		return distribution.Descriptor{}, err
 	}
 
@@ -368,21 +361,17 @@ func (s *saveSession) saveLayer(id layer.ChainID, legacyImg image.V1Image, creat
 		return distribution.Descriptor{}, err
 	}
 
-	if err := ioutil.WriteFile(filepath.Join(outDir, legacyConfigFileName), imageConfig, 0644); err != nil {
+	if err := os.WriteFile(filepath.Join(outDir, legacyConfigFileName), imageConfig, 0644); err != nil {
 		return distribution.Descriptor{}, err
 	}
 
 	// serialize filesystem
 	layerPath := filepath.Join(outDir, legacyLayerFileName)
-	operatingSystem := legacyImg.OS
-	if operatingSystem == "" {
-		operatingSystem = runtime.GOOS
-	}
-	l, err := s.lss[operatingSystem].Get(id)
+	l, err := s.lss.Get(id)
 	if err != nil {
 		return distribution.Descriptor{}, err
 	}
-	defer layer.ReleaseAndLog(s.lss[operatingSystem], l)
+	defer layer.ReleaseAndLog(s.lss, l)
 
 	if oldPath, exists := s.diffIDPaths[l.DiffID()]; exists {
 		relPath, err := filepath.Rel(outDir, oldPath)
diff --git a/image/tarexport/tarexport.go b/image/tarexport/tarexport.go
index beff668cd87d0..5bcad2265c878 100644
--- a/image/tarexport/tarexport.go
+++ b/image/tarexport/tarexport.go
@@ -25,19 +25,19 @@ type manifestItem struct {
 
 type tarexporter struct {
 	is             image.Store
-	lss            map[string]layer.Store
+	lss            layer.Store
 	rs             refstore.Store
 	loggerImgEvent LogImageEvent
 }
 
 // LogImageEvent defines interface for event generation related to image tar(load and save) operations
 type LogImageEvent interface {
-	//LogImageEvent generates an event related to an image operation
+	// LogImageEvent generates an event related to an image operation
 	LogImageEvent(imageID, refName, action string)
 }
 
 // NewTarExporter returns new Exporter for tar packages
-func NewTarExporter(is image.Store, lss map[string]layer.Store, rs refstore.Store, loggerImgEvent LogImageEvent) image.Exporter {
+func NewTarExporter(is image.Store, lss layer.Store, rs refstore.Store, loggerImgEvent LogImageEvent) image.Exporter {
 	return &tarexporter{
 		is:             is,
 		lss:            lss,
diff --git a/image/v1/imagev1.go b/image/v1/imagev1.go
index c341ceaa77038..1eec8dfc8c00a 100644
--- a/image/v1/imagev1.go
+++ b/image/v1/imagev1.go
@@ -9,7 +9,7 @@ import (
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
 	"github.com/sirupsen/logrus"
 )
 
diff --git a/integration-cli/benchmark_test.go b/integration-cli/benchmark_test.go
index ae0f67f6b04da..8ab87b22d9dbb 100644
--- a/integration-cli/benchmark_test.go
+++ b/integration-cli/benchmark_test.go
@@ -2,17 +2,16 @@ package main
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"runtime"
 	"strings"
 	"sync"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) BenchmarkConcurrentContainerActions(c *check.C) {
+func (s *DockerSuite) BenchmarkConcurrentContainerActions(c *testing.B) {
 	maxConcurrency := runtime.GOMAXPROCS(0)
 	numIterations := c.N
 	outerGroup := &sync.WaitGroup{}
@@ -28,7 +27,7 @@ func (s *DockerSuite) BenchmarkConcurrentContainerActions(c *check.C) {
 			go func() {
 				defer innerGroup.Done()
 				for i := 0; i < numIterations; i++ {
-					args := []string{"run", "-d", defaultSleepImage}
+					args := []string{"run", "-d", "busybox"}
 					args = append(args, sleepCommandForDaemonPlatform()...)
 					out, _, err := dockerCmdWithError(args...)
 					if err != nil {
@@ -37,7 +36,7 @@ func (s *DockerSuite) BenchmarkConcurrentContainerActions(c *check.C) {
 					}
 
 					id := strings.TrimSpace(out)
-					tmpDir, err := ioutil.TempDir("", "docker-concurrent-test-"+id)
+					tmpDir, err := os.MkdirTemp("", "docker-concurrent-test-"+id)
 					if err != nil {
 						chErr <- err
 						return
@@ -90,6 +89,6 @@ func (s *DockerSuite) BenchmarkConcurrentContainerActions(c *check.C) {
 	close(chErr)
 
 	for err := range chErr {
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	}
 }
diff --git a/integration-cli/check_test.go b/integration-cli/check_test.go
index 2282967ee5695..9a710b5210d59 100644
--- a/integration-cli/check_test.go
+++ b/integration-cli/check_test.go
@@ -2,8 +2,8 @@ package main
 
 import (
 	"context"
+	"flag"
 	"fmt"
-	"io/ioutil"
 	"net/http/httptest"
 	"os"
 	"path"
@@ -14,17 +14,17 @@ import (
 	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/daemon"
 	"github.com/docker/docker/integration-cli/environment"
-	testdaemon "github.com/docker/docker/internal/test/daemon"
-	ienv "github.com/docker/docker/internal/test/environment"
-	"github.com/docker/docker/internal/test/fakestorage"
-	"github.com/docker/docker/internal/test/fixtures/plugin"
-	"github.com/docker/docker/internal/test/registry"
+	"github.com/docker/docker/internal/test/suite"
 	"github.com/docker/docker/pkg/reexec"
-	"github.com/go-check/check"
+	testdaemon "github.com/docker/docker/testutil/daemon"
+	ienv "github.com/docker/docker/testutil/environment"
+	"github.com/docker/docker/testutil/fakestorage"
+	"github.com/docker/docker/testutil/fixtures/plugin"
+	"github.com/docker/docker/testutil/registry"
+	"gotest.tools/v3/assert"
 )
 
 const (
@@ -43,6 +43,8 @@ var (
 
 	// the docker client binary to use
 	dockerBinary = ""
+
+	testEnvOnce sync.Once
 )
 
 func init() {
@@ -58,6 +60,9 @@ func init() {
 }
 
 func TestMain(m *testing.M) {
+	flag.Parse()
+
+	// Global set up
 	dockerBinary = testEnv.DockerBinary()
 	err := ienv.EnsureFrozenImagesLinux(&testEnv.Execution)
 	if err != nil {
@@ -69,26 +74,83 @@ func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
 
-func Test(t *testing.T) {
-	cli.SetTestEnvironment(testEnv)
-	fakestorage.SetTestEnvironment(&testEnv.Execution)
-	ienv.ProtectAll(t, &testEnv.Execution)
-	check.TestingT(t)
+func ensureTestEnvSetup(t *testing.T) {
+	testEnvOnce.Do(func() {
+		cli.SetTestEnvironment(testEnv)
+		fakestorage.SetTestEnvironment(&testEnv.Execution)
+		ienv.ProtectAll(t, &testEnv.Execution)
+	})
 }
 
-func init() {
-	check.Suite(&DockerSuite{})
+func TestDockerSuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	suite.Run(t, &DockerSuite{})
+}
+
+func TestDockerRegistrySuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	suite.Run(t, &DockerRegistrySuite{ds: &DockerSuite{}})
+}
+
+func TestDockerSchema1RegistrySuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	suite.Run(t, &DockerSchema1RegistrySuite{ds: &DockerSuite{}})
+}
+
+func TestDockerRegistryAuthHtpasswdSuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	suite.Run(t, &DockerRegistryAuthHtpasswdSuite{ds: &DockerSuite{}})
+}
+
+func TestDockerRegistryAuthTokenSuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	suite.Run(t, &DockerRegistryAuthTokenSuite{ds: &DockerSuite{}})
+}
+
+func TestDockerDaemonSuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	suite.Run(t, &DockerDaemonSuite{ds: &DockerSuite{}})
+}
+
+func TestDockerSwarmSuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	suite.Run(t, &DockerSwarmSuite{ds: &DockerSuite{}})
+}
+
+func TestDockerPluginSuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	suite.Run(t, &DockerPluginSuite{ds: &DockerSuite{}})
+}
+
+func TestDockerExternalVolumeSuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	testRequires(t, DaemonIsLinux)
+	suite.Run(t, &DockerExternalVolumeSuite{ds: &DockerSuite{}})
+}
+
+func TestDockerNetworkSuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	testRequires(t, DaemonIsLinux)
+	suite.Run(t, &DockerNetworkSuite{ds: &DockerSuite{}})
+}
+
+func TestDockerHubPullSuite(t *testing.T) {
+	ensureTestEnvSetup(t)
+	// FIXME. Temporarily turning this off for Windows as GH16039 was breaking
+	// Windows to Linux CI @icecrime
+	testRequires(t, DaemonIsLinux)
+	suite.Run(t, newDockerHubPullSuite())
 }
 
 type DockerSuite struct {
 }
 
-func (s *DockerSuite) OnTimeout(c *check.C) {
+func (s *DockerSuite) OnTimeout(c *testing.T) {
 	if testEnv.IsRemoteDaemon() {
 		return
 	}
 	path := filepath.Join(os.Getenv("DEST"), "docker.pid")
-	b, err := ioutil.ReadFile(path)
+	b, err := os.ReadFile(path)
 	if err != nil {
 		c.Fatalf("Failed to get daemon PID from %s\n", path)
 	}
@@ -104,34 +166,28 @@ func (s *DockerSuite) OnTimeout(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TearDownTest(c *check.C) {
+func (s *DockerSuite) TearDownTest(c *testing.T) {
 	testEnv.Clean(c)
 }
 
-func init() {
-	check.Suite(&DockerRegistrySuite{
-		ds: &DockerSuite{},
-	})
-}
-
 type DockerRegistrySuite struct {
 	ds  *DockerSuite
 	reg *registry.V2
 	d   *daemon.Daemon
 }
 
-func (s *DockerRegistrySuite) OnTimeout(c *check.C) {
+func (s *DockerRegistrySuite) OnTimeout(c *testing.T) {
 	s.d.DumpStackAndQuit()
 }
 
-func (s *DockerRegistrySuite) SetUpTest(c *check.C) {
-	testRequires(c, DaemonIsLinux, RegistryHosting, SameHostDaemon)
+func (s *DockerRegistrySuite) SetUpTest(c *testing.T) {
+	testRequires(c, DaemonIsLinux, RegistryHosting, testEnv.IsLocalDaemon)
 	s.reg = registry.NewV2(c)
 	s.reg.WaitReady(c)
 	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
 }
 
-func (s *DockerRegistrySuite) TearDownTest(c *check.C) {
+func (s *DockerRegistrySuite) TearDownTest(c *testing.T) {
 	if s.reg != nil {
 		s.reg.Close()
 	}
@@ -141,30 +197,24 @@ func (s *DockerRegistrySuite) TearDownTest(c *check.C) {
 	s.ds.TearDownTest(c)
 }
 
-func init() {
-	check.Suite(&DockerSchema1RegistrySuite{
-		ds: &DockerSuite{},
-	})
-}
-
 type DockerSchema1RegistrySuite struct {
 	ds  *DockerSuite
 	reg *registry.V2
 	d   *daemon.Daemon
 }
 
-func (s *DockerSchema1RegistrySuite) OnTimeout(c *check.C) {
+func (s *DockerSchema1RegistrySuite) OnTimeout(c *testing.T) {
 	s.d.DumpStackAndQuit()
 }
 
-func (s *DockerSchema1RegistrySuite) SetUpTest(c *check.C) {
-	testRequires(c, DaemonIsLinux, RegistryHosting, NotArm64, SameHostDaemon)
+func (s *DockerSchema1RegistrySuite) SetUpTest(c *testing.T) {
+	testRequires(c, DaemonIsLinux, RegistryHosting, NotArm64, testEnv.IsLocalDaemon)
 	s.reg = registry.NewV2(c, registry.Schema1)
 	s.reg.WaitReady(c)
 	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
 }
 
-func (s *DockerSchema1RegistrySuite) TearDownTest(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TearDownTest(c *testing.T) {
 	if s.reg != nil {
 		s.reg.Close()
 	}
@@ -174,33 +224,27 @@ func (s *DockerSchema1RegistrySuite) TearDownTest(c *check.C) {
 	s.ds.TearDownTest(c)
 }
 
-func init() {
-	check.Suite(&DockerRegistryAuthHtpasswdSuite{
-		ds: &DockerSuite{},
-	})
-}
-
 type DockerRegistryAuthHtpasswdSuite struct {
 	ds  *DockerSuite
 	reg *registry.V2
 	d   *daemon.Daemon
 }
 
-func (s *DockerRegistryAuthHtpasswdSuite) OnTimeout(c *check.C) {
+func (s *DockerRegistryAuthHtpasswdSuite) OnTimeout(c *testing.T) {
 	s.d.DumpStackAndQuit()
 }
 
-func (s *DockerRegistryAuthHtpasswdSuite) SetUpTest(c *check.C) {
-	testRequires(c, DaemonIsLinux, RegistryHosting, SameHostDaemon)
+func (s *DockerRegistryAuthHtpasswdSuite) SetUpTest(c *testing.T) {
+	testRequires(c, DaemonIsLinux, RegistryHosting, testEnv.IsLocalDaemon)
 	s.reg = registry.NewV2(c, registry.Htpasswd)
 	s.reg.WaitReady(c)
 	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
 }
 
-func (s *DockerRegistryAuthHtpasswdSuite) TearDownTest(c *check.C) {
+func (s *DockerRegistryAuthHtpasswdSuite) TearDownTest(c *testing.T) {
 	if s.reg != nil {
 		out, err := s.d.Cmd("logout", privateRegistryURL)
-		c.Assert(err, check.IsNil, check.Commentf("%s", out))
+		assert.NilError(c, err, out)
 		s.reg.Close()
 	}
 	if s.d != nil {
@@ -209,31 +253,25 @@ func (s *DockerRegistryAuthHtpasswdSuite) TearDownTest(c *check.C) {
 	s.ds.TearDownTest(c)
 }
 
-func init() {
-	check.Suite(&DockerRegistryAuthTokenSuite{
-		ds: &DockerSuite{},
-	})
-}
-
 type DockerRegistryAuthTokenSuite struct {
 	ds  *DockerSuite
 	reg *registry.V2
 	d   *daemon.Daemon
 }
 
-func (s *DockerRegistryAuthTokenSuite) OnTimeout(c *check.C) {
+func (s *DockerRegistryAuthTokenSuite) OnTimeout(c *testing.T) {
 	s.d.DumpStackAndQuit()
 }
 
-func (s *DockerRegistryAuthTokenSuite) SetUpTest(c *check.C) {
-	testRequires(c, DaemonIsLinux, RegistryHosting, SameHostDaemon)
+func (s *DockerRegistryAuthTokenSuite) SetUpTest(c *testing.T) {
+	testRequires(c, DaemonIsLinux, RegistryHosting, testEnv.IsLocalDaemon)
 	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
 }
 
-func (s *DockerRegistryAuthTokenSuite) TearDownTest(c *check.C) {
+func (s *DockerRegistryAuthTokenSuite) TearDownTest(c *testing.T) {
 	if s.reg != nil {
 		out, err := s.d.Cmd("logout", privateRegistryURL)
-		c.Assert(err, check.IsNil, check.Commentf("%s", out))
+		assert.NilError(c, err, out)
 		s.reg.Close()
 	}
 	if s.d != nil {
@@ -242,7 +280,7 @@ func (s *DockerRegistryAuthTokenSuite) TearDownTest(c *check.C) {
 	s.ds.TearDownTest(c)
 }
 
-func (s *DockerRegistryAuthTokenSuite) setupRegistryWithTokenService(c *check.C, tokenURL string) {
+func (s *DockerRegistryAuthTokenSuite) setupRegistryWithTokenService(c *testing.T, tokenURL string) {
 	if s == nil {
 		c.Fatal("registry suite isn't initialized")
 	}
@@ -250,35 +288,29 @@ func (s *DockerRegistryAuthTokenSuite) setupRegistryWithTokenService(c *check.C,
 	s.reg.WaitReady(c)
 }
 
-func init() {
-	check.Suite(&DockerDaemonSuite{
-		ds: &DockerSuite{},
-	})
-}
-
 type DockerDaemonSuite struct {
 	ds *DockerSuite
 	d  *daemon.Daemon
 }
 
-func (s *DockerDaemonSuite) OnTimeout(c *check.C) {
+func (s *DockerDaemonSuite) OnTimeout(c *testing.T) {
 	s.d.DumpStackAndQuit()
 }
 
-func (s *DockerDaemonSuite) SetUpTest(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerDaemonSuite) SetUpTest(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
 }
 
-func (s *DockerDaemonSuite) TearDownTest(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerDaemonSuite) TearDownTest(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	if s.d != nil {
 		s.d.Stop(c)
 	}
 	s.ds.TearDownTest(c)
 }
 
-func (s *DockerDaemonSuite) TearDownSuite(c *check.C) {
+func (s *DockerDaemonSuite) TearDownSuite(c *testing.T) {
 	filepath.Walk(testdaemon.SockRoot, func(path string, fi os.FileInfo, err error) error {
 		if err != nil {
 			// ignore errors here
@@ -295,21 +327,15 @@ func (s *DockerDaemonSuite) TearDownSuite(c *check.C) {
 
 const defaultSwarmPort = 2477
 
-func init() {
-	check.Suite(&DockerSwarmSuite{
-		ds: &DockerSuite{},
-	})
-}
-
 type DockerSwarmSuite struct {
 	server      *httptest.Server
 	ds          *DockerSuite
+	daemonsLock sync.Mutex // protect access to daemons and portIndex
 	daemons     []*daemon.Daemon
-	daemonsLock sync.Mutex // protect access to daemons
 	portIndex   int
 }
 
-func (s *DockerSwarmSuite) OnTimeout(c *check.C) {
+func (s *DockerSwarmSuite) OnTimeout(c *testing.T) {
 	s.daemonsLock.Lock()
 	defer s.daemonsLock.Unlock()
 	for _, d := range s.daemons {
@@ -317,11 +343,12 @@ func (s *DockerSwarmSuite) OnTimeout(c *check.C) {
 	}
 }
 
-func (s *DockerSwarmSuite) SetUpTest(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSwarmSuite) SetUpTest(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 }
 
-func (s *DockerSwarmSuite) AddDaemon(c *check.C, joinSwarm, manager bool) *daemon.Daemon {
+func (s *DockerSwarmSuite) AddDaemon(c *testing.T, joinSwarm, manager bool) *daemon.Daemon {
+	c.Helper()
 	d := daemon.New(c, dockerBinary, dockerdBinary,
 		testdaemon.WithEnvironment(testEnv.Execution),
 		testdaemon.WithSwarmPort(defaultSwarmPort+s.portIndex),
@@ -333,18 +360,18 @@ func (s *DockerSwarmSuite) AddDaemon(c *check.C, joinSwarm, manager bool) *daemo
 			d.StartAndSwarmInit(c)
 		}
 	} else {
-		d.StartWithBusybox(c, "--iptables=false", "--swarm-default-advertise-addr=lo")
+		d.StartNodeWithBusybox(c)
 	}
 
-	s.portIndex++
 	s.daemonsLock.Lock()
+	s.portIndex++
 	s.daemons = append(s.daemons, d)
 	s.daemonsLock.Unlock()
 
 	return d
 }
 
-func (s *DockerSwarmSuite) TearDownTest(c *check.C) {
+func (s *DockerSwarmSuite) TearDownTest(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	s.daemonsLock.Lock()
 	for _, d := range s.daemons {
@@ -354,18 +381,11 @@ func (s *DockerSwarmSuite) TearDownTest(c *check.C) {
 		}
 	}
 	s.daemons = nil
-	s.daemonsLock.Unlock()
-
 	s.portIndex = 0
+	s.daemonsLock.Unlock()
 	s.ds.TearDownTest(c)
 }
 
-func init() {
-	check.Suite(&DockerPluginSuite{
-		ds: &DockerSuite{},
-	})
-}
-
 type DockerPluginSuite struct {
 	ds       *DockerSuite
 	registry *registry.V2
@@ -382,7 +402,7 @@ func (ps *DockerPluginSuite) getPluginRepoWithTag() string {
 	return ps.getPluginRepo() + ":" + "latest"
 }
 
-func (ps *DockerPluginSuite) SetUpSuite(c *check.C) {
+func (ps *DockerPluginSuite) SetUpSuite(c *testing.T) {
 	testRequires(c, DaemonIsLinux, RegistryHosting)
 	ps.registry = registry.NewV2(c)
 	ps.registry.WaitReady(c)
@@ -391,19 +411,19 @@ func (ps *DockerPluginSuite) SetUpSuite(c *check.C) {
 	defer cancel()
 
 	err := plugin.CreateInRegistry(ctx, ps.getPluginRepo(), nil)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create plugin"))
+	assert.NilError(c, err, "failed to create plugin")
 }
 
-func (ps *DockerPluginSuite) TearDownSuite(c *check.C) {
+func (ps *DockerPluginSuite) TearDownSuite(c *testing.T) {
 	if ps.registry != nil {
 		ps.registry.Close()
 	}
 }
 
-func (ps *DockerPluginSuite) TearDownTest(c *check.C) {
+func (ps *DockerPluginSuite) TearDownTest(c *testing.T) {
 	ps.ds.TearDownTest(c)
 }
 
-func (ps *DockerPluginSuite) OnTimeout(c *check.C) {
+func (ps *DockerPluginSuite) OnTimeout(c *testing.T) {
 	ps.ds.OnTimeout(c)
 }
diff --git a/integration-cli/checker/checker.go b/integration-cli/checker/checker.go
index d7fdc412ba908..a0b5343aa174e 100644
--- a/integration-cli/checker/checker.go
+++ b/integration-cli/checker/checker.go
@@ -1,46 +1,84 @@
-// Package checker provides Docker specific implementations of the go-check.Checker interface.
+// Package checker provides helpers for gotest.tools/assert.
+// Please remove this package whenever possible.
 package checker // import "github.com/docker/docker/integration-cli/checker"
 
 import (
-	"github.com/go-check/check"
-	"github.com/vdemeester/shakers"
-)
+	"fmt"
 
-// As a commodity, we bring all check.Checker variables into the current namespace to avoid having
-// to think about check.X versus checker.X.
-var (
-	DeepEquals   = check.DeepEquals
-	ErrorMatches = check.ErrorMatches
-	FitsTypeOf   = check.FitsTypeOf
-	HasLen       = check.HasLen
-	Implements   = check.Implements
-	IsNil        = check.IsNil
-	Matches      = check.Matches
-	Not          = check.Not
-	NotNil       = check.NotNil
-	PanicMatches = check.PanicMatches
-	Panics       = check.Panics
-
-	Contains           = shakers.Contains
-	ContainsAny        = shakers.ContainsAny
-	Count              = shakers.Count
-	Equals             = shakers.Equals
-	EqualFold          = shakers.EqualFold
-	False              = shakers.False
-	GreaterOrEqualThan = shakers.GreaterOrEqualThan
-	GreaterThan        = shakers.GreaterThan
-	HasPrefix          = shakers.HasPrefix
-	HasSuffix          = shakers.HasSuffix
-	Index              = shakers.Index
-	IndexAny           = shakers.IndexAny
-	IsAfter            = shakers.IsAfter
-	IsBefore           = shakers.IsBefore
-	IsBetween          = shakers.IsBetween
-	IsLower            = shakers.IsLower
-	IsUpper            = shakers.IsUpper
-	LessOrEqualThan    = shakers.LessOrEqualThan
-	LessThan           = shakers.LessThan
-	TimeEquals         = shakers.TimeEquals
-	True               = shakers.True
-	TimeIgnore         = shakers.TimeIgnore
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/assert/cmp"
 )
+
+// Compare defines the interface to compare values
+type Compare func(x interface{}) assert.BoolOrComparison
+
+// False checks if the value is false
+func False() Compare {
+	return func(x interface{}) assert.BoolOrComparison {
+		return !x.(bool)
+	}
+}
+
+// True checks if the value is true
+func True() Compare {
+	return func(x interface{}) assert.BoolOrComparison {
+		return x
+	}
+}
+
+// Equals checks if the value is equal to the given value
+func Equals(y interface{}) Compare {
+	return func(x interface{}) assert.BoolOrComparison {
+		return cmp.Equal(x, y)
+	}
+}
+
+// Contains checks if the value contains the given value
+func Contains(y interface{}) Compare {
+	return func(x interface{}) assert.BoolOrComparison {
+		return cmp.Contains(x, y)
+	}
+}
+
+// Not checks if two values are not
+func Not(c Compare) Compare {
+	return func(x interface{}) assert.BoolOrComparison {
+		r := c(x)
+		switch r := r.(type) {
+		case bool:
+			return !r
+		case cmp.Comparison:
+			return !r().Success()
+		default:
+			panic(fmt.Sprintf("unexpected type %T", r))
+		}
+	}
+}
+
+// DeepEquals checks if two values are equal
+func DeepEquals(y interface{}) Compare {
+	return func(x interface{}) assert.BoolOrComparison {
+		return cmp.DeepEqual(x, y)
+	}
+}
+
+// HasLen checks if the value has the expected number of elements
+func HasLen(y int) Compare {
+	return func(x interface{}) assert.BoolOrComparison {
+		return cmp.Len(x, y)
+	}
+}
+
+// IsNil checks if the value is nil
+func IsNil() Compare {
+	return func(x interface{}) assert.BoolOrComparison {
+		return cmp.Nil(x)
+	}
+}
+
+// GreaterThan checks if the value is greater than the given value
+func GreaterThan(y int) Compare {
+	return func(x interface{}) assert.BoolOrComparison {
+		return x.(int) > y
+	}
+}
diff --git a/integration-cli/cli/build/build.go b/integration-cli/cli/build/build.go
index 0b10ea79f81e5..7afede8a97157 100644
--- a/integration-cli/cli/build/build.go
+++ b/integration-cli/cli/build/build.go
@@ -3,16 +3,12 @@ package build // import "github.com/docker/docker/integration-cli/cli/build"
 import (
 	"io"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/internal/test/fakecontext"
-	"gotest.tools/icmd"
+	"github.com/docker/docker/testutil/fakecontext"
+	"gotest.tools/v3/icmd"
 )
 
-type testingT interface {
-	Fatal(args ...interface{})
-	Fatalf(string, ...interface{})
-}
-
 // WithStdinContext sets the build context from the standard input with the specified reader
 func WithStdinContext(closer io.ReadCloser) func(*icmd.Cmd) func() {
 	return func(cmd *icmd.Cmd) func() {
@@ -58,7 +54,7 @@ func WithExternalBuildContext(ctx *fakecontext.Fake) func(*icmd.Cmd) func() {
 }
 
 // WithBuildContext sets up the build context
-func WithBuildContext(t testingT, contextOperators ...func(*fakecontext.Fake) error) func(*icmd.Cmd) func() {
+func WithBuildContext(t testing.TB, contextOperators ...func(*fakecontext.Fake) error) func(*icmd.Cmd) func() {
 	// FIXME(vdemeester) de-duplicate that
 	ctx := fakecontext.New(t, "", contextOperators...)
 	return func(cmd *icmd.Cmd) func() {
@@ -73,7 +69,7 @@ func WithFile(name, content string) func(*fakecontext.Fake) error {
 	return fakecontext.WithFile(name, content)
 }
 
-func closeBuildContext(t testingT, ctx *fakecontext.Fake) func() {
+func closeBuildContext(t testing.TB, ctx *fakecontext.Fake) func() {
 	return func() {
 		if err := ctx.Close(); err != nil {
 			t.Fatal(err)
diff --git a/integration-cli/cli/cli.go b/integration-cli/cli/cli.go
index bc3f3c194e52e..7b4796db4dd3f 100644
--- a/integration-cli/cli/cli.go
+++ b/integration-cli/cli/cli.go
@@ -4,13 +4,13 @@ import (
 	"fmt"
 	"io"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/integration-cli/daemon"
 	"github.com/docker/docker/integration-cli/environment"
 	"github.com/pkg/errors"
-	"gotest.tools/assert"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/icmd"
 )
 
 var testEnv *environment.Execution
@@ -24,45 +24,40 @@ func SetTestEnvironment(env *environment.Execution) {
 // CmdOperator defines functions that can modify a command
 type CmdOperator func(*icmd.Cmd) func()
 
-type testingT interface {
-	assert.TestingT
-	Fatal(args ...interface{})
-	Fatalf(string, ...interface{})
-}
-
 // DockerCmd executes the specified docker command and expect a success
-func DockerCmd(t testingT, args ...string) *icmd.Result {
+func DockerCmd(t testing.TB, args ...string) *icmd.Result {
+	t.Helper()
 	return Docker(Args(args...)).Assert(t, icmd.Success)
 }
 
 // BuildCmd executes the specified docker build command and expect a success
-func BuildCmd(t testingT, name string, cmdOperators ...CmdOperator) *icmd.Result {
+func BuildCmd(t testing.TB, name string, cmdOperators ...CmdOperator) *icmd.Result {
 	return Docker(Build(name), cmdOperators...).Assert(t, icmd.Success)
 }
 
 // InspectCmd executes the specified docker inspect command and expect a success
-func InspectCmd(t testingT, name string, cmdOperators ...CmdOperator) *icmd.Result {
+func InspectCmd(t testing.TB, name string, cmdOperators ...CmdOperator) *icmd.Result {
 	return Docker(Inspect(name), cmdOperators...).Assert(t, icmd.Success)
 }
 
 // WaitRun will wait for the specified container to be running, maximum 5 seconds.
-func WaitRun(t testingT, name string, cmdOperators ...CmdOperator) {
+func WaitRun(t testing.TB, name string, cmdOperators ...CmdOperator) {
 	WaitForInspectResult(t, name, "{{.State.Running}}", "true", 5*time.Second, cmdOperators...)
 }
 
 // WaitExited will wait for the specified container to state exit, subject
 // to a maximum time limit in seconds supplied by the caller
-func WaitExited(t testingT, name string, timeout time.Duration, cmdOperators ...CmdOperator) {
+func WaitExited(t testing.TB, name string, timeout time.Duration, cmdOperators ...CmdOperator) {
 	WaitForInspectResult(t, name, "{{.State.Status}}", "exited", timeout, cmdOperators...)
 }
 
 // WaitRestart will wait for the specified container to restart once
-func WaitRestart(t testingT, name string, timeout time.Duration, cmdOperators ...CmdOperator) {
+func WaitRestart(t testing.TB, name string, timeout time.Duration, cmdOperators ...CmdOperator) {
 	WaitForInspectResult(t, name, "{{.RestartCount}}", "1", timeout, cmdOperators...)
 }
 
 // WaitForInspectResult waits for the specified expression to be equals to the specified expected string in the given time.
-func WaitForInspectResult(t testingT, name, expr, expected string, timeout time.Duration, cmdOperators ...CmdOperator) {
+func WaitForInspectResult(t testing.TB, name, expr, expected string, timeout time.Duration, cmdOperators ...CmdOperator) {
 	after := time.After(timeout)
 
 	args := []string{"inspect", "-f", expr, name}
diff --git a/integration-cli/daemon/daemon.go b/integration-cli/daemon/daemon.go
index 3d1fa38d5dde3..8a49e85742410 100644
--- a/integration-cli/daemon/daemon.go
+++ b/integration-cli/daemon/daemon.go
@@ -3,26 +3,15 @@ package daemon // import "github.com/docker/docker/integration-cli/daemon"
 import (
 	"fmt"
 	"strings"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/daemon"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/daemon"
 	"github.com/pkg/errors"
-	"gotest.tools/assert"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
-type testingT interface {
-	assert.TestingT
-	logT
-	Fatalf(string, ...interface{})
-}
-
-type logT interface {
-	Logf(string, ...interface{})
-}
-
 // Daemon represents a Docker daemon for the testing framework.
 type Daemon struct {
 	*daemon.Daemon
@@ -32,7 +21,8 @@ type Daemon struct {
 // New returns a Daemon instance to be used for testing.
 // This will create a directory such as d123456789 in the folder specified by $DOCKER_INTEGRATION_DAEMON_DEST or $DEST.
 // The daemon will not automatically start.
-func New(t testingT, dockerBinary string, dockerdBinary string, ops ...func(*daemon.Daemon)) *Daemon {
+func New(t testing.TB, dockerBinary string, dockerdBinary string, ops ...daemon.Option) *Daemon {
+	t.Helper()
 	ops = append(ops, daemon.WithDockerdBinary(dockerdBinary))
 	d := daemon.New(t, ops...)
 	return &Daemon{
@@ -89,13 +79,14 @@ func (d *Daemon) inspectFieldWithError(name, field string) (string, error) {
 
 // CheckActiveContainerCount returns the number of active containers
 // FIXME(vdemeester) should re-use ActivateContainers in some way
-func (d *Daemon) CheckActiveContainerCount(c *check.C) (interface{}, check.CommentInterface) {
+func (d *Daemon) CheckActiveContainerCount(t *testing.T) (interface{}, string) {
+	t.Helper()
 	out, err := d.Cmd("ps", "-q")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(t, err)
 	if len(strings.TrimSpace(out)) == 0 {
-		return 0, nil
+		return 0, ""
 	}
-	return len(strings.Split(strings.TrimSpace(out), "\n")), check.Commentf("output: %q", string(out))
+	return len(strings.Split(strings.TrimSpace(out), "\n")), fmt.Sprintf("output: %q", out)
 }
 
 // WaitRun waits for a container to be running for 10s
diff --git a/integration-cli/daemon/daemon_swarm.go b/integration-cli/daemon/daemon_swarm.go
index 4a6ce8a5c5142..74b5877c8d4a1 100644
--- a/integration-cli/daemon/daemon_swarm.go
+++ b/integration-cli/daemon/daemon_swarm.go
@@ -4,20 +4,19 @@ import (
 	"context"
 	"fmt"
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-	"gotest.tools/assert"
+	"gotest.tools/v3/assert"
 )
 
 // CheckServiceTasksInState returns the number of tasks with a matching state,
 // and optional message substring.
-func (d *Daemon) CheckServiceTasksInState(service string, state swarm.TaskState, message string) func(*check.C) (interface{}, check.CommentInterface) {
-	return func(c *check.C) (interface{}, check.CommentInterface) {
+func (d *Daemon) CheckServiceTasksInState(service string, state swarm.TaskState, message string) func(*testing.T) (interface{}, string) {
+	return func(c *testing.T) (interface{}, string) {
 		tasks := d.GetServiceTasks(c, service)
 		var count int
 		for _, task := range tasks {
@@ -27,14 +26,14 @@ func (d *Daemon) CheckServiceTasksInState(service string, state swarm.TaskState,
 				}
 			}
 		}
-		return count, nil
+		return count, ""
 	}
 }
 
 // CheckServiceTasksInStateWithError returns the number of tasks with a matching state,
 // and optional message substring.
-func (d *Daemon) CheckServiceTasksInStateWithError(service string, state swarm.TaskState, errorMessage string) func(*check.C) (interface{}, check.CommentInterface) {
-	return func(c *check.C) (interface{}, check.CommentInterface) {
+func (d *Daemon) CheckServiceTasksInStateWithError(service string, state swarm.TaskState, errorMessage string) func(*testing.T) (interface{}, string) {
+	return func(c *testing.T) (interface{}, string) {
 		tasks := d.GetServiceTasks(c, service)
 		var count int
 		for _, task := range tasks {
@@ -44,66 +43,63 @@ func (d *Daemon) CheckServiceTasksInStateWithError(service string, state swarm.T
 				}
 			}
 		}
-		return count, nil
+		return count, ""
 	}
 }
 
 // CheckServiceRunningTasks returns the number of running tasks for the specified service
-func (d *Daemon) CheckServiceRunningTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
+func (d *Daemon) CheckServiceRunningTasks(service string) func(*testing.T) (interface{}, string) {
 	return d.CheckServiceTasksInState(service, swarm.TaskStateRunning, "")
 }
 
 // CheckServiceUpdateState returns the current update state for the specified service
-func (d *Daemon) CheckServiceUpdateState(service string) func(*check.C) (interface{}, check.CommentInterface) {
-	return func(c *check.C) (interface{}, check.CommentInterface) {
+func (d *Daemon) CheckServiceUpdateState(service string) func(*testing.T) (interface{}, string) {
+	return func(c *testing.T) (interface{}, string) {
 		service := d.GetService(c, service)
 		if service.UpdateStatus == nil {
-			return "", nil
+			return "", ""
 		}
-		return service.UpdateStatus.State, nil
+		return service.UpdateStatus.State, ""
 	}
 }
 
 // CheckPluginRunning returns the runtime state of the plugin
-func (d *Daemon) CheckPluginRunning(plugin string) func(c *check.C) (interface{}, check.CommentInterface) {
-	return func(c *check.C) (interface{}, check.CommentInterface) {
-		apiclient, err := d.NewClient()
-		assert.NilError(c, err)
+func (d *Daemon) CheckPluginRunning(plugin string) func(c *testing.T) (interface{}, string) {
+	return func(c *testing.T) (interface{}, string) {
+		apiclient := d.NewClientT(c)
 		resp, _, err := apiclient.PluginInspectWithRaw(context.Background(), plugin)
 		if client.IsErrNotFound(err) {
-			return false, check.Commentf("%v", err)
+			return false, fmt.Sprintf("%v", err)
 		}
 		assert.NilError(c, err)
-		return resp.Enabled, check.Commentf("%+v", resp)
+		return resp.Enabled, fmt.Sprintf("%+v", resp)
 	}
 }
 
 // CheckPluginImage returns the runtime state of the plugin
-func (d *Daemon) CheckPluginImage(plugin string) func(c *check.C) (interface{}, check.CommentInterface) {
-	return func(c *check.C) (interface{}, check.CommentInterface) {
-		apiclient, err := d.NewClient()
-		assert.NilError(c, err)
+func (d *Daemon) CheckPluginImage(plugin string) func(c *testing.T) (interface{}, string) {
+	return func(c *testing.T) (interface{}, string) {
+		apiclient := d.NewClientT(c)
 		resp, _, err := apiclient.PluginInspectWithRaw(context.Background(), plugin)
 		if client.IsErrNotFound(err) {
-			return false, check.Commentf("%v", err)
+			return false, fmt.Sprintf("%v", err)
 		}
 		assert.NilError(c, err)
-		return resp.PluginReference, check.Commentf("%+v", resp)
+		return resp.PluginReference, fmt.Sprintf("%+v", resp)
 	}
 }
 
 // CheckServiceTasks returns the number of tasks for the specified service
-func (d *Daemon) CheckServiceTasks(service string) func(*check.C) (interface{}, check.CommentInterface) {
-	return func(c *check.C) (interface{}, check.CommentInterface) {
+func (d *Daemon) CheckServiceTasks(service string) func(*testing.T) (interface{}, string) {
+	return func(c *testing.T) (interface{}, string) {
 		tasks := d.GetServiceTasks(c, service)
-		return len(tasks), nil
+		return len(tasks), ""
 	}
 }
 
 // CheckRunningTaskNetworks returns the number of times each network is referenced from a task.
-func (d *Daemon) CheckRunningTaskNetworks(c *check.C) (interface{}, check.CommentInterface) {
-	cli, err := d.NewClient()
-	c.Assert(err, checker.IsNil)
+func (d *Daemon) CheckRunningTaskNetworks(c *testing.T) (interface{}, string) {
+	cli := d.NewClientT(c)
 	defer cli.Close()
 
 	filterArgs := filters.NewArgs()
@@ -114,7 +110,7 @@ func (d *Daemon) CheckRunningTaskNetworks(c *check.C) (interface{}, check.Commen
 	}
 
 	tasks, err := cli.TaskList(context.Background(), options)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	result := make(map[string]int)
 	for _, task := range tasks {
@@ -122,13 +118,12 @@ func (d *Daemon) CheckRunningTaskNetworks(c *check.C) (interface{}, check.Commen
 			result[network.Target]++
 		}
 	}
-	return result, nil
+	return result, ""
 }
 
 // CheckRunningTaskImages returns the times each image is running as a task.
-func (d *Daemon) CheckRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) {
-	cli, err := d.NewClient()
-	c.Assert(err, checker.IsNil)
+func (d *Daemon) CheckRunningTaskImages(c *testing.T) (interface{}, string) {
+	cli := d.NewClientT(c)
 	defer cli.Close()
 
 	filterArgs := filters.NewArgs()
@@ -139,7 +134,7 @@ func (d *Daemon) CheckRunningTaskImages(c *check.C) (interface{}, check.CommentI
 	}
 
 	tasks, err := cli.TaskList(context.Background(), options)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	result := make(map[string]int)
 	for _, task := range tasks {
@@ -147,11 +142,11 @@ func (d *Daemon) CheckRunningTaskImages(c *check.C) (interface{}, check.CommentI
 			result[task.Spec.ContainerSpec.Image]++
 		}
 	}
-	return result, nil
+	return result, ""
 }
 
 // CheckNodeReadyCount returns the number of ready node on the swarm
-func (d *Daemon) CheckNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) {
+func (d *Daemon) CheckNodeReadyCount(c *testing.T) (interface{}, string) {
 	nodes := d.ListNodes(c)
 	var readyCount int
 	for _, node := range nodes {
@@ -159,29 +154,28 @@ func (d *Daemon) CheckNodeReadyCount(c *check.C) (interface{}, check.CommentInte
 			readyCount++
 		}
 	}
-	return readyCount, nil
+	return readyCount, ""
 }
 
 // CheckLocalNodeState returns the current swarm node state
-func (d *Daemon) CheckLocalNodeState(c *check.C) (interface{}, check.CommentInterface) {
+func (d *Daemon) CheckLocalNodeState(c *testing.T) (interface{}, string) {
 	info := d.SwarmInfo(c)
-	return info.LocalNodeState, nil
+	return info.LocalNodeState, ""
 }
 
 // CheckControlAvailable returns the current swarm control available
-func (d *Daemon) CheckControlAvailable(c *check.C) (interface{}, check.CommentInterface) {
+func (d *Daemon) CheckControlAvailable(c *testing.T) (interface{}, string) {
 	info := d.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
-	return info.ControlAvailable, nil
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
+	return info.ControlAvailable, ""
 }
 
 // CheckLeader returns whether there is a leader on the swarm or not
-func (d *Daemon) CheckLeader(c *check.C) (interface{}, check.CommentInterface) {
-	cli, err := d.NewClient()
-	c.Assert(err, checker.IsNil)
+func (d *Daemon) CheckLeader(c *testing.T) (interface{}, string) {
+	cli := d.NewClientT(c)
 	defer cli.Close()
 
-	errList := check.Commentf("could not get node list")
+	errList := "could not get node list"
 
 	ls, err := cli.NodeList(context.Background(), types.NodeListOptions{})
 	if err != nil {
@@ -190,8 +184,30 @@ func (d *Daemon) CheckLeader(c *check.C) (interface{}, check.CommentInterface) {
 
 	for _, node := range ls {
 		if node.ManagerStatus != nil && node.ManagerStatus.Leader {
-			return nil, nil
+			return nil, ""
 		}
 	}
-	return fmt.Errorf("no leader"), check.Commentf("could not find leader")
+	return fmt.Errorf("no leader"), "could not find leader"
+}
+
+// CmdRetryOutOfSequence tries the specified command against the current daemon
+// up to 10 times, retrying if it encounters an "update out of sequence" error.
+func (d *Daemon) CmdRetryOutOfSequence(args ...string) (string, error) {
+	var (
+		output string
+		err    error
+	)
+
+	for i := 0; i < 10; i++ {
+		output, err = d.Cmd(args...)
+		// error, no error, whatever. if we don't have "update out of
+		// sequence", we don't retry, we just return.
+		if !strings.Contains(output, "update out of sequence") {
+			return output, err
+		}
+	}
+
+	// otherwise, once all of our attempts have been exhausted, just return
+	// whatever the last values were.
+	return output, err
 }
diff --git a/integration-cli/daemon_swarm_hack_test.go b/integration-cli/daemon_swarm_hack_test.go
index 7a23e84bfcf58..4152f735f7305 100644
--- a/integration-cli/daemon_swarm_hack_test.go
+++ b/integration-cli/daemon_swarm_hack_test.go
@@ -1,11 +1,12 @@
 package main
 
 import (
+	"testing"
+
 	"github.com/docker/docker/integration-cli/daemon"
-	"github.com/go-check/check"
 )
 
-func (s *DockerSwarmSuite) getDaemon(c *check.C, nodeID string) *daemon.Daemon {
+func (s *DockerSwarmSuite) getDaemon(c *testing.T, nodeID string) *daemon.Daemon {
 	s.daemonsLock.Lock()
 	defer s.daemonsLock.Unlock()
 	for _, d := range s.daemons {
@@ -18,6 +19,6 @@ func (s *DockerSwarmSuite) getDaemon(c *check.C, nodeID string) *daemon.Daemon {
 }
 
 // nodeCmd executes a command on a given node via the normal docker socket
-func (s *DockerSwarmSuite) nodeCmd(c *check.C, id string, args ...string) (string, error) {
+func (s *DockerSwarmSuite) nodeCmd(c *testing.T, id string, args ...string) (string, error) {
 	return s.getDaemon(c, id).Cmd(args...)
 }
diff --git a/integration-cli/docker_api_attach_test.go b/integration-cli/docker_api_attach_test.go
index 26633841dbfae..48bf47e528dce 100644
--- a/integration-cli/docker_api_attach_test.go
+++ b/integration-cli/docker_api_attach_test.go
@@ -4,53 +4,53 @@ import (
 	"bufio"
 	"bytes"
 	"context"
-	"fmt"
 	"io"
 	"net"
 	"net/http"
-	"net/http/httputil"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/pkg/stdcopy"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/request"
+	"github.com/docker/go-connections/sockets"
 	"github.com/pkg/errors"
 	"golang.org/x/net/websocket"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
-func (s *DockerSuite) TestGetContainersAttachWebsocket(c *check.C) {
+func (s *DockerSuite) TestGetContainersAttachWebsocket(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-dit", "busybox", "cat")
 
-	rwc, err := request.SockConn(time.Duration(10*time.Second), daemonHost())
-	c.Assert(err, checker.IsNil)
+	rwc, err := request.SockConn(10*time.Second, request.DaemonHost())
+	assert.NilError(c, err)
 
 	cleanedContainerID := strings.TrimSpace(out)
 	config, err := websocket.NewConfig(
 		"/containers/"+cleanedContainerID+"/attach/ws?stream=1&stdin=1&stdout=1&stderr=1",
 		"http://localhost",
 	)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	ws, err := websocket.NewClient(config, rwc)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer ws.Close()
 
 	expected := []byte("hello")
 	actual := make([]byte, len(expected))
 
-	outChan := make(chan error)
+	outChan := make(chan error, 1)
 	go func() {
 		_, err := io.ReadFull(ws, actual)
 		outChan <- err
 		close(outChan)
 	}()
 
-	inChan := make(chan error)
+	inChan := make(chan error, 1)
 	go func() {
 		_, err := ws.Write(expected)
 		inChan <- err
@@ -59,126 +59,124 @@ func (s *DockerSuite) TestGetContainersAttachWebsocket(c *check.C) {
 
 	select {
 	case err := <-inChan:
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(5 * time.Second):
 		c.Fatal("Timeout writing to ws")
 	}
 
 	select {
 	case err := <-outChan:
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(5 * time.Second):
 		c.Fatal("Timeout reading from ws")
 	}
 
-	c.Assert(actual, checker.DeepEquals, expected, check.Commentf("Websocket didn't return the expected data"))
+	assert.Assert(c, is.DeepEqual(actual, expected), "Websocket didn't return the expected data")
 }
 
 // regression gh14320
-func (s *DockerSuite) TestPostContainersAttachContainerNotFound(c *check.C) {
+func (s *DockerSuite) TestPostContainersAttachContainerNotFound(c *testing.T) {
 	resp, _, err := request.Post("/containers/doesnotexist/attach")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	// connection will shutdown, err should be "persistent connection closed"
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
+	assert.Equal(c, resp.StatusCode, http.StatusNotFound)
 	content, err := request.ReadBody(resp.Body)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	expected := "No such container: doesnotexist\r\n"
-	c.Assert(string(content), checker.Equals, expected)
+	assert.Equal(c, string(content), expected)
 }
 
-func (s *DockerSuite) TestGetContainersWsAttachContainerNotFound(c *check.C) {
+func (s *DockerSuite) TestGetContainersWsAttachContainerNotFound(c *testing.T) {
 	res, body, err := request.Get("/containers/doesnotexist/attach/ws")
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound)
-	c.Assert(err, checker.IsNil)
+	assert.Equal(c, res.StatusCode, http.StatusNotFound)
+	assert.NilError(c, err)
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	expected := "No such container: doesnotexist"
-	c.Assert(getErrorMessage(c, b), checker.Contains, expected)
+	assert.Assert(c, strings.Contains(getErrorMessage(c, b), expected))
 }
 
-func (s *DockerSuite) TestPostContainersAttach(c *check.C) {
+func (s *DockerSuite) TestPostContainersAttach(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
-	expectSuccess := func(conn net.Conn, br *bufio.Reader, stream string, tty bool) {
-		defer conn.Close()
+	expectSuccess := func(wc io.WriteCloser, br *bufio.Reader, stream string, tty bool) {
+		defer wc.Close()
 		expected := []byte("success")
-		_, err := conn.Write(expected)
-		c.Assert(err, checker.IsNil)
+		_, err := wc.Write(expected)
+		assert.NilError(c, err)
 
-		conn.SetReadDeadline(time.Now().Add(time.Second))
 		lenHeader := 0
 		if !tty {
 			lenHeader = 8
 		}
 		actual := make([]byte, len(expected)+lenHeader)
-		_, err = io.ReadFull(br, actual)
-		c.Assert(err, checker.IsNil)
+		_, err = readTimeout(br, actual, time.Second)
+		assert.NilError(c, err)
 		if !tty {
 			fdMap := map[string]byte{
 				"stdin":  0,
 				"stdout": 1,
 				"stderr": 2,
 			}
-			c.Assert(actual[0], checker.Equals, fdMap[stream])
+			assert.Equal(c, actual[0], fdMap[stream])
 		}
-		c.Assert(actual[lenHeader:], checker.DeepEquals, expected, check.Commentf("Attach didn't return the expected data from %s", stream))
+		assert.Assert(c, is.DeepEqual(actual[lenHeader:], expected), "Attach didn't return the expected data from %s", stream)
 	}
 
-	expectTimeout := func(conn net.Conn, br *bufio.Reader, stream string) {
-		defer conn.Close()
-		_, err := conn.Write([]byte{'t'})
-		c.Assert(err, checker.IsNil)
+	expectTimeout := func(wc io.WriteCloser, br *bufio.Reader, stream string) {
+		defer wc.Close()
+		_, err := wc.Write([]byte{'t'})
+		assert.NilError(c, err)
 
-		conn.SetReadDeadline(time.Now().Add(time.Second))
 		actual := make([]byte, 1)
-		_, err = io.ReadFull(br, actual)
-		opErr, ok := err.(*net.OpError)
-		c.Assert(ok, checker.Equals, true, check.Commentf("Error is expected to be *net.OpError, got %v", err))
-		c.Assert(opErr.Timeout(), checker.Equals, true, check.Commentf("Read from %s is expected to timeout", stream))
+		_, err = readTimeout(br, actual, time.Second)
+		assert.Assert(c, err.Error() == "Timeout", "Read from %s is expected to timeout", stream)
 	}
 
 	// Create a container that only emits stdout.
 	cid, _ := dockerCmd(c, "run", "-di", "busybox", "cat")
 	cid = strings.TrimSpace(cid)
+
 	// Attach to the container's stdout stream.
-	conn, br, err := sockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stdout=1", nil, "text/plain", daemonHost())
-	c.Assert(err, checker.IsNil)
+	wc, br, err := requestHijack(http.MethodPost, "/containers/"+cid+"/attach?stream=1&stdin=1&stdout=1", nil, "text/plain", request.DaemonHost())
+	assert.NilError(c, err)
 	// Check if the data from stdout can be received.
-	expectSuccess(conn, br, "stdout", false)
+	expectSuccess(wc, br, "stdout", false)
+
 	// Attach to the container's stderr stream.
-	conn, br, err = sockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stderr=1", nil, "text/plain", daemonHost())
-	c.Assert(err, checker.IsNil)
+	wc, br, err = requestHijack(http.MethodPost, "/containers/"+cid+"/attach?stream=1&stdin=1&stderr=1", nil, "text/plain", request.DaemonHost())
+	assert.NilError(c, err)
 	// Since the container only emits stdout, attaching to stderr should return nothing.
-	expectTimeout(conn, br, "stdout")
+	expectTimeout(wc, br, "stdout")
 
 	// Test the similar functions of the stderr stream.
 	cid, _ = dockerCmd(c, "run", "-di", "busybox", "/bin/sh", "-c", "cat >&2")
 	cid = strings.TrimSpace(cid)
-	conn, br, err = sockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stderr=1", nil, "text/plain", daemonHost())
-	c.Assert(err, checker.IsNil)
-	expectSuccess(conn, br, "stderr", false)
-	conn, br, err = sockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stdout=1", nil, "text/plain", daemonHost())
-	c.Assert(err, checker.IsNil)
-	expectTimeout(conn, br, "stderr")
+	wc, br, err = requestHijack(http.MethodPost, "/containers/"+cid+"/attach?stream=1&stdin=1&stderr=1", nil, "text/plain", request.DaemonHost())
+	assert.NilError(c, err)
+	expectSuccess(wc, br, "stderr", false)
+	wc, br, err = requestHijack(http.MethodPost, "/containers/"+cid+"/attach?stream=1&stdin=1&stdout=1", nil, "text/plain", request.DaemonHost())
+	assert.NilError(c, err)
+	expectTimeout(wc, br, "stderr")
 
 	// Test with tty.
 	cid, _ = dockerCmd(c, "run", "-dit", "busybox", "/bin/sh", "-c", "cat >&2")
 	cid = strings.TrimSpace(cid)
 	// Attach to stdout only.
-	conn, br, err = sockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stdout=1", nil, "text/plain", daemonHost())
-	c.Assert(err, checker.IsNil)
-	expectSuccess(conn, br, "stdout", true)
+	wc, br, err = requestHijack(http.MethodPost, "/containers/"+cid+"/attach?stream=1&stdin=1&stdout=1", nil, "text/plain", request.DaemonHost())
+	assert.NilError(c, err)
+	expectSuccess(wc, br, "stdout", true)
 
 	// Attach without stdout stream.
-	conn, br, err = sockRequestHijack("POST", "/containers/"+cid+"/attach?stream=1&stdin=1&stderr=1", nil, "text/plain", daemonHost())
-	c.Assert(err, checker.IsNil)
+	wc, br, err = requestHijack(http.MethodPost, "/containers/"+cid+"/attach?stream=1&stdin=1&stderr=1", nil, "text/plain", request.DaemonHost())
+	assert.NilError(c, err)
 	// Nothing should be received because both the stdout and stderr of the container will be
 	// sent to the client as stdout when tty is enabled.
-	expectTimeout(conn, br, "stdout")
+	expectTimeout(wc, br, "stdout")
 
 	// Test the client API
-	client, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	client, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer client.Close()
 
 	cid, _ = dockerCmd(c, "run", "-di", "busybox", "/bin/sh", "-c", "echo hello; cat")
@@ -194,60 +192,48 @@ func (s *DockerSuite) TestPostContainersAttach(c *check.C) {
 	}
 
 	resp, err := client.ContainerAttach(context.Background(), cid, attachOpts)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	expectSuccess(resp.Conn, resp.Reader, "stdout", false)
 
 	// Make sure we do see "hello" if Logs is true
 	attachOpts.Logs = true
 	resp, err = client.ContainerAttach(context.Background(), cid, attachOpts)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	defer resp.Conn.Close()
 	resp.Conn.SetReadDeadline(time.Now().Add(time.Second))
 
 	_, err = resp.Conn.Write([]byte("success"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var outBuf, errBuf bytes.Buffer
+	var nErr net.Error
 	_, err = stdcopy.StdCopy(&outBuf, &errBuf, resp.Reader)
-	if err != nil && errors.Cause(err).(net.Error).Timeout() {
+	if errors.As(err, &nErr) && nErr.Timeout() {
 		// ignore the timeout error as it is expected
 		err = nil
 	}
-	c.Assert(err, checker.IsNil)
-	c.Assert(errBuf.String(), checker.Equals, "")
-	c.Assert(outBuf.String(), checker.Equals, "hello\nsuccess")
+	assert.NilError(c, err)
+	assert.Equal(c, errBuf.String(), "")
+	assert.Equal(c, outBuf.String(), "hello\nsuccess")
 }
 
-// SockRequestHijack creates a connection to specified host (with method, contenttype, …) and returns a hijacked connection
-// and the output as a `bufio.Reader`
-func sockRequestHijack(method, endpoint string, data io.Reader, ct string, daemon string, modifiers ...func(*http.Request)) (net.Conn, *bufio.Reader, error) {
-	req, client, err := newRequestClient(method, endpoint, data, ct, daemon, modifiers...)
-	if err != nil {
-		return nil, nil, err
-	}
+// requestHijack create a http requst to specified host with `Upgrade` header (with method
+// , contenttype, …), if receive a successful "101 Switching Protocols" response return
+// a `io.WriteCloser` and `bufio.Reader`
+func requestHijack(method, endpoint string, data io.Reader, ct, daemon string, modifiers ...func(*http.Request)) (io.WriteCloser, *bufio.Reader, error) {
 
-	client.Do(req)
-	conn, br := client.Hijack()
-	return conn, br, nil
-}
-
-// FIXME(vdemeester) httputil.ClientConn is deprecated, use http.Client instead (closer to actual client)
-// Deprecated: Use New instead of NewRequestClient
-// Deprecated: use request.Do (or Get, Delete, Post) instead
-func newRequestClient(method, endpoint string, data io.Reader, ct, daemon string, modifiers ...func(*http.Request)) (*http.Request, *httputil.ClientConn, error) {
-	c, err := request.SockConn(time.Duration(10*time.Second), daemon)
+	hostURL, err := client.ParseHostURL(daemon)
 	if err != nil {
-		return nil, nil, fmt.Errorf("could not dial docker daemon: %v", err)
+		return nil, nil, errors.Wrap(err, "parse daemon host error")
 	}
 
-	client := httputil.NewClientConn(c, nil)
-
 	req, err := http.NewRequest(method, endpoint, data)
 	if err != nil {
-		client.Close()
-		return nil, nil, fmt.Errorf("could not create new request: %v", err)
+		return nil, nil, errors.Wrap(err, "could not create new request")
 	}
+	req.URL.Scheme = "http"
+	req.URL.Host = hostURL.Host
 
 	for _, opt := range modifiers {
 		opt(req)
@@ -256,5 +242,52 @@ func newRequestClient(method, endpoint string, data io.Reader, ct, daemon string
 	if ct != "" {
 		req.Header.Set("Content-Type", ct)
 	}
-	return req, client, nil
+
+	// must have Upgrade header
+	// server api return 101 Switching Protocols
+	req.Header.Set("Upgrade", "tcp")
+
+	// new client
+	// FIXME use testutil/request newHTTPClient
+	transport := &http.Transport{}
+	err = sockets.ConfigureTransport(transport, hostURL.Scheme, hostURL.Host)
+	if err != nil {
+		return nil, nil, errors.Wrap(err, "configure Transport error")
+	}
+
+	client := http.Client{
+		Transport: transport,
+	}
+
+	resp, err := client.Do(req)
+	if err != nil {
+		return nil, nil, errors.Wrap(err, "client.Do")
+	}
+
+	if !bodyIsWritable(resp) {
+		return nil, nil, errors.New("response.Body not writable")
+	}
+
+	return resp.Body.(io.WriteCloser), bufio.NewReader(resp.Body), nil
+}
+
+// bodyIsWritable check Response.Body is writable
+func bodyIsWritable(r *http.Response) bool {
+	_, ok := r.Body.(io.Writer)
+	return ok
+}
+
+// readTimeout read from io.Reader with timeout
+func readTimeout(r io.Reader, buf []byte, timeout time.Duration) (n int, err error) {
+	ch := make(chan bool, 1)
+	go func() {
+		n, err = io.ReadFull(r, buf)
+		ch <- true
+	}()
+	select {
+	case <-ch:
+		return
+	case <-time.After(timeout):
+		return 0, errors.New("Timeout")
+	}
 }
diff --git a/integration-cli/docker_api_build_test.go b/integration-cli/docker_api_build_test.go
index 144acbd046e4e..507ebe624cacc 100644
--- a/integration-cli/docker_api_build_test.go
+++ b/integration-cli/docker_api_build_test.go
@@ -7,23 +7,21 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net/http"
 	"regexp"
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/fakecontext"
-	"github.com/docker/docker/internal/test/fakegit"
-	"github.com/docker/docker/internal/test/fakestorage"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/go-check/check"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
+	"github.com/docker/docker/testutil/fakecontext"
+	"github.com/docker/docker/testutil/fakegit"
+	"github.com/docker/docker/testutil/fakestorage"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
-func (s *DockerSuite) TestBuildAPIDockerFileRemote(c *check.C) {
+func (s *DockerSuite) TestBuildAPIDockerFileRemote(c *testing.T) {
 	testRequires(c, NotUserNamespace)
 
 	var testD string
@@ -41,20 +39,20 @@ RUN find /tmp/`
 	defer server.Close()
 
 	res, body, err := request.Post("/build?dockerfile=baz&remote="+server.URL()+"/testD", request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	buf, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// Make sure Dockerfile exists.
 	// Make sure 'baz' doesn't exist ANYWHERE despite being mentioned in the URL
 	out := string(buf)
-	c.Assert(out, checker.Contains, "RUN find /tmp")
-	c.Assert(out, checker.Not(checker.Contains), "baz")
+	assert.Assert(c, is.Contains(out, "RUN find /tmp"))
+	assert.Assert(c, !strings.Contains(out, "baz"))
 }
 
-func (s *DockerSuite) TestBuildAPIRemoteTarballContext(c *check.C) {
+func (s *DockerSuite) TestBuildAPIRemoteTarballContext(c *testing.T) {
 	buffer := new(bytes.Buffer)
 	tw := tar.NewWriter(buffer)
 	defer tw.Close()
@@ -64,15 +62,11 @@ func (s *DockerSuite) TestBuildAPIRemoteTarballContext(c *check.C) {
 		Name: "Dockerfile",
 		Size: int64(len(dockerfile)),
 	})
-	// failed to write tar file header
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err, "failed to write tar file header")
 
 	_, err = tw.Write(dockerfile)
-	// failed to write tar file content
-	c.Assert(err, checker.IsNil)
-
-	// failed to close tar archive
-	c.Assert(tw.Close(), checker.IsNil)
+	assert.NilError(c, err, "failed to write tar file content")
+	assert.NilError(c, tw.Close(), "failed to close tar archive")
 
 	server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{
 		"testT.tar": buffer,
@@ -80,12 +74,12 @@ func (s *DockerSuite) TestBuildAPIRemoteTarballContext(c *check.C) {
 	defer server.Close()
 
 	res, b, err := request.Post("/build?remote="+server.URL()+"/testT.tar", request.ContentType("application/tar"))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 	b.Close()
 }
 
-func (s *DockerSuite) TestBuildAPIRemoteTarballContextWithCustomDockerfile(c *check.C) {
+func (s *DockerSuite) TestBuildAPIRemoteTarballContextWithCustomDockerfile(c *testing.T) {
 	buffer := new(bytes.Buffer)
 	tw := tar.NewWriter(buffer)
 	defer tw.Close()
@@ -97,11 +91,11 @@ RUN echo 'wrong'`)
 		Size: int64(len(dockerfile)),
 	})
 	// failed to write tar file header
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	_, err = tw.Write(dockerfile)
 	// failed to write tar file content
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	custom := []byte(`FROM busybox
 RUN echo 'right'
@@ -112,14 +106,14 @@ RUN echo 'right'
 	})
 
 	// failed to write tar file header
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	_, err = tw.Write(custom)
 	// failed to write tar file content
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// failed to close tar archive
-	c.Assert(tw.Close(), checker.IsNil)
+	assert.NilError(c, tw.Close())
 
 	server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{
 		"testT.tar": buffer,
@@ -128,18 +122,18 @@ RUN echo 'right'
 
 	url := "/build?dockerfile=custom&remote=" + server.URL() + "/testT.tar"
 	res, body, err := request.Post(url, request.ContentType("application/tar"))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	defer body.Close()
 	content, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// Build used the wrong dockerfile.
-	c.Assert(string(content), checker.Not(checker.Contains), "wrong")
+	assert.Assert(c, !strings.Contains(string(content), "wrong"))
 }
 
-func (s *DockerSuite) TestBuildAPILowerDockerfile(c *check.C) {
+func (s *DockerSuite) TestBuildAPILowerDockerfile(c *testing.T) {
 	git := fakegit.New(c, "repo", map[string]string{
 		"dockerfile": `FROM busybox
 RUN echo from dockerfile`,
@@ -147,17 +141,17 @@ RUN echo from dockerfile`,
 	defer git.Close()
 
 	res, body, err := request.Post("/build?remote="+git.RepoURL, request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	buf, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out := string(buf)
-	c.Assert(out, checker.Contains, "from dockerfile")
+	assert.Assert(c, is.Contains(out, "from dockerfile"))
 }
 
-func (s *DockerSuite) TestBuildAPIBuildGitWithF(c *check.C) {
+func (s *DockerSuite) TestBuildAPIBuildGitWithF(c *testing.T) {
 	git := fakegit.New(c, "repo", map[string]string{
 		"baz": `FROM busybox
 RUN echo from baz`,
@@ -168,17 +162,17 @@ RUN echo from Dockerfile`,
 
 	// Make sure it tries to 'dockerfile' query param value
 	res, body, err := request.Post("/build?dockerfile=baz&remote="+git.RepoURL, request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	buf, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out := string(buf)
-	c.Assert(out, checker.Contains, "from baz")
+	assert.Assert(c, is.Contains(out, "from baz"))
 }
 
-func (s *DockerSuite) TestBuildAPIDoubleDockerfile(c *check.C) {
+func (s *DockerSuite) TestBuildAPIDoubleDockerfile(c *testing.T) {
 	testRequires(c, UnixCli) // dockerfile overwrites Dockerfile on Windows
 	git := fakegit.New(c, "repo", map[string]string{
 		"Dockerfile": `FROM busybox
@@ -190,17 +184,17 @@ RUN echo from dockerfile`,
 
 	// Make sure it tries to 'dockerfile' query param value
 	res, body, err := request.Post("/build?remote="+git.RepoURL, request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	buf, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out := string(buf)
-	c.Assert(out, checker.Contains, "from Dockerfile")
+	assert.Assert(c, is.Contains(out, "from Dockerfile"))
 }
 
-func (s *DockerSuite) TestBuildAPIUnnormalizedTarPaths(c *check.C) {
+func (s *DockerSuite) TestBuildAPIUnnormalizedTarPaths(c *testing.T) {
 	// Make sure that build context tars with entries of the form
 	// x/./y don't cause caching false positives.
 
@@ -215,36 +209,33 @@ func (s *DockerSuite) TestBuildAPIUnnormalizedTarPaths(c *check.C) {
 			Name: "Dockerfile",
 			Size: int64(len(dockerfile)),
 		})
-		//failed to write tar file header
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err, "failed to write tar file header")
 
 		_, err = tw.Write(dockerfile)
-		// failed to write Dockerfile in tar file content
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err, "failed to write Dockerfile in tar file content")
 
 		err = tw.WriteHeader(&tar.Header{
 			Name: "dir/./file",
 			Size: int64(len(fileContents)),
 		})
-		//failed to write tar file header
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err, "failed to write tar file header")
 
 		_, err = tw.Write(fileContents)
-		// failed to write file contents in tar file content
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err, "failed to write file contents in tar file content")
 
-		// failed to close tar archive
-		c.Assert(tw.Close(), checker.IsNil)
+		assert.NilError(c, tw.Close(), "failed to close tar archive")
 
-		res, body, err := request.Post("/build", request.RawContent(ioutil.NopCloser(buffer)), request.ContentType("application/x-tar"))
-		c.Assert(err, checker.IsNil)
-		c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+		res, body, err := request.Post("/build", request.RawContent(io.NopCloser(buffer)), request.ContentType("application/x-tar"))
+		assert.NilError(c, err)
+		assert.Equal(c, res.StatusCode, http.StatusOK)
 
 		out, err := request.ReadBody(body)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		lines := strings.Split(string(out), "\n")
-		c.Assert(len(lines), checker.GreaterThan, 1)
-		c.Assert(lines[len(lines)-2], checker.Matches, ".*Successfully built [0-9a-f]{12}.*")
+		assert.Assert(c, len(lines) > 1)
+		matched, err := regexp.MatchString(".*Successfully built [0-9a-f]{12}.*", lines[len(lines)-2])
+		assert.NilError(c, err)
+		assert.Assert(c, matched)
 
 		re := regexp.MustCompile("Successfully built ([0-9a-f]{12})")
 		matches := re.FindStringSubmatch(lines[len(lines)-2])
@@ -254,10 +245,10 @@ func (s *DockerSuite) TestBuildAPIUnnormalizedTarPaths(c *check.C) {
 	imageA := buildFromTarContext([]byte("abc"))
 	imageB := buildFromTarContext([]byte("def"))
 
-	c.Assert(imageA, checker.Not(checker.Equals), imageB)
+	assert.Assert(c, imageA != imageB)
 }
 
-func (s *DockerSuite) TestBuildOnBuildWithCopy(c *check.C) {
+func (s *DockerSuite) TestBuildOnBuildWithCopy(c *testing.T) {
 	dockerfile := `
 		FROM ` + minimalBaseImage() + ` as onbuildbase
 		ONBUILD COPY file /file
@@ -274,15 +265,15 @@ func (s *DockerSuite) TestBuildOnBuildWithCopy(c *check.C) {
 		"/build",
 		request.RawContent(ctx.AsTarReader(c)),
 		request.ContentType("application/x-tar"))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	out, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(out), checker.Contains, "Successfully built")
+	assert.NilError(c, err)
+	assert.Assert(c, is.Contains(string(out), "Successfully built"))
 }
 
-func (s *DockerSuite) TestBuildOnBuildCache(c *check.C) {
+func (s *DockerSuite) TestBuildOnBuildCache(c *testing.T) {
 	build := func(dockerfile string) []byte {
 		ctx := fakecontext.New(c, "",
 			fakecontext.WithDockerfile(dockerfile),
@@ -298,7 +289,7 @@ func (s *DockerSuite) TestBuildOnBuildCache(c *check.C) {
 
 		out, err := request.ReadBody(body)
 		assert.NilError(c, err)
-		assert.Check(c, is.Contains(string(out), "Successfully built"))
+		assert.Assert(c, is.Contains(string(out), "Successfully built"))
 		return out
 	}
 
@@ -313,7 +304,7 @@ func (s *DockerSuite) TestBuildOnBuildCache(c *check.C) {
 	out := build(dockerfile)
 
 	imageIDs := getImageIDsFromBuild(c, out)
-	assert.Check(c, is.Len(imageIDs, 2))
+	assert.Assert(c, is.Len(imageIDs, 2))
 	parentID, childID := imageIDs[0], imageIDs[1]
 
 	client := testEnv.APIClient()
@@ -324,7 +315,7 @@ func (s *DockerSuite) TestBuildOnBuildCache(c *check.C) {
 	assert.Check(c, is.Equal(parentID, image.Parent))
 }
 
-func (s *DockerRegistrySuite) TestBuildCopyFromForcePull(c *check.C) {
+func (s *DockerRegistrySuite) TestBuildCopyFromForcePull(c *testing.T) {
 	client := testEnv.APIClient()
 
 	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
@@ -334,7 +325,7 @@ func (s *DockerRegistrySuite) TestBuildCopyFromForcePull(c *check.C) {
 	// push the image to the registry
 	rc, err := client.ImagePush(context.TODO(), repoName, types.ImagePushOptions{RegistryAuth: "{}"})
 	assert.Check(c, err)
-	_, err = io.Copy(ioutil.Discard, rc)
+	_, err = io.Copy(io.Discard, rc)
 	assert.Check(c, err)
 
 	dockerfile := fmt.Sprintf(`
@@ -361,7 +352,7 @@ func (s *DockerRegistrySuite) TestBuildCopyFromForcePull(c *check.C) {
 	assert.Check(c, is.Contains(string(out), "Successfully built"))
 }
 
-func (s *DockerSuite) TestBuildAddRemoteNoDecompress(c *check.C) {
+func (s *DockerSuite) TestBuildAddRemoteNoDecompress(c *testing.T) {
 	buffer := new(bytes.Buffer)
 	tw := tar.NewWriter(buffer)
 	dt := []byte("contents")
@@ -405,7 +396,7 @@ func (s *DockerSuite) TestBuildAddRemoteNoDecompress(c *check.C) {
 	assert.Check(c, is.Contains(string(out), "Successfully built"))
 }
 
-func (s *DockerSuite) TestBuildChownOnCopy(c *check.C) {
+func (s *DockerSuite) TestBuildChownOnCopy(c *testing.T) {
 	// new feature added in 1.31 - https://github.com/moby/moby/pull/34263
 	testRequires(c, DaemonIsLinux, MinimumAPIVersion("1.31"))
 	dockerfile := `FROM busybox
@@ -427,15 +418,15 @@ func (s *DockerSuite) TestBuildChownOnCopy(c *check.C) {
 		"/build",
 		request.RawContent(ctx.AsTarReader(c)),
 		request.ContentType("application/x-tar"))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	out, err := request.ReadBody(body)
 	assert.NilError(c, err)
 	assert.Check(c, is.Contains(string(out), "Successfully built"))
 }
 
-func (s *DockerSuite) TestBuildCopyCacheOnFileChange(c *check.C) {
+func (s *DockerSuite) TestBuildCopyCacheOnFileChange(c *testing.T) {
 
 	dockerfile := `FROM busybox
 COPY file /file`
@@ -457,8 +448,10 @@ COPY file /file`
 
 		out, err := request.ReadBody(body)
 		assert.NilError(c, err)
+		assert.Assert(c, is.Contains(string(out), "Successfully built"))
 
 		ids := getImageIDsFromBuild(c, out)
+		assert.Assert(c, is.Len(ids, 1))
 		return ids[len(ids)-1]
 	}
 
@@ -474,7 +467,7 @@ COPY file /file`
 	}
 }
 
-func (s *DockerSuite) TestBuildAddCacheOnFileChange(c *check.C) {
+func (s *DockerSuite) TestBuildAddCacheOnFileChange(c *testing.T) {
 
 	dockerfile := `FROM busybox
 ADD file /file`
@@ -496,8 +489,10 @@ ADD file /file`
 
 		out, err := request.ReadBody(body)
 		assert.NilError(c, err)
+		assert.Assert(c, is.Contains(string(out), "Successfully built"))
 
 		ids := getImageIDsFromBuild(c, out)
+		assert.Assert(c, is.Len(ids, 1))
 		return ids[len(ids)-1]
 	}
 
@@ -513,7 +508,7 @@ ADD file /file`
 	}
 }
 
-func (s *DockerSuite) TestBuildScratchCopy(c *check.C) {
+func (s *DockerSuite) TestBuildScratchCopy(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerfile := `FROM scratch
 ADD Dockerfile /
@@ -527,8 +522,8 @@ ENV foo bar`
 		"/build",
 		request.RawContent(ctx.AsTarReader(c)),
 		request.ContentType("application/x-tar"))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	out, err := request.ReadBody(body)
 	assert.NilError(c, err)
@@ -542,7 +537,7 @@ type buildLine struct {
 	}
 }
 
-func getImageIDsFromBuild(c *check.C, output []byte) []string {
+func getImageIDsFromBuild(c *testing.T, output []byte) []string {
 	var ids []string
 	for _, line := range bytes.Split(output, []byte("\n")) {
 		if len(line) == 0 {
diff --git a/integration-cli/docker_api_build_windows_test.go b/integration-cli/docker_api_build_windows_test.go
index a605c5be39225..68e19ca036693 100644
--- a/integration-cli/docker_api_build_windows_test.go
+++ b/integration-cli/docker_api_build_windows_test.go
@@ -1,19 +1,19 @@
+//go:build windows
 // +build windows
 
 package main
 
 import (
 	"net/http"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/fakecontext"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/go-check/check"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
+	"github.com/docker/docker/testutil/fakecontext"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
-func (s *DockerSuite) TestBuildWithRecycleBin(c *check.C) {
+func (s *DockerSuite) TestBuildWithRecycleBin(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 
 	dockerfile := "" +
@@ -30,8 +30,8 @@ func (s *DockerSuite) TestBuildWithRecycleBin(c *check.C) {
 		request.RawContent(ctx.AsTarReader(c)),
 		request.ContentType("application/x-tar"))
 
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	out, err := request.ReadBody(body)
 	assert.NilError(c, err)
diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go
index ef67894106f82..75af8fe2d2257 100644
--- a/integration-cli/docker_api_containers_test.go
+++ b/integration-cli/docker_api_containers_test.go
@@ -7,14 +7,13 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net/http"
 	"os"
 	"path/filepath"
 	"regexp"
 	"runtime"
-	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
@@ -23,55 +22,52 @@ import (
 	networktypes "github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/pkg/ioutils"
-	"github.com/docker/docker/pkg/mount"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/docker/docker/testutil/request"
 	"github.com/docker/docker/volume"
 	"github.com/docker/go-connections/nat"
-	"github.com/go-check/check"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
 )
 
-func (s *DockerSuite) TestContainerAPIGetAll(c *check.C) {
+func (s *DockerSuite) TestContainerAPIGetAll(c *testing.T) {
 	startCount := getContainerCount(c)
 	name := "getall"
 	dockerCmd(c, "run", "--name", name, "busybox", "true")
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	options := types.ContainerListOptions{
 		All: true,
 	}
 	containers, err := cli.ContainerList(context.Background(), options)
-	c.Assert(err, checker.IsNil)
-	c.Assert(containers, checker.HasLen, startCount+1)
+	assert.NilError(c, err)
+	assert.Equal(c, len(containers), startCount+1)
 	actual := containers[0].Names[0]
-	c.Assert(actual, checker.Equals, "/"+name)
+	assert.Equal(c, actual, "/"+name)
 }
 
 // regression test for empty json field being omitted #13691
-func (s *DockerSuite) TestContainerAPIGetJSONNoFieldsOmitted(c *check.C) {
+func (s *DockerSuite) TestContainerAPIGetJSONNoFieldsOmitted(c *testing.T) {
 	startCount := getContainerCount(c)
 	dockerCmd(c, "run", "busybox", "true")
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	options := types.ContainerListOptions{
 		All: true,
 	}
 	containers, err := cli.ContainerList(context.Background(), options)
-	c.Assert(err, checker.IsNil)
-	c.Assert(containers, checker.HasLen, startCount+1)
+	assert.NilError(c, err)
+	assert.Equal(c, len(containers), startCount+1)
 	actual := fmt.Sprintf("%+v", containers[0])
 
 	// empty Labels field triggered this bug, make sense to check for everything
@@ -98,57 +94,18 @@ func (s *DockerSuite) TestContainerAPIGetJSONNoFieldsOmitted(c *check.C) {
 	}
 }
 
-type containerPs struct {
-	Names []string
-	Ports []types.Port
-}
-
-// regression test for non-empty fields from #13901
-func (s *DockerSuite) TestContainerAPIPsOmitFields(c *check.C) {
-	// Problematic for Windows porting due to networking not yet being passed back
-	testRequires(c, DaemonIsLinux)
-	name := "pstest"
-	port := 80
-	runSleepingContainer(c, "--name", name, "--expose", strconv.Itoa(port))
-
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
-	defer cli.Close()
-
-	options := types.ContainerListOptions{
-		All: true,
-	}
-	containers, err := cli.ContainerList(context.Background(), options)
-	c.Assert(err, checker.IsNil)
-	var foundContainer containerPs
-	for _, c := range containers {
-		for _, testName := range c.Names {
-			if "/"+name == testName {
-				foundContainer.Names = c.Names
-				foundContainer.Ports = c.Ports
-				break
-			}
-		}
-	}
-
-	c.Assert(foundContainer.Ports, checker.HasLen, 1)
-	c.Assert(foundContainer.Ports[0].PrivatePort, checker.Equals, uint16(port))
-	c.Assert(foundContainer.Ports[0].PublicPort, checker.NotNil)
-	c.Assert(foundContainer.Ports[0].IP, checker.NotNil)
-}
-
-func (s *DockerSuite) TestContainerAPIGetExport(c *check.C) {
+func (s *DockerSuite) TestContainerAPIGetExport(c *testing.T) {
 	// Not supported on Windows as Windows does not support docker export
 	testRequires(c, DaemonIsLinux)
 	name := "exportcontainer"
 	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test")
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	body, err := cli.ContainerExport(context.Background(), name)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer body.Close()
 	found := false
 	for tarReader := tar.NewReader(body); ; {
@@ -161,21 +118,21 @@ func (s *DockerSuite) TestContainerAPIGetExport(c *check.C) {
 			break
 		}
 	}
-	c.Assert(found, checker.True, check.Commentf("The created test file has not been found in the exported image"))
+	assert.Assert(c, found, "The created test file has not been found in the exported image")
 }
 
-func (s *DockerSuite) TestContainerAPIGetChanges(c *check.C) {
+func (s *DockerSuite) TestContainerAPIGetChanges(c *testing.T) {
 	// Not supported on Windows as Windows does not support docker diff (/containers/name/changes)
 	testRequires(c, DaemonIsLinux)
 	name := "changescontainer"
 	dockerCmd(c, "run", "--name", name, "busybox", "rm", "/etc/passwd")
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	changes, err := cli.ContainerDiff(context.Background(), name)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// Check the changelog for removal of /etc/passwd
 	success := false
@@ -184,10 +141,10 @@ func (s *DockerSuite) TestContainerAPIGetChanges(c *check.C) {
 			success = true
 		}
 	}
-	c.Assert(success, checker.True, check.Commentf("/etc/passwd has been removed but is not present in the diff"))
+	assert.Assert(c, success, "/etc/passwd has been removed but is not present in the diff")
 }
 
-func (s *DockerSuite) TestGetContainerStats(c *check.C) {
+func (s *DockerSuite) TestGetContainerStats(c *testing.T) {
 	var (
 		name = "statscontainer"
 	)
@@ -200,12 +157,12 @@ func (s *DockerSuite) TestGetContainerStats(c *check.C) {
 
 	bc := make(chan b, 1)
 	go func() {
-		cli, err := client.NewEnvClient()
-		c.Assert(err, checker.IsNil)
+		cli, err := client.NewClientWithOpts(client.FromEnv)
+		assert.NilError(c, err)
 		defer cli.Close()
 
 		stats, err := cli.ContainerStats(context.Background(), name, true)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		bc <- b{stats, err}
 	}()
 
@@ -223,23 +180,23 @@ func (s *DockerSuite) TestGetContainerStats(c *check.C) {
 		defer sr.stats.Body.Close()
 		var s *types.Stats
 		// decode only one object from the stream
-		c.Assert(dec.Decode(&s), checker.IsNil)
+		assert.NilError(c, dec.Decode(&s))
 	}
 }
 
-func (s *DockerSuite) TestGetContainerStatsRmRunning(c *check.C) {
+func (s *DockerSuite) TestGetContainerStatsRmRunning(c *testing.T) {
 	out := runSleepingContainer(c)
 	id := strings.TrimSpace(out)
 
 	buf := &ChannelBuffer{C: make(chan []byte, 1)}
 	defer buf.Close()
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	stats, err := cli.ContainerStats(context.Background(), id, true)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer stats.Body.Close()
 
 	chErr := make(chan error, 1)
@@ -251,16 +208,16 @@ func (s *DockerSuite) TestGetContainerStatsRmRunning(c *check.C) {
 	b := make([]byte, 32)
 	// make sure we've got some stats
 	_, err = buf.ReadTimeout(b, 2*time.Second)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// Now remove without `-f` and make sure we are still pulling stats
 	_, _, err = dockerCmdWithError("rm", id)
-	c.Assert(err, checker.Not(checker.IsNil), check.Commentf("rm should have failed but didn't"))
+	assert.Assert(c, err != nil, "rm should have failed but didn't")
 	_, err = buf.ReadTimeout(b, 2*time.Second)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "rm", "-f", id)
-	c.Assert(<-chErr, checker.IsNil)
+	assert.Assert(c, <-chErr == nil)
 }
 
 // ChannelBuffer holds a chan of byte array that can be populate in a goroutine.
@@ -294,7 +251,7 @@ func (c *ChannelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) {
 // regression test for gh13421
 // previous test was just checking one stat entry so it didn't fail (stats with
 // stream false always return one stat)
-func (s *DockerSuite) TestGetContainerStatsStream(c *check.C) {
+func (s *DockerSuite) TestGetContainerStatsStream(c *testing.T) {
 	name := "statscontainer"
 	runSleepingContainer(c, "--name", name)
 
@@ -305,12 +262,12 @@ func (s *DockerSuite) TestGetContainerStatsStream(c *check.C) {
 
 	bc := make(chan b, 1)
 	go func() {
-		cli, err := client.NewEnvClient()
-		c.Assert(err, checker.IsNil)
+		cli, err := client.NewClientWithOpts(client.FromEnv)
+		assert.NilError(c, err)
 		defer cli.Close()
 
 		stats, err := cli.ContainerStats(context.Background(), name, true)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		bc <- b{stats, err}
 	}()
 
@@ -324,9 +281,9 @@ func (s *DockerSuite) TestGetContainerStatsStream(c *check.C) {
 	case <-time.After(2 * time.Second):
 		c.Fatal("stream was not closed after container was removed")
 	case sr := <-bc:
-		b, err := ioutil.ReadAll(sr.stats.Body)
+		b, err := io.ReadAll(sr.stats.Body)
 		defer sr.stats.Body.Close()
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		s := string(b)
 		// count occurrences of "read" of types.Stats
 		if l := strings.Count(s, "read"); l < 2 {
@@ -335,7 +292,7 @@ func (s *DockerSuite) TestGetContainerStatsStream(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestGetContainerStatsNoStream(c *check.C) {
+func (s *DockerSuite) TestGetContainerStatsNoStream(c *testing.T) {
 	name := "statscontainer"
 	runSleepingContainer(c, "--name", name)
 
@@ -347,12 +304,12 @@ func (s *DockerSuite) TestGetContainerStatsNoStream(c *check.C) {
 	bc := make(chan b, 1)
 
 	go func() {
-		cli, err := client.NewEnvClient()
-		c.Assert(err, checker.IsNil)
+		cli, err := client.NewClientWithOpts(client.FromEnv)
+		assert.NilError(c, err)
 		defer cli.Close()
 
 		stats, err := cli.ContainerStats(context.Background(), name, false)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		bc <- b{stats, err}
 	}()
 
@@ -366,58 +323,59 @@ func (s *DockerSuite) TestGetContainerStatsNoStream(c *check.C) {
 	case <-time.After(2 * time.Second):
 		c.Fatal("stream was not closed after container was removed")
 	case sr := <-bc:
-		b, err := ioutil.ReadAll(sr.stats.Body)
+		b, err := io.ReadAll(sr.stats.Body)
 		defer sr.stats.Body.Close()
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		s := string(b)
 		// count occurrences of `"read"` of types.Stats
-		c.Assert(strings.Count(s, `"read"`), checker.Equals, 1, check.Commentf("Expected only one stat streamed, got %d", strings.Count(s, `"read"`)))
+		assert.Assert(c, strings.Count(s, `"read"`) == 1, "Expected only one stat streamed, got %d", strings.Count(s, `"read"`))
 	}
 }
 
-func (s *DockerSuite) TestGetStoppedContainerStats(c *check.C) {
+func (s *DockerSuite) TestGetStoppedContainerStats(c *testing.T) {
 	name := "statscontainer"
 	dockerCmd(c, "create", "--name", name, "busybox", "ps")
 
-	chResp := make(chan error)
+	chResp := make(chan error, 1)
 
 	// We expect an immediate response, but if it's not immediate, the test would hang, so put it in a goroutine
 	// below we'll check this on a timeout.
 	go func() {
-		cli, err := client.NewEnvClient()
-		c.Assert(err, checker.IsNil)
+		cli, err := client.NewClientWithOpts(client.FromEnv)
+		assert.NilError(c, err)
 		defer cli.Close()
 
 		resp, err := cli.ContainerStats(context.Background(), name, false)
+		assert.NilError(c, err)
 		defer resp.Body.Close()
 		chResp <- err
 	}()
 
 	select {
 	case err := <-chResp:
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(10 * time.Second):
 		c.Fatal("timeout waiting for stats response for stopped container")
 	}
 }
 
-func (s *DockerSuite) TestContainerAPIPause(c *check.C) {
+func (s *DockerSuite) TestContainerAPIPause(c *testing.T) {
 	// Problematic on Windows as Windows does not support pause
 	testRequires(c, DaemonIsLinux)
 
-	getPaused := func(c *check.C) []string {
+	getPaused := func(c *testing.T) []string {
 		return strings.Fields(cli.DockerCmd(c, "ps", "-f", "status=paused", "-q", "-a").Combined())
 	}
 
 	out := cli.DockerCmd(c, "run", "-d", "busybox", "sleep", "30").Combined()
 	ContainerID := strings.TrimSpace(out)
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerPause(context.Background(), ContainerID)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	pausedContainers := getPaused(c)
 
@@ -426,53 +384,53 @@ func (s *DockerSuite) TestContainerAPIPause(c *check.C) {
 	}
 
 	err = cli.ContainerUnpause(context.Background(), ContainerID)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	pausedContainers = getPaused(c)
-	c.Assert(pausedContainers, checker.HasLen, 0, check.Commentf("There should be no paused container."))
+	assert.Equal(c, len(pausedContainers), 0, "There should be no paused container.")
 }
 
-func (s *DockerSuite) TestContainerAPITop(c *check.C) {
+func (s *DockerSuite) TestContainerAPITop(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
-	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "top")
-	id := strings.TrimSpace(string(out))
-	c.Assert(waitRun(id), checker.IsNil)
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "top && true")
+	id := strings.TrimSpace(out)
+	assert.NilError(c, waitRun(id))
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	// sort by comm[andline] to make sure order stays the same in case of PID rollover
 	top, err := cli.ContainerTop(context.Background(), id, []string{"aux", "--sort=comm"})
-	c.Assert(err, checker.IsNil)
-	c.Assert(top.Titles, checker.HasLen, 11, check.Commentf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles))
+	assert.NilError(c, err)
+	assert.Equal(c, len(top.Titles), 11, fmt.Sprintf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles))
 
 	if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" {
 		c.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles)
 	}
-	c.Assert(top.Processes, checker.HasLen, 2, check.Commentf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes))
-	c.Assert(top.Processes[0][10], checker.Equals, "/bin/sh -c top")
-	c.Assert(top.Processes[1][10], checker.Equals, "top")
+	assert.Equal(c, len(top.Processes), 2, fmt.Sprintf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes))
+	assert.Equal(c, top.Processes[0][10], "/bin/sh -c top && true")
+	assert.Equal(c, top.Processes[1][10], "top")
 }
 
-func (s *DockerSuite) TestContainerAPITopWindows(c *check.C) {
+func (s *DockerSuite) TestContainerAPITopWindows(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	out := runSleepingContainer(c, "-d")
-	id := strings.TrimSpace(string(out))
-	c.Assert(waitRun(id), checker.IsNil)
+	id := strings.TrimSpace(out)
+	assert.NilError(c, waitRun(id))
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	top, err := cli.ContainerTop(context.Background(), id, nil)
-	c.Assert(err, checker.IsNil)
-	c.Assert(top.Titles, checker.HasLen, 4, check.Commentf("expected 4 titles, found %d: %v", len(top.Titles), top.Titles))
+	assert.NilError(c, err)
+	assert.Equal(c, len(top.Titles), 4, "expected 4 titles, found %d: %v", len(top.Titles), top.Titles)
 
 	if top.Titles[0] != "Name" || top.Titles[3] != "Private Working Set" {
 		c.Fatalf("expected `Name` at `Titles[0]` and `Private Working Set` at Titles[3]: %v", top.Titles)
 	}
-	c.Assert(len(top.Processes), checker.GreaterOrEqualThan, 2, check.Commentf("expected at least 2 processes, found %d: %v", len(top.Processes), top.Processes))
+	assert.Assert(c, len(top.Processes) >= 2, "expected at least 2 processes, found %d: %v", len(top.Processes), top.Processes)
 
 	foundProcess := false
 	expectedProcess := "busybox.exe"
@@ -483,15 +441,15 @@ func (s *DockerSuite) TestContainerAPITopWindows(c *check.C) {
 		}
 	}
 
-	c.Assert(foundProcess, checker.Equals, true, check.Commentf("expected to find %s: %v", expectedProcess, top.Processes))
+	assert.Assert(c, foundProcess, "expected to find %s: %v", expectedProcess, top.Processes)
 }
 
-func (s *DockerSuite) TestContainerAPICommit(c *check.C) {
+func (s *DockerSuite) TestContainerAPICommit(c *testing.T) {
 	cName := "testapicommit"
 	dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test")
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	options := types.ContainerCommitOptions{
@@ -499,21 +457,21 @@ func (s *DockerSuite) TestContainerAPICommit(c *check.C) {
 	}
 
 	img, err := cli.ContainerCommit(context.Background(), cName, options)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	cmd := inspectField(c, img.ID, "Config.Cmd")
-	c.Assert(cmd, checker.Equals, "[/bin/sh -c touch /test]", check.Commentf("got wrong Cmd from commit: %q", cmd))
+	assert.Equal(c, cmd, "[/bin/sh -c touch /test]", fmt.Sprintf("got wrong Cmd from commit: %q", cmd))
 
 	// sanity check, make sure the image is what we think it is
 	dockerCmd(c, "run", img.ID, "ls", "/test")
 }
 
-func (s *DockerSuite) TestContainerAPICommitWithLabelInConfig(c *check.C) {
+func (s *DockerSuite) TestContainerAPICommitWithLabelInConfig(c *testing.T) {
 	cName := "testapicommitwithconfig"
 	dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test")
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	config := containertypes.Config{
@@ -525,22 +483,22 @@ func (s *DockerSuite) TestContainerAPICommitWithLabelInConfig(c *check.C) {
 	}
 
 	img, err := cli.ContainerCommit(context.Background(), cName, options)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	label1 := inspectFieldMap(c, img.ID, "Config.Labels", "key1")
-	c.Assert(label1, checker.Equals, "value1")
+	assert.Equal(c, label1, "value1")
 
 	label2 := inspectFieldMap(c, img.ID, "Config.Labels", "key2")
-	c.Assert(label2, checker.Equals, "value2")
+	assert.Equal(c, label2, "value2")
 
 	cmd := inspectField(c, img.ID, "Config.Cmd")
-	c.Assert(cmd, checker.Equals, "[/bin/sh -c touch /test]", check.Commentf("got wrong Cmd from commit: %q", cmd))
+	assert.Equal(c, cmd, "[/bin/sh -c touch /test]", fmt.Sprintf("got wrong Cmd from commit: %q", cmd))
 
 	// sanity check, make sure the image is what we think it is
 	dockerCmd(c, "run", img.ID, "ls", "/test")
 }
 
-func (s *DockerSuite) TestContainerAPIBadPort(c *check.C) {
+func (s *DockerSuite) TestContainerAPIBadPort(c *testing.T) {
 	// TODO Windows to Windows CI - Port this test
 	testRequires(c, DaemonIsLinux)
 
@@ -559,44 +517,44 @@ func (s *DockerSuite) TestContainerAPIBadPort(c *check.C) {
 		},
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err.Error(), checker.Contains, `invalid port specification: "aa80"`)
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+	assert.ErrorContains(c, err, `invalid port specification: "aa80"`)
 }
 
-func (s *DockerSuite) TestContainerAPICreate(c *check.C) {
+func (s *DockerSuite) TestContainerAPICreate(c *testing.T) {
 	config := containertypes.Config{
 		Image: "busybox",
 		Cmd:   []string{"/bin/sh", "-c", "touch /test && ls /test"},
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err, checker.IsNil)
+	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	assert.NilError(c, err)
 
 	out, _ := dockerCmd(c, "start", "-a", container.ID)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "/test")
+	assert.Equal(c, strings.TrimSpace(out), "/test")
 }
 
-func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *check.C) {
+func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *testing.T) {
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
+	_, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
 
 	expected := "No command specified"
-	c.Assert(err.Error(), checker.Contains, expected)
+	assert.ErrorContains(c, err, expected)
 }
 
-func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *check.C) {
+func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *testing.T) {
 	// Container creation must fail if client specified configurations for more than one network
 	config := containertypes.Config{
 		Image: "busybox",
@@ -610,56 +568,33 @@ func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *check.C) {
 		},
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, "")
+	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, nil, "")
 	msg := err.Error()
 	// network name order in error message is not deterministic
-	c.Assert(msg, checker.Contains, "Container cannot be connected to network endpoints")
-	c.Assert(msg, checker.Contains, "net1")
-	c.Assert(msg, checker.Contains, "net2")
-	c.Assert(msg, checker.Contains, "net3")
-}
-
-func (s *DockerSuite) TestContainerAPICreateWithHostName(c *check.C) {
-	domainName := "test-domain"
-	hostName := "test-hostname"
-	config := containertypes.Config{
-		Image:      "busybox",
-		Hostname:   hostName,
-		Domainname: domainName,
-	}
-
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
-	defer cli.Close()
-
-	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err, checker.IsNil)
-
-	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
-	c.Assert(err, checker.IsNil)
-
-	c.Assert(containerJSON.Config.Hostname, checker.Equals, hostName, check.Commentf("Mismatched Hostname"))
-	c.Assert(containerJSON.Config.Domainname, checker.Equals, domainName, check.Commentf("Mismatched Domainname"))
+	assert.Assert(c, strings.Contains(msg, "Container cannot be connected to network endpoints"))
+	assert.Assert(c, strings.Contains(msg, "net1"))
+	assert.Assert(c, strings.Contains(msg, "net2"))
+	assert.Assert(c, strings.Contains(msg, "net3"))
 }
 
-func (s *DockerSuite) TestContainerAPICreateBridgeNetworkMode(c *check.C) {
+func (s *DockerSuite) TestContainerAPICreateBridgeNetworkMode(c *testing.T) {
 	// Windows does not support bridge
 	testRequires(c, DaemonIsLinux)
 	UtilCreateNetworkMode(c, "bridge")
 }
 
-func (s *DockerSuite) TestContainerAPICreateOtherNetworkModes(c *check.C) {
+func (s *DockerSuite) TestContainerAPICreateOtherNetworkModes(c *testing.T) {
 	// Windows does not support these network modes
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	UtilCreateNetworkMode(c, "host")
 	UtilCreateNetworkMode(c, "container:web1")
 }
 
-func UtilCreateNetworkMode(c *check.C, networkMode containertypes.NetworkMode) {
+func UtilCreateNetworkMode(c *testing.T, networkMode containertypes.NetworkMode) {
 	config := containertypes.Config{
 		Image: "busybox",
 	}
@@ -668,20 +603,20 @@ func UtilCreateNetworkMode(c *check.C, networkMode containertypes.NetworkMode) {
 		NetworkMode: networkMode,
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err, checker.IsNil)
+	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(containerJSON.HostConfig.NetworkMode, checker.Equals, containertypes.NetworkMode(networkMode), check.Commentf("Mismatched NetworkMode"))
+	assert.Equal(c, containerJSON.HostConfig.NetworkMode, networkMode, "Mismatched NetworkMode")
 }
 
-func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *check.C) {
+func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *testing.T) {
 	// TODO Windows to Windows CI. The CpuShares part could be ported.
 	testRequires(c, DaemonIsLinux)
 	config := containertypes.Config{
@@ -695,65 +630,65 @@ func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *check.C) {
 		},
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err, checker.IsNil)
+	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out := inspectField(c, containerJSON.ID, "HostConfig.CpuShares")
-	c.Assert(out, checker.Equals, "512")
+	assert.Equal(c, out, "512")
 
 	outCpuset := inspectField(c, containerJSON.ID, "HostConfig.CpusetCpus")
-	c.Assert(outCpuset, checker.Equals, "0")
+	assert.Equal(c, outCpuset, "0")
 }
 
-func (s *DockerSuite) TestContainerAPIVerifyHeader(c *check.C) {
+func (s *DockerSuite) TestContainerAPIVerifyHeader(c *testing.T) {
 	config := map[string]interface{}{
 		"Image": "busybox",
 	}
 
 	create := func(ct string) (*http.Response, io.ReadCloser, error) {
 		jsonData := bytes.NewBuffer(nil)
-		c.Assert(json.NewEncoder(jsonData).Encode(config), checker.IsNil)
-		return request.Post("/containers/create", request.RawContent(ioutil.NopCloser(jsonData)), request.ContentType(ct))
+		assert.Assert(c, json.NewEncoder(jsonData).Encode(config) == nil)
+		return request.Post("/containers/create", request.RawContent(io.NopCloser(jsonData)), request.ContentType(ct))
 	}
 
 	// Try with no content-type
 	res, body, err := create("")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	// todo: we need to figure out a better way to compare between dockerd versions
 	// comparing between daemon API version is not precise.
 	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	} else {
-		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
+		assert.Assert(c, res.StatusCode != http.StatusOK)
 	}
 	body.Close()
 
 	// Try with wrong content-type
 	res, body, err = create("application/xml")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	} else {
-		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
+		assert.Assert(c, res.StatusCode != http.StatusOK)
 	}
 	body.Close()
 
 	// now application/json
 	res, body, err = create("application/json")
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusCreated)
 	body.Close()
 }
 
-//Issue 14230. daemon should return 500 for invalid port syntax
-func (s *DockerSuite) TestContainerAPIInvalidPortSyntax(c *check.C) {
+// Issue 14230. daemon should return 500 for invalid port syntax
+func (s *DockerSuite) TestContainerAPIInvalidPortSyntax(c *testing.T) {
 	config := `{
 				  "Image": "busybox",
 				  "HostConfig": {
@@ -767,19 +702,19 @@ func (s *DockerSuite) TestContainerAPIInvalidPortSyntax(c *check.C) {
 				}`
 
 	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	} else {
-		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
+		assert.Assert(c, res.StatusCode != http.StatusOK)
 	}
 
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b[:]), checker.Contains, "invalid port")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(b[:]), "invalid port"))
 }
 
-func (s *DockerSuite) TestContainerAPIRestartPolicyInvalidPolicyName(c *check.C) {
+func (s *DockerSuite) TestContainerAPIRestartPolicyInvalidPolicyName(c *testing.T) {
 	config := `{
 		"Image": "busybox",
 		"HostConfig": {
@@ -791,19 +726,19 @@ func (s *DockerSuite) TestContainerAPIRestartPolicyInvalidPolicyName(c *check.C)
 	}`
 
 	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	} else {
-		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
+		assert.Assert(c, res.StatusCode != http.StatusOK)
 	}
 
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b[:]), checker.Contains, "invalid restart policy")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(b[:]), "invalid restart policy"))
 }
 
-func (s *DockerSuite) TestContainerAPIRestartPolicyRetryMismatch(c *check.C) {
+func (s *DockerSuite) TestContainerAPIRestartPolicyRetryMismatch(c *testing.T) {
 	config := `{
 		"Image": "busybox",
 		"HostConfig": {
@@ -815,19 +750,19 @@ func (s *DockerSuite) TestContainerAPIRestartPolicyRetryMismatch(c *check.C) {
 	}`
 
 	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	} else {
-		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
+		assert.Assert(c, res.StatusCode != http.StatusOK)
 	}
 
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b[:]), checker.Contains, "maximum retry count cannot be used with restart policy")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(b[:]), "maximum retry count cannot be used with restart policy"))
 }
 
-func (s *DockerSuite) TestContainerAPIRestartPolicyNegativeRetryCount(c *check.C) {
+func (s *DockerSuite) TestContainerAPIRestartPolicyNegativeRetryCount(c *testing.T) {
 	config := `{
 		"Image": "busybox",
 		"HostConfig": {
@@ -839,19 +774,19 @@ func (s *DockerSuite) TestContainerAPIRestartPolicyNegativeRetryCount(c *check.C
 	}`
 
 	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	} else {
-		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
+		assert.Assert(c, res.StatusCode != http.StatusOK)
 	}
 
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b[:]), checker.Contains, "maximum retry count cannot be negative")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(b[:]), "maximum retry count cannot be negative"))
 }
 
-func (s *DockerSuite) TestContainerAPIRestartPolicyDefaultRetryCount(c *check.C) {
+func (s *DockerSuite) TestContainerAPIRestartPolicyDefaultRetryCount(c *testing.T) {
 	config := `{
 		"Image": "busybox",
 		"HostConfig": {
@@ -863,13 +798,13 @@ func (s *DockerSuite) TestContainerAPIRestartPolicyDefaultRetryCount(c *check.C)
 	}`
 
 	res, _, err := request.Post("/containers/create", request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusCreated)
 }
 
 // Issue 7941 - test to make sure a "null" in JSON is just ignored.
 // W/o this fix a null in JSON would be parsed into a string var as "null"
-func (s *DockerSuite) TestContainerAPIPostCreateNull(c *check.C) {
+func (s *DockerSuite) TestContainerAPIPostCreateNull(c *testing.T) {
 	config := `{
 		"Hostname":"",
 		"Domainname":"",
@@ -894,26 +829,26 @@ func (s *DockerSuite) TestContainerAPIPostCreateNull(c *check.C) {
 		"OnBuild":null}`
 
 	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusCreated)
 
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	type createResp struct {
 		ID string
 	}
 	var container createResp
-	c.Assert(json.Unmarshal(b, &container), checker.IsNil)
+	assert.Assert(c, json.Unmarshal(b, &container) == nil)
 	out := inspectField(c, container.ID, "HostConfig.CpusetCpus")
-	c.Assert(out, checker.Equals, "")
+	assert.Equal(c, out, "")
 
 	outMemory := inspectField(c, container.ID, "HostConfig.Memory")
-	c.Assert(outMemory, checker.Equals, "0")
+	assert.Equal(c, outMemory, "0")
 	outMemorySwap := inspectField(c, container.ID, "HostConfig.MemorySwap")
-	c.Assert(outMemorySwap, checker.Equals, "0")
+	assert.Equal(c, outMemorySwap, "0")
 }
 
-func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) {
+func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *testing.T) {
 	// TODO Windows: Port once memory is supported
 	testRequires(c, DaemonIsLinux)
 	config := `{
@@ -925,81 +860,81 @@ func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) {
 	}`
 
 	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	b, err2 := request.ReadBody(body)
-	c.Assert(err2, checker.IsNil)
+	assert.Assert(c, err2 == nil)
 
 	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	} else {
-		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
+		assert.Assert(c, res.StatusCode != http.StatusOK)
 	}
-	c.Assert(string(b), checker.Contains, "Minimum memory limit allowed is 4MB")
+	assert.Assert(c, strings.Contains(string(b), "Minimum memory limit allowed is 6MB"))
 }
 
-func (s *DockerSuite) TestContainerAPIRename(c *check.C) {
+func (s *DockerSuite) TestContainerAPIRename(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "--name", "TestContainerAPIRename", "-d", "busybox", "sh")
 
 	containerID := strings.TrimSpace(out)
 	newName := "TestContainerAPIRenameNew"
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerRename(context.Background(), containerID, newName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	name := inspectField(c, containerID, "Name")
-	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container"))
+	assert.Equal(c, name, "/"+newName, "Failed to rename container")
 }
 
-func (s *DockerSuite) TestContainerAPIKill(c *check.C) {
+func (s *DockerSuite) TestContainerAPIKill(c *testing.T) {
 	name := "test-api-kill"
 	runSleepingContainer(c, "-i", "--name", name)
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerKill(context.Background(), name, "SIGKILL")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	state := inspectField(c, name, "State.Running")
-	c.Assert(state, checker.Equals, "false", check.Commentf("got wrong State from container %s: %q", name, state))
+	assert.Equal(c, state, "false", fmt.Sprintf("got wrong State from container %s: %q", name, state))
 }
 
-func (s *DockerSuite) TestContainerAPIRestart(c *check.C) {
+func (s *DockerSuite) TestContainerAPIRestart(c *testing.T) {
 	name := "test-api-restart"
 	runSleepingContainer(c, "-di", "--name", name)
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	timeout := 1 * time.Second
 	err = cli.ContainerRestart(context.Background(), name, &timeout)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second), checker.IsNil)
+	assert.Assert(c, waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second) == nil)
 }
 
-func (s *DockerSuite) TestContainerAPIRestartNotimeoutParam(c *check.C) {
+func (s *DockerSuite) TestContainerAPIRestartNotimeoutParam(c *testing.T) {
 	name := "test-api-restart-no-timeout-param"
 	out := runSleepingContainer(c, "-di", "--name", name)
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerRestart(context.Background(), name, nil)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second), checker.IsNil)
+	assert.Assert(c, waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second) == nil)
 }
 
-func (s *DockerSuite) TestContainerAPIStart(c *check.C) {
+func (s *DockerSuite) TestContainerAPIStart(c *testing.T) {
 	name := "testing-start"
 	config := containertypes.Config{
 		Image:     "busybox",
@@ -1007,44 +942,44 @@ func (s *DockerSuite) TestContainerAPIStart(c *check.C) {
 		OpenStdin: true,
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name)
-	c.Assert(err, checker.IsNil)
+	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name)
+	assert.NilError(c, err)
 
 	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// second call to start should give 304
 	// maybe add ContainerStartWithRaw to test it
 	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// TODO(tibor): figure out why this doesn't work on windows
 }
 
-func (s *DockerSuite) TestContainerAPIStop(c *check.C) {
+func (s *DockerSuite) TestContainerAPIStop(c *testing.T) {
 	name := "test-api-stop"
 	runSleepingContainer(c, "-i", "--name", name)
 	timeout := 30 * time.Second
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerStop(context.Background(), name, &timeout)
-	c.Assert(err, checker.IsNil)
-	c.Assert(waitInspect(name, "{{ .State.Running  }}", "false", 60*time.Second), checker.IsNil)
+	assert.NilError(c, err)
+	assert.Assert(c, waitInspect(name, "{{ .State.Running  }}", "false", 60*time.Second) == nil)
 
 	// second call to start should give 304
 	// maybe add ContainerStartWithRaw to test it
 	err = cli.ContainerStop(context.Background(), name, &timeout)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestContainerAPIWait(c *check.C) {
+func (s *DockerSuite) TestContainerAPIWait(c *testing.T) {
 	name := "test-api-wait"
 
 	sleepCmd := "/bin/sleep"
@@ -1053,21 +988,21 @@ func (s *DockerSuite) TestContainerAPIWait(c *check.C) {
 	}
 	dockerCmd(c, "run", "--name", name, "busybox", sleepCmd, "2")
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	waitresC, errC := cli.ContainerWait(context.Background(), name, "")
+	waitResC, errC := cli.ContainerWait(context.Background(), name, "")
 
 	select {
 	case err = <-errC:
-		c.Assert(err, checker.IsNil)
-	case waitres := <-waitresC:
-		c.Assert(waitres.StatusCode, checker.Equals, int64(0))
+		assert.NilError(c, err)
+	case waitRes := <-waitResC:
+		assert.Equal(c, waitRes.StatusCode, int64(0))
 	}
 }
 
-func (s *DockerSuite) TestContainerAPICopyNotExistsAnyMore(c *check.C) {
+func (s *DockerSuite) TestContainerAPICopyNotExistsAnyMore(c *testing.T) {
 	name := "test-container-api-copy"
 	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
 
@@ -1076,11 +1011,11 @@ func (s *DockerSuite) TestContainerAPICopyNotExistsAnyMore(c *check.C) {
 	}
 	// no copy in client/
 	res, _, err := request.Post("/containers/"+name+"/copy", request.JSONBody(postData))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusNotFound)
 }
 
-func (s *DockerSuite) TestContainerAPICopyPre124(c *check.C) {
+func (s *DockerSuite) TestContainerAPICopyPre124(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
 	name := "test-container-api-copy"
 	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
@@ -1090,8 +1025,8 @@ func (s *DockerSuite) TestContainerAPICopyPre124(c *check.C) {
 	}
 
 	res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	found := false
 	for tarReader := tar.NewReader(body); ; {
@@ -1107,10 +1042,10 @@ func (s *DockerSuite) TestContainerAPICopyPre124(c *check.C) {
 			break
 		}
 	}
-	c.Assert(found, checker.True)
+	assert.Assert(c, found)
 }
 
-func (s *DockerSuite) TestContainerAPICopyResourcePathEmptyPre124(c *check.C) {
+func (s *DockerSuite) TestContainerAPICopyResourcePathEmptyPre124(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
 	name := "test-container-api-copy-resource-empty"
 	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
@@ -1120,18 +1055,19 @@ func (s *DockerSuite) TestContainerAPICopyResourcePathEmptyPre124(c *check.C) {
 	}
 
 	res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	} else {
-		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
+		assert.Assert(c, res.StatusCode != http.StatusOK)
 	}
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b), checker.Matches, "Path cannot be empty\n")
+	assert.NilError(c, err)
+	assert.Assert(c, is.Regexp("^Path cannot be empty\n$", string(b)))
+
 }
 
-func (s *DockerSuite) TestContainerAPICopyResourcePathNotFoundPre124(c *check.C) {
+func (s *DockerSuite) TestContainerAPICopyResourcePathNotFoundPre124(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
 	name := "test-container-api-copy-resource-not-found"
 	dockerCmd(c, "run", "--name", name, "busybox")
@@ -1141,118 +1077,119 @@ func (s *DockerSuite) TestContainerAPICopyResourcePathNotFoundPre124(c *check.C)
 	}
 
 	res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
+		assert.Equal(c, res.StatusCode, http.StatusInternalServerError)
 	} else {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound)
+		assert.Equal(c, res.StatusCode, http.StatusNotFound)
 	}
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b), checker.Matches, "Could not find the file /notexist in container "+name+"\n")
+	assert.NilError(c, err)
+	assert.Assert(c, is.Regexp("^Could not find the file /notexist in container "+name+"\n$", string(b)))
+
 }
 
-func (s *DockerSuite) TestContainerAPICopyContainerNotFoundPr124(c *check.C) {
+func (s *DockerSuite) TestContainerAPICopyContainerNotFoundPr124(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
 	postData := types.CopyConfig{
 		Resource: "/something",
 	}
 
 	res, _, err := request.Post("/v1.23/containers/notexists/copy", request.JSONBody(postData))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusNotFound)
 }
 
-func (s *DockerSuite) TestContainerAPIDelete(c *check.C) {
+func (s *DockerSuite) TestContainerAPIDelete(c *testing.T) {
 	out := runSleepingContainer(c)
 
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	dockerCmd(c, "stop", id)
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestContainerAPIDeleteNotExist(c *check.C) {
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+func (s *DockerSuite) TestContainerAPIDeleteNotExist(c *testing.T) {
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerRemove(context.Background(), "doesnotexist", types.ContainerRemoveOptions{})
-	c.Assert(err.Error(), checker.Contains, "No such container: doesnotexist")
+	assert.ErrorContains(c, err, "No such container: doesnotexist")
 }
 
-func (s *DockerSuite) TestContainerAPIDeleteForce(c *check.C) {
+func (s *DockerSuite) TestContainerAPIDeleteForce(c *testing.T) {
 	out := runSleepingContainer(c)
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	removeOptions := types.ContainerRemoveOptions{
 		Force: true,
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerRemove(context.Background(), id, removeOptions)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestContainerAPIDeleteRemoveLinks(c *check.C) {
+func (s *DockerSuite) TestContainerAPIDeleteRemoveLinks(c *testing.T) {
 	// Windows does not support links
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top")
 
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top")
 
 	id2 := strings.TrimSpace(out)
-	c.Assert(waitRun(id2), checker.IsNil)
+	assert.Assert(c, waitRun(id2) == nil)
 
 	links := inspectFieldJSON(c, id2, "HostConfig.Links")
-	c.Assert(links, checker.Equals, "[\"/tlink1:/tlink2/tlink1\"]", check.Commentf("expected to have links between containers"))
+	assert.Equal(c, links, "[\"/tlink1:/tlink2/tlink1\"]", "expected to have links between containers")
 
 	removeOptions := types.ContainerRemoveOptions{
 		RemoveLinks: true,
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerRemove(context.Background(), "tlink2/tlink1", removeOptions)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	linksPostRm := inspectFieldJSON(c, id2, "HostConfig.Links")
-	c.Assert(linksPostRm, checker.Equals, "null", check.Commentf("call to api deleteContainer links should have removed the specified links"))
+	assert.Equal(c, linksPostRm, "null", "call to api deleteContainer links should have removed the specified links")
 }
 
-func (s *DockerSuite) TestContainerAPIDeleteConflict(c *check.C) {
+func (s *DockerSuite) TestContainerAPIDeleteConflict(c *testing.T) {
 	out := runSleepingContainer(c)
 
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{})
 	expected := "cannot remove a running container"
-	c.Assert(err.Error(), checker.Contains, expected)
+	assert.ErrorContains(c, err, expected)
 }
 
-func (s *DockerSuite) TestContainerAPIDeleteRemoveVolume(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestContainerAPIDeleteRemoveVolume(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 
 	vol := "/testvolume"
 	if testEnv.OSType == "windows" {
@@ -1262,31 +1199,31 @@ func (s *DockerSuite) TestContainerAPIDeleteRemoveVolume(c *check.C) {
 	out := runSleepingContainer(c, "-v", vol)
 
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	source, err := inspectMountSourceField(id, vol)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	_, err = os.Stat(source)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	removeOptions := types.ContainerRemoveOptions{
 		Force:         true,
 		RemoveVolumes: true,
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerRemove(context.Background(), id, removeOptions)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	_, err = os.Stat(source)
-	c.Assert(os.IsNotExist(err), checker.True, check.Commentf("expected to get ErrNotExist error, got %v", err))
+	assert.Assert(c, os.IsNotExist(err), "expected to get ErrNotExist error, got %v", err)
 }
 
 // Regression test for https://github.com/docker/docker/issues/6231
-func (s *DockerSuite) TestContainerAPIChunkedEncoding(c *check.C) {
+func (s *DockerSuite) TestContainerAPIChunkedEncoding(c *testing.T) {
 
 	config := map[string]interface{}{
 		"Image":     "busybox",
@@ -1301,42 +1238,42 @@ func (s *DockerSuite) TestContainerAPIChunkedEncoding(c *check.C) {
 		req.ContentLength = -1
 		return nil
 	}))
-	c.Assert(err, checker.IsNil, check.Commentf("error creating container with chunked encoding"))
+	assert.Assert(c, err == nil, "error creating container with chunked encoding")
 	defer resp.Body.Close()
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusCreated)
+	assert.Equal(c, resp.StatusCode, http.StatusCreated)
 }
 
-func (s *DockerSuite) TestContainerAPIPostContainerStop(c *check.C) {
+func (s *DockerSuite) TestContainerAPIPostContainerStop(c *testing.T) {
 	out := runSleepingContainer(c)
 
 	containerID := strings.TrimSpace(out)
-	c.Assert(waitRun(containerID), checker.IsNil)
+	assert.Assert(c, waitRun(containerID) == nil)
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerStop(context.Background(), containerID, nil)
-	c.Assert(err, checker.IsNil)
-	c.Assert(waitInspect(containerID, "{{ .State.Running  }}", "false", 60*time.Second), checker.IsNil)
+	assert.NilError(c, err)
+	assert.Assert(c, waitInspect(containerID, "{{ .State.Running  }}", "false", 60*time.Second) == nil)
 }
 
 // #14170
-func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *check.C) {
+func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *testing.T) {
 	config := containertypes.Config{
 		Image:      "busybox",
 		Entrypoint: []string{"echo"},
 		Cmd:        []string{"hello", "world"},
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest")
-	c.Assert(err, checker.IsNil)
+	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest")
+	assert.NilError(c, err)
 	out, _ := dockerCmd(c, "start", "-a", "echotest")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
+	assert.Equal(c, strings.TrimSpace(out), "hello world")
 
 	config2 := struct {
 		Image      string
@@ -1344,26 +1281,26 @@ func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *c
 		Cmd        []string
 	}{"busybox", "echo", []string{"hello", "world"}}
 	_, _, err = request.Post("/containers/create?name=echotest2", request.JSONBody(config2))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	out, _ = dockerCmd(c, "start", "-a", "echotest2")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
+	assert.Equal(c, strings.TrimSpace(out), "hello world")
 }
 
 // #14170
-func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) {
+func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *testing.T) {
 	config := containertypes.Config{
 		Image: "busybox",
 		Cmd:   []string{"echo", "hello", "world"},
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest")
-	c.Assert(err, checker.IsNil)
+	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest")
+	assert.NilError(c, err)
 	out, _ := dockerCmd(c, "start", "-a", "echotest")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
+	assert.Equal(c, strings.TrimSpace(out), "hello world")
 
 	config2 := struct {
 		Image      string
@@ -1371,62 +1308,64 @@ func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) {
 		Cmd        string
 	}{"busybox", "echo", "hello world"}
 	_, _, err = request.Post("/containers/create?name=echotest2", request.JSONBody(config2))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	out, _ = dockerCmd(c, "start", "-a", "echotest2")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
+	assert.Equal(c, strings.TrimSpace(out), "hello world")
 }
 
 // regression #14318
-func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) {
+// for backward compatibility testing with and without CAP_ prefix
+// and with upper and lowercase
+func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *testing.T) {
 	// Windows doesn't support CapAdd/CapDrop
 	testRequires(c, DaemonIsLinux)
 	config := struct {
 		Image   string
 		CapAdd  string
 		CapDrop string
-	}{"busybox", "NET_ADMIN", "SYS_ADMIN"}
+	}{"busybox", "NET_ADMIN", "cap_sys_admin"}
 	res, _, err := request.Post("/containers/create?name=capaddtest0", request.JSONBody(config))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusCreated)
 
 	config2 := containertypes.Config{
 		Image: "busybox",
 	}
 	hostConfig := containertypes.HostConfig{
-		CapAdd:  []string{"NET_ADMIN", "SYS_ADMIN"},
-		CapDrop: []string{"SETGID"},
+		CapAdd:  []string{"net_admin", "SYS_ADMIN"},
+		CapDrop: []string{"SETGID", "CAP_SETPCAP"},
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, "capaddtest1")
-	c.Assert(err, checker.IsNil)
+	_, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, nil, "capaddtest1")
+	assert.NilError(c, err)
 }
 
 // #14915
-func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *check.C) {
+func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows only support 1.25 or later
 	config := containertypes.Config{
 		Image: "busybox",
 	}
 
 	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.18"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err, checker.IsNil)
+	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	assert.NilError(c, err)
 }
 
 // Ensure an error occurs when you have a container read-only rootfs but you
 // extract an archive to a symlink in a writable volume which points to a
 // directory outside of the volume.
-func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) {
+func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *testing.T) {
 	// Windows does not support read-only rootfs
 	// Requires local volume mount bind.
 	// --read-only + userns has remount issues
-	testRequires(c, SameHostDaemon, NotUserNamespace, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, NotUserNamespace, DaemonIsLinux)
 
 	testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-")
 	defer os.RemoveAll(testVol)
@@ -1441,20 +1380,19 @@ func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(
 	// Attempt to extract to a symlink in the volume which points to a
 	// directory outside the volume. This should cause an error because the
 	// rootfs is read-only.
-	var httpClient *http.Client
-	cli, err := client.NewClient(daemonHost(), "v1.20", httpClient, map[string]string{})
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.20"))
+	assert.NilError(c, err)
 
 	err = cli.CopyToContainer(context.Background(), cID, "/vol2/symlinkToAbsDir", nil, types.CopyToContainerOptions{})
-	c.Assert(err.Error(), checker.Contains, "container rootfs is marked read-only")
+	assert.ErrorContains(c, err, "container rootfs is marked read-only")
 }
 
-func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) {
+func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *testing.T) {
 	// Not supported on Windows
 	testRequires(c, DaemonIsLinux)
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	config := containertypes.Config{
@@ -1467,9 +1405,9 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C)
 	}
 	name := "wrong-cpuset-cpus"
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, name)
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, nil, name)
 	expected := "Invalid value 1-42,, for cpuset cpus"
-	c.Assert(err.Error(), checker.Contains, expected)
+	assert.ErrorContains(c, err, expected)
 
 	hostConfig2 := containertypes.HostConfig{
 		Resources: containertypes.Resources{
@@ -1477,12 +1415,12 @@ func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C)
 		},
 	}
 	name = "wrong-cpuset-mems"
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, name)
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, nil, name)
 	expected = "Invalid value 42-3,1-- for cpuset mems"
-	c.Assert(err.Error(), checker.Contains, expected)
+	assert.ErrorContains(c, err, expected)
 }
 
-func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
+func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *testing.T) {
 	// ShmSize is not supported on Windows
 	testRequires(c, DaemonIsLinux)
 	config := containertypes.Config{
@@ -1492,15 +1430,15 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
 		ShmSize: -1,
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err.Error(), checker.Contains, "SHM size can not be less than 0")
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+	assert.ErrorContains(c, err, "SHM size can not be less than 0")
 }
 
-func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) {
+func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *testing.T) {
 	// ShmSize is not supported on Windows
 	testRequires(c, DaemonIsLinux)
 	var defaultSHMSize int64 = 67108864
@@ -1509,17 +1447,17 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.
 		Cmd:   []string{"mount"},
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err, check.IsNil)
+	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize)
+	assert.Equal(c, containerJSON.HostConfig.ShmSize, defaultSHMSize)
 
 	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
 	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
@@ -1528,7 +1466,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.
 	}
 }
 
-func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) {
+func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *testing.T) {
 	// ShmSize is not supported on Windows
 	testRequires(c, DaemonIsLinux)
 	config := containertypes.Config{
@@ -1536,17 +1474,17 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) {
 		Cmd:   []string{"mount"},
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err, check.IsNil)
+	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864))
+	assert.Equal(c, containerJSON.HostConfig.ShmSize, int64(67108864))
 
 	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
 	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
@@ -1555,7 +1493,7 @@ func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) {
+func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *testing.T) {
 	// ShmSize is not supported on Windows
 	testRequires(c, DaemonIsLinux)
 	config := containertypes.Config{
@@ -1567,17 +1505,17 @@ func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) {
 		ShmSize: 1073741824,
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err, check.IsNil)
+	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824))
+	assert.Equal(c, containerJSON.HostConfig.ShmSize, int64(1073741824))
 
 	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
 	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
@@ -1586,32 +1524,32 @@ func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) {
+func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *testing.T) {
 	// Swappiness is not supported on Windows
 	testRequires(c, DaemonIsLinux)
 	config := containertypes.Config{
 		Image: "busybox",
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
-	c.Assert(err, check.IsNil)
+	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
+	assert.NilError(c, err)
 
 	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.31") {
-		c.Assert(*containerJSON.HostConfig.MemorySwappiness, check.Equals, int64(-1))
+		assert.Equal(c, *containerJSON.HostConfig.MemorySwappiness, int64(-1))
 	} else {
-		c.Assert(containerJSON.HostConfig.MemorySwappiness, check.IsNil)
+		assert.Assert(c, containerJSON.HostConfig.MemorySwappiness == nil)
 	}
 }
 
 // check validation is done daemon side and not only in cli
-func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) {
+func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *testing.T) {
 	// OomScoreAdj is not supported on Windows
 	testRequires(c, DaemonIsLinux)
 
@@ -1623,38 +1561,38 @@ func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *che
 		OomScoreAdj: 1001,
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	name := "oomscoreadj-over"
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name)
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name)
 
 	expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
-	c.Assert(err.Error(), checker.Contains, expected)
+	assert.ErrorContains(c, err, expected)
 
 	hostConfig = containertypes.HostConfig{
 		OomScoreAdj: -1001,
 	}
 
 	name = "oomscoreadj-low"
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name)
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name)
 
 	expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
-	c.Assert(err.Error(), checker.Contains, expected)
+	assert.ErrorContains(c, err, expected)
 }
 
 // test case for #22210 where an empty container name caused panic.
-func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *check.C) {
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *testing.T) {
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	err = cli.ContainerRemove(context.Background(), "", types.ContainerRemoveOptions{})
-	c.Assert(err.Error(), checker.Contains, "No such container")
+	assert.ErrorContains(c, err, "No such container")
 }
 
-func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) {
+func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) {
 	// Problematic on Windows as Windows does not support stats
 	testRequires(c, DaemonIsLinux)
 
@@ -1666,17 +1604,17 @@ func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) {
 		NetworkDisabled: true,
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name)
-	c.Assert(err, checker.IsNil)
+	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name)
+	assert.NilError(c, err)
 
 	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(waitRun(name), check.IsNil)
+	assert.Assert(c, waitRun(name) == nil)
 
 	type b struct {
 		stats types.ContainerStats
@@ -1698,12 +1636,12 @@ func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) {
 	case <-time.After(2 * time.Second):
 		c.Fatal("stream was not closed after container was removed")
 	case sr := <-bc:
-		c.Assert(sr.err, checker.IsNil)
+		assert.Assert(c, sr.err == nil)
 		sr.stats.Body.Close()
 	}
 }
 
-func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
+func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
 	type testCase struct {
 		config     containertypes.Config
 		hostConfig containertypes.HostConfig
@@ -1797,9 +1735,9 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
 		},
 	}
 
-	if SameHostDaemon() {
+	if testEnv.IsLocalDaemon() {
 		tmpDir, err := ioutils.TempDir("", "test-mounts-api")
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		defer os.RemoveAll(tmpDir)
 		cases = append(cases, []testCase{
 			{
@@ -1828,6 +1766,32 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
 		}...)
 	}
 
+	if DaemonIsWindows() {
+		cases = append(cases, []testCase{
+			{
+				config: containertypes.Config{
+					Image: "busybox",
+				},
+				hostConfig: containertypes.HostConfig{
+					Mounts: []mounttypes.Mount{
+						{
+							Type:   "volume",
+							Source: "not-supported-on-windows",
+							Target: destPath,
+							VolumeOptions: &mounttypes.VolumeOptions{
+								DriverConfig: &mounttypes.Driver{
+									Name:    "local",
+									Options: map[string]string{"type": "tmpfs"},
+								},
+							},
+						},
+					},
+				},
+				msg: `options are not supported on this platform`,
+			},
+		}...)
+	}
+
 	if DaemonIsLinux() {
 		cases = append(cases, []testCase{
 			{
@@ -1835,14 +1799,83 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
 					Image: "busybox",
 				},
 				hostConfig: containertypes.HostConfig{
-					Mounts: []mounttypes.Mount{{
-						Type:   "volume",
-						Source: "hello3",
-						Target: destPath,
-						VolumeOptions: &mounttypes.VolumeOptions{
-							DriverConfig: &mounttypes.Driver{
-								Name:    "local",
-								Options: map[string]string{"o": "size=1"}}}}}},
+					Mounts: []mounttypes.Mount{
+						{
+							Type:   "volume",
+							Source: "missing-device-opt",
+							Target: destPath,
+							VolumeOptions: &mounttypes.VolumeOptions{
+								DriverConfig: &mounttypes.Driver{
+									Name:    "local",
+									Options: map[string]string{"foobar": "foobaz"},
+								},
+							},
+						},
+					},
+				},
+				msg: `invalid option: "foobar"`,
+			},
+			{
+				config: containertypes.Config{
+					Image: "busybox",
+				},
+				hostConfig: containertypes.HostConfig{
+					Mounts: []mounttypes.Mount{
+						{
+							Type:   "volume",
+							Source: "missing-device-opt",
+							Target: destPath,
+							VolumeOptions: &mounttypes.VolumeOptions{
+								DriverConfig: &mounttypes.Driver{
+									Name:    "local",
+									Options: map[string]string{"type": "tmpfs"},
+								},
+							},
+						},
+					},
+				},
+				msg: `missing required option: "device"`,
+			},
+			{
+				config: containertypes.Config{
+					Image: "busybox",
+				},
+				hostConfig: containertypes.HostConfig{
+					Mounts: []mounttypes.Mount{
+						{
+							Type:   "volume",
+							Source: "missing-type-opt",
+							Target: destPath,
+							VolumeOptions: &mounttypes.VolumeOptions{
+								DriverConfig: &mounttypes.Driver{
+									Name:    "local",
+									Options: map[string]string{"device": "tmpfs"},
+								},
+							},
+						},
+					},
+				},
+				msg: `missing required option: "type"`,
+			},
+			{
+				config: containertypes.Config{
+					Image: "busybox",
+				},
+				hostConfig: containertypes.HostConfig{
+					Mounts: []mounttypes.Mount{
+						{
+							Type:   "volume",
+							Source: "hello4",
+							Target: destPath,
+							VolumeOptions: &mounttypes.VolumeOptions{
+								DriverConfig: &mounttypes.Driver{
+									Name:    "local",
+									Options: map[string]string{"o": "size=1", "type": "tmpfs", "device": "tmpfs"},
+								},
+							},
+						},
+					},
+				},
 				msg: "",
 			},
 			{
@@ -1869,7 +1902,6 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
 						}}}},
 				msg: "",
 			},
-
 			{
 				config: containertypes.Config{
 					Image: "busybox",
@@ -1884,31 +1916,34 @@ func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
 		}...)
 
 	}
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
-	defer cli.Close()
+	apiClient, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
+	defer apiClient.Close()
 
+	// TODO add checks for statuscode returned by API
 	for i, x := range cases {
-		c.Logf("case %d", i)
-		_, err = cli.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, "")
-		if len(x.msg) > 0 {
-			c.Assert(err.Error(), checker.Contains, x.msg, check.Commentf("%v", cases[i].config))
-		} else {
-			c.Assert(err, checker.IsNil)
-		}
+		x := x
+		c.Run(fmt.Sprintf("case %d", i), func(c *testing.T) {
+			_, err = apiClient.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, nil, "")
+			if len(x.msg) > 0 {
+				assert.ErrorContains(c, err, x.msg, "%v", cases[i].config)
+			} else {
+				assert.NilError(c, err)
+			}
+		})
 	}
 }
 
-func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *check.C) {
-	testRequires(c, NotUserNamespace, SameHostDaemon)
+func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *testing.T) {
+	testRequires(c, NotUserNamespace, testEnv.IsLocalDaemon)
 	// also with data in the host side
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	destPath := prefix + slash + "foo"
-	tmpDir, err := ioutil.TempDir("", "test-mounts-api-bind")
-	c.Assert(err, checker.IsNil)
+	tmpDir, err := os.MkdirTemp("", "test-mounts-api-bind")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmpDir)
-	err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 666)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 0666)
+	assert.NilError(c, err)
 	config := containertypes.Config{
 		Image: "busybox",
 		Cmd:   []string{"/bin/sh", "-c", "cat /foo/bar"},
@@ -1918,19 +1953,19 @@ func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *check.C) {
 			{Type: "bind", Source: tmpDir, Target: destPath},
 		},
 	}
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "test")
-	c.Assert(err, checker.IsNil)
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "test")
+	assert.NilError(c, err)
 
 	out, _ := dockerCmd(c, "start", "-a", "test")
-	c.Assert(out, checker.Equals, "hello")
+	assert.Equal(c, out, "hello")
 }
 
 // Test Mounts comes out as expected for the MountPoint
-func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
+func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	destPath := prefix + slash + "foo"
 
@@ -1988,10 +2023,10 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
 		},
 	}
 
-	if SameHostDaemon() {
+	if testEnv.IsLocalDaemon() {
 		// setup temp dir for testing binds
-		tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1")
-		c.Assert(err, checker.IsNil)
+		tmpDir1, err := os.MkdirTemp("", "test-mounts-api-1")
+		assert.NilError(c, err)
 		defer os.RemoveAll(tmpDir1)
 		cases = append(cases, []testCase{
 			{
@@ -2016,11 +2051,10 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
 		// for modes only supported on Linux
 		if DaemonIsLinux() {
 			tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3")
-			c.Assert(err, checker.IsNil)
+			assert.NilError(c, err)
 			defer os.RemoveAll(tmpDir3)
 
-			c.Assert(mount.Mount(tmpDir3, tmpDir3, "none", "bind,rw"), checker.IsNil)
-			c.Assert(mount.ForceMount("", tmpDir3, "none", "shared"), checker.IsNil)
+			assert.Assert(c, mountWrapper(tmpDir3, tmpDir3, "none", "bind,shared") == nil)
 
 			cases = append(cases, []testCase{
 				{
@@ -2060,74 +2094,69 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
 		}...)
 	}
 
-	type wrapper struct {
-		containertypes.Config
-		HostConfig containertypes.HostConfig
-	}
-	type createResp struct {
-		ID string `json:"Id"`
-	}
-
 	ctx := context.Background()
 	apiclient := testEnv.APIClient()
 	for i, x := range cases {
-		c.Logf("case %d - config: %v", i, x.spec)
-		container, err := apiclient.ContainerCreate(
-			ctx,
-			&containertypes.Config{Image: testImg},
-			&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}},
-			&networktypes.NetworkingConfig{},
-			"")
-		assert.NilError(c, err)
+		x := x
+		c.Run(fmt.Sprintf("%d config: %v", i, x.spec), func(c *testing.T) {
+			container, err := apiclient.ContainerCreate(
+				ctx,
+				&containertypes.Config{Image: testImg},
+				&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}},
+				&networktypes.NetworkingConfig{},
+				nil,
+				"")
+			assert.NilError(c, err)
 
-		containerInspect, err := apiclient.ContainerInspect(ctx, container.ID)
-		assert.NilError(c, err)
-		mps := containerInspect.Mounts
-		assert.Assert(c, is.Len(mps, 1))
-		mountPoint := mps[0]
+			containerInspect, err := apiclient.ContainerInspect(ctx, container.ID)
+			assert.NilError(c, err)
+			mps := containerInspect.Mounts
+			assert.Assert(c, is.Len(mps, 1))
+			mountPoint := mps[0]
 
-		if x.expected.Source != "" {
-			assert.Check(c, is.Equal(x.expected.Source, mountPoint.Source))
-		}
-		if x.expected.Name != "" {
-			assert.Check(c, is.Equal(x.expected.Name, mountPoint.Name))
-		}
-		if x.expected.Driver != "" {
-			assert.Check(c, is.Equal(x.expected.Driver, mountPoint.Driver))
-		}
-		if x.expected.Propagation != "" {
-			assert.Check(c, is.Equal(x.expected.Propagation, mountPoint.Propagation))
-		}
-		assert.Check(c, is.Equal(x.expected.RW, mountPoint.RW))
-		assert.Check(c, is.Equal(x.expected.Type, mountPoint.Type))
-		assert.Check(c, is.Equal(x.expected.Mode, mountPoint.Mode))
-		assert.Check(c, is.Equal(x.expected.Destination, mountPoint.Destination))
+			if x.expected.Source != "" {
+				assert.Check(c, is.Equal(x.expected.Source, mountPoint.Source))
+			}
+			if x.expected.Name != "" {
+				assert.Check(c, is.Equal(x.expected.Name, mountPoint.Name))
+			}
+			if x.expected.Driver != "" {
+				assert.Check(c, is.Equal(x.expected.Driver, mountPoint.Driver))
+			}
+			if x.expected.Propagation != "" {
+				assert.Check(c, is.Equal(x.expected.Propagation, mountPoint.Propagation))
+			}
+			assert.Check(c, is.Equal(x.expected.RW, mountPoint.RW))
+			assert.Check(c, is.Equal(x.expected.Type, mountPoint.Type))
+			assert.Check(c, is.Equal(x.expected.Mode, mountPoint.Mode))
+			assert.Check(c, is.Equal(x.expected.Destination, mountPoint.Destination))
 
-		err = apiclient.ContainerStart(ctx, container.ID, types.ContainerStartOptions{})
-		assert.NilError(c, err)
-		poll.WaitOn(c, containerExit(apiclient, container.ID), poll.WithDelay(time.Second))
+			err = apiclient.ContainerStart(ctx, container.ID, types.ContainerStartOptions{})
+			assert.NilError(c, err)
+			poll.WaitOn(c, containerExit(apiclient, container.ID), poll.WithDelay(time.Second))
 
-		err = apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
-			RemoveVolumes: true,
-			Force:         true,
-		})
-		assert.NilError(c, err)
+			err = apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
+				RemoveVolumes: true,
+				Force:         true,
+			})
+			assert.NilError(c, err)
 
-		switch {
+			switch {
 
-		// Named volumes still exist after the container is removed
-		case x.spec.Type == "volume" && len(x.spec.Source) > 0:
-			_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
-			assert.NilError(c, err)
+			// Named volumes still exist after the container is removed
+			case x.spec.Type == "volume" && len(x.spec.Source) > 0:
+				_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
+				assert.NilError(c, err)
 
-		// Bind mounts are never removed with the container
-		case x.spec.Type == "bind":
+			// Bind mounts are never removed with the container
+			case x.spec.Type == "bind":
 
-		// anonymous volumes are removed
-		default:
-			_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
-			assert.Check(c, client.IsErrNotFound(err))
-		}
+			// anonymous volumes are removed
+			default:
+				_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
+				assert.Check(c, client.IsErrNotFound(err))
+			}
+		})
 	}
 }
 
@@ -2145,7 +2174,7 @@ func containerExit(apiclient client.APIClient, name string) func(poll.LogT) poll
 	}
 }
 
-func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) {
+func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	type testCase struct {
 		cfg             mounttypes.Mount
@@ -2169,8 +2198,8 @@ func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) {
 		},
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	config := containertypes.Config{
@@ -2183,11 +2212,11 @@ func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) {
 			Mounts: []mounttypes.Mount{x.cfg},
 		}
 
-		_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, cName)
-		c.Assert(err, checker.IsNil)
+		_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, cName)
+		assert.NilError(c, err)
 		out, _ := dockerCmd(c, "start", "-a", cName)
 		for _, option := range x.expectedOptions {
-			c.Assert(out, checker.Contains, option)
+			assert.Assert(c, strings.Contains(out, option))
 		}
 	}
 }
@@ -2195,15 +2224,15 @@ func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) {
 // Regression test for #33334
 // Makes sure that when a container which has a custom stop signal + restart=always
 // gets killed (with SIGKILL) by the kill API, that the restart policy is cancelled.
-func (s *DockerSuite) TestContainerKillCustomStopSignal(c *check.C) {
+func (s *DockerSuite) TestContainerKillCustomStopSignal(c *testing.T) {
 	id := strings.TrimSpace(runSleepingContainer(c, "--stop-signal=SIGTERM", "--restart=always"))
 	res, _, err := request.Post("/containers/" + id + "/kill")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer res.Body.Close()
 
-	b, err := ioutil.ReadAll(res.Body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent, check.Commentf(string(b)))
+	b, err := io.ReadAll(res.Body)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusNoContent, string(b))
 	err = waitInspect(id, "{{.State.Running}} {{.State.Restarting}}", "false false", 30*time.Second)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
diff --git a/integration-cli/docker_api_containers_unix_test.go b/integration-cli/docker_api_containers_unix_test.go
new file mode 100644
index 0000000000000..be0eb1eb4bf5c
--- /dev/null
+++ b/integration-cli/docker_api_containers_unix_test.go
@@ -0,0 +1,10 @@
+//go:build !windows
+// +build !windows
+
+package main
+
+import "github.com/moby/sys/mount"
+
+func mountWrapper(device, target, mType, options string) error {
+	return mount.Mount(device, target, mType, options)
+}
diff --git a/integration-cli/docker_api_containers_windows_test.go b/integration-cli/docker_api_containers_windows_test.go
index c569574de5350..cfb1596c130c9 100644
--- a/integration-cli/docker_api_containers_windows_test.go
+++ b/integration-cli/docker_api_containers_windows_test.go
@@ -1,3 +1,4 @@
+//go:build windows
 // +build windows
 
 package main
@@ -5,21 +6,23 @@ package main
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"math/rand"
 	"strings"
+	"testing"
 
 	winio "github.com/Microsoft/go-winio"
+	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/mount"
-	"github.com/go-check/check"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
+	"github.com/pkg/errors"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
-func (s *DockerSuite) TestContainersAPICreateMountsBindNamedPipe(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsWindowsAtLeastBuild(16299)) // Named pipe support was added in RS3
+func (s *DockerSuite) TestContainersAPICreateMountsBindNamedPipe(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsWindowsAtLeastBuild(osversion.RS3)) // Named pipe support was added in RS3
 
 	// Create a host pipe to map into the container
 	hostPipeName := fmt.Sprintf(`\\.\pipe\docker-cli-test-pipe-%x`, rand.Uint64())
@@ -38,7 +41,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsBindNamedPipe(c *check.C) {
 	go func() {
 		conn, err := l.Accept()
 		if err == nil {
-			b, err = ioutil.ReadAll(conn)
+			b, err = io.ReadAll(conn)
 			conn.Close()
 		}
 		ch <- err
@@ -64,7 +67,7 @@ func (s *DockerSuite) TestContainersAPICreateMountsBindNamedPipe(c *check.C) {
 				},
 			},
 		},
-		nil, name)
+		nil, nil, name)
 	assert.NilError(c, err)
 
 	err = client.ContainerStart(ctx, name, types.ContainerStartOptions{})
@@ -74,3 +77,8 @@ func (s *DockerSuite) TestContainersAPICreateMountsBindNamedPipe(c *check.C) {
 	assert.NilError(c, err)
 	assert.Check(c, is.Equal(text, strings.TrimSpace(string(b))))
 }
+
+func mountWrapper(device, target, mType, options string) error {
+	// This should never be called.
+	return errors.Errorf("there is no implementation of Mount on this platform")
+}
diff --git a/integration-cli/docker_api_exec_resize_test.go b/integration-cli/docker_api_exec_resize_test.go
index 2db3d3e3170f1..beff0adff9191 100644
--- a/integration-cli/docker_api_exec_resize_test.go
+++ b/integration-cli/docker_api_exec_resize_test.go
@@ -8,30 +8,31 @@ import (
 	"net/http"
 	"strings"
 	"sync"
+	"testing"
 
 	"github.com/docker/docker/api/types/versions"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/request"
+	"github.com/pkg/errors"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestExecResizeAPIHeightWidthNoInt(c *check.C) {
+func (s *DockerSuite) TestExecResizeAPIHeightWidthNoInt(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
 	cleanedContainerID := strings.TrimSpace(out)
 
 	endpoint := "/exec/" + cleanedContainerID + "/resize?h=foo&w=bar"
 	res, _, err := request.Post(endpoint)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
+		assert.Equal(c, res.StatusCode, http.StatusInternalServerError)
 	} else {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	}
 }
 
 // Part of #14845
-func (s *DockerSuite) TestExecResizeImmediatelyAfterExecStart(c *check.C) {
+func (s *DockerSuite) TestExecResizeImmediatelyAfterExecStart(c *testing.T) {
 	name := "exec_resize_test"
 	dockerCmd(c, "run", "-d", "-i", "-t", "--name", name, "--restart", "always", "busybox", "/bin/sh")
 
@@ -46,38 +47,38 @@ func (s *DockerSuite) TestExecResizeImmediatelyAfterExecStart(c *check.C) {
 			return err
 		}
 		if res.StatusCode != http.StatusCreated {
-			return fmt.Errorf("POST %s is expected to return %d, got %d", uri, http.StatusCreated, res.StatusCode)
+			return errors.Errorf("POST %s is expected to return %d, got %d", uri, http.StatusCreated, res.StatusCode)
 		}
 
 		buf, err := request.ReadBody(body)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 
 		out := map[string]string{}
 		err = json.Unmarshal(buf, &out)
 		if err != nil {
-			return fmt.Errorf("ExecCreate returned invalid json. Error: %q", err.Error())
+			return errors.Wrap(err, "ExecCreate returned invalid json")
 		}
 
 		execID := out["Id"]
 		if len(execID) < 1 {
-			return fmt.Errorf("ExecCreate got invalid execID")
+			return errors.New("ExecCreate got invalid execID")
 		}
 
 		payload := bytes.NewBufferString(`{"Tty":true}`)
-		conn, _, err := sockRequestHijack("POST", fmt.Sprintf("/exec/%s/start", execID), payload, "application/json", daemonHost())
+		wc, _, err := requestHijack(http.MethodPost, fmt.Sprintf("/exec/%s/start", execID), payload, "application/json", request.DaemonHost())
 		if err != nil {
-			return fmt.Errorf("Failed to start the exec: %q", err.Error())
+			return errors.Wrap(err, "failed to start the exec")
 		}
-		defer conn.Close()
+		defer wc.Close()
 
 		_, rc, err := request.Post(fmt.Sprintf("/exec/%s/resize?h=24&w=80", execID), request.ContentType("text/plain"))
 		if err != nil {
 			// It's probably a panic of the daemon if io.ErrUnexpectedEOF is returned.
 			if err == io.ErrUnexpectedEOF {
-				return fmt.Errorf("The daemon might have crashed.")
+				return errors.New("the daemon might have crashed")
 			}
 			// Other error happened, should be reported.
-			return fmt.Errorf("Fail to exec resize immediately after start. Error: %q", err.Error())
+			return errors.Wrap(err, "failed to exec resize immediately after start")
 		}
 
 		rc.Close()
diff --git a/integration-cli/docker_api_exec_test.go b/integration-cli/docker_api_exec_test.go
index 118f9971a7026..3bf5d003f8d17 100644
--- a/integration-cli/docker_api_exec_test.go
+++ b/integration-cli/docker_api_exec_test.go
@@ -1,5 +1,3 @@
-// +build !test_no_exec
-
 package main
 
 import (
@@ -7,40 +5,40 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"os"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
 )
 
 // Regression test for #9414
-func (s *DockerSuite) TestExecAPICreateNoCmd(c *check.C) {
+func (s *DockerSuite) TestExecAPICreateNoCmd(c *testing.T) {
 	name := "exec_test"
 	dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
 
 	res, body, err := request.Post(fmt.Sprintf("/containers/%s/exec", name), request.JSONBody(map[string]interface{}{"Cmd": nil}))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
+		assert.Equal(c, res.StatusCode, http.StatusInternalServerError)
 	} else {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	}
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-
-	comment := check.Commentf("Expected message when creating exec command with no Cmd specified")
-	c.Assert(getErrorMessage(c, b), checker.Contains, "No exec command specified", comment)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(getErrorMessage(c, b), "No exec command specified"), "Expected message when creating exec command with no Cmd specified")
 }
 
-func (s *DockerSuite) TestExecAPICreateNoValidContentType(c *check.C) {
+func (s *DockerSuite) TestExecAPICreateNoValidContentType(c *testing.T) {
 	name := "exec_test"
 	dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
 
@@ -49,21 +47,19 @@ func (s *DockerSuite) TestExecAPICreateNoValidContentType(c *check.C) {
 		c.Fatalf("Can not encode data to json %s", err)
 	}
 
-	res, body, err := request.Post(fmt.Sprintf("/containers/%s/exec", name), request.RawContent(ioutil.NopCloser(jsonData)), request.ContentType("test/plain"))
-	c.Assert(err, checker.IsNil)
+	res, body, err := request.Post(fmt.Sprintf("/containers/%s/exec", name), request.RawContent(io.NopCloser(jsonData)), request.ContentType("test/plain"))
+	assert.NilError(c, err)
 	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
+		assert.Equal(c, res.StatusCode, http.StatusInternalServerError)
 	} else {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	}
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-
-	comment := check.Commentf("Expected message when creating exec command with invalid Content-Type specified")
-	c.Assert(getErrorMessage(c, b), checker.Contains, "Content-Type specified", comment)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(getErrorMessage(c, b), "Content-Type specified"), "Expected message when creating exec command with invalid Content-Type specified")
 }
 
-func (s *DockerSuite) TestExecAPICreateContainerPaused(c *check.C) {
+func (s *DockerSuite) TestExecAPICreateContainerPaused(c *testing.T) {
 	// Not relevant on Windows as Windows containers cannot be paused
 	testRequires(c, DaemonIsLinux)
 	name := "exec_create_test"
@@ -71,20 +67,18 @@ func (s *DockerSuite) TestExecAPICreateContainerPaused(c *check.C) {
 
 	dockerCmd(c, "pause", name)
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	config := types.ExecConfig{
 		Cmd: []string{"true"},
 	}
 	_, err = cli.ContainerExecCreate(context.Background(), name, config)
-
-	comment := check.Commentf("Expected message when creating exec command with Container %s is paused", name)
-	c.Assert(err.Error(), checker.Contains, "Container "+name+" is paused, unpause the container before exec", comment)
+	assert.ErrorContains(c, err, "Container "+name+" is paused, unpause the container before exec", "Expected message when creating exec command with Container %s is paused", name)
 }
 
-func (s *DockerSuite) TestExecAPIStart(c *check.C) {
+func (s *DockerSuite) TestExecAPIStart(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Uses pause/unpause but bits may be salvageable to Windows to Windows CI
 	dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
 
@@ -93,7 +87,7 @@ func (s *DockerSuite) TestExecAPIStart(c *check.C) {
 
 	var execJSON struct{ PID int }
 	inspectExec(c, id, &execJSON)
-	c.Assert(execJSON.PID, checker.GreaterThan, 1)
+	assert.Assert(c, execJSON.PID > 1)
 
 	id = createExec(c, "test")
 	dockerCmd(c, "stop", "test")
@@ -111,32 +105,32 @@ func (s *DockerSuite) TestExecAPIStart(c *check.C) {
 	startExec(c, id, http.StatusOK)
 }
 
-func (s *DockerSuite) TestExecAPIStartEnsureHeaders(c *check.C) {
+func (s *DockerSuite) TestExecAPIStartEnsureHeaders(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
 
 	id := createExec(c, "test")
 	resp, _, err := request.Post(fmt.Sprintf("/exec/%s/start", id), request.RawString(`{"Detach": true}`), request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(resp.Header.Get("Server"), checker.Not(checker.Equals), "")
+	assert.NilError(c, err)
+	assert.Assert(c, resp.Header.Get("Server") != "")
 }
 
-func (s *DockerSuite) TestExecAPIStartBackwardsCompatible(c *check.C) {
+func (s *DockerSuite) TestExecAPIStartBackwardsCompatible(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
 	runSleepingContainer(c, "-d", "--name", "test")
 	id := createExec(c, "test")
 
 	resp, body, err := request.Post(fmt.Sprintf("/v1.20/exec/%s/start", id), request.RawString(`{"Detach": true}`), request.ContentType("text/plain"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	b, err := request.ReadBody(body)
-	comment := check.Commentf("response body: %s", b)
-	c.Assert(err, checker.IsNil, comment)
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK, comment)
+	comment := fmt.Sprintf("response body: %s", b)
+	assert.NilError(c, err, comment)
+	assert.Equal(c, resp.StatusCode, http.StatusOK, comment)
 }
 
 // #19362
-func (s *DockerSuite) TestExecAPIStartMultipleTimesError(c *check.C) {
+func (s *DockerSuite) TestExecAPIStartMultipleTimesError(c *testing.T) {
 	runSleepingContainer(c, "-d", "--name", "test")
 	execID := createExec(c, "test")
 	startExec(c, execID, http.StatusOK)
@@ -146,7 +140,7 @@ func (s *DockerSuite) TestExecAPIStartMultipleTimesError(c *check.C) {
 }
 
 // #20638
-func (s *DockerSuite) TestExecAPIStartWithDetach(c *check.C) {
+func (s *DockerSuite) TestExecAPIStartWithDetach(c *testing.T) {
 	name := "foo"
 	runSleepingContainer(c, "-d", "-t", "--name", name)
 
@@ -155,29 +149,29 @@ func (s *DockerSuite) TestExecAPIStartWithDetach(c *check.C) {
 		AttachStderr: true,
 	}
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	createResp, err := cli.ContainerExecCreate(context.Background(), name, config)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	_, body, err := request.Post(fmt.Sprintf("/exec/%s/start", createResp.ID), request.RawString(`{"Detach": true}`), request.JSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	b, err := request.ReadBody(body)
-	comment := check.Commentf("response body: %s", b)
-	c.Assert(err, checker.IsNil, comment)
+	comment := fmt.Sprintf("response body: %s", b)
+	assert.NilError(c, err, comment)
 
 	resp, _, err := request.Get("/_ping")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if resp.StatusCode != http.StatusOK {
 		c.Fatal("daemon is down, it should alive")
 	}
 }
 
 // #30311
-func (s *DockerSuite) TestExecAPIStartValidCommand(c *check.C) {
+func (s *DockerSuite) TestExecAPIStartValidCommand(c *testing.T) {
 	name := "exec_test"
 	dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
 
@@ -189,11 +183,11 @@ func (s *DockerSuite) TestExecAPIStartValidCommand(c *check.C) {
 	var inspectJSON struct{ ExecIDs []string }
 	inspectContainer(c, name, &inspectJSON)
 
-	c.Assert(inspectJSON.ExecIDs, checker.IsNil)
+	assert.Assert(c, inspectJSON.ExecIDs == nil)
 }
 
 // #30311
-func (s *DockerSuite) TestExecAPIStartInvalidCommand(c *check.C) {
+func (s *DockerSuite) TestExecAPIStartInvalidCommand(c *testing.T) {
 	name := "exec_test"
 	dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
 
@@ -208,11 +202,11 @@ func (s *DockerSuite) TestExecAPIStartInvalidCommand(c *check.C) {
 	var inspectJSON struct{ ExecIDs []string }
 	inspectContainer(c, name, &inspectJSON)
 
-	c.Assert(inspectJSON.ExecIDs, checker.IsNil)
+	assert.Assert(c, inspectJSON.ExecIDs == nil)
 }
 
-func (s *DockerSuite) TestExecStateCleanup(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestExecStateCleanup(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 
 	// This test checks accidental regressions. Not part of stable API.
 
@@ -222,71 +216,70 @@ func (s *DockerSuite) TestExecStateCleanup(c *check.C) {
 
 	stateDir := "/var/run/docker/containerd/" + cid
 
-	checkReadDir := func(c *check.C) (interface{}, check.CommentInterface) {
-		fi, err := ioutil.ReadDir(stateDir)
-		c.Assert(err, checker.IsNil)
-		return len(fi), nil
+	checkReadDir := func(c *testing.T) (interface{}, string) {
+		fi, err := os.ReadDir(stateDir)
+		assert.NilError(c, err)
+		return len(fi), ""
 	}
 
-	fi, err := ioutil.ReadDir(stateDir)
-	c.Assert(err, checker.IsNil)
-	c.Assert(len(fi), checker.GreaterThan, 1)
+	fi, err := os.ReadDir(stateDir)
+	assert.NilError(c, err)
+	assert.Assert(c, len(fi) > 1)
 
 	id := createExecCmd(c, name, "ls")
 	startExec(c, id, http.StatusOK)
 	waitForExec(c, id)
 
-	waitAndAssert(c, 5*time.Second, checkReadDir, checker.Equals, len(fi))
+	poll.WaitOn(c, pollCheck(c, checkReadDir, checker.Equals(len(fi))), poll.WithTimeout(5*time.Second))
 
 	id = createExecCmd(c, name, "invalid")
 	startExec(c, id, http.StatusBadRequest)
 	waitForExec(c, id)
 
-	waitAndAssert(c, 5*time.Second, checkReadDir, checker.Equals, len(fi))
+	poll.WaitOn(c, pollCheck(c, checkReadDir, checker.Equals(len(fi))), poll.WithTimeout(5*time.Second))
 
 	dockerCmd(c, "stop", name)
 	_, err = os.Stat(stateDir)
-	c.Assert(err, checker.NotNil)
-	c.Assert(os.IsNotExist(err), checker.True)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, os.IsNotExist(err))
 }
 
-func createExec(c *check.C, name string) string {
+func createExec(c *testing.T, name string) string {
 	return createExecCmd(c, name, "true")
 }
 
-func createExecCmd(c *check.C, name string, cmd string) string {
+func createExecCmd(c *testing.T, name string, cmd string) string {
 	_, reader, err := request.Post(fmt.Sprintf("/containers/%s/exec", name), request.JSONBody(map[string]interface{}{"Cmd": []string{cmd}}))
-	c.Assert(err, checker.IsNil)
-	b, err := ioutil.ReadAll(reader)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
+	b, err := io.ReadAll(reader)
+	assert.NilError(c, err)
 	defer reader.Close()
 	createResp := struct {
 		ID string `json:"Id"`
 	}{}
-	c.Assert(json.Unmarshal(b, &createResp), checker.IsNil, check.Commentf(string(b)))
+	assert.NilError(c, json.Unmarshal(b, &createResp), string(b))
 	return createResp.ID
 }
 
-func startExec(c *check.C, id string, code int) {
+func startExec(c *testing.T, id string, code int) {
 	resp, body, err := request.Post(fmt.Sprintf("/exec/%s/start", id), request.RawString(`{"Detach": true}`), request.JSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	b, err := request.ReadBody(body)
-	comment := check.Commentf("response body: %s", b)
-	c.Assert(err, checker.IsNil, comment)
-	c.Assert(resp.StatusCode, checker.Equals, code, comment)
+	assert.NilError(c, err, "response body: %s", b)
+	assert.Equal(c, resp.StatusCode, code, "response body: %s", b)
 }
 
-func inspectExec(c *check.C, id string, out interface{}) {
+func inspectExec(c *testing.T, id string, out interface{}) {
 	resp, body, err := request.Get(fmt.Sprintf("/exec/%s/json", id))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer body.Close()
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
+	assert.Equal(c, resp.StatusCode, http.StatusOK)
 	err = json.NewDecoder(body).Decode(out)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
 
-func waitForExec(c *check.C, id string) {
+func waitForExec(c *testing.T, id string) {
 	timeout := time.After(60 * time.Second)
 	var execJSON struct{ Running bool }
 	for {
@@ -303,11 +296,11 @@ func waitForExec(c *check.C, id string) {
 	}
 }
 
-func inspectContainer(c *check.C, id string, out interface{}) {
-	resp, body, err := request.Get(fmt.Sprintf("/containers/%s/json", id))
-	c.Assert(err, checker.IsNil)
+func inspectContainer(c *testing.T, id string, out interface{}) {
+	resp, body, err := request.Get("/containers/" + id + "/json")
+	assert.NilError(c, err)
 	defer body.Close()
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
+	assert.Equal(c, resp.StatusCode, http.StatusOK)
 	err = json.NewDecoder(body).Decode(out)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
diff --git a/integration-cli/docker_api_images_test.go b/integration-cli/docker_api_images_test.go
index f306cca6173dd..fd1732f756987 100644
--- a/integration-cli/docker_api_images_test.go
+++ b/integration-cli/docker_api_images_test.go
@@ -7,21 +7,22 @@ import (
 	"runtime"
 	"strconv"
 	"strings"
+	"testing"
 
+	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/pkg/parsers/kernel"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestAPIImagesFilter(c *check.C) {
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+func (s *DockerSuite) TestAPIImagesFilter(c *testing.T) {
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	name := "utest:tag1"
@@ -38,32 +39,34 @@ func (s *DockerSuite) TestAPIImagesFilter(c *check.C) {
 			Filters: filters,
 		}
 		images, err := cli.ImageList(context.Background(), options)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 
 		return images
 	}
 
-	//incorrect number of matches returned
+	// incorrect number of matches returned
 	images := getImages("utest*/*")
-	c.Assert(images[0].RepoTags, checker.HasLen, 2)
+	assert.Equal(c, len(images[0].RepoTags), 2)
 
 	images = getImages("utest")
-	c.Assert(images[0].RepoTags, checker.HasLen, 1)
+	assert.Equal(c, len(images[0].RepoTags), 1)
 
 	images = getImages("utest*")
-	c.Assert(images[0].RepoTags, checker.HasLen, 1)
+	assert.Equal(c, len(images[0].RepoTags), 1)
 
 	images = getImages("*5000*/*")
-	c.Assert(images[0].RepoTags, checker.HasLen, 1)
+	assert.Equal(c, len(images[0].RepoTags), 1)
 }
 
-func (s *DockerSuite) TestAPIImagesSaveAndLoad(c *check.C) {
+func (s *DockerSuite) TestAPIImagesSaveAndLoad(c *testing.T) {
 	if runtime.GOOS == "windows" {
+		// Note we parse kernel.GetKernelVersion rather than osversion.Build()
+		// as test binaries aren't manifested, so would otherwise report build 9200.
 		v, err := kernel.GetKernelVersion()
-		c.Assert(err, checker.IsNil)
-		build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
-		if build == 16299 {
-			c.Skip("Temporarily disabled on RS3 builds")
+		assert.NilError(c, err)
+		buildNumber, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
+		if buildNumber <= osversion.RS3 {
+			c.Skip("Temporarily disabled on RS3 and older because they are too slow. See #39909")
 		}
 	}
 
@@ -72,24 +75,24 @@ func (s *DockerSuite) TestAPIImagesSaveAndLoad(c *check.C) {
 	id := getIDByName(c, "saveandload")
 
 	res, body, err := request.Get("/images/" + id + "/get")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer body.Close()
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	dockerCmd(c, "rmi", id)
 
 	res, loadBody, err := request.Post("/images/load", request.RawContent(body), request.ContentType("application/x-tar"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer loadBody.Close()
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	inspectOut := cli.InspectCmd(c, id, cli.Format(".Id")).Combined()
-	c.Assert(strings.TrimSpace(string(inspectOut)), checker.Equals, id, check.Commentf("load did not work properly"))
+	assert.Equal(c, strings.TrimSpace(inspectOut), id, "load did not work properly")
 }
 
-func (s *DockerSuite) TestAPIImagesDelete(c *check.C) {
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+func (s *DockerSuite) TestAPIImagesDelete(c *testing.T) {
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	if testEnv.OSType != "windows" {
@@ -102,18 +105,18 @@ func (s *DockerSuite) TestAPIImagesDelete(c *check.C) {
 	dockerCmd(c, "tag", name, "test:tag1")
 
 	_, err = cli.ImageRemove(context.Background(), id, types.ImageRemoveOptions{})
-	c.Assert(err.Error(), checker.Contains, "unable to delete")
+	assert.ErrorContains(c, err, "unable to delete")
 
 	_, err = cli.ImageRemove(context.Background(), "test:noexist", types.ImageRemoveOptions{})
-	c.Assert(err.Error(), checker.Contains, "No such image")
+	assert.ErrorContains(c, err, "No such image")
 
 	_, err = cli.ImageRemove(context.Background(), "test:tag1", types.ImageRemoveOptions{})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestAPIImagesHistory(c *check.C) {
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+func (s *DockerSuite) TestAPIImagesHistory(c *testing.T) {
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	if testEnv.OSType != "windows" {
@@ -124,9 +127,9 @@ func (s *DockerSuite) TestAPIImagesHistory(c *check.C) {
 	id := getIDByName(c, name)
 
 	historydata, err := cli.ImageHistory(context.Background(), id)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(historydata, checker.Not(checker.HasLen), 0)
+	assert.Assert(c, len(historydata) != 0)
 	var found bool
 	for _, tag := range historydata[0].Tags {
 		if tag == "test-api-images-history:latest" {
@@ -134,20 +137,22 @@ func (s *DockerSuite) TestAPIImagesHistory(c *check.C) {
 			break
 		}
 	}
-	c.Assert(found, checker.True)
+	assert.Assert(c, found)
 }
 
-func (s *DockerSuite) TestAPIImagesImportBadSrc(c *check.C) {
+func (s *DockerSuite) TestAPIImagesImportBadSrc(c *testing.T) {
 	if runtime.GOOS == "windows" {
+		// Note we parse kernel.GetKernelVersion rather than osversion.Build()
+		// as test binaries aren't manifested, so would otherwise report build 9200.
 		v, err := kernel.GetKernelVersion()
-		c.Assert(err, checker.IsNil)
-		build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
-		if build == 16299 {
+		assert.NilError(c, err)
+		buildNumber, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
+		if buildNumber == osversion.RS3 {
 			c.Skip("Temporarily disabled on RS3 builds")
 		}
 	}
 
-	testRequires(c, Network, SameHostDaemon)
+	testRequires(c, Network, testEnv.IsLocalDaemon)
 
 	server := httptest.NewServer(http.NewServeMux())
 	defer server.Close()
@@ -164,45 +169,45 @@ func (s *DockerSuite) TestAPIImagesImportBadSrc(c *check.C) {
 
 	for _, te := range tt {
 		res, _, err := request.Post(strings.Join([]string{"/images/create?fromSrc=", te.fromSrc}, ""), request.JSON)
-		c.Assert(err, check.IsNil)
-		c.Assert(res.StatusCode, checker.Equals, te.statusExp)
-		c.Assert(res.Header.Get("Content-Type"), checker.Equals, "application/json")
+		assert.NilError(c, err)
+		assert.Equal(c, res.StatusCode, te.statusExp)
+		assert.Equal(c, res.Header.Get("Content-Type"), "application/json")
 	}
 
 }
 
 // #14846
-func (s *DockerSuite) TestAPIImagesSearchJSONContentType(c *check.C) {
+func (s *DockerSuite) TestAPIImagesSearchJSONContentType(c *testing.T) {
 	testRequires(c, Network)
 
 	res, b, err := request.Get("/images/search?term=test", request.JSON)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	b.Close()
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
-	c.Assert(res.Header.Get("Content-Type"), checker.Equals, "application/json")
+	assert.Equal(c, res.StatusCode, http.StatusOK)
+	assert.Equal(c, res.Header.Get("Content-Type"), "application/json")
 }
 
 // Test case for 30027: image size reported as -1 in v1.12 client against v1.13 daemon.
 // This test checks to make sure both v1.12 and v1.13 client against v1.13 daemon get correct `Size` after the fix.
-func (s *DockerSuite) TestAPIImagesSizeCompatibility(c *check.C) {
+func (s *DockerSuite) TestAPIImagesSizeCompatibility(c *testing.T) {
 	apiclient := testEnv.APIClient()
 	defer apiclient.Close()
 
 	images, err := apiclient.ImageList(context.Background(), types.ImageListOptions{})
-	c.Assert(err, checker.IsNil)
-	c.Assert(len(images), checker.Not(checker.Equals), 0)
+	assert.NilError(c, err)
+	assert.Assert(c, len(images) != 0)
 	for _, image := range images {
-		c.Assert(image.Size, checker.Not(checker.Equals), int64(-1))
+		assert.Assert(c, image.Size != int64(-1))
 	}
 
 	apiclient, err = client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.24"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer apiclient.Close()
 
 	v124Images, err := apiclient.ImageList(context.Background(), types.ImageListOptions{})
-	c.Assert(err, checker.IsNil)
-	c.Assert(len(v124Images), checker.Not(checker.Equals), 0)
+	assert.NilError(c, err)
+	assert.Assert(c, len(v124Images) != 0)
 	for _, image := range v124Images {
-		c.Assert(image.Size, checker.Not(checker.Equals), int64(-1))
+		assert.Assert(c, image.Size != int64(-1))
 	}
 }
diff --git a/integration-cli/docker_api_inspect_test.go b/integration-cli/docker_api_inspect_test.go
index 68055b6c145fa..2334bcf60ecf4 100644
--- a/integration-cli/docker_api_inspect_test.go
+++ b/integration-cli/docker_api_inspect_test.go
@@ -4,17 +4,16 @@ import (
 	"context"
 	"encoding/json"
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/versions/v1p20"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
-func (s *DockerSuite) TestInspectAPIContainerResponse(c *check.C) {
+func (s *DockerSuite) TestInspectAPIContainerResponse(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
 
 	cleanedContainerID := strings.TrimSpace(out)
@@ -45,20 +44,20 @@ func (s *DockerSuite) TestInspectAPIContainerResponse(c *check.C) {
 
 		var inspectJSON map[string]interface{}
 		err := json.Unmarshal(body, &inspectJSON)
-		c.Assert(err, checker.IsNil, check.Commentf("Unable to unmarshal body for version %s", cs.version))
+		assert.NilError(c, err, "Unable to unmarshal body for version %s", cs.version)
 
 		for _, key := range cs.keys {
 			_, ok := inspectJSON[key]
-			c.Check(ok, checker.True, check.Commentf("%s does not exist in response for version %s", key, cs.version))
+			assert.Check(c, ok, "%s does not exist in response for version %s", key, cs.version)
 		}
 
 		//Issue #6830: type not properly converted to JSON/back
 		_, ok := inspectJSON["Path"].(bool)
-		c.Assert(ok, checker.False, check.Commentf("Path of `true` should not be converted to boolean `true` via JSON marshalling"))
+		assert.Assert(c, !ok, "Path of `true` should not be converted to boolean `true` via JSON marshalling")
 	}
 }
 
-func (s *DockerSuite) TestInspectAPIContainerVolumeDriverLegacy(c *check.C) {
+func (s *DockerSuite) TestInspectAPIContainerVolumeDriverLegacy(c *testing.T) {
 	// No legacy implications for Windows
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
@@ -71,17 +70,17 @@ func (s *DockerSuite) TestInspectAPIContainerVolumeDriverLegacy(c *check.C) {
 
 		var inspectJSON map[string]interface{}
 		err := json.Unmarshal(body, &inspectJSON)
-		c.Assert(err, checker.IsNil, check.Commentf("Unable to unmarshal body for version %s", version))
+		assert.NilError(c, err, "Unable to unmarshal body for version %s", version)
 
 		config, ok := inspectJSON["Config"]
-		c.Assert(ok, checker.True, check.Commentf("Unable to find 'Config'"))
+		assert.Assert(c, ok, "Unable to find 'Config'")
 		cfg := config.(map[string]interface{})
 		_, ok = cfg["VolumeDriver"]
-		c.Assert(ok, checker.True, check.Commentf("API version %s expected to include VolumeDriver in 'Config'", version))
+		assert.Assert(c, ok, "API version %s expected to include VolumeDriver in 'Config'", version)
 	}
 }
 
-func (s *DockerSuite) TestInspectAPIContainerVolumeDriver(c *check.C) {
+func (s *DockerSuite) TestInspectAPIContainerVolumeDriver(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "--volume-driver", "local", "busybox", "true")
 
 	cleanedContainerID := strings.TrimSpace(out)
@@ -90,37 +89,37 @@ func (s *DockerSuite) TestInspectAPIContainerVolumeDriver(c *check.C) {
 
 	var inspectJSON map[string]interface{}
 	err := json.Unmarshal(body, &inspectJSON)
-	c.Assert(err, checker.IsNil, check.Commentf("Unable to unmarshal body for version 1.25"))
+	assert.NilError(c, err, "Unable to unmarshal body for version 1.25")
 
 	config, ok := inspectJSON["Config"]
-	c.Assert(ok, checker.True, check.Commentf("Unable to find 'Config'"))
+	assert.Assert(c, ok, "Unable to find 'Config'")
 	cfg := config.(map[string]interface{})
 	_, ok = cfg["VolumeDriver"]
-	c.Assert(ok, checker.False, check.Commentf("API version 1.25 expected to not include VolumeDriver in 'Config'"))
+	assert.Assert(c, !ok, "API version 1.25 expected to not include VolumeDriver in 'Config'")
 
 	config, ok = inspectJSON["HostConfig"]
-	c.Assert(ok, checker.True, check.Commentf("Unable to find 'HostConfig'"))
+	assert.Assert(c, ok, "Unable to find 'HostConfig'")
 	cfg = config.(map[string]interface{})
 	_, ok = cfg["VolumeDriver"]
-	c.Assert(ok, checker.True, check.Commentf("API version 1.25 expected to include VolumeDriver in 'HostConfig'"))
+	assert.Assert(c, ok, "API version 1.25 expected to include VolumeDriver in 'HostConfig'")
 }
 
-func (s *DockerSuite) TestInspectAPIImageResponse(c *check.C) {
+func (s *DockerSuite) TestInspectAPIImageResponse(c *testing.T) {
 	dockerCmd(c, "tag", "busybox:latest", "busybox:mytag")
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	imageJSON, _, err := cli.ImageInspectWithRaw(context.Background(), "busybox")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(imageJSON.RepoTags, checker.HasLen, 2)
+	assert.Check(c, len(imageJSON.RepoTags) == 2)
 	assert.Check(c, is.Contains(imageJSON.RepoTags, "busybox:latest"))
 	assert.Check(c, is.Contains(imageJSON.RepoTags, "busybox:mytag"))
 }
 
 // #17131, #17139, #17173
-func (s *DockerSuite) TestInspectAPIEmptyFieldsInConfigPre121(c *check.C) {
+func (s *DockerSuite) TestInspectAPIEmptyFieldsInConfigPre121(c *testing.T) {
 	// Not relevant on Windows
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
@@ -133,18 +132,18 @@ func (s *DockerSuite) TestInspectAPIEmptyFieldsInConfigPre121(c *check.C) {
 
 		var inspectJSON map[string]interface{}
 		err := json.Unmarshal(body, &inspectJSON)
-		c.Assert(err, checker.IsNil, check.Commentf("Unable to unmarshal body for version %s", version))
+		assert.NilError(c, err, "Unable to unmarshal body for version %s", version)
 		config, ok := inspectJSON["Config"]
-		c.Assert(ok, checker.True, check.Commentf("Unable to find 'Config'"))
+		assert.Assert(c, ok, "Unable to find 'Config'")
 		cfg := config.(map[string]interface{})
 		for _, f := range []string{"MacAddress", "NetworkDisabled", "ExposedPorts"} {
 			_, ok := cfg[f]
-			c.Check(ok, checker.True, check.Commentf("API version %s expected to include %s in 'Config'", version, f))
+			assert.Check(c, ok, "API version %s expected to include %s in 'Config'", version, f)
 		}
 	}
 }
 
-func (s *DockerSuite) TestInspectAPIBridgeNetworkSettings120(c *check.C) {
+func (s *DockerSuite) TestInspectAPIBridgeNetworkSettings120(c *testing.T) {
 	// Not relevant on Windows, and besides it doesn't have any bridge network settings
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
@@ -155,13 +154,13 @@ func (s *DockerSuite) TestInspectAPIBridgeNetworkSettings120(c *check.C) {
 
 	var inspectJSON v1p20.ContainerJSON
 	err := json.Unmarshal(body, &inspectJSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	settings := inspectJSON.NetworkSettings
-	c.Assert(settings.IPAddress, checker.Not(checker.HasLen), 0)
+	assert.Assert(c, len(settings.IPAddress) != 0)
 }
 
-func (s *DockerSuite) TestInspectAPIBridgeNetworkSettings121(c *check.C) {
+func (s *DockerSuite) TestInspectAPIBridgeNetworkSettings121(c *testing.T) {
 	// Windows doesn't have any bridge network settings
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
@@ -172,10 +171,10 @@ func (s *DockerSuite) TestInspectAPIBridgeNetworkSettings121(c *check.C) {
 
 	var inspectJSON types.ContainerJSON
 	err := json.Unmarshal(body, &inspectJSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	settings := inspectJSON.NetworkSettings
-	c.Assert(settings.IPAddress, checker.Not(checker.HasLen), 0)
-	c.Assert(settings.Networks["bridge"], checker.Not(checker.IsNil))
-	c.Assert(settings.IPAddress, checker.Equals, settings.Networks["bridge"].IPAddress)
+	assert.Assert(c, len(settings.IPAddress) != 0)
+	assert.Assert(c, settings.Networks["bridge"] != nil)
+	assert.Equal(c, settings.IPAddress, settings.Networks["bridge"].IPAddress)
 }
diff --git a/integration-cli/docker_api_logs_test.go b/integration-cli/docker_api_logs_test.go
index e809b46c2f983..e4a845e70fffc 100644
--- a/integration-cli/docker_api_logs_test.go
+++ b/integration-cli/docker_api_logs_test.go
@@ -6,34 +6,33 @@ import (
 	"context"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net/http"
 	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/pkg/stdcopy"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestLogsAPIWithStdout(c *check.C) {
+func (s *DockerSuite) TestLogsAPIWithStdout(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "-t", "busybox", "/bin/sh", "-c", "while true; do echo hello; sleep 1; done")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	type logOut struct {
 		out string
 		err error
 	}
 
-	chLog := make(chan logOut)
+	chLog := make(chan logOut, 1)
 	res, body, err := request.Get(fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1×tamps=1", id))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 
 	go func() {
 		defer body.Close()
@@ -47,7 +46,7 @@ func (s *DockerSuite) TestLogsAPIWithStdout(c *check.C) {
 
 	select {
 	case l := <-chLog:
-		c.Assert(l.err, checker.IsNil)
+		assert.NilError(c, l.err)
 		if !strings.HasSuffix(l.out, "hello") {
 			c.Fatalf("expected log output to container 'hello', but it does not")
 		}
@@ -56,27 +55,26 @@ func (s *DockerSuite) TestLogsAPIWithStdout(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestLogsAPINoStdoutNorStderr(c *check.C) {
+func (s *DockerSuite) TestLogsAPINoStdoutNorStderr(c *testing.T) {
 	name := "logs_test"
 	dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh")
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	_, err = cli.ContainerLogs(context.Background(), name, types.ContainerLogsOptions{})
-	expected := "Bad parameters: you must choose at least one stream"
-	c.Assert(err.Error(), checker.Contains, expected)
+	assert.ErrorContains(c, err, "Bad parameters: you must choose at least one stream")
 }
 
 // Regression test for #12704
-func (s *DockerSuite) TestLogsAPIFollowEmptyOutput(c *check.C) {
+func (s *DockerSuite) TestLogsAPIFollowEmptyOutput(c *testing.T) {
 	name := "logs_test"
 	t0 := time.Now()
 	dockerCmd(c, "run", "-d", "-t", "--name", name, "busybox", "sleep", "10")
 
 	_, body, err := request.Get(fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1&stderr=1&tail=all", name))
 	t1 := time.Now()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	body.Close()
 	elapsed := t1.Sub(t0).Seconds()
 	if elapsed > 20.0 {
@@ -84,32 +82,32 @@ func (s *DockerSuite) TestLogsAPIFollowEmptyOutput(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestLogsAPIContainerNotFound(c *check.C) {
+func (s *DockerSuite) TestLogsAPIContainerNotFound(c *testing.T) {
 	name := "nonExistentContainer"
 	resp, _, err := request.Get(fmt.Sprintf("/containers/%s/logs?follow=1&stdout=1&stderr=1&tail=all", name))
-	c.Assert(err, checker.IsNil)
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
+	assert.NilError(c, err)
+	assert.Equal(c, resp.StatusCode, http.StatusNotFound)
 }
 
-func (s *DockerSuite) TestLogsAPIUntilFutureFollow(c *check.C) {
+func (s *DockerSuite) TestLogsAPIUntilFutureFollow(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "logsuntilfuturefollow"
 	dockerCmd(c, "run", "-d", "--name", name, "busybox", "/bin/sh", "-c", "while true; do date +%s; sleep 1; done")
-	c.Assert(waitRun(name), checker.IsNil)
+	assert.NilError(c, waitRun(name))
 
 	untilSecs := 5
 	untilDur, err := time.ParseDuration(fmt.Sprintf("%ds", untilSecs))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	until := daemonTime(c).Add(untilDur)
 
-	client, err := client.NewEnvClient()
+	client, err := client.NewClientWithOpts(client.FromEnv)
 	if err != nil {
 		c.Fatal(err)
 	}
 
 	cfg := types.ContainerLogsOptions{Until: until.Format(time.RFC3339Nano), Follow: true, ShowStdout: true, Timestamps: true}
 	reader, err := client.ContainerLogs(context.Background(), name, cfg)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	type logOut struct {
 		out string
@@ -117,6 +115,8 @@ func (s *DockerSuite) TestLogsAPIUntilFutureFollow(c *check.C) {
 	}
 
 	chLog := make(chan logOut)
+	stop := make(chan struct{})
+	defer close(stop)
 
 	go func() {
 		bufReader := bufio.NewReader(reader)
@@ -127,55 +127,64 @@ func (s *DockerSuite) TestLogsAPIUntilFutureFollow(c *check.C) {
 				if err == io.EOF {
 					return
 				}
-				chLog <- logOut{"", err}
+				select {
+				case <-stop:
+					return
+				case chLog <- logOut{"", err}:
+				}
+
 				return
 			}
 
-			chLog <- logOut{strings.TrimSpace(string(out)), err}
+			select {
+			case <-stop:
+				return
+			case chLog <- logOut{strings.TrimSpace(string(out)), err}:
+			}
 		}
 	}()
 
 	for i := 0; i < untilSecs; i++ {
 		select {
 		case l := <-chLog:
-			c.Assert(l.err, checker.IsNil)
+			assert.NilError(c, l.err)
 			i, err := strconv.ParseInt(strings.Split(l.out, " ")[1], 10, 64)
-			c.Assert(err, checker.IsNil)
-			c.Assert(time.Unix(i, 0).UnixNano(), checker.LessOrEqualThan, until.UnixNano())
+			assert.NilError(c, err)
+			assert.Assert(c, time.Unix(i, 0).UnixNano() <= until.UnixNano())
 		case <-time.After(20 * time.Second):
 			c.Fatal("timeout waiting for logs to exit")
 		}
 	}
 }
 
-func (s *DockerSuite) TestLogsAPIUntil(c *check.C) {
+func (s *DockerSuite) TestLogsAPIUntil(c *testing.T) {
 	testRequires(c, MinimumAPIVersion("1.34"))
 	name := "logsuntil"
 	dockerCmd(c, "run", "--name", name, "busybox", "/bin/sh", "-c", "for i in $(seq 1 3); do echo log$i; sleep 1; done")
 
-	client, err := client.NewEnvClient()
+	client, err := client.NewClientWithOpts(client.FromEnv)
 	if err != nil {
 		c.Fatal(err)
 	}
 
-	extractBody := func(c *check.C, cfg types.ContainerLogsOptions) []string {
+	extractBody := func(c *testing.T, cfg types.ContainerLogsOptions) []string {
 		reader, err := client.ContainerLogs(context.Background(), name, cfg)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 
 		actualStdout := new(bytes.Buffer)
-		actualStderr := ioutil.Discard
+		actualStderr := io.Discard
 		_, err = stdcopy.StdCopy(actualStdout, actualStderr, reader)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 
 		return strings.Split(actualStdout.String(), "\n")
 	}
 
 	// Get timestamp of second log line
 	allLogs := extractBody(c, types.ContainerLogsOptions{Timestamps: true, ShowStdout: true})
-	c.Assert(len(allLogs), checker.GreaterOrEqualThan, 3)
+	assert.Assert(c, len(allLogs) >= 3)
 
 	t, err := time.Parse(time.RFC3339Nano, strings.Split(allLogs[1], " ")[0])
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	until := t.Format(time.RFC3339Nano)
 
 	// Get logs until the timestamp of second line, i.e. first two lines
@@ -183,26 +192,26 @@ func (s *DockerSuite) TestLogsAPIUntil(c *check.C) {
 
 	// Ensure log lines after cut-off are excluded
 	logsString := strings.Join(logs, "\n")
-	c.Assert(logsString, checker.Not(checker.Contains), "log3", check.Commentf("unexpected log message returned, until=%v", until))
+	assert.Assert(c, !strings.Contains(logsString, "log3"), "unexpected log message returned, until=%v", until)
 }
 
-func (s *DockerSuite) TestLogsAPIUntilDefaultValue(c *check.C) {
+func (s *DockerSuite) TestLogsAPIUntilDefaultValue(c *testing.T) {
 	name := "logsuntildefaultval"
 	dockerCmd(c, "run", "--name", name, "busybox", "/bin/sh", "-c", "for i in $(seq 1 3); do echo log$i; done")
 
-	client, err := client.NewEnvClient()
+	client, err := client.NewClientWithOpts(client.FromEnv)
 	if err != nil {
 		c.Fatal(err)
 	}
 
-	extractBody := func(c *check.C, cfg types.ContainerLogsOptions) []string {
+	extractBody := func(c *testing.T, cfg types.ContainerLogsOptions) []string {
 		reader, err := client.ContainerLogs(context.Background(), name, cfg)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 
 		actualStdout := new(bytes.Buffer)
-		actualStderr := ioutil.Discard
+		actualStderr := io.Discard
 		_, err = stdcopy.StdCopy(actualStdout, actualStderr, reader)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 
 		return strings.Split(actualStdout.String(), "\n")
 	}
@@ -212,5 +221,5 @@ func (s *DockerSuite) TestLogsAPIUntilDefaultValue(c *check.C) {
 
 	// Test with default value specified and parameter omitted
 	defaultLogs := extractBody(c, types.ContainerLogsOptions{Timestamps: true, ShowStdout: true, Until: "0"})
-	c.Assert(defaultLogs, checker.DeepEquals, allLogs)
+	assert.DeepEqual(c, defaultLogs, allLogs)
 }
diff --git a/integration-cli/docker_api_network_test.go b/integration-cli/docker_api_network_test.go
index 9ec2ba90e870f..716901b8ade94 100644
--- a/integration-cli/docker_api_network_test.go
+++ b/integration-cli/docker_api_network_test.go
@@ -7,26 +7,26 @@ import (
 	"net/http"
 	"net/url"
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/versions"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestAPINetworkGetDefaults(c *check.C) {
+func (s *DockerSuite) TestAPINetworkGetDefaults(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	// By default docker daemon creates 3 networks. check if they are present
 	defaults := []string{"bridge", "host", "none"}
 	for _, nn := range defaults {
-		c.Assert(isNetworkAvailable(c, nn), checker.Equals, true)
+		assert.Assert(c, isNetworkAvailable(c, nn))
 	}
 }
 
-func (s *DockerSuite) TestAPINetworkCreateCheckDuplicate(c *check.C) {
+func (s *DockerSuite) TestAPINetworkCreateCheckDuplicate(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testcheckduplicate"
 	configOnCheck := types.NetworkCreateRequest{
@@ -44,7 +44,7 @@ func (s *DockerSuite) TestAPINetworkCreateCheckDuplicate(c *check.C) {
 
 	// Creating a new network first
 	createNetwork(c, configOnCheck, http.StatusCreated)
-	c.Assert(isNetworkAvailable(c, name), checker.Equals, true)
+	assert.Assert(c, isNetworkAvailable(c, name))
 
 	// Creating another network with same name and CheckDuplicate must fail
 	isOlderAPI := versions.LessThan(testEnv.DaemonAPIVersion(), "1.34")
@@ -64,17 +64,17 @@ func (s *DockerSuite) TestAPINetworkCreateCheckDuplicate(c *check.C) {
 	createNetwork(c, configNotCheck, http.StatusCreated)
 }
 
-func (s *DockerSuite) TestAPINetworkFilter(c *check.C) {
+func (s *DockerSuite) TestAPINetworkFilter(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	nr := getNetworkResource(c, getNetworkIDByName(c, "bridge"))
-	c.Assert(nr.Name, checker.Equals, "bridge")
+	assert.Equal(c, nr.Name, "bridge")
 }
 
-func (s *DockerSuite) TestAPINetworkInspectBridge(c *check.C) {
+func (s *DockerSuite) TestAPINetworkInspectBridge(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	// Inspect default bridge network
 	nr := getNetworkResource(c, "bridge")
-	c.Assert(nr.Name, checker.Equals, "bridge")
+	assert.Equal(c, nr.Name, "bridge")
 
 	// run a container and attach it to the default bridge network
 	out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
@@ -83,19 +83,20 @@ func (s *DockerSuite) TestAPINetworkInspectBridge(c *check.C) {
 
 	// inspect default bridge network again and make sure the container is connected
 	nr = getNetworkResource(c, nr.ID)
-	c.Assert(nr.Driver, checker.Equals, "bridge")
-	c.Assert(nr.Scope, checker.Equals, "local")
-	c.Assert(nr.Internal, checker.Equals, false)
-	c.Assert(nr.EnableIPv6, checker.Equals, false)
-	c.Assert(nr.IPAM.Driver, checker.Equals, "default")
-	c.Assert(nr.Containers[containerID], checker.NotNil)
+	assert.Equal(c, nr.Driver, "bridge")
+	assert.Equal(c, nr.Scope, "local")
+	assert.Equal(c, nr.Internal, false)
+	assert.Equal(c, nr.EnableIPv6, false)
+	assert.Equal(c, nr.IPAM.Driver, "default")
+	_, ok := nr.Containers[containerID]
+	assert.Assert(c, ok)
 
 	ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
-	c.Assert(err, checker.IsNil)
-	c.Assert(ip.String(), checker.Equals, containerIP)
+	assert.NilError(c, err)
+	assert.Equal(c, ip.String(), containerIP)
 }
 
-func (s *DockerSuite) TestAPINetworkInspectUserDefinedNetwork(c *check.C) {
+func (s *DockerSuite) TestAPINetworkInspectUserDefinedNetwork(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	// IPAM configuration inspect
 	ipam := &network.IPAM{
@@ -111,22 +112,22 @@ func (s *DockerSuite) TestAPINetworkInspectUserDefinedNetwork(c *check.C) {
 		},
 	}
 	id0 := createNetwork(c, config, http.StatusCreated)
-	c.Assert(isNetworkAvailable(c, "br0"), checker.Equals, true)
+	assert.Assert(c, isNetworkAvailable(c, "br0"))
 
 	nr := getNetworkResource(c, id0)
-	c.Assert(len(nr.IPAM.Config), checker.Equals, 1)
-	c.Assert(nr.IPAM.Config[0].Subnet, checker.Equals, "172.28.0.0/16")
-	c.Assert(nr.IPAM.Config[0].IPRange, checker.Equals, "172.28.5.0/24")
-	c.Assert(nr.IPAM.Config[0].Gateway, checker.Equals, "172.28.5.254")
-	c.Assert(nr.Options["foo"], checker.Equals, "bar")
-	c.Assert(nr.Options["opts"], checker.Equals, "dopts")
+	assert.Equal(c, len(nr.IPAM.Config), 1)
+	assert.Equal(c, nr.IPAM.Config[0].Subnet, "172.28.0.0/16")
+	assert.Equal(c, nr.IPAM.Config[0].IPRange, "172.28.5.0/24")
+	assert.Equal(c, nr.IPAM.Config[0].Gateway, "172.28.5.254")
+	assert.Equal(c, nr.Options["foo"], "bar")
+	assert.Equal(c, nr.Options["opts"], "dopts")
 
 	// delete the network and make sure it is deleted
 	deleteNetwork(c, id0, true)
-	c.Assert(isNetworkAvailable(c, "br0"), checker.Equals, false)
+	assert.Assert(c, !isNetworkAvailable(c, "br0"))
 }
 
-func (s *DockerSuite) TestAPINetworkConnectDisconnect(c *check.C) {
+func (s *DockerSuite) TestAPINetworkConnectDisconnect(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	// Create test network
 	name := "testnetwork"
@@ -135,9 +136,9 @@ func (s *DockerSuite) TestAPINetworkConnectDisconnect(c *check.C) {
 	}
 	id := createNetwork(c, config, http.StatusCreated)
 	nr := getNetworkResource(c, id)
-	c.Assert(nr.Name, checker.Equals, name)
-	c.Assert(nr.ID, checker.Equals, id)
-	c.Assert(len(nr.Containers), checker.Equals, 0)
+	assert.Equal(c, nr.Name, name)
+	assert.Equal(c, nr.ID, id)
+	assert.Equal(c, len(nr.Containers), 0)
 
 	// run a container
 	out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
@@ -148,26 +149,27 @@ func (s *DockerSuite) TestAPINetworkConnectDisconnect(c *check.C) {
 
 	// inspect the network to make sure container is connected
 	nr = getNetworkResource(c, nr.ID)
-	c.Assert(len(nr.Containers), checker.Equals, 1)
-	c.Assert(nr.Containers[containerID], checker.NotNil)
+	assert.Equal(c, len(nr.Containers), 1)
+	_, ok := nr.Containers[containerID]
+	assert.Assert(c, ok)
 
 	// check if container IP matches network inspect
 	ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	containerIP := findContainerIP(c, "test", "testnetwork")
-	c.Assert(ip.String(), checker.Equals, containerIP)
+	assert.Equal(c, ip.String(), containerIP)
 
 	// disconnect container from the network
 	disconnectNetwork(c, nr.ID, containerID)
 	nr = getNetworkResource(c, nr.ID)
-	c.Assert(nr.Name, checker.Equals, name)
-	c.Assert(len(nr.Containers), checker.Equals, 0)
+	assert.Equal(c, nr.Name, name)
+	assert.Equal(c, len(nr.Containers), 0)
 
 	// delete the network
 	deleteNetwork(c, nr.ID, true)
 }
 
-func (s *DockerSuite) TestAPINetworkIPAMMultipleBridgeNetworks(c *check.C) {
+func (s *DockerSuite) TestAPINetworkIPAMMultipleBridgeNetworks(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	// test0 bridge network
 	ipam0 := &network.IPAM{
@@ -182,7 +184,7 @@ func (s *DockerSuite) TestAPINetworkIPAMMultipleBridgeNetworks(c *check.C) {
 		},
 	}
 	id0 := createNetwork(c, config0, http.StatusCreated)
-	c.Assert(isNetworkAvailable(c, "test0"), checker.Equals, true)
+	assert.Assert(c, isNetworkAvailable(c, "test0"))
 
 	ipam1 := &network.IPAM{
 		Driver: "default",
@@ -201,7 +203,7 @@ func (s *DockerSuite) TestAPINetworkIPAMMultipleBridgeNetworks(c *check.C) {
 	} else {
 		createNetwork(c, config1, http.StatusForbidden)
 	}
-	c.Assert(isNetworkAvailable(c, "test1"), checker.Equals, false)
+	assert.Assert(c, !isNetworkAvailable(c, "test1"))
 
 	ipam2 := &network.IPAM{
 		Driver: "default",
@@ -216,34 +218,34 @@ func (s *DockerSuite) TestAPINetworkIPAMMultipleBridgeNetworks(c *check.C) {
 		},
 	}
 	createNetwork(c, config2, http.StatusCreated)
-	c.Assert(isNetworkAvailable(c, "test2"), checker.Equals, true)
+	assert.Assert(c, isNetworkAvailable(c, "test2"))
 
 	// remove test0 and retry to create test1
 	deleteNetwork(c, id0, true)
 	createNetwork(c, config1, http.StatusCreated)
-	c.Assert(isNetworkAvailable(c, "test1"), checker.Equals, true)
+	assert.Assert(c, isNetworkAvailable(c, "test1"))
 
 	// for networks w/o ipam specified, docker will choose proper non-overlapping subnets
 	createNetwork(c, types.NetworkCreateRequest{Name: "test3"}, http.StatusCreated)
-	c.Assert(isNetworkAvailable(c, "test3"), checker.Equals, true)
+	assert.Assert(c, isNetworkAvailable(c, "test3"))
 	createNetwork(c, types.NetworkCreateRequest{Name: "test4"}, http.StatusCreated)
-	c.Assert(isNetworkAvailable(c, "test4"), checker.Equals, true)
+	assert.Assert(c, isNetworkAvailable(c, "test4"))
 	createNetwork(c, types.NetworkCreateRequest{Name: "test5"}, http.StatusCreated)
-	c.Assert(isNetworkAvailable(c, "test5"), checker.Equals, true)
+	assert.Assert(c, isNetworkAvailable(c, "test5"))
 
 	for i := 1; i < 6; i++ {
 		deleteNetwork(c, fmt.Sprintf("test%d", i), true)
 	}
 }
 
-func (s *DockerSuite) TestAPICreateDeletePredefinedNetworks(c *check.C) {
+func (s *DockerSuite) TestAPICreateDeletePredefinedNetworks(c *testing.T) {
 	testRequires(c, DaemonIsLinux, SwarmInactive)
 	createDeletePredefinedNetwork(c, "bridge")
 	createDeletePredefinedNetwork(c, "none")
 	createDeletePredefinedNetwork(c, "host")
 }
 
-func createDeletePredefinedNetwork(c *check.C, name string) {
+func createDeletePredefinedNetwork(c *testing.T, name string) {
 	// Create pre-defined network
 	config := types.NetworkCreateRequest{
 		Name: name,
@@ -265,15 +267,15 @@ func createDeletePredefinedNetwork(c *check.C, name string) {
 	deleteNetwork(c, name, false)
 }
 
-func isNetworkAvailable(c *check.C, name string) bool {
+func isNetworkAvailable(c *testing.T, name string) bool {
 	resp, body, err := request.Get("/networks")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer resp.Body.Close()
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
+	assert.Equal(c, resp.StatusCode, http.StatusOK)
 
 	var nJSON []types.NetworkResource
 	err = json.NewDecoder(body).Decode(&nJSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	for _, n := range nJSON {
 		if n.Name == name {
@@ -283,23 +285,23 @@ func isNetworkAvailable(c *check.C, name string) bool {
 	return false
 }
 
-func getNetworkIDByName(c *check.C, name string) string {
+func getNetworkIDByName(c *testing.T, name string) string {
 	var (
 		v          = url.Values{}
 		filterArgs = filters.NewArgs()
 	)
 	filterArgs.Add("name", name)
 	filterJSON, err := filters.ToJSON(filterArgs)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	v.Set("filters", filterJSON)
 
 	resp, body, err := request.Get("/networks?" + v.Encode())
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
-	c.Assert(err, checker.IsNil)
+	assert.Equal(c, resp.StatusCode, http.StatusOK)
+	assert.NilError(c, err)
 
 	var nJSON []types.NetworkResource
 	err = json.NewDecoder(body).Decode(&nJSON)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	var res string
 	for _, n := range nJSON {
 		// Find exact match
@@ -307,70 +309,70 @@ func getNetworkIDByName(c *check.C, name string) string {
 			res = n.ID
 		}
 	}
-	c.Assert(res, checker.Not(checker.Equals), "")
+	assert.Assert(c, res != "")
 
 	return res
 }
 
-func getNetworkResource(c *check.C, id string) *types.NetworkResource {
+func getNetworkResource(c *testing.T, id string) *types.NetworkResource {
 	_, obj, err := request.Get("/networks/" + id)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	nr := types.NetworkResource{}
 	err = json.NewDecoder(obj).Decode(&nr)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	return &nr
 }
 
-func createNetwork(c *check.C, config types.NetworkCreateRequest, expectedStatusCode int) string {
+func createNetwork(c *testing.T, config types.NetworkCreateRequest, expectedStatusCode int) string {
 	resp, body, err := request.Post("/networks/create", request.JSONBody(config))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer resp.Body.Close()
 
 	if expectedStatusCode >= 0 {
-		c.Assert(resp.StatusCode, checker.Equals, expectedStatusCode)
+		assert.Equal(c, resp.StatusCode, expectedStatusCode)
 	} else {
-		c.Assert(resp.StatusCode, checker.Not(checker.Equals), -expectedStatusCode)
+		assert.Assert(c, resp.StatusCode != -expectedStatusCode)
 	}
 
 	if expectedStatusCode == http.StatusCreated || expectedStatusCode < 0 {
 		var nr types.NetworkCreateResponse
 		err = json.NewDecoder(body).Decode(&nr)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 
 		return nr.ID
 	}
 	return ""
 }
 
-func connectNetwork(c *check.C, nid, cid string) {
+func connectNetwork(c *testing.T, nid, cid string) {
 	config := types.NetworkConnect{
 		Container: cid,
 	}
 
 	resp, _, err := request.Post("/networks/"+nid+"/connect", request.JSONBody(config))
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
-	c.Assert(err, checker.IsNil)
+	assert.Equal(c, resp.StatusCode, http.StatusOK)
+	assert.NilError(c, err)
 }
 
-func disconnectNetwork(c *check.C, nid, cid string) {
+func disconnectNetwork(c *testing.T, nid, cid string) {
 	config := types.NetworkConnect{
 		Container: cid,
 	}
 
 	resp, _, err := request.Post("/networks/"+nid+"/disconnect", request.JSONBody(config))
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
-	c.Assert(err, checker.IsNil)
+	assert.Equal(c, resp.StatusCode, http.StatusOK)
+	assert.NilError(c, err)
 }
 
-func deleteNetwork(c *check.C, id string, shouldSucceed bool) {
+func deleteNetwork(c *testing.T, id string, shouldSucceed bool) {
 	resp, _, err := request.Delete("/networks/" + id)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer resp.Body.Close()
 	if !shouldSucceed {
-		c.Assert(resp.StatusCode, checker.Not(checker.Equals), http.StatusOK)
+		assert.Assert(c, resp.StatusCode != http.StatusOK)
 		return
 	}
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusNoContent)
+	assert.Equal(c, resp.StatusCode, http.StatusNoContent)
 }
diff --git a/integration-cli/docker_api_stats_test.go b/integration-cli/docker_api_stats_test.go
index 3954e4b2e005f..bbb37bbd977b6 100644
--- a/integration-cli/docker_api_stats_test.go
+++ b/integration-cli/docker_api_stats_test.go
@@ -10,31 +10,34 @@ import (
 	"strconv"
 	"strings"
 	"sync"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
 var expectedNetworkInterfaceStats = strings.Split("rx_bytes rx_dropped rx_errors rx_packets tx_bytes tx_dropped tx_errors tx_packets", " ")
 
-func (s *DockerSuite) TestAPIStatsNoStreamGetCpu(c *check.C) {
+func (s *DockerSuite) TestAPIStatsNoStreamGetCpu(c *testing.T) {
+	skip.If(c, RuntimeIsWindowsContainerd(), "FIXME: Broken on Windows + containerd combination")
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "while true;usleep 100; do echo 'Hello'; done")
 
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 	resp, body, err := request.Get(fmt.Sprintf("/containers/%s/stats?stream=false", id))
-	c.Assert(err, checker.IsNil)
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
-	c.Assert(resp.Header.Get("Content-Type"), checker.Equals, "application/json")
+	assert.NilError(c, err)
+	assert.Equal(c, resp.StatusCode, http.StatusOK)
+	assert.Equal(c, resp.Header.Get("Content-Type"), "application/json")
+	assert.Equal(c, resp.Header.Get("Content-Type"), "application/json")
 
 	var v *types.Stats
 	err = json.NewDecoder(body).Decode(&v)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	body.Close()
 
 	var cpuPercent = 0.0
@@ -58,34 +61,34 @@ func (s *DockerSuite) TestAPIStatsNoStreamGetCpu(c *check.C) {
 		}
 	}
 
-	c.Assert(cpuPercent, check.Not(checker.Equals), 0.0, check.Commentf("docker stats with no-stream get cpu usage failed: was %v", cpuPercent))
+	assert.Assert(c, cpuPercent != 0.0, "docker stats with no-stream get cpu usage failed: was %v", cpuPercent)
 }
 
-func (s *DockerSuite) TestAPIStatsStoppedContainerInGoroutines(c *check.C) {
+func (s *DockerSuite) TestAPIStatsStoppedContainerInGoroutines(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo 1")
 	id := strings.TrimSpace(out)
 
 	getGoRoutines := func() int {
-		_, body, err := request.Get(fmt.Sprintf("/info"))
-		c.Assert(err, checker.IsNil)
+		_, body, err := request.Get("/info")
+		assert.NilError(c, err)
 		info := types.Info{}
 		err = json.NewDecoder(body).Decode(&info)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		body.Close()
 		return info.NGoroutines
 	}
 
 	// When the HTTP connection is closed, the number of goroutines should not increase.
 	routines := getGoRoutines()
-	_, body, err := request.Get(fmt.Sprintf("/containers/%s/stats", id))
-	c.Assert(err, checker.IsNil)
+	_, body, err := request.Get("/containers/" + id + "/stats")
+	assert.NilError(c, err)
 	body.Close()
 
 	t := time.After(30 * time.Second)
 	for {
 		select {
 		case <-t:
-			c.Assert(getGoRoutines(), checker.LessOrEqualThan, routines)
+			assert.Assert(c, getGoRoutines() <= routines)
 			return
 		default:
 			if n := getGoRoutines(); n <= routines {
@@ -96,12 +99,13 @@ func (s *DockerSuite) TestAPIStatsStoppedContainerInGoroutines(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestAPIStatsNetworkStats(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestAPIStatsNetworkStats(c *testing.T) {
+	skip.If(c, RuntimeIsWindowsContainerd(), "FIXME: Broken on Windows + containerd combination")
+	testRequires(c, testEnv.IsLocalDaemon)
 
 	out := runSleepingContainer(c)
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	// Retrieve the container address
 	net := "bridge"
@@ -141,7 +145,7 @@ func (s *DockerSuite) TestAPIStatsNetworkStats(c *check.C) {
 			err = err2
 		}
 	}
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	pingouts := string(pingout[:])
 	nwStatsPost := getNetworkStats(c, id)
 	for _, v := range nwStatsPost {
@@ -157,19 +161,17 @@ func (s *DockerSuite) TestAPIStatsNetworkStats(c *check.C) {
 		expRxPkts++
 		expTxPkts++
 	}
-	c.Assert(postTxPackets, checker.GreaterOrEqualThan, expTxPkts,
-		check.Commentf("Reported less TxPackets than expected. Expected >= %d. Found %d. %s", expTxPkts, postTxPackets, pingouts))
-	c.Assert(postRxPackets, checker.GreaterOrEqualThan, expRxPkts,
-		check.Commentf("Reported less RxPackets than expected. Expected >= %d. Found %d. %s", expRxPkts, postRxPackets, pingouts))
+	assert.Assert(c, postTxPackets >= expTxPkts, "Reported less TxPackets than expected. Expected >= %d. Found %d. %s", expTxPkts, postTxPackets, pingouts)
+	assert.Assert(c, postRxPackets >= expRxPkts, "Reported less RxPackets than expected. Expected >= %d. Found %d. %s", expRxPkts, postRxPackets, pingouts)
 }
 
-func (s *DockerSuite) TestAPIStatsNetworkStatsVersioning(c *check.C) {
+func (s *DockerSuite) TestAPIStatsNetworkStatsVersioning(c *testing.T) {
 	// Windows doesn't support API versions less than 1.25, so no point testing 1.17 .. 1.21
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	out := runSleepingContainer(c)
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 	wg := sync.WaitGroup{}
 
 	for i := 17; i <= 21; i++ {
@@ -179,25 +181,23 @@ func (s *DockerSuite) TestAPIStatsNetworkStatsVersioning(c *check.C) {
 			apiVersion := fmt.Sprintf("v1.%d", i)
 			statsJSONBlob := getVersionedStats(c, id, apiVersion)
 			if versions.LessThan(apiVersion, "v1.21") {
-				c.Assert(jsonBlobHasLTv121NetworkStats(statsJSONBlob), checker.Equals, true,
-					check.Commentf("Stats JSON blob from API %s %#v does not look like a =v1.21 API stats structure", apiVersion, statsJSONBlob))
+				assert.Assert(c, jsonBlobHasGTE121NetworkStats(statsJSONBlob), "Stats JSON blob from API %s %#v does not look like a >=v1.21 API stats structure", apiVersion, statsJSONBlob)
 			}
 		}(i)
 	}
 	wg.Wait()
 }
 
-func getNetworkStats(c *check.C, id string) map[string]types.NetworkStats {
+func getNetworkStats(c *testing.T, id string) map[string]types.NetworkStats {
 	var st *types.StatsJSON
 
-	_, body, err := request.Get(fmt.Sprintf("/containers/%s/stats?stream=false", id))
-	c.Assert(err, checker.IsNil)
+	_, body, err := request.Get("/containers/" + id + "/stats?stream=false")
+	assert.NilError(c, err)
 
 	err = json.NewDecoder(body).Decode(&st)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	body.Close()
 
 	return st.Networks
@@ -207,15 +207,15 @@ func getNetworkStats(c *check.C, id string) map[string]types.NetworkStats {
 // container with id using an API call with version apiVersion. Since the
 // stats result type differs between API versions, we simply return
 // map[string]interface{}.
-func getVersionedStats(c *check.C, id string, apiVersion string) map[string]interface{} {
+func getVersionedStats(c *testing.T, id string, apiVersion string) map[string]interface{} {
 	stats := make(map[string]interface{})
 
-	_, body, err := request.Get(fmt.Sprintf("/%s/containers/%s/stats?stream=false", apiVersion, id))
-	c.Assert(err, checker.IsNil)
+	_, body, err := request.Get("/" + apiVersion + "/containers/" + id + "/stats?stream=false")
+	assert.NilError(c, err)
 	defer body.Close()
 
 	err = json.NewDecoder(body).Decode(&stats)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to decode stat: %s", err))
+	assert.NilError(c, err, "failed to decode stat: %s", err)
 
 	return stats
 }
@@ -260,34 +260,34 @@ func jsonBlobHasGTE121NetworkStats(blob map[string]interface{}) bool {
 	return true
 }
 
-func (s *DockerSuite) TestAPIStatsContainerNotFound(c *check.C) {
+func (s *DockerSuite) TestAPIStatsContainerNotFound(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	expected := "No such container: nonexistent"
 
 	_, err = cli.ContainerStats(context.Background(), "nonexistent", true)
-	c.Assert(err.Error(), checker.Contains, expected)
+	assert.ErrorContains(c, err, expected)
 	_, err = cli.ContainerStats(context.Background(), "nonexistent", false)
-	c.Assert(err.Error(), checker.Contains, expected)
+	assert.ErrorContains(c, err, expected)
 }
 
-func (s *DockerSuite) TestAPIStatsNoStreamConnectedContainers(c *check.C) {
+func (s *DockerSuite) TestAPIStatsNoStreamConnectedContainers(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	out1 := runSleepingContainer(c)
 	id1 := strings.TrimSpace(out1)
-	c.Assert(waitRun(id1), checker.IsNil)
+	assert.NilError(c, waitRun(id1))
 
 	out2 := runSleepingContainer(c, "--net", "container:"+id1)
 	id2 := strings.TrimSpace(out2)
-	c.Assert(waitRun(id2), checker.IsNil)
+	assert.NilError(c, waitRun(id2))
 
 	ch := make(chan error, 1)
 	go func() {
-		resp, body, err := request.Get(fmt.Sprintf("/containers/%s/stats?stream=false", id2))
+		resp, body, err := request.Get("/containers/" + id2 + "/stats?stream=false")
 		defer body.Close()
 		if err != nil {
 			ch <- err
@@ -307,7 +307,7 @@ func (s *DockerSuite) TestAPIStatsNoStreamConnectedContainers(c *check.C) {
 
 	select {
 	case err := <-ch:
-		c.Assert(err, checker.IsNil, check.Commentf("Error in stats Engine API: %v", err))
+		assert.NilError(c, err, "Error in stats Engine API: %v", err)
 	case <-time.After(15 * time.Second):
 		c.Fatalf("Stats did not return after timeout")
 	}
diff --git a/integration-cli/docker_api_swarm_node_test.go b/integration-cli/docker_api_swarm_node_test.go
index 191391620d7f0..e883a44d07b98 100644
--- a/integration-cli/docker_api_swarm_node_test.go
+++ b/integration-cli/docker_api_swarm_node_test.go
@@ -1,23 +1,27 @@
+//go:build !windows
 // +build !windows
 
 package main
 
 import (
+	"fmt"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/daemon"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
 )
 
-func (s *DockerSwarmSuite) TestAPISwarmListNodes(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmListNodes(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, false)
 	d3 := s.AddDaemon(c, true, false)
 
 	nodes := d1.ListNodes(c)
-	c.Assert(len(nodes), checker.Equals, 3, check.Commentf("nodes: %#v", nodes))
+	assert.Equal(c, len(nodes), 3, fmt.Sprintf("nodes: %#v", nodes))
 
 loop0:
 	for _, n := range nodes {
@@ -30,7 +34,7 @@ loop0:
 	}
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmNodeUpdate(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmNodeUpdate(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	nodes := d.ListNodes(c)
@@ -40,17 +44,17 @@ func (s *DockerSwarmSuite) TestAPISwarmNodeUpdate(c *check.C) {
 	})
 
 	n := d.GetNode(c, nodes[0].ID)
-	c.Assert(n.Spec.Availability, checker.Equals, swarm.NodeAvailabilityPause)
+	assert.Equal(c, n.Spec.Availability, swarm.NodeAvailabilityPause)
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmNodeRemove(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmNodeRemove(c *testing.T) {
 	testRequires(c, Network)
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, false)
 	_ = s.AddDaemon(c, true, false)
 
 	nodes := d1.ListNodes(c)
-	c.Assert(len(nodes), checker.Equals, 3, check.Commentf("nodes: %#v", nodes))
+	assert.Equal(c, len(nodes), 3, fmt.Sprintf("nodes: %#v", nodes))
 
 	// Getting the info so we can take the NodeID
 	d2Info := d2.SwarmInfo(c)
@@ -59,39 +63,39 @@ func (s *DockerSwarmSuite) TestAPISwarmNodeRemove(c *check.C) {
 	d1.RemoveNode(c, d2Info.NodeID, true)
 
 	nodes = d1.ListNodes(c)
-	c.Assert(len(nodes), checker.Equals, 2, check.Commentf("nodes: %#v", nodes))
+	assert.Equal(c, len(nodes), 2, fmt.Sprintf("nodes: %#v", nodes))
 
 	// Restart the node that was removed
-	d2.Restart(c)
+	d2.RestartNode(c)
 
 	// Give some time for the node to rejoin
 	time.Sleep(1 * time.Second)
 
 	// Make sure the node didn't rejoin
 	nodes = d1.ListNodes(c)
-	c.Assert(len(nodes), checker.Equals, 2, check.Commentf("nodes: %#v", nodes))
+	assert.Equal(c, len(nodes), 2, fmt.Sprintf("nodes: %#v", nodes))
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmNodeDrainPause(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmNodeDrainPause(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, false)
 
 	time.Sleep(1 * time.Second) // make sure all daemons are ready to accept tasks
 
 	// start a service, expect balanced distribution
-	instances := 8
+	instances := 2
 	id := d1.CreateService(c, simpleTestService, setInstances(instances))
 
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.GreaterThan, 0)
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckActiveContainerCount, checker.GreaterThan, 0)
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, d1.CheckActiveContainerCount, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, d2.CheckActiveContainerCount, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// drain d2, all containers should move to d1
 	d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
 		n.Spec.Availability = swarm.NodeAvailabilityDrain
 	})
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.Equals, instances)
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckActiveContainerCount, checker.Equals, 0)
+	poll.WaitOn(c, pollCheck(c, d1.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, d2.CheckActiveContainerCount, checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// set d2 back to active
 	d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
@@ -100,16 +104,15 @@ func (s *DockerSwarmSuite) TestAPISwarmNodeDrainPause(c *check.C) {
 
 	instances = 1
 	d1.UpdateService(c, d1.GetService(c, id), setInstances(instances))
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout*2))
 
-	waitAndAssert(c, defaultReconciliationTimeout*2, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals, instances)
-
-	instances = 8
+	instances = 2
 	d1.UpdateService(c, d1.GetService(c, id), setInstances(instances))
 
 	// drained node first so we don't get any old containers
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckActiveContainerCount, checker.GreaterThan, 0)
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.GreaterThan, 0)
-	waitAndAssert(c, defaultReconciliationTimeout*2, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, d2.CheckActiveContainerCount, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, d1.CheckActiveContainerCount, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout*2))
 
 	d2ContainerCount := len(d2.ActiveContainers(c))
 
@@ -118,10 +121,9 @@ func (s *DockerSwarmSuite) TestAPISwarmNodeDrainPause(c *check.C) {
 		n.Spec.Availability = swarm.NodeAvailabilityPause
 	})
 
-	instances = 14
+	instances = 4
 	d1.UpdateService(c, d1.GetService(c, id), setInstances(instances))
-
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.Equals, instances-d2ContainerCount)
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckActiveContainerCount, checker.Equals, d2ContainerCount)
+	poll.WaitOn(c, pollCheck(c, d1.CheckActiveContainerCount, checker.Equals(instances-d2ContainerCount)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, d2.CheckActiveContainerCount, checker.Equals(d2ContainerCount)), poll.WithTimeout(defaultReconciliationTimeout))
 
 }
diff --git a/integration-cli/docker_api_swarm_service_test.go b/integration-cli/docker_api_swarm_service_test.go
index 4d39a34b09a5d..1bba494e69275 100644
--- a/integration-cli/docker_api_swarm_service_test.go
+++ b/integration-cli/docker_api_swarm_service_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -7,6 +8,7 @@ import (
 	"fmt"
 	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
@@ -15,10 +17,11 @@ import (
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
 	"github.com/docker/docker/integration-cli/daemon"
-	testdaemon "github.com/docker/docker/internal/test/daemon"
-	"github.com/go-check/check"
+	testdaemon "github.com/docker/docker/testutil/daemon"
 	"golang.org/x/sys/unix"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/poll"
 )
 
 func setPortConfig(portConfig []swarm.PortConfig) testdaemon.ServiceConstructor {
@@ -30,13 +33,13 @@ func setPortConfig(portConfig []swarm.PortConfig) testdaemon.ServiceConstructor
 	}
 }
 
-func (s *DockerSwarmSuite) TestAPIServiceUpdatePort(c *check.C) {
+func (s *DockerSwarmSuite) TestAPIServiceUpdatePort(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a service with a port mapping of 8080:8081.
 	portConfig := []swarm.PortConfig{{TargetPort: 8081, PublishedPort: 8080}}
 	serviceID := d.CreateService(c, simpleTestService, setInstances(1), setPortConfig(portConfig))
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// Update the service: changed the port mapping from 8080:8081 to 8082:8083.
 	updatedPortConfig := []swarm.PortConfig{{TargetPort: 8083, PublishedPort: 8082}}
@@ -45,55 +48,54 @@ func (s *DockerSwarmSuite) TestAPIServiceUpdatePort(c *check.C) {
 
 	// Inspect the service and verify port mapping.
 	updatedService := d.GetService(c, serviceID)
-	c.Assert(updatedService.Spec.EndpointSpec, check.NotNil)
-	c.Assert(len(updatedService.Spec.EndpointSpec.Ports), check.Equals, 1)
-	c.Assert(updatedService.Spec.EndpointSpec.Ports[0].TargetPort, check.Equals, uint32(8083))
-	c.Assert(updatedService.Spec.EndpointSpec.Ports[0].PublishedPort, check.Equals, uint32(8082))
+	assert.Assert(c, updatedService.Spec.EndpointSpec != nil)
+	assert.Equal(c, len(updatedService.Spec.EndpointSpec.Ports), 1)
+	assert.Equal(c, updatedService.Spec.EndpointSpec.Ports[0].TargetPort, uint32(8083))
+	assert.Equal(c, updatedService.Spec.EndpointSpec.Ports[0].PublishedPort, uint32(8082))
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServicesEmptyList(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServicesEmptyList(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	services := d.ListServices(c)
-	c.Assert(services, checker.NotNil)
-	c.Assert(len(services), checker.Equals, 0, check.Commentf("services: %#v", services))
+	assert.Assert(c, services != nil)
+	assert.Assert(c, len(services) == 0, "services: %#v", services)
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServicesCreate(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServicesCreate(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	instances := 2
 	id := d.CreateService(c, simpleTestService, setInstances(instances))
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
-	cli, err := d.NewClient()
-	c.Assert(err, checker.IsNil)
-	defer cli.Close()
+	client := d.NewClientT(c)
+	defer client.Close()
 
 	options := types.ServiceInspectOptions{InsertDefaults: true}
 
 	// insertDefaults inserts UpdateConfig when service is fetched by ID
-	resp, _, err := cli.ServiceInspectWithRaw(context.Background(), id, options)
+	resp, _, err := client.ServiceInspectWithRaw(context.Background(), id, options)
 	out := fmt.Sprintf("%+v", resp)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "UpdateConfig")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, "UpdateConfig"))
 
 	// insertDefaults inserts UpdateConfig when service is fetched by ID
-	resp, _, err = cli.ServiceInspectWithRaw(context.Background(), "top", options)
+	resp, _, err = client.ServiceInspectWithRaw(context.Background(), "top", options)
 	out = fmt.Sprintf("%+v", resp)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(out), checker.Contains, "UpdateConfig")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, "UpdateConfig"))
 
 	service := d.GetService(c, id)
 	instances = 5
 	d.UpdateService(c, service, setInstances(instances))
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	d.RemoveService(c, service.ID)
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 0)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout))
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServicesMultipleAgents(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServicesMultipleAgents(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, false)
 	d3 := s.AddDaemon(c, true, false)
@@ -103,50 +105,50 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesMultipleAgents(c *check.C) {
 	instances := 9
 	id := d1.CreateService(c, simpleTestService, setInstances(instances))
 
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.GreaterThan, 0)
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckActiveContainerCount, checker.GreaterThan, 0)
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckActiveContainerCount, checker.GreaterThan, 0)
+	poll.WaitOn(c, pollCheck(c, d1.CheckActiveContainerCount, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, d2.CheckActiveContainerCount, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, d3.CheckActiveContainerCount, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout))
 
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// reconciliation on d2 node down
 	d2.Stop(c)
 
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// test downscaling
 	instances = 5
 	d1.UpdateService(c, d1.GetService(c, id), setInstances(instances))
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServicesCreateGlobal(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServicesCreateGlobal(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, false)
 	d3 := s.AddDaemon(c, true, false)
 
 	d1.CreateService(c, simpleTestService, setGlobalMode)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.Equals, 1)
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckActiveContainerCount, checker.Equals, 1)
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d1.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, d2.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, d3.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	d4 := s.AddDaemon(c, true, false)
 	d5 := s.AddDaemon(c, true, false)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d4.CheckActiveContainerCount, checker.Equals, 1)
-	waitAndAssert(c, defaultReconciliationTimeout, d5.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d4.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, d5.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServicesUpdate(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServicesUpdate(c *testing.T) {
 	const nodeCount = 3
 	var daemons [nodeCount]*daemon.Daemon
 	for i := 0; i < nodeCount; i++ {
 		daemons[i] = s.AddDaemon(c, true, i == 0)
 	}
 	// wait for nodes ready
-	waitAndAssert(c, 5*time.Second, daemons[0].CheckNodeReadyCount, checker.Equals, nodeCount)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckNodeReadyCount, checker.Equals(nodeCount)), poll.WithTimeout(5*time.Second))
 
 	// service image at start
 	image1 := "busybox:latest"
@@ -156,7 +158,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdate(c *check.C) {
 	// create a different tag
 	for _, d := range daemons {
 		out, err := d.Cmd("tag", image1, image2)
-		c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+		assert.NilError(c, err, out)
 	}
 
 	// create service
@@ -166,40 +168,35 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdate(c *check.C) {
 	id := daemons[0].CreateService(c, serviceForUpdate, setInstances(instances))
 
 	// wait for tasks ready
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances})
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// issue service update
 	service := daemons[0].GetService(c, id)
 	daemons[0].UpdateService(c, service, setImage(image2))
 
 	// first batch
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances - parallelism, image2: parallelism})
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - parallelism, image2: parallelism})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// 2nd batch
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// 3nd batch
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image2: instances})
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image2: instances})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// Roll back to the previous version. This uses the CLI because
 	// rollback used to be a client-side operation.
 	out, err := daemons[0].Cmd("service", "update", "--detach", "--rollback", id)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// first batch
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image2: instances - rollbackParallelism, image1: rollbackParallelism})
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image2: instances - rollbackParallelism, image1: rollbackParallelism})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// 2nd batch
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances})
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout))
+
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateStartFirst(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateStartFirst(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// service image at start
@@ -223,7 +220,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateStartFirst(c *check.C) {
 
 	checkStartingTasks := func(expected int) []swarm.Task {
 		var startingTasks []swarm.Task
-		waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+		poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 			tasks := d.GetServiceTasks(c, id)
 			startingTasks = nil
 			for _, t := range tasks {
@@ -231,8 +228,8 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateStartFirst(c *check.C) {
 					startingTasks = append(startingTasks, t)
 				}
 			}
-			return startingTasks, nil
-		}, checker.HasLen, expected)
+			return startingTasks, ""
+		}, checker.HasLen(expected)), poll.WithTimeout(defaultReconciliationTimeout))
 
 		return startingTasks
 	}
@@ -245,8 +242,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateStartFirst(c *check.C) {
 	}
 
 	// wait for tasks ready
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// issue service update
 	service := d.GetService(c, id)
@@ -257,65 +253,58 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateStartFirst(c *check.C) {
 	// The old tasks should be running, and the new ones should be starting.
 	startingTasks := checkStartingTasks(parallelism)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// make it healthy
 	makeTasksHealthy(startingTasks)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances - parallelism, image2: parallelism})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - parallelism, image2: parallelism})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// 2nd batch
 
 	// The old tasks should be running, and the new ones should be starting.
 	startingTasks = checkStartingTasks(parallelism)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances - parallelism, image2: parallelism})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - parallelism, image2: parallelism})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// make it healthy
 	makeTasksHealthy(startingTasks)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// 3nd batch
 
 	// The old tasks should be running, and the new ones should be starting.
 	startingTasks = checkStartingTasks(1)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// make it healthy
 	makeTasksHealthy(startingTasks)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image2: instances})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image2: instances})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// Roll back to the previous version. This uses the CLI because
 	// rollback is a client-side operation.
 	out, err := d.Cmd("service", "update", "--detach", "--rollback", id)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// first batch
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image2: instances - rollbackParallelism, image1: rollbackParallelism})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image2: instances - rollbackParallelism, image1: rollbackParallelism})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// 2nd batch
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout))
+
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServicesFailedUpdate(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServicesFailedUpdate(c *testing.T) {
 	const nodeCount = 3
 	var daemons [nodeCount]*daemon.Daemon
 	for i := 0; i < nodeCount; i++ {
 		daemons[i] = s.AddDaemon(c, true, i == 0)
 	}
 	// wait for nodes ready
-	waitAndAssert(c, 5*time.Second, daemons[0].CheckNodeReadyCount, checker.Equals, nodeCount)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckNodeReadyCount, checker.Equals(nodeCount)), poll.WithTimeout(5*time.Second))
 
 	// service image at start
 	image1 := "busybox:latest"
@@ -327,89 +316,88 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesFailedUpdate(c *check.C) {
 	id := daemons[0].CreateService(c, serviceForUpdate, setInstances(instances))
 
 	// wait for tasks ready
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances})
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// issue service update
 	service := daemons[0].GetService(c, id)
 	daemons[0].UpdateService(c, service, setImage(image2), setFailureAction(swarm.UpdateFailureActionPause), setMaxFailureRatio(0.25), setParallelism(1))
 
 	// should update 2 tasks and then pause
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckServiceUpdateState(id), checker.Equals, swarm.UpdateStatePaused)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceUpdateState(id), checker.Equals(swarm.UpdateStatePaused)), poll.WithTimeout(defaultReconciliationTimeout))
 	v, _ := daemons[0].CheckServiceRunningTasks(id)(c)
-	c.Assert(v, checker.Equals, instances-2)
+	assert.Assert(c, v == instances-2)
 
 	// Roll back to the previous version. This uses the CLI because
 	// rollback used to be a client-side operation.
 	out, err := daemons[0].Cmd("service", "update", "--detach", "--rollback", id)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
+
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout))
 
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image1: instances})
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintRole(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintRole(c *testing.T) {
 	const nodeCount = 3
 	var daemons [nodeCount]*daemon.Daemon
 	for i := 0; i < nodeCount; i++ {
 		daemons[i] = s.AddDaemon(c, true, i == 0)
 	}
 	// wait for nodes ready
-	waitAndAssert(c, 5*time.Second, daemons[0].CheckNodeReadyCount, checker.Equals, nodeCount)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckNodeReadyCount, checker.Equals(nodeCount)), poll.WithTimeout(5*time.Second))
 
 	// create service
 	constraints := []string{"node.role==worker"}
 	instances := 3
 	id := daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances))
 	// wait for tasks ready
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckServiceRunningTasks(id), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	// validate tasks are running on worker nodes
 	tasks := daemons[0].GetServiceTasks(c, id)
 	for _, task := range tasks {
 		node := daemons[0].GetNode(c, task.NodeID)
-		c.Assert(node.Spec.Role, checker.Equals, swarm.NodeRoleWorker)
+		assert.Equal(c, node.Spec.Role, swarm.NodeRoleWorker)
 	}
-	//remove service
+	// remove service
 	daemons[0].RemoveService(c, id)
 
 	// create service
 	constraints = []string{"node.role!=worker"}
 	id = daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances))
 	// wait for tasks ready
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckServiceRunningTasks(id), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	tasks = daemons[0].GetServiceTasks(c, id)
 	// validate tasks are running on manager nodes
 	for _, task := range tasks {
 		node := daemons[0].GetNode(c, task.NodeID)
-		c.Assert(node.Spec.Role, checker.Equals, swarm.NodeRoleManager)
+		assert.Equal(c, node.Spec.Role, swarm.NodeRoleManager)
 	}
-	//remove service
+	// remove service
 	daemons[0].RemoveService(c, id)
 
 	// create service
 	constraints = []string{"node.role==nosuchrole"}
 	id = daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances))
 	// wait for tasks created
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckServiceTasks(id), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	// let scheduler try
 	time.Sleep(250 * time.Millisecond)
 	// validate tasks are not assigned to any node
 	tasks = daemons[0].GetServiceTasks(c, id)
 	for _, task := range tasks {
-		c.Assert(task.NodeID, checker.Equals, "")
+		assert.Equal(c, task.NodeID, "")
 	}
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintLabel(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintLabel(c *testing.T) {
 	const nodeCount = 3
 	var daemons [nodeCount]*daemon.Daemon
 	for i := 0; i < nodeCount; i++ {
 		daemons[i] = s.AddDaemon(c, true, i == 0)
 	}
 	// wait for nodes ready
-	waitAndAssert(c, 5*time.Second, daemons[0].CheckNodeReadyCount, checker.Equals, nodeCount)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckNodeReadyCount, checker.Equals(nodeCount)), poll.WithTimeout(5*time.Second))
 	nodes := daemons[0].ListNodes(c)
-	c.Assert(len(nodes), checker.Equals, nodeCount)
+	assert.Equal(c, len(nodes), nodeCount)
 
 	// add labels to nodes
 	daemons[0].UpdateNode(c, nodes[0].ID, func(n *swarm.Node) {
@@ -430,40 +418,40 @@ func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintLabel(c *check.C) {
 	constraints := []string{"node.labels.security==high"}
 	id := daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances))
 	// wait for tasks ready
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckServiceRunningTasks(id), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	tasks := daemons[0].GetServiceTasks(c, id)
 	// validate all tasks are running on nodes[0]
 	for _, task := range tasks {
-		c.Assert(task.NodeID, checker.Equals, nodes[0].ID)
+		assert.Assert(c, task.NodeID == nodes[0].ID)
 	}
-	//remove service
+	// remove service
 	daemons[0].RemoveService(c, id)
 
 	// create service
 	constraints = []string{"node.labels.security!=high"}
 	id = daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances))
 	// wait for tasks ready
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckServiceRunningTasks(id), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	tasks = daemons[0].GetServiceTasks(c, id)
 	// validate all tasks are NOT running on nodes[0]
 	for _, task := range tasks {
-		c.Assert(task.NodeID, checker.Not(checker.Equals), nodes[0].ID)
+		assert.Assert(c, task.NodeID != nodes[0].ID)
 	}
-	//remove service
+	// remove service
 	daemons[0].RemoveService(c, id)
 
 	constraints = []string{"node.labels.security==medium"}
 	id = daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances))
 	// wait for tasks created
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckServiceTasks(id), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	// let scheduler try
 	time.Sleep(250 * time.Millisecond)
 	tasks = daemons[0].GetServiceTasks(c, id)
 	// validate tasks are not assigned
 	for _, task := range tasks {
-		c.Assert(task.NodeID, checker.Equals, "")
+		assert.Assert(c, task.NodeID == "")
 	}
-	//remove service
+	// remove service
 	daemons[0].RemoveService(c, id)
 
 	// multiple constraints
@@ -473,13 +461,13 @@ func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintLabel(c *check.C) {
 	}
 	id = daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances))
 	// wait for tasks created
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckServiceTasks(id), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	// let scheduler try
 	time.Sleep(250 * time.Millisecond)
 	tasks = daemons[0].GetServiceTasks(c, id)
 	// validate tasks are not assigned
 	for _, task := range tasks {
-		c.Assert(task.NodeID, checker.Equals, "")
+		assert.Assert(c, task.NodeID == "")
 	}
 	// make nodes[1] fulfills the constraints
 	daemons[0].UpdateNode(c, nodes[1].ID, func(n *swarm.Node) {
@@ -488,23 +476,23 @@ func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintLabel(c *check.C) {
 		}
 	})
 	// wait for tasks ready
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckServiceRunningTasks(id), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	tasks = daemons[0].GetServiceTasks(c, id)
 	for _, task := range tasks {
-		c.Assert(task.NodeID, checker.Equals, nodes[1].ID)
+		assert.Assert(c, task.NodeID == nodes[1].ID)
 	}
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServicePlacementPrefs(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServicePlacementPrefs(c *testing.T) {
 	const nodeCount = 3
 	var daemons [nodeCount]*daemon.Daemon
 	for i := 0; i < nodeCount; i++ {
 		daemons[i] = s.AddDaemon(c, true, i == 0)
 	}
 	// wait for nodes ready
-	waitAndAssert(c, 5*time.Second, daemons[0].CheckNodeReadyCount, checker.Equals, nodeCount)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckNodeReadyCount, checker.Equals(nodeCount)), poll.WithTimeout(5*time.Second))
 	nodes := daemons[0].ListNodes(c)
-	c.Assert(len(nodes), checker.Equals, nodeCount)
+	assert.Equal(c, len(nodes), nodeCount)
 
 	// add labels to nodes
 	daemons[0].UpdateNode(c, nodes[0].ID, func(n *swarm.Node) {
@@ -525,20 +513,20 @@ func (s *DockerSwarmSuite) TestAPISwarmServicePlacementPrefs(c *check.C) {
 	prefs := []swarm.PlacementPreference{{Spread: &swarm.SpreadOver{SpreadDescriptor: "node.labels.rack"}}}
 	id := daemons[0].CreateService(c, simpleTestService, setPlacementPrefs(prefs), setInstances(instances))
 	// wait for tasks ready
-	waitAndAssert(c, defaultReconciliationTimeout, daemons[0].CheckServiceRunningTasks(id), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	tasks := daemons[0].GetServiceTasks(c, id)
 	// validate all tasks are running on nodes[0]
 	tasksOnNode := make(map[string]int)
 	for _, task := range tasks {
 		tasksOnNode[task.NodeID]++
 	}
-	c.Assert(tasksOnNode[nodes[0].ID], checker.Equals, 2)
-	c.Assert(tasksOnNode[nodes[1].ID], checker.Equals, 1)
-	c.Assert(tasksOnNode[nodes[2].ID], checker.Equals, 1)
+	assert.Assert(c, tasksOnNode[nodes[0].ID] == 2)
+	assert.Assert(c, tasksOnNode[nodes[1].ID] == 1)
+	assert.Assert(c, tasksOnNode[nodes[2].ID] == 1)
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServicesStateReporting(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSwarmSuite) TestAPISwarmServicesStateReporting(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	testRequires(c, DaemonIsLinux)
 
 	d1 := s.AddDaemon(c, true, true)
@@ -550,7 +538,7 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesStateReporting(c *check.C) {
 	instances := 9
 	d1.CreateService(c, simpleTestService, setInstances(instances))
 
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	getContainers := func() map[string]*daemon.Daemon {
 		m := make(map[string]*daemon.Daemon)
@@ -563,24 +551,24 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesStateReporting(c *check.C) {
 	}
 
 	containers := getContainers()
-	c.Assert(containers, checker.HasLen, instances)
+	assert.Assert(c, len(containers) == instances)
 	var toRemove string
 	for i := range containers {
 		toRemove = i
 	}
 
 	_, err := containers[toRemove].Cmd("stop", toRemove)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	containers2 := getContainers()
-	c.Assert(containers2, checker.HasLen, instances)
+	assert.Assert(c, len(containers2) == instances)
 	for i := range containers {
 		if i == toRemove {
-			c.Assert(containers2[i], checker.IsNil)
+			assert.Assert(c, containers2[i] == nil)
 		} else {
-			c.Assert(containers2[i], checker.NotNil)
+			assert.Assert(c, containers2[i] != nil)
 		}
 	}
 
@@ -591,22 +579,22 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesStateReporting(c *check.C) {
 
 	// try with killing process outside of docker
 	pidStr, err := containers[toRemove].Cmd("inspect", "-f", "{{.State.Pid}}", toRemove)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	pid, err := strconv.Atoi(strings.TrimSpace(pidStr))
-	c.Assert(err, checker.IsNil)
-	c.Assert(unix.Kill(pid, unix.SIGKILL), checker.IsNil)
+	assert.NilError(c, err)
+	assert.NilError(c, unix.Kill(pid, unix.SIGKILL))
 
 	time.Sleep(time.Second) // give some time to handle the signal
 
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	containers2 = getContainers()
-	c.Assert(containers2, checker.HasLen, instances)
+	assert.Assert(c, len(containers2) == instances)
 	for i := range containers {
 		if i == toRemove {
-			c.Assert(containers2[i], checker.IsNil)
+			assert.Assert(c, containers2[i] == nil)
 		} else {
-			c.Assert(containers2[i], checker.NotNil)
+			assert.Assert(c, containers2[i] != nil)
 		}
 	}
 }
diff --git a/integration-cli/docker_api_swarm_test.go b/integration-cli/docker_api_swarm_test.go
index 10b9938486c92..441f7a1652a45 100644
--- a/integration-cli/docker_api_swarm_test.go
+++ b/integration-cli/docker_api_swarm_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -5,12 +6,14 @@ package main
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
 	"net"
 	"net/http"
+	"os"
 	"path/filepath"
+	"runtime"
 	"strings"
 	"sync"
+	"testing"
 	"time"
 
 	"github.com/cloudflare/cfssl/csr"
@@ -22,35 +25,35 @@ import (
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/daemon"
-	testdaemon "github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/request"
+	testdaemon "github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/request"
 	"github.com/docker/swarmkit/ca"
-	"github.com/go-check/check"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
 )
 
 var defaultReconciliationTimeout = 30 * time.Second
 
-func (s *DockerSwarmSuite) TestAPISwarmInit(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmInit(c *testing.T) {
 	// todo: should find a better way to verify that components are running than /info
 	d1 := s.AddDaemon(c, true, true)
 	info := d1.SwarmInfo(c)
-	c.Assert(info.ControlAvailable, checker.True)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
-	c.Assert(info.Cluster.RootRotationInProgress, checker.False)
+	assert.Equal(c, info.ControlAvailable, true)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
+	assert.Equal(c, info.Cluster.RootRotationInProgress, false)
 
 	d2 := s.AddDaemon(c, true, false)
 	info = d2.SwarmInfo(c)
-	c.Assert(info.ControlAvailable, checker.False)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, info.ControlAvailable, false)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
 
 	// Leaving cluster
-	c.Assert(d2.SwarmLeave(false), checker.IsNil)
+	assert.NilError(c, d2.SwarmLeave(c, false))
 
 	info = d2.SwarmInfo(c)
-	c.Assert(info.ControlAvailable, checker.False)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
+	assert.Equal(c, info.ControlAvailable, false)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive)
 
 	d2.SwarmJoin(c, swarm.JoinRequest{
 		ListenAddr:  d1.SwarmListenAddr(),
@@ -59,26 +62,26 @@ func (s *DockerSwarmSuite) TestAPISwarmInit(c *check.C) {
 	})
 
 	info = d2.SwarmInfo(c)
-	c.Assert(info.ControlAvailable, checker.False)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, info.ControlAvailable, false)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
 
 	// Current state restoring after restarts
 	d1.Stop(c)
 	d2.Stop(c)
 
-	d1.Start(c)
-	d2.Start(c)
+	d1.StartNode(c)
+	d2.StartNode(c)
 
 	info = d1.SwarmInfo(c)
-	c.Assert(info.ControlAvailable, checker.True)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, info.ControlAvailable, true)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
 
 	info = d2.SwarmInfo(c)
-	c.Assert(info.ControlAvailable, checker.False)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, info.ControlAvailable, false)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmJoinToken(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmJoinToken(c *testing.T) {
 	d1 := s.AddDaemon(c, false, false)
 	d1.SwarmInit(c, swarm.InitRequest{})
 
@@ -90,20 +93,18 @@ func (s *DockerSwarmSuite) TestAPISwarmJoinToken(c *check.C) {
 		ListenAddr:  d2.SwarmListenAddr(),
 		RemoteAddrs: []string{d1.SwarmListenAddr()},
 	})
-	c.Assert(err, checker.NotNil)
-	c.Assert(err.Error(), checker.Contains, "join token is necessary")
+	assert.ErrorContains(c, err, "join token is necessary")
 	info := d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive)
 
 	err = c2.SwarmJoin(context.Background(), swarm.JoinRequest{
 		ListenAddr:  d2.SwarmListenAddr(),
 		JoinToken:   "foobaz",
 		RemoteAddrs: []string{d1.SwarmListenAddr()},
 	})
-	c.Assert(err, checker.NotNil)
-	c.Assert(err.Error(), checker.Contains, "invalid join token")
+	assert.ErrorContains(c, err, "invalid join token")
 	info = d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive)
 
 	workerToken := d1.JoinTokens(c).Worker
 
@@ -113,10 +114,10 @@ func (s *DockerSwarmSuite) TestAPISwarmJoinToken(c *check.C) {
 		RemoteAddrs: []string{d1.SwarmListenAddr()},
 	})
 	info = d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
-	c.Assert(d2.SwarmLeave(false), checker.IsNil)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
+	assert.NilError(c, d2.SwarmLeave(c, false))
 	info = d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive)
 
 	// change tokens
 	d1.RotateTokens(c)
@@ -126,19 +127,18 @@ func (s *DockerSwarmSuite) TestAPISwarmJoinToken(c *check.C) {
 		JoinToken:   workerToken,
 		RemoteAddrs: []string{d1.SwarmListenAddr()},
 	})
-	c.Assert(err, checker.NotNil)
-	c.Assert(err.Error(), checker.Contains, "join token is necessary")
+	assert.ErrorContains(c, err, "join token is necessary")
 	info = d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive)
 
 	workerToken = d1.JoinTokens(c).Worker
 
 	d2.SwarmJoin(c, swarm.JoinRequest{JoinToken: workerToken, RemoteAddrs: []string{d1.SwarmListenAddr()}})
 	info = d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
-	c.Assert(d2.SwarmLeave(false), checker.IsNil)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
+	assert.NilError(c, d2.SwarmLeave(c, false))
 	info = d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive)
 
 	// change spec, don't change tokens
 	d1.UpdateSwarm(c, func(s *swarm.Spec) {})
@@ -147,20 +147,19 @@ func (s *DockerSwarmSuite) TestAPISwarmJoinToken(c *check.C) {
 		ListenAddr:  d2.SwarmListenAddr(),
 		RemoteAddrs: []string{d1.SwarmListenAddr()},
 	})
-	c.Assert(err, checker.NotNil)
-	c.Assert(err.Error(), checker.Contains, "join token is necessary")
+	assert.ErrorContains(c, err, "join token is necessary")
 	info = d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive)
 
 	d2.SwarmJoin(c, swarm.JoinRequest{JoinToken: workerToken, RemoteAddrs: []string{d1.SwarmListenAddr()}})
 	info = d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
-	c.Assert(d2.SwarmLeave(false), checker.IsNil)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
+	assert.NilError(c, d2.SwarmLeave(c, false))
 	info = d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive)
 }
 
-func (s *DockerSwarmSuite) TestUpdateSwarmAddExternalCA(c *check.C) {
+func (s *DockerSwarmSuite) TestUpdateSwarmAddExternalCA(c *testing.T) {
 	d1 := s.AddDaemon(c, false, false)
 	d1.SwarmInit(c, swarm.InitRequest{})
 	d1.UpdateSwarm(c, func(s *swarm.Spec) {
@@ -177,12 +176,12 @@ func (s *DockerSwarmSuite) TestUpdateSwarmAddExternalCA(c *check.C) {
 		}
 	})
 	info := d1.SwarmInfo(c)
-	c.Assert(info.Cluster.Spec.CAConfig.ExternalCAs, checker.HasLen, 2)
-	c.Assert(info.Cluster.Spec.CAConfig.ExternalCAs[0].CACert, checker.Equals, "")
-	c.Assert(info.Cluster.Spec.CAConfig.ExternalCAs[1].CACert, checker.Equals, "cacert")
+	assert.Equal(c, len(info.Cluster.Spec.CAConfig.ExternalCAs), 2)
+	assert.Equal(c, info.Cluster.Spec.CAConfig.ExternalCAs[0].CACert, "")
+	assert.Equal(c, info.Cluster.Spec.CAConfig.ExternalCAs[1].CACert, "cacert")
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmCAHash(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmCAHash(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, false, false)
 	splitToken := strings.Split(d1.JoinTokens(c).Worker, "-")
@@ -194,30 +193,29 @@ func (s *DockerSwarmSuite) TestAPISwarmCAHash(c *check.C) {
 		JoinToken:   replacementToken,
 		RemoteAddrs: []string{d1.SwarmListenAddr()},
 	})
-	c.Assert(err, checker.NotNil)
-	c.Assert(err.Error(), checker.Contains, "remote CA does not match fingerprint")
+	assert.ErrorContains(c, err, "remote CA does not match fingerprint")
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmPromoteDemote(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmPromoteDemote(c *testing.T) {
 	d1 := s.AddDaemon(c, false, false)
 	d1.SwarmInit(c, swarm.InitRequest{})
 	d2 := s.AddDaemon(c, true, false)
 
 	info := d2.SwarmInfo(c)
-	c.Assert(info.ControlAvailable, checker.False)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, info.ControlAvailable, false)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
 
 	d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
 		n.Spec.Role = swarm.NodeRoleManager
 	})
 
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckControlAvailable, checker.True)
+	poll.WaitOn(c, pollCheck(c, d2.CheckControlAvailable, checker.True()), poll.WithTimeout(defaultReconciliationTimeout))
 
 	d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
 		n.Spec.Role = swarm.NodeRoleWorker
 	})
 
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckControlAvailable, checker.False)
+	poll.WaitOn(c, pollCheck(c, d2.CheckControlAvailable, checker.False()), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// Wait for the role to change to worker in the cert. This is partially
 	// done because it's something worth testing in its own right, and
@@ -225,27 +223,27 @@ func (s *DockerSwarmSuite) TestAPISwarmPromoteDemote(c *check.C) {
 	// back to manager quickly might cause the node to pause for awhile
 	// while waiting for the role to change to worker, and the test can
 	// time out during this interval.
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
-		certBytes, err := ioutil.ReadFile(filepath.Join(d2.Folder, "root", "swarm", "certificates", "swarm-node.crt"))
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
+		certBytes, err := os.ReadFile(filepath.Join(d2.Folder, "root", "swarm", "certificates", "swarm-node.crt"))
 		if err != nil {
-			return "", check.Commentf("error: %v", err)
+			return "", fmt.Sprintf("error: %v", err)
 		}
 		certs, err := helpers.ParseCertificatesPEM(certBytes)
 		if err == nil && len(certs) > 0 && len(certs[0].Subject.OrganizationalUnit) > 0 {
-			return certs[0].Subject.OrganizationalUnit[0], nil
+			return certs[0].Subject.OrganizationalUnit[0], ""
 		}
-		return "", check.Commentf("could not get organizational unit from certificate")
-	}, checker.Equals, "swarm-worker")
+		return "", "could not get organizational unit from certificate"
+	}, checker.Equals("swarm-worker")), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// Demoting last node should fail
 	node := d1.GetNode(c, d1.NodeID())
 	node.Spec.Role = swarm.NodeRoleWorker
 	url := fmt.Sprintf("/nodes/%s/update?version=%d", node.ID, node.Version.Index)
 	res, body, err := request.Post(url, request.Host(d1.Sock()), request.JSONBody(node.Spec))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest, check.Commentf("output: %q", string(b)))
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusBadRequest, "output: %q", string(b))
 
 	// The warning specific to demoting the last manager is best-effort and
 	// won't appear until the Role field of the demoted manager has been
@@ -254,21 +252,21 @@ func (s *DockerSwarmSuite) TestAPISwarmPromoteDemote(c *check.C) {
 	// it anchors the regexp contrary to the documentation, and this makes
 	// it impossible to match something that includes a line break.
 	if !strings.Contains(string(b), "last manager of the swarm") {
-		c.Assert(string(b), checker.Contains, "this would result in a loss of quorum")
+		assert.Assert(c, strings.Contains(string(b), "this would result in a loss of quorum"))
 	}
 	info = d1.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
-	c.Assert(info.ControlAvailable, checker.True)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
+	assert.Equal(c, info.ControlAvailable, true)
 
 	// Promote already demoted node
 	d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
 		n.Spec.Role = swarm.NodeRoleManager
 	})
 
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckControlAvailable, checker.True)
+	poll.WaitOn(c, pollCheck(c, d2.CheckControlAvailable, checker.True()), poll.WithTimeout(defaultReconciliationTimeout))
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmLeaderProxy(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmLeaderProxy(c *testing.T) {
 	// add three managers, one of these is leader
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, true)
@@ -289,20 +287,27 @@ func (s *DockerSwarmSuite) TestAPISwarmLeaderProxy(c *check.C) {
 	// query each node and make sure it returns 3 services
 	for _, d := range []*daemon.Daemon{d1, d2, d3} {
 		services := d.ListServices(c)
-		c.Assert(services, checker.HasLen, 3)
+		assert.Equal(c, len(services), 3)
 	}
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmLeaderElection(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmLeaderElection(c *testing.T) {
+	if runtime.GOARCH == "s390x" {
+		c.Skip("Disabled on s390x")
+	}
+	if runtime.GOARCH == "ppc64le" {
+		c.Skip("Disabled on  ppc64le")
+	}
+
 	// Create 3 nodes
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, true)
 	d3 := s.AddDaemon(c, true, true)
 
 	// assert that the first node we made is the leader, and the other two are followers
-	c.Assert(d1.GetNode(c, d1.NodeID()).ManagerStatus.Leader, checker.True)
-	c.Assert(d1.GetNode(c, d2.NodeID()).ManagerStatus.Leader, checker.False)
-	c.Assert(d1.GetNode(c, d3.NodeID()).ManagerStatus.Leader, checker.False)
+	assert.Equal(c, d1.GetNode(c, d1.NodeID()).ManagerStatus.Leader, true)
+	assert.Equal(c, d1.GetNode(c, d2.NodeID()).ManagerStatus.Leader, false)
+	assert.Equal(c, d1.GetNode(c, d3.NodeID()).ManagerStatus.Leader, false)
 
 	d1.Stop(c)
 
@@ -310,13 +315,24 @@ func (s *DockerSwarmSuite) TestAPISwarmLeaderElection(c *check.C) {
 		leader    *daemon.Daemon   // keep track of leader
 		followers []*daemon.Daemon // keep track of followers
 	)
+	var lastErr error
 	checkLeader := func(nodes ...*daemon.Daemon) checkF {
-		return func(c *check.C) (interface{}, check.CommentInterface) {
+		return func(c *testing.T) (interface{}, string) {
 			// clear these out before each run
 			leader = nil
 			followers = nil
 			for _, d := range nodes {
-				if d.GetNode(c, d.NodeID()).ManagerStatus.Leader {
+				n := d.GetNode(c, d.NodeID(), func(err error) bool {
+					if strings.Contains(err.Error(), context.DeadlineExceeded.Error()) || strings.Contains(err.Error(), "swarm does not have a leader") {
+						lastErr = err
+						return true
+					}
+					return false
+				})
+				if n == nil {
+					return false, fmt.Sprintf("failed to get node: %v", lastErr)
+				}
+				if n.ManagerStatus.Leader {
 					leader = d
 				} else {
 					followers = append(followers, d)
@@ -324,39 +340,46 @@ func (s *DockerSwarmSuite) TestAPISwarmLeaderElection(c *check.C) {
 			}
 
 			if leader == nil {
-				return false, check.Commentf("no leader elected")
+				return false, "no leader elected"
 			}
 
-			return true, check.Commentf("elected %v", leader.ID())
+			return true, fmt.Sprintf("elected %v", leader.ID())
 		}
 	}
 
 	// wait for an election to occur
-	waitAndAssert(c, defaultReconciliationTimeout, checkLeader(d2, d3), checker.True)
+	c.Logf("Waiting for election to occur...")
+	poll.WaitOn(c, pollCheck(c, checkLeader(d2, d3), checker.True()), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// assert that we have a new leader
-	c.Assert(leader, checker.NotNil)
+	assert.Assert(c, leader != nil)
 
 	// Keep track of the current leader, since we want that to be chosen.
 	stableleader := leader
 
 	// add the d1, the initial leader, back
-	d1.Start(c)
-
-	// TODO(stevvooe): may need to wait for rejoin here
+	d1.StartNode(c)
 
 	// wait for possible election
-	waitAndAssert(c, defaultReconciliationTimeout, checkLeader(d1, d2, d3), checker.True)
+	c.Logf("Waiting for possible election...")
+	poll.WaitOn(c, pollCheck(c, checkLeader(d1, d2, d3), checker.True()), poll.WithTimeout(defaultReconciliationTimeout))
 	// pick out the leader and the followers again
 
 	// verify that we still only have 1 leader and 2 followers
-	c.Assert(leader, checker.NotNil)
-	c.Assert(followers, checker.HasLen, 2)
+	assert.Assert(c, leader != nil)
+	assert.Equal(c, len(followers), 2)
 	// and that after we added d1 back, the leader hasn't changed
-	c.Assert(leader.NodeID(), checker.Equals, stableleader.NodeID())
+	assert.Equal(c, leader.NodeID(), stableleader.NodeID())
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmRaftQuorum(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmRaftQuorum(c *testing.T) {
+	if runtime.GOARCH == "s390x" {
+		c.Skip("Disabled on s390x")
+	}
+	if runtime.GOARCH == "ppc64le" {
+		c.Skip("Disabled on  ppc64le")
+	}
+
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, true)
 	d3 := s.AddDaemon(c, true, true)
@@ -366,7 +389,7 @@ func (s *DockerSwarmSuite) TestAPISwarmRaftQuorum(c *check.C) {
 	d2.Stop(c)
 
 	// make sure there is a leader
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckLeader, checker.IsNil)
+	poll.WaitOn(c, pollCheck(c, d1.CheckLeader, checker.IsNil()), poll.WithTimeout(defaultReconciliationTimeout))
 
 	d1.CreateService(c, simpleTestService, func(s *swarm.Service) {
 		s.Spec.Name = "top1"
@@ -377,56 +400,55 @@ func (s *DockerSwarmSuite) TestAPISwarmRaftQuorum(c *check.C) {
 	var service swarm.Service
 	simpleTestService(&service)
 	service.Spec.Name = "top2"
-	cli, err := d1.NewClient()
-	c.Assert(err, checker.IsNil)
+	cli := d1.NewClientT(c)
 	defer cli.Close()
 
 	// d1 will eventually step down from leader because there is no longer an active quorum, wait for that to happen
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
-		_, err = cli.ServiceCreate(context.Background(), service.Spec, types.ServiceCreateOptions{})
-		return err.Error(), nil
-	}, checker.Contains, "Make sure more than half of the managers are online.")
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
+		_, err := cli.ServiceCreate(context.Background(), service.Spec, types.ServiceCreateOptions{})
+		return err.Error(), ""
+	}, checker.Contains("Make sure more than half of the managers are online.")), poll.WithTimeout(defaultReconciliationTimeout*2))
 
-	d2.Start(c)
+	d2.StartNode(c)
 
 	// make sure there is a leader
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckLeader, checker.IsNil)
+	poll.WaitOn(c, pollCheck(c, d1.CheckLeader, checker.IsNil()), poll.WithTimeout(defaultReconciliationTimeout))
 
 	d1.CreateService(c, simpleTestService, func(s *swarm.Service) {
 		s.Spec.Name = "top3"
 	})
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmLeaveRemovesContainer(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmLeaveRemovesContainer(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	instances := 2
 	d.CreateService(c, simpleTestService, setInstances(instances))
 
 	id, err := d.Cmd("run", "-d", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", id))
+	assert.NilError(c, err, id)
 	id = strings.TrimSpace(id)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances+1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(instances+1)), poll.WithTimeout(defaultReconciliationTimeout))
 
-	c.Assert(d.SwarmLeave(false), checker.NotNil)
-	c.Assert(d.SwarmLeave(true), checker.IsNil)
+	assert.ErrorContains(c, d.SwarmLeave(c, false), "")
+	assert.NilError(c, d.SwarmLeave(c, true))
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	id2, err := d.Cmd("ps", "-q")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", id2))
-	c.Assert(id, checker.HasPrefix, strings.TrimSpace(id2))
+	assert.NilError(c, err, id2)
+	assert.Assert(c, strings.HasPrefix(id, strings.TrimSpace(id2)))
 }
 
 // #23629
-func (s *DockerSwarmSuite) TestAPISwarmLeaveOnPendingJoin(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmLeaveOnPendingJoin(c *testing.T) {
 	testRequires(c, Network)
 	s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, false, false)
 
 	id, err := d2.Cmd("run", "-d", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", id))
+	assert.NilError(c, err, id)
 	id = strings.TrimSpace(id)
 
 	c2 := d2.NewClientT(c)
@@ -434,23 +456,22 @@ func (s *DockerSwarmSuite) TestAPISwarmLeaveOnPendingJoin(c *check.C) {
 		ListenAddr:  d2.SwarmListenAddr(),
 		RemoteAddrs: []string{"123.123.123.123:1234"},
 	})
-	c.Assert(err, check.NotNil)
-	c.Assert(err.Error(), checker.Contains, "Timeout was reached")
+	assert.ErrorContains(c, err, "Timeout was reached")
 
 	info := d2.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStatePending)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStatePending)
 
-	c.Assert(d2.SwarmLeave(true), checker.IsNil)
+	assert.NilError(c, d2.SwarmLeave(c, true))
 
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d2.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	id2, err := d2.Cmd("ps", "-q")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", id2))
-	c.Assert(id, checker.HasPrefix, strings.TrimSpace(id2))
+	assert.NilError(c, err, id2)
+	assert.Assert(c, strings.HasPrefix(id, strings.TrimSpace(id2)))
 }
 
 // #23705
-func (s *DockerSwarmSuite) TestAPISwarmRestoreOnPendingJoin(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmRestoreOnPendingJoin(c *testing.T) {
 	testRequires(c, Network)
 	d := s.AddDaemon(c, false, false)
 	client := d.NewClientT(c)
@@ -458,58 +479,54 @@ func (s *DockerSwarmSuite) TestAPISwarmRestoreOnPendingJoin(c *check.C) {
 		ListenAddr:  d.SwarmListenAddr(),
 		RemoteAddrs: []string{"123.123.123.123:1234"},
 	})
-	c.Assert(err, check.NotNil)
-	c.Assert(err.Error(), checker.Contains, "Timeout was reached")
+	assert.ErrorContains(c, err, "Timeout was reached")
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckLocalNodeState, checker.Equals, swarm.LocalNodeStatePending)
+	poll.WaitOn(c, pollCheck(c, d.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStatePending)), poll.WithTimeout(defaultReconciliationTimeout))
 
-	d.Stop(c)
-	d.Start(c)
+	d.RestartNode(c)
 
 	info := d.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive)
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmManagerRestore(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmManagerRestore(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 
 	instances := 2
 	id := d1.CreateService(c, simpleTestService, setInstances(instances))
 
 	d1.GetService(c, id)
-	d1.Stop(c)
-	d1.Start(c)
+	d1.RestartNode(c)
 	d1.GetService(c, id)
 
 	d2 := s.AddDaemon(c, true, true)
 	d2.GetService(c, id)
-	d2.Stop(c)
-	d2.Start(c)
+	d2.RestartNode(c)
 	d2.GetService(c, id)
 
 	d3 := s.AddDaemon(c, true, true)
 	d3.GetService(c, id)
-	d3.Stop(c)
-	d3.Start(c)
+	d3.RestartNode(c)
 	d3.GetService(c, id)
 
-	d3.Kill()
+	err := d3.Kill()
+	assert.NilError(c, err)
 	time.Sleep(1 * time.Second) // time to handle signal
-	d3.Start(c)
+	d3.StartNode(c)
 	d3.GetService(c, id)
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmScaleNoRollingUpdate(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmScaleNoRollingUpdate(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	instances := 2
 	id := d.CreateService(c, simpleTestService, setInstances(instances))
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	containers := d.ActiveContainers(c)
 	instances = 4
 	d.UpdateService(c, d.GetService(c, id), setInstances(instances))
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 	containers2 := d.ActiveContainers(c)
 
 loop0:
@@ -523,38 +540,38 @@ loop0:
 	}
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmInvalidAddress(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmInvalidAddress(c *testing.T) {
 	d := s.AddDaemon(c, false, false)
 	req := swarm.InitRequest{
 		ListenAddr: "",
 	}
 	res, _, err := request.Post("/swarm/init", request.Host(d.Sock()), request.JSONBody(req))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 
 	req2 := swarm.JoinRequest{
 		ListenAddr:  "0.0.0.0:2377",
 		RemoteAddrs: []string{""},
 	}
 	res, _, err = request.Post("/swarm/join", request.Host(d.Sock()), request.JSONBody(req2))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmForceNewCluster(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmForceNewCluster(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, true)
 
 	instances := 2
 	id := d1.CreateService(c, simpleTestService, setInstances(instances))
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// drain d2, all containers should move to d1
 	d1.UpdateNode(c, d2.NodeID(), func(n *swarm.Node) {
 		n.Spec.Availability = swarm.NodeAvailabilityDrain
 	})
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.Equals, instances)
-	waitAndAssert(c, defaultReconciliationTimeout, d2.CheckActiveContainerCount, checker.Equals, 0)
+	poll.WaitOn(c, pollCheck(c, d1.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, d2.CheckActiveContainerCount, checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	d2.Stop(c)
 
@@ -563,22 +580,22 @@ func (s *DockerSwarmSuite) TestAPISwarmForceNewCluster(c *check.C) {
 		Spec:            swarm.Spec{},
 	})
 
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckActiveContainerCount, checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, d1.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	d3 := s.AddDaemon(c, true, true)
 	info := d3.SwarmInfo(c)
-	c.Assert(info.ControlAvailable, checker.True)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, info.ControlAvailable, true)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
 
 	instances = 4
 	d3.UpdateService(c, d3.GetService(c, id), setInstances(instances))
 
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 }
 
 func simpleTestService(s *swarm.Service) {
 	ureplicas := uint64(1)
-	restartDelay := time.Duration(100 * time.Millisecond)
+	restartDelay := 100 * time.Millisecond
 
 	s.Spec = swarm.ServiceSpec{
 		TaskTemplate: swarm.TaskSpec{
@@ -601,7 +618,7 @@ func simpleTestService(s *swarm.Service) {
 
 func serviceForUpdate(s *swarm.Service) {
 	ureplicas := uint64(1)
-	restartDelay := time.Duration(100 * time.Millisecond)
+	restartDelay := 100 * time.Millisecond
 
 	s.Spec = swarm.ServiceSpec{
 		TaskTemplate: swarm.TaskSpec{
@@ -712,7 +729,7 @@ func setGlobalMode(s *swarm.Service) {
 	}
 }
 
-func checkClusterHealth(c *check.C, cl []*daemon.Daemon, managerCount, workerCount int) {
+func checkClusterHealth(c *testing.T, cl []*daemon.Daemon, managerCount, workerCount int) {
 	var totalMCount, totalWCount int
 
 	for _, d := range cl {
@@ -720,14 +737,14 @@ func checkClusterHealth(c *check.C, cl []*daemon.Daemon, managerCount, workerCou
 			info swarm.Info
 		)
 
-		// check info in a waitAndAssert, because if the cluster doesn't have a leader, `info` will return an error
-		checkInfo := func(c *check.C) (interface{}, check.CommentInterface) {
+		// check info in a poll.WaitOn(), because if the cluster doesn't have a leader, `info` will return an error
+		checkInfo := func(c *testing.T) (interface{}, string) {
 			client := d.NewClientT(c)
 			daemonInfo, err := client.Info(context.Background())
 			info = daemonInfo.Swarm
-			return err, check.Commentf("cluster not ready in time")
+			return err, "cluster not ready in time"
 		}
-		waitAndAssert(c, defaultReconciliationTimeout, checkInfo, checker.IsNil)
+		poll.WaitOn(c, pollCheck(c, checkInfo, checker.IsNil()), poll.WithTimeout(defaultReconciliationTimeout))
 		if !info.ControlAvailable {
 			totalWCount++
 			continue
@@ -738,62 +755,62 @@ func checkClusterHealth(c *check.C, cl []*daemon.Daemon, managerCount, workerCou
 		var mCount, wCount int
 
 		for _, n := range d.ListNodes(c) {
-			waitReady := func(c *check.C) (interface{}, check.CommentInterface) {
+			waitReady := func(c *testing.T) (interface{}, string) {
 				if n.Status.State == swarm.NodeStateReady {
-					return true, nil
+					return true, ""
 				}
 				nn := d.GetNode(c, n.ID)
 				n = *nn
-				return n.Status.State == swarm.NodeStateReady, check.Commentf("state of node %s, reported by %s", n.ID, d.NodeID())
+				return n.Status.State == swarm.NodeStateReady, fmt.Sprintf("state of node %s, reported by %s", n.ID, d.NodeID())
 			}
-			waitAndAssert(c, defaultReconciliationTimeout, waitReady, checker.True)
+			poll.WaitOn(c, pollCheck(c, waitReady, checker.True()), poll.WithTimeout(defaultReconciliationTimeout))
 
-			waitActive := func(c *check.C) (interface{}, check.CommentInterface) {
+			waitActive := func(c *testing.T) (interface{}, string) {
 				if n.Spec.Availability == swarm.NodeAvailabilityActive {
-					return true, nil
+					return true, ""
 				}
 				nn := d.GetNode(c, n.ID)
 				n = *nn
-				return n.Spec.Availability == swarm.NodeAvailabilityActive, check.Commentf("availability of node %s, reported by %s", n.ID, d.NodeID())
+				return n.Spec.Availability == swarm.NodeAvailabilityActive, fmt.Sprintf("availability of node %s, reported by %s", n.ID, d.NodeID())
 			}
-			waitAndAssert(c, defaultReconciliationTimeout, waitActive, checker.True)
+			poll.WaitOn(c, pollCheck(c, waitActive, checker.True()), poll.WithTimeout(defaultReconciliationTimeout))
 
 			if n.Spec.Role == swarm.NodeRoleManager {
-				c.Assert(n.ManagerStatus, checker.NotNil, check.Commentf("manager status of node %s (manager), reported by %s", n.ID, d.NodeID()))
+				assert.Assert(c, n.ManagerStatus != nil, "manager status of node %s (manager), reported by %s", n.ID, d.NodeID())
 				if n.ManagerStatus.Leader {
 					leaderFound = true
 				}
 				mCount++
 			} else {
-				c.Assert(n.ManagerStatus, checker.IsNil, check.Commentf("manager status of node %s (worker), reported by %s", n.ID, d.NodeID()))
+				assert.Assert(c, n.ManagerStatus == nil, "manager status of node %s (worker), reported by %s", n.ID, d.NodeID())
 				wCount++
 			}
 		}
-		c.Assert(leaderFound, checker.True, check.Commentf("lack of leader reported by node %s", info.NodeID))
-		c.Assert(mCount, checker.Equals, managerCount, check.Commentf("managers count reported by node %s", info.NodeID))
-		c.Assert(wCount, checker.Equals, workerCount, check.Commentf("workers count reported by node %s", info.NodeID))
+		assert.Equal(c, leaderFound, true, "lack of leader reported by node %s", info.NodeID)
+		assert.Equal(c, mCount, managerCount, "managers count reported by node %s", info.NodeID)
+		assert.Equal(c, wCount, workerCount, "workers count reported by node %s", info.NodeID)
 	}
-	c.Assert(totalMCount, checker.Equals, managerCount)
-	c.Assert(totalWCount, checker.Equals, workerCount)
+	assert.Equal(c, totalMCount, managerCount)
+	assert.Equal(c, totalWCount, workerCount)
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmRestartCluster(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmRestartCluster(c *testing.T) {
 	mCount, wCount := 5, 1
 
 	var nodes []*daemon.Daemon
 	for i := 0; i < mCount; i++ {
 		manager := s.AddDaemon(c, true, true)
 		info := manager.SwarmInfo(c)
-		c.Assert(info.ControlAvailable, checker.True)
-		c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
+		assert.Equal(c, info.ControlAvailable, true)
+		assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
 		nodes = append(nodes, manager)
 	}
 
 	for i := 0; i < wCount; i++ {
 		worker := s.AddDaemon(c, true, false)
 		info := worker.SwarmInfo(c)
-		c.Assert(info.ControlAvailable, checker.False)
-		c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
+		assert.Equal(c, info.ControlAvailable, false)
+		assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
 		nodes = append(nodes, worker)
 	}
 
@@ -814,7 +831,7 @@ func (s *DockerSwarmSuite) TestAPISwarmRestartCluster(c *check.C) {
 		wg.Wait()
 		close(errs)
 		for err := range errs {
-			c.Assert(err, check.IsNil)
+			assert.NilError(c, err)
 		}
 	}
 
@@ -835,61 +852,57 @@ func (s *DockerSwarmSuite) TestAPISwarmRestartCluster(c *check.C) {
 		wg.Wait()
 		close(errs)
 		for err := range errs {
-			c.Assert(err, check.IsNil)
+			assert.NilError(c, err)
 		}
 	}
 
 	checkClusterHealth(c, nodes, mCount, wCount)
 }
 
-func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateWithName(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateWithName(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	instances := 2
 	id := d.CreateService(c, simpleTestService, setInstances(instances))
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	service := d.GetService(c, id)
 	instances = 5
 
 	setInstances(instances)(service)
-	cli, err := d.NewClient()
-	c.Assert(err, checker.IsNil)
+	cli := d.NewClientT(c)
 	defer cli.Close()
-	_, err = cli.ServiceUpdate(context.Background(), service.Spec.Name, service.Version, service.Spec, types.ServiceUpdateOptions{})
-	c.Assert(err, checker.IsNil)
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances)
+	_, err := cli.ServiceUpdate(context.Background(), service.Spec.Name, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(c, err)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 }
 
 // Unlocking an unlocked swarm results in an error
-func (s *DockerSwarmSuite) TestAPISwarmUnlockNotLocked(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmUnlockNotLocked(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
-	err := d.SwarmUnlock(swarm.UnlockRequest{UnlockKey: "wrong-key"})
-	c.Assert(err, checker.NotNil)
-	c.Assert(err.Error(), checker.Contains, "swarm is not locked")
+	err := d.SwarmUnlock(c, swarm.UnlockRequest{UnlockKey: "wrong-key"})
+	assert.ErrorContains(c, err, "swarm is not locked")
 }
 
 // #29885
-func (s *DockerSwarmSuite) TestAPISwarmErrorHandling(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmErrorHandling(c *testing.T) {
 	ln, err := net.Listen("tcp", fmt.Sprintf(":%d", defaultSwarmPort))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer ln.Close()
 	d := s.AddDaemon(c, false, false)
 	client := d.NewClientT(c)
 	_, err = client.SwarmInit(context.Background(), swarm.InitRequest{
 		ListenAddr: d.SwarmListenAddr(),
 	})
-	c.Assert(err, checker.NotNil)
-	c.Assert(err.Error(), checker.Contains, "address already in use")
+	assert.ErrorContains(c, err, "address already in use")
 }
 
 // Test case for 30242, where duplicate networks, with different drivers `bridge` and `overlay`,
 // caused both scopes to be `swarm` for `docker network inspect` and `docker network ls`.
 // This test makes sure the fixes correctly output scopes instead.
-func (s *DockerSwarmSuite) TestAPIDuplicateNetworks(c *check.C) {
+func (s *DockerSwarmSuite) TestAPIDuplicateNetworks(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
-	cli, err := d.NewClient()
-	c.Assert(err, checker.IsNil)
+	cli := d.NewClientT(c)
 	defer cli.Close()
 
 	name := "foo"
@@ -900,30 +913,30 @@ func (s *DockerSwarmSuite) TestAPIDuplicateNetworks(c *check.C) {
 	networkCreate.Driver = "bridge"
 
 	n1, err := cli.NetworkCreate(context.Background(), name, networkCreate)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	networkCreate.Driver = "overlay"
 
 	n2, err := cli.NetworkCreate(context.Background(), name, networkCreate)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	r1, err := cli.NetworkInspect(context.Background(), n1.ID, types.NetworkInspectOptions{})
-	c.Assert(err, checker.IsNil)
-	c.Assert(r1.Scope, checker.Equals, "local")
+	assert.NilError(c, err)
+	assert.Equal(c, r1.Scope, "local")
 
 	r2, err := cli.NetworkInspect(context.Background(), n2.ID, types.NetworkInspectOptions{})
-	c.Assert(err, checker.IsNil)
-	c.Assert(r2.Scope, checker.Equals, "swarm")
+	assert.NilError(c, err)
+	assert.Equal(c, r2.Scope, "swarm")
 }
 
 // Test case for 30178
-func (s *DockerSwarmSuite) TestAPISwarmHealthcheckNone(c *check.C) {
+func (s *DockerSwarmSuite) TestAPISwarmHealthcheckNone(c *testing.T) {
 	// Issue #36386 can be a independent one, which is worth further investigation.
 	c.Skip("Root cause of Issue #36386 is needed")
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "-d", "overlay", "lb")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	instances := 1
 	d.CreateService(c, simpleTestService, setInstances(instances), func(s *swarm.Service) {
@@ -936,15 +949,15 @@ func (s *DockerSwarmSuite) TestAPISwarmHealthcheckNone(c *check.C) {
 		}
 	})
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, instances)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	containers := d.ActiveContainers(c)
 
 	out, err = d.Cmd("exec", containers[0], "ping", "-c1", "-W3", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *testing.T) {
 	m := s.AddDaemon(c, true, true)
 	w := s.AddDaemon(c, true, false)
 
@@ -962,7 +975,7 @@ func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
 				KeyRequest: csr.NewBasicKeyRequest(),
 				CA:         &csr.CAConfig{Expiry: ca.RootCAExpiration},
 			})
-			c.Assert(err, checker.IsNil)
+			assert.NilError(c, err)
 		}
 		expectedCert := string(cert)
 		m.UpdateSwarm(c, func(s *swarm.Spec) {
@@ -977,8 +990,8 @@ func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
 			info := m.SwarmInfo(c)
 
 			// the desired CA cert and key is always redacted
-			c.Assert(info.Cluster.Spec.CAConfig.SigningCAKey, checker.Equals, "")
-			c.Assert(info.Cluster.Spec.CAConfig.SigningCACert, checker.Equals, "")
+			assert.Equal(c, info.Cluster.Spec.CAConfig.SigningCAKey, "")
+			assert.Equal(c, info.Cluster.Spec.CAConfig.SigningCACert, "")
 
 			clusterTLSInfo = info.Cluster.TLSInfo
 
@@ -991,7 +1004,7 @@ func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
 			time.Sleep(250 * time.Millisecond)
 		}
 		if cert != nil {
-			c.Assert(clusterTLSInfo.TrustRoot, checker.Equals, expectedCert)
+			assert.Equal(c, clusterTLSInfo.TrustRoot, expectedCert)
 		}
 		// could take another second or two for the nodes to trust the new roots after they've all gotten
 		// new TLS certificates
@@ -1007,19 +1020,18 @@ func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
 			time.Sleep(250 * time.Millisecond)
 		}
 
-		c.Assert(m.GetNode(c, m.NodeID()).Description.TLSInfo, checker.DeepEquals, clusterTLSInfo)
-		c.Assert(m.GetNode(c, w.NodeID()).Description.TLSInfo, checker.DeepEquals, clusterTLSInfo)
+		assert.DeepEqual(c, m.GetNode(c, m.NodeID()).Description.TLSInfo, clusterTLSInfo)
+		assert.DeepEqual(c, m.GetNode(c, w.NodeID()).Description.TLSInfo, clusterTLSInfo)
 		currentTrustRoot = clusterTLSInfo.TrustRoot
 	}
 }
 
-func (s *DockerSwarmSuite) TestAPINetworkInspectWithScope(c *check.C) {
+func (s *DockerSwarmSuite) TestAPINetworkInspectWithScope(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "test-scoped-network"
 	ctx := context.Background()
-	apiclient, err := d.NewClient()
-	assert.NilError(c, err)
+	apiclient := d.NewClientT(c)
 
 	resp, err := apiclient.NetworkCreate(ctx, name, types.NetworkCreate{Driver: "overlay"})
 	assert.NilError(c, err)
diff --git a/integration-cli/docker_api_test.go b/integration-cli/docker_api_test.go
index 5b7e3e97f9d80..6a091bc8a4fac 100644
--- a/integration-cli/docker_api_test.go
+++ b/integration-cli/docker_api_test.go
@@ -2,38 +2,38 @@ package main
 
 import (
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"runtime"
 	"strconv"
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api/types/versions"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestAPIOptionsRoute(c *check.C) {
+func (s *DockerSuite) TestAPIOptionsRoute(c *testing.T) {
 	resp, _, err := request.Do("/", request.Method(http.MethodOptions))
-	c.Assert(err, checker.IsNil)
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, resp.StatusCode, http.StatusOK)
 }
 
-func (s *DockerSuite) TestAPIGetEnabledCORS(c *check.C) {
+func (s *DockerSuite) TestAPIGetEnabledCORS(c *testing.T) {
 	res, body, err := request.Get("/version")
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusOK)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusOK)
 	body.Close()
 	// TODO: @runcom incomplete tests, why old integration tests had this headers
 	// and here none of the headers below are in the response?
 	//c.Log(res.Header)
-	//c.Assert(res.Header.Get("Access-Control-Allow-Origin"), check.Equals, "*")
-	//c.Assert(res.Header.Get("Access-Control-Allow-Headers"), check.Equals, "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
+	//assert.Equal(c, res.Header.Get("Access-Control-Allow-Origin"), "*")
+	//assert.Equal(c, res.Header.Get("Access-Control-Allow-Headers"), "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
 }
 
-func (s *DockerSuite) TestAPIClientVersionOldNotSupported(c *check.C) {
+func (s *DockerSuite) TestAPIClientVersionOldNotSupported(c *testing.T) {
 	if testEnv.OSType != runtime.GOOS {
 		c.Skip("Daemon platform doesn't match test platform")
 	}
@@ -42,69 +42,69 @@ func (s *DockerSuite) TestAPIClientVersionOldNotSupported(c *check.C) {
 	}
 	v := strings.Split(api.MinVersion, ".")
 	vMinInt, err := strconv.Atoi(v[1])
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	vMinInt--
 	v[1] = strconv.Itoa(vMinInt)
 	version := strings.Join(v, ".")
 
 	resp, body, err := request.Get("/v" + version + "/version")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer body.Close()
-	c.Assert(resp.StatusCode, checker.Equals, http.StatusBadRequest)
+	assert.Equal(c, resp.StatusCode, http.StatusBadRequest)
 	expected := fmt.Sprintf("client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version", version, api.MinVersion)
-	content, err := ioutil.ReadAll(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(string(content)), checker.Contains, expected)
+	content, err := io.ReadAll(body)
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(string(content)), expected)
 }
 
-func (s *DockerSuite) TestAPIErrorJSON(c *check.C) {
+func (s *DockerSuite) TestAPIErrorJSON(c *testing.T) {
 	httpResp, body, err := request.Post("/containers/create", request.JSONBody(struct{}{}))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(httpResp.StatusCode, checker.Equals, http.StatusInternalServerError)
+		assert.Equal(c, httpResp.StatusCode, http.StatusInternalServerError)
 	} else {
-		c.Assert(httpResp.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, httpResp.StatusCode, http.StatusBadRequest)
 	}
-	c.Assert(httpResp.Header.Get("Content-Type"), checker.Equals, "application/json")
+	assert.Assert(c, strings.Contains(httpResp.Header.Get("Content-Type"), "application/json"))
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(getErrorMessage(c, b), checker.Equals, "Config cannot be empty in order to create a container")
+	assert.NilError(c, err)
+	assert.Equal(c, getErrorMessage(c, b), "Config cannot be empty in order to create a container")
 }
 
-func (s *DockerSuite) TestAPIErrorPlainText(c *check.C) {
+func (s *DockerSuite) TestAPIErrorPlainText(c *testing.T) {
 	// Windows requires API 1.25 or later. This test is validating a behaviour which was present
 	// in v1.23, but changed in 1.24, hence not applicable on Windows. See apiVersionSupportsJSONErrors
 	testRequires(c, DaemonIsLinux)
 	httpResp, body, err := request.Post("/v1.23/containers/create", request.JSONBody(struct{}{}))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(httpResp.StatusCode, checker.Equals, http.StatusInternalServerError)
+		assert.Equal(c, httpResp.StatusCode, http.StatusInternalServerError)
 	} else {
-		c.Assert(httpResp.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, httpResp.StatusCode, http.StatusBadRequest)
 	}
-	c.Assert(httpResp.Header.Get("Content-Type"), checker.Contains, "text/plain")
+	assert.Assert(c, strings.Contains(httpResp.Header.Get("Content-Type"), "text/plain"))
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(string(b)), checker.Equals, "Config cannot be empty in order to create a container")
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(string(b)), "Config cannot be empty in order to create a container")
 }
 
-func (s *DockerSuite) TestAPIErrorNotFoundJSON(c *check.C) {
+func (s *DockerSuite) TestAPIErrorNotFoundJSON(c *testing.T) {
 	// 404 is a different code path to normal errors, so test separately
 	httpResp, body, err := request.Get("/notfound", request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(httpResp.StatusCode, checker.Equals, http.StatusNotFound)
-	c.Assert(httpResp.Header.Get("Content-Type"), checker.Equals, "application/json")
+	assert.NilError(c, err)
+	assert.Equal(c, httpResp.StatusCode, http.StatusNotFound)
+	assert.Assert(c, strings.Contains(httpResp.Header.Get("Content-Type"), "application/json"))
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(getErrorMessage(c, b), checker.Equals, "page not found")
+	assert.NilError(c, err)
+	assert.Equal(c, getErrorMessage(c, b), "page not found")
 }
 
-func (s *DockerSuite) TestAPIErrorNotFoundPlainText(c *check.C) {
+func (s *DockerSuite) TestAPIErrorNotFoundPlainText(c *testing.T) {
 	httpResp, body, err := request.Get("/v1.23/notfound", request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(httpResp.StatusCode, checker.Equals, http.StatusNotFound)
-	c.Assert(httpResp.Header.Get("Content-Type"), checker.Contains, "text/plain")
+	assert.NilError(c, err)
+	assert.Equal(c, httpResp.StatusCode, http.StatusNotFound)
+	assert.Assert(c, strings.Contains(httpResp.Header.Get("Content-Type"), "text/plain"))
 	b, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(string(b)), checker.Equals, "page not found")
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(string(b)), "page not found")
 }
diff --git a/integration-cli/docker_cli_attach_test.go b/integration-cli/docker_cli_attach_test.go
index ef2c708bbe63a..d82565c68e17e 100644
--- a/integration-cli/docker_cli_attach_test.go
+++ b/integration-cli/docker_cli_attach_test.go
@@ -8,16 +8,17 @@ import (
 	"runtime"
 	"strings"
 	"sync"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/integration-cli/cli"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
 const attachWait = 5 * time.Second
 
-func (s *DockerSuite) TestAttachMultipleAndRestart(c *check.C) {
+func (s *DockerSuite) TestAttachMultipleAndRestart(c *testing.T) {
 	endGroup := &sync.WaitGroup{}
 	startGroup := &sync.WaitGroup{}
 	endGroup.Add(3)
@@ -50,24 +51,24 @@ func (s *DockerSuite) TestAttachMultipleAndRestart(c *check.C) {
 
 			out, err := cmd.StdoutPipe()
 			if err != nil {
-				c.Fatal(err)
+				c.Error(err)
 			}
 			defer out.Close()
 
 			if err := cmd.Start(); err != nil {
-				c.Fatal(err)
+				c.Error(err)
 			}
 
 			buf := make([]byte, 1024)
 
 			if _, err := out.Read(buf); err != nil && err != io.EOF {
-				c.Fatal(err)
+				c.Error(err)
 			}
 
 			startGroup.Done()
 
 			if !strings.Contains(string(buf), "hello") {
-				c.Fatalf("unexpected output %s expected hello\n", string(buf))
+				c.Errorf("unexpected output %s expected hello\n", string(buf))
 			}
 		}()
 	}
@@ -87,8 +88,8 @@ func (s *DockerSuite) TestAttachMultipleAndRestart(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestAttachTTYWithoutStdin(c *check.C) {
-	// TODO @jhowardmsft. Figure out how to get this running again reliable on Windows.
+func (s *DockerSuite) TestAttachTTYWithoutStdin(c *testing.T) {
+	// TODO: Figure out how to get this running again reliable on Windows.
 	// It works by accident at the moment. Sometimes. I've gone back to v1.13.0 and see the same.
 	// On Windows, docker run -d -ti busybox causes the container to exit immediately.
 	// Obviously a year back when I updated the test, that was not the case. However,
@@ -99,9 +100,9 @@ func (s *DockerSuite) TestAttachTTYWithoutStdin(c *check.C) {
 	out, _ := dockerCmd(c, "run", "-d", "-ti", "busybox")
 
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), check.IsNil)
+	assert.NilError(c, waitRun(id))
 
-	done := make(chan error)
+	done := make(chan error, 1)
 	go func() {
 		defer close(done)
 
@@ -126,13 +127,13 @@ func (s *DockerSuite) TestAttachTTYWithoutStdin(c *check.C) {
 
 	select {
 	case err := <-done:
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(attachWait):
 		c.Fatal("attach is running but should have failed")
 	}
 }
 
-func (s *DockerSuite) TestAttachDisconnect(c *check.C) {
+func (s *DockerSuite) TestAttachDisconnect(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-di", "busybox", "/bin/cat")
 	id := strings.TrimSpace(out)
@@ -144,28 +145,28 @@ func (s *DockerSuite) TestAttachDisconnect(c *check.C) {
 	}
 	defer stdin.Close()
 	stdout, err := cmd.StdoutPipe()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	defer stdout.Close()
-	c.Assert(cmd.Start(), check.IsNil)
+	assert.Assert(c, cmd.Start() == nil)
 	defer func() {
 		cmd.Process.Kill()
 		cmd.Wait()
 	}()
 
 	_, err = stdin.Write([]byte("hello\n"))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	out, err = bufio.NewReader(stdout).ReadString('\n')
-	c.Assert(err, check.IsNil)
-	c.Assert(strings.TrimSpace(out), check.Equals, "hello")
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 
-	c.Assert(stdin.Close(), check.IsNil)
+	assert.Assert(c, stdin.Close() == nil)
 
 	// Expect container to still be running after stdin is closed
 	running := inspectField(c, id, "State.Running")
-	c.Assert(running, check.Equals, "true")
+	assert.Equal(c, running, "true")
 }
 
-func (s *DockerSuite) TestAttachPausedContainer(c *check.C) {
+func (s *DockerSuite) TestAttachPausedContainer(c *testing.T) {
 	testRequires(c, IsPausable)
 	runSleepingContainer(c, "-d", "--name=test")
 	dockerCmd(c, "pause", "test")
diff --git a/integration-cli/docker_cli_attach_unix_test.go b/integration-cli/docker_cli_attach_unix_test.go
index e270fda9cbef6..8dbb55e761829 100644
--- a/integration-cli/docker_cli_attach_unix_test.go
+++ b/integration-cli/docker_cli_attach_unix_test.go
@@ -1,39 +1,40 @@
+//go:build !windows
 // +build !windows
 
 package main
 
 import (
 	"bufio"
-	"io/ioutil"
+	"io"
 	"os/exec"
 	"strings"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-	"github.com/kr/pty"
+	"github.com/creack/pty"
+	"gotest.tools/v3/assert"
 )
 
 // #9860 Make sure attach ends when container ends (with no errors)
-func (s *DockerSuite) TestAttachClosedOnContainerStop(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestAttachClosedOnContainerStop(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 
 	out, _ := dockerCmd(c, "run", "-dti", "busybox", "/bin/sh", "-c", `trap 'exit 0' SIGTERM; while true; do sleep 1; done`)
 
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), check.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	pty, tty, err := pty.Open()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	attachCmd := exec.Command(dockerBinary, "attach", id)
 	attachCmd.Stdin = tty
 	attachCmd.Stdout = tty
 	attachCmd.Stderr = tty
 	err = attachCmd.Start()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
-	errChan := make(chan error)
+	errChan := make(chan error, 1)
 	go func() {
 		time.Sleep(300 * time.Millisecond)
 		defer close(errChan)
@@ -50,31 +51,31 @@ func (s *DockerSuite) TestAttachClosedOnContainerStop(c *check.C) {
 	select {
 	case err := <-errChan:
 		tty.Close()
-		out, _ := ioutil.ReadAll(pty)
-		c.Assert(err, check.IsNil, check.Commentf("out: %v", string(out)))
+		out, _ := io.ReadAll(pty)
+		assert.Assert(c, err == nil, "out: %v", string(out))
 	case <-time.After(attachWait):
 		c.Fatal("timed out without attach returning")
 	}
 
 }
 
-func (s *DockerSuite) TestAttachAfterDetach(c *check.C) {
+func (s *DockerSuite) TestAttachAfterDetach(c *testing.T) {
 	name := "detachtest"
 
 	cpty, tty, err := pty.Open()
-	c.Assert(err, checker.IsNil, check.Commentf("Could not open pty: %v", err))
+	assert.NilError(c, err, "Could not open pty: %v", err)
 	cmd := exec.Command(dockerBinary, "run", "-ti", "--name", name, "busybox")
 	cmd.Stdin = tty
 	cmd.Stdout = tty
 	cmd.Stderr = tty
 
-	cmdExit := make(chan error)
+	cmdExit := make(chan error, 1)
 	go func() {
 		cmdExit <- cmd.Run()
 		close(cmdExit)
 	}()
 
-	c.Assert(waitRun(name), check.IsNil)
+	assert.Assert(c, waitRun(name) == nil)
 
 	cpty.Write([]byte{16})
 	time.Sleep(100 * time.Millisecond)
@@ -87,7 +88,7 @@ func (s *DockerSuite) TestAttachAfterDetach(c *check.C) {
 	}
 
 	cpty, tty, err = pty.Open()
-	c.Assert(err, checker.IsNil, check.Commentf("Could not open pty: %v", err))
+	assert.NilError(c, err, "Could not open pty: %v", err)
 
 	cmd = exec.Command(dockerBinary, "attach", name)
 	cmd.Stdin = tty
@@ -95,7 +96,7 @@ func (s *DockerSuite) TestAttachAfterDetach(c *check.C) {
 	cmd.Stderr = tty
 
 	err = cmd.Start()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer cmd.Process.Kill()
 
 	bytes := make([]byte, 10)
@@ -114,45 +115,45 @@ func (s *DockerSuite) TestAttachAfterDetach(c *check.C) {
 
 	select {
 	case err := <-readErr:
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(2 * time.Second):
 		c.Fatal("timeout waiting for attach read")
 	}
 
-	c.Assert(string(bytes[:nBytes]), checker.Contains, "/ #")
+	assert.Assert(c, strings.Contains(string(bytes[:nBytes]), "/ #"))
 }
 
 // TestAttachDetach checks that attach in tty mode can be detached using the long container ID
-func (s *DockerSuite) TestAttachDetach(c *check.C) {
+func (s *DockerSuite) TestAttachDetach(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-itd", "busybox", "cat")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), check.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	cpty, tty, err := pty.Open()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	defer cpty.Close()
 
 	cmd := exec.Command(dockerBinary, "attach", id)
 	cmd.Stdin = tty
 	stdout, err := cmd.StdoutPipe()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	defer stdout.Close()
 	err = cmd.Start()
-	c.Assert(err, check.IsNil)
-	c.Assert(waitRun(id), check.IsNil)
+	assert.NilError(c, err)
+	assert.NilError(c, waitRun(id))
 
 	_, err = cpty.Write([]byte("hello\n"))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	out, err = bufio.NewReader(stdout).ReadString('\n')
-	c.Assert(err, check.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello")
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 
 	// escape sequence
 	_, err = cpty.Write([]byte{16})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	time.Sleep(100 * time.Millisecond)
 	_, err = cpty.Write([]byte{17})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	ch := make(chan struct{})
 	go func() {
@@ -167,5 +168,5 @@ func (s *DockerSuite) TestAttachDetach(c *check.C) {
 	}
 
 	running := inspectField(c, id, "State.Running")
-	c.Assert(running, checker.Equals, "true") // container should be running
+	assert.Equal(c, running, "true") // container should be running
 }
diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go
index 091f13709934e..34f61b450f145 100644
--- a/integration-cli/docker_cli_build_test.go
+++ b/integration-cli/docker_cli_build_test.go
@@ -5,7 +5,6 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -13,32 +12,33 @@ import (
 	"runtime"
 	"strconv"
 	"strings"
+	"testing"
 	"text/template"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/docker/docker/internal/test/fakecontext"
-	"github.com/docker/docker/internal/test/fakegit"
-	"github.com/docker/docker/internal/test/fakestorage"
-	"github.com/docker/docker/internal/testutil"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/system"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil"
+	"github.com/docker/docker/testutil/fakecontext"
+	"github.com/docker/docker/testutil/fakegit"
+	"github.com/docker/docker/testutil/fakestorage"
 	"github.com/moby/buildkit/frontend/dockerfile/command"
-	"github.com/opencontainers/go-digest"
-	"gotest.tools/icmd"
+	digest "github.com/opencontainers/go-digest"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/icmd"
 )
 
-func (s *DockerSuite) TestBuildJSONEmptyRun(c *check.C) {
+func (s *DockerSuite) TestBuildJSONEmptyRun(c *testing.T) {
 	cli.BuildCmd(c, "testbuildjsonemptyrun", build.WithDockerfile(`
     FROM busybox
     RUN []
     `))
 }
 
-func (s *DockerSuite) TestBuildShCmdJSONEntrypoint(c *check.C) {
+func (s *DockerSuite) TestBuildShCmdJSONEntrypoint(c *testing.T) {
 	name := "testbuildshcmdjsonentrypoint"
 	expected := "/bin/sh -c echo test"
 	if testEnv.OSType == "windows" {
@@ -57,7 +57,7 @@ func (s *DockerSuite) TestBuildShCmdJSONEntrypoint(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildEnvironmentReplacementUser(c *check.C) {
+func (s *DockerSuite) TestBuildEnvironmentReplacementUser(c *testing.T) {
 	// Windows does not support FROM scratch or the USER command
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildenvironmentreplacement"
@@ -74,7 +74,7 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementUser(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildEnvironmentReplacementVolume(c *check.C) {
+func (s *DockerSuite) TestBuildEnvironmentReplacementVolume(c *testing.T) {
 	name := "testbuildenvironmentreplacement"
 
 	var volumePath string
@@ -99,7 +99,7 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementVolume(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildEnvironmentReplacementExpose(c *check.C) {
+func (s *DockerSuite) TestBuildEnvironmentReplacementExpose(c *testing.T) {
 	// Windows does not support FROM scratch or the EXPOSE command
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildenvironmentreplacement"
@@ -124,7 +124,7 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementExpose(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildEnvironmentReplacementWorkdir(c *check.C) {
+func (s *DockerSuite) TestBuildEnvironmentReplacementWorkdir(c *testing.T) {
 	name := "testbuildenvironmentreplacement"
 
 	buildImageSuccessfully(c, name, build.WithDockerfile(`
@@ -144,7 +144,7 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementWorkdir(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildEnvironmentReplacementAddCopy(c *check.C) {
+func (s *DockerSuite) TestBuildEnvironmentReplacementAddCopy(c *testing.T) {
 	name := "testbuildenvironmentreplacement"
 
 	buildImageSuccessfully(c, name, build.WithBuildContext(c,
@@ -168,7 +168,7 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementAddCopy(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildEnvironmentReplacementEnv(c *check.C) {
+func (s *DockerSuite) TestBuildEnvironmentReplacementEnv(c *testing.T) {
 	// ENV expansions work differently in Windows
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildenvironmentreplacement"
@@ -231,7 +231,7 @@ func (s *DockerSuite) TestBuildEnvironmentReplacementEnv(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildHandleEscapesInVolume(c *check.C) {
+func (s *DockerSuite) TestBuildHandleEscapesInVolume(c *testing.T) {
 	// The volume paths used in this test are invalid on Windows
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildhandleescapes"
@@ -275,7 +275,7 @@ func (s *DockerSuite) TestBuildHandleEscapesInVolume(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildOnBuildLowercase(c *check.C) {
+func (s *DockerSuite) TestBuildOnBuildLowercase(c *testing.T) {
 	name := "testbuildonbuildlowercase"
 	name2 := "testbuildonbuildlowercase2"
 
@@ -299,7 +299,7 @@ func (s *DockerSuite) TestBuildOnBuildLowercase(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildEnvEscapes(c *check.C) {
+func (s *DockerSuite) TestBuildEnvEscapes(c *testing.T) {
 	// ENV expansions work differently in Windows
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildenvescapes"
@@ -316,7 +316,7 @@ func (s *DockerSuite) TestBuildEnvEscapes(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildEnvOverwrite(c *check.C) {
+func (s *DockerSuite) TestBuildEnvOverwrite(c *testing.T) {
 	// ENV expansions work differently in Windows
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildenvoverwrite"
@@ -334,7 +334,7 @@ func (s *DockerSuite) TestBuildEnvOverwrite(c *check.C) {
 }
 
 // FIXME(vdemeester) why we disabled cache here ?
-func (s *DockerSuite) TestBuildOnBuildCmdEntrypointJSON(c *check.C) {
+func (s *DockerSuite) TestBuildOnBuildCmdEntrypointJSON(c *testing.T) {
 	name1 := "onbuildcmd"
 	name2 := "onbuildgenerated"
 
@@ -351,7 +351,7 @@ ONBUILD RUN ["true"]`))
 }
 
 // FIXME(vdemeester) why we disabled cache here ?
-func (s *DockerSuite) TestBuildOnBuildEntrypointJSON(c *check.C) {
+func (s *DockerSuite) TestBuildOnBuildEntrypointJSON(c *testing.T) {
 	name1 := "onbuildcmd"
 	name2 := "onbuildgenerated"
 
@@ -368,7 +368,7 @@ ONBUILD ENTRYPOINT ["echo"]`))
 
 }
 
-func (s *DockerSuite) TestBuildCacheAdd(c *check.C) {
+func (s *DockerSuite) TestBuildCacheAdd(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows doesn't have httpserver image yet
 	name := "testbuildtwoimageswithadd"
 	server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{
@@ -388,8 +388,8 @@ func (s *DockerSuite) TestBuildCacheAdd(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildLastModified(c *check.C) {
-	// Temporary fix for #30890. TODO @jhowardmsft figure out what
+func (s *DockerSuite) TestBuildLastModified(c *testing.T) {
+	// Temporary fix for #30890. TODO: figure out what
 	// has changed in the master busybox image.
 	testRequires(c, DaemonIsLinux)
 
@@ -441,7 +441,7 @@ ADD %s/file /`
 // Regression for https://github.com/docker/docker/pull/27805
 // Makes sure that we don't use the cache if the contents of
 // a file in a subfolder of the context is modified and we re-build.
-func (s *DockerSuite) TestBuildModifyFileInFolder(c *check.C) {
+func (s *DockerSuite) TestBuildModifyFileInFolder(c *testing.T) {
 	name := "testbuildmodifyfileinfolder"
 
 	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`FROM busybox
@@ -463,7 +463,7 @@ ADD folder/file /test/changetarget`))
 	}
 }
 
-func (s *DockerSuite) TestBuildAddSingleFileToRoot(c *check.C) {
+func (s *DockerSuite) TestBuildAddSingleFileToRoot(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testaddimg", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", fmt.Sprintf(`FROM busybox
@@ -479,7 +479,7 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expecte
 }
 
 // Issue #3960: "ADD src ." hangs
-func (s *DockerSuite) TestBuildAddSingleFileToWorkdir(c *check.C) {
+func (s *DockerSuite) TestBuildAddSingleFileToWorkdir(c *testing.T) {
 	name := "testaddsinglefiletoworkdir"
 	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(
 		`FROM busybox
@@ -489,7 +489,7 @@ func (s *DockerSuite) TestBuildAddSingleFileToWorkdir(c *check.C) {
 		}))
 	defer ctx.Close()
 
-	errChan := make(chan error)
+	errChan := make(chan error, 1)
 	go func() {
 		errChan <- buildImage(name, build.WithExternalBuildContext(ctx)).Error
 		close(errChan)
@@ -498,11 +498,11 @@ func (s *DockerSuite) TestBuildAddSingleFileToWorkdir(c *check.C) {
 	case <-time.After(15 * time.Second):
 		c.Fatal("Build with adding to workdir timed out")
 	case err := <-errChan:
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	}
 }
 
-func (s *DockerSuite) TestBuildAddSingleFileToExistDir(c *check.C) {
+func (s *DockerSuite) TestBuildAddSingleFileToExistDir(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	cli.BuildCmd(c, "testaddsinglefiletoexistdir", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM busybox
@@ -518,7 +518,7 @@ RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio'
 		build.WithFile("test_file", "test1")))
 }
 
-func (s *DockerSuite) TestBuildCopyAddMultipleFiles(c *check.C) {
+func (s *DockerSuite) TestBuildCopyAddMultipleFiles(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{
 		"robots.txt": "hello",
@@ -551,7 +551,7 @@ RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio'
 
 // These tests are mainly for user namespaces to verify that new directories
 // are created as the remapped root uid/gid pair
-func (s *DockerSuite) TestBuildUsernamespaceValidateRemappedRoot(c *check.C) {
+func (s *DockerSuite) TestBuildUsernamespaceValidateRemappedRoot(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testCases := []string{
 		"ADD . /new_dir",
@@ -570,7 +570,7 @@ RUN [ $(ls -l / | grep new_dir | awk '{print $3":"$4}') = 'root:root' ]`, tc)),
 	}
 }
 
-func (s *DockerSuite) TestBuildAddAndCopyFileWithWhitespace(c *check.C) {
+func (s *DockerSuite) TestBuildAddAndCopyFileWithWhitespace(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Not currently passing on Windows
 	name := "testaddfilewithwhitespace"
 
@@ -603,7 +603,7 @@ RUN [ $(cat "/test dir/test_file6") = 'test6' ]`, command, command, command, com
 	}
 }
 
-func (s *DockerSuite) TestBuildCopyFileWithWhitespaceOnWindows(c *check.C) {
+func (s *DockerSuite) TestBuildCopyFileWithWhitespaceOnWindows(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	dockerfile := `FROM ` + testEnv.PlatformDefaults.BaseImage + `
 RUN mkdir "C:/test dir"
@@ -633,7 +633,7 @@ RUN find "test6" "C:/test dir/test_file6"`
 	))
 }
 
-func (s *DockerSuite) TestBuildCopyWildcard(c *check.C) {
+func (s *DockerSuite) TestBuildCopyWildcard(c *testing.T) {
 	name := "testcopywildcard"
 	server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{
 		"robots.txt": "hello",
@@ -673,7 +673,7 @@ func (s *DockerSuite) TestBuildCopyWildcard(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildCopyWildcardInName(c *check.C) {
+func (s *DockerSuite) TestBuildCopyWildcardInName(c *testing.T) {
 	// Run this only on Linux
 	// Below is the original comment (that I don't agree with — vdemeester)
 	// Normally we would do c.Fatal(err) here but given that
@@ -693,7 +693,7 @@ func (s *DockerSuite) TestBuildCopyWildcardInName(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildCopyWildcardCache(c *check.C) {
+func (s *DockerSuite) TestBuildCopyWildcardCache(c *testing.T) {
 	name := "testcopywildcardcache"
 	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`FROM busybox
 	COPY file1.txt /tmp/`),
@@ -719,7 +719,7 @@ func (s *DockerSuite) TestBuildCopyWildcardCache(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildAddSingleFileToNonExistingDir(c *check.C) {
+func (s *DockerSuite) TestBuildAddSingleFileToNonExistingDir(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testaddsinglefiletononexistingdir", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM busybox
@@ -734,7 +734,7 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`),
 		build.WithFile("test_file", "test1")))
 }
 
-func (s *DockerSuite) TestBuildAddDirContentToRoot(c *check.C) {
+func (s *DockerSuite) TestBuildAddDirContentToRoot(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testadddircontenttoroot", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM busybox
@@ -748,7 +748,7 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`),
 		build.WithFile("test_dir/test_file", "test1")))
 }
 
-func (s *DockerSuite) TestBuildAddDirContentToExistingDir(c *check.C) {
+func (s *DockerSuite) TestBuildAddDirContentToExistingDir(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testadddircontenttoexistingdir", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM busybox
@@ -764,7 +764,7 @@ RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`),
 		build.WithFile("test_dir/test_file", "test1")))
 }
 
-func (s *DockerSuite) TestBuildAddWholeDirToRoot(c *check.C) {
+func (s *DockerSuite) TestBuildAddWholeDirToRoot(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testaddwholedirtoroot", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", fmt.Sprintf(`FROM busybox
@@ -782,7 +782,7 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expecte
 }
 
 // Testing #5941 : Having an etc directory in context conflicts with the /etc/mtab
-func (s *DockerSuite) TestBuildAddOrCopyEtcToRootShouldNotConflict(c *check.C) {
+func (s *DockerSuite) TestBuildAddOrCopyEtcToRootShouldNotConflict(c *testing.T) {
 	buildImageSuccessfully(c, "testaddetctoroot", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM `+minimalBaseImage()+`
 ADD . /`),
@@ -794,7 +794,7 @@ COPY . /`),
 }
 
 // Testing #9401 : Losing setuid flag after a ADD
-func (s *DockerSuite) TestBuildAddPreservesFilesSpecialBits(c *check.C) {
+func (s *DockerSuite) TestBuildAddPreservesFilesSpecialBits(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testaddetctoroot", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM busybox
@@ -807,7 +807,7 @@ RUN [ $(ls -l /usr/bin/suidbin | awk '{print $1}') = '-rwsr-xr-x' ]`),
 		build.WithFile("/data/usr/test_file", "test1")))
 }
 
-func (s *DockerSuite) TestBuildCopySingleFileToRoot(c *check.C) {
+func (s *DockerSuite) TestBuildCopySingleFileToRoot(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testcopysinglefiletoroot", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", fmt.Sprintf(`FROM busybox
@@ -823,7 +823,7 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expecte
 }
 
 // Issue #3960: "ADD src ." hangs - adapted for COPY
-func (s *DockerSuite) TestBuildCopySingleFileToWorkdir(c *check.C) {
+func (s *DockerSuite) TestBuildCopySingleFileToWorkdir(c *testing.T) {
 	name := "testcopysinglefiletoworkdir"
 	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`FROM busybox
 COPY test_file .`),
@@ -832,7 +832,7 @@ COPY test_file .`),
 		}))
 	defer ctx.Close()
 
-	errChan := make(chan error)
+	errChan := make(chan error, 1)
 	go func() {
 		errChan <- buildImage(name, build.WithExternalBuildContext(ctx)).Error
 		close(errChan)
@@ -841,11 +841,11 @@ COPY test_file .`),
 	case <-time.After(15 * time.Second):
 		c.Fatal("Build with adding to workdir timed out")
 	case err := <-errChan:
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	}
 }
 
-func (s *DockerSuite) TestBuildCopySingleFileToExistDir(c *check.C) {
+func (s *DockerSuite) TestBuildCopySingleFileToExistDir(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testcopysinglefiletoexistdir", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM busybox
@@ -861,7 +861,7 @@ RUN [ $(ls -l /exists/exists_file | awk '{print $3":"$4}') = 'dockerio:dockerio'
 		build.WithFile("test_file", "test1")))
 }
 
-func (s *DockerSuite) TestBuildCopySingleFileToNonExistDir(c *check.C) {
+func (s *DockerSuite) TestBuildCopySingleFileToNonExistDir(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific
 	buildImageSuccessfully(c, "testcopysinglefiletononexistdir", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM busybox
@@ -876,7 +876,7 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`),
 		build.WithFile("test_file", "test1")))
 }
 
-func (s *DockerSuite) TestBuildCopyDirContentToRoot(c *check.C) {
+func (s *DockerSuite) TestBuildCopyDirContentToRoot(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testcopydircontenttoroot", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM busybox
@@ -890,7 +890,7 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`),
 		build.WithFile("test_dir/test_file", "test1")))
 }
 
-func (s *DockerSuite) TestBuildCopyDirContentToExistDir(c *check.C) {
+func (s *DockerSuite) TestBuildCopyDirContentToExistDir(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testcopydircontenttoexistdir", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM busybox
@@ -906,7 +906,7 @@ RUN [ $(ls -l /exists/test_file | awk '{print $3":"$4}') = 'root:root' ]`),
 		build.WithFile("test_dir/test_file", "test1")))
 }
 
-func (s *DockerSuite) TestBuildCopyWholeDirToRoot(c *check.C) {
+func (s *DockerSuite) TestBuildCopyWholeDirToRoot(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Linux specific test
 	buildImageSuccessfully(c, "testcopywholedirtoroot", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", fmt.Sprintf(`FROM busybox
@@ -923,7 +923,7 @@ RUN [ $(ls -l /exists | awk '{print $3":"$4}') = 'dockerio:dockerio' ]`, expecte
 		build.WithFile("test_dir/test_file", "test1")))
 }
 
-func (s *DockerSuite) TestBuildAddBadLinks(c *check.C) {
+func (s *DockerSuite) TestBuildAddBadLinks(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Not currently working on Windows
 
 	dockerfile := `
@@ -938,7 +938,7 @@ func (s *DockerSuite) TestBuildAddBadLinks(c *check.C) {
 	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(dockerfile))
 	defer ctx.Close()
 
-	tempDir, err := ioutil.TempDir("", "test-link-absolute-temp-")
+	tempDir, err := os.MkdirTemp("", "test-link-absolute-temp-")
 	if err != nil {
 		c.Fatalf("failed to create temporary directory: %s", tempDir)
 	}
@@ -997,13 +997,13 @@ func (s *DockerSuite) TestBuildAddBadLinks(c *check.C) {
 	}
 
 	buildImageSuccessfully(c, name, build.WithExternalBuildContext(ctx))
-	if _, err := os.Stat(nonExistingFile); err == nil || err != nil && !os.IsNotExist(err) {
+	if _, err := os.Stat(nonExistingFile); err == nil || !os.IsNotExist(err) {
 		c.Fatalf("%s shouldn't have been written and it shouldn't exist", nonExistingFile)
 	}
 
 }
 
-func (s *DockerSuite) TestBuildAddBadLinksVolume(c *check.C) {
+func (s *DockerSuite) TestBuildAddBadLinksVolume(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // ln not implemented on Windows busybox
 	const (
 		dockerfileTemplate = `
@@ -1014,7 +1014,7 @@ func (s *DockerSuite) TestBuildAddBadLinksVolume(c *check.C) {
 		targetFile = "foo.txt"
 	)
 
-	tempDir, err := ioutil.TempDir("", "test-link-absolute-volume-temp-")
+	tempDir, err := os.MkdirTemp("", "test-link-absolute-volume-temp-")
 	if err != nil {
 		c.Fatalf("failed to create temporary directory: %s", tempDir)
 	}
@@ -1038,7 +1038,7 @@ func (s *DockerSuite) TestBuildAddBadLinksVolume(c *check.C) {
 	}
 
 	buildImageSuccessfully(c, "test-link-absolute-volume", build.WithExternalBuildContext(ctx))
-	if _, err := os.Stat(nonExistingFile); err == nil || err != nil && !os.IsNotExist(err) {
+	if _, err := os.Stat(nonExistingFile); err == nil || !os.IsNotExist(err) {
 		c.Fatalf("%s shouldn't have been written and it shouldn't exist", nonExistingFile)
 	}
 
@@ -1046,8 +1046,8 @@ func (s *DockerSuite) TestBuildAddBadLinksVolume(c *check.C) {
 
 // Issue #5270 - ensure we throw a better error than "unexpected EOF"
 // when we can't access files in the context.
-func (s *DockerSuite) TestBuildWithInaccessibleFilesInContext(c *check.C) {
-	testRequires(c, DaemonIsLinux, UnixCli, SameHostDaemon) // test uses chown/chmod: not available on windows
+func (s *DockerSuite) TestBuildWithInaccessibleFilesInContext(c *testing.T) {
+	testRequires(c, DaemonIsLinux, UnixCli, testEnv.IsLocalDaemon) // test uses chown/chmod: not available on windows
 
 	{
 		name := "testbuildinaccessiblefiles"
@@ -1167,7 +1167,7 @@ func (s *DockerSuite) TestBuildWithInaccessibleFilesInContext(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildForceRm(c *check.C) {
+func (s *DockerSuite) TestBuildForceRm(c *testing.T) {
 	containerCountBefore := getContainerCount(c)
 	name := "testbuildforcerm"
 
@@ -1186,7 +1186,7 @@ func (s *DockerSuite) TestBuildForceRm(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildRm(c *check.C) {
+func (s *DockerSuite) TestBuildRm(c *testing.T) {
 	name := "testbuildrm"
 
 	testCases := []struct {
@@ -1229,7 +1229,7 @@ func (s *DockerSuite) TestBuildRm(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildWithVolumes(c *check.C) {
+func (s *DockerSuite) TestBuildWithVolumes(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Invalid volume paths on Windows
 	var (
 		result   map[string]map[string]struct{}
@@ -1264,7 +1264,7 @@ func (s *DockerSuite) TestBuildWithVolumes(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildMaintainer(c *check.C) {
+func (s *DockerSuite) TestBuildMaintainer(c *testing.T) {
 	name := "testbuildmaintainer"
 
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
@@ -1277,7 +1277,7 @@ func (s *DockerSuite) TestBuildMaintainer(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildUser(c *check.C) {
+func (s *DockerSuite) TestBuildUser(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuilduser"
 	expected := "dockerio"
@@ -1291,7 +1291,7 @@ func (s *DockerSuite) TestBuildUser(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildRelativeWorkdir(c *check.C) {
+func (s *DockerSuite) TestBuildRelativeWorkdir(c *testing.T) {
 	name := "testbuildrelativeworkdir"
 
 	var (
@@ -1333,7 +1333,7 @@ func (s *DockerSuite) TestBuildRelativeWorkdir(c *check.C) {
 
 // #22181 Regression test. Single end-to-end test of using
 // Windows semantics. Most path handling verifications are in unit tests
-func (s *DockerSuite) TestBuildWindowsWorkdirProcessing(c *check.C) {
+func (s *DockerSuite) TestBuildWindowsWorkdirProcessing(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	buildImageSuccessfully(c, "testbuildwindowsworkdirprocessing", build.WithDockerfile(`FROM busybox
 		WORKDIR C:\\foo
@@ -1344,9 +1344,9 @@ func (s *DockerSuite) TestBuildWindowsWorkdirProcessing(c *check.C) {
 
 // #22181 Regression test. Most paths handling verifications are in unit test.
 // One functional test for end-to-end
-func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
+func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
-	// TODO Windows (@jhowardmsft). Needs a follow-up PR to 22181 to
+	// TODO Windows. Needs a follow-up PR to 22181 to
 	// support backslash such as .\\ being equivalent to ./ and c:\\ being
 	// equivalent to c:/. This is not currently (nor ever has been) supported
 	// by docker on the Windows platform.
@@ -1377,7 +1377,7 @@ func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildWorkdirWithEnvVariables(c *check.C) {
+func (s *DockerSuite) TestBuildWorkdirWithEnvVariables(c *testing.T) {
 	name := "testbuildworkdirwithenvvariables"
 
 	var expected string
@@ -1398,7 +1398,7 @@ func (s *DockerSuite) TestBuildWorkdirWithEnvVariables(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildRelativeCopy(c *check.C) {
+func (s *DockerSuite) TestBuildRelativeCopy(c *testing.T) {
 	// cat /test1/test2/foo gets permission denied for the user
 	testRequires(c, NotUserNamespace)
 
@@ -1437,7 +1437,7 @@ func (s *DockerSuite) TestBuildRelativeCopy(c *check.C) {
 }
 
 // FIXME(vdemeester) should be unit test
-func (s *DockerSuite) TestBuildBlankName(c *check.C) {
+func (s *DockerSuite) TestBuildBlankName(c *testing.T) {
 	name := "testbuildblankname"
 	testCases := []struct {
 		expression     string
@@ -1466,7 +1466,7 @@ func (s *DockerSuite) TestBuildBlankName(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildEnv(c *check.C) {
+func (s *DockerSuite) TestBuildEnv(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // ENV expansion is different in Windows
 	name := "testbuildenv"
 	expected := "[PATH=/test:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PORT=2375]"
@@ -1480,7 +1480,7 @@ func (s *DockerSuite) TestBuildEnv(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildPATH(c *check.C) {
+func (s *DockerSuite) TestBuildPATH(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // ENV expansion is different in Windows
 
 	defPath := "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
@@ -1509,11 +1509,11 @@ func (s *DockerSuite) TestBuildPATH(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildContextCleanup(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestBuildContextCleanup(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 
 	name := "testbuildcontextcleanup"
-	entries, err := ioutil.ReadDir(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "tmp"))
+	entries, err := os.ReadDir(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "tmp"))
 	if err != nil {
 		c.Fatalf("failed to list contents of tmp dir: %s", err)
 	}
@@ -1521,7 +1521,7 @@ func (s *DockerSuite) TestBuildContextCleanup(c *check.C) {
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
         ENTRYPOINT ["/bin/echo"]`))
 
-	entriesFinal, err := ioutil.ReadDir(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "tmp"))
+	entriesFinal, err := os.ReadDir(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "tmp"))
 	if err != nil {
 		c.Fatalf("failed to list contents of tmp dir: %s", err)
 	}
@@ -1531,11 +1531,11 @@ func (s *DockerSuite) TestBuildContextCleanup(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildContextCleanupFailedBuild(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestBuildContextCleanupFailedBuild(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 
 	name := "testbuildcontextcleanup"
-	entries, err := ioutil.ReadDir(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "tmp"))
+	entries, err := os.ReadDir(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "tmp"))
 	if err != nil {
 		c.Fatalf("failed to list contents of tmp dir: %s", err)
 	}
@@ -1545,7 +1545,7 @@ func (s *DockerSuite) TestBuildContextCleanupFailedBuild(c *check.C) {
 		ExitCode: 1,
 	})
 
-	entriesFinal, err := ioutil.ReadDir(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "tmp"))
+	entriesFinal, err := os.ReadDir(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "tmp"))
 	if err != nil {
 		c.Fatalf("failed to list contents of tmp dir: %s", err)
 	}
@@ -1555,9 +1555,9 @@ func (s *DockerSuite) TestBuildContextCleanupFailedBuild(c *check.C) {
 
 }
 
-// compareDirectoryEntries compares two sets of FileInfo (usually taken from a directory)
+// compareDirectoryEntries compares two sets of DirEntry (usually taken from a directory)
 // and returns an error if different.
-func compareDirectoryEntries(e1 []os.FileInfo, e2 []os.FileInfo) error {
+func compareDirectoryEntries(e1 []os.DirEntry, e2 []os.DirEntry) error {
 	var (
 		e1Entries = make(map[string]struct{})
 		e2Entries = make(map[string]struct{})
@@ -1574,7 +1574,7 @@ func compareDirectoryEntries(e1 []os.FileInfo, e2 []os.FileInfo) error {
 	return nil
 }
 
-func (s *DockerSuite) TestBuildCmd(c *check.C) {
+func (s *DockerSuite) TestBuildCmd(c *testing.T) {
 	name := "testbuildcmd"
 	expected := "[/bin/echo Hello World]"
 
@@ -1587,7 +1587,7 @@ func (s *DockerSuite) TestBuildCmd(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildExpose(c *check.C) {
+func (s *DockerSuite) TestBuildExpose(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Expose not implemented on Windows
 	name := "testbuildexpose"
 	expected := "map[2375/tcp:{}]"
@@ -1601,7 +1601,7 @@ func (s *DockerSuite) TestBuildExpose(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildExposeMorePorts(c *check.C) {
+func (s *DockerSuite) TestBuildExposeMorePorts(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Expose not implemented on Windows
 	// start building docker file with a large number of ports
 	portList := make([]string, 50)
@@ -1650,7 +1650,7 @@ func (s *DockerSuite) TestBuildExposeMorePorts(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildExposeOrder(c *check.C) {
+func (s *DockerSuite) TestBuildExposeOrder(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Expose not implemented on Windows
 	buildID := func(name, exposed string) string {
 		buildImageSuccessfully(c, name, build.WithDockerfile(fmt.Sprintf(`FROM scratch
@@ -1666,7 +1666,7 @@ func (s *DockerSuite) TestBuildExposeOrder(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildExposeUpperCaseProto(c *check.C) {
+func (s *DockerSuite) TestBuildExposeUpperCaseProto(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Expose not implemented on Windows
 	name := "testbuildexposeuppercaseproto"
 	expected := "map[5678/udp:{}]"
@@ -1678,7 +1678,7 @@ func (s *DockerSuite) TestBuildExposeUpperCaseProto(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildEmptyEntrypointInheritance(c *check.C) {
+func (s *DockerSuite) TestBuildEmptyEntrypointInheritance(c *testing.T) {
 	name := "testbuildentrypointinheritance"
 	name2 := "testbuildentrypointinheritance2"
 
@@ -1701,7 +1701,7 @@ func (s *DockerSuite) TestBuildEmptyEntrypointInheritance(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildEmptyEntrypoint(c *check.C) {
+func (s *DockerSuite) TestBuildEmptyEntrypoint(c *testing.T) {
 	name := "testbuildentrypoint"
 	expected := "[]"
 
@@ -1715,7 +1715,7 @@ func (s *DockerSuite) TestBuildEmptyEntrypoint(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildEntrypoint(c *check.C) {
+func (s *DockerSuite) TestBuildEntrypoint(c *testing.T) {
 	name := "testbuildentrypoint"
 
 	expected := "[/bin/echo]"
@@ -1730,7 +1730,7 @@ func (s *DockerSuite) TestBuildEntrypoint(c *check.C) {
 }
 
 // #6445 ensure ONBUILD triggers aren't committed to grandchildren
-func (s *DockerSuite) TestBuildOnBuildLimitedInheritance(c *check.C) {
+func (s *DockerSuite) TestBuildOnBuildLimitedInheritance(c *testing.T) {
 	buildImageSuccessfully(c, "testonbuildtrigger1", build.WithDockerfile(`
 		FROM busybox
 		RUN echo "GRANDPARENT"
@@ -1748,7 +1748,7 @@ func (s *DockerSuite) TestBuildOnBuildLimitedInheritance(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildSameDockerfileWithAndWithoutCache(c *check.C) {
+func (s *DockerSuite) TestBuildSameDockerfileWithAndWithoutCache(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Expose not implemented on Windows
 	name := "testbuildwithcache"
 	dockerfile := `FROM scratch
@@ -1770,7 +1770,7 @@ func (s *DockerSuite) TestBuildSameDockerfileWithAndWithoutCache(c *check.C) {
 }
 
 // Make sure that ADD/COPY still populate the cache even if they don't use it
-func (s *DockerSuite) TestBuildConditionalCache(c *check.C) {
+func (s *DockerSuite) TestBuildConditionalCache(c *testing.T) {
 	name := "testbuildconditionalcache"
 
 	dockerfile := `
@@ -1804,7 +1804,7 @@ func (s *DockerSuite) TestBuildConditionalCache(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildAddMultipleLocalFileWithAndWithoutCache(c *check.C) {
+func (s *DockerSuite) TestBuildAddMultipleLocalFileWithAndWithoutCache(c *testing.T) {
 	name := "testbuildaddmultiplelocalfilewithcache"
 	baseName := name + "-base"
 
@@ -1836,7 +1836,7 @@ func (s *DockerSuite) TestBuildAddMultipleLocalFileWithAndWithoutCache(c *check.
 	}
 }
 
-func (s *DockerSuite) TestBuildCopyDirButNotFile(c *check.C) {
+func (s *DockerSuite) TestBuildCopyDirButNotFile(c *testing.T) {
 	name := "testbuildcopydirbutnotfile"
 	name2 := "testbuildcopydirbutnotfile2"
 
@@ -1860,7 +1860,7 @@ func (s *DockerSuite) TestBuildCopyDirButNotFile(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildAddCurrentDirWithCache(c *check.C) {
+func (s *DockerSuite) TestBuildAddCurrentDirWithCache(c *testing.T) {
 	name := "testbuildaddcurrentdirwithcache"
 	name2 := name + "2"
 	name3 := name + "3"
@@ -1907,7 +1907,7 @@ func (s *DockerSuite) TestBuildAddCurrentDirWithCache(c *check.C) {
 }
 
 // FIXME(vdemeester) this really seems to test the same thing as before (TestBuildAddMultipleLocalFileWithAndWithoutCache)
-func (s *DockerSuite) TestBuildAddCurrentDirWithoutCache(c *check.C) {
+func (s *DockerSuite) TestBuildAddCurrentDirWithoutCache(c *testing.T) {
 	name := "testbuildaddcurrentdirwithoutcache"
 	dockerfile := `
         FROM ` + minimalBaseImage() + `
@@ -1926,7 +1926,7 @@ func (s *DockerSuite) TestBuildAddCurrentDirWithoutCache(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildAddRemoteFileWithAndWithoutCache(c *check.C) {
+func (s *DockerSuite) TestBuildAddRemoteFileWithAndWithoutCache(c *testing.T) {
 	name := "testbuildaddremotefilewithcache"
 	server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{
 		"baz": "hello",
@@ -1951,7 +1951,7 @@ func (s *DockerSuite) TestBuildAddRemoteFileWithAndWithoutCache(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildAddRemoteFileMTime(c *check.C) {
+func (s *DockerSuite) TestBuildAddRemoteFileMTime(c *testing.T) {
 	name := "testbuildaddremotefilemtime"
 	name2 := name + "2"
 	name3 := name + "3"
@@ -1994,7 +1994,7 @@ func (s *DockerSuite) TestBuildAddRemoteFileMTime(c *check.C) {
 }
 
 // FIXME(vdemeester) this really seems to test the same thing as before (combined)
-func (s *DockerSuite) TestBuildAddLocalAndRemoteFilesWithAndWithoutCache(c *check.C) {
+func (s *DockerSuite) TestBuildAddLocalAndRemoteFilesWithAndWithoutCache(c *testing.T) {
 	name := "testbuildaddlocalandremotefilewithcache"
 	server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{
 		"baz": "hello",
@@ -2023,7 +2023,7 @@ func (s *DockerSuite) TestBuildAddLocalAndRemoteFilesWithAndWithoutCache(c *chec
 	}
 }
 
-func testContextTar(c *check.C, compression archive.Compression) {
+func testContextTar(c *testing.T, compression archive.Compression) {
 	ctx := fakecontext.New(c, "",
 		fakecontext.WithDockerfile(`FROM busybox
 ADD foo /foo
@@ -2042,15 +2042,15 @@ CMD ["cat", "/foo"]`),
 	cli.BuildCmd(c, name, build.WithStdinContext(context))
 }
 
-func (s *DockerSuite) TestBuildContextTarGzip(c *check.C) {
+func (s *DockerSuite) TestBuildContextTarGzip(c *testing.T) {
 	testContextTar(c, archive.Gzip)
 }
 
-func (s *DockerSuite) TestBuildContextTarNoCompression(c *check.C) {
+func (s *DockerSuite) TestBuildContextTarNoCompression(c *testing.T) {
 	testContextTar(c, archive.Uncompressed)
 }
 
-func (s *DockerSuite) TestBuildNoContext(c *check.C) {
+func (s *DockerSuite) TestBuildNoContext(c *testing.T) {
 	name := "nocontext"
 	icmd.RunCmd(icmd.Cmd{
 		Command: []string{dockerBinary, "build", "-t", name, "-"},
@@ -2065,12 +2065,12 @@ func (s *DockerSuite) TestBuildNoContext(c *check.C) {
 }
 
 // FIXME(vdemeester) migrate to docker/cli e2e
-func (s *DockerSuite) TestBuildDockerfileStdin(c *check.C) {
+func (s *DockerSuite) TestBuildDockerfileStdin(c *testing.T) {
 	name := "stdindockerfile"
-	tmpDir, err := ioutil.TempDir("", "fake-context")
-	c.Assert(err, check.IsNil)
-	err = ioutil.WriteFile(filepath.Join(tmpDir, "foo"), []byte("bar"), 0600)
-	c.Assert(err, check.IsNil)
+	tmpDir, err := os.MkdirTemp("", "fake-context")
+	assert.NilError(c, err)
+	err = os.WriteFile(filepath.Join(tmpDir, "foo"), []byte("bar"), 0600)
+	assert.NilError(c, err)
 
 	icmd.RunCmd(icmd.Cmd{
 		Command: []string{dockerBinary, "build", "-t", name, "-f", "-", tmpDir},
@@ -2081,11 +2081,11 @@ CMD ["cat", "/foo"]`),
 	}).Assert(c, icmd.Success)
 
 	res := inspectField(c, name, "Config.Cmd")
-	c.Assert(strings.TrimSpace(string(res)), checker.Equals, `[cat /foo]`)
+	assert.Equal(c, strings.TrimSpace(res), `[cat /foo]`)
 }
 
 // FIXME(vdemeester) migrate to docker/cli tests (unit or e2e)
-func (s *DockerSuite) TestBuildDockerfileStdinConflict(c *check.C) {
+func (s *DockerSuite) TestBuildDockerfileStdinConflict(c *testing.T) {
 	name := "stdindockerfiletarcontext"
 	icmd.RunCmd(icmd.Cmd{
 		Command: []string{dockerBinary, "build", "-t", name, "-f", "-", "-"},
@@ -2095,27 +2095,27 @@ func (s *DockerSuite) TestBuildDockerfileStdinConflict(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildDockerfileStdinNoExtraFiles(c *check.C) {
+func (s *DockerSuite) TestBuildDockerfileStdinNoExtraFiles(c *testing.T) {
 	s.testBuildDockerfileStdinNoExtraFiles(c, false, false)
 }
 
-func (s *DockerSuite) TestBuildDockerfileStdinDockerignore(c *check.C) {
+func (s *DockerSuite) TestBuildDockerfileStdinDockerignore(c *testing.T) {
 	s.testBuildDockerfileStdinNoExtraFiles(c, true, false)
 }
 
-func (s *DockerSuite) TestBuildDockerfileStdinDockerignoreIgnored(c *check.C) {
+func (s *DockerSuite) TestBuildDockerfileStdinDockerignoreIgnored(c *testing.T) {
 	s.testBuildDockerfileStdinNoExtraFiles(c, true, true)
 }
 
-func (s *DockerSuite) testBuildDockerfileStdinNoExtraFiles(c *check.C, hasDockerignore, ignoreDockerignore bool) {
+func (s *DockerSuite) testBuildDockerfileStdinNoExtraFiles(c *testing.T, hasDockerignore, ignoreDockerignore bool) {
 	name := "stdindockerfilenoextra"
-	tmpDir, err := ioutil.TempDir("", "fake-context")
-	c.Assert(err, check.IsNil)
+	tmpDir, err := os.MkdirTemp("", "fake-context")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmpDir)
 
 	writeFile := func(filename, content string) {
-		err = ioutil.WriteFile(filepath.Join(tmpDir, filename), []byte(content), 0600)
-		c.Assert(err, check.IsNil)
+		err = os.WriteFile(filepath.Join(tmpDir, filename), []byte(content), 0600)
+		assert.NilError(c, err)
 	}
 
 	writeFile("foo", "bar")
@@ -2141,13 +2141,13 @@ COPY . /baz`),
 
 	result = cli.DockerCmd(c, "run", "--rm", name, "ls", "-A", "/baz")
 	if hasDockerignore && !ignoreDockerignore {
-		c.Assert(result.Stdout(), checker.Equals, ".dockerignore\nfoo\n")
+		assert.Equal(c, result.Stdout(), ".dockerignore\nfoo\n")
 	} else {
-		c.Assert(result.Stdout(), checker.Equals, "foo\n")
+		assert.Equal(c, result.Stdout(), "foo\n")
 	}
 }
 
-func (s *DockerSuite) TestBuildWithVolumeOwnership(c *check.C) {
+func (s *DockerSuite) TestBuildWithVolumeOwnership(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildimg"
 
@@ -2167,7 +2167,7 @@ func (s *DockerSuite) TestBuildWithVolumeOwnership(c *check.C) {
 
 // testing #1405 - config.Cmd does not get cleaned up if
 // utilizing cache
-func (s *DockerSuite) TestBuildEntrypointRunCleanup(c *check.C) {
+func (s *DockerSuite) TestBuildEntrypointRunCleanup(c *testing.T) {
 	name := "testbuildcmdcleanup"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
         RUN echo "hello"`))
@@ -2186,24 +2186,19 @@ func (s *DockerSuite) TestBuildEntrypointRunCleanup(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildAddFileNotFound(c *check.C) {
+func (s *DockerSuite) TestBuildAddFileNotFound(c *testing.T) {
 	name := "testbuildaddnotfound"
-	expected := "foo: no such file or directory"
-
-	if testEnv.OSType == "windows" {
-		expected = "foo: The system cannot find the file specified"
-	}
 
 	buildImage(name, build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM `+minimalBaseImage()+`
         ADD foo /usr/local/bar`),
 		build.WithFile("bar", "hello"))).Assert(c, icmd.Expected{
 		ExitCode: 1,
-		Err:      expected,
+		Err:      "stat foo: file does not exist",
 	})
 }
 
-func (s *DockerSuite) TestBuildInheritance(c *check.C) {
+func (s *DockerSuite) TestBuildInheritance(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildinheritance"
 
@@ -2224,7 +2219,7 @@ func (s *DockerSuite) TestBuildInheritance(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildFails(c *check.C) {
+func (s *DockerSuite) TestBuildFails(c *testing.T) {
 	name := "testbuildfails"
 	buildImage(name, build.WithDockerfile(`FROM busybox
 		RUN sh -c "exit 23"`)).Assert(c, icmd.Expected{
@@ -2233,7 +2228,7 @@ func (s *DockerSuite) TestBuildFails(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildOnBuild(c *check.C) {
+func (s *DockerSuite) TestBuildOnBuild(c *testing.T) {
 	name := "testbuildonbuild"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
 		ONBUILD RUN touch foobar`))
@@ -2242,7 +2237,7 @@ func (s *DockerSuite) TestBuildOnBuild(c *check.C) {
 }
 
 // gh #2446
-func (s *DockerSuite) TestBuildAddToSymlinkDest(c *check.C) {
+func (s *DockerSuite) TestBuildAddToSymlinkDest(c *testing.T) {
 	makeLink := `ln -s /foo /bar`
 	if testEnv.OSType == "windows" {
 		makeLink = `mklink /D C:\bar C:\foo`
@@ -2260,7 +2255,7 @@ func (s *DockerSuite) TestBuildAddToSymlinkDest(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildEscapeWhitespace(c *check.C) {
+func (s *DockerSuite) TestBuildEscapeWhitespace(c *testing.T) {
 	name := "testbuildescapewhitespace"
 
 	buildImageSuccessfully(c, name, build.WithDockerfile(`
@@ -2278,7 +2273,7 @@ docker.com>"
 
 }
 
-func (s *DockerSuite) TestBuildVerifyIntString(c *check.C) {
+func (s *DockerSuite) TestBuildVerifyIntString(c *testing.T) {
 	// Verify that strings that look like ints are still passed as strings
 	name := "testbuildstringing"
 
@@ -2293,7 +2288,7 @@ func (s *DockerSuite) TestBuildVerifyIntString(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildDockerignore(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignore(c *testing.T) {
 	name := "testbuilddockerignore"
 	buildImageSuccessfully(c, name, build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `
@@ -2331,7 +2326,7 @@ dir`),
 	))
 }
 
-func (s *DockerSuite) TestBuildDockerignoreCleanPaths(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoreCleanPaths(c *testing.T) {
 	name := "testbuilddockerignorecleanpaths"
 	buildImageSuccessfully(c, name, build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `
@@ -2345,7 +2340,7 @@ func (s *DockerSuite) TestBuildDockerignoreCleanPaths(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildDockerignoreExceptions(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoreExceptions(c *testing.T) {
 	name := "testbuilddockerignoreexceptions"
 	buildImageSuccessfully(c, name, build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `
@@ -2390,7 +2385,7 @@ dir
 	))
 }
 
-func (s *DockerSuite) TestBuildDockerignoringDockerfile(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoringDockerfile(c *testing.T) {
 	name := "testbuilddockerignoredockerfile"
 	dockerfile := `
 		FROM busybox
@@ -2408,7 +2403,7 @@ func (s *DockerSuite) TestBuildDockerignoringDockerfile(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildDockerignoringRenamedDockerfile(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoringRenamedDockerfile(c *testing.T) {
 	name := "testbuilddockerignoredockerfile"
 	dockerfile := `
 		FROM busybox
@@ -2429,7 +2424,7 @@ func (s *DockerSuite) TestBuildDockerignoringRenamedDockerfile(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildDockerignoringDockerignore(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoringDockerignore(c *testing.T) {
 	name := "testbuilddockerignoredockerignore"
 	dockerfile := `
 		FROM busybox
@@ -2442,7 +2437,7 @@ func (s *DockerSuite) TestBuildDockerignoringDockerignore(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildDockerignoreTouchDockerfile(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoreTouchDockerfile(c *testing.T) {
 	name := "testbuilddockerignoretouchdockerfile"
 	dockerfile := `
         FROM busybox
@@ -2484,7 +2479,7 @@ func (s *DockerSuite) TestBuildDockerignoreTouchDockerfile(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildDockerignoringWholeDir(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoringWholeDir(c *testing.T) {
 	name := "testbuilddockerignorewholedir"
 
 	dockerfile := `
@@ -2501,7 +2496,7 @@ func (s *DockerSuite) TestBuildDockerignoringWholeDir(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildDockerignoringOnlyDotfiles(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoringOnlyDotfiles(c *testing.T) {
 	name := "testbuilddockerignorewholedir"
 
 	dockerfile := `
@@ -2518,7 +2513,7 @@ func (s *DockerSuite) TestBuildDockerignoringOnlyDotfiles(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildDockerignoringBadExclusion(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoringBadExclusion(c *testing.T) {
 	name := "testbuilddockerignorebadexclusion"
 	buildImage(name, build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `
@@ -2535,7 +2530,7 @@ func (s *DockerSuite) TestBuildDockerignoringBadExclusion(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildDockerignoringWildTopDir(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoringWildTopDir(c *testing.T) {
 	dockerfile := `
 		FROM busybox
 		COPY . /
@@ -2557,7 +2552,7 @@ func (s *DockerSuite) TestBuildDockerignoringWildTopDir(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildDockerignoringWildDirs(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoringWildDirs(c *testing.T) {
 	dockerfile := `
         FROM busybox
 		COPY . /
@@ -2620,7 +2615,7 @@ dir1/dir3/**
 	))
 }
 
-func (s *DockerSuite) TestBuildLineBreak(c *check.C) {
+func (s *DockerSuite) TestBuildLineBreak(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildlinebreak"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM  busybox
@@ -2631,7 +2626,7 @@ RUN    sh -c "[ "$(cat /tmp/passwd)" = "root:testpass" ]"
 RUN    sh -c "[ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]"`))
 }
 
-func (s *DockerSuite) TestBuildEOLInLine(c *check.C) {
+func (s *DockerSuite) TestBuildEOLInLine(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildeolinline"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM   busybox
@@ -2642,7 +2637,7 @@ RUN    sh -c "[ "$(cat /tmp/passwd)" = "root:testpass" ]"
 RUN    sh -c "[ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ]"`))
 }
 
-func (s *DockerSuite) TestBuildCommentsShebangs(c *check.C) {
+func (s *DockerSuite) TestBuildCommentsShebangs(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildcomments"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
@@ -2656,7 +2651,7 @@ RUN [ "$(cat /hello.sh)" = $'#!/bin/sh\necho hello world' ]
 RUN [ "$(/hello.sh)" = "hello world" ]`))
 }
 
-func (s *DockerSuite) TestBuildUsersAndGroups(c *check.C) {
+func (s *DockerSuite) TestBuildUsersAndGroups(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildusers"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
@@ -2712,7 +2707,7 @@ RUN [ "$(id -u):$(id -g)/$(id -un):$(id -gn)/$(id -G):$(id -Gn)" = '1042:1043/10
 }
 
 // FIXME(vdemeester) rename this test (and probably "merge" it with the one below TestBuildEnvUsage2)
-func (s *DockerSuite) TestBuildEnvUsage(c *check.C) {
+func (s *DockerSuite) TestBuildEnvUsage(c *testing.T) {
 	// /docker/world/hello is not owned by the correct user
 	testRequires(c, NotUserNamespace)
 	testRequires(c, DaemonIsLinux)
@@ -2743,7 +2738,7 @@ RUN    [ "$ghi" = "def" ]
 }
 
 // FIXME(vdemeester) rename this test (and probably "merge" it with the one above TestBuildEnvUsage)
-func (s *DockerSuite) TestBuildEnvUsage2(c *check.C) {
+func (s *DockerSuite) TestBuildEnvUsage2(c *testing.T) {
 	// /docker/world/hello is not owned by the correct user
 	testRequires(c, NotUserNamespace)
 	testRequires(c, DaemonIsLinux)
@@ -2812,7 +2807,7 @@ RUN    [ "$eee1,$eee2,$eee3,$eee4" = 'foo,foo,foo,foo' ]
 	))
 }
 
-func (s *DockerSuite) TestBuildAddScript(c *check.C) {
+func (s *DockerSuite) TestBuildAddScript(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildaddscript"
 	dockerfile := `
@@ -2828,7 +2823,7 @@ RUN [ "$(cat /testfile)" = 'test!' ]`
 	))
 }
 
-func (s *DockerSuite) TestBuildAddTar(c *check.C) {
+func (s *DockerSuite) TestBuildAddTar(c *testing.T) {
 	// /test/foo is not owned by the correct user
 	testRequires(c, NotUserNamespace)
 	name := "testbuildaddtar"
@@ -2849,8 +2844,8 @@ ADD test.tar /existing-directory
 RUN cat /existing-directory/test/foo | grep Hi
 ADD test.tar /existing-directory-trailing-slash/
 RUN cat /existing-directory-trailing-slash/test/foo | grep Hi`
-		tmpDir, err := ioutil.TempDir("", "fake-context")
-		c.Assert(err, check.IsNil)
+		tmpDir, err := os.MkdirTemp("", "fake-context")
+		assert.NilError(c, err)
 		testTar, err := os.Create(filepath.Join(tmpDir, "test.tar"))
 		if err != nil {
 			c.Fatalf("failed to create test.tar archive: %v", err)
@@ -2872,7 +2867,7 @@ RUN cat /existing-directory-trailing-slash/test/foo | grep Hi`
 			c.Fatalf("failed to close tar archive: %v", err)
 		}
 
-		if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
+		if err := os.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
 			c.Fatalf("failed to open destination dockerfile: %v", err)
 		}
 		return fakecontext.New(c, tmpDir)
@@ -2882,15 +2877,15 @@ RUN cat /existing-directory-trailing-slash/test/foo | grep Hi`
 	buildImageSuccessfully(c, name, build.WithExternalBuildContext(ctx))
 }
 
-func (s *DockerSuite) TestBuildAddBrokenTar(c *check.C) {
+func (s *DockerSuite) TestBuildAddBrokenTar(c *testing.T) {
 	name := "testbuildaddbrokentar"
 
 	ctx := func() *fakecontext.Fake {
 		dockerfile := `
 FROM busybox
 ADD test.tar /`
-		tmpDir, err := ioutil.TempDir("", "fake-context")
-		c.Assert(err, check.IsNil)
+		tmpDir, err := os.MkdirTemp("", "fake-context")
+		assert.NilError(c, err)
 		testTar, err := os.Create(filepath.Join(tmpDir, "test.tar"))
 		if err != nil {
 			c.Fatalf("failed to create test.tar archive: %v", err)
@@ -2921,7 +2916,7 @@ ADD test.tar /`
 			c.Fatalf("failed to truncate tar archive: %v", err)
 		}
 
-		if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
+		if err := os.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
 			c.Fatalf("failed to open destination dockerfile: %v", err)
 		}
 		return fakecontext.New(c, tmpDir)
@@ -2933,7 +2928,7 @@ ADD test.tar /`
 	})
 }
 
-func (s *DockerSuite) TestBuildAddNonTar(c *check.C) {
+func (s *DockerSuite) TestBuildAddNonTar(c *testing.T) {
 	name := "testbuildaddnontar"
 
 	// Should not try to extract test.tar
@@ -2946,7 +2941,7 @@ func (s *DockerSuite) TestBuildAddNonTar(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildAddTarXz(c *check.C) {
+func (s *DockerSuite) TestBuildAddTarXz(c *testing.T) {
 	// /test/foo is not owned by the correct user
 	testRequires(c, NotUserNamespace)
 	testRequires(c, DaemonIsLinux)
@@ -2957,8 +2952,8 @@ func (s *DockerSuite) TestBuildAddTarXz(c *check.C) {
 			FROM busybox
 			ADD test.tar.xz /
 			RUN cat /test/foo | grep Hi`
-		tmpDir, err := ioutil.TempDir("", "fake-context")
-		c.Assert(err, check.IsNil)
+		tmpDir, err := os.MkdirTemp("", "fake-context")
+		assert.NilError(c, err)
 		testTar, err := os.Create(filepath.Join(tmpDir, "test.tar"))
 		if err != nil {
 			c.Fatalf("failed to create test.tar archive: %v", err)
@@ -2984,7 +2979,7 @@ func (s *DockerSuite) TestBuildAddTarXz(c *check.C) {
 			Command: []string{"xz", "-k", "test.tar"},
 			Dir:     tmpDir,
 		}).Assert(c, icmd.Success)
-		if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
+		if err := os.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
 			c.Fatalf("failed to open destination dockerfile: %v", err)
 		}
 		return fakecontext.New(c, tmpDir)
@@ -2995,7 +2990,7 @@ func (s *DockerSuite) TestBuildAddTarXz(c *check.C) {
 	buildImageSuccessfully(c, name, build.WithExternalBuildContext(ctx))
 }
 
-func (s *DockerSuite) TestBuildAddTarXzGz(c *check.C) {
+func (s *DockerSuite) TestBuildAddTarXzGz(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildaddtarxzgz"
 
@@ -3004,8 +2999,8 @@ func (s *DockerSuite) TestBuildAddTarXzGz(c *check.C) {
 			FROM busybox
 			ADD test.tar.xz.gz /
 			RUN ls /test.tar.xz.gz`
-		tmpDir, err := ioutil.TempDir("", "fake-context")
-		c.Assert(err, check.IsNil)
+		tmpDir, err := os.MkdirTemp("", "fake-context")
+		assert.NilError(c, err)
 		testTar, err := os.Create(filepath.Join(tmpDir, "test.tar"))
 		if err != nil {
 			c.Fatalf("failed to create test.tar archive: %v", err)
@@ -3036,7 +3031,7 @@ func (s *DockerSuite) TestBuildAddTarXzGz(c *check.C) {
 			Command: []string{"gzip", "test.tar.xz"},
 			Dir:     tmpDir,
 		})
-		if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
+		if err := os.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
 			c.Fatalf("failed to open destination dockerfile: %v", err)
 		}
 		return fakecontext.New(c, tmpDir)
@@ -3048,7 +3043,7 @@ func (s *DockerSuite) TestBuildAddTarXzGz(c *check.C) {
 }
 
 // FIXME(vdemeester) most of the from git tests could be moved to `docker/cli` e2e tests
-func (s *DockerSuite) TestBuildFromGit(c *check.C) {
+func (s *DockerSuite) TestBuildFromGit(c *testing.T) {
 	name := "testbuildfromgit"
 	git := fakegit.New(c, "repo", map[string]string{
 		"Dockerfile": `FROM busybox
@@ -3067,7 +3062,7 @@ func (s *DockerSuite) TestBuildFromGit(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildFromGitWithContext(c *check.C) {
+func (s *DockerSuite) TestBuildFromGitWithContext(c *testing.T) {
 	name := "testbuildfromgit"
 	git := fakegit.New(c, "repo", map[string]string{
 		"docker/Dockerfile": `FROM busybox
@@ -3086,7 +3081,7 @@ func (s *DockerSuite) TestBuildFromGitWithContext(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildFromGitWithF(c *check.C) {
+func (s *DockerSuite) TestBuildFromGitWithF(c *testing.T) {
 	name := "testbuildfromgitwithf"
 	git := fakegit.New(c, "repo", map[string]string{
 		"myApp/myDockerfile": `FROM busybox
@@ -3099,7 +3094,7 @@ func (s *DockerSuite) TestBuildFromGitWithF(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildFromRemoteTarball(c *check.C) {
+func (s *DockerSuite) TestBuildFromRemoteTarball(c *testing.T) {
 	name := "testbuildfromremotetarball"
 
 	buffer := new(bytes.Buffer)
@@ -3134,7 +3129,7 @@ func (s *DockerSuite) TestBuildFromRemoteTarball(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildCleanupCmdOnEntrypoint(c *check.C) {
+func (s *DockerSuite) TestBuildCleanupCmdOnEntrypoint(c *testing.T) {
 	name := "testbuildcmdcleanuponentrypoint"
 
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
@@ -3153,7 +3148,7 @@ func (s *DockerSuite) TestBuildCleanupCmdOnEntrypoint(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildClearCmd(c *check.C) {
+func (s *DockerSuite) TestBuildClearCmd(c *testing.T) {
 	name := "testbuildclearcmd"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
    ENTRYPOINT ["/bin/bash"]
@@ -3165,7 +3160,7 @@ func (s *DockerSuite) TestBuildClearCmd(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildEmptyCmd(c *check.C) {
+func (s *DockerSuite) TestBuildEmptyCmd(c *testing.T) {
 	// Skip on Windows. Base image on Windows has a CMD set in the image.
 	testRequires(c, DaemonIsLinux)
 
@@ -3178,7 +3173,7 @@ func (s *DockerSuite) TestBuildEmptyCmd(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildOnBuildOutput(c *check.C) {
+func (s *DockerSuite) TestBuildOnBuildOutput(c *testing.T) {
 	name := "testbuildonbuildparent"
 	buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nONBUILD RUN echo foo\n"))
 
@@ -3188,7 +3183,7 @@ func (s *DockerSuite) TestBuildOnBuildOutput(c *check.C) {
 }
 
 // FIXME(vdemeester) should be a unit test
-func (s *DockerSuite) TestBuildInvalidTag(c *check.C) {
+func (s *DockerSuite) TestBuildInvalidTag(c *testing.T) {
 	name := "abcd:" + testutil.GenerateRandomAlphaOnlyString(200)
 	buildImage(name, build.WithDockerfile("FROM "+minimalBaseImage()+"\nMAINTAINER quux\n")).Assert(c, icmd.Expected{
 		ExitCode: 125,
@@ -3196,14 +3191,14 @@ func (s *DockerSuite) TestBuildInvalidTag(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildCmdShDashC(c *check.C) {
+func (s *DockerSuite) TestBuildCmdShDashC(c *testing.T) {
 	name := "testbuildcmdshc"
 	buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nCMD echo cmd\n"))
 
 	res := inspectFieldJSON(c, name, "Config.Cmd")
 	expected := `["/bin/sh","-c","echo cmd"]`
 	if testEnv.OSType == "windows" {
-		expected = `["cmd","/S","/C","echo cmd"]`
+		expected = `["cmd /S /C echo cmd"]`
 	}
 	if res != expected {
 		c.Fatalf("Expected value %s not in Config.Cmd: %s", expected, res)
@@ -3211,7 +3206,7 @@ func (s *DockerSuite) TestBuildCmdShDashC(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildCmdSpaces(c *check.C) {
+func (s *DockerSuite) TestBuildCmdSpaces(c *testing.T) {
 	// Test to make sure that when we strcat arrays we take into account
 	// the arg separator to make sure ["echo","hi"] and ["echo hi"] don't
 	// look the same
@@ -3237,7 +3232,7 @@ func (s *DockerSuite) TestBuildCmdSpaces(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildCmdJSONNoShDashC(c *check.C) {
+func (s *DockerSuite) TestBuildCmdJSONNoShDashC(c *testing.T) {
 	name := "testbuildcmdjson"
 	buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nCMD [\"echo\", \"cmd\"]"))
 
@@ -3248,7 +3243,7 @@ func (s *DockerSuite) TestBuildCmdJSONNoShDashC(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildEntrypointCanBeOverriddenByChild(c *check.C) {
+func (s *DockerSuite) TestBuildEntrypointCanBeOverriddenByChild(c *testing.T) {
 	buildImageSuccessfully(c, "parent", build.WithDockerfile(`
     FROM busybox
     ENTRYPOINT exit 130
@@ -3268,7 +3263,7 @@ func (s *DockerSuite) TestBuildEntrypointCanBeOverriddenByChild(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildEntrypointCanBeOverriddenByChildInspect(c *check.C) {
+func (s *DockerSuite) TestBuildEntrypointCanBeOverriddenByChildInspect(c *testing.T) {
 	var (
 		name     = "testbuildepinherit"
 		name2    = "testbuildepinherit2"
@@ -3276,7 +3271,7 @@ func (s *DockerSuite) TestBuildEntrypointCanBeOverriddenByChildInspect(c *check.
 	)
 
 	if testEnv.OSType == "windows" {
-		expected = `["cmd","/S","/C","echo quux"]`
+		expected = `["cmd /S /C echo quux"]`
 	}
 
 	buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nENTRYPOINT /foo/bar"))
@@ -3292,14 +3287,14 @@ func (s *DockerSuite) TestBuildEntrypointCanBeOverriddenByChildInspect(c *check.
 	})
 }
 
-func (s *DockerSuite) TestBuildRunShEntrypoint(c *check.C) {
+func (s *DockerSuite) TestBuildRunShEntrypoint(c *testing.T) {
 	name := "testbuildentrypoint"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
                                 ENTRYPOINT echo`))
 	dockerCmd(c, "run", "--rm", name)
 }
 
-func (s *DockerSuite) TestBuildExoticShellInterpolation(c *check.C) {
+func (s *DockerSuite) TestBuildExoticShellInterpolation(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildexoticshellinterpolation"
 
@@ -3324,7 +3319,7 @@ func (s *DockerSuite) TestBuildExoticShellInterpolation(c *check.C) {
 	`))
 }
 
-func (s *DockerSuite) TestBuildVerifySingleQuoteFails(c *check.C) {
+func (s *DockerSuite) TestBuildVerifySingleQuoteFails(c *testing.T) {
 	// This testcase is supposed to generate an error because the
 	// JSON array we're passing in on the CMD uses single quotes instead
 	// of double quotes (per the JSON spec). This means we interpret it
@@ -3332,9 +3327,6 @@ func (s *DockerSuite) TestBuildVerifySingleQuoteFails(c *check.C) {
 	// it should barf on it.
 	name := "testbuildsinglequotefails"
 	expectedExitCode := 2
-	if testEnv.OSType == "windows" {
-		expectedExitCode = 127
-	}
 
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
 		CMD [ '/bin/sh', '-c', 'echo hi' ]`))
@@ -3344,7 +3336,7 @@ func (s *DockerSuite) TestBuildVerifySingleQuoteFails(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildVerboseOut(c *check.C) {
+func (s *DockerSuite) TestBuildVerboseOut(c *testing.T) {
 	name := "testbuildverboseout"
 	expected := "\n123\n"
 
@@ -3358,22 +3350,22 @@ RUN echo 123`)).Assert(c, icmd.Expected{
 	})
 }
 
-func (s *DockerSuite) TestBuildWithTabs(c *check.C) {
+func (s *DockerSuite) TestBuildWithTabs(c *testing.T) {
 	name := "testbuildwithtabs"
 	buildImageSuccessfully(c, name, build.WithDockerfile("FROM busybox\nRUN echo\tone\t\ttwo"))
 	res := inspectFieldJSON(c, name, "ContainerConfig.Cmd")
 	expected1 := `["/bin/sh","-c","echo\tone\t\ttwo"]`
 	expected2 := `["/bin/sh","-c","echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates
 	if testEnv.OSType == "windows" {
-		expected1 = `["cmd","/S","/C","echo\tone\t\ttwo"]`
-		expected2 = `["cmd","/S","/C","echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates
+		expected1 = `["cmd /S /C echo\tone\t\ttwo"]`
+		expected2 = `["cmd /S /C echo\u0009one\u0009\u0009two"]` // syntactically equivalent, and what Go 1.3 generates
 	}
 	if res != expected1 && res != expected2 {
 		c.Fatalf("Missing tabs.\nGot: %s\nExp: %s or %s", res, expected1, expected2)
 	}
 }
 
-func (s *DockerSuite) TestBuildLabels(c *check.C) {
+func (s *DockerSuite) TestBuildLabels(c *testing.T) {
 	name := "testbuildlabel"
 	expected := `{"License":"GPL","Vendor":"Acme"}`
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
@@ -3385,7 +3377,7 @@ func (s *DockerSuite) TestBuildLabels(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildLabelsCache(c *check.C) {
+func (s *DockerSuite) TestBuildLabelsCache(c *testing.T) {
 	name := "testbuildlabelcache"
 
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
@@ -3426,10 +3418,10 @@ func (s *DockerSuite) TestBuildLabelsCache(c *check.C) {
 }
 
 // FIXME(vdemeester) port to docker/cli e2e tests (api tests should test suppressOutput option though)
-func (s *DockerSuite) TestBuildNotVerboseSuccess(c *check.C) {
+func (s *DockerSuite) TestBuildNotVerboseSuccess(c *testing.T) {
 	// This test makes sure that -q works correctly when build is successful:
 	// stdout has only the image ID (long image ID) and stderr is empty.
-	outRegexp := regexp.MustCompile("^(sha256:|)[a-z0-9]{64}\\n$")
+	outRegexp := regexp.MustCompile(`^(sha256:|)[a-z0-9]{64}\n$`)
 	buildFlags := cli.WithFlags("-q")
 
 	tt := []struct {
@@ -3477,7 +3469,7 @@ func (s *DockerSuite) TestBuildNotVerboseSuccess(c *check.C) {
 }
 
 // FIXME(vdemeester) migrate to docker/cli tests
-func (s *DockerSuite) TestBuildNotVerboseFailureWithNonExistImage(c *check.C) {
+func (s *DockerSuite) TestBuildNotVerboseFailureWithNonExistImage(c *testing.T) {
 	// This test makes sure that -q works correctly when build fails by
 	// comparing between the stderr output in quiet mode and in stdout
 	// and stderr output in verbose mode
@@ -3498,7 +3490,7 @@ func (s *DockerSuite) TestBuildNotVerboseFailureWithNonExistImage(c *check.C) {
 }
 
 // FIXME(vdemeester) migrate to docker/cli tests
-func (s *DockerSuite) TestBuildNotVerboseFailure(c *check.C) {
+func (s *DockerSuite) TestBuildNotVerboseFailure(c *testing.T) {
 	// This test makes sure that -q works correctly when build fails by
 	// comparing between the stderr output in quiet mode and in stdout
 	// and stderr output in verbose mode
@@ -3526,7 +3518,7 @@ func (s *DockerSuite) TestBuildNotVerboseFailure(c *check.C) {
 }
 
 // FIXME(vdemeester) migrate to docker/cli tests
-func (s *DockerSuite) TestBuildNotVerboseFailureRemote(c *check.C) {
+func (s *DockerSuite) TestBuildNotVerboseFailureRemote(c *testing.T) {
 	// This test ensures that when given a wrong URL, stderr in quiet mode and
 	// stderr in verbose mode are identical.
 	// TODO(vdemeester) with cobra, stdout has a carriage return too much so this test should not check stdout
@@ -3556,7 +3548,7 @@ func (s *DockerSuite) TestBuildNotVerboseFailureRemote(c *check.C) {
 }
 
 // FIXME(vdemeester) migrate to docker/cli tests
-func (s *DockerSuite) TestBuildStderr(c *check.C) {
+func (s *DockerSuite) TestBuildStderr(c *testing.T) {
 	// This test just makes sure that no non-error output goes
 	// to stderr
 	name := "testbuildstderr"
@@ -3574,7 +3566,7 @@ func (s *DockerSuite) TestBuildStderr(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildChownSingleFile(c *check.C) {
+func (s *DockerSuite) TestBuildChownSingleFile(c *testing.T) {
 	testRequires(c, UnixCli, DaemonIsLinux) // test uses chown: not available on windows
 
 	name := "testbuildchownsinglefile"
@@ -3598,21 +3590,21 @@ RUN [ $(ls -l /test | awk '{print $3":"$4}') = 'root:root' ]
 	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
 }
 
-func (s *DockerSuite) TestBuildSymlinkBreakout(c *check.C) {
+func (s *DockerSuite) TestBuildSymlinkBreakout(c *testing.T) {
 	name := "testbuildsymlinkbreakout"
-	tmpdir, err := ioutil.TempDir("", name)
-	c.Assert(err, check.IsNil)
+	tmpdir, err := os.MkdirTemp("", name)
+	assert.NilError(c, err)
 
 	// See https://github.com/moby/moby/pull/37770 for reason for next line.
 	tmpdir, err = system.GetLongPathName(tmpdir)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	defer os.RemoveAll(tmpdir)
 	ctx := filepath.Join(tmpdir, "context")
 	if err := os.MkdirAll(ctx, 0755); err != nil {
 		c.Fatal(err)
 	}
-	if err := ioutil.WriteFile(filepath.Join(ctx, "Dockerfile"), []byte(`
+	if err := os.WriteFile(filepath.Join(ctx, "Dockerfile"), []byte(`
 	from busybox
 	add symlink.tar /
 	add inject /symlink/
@@ -3620,7 +3612,7 @@ func (s *DockerSuite) TestBuildSymlinkBreakout(c *check.C) {
 		c.Fatal(err)
 	}
 	inject := filepath.Join(ctx, "inject")
-	if err := ioutil.WriteFile(inject, nil, 0644); err != nil {
+	if err := os.WriteFile(inject, nil, 0644); err != nil {
 		c.Fatal(err)
 	}
 	f, err := os.Create(filepath.Join(ctx, "symlink.tar"))
@@ -3653,7 +3645,7 @@ func (s *DockerSuite) TestBuildSymlinkBreakout(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildXZHost(c *check.C) {
+func (s *DockerSuite) TestBuildXZHost(c *testing.T) {
 	// /usr/local/sbin/xz gets permission denied for the user
 	testRequires(c, NotUserNamespace)
 	testRequires(c, DaemonIsLinux)
@@ -3671,7 +3663,7 @@ RUN [ ! -e /injected ]`),
 	))
 }
 
-func (s *DockerSuite) TestBuildVolumesRetainContents(c *check.C) {
+func (s *DockerSuite) TestBuildVolumesRetainContents(c *testing.T) {
 	// /foo/file gets permission denied for the user
 	testRequires(c, NotUserNamespace)
 	testRequires(c, DaemonIsLinux) // TODO Windows: Issue #20127
@@ -3701,7 +3693,7 @@ CMD cat /foo/file`),
 
 }
 
-func (s *DockerSuite) TestBuildFromMixedcaseDockerfile(c *check.C) {
+func (s *DockerSuite) TestBuildFromMixedcaseDockerfile(c *testing.T) {
 	testRequires(c, UnixCli) // Dockerfile overwrites dockerfile on windows
 	testRequires(c, DaemonIsLinux)
 
@@ -3725,7 +3717,7 @@ func (s *DockerSuite) TestBuildFromMixedcaseDockerfile(c *check.C) {
 }
 
 // FIXME(vdemeester) should migrate to docker/cli tests
-func (s *DockerSuite) TestBuildFromURLWithF(c *check.C) {
+func (s *DockerSuite) TestBuildFromURLWithF(c *testing.T) {
 	server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{"baz": `FROM busybox
 RUN echo from baz
 COPY * /tmp/
@@ -3752,7 +3744,7 @@ RUN find /tmp/`}))
 }
 
 // FIXME(vdemeester) should migrate to docker/cli tests
-func (s *DockerSuite) TestBuildFromStdinWithF(c *check.C) {
+func (s *DockerSuite) TestBuildFromStdinWithF(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // TODO Windows: This test is flaky; no idea why
 	ctx := fakecontext.New(c, "", fakecontext.WithDockerfile(`FROM busybox
 RUN echo "from Dockerfile"`))
@@ -3777,7 +3769,7 @@ RUN sh -c "find /tmp/" # sh -c is needed on Windows to use the correct find`)
 
 }
 
-func (s *DockerSuite) TestBuildFromOfficialNames(c *check.C) {
+func (s *DockerSuite) TestBuildFromOfficialNames(c *testing.T) {
 	name := "testbuildfromofficial"
 	fromNames := []string{
 		"busybox",
@@ -3795,7 +3787,7 @@ func (s *DockerSuite) TestBuildFromOfficialNames(c *check.C) {
 }
 
 // FIXME(vdemeester) should be a unit test
-func (s *DockerSuite) TestBuildSpaces(c *check.C) {
+func (s *DockerSuite) TestBuildSpaces(c *testing.T) {
 	// Test to make sure that leading/trailing spaces on a command
 	// doesn't change the error msg we get
 	name := "testspaces"
@@ -3858,7 +3850,7 @@ func (s *DockerSuite) TestBuildSpaces(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildSpacesWithQuotes(c *check.C) {
+func (s *DockerSuite) TestBuildSpacesWithQuotes(c *testing.T) {
 	// Test to make sure that spaces in quotes aren't lost
 	name := "testspacesquotes"
 
@@ -3878,7 +3870,7 @@ RUN echo "  \
 }
 
 // #4393
-func (s *DockerSuite) TestBuildVolumeFileExistsinContainer(c *check.C) {
+func (s *DockerSuite) TestBuildVolumeFileExistsinContainer(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // TODO Windows: This should error out
 	buildImage("docker-test-errcreatevolumewithfile", build.WithDockerfile(`
 	FROM busybox
@@ -3891,7 +3883,7 @@ func (s *DockerSuite) TestBuildVolumeFileExistsinContainer(c *check.C) {
 }
 
 // FIXME(vdemeester) should be a unit test
-func (s *DockerSuite) TestBuildMissingArgs(c *check.C) {
+func (s *DockerSuite) TestBuildMissingArgs(c *testing.T) {
 	// Test to make sure that all Dockerfile commands (except the ones listed
 	// in skipCmds) will generate an error if no args are provided.
 	// Note: INSERT is deprecated so we exclude it because of that.
@@ -3936,7 +3928,7 @@ func (s *DockerSuite) TestBuildMissingArgs(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildEmptyScratch(c *check.C) {
+func (s *DockerSuite) TestBuildEmptyScratch(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	buildImage("sc", build.WithDockerfile("FROM scratch")).Assert(c, icmd.Expected{
 		ExitCode: 1,
@@ -3944,14 +3936,14 @@ func (s *DockerSuite) TestBuildEmptyScratch(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildDotDotFile(c *check.C) {
+func (s *DockerSuite) TestBuildDotDotFile(c *testing.T) {
 	buildImageSuccessfully(c, "sc", build.WithBuildContext(c,
 		build.WithFile("Dockerfile", "FROM busybox\n"),
 		build.WithFile("..gitme", ""),
 	))
 }
 
-func (s *DockerSuite) TestBuildRUNoneJSON(c *check.C) {
+func (s *DockerSuite) TestBuildRUNoneJSON(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // No hello-world Windows image
 	name := "testbuildrunonejson"
 
@@ -3961,7 +3953,7 @@ RUN [ "/hello" ]`)).Assert(c, icmd.Expected{
 	})
 }
 
-func (s *DockerSuite) TestBuildEmptyStringVolume(c *check.C) {
+func (s *DockerSuite) TestBuildEmptyStringVolume(c *testing.T) {
 	name := "testbuildemptystringvolume"
 
 	buildImage(name, build.WithDockerfile(`
@@ -3973,11 +3965,11 @@ func (s *DockerSuite) TestBuildEmptyStringVolume(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildContainerWithCgroupParent(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerSuite) TestBuildContainerWithCgroupParent(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	cgroupParent := "test"
-	data, err := ioutil.ReadFile("/proc/self/cgroup")
+	data, err := os.ReadFile("/proc/self/cgroup")
 	if err != nil {
 		c.Fatalf("failed to read '/proc/self/cgroup - %v", err)
 	}
@@ -3994,14 +3986,14 @@ RUN cat /proc/self/cgroup
 `))
 	result.Assert(c, icmd.Success)
 	m, err := regexp.MatchString(fmt.Sprintf("memory:.*/%s/.*", cgroupParent), result.Combined())
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	if !m {
 		c.Fatalf("There is no expected memory cgroup with parent /%s/: %s", cgroupParent, result.Combined())
 	}
 }
 
 // FIXME(vdemeester) could be a unit test
-func (s *DockerSuite) TestBuildNoDupOutput(c *check.C) {
+func (s *DockerSuite) TestBuildNoDupOutput(c *testing.T) {
 	// Check to make sure our build output prints the Dockerfile cmd
 	// property - there was a bug that caused it to be duplicated on the
 	// Step X  line
@@ -4018,7 +4010,7 @@ func (s *DockerSuite) TestBuildNoDupOutput(c *check.C) {
 
 // GH15826
 // FIXME(vdemeester) could be a unit test
-func (s *DockerSuite) TestBuildStartsFromOne(c *check.C) {
+func (s *DockerSuite) TestBuildStartsFromOne(c *testing.T) {
 	// Explicit check to ensure that build starts from step 1 rather than 0
 	name := "testbuildstartsfromone"
 	result := buildImage(name, build.WithDockerfile(`FROM busybox`))
@@ -4029,7 +4021,7 @@ func (s *DockerSuite) TestBuildStartsFromOne(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildRUNErrMsg(c *check.C) {
+func (s *DockerSuite) TestBuildRUNErrMsg(c *testing.T) {
 	// Test to make sure the bad command is quoted with just "s and
 	// not as a Go []string
 	name := "testbuildbadrunerrmsg"
@@ -4051,7 +4043,7 @@ func (s *DockerSuite) TestBuildRUNErrMsg(c *check.C) {
 }
 
 // Issue #15634: COPY fails when path starts with "null"
-func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) {
+func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *testing.T) {
 	name := "testbuildnullstringinaddcopyvolume"
 	volName := "nullvolume"
 	if testEnv.OSType == "windows" {
@@ -4071,7 +4063,7 @@ func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) {
 	))
 }
 
-func (s *DockerSuite) TestBuildStopSignal(c *check.C) {
+func (s *DockerSuite) TestBuildStopSignal(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support STOPSIGNAL yet
 	imgName := "test_build_stop_signal"
 	buildImageSuccessfully(c, imgName, build.WithDockerfile(`FROM busybox
@@ -4089,7 +4081,7 @@ func (s *DockerSuite) TestBuildStopSignal(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArg(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArg(c *testing.T) {
 	imgName := "bldargtest"
 	envKey := "foo"
 	envVal := "bar"
@@ -4122,7 +4114,7 @@ func (s *DockerSuite) TestBuildBuildTimeArg(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgHistory(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgHistory(c *testing.T) {
 	imgName := "bldargtest"
 	envKey := "foo"
 	envVal := "bar"
@@ -4143,7 +4135,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgHistory(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildTimeArgHistoryExclusions(c *check.C) {
+func (s *DockerSuite) TestBuildTimeArgHistoryExclusions(c *testing.T) {
 	imgName := "bldargtest"
 	envKey := "foo"
 	envVal := "bar"
@@ -4180,10 +4172,10 @@ func (s *DockerSuite) TestBuildTimeArgHistoryExclusions(c *check.C) {
 	result.Assert(c, icmd.Expected{Out: fmt.Sprintf("%s=%s", explicitProxyKey, explicitProxyVal)})
 
 	cacheID := buildImage(imgName + "-two")
-	c.Assert(origID, checker.Equals, cacheID)
+	assert.Equal(c, origID, cacheID)
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgCacheHit(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgCacheHit(c *testing.T) {
 	imgName := "bldargtest"
 	envKey := "foo"
 	envVal := "bar"
@@ -4207,7 +4199,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgCacheHit(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgCacheMissExtraArg(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgCacheMissExtraArg(c *testing.T) {
 	imgName := "bldargtest"
 	envKey := "foo"
 	envVal := "bar"
@@ -4238,7 +4230,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgCacheMissExtraArg(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgCacheMissSameArgDiffVal(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgCacheMissSameArgDiffVal(c *testing.T) {
 	imgName := "bldargtest"
 	envKey := "foo"
 	envVal := "bar"
@@ -4263,7 +4255,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgCacheMissSameArgDiffVal(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgOverrideArgDefinedBeforeEnv(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgOverrideArgDefinedBeforeEnv(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support ARG
 	imgName := "bldargtest"
 	envKey := "foo"
@@ -4292,7 +4284,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgOverrideArgDefinedBeforeEnv(c *check.
 }
 
 // FIXME(vdemeester) might be useful to merge with the one above ?
-func (s *DockerSuite) TestBuildBuildTimeArgOverrideEnvDefinedBeforeArg(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgOverrideEnvDefinedBeforeArg(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support ARG
 	imgName := "bldargtest"
 	envKey := "foo"
@@ -4319,7 +4311,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgOverrideEnvDefinedBeforeArg(c *check.
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgExpansion(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgExpansion(c *testing.T) {
 	imgName := "bldvarstest"
 
 	wdVar := "WDIR"
@@ -4375,7 +4367,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgExpansion(c *check.C) {
 	)
 
 	res := inspectField(c, imgName, "Config.WorkingDir")
-	c.Check(filepath.ToSlash(res), check.Equals, filepath.ToSlash(wdVal))
+	assert.Equal(c, filepath.ToSlash(res), filepath.ToSlash(wdVal))
 
 	var resArr []string
 	inspectFieldAndUnmarshall(c, imgName, "Config.Env", &resArr)
@@ -4409,7 +4401,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgExpansion(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgExpansionOverride(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgExpansionOverride(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support ARG
 	imgName := "bldvarstest"
 	envKey := "foo"
@@ -4437,7 +4429,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgExpansionOverride(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgUntrustedDefinedAfterUse(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgUntrustedDefinedAfterUse(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support ARG
 	imgName := "bldargtest"
 	envKey := "foo"
@@ -4461,7 +4453,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgUntrustedDefinedAfterUse(c *check.C)
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgBuiltinArg(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgBuiltinArg(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support --build-arg
 	imgName := "bldargtest"
 	envKey := "HTTP_PROXY"
@@ -4484,7 +4476,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgBuiltinArg(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgDefaultOverride(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgDefaultOverride(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support ARG
 	imgName := "bldargtest"
 	envKey := "foo"
@@ -4510,7 +4502,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgDefaultOverride(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgUnconsumedArg(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgUnconsumedArg(c *testing.T) {
 	imgName := "bldargtest"
 	envKey := "foo"
 	envVal := "bar"
@@ -4526,7 +4518,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgUnconsumedArg(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgEnv(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgEnv(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support ARG
 	dockerfile := `FROM busybox
 		ARG FOO1=fromfile
@@ -4537,30 +4529,30 @@ func (s *DockerSuite) TestBuildBuildTimeArgEnv(c *check.C) {
 		ARG FOO6
 		ARG FO10
 		RUN env
-		RUN [ "$FOO1" == "fromcmd" ]
-		RUN [ "$FOO2" == "" ]
-		RUN [ "$FOO3" == "fromenv" ]
-		RUN [ "$FOO4" == "fromfile" ]
-		RUN [ "$FOO5" == "fromcmd" ]
+		RUN [ "$FOO1" = "fromcmd" ]
+		RUN [ "$FOO2" = "" ]
+		RUN [ "$FOO3" = "fromenv" ]
+		RUN [ "$FOO4" = "fromfile" ]
+		RUN [ "$FOO5" = "fromcmd" ]
 		# The following should not exist at all in the env
-		RUN [ "$(env | grep FOO6)" == "" ]
-		RUN [ "$(env | grep FOO7)" == "" ]
-		RUN [ "$(env | grep FOO8)" == "" ]
-		RUN [ "$(env | grep FOO9)" == "" ]
-		RUN [ "$FO10" == "" ]
+		RUN [ "$(env | grep FOO6)" = "" ]
+		RUN [ "$(env | grep FOO7)" = "" ]
+		RUN [ "$(env | grep FOO8)" = "" ]
+		RUN [ "$(env | grep FOO9)" = "" ]
+		RUN [ "$FO10" = "" ]
 	    `
 	result := buildImage("testbuildtimeargenv",
 		cli.WithFlags(
-			"--build-arg", fmt.Sprintf("FOO1=fromcmd"),
-			"--build-arg", fmt.Sprintf("FOO2="),
-			"--build-arg", fmt.Sprintf("FOO3"), // set in env
-			"--build-arg", fmt.Sprintf("FOO4"), // not set in env
-			"--build-arg", fmt.Sprintf("FOO5=fromcmd"),
+			"--build-arg", "FOO1=fromcmd",
+			"--build-arg", "FOO2=",
+			"--build-arg", "FOO3", // set in env
+			"--build-arg", "FOO4", // not set in env
+			"--build-arg", "FOO5=fromcmd",
 			// FOO6 is not set at all
-			"--build-arg", fmt.Sprintf("FOO7=fromcmd"), // should produce a warning
-			"--build-arg", fmt.Sprintf("FOO8="), // should produce a warning
-			"--build-arg", fmt.Sprintf("FOO9"), // should produce a warning
-			"--build-arg", fmt.Sprintf("FO10"), // not set in env, empty value
+			"--build-arg", "FOO7=fromcmd", // should produce a warning
+			"--build-arg", "FOO8=", // should produce a warning
+			"--build-arg", "FOO9", // should produce a warning
+			"--build-arg", "FO10", // not set in env, empty value
 		),
 		cli.WithEnvironmentVariables(append(os.Environ(),
 			"FOO1=fromenv",
@@ -4581,12 +4573,12 @@ func (s *DockerSuite) TestBuildBuildTimeArgEnv(c *check.C) {
 	out := result.Combined()[i:] // "out" should contain just the warning message now
 
 	// These were specified on a --build-arg but no ARG was in the Dockerfile
-	c.Assert(out, checker.Contains, "FOO7")
-	c.Assert(out, checker.Contains, "FOO8")
-	c.Assert(out, checker.Contains, "FOO9")
+	assert.Assert(c, strings.Contains(out, "FOO7"))
+	assert.Assert(c, strings.Contains(out, "FOO8"))
+	assert.Assert(c, strings.Contains(out, "FOO9"))
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgQuotedValVariants(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgQuotedValVariants(c *testing.T) {
 	imgName := "bldargtest"
 	envKey := "foo"
 	envKey1 := "foo1"
@@ -4607,7 +4599,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgQuotedValVariants(c *check.C) {
 	buildImageSuccessfully(c, imgName, build.WithDockerfile(dockerfile))
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgEmptyValVariants(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgEmptyValVariants(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support ARG
 	imgName := "bldargtest"
 	envKey := "foo"
@@ -4617,13 +4609,13 @@ func (s *DockerSuite) TestBuildBuildTimeArgEmptyValVariants(c *check.C) {
 		ARG %s=
 		ARG %s=""
 		ARG %s=''
-		RUN [ "$%s" == "$%s" ]
-		RUN [ "$%s" == "$%s" ]
-		RUN [ "$%s" == "$%s" ]`, envKey, envKey1, envKey2, envKey, envKey1, envKey1, envKey2, envKey, envKey2)
+		RUN [ "$%s" = "$%s" ]
+		RUN [ "$%s" = "$%s" ]
+		RUN [ "$%s" = "$%s" ]`, envKey, envKey1, envKey2, envKey, envKey1, envKey1, envKey2, envKey, envKey2)
 	buildImageSuccessfully(c, imgName, build.WithDockerfile(dockerfile))
 }
 
-func (s *DockerSuite) TestBuildBuildTimeArgDefinitionWithNoEnvInjection(c *check.C) {
+func (s *DockerSuite) TestBuildBuildTimeArgDefinitionWithNoEnvInjection(c *testing.T) {
 	imgName := "bldargtest"
 	envKey := "foo"
 	dockerfile := fmt.Sprintf(`FROM busybox
@@ -4637,7 +4629,7 @@ func (s *DockerSuite) TestBuildBuildTimeArgDefinitionWithNoEnvInjection(c *check
 	}
 }
 
-func (s *DockerSuite) TestBuildMultiStageArg(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageArg(c *testing.T) {
 	imgName := "multifrombldargtest"
 	dockerfile := `FROM busybox
     ARG foo=abc
@@ -4654,14 +4646,13 @@ func (s *DockerSuite) TestBuildMultiStageArg(c *check.C) {
 	parentID := strings.TrimSpace(result.Stdout())
 
 	result = cli.DockerCmd(c, "run", "--rm", parentID, "cat", "/out")
-	c.Assert(result.Stdout(), checker.Contains, "foo=abc")
-
+	assert.Assert(c, strings.Contains(result.Stdout(), "foo=abc"))
 	result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out")
-	c.Assert(result.Stdout(), checker.Not(checker.Contains), "foo")
-	c.Assert(result.Stdout(), checker.Contains, "bar=def")
+	assert.Assert(c, !strings.Contains(result.Stdout(), "foo"))
+	assert.Assert(c, strings.Contains(result.Stdout(), "bar=def"))
 }
 
-func (s *DockerSuite) TestBuildMultiStageGlobalArg(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageGlobalArg(c *testing.T) {
 	imgName := "multifrombldargtest"
 	dockerfile := `ARG tag=nosuchtag
      FROM busybox:${tag}
@@ -4673,20 +4664,19 @@ func (s *DockerSuite) TestBuildMultiStageGlobalArg(c *check.C) {
 
 	result := cli.BuildCmd(c, imgName,
 		build.WithDockerfile(dockerfile),
-		cli.WithFlags("--build-arg", fmt.Sprintf("tag=latest")))
+		cli.WithFlags("--build-arg", "tag=latest"))
 	result.Assert(c, icmd.Success)
 
 	result = cli.DockerCmd(c, "images", "-q", "-f", "label=multifromtest=1")
 	parentID := strings.TrimSpace(result.Stdout())
 
 	result = cli.DockerCmd(c, "run", "--rm", parentID, "cat", "/out")
-	c.Assert(result.Stdout(), checker.Not(checker.Contains), "tag")
-
+	assert.Assert(c, !strings.Contains(result.Stdout(), "tag"))
 	result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out")
-	c.Assert(result.Stdout(), checker.Contains, "tag=latest")
+	assert.Assert(c, strings.Contains(result.Stdout(), "tag=latest"))
 }
 
-func (s *DockerSuite) TestBuildMultiStageUnusedArg(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageUnusedArg(c *testing.T) {
 	imgName := "multifromunusedarg"
 	dockerfile := `FROM busybox
     ARG foo
@@ -4696,17 +4686,16 @@ func (s *DockerSuite) TestBuildMultiStageUnusedArg(c *check.C) {
 
 	result := cli.BuildCmd(c, imgName,
 		build.WithDockerfile(dockerfile),
-		cli.WithFlags("--build-arg", fmt.Sprintf("baz=abc")))
+		cli.WithFlags("--build-arg", "baz=abc"))
 	result.Assert(c, icmd.Success)
-	c.Assert(result.Combined(), checker.Contains, "[Warning]")
-	c.Assert(result.Combined(), checker.Contains, "[baz] were not consumed")
-
+	assert.Assert(c, strings.Contains(result.Combined(), "[Warning]"))
+	assert.Assert(c, strings.Contains(result.Combined(), "[baz] were not consumed"))
 	result = cli.DockerCmd(c, "run", "--rm", imgName, "cat", "/out")
-	c.Assert(result.Stdout(), checker.Not(checker.Contains), "bar")
-	c.Assert(result.Stdout(), checker.Not(checker.Contains), "baz")
+	assert.Assert(c, !strings.Contains(result.Stdout(), "bar"))
+	assert.Assert(c, !strings.Contains(result.Stdout(), "baz"))
 }
 
-func (s *DockerSuite) TestBuildNoNamedVolume(c *check.C) {
+func (s *DockerSuite) TestBuildNoNamedVolume(c *testing.T) {
 	volName := "testname:/foo"
 
 	if testEnv.OSType == "windows" {
@@ -4723,7 +4712,7 @@ func (s *DockerSuite) TestBuildNoNamedVolume(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildTagEvent(c *check.C) {
+func (s *DockerSuite) TestBuildTagEvent(c *testing.T) {
 	since := daemonUnixTime(c)
 
 	dockerFile := `FROM busybox
@@ -4743,11 +4732,11 @@ func (s *DockerSuite) TestBuildTagEvent(c *check.C) {
 		}
 	}
 
-	c.Assert(foundTag, checker.True, check.Commentf("No tag event found:\n%s", out))
+	assert.Assert(c, foundTag, "No tag event found:\n%s", out)
 }
 
 // #15780
-func (s *DockerSuite) TestBuildMultipleTags(c *check.C) {
+func (s *DockerSuite) TestBuildMultipleTags(c *testing.T) {
 	dockerfile := `
 	FROM busybox
 	MAINTAINER test-15780
@@ -4756,11 +4745,11 @@ func (s *DockerSuite) TestBuildMultipleTags(c *check.C) {
 
 	id1 := getIDByName(c, "tag1")
 	id2 := getIDByName(c, "tag2:v2")
-	c.Assert(id1, check.Equals, id2)
+	assert.Equal(c, id1, id2)
 }
 
 // #17290
-func (s *DockerSuite) TestBuildCacheBrokenSymlink(c *check.C) {
+func (s *DockerSuite) TestBuildCacheBrokenSymlink(c *testing.T) {
 	name := "testbuildbrokensymlink"
 	ctx := fakecontext.New(c, "",
 		fakecontext.WithDockerfile(`
@@ -4772,14 +4761,14 @@ func (s *DockerSuite) TestBuildCacheBrokenSymlink(c *check.C) {
 	defer ctx.Close()
 
 	err := os.Symlink(filepath.Join(ctx.Dir, "nosuchfile"), filepath.Join(ctx.Dir, "asymlink"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// warm up cache
 	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
 
 	// add new file to context, should invalidate cache
-	err = ioutil.WriteFile(filepath.Join(ctx.Dir, "newfile"), []byte("foo"), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(ctx.Dir, "newfile"), []byte("foo"), 0644)
+	assert.NilError(c, err)
 
 	result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
 	if strings.Contains(result.Combined(), "Using cache") {
@@ -4787,7 +4776,7 @@ func (s *DockerSuite) TestBuildCacheBrokenSymlink(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildFollowSymlinkToFile(c *check.C) {
+func (s *DockerSuite) TestBuildFollowSymlinkToFile(c *testing.T) {
 	name := "testbuildbrokensymlink"
 	ctx := fakecontext.New(c, "",
 		fakecontext.WithDockerfile(`
@@ -4799,25 +4788,25 @@ func (s *DockerSuite) TestBuildFollowSymlinkToFile(c *check.C) {
 	defer ctx.Close()
 
 	err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
 
 	out := cli.DockerCmd(c, "run", "--rm", name, "cat", "target").Combined()
-	c.Assert(out, checker.Matches, "bar")
+	assert.Assert(c, cmp.Regexp("^bar$", out))
 
 	// change target file should invalidate cache
-	err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644)
+	assert.NilError(c, err)
 
 	result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
-	c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache")
-
+	assert.Assert(c, !strings.Contains(result.Combined(), "Using cache"))
 	out = cli.DockerCmd(c, "run", "--rm", name, "cat", "target").Combined()
-	c.Assert(out, checker.Matches, "baz")
+	assert.Assert(c, cmp.Regexp("^baz$", out))
+
 }
 
-func (s *DockerSuite) TestBuildFollowSymlinkToDir(c *check.C) {
+func (s *DockerSuite) TestBuildFollowSymlinkToDir(c *testing.T) {
 	name := "testbuildbrokensymlink"
 	ctx := fakecontext.New(c, "",
 		fakecontext.WithDockerfile(`
@@ -4830,28 +4819,27 @@ func (s *DockerSuite) TestBuildFollowSymlinkToDir(c *check.C) {
 	defer ctx.Close()
 
 	err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
 
 	out := cli.DockerCmd(c, "run", "--rm", name, "cat", "abc", "def").Combined()
-	c.Assert(out, checker.Matches, "barbaz")
+	assert.Assert(c, cmp.Regexp("^barbaz$", out))
 
 	// change target file should invalidate cache
-	err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo/def"), []byte("bax"), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(ctx.Dir, "foo/def"), []byte("bax"), 0644)
+	assert.NilError(c, err)
 
 	result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
-	c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache")
-
+	assert.Assert(c, !strings.Contains(result.Combined(), "Using cache"))
 	out = cli.DockerCmd(c, "run", "--rm", name, "cat", "abc", "def").Combined()
-	c.Assert(out, checker.Matches, "barbax")
+	assert.Assert(c, cmp.Regexp("^barbax$", out))
 
 }
 
 // TestBuildSymlinkBasename tests that target file gets basename from symlink,
 // not from the target file.
-func (s *DockerSuite) TestBuildSymlinkBasename(c *check.C) {
+func (s *DockerSuite) TestBuildSymlinkBasename(c *testing.T) {
 	name := "testbuildbrokensymlink"
 	ctx := fakecontext.New(c, "",
 		fakecontext.WithDockerfile(`
@@ -4863,16 +4851,17 @@ func (s *DockerSuite) TestBuildSymlinkBasename(c *check.C) {
 	defer ctx.Close()
 
 	err := os.Symlink("foo", filepath.Join(ctx.Dir, "asymlink"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
 
 	out := cli.DockerCmd(c, "run", "--rm", name, "cat", "asymlink").Combined()
-	c.Assert(out, checker.Matches, "bar")
+	assert.Assert(c, cmp.Regexp("^bar$", out))
+
 }
 
 // #17827
-func (s *DockerSuite) TestBuildCacheRootSource(c *check.C) {
+func (s *DockerSuite) TestBuildCacheRootSource(c *testing.T) {
 	name := "testbuildrootsource"
 	ctx := fakecontext.New(c, "",
 		fakecontext.WithDockerfile(`
@@ -4887,17 +4876,17 @@ func (s *DockerSuite) TestBuildCacheRootSource(c *check.C) {
 	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
 
 	// change file, should invalidate cache
-	err := ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644)
-	c.Assert(err, checker.IsNil)
+	err := os.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("baz"), 0644)
+	assert.NilError(c, err)
 
 	result := cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
 
-	c.Assert(result.Combined(), checker.Not(checker.Contains), "Using cache")
+	assert.Assert(c, !strings.Contains(result.Combined(), "Using cache"))
 }
 
 // #19375
 // FIXME(vdemeester) should migrate to docker/cli tests
-func (s *DockerSuite) TestBuildFailsGitNotCallable(c *check.C) {
+func (s *DockerSuite) TestBuildFailsGitNotCallable(c *testing.T) {
 	buildImage("gitnotcallable", cli.WithEnvironmentVariables("PATH="),
 		build.WithContextPath("github.com/docker/v1.10-migrator.git")).Assert(c, icmd.Expected{
 		ExitCode: 1,
@@ -4912,7 +4901,7 @@ func (s *DockerSuite) TestBuildFailsGitNotCallable(c *check.C) {
 }
 
 // TestBuildWorkdirWindowsPath tests that a Windows style path works as a workdir
-func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *check.C) {
+func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	name := "testbuildworkdirwindowspath"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`
@@ -4923,7 +4912,7 @@ func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *check.C) {
 	`))
 }
 
-func (s *DockerSuite) TestBuildLabel(c *check.C) {
+func (s *DockerSuite) TestBuildLabel(c *testing.T) {
 	name := "testbuildlabel"
 	testLabel := "foo"
 
@@ -4940,7 +4929,7 @@ func (s *DockerSuite) TestBuildLabel(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildLabelOneNode(c *check.C) {
+func (s *DockerSuite) TestBuildLabelOneNode(c *testing.T) {
 	name := "testbuildlabel"
 	buildImageSuccessfully(c, name, cli.WithFlags("--label", "foo=bar"),
 		build.WithDockerfile("FROM busybox"))
@@ -4951,10 +4940,10 @@ func (s *DockerSuite) TestBuildLabelOneNode(c *check.C) {
 	if !ok {
 		c.Fatal("label `foo` not found in image")
 	}
-	c.Assert(v, checker.Equals, "bar")
+	assert.Equal(c, v, "bar")
 }
 
-func (s *DockerSuite) TestBuildLabelCacheCommit(c *check.C) {
+func (s *DockerSuite) TestBuildLabelCacheCommit(c *testing.T) {
 	name := "testbuildlabelcachecommit"
 	testLabel := "foo"
 
@@ -4975,7 +4964,7 @@ func (s *DockerSuite) TestBuildLabelCacheCommit(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildLabelMultiple(c *check.C) {
+func (s *DockerSuite) TestBuildLabelMultiple(c *testing.T) {
 	name := "testbuildlabelmultiple"
 	testLabels := map[string]string{
 		"foo": "bar",
@@ -5001,7 +4990,7 @@ func (s *DockerSuite) TestBuildLabelMultiple(c *check.C) {
 	}
 }
 
-func (s *DockerRegistryAuthHtpasswdSuite) TestBuildFromAuthenticatedRegistry(c *check.C) {
+func (s *DockerRegistryAuthHtpasswdSuite) TestBuildFromAuthenticatedRegistry(c *testing.T) {
 	dockerCmd(c, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL)
 	baseImage := privateRegistryURL + "/baseimage"
 
@@ -5019,35 +5008,34 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestBuildFromAuthenticatedRegistry(c *
 	`, baseImage)))
 }
 
-func (s *DockerRegistryAuthHtpasswdSuite) TestBuildWithExternalAuth(c *check.C) {
+func (s *DockerRegistryAuthHtpasswdSuite) TestBuildWithExternalAuth(c *testing.T) {
 	osPath := os.Getenv("PATH")
 	defer os.Setenv("PATH", osPath)
 
 	workingDir, err := os.Getwd()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute)
 
 	os.Setenv("PATH", testPath)
 
 	repoName := fmt.Sprintf("%v/dockercli/busybox:authtest", privateRegistryURL)
 
-	tmp, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, checker.IsNil)
+	tmp, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 
 	externalAuthConfig := `{ "credsStore": "shell-test" }`
 
 	configPath := filepath.Join(tmp, "config.json")
-	err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(configPath, []byte(externalAuthConfig), 0644)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL)
 
-	b, err := ioutil.ReadFile(configPath)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":")
-
+	b, err := os.ReadFile(configPath)
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(string(b), "\"auth\":"))
 	dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
 	dockerCmd(c, "--config", tmp, "push", repoName)
 
@@ -5061,7 +5049,7 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestBuildWithExternalAuth(c *check.C)
 }
 
 // Test cases in #22036
-func (s *DockerSuite) TestBuildLabelsOverride(c *check.C) {
+func (s *DockerSuite) TestBuildLabelsOverride(c *testing.T) {
 	// Command line option labels will always override
 	name := "scratchy"
 	expected := `{"bar":"from-flag","foo":"from-flag"}`
@@ -5140,7 +5128,7 @@ func (s *DockerSuite) TestBuildLabelsOverride(c *check.C) {
 }
 
 // Test case for #22855
-func (s *DockerSuite) TestBuildDeleteCommittedFile(c *check.C) {
+func (s *DockerSuite) TestBuildDeleteCommittedFile(c *testing.T) {
 	name := "test-delete-committed-file"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
 		RUN echo test > file
@@ -5150,7 +5138,7 @@ func (s *DockerSuite) TestBuildDeleteCommittedFile(c *check.C) {
 }
 
 // #20083
-func (s *DockerSuite) TestBuildDockerignoreComment(c *check.C) {
+func (s *DockerSuite) TestBuildDockerignoreComment(c *testing.T) {
 	// TODO Windows: Figure out why this test is flakey on TP5. If you add
 	// something like RUN sleep 5, or even RUN ls /tmp after the ADD line,
 	// it is more reliable, but that's not a good fix.
@@ -5184,7 +5172,7 @@ foo2
 }
 
 // Test case for #23221
-func (s *DockerSuite) TestBuildWithUTF8BOM(c *check.C) {
+func (s *DockerSuite) TestBuildWithUTF8BOM(c *testing.T) {
 	name := "test-with-utf8-bom"
 	dockerfile := []byte(`FROM busybox`)
 	bomDockerfile := append([]byte{0xEF, 0xBB, 0xBF}, dockerfile...)
@@ -5194,7 +5182,7 @@ func (s *DockerSuite) TestBuildWithUTF8BOM(c *check.C) {
 }
 
 // Test case for UTF-8 BOM in .dockerignore, related to #23221
-func (s *DockerSuite) TestBuildWithUTF8BOMDockerignore(c *check.C) {
+func (s *DockerSuite) TestBuildWithUTF8BOMDockerignore(c *testing.T) {
 	name := "test-with-utf8-bom-dockerignore"
 	dockerfile := `
         FROM busybox
@@ -5211,7 +5199,7 @@ func (s *DockerSuite) TestBuildWithUTF8BOMDockerignore(c *check.C) {
 }
 
 // #22489 Shell test to confirm config gets updated correctly
-func (s *DockerSuite) TestBuildShellUpdatesConfig(c *check.C) {
+func (s *DockerSuite) TestBuildShellUpdatesConfig(c *testing.T) {
 	name := "testbuildshellupdatesconfig"
 
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
@@ -5228,7 +5216,7 @@ func (s *DockerSuite) TestBuildShellUpdatesConfig(c *check.C) {
 }
 
 // #22489 Changing the shell multiple times and CMD after.
-func (s *DockerSuite) TestBuildShellMultiple(c *check.C) {
+func (s *DockerSuite) TestBuildShellMultiple(c *testing.T) {
 	name := "testbuildshellmultiple"
 
 	result := buildImage(name, build.WithDockerfile(`FROM busybox
@@ -5264,7 +5252,7 @@ func (s *DockerSuite) TestBuildShellMultiple(c *check.C) {
 }
 
 // #22489. Changed SHELL with ENTRYPOINT
-func (s *DockerSuite) TestBuildShellEntrypoint(c *check.C) {
+func (s *DockerSuite) TestBuildShellEntrypoint(c *testing.T) {
 	name := "testbuildshellentrypoint"
 
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
@@ -5279,7 +5267,7 @@ func (s *DockerSuite) TestBuildShellEntrypoint(c *check.C) {
 }
 
 // #22489 Shell test to confirm shell is inherited in a subsequent build
-func (s *DockerSuite) TestBuildShellInherited(c *check.C) {
+func (s *DockerSuite) TestBuildShellInherited(c *testing.T) {
 	name1 := "testbuildshellinherited1"
 	buildImageSuccessfully(c, name1, build.WithDockerfile(`FROM busybox
         SHELL ["ls"]`))
@@ -5292,7 +5280,7 @@ func (s *DockerSuite) TestBuildShellInherited(c *check.C) {
 }
 
 // #22489 Shell test to confirm non-JSON doesn't work
-func (s *DockerSuite) TestBuildShellNotJSON(c *check.C) {
+func (s *DockerSuite) TestBuildShellNotJSON(c *testing.T) {
 	name := "testbuildshellnotjson"
 
 	buildImage(name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
@@ -5305,7 +5293,7 @@ func (s *DockerSuite) TestBuildShellNotJSON(c *check.C) {
 
 // #22489 Windows shell test to confirm native is powershell if executing a PS command
 // This would error if the default shell were still cmd.
-func (s *DockerSuite) TestBuildShellWindowsPowershell(c *check.C) {
+func (s *DockerSuite) TestBuildShellWindowsPowershell(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	name := "testbuildshellpowershell"
 	buildImage(name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
@@ -5317,7 +5305,7 @@ func (s *DockerSuite) TestBuildShellWindowsPowershell(c *check.C) {
 
 // Verify that escape is being correctly applied to words when escape directive is not \.
 // Tests WORKDIR, ADD
-func (s *DockerSuite) TestBuildEscapeNotBackslashWordTest(c *check.C) {
+func (s *DockerSuite) TestBuildEscapeNotBackslashWordTest(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	name := "testbuildescapenotbackslashwordtesta"
 	buildImage(name, build.WithDockerfile(`# escape= `+"`"+`
@@ -5338,58 +5326,77 @@ func (s *DockerSuite) TestBuildEscapeNotBackslashWordTest(c *check.C) {
 	})
 }
 
-// #22868. Make sure shell-form CMD is marked as escaped in the config of the image
-func (s *DockerSuite) TestBuildCmdShellArgsEscaped(c *check.C) {
+// #22868. Make sure shell-form CMD is not marked as escaped in the config of the image,
+// but an exec-form CMD is marked.
+func (s *DockerSuite) TestBuildCmdShellArgsEscaped(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
-	name := "testbuildcmdshellescaped"
-	buildImageSuccessfully(c, name, build.WithDockerfile(`
+	name1 := "testbuildcmdshellescapedshellform"
+	buildImageSuccessfully(c, name1, build.WithDockerfile(`
   FROM `+minimalBaseImage()+`
   CMD "ipconfig"
   `))
-	res := inspectFieldJSON(c, name, "Config.ArgsEscaped")
+	res := inspectFieldJSON(c, name1, "Config.ArgsEscaped")
 	if res != "true" {
 		c.Fatalf("CMD did not update Config.ArgsEscaped on image: %v", res)
 	}
-	dockerCmd(c, "run", "--name", "inspectme", name)
-	dockerCmd(c, "wait", "inspectme")
-	res = inspectFieldJSON(c, name, "Config.Cmd")
+	dockerCmd(c, "run", "--name", "inspectme1", name1)
+	dockerCmd(c, "wait", "inspectme1")
+	res = inspectFieldJSON(c, name1, "Config.Cmd")
 
-	if res != `["cmd","/S","/C","\"ipconfig\""]` {
-		c.Fatalf("CMD was not escaped Config.Cmd: got %v", res)
+	if res != `["cmd /S /C \"ipconfig\""]` {
+		c.Fatalf("CMD incorrect in Config.Cmd: got %v", res)
 	}
+
+	// Now in JSON/exec-form
+	name2 := "testbuildcmdshellescapedexecform"
+	buildImageSuccessfully(c, name2, build.WithDockerfile(`
+  FROM `+minimalBaseImage()+`
+  CMD ["ipconfig"]
+  `))
+	res = inspectFieldJSON(c, name2, "Config.ArgsEscaped")
+	if res != "false" {
+		c.Fatalf("CMD set Config.ArgsEscaped on image: %v", res)
+	}
+	dockerCmd(c, "run", "--name", "inspectme2", name2)
+	dockerCmd(c, "wait", "inspectme2")
+	res = inspectFieldJSON(c, name2, "Config.Cmd")
+
+	if res != `["ipconfig"]` {
+		c.Fatalf("CMD incorrect in Config.Cmd: got %v", res)
+	}
+
 }
 
 // Test case for #24912.
-func (s *DockerSuite) TestBuildStepsWithProgress(c *check.C) {
+func (s *DockerSuite) TestBuildStepsWithProgress(c *testing.T) {
 	name := "testbuildstepswithprogress"
 	totalRun := 5
 	result := buildImage(name, build.WithDockerfile("FROM busybox\n"+strings.Repeat("RUN echo foo\n", totalRun)))
 	result.Assert(c, icmd.Success)
-	c.Assert(result.Combined(), checker.Contains, fmt.Sprintf("Step 1/%d : FROM busybox", 1+totalRun))
+	assert.Assert(c, strings.Contains(result.Combined(), fmt.Sprintf("Step 1/%d : FROM busybox", 1+totalRun)))
 	for i := 2; i <= 1+totalRun; i++ {
-		c.Assert(result.Combined(), checker.Contains, fmt.Sprintf("Step %d/%d : RUN echo foo", i, 1+totalRun))
+		assert.Assert(c, strings.Contains(result.Combined(), fmt.Sprintf("Step %d/%d : RUN echo foo", i, 1+totalRun)))
 	}
 }
 
-func (s *DockerSuite) TestBuildWithFailure(c *check.C) {
+func (s *DockerSuite) TestBuildWithFailure(c *testing.T) {
 	name := "testbuildwithfailure"
 
 	// First test case can only detect `nobody` in runtime so all steps will show up
 	dockerfile := "FROM busybox\nRUN nobody"
 	result := buildImage(name, build.WithDockerfile(dockerfile))
-	c.Assert(result.Error, checker.NotNil)
-	c.Assert(result.Stdout(), checker.Contains, "Step 1/2 : FROM busybox")
-	c.Assert(result.Stdout(), checker.Contains, "Step 2/2 : RUN nobody")
-
+	assert.Assert(c, result.Error != nil)
+	assert.Assert(c, strings.Contains(result.Stdout(), "Step 1/2 : FROM busybox"))
+	assert.Assert(c, strings.Contains(result.Stdout(), "Step 2/2 : RUN nobody"))
 	// Second test case `FFOM` should have been detected before build runs so no steps
 	dockerfile = "FFOM nobody\nRUN nobody"
 	result = buildImage(name, build.WithDockerfile(dockerfile))
-	c.Assert(result.Error, checker.NotNil)
-	c.Assert(result.Stdout(), checker.Not(checker.Contains), "Step 1/2 : FROM busybox")
-	c.Assert(result.Stdout(), checker.Not(checker.Contains), "Step 2/2 : RUN nobody")
+	assert.Assert(c, result.Error != nil)
+	assert.Assert(c, !strings.Contains(result.Stdout(), "Step 1/2 : FROM busybox"))
+	assert.Assert(c, !strings.Contains(result.Stdout(), "Step 2/2 : RUN nobody"))
 }
 
-func (s *DockerSuite) TestBuildCacheFromEqualDiffIDsLength(c *check.C) {
+func (s *DockerSuite) TestBuildCacheFromEqualDiffIDsLength(c *testing.T) {
 	dockerfile := `
 		FROM busybox
 		RUN echo "test"
@@ -5407,11 +5414,11 @@ func (s *DockerSuite) TestBuildCacheFromEqualDiffIDsLength(c *check.C) {
 	// rebuild with cache-from
 	result := cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
 	id2 := getIDByName(c, "build2")
-	c.Assert(id1, checker.Equals, id2)
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2)
+	assert.Equal(c, id1, id2)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 2)
 }
 
-func (s *DockerSuite) TestBuildCacheFrom(c *check.C) {
+func (s *DockerSuite) TestBuildCacheFrom(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows
 	dockerfile := `
 		FROM busybox
@@ -5432,19 +5439,19 @@ func (s *DockerSuite) TestBuildCacheFrom(c *check.C) {
 	// rebuild with cache-from
 	result := cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
 	id2 := getIDByName(c, "build2")
-	c.Assert(id1, checker.Equals, id2)
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3)
+	assert.Equal(c, id1, id2)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 3)
 	cli.DockerCmd(c, "rmi", "build2")
 
 	// no cache match with unknown source
 	result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=nosuchtag"), build.WithExternalBuildContext(ctx))
 	id2 = getIDByName(c, "build2")
-	c.Assert(id1, checker.Not(checker.Equals), id2)
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 0)
+	assert.Assert(c, id1 != id2)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 0)
 	cli.DockerCmd(c, "rmi", "build2")
 
 	// clear parent images
-	tempDir, err := ioutil.TempDir("", "test-build-cache-from-")
+	tempDir, err := os.MkdirTemp("", "test-build-cache-from-")
 	if err != nil {
 		c.Fatalf("failed to create temporary directory: %s", tempDir)
 	}
@@ -5454,23 +5461,23 @@ func (s *DockerSuite) TestBuildCacheFrom(c *check.C) {
 	cli.DockerCmd(c, "rmi", "build1")
 	cli.DockerCmd(c, "load", "-i", tempFile)
 	parentID := cli.DockerCmd(c, "inspect", "-f", "{{.Parent}}", "build1").Combined()
-	c.Assert(strings.TrimSpace(parentID), checker.Equals, "")
+	assert.Equal(c, strings.TrimSpace(parentID), "")
 
 	// cache still applies without parents
 	result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
 	id2 = getIDByName(c, "build2")
-	c.Assert(id1, checker.Equals, id2)
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3)
+	assert.Equal(c, id1, id2)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 3)
 	history1 := cli.DockerCmd(c, "history", "-q", "build2").Combined()
 
 	// Retry, no new intermediate images
 	result = cli.BuildCmd(c, "build3", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
 	id3 := getIDByName(c, "build3")
-	c.Assert(id1, checker.Equals, id3)
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 3)
+	assert.Equal(c, id1, id3)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 3)
 	history2 := cli.DockerCmd(c, "history", "-q", "build3").Combined()
 
-	c.Assert(history1, checker.Equals, history2)
+	assert.Equal(c, history1, history2)
 	cli.DockerCmd(c, "rmi", "build2")
 	cli.DockerCmd(c, "rmi", "build3")
 	cli.DockerCmd(c, "rmi", "build1")
@@ -5482,30 +5489,30 @@ func (s *DockerSuite) TestBuildCacheFrom(c *check.C) {
 		ENV FOO=bar
 		ADD baz /
 		RUN touch newfile`
-	err = ioutil.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(dockerfile), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(dockerfile), 0644)
+	assert.NilError(c, err)
 
 	result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
 	id2 = getIDByName(c, "build2")
-	c.Assert(id1, checker.Not(checker.Equals), id2)
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2)
+	assert.Assert(c, id1 != id2)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 2)
 
 	layers1Str := cli.DockerCmd(c, "inspect", "-f", "{{json .RootFS.Layers}}", "build1").Combined()
 	layers2Str := cli.DockerCmd(c, "inspect", "-f", "{{json .RootFS.Layers}}", "build2").Combined()
 
 	var layers1 []string
 	var layers2 []string
-	c.Assert(json.Unmarshal([]byte(layers1Str), &layers1), checker.IsNil)
-	c.Assert(json.Unmarshal([]byte(layers2Str), &layers2), checker.IsNil)
+	assert.Assert(c, json.Unmarshal([]byte(layers1Str), &layers1) == nil)
+	assert.Assert(c, json.Unmarshal([]byte(layers2Str), &layers2) == nil)
 
-	c.Assert(len(layers1), checker.Equals, len(layers2))
+	assert.Equal(c, len(layers1), len(layers2))
 	for i := 0; i < len(layers1)-1; i++ {
-		c.Assert(layers1[i], checker.Equals, layers2[i])
+		assert.Equal(c, layers1[i], layers2[i])
 	}
-	c.Assert(layers1[len(layers1)-1], checker.Not(checker.Equals), layers2[len(layers1)-1])
+	assert.Assert(c, layers1[len(layers1)-1] != layers2[len(layers1)-1])
 }
 
-func (s *DockerSuite) TestBuildMultiStageCache(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageCache(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // All tests that do save are skipped in windows
 	dockerfile := `
 		FROM busybox
@@ -5522,14 +5529,14 @@ func (s *DockerSuite) TestBuildMultiStageCache(c *check.C) {
 
 	result := cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
 	// second part of dockerfile was a repeat of first so should be cached
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 1)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 1)
 
 	result = cli.BuildCmd(c, "build2", cli.WithFlags("--cache-from=build1"), build.WithExternalBuildContext(ctx))
 	// now both parts of dockerfile should be cached
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 2)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 2)
 }
 
-func (s *DockerSuite) TestBuildNetNone(c *check.C) {
+func (s *DockerSuite) TestBuildNetNone(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildnetnone"
 	buildImage(name, cli.WithFlags("--network=none"), build.WithDockerfile(`
@@ -5541,7 +5548,7 @@ func (s *DockerSuite) TestBuildNetNone(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestBuildNetContainer(c *check.C) {
+func (s *DockerSuite) TestBuildNetContainer(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	id, _ := dockerCmd(c, "run", "--hostname", "foobar", "-d", "busybox", "nc", "-ll", "-p", "1234", "-e", "hostname")
@@ -5554,10 +5561,10 @@ func (s *DockerSuite) TestBuildNetContainer(c *check.C) {
   `))
 
 	host, _ := dockerCmd(c, "run", "testbuildnetcontainer", "cat", "/otherhost")
-	c.Assert(strings.TrimSpace(host), check.Equals, "foobar")
+	assert.Equal(c, strings.TrimSpace(host), "foobar")
 }
 
-func (s *DockerSuite) TestBuildWithExtraHost(c *check.C) {
+func (s *DockerSuite) TestBuildWithExtraHost(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	name := "testbuildwithextrahost"
@@ -5573,7 +5580,7 @@ func (s *DockerSuite) TestBuildWithExtraHost(c *check.C) {
   `))
 }
 
-func (s *DockerSuite) TestBuildWithExtraHostInvalidFormat(c *check.C) {
+func (s *DockerSuite) TestBuildWithExtraHostInvalidFormat(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerfile := `
 		FROM busybox
@@ -5600,33 +5607,7 @@ func (s *DockerSuite) TestBuildWithExtraHostInvalidFormat(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestBuildContChar(c *check.C) {
-	name := "testbuildcontchar"
-
-	buildImage(name, build.WithDockerfile(`FROM busybox\`)).Assert(c, icmd.Expected{
-		Out: "Step 1/1 : FROM busybox",
-	})
-
-	result := buildImage(name, build.WithDockerfile(`FROM busybox
-		 RUN echo hi \`))
-	result.Assert(c, icmd.Success)
-	c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox")
-	c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi\n")
-
-	result = buildImage(name, build.WithDockerfile(`FROM busybox
-		 RUN echo hi \\`))
-	result.Assert(c, icmd.Success)
-	c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox")
-	c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi \\\n")
-
-	result = buildImage(name, build.WithDockerfile(`FROM busybox
-		 RUN echo hi \\\`))
-	result.Assert(c, icmd.Success)
-	c.Assert(result.Combined(), checker.Contains, "Step 1/2 : FROM busybox")
-	c.Assert(result.Combined(), checker.Contains, "Step 2/2 : RUN echo hi \\\\\n")
-}
-
-func (s *DockerSuite) TestBuildMultiStageCopyFromSyntax(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageCopyFromSyntax(c *testing.T) {
 	dockerfile := `
 		FROM busybox AS first
 		COPY foo bar
@@ -5663,28 +5644,28 @@ func (s *DockerSuite) TestBuildMultiStageCopyFromSyntax(c *check.C) {
 	result := cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx))
 
 	// all commands should be cached
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 7)
-	c.Assert(getIDByName(c, "build1"), checker.Equals, getIDByName(c, "build2"))
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 7)
+	assert.Equal(c, getIDByName(c, "build1"), getIDByName(c, "build2"))
 
-	err := ioutil.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(fmt.Sprintf(dockerfile, "COPY baz/aa foo")), 0644)
-	c.Assert(err, checker.IsNil)
+	err := os.WriteFile(filepath.Join(ctx.Dir, "Dockerfile"), []byte(fmt.Sprintf(dockerfile, "COPY baz/aa foo")), 0644)
+	assert.NilError(c, err)
 
 	// changing file in parent block should not affect last block
 	result = cli.BuildCmd(c, "build3", build.WithExternalBuildContext(ctx))
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 5)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 5)
 
-	err = ioutil.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("pqr"), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(ctx.Dir, "foo"), []byte("pqr"), 0644)
+	assert.NilError(c, err)
 
 	// changing file in parent block should affect both first and last block
 	result = cli.BuildCmd(c, "build4", build.WithExternalBuildContext(ctx))
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 5)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 5)
 
 	cli.DockerCmd(c, "run", "build4", "cat", "bay").Assert(c, icmd.Expected{Out: "pqr"})
 	cli.DockerCmd(c, "run", "build4", "cat", "baz").Assert(c, icmd.Expected{Out: "pqr"})
 }
 
-func (s *DockerSuite) TestBuildMultiStageCopyFromErrors(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageCopyFromErrors(c *testing.T) {
 	testCases := []struct {
 		dockerfile    string
 		expectedError string
@@ -5731,7 +5712,7 @@ func (s *DockerSuite) TestBuildMultiStageCopyFromErrors(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestBuildMultiStageMultipleBuilds(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageMultipleBuilds(c *testing.T) {
 	dockerfile := `
 		FROM busybox
 		COPY foo bar`
@@ -5759,12 +5740,12 @@ func (s *DockerSuite) TestBuildMultiStageMultipleBuilds(c *check.C) {
 	cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx))
 
 	out := cli.DockerCmd(c, "run", "build2", "cat", "bar").Combined()
-	c.Assert(strings.TrimSpace(out), check.Equals, "abc")
+	assert.Equal(c, strings.TrimSpace(out), "abc")
 	out = cli.DockerCmd(c, "run", "build2", "cat", "foo").Combined()
-	c.Assert(strings.TrimSpace(out), check.Equals, "def")
+	assert.Equal(c, strings.TrimSpace(out), "def")
 }
 
-func (s *DockerSuite) TestBuildMultiStageImplicitFrom(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageImplicitFrom(c *testing.T) {
 	dockerfile := `
 		FROM busybox
 		COPY --from=busybox /etc/passwd /mypasswd
@@ -5785,13 +5766,13 @@ func (s *DockerSuite) TestBuildMultiStageImplicitFrom(c *check.C) {
 
 	if DaemonIsWindows() {
 		out := cli.DockerCmd(c, "run", "build1", "cat", "License.txt").Combined()
-		c.Assert(len(out), checker.GreaterThan, 10)
+		assert.Assert(c, len(out) > 10)
 		out2 := cli.DockerCmd(c, "run", "build1", "cat", "foo").Combined()
-		c.Assert(out, check.Equals, out2)
+		assert.Equal(c, out, out2)
 	}
 }
 
-func (s *DockerRegistrySuite) TestBuildMultiStageImplicitPull(c *check.C) {
+func (s *DockerRegistrySuite) TestBuildMultiStageImplicitPull(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/testf", privateRegistryURL)
 
 	dockerfile := `
@@ -5821,7 +5802,7 @@ func (s *DockerRegistrySuite) TestBuildMultiStageImplicitPull(c *check.C) {
 	cli.Docker(cli.Args("run", "build1", "cat", "baz")).Assert(c, icmd.Expected{Out: "abc"})
 }
 
-func (s *DockerSuite) TestBuildMultiStageNameVariants(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageNameVariants(c *testing.T) {
 	dockerfile := `
 		FROM busybox as foo
 		COPY foo /
@@ -5846,7 +5827,7 @@ func (s *DockerSuite) TestBuildMultiStageNameVariants(c *check.C) {
 	cli.Docker(cli.Args("run", "build1", "cat", "f2")).Assert(c, icmd.Expected{Out: "bar2"})
 }
 
-func (s *DockerSuite) TestBuildMultiStageMultipleBuildsWindows(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageMultipleBuildsWindows(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	dockerfile := `
 		FROM ` + testEnv.PlatformDefaults.BaseImage + `
@@ -5875,12 +5856,12 @@ func (s *DockerSuite) TestBuildMultiStageMultipleBuildsWindows(c *check.C) {
 	cli.BuildCmd(c, "build2", build.WithExternalBuildContext(ctx))
 
 	out := cli.DockerCmd(c, "run", "build2", "cmd.exe", "/s", "/c", "type", "c:\\bar").Combined()
-	c.Assert(strings.TrimSpace(out), check.Equals, "abc")
+	assert.Equal(c, strings.TrimSpace(out), "abc")
 	out = cli.DockerCmd(c, "run", "build2", "cmd.exe", "/s", "/c", "type", "c:\\foo").Combined()
-	c.Assert(strings.TrimSpace(out), check.Equals, "def")
+	assert.Equal(c, strings.TrimSpace(out), "def")
 }
 
-func (s *DockerSuite) TestBuildCopyFromForbidWindowsSystemPaths(c *check.C) {
+func (s *DockerSuite) TestBuildCopyFromForbidWindowsSystemPaths(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	dockerfile := `
 		FROM ` + testEnv.PlatformDefaults.BaseImage + `
@@ -5897,7 +5878,7 @@ func (s *DockerSuite) TestBuildCopyFromForbidWindowsSystemPaths(c *check.C) {
 	buildImage("testforbidsystempaths4", build.WithDockerfile(fmt.Sprintf(dockerfile, "c:\\\\wInDows"))).Assert(c, exp)
 }
 
-func (s *DockerSuite) TestBuildCopyFromForbidWindowsRelativePaths(c *check.C) {
+func (s *DockerSuite) TestBuildCopyFromForbidWindowsRelativePaths(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	dockerfile := `
 		FROM ` + testEnv.PlatformDefaults.BaseImage + `
@@ -5915,7 +5896,7 @@ func (s *DockerSuite) TestBuildCopyFromForbidWindowsRelativePaths(c *check.C) {
 	buildImage("testforbidsystempaths5", build.WithDockerfile(fmt.Sprintf(dockerfile, "\\\\windows"))).Assert(c, exp)
 }
 
-func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *check.C) {
+func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	dockerfile := `
 		FROM ` + testEnv.PlatformDefaults.BaseImage + `
@@ -5934,7 +5915,7 @@ func (s *DockerSuite) TestBuildCopyFromWindowsIsCaseInsensitive(c *check.C) {
 }
 
 // #33176
-func (s *DockerSuite) TestBuildMulitStageResetScratch(c *check.C) {
+func (s *DockerSuite) TestBuildMultiStageResetScratch(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	dockerfile := `
@@ -5951,10 +5932,10 @@ func (s *DockerSuite) TestBuildMulitStageResetScratch(c *check.C) {
 	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx))
 
 	res := cli.InspectCmd(c, "build1", cli.Format(".Config.WorkingDir")).Combined()
-	c.Assert(strings.TrimSpace(res), checker.Equals, "")
+	assert.Equal(c, strings.TrimSpace(res), "")
 }
 
-func (s *DockerSuite) TestBuildIntermediateTarget(c *check.C) {
+func (s *DockerSuite) TestBuildIntermediateTarget(c *testing.T) {
 	//todo: need to be removed after 18.06 release
 	if strings.Contains(testEnv.DaemonInfo.ServerVersion, "18.05.0") {
 		c.Skip(fmt.Sprintf("Bug fixed in 18.06 or higher.Skipping it for %s", testEnv.DaemonInfo.ServerVersion))
@@ -5972,14 +5953,14 @@ func (s *DockerSuite) TestBuildIntermediateTarget(c *check.C) {
 		cli.WithFlags("--target", "build-env"))
 
 	res := cli.InspectCmd(c, "build1", cli.Format("json .Config.Cmd")).Combined()
-	c.Assert(strings.TrimSpace(res), checker.Equals, `["/dev"]`)
+	assert.Equal(c, strings.TrimSpace(res), `["/dev"]`)
 
 	// Stage name is case-insensitive by design
 	cli.BuildCmd(c, "build1", build.WithExternalBuildContext(ctx),
 		cli.WithFlags("--target", "BUIld-EnV"))
 
 	res = cli.InspectCmd(c, "build1", cli.Format("json .Config.Cmd")).Combined()
-	c.Assert(strings.TrimSpace(res), checker.Equals, `["/dev"]`)
+	assert.Equal(c, strings.TrimSpace(res), `["/dev"]`)
 
 	result := cli.Docker(cli.Build("build1"), build.WithExternalBuildContext(ctx),
 		cli.WithFlags("--target", "nosuchtarget"))
@@ -5992,7 +5973,7 @@ func (s *DockerSuite) TestBuildIntermediateTarget(c *check.C) {
 // TestBuildOpaqueDirectory tests that a build succeeds which
 // creates opaque directories.
 // See https://github.com/docker/docker/issues/25244
-func (s *DockerSuite) TestBuildOpaqueDirectory(c *check.C) {
+func (s *DockerSuite) TestBuildOpaqueDirectory(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerFile := `
 		FROM busybox
@@ -6007,7 +5988,7 @@ func (s *DockerSuite) TestBuildOpaqueDirectory(c *check.C) {
 }
 
 // Windows test for USER in dockerfile
-func (s *DockerSuite) TestBuildWindowsUser(c *check.C) {
+func (s *DockerSuite) TestBuildWindowsUser(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	name := "testbuildwindowsuser"
 	buildImage(name, build.WithDockerfile(`FROM `+testEnv.PlatformDefaults.BaseImage+`
@@ -6024,7 +6005,7 @@ func (s *DockerSuite) TestBuildWindowsUser(c *check.C) {
 // as opposed to the file being copied as a file with the name of the
 // directory. Fix for 27545 (found on Windows, but regression good for Linux too).
 // Note 27545 was reverted in 28505, but a new fix was added subsequently in 28514.
-func (s *DockerSuite) TestBuildCopyFileDotWithWorkdir(c *check.C) {
+func (s *DockerSuite) TestBuildCopyFileDotWithWorkdir(c *testing.T) {
 	name := "testbuildcopyfiledotwithworkdir"
 	buildImageSuccessfully(c, name, build.WithBuildContext(c,
 		build.WithFile("Dockerfile", `FROM busybox
@@ -6037,7 +6018,7 @@ RUN ["cat", "/foo/file"]
 }
 
 // Case-insensitive environment variables on Windows
-func (s *DockerSuite) TestBuildWindowsEnvCaseInsensitive(c *check.C) {
+func (s *DockerSuite) TestBuildWindowsEnvCaseInsensitive(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	name := "testbuildwindowsenvcaseinsensitive"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`
@@ -6051,20 +6032,14 @@ func (s *DockerSuite) TestBuildWindowsEnvCaseInsensitive(c *check.C) {
 }
 
 // Test case for 29667
-func (s *DockerSuite) TestBuildWorkdirImageCmd(c *check.C) {
+func (s *DockerSuite) TestBuildWorkdirImageCmd(c *testing.T) {
 	image := "testworkdirimagecmd"
 	buildImageSuccessfully(c, image, build.WithDockerfile(`
 FROM busybox
 WORKDIR /foo/bar
 `))
 	out, _ := dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image)
-
-	// The Windows busybox image has a blank `cmd`
-	lookingFor := `["sh"]`
-	if testEnv.OSType == "windows" {
-		lookingFor = "null"
-	}
-	c.Assert(strings.TrimSpace(out), checker.Equals, lookingFor)
+	assert.Equal(c, strings.TrimSpace(out), `["sh"]`)
 
 	image = "testworkdirlabelimagecmd"
 	buildImageSuccessfully(c, image, build.WithDockerfile(`
@@ -6074,11 +6049,11 @@ LABEL a=b
 `))
 
 	out, _ = dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image)
-	c.Assert(strings.TrimSpace(out), checker.Equals, lookingFor)
+	assert.Equal(c, strings.TrimSpace(out), `["sh"]`)
 }
 
 // Test case for 28902/28909
-func (s *DockerSuite) TestBuildWorkdirCmd(c *check.C) {
+func (s *DockerSuite) TestBuildWorkdirCmd(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildworkdircmd"
 	dockerFile := `
@@ -6088,22 +6063,22 @@ func (s *DockerSuite) TestBuildWorkdirCmd(c *check.C) {
 	buildImageSuccessfully(c, name, build.WithDockerfile(dockerFile))
 	result := buildImage(name, build.WithDockerfile(dockerFile))
 	result.Assert(c, icmd.Success)
-	c.Assert(strings.Count(result.Combined(), "Using cache"), checker.Equals, 1)
+	assert.Equal(c, strings.Count(result.Combined(), "Using cache"), 1)
 }
 
 // FIXME(vdemeester) should be a unit test
-func (s *DockerSuite) TestBuildLineErrorOnBuild(c *check.C) {
+func (s *DockerSuite) TestBuildLineErrorOnBuild(c *testing.T) {
 	name := "test_build_line_error_onbuild"
 	buildImage(name, build.WithDockerfile(`FROM busybox
   ONBUILD
   `)).Assert(c, icmd.Expected{
 		ExitCode: 1,
-		Err:      "Dockerfile parse error line 2: ONBUILD requires at least one argument",
+		Err:      "parse error line 2: ONBUILD requires at least one argument",
 	})
 }
 
 // FIXME(vdemeester) should be a unit test
-func (s *DockerSuite) TestBuildLineErrorUnknownInstruction(c *check.C) {
+func (s *DockerSuite) TestBuildLineErrorUnknownInstruction(c *testing.T) {
 	name := "test_build_line_error_unknown_instruction"
 	cli.Docker(cli.Build(name), build.WithDockerfile(`FROM busybox
   RUN echo hello world
@@ -6112,12 +6087,12 @@ func (s *DockerSuite) TestBuildLineErrorUnknownInstruction(c *check.C) {
   ERROR
   `)).Assert(c, icmd.Expected{
 		ExitCode: 1,
-		Err:      "Dockerfile parse error line 3: unknown instruction: NOINSTRUCTION",
+		Err:      "parse error line 3: unknown instruction: NOINSTRUCTION",
 	})
 }
 
 // FIXME(vdemeester) should be a unit test
-func (s *DockerSuite) TestBuildLineErrorWithEmptyLines(c *check.C) {
+func (s *DockerSuite) TestBuildLineErrorWithEmptyLines(c *testing.T) {
 	name := "test_build_line_error_with_empty_lines"
 	cli.Docker(cli.Build(name), build.WithDockerfile(`
   FROM busybox
@@ -6129,12 +6104,12 @@ func (s *DockerSuite) TestBuildLineErrorWithEmptyLines(c *check.C) {
   CMD ["/bin/init"]
   `)).Assert(c, icmd.Expected{
 		ExitCode: 1,
-		Err:      "Dockerfile parse error line 6: unknown instruction: NOINSTRUCTION",
+		Err:      "parse error line 6: unknown instruction: NOINSTRUCTION",
 	})
 }
 
 // FIXME(vdemeester) should be a unit test
-func (s *DockerSuite) TestBuildLineErrorWithComments(c *check.C) {
+func (s *DockerSuite) TestBuildLineErrorWithComments(c *testing.T) {
 	name := "test_build_line_error_with_comments"
 	cli.Docker(cli.Build(name), build.WithDockerfile(`FROM busybox
   # This will print hello world
@@ -6143,12 +6118,12 @@ func (s *DockerSuite) TestBuildLineErrorWithComments(c *check.C) {
   NOINSTRUCTION echo ba
   `)).Assert(c, icmd.Expected{
 		ExitCode: 1,
-		Err:      "Dockerfile parse error line 5: unknown instruction: NOINSTRUCTION",
+		Err:      "parse error line 5: unknown instruction: NOINSTRUCTION",
 	})
 }
 
 // #31957
-func (s *DockerSuite) TestBuildSetCommandWithDefinedShell(c *check.C) {
+func (s *DockerSuite) TestBuildSetCommandWithDefinedShell(c *testing.T) {
 	buildImageSuccessfully(c, "build1", build.WithDockerfile(`
 FROM busybox
 SHELL ["/bin/sh", "-c"]
@@ -6159,12 +6134,16 @@ CMD echo foo
 `))
 
 	out, _ := dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", "build2")
-	c.Assert(strings.TrimSpace(out), checker.Equals, `["/bin/sh","-c","echo foo"]`)
+	expected := `["/bin/sh","-c","echo foo"]`
+	if testEnv.OSType == "windows" {
+		expected = `["/bin/sh -c echo foo"]`
+	}
+	assert.Equal(c, strings.TrimSpace(out), expected)
 }
 
 // FIXME(vdemeester) should migrate to docker/cli tests
-func (s *DockerSuite) TestBuildIidFile(c *check.C) {
-	tmpDir, err := ioutil.TempDir("", "TestBuildIidFile")
+func (s *DockerSuite) TestBuildIidFile(c *testing.T) {
+	tmpDir, err := os.MkdirTemp("", "TestBuildIidFile")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -6180,24 +6159,24 @@ FROM `+minimalBaseImage()+`
 ENV BAR BAZ`),
 		cli.WithFlags("--iidfile", tmpIidFile))
 
-	id, err := ioutil.ReadFile(tmpIidFile)
-	c.Assert(err, check.IsNil)
+	id, err := os.ReadFile(tmpIidFile)
+	assert.NilError(c, err)
 	d, err := digest.Parse(string(id))
-	c.Assert(err, check.IsNil)
-	c.Assert(d.String(), checker.Equals, getIDByName(c, name))
+	assert.NilError(c, err)
+	assert.Equal(c, d.String(), getIDByName(c, name))
 }
 
 // FIXME(vdemeester) should migrate to docker/cli tests
-func (s *DockerSuite) TestBuildIidFileCleanupOnFail(c *check.C) {
-	tmpDir, err := ioutil.TempDir("", "TestBuildIidFileCleanupOnFail")
+func (s *DockerSuite) TestBuildIidFileCleanupOnFail(c *testing.T) {
+	tmpDir, err := os.MkdirTemp("", "TestBuildIidFileCleanupOnFail")
 	if err != nil {
 		c.Fatal(err)
 	}
 	defer os.RemoveAll(tmpDir)
 	tmpIidFile := filepath.Join(tmpDir, "iid")
 
-	err = ioutil.WriteFile(tmpIidFile, []byte("Dummy"), 0666)
-	c.Assert(err, check.IsNil)
+	err = os.WriteFile(tmpIidFile, []byte("Dummy"), 0666)
+	assert.NilError(c, err)
 
 	cli.Docker(cli.Build("testbuildiidfilecleanuponfail"),
 		build.WithDockerfile(`FROM `+minimalBaseImage()+`
@@ -6206,6 +6185,6 @@ func (s *DockerSuite) TestBuildIidFileCleanupOnFail(c *check.C) {
 		ExitCode: 1,
 	})
 	_, err = os.Stat(tmpIidFile)
-	c.Assert(err, check.NotNil)
-	c.Assert(os.IsNotExist(err), check.Equals, true)
+	assert.ErrorContains(c, err, "")
+	assert.Equal(c, os.IsNotExist(err), true)
 }
diff --git a/integration-cli/docker_cli_build_unix_test.go b/integration-cli/docker_cli_build_unix_test.go
index 8cad28f457371..92e51b611a6c7 100644
--- a/integration-cli/docker_cli_build_unix_test.go
+++ b/integration-cli/docker_cli_build_unix_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -6,25 +7,24 @@ import (
 	"bufio"
 	"bytes"
 	"encoding/json"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"regexp"
 	"strings"
 	"syscall"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/docker/docker/internal/test/fakecontext"
-	"github.com/docker/go-units"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"github.com/docker/docker/testutil/fakecontext"
+	units "github.com/docker/go-units"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
-func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) {
+func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *testing.T) {
 	testRequires(c, cpuCfsQuota)
 	name := "testbuildresourceconstraints"
 	buildLabel := "DockerSuite.TestBuildResourceConstraintsAreUsed"
@@ -55,16 +55,16 @@ func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) {
 
 	var c1 hostConfig
 	err := json.Unmarshal([]byte(cfg), &c1)
-	c.Assert(err, checker.IsNil, check.Commentf(cfg))
+	assert.Assert(c, err == nil, cfg)
 
-	c.Assert(c1.Memory, checker.Equals, int64(64*1024*1024), check.Commentf("resource constraints not set properly for Memory"))
-	c.Assert(c1.MemorySwap, checker.Equals, int64(-1), check.Commentf("resource constraints not set properly for MemorySwap"))
-	c.Assert(c1.CpusetCpus, checker.Equals, "0", check.Commentf("resource constraints not set properly for CpusetCpus"))
-	c.Assert(c1.CpusetMems, checker.Equals, "0", check.Commentf("resource constraints not set properly for CpusetMems"))
-	c.Assert(c1.CPUShares, checker.Equals, int64(100), check.Commentf("resource constraints not set properly for CPUShares"))
-	c.Assert(c1.CPUQuota, checker.Equals, int64(8000), check.Commentf("resource constraints not set properly for CPUQuota"))
-	c.Assert(c1.Ulimits[0].Name, checker.Equals, "nofile", check.Commentf("resource constraints not set properly for Ulimits"))
-	c.Assert(c1.Ulimits[0].Hard, checker.Equals, int64(42), check.Commentf("resource constraints not set properly for Ulimits"))
+	assert.Equal(c, c1.Memory, int64(64*1024*1024), "resource constraints not set properly for Memory")
+	assert.Equal(c, c1.MemorySwap, int64(-1), "resource constraints not set properly for MemorySwap")
+	assert.Equal(c, c1.CpusetCpus, "0", "resource constraints not set properly for CpusetCpus")
+	assert.Equal(c, c1.CpusetMems, "0", "resource constraints not set properly for CpusetMems")
+	assert.Equal(c, c1.CPUShares, int64(100), "resource constraints not set properly for CPUShares")
+	assert.Equal(c, c1.CPUQuota, int64(8000), "resource constraints not set properly for CPUQuota")
+	assert.Equal(c, c1.Ulimits[0].Name, "nofile", "resource constraints not set properly for Ulimits")
+	assert.Equal(c, c1.Ulimits[0].Hard, int64(42), "resource constraints not set properly for Ulimits")
 
 	// Make sure constraints aren't saved to image
 	cli.DockerCmd(c, "run", "--name=test", name)
@@ -73,18 +73,18 @@ func (s *DockerSuite) TestBuildResourceConstraintsAreUsed(c *check.C) {
 
 	var c2 hostConfig
 	err = json.Unmarshal([]byte(cfg), &c2)
-	c.Assert(err, checker.IsNil, check.Commentf(cfg))
-
-	c.Assert(c2.Memory, check.Not(checker.Equals), int64(64*1024*1024), check.Commentf("resource leaked from build for Memory"))
-	c.Assert(c2.MemorySwap, check.Not(checker.Equals), int64(-1), check.Commentf("resource leaked from build for MemorySwap"))
-	c.Assert(c2.CpusetCpus, check.Not(checker.Equals), "0", check.Commentf("resource leaked from build for CpusetCpus"))
-	c.Assert(c2.CpusetMems, check.Not(checker.Equals), "0", check.Commentf("resource leaked from build for CpusetMems"))
-	c.Assert(c2.CPUShares, check.Not(checker.Equals), int64(100), check.Commentf("resource leaked from build for CPUShares"))
-	c.Assert(c2.CPUQuota, check.Not(checker.Equals), int64(8000), check.Commentf("resource leaked from build for CPUQuota"))
-	c.Assert(c2.Ulimits, checker.IsNil, check.Commentf("resource leaked from build for Ulimits"))
+	assert.Assert(c, err == nil, cfg)
+
+	assert.Assert(c, c2.Memory != int64(64*1024*1024), "resource leaked from build for Memory")
+	assert.Assert(c, c2.MemorySwap != int64(-1), "resource leaked from build for MemorySwap")
+	assert.Assert(c, c2.CpusetCpus != "0", "resource leaked from build for CpusetCpus")
+	assert.Assert(c, c2.CpusetMems != "0", "resource leaked from build for CpusetMems")
+	assert.Assert(c, c2.CPUShares != int64(100), "resource leaked from build for CPUShares")
+	assert.Assert(c, c2.CPUQuota != int64(8000), "resource leaked from build for CPUQuota")
+	assert.Assert(c, c2.Ulimits == nil, "resource leaked from build for Ulimits")
 }
 
-func (s *DockerSuite) TestBuildAddChangeOwnership(c *check.C) {
+func (s *DockerSuite) TestBuildAddChangeOwnership(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testbuildaddown"
 
@@ -95,8 +95,8 @@ func (s *DockerSuite) TestBuildAddChangeOwnership(c *check.C) {
 			RUN [ $(stat -c %U:%G "/bar") = 'root:root' ]
 			RUN [ $(stat -c %U:%G "/bar/foo") = 'root:root' ]
 			`
-		tmpDir, err := ioutil.TempDir("", "fake-context")
-		c.Assert(err, check.IsNil)
+		tmpDir, err := os.MkdirTemp("", "fake-context")
+		assert.NilError(c, err)
 		testFile, err := os.Create(filepath.Join(tmpDir, "foo"))
 		if err != nil {
 			c.Fatalf("failed to create foo file: %v", err)
@@ -108,7 +108,7 @@ func (s *DockerSuite) TestBuildAddChangeOwnership(c *check.C) {
 			Dir:     tmpDir,
 		}).Assert(c, icmd.Success)
 
-		if err := ioutil.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
+		if err := os.WriteFile(filepath.Join(tmpDir, "Dockerfile"), []byte(dockerfile), 0644); err != nil {
 			c.Fatalf("failed to open destination dockerfile: %v", err)
 		}
 		return fakecontext.New(c, tmpDir)
@@ -130,14 +130,14 @@ func (s *DockerSuite) TestBuildAddChangeOwnership(c *check.C) {
 // TODO(buildkit): this test needs to be rewritten for buildkit.
 // It has been manually tested positive. Confirmed issue: docker build output parsing.
 // Potential issue: newEventObserver uses docker events, which is not hooked up to buildkit.
-func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) {
+func (s *DockerSuite) TestBuildCancellationKillsSleep(c *testing.T) {
 	testRequires(c, DaemonIsLinux, TODOBuildkit)
 	name := "testbuildcancellation"
 
 	observer, err := newEventObserver(c)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	err = observer.Start()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer observer.Stop()
 
 	// (Note: one year, will never finish)
@@ -148,7 +148,7 @@ func (s *DockerSuite) TestBuildCancellationKillsSleep(c *check.C) {
 	buildCmd.Dir = ctx.Dir
 
 	stdoutBuild, err := buildCmd.StdoutPipe()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	if err := buildCmd.Start(); err != nil {
 		c.Fatalf("failed to run build: %s", err)
diff --git a/integration-cli/docker_cli_by_digest_test.go b/integration-cli/docker_cli_by_digest_test.go
index 006cf11e1a634..34552253c91d9 100644
--- a/integration-cli/docker_cli_by_digest_test.go
+++ b/integration-cli/docker_cli_by_digest_test.go
@@ -7,31 +7,30 @@ import (
 	"path/filepath"
 	"regexp"
 	"strings"
+	"testing"
 
 	"github.com/docker/distribution/manifest/schema1"
 	"github.com/docker/distribution/manifest/schema2"
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
-	"github.com/opencontainers/go-digest"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
+	digest "github.com/opencontainers/go-digest"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 var (
 	remoteRepoName  = "dockercli/busybox-by-dgst"
 	repoName        = fmt.Sprintf("%s/%s", privateRegistryURL, remoteRepoName)
-	pushDigestRegex = regexp.MustCompile("[\\S]+: digest: ([\\S]+) size: [0-9]+")
-	digestRegex     = regexp.MustCompile("Digest: ([\\S]+)")
+	pushDigestRegex = regexp.MustCompile(`[\S]+: digest: ([\S]+) size: [0-9]+`)
+	digestRegex     = regexp.MustCompile(`Digest: ([\S]+)`)
 )
 
-func setupImage(c *check.C) (digest.Digest, error) {
+func setupImage(c *testing.T) (digest.Digest, error) {
 	return setupImageWithTag(c, "latest")
 }
 
-func setupImageWithTag(c *check.C, tag string) (digest.Digest, error) {
+func setupImageWithTag(c *testing.T, tag string) (digest.Digest, error) {
 	containerName := "busyboxbydigest"
 
 	// new file is committed because this layer is used for detecting malicious
@@ -53,41 +52,41 @@ func setupImageWithTag(c *check.C, tag string) (digest.Digest, error) {
 	cli.DockerCmd(c, "rmi", repoAndTag)
 
 	matches := pushDigestRegex.FindStringSubmatch(out)
-	c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from push output: %s", out))
+	assert.Equal(c, len(matches), 2, "unable to parse digest from push output: %s", out)
 	pushDigest := matches[1]
 
 	return digest.Digest(pushDigest), nil
 }
 
-func testPullByTagDisplaysDigest(c *check.C) {
+func testPullByTagDisplaysDigest(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	pushDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	// pull from the registry using the tag
 	out, _ := dockerCmd(c, "pull", repoName)
 
 	// the pull output includes "Digest: ", so find that
 	matches := digestRegex.FindStringSubmatch(out)
-	c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out))
+	assert.Equal(c, len(matches), 2, "unable to parse digest from push output: %s", out)
 	pullDigest := matches[1]
 
 	// make sure the pushed and pull digests match
-	c.Assert(pushDigest.String(), checker.Equals, pullDigest)
+	assert.Equal(c, pushDigest.String(), pullDigest)
 }
 
-func (s *DockerRegistrySuite) TestPullByTagDisplaysDigest(c *check.C) {
+func (s *DockerRegistrySuite) TestPullByTagDisplaysDigest(c *testing.T) {
 	testPullByTagDisplaysDigest(c)
 }
 
-func (s *DockerSchema1RegistrySuite) TestPullByTagDisplaysDigest(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestPullByTagDisplaysDigest(c *testing.T) {
 	testPullByTagDisplaysDigest(c)
 }
 
-func testPullByDigest(c *check.C) {
+func testPullByDigest(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	pushDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	// pull from the registry using the @ reference
 	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
@@ -95,41 +94,41 @@ func testPullByDigest(c *check.C) {
 
 	// the pull output includes "Digest: ", so find that
 	matches := digestRegex.FindStringSubmatch(out)
-	c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out))
+	assert.Equal(c, len(matches), 2, "unable to parse digest from push output: %s", out)
 	pullDigest := matches[1]
 
 	// make sure the pushed and pull digests match
-	c.Assert(pushDigest.String(), checker.Equals, pullDigest)
+	assert.Equal(c, pushDigest.String(), pullDigest)
 }
 
-func (s *DockerRegistrySuite) TestPullByDigest(c *check.C) {
+func (s *DockerRegistrySuite) TestPullByDigest(c *testing.T) {
 	testPullByDigest(c)
 }
 
-func (s *DockerSchema1RegistrySuite) TestPullByDigest(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestPullByDigest(c *testing.T) {
 	testPullByDigest(c)
 }
 
-func testPullByDigestNoFallback(c *check.C) {
+func testPullByDigestNoFallback(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	// pull from the registry using the @ reference
 	imageReference := fmt.Sprintf("%s@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", repoName)
 	out, _, err := dockerCmdWithError("pull", imageReference)
-	c.Assert(err, checker.NotNil, check.Commentf("expected non-zero exit status and correct error message when pulling non-existing image"))
-	c.Assert(out, checker.Contains, fmt.Sprintf("manifest for %s not found", imageReference), check.Commentf("expected non-zero exit status and correct error message when pulling non-existing image"))
+	assert.Assert(c, err != nil, "expected non-zero exit status and correct error message when pulling non-existing image")
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("manifest for %s not found", imageReference)), "expected non-zero exit status and correct error message when pulling non-existing image")
 }
 
-func (s *DockerRegistrySuite) TestPullByDigestNoFallback(c *check.C) {
+func (s *DockerRegistrySuite) TestPullByDigestNoFallback(c *testing.T) {
 	testPullByDigestNoFallback(c)
 }
 
-func (s *DockerSchema1RegistrySuite) TestPullByDigestNoFallback(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestPullByDigestNoFallback(c *testing.T) {
 	testPullByDigestNoFallback(c)
 }
 
-func (s *DockerRegistrySuite) TestCreateByDigest(c *check.C) {
+func (s *DockerRegistrySuite) TestCreateByDigest(c *testing.T) {
 	pushDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
 
@@ -137,12 +136,12 @@ func (s *DockerRegistrySuite) TestCreateByDigest(c *check.C) {
 	dockerCmd(c, "create", "--name", containerName, imageReference)
 
 	res := inspectField(c, containerName, "Config.Image")
-	c.Assert(res, checker.Equals, imageReference)
+	assert.Equal(c, res, imageReference)
 }
 
-func (s *DockerRegistrySuite) TestRunByDigest(c *check.C) {
+func (s *DockerRegistrySuite) TestRunByDigest(c *testing.T) {
 	pushDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
 
@@ -151,16 +150,16 @@ func (s *DockerRegistrySuite) TestRunByDigest(c *check.C) {
 
 	foundRegex := regexp.MustCompile("found=([^\n]+)")
 	matches := foundRegex.FindStringSubmatch(out)
-	c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out))
-	c.Assert(matches[1], checker.Equals, "1", check.Commentf("Expected %q, got %q", "1", matches[1]))
+	assert.Equal(c, len(matches), 2, fmt.Sprintf("unable to parse digest from pull output: %s", out))
+	assert.Equal(c, matches[1], "1", fmt.Sprintf("Expected %q, got %q", "1", matches[1]))
 
 	res := inspectField(c, containerName, "Config.Image")
-	c.Assert(res, checker.Equals, imageReference)
+	assert.Equal(c, res, imageReference)
 }
 
-func (s *DockerRegistrySuite) TestRemoveImageByDigest(c *check.C) {
+func (s *DockerRegistrySuite) TestRemoveImageByDigest(c *testing.T) {
 	digest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
 
@@ -172,18 +171,17 @@ func (s *DockerRegistrySuite) TestRemoveImageByDigest(c *check.C) {
 
 	// do the delete
 	err = deleteImages(imageReference)
-	c.Assert(err, checker.IsNil, check.Commentf("unexpected error deleting image"))
+	assert.NilError(c, err, "unexpected error deleting image")
 
 	// try to inspect again - it should error this time
 	_, err = inspectFieldWithError(imageReference, "Id")
-	//unexpected nil err trying to inspect what should be a non-existent image
-	c.Assert(err, checker.NotNil)
-	c.Assert(err.Error(), checker.Contains, "No such object")
+	// unexpected nil err trying to inspect what should be a non-existent image
+	assert.ErrorContains(c, err, "No such object")
 }
 
-func (s *DockerRegistrySuite) TestBuildByDigest(c *check.C) {
+func (s *DockerRegistrySuite) TestBuildByDigest(c *testing.T) {
 	digest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
 
@@ -198,17 +196,17 @@ func (s *DockerRegistrySuite) TestBuildByDigest(c *check.C) {
 	buildImageSuccessfully(c, name, build.WithDockerfile(fmt.Sprintf(
 		`FROM %s
      CMD ["/bin/echo", "Hello World"]`, imageReference)))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// get the build's image id
 	res := inspectField(c, name, "Config.Image")
 	// make sure they match
-	c.Assert(res, checker.Equals, imageID)
+	assert.Equal(c, res, imageID)
 }
 
-func (s *DockerRegistrySuite) TestTagByDigest(c *check.C) {
+func (s *DockerRegistrySuite) TestTagByDigest(c *testing.T) {
 	digest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
 
@@ -222,12 +220,12 @@ func (s *DockerRegistrySuite) TestTagByDigest(c *check.C) {
 	expectedID := inspectField(c, imageReference, "Id")
 
 	tagID := inspectField(c, tag, "Id")
-	c.Assert(tagID, checker.Equals, expectedID)
+	assert.Equal(c, tagID, expectedID)
 }
 
-func (s *DockerRegistrySuite) TestListImagesWithoutDigests(c *check.C) {
+func (s *DockerRegistrySuite) TestListImagesWithoutDigests(c *testing.T) {
 	digest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
 
@@ -235,14 +233,14 @@ func (s *DockerRegistrySuite) TestListImagesWithoutDigests(c *check.C) {
 	dockerCmd(c, "pull", imageReference)
 
 	out, _ := dockerCmd(c, "images")
-	c.Assert(out, checker.Not(checker.Contains), "DIGEST", check.Commentf("list output should not have contained DIGEST header"))
+	assert.Assert(c, !strings.Contains(out, "DIGEST"), "list output should not have contained DIGEST header")
 }
 
-func (s *DockerRegistrySuite) TestListImagesWithDigests(c *check.C) {
+func (s *DockerRegistrySuite) TestListImagesWithDigests(c *testing.T) {
 
 	// setup image1
 	digest1, err := setupImageWithTag(c, "tag1")
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 	imageReference1 := fmt.Sprintf("%s@%s", repoName, digest1)
 	c.Logf("imageReference1 = %s", imageReference1)
 
@@ -254,11 +252,10 @@ func (s *DockerRegistrySuite) TestListImagesWithDigests(c *check.C) {
 
 	// make sure repo shown, tag=, digest = $digest1
 	re1 := regexp.MustCompile(`\s*` + repoName + `\s*\s*` + digest1.String() + `\s`)
-	c.Assert(re1.MatchString(out), checker.True, check.Commentf("expected %q: %s", re1.String(), out))
+	assert.Assert(c, re1.MatchString(out), "expected %q: %s", re1.String(), out)
 	// setup image2
 	digest2, err := setupImageWithTag(c, "tag2")
-	//error setting up image
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err, "error setting up image")
 	imageReference2 := fmt.Sprintf("%s@%s", repoName, digest2)
 	c.Logf("imageReference2 = %s", imageReference2)
 
@@ -272,11 +269,11 @@ func (s *DockerRegistrySuite) TestListImagesWithDigests(c *check.C) {
 	out, _ = dockerCmd(c, "images", "--digests")
 
 	// make sure repo shown, tag=, digest = $digest1
-	c.Assert(re1.MatchString(out), checker.True, check.Commentf("expected %q: %s", re1.String(), out))
+	assert.Assert(c, re1.MatchString(out), "expected %q: %s", re1.String(), out)
 
 	// make sure repo shown, tag=, digest = $digest2
 	re2 := regexp.MustCompile(`\s*` + repoName + `\s*\s*` + digest2.String() + `\s`)
-	c.Assert(re2.MatchString(out), checker.True, check.Commentf("expected %q: %s", re2.String(), out))
+	assert.Assert(c, re2.MatchString(out), "expected %q: %s", re2.String(), out)
 
 	// pull tag1
 	dockerCmd(c, "pull", repoName+":tag1")
@@ -286,9 +283,9 @@ func (s *DockerRegistrySuite) TestListImagesWithDigests(c *check.C) {
 
 	// make sure image 1 has repo, tag,  AND repo, , digest
 	reWithDigest1 := regexp.MustCompile(`\s*` + repoName + `\s*tag1\s*` + digest1.String() + `\s`)
-	c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
+	assert.Assert(c, reWithDigest1.MatchString(out), "expected %q: %s", reWithDigest1.String(), out)
 	// make sure image 2 has repo, , digest
-	c.Assert(re2.MatchString(out), checker.True, check.Commentf("expected %q: %s", re2.String(), out))
+	assert.Assert(c, re2.MatchString(out), "expected %q: %s", re2.String(), out)
 
 	// pull tag 2
 	dockerCmd(c, "pull", repoName+":tag2")
@@ -297,28 +294,28 @@ func (s *DockerRegistrySuite) TestListImagesWithDigests(c *check.C) {
 	out, _ = dockerCmd(c, "images", "--digests")
 
 	// make sure image 1 has repo, tag, digest
-	c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
+	assert.Assert(c, reWithDigest1.MatchString(out), "expected %q: %s", reWithDigest1.String(), out)
 
 	// make sure image 2 has repo, tag, digest
 	reWithDigest2 := regexp.MustCompile(`\s*` + repoName + `\s*tag2\s*` + digest2.String() + `\s`)
-	c.Assert(reWithDigest2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest2.String(), out))
+	assert.Assert(c, reWithDigest2.MatchString(out), "expected %q: %s", reWithDigest2.String(), out)
 
 	// list images
 	out, _ = dockerCmd(c, "images", "--digests")
 
 	// make sure image 1 has repo, tag, digest
-	c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
+	assert.Assert(c, reWithDigest1.MatchString(out), "expected %q: %s", reWithDigest1.String(), out)
 	// make sure image 2 has repo, tag, digest
-	c.Assert(reWithDigest2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest2.String(), out))
+	assert.Assert(c, reWithDigest2.MatchString(out), "expected %q: %s", reWithDigest2.String(), out)
 	// make sure busybox has tag, but not digest
 	busyboxRe := regexp.MustCompile(`\s*busybox\s*latest\s*\s`)
-	c.Assert(busyboxRe.MatchString(out), checker.True, check.Commentf("expected %q: %s", busyboxRe.String(), out))
+	assert.Assert(c, busyboxRe.MatchString(out), "expected %q: %s", busyboxRe.String(), out)
 }
 
-func (s *DockerRegistrySuite) TestListDanglingImagesWithDigests(c *check.C) {
+func (s *DockerRegistrySuite) TestListDanglingImagesWithDigests(c *testing.T) {
 	// setup image1
 	digest1, err := setupImageWithTag(c, "dangle1")
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 	imageReference1 := fmt.Sprintf("%s@%s", repoName, digest1)
 	c.Logf("imageReference1 = %s", imageReference1)
 
@@ -330,11 +327,11 @@ func (s *DockerRegistrySuite) TestListDanglingImagesWithDigests(c *check.C) {
 
 	// make sure repo shown, tag=, digest = $digest1
 	re1 := regexp.MustCompile(`\s*` + repoName + `\s*\s*` + digest1.String() + `\s`)
-	c.Assert(re1.MatchString(out), checker.True, check.Commentf("expected %q: %s", re1.String(), out))
+	assert.Assert(c, re1.MatchString(out), "expected %q: %s", re1.String(), out)
 	// setup image2
 	digest2, err := setupImageWithTag(c, "dangle2")
 	//error setting up image
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	imageReference2 := fmt.Sprintf("%s@%s", repoName, digest2)
 	c.Logf("imageReference2 = %s", imageReference2)
 
@@ -348,11 +345,11 @@ func (s *DockerRegistrySuite) TestListDanglingImagesWithDigests(c *check.C) {
 	out, _ = dockerCmd(c, "images", "--digests", "--filter=dangling=true")
 
 	// make sure repo shown, tag=, digest = $digest1
-	c.Assert(re1.MatchString(out), checker.True, check.Commentf("expected %q: %s", re1.String(), out))
+	assert.Assert(c, re1.MatchString(out), "expected %q: %s", re1.String(), out)
 
 	// make sure repo shown, tag=, digest = $digest2
 	re2 := regexp.MustCompile(`\s*` + repoName + `\s*\s*` + digest2.String() + `\s`)
-	c.Assert(re2.MatchString(out), checker.True, check.Commentf("expected %q: %s", re2.String(), out))
+	assert.Assert(c, re2.MatchString(out), "expected %q: %s", re2.String(), out)
 
 	// pull dangle1 tag
 	dockerCmd(c, "pull", repoName+":dangle1")
@@ -362,9 +359,9 @@ func (s *DockerRegistrySuite) TestListDanglingImagesWithDigests(c *check.C) {
 
 	// make sure image 1 has repo, tag,  AND repo, , digest
 	reWithDigest1 := regexp.MustCompile(`\s*` + repoName + `\s*dangle1\s*` + digest1.String() + `\s`)
-	c.Assert(reWithDigest1.MatchString(out), checker.False, check.Commentf("unexpected %q: %s", reWithDigest1.String(), out))
+	assert.Assert(c, !reWithDigest1.MatchString(out), "unexpected %q: %s", reWithDigest1.String(), out)
 	// make sure image 2 has repo, , digest
-	c.Assert(re2.MatchString(out), checker.True, check.Commentf("expected %q: %s", re2.String(), out))
+	assert.Assert(c, re2.MatchString(out), "expected %q: %s", re2.String(), out)
 
 	// pull dangle2 tag
 	dockerCmd(c, "pull", repoName+":dangle2")
@@ -373,24 +370,24 @@ func (s *DockerRegistrySuite) TestListDanglingImagesWithDigests(c *check.C) {
 	out, _ = dockerCmd(c, "images", "--digests")
 
 	// make sure image 1 has repo, tag, digest
-	c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
+	assert.Assert(c, reWithDigest1.MatchString(out), "expected %q: %s", reWithDigest1.String(), out)
 
 	// make sure image 2 has repo, tag, digest
 	reWithDigest2 := regexp.MustCompile(`\s*` + repoName + `\s*dangle2\s*` + digest2.String() + `\s`)
-	c.Assert(reWithDigest2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest2.String(), out))
+	assert.Assert(c, reWithDigest2.MatchString(out), "expected %q: %s", reWithDigest2.String(), out)
 
 	// list images, no longer dangling, should not match
 	out, _ = dockerCmd(c, "images", "--digests", "--filter=dangling=true")
 
 	// make sure image 1 has repo, tag, digest
-	c.Assert(reWithDigest1.MatchString(out), checker.False, check.Commentf("unexpected %q: %s", reWithDigest1.String(), out))
+	assert.Assert(c, !reWithDigest1.MatchString(out), "unexpected %q: %s", reWithDigest1.String(), out)
 	// make sure image 2 has repo, tag, digest
-	c.Assert(reWithDigest2.MatchString(out), checker.False, check.Commentf("unexpected %q: %s", reWithDigest2.String(), out))
+	assert.Assert(c, !reWithDigest2.MatchString(out), "unexpected %q: %s", reWithDigest2.String(), out)
 }
 
-func (s *DockerRegistrySuite) TestInspectImageWithDigests(c *check.C) {
+func (s *DockerRegistrySuite) TestInspectImageWithDigests(c *testing.T) {
 	digest, err := setupImage(c)
-	c.Assert(err, check.IsNil, check.Commentf("error setting up image"))
+	assert.Assert(c, err == nil, "error setting up image")
 
 	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
 
@@ -401,17 +398,17 @@ func (s *DockerRegistrySuite) TestInspectImageWithDigests(c *check.C) {
 
 	var imageJSON []types.ImageInspect
 	err = json.Unmarshal([]byte(out), &imageJSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(imageJSON, checker.HasLen, 1)
-	c.Assert(imageJSON[0].RepoDigests, checker.HasLen, 1)
+	assert.NilError(c, err)
+	assert.Equal(c, len(imageJSON), 1)
+	assert.Equal(c, len(imageJSON[0].RepoDigests), 1)
 	assert.Check(c, is.Contains(imageJSON[0].RepoDigests, imageReference))
 }
 
-func (s *DockerRegistrySuite) TestPsListContainersFilterAncestorImageByDigest(c *check.C) {
+func (s *DockerRegistrySuite) TestPsListContainersFilterAncestorImageByDigest(c *testing.T) {
 	existingContainers := ExistingContainerIDs(c)
 
 	digest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	imageReference := fmt.Sprintf("%s@%s", repoName, digest)
 
@@ -436,17 +433,16 @@ func (s *DockerRegistrySuite) TestPsListContainersFilterAncestorImageByDigest(c
 
 	// Invalid imageReference
 	out, _ := dockerCmd(c, "ps", "-a", "-q", "--no-trunc", fmt.Sprintf("--filter=ancestor=busybox@%s", digest))
-	// Filter container for ancestor filter should be empty
-	c.Assert(strings.TrimSpace(out), checker.Equals, "")
+	assert.Equal(c, strings.TrimSpace(out), "", "Filter container for ancestor filter should be empty")
 
 	// Valid imageReference
 	out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=ancestor="+imageReference)
 	checkPsAncestorFilterOutput(c, RemoveOutputForExistingElements(out, existingContainers), imageReference, expectedIDs)
 }
 
-func (s *DockerRegistrySuite) TestDeleteImageByIDOnlyPulledByDigest(c *check.C) {
+func (s *DockerRegistrySuite) TestDeleteImageByIDOnlyPulledByDigest(c *testing.T) {
 	pushDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	// pull from the registry using the @ reference
 	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
@@ -460,12 +456,12 @@ func (s *DockerRegistrySuite) TestDeleteImageByIDOnlyPulledByDigest(c *check.C)
 	dockerCmd(c, "rmi", imageID)
 
 	_, err = inspectFieldWithError(imageID, "Id")
-	c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted"))
+	assert.ErrorContains(c, err, "", "image should have been deleted")
 }
 
-func (s *DockerRegistrySuite) TestDeleteImageWithDigestAndTag(c *check.C) {
+func (s *DockerRegistrySuite) TestDeleteImageWithDigestAndTag(c *testing.T) {
 	pushDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	// pull from the registry using the @ reference
 	imageReference := fmt.Sprintf("%s@%s", repoName, pushDigest)
@@ -487,12 +483,12 @@ func (s *DockerRegistrySuite) TestDeleteImageWithDigestAndTag(c *check.C) {
 
 	// rmi should have deleted the tag, the digest reference, and the image itself
 	_, err = inspectFieldWithError(imageID, "Id")
-	c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted"))
+	assert.ErrorContains(c, err, "", "image should have been deleted")
 }
 
-func (s *DockerRegistrySuite) TestDeleteImageWithDigestAndMultiRepoTag(c *check.C) {
+func (s *DockerRegistrySuite) TestDeleteImageWithDigestAndMultiRepoTag(c *testing.T) {
 	pushDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	repo2 := fmt.Sprintf("%s/%s", repoName, "repo2")
 
@@ -512,32 +508,32 @@ func (s *DockerRegistrySuite) TestDeleteImageWithDigestAndMultiRepoTag(c *check.
 	// rmi should have deleted repoTag and image reference, but left repoTag2
 	inspectField(c, repoTag2, "Id")
 	_, err = inspectFieldWithError(imageReference, "Id")
-	c.Assert(err, checker.NotNil, check.Commentf("image digest reference should have been removed"))
+	assert.ErrorContains(c, err, "", "image digest reference should have been removed")
 
 	_, err = inspectFieldWithError(repoTag, "Id")
-	c.Assert(err, checker.NotNil, check.Commentf("image tag reference should have been removed"))
+	assert.ErrorContains(c, err, "", "image tag reference should have been removed")
 
 	dockerCmd(c, "rmi", repoTag2)
 
 	// rmi should have deleted the tag, the digest reference, and the image itself
 	_, err = inspectFieldWithError(imageID, "Id")
-	c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted"))
+	assert.ErrorContains(c, err, "", "image should have been deleted")
 }
 
 // TestPullFailsWithAlteredManifest tests that a `docker pull` fails when
 // we have modified a manifest blob and its digest cannot be verified.
 // This is the schema2 version of the test.
-func (s *DockerRegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
+func (s *DockerRegistrySuite) TestPullFailsWithAlteredManifest(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	manifestDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	// Load the target manifest blob.
 	manifestBlob := s.reg.ReadBlobContents(c, manifestDigest)
 
 	var imgManifest schema2.Manifest
 	err = json.Unmarshal(manifestBlob, &imgManifest)
-	c.Assert(err, checker.IsNil, check.Commentf("unable to decode image manifest from blob"))
+	assert.NilError(c, err, "unable to decode image manifest from blob")
 
 	// Change a layer in the manifest.
 	imgManifest.Layers[0].Digest = digest.Digest("sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
@@ -548,7 +544,7 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
 	defer undo()
 
 	alteredManifestBlob, err := json.MarshalIndent(imgManifest, "", "   ")
-	c.Assert(err, checker.IsNil, check.Commentf("unable to encode altered image manifest to JSON"))
+	assert.NilError(c, err, "unable to encode altered image manifest to JSON")
 
 	s.reg.WriteBlobContents(c, manifestDigest, alteredManifestBlob)
 
@@ -558,26 +554,26 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
 	// Pull from the registry using the @ reference.
 	imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
 	out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
-	c.Assert(exitStatus, checker.Not(check.Equals), 0)
+	assert.Assert(c, exitStatus != 0)
 
 	expectedErrorMsg := fmt.Sprintf("manifest verification failed for digest %s", manifestDigest)
-	c.Assert(out, checker.Contains, expectedErrorMsg)
+	assert.Assert(c, is.Contains(out, expectedErrorMsg))
 }
 
 // TestPullFailsWithAlteredManifest tests that a `docker pull` fails when
 // we have modified a manifest blob and its digest cannot be verified.
 // This is the schema1 version of the test.
-func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredManifest(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredManifest(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	manifestDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.Assert(c, err == nil, "error setting up image")
 
 	// Load the target manifest blob.
 	manifestBlob := s.reg.ReadBlobContents(c, manifestDigest)
 
 	var imgManifest schema1.Manifest
 	err = json.Unmarshal(manifestBlob, &imgManifest)
-	c.Assert(err, checker.IsNil, check.Commentf("unable to decode image manifest from blob"))
+	assert.Assert(c, err == nil, "unable to decode image manifest from blob")
 
 	// Change a layer in the manifest.
 	imgManifest.FSLayers[0] = schema1.FSLayer{
@@ -590,7 +586,7 @@ func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredManifest(c *check.C
 	defer undo()
 
 	alteredManifestBlob, err := json.MarshalIndent(imgManifest, "", "   ")
-	c.Assert(err, checker.IsNil, check.Commentf("unable to encode altered image manifest to JSON"))
+	assert.Assert(c, err == nil, "unable to encode altered image manifest to JSON")
 
 	s.reg.WriteBlobContents(c, manifestDigest, alteredManifestBlob)
 
@@ -600,26 +596,26 @@ func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredManifest(c *check.C
 	// Pull from the registry using the @ reference.
 	imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
 	out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
-	c.Assert(exitStatus, checker.Not(check.Equals), 0)
+	assert.Assert(c, exitStatus != 0)
 
 	expectedErrorMsg := fmt.Sprintf("image verification failed for digest %s", manifestDigest)
-	c.Assert(out, checker.Contains, expectedErrorMsg)
+	assert.Assert(c, strings.Contains(out, expectedErrorMsg))
 }
 
 // TestPullFailsWithAlteredLayer tests that a `docker pull` fails when
 // we have modified a layer blob and its digest cannot be verified.
 // This is the schema2 version of the test.
-func (s *DockerRegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
+func (s *DockerRegistrySuite) TestPullFailsWithAlteredLayer(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	manifestDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil)
+	assert.Assert(c, err == nil)
 
 	// Load the target manifest blob.
 	manifestBlob := s.reg.ReadBlobContents(c, manifestDigest)
 
 	var imgManifest schema2.Manifest
 	err = json.Unmarshal(manifestBlob, &imgManifest)
-	c.Assert(err, checker.IsNil)
+	assert.Assert(c, err == nil)
 
 	// Next, get the digest of one of the layers from the manifest.
 	targetLayerDigest := imgManifest.Layers[0].Digest
@@ -643,26 +639,26 @@ func (s *DockerRegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
 	// Pull from the registry using the @ reference.
 	imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
 	out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
-	c.Assert(exitStatus, checker.Not(check.Equals), 0, check.Commentf("expected a non-zero exit status"))
+	assert.Assert(c, exitStatus != 0, "expected a non-zero exit status")
 
 	expectedErrorMsg := fmt.Sprintf("filesystem layer verification failed for digest %s", targetLayerDigest)
-	c.Assert(out, checker.Contains, expectedErrorMsg, check.Commentf("expected error message in output: %s", out))
+	assert.Assert(c, strings.Contains(out, expectedErrorMsg), "expected error message in output: %s", out)
 }
 
 // TestPullFailsWithAlteredLayer tests that a `docker pull` fails when
 // we have modified a layer blob and its digest cannot be verified.
 // This is the schema1 version of the test.
-func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredLayer(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	manifestDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil)
+	assert.Assert(c, err == nil)
 
 	// Load the target manifest blob.
 	manifestBlob := s.reg.ReadBlobContents(c, manifestDigest)
 
 	var imgManifest schema1.Manifest
 	err = json.Unmarshal(manifestBlob, &imgManifest)
-	c.Assert(err, checker.IsNil)
+	assert.Assert(c, err == nil)
 
 	// Next, get the digest of one of the layers from the manifest.
 	targetLayerDigest := imgManifest.FSLayers[0].BlobSum
@@ -686,8 +682,8 @@ func (s *DockerSchema1RegistrySuite) TestPullFailsWithAlteredLayer(c *check.C) {
 	// Pull from the registry using the @ reference.
 	imageReference := fmt.Sprintf("%s@%s", repoName, manifestDigest)
 	out, exitStatus, _ := dockerCmdWithError("pull", imageReference)
-	c.Assert(exitStatus, checker.Not(check.Equals), 0, check.Commentf("expected a non-zero exit status"))
+	assert.Assert(c, exitStatus != 0, "expected a non-zero exit status")
 
 	expectedErrorMsg := fmt.Sprintf("filesystem layer verification failed for digest %s", targetLayerDigest)
-	c.Assert(out, checker.Contains, expectedErrorMsg, check.Commentf("expected error message in output: %s", out))
+	assert.Assert(c, strings.Contains(out, expectedErrorMsg), "expected error message in output: %s", out)
 }
diff --git a/integration-cli/docker_cli_commit_test.go b/integration-cli/docker_cli_commit_test.go
index d9f6e6875cee0..d5c4b8ca59f53 100644
--- a/integration-cli/docker_cli_commit_test.go
+++ b/integration-cli/docker_cli_commit_test.go
@@ -2,14 +2,16 @@ package main
 
 import (
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/api/types/versions"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
-func (s *DockerSuite) TestCommitAfterContainerIsDone(c *check.C) {
+func (s *DockerSuite) TestCommitAfterContainerIsDone(c *testing.T) {
+	skip.If(c, RuntimeIsWindowsContainerd(), "FIXME: Broken on Windows + containerd combination")
 	out := cli.DockerCmd(c, "run", "-i", "-a", "stdin", "busybox", "echo", "foo").Combined()
 
 	cleanedContainerID := strings.TrimSpace(out)
@@ -23,7 +25,7 @@ func (s *DockerSuite) TestCommitAfterContainerIsDone(c *check.C) {
 	cli.DockerCmd(c, "inspect", cleanedImageID)
 }
 
-func (s *DockerSuite) TestCommitWithoutPause(c *check.C) {
+func (s *DockerSuite) TestCommitWithoutPause(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-i", "-a", "stdin", "busybox", "echo", "foo")
 
@@ -38,8 +40,8 @@ func (s *DockerSuite) TestCommitWithoutPause(c *check.C) {
 	dockerCmd(c, "inspect", cleanedImageID)
 }
 
-//test commit a paused container should not unpause it after commit
-func (s *DockerSuite) TestCommitPausedContainer(c *check.C) {
+// TestCommitPausedContainer tests that a paused container is not unpaused after being committed
+func (s *DockerSuite) TestCommitPausedContainer(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-i", "-d", "busybox")
 
@@ -50,10 +52,10 @@ func (s *DockerSuite) TestCommitPausedContainer(c *check.C) {
 
 	out = inspectField(c, cleanedContainerID, "State.Paused")
 	// commit should not unpause a paused container
-	c.Assert(out, checker.Contains, "true")
+	assert.Assert(c, strings.Contains(out, "true"))
 }
 
-func (s *DockerSuite) TestCommitNewFile(c *check.C) {
+func (s *DockerSuite) TestCommitNewFile(c *testing.T) {
 	dockerCmd(c, "run", "--name", "committer", "busybox", "/bin/sh", "-c", "echo koye > /foo")
 
 	imageID, _ := dockerCmd(c, "commit", "committer")
@@ -61,18 +63,17 @@ func (s *DockerSuite) TestCommitNewFile(c *check.C) {
 
 	out, _ := dockerCmd(c, "run", imageID, "cat", "/foo")
 	actual := strings.TrimSpace(out)
-	c.Assert(actual, checker.Equals, "koye")
+	assert.Equal(c, actual, "koye")
 }
 
-func (s *DockerSuite) TestCommitHardlink(c *check.C) {
+func (s *DockerSuite) TestCommitHardlink(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	firstOutput, _ := dockerCmd(c, "run", "-t", "--name", "hardlinks", "busybox", "sh", "-c", "touch file1 && ln file1 file2 && ls -di file1 file2")
 
 	chunks := strings.Split(strings.TrimSpace(firstOutput), " ")
 	inode := chunks[0]
 	chunks = strings.SplitAfterN(strings.TrimSpace(firstOutput), " ", 2)
-	c.Assert(chunks[1], checker.Contains, chunks[0], check.Commentf("Failed to create hardlink in a container. Expected to find %q in %q", inode, chunks[1:]))
-
+	assert.Assert(c, strings.Contains(chunks[1], chunks[0]), "Failed to create hardlink in a container. Expected to find %q in %q", inode, chunks[1:])
 	imageID, _ := dockerCmd(c, "commit", "hardlinks", "hardlinks")
 	imageID = strings.TrimSpace(imageID)
 
@@ -81,10 +82,10 @@ func (s *DockerSuite) TestCommitHardlink(c *check.C) {
 	chunks = strings.Split(strings.TrimSpace(secondOutput), " ")
 	inode = chunks[0]
 	chunks = strings.SplitAfterN(strings.TrimSpace(secondOutput), " ", 2)
-	c.Assert(chunks[1], checker.Contains, chunks[0], check.Commentf("Failed to create hardlink in a container. Expected to find %q in %q", inode, chunks[1:]))
+	assert.Assert(c, strings.Contains(chunks[1], chunks[0]), "Failed to create hardlink in a container. Expected to find %q in %q", inode, chunks[1:])
 }
 
-func (s *DockerSuite) TestCommitTTY(c *check.C) {
+func (s *DockerSuite) TestCommitTTY(c *testing.T) {
 	dockerCmd(c, "run", "-t", "--name", "tty", "busybox", "/bin/ls")
 
 	imageID, _ := dockerCmd(c, "commit", "tty", "ttytest")
@@ -93,7 +94,7 @@ func (s *DockerSuite) TestCommitTTY(c *check.C) {
 	dockerCmd(c, "run", imageID, "/bin/ls")
 }
 
-func (s *DockerSuite) TestCommitWithHostBindMount(c *check.C) {
+func (s *DockerSuite) TestCommitWithHostBindMount(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "--name", "bind-commit", "-v", "/dev/null:/winning", "busybox", "true")
 
@@ -103,7 +104,7 @@ func (s *DockerSuite) TestCommitWithHostBindMount(c *check.C) {
 	dockerCmd(c, "run", imageID, "true")
 }
 
-func (s *DockerSuite) TestCommitChange(c *check.C) {
+func (s *DockerSuite) TestCommitChange(c *testing.T) {
 	dockerCmd(c, "run", "--name", "test", "busybox", "true")
 
 	imageID, _ := dockerCmd(c, "commit",
@@ -153,7 +154,7 @@ func (s *DockerSuite) TestCommitChange(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestCommitChangeLabels(c *check.C) {
+func (s *DockerSuite) TestCommitChangeLabels(c *testing.T) {
 	dockerCmd(c, "run", "--name", "test", "--label", "some=label", "busybox", "true")
 
 	imageID, _ := dockerCmd(c, "commit",
@@ -161,7 +162,7 @@ func (s *DockerSuite) TestCommitChangeLabels(c *check.C) {
 		"test", "test-commit")
 	imageID = strings.TrimSpace(imageID)
 
-	c.Assert(inspectField(c, imageID, "Config.Labels"), checker.Equals, "map[some:label2]")
+	assert.Equal(c, inspectField(c, imageID, "Config.Labels"), "map[some:label2]")
 	// check that container labels didn't change
-	c.Assert(inspectField(c, "test", "Config.Labels"), checker.Equals, "map[some:label]")
+	assert.Equal(c, inspectField(c, "test", "Config.Labels"), "map[some:label]")
 }
diff --git a/integration-cli/docker_cli_cp_from_container_test.go b/integration-cli/docker_cli_cp_from_container_test.go
index 499be54522d84..a35748928059d 100644
--- a/integration-cli/docker_cli_cp_from_container_test.go
+++ b/integration-cli/docker_cli_cp_from_container_test.go
@@ -3,9 +3,9 @@ package main
 import (
 	"os"
 	"path/filepath"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
 // Try all of the test cases from the archive package which implements the
@@ -21,7 +21,7 @@ import (
 // Check that copying from a container to a local symlink copies to the symlink
 // target and does not overwrite the local symlink itself.
 // TODO: move to docker/cli and/or integration/container/copy_test.go
-func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
+func (s *DockerSuite) TestCpFromSymlinkDestination(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
 
@@ -35,38 +35,26 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
 	srcPath := containerCpPath(containerID, "/file2")
 	dstPath := cpPath(tmpDir, "symlinkToFile1")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// The symlink should not have been modified.
-	c.Assert(symlinkTargetEquals(c, dstPath, "file1"), checker.IsNil)
-
-	// The file should have the contents of "file2" now.
-	c.Assert(fileContentEquals(c, cpPath(tmpDir, "file1"), "file2\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, symlinkTargetEquals(c, dstPath, "file1"), "The symlink should not have been modified")
+	assert.NilError(c, fileContentEquals(c, cpPath(tmpDir, "file1"), "file2\n"), `The file should have the contents of "file2" now`)
 
 	// Next, copy a file from the container to a symlink to a directory. This
 	// should copy the file into the symlink target directory.
 	dstPath = cpPath(tmpDir, "symlinkToDir1")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// The symlink should not have been modified.
-	c.Assert(symlinkTargetEquals(c, dstPath, "dir1"), checker.IsNil)
-
-	// The file should have the contents of "file2" now.
-	c.Assert(fileContentEquals(c, cpPath(tmpDir, "file2"), "file2\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, symlinkTargetEquals(c, dstPath, "dir1"), "The symlink should not have been modified")
+	assert.NilError(c, fileContentEquals(c, cpPath(tmpDir, "file2"), "file2\n"), `The file should have the contents of "file2" now`)
 
 	// Next, copy a file from the container to a symlink to a file that does
 	// not exist (a broken symlink). This should create the target file with
 	// the contents of the source file.
 	dstPath = cpPath(tmpDir, "brokenSymlinkToFileX")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// The symlink should not have been modified.
-	c.Assert(symlinkTargetEquals(c, dstPath, "fileX"), checker.IsNil)
-
-	// The file should have the contents of "file2" now.
-	c.Assert(fileContentEquals(c, cpPath(tmpDir, "fileX"), "file2\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, symlinkTargetEquals(c, dstPath, "fileX"), "The symlink should not have been modified")
+	assert.NilError(c, fileContentEquals(c, cpPath(tmpDir, "fileX"), "file2\n"), `The file should have the contents of "file2" now`)
 
 	// Next, copy a directory from the container to a symlink to a local
 	// directory. This should copy the directory into the symlink target
@@ -74,13 +62,9 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
 	srcPath = containerCpPath(containerID, "/dir2")
 	dstPath = cpPath(tmpDir, "symlinkToDir1")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// The symlink should not have been modified.
-	c.Assert(symlinkTargetEquals(c, dstPath, "dir1"), checker.IsNil)
-
-	// The directory should now contain a copy of "dir2".
-	c.Assert(fileContentEquals(c, cpPath(tmpDir, "dir1/dir2/file2-1"), "file2-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, symlinkTargetEquals(c, dstPath, "dir1"), "The symlink should not have been modified")
+	assert.NilError(c, fileContentEquals(c, cpPath(tmpDir, "dir1/dir2/file2-1"), "file2-1\n"), `The directory should now contain a copy of "dir2"`)
 
 	// Next, copy a directory from the container to a symlink to a local
 	// directory that does not exist (a broken symlink). This should create
@@ -88,13 +72,9 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
 	// should not modify the symlink.
 	dstPath = cpPath(tmpDir, "brokenSymlinkToDirX")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// The symlink should not have been modified.
-	c.Assert(symlinkTargetEquals(c, dstPath, "dirX"), checker.IsNil)
-
-	// The "dirX" directory should now be a copy of "dir2".
-	c.Assert(fileContentEquals(c, cpPath(tmpDir, "dirX/file2-1"), "file2-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, symlinkTargetEquals(c, dstPath, "dirX"), "The symlink should not have been modified")
+	assert.NilError(c, fileContentEquals(c, cpPath(tmpDir, "dirX/file2-1"), "file2-1\n"), `The "dirX" directory should now be a copy of "dir2"`)
 }
 
 // Possibilities are reduced to the remaining 10 cases:
@@ -116,7 +96,7 @@ func (s *DockerSuite) TestCpFromSymlinkDestination(c *check.C) {
 // A. SRC specifies a file and DST (no trailing path separator) doesn't
 //    exist. This should create a file with the name DST and copy the
 //    contents of the source file into it.
-func (s *DockerSuite) TestCpFromCaseA(c *check.C) {
+func (s *DockerSuite) TestCpFromCaseA(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -128,15 +108,14 @@ func (s *DockerSuite) TestCpFromCaseA(c *check.C) {
 	srcPath := containerCpPath(containerID, "/root/file1")
 	dstPath := cpPath(tmpDir, "itWorks.txt")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1\n"))
 }
 
 // B. SRC specifies a file and DST (with trailing path separator) doesn't
 //    exist. This should cause an error because the copy operation cannot
 //    create a directory when copying a single file.
-func (s *DockerSuite) TestCpFromCaseB(c *check.C) {
+func (s *DockerSuite) TestCpFromCaseB(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
 
@@ -146,15 +125,14 @@ func (s *DockerSuite) TestCpFromCaseB(c *check.C) {
 	srcPath := containerCpPath(containerID, "/file1")
 	dstDir := cpPathTrailingSep(tmpDir, "testDir")
 
-	err := runDockerCp(c, srcPath, dstDir, nil)
-	c.Assert(err, checker.NotNil)
-
-	c.Assert(isCpDirNotExist(err), checker.True, check.Commentf("expected DirNotExists error, but got %T: %s", err, err))
+	err := runDockerCp(c, srcPath, dstDir)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, isCpDirNotExist(err), "expected DirNotExists error, but got %T: %s", err, err)
 }
 
 // C. SRC specifies a file and DST exists as a file. This should overwrite
 //    the file at DST with the contents of the source file.
-func (s *DockerSuite) TestCpFromCaseC(c *check.C) {
+func (s *DockerSuite) TestCpFromCaseC(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -169,17 +147,15 @@ func (s *DockerSuite) TestCpFromCaseC(c *check.C) {
 	dstPath := cpPath(tmpDir, "file2")
 
 	// Ensure the local file starts with different content.
-	c.Assert(fileContentEquals(c, dstPath, "file2\n"), checker.IsNil)
-
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
+	assert.NilError(c, fileContentEquals(c, dstPath, "file2\n"))
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1\n"))
 }
 
 // D. SRC specifies a file and DST exists as a directory. This should place
 //    a copy of the source file inside it using the basename from SRC. Ensure
 //    this works whether DST has a trailing path separator or not.
-func (s *DockerSuite) TestCpFromCaseD(c *check.C) {
+func (s *DockerSuite) TestCpFromCaseD(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
 
@@ -194,32 +170,27 @@ func (s *DockerSuite) TestCpFromCaseD(c *check.C) {
 
 	// Ensure that dstPath doesn't exist.
 	_, err := os.Stat(dstPath)
-	c.Assert(os.IsNotExist(err), checker.True, check.Commentf("did not expect dstPath %q to exist", dstPath))
-
-	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
+	assert.Assert(c, os.IsNotExist(err), "did not expect dstPath %q to exist", dstPath)
 
-	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstDir))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1\n"))
 
 	// Now try again but using a trailing path separator for dstDir.
 
-	// unable to remove dstDir
-	c.Assert(os.RemoveAll(dstDir), checker.IsNil)
-
-	// unable to make dstDir
-	c.Assert(os.MkdirAll(dstDir, os.FileMode(0755)), checker.IsNil)
+	assert.NilError(c, os.RemoveAll(dstDir), "unable to remove dstDir")
+	assert.NilError(c, os.MkdirAll(dstDir, os.FileMode(0755)), "unable to make dstDir")
 
 	dstDir = cpPathTrailingSep(tmpDir, "dir1")
 
-	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstDir))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1\n"))
 }
 
 // E. SRC specifies a directory and DST does not exist. This should create a
 //    directory at DST and copy the contents of the SRC directory into the DST
 //    directory. Ensure this works whether DST has a trailing path separator or
 //    not.
-func (s *DockerSuite) TestCpFromCaseE(c *check.C) {
+func (s *DockerSuite) TestCpFromCaseE(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
 
@@ -230,25 +201,22 @@ func (s *DockerSuite) TestCpFromCaseE(c *check.C) {
 	dstDir := cpPath(tmpDir, "testDir")
 	dstPath := filepath.Join(dstDir, "file1-1")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1-1\n"))
 
 	// Now try again but using a trailing path separator for dstDir.
 
-	// unable to remove dstDir
-	c.Assert(os.RemoveAll(dstDir), checker.IsNil)
+	assert.NilError(c, os.RemoveAll(dstDir), "unable to remove dstDir")
 
 	dstDir = cpPathTrailingSep(tmpDir, "testDir")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1-1\n"))
 }
 
 // F. SRC specifies a directory and DST exists as a file. This should cause an
 //    error as it is not possible to overwrite a file with a directory.
-func (s *DockerSuite) TestCpFromCaseF(c *check.C) {
+func (s *DockerSuite) TestCpFromCaseF(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -262,16 +230,15 @@ func (s *DockerSuite) TestCpFromCaseF(c *check.C) {
 	srcDir := containerCpPath(containerID, "/root/dir1")
 	dstFile := cpPath(tmpDir, "file1")
 
-	err := runDockerCp(c, srcDir, dstFile, nil)
-	c.Assert(err, checker.NotNil)
-
-	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
+	err := runDockerCp(c, srcDir, dstFile)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, isCpCannotCopyDir(err), "expected ErrCannotCopyDir error, but got %T: %s", err, err)
 }
 
 // G. SRC specifies a directory and DST exists as a directory. This should copy
 //    the SRC directory and all its contents to the DST directory. Ensure this
 //    works whether DST has a trailing path separator or not.
-func (s *DockerSuite) TestCpFromCaseG(c *check.C) {
+func (s *DockerSuite) TestCpFromCaseG(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -287,30 +254,25 @@ func (s *DockerSuite) TestCpFromCaseG(c *check.C) {
 	resultDir := filepath.Join(dstDir, "dir1")
 	dstPath := filepath.Join(resultDir, "file1-1")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1-1\n"))
 
 	// Now try again but using a trailing path separator for dstDir.
 
-	// unable to remove dstDir
-	c.Assert(os.RemoveAll(dstDir), checker.IsNil)
-
-	// unable to make dstDir
-	c.Assert(os.MkdirAll(dstDir, os.FileMode(0755)), checker.IsNil)
+	assert.NilError(c, os.RemoveAll(dstDir), "unable to remove dstDir")
+	assert.NilError(c, os.MkdirAll(dstDir, os.FileMode(0755)), "unable to make dstDir")
 
 	dstDir = cpPathTrailingSep(tmpDir, "dir2")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1-1\n"))
 }
 
 // H. SRC specifies a directory's contents only and DST does not exist. This
 //    should create a directory at DST and copy the contents of the SRC
 //    directory (but not the directory itself) into the DST directory. Ensure
 //    this works whether DST has a trailing path separator or not.
-func (s *DockerSuite) TestCpFromCaseH(c *check.C) {
+func (s *DockerSuite) TestCpFromCaseH(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{addContent: true})
 
@@ -321,26 +283,23 @@ func (s *DockerSuite) TestCpFromCaseH(c *check.C) {
 	dstDir := cpPath(tmpDir, "testDir")
 	dstPath := filepath.Join(dstDir, "file1-1")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1-1\n"))
 
 	// Now try again but using a trailing path separator for dstDir.
 
-	// unable to remove resultDir
-	c.Assert(os.RemoveAll(dstDir), checker.IsNil)
+	assert.NilError(c, os.RemoveAll(dstDir), "unable to remove resultDir")
 
 	dstDir = cpPathTrailingSep(tmpDir, "testDir")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1-1\n"))
 }
 
 // I. SRC specifies a directory's contents only and DST exists as a file. This
 //    should cause an error as it is not possible to overwrite a file with a
 //    directory.
-func (s *DockerSuite) TestCpFromCaseI(c *check.C) {
+func (s *DockerSuite) TestCpFromCaseI(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -354,17 +313,16 @@ func (s *DockerSuite) TestCpFromCaseI(c *check.C) {
 	srcDir := containerCpPathTrailingSep(containerID, "/root/dir1") + "."
 	dstFile := cpPath(tmpDir, "file1")
 
-	err := runDockerCp(c, srcDir, dstFile, nil)
-	c.Assert(err, checker.NotNil)
-
-	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
+	err := runDockerCp(c, srcDir, dstFile)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, isCpCannotCopyDir(err), "expected ErrCannotCopyDir error, but got %T: %s", err, err)
 }
 
 // J. SRC specifies a directory's contents only and DST exists as a directory.
 //    This should copy the contents of the SRC directory (but not the directory
 //    itself) into the DST directory. Ensure this works whether DST has a
 //    trailing path separator or not.
-func (s *DockerSuite) TestCpFromCaseJ(c *check.C) {
+func (s *DockerSuite) TestCpFromCaseJ(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -379,21 +337,16 @@ func (s *DockerSuite) TestCpFromCaseJ(c *check.C) {
 	dstDir := cpPath(tmpDir, "dir2")
 	dstPath := filepath.Join(dstDir, "file1-1")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1-1\n"))
 
 	// Now try again but using a trailing path separator for dstDir.
 
-	// unable to remove dstDir
-	c.Assert(os.RemoveAll(dstDir), checker.IsNil)
-
-	// unable to make dstDir
-	c.Assert(os.MkdirAll(dstDir, os.FileMode(0755)), checker.IsNil)
+	assert.NilError(c, os.RemoveAll(dstDir), "unable to remove dstDir")
+	assert.NilError(c, os.MkdirAll(dstDir, os.FileMode(0755)), "unable to make dstDir")
 
 	dstDir = cpPathTrailingSep(tmpDir, "dir2")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	c.Assert(fileContentEquals(c, dstPath, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, fileContentEquals(c, dstPath, "file1-1\n"))
 }
diff --git a/integration-cli/docker_cli_cp_test.go b/integration-cli/docker_cli_cp_test.go
index b2826ab922729..173cf4eee5c0f 100644
--- a/integration-cli/docker_cli_cp_test.go
+++ b/integration-cli/docker_cli_cp_test.go
@@ -3,16 +3,17 @@ package main
 import (
 	"bytes"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"os"
 	"os/exec"
 	"path"
 	"path/filepath"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/icmd"
 )
 
 const (
@@ -26,35 +27,31 @@ const (
 )
 
 // Ensure that an all-local path case returns an error.
-func (s *DockerSuite) TestCpLocalOnly(c *check.C) {
-	err := runDockerCp(c, "foo", "bar", nil)
-	c.Assert(err, checker.NotNil)
-
-	c.Assert(err.Error(), checker.Contains, "must specify at least one container source")
+func (s *DockerSuite) TestCpLocalOnly(c *testing.T) {
+	err := runDockerCp(c, "foo", "bar")
+	assert.ErrorContains(c, err, "must specify at least one container source")
 }
 
 // Test for #5656
 // Check that garbage paths don't escape the container's rootfs
-func (s *DockerSuite) TestCpGarbagePath(c *check.C) {
+func (s *DockerSuite) TestCpGarbagePath(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
 
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
-
-	c.Assert(os.MkdirAll(cpTestPath, os.ModeDir), checker.IsNil)
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
+	assert.NilError(c, os.MkdirAll(cpTestPath, os.ModeDir))
 
 	hostFile, err := os.Create(cpFullPath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer hostFile.Close()
 	defer os.RemoveAll(cpTestPathParent)
 
 	fmt.Fprintf(hostFile, "%s", cpHostContents)
 
-	tmpdir, err := ioutil.TempDir("", "docker-integration")
-	c.Assert(err, checker.IsNil)
+	tmpdir, err := os.MkdirTemp("", "docker-integration")
+	assert.NilError(c, err)
 
 	tmpname := filepath.Join(tmpdir, cpTestName)
 	defer os.RemoveAll(tmpdir)
@@ -66,37 +63,31 @@ func (s *DockerSuite) TestCpGarbagePath(c *check.C) {
 	file, _ := os.Open(tmpname)
 	defer file.Close()
 
-	test, err := ioutil.ReadAll(file)
-	c.Assert(err, checker.IsNil)
-
-	// output matched host file -- garbage path can escape container rootfs
-	c.Assert(string(test), checker.Not(checker.Equals), cpHostContents)
-
-	// output doesn't match the input for garbage path
-	c.Assert(string(test), checker.Equals, cpContainerContents)
+	test, err := io.ReadAll(file)
+	assert.NilError(c, err)
+	assert.Assert(c, string(test) != cpHostContents, "output matched host file -- garbage path can escape container rootfs")
+	assert.Assert(c, string(test) == cpContainerContents, "output doesn't match the input for garbage path")
 }
 
 // Check that relative paths are relative to the container's rootfs
-func (s *DockerSuite) TestCpRelativePath(c *check.C) {
+func (s *DockerSuite) TestCpRelativePath(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
 
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
-
-	c.Assert(os.MkdirAll(cpTestPath, os.ModeDir), checker.IsNil)
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
+	assert.NilError(c, os.MkdirAll(cpTestPath, os.ModeDir))
 
 	hostFile, err := os.Create(cpFullPath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer hostFile.Close()
 	defer os.RemoveAll(cpTestPathParent)
 
 	fmt.Fprintf(hostFile, "%s", cpHostContents)
 
-	tmpdir, err := ioutil.TempDir("", "docker-integration")
-	c.Assert(err, checker.IsNil)
+	tmpdir, err := os.MkdirTemp("", "docker-integration")
+	assert.NilError(c, err)
 
 	tmpname := filepath.Join(tmpdir, cpTestName)
 	defer os.RemoveAll(tmpdir)
@@ -107,44 +98,38 @@ func (s *DockerSuite) TestCpRelativePath(c *check.C) {
 		// get this unix-path manipulation on windows with filepath.
 		relPath = cpFullPath[1:]
 	}
-	c.Assert(path.IsAbs(cpFullPath), checker.True, check.Commentf("path %s was assumed to be an absolute path", cpFullPath))
+	assert.Assert(c, path.IsAbs(cpFullPath), "path %s was assumed to be an absolute path", cpFullPath)
 
 	dockerCmd(c, "cp", containerID+":"+relPath, tmpdir)
 
 	file, _ := os.Open(tmpname)
 	defer file.Close()
 
-	test, err := ioutil.ReadAll(file)
-	c.Assert(err, checker.IsNil)
-
-	// output matched host file -- relative path can escape container rootfs
-	c.Assert(string(test), checker.Not(checker.Equals), cpHostContents)
-
-	// output doesn't match the input for relative path
-	c.Assert(string(test), checker.Equals, cpContainerContents)
+	test, err := io.ReadAll(file)
+	assert.NilError(c, err)
+	assert.Assert(c, string(test) != cpHostContents, "output matched host file -- relative path can escape container rootfs")
+	assert.Assert(c, string(test) == cpContainerContents, "output doesn't match the input for relative path")
 }
 
 // Check that absolute paths are relative to the container's rootfs
-func (s *DockerSuite) TestCpAbsolutePath(c *check.C) {
+func (s *DockerSuite) TestCpAbsolutePath(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
 
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
-
-	c.Assert(os.MkdirAll(cpTestPath, os.ModeDir), checker.IsNil)
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
+	assert.NilError(c, os.MkdirAll(cpTestPath, os.ModeDir))
 
 	hostFile, err := os.Create(cpFullPath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer hostFile.Close()
 	defer os.RemoveAll(cpTestPathParent)
 
 	fmt.Fprintf(hostFile, "%s", cpHostContents)
 
-	tmpdir, err := ioutil.TempDir("", "docker-integration")
-	c.Assert(err, checker.IsNil)
+	tmpdir, err := os.MkdirTemp("", "docker-integration")
+	assert.NilError(c, err)
 
 	tmpname := filepath.Join(tmpdir, cpTestName)
 	defer os.RemoveAll(tmpdir)
@@ -156,39 +141,34 @@ func (s *DockerSuite) TestCpAbsolutePath(c *check.C) {
 	file, _ := os.Open(tmpname)
 	defer file.Close()
 
-	test, err := ioutil.ReadAll(file)
-	c.Assert(err, checker.IsNil)
-
-	// output matched host file -- absolute path can escape container rootfs
-	c.Assert(string(test), checker.Not(checker.Equals), cpHostContents)
-
-	// output doesn't match the input for absolute path
-	c.Assert(string(test), checker.Equals, cpContainerContents)
+	test, err := io.ReadAll(file)
+	assert.NilError(c, err)
+	assert.Assert(c, string(test) != cpHostContents, "output matched host file -- absolute path can escape container rootfs")
+	assert.Assert(c, string(test) == cpContainerContents, "output doesn't match the input for absolute path")
 }
 
 // Test for #5619
 // Check that absolute symlinks are still relative to the container's rootfs
-func (s *DockerSuite) TestCpAbsoluteSymlink(c *check.C) {
+func (s *DockerSuite) TestCpAbsoluteSymlink(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpFullPath+" container_path")
 
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
-	c.Assert(os.MkdirAll(cpTestPath, os.ModeDir), checker.IsNil)
+	assert.NilError(c, os.MkdirAll(cpTestPath, os.ModeDir))
 
 	hostFile, err := os.Create(cpFullPath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer hostFile.Close()
 	defer os.RemoveAll(cpTestPathParent)
 
 	fmt.Fprintf(hostFile, "%s", cpHostContents)
 
-	tmpdir, err := ioutil.TempDir("", "docker-integration")
-	c.Assert(err, checker.IsNil)
+	tmpdir, err := os.MkdirTemp("", "docker-integration")
+	assert.NilError(c, err)
 
 	tmpname := filepath.Join(tmpdir, "container_path")
 	defer os.RemoveAll(tmpdir)
@@ -199,25 +179,23 @@ func (s *DockerSuite) TestCpAbsoluteSymlink(c *check.C) {
 
 	// We should have copied a symlink *NOT* the file itself!
 	linkTarget, err := os.Readlink(tmpname)
-	c.Assert(err, checker.IsNil)
-
-	c.Assert(linkTarget, checker.Equals, filepath.FromSlash(cpFullPath))
+	assert.NilError(c, err)
+	assert.Equal(c, linkTarget, filepath.FromSlash(cpFullPath))
 }
 
 // Check that symlinks to a directory behave as expected when copying one from
 // a container.
-func (s *DockerSuite) TestCpFromSymlinkToDirectory(c *check.C) {
+func (s *DockerSuite) TestCpFromSymlinkToDirectory(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpTestPathParent+" /dir_link")
 
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
-	testDir, err := ioutil.TempDir("", "test-cp-from-symlink-to-dir-")
-	c.Assert(err, checker.IsNil)
+	testDir, err := os.MkdirTemp("", "test-cp-from-symlink-to-dir-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(testDir)
 
 	// This copy command should copy the symlink, not the target, into the
@@ -226,9 +204,9 @@ func (s *DockerSuite) TestCpFromSymlinkToDirectory(c *check.C) {
 
 	expectedPath := filepath.Join(testDir, "dir_link")
 	linkTarget, err := os.Readlink(expectedPath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(linkTarget, checker.Equals, filepath.FromSlash(cpTestPathParent))
+	assert.Equal(c, linkTarget, filepath.FromSlash(cpTestPathParent))
 
 	os.Remove(expectedPath)
 
@@ -243,23 +221,22 @@ func (s *DockerSuite) TestCpFromSymlinkToDirectory(c *check.C) {
 	if err == nil {
 		out = fmt.Sprintf("target name was copied: %q - %q", stat.Mode(), stat.Name())
 	}
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 
 	// It *should* have copied the directory using the asked name "dir_link".
 	stat, err = os.Lstat(expectedPath)
-	c.Assert(err, checker.IsNil, check.Commentf("unable to stat resource at %q", expectedPath))
-
-	c.Assert(stat.IsDir(), checker.True, check.Commentf("should have copied a directory but got %q instead", stat.Mode()))
+	assert.NilError(c, err, "unable to stat resource at %q", expectedPath)
+	assert.Assert(c, stat.IsDir(), "should have copied a directory but got %q instead", stat.Mode())
 }
 
 // Check that symlinks to a directory behave as expected when copying one to a
 // container.
-func (s *DockerSuite) TestCpToSymlinkToDirectory(c *check.C) {
+func (s *DockerSuite) TestCpToSymlinkToDirectory(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
-	testRequires(c, SameHostDaemon) // Requires local volume mount bind.
+	testRequires(c, testEnv.IsLocalDaemon) // Requires local volume mount bind.
 
-	testVol, err := ioutil.TempDir("", "test-cp-to-symlink-to-dir-")
-	c.Assert(err, checker.IsNil)
+	testVol, err := os.MkdirTemp("", "test-cp-to-symlink-to-dir-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(testVol)
 
 	// Create a test container with a local volume. We will test by copying
@@ -269,26 +246,26 @@ func (s *DockerSuite) TestCpToSymlinkToDirectory(c *check.C) {
 	containerID := strings.TrimSpace(out)
 
 	// Create a temp directory to hold a test file nested in a directory.
-	testDir, err := ioutil.TempDir("", "test-cp-to-symlink-to-dir-")
-	c.Assert(err, checker.IsNil)
+	testDir, err := os.MkdirTemp("", "test-cp-to-symlink-to-dir-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(testDir)
 
 	// This file will be at "/testDir/some/path/test" and will be copied into
 	// the test volume later.
 	hostTestFilename := filepath.Join(testDir, cpFullPath)
-	c.Assert(os.MkdirAll(filepath.Dir(hostTestFilename), os.FileMode(0700)), checker.IsNil)
-	c.Assert(ioutil.WriteFile(hostTestFilename, []byte(cpHostContents), os.FileMode(0600)), checker.IsNil)
+	assert.NilError(c, os.MkdirAll(filepath.Dir(hostTestFilename), os.FileMode(0700)))
+	assert.NilError(c, os.WriteFile(hostTestFilename, []byte(cpHostContents), os.FileMode(0600)))
 
 	// Now create another temp directory to hold a symlink to the
 	// "/testDir/some" directory.
-	linkDir, err := ioutil.TempDir("", "test-cp-to-symlink-to-dir-")
-	c.Assert(err, checker.IsNil)
+	linkDir, err := os.MkdirTemp("", "test-cp-to-symlink-to-dir-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(linkDir)
 
 	// Then symlink "/linkDir/dir_link" to "/testdir/some".
 	linkTarget := filepath.Join(testDir, cpTestPathParent)
 	localLink := filepath.Join(linkDir, "dir_link")
-	c.Assert(os.Symlink(linkTarget, localLink), checker.IsNil)
+	assert.NilError(c, os.Symlink(linkTarget, localLink))
 
 	// Now copy that symlink into the test volume in the container.
 	dockerCmd(c, "cp", localLink, containerID+":/testVol")
@@ -296,9 +273,8 @@ func (s *DockerSuite) TestCpToSymlinkToDirectory(c *check.C) {
 	// This copy command should have copied the symlink *not* the target.
 	expectedPath := filepath.Join(testVol, "dir_link")
 	actualLinkTarget, err := os.Readlink(expectedPath)
-	c.Assert(err, checker.IsNil, check.Commentf("unable to read symlink at %q", expectedPath))
-
-	c.Assert(actualLinkTarget, checker.Equals, linkTarget)
+	assert.NilError(c, err, "unable to read symlink at %q", expectedPath)
+	assert.Equal(c, actualLinkTarget, linkTarget)
 
 	// Good, now remove that copied link for the next test.
 	os.Remove(expectedPath)
@@ -315,47 +291,44 @@ func (s *DockerSuite) TestCpToSymlinkToDirectory(c *check.C) {
 	if err == nil {
 		out = fmt.Sprintf("target name was copied: %q - %q", stat.Mode(), stat.Name())
 	}
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 
 	// It *should* have copied the directory using the asked name "dir_link".
 	stat, err = os.Lstat(expectedPath)
-	c.Assert(err, checker.IsNil, check.Commentf("unable to stat resource at %q", expectedPath))
-
-	c.Assert(stat.IsDir(), checker.True, check.Commentf("should have copied a directory but got %q instead", stat.Mode()))
+	assert.NilError(c, err, "unable to stat resource at %q", expectedPath)
+	assert.Assert(c, stat.IsDir(), "should have copied a directory but got %q instead", stat.Mode())
 
 	// And this directory should contain the file copied from the host at the
 	// expected location: "/testVol/dir_link/path/test"
 	expectedFilepath := filepath.Join(testVol, "dir_link/path/test")
-	fileContents, err := ioutil.ReadFile(expectedFilepath)
-	c.Assert(err, checker.IsNil)
-
-	c.Assert(string(fileContents), checker.Equals, cpHostContents)
+	fileContents, err := os.ReadFile(expectedFilepath)
+	assert.NilError(c, err)
+	assert.Equal(c, string(fileContents), cpHostContents)
 }
 
 // Test for #5619
 // Check that symlinks which are part of the resource path are still relative to the container's rootfs
-func (s *DockerSuite) TestCpSymlinkComponent(c *check.C) {
+func (s *DockerSuite) TestCpSymlinkComponent(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpTestPath+" container_path")
 
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
-	c.Assert(os.MkdirAll(cpTestPath, os.ModeDir), checker.IsNil)
+	assert.NilError(c, os.MkdirAll(cpTestPath, os.ModeDir))
 
 	hostFile, err := os.Create(cpFullPath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer hostFile.Close()
 	defer os.RemoveAll(cpTestPathParent)
 
 	fmt.Fprintf(hostFile, "%s", cpHostContents)
 
-	tmpdir, err := ioutil.TempDir("", "docker-integration")
+	tmpdir, err := os.MkdirTemp("", "docker-integration")
 
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	tmpname := filepath.Join(tmpdir, cpTestName)
 	defer os.RemoveAll(tmpdir)
@@ -367,19 +340,15 @@ func (s *DockerSuite) TestCpSymlinkComponent(c *check.C) {
 	file, _ := os.Open(tmpname)
 	defer file.Close()
 
-	test, err := ioutil.ReadAll(file)
-	c.Assert(err, checker.IsNil)
-
-	// output matched host file -- symlink path component can escape container rootfs
-	c.Assert(string(test), checker.Not(checker.Equals), cpHostContents)
-
-	// output doesn't match the input for symlink path component
-	c.Assert(string(test), checker.Equals, cpContainerContents)
+	test, err := io.ReadAll(file)
+	assert.NilError(c, err)
+	assert.Assert(c, string(test) != cpHostContents, "output matched host file -- symlink path component can escape container rootfs")
+	assert.Equal(c, string(test), cpContainerContents, "output doesn't match the input for symlink path component")
 }
 
 // Check that cp with unprivileged user doesn't return any error
-func (s *DockerSuite) TestCpUnprivilegedUser(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestCpUnprivilegedUser(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	testRequires(c, UnixCli) // uses chmod/su: not available on windows
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "touch "+cpTestName)
@@ -387,27 +356,27 @@ func (s *DockerSuite) TestCpUnprivilegedUser(c *check.C) {
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
-	tmpdir, err := ioutil.TempDir("", "docker-integration")
-	c.Assert(err, checker.IsNil)
+	tmpdir, err := os.MkdirTemp("", "docker-integration")
+	assert.NilError(c, err)
 
 	defer os.RemoveAll(tmpdir)
 
-	c.Assert(os.Chmod(tmpdir, 0777), checker.IsNil)
+	err = os.Chmod(tmpdir, 0777)
+	assert.NilError(c, err)
 
 	result := icmd.RunCommand("su", "unprivilegeduser", "-c",
 		fmt.Sprintf("%s cp %s:%s %s", dockerBinary, containerID, cpTestName, tmpdir))
 	result.Assert(c, icmd.Expected{})
 }
 
-func (s *DockerSuite) TestCpSpecialFiles(c *check.C) {
+func (s *DockerSuite) TestCpSpecialFiles(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
-	testRequires(c, SameHostDaemon)
+	testRequires(c, testEnv.IsLocalDaemon)
 
-	outDir, err := ioutil.TempDir("", "cp-test-special-files")
-	c.Assert(err, checker.IsNil)
+	outDir, err := os.MkdirTemp("", "cp-test-special-files")
+	assert.NilError(c, err)
 	defer os.RemoveAll(outDir)
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "touch /foo")
@@ -415,197 +384,180 @@ func (s *DockerSuite) TestCpSpecialFiles(c *check.C) {
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
 	// Copy actual /etc/resolv.conf
 	dockerCmd(c, "cp", containerID+":/etc/resolv.conf", outDir)
 
 	expected := readContainerFile(c, containerID, "resolv.conf")
-	actual, err := ioutil.ReadFile(outDir + "/resolv.conf")
-	c.Assert(err, checker.IsNil)
-
-	// Expected copied file to be duplicate of the container resolvconf
-	c.Assert(bytes.Equal(actual, expected), checker.True)
+	actual, err := os.ReadFile(outDir + "/resolv.conf")
+	assert.NilError(c, err)
+	assert.Assert(c, bytes.Equal(actual, expected), "Expected copied file to be duplicate of the container resolvconf")
 
 	// Copy actual /etc/hosts
 	dockerCmd(c, "cp", containerID+":/etc/hosts", outDir)
 
 	expected = readContainerFile(c, containerID, "hosts")
-	actual, err = ioutil.ReadFile(outDir + "/hosts")
-	c.Assert(err, checker.IsNil)
-
-	// Expected copied file to be duplicate of the container hosts
-	c.Assert(bytes.Equal(actual, expected), checker.True)
+	actual, err = os.ReadFile(outDir + "/hosts")
+	assert.NilError(c, err)
+	assert.Assert(c, bytes.Equal(actual, expected), "Expected copied file to be duplicate of the container hosts")
 
 	// Copy actual /etc/resolv.conf
 	dockerCmd(c, "cp", containerID+":/etc/hostname", outDir)
 
 	expected = readContainerFile(c, containerID, "hostname")
-	actual, err = ioutil.ReadFile(outDir + "/hostname")
-	c.Assert(err, checker.IsNil)
-
-	// Expected copied file to be duplicate of the container resolvconf
-	c.Assert(bytes.Equal(actual, expected), checker.True)
+	actual, err = os.ReadFile(outDir + "/hostname")
+	assert.NilError(c, err)
+	assert.Assert(c, bytes.Equal(actual, expected), "Expected copied file to be duplicate of the container hostname")
 }
 
-func (s *DockerSuite) TestCpVolumePath(c *check.C) {
+func (s *DockerSuite) TestCpVolumePath(c *testing.T) {
 	//  stat /tmp/cp-test-volumepath851508420/test gets permission denied for the user
 	testRequires(c, NotUserNamespace)
 	testRequires(c, DaemonIsLinux)
-	testRequires(c, SameHostDaemon)
+	testRequires(c, testEnv.IsLocalDaemon)
 
-	tmpDir, err := ioutil.TempDir("", "cp-test-volumepath")
-	c.Assert(err, checker.IsNil)
+	tmpDir, err := os.MkdirTemp("", "cp-test-volumepath")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmpDir)
-	outDir, err := ioutil.TempDir("", "cp-test-volumepath-out")
-	c.Assert(err, checker.IsNil)
+	outDir, err := os.MkdirTemp("", "cp-test-volumepath-out")
+	assert.NilError(c, err)
 	defer os.RemoveAll(outDir)
 	_, err = os.Create(tmpDir + "/test")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _ := dockerCmd(c, "run", "-d", "-v", "/foo", "-v", tmpDir+"/test:/test", "-v", tmpDir+":/baz", "busybox", "/bin/sh", "-c", "touch /foo/bar")
 
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
 	// Copy actual volume path
 	dockerCmd(c, "cp", containerID+":/foo", outDir)
 
 	stat, err := os.Stat(outDir + "/foo")
-	c.Assert(err, checker.IsNil)
-	// expected copied content to be dir
-	c.Assert(stat.IsDir(), checker.True)
+	assert.NilError(c, err)
+	assert.Assert(c, stat.IsDir(), "Expected copied content to be dir")
+
 	stat, err = os.Stat(outDir + "/foo/bar")
-	c.Assert(err, checker.IsNil)
-	// Expected file `bar` to be a file
-	c.Assert(stat.IsDir(), checker.False)
+	assert.NilError(c, err)
+	assert.Assert(c, !stat.IsDir(), "Expected file `bar` to be a file")
 
 	// Copy file nested in volume
 	dockerCmd(c, "cp", containerID+":/foo/bar", outDir)
 
 	stat, err = os.Stat(outDir + "/bar")
-	c.Assert(err, checker.IsNil)
-	// Expected file `bar` to be a file
-	c.Assert(stat.IsDir(), checker.False)
+	assert.NilError(c, err)
+	assert.Assert(c, !stat.IsDir(), "Expected file `bar` to be a file")
 
 	// Copy Bind-mounted dir
 	dockerCmd(c, "cp", containerID+":/baz", outDir)
 	stat, err = os.Stat(outDir + "/baz")
-	c.Assert(err, checker.IsNil)
-	// Expected `baz` to be a dir
-	c.Assert(stat.IsDir(), checker.True)
+	assert.NilError(c, err)
+	assert.Assert(c, stat.IsDir(), "Expected `baz` to be a dir")
 
 	// Copy file nested in bind-mounted dir
 	dockerCmd(c, "cp", containerID+":/baz/test", outDir)
-	fb, err := ioutil.ReadFile(outDir + "/baz/test")
-	c.Assert(err, checker.IsNil)
-	fb2, err := ioutil.ReadFile(tmpDir + "/test")
-	c.Assert(err, checker.IsNil)
-	// Expected copied file to be duplicate of bind-mounted file
-	c.Assert(bytes.Equal(fb, fb2), checker.True)
+	fb, err := os.ReadFile(outDir + "/baz/test")
+	assert.NilError(c, err)
+	fb2, err := os.ReadFile(tmpDir + "/test")
+	assert.NilError(c, err)
+	assert.Assert(c, bytes.Equal(fb, fb2), "Expected copied file to be duplicate of bind-mounted file")
 
 	// Copy bind-mounted file
 	dockerCmd(c, "cp", containerID+":/test", outDir)
-	fb, err = ioutil.ReadFile(outDir + "/test")
-	c.Assert(err, checker.IsNil)
-	fb2, err = ioutil.ReadFile(tmpDir + "/test")
-	c.Assert(err, checker.IsNil)
-	// Expected copied file to be duplicate of bind-mounted file
-	c.Assert(bytes.Equal(fb, fb2), checker.True)
+	fb, err = os.ReadFile(outDir + "/test")
+	assert.NilError(c, err)
+	fb2, err = os.ReadFile(tmpDir + "/test")
+	assert.NilError(c, err)
+	assert.Assert(c, bytes.Equal(fb, fb2), "Expected copied file to be duplicate of bind-mounted file")
 }
 
-func (s *DockerSuite) TestCpToDot(c *check.C) {
+func (s *DockerSuite) TestCpToDot(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test")
 
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
-	tmpdir, err := ioutil.TempDir("", "docker-integration")
-	c.Assert(err, checker.IsNil)
+	tmpdir, err := os.MkdirTemp("", "docker-integration")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmpdir)
 	cwd, err := os.Getwd()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer os.Chdir(cwd)
-	c.Assert(os.Chdir(tmpdir), checker.IsNil)
+	err = os.Chdir(tmpdir)
+	assert.NilError(c, err)
+
 	dockerCmd(c, "cp", containerID+":/test", ".")
-	content, err := ioutil.ReadFile("./test")
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Equals, "lololol\n")
+	content, err := os.ReadFile("./test")
+	assert.NilError(c, err)
+	assert.Equal(c, string(content), "lololol\n")
 }
 
-func (s *DockerSuite) TestCpToStdout(c *check.C) {
+func (s *DockerSuite) TestCpToStdout(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /test")
 
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
 	out, err := RunCommandPipelineWithOutput(
 		exec.Command(dockerBinary, "cp", containerID+":/test", "-"),
 		exec.Command("tar", "-vtf", "-"))
 
-	c.Assert(err, checker.IsNil)
-
-	c.Assert(out, checker.Contains, "test")
-	c.Assert(out, checker.Contains, "-rw")
+	assert.NilError(c, err)
+	assert.Check(c, is.Contains(out, "test"))
+	assert.Check(c, is.Contains(out, "-rw"))
 }
 
-func (s *DockerSuite) TestCpNameHasColon(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerSuite) TestCpNameHasColon(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "echo lololol > /te:s:t")
 
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
-	tmpdir, err := ioutil.TempDir("", "docker-integration")
-	c.Assert(err, checker.IsNil)
+	tmpdir, err := os.MkdirTemp("", "docker-integration")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmpdir)
 	dockerCmd(c, "cp", containerID+":/te:s:t", tmpdir)
-	content, err := ioutil.ReadFile(tmpdir + "/te:s:t")
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Equals, "lololol\n")
+	content, err := os.ReadFile(tmpdir + "/te:s:t")
+	assert.NilError(c, err)
+	assert.Equal(c, string(content), "lololol\n")
 }
 
-func (s *DockerSuite) TestCopyAndRestart(c *check.C) {
+func (s *DockerSuite) TestCopyAndRestart(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	expectedMsg := "hello"
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "echo", expectedMsg)
 	containerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", containerID)
-	// failed to set up container
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
-	tmpDir, err := ioutil.TempDir("", "test-docker-restart-after-copy-")
-	c.Assert(err, checker.IsNil)
+	tmpDir, err := os.MkdirTemp("", "test-docker-restart-after-copy-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmpDir)
 
 	dockerCmd(c, "cp", fmt.Sprintf("%s:/etc/group", containerID), tmpDir)
 
 	out, _ = dockerCmd(c, "start", "-a", containerID)
-
-	c.Assert(strings.TrimSpace(out), checker.Equals, expectedMsg)
+	assert.Equal(c, strings.TrimSpace(out), expectedMsg)
 }
 
-func (s *DockerSuite) TestCopyCreatedContainer(c *check.C) {
+func (s *DockerSuite) TestCopyCreatedContainer(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "create", "--name", "test_cp", "-v", "/test", "busybox")
 
-	tmpDir, err := ioutil.TempDir("", "test")
-	c.Assert(err, checker.IsNil)
+	tmpDir, err := os.MkdirTemp("", "test")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmpDir)
 	dockerCmd(c, "cp", "test_cp:/bin/sh", tmpDir)
 }
@@ -613,24 +565,18 @@ func (s *DockerSuite) TestCopyCreatedContainer(c *check.C) {
 // test copy with option `-L`: following symbol link
 // Check that symlinks to a file behave as expected when copying one from
 // a container to host following symbol link
-func (s *DockerSuite) TestCpSymlinkFromConToHostFollowSymlink(c *check.C) {
+func (s *DockerSuite) TestCpSymlinkFromConToHostFollowSymlink(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, exitCode := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath+" && ln -s "+cpFullPath+" /dir_link")
-	if exitCode != 0 {
-		c.Fatal("failed to create a container", out)
-	}
+	assert.Equal(c, exitCode, 0, "failed to set up container: %s", out)
 
 	cleanedContainerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "wait", cleanedContainerID)
-	if strings.TrimSpace(out) != "0" {
-		c.Fatal("failed to set up container", out)
-	}
+	assert.Equal(c, strings.TrimSpace(out), "0", "failed to set up container")
 
-	testDir, err := ioutil.TempDir("", "test-cp-symlink-container-to-host-follow-symlink")
-	if err != nil {
-		c.Fatal(err)
-	}
+	testDir, err := os.MkdirTemp("", "test-cp-symlink-container-to-host-follow-symlink")
+	assert.NilError(c, err)
 	defer os.RemoveAll(testDir)
 
 	// This copy command should copy the symlink, not the target, into the
@@ -640,13 +586,10 @@ func (s *DockerSuite) TestCpSymlinkFromConToHostFollowSymlink(c *check.C) {
 	expectedPath := filepath.Join(testDir, "dir_link")
 
 	expected := []byte(cpContainerContents)
-	actual, err := ioutil.ReadFile(expectedPath)
-	c.Assert(err, checker.IsNil)
-
-	if !bytes.Equal(actual, expected) {
-		c.Fatalf("Expected copied file to be duplicate of the container symbol link target")
-	}
+	actual, err := os.ReadFile(expectedPath)
+	assert.NilError(c, err)
 	os.Remove(expectedPath)
+	assert.Assert(c, bytes.Equal(actual, expected), "Expected copied file to be duplicate of the container symbol link target")
 
 	// now test copy symbol link to a non-existing file in host
 	expectedPath = filepath.Join(testDir, "somefile_host")
@@ -657,11 +600,8 @@ func (s *DockerSuite) TestCpSymlinkFromConToHostFollowSymlink(c *check.C) {
 
 	dockerCmd(c, "cp", "-L", cleanedContainerID+":"+"/dir_link", expectedPath)
 
-	actual, err = ioutil.ReadFile(expectedPath)
-	c.Assert(err, checker.IsNil)
-
-	if !bytes.Equal(actual, expected) {
-		c.Fatalf("Expected copied file to be duplicate of the container symbol link target")
-	}
+	actual, err = os.ReadFile(expectedPath)
+	assert.NilError(c, err)
 	defer os.Remove(expectedPath)
+	assert.Assert(c, bytes.Equal(actual, expected), "Expected copied file to be duplicate of the container symbol link target")
 }
diff --git a/integration-cli/docker_cli_cp_to_container_test.go b/integration-cli/docker_cli_cp_to_container_test.go
index 77567a3b955e9..6f28077f92f3f 100644
--- a/integration-cli/docker_cli_cp_to_container_test.go
+++ b/integration-cli/docker_cli_cp_to_container_test.go
@@ -2,9 +2,9 @@ package main
 
 import (
 	"os"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
 // Try all of the test cases from the archive package which implements the
@@ -19,11 +19,11 @@ import (
 
 // Check that copying from a local path to a symlink in a container copies to
 // the symlink target and does not overwrite the container symlink itself.
-func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
+func (s *DockerSuite) TestCpToSymlinkDestination(c *testing.T) {
 	//  stat /tmp/test-cp-to-symlink-destination-262430901/vol3 gets permission denied for the user
 	testRequires(c, NotUserNamespace)
 	testRequires(c, DaemonIsLinux)
-	testRequires(c, SameHostDaemon) // Requires local volume mount bind.
+	testRequires(c, testEnv.IsLocalDaemon) // Requires local volume mount bind.
 
 	testVol := getTestDir(c, "test-cp-to-symlink-destination-")
 	defer os.RemoveAll(testVol)
@@ -39,38 +39,26 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
 	srcPath := cpPath(testVol, "file2")
 	dstPath := containerCpPath(containerID, "/vol2/symlinkToFile1")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// The symlink should not have been modified.
-	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToFile1"), "file1"), checker.IsNil)
-
-	// The file should have the contents of "file2" now.
-	c.Assert(fileContentEquals(c, cpPath(testVol, "file1"), "file2\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, symlinkTargetEquals(c, cpPath(testVol, "symlinkToFile1"), "file1"), "The symlink should not have been modified")
+	assert.NilError(c, fileContentEquals(c, cpPath(testVol, "file1"), "file2\n"), `The file should have the contents of "file2" now`)
 
 	// Next, copy a local file to a symlink to a directory in the container.
 	// This should copy the file into the symlink target directory.
 	dstPath = containerCpPath(containerID, "/vol2/symlinkToDir1")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// The symlink should not have been modified.
-	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), checker.IsNil)
-
-	// The file should have the contents of "file2" now.
-	c.Assert(fileContentEquals(c, cpPath(testVol, "file2"), "file2\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), "The symlink should not have been modified")
+	assert.NilError(c, fileContentEquals(c, cpPath(testVol, "file2"), "file2\n"), `The file should have the contents of "file2"" now`)
 
 	// Next, copy a file to a symlink to a file that does not exist (a broken
 	// symlink) in the container. This should create the target file with the
 	// contents of the source file.
 	dstPath = containerCpPath(containerID, "/vol2/brokenSymlinkToFileX")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// The symlink should not have been modified.
-	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToFileX"), "fileX"), checker.IsNil)
-
-	// The file should have the contents of "file2" now.
-	c.Assert(fileContentEquals(c, cpPath(testVol, "fileX"), "file2\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToFileX"), "fileX"), "The symlink should not have been modified")
+	assert.NilError(c, fileContentEquals(c, cpPath(testVol, "fileX"), "file2\n"), `The file should have the contents of "file2"" now`)
 
 	// Next, copy a local directory to a symlink to a directory in the
 	// container. This should copy the directory into the symlink target
@@ -78,13 +66,9 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
 	srcPath = cpPath(testVol, "/dir2")
 	dstPath = containerCpPath(containerID, "/vol2/symlinkToDir1")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// The symlink should not have been modified.
-	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), checker.IsNil)
-
-	// The directory should now contain a copy of "dir2".
-	c.Assert(fileContentEquals(c, cpPath(testVol, "dir1/dir2/file2-1"), "file2-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, symlinkTargetEquals(c, cpPath(testVol, "symlinkToDir1"), "dir1"), "The symlink should not have been modified")
+	assert.NilError(c, fileContentEquals(c, cpPath(testVol, "dir1/dir2/file2-1"), "file2-1\n"), `The directory should now contain a copy of "dir2"`)
 
 	// Next, copy a local directory to a symlink to a local directory that does
 	// not exist (a broken symlink) in the container. This should create the
@@ -92,13 +76,9 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
 	// should not modify the symlink.
 	dstPath = containerCpPath(containerID, "/vol2/brokenSymlinkToDirX")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// The symlink should not have been modified.
-	c.Assert(symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToDirX"), "dirX"), checker.IsNil)
-
-	// The "dirX" directory should now be a copy of "dir2".
-	c.Assert(fileContentEquals(c, cpPath(testVol, "dirX/file2-1"), "file2-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, symlinkTargetEquals(c, cpPath(testVol, "brokenSymlinkToDirX"), "dirX"), "The symlink should not have been modified")
+	assert.NilError(c, fileContentEquals(c, cpPath(testVol, "dirX/file2-1"), "file2-1\n"), `The "dirX" directory should now be a copy of "dir2"`)
 }
 
 // Possibilities are reduced to the remaining 10 cases:
@@ -120,7 +100,7 @@ func (s *DockerSuite) TestCpToSymlinkDestination(c *check.C) {
 // A. SRC specifies a file and DST (no trailing path separator) doesn't
 //    exist. This should create a file with the name DST and copy the
 //    contents of the source file into it.
-func (s *DockerSuite) TestCpToCaseA(c *check.C) {
+func (s *DockerSuite) TestCpToCaseA(c *testing.T) {
 	containerID := makeTestContainer(c, testContainerOptions{
 		workDir: "/root", command: makeCatFileCommand("itWorks.txt"),
 	})
@@ -133,15 +113,14 @@ func (s *DockerSuite) TestCpToCaseA(c *check.C) {
 	srcPath := cpPath(tmpDir, "file1")
 	dstPath := containerCpPath(containerID, "/root/itWorks.txt")
 
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1\n"))
 }
 
 // B. SRC specifies a file and DST (with trailing path separator) doesn't
 //    exist. This should cause an error because the copy operation cannot
 //    create a directory when copying a single file.
-func (s *DockerSuite) TestCpToCaseB(c *check.C) {
+func (s *DockerSuite) TestCpToCaseB(c *testing.T) {
 	containerID := makeTestContainer(c, testContainerOptions{
 		command: makeCatFileCommand("testDir/file1"),
 	})
@@ -154,15 +133,14 @@ func (s *DockerSuite) TestCpToCaseB(c *check.C) {
 	srcPath := cpPath(tmpDir, "file1")
 	dstDir := containerCpPathTrailingSep(containerID, "testDir")
 
-	err := runDockerCp(c, srcPath, dstDir, nil)
-	c.Assert(err, checker.NotNil)
-
-	c.Assert(isCpDirNotExist(err), checker.True, check.Commentf("expected DirNotExists error, but got %T: %s", err, err))
+	err := runDockerCp(c, srcPath, dstDir)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, isCpDirNotExist(err), "expected DirNotExists error, but got %T: %s", err, err)
 }
 
 // C. SRC specifies a file and DST exists as a file. This should overwrite
 //    the file at DST with the contents of the source file.
-func (s *DockerSuite) TestCpToCaseC(c *check.C) {
+func (s *DockerSuite) TestCpToCaseC(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -178,18 +156,15 @@ func (s *DockerSuite) TestCpToCaseC(c *check.C) {
 	dstPath := containerCpPath(containerID, "/root/file2")
 
 	// Ensure the container's file starts with the original content.
-	c.Assert(containerStartOutputEquals(c, containerID, "file2\n"), checker.IsNil)
-
-	c.Assert(runDockerCp(c, srcPath, dstPath, nil), checker.IsNil)
-
-	// Should now contain file1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file2\n"))
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1\n"), "Should now contain file1's contents")
 }
 
 // D. SRC specifies a file and DST exists as a directory. This should place
 //    a copy of the source file inside it using the basename from SRC. Ensure
 //    this works whether DST has a trailing path separator or not.
-func (s *DockerSuite) TestCpToCaseD(c *check.C) {
+func (s *DockerSuite) TestCpToCaseD(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true,
@@ -204,13 +179,9 @@ func (s *DockerSuite) TestCpToCaseD(c *check.C) {
 	srcPath := cpPath(tmpDir, "file1")
 	dstDir := containerCpPath(containerID, "dir1")
 
-	// Ensure that dstPath doesn't exist.
-	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
-
-	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
-
-	// Should now contain file1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
+	assert.NilError(c, containerStartOutputEquals(c, containerID, ""), "dstPath should not have existed")
+	assert.NilError(c, runDockerCp(c, srcPath, dstDir))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1\n"), "Should now contain file1's contents")
 
 	// Now try again but using a trailing path separator for dstDir.
 
@@ -222,20 +193,16 @@ func (s *DockerSuite) TestCpToCaseD(c *check.C) {
 
 	dstDir = containerCpPathTrailingSep(containerID, "dir1")
 
-	// Ensure that dstPath doesn't exist.
-	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
-
-	c.Assert(runDockerCp(c, srcPath, dstDir, nil), checker.IsNil)
-
-	// Should now contain file1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1\n"), checker.IsNil)
+	assert.NilError(c, containerStartOutputEquals(c, containerID, ""), "dstPath should not have existed")
+	assert.NilError(c, runDockerCp(c, srcPath, dstDir))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1\n"), "Should now contain file1's contents")
 }
 
 // E. SRC specifies a directory and DST does not exist. This should create a
 //    directory at DST and copy the contents of the SRC directory into the DST
 //    directory. Ensure this works whether DST has a trailing path separator or
 //    not.
-func (s *DockerSuite) TestCpToCaseE(c *check.C) {
+func (s *DockerSuite) TestCpToCaseE(c *testing.T) {
 	containerID := makeTestContainer(c, testContainerOptions{
 		command: makeCatFileCommand("/testDir/file1-1"),
 	})
@@ -248,10 +215,8 @@ func (s *DockerSuite) TestCpToCaseE(c *check.C) {
 	srcDir := cpPath(tmpDir, "dir1")
 	dstDir := containerCpPath(containerID, "testDir")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	// Should now contain file1-1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1-1\n"), "Should now contain file1-1's contents")
 
 	// Now try again but using a trailing path separator for dstDir.
 
@@ -262,15 +227,13 @@ func (s *DockerSuite) TestCpToCaseE(c *check.C) {
 
 	dstDir = containerCpPathTrailingSep(containerID, "testDir")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	// Should now contain file1-1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1-1\n"), "Should now contain file1-1's contents")
 }
 
 // F. SRC specifies a directory and DST exists as a file. This should cause an
 //    error as it is not possible to overwrite a file with a directory.
-func (s *DockerSuite) TestCpToCaseF(c *check.C) {
+func (s *DockerSuite) TestCpToCaseF(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -284,16 +247,15 @@ func (s *DockerSuite) TestCpToCaseF(c *check.C) {
 	srcDir := cpPath(tmpDir, "dir1")
 	dstFile := containerCpPath(containerID, "/root/file1")
 
-	err := runDockerCp(c, srcDir, dstFile, nil)
-	c.Assert(err, checker.NotNil)
-
-	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
+	err := runDockerCp(c, srcDir, dstFile)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, isCpCannotCopyDir(err), "expected ErrCannotCopyDir error, but got %T: %s", err, err)
 }
 
 // G. SRC specifies a directory and DST exists as a directory. This should copy
 //    the SRC directory and all its contents to the DST directory. Ensure this
 //    works whether DST has a trailing path separator or not.
-func (s *DockerSuite) TestCpToCaseG(c *check.C) {
+func (s *DockerSuite) TestCpToCaseG(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -308,13 +270,9 @@ func (s *DockerSuite) TestCpToCaseG(c *check.C) {
 	srcDir := cpPath(tmpDir, "dir1")
 	dstDir := containerCpPath(containerID, "/root/dir2")
 
-	// Ensure that dstPath doesn't exist.
-	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
-
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	// Should now contain file1-1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, containerStartOutputEquals(c, containerID, ""), "dstPath should not have existed")
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1-1\n"), "Should now contain file1-1's contents")
 
 	// Now try again but using a trailing path separator for dstDir.
 
@@ -326,20 +284,16 @@ func (s *DockerSuite) TestCpToCaseG(c *check.C) {
 
 	dstDir = containerCpPathTrailingSep(containerID, "/dir2")
 
-	// Ensure that dstPath doesn't exist.
-	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
-
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	// Should now contain file1-1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, containerStartOutputEquals(c, containerID, ""), "dstPath should not have existed")
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1-1\n"), "Should now contain file1-1's contents")
 }
 
 // H. SRC specifies a directory's contents only and DST does not exist. This
 //    should create a directory at DST and copy the contents of the SRC
 //    directory (but not the directory itself) into the DST directory. Ensure
 //    this works whether DST has a trailing path separator or not.
-func (s *DockerSuite) TestCpToCaseH(c *check.C) {
+func (s *DockerSuite) TestCpToCaseH(c *testing.T) {
 	containerID := makeTestContainer(c, testContainerOptions{
 		command: makeCatFileCommand("/testDir/file1-1"),
 	})
@@ -352,10 +306,8 @@ func (s *DockerSuite) TestCpToCaseH(c *check.C) {
 	srcDir := cpPathTrailingSep(tmpDir, "dir1") + "."
 	dstDir := containerCpPath(containerID, "testDir")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	// Should now contain file1-1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1-1\n"), "Should now contain file1-1's contents")
 
 	// Now try again but using a trailing path separator for dstDir.
 
@@ -366,16 +318,14 @@ func (s *DockerSuite) TestCpToCaseH(c *check.C) {
 
 	dstDir = containerCpPathTrailingSep(containerID, "testDir")
 
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	// Should now contain file1-1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1-1\n"), "Should now contain file1-1's contents")
 }
 
 // I. SRC specifies a directory's contents only and DST exists as a file. This
 //    should cause an error as it is not possible to overwrite a file with a
 //    directory.
-func (s *DockerSuite) TestCpToCaseI(c *check.C) {
+func (s *DockerSuite) TestCpToCaseI(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -389,17 +339,16 @@ func (s *DockerSuite) TestCpToCaseI(c *check.C) {
 	srcDir := cpPathTrailingSep(tmpDir, "dir1") + "."
 	dstFile := containerCpPath(containerID, "/root/file1")
 
-	err := runDockerCp(c, srcDir, dstFile, nil)
-	c.Assert(err, checker.NotNil)
-
-	c.Assert(isCpCannotCopyDir(err), checker.True, check.Commentf("expected ErrCannotCopyDir error, but got %T: %s", err, err))
+	err := runDockerCp(c, srcDir, dstFile)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, isCpCannotCopyDir(err), "expected ErrCannotCopyDir error, but got %T: %s", err, err)
 }
 
 // J. SRC specifies a directory's contents only and DST exists as a directory.
 //    This should copy the contents of the SRC directory (but not the directory
 //    itself) into the DST directory. Ensure this works whether DST has a
 //    trailing path separator or not.
-func (s *DockerSuite) TestCpToCaseJ(c *check.C) {
+func (s *DockerSuite) TestCpToCaseJ(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	containerID := makeTestContainer(c, testContainerOptions{
 		addContent: true, workDir: "/root",
@@ -414,13 +363,9 @@ func (s *DockerSuite) TestCpToCaseJ(c *check.C) {
 	srcDir := cpPathTrailingSep(tmpDir, "dir1") + "."
 	dstDir := containerCpPath(containerID, "/dir2")
 
-	// Ensure that dstPath doesn't exist.
-	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
-
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	// Should now contain file1-1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, containerStartOutputEquals(c, containerID, ""), "dstPath should not have existed")
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1-1\n"), "Should've contained file1-1's contents")
 
 	// Now try again but using a trailing path separator for dstDir.
 
@@ -431,18 +376,14 @@ func (s *DockerSuite) TestCpToCaseJ(c *check.C) {
 
 	dstDir = containerCpPathTrailingSep(containerID, "/dir2")
 
-	// Ensure that dstPath doesn't exist.
-	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
-
-	c.Assert(runDockerCp(c, srcDir, dstDir, nil), checker.IsNil)
-
-	// Should now contain file1-1's contents.
-	c.Assert(containerStartOutputEquals(c, containerID, "file1-1\n"), checker.IsNil)
+	assert.NilError(c, containerStartOutputEquals(c, containerID, ""), "dstPath should not have existed")
+	assert.NilError(c, runDockerCp(c, srcDir, dstDir))
+	assert.NilError(c, containerStartOutputEquals(c, containerID, "file1-1\n"), "Should've contained file1-1's contents")
 }
 
 // The `docker cp` command should also ensure that you cannot
 // write to a container rootfs that is marked as read-only.
-func (s *DockerSuite) TestCpToErrReadOnlyRootfs(c *check.C) {
+func (s *DockerSuite) TestCpToErrReadOnlyRootfs(c *testing.T) {
 	// --read-only + userns has remount issues
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	tmpDir := getTestDir(c, "test-cp-to-err-read-only-rootfs")
@@ -458,18 +399,16 @@ func (s *DockerSuite) TestCpToErrReadOnlyRootfs(c *check.C) {
 	srcPath := cpPath(tmpDir, "file1")
 	dstPath := containerCpPath(containerID, "/root/shouldNotExist")
 
-	err := runDockerCp(c, srcPath, dstPath, nil)
-	c.Assert(err, checker.NotNil)
+	err := runDockerCp(c, srcPath, dstPath)
+	assert.ErrorContains(c, err, "")
 
-	c.Assert(isCpCannotCopyReadOnly(err), checker.True, check.Commentf("expected ErrContainerRootfsReadonly error, but got %T: %s", err, err))
-
-	// Ensure that dstPath doesn't exist.
-	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
+	assert.Assert(c, isCpCannotCopyReadOnly(err), "expected ErrContainerRootfsReadonly error, but got %T: %s", err, err)
+	assert.NilError(c, containerStartOutputEquals(c, containerID, ""), "dstPath should not have existed")
 }
 
 // The `docker cp` command should also ensure that you
 // cannot write to a volume that is mounted as read-only.
-func (s *DockerSuite) TestCpToErrReadOnlyVolume(c *check.C) {
+func (s *DockerSuite) TestCpToErrReadOnlyVolume(c *testing.T) {
 	// --read-only + userns has remount issues
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	tmpDir := getTestDir(c, "test-cp-to-err-read-only-volume")
@@ -485,11 +424,9 @@ func (s *DockerSuite) TestCpToErrReadOnlyVolume(c *check.C) {
 	srcPath := cpPath(tmpDir, "file1")
 	dstPath := containerCpPath(containerID, "/vol_ro/shouldNotExist")
 
-	err := runDockerCp(c, srcPath, dstPath, nil)
-	c.Assert(err, checker.NotNil)
-
-	c.Assert(isCpCannotCopyReadOnly(err), checker.True, check.Commentf("expected ErrVolumeReadonly error, but got %T: %s", err, err))
+	err := runDockerCp(c, srcPath, dstPath)
+	assert.ErrorContains(c, err, "")
 
-	// Ensure that dstPath doesn't exist.
-	c.Assert(containerStartOutputEquals(c, containerID, ""), checker.IsNil)
+	assert.Assert(c, isCpCannotCopyReadOnly(err), "expected ErrVolumeReadonly error, but got %T: %s", err, err)
+	assert.NilError(c, containerStartOutputEquals(c, containerID, ""), "dstPath should not have existed")
 }
diff --git a/integration-cli/docker_cli_cp_to_container_unix_test.go b/integration-cli/docker_cli_cp_to_container_unix_test.go
index 8f830dcf9d002..80e668c2ec45b 100644
--- a/integration-cli/docker_cli_cp_to_container_unix_test.go
+++ b/integration-cli/docker_cli_cp_to_container_unix_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -5,17 +6,18 @@ package main
 import (
 	"fmt"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"strconv"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/pkg/system"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestCpToContainerWithPermissions(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerSuite) TestCpToContainerWithPermissions(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	tmpDir := getTestDir(c, "test-cp-to-host-with-permissions")
 	defer os.RemoveAll(tmpDir)
@@ -24,22 +26,25 @@ func (s *DockerSuite) TestCpToContainerWithPermissions(c *check.C) {
 
 	containerName := "permtest"
 
-	_, exc := dockerCmd(c, "create", "--name", containerName, "debian:jessie", "/bin/bash", "-c", "stat -c '%u %g %a' /permdirtest /permdirtest/permtest")
-	c.Assert(exc, checker.Equals, 0)
+	_, exc := dockerCmd(c, "create", "--name", containerName, "busybox", "/bin/sh", "-c", "stat -c '%u %g %a' /permdirtest /permdirtest/permtest")
+	assert.Equal(c, exc, 0)
 	defer dockerCmd(c, "rm", "-f", containerName)
 
 	srcPath := cpPath(tmpDir, "permdirtest")
 	dstPath := containerCpPath(containerName, "/")
-	c.Assert(runDockerCp(c, srcPath, dstPath, []string{"-a"}), checker.IsNil)
 
-	out, err := startContainerGetOutput(c, containerName)
-	c.Assert(err, checker.IsNil, check.Commentf("output: %v", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "2 2 700\n65534 65534 400", check.Commentf("output: %v", out))
+	args := []string{"cp", "-a", srcPath, dstPath}
+	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, args...))
+	assert.NilError(c, err, "output: %v", out)
+
+	out, err = startContainerGetOutput(c, containerName)
+	assert.NilError(c, err, "output: %v", out)
+	assert.Equal(c, strings.TrimSpace(out), "2 2 700\n65534 65534 400", "output: %v", out)
 }
 
 // Check ownership is root, both in non-userns and userns enabled modes
-func (s *DockerSuite) TestCpCheckDestOwnership(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestCpCheckDestOwnership(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	tmpVolDir := getTestDir(c, "test-cp-tmpvol")
 	containerID := makeTestContainer(c,
 		testContainerOptions{volumes: []string{fmt.Sprintf("%s:/tmpvol", tmpVolDir)}})
@@ -52,21 +57,20 @@ func (s *DockerSuite) TestCpCheckDestOwnership(c *check.C) {
 	srcPath := cpPath(tmpDir, "file1")
 	dstPath := containerCpPath(containerID, "/tmpvol", "file1")
 
-	err := runDockerCp(c, srcPath, dstPath, nil)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, runDockerCp(c, srcPath, dstPath))
 
 	stat, err := system.Stat(filepath.Join(tmpVolDir, "file1"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	uid, gid, err := getRootUIDGID()
-	c.Assert(err, checker.IsNil)
-	c.Assert(stat.UID(), checker.Equals, uint32(uid), check.Commentf("Copied file not owned by container root UID"))
-	c.Assert(stat.GID(), checker.Equals, uint32(gid), check.Commentf("Copied file not owned by container root GID"))
+	assert.NilError(c, err)
+	assert.Equal(c, stat.UID(), uint32(uid), "Copied file not owned by container root UID")
+	assert.Equal(c, stat.GID(), uint32(gid), "Copied file not owned by container root GID")
 }
 
 func getRootUIDGID() (int, int, error) {
 	uidgid := strings.Split(filepath.Base(testEnv.DaemonInfo.DockerRootDir), ".")
 	if len(uidgid) == 1 {
-		//user namespace remapping is not turned on; return 0
+		// user namespace remapping is not turned on; return 0
 		return 0, 0, nil
 	}
 	uid, err := strconv.Atoi(uidgid[0])
diff --git a/integration-cli/docker_cli_cp_utils_test.go b/integration-cli/docker_cli_cp_utils_test.go
index 79a016f0c613a..4ac95f50599f0 100644
--- a/integration-cli/docker_cli_cp_utils_test.go
+++ b/integration-cli/docker_cli_cp_utils_test.go
@@ -3,16 +3,16 @@ package main
 import (
 	"bytes"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"runtime"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/pkg/archive"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
 type fileType uint32
@@ -92,20 +92,21 @@ func defaultMkContentCommand() string {
 	return mkFilesCommand(defaultFileData)
 }
 
-func makeTestContentInDir(c *check.C, dir string) {
+func makeTestContentInDir(c *testing.T, dir string) {
+	c.Helper()
 	for _, fd := range defaultFileData {
 		path := filepath.Join(dir, filepath.FromSlash(fd.path))
 		switch fd.filetype {
 		case ftRegular:
-			c.Assert(ioutil.WriteFile(path, []byte(fd.contents+"\n"), os.FileMode(fd.mode)), checker.IsNil)
+			assert.NilError(c, os.WriteFile(path, []byte(fd.contents+"\n"), os.FileMode(fd.mode)))
 		case ftDir:
-			c.Assert(os.Mkdir(path, os.FileMode(fd.mode)), checker.IsNil)
+			assert.NilError(c, os.Mkdir(path, os.FileMode(fd.mode)))
 		case ftSymlink:
-			c.Assert(os.Symlink(fd.contents, path), checker.IsNil)
+			assert.NilError(c, os.Symlink(fd.contents, path))
 		}
 
 		if fd.filetype != ftSymlink && runtime.GOOS != "windows" {
-			c.Assert(os.Chown(path, fd.uid, fd.gid), checker.IsNil)
+			assert.NilError(c, os.Chown(path, fd.uid, fd.gid))
 		}
 	}
 }
@@ -118,7 +119,8 @@ type testContainerOptions struct {
 	command    string
 }
 
-func makeTestContainer(c *check.C, options testContainerOptions) (containerID string) {
+func makeTestContainer(c *testing.T, options testContainerOptions) (containerID string) {
+	c.Helper()
 	if options.addContent {
 		mkContentCmd := defaultMkContentCommand()
 		if options.command == "" {
@@ -158,7 +160,7 @@ func makeTestContainer(c *check.C, options testContainerOptions) (containerID st
 	if exitCode != "0" {
 		out, _ = dockerCmd(c, "logs", containerID)
 	}
-	c.Assert(exitCode, checker.Equals, "0", check.Commentf("failed to make test container: %s", out))
+	assert.Equal(c, exitCode, "0", "failed to make test container: %s", out)
 
 	return
 }
@@ -188,25 +190,18 @@ func containerCpPathTrailingSep(containerID string, pathElements ...string) stri
 	return fmt.Sprintf("%s/", containerCpPath(containerID, pathElements...))
 }
 
-func runDockerCp(c *check.C, src, dst string, params []string) (err error) {
-	c.Logf("running `docker cp %s %s %s`", strings.Join(params, " "), src, dst)
+func runDockerCp(c *testing.T, src, dst string) error {
+	c.Helper()
 
-	args := []string{"cp"}
-
-	args = append(args, params...)
-
-	args = append(args, src, dst)
-
-	out, _, err := runCommandWithOutput(exec.Command(dockerBinary, args...))
-	if err != nil {
-		err = fmt.Errorf("error executing `docker cp` command: %s: %s", err, out)
+	args := []string{"cp", src, dst}
+	if out, _, err := runCommandWithOutput(exec.Command(dockerBinary, args...)); err != nil {
+		return fmt.Errorf("error executing `docker cp` command: %s: %s", err, out)
 	}
-
-	return
+	return nil
 }
 
-func startContainerGetOutput(c *check.C, containerID string) (out string, err error) {
-	c.Logf("running `docker start -a %s`", containerID)
+func startContainerGetOutput(c *testing.T, containerID string) (out string, err error) {
+	c.Helper()
 
 	args := []string{"start", "-a", containerID}
 
@@ -218,12 +213,13 @@ func startContainerGetOutput(c *check.C, containerID string) (out string, err er
 	return
 }
 
-func getTestDir(c *check.C, label string) (tmpDir string) {
+func getTestDir(c *testing.T, label string) (tmpDir string) {
+	c.Helper()
 	var err error
 
-	tmpDir, err = ioutil.TempDir("", label)
+	tmpDir, err = os.MkdirTemp("", label)
 	// unable to make temporary directory
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	return
 }
@@ -240,58 +236,58 @@ func isCpCannotCopyReadOnly(err error) bool {
 	return strings.Contains(err.Error(), "marked read-only")
 }
 
-func fileContentEquals(c *check.C, filename, contents string) (err error) {
-	c.Logf("checking that file %q contains %q\n", filename, contents)
+func fileContentEquals(c *testing.T, filename, contents string) error {
+	c.Helper()
 
-	fileBytes, err := ioutil.ReadFile(filename)
+	fileBytes, err := os.ReadFile(filename)
 	if err != nil {
-		return
+		return err
 	}
 
-	expectedBytes, err := ioutil.ReadAll(strings.NewReader(contents))
+	expectedBytes, err := io.ReadAll(strings.NewReader(contents))
 	if err != nil {
-		return
+		return err
 	}
 
 	if !bytes.Equal(fileBytes, expectedBytes) {
-		err = fmt.Errorf("file content not equal - expected %q, got %q", string(expectedBytes), string(fileBytes))
+		return fmt.Errorf("file content not equal - expected %q, got %q", string(expectedBytes), string(fileBytes))
 	}
 
-	return
+	return nil
 }
 
-func symlinkTargetEquals(c *check.C, symlink, expectedTarget string) (err error) {
-	c.Logf("checking that the symlink %q points to %q\n", symlink, expectedTarget)
+func symlinkTargetEquals(c *testing.T, symlink, expectedTarget string) error {
+	c.Helper()
 
 	actualTarget, err := os.Readlink(symlink)
 	if err != nil {
-		return
+		return err
 	}
 
 	if actualTarget != expectedTarget {
-		err = fmt.Errorf("symlink target points to %q not %q", actualTarget, expectedTarget)
+		return fmt.Errorf("symlink target points to %q not %q", actualTarget, expectedTarget)
 	}
 
-	return
+	return nil
 }
 
-func containerStartOutputEquals(c *check.C, containerID, contents string) (err error) {
-	c.Logf("checking that container %q start output contains %q\n", containerID, contents)
+func containerStartOutputEquals(c *testing.T, containerID, contents string) error {
+	c.Helper()
 
 	out, err := startContainerGetOutput(c, containerID)
 	if err != nil {
-		return
+		return err
 	}
 
 	if out != contents {
-		err = fmt.Errorf("output contents not equal - expected %q, got %q", contents, out)
+		return fmt.Errorf("output contents not equal - expected %q, got %q", contents, out)
 	}
 
-	return
+	return nil
 }
 
 func defaultVolumes(tmpDir string) []string {
-	if SameHostDaemon() {
+	if testEnv.IsLocalDaemon() {
 		return []string{
 			"/vol1",
 			fmt.Sprintf("%s:/vol2", tmpDir),
diff --git a/integration-cli/docker_cli_create_test.go b/integration-cli/docker_cli_create_test.go
index cb4de73c5e94f..3b2828ada3e26 100644
--- a/integration-cli/docker_cli_create_test.go
+++ b/integration-cli/docker_cli_create_test.go
@@ -6,19 +6,19 @@ import (
 	"os"
 	"reflect"
 	"strings"
-	"time"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/docker/docker/internal/test/fakecontext"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/docker/docker/testutil/fakecontext"
 	"github.com/docker/go-connections/nat"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 // Make sure we can create a simple container with some args
-func (s *DockerSuite) TestCreateArgs(c *check.C) {
+func (s *DockerSuite) TestCreateArgs(c *testing.T) {
 	// Intentionally clear entrypoint, as the Windows busybox image needs an entrypoint, which breaks this test
 	out, _ := dockerCmd(c, "create", "--entrypoint=", "busybox", "command", "arg1", "arg2", "arg with space", "-c", "flags")
 
@@ -27,19 +27,16 @@ func (s *DockerSuite) TestCreateArgs(c *check.C) {
 	out, _ = dockerCmd(c, "inspect", cleanedContainerID)
 
 	var containers []struct {
-		ID      string
-		Created time.Time
-		Path    string
-		Args    []string
-		Image   string
+		Path string
+		Args []string
 	}
 
 	err := json.Unmarshal([]byte(out), &containers)
-	c.Assert(err, check.IsNil, check.Commentf("Error inspecting the container: %s", err))
-	c.Assert(containers, checker.HasLen, 1)
+	assert.Assert(c, err == nil, "Error inspecting the container: %s", err)
+	assert.Equal(c, len(containers), 1)
 
 	cont := containers[0]
-	c.Assert(string(cont.Path), checker.Equals, "command", check.Commentf("Unexpected container path. Expected command, received: %s", cont.Path))
+	assert.Equal(c, cont.Path, "command", fmt.Sprintf("Unexpected container path. Expected command, received: %s", cont.Path))
 
 	b := false
 	expected := []string{"arg1", "arg2", "arg with space", "-c", "flags"}
@@ -56,7 +53,7 @@ func (s *DockerSuite) TestCreateArgs(c *check.C) {
 }
 
 // Make sure we can grow the container's rootfs at creation time.
-func (s *DockerSuite) TestCreateGrowRootfs(c *check.C) {
+func (s *DockerSuite) TestCreateGrowRootfs(c *testing.T) {
 	// Windows and Devicemapper support growing the rootfs
 	if testEnv.OSType != "windows" {
 		testRequires(c, Devicemapper)
@@ -66,21 +63,21 @@ func (s *DockerSuite) TestCreateGrowRootfs(c *check.C) {
 	cleanedContainerID := strings.TrimSpace(out)
 
 	inspectOut := inspectField(c, cleanedContainerID, "HostConfig.StorageOpt")
-	c.Assert(inspectOut, checker.Equals, "map[size:120G]")
+	assert.Equal(c, inspectOut, "map[size:120G]")
 }
 
 // Make sure we cannot shrink the container's rootfs at creation time.
-func (s *DockerSuite) TestCreateShrinkRootfs(c *check.C) {
+func (s *DockerSuite) TestCreateShrinkRootfs(c *testing.T) {
 	testRequires(c, Devicemapper)
 
 	// Ensure this fails because of the defaultBaseFsSize is 10G
 	out, _, err := dockerCmdWithError("create", "--storage-opt", "size=5G", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "Container size cannot be smaller than")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "Container size cannot be smaller than"))
 }
 
 // Make sure we can set hostconfig options too
-func (s *DockerSuite) TestCreateHostConfig(c *check.C) {
+func (s *DockerSuite) TestCreateHostConfig(c *testing.T) {
 	out, _ := dockerCmd(c, "create", "-P", "busybox", "echo")
 
 	cleanedContainerID := strings.TrimSpace(out)
@@ -94,15 +91,15 @@ func (s *DockerSuite) TestCreateHostConfig(c *check.C) {
 	}
 
 	err := json.Unmarshal([]byte(out), &containers)
-	c.Assert(err, check.IsNil, check.Commentf("Error inspecting the container: %s", err))
-	c.Assert(containers, checker.HasLen, 1)
+	assert.Assert(c, err == nil, "Error inspecting the container: %s", err)
+	assert.Equal(c, len(containers), 1)
 
 	cont := containers[0]
-	c.Assert(cont.HostConfig, check.NotNil, check.Commentf("Expected HostConfig, got none"))
-	c.Assert(cont.HostConfig.PublishAllPorts, check.NotNil, check.Commentf("Expected PublishAllPorts, got false"))
+	assert.Assert(c, cont.HostConfig != nil, "Expected HostConfig, got none")
+	assert.Assert(c, cont.HostConfig.PublishAllPorts, "Expected PublishAllPorts, got false")
 }
 
-func (s *DockerSuite) TestCreateWithPortRange(c *check.C) {
+func (s *DockerSuite) TestCreateWithPortRange(c *testing.T) {
 	out, _ := dockerCmd(c, "create", "-p", "3300-3303:3300-3303/tcp", "busybox", "echo")
 
 	cleanedContainerID := strings.TrimSpace(out)
@@ -115,23 +112,23 @@ func (s *DockerSuite) TestCreateWithPortRange(c *check.C) {
 		}
 	}
 	err := json.Unmarshal([]byte(out), &containers)
-	c.Assert(err, check.IsNil, check.Commentf("Error inspecting the container: %s", err))
-	c.Assert(containers, checker.HasLen, 1)
+	assert.Assert(c, err == nil, "Error inspecting the container: %s", err)
+	assert.Equal(c, len(containers), 1)
 
 	cont := containers[0]
 
-	c.Assert(cont.HostConfig, check.NotNil, check.Commentf("Expected HostConfig, got none"))
-	c.Assert(cont.HostConfig.PortBindings, checker.HasLen, 4, check.Commentf("Expected 4 ports bindings, got %d", len(cont.HostConfig.PortBindings)))
+	assert.Assert(c, cont.HostConfig != nil, "Expected HostConfig, got none")
+	assert.Equal(c, len(cont.HostConfig.PortBindings), 4, fmt.Sprintf("Expected 4 ports bindings, got %d", len(cont.HostConfig.PortBindings)))
 
 	for k, v := range cont.HostConfig.PortBindings {
-		c.Assert(v, checker.HasLen, 1, check.Commentf("Expected 1 ports binding, for the port  %s but found %s", k, v))
-		c.Assert(k.Port(), checker.Equals, v[0].HostPort, check.Commentf("Expected host port %s to match published port %s", k.Port(), v[0].HostPort))
+		assert.Equal(c, len(v), 1, fmt.Sprintf("Expected 1 ports binding, for the port  %s but found %s", k, v))
+		assert.Equal(c, k.Port(), v[0].HostPort, fmt.Sprintf("Expected host port %s to match published port %s", k.Port(), v[0].HostPort))
 
 	}
 
 }
 
-func (s *DockerSuite) TestCreateWithLargePortRange(c *check.C) {
+func (s *DockerSuite) TestCreateWithLargePortRange(c *testing.T) {
 	out, _ := dockerCmd(c, "create", "-p", "1-65535:1-65535/tcp", "busybox", "echo")
 
 	cleanedContainerID := strings.TrimSpace(out)
@@ -145,40 +142,39 @@ func (s *DockerSuite) TestCreateWithLargePortRange(c *check.C) {
 	}
 
 	err := json.Unmarshal([]byte(out), &containers)
-	c.Assert(err, check.IsNil, check.Commentf("Error inspecting the container: %s", err))
-	c.Assert(containers, checker.HasLen, 1)
+	assert.Assert(c, err == nil, "Error inspecting the container: %s", err)
+	assert.Equal(c, len(containers), 1)
 
 	cont := containers[0]
-	c.Assert(cont.HostConfig, check.NotNil, check.Commentf("Expected HostConfig, got none"))
-	c.Assert(cont.HostConfig.PortBindings, checker.HasLen, 65535)
+	assert.Assert(c, cont.HostConfig != nil, "Expected HostConfig, got none")
+	assert.Equal(c, len(cont.HostConfig.PortBindings), 65535)
 
 	for k, v := range cont.HostConfig.PortBindings {
-		c.Assert(v, checker.HasLen, 1)
-		c.Assert(k.Port(), checker.Equals, v[0].HostPort, check.Commentf("Expected host port %s to match published port %s", k.Port(), v[0].HostPort))
+		assert.Equal(c, len(v), 1)
+		assert.Equal(c, k.Port(), v[0].HostPort, fmt.Sprintf("Expected host port %s to match published port %s", k.Port(), v[0].HostPort))
 	}
 
 }
 
 // "test123" should be printed by docker create + start
-func (s *DockerSuite) TestCreateEchoStdout(c *check.C) {
+func (s *DockerSuite) TestCreateEchoStdout(c *testing.T) {
 	out, _ := dockerCmd(c, "create", "busybox", "echo", "test123")
 
 	cleanedContainerID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "start", "-ai", cleanedContainerID)
-	c.Assert(out, checker.Equals, "test123\n", check.Commentf("container should've printed 'test123', got %q", out))
-
+	assert.Equal(c, out, "test123\n", "container should've printed 'test123', got %q", out)
 }
 
-func (s *DockerSuite) TestCreateVolumesCreated(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestCreateVolumesCreated(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 
 	name := "test_create_volume"
 	dockerCmd(c, "create", "--name", name, "-v", prefix+slash+"foo", "busybox")
 
 	dir, err := inspectMountSourceField(name, prefix+slash+"foo")
-	c.Assert(err, check.IsNil, check.Commentf("Error getting volume host path: %q", err))
+	assert.Assert(c, err == nil, "Error getting volume host path: %q", err)
 
 	if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
 		c.Fatalf("Volume was not created")
@@ -189,7 +185,7 @@ func (s *DockerSuite) TestCreateVolumesCreated(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestCreateLabels(c *check.C) {
+func (s *DockerSuite) TestCreateLabels(c *testing.T) {
 	name := "test_create_labels"
 	expected := map[string]string{"k1": "v1", "k2": "v2"}
 	dockerCmd(c, "create", "--name", name, "-l", "k1=v1", "--label", "k2=v2", "busybox")
@@ -202,7 +198,7 @@ func (s *DockerSuite) TestCreateLabels(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestCreateLabelFromImage(c *check.C) {
+func (s *DockerSuite) TestCreateLabelFromImage(c *testing.T) {
 	imageName := "testcreatebuildlabel"
 	buildImageSuccessfully(c, imageName, build.WithDockerfile(`FROM busybox
 		LABEL k1=v1 k2=v2`))
@@ -219,18 +215,17 @@ func (s *DockerSuite) TestCreateLabelFromImage(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestCreateHostnameWithNumber(c *check.C) {
+func (s *DockerSuite) TestCreateHostnameWithNumber(c *testing.T) {
 	image := "busybox"
 	// Busybox on Windows does not implement hostname command
 	if testEnv.OSType == "windows" {
 		image = testEnv.PlatformDefaults.BaseImage
 	}
 	out, _ := dockerCmd(c, "run", "-h", "web.0", image, "hostname")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "web.0", check.Commentf("hostname not set, expected `web.0`, got: %s", out))
-
+	assert.Equal(c, strings.TrimSpace(out), "web.0", "hostname not set, expected `web.0`, got: %s", out)
 }
 
-func (s *DockerSuite) TestCreateRM(c *check.C) {
+func (s *DockerSuite) TestCreateRM(c *testing.T) {
 	// Test to make sure we can 'rm' a new container that is in
 	// "Created" state, and has ever been run. Test "rm -f" too.
 
@@ -247,9 +242,9 @@ func (s *DockerSuite) TestCreateRM(c *check.C) {
 	dockerCmd(c, "rm", "-f", cID)
 }
 
-func (s *DockerSuite) TestCreateModeIpcContainer(c *check.C) {
+func (s *DockerSuite) TestCreateModeIpcContainer(c *testing.T) {
 	// Uses Linux specific functionality (--ipc)
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 
 	out, _ := dockerCmd(c, "create", "busybox")
 	id := strings.TrimSpace(out)
@@ -257,7 +252,7 @@ func (s *DockerSuite) TestCreateModeIpcContainer(c *check.C) {
 	dockerCmd(c, "create", fmt.Sprintf("--ipc=container:%s", id), "busybox")
 }
 
-func (s *DockerSuite) TestCreateByImageID(c *check.C) {
+func (s *DockerSuite) TestCreateByImageID(c *testing.T) {
 	imageName := "testcreatebyimageid"
 	buildImageSuccessfully(c, imageName, build.WithDockerfile(`FROM busybox
 		MAINTAINER dockerio`))
@@ -290,16 +285,15 @@ func (s *DockerSuite) TestCreateByImageID(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestCreateStopSignal(c *check.C) {
+func (s *DockerSuite) TestCreateStopSignal(c *testing.T) {
 	name := "test_create_stop_signal"
 	dockerCmd(c, "create", "--name", name, "--stop-signal", "9", "busybox")
 
 	res := inspectFieldJSON(c, name, "Config.StopSignal")
-	c.Assert(res, checker.Contains, "9")
-
+	assert.Assert(c, strings.Contains(res, "9"))
 }
 
-func (s *DockerSuite) TestCreateWithWorkdir(c *check.C) {
+func (s *DockerSuite) TestCreateWithWorkdir(c *testing.T) {
 	name := "foo"
 
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
@@ -309,30 +303,39 @@ func (s *DockerSuite) TestCreateWithWorkdir(c *check.C) {
 	// Windows does not create the workdir until the container is started
 	if testEnv.OSType == "windows" {
 		dockerCmd(c, "start", name)
+		if IsolationIsHyperv() {
+			// Hyper-V isolated containers do not allow file-operations on a
+			// running container. This test currently uses `docker cp` to verify
+			// that the WORKDIR was automatically created, which cannot be done
+			// while the container is running.
+			dockerCmd(c, "stop", name)
+		}
 	}
+	// TODO: rewrite this test to not use `docker cp` for verifying that the WORKDIR was created
 	dockerCmd(c, "cp", fmt.Sprintf("%s:%s", name, dir), prefix+slash+"tmp")
 }
 
-func (s *DockerSuite) TestCreateWithInvalidLogOpts(c *check.C) {
+func (s *DockerSuite) TestCreateWithInvalidLogOpts(c *testing.T) {
 	name := "test-invalidate-log-opts"
 	out, _, err := dockerCmdWithError("create", "--name", name, "--log-opt", "invalid=true", "busybox")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "unknown log opt")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "unknown log opt"))
+	assert.Assert(c, is.Contains(out, "unknown log opt"))
 
 	out, _ = dockerCmd(c, "ps", "-a")
-	c.Assert(out, checker.Not(checker.Contains), name)
+	assert.Assert(c, !strings.Contains(out, name))
 }
 
 // #20972
-func (s *DockerSuite) TestCreate64ByteHexID(c *check.C) {
+func (s *DockerSuite) TestCreate64ByteHexID(c *testing.T) {
 	out := inspectField(c, "busybox", "Id")
-	imageID := strings.TrimPrefix(strings.TrimSpace(string(out)), "sha256:")
+	imageID := strings.TrimPrefix(strings.TrimSpace(out), "sha256:")
 
 	dockerCmd(c, "create", imageID)
 }
 
 // Test case for #23498
-func (s *DockerSuite) TestCreateUnsetEntrypoint(c *check.C) {
+func (s *DockerSuite) TestCreateUnsetEntrypoint(c *testing.T) {
 	name := "test-entrypoint"
 	dockerfile := `FROM busybox
 ADD entrypoint.sh /entrypoint.sh
@@ -353,22 +356,21 @@ exec "$@"`,
 
 	out := cli.DockerCmd(c, "create", "--entrypoint=", name, "echo", "foo").Combined()
 	id := strings.TrimSpace(out)
-	c.Assert(id, check.Not(check.Equals), "")
+	assert.Assert(c, id != "")
 	out = cli.DockerCmd(c, "start", "-a", id).Combined()
-	c.Assert(strings.TrimSpace(out), check.Equals, "foo")
+	assert.Equal(c, strings.TrimSpace(out), "foo")
 }
 
 // #22471
-func (s *DockerSuite) TestCreateStopTimeout(c *check.C) {
+func (s *DockerSuite) TestCreateStopTimeout(c *testing.T) {
 	name1 := "test_create_stop_timeout_1"
 	dockerCmd(c, "create", "--name", name1, "--stop-timeout", "15", "busybox")
 
 	res := inspectFieldJSON(c, name1, "Config.StopTimeout")
-	c.Assert(res, checker.Contains, "15")
-
+	assert.Assert(c, strings.Contains(res, "15"))
 	name2 := "test_create_stop_timeout_2"
 	dockerCmd(c, "create", "--name", name2, "busybox")
 
 	res = inspectFieldJSON(c, name2, "Config.StopTimeout")
-	c.Assert(res, checker.Contains, "null")
+	assert.Assert(c, strings.Contains(res, "null"))
 }
diff --git a/integration-cli/docker_cli_daemon_plugins_test.go b/integration-cli/docker_cli_daemon_plugins_test.go
index 9a11c68089302..55695b455a063 100644
--- a/integration-cli/docker_cli_daemon_plugins_test.go
+++ b/integration-cli/docker_cli_daemon_plugins_test.go
@@ -1,19 +1,19 @@
+//go:build linux
 // +build linux
 
 package main
 
 import (
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/pkg/mount"
-	"github.com/go-check/check"
 	"golang.org/x/sys/unix"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
 // TestDaemonRestartWithPluginEnabled tests state restore for an enabled plugin
-func (s *DockerDaemonSuite) TestDaemonRestartWithPluginEnabled(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithPluginEnabled(c *testing.T) {
 	testRequires(c, IsAmd64, Network)
 
 	s.d.Start(c)
@@ -37,12 +37,12 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPluginEnabled(c *check.C) {
 	if err != nil {
 		c.Fatalf("Could not list plugins: %v %s", err, out)
 	}
-	c.Assert(out, checker.Contains, pName)
-	c.Assert(out, checker.Contains, "true")
+	assert.Assert(c, strings.Contains(out, pName))
+	assert.Assert(c, strings.Contains(out, "true"))
 }
 
 // TestDaemonRestartWithPluginDisabled tests state restore for a disabled plugin
-func (s *DockerDaemonSuite) TestDaemonRestartWithPluginDisabled(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithPluginDisabled(c *testing.T) {
 	testRequires(c, IsAmd64, Network)
 
 	s.d.Start(c)
@@ -63,13 +63,13 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPluginDisabled(c *check.C) {
 	if err != nil {
 		c.Fatalf("Could not list plugins: %v %s", err, out)
 	}
-	c.Assert(out, checker.Contains, pName)
-	c.Assert(out, checker.Contains, "false")
+	assert.Assert(c, strings.Contains(out, pName))
+	assert.Assert(c, strings.Contains(out, "false"))
 }
 
 // TestDaemonKillLiveRestoreWithPlugins SIGKILLs daemon started with --live-restore.
 // Plugins should continue to run.
-func (s *DockerDaemonSuite) TestDaemonKillLiveRestoreWithPlugins(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonKillLiveRestoreWithPlugins(c *testing.T) {
 	testRequires(c, IsAmd64, Network)
 
 	s.d.Start(c, "--live-restore")
@@ -95,7 +95,7 @@ func (s *DockerDaemonSuite) TestDaemonKillLiveRestoreWithPlugins(c *check.C) {
 
 // TestDaemonShutdownLiveRestoreWithPlugins SIGTERMs daemon started with --live-restore.
 // Plugins should continue to run.
-func (s *DockerDaemonSuite) TestDaemonShutdownLiveRestoreWithPlugins(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonShutdownLiveRestoreWithPlugins(c *testing.T) {
 	testRequires(c, IsAmd64, Network)
 
 	s.d.Start(c, "--live-restore")
@@ -120,8 +120,8 @@ func (s *DockerDaemonSuite) TestDaemonShutdownLiveRestoreWithPlugins(c *check.C)
 }
 
 // TestDaemonShutdownWithPlugins shuts down running plugins.
-func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
-	testRequires(c, IsAmd64, Network, SameHostDaemon)
+func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *testing.T) {
+	testRequires(c, IsAmd64, Network)
 
 	s.d.Start(c)
 	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName); err != nil {
@@ -158,8 +158,8 @@ func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
 }
 
 // TestDaemonKillWithPlugins leaves plugins running.
-func (s *DockerDaemonSuite) TestDaemonKillWithPlugins(c *check.C) {
-	testRequires(c, IsAmd64, Network, SameHostDaemon)
+func (s *DockerDaemonSuite) TestDaemonKillWithPlugins(c *testing.T) {
+	testRequires(c, IsAmd64, Network)
 
 	s.d.Start(c)
 	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName); err != nil {
@@ -185,7 +185,7 @@ func (s *DockerDaemonSuite) TestDaemonKillWithPlugins(c *check.C) {
 }
 
 // TestVolumePlugin tests volume creation using a plugin.
-func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
+func (s *DockerDaemonSuite) TestVolumePlugin(c *testing.T) {
 	testRequires(c, IsAmd64, Network)
 
 	volName := "plugin-volume"
@@ -221,64 +221,51 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
 	if err != nil {
 		c.Fatalf("Could not list volume: %v %s", err, out)
 	}
-	c.Assert(out, checker.Contains, volName)
-	c.Assert(out, checker.Contains, pName)
+	assert.Assert(c, strings.Contains(out, volName))
+	assert.Assert(c, strings.Contains(out, pName))
 
 	out, err = s.d.Cmd("run", "--rm", "-v", volName+":"+destDir, "busybox", "touch", destDir+destFile)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("run", "--rm", "-v", volName+":"+destDir, "busybox", "ls", destDir+destFile)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerDaemonSuite) TestPluginVolumeRemoveOnRestart(c *check.C) {
-	testRequires(c, DaemonIsLinux, Network, IsAmd64)
+func (s *DockerDaemonSuite) TestPluginVolumeRemoveOnRestart(c *testing.T) {
+	testRequires(c, IsAmd64, Network)
 
 	s.d.Start(c, "--live-restore=true")
 
 	out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Contains, pName)
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, pName))
 
 	out, err = s.d.Cmd("volume", "create", "--driver", pName, "test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	s.d.Restart(c, "--live-restore=true")
 
 	out, err = s.d.Cmd("plugin", "disable", pName)
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "in use")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "in use"))
 
 	out, err = s.d.Cmd("volume", "rm", "test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("plugin", "disable", pName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("plugin", "rm", pName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-}
-
-func existsMountpointWithPrefix(mountpointPrefix string) (bool, error) {
-	mounts, err := mount.GetMounts(nil)
-	if err != nil {
-		return false, err
-	}
-	for _, mnt := range mounts {
-		if strings.HasPrefix(mnt.Mountpoint, mountpointPrefix) {
-			return true, nil
-		}
-	}
-	return false, nil
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerDaemonSuite) TestPluginListFilterEnabled(c *check.C) {
+func (s *DockerDaemonSuite) TestPluginListFilterEnabled(c *testing.T) {
 	testRequires(c, IsAmd64, Network)
 
 	s.d.Start(c)
 
 	out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pNameWithTag, "--disable")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	defer func() {
 		if out, err := s.d.Cmd("plugin", "remove", pNameWithTag); err != nil {
@@ -287,26 +274,26 @@ func (s *DockerDaemonSuite) TestPluginListFilterEnabled(c *check.C) {
 	}()
 
 	out, err = s.d.Cmd("plugin", "ls", "--filter", "enabled=true")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), pName)
+	assert.NilError(c, err, out)
+	assert.Assert(c, !strings.Contains(out, pName))
 
 	out, err = s.d.Cmd("plugin", "ls", "--filter", "enabled=false")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, pName)
-	c.Assert(out, checker.Contains, "false")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, pName))
+	assert.Assert(c, strings.Contains(out, "false"))
 
 	out, err = s.d.Cmd("plugin", "ls")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, pName)
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, pName))
 }
 
-func (s *DockerDaemonSuite) TestPluginListFilterCapability(c *check.C) {
+func (s *DockerDaemonSuite) TestPluginListFilterCapability(c *testing.T) {
 	testRequires(c, IsAmd64, Network)
 
 	s.d.Start(c)
 
 	out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pNameWithTag, "--disable")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	defer func() {
 		if out, err := s.d.Cmd("plugin", "remove", pNameWithTag); err != nil {
@@ -315,14 +302,14 @@ func (s *DockerDaemonSuite) TestPluginListFilterCapability(c *check.C) {
 	}()
 
 	out, err = s.d.Cmd("plugin", "ls", "--filter", "capability=volumedriver")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, pName)
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, pName))
 
 	out, err = s.d.Cmd("plugin", "ls", "--filter", "capability=authz")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), pName)
+	assert.NilError(c, err, out)
+	assert.Assert(c, !strings.Contains(out, pName))
 
 	out, err = s.d.Cmd("plugin", "ls")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, pName)
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, pName))
 }
diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go
index 986cc2753031d..cf1cdac245ef8 100644
--- a/integration-cli/docker_cli_daemon_test.go
+++ b/integration-cli/docker_cli_daemon_test.go
@@ -1,3 +1,4 @@
+//go:build linux
 // +build linux
 
 package main
@@ -11,7 +12,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net"
 	"os"
 	"os/exec"
@@ -21,43 +21,41 @@ import (
 	"strconv"
 	"strings"
 	"sync"
+	"testing"
 	"time"
 
 	"github.com/cloudflare/cfssl/helpers"
-	"github.com/docker/docker/api"
+	"github.com/creack/pty"
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/client"
-	moby_daemon "github.com/docker/docker/daemon"
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
 	"github.com/docker/docker/integration-cli/daemon"
-	testdaemon "github.com/docker/docker/internal/test/daemon"
+	"github.com/docker/docker/libnetwork/iptables"
 	"github.com/docker/docker/opts"
-	"github.com/docker/docker/pkg/mount"
-	"github.com/docker/go-units"
-	"github.com/docker/libnetwork/iptables"
+	testdaemon "github.com/docker/docker/testutil/daemon"
+	units "github.com/docker/go-units"
 	"github.com/docker/libtrust"
-	"github.com/go-check/check"
-	"github.com/kr/pty"
+	"github.com/moby/sys/mount"
 	"golang.org/x/sys/unix"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/poll"
 )
 
 const containerdSocket = "/var/run/docker/containerd/containerd.sock"
 
 // TestLegacyDaemonCommand test starting docker daemon using "deprecated" docker daemon
 // command. Remove this test when we remove this.
-func (s *DockerDaemonSuite) TestLegacyDaemonCommand(c *check.C) {
+func (s *DockerDaemonSuite) TestLegacyDaemonCommand(c *testing.T) {
 	cmd := exec.Command(dockerBinary, "daemon", "--storage-driver=vfs", "--debug")
 	err := cmd.Start()
 	go cmd.Wait()
-	c.Assert(err, checker.IsNil, check.Commentf("could not start daemon using 'docker daemon'"))
-
-	c.Assert(cmd.Process.Kill(), checker.IsNil)
+	assert.NilError(c, err, "could not start daemon using 'docker daemon'")
+	assert.NilError(c, cmd.Process.Kill())
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartWithRunningContainersPorts(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithRunningContainersPorts(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	cli.Docker(
@@ -91,7 +89,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithRunningContainersPorts(c *check
 	testRun(map[string]bool{"top1": true, "top2": false}, "After daemon restart: ")
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	if out, err := s.d.Cmd("run", "--name", "volrestarttest1", "-v", "/foo", "busybox"); err != nil {
@@ -109,7 +107,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *check.C) {
 	}
 
 	out, err := s.d.Cmd("inspect", "-f", "{{json .Mounts}}", "volrestarttest1")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	if _, err := inspectMountPointJSON(out, "/foo"); err != nil {
 		c.Fatalf("Expected volume to exist: /foo, error: %v\n", err)
@@ -117,100 +115,109 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *check.C) {
 }
 
 // #11008
-func (s *DockerDaemonSuite) TestDaemonRestartUnlessStopped(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartUnlessStopped(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("run", "-d", "--name", "top1", "--restart", "always", "busybox:latest", "top")
-	c.Assert(err, check.IsNil, check.Commentf("run top1: %v", out))
+	assert.NilError(c, err, "run top1: %v", out)
 
 	out, err = s.d.Cmd("run", "-d", "--name", "top2", "--restart", "unless-stopped", "busybox:latest", "top")
-	c.Assert(err, check.IsNil, check.Commentf("run top2: %v", out))
+	assert.NilError(c, err, "run top2: %v", out)
+
+	out, err = s.d.Cmd("run", "-d", "--name", "exit", "--restart", "unless-stopped", "busybox:latest", "false")
+	assert.NilError(c, err, "run exit: %v", out)
 
 	testRun := func(m map[string]bool, prefix string) {
 		var format string
 		for name, shouldRun := range m {
 			out, err := s.d.Cmd("ps")
-			c.Assert(err, check.IsNil, check.Commentf("run ps: %v", out))
+			assert.Assert(c, err == nil, "run ps: %v", out)
 			if shouldRun {
 				format = "%scontainer %q is not running"
 			} else {
 				format = "%scontainer %q is running"
 			}
-			c.Assert(strings.Contains(out, name), check.Equals, shouldRun, check.Commentf(format, prefix, name))
+			assert.Equal(c, strings.Contains(out, name), shouldRun, fmt.Sprintf(format, prefix, name))
 		}
 	}
 
 	// both running
-	testRun(map[string]bool{"top1": true, "top2": true}, "")
+	testRun(map[string]bool{"top1": true, "top2": true, "exit": true}, "")
+
+	out, err = s.d.Cmd("stop", "exit")
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("stop", "top1")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("stop", "top2")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// both stopped
-	testRun(map[string]bool{"top1": false, "top2": false}, "")
+	testRun(map[string]bool{"top1": false, "top2": false, "exit": false}, "")
 
 	s.d.Restart(c)
 
 	// restart=always running
-	testRun(map[string]bool{"top1": true, "top2": false}, "After daemon restart: ")
+	testRun(map[string]bool{"top1": true, "top2": false, "exit": false}, "After daemon restart: ")
 
 	out, err = s.d.Cmd("start", "top2")
-	c.Assert(err, check.IsNil, check.Commentf("start top2: %v", out))
+	assert.NilError(c, err, "start top2: %v", out)
+
+	out, err = s.d.Cmd("start", "exit")
+	assert.NilError(c, err, "start exit: %v", out)
 
 	s.d.Restart(c)
 
 	// both running
-	testRun(map[string]bool{"top1": true, "top2": true}, "After second daemon restart: ")
+	testRun(map[string]bool{"top1": true, "top2": true, "exit": true}, "After second daemon restart: ")
 
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartOnFailure(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartOnFailure(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("run", "-d", "--name", "test1", "--restart", "on-failure:3", "busybox:latest", "false")
-	c.Assert(err, check.IsNil, check.Commentf("run top1: %v", out))
+	assert.NilError(c, err, "run top1: %v", out)
 
 	// wait test1 to stop
 	hostArgs := []string{"--host", s.d.Sock()}
 	err = waitInspectWithArgs("test1", "{{.State.Running}} {{.State.Restarting}}", "false false", 10*time.Second, hostArgs...)
-	c.Assert(err, checker.IsNil, check.Commentf("test1 should exit but not"))
+	assert.NilError(c, err, "test1 should exit but not")
 
 	// record last start time
 	out, err = s.d.Cmd("inspect", "-f={{.State.StartedAt}}", "test1")
-	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
+	assert.NilError(c, err, "out: %v", out)
 	lastStartTime := out
 
 	s.d.Restart(c)
 
 	// test1 shouldn't restart at all
 	err = waitInspectWithArgs("test1", "{{.State.Running}} {{.State.Restarting}}", "false false", 0, hostArgs...)
-	c.Assert(err, checker.IsNil, check.Commentf("test1 should exit but not"))
+	assert.NilError(c, err, "test1 should exit but not")
 
 	// make sure test1 isn't restarted when daemon restart
 	// if "StartAt" time updates, means test1 was once restarted.
 	out, err = s.d.Cmd("inspect", "-f={{.State.StartedAt}}", "test1")
-	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
-	c.Assert(out, checker.Equals, lastStartTime, check.Commentf("test1 shouldn't start after daemon restarts"))
+	assert.NilError(c, err, "out: %v", out)
+	assert.Equal(c, out, lastStartTime, "test1 shouldn't start after daemon restarts")
 }
 
-func (s *DockerDaemonSuite) TestDaemonStartIptablesFalse(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonStartIptablesFalse(c *testing.T) {
 	s.d.Start(c, "--iptables=false")
 }
 
 // Make sure we cannot shrink base device at daemon restart.
-func (s *DockerDaemonSuite) TestDaemonRestartWithInvalidBasesize(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithInvalidBasesize(c *testing.T) {
 	testRequires(c, Devicemapper)
 	s.d.Start(c)
 
 	oldBasesizeBytes := getBaseDeviceSize(c, s.d)
-	var newBasesizeBytes int64 = 1073741824 //1GB in bytes
+	var newBasesizeBytes int64 = 1073741824 // 1GB in bytes
 
 	if newBasesizeBytes < oldBasesizeBytes {
 		err := s.d.RestartWithError("--storage-opt", fmt.Sprintf("dm.basesize=%d", newBasesizeBytes))
-		c.Assert(err, check.NotNil, check.Commentf("daemon should not have started as new base device size is less than existing base device size: %v", err))
+		assert.Assert(c, err != nil, "daemon should not have started as new base device size is less than existing base device size: %v", err)
 		// 'err != nil' is expected behaviour, no new daemon started,
 		// so no need to stop daemon.
 		if err != nil {
@@ -221,29 +228,29 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithInvalidBasesize(c *check.C) {
 }
 
 // Make sure we can grow base device at daemon restart.
-func (s *DockerDaemonSuite) TestDaemonRestartWithIncreasedBasesize(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithIncreasedBasesize(c *testing.T) {
 	testRequires(c, Devicemapper)
 	s.d.Start(c)
 
 	oldBasesizeBytes := getBaseDeviceSize(c, s.d)
 
-	var newBasesizeBytes int64 = 53687091200 //50GB in bytes
+	var newBasesizeBytes int64 = 53687091200 // 50GB in bytes
 
 	if newBasesizeBytes < oldBasesizeBytes {
 		c.Skip(fmt.Sprintf("New base device size (%v) must be greater than (%s)", units.HumanSize(float64(newBasesizeBytes)), units.HumanSize(float64(oldBasesizeBytes))))
 	}
 
 	err := s.d.RestartWithError("--storage-opt", fmt.Sprintf("dm.basesize=%d", newBasesizeBytes))
-	c.Assert(err, check.IsNil, check.Commentf("we should have been able to start the daemon with increased base device size: %v", err))
+	assert.Assert(c, err == nil, "we should have been able to start the daemon with increased base device size: %v", err)
 
 	basesizeAfterRestart := getBaseDeviceSize(c, s.d)
 	newBasesize, err := convertBasesize(newBasesizeBytes)
-	c.Assert(err, check.IsNil, check.Commentf("Error in converting base device size: %v", err))
-	c.Assert(newBasesize, check.Equals, basesizeAfterRestart, check.Commentf("Basesize passed is not equal to Basesize set"))
+	assert.Assert(c, err == nil, "Error in converting base device size: %v", err)
+	assert.Equal(c, newBasesize, basesizeAfterRestart, "Basesize passed is not equal to Basesize set")
 	s.d.Stop(c)
 }
 
-func getBaseDeviceSize(c *check.C, d *daemon.Daemon) int64 {
+func getBaseDeviceSize(c *testing.T, d *daemon.Daemon) int64 {
 	info := d.Info(c)
 	for _, statusLine := range info.DriverStatus {
 		key, value := statusLine[0], statusLine[1]
@@ -255,9 +262,9 @@ func getBaseDeviceSize(c *check.C, d *daemon.Daemon) int64 {
 	return int64(0)
 }
 
-func parseDeviceSize(c *check.C, raw string) int64 {
+func parseDeviceSize(c *testing.T, raw string) int64 {
 	size, err := units.RAMInBytes(strings.TrimSpace(raw))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	return size
 }
 
@@ -274,7 +281,7 @@ func convertBasesize(basesizeBytes int64) (int64, error) {
 // Issue #8444: If docker0 bridge is modified (intentionally or unintentionally) and
 // no longer has an IP associated, we should gracefully handle that case and associate
 // an IP with it rather than fail daemon start
-func (s *DockerDaemonSuite) TestDaemonStartBridgeWithoutIPAssociation(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonStartBridgeWithoutIPAssociation(c *testing.T) {
 	// rather than depending on brctl commands to verify docker0 is created and up
 	// let's start the daemon and stop it, and then make a modification to run the
 	// actual test
@@ -290,7 +297,7 @@ func (s *DockerDaemonSuite) TestDaemonStartBridgeWithoutIPAssociation(c *check.C
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonIptablesClean(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonIptablesClean(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	if out, err := s.d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top"); err != nil {
@@ -308,7 +315,7 @@ func (s *DockerDaemonSuite) TestDaemonIptablesClean(c *check.C) {
 	verifyIPTablesDoesNotContains(c, ipTablesSearchString)
 }
 
-func (s *DockerDaemonSuite) TestDaemonIptablesCreate(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonIptablesCreate(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	if out, err := s.d.Cmd("run", "-d", "--name", "top", "--restart=always", "-p", "80", "busybox:latest", "top"); err != nil {
@@ -334,7 +341,7 @@ func (s *DockerDaemonSuite) TestDaemonIptablesCreate(c *check.C) {
 	verifyIPTablesContains(c, ipTablesSearchString)
 }
 
-func verifyIPTablesContains(c *check.C, ipTablesSearchString string) {
+func verifyIPTablesContains(c *testing.T, ipTablesSearchString string) {
 	result := icmd.RunCommand("iptables", "-nvL")
 	result.Assert(c, icmd.Success)
 	if !strings.Contains(result.Combined(), ipTablesSearchString) {
@@ -342,7 +349,7 @@ func verifyIPTablesContains(c *check.C, ipTablesSearchString string) {
 	}
 }
 
-func verifyIPTablesDoesNotContains(c *check.C, ipTablesSearchString string) {
+func verifyIPTablesDoesNotContains(c *testing.T, ipTablesSearchString string) {
 	result := icmd.RunCommand("iptables", "-nvL")
 	result.Assert(c, icmd.Success)
 	if strings.Contains(result.Combined(), ipTablesSearchString) {
@@ -352,7 +359,7 @@ func verifyIPTablesDoesNotContains(c *check.C, ipTablesSearchString string) {
 
 // TestDaemonIPv6Enabled checks that when the daemon is started with --ipv6=true that the docker0 bridge
 // has the fe80::1 address and that a container is assigned a link-local address
-func (s *DockerDaemonSuite) TestDaemonIPv6Enabled(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonIPv6Enabled(c *testing.T) {
 	testRequires(c, IPv6)
 
 	setupV6(c)
@@ -411,131 +418,136 @@ func (s *DockerDaemonSuite) TestDaemonIPv6Enabled(c *check.C) {
 
 // TestDaemonIPv6FixedCIDR checks that when the daemon is started with --ipv6=true and a fixed CIDR
 // that running containers are given a link-local and global IPv6 address
-func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDR(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDR(c *testing.T) {
 	// IPv6 setup is messing with local bridge address.
-	testRequires(c, SameHostDaemon)
+	testRequires(c, testEnv.IsLocalDaemon)
 	// Delete the docker0 bridge if its left around from previous daemon. It has to be recreated with
 	// ipv6 enabled
 	deleteInterface(c, "docker0")
 
 	s.d.StartWithBusybox(c, "--ipv6", "--fixed-cidr-v6=2001:db8:2::/64", "--default-gateway-v6=2001:db8:2::100")
 
-	out, err := s.d.Cmd("run", "-itd", "--name=ipv6test", "busybox:latest")
-	c.Assert(err, checker.IsNil, check.Commentf("Could not run container: %s, %v", out, err))
+	out, err := s.d.Cmd("run", "-d", "--name=ipv6test", "busybox:latest", "top")
+	assert.NilError(c, err, "Could not run container: %s, %v", out, err)
 
 	out, err = s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}", "ipv6test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	out = strings.Trim(out, " \r\n'")
 
 	ip := net.ParseIP(out)
-	c.Assert(ip, checker.NotNil, check.Commentf("Container should have a global IPv6 address"))
+	assert.Assert(c, ip != nil, "Container should have a global IPv6 address")
 
 	out, err = s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.IPv6Gateway}}", "ipv6test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
-	c.Assert(strings.Trim(out, " \r\n'"), checker.Equals, "2001:db8:2::100", check.Commentf("Container should have a global IPv6 gateway"))
+	assert.Equal(c, strings.Trim(out, " \r\n'"), "2001:db8:2::100", "Container should have a global IPv6 gateway")
 }
 
 // TestDaemonIPv6FixedCIDRAndMac checks that when the daemon is started with ipv6 fixed CIDR
 // the running containers are given an IPv6 address derived from the MAC address and the ipv6 fixed CIDR
-func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDRAndMac(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDRAndMac(c *testing.T) {
 	// IPv6 setup is messing with local bridge address.
-	testRequires(c, SameHostDaemon)
+	testRequires(c, testEnv.IsLocalDaemon)
 	// Delete the docker0 bridge if its left around from previous daemon. It has to be recreated with
 	// ipv6 enabled
 	deleteInterface(c, "docker0")
 
 	s.d.StartWithBusybox(c, "--ipv6", "--fixed-cidr-v6=2001:db8:1::/64")
 
-	out, err := s.d.Cmd("run", "-itd", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	out, err := s.d.Cmd("run", "-d", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox", "top")
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}", "ipv6test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.Trim(out, " \r\n'"), checker.Equals, "2001:db8:1::aabb:ccdd:eeff")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.Trim(out, " \r\n'"), "2001:db8:1::aabb:ccdd:eeff")
 }
 
 // TestDaemonIPv6HostMode checks that when the running a container with
 // network=host the host ipv6 addresses are not removed
-func (s *DockerDaemonSuite) TestDaemonIPv6HostMode(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerDaemonSuite) TestDaemonIPv6HostMode(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	deleteInterface(c, "docker0")
 
 	s.d.StartWithBusybox(c, "--ipv6", "--fixed-cidr-v6=2001:db8:2::/64")
-	out, err := s.d.Cmd("run", "-itd", "--name=hostcnt", "--network=host", "busybox:latest")
-	c.Assert(err, checker.IsNil, check.Commentf("Could not run container: %s, %v", out, err))
+	out, err := s.d.Cmd("run", "-d", "--name=hostcnt", "--network=host", "busybox:latest", "top")
+	assert.NilError(c, err, "Could not run container: %s, %v", out, err)
 
 	out, err = s.d.Cmd("exec", "hostcnt", "ip", "-6", "addr", "show", "docker0")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.Trim(out, " \r\n'"), checker.Contains, "2001:db8:2::1")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(strings.Trim(out, " \r\n'"), "2001:db8:2::1"))
 }
 
-func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) {
-	c.Assert(s.d.StartWithError("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level"))
+func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *testing.T) {
+	assert.Assert(c, s.d.StartWithError("--log-level=bogus") != nil, "Daemon shouldn't start with wrong log level")
 }
 
-func (s *DockerDaemonSuite) TestDaemonLogLevelDebug(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonLogLevelDebug(c *testing.T) {
 	s.d.Start(c, "--log-level=debug")
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if !strings.Contains(string(content), `level=debug`) {
 		c.Fatalf(`Missing level="debug" in log file:\n%s`, string(content))
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonLogLevelFatal(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonLogLevelFatal(c *testing.T) {
 	// we creating new daemons to create new logFile
 	s.d.Start(c, "--log-level=fatal")
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if strings.Contains(string(content), `level=debug`) {
 		c.Fatalf(`Should not have level="debug" in log file:\n%s`, string(content))
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonFlagD(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonFlagD(c *testing.T) {
 	s.d.Start(c, "-D")
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if !strings.Contains(string(content), `level=debug`) {
 		c.Fatalf(`Should have level="debug" in log file using -D:\n%s`, string(content))
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonFlagDebug(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonFlagDebug(c *testing.T) {
 	s.d.Start(c, "--debug")
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if !strings.Contains(string(content), `level=debug`) {
 		c.Fatalf(`Should have level="debug" in log file using --debug:\n%s`, string(content))
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonFlagDebugLogLevelFatal(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonFlagDebugLogLevelFatal(c *testing.T) {
 	s.d.Start(c, "--debug", "--log-level=fatal")
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if !strings.Contains(string(content), `level=debug`) {
 		c.Fatalf(`Should have level="debug" in log file when using both --debug and --log-level=fatal:\n%s`, string(content))
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonAllocatesListeningPort(c *check.C) {
-	listeningPorts := [][]string{
+func (s *DockerDaemonSuite) TestDaemonAllocatesListeningPort(c *testing.T) {
+	type listener struct {
+		daemon string
+		client string
+		port   string
+	}
+	listeningPorts := []listener{
 		{"0.0.0.0", "0.0.0.0", "5678"},
 		{"127.0.0.1", "127.0.0.1", "1234"},
 		{"localhost", "127.0.0.1", "1235"},
 	}
 
 	cmdArgs := make([]string, 0, len(listeningPorts)*2)
-	for _, hostDirective := range listeningPorts {
-		cmdArgs = append(cmdArgs, "--host", fmt.Sprintf("tcp://%s:%s", hostDirective[0], hostDirective[2]))
+	for _, l := range listeningPorts {
+		cmdArgs = append(cmdArgs, "--tls=false", "--host", fmt.Sprintf("tcp://%s:%s", l.daemon, l.port))
 	}
 
 	s.d.StartWithBusybox(c, cmdArgs...)
 
-	for _, hostDirective := range listeningPorts {
-		output, err := s.d.Cmd("run", "-p", fmt.Sprintf("%s:%s:80", hostDirective[1], hostDirective[2]), "busybox", "true")
+	for _, l := range listeningPorts {
+		output, err := s.d.Cmd("run", "-p", fmt.Sprintf("%s:%s:80", l.client, l.port), "busybox", "true")
 		if err == nil {
 			c.Fatalf("Container should not start, expected port already allocated error: %q", output)
 		} else if !strings.Contains(output, "port is already allocated") {
@@ -544,7 +556,7 @@ func (s *DockerDaemonSuite) TestDaemonAllocatesListeningPort(c *check.C) {
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonKeyGeneration(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonKeyGeneration(c *testing.T) {
 	// TODO: skip or update for Windows daemon
 	os.Remove("/etc/docker/key.json")
 	s.d.Start(c)
@@ -564,25 +576,25 @@ func (s *DockerDaemonSuite) TestDaemonKeyGeneration(c *check.C) {
 // GH#11320 - verify that the daemon exits on failure properly
 // Note that this explicitly tests the conflict of {-b,--bridge} and {--bip} options as the means
 // to get a daemon init failure; no other tests for -b/--bip conflict are therefore required
-func (s *DockerDaemonSuite) TestDaemonExitOnFailure(c *check.C) {
-	//attempt to start daemon with incorrect flags (we know -b and --bip conflict)
+func (s *DockerDaemonSuite) TestDaemonExitOnFailure(c *testing.T) {
+	// attempt to start daemon with incorrect flags (we know -b and --bip conflict)
 	if err := s.d.StartWithError("--bridge", "nosuchbridge", "--bip", "1.1.1.1"); err != nil {
-		//verify we got the right error
-		if !strings.Contains(err.Error(), "Daemon exited") {
+		// verify we got the right error
+		if !strings.Contains(err.Error(), "daemon exited") {
 			c.Fatalf("Expected daemon not to start, got %v", err)
 		}
 		// look in the log and make sure we got the message that daemon is shutting down
 		icmd.RunCommand("grep", "failed to start daemon", s.d.LogFileName()).Assert(c, icmd.Success)
 	} else {
-		//if we didn't get an error and the daemon is running, this is a failure
+		// if we didn't get an error and the daemon is running, this is a failure
 		c.Fatal("Conflicting options should cause the daemon to error out with a failure")
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *testing.T) {
 	d := s.d
 	err := d.StartWithError("--bridge", "nosuchbridge")
-	c.Assert(err, check.NotNil, check.Commentf("--bridge option with an invalid bridge should cause the daemon to fail"))
+	assert.ErrorContains(c, err, "", `--bridge option with an invalid bridge should cause the daemon to fail`)
 	defer d.Restart(c)
 
 	bridgeName := "external-bridge"
@@ -600,16 +612,14 @@ func (s *DockerDaemonSuite) TestDaemonBridgeExternal(c *check.C) {
 	})
 
 	out, err := d.Cmd("run", "-d", "--name", "ExtContainer", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	containerIP := d.FindContainerIP(c, "ExtContainer")
 	ip := net.ParseIP(containerIP)
-	c.Assert(bridgeIPNet.Contains(ip), check.Equals, true,
-		check.Commentf("Container IP-Address must be in the same subnet range : %s",
-			containerIP))
+	assert.Assert(c, bridgeIPNet.Contains(ip), "Container IP-Address must be in the same subnet range : %s", containerIP)
 }
 
-func (s *DockerDaemonSuite) TestDaemonBridgeNone(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonBridgeNone(c *testing.T) {
 	// start with bridge none
 	d := s.d
 	d.StartWithBusybox(c, "--bridge", "none")
@@ -624,22 +634,22 @@ func (s *DockerDaemonSuite) TestDaemonBridgeNone(c *check.C) {
 
 	// verify default "bridge" network is not there
 	out, err := d.Cmd("network", "inspect", "bridge")
-	c.Assert(err, check.NotNil, check.Commentf("\"bridge\" network should not be present if daemon started with --bridge=none"))
-	c.Assert(strings.Contains(out, "No such network"), check.Equals, true)
+	assert.ErrorContains(c, err, "", `"bridge" network should not be present if daemon started with --bridge=none`)
+	assert.Assert(c, strings.Contains(out, "No such network"))
 }
 
-func createInterface(c *check.C, ifType string, ifName string, ipNet string) {
+func createInterface(c *testing.T, ifType string, ifName string, ipNet string) {
 	icmd.RunCommand("ip", "link", "add", "name", ifName, "type", ifType).Assert(c, icmd.Success)
 	icmd.RunCommand("ifconfig", ifName, ipNet, "up").Assert(c, icmd.Success)
 }
 
-func deleteInterface(c *check.C, ifName string) {
+func deleteInterface(c *testing.T, ifName string) {
 	icmd.RunCommand("ip", "link", "delete", ifName).Assert(c, icmd.Success)
 	icmd.RunCommand("iptables", "-t", "nat", "--flush").Assert(c, icmd.Success)
 	icmd.RunCommand("iptables", "--flush").Assert(c, icmd.Success)
 }
 
-func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *testing.T) {
 	// TestDaemonBridgeIP Steps
 	// 1. Delete the existing docker0 Bridge
 	// 2. Set --bip daemon configuration and start the new Docker Daemon
@@ -671,17 +681,15 @@ func (s *DockerDaemonSuite) TestDaemonBridgeIP(c *check.C) {
 	})
 
 	out, err := d.Cmd("run", "-d", "--name", "test", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	containerIP := d.FindContainerIP(c, "test")
 	ip = net.ParseIP(containerIP)
-	c.Assert(bridgeIPNet.Contains(ip), check.Equals, true,
-		check.Commentf("Container IP-Address must be in the same subnet range : %s",
-			containerIP))
+	assert.Equal(c, bridgeIPNet.Contains(ip), true, fmt.Sprintf("Container IP-Address must be in the same subnet range : %s", containerIP))
 	deleteInterface(c, defaultNetworkBridge)
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartWithBridgeIPChange(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithBridgeIPChange(c *testing.T) {
 	s.d.Start(c)
 	defer s.d.Restart(c)
 	s.d.Stop(c)
@@ -694,14 +702,14 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithBridgeIPChange(c *check.C) {
 
 	s.d.Start(c, "--bip", bridgeIP)
 
-	//check if the iptables contains new bridgeIP MASQUERADE rule
+	// check if the iptables contains new bridgeIP MASQUERADE rule
 	ipTablesSearchString := bridgeIPNet.String()
 	icmd.RunCommand("iptables", "-t", "nat", "-nvL").Assert(c, icmd.Expected{
 		Out: ipTablesSearchString,
 	})
 }
 
-func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *testing.T) {
 	d := s.d
 
 	bridgeName := "external-bridge"
@@ -718,13 +726,12 @@ func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) {
 		cName := "Container" + strconv.Itoa(i)
 		out, err := d.Cmd("run", "-d", "--name", cName, "busybox", "top")
 		if err != nil {
-			c.Assert(strings.Contains(out, "no available IPv4 addresses"), check.Equals, true,
-				check.Commentf("Could not run a Container : %s %s", err.Error(), out))
+			assert.Assert(c, strings.Contains(out, "no available IPv4 addresses"), "Could not run a Container : %s %s", err.Error(), out)
 		}
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr2(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr2(c *testing.T) {
 	d := s.d
 
 	bridgeName := "external-bridge"
@@ -737,19 +744,19 @@ func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr2(c *check.C) {
 	defer s.d.Restart(c)
 
 	out, err := d.Cmd("run", "-d", "--name", "bb", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	defer d.Cmd("stop", "bb")
 
 	out, err = d.Cmd("exec", "bb", "/bin/sh", "-c", "ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'")
-	c.Assert(err, check.IsNil)
-	c.Assert(out, checker.Equals, "10.2.2.0\n")
+	assert.NilError(c, err)
+	assert.Equal(c, out, "10.2.2.0\n")
 
 	out, err = d.Cmd("run", "--rm", "busybox", "/bin/sh", "-c", "ifconfig eth0 | awk '/inet addr/{print substr($2,6)}'")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Equals, "10.2.2.2\n")
+	assert.NilError(c, err, out)
+	assert.Equal(c, out, "10.2.2.2\n")
 }
 
-func (s *DockerDaemonSuite) TestDaemonBridgeFixedCIDREqualBridgeNetwork(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonBridgeFixedCIDREqualBridgeNetwork(c *testing.T) {
 	d := s.d
 
 	bridgeName := "external-bridge"
@@ -762,12 +769,12 @@ func (s *DockerDaemonSuite) TestDaemonBridgeFixedCIDREqualBridgeNetwork(c *check
 	defer s.d.Restart(c)
 
 	out, err := d.Cmd("run", "-d", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	cid1 := strings.TrimSpace(out)
 	defer d.Cmd("stop", cid1)
 }
 
-func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4Implicit(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4Implicit(c *testing.T) {
 	defaultNetworkBridge := "docker0"
 	deleteInterface(c, defaultNetworkBridge)
 
@@ -781,14 +788,12 @@ func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4Implicit(c *check.C) {
 
 	expectedMessage := fmt.Sprintf("default via %s dev", bridgeIP)
 	out, err := d.Cmd("run", "busybox", "ip", "-4", "route", "list", "0/0")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.Contains(out, expectedMessage), check.Equals, true,
-		check.Commentf("Implicit default gateway should be bridge IP %s, but default route was '%s'",
-			bridgeIP, strings.TrimSpace(out)))
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.Contains(out, expectedMessage), true, fmt.Sprintf("Implicit default gateway should be bridge IP %s, but default route was '%s'", bridgeIP, strings.TrimSpace(out)))
 	deleteInterface(c, defaultNetworkBridge)
 }
 
-func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4Explicit(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4Explicit(c *testing.T) {
 	defaultNetworkBridge := "docker0"
 	deleteInterface(c, defaultNetworkBridge)
 
@@ -803,14 +808,12 @@ func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4Explicit(c *check.C) {
 
 	expectedMessage := fmt.Sprintf("default via %s dev", gatewayIP)
 	out, err := d.Cmd("run", "busybox", "ip", "-4", "route", "list", "0/0")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.Contains(out, expectedMessage), check.Equals, true,
-		check.Commentf("Explicit default gateway should be %s, but default route was '%s'",
-			gatewayIP, strings.TrimSpace(out)))
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.Contains(out, expectedMessage), true, fmt.Sprintf("Explicit default gateway should be %s, but default route was '%s'", gatewayIP, strings.TrimSpace(out)))
 	deleteInterface(c, defaultNetworkBridge)
 }
 
-func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4ExplicitOutsideContainerSubnet(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4ExplicitOutsideContainerSubnet(c *testing.T) {
 	defaultNetworkBridge := "docker0"
 	deleteInterface(c, defaultNetworkBridge)
 
@@ -821,8 +824,7 @@ func (s *DockerDaemonSuite) TestDaemonDefaultGatewayIPv4ExplicitOutsideContainer
 	s.d.Restart(c)
 }
 
-func (s *DockerDaemonSuite) TestDaemonDefaultNetworkInvalidClusterConfig(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerDaemonSuite) TestDaemonDefaultNetworkInvalidClusterConfig(c *testing.T) {
 
 	// Start daemon without docker0 bridge
 	defaultNetworkBridge := "docker0"
@@ -838,7 +840,7 @@ func (s *DockerDaemonSuite) TestDaemonDefaultNetworkInvalidClusterConfig(c *chec
 	s.d.Restart(c, fmt.Sprintf("--cluster-store=%s", discoveryBackend))
 }
 
-func (s *DockerDaemonSuite) TestDaemonIP(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonIP(c *testing.T) {
 	d := s.d
 
 	ipStr := "192.170.1.1/24"
@@ -848,26 +850,24 @@ func (s *DockerDaemonSuite) TestDaemonIP(c *check.C) {
 	defer d.Restart(c)
 
 	out, err := d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top")
-	c.Assert(err, check.NotNil,
-		check.Commentf("Running a container must fail with an invalid --ip option"))
-	c.Assert(strings.Contains(out, "Error starting userland proxy"), check.Equals, true)
+	assert.Assert(c, err != nil, "Running a container must fail with an invalid --ip option")
+	assert.Equal(c, strings.Contains(out, "Error starting userland proxy"), true)
 
 	ifName := "dummy"
 	createInterface(c, "dummy", ifName, ipStr)
 	defer deleteInterface(c, ifName)
 
 	_, err = d.Cmd("run", "-d", "-p", "8000:8000", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	result := icmd.RunCommand("iptables", "-t", "nat", "-nvL")
 	result.Assert(c, icmd.Success)
 	regex := fmt.Sprintf("DNAT.*%s.*dpt:8000", ip.String())
 	matched, _ := regexp.MatchString(regex, result.Combined())
-	c.Assert(matched, check.Equals, true,
-		check.Commentf("iptables output should have contained %q, but was %q", regex, result.Combined()))
+	assert.Equal(c, matched, true, fmt.Sprintf("iptables output should have contained %q, but was %q", regex, result.Combined()))
 }
 
-func (s *DockerDaemonSuite) TestDaemonICCPing(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonICCPing(c *testing.T) {
 	testRequires(c, bridgeNfIptables)
 	d := s.d
 
@@ -884,9 +884,7 @@ func (s *DockerDaemonSuite) TestDaemonICCPing(c *check.C) {
 	result.Assert(c, icmd.Success)
 	regex := fmt.Sprintf("DROP.*all.*%s.*%s", bridgeName, bridgeName)
 	matched, _ := regexp.MatchString(regex, result.Combined())
-	c.Assert(matched, check.Equals, true,
-		check.Commentf("iptables output should have contained %q, but was %q", regex, result.Combined()))
-
+	assert.Equal(c, matched, true, fmt.Sprintf("iptables output should have contained %q, but was %q", regex, result.Combined()))
 	// Pinging another container must fail with --icc=false
 	pingContainers(c, d, true)
 
@@ -900,10 +898,10 @@ func (s *DockerDaemonSuite) TestDaemonICCPing(c *check.C) {
 	pingCmd := fmt.Sprintf("ping -c 1 %s -W 1", ip.String())
 	runArgs := []string{"run", "--rm", "busybox", "sh", "-c", pingCmd}
 	out, err := d.Cmd(runArgs...)
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *testing.T) {
 	d := s.d
 
 	bridgeName := "external-bridge"
@@ -919,17 +917,15 @@ func (s *DockerDaemonSuite) TestDaemonICCLinkExpose(c *check.C) {
 	result.Assert(c, icmd.Success)
 	regex := fmt.Sprintf("DROP.*all.*%s.*%s", bridgeName, bridgeName)
 	matched, _ := regexp.MatchString(regex, result.Combined())
-	c.Assert(matched, check.Equals, true,
-		check.Commentf("iptables output should have contained %q, but was %q", regex, result.Combined()))
-
+	assert.Equal(c, matched, true, fmt.Sprintf("iptables output should have contained %q, but was %q", regex, result.Combined()))
 	out, err := d.Cmd("run", "-d", "--expose", "4567", "--name", "icc1", "busybox", "nc", "-l", "-p", "4567")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("run", "--link", "icc1:icc1", "busybox", "nc", "icc1", "4567")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *testing.T) {
 	bridgeName := "external-bridge"
 	bridgeIP := "192.169.1.1/24"
 
@@ -940,21 +936,22 @@ func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *che
 	defer s.d.Restart(c)
 
 	out, err := s.d.Cmd("run", "-d", "--name", "child", "--publish", "8080:80", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	out, err = s.d.Cmd("run", "-d", "--name", "parent", "--link", "child:http", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	childIP := s.d.FindContainerIP(c, "child")
 	parentIP := s.d.FindContainerIP(c, "parent")
 
 	sourceRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", childIP, "--sport", "80", "-d", parentIP, "-j", "ACCEPT"}
 	destinationRule := []string{"-i", bridgeName, "-o", bridgeName, "-p", "tcp", "-s", parentIP, "--dport", "80", "-d", childIP, "-j", "ACCEPT"}
-	if !iptables.Exists("filter", "DOCKER", sourceRule...) || !iptables.Exists("filter", "DOCKER", destinationRule...) {
+	iptable := iptables.GetIptable(iptables.IPv4)
+	if !iptable.Exists("filter", "DOCKER", sourceRule...) || !iptable.Exists("filter", "DOCKER", destinationRule...) {
 		c.Fatal("Iptables rules not found")
 	}
 
 	s.d.Cmd("rm", "--link", "parent/http")
-	if iptables.Exists("filter", "DOCKER", sourceRule...) || iptables.Exists("filter", "DOCKER", destinationRule...) {
+	if iptable.Exists("filter", "DOCKER", sourceRule...) || iptable.Exists("filter", "DOCKER", destinationRule...) {
 		c.Fatal("Iptables rules should be removed when unlink")
 	}
 
@@ -962,12 +959,11 @@ func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *che
 	s.d.Cmd("kill", "parent")
 }
 
-func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) {
-	testRequires(c, DaemonIsLinux)
+func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *testing.T) {
 
 	s.d.StartWithBusybox(c, "--default-ulimit", "nofile=42:42", "--default-ulimit", "nproc=1024:1024")
 
-	out, err := s.d.Cmd("run", "--ulimit", "nproc=2048", "--name=test", "busybox", "/bin/sh", "-c", "echo $(ulimit -n); echo $(ulimit -p)")
+	out, err := s.d.Cmd("run", "--ulimit", "nproc=2048", "--name=test", "busybox", "/bin/sh", "-c", "echo $(ulimit -n); echo $(ulimit -u)")
 	if err != nil {
 		c.Fatal(err, out)
 	}
@@ -983,7 +979,7 @@ func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) {
 		c.Fatalf("expected `ulimit -n` to be `42`, got: %s", nofile)
 	}
 	if nproc != "2048" {
-		c.Fatalf("expected `ulimit -p` to be 2048, got: %s", nproc)
+		c.Fatalf("expected `ulimit -u` to be 2048, got: %s", nproc)
 	}
 
 	// Now restart daemon with a new default
@@ -1005,12 +1001,12 @@ func (s *DockerDaemonSuite) TestDaemonUlimitDefaults(c *check.C) {
 		c.Fatalf("expected `ulimit -n` to be `43`, got: %s", nofile)
 	}
 	if nproc != "2048" {
-		c.Fatalf("expected `ulimit -p` to be 2048, got: %s", nproc)
+		c.Fatalf("expected `ulimit -u` to be 2048, got: %s", nproc)
 	}
 }
 
 // #11315
-func (s *DockerDaemonSuite) TestDaemonRestartRenameContainer(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartRenameContainer(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	if out, err := s.d.Cmd("run", "--name=test", "busybox"); err != nil {
@@ -1028,13 +1024,13 @@ func (s *DockerDaemonSuite) TestDaemonRestartRenameContainer(c *check.C) {
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefault(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefault(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("run", "--name=test", "busybox", "echo", "testline")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id, err := s.d.GetIDByName("test")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log")
 
@@ -1066,7 +1062,7 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefault(c *check.C) {
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefaultOverride(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefaultOverride(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("run", "--name=test", "--log-driver=none", "busybox", "echo", "testline")
@@ -1074,7 +1070,7 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefaultOverride(c *check.C) {
 		c.Fatal(out, err)
 	}
 	id, err := s.d.GetIDByName("test")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log")
 
@@ -1083,7 +1079,7 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverDefaultOverride(c *check.C) {
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonLoggingDriverNone(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonLoggingDriverNone(c *testing.T) {
 	s.d.StartWithBusybox(c, "--log-driver=none")
 
 	out, err := s.d.Cmd("run", "--name=test", "busybox", "echo", "testline")
@@ -1091,7 +1087,7 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverNone(c *check.C) {
 		c.Fatal(out, err)
 	}
 	id, err := s.d.GetIDByName("test")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log")
 
@@ -1100,7 +1096,7 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverNone(c *check.C) {
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneOverride(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneOverride(c *testing.T) {
 	s.d.StartWithBusybox(c, "--log-driver=none")
 
 	out, err := s.d.Cmd("run", "--name=test", "--log-driver=json-file", "busybox", "echo", "testline")
@@ -1108,7 +1104,7 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneOverride(c *check.C) {
 		c.Fatal(out, err)
 	}
 	id, err := s.d.GetIDByName("test")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	logPath := filepath.Join(s.d.Root, "containers", id, id+"-json.log")
 
@@ -1140,19 +1136,19 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneOverride(c *check.C) {
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneLogsError(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonLoggingDriverNoneLogsError(c *testing.T) {
 	s.d.StartWithBusybox(c, "--log-driver=none")
 
 	out, err := s.d.Cmd("run", "--name=test", "busybox", "echo", "testline")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("logs", "test")
-	c.Assert(err, check.NotNil, check.Commentf("Logs should fail with 'none' driver"))
+	assert.Assert(c, err != nil, "Logs should fail with 'none' driver")
 	expected := `configured logging driver does not support reading`
-	c.Assert(out, checker.Contains, expected)
+	assert.Assert(c, strings.Contains(out, expected))
 }
 
-func (s *DockerDaemonSuite) TestDaemonLoggingDriverShouldBeIgnoredForBuild(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonLoggingDriverShouldBeIgnoredForBuild(c *testing.T) {
 	s.d.StartWithBusybox(c, "--log-driver=splunk")
 
 	result := cli.BuildCmd(c, "busyboxs", cli.Daemon(s.d),
@@ -1161,14 +1157,14 @@ func (s *DockerDaemonSuite) TestDaemonLoggingDriverShouldBeIgnoredForBuild(c *ch
         RUN echo foo`),
 		build.WithoutCache,
 	)
-	comment := check.Commentf("Failed to build image. output %s, exitCode %d, err %v", result.Combined(), result.ExitCode, result.Error)
-	c.Assert(result.Error, check.IsNil, comment)
-	c.Assert(result.ExitCode, check.Equals, 0, comment)
-	c.Assert(result.Combined(), checker.Contains, "foo", comment)
+	comment := fmt.Sprintf("Failed to build image. output %s, exitCode %d, err %v", result.Combined(), result.ExitCode, result.Error)
+	assert.Assert(c, result.Error == nil, comment)
+	assert.Equal(c, result.ExitCode, 0, comment)
+	assert.Assert(c, strings.Contains(result.Combined(), "foo"), comment)
 }
 
-func (s *DockerDaemonSuite) TestDaemonUnixSockCleanedUp(c *check.C) {
-	dir, err := ioutil.TempDir("", "socket-cleanup-test")
+func (s *DockerDaemonSuite) TestDaemonUnixSockCleanedUp(c *testing.T) {
+	dir, err := os.MkdirTemp("", "socket-cleanup-test")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -1188,7 +1184,7 @@ func (s *DockerDaemonSuite) TestDaemonUnixSockCleanedUp(c *check.C) {
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonWithWrongkey(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonWithWrongkey(c *testing.T) {
 	type Config struct {
 		Crv string `json:"crv"`
 		D   string `json:"d"`
@@ -1203,7 +1199,7 @@ func (s *DockerDaemonSuite) TestDaemonWithWrongkey(c *check.C) {
 	s.d.Stop(c)
 
 	config := &Config{}
-	bytes, err := ioutil.ReadFile("/etc/docker/key.json")
+	bytes, err := os.ReadFile("/etc/docker/key.json")
 	if err != nil {
 		c.Fatalf("Error reading key.json file: %s", err)
 	}
@@ -1213,7 +1209,7 @@ func (s *DockerDaemonSuite) TestDaemonWithWrongkey(c *check.C) {
 		c.Fatalf("Error Unmarshal: %s", err)
 	}
 
-	//replace config.Kid with the fake value
+	// replace config.Kid with the fake value
 	config.Kid = "VSAJ:FUYR:X3H2:B2VZ:KZ6U:CJD5:K7BX:ZXHY:UZXT:P4FT:MJWG:HRJ4"
 
 	// NEW Data-Struct to byte[]
@@ -1223,8 +1219,8 @@ func (s *DockerDaemonSuite) TestDaemonWithWrongkey(c *check.C) {
 	}
 
 	// write back
-	if err := ioutil.WriteFile("/etc/docker/key.json", newBytes, 0400); err != nil {
-		c.Fatalf("Error ioutil.WriteFile: %s", err)
+	if err := os.WriteFile("/etc/docker/key.json", newBytes, 0400); err != nil {
+		c.Fatalf("Error os.WriteFile: %s", err)
 	}
 
 	defer os.Remove("/etc/docker/key.json")
@@ -1234,14 +1230,14 @@ func (s *DockerDaemonSuite) TestDaemonWithWrongkey(c *check.C) {
 	}
 
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
+	assert.Assert(c, err == nil)
 
 	if !strings.Contains(string(content), "Public Key ID does not match") {
 		c.Fatalf("Missing KeyID message from daemon logs: %s", string(content))
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("run", "-id", "busybox", "/bin/cat")
@@ -1256,7 +1252,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *check.C) {
 
 	s.d.Restart(c)
 
-	errchan := make(chan error)
+	errchan := make(chan error, 1)
 	go func() {
 		if out, err := s.d.Cmd("wait", containerID); err != nil {
 			errchan <- fmt.Errorf("%v:\n%s", err, out)
@@ -1275,7 +1271,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *check.C) {
 }
 
 // TestHTTPSInfo connects via two-way authenticated HTTPS to the info endpoint
-func (s *DockerDaemonSuite) TestHTTPSInfo(c *check.C) {
+func (s *DockerDaemonSuite) TestHTTPSInfo(c *testing.T) {
 	const (
 		testDaemonHTTPSAddr = "tcp://localhost:4271"
 	)
@@ -1303,7 +1299,7 @@ func (s *DockerDaemonSuite) TestHTTPSInfo(c *check.C) {
 
 // TestHTTPSRun connects via two-way authenticated HTTPS to the create, attach, start, and wait endpoints.
 // https://github.com/docker/docker/issues/19280
-func (s *DockerDaemonSuite) TestHTTPSRun(c *check.C) {
+func (s *DockerDaemonSuite) TestHTTPSRun(c *testing.T) {
 	const (
 		testDaemonHTTPSAddr = "tcp://localhost:4271"
 	)
@@ -1329,7 +1325,7 @@ func (s *DockerDaemonSuite) TestHTTPSRun(c *check.C) {
 }
 
 // TestTLSVerify verifies that --tlsverify=false turns on tls
-func (s *DockerDaemonSuite) TestTLSVerify(c *check.C) {
+func (s *DockerDaemonSuite) TestTLSVerify(c *testing.T) {
 	out, err := exec.Command(dockerdBinary, "--tlsverify=false").CombinedOutput()
 	if err == nil || !strings.Contains(string(out), "Could not load X509 key pair") {
 		c.Fatalf("Daemon should not have started due to missing certs: %v\n%s", err, string(out))
@@ -1338,7 +1334,7 @@ func (s *DockerDaemonSuite) TestTLSVerify(c *check.C) {
 
 // TestHTTPSInfoRogueCert connects via two-way authenticated HTTPS to the info endpoint
 // by using a rogue client certificate and checks that it fails with the expected error.
-func (s *DockerDaemonSuite) TestHTTPSInfoRogueCert(c *check.C) {
+func (s *DockerDaemonSuite) TestHTTPSInfoRogueCert(c *testing.T) {
 	const (
 		errBadCertificate   = "bad certificate"
 		testDaemonHTTPSAddr = "tcp://localhost:4271"
@@ -1367,7 +1363,7 @@ func (s *DockerDaemonSuite) TestHTTPSInfoRogueCert(c *check.C) {
 
 // TestHTTPSInfoRogueServerCert connects via two-way authenticated HTTPS to the info endpoint
 // which provides a rogue server certificate and checks that it fails with the expected error
-func (s *DockerDaemonSuite) TestHTTPSInfoRogueServerCert(c *check.C) {
+func (s *DockerDaemonSuite) TestHTTPSInfoRogueServerCert(c *testing.T) {
 	const (
 		errCaUnknown             = "x509: certificate signed by unknown authority"
 		testDaemonRogueHTTPSAddr = "tcp://localhost:4272"
@@ -1393,7 +1389,7 @@ func (s *DockerDaemonSuite) TestHTTPSInfoRogueServerCert(c *check.C) {
 	}
 }
 
-func pingContainers(c *check.C, d *daemon.Daemon, expectFailure bool) {
+func pingContainers(c *testing.T, d *daemon.Daemon, expectFailure bool) {
 	var dargs []string
 	if d != nil {
 		dargs = []string{"--host", d.Sock()}
@@ -1408,86 +1404,85 @@ func pingContainers(c *check.C, d *daemon.Daemon, expectFailure bool) {
 	_, _, err := dockerCmdWithError(args...)
 
 	if expectFailure {
-		c.Assert(err, check.NotNil)
+		assert.ErrorContains(c, err, "")
 	} else {
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	}
 
 	args = append(dargs, "rm", "-f", "container1")
 	dockerCmd(c, args...)
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartWithSocketAsVolume(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithSocketAsVolume(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	socket := filepath.Join(s.d.Folder, "docker.sock")
 
 	out, err := s.d.Cmd("run", "--restart=always", "-v", socket+":/sock", "busybox")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 	s.d.Restart(c)
 }
 
 // os.Kill should kill daemon ungracefully, leaving behind container mounts.
 // A subsequent daemon restart should clean up said mounts.
-func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonAndContainerKill(c *check.C) {
+func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonAndContainerKill(c *testing.T) {
 	d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
 	d.StartWithBusybox(c)
 
 	out, err := d.Cmd("run", "-d", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
+
 	id := strings.TrimSpace(out)
 
 	// If there are no mounts with container id visible from the host
 	// (as those are in container's own mount ns), there is nothing
 	// to check here and the test should be skipped.
-	mountOut, err := ioutil.ReadFile("/proc/self/mountinfo")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", mountOut))
+	mountOut, err := os.ReadFile("/proc/self/mountinfo")
+	assert.NilError(c, err, "Output: %s", mountOut)
 	if !strings.Contains(string(mountOut), id) {
 		d.Stop(c)
 		c.Skip("no container mounts visible in host ns")
 	}
 
 	// kill the daemon
-	c.Assert(d.Kill(), check.IsNil)
+	assert.NilError(c, d.Kill())
 
 	// kill the container
 	icmd.RunCommand(ctrBinary, "--address", containerdSocket,
-		"--namespace", moby_daemon.ContainersNamespace, "tasks", "kill", id).Assert(c, icmd.Success)
+		"--namespace", d.ContainersNamespace(), "tasks", "kill", id).Assert(c, icmd.Success)
 
 	// restart daemon.
 	d.Restart(c)
 
 	// Now, container mounts should be gone.
-	mountOut, err = ioutil.ReadFile("/proc/self/mountinfo")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", mountOut))
-	comment := check.Commentf("%s is still mounted from older daemon start:\nDaemon root repository %s\n%s", id, d.Root, mountOut)
-	c.Assert(strings.Contains(string(mountOut), id), check.Equals, false, comment)
+	mountOut, err = os.ReadFile("/proc/self/mountinfo")
+	assert.NilError(c, err, "Output: %s", mountOut)
+	assert.Assert(c, !strings.Contains(string(mountOut), id), "%s is still mounted from older daemon start:\nDaemon root repository %s\n%s", id, d.Root, mountOut)
 
 	d.Stop(c)
 }
 
 // os.Interrupt should perform a graceful daemon shutdown and hence cleanup mounts.
-func (s *DockerDaemonSuite) TestCleanupMountsAfterGracefulShutdown(c *check.C) {
+func (s *DockerDaemonSuite) TestCleanupMountsAfterGracefulShutdown(c *testing.T) {
 	d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
 	d.StartWithBusybox(c)
 
 	out, err := d.Cmd("run", "-d", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 	id := strings.TrimSpace(out)
 
 	// Send SIGINT and daemon should clean up
-	c.Assert(d.Signal(os.Interrupt), check.IsNil)
+	assert.NilError(c, d.Signal(os.Interrupt))
 	// Wait for the daemon to stop.
-	c.Assert(<-d.Wait, checker.IsNil)
+	assert.NilError(c, <-d.Wait)
 
-	mountOut, err := ioutil.ReadFile("/proc/self/mountinfo")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", mountOut))
+	mountOut, err := os.ReadFile("/proc/self/mountinfo")
+	assert.NilError(c, err, "Output: %s", mountOut)
 
-	comment := check.Commentf("%s is still mounted from older daemon start:\nDaemon root repository %s\n%s", id, d.Root, mountOut)
-	c.Assert(strings.Contains(string(mountOut), id), check.Equals, false, comment)
+	assert.Assert(c, !strings.Contains(string(mountOut), id), "%s is still mounted from older daemon start:\nDaemon root repository %s\n%s", id, d.Root, mountOut)
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartWithContainerRunning(t *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithContainerRunning(t *testing.T) {
 	s.d.StartWithBusybox(t)
 	if out, err := s.d.Cmd("run", "-d", "--name", "test", "busybox", "top"); err != nil {
 		t.Fatal(out, err)
@@ -1500,7 +1495,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithContainerRunning(t *check.C) {
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartCleanupNetns(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartCleanupNetns(c *testing.T) {
 	s.d.StartWithBusybox(c)
 	out, err := s.d.Cmd("run", "--name", "netns", "-d", "busybox", "top")
 	if err != nil {
@@ -1538,7 +1533,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartCleanupNetns(c *check.C) {
 }
 
 // tests regression detailed in #13964 where DOCKER_TLS_VERIFY env is ignored
-func (s *DockerDaemonSuite) TestDaemonTLSVerifyIssue13964(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonTLSVerifyIssue13964(c *testing.T) {
 	host := "tcp://localhost:4271"
 	s.d.Start(c, "-H", host)
 	icmd.RunCmd(icmd.Cmd{
@@ -1550,57 +1545,57 @@ func (s *DockerDaemonSuite) TestDaemonTLSVerifyIssue13964(c *check.C) {
 	})
 }
 
-func setupV6(c *check.C) {
+func setupV6(c *testing.T) {
 	// Hack to get the right IPv6 address on docker0, which has already been created
 	result := icmd.RunCommand("ip", "addr", "add", "fe80::1/64", "dev", "docker0")
 	result.Assert(c, icmd.Success)
 }
 
-func teardownV6(c *check.C) {
+func teardownV6(c *testing.T) {
 	result := icmd.RunCommand("ip", "addr", "del", "fe80::1/64", "dev", "docker0")
 	result.Assert(c, icmd.Success)
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartWithContainerWithRestartPolicyAlways(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithContainerWithRestartPolicyAlways(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("run", "-d", "--restart", "always", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	out, err = s.d.Cmd("stop", id)
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	out, err = s.d.Cmd("wait", id)
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("ps", "-q")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
-	c.Assert(out, check.Equals, "")
+	assert.NilError(c, err, out)
+	assert.Equal(c, out, "")
 
 	s.d.Restart(c)
 
 	out, err = s.d.Cmd("ps", "-q")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), check.Equals, id[:12])
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), id[:12])
 }
 
-func (s *DockerDaemonSuite) TestDaemonWideLogConfig(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonWideLogConfig(c *testing.T) {
 	s.d.StartWithBusybox(c, "--log-opt=max-size=1k")
 	name := "logtest"
 	out, err := s.d.Cmd("run", "-d", "--log-opt=max-file=5", "--name", name, "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s, err: %v", out, err))
+	assert.NilError(c, err, "Output: %s, err: %v", out, err)
 
 	out, err = s.d.Cmd("inspect", "-f", "{{ .HostConfig.LogConfig.Config }}", name)
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
-	c.Assert(out, checker.Contains, "max-size:1k")
-	c.Assert(out, checker.Contains, "max-file:5")
+	assert.NilError(c, err, "Output: %s", out)
+	assert.Assert(c, strings.Contains(out, "max-size:1k"))
+	assert.Assert(c, strings.Contains(out, "max-file:5"))
 
 	out, err = s.d.Cmd("inspect", "-f", "{{ .HostConfig.LogConfig.Type }}", name)
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "json-file")
+	assert.NilError(c, err, "Output: %s", out)
+	assert.Equal(c, strings.TrimSpace(out), "json-file")
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartWithPausedContainer(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithPausedContainer(c *testing.T) {
 	s.d.StartWithBusybox(c)
 	if out, err := s.d.Cmd("run", "-i", "-d", "--name", "test", "busybox", "top"); err != nil {
 		c.Fatal(err, out)
@@ -1610,15 +1605,17 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPausedContainer(c *check.C) {
 	}
 	s.d.Restart(c)
 
-	errchan := make(chan error)
+	errchan := make(chan error, 1)
 	go func() {
 		out, err := s.d.Cmd("start", "test")
 		if err != nil {
 			errchan <- fmt.Errorf("%v:\n%s", err, out)
+			return
 		}
 		name := strings.TrimSpace(out)
 		if name != "test" {
 			errchan <- fmt.Errorf("Paused container start error on docker daemon restart, expected 'test' but got '%s'", name)
+			return
 		}
 		close(errchan)
 	}()
@@ -1633,48 +1630,48 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPausedContainer(c *check.C) {
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartRmVolumeInUse(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartRmVolumeInUse(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("create", "-v", "test:/foo", "busybox")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	s.d.Restart(c)
 
 	out, err = s.d.Cmd("volume", "rm", "test")
-	c.Assert(err, check.NotNil, check.Commentf("should not be able to remove in use volume after daemon restart"))
-	c.Assert(out, checker.Contains, "in use")
+	assert.Assert(c, err != nil, "should not be able to remove in use volume after daemon restart")
+	assert.Assert(c, strings.Contains(out, "in use"))
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartLocalVolumes(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartLocalVolumes(c *testing.T) {
 	s.d.Start(c)
 
 	out, err := s.d.Cmd("volume", "create", "test")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	s.d.Restart(c)
 
 	out, err = s.d.Cmd("volume", "inspect", "test")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
 // FIXME(vdemeester) should be a unit test
-func (s *DockerDaemonSuite) TestDaemonCorruptedLogDriverAddress(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonCorruptedLogDriverAddress(c *testing.T) {
 	d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
-	c.Assert(d.StartWithError("--log-driver=syslog", "--log-opt", "syslog-address=corrupted:42"), check.NotNil)
+	assert.Assert(c, d.StartWithError("--log-driver=syslog", "--log-opt", "syslog-address=corrupted:42") != nil)
 	expected := "syslog-address should be in form proto://address"
 	icmd.RunCommand("grep", expected, d.LogFileName()).Assert(c, icmd.Success)
 }
 
 // FIXME(vdemeester) should be a unit test
-func (s *DockerDaemonSuite) TestDaemonCorruptedFluentdAddress(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonCorruptedFluentdAddress(c *testing.T) {
 	d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
-	c.Assert(d.StartWithError("--log-driver=fluentd", "--log-opt", "fluentd-address=corrupted:c"), check.NotNil)
+	assert.Assert(c, d.StartWithError("--log-driver=fluentd", "--log-opt", "fluentd-address=corrupted:c") != nil)
 	expected := "invalid fluentd-address corrupted:c: "
 	icmd.RunCommand("grep", expected, d.LogFileName()).Assert(c, icmd.Success)
 }
 
 // FIXME(vdemeester) Use a new daemon instance instead of the Suite one
-func (s *DockerDaemonSuite) TestDaemonStartWithoutHost(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonStartWithoutHost(c *testing.T) {
 	s.d.UseDefaultHost = true
 	defer func() {
 		s.d.UseDefaultHost = false
@@ -1683,7 +1680,7 @@ func (s *DockerDaemonSuite) TestDaemonStartWithoutHost(c *check.C) {
 }
 
 // FIXME(vdemeester) Use a new daemon instance instead of the Suite one
-func (s *DockerDaemonSuite) TestDaemonStartWithDefaultTLSHost(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonStartWithDefaultTLSHost(c *testing.T) {
 	s.d.UseDefaultTLSHost = true
 	defer func() {
 		s.d.UseDefaultTLSHost = false
@@ -1715,10 +1712,10 @@ func (s *DockerDaemonSuite) TestDaemonStartWithDefaultTLSHost(c *check.C) {
 	}
 
 	// ensure when connecting to the server that only a single acceptable CA is requested
-	contents, err := ioutil.ReadFile("fixtures/https/ca.pem")
-	c.Assert(err, checker.IsNil)
+	contents, err := os.ReadFile("fixtures/https/ca.pem")
+	assert.NilError(c, err)
 	rootCert, err := helpers.ParseCertificatePEM(contents)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	rootPool := x509.NewCertPool()
 	rootPool.AddCert(rootCert)
 
@@ -1734,15 +1731,15 @@ func (s *DockerDaemonSuite) TestDaemonStartWithDefaultTLSHost(c *check.C) {
 			return &cert, nil
 		},
 	})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	conn.Close()
 
-	c.Assert(certRequestInfo, checker.NotNil)
-	c.Assert(certRequestInfo.AcceptableCAs, checker.HasLen, 1)
-	c.Assert(certRequestInfo.AcceptableCAs[0], checker.DeepEquals, rootCert.RawSubject)
+	assert.Assert(c, certRequestInfo != nil)
+	assert.Equal(c, len(certRequestInfo.AcceptableCAs), 1)
+	assert.DeepEqual(c, certRequestInfo.AcceptableCAs[0], rootCert.RawSubject)
 }
 
-func (s *DockerDaemonSuite) TestBridgeIPIsExcludedFromAllocatorPool(c *check.C) {
+func (s *DockerDaemonSuite) TestBridgeIPIsExcludedFromAllocatorPool(c *testing.T) {
 	defaultNetworkBridge := "docker0"
 	deleteInterface(c, defaultNetworkBridge)
 
@@ -1761,42 +1758,42 @@ func (s *DockerDaemonSuite) TestBridgeIPIsExcludedFromAllocatorPool(c *check.C)
 			break
 		}
 		ip, err := s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.IPAddress}}'", contName)
-		c.Assert(err, check.IsNil, check.Commentf("%s", ip))
+		assert.Assert(c, err == nil, ip)
 
-		c.Assert(ip, check.Not(check.Equals), bridgeIP)
+		assert.Assert(c, ip != bridgeIP)
 		cont++
 	}
 }
 
 // Test daemon for no space left on device error
-func (s *DockerDaemonSuite) TestDaemonNoSpaceLeftOnDeviceError(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux, Network)
+func (s *DockerDaemonSuite) TestDaemonNoSpaceLeftOnDeviceError(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, Network)
 
-	testDir, err := ioutil.TempDir("", "no-space-left-on-device-test")
-	c.Assert(err, checker.IsNil)
+	testDir, err := os.MkdirTemp("", "no-space-left-on-device-test")
+	assert.NilError(c, err)
 	defer os.RemoveAll(testDir)
-	c.Assert(mount.MakeRShared(testDir), checker.IsNil)
+	assert.Assert(c, mount.MakeRShared(testDir) == nil)
 	defer mount.Unmount(testDir)
 
 	// create a 3MiB image (with a 2MiB ext4 fs) and mount it as graph root
-	// Why in a container? Because `mount` sometimes behaves weirdly and often fails outright on this test in debian:jessie (which is what the test suite runs under if run from the Makefile)
+	// Why in a container? Because `mount` sometimes behaves weirdly and often fails outright on this test in debian:bullseye (which is what the test suite runs under if run from the Makefile)
 	dockerCmd(c, "run", "--rm", "-v", testDir+":/test", "busybox", "sh", "-c", "dd of=/test/testfs.img bs=1M seek=3 count=0")
 	icmd.RunCommand("mkfs.ext4", "-F", filepath.Join(testDir, "testfs.img")).Assert(c, icmd.Success)
 
-	dockerCmd(c, "run", "--privileged", "--rm", "-v", testDir+":/test:shared", "busybox", "sh", "-c", "mkdir -p /test/test-mount/vfs && mount -n /test/testfs.img /test/test-mount/vfs")
+	dockerCmd(c, "run", "--privileged", "--rm", "-v", testDir+":/test:shared", "busybox", "sh", "-c", "mkdir -p /test/test-mount/vfs && mount -n -t ext4 /test/testfs.img /test/test-mount/vfs")
 	defer mount.Unmount(filepath.Join(testDir, "test-mount"))
 
 	s.d.Start(c, "--storage-driver", "vfs", "--data-root", filepath.Join(testDir, "test-mount"))
 	defer s.d.Stop(c)
 
 	// pull a repository large enough to overfill the mounted filesystem
-	pullOut, err := s.d.Cmd("pull", "debian:stretch")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", pullOut))
-	c.Assert(pullOut, checker.Contains, "no space left on device")
+	pullOut, err := s.d.Cmd("pull", "debian:bullseye-slim")
+	assert.Assert(c, err != nil, pullOut)
+	assert.Assert(c, strings.Contains(pullOut, "no space left on device"))
 }
 
 // Test daemon restart with container links + auto restart
-func (s *DockerDaemonSuite) TestDaemonRestartContainerLinksRestart(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartContainerLinksRestart(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	var parent1Args []string
@@ -1825,7 +1822,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartContainerLinksRestart(c *check.C) {
 	wg.Wait()
 	close(chErr)
 	for err := range chErr {
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	}
 
 	parent1Args = append([]string{"run", "-d"}, parent1Args...)
@@ -1834,9 +1831,9 @@ func (s *DockerDaemonSuite) TestDaemonRestartContainerLinksRestart(c *check.C) {
 	parent2Args = append(parent2Args, []string{"--name=parent2", "--restart=always", "busybox", "top"}...)
 
 	_, err := s.d.Cmd(parent1Args...)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, err = s.d.Cmd(parent2Args...)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	s.d.Stop(c)
 	// clear the log file -- we don't need any of it but may for the next part
@@ -1846,15 +1843,15 @@ func (s *DockerDaemonSuite) TestDaemonRestartContainerLinksRestart(c *check.C) {
 
 	for _, num := range []string{"1", "2"} {
 		out, err := s.d.Cmd("inspect", "-f", "{{ .State.Running }}", "parent"+num)
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 		if strings.TrimSpace(out) != "true" {
-			log, _ := ioutil.ReadFile(s.d.LogFileName())
+			log, _ := os.ReadFile(s.d.LogFileName())
 			c.Fatalf("parent container is not running\n%s", string(log))
 		}
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonCgroupParent(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonCgroupParent(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	cgroupParent := "test"
@@ -1864,12 +1861,12 @@ func (s *DockerDaemonSuite) TestDaemonCgroupParent(c *check.C) {
 	defer s.d.Restart(c)
 
 	out, err := s.d.Cmd("run", "--name", name, "busybox", "cat", "/proc/self/cgroup")
-	c.Assert(err, checker.IsNil)
-	cgroupPaths := ParseCgroupPaths(string(out))
-	c.Assert(len(cgroupPaths), checker.Not(checker.Equals), 0, check.Commentf("unexpected output - %q", string(out)))
+	assert.NilError(c, err)
+	cgroupPaths := ParseCgroupPaths(out)
+	assert.Assert(c, len(cgroupPaths) != 0, "unexpected output - %q", out)
 	out, err = s.d.Cmd("inspect", "-f", "{{.Id}}", name)
-	c.Assert(err, checker.IsNil)
-	id := strings.TrimSpace(string(out))
+	assert.NilError(c, err)
+	id := strings.TrimSpace(out)
 	expectedCgroup := path.Join(cgroupParent, id)
 	found := false
 	for _, path := range cgroupPaths {
@@ -1878,57 +1875,57 @@ func (s *DockerDaemonSuite) TestDaemonCgroupParent(c *check.C) {
 			break
 		}
 	}
-	c.Assert(found, checker.True, check.Commentf("Cgroup path for container (%s) doesn't found in cgroups file: %s", expectedCgroup, cgroupPaths))
+	assert.Assert(c, found, "Cgroup path for container (%s) doesn't found in cgroups file: %s", expectedCgroup, cgroupPaths)
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartWithLinks(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithLinks(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support links
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("run", "-d", "--name=test", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("run", "--name=test2", "--link", "test:abc", "busybox", "sh", "-c", "ping -c 1 -w 1 abc")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	s.d.Restart(c)
 
 	// should fail since test is not running yet
 	out, err = s.d.Cmd("start", "test2")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 
 	out, err = s.d.Cmd("start", "test")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	out, err = s.d.Cmd("start", "-a", "test2")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.Contains(out, "1 packets transmitted, 1 packets received"), check.Equals, true, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.Contains(out, "1 packets transmitted, 1 packets received"), true, out)
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartWithNames(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithNames(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support links
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("create", "--name=test", "busybox")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("run", "-d", "--name=test2", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	test2ID := strings.TrimSpace(out)
 
 	out, err = s.d.Cmd("run", "-d", "--name=test3", "--link", "test2:abc", "busybox", "top")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	test3ID := strings.TrimSpace(out)
 
 	s.d.Restart(c)
 
 	_, err = s.d.Cmd("create", "--name=test", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("expected error trying to create container with duplicate name"))
+	assert.ErrorContains(c, err, "", "expected error trying to create container with duplicate name")
 	// this one is no longer needed, removing simplifies the remainder of the test
 	out, err = s.d.Cmd("rm", "-f", "test")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("ps", "-a", "--no-trunc")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	lines := strings.Split(strings.TrimSpace(out), "\n")[1:]
 
@@ -1939,20 +1936,20 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithNames(c *check.C) {
 		names := fields[len(fields)-1]
 		switch fields[0] {
 		case test2ID:
-			c.Assert(names, check.Equals, "test2,test3/abc")
+			assert.Equal(c, names, "test2,test3/abc")
 			test2validated = true
 		case test3ID:
-			c.Assert(names, check.Equals, "test3")
+			assert.Equal(c, names, "test3")
 			test3validated = true
 		}
 	}
 
-	c.Assert(test2validated, check.Equals, true)
-	c.Assert(test3validated, check.Equals, true)
+	assert.Assert(c, test2validated)
+	assert.Assert(c, test3validated)
 }
 
 // TestDaemonRestartWithKilledRunningContainer requires live restore of running containers
-func (s *DockerDaemonSuite) TestDaemonRestartWithKilledRunningContainer(t *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithKilledRunningContainer(t *testing.T) {
 	testRequires(t, DaemonIsLinux)
 	s.d.StartWithBusybox(t)
 
@@ -1964,7 +1961,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithKilledRunningContainer(t *check
 	cid = strings.TrimSpace(cid)
 
 	pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", cid)
-	t.Assert(err, check.IsNil)
+	assert.NilError(t, err)
 	pid = strings.TrimSpace(pid)
 
 	// Kill the daemon
@@ -1974,7 +1971,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithKilledRunningContainer(t *check
 
 	// kill the container
 	icmd.RunCommand(ctrBinary, "--address", containerdSocket,
-		"--namespace", moby_daemon.ContainersNamespace, "tasks", "kill", cid).Assert(t, icmd.Success)
+		"--namespace", s.d.ContainersNamespace(), "tasks", "kill", cid).Assert(t, icmd.Success)
 
 	// Give time to containerd to process the command if we don't
 	// the exit event might be received after we do the inspect
@@ -1990,7 +1987,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithKilledRunningContainer(t *check
 
 	// Check that we've got the correct exit code
 	out, err := s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", cid)
-	t.Assert(err, check.IsNil)
+	assert.NilError(t, err)
 
 	out = strings.TrimSpace(out)
 	if out != "143" {
@@ -2002,23 +1999,23 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithKilledRunningContainer(t *check
 // os.Kill should kill daemon ungracefully, leaving behind live containers.
 // The live containers should be known to the restarted daemon. Stopping
 // them now, should remove the mounts.
-func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonCrash(c *check.C) {
+func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonCrash(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	s.d.StartWithBusybox(c, "--live-restore")
 
 	out, err := s.d.Cmd("run", "-d", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 	id := strings.TrimSpace(out)
 
 	// kill the daemon
-	c.Assert(s.d.Kill(), check.IsNil)
+	assert.Assert(c, s.d.Kill() == nil)
 
 	// Check if there are mounts with container id visible from the host.
 	// If not, those mounts exist in container's own mount ns, and so
 	// the following check for mounts being cleared is pointless.
 	skipMountCheck := false
-	mountOut, err := ioutil.ReadFile("/proc/self/mountinfo")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", mountOut))
+	mountOut, err := os.ReadFile("/proc/self/mountinfo")
+	assert.Assert(c, err == nil, "Output: %s", mountOut)
 	if !strings.Contains(string(mountOut), id) {
 		skipMountCheck = true
 	}
@@ -2028,7 +2025,7 @@ func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonCrash(c *check.C) {
 
 	// container should be running.
 	out, err = s.d.Cmd("inspect", "--format={{.State.Running}}", id)
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 	out = strings.TrimSpace(out)
 	if out != "true" {
 		c.Fatalf("Container %s expected to stay alive after daemon restart", id)
@@ -2036,20 +2033,20 @@ func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonCrash(c *check.C) {
 
 	// 'docker stop' should work.
 	out, err = s.d.Cmd("stop", id)
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 
 	if skipMountCheck {
 		return
 	}
 	// Now, container mounts should be gone.
-	mountOut, err = ioutil.ReadFile("/proc/self/mountinfo")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", mountOut))
-	comment := check.Commentf("%s is still mounted from older daemon start:\nDaemon root repository %s\n%s", id, s.d.Root, mountOut)
-	c.Assert(strings.Contains(string(mountOut), id), check.Equals, false, comment)
+	mountOut, err = os.ReadFile("/proc/self/mountinfo")
+	assert.Assert(c, err == nil, "Output: %s", mountOut)
+	comment := fmt.Sprintf("%s is still mounted from older daemon start:\nDaemon root repository %s\n%s", id, s.d.Root, mountOut)
+	assert.Equal(c, strings.Contains(string(mountOut), id), false, comment)
 }
 
 // TestDaemonRestartWithUnpausedRunningContainer requires live restore of running containers.
-func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *testing.T) {
 	testRequires(t, DaemonIsLinux)
 	s.d.StartWithBusybox(t, "--live-restore")
 
@@ -2061,7 +2058,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *che
 	cid = strings.TrimSpace(cid)
 
 	pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", cid)
-	t.Assert(err, check.IsNil)
+	assert.NilError(t, err)
 
 	// pause the container
 	if _, err := s.d.Cmd("pause", cid); err != nil {
@@ -2077,23 +2074,23 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *che
 	result := icmd.RunCommand(
 		ctrBinary,
 		"--address", containerdSocket,
-		"--namespace", moby_daemon.ContainersNamespace,
+		"--namespace", s.d.ContainersNamespace(),
 		"tasks", "resume", cid)
 	result.Assert(t, icmd.Success)
 
 	// Give time to containerd to process the command if we don't
 	// the resume event might be received after we do the inspect
-	waitAndAssert(t, defaultReconciliationTimeout, func(*check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(t, pollCheck(t, func(*testing.T) (interface{}, string) {
 		result := icmd.RunCommand("kill", "-0", strings.TrimSpace(pid))
-		return result.ExitCode, nil
-	}, checker.Equals, 0)
+		return result.ExitCode, ""
+	}, checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// restart the daemon
 	s.d.Start(t, "--live-restore")
 
 	// Check that we've got the correct status
 	out, err := s.d.Cmd("inspect", "-f", "{{.State.Status}}", cid)
-	t.Assert(err, check.IsNil)
+	assert.NilError(t, err)
 
 	out = strings.TrimSpace(out)
 	if out != "running" {
@@ -2106,33 +2103,31 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithUnpausedRunningContainer(t *che
 
 // TestRunLinksChanged checks that creating a new container with the same name does not update links
 // this ensures that the old, pre gh#16032 functionality continues on
-func (s *DockerDaemonSuite) TestRunLinksChanged(c *check.C) {
+func (s *DockerDaemonSuite) TestRunLinksChanged(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support links
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("run", "-d", "--name=test", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("run", "--name=test2", "--link=test:abc", "busybox", "sh", "-c", "ping -c 1 abc")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "1 packets transmitted, 1 packets received")
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, "1 packets transmitted, 1 packets received"))
 	out, err = s.d.Cmd("rm", "-f", "test")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = s.d.Cmd("run", "-d", "--name=test", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	out, err = s.d.Cmd("start", "-a", "test2")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, check.Not(checker.Contains), "1 packets transmitted, 1 packets received")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, !strings.Contains(out, "1 packets transmitted, 1 packets received"))
 	s.d.Restart(c)
 	out, err = s.d.Cmd("start", "-a", "test2")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, check.Not(checker.Contains), "1 packets transmitted, 1 packets received")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, !strings.Contains(out, "1 packets transmitted, 1 packets received"))
 }
 
-func (s *DockerDaemonSuite) TestDaemonStartWithoutColors(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonStartWithoutColors(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	infoLog := "\x1b[36mINFO\x1b"
@@ -2141,7 +2136,7 @@ func (s *DockerDaemonSuite) TestDaemonStartWithoutColors(c *check.C) {
 	done := make(chan bool)
 
 	p, tty, err := pty.Open()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer func() {
 		tty.Close()
 		p.Close()
@@ -2157,8 +2152,7 @@ func (s *DockerDaemonSuite) TestDaemonStartWithoutColors(c *check.C) {
 	s.d.Stop(c)
 	// Wait for io.Copy() before checking output
 	<-done
-	c.Assert(b.String(), checker.Contains, infoLog)
-
+	assert.Assert(c, strings.Contains(b.String(), infoLog))
 	b.Reset()
 
 	// "tty" is already closed in prev s.d.Stop(),
@@ -2166,7 +2160,7 @@ func (s *DockerDaemonSuite) TestDaemonStartWithoutColors(c *check.C) {
 	// pty for the next test.
 	p.Close()
 	p, tty, err = pty.Open()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	go func() {
 		io.Copy(b, p)
@@ -2178,17 +2172,17 @@ func (s *DockerDaemonSuite) TestDaemonStartWithoutColors(c *check.C) {
 	s.d.Stop(c)
 	// Wait for io.Copy() before checking output
 	<-done
-	c.Assert(b.String(), check.Not(check.Equals), "")
-	c.Assert(b.String(), check.Not(checker.Contains), infoLog)
+	assert.Assert(c, b.String() != "")
+	assert.Assert(c, !strings.Contains(b.String(), infoLog))
 }
 
-func (s *DockerDaemonSuite) TestDaemonDebugLog(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonDebugLog(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	debugLog := "\x1b[37mDEBU\x1b"
 
 	p, tty, err := pty.Open()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer func() {
 		tty.Close()
 		p.Close()
@@ -2199,16 +2193,16 @@ func (s *DockerDaemonSuite) TestDaemonDebugLog(c *check.C) {
 
 	s.d.StartWithLogFile(tty, "--debug")
 	s.d.Stop(c)
-	c.Assert(b.String(), checker.Contains, debugLog)
+	assert.Assert(c, strings.Contains(b.String(), debugLog))
 }
 
-func (s *DockerDaemonSuite) TestDaemonDiscoveryBackendConfigReload(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerDaemonSuite) TestDaemonDiscoveryBackendConfigReload(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	// daemon config file
 	daemonConfig := `{ "debug" : false }`
-	configFile, err := ioutil.TempFile("", "test-daemon-discovery-backend-config-reload-config")
-	c.Assert(err, checker.IsNil, check.Commentf("could not create temp file for config reload"))
+	configFile, err := os.CreateTemp("", "test-daemon-discovery-backend-config-reload-config")
+	assert.Assert(c, err == nil, "could not create temp file for config reload")
 	configFilePath := configFile.Name()
 	defer func() {
 		configFile.Close()
@@ -2216,7 +2210,7 @@ func (s *DockerDaemonSuite) TestDaemonDiscoveryBackendConfigReload(c *check.C) {
 	}()
 
 	_, err = configFile.Write([]byte(daemonConfig))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// --log-level needs to be set so that d.Start() doesn't add --debug causing
 	// a conflict with the config
@@ -2230,56 +2224,56 @@ func (s *DockerDaemonSuite) TestDaemonDiscoveryBackendConfigReload(c *check.C) {
 	}`
 
 	err = configFile.Truncate(0)
-	c.Assert(err, checker.IsNil)
-	_, err = configFile.Seek(0, os.SEEK_SET)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
+	_, err = configFile.Seek(0, io.SeekStart)
+	assert.NilError(c, err)
 
 	_, err = configFile.Write([]byte(daemonConfig))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	err = s.d.ReloadConfig()
-	c.Assert(err, checker.IsNil, check.Commentf("error reloading daemon config"))
+	assert.Assert(c, err == nil, "error reloading daemon config")
 
 	out, err := s.d.Cmd("info")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(out, checker.Contains, fmt.Sprintf("Cluster Store: consul://consuladdr:consulport/some/path"))
-	c.Assert(out, checker.Contains, fmt.Sprintf("Cluster Advertise: 192.168.56.100:0"))
+	assert.Assert(c, strings.Contains(out, "Cluster Store: consul://consuladdr:consulport/some/path"))
+	assert.Assert(c, strings.Contains(out, "Cluster Advertise: 192.168.56.100:0"))
 }
 
 // Test for #21956
-func (s *DockerDaemonSuite) TestDaemonLogOptions(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonLogOptions(c *testing.T) {
 	s.d.StartWithBusybox(c, "--log-driver=syslog", "--log-opt=syslog-address=udp://127.0.0.1:514")
 
 	out, err := s.d.Cmd("run", "-d", "--log-driver=json-file", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	out, err = s.d.Cmd("inspect", "--format='{{.HostConfig.LogConfig}}'", id)
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "{json-file map[]}")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, "{json-file map[]}"))
 }
 
 // Test case for #20936, #22443
-func (s *DockerDaemonSuite) TestDaemonMaxConcurrency(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonMaxConcurrency(c *testing.T) {
 	s.d.Start(c, "--max-concurrent-uploads=6", "--max-concurrent-downloads=8")
 
 	expectedMaxConcurrentUploads := `level=debug msg="Max Concurrent Uploads: 6"`
 	expectedMaxConcurrentDownloads := `level=debug msg="Max Concurrent Downloads: 8"`
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentUploads)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentDownloads)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads))
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads))
 }
 
 // Test case for #20936, #22443
-func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFile(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFile(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	// daemon config file
 	configFilePath := "test.json"
 	configFile, err := os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer os.Remove(configFilePath)
 
 	daemonConfig := `{ "max-concurrent-downloads" : 8 }`
@@ -2290,17 +2284,16 @@ func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFile(c *check.C) {
 	expectedMaxConcurrentUploads := `level=debug msg="Max Concurrent Uploads: 5"`
 	expectedMaxConcurrentDownloads := `level=debug msg="Max Concurrent Downloads: 8"`
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentUploads)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentDownloads)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads))
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads))
 	configFile, err = os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	daemonConfig = `{ "max-concurrent-uploads" : 7, "max-concurrent-downloads" : 9 }`
 	fmt.Fprintf(configFile, "%s", daemonConfig)
 	configFile.Close()
 
-	c.Assert(s.d.Signal(unix.SIGHUP), checker.IsNil)
+	assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil)
 	// unix.Kill(s.d.cmd.Process.Pid, unix.SIGHUP)
 
 	time.Sleep(3 * time.Second)
@@ -2308,19 +2301,19 @@ func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFile(c *check.C) {
 	expectedMaxConcurrentUploads = `level=debug msg="Reset Max Concurrent Uploads: 7"`
 	expectedMaxConcurrentDownloads = `level=debug msg="Reset Max Concurrent Downloads: 9"`
 	content, err = s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentUploads)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentDownloads)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads))
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads))
 }
 
 // Test case for #20936, #22443
-func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFileReload(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFileReload(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	// daemon config file
 	configFilePath := "test.json"
 	configFile, err := os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer os.Remove(configFilePath)
 
 	daemonConfig := `{ "max-concurrent-uploads" : null }`
@@ -2331,17 +2324,16 @@ func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFileReload(c *chec
 	expectedMaxConcurrentUploads := `level=debug msg="Max Concurrent Uploads: 5"`
 	expectedMaxConcurrentDownloads := `level=debug msg="Max Concurrent Downloads: 3"`
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentUploads)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentDownloads)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads))
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads))
 	configFile, err = os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	daemonConfig = `{ "max-concurrent-uploads" : 1, "max-concurrent-downloads" : null }`
 	fmt.Fprintf(configFile, "%s", daemonConfig)
 	configFile.Close()
 
-	c.Assert(s.d.Signal(unix.SIGHUP), checker.IsNil)
+	assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil)
 	// unix.Kill(s.d.cmd.Process.Pid, unix.SIGHUP)
 
 	time.Sleep(3 * time.Second)
@@ -2349,29 +2341,28 @@ func (s *DockerDaemonSuite) TestDaemonMaxConcurrencyWithConfigFileReload(c *chec
 	expectedMaxConcurrentUploads = `level=debug msg="Reset Max Concurrent Uploads: 1"`
 	expectedMaxConcurrentDownloads = `level=debug msg="Reset Max Concurrent Downloads: 3"`
 	content, err = s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentUploads)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentDownloads)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads))
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads))
 	configFile, err = os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	daemonConfig = `{ "labels":["foo=bar"] }`
 	fmt.Fprintf(configFile, "%s", daemonConfig)
 	configFile.Close()
 
-	c.Assert(s.d.Signal(unix.SIGHUP), checker.IsNil)
+	assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil)
 
 	time.Sleep(3 * time.Second)
 
 	expectedMaxConcurrentUploads = `level=debug msg="Reset Max Concurrent Uploads: 5"`
 	expectedMaxConcurrentDownloads = `level=debug msg="Reset Max Concurrent Downloads: 3"`
 	content, err = s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentUploads)
-	c.Assert(string(content), checker.Contains, expectedMaxConcurrentDownloads)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentUploads))
+	assert.Assert(c, strings.Contains(string(content), expectedMaxConcurrentDownloads))
 }
 
-func (s *DockerDaemonSuite) TestBuildOnDisabledBridgeNetworkDaemon(c *check.C) {
+func (s *DockerDaemonSuite) TestBuildOnDisabledBridgeNetworkDaemon(c *testing.T) {
 	s.d.StartWithBusybox(c, "-b=none", "--iptables=false")
 
 	result := cli.BuildCmd(c, "busyboxs", cli.Daemon(s.d),
@@ -2380,29 +2371,29 @@ func (s *DockerDaemonSuite) TestBuildOnDisabledBridgeNetworkDaemon(c *check.C) {
         RUN cat /etc/hosts`),
 		build.WithoutCache,
 	)
-	comment := check.Commentf("Failed to build image. output %s, exitCode %d, err %v", result.Combined(), result.ExitCode, result.Error)
-	c.Assert(result.Error, check.IsNil, comment)
-	c.Assert(result.ExitCode, check.Equals, 0, comment)
+	comment := fmt.Sprintf("Failed to build image. output %s, exitCode %d, err %v", result.Combined(), result.ExitCode, result.Error)
+	assert.Assert(c, result.Error == nil, comment)
+	assert.Equal(c, result.ExitCode, 0, comment)
 }
 
 // Test case for #21976
-func (s *DockerDaemonSuite) TestDaemonDNSFlagsInHostMode(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerDaemonSuite) TestDaemonDNSFlagsInHostMode(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	s.d.StartWithBusybox(c, "--dns", "1.2.3.4", "--dns-search", "example.com", "--dns-opt", "timeout:3")
 
 	expectedOutput := "nameserver 1.2.3.4"
 	out, _ := s.d.Cmd("run", "--net=host", "busybox", "cat", "/etc/resolv.conf")
-	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
+	assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out)
 	expectedOutput = "search example.com"
-	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
+	assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out)
 	expectedOutput = "options timeout:3"
-	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
+	assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out)
 }
 
-func (s *DockerDaemonSuite) TestRunWithRuntimeFromConfigFile(c *check.C) {
-	conf, err := ioutil.TempFile("", "config-file-")
-	c.Assert(err, check.IsNil)
+func (s *DockerDaemonSuite) TestRunWithRuntimeFromConfigFile(c *testing.T) {
+	conf, err := os.CreateTemp("", "config-file-")
+	assert.NilError(c, err)
 	configName := conf.Name()
 	conf.Close()
 	defer os.Remove(configName)
@@ -2422,26 +2413,25 @@ func (s *DockerDaemonSuite) TestRunWithRuntimeFromConfigFile(c *check.C) {
     }
 }
 `
-	ioutil.WriteFile(configName, []byte(config), 0644)
+	os.WriteFile(configName, []byte(config), 0644)
 	s.d.StartWithBusybox(c, "--config-file", configName)
 
 	// Run with default runtime
 	out, err := s.d.Cmd("run", "--rm", "busybox", "ls")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Run with default runtime explicitly
 	out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Run with oci (same path as default) but keep it around
 	out, err = s.d.Cmd("run", "--name", "oci-runtime-ls", "--runtime=oci", "busybox", "ls")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Run with "vm"
 	out, err = s.d.Cmd("run", "--rm", "--runtime=vm", "busybox", "ls")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "/usr/local/bin/vm-manager: no such file or directory")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "/usr/local/bin/vm-manager: no such file or directory"))
 	// Reset config to only have the default
 	config = `
 {
@@ -2449,25 +2439,23 @@ func (s *DockerDaemonSuite) TestRunWithRuntimeFromConfigFile(c *check.C) {
     }
 }
 `
-	ioutil.WriteFile(configName, []byte(config), 0644)
-	c.Assert(s.d.Signal(unix.SIGHUP), checker.IsNil)
+	os.WriteFile(configName, []byte(config), 0644)
+	assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil)
 	// Give daemon time to reload config
 	<-time.After(1 * time.Second)
 
 	// Run with default runtime
 	out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Run with "oci"
 	out, err = s.d.Cmd("run", "--rm", "--runtime=oci", "busybox", "ls")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "Unknown runtime specified oci")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "Unknown runtime specified oci"))
 	// Start previously created container with oci
 	out, err = s.d.Cmd("start", "oci-runtime-ls")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "Unknown runtime specified oci")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "Unknown runtime specified oci"))
 	// Check that we can't override the default runtime
 	config = `
 {
@@ -2478,15 +2466,14 @@ func (s *DockerDaemonSuite) TestRunWithRuntimeFromConfigFile(c *check.C) {
     }
 }
 `
-	ioutil.WriteFile(configName, []byte(config), 0644)
-	c.Assert(s.d.Signal(unix.SIGHUP), checker.IsNil)
+	os.WriteFile(configName, []byte(config), 0644)
+	assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil)
 	// Give daemon time to reload config
 	<-time.After(1 * time.Second)
 
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, `file configuration validation failed (runtime name 'runc' is reserved)`)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), `file configuration validation failed: runtime name 'runc' is reserved`))
 	// Check that we can select a default runtime
 	config = `
 {
@@ -2504,104 +2491,97 @@ func (s *DockerDaemonSuite) TestRunWithRuntimeFromConfigFile(c *check.C) {
     }
 }
 `
-	ioutil.WriteFile(configName, []byte(config), 0644)
-	c.Assert(s.d.Signal(unix.SIGHUP), checker.IsNil)
+	os.WriteFile(configName, []byte(config), 0644)
+	assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil)
 	// Give daemon time to reload config
 	<-time.After(1 * time.Second)
 
 	out, err = s.d.Cmd("run", "--rm", "busybox", "ls")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "/usr/local/bin/vm-manager: no such file or directory")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "/usr/local/bin/vm-manager: no such file or directory"))
 	// Run with default runtime explicitly
 	out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerDaemonSuite) TestRunWithRuntimeFromCommandLine(c *check.C) {
+func (s *DockerDaemonSuite) TestRunWithRuntimeFromCommandLine(c *testing.T) {
 	s.d.StartWithBusybox(c, "--add-runtime", "oci=runc", "--add-runtime", "vm=/usr/local/bin/vm-manager")
 
 	// Run with default runtime
 	out, err := s.d.Cmd("run", "--rm", "busybox", "ls")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Run with default runtime explicitly
 	out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Run with oci (same path as default) but keep it around
 	out, err = s.d.Cmd("run", "--name", "oci-runtime-ls", "--runtime=oci", "busybox", "ls")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Run with "vm"
 	out, err = s.d.Cmd("run", "--rm", "--runtime=vm", "busybox", "ls")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "/usr/local/bin/vm-manager: no such file or directory")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "/usr/local/bin/vm-manager: no such file or directory"))
 	// Start a daemon without any extra runtimes
 	s.d.Stop(c)
 	s.d.StartWithBusybox(c)
 
 	// Run with default runtime
 	out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Run with "oci"
 	out, err = s.d.Cmd("run", "--rm", "--runtime=oci", "busybox", "ls")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "Unknown runtime specified oci")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "Unknown runtime specified oci"))
 	// Start previously created container with oci
 	out, err = s.d.Cmd("start", "oci-runtime-ls")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "Unknown runtime specified oci")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "Unknown runtime specified oci"))
 	// Check that we can't override the default runtime
 	s.d.Stop(c)
-	c.Assert(s.d.StartWithError("--add-runtime", "runc=my-runc"), checker.NotNil)
+	assert.Assert(c, s.d.StartWithError("--add-runtime", "runc=my-runc") != nil)
 
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, `runtime name 'runc' is reserved`)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), `runtime name 'runc' is reserved`))
 	// Check that we can select a default runtime
 	s.d.Stop(c)
 	s.d.StartWithBusybox(c, "--default-runtime=vm", "--add-runtime", "oci=runc", "--add-runtime", "vm=/usr/local/bin/vm-manager")
 
 	out, err = s.d.Cmd("run", "--rm", "busybox", "ls")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "/usr/local/bin/vm-manager: no such file or directory")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "/usr/local/bin/vm-manager: no such file or directory"))
 	// Run with default runtime explicitly
 	out, err = s.d.Cmd("run", "--rm", "--runtime=runc", "busybox", "ls")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartWithAutoRemoveContainer(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartWithAutoRemoveContainer(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	// top1 will exist after daemon restarts
 	out, err := s.d.Cmd("run", "-d", "--name", "top1", "busybox:latest", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("run top1: %v", out))
+	assert.Assert(c, err == nil, "run top1: %v", out)
 	// top2 will be removed after daemon restarts
 	out, err = s.d.Cmd("run", "-d", "--rm", "--name", "top2", "busybox:latest", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("run top2: %v", out))
+	assert.Assert(c, err == nil, "run top2: %v", out)
 
 	out, err = s.d.Cmd("ps")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "top1", check.Commentf("top1 should be running"))
-	c.Assert(out, checker.Contains, "top2", check.Commentf("top2 should be running"))
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, "top1"), "top1 should be running")
+	assert.Assert(c, strings.Contains(out, "top2"), "top2 should be running")
 	// now restart daemon gracefully
 	s.d.Restart(c)
 
 	out, err = s.d.Cmd("ps", "-a")
-	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
-	c.Assert(out, checker.Contains, "top1", check.Commentf("top1 should exist after daemon restarts"))
-	c.Assert(out, checker.Not(checker.Contains), "top2", check.Commentf("top2 should be removed after daemon restarts"))
+	assert.NilError(c, err, "out: %v", out)
+	assert.Assert(c, strings.Contains(out, "top1"), "top1 should exist after daemon restarts")
+	assert.Assert(c, !strings.Contains(out, "top2"), "top2 should be removed after daemon restarts")
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartSaveContainerExitCode(c *check.C) {
+func (s *DockerDaemonSuite) TestDaemonRestartSaveContainerExitCode(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	containerName := "error-values"
@@ -2614,73 +2594,72 @@ func (s *DockerDaemonSuite) TestDaemonRestartSaveContainerExitCode(c *check.C) {
 	// See the discussion on https://github.com/docker/docker/pull/30227#issuecomment-274161426,
 	// and https://github.com/docker/docker/pull/26061#r78054578 for more information.
 	_, err := s.d.Cmd("run", "--name", containerName, "--init=false", "busybox", "toto")
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// Check that those values were saved on disk
 	out, err := s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", containerName)
 	out = strings.TrimSpace(out)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Equals, "127")
+	assert.NilError(c, err)
+	assert.Equal(c, out, "127")
 
 	errMsg1, err := s.d.Cmd("inspect", "-f", "{{.State.Error}}", containerName)
 	errMsg1 = strings.TrimSpace(errMsg1)
-	c.Assert(err, checker.IsNil)
-	c.Assert(errMsg1, checker.Contains, "executable file not found")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(errMsg1, "executable file not found"))
 	// now restart daemon
 	s.d.Restart(c)
 
 	// Check that those values are still around
 	out, err = s.d.Cmd("inspect", "-f", "{{.State.ExitCode}}", containerName)
 	out = strings.TrimSpace(out)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Equals, "127")
+	assert.NilError(c, err)
+	assert.Equal(c, out, "127")
 
 	out, err = s.d.Cmd("inspect", "-f", "{{.State.Error}}", containerName)
 	out = strings.TrimSpace(out)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Equals, errMsg1)
+	assert.NilError(c, err)
+	assert.Equal(c, out, errMsg1)
 }
 
-func (s *DockerDaemonSuite) TestDaemonWithUserlandProxyPath(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerDaemonSuite) TestDaemonWithUserlandProxyPath(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	dockerProxyPath, err := exec.LookPath("docker-proxy")
-	c.Assert(err, checker.IsNil)
-	tmpDir, err := ioutil.TempDir("", "test-docker-proxy")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
+	tmpDir, err := os.MkdirTemp("", "test-docker-proxy")
+	assert.NilError(c, err)
 
 	newProxyPath := filepath.Join(tmpDir, "docker-proxy")
 	cmd := exec.Command("cp", dockerProxyPath, newProxyPath)
-	c.Assert(cmd.Run(), checker.IsNil)
+	assert.NilError(c, cmd.Run())
 
 	// custom one
 	s.d.StartWithBusybox(c, "--userland-proxy-path", newProxyPath)
 	out, err := s.d.Cmd("run", "-p", "5000:5000", "busybox:latest", "true")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// try with the original one
 	s.d.Restart(c, "--userland-proxy-path", dockerProxyPath)
 	out, err = s.d.Cmd("run", "-p", "5000:5000", "busybox:latest", "true")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// not exist
 	s.d.Restart(c, "--userland-proxy-path", "/does/not/exist")
 	out, err = s.d.Cmd("run", "-p", "5000:5000", "busybox:latest", "true")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "driver failed programming external connectivity on endpoint")
-	c.Assert(out, checker.Contains, "/does/not/exist: no such file or directory")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "driver failed programming external connectivity on endpoint"))
+	assert.Assert(c, strings.Contains(out, "/does/not/exist: no such file or directory"))
 }
 
 // Test case for #22471
-func (s *DockerDaemonSuite) TestDaemonShutdownTimeout(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerDaemonSuite) TestDaemonShutdownTimeout(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	s.d.StartWithBusybox(c, "--shutdown-timeout=3")
 
 	_, err := s.d.Cmd("run", "-d", "busybox", "top")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(s.d.Signal(unix.SIGINT), checker.IsNil)
+	assert.Assert(c, s.d.Signal(unix.SIGINT) == nil)
 
 	select {
 	case <-s.d.Wait:
@@ -2689,18 +2668,18 @@ func (s *DockerDaemonSuite) TestDaemonShutdownTimeout(c *check.C) {
 
 	expectedMessage := `level=debug msg="daemon configured with a 3 seconds minimum shutdown timeout"`
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, expectedMessage)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), expectedMessage))
 }
 
 // Test case for #22471
-func (s *DockerDaemonSuite) TestDaemonShutdownTimeoutWithConfigFile(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerDaemonSuite) TestDaemonShutdownTimeoutWithConfigFile(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 
 	// daemon config file
 	configFilePath := "test.json"
 	configFile, err := os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer os.Remove(configFilePath)
 
 	daemonConfig := `{ "shutdown-timeout" : 8 }`
@@ -2709,12 +2688,12 @@ func (s *DockerDaemonSuite) TestDaemonShutdownTimeoutWithConfigFile(c *check.C)
 	s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath))
 
 	configFile, err = os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	daemonConfig = `{ "shutdown-timeout" : 5 }`
 	fmt.Fprintf(configFile, "%s", daemonConfig)
 	configFile.Close()
 
-	c.Assert(s.d.Signal(unix.SIGHUP), checker.IsNil)
+	assert.Assert(c, s.d.Signal(unix.SIGHUP) == nil)
 
 	select {
 	case <-s.d.Wait:
@@ -2723,44 +2702,44 @@ func (s *DockerDaemonSuite) TestDaemonShutdownTimeoutWithConfigFile(c *check.C)
 
 	expectedMessage := `level=debug msg="Reset Shutdown Timeout: 5"`
 	content, err := s.d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, expectedMessage)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), expectedMessage))
 }
 
 // Test case for 29342
-func (s *DockerDaemonSuite) TestExecWithUserAfterLiveRestore(c *check.C) {
+func (s *DockerDaemonSuite) TestExecWithUserAfterLiveRestore(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	s.d.StartWithBusybox(c, "--live-restore")
 
-	out, err := s.d.Cmd("run", "-d", "--name=top", "busybox", "sh", "-c", "addgroup -S test && adduser -S -G test test -D -s /bin/sh && touch /adduser_end && top")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	out, err := s.d.Cmd("run", "--init", "-d", "--name=top", "busybox", "sh", "-c", "addgroup -S test && adduser -S -G test test -D -s /bin/sh && touch /adduser_end && exec top")
+	assert.NilError(c, err, "Output: %s", out)
 
 	s.d.WaitRun("top")
 
 	// Wait for shell command to be completed
 	_, err = s.d.Cmd("exec", "top", "sh", "-c", `for i in $(seq 1 5); do if [ -e /adduser_end ]; then rm -f /adduser_end && break; else sleep 1 && false; fi; done`)
-	c.Assert(err, check.IsNil, check.Commentf("Timeout waiting for shell command to be completed"))
+	assert.Assert(c, err == nil, "Timeout waiting for shell command to be completed")
 
 	out1, err := s.d.Cmd("exec", "-u", "test", "top", "id")
 	// uid=100(test) gid=101(test) groups=101(test)
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out1))
+	assert.Assert(c, err == nil, "Output: %s", out1)
 
 	// restart daemon.
 	s.d.Restart(c, "--live-restore")
 
 	out2, err := s.d.Cmd("exec", "-u", "test", "top", "id")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out2))
-	c.Assert(out2, check.Equals, out1, check.Commentf("Output: before restart '%s', after restart '%s'", out1, out2))
+	assert.Assert(c, err == nil, "Output: %s", out2)
+	assert.Equal(c, out2, out1, fmt.Sprintf("Output: before restart '%s', after restart '%s'", out1, out2))
 
 	out, err = s.d.Cmd("stop", "top")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 }
 
-func (s *DockerDaemonSuite) TestRemoveContainerAfterLiveRestore(c *check.C) {
-	testRequires(c, DaemonIsLinux, overlayFSSupported, SameHostDaemon)
+func (s *DockerDaemonSuite) TestRemoveContainerAfterLiveRestore(c *testing.T) {
+	testRequires(c, DaemonIsLinux, overlayFSSupported, testEnv.IsLocalDaemon)
 	s.d.StartWithBusybox(c, "--live-restore", "--storage-driver", "overlay")
 	out, err := s.d.Cmd("run", "-d", "--name=top", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 
 	s.d.WaitRun("top")
 
@@ -2768,13 +2747,13 @@ func (s *DockerDaemonSuite) TestRemoveContainerAfterLiveRestore(c *check.C) {
 	s.d.Restart(c, "--live-restore", "--storage-driver", "overlay")
 
 	out, err = s.d.Cmd("stop", "top")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 
 	// test if the rootfs mountpoint still exist
 	mountpoint, err := s.d.InspectField("top", ".GraphDriver.Data.MergedDir")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	f, err := os.Open("/proc/self/mountinfo")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	defer f.Close()
 	sc := bufio.NewScanner(f)
 	for sc.Scan() {
@@ -2785,16 +2764,16 @@ func (s *DockerDaemonSuite) TestRemoveContainerAfterLiveRestore(c *check.C) {
 	}
 
 	out, err = s.d.Cmd("rm", "top")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 }
 
 // #29598
-func (s *DockerDaemonSuite) TestRestartPolicyWithLiveRestore(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerDaemonSuite) TestRestartPolicyWithLiveRestore(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	s.d.StartWithBusybox(c, "--live-restore")
 
 	out, err := s.d.Cmd("run", "-d", "--restart", "always", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 	id := strings.TrimSpace(out)
 
 	type state struct {
@@ -2802,20 +2781,20 @@ func (s *DockerDaemonSuite) TestRestartPolicyWithLiveRestore(c *check.C) {
 		StartedAt time.Time
 	}
 	out, err = s.d.Cmd("inspect", "-f", "{{json .State}}", id)
-	c.Assert(err, checker.IsNil, check.Commentf("output: %s", out))
+	assert.Assert(c, err == nil, "output: %s", out)
 
 	var origState state
 	err = json.Unmarshal([]byte(strings.TrimSpace(out)), &origState)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	s.d.Restart(c, "--live-restore")
 
 	pid, err := s.d.Cmd("inspect", "-f", "{{.State.Pid}}", id)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	pidint, err := strconv.Atoi(strings.TrimSpace(pid))
-	c.Assert(err, check.IsNil)
-	c.Assert(pidint, checker.GreaterThan, 0)
-	c.Assert(unix.Kill(pidint, unix.SIGKILL), check.IsNil)
+	assert.NilError(c, err)
+	assert.Assert(c, pidint > 0)
+	assert.NilError(c, unix.Kill(pidint, unix.SIGKILL))
 
 	ticker := time.NewTicker(50 * time.Millisecond)
 	timeout := time.After(10 * time.Second)
@@ -2828,11 +2807,11 @@ func (s *DockerDaemonSuite) TestRestartPolicyWithLiveRestore(c *check.C) {
 		}
 
 		out, err := s.d.Cmd("inspect", "-f", "{{json .State}}", id)
-		c.Assert(err, checker.IsNil, check.Commentf("output: %s", out))
+		assert.Assert(c, err == nil, "output: %s", out)
 
 		var newState state
 		err = json.Unmarshal([]byte(strings.TrimSpace(out)), &newState)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 
 		if !newState.Running {
 			continue
@@ -2843,10 +2822,10 @@ func (s *DockerDaemonSuite) TestRestartPolicyWithLiveRestore(c *check.C) {
 	}
 
 	out, err = s.d.Cmd("stop", id)
-	c.Assert(err, check.IsNil, check.Commentf("output: %s", out))
+	assert.NilError(c, err, "Output: %s", out)
 }
 
-func (s *DockerDaemonSuite) TestShmSize(c *check.C) {
+func (s *DockerDaemonSuite) TestShmSize(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	size := 67108864 * 2
@@ -2856,66 +2835,66 @@ func (s *DockerDaemonSuite) TestShmSize(c *check.C) {
 
 	name := "shm1"
 	out, err := s.d.Cmd("run", "--name", name, "busybox", "mount")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
-	c.Assert(pattern.MatchString(out), checker.True)
+	assert.NilError(c, err, "Output: %s", out)
+	assert.Assert(c, pattern.MatchString(out))
 	out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
-	c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
+	assert.NilError(c, err, "Output: %s", out)
+	assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%v", size))
 }
 
-func (s *DockerDaemonSuite) TestShmSizeReload(c *check.C) {
+func (s *DockerDaemonSuite) TestShmSizeReload(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
-	configPath, err := ioutil.TempDir("", "test-daemon-shm-size-reload-config")
-	c.Assert(err, checker.IsNil, check.Commentf("could not create temp file for config reload"))
+	configPath, err := os.MkdirTemp("", "test-daemon-shm-size-reload-config")
+	assert.Assert(c, err == nil, "could not create temp file for config reload")
 	defer os.RemoveAll(configPath) // clean up
 	configFile := filepath.Join(configPath, "config.json")
 
 	size := 67108864 * 2
 	configData := []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024))
-	c.Assert(ioutil.WriteFile(configFile, configData, 0666), checker.IsNil, check.Commentf("could not write temp file for config reload"))
+	assert.Assert(c, os.WriteFile(configFile, configData, 0666) == nil, "could not write temp file for config reload")
 	pattern := regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
 
 	s.d.StartWithBusybox(c, "--config-file", configFile)
 
 	name := "shm1"
 	out, err := s.d.Cmd("run", "--name", name, "busybox", "mount")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
-	c.Assert(pattern.MatchString(out), checker.True)
+	assert.NilError(c, err, "Output: %s", out)
+	assert.Assert(c, pattern.MatchString(out))
 	out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
-	c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
+	assert.NilError(c, err, "Output: %s", out)
+	assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%v", size))
 
 	size = 67108864 * 3
 	configData = []byte(fmt.Sprintf(`{"default-shm-size": "%dM"}`, size/1024/1024))
-	c.Assert(ioutil.WriteFile(configFile, configData, 0666), checker.IsNil, check.Commentf("could not write temp file for config reload"))
+	assert.Assert(c, os.WriteFile(configFile, configData, 0666) == nil, "could not write temp file for config reload")
 	pattern = regexp.MustCompile(fmt.Sprintf("shm on /dev/shm type tmpfs(.*)size=%dk", size/1024))
 
 	err = s.d.ReloadConfig()
-	c.Assert(err, checker.IsNil, check.Commentf("error reloading daemon config"))
+	assert.Assert(c, err == nil, "error reloading daemon config")
 
 	name = "shm2"
 	out, err = s.d.Cmd("run", "--name", name, "busybox", "mount")
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
-	c.Assert(pattern.MatchString(out), checker.True)
+	assert.NilError(c, err, "Output: %s", out)
+	assert.Assert(c, pattern.MatchString(out))
 	out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.ShmSize}}", name)
-	c.Assert(err, check.IsNil, check.Commentf("Output: %s", out))
-	c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
+	assert.NilError(c, err, "Output: %s", out)
+	assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%v", size))
 }
 
-func testDaemonStartIpcMode(c *check.C, from, mode string, valid bool) {
+func testDaemonStartIpcMode(c *testing.T, from, mode string, valid bool) {
 	d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
 	c.Logf("Checking IpcMode %s set from %s\n", mode, from)
 	var serr error
 	switch from {
 	case "config":
-		f, err := ioutil.TempFile("", "test-daemon-ipc-config")
-		c.Assert(err, checker.IsNil)
+		f, err := os.CreateTemp("", "test-daemon-ipc-config")
+		assert.NilError(c, err)
 		defer os.Remove(f.Name())
 		config := `{"default-ipc-mode": "` + mode + `"}`
 		_, err = f.WriteString(config)
-		c.Assert(f.Close(), checker.IsNil)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, f.Close())
+		assert.NilError(c, err)
 
 		serr = d.StartWithError("--config-file", f.Name())
 	case "cli":
@@ -2928,9 +2907,9 @@ func testDaemonStartIpcMode(c *check.C, from, mode string, valid bool) {
 	}
 
 	if valid {
-		c.Assert(serr, check.IsNil)
+		assert.NilError(c, serr)
 	} else {
-		c.Assert(serr, check.NotNil)
+		assert.ErrorContains(c, serr, "")
 		icmd.RunCommand("grep", "-E", "IPC .* is (invalid|not supported)", d.LogFileName()).Assert(c, icmd.Success)
 	}
 }
@@ -2938,8 +2917,8 @@ func testDaemonStartIpcMode(c *check.C, from, mode string, valid bool) {
 // TestDaemonStartWithIpcModes checks that daemon starts fine given correct
 // arguments for default IPC mode, and bails out with incorrect ones.
 // Both CLI option (--default-ipc-mode) and config parameter are tested.
-func (s *DockerDaemonSuite) TestDaemonStartWithIpcModes(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerDaemonSuite) TestDaemonStartWithIpcModes(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 
 	ipcModes := []struct {
 		mode  string
@@ -2960,55 +2939,13 @@ func (s *DockerDaemonSuite) TestDaemonStartWithIpcModes(c *check.C) {
 	}
 }
 
-// TestDaemonRestartIpcMode makes sure a container keeps its ipc mode
-// (derived from daemon default) even after the daemon is restarted
-// with a different default ipc mode.
-func (s *DockerDaemonSuite) TestDaemonRestartIpcMode(c *check.C) {
-	f, err := ioutil.TempFile("", "test-daemon-ipc-config-restart")
-	c.Assert(err, checker.IsNil)
-	file := f.Name()
-	defer os.Remove(file)
-	c.Assert(f.Close(), checker.IsNil)
-
-	config := []byte(`{"default-ipc-mode": "private"}`)
-	c.Assert(ioutil.WriteFile(file, config, 0644), checker.IsNil)
-	s.d.StartWithBusybox(c, "--config-file", file)
-
-	// check the container is created with private ipc mode as per daemon default
-	name := "ipc1"
-	_, err = s.d.Cmd("run", "-d", "--name", name, "--restart=always", "busybox", "top")
-	c.Assert(err, checker.IsNil)
-	m, err := s.d.InspectField(name, ".HostConfig.IpcMode")
-	c.Assert(err, check.IsNil)
-	c.Assert(m, checker.Equals, "private")
-
-	// restart the daemon with shareable default ipc mode
-	config = []byte(`{"default-ipc-mode": "shareable"}`)
-	c.Assert(ioutil.WriteFile(file, config, 0644), checker.IsNil)
-	s.d.Restart(c, "--config-file", file)
-
-	// check the container is still having private ipc mode
-	m, err = s.d.InspectField(name, ".HostConfig.IpcMode")
-	c.Assert(err, check.IsNil)
-	c.Assert(m, checker.Equals, "private")
-
-	// check a new container is created with shareable ipc mode as per new daemon default
-	name = "ipc2"
-	_, err = s.d.Cmd("run", "-d", "--name", name, "busybox", "top")
-	c.Assert(err, checker.IsNil)
-	m, err = s.d.InspectField(name, ".HostConfig.IpcMode")
-	c.Assert(err, check.IsNil)
-	c.Assert(m, checker.Equals, "shareable")
-}
-
 // TestFailedPluginRemove makes sure that a failed plugin remove does not block
 // the daemon from starting
-func (s *DockerDaemonSuite) TestFailedPluginRemove(c *check.C) {
-	testRequires(c, DaemonIsLinux, IsAmd64, SameHostDaemon)
+func (s *DockerDaemonSuite) TestFailedPluginRemove(c *testing.T) {
+	testRequires(c, DaemonIsLinux, IsAmd64, testEnv.IsLocalDaemon)
 	d := daemon.New(c, dockerBinary, dockerdBinary)
 	d.Start(c)
-	cli, err := client.NewClient(d.Sock(), api.DefaultVersion, nil, nil)
-	c.Assert(err, checker.IsNil)
+	cli := d.NewClientT(c)
 
 	ctx, cancel := context.WithTimeout(context.Background(), 300*time.Second)
 	defer cancel()
@@ -3019,26 +2956,26 @@ func (s *DockerDaemonSuite) TestFailedPluginRemove(c *check.C) {
 		AcceptAllPermissions: true,
 		RemoteRef:            "cpuguy83/docker-logdriver-test",
 	})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer out.Close()
-	io.Copy(ioutil.Discard, out)
+	io.Copy(io.Discard, out)
 
 	ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
 	defer cancel()
 	p, _, err := cli.PluginInspectWithRaw(ctx, name)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// simulate a bad/partial removal by removing the plugin config.
 	configPath := filepath.Join(d.Root, "plugins", p.ID, "config.json")
-	c.Assert(os.Remove(configPath), checker.IsNil)
+	assert.NilError(c, os.Remove(configPath))
 
 	d.Restart(c)
 	ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
 	defer cancel()
 	_, err = cli.Ping(ctx)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	_, _, err = cli.PluginInspectWithRaw(ctx, name)
 	// plugin should be gone since the config.json is gone
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 }
diff --git a/integration-cli/docker_cli_events_test.go b/integration-cli/docker_cli_events_test.go
index 8db120d03ded1..642960b77027a 100644
--- a/integration-cli/docker_cli_events_test.go
+++ b/integration-cli/docker_cli_events_test.go
@@ -6,24 +6,25 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"os/exec"
+	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	eventtypes "github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/client"
 	eventstestutils "github.com/docker/docker/daemon/events/testutils"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/icmd"
 )
 
-func (s *DockerSuite) TestEventsTimestampFormats(c *check.C) {
+func (s *DockerSuite) TestEventsTimestampFormats(c *testing.T) {
 	name := "events-time-format-test"
 
 	// Start stopwatch, generate an event
@@ -46,19 +47,13 @@ func (s *DockerSuite) TestEventsTimestampFormats(c *check.C) {
 		events = events[:len(events)-1]
 
 		nEvents := len(events)
-		c.Assert(nEvents, checker.GreaterOrEqualThan, 5) //Missing expected event
+		assert.Assert(c, nEvents >= 5)
 		containerEvents := eventActionsByIDAndType(c, events, name, "container")
-		c.Assert(containerEvents, checker.HasLen, 5, check.Commentf("events: %v", events))
-
-		c.Assert(containerEvents[0], checker.Equals, "create", check.Commentf("%s", out))
-		c.Assert(containerEvents[1], checker.Equals, "attach", check.Commentf("%s", out))
-		c.Assert(containerEvents[2], checker.Equals, "start", check.Commentf("%s", out))
-		c.Assert(containerEvents[3], checker.Equals, "die", check.Commentf("%s", out))
-		c.Assert(containerEvents[4], checker.Equals, "destroy", check.Commentf("%s", out))
+		assert.Assert(c, is.DeepEqual(containerEvents, []string{"create", "attach", "start", "die", "destroy"}), out)
 	}
 }
 
-func (s *DockerSuite) TestEventsUntag(c *check.C) {
+func (s *DockerSuite) TestEventsUntag(c *testing.T) {
 	image := "busybox"
 	dockerCmd(c, "tag", image, "utest:tag1")
 	dockerCmd(c, "tag", image, "utest:tag2")
@@ -77,30 +72,25 @@ func (s *DockerSuite) TestEventsUntag(c *check.C) {
 	// get the two elements before the last, which are the untags we're
 	// looking for.
 	for _, v := range events[nEvents-3 : nEvents-1] {
-		c.Assert(v, checker.Contains, "untag", check.Commentf("event should be untag"))
+		assert.Check(c, strings.Contains(v, "untag"), "event should be untag")
 	}
 }
 
-func (s *DockerSuite) TestEventsContainerEvents(c *check.C) {
+func (s *DockerSuite) TestEventsContainerEvents(c *testing.T) {
 	dockerCmd(c, "run", "--rm", "--name", "container-events-test", "busybox", "true")
 
 	out, _ := dockerCmd(c, "events", "--until", daemonUnixTime(c))
 	events := strings.Split(out, "\n")
 	events = events[:len(events)-1]
 
-	nEvents := len(events)
-	c.Assert(nEvents, checker.GreaterOrEqualThan, 5) //Missing expected event
 	containerEvents := eventActionsByIDAndType(c, events, "container-events-test", "container")
-	c.Assert(containerEvents, checker.HasLen, 5, check.Commentf("events: %v", events))
-
-	c.Assert(containerEvents[0], checker.Equals, "create", check.Commentf("%s", out))
-	c.Assert(containerEvents[1], checker.Equals, "attach", check.Commentf("%s", out))
-	c.Assert(containerEvents[2], checker.Equals, "start", check.Commentf("%s", out))
-	c.Assert(containerEvents[3], checker.Equals, "die", check.Commentf("%s", out))
-	c.Assert(containerEvents[4], checker.Equals, "destroy", check.Commentf("%s", out))
+	if len(containerEvents) > 5 {
+		containerEvents = containerEvents[:5]
+	}
+	assert.Assert(c, is.DeepEqual(containerEvents, []string{"create", "attach", "start", "die", "destroy"}), out)
 }
 
-func (s *DockerSuite) TestEventsContainerEventsAttrSort(c *check.C) {
+func (s *DockerSuite) TestEventsContainerEventsAttrSort(c *testing.T) {
 	since := daemonUnixTime(c)
 	dockerCmd(c, "run", "--rm", "--name", "container-events-test", "busybox", "true")
 
@@ -108,22 +98,23 @@ func (s *DockerSuite) TestEventsContainerEventsAttrSort(c *check.C) {
 	events := strings.Split(out, "\n")
 
 	nEvents := len(events)
-	c.Assert(nEvents, checker.GreaterOrEqualThan, 3) //Missing expected event
+	assert.Assert(c, nEvents >= 3)
 	matchedEvents := 0
 	for _, event := range events {
 		matches := eventstestutils.ScanMap(event)
 		if matches["eventType"] == "container" && matches["action"] == "create" {
 			matchedEvents++
-			c.Assert(out, checker.Contains, "(image=busybox, name=container-events-test)", check.Commentf("Event attributes not sorted"))
+			assert.Check(c, strings.Contains(out, "(image=busybox, name=container-events-test)"), "Event attributes not sorted")
+
 		} else if matches["eventType"] == "container" && matches["action"] == "start" {
 			matchedEvents++
-			c.Assert(out, checker.Contains, "(image=busybox, name=container-events-test)", check.Commentf("Event attributes not sorted"))
+			assert.Check(c, strings.Contains(out, "(image=busybox, name=container-events-test)"), "Event attributes not sorted")
 		}
 	}
-	c.Assert(matchedEvents, checker.Equals, 2, check.Commentf("missing events for container container-events-test:\n%s", out))
+	assert.Equal(c, matchedEvents, 2, "missing events for container container-events-test:\n%s", out)
 }
 
-func (s *DockerSuite) TestEventsContainerEventsSinceUnixEpoch(c *check.C) {
+func (s *DockerSuite) TestEventsContainerEventsSinceUnixEpoch(c *testing.T) {
 	dockerCmd(c, "run", "--rm", "--name", "since-epoch-test", "busybox", "true")
 	timeBeginning := time.Unix(0, 0).Format(time.RFC3339Nano)
 	timeBeginning = strings.Replace(timeBeginning, "Z", ".000000000Z", -1)
@@ -132,18 +123,12 @@ func (s *DockerSuite) TestEventsContainerEventsSinceUnixEpoch(c *check.C) {
 	events = events[:len(events)-1]
 
 	nEvents := len(events)
-	c.Assert(nEvents, checker.GreaterOrEqualThan, 5) //Missing expected event
+	assert.Assert(c, nEvents >= 5)
 	containerEvents := eventActionsByIDAndType(c, events, "since-epoch-test", "container")
-	c.Assert(containerEvents, checker.HasLen, 5, check.Commentf("events: %v", events))
-
-	c.Assert(containerEvents[0], checker.Equals, "create", check.Commentf("%s", out))
-	c.Assert(containerEvents[1], checker.Equals, "attach", check.Commentf("%s", out))
-	c.Assert(containerEvents[2], checker.Equals, "start", check.Commentf("%s", out))
-	c.Assert(containerEvents[3], checker.Equals, "die", check.Commentf("%s", out))
-	c.Assert(containerEvents[4], checker.Equals, "destroy", check.Commentf("%s", out))
+	assert.Assert(c, is.DeepEqual(containerEvents, []string{"create", "attach", "start", "die", "destroy"}), out)
 }
 
-func (s *DockerSuite) TestEventsImageTag(c *check.C) {
+func (s *DockerSuite) TestEventsImageTag(c *testing.T) {
 	time.Sleep(1 * time.Second) // because API has seconds granularity
 	since := daemonUnixTime(c)
 	image := "testimageevents:tag"
@@ -153,15 +138,15 @@ func (s *DockerSuite) TestEventsImageTag(c *check.C) {
 		"--since", since, "--until", daemonUnixTime(c))
 
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(events, checker.HasLen, 1, check.Commentf("was expecting 1 event. out=%s", out))
+	assert.Equal(c, len(events), 1, "was expecting 1 event. out=%s", out)
 	event := strings.TrimSpace(events[0])
 
 	matches := eventstestutils.ScanMap(event)
-	c.Assert(matchEventID(matches, image), checker.True, check.Commentf("matches: %v\nout:\n%s", matches, out))
-	c.Assert(matches["action"], checker.Equals, "tag")
+	assert.Assert(c, matchEventID(matches, image), "matches: %v\nout:\n%s", matches, out)
+	assert.Equal(c, matches["action"], "tag")
 }
 
-func (s *DockerSuite) TestEventsImagePull(c *check.C) {
+func (s *DockerSuite) TestEventsImagePull(c *testing.T) {
 	// TODO Windows: Enable this test once pull and reliable image names are available
 	testRequires(c, DaemonIsLinux)
 	since := daemonUnixTime(c)
@@ -175,12 +160,11 @@ func (s *DockerSuite) TestEventsImagePull(c *check.C) {
 	events := strings.Split(strings.TrimSpace(out), "\n")
 	event := strings.TrimSpace(events[len(events)-1])
 	matches := eventstestutils.ScanMap(event)
-	c.Assert(matches["id"], checker.Equals, "hello-world:latest")
-	c.Assert(matches["action"], checker.Equals, "pull")
-
+	assert.Equal(c, matches["id"], "hello-world:latest")
+	assert.Equal(c, matches["action"], "pull")
 }
 
-func (s *DockerSuite) TestEventsImageImport(c *check.C) {
+func (s *DockerSuite) TestEventsImageImport(c *testing.T) {
 	// TODO Windows CI. This should be portable once export/import are
 	// more reliable (@swernli)
 	testRequires(c, DaemonIsLinux)
@@ -193,18 +177,18 @@ func (s *DockerSuite) TestEventsImageImport(c *check.C) {
 		exec.Command(dockerBinary, "export", cleanedContainerID),
 		exec.Command(dockerBinary, "import", "-"),
 	)
-	c.Assert(err, checker.IsNil, check.Commentf("import failed with output: %q", out))
+	assert.NilError(c, err, "import failed with output: %q", out)
 	imageRef := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=import")
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(events, checker.HasLen, 1)
+	assert.Equal(c, len(events), 1)
 	matches := eventstestutils.ScanMap(events[0])
-	c.Assert(matches["id"], checker.Equals, imageRef, check.Commentf("matches: %v\nout:\n%s\n", matches, out))
-	c.Assert(matches["action"], checker.Equals, "import", check.Commentf("matches: %v\nout:\n%s\n", matches, out))
+	assert.Equal(c, matches["id"], imageRef, "matches: %v\nout:\n%s\n", matches, out)
+	assert.Equal(c, matches["action"], "import", "matches: %v\nout:\n%s\n", matches, out)
 }
 
-func (s *DockerSuite) TestEventsImageLoad(c *check.C) {
+func (s *DockerSuite) TestEventsImageLoad(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	myImageName := "footest:v1"
 	dockerCmd(c, "tag", "busybox", myImageName)
@@ -212,13 +196,13 @@ func (s *DockerSuite) TestEventsImageLoad(c *check.C) {
 
 	out, _ := dockerCmd(c, "images", "-q", "--no-trunc", myImageName)
 	longImageID := strings.TrimSpace(out)
-	c.Assert(longImageID, checker.Not(check.Equals), "", check.Commentf("Id should not be empty"))
+	assert.Assert(c, longImageID != "", "Id should not be empty")
 
 	dockerCmd(c, "save", "-o", "saveimg.tar", myImageName)
 	dockerCmd(c, "rmi", myImageName)
 	out, _ = dockerCmd(c, "images", "-q", myImageName)
 	noImageID := strings.TrimSpace(out)
-	c.Assert(noImageID, checker.Equals, "", check.Commentf("Should not have any image"))
+	assert.Equal(c, noImageID, "", "Should not have any image")
 	dockerCmd(c, "load", "-i", "saveimg.tar")
 
 	result := icmd.RunCommand("rm", "-rf", "saveimg.tar")
@@ -226,24 +210,24 @@ func (s *DockerSuite) TestEventsImageLoad(c *check.C) {
 
 	out, _ = dockerCmd(c, "images", "-q", "--no-trunc", myImageName)
 	imageID := strings.TrimSpace(out)
-	c.Assert(imageID, checker.Equals, longImageID, check.Commentf("Should have same image id as before"))
+	assert.Equal(c, imageID, longImageID, "Should have same image id as before")
 
 	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=load")
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(events, checker.HasLen, 1)
+	assert.Equal(c, len(events), 1)
 	matches := eventstestutils.ScanMap(events[0])
-	c.Assert(matches["id"], checker.Equals, imageID, check.Commentf("matches: %v\nout:\n%s\n", matches, out))
-	c.Assert(matches["action"], checker.Equals, "load", check.Commentf("matches: %v\nout:\n%s\n", matches, out))
+	assert.Equal(c, matches["id"], imageID, "matches: %v\nout:\n%s\n", matches, out)
+	assert.Equal(c, matches["action"], "load", "matches: %v\nout:\n%s\n", matches, out)
 
 	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", "event=save")
 	events = strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(events, checker.HasLen, 1)
+	assert.Equal(c, len(events), 1)
 	matches = eventstestutils.ScanMap(events[0])
-	c.Assert(matches["id"], checker.Equals, imageID, check.Commentf("matches: %v\nout:\n%s\n", matches, out))
-	c.Assert(matches["action"], checker.Equals, "save", check.Commentf("matches: %v\nout:\n%s\n", matches, out))
+	assert.Equal(c, matches["id"], imageID, "matches: %v\nout:\n%s\n", matches, out)
+	assert.Equal(c, matches["action"], "save", "matches: %v\nout:\n%s\n", matches, out)
 }
 
-func (s *DockerSuite) TestEventsPluginOps(c *check.C) {
+func (s *DockerSuite) TestEventsPluginOps(c *testing.T) {
 	testRequires(c, DaemonIsLinux, IsAmd64, Network)
 
 	since := daemonUnixTime(c)
@@ -256,19 +240,13 @@ func (s *DockerSuite) TestEventsPluginOps(c *check.C) {
 	events := strings.Split(out, "\n")
 	events = events[:len(events)-1]
 
-	nEvents := len(events)
-	c.Assert(nEvents, checker.GreaterOrEqualThan, 4)
+	assert.Assert(c, len(events) >= 4)
 
 	pluginEvents := eventActionsByIDAndType(c, events, pNameWithTag, "plugin")
-	c.Assert(pluginEvents, checker.HasLen, 4, check.Commentf("events: %v", events))
-
-	c.Assert(pluginEvents[0], checker.Equals, "pull", check.Commentf("%s", out))
-	c.Assert(pluginEvents[1], checker.Equals, "enable", check.Commentf("%s", out))
-	c.Assert(pluginEvents[2], checker.Equals, "disable", check.Commentf("%s", out))
-	c.Assert(pluginEvents[3], checker.Equals, "remove", check.Commentf("%s", out))
+	assert.Assert(c, is.DeepEqual(pluginEvents, []string{"pull", "enable", "disable", "remove"}), out)
 }
 
-func (s *DockerSuite) TestEventsFilters(c *check.C) {
+func (s *DockerSuite) TestEventsFilters(c *testing.T) {
 	since := daemonUnixTime(c)
 	dockerCmd(c, "run", "--rm", "busybox", "true")
 	dockerCmd(c, "run", "--rm", "busybox", "true")
@@ -280,11 +258,10 @@ func (s *DockerSuite) TestEventsFilters(c *check.C) {
 
 	// make sure we at least got 2 start events
 	count := strings.Count(out, "start")
-	c.Assert(strings.Count(out, "start"), checker.GreaterOrEqualThan, 2, check.Commentf("should have had 2 start events but had %d, out: %s", count, out))
-
+	assert.Assert(c, count >= 2, "should have had 2 start events but had %d, out: %s", count, out)
 }
 
-func (s *DockerSuite) TestEventsFilterImageName(c *check.C) {
+func (s *DockerSuite) TestEventsFilterImageName(c *testing.T) {
 	since := daemonUnixTime(c)
 
 	out, _ := dockerCmd(c, "run", "--name", "container_1", "-d", "busybox:latest", "true")
@@ -297,7 +274,7 @@ func (s *DockerSuite) TestEventsFilterImageName(c *check.C) {
 	out, _ = dockerCmd(c, "events", "--since", since, "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("image=%s", name))
 	events := strings.Split(out, "\n")
 	events = events[:len(events)-1]
-	c.Assert(events, checker.Not(checker.HasLen), 0) //Expected events but found none for the image busybox:latest
+	assert.Assert(c, len(events) != 0, "Expected events but found none for the image busybox:latest")
 	count1 := 0
 	count2 := 0
 
@@ -308,38 +285,46 @@ func (s *DockerSuite) TestEventsFilterImageName(c *check.C) {
 			count2++
 		}
 	}
-	c.Assert(count1, checker.Not(checker.Equals), 0, check.Commentf("Expected event from container but got %d from %s", count1, container1))
-	c.Assert(count2, checker.Not(checker.Equals), 0, check.Commentf("Expected event from container but got %d from %s", count2, container2))
-
+	assert.Assert(c, count1 != 0, "Expected event from container but got %d from %s", count1, container1)
+	assert.Assert(c, count2 != 0, "Expected event from container but got %d from %s", count2, container2)
 }
 
-func (s *DockerSuite) TestEventsFilterLabels(c *check.C) {
-	since := daemonUnixTime(c)
+func (s *DockerSuite) TestEventsFilterLabels(c *testing.T) {
+	since := strconv.FormatUint(uint64(daemonTime(c).Unix()), 10)
 	label := "io.docker.testing=foo"
 
-	out, _ := dockerCmd(c, "run", "-d", "-l", label, "busybox:latest", "true")
+	out, exit := dockerCmd(c, "create", "-l", label, "busybox")
+	assert.Equal(c, exit, 0)
 	container1 := strings.TrimSpace(out)
 
-	out, _ = dockerCmd(c, "run", "-d", "busybox", "true")
+	out, exit = dockerCmd(c, "create", "busybox")
+	assert.Equal(c, exit, 0)
 	container2 := strings.TrimSpace(out)
 
+	// fetch events with `--until`, so that the client detaches after a second
+	// instead of staying attached, waiting for more events to arrive.
 	out, _ = dockerCmd(
 		c,
 		"events",
 		"--since", since,
-		"--until", daemonUnixTime(c),
-		"--filter", fmt.Sprintf("label=%s", label))
+		"--until", strconv.FormatUint(uint64(daemonTime(c).Add(time.Second).Unix()), 10),
+		"--filter", "label="+label,
+	)
 
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(events), checker.Equals, 3)
+	assert.Assert(c, len(events) > 0)
 
+	var found bool
 	for _, e := range events {
-		c.Assert(e, checker.Contains, container1)
-		c.Assert(e, checker.Not(checker.Contains), container2)
+		if strings.Contains(e, container1) {
+			found = true
+		}
+		assert.Assert(c, !strings.Contains(e, container2))
 	}
+	assert.Assert(c, found)
 }
 
-func (s *DockerSuite) TestEventsFilterImageLabels(c *check.C) {
+func (s *DockerSuite) TestEventsFilterImageLabels(c *testing.T) {
 	since := daemonUnixTime(c)
 	name := "labelfiltertest"
 	label := "io.docker.testing=image"
@@ -363,13 +348,13 @@ func (s *DockerSuite) TestEventsFilterImageLabels(c *check.C) {
 	events := strings.Split(strings.TrimSpace(out), "\n")
 
 	// 2 events from the "docker tag" command, another one is from "docker build"
-	c.Assert(events, checker.HasLen, 3, check.Commentf("Events == %s", events))
+	assert.Equal(c, len(events), 3, "Events == %s", events)
 	for _, e := range events {
-		c.Assert(e, checker.Contains, "labelfiltertest")
+		assert.Check(c, strings.Contains(e, "labelfiltertest"))
 	}
 }
 
-func (s *DockerSuite) TestEventsFilterContainer(c *check.C) {
+func (s *DockerSuite) TestEventsFilterContainer(c *testing.T) {
 	since := daemonUnixTime(c)
 	nameID := make(map[string]string)
 
@@ -398,16 +383,16 @@ func (s *DockerSuite) TestEventsFilterContainer(c *check.C) {
 		// filter by names
 		out, _ := dockerCmd(c, "events", "--since", since, "--until", until, "--filter", "container="+name)
 		events := strings.Split(strings.TrimSuffix(out, "\n"), "\n")
-		c.Assert(checkEvents(ID, events), checker.IsNil)
+		assert.NilError(c, checkEvents(ID, events))
 
 		// filter by ID's
 		out, _ = dockerCmd(c, "events", "--since", since, "--until", until, "--filter", "container="+ID)
 		events = strings.Split(strings.TrimSuffix(out, "\n"), "\n")
-		c.Assert(checkEvents(ID, events), checker.IsNil)
+		assert.NilError(c, checkEvents(ID, events))
 	}
 }
 
-func (s *DockerSuite) TestEventsCommit(c *check.C) {
+func (s *DockerSuite) TestEventsCommit(c *testing.T) {
 	// Problematic on Windows as cannot commit a running container
 	testRequires(c, DaemonIsLinux)
 
@@ -421,10 +406,10 @@ func (s *DockerSuite) TestEventsCommit(c *check.C) {
 
 	until := daemonUnixTime(c)
 	out = cli.DockerCmd(c, "events", "-f", "container="+cID, "--until="+until).Combined()
-	c.Assert(out, checker.Contains, "commit", check.Commentf("Missing 'commit' log event"))
+	assert.Assert(c, strings.Contains(out, "commit"), "Missing 'commit' log event")
 }
 
-func (s *DockerSuite) TestEventsCopy(c *check.C) {
+func (s *DockerSuite) TestEventsCopy(c *testing.T) {
 	// Build a test image.
 	buildImageSuccessfully(c, "cpimg", build.WithDockerfile(`
 		  FROM busybox
@@ -432,11 +417,11 @@ func (s *DockerSuite) TestEventsCopy(c *check.C) {
 	id := getIDByName(c, "cpimg")
 
 	// Create an empty test file.
-	tempFile, err := ioutil.TempFile("", "test-events-copy-")
-	c.Assert(err, checker.IsNil)
+	tempFile, err := os.CreateTemp("", "test-events-copy-")
+	assert.NilError(c, err)
 	defer os.Remove(tempFile.Name())
 
-	c.Assert(tempFile.Close(), checker.IsNil)
+	assert.NilError(c, tempFile.Close())
 
 	dockerCmd(c, "create", "--name=cptest", id)
 
@@ -444,22 +429,22 @@ func (s *DockerSuite) TestEventsCopy(c *check.C) {
 
 	until := daemonUnixTime(c)
 	out, _ := dockerCmd(c, "events", "--since=0", "-f", "container=cptest", "--until="+until)
-	c.Assert(out, checker.Contains, "archive-path", check.Commentf("Missing 'archive-path' log event\n"))
+	assert.Assert(c, strings.Contains(out, "archive-path"), "Missing 'archive-path' log event")
 
 	dockerCmd(c, "cp", tempFile.Name(), "cptest:/filecopy")
 
 	until = daemonUnixTime(c)
 	out, _ = dockerCmd(c, "events", "-f", "container=cptest", "--until="+until)
-	c.Assert(out, checker.Contains, "extract-to-dir", check.Commentf("Missing 'extract-to-dir' log event"))
+	assert.Assert(c, strings.Contains(out, "extract-to-dir"), "Missing 'extract-to-dir' log event")
 }
 
-func (s *DockerSuite) TestEventsResize(c *check.C) {
-	out := runSleepingContainer(c, "-d")
+func (s *DockerSuite) TestEventsResize(c *testing.T) {
+	out := runSleepingContainer(c, "-d", "-t")
 	cID := strings.TrimSpace(out)
-	c.Assert(waitRun(cID), checker.IsNil)
+	assert.NilError(c, waitRun(cID))
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	options := types.ResizeOptions{
@@ -467,16 +452,16 @@ func (s *DockerSuite) TestEventsResize(c *check.C) {
 		Width:  24,
 	}
 	err = cli.ContainerResize(context.Background(), cID, options)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "stop", cID)
 
 	until := daemonUnixTime(c)
 	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until="+until)
-	c.Assert(out, checker.Contains, "resize", check.Commentf("Missing 'resize' log event"))
+	assert.Assert(c, strings.Contains(out, "resize"), "Missing 'resize' log event")
 }
 
-func (s *DockerSuite) TestEventsAttach(c *check.C) {
+func (s *DockerSuite) TestEventsAttach(c *testing.T) {
 	// TODO Windows CI: Figure out why this test fails intermittently (TP5).
 	testRequires(c, DaemonIsLinux)
 
@@ -486,12 +471,12 @@ func (s *DockerSuite) TestEventsAttach(c *check.C) {
 
 	cmd := exec.Command(dockerBinary, "attach", cID)
 	stdin, err := cmd.StdinPipe()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer stdin.Close()
 	stdout, err := cmd.StdoutPipe()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer stdout.Close()
-	c.Assert(cmd.Start(), checker.IsNil)
+	assert.NilError(c, cmd.Start())
 	defer func() {
 		cmd.Process.Kill()
 		cmd.Wait()
@@ -499,22 +484,22 @@ func (s *DockerSuite) TestEventsAttach(c *check.C) {
 
 	// Make sure we're done attaching by writing/reading some stuff
 	_, err = stdin.Write([]byte("hello\n"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	out, err = bufio.NewReader(stdout).ReadString('\n')
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello", check.Commentf("expected 'hello'"))
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 
-	c.Assert(stdin.Close(), checker.IsNil)
+	assert.NilError(c, stdin.Close())
 
 	cli.DockerCmd(c, "kill", cID)
 	cli.WaitExited(c, cID, 5*time.Second)
 
 	until := daemonUnixTime(c)
 	out = cli.DockerCmd(c, "events", "-f", "container="+cID, "--until="+until).Combined()
-	c.Assert(out, checker.Contains, "attach", check.Commentf("Missing 'attach' log event"))
+	assert.Assert(c, strings.Contains(out, "attach"), "Missing 'attach' log event")
 }
 
-func (s *DockerSuite) TestEventsRename(c *check.C) {
+func (s *DockerSuite) TestEventsRename(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "--name", "oldName", "busybox", "true")
 	cID := strings.TrimSpace(out)
 	dockerCmd(c, "rename", "oldName", "newName")
@@ -522,27 +507,27 @@ func (s *DockerSuite) TestEventsRename(c *check.C) {
 	until := daemonUnixTime(c)
 	// filter by the container id because the name in the event will be the new name.
 	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until", until)
-	c.Assert(out, checker.Contains, "rename", check.Commentf("Missing 'rename' log event\n"))
+	assert.Assert(c, strings.Contains(out, "rename"), "Missing 'rename' log event")
 }
 
-func (s *DockerSuite) TestEventsTop(c *check.C) {
+func (s *DockerSuite) TestEventsTop(c *testing.T) {
 	// Problematic on Windows as Windows does not support top
 	testRequires(c, DaemonIsLinux)
 
 	out := runSleepingContainer(c, "-d")
 	cID := strings.TrimSpace(out)
-	c.Assert(waitRun(cID), checker.IsNil)
+	assert.NilError(c, waitRun(cID))
 
 	dockerCmd(c, "top", cID)
 	dockerCmd(c, "stop", cID)
 
 	until := daemonUnixTime(c)
 	out, _ = dockerCmd(c, "events", "-f", "container="+cID, "--until="+until)
-	c.Assert(out, checker.Contains, " top", check.Commentf("Missing 'top' log event"))
+	assert.Assert(c, strings.Contains(out, "top"), "Missing 'top' log event")
 }
 
 // #14316
-func (s *DockerRegistrySuite) TestEventsImageFilterPush(c *check.C) {
+func (s *DockerRegistrySuite) TestEventsImageFilterPush(c *testing.T) {
 	// Problematic to port for Windows CI during TP5 timeframe until
 	// supporting push
 	testRequires(c, DaemonIsLinux)
@@ -551,7 +536,7 @@ func (s *DockerRegistrySuite) TestEventsImageFilterPush(c *check.C) {
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
 	cID := strings.TrimSpace(out)
-	c.Assert(waitRun(cID), checker.IsNil)
+	assert.NilError(c, waitRun(cID))
 
 	dockerCmd(c, "commit", cID, repoName)
 	dockerCmd(c, "stop", cID)
@@ -559,12 +544,12 @@ func (s *DockerRegistrySuite) TestEventsImageFilterPush(c *check.C) {
 
 	until := daemonUnixTime(c)
 	out, _ = dockerCmd(c, "events", "-f", "image="+repoName, "-f", "event=push", "--until", until)
-	c.Assert(out, checker.Contains, repoName, check.Commentf("Missing 'push' log event for %s", repoName))
+	assert.Assert(c, strings.Contains(out, repoName), "Missing 'push' log event for %s", repoName)
 }
 
-func (s *DockerSuite) TestEventsFilterType(c *check.C) {
+func (s *DockerSuite) TestEventsFilterType(c *testing.T) {
 	// FIXME(vdemeester) fails on e2e run
-	testRequires(c, SameHostDaemon)
+	testRequires(c, testEnv.IsLocalDaemon)
 	since := daemonUnixTime(c)
 	name := "labelfiltertest"
 	label := "io.docker.testing=image"
@@ -588,9 +573,9 @@ func (s *DockerSuite) TestEventsFilterType(c *check.C) {
 	events := strings.Split(strings.TrimSpace(out), "\n")
 
 	// 2 events from the "docker tag" command, another one is from "docker build"
-	c.Assert(events, checker.HasLen, 3, check.Commentf("Events == %s", events))
+	assert.Equal(c, len(events), 3, "Events == %s", events)
 	for _, e := range events {
-		c.Assert(e, checker.Contains, "labelfiltertest")
+		assert.Check(c, strings.Contains(e, "labelfiltertest"))
 	}
 
 	out, _ = dockerCmd(
@@ -603,7 +588,7 @@ func (s *DockerSuite) TestEventsFilterType(c *check.C) {
 	events = strings.Split(strings.TrimSpace(out), "\n")
 
 	// Events generated by the container that builds the image
-	c.Assert(events, checker.HasLen, 2, check.Commentf("Events == %s", events))
+	assert.Equal(c, len(events), 2, "Events == %s", events)
 
 	out, _ = dockerCmd(
 		c,
@@ -612,11 +597,11 @@ func (s *DockerSuite) TestEventsFilterType(c *check.C) {
 		"--until", daemonUnixTime(c),
 		"--filter", "type=network")
 	events = strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(events), checker.GreaterOrEqualThan, 1, check.Commentf("Events == %s", events))
+	assert.Assert(c, len(events) >= 1, "Events == %s", events)
 }
 
 // #25798
-func (s *DockerSuite) TestEventsSpecialFiltersWithExecCreate(c *check.C) {
+func (s *DockerSuite) TestEventsSpecialFiltersWithExecCreate(c *testing.T) {
 	since := daemonUnixTime(c)
 	runSleepingContainer(c, "--name", "test-container", "-d")
 	waitRun("test-container")
@@ -633,7 +618,7 @@ func (s *DockerSuite) TestEventsSpecialFiltersWithExecCreate(c *check.C) {
 	)
 
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(events), checker.Equals, 1, check.Commentf("%s", out))
+	assert.Equal(c, len(events), 1, out)
 
 	out, _ = dockerCmd(
 		c,
@@ -643,20 +628,20 @@ func (s *DockerSuite) TestEventsSpecialFiltersWithExecCreate(c *check.C) {
 		"--filter",
 		"event=exec_create",
 	)
-	c.Assert(len(events), checker.Equals, 1, check.Commentf("%s", out))
+	assert.Equal(c, len(events), 1, out)
 }
 
-func (s *DockerSuite) TestEventsFilterImageInContainerAction(c *check.C) {
+func (s *DockerSuite) TestEventsFilterImageInContainerAction(c *testing.T) {
 	since := daemonUnixTime(c)
 	dockerCmd(c, "run", "--name", "test-container", "-d", "busybox", "true")
 	waitRun("test-container")
 
 	out, _ := dockerCmd(c, "events", "--filter", "image=busybox", "--since", since, "--until", daemonUnixTime(c))
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(events), checker.GreaterThan, 1, check.Commentf("%s", out))
+	assert.Assert(c, len(events) > 1, out)
 }
 
-func (s *DockerSuite) TestEventsContainerRestart(c *check.C) {
+func (s *DockerSuite) TestEventsContainerRestart(c *testing.T) {
 	dockerCmd(c, "run", "-d", "--name=testEvent", "--restart=on-failure:3", "busybox", "false")
 
 	// wait until test2 is auto removed.
@@ -667,7 +652,7 @@ func (s *DockerSuite) TestEventsContainerRestart(c *check.C) {
 	}
 
 	err := waitInspect("testEvent", "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTime)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var (
 		createCount int
@@ -678,7 +663,7 @@ func (s *DockerSuite) TestEventsContainerRestart(c *check.C) {
 	events := strings.Split(strings.TrimSpace(out), "\n")
 
 	nEvents := len(events)
-	c.Assert(nEvents, checker.GreaterOrEqualThan, 1) //Missing expected event
+	assert.Assert(c, nEvents >= 1)
 	actions := eventActionsByIDAndType(c, events, "testEvent", "container")
 
 	for _, a := range actions {
@@ -691,12 +676,12 @@ func (s *DockerSuite) TestEventsContainerRestart(c *check.C) {
 			dieCount++
 		}
 	}
-	c.Assert(createCount, checker.Equals, 1, check.Commentf("testEvent should be created 1 times: %v", actions))
-	c.Assert(startCount, checker.Equals, 4, check.Commentf("testEvent should start 4 times: %v", actions))
-	c.Assert(dieCount, checker.Equals, 4, check.Commentf("testEvent should die 4 times: %v", actions))
+	assert.Equal(c, createCount, 1, "testEvent should be created 1 times: %v", actions)
+	assert.Equal(c, startCount, 4, "testEvent should start 4 times: %v", actions)
+	assert.Equal(c, dieCount, 4, "testEvent should die 4 times: %v", actions)
 }
 
-func (s *DockerSuite) TestEventsSinceInTheFuture(c *check.C) {
+func (s *DockerSuite) TestEventsSinceInTheFuture(c *testing.T) {
 	dockerCmd(c, "run", "--name", "test-container", "-d", "busybox", "true")
 	waitRun("test-container")
 
@@ -704,11 +689,11 @@ func (s *DockerSuite) TestEventsSinceInTheFuture(c *check.C) {
 	until := since.Add(time.Duration(-24) * time.Hour)
 	out, _, err := dockerCmdWithError("events", "--filter", "image=busybox", "--since", parseEventTime(since), "--until", parseEventTime(until))
 
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "cannot be after `until`")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "cannot be after `until`"))
 }
 
-func (s *DockerSuite) TestEventsUntilInThePast(c *check.C) {
+func (s *DockerSuite) TestEventsUntilInThePast(c *testing.T) {
 	since := daemonUnixTime(c)
 
 	dockerCmd(c, "run", "--name", "test-container", "-d", "busybox", "true")
@@ -721,11 +706,11 @@ func (s *DockerSuite) TestEventsUntilInThePast(c *check.C) {
 
 	out, _ := dockerCmd(c, "events", "--filter", "image=busybox", "--since", since, "--until", until)
 
-	c.Assert(out, checker.Not(checker.Contains), "test-container2")
-	c.Assert(out, checker.Contains, "test-container")
+	assert.Assert(c, !strings.Contains(out, "test-container2"))
+	assert.Assert(c, strings.Contains(out, "test-container"))
 }
 
-func (s *DockerSuite) TestEventsFormat(c *check.C) {
+func (s *DockerSuite) TestEventsFormat(c *testing.T) {
 	since := daemonUnixTime(c)
 	dockerCmd(c, "run", "--rm", "busybox", "true")
 	dockerCmd(c, "run", "--rm", "busybox", "true")
@@ -739,16 +724,16 @@ func (s *DockerSuite) TestEventsFormat(c *check.C) {
 		if err = dec.Decode(&ev); err == io.EOF {
 			break
 		}
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		if ev.Status == "start" {
 			startCount++
 		}
 	}
 
-	c.Assert(startCount, checker.Equals, 2, check.Commentf("should have had 2 start events but had %d, out: %s", startCount, out))
+	assert.Equal(c, startCount, 2, "should have had 2 start events but had %d, out: %s", startCount, out)
 }
 
-func (s *DockerSuite) TestEventsFormatBadFunc(c *check.C) {
+func (s *DockerSuite) TestEventsFormatBadFunc(c *testing.T) {
 	// make sure it fails immediately, without receiving any event
 	result := dockerCmdWithResult("events", "--format", "{{badFuncString .}}")
 	result.Assert(c, icmd.Expected{
@@ -758,7 +743,7 @@ func (s *DockerSuite) TestEventsFormatBadFunc(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestEventsFormatBadField(c *check.C) {
+func (s *DockerSuite) TestEventsFormatBadField(c *testing.T) {
 	// make sure it fails immediately, without receiving any event
 	result := dockerCmdWithResult("events", "--format", "{{.badFieldString}}")
 	result.Assert(c, icmd.Expected{
diff --git a/integration-cli/docker_cli_events_unix_test.go b/integration-cli/docker_cli_events_unix_test.go
index 92403e006e9c5..dcadf56a2411a 100644
--- a/integration-cli/docker_cli_events_unix_test.go
+++ b/integration-cli/docker_cli_events_unix_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -6,52 +7,51 @@ import (
 	"bufio"
 	"bytes"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"strings"
+	"testing"
 	"time"
 	"unicode"
 
-	"github.com/docker/docker/integration-cli/checker"
+	"github.com/creack/pty"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
-	"github.com/kr/pty"
 	"golang.org/x/sys/unix"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 // #5979
-func (s *DockerSuite) TestEventsRedirectStdout(c *check.C) {
+func (s *DockerSuite) TestEventsRedirectStdout(c *testing.T) {
 	since := daemonUnixTime(c)
 	dockerCmd(c, "run", "busybox", "true")
 
-	file, err := ioutil.TempFile("", "")
-	c.Assert(err, checker.IsNil, check.Commentf("could not create temp file"))
+	file, err := os.CreateTemp("", "")
+	assert.NilError(c, err, "could not create temp file")
 	defer os.Remove(file.Name())
 
 	command := fmt.Sprintf("%s events --since=%s --until=%s > %s", dockerBinary, since, daemonUnixTime(c), file.Name())
 	_, tty, err := pty.Open()
-	c.Assert(err, checker.IsNil, check.Commentf("Could not open pty"))
+	assert.NilError(c, err, "Could not open pty")
 	cmd := exec.Command("sh", "-c", command)
 	cmd.Stdin = tty
 	cmd.Stdout = tty
 	cmd.Stderr = tty
-	c.Assert(cmd.Run(), checker.IsNil, check.Commentf("run err for command %q", command))
+	assert.NilError(c, cmd.Run(), "run err for command %q", command)
 
 	scanner := bufio.NewScanner(file)
 	for scanner.Scan() {
 		for _, ch := range scanner.Text() {
-			c.Assert(unicode.IsControl(ch), checker.False, check.Commentf("found control character %v", []byte(string(ch))))
+			assert.Check(c, unicode.IsControl(ch) == false, "found control character %v", []byte(string(ch)))
 		}
 	}
-	c.Assert(scanner.Err(), checker.IsNil, check.Commentf("Scan err for command %q", command))
-
+	assert.NilError(c, scanner.Err(), "Scan err for command %q", command)
 }
 
-func (s *DockerSuite) TestEventsOOMDisableFalse(c *check.C) {
+func (s *DockerSuite) TestEventsOOMDisableFalse(c *testing.T) {
 	testRequires(c, DaemonIsLinux, oomControl, memoryLimitSupport, swapMemorySupport, NotPpc64le)
 
-	errChan := make(chan error)
+	errChan := make(chan error, 1)
 	go func() {
 		defer close(errChan)
 		out, exitCode, _ := dockerCmdWithError("run", "--name", "oomFalse", "-m", "10MB", "busybox", "sh", "-c", "x=a; while true; do x=$x$x$x$x; done")
@@ -61,7 +61,7 @@ func (s *DockerSuite) TestEventsOOMDisableFalse(c *check.C) {
 	}()
 	select {
 	case err := <-errChan:
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(30 * time.Second):
 		c.Fatal("Timeout waiting for container to die on OOM")
 	}
@@ -70,22 +70,22 @@ func (s *DockerSuite) TestEventsOOMDisableFalse(c *check.C) {
 	events := strings.Split(strings.TrimSuffix(out, "\n"), "\n")
 	nEvents := len(events)
 
-	c.Assert(nEvents, checker.GreaterOrEqualThan, 5) //Missing expected event
-	c.Assert(parseEventAction(c, events[nEvents-5]), checker.Equals, "create")
-	c.Assert(parseEventAction(c, events[nEvents-4]), checker.Equals, "attach")
-	c.Assert(parseEventAction(c, events[nEvents-3]), checker.Equals, "start")
-	c.Assert(parseEventAction(c, events[nEvents-2]), checker.Equals, "oom")
-	c.Assert(parseEventAction(c, events[nEvents-1]), checker.Equals, "die")
+	assert.Assert(c, nEvents >= 5)
+	assert.Equal(c, parseEventAction(c, events[nEvents-5]), "create")
+	assert.Equal(c, parseEventAction(c, events[nEvents-4]), "attach")
+	assert.Equal(c, parseEventAction(c, events[nEvents-3]), "start")
+	assert.Equal(c, parseEventAction(c, events[nEvents-2]), "oom")
+	assert.Equal(c, parseEventAction(c, events[nEvents-1]), "die")
 }
 
-func (s *DockerSuite) TestEventsOOMDisableTrue(c *check.C) {
+func (s *DockerSuite) TestEventsOOMDisableTrue(c *testing.T) {
 	testRequires(c, DaemonIsLinux, oomControl, memoryLimitSupport, NotArm, swapMemorySupport, NotPpc64le)
 
-	errChan := make(chan error)
+	errChan := make(chan error, 1)
 	observer, err := newEventObserver(c)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	err = observer.Start()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer observer.Stop()
 
 	go func() {
@@ -96,7 +96,7 @@ func (s *DockerSuite) TestEventsOOMDisableTrue(c *check.C) {
 		}
 	}()
 
-	c.Assert(waitRun("oomTrue"), checker.IsNil)
+	assert.NilError(c, waitRun("oomTrue"))
 	defer dockerCmdWithResult("kill", "oomTrue")
 	containerID := inspectField(c, "oomTrue", "Id")
 
@@ -122,11 +122,11 @@ func (s *DockerSuite) TestEventsOOMDisableTrue(c *check.C) {
 	}
 
 	status := inspectField(c, "oomTrue", "State.Status")
-	c.Assert(strings.TrimSpace(status), checker.Equals, "running", check.Commentf("container should be still running"))
+	assert.Equal(c, strings.TrimSpace(status), "running", "container should be still running")
 }
 
 // #18453
-func (s *DockerSuite) TestEventsContainerFilterByName(c *check.C) {
+func (s *DockerSuite) TestEventsContainerFilterByName(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	cOut, _ := dockerCmd(c, "run", "--name=foo", "-d", "busybox", "top")
 	c1 := strings.TrimSpace(cOut)
@@ -135,17 +135,17 @@ func (s *DockerSuite) TestEventsContainerFilterByName(c *check.C) {
 	c2 := strings.TrimSpace(cOut)
 	waitRun("bar")
 	out, _ := dockerCmd(c, "events", "-f", "container=foo", "--since=0", "--until", daemonUnixTime(c))
-	c.Assert(out, checker.Contains, c1, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), c2, check.Commentf("%s", out))
+	assert.Assert(c, strings.Contains(out, c1), out)
+	assert.Assert(c, !strings.Contains(out, c2), out)
 }
 
 // #18453
-func (s *DockerSuite) TestEventsContainerFilterBeforeCreate(c *check.C) {
+func (s *DockerSuite) TestEventsContainerFilterBeforeCreate(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	buf := &bytes.Buffer{}
 	cmd := exec.Command(dockerBinary, "events", "-f", "container=foo", "--since=0")
 	cmd.Stdout = buf
-	c.Assert(cmd.Start(), check.IsNil)
+	assert.NilError(c, cmd.Start())
 	defer cmd.Wait()
 	defer cmd.Process.Kill()
 
@@ -165,7 +165,7 @@ func (s *DockerSuite) TestEventsContainerFilterBeforeCreate(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestVolumeEvents(c *check.C) {
+func (s *DockerSuite) TestVolumeEvents(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	since := daemonUnixTime(c)
@@ -182,18 +182,17 @@ func (s *DockerSuite) TestVolumeEvents(c *check.C) {
 	until := daemonUnixTime(c)
 	out, _ := dockerCmd(c, "events", "--since", since, "--until", until)
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(events), checker.GreaterThan, 4)
+	assert.Assert(c, len(events) > 3)
 
 	volumeEvents := eventActionsByIDAndType(c, events, "test-event-volume-local", "volume")
-	c.Assert(volumeEvents, checker.HasLen, 5)
-	c.Assert(volumeEvents[0], checker.Equals, "create")
-	c.Assert(volumeEvents[1], checker.Equals, "create")
-	c.Assert(volumeEvents[2], checker.Equals, "mount")
-	c.Assert(volumeEvents[3], checker.Equals, "unmount")
-	c.Assert(volumeEvents[4], checker.Equals, "destroy")
+	assert.Equal(c, len(volumeEvents), 4)
+	assert.Equal(c, volumeEvents[0], "create")
+	assert.Equal(c, volumeEvents[1], "mount")
+	assert.Equal(c, volumeEvents[2], "unmount")
+	assert.Equal(c, volumeEvents[3], "destroy")
 }
 
-func (s *DockerSuite) TestNetworkEvents(c *check.C) {
+func (s *DockerSuite) TestNetworkEvents(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	since := daemonUnixTime(c)
@@ -210,17 +209,17 @@ func (s *DockerSuite) TestNetworkEvents(c *check.C) {
 	until := daemonUnixTime(c)
 	out, _ := dockerCmd(c, "events", "--since", since, "--until", until)
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(events), checker.GreaterThan, 4)
+	assert.Assert(c, len(events) > 4)
 
 	netEvents := eventActionsByIDAndType(c, events, "test-event-network-local", "network")
-	c.Assert(netEvents, checker.HasLen, 4)
-	c.Assert(netEvents[0], checker.Equals, "create")
-	c.Assert(netEvents[1], checker.Equals, "connect")
-	c.Assert(netEvents[2], checker.Equals, "disconnect")
-	c.Assert(netEvents[3], checker.Equals, "destroy")
+	assert.Equal(c, len(netEvents), 4)
+	assert.Equal(c, netEvents[0], "create")
+	assert.Equal(c, netEvents[1], "connect")
+	assert.Equal(c, netEvents[2], "disconnect")
+	assert.Equal(c, netEvents[3], "destroy")
 }
 
-func (s *DockerSuite) TestEventsContainerWithMultiNetwork(c *check.C) {
+func (s *DockerSuite) TestEventsContainerWithMultiNetwork(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	// Observe create/connect network actions
@@ -239,22 +238,22 @@ func (s *DockerSuite) TestEventsContainerWithMultiNetwork(c *check.C) {
 	netEvents := strings.Split(strings.TrimSpace(out), "\n")
 
 	// received two network disconnect events
-	c.Assert(len(netEvents), checker.Equals, 2)
-	c.Assert(netEvents[0], checker.Contains, "disconnect")
-	c.Assert(netEvents[1], checker.Contains, "disconnect")
+	assert.Equal(c, len(netEvents), 2)
+	assert.Assert(c, strings.Contains(netEvents[0], "disconnect"))
+	assert.Assert(c, strings.Contains(netEvents[1], "disconnect"))
 
-	//both networks appeared in the network event output
-	c.Assert(out, checker.Contains, "test-event-network-local-1")
-	c.Assert(out, checker.Contains, "test-event-network-local-2")
+	// both networks appeared in the network event output
+	assert.Assert(c, strings.Contains(out, "test-event-network-local-1"))
+	assert.Assert(c, strings.Contains(out, "test-event-network-local-2"))
 }
 
-func (s *DockerSuite) TestEventsStreaming(c *check.C) {
+func (s *DockerSuite) TestEventsStreaming(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	observer, err := newEventObserver(c)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	err = observer.Start()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer observer.Stop()
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox:latest", "true")
@@ -302,20 +301,20 @@ func (s *DockerSuite) TestEventsStreaming(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestEventsImageUntagDelete(c *check.C) {
+func (s *DockerSuite) TestEventsImageUntagDelete(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	observer, err := newEventObserver(c)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	err = observer.Start()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer observer.Stop()
 
 	name := "testimageevents"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM scratch
 		MAINTAINER "docker"`))
 	imageID := getIDByName(c, name)
-	c.Assert(deleteImages(name), checker.IsNil)
+	assert.NilError(c, deleteImages(name))
 
 	testActions := map[string]chan bool{
 		"untag":  make(chan bool, 1),
@@ -341,7 +340,7 @@ func (s *DockerSuite) TestEventsImageUntagDelete(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestEventsFilterVolumeAndNetworkType(c *check.C) {
+func (s *DockerSuite) TestEventsFilterVolumeAndNetworkType(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	since := daemonUnixTime(c)
@@ -351,16 +350,16 @@ func (s *DockerSuite) TestEventsFilterVolumeAndNetworkType(c *check.C) {
 
 	out, _ := dockerCmd(c, "events", "--filter", "type=volume", "--filter", "type=network", "--since", since, "--until", daemonUnixTime(c))
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(events), checker.GreaterOrEqualThan, 2, check.Commentf("%s", out))
+	assert.Assert(c, len(events) >= 2, out)
 
 	networkActions := eventActionsByIDAndType(c, events, "test-event-network-type", "network")
 	volumeActions := eventActionsByIDAndType(c, events, "test-event-volume-type", "volume")
 
-	c.Assert(volumeActions[0], checker.Equals, "create")
-	c.Assert(networkActions[0], checker.Equals, "create")
+	assert.Equal(c, volumeActions[0], "create")
+	assert.Equal(c, networkActions[0], "create")
 }
 
-func (s *DockerSuite) TestEventsFilterVolumeID(c *check.C) {
+func (s *DockerSuite) TestEventsFilterVolumeID(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	since := daemonUnixTime(c)
@@ -368,13 +367,14 @@ func (s *DockerSuite) TestEventsFilterVolumeID(c *check.C) {
 	dockerCmd(c, "volume", "create", "test-event-volume-id")
 	out, _ := dockerCmd(c, "events", "--filter", "volume=test-event-volume-id", "--since", since, "--until", daemonUnixTime(c))
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(events, checker.HasLen, 1)
+	assert.Equal(c, len(events), 1)
 
-	c.Assert(events[0], checker.Contains, "test-event-volume-id")
-	c.Assert(events[0], checker.Contains, "driver=local")
+	assert.Equal(c, len(events), 1)
+	assert.Assert(c, strings.Contains(events[0], "test-event-volume-id"))
+	assert.Assert(c, strings.Contains(events[0], "driver=local"))
 }
 
-func (s *DockerSuite) TestEventsFilterNetworkID(c *check.C) {
+func (s *DockerSuite) TestEventsFilterNetworkID(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	since := daemonUnixTime(c)
@@ -382,57 +382,38 @@ func (s *DockerSuite) TestEventsFilterNetworkID(c *check.C) {
 	dockerCmd(c, "network", "create", "test-event-network-local")
 	out, _ := dockerCmd(c, "events", "--filter", "network=test-event-network-local", "--since", since, "--until", daemonUnixTime(c))
 	events := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(events, checker.HasLen, 1)
-
-	c.Assert(events[0], checker.Contains, "test-event-network-local")
-	c.Assert(events[0], checker.Contains, "type=bridge")
+	assert.Equal(c, len(events), 1)
+	assert.Assert(c, strings.Contains(events[0], "test-event-network-local"))
+	assert.Assert(c, strings.Contains(events[0], "type=bridge"))
 }
 
-func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerDaemonSuite) TestDaemonEvents(c *testing.T) {
 
 	// daemon config file
 	configFilePath := "test.json"
-	configFile, err := os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
 	defer os.Remove(configFilePath)
 
 	daemonConfig := `{"labels":["foo=bar"]}`
-	fmt.Fprintf(configFile, "%s", daemonConfig)
-	configFile.Close()
-	s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath))
-
-	// Get daemon ID
-	out, err := s.d.Cmd("info")
-	c.Assert(err, checker.IsNil)
-	daemonID := ""
-	daemonName := ""
-	for _, line := range strings.Split(out, "\n") {
-		if strings.HasPrefix(line, "ID: ") {
-			daemonID = strings.TrimPrefix(line, "ID: ")
-		} else if strings.HasPrefix(line, "Name: ") {
-			daemonName = strings.TrimPrefix(line, "Name: ")
-		}
-	}
-	c.Assert(daemonID, checker.Not(checker.Equals), "")
+	err := os.WriteFile(configFilePath, []byte(daemonConfig), 0644)
+	assert.NilError(c, err)
+	s.d.Start(c, "--config-file="+configFilePath)
 
-	configFile, err = os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
-	daemonConfig = `{"max-concurrent-downloads":1,"labels":["bar=foo"], "shutdown-timeout": 10}`
-	fmt.Fprintf(configFile, "%s", daemonConfig)
-	configFile.Close()
+	info := s.d.Info(c)
 
-	c.Assert(s.d.Signal(unix.SIGHUP), checker.IsNil)
+	daemonConfig = `{"max-concurrent-downloads":1,"labels":["bar=foo"], "shutdown-timeout": 10}`
+	err = os.WriteFile(configFilePath, []byte(daemonConfig), 0644)
+	assert.NilError(c, err)
 
+	assert.NilError(c, s.d.Signal(unix.SIGHUP))
 	time.Sleep(3 * time.Second)
 
-	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
-	c.Assert(err, checker.IsNil)
+	out, err := s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
+	assert.NilError(c, err)
 
 	// only check for values known (daemon ID/name) or explicitly set above,
 	// otherwise just check for names being present.
 	expectedSubstrings := []string{
-		" daemon reload " + daemonID + " ",
+		" daemon reload " + info.ID + " ",
 		"(allow-nondistributable-artifacts=[",
 		" cluster-advertise=, ",
 		" cluster-store=, ",
@@ -446,66 +427,50 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
 		" live-restore=",
 		" max-concurrent-downloads=1, ",
 		" max-concurrent-uploads=5, ",
-		" name=" + daemonName,
+		" name=" + info.Name,
 		" registry-mirrors=[",
 		" runtimes=",
 		" shutdown-timeout=10)",
 	}
 
 	for _, s := range expectedSubstrings {
-		c.Assert(out, checker.Contains, s)
+		assert.Check(c, is.Contains(out, s))
 	}
 }
 
-func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *testing.T) {
 
 	// daemon config file
 	configFilePath := "test.json"
-	configFile, err := os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
 	defer os.Remove(configFilePath)
 
 	daemonConfig := `{"labels":["foo=bar"]}`
-	fmt.Fprintf(configFile, "%s", daemonConfig)
-	configFile.Close()
-	s.d.Start(c, fmt.Sprintf("--config-file=%s", configFilePath))
-
-	// Get daemon ID
-	out, err := s.d.Cmd("info")
-	c.Assert(err, checker.IsNil)
-	daemonID := ""
-	daemonName := ""
-	for _, line := range strings.Split(out, "\n") {
-		if strings.HasPrefix(line, "ID: ") {
-			daemonID = strings.TrimPrefix(line, "ID: ")
-		} else if strings.HasPrefix(line, "Name: ") {
-			daemonName = strings.TrimPrefix(line, "Name: ")
-		}
-	}
-	c.Assert(daemonID, checker.Not(checker.Equals), "")
+	err := os.WriteFile(configFilePath, []byte(daemonConfig), 0644)
+	assert.NilError(c, err)
+	s.d.Start(c, "--config-file="+configFilePath)
 
-	c.Assert(s.d.Signal(unix.SIGHUP), checker.IsNil)
+	info := s.d.Info(c)
 
+	assert.NilError(c, s.d.Signal(unix.SIGHUP))
 	time.Sleep(3 * time.Second)
 
-	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", daemonID))
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s", daemonID))
+	out, err := s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", info.ID))
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("daemon reload %s", info.ID)))
 
-	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", daemonName))
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s", daemonID))
+	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", fmt.Sprintf("daemon=%s", info.ID))
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("daemon reload %s", info.ID)))
 
 	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "daemon=foo")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Not(checker.Contains), fmt.Sprintf("daemon reload %s", daemonID))
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(out, fmt.Sprintf("daemon reload %s", info.ID)))
 
 	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "type=daemon")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s", daemonID))
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("daemon reload %s", info.ID)))
 
 	out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c), "--filter", "type=container")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Not(checker.Contains), fmt.Sprintf("daemon reload %s", daemonID))
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(out, fmt.Sprintf("daemon reload %s", info.ID)))
 }
diff --git a/integration-cli/docker_cli_exec_test.go b/integration-cli/docker_cli_exec_test.go
index 72059c79e6305..1ef31cfc373a4 100644
--- a/integration-cli/docker_cli_exec_test.go
+++ b/integration-cli/docker_cli_exec_test.go
@@ -1,5 +1,3 @@
-// +build !test_no_exec
-
 package main
 
 import (
@@ -11,134 +9,127 @@ import (
 	"reflect"
 	"runtime"
 	"sort"
-	"strconv"
 	"strings"
 	"sync"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/docker/docker/pkg/parsers/kernel"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/icmd"
 )
 
-func (s *DockerSuite) TestExec(c *check.C) {
+func (s *DockerSuite) TestExec(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && top")
-	c.Assert(waitRun(strings.TrimSpace(out)), check.IsNil)
+	assert.NilError(c, waitRun(strings.TrimSpace(out)))
 
 	out, _ = dockerCmd(c, "exec", "testing", "cat", "/tmp/file")
-	out = strings.Trim(out, "\r\n")
-	c.Assert(out, checker.Equals, "test")
-
+	assert.Equal(c, strings.Trim(out, "\r\n"), "test")
 }
 
-func (s *DockerSuite) TestExecInteractive(c *check.C) {
+func (s *DockerSuite) TestExecInteractive(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "sh", "-c", "echo test > /tmp/file && top")
 
 	execCmd := exec.Command(dockerBinary, "exec", "-i", "testing", "sh")
 	stdin, err := execCmd.StdinPipe()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	stdout, err := execCmd.StdoutPipe()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	err = execCmd.Start()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	_, err = stdin.Write([]byte("cat /tmp/file\n"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	r := bufio.NewReader(stdout)
 	line, err := r.ReadString('\n')
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	line = strings.TrimSpace(line)
-	c.Assert(line, checker.Equals, "test")
+	assert.Equal(c, line, "test")
 	err = stdin.Close()
-	c.Assert(err, checker.IsNil)
-	errChan := make(chan error)
+	assert.NilError(c, err)
+	errChan := make(chan error, 1)
 	go func() {
 		errChan <- execCmd.Wait()
 		close(errChan)
 	}()
 	select {
 	case err := <-errChan:
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(1 * time.Second):
 		c.Fatal("docker exec failed to exit on stdin close")
 	}
 
 }
 
-func (s *DockerSuite) TestExecAfterContainerRestart(c *check.C) {
+func (s *DockerSuite) TestExecAfterContainerRestart(c *testing.T) {
 	out := runSleepingContainer(c)
 	cleanedContainerID := strings.TrimSpace(out)
-	c.Assert(waitRun(cleanedContainerID), check.IsNil)
+	assert.NilError(c, waitRun(cleanedContainerID))
 	dockerCmd(c, "restart", cleanedContainerID)
-	c.Assert(waitRun(cleanedContainerID), check.IsNil)
+	assert.NilError(c, waitRun(cleanedContainerID))
 
 	out, _ = dockerCmd(c, "exec", cleanedContainerID, "echo", "hello")
-	outStr := strings.TrimSpace(out)
-	c.Assert(outStr, checker.Equals, "hello")
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 }
 
-func (s *DockerDaemonSuite) TestExecAfterDaemonRestart(c *check.C) {
-	// TODO Windows CI: Requires a little work to get this ported.
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerDaemonSuite) TestExecAfterDaemonRestart(c *testing.T) {
+	// TODO Windows CI: DockerDaemonSuite doesn't run on Windows, and requires a little work to get this ported.
 	s.d.StartWithBusybox(c)
 
 	out, err := s.d.Cmd("run", "-d", "--name", "top", "-p", "80", "busybox:latest", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("Could not run top: %s", out))
+	assert.NilError(c, err, "Could not run top: %s", out)
 
 	s.d.Restart(c)
 
 	out, err = s.d.Cmd("start", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("Could not start top after daemon restart: %s", out))
+	assert.NilError(c, err, "Could not start top after daemon restart: %s", out)
 
 	out, err = s.d.Cmd("exec", "top", "echo", "hello")
-	c.Assert(err, checker.IsNil, check.Commentf("Could not exec on container top: %s", out))
-
-	outStr := strings.TrimSpace(string(out))
-	c.Assert(outStr, checker.Equals, "hello")
+	assert.NilError(c, err, "Could not exec on container top: %s", out)
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 }
 
 // Regression test for #9155, #9044
-func (s *DockerSuite) TestExecEnv(c *check.C) {
+func (s *DockerSuite) TestExecEnv(c *testing.T) {
 	// TODO Windows CI: This one is interesting and may just end up being a feature
 	// difference between Windows and Linux. On Windows, the environment is passed
 	// into the process that is launched, not into the machine environment. Hence
 	// a subsequent exec will not have LALA set/
 	testRequires(c, DaemonIsLinux)
 	runSleepingContainer(c, "-e", "LALA=value1", "-e", "LALA=value2", "-d", "--name", "testing")
-	c.Assert(waitRun("testing"), check.IsNil)
+	assert.NilError(c, waitRun("testing"))
 
 	out, _ := dockerCmd(c, "exec", "testing", "env")
-	c.Assert(out, checker.Not(checker.Contains), "LALA=value1")
-	c.Assert(out, checker.Contains, "LALA=value2")
-	c.Assert(out, checker.Contains, "HOME=/root")
+	assert.Check(c, !strings.Contains(out, "LALA=value1"))
+	assert.Check(c, strings.Contains(out, "LALA=value2"))
+	assert.Check(c, strings.Contains(out, "HOME=/root"))
 }
 
-func (s *DockerSuite) TestExecSetEnv(c *check.C) {
+func (s *DockerSuite) TestExecSetEnv(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	runSleepingContainer(c, "-e", "HOME=/root", "-d", "--name", "testing")
-	c.Assert(waitRun("testing"), check.IsNil)
+	assert.NilError(c, waitRun("testing"))
 
 	out, _ := dockerCmd(c, "exec", "-e", "HOME=/another", "-e", "ABC=xyz", "testing", "env")
-	c.Assert(out, checker.Not(checker.Contains), "HOME=/root")
-	c.Assert(out, checker.Contains, "HOME=/another")
-	c.Assert(out, checker.Contains, "ABC=xyz")
+	assert.Check(c, !strings.Contains(out, "HOME=/root"))
+	assert.Check(c, strings.Contains(out, "HOME=/another"))
+	assert.Check(c, strings.Contains(out, "ABC=xyz"))
 }
 
-func (s *DockerSuite) TestExecExitStatus(c *check.C) {
+func (s *DockerSuite) TestExecExitStatus(c *testing.T) {
 	runSleepingContainer(c, "-d", "--name", "top")
 
 	result := icmd.RunCommand(dockerBinary, "exec", "top", "sh", "-c", "exit 23")
 	result.Assert(c, icmd.Expected{ExitCode: 23, Error: "exit status 23"})
 }
 
-func (s *DockerSuite) TestExecPausedContainer(c *check.C) {
+func (s *DockerSuite) TestExecPausedContainer(c *testing.T) {
 	testRequires(c, IsPausable)
 
 	out := runSleepingContainer(c, "-d", "--name", "testing")
@@ -146,40 +137,40 @@ func (s *DockerSuite) TestExecPausedContainer(c *check.C) {
 
 	dockerCmd(c, "pause", "testing")
 	out, _, err := dockerCmdWithError("exec", ContainerID, "echo", "hello")
-	c.Assert(err, checker.NotNil, check.Commentf("container should fail to exec new command if it is paused"))
+	assert.ErrorContains(c, err, "", "container should fail to exec new command if it is paused")
 
 	expected := ContainerID + " is paused, unpause the container before exec"
-	c.Assert(out, checker.Contains, expected, check.Commentf("container should not exec new command if it is paused"))
+	assert.Assert(c, is.Contains(out, expected), "container should not exec new command if it is paused")
 }
 
 // regression test for #9476
-func (s *DockerSuite) TestExecTTYCloseStdin(c *check.C) {
+func (s *DockerSuite) TestExecTTYCloseStdin(c *testing.T) {
 	// TODO Windows CI: This requires some work to port to Windows.
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "-it", "--name", "exec_tty_stdin", "busybox")
 
 	cmd := exec.Command(dockerBinary, "exec", "-i", "exec_tty_stdin", "cat")
 	stdinRw, err := cmd.StdinPipe()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	stdinRw.Write([]byte("test"))
 	stdinRw.Close()
 
 	out, _, err := runCommandWithOutput(cmd)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, _ = dockerCmd(c, "top", "exec_tty_stdin")
 	outArr := strings.Split(out, "\n")
-	c.Assert(len(outArr), checker.LessOrEqualThan, 3, check.Commentf("exec process left running"))
-	c.Assert(out, checker.Not(checker.Contains), "nsenter-exec")
+	assert.Assert(c, len(outArr) <= 3, "exec process left running")
+	assert.Assert(c, !strings.Contains(out, "nsenter-exec"))
 }
 
-func (s *DockerSuite) TestExecTTYWithoutStdin(c *check.C) {
+func (s *DockerSuite) TestExecTTYWithoutStdin(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "-ti", "busybox")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
-	errChan := make(chan error)
+	errChan := make(chan error, 1)
 	go func() {
 		defer close(errChan)
 
@@ -204,14 +195,14 @@ func (s *DockerSuite) TestExecTTYWithoutStdin(c *check.C) {
 
 	select {
 	case err := <-errChan:
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(3 * time.Second):
 		c.Fatal("exec is running but should have failed")
 	}
 }
 
 // FIXME(vdemeester) this should be a unit tests on cli/command/container package
-func (s *DockerSuite) TestExecParseError(c *check.C) {
+func (s *DockerSuite) TestExecParseError(c *testing.T) {
 	// TODO Windows CI: Requires some extra work. Consider copying the
 	// runSleepingContainer helper to have an exec version.
 	testRequires(c, DaemonIsLinux)
@@ -225,7 +216,7 @@ func (s *DockerSuite) TestExecParseError(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestExecStopNotHanging(c *check.C) {
+func (s *DockerSuite) TestExecStopNotHanging(c *testing.T) {
 	// TODO Windows CI: Requires some extra work. Consider copying the
 	// runSleepingContainer helper to have an exec version.
 	testRequires(c, DaemonIsLinux)
@@ -239,7 +230,7 @@ func (s *DockerSuite) TestExecStopNotHanging(c *check.C) {
 		out string
 		err error
 	}
-	ch := make(chan dstop)
+	ch := make(chan dstop, 1)
 	go func() {
 		result := icmd.RunCommand(dockerBinary, "stop", "testing")
 		ch <- dstop{result.Combined(), result.Error}
@@ -249,11 +240,11 @@ func (s *DockerSuite) TestExecStopNotHanging(c *check.C) {
 	case <-time.After(3 * time.Second):
 		c.Fatal("Container stop timed out")
 	case s := <-ch:
-		c.Assert(s.err, check.IsNil)
+		assert.NilError(c, s.err)
 	}
 }
 
-func (s *DockerSuite) TestExecCgroup(c *check.C) {
+func (s *DockerSuite) TestExecCgroup(c *testing.T) {
 	// Not applicable on Windows - using Linux specific functionality
 	testRequires(c, NotUserNamespace)
 	testRequires(c, DaemonIsLinux)
@@ -265,11 +256,12 @@ func (s *DockerSuite) TestExecCgroup(c *check.C) {
 	var wg sync.WaitGroup
 	var mu sync.Mutex
 	var execCgroups []sort.StringSlice
-	errChan := make(chan error)
+	errChan := make(chan error, 5)
 	// exec a few times concurrently to get consistent failure
 	for i := 0; i < 5; i++ {
 		wg.Add(1)
 		go func() {
+			defer wg.Done()
 			out, _, err := dockerCmdWithError("exec", "testing", "cat", "/proc/self/cgroup")
 			if err != nil {
 				errChan <- err
@@ -280,14 +272,13 @@ func (s *DockerSuite) TestExecCgroup(c *check.C) {
 			mu.Lock()
 			execCgroups = append(execCgroups, cg)
 			mu.Unlock()
-			wg.Done()
 		}()
 	}
 	wg.Wait()
 	close(errChan)
 
 	for err := range errChan {
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	}
 
 	for _, cg := range execCgroups {
@@ -306,19 +297,19 @@ func (s *DockerSuite) TestExecCgroup(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestExecInspectID(c *check.C) {
+func (s *DockerSuite) TestExecInspectID(c *testing.T) {
 	out := runSleepingContainer(c, "-d")
 	id := strings.TrimSuffix(out, "\n")
 
 	out = inspectField(c, id, "ExecIDs")
-	c.Assert(out, checker.Equals, "[]", check.Commentf("ExecIDs should be empty, got: %s", out))
+	assert.Equal(c, out, "[]", "ExecIDs should be empty, got: %s", out)
 
 	// Start an exec, have it block waiting so we can do some checking
 	cmd := exec.Command(dockerBinary, "exec", id, "sh", "-c",
 		"while ! test -e /execid1; do sleep 1; done")
 
 	err := cmd.Start()
-	c.Assert(err, checker.IsNil, check.Commentf("failed to start the exec cmd"))
+	assert.NilError(c, err, "failed to start the exec cmd")
 
 	// Give the exec 10 chances/seconds to start then give up and stop the test
 	tries := 10
@@ -329,19 +320,17 @@ func (s *DockerSuite) TestExecInspectID(c *check.C) {
 		if out != "[]" && out != "" {
 			break
 		}
-		c.Assert(i+1, checker.Not(checker.Equals), tries, check.Commentf("ExecIDs still empty after 10 second"))
+		assert.Check(c, i+1 != tries, "ExecIDs still empty after 10 second")
 		time.Sleep(1 * time.Second)
 	}
 
 	// Save execID for later
 	execID, err := inspectFilter(id, "index .ExecIDs 0")
-	c.Assert(err, checker.IsNil, check.Commentf("failed to get the exec id"))
+	assert.NilError(c, err, "failed to get the exec id")
 
 	// End the exec by creating the missing file
-	err = exec.Command(dockerBinary, "exec", id,
-		"sh", "-c", "touch /execid1").Run()
-
-	c.Assert(err, checker.IsNil, check.Commentf("failed to run the 2nd exec cmd"))
+	err = exec.Command(dockerBinary, "exec", id, "sh", "-c", "touch /execid1").Run()
+	assert.NilError(c, err, "failed to run the 2nd exec cmd")
 
 	// Wait for 1st exec to complete
 	cmd.Wait()
@@ -354,47 +343,46 @@ func (s *DockerSuite) TestExecInspectID(c *check.C) {
 		if out == "[]" {
 			break
 		}
-		c.Assert(i+1, checker.Not(checker.Equals), tries, check.Commentf("ExecIDs still not empty after 10 second"))
+		assert.Check(c, i+1 != tries, "ExecIDs still empty after 10 second")
 		time.Sleep(1 * time.Second)
 	}
 
 	// But we should still be able to query the execID
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	_, err = cli.ContainerExecInspect(context.Background(), execID)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// Now delete the container and then an 'inspect' on the exec should
 	// result in a 404 (not 'container not running')
 	out, ec := dockerCmd(c, "rm", "-f", id)
-	c.Assert(ec, checker.Equals, 0, check.Commentf("error removing container: %s", out))
+	assert.Equal(c, ec, 0, "error removing container: %s", out)
 
 	_, err = cli.ContainerExecInspect(context.Background(), execID)
-	expected := "No such exec instance"
-	c.Assert(err.Error(), checker.Contains, expected)
+	assert.ErrorContains(c, err, "No such exec instance")
 }
 
-func (s *DockerSuite) TestLinksPingLinkedContainersOnRename(c *check.C) {
+func (s *DockerSuite) TestLinksPingLinkedContainersOnRename(c *testing.T) {
 	// Problematic on Windows as Windows does not support links
 	testRequires(c, DaemonIsLinux)
 	var out string
 	out, _ = dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top")
 	idA := strings.TrimSpace(out)
-	c.Assert(idA, checker.Not(checker.Equals), "", check.Commentf("%s, id should not be nil", out))
+	assert.Assert(c, idA != "", "%s, id should not be nil", out)
 	out, _ = dockerCmd(c, "run", "-d", "--link", "container1:alias1", "--name", "container2", "busybox", "top")
 	idB := strings.TrimSpace(out)
-	c.Assert(idB, checker.Not(checker.Equals), "", check.Commentf("%s, id should not be nil", out))
+	assert.Assert(c, idB != "", "%s, id should not be nil", out)
 
 	dockerCmd(c, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1")
 	dockerCmd(c, "rename", "container1", "container_new")
 	dockerCmd(c, "exec", "container2", "ping", "-c", "1", "alias1", "-W", "1")
 }
 
-func (s *DockerSuite) TestRunMutableNetworkFiles(c *check.C) {
+func (s *DockerSuite) TestRunMutableNetworkFiles(c *testing.T) {
 	// Not applicable on Windows to Windows CI.
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 	for _, fn := range []string{"resolv.conf", "hosts"} {
 		containers := cli.DockerCmd(c, "ps", "-q", "-a").Combined()
 		if containers != "" {
@@ -403,14 +391,14 @@ func (s *DockerSuite) TestRunMutableNetworkFiles(c *check.C) {
 
 		content := runCommandAndReadContainerFile(c, fn, dockerBinary, "run", "-d", "--name", "c1", "busybox", "sh", "-c", fmt.Sprintf("echo success >/etc/%s && top", fn))
 
-		c.Assert(strings.TrimSpace(string(content)), checker.Equals, "success", check.Commentf("Content was not what was modified in the container", string(content)))
+		assert.Equal(c, strings.TrimSpace(string(content)), "success", "Content was not what was modified in the container", string(content))
 
 		out, _ := dockerCmd(c, "run", "-d", "--name", "c2", "busybox", "top")
 		contID := strings.TrimSpace(out)
 		netFilePath := containerStorageFile(contID, fn)
 
 		f, err := os.OpenFile(netFilePath, os.O_WRONLY|os.O_SYNC|os.O_APPEND, 0644)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 
 		if _, err := f.Seek(0, 0); err != nil {
 			f.Close()
@@ -429,24 +417,24 @@ func (s *DockerSuite) TestRunMutableNetworkFiles(c *check.C) {
 		f.Close()
 
 		res, _ := dockerCmd(c, "exec", contID, "cat", "/etc/"+fn)
-		c.Assert(res, checker.Equals, "success2\n")
+		assert.Equal(c, res, "success2\n")
 	}
 }
 
-func (s *DockerSuite) TestExecWithUser(c *check.C) {
+func (s *DockerSuite) TestExecWithUser(c *testing.T) {
 	// TODO Windows CI: This may be fixable in the future once Windows
 	// supports users
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "--name", "parent", "busybox", "top")
 
 	out, _ := dockerCmd(c, "exec", "-u", "1", "parent", "id")
-	c.Assert(out, checker.Contains, "uid=1(daemon) gid=1(daemon)")
+	assert.Assert(c, strings.Contains(out, "uid=1(daemon) gid=1(daemon)"))
 
 	out, _ = dockerCmd(c, "exec", "-u", "root", "parent", "id")
-	c.Assert(out, checker.Contains, "uid=0(root) gid=0(root)", check.Commentf("exec with user by id expected daemon user got %s", out))
+	assert.Assert(c, strings.Contains(out, "uid=0(root) gid=0(root)"), "exec with user by id expected daemon user got %s", out)
 }
 
-func (s *DockerSuite) TestExecWithPrivileged(c *check.C) {
+func (s *DockerSuite) TestExecWithPrivileged(c *testing.T) {
 	// Not applicable on Windows
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	// Start main loop which attempts mknod repeatedly
@@ -463,7 +451,7 @@ func (s *DockerSuite) TestExecWithPrivileged(c *check.C) {
 	result.Assert(c, icmd.Success)
 
 	actual := strings.TrimSpace(result.Combined())
-	c.Assert(actual, checker.Equals, "ok", check.Commentf("exec mknod in --cap-drop=ALL container with --privileged failed, output: %q", result.Combined()))
+	assert.Equal(c, actual, "ok", "exec mknod in --cap-drop=ALL container with --privileged failed, output: %q", result.Combined())
 
 	// Check subsequent unprivileged exec cannot mknod
 	icmd.RunCommand(dockerBinary, "exec", "parent", "sh", "-c", "mknod /tmp/sdc b 8 32").Assert(c, icmd.Expected{
@@ -473,11 +461,10 @@ func (s *DockerSuite) TestExecWithPrivileged(c *check.C) {
 	// Confirm at no point was mknod allowed
 	result = icmd.RunCommand(dockerBinary, "logs", "parent")
 	result.Assert(c, icmd.Success)
-	c.Assert(result.Combined(), checker.Not(checker.Contains), "Success")
-
+	assert.Assert(c, !strings.Contains(result.Combined(), "Success"))
 }
 
-func (s *DockerSuite) TestExecWithImageUser(c *check.C) {
+func (s *DockerSuite) TestExecWithImageUser(c *testing.T) {
 	// Not applicable on Windows
 	testRequires(c, DaemonIsLinux)
 	name := "testbuilduser"
@@ -487,10 +474,10 @@ func (s *DockerSuite) TestExecWithImageUser(c *check.C) {
 	dockerCmd(c, "run", "-d", "--name", "dockerioexec", name, "top")
 
 	out, _ := dockerCmd(c, "exec", "dockerioexec", "whoami")
-	c.Assert(out, checker.Contains, "dockerio", check.Commentf("exec with user by id expected dockerio user got %s", out))
+	assert.Assert(c, strings.Contains(out, "dockerio"), "exec with user by id expected dockerio user got %s", out)
 }
 
-func (s *DockerSuite) TestExecOnReadonlyContainer(c *check.C) {
+func (s *DockerSuite) TestExecOnReadonlyContainer(c *testing.T) {
 	// Windows does not support read-only
 	// --read-only + userns has remount issues
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
@@ -498,144 +485,47 @@ func (s *DockerSuite) TestExecOnReadonlyContainer(c *check.C) {
 	dockerCmd(c, "exec", "parent", "true")
 }
 
-func (s *DockerSuite) TestExecUlimits(c *check.C) {
+func (s *DockerSuite) TestExecUlimits(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "testexeculimits"
 	runSleepingContainer(c, "-d", "--ulimit", "nofile=511:511", "--name", name)
-	c.Assert(waitRun(name), checker.IsNil)
+	assert.NilError(c, waitRun(name))
 
 	out, _, err := dockerCmdWithError("exec", name, "sh", "-c", "ulimit -n")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "511")
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), "511")
 }
 
 // #15750
-func (s *DockerSuite) TestExecStartFails(c *check.C) {
+func (s *DockerSuite) TestExecStartFails(c *testing.T) {
 	// TODO Windows CI. This test should be portable. Figure out why it fails
 	// currently.
 	testRequires(c, DaemonIsLinux)
 	name := "exec-15750"
 	runSleepingContainer(c, "-d", "--name", name)
-	c.Assert(waitRun(name), checker.IsNil)
+	assert.NilError(c, waitRun(name))
 
 	out, _, err := dockerCmdWithError("exec", name, "no-such-cmd")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "executable file not found")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "executable file not found"))
 }
 
 // Fix regression in https://github.com/docker/docker/pull/26461#issuecomment-250287297
-func (s *DockerSuite) TestExecWindowsPathNotWiped(c *check.C) {
+func (s *DockerSuite) TestExecWindowsPathNotWiped(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	out, _ := dockerCmd(c, "run", "-d", "--name", "testing", minimalBaseImage(), "powershell", "start-sleep", "60")
-	c.Assert(waitRun(strings.TrimSpace(out)), check.IsNil)
+	assert.NilError(c, waitRun(strings.TrimSpace(out)))
 
 	out, _ = dockerCmd(c, "exec", "testing", "powershell", "write-host", "$env:PATH")
 	out = strings.ToLower(strings.Trim(out, "\r\n"))
-	c.Assert(out, checker.Contains, `windowspowershell\v1.0`)
+	assert.Assert(c, strings.Contains(out, `windowspowershell\v1.0`))
 }
 
-func (s *DockerSuite) TestExecEnvLinksHost(c *check.C) {
+func (s *DockerSuite) TestExecEnvLinksHost(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	runSleepingContainer(c, "-d", "--name", "foo")
 	runSleepingContainer(c, "-d", "--link", "foo:db", "--hostname", "myhost", "--name", "bar")
 	out, _ := dockerCmd(c, "exec", "bar", "env")
-	c.Assert(out, checker.Contains, "HOSTNAME=myhost")
-	c.Assert(out, checker.Contains, "DB_NAME=/bar/db")
-}
-
-func (s *DockerSuite) TestExecWindowsOpenHandles(c *check.C) {
-	testRequires(c, DaemonIsWindows)
-
-	if runtime.GOOS == "windows" {
-		v, err := kernel.GetKernelVersion()
-		c.Assert(err, checker.IsNil)
-		build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
-		if build >= 17743 {
-			c.Skip("Temporarily disabled on RS5 17743+ builds due to platform bug")
-
-			// This is being tracked internally. @jhowardmsft. Summary of failure
-			// from an email in early July 2018 below:
-			//
-			// Platform regression. In cmd.exe by the look of it. I can repro
-			// it outside of CI.  It fails the same on 17681, 17676 and even as
-			// far back as 17663, over a month old. From investigating, I can see
-			// what's happening in the container, but not the reason. The test
-			// starts a long-running container based on the Windows busybox image.
-			// It then adds another process (docker exec) to that container to
-			// sleep. It loops waiting for two instances of busybox.exe running,
-			// and cmd.exe to quit. What's actually happening is that the second
-			// exec hangs indefinitely, and from docker top, I can see
-			// "OpenWith.exe" running.
-
-			//Manual repro would be
-			//# Start the first long-running container
-			//docker run --rm -d --name test busybox sleep 300
-
-			//# In another window, docker top test. There should be a single instance of busybox.exe running
-			//# In a third window, docker exec test cmd /c start sleep 10  NOTE THIS HANGS UNTIL 5 MIN TIMEOUT
-			//# In the second window, run docker top test. Note that OpenWith.exe is running, one cmd.exe and only one busybox. I would expect no "OpenWith" and two busybox.exe's.
-		}
-	}
-
-	runSleepingContainer(c, "-d", "--name", "test")
-	exec := make(chan bool)
-	go func() {
-		dockerCmd(c, "exec", "test", "cmd", "/c", "start sleep 10")
-		exec <- true
-	}()
-
-	count := 0
-	for {
-		top := make(chan string)
-		var out string
-		go func() {
-			out, _ := dockerCmd(c, "top", "test")
-			top <- out
-		}()
-
-		select {
-		case <-time.After(time.Second * 5):
-			c.Fatal("timed out waiting for top while exec is exiting")
-		case out = <-top:
-			break
-		}
-
-		if strings.Count(out, "busybox.exe") == 2 && !strings.Contains(out, "cmd.exe") {
-			// The initial exec process (cmd.exe) has exited, and both sleeps are currently running
-			break
-		}
-		count++
-		if count >= 30 {
-			c.Fatal("too many retries")
-		}
-		time.Sleep(1 * time.Second)
-	}
-
-	inspect := make(chan bool)
-	go func() {
-		dockerCmd(c, "inspect", "test")
-		inspect <- true
-	}()
-
-	select {
-	case <-time.After(time.Second * 5):
-		c.Fatal("timed out waiting for inspect while exec is exiting")
-	case <-inspect:
-		break
-	}
-
-	// Ensure the background sleep is still running
-	out, _ := dockerCmd(c, "top", "test")
-	c.Assert(strings.Count(out, "busybox.exe"), checker.Equals, 2)
-
-	// The exec should exit when the background sleep exits
-	select {
-	case <-time.After(time.Second * 15):
-		c.Fatal("timed out waiting for async exec to exit")
-	case <-exec:
-		// Ensure the background sleep has actually exited
-		out, _ := dockerCmd(c, "top", "test")
-		c.Assert(strings.Count(out, "busybox.exe"), checker.Equals, 1)
-		break
-	}
+	assert.Check(c, is.Contains(out, "HOSTNAME=myhost"))
+	assert.Check(c, is.Contains(out, "DB_NAME=/bar/db"))
 }
diff --git a/integration-cli/docker_cli_exec_unix_test.go b/integration-cli/docker_cli_exec_unix_test.go
index 4c77df4f110c7..f83b2aad1cd74 100644
--- a/integration-cli/docker_cli_exec_unix_test.go
+++ b/integration-cli/docker_cli_exec_unix_test.go
@@ -1,4 +1,5 @@
-// +build !windows,!test_no_exec
+//go:build !windows
+// +build !windows
 
 package main
 
@@ -7,91 +8,91 @@ import (
 	"io"
 	"os/exec"
 	"strings"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-	"github.com/kr/pty"
+	"github.com/creack/pty"
+	"gotest.tools/v3/assert"
 )
 
 // regression test for #12546
-func (s *DockerSuite) TestExecInteractiveStdinClose(c *check.C) {
+func (s *DockerSuite) TestExecInteractiveStdinClose(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-itd", "busybox", "/bin/cat")
 	contID := strings.TrimSpace(out)
 
 	cmd := exec.Command(dockerBinary, "exec", "-i", contID, "echo", "-n", "hello")
 	p, err := pty.Start(cmd)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	b := bytes.NewBuffer(nil)
 
-	ch := make(chan error)
+	ch := make(chan error, 1)
 	go func() { ch <- cmd.Wait() }()
 
 	select {
 	case err := <-ch:
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		io.Copy(b, p)
 		p.Close()
 		bs := b.Bytes()
 		bs = bytes.Trim(bs, "\x00")
 		output := string(bs[:])
-		c.Assert(strings.TrimSpace(output), checker.Equals, "hello")
+		assert.Equal(c, strings.TrimSpace(output), "hello")
 	case <-time.After(5 * time.Second):
 		p.Close()
 		c.Fatal("timed out running docker exec")
 	}
 }
 
-func (s *DockerSuite) TestExecTTY(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestExecTTY(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	dockerCmd(c, "run", "-d", "--name=test", "busybox", "sh", "-c", "echo hello > /foo && top")
 
 	cmd := exec.Command(dockerBinary, "exec", "-it", "test", "sh")
 	p, err := pty.Start(cmd)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer p.Close()
 
 	_, err = p.Write([]byte("cat /foo && exit\n"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	chErr := make(chan error)
+	chErr := make(chan error, 1)
 	go func() {
 		chErr <- cmd.Wait()
 	}()
 	select {
 	case err := <-chErr:
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(3 * time.Second):
 		c.Fatal("timeout waiting for exec to exit")
 	}
 
 	buf := make([]byte, 256)
 	read, err := p.Read(buf)
-	c.Assert(err, checker.IsNil)
-	c.Assert(bytes.Contains(buf, []byte("hello")), checker.Equals, true, check.Commentf(string(buf[:read])))
+	assert.NilError(c, err)
+	assert.Assert(c, bytes.Contains(buf, []byte("hello")), string(buf[:read]))
 }
 
 // Test the TERM env var is set when -t is provided on exec
-func (s *DockerSuite) TestExecWithTERM(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestExecWithTERM(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	out, _ := dockerCmd(c, "run", "-id", "busybox", "/bin/cat")
 	contID := strings.TrimSpace(out)
 	cmd := exec.Command(dockerBinary, "exec", "-t", contID, "sh", "-c", "if [ -z $TERM ]; then exit 1; else exit 0; fi")
 	if err := cmd.Run(); err != nil {
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	}
 }
 
 // Test that the TERM env var is not set on exec when -t is not provided, even if it was set
 // on run
-func (s *DockerSuite) TestExecWithNoTERM(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestExecWithNoTERM(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	out, _ := dockerCmd(c, "run", "-itd", "busybox", "/bin/cat")
 	contID := strings.TrimSpace(out)
 	cmd := exec.Command(dockerBinary, "exec", contID, "sh", "-c", "if [ -z $TERM ]; then exit 0; else exit 1; fi")
 	if err := cmd.Run(); err != nil {
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	}
 }
diff --git a/integration-cli/docker_cli_external_volume_driver_test.go b/integration-cli/docker_cli_external_volume_driver_test.go
new file mode 100644
index 0000000000000..0428b7cc2054b
--- /dev/null
+++ b/integration-cli/docker_cli_external_volume_driver_test.go
@@ -0,0 +1,617 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/integration-cli/daemon"
+	"github.com/docker/docker/pkg/stringid"
+	testdaemon "github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/volume"
+	"gotest.tools/v3/assert"
+)
+
+const volumePluginName = "test-external-volume-driver"
+
+type eventCounter struct {
+	activations int
+	creations   int
+	removals    int
+	mounts      int
+	unmounts    int
+	paths       int
+	lists       int
+	gets        int
+	caps        int
+}
+
+type DockerExternalVolumeSuite struct {
+	ds *DockerSuite
+	d  *daemon.Daemon
+	*volumePlugin
+}
+
+func (s *DockerExternalVolumeSuite) SetUpTest(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
+	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
+	s.ec = &eventCounter{}
+}
+
+func (s *DockerExternalVolumeSuite) TearDownTest(c *testing.T) {
+	if s.d != nil {
+		s.d.Stop(c)
+		s.ds.TearDownTest(c)
+	}
+}
+
+func (s *DockerExternalVolumeSuite) SetUpSuite(c *testing.T) {
+	s.volumePlugin = newVolumePlugin(c, volumePluginName)
+}
+
+type volumePlugin struct {
+	ec *eventCounter
+	*httptest.Server
+	vols map[string]vol
+}
+
+type vol struct {
+	Name       string
+	Mountpoint string
+	Ninja      bool // hack used to trigger a null volume return on `Get`
+	Status     map[string]interface{}
+	Options    map[string]string
+}
+
+func (p *volumePlugin) Close() {
+	p.Server.Close()
+}
+
+func newVolumePlugin(c *testing.T, name string) *volumePlugin {
+	mux := http.NewServeMux()
+	s := &volumePlugin{Server: httptest.NewServer(mux), ec: &eventCounter{}, vols: make(map[string]vol)}
+
+	type pluginRequest struct {
+		Name string
+		Opts map[string]string
+		ID   string
+	}
+
+	type pluginResp struct {
+		Mountpoint string `json:",omitempty"`
+		Err        string `json:",omitempty"`
+	}
+
+	read := func(b io.ReadCloser) (pluginRequest, error) {
+		defer b.Close()
+		var pr pluginRequest
+		err := json.NewDecoder(b).Decode(&pr)
+		return pr, err
+	}
+
+	send := func(w http.ResponseWriter, data interface{}) {
+		switch t := data.(type) {
+		case error:
+			http.Error(w, t.Error(), 500)
+		case string:
+			w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
+			fmt.Fprintln(w, t)
+		default:
+			w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
+			json.NewEncoder(w).Encode(&data)
+		}
+	}
+
+	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
+		s.ec.activations++
+		send(w, `{"Implements": ["VolumeDriver"]}`)
+	})
+
+	mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) {
+		s.ec.creations++
+		pr, err := read(r.Body)
+		if err != nil {
+			send(w, err)
+			return
+		}
+		_, isNinja := pr.Opts["ninja"]
+		status := map[string]interface{}{"Hello": "world"}
+		s.vols[pr.Name] = vol{Name: pr.Name, Ninja: isNinja, Status: status, Options: pr.Opts}
+		send(w, nil)
+	})
+
+	mux.HandleFunc("/VolumeDriver.List", func(w http.ResponseWriter, r *http.Request) {
+		s.ec.lists++
+		vols := make([]vol, 0, len(s.vols))
+		for _, v := range s.vols {
+			if v.Ninja {
+				continue
+			}
+			vols = append(vols, v)
+		}
+		send(w, map[string][]vol{"Volumes": vols})
+	})
+
+	mux.HandleFunc("/VolumeDriver.Get", func(w http.ResponseWriter, r *http.Request) {
+		s.ec.gets++
+		pr, err := read(r.Body)
+		if err != nil {
+			send(w, err)
+			return
+		}
+
+		v, exists := s.vols[pr.Name]
+		if !exists {
+			send(w, `{"Err": "no such volume"}`)
+		}
+
+		if v.Ninja {
+			send(w, map[string]vol{})
+			return
+		}
+
+		v.Mountpoint = hostVolumePath(pr.Name)
+		send(w, map[string]vol{"Volume": v})
+	})
+
+	mux.HandleFunc("/VolumeDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
+		s.ec.removals++
+		pr, err := read(r.Body)
+		if err != nil {
+			send(w, err)
+			return
+		}
+
+		v, ok := s.vols[pr.Name]
+		if !ok {
+			send(w, nil)
+			return
+		}
+
+		if err := os.RemoveAll(hostVolumePath(v.Name)); err != nil {
+			send(w, &pluginResp{Err: err.Error()})
+			return
+		}
+		delete(s.vols, v.Name)
+		send(w, nil)
+	})
+
+	mux.HandleFunc("/VolumeDriver.Path", func(w http.ResponseWriter, r *http.Request) {
+		s.ec.paths++
+
+		pr, err := read(r.Body)
+		if err != nil {
+			send(w, err)
+			return
+		}
+		p := hostVolumePath(pr.Name)
+		send(w, &pluginResp{Mountpoint: p})
+	})
+
+	mux.HandleFunc("/VolumeDriver.Mount", func(w http.ResponseWriter, r *http.Request) {
+		s.ec.mounts++
+
+		pr, err := read(r.Body)
+		if err != nil {
+			send(w, err)
+			return
+		}
+
+		if v, exists := s.vols[pr.Name]; exists {
+			// Use this to simulate a mount failure
+			if _, exists := v.Options["invalidOption"]; exists {
+				send(w, fmt.Errorf("invalid argument"))
+				return
+			}
+		}
+
+		p := hostVolumePath(pr.Name)
+		if err := os.MkdirAll(p, 0755); err != nil {
+			send(w, &pluginResp{Err: err.Error()})
+			return
+		}
+
+		if err := os.WriteFile(filepath.Join(p, "test"), []byte(s.Server.URL), 0644); err != nil {
+			send(w, err)
+			return
+		}
+
+		if err := os.WriteFile(filepath.Join(p, "mountID"), []byte(pr.ID), 0644); err != nil {
+			send(w, err)
+			return
+		}
+
+		send(w, &pluginResp{Mountpoint: p})
+	})
+
+	mux.HandleFunc("/VolumeDriver.Unmount", func(w http.ResponseWriter, r *http.Request) {
+		s.ec.unmounts++
+
+		_, err := read(r.Body)
+		if err != nil {
+			send(w, err)
+			return
+		}
+
+		send(w, nil)
+	})
+
+	mux.HandleFunc("/VolumeDriver.Capabilities", func(w http.ResponseWriter, r *http.Request) {
+		s.ec.caps++
+
+		_, err := read(r.Body)
+		if err != nil {
+			send(w, err)
+			return
+		}
+
+		send(w, `{"Capabilities": { "Scope": "global" }}`)
+	})
+
+	err := os.MkdirAll("/etc/docker/plugins", 0755)
+	assert.NilError(c, err)
+
+	err = os.WriteFile("/etc/docker/plugins/"+name+".spec", []byte(s.Server.URL), 0644)
+	assert.NilError(c, err)
+	return s
+}
+
+func (s *DockerExternalVolumeSuite) TearDownSuite(c *testing.T) {
+	s.volumePlugin.Close()
+
+	err := os.RemoveAll("/etc/docker/plugins")
+	assert.NilError(c, err)
+}
+
+func (s *DockerExternalVolumeSuite) TestVolumeCLICreateOptionConflict(c *testing.T) {
+	dockerCmd(c, "volume", "create", "test")
+
+	out, _, err := dockerCmdWithError("volume", "create", "test", "--driver", volumePluginName)
+	assert.Assert(c, err != nil, "volume create exception name already in use with another driver")
+	assert.Assert(c, strings.Contains(out, "must be unique"))
+	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Driver }}", "test")
+	_, _, err = dockerCmdWithError("volume", "create", "test", "--driver", strings.TrimSpace(out))
+	assert.NilError(c, err)
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverNamed(c *testing.T) {
+	s.d.StartWithBusybox(c)
+
+	out, err := s.d.Cmd("run", "--rm", "--name", "test-data", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", volumePluginName, "busybox:latest", "cat", "/tmp/external-volume-test/test")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, s.Server.URL))
+	_, err = s.d.Cmd("volume", "rm", "external-volume-test")
+	assert.NilError(c, err)
+
+	p := hostVolumePath("external-volume-test")
+	_, err = os.Lstat(p)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, os.IsNotExist(err), "Expected volume path in host to not exist: %s, %v\n", p, err)
+
+	assert.Equal(c, s.ec.activations, 1)
+	assert.Equal(c, s.ec.creations, 1)
+	assert.Equal(c, s.ec.removals, 1)
+	assert.Equal(c, s.ec.mounts, 1)
+	assert.Equal(c, s.ec.unmounts, 1)
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnnamed(c *testing.T) {
+	s.d.StartWithBusybox(c)
+
+	out, err := s.d.Cmd("run", "--rm", "--name", "test-data", "-v", "/tmp/external-volume-test", "--volume-driver", volumePluginName, "busybox:latest", "cat", "/tmp/external-volume-test/test")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, s.Server.URL))
+	assert.Equal(c, s.ec.activations, 1)
+	assert.Equal(c, s.ec.creations, 1)
+	assert.Equal(c, s.ec.removals, 1)
+	assert.Equal(c, s.ec.mounts, 1)
+	assert.Equal(c, s.ec.unmounts, 1)
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverVolumesFrom(c *testing.T) {
+	s.d.StartWithBusybox(c)
+
+	out, err := s.d.Cmd("run", "--name", "vol-test1", "-v", "/foo", "--volume-driver", volumePluginName, "busybox:latest")
+	assert.NilError(c, err, out)
+
+	out, err = s.d.Cmd("run", "--rm", "--volumes-from", "vol-test1", "--name", "vol-test2", "busybox", "ls", "/tmp")
+	assert.NilError(c, err, out)
+
+	out, err = s.d.Cmd("rm", "-fv", "vol-test1")
+	assert.NilError(c, err, out)
+
+	assert.Equal(c, s.ec.activations, 1)
+	assert.Equal(c, s.ec.creations, 1)
+	assert.Equal(c, s.ec.removals, 1)
+	assert.Equal(c, s.ec.mounts, 2)
+	assert.Equal(c, s.ec.unmounts, 2)
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverDeleteContainer(c *testing.T) {
+	s.d.StartWithBusybox(c)
+
+	out, err := s.d.Cmd("run", "--name", "vol-test1", "-v", "/foo", "--volume-driver", volumePluginName, "busybox:latest")
+	assert.NilError(c, err, out)
+
+	out, err = s.d.Cmd("rm", "-fv", "vol-test1")
+	assert.NilError(c, err, out)
+
+	assert.Equal(c, s.ec.activations, 1)
+	assert.Equal(c, s.ec.creations, 1)
+	assert.Equal(c, s.ec.removals, 1)
+	assert.Equal(c, s.ec.mounts, 1)
+	assert.Equal(c, s.ec.unmounts, 1)
+}
+
+func hostVolumePath(name string) string {
+	return fmt.Sprintf("/var/lib/docker/volumes/%s", name)
+}
+
+// Make sure a request to use a down driver doesn't block other requests
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverLookupNotBlocked(c *testing.T) {
+	specPath := "/etc/docker/plugins/down-driver.spec"
+	err := os.WriteFile(specPath, []byte("tcp://127.0.0.7:9999"), 0644)
+	assert.NilError(c, err)
+	defer os.RemoveAll(specPath)
+
+	chCmd1 := make(chan struct{})
+	chCmd2 := make(chan error, 1)
+	cmd1 := exec.Command(dockerBinary, "volume", "create", "-d", "down-driver")
+	cmd2 := exec.Command(dockerBinary, "volume", "create")
+
+	assert.Assert(c, cmd1.Start() == nil)
+	defer cmd1.Process.Kill()
+	time.Sleep(100 * time.Millisecond) // ensure API has been called
+	assert.Assert(c, cmd2.Start() == nil)
+
+	go func() {
+		cmd1.Wait()
+		close(chCmd1)
+	}()
+	go func() {
+		chCmd2 <- cmd2.Wait()
+	}()
+
+	select {
+	case <-chCmd1:
+		cmd2.Process.Kill()
+		c.Fatalf("volume create with down driver finished unexpectedly")
+	case err := <-chCmd2:
+		assert.NilError(c, err)
+	case <-time.After(5 * time.Second):
+		cmd2.Process.Kill()
+		c.Fatal("volume creates are blocked by previous create requests when previous driver is down")
+	}
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverRetryNotImmediatelyExists(c *testing.T) {
+	s.d.StartWithBusybox(c)
+	driverName := "test-external-volume-driver-retry"
+
+	errchan := make(chan error, 1)
+	started := make(chan struct{})
+	go func() {
+		close(started)
+		if out, err := s.d.Cmd("run", "--rm", "--name", "test-data-retry", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", driverName, "busybox:latest"); err != nil {
+			errchan <- fmt.Errorf("%v:\n%s", err, out)
+		}
+		close(errchan)
+	}()
+
+	<-started
+	// wait for a retry to occur, then create spec to allow plugin to register
+	time.Sleep(2 * time.Second)
+	p := newVolumePlugin(c, driverName)
+	defer p.Close()
+
+	select {
+	case err := <-errchan:
+		assert.NilError(c, err)
+	case <-time.After(8 * time.Second):
+		c.Fatal("volume creates fail when plugin not immediately available")
+	}
+
+	_, err := s.d.Cmd("volume", "rm", "external-volume-test")
+	assert.NilError(c, err)
+
+	assert.Equal(c, p.ec.activations, 1)
+	assert.Equal(c, p.ec.creations, 1)
+	assert.Equal(c, p.ec.removals, 1)
+	assert.Equal(c, p.ec.mounts, 1)
+	assert.Equal(c, p.ec.unmounts, 1)
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverBindExternalVolume(c *testing.T) {
+	dockerCmd(c, "volume", "create", "-d", volumePluginName, "foo")
+	dockerCmd(c, "run", "-d", "--name", "testing", "-v", "foo:/bar", "busybox", "top")
+
+	var mounts []struct {
+		Name   string
+		Driver string
+	}
+	out := inspectFieldJSON(c, "testing", "Mounts")
+	assert.Assert(c, json.NewDecoder(strings.NewReader(out)).Decode(&mounts) == nil)
+	assert.Equal(c, len(mounts), 1, out)
+	assert.Equal(c, mounts[0].Name, "foo")
+	assert.Equal(c, mounts[0].Driver, volumePluginName)
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverList(c *testing.T) {
+	dockerCmd(c, "volume", "create", "-d", volumePluginName, "abc3")
+	out, _ := dockerCmd(c, "volume", "ls")
+	ls := strings.Split(strings.TrimSpace(out), "\n")
+	assert.Equal(c, len(ls), 2, fmt.Sprintf("\n%s", out))
+
+	vol := strings.Fields(ls[len(ls)-1])
+	assert.Equal(c, len(vol), 2, fmt.Sprintf("%v", vol))
+	assert.Equal(c, vol[0], volumePluginName)
+	assert.Equal(c, vol[1], "abc3")
+
+	assert.Equal(c, s.ec.lists, 1)
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverGet(c *testing.T) {
+	out, _, err := dockerCmdWithError("volume", "inspect", "dummy")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "No such volume"))
+	assert.Equal(c, s.ec.gets, 1)
+
+	dockerCmd(c, "volume", "create", "test", "-d", volumePluginName)
+	out, _ = dockerCmd(c, "volume", "inspect", "test")
+
+	type vol struct {
+		Status map[string]string
+	}
+	var st []vol
+
+	assert.Assert(c, json.Unmarshal([]byte(out), &st) == nil)
+	assert.Equal(c, len(st), 1)
+	assert.Equal(c, len(st[0].Status), 1, fmt.Sprintf("%v", st[0]))
+	assert.Equal(c, st[0].Status["Hello"], "world", fmt.Sprintf("%v", st[0].Status))
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverWithDaemonRestart(c *testing.T) {
+	dockerCmd(c, "volume", "create", "-d", volumePluginName, "abc1")
+	s.d.Restart(c)
+
+	dockerCmd(c, "run", "--name=test", "-v", "abc1:/foo", "busybox", "true")
+	var mounts []types.MountPoint
+	inspectFieldAndUnmarshall(c, "test", "Mounts", &mounts)
+	assert.Equal(c, len(mounts), 1)
+	assert.Equal(c, mounts[0].Driver, volumePluginName)
+}
+
+// Ensures that the daemon handles when the plugin responds to a `Get` request with a null volume and a null error.
+// Prior the daemon would panic in this scenario.
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverGetEmptyResponse(c *testing.T) {
+	s.d.Start(c)
+
+	out, err := s.d.Cmd("volume", "create", "-d", volumePluginName, "abc2", "--opt", "ninja=1")
+	assert.NilError(c, err, out)
+
+	out, err = s.d.Cmd("volume", "inspect", "abc2")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "No such volume"))
+}
+
+// Ensure only cached paths are used in volume list to prevent N+1 calls to `VolumeDriver.Path`
+//
+// TODO(@cpuguy83): This test is testing internal implementation. In all the cases here, there may not even be a path
+// 	available because the volume is not even mounted. Consider removing this test.
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverPathCalls(c *testing.T) {
+	s.d.Start(c)
+	assert.Equal(c, s.ec.paths, 0)
+
+	out, err := s.d.Cmd("volume", "create", "test", "--driver=test-external-volume-driver")
+	assert.NilError(c, err, out)
+	assert.Equal(c, s.ec.paths, 0)
+
+	out, err = s.d.Cmd("volume", "ls")
+	assert.NilError(c, err, out)
+	assert.Equal(c, s.ec.paths, 0)
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverMountID(c *testing.T) {
+	s.d.StartWithBusybox(c)
+
+	out, err := s.d.Cmd("run", "--rm", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", volumePluginName, "busybox:latest", "cat", "/tmp/external-volume-test/test")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
+}
+
+// Check that VolumeDriver.Capabilities gets called, and only called once
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverCapabilities(c *testing.T) {
+	s.d.Start(c)
+	assert.Equal(c, s.ec.caps, 0)
+
+	for i := 0; i < 3; i++ {
+		out, err := s.d.Cmd("volume", "create", "-d", volumePluginName, fmt.Sprintf("test%d", i))
+		assert.NilError(c, err, out)
+		assert.Equal(c, s.ec.caps, 1)
+		out, err = s.d.Cmd("volume", "inspect", "--format={{.Scope}}", fmt.Sprintf("test%d", i))
+		assert.NilError(c, err)
+		assert.Equal(c, strings.TrimSpace(out), volume.GlobalScope)
+	}
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverOutOfBandDelete(c *testing.T) {
+	driverName := stringid.GenerateRandomID()
+	p := newVolumePlugin(c, driverName)
+	defer p.Close()
+
+	s.d.StartWithBusybox(c)
+
+	out, err := s.d.Cmd("volume", "create", "-d", driverName, "--name", "test")
+	assert.NilError(c, err, out)
+
+	out, err = s.d.Cmd("volume", "create", "-d", "local", "--name", "test")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "must be unique"))
+	// simulate out of band volume deletion on plugin level
+	delete(p.vols, "test")
+
+	// test re-create with same driver
+	out, err = s.d.Cmd("volume", "create", "-d", driverName, "--opt", "foo=bar", "--name", "test")
+	assert.NilError(c, err, out)
+	out, err = s.d.Cmd("volume", "inspect", "test")
+	assert.NilError(c, err, out)
+
+	var vs []types.Volume
+	err = json.Unmarshal([]byte(out), &vs)
+	assert.NilError(c, err)
+	assert.Equal(c, len(vs), 1)
+	assert.Equal(c, vs[0].Driver, driverName)
+	assert.Assert(c, vs[0].Options != nil)
+	assert.Equal(c, vs[0].Options["foo"], "bar")
+	assert.Equal(c, vs[0].Driver, driverName)
+
+	// simulate out of band volume deletion on plugin level
+	delete(p.vols, "test")
+
+	// test create with different driver
+	out, err = s.d.Cmd("volume", "create", "-d", "local", "--name", "test")
+	assert.NilError(c, err, out)
+
+	out, err = s.d.Cmd("volume", "inspect", "test")
+	assert.NilError(c, err, out)
+	vs = nil
+	err = json.Unmarshal([]byte(out), &vs)
+	assert.NilError(c, err)
+	assert.Equal(c, len(vs), 1)
+	assert.Equal(c, len(vs[0].Options), 0)
+	assert.Equal(c, vs[0].Driver, "local")
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnmountOnMountFail(c *testing.T) {
+	s.d.StartWithBusybox(c)
+	s.d.Cmd("volume", "create", "-d", "test-external-volume-driver", "--opt=invalidOption=1", "--name=testumount")
+
+	out, _ := s.d.Cmd("run", "-v", "testumount:/foo", "busybox", "true")
+	assert.Equal(c, s.ec.unmounts, 0, out)
+	out, _ = s.d.Cmd("run", "-w", "/foo", "-v", "testumount:/foo", "busybox", "true")
+	assert.Equal(c, s.ec.unmounts, 0, out)
+}
+
+func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnmountOnCp(c *testing.T) {
+	s.d.StartWithBusybox(c)
+	s.d.Cmd("volume", "create", "-d", "test-external-volume-driver", "--name=test")
+
+	out, _ := s.d.Cmd("run", "-d", "--name=test", "-v", "test:/foo", "busybox", "/bin/sh", "-c", "touch /test && top")
+	assert.Equal(c, s.ec.mounts, 1, out)
+
+	out, _ = s.d.Cmd("cp", "test:/test", "/tmp/test")
+	assert.Equal(c, s.ec.mounts, 2, out)
+	assert.Equal(c, s.ec.unmounts, 1, out)
+
+	out, _ = s.d.Cmd("kill", "test")
+	assert.Equal(c, s.ec.unmounts, 2, out)
+}
diff --git a/integration-cli/docker_cli_external_volume_driver_unix_test.go b/integration-cli/docker_cli_external_volume_driver_unix_test.go
deleted file mode 100644
index da8bb7e0115f0..0000000000000
--- a/integration-cli/docker_cli_external_volume_driver_unix_test.go
+++ /dev/null
@@ -1,631 +0,0 @@
-// +build !windows
-
-package main
-
-import (
-	"encoding/json"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"net/http/httptest"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"strings"
-	"time"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/integration-cli/daemon"
-	testdaemon "github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/docker/volume"
-	"github.com/go-check/check"
-)
-
-const volumePluginName = "test-external-volume-driver"
-
-func init() {
-	check.Suite(&DockerExternalVolumeSuite{
-		ds: &DockerSuite{},
-	})
-}
-
-type eventCounter struct {
-	activations int
-	creations   int
-	removals    int
-	mounts      int
-	unmounts    int
-	paths       int
-	lists       int
-	gets        int
-	caps        int
-}
-
-type DockerExternalVolumeSuite struct {
-	ds *DockerSuite
-	d  *daemon.Daemon
-	*volumePlugin
-}
-
-func (s *DockerExternalVolumeSuite) SetUpTest(c *check.C) {
-	testRequires(c, SameHostDaemon)
-	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
-	s.ec = &eventCounter{}
-}
-
-func (s *DockerExternalVolumeSuite) TearDownTest(c *check.C) {
-	if s.d != nil {
-		s.d.Stop(c)
-		s.ds.TearDownTest(c)
-	}
-}
-
-func (s *DockerExternalVolumeSuite) SetUpSuite(c *check.C) {
-	s.volumePlugin = newVolumePlugin(c, volumePluginName)
-}
-
-type volumePlugin struct {
-	ec *eventCounter
-	*httptest.Server
-	vols map[string]vol
-}
-
-type vol struct {
-	Name       string
-	Mountpoint string
-	Ninja      bool // hack used to trigger a null volume return on `Get`
-	Status     map[string]interface{}
-	Options    map[string]string
-}
-
-func (p *volumePlugin) Close() {
-	p.Server.Close()
-}
-
-func newVolumePlugin(c *check.C, name string) *volumePlugin {
-	mux := http.NewServeMux()
-	s := &volumePlugin{Server: httptest.NewServer(mux), ec: &eventCounter{}, vols: make(map[string]vol)}
-
-	type pluginRequest struct {
-		Name string
-		Opts map[string]string
-		ID   string
-	}
-
-	type pluginResp struct {
-		Mountpoint string `json:",omitempty"`
-		Err        string `json:",omitempty"`
-	}
-
-	read := func(b io.ReadCloser) (pluginRequest, error) {
-		defer b.Close()
-		var pr pluginRequest
-		err := json.NewDecoder(b).Decode(&pr)
-		return pr, err
-	}
-
-	send := func(w http.ResponseWriter, data interface{}) {
-		switch t := data.(type) {
-		case error:
-			http.Error(w, t.Error(), 500)
-		case string:
-			w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
-			fmt.Fprintln(w, t)
-		default:
-			w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
-			json.NewEncoder(w).Encode(&data)
-		}
-	}
-
-	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
-		s.ec.activations++
-		send(w, `{"Implements": ["VolumeDriver"]}`)
-	})
-
-	mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) {
-		s.ec.creations++
-		pr, err := read(r.Body)
-		if err != nil {
-			send(w, err)
-			return
-		}
-		_, isNinja := pr.Opts["ninja"]
-		status := map[string]interface{}{"Hello": "world"}
-		s.vols[pr.Name] = vol{Name: pr.Name, Ninja: isNinja, Status: status, Options: pr.Opts}
-		send(w, nil)
-	})
-
-	mux.HandleFunc("/VolumeDriver.List", func(w http.ResponseWriter, r *http.Request) {
-		s.ec.lists++
-		vols := make([]vol, 0, len(s.vols))
-		for _, v := range s.vols {
-			if v.Ninja {
-				continue
-			}
-			vols = append(vols, v)
-		}
-		send(w, map[string][]vol{"Volumes": vols})
-	})
-
-	mux.HandleFunc("/VolumeDriver.Get", func(w http.ResponseWriter, r *http.Request) {
-		s.ec.gets++
-		pr, err := read(r.Body)
-		if err != nil {
-			send(w, err)
-			return
-		}
-
-		v, exists := s.vols[pr.Name]
-		if !exists {
-			send(w, `{"Err": "no such volume"}`)
-		}
-
-		if v.Ninja {
-			send(w, map[string]vol{})
-			return
-		}
-
-		v.Mountpoint = hostVolumePath(pr.Name)
-		send(w, map[string]vol{"Volume": v})
-		return
-	})
-
-	mux.HandleFunc("/VolumeDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
-		s.ec.removals++
-		pr, err := read(r.Body)
-		if err != nil {
-			send(w, err)
-			return
-		}
-
-		v, ok := s.vols[pr.Name]
-		if !ok {
-			send(w, nil)
-			return
-		}
-
-		if err := os.RemoveAll(hostVolumePath(v.Name)); err != nil {
-			send(w, &pluginResp{Err: err.Error()})
-			return
-		}
-		delete(s.vols, v.Name)
-		send(w, nil)
-	})
-
-	mux.HandleFunc("/VolumeDriver.Path", func(w http.ResponseWriter, r *http.Request) {
-		s.ec.paths++
-
-		pr, err := read(r.Body)
-		if err != nil {
-			send(w, err)
-			return
-		}
-		p := hostVolumePath(pr.Name)
-		send(w, &pluginResp{Mountpoint: p})
-	})
-
-	mux.HandleFunc("/VolumeDriver.Mount", func(w http.ResponseWriter, r *http.Request) {
-		s.ec.mounts++
-
-		pr, err := read(r.Body)
-		if err != nil {
-			send(w, err)
-			return
-		}
-
-		if v, exists := s.vols[pr.Name]; exists {
-			// Use this to simulate a mount failure
-			if _, exists := v.Options["invalidOption"]; exists {
-				send(w, fmt.Errorf("invalid argument"))
-				return
-			}
-		}
-
-		p := hostVolumePath(pr.Name)
-		if err := os.MkdirAll(p, 0755); err != nil {
-			send(w, &pluginResp{Err: err.Error()})
-			return
-		}
-
-		if err := ioutil.WriteFile(filepath.Join(p, "test"), []byte(s.Server.URL), 0644); err != nil {
-			send(w, err)
-			return
-		}
-
-		if err := ioutil.WriteFile(filepath.Join(p, "mountID"), []byte(pr.ID), 0644); err != nil {
-			send(w, err)
-			return
-		}
-
-		send(w, &pluginResp{Mountpoint: p})
-	})
-
-	mux.HandleFunc("/VolumeDriver.Unmount", func(w http.ResponseWriter, r *http.Request) {
-		s.ec.unmounts++
-
-		_, err := read(r.Body)
-		if err != nil {
-			send(w, err)
-			return
-		}
-
-		send(w, nil)
-	})
-
-	mux.HandleFunc("/VolumeDriver.Capabilities", func(w http.ResponseWriter, r *http.Request) {
-		s.ec.caps++
-
-		_, err := read(r.Body)
-		if err != nil {
-			send(w, err)
-			return
-		}
-
-		send(w, `{"Capabilities": { "Scope": "global" }}`)
-	})
-
-	err := os.MkdirAll("/etc/docker/plugins", 0755)
-	c.Assert(err, checker.IsNil)
-
-	err = ioutil.WriteFile("/etc/docker/plugins/"+name+".spec", []byte(s.Server.URL), 0644)
-	c.Assert(err, checker.IsNil)
-	return s
-}
-
-func (s *DockerExternalVolumeSuite) TearDownSuite(c *check.C) {
-	s.volumePlugin.Close()
-
-	err := os.RemoveAll("/etc/docker/plugins")
-	c.Assert(err, checker.IsNil)
-}
-
-func (s *DockerExternalVolumeSuite) TestVolumeCLICreateOptionConflict(c *check.C) {
-	dockerCmd(c, "volume", "create", "test")
-
-	out, _, err := dockerCmdWithError("volume", "create", "test", "--driver", volumePluginName)
-	c.Assert(err, check.NotNil, check.Commentf("volume create exception name already in use with another driver"))
-	c.Assert(out, checker.Contains, "must be unique")
-
-	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Driver }}", "test")
-	_, _, err = dockerCmdWithError("volume", "create", "test", "--driver", strings.TrimSpace(out))
-	c.Assert(err, check.IsNil)
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverNamed(c *check.C) {
-	s.d.StartWithBusybox(c)
-
-	out, err := s.d.Cmd("run", "--rm", "--name", "test-data", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", volumePluginName, "busybox:latest", "cat", "/tmp/external-volume-test/test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, s.Server.URL)
-
-	_, err = s.d.Cmd("volume", "rm", "external-volume-test")
-	c.Assert(err, checker.IsNil)
-
-	p := hostVolumePath("external-volume-test")
-	_, err = os.Lstat(p)
-	c.Assert(err, checker.NotNil)
-	c.Assert(os.IsNotExist(err), checker.True, check.Commentf("Expected volume path in host to not exist: %s, %v\n", p, err))
-
-	c.Assert(s.ec.activations, checker.Equals, 1)
-	c.Assert(s.ec.creations, checker.Equals, 1)
-	c.Assert(s.ec.removals, checker.Equals, 1)
-	c.Assert(s.ec.mounts, checker.Equals, 1)
-	c.Assert(s.ec.unmounts, checker.Equals, 1)
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnnamed(c *check.C) {
-	s.d.StartWithBusybox(c)
-
-	out, err := s.d.Cmd("run", "--rm", "--name", "test-data", "-v", "/tmp/external-volume-test", "--volume-driver", volumePluginName, "busybox:latest", "cat", "/tmp/external-volume-test/test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, s.Server.URL)
-
-	c.Assert(s.ec.activations, checker.Equals, 1)
-	c.Assert(s.ec.creations, checker.Equals, 1)
-	c.Assert(s.ec.removals, checker.Equals, 1)
-	c.Assert(s.ec.mounts, checker.Equals, 1)
-	c.Assert(s.ec.unmounts, checker.Equals, 1)
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverVolumesFrom(c *check.C) {
-	s.d.StartWithBusybox(c)
-
-	out, err := s.d.Cmd("run", "--name", "vol-test1", "-v", "/foo", "--volume-driver", volumePluginName, "busybox:latest")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	out, err = s.d.Cmd("run", "--rm", "--volumes-from", "vol-test1", "--name", "vol-test2", "busybox", "ls", "/tmp")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	out, err = s.d.Cmd("rm", "-fv", "vol-test1")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	c.Assert(s.ec.activations, checker.Equals, 1)
-	c.Assert(s.ec.creations, checker.Equals, 1)
-	c.Assert(s.ec.removals, checker.Equals, 1)
-	c.Assert(s.ec.mounts, checker.Equals, 2)
-	c.Assert(s.ec.unmounts, checker.Equals, 2)
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverDeleteContainer(c *check.C) {
-	s.d.StartWithBusybox(c)
-
-	out, err := s.d.Cmd("run", "--name", "vol-test1", "-v", "/foo", "--volume-driver", volumePluginName, "busybox:latest")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	out, err = s.d.Cmd("rm", "-fv", "vol-test1")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	c.Assert(s.ec.activations, checker.Equals, 1)
-	c.Assert(s.ec.creations, checker.Equals, 1)
-	c.Assert(s.ec.removals, checker.Equals, 1)
-	c.Assert(s.ec.mounts, checker.Equals, 1)
-	c.Assert(s.ec.unmounts, checker.Equals, 1)
-}
-
-func hostVolumePath(name string) string {
-	return fmt.Sprintf("/var/lib/docker/volumes/%s", name)
-}
-
-// Make sure a request to use a down driver doesn't block other requests
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverLookupNotBlocked(c *check.C) {
-	specPath := "/etc/docker/plugins/down-driver.spec"
-	err := ioutil.WriteFile(specPath, []byte("tcp://127.0.0.7:9999"), 0644)
-	c.Assert(err, check.IsNil)
-	defer os.RemoveAll(specPath)
-
-	chCmd1 := make(chan struct{})
-	chCmd2 := make(chan error)
-	cmd1 := exec.Command(dockerBinary, "volume", "create", "-d", "down-driver")
-	cmd2 := exec.Command(dockerBinary, "volume", "create")
-
-	c.Assert(cmd1.Start(), checker.IsNil)
-	defer cmd1.Process.Kill()
-	time.Sleep(100 * time.Millisecond) // ensure API has been called
-	c.Assert(cmd2.Start(), checker.IsNil)
-
-	go func() {
-		cmd1.Wait()
-		close(chCmd1)
-	}()
-	go func() {
-		chCmd2 <- cmd2.Wait()
-	}()
-
-	select {
-	case <-chCmd1:
-		cmd2.Process.Kill()
-		c.Fatalf("volume create with down driver finished unexpectedly")
-	case err := <-chCmd2:
-		c.Assert(err, checker.IsNil)
-	case <-time.After(5 * time.Second):
-		cmd2.Process.Kill()
-		c.Fatal("volume creates are blocked by previous create requests when previous driver is down")
-	}
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverRetryNotImmediatelyExists(c *check.C) {
-	s.d.StartWithBusybox(c)
-	driverName := "test-external-volume-driver-retry"
-
-	errchan := make(chan error)
-	started := make(chan struct{})
-	go func() {
-		close(started)
-		if out, err := s.d.Cmd("run", "--rm", "--name", "test-data-retry", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", driverName, "busybox:latest"); err != nil {
-			errchan <- fmt.Errorf("%v:\n%s", err, out)
-		}
-		close(errchan)
-	}()
-
-	<-started
-	// wait for a retry to occur, then create spec to allow plugin to register
-	time.Sleep(2 * time.Second)
-	p := newVolumePlugin(c, driverName)
-	defer p.Close()
-
-	select {
-	case err := <-errchan:
-		c.Assert(err, checker.IsNil)
-	case <-time.After(8 * time.Second):
-		c.Fatal("volume creates fail when plugin not immediately available")
-	}
-
-	_, err := s.d.Cmd("volume", "rm", "external-volume-test")
-	c.Assert(err, checker.IsNil)
-
-	c.Assert(p.ec.activations, checker.Equals, 1)
-	c.Assert(p.ec.creations, checker.Equals, 1)
-	c.Assert(p.ec.removals, checker.Equals, 1)
-	c.Assert(p.ec.mounts, checker.Equals, 1)
-	c.Assert(p.ec.unmounts, checker.Equals, 1)
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverBindExternalVolume(c *check.C) {
-	dockerCmd(c, "volume", "create", "-d", volumePluginName, "foo")
-	dockerCmd(c, "run", "-d", "--name", "testing", "-v", "foo:/bar", "busybox", "top")
-
-	var mounts []struct {
-		Name   string
-		Driver string
-	}
-	out := inspectFieldJSON(c, "testing", "Mounts")
-	c.Assert(json.NewDecoder(strings.NewReader(out)).Decode(&mounts), checker.IsNil)
-	c.Assert(len(mounts), checker.Equals, 1, check.Commentf("%s", out))
-	c.Assert(mounts[0].Name, checker.Equals, "foo")
-	c.Assert(mounts[0].Driver, checker.Equals, volumePluginName)
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverList(c *check.C) {
-	dockerCmd(c, "volume", "create", "-d", volumePluginName, "abc3")
-	out, _ := dockerCmd(c, "volume", "ls")
-	ls := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(ls), check.Equals, 2, check.Commentf("\n%s", out))
-
-	vol := strings.Fields(ls[len(ls)-1])
-	c.Assert(len(vol), check.Equals, 2, check.Commentf("%v", vol))
-	c.Assert(vol[0], check.Equals, volumePluginName)
-	c.Assert(vol[1], check.Equals, "abc3")
-
-	c.Assert(s.ec.lists, check.Equals, 1)
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverGet(c *check.C) {
-	out, _, err := dockerCmdWithError("volume", "inspect", "dummy")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "No such volume")
-	c.Assert(s.ec.gets, check.Equals, 1)
-
-	dockerCmd(c, "volume", "create", "test", "-d", volumePluginName)
-	out, _ = dockerCmd(c, "volume", "inspect", "test")
-
-	type vol struct {
-		Status map[string]string
-	}
-	var st []vol
-
-	c.Assert(json.Unmarshal([]byte(out), &st), checker.IsNil)
-	c.Assert(st, checker.HasLen, 1)
-	c.Assert(st[0].Status, checker.HasLen, 1, check.Commentf("%v", st[0]))
-	c.Assert(st[0].Status["Hello"], checker.Equals, "world", check.Commentf("%v", st[0].Status))
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverWithDaemonRestart(c *check.C) {
-	dockerCmd(c, "volume", "create", "-d", volumePluginName, "abc1")
-	s.d.Restart(c)
-
-	dockerCmd(c, "run", "--name=test", "-v", "abc1:/foo", "busybox", "true")
-	var mounts []types.MountPoint
-	inspectFieldAndUnmarshall(c, "test", "Mounts", &mounts)
-	c.Assert(mounts, checker.HasLen, 1)
-	c.Assert(mounts[0].Driver, checker.Equals, volumePluginName)
-}
-
-// Ensures that the daemon handles when the plugin responds to a `Get` request with a null volume and a null error.
-// Prior the daemon would panic in this scenario.
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverGetEmptyResponse(c *check.C) {
-	s.d.Start(c)
-
-	out, err := s.d.Cmd("volume", "create", "-d", volumePluginName, "abc2", "--opt", "ninja=1")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	out, err = s.d.Cmd("volume", "inspect", "abc2")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "No such volume")
-}
-
-// Ensure only cached paths are used in volume list to prevent N+1 calls to `VolumeDriver.Path`
-//
-// TODO(@cpuguy83): This test is testing internal implementation. In all the cases here, there may not even be a path
-// 	available because the volume is not even mounted. Consider removing this test.
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverPathCalls(c *check.C) {
-	s.d.Start(c)
-	c.Assert(s.ec.paths, checker.Equals, 0)
-
-	out, err := s.d.Cmd("volume", "create", "test", "--driver=test-external-volume-driver")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(s.ec.paths, checker.Equals, 0)
-
-	out, err = s.d.Cmd("volume", "ls")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(s.ec.paths, checker.Equals, 0)
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverMountID(c *check.C) {
-	s.d.StartWithBusybox(c)
-
-	out, err := s.d.Cmd("run", "--rm", "-v", "external-volume-test:/tmp/external-volume-test", "--volume-driver", volumePluginName, "busybox:latest", "cat", "/tmp/external-volume-test/test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
-}
-
-// Check that VolumeDriver.Capabilities gets called, and only called once
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverCapabilities(c *check.C) {
-	s.d.Start(c)
-	c.Assert(s.ec.caps, checker.Equals, 0)
-
-	for i := 0; i < 3; i++ {
-		out, err := s.d.Cmd("volume", "create", "-d", volumePluginName, fmt.Sprintf("test%d", i))
-		c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-		c.Assert(s.ec.caps, checker.Equals, 1)
-		out, err = s.d.Cmd("volume", "inspect", "--format={{.Scope}}", fmt.Sprintf("test%d", i))
-		c.Assert(err, checker.IsNil)
-		c.Assert(strings.TrimSpace(out), checker.Equals, volume.GlobalScope)
-	}
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverOutOfBandDelete(c *check.C) {
-	driverName := stringid.GenerateNonCryptoID()
-	p := newVolumePlugin(c, driverName)
-	defer p.Close()
-
-	s.d.StartWithBusybox(c)
-
-	out, err := s.d.Cmd("volume", "create", "-d", driverName, "--name", "test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	out, err = s.d.Cmd("volume", "create", "-d", "local", "--name", "test")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "must be unique")
-
-	// simulate out of band volume deletion on plugin level
-	delete(p.vols, "test")
-
-	// test re-create with same driver
-	out, err = s.d.Cmd("volume", "create", "-d", driverName, "--opt", "foo=bar", "--name", "test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	out, err = s.d.Cmd("volume", "inspect", "test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	var vs []types.Volume
-	err = json.Unmarshal([]byte(out), &vs)
-	c.Assert(err, checker.IsNil)
-	c.Assert(vs, checker.HasLen, 1)
-	c.Assert(vs[0].Driver, checker.Equals, driverName)
-	c.Assert(vs[0].Options, checker.NotNil)
-	c.Assert(vs[0].Options["foo"], checker.Equals, "bar")
-	c.Assert(vs[0].Driver, checker.Equals, driverName)
-
-	// simulate out of band volume deletion on plugin level
-	delete(p.vols, "test")
-
-	// test create with different driver
-	out, err = s.d.Cmd("volume", "create", "-d", "local", "--name", "test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	out, err = s.d.Cmd("volume", "inspect", "test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	vs = nil
-	err = json.Unmarshal([]byte(out), &vs)
-	c.Assert(err, checker.IsNil)
-	c.Assert(vs, checker.HasLen, 1)
-	c.Assert(vs[0].Options, checker.HasLen, 0)
-	c.Assert(vs[0].Driver, checker.Equals, "local")
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnmountOnMountFail(c *check.C) {
-	s.d.StartWithBusybox(c)
-	s.d.Cmd("volume", "create", "-d", "test-external-volume-driver", "--opt=invalidOption=1", "--name=testumount")
-
-	out, _ := s.d.Cmd("run", "-v", "testumount:/foo", "busybox", "true")
-	c.Assert(s.ec.unmounts, checker.Equals, 0, check.Commentf("%s", out))
-	out, _ = s.d.Cmd("run", "-w", "/foo", "-v", "testumount:/foo", "busybox", "true")
-	c.Assert(s.ec.unmounts, checker.Equals, 0, check.Commentf("%s", out))
-}
-
-func (s *DockerExternalVolumeSuite) TestExternalVolumeDriverUnmountOnCp(c *check.C) {
-	s.d.StartWithBusybox(c)
-	s.d.Cmd("volume", "create", "-d", "test-external-volume-driver", "--name=test")
-
-	out, _ := s.d.Cmd("run", "-d", "--name=test", "-v", "test:/foo", "busybox", "/bin/sh", "-c", "touch /test && top")
-	c.Assert(s.ec.mounts, checker.Equals, 1, check.Commentf("%s", out))
-
-	out, _ = s.d.Cmd("cp", "test:/test", "/tmp/test")
-	c.Assert(s.ec.mounts, checker.Equals, 2, check.Commentf("%s", out))
-	c.Assert(s.ec.unmounts, checker.Equals, 1, check.Commentf("%s", out))
-
-	out, _ = s.d.Cmd("kill", "test")
-	c.Assert(s.ec.unmounts, checker.Equals, 2, check.Commentf("%s", out))
-}
diff --git a/integration-cli/docker_cli_health_test.go b/integration-cli/docker_cli_health_test.go
index 4fb63994bb699..5278e18d05f2c 100644
--- a/integration-cli/docker_cli_health_test.go
+++ b/integration-cli/docker_cli_health_test.go
@@ -4,15 +4,15 @@ import (
 	"encoding/json"
 	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
-func waitForHealthStatus(c *check.C, name string, prev string, expected string) {
+func waitForHealthStatus(c *testing.T, name string, prev string, expected string) {
 	prev = prev + "\n"
 	expected = expected + "\n"
 	for {
@@ -20,7 +20,7 @@ func waitForHealthStatus(c *check.C, name string, prev string, expected string)
 		if out == expected {
 			return
 		}
-		c.Check(out, checker.Equals, prev)
+		assert.Equal(c, out, prev)
 		if out != prev {
 			return
 		}
@@ -28,15 +28,15 @@ func waitForHealthStatus(c *check.C, name string, prev string, expected string)
 	}
 }
 
-func getHealth(c *check.C, name string) *types.Health {
+func getHealth(c *testing.T, name string) *types.Health {
 	out, _ := dockerCmd(c, "inspect", "--format={{json .State.Health}}", name)
 	var health types.Health
 	err := json.Unmarshal([]byte(out), &health)
-	c.Check(err, checker.Equals, nil)
+	assert.Equal(c, err, nil)
 	return &health
 }
 
-func (s *DockerSuite) TestHealth(c *check.C) {
+func (s *DockerSuite) TestHealth(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows
 
 	existingContainers := ExistingContainerIDs(c)
@@ -54,12 +54,12 @@ func (s *DockerSuite) TestHealth(c *check.C) {
 	cid, _ := dockerCmd(c, "create", "--name", name, imageName)
 	out, _ := dockerCmd(c, "ps", "-a", "--format={{.ID}} {{.Status}}")
 	out = RemoveOutputForExistingElements(out, existingContainers)
-	c.Check(out, checker.Equals, cid[:12]+" Created\n")
+	assert.Equal(c, out, cid[:12]+" Created\n")
 
 	// Inspect the options
 	out, _ = dockerCmd(c, "inspect",
 		"--format=timeout={{.Config.Healthcheck.Timeout}} interval={{.Config.Healthcheck.Interval}} retries={{.Config.Healthcheck.Retries}} test={{.Config.Healthcheck.Test}}", name)
-	c.Check(out, checker.Equals, "timeout=30s interval=1s retries=0 test=[CMD-SHELL cat /status]\n")
+	assert.Equal(c, out, "timeout=30s interval=1s retries=0 test=[CMD-SHELL cat /status]\n")
 
 	// Start
 	dockerCmd(c, "start", name)
@@ -71,7 +71,7 @@ func (s *DockerSuite) TestHealth(c *check.C) {
 
 	// Inspect the status
 	out, _ = dockerCmd(c, "inspect", "--format={{.State.Health.Status}}", name)
-	c.Check(out, checker.Equals, "unhealthy\n")
+	assert.Equal(c, out, "unhealthy\n")
 
 	// Make it healthy again
 	dockerCmd(c, "exec", name, "touch", "/status")
@@ -83,7 +83,7 @@ func (s *DockerSuite) TestHealth(c *check.C) {
 	// Disable the check from the CLI
 	dockerCmd(c, "create", "--name=noh", "--no-healthcheck", imageName)
 	out, _ = dockerCmd(c, "inspect", "--format={{.Config.Healthcheck.Test}}", "noh")
-	c.Check(out, checker.Equals, "[NONE]\n")
+	assert.Equal(c, out, "[NONE]\n")
 	dockerCmd(c, "rm", "noh")
 
 	// Disable the check with a new build
@@ -91,7 +91,7 @@ func (s *DockerSuite) TestHealth(c *check.C) {
 		HEALTHCHECK NONE`))
 
 	out, _ = dockerCmd(c, "inspect", "--format={{.Config.Healthcheck.Test}}", "no_healthcheck")
-	c.Check(out, checker.Equals, "[NONE]\n")
+	assert.Equal(c, out, "[NONE]\n")
 
 	// Enable the checks from the CLI
 	_, _ = dockerCmd(c, "run", "-d", "--name=fatal_healthcheck",
@@ -101,11 +101,11 @@ func (s *DockerSuite) TestHealth(c *check.C) {
 		"no_healthcheck")
 	waitForHealthStatus(c, "fatal_healthcheck", "starting", "healthy")
 	health := getHealth(c, "fatal_healthcheck")
-	c.Check(health.Status, checker.Equals, "healthy")
-	c.Check(health.FailingStreak, checker.Equals, 0)
+	assert.Equal(c, health.Status, "healthy")
+	assert.Equal(c, health.FailingStreak, 0)
 	last := health.Log[len(health.Log)-1]
-	c.Check(last.ExitCode, checker.Equals, 0)
-	c.Check(last.Output, checker.Equals, "OK\n")
+	assert.Equal(c, last.ExitCode, 0)
+	assert.Equal(c, last.Output, "OK\n")
 
 	// Fail the check
 	dockerCmd(c, "exec", "fatal_healthcheck", "rm", "/status")
@@ -113,8 +113,8 @@ func (s *DockerSuite) TestHealth(c *check.C) {
 
 	failsStr, _ := dockerCmd(c, "inspect", "--format={{.State.Health.FailingStreak}}", "fatal_healthcheck")
 	fails, err := strconv.Atoi(strings.TrimSpace(failsStr))
-	c.Check(err, check.IsNil)
-	c.Check(fails >= 3, checker.Equals, true)
+	assert.Assert(c, err == nil)
+	assert.Equal(c, fails >= 3, true)
 	dockerCmd(c, "rm", "-f", "fatal_healthcheck")
 
 	// Check timeout
@@ -125,9 +125,9 @@ func (s *DockerSuite) TestHealth(c *check.C) {
 	waitForHealthStatus(c, "test", "starting", "unhealthy")
 	health = getHealth(c, "test")
 	last = health.Log[len(health.Log)-1]
-	c.Check(health.Status, checker.Equals, "unhealthy")
-	c.Check(last.ExitCode, checker.Equals, -1)
-	c.Check(last.Output, checker.Equals, "Health check exceeded timeout (1s)")
+	assert.Equal(c, health.Status, "unhealthy")
+	assert.Equal(c, last.ExitCode, -1)
+	assert.Equal(c, last.Output, "Health check exceeded timeout (1s)")
 	dockerCmd(c, "rm", "-f", "test")
 
 	// Check JSON-format
@@ -139,12 +139,12 @@ func (s *DockerSuite) TestHealth(c *check.C) {
 		  CMD ["cat", "/my status"]`))
 	out, _ = dockerCmd(c, "inspect",
 		"--format={{.Config.Healthcheck.Test}}", imageName)
-	c.Check(out, checker.Equals, "[CMD cat /my status]\n")
+	assert.Equal(c, out, "[CMD cat /my status]\n")
 
 }
 
 // GitHub #33021
-func (s *DockerSuite) TestUnsetEnvVarHealthCheck(c *check.C) {
+func (s *DockerSuite) TestUnsetEnvVarHealthCheck(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows
 
 	imageName := "testhealth"
diff --git a/integration-cli/docker_cli_history_test.go b/integration-cli/docker_cli_history_test.go
index 43c4b94334670..8ae1d275ab24f 100644
--- a/integration-cli/docker_cli_history_test.go
+++ b/integration-cli/docker_cli_history_test.go
@@ -5,15 +5,16 @@ import (
 	"regexp"
 	"strconv"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/assert/cmp"
 )
 
 // This is a heisen-test.  Because the created timestamp of images and the behavior of
 // sort is not predictable it doesn't always fail.
-func (s *DockerSuite) TestBuildHistory(c *check.C) {
+func (s *DockerSuite) TestBuildHistory(c *testing.T) {
 	name := "testbuildhistory"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM `+minimalBaseImage()+`
 LABEL label.A="A"
@@ -50,21 +51,21 @@ LABEL label.Z="Z"`))
 	for i := 0; i < 26; i++ {
 		echoValue := fmt.Sprintf("LABEL label.%s=%s", expectedValues[i], expectedValues[i])
 		actualValue := actualValues[i]
-		c.Assert(actualValue, checker.Contains, echoValue)
+		assert.Assert(c, strings.Contains(actualValue, echoValue))
 	}
 
 }
 
-func (s *DockerSuite) TestHistoryExistentImage(c *check.C) {
+func (s *DockerSuite) TestHistoryExistentImage(c *testing.T) {
 	dockerCmd(c, "history", "busybox")
 }
 
-func (s *DockerSuite) TestHistoryNonExistentImage(c *check.C) {
+func (s *DockerSuite) TestHistoryNonExistentImage(c *testing.T) {
 	_, _, err := dockerCmdWithError("history", "testHistoryNonExistentImage")
-	c.Assert(err, checker.NotNil, check.Commentf("history on a non-existent image should fail."))
+	assert.Assert(c, err != nil, "history on a non-existent image should fail.")
 }
 
-func (s *DockerSuite) TestHistoryImageWithComment(c *check.C) {
+func (s *DockerSuite) TestHistoryImageWithComment(c *testing.T) {
 	name := "testhistoryimagewithcomment"
 
 	// make an image through docker commit  [ -m messages ]
@@ -80,10 +81,10 @@ func (s *DockerSuite) TestHistoryImageWithComment(c *check.C) {
 	out, _ := dockerCmd(c, "history", name)
 	outputTabs := strings.Fields(strings.Split(out, "\n")[1])
 	actualValue := outputTabs[len(outputTabs)-1]
-	c.Assert(actualValue, checker.Contains, comment)
+	assert.Assert(c, strings.Contains(actualValue, comment))
 }
 
-func (s *DockerSuite) TestHistoryHumanOptionFalse(c *check.C) {
+func (s *DockerSuite) TestHistoryHumanOptionFalse(c *testing.T) {
 	out, _ := dockerCmd(c, "history", "--human=false", "busybox")
 	lines := strings.Split(out, "\n")
 	sizeColumnRegex, _ := regexp.Compile("SIZE +")
@@ -97,11 +98,11 @@ func (s *DockerSuite) TestHistoryHumanOptionFalse(c *check.C) {
 		sizeString := lines[i][startIndex:endIndex]
 
 		_, err := strconv.Atoi(strings.TrimSpace(sizeString))
-		c.Assert(err, checker.IsNil, check.Commentf("The size '%s' was not an Integer", sizeString))
+		assert.Assert(c, err == nil, "The size '%s' was not an Integer", sizeString)
 	}
 }
 
-func (s *DockerSuite) TestHistoryHumanOptionTrue(c *check.C) {
+func (s *DockerSuite) TestHistoryHumanOptionTrue(c *testing.T) {
 	out, _ := dockerCmd(c, "history", "--human=true", "busybox")
 	lines := strings.Split(out, "\n")
 	sizeColumnRegex, _ := regexp.Compile("SIZE +")
@@ -114,6 +115,7 @@ func (s *DockerSuite) TestHistoryHumanOptionTrue(c *check.C) {
 			endIndex = len(lines[i])
 		}
 		sizeString := lines[i][startIndex:endIndex]
-		c.Assert(strings.TrimSpace(sizeString), checker.Matches, humanSizeRegexRaw, check.Commentf("The size '%s' was not in human format", sizeString))
+		assert.Assert(c, cmp.Regexp("^"+humanSizeRegexRaw+"$",
+			strings.TrimSpace(sizeString)), fmt.Sprintf("The size '%s' was not in human format", sizeString))
 	}
 }
diff --git a/integration-cli/docker_cli_images_test.go b/integration-cli/docker_cli_images_test.go
index 0dd319fbc9ac5..193cb20ab0588 100644
--- a/integration-cli/docker_cli_images_test.go
+++ b/integration-cli/docker_cli_images_test.go
@@ -2,51 +2,50 @@ package main
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"reflect"
 	"sort"
 	"strings"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli/build"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/icmd"
 )
 
-func (s *DockerSuite) TestImagesEnsureImageIsListed(c *check.C) {
+func (s *DockerSuite) TestImagesEnsureImageIsListed(c *testing.T) {
 	imagesOut, _ := dockerCmd(c, "images")
-	c.Assert(imagesOut, checker.Contains, "busybox")
+	assert.Assert(c, strings.Contains(imagesOut, "busybox"))
 }
 
-func (s *DockerSuite) TestImagesEnsureImageWithTagIsListed(c *check.C) {
+func (s *DockerSuite) TestImagesEnsureImageWithTagIsListed(c *testing.T) {
 	name := "imagewithtag"
 	dockerCmd(c, "tag", "busybox", name+":v1")
 	dockerCmd(c, "tag", "busybox", name+":v1v1")
 	dockerCmd(c, "tag", "busybox", name+":v2")
 
 	imagesOut, _ := dockerCmd(c, "images", name+":v1")
-	c.Assert(imagesOut, checker.Contains, name)
-	c.Assert(imagesOut, checker.Contains, "v1")
-	c.Assert(imagesOut, checker.Not(checker.Contains), "v2")
-	c.Assert(imagesOut, checker.Not(checker.Contains), "v1v1")
-
+	assert.Assert(c, strings.Contains(imagesOut, name))
+	assert.Assert(c, strings.Contains(imagesOut, "v1"))
+	assert.Assert(c, !strings.Contains(imagesOut, "v2"))
+	assert.Assert(c, !strings.Contains(imagesOut, "v1v1"))
 	imagesOut, _ = dockerCmd(c, "images", name)
-	c.Assert(imagesOut, checker.Contains, name)
-	c.Assert(imagesOut, checker.Contains, "v1")
-	c.Assert(imagesOut, checker.Contains, "v1v1")
-	c.Assert(imagesOut, checker.Contains, "v2")
+	assert.Assert(c, strings.Contains(imagesOut, name))
+	assert.Assert(c, strings.Contains(imagesOut, "v1"))
+	assert.Assert(c, strings.Contains(imagesOut, "v1v1"))
+	assert.Assert(c, strings.Contains(imagesOut, "v2"))
 }
 
-func (s *DockerSuite) TestImagesEnsureImageWithBadTagIsNotListed(c *check.C) {
+func (s *DockerSuite) TestImagesEnsureImageWithBadTagIsNotListed(c *testing.T) {
 	imagesOut, _ := dockerCmd(c, "images", "busybox:nonexistent")
-	c.Assert(imagesOut, checker.Not(checker.Contains), "busybox")
+	assert.Assert(c, !strings.Contains(imagesOut, "busybox"))
 }
 
-func (s *DockerSuite) TestImagesOrderedByCreationDate(c *check.C) {
+func (s *DockerSuite) TestImagesOrderedByCreationDate(c *testing.T) {
 	buildImageSuccessfully(c, "order:test_a", build.WithDockerfile(`FROM busybox
                 MAINTAINER dockerio1`))
 	id1 := getIDByName(c, "order:test_a")
@@ -61,18 +60,18 @@ func (s *DockerSuite) TestImagesOrderedByCreationDate(c *check.C) {
 
 	out, _ := dockerCmd(c, "images", "-q", "--no-trunc")
 	imgs := strings.Split(out, "\n")
-	c.Assert(imgs[0], checker.Equals, id3, check.Commentf("First image must be %s, got %s", id3, imgs[0]))
-	c.Assert(imgs[1], checker.Equals, id2, check.Commentf("First image must be %s, got %s", id2, imgs[1]))
-	c.Assert(imgs[2], checker.Equals, id1, check.Commentf("First image must be %s, got %s", id1, imgs[2]))
+	assert.Equal(c, imgs[0], id3, fmt.Sprintf("First image must be %s, got %s", id3, imgs[0]))
+	assert.Equal(c, imgs[1], id2, fmt.Sprintf("First image must be %s, got %s", id2, imgs[1]))
+	assert.Equal(c, imgs[2], id1, fmt.Sprintf("First image must be %s, got %s", id1, imgs[2]))
 }
 
-func (s *DockerSuite) TestImagesErrorWithInvalidFilterNameTest(c *check.C) {
+func (s *DockerSuite) TestImagesErrorWithInvalidFilterNameTest(c *testing.T) {
 	out, _, err := dockerCmdWithError("images", "-f", "FOO=123")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "Invalid filter")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Invalid filter"))
 }
 
-func (s *DockerSuite) TestImagesFilterLabelMatch(c *check.C) {
+func (s *DockerSuite) TestImagesFilterLabelMatch(c *testing.T) {
 	imageName1 := "images_filter_test1"
 	imageName2 := "images_filter_test2"
 	imageName3 := "images_filter_test3"
@@ -90,17 +89,19 @@ func (s *DockerSuite) TestImagesFilterLabelMatch(c *check.C) {
 
 	out, _ := dockerCmd(c, "images", "--no-trunc", "-q", "-f", "label=match")
 	out = strings.TrimSpace(out)
-	c.Assert(out, check.Matches, fmt.Sprintf("[\\s\\w:]*%s[\\s\\w:]*", image1ID))
-	c.Assert(out, check.Matches, fmt.Sprintf("[\\s\\w:]*%s[\\s\\w:]*", image2ID))
-	c.Assert(out, check.Not(check.Matches), fmt.Sprintf("[\\s\\w:]*%s[\\s\\w:]*", image3ID))
+	assert.Assert(c, is.Regexp(fmt.Sprintf("^[\\s\\w:]*%s[\\s\\w:]*$", image1ID), out))
+
+	assert.Assert(c, is.Regexp(fmt.Sprintf("^[\\s\\w:]*%s[\\s\\w:]*$", image2ID), out))
+
+	assert.Assert(c, !is.Regexp(fmt.Sprintf("^[\\s\\w:]*%s[\\s\\w:]*$", image3ID), out)().Success())
 
 	out, _ = dockerCmd(c, "images", "--no-trunc", "-q", "-f", "label=match=me too")
 	out = strings.TrimSpace(out)
-	c.Assert(out, check.Equals, image2ID)
+	assert.Equal(c, out, image2ID)
 }
 
 // Regression : #15659
-func (s *DockerSuite) TestCommitWithFilterLabel(c *check.C) {
+func (s *DockerSuite) TestCommitWithFilterLabel(c *testing.T) {
 	// Create a container
 	dockerCmd(c, "run", "--name", "bar", "busybox", "/bin/sh")
 	// Commit with labels "using changes"
@@ -109,10 +110,10 @@ func (s *DockerSuite) TestCommitWithFilterLabel(c *check.C) {
 
 	out, _ = dockerCmd(c, "images", "--no-trunc", "-q", "-f", "label=foo.version=1.0.0-1")
 	out = strings.TrimSpace(out)
-	c.Assert(out, check.Equals, imageID)
+	assert.Equal(c, out, imageID)
 }
 
-func (s *DockerSuite) TestImagesFilterSinceAndBefore(c *check.C) {
+func (s *DockerSuite) TestImagesFilterSinceAndBefore(c *testing.T) {
 	buildImageSuccessfully(c, "image:1", build.WithDockerfile(`FROM `+minimalBaseImage()+`
 LABEL number=1`))
 	imageID1 := getIDByName(c, "image:1")
@@ -126,34 +127,34 @@ LABEL number=3`))
 	expected := []string{imageID3, imageID2}
 
 	out, _ := dockerCmd(c, "images", "-f", "since=image:1", "image")
-	c.Assert(assertImageList(out, expected), checker.Equals, true, check.Commentf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out))
+	assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out))
 
 	out, _ = dockerCmd(c, "images", "-f", "since="+imageID1, "image")
-	c.Assert(assertImageList(out, expected), checker.Equals, true, check.Commentf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out))
+	assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out))
 
 	expected = []string{imageID3}
 
 	out, _ = dockerCmd(c, "images", "-f", "since=image:2", "image")
-	c.Assert(assertImageList(out, expected), checker.Equals, true, check.Commentf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out))
+	assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out))
 
 	out, _ = dockerCmd(c, "images", "-f", "since="+imageID2, "image")
-	c.Assert(assertImageList(out, expected), checker.Equals, true, check.Commentf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out))
+	assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out))
 
 	expected = []string{imageID2, imageID1}
 
 	out, _ = dockerCmd(c, "images", "-f", "before=image:3", "image")
-	c.Assert(assertImageList(out, expected), checker.Equals, true, check.Commentf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out))
+	assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out))
 
 	out, _ = dockerCmd(c, "images", "-f", "before="+imageID3, "image")
-	c.Assert(assertImageList(out, expected), checker.Equals, true, check.Commentf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out))
+	assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out))
 
 	expected = []string{imageID1}
 
 	out, _ = dockerCmd(c, "images", "-f", "before=image:2", "image")
-	c.Assert(assertImageList(out, expected), checker.Equals, true, check.Commentf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out))
+	assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out))
 
 	out, _ = dockerCmd(c, "images", "-f", "before="+imageID2, "image")
-	c.Assert(assertImageList(out, expected), checker.Equals, true, check.Commentf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out))
+	assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out))
 }
 
 func assertImageList(out string, expected []string) bool {
@@ -182,7 +183,7 @@ func assertImageList(out string, expected []string) bool {
 }
 
 // FIXME(vdemeester) should be a unit test on `docker image ls`
-func (s *DockerSuite) TestImagesFilterSpaceTrimCase(c *check.C) {
+func (s *DockerSuite) TestImagesFilterSpaceTrimCase(c *testing.T) {
 	imageName := "images_filter_test"
 	// Build a image and fail to build so that we have dangling images ?
 	buildImage(imageName, build.WithDockerfile(`FROM busybox
@@ -200,7 +201,7 @@ func (s *DockerSuite) TestImagesFilterSpaceTrimCase(c *check.C) {
 		"dangling = true",
 	}
 
-	imageListings := make([][]string, 5, 5)
+	imageListings := make([][]string, 5)
 	for idx, filter := range filters {
 		out, _ := dockerCmd(c, "images", "-q", "-f", filter)
 		listing := strings.Split(out, "\n")
@@ -222,7 +223,7 @@ func (s *DockerSuite) TestImagesFilterSpaceTrimCase(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestImagesEnsureDanglingImageOnlyListedOnce(c *check.C) {
+func (s *DockerSuite) TestImagesEnsureDanglingImageOnlyListedOnce(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	// create container 1
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
@@ -237,26 +238,24 @@ func (s *DockerSuite) TestImagesEnsureDanglingImageOnlyListedOnce(c *check.C) {
 
 	out, _ = dockerCmd(c, "images", "-q", "-f", "dangling=true")
 	// Expect one dangling image
-	c.Assert(strings.Count(out, imageID), checker.Equals, 1)
+	assert.Equal(c, strings.Count(out, imageID), 1)
 
 	out, _ = dockerCmd(c, "images", "-q", "-f", "dangling=false")
-	//dangling=false would not include dangling images
-	c.Assert(out, checker.Not(checker.Contains), imageID)
-
+	// dangling=false would not include dangling images
+	assert.Assert(c, !strings.Contains(out, imageID))
 	out, _ = dockerCmd(c, "images")
-	//docker images still include dangling images
-	c.Assert(out, checker.Contains, imageID)
-
+	// docker images still include dangling images
+	assert.Assert(c, strings.Contains(out, imageID))
 }
 
 // FIXME(vdemeester) should be a unit test for `docker image ls`
-func (s *DockerSuite) TestImagesWithIncorrectFilter(c *check.C) {
+func (s *DockerSuite) TestImagesWithIncorrectFilter(c *testing.T) {
 	out, _, err := dockerCmdWithError("images", "-f", "dangling=invalid")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, "Invalid filter")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Invalid filter"))
 }
 
-func (s *DockerSuite) TestImagesEnsureOnlyHeadsImagesShown(c *check.C) {
+func (s *DockerSuite) TestImagesEnsureOnlyHeadsImagesShown(c *testing.T) {
 	dockerfile := `
         FROM busybox
         MAINTAINER docker
@@ -274,12 +273,12 @@ func (s *DockerSuite) TestImagesEnsureOnlyHeadsImagesShown(c *check.C) {
 
 	out, _ := dockerCmd(c, "images")
 	// images shouldn't show non-heads images
-	c.Assert(out, checker.Not(checker.Contains), intermediate)
+	assert.Assert(c, !strings.Contains(out, intermediate))
 	// images should contain final built images
-	c.Assert(out, checker.Contains, stringid.TruncateID(id))
+	assert.Assert(c, strings.Contains(out, stringid.TruncateID(id)))
 }
 
-func (s *DockerSuite) TestImagesEnsureImagesFromScratchShown(c *check.C) {
+func (s *DockerSuite) TestImagesEnsureImagesFromScratchShown(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support FROM scratch
 	dockerfile := `
         FROM scratch
@@ -291,12 +290,12 @@ func (s *DockerSuite) TestImagesEnsureImagesFromScratchShown(c *check.C) {
 
 	out, _ := dockerCmd(c, "images")
 	// images should contain images built from scratch
-	c.Assert(out, checker.Contains, stringid.TruncateID(id))
+	assert.Assert(c, strings.Contains(out, stringid.TruncateID(id)))
 }
 
 // For W2W - equivalent to TestImagesEnsureImagesFromScratchShown but Windows
 // doesn't support from scratch
-func (s *DockerSuite) TestImagesEnsureImagesFromBusyboxShown(c *check.C) {
+func (s *DockerSuite) TestImagesEnsureImagesFromBusyboxShown(c *testing.T) {
 	dockerfile := `
         FROM busybox
         MAINTAINER docker`
@@ -307,40 +306,38 @@ func (s *DockerSuite) TestImagesEnsureImagesFromBusyboxShown(c *check.C) {
 
 	out, _ := dockerCmd(c, "images")
 	// images should contain images built from busybox
-	c.Assert(out, checker.Contains, stringid.TruncateID(id))
+	assert.Assert(c, strings.Contains(out, stringid.TruncateID(id)))
 }
 
 // #18181
-func (s *DockerSuite) TestImagesFilterNameWithPort(c *check.C) {
+func (s *DockerSuite) TestImagesFilterNameWithPort(c *testing.T) {
 	tag := "a.b.c.d:5000/hello"
 	dockerCmd(c, "tag", "busybox", tag)
 	out, _ := dockerCmd(c, "images", tag)
-	c.Assert(out, checker.Contains, tag)
-
+	assert.Assert(c, strings.Contains(out, tag))
 	out, _ = dockerCmd(c, "images", tag+":latest")
-	c.Assert(out, checker.Contains, tag)
-
+	assert.Assert(c, strings.Contains(out, tag))
 	out, _ = dockerCmd(c, "images", tag+":no-such-tag")
-	c.Assert(out, checker.Not(checker.Contains), tag)
+	assert.Assert(c, !strings.Contains(out, tag))
 }
 
-func (s *DockerSuite) TestImagesFormat(c *check.C) {
+func (s *DockerSuite) TestImagesFormat(c *testing.T) {
 	// testRequires(c, DaemonIsLinux)
 	tag := "myimage"
 	dockerCmd(c, "tag", "busybox", tag+":v1")
 	dockerCmd(c, "tag", "busybox", tag+":v2")
 
 	out, _ := dockerCmd(c, "images", "--format", "{{.Repository}}", tag)
-	lines := strings.Split(strings.TrimSpace(string(out)), "\n")
+	lines := strings.Split(strings.TrimSpace(out), "\n")
 
 	expected := []string{"myimage", "myimage"}
 	var names []string
 	names = append(names, lines...)
-	c.Assert(names, checker.DeepEquals, expected, check.Commentf("Expected array with truncated names: %v, got: %v", expected, names))
+	assert.Assert(c, is.DeepEqual(names, expected), "Expected array with truncated names: %v, got: %v", expected, names)
 }
 
 // ImagesDefaultFormatAndQuiet
-func (s *DockerSuite) TestImagesFormatDefaultFormat(c *check.C) {
+func (s *DockerSuite) TestImagesFormatDefaultFormat(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	// create container 1
@@ -354,13 +351,13 @@ func (s *DockerSuite) TestImagesFormatDefaultFormat(c *check.C) {
 	config := `{
 		"imagesFormat": "{{ .ID }} default"
 }`
-	d, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, checker.IsNil)
+	d, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(d)
 
-	err = ioutil.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
+	assert.NilError(c, err)
 
 	out, _ = dockerCmd(c, "--config", d, "images", "-q", "myimage")
-	c.Assert(out, checker.Equals, imageID+"\n", check.Commentf("Expected to print only the image id, got %v\n", out))
+	assert.Equal(c, out, imageID+"\n", "Expected to print only the image id, got %v\n", out)
 }
diff --git a/integration-cli/docker_cli_import_test.go b/integration-cli/docker_cli_import_test.go
index 9f8e9158036c9..03ce0b9facfb1 100644
--- a/integration-cli/docker_cli_import_test.go
+++ b/integration-cli/docker_cli_import_test.go
@@ -3,19 +3,18 @@ package main
 import (
 	"bufio"
 	"compress/gzip"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"regexp"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
-func (s *DockerSuite) TestImportDisplay(c *check.C) {
+func (s *DockerSuite) TestImportDisplay(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
 	cleanedContainerID := strings.TrimSpace(out)
@@ -24,18 +23,18 @@ func (s *DockerSuite) TestImportDisplay(c *check.C) {
 		exec.Command(dockerBinary, "export", cleanedContainerID),
 		exec.Command(dockerBinary, "import", "-"),
 	)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(out, checker.Count, "\n", 1, check.Commentf("display is expected 1 '\\n' but didn't"))
+	assert.Assert(c, strings.Count(out, "\n") == 1, "display is expected 1 '\\n' but didn't")
 
 	image := strings.TrimSpace(out)
 	out, _ = dockerCmd(c, "run", "--rm", image, "true")
-	c.Assert(out, checker.Equals, "", check.Commentf("command output should've been nothing."))
+	assert.Equal(c, out, "", "command output should've been nothing.")
 }
 
-func (s *DockerSuite) TestImportBadURL(c *check.C) {
+func (s *DockerSuite) TestImportBadURL(c *testing.T) {
 	out, _, err := dockerCmdWithError("import", "http://nourl/bad")
-	c.Assert(err, checker.NotNil, check.Commentf("import was supposed to fail but didn't"))
+	assert.Assert(c, err != nil, "import was supposed to fail but didn't")
 	// Depending on your system you can get either of these errors
 	if !strings.Contains(out, "dial tcp") &&
 		!strings.Contains(out, "ApplyLayer exit status 1 stdout:  stderr: archive/tar: invalid tar header") &&
@@ -44,12 +43,12 @@ func (s *DockerSuite) TestImportBadURL(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestImportFile(c *check.C) {
+func (s *DockerSuite) TestImportFile(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "--name", "test-import", "busybox", "true")
 
-	temporaryFile, err := ioutil.TempFile("", "exportImportTest")
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary file"))
+	temporaryFile, err := os.CreateTemp("", "exportImportTest")
+	assert.Assert(c, err == nil, "failed to create temporary file")
 	defer os.Remove(temporaryFile.Name())
 
 	icmd.RunCmd(icmd.Cmd{
@@ -58,19 +57,19 @@ func (s *DockerSuite) TestImportFile(c *check.C) {
 	}).Assert(c, icmd.Success)
 
 	out, _ := dockerCmd(c, "import", temporaryFile.Name())
-	c.Assert(out, checker.Count, "\n", 1, check.Commentf("display is expected 1 '\\n' but didn't"))
+	assert.Assert(c, strings.Count(out, "\n") == 1, "display is expected 1 '\\n' but didn't")
 	image := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "run", "--rm", image, "true")
-	c.Assert(out, checker.Equals, "", check.Commentf("command output should've been nothing."))
+	assert.Equal(c, out, "", "command output should've been nothing.")
 }
 
-func (s *DockerSuite) TestImportGzipped(c *check.C) {
+func (s *DockerSuite) TestImportGzipped(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "--name", "test-import", "busybox", "true")
 
-	temporaryFile, err := ioutil.TempFile("", "exportImportTest")
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary file"))
+	temporaryFile, err := os.CreateTemp("", "exportImportTest")
+	assert.Assert(c, err == nil, "failed to create temporary file")
 	defer os.Remove(temporaryFile.Name())
 
 	w := gzip.NewWriter(temporaryFile)
@@ -78,22 +77,22 @@ func (s *DockerSuite) TestImportGzipped(c *check.C) {
 		Command: []string{dockerBinary, "export", "test-import"},
 		Stdout:  w,
 	}).Assert(c, icmd.Success)
-	c.Assert(w.Close(), checker.IsNil, check.Commentf("failed to close gzip writer"))
+	assert.Assert(c, w.Close() == nil, "failed to close gzip writer")
 	temporaryFile.Close()
 	out, _ := dockerCmd(c, "import", temporaryFile.Name())
-	c.Assert(out, checker.Count, "\n", 1, check.Commentf("display is expected 1 '\\n' but didn't"))
+	assert.Assert(c, strings.Count(out, "\n") == 1, "display is expected 1 '\\n' but didn't")
 	image := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "run", "--rm", image, "true")
-	c.Assert(out, checker.Equals, "", check.Commentf("command output should've been nothing."))
+	assert.Equal(c, out, "", "command output should've been nothing.")
 }
 
-func (s *DockerSuite) TestImportFileWithMessage(c *check.C) {
+func (s *DockerSuite) TestImportFileWithMessage(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "--name", "test-import", "busybox", "true")
 
-	temporaryFile, err := ioutil.TempFile("", "exportImportTest")
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary file"))
+	temporaryFile, err := os.CreateTemp("", "exportImportTest")
+	assert.Assert(c, err == nil, "failed to create temporary file")
 	defer os.Remove(temporaryFile.Name())
 
 	icmd.RunCmd(icmd.Cmd{
@@ -103,33 +102,33 @@ func (s *DockerSuite) TestImportFileWithMessage(c *check.C) {
 
 	message := "Testing commit message"
 	out, _ := dockerCmd(c, "import", "-m", message, temporaryFile.Name())
-	c.Assert(out, checker.Count, "\n", 1, check.Commentf("display is expected 1 '\\n' but didn't"))
+	assert.Assert(c, strings.Count(out, "\n") == 1, "display is expected 1 '\\n' but didn't")
 	image := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "history", image)
 	split := strings.Split(out, "\n")
 
-	c.Assert(split, checker.HasLen, 3, check.Commentf("expected 3 lines from image history"))
-	r := regexp.MustCompile("[\\s]{2,}")
+	assert.Equal(c, len(split), 3, "expected 3 lines from image history")
+	r := regexp.MustCompile(`[\s]{2,}`)
 	split = r.Split(split[1], -1)
 
-	c.Assert(message, checker.Equals, split[3], check.Commentf("didn't get expected value in commit message"))
+	assert.Equal(c, message, split[3], "didn't get expected value in commit message")
 
 	out, _ = dockerCmd(c, "run", "--rm", image, "true")
-	c.Assert(out, checker.Equals, "", check.Commentf("command output should've been nothing"))
+	assert.Equal(c, out, "", "command output should've been nothing")
 }
 
-func (s *DockerSuite) TestImportFileNonExistentFile(c *check.C) {
+func (s *DockerSuite) TestImportFileNonExistentFile(c *testing.T) {
 	_, _, err := dockerCmdWithError("import", "example.com/myImage.tar")
-	c.Assert(err, checker.NotNil, check.Commentf("import non-existing file must failed"))
+	assert.Assert(c, err != nil, "import non-existing file must failed")
 }
 
-func (s *DockerSuite) TestImportWithQuotedChanges(c *check.C) {
+func (s *DockerSuite) TestImportWithQuotedChanges(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	cli.DockerCmd(c, "run", "--name", "test-import", "busybox", "true")
 
-	temporaryFile, err := ioutil.TempFile("", "exportImportTest")
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary file"))
+	temporaryFile, err := os.CreateTemp("", "exportImportTest")
+	assert.Assert(c, err == nil, "failed to create temporary file")
 	defer os.Remove(temporaryFile.Name())
 
 	cli.Docker(cli.Args("export", "test-import"), cli.WithStdout(bufio.NewWriter(temporaryFile))).Assert(c, icmd.Success)
diff --git a/integration-cli/docker_cli_info_test.go b/integration-cli/docker_cli_info_test.go
index 65091029eec23..f56c517a1cce0 100644
--- a/integration-cli/docker_cli_info_test.go
+++ b/integration-cli/docker_cli_info_test.go
@@ -3,17 +3,14 @@ package main
 import (
 	"encoding/json"
 	"fmt"
-	"net"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/integration-cli/daemon"
-	testdaemon "github.com/docker/docker/internal/test/daemon"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
 // ensure docker info succeeds
-func (s *DockerSuite) TestInfoEnsureSucceeds(c *check.C) {
+func (s *DockerSuite) TestInfoEnsureSucceeds(c *testing.T) {
 	out, _ := dockerCmd(c, "info")
 
 	// always shown fields
@@ -52,95 +49,24 @@ func (s *DockerSuite) TestInfoEnsureSucceeds(c *check.C) {
 	}
 
 	for _, linePrefix := range stringsToCheck {
-		c.Assert(out, checker.Contains, linePrefix, check.Commentf("couldn't find string %v in output", linePrefix))
+		assert.Assert(c, strings.Contains(out, linePrefix), "couldn't find string %v in output", linePrefix)
 	}
 }
 
-// TestInfoFormat tests `docker info --format`
-func (s *DockerSuite) TestInfoFormat(c *check.C) {
-	out, status := dockerCmd(c, "info", "--format", "{{json .}}")
-	c.Assert(status, checker.Equals, 0)
-	var m map[string]interface{}
-	err := json.Unmarshal([]byte(out), &m)
-	c.Assert(err, checker.IsNil)
-	_, _, err = dockerCmdWithError("info", "--format", "{{.badString}}")
-	c.Assert(err, checker.NotNil)
-}
-
-// TestInfoDiscoveryBackend verifies that a daemon run with `--cluster-advertise` and
-// `--cluster-store` properly show the backend's endpoint in info output.
-func (s *DockerSuite) TestInfoDiscoveryBackend(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
-
-	d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
-	discoveryBackend := "consul://consuladdr:consulport/some/path"
-	discoveryAdvertise := "1.1.1.1:2375"
-	d.Start(c, fmt.Sprintf("--cluster-store=%s", discoveryBackend), fmt.Sprintf("--cluster-advertise=%s", discoveryAdvertise))
-	defer d.Stop(c)
-
-	out, err := d.Cmd("info")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, fmt.Sprintf("Cluster Store: %s\n", discoveryBackend))
-	c.Assert(out, checker.Contains, fmt.Sprintf("Cluster Advertise: %s\n", discoveryAdvertise))
-}
-
-// TestInfoDiscoveryInvalidAdvertise verifies that a daemon run with
-// an invalid `--cluster-advertise` configuration
-func (s *DockerSuite) TestInfoDiscoveryInvalidAdvertise(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
-
-	d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
-	discoveryBackend := "consul://consuladdr:consulport/some/path"
-
-	// --cluster-advertise with an invalid string is an error
-	err := d.StartWithError(fmt.Sprintf("--cluster-store=%s", discoveryBackend), "--cluster-advertise=invalid")
-	c.Assert(err, checker.NotNil)
-
-	// --cluster-advertise without --cluster-store is also an error
-	err = d.StartWithError("--cluster-advertise=1.1.1.1:2375")
-	c.Assert(err, checker.NotNil)
-}
-
-// TestInfoDiscoveryAdvertiseInterfaceName verifies that a daemon run with `--cluster-advertise`
-// configured with interface name properly show the advertise ip-address in info output.
-func (s *DockerSuite) TestInfoDiscoveryAdvertiseInterfaceName(c *check.C) {
-	testRequires(c, SameHostDaemon, Network, DaemonIsLinux)
-
-	d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
-	discoveryBackend := "consul://consuladdr:consulport/some/path"
-	discoveryAdvertise := "eth0"
-
-	d.Start(c, fmt.Sprintf("--cluster-store=%s", discoveryBackend), fmt.Sprintf("--cluster-advertise=%s:2375", discoveryAdvertise))
-	defer d.Stop(c)
-
-	iface, err := net.InterfaceByName(discoveryAdvertise)
-	c.Assert(err, checker.IsNil)
-	addrs, err := iface.Addrs()
-	c.Assert(err, checker.IsNil)
-	c.Assert(len(addrs), checker.GreaterThan, 0)
-	ip, _, err := net.ParseCIDR(addrs[0].String())
-	c.Assert(err, checker.IsNil)
-
-	out, err := d.Cmd("info")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, fmt.Sprintf("Cluster Store: %s\n", discoveryBackend))
-	c.Assert(out, checker.Contains, fmt.Sprintf("Cluster Advertise: %s:2375\n", ip.String()))
-}
-
-func (s *DockerSuite) TestInfoDisplaysRunningContainers(c *check.C) {
+func (s *DockerSuite) TestInfoDisplaysRunningContainers(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	existing := existingContainerStates(c)
 
 	dockerCmd(c, "run", "-d", "busybox", "top")
 	out, _ := dockerCmd(c, "info")
-	c.Assert(out, checker.Contains, fmt.Sprintf("Containers: %d\n", existing["Containers"]+1))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" Running: %d\n", existing["ContainersRunning"]+1))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" Paused: %d\n", existing["ContainersPaused"]))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" Stopped: %d\n", existing["ContainersStopped"]))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("Containers: %d\n", existing["Containers"]+1)))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf(" Running: %d\n", existing["ContainersRunning"]+1)))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf(" Paused: %d\n", existing["ContainersPaused"])))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf(" Stopped: %d\n", existing["ContainersStopped"])))
 }
 
-func (s *DockerSuite) TestInfoDisplaysPausedContainers(c *check.C) {
+func (s *DockerSuite) TestInfoDisplaysPausedContainers(c *testing.T) {
 	testRequires(c, IsPausable)
 
 	existing := existingContainerStates(c)
@@ -151,13 +77,13 @@ func (s *DockerSuite) TestInfoDisplaysPausedContainers(c *check.C) {
 	dockerCmd(c, "pause", cleanedContainerID)
 
 	out, _ = dockerCmd(c, "info")
-	c.Assert(out, checker.Contains, fmt.Sprintf("Containers: %d\n", existing["Containers"]+1))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" Running: %d\n", existing["ContainersRunning"]))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" Paused: %d\n", existing["ContainersPaused"]+1))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" Stopped: %d\n", existing["ContainersStopped"]))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("Containers: %d\n", existing["Containers"]+1)))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf(" Running: %d\n", existing["ContainersRunning"])))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf(" Paused: %d\n", existing["ContainersPaused"]+1)))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf(" Stopped: %d\n", existing["ContainersStopped"])))
 }
 
-func (s *DockerSuite) TestInfoDisplaysStoppedContainers(c *check.C) {
+func (s *DockerSuite) TestInfoDisplaysStoppedContainers(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	existing := existingContainerStates(c)
@@ -168,67 +94,17 @@ func (s *DockerSuite) TestInfoDisplaysStoppedContainers(c *check.C) {
 	dockerCmd(c, "stop", cleanedContainerID)
 
 	out, _ = dockerCmd(c, "info")
-	c.Assert(out, checker.Contains, fmt.Sprintf("Containers: %d\n", existing["Containers"]+1))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" Running: %d\n", existing["ContainersRunning"]))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" Paused: %d\n", existing["ContainersPaused"]))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" Stopped: %d\n", existing["ContainersStopped"]+1))
-}
-
-func (s *DockerSuite) TestInfoDebug(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
-
-	d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
-	d.Start(c, "--debug")
-	defer d.Stop(c)
-
-	out, err := d.Cmd("--debug", "info")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "Debug Mode (client): true\n")
-	c.Assert(out, checker.Contains, "Debug Mode (server): true\n")
-	c.Assert(out, checker.Contains, "File Descriptors")
-	c.Assert(out, checker.Contains, "Goroutines")
-	c.Assert(out, checker.Contains, "System Time")
-	c.Assert(out, checker.Contains, "EventsListeners")
-	c.Assert(out, checker.Contains, "Docker Root Dir")
-}
-
-func (s *DockerSuite) TestInsecureRegistries(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
-
-	registryCIDR := "192.168.1.0/24"
-	registryHost := "insecurehost.com:5000"
-
-	d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
-	d.Start(c, "--insecure-registry="+registryCIDR, "--insecure-registry="+registryHost)
-	defer d.Stop(c)
-
-	out, err := d.Cmd("info")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "Insecure Registries:\n")
-	c.Assert(out, checker.Contains, fmt.Sprintf(" %s\n", registryHost))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" %s\n", registryCIDR))
-}
-
-func (s *DockerDaemonSuite) TestRegistryMirrors(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
-
-	registryMirror1 := "https://192.168.1.2"
-	registryMirror2 := "http://registry.mirror.com:5000"
-
-	s.d.Start(c, "--registry-mirror="+registryMirror1, "--registry-mirror="+registryMirror2)
-
-	out, err := s.d.Cmd("info")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "Registry Mirrors:\n")
-	c.Assert(out, checker.Contains, fmt.Sprintf(" %s", registryMirror1))
-	c.Assert(out, checker.Contains, fmt.Sprintf(" %s", registryMirror2))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("Containers: %d\n", existing["Containers"]+1)))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf(" Running: %d\n", existing["ContainersRunning"])))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf(" Paused: %d\n", existing["ContainersPaused"])))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf(" Stopped: %d\n", existing["ContainersStopped"]+1)))
 }
 
-func existingContainerStates(c *check.C) map[string]int {
+func existingContainerStates(c *testing.T) map[string]int {
 	out, _ := dockerCmd(c, "info", "--format", "{{json .}}")
 	var m map[string]interface{}
 	err := json.Unmarshal([]byte(out), &m)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	res := map[string]int{}
 	res["Containers"] = int(m["Containers"].(float64))
 	res["ContainersRunning"] = int(m["ContainersRunning"].(float64))
diff --git a/integration-cli/docker_cli_info_unix_test.go b/integration-cli/docker_cli_info_unix_test.go
index d55c05c4a51b3..f684a6c67575a 100644
--- a/integration-cli/docker_cli_info_unix_test.go
+++ b/integration-cli/docker_cli_info_unix_test.go
@@ -1,15 +1,34 @@
+//go:build !windows
 // +build !windows
 
 package main
 
 import (
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"context"
+	"testing"
+
+	"github.com/docker/docker/client"
+	"github.com/docker/docker/daemon/config"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
-func (s *DockerSuite) TestInfoSecurityOptions(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled, Apparmor, DaemonIsLinux)
+func (s *DockerSuite) TestInfoSecurityOptions(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
+	if !seccompEnabled() && !Apparmor() {
+		c.Skip("test requires Seccomp and/or AppArmor")
+	}
+
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
+	defer cli.Close()
+	info, err := cli.Info(context.Background())
+	assert.NilError(c, err)
 
-	out, _ := dockerCmd(c, "info")
-	c.Assert(out, checker.Contains, "Security Options:\n apparmor\n seccomp\n  Profile: default\n")
+	if Apparmor() {
+		assert.Check(c, is.Contains(info.SecurityOptions, "name=apparmor"))
+	}
+	if seccompEnabled() {
+		assert.Check(c, is.Contains(info.SecurityOptions, "name=seccomp,profile="+config.SeccompProfileDefault))
+	}
 }
diff --git a/integration-cli/docker_cli_inspect_test.go b/integration-cli/docker_cli_inspect_test.go
index d027c447750d1..e380e0c8c8c7b 100644
--- a/integration-cli/docker_cli_inspect_test.go
+++ b/integration-cli/docker_cli_inspect_test.go
@@ -6,22 +6,22 @@ import (
 	"os"
 	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
-func checkValidGraphDriver(c *check.C, name string) {
+func checkValidGraphDriver(c *testing.T, name string) {
 	if name != "devicemapper" && name != "overlay" && name != "vfs" && name != "zfs" && name != "btrfs" && name != "aufs" {
 		c.Fatalf("%v is not a valid graph driver name", name)
 	}
 }
 
-func (s *DockerSuite) TestInspectImage(c *check.C) {
+func (s *DockerSuite) TestInspectImage(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	imageTest := "emptyfs"
 	// It is important that this ID remain stable. If a code change causes
@@ -32,113 +32,114 @@ func (s *DockerSuite) TestInspectImage(c *check.C) {
 	imageTestID := "sha256:11f64303f0f7ffdc71f001788132bca5346831939a956e3e975c93267d89a16d"
 	id := inspectField(c, imageTest, "Id")
 
-	c.Assert(id, checker.Equals, imageTestID)
+	assert.Equal(c, id, imageTestID)
 }
 
-func (s *DockerSuite) TestInspectInt64(c *check.C) {
+func (s *DockerSuite) TestInspectInt64(c *testing.T) {
 	dockerCmd(c, "run", "-d", "-m=300M", "--name", "inspectTest", "busybox", "true")
 	inspectOut := inspectField(c, "inspectTest", "HostConfig.Memory")
-	c.Assert(inspectOut, checker.Equals, "314572800")
+	assert.Equal(c, inspectOut, "314572800")
 }
 
-func (s *DockerSuite) TestInspectDefault(c *check.C) {
-	//Both the container and image are named busybox. docker inspect will fetch the container JSON.
-	//If the container JSON is not available, it will go for the image JSON.
+func (s *DockerSuite) TestInspectDefault(c *testing.T) {
+	// Both the container and image are named busybox. docker inspect will fetch the container JSON.
+	// If the container JSON is not available, it will go for the image JSON.
 
 	out, _ := dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "true")
 	containerID := strings.TrimSpace(out)
 
 	inspectOut := inspectField(c, "busybox", "Id")
-	c.Assert(strings.TrimSpace(inspectOut), checker.Equals, containerID)
+	assert.Equal(c, strings.TrimSpace(inspectOut), containerID)
 }
 
-func (s *DockerSuite) TestInspectStatus(c *check.C) {
+func (s *DockerSuite) TestInspectStatus(c *testing.T) {
 	out := runSleepingContainer(c, "-d")
 	out = strings.TrimSpace(out)
 
 	inspectOut := inspectField(c, out, "State.Status")
-	c.Assert(inspectOut, checker.Equals, "running")
+	assert.Equal(c, inspectOut, "running")
 
 	// Windows does not support pause/unpause on Windows Server Containers.
 	// (RS1 does for Hyper-V Containers, but production CI is not setup for that)
 	if testEnv.OSType != "windows" {
 		dockerCmd(c, "pause", out)
 		inspectOut = inspectField(c, out, "State.Status")
-		c.Assert(inspectOut, checker.Equals, "paused")
+		assert.Equal(c, inspectOut, "paused")
 
 		dockerCmd(c, "unpause", out)
 		inspectOut = inspectField(c, out, "State.Status")
-		c.Assert(inspectOut, checker.Equals, "running")
+		assert.Equal(c, inspectOut, "running")
 	}
 
 	dockerCmd(c, "stop", out)
 	inspectOut = inspectField(c, out, "State.Status")
-	c.Assert(inspectOut, checker.Equals, "exited")
+	assert.Equal(c, inspectOut, "exited")
 
 }
 
-func (s *DockerSuite) TestInspectTypeFlagContainer(c *check.C) {
-	//Both the container and image are named busybox. docker inspect will fetch container
-	//JSON State.Running field. If the field is true, it's a container.
+func (s *DockerSuite) TestInspectTypeFlagContainer(c *testing.T) {
+	// Both the container and image are named busybox. docker inspect will fetch container
+	// JSON State.Running field. If the field is true, it's a container.
 	runSleepingContainer(c, "--name=busybox", "-d")
 
 	formatStr := "--format={{.State.Running}}"
 	out, _ := dockerCmd(c, "inspect", "--type=container", formatStr, "busybox")
-	c.Assert(out, checker.Equals, "true\n") // not a container JSON
+	assert.Equal(c, out, "true\n") // not a container JSON
 }
 
-func (s *DockerSuite) TestInspectTypeFlagWithNoContainer(c *check.C) {
-	//Run this test on an image named busybox. docker inspect will try to fetch container
-	//JSON. Since there is no container named busybox and --type=container, docker inspect will
-	//not try to get the image JSON. It will throw an error.
+func (s *DockerSuite) TestInspectTypeFlagWithNoContainer(c *testing.T) {
+	// Run this test on an image named busybox. docker inspect will try to fetch container
+	// JSON. Since there is no container named busybox and --type=container, docker inspect will
+	// not try to get the image JSON. It will throw an error.
 
 	dockerCmd(c, "run", "-d", "busybox", "true")
 
 	_, _, err := dockerCmdWithError("inspect", "--type=container", "busybox")
 	// docker inspect should fail, as there is no container named busybox
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 }
 
-func (s *DockerSuite) TestInspectTypeFlagWithImage(c *check.C) {
-	//Both the container and image are named busybox. docker inspect will fetch image
-	//JSON as --type=image. if there is no image with name busybox, docker inspect
-	//will throw an error.
+func (s *DockerSuite) TestInspectTypeFlagWithImage(c *testing.T) {
+	// Both the container and image are named busybox. docker inspect will fetch image
+	// JSON as --type=image. if there is no image with name busybox, docker inspect
+	// will throw an error.
 
 	dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "true")
 
 	out, _ := dockerCmd(c, "inspect", "--type=image", "busybox")
-	c.Assert(out, checker.Not(checker.Contains), "State") // not an image JSON
+	// not an image JSON
+	assert.Assert(c, !strings.Contains(out, "State"))
 }
 
-func (s *DockerSuite) TestInspectTypeFlagWithInvalidValue(c *check.C) {
-	//Both the container and image are named busybox. docker inspect will fail
-	//as --type=foobar is not a valid value for the flag.
+func (s *DockerSuite) TestInspectTypeFlagWithInvalidValue(c *testing.T) {
+	// Both the container and image are named busybox. docker inspect will fail
+	// as --type=foobar is not a valid value for the flag.
 
 	dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "true")
 
 	out, exitCode, err := dockerCmdWithError("inspect", "--type=foobar", "busybox")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", exitCode))
-	c.Assert(exitCode, checker.Equals, 1, check.Commentf("%s", err))
-	c.Assert(out, checker.Contains, "not a valid value for --type")
+	assert.Assert(c, err != nil, "%d", exitCode)
+	assert.Equal(c, exitCode, 1, fmt.Sprintf("%s", err))
+	assert.Assert(c, strings.Contains(out, "not a valid value for --type"))
 }
 
-func (s *DockerSuite) TestInspectImageFilterInt(c *check.C) {
+func (s *DockerSuite) TestInspectImageFilterInt(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	imageTest := "emptyfs"
 	out := inspectField(c, imageTest, "Size")
 
 	size, err := strconv.Atoi(out)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect size of the image: %s, %v", out, err))
+	assert.Assert(c, err == nil, "failed to inspect size of the image: %s, %v", out, err)
 
 	//now see if the size turns out to be the same
 	formatStr := fmt.Sprintf("--format={{eq .Size %d}}", size)
 	out, _ = dockerCmd(c, "inspect", formatStr, imageTest)
 	result, err := strconv.ParseBool(strings.TrimSuffix(out, "\n"))
-	c.Assert(err, checker.IsNil)
-	c.Assert(result, checker.Equals, true)
+	assert.NilError(c, err)
+	assert.Equal(c, result, true)
 }
 
-func (s *DockerSuite) TestInspectContainerFilterInt(c *check.C) {
+func (s *DockerSuite) TestInspectContainerFilterInt(c *testing.T) {
 	result := icmd.RunCmd(icmd.Cmd{
 		Command: []string{dockerBinary, "run", "-i", "-a", "stdin", "busybox", "cat"},
 		Stdin:   strings.NewReader("blahblah"),
@@ -150,17 +151,17 @@ func (s *DockerSuite) TestInspectContainerFilterInt(c *check.C) {
 	out = inspectField(c, id, "State.ExitCode")
 
 	exitCode, err := strconv.Atoi(out)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect exitcode of the container: %s, %v", out, err))
+	assert.Assert(c, err == nil, "failed to inspect exitcode of the container: %s, %v", out, err)
 
 	//now get the exit code to verify
 	formatStr := fmt.Sprintf("--format={{eq .State.ExitCode %d}}", exitCode)
 	out, _ = dockerCmd(c, "inspect", formatStr, id)
 	inspectResult, err := strconv.ParseBool(strings.TrimSuffix(out, "\n"))
-	c.Assert(err, checker.IsNil)
-	c.Assert(inspectResult, checker.Equals, true)
+	assert.NilError(c, err)
+	assert.Equal(c, inspectResult, true)
 }
 
-func (s *DockerSuite) TestInspectImageGraphDriver(c *check.C) {
+func (s *DockerSuite) TestInspectImageGraphDriver(c *testing.T) {
 	testRequires(c, DaemonIsLinux, Devicemapper)
 	imageTest := "emptyfs"
 	name := inspectField(c, imageTest, "GraphDriver.Name")
@@ -170,15 +171,15 @@ func (s *DockerSuite) TestInspectImageGraphDriver(c *check.C) {
 	deviceID := inspectField(c, imageTest, "GraphDriver.Data.DeviceId")
 
 	_, err := strconv.Atoi(deviceID)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect DeviceId of the image: %s, %v", deviceID, err))
+	assert.Assert(c, err == nil, "failed to inspect DeviceId of the image: %s, %v", deviceID, err)
 
 	deviceSize := inspectField(c, imageTest, "GraphDriver.Data.DeviceSize")
 
 	_, err = strconv.ParseUint(deviceSize, 10, 64)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect DeviceSize of the image: %s, %v", deviceSize, err))
+	assert.Assert(c, err == nil, "failed to inspect DeviceSize of the image: %s, %v", deviceSize, err)
 }
 
-func (s *DockerSuite) TestInspectContainerGraphDriver(c *check.C) {
+func (s *DockerSuite) TestInspectContainerGraphDriver(c *testing.T) {
 	testRequires(c, DaemonIsLinux, Devicemapper)
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
@@ -192,18 +193,18 @@ func (s *DockerSuite) TestInspectContainerGraphDriver(c *check.C) {
 
 	deviceID := inspectField(c, out, "GraphDriver.Data.DeviceId")
 
-	c.Assert(imageDeviceID, checker.Not(checker.Equals), deviceID)
+	assert.Assert(c, imageDeviceID != deviceID)
 
 	_, err := strconv.Atoi(deviceID)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect DeviceId of the image: %s, %v", deviceID, err))
+	assert.Assert(c, err == nil, "failed to inspect DeviceId of the image: %s, %v", deviceID, err)
 
 	deviceSize := inspectField(c, out, "GraphDriver.Data.DeviceSize")
 
 	_, err = strconv.ParseUint(deviceSize, 10, 64)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect DeviceSize of the image: %s, %v", deviceSize, err))
+	assert.Assert(c, err == nil, "failed to inspect DeviceSize of the image: %s, %v", deviceSize, err)
 }
 
-func (s *DockerSuite) TestInspectBindMountPoint(c *check.C) {
+func (s *DockerSuite) TestInspectBindMountPoint(c *testing.T) {
 	modifier := ",z"
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	if testEnv.OSType == "windows" {
@@ -218,24 +219,24 @@ func (s *DockerSuite) TestInspectBindMountPoint(c *check.C) {
 
 	var mp []types.MountPoint
 	err := json.Unmarshal([]byte(vol), &mp)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// check that there is only one mountpoint
-	c.Assert(mp, check.HasLen, 1)
+	assert.Equal(c, len(mp), 1)
 
 	m := mp[0]
 
-	c.Assert(m.Name, checker.Equals, "")
-	c.Assert(m.Driver, checker.Equals, "")
-	c.Assert(m.Source, checker.Equals, prefix+slash+"data")
-	c.Assert(m.Destination, checker.Equals, prefix+slash+"data")
+	assert.Equal(c, m.Name, "")
+	assert.Equal(c, m.Driver, "")
+	assert.Equal(c, m.Source, prefix+slash+"data")
+	assert.Equal(c, m.Destination, prefix+slash+"data")
 	if testEnv.OSType != "windows" { // Windows does not set mode
-		c.Assert(m.Mode, checker.Equals, "ro"+modifier)
+		assert.Equal(c, m.Mode, "ro"+modifier)
 	}
-	c.Assert(m.RW, checker.Equals, false)
+	assert.Equal(c, m.RW, false)
 }
 
-func (s *DockerSuite) TestInspectNamedMountPoint(c *check.C) {
+func (s *DockerSuite) TestInspectNamedMountPoint(c *testing.T) {
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 
 	dockerCmd(c, "run", "-d", "--name", "test", "-v", "data:"+prefix+slash+"data", "busybox", "cat")
@@ -244,22 +245,22 @@ func (s *DockerSuite) TestInspectNamedMountPoint(c *check.C) {
 
 	var mp []types.MountPoint
 	err := json.Unmarshal([]byte(vol), &mp)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// check that there is only one mountpoint
-	c.Assert(mp, checker.HasLen, 1)
+	assert.Equal(c, len(mp), 1)
 
 	m := mp[0]
 
-	c.Assert(m.Name, checker.Equals, "data")
-	c.Assert(m.Driver, checker.Equals, "local")
-	c.Assert(m.Source, checker.Not(checker.Equals), "")
-	c.Assert(m.Destination, checker.Equals, prefix+slash+"data")
-	c.Assert(m.RW, checker.Equals, true)
+	assert.Equal(c, m.Name, "data")
+	assert.Equal(c, m.Driver, "local")
+	assert.Assert(c, m.Source != "")
+	assert.Equal(c, m.Destination, prefix+slash+"data")
+	assert.Equal(c, m.RW, true)
 }
 
 // #14947
-func (s *DockerSuite) TestInspectTimesAsRFC3339Nano(c *check.C) {
+func (s *DockerSuite) TestInspectTimesAsRFC3339Nano(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
 	id := strings.TrimSpace(out)
 	startedAt := inspectField(c, id, "State.StartedAt")
@@ -267,194 +268,190 @@ func (s *DockerSuite) TestInspectTimesAsRFC3339Nano(c *check.C) {
 	created := inspectField(c, id, "Created")
 
 	_, err := time.Parse(time.RFC3339Nano, startedAt)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	_, err = time.Parse(time.RFC3339Nano, finishedAt)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	_, err = time.Parse(time.RFC3339Nano, created)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	created = inspectField(c, "busybox", "Created")
 
 	_, err = time.Parse(time.RFC3339Nano, created)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
 
 // #15633
-func (s *DockerSuite) TestInspectLogConfigNoType(c *check.C) {
+func (s *DockerSuite) TestInspectLogConfigNoType(c *testing.T) {
 	dockerCmd(c, "create", "--name=test", "--log-opt", "max-file=42", "busybox")
 	var logConfig container.LogConfig
 
 	out := inspectFieldJSON(c, "test", "HostConfig.LogConfig")
 
 	err := json.NewDecoder(strings.NewReader(out)).Decode(&logConfig)
-	c.Assert(err, checker.IsNil, check.Commentf("%v", out))
+	assert.Assert(c, err == nil, "%v", out)
 
-	c.Assert(logConfig.Type, checker.Equals, "json-file")
-	c.Assert(logConfig.Config["max-file"], checker.Equals, "42", check.Commentf("%v", logConfig))
+	assert.Equal(c, logConfig.Type, "json-file")
+	assert.Equal(c, logConfig.Config["max-file"], "42", fmt.Sprintf("%v", logConfig))
 }
 
-func (s *DockerSuite) TestInspectNoSizeFlagContainer(c *check.C) {
-
-	//Both the container and image are named busybox. docker inspect will fetch container
-	//JSON SizeRw and SizeRootFs field. If there is no flag --size/-s, there are no size fields.
+func (s *DockerSuite) TestInspectNoSizeFlagContainer(c *testing.T) {
+	// Both the container and image are named busybox. docker inspect will fetch container
+	// JSON SizeRw and SizeRootFs field. If there is no flag --size/-s, there are no size fields.
 
 	runSleepingContainer(c, "--name=busybox", "-d")
 
 	formatStr := "--format={{.SizeRw}},{{.SizeRootFs}}"
 	out, _ := dockerCmd(c, "inspect", "--type=container", formatStr, "busybox")
-	c.Assert(strings.TrimSpace(out), check.Equals, ",", check.Commentf("Expected not to display size info: %s", out))
+	assert.Equal(c, strings.TrimSpace(out), ",", fmt.Sprintf("Expected not to display size info: %s", out))
 }
 
-func (s *DockerSuite) TestInspectSizeFlagContainer(c *check.C) {
+func (s *DockerSuite) TestInspectSizeFlagContainer(c *testing.T) {
 	runSleepingContainer(c, "--name=busybox", "-d")
 
 	formatStr := "--format='{{.SizeRw}},{{.SizeRootFs}}'"
 	out, _ := dockerCmd(c, "inspect", "-s", "--type=container", formatStr, "busybox")
 	sz := strings.Split(out, ",")
 
-	c.Assert(strings.TrimSpace(sz[0]), check.Not(check.Equals), "")
-	c.Assert(strings.TrimSpace(sz[1]), check.Not(check.Equals), "")
+	assert.Assert(c, strings.TrimSpace(sz[0]) != "")
+	assert.Assert(c, strings.TrimSpace(sz[1]) != "")
 }
 
-func (s *DockerSuite) TestInspectTemplateError(c *check.C) {
+func (s *DockerSuite) TestInspectTemplateError(c *testing.T) {
 	// Template parsing error for both the container and image.
 
 	runSleepingContainer(c, "--name=container1", "-d")
 
 	out, _, err := dockerCmdWithError("inspect", "--type=container", "--format='Format container: {{.ThisDoesNotExist}}'", "container1")
-	c.Assert(err, check.Not(check.IsNil))
-	c.Assert(out, checker.Contains, "Template parsing error")
-
+	assert.Assert(c, err != nil)
+	assert.Assert(c, strings.Contains(out, "Template parsing error"))
 	out, _, err = dockerCmdWithError("inspect", "--type=image", "--format='Format container: {{.ThisDoesNotExist}}'", "busybox")
-	c.Assert(err, check.Not(check.IsNil))
-	c.Assert(out, checker.Contains, "Template parsing error")
+	assert.Assert(c, err != nil)
+	assert.Assert(c, strings.Contains(out, "Template parsing error"))
 }
 
-func (s *DockerSuite) TestInspectJSONFields(c *check.C) {
+func (s *DockerSuite) TestInspectJSONFields(c *testing.T) {
 	runSleepingContainer(c, "--name=busybox", "-d")
 	out, _, err := dockerCmdWithError("inspect", "--type=container", "--format={{.HostConfig.Dns}}", "busybox")
 
-	c.Assert(err, check.IsNil)
-	c.Assert(out, checker.Equals, "[]\n")
+	assert.NilError(c, err)
+	assert.Equal(c, out, "[]\n")
 }
 
-func (s *DockerSuite) TestInspectByPrefix(c *check.C) {
+func (s *DockerSuite) TestInspectByPrefix(c *testing.T) {
 	id := inspectField(c, "busybox", "Id")
-	c.Assert(id, checker.HasPrefix, "sha256:")
+	assert.Assert(c, strings.HasPrefix(id, "sha256:"))
 
 	id2 := inspectField(c, id[:12], "Id")
-	c.Assert(id, checker.Equals, id2)
+	assert.Equal(c, id, id2)
 
 	id3 := inspectField(c, strings.TrimPrefix(id, "sha256:")[:12], "Id")
-	c.Assert(id, checker.Equals, id3)
+	assert.Equal(c, id, id3)
 }
 
-func (s *DockerSuite) TestInspectStopWhenNotFound(c *check.C) {
+func (s *DockerSuite) TestInspectStopWhenNotFound(c *testing.T) {
 	runSleepingContainer(c, "--name=busybox1", "-d")
 	runSleepingContainer(c, "--name=busybox2", "-d")
 	result := dockerCmdWithResult("inspect", "--type=container", "--format='{{.Name}}'", "busybox1", "busybox2", "missing")
 
-	c.Assert(result.Error, checker.Not(check.IsNil))
-	c.Assert(result.Stdout(), checker.Contains, "busybox1")
-	c.Assert(result.Stdout(), checker.Contains, "busybox2")
-	c.Assert(result.Stderr(), checker.Contains, "Error: No such container: missing")
-
+	assert.Assert(c, result.Error != nil)
+	assert.Assert(c, strings.Contains(result.Stdout(), "busybox1"))
+	assert.Assert(c, strings.Contains(result.Stdout(), "busybox2"))
+	assert.Assert(c, strings.Contains(result.Stderr(), "Error: No such container: missing"))
 	// test inspect would not fast fail
 	result = dockerCmdWithResult("inspect", "--type=container", "--format='{{.Name}}'", "missing", "busybox1", "busybox2")
 
-	c.Assert(result.Error, checker.Not(check.IsNil))
-	c.Assert(result.Stdout(), checker.Contains, "busybox1")
-	c.Assert(result.Stdout(), checker.Contains, "busybox2")
-	c.Assert(result.Stderr(), checker.Contains, "Error: No such container: missing")
+	assert.Assert(c, result.Error != nil)
+	assert.Assert(c, strings.Contains(result.Stdout(), "busybox1"))
+	assert.Assert(c, strings.Contains(result.Stdout(), "busybox2"))
+	assert.Assert(c, strings.Contains(result.Stderr(), "Error: No such container: missing"))
 }
 
-func (s *DockerSuite) TestInspectHistory(c *check.C) {
+func (s *DockerSuite) TestInspectHistory(c *testing.T) {
 	dockerCmd(c, "run", "--name=testcont", "busybox", "echo", "hello")
 	dockerCmd(c, "commit", "-m", "test comment", "testcont", "testimg")
 	out, _, err := dockerCmdWithError("inspect", "--format='{{.Comment}}'", "testimg")
-	c.Assert(err, check.IsNil)
-	c.Assert(out, checker.Contains, "test comment")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, "test comment"))
 }
 
-func (s *DockerSuite) TestInspectContainerNetworkDefault(c *check.C) {
+func (s *DockerSuite) TestInspectContainerNetworkDefault(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	contName := "test1"
 	dockerCmd(c, "run", "--name", contName, "-d", "busybox", "top")
 	netOut, _ := dockerCmd(c, "network", "inspect", "--format={{.ID}}", "bridge")
 	out := inspectField(c, contName, "NetworkSettings.Networks")
-	c.Assert(out, checker.Contains, "bridge")
+	assert.Assert(c, strings.Contains(out, "bridge"))
 	out = inspectField(c, contName, "NetworkSettings.Networks.bridge.NetworkID")
-	c.Assert(strings.TrimSpace(out), checker.Equals, strings.TrimSpace(netOut))
+	assert.Equal(c, strings.TrimSpace(out), strings.TrimSpace(netOut))
 }
 
-func (s *DockerSuite) TestInspectContainerNetworkCustom(c *check.C) {
+func (s *DockerSuite) TestInspectContainerNetworkCustom(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	netOut, _ := dockerCmd(c, "network", "create", "net1")
 	dockerCmd(c, "run", "--name=container1", "--net=net1", "-d", "busybox", "top")
 	out := inspectField(c, "container1", "NetworkSettings.Networks")
-	c.Assert(out, checker.Contains, "net1")
+	assert.Assert(c, strings.Contains(out, "net1"))
 	out = inspectField(c, "container1", "NetworkSettings.Networks.net1.NetworkID")
-	c.Assert(strings.TrimSpace(out), checker.Equals, strings.TrimSpace(netOut))
+	assert.Equal(c, strings.TrimSpace(out), strings.TrimSpace(netOut))
 }
 
-func (s *DockerSuite) TestInspectRootFS(c *check.C) {
+func (s *DockerSuite) TestInspectRootFS(c *testing.T) {
 	out, _, err := dockerCmdWithError("inspect", "busybox")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	var imageJSON []types.ImageInspect
 	err = json.Unmarshal([]byte(out), &imageJSON)
-	c.Assert(err, checker.IsNil)
-
-	c.Assert(len(imageJSON[0].RootFS.Layers), checker.GreaterOrEqualThan, 1)
+	assert.NilError(c, err)
+	assert.Assert(c, len(imageJSON[0].RootFS.Layers) >= 1)
 }
 
-func (s *DockerSuite) TestInspectAmpersand(c *check.C) {
+func (s *DockerSuite) TestInspectAmpersand(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	name := "test"
 	out, _ := dockerCmd(c, "run", "--name", name, "--env", `TEST_ENV="soanni&rtr"`, "busybox", "env")
-	c.Assert(out, checker.Contains, `soanni&rtr`)
+	assert.Assert(c, strings.Contains(out, `soanni&rtr`))
 	out, _ = dockerCmd(c, "inspect", name)
-	c.Assert(out, checker.Contains, `soanni&rtr`)
+	assert.Assert(c, strings.Contains(out, `soanni&rtr`))
 }
 
-func (s *DockerSuite) TestInspectPlugin(c *check.C) {
+func (s *DockerSuite) TestInspectPlugin(c *testing.T) {
 	testRequires(c, DaemonIsLinux, IsAmd64, Network)
 	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err := dockerCmdWithError("inspect", "--type", "plugin", "--format", "{{.Name}}", pNameWithTag)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, pNameWithTag)
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), pNameWithTag)
 
 	out, _, err = dockerCmdWithError("inspect", "--format", "{{.Name}}", pNameWithTag)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, pNameWithTag)
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), pNameWithTag)
 
 	// Even without tag the inspect still work
 	out, _, err = dockerCmdWithError("inspect", "--type", "plugin", "--format", "{{.Name}}", pNameWithTag)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, pNameWithTag)
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), pNameWithTag)
 
 	out, _, err = dockerCmdWithError("inspect", "--format", "{{.Name}}", pNameWithTag)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, pNameWithTag)
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), pNameWithTag)
 
 	_, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, pNameWithTag)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, pNameWithTag))
 }
 
 // Test case for 29185
-func (s *DockerSuite) TestInspectUnknownObject(c *check.C) {
+func (s *DockerSuite) TestInspectUnknownObject(c *testing.T) {
 	// This test should work on both Windows and Linux
 	out, _, err := dockerCmdWithError("inspect", "foobar")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "Error: No such object: foobar")
-	c.Assert(err.Error(), checker.Contains, "Error: No such object: foobar")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Error: No such object: foobar"))
+	assert.ErrorContains(c, err, "Error: No such object: foobar")
 }
diff --git a/integration-cli/docker_cli_links_test.go b/integration-cli/docker_cli_links_test.go
index 17b25d7994ce1..0131233e6a6ea 100644
--- a/integration-cli/docker_cli_links_test.go
+++ b/integration-cli/docker_cli_links_test.go
@@ -6,34 +6,35 @@ import (
 	"regexp"
 	"sort"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/runconfig"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/assert/cmp"
 )
 
-func (s *DockerSuite) TestLinksPingUnlinkedContainers(c *check.C) {
+func (s *DockerSuite) TestLinksPingUnlinkedContainers(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	_, exitCode, err := dockerCmdWithError("run", "--rm", "busybox", "sh", "-c", "ping -c 1 alias1 -W 1 && ping -c 1 alias2 -W 1")
 
 	// run ping failed with error
-	c.Assert(exitCode, checker.Equals, 1, check.Commentf("error: %v", err))
+	assert.Equal(c, exitCode, 1, fmt.Sprintf("error: %v", err))
 }
 
 // Test for appropriate error when calling --link with an invalid target container
-func (s *DockerSuite) TestLinksInvalidContainerTarget(c *check.C) {
+func (s *DockerSuite) TestLinksInvalidContainerTarget(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "--link", "bogus:alias", "busybox", "true")
 
 	// an invalid container target should produce an error
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+	assert.Assert(c, err != nil, "out: %s", out)
 	// an invalid container target should produce an error
 	// note: convert the output to lowercase first as the error string
 	// capitalization was changed after API version 1.32
-	c.Assert(strings.ToLower(out), checker.Contains, "could not get container")
+	assert.Assert(c, strings.Contains(strings.ToLower(out), "could not get container"))
 }
 
-func (s *DockerSuite) TestLinksPingLinkedContainers(c *check.C) {
+func (s *DockerSuite) TestLinksPingLinkedContainers(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	// Test with the three different ways of specifying the default network on Linux
 	testLinkPingOnNetwork(c, "")
@@ -41,7 +42,7 @@ func (s *DockerSuite) TestLinksPingLinkedContainers(c *check.C) {
 	testLinkPingOnNetwork(c, "bridge")
 }
 
-func testLinkPingOnNetwork(c *check.C, network string) {
+func testLinkPingOnNetwork(c *testing.T, network string) {
 	var postArgs []string
 	if network != "" {
 		postArgs = append(postArgs, []string{"--net", network}...)
@@ -77,7 +78,7 @@ func testLinkPingOnNetwork(c *check.C, network string) {
 	dockerCmd(c, "rm", "-f", "container2")
 }
 
-func (s *DockerSuite) TestLinksPingLinkedContainersAfterRename(c *check.C) {
+func (s *DockerSuite) TestLinksPingLinkedContainersAfterRename(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top")
 	idA := strings.TrimSpace(out)
@@ -90,7 +91,7 @@ func (s *DockerSuite) TestLinksPingLinkedContainersAfterRename(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestLinksInspectLinksStarted(c *check.C) {
+func (s *DockerSuite) TestLinksInspectLinksStarted(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top")
 	dockerCmd(c, "run", "-d", "--name", "container2", "busybox", "top")
@@ -99,17 +100,17 @@ func (s *DockerSuite) TestLinksInspectLinksStarted(c *check.C) {
 
 	var result []string
 	err := json.Unmarshal([]byte(links), &result)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var expected = []string{
 		"/container1:/testinspectlink/alias1",
 		"/container2:/testinspectlink/alias2",
 	}
 	sort.Strings(result)
-	c.Assert(result, checker.DeepEquals, expected)
+	assert.DeepEqual(c, result, expected)
 }
 
-func (s *DockerSuite) TestLinksInspectLinksStopped(c *check.C) {
+func (s *DockerSuite) TestLinksInspectLinksStopped(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top")
@@ -119,17 +120,17 @@ func (s *DockerSuite) TestLinksInspectLinksStopped(c *check.C) {
 
 	var result []string
 	err := json.Unmarshal([]byte(links), &result)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var expected = []string{
 		"/container1:/testinspectlink/alias1",
 		"/container2:/testinspectlink/alias2",
 	}
 	sort.Strings(result)
-	c.Assert(result, checker.DeepEquals, expected)
+	assert.DeepEqual(c, result, expected)
 }
 
-func (s *DockerSuite) TestLinksNotStartedParentNotFail(c *check.C) {
+func (s *DockerSuite) TestLinksNotStartedParentNotFail(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "create", "--name=first", "busybox", "top")
 	dockerCmd(c, "create", "--name=second", "--link=first:first", "busybox", "top")
@@ -137,9 +138,9 @@ func (s *DockerSuite) TestLinksNotStartedParentNotFail(c *check.C) {
 
 }
 
-func (s *DockerSuite) TestLinksHostsFilesInject(c *check.C) {
+func (s *DockerSuite) TestLinksHostsFilesInject(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
-	testRequires(c, SameHostDaemon, ExecSupport)
+	testRequires(c, testEnv.IsLocalDaemon)
 
 	out, _ := dockerCmd(c, "run", "-itd", "--name", "one", "busybox", "top")
 	idOne := strings.TrimSpace(out)
@@ -147,20 +148,20 @@ func (s *DockerSuite) TestLinksHostsFilesInject(c *check.C) {
 	out, _ = dockerCmd(c, "run", "-itd", "--name", "two", "--link", "one:onetwo", "busybox", "top")
 	idTwo := strings.TrimSpace(out)
 
-	c.Assert(waitRun(idTwo), checker.IsNil)
+	assert.Assert(c, waitRun(idTwo) == nil)
 
 	readContainerFileWithExec(c, idOne, "/etc/hosts")
 	contentTwo := readContainerFileWithExec(c, idTwo, "/etc/hosts")
 	// Host is not present in updated hosts file
-	c.Assert(string(contentTwo), checker.Contains, "onetwo")
+	assert.Assert(c, strings.Contains(string(contentTwo), "onetwo"))
 }
 
-func (s *DockerSuite) TestLinksUpdateOnRestart(c *check.C) {
+func (s *DockerSuite) TestLinksUpdateOnRestart(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
-	testRequires(c, SameHostDaemon, ExecSupport)
+	testRequires(c, testEnv.IsLocalDaemon)
 	dockerCmd(c, "run", "-d", "--name", "one", "busybox", "top")
 	out, _ := dockerCmd(c, "run", "-d", "--name", "two", "--link", "one:onetwo", "--link", "one:one", "busybox", "top")
-	id := strings.TrimSpace(string(out))
+	id := strings.TrimSpace(out)
 
 	realIP := inspectField(c, "one", "NetworkSettings.Networks.bridge.IPAddress")
 	content := readContainerFileWithExec(c, id, "/etc/hosts")
@@ -168,70 +169,70 @@ func (s *DockerSuite) TestLinksUpdateOnRestart(c *check.C) {
 	getIP := func(hosts []byte, hostname string) string {
 		re := regexp.MustCompile(fmt.Sprintf(`(\S*)\t%s`, regexp.QuoteMeta(hostname)))
 		matches := re.FindSubmatch(hosts)
-		c.Assert(matches, checker.NotNil, check.Commentf("Hostname %s have no matches in hosts", hostname))
+		assert.Assert(c, matches != nil, "Hostname %s have no matches in hosts", hostname)
 		return string(matches[1])
 	}
 	ip := getIP(content, "one")
-	c.Assert(ip, checker.Equals, realIP)
+	assert.Equal(c, ip, realIP)
 
 	ip = getIP(content, "onetwo")
-	c.Assert(ip, checker.Equals, realIP)
+	assert.Equal(c, ip, realIP)
 
 	dockerCmd(c, "restart", "one")
 	realIP = inspectField(c, "one", "NetworkSettings.Networks.bridge.IPAddress")
 
 	content = readContainerFileWithExec(c, id, "/etc/hosts")
 	ip = getIP(content, "one")
-	c.Assert(ip, checker.Equals, realIP)
+	assert.Equal(c, ip, realIP)
 
 	ip = getIP(content, "onetwo")
-	c.Assert(ip, checker.Equals, realIP)
+	assert.Equal(c, ip, realIP)
 }
 
-func (s *DockerSuite) TestLinksEnvs(c *check.C) {
+func (s *DockerSuite) TestLinksEnvs(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "-e", "e1=", "-e", "e2=v2", "-e", "e3=v3=v3", "--name=first", "busybox", "top")
 	out, _ := dockerCmd(c, "run", "--name=second", "--link=first:first", "busybox", "env")
-	c.Assert(out, checker.Contains, "FIRST_ENV_e1=\n")
-	c.Assert(out, checker.Contains, "FIRST_ENV_e2=v2")
-	c.Assert(out, checker.Contains, "FIRST_ENV_e3=v3=v3")
+	assert.Assert(c, strings.Contains(out, "FIRST_ENV_e1=\n"))
+	assert.Assert(c, strings.Contains(out, "FIRST_ENV_e2=v2"))
+	assert.Assert(c, strings.Contains(out, "FIRST_ENV_e3=v3=v3"))
 }
 
-func (s *DockerSuite) TestLinkShortDefinition(c *check.C) {
+func (s *DockerSuite) TestLinkShortDefinition(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "--name", "shortlinkdef", "busybox", "top")
 
 	cid := strings.TrimSpace(out)
-	c.Assert(waitRun(cid), checker.IsNil)
+	assert.Assert(c, waitRun(cid) == nil)
 
 	out, _ = dockerCmd(c, "run", "-d", "--name", "link2", "--link", "shortlinkdef", "busybox", "top")
 
 	cid2 := strings.TrimSpace(out)
-	c.Assert(waitRun(cid2), checker.IsNil)
+	assert.Assert(c, waitRun(cid2) == nil)
 
 	links := inspectFieldJSON(c, cid2, "HostConfig.Links")
-	c.Assert(links, checker.Equals, "[\"/shortlinkdef:/link2/shortlinkdef\"]")
+	assert.Equal(c, links, "[\"/shortlinkdef:/link2/shortlinkdef\"]")
 }
 
-func (s *DockerSuite) TestLinksNetworkHostContainer(c *check.C) {
+func (s *DockerSuite) TestLinksNetworkHostContainer(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	dockerCmd(c, "run", "-d", "--net", "host", "--name", "host_container", "busybox", "top")
 	out, _, err := dockerCmdWithError("run", "--name", "should_fail", "--link", "host_container:tester", "busybox", "true")
 
 	// Running container linking to a container with --net host should have failed
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+	assert.Assert(c, err != nil, "out: %s", out)
 	// Running container linking to a container with --net host should have failed
-	c.Assert(out, checker.Contains, runconfig.ErrConflictHostNetworkAndLinks.Error())
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetworkAndLinks.Error()))
 }
 
-func (s *DockerSuite) TestLinksEtcHostsRegularFile(c *check.C) {
+func (s *DockerSuite) TestLinksEtcHostsRegularFile(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	out, _ := dockerCmd(c, "run", "--net=host", "busybox", "ls", "-la", "/etc/hosts")
 	// /etc/hosts should be a regular file
-	c.Assert(out, checker.Matches, "^-.+\n")
+	assert.Assert(c, cmp.Regexp("^-.+\n$", out))
 }
 
-func (s *DockerSuite) TestLinksMultipleWithSameName(c *check.C) {
+func (s *DockerSuite) TestLinksMultipleWithSameName(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "--name=upstream-a", "busybox", "top")
 	dockerCmd(c, "run", "-d", "--name=upstream-b", "busybox", "top")
diff --git a/integration-cli/docker_cli_login_test.go b/integration-cli/docker_cli_login_test.go
index 546f55c0b91c6..04fb9eaa30bd7 100644
--- a/integration-cli/docker_cli_login_test.go
+++ b/integration-cli/docker_cli_login_test.go
@@ -3,12 +3,13 @@ package main
 import (
 	"bytes"
 	"os/exec"
+	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestLoginWithoutTTY(c *check.C) {
+func (s *DockerSuite) TestLoginWithoutTTY(c *testing.T) {
 	cmd := exec.Command(dockerBinary, "login")
 
 	// Send to stdin so the process does not get the TTY
@@ -16,14 +17,14 @@ func (s *DockerSuite) TestLoginWithoutTTY(c *check.C) {
 
 	// run the command and block until it's done
 	err := cmd.Run()
-	c.Assert(err, checker.NotNil) //"Expected non nil err when logging in & TTY not available"
+	assert.ErrorContains(c, err, "") //"Expected non nil err when logging in & TTY not available"
 }
 
-func (s *DockerRegistryAuthHtpasswdSuite) TestLoginToPrivateRegistry(c *check.C) {
+func (s *DockerRegistryAuthHtpasswdSuite) TestLoginToPrivateRegistry(c *testing.T) {
 	// wrong credentials
 	out, _, err := dockerCmdWithError("login", "-u", s.reg.Username(), "-p", "WRONGPASSWORD", privateRegistryURL)
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "401 Unauthorized")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "401 Unauthorized"))
 
 	// now it's fine
 	dockerCmd(c, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL)
diff --git a/integration-cli/docker_cli_logout_test.go b/integration-cli/docker_cli_logout_test.go
index ee4b032b76c54..37d5a2742ee34 100644
--- a/integration-cli/docker_cli_logout_test.go
+++ b/integration-cli/docker_cli_logout_test.go
@@ -3,75 +3,75 @@ package main
 import (
 	"bytes"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
+	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerRegistryAuthHtpasswdSuite) TestLogoutWithExternalAuth(c *check.C) {
+func (s *DockerRegistryAuthHtpasswdSuite) TestLogoutWithExternalAuth(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	osPath := os.Getenv("PATH")
 	defer os.Setenv("PATH", osPath)
 
 	workingDir, err := os.Getwd()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute)
 
 	os.Setenv("PATH", testPath)
 
 	repoName := fmt.Sprintf("%v/dockercli/busybox:authtest", privateRegistryURL)
 
-	tmp, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, checker.IsNil)
+	tmp, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmp)
 
 	externalAuthConfig := `{ "credsStore": "shell-test" }`
 
 	configPath := filepath.Join(tmp, "config.json")
-	err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(configPath, []byte(externalAuthConfig), 0644)
+	assert.NilError(c, err)
 
 	_, err = s.d.Cmd("--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	b, err := ioutil.ReadFile(configPath)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":")
-	c.Assert(string(b), checker.Contains, privateRegistryURL)
+	b, err := os.ReadFile(configPath)
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(string(b), `"auth":`))
+	assert.Assert(c, strings.Contains(string(b), privateRegistryURL))
 
 	_, err = s.d.Cmd("--config", tmp, "tag", "busybox", repoName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	_, err = s.d.Cmd("--config", tmp, "push", repoName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	_, err = s.d.Cmd("--config", tmp, "logout", privateRegistryURL)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	b, err = ioutil.ReadFile(configPath)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b), checker.Not(checker.Contains), privateRegistryURL)
+	b, err = os.ReadFile(configPath)
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(string(b), privateRegistryURL))
 
 	// check I cannot pull anymore
 	out, err := s.d.Cmd("--config", tmp, "pull", repoName)
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "no basic auth credentials")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "no basic auth credentials"))
 }
 
 // #23100
-func (s *DockerRegistryAuthHtpasswdSuite) TestLogoutWithWrongHostnamesStored(c *check.C) {
+func (s *DockerRegistryAuthHtpasswdSuite) TestLogoutWithWrongHostnamesStored(c *testing.T) {
 	osPath := os.Getenv("PATH")
 	defer os.Setenv("PATH", osPath)
 
 	workingDir, err := os.Getwd()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute)
 
 	os.Setenv("PATH", testPath)
@@ -79,28 +79,28 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestLogoutWithWrongHostnamesStored(c *
 	cmd := exec.Command("docker-credential-shell-test", "store")
 	stdin := bytes.NewReader([]byte(fmt.Sprintf(`{"ServerURL": "https://%s", "Username": "%s", "Secret": "%s"}`, privateRegistryURL, s.reg.Username(), s.reg.Password())))
 	cmd.Stdin = stdin
-	c.Assert(cmd.Run(), checker.IsNil)
+	assert.NilError(c, cmd.Run())
 
-	tmp, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, checker.IsNil)
+	tmp, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 
 	externalAuthConfig := fmt.Sprintf(`{ "auths": {"https://%s": {}}, "credsStore": "shell-test" }`, privateRegistryURL)
 
 	configPath := filepath.Join(tmp, "config.json")
-	err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(configPath, []byte(externalAuthConfig), 0644)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL)
 
-	b, err := ioutil.ReadFile(configPath)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b), checker.Contains, fmt.Sprintf("\"https://%s\": {}", privateRegistryURL))
-	c.Assert(string(b), checker.Contains, fmt.Sprintf("\"%s\": {}", privateRegistryURL))
+	b, err := os.ReadFile(configPath)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(b), fmt.Sprintf(`"https://%s": {}`, privateRegistryURL)))
+	assert.Assert(c, strings.Contains(string(b), fmt.Sprintf(`"%s": {}`, privateRegistryURL)))
 
 	dockerCmd(c, "--config", tmp, "logout", privateRegistryURL)
 
-	b, err = ioutil.ReadFile(configPath)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b), checker.Not(checker.Contains), fmt.Sprintf("\"https://%s\": {}", privateRegistryURL))
-	c.Assert(string(b), checker.Not(checker.Contains), fmt.Sprintf("\"%s\": {}", privateRegistryURL))
+	b, err = os.ReadFile(configPath)
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(string(b), fmt.Sprintf(`"https://%s": {}`, privateRegistryURL)))
+	assert.Assert(c, !strings.Contains(string(b), fmt.Sprintf(`"%s": {}`, privateRegistryURL)))
 }
diff --git a/integration-cli/docker_cli_logs_bench_test.go b/integration-cli/docker_cli_logs_bench_test.go
index eeb008de70198..4702b2a4b913c 100644
--- a/integration-cli/docker_cli_logs_bench_test.go
+++ b/integration-cli/docker_cli_logs_bench_test.go
@@ -3,12 +3,11 @@ package main
 import (
 	"fmt"
 	"strings"
+	"testing"
 	"time"
-
-	"github.com/go-check/check"
 )
 
-func (s *DockerSuite) BenchmarkLogsCLIRotateFollow(c *check.C) {
+func (s *DockerSuite) BenchmarkLogsCLIRotateFollow(c *testing.B) {
 	out, _ := dockerCmd(c, "run", "-d", "--log-opt", "max-size=1b", "--log-opt", "max-file=10", "busybox", "sh", "-c", "while true; do usleep 50000; echo hello; done")
 	id := strings.TrimSpace(out)
 	ch := make(chan error, 1)
diff --git a/integration-cli/docker_cli_logs_test.go b/integration-cli/docker_cli_logs_test.go
index 2740de6f0665c..fe41f0351b2e9 100644
--- a/integration-cli/docker_cli_logs_test.go
+++ b/integration-cli/docker_cli_logs_test.go
@@ -6,39 +6,39 @@ import (
 	"os/exec"
 	"regexp"
 	"strings"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/pkg/jsonmessage"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
 // This used to work, it test a log of PageSize-1 (gh#4851)
-func (s *DockerSuite) TestLogsContainerSmallerThanPage(c *check.C) {
+func (s *DockerSuite) TestLogsContainerSmallerThanPage(c *testing.T) {
 	testLogsContainerPagination(c, 32767)
 }
 
 // Regression test: When going over the PageSize, it used to panic (gh#4851)
-func (s *DockerSuite) TestLogsContainerBiggerThanPage(c *check.C) {
+func (s *DockerSuite) TestLogsContainerBiggerThanPage(c *testing.T) {
 	testLogsContainerPagination(c, 32768)
 }
 
 // Regression test: When going much over the PageSize, it used to block (gh#4851)
-func (s *DockerSuite) TestLogsContainerMuchBiggerThanPage(c *check.C) {
+func (s *DockerSuite) TestLogsContainerMuchBiggerThanPage(c *testing.T) {
 	testLogsContainerPagination(c, 33000)
 }
 
-func testLogsContainerPagination(c *check.C, testLen int) {
+func testLogsContainerPagination(c *testing.T, testLen int) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", fmt.Sprintf("for i in $(seq 1 %d); do echo -n = >> a.a; done; echo >> a.a; cat a.a", testLen))
 	id := strings.TrimSpace(out)
 	dockerCmd(c, "wait", id)
 	out, _ = dockerCmd(c, "logs", id)
-	c.Assert(out, checker.HasLen, testLen+1)
+	assert.Equal(c, len(out), testLen+1)
 }
 
-func (s *DockerSuite) TestLogsTimestamps(c *check.C) {
+func (s *DockerSuite) TestLogsTimestamps(c *testing.T) {
 	testLen := 100
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", fmt.Sprintf("for i in $(seq 1 %d); do echo = >> a.a; done; cat a.a", testLen))
 
@@ -49,21 +49,21 @@ func (s *DockerSuite) TestLogsTimestamps(c *check.C) {
 
 	lines := strings.Split(out, "\n")
 
-	c.Assert(lines, checker.HasLen, testLen+1)
+	assert.Equal(c, len(lines), testLen+1)
 
 	ts := regexp.MustCompile(`^.* `)
 
 	for _, l := range lines {
 		if l != "" {
 			_, err := time.Parse(jsonmessage.RFC3339NanoFixed+" ", ts.FindString(l))
-			c.Assert(err, checker.IsNil, check.Commentf("Failed to parse timestamp from %v", l))
+			assert.NilError(c, err, "Failed to parse timestamp from %v", l)
 			// ensure we have padded 0's
-			c.Assert(l[29], checker.Equals, uint8('Z'))
+			assert.Equal(c, l[29], uint8('Z'))
 		}
 	}
 }
 
-func (s *DockerSuite) TestLogsSeparateStderr(c *check.C) {
+func (s *DockerSuite) TestLogsSeparateStderr(c *testing.T) {
 	msg := "stderr_log"
 	out := cli.DockerCmd(c, "run", "-d", "busybox", "sh", "-c", fmt.Sprintf("echo %s 1>&2", msg)).Combined()
 	id := strings.TrimSpace(out)
@@ -74,7 +74,7 @@ func (s *DockerSuite) TestLogsSeparateStderr(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestLogsStderrInStdout(c *check.C) {
+func (s *DockerSuite) TestLogsStderrInStdout(c *testing.T) {
 	// TODO Windows: Needs investigation why this fails. Obtained string includes
 	// a bunch of ANSI escape sequences before the "stderr_log" message.
 	testRequires(c, DaemonIsLinux)
@@ -89,7 +89,7 @@ func (s *DockerSuite) TestLogsStderrInStdout(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestLogsTail(c *check.C) {
+func (s *DockerSuite) TestLogsTail(c *testing.T) {
 	testLen := 100
 	out := cli.DockerCmd(c, "run", "-d", "busybox", "sh", "-c", fmt.Sprintf("for i in $(seq 1 %d); do echo =; done;", testLen)).Combined()
 
@@ -98,37 +98,37 @@ func (s *DockerSuite) TestLogsTail(c *check.C) {
 
 	out = cli.DockerCmd(c, "logs", "--tail", "0", id).Combined()
 	lines := strings.Split(out, "\n")
-	c.Assert(lines, checker.HasLen, 1)
+	assert.Equal(c, len(lines), 1)
 
 	out = cli.DockerCmd(c, "logs", "--tail", "5", id).Combined()
 	lines = strings.Split(out, "\n")
-	c.Assert(lines, checker.HasLen, 6)
+	assert.Equal(c, len(lines), 6)
 
 	out = cli.DockerCmd(c, "logs", "--tail", "99", id).Combined()
 	lines = strings.Split(out, "\n")
-	c.Assert(lines, checker.HasLen, 100)
+	assert.Equal(c, len(lines), 100)
 
 	out = cli.DockerCmd(c, "logs", "--tail", "all", id).Combined()
 	lines = strings.Split(out, "\n")
-	c.Assert(lines, checker.HasLen, testLen+1)
+	assert.Equal(c, len(lines), testLen+1)
 
 	out = cli.DockerCmd(c, "logs", "--tail", "-1", id).Combined()
 	lines = strings.Split(out, "\n")
-	c.Assert(lines, checker.HasLen, testLen+1)
+	assert.Equal(c, len(lines), testLen+1)
 
 	out = cli.DockerCmd(c, "logs", "--tail", "random", id).Combined()
 	lines = strings.Split(out, "\n")
-	c.Assert(lines, checker.HasLen, testLen+1)
+	assert.Equal(c, len(lines), testLen+1)
 }
 
-func (s *DockerSuite) TestLogsFollowStopped(c *check.C) {
+func (s *DockerSuite) TestLogsFollowStopped(c *testing.T) {
 	dockerCmd(c, "run", "--name=test", "busybox", "echo", "hello")
 	id := getIDByName(c, "test")
 
 	logsCmd := exec.Command(dockerBinary, "logs", "-f", id)
-	c.Assert(logsCmd.Start(), checker.IsNil)
+	assert.NilError(c, logsCmd.Start())
 
-	errChan := make(chan error)
+	errChan := make(chan error, 1)
 	go func() {
 		errChan <- logsCmd.Wait()
 		close(errChan)
@@ -136,32 +136,32 @@ func (s *DockerSuite) TestLogsFollowStopped(c *check.C) {
 
 	select {
 	case err := <-errChan:
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(30 * time.Second):
 		c.Fatal("Following logs is hanged")
 	}
 }
 
-func (s *DockerSuite) TestLogsSince(c *check.C) {
+func (s *DockerSuite) TestLogsSince(c *testing.T) {
 	name := "testlogssince"
 	dockerCmd(c, "run", "--name="+name, "busybox", "/bin/sh", "-c", "for i in $(seq 1 3); do sleep 2; echo log$i; done")
 	out, _ := dockerCmd(c, "logs", "-t", name)
 
 	log2Line := strings.Split(strings.Split(out, "\n")[1], " ")
 	t, err := time.Parse(time.RFC3339Nano, log2Line[0]) // the timestamp log2 is written
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	since := t.Unix() + 1 // add 1s so log1 & log2 doesn't show up
 	out, _ = dockerCmd(c, "logs", "-t", fmt.Sprintf("--since=%v", since), name)
 
 	// Skip 2 seconds
 	unexpected := []string{"log1", "log2"}
 	for _, v := range unexpected {
-		c.Assert(out, checker.Not(checker.Contains), v, check.Commentf("unexpected log message returned, since=%v", since))
+		assert.Check(c, !strings.Contains(out, v), "unexpected log message returned, since=%v", since)
 	}
 
 	// Test to make sure a bad since format is caught by the client
 	out, _, _ = dockerCmdWithError("logs", "-t", "--since=2006-01-02T15:04:0Z", name)
-	c.Assert(out, checker.Contains, "cannot parse \"0Z\" as \"05\"", check.Commentf("bad since format passed to server"))
+	assert.Assert(c, strings.Contains(out, `cannot parse "0Z" as "05"`), "bad since format passed to server")
 
 	// Test with default value specified and parameter omitted
 	expected := []string{"log1", "log2", "log3"}
@@ -172,12 +172,12 @@ func (s *DockerSuite) TestLogsSince(c *check.C) {
 		result := icmd.RunCommand(dockerBinary, cmd...)
 		result.Assert(c, icmd.Success)
 		for _, v := range expected {
-			c.Assert(result.Combined(), checker.Contains, v)
+			assert.Check(c, strings.Contains(result.Combined(), v))
 		}
 	}
 }
 
-func (s *DockerSuite) TestLogsSinceFutureFollow(c *check.C) {
+func (s *DockerSuite) TestLogsSinceFutureFollow(c *testing.T) {
 	// TODO Windows TP5 - Figure out why this test is so flakey. Disabled for now.
 	testRequires(c, DaemonIsLinux)
 	name := "testlogssincefuturefollow"
@@ -195,23 +195,23 @@ func (s *DockerSuite) TestLogsSinceFutureFollow(c *check.C) {
 		}
 	}
 
-	c.Assert(timestamp, checker.Not(checker.Equals), "")
+	assert.Assert(c, timestamp != "")
 	t, err := time.Parse(time.RFC3339Nano, timestamp)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	since := t.Unix() + 2
 	out, _ := dockerCmd(c, "logs", "-t", "-f", fmt.Sprintf("--since=%v", since), name)
-	c.Assert(out, checker.Not(checker.HasLen), 0, check.Commentf("cannot read from empty log"))
+	assert.Assert(c, len(out) != 0, "cannot read from empty log")
 	lines := strings.Split(strings.TrimSpace(out), "\n")
 	for _, v := range lines {
 		ts, err := time.Parse(time.RFC3339Nano, strings.Split(v, " ")[0])
-		c.Assert(err, checker.IsNil, check.Commentf("cannot parse timestamp output from log: '%v'", v))
-		c.Assert(ts.Unix() >= since, checker.Equals, true, check.Commentf("earlier log found. since=%v logdate=%v", since, ts))
+		assert.NilError(c, err, "cannot parse timestamp output from log: '%v'", v)
+		assert.Assert(c, ts.Unix() >= since, "earlier log found. since=%v logdate=%v", since, ts)
 	}
 }
 
 // Regression test for #8832
-func (s *DockerSuite) TestLogsFollowSlowStdoutConsumer(c *check.C) {
+func (s *DockerSuite) TestLogsFollowSlowStdoutConsumer(c *testing.T) {
 	// TODO Windows: Fix this test for TP5.
 	testRequires(c, DaemonIsLinux)
 	expected := 150000
@@ -228,22 +228,22 @@ func (s *DockerSuite) TestLogsFollowSlowStdoutConsumer(c *check.C) {
 
 	logCmd := exec.Command(dockerBinary, "logs", "-f", id)
 	stdout, err := logCmd.StdoutPipe()
-	c.Assert(err, checker.IsNil)
-	c.Assert(logCmd.Start(), checker.IsNil)
+	assert.NilError(c, err)
+	assert.NilError(c, logCmd.Start())
 	defer func() { go logCmd.Wait() }()
 
 	// First read slowly
 	bytes1, err := ConsumeWithSpeed(stdout, 10, 50*time.Millisecond, stopSlowRead)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// After the container has finished we can continue reading fast
 	bytes2, err := ConsumeWithSpeed(stdout, 32*1024, 0, nil)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(logCmd.Wait(), checker.IsNil)
+	assert.NilError(c, logCmd.Wait())
 
 	actual := bytes1 + bytes2
-	c.Assert(actual, checker.Equals, expected)
+	assert.Equal(c, actual, expected)
 }
 
 // ConsumeWithSpeed reads chunkSize bytes from reader before sleeping
@@ -269,17 +269,17 @@ func ConsumeWithSpeed(reader io.Reader, chunkSize int, interval time.Duration, s
 	}
 }
 
-func (s *DockerSuite) TestLogsFollowGoroutinesWithStdout(c *check.C) {
+func (s *DockerSuite) TestLogsFollowGoroutinesWithStdout(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "while true; do echo hello; sleep 2; done")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	nroutines, err := getGoroutineNumber()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	cmd := exec.Command(dockerBinary, "logs", "-f", id)
 	r, w := io.Pipe()
 	cmd.Stdout = w
-	c.Assert(cmd.Start(), checker.IsNil)
+	assert.NilError(c, cmd.Start())
 	go cmd.Wait()
 
 	// Make sure pipe is written to
@@ -289,48 +289,48 @@ func (s *DockerSuite) TestLogsFollowGoroutinesWithStdout(c *check.C) {
 		_, err := r.Read(b)
 		chErr <- err
 	}()
-	c.Assert(<-chErr, checker.IsNil)
-	c.Assert(cmd.Process.Kill(), checker.IsNil)
+	assert.NilError(c, <-chErr)
+	assert.NilError(c, cmd.Process.Kill())
 	r.Close()
 	cmd.Wait()
 	// NGoroutines is not updated right away, so we need to wait before failing
-	c.Assert(waitForGoroutines(nroutines), checker.IsNil)
+	assert.NilError(c, waitForGoroutines(nroutines))
 }
 
-func (s *DockerSuite) TestLogsFollowGoroutinesNoOutput(c *check.C) {
+func (s *DockerSuite) TestLogsFollowGoroutinesNoOutput(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "while true; do sleep 2; done")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	nroutines, err := getGoroutineNumber()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	cmd := exec.Command(dockerBinary, "logs", "-f", id)
-	c.Assert(cmd.Start(), checker.IsNil)
+	assert.NilError(c, cmd.Start())
 	go cmd.Wait()
 	time.Sleep(200 * time.Millisecond)
-	c.Assert(cmd.Process.Kill(), checker.IsNil)
+	assert.NilError(c, cmd.Process.Kill())
 	cmd.Wait()
 
 	// NGoroutines is not updated right away, so we need to wait before failing
-	c.Assert(waitForGoroutines(nroutines), checker.IsNil)
+	assert.NilError(c, waitForGoroutines(nroutines))
 }
 
-func (s *DockerSuite) TestLogsCLIContainerNotFound(c *check.C) {
+func (s *DockerSuite) TestLogsCLIContainerNotFound(c *testing.T) {
 	name := "testlogsnocontainer"
 	out, _, _ := dockerCmdWithError("logs", name)
 	message := fmt.Sprintf("No such container: %s\n", name)
-	c.Assert(out, checker.Contains, message)
+	assert.Assert(c, strings.Contains(out, message))
 }
 
-func (s *DockerSuite) TestLogsWithDetails(c *check.C) {
+func (s *DockerSuite) TestLogsWithDetails(c *testing.T) {
 	dockerCmd(c, "run", "--name=test", "--label", "foo=bar", "-e", "baz=qux", "--log-opt", "labels=foo", "--log-opt", "env=baz", "busybox", "echo", "hello")
 	out, _ := dockerCmd(c, "logs", "--details", "--timestamps", "test")
 
 	logFields := strings.Fields(strings.TrimSpace(out))
-	c.Assert(len(logFields), checker.Equals, 3, check.Commentf("%s", out))
+	assert.Equal(c, len(logFields), 3, out)
 
 	details := strings.Split(logFields[1], ",")
-	c.Assert(details, checker.HasLen, 2)
-	c.Assert(details[0], checker.Equals, "baz=qux")
-	c.Assert(details[1], checker.Equals, "foo=bar")
+	assert.Equal(c, len(details), 2)
+	assert.Equal(c, details[0], "baz=qux")
+	assert.Equal(c, details[1], "foo=bar")
 }
diff --git a/integration-cli/docker_cli_netmode_test.go b/integration-cli/docker_cli_netmode_test.go
index 76f9898d887c6..fe6d9487ac17e 100644
--- a/integration-cli/docker_cli_netmode_test.go
+++ b/integration-cli/docker_cli_netmode_test.go
@@ -2,10 +2,10 @@ package main
 
 import (
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/runconfig"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
 // GH14530. Validates combinations of --net= with other options
@@ -17,80 +17,70 @@ const stringCheckPS = "PID   USER"
 // DockerCmdWithFail executes a docker command that is supposed to fail and returns
 // the output, the exit code. If the command returns a Nil error, it will fail and
 // stop the tests.
-func dockerCmdWithFail(c *check.C, args ...string) (string, int) {
+func dockerCmdWithFail(c *testing.T, args ...string) (string, int) {
 	out, status, err := dockerCmdWithError(args...)
-	c.Assert(err, check.NotNil, check.Commentf("%v", out))
+	assert.Assert(c, err != nil, "%v", out)
 	return out, status
 }
 
-func (s *DockerSuite) TestNetHostnameWithNetHost(c *check.C) {
+func (s *DockerSuite) TestNetHostnameWithNetHost(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 
 	out, _ := dockerCmd(c, "run", "--net=host", "busybox", "ps")
-	c.Assert(out, checker.Contains, stringCheckPS)
+	assert.Assert(c, strings.Contains(out, stringCheckPS))
 }
 
-func (s *DockerSuite) TestNetHostname(c *check.C) {
+func (s *DockerSuite) TestNetHostname(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	out, _ := dockerCmd(c, "run", "-h=name", "busybox", "ps")
-	c.Assert(out, checker.Contains, stringCheckPS)
-
+	assert.Assert(c, strings.Contains(out, stringCheckPS))
 	out, _ = dockerCmd(c, "run", "-h=name", "--net=bridge", "busybox", "ps")
-	c.Assert(out, checker.Contains, stringCheckPS)
-
+	assert.Assert(c, strings.Contains(out, stringCheckPS))
 	out, _ = dockerCmd(c, "run", "-h=name", "--net=none", "busybox", "ps")
-	c.Assert(out, checker.Contains, stringCheckPS)
-
+	assert.Assert(c, strings.Contains(out, stringCheckPS))
 	out, _ = dockerCmdWithFail(c, "run", "-h=name", "--net=container:other", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictNetworkHostname.Error())
-
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictNetworkHostname.Error()))
 	out, _ = dockerCmdWithFail(c, "run", "--net=container", "busybox", "ps")
-	c.Assert(out, checker.Contains, "invalid container format container:")
-
+	assert.Assert(c, strings.Contains(out, "invalid container format container:"))
 	out, _ = dockerCmdWithFail(c, "run", "--net=weird", "busybox", "ps")
-	c.Assert(strings.ToLower(out), checker.Contains, "not found")
+	assert.Assert(c, strings.Contains(strings.ToLower(out), "not found"))
 }
 
-func (s *DockerSuite) TestConflictContainerNetworkAndLinks(c *check.C) {
+func (s *DockerSuite) TestConflictContainerNetworkAndLinks(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	out, _ := dockerCmdWithFail(c, "run", "--net=container:other", "--link=zip:zap", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictContainerNetworkAndLinks.Error())
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictContainerNetworkAndLinks.Error()))
 }
 
-func (s *DockerSuite) TestConflictContainerNetworkHostAndLinks(c *check.C) {
+func (s *DockerSuite) TestConflictContainerNetworkHostAndLinks(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 
 	out, _ := dockerCmdWithFail(c, "run", "--net=host", "--link=zip:zap", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictHostNetworkAndLinks.Error())
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetworkAndLinks.Error()))
 }
 
-func (s *DockerSuite) TestConflictNetworkModeNetHostAndOptions(c *check.C) {
+func (s *DockerSuite) TestConflictNetworkModeNetHostAndOptions(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 
 	out, _ := dockerCmdWithFail(c, "run", "--net=host", "--mac-address=92:d0:c6:0a:29:33", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictContainerNetworkAndMac.Error())
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictContainerNetworkAndMac.Error()))
 }
 
-func (s *DockerSuite) TestConflictNetworkModeAndOptions(c *check.C) {
+func (s *DockerSuite) TestConflictNetworkModeAndOptions(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	out, _ := dockerCmdWithFail(c, "run", "--net=container:other", "--dns=8.8.8.8", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictNetworkAndDNS.Error())
-
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictNetworkAndDNS.Error()))
 	out, _ = dockerCmdWithFail(c, "run", "--net=container:other", "--add-host=name:8.8.8.8", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictNetworkHosts.Error())
-
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictNetworkHosts.Error()))
 	out, _ = dockerCmdWithFail(c, "run", "--net=container:other", "--mac-address=92:d0:c6:0a:29:33", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictContainerNetworkAndMac.Error())
-
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictContainerNetworkAndMac.Error()))
 	out, _ = dockerCmdWithFail(c, "run", "--net=container:other", "-P", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictNetworkPublishPorts.Error())
-
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictNetworkPublishPorts.Error()))
 	out, _ = dockerCmdWithFail(c, "run", "--net=container:other", "-p", "8080", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictNetworkPublishPorts.Error())
-
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictNetworkPublishPorts.Error()))
 	out, _ = dockerCmdWithFail(c, "run", "--net=container:other", "--expose", "8000-9000", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictNetworkExposePorts.Error())
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictNetworkExposePorts.Error()))
 }
diff --git a/integration-cli/docker_cli_network_test.go b/integration-cli/docker_cli_network_test.go
new file mode 100644
index 0000000000000..ad1c95876fd8d
--- /dev/null
+++ b/integration-cli/docker_cli_network_test.go
@@ -0,0 +1,13 @@
+package main
+
+import (
+	"net/http/httptest"
+
+	"github.com/docker/docker/integration-cli/daemon"
+)
+
+type DockerNetworkSuite struct {
+	server *httptest.Server
+	ds     *DockerSuite
+	d      *daemon.Daemon
+}
diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go
index d3d6256a752c0..ea5da4b40167d 100644
--- a/integration-cli/docker_cli_network_unix_test.go
+++ b/integration-cli/docker_cli_network_unix_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -5,31 +6,30 @@ package main
 import (
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"net"
 	"net/http"
 	"net/http/httptest"
 	"os"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/versions/v1p20"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/daemon"
-	testdaemon "github.com/docker/docker/internal/test/daemon"
+	"github.com/docker/docker/libnetwork/driverapi"
+	remoteapi "github.com/docker/docker/libnetwork/drivers/remote/api"
+	"github.com/docker/docker/libnetwork/ipamapi"
+	remoteipam "github.com/docker/docker/libnetwork/ipams/remote/api"
+	"github.com/docker/docker/libnetwork/netlabel"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/runconfig"
-	"github.com/docker/libnetwork/driverapi"
-	remoteapi "github.com/docker/libnetwork/drivers/remote/api"
-	"github.com/docker/libnetwork/ipamapi"
-	remoteipam "github.com/docker/libnetwork/ipams/remote/api"
-	"github.com/docker/libnetwork/netlabel"
-	"github.com/go-check/check"
+	testdaemon "github.com/docker/docker/testutil/daemon"
 	"github.com/vishvananda/netlink"
 	"golang.org/x/sys/unix"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
 const dummyNetworkDriver = "dummy-network-driver"
@@ -37,37 +37,25 @@ const dummyIPAMDriver = "dummy-ipam-driver"
 
 var remoteDriverNetworkRequest remoteapi.CreateNetworkRequest
 
-func init() {
-	check.Suite(&DockerNetworkSuite{
-		ds: &DockerSuite{},
-	})
-}
-
-type DockerNetworkSuite struct {
-	server *httptest.Server
-	ds     *DockerSuite
-	d      *daemon.Daemon
-}
-
-func (s *DockerNetworkSuite) SetUpTest(c *check.C) {
+func (s *DockerNetworkSuite) SetUpTest(c *testing.T) {
 	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
 }
 
-func (s *DockerNetworkSuite) TearDownTest(c *check.C) {
+func (s *DockerNetworkSuite) TearDownTest(c *testing.T) {
 	if s.d != nil {
 		s.d.Stop(c)
 		s.ds.TearDownTest(c)
 	}
 }
 
-func (s *DockerNetworkSuite) SetUpSuite(c *check.C) {
+func (s *DockerNetworkSuite) SetUpSuite(c *testing.T) {
 	mux := http.NewServeMux()
 	s.server = httptest.NewServer(mux)
-	c.Assert(s.server, check.NotNil, check.Commentf("Failed to start an HTTP Server"))
+	assert.Assert(c, s.server != nil, "Failed to start an HTTP Server")
 	setupRemoteNetworkDrivers(c, mux, s.server.URL, dummyNetworkDriver, dummyIPAMDriver)
 }
 
-func setupRemoteNetworkDrivers(c *check.C, mux *http.ServeMux, url, netDrv, ipamDrv string) {
+func setupRemoteNetworkDrivers(c *testing.T, mux *http.ServeMux, url, netDrv, ipamDrv string) {
 
 	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
@@ -209,18 +197,18 @@ func setupRemoteNetworkDrivers(c *check.C, mux *http.ServeMux, url, netDrv, ipam
 	})
 
 	err := os.MkdirAll("/etc/docker/plugins", 0755)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv)
-	err = ioutil.WriteFile(fileName, []byte(url), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(fileName, []byte(url), 0644)
+	assert.NilError(c, err)
 
 	ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv)
-	err = ioutil.WriteFile(ipamFileName, []byte(url), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(ipamFileName, []byte(url), 0644)
+	assert.NilError(c, err)
 }
 
-func (s *DockerNetworkSuite) TearDownSuite(c *check.C) {
+func (s *DockerNetworkSuite) TearDownSuite(c *testing.T) {
 	if s.server == nil {
 		return
 	}
@@ -228,22 +216,22 @@ func (s *DockerNetworkSuite) TearDownSuite(c *check.C) {
 	s.server.Close()
 
 	err := os.RemoveAll("/etc/docker/plugins")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
 
-func assertNwIsAvailable(c *check.C, name string) {
+func assertNwIsAvailable(c *testing.T, name string) {
 	if !isNwPresent(c, name) {
 		c.Fatalf("Network %s not found in network ls o/p", name)
 	}
 }
 
-func assertNwNotAvailable(c *check.C, name string) {
+func assertNwNotAvailable(c *testing.T, name string) {
 	if isNwPresent(c, name) {
 		c.Fatalf("Found network %s in network ls o/p", name)
 	}
 }
 
-func isNwPresent(c *check.C, name string) bool {
+func isNwPresent(c *testing.T, name string) bool {
 	out, _ := dockerCmd(c, "network", "ls")
 	lines := strings.Split(out, "\n")
 	for i := 1; i < len(lines)-1; i++ {
@@ -258,7 +246,7 @@ func isNwPresent(c *check.C, name string) bool {
 // assertNwList checks network list retrieved with ls command
 // equals to expected network list
 // note: out should be `network ls [option]` result
-func assertNwList(c *check.C, out string, expectNws []string) {
+func assertNwList(c *testing.T, out string, expectNws []string) {
 	lines := strings.Split(out, "\n")
 	var nwList []string
 	for _, line := range lines[1 : len(lines)-1] {
@@ -268,54 +256,54 @@ func assertNwList(c *check.C, out string, expectNws []string) {
 	}
 
 	// network ls should contains all expected networks
-	c.Assert(nwList, checker.DeepEquals, expectNws)
+	assert.DeepEqual(c, nwList, expectNws)
 }
 
-func getNwResource(c *check.C, name string) *types.NetworkResource {
+func getNwResource(c *testing.T, name string) *types.NetworkResource {
 	out, _ := dockerCmd(c, "network", "inspect", name)
 	var nr []types.NetworkResource
 	err := json.Unmarshal([]byte(out), &nr)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	return &nr[0]
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkLsDefault(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkLsDefault(c *testing.T) {
 	defaults := []string{"bridge", "host", "none"}
 	for _, nn := range defaults {
 		assertNwIsAvailable(c, nn)
 	}
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkCreatePredefined(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkCreatePredefined(c *testing.T) {
 	predefined := []string{"bridge", "host", "none", "default"}
 	for _, net := range predefined {
 		// predefined networks can't be created again
 		out, _, err := dockerCmdWithError("network", "create", net)
-		c.Assert(err, checker.NotNil, check.Commentf("%v", out))
+		assert.ErrorContains(c, err, "", out)
 	}
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkCreateHostBind(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkCreateHostBind(c *testing.T) {
 	dockerCmd(c, "network", "create", "--subnet=192.168.10.0/24", "--gateway=192.168.10.1", "-o", "com.docker.network.bridge.host_binding_ipv4=192.168.10.1", "testbind")
 	assertNwIsAvailable(c, "testbind")
 
 	out := runSleepingContainer(c, "--net=testbind", "-p", "5000:5000")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 	out, _ = dockerCmd(c, "ps")
-	c.Assert(out, checker.Contains, "192.168.10.1:5000->5000/tcp")
+	assert.Assert(c, strings.Contains(out, "192.168.10.1:5000->5000/tcp"))
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkRmPredefined(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkRmPredefined(c *testing.T) {
 	predefined := []string{"bridge", "host", "none", "default"}
 	for _, net := range predefined {
 		// predefined networks can't be removed
 		out, _, err := dockerCmdWithError("network", "rm", net)
-		c.Assert(err, checker.NotNil, check.Commentf("%v", out))
+		assert.ErrorContains(c, err, "", out)
 	}
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *testing.T) {
 	testRequires(c, OnlyDefaultNetworks)
 	testNet := "testnet1"
 	testLabel := "foo"
@@ -359,7 +347,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *check.C) {
 
 	out, _ = dockerCmd(c, "network", "ls", "-f", "label=nonexistent")
 	outArr := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(outArr), check.Equals, 1, check.Commentf("%s\n", out))
+	assert.Equal(c, len(outArr), 1, fmt.Sprintf("%s\n", out))
 
 	out, _ = dockerCmd(c, "network", "ls", "-f", "driver=null")
 	assertNwList(c, out, []string{"none"})
@@ -371,7 +359,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkLsFilter(c *check.C) {
 	assertNwList(c, out, []string{"bridge", "dev", testNet})
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *testing.T) {
 	dockerCmd(c, "network", "create", "test")
 	assertNwIsAvailable(c, "test")
 
@@ -379,7 +367,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *check.C) {
 	assertNwNotAvailable(c, "test")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkCreateLabel(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkCreateLabel(c *testing.T) {
 	testNet := "testnetcreatelabel"
 	testLabel := "foo"
 	testValue := "bar"
@@ -388,19 +376,19 @@ func (s *DockerNetworkSuite) TestDockerNetworkCreateLabel(c *check.C) {
 	assertNwIsAvailable(c, testNet)
 
 	out, _, err := dockerCmdWithError("network", "inspect", "--format={{ .Labels."+testLabel+" }}", testNet)
-	c.Assert(err, check.IsNil)
-	c.Assert(strings.TrimSpace(out), check.Equals, testValue)
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), testValue)
 
 	dockerCmd(c, "network", "rm", testNet)
 	assertNwNotAvailable(c, testNet)
 }
 
-func (s *DockerSuite) TestDockerNetworkDeleteNotExists(c *check.C) {
+func (s *DockerSuite) TestDockerNetworkDeleteNotExists(c *testing.T) {
 	out, _, err := dockerCmdWithError("network", "rm", "test")
-	c.Assert(err, checker.NotNil, check.Commentf("%v", out))
+	assert.ErrorContains(c, err, "", out)
 }
 
-func (s *DockerSuite) TestDockerNetworkDeleteMultiple(c *check.C) {
+func (s *DockerSuite) TestDockerNetworkDeleteMultiple(c *testing.T) {
 	dockerCmd(c, "network", "create", "testDelMulti0")
 	assertNwIsAvailable(c, "testDelMulti0")
 	dockerCmd(c, "network", "create", "testDelMulti1")
@@ -415,48 +403,48 @@ func (s *DockerSuite) TestDockerNetworkDeleteMultiple(c *check.C) {
 	// contains active container, its deletion should fail.
 	out, _, err := dockerCmdWithError("network", "rm", "testDelMulti0", "testDelMulti1", "testDelMulti2")
 	// err should not be nil due to deleting testDelMulti2 failed.
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+	assert.Assert(c, err != nil, "out: %s", out)
 	// testDelMulti2 should fail due to network has active endpoints
-	c.Assert(out, checker.Contains, "has active endpoints")
+	assert.Assert(c, strings.Contains(out, "has active endpoints"))
 	assertNwNotAvailable(c, "testDelMulti0")
 	assertNwNotAvailable(c, "testDelMulti1")
 	// testDelMulti2 can't be deleted, so it should exist
 	assertNwIsAvailable(c, "testDelMulti2")
 }
 
-func (s *DockerSuite) TestDockerNetworkInspect(c *check.C) {
+func (s *DockerSuite) TestDockerNetworkInspect(c *testing.T) {
 	out, _ := dockerCmd(c, "network", "inspect", "host")
 	var networkResources []types.NetworkResource
 	err := json.Unmarshal([]byte(out), &networkResources)
-	c.Assert(err, check.IsNil)
-	c.Assert(networkResources, checker.HasLen, 1)
+	assert.NilError(c, err)
+	assert.Equal(c, len(networkResources), 1)
 
 	out, _ = dockerCmd(c, "network", "inspect", "--format={{ .Name }}", "host")
-	c.Assert(strings.TrimSpace(out), check.Equals, "host")
+	assert.Equal(c, strings.TrimSpace(out), "host")
 }
 
-func (s *DockerSuite) TestDockerNetworkInspectWithID(c *check.C) {
+func (s *DockerSuite) TestDockerNetworkInspectWithID(c *testing.T) {
 	out, _ := dockerCmd(c, "network", "create", "test2")
 	networkID := strings.TrimSpace(out)
 	assertNwIsAvailable(c, "test2")
 	out, _ = dockerCmd(c, "network", "inspect", "--format={{ .Id }}", "test2")
-	c.Assert(strings.TrimSpace(out), check.Equals, networkID)
+	assert.Equal(c, strings.TrimSpace(out), networkID)
 
 	out, _ = dockerCmd(c, "network", "inspect", "--format={{ .ID }}", "test2")
-	c.Assert(strings.TrimSpace(out), check.Equals, networkID)
+	assert.Equal(c, strings.TrimSpace(out), networkID)
 }
 
-func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *check.C) {
+func (s *DockerSuite) TestDockerInspectMultipleNetwork(c *testing.T) {
 	result := dockerCmdWithResult("network", "inspect", "host", "none")
 	result.Assert(c, icmd.Success)
 
 	var networkResources []types.NetworkResource
 	err := json.Unmarshal([]byte(result.Stdout()), &networkResources)
-	c.Assert(err, check.IsNil)
-	c.Assert(networkResources, checker.HasLen, 2)
+	assert.NilError(c, err)
+	assert.Equal(c, len(networkResources), 2)
 }
 
-func (s *DockerSuite) TestDockerInspectMultipleNetworksIncludingNonexistent(c *check.C) {
+func (s *DockerSuite) TestDockerInspectMultipleNetworksIncludingNonexistent(c *testing.T) {
 	// non-existent network was not at the beginning of the inspect list
 	// This should print an error, return an exitCode 1 and print the host network
 	result := dockerCmdWithResult("network", "inspect", "host", "nonexistent")
@@ -468,8 +456,8 @@ func (s *DockerSuite) TestDockerInspectMultipleNetworksIncludingNonexistent(c *c
 
 	var networkResources []types.NetworkResource
 	err := json.Unmarshal([]byte(result.Stdout()), &networkResources)
-	c.Assert(err, check.IsNil)
-	c.Assert(networkResources, checker.HasLen, 1)
+	assert.NilError(c, err)
+	assert.Equal(c, len(networkResources), 1)
 
 	// Only one non-existent network to inspect
 	// Should print an error and return an exitCode, nothing else
@@ -491,11 +479,11 @@ func (s *DockerSuite) TestDockerInspectMultipleNetworksIncludingNonexistent(c *c
 
 	networkResources = []types.NetworkResource{}
 	err = json.Unmarshal([]byte(result.Stdout()), &networkResources)
-	c.Assert(err, check.IsNil)
-	c.Assert(networkResources, checker.HasLen, 1)
+	assert.NilError(c, err)
+	assert.Equal(c, len(networkResources), 1)
 }
 
-func (s *DockerSuite) TestDockerInspectNetworkWithContainerName(c *check.C) {
+func (s *DockerSuite) TestDockerInspectNetworkWithContainerName(c *testing.T) {
 	dockerCmd(c, "network", "create", "brNetForInspect")
 	assertNwIsAvailable(c, "brNetForInspect")
 	defer func() {
@@ -504,7 +492,7 @@ func (s *DockerSuite) TestDockerInspectNetworkWithContainerName(c *check.C) {
 	}()
 
 	out, _ := dockerCmd(c, "run", "-d", "--name", "testNetInspect1", "--net", "brNetForInspect", "busybox", "top")
-	c.Assert(waitRun("testNetInspect1"), check.IsNil)
+	assert.Assert(c, waitRun("testNetInspect1") == nil)
 	containerID := strings.TrimSpace(out)
 	defer func() {
 		// we don't stop container by name, because we'll rename it later
@@ -514,11 +502,11 @@ func (s *DockerSuite) TestDockerInspectNetworkWithContainerName(c *check.C) {
 	out, _ = dockerCmd(c, "network", "inspect", "brNetForInspect")
 	var networkResources []types.NetworkResource
 	err := json.Unmarshal([]byte(out), &networkResources)
-	c.Assert(err, check.IsNil)
-	c.Assert(networkResources, checker.HasLen, 1)
+	assert.NilError(c, err)
+	assert.Equal(c, len(networkResources), 1)
 	container, ok := networkResources[0].Containers[containerID]
-	c.Assert(ok, checker.True)
-	c.Assert(container.Name, checker.Equals, "testNetInspect1")
+	assert.Assert(c, ok)
+	assert.Equal(c, container.Name, "testNetInspect1")
 
 	// rename container and check docker inspect output update
 	newName := "HappyNewName"
@@ -528,25 +516,24 @@ func (s *DockerSuite) TestDockerInspectNetworkWithContainerName(c *check.C) {
 	out, _ = dockerCmd(c, "network", "inspect", "brNetForInspect")
 	var newNetRes []types.NetworkResource
 	err = json.Unmarshal([]byte(out), &newNetRes)
-	c.Assert(err, check.IsNil)
-	c.Assert(newNetRes, checker.HasLen, 1)
+	assert.NilError(c, err)
+	assert.Equal(c, len(newNetRes), 1)
 	container1, ok := newNetRes[0].Containers[containerID]
-	c.Assert(ok, checker.True)
-	c.Assert(container1.Name, checker.Equals, newName)
-
+	assert.Assert(c, ok)
+	assert.Equal(c, container1.Name, newName)
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnect(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnect(c *testing.T) {
 	dockerCmd(c, "network", "create", "test")
 	assertNwIsAvailable(c, "test")
 	nr := getNwResource(c, "test")
 
-	c.Assert(nr.Name, checker.Equals, "test")
-	c.Assert(len(nr.Containers), checker.Equals, 0)
+	assert.Equal(c, nr.Name, "test")
+	assert.Equal(c, len(nr.Containers), 0)
 
 	// run a container
 	out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
-	c.Assert(waitRun("test"), check.IsNil)
+	assert.Assert(c, waitRun("test") == nil)
 	containerID := strings.TrimSpace(out)
 
 	// connect the container to the test network
@@ -554,43 +541,42 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnect(c *check.C) {
 
 	// inspect the network to make sure container is connected
 	nr = getNetworkResource(c, nr.ID)
-	c.Assert(len(nr.Containers), checker.Equals, 1)
-	c.Assert(nr.Containers[containerID], check.NotNil)
+	assert.Equal(c, len(nr.Containers), 1)
 
 	// check if container IP matches network inspect
 	ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	containerIP := findContainerIP(c, "test", "test")
-	c.Assert(ip.String(), checker.Equals, containerIP)
+	assert.Equal(c, ip.String(), containerIP)
 
 	// disconnect container from the network
 	dockerCmd(c, "network", "disconnect", "test", containerID)
 	nr = getNwResource(c, "test")
-	c.Assert(nr.Name, checker.Equals, "test")
-	c.Assert(len(nr.Containers), checker.Equals, 0)
+	assert.Equal(c, nr.Name, "test")
+	assert.Equal(c, len(nr.Containers), 0)
 
 	// run another container
 	out, _ = dockerCmd(c, "run", "-d", "--net", "test", "--name", "test2", "busybox", "top")
-	c.Assert(waitRun("test2"), check.IsNil)
+	assert.Assert(c, waitRun("test2") == nil)
 	containerID = strings.TrimSpace(out)
 
 	nr = getNwResource(c, "test")
-	c.Assert(nr.Name, checker.Equals, "test")
-	c.Assert(len(nr.Containers), checker.Equals, 1)
+	assert.Equal(c, nr.Name, "test")
+	assert.Equal(c, len(nr.Containers), 1)
 
 	// force disconnect the container to the test network
 	dockerCmd(c, "network", "disconnect", "-f", "test", containerID)
 
 	nr = getNwResource(c, "test")
-	c.Assert(nr.Name, checker.Equals, "test")
-	c.Assert(len(nr.Containers), checker.Equals, 0)
+	assert.Equal(c, nr.Name, "test")
+	assert.Equal(c, len(nr.Containers), 0)
 
 	dockerCmd(c, "network", "rm", "test")
 	assertNwNotAvailable(c, "test")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkIPAMMultipleNetworks(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkIPAMMultipleNetworks(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	// test0 bridge network
 	dockerCmd(c, "network", "create", "--subnet=192.168.0.0/16", "test1")
 	assertNwIsAvailable(c, "test1")
@@ -630,24 +616,24 @@ func (s *DockerNetworkSuite) TestDockerNetworkIPAMMultipleNetworks(c *check.C) {
 	}
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkCustomIPAM(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkCustomIPAM(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	// Create a bridge network using custom ipam driver
 	dockerCmd(c, "network", "create", "--ipam-driver", dummyIPAMDriver, "br0")
 	assertNwIsAvailable(c, "br0")
 
 	// Verify expected network ipam fields are there
 	nr := getNetworkResource(c, "br0")
-	c.Assert(nr.Driver, checker.Equals, "bridge")
-	c.Assert(nr.IPAM.Driver, checker.Equals, dummyIPAMDriver)
+	assert.Equal(c, nr.Driver, "bridge")
+	assert.Equal(c, nr.IPAM.Driver, dummyIPAMDriver)
 
 	// remove network and exercise remote ipam driver
 	dockerCmd(c, "network", "rm", "br0")
 	assertNwNotAvailable(c, "br0")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkIPAMOptions(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkIPAMOptions(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	// Create a bridge network using custom ipam driver and options
 	dockerCmd(c, "network", "create", "--ipam-driver", dummyIPAMDriver, "--ipam-opt", "opt1=drv1", "--ipam-opt", "opt2=drv2", "br0")
 	assertNwIsAvailable(c, "br0")
@@ -655,104 +641,100 @@ func (s *DockerNetworkSuite) TestDockerNetworkIPAMOptions(c *check.C) {
 	// Verify expected network ipam options
 	nr := getNetworkResource(c, "br0")
 	opts := nr.IPAM.Options
-	c.Assert(opts["opt1"], checker.Equals, "drv1")
-	c.Assert(opts["opt2"], checker.Equals, "drv2")
+	assert.Equal(c, opts["opt1"], "drv1")
+	assert.Equal(c, opts["opt2"], "drv2")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkNullIPAMDriver(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkNullIPAMDriver(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	// Create a network with null ipam driver
 	_, _, err := dockerCmdWithError("network", "create", "-d", dummyNetworkDriver, "--ipam-driver", "null", "test000")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	assertNwIsAvailable(c, "test000")
 
 	// Verify the inspect data contains the default subnet provided by the null
 	// ipam driver and no gateway, as the null ipam driver does not provide one
 	nr := getNetworkResource(c, "test000")
-	c.Assert(nr.IPAM.Driver, checker.Equals, "null")
-	c.Assert(len(nr.IPAM.Config), checker.Equals, 1)
-	c.Assert(nr.IPAM.Config[0].Subnet, checker.Equals, "0.0.0.0/0")
-	c.Assert(nr.IPAM.Config[0].Gateway, checker.Equals, "")
+	assert.Equal(c, nr.IPAM.Driver, "null")
+	assert.Equal(c, len(nr.IPAM.Config), 1)
+	assert.Equal(c, nr.IPAM.Config[0].Subnet, "0.0.0.0/0")
+	assert.Equal(c, nr.IPAM.Config[0].Gateway, "")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkInspectDefault(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkInspectDefault(c *testing.T) {
 	nr := getNetworkResource(c, "none")
-	c.Assert(nr.Driver, checker.Equals, "null")
-	c.Assert(nr.Scope, checker.Equals, "local")
-	c.Assert(nr.Internal, checker.Equals, false)
-	c.Assert(nr.EnableIPv6, checker.Equals, false)
-	c.Assert(nr.IPAM.Driver, checker.Equals, "default")
-	c.Assert(len(nr.IPAM.Config), checker.Equals, 0)
+	assert.Equal(c, nr.Driver, "null")
+	assert.Equal(c, nr.Scope, "local")
+	assert.Equal(c, nr.Internal, false)
+	assert.Equal(c, nr.EnableIPv6, false)
+	assert.Equal(c, nr.IPAM.Driver, "default")
+	assert.Equal(c, len(nr.IPAM.Config), 0)
 
 	nr = getNetworkResource(c, "host")
-	c.Assert(nr.Driver, checker.Equals, "host")
-	c.Assert(nr.Scope, checker.Equals, "local")
-	c.Assert(nr.Internal, checker.Equals, false)
-	c.Assert(nr.EnableIPv6, checker.Equals, false)
-	c.Assert(nr.IPAM.Driver, checker.Equals, "default")
-	c.Assert(len(nr.IPAM.Config), checker.Equals, 0)
+	assert.Equal(c, nr.Driver, "host")
+	assert.Equal(c, nr.Scope, "local")
+	assert.Equal(c, nr.Internal, false)
+	assert.Equal(c, nr.EnableIPv6, false)
+	assert.Equal(c, nr.IPAM.Driver, "default")
+	assert.Equal(c, len(nr.IPAM.Config), 0)
 
 	nr = getNetworkResource(c, "bridge")
-	c.Assert(nr.Driver, checker.Equals, "bridge")
-	c.Assert(nr.Scope, checker.Equals, "local")
-	c.Assert(nr.Internal, checker.Equals, false)
-	c.Assert(nr.EnableIPv6, checker.Equals, false)
-	c.Assert(nr.IPAM.Driver, checker.Equals, "default")
-	c.Assert(len(nr.IPAM.Config), checker.Equals, 1)
-	c.Assert(nr.IPAM.Config[0].Subnet, checker.NotNil)
-	c.Assert(nr.IPAM.Config[0].Gateway, checker.NotNil)
+	assert.Equal(c, nr.Driver, "bridge")
+	assert.Equal(c, nr.Scope, "local")
+	assert.Equal(c, nr.Internal, false)
+	assert.Equal(c, nr.EnableIPv6, false)
+	assert.Equal(c, nr.IPAM.Driver, "default")
+	assert.Equal(c, len(nr.IPAM.Config), 1)
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkInspectCustomUnspecified(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkInspectCustomUnspecified(c *testing.T) {
 	// if unspecified, network subnet will be selected from inside preferred pool
 	dockerCmd(c, "network", "create", "test01")
 	assertNwIsAvailable(c, "test01")
 
 	nr := getNetworkResource(c, "test01")
-	c.Assert(nr.Driver, checker.Equals, "bridge")
-	c.Assert(nr.Scope, checker.Equals, "local")
-	c.Assert(nr.Internal, checker.Equals, false)
-	c.Assert(nr.EnableIPv6, checker.Equals, false)
-	c.Assert(nr.IPAM.Driver, checker.Equals, "default")
-	c.Assert(len(nr.IPAM.Config), checker.Equals, 1)
-	c.Assert(nr.IPAM.Config[0].Subnet, checker.NotNil)
-	c.Assert(nr.IPAM.Config[0].Gateway, checker.NotNil)
+	assert.Equal(c, nr.Driver, "bridge")
+	assert.Equal(c, nr.Scope, "local")
+	assert.Equal(c, nr.Internal, false)
+	assert.Equal(c, nr.EnableIPv6, false)
+	assert.Equal(c, nr.IPAM.Driver, "default")
+	assert.Equal(c, len(nr.IPAM.Config), 1)
 
 	dockerCmd(c, "network", "rm", "test01")
 	assertNwNotAvailable(c, "test01")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkInspectCustomSpecified(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkInspectCustomSpecified(c *testing.T) {
 	dockerCmd(c, "network", "create", "--driver=bridge", "--ipv6", "--subnet=fd80:24e2:f998:72d6::/64", "--subnet=172.28.0.0/16", "--ip-range=172.28.5.0/24", "--gateway=172.28.5.254", "br0")
 	assertNwIsAvailable(c, "br0")
 
 	nr := getNetworkResource(c, "br0")
-	c.Assert(nr.Driver, checker.Equals, "bridge")
-	c.Assert(nr.Scope, checker.Equals, "local")
-	c.Assert(nr.Internal, checker.Equals, false)
-	c.Assert(nr.EnableIPv6, checker.Equals, true)
-	c.Assert(nr.IPAM.Driver, checker.Equals, "default")
-	c.Assert(len(nr.IPAM.Config), checker.Equals, 2)
-	c.Assert(nr.IPAM.Config[0].Subnet, checker.Equals, "172.28.0.0/16")
-	c.Assert(nr.IPAM.Config[0].IPRange, checker.Equals, "172.28.5.0/24")
-	c.Assert(nr.IPAM.Config[0].Gateway, checker.Equals, "172.28.5.254")
-	c.Assert(nr.Internal, checker.False)
+	assert.Equal(c, nr.Driver, "bridge")
+	assert.Equal(c, nr.Scope, "local")
+	assert.Equal(c, nr.Internal, false)
+	assert.Equal(c, nr.EnableIPv6, true)
+	assert.Equal(c, nr.IPAM.Driver, "default")
+	assert.Equal(c, len(nr.IPAM.Config), 2)
+	assert.Equal(c, nr.IPAM.Config[0].Subnet, "172.28.0.0/16")
+	assert.Equal(c, nr.IPAM.Config[0].IPRange, "172.28.5.0/24")
+	assert.Equal(c, nr.IPAM.Config[0].Gateway, "172.28.5.254")
+	assert.Equal(c, nr.Internal, false)
 	dockerCmd(c, "network", "rm", "br0")
 	assertNwNotAvailable(c, "br0")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkIPAMInvalidCombinations(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkIPAMInvalidCombinations(c *testing.T) {
 	// network with ip-range out of subnet range
 	_, _, err := dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--ip-range=192.170.0.0/16", "test")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// network with multiple gateways for a single subnet
 	_, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--gateway=192.168.0.1", "--gateway=192.168.0.2", "test")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// Multiple overlapping subnets in the same network must fail
 	_, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--subnet=192.168.1.0/16", "test")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// overlapping subnets across networks must fail
 	// create a valid test0 network
@@ -760,27 +742,27 @@ func (s *DockerNetworkSuite) TestDockerNetworkIPAMInvalidCombinations(c *check.C
 	assertNwIsAvailable(c, "test0")
 	// create an overlapping test1 network
 	_, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.128.0/17", "test1")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	dockerCmd(c, "network", "rm", "test0")
 	assertNwNotAvailable(c, "test0")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkDriverOptions(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkDriverOptions(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, "-o", "opt1=drv1", "-o", "opt2=drv2", "testopt")
 	assertNwIsAvailable(c, "testopt")
 	gopts := remoteDriverNetworkRequest.Options[netlabel.GenericData]
-	c.Assert(gopts, checker.NotNil)
+	assert.Assert(c, gopts != nil)
 	opts, ok := gopts.(map[string]interface{})
-	c.Assert(ok, checker.Equals, true)
-	c.Assert(opts["opt1"], checker.Equals, "drv1")
-	c.Assert(opts["opt2"], checker.Equals, "drv2")
+	assert.Equal(c, ok, true)
+	assert.Equal(c, opts["opt1"], "drv1")
+	assert.Equal(c, opts["opt2"], "drv2")
 	dockerCmd(c, "network", "rm", "testopt")
 	assertNwNotAvailable(c, "testopt")
 
 }
 
-func (s *DockerNetworkSuite) TestDockerPluginV2NetworkDriver(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerPluginV2NetworkDriver(c *testing.T) {
 	testRequires(c, DaemonIsLinux, IsAmd64, Network)
 
 	var (
@@ -789,14 +771,13 @@ func (s *DockerNetworkSuite) TestDockerPluginV2NetworkDriver(c *check.C) {
 		npNameWithTag = npName + ":" + npTag
 	)
 	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", npNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err := dockerCmdWithError("plugin", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, npName)
-	c.Assert(out, checker.Contains, npTag)
-	c.Assert(out, checker.Contains, "true")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, npName))
+	assert.Assert(c, strings.Contains(out, npTag))
+	assert.Assert(c, strings.Contains(out, "true"))
 	dockerCmd(c, "network", "create", "-d", npNameWithTag, "v2net")
 	assertNwIsAvailable(c, "v2net")
 	dockerCmd(c, "network", "rm", "v2net")
@@ -804,8 +785,7 @@ func (s *DockerNetworkSuite) TestDockerPluginV2NetworkDriver(c *check.C) {
 
 }
 
-func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c *check.C) {
-	testRequires(c, ExecSupport)
+func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c *testing.T) {
 	// On default bridge network built-in service discovery should not happen
 	hostsFile := "/etc/hosts"
 	bridgeName := "external-bridge"
@@ -818,52 +798,47 @@ func (s *DockerDaemonSuite) TestDockerNetworkNoDiscoveryDefaultBridgeNetwork(c *
 
 	// run two containers and store first container's etc/hosts content
 	out, err := s.d.Cmd("run", "-d", "busybox", "top")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	cid1 := strings.TrimSpace(out)
 	defer s.d.Cmd("stop", cid1)
 
 	hosts, err := s.d.Cmd("exec", cid1, "cat", hostsFile)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, err = s.d.Cmd("run", "-d", "--name", "container2", "busybox", "top")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	cid2 := strings.TrimSpace(out)
 
 	// verify first container's etc/hosts file has not changed after spawning the second named container
 	hostsPost, err := s.d.Cmd("exec", cid1, "cat", hostsFile)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(hosts), checker.Equals, string(hostsPost),
-		check.Commentf("Unexpected %s change on second container creation", hostsFile))
-
+	assert.NilError(c, err)
+	assert.Equal(c, hosts, hostsPost, fmt.Sprintf("Unexpected %s change on second container creation", hostsFile))
 	// stop container 2 and verify first container's etc/hosts has not changed
 	_, err = s.d.Cmd("stop", cid2)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	hostsPost, err = s.d.Cmd("exec", cid1, "cat", hostsFile)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(hosts), checker.Equals, string(hostsPost),
-		check.Commentf("Unexpected %s change on second container creation", hostsFile))
-
+	assert.NilError(c, err)
+	assert.Equal(c, hosts, hostsPost, fmt.Sprintf("Unexpected %s change on second container creation", hostsFile))
 	// but discovery is on when connecting to non default bridge network
 	network := "anotherbridge"
 	out, err = s.d.Cmd("network", "create", network)
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	defer s.d.Cmd("network", "rm", network)
 
 	out, err = s.d.Cmd("network", "connect", network, cid1)
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	hosts, err = s.d.Cmd("exec", cid1, "cat", hostsFile)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	hostsPost, err = s.d.Cmd("exec", cid1, "cat", hostsFile)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(hosts), checker.Equals, string(hostsPost),
-		check.Commentf("Unexpected %s change on second network connection", hostsFile))
+	assert.NilError(c, err)
+	assert.Equal(c, hosts, hostsPost, fmt.Sprintf("Unexpected %s change on second network connection", hostsFile))
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) {
-	testRequires(c, ExecSupport, NotArm)
+func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *testing.T) {
+	testRequires(c, NotArm)
 	hostsFile := "/etc/hosts"
 	cstmBridgeNw := "custom-bridge-nw"
 	cstmBridgeNw1 := "custom-bridge-nw1"
@@ -882,9 +857,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) {
 
 	// verify first container etc/hosts file has not changed
 	hosts1post := readContainerFileWithExec(c, cid1, hostsFile)
-	c.Assert(string(hosts1), checker.Equals, string(hosts1post),
-		check.Commentf("Unexpected %s change on anonymous container creation", hostsFile))
-
+	assert.Equal(c, string(hosts1), string(hosts1post), fmt.Sprintf("Unexpected %s change on anonymous container creation", hostsFile))
 	// Connect the 2nd container to a new network and verify the
 	// first container /etc/hosts file still hasn't changed.
 	dockerCmd(c, "network", "create", "-d", "bridge", cstmBridgeNw1)
@@ -894,9 +867,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) {
 
 	hosts2 := readContainerFileWithExec(c, cid2, hostsFile)
 	hosts1post = readContainerFileWithExec(c, cid1, hostsFile)
-	c.Assert(string(hosts1), checker.Equals, string(hosts1post),
-		check.Commentf("Unexpected %s change on container connect", hostsFile))
-
+	assert.Equal(c, string(hosts1), string(hosts1post), fmt.Sprintf("Unexpected %s change on container connect", hostsFile))
 	// start a named container
 	cName := "AnyName"
 	out, _ = dockerCmd(c, "run", "-d", "--net", cstmBridgeNw, "--name", cName, "busybox", "top")
@@ -909,21 +880,17 @@ func (s *DockerNetworkSuite) TestDockerNetworkAnonymousEndpoint(c *check.C) {
 	// Stop named container and verify first two containers' etc/hosts file hasn't changed
 	dockerCmd(c, "stop", cid3)
 	hosts1post = readContainerFileWithExec(c, cid1, hostsFile)
-	c.Assert(string(hosts1), checker.Equals, string(hosts1post),
-		check.Commentf("Unexpected %s change on name container creation", hostsFile))
-
+	assert.Equal(c, string(hosts1), string(hosts1post), fmt.Sprintf("Unexpected %s change on name container creation", hostsFile))
 	hosts2post := readContainerFileWithExec(c, cid2, hostsFile)
-	c.Assert(string(hosts2), checker.Equals, string(hosts2post),
-		check.Commentf("Unexpected %s change on name container creation", hostsFile))
-
+	assert.Equal(c, string(hosts2), string(hosts2post), fmt.Sprintf("Unexpected %s change on name container creation", hostsFile))
 	// verify that container 1 and 2 can't ping the named container now
 	_, _, err := dockerCmdWithError("exec", cid1, "ping", "-c", "1", cName)
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	_, _, err = dockerCmdWithError("exec", cid2, "ping", "-c", "1", cName)
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkLinkOnDefaultNetworkOnly(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkLinkOnDefaultNetworkOnly(c *testing.T) {
 	// Legacy Link feature must work only on default network, and not across networks
 	cnt1 := "container1"
 	cnt2 := "container2"
@@ -942,15 +909,15 @@ func (s *DockerNetworkSuite) TestDockerNetworkLinkOnDefaultNetworkOnly(c *check.
 
 	// Try launching a container on default network, linking to the second container. Must fail
 	_, _, err := dockerCmdWithError("run", "-d", "--link", fmt.Sprintf("%s:%s", cnt2, cnt2), "busybox", "top")
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// Connect second container to default network. Now a container on default network can link to it
 	dockerCmd(c, "network", "connect", "bridge", cnt2)
 	dockerCmd(c, "run", "-d", "--link", fmt.Sprintf("%s:%s", cnt2, cnt2), "busybox", "top")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkOverlayPortMapping(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkOverlayPortMapping(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	// Verify exposed ports are present in ps output when running a container on
 	// a network managed by a driver which does not provide the default gateway
 	// for the container
@@ -971,13 +938,13 @@ func (s *DockerNetworkSuite) TestDockerNetworkOverlayPortMapping(c *check.C) {
 	unpPort2 := fmt.Sprintf("%d/tcp", port2)
 	out, _ := dockerCmd(c, "ps", "-n=1")
 	// Missing unpublished ports in docker ps output
-	c.Assert(out, checker.Contains, unpPort1)
+	assert.Assert(c, strings.Contains(out, unpPort1))
 	// Missing unpublished ports in docker ps output
-	c.Assert(out, checker.Contains, unpPort2)
+	assert.Assert(c, strings.Contains(out, unpPort2))
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkDriverUngracefulRestart(c *check.C) {
-	testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkDriverUngracefulRestart(c *testing.T) {
+	testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon)
 	dnd := "dnd"
 	did := "did"
 
@@ -987,13 +954,13 @@ func (s *DockerNetworkSuite) TestDockerNetworkDriverUngracefulRestart(c *check.C
 
 	s.d.StartWithBusybox(c)
 	_, err := s.d.Cmd("network", "create", "-d", dnd, "--subnet", "1.1.1.0/24", "net1")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	_, err = s.d.Cmd("run", "-itd", "--net", "net1", "--name", "foo", "--ip", "1.1.1.10", "busybox", "sh")
-	c.Assert(err, checker.IsNil)
+	_, err = s.d.Cmd("run", "-d", "--net", "net1", "--name", "foo", "--ip", "1.1.1.10", "busybox", "top")
+	assert.NilError(c, err)
 
 	// Kill daemon and restart
-	c.Assert(s.d.Kill(), checker.IsNil)
+	assert.Assert(c, s.d.Kill() == nil)
 
 	server.Close()
 
@@ -1013,12 +980,12 @@ func (s *DockerNetworkSuite) TestDockerNetworkDriverUngracefulRestart(c *check.C
 	setupRemoteNetworkDrivers(c, mux, server.URL, dnd, did)
 
 	// trying to reuse the same ip must succeed
-	_, err = s.d.Cmd("run", "-itd", "--net", "net1", "--name", "bar", "--ip", "1.1.1.10", "busybox", "sh")
-	c.Assert(err, checker.IsNil)
+	_, err = s.d.Cmd("run", "-d", "--net", "net1", "--name", "bar", "--ip", "1.1.1.10", "busybox", "top")
+	assert.NilError(c, err)
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkMacInspect(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkMacInspect(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	// Verify endpoint MAC address is correctly populated in container's network settings
 	nwn := "ov"
 	ctn := "bb"
@@ -1029,15 +996,15 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacInspect(c *check.C) {
 	dockerCmd(c, "run", "-d", "--net", nwn, "--name", ctn, "busybox", "top")
 
 	mac := inspectField(c, ctn, "NetworkSettings.Networks."+nwn+".MacAddress")
-	c.Assert(mac, checker.Equals, "a0:b1:c2:d3:e4:f5")
+	assert.Equal(c, mac, "a0:b1:c2:d3:e4:f5")
 }
 
-func (s *DockerSuite) TestInspectAPIMultipleNetworks(c *check.C) {
+func (s *DockerSuite) TestInspectAPIMultipleNetworks(c *testing.T) {
 	dockerCmd(c, "network", "create", "mybridge1")
 	dockerCmd(c, "network", "create", "mybridge2")
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), check.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	dockerCmd(c, "network", "connect", "mybridge1", id)
 	dockerCmd(c, "network", "connect", "mybridge2", id)
@@ -1045,46 +1012,46 @@ func (s *DockerSuite) TestInspectAPIMultipleNetworks(c *check.C) {
 	body := getInspectBody(c, "v1.20", id)
 	var inspect120 v1p20.ContainerJSON
 	err := json.Unmarshal(body, &inspect120)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	versionedIP := inspect120.NetworkSettings.IPAddress
 
 	body = getInspectBody(c, "v1.21", id)
 	var inspect121 types.ContainerJSON
 	err = json.Unmarshal(body, &inspect121)
-	c.Assert(err, checker.IsNil)
-	c.Assert(inspect121.NetworkSettings.Networks, checker.HasLen, 3)
+	assert.NilError(c, err)
+	assert.Equal(c, len(inspect121.NetworkSettings.Networks), 3)
 
 	bridge := inspect121.NetworkSettings.Networks["bridge"]
-	c.Assert(bridge.IPAddress, checker.Equals, versionedIP)
-	c.Assert(bridge.IPAddress, checker.Equals, inspect121.NetworkSettings.IPAddress)
+	assert.Equal(c, bridge.IPAddress, versionedIP)
+	assert.Equal(c, bridge.IPAddress, inspect121.NetworkSettings.IPAddress)
 }
 
-func connectContainerToNetworks(c *check.C, d *daemon.Daemon, cName string, nws []string) {
+func connectContainerToNetworks(c *testing.T, d *daemon.Daemon, cName string, nws []string) {
 	// Run a container on the default network
 	out, err := d.Cmd("run", "-d", "--name", cName, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Attach the container to other networks
 	for _, nw := range nws {
 		out, err = d.Cmd("network", "create", nw)
-		c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+		assert.NilError(c, err, out)
 		out, err = d.Cmd("network", "connect", nw, cName)
-		c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+		assert.NilError(c, err, out)
 	}
 }
 
-func verifyContainerIsConnectedToNetworks(c *check.C, d *daemon.Daemon, cName string, nws []string) {
+func verifyContainerIsConnectedToNetworks(c *testing.T, d *daemon.Daemon, cName string, nws []string) {
 	// Verify container is connected to all the networks
 	for _, nw := range nws {
 		out, err := d.Cmd("inspect", "-f", fmt.Sprintf("{{.NetworkSettings.Networks.%s}}", nw), cName)
-		c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-		c.Assert(out, checker.Not(checker.Equals), "\n")
+		assert.NilError(c, err, out)
+		assert.Assert(c, out != "\n")
 	}
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksGracefulDaemonRestart(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksGracefulDaemonRestart(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	cName := "bb"
 	nwList := []string{"nw1", "nw2", "nw3"}
 
@@ -1097,13 +1064,13 @@ func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksGracefulDaemonRest
 	s.d.Restart(c)
 
 	_, err := s.d.Cmd("start", cName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList)
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksUngracefulDaemonRestart(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksUngracefulDaemonRestart(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	cName := "cc"
 	nwList := []string{"nw1", "nw2", "nw3"}
 
@@ -1113,83 +1080,83 @@ func (s *DockerNetworkSuite) TestDockerNetworkMultipleNetworksUngracefulDaemonRe
 	verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList)
 
 	// Kill daemon and restart
-	c.Assert(s.d.Kill(), checker.IsNil)
+	assert.Assert(c, s.d.Kill() == nil)
 	s.d.Restart(c)
 
 	// Restart container
 	_, err := s.d.Cmd("start", cName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	verifyContainerIsConnectedToNetworks(c, s.d, cName, nwList)
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkRunNetByID(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkRunNetByID(c *testing.T) {
 	out, _ := dockerCmd(c, "network", "create", "one")
 	containerOut, _, err := dockerCmdWithError("run", "-d", "--net", strings.TrimSpace(out), "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf(containerOut))
+	assert.Assert(c, err == nil, containerOut)
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkHostModeUngracefulDaemonRestart(c *check.C) {
-	testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkHostModeUngracefulDaemonRestart(c *testing.T) {
+	testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon)
 	s.d.StartWithBusybox(c)
 
 	// Run a few containers on host network
 	for i := 0; i < 10; i++ {
 		cName := fmt.Sprintf("hostc-%d", i)
 		out, err := s.d.Cmd("run", "-d", "--name", cName, "--net=host", "--restart=always", "busybox", "top")
-		c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+		assert.NilError(c, err, out)
 
 		// verify container has finished starting before killing daemon
 		err = s.d.WaitRun(cName)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	}
 
 	// Kill daemon ungracefully and restart
-	c.Assert(s.d.Kill(), checker.IsNil)
+	assert.Assert(c, s.d.Kill() == nil)
 	s.d.Restart(c)
 
 	// make sure all the containers are up and running
 	for i := 0; i < 10; i++ {
 		err := s.d.WaitRun(fmt.Sprintf("hostc-%d", i))
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	}
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkConnectToHostFromOtherNetwork(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkConnectToHostFromOtherNetwork(c *testing.T) {
 	dockerCmd(c, "run", "-d", "--name", "container1", "busybox", "top")
-	c.Assert(waitRun("container1"), check.IsNil)
+	assert.Assert(c, waitRun("container1") == nil)
 	dockerCmd(c, "network", "disconnect", "bridge", "container1")
 	out, _, err := dockerCmdWithError("network", "connect", "host", "container1")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, runconfig.ErrConflictHostNetwork.Error())
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetwork.Error()))
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromHost(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromHost(c *testing.T) {
 	dockerCmd(c, "run", "-d", "--name", "container1", "--net=host", "busybox", "top")
-	c.Assert(waitRun("container1"), check.IsNil)
+	assert.Assert(c, waitRun("container1") == nil)
 	out, _, err := dockerCmdWithError("network", "disconnect", "host", "container1")
-	c.Assert(err, checker.NotNil, check.Commentf("Should err out disconnect from host"))
-	c.Assert(out, checker.Contains, runconfig.ErrConflictHostNetwork.Error())
+	assert.Assert(c, err != nil, "Should err out disconnect from host")
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictHostNetwork.Error()))
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkConnectWithPortMapping(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkConnectWithPortMapping(c *testing.T) {
 	testRequires(c, NotArm)
 	dockerCmd(c, "network", "create", "test1")
 	dockerCmd(c, "run", "-d", "--name", "c1", "-p", "5000:5000", "busybox", "top")
-	c.Assert(waitRun("c1"), check.IsNil)
+	assert.Assert(c, waitRun("c1") == nil)
 	dockerCmd(c, "network", "connect", "test1", "c1")
 }
 
-func verifyPortMap(c *check.C, container, port, originalMapping string, mustBeEqual bool) {
-	chk := checker.Equals
-	if !mustBeEqual {
-		chk = checker.Not(checker.Equals)
-	}
+func verifyPortMap(c *testing.T, container, port, originalMapping string, mustBeEqual bool) {
 	currentMapping, _ := dockerCmd(c, "port", container, port)
-	c.Assert(currentMapping, chk, originalMapping)
+	if mustBeEqual {
+		assert.Equal(c, currentMapping, originalMapping)
+	} else {
+		assert.Assert(c, currentMapping != originalMapping)
+	}
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectWithPortMapping(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectWithPortMapping(c *testing.T) {
 	// Connect and disconnect a container with explicit and non-explicit
 	// host port mapping to/from networks which do cause and do not cause
 	// the container default gateway to change, and verify docker port cmd
@@ -1200,7 +1167,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectWithPortMapping(c
 	dockerCmd(c, "network", "create", "ccc")
 
 	dockerCmd(c, "run", "-d", "--name", cnt, "-p", "9000:90", "-p", "70", "busybox", "top")
-	c.Assert(waitRun(cnt), check.IsNil)
+	assert.Assert(c, waitRun(cnt) == nil)
 	curPortMap, _ := dockerCmd(c, "port", cnt, "70")
 	curExplPortMap, _ := dockerCmd(c, "port", cnt, "90")
 
@@ -1226,51 +1193,49 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectWithPortMapping(c
 	verifyPortMap(c, cnt, "90", curExplPortMap, true)
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkConnectWithMac(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkConnectWithMac(c *testing.T) {
 	macAddress := "02:42:ac:11:00:02"
 	dockerCmd(c, "network", "create", "mynetwork")
 	dockerCmd(c, "run", "--name=test", "-d", "--mac-address", macAddress, "busybox", "top")
-	c.Assert(waitRun("test"), check.IsNil)
+	assert.Assert(c, waitRun("test") == nil)
 	mac1 := inspectField(c, "test", "NetworkSettings.Networks.bridge.MacAddress")
-	c.Assert(strings.TrimSpace(mac1), checker.Equals, macAddress)
+	assert.Equal(c, strings.TrimSpace(mac1), macAddress)
 	dockerCmd(c, "network", "connect", "mynetwork", "test")
 	mac2 := inspectField(c, "test", "NetworkSettings.Networks.mynetwork.MacAddress")
-	c.Assert(strings.TrimSpace(mac2), checker.Not(checker.Equals), strings.TrimSpace(mac1))
+	assert.Assert(c, strings.TrimSpace(mac2) != strings.TrimSpace(mac1))
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkInspectCreatedContainer(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkInspectCreatedContainer(c *testing.T) {
 	dockerCmd(c, "create", "--name", "test", "busybox")
 	networks := inspectField(c, "test", "NetworkSettings.Networks")
-	c.Assert(networks, checker.Contains, "bridge", check.Commentf("Should return 'bridge' network"))
+	assert.Assert(c, strings.Contains(networks, "bridge"), "Should return 'bridge' network")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkRestartWithMultipleNetworks(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkRestartWithMultipleNetworks(c *testing.T) {
 	dockerCmd(c, "network", "create", "test")
 	dockerCmd(c, "run", "--name=foo", "-d", "busybox", "top")
-	c.Assert(waitRun("foo"), checker.IsNil)
+	assert.Assert(c, waitRun("foo") == nil)
 	dockerCmd(c, "network", "connect", "test", "foo")
 	dockerCmd(c, "restart", "foo")
 	networks := inspectField(c, "foo", "NetworkSettings.Networks")
-	c.Assert(networks, checker.Contains, "bridge", check.Commentf("Should contain 'bridge' network"))
-	c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network"))
+	assert.Assert(c, strings.Contains(networks, "bridge"), "Should contain 'bridge' network")
+	assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectToStoppedContainer(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectToStoppedContainer(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	dockerCmd(c, "network", "create", "test")
 	dockerCmd(c, "create", "--name=foo", "busybox", "top")
 	dockerCmd(c, "network", "connect", "test", "foo")
 	networks := inspectField(c, "foo", "NetworkSettings.Networks")
-	c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network"))
-
+	assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network")
 	// Restart docker daemon to test the config has persisted to disk
 	s.d.Restart(c)
 	networks = inspectField(c, "foo", "NetworkSettings.Networks")
-	c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network"))
-
+	assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network")
 	// start the container and test if we can ping it from another container in the same network
 	dockerCmd(c, "start", "foo")
-	c.Assert(waitRun("foo"), checker.IsNil)
+	assert.Assert(c, waitRun("foo") == nil)
 	ip := inspectField(c, "foo", "NetworkSettings.Networks.test.IPAddress")
 	ip = strings.TrimSpace(ip)
 	dockerCmd(c, "run", "--net=test", "busybox", "sh", "-c", fmt.Sprintf("ping -c 1 %s", ip))
@@ -1280,21 +1245,18 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnectToStoppedContaine
 	// Test disconnect
 	dockerCmd(c, "network", "disconnect", "test", "foo")
 	networks = inspectField(c, "foo", "NetworkSettings.Networks")
-	c.Assert(networks, checker.Not(checker.Contains), "test", check.Commentf("Should not contain 'test' network"))
-
+	assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network")
 	// Restart docker daemon to test the config has persisted to disk
 	s.d.Restart(c)
 	networks = inspectField(c, "foo", "NetworkSettings.Networks")
-	c.Assert(networks, checker.Not(checker.Contains), "test", check.Commentf("Should not contain 'test' network"))
-
+	assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkDisconnectContainerNonexistingNetwork(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkDisconnectContainerNonexistingNetwork(c *testing.T) {
 	dockerCmd(c, "network", "create", "test")
 	dockerCmd(c, "run", "--net=test", "-d", "--name=foo", "busybox", "top")
 	networks := inspectField(c, "foo", "NetworkSettings.Networks")
-	c.Assert(networks, checker.Contains, "test", check.Commentf("Should contain 'test' network"))
-
+	assert.Assert(c, strings.Contains(networks, "test"), "Should contain 'test' network")
 	// Stop container and remove network
 	dockerCmd(c, "stop", "foo")
 	dockerCmd(c, "network", "rm", "test")
@@ -1302,10 +1264,10 @@ func (s *DockerNetworkSuite) TestDockerNetworkDisconnectContainerNonexistingNetw
 	// Test disconnecting stopped container from nonexisting network
 	dockerCmd(c, "network", "disconnect", "-f", "test", "foo")
 	networks = inspectField(c, "foo", "NetworkSettings.Networks")
-	c.Assert(networks, checker.Not(checker.Contains), "test", check.Commentf("Should not contain 'test' network"))
+	assert.Assert(c, !strings.Contains(networks, "test"), "Should not contain 'test' network")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *testing.T) {
 	// create two networks
 	dockerCmd(c, "network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "n0")
 	assertNwIsAvailable(c, "n0")
@@ -1315,7 +1277,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *check.C) {
 
 	// run a container on first network specifying the ip addresses
 	dockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top")
-	c.Assert(waitRun("c0"), check.IsNil)
+	assert.Assert(c, waitRun("c0") == nil)
 	verifyIPAddressConfig(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988")
 	verifyIPAddresses(c, "c0", "n0", "172.28.99.88", "2001:db8:1234::9988")
 
@@ -1336,12 +1298,11 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIP(c *check.C) {
 
 	// Still it should fail to connect to the default network with a specified IP (whatever ip)
 	out, _, err := dockerCmdWithError("network", "connect", "--ip", "172.21.55.44", "bridge", "c0")
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
-	c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkAndIP.Error())
-
+	assert.Assert(c, err != nil, "out: %s", out)
+	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndIP.Error()))
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIPStoppedContainer(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIPStoppedContainer(c *testing.T) {
 	// create a container
 	dockerCmd(c, "create", "--name", "c0", "busybox", "top")
 
@@ -1355,7 +1316,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIPStoppedContainer
 
 	// start the container, verify config has not changed and ip addresses are assigned
 	dockerCmd(c, "start", "c0")
-	c.Assert(waitRun("c0"), check.IsNil)
+	assert.Assert(c, waitRun("c0") == nil)
 	verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544")
 	verifyIPAddresses(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544")
 
@@ -1364,7 +1325,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectPreferredIPStoppedContainer
 	verifyIPAddressConfig(c, "c0", "n0", "172.30.55.44", "2001:db8:abcd::5544")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkUnsupportedRequiredIP(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkUnsupportedRequiredIP(c *testing.T) {
 	// requested IP is not supported on predefined networks
 	for _, mode := range []string{"none", "host", "bridge", "default"} {
 		checkUnsupportedNetworkAndIP(c, mode)
@@ -1375,72 +1336,70 @@ func (s *DockerNetworkSuite) TestDockerNetworkUnsupportedRequiredIP(c *check.C)
 	assertNwIsAvailable(c, "n0")
 
 	out, _, err := dockerCmdWithError("run", "-d", "--ip", "172.28.99.88", "--net", "n0", "busybox", "top")
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
-	c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error())
-
+	assert.Assert(c, err != nil, "out: %s", out)
+	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error()))
 	out, _, err = dockerCmdWithError("run", "-d", "--ip6", "2001:db8:1234::9988", "--net", "n0", "busybox", "top")
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
-	c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error())
-
+	assert.Assert(c, err != nil, "out: %s", out)
+	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkNoSubnetAndIP.Error()))
 	dockerCmd(c, "network", "rm", "n0")
 	assertNwNotAvailable(c, "n0")
 }
 
-func checkUnsupportedNetworkAndIP(c *check.C, nwMode string) {
+func checkUnsupportedNetworkAndIP(c *testing.T, nwMode string) {
 	out, _, err := dockerCmdWithError("run", "-d", "--net", nwMode, "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top")
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
-	c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkAndIP.Error())
+	assert.Assert(c, err != nil, "out: %s", out)
+	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndIP.Error()))
 }
 
-func verifyIPAddressConfig(c *check.C, cName, nwname, ipv4, ipv6 string) {
+func verifyIPAddressConfig(c *testing.T, cName, nwname, ipv4, ipv6 string) {
 	if ipv4 != "" {
 		out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAMConfig.IPv4Address", nwname))
-		c.Assert(strings.TrimSpace(out), check.Equals, ipv4)
+		assert.Equal(c, strings.TrimSpace(out), ipv4)
 	}
 
 	if ipv6 != "" {
 		out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAMConfig.IPv6Address", nwname))
-		c.Assert(strings.TrimSpace(out), check.Equals, ipv6)
+		assert.Equal(c, strings.TrimSpace(out), ipv6)
 	}
 }
 
-func verifyIPAddresses(c *check.C, cName, nwname, ipv4, ipv6 string) {
+func verifyIPAddresses(c *testing.T, cName, nwname, ipv4, ipv6 string) {
 	out := inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.IPAddress", nwname))
-	c.Assert(strings.TrimSpace(out), check.Equals, ipv4)
+	assert.Equal(c, strings.TrimSpace(out), ipv4)
 
 	out = inspectField(c, cName, fmt.Sprintf("NetworkSettings.Networks.%s.GlobalIPv6Address", nwname))
-	c.Assert(strings.TrimSpace(out), check.Equals, ipv6)
+	assert.Equal(c, strings.TrimSpace(out), ipv6)
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkConnectLinkLocalIP(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkConnectLinkLocalIP(c *testing.T) {
 	// create one test network
 	dockerCmd(c, "network", "create", "--ipv6", "--subnet=2001:db8:1234::/64", "n0")
 	assertNwIsAvailable(c, "n0")
 
 	// run a container with incorrect link-local address
-	_, _, err := dockerCmdWithError("run", "--link-local-ip", "169.253.5.5", "busybox", "top")
-	c.Assert(err, check.NotNil)
-	_, _, err = dockerCmdWithError("run", "--link-local-ip", "2001:db8::89", "busybox", "top")
-	c.Assert(err, check.NotNil)
+	_, _, err := dockerCmdWithError("run", "--link-local-ip", "169.253.5.5", "busybox", "true")
+	assert.ErrorContains(c, err, "")
+	_, _, err = dockerCmdWithError("run", "--link-local-ip", "2001:db8::89", "busybox", "true")
+	assert.ErrorContains(c, err, "")
 
 	// run two containers with link-local ip on the test network
 	dockerCmd(c, "run", "-d", "--name", "c0", "--net=n0", "--link-local-ip", "169.254.7.7", "--link-local-ip", "fe80::254:77", "busybox", "top")
-	c.Assert(waitRun("c0"), check.IsNil)
+	assert.Assert(c, waitRun("c0") == nil)
 	dockerCmd(c, "run", "-d", "--name", "c1", "--net=n0", "--link-local-ip", "169.254.8.8", "--link-local-ip", "fe80::254:88", "busybox", "top")
-	c.Assert(waitRun("c1"), check.IsNil)
+	assert.Assert(c, waitRun("c1") == nil)
 
 	// run a container on the default network and connect it to the test network specifying a link-local address
 	dockerCmd(c, "run", "-d", "--name", "c2", "busybox", "top")
-	c.Assert(waitRun("c2"), check.IsNil)
+	assert.Assert(c, waitRun("c2") == nil)
 	dockerCmd(c, "network", "connect", "--link-local-ip", "169.254.9.9", "n0", "c2")
 
 	// verify the three containers can ping each other via the link-local addresses
 	_, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// Stop and restart the three containers
 	dockerCmd(c, "stop", "c0")
@@ -1452,32 +1411,32 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectLinkLocalIP(c *check.C) {
 
 	// verify the ping again
 	_, _, err = dockerCmdWithError("exec", "c0", "ping", "-c", "1", "169.254.8.8")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "c1", "ping", "-c", "1", "169.254.9.9")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "c2", "ping", "-c", "1", "169.254.7.7")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectLink(c *check.C) {
+func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectLink(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	dockerCmd(c, "network", "create", "-d", "bridge", "foo1")
 	dockerCmd(c, "network", "create", "-d", "bridge", "foo2")
 
 	dockerCmd(c, "run", "-d", "--net=foo1", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 
 	// run a container in a user-defined network with a link for an existing container
 	// and a link for a container that doesn't exist
 	dockerCmd(c, "run", "-d", "--net=foo1", "--name=second", "--link=first:FirstInFoo1",
 		"--link=third:bar", "busybox", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 
 	// ping to first and its alias FirstInFoo1 must succeed
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// connect first container to foo2 network
 	dockerCmd(c, "network", "connect", "foo2", "first")
@@ -1486,21 +1445,21 @@ func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectLink(c *check.C) {
 
 	// ping the new alias in network foo2
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// disconnect first container from foo1 network
 	dockerCmd(c, "network", "disconnect", "foo1", "first")
 
 	// link in foo1 network must fail
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo1")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// link in foo2 network must succeed
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "FirstInFoo2")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkDisconnectDefault(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkDisconnectDefault(c *testing.T) {
 	netWorkName1 := "test1"
 	netWorkName2 := "test2"
 	containerName := "foo"
@@ -1513,14 +1472,14 @@ func (s *DockerNetworkSuite) TestDockerNetworkDisconnectDefault(c *check.C) {
 	dockerCmd(c, "network", "disconnect", "bridge", containerName)
 
 	dockerCmd(c, "start", containerName)
-	c.Assert(waitRun(containerName), checker.IsNil)
+	assert.Assert(c, waitRun(containerName) == nil)
 	networks := inspectField(c, containerName, "NetworkSettings.Networks")
-	c.Assert(networks, checker.Contains, netWorkName1, check.Commentf(fmt.Sprintf("Should contain '%s' network", netWorkName1)))
-	c.Assert(networks, checker.Contains, netWorkName2, check.Commentf(fmt.Sprintf("Should contain '%s' network", netWorkName2)))
-	c.Assert(networks, checker.Not(checker.Contains), "bridge", check.Commentf("Should not contain 'bridge' network"))
+	assert.Assert(c, strings.Contains(networks, netWorkName1), fmt.Sprintf("Should contain '%s' network", netWorkName1))
+	assert.Assert(c, strings.Contains(networks, netWorkName2), fmt.Sprintf("Should contain '%s' network", netWorkName2))
+	assert.Assert(c, !strings.Contains(networks, "bridge"), "Should not contain 'bridge' network")
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkConnectWithAliasOnDefaultNetworks(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkConnectWithAliasOnDefaultNetworks(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 
 	defaults := []string{"bridge", "host", "none"}
@@ -1528,31 +1487,31 @@ func (s *DockerNetworkSuite) TestDockerNetworkConnectWithAliasOnDefaultNetworks(
 	containerID := strings.TrimSpace(out)
 	for _, net := range defaults {
 		res, _, err := dockerCmdWithError("network", "connect", "--alias", "alias"+net, net, containerID)
-		c.Assert(err, checker.NotNil)
-		c.Assert(res, checker.Contains, runconfig.ErrUnsupportedNetworkAndAlias.Error())
+		assert.ErrorContains(c, err, "")
+		assert.Assert(c, strings.Contains(res, runconfig.ErrUnsupportedNetworkAndAlias.Error()))
 	}
 }
 
-func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectAlias(c *check.C) {
+func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectAlias(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	dockerCmd(c, "network", "create", "-d", "bridge", "net1")
 	dockerCmd(c, "network", "create", "-d", "bridge", "net2")
 
 	cid, _ := dockerCmd(c, "run", "-d", "--net=net1", "--name=first", "--net-alias=foo", "busybox:glibc", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 
 	dockerCmd(c, "run", "-d", "--net=net1", "--name=second", "busybox:glibc", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 
 	// ping first container and its alias
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// ping first container's short-id alias
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", stringid.TruncateID(cid))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// connect first container to net2 network
 	dockerCmd(c, "network", "connect", "--alias=bar", "net2", "first")
@@ -1561,98 +1520,97 @@ func (s *DockerSuite) TestUserDefinedNetworkConnectDisconnectAlias(c *check.C) {
 
 	// ping the new alias in network foo2
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// disconnect first container from net1 network
 	dockerCmd(c, "network", "disconnect", "net1", "first")
 
 	// ping to net1 scoped alias "foo" must fail
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// ping to net2 scoped alias "bar" must still succeed
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	// ping to net2 scoped alias short-id must still succeed
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", stringid.TruncateID(cid))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// verify the alias option is rejected when running on predefined network
-	out, _, err := dockerCmdWithError("run", "--rm", "--name=any", "--net-alias=any", "busybox:glibc", "top")
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
-	c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkAndAlias.Error())
-
+	out, _, err := dockerCmdWithError("run", "--rm", "--name=any", "--net-alias=any", "busybox:glibc", "true")
+	assert.Assert(c, err != nil, "out: %s", out)
+	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndAlias.Error()))
 	// verify the alias option is rejected when connecting to predefined network
 	out, _, err = dockerCmdWithError("network", "connect", "--alias=any", "bridge", "first")
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
-	c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkAndAlias.Error())
+	assert.Assert(c, err != nil, "out: %s", out)
+	assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndAlias.Error()))
 }
 
-func (s *DockerSuite) TestUserDefinedNetworkConnectivity(c *check.C) {
+func (s *DockerSuite) TestUserDefinedNetworkConnectivity(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	dockerCmd(c, "network", "create", "-d", "bridge", "br.net1")
 
 	dockerCmd(c, "run", "-d", "--net=br.net1", "--name=c1.net1", "busybox:glibc", "top")
-	c.Assert(waitRun("c1.net1"), check.IsNil)
+	assert.Assert(c, waitRun("c1.net1") == nil)
 
 	dockerCmd(c, "run", "-d", "--net=br.net1", "--name=c2.net1", "busybox:glibc", "top")
-	c.Assert(waitRun("c2.net1"), check.IsNil)
+	assert.Assert(c, waitRun("c2.net1") == nil)
 
 	// ping first container by its unqualified name
 	_, _, err := dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// ping first container by its qualified name
 	_, _, err = dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1.br.net1")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// ping with first qualified name masked by an additional domain. should fail
 	_, _, err = dockerCmdWithError("exec", "c2.net1", "ping", "-c", "1", "c1.net1.br.net1.google.com")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 }
 
-func (s *DockerSuite) TestEmbeddedDNSInvalidInput(c *check.C) {
+func (s *DockerSuite) TestEmbeddedDNSInvalidInput(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	dockerCmd(c, "network", "create", "-d", "bridge", "nw1")
 
 	// Sending garbage to embedded DNS shouldn't crash the daemon
-	dockerCmd(c, "run", "-i", "--net=nw1", "--name=c1", "debian:jessie", "bash", "-c", "echo InvalidQuery > /dev/udp/127.0.0.11/53")
+	dockerCmd(c, "run", "-i", "--net=nw1", "--name=c1", "debian:bullseye-slim", "bash", "-c", "echo InvalidQuery > /dev/udp/127.0.0.11/53")
 }
 
-func (s *DockerSuite) TestDockerNetworkConnectFailsNoInspectChange(c *check.C) {
+func (s *DockerSuite) TestDockerNetworkConnectFailsNoInspectChange(c *testing.T) {
 	dockerCmd(c, "run", "-d", "--name=bb", "busybox", "top")
-	c.Assert(waitRun("bb"), check.IsNil)
+	assert.Assert(c, waitRun("bb") == nil)
 	defer dockerCmd(c, "stop", "bb")
 
 	ns0 := inspectField(c, "bb", "NetworkSettings.Networks.bridge")
 
 	// A failing redundant network connect should not alter current container's endpoint settings
 	_, _, err := dockerCmdWithError("network", "connect", "bridge", "bb")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	ns1 := inspectField(c, "bb", "NetworkSettings.Networks.bridge")
-	c.Assert(ns1, check.Equals, ns0)
+	assert.Equal(c, ns1, ns0)
 }
 
-func (s *DockerSuite) TestDockerNetworkInternalMode(c *check.C) {
+func (s *DockerSuite) TestDockerNetworkInternalMode(c *testing.T) {
 	dockerCmd(c, "network", "create", "--driver=bridge", "--internal", "internal")
 	assertNwIsAvailable(c, "internal")
 	nr := getNetworkResource(c, "internal")
-	c.Assert(nr.Internal, checker.True)
+	assert.Assert(c, nr.Internal)
 
 	dockerCmd(c, "run", "-d", "--net=internal", "--name=first", "busybox:glibc", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 	dockerCmd(c, "run", "-d", "--net=internal", "--name=second", "busybox:glibc", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 	out, _, err := dockerCmdWithError("exec", "first", "ping", "-W", "4", "-c", "1", "8.8.8.8")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, "100% packet loss")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "100% packet loss"))
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
 // Test for #21401
-func (s *DockerNetworkSuite) TestDockerNetworkCreateDeleteSpecialCharacters(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkCreateDeleteSpecialCharacters(c *testing.T) {
 	dockerCmd(c, "network", "create", "test@#$")
 	assertNwIsAvailable(c, "test@#$")
 	dockerCmd(c, "network", "rm", "test@#$")
@@ -1664,8 +1622,7 @@ func (s *DockerNetworkSuite) TestDockerNetworkCreateDeleteSpecialCharacters(c *c
 	assertNwNotAvailable(c, "kiwl$%^")
 }
 
-func (s *DockerDaemonSuite) TestDaemonRestartRestoreBridgeNetwork(t *check.C) {
-	testRequires(t, DaemonIsLinux)
+func (s *DockerDaemonSuite) TestDaemonRestartRestoreBridgeNetwork(t *testing.T) {
 	s.d.StartWithBusybox(t, "--live-restore")
 	defer s.d.Stop(t)
 	oldCon := "old"
@@ -1726,7 +1683,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartRestoreBridgeNetwork(t *check.C) {
 	// Cleanup because these containers will not be shut down by daemon
 	out, err = s.d.Cmd("stop", newCon)
 	if err != nil {
-		t.Fatalf("err: %v %v", err, string(out))
+		t.Fatalf("err: %v %v", err, out)
 	}
 	_, err = s.d.Cmd("stop", strings.TrimSpace(id))
 	if err != nil {
@@ -1734,43 +1691,41 @@ func (s *DockerDaemonSuite) TestDaemonRestartRestoreBridgeNetwork(t *check.C) {
 	}
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkFlagAlias(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkFlagAlias(c *testing.T) {
 	dockerCmd(c, "network", "create", "user")
 	output, status := dockerCmd(c, "run", "--rm", "--network=user", "--network-alias=foo", "busybox", "true")
-	c.Assert(status, checker.Equals, 0, check.Commentf("unexpected status code %d (%s)", status, output))
-
-	output, status, _ = dockerCmdWithError("run", "--rm", "--net=user", "--network=user", "busybox", "true")
-	c.Assert(status, checker.Equals, 0, check.Commentf("unexpected status code %d (%s)", status, output))
+	assert.Equal(c, status, 0, fmt.Sprintf("unexpected status code %d (%s)", status, output))
 
 	output, status, _ = dockerCmdWithError("run", "--rm", "--network=user", "--net-alias=foo", "--network-alias=bar", "busybox", "true")
-	c.Assert(status, checker.Equals, 0, check.Commentf("unexpected status code %d (%s)", status, output))
+	assert.Equal(c, status, 0, fmt.Sprintf("unexpected status code %d (%s)", status, output))
 }
 
-func (s *DockerNetworkSuite) TestDockerNetworkValidateIP(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkValidateIP(c *testing.T) {
 	_, _, err := dockerCmdWithError("network", "create", "--ipv6", "--subnet=172.28.0.0/16", "--subnet=2001:db8:1234::/64", "mynet")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	assertNwIsAvailable(c, "mynet")
 
 	_, _, err = dockerCmdWithError("run", "-d", "--name", "mynet0", "--net=mynet", "--ip", "172.28.99.88", "--ip6", "2001:db8:1234::9988", "busybox", "top")
-	c.Assert(err, check.IsNil)
-	c.Assert(waitRun("mynet0"), check.IsNil)
+	assert.NilError(c, err)
+	assert.Assert(c, waitRun("mynet0") == nil)
 	verifyIPAddressConfig(c, "mynet0", "mynet", "172.28.99.88", "2001:db8:1234::9988")
 	verifyIPAddresses(c, "mynet0", "mynet", "172.28.99.88", "2001:db8:1234::9988")
 
 	_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip", "mynet_ip", "--ip6", "2001:db8:1234::9999", "busybox", "top")
-	c.Assert(err.Error(), checker.Contains, "invalid IPv4 address")
+	assert.ErrorContains(c, err, "invalid IPv4 address")
 	_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip", "172.28.99.99", "--ip6", "mynet_ip6", "busybox", "top")
-	c.Assert(err.Error(), checker.Contains, "invalid IPv6 address")
+	assert.ErrorContains(c, err, "invalid IPv6 address")
+
 	// This is a case of IPv4 address to `--ip6`
 	_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip6", "172.28.99.99", "busybox", "top")
-	c.Assert(err.Error(), checker.Contains, "invalid IPv6 address")
+	assert.ErrorContains(c, err, "invalid IPv6 address")
 	// This is a special case of an IPv4-mapped IPv6 address
 	_, _, err = dockerCmdWithError("run", "--net=mynet", "--ip6", "::ffff:172.28.99.99", "busybox", "top")
-	c.Assert(err.Error(), checker.Contains, "invalid IPv6 address")
+	assert.ErrorContains(c, err, "invalid IPv6 address")
 }
 
 // Test case for 26220
-func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromBridge(c *check.C) {
+func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromBridge(c *testing.T) {
 	out, _ := dockerCmd(c, "network", "inspect", "--format", "{{.Id}}", "bridge")
 
 	network := strings.TrimSpace(out)
@@ -1779,29 +1734,29 @@ func (s *DockerNetworkSuite) TestDockerNetworkDisconnectFromBridge(c *check.C) {
 	dockerCmd(c, "create", "--name", name, "busybox", "top")
 
 	_, _, err := dockerCmdWithError("network", "disconnect", network, name)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
 // TestConntrackFlowsLeak covers the failure scenario of ticket: https://github.com/docker/docker/issues/8795
 // Validates that conntrack is correctly cleaned once a container is destroyed
-func (s *DockerNetworkSuite) TestConntrackFlowsLeak(c *check.C) {
-	testRequires(c, IsAmd64, DaemonIsLinux, Network, SameHostDaemon)
+func (s *DockerNetworkSuite) TestConntrackFlowsLeak(c *testing.T) {
+	testRequires(c, IsAmd64, DaemonIsLinux, Network, testEnv.IsLocalDaemon)
 
 	// Create a new network
 	cli.DockerCmd(c, "network", "create", "--subnet=192.168.10.0/24", "--gateway=192.168.10.1", "-o", "com.docker.network.bridge.host_binding_ipv4=192.168.10.1", "testbind")
 	assertNwIsAvailable(c, "testbind")
 
 	// Launch the server, this will remain listening on an exposed port and reply to any request in a ping/pong fashion
-	cmd := "while true; do echo hello | nc -w 1 -lu 8080; done"
-	cli.DockerCmd(c, "run", "-d", "--name", "server", "--net", "testbind", "-p", "8080:8080/udp", "appropriate/nc", "sh", "-c", cmd)
+	cmd := "while true; do echo hello | nc -w 1 -l -u -p 8080; done"
+	cli.DockerCmd(c, "run", "-d", "--name", "server", "--net", "testbind", "-p", "8080:8080/udp", "busybox", "sh", "-c", cmd)
 
 	// Launch a container client, here the objective is to create a flow that is natted in order to expose the bug
-	cmd = "echo world | nc -q 1 -u 192.168.10.1 8080"
-	cli.DockerCmd(c, "run", "-d", "--name", "client", "--net=host", "appropriate/nc", "sh", "-c", cmd)
+	cmd = "echo world | nc -w 1 -u 192.168.10.1 8080"
+	cli.DockerCmd(c, "run", "-d", "--name", "client", "--net=host", "busybox", "sh", "-c", cmd)
 
 	// Get all the flows using netlink
 	flows, err := netlink.ConntrackTableList(netlink.ConntrackTable, unix.AF_INET)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	var flowMatch int
 	for _, flow := range flows {
 		// count only the flows that we are interested in, skipping others that can be laying around the host
@@ -1812,14 +1767,14 @@ func (s *DockerNetworkSuite) TestConntrackFlowsLeak(c *check.C) {
 		}
 	}
 	// The client should have created only 1 flow
-	c.Assert(flowMatch, checker.Equals, 1)
+	assert.Equal(c, flowMatch, 1)
 
 	// Now delete the server, this will trigger the conntrack cleanup
 	cli.DockerCmd(c, "rm", "-fv", "server")
 
 	// Fetch again all the flows and validate that there is no server flow in the conntrack laying around
 	flows, err = netlink.ConntrackTableList(netlink.ConntrackTable, unix.AF_INET)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	flowMatch = 0
 	for _, flow := range flows {
 		if flow.Forward.Protocol == unix.IPPROTO_UDP &&
@@ -1829,5 +1784,5 @@ func (s *DockerNetworkSuite) TestConntrackFlowsLeak(c *check.C) {
 		}
 	}
 	// All the flows have to be gone
-	c.Assert(flowMatch, checker.Equals, 0)
+	assert.Equal(c, flowMatch, 0)
 }
diff --git a/integration-cli/docker_cli_plugins_logdriver_test.go b/integration-cli/docker_cli_plugins_logdriver_test.go
index 7d1ffcb632a5f..0c521ade7cf06 100644
--- a/integration-cli/docker_cli_plugins_logdriver_test.go
+++ b/integration-cli/docker_cli_plugins_logdriver_test.go
@@ -3,13 +3,13 @@ package main
 import (
 	"context"
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestPluginLogDriver(c *check.C) {
+func (s *DockerSuite) TestPluginLogDriver(c *testing.T) {
 	testRequires(c, IsAmd64, DaemonIsLinux)
 
 	pluginName := "cpuguy83/docker-logdriver-test:latest"
@@ -17,11 +17,11 @@ func (s *DockerSuite) TestPluginLogDriver(c *check.C) {
 	dockerCmd(c, "plugin", "install", pluginName)
 	dockerCmd(c, "run", "--log-driver", pluginName, "--name=test", "busybox", "echo", "hello")
 	out, _ := dockerCmd(c, "logs", "test")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello")
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 
 	dockerCmd(c, "start", "-a", "test")
 	out, _ = dockerCmd(c, "logs", "test")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello\nhello")
+	assert.Equal(c, strings.TrimSpace(out), "hello\nhello")
 
 	dockerCmd(c, "rm", "test")
 	dockerCmd(c, "plugin", "disable", pluginName)
@@ -29,20 +29,20 @@ func (s *DockerSuite) TestPluginLogDriver(c *check.C) {
 }
 
 // Make sure log drivers are listed in info, and v2 plugins are not.
-func (s *DockerSuite) TestPluginLogDriverInfoList(c *check.C) {
+func (s *DockerSuite) TestPluginLogDriverInfoList(c *testing.T) {
 	testRequires(c, IsAmd64, DaemonIsLinux)
 	pluginName := "cpuguy83/docker-logdriver-test"
 
 	dockerCmd(c, "plugin", "install", pluginName)
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	info, err := cli.Info(context.Background())
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	drivers := strings.Join(info.Plugins.Log, " ")
-	c.Assert(drivers, checker.Contains, "json-file")
-	c.Assert(drivers, checker.Not(checker.Contains), pluginName)
+	assert.Assert(c, strings.Contains(drivers, "json-file"))
+	assert.Assert(c, !strings.Contains(drivers, pluginName))
 }
diff --git a/integration-cli/docker_cli_plugins_test.go b/integration-cli/docker_cli_plugins_test.go
index 2cc5bfe2ff7e5..716186766919a 100644
--- a/integration-cli/docker_cli_plugins_test.go
+++ b/integration-cli/docker_cli_plugins_test.go
@@ -3,20 +3,20 @@ package main
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"os"
 	"path"
 	"path/filepath"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/daemon"
-	"github.com/docker/docker/internal/test/fixtures/plugin"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/fixtures/plugin"
+	"gotest.tools/v3/assert"
 )
 
 var (
@@ -28,135 +28,123 @@ var (
 	npNameWithTag     = npName + ":" + pTag
 )
 
-func (ps *DockerPluginSuite) TestPluginBasicOps(c *check.C) {
+func (ps *DockerPluginSuite) TestPluginBasicOps(c *testing.T) {
 	plugin := ps.getPluginRepoWithTag()
 	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", plugin)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err := dockerCmdWithError("plugin", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, plugin)
-	c.Assert(out, checker.Contains, "true")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, plugin))
+	assert.Assert(c, strings.Contains(out, "true"))
 	id, _, err := dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", plugin)
 	id = strings.TrimSpace(id)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err = dockerCmdWithError("plugin", "remove", plugin)
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "is enabled")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "is enabled"))
 	_, _, err = dockerCmdWithError("plugin", "disable", plugin)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err = dockerCmdWithError("plugin", "remove", plugin)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, plugin)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, plugin))
 	_, err = os.Stat(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "plugins", id))
 	if !os.IsNotExist(err) {
 		c.Fatal(err)
 	}
 }
 
-func (ps *DockerPluginSuite) TestPluginForceRemove(c *check.C) {
+func (ps *DockerPluginSuite) TestPluginForceRemove(c *testing.T) {
 	pNameWithTag := ps.getPluginRepoWithTag()
 
 	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, _ := dockerCmdWithError("plugin", "remove", pNameWithTag)
-	c.Assert(out, checker.Contains, "is enabled")
-
+	assert.Assert(c, strings.Contains(out, "is enabled"))
 	out, _, err = dockerCmdWithError("plugin", "remove", "--force", pNameWithTag)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, pNameWithTag)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, pNameWithTag))
 }
 
-func (s *DockerSuite) TestPluginActive(c *check.C) {
+func (s *DockerSuite) TestPluginActive(c *testing.T) {
 	testRequires(c, DaemonIsLinux, IsAmd64, Network)
 
 	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	_, _, err = dockerCmdWithError("volume", "create", "-d", pNameWithTag, "--name", "testvol1")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, _ := dockerCmdWithError("plugin", "disable", pNameWithTag)
-	c.Assert(out, checker.Contains, "in use")
-
+	assert.Assert(c, strings.Contains(out, "in use"))
 	_, _, err = dockerCmdWithError("volume", "rm", "testvol1")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	_, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, pNameWithTag)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, pNameWithTag))
 }
 
-func (s *DockerSuite) TestPluginActiveNetwork(c *check.C) {
+func (s *DockerSuite) TestPluginActiveNetwork(c *testing.T) {
 	testRequires(c, DaemonIsLinux, IsAmd64, Network)
 	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", npNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err := dockerCmdWithError("network", "create", "-d", npNameWithTag, "test")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	nID := strings.TrimSpace(out)
 
 	out, _, _ = dockerCmdWithError("plugin", "remove", npNameWithTag)
-	c.Assert(out, checker.Contains, "is in use")
-
+	assert.Assert(c, strings.Contains(out, "is in use"))
 	_, _, err = dockerCmdWithError("network", "rm", nID)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, _ = dockerCmdWithError("plugin", "remove", npNameWithTag)
-	c.Assert(out, checker.Contains, "is enabled")
-
+	assert.Assert(c, strings.Contains(out, "is enabled"))
 	_, _, err = dockerCmdWithError("plugin", "disable", npNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err = dockerCmdWithError("plugin", "remove", npNameWithTag)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, npNameWithTag)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, npNameWithTag))
 }
 
-func (ps *DockerPluginSuite) TestPluginInstallDisable(c *check.C) {
+func (ps *DockerPluginSuite) TestPluginInstallDisable(c *testing.T) {
 	pName := ps.getPluginRepoWithTag()
 
 	out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", "--disable", pName)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, pName)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName))
 	out, _, err = dockerCmdWithError("plugin", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "false")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, "false"))
 	out, _, err = dockerCmdWithError("plugin", "enable", pName)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, pName)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName))
 	out, _, err = dockerCmdWithError("plugin", "disable", pName)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, pName)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName))
 	out, _, err = dockerCmdWithError("plugin", "remove", pName)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, pName)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName))
 }
 
-func (s *DockerSuite) TestPluginInstallDisableVolumeLs(c *check.C) {
+func (s *DockerSuite) TestPluginInstallDisableVolumeLs(c *testing.T) {
 	testRequires(c, DaemonIsLinux, IsAmd64, Network)
 	out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", "--disable", pName)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, pName)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName))
 	dockerCmd(c, "volume", "ls")
 }
 
-func (ps *DockerPluginSuite) TestPluginSet(c *check.C) {
+func (ps *DockerPluginSuite) TestPluginSet(c *testing.T) {
 	client := testEnv.APIClient()
 
 	name := "test"
@@ -179,35 +167,31 @@ func (ps *DockerPluginSuite) TestPluginSet(c *check.C) {
 			{Name: "pdev2", Settable: []string{"path"}}, // Device without Path is invalid.
 		}
 	})
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create test plugin"))
+	assert.Assert(c, err == nil, "failed to create test plugin")
 
 	env, _ := dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", name)
-	c.Assert(strings.TrimSpace(env), checker.Equals, "[DEBUG=0]")
+	assert.Equal(c, strings.TrimSpace(env), "[DEBUG=0]")
 
 	dockerCmd(c, "plugin", "set", name, "DEBUG=1")
 
 	env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", name)
-	c.Assert(strings.TrimSpace(env), checker.Equals, "[DEBUG=1]")
+	assert.Equal(c, strings.TrimSpace(env), "[DEBUG=1]")
 
 	env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{with $mount := index .Settings.Mounts 0}}{{$mount.Source}}{{end}}", name)
-	c.Assert(strings.TrimSpace(env), checker.Contains, mntSrc)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(env), mntSrc))
 	dockerCmd(c, "plugin", "set", name, "pmount1.source=bar")
 
 	env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{with $mount := index .Settings.Mounts 0}}{{$mount.Source}}{{end}}", name)
-	c.Assert(strings.TrimSpace(env), checker.Contains, "bar")
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(env), "bar"))
 	out, _, err := dockerCmdWithError("plugin", "set", name, "pmount2.source=bar2")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "Plugin config has no mount source")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Plugin config has no mount source"))
 	out, _, err = dockerCmdWithError("plugin", "set", name, "pdev2.path=/dev/bar2")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "Plugin config has no device path")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Plugin config has no device path"))
 }
 
-func (ps *DockerPluginSuite) TestPluginInstallArgs(c *check.C) {
+func (ps *DockerPluginSuite) TestPluginInstallArgs(c *testing.T) {
 	pName := path.Join(ps.registryHost(), "plugin", "testplugininstallwithargs")
 	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
 	defer cancel()
@@ -217,13 +201,12 @@ func (ps *DockerPluginSuite) TestPluginInstallArgs(c *check.C) {
 	})
 
 	out, _ := dockerCmd(c, "plugin", "install", "--grant-all-permissions", "--disable", pName, "DEBUG=1")
-	c.Assert(strings.TrimSpace(out), checker.Contains, pName)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName))
 	env, _ := dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", pName)
-	c.Assert(strings.TrimSpace(env), checker.Equals, "[DEBUG=1]")
+	assert.Equal(c, strings.TrimSpace(env), "[DEBUG=1]")
 }
 
-func (ps *DockerPluginSuite) TestPluginInstallImage(c *check.C) {
+func (ps *DockerPluginSuite) TestPluginInstallImage(c *testing.T) {
 	testRequires(c, IsAmd64)
 
 	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
@@ -233,125 +216,117 @@ func (ps *DockerPluginSuite) TestPluginInstallImage(c *check.C) {
 	dockerCmd(c, "push", repoName)
 
 	out, _, err := dockerCmdWithError("plugin", "install", repoName)
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, `Encountered remote "application/vnd.docker.container.image.v1+json"(image) when fetching`)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, `Encountered remote "application/vnd.docker.container.image.v1+json"(image) when fetching`))
 }
 
-func (ps *DockerPluginSuite) TestPluginEnableDisableNegative(c *check.C) {
+func (ps *DockerPluginSuite) TestPluginEnableDisableNegative(c *testing.T) {
 	pName := ps.getPluginRepoWithTag()
 
 	out, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pName)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, pName)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), pName))
 	out, _, err = dockerCmdWithError("plugin", "enable", pName)
-	c.Assert(err, checker.NotNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, "already enabled")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "already enabled"))
 	_, _, err = dockerCmdWithError("plugin", "disable", pName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err = dockerCmdWithError("plugin", "disable", pName)
-	c.Assert(err, checker.NotNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, "already disabled")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "already disabled"))
 	_, _, err = dockerCmdWithError("plugin", "remove", pName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
 
-func (ps *DockerPluginSuite) TestPluginCreate(c *check.C) {
+func (ps *DockerPluginSuite) TestPluginCreate(c *testing.T) {
 	name := "foo/bar-driver"
-	temp, err := ioutil.TempDir("", "foo")
-	c.Assert(err, checker.IsNil)
+	temp, err := os.MkdirTemp("", "foo")
+	assert.NilError(c, err)
 	defer os.RemoveAll(temp)
 
 	data := `{"description": "foo plugin"}`
-	err = ioutil.WriteFile(filepath.Join(temp, "config.json"), []byte(data), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(temp, "config.json"), []byte(data), 0644)
+	assert.NilError(c, err)
 
 	err = os.MkdirAll(filepath.Join(temp, "rootfs"), 0700)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err := dockerCmdWithError("plugin", "create", name, temp)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, name)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, name))
 	out, _, err = dockerCmdWithError("plugin", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, name)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, name))
 	out, _, err = dockerCmdWithError("plugin", "create", name, temp)
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "already exist")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "already exist"))
 	out, _, err = dockerCmdWithError("plugin", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, name)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, name))
 	// The output will consists of one HEADER line and one line of foo/bar-driver
-	c.Assert(len(strings.Split(strings.TrimSpace(out), "\n")), checker.Equals, 2)
+	assert.Equal(c, len(strings.Split(strings.TrimSpace(out), "\n")), 2)
 }
 
-func (ps *DockerPluginSuite) TestPluginInspect(c *check.C) {
+func (ps *DockerPluginSuite) TestPluginInspect(c *testing.T) {
 	pNameWithTag := ps.getPluginRepoWithTag()
 
 	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err := dockerCmdWithError("plugin", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, pNameWithTag)
-	c.Assert(out, checker.Contains, "true")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, pNameWithTag))
+	assert.Assert(c, strings.Contains(out, "true"))
 	// Find the ID first
 	out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	id := strings.TrimSpace(out)
-	c.Assert(id, checker.Not(checker.Equals), "")
+	assert.Assert(c, id != "")
 
 	// Long form
 	out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", id)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, id)
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), id)
 
 	// Short form
 	out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", id[:5])
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, id)
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), id)
 
 	// Name with tag form
 	out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pNameWithTag)
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, id)
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), id)
 
 	// Name without tag form
 	out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", ps.getPluginRepo())
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, id)
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), id)
 
 	_, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, pNameWithTag)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, pNameWithTag))
 	// After remove nothing should be found
 	_, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", id[:5])
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 }
 
 // Test case for https://github.com/docker/docker/pull/29186#discussion_r91277345
-func (s *DockerSuite) TestPluginInspectOnWindows(c *check.C) {
+func (s *DockerSuite) TestPluginInspectOnWindows(c *testing.T) {
 	// This test should work on Windows only
 	testRequires(c, DaemonIsWindows)
 
 	out, _, err := dockerCmdWithError("plugin", "inspect", "foobar")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "plugins are not supported on this platform")
-	c.Assert(err.Error(), checker.Contains, "plugins are not supported on this platform")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "plugins are not supported on this platform"))
+	assert.ErrorContains(c, err, "plugins are not supported on this platform")
 }
 
-func (ps *DockerPluginSuite) TestPluginIDPrefix(c *check.C) {
+func (ps *DockerPluginSuite) TestPluginIDPrefix(c *testing.T) {
 	name := "test"
 	client := testEnv.APIClient()
 
@@ -362,59 +337,56 @@ func (ps *DockerPluginSuite) TestPluginIDPrefix(c *check.C) {
 	})
 	cancel()
 
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create test plugin"))
+	assert.Assert(c, err == nil, "failed to create test plugin")
 
 	// Find ID first
 	id, _, err := dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", name)
 	id = strings.TrimSpace(id)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// List current state
 	out, _, err := dockerCmdWithError("plugin", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, name)
-	c.Assert(out, checker.Contains, "false")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, name))
+	assert.Assert(c, strings.Contains(out, "false"))
 	env, _ := dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", id[:5])
-	c.Assert(strings.TrimSpace(env), checker.Equals, "[DEBUG=0]")
+	assert.Equal(c, strings.TrimSpace(env), "[DEBUG=0]")
 
 	dockerCmd(c, "plugin", "set", id[:5], "DEBUG=1")
 
 	env, _ = dockerCmd(c, "plugin", "inspect", "-f", "{{.Settings.Env}}", id[:5])
-	c.Assert(strings.TrimSpace(env), checker.Equals, "[DEBUG=1]")
+	assert.Equal(c, strings.TrimSpace(env), "[DEBUG=1]")
 
 	// Enable
 	_, _, err = dockerCmdWithError("plugin", "enable", id[:5])
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	out, _, err = dockerCmdWithError("plugin", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, name)
-	c.Assert(out, checker.Contains, "true")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, name))
+	assert.Assert(c, strings.Contains(out, "true"))
 	// Disable
 	_, _, err = dockerCmdWithError("plugin", "disable", id[:5])
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	out, _, err = dockerCmdWithError("plugin", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, name)
-	c.Assert(out, checker.Contains, "false")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, name))
+	assert.Assert(c, strings.Contains(out, "false"))
 	// Remove
 	_, _, err = dockerCmdWithError("plugin", "remove", id[:5])
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	// List returns none
 	out, _, err = dockerCmdWithError("plugin", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Not(checker.Contains), name)
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(out, name))
 }
 
-func (ps *DockerPluginSuite) TestPluginListDefaultFormat(c *check.C) {
-	config, err := ioutil.TempDir("", "config-file-")
-	c.Assert(err, check.IsNil)
+func (ps *DockerPluginSuite) TestPluginListDefaultFormat(c *testing.T) {
+	config, err := os.MkdirTemp("", "config-file-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(config)
 
-	err = ioutil.WriteFile(filepath.Join(config, "config.json"), []byte(`{"pluginsFormat": "raw"}`), 0644)
-	c.Assert(err, check.IsNil)
+	err = os.WriteFile(filepath.Join(config, "config.json"), []byte(`{"pluginsFormat": "raw"}`), 0644)
+	assert.NilError(c, err)
 
 	name := "test:latest"
 	client := testEnv.APIClient()
@@ -424,7 +396,7 @@ func (ps *DockerPluginSuite) TestPluginListDefaultFormat(c *check.C) {
 	err = plugin.Create(ctx, client, name, func(cfg *plugin.Config) {
 		cfg.Description = "test plugin"
 	})
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create test plugin"))
+	assert.Assert(c, err == nil, "failed to create test plugin")
 
 	out, _ := dockerCmd(c, "plugin", "inspect", "--format", "{{.ID}}", name)
 	id := strings.TrimSpace(out)
@@ -436,11 +408,11 @@ description: test plugin
 enabled: false`, id, name)
 
 	out, _ = dockerCmd(c, "--config", config, "plugin", "ls", "--no-trunc")
-	c.Assert(strings.TrimSpace(out), checker.Contains, expectedOutput)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), expectedOutput))
 }
 
-func (s *DockerSuite) TestPluginUpgrade(c *check.C) {
-	testRequires(c, DaemonIsLinux, Network, SameHostDaemon, IsAmd64, NotUserNamespace)
+func (s *DockerSuite) TestPluginUpgrade(c *testing.T) {
+	testRequires(c, DaemonIsLinux, Network, testEnv.IsLocalDaemon, IsAmd64, NotUserNamespace)
 	plugin := "cpuguy83/docker-volume-driver-plugin-local:latest"
 	pluginV2 := "cpuguy83/docker-volume-driver-plugin-local:v2"
 
@@ -449,45 +421,44 @@ func (s *DockerSuite) TestPluginUpgrade(c *check.C) {
 	dockerCmd(c, "run", "--rm", "-v", "bananas:/apple", "busybox", "sh", "-c", "touch /apple/core")
 
 	out, _, err := dockerCmdWithError("plugin", "upgrade", "--grant-all-permissions", plugin, pluginV2)
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "disabled before upgrading")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "disabled before upgrading"))
 	out, _ = dockerCmd(c, "plugin", "inspect", "--format={{.ID}}", plugin)
 	id := strings.TrimSpace(out)
 
 	// make sure "v2" does not exists
 	_, err = os.Stat(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "plugins", id, "rootfs", "v2"))
-	c.Assert(os.IsNotExist(err), checker.True, check.Commentf("%s", out))
+	assert.Assert(c, os.IsNotExist(err), out)
 
 	dockerCmd(c, "plugin", "disable", "-f", plugin)
 	dockerCmd(c, "plugin", "upgrade", "--grant-all-permissions", "--skip-remote-check", plugin, pluginV2)
 
 	// make sure "v2" file exists
 	_, err = os.Stat(filepath.Join(testEnv.DaemonInfo.DockerRootDir, "plugins", id, "rootfs", "v2"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "plugin", "enable", plugin)
 	dockerCmd(c, "volume", "inspect", "bananas")
 	dockerCmd(c, "run", "--rm", "-v", "bananas:/apple", "busybox", "sh", "-c", "ls -lh /apple/core")
 }
 
-func (s *DockerSuite) TestPluginMetricsCollector(c *check.C) {
-	testRequires(c, DaemonIsLinux, Network, SameHostDaemon, IsAmd64)
+func (s *DockerSuite) TestPluginMetricsCollector(c *testing.T) {
+	testRequires(c, DaemonIsLinux, Network, testEnv.IsLocalDaemon, IsAmd64)
 	d := daemon.New(c, dockerBinary, dockerdBinary)
 	d.Start(c)
 	defer d.Stop(c)
 
 	name := "cpuguy83/docker-metrics-plugin-test:latest"
 	r := cli.Docker(cli.Args("plugin", "install", "--grant-all-permissions", name), cli.Daemon(d))
-	c.Assert(r.Error, checker.IsNil, check.Commentf(r.Combined()))
+	assert.Assert(c, r.Error == nil, r.Combined())
 
 	// plugin lisens on localhost:19393 and proxies the metrics
 	resp, err := http.Get("http://localhost:19393/metrics")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer resp.Body.Close()
 
-	b, err := ioutil.ReadAll(resp.Body)
-	c.Assert(err, checker.IsNil)
+	b, err := io.ReadAll(resp.Body)
+	assert.NilError(c, err)
 	// check that a known metric is there... don't expect this metric to change over time.. probably safe
-	c.Assert(string(b), checker.Contains, "container_actions")
+	assert.Assert(c, strings.Contains(string(b), "container_actions"))
 }
diff --git a/integration-cli/docker_cli_port_test.go b/integration-cli/docker_cli_port_test.go
index 84058cda10c44..dd41d9891fd35 100644
--- a/integration-cli/docker_cli_port_test.go
+++ b/integration-cli/docker_cli_port_test.go
@@ -1,18 +1,18 @@
 package main
 
 import (
+	"context"
 	"fmt"
-	"net"
 	"regexp"
 	"sort"
 	"strconv"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestPortList(c *check.C) {
+func (s *DockerSuite) TestPortList(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	// one port
 	out, _ := dockerCmd(c, "run", "-d", "-p", "9876:80", "busybox", "top")
@@ -20,15 +20,15 @@ func (s *DockerSuite) TestPortList(c *check.C) {
 
 	out, _ = dockerCmd(c, "port", firstID, "80")
 
-	err := assertPortList(c, out, []string{"0.0.0.0:9876"})
+	err := assertPortList(c, out, []string{"0.0.0.0:9876", "[::]:9876"})
 	// Port list is not correct
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _ = dockerCmd(c, "port", firstID)
 
-	err = assertPortList(c, out, []string{"80/tcp -> 0.0.0.0:9876"})
+	err = assertPortList(c, out, []string{"80/tcp -> 0.0.0.0:9876", "80/tcp -> [::]:9876"})
 	// Port list is not correct
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "rm", "-f", firstID)
 
@@ -42,18 +42,22 @@ func (s *DockerSuite) TestPortList(c *check.C) {
 
 	out, _ = dockerCmd(c, "port", ID, "80")
 
-	err = assertPortList(c, out, []string{"0.0.0.0:9876"})
+	err = assertPortList(c, out, []string{"0.0.0.0:9876", "[::]:9876"})
 	// Port list is not correct
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _ = dockerCmd(c, "port", ID)
 
 	err = assertPortList(c, out, []string{
 		"80/tcp -> 0.0.0.0:9876",
+		"80/tcp -> [::]:9876",
 		"81/tcp -> 0.0.0.0:9877",
-		"82/tcp -> 0.0.0.0:9878"})
+		"81/tcp -> [::]:9877",
+		"82/tcp -> 0.0.0.0:9878",
+		"82/tcp -> [::]:9878",
+	})
 	// Port list is not correct
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "rm", "-f", ID)
 
@@ -68,43 +72,47 @@ func (s *DockerSuite) TestPortList(c *check.C) {
 
 	out, _ = dockerCmd(c, "port", ID, "80")
 
-	err = assertPortList(c, out, []string{"0.0.0.0:9876", "0.0.0.0:9999"})
+	err = assertPortList(c, out, []string{"0.0.0.0:9876", "[::]:9876", "0.0.0.0:9999", "[::]:9999"})
 	// Port list is not correct
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _ = dockerCmd(c, "port", ID)
 
 	err = assertPortList(c, out, []string{
 		"80/tcp -> 0.0.0.0:9876",
 		"80/tcp -> 0.0.0.0:9999",
+		"80/tcp -> [::]:9876",
+		"80/tcp -> [::]:9999",
 		"81/tcp -> 0.0.0.0:9877",
-		"82/tcp -> 0.0.0.0:9878"})
+		"81/tcp -> [::]:9877",
+		"82/tcp -> 0.0.0.0:9878",
+		"82/tcp -> [::]:9878",
+	})
 	// Port list is not correct
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	dockerCmd(c, "rm", "-f", ID)
 
 	testRange := func() {
 		// host port ranges used
 		IDs := make([]string, 3)
 		for i := 0; i < 3; i++ {
-			out, _ = dockerCmd(c, "run", "-d",
-				"-p", "9090-9092:80",
-				"busybox", "top")
+			out, _ = dockerCmd(c, "run", "-d", "-p", "9090-9092:80", "busybox", "top")
 			IDs[i] = strings.TrimSpace(out)
 
 			out, _ = dockerCmd(c, "port", IDs[i])
 
-			err = assertPortList(c, out, []string{fmt.Sprintf("80/tcp -> 0.0.0.0:%d", 9090+i)})
+			err = assertPortList(c, out, []string{
+				fmt.Sprintf("80/tcp -> 0.0.0.0:%d", 9090+i),
+				fmt.Sprintf("80/tcp -> [::]:%d", 9090+i),
+			})
 			// Port list is not correct
-			c.Assert(err, checker.IsNil)
+			assert.NilError(c, err)
 		}
 
 		// test port range exhaustion
-		out, _, err = dockerCmdWithError("run", "-d",
-			"-p", "9090-9092:80",
-			"busybox", "top")
+		out, _, err = dockerCmdWithError("run", "-d", "-p", "9090-9092:80", "busybox", "top")
 		// Exhausted port range did not return an error
-		c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+		assert.Assert(c, err != nil, "out: %s", out)
 
 		for i := 0; i < 3; i++ {
 			dockerCmd(c, "rm", "-f", IDs[i])
@@ -116,47 +124,46 @@ func (s *DockerSuite) TestPortList(c *check.C) {
 
 	// test invalid port ranges
 	for _, invalidRange := range []string{"9090-9089:80", "9090-:80", "-9090:80"} {
-		out, _, err = dockerCmdWithError("run", "-d",
-			"-p", invalidRange,
-			"busybox", "top")
+		out, _, err = dockerCmdWithError("run", "-d", "-p", invalidRange, "busybox", "top")
 		// Port range should have returned an error
-		c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+		assert.Assert(c, err != nil, "out: %s", out)
 	}
 
 	// test host range:container range spec.
-	out, _ = dockerCmd(c, "run", "-d",
-		"-p", "9800-9803:80-83",
-		"busybox", "top")
+	out, _ = dockerCmd(c, "run", "-d", "-p", "9800-9803:80-83", "busybox", "top")
 	ID = strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "port", ID)
 
 	err = assertPortList(c, out, []string{
 		"80/tcp -> 0.0.0.0:9800",
+		"80/tcp -> [::]:9800",
 		"81/tcp -> 0.0.0.0:9801",
+		"81/tcp -> [::]:9801",
 		"82/tcp -> 0.0.0.0:9802",
-		"83/tcp -> 0.0.0.0:9803"})
+		"82/tcp -> [::]:9802",
+		"83/tcp -> 0.0.0.0:9803",
+		"83/tcp -> [::]:9803",
+	})
 	// Port list is not correct
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	dockerCmd(c, "rm", "-f", ID)
 
 	// test mixing protocols in same port range
-	out, _ = dockerCmd(c, "run", "-d",
-		"-p", "8000-8080:80",
-		"-p", "8000-8080:80/udp",
-		"busybox", "top")
+	out, _ = dockerCmd(c, "run", "-d", "-p", "8000-8080:80", "-p", "8000-8080:80/udp", "busybox", "top")
 	ID = strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "port", ID)
 
 	// Running this test multiple times causes the TCP port to increment.
-	err = assertPortRange(c, out, []int{8000, 8080}, []int{8000, 8080})
+	err = assertPortRange(ID, []int{8000, 8080}, []int{8000, 8080})
 	// Port list is not correct
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	dockerCmd(c, "rm", "-f", ID)
 }
 
-func assertPortList(c *check.C, out string, expected []string) error {
+func assertPortList(c *testing.T, out string, expected []string) error {
+	c.Helper()
 	lines := strings.Split(strings.Trim(out, "\n "), "\n")
 	if len(lines) != len(expected) {
 		return fmt.Errorf("different size lists %s, %d, %d", out, len(lines), len(expected))
@@ -164,8 +171,20 @@ func assertPortList(c *check.C, out string, expected []string) error {
 	sort.Strings(lines)
 	sort.Strings(expected)
 
+	// "docker port" does not yet have a "--format" flag, and older versions
+	// of the CLI used an incorrect output format for mappings on IPv6 addresses
+	// for example, "80/tcp -> :::80" instead of "80/tcp -> [::]:80".
+	oldFormat := func(mapping string) string {
+		old := strings.Replace(mapping, "[", "", 1)
+		old = strings.Replace(old, "]:", ":", 1)
+		return old
+	}
+
 	for i := 0; i < len(expected); i++ {
-		if lines[i] != expected[i] {
+		if lines[i] == expected[i] {
+			continue
+		}
+		if lines[i] != oldFormat(expected[i]) {
 			return fmt.Errorf("|" + lines[i] + "!=" + expected[i] + "|")
 		}
 	}
@@ -173,43 +192,56 @@ func assertPortList(c *check.C, out string, expected []string) error {
 	return nil
 }
 
-func assertPortRange(c *check.C, out string, expectedTcp, expectedUdp []int) error {
-	lines := strings.Split(strings.Trim(out, "\n "), "\n")
+func assertPortRange(id string, expectedTCP, expectedUDP []int) error {
+	client := testEnv.APIClient()
+	inspect, err := client.ContainerInspect(context.TODO(), id)
+	if err != nil {
+		return err
+	}
 
-	var validTcp, validUdp bool
-	for _, l := range lines {
-		// 80/tcp -> 0.0.0.0:8015
-		port, err := strconv.Atoi(strings.Split(l, ":")[1])
-		if err != nil {
-			return err
+	var validTCP, validUDP bool
+	for portAndProto, binding := range inspect.NetworkSettings.Ports {
+		if portAndProto.Proto() == "tcp" && len(expectedTCP) == 0 {
+			continue
 		}
-		if strings.Contains(l, "tcp") && expectedTcp != nil {
-			if port < expectedTcp[0] || port > expectedTcp[1] {
-				return fmt.Errorf("tcp port (%d) not in range expected range %d-%d", port, expectedTcp[0], expectedTcp[1])
-			}
-			validTcp = true
+		if portAndProto.Proto() == "udp" && len(expectedTCP) == 0 {
+			continue
 		}
-		if strings.Contains(l, "udp") && expectedUdp != nil {
-			if port < expectedUdp[0] || port > expectedUdp[1] {
-				return fmt.Errorf("udp port (%d) not in range expected range %d-%d", port, expectedUdp[0], expectedUdp[1])
+
+		for _, b := range binding {
+			port, err := strconv.Atoi(b.HostPort)
+			if err != nil {
+				return err
+			}
+
+			if len(expectedTCP) > 0 {
+				if port < expectedTCP[0] || port > expectedTCP[1] {
+					return fmt.Errorf("tcp port (%d) not in range expected range %d-%d", port, expectedTCP[0], expectedTCP[1])
+				}
+				validTCP = true
+			}
+			if len(expectedUDP) > 0 {
+				if port < expectedUDP[0] || port > expectedUDP[1] {
+					return fmt.Errorf("udp port (%d) not in range expected range %d-%d", port, expectedUDP[0], expectedUDP[1])
+				}
+				validUDP = true
 			}
-			validUdp = true
 		}
 	}
-	if !validTcp {
+	if !validTCP {
 		return fmt.Errorf("tcp port not found")
 	}
-	if !validUdp {
+	if !validUDP {
 		return fmt.Errorf("udp port not found")
 	}
 	return nil
 }
 
-func stopRemoveContainer(id string, c *check.C) {
+func stopRemoveContainer(id string, c *testing.T) {
 	dockerCmd(c, "rm", "-f", id)
 }
 
-func (s *DockerSuite) TestUnpublishedPortsInPsOutput(c *check.C) {
+func (s *DockerSuite) TestUnpublishedPortsInPsOutput(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	// Run busybox with command line expose (equivalent to EXPOSE in image's Dockerfile) for the following ports
 	port1 := 80
@@ -223,10 +255,9 @@ func (s *DockerSuite) TestUnpublishedPortsInPsOutput(c *check.C) {
 	unpPort2 := fmt.Sprintf("%d/tcp", port2)
 	out, _ := dockerCmd(c, "ps", "-n=1")
 	// Missing unpublished ports in docker ps output
-	c.Assert(out, checker.Contains, unpPort1)
+	assert.Assert(c, strings.Contains(out, unpPort1))
 	// Missing unpublished ports in docker ps output
-	c.Assert(out, checker.Contains, unpPort2)
-
+	assert.Assert(c, strings.Contains(out, unpPort2))
 	// Run the container forcing to publish the exposed ports
 	dockerCmd(c, "run", "-d", "-P", expose1, expose2, "busybox", "sleep", "5")
 
@@ -235,9 +266,9 @@ func (s *DockerSuite) TestUnpublishedPortsInPsOutput(c *check.C) {
 	expBndRegx2 := regexp.MustCompile(`0.0.0.0:\d\d\d\d\d->` + unpPort2)
 	out, _ = dockerCmd(c, "ps", "-n=1")
 	// Cannot find expected port binding port (0.0.0.0:xxxxx->unpPort1) in docker ps output
-	c.Assert(expBndRegx1.MatchString(out), checker.Equals, true, check.Commentf("out: %s; unpPort1: %s", out, unpPort1))
+	assert.Equal(c, expBndRegx1.MatchString(out), true, fmt.Sprintf("out: %s; unpPort1: %s", out, unpPort1))
 	// Cannot find expected port binding port (0.0.0.0:xxxxx->unpPort2) in docker ps output
-	c.Assert(expBndRegx2.MatchString(out), checker.Equals, true, check.Commentf("out: %s; unpPort2: %s", out, unpPort2))
+	assert.Equal(c, expBndRegx2.MatchString(out), true, fmt.Sprintf("out: %s; unpPort2: %s", out, unpPort2))
 
 	// Run the container specifying explicit port bindings for the exposed ports
 	offset := 10000
@@ -251,10 +282,9 @@ func (s *DockerSuite) TestUnpublishedPortsInPsOutput(c *check.C) {
 	expBnd2 := fmt.Sprintf("0.0.0.0:%d->%s", offset+port2, unpPort2)
 	out, _ = dockerCmd(c, "ps", "-n=1")
 	// Cannot find expected port binding (expBnd1) in docker ps output
-	c.Assert(out, checker.Contains, expBnd1)
+	assert.Assert(c, strings.Contains(out, expBnd1))
 	// Cannot find expected port binding (expBnd2) in docker ps output
-	c.Assert(out, checker.Contains, expBnd2)
-
+	assert.Assert(c, strings.Contains(out, expBnd2))
 	// Remove container now otherwise it will interfere with next test
 	stopRemoveContainer(id, c)
 
@@ -265,9 +295,9 @@ func (s *DockerSuite) TestUnpublishedPortsInPsOutput(c *check.C) {
 	// Check docker ps o/p for last created container reports the specified port mappings
 	out, _ = dockerCmd(c, "ps", "-n=1")
 	// Cannot find expected port binding (expBnd1) in docker ps output
-	c.Assert(out, checker.Contains, expBnd1)
+	assert.Assert(c, strings.Contains(out, expBnd1))
 	// Cannot find expected port binding (expBnd2) in docker ps output
-	c.Assert(out, checker.Contains, expBnd2)
+	assert.Assert(c, strings.Contains(out, expBnd2))
 	// Remove container now otherwise it will interfere with next test
 	stopRemoveContainer(id, c)
 
@@ -277,75 +307,64 @@ func (s *DockerSuite) TestUnpublishedPortsInPsOutput(c *check.C) {
 	// Check docker ps o/p for last created container reports the specified unpublished port and port mapping
 	out, _ = dockerCmd(c, "ps", "-n=1")
 	// Missing unpublished exposed ports (unpPort1) in docker ps output
-	c.Assert(out, checker.Contains, unpPort1)
+	assert.Assert(c, strings.Contains(out, unpPort1))
 	// Missing port binding (expBnd2) in docker ps output
-	c.Assert(out, checker.Contains, expBnd2)
+	assert.Assert(c, strings.Contains(out, expBnd2))
 }
 
-func (s *DockerSuite) TestPortHostBinding(c *check.C) {
+func (s *DockerSuite) TestPortHostBinding(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
-	out, _ := dockerCmd(c, "run", "-d", "-p", "9876:80", "busybox",
-		"nc", "-l", "-p", "80")
+	out, _ := dockerCmd(c, "run", "-d", "-p", "9876:80", "busybox", "nc", "-l", "-p", "80")
 	firstID := strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "port", firstID, "80")
 
-	err := assertPortList(c, out, []string{"0.0.0.0:9876"})
+	err := assertPortList(c, out, []string{"0.0.0.0:9876", "[::]:9876"})
 	// Port list is not correct
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	dockerCmd(c, "run", "--net=host", "busybox",
-		"nc", "localhost", "9876")
+	dockerCmd(c, "run", "--net=host", "busybox", "nc", "localhost", "9876")
 
 	dockerCmd(c, "rm", "-f", firstID)
 
 	out, _, err = dockerCmdWithError("run", "--net=host", "busybox", "nc", "localhost", "9876")
 	// Port is still bound after the Container is removed
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+	assert.Assert(c, err != nil, "out: %s", out)
 }
 
-func (s *DockerSuite) TestPortExposeHostBinding(c *check.C) {
+func (s *DockerSuite) TestPortExposeHostBinding(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
-	out, _ := dockerCmd(c, "run", "-d", "-P", "--expose", "80", "busybox",
-		"nc", "-l", "-p", "80")
+	out, _ := dockerCmd(c, "run", "-d", "-P", "--expose", "80", "busybox", "nc", "-l", "-p", "80")
 	firstID := strings.TrimSpace(out)
 
-	out, _ = dockerCmd(c, "port", firstID, "80")
-
-	_, exposedPort, err := net.SplitHostPort(out)
-	c.Assert(err, checker.IsNil, check.Commentf("out: %s", out))
+	out, _ = dockerCmd(c, "inspect", "--format", `{{index .NetworkSettings.Ports "80/tcp" 0 "HostPort" }}`, firstID)
 
-	dockerCmd(c, "run", "--net=host", "busybox",
-		"nc", "localhost", strings.TrimSpace(exposedPort))
+	exposedPort := strings.TrimSpace(out)
+	dockerCmd(c, "run", "--net=host", "busybox", "nc", "127.0.0.1", exposedPort)
 
 	dockerCmd(c, "rm", "-f", firstID)
 
-	out, _, err = dockerCmdWithError("run", "--net=host", "busybox",
-		"nc", "localhost", strings.TrimSpace(exposedPort))
+	out, _, err := dockerCmdWithError("run", "--net=host", "busybox", "nc", "127.0.0.1", exposedPort)
 	// Port is still bound after the Container is removed
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+	assert.Assert(c, err != nil, "out: %s", out)
 }
 
-func (s *DockerSuite) TestPortBindingOnSandbox(c *check.C) {
+func (s *DockerSuite) TestPortBindingOnSandbox(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	dockerCmd(c, "network", "create", "--internal", "-d", "bridge", "internal-net")
 	nr := getNetworkResource(c, "internal-net")
-	c.Assert(nr.Internal, checker.Equals, true)
+	assert.Equal(c, nr.Internal, true)
 
 	dockerCmd(c, "run", "--net", "internal-net", "-d", "--name", "c1",
 		"-p", "8080:8080", "busybox", "nc", "-l", "-p", "8080")
-	c.Assert(waitRun("c1"), check.IsNil)
+	assert.Assert(c, waitRun("c1") == nil)
 
 	_, _, err := dockerCmdWithError("run", "--net=host", "busybox", "nc", "localhost", "8080")
-	c.Assert(err, check.NotNil,
-		check.Commentf("Port mapping on internal network is expected to fail"))
-
+	assert.Assert(c, err != nil, "Port mapping on internal network is expected to fail")
 	// Connect container to another normal bridge network
 	dockerCmd(c, "network", "create", "-d", "bridge", "foo-net")
 	dockerCmd(c, "network", "connect", "foo-net", "c1")
 
 	_, _, err = dockerCmdWithError("run", "--net=host", "busybox", "nc", "localhost", "8080")
-	c.Assert(err, check.IsNil,
-		check.Commentf("Port mapping on the new network is expected to succeed"))
-
+	assert.Assert(c, err == nil, "Port mapping on the new network is expected to succeed")
 }
diff --git a/integration-cli/docker_cli_proxy_test.go b/integration-cli/docker_cli_proxy_test.go
index 52159aa9c538e..a803328665081 100644
--- a/integration-cli/docker_cli_proxy_test.go
+++ b/integration-cli/docker_cli_proxy_test.go
@@ -3,14 +3,14 @@ package main
 import (
 	"net"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
-func (s *DockerSuite) TestCLIProxyDisableProxyUnixSock(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestCLIProxyDisableProxyUnixSock(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 
 	icmd.RunCmd(icmd.Cmd{
 		Command: []string{dockerBinary, "info"},
@@ -20,11 +20,10 @@ func (s *DockerSuite) TestCLIProxyDisableProxyUnixSock(c *check.C) {
 
 // Can't use localhost here since go has a special case to not use proxy if connecting to localhost
 // See https://golang.org/pkg/net/http/#ProxyFromEnvironment
-func (s *DockerDaemonSuite) TestCLIProxyProxyTCPSock(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerDaemonSuite) TestCLIProxyProxyTCPSock(c *testing.T) {
 	// get the IP to use to connect since we can't use localhost
 	addrs, err := net.InterfaceAddrs()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	var ip string
 	for _, addr := range addrs {
 		sAddr := addr.String()
@@ -35,7 +34,7 @@ func (s *DockerDaemonSuite) TestCLIProxyProxyTCPSock(c *check.C) {
 		}
 	}
 
-	c.Assert(ip, checker.Not(checker.Equals), "")
+	assert.Assert(c, ip != "")
 
 	s.d.Start(c, "-H", "tcp://"+ip+":2375")
 
diff --git a/integration-cli/docker_cli_prune_unix_test.go b/integration-cli/docker_cli_prune_unix_test.go
index d60420b591481..f3afe70a2093b 100644
--- a/integration-cli/docker_cli_prune_unix_test.go
+++ b/integration-cli/docker_cli_prune_unix_test.go
@@ -1,58 +1,61 @@
+//go:build !windows
 // +build !windows
 
 package main
 
 import (
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
 	"github.com/docker/docker/integration-cli/daemon"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/poll"
 )
 
-func pruneNetworkAndVerify(c *check.C, d *daemon.Daemon, kept, pruned []string) {
+func pruneNetworkAndVerify(c *testing.T, d *daemon.Daemon, kept, pruned []string) {
 	_, err := d.Cmd("network", "prune", "--force")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	for _, s := range kept {
-		waitAndAssert(c, defaultReconciliationTimeout, func(*check.C) (interface{}, check.CommentInterface) {
+		poll.WaitOn(c, pollCheck(c, func(*testing.T) (interface{}, string) {
 			out, err := d.Cmd("network", "ls", "--format", "{{.Name}}")
-			c.Assert(err, checker.IsNil)
-			return out, nil
-		}, checker.Contains, s)
+			assert.NilError(c, err)
+			return out, ""
+		}, checker.Contains(s)), poll.WithTimeout(defaultReconciliationTimeout))
+
 	}
 
 	for _, s := range pruned {
-		waitAndAssert(c, defaultReconciliationTimeout, func(*check.C) (interface{}, check.CommentInterface) {
+		poll.WaitOn(c, pollCheck(c, func(*testing.T) (interface{}, string) {
 			out, err := d.Cmd("network", "ls", "--format", "{{.Name}}")
-			c.Assert(err, checker.IsNil)
-			return out, nil
-		}, checker.Not(checker.Contains), s)
+			assert.NilError(c, err)
+			return out, ""
+		}, checker.Not(checker.Contains(s))), poll.WithTimeout(defaultReconciliationTimeout))
 	}
 }
 
-func (s *DockerSwarmSuite) TestPruneNetwork(c *check.C) {
+func (s *DockerSwarmSuite) TestPruneNetwork(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 	_, err := d.Cmd("network", "create", "n1") // used by container (testprune)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	_, err = d.Cmd("network", "create", "n2")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	_, err = d.Cmd("network", "create", "n3", "--driver", "overlay") // used by service (testprunesvc)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	_, err = d.Cmd("network", "create", "n4", "--driver", "overlay")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	cName := "testprune"
 	_, err = d.Cmd("run", "-d", "--name", cName, "--net", "n1", "busybox", "top")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	serviceName := "testprunesvc"
 	replicas := 1
@@ -61,24 +64,24 @@ func (s *DockerSwarmSuite) TestPruneNetwork(c *check.C) {
 		"--replicas", strconv.Itoa(replicas),
 		"--network", "n3",
 		"busybox", "top")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, replicas+1)
+	assert.NilError(c, err)
+	assert.Assert(c, strings.TrimSpace(out) != "")
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(replicas+1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// prune and verify
 	pruneNetworkAndVerify(c, d, []string{"n1", "n3"}, []string{"n2", "n4"})
 
 	// remove containers, then prune and verify again
 	_, err = d.Cmd("rm", "-f", cName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	_, err = d.Cmd("service", "rm", serviceName)
-	c.Assert(err, checker.IsNil)
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 0)
+	assert.NilError(c, err)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	pruneNetworkAndVerify(c, d, []string{}, []string{"n1", "n3"})
 }
 
-func (s *DockerDaemonSuite) TestPruneImageDangling(c *check.C) {
+func (s *DockerDaemonSuite) TestPruneImageDangling(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	result := cli.BuildCmd(c, "test", cli.Daemon(s.d),
@@ -90,27 +93,23 @@ func (s *DockerDaemonSuite) TestPruneImageDangling(c *check.C) {
 	id := strings.TrimSpace(result.Combined())
 
 	out, err := s.d.Cmd("images", "-q", "--no-trunc")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id))
 	out, err = s.d.Cmd("image", "prune", "--force")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id)
-
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id))
 	out, err = s.d.Cmd("images", "-q", "--no-trunc")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id))
 	out, err = s.d.Cmd("image", "prune", "--force", "--all")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id))
 	out, err = s.d.Cmd("images", "-q", "--no-trunc")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id)
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id))
 }
 
-func (s *DockerSuite) TestPruneContainerUntil(c *check.C) {
+func (s *DockerSuite) TestPruneContainerUntil(c *testing.T) {
 	out := cli.DockerCmd(c, "run", "-d", "busybox").Combined()
 	id1 := strings.TrimSpace(out)
 	cli.WaitExited(c, id1, 5*time.Second)
@@ -122,15 +121,14 @@ func (s *DockerSuite) TestPruneContainerUntil(c *check.C) {
 	cli.WaitExited(c, id2, 5*time.Second)
 
 	out = cli.DockerCmd(c, "container", "prune", "--force", "--filter", "until="+until).Combined()
-	c.Assert(strings.TrimSpace(out), checker.Contains, id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
 	out = cli.DockerCmd(c, "ps", "-a", "-q", "--no-trunc").Combined()
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id1)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id2)
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
 }
 
-func (s *DockerSuite) TestPruneContainerLabel(c *check.C) {
+func (s *DockerSuite) TestPruneContainerLabel(c *testing.T) {
 	out := cli.DockerCmd(c, "run", "-d", "--label", "foo", "busybox").Combined()
 	id1 := strings.TrimSpace(out)
 	cli.WaitExited(c, id1, 5*time.Second)
@@ -149,125 +147,111 @@ func (s *DockerSuite) TestPruneContainerLabel(c *check.C) {
 
 	// Add a config file of label=foobar, that will have no impact if cli is label!=foobar
 	config := `{"pruneFilters": ["label=foobar"]}`
-	d, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, checker.IsNil)
+	d, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(d)
-	err = ioutil.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
+	assert.NilError(c, err)
 
 	// With config.json only, prune based on label=foobar
 	out = cli.DockerCmd(c, "--config", d, "container", "prune", "--force").Combined()
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id3)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id4)
-
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id3))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id4))
 	out = cli.DockerCmd(c, "container", "prune", "--force", "--filter", "label=foo").Combined()
-	c.Assert(strings.TrimSpace(out), checker.Contains, id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id3)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id3))
 	out = cli.DockerCmd(c, "ps", "-a", "-q", "--no-trunc").Combined()
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id1)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id2)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id3)
-
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id3))
 	out = cli.DockerCmd(c, "container", "prune", "--force", "--filter", "label!=bar").Combined()
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id3)
-
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id3))
 	out = cli.DockerCmd(c, "ps", "-a", "-q", "--no-trunc").Combined()
-	c.Assert(strings.TrimSpace(out), checker.Contains, id2)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id3)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id3))
 	// With config.json label=foobar and CLI label!=foobar, CLI label!=foobar supersede
 	out = cli.DockerCmd(c, "--config", d, "container", "prune", "--force", "--filter", "label!=foobar").Combined()
-	c.Assert(strings.TrimSpace(out), checker.Contains, id2)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
 	out = cli.DockerCmd(c, "ps", "-a", "-q", "--no-trunc").Combined()
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
 }
 
-func (s *DockerSuite) TestPruneVolumeLabel(c *check.C) {
+func (s *DockerSuite) TestPruneVolumeLabel(c *testing.T) {
 	out, _ := dockerCmd(c, "volume", "create", "--label", "foo")
 	id1 := strings.TrimSpace(out)
-	c.Assert(id1, checker.Not(checker.Equals), "")
+	assert.Assert(c, id1 != "")
 
 	out, _ = dockerCmd(c, "volume", "create", "--label", "bar")
 	id2 := strings.TrimSpace(out)
-	c.Assert(id2, checker.Not(checker.Equals), "")
+	assert.Assert(c, id2 != "")
 
 	out, _ = dockerCmd(c, "volume", "create")
 	id3 := strings.TrimSpace(out)
-	c.Assert(id3, checker.Not(checker.Equals), "")
+	assert.Assert(c, id3 != "")
 
 	out, _ = dockerCmd(c, "volume", "create", "--label", "foobar")
 	id4 := strings.TrimSpace(out)
-	c.Assert(id4, checker.Not(checker.Equals), "")
+	assert.Assert(c, id4 != "")
 
 	// Add a config file of label=foobar, that will have no impact if cli is label!=foobar
 	config := `{"pruneFilters": ["label=foobar"]}`
-	d, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, checker.IsNil)
+	d, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(d)
-	err = ioutil.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
+	assert.NilError(c, err)
 
 	// With config.json only, prune based on label=foobar
 	out, _ = dockerCmd(c, "--config", d, "volume", "prune", "--force")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id3)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id4)
-
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id3))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id4))
 	out, _ = dockerCmd(c, "volume", "prune", "--force", "--filter", "label=foo")
-	c.Assert(strings.TrimSpace(out), checker.Contains, id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id3)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id3))
 	out, _ = dockerCmd(c, "volume", "ls", "--format", "{{.Name}}")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id1)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id2)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id3)
-
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id3))
 	out, _ = dockerCmd(c, "volume", "prune", "--force", "--filter", "label!=bar")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id3)
-
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id3))
 	out, _ = dockerCmd(c, "volume", "ls", "--format", "{{.Name}}")
-	c.Assert(strings.TrimSpace(out), checker.Contains, id2)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id3)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id3))
 	// With config.json label=foobar and CLI label!=foobar, CLI label!=foobar supersede
 	out, _ = dockerCmd(c, "--config", d, "volume", "prune", "--force", "--filter", "label!=foobar")
-	c.Assert(strings.TrimSpace(out), checker.Contains, id2)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
 	out, _ = dockerCmd(c, "volume", "ls", "--format", "{{.Name}}")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
 }
 
-func (s *DockerSuite) TestPruneNetworkLabel(c *check.C) {
+func (s *DockerSuite) TestPruneNetworkLabel(c *testing.T) {
 	dockerCmd(c, "network", "create", "--label", "foo", "n1")
 	dockerCmd(c, "network", "create", "--label", "bar", "n2")
 	dockerCmd(c, "network", "create", "n3")
 
 	out, _ := dockerCmd(c, "network", "prune", "--force", "--filter", "label=foo")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "n1")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), "n2")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), "n3")
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "n1"))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), "n2"))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), "n3"))
 	out, _ = dockerCmd(c, "network", "prune", "--force", "--filter", "label!=bar")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), "n1")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), "n2")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "n3")
-
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), "n1"))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), "n2"))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "n3"))
 	out, _ = dockerCmd(c, "network", "prune", "--force")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), "n1")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "n2")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), "n3")
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), "n1"))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "n2"))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), "n3"))
 }
 
-func (s *DockerDaemonSuite) TestPruneImageLabel(c *check.C) {
+func (s *DockerDaemonSuite) TestPruneImageLabel(c *testing.T) {
 	s.d.StartWithBusybox(c)
 
 	result := cli.BuildCmd(c, "test1", cli.Daemon(s.d),
@@ -278,9 +262,8 @@ func (s *DockerDaemonSuite) TestPruneImageLabel(c *check.C) {
 	result.Assert(c, icmd.Success)
 	id1 := strings.TrimSpace(result.Combined())
 	out, err := s.d.Cmd("images", "-q", "--no-trunc")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id1)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id1))
 	result = cli.BuildCmd(c, "test2", cli.Daemon(s.d),
 		build.WithDockerfile(`FROM busybox
                  LABEL bar=foo`),
@@ -289,21 +272,18 @@ func (s *DockerDaemonSuite) TestPruneImageLabel(c *check.C) {
 	result.Assert(c, icmd.Success)
 	id2 := strings.TrimSpace(result.Combined())
 	out, err = s.d.Cmd("images", "-q", "--no-trunc")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id2)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
 	out, err = s.d.Cmd("image", "prune", "--force", "--all", "--filter", "label=foo=bar")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
 	out, err = s.d.Cmd("image", "prune", "--force", "--all", "--filter", "label!=bar=foo")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id2)
-
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id2))
 	out, err = s.d.Cmd("image", "prune", "--force", "--all", "--filter", "label=bar=foo")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), id1)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id2)
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
 }
diff --git a/integration-cli/docker_cli_ps_test.go b/integration-cli/docker_cli_ps_test.go
index 824ca683ba5cc..fe625263bc593 100644
--- a/integration-cli/docker_cli_ps_test.go
+++ b/integration-cli/docker_cli_ps_test.go
@@ -5,18 +5,20 @@ import (
 	"sort"
 	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types/versions"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/skip"
 )
 
-func (s *DockerSuite) TestPsListContainersBase(c *check.C) {
+func (s *DockerSuite) TestPsListContainersBase(c *testing.T) {
 	existingContainers := ExistingContainerIDs(c)
 
 	out := runSleepingContainer(c, "-d")
@@ -33,89 +35,89 @@ func (s *DockerSuite) TestPsListContainersBase(c *check.C) {
 	fourthID := strings.TrimSpace(out)
 
 	// make sure the second is running
-	c.Assert(waitRun(secondID), checker.IsNil)
+	assert.Assert(c, waitRun(secondID) == nil)
 
 	// make sure third one is not running
 	dockerCmd(c, "wait", thirdID)
 
 	// make sure the forth is running
-	c.Assert(waitRun(fourthID), checker.IsNil)
+	assert.Assert(c, waitRun(fourthID) == nil)
 
 	// all
 	out, _ = dockerCmd(c, "ps", "-a")
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), []string{fourthID, thirdID, secondID, firstID}), checker.Equals, true, check.Commentf("ALL: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), []string{fourthID, thirdID, secondID, firstID}), true, fmt.Sprintf("ALL: Container list is not in the correct order: \n%s", out))
 
 	// running
 	out, _ = dockerCmd(c, "ps")
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), []string{fourthID, secondID, firstID}), checker.Equals, true, check.Commentf("RUNNING: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), []string{fourthID, secondID, firstID}), true, fmt.Sprintf("RUNNING: Container list is not in the correct order: \n%s", out))
 
 	// limit
 	out, _ = dockerCmd(c, "ps", "-n=2", "-a")
 	expected := []string{fourthID, thirdID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("LIMIT & ALL: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("LIMIT & ALL: Container list is not in the correct order: \n%s", out))
 
 	out, _ = dockerCmd(c, "ps", "-n=2")
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("LIMIT: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("LIMIT: Container list is not in the correct order: \n%s", out))
 
 	// filter since
 	out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-a")
 	expected = []string{fourthID, thirdID, secondID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("SINCE filter & ALL: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter & ALL: Container list is not in the correct order: \n%s", out))
 
 	out, _ = dockerCmd(c, "ps", "-f", "since="+firstID)
 	expected = []string{fourthID, secondID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("SINCE filter: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter: Container list is not in the correct order: \n%s", out))
 
 	out, _ = dockerCmd(c, "ps", "-f", "since="+thirdID)
 	expected = []string{fourthID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("SINCE filter: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter: Container list is not in the correct order: \n%s", out))
 
 	// filter before
 	out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID, "-a")
 	expected = []string{thirdID, secondID, firstID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("BEFORE filter & ALL: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("BEFORE filter & ALL: Container list is not in the correct order: \n%s", out))
 
 	out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID)
 	expected = []string{secondID, firstID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("BEFORE filter: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("BEFORE filter: Container list is not in the correct order: \n%s", out))
 
 	out, _ = dockerCmd(c, "ps", "-f", "before="+thirdID)
 	expected = []string{secondID, firstID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("SINCE filter: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter: Container list is not in the correct order: \n%s", out))
 
 	// filter since & before
 	out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID, "-a")
 	expected = []string{thirdID, secondID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("SINCE filter, BEFORE filter & ALL: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, BEFORE filter & ALL: Container list is not in the correct order: \n%s", out))
 
 	out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID)
 	expected = []string{secondID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("SINCE filter, BEFORE filter: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, BEFORE filter: Container list is not in the correct order: \n%s", out))
 
 	// filter since & limit
 	out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-n=2", "-a")
 	expected = []string{fourthID, thirdID}
 
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("SINCE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
 
 	out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-n=2")
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("SINCE filter, LIMIT: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, LIMIT: Container list is not in the correct order: \n%s", out))
 
 	// filter before & limit
 	out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID, "-n=1", "-a")
 	expected = []string{thirdID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("BEFORE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("BEFORE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
 
 	out, _ = dockerCmd(c, "ps", "-f", "before="+fourthID, "-n=1")
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("BEFORE filter, LIMIT: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("BEFORE filter, LIMIT: Container list is not in the correct order: \n%s", out))
 
 	// filter since & filter before & limit
 	out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID, "-n=1", "-a")
 	expected = []string{thirdID}
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("SINCE filter, BEFORE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, BEFORE filter, LIMIT & ALL: Container list is not in the correct order: \n%s", out))
 
 	out, _ = dockerCmd(c, "ps", "-f", "since="+firstID, "-f", "before="+fourthID, "-n=1")
-	c.Assert(assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), checker.Equals, true, check.Commentf("SINCE filter, BEFORE filter, LIMIT: Container list is not in the correct order: \n%s", out))
+	assert.Equal(c, assertContainerList(RemoveOutputForExistingElements(out, existingContainers), expected), true, fmt.Sprintf("SINCE filter, BEFORE filter, LIMIT: Container list is not in the correct order: \n%s", out))
 
 }
 
@@ -137,7 +139,7 @@ func assertContainerList(out string, expected []string) bool {
 	return true
 }
 
-func (s *DockerSuite) TestPsListContainersSize(c *check.C) {
+func (s *DockerSuite) TestPsListContainersSize(c *testing.T) {
 	// Problematic on Windows as it doesn't report the size correctly @swernli
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "busybox")
@@ -147,7 +149,7 @@ func (s *DockerSuite) TestPsListContainersSize(c *check.C) {
 	baseSizeIndex := strings.Index(baseLines[0], "SIZE")
 	baseFoundsize := baseLines[1][baseSizeIndex:]
 	baseBytes, err := strconv.Atoi(strings.Split(baseFoundsize, "B")[0])
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	name := "test_size"
 	dockerCmd(c, "run", "--name", name, "busybox", "sh", "-c", "echo 1 > test")
@@ -167,17 +169,17 @@ func (s *DockerSuite) TestPsListContainersSize(c *check.C) {
 	}
 	result.Assert(c, icmd.Success)
 	lines := strings.Split(strings.Trim(result.Combined(), "\n "), "\n")
-	c.Assert(lines, checker.HasLen, 2, check.Commentf("Expected 2 lines for 'ps -s -n=1' output, got %d", len(lines)))
+	assert.Equal(c, len(lines), 2, "Expected 2 lines for 'ps -s -n=1' output, got %d", len(lines))
 	sizeIndex := strings.Index(lines[0], "SIZE")
 	idIndex := strings.Index(lines[0], "CONTAINER ID")
 	foundID := lines[1][idIndex : idIndex+12]
-	c.Assert(foundID, checker.Equals, id[:12], check.Commentf("Expected id %s, got %s", id[:12], foundID))
+	assert.Equal(c, foundID, id[:12], fmt.Sprintf("Expected id %s, got %s", id[:12], foundID))
 	expectedSize := fmt.Sprintf("%dB", 2+baseBytes)
 	foundSize := lines[1][sizeIndex:]
-	c.Assert(foundSize, checker.Contains, expectedSize, check.Commentf("Expected size %q, got %q", expectedSize, foundSize))
+	assert.Assert(c, strings.Contains(foundSize, expectedSize), "Expected size %q, got %q", expectedSize, foundSize)
 }
 
-func (s *DockerSuite) TestPsListContainersFilterStatus(c *check.C) {
+func (s *DockerSuite) TestPsListContainersFilterStatus(c *testing.T) {
 	existingContainers := ExistingContainerIDs(c)
 
 	// start exited container
@@ -194,11 +196,11 @@ func (s *DockerSuite) TestPsListContainersFilterStatus(c *check.C) {
 	// filter containers by exited
 	out = cli.DockerCmd(c, "ps", "--no-trunc", "-q", "--filter=status=exited").Combined()
 	containerOut := strings.TrimSpace(out)
-	c.Assert(RemoveOutputForExistingElements(containerOut, existingContainers), checker.Equals, firstID)
+	assert.Equal(c, RemoveOutputForExistingElements(containerOut, existingContainers), firstID)
 
 	out = cli.DockerCmd(c, "ps", "-a", "--no-trunc", "-q", "--filter=status=running").Combined()
 	containerOut = strings.TrimSpace(out)
-	c.Assert(RemoveOutputForExistingElements(containerOut, existingContainers), checker.Equals, secondID)
+	assert.Equal(c, RemoveOutputForExistingElements(containerOut, existingContainers), secondID)
 
 	result := cli.Docker(cli.Args("ps", "-a", "-q", "--filter=status=rubbish"), cli.WithTimeout(time.Second*60))
 	err := "Invalid filter 'status=rubbish'"
@@ -220,11 +222,12 @@ func (s *DockerSuite) TestPsListContainersFilterStatus(c *check.C) {
 
 		out = cli.DockerCmd(c, "ps", "--no-trunc", "-q", "--filter=status=paused").Combined()
 		containerOut = strings.TrimSpace(out)
-		c.Assert(RemoveOutputForExistingElements(containerOut, existingContainers), checker.Equals, pausedID)
+		assert.Equal(c, RemoveOutputForExistingElements(containerOut, existingContainers), pausedID)
 	}
 }
 
-func (s *DockerSuite) TestPsListContainersFilterHealth(c *check.C) {
+func (s *DockerSuite) TestPsListContainersFilterHealth(c *testing.T) {
+	skip.If(c, RuntimeIsWindowsContainerd(), "FIXME. Hang on Windows + containerd combination")
 	existingContainers := ExistingContainerIDs(c)
 	// Test legacy no health check
 	out := runSleepingContainer(c, "--name=none_legacy")
@@ -234,7 +237,7 @@ func (s *DockerSuite) TestPsListContainersFilterHealth(c *check.C) {
 
 	out = cli.DockerCmd(c, "ps", "-q", "-l", "--no-trunc", "--filter=health=none").Combined()
 	containerOut := strings.TrimSpace(out)
-	c.Assert(containerOut, checker.Equals, containerID, check.Commentf("Expected id %s, got %s for legacy none filter, output: %q", containerID, containerOut, out))
+	assert.Equal(c, containerOut, containerID, fmt.Sprintf("Expected id %s, got %s for legacy none filter, output: %q", containerID, containerOut, out))
 
 	// Test no health check specified explicitly
 	out = runSleepingContainer(c, "--name=none", "--no-healthcheck")
@@ -244,7 +247,7 @@ func (s *DockerSuite) TestPsListContainersFilterHealth(c *check.C) {
 
 	out = cli.DockerCmd(c, "ps", "-q", "-l", "--no-trunc", "--filter=health=none").Combined()
 	containerOut = strings.TrimSpace(out)
-	c.Assert(containerOut, checker.Equals, containerID, check.Commentf("Expected id %s, got %s for none filter, output: %q", containerID, containerOut, out))
+	assert.Equal(c, containerOut, containerID, fmt.Sprintf("Expected id %s, got %s for none filter, output: %q", containerID, containerOut, out))
 
 	// Test failing health check
 	out = runSleepingContainer(c, "--name=failing_container", "--health-cmd=exit 1", "--health-interval=1s")
@@ -254,7 +257,7 @@ func (s *DockerSuite) TestPsListContainersFilterHealth(c *check.C) {
 
 	out = cli.DockerCmd(c, "ps", "-q", "--no-trunc", "--filter=health=unhealthy").Combined()
 	containerOut = strings.TrimSpace(out)
-	c.Assert(containerOut, checker.Equals, containerID, check.Commentf("Expected containerID %s, got %s for unhealthy filter, output: %q", containerID, containerOut, out))
+	assert.Equal(c, containerOut, containerID, fmt.Sprintf("Expected containerID %s, got %s for unhealthy filter, output: %q", containerID, containerOut, out))
 
 	// Check passing healthcheck
 	out = runSleepingContainer(c, "--name=passing_container", "--health-cmd=exit 0", "--health-interval=1s")
@@ -264,10 +267,10 @@ func (s *DockerSuite) TestPsListContainersFilterHealth(c *check.C) {
 
 	out = cli.DockerCmd(c, "ps", "-q", "--no-trunc", "--filter=health=healthy").Combined()
 	containerOut = strings.TrimSpace(RemoveOutputForExistingElements(out, existingContainers))
-	c.Assert(containerOut, checker.Equals, containerID, check.Commentf("Expected containerID %s, got %s for healthy filter, output: %q", containerID, containerOut, out))
+	assert.Equal(c, containerOut, containerID, fmt.Sprintf("Expected containerID %s, got %s for healthy filter, output: %q", containerID, containerOut, out))
 }
 
-func (s *DockerSuite) TestPsListContainersFilterID(c *check.C) {
+func (s *DockerSuite) TestPsListContainersFilterID(c *testing.T) {
 	// start container
 	out, _ := dockerCmd(c, "run", "-d", "busybox")
 	firstID := strings.TrimSpace(out)
@@ -278,10 +281,10 @@ func (s *DockerSuite) TestPsListContainersFilterID(c *check.C) {
 	// filter containers by id
 	out, _ = dockerCmd(c, "ps", "-a", "-q", "--filter=id="+firstID)
 	containerOut := strings.TrimSpace(out)
-	c.Assert(containerOut, checker.Equals, firstID[:12], check.Commentf("Expected id %s, got %s for exited filter, output: %q", firstID[:12], containerOut, out))
+	assert.Equal(c, containerOut, firstID[:12], fmt.Sprintf("Expected id %s, got %s for exited filter, output: %q", firstID[:12], containerOut, out))
 }
 
-func (s *DockerSuite) TestPsListContainersFilterName(c *check.C) {
+func (s *DockerSuite) TestPsListContainersFilterName(c *testing.T) {
 	// start container
 	dockerCmd(c, "run", "--name=a_name_to_match", "busybox")
 	id := getIDByName(c, "a_name_to_match")
@@ -292,7 +295,7 @@ func (s *DockerSuite) TestPsListContainersFilterName(c *check.C) {
 	// filter containers by name
 	out, _ := dockerCmd(c, "ps", "-a", "-q", "--filter=name=a_name_to_match")
 	containerOut := strings.TrimSpace(out)
-	c.Assert(containerOut, checker.Equals, id[:12], check.Commentf("Expected id %s, got %s for exited filter, output: %q", id[:12], containerOut, out))
+	assert.Equal(c, containerOut, id[:12], fmt.Sprintf("Expected id %s, got %s for exited filter, output: %q", id[:12], containerOut, out))
 }
 
 // Test for the ancestor filter for ps.
@@ -303,7 +306,7 @@ func (s *DockerSuite) TestPsListContainersFilterName(c *check.C) {
 // - Create an image based on the previous image (images_ps_filter_test2)
 // - Run containers for each of those image (busybox, images_ps_filter_test1, images_ps_filter_test2)
 // - Filter them out :P
-func (s *DockerSuite) TestPsListContainersFilterAncestorImage(c *check.C) {
+func (s *DockerSuite) TestPsListContainersFilterAncestorImage(c *testing.T) {
 	existingContainers := ExistingContainerIDs(c)
 
 	// Build images
@@ -376,7 +379,7 @@ func (s *DockerSuite) TestPsListContainersFilterAncestorImage(c *check.C) {
 	checkPsAncestorFilterOutput(c, RemoveOutputForExistingElements(out, existingContainers), imageName2+","+imageName1Tagged, []string{fourthID, fifthID})
 }
 
-func checkPsAncestorFilterOutput(c *check.C, out string, filterName string, expectedIDs []string) {
+func checkPsAncestorFilterOutput(c *testing.T, out string, filterName string, expectedIDs []string) {
 	var actualIDs []string
 	if out != "" {
 		actualIDs = strings.Split(out[:len(out)-1], "\n")
@@ -384,7 +387,7 @@ func checkPsAncestorFilterOutput(c *check.C, out string, filterName string, expe
 	sort.Strings(actualIDs)
 	sort.Strings(expectedIDs)
 
-	c.Assert(actualIDs, checker.HasLen, len(expectedIDs), check.Commentf("Expected filtered container(s) for %s ancestor filter to be %v:%v, got %v:%v", filterName, len(expectedIDs), expectedIDs, len(actualIDs), actualIDs))
+	assert.Equal(c, len(actualIDs), len(expectedIDs), fmt.Sprintf("Expected filtered container(s) for %s ancestor filter to be %v:%v, got %v:%v", filterName, len(expectedIDs), expectedIDs, len(actualIDs), actualIDs))
 	if len(expectedIDs) > 0 {
 		same := true
 		for i := range expectedIDs {
@@ -394,11 +397,11 @@ func checkPsAncestorFilterOutput(c *check.C, out string, filterName string, expe
 				break
 			}
 		}
-		c.Assert(same, checker.Equals, true, check.Commentf("Expected filtered container(s) for %s ancestor filter to be %v, got %v", filterName, expectedIDs, actualIDs))
+		assert.Equal(c, same, true, fmt.Sprintf("Expected filtered container(s) for %s ancestor filter to be %v, got %v", filterName, expectedIDs, actualIDs))
 	}
 }
 
-func (s *DockerSuite) TestPsListContainersFilterLabel(c *check.C) {
+func (s *DockerSuite) TestPsListContainersFilterLabel(c *testing.T) {
 	// start container
 	dockerCmd(c, "run", "--name=first", "-l", "match=me", "-l", "second=tag", "busybox")
 	firstID := getIDByName(c, "first")
@@ -414,60 +417,58 @@ func (s *DockerSuite) TestPsListContainersFilterLabel(c *check.C) {
 	// filter containers by exact match
 	out, _ := dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=label=match=me")
 	containerOut := strings.TrimSpace(out)
-	c.Assert(containerOut, checker.Equals, firstID, check.Commentf("Expected id %s, got %s for exited filter, output: %q", firstID, containerOut, out))
+	assert.Equal(c, containerOut, firstID, fmt.Sprintf("Expected id %s, got %s for exited filter, output: %q", firstID, containerOut, out))
 
 	// filter containers by two labels
 	out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=label=match=me", "--filter=label=second=tag")
 	containerOut = strings.TrimSpace(out)
-	c.Assert(containerOut, checker.Equals, firstID, check.Commentf("Expected id %s, got %s for exited filter, output: %q", firstID, containerOut, out))
+	assert.Equal(c, containerOut, firstID, fmt.Sprintf("Expected id %s, got %s for exited filter, output: %q", firstID, containerOut, out))
 
 	// filter containers by two labels, but expect not found because of AND behavior
 	out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=label=match=me", "--filter=label=second=tag-no")
 	containerOut = strings.TrimSpace(out)
-	c.Assert(containerOut, checker.Equals, "", check.Commentf("Expected nothing, got %s for exited filter, output: %q", containerOut, out))
+	assert.Equal(c, containerOut, "", fmt.Sprintf("Expected nothing, got %s for exited filter, output: %q", containerOut, out))
 
 	// filter containers by exact key
 	out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=label=match")
 	containerOut = strings.TrimSpace(out)
-	c.Assert(containerOut, checker.Contains, firstID)
-	c.Assert(containerOut, checker.Contains, secondID)
-	c.Assert(containerOut, checker.Not(checker.Contains), thirdID)
+	assert.Assert(c, strings.Contains(containerOut, firstID))
+	assert.Assert(c, strings.Contains(containerOut, secondID))
+	assert.Assert(c, !strings.Contains(containerOut, thirdID))
 }
 
-func (s *DockerSuite) TestPsListContainersFilterExited(c *check.C) {
+func (s *DockerSuite) TestPsListContainersFilterExited(c *testing.T) {
+	// TODO Flaky on  Windows CI [both RS1 and RS5]
+	// On slower machines the container may not have exited
+	// yet when we filter below by exit status/exit value.
+	skip.If(c, DaemonIsWindows(), "FLAKY on Windows, see #20819")
 	runSleepingContainer(c, "--name=sleep")
 
-	dockerCmd(c, "run", "--name", "zero1", "busybox", "true")
-	firstZero := getIDByName(c, "zero1")
-
-	dockerCmd(c, "run", "--name", "zero2", "busybox", "true")
-	secondZero := getIDByName(c, "zero2")
+	firstZero, _ := dockerCmd(c, "run", "-d", "busybox", "true")
+	secondZero, _ := dockerCmd(c, "run", "-d", "busybox", "true")
 
 	out, _, err := dockerCmdWithError("run", "--name", "nonzero1", "busybox", "false")
-	c.Assert(err, checker.NotNil, check.Commentf("Should fail.", out, err))
-
+	assert.Assert(c, err != nil, "Should fail. out: %s", out)
 	firstNonZero := getIDByName(c, "nonzero1")
 
 	out, _, err = dockerCmdWithError("run", "--name", "nonzero2", "busybox", "false")
-	c.Assert(err, checker.NotNil, check.Commentf("Should fail.", out, err))
+	assert.Assert(c, err != nil, "Should fail. out: %s", out)
 	secondNonZero := getIDByName(c, "nonzero2")
 
 	// filter containers by exited=0
 	out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=exited=0")
-	ids := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(ids, checker.HasLen, 2, check.Commentf("Should be 2 zero exited containers got %d: %s", len(ids), out))
-	c.Assert(ids[0], checker.Equals, secondZero, check.Commentf("First in list should be %q, got %q", secondZero, ids[0]))
-	c.Assert(ids[1], checker.Equals, firstZero, check.Commentf("Second in list should be %q, got %q", firstZero, ids[1]))
-
+	assert.Assert(c, strings.Contains(out, strings.TrimSpace(firstZero)))
+	assert.Assert(c, strings.Contains(out, strings.TrimSpace(secondZero)))
+	assert.Assert(c, !strings.Contains(out, strings.TrimSpace(firstNonZero)))
+	assert.Assert(c, !strings.Contains(out, strings.TrimSpace(secondNonZero)))
 	out, _ = dockerCmd(c, "ps", "-a", "-q", "--no-trunc", "--filter=exited=1")
-	ids = strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(ids, checker.HasLen, 2, check.Commentf("Should be 2 zero exited containers got %d", len(ids)))
-	c.Assert(ids[0], checker.Equals, secondNonZero, check.Commentf("First in list should be %q, got %q", secondNonZero, ids[0]))
-	c.Assert(ids[1], checker.Equals, firstNonZero, check.Commentf("Second in list should be %q, got %q", firstNonZero, ids[1]))
-
+	assert.Assert(c, strings.Contains(out, strings.TrimSpace(firstNonZero)))
+	assert.Assert(c, strings.Contains(out, strings.TrimSpace(secondNonZero)))
+	assert.Assert(c, !strings.Contains(out, strings.TrimSpace(firstZero)))
+	assert.Assert(c, !strings.Contains(out, strings.TrimSpace(secondZero)))
 }
 
-func (s *DockerSuite) TestPsRightTagName(c *check.C) {
+func (s *DockerSuite) TestPsRightTagName(c *testing.T) {
 	// TODO Investigate further why this fails on Windows to Windows CI
 	testRequires(c, DaemonIsLinux)
 
@@ -478,42 +479,42 @@ func (s *DockerSuite) TestPsRightTagName(c *check.C) {
 
 	var id1 string
 	out := runSleepingContainer(c)
-	id1 = strings.TrimSpace(string(out))
+	id1 = strings.TrimSpace(out)
 
 	var id2 string
 	out = runSleepingContainerInImage(c, tag)
-	id2 = strings.TrimSpace(string(out))
+	id2 = strings.TrimSpace(out)
 
 	var imageID string
 	out = inspectField(c, "busybox", "Id")
-	imageID = strings.TrimSpace(string(out))
+	imageID = strings.TrimSpace(out)
 
 	var id3 string
 	out = runSleepingContainerInImage(c, imageID)
-	id3 = strings.TrimSpace(string(out))
+	id3 = strings.TrimSpace(out)
 
 	out, _ = dockerCmd(c, "ps", "--no-trunc")
-	lines := strings.Split(strings.TrimSpace(string(out)), "\n")
+	lines := strings.Split(strings.TrimSpace(out), "\n")
 	lines = RemoveLinesForExistingElements(lines, existingContainers)
 	// skip header
 	lines = lines[1:]
-	c.Assert(lines, checker.HasLen, 3, check.Commentf("There should be 3 running container, got %d", len(lines)))
+	assert.Equal(c, len(lines), 3, "There should be 3 running container, got %d", len(lines))
 	for _, line := range lines {
 		f := strings.Fields(line)
 		switch f[0] {
 		case id1:
-			c.Assert(f[1], checker.Equals, "busybox", check.Commentf("Expected %s tag for id %s, got %s", "busybox", id1, f[1]))
+			assert.Equal(c, f[1], "busybox", fmt.Sprintf("Expected %s tag for id %s, got %s", "busybox", id1, f[1]))
 		case id2:
-			c.Assert(f[1], checker.Equals, tag, check.Commentf("Expected %s tag for id %s, got %s", tag, id2, f[1]))
+			assert.Equal(c, f[1], tag, fmt.Sprintf("Expected %s tag for id %s, got %s", tag, id2, f[1]))
 		case id3:
-			c.Assert(f[1], checker.Equals, imageID, check.Commentf("Expected %s imageID for id %s, got %s", tag, id3, f[1]))
+			assert.Equal(c, f[1], imageID, fmt.Sprintf("Expected %s imageID for id %s, got %s", tag, id3, f[1]))
 		default:
 			c.Fatalf("Unexpected id %s, expected %s and %s and %s", f[0], id1, id2, id3)
 		}
 	}
 }
 
-func (s *DockerSuite) TestPsListContainersFilterCreated(c *check.C) {
+func (s *DockerSuite) TestPsListContainersFilterCreated(c *testing.T) {
 	// create a container
 	out, _ := dockerCmd(c, "create", "busybox")
 	cID := strings.TrimSpace(out)
@@ -521,8 +522,7 @@ func (s *DockerSuite) TestPsListContainersFilterCreated(c *check.C) {
 
 	// Make sure it DOESN'T show up w/o a '-a' for normal 'ps'
 	out, _ = dockerCmd(c, "ps", "-q")
-	c.Assert(out, checker.Not(checker.Contains), shortCID, check.Commentf("Should have not seen '%s' in ps output:\n%s", shortCID, out))
-
+	assert.Assert(c, !strings.Contains(out, shortCID), "Should have not seen '%s' in ps output:\n%s", shortCID, out)
 	// Make sure it DOES show up as 'Created' for 'ps -a'
 	out, _ = dockerCmd(c, "ps", "-a")
 
@@ -532,19 +532,19 @@ func (s *DockerSuite) TestPsListContainersFilterCreated(c *check.C) {
 			continue
 		}
 		hits++
-		c.Assert(line, checker.Contains, "Created", check.Commentf("Missing 'Created' on '%s'", line))
+		assert.Assert(c, strings.Contains(line, "Created"), "Missing 'Created' on '%s'", line)
 	}
 
-	c.Assert(hits, checker.Equals, 1, check.Commentf("Should have seen '%s' in ps -a output once:%d\n%s", shortCID, hits, out))
+	assert.Equal(c, hits, 1, fmt.Sprintf("Should have seen '%s' in ps -a output once:%d\n%s", shortCID, hits, out))
 
 	// filter containers by 'create' - note, no -a needed
 	out, _ = dockerCmd(c, "ps", "-q", "-f", "status=created")
 	containerOut := strings.TrimSpace(out)
-	c.Assert(cID, checker.HasPrefix, containerOut)
+	assert.Assert(c, strings.HasPrefix(cID, containerOut))
 }
 
 // Test for GitHub issue #12595
-func (s *DockerSuite) TestPsImageIDAfterUpdate(c *check.C) {
+func (s *DockerSuite) TestPsImageIDAfterUpdate(c *testing.T) {
 	// TODO: Investigate why this fails on Windows to Windows CI further.
 	testRequires(c, DaemonIsLinux)
 	originalImageName := "busybox:TestPsImageIDAfterUpdate-original"
@@ -563,15 +563,15 @@ func (s *DockerSuite) TestPsImageIDAfterUpdate(c *check.C) {
 	result = icmd.RunCommand(dockerBinary, "ps", "--no-trunc")
 	result.Assert(c, icmd.Success)
 
-	lines := strings.Split(strings.TrimSpace(string(result.Combined())), "\n")
+	lines := strings.Split(strings.TrimSpace(result.Combined()), "\n")
 	lines = RemoveLinesForExistingElements(lines, existingContainers)
 	// skip header
 	lines = lines[1:]
-	c.Assert(len(lines), checker.Equals, 1)
+	assert.Equal(c, len(lines), 1)
 
 	for _, line := range lines {
 		f := strings.Fields(line)
-		c.Assert(f[1], checker.Equals, originalImageName)
+		assert.Equal(c, f[1], originalImageName)
 	}
 
 	icmd.RunCommand(dockerBinary, "commit", containerID, updatedImageName).Assert(c, icmd.Success)
@@ -580,38 +580,34 @@ func (s *DockerSuite) TestPsImageIDAfterUpdate(c *check.C) {
 	result = icmd.RunCommand(dockerBinary, "ps", "--no-trunc")
 	result.Assert(c, icmd.Success)
 
-	lines = strings.Split(strings.TrimSpace(string(result.Combined())), "\n")
+	lines = strings.Split(strings.TrimSpace(result.Combined()), "\n")
 	lines = RemoveLinesForExistingElements(lines, existingContainers)
 	// skip header
 	lines = lines[1:]
-	c.Assert(len(lines), checker.Equals, 1)
+	assert.Equal(c, len(lines), 1)
 
 	for _, line := range lines {
 		f := strings.Fields(line)
-		c.Assert(f[1], checker.Equals, originalImageID)
+		assert.Equal(c, f[1], originalImageID)
 	}
 
 }
 
-func (s *DockerSuite) TestPsNotShowPortsOfStoppedContainer(c *check.C) {
+func (s *DockerSuite) TestPsNotShowPortsOfStoppedContainer(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
-	dockerCmd(c, "run", "--name=foo", "-d", "-p", "5000:5000", "busybox", "top")
-	c.Assert(waitRun("foo"), checker.IsNil)
-	out, _ := dockerCmd(c, "ps")
-	lines := strings.Split(strings.TrimSpace(string(out)), "\n")
-	expected := "0.0.0.0:5000->5000/tcp"
-	fields := strings.Fields(lines[1])
-	c.Assert(fields[len(fields)-2], checker.Equals, expected, check.Commentf("Expected: %v, got: %v", expected, fields[len(fields)-2]))
+	dockerCmd(c, "run", "--name=foo", "-d", "-p", "6000:5000", "busybox", "top")
+	assert.Assert(c, waitRun("foo") == nil)
+	ports, _ := dockerCmd(c, "ps", "--format", "{{ .Ports }}", "--filter", "name=foo")
+	expected := ":6000->5000/tcp"
+	assert.Assert(c, is.Contains(ports, expected), "Expected: %v, got: %v", expected, ports)
 
 	dockerCmd(c, "kill", "foo")
 	dockerCmd(c, "wait", "foo")
-	out, _ = dockerCmd(c, "ps", "-l")
-	lines = strings.Split(strings.TrimSpace(string(out)), "\n")
-	fields = strings.Fields(lines[1])
-	c.Assert(fields[len(fields)-2], checker.Not(checker.Equals), expected, check.Commentf("Should not got %v", expected))
+	ports, _ = dockerCmd(c, "ps", "--format", "{{ .Ports }}", "--filter", "name=foo")
+	assert.Equal(c, ports, "", "Should not got %v", expected)
 }
 
-func (s *DockerSuite) TestPsShowMounts(c *check.C) {
+func (s *DockerSuite) TestPsShowMounts(c *testing.T) {
 	existingContainers := ExistingContainerNames(c)
 
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
@@ -621,9 +617,9 @@ func (s *DockerSuite) TestPsShowMounts(c *check.C) {
 	dockerCmd(c, "volume", "create", "ps-volume-test")
 	// volume mount containers
 	runSleepingContainer(c, "--name=volume-test-1", "--volume", "ps-volume-test:"+mp)
-	c.Assert(waitRun("volume-test-1"), checker.IsNil)
+	assert.Assert(c, waitRun("volume-test-1") == nil)
 	runSleepingContainer(c, "--name=volume-test-2", "--volume", mp)
-	c.Assert(waitRun("volume-test-2"), checker.IsNil)
+	assert.Assert(c, waitRun("volume-test-2") == nil)
 	// bind mount container
 	var bindMountSource string
 	var bindMountDestination string
@@ -635,83 +631,83 @@ func (s *DockerSuite) TestPsShowMounts(c *check.C) {
 		bindMountDestination = "/t"
 	}
 	runSleepingContainer(c, "--name=bind-mount-test", "-v", bindMountSource+":"+bindMountDestination)
-	c.Assert(waitRun("bind-mount-test"), checker.IsNil)
+	assert.Assert(c, waitRun("bind-mount-test") == nil)
 
 	out, _ := dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}")
 
-	lines := strings.Split(strings.TrimSpace(string(out)), "\n")
+	lines := strings.Split(strings.TrimSpace(out), "\n")
 	lines = RemoveLinesForExistingElements(lines, existingContainers)
-	c.Assert(lines, checker.HasLen, 3)
+	assert.Equal(c, len(lines), 3)
 
 	fields := strings.Fields(lines[0])
-	c.Assert(fields, checker.HasLen, 2)
-	c.Assert(fields[0], checker.Equals, "bind-mount-test")
-	c.Assert(fields[1], checker.Equals, bindMountSource)
+	assert.Equal(c, len(fields), 2)
+	assert.Equal(c, fields[0], "bind-mount-test")
+	assert.Equal(c, fields[1], bindMountSource)
 
 	fields = strings.Fields(lines[1])
-	c.Assert(fields, checker.HasLen, 2)
+	assert.Equal(c, len(fields), 2)
 
 	anonymousVolumeID := fields[1]
 
 	fields = strings.Fields(lines[2])
-	c.Assert(fields[1], checker.Equals, "ps-volume-test")
+	assert.Equal(c, fields[1], "ps-volume-test")
 
 	// filter by volume name
 	out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume=ps-volume-test")
 
-	lines = strings.Split(strings.TrimSpace(string(out)), "\n")
+	lines = strings.Split(strings.TrimSpace(out), "\n")
 	lines = RemoveLinesForExistingElements(lines, existingContainers)
-	c.Assert(lines, checker.HasLen, 1)
+	assert.Equal(c, len(lines), 1)
 
 	fields = strings.Fields(lines[0])
-	c.Assert(fields[1], checker.Equals, "ps-volume-test")
+	assert.Equal(c, fields[1], "ps-volume-test")
 
 	// empty results filtering by unknown volume
 	out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume=this-volume-should-not-exist")
-	c.Assert(strings.TrimSpace(string(out)), checker.HasLen, 0)
+	assert.Equal(c, len(strings.TrimSpace(out)), 0)
 
 	// filter by mount destination
 	out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume="+mp)
 
-	lines = strings.Split(strings.TrimSpace(string(out)), "\n")
+	lines = strings.Split(strings.TrimSpace(out), "\n")
 	lines = RemoveLinesForExistingElements(lines, existingContainers)
-	c.Assert(lines, checker.HasLen, 2)
+	assert.Equal(c, len(lines), 2)
 
 	fields = strings.Fields(lines[0])
-	c.Assert(fields[1], checker.Equals, anonymousVolumeID)
+	assert.Equal(c, fields[1], anonymousVolumeID)
 	fields = strings.Fields(lines[1])
-	c.Assert(fields[1], checker.Equals, "ps-volume-test")
+	assert.Equal(c, fields[1], "ps-volume-test")
 
 	// filter by bind mount source
 	out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume="+bindMountSource)
 
-	lines = strings.Split(strings.TrimSpace(string(out)), "\n")
+	lines = strings.Split(strings.TrimSpace(out), "\n")
 	lines = RemoveLinesForExistingElements(lines, existingContainers)
-	c.Assert(lines, checker.HasLen, 1)
+	assert.Equal(c, len(lines), 1)
 
 	fields = strings.Fields(lines[0])
-	c.Assert(fields, checker.HasLen, 2)
-	c.Assert(fields[0], checker.Equals, "bind-mount-test")
-	c.Assert(fields[1], checker.Equals, bindMountSource)
+	assert.Equal(c, len(fields), 2)
+	assert.Equal(c, fields[0], "bind-mount-test")
+	assert.Equal(c, fields[1], bindMountSource)
 
 	// filter by bind mount destination
 	out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume="+bindMountDestination)
 
-	lines = strings.Split(strings.TrimSpace(string(out)), "\n")
+	lines = strings.Split(strings.TrimSpace(out), "\n")
 	lines = RemoveLinesForExistingElements(lines, existingContainers)
-	c.Assert(lines, checker.HasLen, 1)
+	assert.Equal(c, len(lines), 1)
 
 	fields = strings.Fields(lines[0])
-	c.Assert(fields, checker.HasLen, 2)
-	c.Assert(fields[0], checker.Equals, "bind-mount-test")
-	c.Assert(fields[1], checker.Equals, bindMountSource)
+	assert.Equal(c, len(fields), 2)
+	assert.Equal(c, fields[0], "bind-mount-test")
+	assert.Equal(c, fields[1], bindMountSource)
 
 	// empty results filtering by unknown mount point
 	out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume="+prefix+slash+"this-path-was-never-mounted")
-	c.Assert(strings.TrimSpace(string(out)), checker.HasLen, 0)
+	assert.Equal(c, len(strings.TrimSpace(out)), 0)
 }
 
-func (s *DockerSuite) TestPsListContainersFilterNetwork(c *check.C) {
+func (s *DockerSuite) TestPsListContainersFilterNetwork(c *testing.T) {
 	existing := ExistingContainerIDs(c)
 
 	// TODO default network on Windows is not called "bridge", and creating a
@@ -724,18 +720,18 @@ func (s *DockerSuite) TestPsListContainersFilterNetwork(c *check.C) {
 
 	// Filter docker ps on non existing network
 	out, _ := dockerCmd(c, "ps", "--filter", "network=doesnotexist")
-	containerOut := strings.TrimSpace(string(out))
+	containerOut := strings.TrimSpace(out)
 	lines := strings.Split(containerOut, "\n")
 
 	// skip header
 	lines = lines[1:]
 
 	// ps output should have no containers
-	c.Assert(RemoveLinesForExistingElements(lines, existing), checker.HasLen, 0)
+	assert.Equal(c, len(RemoveLinesForExistingElements(lines, existing)), 0)
 
 	// Filter docker ps on network bridge
 	out, _ = dockerCmd(c, "ps", "--filter", "network=bridge")
-	containerOut = strings.TrimSpace(string(out))
+	containerOut = strings.TrimSpace(out)
 
 	lines = strings.Split(containerOut, "\n")
 
@@ -743,40 +739,38 @@ func (s *DockerSuite) TestPsListContainersFilterNetwork(c *check.C) {
 	lines = lines[1:]
 
 	// ps output should have only one container
-	c.Assert(RemoveLinesForExistingElements(lines, existing), checker.HasLen, 1)
+	assert.Equal(c, len(RemoveLinesForExistingElements(lines, existing)), 1)
 
 	// Making sure onbridgenetwork is on the output
-	c.Assert(containerOut, checker.Contains, "onbridgenetwork", check.Commentf("Missing the container on network\n"))
-
+	assert.Assert(c, strings.Contains(containerOut, "onbridgenetwork"), "Missing the container on network\n")
 	// Filter docker ps on networks bridge and none
 	out, _ = dockerCmd(c, "ps", "--filter", "network=bridge", "--filter", "network=none")
-	containerOut = strings.TrimSpace(string(out))
+	containerOut = strings.TrimSpace(out)
 
 	lines = strings.Split(containerOut, "\n")
 
 	// skip header
 	lines = lines[1:]
 
-	//ps output should have both the containers
-	c.Assert(RemoveLinesForExistingElements(lines, existing), checker.HasLen, 2)
+	// ps output should have both the containers
+	assert.Equal(c, len(RemoveLinesForExistingElements(lines, existing)), 2)
 
 	// Making sure onbridgenetwork and onnonenetwork is on the output
-	c.Assert(containerOut, checker.Contains, "onnonenetwork", check.Commentf("Missing the container on none network\n"))
-	c.Assert(containerOut, checker.Contains, "onbridgenetwork", check.Commentf("Missing the container on bridge network\n"))
-
+	assert.Assert(c, strings.Contains(containerOut, "onnonenetwork"), "Missing the container on none network\n")
+	assert.Assert(c, strings.Contains(containerOut, "onbridgenetwork"), "Missing the container on bridge network\n")
 	nwID, _ := dockerCmd(c, "network", "inspect", "--format", "{{.ID}}", "bridge")
 
 	// Filter by network ID
 	out, _ = dockerCmd(c, "ps", "--filter", "network="+nwID)
-	containerOut = strings.TrimSpace(string(out))
+	containerOut = strings.TrimSpace(out)
 
-	c.Assert(containerOut, checker.Contains, "onbridgenetwork")
+	assert.Assert(c, is.Contains(containerOut, "onbridgenetwork"))
 
 	// Filter by partial network ID
-	partialnwID := string(nwID[0:4])
+	partialnwID := nwID[0:4]
 
 	out, _ = dockerCmd(c, "ps", "--filter", "network="+partialnwID)
-	containerOut = strings.TrimSpace(string(out))
+	containerOut = strings.TrimSpace(out)
 
 	lines = strings.Split(containerOut, "\n")
 
@@ -784,14 +778,13 @@ func (s *DockerSuite) TestPsListContainersFilterNetwork(c *check.C) {
 	lines = lines[1:]
 
 	// ps output should have only one container
-	c.Assert(RemoveLinesForExistingElements(lines, existing), checker.HasLen, 1)
+	assert.Equal(c, len(RemoveLinesForExistingElements(lines, existing)), 1)
 
 	// Making sure onbridgenetwork is on the output
-	c.Assert(containerOut, checker.Contains, "onbridgenetwork", check.Commentf("Missing the container on network\n"))
-
+	assert.Assert(c, strings.Contains(containerOut, "onbridgenetwork"), "Missing the container on network\n")
 }
 
-func (s *DockerSuite) TestPsByOrder(c *check.C) {
+func (s *DockerSuite) TestPsByOrder(c *testing.T) {
 	out := runSleepingContainer(c, "--name", "xyz-abc")
 	container1 := strings.TrimSpace(out)
 
@@ -803,14 +796,14 @@ func (s *DockerSuite) TestPsByOrder(c *check.C) {
 
 	// Run multiple time should have the same result
 	out = cli.DockerCmd(c, "ps", "--no-trunc", "-q", "-f", "name=xyz").Combined()
-	c.Assert(strings.TrimSpace(out), checker.Equals, fmt.Sprintf("%s\n%s", container2, container1))
+	assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%s\n%s", container2, container1))
 
 	// Run multiple time should have the same result
 	out = cli.DockerCmd(c, "ps", "--no-trunc", "-q", "-f", "name=xyz").Combined()
-	c.Assert(strings.TrimSpace(out), checker.Equals, fmt.Sprintf("%s\n%s", container2, container1))
+	assert.Equal(c, strings.TrimSpace(out), fmt.Sprintf("%s\n%s", container2, container1))
 }
 
-func (s *DockerSuite) TestPsListContainersFilterPorts(c *check.C) {
+func (s *DockerSuite) TestPsListContainersFilterPorts(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	existingContainers := ExistingContainerIDs(c)
 
@@ -820,33 +813,47 @@ func (s *DockerSuite) TestPsListContainersFilterPorts(c *check.C) {
 	out, _ = dockerCmd(c, "run", "-d", "--expose=8080", "busybox", "top")
 	id2 := strings.TrimSpace(out)
 
+	out, _ = dockerCmd(c, "run", "-d", "-p", "1090:90", "busybox", "top")
+	id3 := strings.TrimSpace(out)
+
 	out, _ = dockerCmd(c, "ps", "--no-trunc", "-q")
-	c.Assert(strings.TrimSpace(out), checker.Contains, id1)
-	c.Assert(strings.TrimSpace(out), checker.Contains, id2)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id1))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id2))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), id3))
 
 	out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "publish=80-8080/udp")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id2)
+	assert.Assert(c, strings.TrimSpace(out) != id1)
+	assert.Assert(c, strings.TrimSpace(out) != id2)
+	assert.Assert(c, strings.TrimSpace(out) != id3)
 
 	out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "expose=8081")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id2)
+	assert.Assert(c, strings.TrimSpace(out) != id1)
+	assert.Assert(c, strings.TrimSpace(out) != id2)
+	assert.Assert(c, strings.TrimSpace(out) != id3)
 
 	out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "publish=80-81")
-	c.Assert(strings.TrimSpace(out), checker.Equals, id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id2)
+	assert.Assert(c, strings.TrimSpace(out) != id1)
+	assert.Assert(c, strings.TrimSpace(out) != id2)
+	assert.Assert(c, strings.TrimSpace(out) != id3)
 
 	out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "expose=80/tcp")
-	c.Assert(strings.TrimSpace(out), checker.Equals, id1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id2)
+	assert.Equal(c, strings.TrimSpace(out), id1)
+	assert.Assert(c, strings.TrimSpace(out) != id2)
+	assert.Assert(c, strings.TrimSpace(out) != id3)
+
+	out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "publish=1090")
+	assert.Assert(c, strings.TrimSpace(out) != id1)
+	assert.Assert(c, strings.TrimSpace(out) != id2)
+	assert.Equal(c, strings.TrimSpace(out), id3)
 
 	out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "expose=8080/tcp")
 	out = RemoveOutputForExistingElements(out, existingContainers)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id1)
-	c.Assert(strings.TrimSpace(out), checker.Equals, id2)
+	assert.Assert(c, strings.TrimSpace(out) != id1)
+	assert.Equal(c, strings.TrimSpace(out), id2)
+	assert.Assert(c, strings.TrimSpace(out) != id3)
 }
 
-func (s *DockerSuite) TestPsNotShowLinknamesOfDeletedContainer(c *check.C) {
+func (s *DockerSuite) TestPsNotShowLinknamesOfDeletedContainer(c *testing.T) {
 	testRequires(c, DaemonIsLinux, MinimumAPIVersion("1.31"))
 	existingContainers := ExistingContainerNames(c)
 
@@ -854,16 +861,16 @@ func (s *DockerSuite) TestPsNotShowLinknamesOfDeletedContainer(c *check.C) {
 	dockerCmd(c, "create", "--name=bbb", "--link=aaa", "busybox", "top")
 
 	out, _ := dockerCmd(c, "ps", "--no-trunc", "-a", "--format", "{{.Names}}")
-	lines := strings.Split(strings.TrimSpace(string(out)), "\n")
+	lines := strings.Split(strings.TrimSpace(out), "\n")
 	lines = RemoveLinesForExistingElements(lines, existingContainers)
 	expected := []string{"bbb", "aaa,bbb/aaa"}
 	var names []string
 	names = append(names, lines...)
-	c.Assert(expected, checker.DeepEquals, names, check.Commentf("Expected array with non-truncated names: %v, got: %v", expected, names))
+	assert.Assert(c, is.DeepEqual(names, expected), "Expected array with non-truncated names: %v, got: %v", expected, names)
 
 	dockerCmd(c, "rm", "bbb")
 
 	out, _ = dockerCmd(c, "ps", "--no-trunc", "-a", "--format", "{{.Names}}")
 	out = RemoveOutputForExistingElements(out, existingContainers)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "aaa")
+	assert.Equal(c, strings.TrimSpace(out), "aaa")
 }
diff --git a/integration-cli/docker_cli_pull_local_test.go b/integration-cli/docker_cli_pull_local_test.go
index 33d4ae5e7c612..489ca504febc8 100644
--- a/integration-cli/docker_cli_pull_local_test.go
+++ b/integration-cli/docker_cli_pull_local_test.go
@@ -3,28 +3,27 @@ package main
 import (
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime"
 	"strings"
+	"testing"
 
 	"github.com/docker/distribution"
 	"github.com/docker/distribution/manifest"
 	"github.com/docker/distribution/manifest/manifestlist"
 	"github.com/docker/distribution/manifest/schema2"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
-	"github.com/opencontainers/go-digest"
-	"gotest.tools/icmd"
+	digest "github.com/opencontainers/go-digest"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
 // testPullImageWithAliases pulls a specific image tag and verifies that any aliases (i.e., other
 // tags for the same image) are not also pulled down.
 //
 // Ref: docker/docker#8141
-func testPullImageWithAliases(c *check.C) {
+func testPullImageWithAliases(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
 
 	var repos []string
@@ -47,20 +46,20 @@ func testPullImageWithAliases(c *check.C) {
 	dockerCmd(c, "inspect", repos[0])
 	for _, repo := range repos[1:] {
 		_, _, err := dockerCmdWithError("inspect", repo)
-		c.Assert(err, checker.NotNil, check.Commentf("Image %v shouldn't have been pulled down", repo))
+		assert.ErrorContains(c, err, "", "Image %v shouldn't have been pulled down", repo)
 	}
 }
 
-func (s *DockerRegistrySuite) TestPullImageWithAliases(c *check.C) {
+func (s *DockerRegistrySuite) TestPullImageWithAliases(c *testing.T) {
 	testPullImageWithAliases(c)
 }
 
-func (s *DockerSchema1RegistrySuite) TestPullImageWithAliases(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestPullImageWithAliases(c *testing.T) {
 	testPullImageWithAliases(c)
 }
 
 // testConcurrentPullWholeRepo pulls the same repo concurrently.
-func testConcurrentPullWholeRepo(c *check.C) {
+func testConcurrentPullWholeRepo(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
 
 	var repos []string
@@ -82,8 +81,8 @@ func testConcurrentPullWholeRepo(c *check.C) {
 	dockerCmd(c, args...)
 
 	// Run multiple re-pulls concurrently
-	results := make(chan error)
 	numPulls := 3
+	results := make(chan error, numPulls)
 
 	for i := 0; i != numPulls; i++ {
 		go func() {
@@ -96,32 +95,32 @@ func testConcurrentPullWholeRepo(c *check.C) {
 	// package is not goroutine-safe.
 	for i := 0; i != numPulls; i++ {
 		err := <-results
-		c.Assert(err, checker.IsNil, check.Commentf("concurrent pull failed with error: %v", err))
+		assert.NilError(c, err, "concurrent pull failed with error: %v", err)
 	}
 
 	// Ensure all tags were pulled successfully
 	for _, repo := range repos {
 		dockerCmd(c, "inspect", repo)
 		out, _ := dockerCmd(c, "run", "--rm", repo)
-		c.Assert(strings.TrimSpace(out), checker.Equals, "/bin/sh -c echo "+repo)
+		assert.Equal(c, strings.TrimSpace(out), "/bin/sh -c echo "+repo)
 	}
 }
 
-func (s *DockerRegistrySuite) testConcurrentPullWholeRepo(c *check.C) {
+func (s *DockerRegistrySuite) TestConcurrentPullWholeRepo(c *testing.T) {
 	testConcurrentPullWholeRepo(c)
 }
 
-func (s *DockerSchema1RegistrySuite) testConcurrentPullWholeRepo(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestConcurrentPullWholeRepo(c *testing.T) {
 	testConcurrentPullWholeRepo(c)
 }
 
 // testConcurrentFailingPull tries a concurrent pull that doesn't succeed.
-func testConcurrentFailingPull(c *check.C) {
+func testConcurrentFailingPull(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
 
 	// Run multiple pulls concurrently
-	results := make(chan error)
 	numPulls := 3
+	results := make(chan error, numPulls)
 
 	for i := 0; i != numPulls; i++ {
 		go func() {
@@ -134,21 +133,21 @@ func testConcurrentFailingPull(c *check.C) {
 	// package is not goroutine-safe.
 	for i := 0; i != numPulls; i++ {
 		err := <-results
-		c.Assert(err, checker.NotNil, check.Commentf("expected pull to fail"))
+		assert.ErrorContains(c, err, "", "expected pull to fail")
 	}
 }
 
-func (s *DockerRegistrySuite) testConcurrentFailingPull(c *check.C) {
+func (s *DockerRegistrySuite) TestConcurrentFailingPull(c *testing.T) {
 	testConcurrentFailingPull(c)
 }
 
-func (s *DockerSchema1RegistrySuite) testConcurrentFailingPull(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestConcurrentFailingPull(c *testing.T) {
 	testConcurrentFailingPull(c)
 }
 
 // testConcurrentPullMultipleTags pulls multiple tags from the same repo
 // concurrently.
-func testConcurrentPullMultipleTags(c *check.C) {
+func testConcurrentPullMultipleTags(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
 
 	var repos []string
@@ -170,7 +169,7 @@ func testConcurrentPullMultipleTags(c *check.C) {
 	dockerCmd(c, args...)
 
 	// Re-pull individual tags, in parallel
-	results := make(chan error)
+	results := make(chan error, len(repos))
 
 	for _, repo := range repos {
 		go func(repo string) {
@@ -183,28 +182,28 @@ func testConcurrentPullMultipleTags(c *check.C) {
 	// package is not goroutine-safe.
 	for range repos {
 		err := <-results
-		c.Assert(err, checker.IsNil, check.Commentf("concurrent pull failed with error: %v", err))
+		assert.NilError(c, err, "concurrent pull failed with error: %v", err)
 	}
 
 	// Ensure all tags were pulled successfully
 	for _, repo := range repos {
 		dockerCmd(c, "inspect", repo)
 		out, _ := dockerCmd(c, "run", "--rm", repo)
-		c.Assert(strings.TrimSpace(out), checker.Equals, "/bin/sh -c echo "+repo)
+		assert.Equal(c, strings.TrimSpace(out), "/bin/sh -c echo "+repo)
 	}
 }
 
-func (s *DockerRegistrySuite) TestConcurrentPullMultipleTags(c *check.C) {
+func (s *DockerRegistrySuite) TestConcurrentPullMultipleTags(c *testing.T) {
 	testConcurrentPullMultipleTags(c)
 }
 
-func (s *DockerSchema1RegistrySuite) TestConcurrentPullMultipleTags(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestConcurrentPullMultipleTags(c *testing.T) {
 	testConcurrentPullMultipleTags(c)
 }
 
 // testPullIDStability verifies that pushing an image and pulling it back
 // preserves the image ID.
-func testPullIDStability(c *check.C) {
+func testPullIDStability(c *testing.T) {
 	derivedImage := privateRegistryURL + "/dockercli/id-stability"
 	baseImage := "busybox"
 
@@ -255,16 +254,16 @@ func testPullIDStability(c *check.C) {
 	}
 }
 
-func (s *DockerRegistrySuite) TestPullIDStability(c *check.C) {
+func (s *DockerRegistrySuite) TestPullIDStability(c *testing.T) {
 	testPullIDStability(c)
 }
 
-func (s *DockerSchema1RegistrySuite) TestPullIDStability(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestPullIDStability(c *testing.T) {
 	testPullIDStability(c)
 }
 
 // #21213
-func testPullNoLayers(c *check.C) {
+func testPullNoLayers(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/scratch", privateRegistryURL)
 
 	buildImageSuccessfully(c, repoName, build.WithDockerfile(`
@@ -275,18 +274,18 @@ func testPullNoLayers(c *check.C) {
 	dockerCmd(c, "pull", repoName)
 }
 
-func (s *DockerRegistrySuite) TestPullNoLayers(c *check.C) {
+func (s *DockerRegistrySuite) TestPullNoLayers(c *testing.T) {
 	testPullNoLayers(c)
 }
 
-func (s *DockerSchema1RegistrySuite) TestPullNoLayers(c *check.C) {
+func (s *DockerSchema1RegistrySuite) TestPullNoLayers(c *testing.T) {
 	testPullNoLayers(c)
 }
 
-func (s *DockerRegistrySuite) TestPullManifestList(c *check.C) {
+func (s *DockerRegistrySuite) TestPullManifestList(c *testing.T) {
 	testRequires(c, NotArm)
 	pushDigest, err := setupImage(c)
-	c.Assert(err, checker.IsNil, check.Commentf("error setting up image"))
+	assert.NilError(c, err, "error setting up image")
 
 	// Inject a manifest list into the registry
 	manifestList := &manifestlist.ManifestList{
@@ -321,7 +320,7 @@ func (s *DockerRegistrySuite) TestPullManifestList(c *check.C) {
 	}
 
 	manifestListJSON, err := json.MarshalIndent(manifestList, "", "   ")
-	c.Assert(err, checker.IsNil, check.Commentf("error marshalling manifest list"))
+	assert.NilError(c, err, "error marshalling manifest list")
 
 	manifestListDigest := digest.FromBytes(manifestListJSON)
 	hexDigest := manifestListDigest.Hex()
@@ -331,34 +330,34 @@ func (s *DockerRegistrySuite) TestPullManifestList(c *check.C) {
 	// Write manifest list to blob store
 	blobDir := filepath.Join(registryV2Path, "blobs", "sha256", hexDigest[:2], hexDigest)
 	err = os.MkdirAll(blobDir, 0755)
-	c.Assert(err, checker.IsNil, check.Commentf("error creating blob dir"))
+	assert.NilError(c, err, "error creating blob dir")
 	blobPath := filepath.Join(blobDir, "data")
-	err = ioutil.WriteFile(blobPath, []byte(manifestListJSON), 0644)
-	c.Assert(err, checker.IsNil, check.Commentf("error writing manifest list"))
+	err = os.WriteFile(blobPath, manifestListJSON, 0644)
+	assert.NilError(c, err, "error writing manifest list")
 
 	// Add to revision store
 	revisionDir := filepath.Join(registryV2Path, "repositories", remoteRepoName, "_manifests", "revisions", "sha256", hexDigest)
 	err = os.Mkdir(revisionDir, 0755)
-	c.Assert(err, checker.IsNil, check.Commentf("error creating revision dir"))
+	assert.Assert(c, err == nil, "error creating revision dir")
 	revisionPath := filepath.Join(revisionDir, "link")
-	err = ioutil.WriteFile(revisionPath, []byte(manifestListDigest.String()), 0644)
-	c.Assert(err, checker.IsNil, check.Commentf("error writing revision link"))
+	err = os.WriteFile(revisionPath, []byte(manifestListDigest.String()), 0644)
+	assert.Assert(c, err == nil, "error writing revision link")
 
 	// Update tag
 	tagPath := filepath.Join(registryV2Path, "repositories", remoteRepoName, "_manifests", "tags", "latest", "current", "link")
-	err = ioutil.WriteFile(tagPath, []byte(manifestListDigest.String()), 0644)
-	c.Assert(err, checker.IsNil, check.Commentf("error writing tag link"))
+	err = os.WriteFile(tagPath, []byte(manifestListDigest.String()), 0644)
+	assert.NilError(c, err, "error writing tag link")
 
 	// Verify that the image can be pulled through the manifest list.
 	out, _ := dockerCmd(c, "pull", repoName)
 
 	// The pull output includes "Digest: ", so find that
 	matches := digestRegex.FindStringSubmatch(out)
-	c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", out))
+	assert.Equal(c, len(matches), 2, fmt.Sprintf("unable to parse digest from pull output: %s", out))
 	pullDigest := matches[1]
 
 	// Make sure the pushed and pull digests match
-	c.Assert(manifestListDigest.String(), checker.Equals, pullDigest)
+	assert.Equal(c, manifestListDigest.String(), pullDigest)
 
 	// Was the image actually created?
 	dockerCmd(c, "inspect", repoName)
@@ -367,35 +366,34 @@ func (s *DockerRegistrySuite) TestPullManifestList(c *check.C) {
 }
 
 // #23100
-func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuthLoginWithScheme(c *check.C) {
+func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuthLoginWithScheme(c *testing.T) {
 	osPath := os.Getenv("PATH")
 	defer os.Setenv("PATH", osPath)
 
 	workingDir, err := os.Getwd()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute)
 
 	os.Setenv("PATH", testPath)
 
 	repoName := fmt.Sprintf("%v/dockercli/busybox:authtest", privateRegistryURL)
 
-	tmp, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, checker.IsNil)
+	tmp, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 
 	externalAuthConfig := `{ "credsStore": "shell-test" }`
 
 	configPath := filepath.Join(tmp, "config.json")
-	err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(configPath, []byte(externalAuthConfig), 0644)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL)
 
-	b, err := ioutil.ReadFile(configPath)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":")
-
+	b, err := os.ReadFile(configPath)
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(string(b), "\"auth\":"))
 	dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
 	dockerCmd(c, "--config", tmp, "push", repoName)
 
@@ -412,35 +410,34 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuthLoginWithSchem
 	dockerCmd(c, "--config", tmp, "logout", "https://"+privateRegistryURL)
 }
 
-func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuth(c *check.C) {
+func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuth(c *testing.T) {
 	osPath := os.Getenv("PATH")
 	defer os.Setenv("PATH", osPath)
 
 	workingDir, err := os.Getwd()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	absolute, err := filepath.Abs(filepath.Join(workingDir, "fixtures", "auth"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	testPath := fmt.Sprintf("%s%c%s", osPath, filepath.ListSeparator, absolute)
 
 	os.Setenv("PATH", testPath)
 
 	repoName := fmt.Sprintf("%v/dockercli/busybox:authtest", privateRegistryURL)
 
-	tmp, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, checker.IsNil)
+	tmp, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 
 	externalAuthConfig := `{ "credsStore": "shell-test" }`
 
 	configPath := filepath.Join(tmp, "config.json")
-	err = ioutil.WriteFile(configPath, []byte(externalAuthConfig), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(configPath, []byte(externalAuthConfig), 0644)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "--config", tmp, "login", "-u", s.reg.Username(), "-p", s.reg.Password(), privateRegistryURL)
 
-	b, err := ioutil.ReadFile(configPath)
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":")
-
+	b, err := os.ReadFile(configPath)
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(string(b), "\"auth\":"))
 	dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
 	dockerCmd(c, "--config", tmp, "push", repoName)
 
@@ -448,7 +445,7 @@ func (s *DockerRegistryAuthHtpasswdSuite) TestPullWithExternalAuth(c *check.C) {
 }
 
 // TestRunImplicitPullWithNoTag should pull implicitly only the default tag (latest)
-func (s *DockerRegistrySuite) TestRunImplicitPullWithNoTag(c *check.C) {
+func (s *DockerRegistrySuite) TestRunImplicitPullWithNoTag(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	repo := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
 	repoTag1 := fmt.Sprintf("%v:latest", repo)
@@ -461,10 +458,9 @@ func (s *DockerRegistrySuite) TestRunImplicitPullWithNoTag(c *check.C) {
 	dockerCmd(c, "rmi", repoTag2)
 
 	out, _ := dockerCmd(c, "run", repo)
-	c.Assert(out, checker.Contains, fmt.Sprintf("Unable to find image '%s:latest' locally", repo))
-
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("Unable to find image '%s:latest' locally", repo)))
 	// There should be only one line for repo, the one with repo:latest
 	outImageCmd, _ := dockerCmd(c, "images", repo)
 	splitOutImageCmd := strings.Split(strings.TrimSpace(outImageCmd), "\n")
-	c.Assert(splitOutImageCmd, checker.HasLen, 2)
+	assert.Equal(c, len(splitOutImageCmd), 2)
 }
diff --git a/integration-cli/docker_cli_pull_test.go b/integration-cli/docker_cli_pull_test.go
index 0e88b1e56f672..9606d7a9033e9 100644
--- a/integration-cli/docker_cli_pull_test.go
+++ b/integration-cli/docker_cli_pull_test.go
@@ -5,40 +5,42 @@ import (
 	"regexp"
 	"strings"
 	"sync"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 // TestPullFromCentralRegistry pulls an image from the central registry and verifies that the client
 // prints all expected output.
-func (s *DockerHubPullSuite) TestPullFromCentralRegistry(c *check.C) {
+func (s *DockerHubPullSuite) TestPullFromCentralRegistry(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out := s.Cmd(c, "pull", "hello-world")
 	defer deleteImages("hello-world")
 
-	c.Assert(out, checker.Contains, "Using default tag: latest", check.Commentf("expected the 'latest' tag to be automatically assumed"))
-	c.Assert(out, checker.Contains, "Pulling from library/hello-world", check.Commentf("expected the 'library/' prefix to be automatically assumed"))
-	c.Assert(out, checker.Contains, "Downloaded newer image for hello-world:latest")
+	assert.Assert(c, strings.Contains(out, "Using default tag: latest"), "expected the 'latest' tag to be automatically assumed")
+	assert.Assert(c, strings.Contains(out, "Pulling from library/hello-world"), "expected the 'library/' prefix to be automatically assumed")
+	assert.Assert(c, strings.Contains(out, "Downloaded newer image for hello-world:latest"))
 
 	matches := regexp.MustCompile(`Digest: (.+)\n`).FindAllStringSubmatch(out, -1)
-	c.Assert(len(matches), checker.Equals, 1, check.Commentf("expected exactly one image digest in the output"))
-	c.Assert(len(matches[0]), checker.Equals, 2, check.Commentf("unexpected number of submatches for the digest"))
+	assert.Equal(c, len(matches), 1, "expected exactly one image digest in the output")
+	assert.Equal(c, len(matches[0]), 2, "unexpected number of submatches for the digest")
 	_, err := digest.Parse(matches[0][1])
-	c.Check(err, checker.IsNil, check.Commentf("invalid digest %q in output", matches[0][1]))
+	assert.NilError(c, err, "invalid digest %q in output", matches[0][1])
 
 	// We should have a single entry in images.
 	img := strings.TrimSpace(s.Cmd(c, "images"))
 	splitImg := strings.Split(img, "\n")
-	c.Assert(splitImg, checker.HasLen, 2)
-	c.Assert(splitImg[1], checker.Matches, `hello-world\s+latest.*?`, check.Commentf("invalid output for `docker images` (expected image and tag name"))
+	assert.Equal(c, len(splitImg), 2)
+	match, _ := regexp.MatchString(`hello-world\s+latest.*?`, splitImg[1])
+	assert.Assert(c, match, "invalid output for `docker images` (expected image and tag name)")
 }
 
 // TestPullNonExistingImage pulls non-existing images from the central registry, with different
 // combinations of implicit tag and library prefix.
-func (s *DockerHubPullSuite) TestPullNonExistingImage(c *check.C) {
+func (s *DockerHubPullSuite) TestPullNonExistingImage(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	type entry struct {
@@ -97,13 +99,13 @@ func (s *DockerHubPullSuite) TestPullNonExistingImage(c *check.C) {
 	// Process the results (out, err).
 	for record := range recordChan {
 		if len(record.option) == 0 {
-			c.Assert(record.err, checker.NotNil, check.Commentf("expected non-zero exit status when pulling non-existing image: %s", record.out))
-			c.Assert(record.out, checker.Contains, fmt.Sprintf("pull access denied for %s, repository does not exist or may require 'docker login'", record.e.repo), check.Commentf("expected image not found error messages"))
+			assert.ErrorContains(c, record.err, "", "expected non-zero exit status when pulling non-existing image: %s", record.out)
+			assert.Assert(c, strings.Contains(record.out, fmt.Sprintf("pull access denied for %s, repository does not exist or may require 'docker login'", record.e.repo)), "expected image not found error messages")
 		} else {
 			// pull -a on a nonexistent registry should fall back as well
-			c.Assert(record.err, checker.NotNil, check.Commentf("expected non-zero exit status when pulling non-existing image: %s", record.out))
-			c.Assert(record.out, checker.Contains, fmt.Sprintf("pull access denied for %s, repository does not exist or may require 'docker login'", record.e.repo), check.Commentf("expected image not found error messages"))
-			c.Assert(record.out, checker.Not(checker.Contains), "unauthorized", check.Commentf(`message should not contain "unauthorized"`))
+			assert.ErrorContains(c, record.err, "", "expected non-zero exit status when pulling non-existing image: %s", record.out)
+			assert.Assert(c, strings.Contains(record.out, fmt.Sprintf("pull access denied for %s, repository does not exist or may require 'docker login'", record.e.repo)), "expected image not found error messages")
+			assert.Assert(c, !strings.Contains(record.out, "unauthorized"), `message should not contain "unauthorized"`)
 		}
 	}
 
@@ -113,7 +115,7 @@ func (s *DockerHubPullSuite) TestPullNonExistingImage(c *check.C) {
 // that pulling the same image with different combinations of implicit elements of the image
 // reference (tag, repository, central registry url, ...) doesn't trigger a new pull nor leads to
 // multiple images.
-func (s *DockerHubPullSuite) TestPullFromCentralRegistryImplicitRefParts(c *check.C) {
+func (s *DockerHubPullSuite) TestPullFromCentralRegistryImplicitRefParts(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	// Pull hello-world from v2
@@ -168,7 +170,7 @@ func (s *DockerHubPullSuite) TestPullFromCentralRegistryImplicitRefParts(c *chec
 			s.Cmd(c, "rmi", ref)
 			s.Cmd(c, "tag", "hello-world-backup", "hello-world")
 		}
-		c.Assert(out, checker.Contains, "Image is up to date for hello-world:latest")
+		assert.Assert(c, strings.Contains(out, "Image is up to date for hello-world:latest"))
 	}
 
 	s.Cmd(c, "rmi", "hello-world-backup")
@@ -176,32 +178,33 @@ func (s *DockerHubPullSuite) TestPullFromCentralRegistryImplicitRefParts(c *chec
 	// We should have a single entry in images.
 	img := strings.TrimSpace(s.Cmd(c, "images"))
 	splitImg := strings.Split(img, "\n")
-	c.Assert(splitImg, checker.HasLen, 2)
-	c.Assert(splitImg[1], checker.Matches, `hello-world\s+latest.*?`, check.Commentf("invalid output for `docker images` (expected image and tag name"))
+	assert.Equal(c, len(splitImg), 2)
+	match, _ := regexp.MatchString(`hello-world\s+latest.*?`, splitImg[1])
+	assert.Assert(c, match, "invalid output for `docker images` (expected image and tag name)")
 }
 
 // TestPullScratchNotAllowed verifies that pulling 'scratch' is rejected.
-func (s *DockerHubPullSuite) TestPullScratchNotAllowed(c *check.C) {
+func (s *DockerHubPullSuite) TestPullScratchNotAllowed(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, err := s.CmdWithError("pull", "scratch")
-	c.Assert(err, checker.NotNil, check.Commentf("expected pull of scratch to fail"))
-	c.Assert(out, checker.Contains, "'scratch' is a reserved name")
-	c.Assert(out, checker.Not(checker.Contains), "Pulling repository scratch")
+	assert.ErrorContains(c, err, "", "expected pull of scratch to fail")
+	assert.Assert(c, strings.Contains(out, "'scratch' is a reserved name"))
+	assert.Assert(c, !strings.Contains(out, "Pulling repository scratch"))
 }
 
 // TestPullAllTagsFromCentralRegistry pulls using `all-tags` for a given image and verifies that it
 // results in more images than a naked pull.
-func (s *DockerHubPullSuite) TestPullAllTagsFromCentralRegistry(c *check.C) {
+func (s *DockerHubPullSuite) TestPullAllTagsFromCentralRegistry(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	s.Cmd(c, "pull", "dockercore/engine-pull-all-test-fixture")
 	outImageCmd := s.Cmd(c, "images", "dockercore/engine-pull-all-test-fixture")
 	splitOutImageCmd := strings.Split(strings.TrimSpace(outImageCmd), "\n")
-	c.Assert(splitOutImageCmd, checker.HasLen, 2)
+	assert.Equal(c, len(splitOutImageCmd), 2)
 
 	s.Cmd(c, "pull", "--all-tags=true", "dockercore/engine-pull-all-test-fixture")
 	outImageAllTagCmd := s.Cmd(c, "images", "dockercore/engine-pull-all-test-fixture")
 	linesCount := strings.Count(outImageAllTagCmd, "\n")
-	c.Assert(linesCount, checker.GreaterThan, 2, check.Commentf("pulling all tags should provide more than two images, got %s", outImageAllTagCmd))
+	assert.Assert(c, linesCount > 2, "pulling all tags should provide more than two images, got %s", outImageAllTagCmd)
 
 	// Verify that the line for 'dockercore/engine-pull-all-test-fixture:latest' is left unchanged.
 	var latestLine string
@@ -211,7 +214,7 @@ func (s *DockerHubPullSuite) TestPullAllTagsFromCentralRegistry(c *check.C) {
 			break
 		}
 	}
-	c.Assert(latestLine, checker.Not(checker.Equals), "", check.Commentf("no entry for dockercore/engine-pull-all-test-fixture:latest found after pulling all tags"))
+	assert.Assert(c, latestLine != "", "no entry for dockercore/engine-pull-all-test-fixture:latest found after pulling all tags")
 
 	splitLatest := strings.Fields(latestLine)
 	splitCurrent := strings.Fields(splitOutImageCmd[1])
@@ -228,47 +231,47 @@ func (s *DockerHubPullSuite) TestPullAllTagsFromCentralRegistry(c *check.C) {
 	splitCurrent[4] = ""
 	splitCurrent[5] = ""
 
-	c.Assert(splitLatest, checker.DeepEquals, splitCurrent, check.Commentf("dockercore/engine-pull-all-test-fixture:latest was changed after pulling all tags"))
+	assert.Assert(c, is.DeepEqual(splitLatest, splitCurrent), "dockercore/engine-pull-all-test-fixture:latest was changed after pulling all tags")
 }
 
 // TestPullClientDisconnect kills the client during a pull operation and verifies that the operation
 // gets cancelled.
 //
 // Ref: docker/docker#15589
-func (s *DockerHubPullSuite) TestPullClientDisconnect(c *check.C) {
+func (s *DockerHubPullSuite) TestPullClientDisconnect(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	repoName := "hello-world:latest"
 
 	pullCmd := s.MakeCmd("pull", repoName)
 	stdout, err := pullCmd.StdoutPipe()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	err = pullCmd.Start()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	go pullCmd.Wait()
 
 	// Cancel as soon as we get some output.
 	buf := make([]byte, 10)
 	_, err = stdout.Read(buf)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	err = pullCmd.Process.Kill()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	time.Sleep(2 * time.Second)
 	_, err = s.CmdWithError("inspect", repoName)
-	c.Assert(err, checker.NotNil, check.Commentf("image was pulled after client disconnected"))
+	assert.ErrorContains(c, err, "", "image was pulled after client disconnected")
 }
 
 // Regression test for https://github.com/docker/docker/issues/26429
-func (s *DockerSuite) TestPullLinuxImageFailsOnWindows(c *check.C) {
+func (s *DockerSuite) TestPullLinuxImageFailsOnWindows(c *testing.T) {
 	testRequires(c, DaemonIsWindows, Network)
 	_, _, err := dockerCmdWithError("pull", "ubuntu")
-	c.Assert(err.Error(), checker.Contains, "no matching manifest")
+	assert.ErrorContains(c, err, "no matching manifest for windows")
 }
 
 // Regression test for https://github.com/docker/docker/issues/28892
-func (s *DockerSuite) TestPullWindowsImageFailsOnLinux(c *check.C) {
+func (s *DockerSuite) TestPullWindowsImageFailsOnLinux(c *testing.T) {
 	testRequires(c, DaemonIsLinux, Network)
-	_, _, err := dockerCmdWithError("pull", "microsoft/nanoserver")
-	c.Assert(err.Error(), checker.Contains, "cannot be used on this platform")
+	_, _, err := dockerCmdWithError("pull", "mcr.microsoft.com/windows/servercore:ltsc2019")
+	assert.ErrorContains(c, err, "no matching manifest for linux")
 }
diff --git a/integration-cli/docker_cli_push_test.go b/integration-cli/docker_cli_push_test.go
index 0f0df1ab24cd5..d3af28e46eef4 100644
--- a/integration-cli/docker_cli_push_test.go
+++ b/integration-cli/docker_cli_push_test.go
@@ -3,22 +3,21 @@ package main
 import (
 	"archive/tar"
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
 	"os"
 	"strings"
 	"sync"
+	"testing"
 
 	"github.com/docker/distribution/reference"
-	"github.com/docker/docker/integration-cli/checker"
+	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
-// Pushing an image to a private registry.
-func testPushBusyboxImage(c *check.C) {
+func (s *DockerRegistrySuite) TestPushBusyboxImage(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
 	// tag the image to upload it to the private registry
 	dockerCmd(c, "tag", "busybox", repoName)
@@ -26,69 +25,51 @@ func testPushBusyboxImage(c *check.C) {
 	dockerCmd(c, "push", repoName)
 }
 
-func (s *DockerRegistrySuite) TestPushBusyboxImage(c *check.C) {
-	testPushBusyboxImage(c)
-}
-
-func (s *DockerSchema1RegistrySuite) TestPushBusyboxImage(c *check.C) {
-	testPushBusyboxImage(c)
-}
-
 // pushing an image without a prefix should throw an error
-func (s *DockerSuite) TestPushUnprefixedRepo(c *check.C) {
+func (s *DockerSuite) TestPushUnprefixedRepo(c *testing.T) {
 	out, _, err := dockerCmdWithError("push", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("pushing an unprefixed repo didn't result in a non-zero exit status: %s", out))
+	assert.ErrorContains(c, err, "", "pushing an unprefixed repo didn't result in a non-zero exit status: %s", out)
 }
 
-func testPushUntagged(c *check.C) {
+func (s *DockerRegistrySuite) TestPushUntagged(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
 	expected := "An image does not exist locally with the tag"
 
 	out, _, err := dockerCmdWithError("push", repoName)
-	c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out))
-	c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed"))
-}
-
-func (s *DockerRegistrySuite) TestPushUntagged(c *check.C) {
-	testPushUntagged(c)
-}
-
-func (s *DockerSchema1RegistrySuite) TestPushUntagged(c *check.C) {
-	testPushUntagged(c)
+	assert.ErrorContains(c, err, "", "pushing the image to the private registry should have failed: output %q", out)
+	assert.Assert(c, strings.Contains(out, expected), "pushing the image failed")
 }
 
-func testPushBadTag(c *check.C) {
+func (s *DockerRegistrySuite) TestPushBadTag(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL)
 	expected := "does not exist"
 
 	out, _, err := dockerCmdWithError("push", repoName)
-	c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out))
-	c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed"))
+	assert.ErrorContains(c, err, "", "pushing the image to the private registry should have failed: output %q", out)
+	assert.Assert(c, strings.Contains(out, expected), "pushing the image failed")
 }
 
-func (s *DockerRegistrySuite) TestPushBadTag(c *check.C) {
-	testPushBadTag(c)
-}
-
-func (s *DockerSchema1RegistrySuite) TestPushBadTag(c *check.C) {
-	testPushBadTag(c)
-}
-
-func testPushMultipleTags(c *check.C) {
+func (s *DockerRegistrySuite) TestPushMultipleTags(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
 	repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL)
 	repoTag2 := fmt.Sprintf("%v/dockercli/busybox:t2", privateRegistryURL)
 	// tag the image and upload it to the private registry
 	dockerCmd(c, "tag", "busybox", repoTag1)
-
 	dockerCmd(c, "tag", "busybox", repoTag2)
 
-	dockerCmd(c, "push", repoName)
+	args := []string{"push"}
+	if versions.GreaterThanOrEqualTo(DockerCLIVersion(c), "20.10.0") {
+		// 20.10 CLI removed implicit push all tags and requires the "--all" flag
+		args = append(args, "--all-tags")
+	}
+	args = append(args, repoName)
 
-	// Ensure layer list is equivalent for repoTag1 and repoTag2
-	out1, _ := dockerCmd(c, "pull", repoTag1)
+	dockerCmd(c, args...)
 
 	imageAlreadyExists := ": Image already exists"
+
+	// Ensure layer list is equivalent for repoTag1 and repoTag2
+	out1, _ := dockerCmd(c, "push", repoTag1)
 	var out1Lines []string
 	for _, outputLine := range strings.Split(out1, "\n") {
 		if strings.Contains(outputLine, imageAlreadyExists) {
@@ -96,40 +77,27 @@ func testPushMultipleTags(c *check.C) {
 		}
 	}
 
-	out2, _ := dockerCmd(c, "pull", repoTag2)
-
+	out2, _ := dockerCmd(c, "push", repoTag2)
 	var out2Lines []string
 	for _, outputLine := range strings.Split(out2, "\n") {
 		if strings.Contains(outputLine, imageAlreadyExists) {
-			out1Lines = append(out1Lines, outputLine)
+			out2Lines = append(out2Lines, outputLine)
 		}
 	}
-	c.Assert(out2Lines, checker.HasLen, len(out1Lines))
-
-	for i := range out1Lines {
-		c.Assert(out1Lines[i], checker.Equals, out2Lines[i])
-	}
-}
-
-func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) {
-	testPushMultipleTags(c)
+	assert.DeepEqual(c, out1Lines, out2Lines)
 }
 
-func (s *DockerSchema1RegistrySuite) TestPushMultipleTags(c *check.C) {
-	testPushMultipleTags(c)
-}
-
-func testPushEmptyLayer(c *check.C) {
+func (s *DockerRegistrySuite) TestPushEmptyLayer(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL)
-	emptyTarball, err := ioutil.TempFile("", "empty_tarball")
-	c.Assert(err, check.IsNil, check.Commentf("Unable to create test file"))
+	emptyTarball, err := os.CreateTemp("", "empty_tarball")
+	assert.NilError(c, err, "Unable to create test file")
 
 	tw := tar.NewWriter(emptyTarball)
 	err = tw.Close()
-	c.Assert(err, check.IsNil, check.Commentf("Error creating empty tarball"))
+	assert.NilError(c, err, "Error creating empty tarball")
 
 	freader, err := os.Open(emptyTarball.Name())
-	c.Assert(err, check.IsNil, check.Commentf("Could not open test tarball"))
+	assert.NilError(c, err, "Could not open test tarball")
 	defer freader.Close()
 
 	icmd.RunCmd(icmd.Cmd{
@@ -139,20 +107,12 @@ func testPushEmptyLayer(c *check.C) {
 
 	// Now verify we can push it
 	out, _, err := dockerCmdWithError("push", repoName)
-	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out))
-}
-
-func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) {
-	testPushEmptyLayer(c)
+	assert.NilError(c, err, "pushing the image to the private registry has failed: %s", out)
 }
 
-func (s *DockerSchema1RegistrySuite) TestPushEmptyLayer(c *check.C) {
-	testPushEmptyLayer(c)
-}
-
-// testConcurrentPush pushes multiple tags to the same repo
+// TestConcurrentPush pushes multiple tags to the same repo
 // concurrently.
-func testConcurrentPush(c *check.C) {
+func (s *DockerRegistrySuite) TestConcurrentPush(c *testing.T) {
 	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
 
 	var repos []string
@@ -169,7 +129,7 @@ func testConcurrentPush(c *check.C) {
 	}
 
 	// Push tags, in parallel
-	results := make(chan error)
+	results := make(chan error, len(repos))
 
 	for _, repo := range repos {
 		go func(repo string) {
@@ -180,7 +140,7 @@ func testConcurrentPush(c *check.C) {
 
 	for range repos {
 		err := <-results
-		c.Assert(err, checker.IsNil, check.Commentf("concurrent push failed with error: %v", err))
+		assert.NilError(c, err, "concurrent push failed with error: %v", err)
 	}
 
 	// Clear local images store.
@@ -192,109 +152,69 @@ func testConcurrentPush(c *check.C) {
 		dockerCmd(c, "pull", repo)
 		dockerCmd(c, "inspect", repo)
 		out, _ := dockerCmd(c, "run", "--rm", repo)
-		c.Assert(strings.TrimSpace(out), checker.Equals, "/bin/sh -c echo "+repo)
+		assert.Equal(c, strings.TrimSpace(out), "/bin/sh -c echo "+repo)
 	}
 }
 
-func (s *DockerRegistrySuite) TestConcurrentPush(c *check.C) {
-	testConcurrentPush(c)
-}
-
-func (s *DockerSchema1RegistrySuite) TestConcurrentPush(c *check.C) {
-	testConcurrentPush(c)
-}
-
-func (s *DockerRegistrySuite) TestCrossRepositoryLayerPush(c *check.C) {
+func (s *DockerRegistrySuite) TestCrossRepositoryLayerPush(c *testing.T) {
 	sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
 	// tag the image to upload it to the private registry
 	dockerCmd(c, "tag", "busybox", sourceRepoName)
 	// push the image to the registry
 	out1, _, err := dockerCmdWithError("push", sourceRepoName)
-	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out1))
+	assert.NilError(c, err, "pushing the image to the private registry has failed: %s", out1)
 	// ensure that none of the layers were mounted from another repository during push
-	c.Assert(strings.Contains(out1, "Mounted from"), check.Equals, false)
+	assert.Assert(c, !strings.Contains(out1, "Mounted from"))
 
 	digest1 := reference.DigestRegexp.FindString(out1)
-	c.Assert(len(digest1), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
+	assert.Assert(c, len(digest1) > 0, "no digest found for pushed manifest")
 
 	destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL)
 	// retag the image to upload the same layers to another repo in the same registry
 	dockerCmd(c, "tag", "busybox", destRepoName)
 	// push the image to the registry
 	out2, _, err := dockerCmdWithError("push", destRepoName)
-	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2))
+	assert.NilError(c, err, "pushing the image to the private registry has failed: %s", out2)
+
 	// ensure that layers were mounted from the first repo during push
-	c.Assert(strings.Contains(out2, "Mounted from dockercli/busybox"), check.Equals, true)
+	assert.Assert(c, strings.Contains(out2, "Mounted from dockercli/busybox"))
 
 	digest2 := reference.DigestRegexp.FindString(out2)
-	c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
-	c.Assert(digest1, check.Equals, digest2)
+	assert.Assert(c, len(digest2) > 0, "no digest found for pushed manifest")
+	assert.Equal(c, digest1, digest2)
 
 	// ensure that pushing again produces the same digest
 	out3, _, err := dockerCmdWithError("push", destRepoName)
-	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2))
+	assert.NilError(c, err, "pushing the image to the private registry has failed: %s", out3)
 
 	digest3 := reference.DigestRegexp.FindString(out3)
-	c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
-	c.Assert(digest3, check.Equals, digest2)
+	assert.Assert(c, len(digest3) > 0, "no digest found for pushed manifest")
+	assert.Equal(c, digest3, digest2)
 
 	// ensure that we can pull and run the cross-repo-pushed repository
 	dockerCmd(c, "rmi", destRepoName)
 	dockerCmd(c, "pull", destRepoName)
 	out4, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world")
-	c.Assert(out4, check.Equals, "hello world")
-}
-
-func (s *DockerSchema1RegistrySuite) TestCrossRepositoryLayerPushNotSupported(c *check.C) {
-	sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
-	// tag the image to upload it to the private registry
-	dockerCmd(c, "tag", "busybox", sourceRepoName)
-	// push the image to the registry
-	out1, _, err := dockerCmdWithError("push", sourceRepoName)
-	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out1))
-	// ensure that none of the layers were mounted from another repository during push
-	c.Assert(strings.Contains(out1, "Mounted from"), check.Equals, false)
-
-	digest1 := reference.DigestRegexp.FindString(out1)
-	c.Assert(len(digest1), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
-
-	destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL)
-	// retag the image to upload the same layers to another repo in the same registry
-	dockerCmd(c, "tag", "busybox", destRepoName)
-	// push the image to the registry
-	out2, _, err := dockerCmdWithError("push", destRepoName)
-	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2))
-	// schema1 registry should not support cross-repo layer mounts, so ensure that this does not happen
-	c.Assert(strings.Contains(out2, "Mounted from"), check.Equals, false)
-
-	digest2 := reference.DigestRegexp.FindString(out2)
-	c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
-	c.Assert(digest1, check.Not(check.Equals), digest2)
-
-	// ensure that we can pull and run the second pushed repository
-	dockerCmd(c, "rmi", destRepoName)
-	dockerCmd(c, "pull", destRepoName)
-	out3, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world")
-	c.Assert(out3, check.Equals, "hello world")
+	assert.Equal(c, out4, "hello world")
 }
 
-func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *check.C) {
+func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *testing.T) {
 	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
 	dockerCmd(c, "tag", "busybox", repoName)
 	out, _, err := dockerCmdWithError("push", repoName)
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, check.Not(checker.Contains), "Retrying")
-	c.Assert(out, checker.Contains, "no basic auth credentials")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, !strings.Contains(out, "Retrying"))
+	assert.Assert(c, strings.Contains(out, "no basic auth credentials"))
 }
 
 // This may be flaky but it's needed not to regress on unauthorized push, see #21054
-func (s *DockerSuite) TestPushToCentralRegistryUnauthorized(c *check.C) {
+func (s *DockerSuite) TestPushToCentralRegistryUnauthorized(c *testing.T) {
 	testRequires(c, Network)
 	repoName := "test/busybox"
 	dockerCmd(c, "tag", "busybox", repoName)
 	out, _, err := dockerCmdWithError("push", repoName)
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, check.Not(checker.Contains), "Retrying")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, !strings.Contains(out, "Retrying"))
 }
 
 func getTestTokenService(status int, body string, retries int) *httptest.Server {
@@ -315,68 +235,68 @@ func getTestTokenService(status int, body string, retries int) *httptest.Server
 	}))
 }
 
-func (s *DockerRegistryAuthTokenSuite) TestPushTokenServiceUnauthResponse(c *check.C) {
+func (s *DockerRegistryAuthTokenSuite) TestPushTokenServiceUnauthResponse(c *testing.T) {
 	ts := getTestTokenService(http.StatusUnauthorized, `{"errors": [{"Code":"UNAUTHORIZED", "message": "a message", "detail": null}]}`, 0)
 	defer ts.Close()
 	s.setupRegistryWithTokenService(c, ts.URL)
 	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
 	dockerCmd(c, "tag", "busybox", repoName)
 	out, _, err := dockerCmdWithError("push", repoName)
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), "Retrying")
-	c.Assert(out, checker.Contains, "unauthorized: a message")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, !strings.Contains(out, "Retrying"))
+	assert.Assert(c, strings.Contains(out, "unauthorized: a message"))
 }
 
-func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnauthorized(c *check.C) {
+func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnauthorized(c *testing.T) {
 	ts := getTestTokenService(http.StatusUnauthorized, `{"error": "unauthorized"}`, 0)
 	defer ts.Close()
 	s.setupRegistryWithTokenService(c, ts.URL)
 	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
 	dockerCmd(c, "tag", "busybox", repoName)
 	out, _, err := dockerCmdWithError("push", repoName)
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), "Retrying")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, !strings.Contains(out, "Retrying"))
 	split := strings.Split(out, "\n")
-	c.Assert(split[len(split)-2], check.Equals, "unauthorized: authentication required")
+	assert.Equal(c, split[len(split)-2], "unauthorized: authentication required")
 }
 
-func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseError(c *check.C) {
+func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseError(c *testing.T) {
 	ts := getTestTokenService(http.StatusTooManyRequests, `{"errors": [{"code":"TOOMANYREQUESTS","message":"out of tokens"}]}`, 3)
 	defer ts.Close()
 	s.setupRegistryWithTokenService(c, ts.URL)
 	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
 	dockerCmd(c, "tag", "busybox", repoName)
 	out, _, err := dockerCmdWithError("push", repoName)
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 	// TODO: isolate test so that it can be guaranteed that the 503 will trigger xfer retries
-	//c.Assert(out, checker.Contains, "Retrying")
-	//c.Assert(out, checker.Not(checker.Contains), "Retrying in 15")
+	//assert.Assert(c, strings.Contains(out, "Retrying"))
+	//assert.Assert(c, !strings.Contains(out, "Retrying in 15"))
 	split := strings.Split(out, "\n")
-	c.Assert(split[len(split)-2], check.Equals, "toomanyrequests: out of tokens")
+	assert.Equal(c, split[len(split)-2], "toomanyrequests: out of tokens")
 }
 
-func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnparsable(c *check.C) {
+func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnparsable(c *testing.T) {
 	ts := getTestTokenService(http.StatusForbidden, `no way`, 0)
 	defer ts.Close()
 	s.setupRegistryWithTokenService(c, ts.URL)
 	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
 	dockerCmd(c, "tag", "busybox", repoName)
 	out, _, err := dockerCmdWithError("push", repoName)
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), "Retrying")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, !strings.Contains(out, "Retrying"))
 	split := strings.Split(out, "\n")
-	c.Assert(split[len(split)-2], checker.Contains, "error parsing HTTP 403 response body: ")
+	assert.Assert(c, strings.Contains(split[len(split)-2], "error parsing HTTP 403 response body: "))
 }
 
-func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseNoToken(c *check.C) {
+func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseNoToken(c *testing.T) {
 	ts := getTestTokenService(http.StatusOK, `{"something": "wrong"}`, 0)
 	defer ts.Close()
 	s.setupRegistryWithTokenService(c, ts.URL)
 	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
 	dockerCmd(c, "tag", "busybox", repoName)
 	out, _, err := dockerCmdWithError("push", repoName)
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), "Retrying")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, !strings.Contains(out, "Retrying"))
 	split := strings.Split(out, "\n")
-	c.Assert(split[len(split)-2], check.Equals, "authorization server did not include a token in the response")
+	assert.Equal(c, split[len(split)-2], "authorization server did not include a token in the response")
 }
diff --git a/integration-cli/docker_cli_registry_user_agent_test.go b/integration-cli/docker_cli_registry_user_agent_test.go
index 7ee3c3d1ba060..13e2c8058e5b6 100644
--- a/integration-cli/docker_cli_registry_user_agent_test.go
+++ b/integration-cli/docker_cli_registry_user_agent_test.go
@@ -2,13 +2,13 @@ package main
 
 import (
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"os"
 	"regexp"
+	"testing"
 
-	"github.com/docker/docker/internal/test/registry"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/registry"
+	"gotest.tools/v3/assert"
 )
 
 // unescapeBackslashSemicolonParens unescapes \;()
@@ -17,36 +17,36 @@ func unescapeBackslashSemicolonParens(s string) string {
 	ret := re.ReplaceAll([]byte(s), []byte(";"))
 
 	re = regexp.MustCompile(`\\\(`)
-	ret = re.ReplaceAll([]byte(ret), []byte("("))
+	ret = re.ReplaceAll(ret, []byte("("))
 
 	re = regexp.MustCompile(`\\\)`)
-	ret = re.ReplaceAll([]byte(ret), []byte(")"))
+	ret = re.ReplaceAll(ret, []byte(")"))
 
 	re = regexp.MustCompile(`\\\\`)
-	ret = re.ReplaceAll([]byte(ret), []byte(`\`))
+	ret = re.ReplaceAll(ret, []byte(`\`))
 
 	return string(ret)
 }
 
-func regexpCheckUA(c *check.C, ua string) {
+func regexpCheckUA(c *testing.T, ua string) {
 	re := regexp.MustCompile("(?P.+) UpstreamClient(?P.+)")
 	substrArr := re.FindStringSubmatch(ua)
 
-	c.Assert(substrArr, check.HasLen, 3, check.Commentf("Expected 'UpstreamClient()' with upstream client UA"))
+	assert.Equal(c, len(substrArr), 3, "Expected 'UpstreamClient()' with upstream client UA")
 	dockerUA := substrArr[1]
 	upstreamUAEscaped := substrArr[2]
 
 	// check dockerUA looks correct
 	reDockerUA := regexp.MustCompile("^docker/[0-9A-Za-z+]")
 	bMatchDockerUA := reDockerUA.MatchString(dockerUA)
-	c.Assert(bMatchDockerUA, check.Equals, true, check.Commentf("Docker Engine User-Agent malformed"))
+	assert.Assert(c, bMatchDockerUA, "Docker Engine User-Agent malformed")
 
 	// check upstreamUA looks correct
 	// Expecting something like:  Docker-Client/1.11.0-dev (linux)
 	upstreamUA := unescapeBackslashSemicolonParens(upstreamUAEscaped)
-	reUpstreamUA := regexp.MustCompile("^\\(Docker-Client/[0-9A-Za-z+]")
+	reUpstreamUA := regexp.MustCompile(`^\(Docker-Client/[0-9A-Za-z+]`)
 	bMatchUpstreamUA := reUpstreamUA.MatchString(upstreamUA)
-	c.Assert(bMatchUpstreamUA, check.Equals, true, check.Commentf("(Upstream) Docker Client User-Agent malformed"))
+	assert.Assert(c, bMatchUpstreamUA, "(Upstream) Docker Client User-Agent malformed")
 }
 
 // registerUserAgentHandler registers a handler for the `/v2/*` endpoint.
@@ -70,23 +70,24 @@ func registerUserAgentHandler(reg *registry.Mock, result *string) {
 // TestUserAgentPassThrough verifies that when an image is pulled from
 // a registry, the registry should see a User-Agent string of the form
 // [docker engine UA] UpstreamClientSTREAM-CLIENT([client UA])
-func (s *DockerRegistrySuite) TestUserAgentPassThrough(c *check.C) {
+func (s *DockerRegistrySuite) TestUserAgentPassThrough(c *testing.T) {
 	var ua string
 
 	reg, err := registry.NewMock(c)
+	assert.NilError(c, err)
 	defer reg.Close()
-	c.Assert(err, check.IsNil)
+
 	registerUserAgentHandler(reg, &ua)
 	repoName := fmt.Sprintf("%s/busybox", reg.URL())
 
 	s.d.StartWithBusybox(c, "--insecure-registry", reg.URL())
 
-	tmp, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, check.IsNil)
+	tmp, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmp)
 
 	dockerfile, err := makefile(tmp, fmt.Sprintf("FROM %s", repoName))
-	c.Assert(err, check.IsNil, check.Commentf("Unable to create test dockerfile"))
+	assert.NilError(c, err, "Unable to create test dockerfile")
 
 	s.d.Cmd("build", "--file", dockerfile, tmp)
 	regexpCheckUA(c, ua)
diff --git a/integration-cli/docker_cli_restart_test.go b/integration-cli/docker_cli_restart_test.go
index 791677e6e0c6d..fc6f4918f1073 100644
--- a/integration-cli/docker_cli_restart_test.go
+++ b/integration-cli/docker_cli_restart_test.go
@@ -4,277 +4,291 @@ import (
 	"os"
 	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
 )
 
-func (s *DockerSuite) TestRestartStoppedContainer(c *check.C) {
+func (s *DockerSuite) TestRestartStoppedContainer(c *testing.T) {
 	dockerCmd(c, "run", "--name=test", "busybox", "echo", "foobar")
 	cleanedContainerID := getIDByName(c, "test")
 
 	out, _ := dockerCmd(c, "logs", cleanedContainerID)
-	c.Assert(out, checker.Equals, "foobar\n")
+	assert.Equal(c, out, "foobar\n")
 
 	dockerCmd(c, "restart", cleanedContainerID)
 
 	// Wait until the container has stopped
 	err := waitInspect(cleanedContainerID, "{{.State.Running}}", "false", 20*time.Second)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _ = dockerCmd(c, "logs", cleanedContainerID)
-	c.Assert(out, checker.Equals, "foobar\nfoobar\n")
+	assert.Equal(c, out, "foobar\nfoobar\n")
 }
 
-func (s *DockerSuite) TestRestartRunningContainer(c *check.C) {
+func (s *DockerSuite) TestRestartRunningContainer(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "echo foobar && sleep 30 && echo 'should not print this'")
 
 	cleanedContainerID := strings.TrimSpace(out)
 
-	c.Assert(waitRun(cleanedContainerID), checker.IsNil)
+	assert.NilError(c, waitRun(cleanedContainerID))
 
-	getLogs := func(c *check.C) (interface{}, check.CommentInterface) {
+	getLogs := func(c *testing.T) (interface{}, string) {
 		out, _ := dockerCmd(c, "logs", cleanedContainerID)
-		return out, nil
+		return out, ""
 	}
 
 	// Wait 10 seconds for the 'echo' to appear in the logs
-	waitAndAssert(c, 10*time.Second, getLogs, checker.Equals, "foobar\n")
+	poll.WaitOn(c, pollCheck(c, getLogs, checker.Equals("foobar\n")), poll.WithTimeout(10*time.Second))
 
 	dockerCmd(c, "restart", "-t", "1", cleanedContainerID)
-	c.Assert(waitRun(cleanedContainerID), checker.IsNil)
+	assert.NilError(c, waitRun(cleanedContainerID))
 
 	// Wait 10 seconds for first 'echo' appear (again) in the logs
-	waitAndAssert(c, 10*time.Second, getLogs, checker.Equals, "foobar\nfoobar\n")
+	poll.WaitOn(c, pollCheck(c, getLogs, checker.Equals("foobar\nfoobar\n")), poll.WithTimeout(10*time.Second))
 }
 
 // Test that restarting a container with a volume does not create a new volume on restart. Regression test for #819.
-func (s *DockerSuite) TestRestartWithVolumes(c *check.C) {
+func (s *DockerSuite) TestRestartWithVolumes(c *testing.T) {
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	out := runSleepingContainer(c, "-d", "-v", prefix+slash+"test")
 
 	cleanedContainerID := strings.TrimSpace(out)
 	out, err := inspectFilter(cleanedContainerID, "len .Mounts")
-	c.Assert(err, check.IsNil, check.Commentf("failed to inspect %s: %s", cleanedContainerID, out))
+	assert.NilError(c, err, "failed to inspect %s: %s", cleanedContainerID, out)
 	out = strings.Trim(out, " \n\r")
-	c.Assert(out, checker.Equals, "1")
+	assert.Equal(c, out, "1")
 
 	source, err := inspectMountSourceField(cleanedContainerID, prefix+slash+"test")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "restart", cleanedContainerID)
 
 	out, err = inspectFilter(cleanedContainerID, "len .Mounts")
-	c.Assert(err, check.IsNil, check.Commentf("failed to inspect %s: %s", cleanedContainerID, out))
+	assert.NilError(c, err, "failed to inspect %s: %s", cleanedContainerID, out)
 	out = strings.Trim(out, " \n\r")
-	c.Assert(out, checker.Equals, "1")
+	assert.Equal(c, out, "1")
 
 	sourceAfterRestart, err := inspectMountSourceField(cleanedContainerID, prefix+slash+"test")
-	c.Assert(err, checker.IsNil)
-	c.Assert(source, checker.Equals, sourceAfterRestart)
+	assert.NilError(c, err)
+	assert.Equal(c, source, sourceAfterRestart)
 }
 
-func (s *DockerSuite) TestRestartDisconnectedContainer(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace, NotArm)
+func (s *DockerSuite) TestRestartDisconnectedContainer(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, NotUserNamespace, NotArm)
 
 	// Run a container on the default bridge network
 	out, _ := dockerCmd(c, "run", "-d", "--name", "c0", "busybox", "top")
 	cleanedContainerID := strings.TrimSpace(out)
-	c.Assert(waitRun(cleanedContainerID), checker.IsNil)
+	assert.NilError(c, waitRun(cleanedContainerID))
 
 	// Disconnect the container from the network
-	out, err := dockerCmd(c, "network", "disconnect", "bridge", "c0")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	out, exitCode := dockerCmd(c, "network", "disconnect", "bridge", "c0")
+	assert.Assert(c, exitCode == 0, out)
 
 	// Restart the container
-	dockerCmd(c, "restart", "c0")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	out, exitCode = dockerCmd(c, "restart", "c0")
+	assert.Assert(c, exitCode == 0, out)
 }
 
-func (s *DockerSuite) TestRestartPolicyNO(c *check.C) {
+func (s *DockerSuite) TestRestartPolicyNO(c *testing.T) {
 	out, _ := dockerCmd(c, "create", "--restart=no", "busybox")
 
-	id := strings.TrimSpace(string(out))
+	id := strings.TrimSpace(out)
 	name := inspectField(c, id, "HostConfig.RestartPolicy.Name")
-	c.Assert(name, checker.Equals, "no")
+	assert.Equal(c, name, "no")
 }
 
-func (s *DockerSuite) TestRestartPolicyAlways(c *check.C) {
+func (s *DockerSuite) TestRestartPolicyAlways(c *testing.T) {
 	out, _ := dockerCmd(c, "create", "--restart=always", "busybox")
 
-	id := strings.TrimSpace(string(out))
+	id := strings.TrimSpace(out)
 	name := inspectField(c, id, "HostConfig.RestartPolicy.Name")
-	c.Assert(name, checker.Equals, "always")
+	assert.Equal(c, name, "always")
 
 	MaximumRetryCount := inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount")
 
 	// MaximumRetryCount=0 if the restart policy is always
-	c.Assert(MaximumRetryCount, checker.Equals, "0")
+	assert.Equal(c, MaximumRetryCount, "0")
 }
 
-func (s *DockerSuite) TestRestartPolicyOnFailure(c *check.C) {
+func (s *DockerSuite) TestRestartPolicyOnFailure(c *testing.T) {
 	out, _, err := dockerCmdWithError("create", "--restart=on-failure:-1", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "maximum retry count cannot be negative")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "maximum retry count cannot be negative"))
 
 	out, _ = dockerCmd(c, "create", "--restart=on-failure:1", "busybox")
 
-	id := strings.TrimSpace(string(out))
+	id := strings.TrimSpace(out)
 	name := inspectField(c, id, "HostConfig.RestartPolicy.Name")
 	maxRetry := inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount")
 
-	c.Assert(name, checker.Equals, "on-failure")
-	c.Assert(maxRetry, checker.Equals, "1")
+	assert.Equal(c, name, "on-failure")
+	assert.Equal(c, maxRetry, "1")
 
 	out, _ = dockerCmd(c, "create", "--restart=on-failure:0", "busybox")
 
-	id = strings.TrimSpace(string(out))
+	id = strings.TrimSpace(out)
 	name = inspectField(c, id, "HostConfig.RestartPolicy.Name")
 	maxRetry = inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount")
 
-	c.Assert(name, checker.Equals, "on-failure")
-	c.Assert(maxRetry, checker.Equals, "0")
+	assert.Equal(c, name, "on-failure")
+	assert.Equal(c, maxRetry, "0")
 
 	out, _ = dockerCmd(c, "create", "--restart=on-failure", "busybox")
 
-	id = strings.TrimSpace(string(out))
+	id = strings.TrimSpace(out)
 	name = inspectField(c, id, "HostConfig.RestartPolicy.Name")
 	maxRetry = inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount")
 
-	c.Assert(name, checker.Equals, "on-failure")
-	c.Assert(maxRetry, checker.Equals, "0")
+	assert.Equal(c, name, "on-failure")
+	assert.Equal(c, maxRetry, "0")
 }
 
 // a good container with --restart=on-failure:3
 // MaximumRetryCount!=0; RestartCount=0
-func (s *DockerSuite) TestRestartContainerwithGoodContainer(c *check.C) {
+func (s *DockerSuite) TestRestartContainerwithGoodContainer(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "--restart=on-failure:3", "busybox", "true")
 
-	id := strings.TrimSpace(string(out))
+	id := strings.TrimSpace(out)
 	err := waitInspect(id, "{{ .State.Restarting }} {{ .State.Running }}", "false false", 30*time.Second)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	count := inspectField(c, id, "RestartCount")
-	c.Assert(count, checker.Equals, "0")
+	assert.Equal(c, count, "0")
 
 	MaximumRetryCount := inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount")
-	c.Assert(MaximumRetryCount, checker.Equals, "3")
-
+	assert.Equal(c, MaximumRetryCount, "3")
 }
 
-func (s *DockerSuite) TestRestartContainerSuccess(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestRestartContainerSuccess(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
+	// Skipped for Hyper-V isolated containers. Test is currently written
+	// such that it assumes there is a host process to kill. In Hyper-V
+	// containers, the process is inside the utility VM, not on the host.
+	if DaemonIsWindows() {
+		testRequires(c, IsolationIsProcess)
+	}
 
 	out := runSleepingContainer(c, "-d", "--restart=always")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), check.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	pidStr := inspectField(c, id, "State.Pid")
 
 	pid, err := strconv.Atoi(pidStr)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	p, err := os.FindProcess(pid)
-	c.Assert(err, check.IsNil)
-	c.Assert(p, check.NotNil)
+	assert.NilError(c, err)
+	assert.Assert(c, p != nil)
 
 	err = p.Kill()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	err = waitInspect(id, "{{.State.Status}}", "running", 30*time.Second)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestRestartWithPolicyUserDefinedNetwork(c *check.C) {
+func (s *DockerSuite) TestRestartWithPolicyUserDefinedNetwork(c *testing.T) {
 	// TODO Windows. This may be portable following HNS integration post TP5.
-	testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace, NotArm)
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, NotUserNamespace, NotArm)
 	dockerCmd(c, "network", "create", "-d", "bridge", "udNet")
 
 	dockerCmd(c, "run", "-d", "--net=udNet", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.NilError(c, waitRun("first"))
 
 	dockerCmd(c, "run", "-d", "--restart=always", "--net=udNet", "--name=second",
 		"--link=first:foo", "busybox", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.NilError(c, waitRun("second"))
 
 	// ping to first and its alias foo must succeed
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// Now kill the second container and let the restart policy kick in
 	pidStr := inspectField(c, "second", "State.Pid")
 
 	pid, err := strconv.Atoi(pidStr)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	p, err := os.FindProcess(pid)
-	c.Assert(err, check.IsNil)
-	c.Assert(p, check.NotNil)
+	assert.NilError(c, err)
+	assert.Assert(c, p != nil)
 
 	err = p.Kill()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	err = waitInspect("second", "{{.RestartCount}}", "1", 5*time.Second)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	err = waitInspect("second", "{{.State.Status}}", "running", 5*time.Second)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// ping to first and its alias foo must still succeed
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestRestartPolicyAfterRestart(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestRestartPolicyAfterRestart(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
+	// Skipped for Hyper-V isolated containers. Test is currently written
+	// such that it assumes there is a host process to kill. In Hyper-V
+	// containers, the process is inside the utility VM, not on the host.
+	if DaemonIsWindows() {
+		testRequires(c, IsolationIsProcess)
+	}
 
 	out := runSleepingContainer(c, "-d", "--restart=always")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), check.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	dockerCmd(c, "restart", id)
 
-	c.Assert(waitRun(id), check.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	pidStr := inspectField(c, id, "State.Pid")
 
 	pid, err := strconv.Atoi(pidStr)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	p, err := os.FindProcess(pid)
-	c.Assert(err, check.IsNil)
-	c.Assert(p, check.NotNil)
+	assert.NilError(c, err)
+	assert.Assert(c, p != nil)
 
 	err = p.Kill()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	err = waitInspect(id, "{{.State.Status}}", "running", 30*time.Second)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestRestartContainerwithRestartPolicy(c *check.C) {
+func (s *DockerSuite) TestRestartContainerwithRestartPolicy(c *testing.T) {
 	out1, _ := dockerCmd(c, "run", "-d", "--restart=on-failure:3", "busybox", "false")
 	out2, _ := dockerCmd(c, "run", "-d", "--restart=always", "busybox", "false")
 
-	id1 := strings.TrimSpace(string(out1))
-	id2 := strings.TrimSpace(string(out2))
+	id1 := strings.TrimSpace(out1)
+	id2 := strings.TrimSpace(out2)
 	waitTimeout := 15 * time.Second
 	if testEnv.OSType == "windows" {
 		waitTimeout = 150 * time.Second
 	}
 	err := waitInspect(id1, "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTimeout)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "restart", id1)
 	dockerCmd(c, "restart", id2)
@@ -285,25 +299,25 @@ func (s *DockerSuite) TestRestartContainerwithRestartPolicy(c *check.C) {
 	dockerCmd(c, "start", id1)
 	dockerCmd(c, "start", id2)
 
-	// Kill the containers, making sure the are stopped at the end of the test
+	// Kill the containers, making sure they are stopped at the end of the test
 	dockerCmd(c, "kill", id1)
 	dockerCmd(c, "kill", id2)
 	err = waitInspect(id1, "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTimeout)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	err = waitInspect(id2, "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTimeout)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestRestartAutoRemoveContainer(c *check.C) {
+func (s *DockerSuite) TestRestartAutoRemoveContainer(c *testing.T) {
 	out := runSleepingContainer(c, "--rm")
 
-	id := strings.TrimSpace(string(out))
+	id := strings.TrimSpace(out)
 	dockerCmd(c, "restart", id)
 	err := waitInspect(id, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 15*time.Second)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _ = dockerCmd(c, "ps")
-	c.Assert(out, checker.Contains, id[:12], check.Commentf("container should be restarted instead of removed: %v", out))
+	assert.Assert(c, is.Contains(out, id[:12]), "container should be restarted instead of removed: %v", out)
 
 	// Kill the container to make sure it will be removed
 	dockerCmd(c, "kill", id)
diff --git a/integration-cli/docker_cli_rmi_test.go b/integration-cli/docker_cli_rmi_test.go
index 662285682327d..f0bb557f9564e 100644
--- a/integration-cli/docker_cli_rmi_test.go
+++ b/integration-cli/docker_cli_rmi_test.go
@@ -3,17 +3,17 @@ package main
 import (
 	"fmt"
 	"strings"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
-func (s *DockerSuite) TestRmiWithContainerFails(c *check.C) {
+func (s *DockerSuite) TestRmiWithContainerFails(c *testing.T) {
 	errSubstr := "is using it"
 
 	// create a container
@@ -24,45 +24,44 @@ func (s *DockerSuite) TestRmiWithContainerFails(c *check.C) {
 	// try to delete the image
 	out, _, err := dockerCmdWithError("rmi", "busybox")
 	// Container is using image, should not be able to rmi
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 	// Container is using image, error message should contain errSubstr
-	c.Assert(out, checker.Contains, errSubstr, check.Commentf("Container: %q", cleanedContainerID))
-
+	assert.Assert(c, strings.Contains(out, errSubstr), "Container: %q", cleanedContainerID)
 	// make sure it didn't delete the busybox name
 	images, _ := dockerCmd(c, "images")
 	// The name 'busybox' should not have been removed from images
-	c.Assert(images, checker.Contains, "busybox")
+	assert.Assert(c, strings.Contains(images, "busybox"))
 }
 
-func (s *DockerSuite) TestRmiTag(c *check.C) {
+func (s *DockerSuite) TestRmiTag(c *testing.T) {
 	imagesBefore, _ := dockerCmd(c, "images", "-a")
 	dockerCmd(c, "tag", "busybox", "utest:tag1")
 	dockerCmd(c, "tag", "busybox", "utest/docker:tag2")
 	dockerCmd(c, "tag", "busybox", "utest:5000/docker:tag3")
 	{
 		imagesAfter, _ := dockerCmd(c, "images", "-a")
-		c.Assert(strings.Count(imagesAfter, "\n"), checker.Equals, strings.Count(imagesBefore, "\n")+3, check.Commentf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
+		assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n")+3, fmt.Sprintf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
 	}
 	dockerCmd(c, "rmi", "utest/docker:tag2")
 	{
 		imagesAfter, _ := dockerCmd(c, "images", "-a")
-		c.Assert(strings.Count(imagesAfter, "\n"), checker.Equals, strings.Count(imagesBefore, "\n")+2, check.Commentf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
+		assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n")+2, fmt.Sprintf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
 	}
 	dockerCmd(c, "rmi", "utest:5000/docker:tag3")
 	{
 		imagesAfter, _ := dockerCmd(c, "images", "-a")
-		c.Assert(strings.Count(imagesAfter, "\n"), checker.Equals, strings.Count(imagesBefore, "\n")+1, check.Commentf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
+		assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n")+1, fmt.Sprintf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
 
 	}
 	dockerCmd(c, "rmi", "utest:tag1")
 	{
 		imagesAfter, _ := dockerCmd(c, "images", "-a")
-		c.Assert(strings.Count(imagesAfter, "\n"), checker.Equals, strings.Count(imagesBefore, "\n"), check.Commentf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
+		assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n"), fmt.Sprintf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
 
 	}
 }
 
-func (s *DockerSuite) TestRmiImgIDMultipleTag(c *check.C) {
+func (s *DockerSuite) TestRmiImgIDMultipleTag(c *testing.T) {
 	out := cli.DockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir '/busybox-one'").Combined()
 	containerID := strings.TrimSpace(out)
 
@@ -80,7 +79,7 @@ func (s *DockerSuite) TestRmiImgIDMultipleTag(c *check.C) {
 
 	imagesAfter := cli.DockerCmd(c, "images", "-a").Combined()
 	// tag busybox to create 2 more images with same imageID
-	c.Assert(strings.Count(imagesAfter, "\n"), checker.Equals, strings.Count(imagesBefore, "\n")+2, check.Commentf("docker images shows: %q\n", imagesAfter))
+	assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n")+2, fmt.Sprintf("docker images shows: %q\n", imagesAfter))
 
 	imgID := inspectField(c, "busybox-one:tag1", "Id")
 
@@ -100,10 +99,10 @@ func (s *DockerSuite) TestRmiImgIDMultipleTag(c *check.C) {
 
 	imagesAfter = cli.DockerCmd(c, "images", "-a").Combined()
 	// rmi -f failed, image still exists
-	c.Assert(imagesAfter, checker.Not(checker.Contains), imgID[:12], check.Commentf("ImageID:%q; ImagesAfter: %q", imgID, imagesAfter))
+	assert.Assert(c, !strings.Contains(imagesAfter, imgID[:12]), "ImageID:%q; ImagesAfter: %q", imgID, imagesAfter)
 }
 
-func (s *DockerSuite) TestRmiImgIDForce(c *check.C) {
+func (s *DockerSuite) TestRmiImgIDForce(c *testing.T) {
 	out := cli.DockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir '/busybox-test'").Combined()
 	containerID := strings.TrimSpace(out)
 
@@ -122,7 +121,7 @@ func (s *DockerSuite) TestRmiImgIDForce(c *check.C) {
 	cli.DockerCmd(c, "tag", "busybox-test", "utest:5000/docker:tag4")
 	{
 		imagesAfter := cli.DockerCmd(c, "images", "-a").Combined()
-		c.Assert(strings.Count(imagesAfter, "\n"), checker.Equals, strings.Count(imagesBefore, "\n")+4, check.Commentf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
+		assert.Equal(c, strings.Count(imagesAfter, "\n"), strings.Count(imagesBefore, "\n")+4, fmt.Sprintf("before: %q\n\nafter: %q\n", imagesBefore, imagesAfter))
 	}
 	imgID := inspectField(c, "busybox-test", "Id")
 
@@ -136,12 +135,12 @@ func (s *DockerSuite) TestRmiImgIDForce(c *check.C) {
 	{
 		imagesAfter := cli.DockerCmd(c, "images", "-a").Combined()
 		// rmi failed, image still exists
-		c.Assert(imagesAfter, checker.Not(checker.Contains), imgID[:12])
+		assert.Assert(c, !strings.Contains(imagesAfter, imgID[:12]))
 	}
 }
 
 // See https://github.com/docker/docker/issues/14116
-func (s *DockerSuite) TestRmiImageIDForceWithRunningContainersAndMultipleTags(c *check.C) {
+func (s *DockerSuite) TestRmiImageIDForceWithRunningContainersAndMultipleTags(c *testing.T) {
 	dockerfile := "FROM busybox\nRUN echo test 14116\n"
 	buildImageSuccessfully(c, "test-14116", build.WithDockerfile(dockerfile))
 	imgID := getIDByName(c, "test-14116")
@@ -152,11 +151,11 @@ func (s *DockerSuite) TestRmiImageIDForceWithRunningContainersAndMultipleTags(c
 
 	out, _, err := dockerCmdWithError("rmi", "-f", imgID)
 	// rmi -f should not delete image with running containers
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "(cannot be forced) - image is being used by running container")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "(cannot be forced) - image is being used by running container"))
 }
 
-func (s *DockerSuite) TestRmiTagWithExistingContainers(c *check.C) {
+func (s *DockerSuite) TestRmiTagWithExistingContainers(c *testing.T) {
 	container := "test-delete-tag"
 	newtag := "busybox:newtag"
 	bb := "busybox:latest"
@@ -165,10 +164,10 @@ func (s *DockerSuite) TestRmiTagWithExistingContainers(c *check.C) {
 	dockerCmd(c, "run", "--name", container, bb, "/bin/true")
 
 	out, _ := dockerCmd(c, "rmi", newtag)
-	c.Assert(strings.Count(out, "Untagged: "), checker.Equals, 1)
+	assert.Equal(c, strings.Count(out, "Untagged: "), 1)
 }
 
-func (s *DockerSuite) TestRmiForceWithExistingContainers(c *check.C) {
+func (s *DockerSuite) TestRmiForceWithExistingContainers(c *testing.T) {
 	image := "busybox-clone"
 
 	icmd.RunCmd(icmd.Cmd{
@@ -182,7 +181,7 @@ MAINTAINER foo`),
 	dockerCmd(c, "rmi", "-f", image)
 }
 
-func (s *DockerSuite) TestRmiWithMultipleRepositories(c *check.C) {
+func (s *DockerSuite) TestRmiWithMultipleRepositories(c *testing.T) {
 	newRepo := "127.0.0.1:5000/busybox"
 	oldRepo := "busybox"
 	newTag := "busybox:test"
@@ -193,10 +192,10 @@ func (s *DockerSuite) TestRmiWithMultipleRepositories(c *check.C) {
 	dockerCmd(c, "commit", "test", newTag)
 
 	out, _ := dockerCmd(c, "rmi", newTag)
-	c.Assert(out, checker.Contains, "Untagged: "+newTag)
+	assert.Assert(c, strings.Contains(out, "Untagged: "+newTag))
 }
 
-func (s *DockerSuite) TestRmiForceWithMultipleRepositories(c *check.C) {
+func (s *DockerSuite) TestRmiForceWithMultipleRepositories(c *testing.T) {
 	imageName := "rmiimage"
 	tag1 := imageName + ":tag1"
 	tag2 := imageName + ":tag2"
@@ -206,25 +205,24 @@ func (s *DockerSuite) TestRmiForceWithMultipleRepositories(c *check.C) {
 	dockerCmd(c, "tag", tag1, tag2)
 
 	out, _ := dockerCmd(c, "rmi", "-f", tag2)
-	c.Assert(out, checker.Contains, "Untagged: "+tag2)
-	c.Assert(out, checker.Not(checker.Contains), "Untagged: "+tag1)
-
+	assert.Assert(c, strings.Contains(out, "Untagged: "+tag2))
+	assert.Assert(c, !strings.Contains(out, "Untagged: "+tag1))
 	// Check built image still exists
 	images, _ := dockerCmd(c, "images", "-a")
-	c.Assert(images, checker.Contains, imageName, check.Commentf("Built image missing %q; Images: %q", imageName, images))
+	assert.Assert(c, strings.Contains(images, imageName), "Built image missing %q; Images: %q", imageName, images)
 }
 
-func (s *DockerSuite) TestRmiBlank(c *check.C) {
+func (s *DockerSuite) TestRmiBlank(c *testing.T) {
 	out, _, err := dockerCmdWithError("rmi", " ")
 	// Should have failed to delete ' ' image
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 	// Wrong error message generated
-	c.Assert(out, checker.Not(checker.Contains), "no such id", check.Commentf("out: %s", out))
+	assert.Assert(c, !strings.Contains(out, "no such id"), "out: %s", out)
 	// Expected error message not generated
-	c.Assert(out, checker.Contains, "image name cannot be blank", check.Commentf("out: %s", out))
+	assert.Assert(c, strings.Contains(out, "image name cannot be blank"), "out: %s", out)
 }
 
-func (s *DockerSuite) TestRmiContainerImageNotFound(c *check.C) {
+func (s *DockerSuite) TestRmiContainerImageNotFound(c *testing.T) {
 	// Build 2 images for testing.
 	imageNames := []string{"test1", "test2"}
 	imageIds := make([]string, 2)
@@ -245,12 +243,12 @@ func (s *DockerSuite) TestRmiContainerImageNotFound(c *check.C) {
 	// Try to remove the image of the running container and see if it fails as expected.
 	out, _, err := dockerCmdWithError("rmi", "-f", imageIds[0])
 	// The image of the running container should not be removed.
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "image is being used by running container", check.Commentf("out: %s", out))
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "image is being used by running container"), "out: %s", out)
 }
 
 // #13422
-func (s *DockerSuite) TestRmiUntagHistoryLayer(c *check.C) {
+func (s *DockerSuite) TestRmiUntagHistoryLayer(c *testing.T) {
 	image := "tmp1"
 	// Build an image for testing.
 	dockerfile := `FROM busybox
@@ -273,7 +271,7 @@ RUN echo 2 #layer2
 	// See if the "tmp2" can be untagged.
 	out, _ = dockerCmd(c, "rmi", newTag)
 	// Expected 1 untagged entry
-	c.Assert(strings.Count(out, "Untagged: "), checker.Equals, 1, check.Commentf("out: %s", out))
+	assert.Equal(c, strings.Count(out, "Untagged: "), 1, fmt.Sprintf("out: %s", out))
 
 	// Now let's add the tag again and create a container based on it.
 	dockerCmd(c, "tag", idToTag, newTag)
@@ -284,30 +282,29 @@ RUN echo 2 #layer2
 	// Try to untag "tmp2" without the -f flag.
 	out, _, err := dockerCmdWithError("rmi", newTag)
 	// should not be untagged without the -f flag
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, cid[:12])
-	c.Assert(out, checker.Contains, "(must force)")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, cid[:12]))
+	assert.Assert(c, strings.Contains(out, "(must force)"))
 	// Add the -f flag and test again.
 	out, _ = dockerCmd(c, "rmi", "-f", newTag)
 	// should be allowed to untag with the -f flag
-	c.Assert(out, checker.Contains, fmt.Sprintf("Untagged: %s:latest", newTag))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("Untagged: %s:latest", newTag)))
 }
 
-func (*DockerSuite) TestRmiParentImageFail(c *check.C) {
+func (*DockerSuite) TestRmiParentImageFail(c *testing.T) {
 	buildImageSuccessfully(c, "test", build.WithDockerfile(`
 	FROM busybox
 	RUN echo hello`))
 
 	id := inspectField(c, "busybox", "ID")
 	out, _, err := dockerCmdWithError("rmi", id)
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	if !strings.Contains(out, "image has dependent child images") {
 		c.Fatalf("rmi should have failed because it's a parent image, got %s", out)
 	}
 }
 
-func (s *DockerSuite) TestRmiWithParentInUse(c *check.C) {
+func (s *DockerSuite) TestRmiWithParentInUse(c *testing.T) {
 	out, _ := dockerCmd(c, "create", "busybox")
 	cID := strings.TrimSpace(out)
 
@@ -324,15 +321,15 @@ func (s *DockerSuite) TestRmiWithParentInUse(c *check.C) {
 }
 
 // #18873
-func (s *DockerSuite) TestRmiByIDHardConflict(c *check.C) {
+func (s *DockerSuite) TestRmiByIDHardConflict(c *testing.T) {
 	dockerCmd(c, "create", "busybox")
 
 	imgID := inspectField(c, "busybox:latest", "Id")
 
 	_, _, err := dockerCmdWithError("rmi", imgID[:12])
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// check that tag was not removed
 	imgID2 := inspectField(c, "busybox:latest", "Id")
-	c.Assert(imgID, checker.Equals, imgID2)
+	assert.Equal(c, imgID, imgID2)
 }
diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go
index 4f55c05aeb31a..7977f73bb2158 100644
--- a/integration-cli/docker_cli_run_test.go
+++ b/integration-cli/docker_cli_run_test.go
@@ -7,7 +7,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net"
 	"os"
 	"os/exec"
@@ -20,27 +19,27 @@ import (
 	"strconv"
 	"strings"
 	"sync"
+	"testing"
 	"time"
 
+	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/docker/docker/internal/test/fakecontext"
-	"github.com/docker/docker/internal/testutil"
-	"github.com/docker/docker/pkg/mount"
-	"github.com/docker/docker/pkg/parsers/kernel"
+	"github.com/docker/docker/libnetwork/resolvconf"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/runconfig"
+	"github.com/docker/docker/testutil"
+	"github.com/docker/docker/testutil/fakecontext"
 	"github.com/docker/go-connections/nat"
-	"github.com/docker/libnetwork/resolvconf"
-	"github.com/docker/libnetwork/types"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"github.com/moby/sys/mountinfo"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/skip"
 )
 
 // "test123" should be printed by docker run
-func (s *DockerSuite) TestRunEchoStdout(c *check.C) {
+func (s *DockerSuite) TestRunEchoStdout(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "busybox", "echo", "test123")
 	if out != "test123\n" {
 		c.Fatalf("container should've printed 'test123', got '%s'", out)
@@ -48,7 +47,7 @@ func (s *DockerSuite) TestRunEchoStdout(c *check.C) {
 }
 
 // "test" should be printed
-func (s *DockerSuite) TestRunEchoNamedContainer(c *check.C) {
+func (s *DockerSuite) TestRunEchoNamedContainer(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "--name", "testfoonamedcontainer", "busybox", "echo", "test")
 	if out != "test\n" {
 		c.Errorf("container should've printed 'test'")
@@ -57,7 +56,7 @@ func (s *DockerSuite) TestRunEchoNamedContainer(c *check.C) {
 
 // docker run should not leak file descriptors. This test relies on Unix
 // specific functionality and cannot run on Windows.
-func (s *DockerSuite) TestRunLeakyFileDescriptors(c *check.C) {
+func (s *DockerSuite) TestRunLeakyFileDescriptors(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "busybox", "ls", "-C", "/proc/self/fd")
 
@@ -69,7 +68,7 @@ func (s *DockerSuite) TestRunLeakyFileDescriptors(c *check.C) {
 
 // it should be possible to lookup Google DNS
 // this will fail when Internet access is unavailable
-func (s *DockerSuite) TestRunLookupGoogleDNS(c *check.C) {
+func (s *DockerSuite) TestRunLookupGoogleDNS(c *testing.T) {
 	testRequires(c, Network, NotArm)
 	if testEnv.OSType == "windows" {
 		// nslookup isn't present in Windows busybox. Is built-in. Further,
@@ -82,19 +81,19 @@ func (s *DockerSuite) TestRunLookupGoogleDNS(c *check.C) {
 }
 
 // the exit code should be 0
-func (s *DockerSuite) TestRunExitCodeZero(c *check.C) {
+func (s *DockerSuite) TestRunExitCodeZero(c *testing.T) {
 	dockerCmd(c, "run", "busybox", "true")
 }
 
 // the exit code should be 1
-func (s *DockerSuite) TestRunExitCodeOne(c *check.C) {
+func (s *DockerSuite) TestRunExitCodeOne(c *testing.T) {
 	_, exitCode, err := dockerCmdWithError("run", "busybox", "false")
-	c.Assert(err, checker.NotNil)
-	c.Assert(exitCode, checker.Equals, 1)
+	assert.ErrorContains(c, err, "")
+	assert.Equal(c, exitCode, 1)
 }
 
 // it should be possible to pipe in data via stdin to a process running in a container
-func (s *DockerSuite) TestRunStdinPipe(c *check.C) {
+func (s *DockerSuite) TestRunStdinPipe(c *testing.T) {
 	// TODO Windows: This needs some work to make compatible.
 	testRequires(c, DaemonIsLinux)
 	result := icmd.RunCmd(icmd.Cmd{
@@ -118,7 +117,7 @@ func (s *DockerSuite) TestRunStdinPipe(c *check.C) {
 }
 
 // the container's ID should be printed when starting a container in detached mode
-func (s *DockerSuite) TestRunDetachedContainerIDPrinting(c *check.C) {
+func (s *DockerSuite) TestRunDetachedContainerIDPrinting(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "true")
 
 	out = strings.TrimSpace(out)
@@ -133,7 +132,7 @@ func (s *DockerSuite) TestRunDetachedContainerIDPrinting(c *check.C) {
 }
 
 // the working directory should be set correctly
-func (s *DockerSuite) TestRunWorkingDirectory(c *check.C) {
+func (s *DockerSuite) TestRunWorkingDirectory(c *testing.T) {
 	dir := "/root"
 	image := "busybox"
 	if testEnv.OSType == "windows" {
@@ -156,7 +155,7 @@ func (s *DockerSuite) TestRunWorkingDirectory(c *check.C) {
 }
 
 // pinging Google's DNS resolver should fail when we disable the networking
-func (s *DockerSuite) TestRunWithoutNetworking(c *check.C) {
+func (s *DockerSuite) TestRunWithoutNetworking(c *testing.T) {
 	count := "-c"
 	image := "busybox"
 	if testEnv.OSType == "windows" {
@@ -174,8 +173,8 @@ func (s *DockerSuite) TestRunWithoutNetworking(c *check.C) {
 	}
 }
 
-//test --link use container name to link target
-func (s *DockerSuite) TestRunLinksContainerWithContainerName(c *check.C) {
+// test --link use container name to link target
+func (s *DockerSuite) TestRunLinksContainerWithContainerName(c *testing.T) {
 	// TODO Windows: This test cannot run on a Windows daemon as the networking
 	// settings are not populated back yet on inspect.
 	testRequires(c, DaemonIsLinux)
@@ -189,8 +188,8 @@ func (s *DockerSuite) TestRunLinksContainerWithContainerName(c *check.C) {
 	}
 }
 
-//test --link use container id to link target
-func (s *DockerSuite) TestRunLinksContainerWithContainerID(c *check.C) {
+// test --link use container id to link target
+func (s *DockerSuite) TestRunLinksContainerWithContainerID(c *testing.T) {
 	// TODO Windows: This test cannot run on a Windows daemon as the networking
 	// settings are not populated back yet on inspect.
 	testRequires(c, DaemonIsLinux)
@@ -205,147 +204,145 @@ func (s *DockerSuite) TestRunLinksContainerWithContainerID(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestUserDefinedNetworkLinks(c *check.C) {
+func (s *DockerSuite) TestUserDefinedNetworkLinks(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	dockerCmd(c, "network", "create", "-d", "bridge", "udlinkNet")
 
 	dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 
 	// run a container in user-defined network udlinkNet with a link for an existing container
 	// and a link for a container that doesn't exist
 	dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=second", "--link=first:foo",
 		"--link=third:bar", "busybox", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 
 	// ping to first and its alias foo must succeed
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// ping to third and its alias must fail
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "third")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// start third container now
 	dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=third", "busybox", "top")
-	c.Assert(waitRun("third"), check.IsNil)
+	assert.Assert(c, waitRun("third") == nil)
 
 	// ping to third and its alias must succeed now
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "third")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "bar")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestUserDefinedNetworkLinksWithRestart(c *check.C) {
+func (s *DockerSuite) TestUserDefinedNetworkLinksWithRestart(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	dockerCmd(c, "network", "create", "-d", "bridge", "udlinkNet")
 
 	dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 
 	dockerCmd(c, "run", "-d", "--net=udlinkNet", "--name=second", "--link=first:foo",
 		"busybox", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 
 	// ping to first and its alias foo must succeed
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// Restart first container
 	dockerCmd(c, "restart", "first")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 
 	// ping to first and its alias foo must still succeed
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// Restart second container
 	dockerCmd(c, "restart", "second")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 
 	// ping to first and its alias foo must still succeed
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSuite) TestRunWithNetAliasOnDefaultNetworks(c *check.C) {
+func (s *DockerSuite) TestRunWithNetAliasOnDefaultNetworks(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 
 	defaults := []string{"bridge", "host", "none"}
 	for _, net := range defaults {
 		out, _, err := dockerCmdWithError("run", "-d", "--net", net, "--net-alias", "alias_"+net, "busybox", "top")
-		c.Assert(err, checker.NotNil)
-		c.Assert(out, checker.Contains, runconfig.ErrUnsupportedNetworkAndAlias.Error())
+		assert.ErrorContains(c, err, "")
+		assert.Assert(c, strings.Contains(out, runconfig.ErrUnsupportedNetworkAndAlias.Error()))
 	}
 }
 
-func (s *DockerSuite) TestUserDefinedNetworkAlias(c *check.C) {
+func (s *DockerSuite) TestUserDefinedNetworkAlias(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	dockerCmd(c, "network", "create", "-d", "bridge", "net1")
 
 	cid1, _ := dockerCmd(c, "run", "-d", "--net=net1", "--name=first", "--net-alias=foo1", "--net-alias=foo2", "busybox:glibc", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 
 	// Check if default short-id alias is added automatically
 	id := strings.TrimSpace(cid1)
 	aliases := inspectField(c, id, "NetworkSettings.Networks.net1.Aliases")
-	c.Assert(aliases, checker.Contains, stringid.TruncateID(id))
-
+	assert.Assert(c, strings.Contains(aliases, stringid.TruncateID(id)))
 	cid2, _ := dockerCmd(c, "run", "-d", "--net=net1", "--name=second", "busybox:glibc", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 
 	// Check if default short-id alias is added automatically
 	id = strings.TrimSpace(cid2)
 	aliases = inspectField(c, id, "NetworkSettings.Networks.net1.Aliases")
-	c.Assert(aliases, checker.Contains, stringid.TruncateID(id))
-
+	assert.Assert(c, strings.Contains(aliases, stringid.TruncateID(id)))
 	// ping to first and its network-scoped aliases
 	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo1")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo2")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	// ping first container's short-id alias
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", stringid.TruncateID(cid1))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// Restart first container
 	dockerCmd(c, "restart", "first")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 
 	// ping to first and its network-scoped aliases must succeed
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo1")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo2")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	// ping first container's short-id alias
 	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", stringid.TruncateID(cid1))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
 // Issue 9677.
-func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) {
+func (s *DockerSuite) TestRunWithDaemonFlags(c *testing.T) {
 	out, _, err := dockerCmdWithError("--exec-opt", "foo=bar", "run", "-i", "busybox", "true")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "unknown flag: --exec-opt")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "unknown flag: --exec-opt"))
 }
 
 // Regression test for #4979
-func (s *DockerSuite) TestRunWithVolumesFromExited(c *check.C) {
+func (s *DockerSuite) TestRunWithVolumesFromExited(c *testing.T) {
 
 	var (
 		out      string
@@ -375,7 +372,7 @@ func (s *DockerSuite) TestRunWithVolumesFromExited(c *check.C) {
 
 // Volume path is a symlink which also exists on the host, and the host side is a file not a dir
 // But the volume call is just a normal volume, not a bind mount
-func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir(c *check.C) {
+func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir(c *testing.T) {
 	var (
 		dockerFile    string
 		containerPath string
@@ -383,10 +380,10 @@ func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir(c *check.C) {
 	)
 	// This test cannot run on a Windows daemon as
 	// Windows does not support symlinks inside a volume path
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 	name := "test-volume-symlink"
 
-	dir, err := ioutil.TempDir("", name)
+	dir, err := os.MkdirTemp("", name)
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -419,7 +416,7 @@ func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir(c *check.C) {
 }
 
 // Volume path is a symlink in the container
-func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir2(c *check.C) {
+func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir2(c *testing.T) {
 	var (
 		dockerFile    string
 		containerPath string
@@ -427,7 +424,7 @@ func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir2(c *check.C) {
 	)
 	// This test cannot run on a Windows daemon as
 	// Windows does not support symlinks inside a volume path
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 	name := "test-volume-symlink2"
 
 	if testEnv.OSType == "windows" {
@@ -443,13 +440,13 @@ func (s *DockerSuite) TestRunCreateVolumesInSymlinkDir2(c *check.C) {
 	dockerCmd(c, "run", "-v", containerPath, name, cmd)
 }
 
-func (s *DockerSuite) TestRunVolumesMountedAsReadonly(c *check.C) {
+func (s *DockerSuite) TestRunVolumesMountedAsReadonly(c *testing.T) {
 	if _, code, err := dockerCmdWithError("run", "-v", "/test:/test:ro", "busybox", "touch", "/test/somefile"); err == nil || code == 0 {
 		c.Fatalf("run should fail because volume is ro: exit code %d", code)
 	}
 }
 
-func (s *DockerSuite) TestRunVolumesFromInReadonlyModeFails(c *check.C) {
+func (s *DockerSuite) TestRunVolumesFromInReadonlyModeFails(c *testing.T) {
 	var (
 		volumeDir string
 		fileInVol string
@@ -470,7 +467,7 @@ func (s *DockerSuite) TestRunVolumesFromInReadonlyModeFails(c *check.C) {
 }
 
 // Regression test for #1201
-func (s *DockerSuite) TestRunVolumesFromInReadWriteMode(c *check.C) {
+func (s *DockerSuite) TestRunVolumesFromInReadWriteMode(c *testing.T) {
 	var (
 		volumeDir string
 		fileInVol string
@@ -493,8 +490,8 @@ func (s *DockerSuite) TestRunVolumesFromInReadWriteMode(c *check.C) {
 	dockerCmd(c, "run", "--volumes-from", "parent", "busybox", "touch", fileInVol)
 }
 
-func (s *DockerSuite) TestVolumesFromGetsProperMode(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestVolumesFromGetsProperMode(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	hostpath := RandomTmpDirPath("test", testEnv.OSType)
 	if err := os.MkdirAll(hostpath, 0755); err != nil {
@@ -518,14 +515,14 @@ func (s *DockerSuite) TestVolumesFromGetsProperMode(c *check.C) {
 }
 
 // Test for GH#10618
-func (s *DockerSuite) TestRunNoDupVolumes(c *check.C) {
+func (s *DockerSuite) TestRunNoDupVolumes(c *testing.T) {
 	path1 := RandomTmpDirPath("test1", testEnv.OSType)
 	path2 := RandomTmpDirPath("test2", testEnv.OSType)
 
 	someplace := ":/someplace"
 	if testEnv.OSType == "windows" {
 		// Windows requires that the source directory exists before calling HCS
-		testRequires(c, SameHostDaemon)
+		testRequires(c, testEnv.IsLocalDaemon)
 		someplace = `:c:\someplace`
 		if err := os.MkdirAll(path1, 0755); err != nil {
 			c.Fatalf("Failed to create %s: %q", path1, err)
@@ -570,7 +567,7 @@ func (s *DockerSuite) TestRunNoDupVolumes(c *check.C) {
 }
 
 // Test for #1351
-func (s *DockerSuite) TestRunApplyVolumesFromBeforeVolumes(c *check.C) {
+func (s *DockerSuite) TestRunApplyVolumesFromBeforeVolumes(c *testing.T) {
 	prefix := ""
 	if testEnv.OSType == "windows" {
 		prefix = `c:`
@@ -579,7 +576,7 @@ func (s *DockerSuite) TestRunApplyVolumesFromBeforeVolumes(c *check.C) {
 	dockerCmd(c, "run", "--volumes-from", "parent", "-v", prefix+"/test", "busybox", "cat", prefix+"/test/foo")
 }
 
-func (s *DockerSuite) TestRunMultipleVolumesFrom(c *check.C) {
+func (s *DockerSuite) TestRunMultipleVolumesFrom(c *testing.T) {
 	prefix := ""
 	if testEnv.OSType == "windows" {
 		prefix = `c:`
@@ -590,7 +587,7 @@ func (s *DockerSuite) TestRunMultipleVolumesFrom(c *check.C) {
 }
 
 // this tests verifies the ID format for the container
-func (s *DockerSuite) TestRunVerifyContainerID(c *check.C) {
+func (s *DockerSuite) TestRunVerifyContainerID(c *testing.T) {
 	out, exit, err := dockerCmdWithError("run", "-d", "busybox", "true")
 	if err != nil {
 		c.Fatal(err)
@@ -609,7 +606,7 @@ func (s *DockerSuite) TestRunVerifyContainerID(c *check.C) {
 }
 
 // Test that creating a container with a volume doesn't crash. Regression test for #995.
-func (s *DockerSuite) TestRunCreateVolume(c *check.C) {
+func (s *DockerSuite) TestRunCreateVolume(c *testing.T) {
 	prefix := ""
 	if testEnv.OSType == "windows" {
 		prefix = `c:`
@@ -619,11 +616,11 @@ func (s *DockerSuite) TestRunCreateVolume(c *check.C) {
 
 // Test that creating a volume with a symlink in its path works correctly. Test for #5152.
 // Note that this bug happens only with symlinks with a target that starts with '/'.
-func (s *DockerSuite) TestRunCreateVolumeWithSymlink(c *check.C) {
+func (s *DockerSuite) TestRunCreateVolumeWithSymlink(c *testing.T) {
 	// Cannot run on Windows as relies on Linux-specific functionality (sh -c mount...)
 	testRequires(c, DaemonIsLinux)
-	workingDirectory, err := ioutil.TempDir("", "TestRunCreateVolumeWithSymlink")
-	c.Assert(err, checker.IsNil)
+	workingDirectory, err := os.MkdirTemp("", "TestRunCreateVolumeWithSymlink")
+	assert.NilError(c, err)
 	image := "docker-test-createvolumewithsymlink"
 
 	buildCmd := exec.Command(dockerBinary, "build", "-t", image, "-")
@@ -641,7 +638,7 @@ func (s *DockerSuite) TestRunCreateVolumeWithSymlink(c *check.C) {
 	}
 
 	volPath, err := inspectMountSourceField("test-createvolumewithsymlink", "/bar/foo")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	_, exitCode, err = dockerCmdWithError("rm", "-v", "test-createvolumewithsymlink")
 	if err != nil || exitCode != 0 {
@@ -655,13 +652,13 @@ func (s *DockerSuite) TestRunCreateVolumeWithSymlink(c *check.C) {
 }
 
 // Tests that a volume path that has a symlink exists in a container mounting it with `--volumes-from`.
-func (s *DockerSuite) TestRunVolumesFromSymlinkPath(c *check.C) {
+func (s *DockerSuite) TestRunVolumesFromSymlinkPath(c *testing.T) {
 	// This test cannot run on a Windows daemon as
 	// Windows does not support symlinks inside a volume path
 	testRequires(c, DaemonIsLinux)
 
-	workingDirectory, err := ioutil.TempDir("", "TestRunVolumesFromSymlinkPath")
-	c.Assert(err, checker.IsNil)
+	workingDirectory, err := os.MkdirTemp("", "TestRunVolumesFromSymlinkPath")
+	assert.NilError(c, err)
 	name := "docker-test-volumesfromsymlinkpath"
 	prefix := ""
 	dfContents := `FROM busybox
@@ -696,7 +693,7 @@ func (s *DockerSuite) TestRunVolumesFromSymlinkPath(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunExitCode(c *check.C) {
+func (s *DockerSuite) TestRunExitCode(c *testing.T) {
 	var (
 		exit int
 		err  error
@@ -712,10 +709,10 @@ func (s *DockerSuite) TestRunExitCode(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunUserDefaults(c *check.C) {
+func (s *DockerSuite) TestRunUserDefaults(c *testing.T) {
 	expected := "uid=0(root) gid=0(root)"
 	if testEnv.OSType == "windows" {
-		expected = "uid=1000(ContainerAdministrator) gid=1000(ContainerAdministrator)"
+		expected = "uid=0(root) gid=0(root) groups=0(root)"
 	}
 	out, _ := dockerCmd(c, "run", "busybox", "id")
 	if !strings.Contains(out, expected) {
@@ -723,7 +720,7 @@ func (s *DockerSuite) TestRunUserDefaults(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunUserByName(c *check.C) {
+func (s *DockerSuite) TestRunUserByName(c *testing.T) {
 	// TODO Windows: This test cannot run on a Windows daemon as Windows does
 	// not support the use of -u
 	testRequires(c, DaemonIsLinux)
@@ -733,7 +730,7 @@ func (s *DockerSuite) TestRunUserByName(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunUserByID(c *check.C) {
+func (s *DockerSuite) TestRunUserByID(c *testing.T) {
 	// TODO Windows: This test cannot run on a Windows daemon as Windows does
 	// not support the use of -u
 	testRequires(c, DaemonIsLinux)
@@ -743,7 +740,7 @@ func (s *DockerSuite) TestRunUserByID(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunUserByIDBig(c *check.C) {
+func (s *DockerSuite) TestRunUserByIDBig(c *testing.T) {
 	// TODO Windows: This test cannot run on a Windows daemon as Windows does
 	// not support the use of -u
 	testRequires(c, DaemonIsLinux, NotArm)
@@ -756,7 +753,7 @@ func (s *DockerSuite) TestRunUserByIDBig(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunUserByIDNegative(c *check.C) {
+func (s *DockerSuite) TestRunUserByIDNegative(c *testing.T) {
 	// TODO Windows: This test cannot run on a Windows daemon as Windows does
 	// not support the use of -u
 	testRequires(c, DaemonIsLinux)
@@ -769,7 +766,7 @@ func (s *DockerSuite) TestRunUserByIDNegative(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunUserByIDZero(c *check.C) {
+func (s *DockerSuite) TestRunUserByIDZero(c *testing.T) {
 	// TODO Windows: This test cannot run on a Windows daemon as Windows does
 	// not support the use of -u
 	testRequires(c, DaemonIsLinux)
@@ -782,7 +779,7 @@ func (s *DockerSuite) TestRunUserByIDZero(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunUserNotFound(c *check.C) {
+func (s *DockerSuite) TestRunUserNotFound(c *testing.T) {
 	// TODO Windows: This test cannot run on a Windows daemon as Windows does
 	// not support the use of -u
 	testRequires(c, DaemonIsLinux)
@@ -792,7 +789,7 @@ func (s *DockerSuite) TestRunUserNotFound(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunTwoConcurrentContainers(c *check.C) {
+func (s *DockerSuite) TestRunTwoConcurrentContainers(c *testing.T) {
 	sleepTime := "2"
 	group := sync.WaitGroup{}
 	group.Add(2)
@@ -810,11 +807,11 @@ func (s *DockerSuite) TestRunTwoConcurrentContainers(c *check.C) {
 	close(errChan)
 
 	for err := range errChan {
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	}
 }
 
-func (s *DockerSuite) TestRunEnvironment(c *check.C) {
+func (s *DockerSuite) TestRunEnvironment(c *testing.T) {
 	// TODO Windows: Environment handling is different between Linux and
 	// Windows and this test relies currently on unix functionality.
 	testRequires(c, DaemonIsLinux)
@@ -853,7 +850,7 @@ func (s *DockerSuite) TestRunEnvironment(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunEnvironmentErase(c *check.C) {
+func (s *DockerSuite) TestRunEnvironmentErase(c *testing.T) {
 	// TODO Windows: Environment handling is different between Linux and
 	// Windows and this test relies currently on unix functionality.
 	testRequires(c, DaemonIsLinux)
@@ -886,7 +883,7 @@ func (s *DockerSuite) TestRunEnvironmentErase(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunEnvironmentOverride(c *check.C) {
+func (s *DockerSuite) TestRunEnvironmentOverride(c *testing.T) {
 	// TODO Windows: Environment handling is different between Linux and
 	// Windows and this test relies currently on unix functionality.
 	testRequires(c, DaemonIsLinux)
@@ -919,7 +916,7 @@ func (s *DockerSuite) TestRunEnvironmentOverride(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunContainerNetwork(c *check.C) {
+func (s *DockerSuite) TestRunContainerNetwork(c *testing.T) {
 	if testEnv.OSType == "windows" {
 		// Windows busybox does not have ping. Use built in ping instead.
 		dockerCmd(c, "run", testEnv.PlatformDefaults.BaseImage, "ping", "-n", "1", "127.0.0.1")
@@ -928,7 +925,7 @@ func (s *DockerSuite) TestRunContainerNetwork(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunNetHostNotAllowedWithLinks(c *check.C) {
+func (s *DockerSuite) TestRunNetHostNotAllowedWithLinks(c *testing.T) {
 	// TODO Windows: This is Linux specific as --link is not supported and
 	// this will be deprecated in favor of container networking model.
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
@@ -945,7 +942,7 @@ func (s *DockerSuite) TestRunNetHostNotAllowedWithLinks(c *check.C) {
 // and use "--net=host" (as the original issue submitter did), as the same
 // codepath is executed with "docker run -h ".  Both were manually
 // tested, but this testcase takes the simpler path of using "run -h .."
-func (s *DockerSuite) TestRunFullHostnameSet(c *check.C) {
+func (s *DockerSuite) TestRunFullHostnameSet(c *testing.T) {
 	// TODO Windows: -h is not yet functional.
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-h", "foo.bar.baz", "busybox", "hostname")
@@ -954,7 +951,7 @@ func (s *DockerSuite) TestRunFullHostnameSet(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunPrivilegedCanMknod(c *check.C) {
+func (s *DockerSuite) TestRunPrivilegedCanMknod(c *testing.T) {
 	// Not applicable for Windows as Windows daemon does not support
 	// the concept of --privileged, and mknod is a Unix concept.
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
@@ -964,7 +961,7 @@ func (s *DockerSuite) TestRunPrivilegedCanMknod(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunUnprivilegedCanMknod(c *check.C) {
+func (s *DockerSuite) TestRunUnprivilegedCanMknod(c *testing.T) {
 	// Not applicable for Windows as Windows daemon does not support
 	// the concept of --privileged, and mknod is a Unix concept.
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
@@ -974,7 +971,7 @@ func (s *DockerSuite) TestRunUnprivilegedCanMknod(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapDropInvalid(c *check.C) {
+func (s *DockerSuite) TestRunCapDropInvalid(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --cap-drop
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "--cap-drop=CHPASS", "busybox", "ls")
@@ -983,7 +980,7 @@ func (s *DockerSuite) TestRunCapDropInvalid(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapDropCannotMknod(c *check.C) {
+func (s *DockerSuite) TestRunCapDropCannotMknod(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --cap-drop or mknod
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "--cap-drop=MKNOD", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
@@ -996,7 +993,7 @@ func (s *DockerSuite) TestRunCapDropCannotMknod(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapDropCannotMknodLowerCase(c *check.C) {
+func (s *DockerSuite) TestRunCapDropCannotMknodLowerCase(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --cap-drop or mknod
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "--cap-drop=mknod", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
@@ -1009,7 +1006,7 @@ func (s *DockerSuite) TestRunCapDropCannotMknodLowerCase(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapDropALLCannotMknod(c *check.C) {
+func (s *DockerSuite) TestRunCapDropALLCannotMknod(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --cap-drop or mknod
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "--cap-drop=ALL", "--cap-add=SETGID", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
@@ -1021,7 +1018,7 @@ func (s *DockerSuite) TestRunCapDropALLCannotMknod(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapDropALLAddMknodCanMknod(c *check.C) {
+func (s *DockerSuite) TestRunCapDropALLAddMknodCanMknod(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --cap-drop or mknod
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	out, _ := dockerCmd(c, "run", "--cap-drop=ALL", "--cap-add=MKNOD", "--cap-add=SETGID", "busybox", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
@@ -1031,7 +1028,7 @@ func (s *DockerSuite) TestRunCapDropALLAddMknodCanMknod(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapAddInvalid(c *check.C) {
+func (s *DockerSuite) TestRunCapAddInvalid(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --cap-add
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "--cap-add=CHPASS", "busybox", "ls")
@@ -1040,7 +1037,7 @@ func (s *DockerSuite) TestRunCapAddInvalid(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapAddCanDownInterface(c *check.C) {
+func (s *DockerSuite) TestRunCapAddCanDownInterface(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --cap-add
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "--cap-add=NET_ADMIN", "busybox", "sh", "-c", "ip link set eth0 down && echo ok")
@@ -1050,7 +1047,7 @@ func (s *DockerSuite) TestRunCapAddCanDownInterface(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapAddALLCanDownInterface(c *check.C) {
+func (s *DockerSuite) TestRunCapAddALLCanDownInterface(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --cap-add
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "--cap-add=ALL", "busybox", "sh", "-c", "ip link set eth0 down && echo ok")
@@ -1060,7 +1057,7 @@ func (s *DockerSuite) TestRunCapAddALLCanDownInterface(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapAddALLDropNetAdminCanDownInterface(c *check.C) {
+func (s *DockerSuite) TestRunCapAddALLDropNetAdminCanDownInterface(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --cap-add
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "--cap-add=ALL", "--cap-drop=NET_ADMIN", "busybox", "sh", "-c", "ip link set eth0 down && echo ok")
@@ -1072,7 +1069,7 @@ func (s *DockerSuite) TestRunCapAddALLDropNetAdminCanDownInterface(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunGroupAdd(c *check.C) {
+func (s *DockerSuite) TestRunGroupAdd(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --group-add
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "--group-add=audio", "--group-add=staff", "--group-add=777", "busybox", "sh", "-c", "id")
@@ -1083,7 +1080,7 @@ func (s *DockerSuite) TestRunGroupAdd(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunPrivilegedCanMount(c *check.C) {
+func (s *DockerSuite) TestRunPrivilegedCanMount(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --privileged
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	out, _ := dockerCmd(c, "run", "--privileged", "busybox", "sh", "-c", "mount -t tmpfs none /tmp && echo ok")
@@ -1093,7 +1090,7 @@ func (s *DockerSuite) TestRunPrivilegedCanMount(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunUnprivilegedCannotMount(c *check.C) {
+func (s *DockerSuite) TestRunUnprivilegedCannotMount(c *testing.T) {
 	// Not applicable for Windows as there is no concept of unprivileged
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "busybox", "sh", "-c", "mount -t tmpfs none /tmp && echo ok")
@@ -1106,7 +1103,7 @@ func (s *DockerSuite) TestRunUnprivilegedCannotMount(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunSysNotWritableInNonPrivilegedContainers(c *check.C) {
+func (s *DockerSuite) TestRunSysNotWritableInNonPrivilegedContainers(c *testing.T) {
 	// Not applicable for Windows as there is no concept of unprivileged
 	testRequires(c, DaemonIsLinux, NotArm)
 	if _, code, err := dockerCmdWithError("run", "busybox", "touch", "/sys/kernel/profiling"); err == nil || code == 0 {
@@ -1114,7 +1111,7 @@ func (s *DockerSuite) TestRunSysNotWritableInNonPrivilegedContainers(c *check.C)
 	}
 }
 
-func (s *DockerSuite) TestRunSysWritableInPrivilegedContainers(c *check.C) {
+func (s *DockerSuite) TestRunSysWritableInPrivilegedContainers(c *testing.T) {
 	// Not applicable for Windows as there is no concept of unprivileged
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	if _, code, err := dockerCmdWithError("run", "--privileged", "busybox", "touch", "/sys/kernel/profiling"); err != nil || code != 0 {
@@ -1122,7 +1119,7 @@ func (s *DockerSuite) TestRunSysWritableInPrivilegedContainers(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunProcNotWritableInNonPrivilegedContainers(c *check.C) {
+func (s *DockerSuite) TestRunProcNotWritableInNonPrivilegedContainers(c *testing.T) {
 	// Not applicable for Windows as there is no concept of unprivileged
 	testRequires(c, DaemonIsLinux)
 	if _, code, err := dockerCmdWithError("run", "busybox", "touch", "/proc/sysrq-trigger"); err == nil || code == 0 {
@@ -1130,7 +1127,7 @@ func (s *DockerSuite) TestRunProcNotWritableInNonPrivilegedContainers(c *check.C
 	}
 }
 
-func (s *DockerSuite) TestRunProcWritableInPrivilegedContainers(c *check.C) {
+func (s *DockerSuite) TestRunProcWritableInPrivilegedContainers(c *testing.T) {
 	// Not applicable for Windows as there is no concept of --privileged
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	if _, code := dockerCmd(c, "run", "--privileged", "busybox", "sh", "-c", "touch /proc/sysrq-trigger"); code != 0 {
@@ -1138,7 +1135,7 @@ func (s *DockerSuite) TestRunProcWritableInPrivilegedContainers(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunDeviceNumbers(c *check.C) {
+func (s *DockerSuite) TestRunDeviceNumbers(c *testing.T) {
 	// Not applicable on Windows as /dev/ is a Unix specific concept
 	// TODO: NotUserNamespace could be removed here if "root" "root" is replaced w user
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
@@ -1154,7 +1151,7 @@ func (s *DockerSuite) TestRunDeviceNumbers(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunThatCharacterDevicesActLikeCharacterDevices(c *check.C) {
+func (s *DockerSuite) TestRunThatCharacterDevicesActLikeCharacterDevices(c *testing.T) {
 	// Not applicable on Windows as /dev/ is a Unix specific concept
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "busybox", "sh", "-c", "dd if=/dev/zero of=/zero bs=1k count=5 2> /dev/null ; du -h /zero")
@@ -1163,13 +1160,13 @@ func (s *DockerSuite) TestRunThatCharacterDevicesActLikeCharacterDevices(c *chec
 	}
 }
 
-func (s *DockerSuite) TestRunUnprivilegedWithChroot(c *check.C) {
+func (s *DockerSuite) TestRunUnprivilegedWithChroot(c *testing.T) {
 	// Not applicable on Windows as it does not support chroot
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "busybox", "chroot", "/", "true")
 }
 
-func (s *DockerSuite) TestRunAddingOptionalDevices(c *check.C) {
+func (s *DockerSuite) TestRunAddingOptionalDevices(c *testing.T) {
 	// Not applicable on Windows as Windows does not support --device
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	out, _ := dockerCmd(c, "run", "--device", "/dev/zero:/dev/nulo", "busybox", "sh", "-c", "ls /dev/nulo")
@@ -1178,7 +1175,7 @@ func (s *DockerSuite) TestRunAddingOptionalDevices(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunAddingOptionalDevicesNoSrc(c *check.C) {
+func (s *DockerSuite) TestRunAddingOptionalDevicesNoSrc(c *testing.T) {
 	// Not applicable on Windows as Windows does not support --device
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	out, _ := dockerCmd(c, "run", "--device", "/dev/zero:rw", "busybox", "sh", "-c", "ls /dev/zero")
@@ -1187,7 +1184,7 @@ func (s *DockerSuite) TestRunAddingOptionalDevicesNoSrc(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunAddingOptionalDevicesInvalidMode(c *check.C) {
+func (s *DockerSuite) TestRunAddingOptionalDevicesInvalidMode(c *testing.T) {
 	// Not applicable on Windows as Windows does not support --device
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	_, _, err := dockerCmdWithError("run", "--device", "/dev/zero:ro", "busybox", "sh", "-c", "ls /dev/zero")
@@ -1196,9 +1193,9 @@ func (s *DockerSuite) TestRunAddingOptionalDevicesInvalidMode(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunModeHostname(c *check.C) {
+func (s *DockerSuite) TestRunModeHostname(c *testing.T) {
 	// Not applicable on Windows as Windows does not support -h
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
 
 	out, _ := dockerCmd(c, "run", "-h=testhostname", "busybox", "cat", "/etc/hostname")
 
@@ -1217,7 +1214,7 @@ func (s *DockerSuite) TestRunModeHostname(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunRootWorkdir(c *check.C) {
+func (s *DockerSuite) TestRunRootWorkdir(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "--workdir", "/", "busybox", "pwd")
 	expected := "/\n"
 	if testEnv.OSType == "windows" {
@@ -1228,7 +1225,7 @@ func (s *DockerSuite) TestRunRootWorkdir(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunAllowBindMountingRoot(c *check.C) {
+func (s *DockerSuite) TestRunAllowBindMountingRoot(c *testing.T) {
 	if testEnv.OSType == "windows" {
 		// Windows busybox will fail with Permission Denied on items such as pagefile.sys
 		dockerCmd(c, "run", "-v", `c:\:c:\host`, testEnv.PlatformDefaults.BaseImage, "cmd", "-c", "dir", `c:\host`)
@@ -1237,7 +1234,7 @@ func (s *DockerSuite) TestRunAllowBindMountingRoot(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunDisallowBindMountingRootToRoot(c *check.C) {
+func (s *DockerSuite) TestRunDisallowBindMountingRootToRoot(c *testing.T) {
 	mount := "/:/"
 	targetDir := "/host"
 	if testEnv.OSType == "windows" {
@@ -1251,18 +1248,18 @@ func (s *DockerSuite) TestRunDisallowBindMountingRootToRoot(c *check.C) {
 }
 
 // Verify that a container gets default DNS when only localhost resolvers exist
-func (s *DockerSuite) TestRunDNSDefaultOptions(c *check.C) {
+func (s *DockerSuite) TestRunDNSDefaultOptions(c *testing.T) {
 	// Not applicable on Windows as this is testing Unix specific functionality
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	// preserve original resolv.conf for restoring after test
-	origResolvConf, err := ioutil.ReadFile("/etc/resolv.conf")
+	origResolvConf, err := os.ReadFile("/etc/resolv.conf")
 	if os.IsNotExist(err) {
 		c.Fatalf("/etc/resolv.conf does not exist")
 	}
 	// defer restored original conf
 	defer func() {
-		if err := ioutil.WriteFile("/etc/resolv.conf", origResolvConf, 0644); err != nil {
+		if err := os.WriteFile("/etc/resolv.conf", origResolvConf, 0644); err != nil {
 			c.Fatal(err)
 		}
 	}()
@@ -1271,7 +1268,7 @@ func (s *DockerSuite) TestRunDNSDefaultOptions(c *check.C) {
 	// 2 are removed from the file at container start, and the 3rd (commented out) one is ignored by
 	// GetNameservers(), leading to a replacement of nameservers with the default set
 	tmpResolvConf := []byte("nameserver 127.0.0.1\n#nameserver 127.0.2.1\nnameserver ::1")
-	if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil {
+	if err := os.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil {
 		c.Fatal(err)
 	}
 
@@ -1285,7 +1282,7 @@ func (s *DockerSuite) TestRunDNSDefaultOptions(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunDNSOptions(c *check.C) {
+func (s *DockerSuite) TestRunDNSOptions(c *testing.T) {
 	// Not applicable on Windows as Windows does not support --dns*, or
 	// the Unix-specific functionality of resolv.conf.
 	testRequires(c, DaemonIsLinux)
@@ -1309,7 +1306,7 @@ func (s *DockerSuite) TestRunDNSOptions(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunDNSRepeatOptions(c *check.C) {
+func (s *DockerSuite) TestRunDNSRepeatOptions(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out := cli.DockerCmd(c, "run", "--dns=1.1.1.1", "--dns=2.2.2.2", "--dns-search=mydomain", "--dns-search=mydomain2", "--dns-opt=ndots:9", "--dns-opt=timeout:3", "busybox", "cat", "/etc/resolv.conf").Stdout()
 
@@ -1319,23 +1316,23 @@ func (s *DockerSuite) TestRunDNSRepeatOptions(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunDNSOptionsBasedOnHostResolvConf(c *check.C) {
+func (s *DockerSuite) TestRunDNSOptionsBasedOnHostResolvConf(c *testing.T) {
 	// Not applicable on Windows as testing Unix specific functionality
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
-	origResolvConf, err := ioutil.ReadFile("/etc/resolv.conf")
+	origResolvConf, err := os.ReadFile("/etc/resolv.conf")
 	if os.IsNotExist(err) {
 		c.Fatalf("/etc/resolv.conf does not exist")
 	}
 
-	hostNameservers := resolvconf.GetNameservers(origResolvConf, types.IP)
+	hostNameservers := resolvconf.GetNameservers(origResolvConf, resolvconf.IP)
 	hostSearch := resolvconf.GetSearchDomains(origResolvConf)
 
 	var out string
 	out, _ = dockerCmd(c, "run", "--dns=127.0.0.1", "busybox", "cat", "/etc/resolv.conf")
 
-	if actualNameservers := resolvconf.GetNameservers([]byte(out), types.IP); string(actualNameservers[0]) != "127.0.0.1" {
-		c.Fatalf("expected '127.0.0.1', but says: %q", string(actualNameservers[0]))
+	if actualNameservers := resolvconf.GetNameservers([]byte(out), resolvconf.IP); actualNameservers[0] != "127.0.0.1" {
+		c.Fatalf("expected '127.0.0.1', but says: %q", actualNameservers[0])
 	}
 
 	actualSearch := resolvconf.GetSearchDomains([]byte(out))
@@ -1350,7 +1347,7 @@ func (s *DockerSuite) TestRunDNSOptionsBasedOnHostResolvConf(c *check.C) {
 
 	out, _ = dockerCmd(c, "run", "--dns-search=mydomain", "busybox", "cat", "/etc/resolv.conf")
 
-	actualNameservers := resolvconf.GetNameservers([]byte(out), types.IP)
+	actualNameservers := resolvconf.GetNameservers([]byte(out), resolvconf.IP)
 	if len(actualNameservers) != len(hostNameservers) {
 		c.Fatalf("expected %q nameserver(s), but it has: %q", len(hostNameservers), len(actualNameservers))
 	}
@@ -1360,23 +1357,23 @@ func (s *DockerSuite) TestRunDNSOptionsBasedOnHostResolvConf(c *check.C) {
 		}
 	}
 
-	if actualSearch = resolvconf.GetSearchDomains([]byte(out)); string(actualSearch[0]) != "mydomain" {
-		c.Fatalf("expected 'mydomain', but says: %q", string(actualSearch[0]))
+	if actualSearch = resolvconf.GetSearchDomains([]byte(out)); actualSearch[0] != "mydomain" {
+		c.Fatalf("expected 'mydomain', but says: %q", actualSearch[0])
 	}
 
 	// test with file
 	tmpResolvConf := []byte("search example.com\nnameserver 12.34.56.78\nnameserver 127.0.0.1")
-	if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil {
+	if err := os.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil {
 		c.Fatal(err)
 	}
 	// put the old resolvconf back
 	defer func() {
-		if err := ioutil.WriteFile("/etc/resolv.conf", origResolvConf, 0644); err != nil {
+		if err := os.WriteFile("/etc/resolv.conf", origResolvConf, 0644); err != nil {
 			c.Fatal(err)
 		}
 	}()
 
-	resolvConf, err := ioutil.ReadFile("/etc/resolv.conf")
+	resolvConf, err := os.ReadFile("/etc/resolv.conf")
 	if os.IsNotExist(err) {
 		c.Fatalf("/etc/resolv.conf does not exist")
 	}
@@ -1384,7 +1381,7 @@ func (s *DockerSuite) TestRunDNSOptionsBasedOnHostResolvConf(c *check.C) {
 	hostSearch = resolvconf.GetSearchDomains(resolvConf)
 
 	out, _ = dockerCmd(c, "run", "busybox", "cat", "/etc/resolv.conf")
-	if actualNameservers = resolvconf.GetNameservers([]byte(out), types.IP); string(actualNameservers[0]) != "12.34.56.78" || len(actualNameservers) != 1 {
+	if actualNameservers = resolvconf.GetNameservers([]byte(out), resolvconf.IP); actualNameservers[0] != "12.34.56.78" || len(actualNameservers) != 1 {
 		c.Fatalf("expected '12.34.56.78', but has: %v", actualNameservers)
 	}
 
@@ -1401,11 +1398,11 @@ func (s *DockerSuite) TestRunDNSOptionsBasedOnHostResolvConf(c *check.C) {
 
 // Test to see if a non-root user can resolve a DNS name. Also
 // check if the container resolv.conf file has at least 0644 perm.
-func (s *DockerSuite) TestRunNonRootUserResolvName(c *check.C) {
+func (s *DockerSuite) TestRunNonRootUserResolvName(c *testing.T) {
 	// Not applicable on Windows as Windows does not support --user
-	testRequires(c, SameHostDaemon, Network, DaemonIsLinux, NotArm)
+	testRequires(c, testEnv.IsLocalDaemon, Network, DaemonIsLinux, NotArm)
 
-	dockerCmd(c, "run", "--name=testperm", "--user=nobody", "busybox", "nslookup", "apt.dockerproject.org")
+	dockerCmd(c, "run", "--name=testperm", "--user=nobody", "busybox", "nslookup", "example.com")
 
 	cID := getIDByName(c, "testperm")
 
@@ -1423,16 +1420,16 @@ func (s *DockerSuite) TestRunNonRootUserResolvName(c *check.C) {
 // Test if container resolv.conf gets updated the next time it restarts
 // if host /etc/resolv.conf has changed. This only applies if the container
 // uses the host's /etc/resolv.conf and does not have any dns options provided.
-func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) {
+func (s *DockerSuite) TestRunResolvconfUpdate(c *testing.T) {
 	// Not applicable on Windows as testing unix specific functionality
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 	c.Skip("Unstable test, to be re-activated once #19937 is resolved")
 
 	tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\n")
 	tmpLocalhostResolvConf := []byte("nameserver 127.0.0.1")
 
-	//take a copy of resolv.conf for restoring after test completes
-	resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
+	// take a copy of resolv.conf for restoring after test completes
+	resolvConfSystem, err := os.ReadFile("/etc/resolv.conf")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -1440,7 +1437,7 @@ func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) {
 	// This test case is meant to test monitoring resolv.conf when it is
 	// a regular file not a bind mounc. So we unmount resolv.conf and replace
 	// it with a file containing the original settings.
-	mounted, err := mount.Mounted("/etc/resolv.conf")
+	mounted, err := mountinfo.Mounted("/etc/resolv.conf")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -1448,20 +1445,19 @@ func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) {
 		icmd.RunCommand("umount", "/etc/resolv.conf").Assert(c, icmd.Success)
 	}
 
-	//cleanup
+	// cleanup
 	defer func() {
-		if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
+		if err := os.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
 			c.Fatal(err)
 		}
 	}()
 
-	//1. test that a restarting container gets an updated resolv.conf
+	// 1. test that a restarting container gets an updated resolv.conf
 	dockerCmd(c, "run", "--name=first", "busybox", "true")
 	containerID1 := getIDByName(c, "first")
 
 	// replace resolv.conf with our temporary copy
-	bytesResolvConf := []byte(tmpResolvConf)
-	if err := ioutil.WriteFile("/etc/resolv.conf", bytesResolvConf, 0644); err != nil {
+	if err := os.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil {
 		c.Fatal(err)
 	}
 
@@ -1470,21 +1466,21 @@ func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) {
 
 	// check for update in container
 	containerResolv := readContainerFile(c, containerID1, "resolv.conf")
-	if !bytes.Equal(containerResolv, bytesResolvConf) {
+	if !bytes.Equal(containerResolv, tmpResolvConf) {
 		c.Fatalf("Restarted container does not have updated resolv.conf; expected %q, got %q", tmpResolvConf, string(containerResolv))
 	}
 
-	/*	//make a change to resolv.conf (in this case replacing our tmp copy with orig copy)
-		if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
+	/*	// make a change to resolv.conf (in this case replacing our tmp copy with orig copy)
+		if err := os.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
 						c.Fatal(err)
 								} */
-	//2. test that a restarting container does not receive resolv.conf updates
+	// 2. test that a restarting container does not receive resolv.conf updates
 	//   if it modified the container copy of the starting point resolv.conf
 	dockerCmd(c, "run", "--name=second", "busybox", "sh", "-c", "echo 'search mylittlepony.com' >>/etc/resolv.conf")
 	containerID2 := getIDByName(c, "second")
 
-	//make a change to resolv.conf (in this case replacing our tmp copy with orig copy)
-	if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
+	// make a change to resolv.conf (in this case replacing our tmp copy with orig copy)
+	if err := os.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
 		c.Fatal(err)
 	}
 
@@ -1497,37 +1493,36 @@ func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) {
 		c.Fatalf("Container's resolv.conf should not have been updated with host resolv.conf: %q", string(containerResolv))
 	}
 
-	//3. test that a running container's resolv.conf is not modified while running
+	// 3. test that a running container's resolv.conf is not modified while running
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
 	runningContainerID := strings.TrimSpace(out)
 
 	// replace resolv.conf
-	if err := ioutil.WriteFile("/etc/resolv.conf", bytesResolvConf, 0644); err != nil {
+	if err := os.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil {
 		c.Fatal(err)
 	}
 
 	// check for update in container
 	containerResolv = readContainerFile(c, runningContainerID, "resolv.conf")
-	if bytes.Equal(containerResolv, bytesResolvConf) {
+	if bytes.Equal(containerResolv, tmpResolvConf) {
 		c.Fatalf("Running container should not have updated resolv.conf; expected %q, got %q", string(resolvConfSystem), string(containerResolv))
 	}
 
-	//4. test that a running container's resolv.conf is updated upon restart
+	// 4. test that a running container's resolv.conf is updated upon restart
 	//   (the above container is still running..)
 	dockerCmd(c, "restart", runningContainerID)
 
 	// check for update in container
 	containerResolv = readContainerFile(c, runningContainerID, "resolv.conf")
-	if !bytes.Equal(containerResolv, bytesResolvConf) {
-		c.Fatalf("Restarted container should have updated resolv.conf; expected %q, got %q", string(bytesResolvConf), string(containerResolv))
+	if !bytes.Equal(containerResolv, tmpResolvConf) {
+		c.Fatalf("Restarted container should have updated resolv.conf; expected %q, got %q", string(tmpResolvConf), string(containerResolv))
 	}
 
-	//5. test that additions of a localhost resolver are cleaned from
+	// 5. test that additions of a localhost resolver are cleaned from
 	//   host resolv.conf before updating container's resolv.conf copies
 
 	// replace resolv.conf with a localhost-only nameserver copy
-	bytesResolvConf = []byte(tmpLocalhostResolvConf)
-	if err = ioutil.WriteFile("/etc/resolv.conf", bytesResolvConf, 0644); err != nil {
+	if err = os.WriteFile("/etc/resolv.conf", tmpLocalhostResolvConf, 0644); err != nil {
 		c.Fatal(err)
 	}
 
@@ -1542,11 +1537,11 @@ func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) {
 		c.Fatalf("Container does not have cleaned/replaced DNS in resolv.conf; expected %q, got %q", expected, string(containerResolv))
 	}
 
-	//6. Test that replacing (as opposed to modifying) resolv.conf triggers an update
+	// 6. Test that replacing (as opposed to modifying) resolv.conf triggers an update
 	//   of containers' resolv.conf.
 
 	// Restore the original resolv.conf
-	if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
+	if err := os.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
 		c.Fatal(err)
 	}
 
@@ -1555,8 +1550,7 @@ func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) {
 	containerID3 := getIDByName(c, "third")
 
 	// Create a modified resolv.conf.aside and override resolv.conf with it
-	bytesResolvConf = []byte(tmpResolvConf)
-	if err := ioutil.WriteFile("/etc/resolv.conf.aside", bytesResolvConf, 0644); err != nil {
+	if err := os.WriteFile("/etc/resolv.conf.aside", tmpResolvConf, 0644); err != nil {
 		c.Fatal(err)
 	}
 
@@ -1570,14 +1564,14 @@ func (s *DockerSuite) TestRunResolvconfUpdate(c *check.C) {
 
 	// check for update in container
 	containerResolv = readContainerFile(c, containerID3, "resolv.conf")
-	if !bytes.Equal(containerResolv, bytesResolvConf) {
+	if !bytes.Equal(containerResolv, tmpResolvConf) {
 		c.Fatalf("Stopped container does not have updated resolv.conf; expected\n%q\n got\n%q", tmpResolvConf, string(containerResolv))
 	}
 
-	//cleanup, restore original resolv.conf happens in defer func()
+	// cleanup, restore original resolv.conf happens in defer func()
 }
 
-func (s *DockerSuite) TestRunAddHost(c *check.C) {
+func (s *DockerSuite) TestRunAddHost(c *testing.T) {
 	// Not applicable on Windows as it does not support --add-host
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "--add-host=extra:86.75.30.9", "busybox", "grep", "extra", "/etc/hosts")
@@ -1589,7 +1583,7 @@ func (s *DockerSuite) TestRunAddHost(c *check.C) {
 }
 
 // Regression test for #6983
-func (s *DockerSuite) TestRunAttachStdErrOnlyTTYMode(c *check.C) {
+func (s *DockerSuite) TestRunAttachStdErrOnlyTTYMode(c *testing.T) {
 	_, exitCode := dockerCmd(c, "run", "-t", "-a", "stderr", "busybox", "true")
 	if exitCode != 0 {
 		c.Fatalf("Container should have exited with error code 0")
@@ -1597,7 +1591,7 @@ func (s *DockerSuite) TestRunAttachStdErrOnlyTTYMode(c *check.C) {
 }
 
 // Regression test for #6983
-func (s *DockerSuite) TestRunAttachStdOutOnlyTTYMode(c *check.C) {
+func (s *DockerSuite) TestRunAttachStdOutOnlyTTYMode(c *testing.T) {
 	_, exitCode := dockerCmd(c, "run", "-t", "-a", "stdout", "busybox", "true")
 	if exitCode != 0 {
 		c.Fatalf("Container should have exited with error code 0")
@@ -1605,7 +1599,7 @@ func (s *DockerSuite) TestRunAttachStdOutOnlyTTYMode(c *check.C) {
 }
 
 // Regression test for #6983
-func (s *DockerSuite) TestRunAttachStdOutAndErrTTYMode(c *check.C) {
+func (s *DockerSuite) TestRunAttachStdOutAndErrTTYMode(c *testing.T) {
 	_, exitCode := dockerCmd(c, "run", "-t", "-a", "stdout", "-a", "stderr", "busybox", "true")
 	if exitCode != 0 {
 		c.Fatalf("Container should have exited with error code 0")
@@ -1614,7 +1608,7 @@ func (s *DockerSuite) TestRunAttachStdOutAndErrTTYMode(c *check.C) {
 
 // Test for #10388 - this will run the same test as TestRunAttachStdOutAndErrTTYMode
 // but using --attach instead of -a to make sure we read the flag correctly
-func (s *DockerSuite) TestRunAttachWithDetach(c *check.C) {
+func (s *DockerSuite) TestRunAttachWithDetach(c *testing.T) {
 	icmd.RunCommand(dockerBinary, "run", "-d", "--attach", "stdout", "busybox", "true").Assert(c, icmd.Expected{
 		ExitCode: 1,
 		Error:    "exit status 1",
@@ -1622,7 +1616,7 @@ func (s *DockerSuite) TestRunAttachWithDetach(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestRunState(c *check.C) {
+func (s *DockerSuite) TestRunState(c *testing.T) {
 	// TODO Windows: This needs some rework as Windows busybox does not support top
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
@@ -1659,7 +1653,7 @@ func (s *DockerSuite) TestRunState(c *check.C) {
 }
 
 // Test for #1737
-func (s *DockerSuite) TestRunCopyVolumeUIDGID(c *check.C) {
+func (s *DockerSuite) TestRunCopyVolumeUIDGID(c *testing.T) {
 	// Not applicable on Windows as it does not support uid or gid in this way
 	testRequires(c, DaemonIsLinux)
 	name := "testrunvolumesuidgid"
@@ -1677,7 +1671,7 @@ func (s *DockerSuite) TestRunCopyVolumeUIDGID(c *check.C) {
 }
 
 // Test for #1582
-func (s *DockerSuite) TestRunCopyVolumeContent(c *check.C) {
+func (s *DockerSuite) TestRunCopyVolumeContent(c *testing.T) {
 	// TODO Windows, post RS1. Windows does not yet support volume functionality
 	// that copies from the image to the volume.
 	testRequires(c, DaemonIsLinux)
@@ -1692,7 +1686,7 @@ func (s *DockerSuite) TestRunCopyVolumeContent(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCleanupCmdOnEntrypoint(c *check.C) {
+func (s *DockerSuite) TestRunCleanupCmdOnEntrypoint(c *testing.T) {
 	name := "testrunmdcleanuponentrypoint"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
 		ENTRYPOINT ["echo"]
@@ -1705,7 +1699,7 @@ func (s *DockerSuite) TestRunCleanupCmdOnEntrypoint(c *check.C) {
 	out = strings.TrimSpace(out)
 	expected := "root"
 	if testEnv.OSType == "windows" {
-		if strings.Contains(testEnv.PlatformDefaults.BaseImage, "windowsservercore") {
+		if strings.Contains(testEnv.PlatformDefaults.BaseImage, "servercore") {
 			expected = `user manager\containeradministrator`
 		} else {
 			expected = `ContainerAdministrator` // nanoserver
@@ -1717,7 +1711,7 @@ func (s *DockerSuite) TestRunCleanupCmdOnEntrypoint(c *check.C) {
 }
 
 // TestRunWorkdirExistsAndIsFile checks that if 'docker run -w' with existing file can be detected
-func (s *DockerSuite) TestRunWorkdirExistsAndIsFile(c *check.C) {
+func (s *DockerSuite) TestRunWorkdirExistsAndIsFile(c *testing.T) {
 	existingFile := "/bin/cat"
 	expected := "not a directory"
 	if testEnv.OSType == "windows" {
@@ -1731,7 +1725,7 @@ func (s *DockerSuite) TestRunWorkdirExistsAndIsFile(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunExitOnStdinClose(c *check.C) {
+func (s *DockerSuite) TestRunExitOnStdinClose(c *testing.T) {
 	name := "testrunexitonstdinclose"
 
 	meow := "/bin/cat"
@@ -1769,14 +1763,14 @@ func (s *DockerSuite) TestRunExitOnStdinClose(c *check.C) {
 	if err := stdin.Close(); err != nil {
 		c.Fatal(err)
 	}
-	finish := make(chan error)
+	finish := make(chan error, 1)
 	go func() {
 		finish <- runCmd.Wait()
 		close(finish)
 	}()
 	select {
 	case err := <-finish:
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(time.Duration(delay) * time.Second):
 		c.Fatal("docker run failed to exit on stdin close")
 	}
@@ -1788,24 +1782,22 @@ func (s *DockerSuite) TestRunExitOnStdinClose(c *check.C) {
 }
 
 // Test run -i --restart xxx doesn't hang
-func (s *DockerSuite) TestRunInteractiveWithRestartPolicy(c *check.C) {
+func (s *DockerSuite) TestRunInteractiveWithRestartPolicy(c *testing.T) {
 	name := "test-inter-restart"
 
-	result := icmd.StartCmd(icmd.Cmd{
+	result := icmd.RunCmd(icmd.Cmd{
 		Command: []string{dockerBinary, "run", "-i", "--name", name, "--restart=always", "busybox", "sh"},
 		Stdin:   bytes.NewBufferString("exit 11"),
 	})
-	c.Assert(result.Error, checker.IsNil)
 	defer func() {
-		dockerCmdWithResult("stop", name).Assert(c, icmd.Success)
+		cli.Docker(cli.Args("stop", name)).Assert(c, icmd.Success)
 	}()
 
-	result = icmd.WaitOnCmd(60*time.Second, result)
 	result.Assert(c, icmd.Expected{ExitCode: 11})
 }
 
 // Test for #2267
-func (s *DockerSuite) TestRunWriteSpecialFilesAndNotCommit(c *check.C) {
+func (s *DockerSuite) TestRunWriteSpecialFilesAndNotCommit(c *testing.T) {
 	// Cannot run on Windows as this files are not present in Windows
 	testRequires(c, DaemonIsLinux)
 
@@ -1814,7 +1806,7 @@ func (s *DockerSuite) TestRunWriteSpecialFilesAndNotCommit(c *check.C) {
 	testRunWriteSpecialFilesAndNotCommit(c, "writeresolv", "/etc/resolv.conf")
 }
 
-func testRunWriteSpecialFilesAndNotCommit(c *check.C, name, path string) {
+func testRunWriteSpecialFilesAndNotCommit(c *testing.T, name, path string) {
 	command := fmt.Sprintf("echo test2267 >> %s && cat %s", path, path)
 	out, _ := dockerCmd(c, "run", "--name", name, "busybox", "sh", "-c", command)
 	if !strings.Contains(out, "test2267") {
@@ -1827,7 +1819,7 @@ func testRunWriteSpecialFilesAndNotCommit(c *check.C, name, path string) {
 	}
 }
 
-func eqToBaseDiff(out string, c *check.C) bool {
+func eqToBaseDiff(out string, c *testing.T) bool {
 	name := "eqToBaseDiff" + testutil.GenerateRandomAlphaOnlyString(32)
 	dockerCmd(c, "run", "--name", name, "busybox", "echo", "hello")
 	cID := getIDByName(c, name)
@@ -1853,7 +1845,7 @@ func sliceEq(a, b []string) bool {
 	return true
 }
 
-func (s *DockerSuite) TestRunWithBadDevice(c *check.C) {
+func (s *DockerSuite) TestRunWithBadDevice(c *testing.T) {
 	// Cannot run on Windows as Windows does not support --device
 	testRequires(c, DaemonIsLinux)
 	name := "baddevice"
@@ -1868,7 +1860,7 @@ func (s *DockerSuite) TestRunWithBadDevice(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunEntrypoint(c *check.C) {
+func (s *DockerSuite) TestRunEntrypoint(c *testing.T) {
 	name := "entrypoint"
 
 	out, _ := dockerCmd(c, "run", "--name", name, "--entrypoint", "echo", "busybox", "-n", "foobar")
@@ -1879,15 +1871,20 @@ func (s *DockerSuite) TestRunEntrypoint(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunBindMounts(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestRunBindMounts(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	if testEnv.OSType == "linux" {
 		testRequires(c, DaemonIsLinux, NotUserNamespace)
 	}
 
+	if testEnv.OSType == "windows" {
+		// Disabled prior to RS5 due to how volumes are mapped
+		testRequires(c, DaemonIsWindowsAtLeastBuild(osversion.RS5))
+	}
+
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 
-	tmpDir, err := ioutil.TempDir("", "docker-test-container")
+	tmpDir, err := os.MkdirTemp("", "docker-test-container")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -1896,7 +1893,7 @@ func (s *DockerSuite) TestRunBindMounts(c *check.C) {
 	writeFile(path.Join(tmpDir, "touch-me"), "", c)
 
 	// Test reading from a read-only bind mount
-	out, _ := dockerCmd(c, "run", "-v", fmt.Sprintf("%s:%s/tmp:ro", tmpDir, prefix), "busybox", "ls", prefix+"/tmp")
+	out, _ := dockerCmd(c, "run", "-v", fmt.Sprintf("%s:%s/tmpx:ro", tmpDir, prefix), "busybox", "ls", prefix+"/tmpx")
 	if !strings.Contains(out, "touch-me") {
 		c.Fatal("Container failed to read from bind mount")
 	}
@@ -1930,11 +1927,11 @@ func (s *DockerSuite) TestRunBindMounts(c *check.C) {
 
 // Ensure that CIDFile gets deleted if it's empty
 // Perform this test by making `docker run` fail
-func (s *DockerSuite) TestRunCidFileCleanupIfEmpty(c *check.C) {
+func (s *DockerSuite) TestRunCidFileCleanupIfEmpty(c *testing.T) {
 	// Skip on Windows. Base image on Windows has a CMD set in the image.
 	testRequires(c, DaemonIsLinux)
 
-	tmpDir, err := ioutil.TempDir("", "TestRunCidFile")
+	tmpDir, err := os.MkdirTemp("", "TestRunCidFile")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -1959,10 +1956,10 @@ func (s *DockerSuite) TestRunCidFileCleanupIfEmpty(c *check.C) {
 }
 
 // #2098 - Docker cidFiles only contain short version of the containerId
-//sudo docker run --cidfile /tmp/docker_tesc.cid ubuntu echo "test"
+// sudo docker run --cidfile /tmp/docker_tesc.cid ubuntu echo "test"
 // TestRunCidFile tests that run --cidfile returns the longid
-func (s *DockerSuite) TestRunCidFileCheckIDLength(c *check.C) {
-	tmpDir, err := ioutil.TempDir("", "TestRunCidFile")
+func (s *DockerSuite) TestRunCidFileCheckIDLength(c *testing.T) {
+	tmpDir, err := os.MkdirTemp("", "TestRunCidFile")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -1972,7 +1969,7 @@ func (s *DockerSuite) TestRunCidFileCheckIDLength(c *check.C) {
 	out, _ := dockerCmd(c, "run", "-d", "--cidfile", tmpCidFile, "busybox", "true")
 
 	id := strings.TrimSpace(out)
-	buffer, err := ioutil.ReadFile(tmpCidFile)
+	buffer, err := os.ReadFile(tmpCidFile)
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -1985,7 +1982,8 @@ func (s *DockerSuite) TestRunCidFileCheckIDLength(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunSetMacAddress(c *check.C) {
+func (s *DockerSuite) TestRunSetMacAddress(c *testing.T) {
+	skip.If(c, RuntimeIsWindowsContainerd(), "FIXME: Broken on Windows + containerd combination")
 	mac := "12:34:56:78:9a:bc"
 	var out string
 	if testEnv.OSType == "windows" {
@@ -2001,7 +1999,7 @@ func (s *DockerSuite) TestRunSetMacAddress(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunInspectMacAddress(c *check.C) {
+func (s *DockerSuite) TestRunInspectMacAddress(c *testing.T) {
 	// TODO Windows. Network settings are not propagated back to inspect.
 	testRequires(c, DaemonIsLinux)
 	mac := "12:34:56:78:9a:bc"
@@ -2015,17 +2013,17 @@ func (s *DockerSuite) TestRunInspectMacAddress(c *check.C) {
 }
 
 // test docker run use an invalid mac address
-func (s *DockerSuite) TestRunWithInvalidMacAddress(c *check.C) {
+func (s *DockerSuite) TestRunWithInvalidMacAddress(c *testing.T) {
 	out, _, err := dockerCmdWithError("run", "--mac-address", "92:d0:c6:0a:29", "busybox")
-	//use an invalid mac address should with an error out
+	// use an invalid mac address should with an error out
 	if err == nil || !strings.Contains(out, "is not a valid mac address") {
 		c.Fatalf("run with an invalid --mac-address should with error out")
 	}
 }
 
-func (s *DockerSuite) TestRunDeallocatePortOnMissingIptablesRule(c *check.C) {
+func (s *DockerSuite) TestRunDeallocatePortOnMissingIptablesRule(c *testing.T) {
 	// TODO Windows. Network settings are not propagated back to inspect.
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	out := cli.DockerCmd(c, "run", "-d", "-p", "23:23", "busybox", "top").Combined()
 
@@ -2039,11 +2037,11 @@ func (s *DockerSuite) TestRunDeallocatePortOnMissingIptablesRule(c *check.C) {
 	cli.DockerCmd(c, "run", "-d", "-p", "23:23", "busybox", "top")
 }
 
-func (s *DockerSuite) TestRunPortInUse(c *check.C) {
+func (s *DockerSuite) TestRunPortInUse(c *testing.T) {
 	// TODO Windows. The duplicate NAT message returned by Windows will be
 	// changing as is currently completely undecipherable. Does need modifying
 	// to run sh rather than top though as top isn't in Windows busybox.
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	port := "1234"
 	dockerCmd(c, "run", "-d", "-p", port+":80", "busybox", "top")
@@ -2058,19 +2056,18 @@ func (s *DockerSuite) TestRunPortInUse(c *check.C) {
 }
 
 // https://github.com/docker/docker/issues/12148
-func (s *DockerSuite) TestRunAllocatePortInReservedRange(c *check.C) {
+func (s *DockerSuite) TestRunAllocatePortInReservedRange(c *testing.T) {
 	// TODO Windows. -P is not yet supported
 	testRequires(c, DaemonIsLinux)
 	// allocate a dynamic port to get the most recent
 	out, _ := dockerCmd(c, "run", "-d", "-P", "-p", "80", "busybox", "top")
 
 	id := strings.TrimSpace(out)
-	out, _ = dockerCmd(c, "port", id, "80")
-
-	strPort := strings.Split(strings.TrimSpace(out), ":")[1]
-	port, err := strconv.ParseInt(strPort, 10, 64)
+	out, _ = dockerCmd(c, "inspect", "--format", `{{index .NetworkSettings.Ports "80/tcp" 0 "HostPort" }}`, id)
+	out = strings.TrimSpace(out)
+	port, err := strconv.ParseInt(out, 10, 64)
 	if err != nil {
-		c.Fatalf("invalid port, got: %s, error: %s", strPort, err)
+		c.Fatalf("invalid port, got: %s, error: %s", out, err)
 	}
 
 	// allocate a static port and a dynamic port together, with static port
@@ -2079,18 +2076,18 @@ func (s *DockerSuite) TestRunAllocatePortInReservedRange(c *check.C) {
 }
 
 // Regression test for #7792
-func (s *DockerSuite) TestRunMountOrdering(c *check.C) {
+func (s *DockerSuite) TestRunMountOrdering(c *testing.T) {
 	// TODO Windows: Post RS1. Windows does not support nested mounts.
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 
-	tmpDir, err := ioutil.TempDir("", "docker_nested_mount_test")
+	tmpDir, err := os.MkdirTemp("", "docker_nested_mount_test")
 	if err != nil {
 		c.Fatal(err)
 	}
 	defer os.RemoveAll(tmpDir)
 
-	tmpDir2, err := ioutil.TempDir("", "docker_nested_mount_test2")
+	tmpDir2, err := os.MkdirTemp("", "docker_nested_mount_test2")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -2102,15 +2099,15 @@ func (s *DockerSuite) TestRunMountOrdering(c *check.C) {
 		c.Fatalf("failed to mkdir at %s - %s", fooDir, err)
 	}
 
-	if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", fooDir), []byte{}, 0644); err != nil {
+	if err := os.WriteFile(fmt.Sprintf("%s/touch-me", fooDir), []byte{}, 0644); err != nil {
 		c.Fatal(err)
 	}
 
-	if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", tmpDir), []byte{}, 0644); err != nil {
+	if err := os.WriteFile(fmt.Sprintf("%s/touch-me", tmpDir), []byte{}, 0644); err != nil {
 		c.Fatal(err)
 	}
 
-	if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", tmpDir2), []byte{}, 0644); err != nil {
+	if err := os.WriteFile(fmt.Sprintf("%s/touch-me", tmpDir2), []byte{}, 0644); err != nil {
 		c.Fatal(err)
 	}
 
@@ -2124,12 +2121,12 @@ func (s *DockerSuite) TestRunMountOrdering(c *check.C) {
 }
 
 // Regression test for https://github.com/docker/docker/issues/8259
-func (s *DockerSuite) TestRunReuseBindVolumeThatIsSymlink(c *check.C) {
+func (s *DockerSuite) TestRunReuseBindVolumeThatIsSymlink(c *testing.T) {
 	// Not applicable on Windows as Windows does not support volumes
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 
-	tmpDir, err := ioutil.TempDir(os.TempDir(), "testlink")
+	tmpDir, err := os.MkdirTemp(os.TempDir(), "testlink")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -2149,8 +2146,8 @@ func (s *DockerSuite) TestRunReuseBindVolumeThatIsSymlink(c *check.C) {
 	dockerCmd(c, "run", "-v", fmt.Sprintf("%s:"+prefix+"/tmp/test", linkPath), "busybox", "ls", prefix+"/tmp/test")
 }
 
-//GH#10604: Test an "/etc" volume doesn't overlay special bind mounts in container
-func (s *DockerSuite) TestRunCreateVolumeEtc(c *check.C) {
+// GH#10604: Test an "/etc" volume doesn't overlay special bind mounts in container
+func (s *DockerSuite) TestRunCreateVolumeEtc(c *testing.T) {
 	// While Windows supports volumes, it does not support --add-host hence
 	// this test is not applicable on Windows.
 	testRequires(c, DaemonIsLinux)
@@ -2171,7 +2168,7 @@ func (s *DockerSuite) TestRunCreateVolumeEtc(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestVolumesNoCopyData(c *check.C) {
+func (s *DockerSuite) TestVolumesNoCopyData(c *testing.T) {
 	// TODO Windows (Post RS1). Windows does not support volumes which
 	// are pre-populated such as is built in the dockerfile used in this test.
 	testRequires(c, DaemonIsLinux)
@@ -2191,7 +2188,7 @@ func (s *DockerSuite) TestVolumesNoCopyData(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunNoOutputFromPullInStdout(c *check.C) {
+func (s *DockerSuite) TestRunNoOutputFromPullInStdout(c *testing.T) {
 	// just run with unknown image
 	cmd := exec.Command(dockerBinary, "run", "asdfsg")
 	stdout := bytes.NewBuffer(nil)
@@ -2204,8 +2201,8 @@ func (s *DockerSuite) TestRunNoOutputFromPullInStdout(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) {
-	testRequires(c, SameHostDaemon)
+func (s *DockerSuite) TestRunVolumesCleanPaths(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon)
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	buildImageSuccessfully(c, "run_volumes_clean_paths", build.WithDockerfile(`FROM busybox
 		VOLUME `+prefix+`/foo/`))
@@ -2217,7 +2214,7 @@ func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) {
 	}
 
 	out, err = inspectMountSourceField("dark_helmet", prefix+slash+`foo`)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	if !strings.Contains(strings.ToLower(out), strings.ToLower(testEnv.PlatformDefaults.VolumesConfigPath)) {
 		c.Fatalf("Volume was not defined for %s/foo\n%q", prefix, out)
 	}
@@ -2228,14 +2225,14 @@ func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) {
 	}
 
 	out, err = inspectMountSourceField("dark_helmet", prefix+slash+"bar")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	if !strings.Contains(strings.ToLower(out), strings.ToLower(testEnv.PlatformDefaults.VolumesConfigPath)) {
 		c.Fatalf("Volume was not defined for %s/bar\n%q", prefix, out)
 	}
 }
 
 // Regression test for #3631
-func (s *DockerSuite) TestRunSlowStdoutConsumer(c *check.C) {
+func (s *DockerSuite) TestRunSlowStdoutConsumer(c *testing.T) {
 	// TODO Windows: This should be able to run on Windows if can find an
 	// alternate to /dev/zero and /dev/stdout.
 	testRequires(c, DaemonIsLinux)
@@ -2263,7 +2260,7 @@ func (s *DockerSuite) TestRunSlowStdoutConsumer(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunAllowPortRangeThroughExpose(c *check.C) {
+func (s *DockerSuite) TestRunAllowPortRangeThroughExpose(c *testing.T) {
 	// TODO Windows: -P is not currently supported. Also network
 	// settings are not propagated back.
 	testRequires(c, DaemonIsLinux)
@@ -2280,21 +2277,21 @@ func (s *DockerSuite) TestRunAllowPortRangeThroughExpose(c *check.C) {
 		if portnum < 3000 || portnum > 3003 {
 			c.Fatalf("Port %d is out of range ", portnum)
 		}
-		if binding == nil || len(binding) != 1 || len(binding[0].HostPort) == 0 {
+		if len(binding) == 0 || len(binding[0].HostPort) == 0 {
 			c.Fatalf("Port is not mapped for the port %s", port)
 		}
 	}
 }
 
-func (s *DockerSuite) TestRunExposePort(c *check.C) {
+func (s *DockerSuite) TestRunExposePort(c *testing.T) {
 	out, _, err := dockerCmdWithError("run", "--expose", "80000", "busybox")
-	c.Assert(err, checker.NotNil, check.Commentf("--expose with an invalid port should error out"))
-	c.Assert(out, checker.Contains, "invalid range format for --expose")
+	assert.Assert(c, err != nil, "--expose with an invalid port should error out")
+	assert.Assert(c, strings.Contains(out, "invalid range format for --expose"))
 }
 
-func (s *DockerSuite) TestRunModeIpcHost(c *check.C) {
+func (s *DockerSuite) TestRunModeIpcHost(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
 
 	hostIpc, err := os.Readlink("/proc/1/ns/ipc")
 	if err != nil {
@@ -2314,7 +2311,7 @@ func (s *DockerSuite) TestRunModeIpcHost(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunModeIpcContainerNotExists(c *check.C) {
+func (s *DockerSuite) TestRunModeIpcContainerNotExists(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "-d", "--ipc", "container:abcd1234", "busybox", "top")
@@ -2323,9 +2320,9 @@ func (s *DockerSuite) TestRunModeIpcContainerNotExists(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunModeIpcContainerNotRunning(c *check.C) {
+func (s *DockerSuite) TestRunModeIpcContainerNotRunning(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	out, _ := dockerCmd(c, "create", "busybox")
 
@@ -2336,9 +2333,9 @@ func (s *DockerSuite) TestRunModeIpcContainerNotRunning(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunModePIDContainer(c *check.C) {
+func (s *DockerSuite) TestRunModePIDContainer(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "top")
 
@@ -2361,7 +2358,7 @@ func (s *DockerSuite) TestRunModePIDContainer(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunModePIDContainerNotExists(c *check.C) {
+func (s *DockerSuite) TestRunModePIDContainerNotExists(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "-d", "--pid", "container:abcd1234", "busybox", "top")
@@ -2370,9 +2367,9 @@ func (s *DockerSuite) TestRunModePIDContainerNotExists(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunModePIDContainerNotRunning(c *check.C) {
+func (s *DockerSuite) TestRunModePIDContainerNotRunning(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	out, _ := dockerCmd(c, "create", "busybox")
 
@@ -2383,15 +2380,15 @@ func (s *DockerSuite) TestRunModePIDContainerNotRunning(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunMountShmMqueueFromHost(c *check.C) {
+func (s *DockerSuite) TestRunMountShmMqueueFromHost(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
 
 	dockerCmd(c, "run", "-d", "--name", "shmfromhost", "-v", "/dev/shm:/dev/shm", "-v", "/dev/mqueue:/dev/mqueue", "busybox", "sh", "-c", "echo -n test > /dev/shm/test && touch /dev/mqueue/toto && top")
 	defer os.Remove("/dev/mqueue/toto")
 	defer os.Remove("/dev/shm/test")
 	volPath, err := inspectMountSourceField("shmfromhost", "/dev/shm")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	if volPath != "/dev/shm" {
 		c.Fatalf("volumePath should have been /dev/shm, was %s", volPath)
 	}
@@ -2407,13 +2404,13 @@ func (s *DockerSuite) TestRunMountShmMqueueFromHost(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestContainerNetworkMode(c *check.C) {
+func (s *DockerSuite) TestContainerNetworkMode(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), check.IsNil)
+	assert.NilError(c, waitRun(id))
 	pid1 := inspectField(c, id, "State.Pid")
 
 	parentContainerNet, err := os.Readlink(fmt.Sprintf("/proc/%s/ns/net", pid1))
@@ -2428,31 +2425,9 @@ func (s *DockerSuite) TestContainerNetworkMode(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunModePIDHost(c *check.C) {
-	// Not applicable on Windows as uses Unix-specific capabilities
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
-
-	hostPid, err := os.Readlink("/proc/1/ns/pid")
-	if err != nil {
-		c.Fatal(err)
-	}
-
-	out, _ := dockerCmd(c, "run", "--pid=host", "busybox", "readlink", "/proc/self/ns/pid")
-	out = strings.Trim(out, "\n")
-	if hostPid != out {
-		c.Fatalf("PID different with --pid=host %s != %s\n", hostPid, out)
-	}
-
-	out, _ = dockerCmd(c, "run", "busybox", "readlink", "/proc/self/ns/pid")
-	out = strings.Trim(out, "\n")
-	if hostPid == out {
-		c.Fatalf("PID should be different without --pid=host %s == %s\n", hostPid, out)
-	}
-}
-
-func (s *DockerSuite) TestRunModeUTSHost(c *check.C) {
+func (s *DockerSuite) TestRunModeUTSHost(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	hostUTS, err := os.Readlink("/proc/1/ns/uts")
 	if err != nil {
@@ -2472,12 +2447,12 @@ func (s *DockerSuite) TestRunModeUTSHost(c *check.C) {
 	}
 
 	out, _ = dockerCmdWithFail(c, "run", "-h=name", "--uts=host", "busybox", "ps")
-	c.Assert(out, checker.Contains, runconfig.ErrConflictUTSHostname.Error())
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictUTSHostname.Error()))
 }
 
-func (s *DockerSuite) TestRunTLSVerify(c *check.C) {
+func (s *DockerSuite) TestRunTLSVerify(c *testing.T) {
 	// Remote daemons use TLS and this test is not applicable when TLS is required.
-	testRequires(c, SameHostDaemon)
+	testRequires(c, testEnv.IsLocalDaemon)
 	if out, code, err := dockerCmdWithError("ps"); err != nil || code != 0 {
 		c.Fatalf("Should have worked: %v:\n%v", err, out)
 	}
@@ -2491,7 +2466,7 @@ func (s *DockerSuite) TestRunTLSVerify(c *check.C) {
 	result.Assert(c, icmd.Expected{ExitCode: 1, Err: "cert"})
 }
 
-func (s *DockerSuite) TestRunPortFromDockerRangeInUse(c *check.C) {
+func (s *DockerSuite) TestRunPortFromDockerRangeInUse(c *testing.T) {
 	// TODO Windows. Once moved to libnetwork/CNM, this may be able to be
 	// re-instated.
 	testRequires(c, DaemonIsLinux)
@@ -2499,13 +2474,12 @@ func (s *DockerSuite) TestRunPortFromDockerRangeInUse(c *check.C) {
 	out, _ := dockerCmd(c, "run", "-d", "-p", ":80", "busybox", "top")
 
 	id := strings.TrimSpace(out)
-	out, _ = dockerCmd(c, "port", id)
 
+	out, _ = dockerCmd(c, "inspect", "--format", `{{index .NetworkSettings.Ports "80/tcp" 0 "HostPort" }}`, id)
 	out = strings.TrimSpace(out)
 	if out == "" {
 		c.Fatal("docker port command output is empty")
 	}
-	out = strings.Split(out, ":")[1]
 	lastPort, err := strconv.Atoi(out)
 	if err != nil {
 		c.Fatal(err)
@@ -2523,8 +2497,8 @@ func (s *DockerSuite) TestRunPortFromDockerRangeInUse(c *check.C) {
 	dockerCmd(c, "port", id)
 }
 
-func (s *DockerSuite) TestRunTTYWithPipe(c *check.C) {
-	errChan := make(chan error)
+func (s *DockerSuite) TestRunTTYWithPipe(c *testing.T) {
+	errChan := make(chan error, 1)
 	go func() {
 		defer close(errChan)
 
@@ -2549,13 +2523,13 @@ func (s *DockerSuite) TestRunTTYWithPipe(c *check.C) {
 
 	select {
 	case err := <-errChan:
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(30 * time.Second):
 		c.Fatal("container is running but should have failed")
 	}
 }
 
-func (s *DockerSuite) TestRunNonLocalMacAddress(c *check.C) {
+func (s *DockerSuite) TestRunNonLocalMacAddress(c *testing.T) {
 	addr := "00:16:3E:08:00:50"
 	args := []string{"run", "--mac-address", addr}
 	expected := addr
@@ -2572,9 +2546,9 @@ func (s *DockerSuite) TestRunNonLocalMacAddress(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunNetHost(c *check.C) {
+func (s *DockerSuite) TestRunNetHost(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
 
 	hostNet, err := os.Readlink("/proc/1/ns/net")
 	if err != nil {
@@ -2594,18 +2568,18 @@ func (s *DockerSuite) TestRunNetHost(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunNetHostTwiceSameName(c *check.C) {
+func (s *DockerSuite) TestRunNetHostTwiceSameName(c *testing.T) {
 	// TODO Windows. As Windows networking evolves and converges towards
 	// CNM, this test may be possible to enable on Windows.
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
 
 	dockerCmd(c, "run", "--rm", "--name=thost", "--net=host", "busybox", "true")
 	dockerCmd(c, "run", "--rm", "--name=thost", "--net=host", "busybox", "true")
 }
 
-func (s *DockerSuite) TestRunNetContainerWhichHost(c *check.C) {
+func (s *DockerSuite) TestRunNetContainerWhichHost(c *testing.T) {
 	// Not applicable on Windows as uses Unix-specific capabilities
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
 
 	hostNet, err := os.Readlink("/proc/1/ns/net")
 	if err != nil {
@@ -2621,7 +2595,7 @@ func (s *DockerSuite) TestRunNetContainerWhichHost(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunAllowPortRangeThroughPublish(c *check.C) {
+func (s *DockerSuite) TestRunAllowPortRangeThroughPublish(c *testing.T) {
 	// TODO Windows. This may be possible to enable in the future. However,
 	// Windows does not currently support --expose, or populate the network
 	// settings seen through inspect.
@@ -2633,19 +2607,19 @@ func (s *DockerSuite) TestRunAllowPortRangeThroughPublish(c *check.C) {
 
 	var ports nat.PortMap
 	err := json.Unmarshal([]byte(portstr), &ports)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to unmarshal: %v", portstr))
+	assert.NilError(c, err, "failed to unmarshal: %v", portstr)
 	for port, binding := range ports {
 		portnum, _ := strconv.Atoi(strings.Split(string(port), "/")[0])
 		if portnum < 3000 || portnum > 3003 {
 			c.Fatalf("Port %d is out of range ", portnum)
 		}
-		if binding == nil || len(binding) != 1 || len(binding[0].HostPort) == 0 {
+		if len(binding) == 0 || len(binding[0].HostPort) == 0 {
 			c.Fatal("Port is not mapped for the port "+port, out)
 		}
 	}
 }
 
-func (s *DockerSuite) TestRunSetDefaultRestartPolicy(c *check.C) {
+func (s *DockerSuite) TestRunSetDefaultRestartPolicy(c *testing.T) {
 	runSleepingContainer(c, "--name=testrunsetdefaultrestartpolicy")
 	out := inspectField(c, "testrunsetdefaultrestartpolicy", "HostConfig.RestartPolicy.Name")
 	if out != "no" {
@@ -2653,14 +2627,14 @@ func (s *DockerSuite) TestRunSetDefaultRestartPolicy(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunRestartMaxRetries(c *check.C) {
+func (s *DockerSuite) TestRunRestartMaxRetries(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "-d", "--restart=on-failure:3", "busybox", "false")
 	timeout := 10 * time.Second
 	if testEnv.OSType == "windows" {
 		timeout = 120 * time.Second
 	}
 
-	id := strings.TrimSpace(string(out))
+	id := strings.TrimSpace(out)
 	if err := waitInspect(id, "{{ .State.Restarting }} {{ .State.Running }}", "false false", timeout); err != nil {
 		c.Fatal(err)
 	}
@@ -2676,11 +2650,11 @@ func (s *DockerSuite) TestRunRestartMaxRetries(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunContainerWithWritableRootfs(c *check.C) {
+func (s *DockerSuite) TestRunContainerWithWritableRootfs(c *testing.T) {
 	dockerCmd(c, "run", "--rm", "busybox", "touch", "/file")
 }
 
-func (s *DockerSuite) TestRunContainerWithReadonlyRootfs(c *check.C) {
+func (s *DockerSuite) TestRunContainerWithReadonlyRootfs(c *testing.T) {
 	// Not applicable on Windows which does not support --read-only
 	testRequires(c, DaemonIsLinux, UserNamespaceROMount)
 
@@ -2692,7 +2666,7 @@ func (s *DockerSuite) TestRunContainerWithReadonlyRootfs(c *check.C) {
 	testReadOnlyFile(c, testPriv, "/file", "/etc/hosts", "/etc/resolv.conf", "/etc/hostname")
 }
 
-func (s *DockerSuite) TestPermissionsPtsReadonlyRootfs(c *check.C) {
+func (s *DockerSuite) TestPermissionsPtsReadonlyRootfs(c *testing.T) {
 	// Not applicable on Windows due to use of Unix specific functionality, plus
 	// the use of --read-only which is not supported.
 	testRequires(c, DaemonIsLinux, UserNamespaceROMount)
@@ -2703,19 +2677,19 @@ func (s *DockerSuite) TestPermissionsPtsReadonlyRootfs(c *check.C) {
 		c.Fatal("Could not obtain mounts when checking /dev/pts mntpnt.")
 	}
 	expected := "type devpts (rw,"
-	if !strings.Contains(string(out), expected) {
+	if !strings.Contains(out, expected) {
 		c.Fatalf("expected output to contain %s but contains %s", expected, out)
 	}
 }
 
-func testReadOnlyFile(c *check.C, testPriv bool, filenames ...string) {
+func testReadOnlyFile(c *testing.T, testPriv bool, filenames ...string) {
 	touch := "touch " + strings.Join(filenames, " ")
 	out, _, err := dockerCmdWithError("run", "--read-only", "--rm", "busybox", "sh", "-c", touch)
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	for _, f := range filenames {
 		expected := "touch: " + f + ": Read-only file system"
-		c.Assert(out, checker.Contains, expected)
+		assert.Assert(c, strings.Contains(out, expected))
 	}
 
 	if !testPriv {
@@ -2723,47 +2697,47 @@ func testReadOnlyFile(c *check.C, testPriv bool, filenames ...string) {
 	}
 
 	out, _, err = dockerCmdWithError("run", "--read-only", "--privileged", "--rm", "busybox", "sh", "-c", touch)
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	for _, f := range filenames {
 		expected := "touch: " + f + ": Read-only file system"
-		c.Assert(out, checker.Contains, expected)
+		assert.Assert(c, strings.Contains(out, expected))
 	}
 }
 
-func (s *DockerSuite) TestRunContainerWithReadonlyEtcHostsAndLinkedContainer(c *check.C) {
+func (s *DockerSuite) TestRunContainerWithReadonlyEtcHostsAndLinkedContainer(c *testing.T) {
 	// Not applicable on Windows which does not support --link
 	testRequires(c, DaemonIsLinux, UserNamespaceROMount)
 
 	dockerCmd(c, "run", "-d", "--name", "test-etc-hosts-ro-linked", "busybox", "top")
 
 	out, _ := dockerCmd(c, "run", "--read-only", "--link", "test-etc-hosts-ro-linked:testlinked", "busybox", "cat", "/etc/hosts")
-	if !strings.Contains(string(out), "testlinked") {
+	if !strings.Contains(out, "testlinked") {
 		c.Fatal("Expected /etc/hosts to be updated even if --read-only enabled")
 	}
 }
 
-func (s *DockerSuite) TestRunContainerWithReadonlyRootfsWithDNSFlag(c *check.C) {
+func (s *DockerSuite) TestRunContainerWithReadonlyRootfsWithDNSFlag(c *testing.T) {
 	// Not applicable on Windows which does not support either --read-only or --dns.
 	testRequires(c, DaemonIsLinux, UserNamespaceROMount)
 
 	out, _ := dockerCmd(c, "run", "--read-only", "--dns", "1.1.1.1", "busybox", "/bin/cat", "/etc/resolv.conf")
-	if !strings.Contains(string(out), "1.1.1.1") {
+	if !strings.Contains(out, "1.1.1.1") {
 		c.Fatal("Expected /etc/resolv.conf to be updated even if --read-only enabled and --dns flag used")
 	}
 }
 
-func (s *DockerSuite) TestRunContainerWithReadonlyRootfsWithAddHostFlag(c *check.C) {
+func (s *DockerSuite) TestRunContainerWithReadonlyRootfsWithAddHostFlag(c *testing.T) {
 	// Not applicable on Windows which does not support --read-only
 	testRequires(c, DaemonIsLinux, UserNamespaceROMount)
 
 	out, _ := dockerCmd(c, "run", "--read-only", "--add-host", "testreadonly:127.0.0.1", "busybox", "/bin/cat", "/etc/hosts")
-	if !strings.Contains(string(out), "testreadonly") {
+	if !strings.Contains(out, "testreadonly") {
 		c.Fatal("Expected /etc/hosts to be updated even if --read-only enabled and --add-host flag used")
 	}
 }
 
-func (s *DockerSuite) TestRunVolumesFromRestartAfterRemoved(c *check.C) {
+func (s *DockerSuite) TestRunVolumesFromRestartAfterRemoved(c *testing.T) {
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 	runSleepingContainer(c, "--name=voltest", "-v", prefix+"/foo")
 	runSleepingContainer(c, "--name=restarter", "--volumes-from", "voltest")
@@ -2776,7 +2750,7 @@ func (s *DockerSuite) TestRunVolumesFromRestartAfterRemoved(c *check.C) {
 }
 
 // run container with --rm should remove container if exit code != 0
-func (s *DockerSuite) TestRunContainerWithRmFlagExitCodeNotEqualToZero(c *check.C) {
+func (s *DockerSuite) TestRunContainerWithRmFlagExitCodeNotEqualToZero(c *testing.T) {
 	existingContainers := ExistingContainerIDs(c)
 	name := "flowers"
 	cli.Docker(cli.Args("run", "--name", name, "--rm", "busybox", "ls", "/notexists")).Assert(c, icmd.Expected{
@@ -2790,7 +2764,7 @@ func (s *DockerSuite) TestRunContainerWithRmFlagExitCodeNotEqualToZero(c *check.
 	}
 }
 
-func (s *DockerSuite) TestRunContainerWithRmFlagCannotStartContainer(c *check.C) {
+func (s *DockerSuite) TestRunContainerWithRmFlagCannotStartContainer(c *testing.T) {
 	existingContainers := ExistingContainerIDs(c)
 	name := "sparkles"
 	cli.Docker(cli.Args("run", "--name", name, "--rm", "busybox", "commandNotFound")).Assert(c, icmd.Expected{
@@ -2803,15 +2777,15 @@ func (s *DockerSuite) TestRunContainerWithRmFlagCannotStartContainer(c *check.C)
 	}
 }
 
-func (s *DockerSuite) TestRunPIDHostWithChildIsKillable(c *check.C) {
+func (s *DockerSuite) TestRunPIDHostWithChildIsKillable(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	name := "ibuildthecloud"
 	dockerCmd(c, "run", "-d", "--pid=host", "--name", name, "busybox", "sh", "-c", "sleep 30; echo hi")
 
-	c.Assert(waitRun(name), check.IsNil)
+	assert.Assert(c, waitRun(name) == nil)
 
-	errchan := make(chan error)
+	errchan := make(chan error, 1)
 	go func() {
 		if out, _, err := dockerCmdWithError("kill", name); err != nil {
 			errchan <- fmt.Errorf("%v:\n%s", err, out)
@@ -2820,25 +2794,23 @@ func (s *DockerSuite) TestRunPIDHostWithChildIsKillable(c *check.C) {
 	}()
 	select {
 	case err := <-errchan:
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(5 * time.Second):
 		c.Fatal("Kill container timed out")
 	}
 }
 
-func (s *DockerSuite) TestRunWithTooSmallMemoryLimit(c *check.C) {
-	// TODO Windows. This may be possible to enable once Windows supports
-	// memory limits on containers
+func (s *DockerSuite) TestRunWithTooSmallMemoryLimit(c *testing.T) {
+	// TODO Windows. This may be possible to enable once Windows supports memory limits on containers
 	testRequires(c, DaemonIsLinux)
-	// this memory limit is 1 byte less than the min, which is 4MB
-	// https://github.com/docker/docker/blob/v1.5.0/daemon/create.go#L22
-	out, _, err := dockerCmdWithError("run", "-m", "4194303", "busybox")
-	if err == nil || !strings.Contains(out, "Minimum memory limit allowed is 4MB") {
+	// this memory limit is 1 byte less than the min (daemon.linuxMinMemory), which is 6MB (6291456 bytes)
+	out, _, err := dockerCmdWithError("create", "-m", "6291455", "busybox")
+	if err == nil || !strings.Contains(out, "Minimum memory limit allowed is 6MB") {
 		c.Fatalf("expected run to fail when using too low a memory limit: %q", out)
 	}
 }
 
-func (s *DockerSuite) TestRunWriteToProcAsound(c *check.C) {
+func (s *DockerSuite) TestRunWriteToProcAsound(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 	_, code, err := dockerCmdWithError("run", "busybox", "sh", "-c", "echo 111 >> /proc/asound/version")
@@ -2847,7 +2819,7 @@ func (s *DockerSuite) TestRunWriteToProcAsound(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunReadProcTimer(c *check.C) {
+func (s *DockerSuite) TestRunReadProcTimer(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 	out, code, err := dockerCmdWithError("run", "busybox", "cat", "/proc/timer_stats")
@@ -2862,7 +2834,7 @@ func (s *DockerSuite) TestRunReadProcTimer(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunReadProcLatency(c *check.C) {
+func (s *DockerSuite) TestRunReadProcLatency(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 	// some kernels don't have this configured so skip the test if this file is not found
@@ -2883,7 +2855,7 @@ func (s *DockerSuite) TestRunReadProcLatency(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunReadFilteredProc(c *check.C) {
+func (s *DockerSuite) TestRunReadFilteredProc(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, Apparmor, DaemonIsLinux, NotUserNamespace)
 
@@ -2906,7 +2878,7 @@ func (s *DockerSuite) TestRunReadFilteredProc(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestMountIntoProc(c *check.C) {
+func (s *DockerSuite) TestMountIntoProc(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 	_, code, err := dockerCmdWithError("run", "-v", "/proc//sys", "busybox", "true")
@@ -2915,14 +2887,14 @@ func (s *DockerSuite) TestMountIntoProc(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestMountIntoSys(c *check.C) {
+func (s *DockerSuite) TestMountIntoSys(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, NotUserNamespace)
 	dockerCmd(c, "run", "-v", "/sys/fs/cgroup", "busybox", "true")
 }
 
-func (s *DockerSuite) TestRunUnshareProc(c *check.C) {
+func (s *DockerSuite) TestRunUnshareProc(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, Apparmor, DaemonIsLinux, NotUserNamespace)
 
@@ -2931,7 +2903,7 @@ func (s *DockerSuite) TestRunUnshareProc(c *check.C) {
 
 	go func() {
 		name := "acidburn"
-		out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:jessie", "unshare", "-p", "-m", "-f", "-r", "--mount-proc=/proc", "mount")
+		out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:bullseye-slim", "unshare", "-p", "-m", "-f", "-r", "--mount-proc=/proc", "mount")
 		if err == nil ||
 			!(strings.Contains(strings.ToLower(out), "permission denied") ||
 				strings.Contains(strings.ToLower(out), "operation not permitted")) {
@@ -2943,7 +2915,7 @@ func (s *DockerSuite) TestRunUnshareProc(c *check.C) {
 
 	go func() {
 		name := "cereal"
-		out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:jessie", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
+		out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp=unconfined", "debian:bullseye-slim", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
 		if err == nil ||
 			!(strings.Contains(strings.ToLower(out), "mount: cannot mount none") ||
 				strings.Contains(strings.ToLower(out), "permission denied") ||
@@ -2957,7 +2929,7 @@ func (s *DockerSuite) TestRunUnshareProc(c *check.C) {
 	/* Ensure still fails if running privileged with the default policy */
 	go func() {
 		name := "crashoverride"
-		out, _, err := dockerCmdWithError("run", "--privileged", "--security-opt", "seccomp=unconfined", "--security-opt", "apparmor=docker-default", "--name", name, "debian:jessie", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
+		out, _, err := dockerCmdWithError("run", "--privileged", "--security-opt", "seccomp=unconfined", "--security-opt", "apparmor=docker-default", "--name", name, "debian:bullseye-slim", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
 		if err == nil ||
 			!(strings.Contains(strings.ToLower(out), "mount: cannot mount none") ||
 				strings.Contains(strings.ToLower(out), "permission denied") ||
@@ -2980,7 +2952,7 @@ func (s *DockerSuite) TestRunUnshareProc(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunPublishPort(c *check.C) {
+func (s *DockerSuite) TestRunPublishPort(c *testing.T) {
 	// TODO Windows: This may be possible once Windows moves to libnetwork and CNM
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "--name", "test", "--expose", "8080", "busybox", "top")
@@ -2992,7 +2964,7 @@ func (s *DockerSuite) TestRunPublishPort(c *check.C) {
 }
 
 // Issue #10184.
-func (s *DockerSuite) TestDevicePermissions(c *check.C) {
+func (s *DockerSuite) TestDevicePermissions(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 	const permissions = "crw-rw-rw-"
@@ -3005,7 +2977,7 @@ func (s *DockerSuite) TestDevicePermissions(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapAddCHOWN(c *check.C) {
+func (s *DockerSuite) TestRunCapAddCHOWN(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "--cap-drop=ALL", "--cap-add=CHOWN", "busybox", "sh", "-c", "adduser -D -H newuser && chown newuser /home && echo ok")
@@ -3016,7 +2988,7 @@ func (s *DockerSuite) TestRunCapAddCHOWN(c *check.C) {
 }
 
 // https://github.com/docker/docker/pull/14498
-func (s *DockerSuite) TestVolumeFromMixedRWOptions(c *check.C) {
+func (s *DockerSuite) TestVolumeFromMixedRWOptions(c *testing.T) {
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 
 	dockerCmd(c, "run", "--name", "parent", "-v", prefix+"/test", "busybox", "true")
@@ -3026,20 +2998,20 @@ func (s *DockerSuite) TestVolumeFromMixedRWOptions(c *check.C) {
 
 	if testEnv.OSType != "windows" {
 		mRO, err := inspectMountPoint("test-volumes-1", prefix+slash+"test")
-		c.Assert(err, checker.IsNil, check.Commentf("failed to inspect mount point"))
+		assert.NilError(c, err, "failed to inspect mount point")
 		if mRO.RW {
 			c.Fatalf("Expected RO volume was RW")
 		}
 	}
 
 	mRW, err := inspectMountPoint("test-volumes-2", prefix+slash+"test")
-	c.Assert(err, checker.IsNil, check.Commentf("failed to inspect mount point"))
+	assert.NilError(c, err, "failed to inspect mount point")
 	if !mRW.RW {
 		c.Fatalf("Expected RW volume was RO")
 	}
 }
 
-func (s *DockerSuite) TestRunWriteFilteredProc(c *check.C) {
+func (s *DockerSuite) TestRunWriteFilteredProc(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, Apparmor, DaemonIsLinux, NotUserNamespace)
 
@@ -3066,9 +3038,9 @@ func (s *DockerSuite) TestRunWriteFilteredProc(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunNetworkFilesBindMount(c *check.C) {
+func (s *DockerSuite) TestRunNetworkFilesBindMount(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	expected := "test123"
 
@@ -3090,9 +3062,9 @@ func (s *DockerSuite) TestRunNetworkFilesBindMount(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunNetworkFilesBindMountRO(c *check.C) {
+func (s *DockerSuite) TestRunNetworkFilesBindMountRO(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	filename := createTmpFile(c, "test123")
 	defer os.Remove(filename)
@@ -3112,9 +3084,9 @@ func (s *DockerSuite) TestRunNetworkFilesBindMountRO(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunNetworkFilesBindMountROFilesystem(c *check.C) {
+func (s *DockerSuite) TestRunNetworkFilesBindMountROFilesystem(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
-	testRequires(c, SameHostDaemon, DaemonIsLinux, UserNamespaceROMount)
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, UserNamespaceROMount)
 
 	filename := createTmpFile(c, "test123")
 	defer os.Remove(filename)
@@ -3141,13 +3113,13 @@ func (s *DockerSuite) TestRunNetworkFilesBindMountROFilesystem(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestPtraceContainerProcsFromHost(c *check.C) {
+func (s *DockerSuite) TestPtraceContainerProcsFromHost(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), check.IsNil)
+	assert.NilError(c, waitRun(id))
 	pid1 := inspectField(c, id, "State.Pid")
 
 	_, err := os.Readlink(fmt.Sprintf("/proc/%s/ns/net", pid1))
@@ -3156,9 +3128,9 @@ func (s *DockerSuite) TestPtraceContainerProcsFromHost(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestAppArmorDeniesPtrace(c *check.C) {
+func (s *DockerSuite) TestAppArmorDeniesPtrace(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
-	testRequires(c, SameHostDaemon, Apparmor, DaemonIsLinux)
+	testRequires(c, testEnv.IsLocalDaemon, Apparmor, DaemonIsLinux)
 
 	// Run through 'sh' so we are NOT pid 1. Pid 1 may be able to trace
 	// itself, but pid>1 should not be able to trace pid1.
@@ -3168,9 +3140,9 @@ func (s *DockerSuite) TestAppArmorDeniesPtrace(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestAppArmorTraceSelf(c *check.C) {
+func (s *DockerSuite) TestAppArmorTraceSelf(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
-	testRequires(c, DaemonIsLinux, SameHostDaemon, Apparmor)
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, Apparmor)
 
 	_, exitCode, _ := dockerCmdWithError("run", "busybox", "readlink", "/proc/1/ns/net")
 	if exitCode != 0 {
@@ -3178,9 +3150,9 @@ func (s *DockerSuite) TestAppArmorTraceSelf(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestAppArmorDeniesChmodProc(c *check.C) {
+func (s *DockerSuite) TestAppArmorDeniesChmodProc(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
-	testRequires(c, SameHostDaemon, Apparmor, DaemonIsLinux, NotUserNamespace)
+	testRequires(c, testEnv.IsLocalDaemon, Apparmor, DaemonIsLinux, NotUserNamespace)
 	_, exitCode, _ := dockerCmdWithError("run", "busybox", "chmod", "744", "/proc/cpuinfo")
 	if exitCode == 0 {
 		// If our test failed, attempt to repair the host system...
@@ -3191,7 +3163,7 @@ func (s *DockerSuite) TestAppArmorDeniesChmodProc(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunCapAddSYSTIME(c *check.C) {
+func (s *DockerSuite) TestRunCapAddSYSTIME(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 
@@ -3199,31 +3171,31 @@ func (s *DockerSuite) TestRunCapAddSYSTIME(c *check.C) {
 }
 
 // run create container failed should clean up the container
-func (s *DockerSuite) TestRunCreateContainerFailedCleanUp(c *check.C) {
+func (s *DockerSuite) TestRunCreateContainerFailedCleanUp(c *testing.T) {
 	// TODO Windows. This may be possible to enable once link is supported
 	testRequires(c, DaemonIsLinux)
 	name := "unique_name"
 	_, _, err := dockerCmdWithError("run", "--name", name, "--link", "nothing:nothing", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("Expected docker run to fail!"))
+	assert.Assert(c, err != nil, "Expected docker run to fail!")
 
 	containerID, err := inspectFieldWithError(name, "Id")
-	c.Assert(err, checker.NotNil, check.Commentf("Expected not to have this container: %s!", containerID))
-	c.Assert(containerID, check.Equals, "", check.Commentf("Expected not to have this container: %s!", containerID))
+	assert.Assert(c, err != nil, "Expected not to have this container: %s!", containerID)
+	assert.Equal(c, containerID, "", fmt.Sprintf("Expected not to have this container: %s!", containerID))
 }
 
-func (s *DockerSuite) TestRunNamedVolume(c *check.C) {
+func (s *DockerSuite) TestRunNamedVolume(c *testing.T) {
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "--name=test", "-v", "testing:"+prefix+"/foo", "busybox", "sh", "-c", "echo hello > "+prefix+"/foo/bar")
 
 	out, _ := dockerCmd(c, "run", "--volumes-from", "test", "busybox", "sh", "-c", "cat "+prefix+"/foo/bar")
-	c.Assert(strings.TrimSpace(out), check.Equals, "hello")
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 
 	out, _ = dockerCmd(c, "run", "-v", "testing:"+prefix+"/foo", "busybox", "sh", "-c", "cat "+prefix+"/foo/bar")
-	c.Assert(strings.TrimSpace(out), check.Equals, "hello")
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 }
 
-func (s *DockerSuite) TestRunWithUlimits(c *check.C) {
+func (s *DockerSuite) TestRunWithUlimits(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 
@@ -3234,7 +3206,7 @@ func (s *DockerSuite) TestRunWithUlimits(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunContainerWithCgroupParent(c *check.C) {
+func (s *DockerSuite) TestRunContainerWithCgroupParent(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 
@@ -3245,14 +3217,14 @@ func (s *DockerSuite) TestRunContainerWithCgroupParent(c *check.C) {
 	testRunContainerWithCgroupParent(c, "/cgroup-parent/test", "cgroup-test-absolute")
 }
 
-func testRunContainerWithCgroupParent(c *check.C, cgroupParent, name string) {
+func testRunContainerWithCgroupParent(c *testing.T, cgroupParent, name string) {
 	out, _, err := dockerCmdWithError("run", "--cgroup-parent", cgroupParent, "--name", name, "busybox", "cat", "/proc/self/cgroup")
 	if err != nil {
-		c.Fatalf("unexpected failure when running container with --cgroup-parent option - %s\n%v", string(out), err)
+		c.Fatalf("unexpected failure when running container with --cgroup-parent option - %s\n%v", out, err)
 	}
-	cgroupPaths := ParseCgroupPaths(string(out))
+	cgroupPaths := ParseCgroupPaths(out)
 	if len(cgroupPaths) == 0 {
-		c.Fatalf("unexpected output - %q", string(out))
+		c.Fatalf("unexpected output - %q", out)
 	}
 	id := getIDByName(c, name)
 	expectedCgroup := path.Join(cgroupParent, id)
@@ -3269,7 +3241,7 @@ func testRunContainerWithCgroupParent(c *check.C, cgroupParent, name string) {
 }
 
 // TestRunInvalidCgroupParent checks that a specially-crafted cgroup parent doesn't cause Docker to crash or start modifying /.
-func (s *DockerSuite) TestRunInvalidCgroupParent(c *check.C) {
+func (s *DockerSuite) TestRunInvalidCgroupParent(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	testRequires(c, DaemonIsLinux)
 
@@ -3278,11 +3250,11 @@ func (s *DockerSuite) TestRunInvalidCgroupParent(c *check.C) {
 	testRunInvalidCgroupParent(c, "/../../../../../../../../SHOULD_NOT_EXIST", "/SHOULD_NOT_EXIST", "cgroup-absolute-invalid-test")
 }
 
-func testRunInvalidCgroupParent(c *check.C, cgroupParent, cleanCgroupParent, name string) {
+func testRunInvalidCgroupParent(c *testing.T, cgroupParent, cleanCgroupParent, name string) {
 	out, _, err := dockerCmdWithError("run", "--cgroup-parent", cgroupParent, "--name", name, "busybox", "cat", "/proc/self/cgroup")
 	if err != nil {
 		// XXX: This may include a daemon crash.
-		c.Fatalf("unexpected failure when running container with --cgroup-parent option - %s\n%v", string(out), err)
+		c.Fatalf("unexpected failure when running container with --cgroup-parent option - %s\n%v", out, err)
 	}
 
 	// We expect "/SHOULD_NOT_EXIST" to not exist. If not, we have a security issue.
@@ -3290,9 +3262,9 @@ func testRunInvalidCgroupParent(c *check.C, cgroupParent, cleanCgroupParent, nam
 		c.Fatalf("SECURITY: --cgroup-parent with ../../ relative paths cause files to be created in the host (this is bad) !!")
 	}
 
-	cgroupPaths := ParseCgroupPaths(string(out))
+	cgroupPaths := ParseCgroupPaths(out)
 	if len(cgroupPaths) == 0 {
-		c.Fatalf("unexpected output - %q", string(out))
+		c.Fatalf("unexpected output - %q", out)
 	}
 	id := getIDByName(c, name)
 	expectedCgroup := path.Join(cleanCgroupParent, id)
@@ -3308,7 +3280,7 @@ func testRunInvalidCgroupParent(c *check.C, cgroupParent, cleanCgroupParent, nam
 	}
 }
 
-func (s *DockerSuite) TestRunContainerWithCgroupMountRO(c *check.C) {
+func (s *DockerSuite) TestRunContainerWithCgroupMountRO(c *testing.T) {
 	// Not applicable on Windows as uses Unix specific functionality
 	// --read-only + userns has remount issues
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
@@ -3324,7 +3296,7 @@ func (s *DockerSuite) TestRunContainerWithCgroupMountRO(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunContainerNetworkModeToSelf(c *check.C) {
+func (s *DockerSuite) TestRunContainerNetworkModeToSelf(c *testing.T) {
 	// Not applicable on Windows which does not support --net=container
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "--name=me", "--net=container:me", "busybox", "true")
@@ -3333,7 +3305,7 @@ func (s *DockerSuite) TestRunContainerNetworkModeToSelf(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunContainerNetModeWithDNSMacHosts(c *check.C) {
+func (s *DockerSuite) TestRunContainerNetModeWithDNSMacHosts(c *testing.T) {
 	// Not applicable on Windows which does not support --net=container
 	testRequires(c, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "-d", "--name", "parent", "busybox", "top")
@@ -3357,7 +3329,7 @@ func (s *DockerSuite) TestRunContainerNetModeWithDNSMacHosts(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunContainerNetModeWithExposePort(c *check.C) {
+func (s *DockerSuite) TestRunContainerNetModeWithExposePort(c *testing.T) {
 	// Not applicable on Windows which does not support --net=container
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "--name", "parent", "busybox", "top")
@@ -3378,7 +3350,7 @@ func (s *DockerSuite) TestRunContainerNetModeWithExposePort(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunLinkToContainerNetMode(c *check.C) {
+func (s *DockerSuite) TestRunLinkToContainerNetMode(c *testing.T) {
 	// Not applicable on Windows which does not support --net=container or --link
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "--name", "test", "-d", "busybox", "top")
@@ -3388,7 +3360,7 @@ func (s *DockerSuite) TestRunLinkToContainerNetMode(c *check.C) {
 	dockerCmd(c, "run", "-d", "--link=child:child", "busybox", "top")
 }
 
-func (s *DockerSuite) TestRunLoopbackOnlyExistsWhenNetworkingDisabled(c *check.C) {
+func (s *DockerSuite) TestRunLoopbackOnlyExistsWhenNetworkingDisabled(c *testing.T) {
 	// TODO Windows: This may be possible to convert.
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "--net=none", "busybox", "ip", "-o", "-4", "a", "show", "up")
@@ -3414,7 +3386,7 @@ func (s *DockerSuite) TestRunLoopbackOnlyExistsWhenNetworkingDisabled(c *check.C
 }
 
 // Issue #4681
-func (s *DockerSuite) TestRunLoopbackWhenNetworkDisabled(c *check.C) {
+func (s *DockerSuite) TestRunLoopbackWhenNetworkDisabled(c *testing.T) {
 	if testEnv.OSType == "windows" {
 		dockerCmd(c, "run", "--net=none", testEnv.PlatformDefaults.BaseImage, "ping", "-n", "1", "127.0.0.1")
 	} else {
@@ -3422,9 +3394,9 @@ func (s *DockerSuite) TestRunLoopbackWhenNetworkDisabled(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunModeNetContainerHostname(c *check.C) {
+func (s *DockerSuite) TestRunModeNetContainerHostname(c *testing.T) {
 	// Windows does not support --net=container
-	testRequires(c, DaemonIsLinux, ExecSupport)
+	testRequires(c, DaemonIsLinux)
 
 	dockerCmd(c, "run", "-i", "-d", "--name", "parent", "busybox", "top")
 	out, _ := dockerCmd(c, "exec", "parent", "cat", "/etc/hostname")
@@ -3435,7 +3407,7 @@ func (s *DockerSuite) TestRunModeNetContainerHostname(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunNetworkNotInitializedNoneMode(c *check.C) {
+func (s *DockerSuite) TestRunNetworkNotInitializedNoneMode(c *testing.T) {
 	// TODO Windows: Network settings are not currently propagated. This may
 	// be resolved in the future with the move to libnetwork and CNM.
 	testRequires(c, DaemonIsLinux)
@@ -3447,7 +3419,7 @@ func (s *DockerSuite) TestRunNetworkNotInitializedNoneMode(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestTwoContainersInNetHost(c *check.C) {
+func (s *DockerSuite) TestTwoContainersInNetHost(c *testing.T) {
 	// Not applicable as Windows does not support --net=host
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotUserNamespace)
 	dockerCmd(c, "run", "-d", "--net=host", "--name=first", "busybox", "top")
@@ -3456,24 +3428,24 @@ func (s *DockerSuite) TestTwoContainersInNetHost(c *check.C) {
 	dockerCmd(c, "stop", "second")
 }
 
-func (s *DockerSuite) TestContainersInUserDefinedNetwork(c *check.C) {
+func (s *DockerSuite) TestContainersInUserDefinedNetwork(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork")
 	dockerCmd(c, "run", "-d", "--net=testnetwork", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 	dockerCmd(c, "run", "-t", "--net=testnetwork", "--name=second", "busybox", "ping", "-c", "1", "first")
 }
 
-func (s *DockerSuite) TestContainersInMultipleNetworks(c *check.C) {
+func (s *DockerSuite) TestContainersInMultipleNetworks(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	// Create 2 networks using bridge driver
 	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
 	// Run and connect containers to testnetwork1
 	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 	// Check connectivity between containers in testnetwork2
 	dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1")
 	// Connect containers to testnetwork2
@@ -3483,52 +3455,52 @@ func (s *DockerSuite) TestContainersInMultipleNetworks(c *check.C) {
 	dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2")
 }
 
-func (s *DockerSuite) TestContainersNetworkIsolation(c *check.C) {
+func (s *DockerSuite) TestContainersNetworkIsolation(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	// Create 2 networks using bridge driver
 	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
 	// Run 1 container in testnetwork1 and another in testnetwork2
 	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 	dockerCmd(c, "run", "-d", "--net=testnetwork2", "--name=second", "busybox", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 
 	// Check Isolation between containers : ping must fail
 	_, _, err := dockerCmdWithError("exec", "first", "ping", "-c", "1", "second")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	// Connect first container to testnetwork2
 	dockerCmd(c, "network", "connect", "testnetwork2", "first")
 	// ping must succeed now
 	_, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// Disconnect first container from testnetwork2
 	dockerCmd(c, "network", "disconnect", "testnetwork2", "first")
 	// ping must fail again
 	_, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 }
 
-func (s *DockerSuite) TestNetworkRmWithActiveContainers(c *check.C) {
+func (s *DockerSuite) TestNetworkRmWithActiveContainers(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	// Create 2 networks using bridge driver
 	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 	// Run and connect containers to testnetwork1
 	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 	// Network delete with active containers must fail
 	_, _, err := dockerCmdWithError("network", "rm", "testnetwork1")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	dockerCmd(c, "stop", "first")
 	_, _, err = dockerCmdWithError("network", "rm", "testnetwork1")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 }
 
-func (s *DockerSuite) TestContainerRestartInMultipleNetworks(c *check.C) {
+func (s *DockerSuite) TestContainerRestartInMultipleNetworks(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	// Create 2 networks using bridge driver
 	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
@@ -3536,9 +3508,9 @@ func (s *DockerSuite) TestContainerRestartInMultipleNetworks(c *check.C) {
 
 	// Run and connect containers to testnetwork1
 	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 	// Check connectivity between containers in testnetwork2
 	dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1")
 	// Connect containers to testnetwork2
@@ -3550,9 +3522,9 @@ func (s *DockerSuite) TestContainerRestartInMultipleNetworks(c *check.C) {
 	// Stop second container and test ping failures on both networks
 	dockerCmd(c, "stop", "second")
 	_, _, err := dockerCmdWithError("exec", "first", "ping", "-c", "1", "second.testnetwork1")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	_, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second.testnetwork2")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// Start second container and connectivity must be restored on both networks
 	dockerCmd(c, "start", "second")
@@ -3560,99 +3532,98 @@ func (s *DockerSuite) TestContainerRestartInMultipleNetworks(c *check.C) {
 	dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2")
 }
 
-func (s *DockerSuite) TestContainerWithConflictingHostNetworks(c *check.C) {
+func (s *DockerSuite) TestContainerWithConflictingHostNetworks(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	// Run a container with --net=host
 	dockerCmd(c, "run", "-d", "--net=host", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 
 	// Create a network using bridge driver
 	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 
 	// Connecting to the user defined network must fail
 	_, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "first")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 }
 
-func (s *DockerSuite) TestContainerWithConflictingSharedNetwork(c *check.C) {
+func (s *DockerSuite) TestContainerWithConflictingSharedNetwork(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 	// Run second container in first container's network namespace
 	dockerCmd(c, "run", "-d", "--net=container:first", "--name=second", "busybox", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 
 	// Create a network using bridge driver
 	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 
 	// Connecting to the user defined network must fail
 	out, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "second")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, runconfig.ErrConflictSharedNetwork.Error())
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictSharedNetwork.Error()))
 }
 
-func (s *DockerSuite) TestContainerWithConflictingNoneNetwork(c *check.C) {
+func (s *DockerSuite) TestContainerWithConflictingNoneNetwork(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "-d", "--net=none", "--name=first", "busybox", "top")
-	c.Assert(waitRun("first"), check.IsNil)
+	assert.Assert(c, waitRun("first") == nil)
 
 	// Create a network using bridge driver
 	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 
 	// Connecting to the user defined network must fail
 	out, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "first")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, runconfig.ErrConflictNoNetwork.Error())
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, runconfig.ErrConflictNoNetwork.Error()))
 	// create a container connected to testnetwork1
 	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
-	c.Assert(waitRun("second"), check.IsNil)
+	assert.Assert(c, waitRun("second") == nil)
 
 	// Connect second container to none network. it must fail as well
 	_, _, err = dockerCmdWithError("network", "connect", "none", "second")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 }
 
 // #11957 - stdin with no tty does not exit if stdin is not closed even though container exited
-func (s *DockerSuite) TestRunStdinBlockedAfterContainerExit(c *check.C) {
+func (s *DockerSuite) TestRunStdinBlockedAfterContainerExit(c *testing.T) {
 	cmd := exec.Command(dockerBinary, "run", "-i", "--name=test", "busybox", "true")
 	in, err := cmd.StdinPipe()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	defer in.Close()
 	stdout := bytes.NewBuffer(nil)
 	cmd.Stdout = stdout
 	cmd.Stderr = stdout
-	c.Assert(cmd.Start(), check.IsNil)
+	assert.Assert(c, cmd.Start() == nil)
 
-	waitChan := make(chan error)
+	waitChan := make(chan error, 1)
 	go func() {
 		waitChan <- cmd.Wait()
 	}()
 
 	select {
 	case err := <-waitChan:
-		c.Assert(err, check.IsNil, check.Commentf(stdout.String()))
+		assert.Assert(c, err == nil, stdout.String())
 	case <-time.After(30 * time.Second):
 		c.Fatal("timeout waiting for command to exit")
 	}
 }
 
-func (s *DockerSuite) TestRunWrongCpusetCpusFlagValue(c *check.C) {
+func (s *DockerSuite) TestRunWrongCpusetCpusFlagValue(c *testing.T) {
 	// TODO Windows: This needs validation (error out) in the daemon.
 	testRequires(c, DaemonIsLinux)
 	out, exitCode, err := dockerCmdWithError("run", "--cpuset-cpus", "1-10,11--", "busybox", "true")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	expected := "Error response from daemon: Invalid value 1-10,11-- for cpuset cpus.\n"
 	if !(strings.Contains(out, expected) || exitCode == 125) {
 		c.Fatalf("Expected output to contain %q with exitCode 125, got out: %q exitCode: %v", expected, out, exitCode)
 	}
 }
 
-func (s *DockerSuite) TestRunWrongCpusetMemsFlagValue(c *check.C) {
+func (s *DockerSuite) TestRunWrongCpusetMemsFlagValue(c *testing.T) {
 	// TODO Windows: This needs validation (error out) in the daemon.
 	testRequires(c, DaemonIsLinux)
 	out, exitCode, err := dockerCmdWithError("run", "--cpuset-mems", "1-42--", "busybox", "true")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	expected := "Error response from daemon: Invalid value 1-42-- for cpuset mems.\n"
 	if !(strings.Contains(out, expected) || exitCode == 125) {
 		c.Fatalf("Expected output to contain %q with exitCode 125, got out: %q exitCode: %v", expected, out, exitCode)
@@ -3660,7 +3631,7 @@ func (s *DockerSuite) TestRunWrongCpusetMemsFlagValue(c *check.C) {
 }
 
 // TestRunNonExecutableCmd checks that 'docker run busybox foo' exits with error code 127'
-func (s *DockerSuite) TestRunNonExecutableCmd(c *check.C) {
+func (s *DockerSuite) TestRunNonExecutableCmd(c *testing.T) {
 	name := "testNonExecutableCmd"
 	icmd.RunCommand(dockerBinary, "run", "--name", name, "busybox", "foo").Assert(c, icmd.Expected{
 		ExitCode: 127,
@@ -3669,7 +3640,7 @@ func (s *DockerSuite) TestRunNonExecutableCmd(c *check.C) {
 }
 
 // TestRunNonExistingCmd checks that 'docker run busybox /bin/foo' exits with code 127.
-func (s *DockerSuite) TestRunNonExistingCmd(c *check.C) {
+func (s *DockerSuite) TestRunNonExistingCmd(c *testing.T) {
 	name := "testNonExistingCmd"
 	icmd.RunCommand(dockerBinary, "run", "--name", name, "busybox", "/bin/foo").Assert(c, icmd.Expected{
 		ExitCode: 127,
@@ -3680,7 +3651,7 @@ func (s *DockerSuite) TestRunNonExistingCmd(c *check.C) {
 // TestCmdCannotBeInvoked checks that 'docker run busybox /etc' exits with 126, or
 // 127 on Windows. The difference is that in Windows, the container must be started
 // as that's when the check is made (and yes, by its design...)
-func (s *DockerSuite) TestCmdCannotBeInvoked(c *check.C) {
+func (s *DockerSuite) TestCmdCannotBeInvoked(c *testing.T) {
 	expected := 126
 	if testEnv.OSType == "windows" {
 		expected = 127
@@ -3694,7 +3665,7 @@ func (s *DockerSuite) TestCmdCannotBeInvoked(c *check.C) {
 
 // TestRunNonExistingImage checks that 'docker run foo' exits with error msg 125 and contains  'Unable to find image'
 // FIXME(vdemeester) should be a unit test
-func (s *DockerSuite) TestRunNonExistingImage(c *check.C) {
+func (s *DockerSuite) TestRunNonExistingImage(c *testing.T) {
 	icmd.RunCommand(dockerBinary, "run", "foo").Assert(c, icmd.Expected{
 		ExitCode: 125,
 		Err:      "Unable to find image",
@@ -3703,7 +3674,7 @@ func (s *DockerSuite) TestRunNonExistingImage(c *check.C) {
 
 // TestDockerFails checks that 'docker run -foo busybox' exits with 125 to signal docker run failed
 // FIXME(vdemeester) should be a unit test
-func (s *DockerSuite) TestDockerFails(c *check.C) {
+func (s *DockerSuite) TestDockerFails(c *testing.T) {
 	icmd.RunCommand(dockerBinary, "run", "-foo", "busybox").Assert(c, icmd.Expected{
 		ExitCode: 125,
 		Error:    "exit status 125",
@@ -3711,7 +3682,7 @@ func (s *DockerSuite) TestDockerFails(c *check.C) {
 }
 
 // TestRunInvalidReference invokes docker run with a bad reference.
-func (s *DockerSuite) TestRunInvalidReference(c *check.C) {
+func (s *DockerSuite) TestRunInvalidReference(c *testing.T) {
 	out, exit, _ := dockerCmdWithError("run", "busybox@foo")
 	if exit == 0 {
 		c.Fatalf("expected non-zero exist code; received %d", exit)
@@ -3723,7 +3694,7 @@ func (s *DockerSuite) TestRunInvalidReference(c *check.C) {
 }
 
 // Test fix for issue #17854
-func (s *DockerSuite) TestRunInitLayerPathOwnership(c *check.C) {
+func (s *DockerSuite) TestRunInitLayerPathOwnership(c *testing.T) {
 	// Not applicable on Windows as it does not support Linux uid/gid ownership
 	testRequires(c, DaemonIsLinux)
 	name := "testetcfileownership"
@@ -3740,7 +3711,7 @@ func (s *DockerSuite) TestRunInitLayerPathOwnership(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunWithOomScoreAdj(c *check.C) {
+func (s *DockerSuite) TestRunWithOomScoreAdj(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	expected := "642"
@@ -3751,111 +3722,31 @@ func (s *DockerSuite) TestRunWithOomScoreAdj(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunWithOomScoreAdjInvalidRange(c *check.C) {
+func (s *DockerSuite) TestRunWithOomScoreAdjInvalidRange(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	out, _, err := dockerCmdWithError("run", "--oom-score-adj", "1001", "busybox", "true")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]."
 	if !strings.Contains(out, expected) {
 		c.Fatalf("Expected output to contain %q, got %q instead", expected, out)
 	}
 	out, _, err = dockerCmdWithError("run", "--oom-score-adj", "-1001", "busybox", "true")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]."
 	if !strings.Contains(out, expected) {
 		c.Fatalf("Expected output to contain %q, got %q instead", expected, out)
 	}
 }
 
-func (s *DockerSuite) TestRunVolumesMountedAsShared(c *check.C) {
-	// Volume propagation is linux only. Also it creates directories for
-	// bind mounting, so needs to be same host.
-	testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace)
-
-	// Prepare a source directory to bind mount
-	tmpDir, err := ioutil.TempDir("", "volume-source")
-	if err != nil {
-		c.Fatal(err)
-	}
-	defer os.RemoveAll(tmpDir)
-
-	if err := os.Mkdir(path.Join(tmpDir, "mnt1"), 0755); err != nil {
-		c.Fatal(err)
-	}
-
-	// Convert this directory into a shared mount point so that we do
-	// not rely on propagation properties of parent mount.
-	icmd.RunCommand("mount", "--bind", tmpDir, tmpDir).Assert(c, icmd.Success)
-	icmd.RunCommand("mount", "--make-private", "--make-shared", tmpDir).Assert(c, icmd.Success)
-
-	dockerCmd(c, "run", "--privileged", "-v", fmt.Sprintf("%s:/volume-dest:shared", tmpDir), "busybox", "mount", "--bind", "/volume-dest/mnt1", "/volume-dest/mnt1")
-
-	// Make sure a bind mount under a shared volume propagated to host.
-	if mounted, _ := mount.Mounted(path.Join(tmpDir, "mnt1")); !mounted {
-		c.Fatalf("Bind mount under shared volume did not propagate to host")
-	}
-
-	mount.Unmount(path.Join(tmpDir, "mnt1"))
-}
-
-func (s *DockerSuite) TestRunVolumesMountedAsSlave(c *check.C) {
-	// Volume propagation is linux only. Also it creates directories for
-	// bind mounting, so needs to be same host.
-	testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace)
-
-	// Prepare a source directory to bind mount
-	tmpDir, err := ioutil.TempDir("", "volume-source")
-	if err != nil {
-		c.Fatal(err)
-	}
-	defer os.RemoveAll(tmpDir)
-
-	if err := os.Mkdir(path.Join(tmpDir, "mnt1"), 0755); err != nil {
-		c.Fatal(err)
-	}
-
-	// Prepare a source directory with file in it. We will bind mount this
-	// directory and see if file shows up.
-	tmpDir2, err := ioutil.TempDir("", "volume-source2")
-	if err != nil {
-		c.Fatal(err)
-	}
-	defer os.RemoveAll(tmpDir2)
-
-	if err := ioutil.WriteFile(path.Join(tmpDir2, "slave-testfile"), []byte("Test"), 0644); err != nil {
-		c.Fatal(err)
-	}
-
-	// Convert this directory into a shared mount point so that we do
-	// not rely on propagation properties of parent mount.
-	icmd.RunCommand("mount", "--bind", tmpDir, tmpDir).Assert(c, icmd.Success)
-	icmd.RunCommand("mount", "--make-private", "--make-shared", tmpDir).Assert(c, icmd.Success)
-
-	dockerCmd(c, "run", "-i", "-d", "--name", "parent", "-v", fmt.Sprintf("%s:/volume-dest:slave", tmpDir), "busybox", "top")
-
-	// Bind mount tmpDir2/ onto tmpDir/mnt1. If mount propagates inside
-	// container then contents of tmpDir2/slave-testfile should become
-	// visible at "/volume-dest/mnt1/slave-testfile"
-	icmd.RunCommand("mount", "--bind", tmpDir2, path.Join(tmpDir, "mnt1")).Assert(c, icmd.Success)
-
-	out, _ := dockerCmd(c, "exec", "parent", "cat", "/volume-dest/mnt1/slave-testfile")
-
-	mount.Unmount(path.Join(tmpDir, "mnt1"))
-
-	if out != "Test" {
-		c.Fatalf("Bind mount under slave volume did not propagate to container")
-	}
-}
-
-func (s *DockerSuite) TestRunNamedVolumesMountedAsShared(c *check.C) {
+func (s *DockerSuite) TestRunNamedVolumesMountedAsShared(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	out, exitCode, _ := dockerCmdWithError("run", "-v", "foo:/test:shared", "busybox", "touch", "/test/somefile")
-	c.Assert(exitCode, checker.Not(checker.Equals), 0)
-	c.Assert(out, checker.Contains, "invalid mount config")
+	assert.Assert(c, exitCode != 0)
+	assert.Assert(c, strings.Contains(out, "invalid mount config"))
 }
 
-func (s *DockerSuite) TestRunNamedVolumeCopyImageData(c *check.C) {
+func (s *DockerSuite) TestRunNamedVolumeCopyImageData(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	testImg := "testvolumecopy"
@@ -3866,10 +3757,10 @@ func (s *DockerSuite) TestRunNamedVolumeCopyImageData(c *check.C) {
 
 	dockerCmd(c, "run", "-v", "foo:/foo", testImg)
 	out, _ := dockerCmd(c, "run", "-v", "foo:/foo", "busybox", "cat", "/foo/hello")
-	c.Assert(strings.TrimSpace(out), check.Equals, "hello")
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 }
 
-func (s *DockerSuite) TestRunNamedVolumeNotRemoved(c *check.C) {
+func (s *DockerSuite) TestRunNamedVolumeNotRemoved(c *testing.T) {
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 
 	dockerCmd(c, "volume", "create", "test")
@@ -3877,35 +3768,35 @@ func (s *DockerSuite) TestRunNamedVolumeNotRemoved(c *check.C) {
 	dockerCmd(c, "run", "--rm", "-v", "test:"+prefix+"/foo", "-v", prefix+"/bar", "busybox", "true")
 	dockerCmd(c, "volume", "inspect", "test")
 	out, _ := dockerCmd(c, "volume", "ls", "-q")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "test")
+	assert.Assert(c, strings.Contains(out, "test"))
 
 	dockerCmd(c, "run", "--name=test", "-v", "test:"+prefix+"/foo", "-v", prefix+"/bar", "busybox", "true")
 	dockerCmd(c, "rm", "-fv", "test")
 	dockerCmd(c, "volume", "inspect", "test")
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "test")
+	assert.Assert(c, strings.Contains(out, "test"))
 }
 
-func (s *DockerSuite) TestRunNamedVolumesFromNotRemoved(c *check.C) {
+func (s *DockerSuite) TestRunNamedVolumesFromNotRemoved(c *testing.T) {
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 
 	dockerCmd(c, "volume", "create", "test")
 	cid, _ := dockerCmd(c, "run", "-d", "--name=parent", "-v", "test:"+prefix+"/foo", "-v", prefix+"/bar", "busybox", "true")
 	dockerCmd(c, "run", "--name=child", "--volumes-from=parent", "busybox", "true")
 
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	container, err := cli.ContainerInspect(context.Background(), strings.TrimSpace(cid))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	var vname string
 	for _, v := range container.Mounts {
 		if v.Name != "test" {
 			vname = v.Name
 		}
 	}
-	c.Assert(vname, checker.Not(checker.Equals), "")
+	assert.Assert(c, vname != "")
 
 	// Remove the parent so there are not other references to the volumes
 	dockerCmd(c, "rm", "-f", "parent")
@@ -3913,60 +3804,47 @@ func (s *DockerSuite) TestRunNamedVolumesFromNotRemoved(c *check.C) {
 	dockerCmd(c, "rm", "-fv", "child")
 	dockerCmd(c, "volume", "inspect", "test")
 	out, _ := dockerCmd(c, "volume", "ls", "-q")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "test")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), vname)
-}
-
-func (s *DockerSuite) TestRunAttachFailedNoLeak(c *check.C) {
-	// TODO @msabansal - https://github.com/moby/moby/issues/35023. Duplicate
-	// port mappings are not errored out on RS3 builds. Temporarily disabling
-	// this test pending further investigation. Note we parse kernel.GetKernelVersion
-	// rather than system.GetOSVersion as test binaries aren't manifested, so would
-	// otherwise report build 9200.
-	if runtime.GOOS == "windows" {
-		v, err := kernel.GetKernelVersion()
-		c.Assert(err, checker.IsNil)
-		build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
-		if build == 16299 {
-			c.Skip("Temporarily disabled on RS3 builds")
-		}
-	}
+	assert.Assert(c, strings.Contains(out, "test"))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), vname))
+}
 
+func (s *DockerSuite) TestRunAttachFailedNoLeak(c *testing.T) {
 	nroutines, err := getGoroutineNumber()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	runSleepingContainer(c, "--name=test", "-p", "8000:8000")
 
 	// Wait until container is fully up and running
-	c.Assert(waitRun("test"), check.IsNil)
+	assert.Assert(c, waitRun("test") == nil)
 
 	out, _, err := dockerCmdWithError("run", "--name=fail", "-p", "8000:8000", "busybox", "true")
 	// We will need the following `inspect` to diagnose the issue if test fails (#21247)
 	out1, err1 := dockerCmd(c, "inspect", "--format", "{{json .State}}", "test")
 	out2, err2 := dockerCmd(c, "inspect", "--format", "{{json .State}}", "fail")
-	c.Assert(err, checker.NotNil, check.Commentf("Command should have failed but succeeded with: %s\nContainer 'test' [%+v]: %s\nContainer 'fail' [%+v]: %s", out, err1, out1, err2, out2))
+	assert.Assert(c, err != nil, "Command should have failed but succeeded with: %s\nContainer 'test' [%+v]: %s\nContainer 'fail' [%+v]: %s", out, err1, out1, err2, out2)
 	// check for windows error as well
 	// TODO Windows Post TP5. Fix the error message string
-	c.Assert(strings.Contains(string(out), "port is already allocated") ||
-		strings.Contains(string(out), "were not connected because a duplicate name exists") ||
-		strings.Contains(string(out), "The specified port already exists") ||
-		strings.Contains(string(out), "HNS failed with error : Failed to create endpoint") ||
-		strings.Contains(string(out), "HNS failed with error : The object already exists"), checker.Equals, true, check.Commentf("Output: %s", out))
+	outLowerCase := strings.ToLower(out)
+	assert.Assert(c, strings.Contains(outLowerCase, "port is already allocated") ||
+		strings.Contains(outLowerCase, "were not connected because a duplicate name exists") ||
+		strings.Contains(outLowerCase, "the specified port already exists") ||
+		strings.Contains(outLowerCase, "hns failed with error : failed to create endpoint") ||
+		strings.Contains(outLowerCase, "hns failed with error : the object already exists"), fmt.Sprintf("Output: %s", out))
 	dockerCmd(c, "rm", "-f", "test")
 
 	// NGoroutines is not updated right away, so we need to wait before failing
-	c.Assert(waitForGoroutines(nroutines), checker.IsNil)
+	assert.Assert(c, waitForGoroutines(nroutines) == nil)
 }
 
 // Test for one character directory name case (#20122)
-func (s *DockerSuite) TestRunVolumeWithOneCharacter(c *check.C) {
+func (s *DockerSuite) TestRunVolumeWithOneCharacter(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	out, _ := dockerCmd(c, "run", "-v", "/tmp/q:/foo", "busybox", "sh", "-c", "find /foo")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "/foo")
+	assert.Equal(c, strings.TrimSpace(out), "/foo")
 }
 
-func (s *DockerSuite) TestRunVolumeCopyFlag(c *check.C) {
+func (s *DockerSuite) TestRunVolumeCopyFlag(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // Windows does not support copying data from image to the volume
 	buildImageSuccessfully(c, "volumecopy", build.WithDockerfile(`FROM busybox
 		RUN mkdir /foo && echo hello > /foo/bar
@@ -3975,33 +3853,33 @@ func (s *DockerSuite) TestRunVolumeCopyFlag(c *check.C) {
 
 	// test with the nocopy flag
 	out, _, err := dockerCmdWithError("run", "-v", "test:/foo:nocopy", "volumecopy")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 	// test default behavior which is to copy for non-binds
 	out, _ = dockerCmd(c, "run", "-v", "test:/foo", "volumecopy")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello")
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 	// error out when the volume is already populated
 	out, _, err = dockerCmdWithError("run", "-v", "test:/foo:copy", "volumecopy")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 	// do not error out when copy isn't explicitly set even though it's already populated
 	out, _ = dockerCmd(c, "run", "-v", "test:/foo", "volumecopy")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello")
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 
 	// do not allow copy modes on volumes-from
 	dockerCmd(c, "run", "--name=test", "-v", "/foo", "busybox", "true")
 	out, _, err = dockerCmdWithError("run", "--volumes-from=test:copy", "busybox", "true")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 	out, _, err = dockerCmdWithError("run", "--volumes-from=test:nocopy", "busybox", "true")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 
 	// do not allow copy modes on binds
 	out, _, err = dockerCmdWithError("run", "-v", "/foo:/bar:copy", "busybox", "true")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 	out, _, err = dockerCmdWithError("run", "-v", "/foo:/bar:nocopy", "busybox", "true")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 }
 
 // Test case for #21976
-func (s *DockerSuite) TestRunDNSInHostMode(c *check.C) {
+func (s *DockerSuite) TestRunDNSInHostMode(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 
 	expectedOutput := "nameserver 127.0.0.1"
@@ -4030,31 +3908,31 @@ func (s *DockerSuite) TestRunDNSInHostMode(c *check.C) {
 	expectedOutput2 := "search example.com"
 	expectedOutput3 := "options timeout:3"
 	out := cli.DockerCmd(c, "run", "--dns=1.2.3.4", "--dns-search=example.com", "--dns-opt=timeout:3", "--net=host", "busybox", "cat", "/etc/resolv.conf").Combined()
-	c.Assert(out, checker.Contains, expectedOutput1, check.Commentf("Expected '%s', but got %q", expectedOutput1, out))
-	c.Assert(out, checker.Contains, expectedOutput2, check.Commentf("Expected '%s', but got %q", expectedOutput2, out))
-	c.Assert(out, checker.Contains, expectedOutput3, check.Commentf("Expected '%s', but got %q", expectedOutput3, out))
+	assert.Assert(c, strings.Contains(out, expectedOutput1), "Expected '%s', but got %q", expectedOutput1, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput2), "Expected '%s', but got %q", expectedOutput2, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput3), "Expected '%s', but got %q", expectedOutput3, out)
 }
 
 // Test case for #21976
-func (s *DockerSuite) TestRunAddHostInHostMode(c *check.C) {
+func (s *DockerSuite) TestRunAddHostInHostMode(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 
 	expectedOutput := "1.2.3.4\textra"
 	out, _ := dockerCmd(c, "run", "--add-host=extra:1.2.3.4", "--net=host", "busybox", "cat", "/etc/hosts")
-	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
+	assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out)
 }
 
-func (s *DockerSuite) TestRunRmAndWait(c *check.C) {
+func (s *DockerSuite) TestRunRmAndWait(c *testing.T) {
 	dockerCmd(c, "run", "--name=test", "--rm", "-d", "busybox", "sh", "-c", "sleep 3;exit 2")
 
 	out, code, err := dockerCmdWithError("wait", "test")
-	c.Assert(err, checker.IsNil, check.Commentf("out: %s; exit code: %d", out, code))
-	c.Assert(out, checker.Equals, "2\n", check.Commentf("exit code: %d", code))
-	c.Assert(code, checker.Equals, 0)
+	assert.Assert(c, err == nil, "out: %s; exit code: %d", out, code)
+	assert.Equal(c, out, "2\n", "exit code: %d", code)
+	assert.Equal(c, code, 0)
 }
 
 // Test that auto-remove is performed by the daemon (API 1.25 and above)
-func (s *DockerSuite) TestRunRm(c *check.C) {
+func (s *DockerSuite) TestRunRm(c *testing.T) {
 	name := "miss-me-when-im-gone"
 	cli.DockerCmd(c, "run", "--name="+name, "--rm", "busybox")
 
@@ -4065,7 +3943,7 @@ func (s *DockerSuite) TestRunRm(c *check.C) {
 }
 
 // Test that auto-remove is performed by the client on API versions that do not support daemon-side api-remove (API < 1.25)
-func (s *DockerSuite) TestRunRmPre125Api(c *check.C) {
+func (s *DockerSuite) TestRunRmPre125Api(c *testing.T) {
 	name := "miss-me-when-im-gone"
 	envs := appendBaseEnv(os.Getenv("DOCKER_TLS_VERIFY") != "", "DOCKER_API_VERSION=1.24")
 	cli.Docker(cli.Args("run", "--name="+name, "--rm", "busybox"), cli.WithEnvironmentVariables(envs...)).Assert(c, icmd.Success)
@@ -4077,7 +3955,7 @@ func (s *DockerSuite) TestRunRmPre125Api(c *check.C) {
 }
 
 // Test case for #23498
-func (s *DockerSuite) TestRunUnsetEntrypoint(c *check.C) {
+func (s *DockerSuite) TestRunUnsetEntrypoint(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "test-entrypoint"
 	dockerfile := `FROM busybox
@@ -4098,7 +3976,7 @@ exec "$@"`,
 	cli.BuildCmd(c, name, build.WithExternalBuildContext(ctx))
 
 	out := cli.DockerCmd(c, "run", "--entrypoint=", "-t", name, "echo", "foo").Combined()
-	c.Assert(strings.TrimSpace(out), check.Equals, "foo")
+	assert.Equal(c, strings.TrimSpace(out), "foo")
 
 	// CMD will be reset as well (the same as setting a custom entrypoint)
 	cli.Docker(cli.Args("run", "--entrypoint=", "-t", name)).Assert(c, icmd.Expected{
@@ -4107,74 +3985,80 @@ exec "$@"`,
 	})
 }
 
-func (s *DockerDaemonSuite) TestRunWithUlimitAndDaemonDefault(c *check.C) {
+func (s *DockerDaemonSuite) TestRunWithUlimitAndDaemonDefault(c *testing.T) {
 	s.d.StartWithBusybox(c, "--debug", "--default-ulimit=nofile=65535")
 
 	name := "test-A"
 	_, err := s.d.Cmd("run", "--name", name, "-d", "busybox", "top")
-	c.Assert(err, checker.IsNil)
-	c.Assert(s.d.WaitRun(name), check.IsNil)
+	assert.NilError(c, err)
+	assert.NilError(c, s.d.WaitRun(name))
 
 	out, err := s.d.Cmd("inspect", "--format", "{{.HostConfig.Ulimits}}", name)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "[nofile=65535:65535]")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, "[nofile=65535:65535]"))
 	name = "test-B"
 	_, err = s.d.Cmd("run", "--name", name, "--ulimit=nofile=42", "-d", "busybox", "top")
-	c.Assert(err, checker.IsNil)
-	c.Assert(s.d.WaitRun(name), check.IsNil)
+	assert.NilError(c, err)
+	assert.NilError(c, s.d.WaitRun(name))
 
 	out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.Ulimits}}", name)
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "[nofile=42:42]")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, "[nofile=42:42]"))
 }
 
-func (s *DockerSuite) TestRunStoppedLoggingDriverNoLeak(c *check.C) {
+func (s *DockerSuite) TestRunStoppedLoggingDriverNoLeak(c *testing.T) {
 	nroutines, err := getGoroutineNumber()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, _, err := dockerCmdWithError("run", "--name=fail", "--log-driver=splunk", "busybox", "true")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "failed to initialize logging driver", check.Commentf("error should be about logging driver, got output %s", out))
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "failed to initialize logging driver"), "error should be about logging driver, got output %s", out)
 	// NGoroutines is not updated right away, so we need to wait before failing
-	c.Assert(waitForGoroutines(nroutines), checker.IsNil)
+	assert.Assert(c, waitForGoroutines(nroutines) == nil)
 }
 
 // Handles error conditions for --credentialspec. Validating E2E success cases
 // requires additional infrastructure (AD for example) on CI servers.
-func (s *DockerSuite) TestRunCredentialSpecFailures(c *check.C) {
+func (s *DockerSuite) TestRunCredentialSpecFailures(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
+
 	attempts := []struct{ value, expectedError string }{
-		{"rubbish", "invalid credential spec security option - value must be prefixed file:// or registry://"},
-		{"rubbish://", "invalid credential spec security option - value must be prefixed file:// or registry://"},
-		{"file://", "no value supplied for file:// credential spec security option"},
-		{"registry://", "no value supplied for registry:// credential spec security option"},
+		{"rubbish", "invalid credential spec security option - value must be prefixed by 'file://', 'registry://', or 'raw://' followed by a non-empty value"},
+		{"rubbish://", "invalid credential spec security option - value must be prefixed by 'file://', 'registry://', or 'raw://' followed by a non-empty value"},
+		{"file://", "invalid credential spec security option - value must be prefixed by 'file://', 'registry://', or 'raw://' followed by a non-empty value"},
+		{"registry://", "invalid credential spec security option - value must be prefixed by 'file://', 'registry://', or 'raw://' followed by a non-empty value"},
 		{`file://c:\blah.txt`, "path cannot be absolute"},
 		{`file://doesnotexist.txt`, "The system cannot find the file specified"},
 	}
 	for _, attempt := range attempts {
 		_, _, err := dockerCmdWithError("run", "--security-opt=credentialspec="+attempt.value, "busybox", "true")
-		c.Assert(err, checker.NotNil, check.Commentf("%s expected non-nil err", attempt.value))
-		c.Assert(err.Error(), checker.Contains, attempt.expectedError, check.Commentf("%s expected %s got %s", attempt.value, attempt.expectedError, err))
+		assert.Assert(c, err != nil, "%s expected non-nil err", attempt.value)
+		assert.Assert(c, strings.Contains(err.Error(), attempt.expectedError), "%s expected %s got %s", attempt.value, attempt.expectedError, err)
 	}
 }
 
 // Windows specific test to validate credential specs with a well-formed spec.
-// Note it won't actually do anything in CI configuration with the spec, but
-// it should not fail to run a container.
-func (s *DockerSuite) TestRunCredentialSpecWellFormed(c *check.C) {
-	testRequires(c, DaemonIsWindows, SameHostDaemon)
-	validCS := readFile(`fixtures\credentialspecs\valid.json`, c)
-	writeFile(filepath.Join(testEnv.DaemonInfo.DockerRootDir, `credentialspecs\valid.json`), validCS, c)
-	dockerCmd(c, "run", `--security-opt=credentialspec=file://valid.json`, "busybox", "true")
+func (s *DockerSuite) TestRunCredentialSpecWellFormed(c *testing.T) {
+	testRequires(c, DaemonIsWindows, testEnv.IsLocalDaemon)
+
+	validCredSpecs := readFile(`fixtures\credentialspecs\valid.json`, c)
+	writeFile(filepath.Join(testEnv.DaemonInfo.DockerRootDir, `credentialspecs\valid.json`), validCredSpecs, c)
+
+	for _, value := range []string{"file://valid.json", "raw://" + validCredSpecs} {
+		// `nltest /PARENTDOMAIN` simply reads the local config, and does not require having an AD
+		// controller handy
+		out, _ := dockerCmd(c, "run", "--rm", "--security-opt=credentialspec="+value, minimalBaseImage(), "nltest", "/PARENTDOMAIN")
+
+		assert.Assert(c, strings.Contains(out, "hyperv.local."))
+		assert.Assert(c, strings.Contains(out, "The command completed successfully"))
+	}
 }
 
-func (s *DockerSuite) TestRunDuplicateMount(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
+func (s *DockerSuite) TestRunDuplicateMount(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
 
-	tmpFile, err := ioutil.TempFile("", "touch-me")
-	c.Assert(err, checker.IsNil)
+	tmpFile, err := os.CreateTemp("", "touch-me")
+	assert.NilError(c, err)
 	defer tmpFile.Close()
 
 	data := "touch-me-foo-bar\n"
@@ -4184,98 +4068,95 @@ func (s *DockerSuite) TestRunDuplicateMount(c *check.C) {
 
 	name := "test"
 	out, _ := dockerCmd(c, "run", "--name", name, "-v", "/tmp:/tmp", "-v", "/tmp:/tmp", "busybox", "sh", "-c", "cat "+tmpFile.Name()+" && ls /")
-	c.Assert(out, checker.Not(checker.Contains), "tmp:")
-	c.Assert(out, checker.Contains, data)
-
+	assert.Assert(c, !strings.Contains(out, "tmp:"))
+	assert.Assert(c, strings.Contains(out, data))
 	out = inspectFieldJSON(c, name, "Config.Volumes")
-	c.Assert(out, checker.Contains, "null")
+	assert.Assert(c, strings.Contains(out, "null"))
 }
 
-func (s *DockerSuite) TestRunWindowsWithCPUCount(c *check.C) {
+func (s *DockerSuite) TestRunWindowsWithCPUCount(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 
 	out, _ := dockerCmd(c, "run", "--cpu-count=1", "--name", "test", "busybox", "echo", "testing")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "testing")
+	assert.Equal(c, strings.TrimSpace(out), "testing")
 
 	out = inspectField(c, "test", "HostConfig.CPUCount")
-	c.Assert(out, check.Equals, "1")
+	assert.Equal(c, out, "1")
 }
 
-func (s *DockerSuite) TestRunWindowsWithCPUShares(c *check.C) {
+func (s *DockerSuite) TestRunWindowsWithCPUShares(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 
 	out, _ := dockerCmd(c, "run", "--cpu-shares=1000", "--name", "test", "busybox", "echo", "testing")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "testing")
+	assert.Equal(c, strings.TrimSpace(out), "testing")
 
 	out = inspectField(c, "test", "HostConfig.CPUShares")
-	c.Assert(out, check.Equals, "1000")
+	assert.Equal(c, out, "1000")
 }
 
-func (s *DockerSuite) TestRunWindowsWithCPUPercent(c *check.C) {
+func (s *DockerSuite) TestRunWindowsWithCPUPercent(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 
 	out, _ := dockerCmd(c, "run", "--cpu-percent=80", "--name", "test", "busybox", "echo", "testing")
-	c.Assert(strings.TrimSpace(out), checker.Equals, "testing")
+	assert.Equal(c, strings.TrimSpace(out), "testing")
 
 	out = inspectField(c, "test", "HostConfig.CPUPercent")
-	c.Assert(out, check.Equals, "80")
+	assert.Equal(c, out, "80")
 }
 
-func (s *DockerSuite) TestRunProcessIsolationWithCPUCountCPUSharesAndCPUPercent(c *check.C) {
-	testRequires(c, DaemonIsWindows, IsolationIsProcess)
+func (s *DockerSuite) TestRunProcessIsolationWithCPUCountCPUSharesAndCPUPercent(c *testing.T) {
+	testRequires(c, IsolationIsProcess)
 
 	out, _ := dockerCmd(c, "run", "--cpu-count=1", "--cpu-shares=1000", "--cpu-percent=80", "--name", "test", "busybox", "echo", "testing")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "WARNING: Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "WARNING: Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "testing")
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "WARNING: Conflicting options: CPU count takes priority over CPU shares on Windows Server Containers. CPU shares discarded"))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "WARNING: Conflicting options: CPU count takes priority over CPU percent on Windows Server Containers. CPU percent discarded"))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "testing"))
 	out = inspectField(c, "test", "HostConfig.CPUCount")
-	c.Assert(out, check.Equals, "1")
+	assert.Equal(c, out, "1")
 
 	out = inspectField(c, "test", "HostConfig.CPUShares")
-	c.Assert(out, check.Equals, "0")
+	assert.Equal(c, out, "0")
 
 	out = inspectField(c, "test", "HostConfig.CPUPercent")
-	c.Assert(out, check.Equals, "0")
+	assert.Equal(c, out, "0")
 }
 
-func (s *DockerSuite) TestRunHypervIsolationWithCPUCountCPUSharesAndCPUPercent(c *check.C) {
-	testRequires(c, DaemonIsWindows, IsolationIsHyperv)
+func (s *DockerSuite) TestRunHypervIsolationWithCPUCountCPUSharesAndCPUPercent(c *testing.T) {
+	testRequires(c, IsolationIsHyperv)
 
 	out, _ := dockerCmd(c, "run", "--cpu-count=1", "--cpu-shares=1000", "--cpu-percent=80", "--name", "test", "busybox", "echo", "testing")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "testing")
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "testing"))
 	out = inspectField(c, "test", "HostConfig.CPUCount")
-	c.Assert(out, check.Equals, "1")
+	assert.Equal(c, out, "1")
 
 	out = inspectField(c, "test", "HostConfig.CPUShares")
-	c.Assert(out, check.Equals, "1000")
+	assert.Equal(c, out, "1000")
 
 	out = inspectField(c, "test", "HostConfig.CPUPercent")
-	c.Assert(out, check.Equals, "80")
+	assert.Equal(c, out, "80")
 }
 
 // Test for #25099
-func (s *DockerSuite) TestRunEmptyEnv(c *check.C) {
+func (s *DockerSuite) TestRunEmptyEnv(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	expectedOutput := "invalid environment variable:"
 
 	out, _, err := dockerCmdWithError("run", "-e", "", "busybox", "true")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, expectedOutput)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, expectedOutput))
 
 	out, _, err = dockerCmdWithError("run", "-e", "=", "busybox", "true")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, expectedOutput)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, expectedOutput))
 
 	out, _, err = dockerCmdWithError("run", "-e", "=foo", "busybox", "true")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, expectedOutput)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, expectedOutput))
 }
 
 // #28658
-func (s *DockerSuite) TestSlowStdinClosing(c *check.C) {
+func (s *DockerSuite) TestSlowStdinClosing(c *testing.T) {
 	name := "testslowstdinclosing"
 	repeat := 3 // regression happened 50% of the time
 	for i := 0; i < repeat; i++ {
@@ -4293,7 +4174,7 @@ func (s *DockerSuite) TestSlowStdinClosing(c *check.C) {
 		case <-time.After(30 * time.Second):
 			c.Fatal("running container timed out") // cleanup in teardown
 		case err := <-done:
-			c.Assert(err, checker.IsNil)
+			assert.NilError(c, err)
 		}
 	}
 }
@@ -4306,23 +4187,23 @@ func (s *delayedReader) Read([]byte) (int, error) {
 }
 
 // #28823 (originally #28639)
-func (s *DockerSuite) TestRunMountReadOnlyDevShm(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
-	emptyDir, err := ioutil.TempDir("", "test-read-only-dev-shm")
-	c.Assert(err, check.IsNil)
+func (s *DockerSuite) TestRunMountReadOnlyDevShm(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux, NotUserNamespace)
+	emptyDir, err := os.MkdirTemp("", "test-read-only-dev-shm")
+	assert.NilError(c, err)
 	defer os.RemoveAll(emptyDir)
 	out, _, err := dockerCmdWithError("run", "--rm", "--read-only",
 		"-v", fmt.Sprintf("%s:/dev/shm:ro", emptyDir),
 		"busybox", "touch", "/dev/shm/foo")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "Read-only file system")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "Read-only file system"))
 }
 
-func (s *DockerSuite) TestRunMount(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace)
+func (s *DockerSuite) TestRunMount(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, NotUserNamespace)
 
 	// mnt1, mnt2, and testCatFooBar are commonly used in multiple test cases
-	tmpDir, err := ioutil.TempDir("", "mount")
+	tmpDir, err := os.MkdirTemp("", "mount")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -4334,10 +4215,10 @@ func (s *DockerSuite) TestRunMount(c *check.C) {
 	if err := os.Mkdir(mnt2, 0755); err != nil {
 		c.Fatal(err)
 	}
-	if err := ioutil.WriteFile(path.Join(mnt1, "test1"), []byte("test1"), 0644); err != nil {
+	if err := os.WriteFile(path.Join(mnt1, "test1"), []byte("test1"), 0644); err != nil {
 		c.Fatal(err)
 	}
-	if err := ioutil.WriteFile(path.Join(mnt2, "test2"), []byte("test2"), 0644); err != nil {
+	if err := os.WriteFile(path.Join(mnt2, "test2"), []byte("test2"), 0644); err != nil {
 		c.Fatal(err)
 	}
 	testCatFooBar := func(cName string) error {
@@ -4483,14 +4364,11 @@ func (s *DockerSuite) TestRunMount(c *check.C) {
 			_, _, err := dockerCmdWithError(append([]string{"run", "-i", "-d", "--name", cName},
 				append(opts, []string{"busybox", "top"}...)...)...)
 			if testCase.valid {
-				c.Assert(err, check.IsNil,
-					check.Commentf("got error while creating a container with %v (%s)", opts, cName))
-				c.Assert(testCase.fn(cName), check.IsNil,
-					check.Commentf("got error while executing test for %v (%s)", opts, cName))
+				assert.Assert(c, err == nil, "got error while creating a container with %v (%s)", opts, cName)
+				assert.Assert(c, testCase.fn(cName) == nil, "got error while executing test for %v (%s)", opts, cName)
 				dockerCmd(c, "rm", "-f", cName)
 			} else {
-				c.Assert(err, checker.NotNil,
-					check.Commentf("got nil while creating a container with %v (%s)", opts, cName))
+				assert.Assert(c, err != nil, "got nil while creating a container with %v (%s)", opts, cName)
 			}
 		}
 	}
@@ -4498,28 +4376,28 @@ func (s *DockerSuite) TestRunMount(c *check.C) {
 
 // Test that passing a FQDN as hostname properly sets hostname, and
 // /etc/hostname. Test case for 29100
-func (s *DockerSuite) TestRunHostnameFQDN(c *check.C) {
+func (s *DockerSuite) TestRunHostnameFQDN(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	expectedOutput := "foobar.example.com\nfoobar.example.com\nfoobar\nexample.com\nfoobar.example.com"
 	out, _ := dockerCmd(c, "run", "--hostname=foobar.example.com", "busybox", "sh", "-c", `cat /etc/hostname && hostname && hostname -s && hostname -d && hostname -f`)
-	c.Assert(strings.TrimSpace(out), checker.Equals, expectedOutput)
+	assert.Equal(c, strings.TrimSpace(out), expectedOutput)
 
 	out, _ = dockerCmd(c, "run", "--hostname=foobar.example.com", "busybox", "sh", "-c", `cat /etc/hosts`)
 	expectedOutput = "foobar.example.com foobar"
-	c.Assert(strings.TrimSpace(out), checker.Contains, expectedOutput)
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), expectedOutput))
 }
 
 // Test case for 29129
-func (s *DockerSuite) TestRunHostnameInHostMode(c *check.C) {
+func (s *DockerSuite) TestRunHostnameInHostMode(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 
 	expectedOutput := "foobar\nfoobar"
 	out, _ := dockerCmd(c, "run", "--net=host", "--hostname=foobar", "busybox", "sh", "-c", `echo $HOSTNAME && hostname`)
-	c.Assert(strings.TrimSpace(out), checker.Equals, expectedOutput)
+	assert.Equal(c, strings.TrimSpace(out), expectedOutput)
 }
 
-func (s *DockerSuite) TestRunAddDeviceCgroupRule(c *check.C) {
+func (s *DockerSuite) TestRunAddDeviceCgroupRule(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	deviceRule := "c 7:128 rwm"
@@ -4530,12 +4408,12 @@ func (s *DockerSuite) TestRunAddDeviceCgroupRule(c *check.C) {
 	}
 
 	out, _ = dockerCmd(c, "run", "--rm", fmt.Sprintf("--device-cgroup-rule=%s", deviceRule), "busybox", "grep", deviceRule, "/sys/fs/cgroup/devices/devices.list")
-	c.Assert(strings.TrimSpace(out), checker.Equals, deviceRule)
+	assert.Equal(c, strings.TrimSpace(out), deviceRule)
 }
 
 // Verifies that running as local system is operating correctly on Windows
-func (s *DockerSuite) TestWindowsRunAsSystem(c *check.C) {
-	testRequires(c, DaemonIsWindowsAtLeastBuild(15000))
+func (s *DockerSuite) TestWindowsRunAsSystem(c *testing.T) {
+	testRequires(c, DaemonIsWindowsAtLeastBuild(osversion.RS3))
 	out, _ := dockerCmd(c, "run", "--net=none", `--user=nt authority\system`, "--hostname=XYZZY", minimalBaseImage(), "cmd", "/c", `@echo %USERNAME%`)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "XYZZY$")
+	assert.Equal(c, strings.TrimSpace(out), "XYZZY$")
 }
diff --git a/integration-cli/docker_cli_run_unix_test.go b/integration-cli/docker_cli_run_unix_test.go
index 463a52c85b4b0..280948d6b2e67 100644
--- a/integration-cli/docker_cli_run_unix_test.go
+++ b/integration-cli/docker_cli_run_unix_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -7,7 +8,6 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -15,32 +15,32 @@ import (
 	"strconv"
 	"strings"
 	"syscall"
+	"testing"
 	"time"
 
+	"github.com/creack/pty"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
 	"github.com/docker/docker/pkg/homedir"
-	"github.com/docker/docker/pkg/mount"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/sysinfo"
-	"github.com/go-check/check"
-	"github.com/kr/pty"
-	"gotest.tools/icmd"
+	"github.com/moby/sys/mount"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
 // #6509
-func (s *DockerSuite) TestRunRedirectStdout(c *check.C) {
+func (s *DockerSuite) TestRunRedirectStdout(c *testing.T) {
 	checkRedirect := func(command string) {
 		_, tty, err := pty.Open()
-		c.Assert(err, checker.IsNil, check.Commentf("Could not open pty"))
+		assert.Assert(c, err == nil, "Could not open pty")
 		cmd := exec.Command("sh", "-c", command)
 		cmd.Stdin = tty
 		cmd.Stdout = tty
 		cmd.Stderr = tty
-		c.Assert(cmd.Start(), checker.IsNil)
-		ch := make(chan error)
+		assert.NilError(c, cmd.Start())
+		ch := make(chan error, 1)
 		go func() {
 			ch <- cmd.Wait()
 			close(ch)
@@ -50,7 +50,7 @@ func (s *DockerSuite) TestRunRedirectStdout(c *check.C) {
 		case <-time.After(10 * time.Second):
 			c.Fatal("command timeout")
 		case err := <-ch:
-			c.Assert(err, checker.IsNil, check.Commentf("wait err"))
+			assert.Assert(c, err == nil, "wait err")
 		}
 	}
 
@@ -59,71 +59,70 @@ func (s *DockerSuite) TestRunRedirectStdout(c *check.C) {
 }
 
 // Test recursive bind mount works by default
-func (s *DockerSuite) TestRunWithVolumesIsRecursive(c *check.C) {
+func (s *DockerSuite) TestRunWithVolumesIsRecursive(c *testing.T) {
 	// /tmp gets permission denied
-	testRequires(c, NotUserNamespace, SameHostDaemon)
-	tmpDir, err := ioutil.TempDir("", "docker_recursive_mount_test")
-	c.Assert(err, checker.IsNil)
+	testRequires(c, NotUserNamespace, testEnv.IsLocalDaemon)
+	tmpDir, err := os.MkdirTemp("", "docker_recursive_mount_test")
+	assert.NilError(c, err)
 
 	defer os.RemoveAll(tmpDir)
 
 	// Create a temporary tmpfs mount.
 	tmpfsDir := filepath.Join(tmpDir, "tmpfs")
-	c.Assert(os.MkdirAll(tmpfsDir, 0777), checker.IsNil, check.Commentf("failed to mkdir at %s", tmpfsDir))
-	c.Assert(mount.Mount("tmpfs", tmpfsDir, "tmpfs", ""), checker.IsNil, check.Commentf("failed to create a tmpfs mount at %s", tmpfsDir))
+	assert.Assert(c, os.MkdirAll(tmpfsDir, 0777) == nil, "failed to mkdir at %s", tmpfsDir)
+	assert.Assert(c, mount.Mount("tmpfs", tmpfsDir, "tmpfs", "") == nil, "failed to create a tmpfs mount at %s", tmpfsDir)
 
-	f, err := ioutil.TempFile(tmpfsDir, "touch-me")
-	c.Assert(err, checker.IsNil)
+	f, err := os.CreateTemp(tmpfsDir, "touch-me")
+	assert.NilError(c, err)
 	defer f.Close()
 
 	out, _ := dockerCmd(c, "run", "--name", "test-data", "--volume", fmt.Sprintf("%s:/tmp:ro", tmpDir), "busybox:latest", "ls", "/tmp/tmpfs")
-	c.Assert(out, checker.Contains, filepath.Base(f.Name()), check.Commentf("Recursive bind mount test failed. Expected file not found"))
+	assert.Assert(c, strings.Contains(out, filepath.Base(f.Name())), "Recursive bind mount test failed. Expected file not found")
 }
 
-func (s *DockerSuite) TestRunDeviceDirectory(c *check.C) {
+func (s *DockerSuite) TestRunDeviceDirectory(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
 	if _, err := os.Stat("/dev/snd"); err != nil {
 		c.Skip("Host does not have /dev/snd")
 	}
 
 	out, _ := dockerCmd(c, "run", "--device", "/dev/snd:/dev/snd", "busybox", "sh", "-c", "ls /dev/snd/")
-	c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "timer", check.Commentf("expected output /dev/snd/timer"))
-
+	assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "timer"), "expected output /dev/snd/timer")
 	out, _ = dockerCmd(c, "run", "--device", "/dev/snd:/dev/othersnd", "busybox", "sh", "-c", "ls /dev/othersnd/")
-	c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "seq", check.Commentf("expected output /dev/othersnd/seq"))
+	assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "seq"), "expected output /dev/othersnd/seq")
 }
 
 // TestRunAttachDetach checks attaching and detaching with the default escape sequence.
-func (s *DockerSuite) TestRunAttachDetach(c *check.C) {
+func (s *DockerSuite) TestRunAttachDetach(c *testing.T) {
 	name := "attach-detach"
 
 	dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
 
 	cmd := exec.Command(dockerBinary, "attach", name)
 	stdout, err := cmd.StdoutPipe()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	cpty, tty, err := pty.Open()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer cpty.Close()
 	cmd.Stdin = tty
-	c.Assert(cmd.Start(), checker.IsNil)
-	c.Assert(waitRun(name), check.IsNil)
+	assert.NilError(c, cmd.Start())
+	assert.Assert(c, waitRun(name) == nil)
 
 	_, err = cpty.Write([]byte("hello\n"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, err := bufio.NewReader(stdout).ReadString('\n')
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "hello")
+	assert.NilError(c, err)
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 
 	// escape sequence
 	_, err = cpty.Write([]byte{16})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	time.Sleep(100 * time.Millisecond)
 	_, err = cpty.Write([]byte{17})
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	ch := make(chan struct{})
+	ch := make(chan struct{}, 1)
 	go func() {
 		cmd.Wait()
 		ch <- struct{}{}
@@ -136,16 +135,16 @@ func (s *DockerSuite) TestRunAttachDetach(c *check.C) {
 	}
 
 	running := inspectField(c, name, "State.Running")
-	c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
+	assert.Equal(c, running, "true", "expected container to still be running")
 
 	out, _ = dockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c), "-f", "container="+name)
 	// attach and detach event should be monitored
-	c.Assert(out, checker.Contains, "attach")
-	c.Assert(out, checker.Contains, "detach")
+	assert.Assert(c, strings.Contains(out, "attach"))
+	assert.Assert(c, strings.Contains(out, "detach"))
 }
 
 // TestRunAttachDetachFromFlag checks attaching and detaching with the escape sequence specified via flags.
-func (s *DockerSuite) TestRunAttachDetachFromFlag(c *check.C) {
+func (s *DockerSuite) TestRunAttachDetachFromFlag(c *testing.T) {
 	name := "attach-detach"
 	keyCtrlA := []byte{1}
 	keyA := []byte{97}
@@ -166,7 +165,7 @@ func (s *DockerSuite) TestRunAttachDetachFromFlag(c *check.C) {
 	if err := cmd.Start(); err != nil {
 		c.Fatal(err)
 	}
-	c.Assert(waitRun(name), check.IsNil)
+	assert.Assert(c, waitRun(name) == nil)
 
 	if _, err := cpty.Write([]byte("hello\n")); err != nil {
 		c.Fatal(err)
@@ -189,7 +188,7 @@ func (s *DockerSuite) TestRunAttachDetachFromFlag(c *check.C) {
 		c.Fatal(err)
 	}
 
-	ch := make(chan struct{})
+	ch := make(chan struct{}, 1)
 	go func() {
 		cmd.Wait()
 		ch <- struct{}{}
@@ -202,14 +201,14 @@ func (s *DockerSuite) TestRunAttachDetachFromFlag(c *check.C) {
 	}
 
 	running := inspectField(c, name, "State.Running")
-	c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
+	assert.Equal(c, running, "true", "expected container to still be running")
 }
 
 // TestRunAttachDetachFromInvalidFlag checks attaching and detaching with the escape sequence specified via flags.
-func (s *DockerSuite) TestRunAttachDetachFromInvalidFlag(c *check.C) {
+func (s *DockerSuite) TestRunAttachDetachFromInvalidFlag(c *testing.T) {
 	name := "attach-detach"
 	dockerCmd(c, "run", "--name", name, "-itd", "busybox", "top")
-	c.Assert(waitRun(name), check.IsNil)
+	assert.Assert(c, waitRun(name) == nil)
 
 	// specify an invalid detach key, container will ignore it and use default
 	cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-A,a", name)
@@ -235,19 +234,19 @@ func (s *DockerSuite) TestRunAttachDetachFromInvalidFlag(c *check.C) {
 	}
 	// it should print a warning to indicate the detach key flag is invalid
 	errStr := "Invalid detach keys (ctrl-A,a) provided"
-	c.Assert(strings.TrimSpace(out), checker.Equals, errStr)
+	assert.Equal(c, strings.TrimSpace(out), errStr)
 }
 
 // TestRunAttachDetachFromConfig checks attaching and detaching with the escape sequence specified via config file.
-func (s *DockerSuite) TestRunAttachDetachFromConfig(c *check.C) {
+func (s *DockerSuite) TestRunAttachDetachFromConfig(c *testing.T) {
 	keyCtrlA := []byte{1}
 	keyA := []byte{97}
 
 	// Setup config
 	homeKey := homedir.Key()
 	homeVal := homedir.Get()
-	tmpDir, err := ioutil.TempDir("", "fake-home")
-	c.Assert(err, checker.IsNil)
+	tmpDir, err := os.MkdirTemp("", "fake-home")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmpDir)
 
 	dotDocker := filepath.Join(tmpDir, ".docker")
@@ -261,8 +260,8 @@ func (s *DockerSuite) TestRunAttachDetachFromConfig(c *check.C) {
 		"detachKeys": "ctrl-a,a"
 	}`
 
-	err = ioutil.WriteFile(tmpCfg, []byte(data), 0600)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(tmpCfg, []byte(data), 0600)
+	assert.NilError(c, err)
 
 	// Then do the work
 	name := "attach-detach"
@@ -282,7 +281,7 @@ func (s *DockerSuite) TestRunAttachDetachFromConfig(c *check.C) {
 	if err := cmd.Start(); err != nil {
 		c.Fatal(err)
 	}
-	c.Assert(waitRun(name), check.IsNil)
+	assert.Assert(c, waitRun(name) == nil)
 
 	if _, err := cpty.Write([]byte("hello\n")); err != nil {
 		c.Fatal(err)
@@ -305,7 +304,7 @@ func (s *DockerSuite) TestRunAttachDetachFromConfig(c *check.C) {
 		c.Fatal(err)
 	}
 
-	ch := make(chan struct{})
+	ch := make(chan struct{}, 1)
 	go func() {
 		cmd.Wait()
 		ch <- struct{}{}
@@ -318,19 +317,19 @@ func (s *DockerSuite) TestRunAttachDetachFromConfig(c *check.C) {
 	}
 
 	running := inspectField(c, name, "State.Running")
-	c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
+	assert.Equal(c, running, "true", "expected container to still be running")
 }
 
 // TestRunAttachDetachKeysOverrideConfig checks attaching and detaching with the detach flags, making sure it overrides config file
-func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *check.C) {
+func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *testing.T) {
 	keyCtrlA := []byte{1}
 	keyA := []byte{97}
 
 	// Setup config
 	homeKey := homedir.Key()
 	homeVal := homedir.Get()
-	tmpDir, err := ioutil.TempDir("", "fake-home")
-	c.Assert(err, checker.IsNil)
+	tmpDir, err := os.MkdirTemp("", "fake-home")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmpDir)
 
 	dotDocker := filepath.Join(tmpDir, ".docker")
@@ -344,8 +343,8 @@ func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *check.C) {
 		"detachKeys": "ctrl-e,e"
 	}`
 
-	err = ioutil.WriteFile(tmpCfg, []byte(data), 0600)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(tmpCfg, []byte(data), 0600)
+	assert.NilError(c, err)
 
 	// Then do the work
 	name := "attach-detach"
@@ -365,7 +364,7 @@ func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *check.C) {
 	if err := cmd.Start(); err != nil {
 		c.Fatal(err)
 	}
-	c.Assert(waitRun(name), check.IsNil)
+	assert.Assert(c, waitRun(name) == nil)
 
 	if _, err := cpty.Write([]byte("hello\n")); err != nil {
 		c.Fatal(err)
@@ -388,7 +387,7 @@ func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *check.C) {
 		c.Fatal(err)
 	}
 
-	ch := make(chan struct{})
+	ch := make(chan struct{}, 1)
 	go func() {
 		cmd.Wait()
 		ch <- struct{}{}
@@ -401,10 +400,10 @@ func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *check.C) {
 	}
 
 	running := inspectField(c, name, "State.Running")
-	c.Assert(running, checker.Equals, "true", check.Commentf("expected container to still be running"))
+	assert.Equal(c, running, "true", "expected container to still be running")
 }
 
-func (s *DockerSuite) TestRunAttachInvalidDetachKeySequencePreserved(c *check.C) {
+func (s *DockerSuite) TestRunAttachInvalidDetachKeySequencePreserved(c *testing.T) {
 	name := "attach-detach"
 	keyA := []byte{97}
 	keyB := []byte{98}
@@ -426,7 +425,7 @@ func (s *DockerSuite) TestRunAttachInvalidDetachKeySequencePreserved(c *check.C)
 		c.Fatal(err)
 	}
 	go cmd.Wait()
-	c.Assert(waitRun(name), check.IsNil)
+	assert.Assert(c, waitRun(name) == nil)
 
 	// Invalid escape sequence aba, should print aba in output
 	if _, err := cpty.Write(keyA); err != nil {
@@ -455,87 +454,60 @@ func (s *DockerSuite) TestRunAttachInvalidDetachKeySequencePreserved(c *check.C)
 }
 
 // "test" should be printed
-func (s *DockerSuite) TestRunWithCPUQuota(c *check.C) {
+func (s *DockerSuite) TestRunWithCPUQuota(c *testing.T) {
 	testRequires(c, cpuCfsQuota)
 
 	file := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
 	out, _ := dockerCmd(c, "run", "--cpu-quota", "8000", "--name", "test", "busybox", "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "8000")
+	assert.Equal(c, strings.TrimSpace(out), "8000")
 
 	out = inspectField(c, "test", "HostConfig.CpuQuota")
-	c.Assert(out, checker.Equals, "8000", check.Commentf("setting the CPU CFS quota failed"))
+	assert.Equal(c, out, "8000", "setting the CPU CFS quota failed")
 }
 
-func (s *DockerSuite) TestRunWithCpuPeriod(c *check.C) {
+func (s *DockerSuite) TestRunWithCpuPeriod(c *testing.T) {
 	testRequires(c, cpuCfsPeriod)
 
 	file := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
 	out, _ := dockerCmd(c, "run", "--cpu-period", "50000", "--name", "test", "busybox", "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "50000")
+	assert.Equal(c, strings.TrimSpace(out), "50000")
 
 	out, _ = dockerCmd(c, "run", "--cpu-period", "0", "busybox", "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "100000")
+	assert.Equal(c, strings.TrimSpace(out), "100000")
 
 	out = inspectField(c, "test", "HostConfig.CpuPeriod")
-	c.Assert(out, checker.Equals, "50000", check.Commentf("setting the CPU CFS period failed"))
+	assert.Equal(c, out, "50000", "setting the CPU CFS period failed")
 }
 
-func (s *DockerSuite) TestRunWithInvalidCpuPeriod(c *check.C) {
+func (s *DockerSuite) TestRunWithInvalidCpuPeriod(c *testing.T) {
 	testRequires(c, cpuCfsPeriod)
 	out, _, err := dockerCmdWithError("run", "--cpu-period", "900", "busybox", "true")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	expected := "CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)"
-	c.Assert(out, checker.Contains, expected)
+	assert.Assert(c, strings.Contains(out, expected))
 
 	out, _, err = dockerCmdWithError("run", "--cpu-period", "2000000", "busybox", "true")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, expected)
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, expected))
 
 	out, _, err = dockerCmdWithError("run", "--cpu-period", "-3", "busybox", "true")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, expected)
-}
-
-func (s *DockerSuite) TestRunWithKernelMemory(c *check.C) {
-	testRequires(c, kernelMemorySupport)
-
-	file := "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"
-	cli.DockerCmd(c, "run", "--kernel-memory", "50M", "--name", "test1", "busybox", "cat", file).Assert(c, icmd.Expected{
-		Out: "52428800",
-	})
-
-	cli.InspectCmd(c, "test1", cli.Format(".HostConfig.KernelMemory")).Assert(c, icmd.Expected{
-		Out: "52428800",
-	})
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, expected))
 }
 
-func (s *DockerSuite) TestRunWithInvalidKernelMemory(c *check.C) {
-	testRequires(c, kernelMemorySupport)
-
-	out, _, err := dockerCmdWithError("run", "--kernel-memory", "2M", "busybox", "true")
-	c.Assert(err, check.NotNil)
-	expected := "Minimum kernel memory limit allowed is 4MB"
-	c.Assert(out, checker.Contains, expected)
-
-	out, _, err = dockerCmdWithError("run", "--kernel-memory", "-16m", "--name", "test2", "busybox", "echo", "test")
-	c.Assert(err, check.NotNil)
-	expected = "invalid size"
-	c.Assert(out, checker.Contains, expected)
-}
-
-func (s *DockerSuite) TestRunWithCPUShares(c *check.C) {
+func (s *DockerSuite) TestRunWithCPUShares(c *testing.T) {
 	testRequires(c, cpuShare)
 
 	file := "/sys/fs/cgroup/cpu/cpu.shares"
 	out, _ := dockerCmd(c, "run", "--cpu-shares", "1000", "--name", "test", "busybox", "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "1000")
+	assert.Equal(c, strings.TrimSpace(out), "1000")
 
 	out = inspectField(c, "test", "HostConfig.CPUShares")
-	c.Assert(out, check.Equals, "1000")
+	assert.Equal(c, out, "1000")
 }
 
 // "test" should be printed
-func (s *DockerSuite) TestRunEchoStdoutWithCPUSharesAndMemoryLimit(c *check.C) {
+func (s *DockerSuite) TestRunEchoStdoutWithCPUSharesAndMemoryLimit(c *testing.T) {
 	testRequires(c, cpuShare)
 	testRequires(c, memoryLimitSupport)
 	cli.DockerCmd(c, "run", "--cpu-shares", "1000", "-m", "32m", "busybox", "echo", "test").Assert(c, icmd.Expected{
@@ -543,80 +515,80 @@ func (s *DockerSuite) TestRunEchoStdoutWithCPUSharesAndMemoryLimit(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestRunWithCpusetCpus(c *check.C) {
+func (s *DockerSuite) TestRunWithCpusetCpus(c *testing.T) {
 	testRequires(c, cgroupCpuset)
 
 	file := "/sys/fs/cgroup/cpuset/cpuset.cpus"
 	out, _ := dockerCmd(c, "run", "--cpuset-cpus", "0", "--name", "test", "busybox", "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0")
 
 	out = inspectField(c, "test", "HostConfig.CpusetCpus")
-	c.Assert(out, check.Equals, "0")
+	assert.Equal(c, out, "0")
 }
 
-func (s *DockerSuite) TestRunWithCpusetMems(c *check.C) {
+func (s *DockerSuite) TestRunWithCpusetMems(c *testing.T) {
 	testRequires(c, cgroupCpuset)
 
 	file := "/sys/fs/cgroup/cpuset/cpuset.mems"
 	out, _ := dockerCmd(c, "run", "--cpuset-mems", "0", "--name", "test", "busybox", "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0")
 
 	out = inspectField(c, "test", "HostConfig.CpusetMems")
-	c.Assert(out, check.Equals, "0")
+	assert.Equal(c, out, "0")
 }
 
-func (s *DockerSuite) TestRunWithBlkioWeight(c *check.C) {
+func (s *DockerSuite) TestRunWithBlkioWeight(c *testing.T) {
 	testRequires(c, blkioWeight)
 
 	file := "/sys/fs/cgroup/blkio/blkio.weight"
 	out, _ := dockerCmd(c, "run", "--blkio-weight", "300", "--name", "test", "busybox", "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "300")
+	assert.Equal(c, strings.TrimSpace(out), "300")
 
 	out = inspectField(c, "test", "HostConfig.BlkioWeight")
-	c.Assert(out, check.Equals, "300")
+	assert.Equal(c, out, "300")
 }
 
-func (s *DockerSuite) TestRunWithInvalidBlkioWeight(c *check.C) {
+func (s *DockerSuite) TestRunWithInvalidBlkioWeight(c *testing.T) {
 	testRequires(c, blkioWeight)
 	out, _, err := dockerCmdWithError("run", "--blkio-weight", "5", "busybox", "true")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 	expected := "Range of blkio weight is from 10 to 1000"
-	c.Assert(out, checker.Contains, expected)
+	assert.Assert(c, strings.Contains(out, expected))
 }
 
-func (s *DockerSuite) TestRunWithInvalidPathforBlkioWeightDevice(c *check.C) {
+func (s *DockerSuite) TestRunWithInvalidPathforBlkioWeightDevice(c *testing.T) {
 	testRequires(c, blkioWeight)
 	out, _, err := dockerCmdWithError("run", "--blkio-weight-device", "/dev/sdX:100", "busybox", "true")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 }
 
-func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceReadBps(c *check.C) {
+func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceReadBps(c *testing.T) {
 	testRequires(c, blkioWeight)
 	out, _, err := dockerCmdWithError("run", "--device-read-bps", "/dev/sdX:500", "busybox", "true")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 }
 
-func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceWriteBps(c *check.C) {
+func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceWriteBps(c *testing.T) {
 	testRequires(c, blkioWeight)
 	out, _, err := dockerCmdWithError("run", "--device-write-bps", "/dev/sdX:500", "busybox", "true")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 }
 
-func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceReadIOps(c *check.C) {
+func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceReadIOps(c *testing.T) {
 	testRequires(c, blkioWeight)
 	out, _, err := dockerCmdWithError("run", "--device-read-iops", "/dev/sdX:500", "busybox", "true")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 }
 
-func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceWriteIOps(c *check.C) {
+func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceWriteIOps(c *testing.T) {
 	testRequires(c, blkioWeight)
 	out, _, err := dockerCmdWithError("run", "--device-write-iops", "/dev/sdX:500", "busybox", "true")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 }
 
-func (s *DockerSuite) TestRunOOMExitCode(c *check.C) {
+func (s *DockerSuite) TestRunOOMExitCode(c *testing.T) {
 	testRequires(c, memoryLimitSupport, swapMemorySupport, NotPpc64le)
-	errChan := make(chan error)
+	errChan := make(chan error, 1)
 	go func() {
 		defer close(errChan)
 		// memory limit lower than 8MB will raise an error of "device or resource busy" from docker-runc.
@@ -628,13 +600,13 @@ func (s *DockerSuite) TestRunOOMExitCode(c *check.C) {
 
 	select {
 	case err := <-errChan:
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(600 * time.Second):
 		c.Fatal("Timeout waiting for container to die on OOM")
 	}
 }
 
-func (s *DockerSuite) TestRunWithMemoryLimit(c *check.C) {
+func (s *DockerSuite) TestRunWithMemoryLimit(c *testing.T) {
 	testRequires(c, memoryLimitSupport)
 
 	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
@@ -650,88 +622,86 @@ func (s *DockerSuite) TestRunWithMemoryLimit(c *check.C) {
 // memory limit, this means the processes in the container can use
 // 16M memory and as much swap memory as they need (if the host
 // supports swap memory).
-func (s *DockerSuite) TestRunWithoutMemoryswapLimit(c *check.C) {
+func (s *DockerSuite) TestRunWithoutMemoryswapLimit(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 	testRequires(c, swapMemorySupport)
 	dockerCmd(c, "run", "-m", "32m", "--memory-swap", "-1", "busybox", "true")
 }
 
-func (s *DockerSuite) TestRunWithSwappiness(c *check.C) {
+func (s *DockerSuite) TestRunWithSwappiness(c *testing.T) {
 	testRequires(c, memorySwappinessSupport)
 	file := "/sys/fs/cgroup/memory/memory.swappiness"
 	out, _ := dockerCmd(c, "run", "--memory-swappiness", "0", "--name", "test", "busybox", "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0")
 
 	out = inspectField(c, "test", "HostConfig.MemorySwappiness")
-	c.Assert(out, check.Equals, "0")
+	assert.Equal(c, out, "0")
 }
 
-func (s *DockerSuite) TestRunWithSwappinessInvalid(c *check.C) {
+func (s *DockerSuite) TestRunWithSwappinessInvalid(c *testing.T) {
 	testRequires(c, memorySwappinessSupport)
 	out, _, err := dockerCmdWithError("run", "--memory-swappiness", "101", "busybox", "true")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	expected := "Valid memory swappiness range is 0-100"
-	c.Assert(out, checker.Contains, expected, check.Commentf("Expected output to contain %q, not %q", out, expected))
-
+	assert.Assert(c, strings.Contains(out, expected), "Expected output to contain %q, not %q", out, expected)
 	out, _, err = dockerCmdWithError("run", "--memory-swappiness", "-10", "busybox", "true")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, expected, check.Commentf("Expected output to contain %q, not %q", out, expected))
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, expected), "Expected output to contain %q, not %q", out, expected)
 }
 
-func (s *DockerSuite) TestRunWithMemoryReservation(c *check.C) {
-	testRequires(c, SameHostDaemon, memoryReservationSupport)
+func (s *DockerSuite) TestRunWithMemoryReservation(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, memoryReservationSupport)
 
 	file := "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"
 	out, _ := dockerCmd(c, "run", "--memory-reservation", "200M", "--name", "test", "busybox", "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "209715200")
+	assert.Equal(c, strings.TrimSpace(out), "209715200")
 
 	out = inspectField(c, "test", "HostConfig.MemoryReservation")
-	c.Assert(out, check.Equals, "209715200")
+	assert.Equal(c, out, "209715200")
 }
 
-func (s *DockerSuite) TestRunWithMemoryReservationInvalid(c *check.C) {
+func (s *DockerSuite) TestRunWithMemoryReservationInvalid(c *testing.T) {
 	testRequires(c, memoryLimitSupport)
-	testRequires(c, SameHostDaemon, memoryReservationSupport)
+	testRequires(c, testEnv.IsLocalDaemon, memoryReservationSupport)
 	out, _, err := dockerCmdWithError("run", "-m", "500M", "--memory-reservation", "800M", "busybox", "true")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	expected := "Minimum memory limit can not be less than memory reservation limit"
-	c.Assert(strings.TrimSpace(out), checker.Contains, expected, check.Commentf("run container should fail with invalid memory reservation"))
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), expected), "run container should fail with invalid memory reservation")
 	out, _, err = dockerCmdWithError("run", "--memory-reservation", "1k", "busybox", "true")
-	c.Assert(err, check.NotNil)
-	expected = "Minimum memory reservation allowed is 4MB"
-	c.Assert(strings.TrimSpace(out), checker.Contains, expected, check.Commentf("run container should fail with invalid memory reservation"))
+	assert.ErrorContains(c, err, "")
+	expected = "Minimum memory reservation allowed is 6MB"
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), expected), "run container should fail with invalid memory reservation")
 }
 
-func (s *DockerSuite) TestStopContainerSignal(c *check.C) {
+func (s *DockerSuite) TestStopContainerSignal(c *testing.T) {
 	out, _ := dockerCmd(c, "run", "--stop-signal", "SIGUSR1", "-d", "busybox", "/bin/sh", "-c", `trap 'echo "exit trapped"; exit 0' USR1; while true; do sleep 1; done`)
 	containerID := strings.TrimSpace(out)
 
-	c.Assert(waitRun(containerID), checker.IsNil)
+	assert.Assert(c, waitRun(containerID) == nil)
 
 	dockerCmd(c, "stop", containerID)
 	out, _ = dockerCmd(c, "logs", containerID)
 
-	c.Assert(out, checker.Contains, "exit trapped", check.Commentf("Expected `exit trapped` in the log"))
+	assert.Assert(c, strings.Contains(out, "exit trapped"), "Expected `exit trapped` in the log")
 }
 
-func (s *DockerSuite) TestRunSwapLessThanMemoryLimit(c *check.C) {
+func (s *DockerSuite) TestRunSwapLessThanMemoryLimit(c *testing.T) {
 	testRequires(c, memoryLimitSupport)
 	testRequires(c, swapMemorySupport)
 	out, _, err := dockerCmdWithError("run", "-m", "16m", "--memory-swap", "15m", "busybox", "echo", "test")
 	expected := "Minimum memoryswap limit should be larger than memory limit"
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
-	c.Assert(out, checker.Contains, expected)
+	assert.Assert(c, strings.Contains(out, expected))
 }
 
-func (s *DockerSuite) TestRunInvalidCpusetCpusFlagValue(c *check.C) {
-	testRequires(c, cgroupCpuset, SameHostDaemon)
+func (s *DockerSuite) TestRunInvalidCpusetCpusFlagValue(c *testing.T) {
+	testRequires(c, cgroupCpuset, testEnv.IsLocalDaemon)
 
-	sysInfo := sysinfo.New(true)
+	sysInfo := sysinfo.New()
 	cpus, err := parsers.ParseUintList(sysInfo.Cpus)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	var invalid int
 	for i := 0; i <= len(cpus)+1; i++ {
 		if !cpus[i] {
@@ -740,17 +710,17 @@ func (s *DockerSuite) TestRunInvalidCpusetCpusFlagValue(c *check.C) {
 		}
 	}
 	out, _, err := dockerCmdWithError("run", "--cpuset-cpus", strconv.Itoa(invalid), "busybox", "true")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	expected := fmt.Sprintf("Error response from daemon: Requested CPUs are not available - requested %s, available: %s", strconv.Itoa(invalid), sysInfo.Cpus)
-	c.Assert(out, checker.Contains, expected)
+	assert.Assert(c, strings.Contains(out, expected))
 }
 
-func (s *DockerSuite) TestRunInvalidCpusetMemsFlagValue(c *check.C) {
+func (s *DockerSuite) TestRunInvalidCpusetMemsFlagValue(c *testing.T) {
 	testRequires(c, cgroupCpuset)
 
-	sysInfo := sysinfo.New(true)
+	sysInfo := sysinfo.New()
 	mems, err := parsers.ParseUintList(sysInfo.Mems)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	var invalid int
 	for i := 0; i <= len(mems)+1; i++ {
 		if !mems[i] {
@@ -759,30 +729,30 @@ func (s *DockerSuite) TestRunInvalidCpusetMemsFlagValue(c *check.C) {
 		}
 	}
 	out, _, err := dockerCmdWithError("run", "--cpuset-mems", strconv.Itoa(invalid), "busybox", "true")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 	expected := fmt.Sprintf("Error response from daemon: Requested memory nodes are not available - requested %s, available: %s", strconv.Itoa(invalid), sysInfo.Mems)
-	c.Assert(out, checker.Contains, expected)
+	assert.Assert(c, strings.Contains(out, expected))
 }
 
-func (s *DockerSuite) TestRunInvalidCPUShares(c *check.C) {
+func (s *DockerSuite) TestRunInvalidCPUShares(c *testing.T) {
 	testRequires(c, cpuShare, DaemonIsLinux)
 	out, _, err := dockerCmdWithError("run", "--cpu-shares", "1", "busybox", "echo", "test")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	expected := "The minimum allowed cpu-shares is 2"
-	c.Assert(out, checker.Contains, expected)
+	assert.ErrorContains(c, err, "", out)
+	expected := "minimum allowed cpu-shares is 2"
+	assert.Assert(c, strings.Contains(out, expected))
 
 	out, _, err = dockerCmdWithError("run", "--cpu-shares", "-1", "busybox", "echo", "test")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 	expected = "shares: invalid argument"
-	c.Assert(out, checker.Contains, expected)
+	assert.Assert(c, strings.Contains(out, expected))
 
 	out, _, err = dockerCmdWithError("run", "--cpu-shares", "99999999", "busybox", "echo", "test")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	expected = "The maximum allowed cpu-shares is"
-	c.Assert(out, checker.Contains, expected)
+	assert.ErrorContains(c, err, "", out)
+	expected = "maximum allowed cpu-shares is"
+	assert.Assert(c, strings.Contains(out, expected))
 }
 
-func (s *DockerSuite) TestRunWithDefaultShmSize(c *check.C) {
+func (s *DockerSuite) TestRunWithDefaultShmSize(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	name := "shm-default"
@@ -792,10 +762,10 @@ func (s *DockerSuite) TestRunWithDefaultShmSize(c *check.C) {
 		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
 	}
 	shmSize := inspectField(c, name, "HostConfig.ShmSize")
-	c.Assert(shmSize, check.Equals, "67108864")
+	assert.Equal(c, shmSize, "67108864")
 }
 
-func (s *DockerSuite) TestRunWithShmSize(c *check.C) {
+func (s *DockerSuite) TestRunWithShmSize(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	name := "shm"
@@ -805,18 +775,18 @@ func (s *DockerSuite) TestRunWithShmSize(c *check.C) {
 		c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
 	}
 	shmSize := inspectField(c, name, "HostConfig.ShmSize")
-	c.Assert(shmSize, check.Equals, "1073741824")
+	assert.Equal(c, shmSize, "1073741824")
 }
 
-func (s *DockerSuite) TestRunTmpfsMountsEnsureOrdered(c *check.C) {
-	tmpFile, err := ioutil.TempFile("", "test")
-	c.Assert(err, check.IsNil)
+func (s *DockerSuite) TestRunTmpfsMountsEnsureOrdered(c *testing.T) {
+	tmpFile, err := os.CreateTemp("", "test")
+	assert.NilError(c, err)
 	defer tmpFile.Close()
 	out, _ := dockerCmd(c, "run", "--tmpfs", "/run", "-v", tmpFile.Name()+":/run/test", "busybox", "ls", "/run")
-	c.Assert(out, checker.Contains, "test")
+	assert.Assert(c, strings.Contains(out, "test"))
 }
 
-func (s *DockerSuite) TestRunTmpfsMounts(c *check.C) {
+func (s *DockerSuite) TestRunTmpfsMounts(c *testing.T) {
 	// TODO Windows (Post TP5): This test cannot run on a Windows daemon as
 	// Windows does not support tmpfs mounts.
 	testRequires(c, DaemonIsLinux)
@@ -837,7 +807,7 @@ func (s *DockerSuite) TestRunTmpfsMounts(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunTmpfsMountsOverrideImageVolumes(c *check.C) {
+func (s *DockerSuite) TestRunTmpfsMountsOverrideImageVolumes(c *testing.T) {
 	name := "img-with-volumes"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`
     FROM busybox
@@ -845,72 +815,70 @@ func (s *DockerSuite) TestRunTmpfsMountsOverrideImageVolumes(c *check.C) {
     RUN touch /run/stuff
     `))
 	out, _ := dockerCmd(c, "run", "--tmpfs", "/run", name, "ls", "/run")
-	c.Assert(out, checker.Not(checker.Contains), "stuff")
+	assert.Assert(c, !strings.Contains(out, "stuff"))
 }
 
 // Test case for #22420
-func (s *DockerSuite) TestRunTmpfsMountsWithOptions(c *check.C) {
+func (s *DockerSuite) TestRunTmpfsMountsWithOptions(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	expectedOptions := []string{"rw", "nosuid", "nodev", "noexec", "relatime"}
 	out, _ := dockerCmd(c, "run", "--tmpfs", "/tmp", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'")
 	for _, option := range expectedOptions {
-		c.Assert(out, checker.Contains, option)
+		assert.Assert(c, strings.Contains(out, option))
 	}
-	c.Assert(out, checker.Not(checker.Contains), "size=")
-
+	assert.Assert(c, !strings.Contains(out, "size="))
 	expectedOptions = []string{"rw", "nosuid", "nodev", "noexec", "relatime"}
 	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:rw", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'")
 	for _, option := range expectedOptions {
-		c.Assert(out, checker.Contains, option)
+		assert.Assert(c, strings.Contains(out, option))
 	}
-	c.Assert(out, checker.Not(checker.Contains), "size=")
-
+	assert.Assert(c, !strings.Contains(out, "size="))
 	expectedOptions = []string{"rw", "nosuid", "nodev", "relatime", "size=8192k"}
 	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:rw,exec,size=8192k", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'")
 	for _, option := range expectedOptions {
-		c.Assert(out, checker.Contains, option)
+		assert.Assert(c, strings.Contains(out, option))
 	}
 
 	expectedOptions = []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k"}
 	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:rw,size=8192k,exec,size=4096k,noexec", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'")
 	for _, option := range expectedOptions {
-		c.Assert(out, checker.Contains, option)
+		assert.Assert(c, strings.Contains(out, option))
 	}
 
-	// We use debian:jessie as there is no findmnt in busybox. Also the output will be in the format of
+	// We use debian:bullseye-slim as there is no findmnt in busybox. Also the output will be in the format of
 	// TARGET PROPAGATION
 	// /tmp   shared
 	// so we only capture `shared` here.
 	expectedOptions = []string{"shared"}
-	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:shared", "debian:jessie", "findmnt", "-o", "TARGET,PROPAGATION", "/tmp")
+	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:shared", "debian:bullseye-slim", "findmnt", "-o", "TARGET,PROPAGATION", "/tmp")
 	for _, option := range expectedOptions {
-		c.Assert(out, checker.Contains, option)
+		assert.Assert(c, strings.Contains(out, option))
 	}
 }
 
-func (s *DockerSuite) TestRunSysctls(c *check.C) {
+func (s *DockerSuite) TestRunSysctls(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	var err error
 
 	out, _ := dockerCmd(c, "run", "--sysctl", "net.ipv4.ip_forward=1", "--name", "test", "busybox", "cat", "/proc/sys/net/ipv4/ip_forward")
-	c.Assert(strings.TrimSpace(out), check.Equals, "1")
+	assert.Equal(c, strings.TrimSpace(out), "1")
 
 	out = inspectFieldJSON(c, "test", "HostConfig.Sysctls")
 
 	sysctls := make(map[string]string)
 	err = json.Unmarshal([]byte(out), &sysctls)
-	c.Assert(err, check.IsNil)
-	c.Assert(sysctls["net.ipv4.ip_forward"], check.Equals, "1")
+	assert.NilError(c, err)
+	assert.Equal(c, sysctls["net.ipv4.ip_forward"], "1")
 
 	out, _ = dockerCmd(c, "run", "--sysctl", "net.ipv4.ip_forward=0", "--name", "test1", "busybox", "cat", "/proc/sys/net/ipv4/ip_forward")
-	c.Assert(strings.TrimSpace(out), check.Equals, "0")
+	assert.Equal(c, strings.TrimSpace(out), "0")
 
 	out = inspectFieldJSON(c, "test1", "HostConfig.Sysctls")
 
 	err = json.Unmarshal([]byte(out), &sysctls)
-	c.Assert(err, check.IsNil)
-	c.Assert(sysctls["net.ipv4.ip_forward"], check.Equals, "0")
+	assert.NilError(c, err)
+	assert.Equal(c, sysctls["net.ipv4.ip_forward"], "0")
 
 	icmd.RunCommand(dockerBinary, "run", "--sysctl", "kernel.foobar=1", "--name", "test2",
 		"busybox", "cat", "/proc/sys/kernel/foobar").Assert(c, icmd.Expected{
@@ -919,9 +887,9 @@ func (s *DockerSuite) TestRunSysctls(c *check.C) {
 	})
 }
 
-// TestRunSeccompProfileDenyUnshare checks that 'docker run --security-opt seccomp=/tmp/profile.json debian:jessie unshare' exits with operation not permitted.
-func (s *DockerSuite) TestRunSeccompProfileDenyUnshare(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled, NotArm, Apparmor)
+// TestRunSeccompProfileDenyUnshare checks that 'docker run --security-opt seccomp=/tmp/profile.json debian:bullseye-slim unshare' exits with operation not permitted.
+func (s *DockerSuite) TestRunSeccompProfileDenyUnshare(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotArm, Apparmor)
 	jsonData := `{
 	"defaultAction": "SCMP_ACT_ALLOW",
 	"syscalls": [
@@ -931,7 +899,7 @@ func (s *DockerSuite) TestRunSeccompProfileDenyUnshare(c *check.C) {
 		}
 	]
 }`
-	tmpFile, err := ioutil.TempFile("", "profile.json")
+	tmpFile, err := os.CreateTemp("", "profile.json")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -942,15 +910,15 @@ func (s *DockerSuite) TestRunSeccompProfileDenyUnshare(c *check.C) {
 	}
 	icmd.RunCommand(dockerBinary, "run", "--security-opt", "apparmor=unconfined",
 		"--security-opt", "seccomp="+tmpFile.Name(),
-		"debian:jessie", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc").Assert(c, icmd.Expected{
+		"debian:bullseye-slim", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc").Assert(c, icmd.Expected{
 		ExitCode: 1,
 		Err:      "Operation not permitted",
 	})
 }
 
 // TestRunSeccompProfileDenyChmod checks that 'docker run --security-opt seccomp=/tmp/profile.json busybox chmod 400 /etc/hostname' exits with operation not permitted.
-func (s *DockerSuite) TestRunSeccompProfileDenyChmod(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled)
+func (s *DockerSuite) TestRunSeccompProfileDenyChmod(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
 	jsonData := `{
 	"defaultAction": "SCMP_ACT_ALLOW",
 	"syscalls": [
@@ -968,8 +936,8 @@ func (s *DockerSuite) TestRunSeccompProfileDenyChmod(c *check.C) {
 		}
 	]
 }`
-	tmpFile, err := ioutil.TempFile("", "profile.json")
-	c.Assert(err, check.IsNil)
+	tmpFile, err := os.CreateTemp("", "profile.json")
+	assert.NilError(c, err)
 	defer tmpFile.Close()
 
 	if _, err := tmpFile.Write([]byte(jsonData)); err != nil {
@@ -982,10 +950,10 @@ func (s *DockerSuite) TestRunSeccompProfileDenyChmod(c *check.C) {
 	})
 }
 
-// TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:jessie unshare --map-root-user --user sh -c whoami' with a specific profile to
+// TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:bullseye-slim unshare --map-root-user --user sh -c whoami' with a specific profile to
 // deny unshare of a userns exits with operation not permitted.
-func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled, NotArm, Apparmor)
+func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotArm, Apparmor)
 	// from sched.h
 	jsonData := fmt.Sprintf(`{
 	"defaultAction": "SCMP_ACT_ALLOW",
@@ -1003,7 +971,7 @@ func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) {
 		}
 	]
 }`, uint64(0x10000000))
-	tmpFile, err := ioutil.TempFile("", "profile.json")
+	tmpFile, err := os.CreateTemp("", "profile.json")
 	if err != nil {
 		c.Fatal(err)
 	}
@@ -1014,7 +982,7 @@ func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) {
 	}
 	icmd.RunCommand(dockerBinary, "run",
 		"--security-opt", "apparmor=unconfined", "--security-opt", "seccomp="+tmpFile.Name(),
-		"debian:jessie", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami").Assert(c, icmd.Expected{
+		"debian:bullseye-slim", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami").Assert(c, icmd.Expected{
 		ExitCode: 1,
 		Err:      "Operation not permitted",
 	})
@@ -1022,8 +990,8 @@ func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) {
 
 // TestRunSeccompProfileDenyCloneUserns checks that 'docker run syscall-test'
 // with a the default seccomp profile exits with operation not permitted.
-func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled)
+func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
 	ensureSyscallTest(c)
 
 	icmd.RunCommand(dockerBinary, "run", "syscall-test", "userns-test", "id").Assert(c, icmd.Expected{
@@ -1034,8 +1002,8 @@ func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *check.C) {
 
 // TestRunSeccompUnconfinedCloneUserns checks that
 // 'docker run --security-opt seccomp=unconfined syscall-test' allows creating a userns.
-func (s *DockerSuite) TestRunSeccompUnconfinedCloneUserns(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled, UserNamespaceInKernel, NotUserNamespace, unprivilegedUsernsClone)
+func (s *DockerSuite) TestRunSeccompUnconfinedCloneUserns(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, UserNamespaceInKernel, NotUserNamespace, unprivilegedUsernsClone)
 	ensureSyscallTest(c)
 
 	// make sure running w privileged is ok
@@ -1047,8 +1015,8 @@ func (s *DockerSuite) TestRunSeccompUnconfinedCloneUserns(c *check.C) {
 
 // TestRunSeccompAllowPrivCloneUserns checks that 'docker run --privileged syscall-test'
 // allows creating a userns.
-func (s *DockerSuite) TestRunSeccompAllowPrivCloneUserns(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled, UserNamespaceInKernel, NotUserNamespace)
+func (s *DockerSuite) TestRunSeccompAllowPrivCloneUserns(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, UserNamespaceInKernel, NotUserNamespace)
 	ensureSyscallTest(c)
 
 	// make sure running w privileged is ok
@@ -1059,23 +1027,23 @@ func (s *DockerSuite) TestRunSeccompAllowPrivCloneUserns(c *check.C) {
 
 // TestRunSeccompProfileAllow32Bit checks that 32 bit code can run on x86_64
 // with the default seccomp profile.
-func (s *DockerSuite) TestRunSeccompProfileAllow32Bit(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled, IsAmd64)
+func (s *DockerSuite) TestRunSeccompProfileAllow32Bit(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, IsAmd64)
 	ensureSyscallTest(c)
 
 	icmd.RunCommand(dockerBinary, "run", "syscall-test", "exit32-test").Assert(c, icmd.Success)
 }
 
-// TestRunSeccompAllowSetrlimit checks that 'docker run debian:jessie ulimit -v 1048510' succeeds.
-func (s *DockerSuite) TestRunSeccompAllowSetrlimit(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled)
+// TestRunSeccompAllowSetrlimit checks that 'docker run debian:bullseye-slim ulimit -v 1048510' succeeds.
+func (s *DockerSuite) TestRunSeccompAllowSetrlimit(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
 
 	// ulimit uses setrlimit, so we want to make sure we don't break it
-	icmd.RunCommand(dockerBinary, "run", "debian:jessie", "bash", "-c", "ulimit -v 1048510").Assert(c, icmd.Success)
+	icmd.RunCommand(dockerBinary, "run", "debian:bullseye-slim", "bash", "-c", "ulimit -v 1048510").Assert(c, icmd.Success)
 }
 
-func (s *DockerSuite) TestRunSeccompDefaultProfileAcct(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled, NotUserNamespace)
+func (s *DockerSuite) TestRunSeccompDefaultProfileAcct(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotUserNamespace)
 	ensureSyscallTest(c)
 
 	out, _, err := dockerCmdWithError("run", "syscall-test", "acct-test")
@@ -1104,8 +1072,8 @@ func (s *DockerSuite) TestRunSeccompDefaultProfileAcct(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestRunSeccompDefaultProfileNS(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled, NotUserNamespace)
+func (s *DockerSuite) TestRunSeccompDefaultProfileNS(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotUserNamespace)
 	ensureSyscallTest(c)
 
 	out, _, err := dockerCmdWithError("run", "syscall-test", "ns-test", "echo", "hello0")
@@ -1141,8 +1109,8 @@ func (s *DockerSuite) TestRunSeccompDefaultProfileNS(c *check.C) {
 
 // TestRunNoNewPrivSetuid checks that --security-opt='no-new-privileges=true' prevents
 // effective uid transitions on executing setuid binaries.
-func (s *DockerSuite) TestRunNoNewPrivSetuid(c *check.C) {
-	testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon)
+func (s *DockerSuite) TestRunNoNewPrivSetuid(c *testing.T) {
+	testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon)
 	ensureNNPTest(c)
 
 	// test that running a setuid binary results in no effective uid transition
@@ -1154,8 +1122,8 @@ func (s *DockerSuite) TestRunNoNewPrivSetuid(c *check.C) {
 
 // TestLegacyRunNoNewPrivSetuid checks that --security-opt=no-new-privileges prevents
 // effective uid transitions on executing setuid binaries.
-func (s *DockerSuite) TestLegacyRunNoNewPrivSetuid(c *check.C) {
-	testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon)
+func (s *DockerSuite) TestLegacyRunNoNewPrivSetuid(c *testing.T) {
+	testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon)
 	ensureNNPTest(c)
 
 	// test that running a setuid binary results in no effective uid transition
@@ -1165,8 +1133,8 @@ func (s *DockerSuite) TestLegacyRunNoNewPrivSetuid(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestUserNoEffectiveCapabilitiesChown(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestUserNoEffectiveCapabilitiesChown(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	ensureSyscallTest(c)
 
 	// test that a root user has default capability CAP_CHOWN
@@ -1183,8 +1151,8 @@ func (s *DockerSuite) TestUserNoEffectiveCapabilitiesChown(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestUserNoEffectiveCapabilitiesDacOverride(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestUserNoEffectiveCapabilitiesDacOverride(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	ensureSyscallTest(c)
 
 	// test that a root user has default capability CAP_DAC_OVERRIDE
@@ -1196,8 +1164,8 @@ func (s *DockerSuite) TestUserNoEffectiveCapabilitiesDacOverride(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestUserNoEffectiveCapabilitiesFowner(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestUserNoEffectiveCapabilitiesFowner(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	ensureSyscallTest(c)
 
 	// test that a root user has default capability CAP_FOWNER
@@ -1212,8 +1180,8 @@ func (s *DockerSuite) TestUserNoEffectiveCapabilitiesFowner(c *check.C) {
 
 // TODO CAP_KILL
 
-func (s *DockerSuite) TestUserNoEffectiveCapabilitiesSetuid(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestUserNoEffectiveCapabilitiesSetuid(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	ensureSyscallTest(c)
 
 	// test that a root user has default capability CAP_SETUID
@@ -1230,8 +1198,8 @@ func (s *DockerSuite) TestUserNoEffectiveCapabilitiesSetuid(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestUserNoEffectiveCapabilitiesSetgid(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestUserNoEffectiveCapabilitiesSetgid(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	ensureSyscallTest(c)
 
 	// test that a root user has default capability CAP_SETGID
@@ -1250,26 +1218,45 @@ func (s *DockerSuite) TestUserNoEffectiveCapabilitiesSetgid(c *check.C) {
 
 // TODO CAP_SETPCAP
 
-func (s *DockerSuite) TestUserNoEffectiveCapabilitiesNetBindService(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+// sysctlExists checks if a sysctl exists; runc will error if we add any that do not actually
+// exist, so do not add the default ones if running on an old kernel.
+func sysctlExists(s string) bool {
+	f := filepath.Join("/proc", "sys", strings.Replace(s, ".", "/", -1))
+	_, err := os.Stat(f)
+	return err == nil
+}
+
+func (s *DockerSuite) TestUserNoEffectiveCapabilitiesNetBindService(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	ensureSyscallTest(c)
 
 	// test that a root user has default capability CAP_NET_BIND_SERVICE
 	dockerCmd(c, "run", "syscall-test", "socket-test")
 	// test that non root user does not have default capability CAP_NET_BIND_SERVICE
-	icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "syscall-test", "socket-test").Assert(c, icmd.Expected{
+	// as we allow this via sysctl, also tweak the sysctl back to default
+	args := []string{"run", "--user", "1000:1000"}
+	if sysctlExists("net.ipv4.ip_unprivileged_port_start") {
+		args = append(args, "--sysctl", "net.ipv4.ip_unprivileged_port_start=1024")
+	}
+	args = append(args, "syscall-test", "socket-test")
+	icmd.RunCommand(dockerBinary, args...).Assert(c, icmd.Expected{
 		ExitCode: 1,
 		Err:      "Permission denied",
 	})
 	// test that root user can drop default capability CAP_NET_BIND_SERVICE
-	icmd.RunCommand(dockerBinary, "run", "--cap-drop", "net_bind_service", "syscall-test", "socket-test").Assert(c, icmd.Expected{
+	args = []string{"run", "--cap-drop", "net_bind_service"}
+	if sysctlExists("net.ipv4.ip_unprivileged_port_start") {
+		args = append(args, "--sysctl", "net.ipv4.ip_unprivileged_port_start=1024")
+	}
+	args = append(args, "syscall-test", "socket-test")
+	icmd.RunCommand(dockerBinary, args...).Assert(c, icmd.Expected{
 		ExitCode: 1,
 		Err:      "Permission denied",
 	})
 }
 
-func (s *DockerSuite) TestUserNoEffectiveCapabilitiesNetRaw(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestUserNoEffectiveCapabilitiesNetRaw(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	ensureSyscallTest(c)
 
 	// test that a root user has default capability CAP_NET_RAW
@@ -1286,8 +1273,8 @@ func (s *DockerSuite) TestUserNoEffectiveCapabilitiesNetRaw(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestUserNoEffectiveCapabilitiesChroot(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerSuite) TestUserNoEffectiveCapabilitiesChroot(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	ensureSyscallTest(c)
 
 	// test that a root user has default capability CAP_SYS_CHROOT
@@ -1304,8 +1291,8 @@ func (s *DockerSuite) TestUserNoEffectiveCapabilitiesChroot(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestUserNoEffectiveCapabilitiesMknod(c *check.C) {
-	testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon)
+func (s *DockerSuite) TestUserNoEffectiveCapabilitiesMknod(c *testing.T) {
+	testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon)
 	ensureSyscallTest(c)
 
 	// test that a root user has default capability CAP_MKNOD
@@ -1326,8 +1313,8 @@ func (s *DockerSuite) TestUserNoEffectiveCapabilitiesMknod(c *check.C) {
 // TODO CAP_AUDIT_WRITE
 // TODO CAP_SETFCAP
 
-func (s *DockerSuite) TestRunApparmorProcDirectory(c *check.C) {
-	testRequires(c, SameHostDaemon, Apparmor)
+func (s *DockerSuite) TestRunApparmorProcDirectory(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, Apparmor)
 
 	// running w seccomp unconfined tests the apparmor profile
 	result := icmd.RunCommand(dockerBinary, "run", "--security-opt", "seccomp=unconfined", "busybox", "chmod", "777", "/proc/1/cgroup")
@@ -1345,40 +1332,40 @@ func (s *DockerSuite) TestRunApparmorProcDirectory(c *check.C) {
 
 // make sure the default profile can be successfully parsed (using unshare as it is
 // something which we know is blocked in the default profile)
-func (s *DockerSuite) TestRunSeccompWithDefaultProfile(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled)
+func (s *DockerSuite) TestRunSeccompWithDefaultProfile(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
 
-	out, _, err := dockerCmdWithError("run", "--security-opt", "seccomp=../profiles/seccomp/default.json", "debian:jessie", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "unshare: unshare failed: Operation not permitted")
+	out, _, err := dockerCmdWithError("run", "--security-opt", "seccomp=../profiles/seccomp/default.json", "debian:bullseye-slim", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami")
+	assert.ErrorContains(c, err, "", out)
+	assert.Equal(c, strings.TrimSpace(out), "unshare: unshare failed: Operation not permitted")
 }
 
 // TestRunDeviceSymlink checks run with device that follows symlink (#13840 and #22271)
-func (s *DockerSuite) TestRunDeviceSymlink(c *check.C) {
-	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm, SameHostDaemon)
+func (s *DockerSuite) TestRunDeviceSymlink(c *testing.T) {
+	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm, testEnv.IsLocalDaemon)
 	if _, err := os.Stat("/dev/zero"); err != nil {
 		c.Skip("Host does not have /dev/zero")
 	}
 
 	// Create a temporary directory to create symlink
-	tmpDir, err := ioutil.TempDir("", "docker_device_follow_symlink_tests")
-	c.Assert(err, checker.IsNil)
+	tmpDir, err := os.MkdirTemp("", "docker_device_follow_symlink_tests")
+	assert.NilError(c, err)
 
 	defer os.RemoveAll(tmpDir)
 
 	// Create a symbolic link to /dev/zero
 	symZero := filepath.Join(tmpDir, "zero")
 	err = os.Symlink("/dev/zero", symZero)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// Create a temporary file "temp" inside tmpDir, write some data to "tmpDir/temp",
 	// then create a symlink "tmpDir/file" to the temporary file "tmpDir/temp".
 	tmpFile := filepath.Join(tmpDir, "temp")
-	err = ioutil.WriteFile(tmpFile, []byte("temp"), 0666)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(tmpFile, []byte("temp"), 0666)
+	assert.NilError(c, err)
 	symFile := filepath.Join(tmpDir, "file")
 	err = os.Symlink(tmpFile, symFile)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// Create a symbolic link to /dev/zero, this time with a relative path (#22271)
 	err = os.Symlink("zero", "/dev/symzero")
@@ -1390,40 +1377,38 @@ func (s *DockerSuite) TestRunDeviceSymlink(c *check.C) {
 
 	// md5sum of 'dd if=/dev/zero bs=4K count=8' is bb7df04e1b0a2570657527a7e108ae23
 	out, _ := dockerCmd(c, "run", "--device", symZero+":/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum")
-	c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "bb7df04e1b0a2570657527a7e108ae23", check.Commentf("expected output bb7df04e1b0a2570657527a7e108ae23"))
-
+	assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "bb7df04e1b0a2570657527a7e108ae23"), "expected output bb7df04e1b0a2570657527a7e108ae23")
 	// symlink "tmpDir/file" to a file "tmpDir/temp" will result in an error as it is not a device.
 	out, _, err = dockerCmdWithError("run", "--device", symFile+":/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum")
-	c.Assert(err, check.NotNil)
-	c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "not a device node", check.Commentf("expected output 'not a device node'"))
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "not a device node"), "expected output 'not a device node'")
 	// md5sum of 'dd if=/dev/zero bs=4K count=8' is bb7df04e1b0a2570657527a7e108ae23 (this time check with relative path backed, see #22271)
 	out, _ = dockerCmd(c, "run", "--device", "/dev/symzero:/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum")
-	c.Assert(strings.Trim(out, "\r\n"), checker.Contains, "bb7df04e1b0a2570657527a7e108ae23", check.Commentf("expected output bb7df04e1b0a2570657527a7e108ae23"))
+	assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "bb7df04e1b0a2570657527a7e108ae23"), "expected output bb7df04e1b0a2570657527a7e108ae23")
 }
 
 // TestRunPIDsLimit makes sure the pids cgroup is set with --pids-limit
-func (s *DockerSuite) TestRunPIDsLimit(c *check.C) {
-	testRequires(c, SameHostDaemon, pidsLimit)
+func (s *DockerSuite) TestRunPIDsLimit(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, pidsLimit)
 
 	file := "/sys/fs/cgroup/pids/pids.max"
 	out, _ := dockerCmd(c, "run", "--name", "skittles", "--pids-limit", "4", "busybox", "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "4")
+	assert.Equal(c, strings.TrimSpace(out), "4")
 
 	out = inspectField(c, "skittles", "HostConfig.PidsLimit")
-	c.Assert(out, checker.Equals, "4", check.Commentf("setting the pids limit failed"))
+	assert.Equal(c, out, "4", "setting the pids limit failed")
 }
 
-func (s *DockerSuite) TestRunPrivilegedAllowedDevices(c *check.C) {
+func (s *DockerSuite) TestRunPrivilegedAllowedDevices(c *testing.T) {
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 
 	file := "/sys/fs/cgroup/devices/devices.list"
 	out, _ := dockerCmd(c, "run", "--privileged", "busybox", "cat", file)
 	c.Logf("out: %q", out)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "a *:* rwm")
+	assert.Equal(c, strings.TrimSpace(out), "a *:* rwm")
 }
 
-func (s *DockerSuite) TestRunUserDeviceAllowed(c *check.C) {
+func (s *DockerSuite) TestRunUserDeviceAllowed(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	fi, err := os.Stat("/dev/snd/timer")
@@ -1437,11 +1422,11 @@ func (s *DockerSuite) TestRunUserDeviceAllowed(c *check.C) {
 
 	file := "/sys/fs/cgroup/devices/devices.list"
 	out, _ := dockerCmd(c, "run", "--device", "/dev/snd/timer:w", "busybox", "cat", file)
-	c.Assert(out, checker.Contains, fmt.Sprintf("c %d:%d w", stat.Rdev/256, stat.Rdev%256))
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("c %d:%d w", stat.Rdev/256, stat.Rdev%256)))
 }
 
-func (s *DockerDaemonSuite) TestRunSeccompJSONNewFormat(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled)
+func (s *DockerDaemonSuite) TestRunSeccompJSONNewFormat(c *testing.T) {
+	testRequires(c, seccompEnabled)
 
 	s.d.StartWithBusybox(c)
 
@@ -1454,19 +1439,19 @@ func (s *DockerDaemonSuite) TestRunSeccompJSONNewFormat(c *check.C) {
 		}
 	]
 }`
-	tmpFile, err := ioutil.TempFile("", "profile.json")
-	c.Assert(err, check.IsNil)
+	tmpFile, err := os.CreateTemp("", "profile.json")
+	assert.NilError(c, err)
 	defer tmpFile.Close()
 	_, err = tmpFile.Write([]byte(jsonData))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	out, err := s.d.Cmd("run", "--security-opt", "seccomp="+tmpFile.Name(), "busybox", "chmod", "777", ".")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, "Operation not permitted")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Operation not permitted"))
 }
 
-func (s *DockerDaemonSuite) TestRunSeccompJSONNoNameAndNames(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled)
+func (s *DockerDaemonSuite) TestRunSeccompJSONNoNameAndNames(c *testing.T) {
+	testRequires(c, seccompEnabled)
 
 	s.d.StartWithBusybox(c)
 
@@ -1480,19 +1465,19 @@ func (s *DockerDaemonSuite) TestRunSeccompJSONNoNameAndNames(c *check.C) {
 		}
 	]
 }`
-	tmpFile, err := ioutil.TempFile("", "profile.json")
-	c.Assert(err, check.IsNil)
+	tmpFile, err := os.CreateTemp("", "profile.json")
+	assert.NilError(c, err)
 	defer tmpFile.Close()
 	_, err = tmpFile.Write([]byte(jsonData))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	out, err := s.d.Cmd("run", "--security-opt", "seccomp="+tmpFile.Name(), "busybox", "chmod", "777", ".")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, "'name' and 'names' were specified in the seccomp profile, use either 'name' or 'names'")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "use either 'name' or 'names'"))
 }
 
-func (s *DockerDaemonSuite) TestRunSeccompJSONNoArchAndArchMap(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled)
+func (s *DockerDaemonSuite) TestRunSeccompJSONNoArchAndArchMap(c *testing.T) {
+	testRequires(c, seccompEnabled)
 
 	s.d.StartWithBusybox(c)
 
@@ -1517,25 +1502,25 @@ func (s *DockerDaemonSuite) TestRunSeccompJSONNoArchAndArchMap(c *check.C) {
 		}
 	]
 }`
-	tmpFile, err := ioutil.TempFile("", "profile.json")
-	c.Assert(err, check.IsNil)
+	tmpFile, err := os.CreateTemp("", "profile.json")
+	assert.NilError(c, err)
 	defer tmpFile.Close()
 	_, err = tmpFile.Write([]byte(jsonData))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	out, err := s.d.Cmd("run", "--security-opt", "seccomp="+tmpFile.Name(), "busybox", "chmod", "777", ".")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, "'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "use either 'architectures' or 'archMap'"))
 }
 
-func (s *DockerDaemonSuite) TestRunWithDaemonDefaultSeccompProfile(c *check.C) {
-	testRequires(c, SameHostDaemon, seccompEnabled)
+func (s *DockerDaemonSuite) TestRunWithDaemonDefaultSeccompProfile(c *testing.T) {
+	testRequires(c, seccompEnabled)
 
 	s.d.StartWithBusybox(c)
 
 	// 1) verify I can run containers with the Docker default shipped profile which allows chmod
 	_, err := s.d.Cmd("run", "busybox", "chmod", "777", ".")
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	jsonData := `{
 	"defaultAction": "SCMP_ACT_ALLOW",
@@ -1543,43 +1528,47 @@ func (s *DockerDaemonSuite) TestRunWithDaemonDefaultSeccompProfile(c *check.C) {
 		{
 			"name": "chmod",
 			"action": "SCMP_ACT_ERRNO"
+		},
+		{
+			"name": "fchmodat",
+			"action": "SCMP_ACT_ERRNO"
 		}
 	]
 }`
-	tmpFile, err := ioutil.TempFile("", "profile.json")
-	c.Assert(err, check.IsNil)
+	tmpFile, err := os.CreateTemp("", "profile.json")
+	assert.NilError(c, err)
 	defer tmpFile.Close()
 	_, err = tmpFile.Write([]byte(jsonData))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// 2) restart the daemon and add a custom seccomp profile in which we deny chmod
 	s.d.Restart(c, "--seccomp-profile="+tmpFile.Name())
 
 	out, err := s.d.Cmd("run", "busybox", "chmod", "777", ".")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, "Operation not permitted")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Operation not permitted"))
 }
 
-func (s *DockerSuite) TestRunWithNanoCPUs(c *check.C) {
+func (s *DockerSuite) TestRunWithNanoCPUs(c *testing.T) {
 	testRequires(c, cpuCfsQuota, cpuCfsPeriod)
 
 	file1 := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
 	file2 := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
 	out, _ := dockerCmd(c, "run", "--cpus", "0.5", "--name", "test", "busybox", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "50000\n100000")
+	assert.Equal(c, strings.TrimSpace(out), "50000\n100000")
 
-	clt, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	clt, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	inspect, err := clt.ContainerInspect(context.Background(), "test")
-	c.Assert(err, checker.IsNil)
-	c.Assert(inspect.HostConfig.NanoCPUs, checker.Equals, int64(500000000))
+	assert.NilError(c, err)
+	assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(500000000))
 
 	out = inspectField(c, "test", "HostConfig.CpuQuota")
-	c.Assert(out, checker.Equals, "0", check.Commentf("CPU CFS quota should be 0"))
+	assert.Equal(c, out, "0", "CPU CFS quota should be 0")
 	out = inspectField(c, "test", "HostConfig.CpuPeriod")
-	c.Assert(out, checker.Equals, "0", check.Commentf("CPU CFS period should be 0"))
+	assert.Equal(c, out, "0", "CPU CFS period should be 0")
 
 	out, _, err = dockerCmdWithError("run", "--cpus", "0.5", "--cpu-quota", "50000", "--cpu-period", "100000", "busybox", "sh")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, "Conflicting options: Nano CPUs and CPU Period cannot both be set")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Conflicting options: Nano CPUs and CPU Period cannot both be set"))
 }
diff --git a/integration-cli/docker_cli_save_load_test.go b/integration-cli/docker_cli_save_load_test.go
index 688eac684ea83..eac6fb054c96a 100644
--- a/integration-cli/docker_cli_save_load_test.go
+++ b/integration-cli/docker_cli_save_load_test.go
@@ -5,7 +5,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -13,17 +12,18 @@ import (
 	"regexp"
 	"sort"
 	"strings"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
-	"github.com/opencontainers/go-digest"
-	"gotest.tools/icmd"
+	digest "github.com/opencontainers/go-digest"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/icmd"
 )
 
 // save a repo using gz compression and try to load it using stdout
-func (s *DockerSuite) TestSaveXzAndLoadRepoStdout(c *check.C) {
+func (s *DockerSuite) TestSaveXzAndLoadRepoStdout(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "test-save-xz-and-load-repo-stdout"
 	dockerCmd(c, "run", "--name", name, "busybox", "true")
@@ -37,7 +37,7 @@ func (s *DockerSuite) TestSaveXzAndLoadRepoStdout(c *check.C) {
 		exec.Command(dockerBinary, "save", repoName),
 		exec.Command("xz", "-c"),
 		exec.Command("gzip", "-c"))
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save repo: %v %v", out, err))
+	assert.NilError(c, err, "failed to save repo: %v %v", out, err)
 	deleteImages(repoName)
 
 	icmd.RunCmd(icmd.Cmd{
@@ -48,11 +48,11 @@ func (s *DockerSuite) TestSaveXzAndLoadRepoStdout(c *check.C) {
 	})
 
 	after, _, err := dockerCmdWithError("inspect", repoName)
-	c.Assert(err, checker.NotNil, check.Commentf("the repo should not exist: %v", after))
+	assert.ErrorContains(c, err, "", "the repo should not exist: %v", after)
 }
 
 // save a repo using xz+gz compression and try to load it using stdout
-func (s *DockerSuite) TestSaveXzGzAndLoadRepoStdout(c *check.C) {
+func (s *DockerSuite) TestSaveXzGzAndLoadRepoStdout(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "test-save-xz-gz-and-load-repo-stdout"
 	dockerCmd(c, "run", "--name", name, "busybox", "true")
@@ -66,7 +66,7 @@ func (s *DockerSuite) TestSaveXzGzAndLoadRepoStdout(c *check.C) {
 		exec.Command(dockerBinary, "save", repoName),
 		exec.Command("xz", "-c"),
 		exec.Command("gzip", "-c"))
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save repo: %v %v", out, err))
+	assert.NilError(c, err, "failed to save repo: %v %v", out, err)
 
 	deleteImages(repoName)
 
@@ -78,10 +78,10 @@ func (s *DockerSuite) TestSaveXzGzAndLoadRepoStdout(c *check.C) {
 	})
 
 	after, _, err := dockerCmdWithError("inspect", repoName)
-	c.Assert(err, checker.NotNil, check.Commentf("the repo should not exist: %v", after))
+	assert.ErrorContains(c, err, "", "the repo should not exist: %v", after)
 }
 
-func (s *DockerSuite) TestSaveSingleTag(c *check.C) {
+func (s *DockerSuite) TestSaveSingleTag(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	repoName := "foobar-save-single-tag-test"
 	dockerCmd(c, "tag", "busybox:latest", fmt.Sprintf("%v:latest", repoName))
@@ -93,10 +93,10 @@ func (s *DockerSuite) TestSaveSingleTag(c *check.C) {
 		exec.Command(dockerBinary, "save", fmt.Sprintf("%v:latest", repoName)),
 		exec.Command("tar", "t"),
 		exec.Command("grep", "-E", fmt.Sprintf("(^repositories$|%v)", cleanedImageID)))
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save repo with image ID and 'repositories' file: %s, %v", out, err))
+	assert.NilError(c, err, "failed to save repo with image ID and 'repositories' file: %s, %v", out, err)
 }
 
-func (s *DockerSuite) TestSaveCheckTimes(c *check.C) {
+func (s *DockerSuite) TestSaveCheckTimes(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	repoName := "busybox:latest"
 	out, _ := dockerCmd(c, "inspect", repoName)
@@ -105,17 +105,17 @@ func (s *DockerSuite) TestSaveCheckTimes(c *check.C) {
 		Created time.Time
 	}
 	err := json.Unmarshal([]byte(out), &data)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to marshal from %q: err %v", repoName, err))
-	c.Assert(len(data), checker.Not(checker.Equals), 0, check.Commentf("failed to marshal the data from %q", repoName))
+	assert.NilError(c, err, "failed to marshal from %q: err %v", repoName, err)
+	assert.Assert(c, len(data) != 0, "failed to marshal the data from %q", repoName)
 	tarTvTimeFormat := "2006-01-02 15:04"
 	out, err = RunCommandPipelineWithOutput(
 		exec.Command(dockerBinary, "save", repoName),
 		exec.Command("tar", "tv"),
 		exec.Command("grep", "-E", fmt.Sprintf("%s %s", data[0].Created.Format(tarTvTimeFormat), digest.Digest(data[0].ID).Hex())))
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save repo with image ID and 'repositories' file: %s, %v", out, err))
+	assert.NilError(c, err, "failed to save repo with image ID and 'repositories' file: %s, %v", out, err)
 }
 
-func (s *DockerSuite) TestSaveImageId(c *check.C) {
+func (s *DockerSuite) TestSaveImageId(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	repoName := "foobar-save-image-id-test"
 	dockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v:latest", repoName))
@@ -127,21 +127,21 @@ func (s *DockerSuite) TestSaveImageId(c *check.C) {
 	cleanedShortImageID := strings.TrimSpace(out)
 
 	// Make sure IDs are not empty
-	c.Assert(cleanedLongImageID, checker.Not(check.Equals), "", check.Commentf("Id should not be empty."))
-	c.Assert(cleanedShortImageID, checker.Not(check.Equals), "", check.Commentf("Id should not be empty."))
+	assert.Assert(c, cleanedLongImageID != "", "Id should not be empty.")
+	assert.Assert(c, cleanedShortImageID != "", "Id should not be empty.")
 
 	saveCmd := exec.Command(dockerBinary, "save", cleanedShortImageID)
 	tarCmd := exec.Command("tar", "t")
 
 	var err error
 	tarCmd.Stdin, err = saveCmd.StdoutPipe()
-	c.Assert(err, checker.IsNil, check.Commentf("cannot set stdout pipe for tar: %v", err))
+	assert.Assert(c, err == nil, "cannot set stdout pipe for tar: %v", err)
 	grepCmd := exec.Command("grep", cleanedLongImageID)
 	grepCmd.Stdin, err = tarCmd.StdoutPipe()
-	c.Assert(err, checker.IsNil, check.Commentf("cannot set stdout pipe for grep: %v", err))
+	assert.Assert(c, err == nil, "cannot set stdout pipe for grep: %v", err)
 
-	c.Assert(tarCmd.Start(), checker.IsNil, check.Commentf("tar failed with error: %v", err))
-	c.Assert(saveCmd.Start(), checker.IsNil, check.Commentf("docker save failed with error: %v", err))
+	assert.Assert(c, tarCmd.Start() == nil, "tar failed with error: %v", err)
+	assert.Assert(c, saveCmd.Start() == nil, "docker save failed with error: %v", err)
 	defer func() {
 		saveCmd.Wait()
 		tarCmd.Wait()
@@ -150,11 +150,11 @@ func (s *DockerSuite) TestSaveImageId(c *check.C) {
 
 	out, _, err = runCommandWithOutput(grepCmd)
 
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save repo with image ID: %s, %v", out, err))
+	assert.Assert(c, err == nil, "failed to save repo with image ID: %s, %v", out, err)
 }
 
 // save a repo and try to load it using flags
-func (s *DockerSuite) TestSaveAndLoadRepoFlags(c *check.C) {
+func (s *DockerSuite) TestSaveAndLoadRepoFlags(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	name := "test-save-and-load-repo-flags"
 	dockerCmd(c, "run", "--name", name, "busybox", "true")
@@ -169,23 +169,23 @@ func (s *DockerSuite) TestSaveAndLoadRepoFlags(c *check.C) {
 	out, err := RunCommandPipelineWithOutput(
 		exec.Command(dockerBinary, "save", repoName),
 		exec.Command(dockerBinary, "load"))
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save and load repo: %s, %v", out, err))
+	assert.NilError(c, err, "failed to save and load repo: %s, %v", out, err)
 
 	after, _ := dockerCmd(c, "inspect", repoName)
-	c.Assert(before, checker.Equals, after, check.Commentf("inspect is not the same after a save / load"))
+	assert.Equal(c, before, after, "inspect is not the same after a save / load")
 }
 
-func (s *DockerSuite) TestSaveWithNoExistImage(c *check.C) {
+func (s *DockerSuite) TestSaveWithNoExistImage(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	imgName := "foobar-non-existing-image"
 
 	out, _, err := dockerCmdWithError("save", "-o", "test-img.tar", imgName)
-	c.Assert(err, checker.NotNil, check.Commentf("save image should fail for non-existing image"))
-	c.Assert(out, checker.Contains, fmt.Sprintf("No such image: %s", imgName))
+	assert.ErrorContains(c, err, "", "save image should fail for non-existing image")
+	assert.Assert(c, strings.Contains(out, fmt.Sprintf("No such image: %s", imgName)))
 }
 
-func (s *DockerSuite) TestSaveMultipleNames(c *check.C) {
+func (s *DockerSuite) TestSaveMultipleNames(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	repoName := "foobar-save-multi-name-test"
 
@@ -200,10 +200,10 @@ func (s *DockerSuite) TestSaveMultipleNames(c *check.C) {
 		exec.Command("tar", "xO", "repositories"),
 		exec.Command("grep", "-q", "-E", "(-one|-two)"),
 	)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save multiple repos: %s, %v", out, err))
+	assert.NilError(c, err, "failed to save multiple repos: %s, %v", out, err)
 }
 
-func (s *DockerSuite) TestSaveRepoWithMultipleImages(c *check.C) {
+func (s *DockerSuite) TestSaveRepoWithMultipleImages(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	makeImage := func(from string, tag string) string {
 		var (
@@ -230,12 +230,12 @@ func (s *DockerSuite) TestSaveRepoWithMultipleImages(c *check.C) {
 	out, err := RunCommandPipelineWithOutput(
 		exec.Command(dockerBinary, "save", repoName, "busybox:latest"),
 		exec.Command("tar", "t"))
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save multiple images: %s, %v", out, err))
+	assert.NilError(c, err, "failed to save multiple images: %s, %v", out, err)
 
 	lines := strings.Split(strings.TrimSpace(out), "\n")
 	var actual []string
 	for _, l := range lines {
-		if regexp.MustCompile("^[a-f0-9]{64}\\.json$").Match([]byte(l)) {
+		if regexp.MustCompile(`^[a-f0-9]{64}\.json$`).Match([]byte(l)) {
 			actual = append(actual, strings.TrimSuffix(l, ".json"))
 		}
 	}
@@ -251,18 +251,18 @@ func (s *DockerSuite) TestSaveRepoWithMultipleImages(c *check.C) {
 
 	sort.Strings(actual)
 	sort.Strings(expected)
-	c.Assert(actual, checker.DeepEquals, expected, check.Commentf("archive does not contains the right layers: got %v, expected %v, output: %q", actual, expected, out))
+	assert.Assert(c, is.DeepEqual(actual, expected), "archive does not contains the right layers: got %v, expected %v, output: %q", actual, expected, out)
 }
 
 // Issue #6722 #5892 ensure directories are included in changes
-func (s *DockerSuite) TestSaveDirectoryPermissions(c *check.C) {
+func (s *DockerSuite) TestSaveDirectoryPermissions(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	layerEntries := []string{"opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"}
 	layerEntriesAUFS := []string{"./", ".wh..wh.aufs", ".wh..wh.orph/", ".wh..wh.plnk/", "opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"}
 
 	name := "save-directory-permissions"
-	tmpDir, err := ioutil.TempDir("", "save-layers-with-directories")
-	c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary directory: %s", err))
+	tmpDir, err := os.MkdirTemp("", "save-layers-with-directories")
+	assert.Assert(c, err == nil, "failed to create temporary directory: %s", err)
 	extractionDirectory := filepath.Join(tmpDir, "image-extraction-dir")
 	os.Mkdir(extractionDirectory, 0777)
 
@@ -275,10 +275,10 @@ func (s *DockerSuite) TestSaveDirectoryPermissions(c *check.C) {
 		exec.Command(dockerBinary, "save", name),
 		exec.Command("tar", "-xf", "-", "-C", extractionDirectory),
 	)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save and extract image: %s", out))
+	assert.NilError(c, err, "failed to save and extract image: %s", out)
 
-	dirs, err := ioutil.ReadDir(extractionDirectory)
-	c.Assert(err, checker.IsNil, check.Commentf("failed to get a listing of the layer directories: %s", err))
+	dirs, err := os.ReadDir(extractionDirectory)
+	assert.NilError(c, err, "failed to get a listing of the layer directories: %s", err)
 
 	found := false
 	for _, entry := range dirs {
@@ -287,7 +287,8 @@ func (s *DockerSuite) TestSaveDirectoryPermissions(c *check.C) {
 			layerPath := filepath.Join(extractionDirectory, entry.Name(), "layer.tar")
 
 			f, err := os.Open(layerPath)
-			c.Assert(err, checker.IsNil, check.Commentf("failed to open %s: %s", layerPath, err))
+			assert.NilError(c, err, "failed to open %s: %s", layerPath, err)
+
 			defer f.Close()
 
 			entries, err := listTar(f)
@@ -296,7 +297,7 @@ func (s *DockerSuite) TestSaveDirectoryPermissions(c *check.C) {
 					entriesSansDev = append(entriesSansDev, e)
 				}
 			}
-			c.Assert(err, checker.IsNil, check.Commentf("encountered error while listing tar entries: %s", err))
+			assert.NilError(c, err, "encountered error while listing tar entries: %s", err)
 
 			if reflect.DeepEqual(entriesSansDev, layerEntries) || reflect.DeepEqual(entriesSansDev, layerEntriesAUFS) {
 				found = true
@@ -305,8 +306,7 @@ func (s *DockerSuite) TestSaveDirectoryPermissions(c *check.C) {
 		}
 	}
 
-	c.Assert(found, checker.Equals, true, check.Commentf("failed to find the layer with the right content listing"))
-
+	assert.Assert(c, found, "failed to find the layer with the right content listing")
 }
 
 func listTar(f io.Reader) ([]string, error) {
@@ -329,15 +329,15 @@ func listTar(f io.Reader) ([]string, error) {
 // Test loading a weird image where one of the layers is of zero size.
 // The layer.tar file is actually zero bytes, no padding or anything else.
 // See issue: 18170
-func (s *DockerSuite) TestLoadZeroSizeLayer(c *check.C) {
+func (s *DockerSuite) TestLoadZeroSizeLayer(c *testing.T) {
 	// this will definitely not work if using remote daemon
 	// very weird test
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 
 	dockerCmd(c, "load", "-i", "testdata/emptyLayer.tar")
 }
 
-func (s *DockerSuite) TestSaveLoadParents(c *check.C) {
+func (s *DockerSuite) TestSaveLoadParents(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	makeImage := func(from string, addfile string) string {
@@ -357,8 +357,8 @@ func (s *DockerSuite) TestSaveLoadParents(c *check.C) {
 	idFoo := makeImage("busybox", "foo")
 	idBar := makeImage(idFoo, "bar")
 
-	tmpDir, err := ioutil.TempDir("", "save-load-parents")
-	c.Assert(err, checker.IsNil)
+	tmpDir, err := os.MkdirTemp("", "save-load-parents")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmpDir)
 
 	c.Log("tmpdir", tmpDir)
@@ -370,13 +370,13 @@ func (s *DockerSuite) TestSaveLoadParents(c *check.C) {
 	dockerCmd(c, "load", "-i", outfile)
 
 	inspectOut := inspectField(c, idBar, "Parent")
-	c.Assert(inspectOut, checker.Equals, idFoo)
+	assert.Equal(c, inspectOut, idFoo)
 
 	inspectOut = inspectField(c, idFoo, "Parent")
-	c.Assert(inspectOut, checker.Equals, "")
+	assert.Equal(c, inspectOut, "")
 }
 
-func (s *DockerSuite) TestSaveLoadNoTag(c *check.C) {
+func (s *DockerSuite) TestSaveLoadNoTag(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	name := "saveloadnotag"
@@ -388,18 +388,18 @@ func (s *DockerSuite) TestSaveLoadNoTag(c *check.C) {
 	out, err := RunCommandPipelineWithOutput(
 		exec.Command(dockerBinary, "save", id),
 		exec.Command(dockerBinary, "load"))
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save and load repo: %s, %v", out, err))
+	assert.NilError(c, err, "failed to save and load repo: %s, %v", out, err)
 
 	// Should not show 'name' but should show the image ID during the load
-	c.Assert(out, checker.Not(checker.Contains), "Loaded image: ")
-	c.Assert(out, checker.Contains, "Loaded image ID:")
-	c.Assert(out, checker.Contains, id)
-
+	assert.Assert(c, !strings.Contains(out, "Loaded image: "))
+	assert.Assert(c, strings.Contains(out, "Loaded image ID:"))
+	assert.Assert(c, strings.Contains(out, id))
 	// Test to make sure that save by name shows that name during load
 	out, err = RunCommandPipelineWithOutput(
 		exec.Command(dockerBinary, "save", name),
 		exec.Command(dockerBinary, "load"))
-	c.Assert(err, checker.IsNil, check.Commentf("failed to save and load repo: %s, %v", out, err))
-	c.Assert(out, checker.Contains, "Loaded image: "+name+":latest")
-	c.Assert(out, checker.Not(checker.Contains), "Loaded image ID:")
+	assert.NilError(c, err, "failed to save and load repo: %s, %v", out, err)
+
+	assert.Assert(c, strings.Contains(out, "Loaded image: "+name+":latest"))
+	assert.Assert(c, !strings.Contains(out, "Loaded image ID:"))
 }
diff --git a/integration-cli/docker_cli_save_load_unix_test.go b/integration-cli/docker_cli_save_load_unix_test.go
index da520e41c0cb6..89776b92635cc 100644
--- a/integration-cli/docker_cli_save_load_unix_test.go
+++ b/integration-cli/docker_cli_save_load_unix_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -5,21 +6,20 @@ package main
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"strings"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
+	"github.com/creack/pty"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
-	"github.com/kr/pty"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
 // save a repo and try to load it using stdout
-func (s *DockerSuite) TestSaveAndLoadRepoStdout(c *check.C) {
+func (s *DockerSuite) TestSaveAndLoadRepoStdout(c *testing.T) {
 	name := "test-save-and-load-repo-stdout"
 	dockerCmd(c, "run", "--name", name, "busybox", "true")
 
@@ -27,8 +27,8 @@ func (s *DockerSuite) TestSaveAndLoadRepoStdout(c *check.C) {
 	before, _ := dockerCmd(c, "commit", name, repoName)
 	before = strings.TrimRight(before, "\n")
 
-	tmpFile, err := ioutil.TempFile("", "foobar-save-load-test.tar")
-	c.Assert(err, check.IsNil)
+	tmpFile, err := os.CreateTemp("", "foobar-save-load-test.tar")
+	assert.NilError(c, err)
 	defer os.Remove(tmpFile.Name())
 
 	icmd.RunCmd(icmd.Cmd{
@@ -37,7 +37,7 @@ func (s *DockerSuite) TestSaveAndLoadRepoStdout(c *check.C) {
 	}).Assert(c, icmd.Success)
 
 	tmpFile, err = os.Open(tmpFile.Name())
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	defer tmpFile.Close()
 
 	deleteImages(repoName)
@@ -50,27 +50,27 @@ func (s *DockerSuite) TestSaveAndLoadRepoStdout(c *check.C) {
 	after := inspectField(c, repoName, "Id")
 	after = strings.TrimRight(after, "\n")
 
-	c.Assert(after, check.Equals, before) //inspect is not the same after a save / load
+	assert.Equal(c, after, before, "inspect is not the same after a save / load")
 
 	deleteImages(repoName)
 
 	pty, tty, err := pty.Open()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	cmd := exec.Command(dockerBinary, "save", repoName)
 	cmd.Stdin = tty
 	cmd.Stdout = tty
 	cmd.Stderr = tty
-	c.Assert(cmd.Start(), check.IsNil)
-	c.Assert(cmd.Wait(), check.NotNil) //did not break writing to a TTY
+	assert.NilError(c, cmd.Start())
+	assert.ErrorContains(c, cmd.Wait(), "", "did not break writing to a TTY")
 
 	buf := make([]byte, 1024)
 
 	n, err := pty.Read(buf)
-	c.Assert(err, check.IsNil) //could not read tty output
-	c.Assert(string(buf[:n]), checker.Contains, "cowardly refusing", check.Commentf("help output is not being yielded"))
+	assert.NilError(c, err, "could not read tty output")
+	assert.Assert(c, strings.Contains(string(buf[:n]), "cowardly refusing"), "help output is not being yielded")
 }
 
-func (s *DockerSuite) TestSaveAndLoadWithProgressBar(c *check.C) {
+func (s *DockerSuite) TestSaveAndLoadWithProgressBar(c *testing.T) {
 	name := "test-load"
 	buildImageSuccessfully(c, name, build.WithDockerfile(`FROM busybox
 	RUN touch aa
@@ -84,24 +84,24 @@ func (s *DockerSuite) TestSaveAndLoadWithProgressBar(c *check.C) {
 	dockerCmd(c, "tag", "busybox", name)
 	out, _ := dockerCmd(c, "load", "-i", tmptar)
 	expected := fmt.Sprintf("The image %s:latest already exists, renaming the old one with ID", name)
-	c.Assert(out, checker.Contains, expected)
+	assert.Assert(c, strings.Contains(out, expected))
 }
 
 // fail because load didn't receive data from stdin
-func (s *DockerSuite) TestLoadNoStdinFail(c *check.C) {
+func (s *DockerSuite) TestLoadNoStdinFail(c *testing.T) {
 	pty, tty, err := pty.Open()
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 	defer cancel()
 	cmd := exec.CommandContext(ctx, dockerBinary, "load")
 	cmd.Stdin = tty
 	cmd.Stdout = tty
 	cmd.Stderr = tty
-	c.Assert(cmd.Run(), check.NotNil) // docker-load should fail
+	assert.ErrorContains(c, cmd.Run(), "", "docker-load should fail")
 
 	buf := make([]byte, 1024)
 
 	n, err := pty.Read(buf)
-	c.Assert(err, check.IsNil) //could not read tty output
-	c.Assert(string(buf[:n]), checker.Contains, "requested load from stdin, but stdin is empty")
+	assert.NilError(c, err) //could not read tty output
+	assert.Assert(c, strings.Contains(string(buf[:n]), "requested load from stdin, but stdin is empty"))
 }
diff --git a/integration-cli/docker_cli_search_test.go b/integration-cli/docker_cli_search_test.go
index 809088a11563d..baace44fb312f 100644
--- a/integration-cli/docker_cli_search_test.go
+++ b/integration-cli/docker_cli_search_test.go
@@ -3,129 +3,78 @@ package main
 import (
 	"fmt"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
 // search for repos named  "registry" on the central registry
-func (s *DockerSuite) TestSearchOnCentralRegistry(c *check.C) {
-	testRequires(c, Network, DaemonIsLinux)
-
+func (s *DockerSuite) TestSearchOnCentralRegistry(c *testing.T) {
 	out, _ := dockerCmd(c, "search", "busybox")
-	c.Assert(out, checker.Contains, "Busybox base image.", check.Commentf("couldn't find any repository named (or containing) 'Busybox base image.'"))
+	assert.Assert(c, strings.Contains(out, "Busybox base image."), "couldn't find any repository named (or containing) 'Busybox base image.'")
 }
 
-func (s *DockerSuite) TestSearchStarsOptionWithWrongParameter(c *check.C) {
+func (s *DockerSuite) TestSearchStarsOptionWithWrongParameter(c *testing.T) {
 	out, _, err := dockerCmdWithError("search", "--filter", "stars=a", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "Invalid filter", check.Commentf("couldn't find the invalid filter warning"))
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "Invalid filter"), "couldn't find the invalid filter warning")
 
 	out, _, err = dockerCmdWithError("search", "-f", "stars=a", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "Invalid filter", check.Commentf("couldn't find the invalid filter warning"))
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "Invalid filter"), "couldn't find the invalid filter warning")
 
 	out, _, err = dockerCmdWithError("search", "-f", "is-automated=a", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "Invalid filter", check.Commentf("couldn't find the invalid filter warning"))
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "Invalid filter"), "couldn't find the invalid filter warning")
 
 	out, _, err = dockerCmdWithError("search", "-f", "is-official=a", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "Invalid filter", check.Commentf("couldn't find the invalid filter warning"))
-
-	// -s --stars deprecated since Docker 1.13
-	out, _, err = dockerCmdWithError("search", "--stars=a", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "invalid syntax", check.Commentf("couldn't find the invalid value warning"))
-
-	// -s --stars deprecated since Docker 1.13
-	out, _, err = dockerCmdWithError("search", "-s=-1", "busybox")
-	c.Assert(err, check.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "invalid syntax", check.Commentf("couldn't find the invalid value warning"))
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "Invalid filter"), "couldn't find the invalid filter warning")
 }
 
-func (s *DockerSuite) TestSearchCmdOptions(c *check.C) {
-	testRequires(c, Network, DaemonIsLinux)
-
-	out, _ := dockerCmd(c, "search", "--help")
-	c.Assert(out, checker.Contains, "Usage:\tdocker search [OPTIONS] TERM")
-
+func (s *DockerSuite) TestSearchCmdOptions(c *testing.T) {
 	outSearchCmd, _ := dockerCmd(c, "search", "busybox")
-	outSearchCmdNotrunc, _ := dockerCmd(c, "search", "--no-trunc=true", "busybox")
+	assert.Assert(c, strings.Count(outSearchCmd, "\n") > 3, outSearchCmd)
 
-	c.Assert(len(outSearchCmd) > len(outSearchCmdNotrunc), check.Equals, false, check.Commentf("The no-trunc option can't take effect."))
-
-	outSearchCmdautomated, _ := dockerCmd(c, "search", "--filter", "is-automated=true", "busybox") //The busybox is a busybox base image, not an AUTOMATED image.
+	outSearchCmdautomated, _ := dockerCmd(c, "search", "--filter", "is-automated=true", "busybox") // The busybox is a busybox base image, not an AUTOMATED image.
 	outSearchCmdautomatedSlice := strings.Split(outSearchCmdautomated, "\n")
 	for i := range outSearchCmdautomatedSlice {
-		c.Assert(strings.HasPrefix(outSearchCmdautomatedSlice[i], "busybox "), check.Equals, false, check.Commentf("The busybox is not an AUTOMATED image: %s", outSearchCmdautomated))
+		assert.Assert(c, !strings.HasPrefix(outSearchCmdautomatedSlice[i], "busybox "), "The busybox is not an AUTOMATED image: %s", outSearchCmdautomated)
 	}
 
-	outSearchCmdNotOfficial, _ := dockerCmd(c, "search", "--filter", "is-official=false", "busybox") //The busybox is a busybox base image, official image.
+	outSearchCmdNotOfficial, _ := dockerCmd(c, "search", "--filter", "is-official=false", "busybox") // The busybox is a busybox base image, official image.
 	outSearchCmdNotOfficialSlice := strings.Split(outSearchCmdNotOfficial, "\n")
 	for i := range outSearchCmdNotOfficialSlice {
-		c.Assert(strings.HasPrefix(outSearchCmdNotOfficialSlice[i], "busybox "), check.Equals, false, check.Commentf("The busybox is not an OFFICIAL image: %s", outSearchCmdNotOfficial))
+		assert.Assert(c, !strings.HasPrefix(outSearchCmdNotOfficialSlice[i], "busybox "), "The busybox is not an OFFICIAL image: %s", outSearchCmdNotOfficial)
 	}
 
-	outSearchCmdOfficial, _ := dockerCmd(c, "search", "--filter", "is-official=true", "busybox") //The busybox is a busybox base image, official image.
+	outSearchCmdOfficial, _ := dockerCmd(c, "search", "--filter", "is-official=true", "busybox") // The busybox is a busybox base image, official image.
 	outSearchCmdOfficialSlice := strings.Split(outSearchCmdOfficial, "\n")
-	c.Assert(outSearchCmdOfficialSlice, checker.HasLen, 3) // 1 header, 1 line, 1 carriage return
-	c.Assert(strings.HasPrefix(outSearchCmdOfficialSlice[1], "busybox "), check.Equals, true, check.Commentf("The busybox is an OFFICIAL image: %s", outSearchCmdNotOfficial))
+	assert.Equal(c, len(outSearchCmdOfficialSlice), 3) // 1 header, 1 line, 1 carriage return
+	assert.Assert(c, strings.HasPrefix(outSearchCmdOfficialSlice[1], "busybox "), "The busybox is an OFFICIAL image: %s", outSearchCmdOfficial)
 
-	outSearchCmdStars, _ := dockerCmd(c, "search", "--filter", "stars=2", "busybox")
-	c.Assert(strings.Count(outSearchCmdStars, "[OK]") > strings.Count(outSearchCmd, "[OK]"), check.Equals, false, check.Commentf("The quantity of images with stars should be less than that of all images: %s", outSearchCmdStars))
+	outSearchCmdStars, _ := dockerCmd(c, "search", "--filter", "stars=10", "busybox")
+	assert.Assert(c, strings.Count(outSearchCmdStars, "\n") <= strings.Count(outSearchCmd, "\n"), "Number of images with 10+ stars should be less than that of all images:\noutSearchCmdStars: %s\noutSearch: %s\n", outSearchCmdStars, outSearchCmd)
 
 	dockerCmd(c, "search", "--filter", "is-automated=true", "--filter", "stars=2", "--no-trunc=true", "busybox")
-
-	// --automated deprecated since Docker 1.13
-	outSearchCmdautomated1, _ := dockerCmd(c, "search", "--automated=true", "busybox") //The busybox is a busybox base image, not an AUTOMATED image.
-	outSearchCmdautomatedSlice1 := strings.Split(outSearchCmdautomated1, "\n")
-	for i := range outSearchCmdautomatedSlice1 {
-		c.Assert(strings.HasPrefix(outSearchCmdautomatedSlice1[i], "busybox "), check.Equals, false, check.Commentf("The busybox is not an AUTOMATED image: %s", outSearchCmdautomated))
-	}
-
-	// -s --stars deprecated since Docker 1.13
-	outSearchCmdStars1, _ := dockerCmd(c, "search", "--stars=2", "busybox")
-	c.Assert(strings.Count(outSearchCmdStars1, "[OK]") > strings.Count(outSearchCmd, "[OK]"), check.Equals, false, check.Commentf("The quantity of images with stars should be less than that of all images: %s", outSearchCmdStars1))
-
-	// -s --stars deprecated since Docker 1.13
-	dockerCmd(c, "search", "--stars=2", "--automated=true", "--no-trunc=true", "busybox")
 }
 
 // search for repos which start with "ubuntu-" on the central registry
-func (s *DockerSuite) TestSearchOnCentralRegistryWithDash(c *check.C) {
-	testRequires(c, Network, DaemonIsLinux)
-
+func (s *DockerSuite) TestSearchOnCentralRegistryWithDash(c *testing.T) {
 	dockerCmd(c, "search", "ubuntu-")
 }
 
 // test case for #23055
-func (s *DockerSuite) TestSearchWithLimit(c *check.C) {
-	testRequires(c, Network, DaemonIsLinux)
-
-	limit := 10
-	out, _, err := dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
-	c.Assert(err, checker.IsNil)
-	outSlice := strings.Split(out, "\n")
-	c.Assert(outSlice, checker.HasLen, limit+2) // 1 header, 1 carriage return
-
-	limit = 50
-	out, _, err = dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
-	c.Assert(err, checker.IsNil)
-	outSlice = strings.Split(out, "\n")
-	c.Assert(outSlice, checker.HasLen, limit+2) // 1 header, 1 carriage return
-
-	limit = 100
-	out, _, err = dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
-	c.Assert(err, checker.IsNil)
-	outSlice = strings.Split(out, "\n")
-	c.Assert(outSlice, checker.HasLen, limit+2) // 1 header, 1 carriage return
-
-	limit = 0
-	_, _, err = dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
-	c.Assert(err, checker.Not(checker.IsNil))
-
-	limit = 200
-	_, _, err = dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
-	c.Assert(err, checker.Not(checker.IsNil))
+func (s *DockerSuite) TestSearchWithLimit(c *testing.T) {
+	for _, limit := range []int{10, 50, 100} {
+		out, _, err := dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
+		assert.NilError(c, err)
+		outSlice := strings.Split(out, "\n")
+		assert.Equal(c, len(outSlice), limit+2) // 1 header, 1 carriage return
+	}
+
+	for _, limit := range []int{-1, 0, 101} {
+		_, _, err := dockerCmdWithError("search", fmt.Sprintf("--limit=%d", limit), "docker")
+		assert.ErrorContains(c, err, "")
+	}
 }
diff --git a/integration-cli/docker_cli_service_create_test.go b/integration-cli/docker_cli_service_create_test.go
index d00e15a0e3484..6d4dc3a375f8b 100644
--- a/integration-cli/docker_cli_service_create_test.go
+++ b/integration-cli/docker_cli_service_create_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -7,63 +8,65 @@ import (
 	"fmt"
 	"path/filepath"
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
 )
 
-func (s *DockerSwarmSuite) TestServiceCreateMountVolume(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceCreateMountVolume(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--mount", "type=volume,source=foo,target=/foo,volume-nocopy", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	var tasks []swarm.Task
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		tasks = d.GetServiceTasks(c, id)
-		return len(tasks) > 0, nil
-	}, checker.Equals, true)
+		return len(tasks) > 0, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	task := tasks[0]
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		if task.NodeID == "" || task.Status.ContainerStatus == nil {
 			task = d.GetTask(c, task.ID)
 		}
-		return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
-	}, checker.Equals, true)
+		return task.NodeID != "" && task.Status.ContainerStatus != nil, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// check container mount config
 	out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .HostConfig.Mounts}}", task.Status.ContainerStatus.ContainerID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	var mountConfig []mount.Mount
-	c.Assert(json.Unmarshal([]byte(out), &mountConfig), checker.IsNil)
-	c.Assert(mountConfig, checker.HasLen, 1)
+	assert.Assert(c, json.Unmarshal([]byte(out), &mountConfig) == nil)
+	assert.Equal(c, len(mountConfig), 1)
 
-	c.Assert(mountConfig[0].Source, checker.Equals, "foo")
-	c.Assert(mountConfig[0].Target, checker.Equals, "/foo")
-	c.Assert(mountConfig[0].Type, checker.Equals, mount.TypeVolume)
-	c.Assert(mountConfig[0].VolumeOptions, checker.NotNil)
-	c.Assert(mountConfig[0].VolumeOptions.NoCopy, checker.True)
+	assert.Equal(c, mountConfig[0].Source, "foo")
+	assert.Equal(c, mountConfig[0].Target, "/foo")
+	assert.Equal(c, mountConfig[0].Type, mount.TypeVolume)
+	assert.Assert(c, mountConfig[0].VolumeOptions != nil)
+	assert.Assert(c, mountConfig[0].VolumeOptions.NoCopy)
 
 	// check container mounts actual
 	out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .Mounts}}", task.Status.ContainerStatus.ContainerID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	var mounts []types.MountPoint
-	c.Assert(json.Unmarshal([]byte(out), &mounts), checker.IsNil)
-	c.Assert(mounts, checker.HasLen, 1)
+	assert.Assert(c, json.Unmarshal([]byte(out), &mounts) == nil)
+	assert.Equal(c, len(mounts), 1)
 
-	c.Assert(mounts[0].Type, checker.Equals, mount.TypeVolume)
-	c.Assert(mounts[0].Name, checker.Equals, "foo")
-	c.Assert(mounts[0].Destination, checker.Equals, "/foo")
-	c.Assert(mounts[0].RW, checker.Equals, true)
+	assert.Equal(c, mounts[0].Type, mount.TypeVolume)
+	assert.Equal(c, mounts[0].Name, "foo")
+	assert.Equal(c, mounts[0].Destination, "/foo")
+	assert.Equal(c, mounts[0].RW, true)
 }
 
-func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	serviceName := "test-service-secret"
@@ -74,30 +77,30 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSimple(c *check.C) {
 		},
 		Data: []byte("TESTINGDATA"),
 	})
-	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
+	assert.Assert(c, id != "", "secrets: %s", id)
 
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--secret", testName, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var refs []swarm.SecretReference
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
-	c.Assert(refs, checker.HasLen, 1)
+	assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil)
+	assert.Equal(c, len(refs), 1)
 
-	c.Assert(refs[0].SecretName, checker.Equals, testName)
-	c.Assert(refs[0].File, checker.Not(checker.IsNil))
-	c.Assert(refs[0].File.Name, checker.Equals, testName)
-	c.Assert(refs[0].File.UID, checker.Equals, "0")
-	c.Assert(refs[0].File.GID, checker.Equals, "0")
+	assert.Equal(c, refs[0].SecretName, testName)
+	assert.Assert(c, refs[0].File != nil)
+	assert.Equal(c, refs[0].File.Name, testName)
+	assert.Equal(c, refs[0].File.UID, "0")
+	assert.Equal(c, refs[0].File.GID, "0")
 
 	out, err = d.Cmd("service", "rm", serviceName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	d.DeleteSecret(c, testName)
 }
 
-func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	testPaths := map[string]string{
@@ -116,7 +119,7 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *check
 			},
 			Data: []byte("TESTINGDATA " + testName + " " + testTarget),
 		})
-		c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
+		assert.Assert(c, id != "", "secrets: %s", id)
 
 		secretFlags = append(secretFlags, "--secret", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
 	}
@@ -126,28 +129,28 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *check
 	serviceCmd = append(serviceCmd, secretFlags...)
 	serviceCmd = append(serviceCmd, "busybox", "top")
 	out, err := d.Cmd(serviceCmd...)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var refs []swarm.SecretReference
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
-	c.Assert(refs, checker.HasLen, len(testPaths))
+	assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil)
+	assert.Equal(c, len(refs), len(testPaths))
 
 	var tasks []swarm.Task
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		tasks = d.GetServiceTasks(c, serviceName)
-		return len(tasks) > 0, nil
-	}, checker.Equals, true)
+		return len(tasks) > 0, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	task := tasks[0]
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		if task.NodeID == "" || task.Status.ContainerStatus == nil {
 			task = d.GetTask(c, task.ID)
 		}
-		return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
-	}, checker.Equals, true)
+		return task.NodeID != "" && task.Status.ContainerStatus != nil, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	for testName, testTarget := range testPaths {
 		path := testTarget
@@ -155,15 +158,15 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretSourceTargetPaths(c *check
 			path = filepath.Join("/run/secrets", path)
 		}
 		out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
-		c.Assert(err, checker.IsNil)
-		c.Assert(out, checker.Equals, "TESTINGDATA "+testName+" "+testTarget)
+		assert.NilError(c, err)
+		assert.Equal(c, out, "TESTINGDATA "+testName+" "+testTarget)
 	}
 
 	out, err = d.Cmd("service", "rm", serviceName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerSwarmSuite) TestServiceCreateWithSecretReferencedTwice(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceCreateWithSecretReferencedTwice(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	id := d.CreateSecret(c, swarm.SecretSpec{
@@ -172,46 +175,46 @@ func (s *DockerSwarmSuite) TestServiceCreateWithSecretReferencedTwice(c *check.C
 		},
 		Data: []byte("TESTINGDATA"),
 	})
-	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
+	assert.Assert(c, id != "", "secrets: %s", id)
 
 	serviceName := "svc"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--secret", "source=mysecret,target=target1", "--secret", "source=mysecret,target=target2", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var refs []swarm.SecretReference
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
-	c.Assert(refs, checker.HasLen, 2)
+	assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil)
+	assert.Equal(c, len(refs), 2)
 
 	var tasks []swarm.Task
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		tasks = d.GetServiceTasks(c, serviceName)
-		return len(tasks) > 0, nil
-	}, checker.Equals, true)
+		return len(tasks) > 0, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	task := tasks[0]
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		if task.NodeID == "" || task.Status.ContainerStatus == nil {
 			task = d.GetTask(c, task.ID)
 		}
-		return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
-	}, checker.Equals, true)
+		return task.NodeID != "" && task.Status.ContainerStatus != nil, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	for _, target := range []string{"target1", "target2"} {
-		c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+		assert.NilError(c, err, out)
 		path := filepath.Join("/run/secrets", target)
 		out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
-		c.Assert(err, checker.IsNil)
-		c.Assert(out, checker.Equals, "TESTINGDATA")
+		assert.NilError(c, err)
+		assert.Equal(c, out, "TESTINGDATA")
 	}
 
 	out, err = d.Cmd("service", "rm", serviceName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerSwarmSuite) TestServiceCreateWithConfigSimple(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceCreateWithConfigSimple(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	serviceName := "test-service-config"
@@ -222,30 +225,30 @@ func (s *DockerSwarmSuite) TestServiceCreateWithConfigSimple(c *check.C) {
 		},
 		Data: []byte("TESTINGDATA"),
 	})
-	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
+	assert.Assert(c, id != "", "configs: %s", id)
 
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--config", testName, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var refs []swarm.ConfigReference
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
-	c.Assert(refs, checker.HasLen, 1)
+	assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil)
+	assert.Equal(c, len(refs), 1)
 
-	c.Assert(refs[0].ConfigName, checker.Equals, testName)
-	c.Assert(refs[0].File, checker.Not(checker.IsNil))
-	c.Assert(refs[0].File.Name, checker.Equals, testName)
-	c.Assert(refs[0].File.UID, checker.Equals, "0")
-	c.Assert(refs[0].File.GID, checker.Equals, "0")
+	assert.Equal(c, refs[0].ConfigName, testName)
+	assert.Assert(c, refs[0].File != nil)
+	assert.Equal(c, refs[0].File.Name, testName)
+	assert.Equal(c, refs[0].File.UID, "0")
+	assert.Equal(c, refs[0].File.GID, "0")
 
 	out, err = d.Cmd("service", "rm", serviceName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	d.DeleteConfig(c, testName)
 }
 
-func (s *DockerSwarmSuite) TestServiceCreateWithConfigSourceTargetPaths(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceCreateWithConfigSourceTargetPaths(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	testPaths := map[string]string{
@@ -263,7 +266,7 @@ func (s *DockerSwarmSuite) TestServiceCreateWithConfigSourceTargetPaths(c *check
 			},
 			Data: []byte("TESTINGDATA " + testName + " " + testTarget),
 		})
-		c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
+		assert.Assert(c, id != "", "configs: %s", id)
 
 		configFlags = append(configFlags, "--config", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
 	}
@@ -273,28 +276,28 @@ func (s *DockerSwarmSuite) TestServiceCreateWithConfigSourceTargetPaths(c *check
 	serviceCmd = append(serviceCmd, configFlags...)
 	serviceCmd = append(serviceCmd, "busybox", "top")
 	out, err := d.Cmd(serviceCmd...)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var refs []swarm.ConfigReference
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
-	c.Assert(refs, checker.HasLen, len(testPaths))
+	assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil)
+	assert.Equal(c, len(refs), len(testPaths))
 
 	var tasks []swarm.Task
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		tasks = d.GetServiceTasks(c, serviceName)
-		return len(tasks) > 0, nil
-	}, checker.Equals, true)
+		return len(tasks) > 0, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	task := tasks[0]
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		if task.NodeID == "" || task.Status.ContainerStatus == nil {
 			task = d.GetTask(c, task.ID)
 		}
-		return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
-	}, checker.Equals, true)
+		return task.NodeID != "" && task.Status.ContainerStatus != nil, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	for testName, testTarget := range testPaths {
 		path := testTarget
@@ -302,15 +305,15 @@ func (s *DockerSwarmSuite) TestServiceCreateWithConfigSourceTargetPaths(c *check
 			path = filepath.Join("/", path)
 		}
 		out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
-		c.Assert(err, checker.IsNil)
-		c.Assert(out, checker.Equals, "TESTINGDATA "+testName+" "+testTarget)
+		assert.NilError(c, err)
+		assert.Equal(c, out, "TESTINGDATA "+testName+" "+testTarget)
 	}
 
 	out, err = d.Cmd("service", "rm", serviceName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerSwarmSuite) TestServiceCreateWithConfigReferencedTwice(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceCreateWithConfigReferencedTwice(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	id := d.CreateConfig(c, swarm.ConfigSpec{
@@ -319,129 +322,129 @@ func (s *DockerSwarmSuite) TestServiceCreateWithConfigReferencedTwice(c *check.C
 		},
 		Data: []byte("TESTINGDATA"),
 	})
-	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
+	assert.Assert(c, id != "", "configs: %s", id)
 
 	serviceName := "svc"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--config", "source=myconfig,target=target1", "--config", "source=myconfig,target=target2", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var refs []swarm.ConfigReference
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
-	c.Assert(refs, checker.HasLen, 2)
+	assert.Assert(c, json.Unmarshal([]byte(out), &refs) == nil)
+	assert.Equal(c, len(refs), 2)
 
 	var tasks []swarm.Task
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		tasks = d.GetServiceTasks(c, serviceName)
-		return len(tasks) > 0, nil
-	}, checker.Equals, true)
+		return len(tasks) > 0, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	task := tasks[0]
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		if task.NodeID == "" || task.Status.ContainerStatus == nil {
 			task = d.GetTask(c, task.ID)
 		}
-		return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
-	}, checker.Equals, true)
+		return task.NodeID != "" && task.Status.ContainerStatus != nil, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	for _, target := range []string{"target1", "target2"} {
-		c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+		assert.NilError(c, err, out)
 		path := filepath.Join("/", target)
 		out, err := d.Cmd("exec", task.Status.ContainerStatus.ContainerID, "cat", path)
-		c.Assert(err, checker.IsNil)
-		c.Assert(out, checker.Equals, "TESTINGDATA")
+		assert.NilError(c, err)
+		assert.Equal(c, out, "TESTINGDATA")
 	}
 
 	out, err = d.Cmd("service", "rm", serviceName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerSwarmSuite) TestServiceCreateMountTmpfs(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceCreateMountTmpfs(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
-	out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--mount", "type=tmpfs,target=/foo,tmpfs-size=1MB", "busybox", "sh", "-c", "mount | grep foo; tail -f /dev/null")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--mount", "type=tmpfs,target=/foo,tmpfs-size=1MB", "busybox", "sh", "-c", "mount | grep foo; exec tail -f /dev/null")
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	var tasks []swarm.Task
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		tasks = d.GetServiceTasks(c, id)
-		return len(tasks) > 0, nil
-	}, checker.Equals, true)
+		return len(tasks) > 0, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	task := tasks[0]
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		if task.NodeID == "" || task.Status.ContainerStatus == nil {
 			task = d.GetTask(c, task.ID)
 		}
-		return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
-	}, checker.Equals, true)
+		return task.NodeID != "" && task.Status.ContainerStatus != nil, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// check container mount config
 	out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .HostConfig.Mounts}}", task.Status.ContainerStatus.ContainerID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	var mountConfig []mount.Mount
-	c.Assert(json.Unmarshal([]byte(out), &mountConfig), checker.IsNil)
-	c.Assert(mountConfig, checker.HasLen, 1)
+	assert.Assert(c, json.Unmarshal([]byte(out), &mountConfig) == nil)
+	assert.Equal(c, len(mountConfig), 1)
 
-	c.Assert(mountConfig[0].Source, checker.Equals, "")
-	c.Assert(mountConfig[0].Target, checker.Equals, "/foo")
-	c.Assert(mountConfig[0].Type, checker.Equals, mount.TypeTmpfs)
-	c.Assert(mountConfig[0].TmpfsOptions, checker.NotNil)
-	c.Assert(mountConfig[0].TmpfsOptions.SizeBytes, checker.Equals, int64(1048576))
+	assert.Equal(c, mountConfig[0].Source, "")
+	assert.Equal(c, mountConfig[0].Target, "/foo")
+	assert.Equal(c, mountConfig[0].Type, mount.TypeTmpfs)
+	assert.Assert(c, mountConfig[0].TmpfsOptions != nil)
+	assert.Equal(c, mountConfig[0].TmpfsOptions.SizeBytes, int64(1048576))
 
 	// check container mounts actual
 	out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .Mounts}}", task.Status.ContainerStatus.ContainerID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	var mounts []types.MountPoint
-	c.Assert(json.Unmarshal([]byte(out), &mounts), checker.IsNil)
-	c.Assert(mounts, checker.HasLen, 1)
+	assert.Assert(c, json.Unmarshal([]byte(out), &mounts) == nil)
+	assert.Equal(c, len(mounts), 1)
 
-	c.Assert(mounts[0].Type, checker.Equals, mount.TypeTmpfs)
-	c.Assert(mounts[0].Name, checker.Equals, "")
-	c.Assert(mounts[0].Destination, checker.Equals, "/foo")
-	c.Assert(mounts[0].RW, checker.Equals, true)
+	assert.Equal(c, mounts[0].Type, mount.TypeTmpfs)
+	assert.Equal(c, mounts[0].Name, "")
+	assert.Equal(c, mounts[0].Destination, "/foo")
+	assert.Equal(c, mounts[0].RW, true)
 
 	out, err = s.nodeCmd(c, task.NodeID, "logs", task.Status.ContainerStatus.ContainerID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.HasPrefix, "tmpfs on /foo type tmpfs")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "size=1024k")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.HasPrefix(strings.TrimSpace(out), "tmpfs on /foo type tmpfs"))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "size=1024k"))
 }
 
-func (s *DockerSwarmSuite) TestServiceCreateWithNetworkAlias(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceCreateWithNetworkAlias(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 	out, err := d.Cmd("network", "create", "--scope=swarm", "test_swarm_br")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--network=name=test_swarm_br,alias=srv_alias", "--name=alias_tst_container", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	var tasks []swarm.Task
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		tasks = d.GetServiceTasks(c, id)
-		return len(tasks) > 0, nil
-	}, checker.Equals, true)
+		return len(tasks) > 0, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	task := tasks[0]
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		if task.NodeID == "" || task.Status.ContainerStatus == nil {
 			task = d.GetTask(c, task.ID)
 		}
-		return task.NodeID != "" && task.Status.ContainerStatus != nil, nil
-	}, checker.Equals, true)
+		return task.NodeID != "" && task.Status.ContainerStatus != nil, ""
+	}, checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// check container alias config
 	out, err = s.nodeCmd(c, task.NodeID, "inspect", "--format", "{{json .NetworkSettings.Networks.test_swarm_br.Aliases}}", task.Status.ContainerStatus.ContainerID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Make sure the only alias seen is the container-id
 	var aliases []string
-	c.Assert(json.Unmarshal([]byte(out), &aliases), checker.IsNil)
-	c.Assert(aliases, checker.HasLen, 1)
+	assert.Assert(c, json.Unmarshal([]byte(out), &aliases) == nil)
+	assert.Equal(c, len(aliases), 1)
 
-	c.Assert(task.Status.ContainerStatus.ContainerID, checker.Contains, aliases[0])
+	assert.Assert(c, strings.Contains(task.Status.ContainerStatus.ContainerID, aliases[0]))
 }
diff --git a/integration-cli/docker_cli_service_health_test.go b/integration-cli/docker_cli_service_health_test.go
index 994ee77b9951c..0caab32ea50b4 100644
--- a/integration-cli/docker_cli_service_health_test.go
+++ b/integration-cli/docker_cli_service_health_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -5,19 +6,21 @@ package main
 import (
 	"strconv"
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/daemon/cluster/executor/container"
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/poll"
 )
 
 // start a service, and then make its task unhealthy during running
 // finally, unhealthy task should be detected and killed
-func (s *DockerSwarmSuite) TestServiceHealthRun(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceHealthRun(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows
 
 	d := s.AddDaemon(c, true, true)
@@ -27,50 +30,51 @@ func (s *DockerSwarmSuite) TestServiceHealthRun(c *check.C) {
 	result := cli.BuildCmd(c, imageName, cli.Daemon(d),
 		build.WithDockerfile(`FROM busybox
 		RUN touch /status
-		HEALTHCHECK --interval=1s --timeout=1s --retries=1\
+		HEALTHCHECK --interval=1s --timeout=5s --retries=1\
 		  CMD cat /status`),
 	)
 	result.Assert(c, icmd.Success)
 
 	serviceName := "healthServiceRun"
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--name", serviceName, imageName, "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	var tasks []swarm.Task
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		tasks = d.GetServiceTasks(c, id)
-		return tasks, nil
-	}, checker.HasLen, 1)
+		return tasks, ""
+	}, checker.HasLen(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	task := tasks[0]
 
 	// wait for task to start
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		task = d.GetTask(c, task.ID)
-		return task.Status.State, nil
-	}, checker.Equals, swarm.TaskStateRunning)
+		return task.Status.State, ""
+	}, checker.Equals(swarm.TaskStateRunning)), poll.WithTimeout(defaultReconciliationTimeout))
+
 	containerID := task.Status.ContainerStatus.ContainerID
 
 	// wait for container to be healthy
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		out, _ := d.Cmd("inspect", "--format={{.State.Health.Status}}", containerID)
-		return strings.TrimSpace(out), nil
-	}, checker.Equals, "healthy")
+		return strings.TrimSpace(out), ""
+	}, checker.Equals("healthy")), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// make it fail
 	d.Cmd("exec", containerID, "rm", "/status")
 	// wait for container to be unhealthy
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		out, _ := d.Cmd("inspect", "--format={{.State.Health.Status}}", containerID)
-		return strings.TrimSpace(out), nil
-	}, checker.Equals, "unhealthy")
+		return strings.TrimSpace(out), ""
+	}, checker.Equals("unhealthy")), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// Task should be terminated
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		task = d.GetTask(c, task.ID)
-		return task.Status.State, nil
-	}, checker.Equals, swarm.TaskStateFailed)
+		return task.Status.State, ""
+	}, checker.Equals(swarm.TaskStateFailed)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	if !strings.Contains(task.Status.Err, container.ErrContainerUnhealthy.Error()) {
 		c.Fatal("unhealthy task exits because of other error")
@@ -79,7 +83,7 @@ func (s *DockerSwarmSuite) TestServiceHealthRun(c *check.C) {
 
 // start a service whose task is unhealthy at beginning
 // its tasks should be blocked in starting stage, until health check is passed
-func (s *DockerSwarmSuite) TestServiceHealthStart(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceHealthStart(c *testing.T) {
 	testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows
 
 	d := s.AddDaemon(c, true, true)
@@ -95,42 +99,43 @@ func (s *DockerSwarmSuite) TestServiceHealthStart(c *check.C) {
 
 	serviceName := "healthServiceStart"
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--name", serviceName, imageName, "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	var tasks []swarm.Task
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		tasks = d.GetServiceTasks(c, id)
-		return tasks, nil
-	}, checker.HasLen, 1)
+		return tasks, ""
+	}, checker.HasLen(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	task := tasks[0]
 
 	// wait for task to start
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		task = d.GetTask(c, task.ID)
-		return task.Status.State, nil
-	}, checker.Equals, swarm.TaskStateStarting)
+		return task.Status.State, ""
+	}, checker.Equals(swarm.TaskStateStarting)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	containerID := task.Status.ContainerStatus.ContainerID
 
 	// wait for health check to work
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		out, _ := d.Cmd("inspect", "--format={{.State.Health.FailingStreak}}", containerID)
 		failingStreak, _ := strconv.Atoi(strings.TrimSpace(out))
-		return failingStreak, nil
-	}, checker.GreaterThan, 0)
+		return failingStreak, ""
+	}, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// task should be blocked at starting status
 	task = d.GetTask(c, task.ID)
-	c.Assert(task.Status.State, check.Equals, swarm.TaskStateStarting)
+	assert.Equal(c, task.Status.State, swarm.TaskStateStarting)
 
 	// make it healthy
 	d.Cmd("exec", containerID, "touch", "/status")
 
 	// Task should be at running status
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
 		task = d.GetTask(c, task.ID)
-		return task.Status.State, nil
-	}, checker.Equals, swarm.TaskStateRunning)
+		return task.Status.State, ""
+	}, checker.Equals(swarm.TaskStateRunning)), poll.WithTimeout(defaultReconciliationTimeout))
+
 }
diff --git a/integration-cli/docker_cli_service_logs_test.go b/integration-cli/docker_cli_service_logs_test.go
index ba337491b1440..7727e1fe2a4ed 100644
--- a/integration-cli/docker_cli_service_logs_test.go
+++ b/integration-cli/docker_cli_service_logs_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -8,12 +9,14 @@ import (
 	"io"
 	"os/exec"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/daemon"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/poll"
 )
 
 type logMessage struct {
@@ -21,7 +24,7 @@ type logMessage struct {
 	data []byte
 }
 
-func (s *DockerSwarmSuite) TestServiceLogs(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceLogs(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// we have multiple services here for detecting the goroutine issue #28915
@@ -32,151 +35,151 @@ func (s *DockerSwarmSuite) TestServiceLogs(c *check.C) {
 
 	for name, message := range services {
 		out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox",
-			"sh", "-c", fmt.Sprintf("echo %s; tail -f /dev/null", message))
-		c.Assert(err, checker.IsNil)
-		c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+			"sh", "-c", fmt.Sprintf("echo %s; exec tail -f /dev/null", message))
+		assert.NilError(c, err)
+		assert.Assert(c, strings.TrimSpace(out) != "")
 	}
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout,
-		d.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{"busybox:latest": len(services)})
+	poll.WaitOn(c, pollCheck(c,
+		d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{"busybox:latest": len(services)})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	for name, message := range services {
 		out, err := d.Cmd("service", "logs", name)
-		c.Assert(err, checker.IsNil)
-		c.Logf("log for %q: %q", name, out)
-		c.Assert(out, checker.Contains, message)
+		assert.NilError(c, err)
+		assert.Assert(c, strings.Contains(out, message), "log for %q: %q", name, out)
 	}
 }
 
-// countLogLines returns a closure that can be used with waitAndAssert to
+// countLogLines returns a closure that can be used with poll.WaitOn() to
 // verify that a minimum number of expected container log messages have been
 // output.
-func countLogLines(d *daemon.Daemon, name string) func(*check.C) (interface{}, check.CommentInterface) {
-	return func(c *check.C) (interface{}, check.CommentInterface) {
+func countLogLines(d *daemon.Daemon, name string) func(*testing.T) (interface{}, string) {
+	return func(c *testing.T) (interface{}, string) {
 		result := icmd.RunCmd(d.Command("service", "logs", "-t", "--raw", name))
 		result.Assert(c, icmd.Expected{})
 		// if this returns an emptystring, trying to split it later will return
 		// an array containing emptystring. a valid log line will NEVER be
 		// emptystring because we ask for the timestamp.
 		if result.Stdout() == "" {
-			return 0, check.Commentf("Empty stdout")
+			return 0, "Empty stdout"
 		}
 		lines := strings.Split(strings.TrimSpace(result.Stdout()), "\n")
-		return len(lines), check.Commentf("output, %q", string(result.Stdout()))
+		return len(lines), fmt.Sprintf("output, %q", result.Stdout())
 	}
 }
 
-func (s *DockerSwarmSuite) TestServiceLogsCompleteness(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceLogsCompleteness(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "TestServiceLogsCompleteness"
 
 	// make a service that prints 6 lines
-	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "for line in $(seq 0 5); do echo log test $line; done; sleep 100000")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "for line in $(seq 0 5); do echo log test $line; done; exec tail /dev/null")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 	// and make sure we have all the log lines
-	waitAndAssert(c, defaultReconciliationTimeout, countLogLines(d, name), checker.Equals, 6)
+	poll.WaitOn(c, pollCheck(c, countLogLines(d, name), checker.Equals(6)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("service", "logs", name)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	lines := strings.Split(strings.TrimSpace(out), "\n")
 
 	// i have heard anecdotal reports that logs may come back from the engine
 	// mis-ordered. if this tests fails, consider the possibility that that
 	// might be occurring
 	for i, line := range lines {
-		c.Assert(line, checker.Contains, fmt.Sprintf("log test %v", i))
+		assert.Assert(c, strings.Contains(line, fmt.Sprintf("log test %v", i)))
 	}
 }
 
-func (s *DockerSwarmSuite) TestServiceLogsTail(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceLogsTail(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "TestServiceLogsTail"
 
 	// make a service that prints 6 lines
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "for line in $(seq 1 6); do echo log test $line; done; sleep 100000")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
-	waitAndAssert(c, defaultReconciliationTimeout, countLogLines(d, name), checker.Equals, 6)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, countLogLines(d, name), checker.Equals(6)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("service", "logs", "--tail=2", name)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	lines := strings.Split(strings.TrimSpace(out), "\n")
 
 	for i, line := range lines {
 		// doing i+5 is hacky but not too fragile, it's good enough. if it flakes something else is wrong
-		c.Assert(line, checker.Contains, fmt.Sprintf("log test %v", i+5))
+		assert.Assert(c, strings.Contains(line, fmt.Sprintf("log test %v", i+5)))
 	}
 }
 
-func (s *DockerSwarmSuite) TestServiceLogsSince(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceLogsSince(c *testing.T) {
 	// See DockerSuite.TestLogsSince, which is where this comes from
 	d := s.AddDaemon(c, true, true)
 
 	name := "TestServiceLogsSince"
 
-	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "for i in $(seq 1 3); do sleep .1; echo log$i; done; sleep 10000000")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "for i in $(seq 1 3); do usleep 100000; echo log$i; done; exec tail /dev/null")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.TrimSpace(out) != "")
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 	// wait a sec for the logs to come in
-	waitAndAssert(c, defaultReconciliationTimeout, countLogLines(d, name), checker.Equals, 3)
+	poll.WaitOn(c, pollCheck(c, countLogLines(d, name), checker.Equals(3)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("service", "logs", "-t", name)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	log2Line := strings.Split(strings.Split(out, "\n")[1], " ")
 	t, err := time.Parse(time.RFC3339Nano, log2Line[0]) // timestamp log2 is written
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	u := t.Add(50 * time.Millisecond) // add .05s so log1 & log2 don't show up
 	since := u.Format(time.RFC3339Nano)
 
 	out, err = d.Cmd("service", "logs", "-t", fmt.Sprintf("--since=%v", since), name)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	unexpected := []string{"log1", "log2"}
 	expected := []string{"log3"}
 	for _, v := range unexpected {
-		c.Assert(out, checker.Not(checker.Contains), v, check.Commentf("unexpected log message returned, since=%v", u))
+		assert.Assert(c, !strings.Contains(out, v), "unexpected log message returned, since=%v", u)
 	}
 	for _, v := range expected {
-		c.Assert(out, checker.Contains, v, check.Commentf("expected log message %v, was not present, since=%v", u))
+		assert.Assert(c, strings.Contains(out, v), "expected log message %v, was not present, since=%v", u)
 	}
 }
 
-func (s *DockerSwarmSuite) TestServiceLogsFollow(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceLogsFollow(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "TestServiceLogsFollow"
 
-	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "while true; do echo log test; sleep 0.1; done")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", "trap 'exit 0' TERM; while true; do echo log test; usleep 100000; done")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	args := []string{"service", "logs", "-f", name}
 	cmd := exec.Command(dockerBinary, d.PrependHostArg(args)...)
 	r, w := io.Pipe()
 	cmd.Stdout = w
 	cmd.Stderr = w
-	c.Assert(cmd.Start(), checker.IsNil)
+	assert.NilError(c, cmd.Start())
 	go cmd.Wait()
 
 	// Make sure pipe is written to
 	ch := make(chan *logMessage)
 	done := make(chan struct{})
+	stop := make(chan struct{})
+	defer close(stop)
 	go func() {
 		reader := bufio.NewReader(r)
 		for {
@@ -184,6 +187,8 @@ func (s *DockerSwarmSuite) TestServiceLogsFollow(c *check.C) {
 			msg.data, _, msg.err = reader.ReadLine()
 			select {
 			case ch <- msg:
+			case <-stop:
+				return
 			case <-done:
 				return
 			}
@@ -192,15 +197,15 @@ func (s *DockerSwarmSuite) TestServiceLogsFollow(c *check.C) {
 
 	for i := 0; i < 3; i++ {
 		msg := <-ch
-		c.Assert(msg.err, checker.IsNil)
-		c.Assert(string(msg.data), checker.Contains, "log test")
+		assert.NilError(c, msg.err)
+		assert.Assert(c, strings.Contains(string(msg.data), "log test"))
 	}
 	close(done)
 
-	c.Assert(cmd.Process.Kill(), checker.IsNil)
+	assert.NilError(c, cmd.Process.Kill())
 }
 
-func (s *DockerSwarmSuite) TestServiceLogsTaskLogs(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceLogsTaskLogs(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "TestServicelogsTaskLogs"
@@ -214,28 +219,28 @@ func (s *DockerSwarmSuite) TestServiceLogsTaskLogs(c *check.C) {
 		// which has this the task id as an environment variable templated in
 		"--env", "TASK={{.Task.ID}}",
 		// and runs this command to print exactly 6 logs lines
-		"busybox", "sh", "-c", "for line in $(seq 0 5); do echo $TASK log test $line; done; sleep 100000",
+		"busybox", "sh", "-c", "trap 'exit 0' TERM; for line in $(seq 0 5); do echo $TASK log test $line; done; sleep 100000",
 	))
 	result.Assert(c, icmd.Expected{})
 	// ^^ verify that we get no error
 	// then verify that we have an id in stdout
 	id := strings.TrimSpace(result.Stdout())
-	c.Assert(id, checker.Not(checker.Equals), "")
+	assert.Assert(c, id != "")
 	// so, right here, we're basically inspecting by id and returning only
 	// the ID. if they don't match, the service doesn't exist.
 	result = icmd.RunCmd(d.Command("service", "inspect", "--format=\"{{.ID}}\"", id))
 	result.Assert(c, icmd.Expected{Out: id})
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, replicas)
-	waitAndAssert(c, defaultReconciliationTimeout, countLogLines(d, name), checker.Equals, 6*replicas)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(replicas)), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, countLogLines(d, name), checker.Equals(6*replicas)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// get the task ids
 	result = icmd.RunCmd(d.Command("service", "ps", "-q", name))
 	result.Assert(c, icmd.Expected{})
 	// make sure we have two tasks
 	taskIDs := strings.Split(strings.TrimSpace(result.Stdout()), "\n")
-	c.Assert(taskIDs, checker.HasLen, replicas)
+	assert.Equal(c, len(taskIDs), replicas)
 
 	for _, taskID := range taskIDs {
 		c.Logf("checking task %v", taskID)
@@ -246,14 +251,14 @@ func (s *DockerSwarmSuite) TestServiceLogsTaskLogs(c *check.C) {
 		c.Logf("checking messages for %v", taskID)
 		for i, line := range lines {
 			// make sure the message is in order
-			c.Assert(line, checker.Contains, fmt.Sprintf("log test %v", i))
+			assert.Assert(c, strings.Contains(line, fmt.Sprintf("log test %v", i)))
 			// make sure it contains the task id
-			c.Assert(line, checker.Contains, taskID)
+			assert.Assert(c, strings.Contains(line, taskID))
 		}
 	}
 }
 
-func (s *DockerSwarmSuite) TestServiceLogsTTY(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceLogsTTY(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "TestServiceLogsTTY"
@@ -273,16 +278,16 @@ func (s *DockerSwarmSuite) TestServiceLogsTTY(c *check.C) {
 
 	result.Assert(c, icmd.Expected{})
 	id := strings.TrimSpace(result.Stdout())
-	c.Assert(id, checker.Not(checker.Equals), "")
+	assert.Assert(c, id != "")
 	// so, right here, we're basically inspecting by id and returning only
 	// the ID. if they don't match, the service doesn't exist.
 	result = icmd.RunCmd(d.Command("service", "inspect", "--format=\"{{.ID}}\"", id))
 	result.Assert(c, icmd.Expected{Out: id})
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 	// and make sure we have all the log lines
-	waitAndAssert(c, defaultReconciliationTimeout, countLogLines(d, name), checker.Equals, 2)
+	poll.WaitOn(c, pollCheck(c, countLogLines(d, name), checker.Equals(2)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	cmd := d.Command("service", "logs", "--raw", name)
 	result = icmd.RunCmd(cmd)
@@ -291,7 +296,7 @@ func (s *DockerSwarmSuite) TestServiceLogsTTY(c *check.C) {
 	result.Assert(c, icmd.Expected{Out: "out\r\nerr\r\n"})
 }
 
-func (s *DockerSwarmSuite) TestServiceLogsNoHangDeletedContainer(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceLogsNoHangDeletedContainer(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "TestServiceLogsNoHangDeletedContainer"
@@ -311,20 +316,18 @@ func (s *DockerSwarmSuite) TestServiceLogsNoHangDeletedContainer(c *check.C) {
 	result.Assert(c, icmd.Expected{})
 	// get the service id
 	id := strings.TrimSpace(result.Stdout())
-	c.Assert(id, checker.Not(checker.Equals), "")
+	assert.Assert(c, id != "")
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 	// and make sure we have all the log lines
-	waitAndAssert(c, defaultReconciliationTimeout, countLogLines(d, name), checker.Equals, 2)
+	poll.WaitOn(c, pollCheck(c, countLogLines(d, name), checker.Equals(2)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// now find and nuke the container
 	result = icmd.RunCmd(d.Command("ps", "-q"))
 	containerID := strings.TrimSpace(result.Stdout())
-	c.Assert(containerID, checker.Not(checker.Equals), "")
-	result = icmd.RunCmd(d.Command("stop", containerID))
-	result.Assert(c, icmd.Expected{Out: containerID})
-	result = icmd.RunCmd(d.Command("rm", containerID))
+	assert.Assert(c, containerID != "")
+	result = icmd.RunCmd(d.Command("rm", "-f", containerID))
 	result.Assert(c, icmd.Expected{Out: containerID})
 
 	// run logs. use tail 2 to make sure we don't try to get a bunch of logs
@@ -340,7 +343,7 @@ func (s *DockerSwarmSuite) TestServiceLogsNoHangDeletedContainer(c *check.C) {
 	result.Assert(c, icmd.Expected{})
 }
 
-func (s *DockerSwarmSuite) TestServiceLogsDetails(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceLogsDetails(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "TestServiceLogsDetails"
@@ -359,17 +362,17 @@ func (s *DockerSwarmSuite) TestServiceLogsDetails(c *check.C) {
 		// busybox image, shell string
 		"busybox", "sh", "-c",
 		// make a log line
-		"echo LogLine; while true; do sleep 1; done;",
+		"trap 'exit 0' TERM; echo LogLine; while true; do sleep 1; done;",
 	))
 
 	result.Assert(c, icmd.Expected{})
 	id := strings.TrimSpace(result.Stdout())
-	c.Assert(id, checker.Not(checker.Equals), "")
+	assert.Assert(c, id != "")
 
 	// make sure task has been deployed
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 	// and make sure we have all the log lines
-	waitAndAssert(c, defaultReconciliationTimeout, countLogLines(d, name), checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, countLogLines(d, name), checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// First, test without pretty printing
 	// call service logs with details. set raw to skip pretty printing
diff --git a/integration-cli/docker_cli_service_scale_test.go b/integration-cli/docker_cli_service_scale_test.go
index ad045ee570bd9..40afcc2a8ffab 100644
--- a/integration-cli/docker_cli_service_scale_test.go
+++ b/integration-cli/docker_cli_service_scale_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -5,33 +6,33 @@ package main
 import (
 	"fmt"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSwarmSuite) TestServiceScale(c *check.C) {
+func (s *DockerSwarmSuite) TestServiceScale(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	service1Name := "TestService1"
-	service1Args := append([]string{"service", "create", "--detach", "--no-resolve-image", "--name", service1Name, defaultSleepImage}, sleepCommandForDaemonPlatform()...)
+	service1Args := append([]string{"service", "create", "--detach", "--no-resolve-image", "--name", service1Name, "busybox"}, sleepCommandForDaemonPlatform()...)
 
 	// global mode
 	service2Name := "TestService2"
-	service2Args := append([]string{"service", "create", "--detach", "--no-resolve-image", "--name", service2Name, "--mode=global", defaultSleepImage}, sleepCommandForDaemonPlatform()...)
+	service2Args := append([]string{"service", "create", "--detach", "--no-resolve-image", "--name", service2Name, "--mode=global", "busybox"}, sleepCommandForDaemonPlatform()...)
 
 	// Create services
 	_, err := d.Cmd(service1Args...)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	_, err = d.Cmd(service2Args...)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	_, err = d.Cmd("service", "scale", "TestService1=2")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	out, err := d.Cmd("service", "scale", "TestService1=foobar")
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	str := fmt.Sprintf("%s: invalid replicas value %s", service1Name, "foobar")
 	if !strings.Contains(out, str) {
@@ -39,7 +40,7 @@ func (s *DockerSwarmSuite) TestServiceScale(c *check.C) {
 	}
 
 	out, err = d.Cmd("service", "scale", "TestService1=-1")
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	str = fmt.Sprintf("%s: invalid replicas value %s", service1Name, "-1")
 	if !strings.Contains(out, str) {
@@ -48,7 +49,7 @@ func (s *DockerSwarmSuite) TestServiceScale(c *check.C) {
 
 	// TestService2 is a global mode
 	out, err = d.Cmd("service", "scale", "TestService2=2")
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	str = fmt.Sprintf("%s: scale can only be used with replicated mode\n", service2Name)
 	if out != str {
diff --git a/integration-cli/docker_cli_service_update_test.go b/integration-cli/docker_cli_service_update_test.go
deleted file mode 100644
index c729860ec9374..0000000000000
--- a/integration-cli/docker_cli_service_update_test.go
+++ /dev/null
@@ -1,137 +0,0 @@
-// +build !windows
-
-package main
-
-import (
-	"encoding/json"
-	"fmt"
-
-	"github.com/docker/docker/api/types/swarm"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-)
-
-func (s *DockerSwarmSuite) TestServiceUpdateLabel(c *check.C) {
-	d := s.AddDaemon(c, true, true)
-	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name=test", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	service := d.GetService(c, "test")
-	c.Assert(service.Spec.Labels, checker.HasLen, 0)
-
-	// add label to empty set
-	out, err = d.Cmd("service", "update", "--detach", "test", "--label-add", "foo=bar")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	service = d.GetService(c, "test")
-	c.Assert(service.Spec.Labels, checker.HasLen, 1)
-	c.Assert(service.Spec.Labels["foo"], checker.Equals, "bar")
-
-	// add label to non-empty set
-	out, err = d.Cmd("service", "update", "--detach", "test", "--label-add", "foo2=bar")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	service = d.GetService(c, "test")
-	c.Assert(service.Spec.Labels, checker.HasLen, 2)
-	c.Assert(service.Spec.Labels["foo2"], checker.Equals, "bar")
-
-	out, err = d.Cmd("service", "update", "--detach", "test", "--label-rm", "foo2")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	service = d.GetService(c, "test")
-	c.Assert(service.Spec.Labels, checker.HasLen, 1)
-	c.Assert(service.Spec.Labels["foo2"], checker.Equals, "")
-
-	out, err = d.Cmd("service", "update", "--detach", "test", "--label-rm", "foo")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	service = d.GetService(c, "test")
-	c.Assert(service.Spec.Labels, checker.HasLen, 0)
-	c.Assert(service.Spec.Labels["foo"], checker.Equals, "")
-
-	// now make sure we can add again
-	out, err = d.Cmd("service", "update", "--detach", "test", "--label-add", "foo=bar")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	service = d.GetService(c, "test")
-	c.Assert(service.Spec.Labels, checker.HasLen, 1)
-	c.Assert(service.Spec.Labels["foo"], checker.Equals, "bar")
-}
-
-func (s *DockerSwarmSuite) TestServiceUpdateSecrets(c *check.C) {
-	d := s.AddDaemon(c, true, true)
-	testName := "test_secret"
-	id := d.CreateSecret(c, swarm.SecretSpec{
-		Annotations: swarm.Annotations{
-			Name: testName,
-		},
-		Data: []byte("TESTINGDATA"),
-	})
-	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
-	testTarget := "testing"
-	serviceName := "test"
-
-	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	// add secret
-	out, err = d.Cmd("service", "update", "--detach", "test", "--secret-add", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
-	c.Assert(err, checker.IsNil)
-
-	var refs []swarm.SecretReference
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
-	c.Assert(refs, checker.HasLen, 1)
-
-	c.Assert(refs[0].SecretName, checker.Equals, testName)
-	c.Assert(refs[0].File, checker.Not(checker.IsNil))
-	c.Assert(refs[0].File.Name, checker.Equals, testTarget)
-
-	// remove
-	out, err = d.Cmd("service", "update", "--detach", "test", "--secret-rm", testName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", serviceName)
-	c.Assert(err, checker.IsNil)
-
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
-	c.Assert(refs, checker.HasLen, 0)
-}
-
-func (s *DockerSwarmSuite) TestServiceUpdateConfigs(c *check.C) {
-	d := s.AddDaemon(c, true, true)
-	testName := "test_config"
-	id := d.CreateConfig(c, swarm.ConfigSpec{
-		Annotations: swarm.Annotations{
-			Name: testName,
-		},
-		Data: []byte("TESTINGDATA"),
-	})
-	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
-	testTarget := "/testing"
-	serviceName := "test"
-
-	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	// add config
-	out, err = d.Cmd("service", "update", "--detach", "test", "--config-add", fmt.Sprintf("source=%s,target=%s", testName, testTarget))
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
-	c.Assert(err, checker.IsNil)
-
-	var refs []swarm.ConfigReference
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
-	c.Assert(refs, checker.HasLen, 1)
-
-	c.Assert(refs[0].ConfigName, checker.Equals, testName)
-	c.Assert(refs[0].File, checker.Not(checker.IsNil))
-	c.Assert(refs[0].File.Name, checker.Equals, testTarget)
-
-	// remove
-	out, err = d.Cmd("service", "update", "--detach", "test", "--config-rm", testName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-
-	out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Configs }}", serviceName)
-	c.Assert(err, checker.IsNil)
-
-	c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil)
-	c.Assert(refs, checker.HasLen, 0)
-}
diff --git a/integration-cli/docker_cli_sni_test.go b/integration-cli/docker_cli_sni_test.go
index f50b5bbf6d1d5..90e4f095d5a99 100644
--- a/integration-cli/docker_cli_sni_test.go
+++ b/integration-cli/docker_cli_sni_test.go
@@ -2,18 +2,19 @@ package main
 
 import (
 	"fmt"
-	"io/ioutil"
+	"io"
 	"log"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
 	"os/exec"
 	"strings"
+	"testing"
 
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestClientSetsTLSServerName(c *check.C) {
+func (s *DockerSuite) TestClientSetsTLSServerName(c *testing.T) {
 	c.Skip("Flakey test")
 	// there may be more than one hit to the server for each registry request
 	var serverNameReceived []string
@@ -24,10 +25,10 @@ func (s *DockerSuite) TestClientSetsTLSServerName(c *check.C) {
 	}))
 	defer virtualHostServer.Close()
 	// discard TLS handshake errors written by default to os.Stderr
-	virtualHostServer.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
+	virtualHostServer.Config.ErrorLog = log.New(io.Discard, "", 0)
 
 	u, err := url.Parse(virtualHostServer.URL)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	hostPort := u.Host
 	serverName = strings.Split(hostPort, ":")[0]
 
@@ -36,9 +37,9 @@ func (s *DockerSuite) TestClientSetsTLSServerName(c *check.C) {
 	cmd.Run()
 
 	// check that the fake server was hit at least once
-	c.Assert(len(serverNameReceived) > 0, check.Equals, true)
+	assert.Assert(c, len(serverNameReceived) > 0)
 	// check that for each hit the right server name was received
 	for _, item := range serverNameReceived {
-		c.Check(item, check.Equals, serverName)
+		assert.Check(c, item == serverName)
 	}
 }
diff --git a/integration-cli/docker_cli_start_test.go b/integration-cli/docker_cli_start_test.go
index 303deddb28b06..f3b57f2a7303e 100644
--- a/integration-cli/docker_cli_start_test.go
+++ b/integration-cli/docker_cli_start_test.go
@@ -2,17 +2,21 @@ package main
 
 import (
 	"fmt"
+	"runtime"
+	"strconv"
 	"strings"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
+	"github.com/Microsoft/hcsshim/osversion"
 	"github.com/docker/docker/integration-cli/cli"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"github.com/docker/docker/pkg/parsers/kernel"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
 // Regression test for https://github.com/docker/docker/issues/7843
-func (s *DockerSuite) TestStartAttachReturnsOnError(c *check.C) {
+func (s *DockerSuite) TestStartAttachReturnsOnError(c *testing.T) {
 	// Windows does not support link
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "run", "--name", "test", "busybox")
@@ -20,9 +24,9 @@ func (s *DockerSuite) TestStartAttachReturnsOnError(c *check.C) {
 	// Expect this to fail because the above container is stopped, this is what we want
 	out, _, err := dockerCmdWithError("run", "--name", "test2", "--link", "test:test", "busybox")
 	// err shouldn't be nil because container test2 try to link to stopped container
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+	assert.Assert(c, err != nil, "out: %s", out)
 
-	ch := make(chan error)
+	ch := make(chan error, 1)
 	go func() {
 		// Attempt to start attached to the container that won't start
 		// This should return an error immediately since the container can't be started
@@ -34,14 +38,14 @@ func (s *DockerSuite) TestStartAttachReturnsOnError(c *check.C) {
 
 	select {
 	case err := <-ch:
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	case <-time.After(5 * time.Second):
 		c.Fatalf("Attach did not exit properly")
 	}
 }
 
 // gh#8555: Exit code should be passed through when using start -a
-func (s *DockerSuite) TestStartAttachCorrectExitCode(c *check.C) {
+func (s *DockerSuite) TestStartAttachCorrectExitCode(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	out := cli.DockerCmd(c, "run", "-d", "busybox", "sh", "-c", "sleep 2; exit 1").Stdout()
 	out = strings.TrimSpace(out)
@@ -54,7 +58,7 @@ func (s *DockerSuite) TestStartAttachCorrectExitCode(c *check.C) {
 	})
 }
 
-func (s *DockerSuite) TestStartAttachSilent(c *check.C) {
+func (s *DockerSuite) TestStartAttachSilent(c *testing.T) {
 	name := "teststartattachcorrectexitcode"
 	dockerCmd(c, "run", "--name", name, "busybox", "echo", "test")
 
@@ -63,35 +67,34 @@ func (s *DockerSuite) TestStartAttachSilent(c *check.C) {
 
 	startOut, _ := dockerCmd(c, "start", "-a", name)
 	// start -a produced unexpected output
-	c.Assert(startOut, checker.Equals, "test\n")
+	assert.Equal(c, startOut, "test\n")
 }
 
-func (s *DockerSuite) TestStartRecordError(c *check.C) {
+func (s *DockerSuite) TestStartRecordError(c *testing.T) {
 	// TODO Windows CI: Requires further porting work. Should be possible.
 	testRequires(c, DaemonIsLinux)
 	// when container runs successfully, we should not have state.Error
 	dockerCmd(c, "run", "-d", "-p", "9999:9999", "--name", "test", "busybox", "top")
 	stateErr := inspectField(c, "test", "State.Error")
 	// Expected to not have state error
-	c.Assert(stateErr, checker.Equals, "")
+	assert.Equal(c, stateErr, "")
 
 	// Expect this to fail and records error because of ports conflict
 	out, _, err := dockerCmdWithError("run", "-d", "--name", "test2", "-p", "9999:9999", "busybox", "top")
 	// err shouldn't be nil because docker run will fail
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+	assert.Assert(c, err != nil, "out: %s", out)
 
 	stateErr = inspectField(c, "test2", "State.Error")
-	c.Assert(stateErr, checker.Contains, "port is already allocated")
-
+	assert.Assert(c, strings.Contains(stateErr, "port is already allocated"))
 	// Expect the conflict to be resolved when we stop the initial container
 	dockerCmd(c, "stop", "test")
 	dockerCmd(c, "start", "test2")
 	stateErr = inspectField(c, "test2", "State.Error")
 	// Expected to not have state error but got one
-	c.Assert(stateErr, checker.Equals, "")
+	assert.Equal(c, stateErr, "")
 }
 
-func (s *DockerSuite) TestStartPausedContainer(c *check.C) {
+func (s *DockerSuite) TestStartPausedContainer(c *testing.T) {
 	// Windows does not support pausing containers
 	testRequires(c, IsPausable)
 
@@ -101,12 +104,12 @@ func (s *DockerSuite) TestStartPausedContainer(c *check.C) {
 
 	out, _, err := dockerCmdWithError("start", "testing")
 	// an error should have been shown that you cannot start paused container
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+	assert.Assert(c, err != nil, "out: %s", out)
 	// an error should have been shown that you cannot start paused container
-	c.Assert(strings.ToLower(out), checker.Contains, "cannot start a paused container, try unpause instead")
+	assert.Assert(c, strings.Contains(strings.ToLower(out), "cannot start a paused container, try unpause instead"))
 }
 
-func (s *DockerSuite) TestStartMultipleContainers(c *check.C) {
+func (s *DockerSuite) TestStartMultipleContainers(c *testing.T) {
 	// Windows does not support --link
 	testRequires(c, DaemonIsLinux)
 	// run a container named 'parent' and create two container link to `parent`
@@ -121,7 +124,7 @@ func (s *DockerSuite) TestStartMultipleContainers(c *check.C) {
 
 	out := inspectField(c, "parent", "State.Running")
 	// Container should be stopped
-	c.Assert(out, checker.Equals, "false")
+	assert.Equal(c, out, "false")
 
 	// start all the three containers, container `child_first` start first which should be failed
 	// container 'parent' start second and then start container 'child_second'
@@ -129,7 +132,7 @@ func (s *DockerSuite) TestStartMultipleContainers(c *check.C) {
 	expErr := "failed to start containers: [child_first]"
 	out, _, err := dockerCmdWithError("start", "child_first", "parent", "child_second")
 	// err shouldn't be nil because start will fail
-	c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+	assert.Assert(c, err != nil, "out: %s", out)
 	// output does not correspond to what was expected
 	if !(strings.Contains(out, expOut) || strings.Contains(err.Error(), expErr)) {
 		c.Fatalf("Expected out: %v with err: %v  but got out: %v with err: %v", expOut, expErr, out, err)
@@ -138,11 +141,11 @@ func (s *DockerSuite) TestStartMultipleContainers(c *check.C) {
 	for container, expected := range map[string]string{"parent": "true", "child_first": "false", "child_second": "true"} {
 		out := inspectField(c, container, "State.Running")
 		// Container running state wrong
-		c.Assert(out, checker.Equals, expected)
+		assert.Equal(c, out, expected)
 	}
 }
 
-func (s *DockerSuite) TestStartAttachMultipleContainers(c *check.C) {
+func (s *DockerSuite) TestStartAttachMultipleContainers(c *testing.T) {
 	// run  multiple containers to test
 	for _, container := range []string{"test1", "test2", "test3"} {
 		runSleepingContainer(c, "--name", container)
@@ -157,21 +160,21 @@ func (s *DockerSuite) TestStartAttachMultipleContainers(c *check.C) {
 	for _, option := range []string{"-a", "-i", "-ai"} {
 		out, _, err := dockerCmdWithError("start", option, "test1", "test2", "test3")
 		// err shouldn't be nil because start will fail
-		c.Assert(err, checker.NotNil, check.Commentf("out: %s", out))
+		assert.Assert(c, err != nil, "out: %s", out)
 		// output does not correspond to what was expected
-		c.Assert(out, checker.Contains, "you cannot start and attach multiple containers at once")
+		assert.Assert(c, strings.Contains(out, "you cannot start and attach multiple containers at once"))
 	}
 
 	// confirm the state of all the containers be stopped
 	for container, expected := range map[string]string{"test1": "false", "test2": "false", "test3": "false"} {
 		out := inspectField(c, container, "State.Running")
 		// Container running state wrong
-		c.Assert(out, checker.Equals, expected)
+		assert.Equal(c, out, expected)
 	}
 }
 
 // Test case for #23716
-func (s *DockerSuite) TestStartAttachWithRename(c *check.C) {
+func (s *DockerSuite) TestStartAttachWithRename(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	cli.DockerCmd(c, "create", "-t", "--name", "before", "busybox")
 	go func() {
@@ -183,18 +186,30 @@ func (s *DockerSuite) TestStartAttachWithRename(c *check.C) {
 	result := cli.Docker(cli.Args("start", "-a", "before")).Assert(c, icmd.Expected{
 		ExitCode: 137,
 	})
-	c.Assert(result.Stderr(), checker.Not(checker.Contains), "No such container")
+	assert.Assert(c, !strings.Contains(result.Stderr(), "No such container"))
 }
 
-func (s *DockerSuite) TestStartReturnCorrectExitCode(c *check.C) {
+func (s *DockerSuite) TestStartReturnCorrectExitCode(c *testing.T) {
+	// Note we parse kernel.GetKernelVersion rather than system.GetOSVersion
+	// as test binaries aren't manifested, so would otherwise report the wrong
+	// build number.
+	if runtime.GOOS == "windows" {
+		v, err := kernel.GetKernelVersion()
+		assert.NilError(c, err)
+		build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
+		if build < osversion.RS3 {
+			c.Skip("FLAKY on Windows RS1, see #38521")
+		}
+	}
+
 	dockerCmd(c, "create", "--restart=on-failure:2", "--name", "withRestart", "busybox", "sh", "-c", "exit 11")
 	dockerCmd(c, "create", "--rm", "--name", "withRm", "busybox", "sh", "-c", "exit 12")
 
 	out, exitCode, err := dockerCmdWithError("start", "-a", "withRestart")
-	c.Assert(err, checker.NotNil)
-	c.Assert(exitCode, checker.Equals, 11, check.Commentf("out: %s", out))
+	assert.ErrorContains(c, err, "")
+	assert.Equal(c, exitCode, 11, fmt.Sprintf("out: %s", out))
 
 	out, exitCode, err = dockerCmdWithError("start", "-a", "withRm")
-	c.Assert(err, checker.NotNil)
-	c.Assert(exitCode, checker.Equals, 12, check.Commentf("out: %s", out))
+	assert.ErrorContains(c, err, "")
+	assert.Equal(c, exitCode, 12, fmt.Sprintf("out: %s", out))
 }
diff --git a/integration-cli/docker_cli_stats_test.go b/integration-cli/docker_cli_stats_test.go
index 454836367f625..5941e6b2943bf 100644
--- a/integration-cli/docker_cli_stats_test.go
+++ b/integration-cli/docker_cli_stats_test.go
@@ -5,19 +5,20 @@ import (
 	"os/exec"
 	"regexp"
 	"strings"
+	"testing"
 	"time"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
-func (s *DockerSuite) TestStatsNoStream(c *check.C) {
+func (s *DockerSuite) TestStatsNoStream(c *testing.T) {
 	// Windows does not support stats
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
 	id := strings.TrimSpace(out)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, waitRun(id))
 
 	statsCmd := exec.Command(dockerBinary, "stats", "--no-stream", id)
 	type output struct {
@@ -25,7 +26,7 @@ func (s *DockerSuite) TestStatsNoStream(c *check.C) {
 		err error
 	}
 
-	ch := make(chan output)
+	ch := make(chan output, 1)
 	go func() {
 		out, err := statsCmd.Output()
 		ch <- output{out, err}
@@ -33,40 +34,40 @@ func (s *DockerSuite) TestStatsNoStream(c *check.C) {
 
 	select {
 	case outerr := <-ch:
-		c.Assert(outerr.err, checker.IsNil, check.Commentf("Error running stats: %v", outerr.err))
-		c.Assert(string(outerr.out), checker.Contains, id[:12]) //running container wasn't present in output
+		assert.NilError(c, outerr.err, "Error running stats: %v", outerr.err)
+		assert.Assert(c, is.Contains(string(outerr.out), id[:12]), "running container wasn't present in output")
 	case <-time.After(3 * time.Second):
 		statsCmd.Process.Kill()
 		c.Fatalf("stats did not return immediately when not streaming")
 	}
 }
 
-func (s *DockerSuite) TestStatsContainerNotFound(c *check.C) {
+func (s *DockerSuite) TestStatsContainerNotFound(c *testing.T) {
 	// Windows does not support stats
 	testRequires(c, DaemonIsLinux)
 
 	out, _, err := dockerCmdWithError("stats", "notfound")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "No such container: notfound", check.Commentf("Expected to fail on not found container stats, got %q instead", out))
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, is.Contains(out, "No such container: notfound"), "Expected to fail on not found container stats, got %q instead", out)
 
 	out, _, err = dockerCmdWithError("stats", "--no-stream", "notfound")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "No such container: notfound", check.Commentf("Expected to fail on not found container stats with --no-stream, got %q instead", out))
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, is.Contains(out, "No such container: notfound"), "Expected to fail on not found container stats with --no-stream, got %q instead", out)
 }
 
-func (s *DockerSuite) TestStatsAllRunningNoStream(c *check.C) {
+func (s *DockerSuite) TestStatsAllRunningNoStream(c *testing.T) {
 	// Windows does not support stats
 	testRequires(c, DaemonIsLinux)
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
 	id1 := strings.TrimSpace(out)[:12]
-	c.Assert(waitRun(id1), check.IsNil)
+	assert.NilError(c, waitRun(id1))
 	out, _ = dockerCmd(c, "run", "-d", "busybox", "top")
 	id2 := strings.TrimSpace(out)[:12]
-	c.Assert(waitRun(id2), check.IsNil)
+	assert.NilError(c, waitRun(id2))
 	out, _ = dockerCmd(c, "run", "-d", "busybox", "top")
 	id3 := strings.TrimSpace(out)[:12]
-	c.Assert(waitRun(id3), check.IsNil)
+	assert.NilError(c, waitRun(id3))
 	dockerCmd(c, "stop", id3)
 
 	out, _ = dockerCmd(c, "stats", "--no-stream")
@@ -84,23 +85,23 @@ func (s *DockerSuite) TestStatsAllRunningNoStream(c *check.C) {
 	outLines := strings.Split(out, "\n")
 	// check stat result of id2 contains real data
 	realData := reg.Find([]byte(outLines[1][12:]))
-	c.Assert(realData, checker.NotNil, check.Commentf("stat result are empty: %s", out))
+	assert.Assert(c, realData != nil, "stat result are empty: %s", out)
 	// check stat result of id1 contains real data
 	realData = reg.Find([]byte(outLines[2][12:]))
-	c.Assert(realData, checker.NotNil, check.Commentf("stat result are empty: %s", out))
+	assert.Assert(c, realData != nil, "stat result are empty: %s", out)
 }
 
-func (s *DockerSuite) TestStatsAllNoStream(c *check.C) {
+func (s *DockerSuite) TestStatsAllNoStream(c *testing.T) {
 	// Windows does not support stats
 	testRequires(c, DaemonIsLinux)
 
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
 	id1 := strings.TrimSpace(out)[:12]
-	c.Assert(waitRun(id1), check.IsNil)
+	assert.NilError(c, waitRun(id1))
 	dockerCmd(c, "stop", id1)
 	out, _ = dockerCmd(c, "run", "-d", "busybox", "top")
 	id2 := strings.TrimSpace(out)[:12]
-	c.Assert(waitRun(id2), check.IsNil)
+	assert.NilError(c, waitRun(id2))
 
 	out, _ = dockerCmd(c, "stats", "--all", "--no-stream")
 	if !strings.Contains(out, id1) || !strings.Contains(out, id2) {
@@ -113,13 +114,14 @@ func (s *DockerSuite) TestStatsAllNoStream(c *check.C) {
 	outLines := strings.Split(out, "\n")
 	// check stat result of id2 contains real data
 	realData := reg.Find([]byte(outLines[1][12:]))
-	c.Assert(realData, checker.NotNil, check.Commentf("stat result of %s is empty: %s", id2, out))
+	assert.Assert(c, realData != nil, "stat result of %s is empty: %s", id2, out)
+
 	// check stat result of id1 contains all zero
 	realData = reg.Find([]byte(outLines[2][12:]))
-	c.Assert(realData, checker.IsNil, check.Commentf("stat result of %s should be empty : %s", id1, out))
+	assert.Assert(c, realData == nil, "stat result of %s should be empty : %s", id1, out)
 }
 
-func (s *DockerSuite) TestStatsAllNewContainersAdded(c *check.C) {
+func (s *DockerSuite) TestStatsAllNewContainersAdded(c *testing.T) {
 	// Windows does not support stats
 	testRequires(c, DaemonIsLinux)
 
@@ -129,8 +131,8 @@ func (s *DockerSuite) TestStatsAllNewContainersAdded(c *check.C) {
 	runSleepingContainer(c, "-d")
 	statsCmd := exec.Command(dockerBinary, "stats")
 	stdout, err := statsCmd.StdoutPipe()
-	c.Assert(err, check.IsNil)
-	c.Assert(statsCmd.Start(), check.IsNil)
+	assert.NilError(c, err)
+	assert.NilError(c, statsCmd.Start())
 	go statsCmd.Wait()
 	defer statsCmd.Process.Kill()
 
@@ -149,7 +151,7 @@ func (s *DockerSuite) TestStatsAllNewContainersAdded(c *check.C) {
 	}()
 
 	out := runSleepingContainer(c, "-d")
-	c.Assert(waitRun(strings.TrimSpace(out)), check.IsNil)
+	assert.NilError(c, waitRun(strings.TrimSpace(out)))
 	id <- strings.TrimSpace(out)[:12]
 
 	select {
@@ -160,7 +162,7 @@ func (s *DockerSuite) TestStatsAllNewContainersAdded(c *check.C) {
 	}
 }
 
-func (s *DockerSuite) TestStatsFormatAll(c *check.C) {
+func (s *DockerSuite) TestStatsFormatAll(c *testing.T) {
 	// Windows does not support stats
 	testRequires(c, DaemonIsLinux)
 
@@ -171,10 +173,10 @@ func (s *DockerSuite) TestStatsFormatAll(c *check.C) {
 	cli.WaitExited(c, "ExitedOne", 5*time.Second)
 
 	out := cli.DockerCmd(c, "stats", "--no-stream", "--format", "{{.Name}}").Combined()
-	c.Assert(out, checker.Contains, "RunningOne")
-	c.Assert(out, checker.Not(checker.Contains), "ExitedOne")
+	assert.Assert(c, is.Contains(out, "RunningOne"))
+	assert.Assert(c, !strings.Contains(out, "ExitedOne"))
 
 	out = cli.DockerCmd(c, "stats", "--all", "--no-stream", "--format", "{{.Name}}").Combined()
-	c.Assert(out, checker.Contains, "RunningOne")
-	c.Assert(out, checker.Contains, "ExitedOne")
+	assert.Assert(c, is.Contains(out, "RunningOne"))
+	assert.Assert(c, is.Contains(out, "ExitedOne"))
 }
diff --git a/integration-cli/docker_cli_swarm_test.go b/integration-cli/docker_cli_swarm_test.go
index 9f99d0c84900a..daabbad4c6d68 100644
--- a/integration-cli/docker_cli_swarm_test.go
+++ b/integration-cli/docker_cli_swarm_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -8,12 +9,13 @@ import (
 	"encoding/json"
 	"encoding/pem"
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
 	"os"
 	"path/filepath"
+	"runtime"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/cloudflare/cfssl/helpers"
@@ -22,17 +24,18 @@ import (
 	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/daemon"
-	"github.com/docker/libnetwork/driverapi"
-	"github.com/docker/libnetwork/ipamapi"
-	remoteipam "github.com/docker/libnetwork/ipams/remote/api"
+	"github.com/docker/docker/libnetwork/driverapi"
+	"github.com/docker/docker/libnetwork/ipamapi"
+	remoteipam "github.com/docker/docker/libnetwork/ipams/remote/api"
 	"github.com/docker/swarmkit/ca/keyutils"
-	"github.com/go-check/check"
 	"github.com/vishvananda/netlink"
-	"gotest.tools/fs"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/fs"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/poll"
 )
 
-func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmUpdate(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	getSpec := func() swarm.Spec {
@@ -41,31 +44,31 @@ func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
 	}
 
 	out, err := d.Cmd("swarm", "update", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	spec := getSpec()
-	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
-	c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 11*time.Second)
+	assert.Equal(c, spec.CAConfig.NodeCertExpiry, 30*time.Hour)
+	assert.Equal(c, spec.Dispatcher.HeartbeatPeriod, 11*time.Second)
 
 	// setting anything under 30m for cert-expiry is not allowed
 	out, err = d.Cmd("swarm", "update", "--cert-expiry", "15m")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "minimum certificate expiry time")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "minimum certificate expiry time"))
 	spec = getSpec()
-	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
+	assert.Equal(c, spec.CAConfig.NodeCertExpiry, 30*time.Hour)
 
 	// passing an external CA (this is without starting a root rotation) does not fail
 	cli.Docker(cli.Args("swarm", "update", "--external-ca", "protocol=cfssl,url=https://something.org",
 		"--external-ca", "protocol=cfssl,url=https://somethingelse.org,cacert=fixtures/https/ca.pem"),
 		cli.Daemon(d)).Assert(c, icmd.Success)
 
-	expected, err := ioutil.ReadFile("fixtures/https/ca.pem")
-	c.Assert(err, checker.IsNil)
+	expected, err := os.ReadFile("fixtures/https/ca.pem")
+	assert.NilError(c, err)
 
 	spec = getSpec()
-	c.Assert(spec.CAConfig.ExternalCAs, checker.HasLen, 2)
-	c.Assert(spec.CAConfig.ExternalCAs[0].CACert, checker.Equals, "")
-	c.Assert(spec.CAConfig.ExternalCAs[1].CACert, checker.Equals, string(expected))
+	assert.Equal(c, len(spec.CAConfig.ExternalCAs), 2)
+	assert.Equal(c, spec.CAConfig.ExternalCAs[0].CACert, "")
+	assert.Equal(c, spec.CAConfig.ExternalCAs[1].CACert, string(expected))
 
 	// passing an invalid external CA fails
 	tempFile := fs.NewFile(c, "testfile", fs.WithContent("fakecert"))
@@ -80,7 +83,7 @@ func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
 	})
 }
 
-func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmInit(c *testing.T) {
 	d := s.AddDaemon(c, false, false)
 
 	getSpec := func() swarm.Spec {
@@ -105,25 +108,25 @@ func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
 		"--external-ca", "protocol=cfssl,url=https://somethingelse.org,cacert=fixtures/https/ca.pem"),
 		cli.Daemon(d)).Assert(c, icmd.Success)
 
-	expected, err := ioutil.ReadFile("fixtures/https/ca.pem")
-	c.Assert(err, checker.IsNil)
+	expected, err := os.ReadFile("fixtures/https/ca.pem")
+	assert.NilError(c, err)
 
 	spec := getSpec()
-	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
-	c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 11*time.Second)
-	c.Assert(spec.CAConfig.ExternalCAs, checker.HasLen, 2)
-	c.Assert(spec.CAConfig.ExternalCAs[0].CACert, checker.Equals, "")
-	c.Assert(spec.CAConfig.ExternalCAs[1].CACert, checker.Equals, string(expected))
+	assert.Equal(c, spec.CAConfig.NodeCertExpiry, 30*time.Hour)
+	assert.Equal(c, spec.Dispatcher.HeartbeatPeriod, 11*time.Second)
+	assert.Equal(c, len(spec.CAConfig.ExternalCAs), 2)
+	assert.Equal(c, spec.CAConfig.ExternalCAs[0].CACert, "")
+	assert.Equal(c, spec.CAConfig.ExternalCAs[1].CACert, string(expected))
 
-	c.Assert(d.SwarmLeave(true), checker.IsNil)
+	assert.Assert(c, d.SwarmLeave(c, true) == nil)
 	cli.Docker(cli.Args("swarm", "init"), cli.Daemon(d)).Assert(c, icmd.Success)
 
 	spec = getSpec()
-	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 90*24*time.Hour)
-	c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 5*time.Second)
+	assert.Equal(c, spec.CAConfig.NodeCertExpiry, 90*24*time.Hour)
+	assert.Equal(c, spec.Dispatcher.HeartbeatPeriod, 5*time.Second)
 }
 
-func (s *DockerSwarmSuite) TestSwarmInitIPv6(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmInitIPv6(c *testing.T) {
 	testRequires(c, IPv6)
 	d1 := s.AddDaemon(c, false, false)
 	cli.Docker(cli.Args("swarm", "init", "--listen-add", "::1"), cli.Daemon(d1)).Assert(c, icmd.Success)
@@ -132,338 +135,336 @@ func (s *DockerSwarmSuite) TestSwarmInitIPv6(c *check.C) {
 	cli.Docker(cli.Args("swarm", "join", "::1"), cli.Daemon(d2)).Assert(c, icmd.Success)
 
 	out := cli.Docker(cli.Args("info"), cli.Daemon(d2)).Assert(c, icmd.Success).Combined()
-	c.Assert(out, checker.Contains, "Swarm: active")
+	assert.Assert(c, strings.Contains(out, "Swarm: active"))
 }
 
-func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedAdvertiseAddr(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedAdvertiseAddr(c *testing.T) {
 	d := s.AddDaemon(c, false, false)
 	out, err := d.Cmd("swarm", "init", "--advertise-addr", "0.0.0.0")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "advertise address must be a non-zero IP address")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "advertise address must be a non-zero IP address"))
 }
 
-func (s *DockerSwarmSuite) TestSwarmIncompatibleDaemon(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmIncompatibleDaemon(c *testing.T) {
 	// init swarm mode and stop a daemon
 	d := s.AddDaemon(c, true, true)
 	info := d.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
 	d.Stop(c)
 
 	// start a daemon with --cluster-store and --cluster-advertise
 	err := d.StartWithError("--cluster-store=consul://consuladdr:consulport/some/path", "--cluster-advertise=1.1.1.1:2375")
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 	content, err := d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, "--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), "--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode"))
 	// start a daemon with --live-restore
 	err = d.StartWithError("--live-restore")
-	c.Assert(err, checker.NotNil)
+	assert.ErrorContains(c, err, "")
 	content, err = d.ReadLogFile()
-	c.Assert(err, checker.IsNil)
-	c.Assert(string(content), checker.Contains, "--live-restore daemon configuration is incompatible with swarm mode")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(string(content), "--live-restore daemon configuration is incompatible with swarm mode"))
 	// restart for teardown
-	d.Start(c)
+	d.StartNode(c)
 }
 
-func (s *DockerSwarmSuite) TestSwarmServiceTemplatingHostname(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmServiceTemplatingHostname(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 	hostname, err := d.Cmd("node", "inspect", "--format", "{{.Description.Hostname}}", "self")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", hostname))
+	assert.Assert(c, err == nil, hostname)
 
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "test", "--hostname", "{{.Service.Name}}-{{.Task.Slot}}-{{.Node.Hostname}}", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	containers := d.ActiveContainers(c)
 	out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.Hostname}}", containers[0])
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.Split(out, "\n")[0], checker.Equals, "test-1-"+strings.Split(hostname, "\n")[0], check.Commentf("hostname with templating invalid"))
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.Split(out, "\n")[0], "test-1-"+strings.Split(hostname, "\n")[0], "hostname with templating invalid")
 }
 
 // Test case for #24270
-func (s *DockerSwarmSuite) TestSwarmServiceListFilter(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmServiceListFilter(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name1 := "redis-cluster-md5"
 	name2 := "redis-cluster"
 	name3 := "other-cluster"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name1, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name2, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name3, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	filter1 := "name=redis-cluster-md5"
 	filter2 := "name=redis-cluster"
 
 	// We search checker.Contains with `name+" "` to prevent prefix only.
 	out, err = d.Cmd("service", "ls", "--filter", filter1)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, name1+" ")
-	c.Assert(out, checker.Not(checker.Contains), name2+" ")
-	c.Assert(out, checker.Not(checker.Contains), name3+" ")
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, name1+" "), out)
+	assert.Assert(c, !strings.Contains(out, name2+" "), out)
+	assert.Assert(c, !strings.Contains(out, name3+" "), out)
 	out, err = d.Cmd("service", "ls", "--filter", filter2)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, name1+" ")
-	c.Assert(out, checker.Contains, name2+" ")
-	c.Assert(out, checker.Not(checker.Contains), name3+" ")
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, name1+" "), out)
+	assert.Assert(c, strings.Contains(out, name2+" "), out)
+	assert.Assert(c, !strings.Contains(out, name3+" "), out)
 	out, err = d.Cmd("service", "ls")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, name1+" ")
-	c.Assert(out, checker.Contains, name2+" ")
-	c.Assert(out, checker.Contains, name3+" ")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, name1+" "), out)
+	assert.Assert(c, strings.Contains(out, name2+" "), out)
+	assert.Assert(c, strings.Contains(out, name3+" "), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmNodeListFilter(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmNodeListFilter(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("node", "inspect", "--format", "{{ .Description.Hostname }}", "self")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 	name := strings.TrimSpace(out)
 
 	filter := "name=" + name[:4]
 
 	out, err = d.Cmd("node", "ls", "--filter", filter)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, name)
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, name), out)
 	out, err = d.Cmd("node", "ls", "--filter", "name=none")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), name)
+	assert.NilError(c, err, out)
+	assert.Assert(c, !strings.Contains(out, name), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmNodeTaskListFilter(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmNodeTaskListFilter(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "redis-cluster-md5"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--replicas=3", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 3)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(3)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	filter := "name=redis-cluster"
 
 	out, err = d.Cmd("node", "ps", "--filter", filter, "self")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, name+".1")
-	c.Assert(out, checker.Contains, name+".2")
-	c.Assert(out, checker.Contains, name+".3")
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, name+".1"), out)
+	assert.Assert(c, strings.Contains(out, name+".2"), out)
+	assert.Assert(c, strings.Contains(out, name+".3"), out)
 	out, err = d.Cmd("node", "ps", "--filter", "name=none", "self")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), name+".1")
-	c.Assert(out, checker.Not(checker.Contains), name+".2")
-	c.Assert(out, checker.Not(checker.Contains), name+".3")
+	assert.NilError(c, err, out)
+	assert.Assert(c, !strings.Contains(out, name+".1"), out)
+	assert.Assert(c, !strings.Contains(out, name+".2"), out)
+	assert.Assert(c, !strings.Contains(out, name+".3"), out)
 }
 
 // Test case for #25375
-func (s *DockerSwarmSuite) TestSwarmPublishAdd(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmPublishAdd(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "top"
+	// this first command does not have to be retried because service creates
+	// don't return out of sequence errors.
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--label", "x=y", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
-	out, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", name)
+	assert.NilError(c, err, out)
 
-	out, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", name)
+	assert.NilError(c, err, out)
 
-	_, err = d.Cmd("service", "update", "--detach", "--publish-add", "80:80", "--publish-add", "80:20", name)
-	c.Assert(err, checker.NotNil)
+	_, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", "--publish-add", "80:20", name)
+	assert.ErrorContains(c, err, "")
 
+	// this last command does not have to be retried because service inspect
+	// does not return out of sequence errors.
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.EndpointSpec.Ports }}", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "[{ tcp 80 80 ingress}]")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "[{ tcp 80 80 ingress}]")
 }
 
-func (s *DockerSwarmSuite) TestSwarmServiceWithGroup(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmServiceWithGroup(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "top"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--user", "root:root", "--group", "wheel", "--group", "audio", "--group", "staff", "--group", "777", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("ps", "-q")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	container := strings.TrimSpace(out)
 
 	out, err = d.Cmd("exec", container, "id")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "uid=0(root) gid=0(root) groups=10(wheel),29(audio),50(staff),777")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "uid=0(root) gid=0(root) groups=10(wheel),29(audio),50(staff),777")
 }
 
-func (s *DockerSwarmSuite) TestSwarmContainerAutoStart(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmContainerAutoStart(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	out, err = d.Cmd("run", "-id", "--restart=always", "--net=foo", "--name=test", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	out, err = d.Cmd("ps", "-q")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
-	d.Restart(c)
+	d.RestartNode(c)
 
 	out, err = d.Cmd("ps", "-q")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 }
 
-func (s *DockerSwarmSuite) TestSwarmContainerEndpointOptions(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmContainerEndpointOptions(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	out, err = d.Cmd("run", "-d", "--net=foo", "--name=first", "--net-alias=first-alias", "busybox:glibc", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("run", "-d", "--net=foo", "--name=second", "busybox:glibc", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("run", "-d", "--net=foo", "--net-alias=third-alias", "busybox:glibc", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// ping first container and its alias, also ping third and anonymous container by its alias
 	out, err = d.Cmd("exec", "second", "ping", "-c", "1", "first")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	out, err = d.Cmd("exec", "second", "ping", "-c", "1", "first-alias")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	out, err = d.Cmd("exec", "second", "ping", "-c", "1", "third-alias")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmContainerAttachByNetworkId(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmContainerAttachByNetworkId(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "testnet")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 	networkID := strings.TrimSpace(out)
 
 	out, err = d.Cmd("run", "-d", "--net", networkID, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	cID := strings.TrimSpace(out)
 	d.WaitRun(cID)
 
 	out, err = d.Cmd("rm", "-f", cID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("network", "rm", "testnet")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
-	checkNetwork := func(*check.C) (interface{}, check.CommentInterface) {
+	checkNetwork := func(*testing.T) (interface{}, string) {
 		out, err := d.Cmd("network", "ls")
-		c.Assert(err, checker.IsNil)
-		return out, nil
+		assert.NilError(c, err)
+		return out, ""
 	}
 
-	waitAndAssert(c, 3*time.Second, checkNetwork, checker.Not(checker.Contains), "testnet")
+	poll.WaitOn(c, pollCheck(c, checkNetwork, checker.Not(checker.Contains("testnet"))), poll.WithTimeout(3*time.Second))
 }
 
-func (s *DockerSwarmSuite) TestOverlayAttachable(c *check.C) {
+func (s *DockerSwarmSuite) TestOverlayAttachable(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", "ovnet")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// validate attachable
 	out, err = d.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "true")
 
 	// validate containers can attach to this overlay network
 	out, err = d.Cmd("run", "-d", "--network", "ovnet", "--name", "c1", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// redo validation, there was a bug that the value of attachable changes after
 	// containers attach to the network
 	out, err = d.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "true")
 }
 
-func (s *DockerSwarmSuite) TestOverlayAttachableOnSwarmLeave(c *check.C) {
+func (s *DockerSwarmSuite) TestOverlayAttachableOnSwarmLeave(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create an attachable swarm network
 	nwName := "attovl"
 	out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", nwName)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Connect a container to the network
 	out, err = d.Cmd("run", "-d", "--network", nwName, "--name", "c1", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Leave the swarm
-	err = d.SwarmLeave(true)
-	c.Assert(err, checker.IsNil)
+	assert.Assert(c, d.SwarmLeave(c, true) == nil)
 
 	// Check the container is disconnected
 	out, err = d.Cmd("inspect", "c1", "--format", "{{.NetworkSettings.Networks."+nwName+"}}")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "")
 
 	// Check the network is gone
 	out, err = d.Cmd("network", "ls", "--format", "{{.Name}}")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), nwName)
+	assert.NilError(c, err, out)
+	assert.Assert(c, !strings.Contains(out, nwName), out)
 }
 
-func (s *DockerSwarmSuite) TestOverlayAttachableReleaseResourcesOnFailure(c *check.C) {
+func (s *DockerSwarmSuite) TestOverlayAttachableReleaseResourcesOnFailure(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create attachable network
 	out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", "--subnet", "10.10.9.0/24", "ovnet")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Attach a container with specific IP
 	out, err = d.Cmd("run", "-d", "--network", "ovnet", "--name", "c1", "--ip", "10.10.9.33", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Attempt to attach another container with same IP, must fail
 	out, err = d.Cmd("run", "-d", "--network", "ovnet", "--name", "c2", "--ip", "10.10.9.33", "busybox", "top")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
+	assert.ErrorContains(c, err, "", out)
 
 	// Remove first container
 	out, err = d.Cmd("rm", "-f", "c1")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Verify the network can be removed, no phantom network attachment task left over
 	out, err = d.Cmd("network", "rm", "ovnet")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmIngressNetwork(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmIngressNetwork(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// Ingress network can be removed
@@ -478,16 +479,15 @@ func (s *DockerSwarmSuite) TestSwarmIngressNetwork(c *check.C) {
 
 	// And recreated
 	out, err := d.Cmd("network", "create", "-d", "overlay", "--ingress", "new-ingress")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// But only one is allowed
 	out, err = d.Cmd("network", "create", "-d", "overlay", "--ingress", "another-ingress")
-	c.Assert(err, checker.NotNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, "is already present")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "is already present"), out)
 	// It cannot be removed if it is being used
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "srv1", "-p", "9000:8000", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	result = removeNetwork("new-ingress")
 	result.Assert(c, icmd.Expected{
@@ -497,27 +497,25 @@ func (s *DockerSwarmSuite) TestSwarmIngressNetwork(c *check.C) {
 
 	// But it can be removed once no more services depend on it
 	out, err = d.Cmd("service", "update", "--detach", "--publish-rm", "9000:8000", "srv1")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	result = removeNetwork("new-ingress")
 	result.Assert(c, icmd.Success)
 
 	// A service which needs the ingress network cannot be created if no ingress is present
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "srv2", "-p", "500:500", "busybox", "top")
-	c.Assert(err, checker.NotNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, "no ingress network is present")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "no ingress network is present"), out)
 	// An existing service cannot be updated to use the ingress nw if the nw is not present
 	out, err = d.Cmd("service", "update", "--detach", "--publish-add", "9000:8000", "srv1")
-	c.Assert(err, checker.NotNil)
-	c.Assert(strings.TrimSpace(out), checker.Contains, "no ingress network is present")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "no ingress network is present"), out)
 	// But services which do not need routing mesh can be created regardless
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "srv3", "--endpoint-mode", "dnsrr", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmCreateServiceWithNoIngressNetwork(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmCreateServiceWithNoIngressNetwork(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// Remove ingress network
@@ -529,105 +527,100 @@ func (s *DockerSwarmSuite) TestSwarmCreateServiceWithNoIngressNetwork(c *check.C
 	// Create a overlay network and launch a service on it
 	// Make sure nothing panics because ingress network is missing
 	out, err := d.Cmd("network", "create", "-d", "overlay", "another-network")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "srv4", "--network", "another-network", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 }
 
 // Test case for #24108, also the case from:
 // https://github.com/docker/docker/pull/24620#issuecomment-233715656
-func (s *DockerSwarmSuite) TestSwarmTaskListFilter(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmTaskListFilter(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "redis-cluster-md5"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--replicas=3", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	filter := "name=redis-cluster"
 
-	checkNumTasks := func(*check.C) (interface{}, check.CommentInterface) {
+	checkNumTasks := func(*testing.T) (interface{}, string) {
 		out, err := d.Cmd("service", "ps", "--filter", filter, name)
-		c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-		return len(strings.Split(out, "\n")) - 2, nil // includes header and nl in last line
+		assert.NilError(c, err, out)
+		return len(strings.Split(out, "\n")) - 2, "" // includes header and nl in last line
 	}
 
 	// wait until all tasks have been created
-	waitAndAssert(c, defaultReconciliationTimeout, checkNumTasks, checker.Equals, 3)
+	poll.WaitOn(c, pollCheck(c, checkNumTasks, checker.Equals(3)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("service", "ps", "--filter", filter, name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, name+".1")
-	c.Assert(out, checker.Contains, name+".2")
-	c.Assert(out, checker.Contains, name+".3")
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, name+".1"), out)
+	assert.Assert(c, strings.Contains(out, name+".2"), out)
+	assert.Assert(c, strings.Contains(out, name+".3"), out)
 	out, err = d.Cmd("service", "ps", "--filter", "name="+name+".1", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, name+".1")
-	c.Assert(out, checker.Not(checker.Contains), name+".2")
-	c.Assert(out, checker.Not(checker.Contains), name+".3")
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, name+".1"), out)
+	assert.Assert(c, !strings.Contains(out, name+".2"), out)
+	assert.Assert(c, !strings.Contains(out, name+".3"), out)
 	out, err = d.Cmd("service", "ps", "--filter", "name=none", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), name+".1")
-	c.Assert(out, checker.Not(checker.Contains), name+".2")
-	c.Assert(out, checker.Not(checker.Contains), name+".3")
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, !strings.Contains(out, name+".1"), out)
+	assert.Assert(c, !strings.Contains(out, name+".2"), out)
+	assert.Assert(c, !strings.Contains(out, name+".3"), out)
 	name = "redis-cluster-sha1"
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--mode=global", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
-	waitAndAssert(c, defaultReconciliationTimeout, checkNumTasks, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, checkNumTasks, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	filter = "name=redis-cluster"
 	out, err = d.Cmd("service", "ps", "--filter", filter, name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, name)
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, name), out)
 	out, err = d.Cmd("service", "ps", "--filter", "name="+name, name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, name)
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, name), out)
 	out, err = d.Cmd("service", "ps", "--filter", "name=none", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Not(checker.Contains), name)
+	assert.NilError(c, err, out)
+	assert.Assert(c, !strings.Contains(out, name), out)
 }
 
-func (s *DockerSwarmSuite) TestPsListContainersFilterIsTask(c *check.C) {
+func (s *DockerSwarmSuite) TestPsListContainersFilterIsTask(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a bare container
 	out, err := d.Cmd("run", "-d", "--name=bare-container", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	bareID := strings.TrimSpace(out)[:12]
 	// Create a service
 	name := "busybox-top"
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckServiceRunningTasks(name), checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckServiceRunningTasks(name), checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// Filter non-tasks
 	out, err = d.Cmd("ps", "-a", "-q", "--filter=is-task=false")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	psOut := strings.TrimSpace(out)
-	c.Assert(psOut, checker.Equals, bareID, check.Commentf("Expected id %s, got %s for is-task label, output %q", bareID, psOut, out))
+	assert.Equal(c, psOut, bareID, fmt.Sprintf("Expected id %s, got %s for is-task label, output %q", bareID, psOut, out))
 
 	// Filter tasks
 	out, err = d.Cmd("ps", "-a", "-q", "--filter=is-task=true")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	lines := strings.Split(strings.Trim(out, "\n "), "\n")
-	c.Assert(lines, checker.HasLen, 1)
-	c.Assert(lines[0], checker.Not(checker.Equals), bareID, check.Commentf("Expected not %s, but got it for is-task label, output %q", bareID, out))
+	assert.Equal(c, len(lines), 1)
+	assert.Assert(c, lines[0] != bareID, "Expected not %s, but got it for is-task label, output %q", bareID, out)
 }
 
 const globalNetworkPlugin = "global-network-plugin"
 const globalIPAMPlugin = "global-ipam-plugin"
 
-func setupRemoteGlobalNetworkPlugin(c *check.C, mux *http.ServeMux, url, netDrv, ipamDrv string) {
+func setupRemoteGlobalNetworkPlugin(c *testing.T, mux *http.ServeMux, url, netDrv, ipamDrv string) {
 
 	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
@@ -784,139 +777,138 @@ func setupRemoteGlobalNetworkPlugin(c *check.C, mux *http.ServeMux, url, netDrv,
 	})
 
 	err := os.MkdirAll("/etc/docker/plugins", 0755)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv)
-	err = ioutil.WriteFile(fileName, []byte(url), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(fileName, []byte(url), 0644)
+	assert.NilError(c, err)
 
 	ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv)
-	err = ioutil.WriteFile(ipamFileName, []byte(url), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(ipamFileName, []byte(url), 0644)
+	assert.NilError(c, err)
 }
 
-func (s *DockerSwarmSuite) TestSwarmNetworkPlugin(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmNetworkPlugin(c *testing.T) {
 	mux := http.NewServeMux()
 	s.server = httptest.NewServer(mux)
-	c.Assert(s.server, check.NotNil) // check that HTTP server has started
+	assert.Assert(c, s.server != nil) // check that HTTP server has started
 	setupRemoteGlobalNetworkPlugin(c, mux, s.server.URL, globalNetworkPlugin, globalIPAMPlugin)
 	defer func() {
 		s.server.Close()
 		err := os.RemoveAll("/etc/docker/plugins")
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	}()
 
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "-d", globalNetworkPlugin, "foo")
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "not supported in swarm mode")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "not supported in swarm mode"), out)
 }
 
 // Test case for #24712
-func (s *DockerSwarmSuite) TestSwarmServiceEnvFile(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmServiceEnvFile(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	path := filepath.Join(d.Folder, "env.txt")
-	err := ioutil.WriteFile(path, []byte("VAR1=A\nVAR2=A\n"), 0644)
-	c.Assert(err, checker.IsNil)
+	err := os.WriteFile(path, []byte("VAR1=A\nVAR2=A\n"), 0644)
+	assert.NilError(c, err)
 
 	name := "worker"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--env-file", path, "--env", "VAR1=B", "--env", "VAR1=C", "--env", "VAR2=", "--env", "VAR2", "--name", name, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	// The complete env is [VAR1=A VAR2=A VAR1=B VAR1=C VAR2= VAR2] and duplicates will be removed => [VAR1=C VAR2]
 	out, err = d.Cmd("inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.Env }}", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "[VAR1=C VAR2]")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, "[VAR1=C VAR2]"), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmServiceTTY(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmServiceTTY(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "top"
 
-	ttyCheck := "if [ -t 0 ]; then echo TTY > /status && top; else echo none > /status && top; fi"
+	ttyCheck := "if [ -t 0 ]; then echo TTY > /status; else echo none > /status; fi; exec top"
 
 	// Without --tty
 	expectedOutput := "none"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", ttyCheck)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// We need to get the container id.
 	out, err = d.Cmd("ps", "-q", "--no-trunc")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	out, err = d.Cmd("exec", id, "cat", "/status")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out)
 	// Remove service
 	out, err = d.Cmd("service", "rm", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	// Make sure container has been destroyed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 0)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// With --tty
 	expectedOutput = "TTY"
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--tty", "busybox", "sh", "-c", ttyCheck)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// We need to get the container id.
 	out, err = d.Cmd("ps", "-q", "--no-trunc")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id = strings.TrimSpace(out)
 
 	out, err = d.Cmd("exec", id, "cat", "/status")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmServiceTTYUpdate(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmServiceTTYUpdate(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a service
 	name := "top"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "false")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "false")
 
 	out, err = d.Cmd("service", "update", "--detach", "--tty", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "true")
 }
 
-func (s *DockerSwarmSuite) TestSwarmServiceNetworkUpdate(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmServiceNetworkUpdate(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	result := icmd.RunCmd(d.Command("network", "create", "-d", "overlay", "foo"))
 	result.Assert(c, icmd.Success)
-	fooNetwork := strings.TrimSpace(string(result.Combined()))
+	fooNetwork := strings.TrimSpace(result.Combined())
 
 	result = icmd.RunCmd(d.Command("network", "create", "-d", "overlay", "bar"))
 	result.Assert(c, icmd.Success)
-	barNetwork := strings.TrimSpace(string(result.Combined()))
+	barNetwork := strings.TrimSpace(result.Combined())
 
 	result = icmd.RunCmd(d.Command("network", "create", "-d", "overlay", "baz"))
 	result.Assert(c, icmd.Success)
-	bazNetwork := strings.TrimSpace(string(result.Combined()))
+	bazNetwork := strings.TrimSpace(result.Combined())
 
 	// Create a service
 	name := "top"
@@ -924,38 +916,36 @@ func (s *DockerSwarmSuite) TestSwarmServiceNetworkUpdate(c *check.C) {
 	result.Assert(c, icmd.Success)
 
 	// Make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskNetworks, checker.DeepEquals,
-		map[string]int{fooNetwork: 1, barNetwork: 1})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskNetworks, checker.DeepEquals(map[string]int{fooNetwork: 1, barNetwork: 1})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// Remove a network
 	result = icmd.RunCmd(d.Command("service", "update", "--detach", "--network-rm", "foo", name))
 	result.Assert(c, icmd.Success)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskNetworks, checker.DeepEquals,
-		map[string]int{barNetwork: 1})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskNetworks, checker.DeepEquals(map[string]int{barNetwork: 1})), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// Add a network
 	result = icmd.RunCmd(d.Command("service", "update", "--detach", "--network-add", "baz", name))
 	result.Assert(c, icmd.Success)
 
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskNetworks, checker.DeepEquals,
-		map[string]int{barNetwork: 1, bazNetwork: 1})
+	poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskNetworks, checker.DeepEquals(map[string]int{barNetwork: 1, bazNetwork: 1})), poll.WithTimeout(defaultReconciliationTimeout))
+
 }
 
-func (s *DockerSwarmSuite) TestDNSConfig(c *check.C) {
+func (s *DockerSwarmSuite) TestDNSConfig(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a service
 	name := "top"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--dns=1.2.3.4", "--dns-search=example.com", "--dns-option=timeout:3", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// We need to get the container id.
 	out, err = d.Cmd("ps", "-a", "-q", "--no-trunc")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	// Compare against expected output.
@@ -963,69 +953,69 @@ func (s *DockerSwarmSuite) TestDNSConfig(c *check.C) {
 	expectedOutput2 := "search example.com"
 	expectedOutput3 := "options timeout:3"
 	out, err = d.Cmd("exec", id, "cat", "/etc/resolv.conf")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, expectedOutput1, check.Commentf("Expected '%s', but got %q", expectedOutput1, out))
-	c.Assert(out, checker.Contains, expectedOutput2, check.Commentf("Expected '%s', but got %q", expectedOutput2, out))
-	c.Assert(out, checker.Contains, expectedOutput3, check.Commentf("Expected '%s', but got %q", expectedOutput3, out))
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput1), "Expected '%s', but got %q", expectedOutput1, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput2), "Expected '%s', but got %q", expectedOutput2, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput3), "Expected '%s', but got %q", expectedOutput3, out)
 }
 
-func (s *DockerSwarmSuite) TestDNSConfigUpdate(c *check.C) {
+func (s *DockerSwarmSuite) TestDNSConfigUpdate(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a service
 	name := "top"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("service", "update", "--detach", "--dns-add=1.2.3.4", "--dns-search-add=example.com", "--dns-option-add=timeout:3", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.DNSConfig }}", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "{[1.2.3.4] [example.com] [timeout:3]}")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "{[1.2.3.4] [example.com] [timeout:3]}")
 }
 
-func getNodeStatus(c *check.C, d *daemon.Daemon) swarm.LocalNodeState {
+func getNodeStatus(c *testing.T, d *daemon.Daemon) swarm.LocalNodeState {
 	info := d.SwarmInfo(c)
 	return info.LocalNodeState
 }
 
-func checkKeyIsEncrypted(d *daemon.Daemon) func(*check.C) (interface{}, check.CommentInterface) {
-	return func(c *check.C) (interface{}, check.CommentInterface) {
-		keyBytes, err := ioutil.ReadFile(filepath.Join(d.Folder, "root", "swarm", "certificates", "swarm-node.key"))
+func checkKeyIsEncrypted(d *daemon.Daemon) func(*testing.T) (interface{}, string) {
+	return func(c *testing.T) (interface{}, string) {
+		keyBytes, err := os.ReadFile(filepath.Join(d.Folder, "root", "swarm", "certificates", "swarm-node.key"))
 		if err != nil {
-			return fmt.Errorf("error reading key: %v", err), nil
+			return fmt.Errorf("error reading key: %v", err), ""
 		}
 
 		keyBlock, _ := pem.Decode(keyBytes)
 		if keyBlock == nil {
-			return fmt.Errorf("invalid PEM-encoded private key"), nil
+			return fmt.Errorf("invalid PEM-encoded private key"), ""
 		}
 
-		return keyutils.IsEncryptedPEMBlock(keyBlock), nil
+		return keyutils.IsEncryptedPEMBlock(keyBlock), ""
 	}
 }
 
-func checkSwarmLockedToUnlocked(c *check.C, d *daemon.Daemon, unlockKey string) {
+func checkSwarmLockedToUnlocked(c *testing.T, d *daemon.Daemon) {
 	// Wait for the PEM file to become unencrypted
-	waitAndAssert(c, defaultReconciliationTimeout, checkKeyIsEncrypted(d), checker.Equals, false)
+	poll.WaitOn(c, pollCheck(c, checkKeyIsEncrypted(d), checker.Equals(false)), poll.WithTimeout(defaultReconciliationTimeout))
 
-	d.Restart(c)
-	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
+	d.RestartNode(c)
+	poll.WaitOn(c, pollCheck(c, d.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStateActive)), poll.WithTimeout(time.Second))
 }
 
-func checkSwarmUnlockedToLocked(c *check.C, d *daemon.Daemon) {
+func checkSwarmUnlockedToLocked(c *testing.T, d *daemon.Daemon) {
 	// Wait for the PEM file to become encrypted
-	waitAndAssert(c, defaultReconciliationTimeout, checkKeyIsEncrypted(d), checker.Equals, true)
+	poll.WaitOn(c, pollCheck(c, checkKeyIsEncrypted(d), checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout))
 
-	d.Restart(c)
-	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
+	d.RestartNode(c)
+	poll.WaitOn(c, pollCheck(c, d.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStateLocked)), poll.WithTimeout(time.Second))
 }
 
-func (s *DockerSwarmSuite) TestUnlockEngineAndUnlockedSwarm(c *check.C) {
+func (s *DockerSwarmSuite) TestUnlockEngineAndUnlockedSwarm(c *testing.T) {
 	d := s.AddDaemon(c, false, false)
 
 	// unlocking a normal engine should return an error - it does not even ask for the key
@@ -1034,11 +1024,11 @@ func (s *DockerSwarmSuite) TestUnlockEngineAndUnlockedSwarm(c *check.C) {
 	result.Assert(c, icmd.Expected{
 		ExitCode: 1,
 	})
-	c.Assert(result.Combined(), checker.Contains, "Error: This node is not part of a swarm")
-	c.Assert(result.Combined(), checker.Not(checker.Contains), "Please enter unlock key")
-
+	out := result.Combined()
+	assert.Assert(c, strings.Contains(result.Combined(), "Error: This node is not part of a swarm"), out)
+	assert.Assert(c, !strings.Contains(result.Combined(), "Please enter unlock key"), out)
 	out, err := d.Cmd("swarm", "init")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// unlocking an unlocked swarm should return an error - it does not even ask for the key
 	cmd = d.Command("swarm", "unlock")
@@ -1046,37 +1036,23 @@ func (s *DockerSwarmSuite) TestUnlockEngineAndUnlockedSwarm(c *check.C) {
 	result.Assert(c, icmd.Expected{
 		ExitCode: 1,
 	})
-	c.Assert(result.Combined(), checker.Contains, "Error: swarm is not locked")
-	c.Assert(result.Combined(), checker.Not(checker.Contains), "Please enter unlock key")
+	out = result.Combined()
+	assert.Assert(c, strings.Contains(result.Combined(), "Error: swarm is not locked"), out)
+	assert.Assert(c, !strings.Contains(result.Combined(), "Please enter unlock key"), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmInitLocked(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmInitLocked(c *testing.T) {
 	d := s.AddDaemon(c, false, false)
 
 	outs, err := d.Cmd("swarm", "init", "--autolock")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
-
-	c.Assert(outs, checker.Contains, "docker swarm unlock")
-
-	var unlockKey string
-	for _, line := range strings.Split(outs, "\n") {
-		if strings.Contains(line, "SWMKEY") {
-			unlockKey = strings.TrimSpace(line)
-			break
-		}
-	}
-
-	c.Assert(unlockKey, checker.Not(checker.Equals), "")
-
-	outs, err = d.Cmd("swarm", "unlock-key", "-q")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
-	c.Assert(outs, checker.Equals, unlockKey+"\n")
+	assert.Assert(c, err == nil, outs)
+	unlockKey := getUnlockKey(d, c, outs)
 
-	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive)
 
 	// It starts off locked
-	d.Restart(c)
-	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
+	d.RestartNode(c)
+	assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked)
 
 	cmd := d.Command("swarm", "unlock")
 	cmd.Stdin = bytes.NewBufferString("wrong-secret-key")
@@ -1085,92 +1061,74 @@ func (s *DockerSwarmSuite) TestSwarmInitLocked(c *check.C) {
 		Err:      "invalid key",
 	})
 
-	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
+	assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked)
 
 	cmd = d.Command("swarm", "unlock")
 	cmd.Stdin = bytes.NewBufferString(unlockKey)
 	icmd.RunCmd(cmd).Assert(c, icmd.Success)
 
-	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive)
 
 	outs, err = d.Cmd("node", "ls")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
-	c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
-
+	assert.Assert(c, err == nil, outs)
+	assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs)
 	outs, err = d.Cmd("swarm", "update", "--autolock=false")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
+	assert.Assert(c, err == nil, outs)
 
-	checkSwarmLockedToUnlocked(c, d, unlockKey)
+	checkSwarmLockedToUnlocked(c, d)
 
 	outs, err = d.Cmd("node", "ls")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
-	c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
+	assert.Assert(c, err == nil, outs)
+	assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs)
 }
 
-func (s *DockerSwarmSuite) TestSwarmLeaveLocked(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmLeaveLocked(c *testing.T) {
 	d := s.AddDaemon(c, false, false)
 
 	outs, err := d.Cmd("swarm", "init", "--autolock")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
+	assert.Assert(c, err == nil, outs)
 
 	// It starts off locked
-	d.Restart(c, "--swarm-default-advertise-addr=lo")
+	d.RestartNode(c)
 
 	info := d.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateLocked)
 
 	outs, _ = d.Cmd("node", "ls")
-	c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
-
+	assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs)
 	// `docker swarm leave` a locked swarm without --force will return an error
 	outs, _ = d.Cmd("swarm", "leave")
-	c.Assert(outs, checker.Contains, "Swarm is encrypted and locked.")
-
+	assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and locked."), outs)
 	// It is OK for user to leave a locked swarm with --force
 	outs, err = d.Cmd("swarm", "leave", "--force")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
+	assert.Assert(c, err == nil, outs)
 
 	info = d.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive)
 
 	outs, err = d.Cmd("swarm", "init")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
+	assert.Assert(c, err == nil, outs)
 
 	info = d.SwarmInfo(c)
-	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive)
 }
 
-func (s *DockerSwarmSuite) TestSwarmLockUnlockCluster(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmLockUnlockCluster(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, true)
 	d3 := s.AddDaemon(c, true, true)
 
 	// they start off unlocked
-	d2.Restart(c)
-	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
+	d2.RestartNode(c)
+	assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateActive)
 
 	// stop this one so it does not get autolock info
 	d2.Stop(c)
 
 	// enable autolock
 	outs, err := d1.Cmd("swarm", "update", "--autolock")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
-
-	c.Assert(outs, checker.Contains, "docker swarm unlock")
-
-	var unlockKey string
-	for _, line := range strings.Split(outs, "\n") {
-		if strings.Contains(line, "SWMKEY") {
-			unlockKey = strings.TrimSpace(line)
-			break
-		}
-	}
-
-	c.Assert(unlockKey, checker.Not(checker.Equals), "")
-
-	outs, err = d1.Cmd("swarm", "unlock-key", "-q")
-	c.Assert(err, checker.IsNil)
-	c.Assert(outs, checker.Equals, unlockKey+"\n")
+	assert.Assert(c, err == nil, outs)
+	unlockKey := getUnlockKey(d1, c, outs)
 
 	// The ones that got the cluster update should be set to locked
 	for _, d := range []*daemon.Daemon{d1, d3} {
@@ -1179,76 +1137,60 @@ func (s *DockerSwarmSuite) TestSwarmLockUnlockCluster(c *check.C) {
 		cmd := d.Command("swarm", "unlock")
 		cmd.Stdin = bytes.NewBufferString(unlockKey)
 		icmd.RunCmd(cmd).Assert(c, icmd.Success)
-		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
+		assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive)
 	}
 
 	// d2 never got the cluster update, so it is still set to unlocked
-	d2.Start(c)
-	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
+	d2.StartNode(c)
+	assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateActive)
 
 	// d2 is now set to lock
 	checkSwarmUnlockedToLocked(c, d2)
 
 	// leave it locked, and set the cluster to no longer autolock
 	outs, err = d1.Cmd("swarm", "update", "--autolock=false")
-	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
+	assert.Assert(c, err == nil, "out: %v", outs)
 
 	// the ones that got the update are now set to unlocked
 	for _, d := range []*daemon.Daemon{d1, d3} {
-		checkSwarmLockedToUnlocked(c, d, unlockKey)
+		checkSwarmLockedToUnlocked(c, d)
 	}
 
 	// d2 still locked
-	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateLocked)
+	assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateLocked)
 
 	// unlock it
 	cmd := d2.Command("swarm", "unlock")
 	cmd.Stdin = bytes.NewBufferString(unlockKey)
 	icmd.RunCmd(cmd).Assert(c, icmd.Success)
-	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
+	assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateActive)
 
 	// once it's caught up, d2 is set to not be locked
-	checkSwarmLockedToUnlocked(c, d2, unlockKey)
+	checkSwarmLockedToUnlocked(c, d2)
 
 	// managers who join now are never set to locked in the first place
 	d4 := s.AddDaemon(c, true, true)
-	d4.Restart(c)
-	c.Assert(getNodeStatus(c, d4), checker.Equals, swarm.LocalNodeStateActive)
+	d4.RestartNode(c)
+	assert.Equal(c, getNodeStatus(c, d4), swarm.LocalNodeStateActive)
 }
 
-func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 
 	// enable autolock
 	outs, err := d1.Cmd("swarm", "update", "--autolock")
-	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
-
-	c.Assert(outs, checker.Contains, "docker swarm unlock")
-
-	var unlockKey string
-	for _, line := range strings.Split(outs, "\n") {
-		if strings.Contains(line, "SWMKEY") {
-			unlockKey = strings.TrimSpace(line)
-			break
-		}
-	}
-
-	c.Assert(unlockKey, checker.Not(checker.Equals), "")
-
-	outs, err = d1.Cmd("swarm", "unlock-key", "-q")
-	c.Assert(err, checker.IsNil)
-	c.Assert(outs, checker.Equals, unlockKey+"\n")
+	assert.Assert(c, err == nil, "out: %v", outs)
+	unlockKey := getUnlockKey(d1, c, outs)
 
 	// joined workers start off unlocked
 	d2 := s.AddDaemon(c, true, false)
-	d2.Restart(c)
-	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
+	d2.RestartNode(c)
+	poll.WaitOn(c, pollCheck(c, d2.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStateActive)), poll.WithTimeout(time.Second))
 
 	// promote worker
 	outs, err = d1.Cmd("node", "promote", d2.NodeID())
-	c.Assert(err, checker.IsNil)
-	c.Assert(outs, checker.Contains, "promoted to a manager in the swarm")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(outs, "promoted to a manager in the swarm"), outs)
 	// join new manager node
 	d3 := s.AddDaemon(c, true, true)
 
@@ -1259,74 +1201,57 @@ func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *check.C) {
 		cmd := d.Command("swarm", "unlock")
 		cmd.Stdin = bytes.NewBufferString(unlockKey)
 		icmd.RunCmd(cmd).Assert(c, icmd.Success)
-		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
+		assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive)
 	}
 
 	// demote manager back to worker - workers are not locked
 	outs, err = d1.Cmd("node", "demote", d3.NodeID())
-	c.Assert(err, checker.IsNil)
-	c.Assert(outs, checker.Contains, "demoted in the swarm")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(outs, "demoted in the swarm"), outs)
 	// Wait for it to actually be demoted, for the key and cert to be replaced.
 	// Then restart and assert that the node is not locked.  If we don't wait for the cert
 	// to be replaced, then the node still has the manager TLS key which is still locked
 	// (because we never want a manager TLS key to be on disk unencrypted if the cluster
 	// is set to autolock)
-	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckControlAvailable, checker.False)
-	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
-		certBytes, err := ioutil.ReadFile(filepath.Join(d3.Folder, "root", "swarm", "certificates", "swarm-node.crt"))
+	poll.WaitOn(c, pollCheck(c, d3.CheckControlAvailable, checker.False()), poll.WithTimeout(defaultReconciliationTimeout))
+	poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) {
+		certBytes, err := os.ReadFile(filepath.Join(d3.Folder, "root", "swarm", "certificates", "swarm-node.crt"))
 		if err != nil {
-			return "", check.Commentf("error: %v", err)
+			return "", fmt.Sprintf("error: %v", err)
 		}
 		certs, err := helpers.ParseCertificatesPEM(certBytes)
 		if err == nil && len(certs) > 0 && len(certs[0].Subject.OrganizationalUnit) > 0 {
-			return certs[0].Subject.OrganizationalUnit[0], nil
+			return certs[0].Subject.OrganizationalUnit[0], ""
 		}
-		return "", check.Commentf("could not get organizational unit from certificate")
-	}, checker.Equals, "swarm-worker")
+		return "", "could not get organizational unit from certificate"
+	}, checker.Equals("swarm-worker")), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// by now, it should *never* be locked on restart
-	d3.Restart(c)
-	c.Assert(getNodeStatus(c, d3), checker.Equals, swarm.LocalNodeStateActive)
+	d3.RestartNode(c)
+	poll.WaitOn(c, pollCheck(c, d3.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStateActive)), poll.WithTimeout(time.Second))
 }
 
-func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	outs, err := d.Cmd("swarm", "update", "--autolock")
-	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
-
-	c.Assert(outs, checker.Contains, "docker swarm unlock")
-
-	var unlockKey string
-	for _, line := range strings.Split(outs, "\n") {
-		if strings.Contains(line, "SWMKEY") {
-			unlockKey = strings.TrimSpace(line)
-			break
-		}
-	}
-
-	c.Assert(unlockKey, checker.Not(checker.Equals), "")
-
-	outs, err = d.Cmd("swarm", "unlock-key", "-q")
-	c.Assert(err, checker.IsNil)
-	c.Assert(outs, checker.Equals, unlockKey+"\n")
+	assert.Assert(c, err == nil, "out: %v", outs)
+	unlockKey := getUnlockKey(d, c, outs)
 
 	// Rotate multiple times
 	for i := 0; i != 3; i++ {
 		outs, err = d.Cmd("swarm", "unlock-key", "-q", "--rotate")
-		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
+		assert.Assert(c, err == nil, "out: %v", outs)
 		// Strip \n
 		newUnlockKey := outs[:len(outs)-1]
-		c.Assert(newUnlockKey, checker.Not(checker.Equals), "")
-		c.Assert(newUnlockKey, checker.Not(checker.Equals), unlockKey)
+		assert.Assert(c, newUnlockKey != "")
+		assert.Assert(c, newUnlockKey != unlockKey)
 
-		d.Restart(c)
-		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
+		d.RestartNode(c)
+		assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked)
 
 		outs, _ = d.Cmd("node", "ls")
-		c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
-
+		assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs)
 		cmd := d.Command("swarm", "unlock")
 		cmd.Stdin = bytes.NewBufferString(unlockKey)
 		result := icmd.RunCmd(cmd)
@@ -1342,7 +1267,7 @@ func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) {
 
 			time.Sleep(3 * time.Second)
 
-			d.Restart(c)
+			d.RestartNode(c)
 
 			cmd = d.Command("swarm", "unlock")
 			cmd.Stdin = bytes.NewBufferString(unlockKey)
@@ -1354,17 +1279,28 @@ func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) {
 		})
 
 		outs, _ = d.Cmd("node", "ls")
-		c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
-
+		assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs)
 		cmd = d.Command("swarm", "unlock")
 		cmd.Stdin = bytes.NewBufferString(newUnlockKey)
 		icmd.RunCmd(cmd).Assert(c, icmd.Success)
 
-		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
+		assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive)
 
-		outs, err = d.Cmd("node", "ls")
-		c.Assert(err, checker.IsNil)
-		c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
+		retry := 0
+		for {
+			// an issue sometimes prevents leader to be available right away
+			outs, err = d.Cmd("node", "ls")
+			if err != nil && retry < 5 {
+				if strings.Contains(outs, "swarm does not have a leader") {
+					retry++
+					time.Sleep(3 * time.Second)
+					continue
+				}
+			}
+			assert.NilError(c, err)
+			assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs)
+			break
+		}
 
 		unlockKey = newUnlockKey
 	}
@@ -1373,48 +1309,39 @@ func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) {
 // This differs from `TestSwarmRotateUnlockKey` because that one rotates a single node, which is the leader.
 // This one keeps the leader up, and asserts that other manager nodes in the cluster also have their unlock
 // key rotated.
-func (s *DockerSwarmSuite) TestSwarmClusterRotateUnlockKey(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmClusterRotateUnlockKey(c *testing.T) {
+	if runtime.GOARCH == "s390x" {
+		c.Skip("Disabled on s390x")
+	}
+	if runtime.GOARCH == "ppc64le" {
+		c.Skip("Disabled on  ppc64le")
+	}
+
 	d1 := s.AddDaemon(c, true, true) // leader - don't restart this one, we don't want leader election delays
 	d2 := s.AddDaemon(c, true, true)
 	d3 := s.AddDaemon(c, true, true)
 
 	outs, err := d1.Cmd("swarm", "update", "--autolock")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
-
-	c.Assert(outs, checker.Contains, "docker swarm unlock")
-
-	var unlockKey string
-	for _, line := range strings.Split(outs, "\n") {
-		if strings.Contains(line, "SWMKEY") {
-			unlockKey = strings.TrimSpace(line)
-			break
-		}
-	}
-
-	c.Assert(unlockKey, checker.Not(checker.Equals), "")
-
-	outs, err = d1.Cmd("swarm", "unlock-key", "-q")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
-	c.Assert(outs, checker.Equals, unlockKey+"\n")
+	assert.Assert(c, err == nil, outs)
+	unlockKey := getUnlockKey(d1, c, outs)
 
 	// Rotate multiple times
 	for i := 0; i != 3; i++ {
 		outs, err = d1.Cmd("swarm", "unlock-key", "-q", "--rotate")
-		c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
+		assert.Assert(c, err == nil, outs)
 		// Strip \n
 		newUnlockKey := outs[:len(outs)-1]
-		c.Assert(newUnlockKey, checker.Not(checker.Equals), "")
-		c.Assert(newUnlockKey, checker.Not(checker.Equals), unlockKey)
+		assert.Assert(c, newUnlockKey != "")
+		assert.Assert(c, newUnlockKey != unlockKey)
 
-		d2.Restart(c)
-		d3.Restart(c)
+		d2.RestartNode(c)
+		d3.RestartNode(c)
 
 		for _, d := range []*daemon.Daemon{d2, d3} {
-			c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
+			assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked)
 
 			outs, _ := d.Cmd("node", "ls")
-			c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
-
+			assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs)
 			cmd := d.Command("swarm", "unlock")
 			cmd.Stdin = bytes.NewBufferString(unlockKey)
 			result := icmd.RunCmd(cmd)
@@ -1430,7 +1357,7 @@ func (s *DockerSwarmSuite) TestSwarmClusterRotateUnlockKey(c *check.C) {
 
 				time.Sleep(3 * time.Second)
 
-				d.Restart(c)
+				d.RestartNode(c)
 
 				cmd = d.Command("swarm", "unlock")
 				cmd.Stdin = bytes.NewBufferString(unlockKey)
@@ -1442,141 +1369,147 @@ func (s *DockerSwarmSuite) TestSwarmClusterRotateUnlockKey(c *check.C) {
 			})
 
 			outs, _ = d.Cmd("node", "ls")
-			c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
-
+			assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs)
 			cmd = d.Command("swarm", "unlock")
 			cmd.Stdin = bytes.NewBufferString(newUnlockKey)
 			icmd.RunCmd(cmd).Assert(c, icmd.Success)
 
-			c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
-
-			outs, err = d.Cmd("node", "ls")
-			c.Assert(err, checker.IsNil, check.Commentf("%s", outs))
-			c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
+			assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive)
+
+			retry := 0
+			for {
+				// an issue sometimes prevents leader to be available right away
+				outs, err = d.Cmd("node", "ls")
+				if err != nil && retry < 5 {
+					if strings.Contains(outs, "swarm does not have a leader") {
+						retry++
+						c.Logf("[%s] got 'swarm does not have a leader'. retrying (attempt %d/5)", d.ID(), retry)
+						time.Sleep(3 * time.Second)
+						continue
+					} else {
+						c.Logf("[%s] gave error: '%v'. retrying (attempt %d/5): %s", d.ID(), err, retry, outs)
+					}
+				}
+				assert.NilError(c, err, "[%s] failed after %d retries: %v (%s)", d.ID(), retry, err, outs)
+				assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs)
+				break
+			}
 		}
 
 		unlockKey = newUnlockKey
 	}
 }
 
-func (s *DockerSwarmSuite) TestSwarmAlternateLockUnlock(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmAlternateLockUnlock(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
-	var unlockKey string
 	for i := 0; i < 2; i++ {
 		// set to lock
 		outs, err := d.Cmd("swarm", "update", "--autolock")
-		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
-		c.Assert(outs, checker.Contains, "docker swarm unlock")
-
-		for _, line := range strings.Split(outs, "\n") {
-			if strings.Contains(line, "SWMKEY") {
-				unlockKey = strings.TrimSpace(line)
-				break
-			}
-		}
+		assert.Assert(c, err == nil, "out: %v", outs)
+		assert.Assert(c, strings.Contains(outs, "docker swarm unlock"), outs)
+		unlockKey := getUnlockKey(d, c, outs)
 
-		c.Assert(unlockKey, checker.Not(checker.Equals), "")
 		checkSwarmUnlockedToLocked(c, d)
 
 		cmd := d.Command("swarm", "unlock")
 		cmd.Stdin = bytes.NewBufferString(unlockKey)
 		icmd.RunCmd(cmd).Assert(c, icmd.Success)
 
-		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
+		assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive)
 
 		outs, err = d.Cmd("swarm", "update", "--autolock=false")
-		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
+		assert.Assert(c, err == nil, "out: %v", outs)
 
-		checkSwarmLockedToUnlocked(c, d, unlockKey)
+		checkSwarmLockedToUnlocked(c, d)
 	}
 }
 
-func (s *DockerSwarmSuite) TestExtraHosts(c *check.C) {
+func (s *DockerSwarmSuite) TestExtraHosts(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a service
 	name := "top"
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--host=example.com:1.2.3.4", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// We need to get the container id.
 	out, err = d.Cmd("ps", "-a", "-q", "--no-trunc")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	// Compare against expected output.
 	expectedOutput := "1.2.3.4\texample.com"
 	out, err = d.Cmd("exec", id, "cat", "/etc/hosts")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, false)
 	d3 := s.AddDaemon(c, true, false)
 
 	// Manager Addresses will always show Node 1's address
-	expectedOutput := fmt.Sprintf("Manager Addresses:\n  127.0.0.1:%d\n", d1.SwarmPort)
+	expectedOutput := fmt.Sprintf("127.0.0.1:%d", d1.SwarmPort)
 
-	out, err := d1.Cmd("info")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, expectedOutput)
+	out, err := d1.Cmd("info", "--format", "{{ (index .Swarm.RemoteManagers 0).Addr }}")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput), out)
 
-	out, err = d2.Cmd("info")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, expectedOutput)
+	out, err = d2.Cmd("info", "--format", "{{ (index .Swarm.RemoteManagers 0).Addr }}")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput), out)
 
-	out, err = d3.Cmd("info")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, expectedOutput)
+	out, err = d3.Cmd("info", "--format", "{{ (index .Swarm.RemoteManagers 0).Addr }}")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, expectedOutput), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmNetworkIPAMOptions(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmNetworkIPAMOptions(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "-d", "overlay", "--ipam-opt", "foo=bar", "foo")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Contains, "foo:bar")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "com.docker.network.ipam.serial:true")
-
+	out = strings.TrimSpace(out)
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, "foo:bar"), out)
+	assert.Assert(c, strings.Contains(out, "com.docker.network.ipam.serial:true"), out)
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--network=foo", "--name", "top", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Contains, "foo:bar")
-	c.Assert(strings.TrimSpace(out), checker.Contains, "com.docker.network.ipam.serial:true")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, "foo:bar"), out)
+	assert.Assert(c, strings.Contains(out, "com.docker.network.ipam.serial:true"), out)
 }
 
 // Test case for issue #27866, which did not allow NW name that is the prefix of a swarm NW ID.
 // e.g. if the ingress ID starts with "n1", it was impossible to create a NW named "n1".
-func (s *DockerSwarmSuite) TestSwarmNetworkCreateIssue27866(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmNetworkCreateIssue27866(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 	out, err := d.Cmd("network", "inspect", "-f", "{{.Id}}", "ingress")
-	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
+	assert.NilError(c, err, "out: %v", out)
 	ingressID := strings.TrimSpace(out)
-	c.Assert(ingressID, checker.Not(checker.Equals), "")
+	assert.Assert(c, ingressID != "")
 
 	// create a network of which name is the prefix of the ID of an overlay network
 	// (ingressID in this case)
 	newNetName := ingressID[0:2]
 	out, err = d.Cmd("network", "create", "--driver", "overlay", newNetName)
 	// In #27866, it was failing because of "network with name %s already exists"
-	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
+	assert.NilError(c, err, "out: %v", out)
 	out, err = d.Cmd("network", "rm", newNetName)
-	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
+	assert.NilError(c, err, "out: %v", out)
 }
 
 // Test case for https://github.com/docker/docker/pull/27938#issuecomment-265768303
@@ -1585,108 +1518,103 @@ func (s *DockerSwarmSuite) TestSwarmNetworkCreateIssue27866(c *check.C) {
 // "network with name FOO already exists".
 // Note that it is to ok have multiple networks with the same name if the operations are done
 // in parallel. (#18864)
-func (s *DockerSwarmSuite) TestSwarmNetworkCreateDup(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmNetworkCreateDup(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 	drivers := []string{"bridge", "overlay"}
 	for i, driver1 := range drivers {
-		nwName := fmt.Sprintf("network-test-%d", i)
 		for _, driver2 := range drivers {
-			c.Logf("Creating a network named %q with %q, then %q",
-				nwName, driver1, driver2)
-			out, err := d.Cmd("network", "create", "--driver", driver1, nwName)
-			c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
-			out, err = d.Cmd("network", "create", "--driver", driver2, nwName)
-			c.Assert(out, checker.Contains,
-				fmt.Sprintf("network with name %s already exists", nwName))
-			c.Assert(err, checker.NotNil)
-			c.Logf("As expected, the attempt to network %q with %q failed: %s",
-				nwName, driver2, out)
-			out, err = d.Cmd("network", "rm", nwName)
-			c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
+			c.Run(fmt.Sprintf("driver %s then %s", driver1, driver2), func(c *testing.T) {
+				nwName := fmt.Sprintf("network-test-%d", i)
+				out, err := d.Cmd("network", "create", "--driver", driver1, nwName)
+				assert.NilError(c, err, "out: %v", out)
+				out, err = d.Cmd("network", "create", "--driver", driver2, nwName)
+				assert.Assert(c, strings.Contains(out, fmt.Sprintf("network with name %s already exists", nwName)), out)
+				assert.ErrorContains(c, err, "")
+				out, err = d.Cmd("network", "rm", nwName)
+				assert.NilError(c, err, "out: %v", out)
+			})
 		}
 	}
 }
 
-func (s *DockerSwarmSuite) TestSwarmPublishDuplicatePorts(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmPublishDuplicatePorts(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--publish", "5005:80", "--publish", "5006:80", "--publish", "80", "--publish", "80", "busybox", "top")
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	id := strings.TrimSpace(out)
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// Total len = 4, with 2 dynamic ports and 2 non-dynamic ports
 	// Dynamic ports are likely to be 30000 and 30001 but doesn't matter
 	out, err = d.Cmd("service", "inspect", "--format", "{{.Endpoint.Ports}} len={{len .Endpoint.Ports}}", id)
-	c.Assert(err, check.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "len=4")
-	c.Assert(out, checker.Contains, "{ tcp 80 5005 ingress}")
-	c.Assert(out, checker.Contains, "{ tcp 80 5006 ingress}")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, "len=4"), out)
+	assert.Assert(c, strings.Contains(out, "{ tcp 80 5005 ingress}"), out)
+	assert.Assert(c, strings.Contains(out, "{ tcp 80 5006 ingress}"), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmJoinWithDrain(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmJoinWithDrain(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("node", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Not(checker.Contains), "Drain")
-
+	assert.NilError(c, err)
+	assert.Assert(c, !strings.Contains(out, "Drain"), out)
 	out, err = d.Cmd("swarm", "join-token", "-q", "manager")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	token := strings.TrimSpace(out)
 
 	d1 := s.AddDaemon(c, false, false)
 
 	out, err = d1.Cmd("swarm", "join", "--availability=drain", "--token", token, d.SwarmListenAddr())
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	out, err = d.Cmd("node", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "Drain")
-
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, "Drain"), out)
 	out, err = d1.Cmd("node", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "Drain")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, "Drain"), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmInitWithDrain(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmInitWithDrain(c *testing.T) {
 	d := s.AddDaemon(c, false, false)
 
 	out, err := d.Cmd("swarm", "init", "--availability", "drain")
-	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
+	assert.NilError(c, err, "out: %v", out)
 
 	out, err = d.Cmd("node", "ls")
-	c.Assert(err, checker.IsNil)
-	c.Assert(out, checker.Contains, "Drain")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.Contains(out, "Drain"))
 }
 
-func (s *DockerSwarmSuite) TestSwarmReadonlyRootfs(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmReadonlyRootfs(c *testing.T) {
 	testRequires(c, DaemonIsLinux, UserNamespaceROMount)
 
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top", "--read-only", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.ReadOnly }}", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "true")
 
 	containers := d.ActiveContainers(c)
 	out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.HostConfig.ReadonlyRootfs}}", containers[0])
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "true")
 }
 
-func (s *DockerSwarmSuite) TestNetworkInspectWithDuplicateNames(c *check.C) {
+func (s *DockerSwarmSuite) TestNetworkInspectWithDuplicateNames(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "foo"
@@ -1695,140 +1623,136 @@ func (s *DockerSwarmSuite) TestNetworkInspectWithDuplicateNames(c *check.C) {
 		Driver:         "bridge",
 	}
 
-	cli, err := d.NewClient()
-	c.Assert(err, checker.IsNil)
+	cli := d.NewClientT(c)
 	defer cli.Close()
 
 	n1, err := cli.NetworkCreate(context.Background(), name, options)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// Full ID always works
 	out, err := d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, n1.ID)
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), n1.ID)
 
 	// Name works if it is unique
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, n1.ID)
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), n1.ID)
 
 	n2, err := cli.NetworkCreate(context.Background(), name, options)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	// Full ID always works
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, n1.ID)
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), n1.ID)
 
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n2.ID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, n2.ID)
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), n2.ID)
 
 	// Name with duplicates
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name)
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "2 matches found based on name")
-
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "2 matches found based on name"), out)
 	out, err = d.Cmd("network", "rm", n2.ID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Duplicates with name but with different driver
 	options.Driver = "overlay"
 
 	n2, err = cli.NetworkCreate(context.Background(), name, options)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// Full ID always works
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, n1.ID)
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), n1.ID)
 
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n2.ID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, n2.ID)
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), n2.ID)
 
 	// Name with duplicates
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name)
-	c.Assert(err, checker.NotNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "2 matches found based on name")
+	assert.ErrorContains(c, err, "", out)
+	assert.Assert(c, strings.Contains(out, "2 matches found based on name"), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmStopSignal(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmStopSignal(c *testing.T) {
 	testRequires(c, DaemonIsLinux, UserNamespaceROMount)
 
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top", "--stop-signal=SIGHUP", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.StopSignal }}", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "SIGHUP")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "SIGHUP")
 
 	containers := d.ActiveContainers(c)
 	out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.StopSignal}}", containers[0])
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "SIGHUP")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "SIGHUP")
 
 	out, err = d.Cmd("service", "update", "--detach", "--stop-signal=SIGUSR1", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.StopSignal }}", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "SIGUSR1")
+	assert.NilError(c, err, out)
+	assert.Equal(c, strings.TrimSpace(out), "SIGUSR1")
 }
 
-func (s *DockerSwarmSuite) TestSwarmServiceLsFilterMode(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmServiceLsFilterMode(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top1", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top2", "--mode=global", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 2)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(2)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("service", "ls")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "top1")
-	c.Assert(out, checker.Contains, "top2")
-	c.Assert(out, checker.Not(checker.Contains), "localnet")
-
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, "top1"), out)
+	assert.Assert(c, strings.Contains(out, "top2"), out)
+	assert.Assert(c, !strings.Contains(out, "localnet"), out)
 	out, err = d.Cmd("service", "ls", "--filter", "mode=global")
-	c.Assert(out, checker.Not(checker.Contains), "top1")
-	c.Assert(out, checker.Contains, "top2")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.Assert(c, !strings.Contains(out, "top1"), out)
+	assert.Assert(c, strings.Contains(out, "top2"), out)
+	assert.NilError(c, err, out)
 
 	out, err = d.Cmd("service", "ls", "--filter", "mode=replicated")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
-	c.Assert(out, checker.Contains, "top1")
-	c.Assert(out, checker.Not(checker.Contains), "top2")
+	assert.NilError(c, err, out)
+	assert.Assert(c, strings.Contains(out, "top1"), out)
+	assert.Assert(c, !strings.Contains(out, "top2"), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedDataPathAddr(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedDataPathAddr(c *testing.T) {
 	d := s.AddDaemon(c, false, false)
 
 	out, err := d.Cmd("swarm", "init", "--data-path-addr", "0.0.0.0")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "data path address must be a non-zero IP")
-
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "data path address must be a non-zero IP"), out)
 	out, err = d.Cmd("swarm", "init", "--data-path-addr", "0.0.0.0:2000")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "data path address must be a non-zero IP")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "data path address must be a non-zero IP"), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmJoinLeave(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmJoinLeave(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("swarm", "join-token", "-q", "worker")
-	c.Assert(err, checker.IsNil)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.NilError(c, err)
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	token := strings.TrimSpace(out)
 
@@ -1836,17 +1760,17 @@ func (s *DockerSwarmSuite) TestSwarmJoinLeave(c *check.C) {
 	d1 := s.AddDaemon(c, false, false)
 	for i := 0; i < 10; i++ {
 		out, err = d1.Cmd("swarm", "join", "--token", token, d.SwarmListenAddr())
-		c.Assert(err, checker.IsNil)
-		c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+		assert.NilError(c, err)
+		assert.Assert(c, strings.TrimSpace(out) != "")
 
 		_, err = d1.Cmd("swarm", "leave")
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 	}
 }
 
 const defaultRetryCount = 10
 
-func waitForEvent(c *check.C, d *daemon.Daemon, since string, filter string, event string, retry int) string {
+func waitForEvent(c *testing.T, d *daemon.Daemon, since string, filter string, event string, retry int) string {
 	if retry < 1 {
 		c.Fatalf("retry count %d is invalid. It should be no less than 1", retry)
 		return ""
@@ -1860,7 +1784,7 @@ func waitForEvent(c *check.C, d *daemon.Daemon, since string, filter string, eve
 		} else {
 			out, err = d.Cmd("events", "--since", since, "--until", until)
 		}
-		c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+		assert.NilError(c, err, out)
 		if strings.Contains(out, event) {
 			return strings.TrimSpace(out)
 		}
@@ -1873,16 +1797,16 @@ func waitForEvent(c *check.C, d *daemon.Daemon, since string, filter string, eve
 	return ""
 }
 
-func (s *DockerSwarmSuite) TestSwarmClusterEventsSource(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmClusterEventsSource(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, true)
 	d3 := s.AddDaemon(c, true, false)
 
 	// create a network
 	out, err := d1.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	networkID := strings.TrimSpace(out)
-	c.Assert(networkID, checker.Not(checker.Equals), "")
+	assert.Assert(c, networkID != "")
 
 	// d1, d2 are managers that can get swarm events
 	waitForEvent(c, d1, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount)
@@ -1890,59 +1814,57 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsSource(c *check.C) {
 
 	// d3 is a worker, not able to get cluster events
 	out = waitForEvent(c, d3, "0", "-f scope=swarm", "", 1)
-	c.Assert(out, checker.Not(checker.Contains), "network create ")
+	assert.Assert(c, !strings.Contains(out, "network create "), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmClusterEventsScope(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmClusterEventsScope(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// create a service
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	serviceID := strings.Split(out, "\n")[0]
 
 	// scope swarm filters cluster events
 	out = waitForEvent(c, d, "0", "-f scope=swarm", "service create "+serviceID, defaultRetryCount)
-	c.Assert(out, checker.Not(checker.Contains), "container create ")
-
+	assert.Assert(c, !strings.Contains(out, "container create "), out)
 	// all events are returned if scope is not specified
 	waitForEvent(c, d, "0", "", "service create "+serviceID, 1)
 	waitForEvent(c, d, "0", "", "container create ", defaultRetryCount)
 
 	// scope local only shows non-cluster events
 	out = waitForEvent(c, d, "0", "-f scope=local", "container create ", 1)
-	c.Assert(out, checker.Not(checker.Contains), "service create ")
+	assert.Assert(c, !strings.Contains(out, "service create "), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmClusterEventsType(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmClusterEventsType(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// create a service
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	serviceID := strings.Split(out, "\n")[0]
 
 	// create a network
 	out, err = d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	networkID := strings.TrimSpace(out)
-	c.Assert(networkID, checker.Not(checker.Equals), "")
+	assert.Assert(c, networkID != "")
 
 	// filter by service
 	out = waitForEvent(c, d, "0", "-f type=service", "service create "+serviceID, defaultRetryCount)
-	c.Assert(out, checker.Not(checker.Contains), "network create")
-
+	assert.Assert(c, !strings.Contains(out, "network create"), out)
 	// filter by network
 	out = waitForEvent(c, d, "0", "-f type=network", "network create "+networkID, defaultRetryCount)
-	c.Assert(out, checker.Not(checker.Contains), "service create")
+	assert.Assert(c, !strings.Contains(out, "service create"), out)
 }
 
-func (s *DockerSwarmSuite) TestSwarmClusterEventsService(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmClusterEventsService(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// create a service
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	serviceID := strings.Split(out, "\n")[0]
 
 	// validate service create event
@@ -1950,34 +1872,31 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsService(c *check.C) {
 
 	t1 := daemonUnixTime(c)
 	out, err = d.Cmd("service", "update", "--force", "--detach=false", "test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// wait for service update start
 	out = waitForEvent(c, d, t1, "-f scope=swarm", "service update "+serviceID, defaultRetryCount)
-	c.Assert(out, checker.Contains, "updatestate.new=updating")
-
+	assert.Assert(c, strings.Contains(out, "updatestate.new=updating"), out)
 	// allow service update complete. This is a service with 1 instance
 	time.Sleep(400 * time.Millisecond)
 	out = waitForEvent(c, d, t1, "-f scope=swarm", "service update "+serviceID, defaultRetryCount)
-	c.Assert(out, checker.Contains, "updatestate.new=completed, updatestate.old=updating")
-
+	assert.Assert(c, strings.Contains(out, "updatestate.new=completed, updatestate.old=updating"), out)
 	// scale service
 	t2 := daemonUnixTime(c)
 	out, err = d.Cmd("service", "scale", "test=3")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	out = waitForEvent(c, d, t2, "-f scope=swarm", "service update "+serviceID, defaultRetryCount)
-	c.Assert(out, checker.Contains, "replicas.new=3, replicas.old=1")
-
+	assert.Assert(c, strings.Contains(out, "replicas.new=3, replicas.old=1"), out)
 	// remove service
 	t3 := daemonUnixTime(c)
 	out, err = d.Cmd("service", "rm", "test")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	waitForEvent(c, d, t3, "-f scope=swarm", "service remove "+serviceID, defaultRetryCount)
 }
 
-func (s *DockerSwarmSuite) TestSwarmClusterEventsNode(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmClusterEventsNode(c *testing.T) {
 	d1 := s.AddDaemon(c, true, true)
 	s.AddDaemon(c, true, true)
 	d3 := s.AddDaemon(c, true, true)
@@ -1987,32 +1906,31 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsNode(c *check.C) {
 
 	t1 := daemonUnixTime(c)
 	out, err := d1.Cmd("node", "update", "--availability=pause", d3ID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// filter by type
 	out = waitForEvent(c, d1, t1, "-f type=node", "node update "+d3ID, defaultRetryCount)
-	c.Assert(out, checker.Contains, "availability.new=pause, availability.old=active")
-
+	assert.Assert(c, strings.Contains(out, "availability.new=pause, availability.old=active"), out)
 	t2 := daemonUnixTime(c)
 	out, err = d1.Cmd("node", "demote", d3ID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	waitForEvent(c, d1, t2, "-f type=node", "node update "+d3ID, defaultRetryCount)
 
 	t3 := daemonUnixTime(c)
 	out, err = d1.Cmd("node", "rm", "-f", d3ID)
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// filter by scope
 	waitForEvent(c, d1, t3, "-f scope=swarm", "node remove "+d3ID, defaultRetryCount)
 }
 
-func (s *DockerSwarmSuite) TestSwarmClusterEventsNetwork(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmClusterEventsNetwork(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	// create a network
 	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 	networkID := strings.TrimSpace(out)
 
 	waitForEvent(c, d, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount)
@@ -2020,13 +1938,13 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsNetwork(c *check.C) {
 	// remove network
 	t1 := daemonUnixTime(c)
 	out, err = d.Cmd("network", "rm", "foo")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// filtered by network
 	waitForEvent(c, d, t1, "-f type=network", "network remove "+networkID, defaultRetryCount)
 }
 
-func (s *DockerSwarmSuite) TestSwarmClusterEventsSecret(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmClusterEventsSecret(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	testName := "test_secret"
@@ -2036,7 +1954,7 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsSecret(c *check.C) {
 		},
 		Data: []byte("TESTINGDATA"),
 	})
-	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
+	assert.Assert(c, id != "", "secrets: %s", id)
 
 	waitForEvent(c, d, "0", "-f scope=swarm", "secret create "+id, defaultRetryCount)
 
@@ -2046,7 +1964,7 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsSecret(c *check.C) {
 	waitForEvent(c, d, t1, "-f type=secret", "secret remove "+id, defaultRetryCount)
 }
 
-func (s *DockerSwarmSuite) TestSwarmClusterEventsConfig(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmClusterEventsConfig(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	testName := "test_config"
@@ -2056,7 +1974,7 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsConfig(c *check.C) {
 		},
 		Data: []byte("TESTINGDATA"),
 	})
-	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id))
+	assert.Assert(c, id != "", "configs: %s", id)
 
 	waitForEvent(c, d, "0", "-f scope=swarm", "config create "+id, defaultRetryCount)
 
@@ -2065,3 +1983,15 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsConfig(c *check.C) {
 	// filtered by config
 	waitForEvent(c, d, t1, "-f type=config", "config remove "+id, defaultRetryCount)
 }
+
+func getUnlockKey(d *daemon.Daemon, c *testing.T, autolockOutput string) string {
+	unlockKey, err := d.Cmd("swarm", "unlock-key", "-q")
+	assert.Assert(c, err == nil, unlockKey)
+	unlockKey = strings.TrimSuffix(unlockKey, "\n")
+
+	// Check that "docker swarm init --autolock" or "docker swarm update --autolock"
+	// contains all the expected strings, including the unlock key
+	assert.Assert(c, strings.Contains(autolockOutput, "docker swarm unlock"), autolockOutput)
+	assert.Assert(c, strings.Contains(autolockOutput, unlockKey), autolockOutput)
+	return unlockKey
+}
diff --git a/integration-cli/docker_cli_swarm_unix_test.go b/integration-cli/docker_cli_swarm_unix_test.go
index 7473937eda518..35f61a55f0345 100644
--- a/integration-cli/docker_cli_swarm_unix_test.go
+++ b/integration-cli/docker_cli_swarm_unix_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -5,56 +6,58 @@ package main
 import (
 	"encoding/json"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
 )
 
-func (s *DockerSwarmSuite) TestSwarmVolumePlugin(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmVolumePlugin(c *testing.T) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--mount", "type=volume,source=my-volume,destination=/foo,volume-driver=customvolumedriver", "--name", "top", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// Make sure task stays pending before plugin is available
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckServiceTasksInStateWithError("top", swarm.TaskStatePending, "missing plugin on 1 node"), checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckServiceTasksInStateWithError("top", swarm.TaskStatePending, "missing plugin on 1 node"), checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	plugin := newVolumePlugin(c, "customvolumedriver")
 	defer plugin.Close()
 
 	// create a dummy volume to trigger lazy loading of the plugin
 	out, err = d.Cmd("volume", "create", "-d", "customvolumedriver", "hello")
-	c.Assert(err, checker.IsNil, check.Commentf("%s", out))
+	assert.NilError(c, err, out)
 
 	// TODO(aaronl): It will take about 15 seconds for swarm to realize the
 	// plugin was loaded. Switching the test over to plugin v2 would avoid
 	// this long delay.
 
 	// make sure task has been deployed.
-	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
+	poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	out, err = d.Cmd("ps", "-q")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	containerID := strings.TrimSpace(out)
 
 	out, err = d.Cmd("inspect", "-f", "{{json .Mounts}}", containerID)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	var mounts []struct {
 		Name   string
 		Driver string
 	}
 
-	c.Assert(json.NewDecoder(strings.NewReader(out)).Decode(&mounts), checker.IsNil)
-	c.Assert(len(mounts), checker.Equals, 1, check.Commentf("%s", out))
-	c.Assert(mounts[0].Name, checker.Equals, "my-volume")
-	c.Assert(mounts[0].Driver, checker.Equals, "customvolumedriver")
+	assert.NilError(c, json.NewDecoder(strings.NewReader(out)).Decode(&mounts))
+	assert.Equal(c, len(mounts), 1, out)
+	assert.Equal(c, mounts[0].Name, "my-volume")
+	assert.Equal(c, mounts[0].Driver, "customvolumedriver")
 }
 
 // Test network plugin filter in swarm
-func (s *DockerSwarmSuite) TestSwarmNetworkPluginV2(c *check.C) {
+func (s *DockerSwarmSuite) TestSwarmNetworkPluginV2(c *testing.T) {
 	testRequires(c, IsAmd64)
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, false)
@@ -63,43 +66,43 @@ func (s *DockerSwarmSuite) TestSwarmNetworkPluginV2(c *check.C) {
 	pluginName := "aragunathan/global-net-plugin:latest"
 
 	_, err := d1.Cmd("plugin", "install", pluginName, "--grant-all-permissions")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	_, err = d2.Cmd("plugin", "install", pluginName, "--grant-all-permissions")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// create network
 	networkName := "globalnet"
 	_, err = d1.Cmd("network", "create", "--driver", pluginName, networkName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// create a global service to ensure that both nodes will have an instance
 	serviceName := "my-service"
 	_, err = d1.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--mode=global", "--network", networkName, "busybox", "top")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// wait for tasks ready
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals, 2)
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals(2)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// remove service
 	_, err = d1.Cmd("service", "rm", serviceName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// wait to ensure all containers have exited before removing the plugin. Else there's a
 	// possibility of container exits erroring out due to plugins being unavailable.
-	waitAndAssert(c, defaultReconciliationTimeout, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals, 0)
+	poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount), checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout))
 
 	// disable plugin on worker
 	_, err = d2.Cmd("plugin", "disable", "-f", pluginName)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	time.Sleep(20 * time.Second)
 
 	image := "busybox:latest"
 	// create a new global service again.
 	_, err = d1.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "--mode=global", "--network", networkName, image, "top")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
+
+	poll.WaitOn(c, pollCheck(c, d1.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image: 1})), poll.WithTimeout(defaultReconciliationTimeout))
 
-	waitAndAssert(c, defaultReconciliationTimeout, d1.CheckRunningTaskImages, checker.DeepEquals,
-		map[string]int{image: 1})
 }
diff --git a/integration-cli/docker_cli_top_test.go b/integration-cli/docker_cli_top_test.go
index 50744b0111187..9bc29f39c67f7 100644
--- a/integration-cli/docker_cli_top_test.go
+++ b/integration-cli/docker_cli_top_test.go
@@ -2,13 +2,13 @@ package main
 
 import (
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
-func (s *DockerSuite) TestTopMultipleArgs(c *check.C) {
+func (s *DockerSuite) TestTopMultipleArgs(c *testing.T) {
 	out := runSleepingContainer(c, "-d")
 	cleanedContainerID := strings.TrimSpace(out)
 
@@ -23,7 +23,7 @@ func (s *DockerSuite) TestTopMultipleArgs(c *check.C) {
 	result.Assert(c, expected)
 }
 
-func (s *DockerSuite) TestTopNonPrivileged(c *check.C) {
+func (s *DockerSuite) TestTopNonPrivileged(c *testing.T) {
 	out := runSleepingContainer(c, "-d")
 	cleanedContainerID := strings.TrimSpace(out)
 
@@ -40,25 +40,25 @@ func (s *DockerSuite) TestTopNonPrivileged(c *check.C) {
 		lookingFor = "top"
 	}
 
-	c.Assert(out1, checker.Contains, lookingFor, check.Commentf("top should've listed `%s` in the process list, but failed the first time", lookingFor))
-	c.Assert(out2, checker.Contains, lookingFor, check.Commentf("top should've listed `%s` in the process list, but failed the second time", lookingFor))
+	assert.Assert(c, strings.Contains(out1, lookingFor), "top should've listed `%s` in the process list, but failed the first time", lookingFor)
+	assert.Assert(c, strings.Contains(out2, lookingFor), "top should've listed `%s` in the process list, but failed the second time", lookingFor)
 }
 
 // TestTopWindowsCoreProcesses validates that there are lines for the critical
 // processes which are found in a Windows container. Note Windows is architecturally
 // very different to Linux in this regard.
-func (s *DockerSuite) TestTopWindowsCoreProcesses(c *check.C) {
+func (s *DockerSuite) TestTopWindowsCoreProcesses(c *testing.T) {
 	testRequires(c, DaemonIsWindows)
 	out := runSleepingContainer(c, "-d")
 	cleanedContainerID := strings.TrimSpace(out)
 	out1, _ := dockerCmd(c, "top", cleanedContainerID)
 	lookingFor := []string{"smss.exe", "csrss.exe", "wininit.exe", "services.exe", "lsass.exe", "CExecSvc.exe"}
 	for i, s := range lookingFor {
-		c.Assert(out1, checker.Contains, s, check.Commentf("top should've listed `%s` in the process list, but failed. Test case %d", s, i))
+		assert.Assert(c, strings.Contains(out1, s), "top should've listed `%s` in the process list, but failed. Test case %d", s, i)
 	}
 }
 
-func (s *DockerSuite) TestTopPrivileged(c *check.C) {
+func (s *DockerSuite) TestTopPrivileged(c *testing.T) {
 	// Windows does not support --privileged
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	out, _ := dockerCmd(c, "run", "--privileged", "-i", "-d", "busybox", "top")
@@ -68,6 +68,6 @@ func (s *DockerSuite) TestTopPrivileged(c *check.C) {
 	out2, _ := dockerCmd(c, "top", cleanedContainerID)
 	dockerCmd(c, "kill", cleanedContainerID)
 
-	c.Assert(out1, checker.Contains, "top", check.Commentf("top should've listed `top` in the process list, but failed the first time"))
-	c.Assert(out2, checker.Contains, "top", check.Commentf("top should've listed `top` in the process list, but failed the second time"))
+	assert.Assert(c, strings.Contains(out1, "top"), "top should've listed `top` in the process list, but failed the first time")
+	assert.Assert(c, strings.Contains(out2, "top"), "top should've listed `top` in the process list, but failed the second time")
 }
diff --git a/integration-cli/docker_cli_update_unix_test.go b/integration-cli/docker_cli_update_unix_test.go
index 1fb30f04d6df3..459b70e6468a7 100644
--- a/integration-cli/docker_cli_update_unix_test.go
+++ b/integration-cli/docker_cli_update_unix_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -8,18 +9,17 @@ import (
 	"fmt"
 	"os/exec"
 	"strings"
+	"testing"
 	"time"
 
+	"github.com/creack/pty"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/docker/docker/pkg/parsers/kernel"
-	"github.com/go-check/check"
-	"github.com/kr/pty"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
 )
 
-func (s *DockerSuite) TestUpdateRunningContainer(c *check.C) {
+func (s *DockerSuite) TestUpdateRunningContainer(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 
@@ -27,14 +27,14 @@ func (s *DockerSuite) TestUpdateRunningContainer(c *check.C) {
 	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "top")
 	dockerCmd(c, "update", "-m", "500M", name)
 
-	c.Assert(inspectField(c, name, "HostConfig.Memory"), checker.Equals, "524288000")
+	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
 
 	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
 	out, _ := dockerCmd(c, "exec", name, "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
+	assert.Equal(c, strings.TrimSpace(out), "524288000")
 }
 
-func (s *DockerSuite) TestUpdateRunningContainerWithRestart(c *check.C) {
+func (s *DockerSuite) TestUpdateRunningContainerWithRestart(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 
@@ -43,14 +43,14 @@ func (s *DockerSuite) TestUpdateRunningContainerWithRestart(c *check.C) {
 	dockerCmd(c, "update", "-m", "500M", name)
 	dockerCmd(c, "restart", name)
 
-	c.Assert(inspectField(c, name, "HostConfig.Memory"), checker.Equals, "524288000")
+	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
 
 	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
 	out, _ := dockerCmd(c, "exec", name, "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
+	assert.Equal(c, strings.TrimSpace(out), "524288000")
 }
 
-func (s *DockerSuite) TestUpdateStoppedContainer(c *check.C) {
+func (s *DockerSuite) TestUpdateStoppedContainer(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 
@@ -59,13 +59,13 @@ func (s *DockerSuite) TestUpdateStoppedContainer(c *check.C) {
 	dockerCmd(c, "run", "--name", name, "-m", "300M", "busybox", "cat", file)
 	dockerCmd(c, "update", "-m", "500M", name)
 
-	c.Assert(inspectField(c, name, "HostConfig.Memory"), checker.Equals, "524288000")
+	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "524288000")
 
 	out, _ := dockerCmd(c, "start", "-a", name)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "524288000")
+	assert.Equal(c, strings.TrimSpace(out), "524288000")
 }
 
-func (s *DockerSuite) TestUpdatePausedContainer(c *check.C) {
+func (s *DockerSuite) TestUpdatePausedContainer(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, cpuShare)
 
@@ -74,15 +74,15 @@ func (s *DockerSuite) TestUpdatePausedContainer(c *check.C) {
 	dockerCmd(c, "pause", name)
 	dockerCmd(c, "update", "--cpu-shares", "500", name)
 
-	c.Assert(inspectField(c, name, "HostConfig.CPUShares"), checker.Equals, "500")
+	assert.Equal(c, inspectField(c, name, "HostConfig.CPUShares"), "500")
 
 	dockerCmd(c, "unpause", name)
 	file := "/sys/fs/cgroup/cpu/cpu.shares"
 	out, _ := dockerCmd(c, "exec", name, "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "500")
+	assert.Equal(c, strings.TrimSpace(out), "500")
 }
 
-func (s *DockerSuite) TestUpdateWithUntouchedFields(c *check.C) {
+func (s *DockerSuite) TestUpdateWithUntouchedFields(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 	testRequires(c, cpuShare)
@@ -93,97 +93,36 @@ func (s *DockerSuite) TestUpdateWithUntouchedFields(c *check.C) {
 
 	// Update memory and not touch cpus, `cpuset.cpus` should still have the old value
 	out := inspectField(c, name, "HostConfig.CPUShares")
-	c.Assert(out, check.Equals, "800")
+	assert.Equal(c, out, "800")
 
 	file := "/sys/fs/cgroup/cpu/cpu.shares"
 	out, _ = dockerCmd(c, "exec", name, "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "800")
+	assert.Equal(c, strings.TrimSpace(out), "800")
 }
 
-func (s *DockerSuite) TestUpdateContainerInvalidValue(c *check.C) {
+func (s *DockerSuite) TestUpdateContainerInvalidValue(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 
 	name := "test-update-container"
 	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
 	out, _, err := dockerCmdWithError("update", "-m", "2M", name)
-	c.Assert(err, check.NotNil)
-	expected := "Minimum memory limit allowed is 4MB"
-	c.Assert(out, checker.Contains, expected)
+	assert.ErrorContains(c, err, "")
+	expected := "Minimum memory limit allowed is 6MB"
+	assert.Assert(c, strings.Contains(out, expected))
 }
 
-func (s *DockerSuite) TestUpdateContainerWithoutFlags(c *check.C) {
+func (s *DockerSuite) TestUpdateContainerWithoutFlags(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 
 	name := "test-update-container"
 	dockerCmd(c, "run", "-d", "--name", name, "-m", "300M", "busybox", "true")
 	_, _, err := dockerCmdWithError("update", name)
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 }
 
-func (s *DockerSuite) TestUpdateKernelMemory(c *check.C) {
-	testRequires(c, DaemonIsLinux, kernelMemorySupport)
-
-	name := "test-update-container"
-	dockerCmd(c, "run", "-d", "--name", name, "--kernel-memory", "50M", "busybox", "top")
-	dockerCmd(c, "update", "--kernel-memory", "100M", name)
-
-	c.Assert(inspectField(c, name, "HostConfig.KernelMemory"), checker.Equals, "104857600")
-
-	file := "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"
-	out, _ := dockerCmd(c, "exec", name, "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "104857600")
-}
-
-func (s *DockerSuite) TestUpdateKernelMemoryUninitialized(c *check.C) {
-	testRequires(c, DaemonIsLinux, kernelMemorySupport)
-
-	isNewKernel := CheckKernelVersion(4, 6, 0)
-	name := "test-update-container"
-	dockerCmd(c, "run", "-d", "--name", name, "busybox", "top")
-	_, _, err := dockerCmdWithError("update", "--kernel-memory", "100M", name)
-	// Update kernel memory to a running container without kernel memory initialized
-	// is not allowed before kernel version 4.6.
-	if !isNewKernel {
-		c.Assert(err, check.NotNil)
-	} else {
-		c.Assert(err, check.IsNil)
-	}
-
-	dockerCmd(c, "pause", name)
-	_, _, err = dockerCmdWithError("update", "--kernel-memory", "200M", name)
-	if !isNewKernel {
-		c.Assert(err, check.NotNil)
-	} else {
-		c.Assert(err, check.IsNil)
-	}
-	dockerCmd(c, "unpause", name)
-
-	dockerCmd(c, "stop", name)
-	dockerCmd(c, "update", "--kernel-memory", "300M", name)
-	dockerCmd(c, "start", name)
-
-	c.Assert(inspectField(c, name, "HostConfig.KernelMemory"), checker.Equals, "314572800")
-
-	file := "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"
-	out, _ := dockerCmd(c, "exec", name, "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "314572800")
-}
-
-// GetKernelVersion gets the current kernel version.
-func GetKernelVersion() *kernel.VersionInfo {
-	v, _ := kernel.ParseRelease(testEnv.DaemonInfo.KernelVersion)
-	return v
-}
-
-// CheckKernelVersion checks if current kernel is newer than (or equal to)
-// the given version.
-func CheckKernelVersion(k, major, minor int) bool {
-	return kernel.CompareKernelVersion(*GetKernelVersion(), kernel.VersionInfo{Kernel: k, Major: major, Minor: minor}) >= 0
-}
-
-func (s *DockerSuite) TestUpdateSwapMemoryOnly(c *check.C) {
+func (s *DockerSuite) TestUpdateSwapMemoryOnly(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 	testRequires(c, swapMemorySupport)
@@ -192,14 +131,14 @@ func (s *DockerSuite) TestUpdateSwapMemoryOnly(c *check.C) {
 	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "--memory-swap", "500M", "busybox", "top")
 	dockerCmd(c, "update", "--memory-swap", "600M", name)
 
-	c.Assert(inspectField(c, name, "HostConfig.MemorySwap"), checker.Equals, "629145600")
+	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "629145600")
 
 	file := "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
 	out, _ := dockerCmd(c, "exec", name, "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "629145600")
+	assert.Equal(c, strings.TrimSpace(out), "629145600")
 }
 
-func (s *DockerSuite) TestUpdateInvalidSwapMemory(c *check.C) {
+func (s *DockerSuite) TestUpdateInvalidSwapMemory(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 	testRequires(c, swapMemorySupport)
@@ -209,38 +148,38 @@ func (s *DockerSuite) TestUpdateInvalidSwapMemory(c *check.C) {
 	_, _, err := dockerCmdWithError("update", "--memory-swap", "200M", name)
 	// Update invalid swap memory should fail.
 	// This will pass docker config validation, but failed at kernel validation
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// Update invalid swap memory with failure should not change HostConfig
-	c.Assert(inspectField(c, name, "HostConfig.Memory"), checker.Equals, "314572800")
-	c.Assert(inspectField(c, name, "HostConfig.MemorySwap"), checker.Equals, "524288000")
+	assert.Equal(c, inspectField(c, name, "HostConfig.Memory"), "314572800")
+	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "524288000")
 
 	dockerCmd(c, "update", "--memory-swap", "600M", name)
 
-	c.Assert(inspectField(c, name, "HostConfig.MemorySwap"), checker.Equals, "629145600")
+	assert.Equal(c, inspectField(c, name, "HostConfig.MemorySwap"), "629145600")
 
 	file := "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"
 	out, _ := dockerCmd(c, "exec", name, "cat", file)
-	c.Assert(strings.TrimSpace(out), checker.Equals, "629145600")
+	assert.Equal(c, strings.TrimSpace(out), "629145600")
 }
 
-func (s *DockerSuite) TestUpdateStats(c *check.C) {
+func (s *DockerSuite) TestUpdateStats(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 	testRequires(c, cpuCfsQuota)
 	name := "foo"
 	dockerCmd(c, "run", "-d", "-ti", "--name", name, "-m", "500m", "busybox")
 
-	c.Assert(waitRun(name), checker.IsNil)
+	assert.NilError(c, waitRun(name))
 
 	getMemLimit := func(id string) uint64 {
 		resp, body, err := request.Get(fmt.Sprintf("/containers/%s/stats?stream=false", id))
-		c.Assert(err, checker.IsNil)
-		c.Assert(resp.Header.Get("Content-Type"), checker.Equals, "application/json")
+		assert.NilError(c, err)
+		assert.Equal(c, resp.Header.Get("Content-Type"), "application/json")
 
 		var v *types.Stats
 		err = json.NewDecoder(body).Decode(&v)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 		body.Close()
 
 		return v.MemoryStats.Limit
@@ -250,12 +189,10 @@ func (s *DockerSuite) TestUpdateStats(c *check.C) {
 	dockerCmd(c, "update", "--cpu-quota", "2000", name)
 
 	curMemLimit := getMemLimit(name)
-
-	c.Assert(preMemLimit, checker.Equals, curMemLimit)
-
+	assert.Equal(c, preMemLimit, curMemLimit)
 }
 
-func (s *DockerSuite) TestUpdateMemoryWithSwapMemory(c *check.C) {
+func (s *DockerSuite) TestUpdateMemoryWithSwapMemory(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 	testRequires(c, memoryLimitSupport)
 	testRequires(c, swapMemorySupport)
@@ -263,77 +200,77 @@ func (s *DockerSuite) TestUpdateMemoryWithSwapMemory(c *check.C) {
 	name := "test-update-container"
 	dockerCmd(c, "run", "-d", "--name", name, "--memory", "300M", "busybox", "top")
 	out, _, err := dockerCmdWithError("update", "--memory", "800M", name)
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "Memory limit should be smaller than already set memoryswap limit")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Memory limit should be smaller than already set memoryswap limit"))
 
 	dockerCmd(c, "update", "--memory", "800M", "--memory-swap", "1000M", name)
 }
 
-func (s *DockerSuite) TestUpdateNotAffectMonitorRestartPolicy(c *check.C) {
+func (s *DockerSuite) TestUpdateNotAffectMonitorRestartPolicy(c *testing.T) {
 	testRequires(c, DaemonIsLinux, cpuShare)
 
 	out, _ := dockerCmd(c, "run", "-tid", "--restart=always", "busybox", "sh")
-	id := strings.TrimSpace(string(out))
+	id := strings.TrimSpace(out)
 	dockerCmd(c, "update", "--cpu-shares", "512", id)
 
 	cpty, tty, err := pty.Open()
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer cpty.Close()
 
 	cmd := exec.Command(dockerBinary, "attach", id)
 	cmd.Stdin = tty
 
-	c.Assert(cmd.Start(), checker.IsNil)
+	assert.NilError(c, cmd.Start())
 	defer cmd.Process.Kill()
 
 	_, err = cpty.Write([]byte("exit\n"))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
-	c.Assert(cmd.Wait(), checker.IsNil)
+	assert.NilError(c, cmd.Wait())
 
 	// container should restart again and keep running
 	err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second)
-	c.Assert(err, checker.IsNil)
-	c.Assert(waitRun(id), checker.IsNil)
+	assert.NilError(c, err)
+	assert.NilError(c, waitRun(id))
 }
 
-func (s *DockerSuite) TestUpdateWithNanoCPUs(c *check.C) {
+func (s *DockerSuite) TestUpdateWithNanoCPUs(c *testing.T) {
 	testRequires(c, cpuCfsQuota, cpuCfsPeriod)
 
 	file1 := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
 	file2 := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
 
 	out, _ := dockerCmd(c, "run", "-d", "--cpus", "0.5", "--name", "top", "busybox", "top")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.Assert(c, strings.TrimSpace(out) != "")
 
 	out, _ = dockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "50000\n100000")
+	assert.Equal(c, strings.TrimSpace(out), "50000\n100000")
 
-	clt, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	clt, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	inspect, err := clt.ContainerInspect(context.Background(), "top")
-	c.Assert(err, checker.IsNil)
-	c.Assert(inspect.HostConfig.NanoCPUs, checker.Equals, int64(500000000))
+	assert.NilError(c, err)
+	assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(500000000))
 
 	out = inspectField(c, "top", "HostConfig.CpuQuota")
-	c.Assert(out, checker.Equals, "0", check.Commentf("CPU CFS quota should be 0"))
+	assert.Equal(c, out, "0", "CPU CFS quota should be 0")
 	out = inspectField(c, "top", "HostConfig.CpuPeriod")
-	c.Assert(out, checker.Equals, "0", check.Commentf("CPU CFS period should be 0"))
+	assert.Equal(c, out, "0", "CPU CFS period should be 0")
 
 	out, _, err = dockerCmdWithError("update", "--cpu-quota", "80000", "top")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "Conflicting options: CPU Quota cannot be updated as NanoCPUs has already been set")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Conflicting options: CPU Quota cannot be updated as NanoCPUs has already been set"))
 
 	dockerCmd(c, "update", "--cpus", "0.8", "top")
 	inspect, err = clt.ContainerInspect(context.Background(), "top")
-	c.Assert(err, checker.IsNil)
-	c.Assert(inspect.HostConfig.NanoCPUs, checker.Equals, int64(800000000))
+	assert.NilError(c, err)
+	assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(800000000))
 
 	out = inspectField(c, "top", "HostConfig.CpuQuota")
-	c.Assert(out, checker.Equals, "0", check.Commentf("CPU CFS quota should be 0"))
+	assert.Equal(c, out, "0", "CPU CFS quota should be 0")
 	out = inspectField(c, "top", "HostConfig.CpuPeriod")
-	c.Assert(out, checker.Equals, "0", check.Commentf("CPU CFS period should be 0"))
+	assert.Equal(c, out, "0", "CPU CFS period should be 0")
 
 	out, _ = dockerCmd(c, "exec", "top", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
-	c.Assert(strings.TrimSpace(out), checker.Equals, "80000\n100000")
+	assert.Equal(c, strings.TrimSpace(out), "80000\n100000")
 }
diff --git a/integration-cli/docker_cli_userns_test.go b/integration-cli/docker_cli_userns_test.go
index 13d1f56dc63d7..5c048c1f33fd8 100644
--- a/integration-cli/docker_cli_userns_test.go
+++ b/integration-cli/docker_cli_userns_test.go
@@ -1,33 +1,33 @@
+//go:build !windows
 // +build !windows
 
 package main
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path"
 	"path/filepath"
 	"strconv"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/system"
-	"github.com/go-check/check"
+	"gotest.tools/v3/assert"
 )
 
 // user namespaces test: run daemon with remapped root setting
 // 1. validate uid/gid maps are set properly
 // 2. verify that files created are owned by remapped root
-func (s *DockerDaemonSuite) TestDaemonUserNamespaceRootSetting(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon, UserNamespaceInKernel)
+func (s *DockerDaemonSuite) TestDaemonUserNamespaceRootSetting(c *testing.T) {
+	testRequires(c, UserNamespaceInKernel)
 
 	s.d.StartWithBusybox(c, "--userns-remap", "default")
 
-	tmpDir, err := ioutil.TempDir("", "userns")
-	c.Assert(err, checker.IsNil)
+	tmpDir, err := os.MkdirTemp("", "userns")
+	assert.NilError(c, err)
 
 	defer os.RemoveAll(tmpDir)
 
@@ -37,58 +37,59 @@ func (s *DockerDaemonSuite) TestDaemonUserNamespaceRootSetting(c *check.C) {
 
 	// we need to find the uid and gid of the remapped root from the daemon's root dir info
 	uidgid := strings.Split(filepath.Base(s.d.Root), ".")
-	c.Assert(uidgid, checker.HasLen, 2, check.Commentf("Should have gotten uid/gid strings from root dirname: %s", filepath.Base(s.d.Root)))
+	assert.Equal(c, len(uidgid), 2, fmt.Sprintf("Should have gotten uid/gid strings from root dirname: %s", filepath.Base(s.d.Root)))
 	uid, err := strconv.Atoi(uidgid[0])
-	c.Assert(err, checker.IsNil, check.Commentf("Can't parse uid"))
+	assert.NilError(c, err, "Can't parse uid")
 	gid, err := strconv.Atoi(uidgid[1])
-	c.Assert(err, checker.IsNil, check.Commentf("Can't parse gid"))
+	assert.NilError(c, err, "Can't parse gid")
 
 	// writable by the remapped root UID/GID pair
-	c.Assert(os.Chown(tmpDir, uid, gid), checker.IsNil)
+	assert.NilError(c, os.Chown(tmpDir, uid, gid))
+
+	out, err := s.d.Cmd("run", "-d", "--name", "userns", "-v", tmpDir+":/goofy", "-v", tmpDirNotExists+":/donald", "busybox", "sh", "-c", "touch /goofy/testfile; exec top")
+	assert.NilError(c, err, "Output: %s", out)
 
-	out, err := s.d.Cmd("run", "-d", "--name", "userns", "-v", tmpDir+":/goofy", "-v", tmpDirNotExists+":/donald", "busybox", "sh", "-c", "touch /goofy/testfile; top")
-	c.Assert(err, checker.IsNil, check.Commentf("Output: %s", out))
 	user := s.findUser(c, "userns")
-	c.Assert(uidgid[0], checker.Equals, user)
+	assert.Equal(c, uidgid[0], user)
 
 	// check that the created directory is owned by remapped uid:gid
 	statNotExists, err := system.Stat(tmpDirNotExists)
-	c.Assert(err, checker.IsNil)
-	c.Assert(statNotExists.UID(), checker.Equals, uint32(uid), check.Commentf("Created directory not owned by remapped root UID"))
-	c.Assert(statNotExists.GID(), checker.Equals, uint32(gid), check.Commentf("Created directory not owned by remapped root GID"))
+	assert.NilError(c, err)
+	assert.Equal(c, statNotExists.UID(), uint32(uid), "Created directory not owned by remapped root UID")
+	assert.Equal(c, statNotExists.GID(), uint32(gid), "Created directory not owned by remapped root GID")
 
 	pid, err := s.d.Cmd("inspect", "--format={{.State.Pid}}", "userns")
-	c.Assert(err, checker.IsNil, check.Commentf("Could not inspect running container: out: %q", pid))
+	assert.Assert(c, err == nil, "Could not inspect running container: out: %q", pid)
 	// check the uid and gid maps for the PID to ensure root is remapped
 	// (cmd = cat /proc//uid_map | grep -E '0\s+9999\s+1')
 	_, err = RunCommandPipelineWithOutput(
 		exec.Command("cat", "/proc/"+strings.TrimSpace(pid)+"/uid_map"),
 		exec.Command("grep", "-E", fmt.Sprintf("0[[:space:]]+%d[[:space:]]+", uid)))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	_, err = RunCommandPipelineWithOutput(
 		exec.Command("cat", "/proc/"+strings.TrimSpace(pid)+"/gid_map"),
 		exec.Command("grep", "-E", fmt.Sprintf("0[[:space:]]+%d[[:space:]]+", gid)))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// check that the touched file is owned by remapped uid:gid
 	stat, err := system.Stat(filepath.Join(tmpDir, "testfile"))
-	c.Assert(err, checker.IsNil)
-	c.Assert(stat.UID(), checker.Equals, uint32(uid), check.Commentf("Touched file not owned by remapped root UID"))
-	c.Assert(stat.GID(), checker.Equals, uint32(gid), check.Commentf("Touched file not owned by remapped root GID"))
+	assert.NilError(c, err)
+	assert.Equal(c, stat.UID(), uint32(uid), "Touched file not owned by remapped root UID")
+	assert.Equal(c, stat.GID(), uint32(gid), "Touched file not owned by remapped root GID")
 
 	// use host usernamespace
-	out, err = s.d.Cmd("run", "-d", "--name", "userns_skip", "--userns", "host", "busybox", "sh", "-c", "touch /goofy/testfile; top")
-	c.Assert(err, checker.IsNil, check.Commentf("Output: %s", out))
+	out, err = s.d.Cmd("run", "-d", "--name", "userns_skip", "--userns", "host", "busybox", "sh", "-c", "touch /goofy/testfile; exec top")
+	assert.Assert(c, err == nil, "Output: %s", out)
 	user = s.findUser(c, "userns_skip")
 	// userns are skipped, user is root
-	c.Assert(user, checker.Equals, "root")
+	assert.Equal(c, user, "root")
 }
 
 // findUser finds the uid or name of the user of the first process that runs in a container
-func (s *DockerDaemonSuite) findUser(c *check.C, container string) string {
+func (s *DockerDaemonSuite) findUser(c *testing.T, container string) string {
 	out, err := s.d.Cmd("top", container)
-	c.Assert(err, checker.IsNil, check.Commentf("Output: %s", out))
+	assert.Assert(c, err == nil, "Output: %s", out)
 	rows := strings.Split(out, "\n")
 	if len(rows) < 2 {
 		// No process rows founds
diff --git a/integration-cli/docker_cli_v2_only_test.go b/integration-cli/docker_cli_v2_only_test.go
index df0c01a517c10..5620350fa6ba8 100644
--- a/integration-cli/docker_cli_v2_only_test.go
+++ b/integration-cli/docker_cli_v2_only_test.go
@@ -2,20 +2,20 @@ package main
 
 import (
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"os"
+	"testing"
 
-	"github.com/docker/docker/internal/test/registry"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/registry"
+	"gotest.tools/v3/assert"
 )
 
 func makefile(path string, contents string) (string, error) {
-	f, err := ioutil.TempFile(path, "tmp")
+	f, err := os.CreateTemp(path, "tmp")
 	if err != nil {
 		return "", err
 	}
-	err = ioutil.WriteFile(f.Name(), []byte(contents), os.ModePerm)
+	err = os.WriteFile(f.Name(), []byte(contents), os.ModePerm)
 	if err != nil {
 		return "", err
 	}
@@ -24,10 +24,10 @@ func makefile(path string, contents string) (string, error) {
 
 // TestV2Only ensures that a daemon does not
 // attempt to contact any v1 registry endpoints.
-func (s *DockerRegistrySuite) TestV2Only(c *check.C) {
+func (s *DockerRegistrySuite) TestV2Only(c *testing.T) {
 	reg, err := registry.NewMock(c)
+	assert.NilError(c, err)
 	defer reg.Close()
-	c.Assert(err, check.IsNil)
 
 	reg.RegisterHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
 		w.WriteHeader(404)
@@ -41,18 +41,17 @@ func (s *DockerRegistrySuite) TestV2Only(c *check.C) {
 
 	s.d.Start(c, "--insecure-registry", reg.URL())
 
-	tmp, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, check.IsNil)
+	tmp, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(tmp)
 
 	dockerfileName, err := makefile(tmp, fmt.Sprintf("FROM %s/busybox", reg.URL()))
-	c.Assert(err, check.IsNil, check.Commentf("Unable to create test dockerfile"))
-
-	s.d.Cmd("build", "--file", dockerfileName, tmp)
-
-	s.d.Cmd("run", repoName)
-	s.d.Cmd("login", "-u", "richard", "-p", "testtest", reg.URL())
-	s.d.Cmd("tag", "busybox", repoName)
-	s.d.Cmd("push", repoName)
-	s.d.Cmd("pull", repoName)
+	assert.NilError(c, err, "Unable to create test dockerfile")
+
+	_, _ = s.d.Cmd("build", "--file", dockerfileName, tmp)
+	_, _ = s.d.Cmd("run", repoName)
+	_, _ = s.d.Cmd("login", "-u", "richard", "-p", "testtest", reg.URL())
+	_, _ = s.d.Cmd("tag", "busybox", repoName)
+	_, _ = s.d.Cmd("push", repoName)
+	_, _ = s.d.Cmd("pull", repoName)
 }
diff --git a/integration-cli/docker_cli_volume_test.go b/integration-cli/docker_cli_volume_test.go
index 257ac89701d5e..b7424c82afa9f 100644
--- a/integration-cli/docker_cli_volume_test.go
+++ b/integration-cli/docker_cli_volume_test.go
@@ -3,56 +3,50 @@ package main
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli/build"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
 )
 
-func (s *DockerSuite) TestVolumeCLICreate(c *check.C) {
+func (s *DockerSuite) TestVolumeCLICreate(c *testing.T) {
 	dockerCmd(c, "volume", "create")
 
 	_, _, err := dockerCmdWithError("volume", "create", "-d", "nosuchdriver")
-	c.Assert(err, check.NotNil)
+	assert.ErrorContains(c, err, "")
 
 	// test using hidden --name option
 	out, _ := dockerCmd(c, "volume", "create", "--name=test")
 	name := strings.TrimSpace(out)
-	c.Assert(name, check.Equals, "test")
+	assert.Equal(c, name, "test")
 
 	out, _ = dockerCmd(c, "volume", "create", "test2")
 	name = strings.TrimSpace(out)
-	c.Assert(name, check.Equals, "test2")
+	assert.Equal(c, name, "test2")
 }
 
-func (s *DockerSuite) TestVolumeCLIInspect(c *check.C) {
-	c.Assert(
-		exec.Command(dockerBinary, "volume", "inspect", "doesnotexist").Run(),
-		check.Not(check.IsNil),
-		check.Commentf("volume inspect should error on non-existent volume"),
-	)
-
+func (s *DockerSuite) TestVolumeCLIInspect(c *testing.T) {
+	assert.Assert(c, exec.Command(dockerBinary, "volume", "inspect", "doesnotexist").Run() != nil, "volume inspect should error on non-existent volume")
 	out, _ := dockerCmd(c, "volume", "create")
 	name := strings.TrimSpace(out)
 	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", name)
-	c.Assert(strings.TrimSpace(out), check.Equals, name)
+	assert.Equal(c, strings.TrimSpace(out), name)
 
 	dockerCmd(c, "volume", "create", "test")
 	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", "test")
-	c.Assert(strings.TrimSpace(out), check.Equals, "test")
+	assert.Equal(c, strings.TrimSpace(out), "test")
 }
 
-func (s *DockerSuite) TestVolumeCLIInspectMulti(c *check.C) {
+func (s *DockerSuite) TestVolumeCLIInspectMulti(c *testing.T) {
 	dockerCmd(c, "volume", "create", "test1")
 	dockerCmd(c, "volume", "create", "test2")
 	dockerCmd(c, "volume", "create", "test3")
@@ -64,12 +58,12 @@ func (s *DockerSuite) TestVolumeCLIInspectMulti(c *check.C) {
 	})
 
 	out := result.Stdout()
-	c.Assert(out, checker.Contains, "test1")
-	c.Assert(out, checker.Contains, "test2")
-	c.Assert(out, checker.Contains, "test3")
+	assert.Assert(c, strings.Contains(out, "test1"))
+	assert.Assert(c, strings.Contains(out, "test2"))
+	assert.Assert(c, strings.Contains(out, "test3"))
 }
 
-func (s *DockerSuite) TestVolumeCLILs(c *check.C) {
+func (s *DockerSuite) TestVolumeCLILs(c *testing.T) {
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 	dockerCmd(c, "volume", "create", "aaa")
 
@@ -82,7 +76,7 @@ func (s *DockerSuite) TestVolumeCLILs(c *check.C) {
 	assertVolumesInList(c, out, []string{"aaa", "soo", "test"})
 }
 
-func (s *DockerSuite) TestVolumeLsFormat(c *check.C) {
+func (s *DockerSuite) TestVolumeLsFormat(c *testing.T) {
 	dockerCmd(c, "volume", "create", "aaa")
 	dockerCmd(c, "volume", "create", "test")
 	dockerCmd(c, "volume", "create", "soo")
@@ -91,7 +85,7 @@ func (s *DockerSuite) TestVolumeLsFormat(c *check.C) {
 	assertVolumesInList(c, out, []string{"aaa", "soo", "test"})
 }
 
-func (s *DockerSuite) TestVolumeLsFormatDefaultFormat(c *check.C) {
+func (s *DockerSuite) TestVolumeLsFormatDefaultFormat(c *testing.T) {
 	dockerCmd(c, "volume", "create", "aaa")
 	dockerCmd(c, "volume", "create", "test")
 	dockerCmd(c, "volume", "create", "soo")
@@ -99,35 +93,19 @@ func (s *DockerSuite) TestVolumeLsFormatDefaultFormat(c *check.C) {
 	config := `{
 		"volumesFormat": "{{ .Name }} default"
 }`
-	d, err := ioutil.TempDir("", "integration-cli-")
-	c.Assert(err, checker.IsNil)
+	d, err := os.MkdirTemp("", "integration-cli-")
+	assert.NilError(c, err)
 	defer os.RemoveAll(d)
 
-	err = ioutil.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0644)
+	assert.NilError(c, err)
 
 	out, _ := dockerCmd(c, "--config", d, "volume", "ls")
 	assertVolumesInList(c, out, []string{"aaa default", "soo default", "test default"})
 }
 
-// assertVolList checks volume retrieved with ls command
-// equals to expected volume list
-// note: out should be `volume ls [option]` result
-func assertVolList(c *check.C, out string, expectVols []string) {
-	lines := strings.Split(out, "\n")
-	var volList []string
-	for _, line := range lines[1 : len(lines)-1] {
-		volFields := strings.Fields(line)
-		// wrap all volume name in volList
-		volList = append(volList, volFields[1])
-	}
-
-	// volume ls should contains all expected volumes
-	c.Assert(volList, checker.DeepEquals, expectVols)
-}
-
-func assertVolumesInList(c *check.C, out string, expected []string) {
-	lines := strings.Split(strings.TrimSpace(string(out)), "\n")
+func assertVolumesInList(c *testing.T, out string, expected []string) {
+	lines := strings.Split(strings.TrimSpace(out), "\n")
 	for _, expect := range expected {
 		found := false
 		for _, v := range lines {
@@ -136,11 +114,11 @@ func assertVolumesInList(c *check.C, out string, expected []string) {
 				break
 			}
 		}
-		c.Assert(found, checker.Equals, true, check.Commentf("Expected volume not found: %v, got: %v", expect, lines))
+		assert.Assert(c, found, "Expected volume not found: %v, got: %v", expect, lines)
 	}
 }
 
-func (s *DockerSuite) TestVolumeCLILsFilterDangling(c *check.C) {
+func (s *DockerSuite) TestVolumeCLILsFilterDangling(c *testing.T) {
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 	dockerCmd(c, "volume", "create", "testnotinuse1")
 	dockerCmd(c, "volume", "create", "testisinuse1")
@@ -154,55 +132,50 @@ func (s *DockerSuite) TestVolumeCLILsFilterDangling(c *check.C) {
 	out, _ := dockerCmd(c, "volume", "ls")
 
 	// No filter, all volumes should show
-	c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
-	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
-	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
-
+	assert.Assert(c, strings.Contains(out, "testnotinuse1\n"), "expected volume 'testnotinuse1' in output")
+	assert.Assert(c, strings.Contains(out, "testisinuse1\n"), "expected volume 'testisinuse1' in output")
+	assert.Assert(c, strings.Contains(out, "testisinuse2\n"), "expected volume 'testisinuse2' in output")
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=false")
 
 	// Explicitly disabling dangling
-	c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
-	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
-	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
-
+	assert.Assert(c, !strings.Contains(out, "testnotinuse1\n"), "expected volume 'testnotinuse1' in output")
+	assert.Assert(c, strings.Contains(out, "testisinuse1\n"), "expected volume 'testisinuse1' in output")
+	assert.Assert(c, strings.Contains(out, "testisinuse2\n"), "expected volume 'testisinuse2' in output")
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=true")
 
 	// Filter "dangling" volumes; only "dangling" (unused) volumes should be in the output
-	c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
-	c.Assert(out, check.Not(checker.Contains), "testisinuse1\n", check.Commentf("volume 'testisinuse1' in output, but not expected"))
-	c.Assert(out, check.Not(checker.Contains), "testisinuse2\n", check.Commentf("volume 'testisinuse2' in output, but not expected"))
-
+	assert.Assert(c, strings.Contains(out, "testnotinuse1\n"), "expected volume 'testnotinuse1' in output")
+	assert.Assert(c, !strings.Contains(out, "testisinuse1\n"), "volume 'testisinuse1' in output, but not expected")
+	assert.Assert(c, !strings.Contains(out, "testisinuse2\n"), "volume 'testisinuse2' in output, but not expected")
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=1")
 	// Filter "dangling" volumes; only "dangling" (unused) volumes should be in the output, dangling also accept 1
-	c.Assert(out, checker.Contains, "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
-	c.Assert(out, check.Not(checker.Contains), "testisinuse1\n", check.Commentf("volume 'testisinuse1' in output, but not expected"))
-	c.Assert(out, check.Not(checker.Contains), "testisinuse2\n", check.Commentf("volume 'testisinuse2' in output, but not expected"))
-
+	assert.Assert(c, strings.Contains(out, "testnotinuse1\n"), "expected volume 'testnotinuse1' in output")
+	assert.Assert(c, !strings.Contains(out, "testisinuse1\n"), "volume 'testisinuse1' in output, but not expected")
+	assert.Assert(c, !strings.Contains(out, "testisinuse2\n"), "volume 'testisinuse2' in output, but not expected")
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "dangling=0")
 	// dangling=0 is same as dangling=false case
-	c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
-	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
-	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
-
+	assert.Assert(c, !strings.Contains(out, "testnotinuse1\n"), "expected volume 'testnotinuse1' in output")
+	assert.Assert(c, strings.Contains(out, "testisinuse1\n"), "expected volume 'testisinuse1' in output")
+	assert.Assert(c, strings.Contains(out, "testisinuse2\n"), "expected volume 'testisinuse2' in output")
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "name=testisin")
-	c.Assert(out, check.Not(checker.Contains), "testnotinuse1\n", check.Commentf("expected volume 'testnotinuse1' in output"))
-	c.Assert(out, checker.Contains, "testisinuse1\n", check.Commentf("expected volume 'testisinuse1' in output"))
-	c.Assert(out, checker.Contains, "testisinuse2\n", check.Commentf("expected volume 'testisinuse2' in output"))
+	assert.Assert(c, !strings.Contains(out, "testnotinuse1\n"), "expected volume 'testnotinuse1' in output")
+	assert.Assert(c, strings.Contains(out, "testisinuse1\n"), "expected volume 'testisinuse1' in output")
+	assert.Assert(c, strings.Contains(out, "testisinuse2\n"), "expected volume 'testisinuse2' in output")
 }
 
-func (s *DockerSuite) TestVolumeCLILsErrorWithInvalidFilterName(c *check.C) {
+func (s *DockerSuite) TestVolumeCLILsErrorWithInvalidFilterName(c *testing.T) {
 	out, _, err := dockerCmdWithError("volume", "ls", "-f", "FOO=123")
-	c.Assert(err, checker.NotNil)
-	c.Assert(out, checker.Contains, "Invalid filter")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Invalid filter"))
 }
 
-func (s *DockerSuite) TestVolumeCLILsWithIncorrectFilterValue(c *check.C) {
+func (s *DockerSuite) TestVolumeCLILsWithIncorrectFilterValue(c *testing.T) {
 	out, _, err := dockerCmdWithError("volume", "ls", "-f", "dangling=invalid")
-	c.Assert(err, check.NotNil)
-	c.Assert(out, checker.Contains, "Invalid filter")
+	assert.ErrorContains(c, err, "")
+	assert.Assert(c, strings.Contains(out, "Invalid filter"))
 }
 
-func (s *DockerSuite) TestVolumeCLIRm(c *check.C) {
+func (s *DockerSuite) TestVolumeCLIRm(c *testing.T) {
 	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
 	out, _ := dockerCmd(c, "volume", "create")
 	id := strings.TrimSpace(out)
@@ -220,30 +193,25 @@ func (s *DockerSuite) TestVolumeCLIRm(c *check.C) {
 	})
 
 	out, _ = dockerCmd(c, "run", "--volumes-from=test", "--name=test2", "busybox", "sh", "-c", "cat /foo/bar")
-	c.Assert(strings.TrimSpace(out), check.Equals, "hello")
+	assert.Equal(c, strings.TrimSpace(out), "hello")
 	dockerCmd(c, "rm", "-fv", "test2")
 	dockerCmd(c, "volume", "inspect", volumeID)
 	dockerCmd(c, "rm", "-f", "test")
 
 	out, _ = dockerCmd(c, "run", "--name=test2", "-v", volumeID+":"+prefix+"/foo", "busybox", "sh", "-c", "cat /foo/bar")
-	c.Assert(strings.TrimSpace(out), check.Equals, "hello", check.Commentf("volume data was removed"))
+	assert.Equal(c, strings.TrimSpace(out), "hello", "volume data was removed")
 	dockerCmd(c, "rm", "test2")
 
 	dockerCmd(c, "volume", "rm", volumeID)
-	c.Assert(
-		exec.Command("volume", "rm", "doesnotexist").Run(),
-		check.Not(check.IsNil),
-		check.Commentf("volume rm should fail with non-existent volume"),
-	)
+	assert.Assert(c, exec.Command("volume", "rm", "doesnotexist").Run() != nil, "volume rm should fail with non-existent volume")
 }
 
 // FIXME(vdemeester) should be a unit test in cli/command/volume package
-func (s *DockerSuite) TestVolumeCLINoArgs(c *check.C) {
+func (s *DockerSuite) TestVolumeCLINoArgs(c *testing.T) {
 	out, _ := dockerCmd(c, "volume")
 	// no args should produce the cmd usage output
 	usage := "Usage:	docker volume COMMAND"
-	c.Assert(out, checker.Contains, usage)
-
+	assert.Assert(c, strings.Contains(out, usage))
 	// invalid arg should error and show the command usage on stderr
 	icmd.RunCommand(dockerBinary, "volume", "somearg").Assert(c, icmd.Expected{
 		ExitCode: 1,
@@ -258,20 +226,20 @@ func (s *DockerSuite) TestVolumeCLINoArgs(c *check.C) {
 		Error:    "exit status 125",
 		Err:      usage,
 	})
-	c.Assert(result.Stderr(), checker.Contains, "unknown flag: --no-such-flag")
+	assert.Assert(c, strings.Contains(result.Stderr(), "unknown flag: --no-such-flag"))
 }
 
-func (s *DockerSuite) TestVolumeCLIInspectTmplError(c *check.C) {
+func (s *DockerSuite) TestVolumeCLIInspectTmplError(c *testing.T) {
 	out, _ := dockerCmd(c, "volume", "create")
 	name := strings.TrimSpace(out)
 
 	out, exitCode, err := dockerCmdWithError("volume", "inspect", "--format='{{ .FooBar }}'", name)
-	c.Assert(err, checker.NotNil, check.Commentf("Output: %s", out))
-	c.Assert(exitCode, checker.Equals, 1, check.Commentf("Output: %s", out))
-	c.Assert(out, checker.Contains, "Template parsing error")
+	assert.Assert(c, err != nil, "Output: %s", out)
+	assert.Equal(c, exitCode, 1, fmt.Sprintf("Output: %s", out))
+	assert.Assert(c, strings.Contains(out, "Template parsing error"))
 }
 
-func (s *DockerSuite) TestVolumeCLICreateWithOpts(c *check.C) {
+func (s *DockerSuite) TestVolumeCLICreateWithOpts(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	dockerCmd(c, "volume", "create", "-d", "local", "test", "--opt=type=tmpfs", "--opt=device=tmpfs", "--opt=o=size=1m,uid=1000")
@@ -284,30 +252,30 @@ func (s *DockerSuite) TestVolumeCLICreateWithOpts(c *check.C) {
 			found = true
 			info := strings.Fields(m)
 			// tmpfs on  type tmpfs (rw,relatime,size=1024k,uid=1000)
-			c.Assert(info[0], checker.Equals, "tmpfs")
-			c.Assert(info[2], checker.Equals, "/foo")
-			c.Assert(info[4], checker.Equals, "tmpfs")
-			c.Assert(info[5], checker.Contains, "uid=1000")
-			c.Assert(info[5], checker.Contains, "size=1024k")
+			assert.Equal(c, info[0], "tmpfs")
+			assert.Equal(c, info[2], "/foo")
+			assert.Equal(c, info[4], "tmpfs")
+			assert.Assert(c, strings.Contains(info[5], "uid=1000"))
+			assert.Assert(c, strings.Contains(info[5], "size=1024k"))
 			break
 		}
 	}
-	c.Assert(found, checker.Equals, true)
+	assert.Equal(c, found, true)
 }
 
-func (s *DockerSuite) TestVolumeCLICreateLabel(c *check.C) {
+func (s *DockerSuite) TestVolumeCLICreateLabel(c *testing.T) {
 	testVol := "testvolcreatelabel"
 	testLabel := "foo"
 	testValue := "bar"
 
 	_, _, err := dockerCmdWithError("volume", "create", "--label", testLabel+"="+testValue, testVol)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	out, _ := dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+testLabel+" }}", testVol)
-	c.Assert(strings.TrimSpace(out), check.Equals, testValue)
+	assert.Equal(c, strings.TrimSpace(out), testValue)
 }
 
-func (s *DockerSuite) TestVolumeCLICreateLabelMultiple(c *check.C) {
+func (s *DockerSuite) TestVolumeCLICreateLabelMultiple(c *testing.T) {
 	testVol := "testvolcreatelabel"
 
 	testLabels := map[string]string{
@@ -326,76 +294,73 @@ func (s *DockerSuite) TestVolumeCLICreateLabelMultiple(c *check.C) {
 	}
 
 	_, _, err := dockerCmdWithError(args...)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	for k, v := range testLabels {
 		out, _ := dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+k+" }}", testVol)
-		c.Assert(strings.TrimSpace(out), check.Equals, v)
+		assert.Equal(c, strings.TrimSpace(out), v)
 	}
 }
 
-func (s *DockerSuite) TestVolumeCLILsFilterLabels(c *check.C) {
+func (s *DockerSuite) TestVolumeCLILsFilterLabels(c *testing.T) {
 	testVol1 := "testvolcreatelabel-1"
 	_, _, err := dockerCmdWithError("volume", "create", "--label", "foo=bar1", testVol1)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	testVol2 := "testvolcreatelabel-2"
 	_, _, err = dockerCmdWithError("volume", "create", "--label", "foo=bar2", testVol2)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	out, _ := dockerCmd(c, "volume", "ls", "--filter", "label=foo")
 
 	// filter with label=key
-	c.Assert(out, checker.Contains, "testvolcreatelabel-1\n", check.Commentf("expected volume 'testvolcreatelabel-1' in output"))
-	c.Assert(out, checker.Contains, "testvolcreatelabel-2\n", check.Commentf("expected volume 'testvolcreatelabel-2' in output"))
-
+	assert.Assert(c, strings.Contains(out, "testvolcreatelabel-1\n"), "expected volume 'testvolcreatelabel-1' in output")
+	assert.Assert(c, strings.Contains(out, "testvolcreatelabel-2\n"), "expected volume 'testvolcreatelabel-2' in output")
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo=bar1")
 
 	// filter with label=key=value
-	c.Assert(out, checker.Contains, "testvolcreatelabel-1\n", check.Commentf("expected volume 'testvolcreatelabel-1' in output"))
-	c.Assert(out, check.Not(checker.Contains), "testvolcreatelabel-2\n", check.Commentf("expected volume 'testvolcreatelabel-2 in output"))
-
+	assert.Assert(c, strings.Contains(out, "testvolcreatelabel-1\n"), "expected volume 'testvolcreatelabel-1' in output")
+	assert.Assert(c, !strings.Contains(out, "testvolcreatelabel-2\n"), "expected volume 'testvolcreatelabel-2 in output")
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=non-exist")
 	outArr := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
+	assert.Equal(c, len(outArr), 1, fmt.Sprintf("\n%s", out))
 
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "label=foo=non-exist")
 	outArr = strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
+	assert.Equal(c, len(outArr), 1, fmt.Sprintf("\n%s", out))
 }
 
-func (s *DockerSuite) TestVolumeCLILsFilterDrivers(c *check.C) {
+func (s *DockerSuite) TestVolumeCLILsFilterDrivers(c *testing.T) {
 	// using default volume driver local to create volumes
 	testVol1 := "testvol-1"
 	_, _, err := dockerCmdWithError("volume", "create", testVol1)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	testVol2 := "testvol-2"
 	_, _, err = dockerCmdWithError("volume", "create", testVol2)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	// filter with driver=local
 	out, _ := dockerCmd(c, "volume", "ls", "--filter", "driver=local")
-	c.Assert(out, checker.Contains, "testvol-1\n", check.Commentf("expected volume 'testvol-1' in output"))
-	c.Assert(out, checker.Contains, "testvol-2\n", check.Commentf("expected volume 'testvol-2' in output"))
-
+	assert.Assert(c, strings.Contains(out, "testvol-1\n"), "expected volume 'testvol-1' in output")
+	assert.Assert(c, strings.Contains(out, "testvol-2\n"), "expected volume 'testvol-2' in output")
 	// filter with driver=invaliddriver
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=invaliddriver")
 	outArr := strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
+	assert.Equal(c, len(outArr), 1, fmt.Sprintf("\n%s", out))
 
 	// filter with driver=loca
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=loca")
 	outArr = strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
+	assert.Equal(c, len(outArr), 1, fmt.Sprintf("\n%s", out))
 
 	// filter with driver=
 	out, _ = dockerCmd(c, "volume", "ls", "--filter", "driver=")
 	outArr = strings.Split(strings.TrimSpace(out), "\n")
-	c.Assert(len(outArr), check.Equals, 1, check.Commentf("\n%s", out))
+	assert.Equal(c, len(outArr), 1, fmt.Sprintf("\n%s", out))
 }
 
-func (s *DockerSuite) TestVolumeCLIRmForceUsage(c *check.C) {
+func (s *DockerSuite) TestVolumeCLIRmForceUsage(c *testing.T) {
 	out, _ := dockerCmd(c, "volume", "create")
 	id := strings.TrimSpace(out)
 
@@ -403,77 +368,74 @@ func (s *DockerSuite) TestVolumeCLIRmForceUsage(c *check.C) {
 	dockerCmd(c, "volume", "rm", "--force", "nonexist")
 }
 
-func (s *DockerSuite) TestVolumeCLIRmForce(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerSuite) TestVolumeCLIRmForce(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	name := "test"
 	out, _ := dockerCmd(c, "volume", "create", name)
 	id := strings.TrimSpace(out)
-	c.Assert(id, checker.Equals, name)
+	assert.Equal(c, id, name)
 
 	out, _ = dockerCmd(c, "volume", "inspect", "--format", "{{.Mountpoint}}", name)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
+	assert.Assert(c, strings.TrimSpace(out) != "")
 	// Mountpoint is in the form of "/var/lib/docker/volumes/.../_data", removing `/_data`
 	path := strings.TrimSuffix(strings.TrimSpace(out), "/_data")
 	icmd.RunCommand("rm", "-rf", path).Assert(c, icmd.Success)
 
 	dockerCmd(c, "volume", "rm", "-f", name)
 	out, _ = dockerCmd(c, "volume", "ls")
-	c.Assert(out, checker.Not(checker.Contains), name)
+	assert.Assert(c, !strings.Contains(out, name))
 	dockerCmd(c, "volume", "create", name)
 	out, _ = dockerCmd(c, "volume", "ls")
-	c.Assert(out, checker.Contains, name)
+	assert.Assert(c, strings.Contains(out, name))
 }
 
 // TestVolumeCLIRmForceInUse verifies that repeated `docker volume rm -f` calls does not remove a volume
 // if it is in use. Test case for https://github.com/docker/docker/issues/31446
-func (s *DockerSuite) TestVolumeCLIRmForceInUse(c *check.C) {
+func (s *DockerSuite) TestVolumeCLIRmForceInUse(c *testing.T) {
 	name := "testvolume"
 	out, _ := dockerCmd(c, "volume", "create", name)
 	id := strings.TrimSpace(out)
-	c.Assert(id, checker.Equals, name)
+	assert.Equal(c, id, name)
 
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	out, _ = dockerCmd(c, "create", "-v", "testvolume:"+prefix+slash+"foo", "busybox")
 	cid := strings.TrimSpace(out)
 
 	_, _, err := dockerCmdWithError("volume", "rm", "-f", name)
-	c.Assert(err, check.NotNil)
-	c.Assert(err.Error(), checker.Contains, "volume is in use")
+	assert.ErrorContains(c, err, "")
+	assert.ErrorContains(c, err, "volume is in use")
 	out, _ = dockerCmd(c, "volume", "ls")
-	c.Assert(out, checker.Contains, name)
-
+	assert.Assert(c, strings.Contains(out, name))
 	// The original issue did not _remove_ the volume from the list
 	// the first time. But a second call to `volume rm` removed it.
 	// Calling `volume rm` a second time to confirm it's not removed
 	// when calling twice.
 	_, _, err = dockerCmdWithError("volume", "rm", "-f", name)
-	c.Assert(err, check.NotNil)
-	c.Assert(err.Error(), checker.Contains, "volume is in use")
+	assert.ErrorContains(c, err, "")
+	assert.ErrorContains(c, err, "volume is in use")
 	out, _ = dockerCmd(c, "volume", "ls")
-	c.Assert(out, checker.Contains, name)
-
+	assert.Assert(c, strings.Contains(out, name))
 	// Verify removing the volume after the container is removed works
 	_, e := dockerCmd(c, "rm", cid)
-	c.Assert(e, check.Equals, 0)
+	assert.Equal(c, e, 0)
 
 	_, e = dockerCmd(c, "volume", "rm", "-f", name)
-	c.Assert(e, check.Equals, 0)
+	assert.Equal(c, e, 0)
 
 	out, e = dockerCmd(c, "volume", "ls")
-	c.Assert(e, check.Equals, 0)
-	c.Assert(out, checker.Not(checker.Contains), name)
+	assert.Equal(c, e, 0)
+	assert.Assert(c, !strings.Contains(out, name))
 }
 
-func (s *DockerSuite) TestVolumeCliInspectWithVolumeOpts(c *check.C) {
+func (s *DockerSuite) TestVolumeCliInspectWithVolumeOpts(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	// Without options
 	name := "test1"
 	dockerCmd(c, "volume", "create", "-d", "local", name)
 	out, _ := dockerCmd(c, "volume", "inspect", "--format={{ .Options }}", name)
-	c.Assert(strings.TrimSpace(out), checker.Contains, "map[]")
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), "map[]"))
 	// With options
 	name = "test2"
 	k1, v1 := "type", "tmpfs"
@@ -481,13 +443,13 @@ func (s *DockerSuite) TestVolumeCliInspectWithVolumeOpts(c *check.C) {
 	k3, v3 := "o", "size=1m,uid=1000"
 	dockerCmd(c, "volume", "create", "-d", "local", name, "--opt", fmt.Sprintf("%s=%s", k1, v1), "--opt", fmt.Sprintf("%s=%s", k2, v2), "--opt", fmt.Sprintf("%s=%s", k3, v3))
 	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Options }}", name)
-	c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k1, v1))
-	c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k2, v2))
-	c.Assert(strings.TrimSpace(out), checker.Contains, fmt.Sprintf("%s:%s", k3, v3))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), fmt.Sprintf("%s:%s", k1, v1)))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), fmt.Sprintf("%s:%s", k2, v2)))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), fmt.Sprintf("%s:%s", k3, v3)))
 }
 
 // Test case (1) for 21845: duplicate targets for --volumes-from
-func (s *DockerSuite) TestDuplicateMountpointsForVolumesFrom(c *check.C) {
+func (s *DockerSuite) TestDuplicateMountpointsForVolumesFrom(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	image := "vimage"
@@ -500,23 +462,22 @@ func (s *DockerSuite) TestDuplicateMountpointsForVolumesFrom(c *check.C) {
 
 	out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
 	data1 := strings.TrimSpace(out)
-	c.Assert(data1, checker.Not(checker.Equals), "")
+	assert.Assert(c, data1 != "")
 
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
 	data2 := strings.TrimSpace(out)
-	c.Assert(data2, checker.Not(checker.Equals), "")
+	assert.Assert(c, data2 != "")
 
 	// Both volume should exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
-	c.Assert(strings.TrimSpace(out), checker.Contains, data1)
-	c.Assert(strings.TrimSpace(out), checker.Contains, data2)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), data1))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), data2))
 	out, _, err := dockerCmdWithError("run", "--name=app", "--volumes-from=data1", "--volumes-from=data2", "-d", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("Out: %s", out))
+	assert.Assert(c, err == nil, "Out: %s", out)
 
 	// Only the second volume will be referenced, this is backward compatible
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
-	c.Assert(strings.TrimSpace(out), checker.Equals, data2)
+	assert.Equal(c, strings.TrimSpace(out), data2)
 
 	dockerCmd(c, "rm", "-f", "-v", "app")
 	dockerCmd(c, "rm", "-f", "-v", "data1")
@@ -524,12 +485,12 @@ func (s *DockerSuite) TestDuplicateMountpointsForVolumesFrom(c *check.C) {
 
 	// Both volume should not exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), data1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), data2))
 }
 
 // Test case (2) for 21845: duplicate targets for --volumes-from and -v (bind)
-func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndBind(c *check.C) {
+func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndBind(c *testing.T) {
 	testRequires(c, DaemonIsLinux)
 
 	image := "vimage"
@@ -542,39 +503,37 @@ func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndBind(c *check.C)
 
 	out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
 	data1 := strings.TrimSpace(out)
-	c.Assert(data1, checker.Not(checker.Equals), "")
+	assert.Assert(c, data1 != "")
 
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
 	data2 := strings.TrimSpace(out)
-	c.Assert(data2, checker.Not(checker.Equals), "")
+	assert.Assert(c, data2 != "")
 
 	// Both volume should exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
-	c.Assert(strings.TrimSpace(out), checker.Contains, data1)
-	c.Assert(strings.TrimSpace(out), checker.Contains, data2)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), data1))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), data2))
 	// /tmp/data is automatically created, because we are not using the modern mount API here
 	out, _, err := dockerCmdWithError("run", "--name=app", "--volumes-from=data1", "--volumes-from=data2", "-v", "/tmp/data:/tmp/data", "-d", "busybox", "top")
-	c.Assert(err, checker.IsNil, check.Commentf("Out: %s", out))
+	assert.Assert(c, err == nil, "Out: %s", out)
 
 	// No volume will be referenced (mount is /tmp/data), this is backward compatible
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
-
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), data1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), data2))
 	dockerCmd(c, "rm", "-f", "-v", "app")
 	dockerCmd(c, "rm", "-f", "-v", "data1")
 	dockerCmd(c, "rm", "-f", "-v", "data2")
 
 	// Both volume should not exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), data1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), data2))
 }
 
 // Test case (3) for 21845: duplicate targets for --volumes-from and `Mounts` (API only)
-func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c *check.C) {
-	testRequires(c, SameHostDaemon, DaemonIsLinux)
+func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c *testing.T) {
+	testRequires(c, testEnv.IsLocalDaemon, DaemonIsLinux)
 
 	image := "vimage"
 	buildImageSuccessfully(c, image, build.WithDockerfile(`
@@ -586,22 +545,21 @@ func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c *check.C
 
 	out, _ := dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data1")
 	data1 := strings.TrimSpace(out)
-	c.Assert(data1, checker.Not(checker.Equals), "")
+	assert.Assert(c, data1 != "")
 
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "data2")
 	data2 := strings.TrimSpace(out)
-	c.Assert(data2, checker.Not(checker.Equals), "")
+	assert.Assert(c, data2 != "")
 
 	// Both volume should exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
-	c.Assert(strings.TrimSpace(out), checker.Contains, data1)
-	c.Assert(strings.TrimSpace(out), checker.Contains, data2)
-
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), data1))
+	assert.Assert(c, strings.Contains(strings.TrimSpace(out), data2))
 	err := os.MkdirAll("/tmp/data", 0755)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	// Mounts is available in API
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	config := container.Config{
@@ -619,21 +577,20 @@ func (s *DockerSuite) TestDuplicateMountpointsForVolumesFromAndMounts(c *check.C
 			},
 		},
 	}
-	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, "app")
+	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &network.NetworkingConfig{}, nil, "app")
 
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	// No volume will be referenced (mount is /tmp/data), this is backward compatible
 	out, _ = dockerCmd(c, "inspect", "--format", "{{(index .Mounts 0).Name}}", "app")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
-
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), data1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), data2))
 	dockerCmd(c, "rm", "-f", "-v", "app")
 	dockerCmd(c, "rm", "-f", "-v", "data1")
 	dockerCmd(c, "rm", "-f", "-v", "data2")
 
 	// Both volume should not exist
 	out, _ = dockerCmd(c, "volume", "ls", "-q")
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data1)
-	c.Assert(strings.TrimSpace(out), checker.Not(checker.Contains), data2)
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), data1))
+	assert.Assert(c, !strings.Contains(strings.TrimSpace(out), data2))
 }
diff --git a/integration-cli/docker_deprecated_api_v124_test.go b/integration-cli/docker_deprecated_api_v124_test.go
index ffb06da40fab5..ea778fdede33f 100644
--- a/integration-cli/docker_deprecated_api_v124_test.go
+++ b/integration-cli/docker_deprecated_api_v124_test.go
@@ -6,37 +6,38 @@ package main
 import (
 	"net/http"
 	"strings"
+	"testing"
 
 	"github.com/docker/docker/api/types/versions"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 func formatV123StartAPIURL(url string) string {
 	return "/v1.23" + url
 }
 
-func (s *DockerSuite) TestDeprecatedContainerAPIStartHostConfig(c *check.C) {
+func (s *DockerSuite) TestDeprecatedContainerAPIStartHostConfig(c *testing.T) {
 	name := "test-deprecated-api-124"
 	dockerCmd(c, "create", "--name", name, "busybox")
 	config := map[string]interface{}{
 		"Binds": []string{"/aa:/bb"},
 	}
 	res, body, err := request.Post("/containers/"+name+"/start", request.JSONBody(config))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
 		// assertions below won't work before 1.32
 		buf, err := request.ReadBody(body)
-		c.Assert(err, checker.IsNil)
+		assert.NilError(c, err)
 
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
-		c.Assert(string(buf), checker.Contains, "was deprecated since API v1.22")
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
+		assert.Assert(c, strings.Contains(string(buf), "was deprecated since API v1.22"))
 	}
 }
 
-func (s *DockerSuite) TestDeprecatedContainerAPIStartVolumeBinds(c *check.C) {
+func (s *DockerSuite) TestDeprecatedContainerAPIStartVolumeBinds(c *testing.T) {
 	// TODO Windows CI: Investigate further why this fails on Windows to Windows CI.
 	testRequires(c, DaemonIsLinux)
 	path := "/foo"
@@ -50,24 +51,24 @@ func (s *DockerSuite) TestDeprecatedContainerAPIStartVolumeBinds(c *check.C) {
 	}
 
 	res, _, err := request.Post(formatV123StartAPIURL("/containers/create?name="+name), request.JSONBody(config))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusCreated)
 
 	bindPath := RandomTmpDirPath("test", testEnv.OSType)
 	config = map[string]interface{}{
 		"Binds": []string{bindPath + ":" + path},
 	}
 	res, _, err = request.Post(formatV123StartAPIURL("/containers/"+name+"/start"), request.JSONBody(config))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusNoContent)
 
 	pth, err := inspectMountSourceField(name, path)
-	c.Assert(err, checker.IsNil)
-	c.Assert(pth, checker.Equals, bindPath, check.Commentf("expected volume host path to be %s, got %s", bindPath, pth))
+	assert.NilError(c, err)
+	assert.Equal(c, pth, bindPath, "expected volume host path to be %s, got %s", bindPath, pth)
 }
 
 // Test for GH#10618
-func (s *DockerSuite) TestDeprecatedContainerAPIStartDupVolumeBinds(c *check.C) {
+func (s *DockerSuite) TestDeprecatedContainerAPIStartDupVolumeBinds(c *testing.T) {
 	// TODO Windows to Windows CI - Port this
 	testRequires(c, DaemonIsLinux)
 	name := "testdups"
@@ -77,8 +78,8 @@ func (s *DockerSuite) TestDeprecatedContainerAPIStartDupVolumeBinds(c *check.C)
 	}
 
 	res, _, err := request.Post(formatV123StartAPIURL("/containers/create?name="+name), request.JSONBody(config))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusCreated)
 
 	bindPath1 := RandomTmpDirPath("test1", testEnv.OSType)
 	bindPath2 := RandomTmpDirPath("test2", testEnv.OSType)
@@ -87,20 +88,20 @@ func (s *DockerSuite) TestDeprecatedContainerAPIStartDupVolumeBinds(c *check.C)
 		"Binds": []string{bindPath1 + ":/tmp", bindPath2 + ":/tmp"},
 	}
 	res, body, err := request.Post(formatV123StartAPIURL("/containers/"+name+"/start"), request.JSONBody(config))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	buf, err := request.ReadBody(body)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
+		assert.Equal(c, res.StatusCode, http.StatusInternalServerError)
 	} else {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	}
-	c.Assert(string(buf), checker.Contains, "Duplicate mount point", check.Commentf("Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(buf), err))
+	assert.Assert(c, strings.Contains(string(buf), "Duplicate mount point"), "Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(buf), err)
 }
 
-func (s *DockerSuite) TestDeprecatedContainerAPIStartVolumesFrom(c *check.C) {
+func (s *DockerSuite) TestDeprecatedContainerAPIStartVolumesFrom(c *testing.T) {
 	// TODO Windows to Windows CI - Port this
 	testRequires(c, DaemonIsLinux)
 	volName := "voltst"
@@ -115,45 +116,45 @@ func (s *DockerSuite) TestDeprecatedContainerAPIStartVolumesFrom(c *check.C) {
 	}
 
 	res, _, err := request.Post(formatV123StartAPIURL("/containers/create?name="+name), request.JSONBody(config))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusCreated)
 
 	config = map[string]interface{}{
 		"VolumesFrom": []string{volName},
 	}
 	res, _, err = request.Post(formatV123StartAPIURL("/containers/"+name+"/start"), request.JSONBody(config))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusNoContent)
 
 	pth, err := inspectMountSourceField(name, volPath)
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	pth2, err := inspectMountSourceField(volName, volPath)
-	c.Assert(err, checker.IsNil)
-	c.Assert(pth, checker.Equals, pth2, check.Commentf("expected volume host path to be %s, got %s", pth, pth2))
+	assert.NilError(c, err)
+	assert.Equal(c, pth, pth2, "expected volume host path to be %s, got %s", pth, pth2)
 }
 
 // #9981 - Allow a docker created volume (ie, one in /var/lib/docker/volumes) to be used to overwrite (via passing in Binds on api start) an existing volume
-func (s *DockerSuite) TestDeprecatedPostContainerBindNormalVolume(c *check.C) {
+func (s *DockerSuite) TestDeprecatedPostContainerBindNormalVolume(c *testing.T) {
 	// TODO Windows to Windows CI - Port this
 	testRequires(c, DaemonIsLinux)
 	dockerCmd(c, "create", "-v", "/foo", "--name=one", "busybox")
 
 	fooDir, err := inspectMountSourceField("one", "/foo")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 
 	dockerCmd(c, "create", "-v", "/foo", "--name=two", "busybox")
 
 	bindSpec := map[string][]string{"Binds": {fooDir + ":/foo"}}
 	res, _, err := request.Post(formatV123StartAPIURL("/containers/two/start"), request.JSONBody(bindSpec))
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusNoContent)
 
 	fooDir2, err := inspectMountSourceField("two", "/foo")
-	c.Assert(err, checker.IsNil)
-	c.Assert(fooDir2, checker.Equals, fooDir, check.Commentf("expected volume path to be %s, got: %s", fooDir, fooDir2))
+	assert.NilError(c, err)
+	assert.Equal(c, fooDir2, fooDir, "expected volume path to be %s, got: %s", fooDir, fooDir2)
 }
 
-func (s *DockerSuite) TestDeprecatedStartWithTooLowMemoryLimit(c *check.C) {
+func (s *DockerSuite) TestDeprecatedStartWithTooLowMemoryLimit(c *testing.T) {
 	// TODO Windows: Port once memory is supported
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "create", "busybox")
@@ -166,19 +167,19 @@ func (s *DockerSuite) TestDeprecatedStartWithTooLowMemoryLimit(c *check.C) {
         }`
 
 	res, body, err := request.Post(formatV123StartAPIURL("/containers/"+containerID+"/start"), request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
-	b, err2 := request.ReadBody(body)
-	c.Assert(err2, checker.IsNil)
+	assert.NilError(c, err)
+	b, err := request.ReadBody(body)
+	assert.NilError(c, err)
 	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
+		assert.Equal(c, res.StatusCode, http.StatusInternalServerError)
 	} else {
-		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
+		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
 	}
-	c.Assert(string(b), checker.Contains, "Minimum memory limit allowed is 4MB")
+	assert.Assert(c, is.Contains(string(b), "Minimum memory limit allowed is 6MB"))
 }
 
 // #14640
-func (s *DockerSuite) TestDeprecatedPostContainersStartWithoutLinksInHostConfig(c *check.C) {
+func (s *DockerSuite) TestDeprecatedPostContainersStartWithoutLinksInHostConfig(c *testing.T) {
 	// TODO Windows: Windows doesn't support supplying a hostconfig on start.
 	// An alternate test could be written to validate the negative testing aspect of this
 	testRequires(c, DaemonIsLinux)
@@ -189,13 +190,13 @@ func (s *DockerSuite) TestDeprecatedPostContainersStartWithoutLinksInHostConfig(
 	config := `{"HostConfig":` + hc + `}`
 
 	res, b, err := request.Post(formatV123StartAPIURL("/containers/"+name+"/start"), request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusNoContent)
 	b.Close()
 }
 
 // #14640
-func (s *DockerSuite) TestDeprecatedPostContainersStartWithLinksInHostConfig(c *check.C) {
+func (s *DockerSuite) TestDeprecatedPostContainersStartWithLinksInHostConfig(c *testing.T) {
 	// TODO Windows: Windows doesn't support supplying a hostconfig on start.
 	// An alternate test could be written to validate the negative testing aspect of this
 	testRequires(c, DaemonIsLinux)
@@ -207,13 +208,13 @@ func (s *DockerSuite) TestDeprecatedPostContainersStartWithLinksInHostConfig(c *
 	config := `{"HostConfig":` + hc + `}`
 
 	res, b, err := request.Post(formatV123StartAPIURL("/containers/"+name+"/start"), request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusNoContent)
 	b.Close()
 }
 
 // #14640
-func (s *DockerSuite) TestDeprecatedPostContainersStartWithLinksInHostConfigIdLinked(c *check.C) {
+func (s *DockerSuite) TestDeprecatedPostContainersStartWithLinksInHostConfigIdLinked(c *testing.T) {
 	// Windows does not support links
 	testRequires(c, DaemonIsLinux)
 	name := "test-host-config-links"
@@ -227,12 +228,12 @@ func (s *DockerSuite) TestDeprecatedPostContainersStartWithLinksInHostConfigIdLi
 	config := `{"HostConfig":` + hc + `}`
 
 	res, b, err := request.Post(formatV123StartAPIURL("/containers/"+name+"/start"), request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusNoContent)
 	b.Close()
 }
 
-func (s *DockerSuite) TestDeprecatedStartWithNilDNS(c *check.C) {
+func (s *DockerSuite) TestDeprecatedStartWithNilDNS(c *testing.T) {
 	// TODO Windows: Add once DNS is supported
 	testRequires(c, DaemonIsLinux)
 	out, _ := dockerCmd(c, "create", "busybox")
@@ -241,10 +242,10 @@ func (s *DockerSuite) TestDeprecatedStartWithNilDNS(c *check.C) {
 	config := `{"HostConfig": {"Dns": null}}`
 
 	res, b, err := request.Post(formatV123StartAPIURL("/containers/"+containerID+"/start"), request.RawString(config), request.JSON)
-	c.Assert(err, checker.IsNil)
-	c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent)
+	assert.NilError(c, err)
+	assert.Equal(c, res.StatusCode, http.StatusNoContent)
 	b.Close()
 
 	dns := inspectFieldJSON(c, containerID, "HostConfig.Dns")
-	c.Assert(dns, checker.Equals, "[]")
+	assert.Equal(c, dns, "[]")
 }
diff --git a/integration-cli/docker_deprecated_api_v124_unix_test.go b/integration-cli/docker_deprecated_api_v124_unix_test.go
index c182b2a7aa8c6..f8665f58b1e82 100644
--- a/integration-cli/docker_deprecated_api_v124_unix_test.go
+++ b/integration-cli/docker_deprecated_api_v124_unix_test.go
@@ -1,17 +1,18 @@
+//go:build !windows
 // +build !windows
 
 package main
 
 import (
-	"fmt"
+	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
 )
 
 // #19100 This is a deprecated feature test, it should be removed in Docker 1.12
-func (s *DockerNetworkSuite) TestDeprecatedDockerNetworkStartAPIWithHostconfig(c *check.C) {
+func (s *DockerNetworkSuite) TestDeprecatedDockerNetworkStartAPIWithHostconfig(c *testing.T) {
 	netName := "test"
 	conName := "foo"
 	dockerCmd(c, "network", "create", netName)
@@ -23,9 +24,9 @@ func (s *DockerNetworkSuite) TestDeprecatedDockerNetworkStartAPIWithHostconfig(c
 		},
 	}
 	_, _, err := request.Post(formatV123StartAPIURL("/containers/"+conName+"/start"), request.JSONBody(config))
-	c.Assert(err, checker.IsNil)
-	c.Assert(waitRun(conName), checker.IsNil)
+	assert.NilError(c, err)
+	assert.NilError(c, waitRun(conName))
 	networks := inspectField(c, conName, "NetworkSettings.Networks")
-	c.Assert(networks, checker.Contains, netName, check.Commentf(fmt.Sprintf("Should contain '%s' network", netName)))
-	c.Assert(networks, checker.Not(checker.Contains), "bridge", check.Commentf("Should not contain 'bridge' network"))
+	assert.Assert(c, strings.Contains(networks, netName), "Should contain '%s' network", netName)
+	assert.Assert(c, !strings.Contains(networks, "bridge"), "Should not contain 'bridge' network")
 }
diff --git a/integration-cli/docker_hub_pull_suite_test.go b/integration-cli/docker_hub_pull_suite_test.go
index 125b8c10aa765..dbc6e9478549f 100644
--- a/integration-cli/docker_hub_pull_suite_test.go
+++ b/integration-cli/docker_hub_pull_suite_test.go
@@ -2,23 +2,14 @@ package main
 
 import (
 	"os/exec"
-	"runtime"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/daemon"
-	testdaemon "github.com/docker/docker/internal/test/daemon"
-	"github.com/go-check/check"
+	testdaemon "github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
 )
 
-func init() {
-	// FIXME. Temporarily turning this off for Windows as GH16039 was breaking
-	// Windows to Linux CI @icecrime
-	if runtime.GOOS != "windows" {
-		check.Suite(newDockerHubPullSuite())
-	}
-}
-
 // DockerHubPullSuite provides an isolated daemon that doesn't have all the
 // images that are baked into our 'global' test environment daemon (e.g.,
 // busybox, httpserver, ...).
@@ -39,26 +30,26 @@ func newDockerHubPullSuite() *DockerHubPullSuite {
 }
 
 // SetUpSuite starts the suite daemon.
-func (s *DockerHubPullSuite) SetUpSuite(c *check.C) {
-	testRequires(c, DaemonIsLinux, SameHostDaemon)
+func (s *DockerHubPullSuite) SetUpSuite(c *testing.T) {
+	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
 	s.d = daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution))
 	s.d.Start(c)
 }
 
 // TearDownSuite stops the suite daemon.
-func (s *DockerHubPullSuite) TearDownSuite(c *check.C) {
+func (s *DockerHubPullSuite) TearDownSuite(c *testing.T) {
 	if s.d != nil {
 		s.d.Stop(c)
 	}
 }
 
 // SetUpTest declares that all tests of this suite require network.
-func (s *DockerHubPullSuite) SetUpTest(c *check.C) {
+func (s *DockerHubPullSuite) SetUpTest(c *testing.T) {
 	testRequires(c, Network)
 }
 
 // TearDownTest removes all images from the suite daemon.
-func (s *DockerHubPullSuite) TearDownTest(c *check.C) {
+func (s *DockerHubPullSuite) TearDownTest(c *testing.T) {
 	out := s.Cmd(c, "images", "-aq")
 	images := strings.Split(out, "\n")
 	images = append([]string{"rmi", "-f"}, images...)
@@ -68,9 +59,9 @@ func (s *DockerHubPullSuite) TearDownTest(c *check.C) {
 
 // Cmd executes a command against the suite daemon and returns the combined
 // output. The function fails the test when the command returns an error.
-func (s *DockerHubPullSuite) Cmd(c *check.C, name string, arg ...string) string {
+func (s *DockerHubPullSuite) Cmd(c *testing.T, name string, arg ...string) string {
 	out, err := s.CmdWithError(name, arg...)
-	c.Assert(err, checker.IsNil, check.Commentf("%q failed with errors: %s, %v", strings.Join(arg, " "), out, err))
+	assert.Assert(c, err == nil, "%q failed with errors: %s, %v", strings.Join(arg, " "), out, err)
 	return out
 }
 
diff --git a/integration-cli/docker_utils_test.go b/integration-cli/docker_utils_test.go
index 1c05bf5d04ac4..f8c8fcdca88ac 100644
--- a/integration-cli/docker_utils_test.go
+++ b/integration-cli/docker_utils_test.go
@@ -6,29 +6,24 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"path"
 	"path/filepath"
 	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
 	"github.com/docker/docker/integration-cli/cli"
 	"github.com/docker/docker/integration-cli/daemon"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/go-check/check"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/poll"
 )
 
-// Deprecated
-func daemonHost() string {
-	return request.DaemonHost()
-}
-
 func deleteImages(images ...string) error {
 	args := []string{dockerBinary, "rmi", "-f"}
 	return icmd.RunCmd(icmd.Cmd{Command: append(args, images...)}).Error
@@ -44,7 +39,8 @@ func dockerCmdWithError(args ...string) (string, int, error) {
 }
 
 // Deprecated: use cli.Docker or cli.DockerCmd
-func dockerCmd(c *check.C, args ...string) (string, int) {
+func dockerCmd(c testing.TB, args ...string) (string, int) {
+	c.Helper()
 	result := cli.DockerCmd(c, args...)
 	return result.Combined(), result.ExitCode
 }
@@ -54,12 +50,14 @@ func dockerCmdWithResult(args ...string) *icmd.Result {
 	return cli.Docker(cli.Args(args...))
 }
 
-func findContainerIP(c *check.C, id string, network string) string {
+func findContainerIP(c *testing.T, id string, network string) string {
+	c.Helper()
 	out, _ := dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.IPAddress }}'", network), id)
 	return strings.Trim(out, " \r\n'")
 }
 
-func getContainerCount(c *check.C) int {
+func getContainerCount(c *testing.T) int {
+	c.Helper()
 	const containers = "Containers:"
 
 	result := icmd.RunCommand(dockerBinary, "info")
@@ -69,21 +67,22 @@ func getContainerCount(c *check.C) int {
 	for _, line := range lines {
 		if strings.Contains(line, containers) {
 			output := strings.TrimSpace(line)
-			output = strings.TrimLeft(output, containers)
+			output = strings.TrimPrefix(output, containers)
 			output = strings.Trim(output, " ")
 			containerCount, err := strconv.Atoi(output)
-			c.Assert(err, checker.IsNil)
+			assert.NilError(c, err)
 			return containerCount
 		}
 	}
 	return 0
 }
 
-func inspectFieldAndUnmarshall(c *check.C, name, field string, output interface{}) {
+func inspectFieldAndUnmarshall(c *testing.T, name, field string, output interface{}) {
+	c.Helper()
 	str := inspectFieldJSON(c, name, field)
 	err := json.Unmarshal([]byte(str), output)
 	if c != nil {
-		c.Assert(err, check.IsNil, check.Commentf("failed to unmarshal: %v", err))
+		assert.Assert(c, err == nil, "failed to unmarshal: %v", err)
 	}
 }
 
@@ -103,28 +102,31 @@ func inspectFieldWithError(name, field string) (string, error) {
 }
 
 // Deprecated: use cli.Inspect
-func inspectField(c *check.C, name, field string) string {
+func inspectField(c *testing.T, name, field string) string {
+	c.Helper()
 	out, err := inspectFilter(name, fmt.Sprintf(".%s", field))
 	if c != nil {
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	}
 	return out
 }
 
 // Deprecated: use cli.Inspect
-func inspectFieldJSON(c *check.C, name, field string) string {
+func inspectFieldJSON(c *testing.T, name, field string) string {
+	c.Helper()
 	out, err := inspectFilter(name, fmt.Sprintf("json .%s", field))
 	if c != nil {
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	}
 	return out
 }
 
 // Deprecated: use cli.Inspect
-func inspectFieldMap(c *check.C, name, path, field string) string {
+func inspectFieldMap(c *testing.T, name, path, field string) string {
+	c.Helper()
 	out, err := inspectFilter(name, fmt.Sprintf("index .%s %q", path, field))
 	if c != nil {
-		c.Assert(err, check.IsNil)
+		assert.NilError(c, err)
 	}
 	return out
 }
@@ -172,27 +174,16 @@ func inspectMountPointJSON(j, destination string) (types.MountPoint, error) {
 	return *m, nil
 }
 
-// Deprecated: use cli.Inspect
-func inspectImage(c *check.C, name, filter string) string {
-	args := []string{"inspect", "--type", "image"}
-	if filter != "" {
-		format := fmt.Sprintf("{{%s}}", filter)
-		args = append(args, "-f", format)
-	}
-	args = append(args, name)
-	result := icmd.RunCommand(dockerBinary, args...)
-	result.Assert(c, icmd.Success)
-	return strings.TrimSpace(result.Combined())
-}
-
-func getIDByName(c *check.C, name string) string {
+func getIDByName(c *testing.T, name string) string {
+	c.Helper()
 	id, err := inspectFieldWithError(name, "Id")
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	return id
 }
 
 // Deprecated: use cli.Build
-func buildImageSuccessfully(c *check.C, name string, cmdOperators ...cli.CmdOperator) {
+func buildImageSuccessfully(c *testing.T, name string, cmdOperators ...cli.CmdOperator) {
+	c.Helper()
 	buildImage(name, cmdOperators...).Assert(c, icmd.Success)
 }
 
@@ -205,22 +196,24 @@ func buildImage(name string, cmdOperators ...cli.CmdOperator) *icmd.Result {
 // as well as any missing directories.
 // The file is truncated if it already exists.
 // Fail the test when error occurs.
-func writeFile(dst, content string, c *check.C) {
+func writeFile(dst, content string, c *testing.T) {
+	c.Helper()
 	// Create subdirectories if necessary
-	c.Assert(os.MkdirAll(path.Dir(dst), 0700), check.IsNil)
+	assert.Assert(c, os.MkdirAll(path.Dir(dst), 0700) == nil)
 	f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	defer f.Close()
 	// Write content (truncate if it exists)
 	_, err = io.Copy(f, strings.NewReader(content))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 }
 
 // Return the contents of file at path `src`.
 // Fail the test when error occurs.
-func readFile(src string, c *check.C) (content string) {
-	data, err := ioutil.ReadFile(src)
-	c.Assert(err, check.IsNil)
+func readFile(src string, c *testing.T) (content string) {
+	c.Helper()
+	data, err := os.ReadFile(src)
+	assert.NilError(c, err)
 
 	return string(data)
 }
@@ -230,7 +223,8 @@ func containerStorageFile(containerID, basename string) string {
 }
 
 // docker commands that use this function must be run with the '-d' switch.
-func runCommandAndReadContainerFile(c *check.C, filename string, command string, args ...string) []byte {
+func runCommandAndReadContainerFile(c *testing.T, filename string, command string, args ...string) []byte {
+	c.Helper()
 	result := icmd.RunCommand(command, args...)
 	result.Assert(c, icmd.Success)
 	contID := strings.TrimSpace(result.Combined())
@@ -240,42 +234,46 @@ func runCommandAndReadContainerFile(c *check.C, filename string, command string,
 	return readContainerFile(c, contID, filename)
 }
 
-func readContainerFile(c *check.C, containerID, filename string) []byte {
+func readContainerFile(c *testing.T, containerID, filename string) []byte {
+	c.Helper()
 	f, err := os.Open(containerStorageFile(containerID, filename))
-	c.Assert(err, checker.IsNil)
+	assert.NilError(c, err)
 	defer f.Close()
 
-	content, err := ioutil.ReadAll(f)
-	c.Assert(err, checker.IsNil)
+	content, err := io.ReadAll(f)
+	assert.NilError(c, err)
 	return content
 }
 
-func readContainerFileWithExec(c *check.C, containerID, filename string) []byte {
+func readContainerFileWithExec(c *testing.T, containerID, filename string) []byte {
+	c.Helper()
 	result := icmd.RunCommand(dockerBinary, "exec", containerID, "cat", filename)
 	result.Assert(c, icmd.Success)
 	return []byte(result.Combined())
 }
 
 // daemonTime provides the current time on the daemon host
-func daemonTime(c *check.C) time.Time {
+func daemonTime(c *testing.T) time.Time {
+	c.Helper()
 	if testEnv.IsLocalDaemon() {
 		return time.Now()
 	}
-	cli, err := client.NewEnvClient()
-	c.Assert(err, check.IsNil)
+	cli, err := client.NewClientWithOpts(client.FromEnv)
+	assert.NilError(c, err)
 	defer cli.Close()
 
 	info, err := cli.Info(context.Background())
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 
 	dt, err := time.Parse(time.RFC3339Nano, info.SystemTime)
-	c.Assert(err, check.IsNil, check.Commentf("invalid time format in GET /info response"))
+	assert.Assert(c, err == nil, "invalid time format in GET /info response")
 	return dt
 }
 
 // daemonUnixTime returns the current time on the daemon host with nanoseconds precision.
 // It return the time formatted how the client sends timestamps to the server.
-func daemonUnixTime(c *check.C) string {
+func daemonUnixTime(c *testing.T) string {
+	c.Helper()
 	return parseEventTime(daemonTime(c))
 }
 
@@ -310,14 +308,15 @@ func appendBaseEnv(isTLS bool, env ...string) []string {
 	return env
 }
 
-func createTmpFile(c *check.C, content string) string {
-	f, err := ioutil.TempFile("", "testfile")
-	c.Assert(err, check.IsNil)
+func createTmpFile(c *testing.T, content string) string {
+	c.Helper()
+	f, err := os.CreateTemp("", "testfile")
+	assert.NilError(c, err)
 
 	filename := f.Name()
 
-	err = ioutil.WriteFile(filename, []byte(content), 0644)
-	c.Assert(err, check.IsNil)
+	err = os.WriteFile(filename, []byte(content), 0644)
+	assert.NilError(c, err)
 
 	return filename
 }
@@ -341,24 +340,27 @@ func waitInspectWithArgs(name, expr, expected string, timeout time.Duration, arg
 	return daemon.WaitInspectWithArgs(dockerBinary, name, expr, expected, timeout, arg...)
 }
 
-func getInspectBody(c *check.C, version, id string) []byte {
+func getInspectBody(c *testing.T, version, id string) []byte {
+	c.Helper()
 	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion(version))
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	defer cli.Close()
 	_, body, err := cli.ContainerInspectWithRaw(context.Background(), id, false)
-	c.Assert(err, check.IsNil)
+	assert.NilError(c, err)
 	return body
 }
 
 // Run a long running idle task in a background container using the
 // system-specific default image and command.
-func runSleepingContainer(c *check.C, extraArgs ...string) string {
-	return runSleepingContainerInImage(c, defaultSleepImage, extraArgs...)
+func runSleepingContainer(c *testing.T, extraArgs ...string) string {
+	c.Helper()
+	return runSleepingContainerInImage(c, "busybox", extraArgs...)
 }
 
 // Run a long running idle task in a background container using the specified
 // image and the system-specific command.
-func runSleepingContainerInImage(c *check.C, image string, extraArgs ...string) string {
+func runSleepingContainerInImage(c *testing.T, image string, extraArgs ...string) string {
+	c.Helper()
 	args := []string{"run", "-d"}
 	args = append(args, extraArgs...)
 	args = append(args, image)
@@ -373,7 +375,7 @@ func minimalBaseImage() string {
 }
 
 func getGoroutineNumber() (int, error) {
-	cli, err := client.NewEnvClient()
+	cli, err := client.NewClientWithOpts(client.FromEnv)
 	if err != nil {
 		return 0, err
 	}
@@ -412,48 +414,50 @@ func waitForGoroutines(expected int) error {
 }
 
 // getErrorMessage returns the error message from an error API response
-func getErrorMessage(c *check.C, body []byte) string {
+func getErrorMessage(c *testing.T, body []byte) string {
+	c.Helper()
 	var resp types.ErrorResponse
-	c.Assert(json.Unmarshal(body, &resp), check.IsNil)
+	assert.Assert(c, json.Unmarshal(body, &resp) == nil)
 	return strings.TrimSpace(resp.Message)
 }
 
-func waitAndAssert(c *check.C, timeout time.Duration, f checkF, checker check.Checker, args ...interface{}) {
-	after := time.After(timeout)
-	for {
-		v, comment := f(c)
-		assert, _ := checker.Check(append([]interface{}{v}, args...), checker.Info().Params)
-		select {
-		case <-after:
-			assert = true
-		default:
-		}
-		if assert {
-			if comment != nil {
-				args = append(args, comment)
+type checkF func(*testing.T) (interface{}, string)
+type reducer func(...interface{}) interface{}
+
+func pollCheck(t *testing.T, f checkF, compare func(x interface{}) assert.BoolOrComparison) poll.Check {
+	return func(poll.LogT) poll.Result {
+		t.Helper()
+		v, comment := f(t)
+		r := compare(v)
+		switch r := r.(type) {
+		case bool:
+			if r {
+				return poll.Success()
 			}
-			c.Assert(v, checker, args...)
-			return
+		case cmp.Comparison:
+			if r().Success() {
+				return poll.Success()
+			}
+		default:
+			panic(fmt.Errorf("pollCheck: type %T not implemented", r))
 		}
-		time.Sleep(100 * time.Millisecond)
+		return poll.Continue(comment)
 	}
 }
 
-type checkF func(*check.C) (interface{}, check.CommentInterface)
-type reducer func(...interface{}) interface{}
-
 func reducedCheck(r reducer, funcs ...checkF) checkF {
-	return func(c *check.C) (interface{}, check.CommentInterface) {
+	return func(c *testing.T) (interface{}, string) {
+		c.Helper()
 		var values []interface{}
 		var comments []string
 		for _, f := range funcs {
 			v, comment := f(c)
 			values = append(values, v)
-			if comment != nil {
-				comments = append(comments, comment.CheckCommentString())
+			if len(comment) > 0 {
+				comments = append(comments, comment)
 			}
 		}
-		return r(values...), check.Commentf("%v", strings.Join(comments, ", "))
+		return r(values...), fmt.Sprintf("%v", strings.Join(comments, ", "))
 	}
 }
 
diff --git a/integration-cli/environment/environment.go b/integration-cli/environment/environment.go
index 82cf99652bc8c..0dcf8d929476b 100644
--- a/integration-cli/environment/environment.go
+++ b/integration-cli/environment/environment.go
@@ -4,7 +4,7 @@ import (
 	"os"
 	"os/exec"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var (
diff --git a/integration-cli/events_utils_test.go b/integration-cli/events_utils_test.go
index 356b2c326da18..6a094ba160217 100644
--- a/integration-cli/events_utils_test.go
+++ b/integration-cli/events_utils_test.go
@@ -8,11 +8,11 @@ import (
 	"regexp"
 	"strconv"
 	"strings"
+	"testing"
 
 	eventstestutils "github.com/docker/docker/daemon/events/testutils"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
 	"github.com/sirupsen/logrus"
+	"gotest.tools/v3/assert"
 )
 
 // eventMatcher is a function that tries to match an event input.
@@ -35,13 +35,13 @@ type eventObserver struct {
 
 // newEventObserver creates the observer and initializes the command
 // without running it. Users must call `eventObserver.Start` to start the command.
-func newEventObserver(c *check.C, args ...string) (*eventObserver, error) {
+func newEventObserver(c *testing.T, args ...string) (*eventObserver, error) {
 	since := daemonTime(c).Unix()
 	return newEventObserverWithBacklog(c, since, args...)
 }
 
 // newEventObserverWithBacklog creates a new observer changing the start time of the backlog to return.
-func newEventObserverWithBacklog(c *check.C, since int64, args ...string) (*eventObserver, error) {
+func newEventObserverWithBacklog(c *testing.T, since int64, args ...string) (*eventObserver, error) {
 	startTime := strconv.FormatInt(since, 10)
 	cmdArgs := []string{"events", "--since", startTime}
 	if len(args) > 0 {
@@ -93,7 +93,7 @@ func (e *eventObserver) Match(match eventMatcher, process eventMatchProcessor) {
 	e.disconnectionError = err
 }
 
-func (e *eventObserver) CheckEventError(c *check.C, id, event string, match eventMatcher) {
+func (e *eventObserver) CheckEventError(c *testing.T, id, event string, match eventMatcher) {
 	var foundEvent bool
 	scannerOut := e.buffer.String()
 
@@ -144,18 +144,18 @@ func processEventMatch(actions map[string]chan bool) eventMatchProcessor {
 
 // parseEventAction parses an event text and returns the action.
 // It fails if the text is not in the event format.
-func parseEventAction(c *check.C, text string) string {
+func parseEventAction(c *testing.T, text string) string {
 	matches := eventstestutils.ScanMap(text)
 	return matches["action"]
 }
 
 // eventActionsByIDAndType returns the actions for a given id and type.
 // It fails if the text is not in the event format.
-func eventActionsByIDAndType(c *check.C, events []string, id, eventType string) []string {
+func eventActionsByIDAndType(c *testing.T, events []string, id, eventType string) []string {
 	var filtered []string
 	for _, event := range events {
 		matches := eventstestutils.ScanMap(event)
-		c.Assert(matches, checker.Not(checker.IsNil))
+		assert.Assert(c, matches != nil)
 		if matchIDAndEventType(matches, id, eventType) {
 			filtered = append(filtered, matches["action"])
 		}
@@ -183,24 +183,12 @@ func matchEventID(matches map[string]string, id string) bool {
 	return matchID
 }
 
-func parseEvents(c *check.C, out, match string) {
+func parseEvents(c *testing.T, out, match string) {
 	events := strings.Split(strings.TrimSpace(out), "\n")
 	for _, event := range events {
 		matches := eventstestutils.ScanMap(event)
 		matched, err := regexp.MatchString(match, matches["action"])
-		c.Assert(err, checker.IsNil)
-		c.Assert(matched, checker.True, check.Commentf("Matcher: %s did not match %s", match, matches["action"]))
-	}
-}
-
-func parseEventsWithID(c *check.C, out, match, id string) {
-	events := strings.Split(strings.TrimSpace(out), "\n")
-	for _, event := range events {
-		matches := eventstestutils.ScanMap(event)
-		c.Assert(matchEventID(matches, id), checker.True)
-
-		matched, err := regexp.MatchString(match, matches["action"])
-		c.Assert(err, checker.IsNil)
-		c.Assert(matched, checker.True, check.Commentf("Matcher: %s did not match %s", match, matches["action"]))
+		assert.NilError(c, err)
+		assert.Assert(c, matched, "Matcher: %s did not match %s", match, matches["action"])
 	}
 }
diff --git a/integration-cli/fixtures/auth/docker-credential-shell-test b/integration-cli/fixtures/auth/docker-credential-shell-test
index 97b3f1483e691..5acab521795ee 100755
--- a/integration-cli/fixtures/auth/docker-credential-shell-test
+++ b/integration-cli/fixtures/auth/docker-credential-shell-test
@@ -6,7 +6,7 @@ listFile=shell_test_list.json
 
 case $1 in
 	"store")
-		in=$( $TEMP/$listFile
 		else
-			list=$(<$TEMP/$listFile)
+			list=$(< $TEMP/$listFile)
 			echo "$list" | jq ". + {\"${server}\": \"${username}\"}" > $TEMP/$listFile
 		fi
 		;;
 	"get")
-		in=$( $TEMP/$listFile
 		;;
 	"list")
 		if [[ ! -f $TEMP/$listFile ]]; then
 			echo "{}"
 		else
-			payload=$(<$TEMP/$listFile)
+			payload=$(< $TEMP/$listFile)
 			echo "$payload"
 		fi
 		;;
diff --git a/integration-cli/fixtures_linux_daemon_test.go b/integration-cli/fixtures_linux_daemon_test.go
index 2387a9ebee2ba..b91b510c1fcab 100644
--- a/integration-cli/fixtures_linux_daemon_test.go
+++ b/integration-cli/fixtures_linux_daemon_test.go
@@ -2,39 +2,24 @@ package main
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"runtime"
 	"strings"
-	"sync"
+	"testing"
 
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/docker/docker/internal/test/fixtures/load"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil/fixtures/load"
+	"gotest.tools/v3/assert"
 )
 
-type testingT interface {
-	logT
-	Fatalf(string, ...interface{})
-}
-
-type logT interface {
-	Logf(string, ...interface{})
-}
-
-var ensureSyscallTestOnce sync.Once
+func ensureSyscallTest(c *testing.T) {
+	defer testEnv.ProtectImage(c, "syscall-test:latest")
 
-func ensureSyscallTest(c *check.C) {
-	var doIt bool
-	ensureSyscallTestOnce.Do(func() {
-		doIt = true
-	})
-	if !doIt {
+	// If the image already exists, there's nothing left to do.
+	if testEnv.HasExistingImage(c, "syscall-test:latest") {
 		return
 	}
-	defer testEnv.ProtectImage(c, "syscall-test:latest")
 
 	// if no match, must build in docker, which is significantly slower
 	// (slower mostly because of the vfs graphdriver)
@@ -43,31 +28,31 @@ func ensureSyscallTest(c *check.C) {
 		return
 	}
 
-	tmp, err := ioutil.TempDir("", "syscall-test-build")
-	c.Assert(err, checker.IsNil, check.Commentf("couldn't create temp dir"))
+	tmp, err := os.MkdirTemp("", "syscall-test-build")
+	assert.NilError(c, err, "couldn't create temp dir")
 	defer os.RemoveAll(tmp)
 
 	gcc, err := exec.LookPath("gcc")
-	c.Assert(err, checker.IsNil, check.Commentf("could not find gcc"))
+	assert.NilError(c, err, "could not find gcc")
 
 	tests := []string{"userns", "ns", "acct", "setuid", "setgid", "socket", "raw"}
 	for _, test := range tests {
 		out, err := exec.Command(gcc, "-g", "-Wall", "-static", fmt.Sprintf("../contrib/syscall-test/%s.c", test), "-o", fmt.Sprintf("%s/%s-test", tmp, test)).CombinedOutput()
-		c.Assert(err, checker.IsNil, check.Commentf(string(out)))
+		assert.NilError(c, err, string(out))
 	}
 
 	if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" {
 		out, err := exec.Command(gcc, "-s", "-m32", "-nostdlib", "-static", "../contrib/syscall-test/exit32.s", "-o", tmp+"/"+"exit32-test").CombinedOutput()
-		c.Assert(err, checker.IsNil, check.Commentf(string(out)))
+		assert.NilError(c, err, string(out))
 	}
 
 	dockerFile := filepath.Join(tmp, "Dockerfile")
 	content := []byte(`
-	FROM debian:jessie
+	FROM debian:bullseye-slim
 	COPY . /usr/bin/
 	`)
-	err = ioutil.WriteFile(dockerFile, content, 600)
-	c.Assert(err, checker.IsNil)
+	err = os.WriteFile(dockerFile, content, 0600)
+	assert.NilError(c, err)
 
 	var buildArgs []string
 	if arg := os.Getenv("DOCKER_BUILD_ARGS"); strings.TrimSpace(arg) != "" {
@@ -78,9 +63,9 @@ func ensureSyscallTest(c *check.C) {
 	dockerCmd(c, buildArgs...)
 }
 
-func ensureSyscallTestBuild(c *check.C) {
-	err := load.FrozenImagesLinux(testEnv.APIClient(), "buildpack-deps:jessie")
-	c.Assert(err, checker.IsNil)
+func ensureSyscallTestBuild(c *testing.T) {
+	err := load.FrozenImagesLinux(testEnv.APIClient(), "debian:bullseye-slim")
+	assert.NilError(c, err)
 
 	var buildArgs []string
 	if arg := os.Getenv("DOCKER_BUILD_ARGS"); strings.TrimSpace(arg) != "" {
@@ -91,30 +76,38 @@ func ensureSyscallTestBuild(c *check.C) {
 	dockerCmd(c, buildArgs...)
 }
 
-func ensureNNPTest(c *check.C) {
+func ensureNNPTest(c *testing.T) {
 	defer testEnv.ProtectImage(c, "nnp-test:latest")
+
+	// If the image already exists, there's nothing left to do.
+	if testEnv.HasExistingImage(c, "nnp-test:latest") {
+		return
+	}
+
+	// if no match, must build in docker, which is significantly slower
+	// (slower mostly because of the vfs graphdriver)
 	if testEnv.OSType != runtime.GOOS {
 		ensureNNPTestBuild(c)
 		return
 	}
 
-	tmp, err := ioutil.TempDir("", "docker-nnp-test")
-	c.Assert(err, checker.IsNil)
+	tmp, err := os.MkdirTemp("", "docker-nnp-test")
+	assert.NilError(c, err)
 
 	gcc, err := exec.LookPath("gcc")
-	c.Assert(err, checker.IsNil, check.Commentf("could not find gcc"))
+	assert.NilError(c, err, "could not find gcc")
 
 	out, err := exec.Command(gcc, "-g", "-Wall", "-static", "../contrib/nnp-test/nnp-test.c", "-o", filepath.Join(tmp, "nnp-test")).CombinedOutput()
-	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
+	assert.NilError(c, err, string(out))
 
 	dockerfile := filepath.Join(tmp, "Dockerfile")
 	content := `
-	FROM debian:jessie
+	FROM debian:bullseye-slim
 	COPY . /usr/bin
 	RUN chmod +s /usr/bin/nnp-test
 	`
-	err = ioutil.WriteFile(dockerfile, []byte(content), 600)
-	c.Assert(err, checker.IsNil, check.Commentf("could not write Dockerfile for nnp-test image"))
+	err = os.WriteFile(dockerfile, []byte(content), 0600)
+	assert.NilError(c, err, "could not write Dockerfile for nnp-test image")
 
 	var buildArgs []string
 	if arg := os.Getenv("DOCKER_BUILD_ARGS"); strings.TrimSpace(arg) != "" {
@@ -125,9 +118,9 @@ func ensureNNPTest(c *check.C) {
 	dockerCmd(c, buildArgs...)
 }
 
-func ensureNNPTestBuild(c *check.C) {
-	err := load.FrozenImagesLinux(testEnv.APIClient(), "buildpack-deps:jessie")
-	c.Assert(err, checker.IsNil)
+func ensureNNPTestBuild(c *testing.T) {
+	err := load.FrozenImagesLinux(testEnv.APIClient(), "debian:bullseye-slim")
+	assert.NilError(c, err)
 
 	var buildArgs []string
 	if arg := os.Getenv("DOCKER_BUILD_ARGS"); strings.TrimSpace(arg) != "" {
diff --git a/integration-cli/requirement/requirement.go b/integration-cli/requirement/requirement.go
index 45a1bcabfd914..f17e5291b7888 100644
--- a/integration-cli/requirement/requirement.go
+++ b/integration-cli/requirement/requirement.go
@@ -1,29 +1,25 @@
 package requirement // import "github.com/docker/docker/integration-cli/requirement"
 
 import (
-	"fmt"
 	"path"
 	"reflect"
 	"runtime"
 	"strings"
+	"testing"
 )
 
-// SkipT is the interface required to skip tests
-type SkipT interface {
-	Skip(reason string)
-}
-
 // Test represent a function that can be used as a requirement validation.
 type Test func() bool
 
 // Is checks if the environment satisfies the requirements
 // for the test to run or skips the tests.
-func Is(s SkipT, requirements ...Test) {
+func Is(t *testing.T, requirements ...Test) {
+	t.Helper()
 	for _, r := range requirements {
 		isValid := r()
 		if !isValid {
 			requirementFunc := runtime.FuncForPC(reflect.ValueOf(r).Pointer()).Name()
-			s.Skip(fmt.Sprintf("unmatched requirement %s", extractRequirement(requirementFunc)))
+			t.Skipf("unmatched requirement %s", extractRequirement(requirementFunc))
 		}
 	}
 }
diff --git a/integration-cli/requirements_test.go b/integration-cli/requirements_test.go
index 194a9b22b8d2d..1f466081192bb 100644
--- a/integration-cli/requirements_test.go
+++ b/integration-cli/requirements_test.go
@@ -3,12 +3,12 @@ package main
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"os"
 	"os/exec"
 	"strconv"
 	"strings"
+	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
@@ -16,7 +16,7 @@ import (
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration-cli/requirement"
-	"github.com/docker/docker/internal/test/registry"
+	"github.com/docker/docker/testutil/registry"
 )
 
 func ArchitectureIsNot(arch string) bool {
@@ -49,7 +49,7 @@ func MinimumAPIVersion(version string) func() bool {
 }
 
 func OnlyDefaultNetworks() bool {
-	cli, err := client.NewEnvClient()
+	cli, err := client.NewClientWithOpts(client.FromEnv)
 	if err != nil {
 		return false
 	}
@@ -60,11 +60,6 @@ func OnlyDefaultNetworks() bool {
 	return true
 }
 
-// Deprecated: use skip.If(t, !testEnv.DaemonInfo.ExperimentalBuild)
-func ExperimentalDaemon() bool {
-	return testEnv.DaemonInfo.ExperimentalBuild
-}
-
 func IsAmd64() bool {
 	return os.Getenv("DOCKER_ENGINE_GOARCH") == "amd64"
 }
@@ -81,26 +76,14 @@ func NotPpc64le() bool {
 	return ArchitectureIsNot("ppc64le")
 }
 
-func NotS390X() bool {
-	return ArchitectureIsNot("s390x")
-}
-
-func SameHostDaemon() bool {
-	return testEnv.IsLocalDaemon()
-}
-
 func UnixCli() bool {
 	return isUnixCli
 }
 
-func ExecSupport() bool {
-	return supportsExec
-}
-
 func Network() bool {
 	// Set a timeout on the GET at 15s
-	var timeout = time.Duration(15 * time.Second)
-	var url = "https://hub.docker.com"
+	const timeout = 15 * time.Second
+	const url = "https://hub.docker.com"
 
 	client := http.Client{
 		Timeout: timeout,
@@ -120,7 +103,7 @@ func Apparmor() bool {
 	if strings.HasPrefix(testEnv.DaemonInfo.OperatingSystem, "SUSE Linux Enterprise Server ") {
 		return false
 	}
-	buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled")
+	buf, err := os.ReadFile("/sys/module/apparmor/parameters/enabled")
 	return err == nil && len(buf) > 1 && buf[0] == 'Y'
 }
 
@@ -176,13 +159,6 @@ func IsPausable() bool {
 	return true
 }
 
-func NotPausable() bool {
-	if testEnv.OSType == "windows" {
-		return testEnv.DaemonInfo.Isolation == "process"
-	}
-	return false
-}
-
 func IsolationIs(expectedIsolation string) bool {
 	return testEnv.OSType == "windows" && string(testEnv.DaemonInfo.Isolation) == expectedIsolation
 }
@@ -204,6 +180,10 @@ func RegistryHosting() bool {
 	return err == nil
 }
 
+func RuntimeIsWindowsContainerd() bool {
+	return os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME") == "1"
+}
+
 func SwarmInactive() bool {
 	return testEnv.DaemonInfo.Swarm.LocalNodeState == swarm.LocalNodeStateInactive
 }
@@ -212,8 +192,18 @@ func TODOBuildkit() bool {
 	return os.Getenv("DOCKER_BUILDKIT") == ""
 }
 
+func DockerCLIVersion(t testing.TB) string {
+	out, _ := dockerCmd(t, "--version")
+	version := strings.Fields(out)
+	if len(version) < 3 {
+		t.Fatal("unknown version output", version)
+	}
+	return version[2]
+}
+
 // testRequires checks if the environment satisfies the requirements
 // for the test to run or skips the tests.
-func testRequires(c requirement.SkipT, requirements ...requirement.Test) {
-	requirement.Is(c, requirements...)
+func testRequires(t *testing.T, requirements ...requirement.Test) {
+	t.Helper()
+	requirement.Is(t, requirements...)
 }
diff --git a/integration-cli/requirements_unix_test.go b/integration-cli/requirements_unix_test.go
index 7c594f7db481e..b0cc0e8f11fe9 100644
--- a/integration-cli/requirements_unix_test.go
+++ b/integration-cli/requirements_unix_test.go
@@ -1,14 +1,14 @@
+//go:build !windows
 // +build !windows
 
 package main
 
 import (
 	"bytes"
-	"io/ioutil"
+	"os"
 	"os/exec"
 	"strings"
 
-	"github.com/docker/docker/pkg/parsers/kernel"
 	"github.com/docker/docker/pkg/sysinfo"
 )
 
@@ -37,10 +37,6 @@ func pidsLimit() bool {
 	return SysInfo.PidsLimit
 }
 
-func kernelMemorySupport() bool {
-	return testEnv.DaemonInfo.KernelMemory
-}
-
 func memoryLimitSupport() bool {
 	return testEnv.DaemonInfo.MemoryLimit
 }
@@ -54,11 +50,11 @@ func swapMemorySupport() bool {
 }
 
 func memorySwappinessSupport() bool {
-	return SameHostDaemon() && SysInfo.MemorySwappiness
+	return testEnv.IsLocalDaemon() && SysInfo.MemorySwappiness
 }
 
 func blkioWeight() bool {
-	return SameHostDaemon() && SysInfo.BlkioWeight
+	return testEnv.IsLocalDaemon() && SysInfo.BlkioWeight
 }
 
 func cgroupCpuset() bool {
@@ -73,20 +69,11 @@ func bridgeNfIptables() bool {
 	return !SysInfo.BridgeNFCallIPTablesDisabled
 }
 
-func bridgeNfIP6tables() bool {
-	return !SysInfo.BridgeNFCallIP6TablesDisabled
-}
-
 func unprivilegedUsernsClone() bool {
-	content, err := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
+	content, err := os.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
 	return err != nil || !strings.Contains(string(content), "0")
 }
 
-func ambientCapabilities() bool {
-	content, err := ioutil.ReadFile("/proc/self/status")
-	return err != nil || strings.Contains(string(content), "CapAmb:")
-}
-
 func overlayFSSupported() bool {
 	cmd := exec.Command(dockerBinary, "run", "--rm", "busybox", "/bin/sh", "-c", "cat /proc/filesystems")
 	out, err := cmd.CombinedOutput()
@@ -96,22 +83,8 @@ func overlayFSSupported() bool {
 	return bytes.Contains(out, []byte("overlay\n"))
 }
 
-func overlay2Supported() bool {
-	if !overlayFSSupported() {
-		return false
-	}
-
-	daemonV, err := kernel.ParseRelease(testEnv.DaemonInfo.KernelVersion)
-	if err != nil {
-		return false
-	}
-	requiredV := kernel.VersionInfo{Kernel: 4}
-	return kernel.CompareKernelVersion(*daemonV, requiredV) > -1
-
-}
-
 func init() {
-	if SameHostDaemon() {
-		SysInfo = sysinfo.New(true)
+	if testEnv.IsLocalDaemon() {
+		SysInfo = sysinfo.New()
 	}
 }
diff --git a/integration-cli/test_vars_exec_test.go b/integration-cli/test_vars_exec_test.go
deleted file mode 100644
index 7633b346ba9bd..0000000000000
--- a/integration-cli/test_vars_exec_test.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// +build !test_no_exec
-
-package main
-
-const (
-	// indicates docker daemon tested supports 'docker exec'
-	supportsExec = true
-)
diff --git a/integration-cli/test_vars_noexec_test.go b/integration-cli/test_vars_noexec_test.go
deleted file mode 100644
index 08450905247f2..0000000000000
--- a/integration-cli/test_vars_noexec_test.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// +build test_no_exec
-
-package main
-
-const (
-	// indicates docker daemon tested supports 'docker exec'
-	supportsExec = false
-)
diff --git a/integration-cli/test_vars_noseccomp_test.go b/integration-cli/test_vars_noseccomp_test.go
index 2f47ab07a0b8d..79c7f3bb6e205 100644
--- a/integration-cli/test_vars_noseccomp_test.go
+++ b/integration-cli/test_vars_noseccomp_test.go
@@ -1,3 +1,4 @@
+//go:build !seccomp
 // +build !seccomp
 
 package main
diff --git a/integration-cli/test_vars_seccomp_test.go b/integration-cli/test_vars_seccomp_test.go
index 00cf697209193..0c16cf4ae85bb 100644
--- a/integration-cli/test_vars_seccomp_test.go
+++ b/integration-cli/test_vars_seccomp_test.go
@@ -1,3 +1,4 @@
+//go:build seccomp
 // +build seccomp
 
 package main
diff --git a/integration-cli/test_vars_unix_test.go b/integration-cli/test_vars_unix_test.go
index f9ecc01123cd9..57666fc14379b 100644
--- a/integration-cli/test_vars_unix_test.go
+++ b/integration-cli/test_vars_unix_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package main
@@ -7,8 +8,4 @@ const (
 	isUnixCli = true
 
 	expectedFileChmod = "-rw-r--r--"
-
-	// On Unix variants, the busybox image comes with the `top` command which
-	// runs indefinitely while still being interruptible by a signal.
-	defaultSleepImage = "busybox"
 )
diff --git a/integration-cli/test_vars_windows_test.go b/integration-cli/test_vars_windows_test.go
index bfc9a5a915c09..c2d892f6257f9 100644
--- a/integration-cli/test_vars_windows_test.go
+++ b/integration-cli/test_vars_windows_test.go
@@ -1,3 +1,4 @@
+//go:build windows
 // +build windows
 
 package main
@@ -8,8 +9,4 @@ const (
 
 	// this is the expected file permission set on windows: gh#11395
 	expectedFileChmod = "-rwxr-xr-x"
-
-	// On Windows, the busybox image doesn't have the `top` command, so we rely
-	// on `sleep` with a high duration.
-	defaultSleepImage = "busybox"
 )
diff --git a/integration-cli/utils_test.go b/integration-cli/utils_test.go
index fd083681f266b..5d1ad302b2703 100644
--- a/integration-cli/utils_test.go
+++ b/integration-cli/utils_test.go
@@ -6,11 +6,11 @@ import (
 	"os/exec"
 	"path/filepath"
 	"strings"
+	"testing"
 
-	"github.com/docker/docker/internal/testutil"
-	"github.com/go-check/check"
+	"github.com/docker/docker/testutil"
 	"github.com/pkg/errors"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/icmd"
 )
 
 func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
@@ -118,7 +118,7 @@ type elementListOptions struct {
 	element, format string
 }
 
-func existingElements(c *check.C, opts elementListOptions) []string {
+func existingElements(c *testing.T, opts elementListOptions) []string {
 	var args []string
 	switch opts.element {
 	case "container":
@@ -146,12 +146,12 @@ func existingElements(c *check.C, opts elementListOptions) []string {
 }
 
 // ExistingContainerIDs returns a list of currently existing container IDs.
-func ExistingContainerIDs(c *check.C) []string {
+func ExistingContainerIDs(c *testing.T) []string {
 	return existingElements(c, elementListOptions{element: "container", format: "{{.ID}}"})
 }
 
 // ExistingContainerNames returns a list of existing container names.
-func ExistingContainerNames(c *check.C) []string {
+func ExistingContainerNames(c *testing.T) []string {
 	return existingElements(c, elementListOptions{element: "container", format: "{{.Names}}"})
 }
 
diff --git a/integration/build/build_cgroupns_linux_test.go b/integration/build/build_cgroupns_linux_test.go
new file mode 100644
index 0000000000000..20e982d85f9fb
--- /dev/null
+++ b/integration/build/build_cgroupns_linux_test.go
@@ -0,0 +1,92 @@
+package build // import "github.com/docker/docker/integration/build"
+
+import (
+	"context"
+	"encoding/json"
+	"io"
+	"strings"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/integration/internal/requirement"
+	"github.com/docker/docker/pkg/jsonmessage"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/fakecontext"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
+)
+
+// Finds the output of `readlink /proc//ns/cgroup` in build output
+func getCgroupFromBuildOutput(buildOutput io.Reader) (string, error) {
+	const prefix = "cgroup:"
+
+	dec := json.NewDecoder(buildOutput)
+	for {
+		m := jsonmessage.JSONMessage{}
+		err := dec.Decode(&m)
+		if err == io.EOF {
+			return "", nil
+		}
+		if err != nil {
+			return "", err
+		}
+		if ix := strings.Index(m.Stream, prefix); ix == 0 {
+			return strings.TrimSpace(m.Stream), nil
+		}
+	}
+}
+
+// Runs a docker build against a daemon with the given cgroup namespace default value.
+// Returns the container cgroup and daemon cgroup.
+func testBuildWithCgroupNs(t *testing.T, daemonNsMode string) (string, string) {
+	d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
+	d.StartWithBusybox(t)
+	defer d.Stop(t)
+
+	dockerfile := `
+		FROM busybox
+		RUN readlink /proc/self/ns/cgroup
+	`
+	ctx := context.Background()
+	source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
+	defer source.Close()
+
+	client := d.NewClientT(t)
+	resp, err := client.ImageBuild(ctx,
+		source.AsTarReader(t),
+		types.ImageBuildOptions{
+			Remove:      true,
+			ForceRemove: true,
+			Tags:        []string{"buildcgroupns"},
+		})
+	assert.NilError(t, err)
+	defer resp.Body.Close()
+
+	containerCgroup, err := getCgroupFromBuildOutput(resp.Body)
+	assert.NilError(t, err)
+	daemonCgroup := d.CgroupNamespace(t)
+
+	return containerCgroup, daemonCgroup
+}
+
+func TestCgroupNamespacesBuild(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !requirement.CgroupNamespacesEnabled())
+
+	// When the daemon defaults to private cgroup namespaces, containers launched
+	// should be in their own private cgroup namespace by default
+	containerCgroup, daemonCgroup := testBuildWithCgroupNs(t, "private")
+	assert.Assert(t, daemonCgroup != containerCgroup)
+}
+
+func TestCgroupNamespacesBuildDaemonHostMode(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !requirement.CgroupNamespacesEnabled())
+
+	// When the daemon defaults to host cgroup namespaces, containers
+	// launched should not be inside their own cgroup namespaces
+	containerCgroup, daemonCgroup := testBuildWithCgroupNs(t, "host")
+	assert.Assert(t, daemonCgroup == containerCgroup)
+}
diff --git a/integration/build/build_session_test.go b/integration/build/build_session_test.go
index e1f5ae43ba0b9..2ca31a635dd5f 100644
--- a/integration/build/build_session_test.go
+++ b/integration/build/build_session_test.go
@@ -2,30 +2,29 @@ package build
 
 import (
 	"context"
-	"io/ioutil"
+	"io"
+	"net"
 	"net/http"
 	"strings"
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/versions"
 	dclient "github.com/docker/docker/client"
-	"github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/fakecontext"
-	"github.com/docker/docker/internal/test/request"
+	"github.com/docker/docker/testutil/fakecontext"
+	"github.com/docker/docker/testutil/request"
 	"github.com/moby/buildkit/session"
 	"github.com/moby/buildkit/session/filesync"
 	"golang.org/x/sync/errgroup"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestBuildWithSession(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+	t.Skip("TODO: BuildKit")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
-	d := daemon.New(t, daemon.WithExperimental)
-	d.StartWithBusybox(t)
-	defer d.Stop(t)
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "experimental in older versions")
 
 	client := testEnv.APIClient()
 
@@ -54,14 +53,14 @@ func TestBuildWithSession(t *testing.T) {
 	assert.Check(t, is.Equal(strings.Count(out, "Using cache"), 2))
 	assert.Check(t, is.Contains(out, "contentcontent"))
 
-	du, err := client.DiskUsage(context.TODO())
+	du, err := client.DiskUsage(context.TODO(), types.DiskUsageOptions{})
 	assert.Check(t, err)
 	assert.Check(t, du.BuilderSize > 10)
 
 	out = testBuildWithSession(t, client, client.DaemonHost(), fctx.Dir, dockerfile)
 	assert.Check(t, is.Equal(strings.Count(out, "Using cache"), 4))
 
-	du2, err := client.DiskUsage(context.TODO())
+	du2, err := client.DiskUsage(context.TODO(), types.DiskUsageOptions{})
 	assert.Check(t, err)
 	assert.Check(t, is.Equal(du.BuilderSize, du2.BuilderSize))
 
@@ -85,11 +84,12 @@ func TestBuildWithSession(t *testing.T) {
 	_, err = client.BuildCachePrune(context.TODO(), types.BuildCachePruneOptions{All: true})
 	assert.Check(t, err)
 
-	du, err = client.DiskUsage(context.TODO())
+	du, err = client.DiskUsage(context.TODO(), types.DiskUsageOptions{})
 	assert.Check(t, err)
 	assert.Check(t, is.Equal(du.BuilderSize, int64(0)))
 }
 
+//nolint:unused // false positive: linter detects this as "unused"
 func testBuildWithSession(t *testing.T, client dclient.APIClient, daemonHost string, dir, dockerfile string) (outStr string) {
 	ctx := context.Background()
 	sess, err := session.NewSession(ctx, "foo1", "foo")
@@ -103,7 +103,9 @@ func testBuildWithSession(t *testing.T, client dclient.APIClient, daemonHost str
 	g, ctx := errgroup.WithContext(ctx)
 
 	g.Go(func() error {
-		return sess.Run(ctx, client.DialSession)
+		return sess.Run(ctx, func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) {
+			return client.DialHijack(ctx, "/session", "h2c", meta)
+		})
 	})
 
 	g.Go(func() error {
@@ -113,7 +115,7 @@ func testBuildWithSession(t *testing.T, client dclient.APIClient, daemonHost str
 			request.Host(daemonHost),
 			request.Method(http.MethodPost),
 			request.With(func(req *http.Request) error {
-				req.Body = ioutil.NopCloser(strings.NewReader(dockerfile))
+				req.Body = io.NopCloser(strings.NewReader(dockerfile))
 				return nil
 			}),
 		)
diff --git a/integration/build/build_squash_test.go b/integration/build/build_squash_test.go
index 8acdec596a7ce..2e8e45604b255 100644
--- a/integration/build/build_squash_test.go
+++ b/integration/build/build_squash_test.go
@@ -4,29 +4,34 @@ import (
 	"bytes"
 	"context"
 	"io"
-	"io/ioutil"
 	"strings"
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	dclient "github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/fakecontext"
 	"github.com/docker/docker/pkg/stdcopy"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/fakecontext"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestBuildSquashParent(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
-	skip.If(t, !testEnv.DaemonInfo.ExperimentalBuild)
-	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
-	d := daemon.New(t, daemon.WithExperimental)
-	d.StartWithBusybox(t)
-	defer d.Stop(t)
 
-	client := testEnv.APIClient()
+	var client dclient.APIClient
+	if !testEnv.DaemonInfo.ExperimentalBuild {
+		skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+
+		d := daemon.New(t, daemon.WithExperimental())
+		d.StartWithBusybox(t)
+		defer d.Stop(t)
+		client = d.NewClientT(t)
+	} else {
+		client = testEnv.APIClient()
+	}
 
 	dockerfile := `
 		FROM busybox
@@ -42,7 +47,7 @@ func TestBuildSquashParent(t *testing.T) {
 	source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
 	defer source.Close()
 
-	name := "test"
+	name := strings.ToLower(t.Name())
 	resp, err := client.ImageBuild(ctx,
 		source.AsTarReader(t),
 		types.ImageBuildOptions{
@@ -51,7 +56,7 @@ func TestBuildSquashParent(t *testing.T) {
 			Tags:        []string{name},
 		})
 	assert.NilError(t, err)
-	_, err = io.Copy(ioutil.Discard, resp.Body)
+	_, err = io.Copy(io.Discard, resp.Body)
 	resp.Body.Close()
 	assert.NilError(t, err)
 
@@ -69,11 +74,11 @@ func TestBuildSquashParent(t *testing.T) {
 			Tags:        []string{name},
 		})
 	assert.NilError(t, err)
-	_, err = io.Copy(ioutil.Discard, resp.Body)
+	_, err = io.Copy(io.Discard, resp.Body)
 	resp.Body.Close()
 	assert.NilError(t, err)
 
-	cid := container.Run(t, ctx, client,
+	cid := container.Run(ctx, t, client,
 		container.WithImage(name),
 		container.WithCmd("/bin/sh", "-c", "cat /hello"),
 	)
@@ -83,18 +88,18 @@ func TestBuildSquashParent(t *testing.T) {
 	assert.NilError(t, err)
 
 	actualStdout := new(bytes.Buffer)
-	actualStderr := ioutil.Discard
+	actualStderr := io.Discard
 	_, err = stdcopy.StdCopy(actualStdout, actualStderr, reader)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(strings.TrimSpace(actualStdout.String()), "hello\nworld"))
 
-	container.Run(t, ctx, client,
+	container.Run(ctx, t, client,
 		container.WithImage(name),
 		container.WithCmd("/bin/sh", "-c", "[ ! -f /remove_me ]"),
 	)
-	container.Run(t, ctx, client,
+	container.Run(ctx, t, client,
 		container.WithImage(name),
-		container.WithCmd("/bin/sh", "-c", `[ "$(echo $HELLO)" == "world" ]`),
+		container.WithCmd("/bin/sh", "-c", `[ "$(echo $HELLO)" = "world" ]`),
 	)
 
 	origHistory, err := client.ImageHistory(ctx, origID)
diff --git a/integration/build/build_test.go b/integration/build/build_test.go
index 7b64cb8c6d8f8..5da3a294a17a6 100644
--- a/integration/build/build_test.go
+++ b/integration/build/build_test.go
@@ -6,26 +6,24 @@ import (
 	"context"
 	"encoding/json"
 	"io"
-	"io/ioutil"
+	"os"
 	"strings"
 	"testing"
-	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/versions"
-	"github.com/docker/docker/internal/test/fakecontext"
-	"github.com/docker/docker/internal/test/request"
+	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/pkg/jsonmessage"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/fakecontext"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestBuildWithRemoveAndForceRemove(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
 	defer setupTest(t)()
-	t.Parallel()
+
 	cases := []struct {
 		name                           string
 		dockerfile                     string
@@ -89,7 +87,7 @@ func TestBuildWithRemoveAndForceRemove(t *testing.T) {
 		},
 	}
 
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 	for _, c := range cases {
 		t.Run(c.name, func(t *testing.T) {
@@ -137,9 +135,59 @@ func buildContainerIdsFilter(buildOutput io.Reader) (filters.Args, error) {
 	}
 }
 
+// TestBuildMultiStageCopy verifies that copying between stages works correctly.
+//
+// Regression test for docker/for-win#4349, ENGCORE-935, where creating the target
+// directory failed on Windows, because `os.MkdirAll()` was called with a volume
+// GUID path (\\?\Volume{dae8d3ac-b9a1-11e9-88eb-e8554b2ba1db}\newdir\hello}),
+// which currently isn't supported by Golang.
+func TestBuildMultiStageCopy(t *testing.T) {
+	ctx := context.Background()
+
+	dockerfile, err := os.ReadFile("testdata/Dockerfile." + t.Name())
+	assert.NilError(t, err)
+
+	source := fakecontext.New(t, "", fakecontext.WithDockerfile(string(dockerfile)))
+	defer source.Close()
+
+	apiclient := testEnv.APIClient()
+
+	for _, target := range []string{"copy_to_root", "copy_to_newdir", "copy_to_newdir_nested", "copy_to_existingdir", "copy_to_newsubdir"} {
+		t.Run(target, func(t *testing.T) {
+			imgName := strings.ToLower(t.Name())
+
+			resp, err := apiclient.ImageBuild(
+				ctx,
+				source.AsTarReader(t),
+				types.ImageBuildOptions{
+					Remove:      true,
+					ForceRemove: true,
+					Target:      target,
+					Tags:        []string{imgName},
+				},
+			)
+			assert.NilError(t, err)
+
+			out := bytes.NewBuffer(nil)
+			_, err = io.Copy(out, resp.Body)
+			_ = resp.Body.Close()
+			if err != nil {
+				t.Log(out)
+			}
+			assert.NilError(t, err)
+
+			// verify the image was successfully built
+			_, _, err = apiclient.ImageInspectWithRaw(ctx, imgName)
+			if err != nil {
+				t.Log(out)
+			}
+			assert.NilError(t, err)
+		})
+	}
+}
+
 func TestBuildMultiStageParentConfig(t *testing.T) {
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.35"), "broken in earlier versions")
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
 	dockerfile := `
 		FROM busybox AS stage0
 		ENV WHO=parent
@@ -157,25 +205,20 @@ func TestBuildMultiStageParentConfig(t *testing.T) {
 	defer source.Close()
 
 	apiclient := testEnv.APIClient()
+	imgName := strings.ToLower(t.Name())
 	resp, err := apiclient.ImageBuild(ctx,
 		source.AsTarReader(t),
 		types.ImageBuildOptions{
 			Remove:      true,
 			ForceRemove: true,
-			Tags:        []string{"build1"},
+			Tags:        []string{imgName},
 		})
 	assert.NilError(t, err)
-	_, err = io.Copy(ioutil.Discard, resp.Body)
+	_, err = io.Copy(io.Discard, resp.Body)
 	resp.Body.Close()
 	assert.NilError(t, err)
 
-	time.Sleep(30 * time.Second)
-
-	imgs, err := apiclient.ImageList(ctx, types.ImageListOptions{})
-	assert.NilError(t, err)
-	t.Log(imgs)
-
-	image, _, err := apiclient.ImageInspectWithRaw(ctx, "build1")
+	image, _, err := apiclient.ImageInspectWithRaw(ctx, imgName)
 	assert.NilError(t, err)
 
 	expected := "/foo/sub2"
@@ -190,7 +233,7 @@ func TestBuildMultiStageParentConfig(t *testing.T) {
 func TestBuildLabelWithTargets(t *testing.T) {
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "test added after 1.38")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
-	bldName := "build-a"
+	imgName := strings.ToLower(t.Name() + "-a")
 	testLabels := map[string]string{
 		"foo":  "bar",
 		"dead": "beef",
@@ -216,16 +259,16 @@ func TestBuildLabelWithTargets(t *testing.T) {
 		types.ImageBuildOptions{
 			Remove:      true,
 			ForceRemove: true,
-			Tags:        []string{bldName},
+			Tags:        []string{imgName},
 			Labels:      testLabels,
 			Target:      "target-a",
 		})
 	assert.NilError(t, err)
-	_, err = io.Copy(ioutil.Discard, resp.Body)
+	_, err = io.Copy(io.Discard, resp.Body)
 	resp.Body.Close()
 	assert.NilError(t, err)
 
-	image, _, err := apiclient.ImageInspectWithRaw(ctx, bldName)
+	image, _, err := apiclient.ImageInspectWithRaw(ctx, imgName)
 	assert.NilError(t, err)
 
 	testLabels["label-a"] = "inline-a"
@@ -236,23 +279,23 @@ func TestBuildLabelWithTargets(t *testing.T) {
 	}
 
 	// For `target-b` build
-	bldName = "build-b"
+	imgName = strings.ToLower(t.Name() + "-b")
 	delete(testLabels, "label-a")
 	resp, err = apiclient.ImageBuild(ctx,
 		source.AsTarReader(t),
 		types.ImageBuildOptions{
 			Remove:      true,
 			ForceRemove: true,
-			Tags:        []string{bldName},
+			Tags:        []string{imgName},
 			Labels:      testLabels,
 			Target:      "target-b",
 		})
 	assert.NilError(t, err)
-	_, err = io.Copy(ioutil.Discard, resp.Body)
+	_, err = io.Copy(io.Discard, resp.Body)
 	resp.Body.Close()
 	assert.NilError(t, err)
 
-	image, _, err = apiclient.ImageInspectWithRaw(ctx, bldName)
+	image, _, err = apiclient.ImageInspectWithRaw(ctx, imgName)
 	assert.NilError(t, err)
 
 	testLabels["label-b"] = "inline-b"
@@ -286,7 +329,7 @@ func TestBuildWithEmptyLayers(t *testing.T) {
 			ForceRemove: true,
 		})
 	assert.NilError(t, err)
-	_, err = io.Copy(ioutil.Discard, resp.Body)
+	_, err = io.Copy(io.Discard, resp.Body)
 	resp.Body.Close()
 	assert.NilError(t, err)
 }
@@ -296,7 +339,6 @@ func TestBuildWithEmptyLayers(t *testing.T) {
 // #35652
 func TestBuildMultiStageOnBuild(t *testing.T) {
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.33"), "broken in earlier versions")
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
 	defer setupTest(t)()
 	// test both metadata and layer based commands as they may be implemented differently
 	dockerfile := `FROM busybox AS stage1
@@ -333,7 +375,7 @@ RUN cat somefile`
 
 	imageIDs, err := getImageIDsFromBuild(out.Bytes())
 	assert.NilError(t, err)
-	assert.Check(t, is.Equal(3, len(imageIDs)))
+	assert.Assert(t, is.Equal(3, len(imageIDs)))
 
 	image, _, err := apiclient.ImageInspectWithRaw(context.Background(), imageIDs[2])
 	assert.NilError(t, err)
@@ -403,7 +445,6 @@ COPY bar /`
 // docker/for-linux#135
 // #35641
 func TestBuildMultiStageLayerLeak(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.37"), "broken in earlier versions")
 	ctx := context.TODO()
 	defer setupTest(t)()
@@ -443,15 +484,22 @@ RUN [ ! -f foo ]
 }
 
 // #37581
+// #40444 (Windows Containers only)
 func TestBuildWithHugeFile(t *testing.T) {
-	skip.If(t, testEnv.OSType == "windows")
 	ctx := context.TODO()
 	defer setupTest(t)()
 
 	dockerfile := `FROM busybox
-# create a sparse file with size over 8GB
+`
+
+	if testEnv.DaemonInfo.OSType == "windows" {
+		dockerfile += `# create a file with size of 8GB
+RUN powershell "fsutil.exe file createnew bigfile.txt 8589934592 ; dir bigfile.txt"`
+	} else {
+		dockerfile += `# create a sparse file with size over 8GB
 RUN for g in $(seq 0 8); do dd if=/dev/urandom of=rnd bs=1K count=1 seek=$((1024*1024*g)) status=none; done && \
     ls -la rnd && du -sk rnd`
+	}
 
 	buf := bytes.NewBuffer(nil)
 	w := tar.NewWriter(buf)
@@ -475,6 +523,179 @@ RUN for g in $(seq 0 8); do dd if=/dev/urandom of=rnd bs=1K count=1 seek=$((1024
 	assert.Check(t, is.Contains(out.String(), "Successfully built"))
 }
 
+func TestBuildWCOWSandboxSize(t *testing.T) {
+	t.Skip("FLAKY_TEST that needs to be fixed; see https://github.com/moby/moby/issues/42743")
+	skip.If(t, testEnv.DaemonInfo.OSType != "windows", "only Windows has sandbox size control")
+	ctx := context.TODO()
+	defer setupTest(t)()
+
+	dockerfile := `FROM busybox AS intermediate
+WORKDIR C:\\stuff
+# Create and delete a 21GB file
+RUN fsutil file createnew C:\\stuff\\bigfile_0.txt 22548578304 && del bigfile_0.txt
+# Create three 7GB files
+RUN fsutil file createnew C:\\stuff\\bigfile_1.txt 7516192768
+RUN fsutil file createnew C:\\stuff\\bigfile_2.txt 7516192768
+RUN fsutil file createnew C:\\stuff\\bigfile_3.txt 7516192768
+# Copy that 21GB of data out into a new target
+FROM busybox
+COPY --from=intermediate C:\\stuff C:\\stuff
+`
+
+	buf := bytes.NewBuffer(nil)
+	w := tar.NewWriter(buf)
+	writeTarRecord(t, w, "Dockerfile", dockerfile)
+	err := w.Close()
+	assert.NilError(t, err)
+
+	apiclient := testEnv.APIClient()
+	resp, err := apiclient.ImageBuild(ctx,
+		buf,
+		types.ImageBuildOptions{
+			Remove:      true,
+			ForceRemove: true,
+		})
+
+	out := bytes.NewBuffer(nil)
+	assert.NilError(t, err)
+	_, err = io.Copy(out, resp.Body)
+	resp.Body.Close()
+	assert.NilError(t, err)
+	// The test passes if either:
+	// - the image build succeeded; or
+	// - The "COPY --from=intermediate" step ran out of space during re-exec'd writing of the transport layer information to hcsshim's temp directory
+	// The latter case means we finished the COPY operation, so the sandbox must have been larger than 20GB, which was the test,
+	// and _then_ ran out of space on the host during `importLayer` in the WindowsFilter graph driver, while committing the layer.
+	// See https://github.com/moby/moby/pull/41636#issuecomment-723038517 for more details on the operations being done here.
+	// Specifically, this happens on the Docker Jenkins CI Windows-RS5 build nodes.
+	// The two parts of the acceptable-failure case are on different lines, so we need two regexp checks.
+	assert.Check(t, is.Regexp("Successfully built|COPY --from=intermediate", out.String()))
+	assert.Check(t, is.Regexp("Successfully built|re-exec error: exit status 1: output: write.*daemon\\\\\\\\tmp\\\\\\\\hcs.*bigfile_[1-3].txt: There is not enough space on the disk.", out.String()))
+}
+
+func TestBuildWithEmptyDockerfile(t *testing.T) {
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "broken in earlier versions")
+	ctx := context.TODO()
+	defer setupTest(t)()
+
+	tests := []struct {
+		name        string
+		dockerfile  string
+		expectedErr string
+	}{
+		{
+			name:        "empty-dockerfile",
+			dockerfile:  "",
+			expectedErr: "cannot be empty",
+		},
+		{
+			name: "empty-lines-dockerfile",
+			dockerfile: `
+
+
+
+			`,
+			expectedErr: "file with no instructions",
+		},
+		{
+			name:        "comment-only-dockerfile",
+			dockerfile:  `# this is a comment`,
+			expectedErr: "file with no instructions",
+		},
+	}
+
+	apiclient := testEnv.APIClient()
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+
+			buf := bytes.NewBuffer(nil)
+			w := tar.NewWriter(buf)
+			writeTarRecord(t, w, "Dockerfile", tc.dockerfile)
+			err := w.Close()
+			assert.NilError(t, err)
+
+			_, err = apiclient.ImageBuild(ctx,
+				buf,
+				types.ImageBuildOptions{
+					Remove:      true,
+					ForceRemove: true,
+				})
+
+			assert.Check(t, is.Contains(err.Error(), tc.expectedErr))
+		})
+	}
+}
+
+func TestBuildPreserveOwnership(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "broken in earlier versions")
+
+	ctx := context.Background()
+
+	dockerfile, err := os.ReadFile("testdata/Dockerfile." + t.Name())
+	assert.NilError(t, err)
+
+	source := fakecontext.New(t, "", fakecontext.WithDockerfile(string(dockerfile)))
+	defer source.Close()
+
+	apiclient := testEnv.APIClient()
+
+	for _, target := range []string{"copy_from", "copy_from_chowned"} {
+		t.Run(target, func(t *testing.T) {
+			resp, err := apiclient.ImageBuild(
+				ctx,
+				source.AsTarReader(t),
+				types.ImageBuildOptions{
+					Remove:      true,
+					ForceRemove: true,
+					Target:      target,
+				},
+			)
+			assert.NilError(t, err)
+
+			out := bytes.NewBuffer(nil)
+			_, err = io.Copy(out, resp.Body)
+			_ = resp.Body.Close()
+			if err != nil {
+				t.Log(out)
+			}
+			assert.NilError(t, err)
+		})
+	}
+}
+
+func TestBuildPlatformInvalid(t *testing.T) {
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "experimental in older versions")
+
+	ctx := context.Background()
+	defer setupTest(t)()
+
+	dockerfile := `FROM busybox
+`
+
+	buf := bytes.NewBuffer(nil)
+	w := tar.NewWriter(buf)
+	writeTarRecord(t, w, "Dockerfile", dockerfile)
+	err := w.Close()
+	assert.NilError(t, err)
+
+	apiclient := testEnv.APIClient()
+	_, err = apiclient.ImageBuild(ctx,
+		buf,
+		types.ImageBuildOptions{
+			Remove:      true,
+			ForceRemove: true,
+			Platform:    "foobar",
+		})
+
+	assert.Assert(t, err != nil)
+	assert.ErrorContains(t, err, "unknown operating system or architecture")
+	assert.Assert(t, errdefs.IsInvalidParameter(err))
+}
+
 func writeTarRecord(t *testing.T, w *tar.Writer, fn, contents string) {
 	err := w.WriteHeader(&tar.Header{
 		Name:     fn,
diff --git a/integration/build/build_userns_linux_test.go b/integration/build/build_userns_linux_test.go
new file mode 100644
index 0000000000000..dbb70f5961d88
--- /dev/null
+++ b/integration/build/build_userns_linux_test.go
@@ -0,0 +1,131 @@
+package build // import "github.com/docker/docker/integration/build"
+
+import (
+	"bufio"
+	"bytes"
+	"context"
+	"io"
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/pkg/jsonmessage"
+	"github.com/docker/docker/pkg/stdcopy"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/fakecontext"
+	"github.com/docker/docker/testutil/fixtures/load"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
+)
+
+// Implements a test for https://github.com/moby/moby/issues/41723
+// Images built in a user-namespaced daemon should have capabilities serialised in
+// VFS_CAP_REVISION_2 (no user-namespace root uid) format rather than V3 (that includes
+// the root uid).
+func TestBuildUserNamespaceValidateCapabilitiesAreV2(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !testEnv.IsUserNamespaceInKernel())
+	skip.If(t, testEnv.IsRootless())
+
+	const imageTag = "capabilities:1.0"
+
+	tmp, err := os.MkdirTemp("", "integration-")
+	assert.NilError(t, err)
+	defer os.RemoveAll(tmp)
+
+	dUserRemap := daemon.New(t)
+	dUserRemap.Start(t, "--userns-remap", "default")
+	ctx := context.Background()
+	clientUserRemap := dUserRemap.NewClientT(t)
+	defer clientUserRemap.Close()
+
+	err = load.FrozenImagesLinux(clientUserRemap, "debian:bullseye-slim")
+	assert.NilError(t, err)
+
+	dUserRemapRunning := true
+	defer func() {
+		if dUserRemapRunning {
+			dUserRemap.Stop(t)
+			dUserRemap.Cleanup(t)
+		}
+	}()
+
+	dockerfile := `
+		FROM debian:bullseye-slim
+		RUN apt-get update && apt-get install -y libcap2-bin --no-install-recommends
+		RUN setcap CAP_NET_BIND_SERVICE=+eip /bin/sleep
+	`
+
+	source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
+	defer source.Close()
+
+	resp, err := clientUserRemap.ImageBuild(ctx,
+		source.AsTarReader(t),
+		types.ImageBuildOptions{
+			Tags: []string{imageTag},
+		})
+	assert.NilError(t, err)
+	defer resp.Body.Close()
+
+	buf := bytes.NewBuffer(nil)
+	err = jsonmessage.DisplayJSONMessagesStream(resp.Body, buf, 0, false, nil)
+	assert.NilError(t, err)
+
+	reader, err := clientUserRemap.ImageSave(ctx, []string{imageTag})
+	assert.NilError(t, err, "failed to download capabilities image")
+	defer reader.Close()
+
+	tar, err := os.Create(tmp + "/image.tar")
+	assert.NilError(t, err, "failed to create image tar file")
+	defer tar.Close()
+
+	_, err = io.Copy(tar, reader)
+	assert.NilError(t, err, "failed to write image tar file")
+
+	dUserRemap.Stop(t)
+	dUserRemap.Cleanup(t)
+	dUserRemapRunning = false
+
+	dNoUserRemap := daemon.New(t)
+	dNoUserRemap.Start(t)
+	defer func() {
+		dNoUserRemap.Stop(t)
+		dNoUserRemap.Cleanup(t)
+	}()
+
+	clientNoUserRemap := dNoUserRemap.NewClientT(t)
+	defer clientNoUserRemap.Close()
+
+	tarFile, err := os.Open(tmp + "/image.tar")
+	assert.NilError(t, err, "failed to open image tar file")
+	defer tarFile.Close()
+
+	tarReader := bufio.NewReader(tarFile)
+	loadResp, err := clientNoUserRemap.ImageLoad(ctx, tarReader, false)
+	assert.NilError(t, err, "failed to load image tar file")
+	defer loadResp.Body.Close()
+	buf = bytes.NewBuffer(nil)
+	err = jsonmessage.DisplayJSONMessagesStream(loadResp.Body, buf, 0, false, nil)
+	assert.NilError(t, err)
+
+	cid := container.Run(ctx, t, clientNoUserRemap,
+		container.WithImage(imageTag),
+		container.WithCmd("/sbin/getcap", "-n", "/bin/sleep"),
+	)
+	logReader, err := clientNoUserRemap.ContainerLogs(ctx, cid, types.ContainerLogsOptions{
+		ShowStdout: true,
+	})
+	assert.NilError(t, err)
+	defer logReader.Close()
+
+	actualStdout := new(bytes.Buffer)
+	actualStderr := io.Discard
+	_, err = stdcopy.StdCopy(actualStdout, actualStderr, logReader)
+	assert.NilError(t, err)
+	if strings.TrimSpace(actualStdout.String()) != "/bin/sleep cap_net_bind_service=eip" {
+		t.Fatalf("run produced invalid output: %q, expected %q", actualStdout.String(), "/bin/sleep cap_net_bind_service=eip")
+	}
+}
diff --git a/integration/build/main_test.go b/integration/build/main_test.go
index fef3909fd5666..735f2a49a6a7a 100644
--- a/integration/build/main_test.go
+++ b/integration/build/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
diff --git a/integration/build/testdata/Dockerfile.TestBuildMultiStageCopy b/integration/build/testdata/Dockerfile.TestBuildMultiStageCopy
new file mode 100644
index 0000000000000..52b0acff16baa
--- /dev/null
+++ b/integration/build/testdata/Dockerfile.TestBuildMultiStageCopy
@@ -0,0 +1,20 @@
+FROM busybox AS base
+RUN mkdir existingdir
+
+FROM base AS source
+RUN echo "Hello World" > /hello
+
+FROM base AS copy_to_root
+COPY --from=source /hello /hello
+
+FROM base AS copy_to_newdir
+COPY --from=source /hello /newdir/hello
+
+FROM base AS copy_to_newdir_nested
+COPY --from=source /hello /newdir/newsubdir/hello
+
+FROM base AS copy_to_existingdir
+COPY --from=source /hello /existingdir/hello
+
+FROM base AS copy_to_newsubdir
+COPY --from=source /hello /existingdir/newsubdir/hello
diff --git a/integration/build/testdata/Dockerfile.TestBuildPreserveOwnership b/integration/build/testdata/Dockerfile.TestBuildPreserveOwnership
new file mode 100644
index 0000000000000..ba9d0599423f5
--- /dev/null
+++ b/integration/build/testdata/Dockerfile.TestBuildPreserveOwnership
@@ -0,0 +1,57 @@
+# Set up files and directories with known ownership
+FROM busybox AS source
+RUN touch /file && chown 100:200 /file \
+ && mkdir -p /dir/subdir \
+ && touch /dir/subdir/nestedfile \
+ && chown 100:200 /dir \
+ && chown 101:201 /dir/subdir \
+ && chown 102:202 /dir/subdir/nestedfile
+
+FROM busybox AS test_base
+RUN mkdir -p /existingdir/existingsubdir \
+ && touch /existingdir/existingfile \
+ && chown 500:600 /existingdir \
+ && chown 501:601 /existingdir/existingsubdir \
+ && chown 501:601 /existingdir/existingfile
+
+
+# Copy files from the source stage
+FROM test_base AS copy_from
+COPY --from=source /file .
+# Copy to a non-existing target directory creates the target directory (as root), then copies the _contents_ of the source directory into it
+COPY --from=source /dir /dir
+# Copying to an existing target directory will copy the _contents_ of the source directory into it
+COPY --from=source /dir/. /existingdir
+
+RUN e="100:200"; p="/file"                         ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="0:0";     p="/dir"                          ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="101:201"; p="/dir/subdir"                   ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="102:202"; p="/dir/subdir/nestedfile"        ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+# Existing files and directories ownership should not be modified
+ && e="500:600"; p="/existingdir"                  ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="501:601"; p="/existingdir/existingsubdir"   ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="501:601"; p="/existingdir/existingfile"     ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+# But new files and directories should maintain their ownership
+ && e="101:201"; p="/existingdir/subdir"           ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="102:202"; p="/existingdir/subdir/nestedfile"; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi
+
+
+# Copy files from the source stage and chown them.
+FROM test_base AS copy_from_chowned
+COPY --from=source --chown=300:400 /file .
+# Copy to a non-existing target directory creates the target directory (as root), then copies the _contents_ of the source directory into it
+COPY --from=source --chown=300:400 /dir /dir
+# Copying to an existing target directory copies the _contents_ of the source directory into it
+COPY --from=source --chown=300:400 /dir/. /existingdir
+
+RUN e="300:400"; p="/file"                         ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="300:400"; p="/dir"                          ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="300:400"; p="/dir/subdir"                   ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="300:400"; p="/dir/subdir/nestedfile"        ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+# Existing files and directories ownership should not be modified
+ && e="500:600"; p="/existingdir"                  ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="501:601"; p="/existingdir/existingsubdir"   ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="501:601"; p="/existingdir/existingfile"     ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+# But new files and directories should be chowned
+ && e="300:400"; p="/existingdir/subdir"           ; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi \
+ && e="300:400"; p="/existingdir/subdir/nestedfile"; a=`stat -c "%u:%g" "$p"`; if [ "$a" != "$e" ]; then echo "incorrect ownership on $p. expected $e, got $a"; exit 1; fi
diff --git a/integration/config/config_test.go b/integration/config/config_test.go
index 042f00e8df0ff..5df1bfc3c78a3 100644
--- a/integration/config/config_test.go
+++ b/integration/config/config_test.go
@@ -12,26 +12,51 @@ import (
 	"github.com/docker/docker/api/types/filters"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/client"
+	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/integration/internal/swarm"
 	"github.com/docker/docker/pkg/stdcopy"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
-func TestConfigList(t *testing.T) {
+func TestConfigInspect(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 
 	ctx := context.Background()
 
+	testName := t.Name()
+	configID := createConfig(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
+
+	insp, body, err := c.ConfigInspectWithRaw(ctx, configID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(insp.Spec.Name, testName))
+
+	var config swarmtypes.Config
+	err = json.Unmarshal(body, &config)
+	assert.NilError(t, err)
+	assert.Check(t, is.DeepEqual(config, insp))
+}
+
+func TestConfigList(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	c := d.NewClientT(t)
+	defer c.Close()
+	ctx := context.Background()
+
 	// This test case is ported from the original TestConfigsEmptyList
-	configs, err := client.ConfigList(ctx, types.ConfigListOptions{})
+	configs, err := c.ConfigList(ctx, types.ConfigListOptions{})
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(len(configs), 0))
 
@@ -41,12 +66,12 @@ func TestConfigList(t *testing.T) {
 	sort.Strings(testNames)
 
 	// create config test0
-	createConfig(ctx, t, client, testName0, []byte("TESTINGDATA0"), map[string]string{"type": "test"})
+	createConfig(ctx, t, c, testName0, []byte("TESTINGDATA0"), map[string]string{"type": "test"})
 
-	config1ID := createConfig(ctx, t, client, testName1, []byte("TESTINGDATA1"), map[string]string{"type": "production"})
+	config1ID := createConfig(ctx, t, c, testName1, []byte("TESTINGDATA1"), map[string]string{"type": "production"})
 
 	// test by `config ls`
-	entries, err := client.ConfigList(ctx, types.ConfigListOptions{})
+	entries, err := c.ConfigList(ctx, types.ConfigListOptions{})
 	assert.NilError(t, err)
 	assert.Check(t, is.DeepEqual(configNamesFromList(entries), testNames))
 
@@ -79,7 +104,7 @@ func TestConfigList(t *testing.T) {
 		},
 	}
 	for _, tc := range testCases {
-		entries, err = client.ConfigList(ctx, types.ConfigListOptions{
+		entries, err = c.ConfigList(ctx, types.ConfigListOptions{
 			Filters: tc.filters,
 		})
 		assert.NilError(t, err)
@@ -107,26 +132,36 @@ func TestConfigsCreateAndDelete(t *testing.T) {
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
-
+	c := d.NewClientT(t)
+	defer c.Close()
 	ctx := context.Background()
 
 	testName := "test_config-" + t.Name()
+	configID := createConfig(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
 
-	// This test case is ported from the original TestConfigsCreate
-	configID := createConfig(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
-
-	insp, _, err := client.ConfigInspectWithRaw(ctx, configID)
+	err := c.ConfigRemove(ctx, configID)
 	assert.NilError(t, err)
-	assert.Check(t, is.Equal(insp.Spec.Name, testName))
 
-	// This test case is ported from the original TestConfigsDelete
-	err = client.ConfigRemove(ctx, configID)
-	assert.NilError(t, err)
+	_, _, err = c.ConfigInspectWithRaw(ctx, configID)
+	assert.Check(t, errdefs.IsNotFound(err))
+	assert.Check(t, is.ErrorContains(err, configID))
+
+	err = c.ConfigRemove(ctx, "non-existing")
+	assert.Check(t, errdefs.IsNotFound(err))
+	assert.Check(t, is.ErrorContains(err, "non-existing"))
 
-	insp, _, err = client.ConfigInspectWithRaw(ctx, configID)
-	assert.Check(t, is.ErrorContains(err, "No such config"))
+	testName = "test_secret_with_labels_" + t.Name()
+	configID = createConfig(ctx, t, c, testName, []byte("TESTINGDATA"), map[string]string{
+		"key1": "value1",
+		"key2": "value2",
+	})
+
+	insp, _, err := c.ConfigInspectWithRaw(ctx, configID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(insp.Spec.Name, testName))
+	assert.Check(t, is.Equal(len(insp.Spec.Labels), 2))
+	assert.Check(t, is.Equal(insp.Spec.Labels["key1"], "value1"))
+	assert.Check(t, is.Equal(insp.Spec.Labels["key2"], "value2"))
 }
 
 func TestConfigsUpdate(t *testing.T) {
@@ -135,51 +170,49 @@ func TestConfigsUpdate(t *testing.T) {
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
-
+	c := d.NewClientT(t)
+	defer c.Close()
 	ctx := context.Background()
 
 	testName := "test_config-" + t.Name()
+	configID := createConfig(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
 
-	// This test case is ported from the original TestConfigsCreate
-	configID := createConfig(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
-
-	insp, _, err := client.ConfigInspectWithRaw(ctx, configID)
+	insp, _, err := c.ConfigInspectWithRaw(ctx, configID)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(insp.ID, configID))
 
 	// test UpdateConfig with full ID
 	insp.Spec.Labels = map[string]string{"test": "test1"}
-	err = client.ConfigUpdate(ctx, configID, insp.Version, insp.Spec)
+	err = c.ConfigUpdate(ctx, configID, insp.Version, insp.Spec)
 	assert.NilError(t, err)
 
-	insp, _, err = client.ConfigInspectWithRaw(ctx, configID)
+	insp, _, err = c.ConfigInspectWithRaw(ctx, configID)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test1"))
 
 	// test UpdateConfig with full name
 	insp.Spec.Labels = map[string]string{"test": "test2"}
-	err = client.ConfigUpdate(ctx, testName, insp.Version, insp.Spec)
+	err = c.ConfigUpdate(ctx, testName, insp.Version, insp.Spec)
 	assert.NilError(t, err)
 
-	insp, _, err = client.ConfigInspectWithRaw(ctx, configID)
+	insp, _, err = c.ConfigInspectWithRaw(ctx, configID)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test2"))
 
 	// test UpdateConfig with prefix ID
 	insp.Spec.Labels = map[string]string{"test": "test3"}
-	err = client.ConfigUpdate(ctx, configID[:1], insp.Version, insp.Spec)
+	err = c.ConfigUpdate(ctx, configID[:1], insp.Version, insp.Spec)
 	assert.NilError(t, err)
 
-	insp, _, err = client.ConfigInspectWithRaw(ctx, configID)
+	insp, _, err = c.ConfigInspectWithRaw(ctx, configID)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test3"))
 
 	// test UpdateConfig in updating Data which is not supported in daemon
 	// this test will produce an error in func UpdateConfig
 	insp.Spec.Data = []byte("TESTINGDATA2")
-	err = client.ConfigUpdate(ctx, configID, insp.Version, insp.Spec)
+	err = c.ConfigUpdate(ctx, configID, insp.Version, insp.Spec)
+	assert.Check(t, errdefs.IsInvalidParameter(err))
 	assert.Check(t, is.ErrorContains(err, "only updates to Labels are allowed"))
 }
 
@@ -187,8 +220,8 @@ func TestTemplatedConfig(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 	ctx := context.Background()
 
 	referencedSecretName := "referencedsecret-" + t.Name()
@@ -198,7 +231,7 @@ func TestTemplatedConfig(t *testing.T) {
 		},
 		Data: []byte("this is a secret"),
 	}
-	referencedSecret, err := client.SecretCreate(ctx, referencedSecretSpec)
+	referencedSecret, err := c.SecretCreate(ctx, referencedSecretSpec)
 	assert.Check(t, err)
 
 	referencedConfigName := "referencedconfig-" + t.Name()
@@ -208,7 +241,7 @@ func TestTemplatedConfig(t *testing.T) {
 		},
 		Data: []byte("this is a config"),
 	}
-	referencedConfig, err := client.ConfigCreate(ctx, referencedConfigSpec)
+	referencedConfig, err := c.ConfigCreate(ctx, referencedConfigSpec)
 	assert.Check(t, err)
 
 	templatedConfigName := "templated_config-" + t.Name()
@@ -224,14 +257,15 @@ func TestTemplatedConfig(t *testing.T) {
 			"{{config \"referencedconfigtarget\"}}\n"),
 	}
 
-	templatedConfig, err := client.ConfigCreate(ctx, configSpec)
+	templatedConfig, err := c.ConfigCreate(ctx, configSpec)
 	assert.Check(t, err)
 
+	serviceName := "svc_" + t.Name()
 	serviceID := swarm.CreateService(t, d,
 		swarm.ServiceWithConfig(
 			&swarmtypes.ConfigReference{
 				File: &swarmtypes.ConfigReferenceFileTarget{
-					Name: "/" + templatedConfigName,
+					Name: "templated_config",
 					UID:  "0",
 					GID:  "0",
 					Mode: 0600,
@@ -264,113 +298,31 @@ func TestTemplatedConfig(t *testing.T) {
 				SecretName: referencedSecretName,
 			},
 		),
-		swarm.ServiceWithName("svc"),
+		swarm.ServiceWithName(serviceName),
 	)
 
-	var tasks []swarmtypes.Task
-	waitAndAssert(t, 60*time.Second, func(t *testing.T) bool {
-		tasks = swarm.GetRunningTasks(t, d, serviceID)
-		return len(tasks) > 0
-	})
+	poll.WaitOn(t, swarm.RunningTasksCount(c, serviceID, 1), swarm.ServicePoll, poll.WithTimeout(1*time.Minute))
 
-	task := tasks[0]
-	waitAndAssert(t, 60*time.Second, func(t *testing.T) bool {
-		if task.NodeID == "" || (task.Status.ContainerStatus == nil || task.Status.ContainerStatus.ContainerID == "") {
-			task, _, _ = client.TaskInspectWithRaw(context.Background(), task.ID)
-		}
-		return task.NodeID != "" && task.Status.ContainerStatus != nil && task.Status.ContainerStatus.ContainerID != ""
-	})
+	tasks := swarm.GetRunningTasks(t, c, serviceID)
+	assert.Assert(t, len(tasks) > 0, "no running tasks found for service %s", serviceID)
 
-	attach := swarm.ExecTask(t, d, task, types.ExecConfig{
-		Cmd:          []string{"/bin/cat", "/" + templatedConfigName},
+	attach := swarm.ExecTask(t, d, tasks[0], types.ExecConfig{
+		Cmd:          []string{"/bin/cat", "/templated_config"},
 		AttachStdout: true,
 		AttachStderr: true,
 	})
 
-	expect := "SERVICE_NAME=svc\n" +
+	expect := "SERVICE_NAME=" + serviceName + "\n" +
 		"this is a secret\n" +
 		"this is a config\n"
 	assertAttachedStream(t, attach, expect)
 
-	attach = swarm.ExecTask(t, d, task, types.ExecConfig{
+	attach = swarm.ExecTask(t, d, tasks[0], types.ExecConfig{
 		Cmd:          []string{"mount"},
 		AttachStdout: true,
 		AttachStderr: true,
 	})
-	assertAttachedStream(t, attach, "tmpfs on /"+templatedConfigName+" type tmpfs")
-}
-
-func assertAttachedStream(t *testing.T, attach types.HijackedResponse, expect string) {
-	buf := bytes.NewBuffer(nil)
-	_, err := stdcopy.StdCopy(buf, buf, attach.Reader)
-	assert.NilError(t, err)
-	assert.Check(t, is.Contains(buf.String(), expect))
-}
-
-func waitAndAssert(t *testing.T, timeout time.Duration, f func(*testing.T) bool) {
-	t.Helper()
-	after := time.After(timeout)
-	for {
-		select {
-		case <-after:
-			t.Fatalf("timed out waiting for condition")
-		default:
-		}
-		if f(t) {
-			return
-		}
-		time.Sleep(100 * time.Millisecond)
-	}
-}
-
-func TestConfigInspect(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
-
-	defer setupTest(t)()
-	d := swarm.NewSwarm(t, testEnv)
-	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
-
-	ctx := context.Background()
-
-	testName := t.Name()
-	configID := createConfig(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
-
-	insp, body, err := client.ConfigInspectWithRaw(ctx, configID)
-	assert.NilError(t, err)
-	assert.Check(t, is.Equal(insp.Spec.Name, testName))
-
-	var config swarmtypes.Config
-	err = json.Unmarshal(body, &config)
-	assert.NilError(t, err)
-	assert.Check(t, is.DeepEqual(config, insp))
-}
-
-func TestConfigCreateWithLabels(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
-
-	defer setupTest(t)()
-	d := swarm.NewSwarm(t, testEnv)
-	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
-
-	ctx := context.Background()
-
-	labels := map[string]string{
-		"key1": "value1",
-		"key2": "value2",
-	}
-	testName := t.Name()
-	configID := createConfig(ctx, t, client, testName, []byte("TESTINGDATA"), labels)
-
-	insp, _, err := client.ConfigInspectWithRaw(ctx, configID)
-	assert.NilError(t, err)
-	assert.Check(t, is.Equal(insp.Spec.Name, testName))
-	assert.Check(t, is.Equal(2, len(insp.Spec.Labels)))
-	assert.Check(t, is.Equal("value1", insp.Spec.Labels["key1"]))
-	assert.Check(t, is.Equal("value2", insp.Spec.Labels["key2"]))
+	assertAttachedStream(t, attach, "tmpfs on /templated_config type tmpfs")
 }
 
 // Test case for 28884
@@ -380,27 +332,27 @@ func TestConfigCreateResolve(t *testing.T) {
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 
 	ctx := context.Background()
 
 	configName := "test_config_" + t.Name()
+	configID := createConfig(ctx, t, c, configName, []byte("foo"), nil)
 
-	configID := createConfig(ctx, t, client, configName, []byte("foo"), nil)
 	fakeName := configID
-	fakeID := createConfig(ctx, t, client, fakeName, []byte("fake foo"), nil)
+	fakeID := createConfig(ctx, t, c, fakeName, []byte("fake foo"), nil)
 
-	entries, err := client.ConfigList(ctx, types.ConfigListOptions{})
+	entries, err := c.ConfigList(ctx, types.ConfigListOptions{})
 	assert.NilError(t, err)
 	assert.Assert(t, is.Contains(configNamesFromList(entries), configName))
 	assert.Assert(t, is.Contains(configNamesFromList(entries), fakeName))
 
-	err = client.ConfigRemove(ctx, configID)
+	err = c.ConfigRemove(ctx, configID)
 	assert.NilError(t, err)
 
 	// Fake one will remain
-	entries, err = client.ConfigList(ctx, types.ConfigListOptions{})
+	entries, err = c.ConfigList(ctx, types.ConfigListOptions{})
 	assert.NilError(t, err)
 	assert.Assert(t, is.DeepEqual(configNamesFromList(entries), []string{fakeName}))
 
@@ -410,20 +362,27 @@ func TestConfigCreateResolve(t *testing.T) {
 	// - Full ID
 	// - Full Name
 	// - Partial ID (prefix)
-	err = client.ConfigRemove(ctx, configID[:5])
+	err = c.ConfigRemove(ctx, configID[:5])
 	assert.Assert(t, nil != err)
-	entries, err = client.ConfigList(ctx, types.ConfigListOptions{})
+	entries, err = c.ConfigList(ctx, types.ConfigListOptions{})
 	assert.NilError(t, err)
 	assert.Assert(t, is.DeepEqual(configNamesFromList(entries), []string{fakeName}))
 
 	// Remove based on ID prefix of the fake one should succeed
-	err = client.ConfigRemove(ctx, fakeID[:5])
+	err = c.ConfigRemove(ctx, fakeID[:5])
 	assert.NilError(t, err)
-	entries, err = client.ConfigList(ctx, types.ConfigListOptions{})
+	entries, err = c.ConfigList(ctx, types.ConfigListOptions{})
 	assert.NilError(t, err)
 	assert.Assert(t, is.Equal(0, len(entries)))
 }
 
+func assertAttachedStream(t *testing.T, attach types.HijackedResponse, expect string) {
+	buf := bytes.NewBuffer(nil)
+	_, err := stdcopy.StdCopy(buf, buf, attach.Reader)
+	assert.NilError(t, err)
+	assert.Check(t, is.Contains(buf.String(), expect))
+}
+
 func configNamesFromList(entries []swarmtypes.Config) []string {
 	var values []string
 	for _, entry := range entries {
diff --git a/integration/config/main_test.go b/integration/config/main_test.go
index 3c8f0483f2372..dc5c8e834d257 100644
--- a/integration/config/main_test.go
+++ b/integration/config/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
diff --git a/integration/container/checkpoint_test.go b/integration/container/checkpoint_test.go
new file mode 100644
index 0000000000000..fb37fcea60465
--- /dev/null
+++ b/integration/container/checkpoint_test.go
@@ -0,0 +1,165 @@
+package container // import "github.com/docker/docker/integration/container"
+
+import (
+	"context"
+	"fmt"
+	"os/exec"
+	"regexp"
+	"sort"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	mounttypes "github.com/docker/docker/api/types/mount"
+	"github.com/docker/docker/client"
+	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
+)
+
+//nolint:unused // false positive: linter detects this as "unused"
+func containerExec(t *testing.T, client client.APIClient, cID string, cmd []string) {
+	t.Logf("Exec: %s", cmd)
+	ctx := context.Background()
+	r, err := container.Exec(ctx, client, cID, cmd)
+	assert.NilError(t, err)
+	t.Log(r.Combined())
+	assert.Equal(t, r.ExitCode, 0)
+}
+
+func TestCheckpoint(t *testing.T) {
+	t.Skip("TestCheckpoint is broken; see https://github.com/moby/moby/issues/38963")
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, !testEnv.DaemonInfo.ExperimentalBuild)
+
+	defer setupTest(t)()
+
+	cmd := exec.Command("criu", "check")
+	stdoutStderr, err := cmd.CombinedOutput()
+	t.Logf("%s", stdoutStderr)
+	assert.NilError(t, err)
+
+	ctx := context.Background()
+	client := request.NewAPIClient(t)
+
+	mnt := mounttypes.Mount{
+		Type:   mounttypes.TypeTmpfs,
+		Target: "/tmp",
+	}
+
+	t.Log("Start a container")
+	cID := container.Run(ctx, t, client, container.WithMount(mnt))
+	poll.WaitOn(t,
+		container.IsInState(ctx, client, cID, "running"),
+		poll.WithDelay(100*time.Millisecond),
+	)
+
+	cptOpt := types.CheckpointCreateOptions{
+		Exit:         false,
+		CheckpointID: "test",
+	}
+
+	{
+		// FIXME: ipv6 iptables modules are not uploaded in the test environment
+		cmd := exec.Command("bash", "-c", "set -x; "+
+			"mount --bind $(type -P true) $(type -P ip6tables-restore) && "+
+			"mount --bind $(type -P true) $(type -P ip6tables-save)")
+		stdoutStderr, err = cmd.CombinedOutput()
+		t.Logf("%s", stdoutStderr)
+		assert.NilError(t, err)
+
+		defer func() {
+			cmd := exec.Command("bash", "-c", "set -x; "+
+				"umount -c -i -l $(type -P ip6tables-restore); "+
+				"umount -c -i -l $(type -P ip6tables-save)")
+			stdoutStderr, err = cmd.CombinedOutput()
+			t.Logf("%s", stdoutStderr)
+			assert.NilError(t, err)
+		}()
+	}
+	t.Log("Do a checkpoint and leave the container running")
+	err = client.CheckpointCreate(ctx, cID, cptOpt)
+	if err != nil {
+		// An error can contain a path to a dump file
+		t.Logf("%s", err)
+		re := regexp.MustCompile("path= (.*): ")
+		m := re.FindStringSubmatch(fmt.Sprintf("%s", err))
+		if len(m) >= 2 {
+			dumpLog := m[1]
+			t.Logf("%s", dumpLog)
+			cmd := exec.Command("cat", dumpLog)
+			stdoutStderr, err = cmd.CombinedOutput()
+			t.Logf("%s", stdoutStderr)
+		}
+	}
+	assert.NilError(t, err)
+
+	inspect, err := client.ContainerInspect(ctx, cID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(true, inspect.State.Running))
+
+	checkpoints, err := client.CheckpointList(ctx, cID, types.CheckpointListOptions{})
+	assert.NilError(t, err)
+	assert.Equal(t, len(checkpoints), 1)
+	assert.Equal(t, checkpoints[0].Name, "test")
+
+	// Create a test file on a tmpfs mount.
+	containerExec(t, client, cID, []string{"touch", "/tmp/test-file"})
+
+	// Do a second checkpoint
+	cptOpt = types.CheckpointCreateOptions{
+		Exit:         true,
+		CheckpointID: "test2",
+	}
+	t.Log("Do a checkpoint and stop the container")
+	err = client.CheckpointCreate(ctx, cID, cptOpt)
+	assert.NilError(t, err)
+
+	poll.WaitOn(t,
+		container.IsInState(ctx, client, cID, "exited"),
+		poll.WithDelay(100*time.Millisecond),
+	)
+
+	inspect, err = client.ContainerInspect(ctx, cID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(false, inspect.State.Running))
+
+	// Check that both checkpoints are listed.
+	checkpoints, err = client.CheckpointList(ctx, cID, types.CheckpointListOptions{})
+	assert.NilError(t, err)
+	assert.Equal(t, len(checkpoints), 2)
+	cptNames := make([]string, 2)
+	for i, c := range checkpoints {
+		cptNames[i] = c.Name
+	}
+	sort.Strings(cptNames)
+	assert.Equal(t, cptNames[0], "test")
+	assert.Equal(t, cptNames[1], "test2")
+
+	// Restore the container from a second checkpoint.
+	startOpt := types.ContainerStartOptions{
+		CheckpointID: "test2",
+	}
+	t.Log("Restore the container")
+	err = client.ContainerStart(ctx, cID, startOpt)
+	assert.NilError(t, err)
+
+	inspect, err = client.ContainerInspect(ctx, cID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(true, inspect.State.Running))
+
+	// Check that the test file has been restored.
+	containerExec(t, client, cID, []string{"test", "-f", "/tmp/test-file"})
+
+	for _, id := range []string{"test", "test2"} {
+		cptDelOpt := types.CheckpointDeleteOptions{
+			CheckpointID: id,
+		}
+
+		err = client.CheckpointDelete(ctx, cID, cptDelOpt)
+		assert.NilError(t, err)
+	}
+}
diff --git a/integration/container/container_test.go b/integration/container/container_test.go
new file mode 100644
index 0000000000000..0402828a77bd3
--- /dev/null
+++ b/integration/container/container_test.go
@@ -0,0 +1,42 @@
+package container // import "github.com/docker/docker/integration/container"
+
+import (
+	"net/http"
+	"testing"
+
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+)
+
+func TestContainerInvalidJSON(t *testing.T) {
+	defer setupTest(t)()
+
+	endpoints := []string{
+		"/containers/foobar/exec",
+		"/exec/foobar/start",
+	}
+
+	for _, ep := range endpoints {
+		ep := ep
+		t.Run(ep, func(t *testing.T) {
+			t.Parallel()
+
+			res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
+			assert.NilError(t, err)
+			assert.Equal(t, res.StatusCode, http.StatusBadRequest)
+
+			buf, err := request.ReadBody(body)
+			assert.NilError(t, err)
+			assert.Check(t, is.Contains(string(buf), "invalid character 'i' looking for beginning of object key string"))
+
+			res, body, err = request.Post(ep, request.JSON)
+			assert.NilError(t, err)
+			assert.Equal(t, res.StatusCode, http.StatusBadRequest)
+
+			buf, err = request.ReadBody(body)
+			assert.NilError(t, err)
+			assert.Check(t, is.Contains(string(buf), "got EOF while reading request body"))
+		})
+	}
+}
diff --git a/integration/container/copy_test.go b/integration/container/copy_test.go
index 9c5c5ce297317..128e2029033bf 100644
--- a/integration/container/copy_test.go
+++ b/integration/container/copy_test.go
@@ -1,25 +1,30 @@
 package container // import "github.com/docker/docker/integration/container"
 
 import (
+	"archive/tar"
 	"context"
+	"encoding/json"
 	"fmt"
+	"io"
+	"os"
 	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"github.com/docker/docker/pkg/jsonmessage"
+	"github.com/docker/docker/testutil/fakecontext"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestCopyFromContainerPathDoesNotExist(t *testing.T) {
 	defer setupTest(t)()
-	skip.If(t, testEnv.OSType == "windows")
 
 	ctx := context.Background()
 	apiclient := testEnv.APIClient()
-	cid := container.Create(t, ctx, apiclient)
+	cid := container.Create(ctx, t, apiclient)
 
 	_, _, err := apiclient.CopyFromContainer(ctx, cid, "/dne")
 	assert.Check(t, client.IsErrNotFound(err))
@@ -29,23 +34,27 @@ func TestCopyFromContainerPathDoesNotExist(t *testing.T) {
 
 func TestCopyFromContainerPathIsNotDir(t *testing.T) {
 	defer setupTest(t)()
-	skip.If(t, testEnv.OSType == "windows")
 
 	ctx := context.Background()
 	apiclient := testEnv.APIClient()
-	cid := container.Create(t, ctx, apiclient)
+	cid := container.Create(ctx, t, apiclient)
 
-	_, _, err := apiclient.CopyFromContainer(ctx, cid, "/etc/passwd/")
-	assert.Assert(t, is.ErrorContains(err, "not a directory"))
+	path := "/etc/passwd/"
+	expected := "not a directory"
+	if testEnv.OSType == "windows" {
+		path = "c:/windows/system32/drivers/etc/hosts/"
+		expected = "The filename, directory name, or volume label syntax is incorrect."
+	}
+	_, _, err := apiclient.CopyFromContainer(ctx, cid, path)
+	assert.Assert(t, is.ErrorContains(err, expected))
 }
 
 func TestCopyToContainerPathDoesNotExist(t *testing.T) {
 	defer setupTest(t)()
-	skip.If(t, testEnv.OSType == "windows")
 
 	ctx := context.Background()
 	apiclient := testEnv.APIClient()
-	cid := container.Create(t, ctx, apiclient)
+	cid := container.Create(ctx, t, apiclient)
 
 	err := apiclient.CopyToContainer(ctx, cid, "/dne", nil, types.CopyToContainerOptions{})
 	assert.Check(t, client.IsErrNotFound(err))
@@ -55,12 +64,105 @@ func TestCopyToContainerPathDoesNotExist(t *testing.T) {
 
 func TestCopyToContainerPathIsNotDir(t *testing.T) {
 	defer setupTest(t)()
-	skip.If(t, testEnv.OSType == "windows")
 
 	ctx := context.Background()
 	apiclient := testEnv.APIClient()
-	cid := container.Create(t, ctx, apiclient)
+	cid := container.Create(ctx, t, apiclient)
 
-	err := apiclient.CopyToContainer(ctx, cid, "/etc/passwd/", nil, types.CopyToContainerOptions{})
+	path := "/etc/passwd/"
+	if testEnv.OSType == "windows" {
+		path = "c:/windows/system32/drivers/etc/hosts/"
+	}
+	err := apiclient.CopyToContainer(ctx, cid, path, nil, types.CopyToContainerOptions{})
 	assert.Assert(t, is.ErrorContains(err, "not a directory"))
 }
+
+func TestCopyFromContainer(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	defer setupTest(t)()
+
+	ctx := context.Background()
+	apiClient := testEnv.APIClient()
+
+	dir, err := os.MkdirTemp("", t.Name())
+	assert.NilError(t, err)
+	defer os.RemoveAll(dir)
+
+	buildCtx := fakecontext.New(t, dir, fakecontext.WithFile("foo", "hello"), fakecontext.WithFile("baz", "world"), fakecontext.WithDockerfile(`
+		FROM busybox
+		COPY foo /foo
+		COPY baz /bar/quux/baz
+		RUN ln -s notexist /bar/notarget && ln -s quux/baz /bar/filesymlink && ln -s quux /bar/dirsymlink && ln -s / /bar/root
+		CMD /fake
+	`))
+	defer buildCtx.Close()
+
+	resp, err := apiClient.ImageBuild(ctx, buildCtx.AsTarReader(t), types.ImageBuildOptions{})
+	assert.NilError(t, err)
+	defer resp.Body.Close()
+
+	var imageID string
+	err = jsonmessage.DisplayJSONMessagesStream(resp.Body, io.Discard, 0, false, func(msg jsonmessage.JSONMessage) {
+		var r types.BuildResult
+		assert.NilError(t, json.Unmarshal(*msg.Aux, &r))
+		imageID = r.ID
+	})
+	assert.NilError(t, err)
+	assert.Assert(t, imageID != "")
+
+	cid := container.Create(ctx, t, apiClient, container.WithImage(imageID))
+
+	for _, x := range []struct {
+		src    string
+		expect map[string]string
+	}{
+		{"/", map[string]string{"/": "", "/foo": "hello", "/bar/quux/baz": "world", "/bar/filesymlink": "", "/bar/dirsymlink": "", "/bar/notarget": ""}},
+		{"/bar/root", map[string]string{"root": ""}},
+		{"/bar/root/", map[string]string{"root/": "", "root/foo": "hello", "root/bar/quux/baz": "world", "root/bar/filesymlink": "", "root/bar/dirsymlink": "", "root/bar/notarget": ""}},
+
+		{"bar/quux", map[string]string{"quux/": "", "quux/baz": "world"}},
+		{"bar/quux/", map[string]string{"quux/": "", "quux/baz": "world"}},
+		{"bar/quux/baz", map[string]string{"baz": "world"}},
+
+		{"bar/filesymlink", map[string]string{"filesymlink": ""}},
+		{"bar/dirsymlink", map[string]string{"dirsymlink": ""}},
+		{"bar/dirsymlink/", map[string]string{"dirsymlink/": "", "dirsymlink/baz": "world"}},
+		{"bar/notarget", map[string]string{"notarget": ""}},
+	} {
+		t.Run(x.src, func(t *testing.T) {
+			rdr, _, err := apiClient.CopyFromContainer(ctx, cid, x.src)
+			assert.NilError(t, err)
+			defer rdr.Close()
+
+			found := make(map[string]bool, len(x.expect))
+			var numFound int
+			tr := tar.NewReader(rdr)
+			for numFound < len(x.expect) {
+				h, err := tr.Next()
+				if err == io.EOF {
+					break
+				}
+				assert.NilError(t, err)
+
+				expected, exists := x.expect[h.Name]
+				if !exists {
+					// this archive will have extra stuff in it since we are copying from root
+					// and docker adds a bunch of stuff
+					continue
+				}
+
+				numFound++
+				found[h.Name] = true
+
+				buf, err := io.ReadAll(tr)
+				if err == nil {
+					assert.Check(t, is.Equal(string(buf), expected))
+				}
+			}
+
+			for f := range x.expect {
+				assert.Check(t, found[f], f+" not found in archive")
+			}
+		})
+	}
+}
diff --git a/integration/container/create_test.go b/integration/container/create_test.go
index 8d5afd1f934dc..b47d4c7dbbb8c 100644
--- a/integration/container/create_test.go
+++ b/integration/container/create_test.go
@@ -4,27 +4,29 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"net/http"
 	"strconv"
 	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
+	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/client"
+	"github.com/docker/docker/errdefs"
 	ctr "github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/oci"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	testCases := []struct {
 		doc           string
@@ -56,16 +58,41 @@ func TestCreateFailsWhenIdentifierDoesNotExist(t *testing.T) {
 				&container.Config{Image: tc.image},
 				&container.HostConfig{},
 				&network.NetworkingConfig{},
+				nil,
 				"",
 			)
 			assert.Check(t, is.ErrorContains(err, tc.expectedError))
+			assert.Check(t, errdefs.IsNotFound(err))
 		})
 	}
 }
 
+// TestCreateLinkToNonExistingContainer verifies that linking to a non-existing
+// container returns an "invalid parameter" (400) status, and not the underlying
+// "non exists" (404).
+func TestCreateLinkToNonExistingContainer(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "legacy links are not supported on windows")
+	defer setupTest(t)()
+	c := testEnv.APIClient()
+
+	_, err := c.ContainerCreate(context.Background(),
+		&container.Config{
+			Image: "busybox",
+		},
+		&container.HostConfig{
+			Links: []string{"no-such-container"},
+		},
+		&network.NetworkingConfig{},
+		nil,
+		"",
+	)
+	assert.Check(t, is.ErrorContains(err, "could not get container for no-such-container"))
+	assert.Check(t, errdefs.IsInvalidParameter(err))
+}
+
 func TestCreateWithInvalidEnv(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	testCases := []struct {
 		env           string
@@ -96,9 +123,11 @@ func TestCreateWithInvalidEnv(t *testing.T) {
 				},
 				&container.HostConfig{},
 				&network.NetworkingConfig{},
+				nil,
 				"",
 			)
 			assert.Check(t, is.ErrorContains(err, tc.expectedError))
+			assert.Check(t, errdefs.IsInvalidParameter(err))
 		})
 	}
 }
@@ -108,7 +137,7 @@ func TestCreateTmpfsMountsTarget(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	testCases := []struct {
 		target        string
@@ -141,16 +170,18 @@ func TestCreateTmpfsMountsTarget(t *testing.T) {
 				Tmpfs: map[string]string{tc.target: ""},
 			},
 			&network.NetworkingConfig{},
+			nil,
 			"",
 		)
 		assert.Check(t, is.ErrorContains(err, tc.expectedError))
+		assert.Check(t, errdefs.IsInvalidParameter(err))
 	}
 }
 func TestCreateWithCustomMaskedPaths(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	testCases := []struct {
@@ -209,6 +240,7 @@ func TestCreateWithCustomMaskedPaths(t *testing.T) {
 			&config,
 			&hc,
 			&network.NetworkingConfig{},
+			nil,
 			name,
 		)
 		assert.NilError(t, err)
@@ -229,11 +261,10 @@ func TestCreateWithCustomReadonlyPaths(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	testCases := []struct {
-		doc           string
 		readonlyPaths []string
 		expected      []string
 	}{
@@ -288,6 +319,7 @@ func TestCreateWithCustomReadonlyPaths(t *testing.T) {
 			&config,
 			&hc,
 			&network.NetworkingConfig{},
+			nil,
 			name,
 		)
 		assert.NilError(t, err)
@@ -306,6 +338,8 @@ func TestCreateWithCustomReadonlyPaths(t *testing.T) {
 
 func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
 	defer setupTest(t)()
+	client := testEnv.APIClient()
+	ctx := context.Background()
 
 	testCases := []struct {
 		doc         string
@@ -353,38 +387,144 @@ func TestCreateWithInvalidHealthcheckParams(t *testing.T) {
 		},
 	}
 
-	for i, tc := range testCases {
-		i := i
+	for _, tc := range testCases {
 		tc := tc
 		t.Run(tc.doc, func(t *testing.T) {
 			t.Parallel()
-			healthCheck := map[string]interface{}{
-				"Interval": tc.interval,
-				"Timeout":  tc.timeout,
-				"Retries":  tc.retries,
+			cfg := container.Config{
+				Image: "busybox",
+				Healthcheck: &container.HealthConfig{
+					Interval: tc.interval,
+					Timeout:  tc.timeout,
+					Retries:  tc.retries,
+				},
 			}
 			if tc.startPeriod != 0 {
-				healthCheck["StartPeriod"] = tc.startPeriod
-			}
-
-			config := map[string]interface{}{
-				"Image":       "busybox",
-				"Healthcheck": healthCheck,
+				cfg.Healthcheck.StartPeriod = tc.startPeriod
 			}
 
-			res, body, err := request.Post("/containers/create?name="+fmt.Sprintf("test_%d_", i)+t.Name(), request.JSONBody(config))
-			assert.NilError(t, err)
+			resp, err := client.ContainerCreate(ctx, &cfg, &container.HostConfig{}, nil, nil, "")
+			assert.Check(t, is.Equal(len(resp.Warnings), 0))
 
 			if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
-				assert.Check(t, is.Equal(http.StatusInternalServerError, res.StatusCode))
+				assert.Check(t, errdefs.IsSystem(err))
 			} else {
-				assert.Check(t, is.Equal(http.StatusBadRequest, res.StatusCode))
+				assert.Check(t, errdefs.IsInvalidParameter(err))
 			}
+			assert.ErrorContains(t, err, tc.expectedErr)
+		})
+	}
+}
+
+// Make sure that anonymous volumes can be overritten by tmpfs
+// https://github.com/moby/moby/issues/40446
+func TestCreateTmpfsOverrideAnonymousVolume(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "windows does not support tmpfs")
+	defer setupTest(t)()
+	client := testEnv.APIClient()
+	ctx := context.Background()
 
-			buf, err := request.ReadBody(body)
-			assert.NilError(t, err)
+	id := ctr.Create(ctx, t, client,
+		ctr.WithVolume("/foo"),
+		ctr.WithTmpfs("/foo"),
+		ctr.WithVolume("/bar"),
+		ctr.WithTmpfs("/bar:size=999"),
+		ctr.WithCmd("/bin/sh", "-c", "mount | grep '/foo' | grep tmpfs && mount | grep '/bar' | grep tmpfs"),
+	)
 
-			assert.Check(t, is.Contains(string(buf), tc.expectedErr))
-		})
+	defer func() {
+		err := client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true})
+		assert.NilError(t, err)
+	}()
+
+	inspect, err := client.ContainerInspect(ctx, id)
+	assert.NilError(t, err)
+	// tmpfs do not currently get added to inspect.Mounts
+	// Normally an anonymous volume would, except now tmpfs should prevent that.
+	assert.Assert(t, is.Len(inspect.Mounts, 0))
+
+	chWait, chErr := client.ContainerWait(ctx, id, container.WaitConditionNextExit)
+	assert.NilError(t, client.ContainerStart(ctx, id, types.ContainerStartOptions{}))
+
+	timeout := time.NewTimer(30 * time.Second)
+	defer timeout.Stop()
+
+	select {
+	case <-timeout.C:
+		t.Fatal("timeout waiting for container to exit")
+	case status := <-chWait:
+		var errMsg string
+		if status.Error != nil {
+			errMsg = status.Error.Message
+		}
+		assert.Equal(t, int(status.StatusCode), 0, errMsg)
+	case err := <-chErr:
+		assert.NilError(t, err)
 	}
 }
+
+// Test that if the referenced image platform does not match the requested platform on container create that we get an
+// error.
+func TestCreateDifferentPlatform(t *testing.T) {
+	defer setupTest(t)()
+	c := testEnv.APIClient()
+	ctx := context.Background()
+
+	img, _, err := c.ImageInspectWithRaw(ctx, "busybox:latest")
+	assert.NilError(t, err)
+	assert.Assert(t, img.Architecture != "")
+
+	t.Run("different os", func(t *testing.T) {
+		p := specs.Platform{
+			OS:           img.Os + "DifferentOS",
+			Architecture: img.Architecture,
+			Variant:      img.Variant,
+		}
+		_, err := c.ContainerCreate(ctx, &containertypes.Config{Image: "busybox:latest"}, &containertypes.HostConfig{}, nil, &p, "")
+		assert.Assert(t, client.IsErrNotFound(err), err)
+	})
+	t.Run("different cpu arch", func(t *testing.T) {
+		p := specs.Platform{
+			OS:           img.Os,
+			Architecture: img.Architecture + "DifferentArch",
+			Variant:      img.Variant,
+		}
+		_, err := c.ContainerCreate(ctx, &containertypes.Config{Image: "busybox:latest"}, &containertypes.HostConfig{}, nil, &p, "")
+		assert.Assert(t, client.IsErrNotFound(err), err)
+	})
+}
+
+func TestCreateVolumesFromNonExistingContainer(t *testing.T) {
+	defer setupTest(t)()
+	cli := testEnv.APIClient()
+
+	_, err := cli.ContainerCreate(
+		context.Background(),
+		&container.Config{Image: "busybox"},
+		&container.HostConfig{VolumesFrom: []string{"nosuchcontainer"}},
+		nil,
+		nil,
+		"",
+	)
+	assert.Check(t, errdefs.IsInvalidParameter(err))
+}
+
+// Test that we can create a container from an image that is for a different platform even if a platform was not specified
+// This is for the regression detailed here: https://github.com/moby/moby/issues/41552
+func TestCreatePlatformSpecificImageNoPlatform(t *testing.T) {
+	defer setupTest(t)()
+
+	skip.If(t, testEnv.DaemonInfo.Architecture == "arm", "test only makes sense to run on non-arm systems")
+	skip.If(t, testEnv.OSType != "linux", "test image is only available on linux")
+	cli := testEnv.APIClient()
+
+	_, err := cli.ContainerCreate(
+		context.Background(),
+		&container.Config{Image: "arm32v7/hello-world"},
+		&container.HostConfig{},
+		nil,
+		nil,
+		"",
+	)
+	assert.NilError(t, err)
+}
diff --git a/integration/container/daemon_linux_test.go b/integration/container/daemon_linux_test.go
index bd13e30939f45..1683c51fec12f 100644
--- a/integration/container/daemon_linux_test.go
+++ b/integration/container/daemon_linux_test.go
@@ -2,18 +2,24 @@ package container // import "github.com/docker/docker/integration/container"
 
 import (
 	"context"
+	"encoding/json"
 	"fmt"
-	"io/ioutil"
+	"os"
+	"path/filepath"
 	"strconv"
 	"strings"
 	"testing"
+	"time"
 
 	"github.com/docker/docker/api/types"
+	containerapi "github.com/docker/docker/api/types/container"
+	realcontainer "github.com/docker/docker/container"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/daemon"
+	"github.com/docker/docker/testutil/daemon"
 	"golang.org/x/sys/unix"
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 // This is a regression test for #36145
@@ -29,25 +35,24 @@ import (
 func TestContainerStartOnDaemonRestart(t *testing.T) {
 	skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
-	skip.If(t, testEnv.IsRemoteDaemon(), "cannot start daemon on remote test run")
+	skip.If(t, testEnv.IsRootless)
 	t.Parallel()
 
 	d := daemon.New(t)
 	d.StartWithBusybox(t, "--iptables=false")
 	defer d.Stop(t)
 
-	client, err := d.NewClient()
-	assert.Check(t, err, "error creating client")
+	c := d.NewClientT(t)
 
 	ctx := context.Background()
 
-	cID := container.Create(t, ctx, client)
-	defer client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true})
+	cID := container.Create(ctx, t, c)
+	defer c.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true})
 
-	err = client.ContainerStart(ctx, cID, types.ContainerStartOptions{})
+	err := c.ContainerStart(ctx, cID, types.ContainerStartOptions{})
 	assert.Check(t, err, "error starting test container")
 
-	inspect, err := client.ContainerInspect(ctx, cID)
+	inspect, err := c.ContainerInspect(ctx, cID)
 	assert.Check(t, err, "error getting inspect data")
 
 	ppid := getContainerdShimPid(t, inspect)
@@ -63,12 +68,12 @@ func TestContainerStartOnDaemonRestart(t *testing.T) {
 
 	d.Start(t, "--iptables=false")
 
-	err = client.ContainerStart(ctx, cID, types.ContainerStartOptions{})
+	err = c.ContainerStart(ctx, cID, types.ContainerStartOptions{})
 	assert.Check(t, err, "failed to start test container")
 }
 
 func getContainerdShimPid(t *testing.T, c types.ContainerJSON) int {
-	statB, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", c.State.Pid))
+	statB, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", c.State.Pid))
 	assert.Check(t, err, "error looking up containerd-shim pid")
 
 	// ppid is the 4th entry in `/proc/pid/stat`
@@ -78,3 +83,152 @@ func getContainerdShimPid(t *testing.T, c types.ContainerJSON) int {
 	assert.Check(t, ppid != 1, "got unexpected ppid")
 	return ppid
 }
+
+// TestDaemonRestartIpcMode makes sure a container keeps its ipc mode
+// (derived from daemon default) even after the daemon is restarted
+// with a different default ipc mode.
+func TestDaemonRestartIpcMode(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	t.Parallel()
+
+	d := daemon.New(t)
+	d.StartWithBusybox(t, "--iptables=false", "--default-ipc-mode=private")
+	defer d.Stop(t)
+
+	c := d.NewClientT(t)
+	ctx := context.Background()
+
+	// check the container is created with private ipc mode as per daemon default
+	cID := container.Run(ctx, t, c,
+		container.WithCmd("top"),
+		container.WithRestartPolicy("always"),
+	)
+	defer c.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true})
+
+	inspect, err := c.ContainerInspect(ctx, cID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(string(inspect.HostConfig.IpcMode), "private"))
+
+	// restart the daemon with shareable default ipc mode
+	d.Restart(t, "--iptables=false", "--default-ipc-mode=shareable")
+
+	// check the container is still having private ipc mode
+	inspect, err = c.ContainerInspect(ctx, cID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(string(inspect.HostConfig.IpcMode), "private"))
+
+	// check a new container is created with shareable ipc mode as per new daemon default
+	cID = container.Run(ctx, t, c)
+	defer c.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true})
+
+	inspect, err = c.ContainerInspect(ctx, cID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(string(inspect.HostConfig.IpcMode), "shareable"))
+}
+
+// TestDaemonHostGatewayIP verifies that when a magic string "host-gateway" is passed
+// to ExtraHosts (--add-host) instead of an IP address, its value is set to
+// 1. Daemon config flag value specified by host-gateway-ip or
+// 2. IP of the default bridge network
+// and is added to the /etc/hosts file
+func TestDaemonHostGatewayIP(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
+	t.Parallel()
+
+	// Verify the IP in /etc/hosts is same as host-gateway-ip
+	d := daemon.New(t)
+	// Verify the IP in /etc/hosts is same as the default bridge's IP
+	d.StartWithBusybox(t)
+	c := d.NewClientT(t)
+	ctx := context.Background()
+	cID := container.Run(ctx, t, c,
+		container.WithExtraHost("host.docker.internal:host-gateway"),
+	)
+	res, err := container.Exec(ctx, c, cID, []string{"cat", "/etc/hosts"})
+	assert.NilError(t, err)
+	assert.Assert(t, is.Len(res.Stderr(), 0))
+	assert.Equal(t, 0, res.ExitCode)
+	inspect, err := c.NetworkInspect(ctx, "bridge", types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	assert.Check(t, is.Contains(res.Stdout(), inspect.IPAM.Config[0].Gateway))
+	c.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true})
+	d.Stop(t)
+
+	// Verify the IP in /etc/hosts is same as host-gateway-ip
+	d.StartWithBusybox(t, "--host-gateway-ip=6.7.8.9")
+	cID = container.Run(ctx, t, c,
+		container.WithExtraHost("host.docker.internal:host-gateway"),
+	)
+	res, err = container.Exec(ctx, c, cID, []string{"cat", "/etc/hosts"})
+	assert.NilError(t, err)
+	assert.Assert(t, is.Len(res.Stderr(), 0))
+	assert.Equal(t, 0, res.ExitCode)
+	assert.Check(t, is.Contains(res.Stdout(), "6.7.8.9"))
+	c.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true})
+	d.Stop(t)
+
+}
+
+// TestRestartDaemonWithRestartingContainer simulates a case where a container is in "restarting" state when
+// dockerd is killed (due to machine reset or something else).
+//
+// Related to moby/moby#41817
+//
+// In this test we'll change the container state to "restarting".
+// This means that the container will not be 'alive' when we attempt to restore in on daemon startup.
+//
+// We could do the same with `docker run -d --resetart=always busybox:latest exit 1`, and then
+// `kill -9` dockerd while the container is in "restarting" state. This is difficult to reproduce reliably
+// in an automated test, so we manipulate on disk state instead.
+func TestRestartDaemonWithRestartingContainer(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+
+	t.Parallel()
+
+	d := daemon.New(t)
+	defer d.Cleanup(t)
+
+	d.StartWithBusybox(t, "--iptables=false")
+	defer d.Stop(t)
+
+	ctx := context.Background()
+	client := d.NewClientT(t)
+
+	// Just create the container, no need to start it to be started.
+	// We really want to make sure there is no process running when docker starts back up.
+	// We will manipulate the on disk state later
+	id := container.Create(ctx, t, client, container.WithRestartPolicy("always"), container.WithCmd("/bin/sh", "-c", "exit 1"))
+
+	d.Stop(t)
+
+	configPath := filepath.Join(d.Root, "containers", id, "config.v2.json")
+	configBytes, err := os.ReadFile(configPath)
+	assert.NilError(t, err)
+
+	var c realcontainer.Container
+
+	assert.NilError(t, json.Unmarshal(configBytes, &c))
+
+	c.State = realcontainer.NewState()
+	c.SetRestarting(&realcontainer.ExitStatus{ExitCode: 1})
+	c.HasBeenStartedBefore = true
+
+	configBytes, err = json.Marshal(&c)
+	assert.NilError(t, err)
+	assert.NilError(t, os.WriteFile(configPath, configBytes, 0600))
+
+	d.Start(t)
+
+	ctxTimeout, cancel := context.WithTimeout(ctx, 30*time.Second)
+	defer cancel()
+	chOk, chErr := client.ContainerWait(ctxTimeout, id, containerapi.WaitConditionNextExit)
+	select {
+	case <-chOk:
+	case err := <-chErr:
+		assert.NilError(t, err)
+	}
+}
diff --git a/integration/container/daemon_test.go b/integration/container/daemon_test.go
new file mode 100644
index 0000000000000..94468a440914f
--- /dev/null
+++ b/integration/container/daemon_test.go
@@ -0,0 +1,51 @@
+package container
+
+import (
+	"context"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
+)
+
+// Make sure a container that does not exit when it upon receiving it's stop signal is actually shutdown on daemon
+// startup.
+func TestContainerKillOnDaemonStart(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, testEnv.IsRootless, "scenario doesn't work with rootless mode")
+
+	t.Parallel()
+
+	d := daemon.New(t)
+	defer d.Cleanup(t)
+
+	d.StartWithBusybox(t, "--iptables=false")
+	defer d.Stop(t)
+
+	client := d.NewClientT(t)
+	ctx := context.Background()
+
+	// The intention of this container is to ignore stop signals.
+	// Sadly this means the test will take longer, but at least this test can be parallelized.
+	id := container.Run(ctx, t, client, container.WithCmd("/bin/sh", "-c", "while true; do echo hello; sleep 1; done"))
+	defer func() {
+		err := client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true})
+		assert.NilError(t, err)
+	}()
+
+	inspect, err := client.ContainerInspect(ctx, id)
+	assert.NilError(t, err)
+	assert.Assert(t, inspect.State.Running)
+
+	assert.NilError(t, d.Kill())
+	d.Start(t)
+
+	inspect, err = client.ContainerInspect(ctx, id)
+	assert.Check(t, is.Nil(err))
+	assert.Assert(t, !inspect.State.Running)
+}
diff --git a/integration/container/diff_test.go b/integration/container/diff_test.go
index 32ad94b934889..7df11ce605f5e 100644
--- a/integration/container/diff_test.go
+++ b/integration/container/diff_test.go
@@ -7,20 +7,19 @@ import (
 
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/pkg/archive"
-	"gotest.tools/assert"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestDiff(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client, container.WithCmd("sh", "-c", `mkdir /foo; echo xyzzy > /foo/bar`))
+	cID := container.Run(ctx, t, client, container.WithCmd("sh", "-c", `mkdir /foo; echo xyzzy > /foo/bar`))
 
 	// Wait for it to exit as cannot diff a running container on Windows, and
 	// it will take a few seconds to exit. Also there's no way in Windows to
diff --git a/integration/container/exec_test.go b/integration/container/exec_test.go
index 8d23f6b1cc71c..5ef8a7506dbc0 100644
--- a/integration/container/exec_test.go
+++ b/integration/container/exec_test.go
@@ -2,27 +2,95 @@ package container // import "github.com/docker/docker/integration/container"
 
 import (
 	"context"
-	"io/ioutil"
+	"io"
 	"testing"
+	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
+// TestExecWithCloseStdin adds case for moby#37870 issue.
+func TestExecWithCloseStdin(t *testing.T) {
+	skip.If(t, testEnv.RuntimeIsWindowsContainerd(), "FIXME. Hang on Windows + containerd combination")
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "broken in earlier versions")
+	defer setupTest(t)()
+
+	ctx := context.Background()
+	client := testEnv.APIClient()
+
+	// run top with detached mode
+	cID := container.Run(ctx, t, client)
+
+	expected := "closeIO"
+	execResp, err := client.ContainerExecCreate(ctx, cID,
+		types.ExecConfig{
+			AttachStdin:  true,
+			AttachStdout: true,
+			Cmd:          strslice.StrSlice([]string{"sh", "-c", "cat && echo " + expected}),
+		},
+	)
+	assert.NilError(t, err)
+
+	resp, err := client.ContainerExecAttach(ctx, execResp.ID,
+		types.ExecStartCheck{
+			Detach: false,
+			Tty:    false,
+		},
+	)
+	assert.NilError(t, err)
+	defer resp.Close()
+
+	// close stdin to send EOF to cat
+	assert.NilError(t, resp.CloseWrite())
+
+	var (
+		waitCh = make(chan struct{})
+		resCh  = make(chan struct {
+			content string
+			err     error
+		}, 1)
+	)
+
+	go func() {
+		close(waitCh)
+		defer close(resCh)
+		r, err := io.ReadAll(resp.Reader)
+
+		resCh <- struct {
+			content string
+			err     error
+		}{
+			content: string(r),
+			err:     err,
+		}
+	}()
+
+	<-waitCh
+	select {
+	case <-time.After(3 * time.Second):
+		t.Fatal("failed to read the content in time")
+	case got := <-resCh:
+		assert.NilError(t, got.err)
+
+		// NOTE: using Contains because no-tty's stream contains UX information
+		// like size, stream type.
+		assert.Assert(t, is.Contains(got.content, expected))
+	}
+}
+
 func TestExec(t *testing.T) {
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.35"), "broken in earlier versions")
-	skip.If(t, testEnv.OSType == "windows", "FIXME. Probably needs to wait for container to be in running state.")
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
-	cID := container.Run(t, ctx, client, container.WithTty(true), container.WithWorkingDir("/root"))
+	cID := container.Run(ctx, t, client, container.WithTty(true), container.WithWorkingDir("/root"))
 
 	id, err := client.ContainerExecCreate(ctx, cID,
 		types.ExecConfig{
@@ -34,6 +102,10 @@ func TestExec(t *testing.T) {
 	)
 	assert.NilError(t, err)
 
+	inspect, err := client.ContainerExecInspect(ctx, id.ID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(inspect.ExecID, id.ID))
+
 	resp, err := client.ContainerExecAttach(ctx, id.ID,
 		types.ExecStartCheck{
 			Detach: false,
@@ -42,10 +114,29 @@ func TestExec(t *testing.T) {
 	)
 	assert.NilError(t, err)
 	defer resp.Close()
-	r, err := ioutil.ReadAll(resp.Reader)
+	r, err := io.ReadAll(resp.Reader)
 	assert.NilError(t, err)
 	out := string(r)
 	assert.NilError(t, err)
-	assert.Assert(t, is.Contains(out, "PWD=/tmp"), "exec command not running in expected /tmp working directory")
+	expected := "PWD=/tmp"
+	if testEnv.OSType == "windows" {
+		expected = "PWD=C:/tmp"
+	}
+	assert.Assert(t, is.Contains(out, expected), "exec command not running in expected /tmp working directory")
 	assert.Assert(t, is.Contains(out, "FOO=BAR"), "exec command not running with expected environment variable FOO")
 }
+
+func TestExecUser(t *testing.T) {
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "broken in earlier versions")
+	skip.If(t, testEnv.OSType == "windows", "FIXME. Probably needs to wait for container to be in running state.")
+	defer setupTest(t)()
+	ctx := context.Background()
+	client := testEnv.APIClient()
+
+	cID := container.Run(ctx, t, client, container.WithTty(true), container.WithUser("1:1"))
+
+	result, err := container.Exec(ctx, client, cID, []string{"id"})
+	assert.NilError(t, err)
+
+	assert.Assert(t, is.Contains(result.Stdout(), "uid=1(daemon) gid=1(daemon)"), "exec command not running as uid/gid 1")
+}
diff --git a/integration/container/export_test.go b/integration/container/export_test.go
index ed3a3305009e2..bcf85cf0c0966 100644
--- a/integration/container/export_test.go
+++ b/integration/container/export_test.go
@@ -3,19 +3,19 @@ package container // import "github.com/docker/docker/integration/container"
 import (
 	"context"
 	"encoding/json"
+	"strings"
 	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/pkg/jsonmessage"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 // export an image and try to import it into a new one
@@ -23,13 +23,13 @@ func TestExportContainerAndImportImage(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client, container.WithCmd("true"))
+	cID := container.Run(ctx, t, client, container.WithCmd("true"))
 	poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond))
 
-	reference := "repo/testexp:v1"
+	reference := "repo/" + strings.ToLower(t.Name()) + ":v1"
 	exportResp, err := client.ContainerExport(ctx, cID)
 	assert.NilError(t, err)
 	importResp, err := client.ImageImport(ctx, types.ImageImportSource{
@@ -59,20 +59,19 @@ func TestExportContainerAndImportImage(t *testing.T) {
 // condition, daemon restart is needed after container creation.
 func TestExportContainerAfterDaemonRestart(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 
 	d := daemon.New(t)
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
 	d.StartWithBusybox(t)
 	defer d.Stop(t)
 
 	ctx := context.Background()
-	ctrID := container.Create(t, ctx, client)
+	ctrID := container.Create(ctx, t, c)
 
 	d.Restart(t)
 
-	_, err = client.ContainerExport(ctx, ctrID)
+	_, err := c.ContainerExport(ctx, ctrID)
 	assert.NilError(t, err)
 }
diff --git a/integration/container/health_test.go b/integration/container/health_test.go
index b90e86e0c0afa..ad0245c6a80d4 100644
--- a/integration/container/health_test.go
+++ b/integration/container/health_test.go
@@ -9,9 +9,9 @@ import (
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 // TestHealthCheckWorkdir verifies that health-checks inherit the containers'
@@ -20,9 +20,9 @@ func TestHealthCheckWorkdir(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
-	cID := container.Run(t, ctx, client, container.WithTty(true), container.WithWorkingDir("/foo"), func(c *container.TestContainerConfig) {
+	cID := container.Run(ctx, t, client, container.WithTty(true), container.WithWorkingDir("/foo"), func(c *container.TestContainerConfig) {
 		c.Config.Healthcheck = &containertypes.HealthConfig{
 			Test:     []string{"CMD-SHELL", "if [ \"$PWD\" = \"/foo\" ]; then exit 0; else exit 1; fi;"},
 			Interval: 50 * time.Millisecond,
@@ -33,6 +33,66 @@ func TestHealthCheckWorkdir(t *testing.T) {
 	poll.WaitOn(t, pollForHealthStatus(ctx, client, cID, types.Healthy), poll.WithDelay(100*time.Millisecond))
 }
 
+// GitHub #37263
+// Do not stop healthchecks just because we sent a signal to the container
+func TestHealthKillContainer(t *testing.T) {
+	skip.If(t, testEnv.OSType == "windows", "Windows only supports SIGKILL and SIGTERM? See https://github.com/moby/moby/issues/39574")
+	defer setupTest(t)()
+
+	ctx := context.Background()
+	client := testEnv.APIClient()
+
+	id := container.Run(ctx, t, client, func(c *container.TestContainerConfig) {
+		cmd := `
+# Set the initial HEALTH value so the healthcheck passes
+HEALTH="1"
+echo $HEALTH > /health
+
+# Any time doHealth is run we flip the value
+# This lets us use kill signals to determine when healtchecks have run.
+doHealth() {
+	case "$HEALTH" in
+		"0")
+			HEALTH="1"
+			;;
+		"1")
+			HEALTH="0"
+			;;
+	esac
+	echo $HEALTH > /health
+}
+
+trap 'doHealth' USR1
+
+while true; do sleep 1; done
+`
+		c.Config.Cmd = []string{"/bin/sh", "-c", cmd}
+		c.Config.Healthcheck = &containertypes.HealthConfig{
+			Test:     []string{"CMD-SHELL", `[ "$(cat /health)" = "1" ]`},
+			Interval: time.Second,
+			Retries:  5,
+		}
+	})
+
+	ctxPoll, cancel := context.WithTimeout(ctx, 30*time.Second)
+	defer cancel()
+	poll.WaitOn(t, pollForHealthStatus(ctxPoll, client, id, "healthy"), poll.WithDelay(100*time.Millisecond))
+
+	err := client.ContainerKill(ctx, id, "SIGUSR1")
+	assert.NilError(t, err)
+
+	ctxPoll, cancel = context.WithTimeout(ctx, 30*time.Second)
+	defer cancel()
+	poll.WaitOn(t, pollForHealthStatus(ctxPoll, client, id, "unhealthy"), poll.WithDelay(100*time.Millisecond))
+
+	err = client.ContainerKill(ctx, id, "SIGUSR1")
+	assert.NilError(t, err)
+
+	ctxPoll, cancel = context.WithTimeout(ctx, 30*time.Second)
+	defer cancel()
+	poll.WaitOn(t, pollForHealthStatus(ctxPoll, client, id, "healthy"), poll.WithDelay(100*time.Millisecond))
+}
+
 func pollForHealthStatus(ctx context.Context, client client.APIClient, containerID string, healthStatus string) func(log poll.LogT) poll.Result {
 	return func(log poll.LogT) poll.Result {
 		inspect, err := client.ContainerInspect(ctx, containerID)
diff --git a/integration/container/inspect_test.go b/integration/container/inspect_test.go
index 11f78b9d40308..f8606a3b09c78 100644
--- a/integration/container/inspect_test.go
+++ b/integration/container/inspect_test.go
@@ -3,16 +3,17 @@ package container // import "github.com/docker/docker/integration/container"
 import (
 	"context"
 	"encoding/json"
+	"strings"
 	"testing"
 	"time"
 
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestInspectCpusetInConfigPre120(t *testing.T) {
@@ -22,9 +23,9 @@ func TestInspectCpusetInConfigPre120(t *testing.T) {
 	client := request.NewAPIClient(t, client.WithVersion("1.19"))
 	ctx := context.Background()
 
-	name := "cpusetinconfig-pre120-" + t.Name()
+	name := strings.ToLower(t.Name())
 	// Create container with up to-date-API
-	container.Run(t, ctx, request.NewAPIClient(t), container.WithName(name),
+	container.Run(ctx, t, request.NewAPIClient(t), container.WithName(name),
 		container.WithCmd("true"),
 		func(c *container.TestContainerConfig) {
 			c.HostConfig.Resources.CpusetCpus = "0"
diff --git a/integration/container/ipcmode_linux_test.go b/integration/container/ipcmode_linux_test.go
new file mode 100644
index 0000000000000..86b60e5f5b131
--- /dev/null
+++ b/integration/container/ipcmode_linux_test.go
@@ -0,0 +1,327 @@
+package container // import "github.com/docker/docker/integration/container"
+
+import (
+	"bufio"
+	"context"
+	"os"
+	"regexp"
+	"strings"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/client"
+	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/fs"
+	"gotest.tools/v3/skip"
+)
+
+// testIpcCheckDevExists checks whether a given mount (identified by its
+// major:minor pair from /proc/self/mountinfo) exists on the host system.
+//
+// The format of /proc/self/mountinfo is like:
+//
+// 29 23 0:24 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw
+//       ^^^^\
+//            - this is the minor:major we look for
+func testIpcCheckDevExists(mm string) (bool, error) {
+	f, err := os.Open("/proc/self/mountinfo")
+	if err != nil {
+		return false, err
+	}
+	defer f.Close()
+
+	s := bufio.NewScanner(f)
+	for s.Scan() {
+		fields := strings.Fields(s.Text())
+		if len(fields) < 7 {
+			continue
+		}
+		if fields[2] == mm {
+			return true, nil
+		}
+	}
+
+	return false, s.Err()
+}
+
+// testIpcNonePrivateShareable is a helper function to test "none",
+// "private" and "shareable" modes.
+func testIpcNonePrivateShareable(t *testing.T, mode string, mustBeMounted bool, mustBeShared bool) {
+	defer setupTest(t)()
+
+	cfg := containertypes.Config{
+		Image: "busybox",
+		Cmd:   []string{"top"},
+	}
+	hostCfg := containertypes.HostConfig{
+		IpcMode: containertypes.IpcMode(mode),
+	}
+	client := testEnv.APIClient()
+	ctx := context.Background()
+
+	resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(len(resp.Warnings), 0))
+
+	err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
+	assert.NilError(t, err)
+
+	// get major:minor pair for /dev/shm from container's /proc/self/mountinfo
+	cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo"
+	result, err := container.Exec(ctx, client, resp.ID, []string{"sh", "-c", cmd})
+	assert.NilError(t, err)
+	mm := result.Combined()
+	if !mustBeMounted {
+		assert.Check(t, is.Equal(mm, ""))
+		// no more checks to perform
+		return
+	}
+	assert.Check(t, is.Equal(true, regexp.MustCompile("^[0-9]+:[0-9]+$").MatchString(mm)))
+
+	shared, err := testIpcCheckDevExists(mm)
+	assert.NilError(t, err)
+	t.Logf("[testIpcPrivateShareable] ipcmode: %v, ipcdev: %v, shared: %v, mustBeShared: %v\n", mode, mm, shared, mustBeShared)
+	assert.Check(t, is.Equal(shared, mustBeShared))
+}
+
+// TestIpcModeNone checks the container "none" IPC mode
+// (--ipc none) works as expected. It makes sure there is no
+// /dev/shm mount inside the container.
+func TestIpcModeNone(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+
+	testIpcNonePrivateShareable(t, "none", false, false)
+}
+
+// TestAPIIpcModePrivate checks the container private IPC mode
+// (--ipc private) works as expected. It gets the minor:major pair
+// of /dev/shm mount from the container, and makes sure there is no
+// such pair on the host.
+func TestIpcModePrivate(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+
+	testIpcNonePrivateShareable(t, "private", true, false)
+}
+
+// TestAPIIpcModeShareable checks the container shareable IPC mode
+// (--ipc shareable) works as expected. It gets the minor:major pair
+// of /dev/shm mount from the container, and makes sure such pair
+// also exists on the host.
+func TestIpcModeShareable(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
+
+	testIpcNonePrivateShareable(t, "shareable", true, true)
+}
+
+// testIpcContainer is a helper function to test --ipc container:NNN mode in various scenarios
+func testIpcContainer(t *testing.T, donorMode string, mustWork bool) {
+	t.Helper()
+
+	defer setupTest(t)()
+
+	cfg := containertypes.Config{
+		Image: "busybox",
+		Cmd:   []string{"top"},
+	}
+	hostCfg := containertypes.HostConfig{
+		IpcMode: containertypes.IpcMode(donorMode),
+	}
+	ctx := context.Background()
+	client := testEnv.APIClient()
+
+	// create and start the "donor" container
+	resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(len(resp.Warnings), 0))
+	name1 := resp.ID
+
+	err = client.ContainerStart(ctx, name1, types.ContainerStartOptions{})
+	assert.NilError(t, err)
+
+	// create and start the second container
+	hostCfg.IpcMode = containertypes.IpcMode("container:" + name1)
+	resp, err = client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(len(resp.Warnings), 0))
+	name2 := resp.ID
+
+	err = client.ContainerStart(ctx, name2, types.ContainerStartOptions{})
+	if !mustWork {
+		// start should fail with a specific error
+		assert.Check(t, is.ErrorContains(err, "non-shareable IPC"))
+		// no more checks to perform here
+		return
+	}
+
+	// start should succeed
+	assert.NilError(t, err)
+
+	// check that IPC is shared
+	// 1. create a file in the first container
+	_, err = container.Exec(ctx, client, name1, []string{"sh", "-c", "printf covfefe > /dev/shm/bar"})
+	assert.NilError(t, err)
+	// 2. check it's the same file in the second one
+	result, err := container.Exec(ctx, client, name2, []string{"cat", "/dev/shm/bar"})
+	assert.NilError(t, err)
+	out := result.Combined()
+	assert.Check(t, is.Equal(true, regexp.MustCompile("^covfefe$").MatchString(out)))
+}
+
+// TestAPIIpcModeShareableAndPrivate checks that
+// 1) a container created with --ipc container:ID can use IPC of another shareable container.
+// 2) a container created with --ipc container:ID can NOT use IPC of another private container.
+func TestAPIIpcModeShareableAndContainer(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+
+	testIpcContainer(t, "shareable", true)
+
+	testIpcContainer(t, "private", false)
+}
+
+/* TestAPIIpcModeHost checks that a container created with --ipc host
+ * can use IPC of the host system.
+ */
+func TestAPIIpcModeHost(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.IsUserNamespace)
+	skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
+
+	cfg := containertypes.Config{
+		Image: "busybox",
+		Cmd:   []string{"top"},
+	}
+	hostCfg := containertypes.HostConfig{
+		IpcMode: containertypes.IPCModeHost,
+	}
+	ctx := context.Background()
+
+	client := testEnv.APIClient()
+	resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, nil, "")
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(len(resp.Warnings), 0))
+	name := resp.ID
+
+	err = client.ContainerStart(ctx, name, types.ContainerStartOptions{})
+	assert.NilError(t, err)
+
+	// check that IPC is shared
+	// 1. create a file inside container
+	_, err = container.Exec(ctx, client, name, []string{"sh", "-c", "printf covfefe > /dev/shm/." + name})
+	assert.NilError(t, err)
+	// 2. check it's the same on the host
+	bytes, err := os.ReadFile("/dev/shm/." + name)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal("covfefe", string(bytes)))
+	// 3. clean up
+	_, err = container.Exec(ctx, client, name, []string{"rm", "-f", "/dev/shm/." + name})
+	assert.NilError(t, err)
+}
+
+// testDaemonIpcPrivateShareable is a helper function to test "private" and "shareable" daemon default ipc modes.
+func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...string) {
+	defer setupTest(t)()
+
+	d := daemon.New(t)
+	d.StartWithBusybox(t, arg...)
+	defer d.Stop(t)
+
+	c := d.NewClientT(t)
+
+	cfg := containertypes.Config{
+		Image: "busybox",
+		Cmd:   []string{"top"},
+	}
+	ctx := context.Background()
+
+	resp, err := c.ContainerCreate(ctx, &cfg, &containertypes.HostConfig{}, nil, nil, "")
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(len(resp.Warnings), 0))
+
+	err = c.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
+	assert.NilError(t, err)
+
+	// get major:minor pair for /dev/shm from container's /proc/self/mountinfo
+	cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo"
+	result, err := container.Exec(ctx, c, resp.ID, []string{"sh", "-c", cmd})
+	assert.NilError(t, err)
+	mm := result.Combined()
+	assert.Check(t, is.Equal(true, regexp.MustCompile("^[0-9]+:[0-9]+$").MatchString(mm)))
+
+	shared, err := testIpcCheckDevExists(mm)
+	assert.NilError(t, err)
+	t.Logf("[testDaemonIpcPrivateShareable] ipcdev: %v, shared: %v, mustBeShared: %v\n", mm, shared, mustBeShared)
+	assert.Check(t, is.Equal(shared, mustBeShared))
+}
+
+// TestDaemonIpcModeShareable checks that --default-ipc-mode shareable works as intended.
+func TestDaemonIpcModeShareable(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
+
+	testDaemonIpcPrivateShareable(t, true, "--default-ipc-mode", "shareable")
+}
+
+// TestDaemonIpcModePrivate checks that --default-ipc-mode private works as intended.
+func TestDaemonIpcModePrivate(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+
+	testDaemonIpcPrivateShareable(t, false, "--default-ipc-mode", "private")
+}
+
+// used to check if an IpcMode given in config works as intended
+func testDaemonIpcFromConfig(t *testing.T, mode string, mustExist bool) {
+	skip.If(t, testEnv.IsRootless, "cannot test /dev/shm in rootless")
+	config := `{"default-ipc-mode": "` + mode + `"}`
+	file := fs.NewFile(t, "test-daemon-ipc-config", fs.WithContent(config))
+	defer file.Remove()
+
+	testDaemonIpcPrivateShareable(t, mustExist, "--config-file", file.Path())
+}
+
+// TestDaemonIpcModePrivateFromConfig checks that "default-ipc-mode: private" config works as intended.
+func TestDaemonIpcModePrivateFromConfig(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+
+	testDaemonIpcFromConfig(t, "private", false)
+}
+
+// TestDaemonIpcModeShareableFromConfig checks that "default-ipc-mode: shareable" config works as intended.
+func TestDaemonIpcModeShareableFromConfig(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+
+	testDaemonIpcFromConfig(t, "shareable", true)
+}
+
+// TestIpcModeOlderClient checks that older client gets shareable IPC mode
+// by default, even when the daemon default is private.
+func TestIpcModeOlderClient(t *testing.T) {
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "requires a daemon with DefaultIpcMode: private")
+	c := testEnv.APIClient()
+	skip.If(t, versions.LessThan(c.ClientVersion(), "1.40"), "requires client API >= 1.40")
+
+	t.Parallel()
+
+	ctx := context.Background()
+
+	// pre-check: default ipc mode in daemon is private
+	cID := container.Create(ctx, t, c, container.WithAutoRemove)
+
+	inspect, err := c.ContainerInspect(ctx, cID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(string(inspect.HostConfig.IpcMode), "private"))
+
+	// main check: using older client creates "shareable" container
+	c = request.NewAPIClient(t, client.WithVersion("1.39"))
+	cID = container.Create(ctx, t, c, container.WithAutoRemove)
+
+	inspect, err = c.ContainerInspect(ctx, cID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(string(inspect.HostConfig.IpcMode), "shareable"))
+}
diff --git a/integration/container/ipcmode_test.go b/integration/container/ipcmode_test.go
deleted file mode 100644
index 7f4f818cfd400..0000000000000
--- a/integration/container/ipcmode_test.go
+++ /dev/null
@@ -1,295 +0,0 @@
-package container // import "github.com/docker/docker/integration/container"
-
-import (
-	"bufio"
-	"context"
-	"io/ioutil"
-	"os"
-	"regexp"
-	"strings"
-	"testing"
-
-	"github.com/docker/docker/api/types"
-	containertypes "github.com/docker/docker/api/types/container"
-	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/fs"
-	"gotest.tools/skip"
-)
-
-// testIpcCheckDevExists checks whether a given mount (identified by its
-// major:minor pair from /proc/self/mountinfo) exists on the host system.
-//
-// The format of /proc/self/mountinfo is like:
-//
-// 29 23 0:24 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw
-//       ^^^^\
-//            - this is the minor:major we look for
-func testIpcCheckDevExists(mm string) (bool, error) {
-	f, err := os.Open("/proc/self/mountinfo")
-	if err != nil {
-		return false, err
-	}
-	defer f.Close()
-
-	s := bufio.NewScanner(f)
-	for s.Scan() {
-		fields := strings.Fields(s.Text())
-		if len(fields) < 7 {
-			continue
-		}
-		if fields[2] == mm {
-			return true, nil
-		}
-	}
-
-	return false, s.Err()
-}
-
-// testIpcNonePrivateShareable is a helper function to test "none",
-// "private" and "shareable" modes.
-func testIpcNonePrivateShareable(t *testing.T, mode string, mustBeMounted bool, mustBeShared bool) {
-	defer setupTest(t)()
-
-	cfg := containertypes.Config{
-		Image: "busybox",
-		Cmd:   []string{"top"},
-	}
-	hostCfg := containertypes.HostConfig{
-		IpcMode: containertypes.IpcMode(mode),
-	}
-	client := request.NewAPIClient(t)
-	ctx := context.Background()
-
-	resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
-	assert.NilError(t, err)
-	assert.Check(t, is.Equal(len(resp.Warnings), 0))
-
-	err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
-	assert.NilError(t, err)
-
-	// get major:minor pair for /dev/shm from container's /proc/self/mountinfo
-	cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo"
-	result, err := container.Exec(ctx, client, resp.ID, []string{"sh", "-c", cmd})
-	assert.NilError(t, err)
-	mm := result.Combined()
-	if !mustBeMounted {
-		assert.Check(t, is.Equal(mm, ""))
-		// no more checks to perform
-		return
-	}
-	assert.Check(t, is.Equal(true, regexp.MustCompile("^[0-9]+:[0-9]+$").MatchString(mm)))
-
-	shared, err := testIpcCheckDevExists(mm)
-	assert.NilError(t, err)
-	t.Logf("[testIpcPrivateShareable] ipcmode: %v, ipcdev: %v, shared: %v, mustBeShared: %v\n", mode, mm, shared, mustBeShared)
-	assert.Check(t, is.Equal(shared, mustBeShared))
-}
-
-// TestIpcModeNone checks the container "none" IPC mode
-// (--ipc none) works as expected. It makes sure there is no
-// /dev/shm mount inside the container.
-func TestIpcModeNone(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon())
-
-	testIpcNonePrivateShareable(t, "none", false, false)
-}
-
-// TestAPIIpcModePrivate checks the container private IPC mode
-// (--ipc private) works as expected. It gets the minor:major pair
-// of /dev/shm mount from the container, and makes sure there is no
-// such pair on the host.
-func TestIpcModePrivate(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon())
-
-	testIpcNonePrivateShareable(t, "private", true, false)
-}
-
-// TestAPIIpcModeShareable checks the container shareable IPC mode
-// (--ipc shareable) works as expected. It gets the minor:major pair
-// of /dev/shm mount from the container, and makes sure such pair
-// also exists on the host.
-func TestIpcModeShareable(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon())
-
-	testIpcNonePrivateShareable(t, "shareable", true, true)
-}
-
-// testIpcContainer is a helper function to test --ipc container:NNN mode in various scenarios
-func testIpcContainer(t *testing.T, donorMode string, mustWork bool) {
-	t.Helper()
-
-	defer setupTest(t)()
-
-	cfg := containertypes.Config{
-		Image: "busybox",
-		Cmd:   []string{"top"},
-	}
-	hostCfg := containertypes.HostConfig{
-		IpcMode: containertypes.IpcMode(donorMode),
-	}
-	ctx := context.Background()
-	client := request.NewAPIClient(t)
-
-	// create and start the "donor" container
-	resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
-	assert.NilError(t, err)
-	assert.Check(t, is.Equal(len(resp.Warnings), 0))
-	name1 := resp.ID
-
-	err = client.ContainerStart(ctx, name1, types.ContainerStartOptions{})
-	assert.NilError(t, err)
-
-	// create and start the second container
-	hostCfg.IpcMode = containertypes.IpcMode("container:" + name1)
-	resp, err = client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
-	assert.NilError(t, err)
-	assert.Check(t, is.Equal(len(resp.Warnings), 0))
-	name2 := resp.ID
-
-	err = client.ContainerStart(ctx, name2, types.ContainerStartOptions{})
-	if !mustWork {
-		// start should fail with a specific error
-		assert.Check(t, is.ErrorContains(err, "non-shareable IPC"))
-		// no more checks to perform here
-		return
-	}
-
-	// start should succeed
-	assert.NilError(t, err)
-
-	// check that IPC is shared
-	// 1. create a file in the first container
-	_, err = container.Exec(ctx, client, name1, []string{"sh", "-c", "printf covfefe > /dev/shm/bar"})
-	assert.NilError(t, err)
-	// 2. check it's the same file in the second one
-	result, err := container.Exec(ctx, client, name2, []string{"cat", "/dev/shm/bar"})
-	assert.NilError(t, err)
-	out := result.Combined()
-	assert.Check(t, is.Equal(true, regexp.MustCompile("^covfefe$").MatchString(out)))
-}
-
-// TestAPIIpcModeShareableAndPrivate checks that
-// 1) a container created with --ipc container:ID can use IPC of another shareable container.
-// 2) a container created with --ipc container:ID can NOT use IPC of another private container.
-func TestAPIIpcModeShareableAndContainer(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
-
-	testIpcContainer(t, "shareable", true)
-
-	testIpcContainer(t, "private", false)
-}
-
-/* TestAPIIpcModeHost checks that a container created with --ipc host
- * can use IPC of the host system.
- */
-func TestAPIIpcModeHost(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon() || testEnv.IsUserNamespace())
-
-	cfg := containertypes.Config{
-		Image: "busybox",
-		Cmd:   []string{"top"},
-	}
-	hostCfg := containertypes.HostConfig{
-		IpcMode: containertypes.IpcMode("host"),
-	}
-	ctx := context.Background()
-
-	client := testEnv.APIClient()
-	resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
-	assert.NilError(t, err)
-	assert.Check(t, is.Equal(len(resp.Warnings), 0))
-	name := resp.ID
-
-	err = client.ContainerStart(ctx, name, types.ContainerStartOptions{})
-	assert.NilError(t, err)
-
-	// check that IPC is shared
-	// 1. create a file inside container
-	_, err = container.Exec(ctx, client, name, []string{"sh", "-c", "printf covfefe > /dev/shm/." + name})
-	assert.NilError(t, err)
-	// 2. check it's the same on the host
-	bytes, err := ioutil.ReadFile("/dev/shm/." + name)
-	assert.NilError(t, err)
-	assert.Check(t, is.Equal("covfefe", string(bytes)))
-	// 3. clean up
-	_, err = container.Exec(ctx, client, name, []string{"rm", "-f", "/dev/shm/." + name})
-	assert.NilError(t, err)
-}
-
-// testDaemonIpcPrivateShareable is a helper function to test "private" and "shareable" daemon default ipc modes.
-func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...string) {
-	defer setupTest(t)()
-
-	d := daemon.New(t)
-	d.StartWithBusybox(t, arg...)
-	defer d.Stop(t)
-
-	client, err := d.NewClient()
-	assert.Check(t, err, "error creating client")
-
-	cfg := containertypes.Config{
-		Image: "busybox",
-		Cmd:   []string{"top"},
-	}
-	ctx := context.Background()
-
-	resp, err := client.ContainerCreate(ctx, &cfg, &containertypes.HostConfig{}, nil, "")
-	assert.NilError(t, err)
-	assert.Check(t, is.Equal(len(resp.Warnings), 0))
-
-	err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
-	assert.NilError(t, err)
-
-	// get major:minor pair for /dev/shm from container's /proc/self/mountinfo
-	cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo"
-	result, err := container.Exec(ctx, client, resp.ID, []string{"sh", "-c", cmd})
-	assert.NilError(t, err)
-	mm := result.Combined()
-	assert.Check(t, is.Equal(true, regexp.MustCompile("^[0-9]+:[0-9]+$").MatchString(mm)))
-
-	shared, err := testIpcCheckDevExists(mm)
-	assert.NilError(t, err)
-	t.Logf("[testDaemonIpcPrivateShareable] ipcdev: %v, shared: %v, mustBeShared: %v\n", mm, shared, mustBeShared)
-	assert.Check(t, is.Equal(shared, mustBeShared))
-}
-
-// TestDaemonIpcModeShareable checks that --default-ipc-mode shareable works as intended.
-func TestDaemonIpcModeShareable(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon())
-
-	testDaemonIpcPrivateShareable(t, true, "--default-ipc-mode", "shareable")
-}
-
-// TestDaemonIpcModePrivate checks that --default-ipc-mode private works as intended.
-func TestDaemonIpcModePrivate(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon())
-
-	testDaemonIpcPrivateShareable(t, false, "--default-ipc-mode", "private")
-}
-
-// used to check if an IpcMode given in config works as intended
-func testDaemonIpcFromConfig(t *testing.T, mode string, mustExist bool) {
-	config := `{"default-ipc-mode": "` + mode + `"}`
-	file := fs.NewFile(t, "test-daemon-ipc-config", fs.WithContent(config))
-	defer file.Remove()
-
-	testDaemonIpcPrivateShareable(t, mustExist, "--config-file", file.Path())
-}
-
-// TestDaemonIpcModePrivateFromConfig checks that "default-ipc-mode: private" config works as intended.
-func TestDaemonIpcModePrivateFromConfig(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon())
-
-	testDaemonIpcFromConfig(t, "private", false)
-}
-
-// TestDaemonIpcModeShareableFromConfig checks that "default-ipc-mode: shareable" config works as intended.
-func TestDaemonIpcModeShareableFromConfig(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon())
-
-	testDaemonIpcFromConfig(t, "shareable", true)
-}
diff --git a/integration/container/kill_test.go b/integration/container/kill_test.go
index 0b51b5163ca4f..50bc72063c0ce 100644
--- a/integration/container/kill_test.go
+++ b/integration/container/kill_test.go
@@ -5,63 +5,68 @@ import (
 	"testing"
 	"time"
 
-	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestKillContainerInvalidSignal(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
-	id := container.Run(t, ctx, client)
+	id := container.Run(ctx, t, client)
 
 	err := client.ContainerKill(ctx, id, "0")
-	assert.Error(t, err, "Error response from daemon: Invalid signal: 0")
+	assert.ErrorContains(t, err, "Error response from daemon:")
+	assert.ErrorContains(t, err, "nvalid signal: 0") // match "(I|i)nvalid" case-insensitive to allow testing against older daemons.
 	poll.WaitOn(t, container.IsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond))
 
 	err = client.ContainerKill(ctx, id, "SIG42")
-	assert.Error(t, err, "Error response from daemon: Invalid signal: SIG42")
+	assert.ErrorContains(t, err, "Error response from daemon:")
+	assert.ErrorContains(t, err, "nvalid signal: SIG42") // match "(I|i)nvalid" case-insensitive to allow testing against older daemons.
 	poll.WaitOn(t, container.IsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond))
 }
 
 func TestKillContainer(t *testing.T) {
-	skip.If(t, testEnv.OSType == "windows", "TODO Windows: FIXME. No SIGWINCH")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	testCases := []struct {
 		doc    string
 		signal string
 		status string
+		skipOs string
 	}{
 		{
 			doc:    "no signal",
 			signal: "",
 			status: "exited",
+			skipOs: "",
 		},
 		{
 			doc:    "non killing signal",
 			signal: "SIGWINCH",
 			status: "running",
+			skipOs: "windows",
 		},
 		{
 			doc:    "killing signal",
 			signal: "SIGTERM",
 			status: "exited",
+			skipOs: "",
 		},
 	}
 
 	for _, tc := range testCases {
 		tc := tc
 		t.Run(tc.doc, func(t *testing.T) {
+			skip.If(t, testEnv.OSType == tc.skipOs, "Windows does not support SIGWINCH")
 			ctx := context.Background()
-			id := container.Run(t, ctx, client)
+			id := container.Run(ctx, t, client)
 			err := client.ContainerKill(ctx, id, tc.signal)
 			assert.NilError(t, err)
 
@@ -73,7 +78,7 @@ func TestKillContainer(t *testing.T) {
 func TestKillWithStopSignalAndRestartPolicies(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows", "Windows only supports 1.25 or later")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	testCases := []struct {
 		doc        string
@@ -96,12 +101,11 @@ func TestKillWithStopSignalAndRestartPolicies(t *testing.T) {
 		tc := tc
 		t.Run(tc.doc, func(t *testing.T) {
 			ctx := context.Background()
-			id := container.Run(t, ctx, client, func(c *container.TestContainerConfig) {
-				c.Config.StopSignal = tc.stopsignal
-				c.HostConfig.RestartPolicy = containertypes.RestartPolicy{
-					Name: "always",
-				}
-			})
+			id := container.Run(ctx, t, client,
+				container.WithRestartPolicy("always"),
+				func(c *container.TestContainerConfig) {
+					c.Config.StopSignal = tc.stopsignal
+				})
 			err := client.ContainerKill(ctx, id, "TERM")
 			assert.NilError(t, err)
 
@@ -114,8 +118,8 @@ func TestKillStoppedContainer(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows", "Windows only supports 1.25 or later")
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
-	id := container.Create(t, ctx, client)
+	client := testEnv.APIClient()
+	id := container.Create(ctx, t, client)
 	err := client.ContainerKill(ctx, id, "SIGKILL")
 	assert.Assert(t, is.ErrorContains(err, ""))
 	assert.Assert(t, is.Contains(err.Error(), "is not running"))
@@ -126,7 +130,7 @@ func TestKillStoppedContainerAPIPre120(t *testing.T) {
 	defer setupTest(t)()
 	ctx := context.Background()
 	client := request.NewAPIClient(t, client.WithVersion("1.19"))
-	id := container.Create(t, ctx, client)
+	id := container.Create(ctx, t, client)
 	err := client.ContainerKill(ctx, id, "SIGKILL")
 	assert.NilError(t, err)
 }
@@ -139,7 +143,7 @@ func TestKillDifferentUserContainer(t *testing.T) {
 	ctx := context.Background()
 	client := request.NewAPIClient(t, client.WithVersion("1.19"))
 
-	id := container.Run(t, ctx, client, func(c *container.TestContainerConfig) {
+	id := container.Run(ctx, t, client, func(c *container.TestContainerConfig) {
 		c.Config.User = "daemon"
 	})
 	poll.WaitOn(t, container.IsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond))
@@ -150,13 +154,16 @@ func TestKillDifferentUserContainer(t *testing.T) {
 }
 
 func TestInspectOomKilledTrue(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows" || !testEnv.DaemonInfo.MemoryLimit || !testEnv.DaemonInfo.SwapLimit)
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
+	skip.If(t, !testEnv.DaemonInfo.MemoryLimit || !testEnv.DaemonInfo.SwapLimit)
+	skip.If(t, testEnv.DaemonInfo.CgroupVersion == "2", "FIXME: flaky on cgroup v2 (https://github.com/moby/moby/issues/41929)")
 
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
-	cID := container.Run(t, ctx, client, container.WithCmd("sh", "-c", "x=a; while true; do x=$x$x$x$x; done"), func(c *container.TestContainerConfig) {
+	cID := container.Run(ctx, t, client, container.WithCmd("sh", "-c", "x=a; while true; do x=$x$x$x$x; done"), func(c *container.TestContainerConfig) {
 		c.HostConfig.Resources.Memory = 32 * 1024 * 1024
 	})
 
@@ -172,9 +179,9 @@ func TestInspectOomKilledFalse(t *testing.T) {
 
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
-	cID := container.Run(t, ctx, client, container.WithCmd("sh", "-c", "echo hello world"))
+	cID := container.Run(ctx, t, client, container.WithCmd("sh", "-c", "echo hello world"))
 
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond))
 
diff --git a/integration/container/links_linux_test.go b/integration/container/links_linux_test.go
index 775bf6d365b17..3f038d8b90636 100644
--- a/integration/container/links_linux_test.go
+++ b/integration/container/links_linux_test.go
@@ -2,30 +2,29 @@ package container // import "github.com/docker/docker/integration/container"
 
 import (
 	"context"
-	"io/ioutil"
 	"os"
 	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestLinksEtcHostsContentMatch(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of /etc/hosts")
 
-	hosts, err := ioutil.ReadFile("/etc/hosts")
+	hosts, err := os.ReadFile("/etc/hosts")
 	skip.If(t, os.IsNotExist(err))
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client, container.WithNetworkMode("host"))
+	cID := container.Run(ctx, t, client, container.WithNetworkMode("host"))
 	res, err := container.Exec(ctx, client, cID, []string{"cat", "/etc/hosts"})
 	assert.NilError(t, err)
 	assert.Assert(t, is.Len(res.Stderr(), 0))
@@ -38,13 +37,13 @@ func TestLinksContainerNames(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	containerA := "first_" + t.Name()
 	containerB := "second_" + t.Name()
-	container.Run(t, ctx, client, container.WithName(containerA))
-	container.Run(t, ctx, client, container.WithName(containerB), container.WithLinks(containerA+":"+containerA))
+	container.Run(ctx, t, client, container.WithName(containerA))
+	container.Run(ctx, t, client, container.WithName(containerB), container.WithLinks(containerA+":"+containerA))
 
 	f := filters.NewArgs(filters.Arg("name", containerA))
 
diff --git a/integration/container/logs_test.go b/integration/container/logs_test.go
index 68fbe13a73709..837c75ac08a01 100644
--- a/integration/container/logs_test.go
+++ b/integration/container/logs_test.go
@@ -2,27 +2,26 @@ package container // import "github.com/docker/docker/integration/container"
 
 import (
 	"context"
-	"io/ioutil"
+	"io"
 	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/pkg/stdcopy"
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
 // Regression test for #35370
 // Makes sure that when following we don't get an EOF error when there are no logs
 func TestLogsFollowTailEmpty(t *testing.T) {
 	// FIXME(vdemeester) fails on a e2e run on linux...
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	id := container.Run(t, ctx, client, container.WithCmd("sleep", "100000"))
+	id := container.Run(ctx, t, client, container.WithCmd("sleep", "100000"))
 
 	logs, err := client.ContainerLogs(ctx, id, types.ContainerLogsOptions{ShowStdout: true, Tail: "2"})
 	if logs != nil {
@@ -30,6 +29,6 @@ func TestLogsFollowTailEmpty(t *testing.T) {
 	}
 	assert.Check(t, err)
 
-	_, err = stdcopy.StdCopy(ioutil.Discard, ioutil.Discard, logs)
+	_, err = stdcopy.StdCopy(io.Discard, io.Discard, logs)
 	assert.Check(t, err)
 }
diff --git a/integration/container/main_test.go b/integration/container/main_test.go
index fb87fddcc260f..bb7b2eb02f5da 100644
--- a/integration/container/main_test.go
+++ b/integration/container/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
diff --git a/integration/container/mounts_linux_test.go b/integration/container/mounts_linux_test.go
index 8e7e663fd4f7c..3c7aabda24578 100644
--- a/integration/container/mounts_linux_test.go
+++ b/integration/container/mounts_linux_test.go
@@ -5,23 +5,28 @@ import (
 	"fmt"
 	"path/filepath"
 	"testing"
+	"time"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/container"
-	"github.com/docker/docker/api/types/mount"
+	containertypes "github.com/docker/docker/api/types/container"
+	mounttypes "github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/api/types/network"
+	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/internal/test/request"
+	"github.com/docker/docker/integration/internal/container"
 	"github.com/docker/docker/pkg/system"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/fs"
-	"gotest.tools/skip"
+	"github.com/moby/sys/mount"
+	"github.com/moby/sys/mountinfo"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/fs"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestContainerNetworkMountsNoChown(t *testing.T) {
 	// chown only applies to Linux bind mounted volumes; must be same host to verify
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows" || testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 
 	defer setupTest(t)()
 
@@ -32,11 +37,11 @@ func TestContainerNetworkMountsNoChown(t *testing.T) {
 
 	tmpNWFileMount := tmpDir.Join("nwfile")
 
-	config := container.Config{
+	config := containertypes.Config{
 		Image: "busybox",
 	}
-	hostConfig := container.HostConfig{
-		Mounts: []mount.Mount{
+	hostConfig := containertypes.HostConfig{
+		Mounts: []mounttypes.Mount{
 			{
 				Type:   "bind",
 				Source: tmpNWFileMount,
@@ -55,11 +60,11 @@ func TestContainerNetworkMountsNoChown(t *testing.T) {
 		},
 	}
 
-	cli, err := client.NewEnvClient()
+	cli, err := client.NewClientWithOpts(client.FromEnv)
 	assert.NilError(t, err)
 	defer cli.Close()
 
-	ctrCreate, err := cli.ContainerCreate(ctx, &config, &hostConfig, &network.NetworkingConfig{}, "")
+	ctrCreate, err := cli.ContainerCreate(ctx, &config, &hostConfig, &network.NetworkingConfig{}, nil, "")
 	assert.NilError(t, err)
 	// container will exit immediately because of no tty, but we only need the start sequence to test the condition
 	err = cli.ContainerStart(ctx, ctrCreate.ID, types.ContainerStartOptions{})
@@ -81,10 +86,10 @@ func TestContainerNetworkMountsNoChown(t *testing.T) {
 }
 
 func TestMountDaemonRoot(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows" || testEnv.IsRemoteDaemon())
-	t.Parallel()
+	skip.If(t, testEnv.IsRemoteDaemon)
 
-	client := request.NewAPIClient(t)
+	defer setupTest(t)()
+	client := testEnv.APIClient()
 	ctx := context.Background()
 	info, err := client.Info(ctx)
 	if err != nil {
@@ -93,39 +98,39 @@ func TestMountDaemonRoot(t *testing.T) {
 
 	for _, test := range []struct {
 		desc        string
-		propagation mount.Propagation
-		expected    mount.Propagation
+		propagation mounttypes.Propagation
+		expected    mounttypes.Propagation
 	}{
 		{
 			desc:        "default",
 			propagation: "",
-			expected:    mount.PropagationRSlave,
+			expected:    mounttypes.PropagationRSlave,
 		},
 		{
 			desc:        "private",
-			propagation: mount.PropagationPrivate,
+			propagation: mounttypes.PropagationPrivate,
 		},
 		{
 			desc:        "rprivate",
-			propagation: mount.PropagationRPrivate,
+			propagation: mounttypes.PropagationRPrivate,
 		},
 		{
 			desc:        "slave",
-			propagation: mount.PropagationSlave,
+			propagation: mounttypes.PropagationSlave,
 		},
 		{
 			desc:        "rslave",
-			propagation: mount.PropagationRSlave,
-			expected:    mount.PropagationRSlave,
+			propagation: mounttypes.PropagationRSlave,
+			expected:    mounttypes.PropagationRSlave,
 		},
 		{
 			desc:        "shared",
-			propagation: mount.PropagationShared,
+			propagation: mounttypes.PropagationShared,
 		},
 		{
 			desc:        "rshared",
-			propagation: mount.PropagationRShared,
-			expected:    mount.PropagationRShared,
+			propagation: mounttypes.PropagationRShared,
+			expected:    mounttypes.PropagationRShared,
 		},
 	} {
 		t.Run(test.desc, func(t *testing.T) {
@@ -139,26 +144,26 @@ func TestMountDaemonRoot(t *testing.T) {
 			bindSpecRoot := info.DockerRootDir + ":" + "/foo" + propagationSpec
 			bindSpecSub := filepath.Join(info.DockerRootDir, "containers") + ":/foo" + propagationSpec
 
-			for name, hc := range map[string]*container.HostConfig{
+			for name, hc := range map[string]*containertypes.HostConfig{
 				"bind root":    {Binds: []string{bindSpecRoot}},
 				"bind subpath": {Binds: []string{bindSpecSub}},
 				"mount root": {
-					Mounts: []mount.Mount{
+					Mounts: []mounttypes.Mount{
 						{
-							Type:        mount.TypeBind,
+							Type:        mounttypes.TypeBind,
 							Source:      info.DockerRootDir,
 							Target:      "/foo",
-							BindOptions: &mount.BindOptions{Propagation: test.propagation},
+							BindOptions: &mounttypes.BindOptions{Propagation: test.propagation},
 						},
 					},
 				},
 				"mount subpath": {
-					Mounts: []mount.Mount{
+					Mounts: []mounttypes.Mount{
 						{
-							Type:        mount.TypeBind,
+							Type:        mounttypes.TypeBind,
 							Source:      filepath.Join(info.DockerRootDir, "containers"),
 							Target:      "/foo",
-							BindOptions: &mount.BindOptions{Propagation: test.propagation},
+							BindOptions: &mounttypes.BindOptions{Propagation: test.propagation},
 						},
 					},
 				},
@@ -167,10 +172,10 @@ func TestMountDaemonRoot(t *testing.T) {
 					hc := hc
 					t.Parallel()
 
-					c, err := client.ContainerCreate(ctx, &container.Config{
+					c, err := client.ContainerCreate(ctx, &containertypes.Config{
 						Image: "busybox",
 						Cmd:   []string{"true"},
-					}, hc, nil, "")
+					}, hc, nil, nil, "")
 
 					if err != nil {
 						if test.expected != "" {
@@ -206,3 +211,183 @@ func TestMountDaemonRoot(t *testing.T) {
 		})
 	}
 }
+
+func TestContainerBindMountNonRecursive(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "BindOptions.NonRecursive requires API v1.40")
+	skip.If(t, testEnv.IsRootless, "cannot be tested because RootlessKit executes the daemon in private mount namespace (https://github.com/rootless-containers/rootlesskit/issues/97)")
+
+	defer setupTest(t)()
+
+	tmpDir1 := fs.NewDir(t, "tmpdir1", fs.WithMode(0755),
+		fs.WithDir("mnt", fs.WithMode(0755)))
+	defer tmpDir1.Remove()
+	tmpDir1Mnt := filepath.Join(tmpDir1.Path(), "mnt")
+	tmpDir2 := fs.NewDir(t, "tmpdir2", fs.WithMode(0755),
+		fs.WithFile("file", "should not be visible when NonRecursive", fs.WithMode(0644)))
+	defer tmpDir2.Remove()
+
+	err := mount.Mount(tmpDir2.Path(), tmpDir1Mnt, "none", "bind,ro")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		if err := mount.Unmount(tmpDir1Mnt); err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	// implicit is recursive (NonRecursive: false)
+	implicit := mounttypes.Mount{
+		Type:     "bind",
+		Source:   tmpDir1.Path(),
+		Target:   "/foo",
+		ReadOnly: true,
+	}
+	recursive := implicit
+	recursive.BindOptions = &mounttypes.BindOptions{
+		NonRecursive: false,
+	}
+	recursiveVerifier := []string{"test", "-f", "/foo/mnt/file"}
+	nonRecursive := implicit
+	nonRecursive.BindOptions = &mounttypes.BindOptions{
+		NonRecursive: true,
+	}
+	nonRecursiveVerifier := []string{"test", "!", "-f", "/foo/mnt/file"}
+
+	ctx := context.Background()
+	client := testEnv.APIClient()
+	containers := []string{
+		container.Run(ctx, t, client, container.WithMount(implicit), container.WithCmd(recursiveVerifier...)),
+		container.Run(ctx, t, client, container.WithMount(recursive), container.WithCmd(recursiveVerifier...)),
+		container.Run(ctx, t, client, container.WithMount(nonRecursive), container.WithCmd(nonRecursiveVerifier...)),
+	}
+
+	for _, c := range containers {
+		poll.WaitOn(t, container.IsSuccessful(ctx, client, c), poll.WithDelay(100*time.Millisecond))
+	}
+}
+
+func TestContainerVolumesMountedAsShared(t *testing.T) {
+	// Volume propagation is linux only. Also it creates directories for
+	// bind mounting, so needs to be same host.
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.IsUserNamespace)
+	skip.If(t, testEnv.IsRootless, "cannot be tested because RootlessKit executes the daemon in private mount namespace (https://github.com/rootless-containers/rootlesskit/issues/97)")
+
+	defer setupTest(t)()
+
+	// Prepare a source directory to bind mount
+	tmpDir1 := fs.NewDir(t, "volume-source", fs.WithMode(0755),
+		fs.WithDir("mnt1", fs.WithMode(0755)))
+	defer tmpDir1.Remove()
+	tmpDir1Mnt := filepath.Join(tmpDir1.Path(), "mnt1")
+
+	// Convert this directory into a shared mount point so that we do
+	// not rely on propagation properties of parent mount.
+	if err := mount.MakePrivate(tmpDir1.Path()); err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		if err := mount.Unmount(tmpDir1.Path()); err != nil {
+			t.Fatal(err)
+		}
+	}()
+	if err := mount.MakeShared(tmpDir1.Path()); err != nil {
+		t.Fatal(err)
+	}
+
+	sharedMount := mounttypes.Mount{
+		Type:   mounttypes.TypeBind,
+		Source: tmpDir1.Path(),
+		Target: "/volume-dest",
+		BindOptions: &mounttypes.BindOptions{
+			Propagation: mounttypes.PropagationShared,
+		},
+	}
+
+	bindMountCmd := []string{"mount", "--bind", "/volume-dest/mnt1", "/volume-dest/mnt1"}
+
+	ctx := context.Background()
+	client := testEnv.APIClient()
+	containerID := container.Run(ctx, t, client, container.WithPrivileged(true), container.WithMount(sharedMount), container.WithCmd(bindMountCmd...))
+	poll.WaitOn(t, container.IsSuccessful(ctx, client, containerID), poll.WithDelay(100*time.Millisecond))
+
+	// Make sure a bind mount under a shared volume propagated to host.
+	if mounted, _ := mountinfo.Mounted(tmpDir1Mnt); !mounted {
+		t.Fatalf("Bind mount under shared volume did not propagate to host")
+	}
+
+	mount.Unmount(tmpDir1Mnt)
+}
+
+func TestContainerVolumesMountedAsSlave(t *testing.T) {
+	// Volume propagation is linux only. Also it creates directories for
+	// bind mounting, so needs to be same host.
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.IsUserNamespace)
+	skip.If(t, testEnv.IsRootless, "cannot be tested because RootlessKit executes the daemon in private mount namespace (https://github.com/rootless-containers/rootlesskit/issues/97)")
+
+	// Prepare a source directory to bind mount
+	tmpDir1 := fs.NewDir(t, "volume-source", fs.WithMode(0755),
+		fs.WithDir("mnt1", fs.WithMode(0755)))
+	defer tmpDir1.Remove()
+	tmpDir1Mnt := filepath.Join(tmpDir1.Path(), "mnt1")
+
+	// Prepare a source directory with file in it. We will bind mount this
+	// directory and see if file shows up.
+	tmpDir2 := fs.NewDir(t, "volume-source2", fs.WithMode(0755),
+		fs.WithFile("slave-testfile", "Test", fs.WithMode(0644)))
+	defer tmpDir2.Remove()
+
+	// Convert this directory into a shared mount point so that we do
+	// not rely on propagation properties of parent mount.
+	if err := mount.MakePrivate(tmpDir1.Path()); err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		if err := mount.Unmount(tmpDir1.Path()); err != nil {
+			t.Fatal(err)
+		}
+	}()
+	if err := mount.MakeShared(tmpDir1.Path()); err != nil {
+		t.Fatal(err)
+	}
+
+	slaveMount := mounttypes.Mount{
+		Type:   mounttypes.TypeBind,
+		Source: tmpDir1.Path(),
+		Target: "/volume-dest",
+		BindOptions: &mounttypes.BindOptions{
+			Propagation: mounttypes.PropagationSlave,
+		},
+	}
+
+	topCmd := []string{"top"}
+
+	ctx := context.Background()
+	client := testEnv.APIClient()
+	containerID := container.Run(ctx, t, client, container.WithTty(true), container.WithMount(slaveMount), container.WithCmd(topCmd...))
+
+	// Bind mount tmpDir2/ onto tmpDir1/mnt1. If mount propagates inside
+	// container then contents of tmpDir2/slave-testfile should become
+	// visible at "/volume-dest/mnt1/slave-testfile"
+	if err := mount.Mount(tmpDir2.Path(), tmpDir1Mnt, "none", "bind"); err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		if err := mount.Unmount(tmpDir1Mnt); err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	mountCmd := []string{"cat", "/volume-dest/mnt1/slave-testfile"}
+
+	if result, err := container.Exec(ctx, client, containerID, mountCmd); err == nil {
+		if result.Stdout() != "Test" {
+			t.Fatalf("Bind mount under slave volume did not propagate to container")
+		}
+	} else {
+		t.Fatal(err)
+	}
+}
diff --git a/integration/container/nat_test.go b/integration/container/nat_test.go
index caaaf10fde0ef..5567e01ab39d2 100644
--- a/integration/container/nat_test.go
+++ b/integration/container/nat_test.go
@@ -5,7 +5,6 @@ import (
 	"context"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net"
 	"strings"
 	"testing"
@@ -13,16 +12,16 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/go-connections/nat"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestNetworkNat(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.OSType == "windows", "FIXME")
+	skip.If(t, testEnv.IsRemoteDaemon)
 
 	defer setupTest(t)()
 
@@ -30,18 +29,17 @@ func TestNetworkNat(t *testing.T) {
 	startServerContainer(t, msg, 8080)
 
 	endpoint := getExternalAddress(t)
-	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", endpoint.String(), 8080))
+	conn, err := net.Dial("tcp", net.JoinHostPort(endpoint.String(), "8080"))
 	assert.NilError(t, err)
 	defer conn.Close()
 
-	data, err := ioutil.ReadAll(conn)
+	data, err := io.ReadAll(conn)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(msg, strings.TrimSpace(string(data))))
 }
 
 func TestNetworkLocalhostTCPNat(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 
 	defer setupTest(t)()
 
@@ -52,13 +50,14 @@ func TestNetworkLocalhostTCPNat(t *testing.T) {
 	assert.NilError(t, err)
 	defer conn.Close()
 
-	data, err := ioutil.ReadAll(conn)
+	data, err := io.ReadAll(conn)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(msg, strings.TrimSpace(string(data))))
 }
 
 func TestNetworkLoopbackNat(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.OSType == "windows", "FIXME")
+	skip.If(t, testEnv.IsRemoteDaemon)
 
 	defer setupTest(t)()
 
@@ -67,10 +66,14 @@ func TestNetworkLoopbackNat(t *testing.T) {
 
 	endpoint := getExternalAddress(t)
 
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client, container.WithCmd("sh", "-c", fmt.Sprintf("stty raw && nc -w 5 %s 8080", endpoint.String())), container.WithTty(true), container.WithNetworkMode("container:"+serverContainerID))
+	cID := container.Run(ctx, t, client,
+		container.WithCmd("sh", "-c", fmt.Sprintf("stty raw && nc -w 1 %s 8080", endpoint.String())),
+		container.WithTty(true),
+		container.WithNetworkMode("container:"+serverContainerID),
+	)
 
 	poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond))
 
@@ -88,26 +91,34 @@ func TestNetworkLoopbackNat(t *testing.T) {
 }
 
 func startServerContainer(t *testing.T, msg string, port int) string {
-	client := request.NewAPIClient(t)
+	t.Helper()
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client, container.WithName("server-"+t.Name()), container.WithCmd("sh", "-c", fmt.Sprintf("echo %q | nc -lp %d", msg, port)), container.WithExposedPorts(fmt.Sprintf("%d/tcp", port)), func(c *container.TestContainerConfig) {
-		c.HostConfig.PortBindings = nat.PortMap{
-			nat.Port(fmt.Sprintf("%d/tcp", port)): []nat.PortBinding{
-				{
-					HostPort: fmt.Sprintf("%d", port),
+	cID := container.Run(ctx, t, client,
+		container.WithName("server-"+t.Name()),
+		container.WithCmd("sh", "-c", fmt.Sprintf("echo %q | nc -lp %d", msg, port)),
+		container.WithExposedPorts(fmt.Sprintf("%d/tcp", port)),
+		func(c *container.TestContainerConfig) {
+			c.HostConfig.PortBindings = nat.PortMap{
+				nat.Port(fmt.Sprintf("%d/tcp", port)): []nat.PortBinding{
+					{
+						HostPort: fmt.Sprintf("%d", port),
+					},
 				},
-			},
-		}
-	})
+			}
+		})
 
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
 	return cID
 }
 
+// getExternalAddress() returns the external IP-address from eth0. If eth0 has
+// multiple IP-addresses, it returns the first IPv4 IP-address; if no IPv4
+// address is present, it returns the first IP-address found.
 func getExternalAddress(t *testing.T) net.IP {
-	skip.If(t, testEnv.OSType == "windows", "FIXME")
+	t.Helper()
 	iface, err := net.InterfaceByName("eth0")
 	skip.If(t, err != nil, "Test not running with `make test-integration`. Interface eth0 not found: %s", err)
 
@@ -115,6 +126,17 @@ func getExternalAddress(t *testing.T) net.IP {
 	assert.NilError(t, err)
 	assert.Check(t, 0 != len(ifaceAddrs))
 
+	if len(ifaceAddrs) > 1 {
+		// Prefer IPv4 address if multiple addresses found, as rootlesskit
+		// does not handle IPv6 currently https://github.com/moby/moby/pull/41908#issuecomment-774200001
+		for _, a := range ifaceAddrs {
+			ifaceIP, _, err := net.ParseCIDR(a.String())
+			assert.NilError(t, err)
+			if ifaceIP.To4() != nil {
+				return ifaceIP
+			}
+		}
+	}
 	ifaceIP, _, err := net.ParseCIDR(ifaceAddrs[0].String())
 	assert.NilError(t, err)
 
diff --git a/integration/container/pause_test.go b/integration/container/pause_test.go
index 0905b95a836db..63e7fe3a6e4fc 100644
--- a/integration/container/pause_test.go
+++ b/integration/container/pause_test.go
@@ -6,26 +6,28 @@ import (
 	"testing"
 	"time"
 
+	containerderrdefs "github.com/containerd/containerd/errdefs"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestPause(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows" && testEnv.DaemonInfo.Isolation == "process")
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, client)
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
 	since := request.DaemonUnixTime(ctx, t, client, testEnv)
@@ -54,24 +56,25 @@ func TestPauseFailsOnWindowsServerContainers(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType != "windows" || testEnv.DaemonInfo.Isolation != "process")
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, client)
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
 	err := client.ContainerPause(ctx, cID)
-	assert.Check(t, is.ErrorContains(err, "cannot pause Windows Server Containers"))
+	assert.Check(t, is.ErrorContains(err, containerderrdefs.ErrNotImplemented.Error()))
 }
 
 func TestPauseStopPausedContainer(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.31"), "broken in earlier versions")
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, client)
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
 	err := client.ContainerPause(ctx, cID)
diff --git a/integration/container/pidmode_linux_test.go b/integration/container/pidmode_linux_test.go
new file mode 100644
index 0000000000000..003192ac05a9c
--- /dev/null
+++ b/integration/container/pidmode_linux_test.go
@@ -0,0 +1,37 @@
+package container // import "github.com/docker/docker/integration/container"
+
+import (
+	"context"
+	"os"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/integration/internal/container"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
+)
+
+func TestPidHost(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+
+	hostPid, err := os.Readlink("/proc/1/ns/pid")
+	assert.NilError(t, err)
+
+	defer setupTest(t)()
+	client := testEnv.APIClient()
+	ctx := context.Background()
+
+	cID := container.Run(ctx, t, client, func(c *container.TestContainerConfig) {
+		c.HostConfig.PidMode = "host"
+	})
+	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
+	cPid := container.GetContainerNS(ctx, t, client, cID, "pid")
+	assert.Assert(t, hostPid == cPid)
+
+	cID = container.Run(ctx, t, client)
+	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
+	cPid = container.GetContainerNS(ctx, t, client, cID, "pid")
+	assert.Assert(t, hostPid != cPid)
+}
diff --git a/integration/container/ps_test.go b/integration/container/ps_test.go
index 4ae07043ab9a0..6e3a09a19ce47 100644
--- a/integration/container/ps_test.go
+++ b/integration/container/ps_test.go
@@ -7,19 +7,18 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 func TestPsFilter(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	prev := container.Create(t, ctx, client)
-	top := container.Create(t, ctx, client)
-	next := container.Create(t, ctx, client)
+	prev := container.Create(ctx, t, client)
+	top := container.Create(ctx, t, client)
+	next := container.Create(ctx, t, client)
 
 	containerIDs := func(containers []types.Container) []string {
 		var entries []string
diff --git a/integration/container/remove_test.go b/integration/container/remove_test.go
index 5de13f22adcef..860d5479e33b4 100644
--- a/integration/container/remove_test.go
+++ b/integration/container/remove_test.go
@@ -9,12 +9,11 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/fs"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/fs"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
@@ -26,18 +25,18 @@ func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
 
 // Test case for #5244: `docker rm` fails if bind dir doesn't exist anymore
 func TestRemoveContainerWithRemovedVolume(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 
 	tempDir := fs.NewDir(t, "test-rm-container-with-removed-volume", fs.WithMode(0755))
 	defer tempDir.Remove()
 
-	cID := container.Run(t, ctx, client, container.WithCmd("true"), container.WithBind(tempDir.Path(), prefix+slash+"test"))
+	cID := container.Run(ctx, t, client, container.WithCmd("true"), container.WithBind(tempDir.Path(), prefix+slash+"test"))
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond))
 
 	err := os.RemoveAll(tempDir.Path())
@@ -56,11 +55,11 @@ func TestRemoveContainerWithRemovedVolume(t *testing.T) {
 func TestRemoveContainerWithVolume(t *testing.T) {
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 
-	cID := container.Run(t, ctx, client, container.WithCmd("true"), container.WithVolume(prefix+slash+"srv"))
+	cID := container.Run(ctx, t, client, container.WithCmd("true"), container.WithVolume(prefix+slash+"srv"))
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond))
 
 	insp, _, err := client.ContainerInspectWithRaw(ctx, cID, true)
@@ -81,9 +80,9 @@ func TestRemoveContainerWithVolume(t *testing.T) {
 func TestRemoveContainerRunning(t *testing.T) {
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, client)
 
 	err := client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{})
 	assert.Check(t, is.ErrorContains(err, "cannot remove a running container"))
@@ -92,9 +91,9 @@ func TestRemoveContainerRunning(t *testing.T) {
 func TestRemoveContainerForceRemoveRunning(t *testing.T) {
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, client)
 
 	err := client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{
 		Force: true,
@@ -105,7 +104,7 @@ func TestRemoveContainerForceRemoveRunning(t *testing.T) {
 func TestRemoveInvalidContainer(t *testing.T) {
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	err := client.ContainerRemove(ctx, "unknown", types.ContainerRemoveOptions{})
 	assert.Check(t, is.ErrorContains(err, "No such container"))
diff --git a/integration/container/rename_test.go b/integration/container/rename_test.go
index 5c98608a8ae39..b2ad4902ecd3c 100644
--- a/integration/container/rename_test.go
+++ b/integration/container/rename_test.go
@@ -10,12 +10,11 @@ import (
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/pkg/stringid"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 // This test simulates the scenario mentioned in #31392:
@@ -27,22 +26,22 @@ func TestRenameLinkedContainer(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	aName := "a0" + t.Name()
 	bName := "b0" + t.Name()
-	aID := container.Run(t, ctx, client, container.WithName(aName))
-	bID := container.Run(t, ctx, client, container.WithName(bName), container.WithLinks(aName))
+	aID := container.Run(ctx, t, client, container.WithName(aName))
+	bID := container.Run(ctx, t, client, container.WithName(bName), container.WithLinks(aName))
 
 	err := client.ContainerRename(ctx, aID, "a1"+t.Name())
 	assert.NilError(t, err)
 
-	container.Run(t, ctx, client, container.WithName(aName))
+	container.Run(ctx, t, client, container.WithName(aName))
 
 	err = client.ContainerRemove(ctx, bID, types.ContainerRemoveOptions{Force: true})
 	assert.NilError(t, err)
 
-	bID = container.Run(t, ctx, client, container.WithName(bName), container.WithLinks(aName))
+	bID = container.Run(ctx, t, client, container.WithName(bName), container.WithLinks(aName))
 
 	inspect, err := client.ContainerInspect(ctx, bID)
 	assert.NilError(t, err)
@@ -52,17 +51,17 @@ func TestRenameLinkedContainer(t *testing.T) {
 func TestRenameStoppedContainer(t *testing.T) {
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	oldName := "first_name" + t.Name()
-	cID := container.Run(t, ctx, client, container.WithName(oldName), container.WithCmd("sh"))
+	cID := container.Run(ctx, t, client, container.WithName(oldName), container.WithCmd("sh"))
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond))
 
 	inspect, err := client.ContainerInspect(ctx, cID)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal("/"+oldName, inspect.Name))
 
-	newName := "new_name" + stringid.GenerateNonCryptoID()
+	newName := "new_name" + stringid.GenerateRandomID()
 	err = client.ContainerRename(ctx, oldName, newName)
 	assert.NilError(t, err)
 
@@ -74,13 +73,13 @@ func TestRenameStoppedContainer(t *testing.T) {
 func TestRenameRunningContainerAndReuse(t *testing.T) {
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	oldName := "first_name" + t.Name()
-	cID := container.Run(t, ctx, client, container.WithName(oldName))
+	cID := container.Run(ctx, t, client, container.WithName(oldName))
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
-	newName := "new_name" + stringid.GenerateNonCryptoID()
+	newName := "new_name" + stringid.GenerateRandomID()
 	err := client.ContainerRename(ctx, oldName, newName)
 	assert.NilError(t, err)
 
@@ -91,7 +90,7 @@ func TestRenameRunningContainerAndReuse(t *testing.T) {
 	_, err = client.ContainerInspect(ctx, oldName)
 	assert.Check(t, is.ErrorContains(err, "No such container: "+oldName))
 
-	cID = container.Run(t, ctx, client, container.WithName(oldName))
+	cID = container.Run(ctx, t, client, container.WithName(oldName))
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
 	inspect, err = client.ContainerInspect(ctx, cID)
@@ -102,10 +101,10 @@ func TestRenameRunningContainerAndReuse(t *testing.T) {
 func TestRenameInvalidName(t *testing.T) {
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	oldName := "first_name" + t.Name()
-	cID := container.Run(t, ctx, client, container.WithName(oldName))
+	cID := container.Run(ctx, t, client, container.WithName(oldName))
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
 	err := client.ContainerRename(ctx, oldName, "new:invalid")
@@ -124,16 +123,15 @@ func TestRenameInvalidName(t *testing.T) {
 // This test is to make sure once the container has been renamed,
 // the service discovery for the (re)named container works.
 func TestRenameAnonymousContainer(t *testing.T) {
-	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	networkName := "network1" + t.Name()
 	_, err := client.NetworkCreate(ctx, networkName, types.NetworkCreate{})
 
 	assert.NilError(t, err)
-	cID := container.Run(t, ctx, client, func(c *container.TestContainerConfig) {
+	cID := container.Run(ctx, t, client, func(c *container.TestContainerConfig) {
 		c.NetworkingConfig.EndpointsConfig = map[string]*network.EndpointSettings{
 			networkName: {},
 		}
@@ -156,7 +154,7 @@ func TestRenameAnonymousContainer(t *testing.T) {
 	if testEnv.OSType == "windows" {
 		count = "-n"
 	}
-	cID = container.Run(t, ctx, client, func(c *container.TestContainerConfig) {
+	cID = container.Run(ctx, t, client, func(c *container.TestContainerConfig) {
 		c.NetworkingConfig.EndpointsConfig = map[string]*network.EndpointSettings{
 			networkName: {},
 		}
@@ -166,17 +164,17 @@ func TestRenameAnonymousContainer(t *testing.T) {
 
 	inspect, err := client.ContainerInspect(ctx, cID)
 	assert.NilError(t, err)
-	assert.Check(t, is.Equal(0, inspect.State.ExitCode), "container %s exited with the wrong exitcode: %+v", cID, inspect)
+	assert.Check(t, is.Equal(0, inspect.State.ExitCode), "container %s exited with the wrong exitcode: %s", cID, inspect.State.Error)
 }
 
 // TODO: should be a unit test
 func TestRenameContainerWithSameName(t *testing.T) {
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	oldName := "old" + t.Name()
-	cID := container.Run(t, ctx, client, container.WithName(oldName))
+	cID := container.Run(ctx, t, client, container.WithName(oldName))
 
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 	err := client.ContainerRename(ctx, oldName, oldName)
@@ -191,20 +189,20 @@ func TestRenameContainerWithSameName(t *testing.T) {
 // of the linked container should be updated so that the other
 // container could still reference to the container that is renamed.
 func TestRenameContainerWithLinkedContainer(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
 
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	db1Name := "db1" + t.Name()
-	db1ID := container.Run(t, ctx, client, container.WithName(db1Name))
+	db1ID := container.Run(ctx, t, client, container.WithName(db1Name))
 	poll.WaitOn(t, container.IsInState(ctx, client, db1ID, "running"), poll.WithDelay(100*time.Millisecond))
 
 	app1Name := "app1" + t.Name()
 	app2Name := "app2" + t.Name()
-	app1ID := container.Run(t, ctx, client, container.WithName(app1Name), container.WithLinks(db1Name+":/mysql"))
+	app1ID := container.Run(ctx, t, client, container.WithName(app1Name), container.WithLinks(db1Name+":/mysql"))
 	poll.WaitOn(t, container.IsInState(ctx, client, app1ID, "running"), poll.WithDelay(100*time.Millisecond))
 
 	err := client.ContainerRename(ctx, app1Name, app2Name)
diff --git a/integration/container/resize_test.go b/integration/container/resize_test.go
index 461ef14dfa00b..05f4816f6e9f2 100644
--- a/integration/container/resize_test.go
+++ b/integration/container/resize_test.go
@@ -9,21 +9,19 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	req "github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	req "github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestResize(t *testing.T) {
-	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, client, container.WithTty(true))
 
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
@@ -36,12 +34,11 @@ func TestResize(t *testing.T) {
 
 func TestResizeWithInvalidSize(t *testing.T) {
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.32"), "broken in earlier versions")
-	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, client)
 
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
@@ -53,10 +50,10 @@ func TestResizeWithInvalidSize(t *testing.T) {
 
 func TestResizeWhenContainerNotStarted(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client, container.WithCmd("echo"))
+	cID := container.Run(ctx, t, client, container.WithCmd("echo"))
 
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond))
 
diff --git a/integration/container/restart_test.go b/integration/container/restart_test.go
index 64b076410fc70..0c29812e8dec4 100644
--- a/integration/container/restart_test.go
+++ b/integration/container/restart_test.go
@@ -8,14 +8,17 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
-	"github.com/docker/docker/internal/test/daemon"
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"github.com/docker/docker/client"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestDaemonRestartKillContainers(t *testing.T) {
 	skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support live-restore")
 	type testCase struct {
 		desc       string
 		config     *container.Config
@@ -24,9 +27,10 @@ func TestDaemonRestartKillContainers(t *testing.T) {
 		xRunning            bool
 		xRunningLiveRestore bool
 		xStart              bool
+		xHealthCheck        bool
 	}
 
-	for _, c := range []testCase{
+	for _, tc := range []testCase{
 		{
 			desc:                "container without restart policy",
 			config:              &container.Config{Image: "busybox", Cmd: []string{"top"}},
@@ -41,6 +45,20 @@ func TestDaemonRestartKillContainers(t *testing.T) {
 			xRunningLiveRestore: true,
 			xStart:              true,
 		},
+		{
+			desc: "container with restart=always and with healthcheck",
+			config: &container.Config{Image: "busybox", Cmd: []string{"top"},
+				Healthcheck: &container.HealthConfig{
+					Test:     []string{"CMD-SHELL", "sleep 1"},
+					Interval: time.Second,
+				},
+			},
+			hostConfig:          &container.HostConfig{RestartPolicy: container.RestartPolicy{Name: "always"}},
+			xRunning:            true,
+			xRunningLiveRestore: true,
+			xStart:              true,
+			xHealthCheck:        true,
+		},
 		{
 			desc:       "container created should not be restarted",
 			config:     &container.Config{Image: "busybox", Cmd: []string{"top"}},
@@ -57,16 +75,15 @@ func TestDaemonRestartKillContainers(t *testing.T) {
 					d.Stop(t)
 				},
 			} {
-				t.Run(fmt.Sprintf("live-restore=%v/%s/%s", liveRestoreEnabled, c.desc, fnName), func(t *testing.T) {
-					c := c
+				t.Run(fmt.Sprintf("live-restore=%v/%s/%s", liveRestoreEnabled, tc.desc, fnName), func(t *testing.T) {
+					c := tc
 					liveRestoreEnabled := liveRestoreEnabled
 					stopDaemon := stopDaemon
 
 					t.Parallel()
 
 					d := daemon.New(t)
-					client, err := d.NewClient()
-					assert.NilError(t, err)
+					client := d.NewClientT(t)
 
 					args := []string{"--iptables=false"}
 					if liveRestoreEnabled {
@@ -77,7 +94,7 @@ func TestDaemonRestartKillContainers(t *testing.T) {
 					defer d.Stop(t)
 					ctx := context.Background()
 
-					resp, err := client.ContainerCreate(ctx, c.config, c.hostConfig, nil, "")
+					resp, err := client.ContainerCreate(ctx, c.config, c.hostConfig, nil, nil, "")
 					assert.NilError(t, err)
 					defer client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
 
@@ -107,9 +124,32 @@ func TestDaemonRestartKillContainers(t *testing.T) {
 
 					}
 					assert.Equal(t, expected, running, "got unexpected running state, expected %v, got: %v", expected, running)
+
+					if c.xHealthCheck {
+						startTime := time.Now()
+						ctxPoll, cancel := context.WithTimeout(ctx, 30*time.Second)
+						defer cancel()
+						poll.WaitOn(t, pollForNewHealthCheck(ctxPoll, client, startTime, resp.ID), poll.WithDelay(100*time.Millisecond))
+					}
 					// TODO(cpuguy83): test pause states... this seems to be rather undefined currently
 				})
 			}
 		}
 	}
 }
+
+func pollForNewHealthCheck(ctx context.Context, client *client.Client, startTime time.Time, containerID string) func(log poll.LogT) poll.Result {
+	return func(log poll.LogT) poll.Result {
+		inspect, err := client.ContainerInspect(ctx, containerID)
+		if err != nil {
+			return poll.Error(err)
+		}
+		healthChecksTotal := len(inspect.State.Health.Log)
+		if healthChecksTotal > 0 {
+			if inspect.State.Health.Log[healthChecksTotal-1].Start.After(startTime) {
+				return poll.Success()
+			}
+		}
+		return poll.Continue("waiting for a new container healthcheck")
+	}
+}
diff --git a/integration/container/run_cgroupns_linux_test.go b/integration/container/run_cgroupns_linux_test.go
new file mode 100644
index 0000000000000..311219003296f
--- /dev/null
+++ b/integration/container/run_cgroupns_linux_test.go
@@ -0,0 +1,145 @@
+package container // import "github.com/docker/docker/integration/container"
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/client"
+	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/integration/internal/requirement"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
+)
+
+// Bring up a daemon with the specified default cgroup namespace mode, and then create a container with the container options
+func testRunWithCgroupNs(t *testing.T, daemonNsMode string, containerOpts ...func(*container.TestContainerConfig)) (string, string) {
+	d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
+	client := d.NewClientT(t)
+	ctx := context.Background()
+
+	d.StartWithBusybox(t)
+	defer d.Stop(t)
+
+	cID := container.Run(ctx, t, client, containerOpts...)
+	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
+
+	daemonCgroup := d.CgroupNamespace(t)
+	containerCgroup := container.GetContainerNS(ctx, t, client, cID, "cgroup")
+	return containerCgroup, daemonCgroup
+}
+
+// Bring up a daemon with the specified default cgroup namespace mode. Create a container with the container options,
+// expecting an error with the specified string
+func testCreateFailureWithCgroupNs(t *testing.T, daemonNsMode string, errStr string, containerOpts ...func(*container.TestContainerConfig)) {
+	d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode(daemonNsMode))
+	client := d.NewClientT(t)
+	ctx := context.Background()
+
+	d.StartWithBusybox(t)
+	defer d.Stop(t)
+	container.CreateExpectingErr(ctx, t, client, errStr, containerOpts...)
+}
+
+func TestCgroupNamespacesRun(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !requirement.CgroupNamespacesEnabled())
+
+	// When the daemon defaults to private cgroup namespaces, containers launched
+	// should be in their own private cgroup namespace by default
+	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private")
+	assert.Assert(t, daemonCgroup != containerCgroup)
+}
+
+func TestCgroupNamespacesRunPrivileged(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !requirement.CgroupNamespacesEnabled())
+	skip.If(t, testEnv.DaemonInfo.CgroupVersion == "2", "on cgroup v2, privileged containers use private cgroupns")
+
+	// When the daemon defaults to private cgroup namespaces, privileged containers
+	// launched should not be inside their own cgroup namespaces
+	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithPrivileged(true))
+	assert.Assert(t, daemonCgroup == containerCgroup)
+}
+
+func TestCgroupNamespacesRunDaemonHostMode(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !requirement.CgroupNamespacesEnabled())
+
+	// When the daemon defaults to host cgroup namespaces, containers
+	// launched should not be inside their own cgroup namespaces
+	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "host")
+	assert.Assert(t, daemonCgroup == containerCgroup)
+}
+
+func TestCgroupNamespacesRunHostMode(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !requirement.CgroupNamespacesEnabled())
+
+	// When the daemon defaults to private cgroup namespaces, containers launched
+	// with a cgroup ns mode of "host" should not be inside their own cgroup namespaces
+	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithCgroupnsMode("host"))
+	assert.Assert(t, daemonCgroup == containerCgroup)
+}
+
+func TestCgroupNamespacesRunPrivateMode(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !requirement.CgroupNamespacesEnabled())
+
+	// When the daemon defaults to private cgroup namespaces, containers launched
+	// with a cgroup ns mode of "private" should be inside their own cgroup namespaces
+	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithCgroupnsMode("private"))
+	assert.Assert(t, daemonCgroup != containerCgroup)
+}
+
+func TestCgroupNamespacesRunPrivilegedAndPrivate(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !requirement.CgroupNamespacesEnabled())
+
+	containerCgroup, daemonCgroup := testRunWithCgroupNs(t, "private", container.WithPrivileged(true), container.WithCgroupnsMode("private"))
+	assert.Assert(t, daemonCgroup != containerCgroup)
+}
+
+func TestCgroupNamespacesRunInvalidMode(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !requirement.CgroupNamespacesEnabled())
+
+	// An invalid cgroup namespace mode should return an error on container creation
+	errStr := "invalid cgroup namespace mode: invalid"
+	testCreateFailureWithCgroupNs(t, "private", errStr, container.WithCgroupnsMode("invalid"))
+}
+
+// Clients before 1.40 expect containers to be created in the host cgroup namespace,
+// regardless of the default setting of the daemon, unless running with cgroup v2
+func TestCgroupNamespacesRunOlderClient(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, !requirement.CgroupNamespacesEnabled())
+
+	d := daemon.New(t, daemon.WithDefaultCgroupNamespaceMode("private"))
+	client := d.NewClientT(t, client.WithVersion("1.39"))
+
+	ctx := context.Background()
+	d.StartWithBusybox(t)
+	defer d.Stop(t)
+
+	cID := container.Run(ctx, t, client)
+	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
+
+	daemonCgroup := d.CgroupNamespace(t)
+	containerCgroup := container.GetContainerNS(ctx, t, client, cID, "cgroup")
+	if testEnv.DaemonInfo.CgroupVersion != "2" {
+		assert.Assert(t, daemonCgroup == containerCgroup)
+	} else {
+		assert.Assert(t, daemonCgroup != containerCgroup)
+	}
+}
diff --git a/integration/container/run_linux_test.go b/integration/container/run_linux_test.go
new file mode 100644
index 0000000000000..a8e47e9f9e56c
--- /dev/null
+++ b/integration/container/run_linux_test.go
@@ -0,0 +1,130 @@
+package container // import "github.com/docker/docker/integration/container"
+
+import (
+	"context"
+	"strings"
+	"testing"
+	"time"
+
+	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/integration/internal/container"
+	net "github.com/docker/docker/integration/internal/network"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
+)
+
+func TestNISDomainname(t *testing.T) {
+	// Older versions of the daemon would concatenate hostname and domainname,
+	// so hostname "foobar" and domainname "baz.cyphar.com" would produce
+	// `foobar.baz.cyphar.com` as hostname.
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "skip test from new feature")
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+
+	// Rootless supports custom Hostname but doesn't support custom Domainname
+	//  OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \
+	//  "write sysctl key kernel.domainname: open /proc/sys/kernel/domainname: permission denied\"": unknown.
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support setting Domainname (TODO: https://github.com/moby/moby/issues/40632)")
+
+	defer setupTest(t)()
+	client := testEnv.APIClient()
+	ctx := context.Background()
+
+	const (
+		hostname   = "foobar"
+		domainname = "baz.cyphar.com"
+	)
+
+	cID := container.Run(ctx, t, client, func(c *container.TestContainerConfig) {
+		c.Config.Hostname = hostname
+		c.Config.Domainname = domainname
+	})
+
+	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
+
+	inspect, err := client.ContainerInspect(ctx, cID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(hostname, inspect.Config.Hostname))
+	assert.Check(t, is.Equal(domainname, inspect.Config.Domainname))
+
+	// Check hostname.
+	res, err := container.Exec(ctx, client, cID,
+		[]string{"cat", "/proc/sys/kernel/hostname"})
+	assert.NilError(t, err)
+	assert.Assert(t, is.Len(res.Stderr(), 0))
+	assert.Equal(t, 0, res.ExitCode)
+	assert.Check(t, is.Equal(hostname, strings.TrimSpace(res.Stdout())))
+
+	// Check domainname.
+	res, err = container.Exec(ctx, client, cID,
+		[]string{"cat", "/proc/sys/kernel/domainname"})
+	assert.NilError(t, err)
+	assert.Assert(t, is.Len(res.Stderr(), 0))
+	assert.Equal(t, 0, res.ExitCode)
+	assert.Check(t, is.Equal(domainname, strings.TrimSpace(res.Stdout())))
+}
+
+func TestHostnameDnsResolution(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+
+	defer setupTest(t)()
+	client := testEnv.APIClient()
+	ctx := context.Background()
+
+	const (
+		hostname = "foobar"
+	)
+
+	// using user defined network as we want to use internal DNS
+	netName := "foobar-net"
+	net.CreateNoError(context.Background(), t, client, netName, net.WithDriver("bridge"))
+
+	cID := container.Run(ctx, t, client, func(c *container.TestContainerConfig) {
+		c.Config.Hostname = hostname
+		c.HostConfig.NetworkMode = containertypes.NetworkMode(netName)
+	})
+
+	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
+
+	inspect, err := client.ContainerInspect(ctx, cID)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(hostname, inspect.Config.Hostname))
+
+	// Clear hosts file so ping will use DNS for hostname resolution
+	res, err := container.Exec(ctx, client, cID,
+		[]string{"sh", "-c", "echo 127.0.0.1 localhost | tee /etc/hosts && ping -c 1 foobar"})
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal("", res.Stderr()))
+	assert.Equal(t, 0, res.ExitCode)
+}
+
+func TestUnprivilegedPortsAndPing(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support setting net.ipv4.ping_group_range and net.ipv4.ip_unprivileged_port_start")
+
+	defer setupTest(t)()
+	client := testEnv.APIClient()
+	ctx := context.Background()
+
+	cID := container.Run(ctx, t, client, func(c *container.TestContainerConfig) {
+		c.Config.User = "1000:1000"
+	})
+
+	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
+
+	// Check net.ipv4.ping_group_range.
+	res, err := container.Exec(ctx, client, cID, []string{"cat", "/proc/sys/net/ipv4/ping_group_range"})
+	assert.NilError(t, err)
+	assert.Assert(t, is.Len(res.Stderr(), 0))
+	assert.Equal(t, 0, res.ExitCode)
+	assert.Equal(t, `0	2147483647`, strings.TrimSpace(res.Stdout()))
+
+	// Check net.ipv4.ip_unprivileged_port_start.
+	res, err = container.Exec(ctx, client, cID, []string{"cat", "/proc/sys/net/ipv4/ip_unprivileged_port_start"})
+	assert.NilError(t, err)
+	assert.Assert(t, is.Len(res.Stderr(), 0))
+	assert.Equal(t, 0, res.ExitCode)
+	assert.Equal(t, "0", strings.TrimSpace(res.Stdout()))
+}
diff --git a/integration/container/stats_test.go b/integration/container/stats_test.go
index 6493a30573e21..b6cd0f5ca7e10 100644
--- a/integration/container/stats_test.go
+++ b/integration/container/stats_test.go
@@ -4,29 +4,30 @@ import (
 	"context"
 	"encoding/json"
 	"io"
+	"reflect"
 	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestStats(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
 	skip.If(t, !testEnv.DaemonInfo.MemoryLimit)
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	info, err := client.Info(ctx)
 	assert.NilError(t, err)
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, client)
 
 	poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond))
 
@@ -34,10 +35,23 @@ func TestStats(t *testing.T) {
 	assert.NilError(t, err)
 	defer resp.Body.Close()
 
-	var v *types.Stats
+	var v types.Stats
 	err = json.NewDecoder(resp.Body).Decode(&v)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(int64(v.MemoryStats.Limit), info.MemTotal))
+	assert.Check(t, !reflect.DeepEqual(v.PreCPUStats, types.CPUStats{}))
+	err = json.NewDecoder(resp.Body).Decode(&v)
+	assert.Assert(t, is.ErrorContains(err, ""), io.EOF)
+
+	resp, err = client.ContainerStatsOneShot(ctx, cID)
+	assert.NilError(t, err)
+	defer resp.Body.Close()
+
+	v = types.Stats{}
+	err = json.NewDecoder(resp.Body).Decode(&v)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(int64(v.MemoryStats.Limit), info.MemTotal))
+	assert.Check(t, is.DeepEqual(v.PreCPUStats, types.CPUStats{}))
 	err = json.NewDecoder(resp.Body).Decode(&v)
 	assert.Assert(t, is.ErrorContains(err, ""), io.EOF)
 }
diff --git a/integration/container/stop_linux_test.go b/integration/container/stop_linux_test.go
index 92db0de41f989..9ce11f37d12d3 100644
--- a/integration/container/stop_linux_test.go
+++ b/integration/container/stop_linux_test.go
@@ -10,11 +10,10 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	"gotest.tools/icmd"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 // TestStopContainerWithTimeout checks that ContainerStop with
@@ -22,7 +21,7 @@ import (
 // waiting is not limited (issue #35311).
 func TestStopContainerWithTimeout(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	testCmd := container.WithCmd("sh", "-c", "sleep 2 && exit 42")
@@ -55,7 +54,7 @@ func TestStopContainerWithTimeout(t *testing.T) {
 		d := d
 		t.Run(strconv.Itoa(d.timeout), func(t *testing.T) {
 			t.Parallel()
-			id := container.Run(t, ctx, client, testCmd)
+			id := container.Run(ctx, t, client, testCmd)
 
 			timeout := time.Duration(d.timeout) * time.Second
 			err := client.ContainerStop(ctx, id, &timeout)
@@ -73,13 +72,13 @@ func TestStopContainerWithTimeout(t *testing.T) {
 
 func TestDeleteDevicemapper(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.Driver != "devicemapper")
-	skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
+	skip.If(t, testEnv.IsRemoteDaemon)
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	id := container.Run(t, ctx, client, container.WithName("foo-"+t.Name()), container.WithCmd("echo"))
+	id := container.Run(ctx, t, client, container.WithName("foo-"+t.Name()), container.WithCmd("echo"))
 
 	poll.WaitOn(t, container.IsStopped(ctx, client, id), poll.WithDelay(100*time.Millisecond))
 
diff --git a/integration/container/stop_test.go b/integration/container/stop_test.go
index 03e76b054d934..7e12996e333f3 100644
--- a/integration/container/stop_test.go
+++ b/integration/container/stop_test.go
@@ -6,21 +6,22 @@ import (
 	"time"
 
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	"gotest.tools/poll"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
 )
 
 func TestStopContainerWithRestartPolicyAlways(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	names := []string{"verifyRestart1-" + t.Name(), "verifyRestart2-" + t.Name()}
 	for _, name := range names {
-		container.Run(t, ctx, client, container.WithName(name), container.WithCmd("false"), func(c *container.TestContainerConfig) {
-			c.HostConfig.RestartPolicy.Name = "always"
-		})
+		container.Run(ctx, t, client,
+			container.WithName(name),
+			container.WithCmd("false"),
+			container.WithRestartPolicy("always"),
+		)
 	}
 
 	for _, name := range names {
diff --git a/integration/container/stop_windows_test.go b/integration/container/stop_windows_test.go
index 0992f62bc9308..a20ba5e6d92e9 100644
--- a/integration/container/stop_windows_test.go
+++ b/integration/container/stop_windows_test.go
@@ -7,10 +7,9 @@ import (
 	"time"
 
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 // TestStopContainerWithTimeout checks that ContainerStop with
@@ -19,7 +18,7 @@ import (
 func TestStopContainerWithTimeout(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	testCmd := container.WithCmd("sh", "-c", "sleep 2 && exit 42")
@@ -52,7 +51,7 @@ func TestStopContainerWithTimeout(t *testing.T) {
 		d := d
 		t.Run(strconv.Itoa(d.timeout), func(t *testing.T) {
 			t.Parallel()
-			id := container.Run(t, ctx, client, testCmd)
+			id := container.Run(ctx, t, client, testCmd)
 
 			timeout := time.Duration(d.timeout) * time.Second
 			err := client.ContainerStop(ctx, id, &timeout)
diff --git a/integration/container/update_linux_test.go b/integration/container/update_linux_test.go
index 7ebe64e7e3e2d..4d43124900bc4 100644
--- a/integration/container/update_linux_test.go
+++ b/integration/container/update_linux_test.go
@@ -8,24 +8,26 @@ import (
 	"time"
 
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestUpdateMemory(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
 	skip.If(t, !testEnv.DaemonInfo.MemoryLimit)
 	skip.If(t, !testEnv.DaemonInfo.SwapLimit)
 
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client, func(c *container.TestContainerConfig) {
+	cID := container.Run(ctx, t, client, func(c *container.TestContainerConfig) {
 		c.HostConfig.Resources = containertypes.Resources{
 			Memory: 200 * 1024 * 1024,
 		}
@@ -51,29 +53,43 @@ func TestUpdateMemory(t *testing.T) {
 	assert.Check(t, is.Equal(setMemory, inspect.HostConfig.Memory))
 	assert.Check(t, is.Equal(setMemorySwap, inspect.HostConfig.MemorySwap))
 
+	memoryFile := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
+	if testEnv.DaemonInfo.CgroupVersion == "2" {
+		memoryFile = "/sys/fs/cgroup/memory.max"
+	}
 	res, err := container.Exec(ctx, client, cID,
-		[]string{"cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"})
+		[]string{"cat", memoryFile})
 	assert.NilError(t, err)
 	assert.Assert(t, is.Len(res.Stderr(), 0))
 	assert.Equal(t, 0, res.ExitCode)
 	assert.Check(t, is.Equal(strconv.FormatInt(setMemory, 10), strings.TrimSpace(res.Stdout())))
 
-	res, err = container.Exec(ctx, client, cID,
-		[]string{"cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"})
-	assert.NilError(t, err)
-	assert.Assert(t, is.Len(res.Stderr(), 0))
-	assert.Equal(t, 0, res.ExitCode)
-	assert.Check(t, is.Equal(strconv.FormatInt(setMemorySwap, 10), strings.TrimSpace(res.Stdout())))
+	// see ConvertMemorySwapToCgroupV2Value() for the convention:
+	// https://github.com/opencontainers/runc/commit/c86be8a2c118ca7bad7bbe9eaf106c659a83940d
+	if testEnv.DaemonInfo.CgroupVersion == "2" {
+		res, err = container.Exec(ctx, client, cID,
+			[]string{"cat", "/sys/fs/cgroup/memory.swap.max"})
+		assert.NilError(t, err)
+		assert.Assert(t, is.Len(res.Stderr(), 0))
+		assert.Equal(t, 0, res.ExitCode)
+		assert.Check(t, is.Equal(strconv.FormatInt(setMemorySwap-setMemory, 10), strings.TrimSpace(res.Stdout())))
+	} else {
+		res, err = container.Exec(ctx, client, cID,
+			[]string{"cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"})
+		assert.NilError(t, err)
+		assert.Assert(t, is.Len(res.Stderr(), 0))
+		assert.Equal(t, 0, res.ExitCode)
+		assert.Check(t, is.Equal(strconv.FormatInt(setMemorySwap, 10), strings.TrimSpace(res.Stdout())))
+	}
 }
 
 func TestUpdateCPUQuota(t *testing.T) {
-	t.Parallel()
-
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, client)
 
 	for _, test := range []struct {
 		desc   string
@@ -84,24 +100,120 @@ func TestUpdateCPUQuota(t *testing.T) {
 		{desc: "a lower value", update: 10000},
 		{desc: "unset value", update: -1},
 	} {
-		if _, err := client.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{
-			Resources: containertypes.Resources{
-				CPUQuota: test.update,
-			},
-		}); err != nil {
-			t.Fatal(err)
+		if testEnv.DaemonInfo.CgroupVersion == "2" {
+			// On v2, specifying CPUQuota without CPUPeriod is currently broken:
+			// https://github.com/opencontainers/runc/issues/2456
+			// As a workaround we set them together.
+			_, err := client.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{
+				Resources: containertypes.Resources{
+					CPUQuota:  test.update,
+					CPUPeriod: 100000,
+				},
+			})
+			assert.NilError(t, err)
+		} else {
+			_, err := client.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{
+				Resources: containertypes.Resources{
+					CPUQuota: test.update,
+				},
+			})
+			assert.NilError(t, err)
 		}
 
 		inspect, err := client.ContainerInspect(ctx, cID)
 		assert.NilError(t, err)
 		assert.Check(t, is.Equal(test.update, inspect.HostConfig.CPUQuota))
 
-		res, err := container.Exec(ctx, client, cID,
-			[]string{"/bin/cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"})
-		assert.NilError(t, err)
-		assert.Assert(t, is.Len(res.Stderr(), 0))
-		assert.Equal(t, 0, res.ExitCode)
+		if testEnv.DaemonInfo.CgroupVersion == "2" {
+			res, err := container.Exec(ctx, client, cID,
+				[]string{"/bin/cat", "/sys/fs/cgroup/cpu.max"})
+			assert.NilError(t, err)
+			assert.Assert(t, is.Len(res.Stderr(), 0))
+			assert.Equal(t, 0, res.ExitCode)
+
+			quotaPeriodPair := strings.Fields(res.Stdout())
+			quota := quotaPeriodPair[0]
+			if test.update == -1 {
+				assert.Check(t, is.Equal("max", quota))
+			} else {
+				assert.Check(t, is.Equal(strconv.FormatInt(test.update, 10), quota))
+			}
+		} else {
+			res, err := container.Exec(ctx, client, cID,
+				[]string{"/bin/cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"})
+			assert.NilError(t, err)
+			assert.Assert(t, is.Len(res.Stderr(), 0))
+			assert.Equal(t, 0, res.ExitCode)
+
+			assert.Check(t, is.Equal(strconv.FormatInt(test.update, 10), strings.TrimSpace(res.Stdout())))
+		}
+	}
+}
+
+func TestUpdatePidsLimit(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, testEnv.DaemonInfo.CgroupDriver == "none")
+	skip.If(t, !testEnv.DaemonInfo.PidsLimit)
+
+	defer setupTest(t)()
+	apiClient := testEnv.APIClient()
+	oldAPIclient := request.NewAPIClient(t, client.WithVersion("1.24"))
+	ctx := context.Background()
+
+	intPtr := func(i int64) *int64 {
+		return &i
+	}
+
+	for _, test := range []struct {
+		desc     string
+		oldAPI   bool
+		initial  *int64
+		update   *int64
+		expect   int64
+		expectCg string
+	}{
+		{desc: "update from none", update: intPtr(32), expect: 32, expectCg: "32"},
+		{desc: "no change", initial: intPtr(32), expect: 32, expectCg: "32"},
+		{desc: "update lower", initial: intPtr(32), update: intPtr(16), expect: 16, expectCg: "16"},
+		{desc: "update on old api ignores value", oldAPI: true, initial: intPtr(32), update: intPtr(16), expect: 32, expectCg: "32"},
+		{desc: "unset limit with zero", initial: intPtr(32), update: intPtr(0), expect: 0, expectCg: "max"},
+		{desc: "unset limit with minus one", initial: intPtr(32), update: intPtr(-1), expect: 0, expectCg: "max"},
+		{desc: "unset limit with minus two", initial: intPtr(32), update: intPtr(-2), expect: 0, expectCg: "max"},
+	} {
+		c := apiClient
+		if test.oldAPI {
+			c = oldAPIclient
+		}
 
-		assert.Check(t, is.Equal(strconv.FormatInt(test.update, 10), strings.TrimSpace(res.Stdout())))
+		t.Run(test.desc, func(t *testing.T) {
+			// Using "network=host" to speed up creation (13.96s vs 6.54s)
+			cID := container.Run(ctx, t, apiClient, container.WithPidsLimit(test.initial), container.WithNetworkMode("host"))
+
+			_, err := c.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{
+				Resources: containertypes.Resources{
+					PidsLimit: test.update,
+				},
+			})
+			assert.NilError(t, err)
+
+			inspect, err := c.ContainerInspect(ctx, cID)
+			assert.NilError(t, err)
+			assert.Assert(t, inspect.HostConfig.Resources.PidsLimit != nil)
+			assert.Equal(t, *inspect.HostConfig.Resources.PidsLimit, test.expect)
+
+			ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
+			defer cancel()
+
+			pidsFile := "/sys/fs/cgroup/pids/pids.max"
+			if testEnv.DaemonInfo.CgroupVersion == "2" {
+				pidsFile = "/sys/fs/cgroup/pids.max"
+			}
+			res, err := container.Exec(ctx, c, cID, []string{"cat", pidsFile})
+			assert.NilError(t, err)
+			assert.Assert(t, is.Len(res.Stderr(), 0))
+
+			out := strings.TrimSpace(res.Stdout())
+			assert.Equal(t, out, test.expectCg)
+		})
 	}
 }
diff --git a/integration/container/update_test.go b/integration/container/update_test.go
index 0e32184d27f10..086014540a983 100644
--- a/integration/container/update_test.go
+++ b/integration/container/update_test.go
@@ -7,18 +7,17 @@ import (
 
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
 )
 
 func TestUpdateRestartPolicy(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client, container.WithCmd("sh", "-c", "sleep 1 && false"), func(c *container.TestContainerConfig) {
+	cID := container.Run(ctx, t, client, container.WithCmd("sh", "-c", "sleep 1 && false"), func(c *container.TestContainerConfig) {
 		c.HostConfig.RestartPolicy = containertypes.RestartPolicy{
 			Name:              "on-failure",
 			MaximumRetryCount: 3,
@@ -48,12 +47,10 @@ func TestUpdateRestartPolicy(t *testing.T) {
 
 func TestUpdateRestartWithAutoRemove(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID := container.Run(t, ctx, client, func(c *container.TestContainerConfig) {
-		c.HostConfig.AutoRemove = true
-	})
+	cID := container.Run(ctx, t, client, container.WithAutoRemove)
 
 	_, err := client.ContainerUpdate(ctx, cID, containertypes.UpdateConfig{
 		RestartPolicy: containertypes.RestartPolicy{
diff --git a/integration/container/wait_test.go b/integration/container/wait_test.go
index 09edcabf1e74d..44e2c8114391e 100644
--- a/integration/container/wait_test.go
+++ b/integration/container/wait_test.go
@@ -6,11 +6,11 @@ import (
 	"time"
 
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestWaitNonBlocked(t *testing.T) {
@@ -39,15 +39,15 @@ func TestWaitNonBlocked(t *testing.T) {
 		t.Run(tc.doc, func(t *testing.T) {
 			t.Parallel()
 			ctx := context.Background()
-			containerID := container.Run(t, ctx, cli, container.WithCmd("sh", "-c", tc.cmd))
+			containerID := container.Run(ctx, t, cli, container.WithCmd("sh", "-c", tc.cmd))
 			poll.WaitOn(t, container.IsInState(ctx, cli, containerID, "exited"), poll.WithTimeout(30*time.Second), poll.WithDelay(100*time.Millisecond))
 
-			waitresC, errC := cli.ContainerWait(ctx, containerID, "")
+			waitResC, errC := cli.ContainerWait(ctx, containerID, "")
 			select {
 			case err := <-errC:
 				assert.NilError(t, err)
-			case waitres := <-waitresC:
-				assert.Check(t, is.Equal(tc.expectedCode, waitres.StatusCode))
+			case waitRes := <-waitResC:
+				assert.Check(t, is.Equal(tc.expectedCode, waitRes.StatusCode))
 			}
 		})
 	}
@@ -81,10 +81,10 @@ func TestWaitBlocked(t *testing.T) {
 		t.Run(tc.doc, func(t *testing.T) {
 			t.Parallel()
 			ctx := context.Background()
-			containerID := container.Run(t, ctx, cli, container.WithCmd("sh", "-c", tc.cmd))
+			containerID := container.Run(ctx, t, cli, container.WithCmd("sh", "-c", tc.cmd))
 			poll.WaitOn(t, container.IsInState(ctx, cli, containerID, "running"), poll.WithTimeout(30*time.Second), poll.WithDelay(100*time.Millisecond))
 
-			waitresC, errC := cli.ContainerWait(ctx, containerID, "")
+			waitResC, errC := cli.ContainerWait(ctx, containerID, "")
 
 			err := cli.ContainerStop(ctx, containerID, nil)
 			assert.NilError(t, err)
@@ -92,8 +92,8 @@ func TestWaitBlocked(t *testing.T) {
 			select {
 			case err := <-errC:
 				assert.NilError(t, err)
-			case waitres := <-waitresC:
-				assert.Check(t, is.Equal(tc.expectedCode, waitres.StatusCode))
+			case waitRes := <-waitResC:
+				assert.Check(t, is.Equal(tc.expectedCode, waitRes.StatusCode))
 			case <-time.After(2 * time.Second):
 				t.Fatal("timeout waiting for `docker wait`")
 			}
diff --git a/integration/daemon/daemon_test.go b/integration/daemon/daemon_test.go
new file mode 100644
index 0000000000000..0c4f80f959ba2
--- /dev/null
+++ b/integration/daemon/daemon_test.go
@@ -0,0 +1,339 @@
+package daemon // import "github.com/docker/docker/integration/daemon"
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"syscall"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/daemon/config"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/env"
+	"gotest.tools/v3/skip"
+)
+
+func TestConfigDaemonLibtrustID(t *testing.T) {
+	skip.If(t, runtime.GOOS == "windows")
+
+	d := daemon.New(t)
+	defer d.Stop(t)
+
+	trustKey := filepath.Join(d.RootDir(), "key.json")
+	err := os.WriteFile(trustKey, []byte(`{"crv":"P-256","d":"dm28PH4Z4EbyUN8L0bPonAciAQa1QJmmyYd876mnypY","kid":"WTJ3:YSIP:CE2E:G6KJ:PSBD:YX2Y:WEYD:M64G:NU2V:XPZV:H2CR:VLUB","kty":"EC","x":"Mh5-JINSjaa_EZdXDttri255Z5fbCEOTQIZjAcScFTk","y":"eUyuAjfxevb07hCCpvi4Zi334Dy4GDWQvEToGEX4exQ"}`), 0644)
+	assert.NilError(t, err)
+
+	config := filepath.Join(d.RootDir(), "daemon.json")
+	err = os.WriteFile(config, []byte(`{"deprecated-key-path": "`+trustKey+`"}`), 0644)
+	assert.NilError(t, err)
+
+	d.Start(t, "--config-file", config)
+	info := d.Info(t)
+	assert.Equal(t, info.ID, "WTJ3:YSIP:CE2E:G6KJ:PSBD:YX2Y:WEYD:M64G:NU2V:XPZV:H2CR:VLUB")
+}
+
+func TestDaemonConfigValidation(t *testing.T) {
+	skip.If(t, runtime.GOOS == "windows")
+
+	d := daemon.New(t)
+	dockerBinary, err := d.BinaryPath()
+	assert.NilError(t, err)
+	params := []string{"--validate", "--config-file"}
+
+	dest := os.Getenv("DOCKER_INTEGRATION_DAEMON_DEST")
+	if dest == "" {
+		dest = os.Getenv("DEST")
+	}
+	testdata := filepath.Join(dest, "..", "..", "integration", "daemon", "testdata")
+
+	const (
+		validOut  = "configuration OK"
+		failedOut = "unable to configure the Docker daemon with file"
+	)
+
+	tests := []struct {
+		name        string
+		args        []string
+		expectedOut string
+	}{
+		{
+			name:        "config with no content",
+			args:        append(params, filepath.Join(testdata, "empty-config-1.json")),
+			expectedOut: validOut,
+		},
+		{
+			name:        "config with {}",
+			args:        append(params, filepath.Join(testdata, "empty-config-2.json")),
+			expectedOut: validOut,
+		},
+		{
+			name:        "invalid config",
+			args:        append(params, filepath.Join(testdata, "invalid-config-1.json")),
+			expectedOut: failedOut,
+		},
+		{
+			name:        "malformed config",
+			args:        append(params, filepath.Join(testdata, "malformed-config.json")),
+			expectedOut: failedOut,
+		},
+		{
+			name:        "valid config",
+			args:        append(params, filepath.Join(testdata, "valid-config-1.json")),
+			expectedOut: validOut,
+		},
+	}
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
+			cmd := exec.Command(dockerBinary, tc.args...)
+			out, err := cmd.CombinedOutput()
+			assert.Check(t, is.Contains(string(out), tc.expectedOut))
+			if tc.expectedOut == failedOut {
+				assert.ErrorContains(t, err, "", "expected an error, but got none")
+			} else {
+				assert.NilError(t, err)
+			}
+		})
+	}
+}
+
+func TestConfigDaemonSeccompProfiles(t *testing.T) {
+	skip.If(t, runtime.GOOS == "windows")
+
+	d := daemon.New(t)
+	defer d.Stop(t)
+
+	tests := []struct {
+		doc             string
+		profile         string
+		expectedProfile string
+	}{
+		{
+			doc:             "empty profile set",
+			profile:         "",
+			expectedProfile: config.SeccompProfileDefault,
+		},
+		{
+			doc:             "default profile",
+			profile:         config.SeccompProfileDefault,
+			expectedProfile: config.SeccompProfileDefault,
+		},
+		{
+			doc:             "unconfined profile",
+			profile:         config.SeccompProfileUnconfined,
+			expectedProfile: config.SeccompProfileUnconfined,
+		},
+	}
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.doc, func(t *testing.T) {
+			d.Start(t, "--seccomp-profile="+tc.profile)
+			info := d.Info(t)
+			assert.Assert(t, is.Contains(info.SecurityOptions, "name=seccomp,profile="+tc.expectedProfile))
+			d.Stop(t)
+
+			cfg := filepath.Join(d.RootDir(), "daemon.json")
+			err := os.WriteFile(cfg, []byte(`{"seccomp-profile": "`+tc.profile+`"}`), 0644)
+			assert.NilError(t, err)
+
+			d.Start(t, "--config-file", cfg)
+			info = d.Info(t)
+			assert.Assert(t, is.Contains(info.SecurityOptions, "name=seccomp,profile="+tc.expectedProfile))
+			d.Stop(t)
+		})
+	}
+}
+
+func TestDaemonProxy(t *testing.T) {
+	skip.If(t, runtime.GOOS == "windows", "cannot start multiple daemons on windows")
+	skip.If(t, os.Getenv("DOCKER_ROOTLESS") != "", "cannot connect to localhost proxy in rootless environment")
+
+	var received string
+	proxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		received = r.Host
+		w.Header().Set("Content-Type", "application/json")
+		_, _ = w.Write([]byte("OK"))
+	}))
+	defer proxyServer.Close()
+
+	const userPass = "myuser:mypassword@"
+
+	// Configure proxy through env-vars
+	t.Run("environment variables", func(t *testing.T) {
+		defer env.Patch(t, "HTTP_PROXY", proxyServer.URL)()
+		defer env.Patch(t, "HTTPS_PROXY", proxyServer.URL)()
+		defer env.Patch(t, "NO_PROXY", "example.com")()
+
+		d := daemon.New(t)
+		c := d.NewClientT(t)
+		defer func() { _ = c.Close() }()
+		ctx := context.Background()
+		d.Start(t)
+
+		_, err := c.ImagePull(ctx, "example.org:5000/some/image:latest", types.ImagePullOptions{})
+		assert.ErrorContains(t, err, "", "pulling should have failed")
+		assert.Equal(t, received, "example.org:5000")
+
+		// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
+		_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
+		assert.ErrorContains(t, err, "", "pulling should have failed")
+		assert.Equal(t, received, "example.org:5000", "should not have used proxy")
+
+		info := d.Info(t)
+		assert.Equal(t, info.HTTPProxy, proxyServer.URL)
+		assert.Equal(t, info.HTTPSProxy, proxyServer.URL)
+		assert.Equal(t, info.NoProxy, "example.com")
+		d.Stop(t)
+	})
+
+	// Configure proxy through command-line flags
+	t.Run("command-line options", func(t *testing.T) {
+		defer env.Patch(t, "HTTP_PROXY", "http://"+userPass+"from-env-http.invalid")()
+		defer env.Patch(t, "http_proxy", "http://"+userPass+"from-env-http.invalid")()
+		defer env.Patch(t, "HTTPS_PROXY", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")()
+		defer env.Patch(t, "https_proxy", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")()
+		defer env.Patch(t, "NO_PROXY", "ignore.invalid")()
+		defer env.Patch(t, "no_proxy", "ignore.invalid")()
+
+		d := daemon.New(t)
+		d.Start(t, "--http-proxy", proxyServer.URL, "--https-proxy", proxyServer.URL, "--no-proxy", "example.com")
+
+		logs, err := d.ReadLogFile()
+		assert.NilError(t, err)
+		assert.Assert(t, is.Contains(string(logs), "overriding existing proxy variable with value from configuration"))
+		for _, v := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} {
+			assert.Assert(t, is.Contains(string(logs), "name="+v))
+			assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
+		}
+
+		c := d.NewClientT(t)
+		defer func() { _ = c.Close() }()
+		ctx := context.Background()
+
+		_, err = c.ImagePull(ctx, "example.org:5001/some/image:latest", types.ImagePullOptions{})
+		assert.ErrorContains(t, err, "", "pulling should have failed")
+		assert.Equal(t, received, "example.org:5001")
+
+		// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
+		_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
+		assert.ErrorContains(t, err, "", "pulling should have failed")
+		assert.Equal(t, received, "example.org:5001", "should not have used proxy")
+
+		info := d.Info(t)
+		assert.Equal(t, info.HTTPProxy, proxyServer.URL)
+		assert.Equal(t, info.HTTPSProxy, proxyServer.URL)
+		assert.Equal(t, info.NoProxy, "example.com")
+
+		d.Stop(t)
+	})
+
+	// Configure proxy through configuration file
+	t.Run("configuration file", func(t *testing.T) {
+		defer env.Patch(t, "HTTP_PROXY", "http://"+userPass+"from-env-http.invalid")()
+		defer env.Patch(t, "http_proxy", "http://"+userPass+"from-env-http.invalid")()
+		defer env.Patch(t, "HTTPS_PROXY", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")()
+		defer env.Patch(t, "https_proxy", "https://"+userPass+"myuser:mypassword@from-env-https.invalid")()
+		defer env.Patch(t, "NO_PROXY", "ignore.invalid")()
+		defer env.Patch(t, "no_proxy", "ignore.invalid")()
+
+		d := daemon.New(t)
+		c := d.NewClientT(t)
+		defer func() { _ = c.Close() }()
+		ctx := context.Background()
+
+		configFile := filepath.Join(d.RootDir(), "daemon.json")
+		configJSON := fmt.Sprintf(`{"http-proxy":%[1]q, "https-proxy": %[1]q, "no-proxy": "example.com"}`, proxyServer.URL)
+		assert.NilError(t, os.WriteFile(configFile, []byte(configJSON), 0644))
+
+		d.Start(t, "--config-file", configFile)
+
+		logs, err := d.ReadLogFile()
+		assert.NilError(t, err)
+		assert.Assert(t, is.Contains(string(logs), "overriding existing proxy variable with value from configuration"))
+		for _, v := range []string{"http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY", "no_proxy", "NO_PROXY"} {
+			assert.Assert(t, is.Contains(string(logs), "name="+v))
+			assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
+		}
+
+		_, err = c.ImagePull(ctx, "example.org:5002/some/image:latest", types.ImagePullOptions{})
+		assert.ErrorContains(t, err, "", "pulling should have failed")
+		assert.Equal(t, received, "example.org:5002")
+
+		// Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
+		_, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
+		assert.ErrorContains(t, err, "", "pulling should have failed")
+		assert.Equal(t, received, "example.org:5002", "should not have used proxy")
+
+		info := d.Info(t)
+		assert.Equal(t, info.HTTPProxy, proxyServer.URL)
+		assert.Equal(t, info.HTTPSProxy, proxyServer.URL)
+		assert.Equal(t, info.NoProxy, "example.com")
+
+		d.Stop(t)
+	})
+
+	// Conflicting options (passed both through command-line options and config file)
+	t.Run("conflicting options", func(t *testing.T) {
+		const (
+			proxyRawURL = "https://" + userPass + "example.org"
+			proxyURL    = "https://xxxxx:xxxxx@example.org"
+		)
+
+		d := daemon.New(t)
+
+		configFile := filepath.Join(d.RootDir(), "daemon.json")
+		configJSON := fmt.Sprintf(`{"http-proxy":%[1]q, "https-proxy": %[1]q, "no-proxy": "example.com"}`, proxyRawURL)
+		assert.NilError(t, os.WriteFile(configFile, []byte(configJSON), 0644))
+
+		err := d.StartWithError("--http-proxy", proxyRawURL, "--https-proxy", proxyRawURL, "--no-proxy", "example.com", "--config-file", configFile, "--validate")
+		assert.ErrorContains(t, err, "daemon exited during startup")
+		logs, err := d.ReadLogFile()
+		assert.NilError(t, err)
+		expected := fmt.Sprintf(
+			`the following directives are specified both as a flag and in the configuration file: http-proxy: (from flag: %[1]s, from file: %[1]s), https-proxy: (from flag: %[1]s, from file: %[1]s), no-proxy: (from flag: example.com, from file: example.com)`,
+			proxyURL,
+		)
+		assert.Assert(t, is.Contains(string(logs), expected))
+	})
+
+	// Make sure values are sanitized when reloading the daemon-config
+	t.Run("reload sanitized", func(t *testing.T) {
+		const (
+			proxyRawURL = "https://" + userPass + "example.org"
+			proxyURL    = "https://xxxxx:xxxxx@example.org"
+		)
+
+		d := daemon.New(t)
+		d.Start(t, "--http-proxy", proxyRawURL, "--https-proxy", proxyRawURL, "--no-proxy", "example.com")
+		defer d.Stop(t)
+		err := d.Signal(syscall.SIGHUP)
+		assert.NilError(t, err)
+
+		logs, err := d.ReadLogFile()
+		assert.NilError(t, err)
+
+		// FIXME: there appears to ba a race condition, which causes ReadLogFile
+		//        to not contain the full logs after signaling the daemon to reload,
+		//        causing the test to fail here. As a workaround, check if we
+		//        received the "reloaded" message after signaling, and only then
+		//        check that it's sanitized properly. For more details on this
+		//        issue, see https://github.com/moby/moby/pull/42835/files#r713120315
+		if !strings.Contains(string(logs), "Reloaded configuration:") {
+			t.Skip("Skipping test, because we did not find 'Reloaded configuration' in the logs")
+		}
+
+		assert.Assert(t, is.Contains(string(logs), proxyURL))
+		assert.Assert(t, !strings.Contains(string(logs), userPass), "logs should not contain the non-sanitized proxy URL: %s", string(logs))
+	})
+}
diff --git a/integration/daemon/main_test.go b/integration/daemon/main_test.go
new file mode 100644
index 0000000000000..74a342e298243
--- /dev/null
+++ b/integration/daemon/main_test.go
@@ -0,0 +1,10 @@
+package daemon // import "github.com/docker/docker/integration/daemon"
+
+import (
+	"os"
+	"testing"
+)
+
+func TestMain(m *testing.M) {
+	os.Exit(m.Run())
+}
diff --git a/integration/daemon/testdata/empty-config-1.json b/integration/daemon/testdata/empty-config-1.json
new file mode 100644
index 0000000000000..8b137891791fe
--- /dev/null
+++ b/integration/daemon/testdata/empty-config-1.json
@@ -0,0 +1 @@
+
diff --git a/integration/daemon/testdata/empty-config-2.json b/integration/daemon/testdata/empty-config-2.json
new file mode 100644
index 0000000000000..0967ef424bce6
--- /dev/null
+++ b/integration/daemon/testdata/empty-config-2.json
@@ -0,0 +1 @@
+{}
diff --git a/integration/daemon/testdata/invalid-config-1.json b/integration/daemon/testdata/invalid-config-1.json
new file mode 100644
index 0000000000000..f3c7781e52f34
--- /dev/null
+++ b/integration/daemon/testdata/invalid-config-1.json
@@ -0,0 +1 @@
+{"unknown-option": true}
diff --git a/integration/daemon/testdata/malformed-config.json b/integration/daemon/testdata/malformed-config.json
new file mode 100644
index 0000000000000..98232c64fce93
--- /dev/null
+++ b/integration/daemon/testdata/malformed-config.json
@@ -0,0 +1 @@
+{
diff --git a/integration/daemon/testdata/valid-config-1.json b/integration/daemon/testdata/valid-config-1.json
new file mode 100644
index 0000000000000..b1608d8f8af6f
--- /dev/null
+++ b/integration/daemon/testdata/valid-config-1.json
@@ -0,0 +1 @@
+{"debug": true}
diff --git a/integration/distribution/inspect_test.go.temp b/integration/distribution/inspect_test.go.temp
new file mode 100644
index 0000000000000..5a821f6415648
--- /dev/null
+++ b/integration/distribution/inspect_test.go.temp
@@ -0,0 +1,24 @@
+package distribution
+
+import (
+	"context"
+	"testing"
+
+	"github.com/docker/docker/internal/test/request"
+	"gotest.tools/v3/assert"
+)
+
+// tagging a named image in a new unprefixed repo should work
+func TestUnknownManifest(t *testing.T) {
+	defer setupTest(t)()
+	client := request.NewAPIClient(t)
+	ctx := context.Background()
+
+	// By name
+	insp, err := client.DistributionInspect(ctx, "nosuchimage:latest", "")
+	assert.NilError(t, err)
+	assert.Equal(t, insp.Descriptor, nil)
+
+	//err = client.ImageTag(ctx, insp.ID, "testfoobarbaz")
+	//assert.NilError(t, err)
+}
diff --git a/integration/distribution/main_test.go.temp b/integration/distribution/main_test.go.temp
new file mode 100644
index 0000000000000..bd75788087687
--- /dev/null
+++ b/integration/distribution/main_test.go.temp
@@ -0,0 +1,33 @@
+package distribution // import "github.com/docker/docker/integration/distribution"
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/docker/docker/internal/test/environment"
+)
+
+var testEnv *environment.Execution
+
+func TestMain(m *testing.M) {
+	var err error
+	testEnv, err = environment.New()
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+	err = environment.EnsureFrozenImagesLinux(testEnv)
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+
+	testEnv.Print()
+	os.Exit(m.Run())
+}
+
+func setupTest(t *testing.T) func() {
+	environment.ProtectAll(t, testEnv)
+	return func() { testEnv.Clean(t) }
+}
diff --git a/integration/image/commit_test.go b/integration/image/commit_test.go
index ad3f89ec79fc8..813af90aa2095 100644
--- a/integration/image/commit_test.go
+++ b/integration/image/commit_test.go
@@ -2,29 +2,30 @@ package image // import "github.com/docker/docker/integration/image"
 
 import (
 	"context"
+	"strings"
 	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestCommitInheritsEnv(t *testing.T) {
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.36"), "broken in earlier versions")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	cID1 := container.Create(t, ctx, client)
+	cID1 := container.Create(ctx, t, client)
+	imgName := strings.ToLower(t.Name())
 
 	commitResp1, err := client.ContainerCommit(ctx, cID1, types.ContainerCommitOptions{
 		Changes:   []string{"ENV PATH=/bin"},
-		Reference: "test-commit-image",
+		Reference: imgName,
 	})
 	assert.NilError(t, err)
 
@@ -34,11 +35,11 @@ func TestCommitInheritsEnv(t *testing.T) {
 	expectedEnv1 := []string{"PATH=/bin"}
 	assert.Check(t, is.DeepEqual(expectedEnv1, image1.Config.Env))
 
-	cID2 := container.Create(t, ctx, client, container.WithImage(image1.ID))
+	cID2 := container.Create(ctx, t, client, container.WithImage(image1.ID))
 
 	commitResp2, err := client.ContainerCommit(ctx, cID2, types.ContainerCommitOptions{
 		Changes:   []string{"ENV PATH=/usr/bin:$PATH"},
-		Reference: "test-commit-image",
+		Reference: imgName,
 	})
 	assert.NilError(t, err)
 
diff --git a/integration/image/import_test.go b/integration/image/import_test.go
index 2e2fdf7192982..2880df8d324ac 100644
--- a/integration/image/import_test.go
+++ b/integration/image/import_test.go
@@ -6,20 +6,29 @@ import (
 	"context"
 	"io"
 	"runtime"
+	"strings"
 	"testing"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/docker/docker/internal/testutil"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
 // Ensure we don't regress on CVE-2017-14992.
 func TestImportExtremelyLargeImageWorks(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
 	skip.If(t, runtime.GOARCH == "arm64", "effective test will be time out")
 	skip.If(t, testEnv.OSType == "windows", "TODO enable on windows")
+	t.Parallel()
 
-	client := request.NewAPIClient(t)
+	// Spin up a new daemon, so that we can run this test in parallel (it's a slow test)
+	d := daemon.New(t)
+	d.Start(t)
+	defer d.Stop(t)
+
+	client := d.NewClientT(t)
 
 	// Construct an empty tar archive with about 8GB of junk padding at the
 	// end. This should not cause any crashes (the padding should be mostly
@@ -27,16 +36,14 @@ func TestImportExtremelyLargeImageWorks(t *testing.T) {
 	var tarBuffer bytes.Buffer
 
 	tw := tar.NewWriter(&tarBuffer)
-	if err := tw.Close(); err != nil {
-		t.Fatal(err)
-	}
+	err := tw.Close()
+	assert.NilError(t, err)
 	imageRdr := io.MultiReader(&tarBuffer, io.LimitReader(testutil.DevZero, 8*1024*1024*1024))
+	reference := strings.ToLower(t.Name()) + ":v42"
 
-	_, err := client.ImageImport(context.Background(),
+	_, err = client.ImageImport(context.Background(),
 		types.ImageImportSource{Source: imageRdr, SourceName: "-"},
-		"test1234:v42",
+		reference,
 		types.ImageImportOptions{})
-	if err != nil {
-		t.Fatal(err)
-	}
+	assert.NilError(t, err)
 }
diff --git a/integration/image/list_test.go b/integration/image/list_test.go
new file mode 100644
index 0000000000000..68dd8d6741baa
--- /dev/null
+++ b/integration/image/list_test.go
@@ -0,0 +1,53 @@
+package image // import "github.com/docker/docker/integration/image"
+
+import (
+	"context"
+	"strings"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/api/types/versions"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
+)
+
+// Regression : #38171
+func TestImagesFilterMultiReference(t *testing.T) {
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "broken in earlier versions")
+	defer setupTest(t)()
+	client := testEnv.APIClient()
+	ctx := context.Background()
+
+	name := strings.ToLower(t.Name())
+	repoTags := []string{
+		name + ":v1",
+		name + ":v2",
+		name + ":v3",
+		name + ":v4",
+	}
+
+	for _, repoTag := range repoTags {
+		err := client.ImageTag(ctx, "busybox:latest", repoTag)
+		assert.NilError(t, err)
+	}
+
+	filter := filters.NewArgs()
+	filter.Add("reference", repoTags[0])
+	filter.Add("reference", repoTags[1])
+	filter.Add("reference", repoTags[2])
+	options := types.ImageListOptions{
+		All:     false,
+		Filters: filter,
+	}
+	images, err := client.ImageList(ctx, options)
+	assert.NilError(t, err)
+
+	assert.Check(t, is.Equal(len(images[0].RepoTags), 3))
+	for _, repoTag := range images[0].RepoTags {
+		if repoTag != repoTags[0] && repoTag != repoTags[1] && repoTag != repoTags[2] {
+			t.Errorf("list images doesn't match any repoTag we expected, repoTag: %s", repoTag)
+		}
+	}
+}
diff --git a/integration/image/main_test.go b/integration/image/main_test.go
index 1b4270dfc64b7..893e2fdf5dca9 100644
--- a/integration/image/main_test.go
+++ b/integration/image/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
diff --git a/integration/image/pull_test.go b/integration/image/pull_test.go
new file mode 100644
index 0000000000000..15db3295d8f85
--- /dev/null
+++ b/integration/image/pull_test.go
@@ -0,0 +1,24 @@
+package image
+
+import (
+	"context"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/errdefs"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
+)
+
+func TestImagePullPlatformInvalid(t *testing.T) {
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "experimental in older versions")
+	defer setupTest(t)()
+	client := testEnv.APIClient()
+	ctx := context.Background()
+
+	_, err := client.ImagePull(ctx, "docker.io/library/hello-world:latest", types.ImagePullOptions{Platform: "foobar"})
+	assert.Assert(t, err != nil)
+	assert.ErrorContains(t, err, "unknown operating system or architecture")
+	assert.Assert(t, errdefs.IsInvalidParameter(err))
+}
diff --git a/integration/image/remove_test.go b/integration/image/remove_test.go
index 76d73a7135264..11d8141da38ac 100644
--- a/integration/image/remove_test.go
+++ b/integration/image/remove_test.go
@@ -2,52 +2,50 @@ package image // import "github.com/docker/docker/integration/image"
 
 import (
 	"context"
+	"strings"
 	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 func TestRemoveImageOrphaning(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
-	img := "test-container-orphaning"
+	imgName := strings.ToLower(t.Name())
 
 	// Create a container from busybox, and commit a small change so we have a new image
-	cID1 := container.Create(t, ctx, client, container.WithCmd(""))
+	cID1 := container.Create(ctx, t, client, container.WithCmd(""))
 	commitResp1, err := client.ContainerCommit(ctx, cID1, types.ContainerCommitOptions{
 		Changes:   []string{`ENTRYPOINT ["true"]`},
-		Reference: img,
+		Reference: imgName,
 	})
 	assert.NilError(t, err)
 
 	// verifies that reference now points to first image
-	resp, _, err := client.ImageInspectWithRaw(ctx, img)
+	resp, _, err := client.ImageInspectWithRaw(ctx, imgName)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(resp.ID, commitResp1.ID))
 
 	// Create a container from created image, and commit a small change with same reference name
-	cID2 := container.Create(t, ctx, client, container.WithImage(img), container.WithCmd(""))
+	cID2 := container.Create(ctx, t, client, container.WithImage(imgName), container.WithCmd(""))
 	commitResp2, err := client.ContainerCommit(ctx, cID2, types.ContainerCommitOptions{
 		Changes:   []string{`LABEL Maintainer="Integration Tests"`},
-		Reference: img,
+		Reference: imgName,
 	})
 	assert.NilError(t, err)
 
 	// verifies that reference now points to second image
-	resp, _, err = client.ImageInspectWithRaw(ctx, img)
+	resp, _, err = client.ImageInspectWithRaw(ctx, imgName)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(resp.ID, commitResp2.ID))
 
 	// try to remove the image, should not error out.
-	_, err = client.ImageRemove(ctx, img, types.ImageRemoveOptions{})
+	_, err = client.ImageRemove(ctx, imgName, types.ImageRemoveOptions{})
 	assert.NilError(t, err)
 
 	// check if the first image is still there
diff --git a/integration/image/remove_unix_test.go b/integration/image/remove_unix_test.go
new file mode 100644
index 0000000000000..65fffe2c29443
--- /dev/null
+++ b/integration/image/remove_unix_test.go
@@ -0,0 +1,119 @@
+//go:build !windows
+// +build !windows
+
+package image // import "github.com/docker/docker/integration/image"
+
+import (
+	"context"
+	"io"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strconv"
+	"strings"
+	"syscall"
+	"testing"
+	"unsafe"
+
+	"github.com/docker/docker/api/types"
+	_ "github.com/docker/docker/daemon/graphdriver/register" // register graph drivers
+	"github.com/docker/docker/daemon/images"
+	"github.com/docker/docker/layer"
+	"github.com/docker/docker/pkg/idtools"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/fakecontext"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
+)
+
+// This is a regression test for #38488
+// It ensures that orphan layers can be found and cleaned up
+// after unsuccessful image removal
+func TestRemoveImageGarbageCollector(t *testing.T) {
+	// This test uses very platform specific way to prevent
+	// daemon for remove image layer.
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support overlay2 on most distros")
+
+	// Create daemon with overlay2 graphdriver because vfs uses disk differently
+	// and this test case would not work with it.
+	d := daemon.New(t, daemon.WithStorageDriver("overlay2"))
+	d.Start(t)
+	defer d.Stop(t)
+
+	ctx := context.Background()
+	client := d.NewClientT(t)
+
+	layerStore, _ := layer.NewStoreFromOptions(layer.StoreOptions{
+		Root:                      d.Root,
+		MetadataStorePathTemplate: filepath.Join(d.RootDir(), "image", "%s", "layerdb"),
+		GraphDriver:               d.StorageDriver(),
+		GraphDriverOptions:        nil,
+		IDMapping:                 &idtools.IdentityMapping{},
+		PluginGetter:              nil,
+		ExperimentalEnabled:       false,
+		OS:                        runtime.GOOS,
+	})
+	i := images.NewImageService(images.ImageServiceConfig{
+		LayerStore: layerStore,
+	})
+
+	img := "test-garbage-collector"
+
+	// Build a image with multiple layers
+	dockerfile := `FROM busybox
+	RUN echo echo Running... > /run.sh`
+	source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
+	defer source.Close()
+	resp, err := client.ImageBuild(ctx,
+		source.AsTarReader(t),
+		types.ImageBuildOptions{
+			Remove:      true,
+			ForceRemove: true,
+			Tags:        []string{img},
+		})
+	assert.NilError(t, err)
+	_, err = io.Copy(io.Discard, resp.Body)
+	resp.Body.Close()
+	assert.NilError(t, err)
+	image, _, err := client.ImageInspectWithRaw(ctx, img)
+	assert.NilError(t, err)
+
+	// Mark latest image layer to immutable
+	data := image.GraphDriver.Data
+	file, _ := os.Open(data["UpperDir"])
+	attr := 0x00000010
+	fsflags := uintptr(0x40086602)
+	argp := uintptr(unsafe.Pointer(&attr))
+	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), fsflags, argp)
+	assert.Equal(t, "errno 0", errno.Error())
+
+	// Try to remove the image, it should generate error
+	// but marking layer back to mutable before checking errors (so we don't break CI server)
+	_, err = client.ImageRemove(ctx, img, types.ImageRemoveOptions{})
+	attr = 0x00000000
+	argp = uintptr(unsafe.Pointer(&attr))
+	_, _, errno = syscall.Syscall(syscall.SYS_IOCTL, file.Fd(), fsflags, argp)
+	assert.Equal(t, "errno 0", errno.Error())
+	assert.Assert(t, err != nil)
+	errStr := err.Error()
+	if !(strings.Contains(errStr, "permission denied") || strings.Contains(errStr, "operation not permitted")) {
+		t.Errorf("ImageRemove error not an permission error %s", errStr)
+	}
+
+	// Verify that layer remaining on disk
+	dir, _ := os.Stat(data["UpperDir"])
+	assert.Equal(t, "true", strconv.FormatBool(dir.IsDir()))
+
+	// Run imageService.Cleanup() and make sure that layer was removed from disk
+	i.Cleanup()
+	_, err = os.Stat(data["UpperDir"])
+	assert.Assert(t, os.IsNotExist(err))
+
+	// Make sure that removal pending layers does not exist on layerdb either
+	layerdbItems, _ := os.ReadDir(filepath.Join(d.RootDir(), "/image/overlay2/layerdb/sha256"))
+	for _, folder := range layerdbItems {
+		assert.Equal(t, false, strings.HasSuffix(folder.Name(), "-removing"))
+	}
+}
diff --git a/integration/image/tag_test.go b/integration/image/tag_test.go
index f3eb9eb7dfff9..a6b44d67e048f 100644
--- a/integration/image/tag_test.go
+++ b/integration/image/tag_test.go
@@ -5,17 +5,15 @@ import (
 	"fmt"
 	"testing"
 
-	"github.com/docker/docker/internal/test/request"
-	"github.com/docker/docker/internal/testutil"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 // tagging a named image in a new unprefixed repo should work
 func TestTagUnprefixedRepoByNameOrName(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	// By name
@@ -33,7 +31,7 @@ func TestTagUnprefixedRepoByNameOrName(t *testing.T) {
 // TODO (yongtang): Migrate to unit tests
 func TestTagInvalidReference(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	invalidRepos := []string{"fo$z$", "Foo@3cc", "Foo$3", "Foo*3", "Fo^3", "Foo!3", "F)xcz(", "fo%asd", "FOO/bar"}
@@ -72,7 +70,7 @@ func TestTagInvalidReference(t *testing.T) {
 // ensure we allow the use of valid tags
 func TestTagValidPrefixedRepo(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	validRepos := []string{"fooo/bar", "fooaa/test", "foooo:t", "HOSTNAME.DOMAIN.COM:443/foo/bar"}
@@ -86,7 +84,7 @@ func TestTagValidPrefixedRepo(t *testing.T) {
 // tag an image with an existed tag name without -f option should work
 func TestTagExistedNameWithoutForce(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	err := client.ImageTag(ctx, "busybox:latest", "busybox:test")
@@ -96,9 +94,8 @@ func TestTagExistedNameWithoutForce(t *testing.T) {
 // ensure tagging using official names works
 // ensure all tags result in the same name
 func TestTagOfficialNames(t *testing.T) {
-	skip.If(t, testEnv.OSType == "windows")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	names := []string{
@@ -128,7 +125,7 @@ func TestTagOfficialNames(t *testing.T) {
 // ensure tags can not match digests
 func TestTagMatchesDigest(t *testing.T) {
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	digest := "busybox@sha256:abcdef76720241213f5303bda7704ec4c2ef75613173910a56fb1b6e20251507"
diff --git a/integration/internal/container/container.go b/integration/internal/container/container.go
index 20ad774242199..d082c6000b469 100644
--- a/integration/internal/container/container.go
+++ b/integration/internal/container/container.go
@@ -2,13 +2,15 @@ package container
 
 import (
 	"context"
+	"runtime"
 	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/client"
-	"gotest.tools/assert"
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
+	"gotest.tools/v3/assert"
 )
 
 // TestContainerConfig holds container configuration struct that
@@ -18,16 +20,20 @@ type TestContainerConfig struct {
 	Config           *container.Config
 	HostConfig       *container.HostConfig
 	NetworkingConfig *network.NetworkingConfig
+	Platform         *specs.Platform
 }
 
-// Create creates a container with the specified options
-// nolint: golint
-func Create(t *testing.T, ctx context.Context, client client.APIClient, ops ...func(*TestContainerConfig)) string { // nolint: golint
+// create creates a container with the specified options
+func create(ctx context.Context, t *testing.T, client client.APIClient, ops ...func(*TestContainerConfig)) (container.ContainerCreateCreatedBody, error) {
 	t.Helper()
+	cmd := []string{"top"}
+	if runtime.GOOS == "windows" {
+		cmd = []string{"sleep", "240"}
+	}
 	config := &TestContainerConfig{
 		Config: &container.Config{
 			Image: "busybox",
-			Cmd:   []string{"top"},
+			Cmd:   cmd,
 		},
 		HostConfig:       &container.HostConfig{},
 		NetworkingConfig: &network.NetworkingConfig{},
@@ -37,17 +43,27 @@ func Create(t *testing.T, ctx context.Context, client client.APIClient, ops ...f
 		op(config)
 	}
 
-	c, err := client.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Name)
+	return client.ContainerCreate(ctx, config.Config, config.HostConfig, config.NetworkingConfig, config.Platform, config.Name)
+}
+
+// Create creates a container with the specified options, asserting that there was no error
+func Create(ctx context.Context, t *testing.T, client client.APIClient, ops ...func(*TestContainerConfig)) string {
+	c, err := create(ctx, t, client, ops...)
 	assert.NilError(t, err)
 
 	return c.ID
 }
 
+// CreateExpectingErr creates a container, expecting an error with the specified message
+func CreateExpectingErr(ctx context.Context, t *testing.T, client client.APIClient, errMsg string, ops ...func(*TestContainerConfig)) {
+	_, err := create(ctx, t, client, ops...)
+	assert.ErrorContains(t, err, errMsg)
+}
+
 // Run creates and start a container with the specified options
-// nolint: golint
-func Run(t *testing.T, ctx context.Context, client client.APIClient, ops ...func(*TestContainerConfig)) string { // nolint: golint
+func Run(ctx context.Context, t *testing.T, client client.APIClient, ops ...func(*TestContainerConfig)) string {
 	t.Helper()
-	id := Create(t, ctx, client, ops...)
+	id := Create(ctx, t, client, ops...)
 
 	err := client.ContainerStart(ctx, id, types.ContainerStartOptions{})
 	assert.NilError(t, err)
diff --git a/integration/internal/container/exec.go b/integration/internal/container/exec.go
index 55ad23aeb5735..dfd46a2436b9d 100644
--- a/integration/internal/container/exec.go
+++ b/integration/internal/container/exec.go
@@ -57,7 +57,7 @@ func Exec(ctx context.Context, cli client.APIClient, id string, cmd []string) (E
 
 	// read the output
 	var outBuf, errBuf bytes.Buffer
-	outputDone := make(chan error)
+	outputDone := make(chan error, 1)
 
 	go func() {
 		// StdCopy demultiplexes the stream into two buffers
diff --git a/integration/internal/container/ns.go b/integration/internal/container/ns.go
new file mode 100644
index 0000000000000..bda06dd74c6f0
--- /dev/null
+++ b/integration/internal/container/ns.go
@@ -0,0 +1,21 @@
+package container
+
+import (
+	"context"
+	"strings"
+	"testing"
+
+	"github.com/docker/docker/client"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+)
+
+// GetContainerNS gets the value of the specified namespace of a container
+func GetContainerNS(ctx context.Context, t *testing.T, client client.APIClient, cID, nsName string) string {
+	t.Helper()
+	res, err := Exec(ctx, client, cID, []string{"readlink", "/proc/self/ns/" + nsName})
+	assert.NilError(t, err)
+	assert.Assert(t, is.Len(res.Stderr(), 0))
+	assert.Equal(t, 0, res.ExitCode)
+	return strings.TrimSpace(res.Stdout())
+}
diff --git a/integration/internal/container/ops.go b/integration/internal/container/ops.go
index df5598b62fb91..dae5a2a51269e 100644
--- a/integration/internal/container/ops.go
+++ b/integration/internal/container/ops.go
@@ -2,11 +2,14 @@ package container
 
 import (
 	"fmt"
+	"strings"
 
 	containertypes "github.com/docker/docker/api/types/container"
+	mounttypes "github.com/docker/docker/api/types/mount"
 	networktypes "github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/go-connections/nat"
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
 // WithName sets the name of the container
@@ -68,13 +71,20 @@ func WithWorkingDir(dir string) func(*TestContainerConfig) {
 	}
 }
 
+// WithMount adds an mount
+func WithMount(m mounttypes.Mount) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		c.HostConfig.Mounts = append(c.HostConfig.Mounts, m)
+	}
+}
+
 // WithVolume sets the volume of the container
-func WithVolume(name string) func(*TestContainerConfig) {
+func WithVolume(target string) func(*TestContainerConfig) {
 	return func(c *TestContainerConfig) {
 		if c.Config.Volumes == nil {
 			c.Config.Volumes = map[string]struct{}{}
 		}
-		c.Config.Volumes[name] = struct{}{}
+		c.Config.Volumes[target] = struct{}{}
 	}
 }
 
@@ -85,6 +95,22 @@ func WithBind(src, target string) func(*TestContainerConfig) {
 	}
 }
 
+// WithTmpfs sets a target path in the container to a tmpfs
+func WithTmpfs(target string) func(config *TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		if c.HostConfig.Tmpfs == nil {
+			c.HostConfig.Tmpfs = make(map[string]string)
+		}
+
+		spec := strings.SplitN(target, ":", 2)
+		var opts string
+		if len(spec) > 1 {
+			opts = spec[1]
+		}
+		c.HostConfig.Tmpfs[spec[0]] = opts
+	}
+}
+
 // WithIPv4 sets the specified ip for the specified network of the container
 func WithIPv4(network, ip string) func(*TestContainerConfig) {
 	return func(c *TestContainerConfig) {
@@ -120,17 +146,70 @@ func WithIPv6(network, ip string) func(*TestContainerConfig) {
 // WithLogDriver sets the log driver to use for the container
 func WithLogDriver(driver string) func(*TestContainerConfig) {
 	return func(c *TestContainerConfig) {
-		if c.HostConfig == nil {
-			c.HostConfig = &containertypes.HostConfig{}
-		}
 		c.HostConfig.LogConfig.Type = driver
 	}
 }
 
 // WithAutoRemove sets the container to be removed on exit
 func WithAutoRemove(c *TestContainerConfig) {
-	if c.HostConfig == nil {
-		c.HostConfig = &containertypes.HostConfig{}
-	}
 	c.HostConfig.AutoRemove = true
 }
+
+// WithPidsLimit sets the container's "pids-limit
+func WithPidsLimit(limit *int64) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		if c.HostConfig == nil {
+			c.HostConfig = &containertypes.HostConfig{}
+		}
+		c.HostConfig.PidsLimit = limit
+	}
+}
+
+// WithRestartPolicy sets container's restart policy
+func WithRestartPolicy(policy string) func(c *TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		c.HostConfig.RestartPolicy.Name = policy
+	}
+}
+
+// WithUser sets the user
+func WithUser(user string) func(c *TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		c.Config.User = user
+	}
+}
+
+// WithPrivileged sets privileged mode for the container
+func WithPrivileged(privileged bool) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		if c.HostConfig == nil {
+			c.HostConfig = &containertypes.HostConfig{}
+		}
+		c.HostConfig.Privileged = privileged
+	}
+}
+
+// WithCgroupnsMode sets the cgroup namespace mode for the container
+func WithCgroupnsMode(mode string) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		if c.HostConfig == nil {
+			c.HostConfig = &containertypes.HostConfig{}
+		}
+		c.HostConfig.CgroupnsMode = containertypes.CgroupnsMode(mode)
+	}
+}
+
+// WithExtraHost sets the user defined IP:Host mappings in the container's
+// /etc/hosts file
+func WithExtraHost(extraHost string) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		c.HostConfig.ExtraHosts = append(c.HostConfig.ExtraHosts, extraHost)
+	}
+}
+
+// WithPlatform specifies the desired platform the image should have.
+func WithPlatform(p *specs.Platform) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		c.Platform = p
+	}
+}
diff --git a/integration/internal/container/states.go b/integration/internal/container/states.go
index 088407deb84eb..fea8e3f598ea8 100644
--- a/integration/internal/container/states.go
+++ b/integration/internal/container/states.go
@@ -5,7 +5,8 @@ import (
 	"strings"
 
 	"github.com/docker/docker/client"
-	"gotest.tools/poll"
+	"github.com/pkg/errors"
+	"gotest.tools/v3/poll"
 )
 
 // IsStopped verifies the container is in stopped state.
@@ -39,3 +40,20 @@ func IsInState(ctx context.Context, client client.APIClient, containerID string,
 		return poll.Continue("waiting for container to be one of (%s), currently %s", strings.Join(state, ", "), inspect.State.Status)
 	}
 }
+
+// IsSuccessful verifies state.Status == "exited" && state.ExitCode == 0
+func IsSuccessful(ctx context.Context, client client.APIClient, containerID string) func(log poll.LogT) poll.Result {
+	return func(log poll.LogT) poll.Result {
+		inspect, err := client.ContainerInspect(ctx, containerID)
+		if err != nil {
+			return poll.Error(err)
+		}
+		if inspect.State.Status == "exited" {
+			if inspect.State.ExitCode == 0 {
+				return poll.Success()
+			}
+			return poll.Error(errors.Errorf("expected exit code 0, got %d", inspect.State.ExitCode))
+		}
+		return poll.Continue("waiting for container to be \"exited\", currently %s", inspect.State.Status)
+	}
+}
diff --git a/integration/internal/network/network.go b/integration/internal/network/network.go
index 07d366433ba1d..04f29c7bb24ca 100644
--- a/integration/internal/network/network.go
+++ b/integration/internal/network/network.go
@@ -6,7 +6,7 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/client"
-	"gotest.tools/assert"
+	"gotest.tools/v3/assert"
 )
 
 func createNetwork(ctx context.Context, client client.APIClient, name string, ops ...func(*types.NetworkCreate)) (string, error) {
@@ -26,8 +26,7 @@ func Create(ctx context.Context, client client.APIClient, name string, ops ...fu
 }
 
 // CreateNoError creates a network with the specified options and verifies there were no errors
-// nolint: golint
-func CreateNoError(t *testing.T, ctx context.Context, client client.APIClient, name string, ops ...func(*types.NetworkCreate)) string { // nolint: golint
+func CreateNoError(ctx context.Context, t *testing.T, client client.APIClient, name string, ops ...func(*types.NetworkCreate)) string { //nolint: golint
 	t.Helper()
 
 	name, err := createNetwork(ctx, client, name, ops...)
diff --git a/integration/internal/network/states.go b/integration/internal/network/states.go
new file mode 100644
index 0000000000000..31bf6ac7fe0fc
--- /dev/null
+++ b/integration/internal/network/states.go
@@ -0,0 +1,20 @@
+package network
+
+import (
+	"context"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/client"
+	"gotest.tools/v3/poll"
+)
+
+// IsRemoved verifies the network is removed.
+func IsRemoved(ctx context.Context, client client.NetworkAPIClient, networkID string) func(log poll.LogT) poll.Result {
+	return func(log poll.LogT) poll.Result {
+		_, err := client.NetworkInspect(ctx, networkID, types.NetworkInspectOptions{})
+		if err == nil {
+			return poll.Continue("waiting for network %s to be removed", networkID)
+		}
+		return poll.Success()
+	}
+}
diff --git a/integration/internal/requirement/requirement_linux.go b/integration/internal/requirement/requirement_linux.go
index 10a36b0fd94e8..0718646f9cbb9 100644
--- a/integration/internal/requirement/requirement_linux.go
+++ b/integration/internal/requirement/requirement_linux.go
@@ -1,12 +1,22 @@
 package requirement // import "github.com/docker/docker/integration/internal/requirement"
 
 import (
+	"os"
 	"strings"
 
 	"github.com/docker/docker/pkg/parsers/kernel"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/icmd"
 )
 
+// CgroupNamespacesEnabled checks if cgroup namespaces are enabled on this host
+func CgroupNamespacesEnabled() bool {
+	if _, err := os.Stat("/proc/self/ns/cgroup"); os.IsNotExist(err) {
+		return false
+	}
+
+	return true
+}
+
 func overlayFSSupported() bool {
 	result := icmd.RunCommand("/bin/sh", "-c", "cat /proc/filesystems")
 	if result.Error != nil {
diff --git a/integration/internal/requirement/requirement_windows.go b/integration/internal/requirement/requirement_windows.go
index 975aa77c7343b..7abcaa2990f50 100644
--- a/integration/internal/requirement/requirement_windows.go
+++ b/integration/internal/requirement/requirement_windows.go
@@ -1,3 +1,4 @@
+//go:build windows
 // +build windows
 
 package requirement // import "github.com/docker/docker/integration/internal/requirement"
diff --git a/integration/internal/swarm/service.go b/integration/internal/swarm/service.go
index 33c8a67d96263..19ebff0e9a691 100644
--- a/integration/internal/swarm/service.go
+++ b/integration/internal/swarm/service.go
@@ -9,17 +9,18 @@ import (
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
-	"github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/environment"
-	"gotest.tools/assert"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"github.com/docker/docker/client"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/environment"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 // ServicePoll tweaks the pollSettings for `service`
 func ServicePoll(config *poll.Settings) {
 	// Override the default pollSettings for `service` resource here ...
-	config.Timeout = 30 * time.Second
+	config.Timeout = 15 * time.Second
 	config.Delay = 100 * time.Millisecond
 	if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" {
 		config.Timeout = 90 * time.Second
@@ -48,12 +49,13 @@ func ContainerPoll(config *poll.Settings) {
 }
 
 // NewSwarm creates a swarm daemon for testing
-func NewSwarm(t *testing.T, testEnv *environment.Execution, ops ...func(*daemon.Daemon)) *daemon.Daemon {
+func NewSwarm(t *testing.T, testEnv *environment.Execution, ops ...daemon.Option) *daemon.Daemon {
 	t.Helper()
 	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
 	if testEnv.DaemonInfo.ExperimentalBuild {
-		ops = append(ops, daemon.WithExperimental)
+		ops = append(ops, daemon.WithExperimental())
 	}
 	d := daemon.New(t, ops...)
 	d.StartAndSwarmInit(t)
@@ -66,27 +68,37 @@ type ServiceSpecOpt func(*swarmtypes.ServiceSpec)
 // CreateService creates a service on the passed in swarm daemon.
 func CreateService(t *testing.T, d *daemon.Daemon, opts ...ServiceSpecOpt) string {
 	t.Helper()
-	spec := defaultServiceSpec()
-	for _, o := range opts {
-		o(&spec)
-	}
 
 	client := d.NewClientT(t)
 	defer client.Close()
 
+	spec := CreateServiceSpec(t, opts...)
 	resp, err := client.ServiceCreate(context.Background(), spec, types.ServiceCreateOptions{})
 	assert.NilError(t, err, "error creating service")
 	return resp.ID
 }
 
-func defaultServiceSpec() swarmtypes.ServiceSpec {
+// CreateServiceSpec creates a default service-spec, and applies the provided options
+func CreateServiceSpec(t *testing.T, opts ...ServiceSpecOpt) swarmtypes.ServiceSpec {
+	t.Helper()
 	var spec swarmtypes.ServiceSpec
 	ServiceWithImage("busybox:latest")(&spec)
 	ServiceWithCommand([]string{"/bin/top"})(&spec)
 	ServiceWithReplicas(1)(&spec)
+
+	for _, o := range opts {
+		o(&spec)
+	}
 	return spec
 }
 
+// ServiceWithMode sets the mode of the service to the provided mode.
+func ServiceWithMode(mode swarmtypes.ServiceMode) func(*swarmtypes.ServiceSpec) {
+	return func(spec *swarmtypes.ServiceSpec) {
+		spec.Mode = mode
+	}
+}
+
 // ServiceWithInit sets whether the service should use init or not
 func ServiceWithInit(b *bool) func(*swarmtypes.ServiceSpec) {
 	return func(spec *swarmtypes.ServiceSpec) {
@@ -138,6 +150,14 @@ func ServiceWithReplicas(n uint64) ServiceSpecOpt {
 	}
 }
 
+// ServiceWithMaxReplicas sets the max replicas for the service
+func ServiceWithMaxReplicas(n uint64) ServiceSpecOpt {
+	return func(spec *swarmtypes.ServiceSpec) {
+		ensurePlacement(spec)
+		spec.TaskTemplate.Placement.MaxReplicas = n
+	}
+}
+
 // ServiceWithName sets the name of the service
 func ServiceWithName(name string) ServiceSpecOpt {
 	return func(spec *swarmtypes.ServiceSpec) {
@@ -168,20 +188,34 @@ func ServiceWithSysctls(sysctls map[string]string) ServiceSpecOpt {
 	}
 }
 
+// ServiceWithCapabilities sets the Capabilities option of the service's ContainerSpec.
+func ServiceWithCapabilities(add []string, drop []string) ServiceSpecOpt {
+	return func(spec *swarmtypes.ServiceSpec) {
+		ensureContainerSpec(spec)
+		spec.TaskTemplate.ContainerSpec.CapabilityAdd = add
+		spec.TaskTemplate.ContainerSpec.CapabilityDrop = drop
+	}
+}
+
+// ServiceWithPidsLimit sets the PidsLimit option of the service's Resources.Limits.
+func ServiceWithPidsLimit(limit int64) ServiceSpecOpt {
+	return func(spec *swarmtypes.ServiceSpec) {
+		ensureResources(spec)
+		spec.TaskTemplate.Resources.Limits.Pids = limit
+	}
+}
+
 // GetRunningTasks gets the list of running tasks for a service
-func GetRunningTasks(t *testing.T, d *daemon.Daemon, serviceID string) []swarmtypes.Task {
+func GetRunningTasks(t *testing.T, c client.ServiceAPIClient, serviceID string) []swarmtypes.Task {
 	t.Helper()
-	client := d.NewClientT(t)
-	defer client.Close()
 
-	filterArgs := filters.NewArgs()
-	filterArgs.Add("desired-state", "running")
-	filterArgs.Add("service", serviceID)
+	tasks, err := c.TaskList(context.Background(), types.TaskListOptions{
+		Filters: filters.NewArgs(
+			filters.Arg("service", serviceID),
+			filters.Arg("desired-state", "running"),
+		),
+	})
 
-	options := types.TaskListOptions{
-		Filters: filterArgs,
-	}
-	tasks, err := client.TaskList(context.Background(), options)
 	assert.NilError(t, err)
 	return tasks
 }
@@ -202,8 +236,26 @@ func ExecTask(t *testing.T, d *daemon.Daemon, task swarmtypes.Task, config types
 	return attach
 }
 
+func ensureResources(spec *swarmtypes.ServiceSpec) {
+	if spec.TaskTemplate.Resources == nil {
+		spec.TaskTemplate.Resources = &swarmtypes.ResourceRequirements{}
+	}
+	if spec.TaskTemplate.Resources.Limits == nil {
+		spec.TaskTemplate.Resources.Limits = &swarmtypes.Limit{}
+	}
+	if spec.TaskTemplate.Resources.Reservations == nil {
+		spec.TaskTemplate.Resources.Reservations = &swarmtypes.Resources{}
+	}
+}
+
 func ensureContainerSpec(spec *swarmtypes.ServiceSpec) {
 	if spec.TaskTemplate.ContainerSpec == nil {
 		spec.TaskTemplate.ContainerSpec = &swarmtypes.ContainerSpec{}
 	}
 }
+
+func ensurePlacement(spec *swarmtypes.ServiceSpec) {
+	if spec.TaskTemplate.Placement == nil {
+		spec.TaskTemplate.Placement = &swarmtypes.Placement{}
+	}
+}
diff --git a/integration/internal/swarm/states.go b/integration/internal/swarm/states.go
new file mode 100644
index 0000000000000..3f69feeef3656
--- /dev/null
+++ b/integration/internal/swarm/states.go
@@ -0,0 +1,165 @@
+package swarm
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	swarmtypes "github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/client"
+	"gotest.tools/v3/poll"
+)
+
+// NoTasksForService verifies that there are no more tasks for the given service
+func NoTasksForService(ctx context.Context, client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
+	return func(log poll.LogT) poll.Result {
+		tasks, err := client.TaskList(ctx, types.TaskListOptions{
+			Filters: filters.NewArgs(
+				filters.Arg("service", serviceID),
+			),
+		})
+		if err == nil {
+			if len(tasks) == 0 {
+				return poll.Success()
+			}
+			if len(tasks) > 0 {
+				return poll.Continue("task count for service %s at %d waiting for 0", serviceID, len(tasks))
+			}
+			return poll.Continue("waiting for tasks for service %s to be deleted", serviceID)
+		}
+		// TODO we should not use an error as indication that the tasks are gone. There may be other reasons for an error to occur.
+		return poll.Success()
+	}
+}
+
+// NoTasks verifies that all tasks are gone
+func NoTasks(ctx context.Context, client client.ServiceAPIClient) func(log poll.LogT) poll.Result {
+	return func(log poll.LogT) poll.Result {
+		tasks, err := client.TaskList(ctx, types.TaskListOptions{})
+		switch {
+		case err != nil:
+			return poll.Error(err)
+		case len(tasks) == 0:
+			return poll.Success()
+		default:
+			return poll.Continue("waiting for all tasks to be removed: task count at %d", len(tasks))
+		}
+	}
+}
+
+// RunningTasksCount verifies there are `instances` tasks running for `serviceID`
+func RunningTasksCount(client client.ServiceAPIClient, serviceID string, instances uint64) func(log poll.LogT) poll.Result {
+	return func(log poll.LogT) poll.Result {
+		filter := filters.NewArgs()
+		filter.Add("service", serviceID)
+		tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
+			Filters: filter,
+		})
+		var running int
+		var taskError string
+		for _, task := range tasks {
+			switch task.Status.State {
+			case swarmtypes.TaskStateRunning:
+				running++
+			case swarmtypes.TaskStateFailed:
+				if task.Status.Err != "" {
+					taskError = task.Status.Err
+				}
+			}
+		}
+
+		switch {
+		case err != nil:
+			return poll.Error(err)
+		case running > int(instances):
+			return poll.Continue("waiting for tasks to terminate")
+		case running < int(instances) && taskError != "":
+			return poll.Continue("waiting for tasks to enter run state. task failed with error: %s", taskError)
+		case running == int(instances):
+			return poll.Success()
+		default:
+			return poll.Continue("running task count at %d waiting for %d (total tasks: %d)", running, instances, len(tasks))
+		}
+	}
+}
+
+// JobComplete is a poll function for determining that a ReplicatedJob is
+// completed additionally, while polling, it verifies that the job never
+// exceeds MaxConcurrent running tasks
+func JobComplete(client client.CommonAPIClient, service swarmtypes.Service) func(log poll.LogT) poll.Result {
+	filter := filters.NewArgs()
+	filter.Add("service", service.ID)
+
+	var jobIteration swarmtypes.Version
+	if service.JobStatus != nil {
+		jobIteration = service.JobStatus.JobIteration
+	}
+
+	maxRaw := service.Spec.Mode.ReplicatedJob.MaxConcurrent
+	totalRaw := service.Spec.Mode.ReplicatedJob.TotalCompletions
+
+	max := int(*maxRaw)
+	total := int(*totalRaw)
+
+	previousResult := ""
+
+	return func(log poll.LogT) poll.Result {
+		tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
+			Filters: filter,
+		})
+
+		if err != nil {
+			poll.Error(err)
+		}
+
+		var running int
+		var completed int
+
+		var runningSlot []int
+		var runningID []string
+
+		for _, task := range tasks {
+			// make sure the task has the same job iteration
+			if task.JobIteration == nil || task.JobIteration.Index != jobIteration.Index {
+				continue
+			}
+			switch task.Status.State {
+			case swarmtypes.TaskStateRunning:
+				running++
+				runningSlot = append(runningSlot, task.Slot)
+				runningID = append(runningID, task.ID)
+			case swarmtypes.TaskStateComplete:
+				completed++
+			}
+		}
+
+		switch {
+		case running > max:
+			return poll.Error(fmt.Errorf(
+				"number of running tasks (%v) exceeds max (%v)", running, max,
+			))
+		case (completed + running) > total:
+			return poll.Error(fmt.Errorf(
+				"number of tasks exceeds total (%v), %v running and %v completed",
+				total, running, completed,
+			))
+		case completed == total && running == 0:
+			return poll.Success()
+		default:
+			newRes := fmt.Sprintf(
+				"Completed: %2d Running: %v\n\t%v",
+				completed, runningSlot, runningID,
+			)
+			if newRes == previousResult {
+			} else {
+				previousResult = newRes
+			}
+
+			return poll.Continue(
+				"Job not yet finished, %v completed and %v running out of %v total",
+				completed, running, total,
+			)
+		}
+	}
+}
diff --git a/integration/network/delete_test.go b/integration/network/delete_test.go
index f9ab91986d4b9..221960e986bba 100644
--- a/integration/network/delete_test.go
+++ b/integration/network/delete_test.go
@@ -6,11 +6,11 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/versions"
+	dclient "github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/network"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func containsNetwork(nws []types.NetworkResource, networkID string) bool {
@@ -27,13 +27,10 @@ func containsNetwork(nws []types.NetworkResource, networkID string) bool {
 // first network's ID as name.
 //
 // After successful creation, properties of all three networks is returned
-func createAmbiguousNetworks(t *testing.T) (string, string, string) {
-	client := request.NewAPIClient(t)
-	ctx := context.Background()
-
-	testNet := network.CreateNoError(t, ctx, client, "testNet")
-	idPrefixNet := network.CreateNoError(t, ctx, client, testNet[:12])
-	fullIDNet := network.CreateNoError(t, ctx, client, testNet)
+func createAmbiguousNetworks(ctx context.Context, t *testing.T, client dclient.APIClient) (string, string, string) {
+	testNet := network.CreateNoError(ctx, t, client, "testNet")
+	idPrefixNet := network.CreateNoError(ctx, t, client, testNet[:12])
+	fullIDNet := network.CreateNoError(ctx, t, client, testNet)
 
 	nws, err := client.NetworkList(ctx, types.NetworkListOptions{})
 	assert.NilError(t, err)
@@ -48,11 +45,11 @@ func createAmbiguousNetworks(t *testing.T) (string, string, string) {
 func TestNetworkCreateDelete(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	netName := "testnetwork_" + t.Name()
-	network.CreateNoError(t, ctx, client, netName,
+	network.CreateNoError(ctx, t, client, netName,
 		network.WithCheckDuplicate(),
 	)
 	assert.Check(t, IsNetworkAvailable(client, netName))
@@ -71,9 +68,9 @@ func TestDockerNetworkDeletePreferID(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows",
 		"FIXME. Windows doesn't run DinD and uses networks shared between control daemon and daemon under test")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
-	testNet, idPrefixNet, fullIDNet := createAmbiguousNetworks(t)
+	testNet, idPrefixNet, fullIDNet := createAmbiguousNetworks(ctx, t, client)
 
 	// Delete the network using a prefix of the first network's ID as name.
 	// This should the network name with the id-prefix, not the original network.
diff --git a/integration/network/dns_test.go b/integration/network/dns_test.go
new file mode 100644
index 0000000000000..c4a50fe9a235a
--- /dev/null
+++ b/integration/network/dns_test.go
@@ -0,0 +1,35 @@
+package network // import "github.com/docker/docker/integration/network"
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/integration/internal/network"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
+)
+
+func TestDaemonDNSFallback(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	skip.If(t, testEnv.IsUserNamespace)
+
+	d := daemon.New(t)
+	d.StartWithBusybox(t, "-b", "none", "--dns", "127.127.127.1", "--dns", "8.8.8.8")
+	defer d.Stop(t)
+
+	c := d.NewClientT(t)
+	ctx := context.Background()
+
+	network.CreateNoError(ctx, t, c, "test")
+	defer c.NetworkRemove(ctx, "test")
+
+	cid := container.Run(ctx, t, c, container.WithNetworkMode("test"), container.WithCmd("nslookup", "docker.com"))
+	defer c.ContainerRemove(ctx, cid, types.ContainerRemoveOptions{Force: true})
+
+	poll.WaitOn(t, container.IsSuccessful(ctx, c, cid), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(10*time.Second))
+}
diff --git a/integration/network/helpers.go b/integration/network/helpers.go
index d5907f2baa125..ac015b17152bc 100644
--- a/integration/network/helpers.go
+++ b/integration/network/helpers.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package network
@@ -5,14 +6,12 @@ package network
 import (
 	"context"
 	"fmt"
-	"os"
 	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/client"
-	"github.com/docker/docker/pkg/parsers/kernel"
-	"gotest.tools/assert/cmp"
-	"gotest.tools/icmd"
+	"gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/icmd"
 )
 
 // CreateMasterDummy creates a dummy network interface
@@ -74,21 +73,3 @@ func IsNetworkNotAvailable(c client.NetworkAPIClient, name string) cmp.Compariso
 		return cmp.ResultSuccess
 	}
 }
-
-// CheckKernelMajorVersionGreaterOrEqualThen returns whether the kernel version is greater or equal than the one provided
-func CheckKernelMajorVersionGreaterOrEqualThen(kernelVersion int, majorVersion int) bool {
-	kv, err := kernel.GetKernelVersion()
-	if err != nil {
-		return false
-	}
-	if kv.Kernel < kernelVersion || (kv.Kernel == kernelVersion && kv.Major < majorVersion) {
-		return false
-	}
-	return true
-}
-
-// IsUserNamespace returns whether the user namespace remapping is enabled
-func IsUserNamespace() bool {
-	root := os.Getenv("DOCKER_REMAP_ROOT")
-	return root != ""
-}
diff --git a/integration/network/helpers_windows.go b/integration/network/helpers_windows.go
index 049d89310a696..35121fb200897 100644
--- a/integration/network/helpers_windows.go
+++ b/integration/network/helpers_windows.go
@@ -3,11 +3,10 @@ package network
 import (
 	"context"
 	"fmt"
-	"os"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/client"
-	"gotest.tools/assert/cmp"
+	"gotest.tools/v3/assert/cmp"
 )
 
 // IsNetworkAvailable provides a comparison to check if a docker network is available
@@ -41,9 +40,3 @@ func IsNetworkNotAvailable(c client.NetworkAPIClient, name string) cmp.Compariso
 		return cmp.ResultSuccess
 	}
 }
-
-// IsUserNamespace returns whether the user namespace remapping is enabled
-func IsUserNamespace() bool {
-	root := os.Getenv("DOCKER_REMAP_ROOT")
-	return root != ""
-}
diff --git a/integration/network/inspect_test.go b/integration/network/inspect_test.go
index 1e9ad94865f3b..0a97154d8e7ad 100644
--- a/integration/network/inspect_test.go
+++ b/integration/network/inspect_test.go
@@ -3,177 +3,101 @@ package network // import "github.com/docker/docker/integration/network"
 import (
 	"context"
 	"testing"
-	"time"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/filters"
-	swarmtypes "github.com/docker/docker/api/types/swarm"
-	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/network"
 	"github.com/docker/docker/integration/internal/swarm"
-	"gotest.tools/assert"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
-const defaultSwarmPort = 2477
-
 func TestInspectNetwork(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 
-	overlayName := "overlay1"
-	overlayID := network.CreateNoError(t, context.Background(), client, overlayName,
+	networkName := "Overlay" + t.Name()
+	overlayID := network.CreateNoError(context.Background(), t, c, networkName,
 		network.WithDriver("overlay"),
 		network.WithCheckDuplicate(),
 	)
 
-	var instances uint64 = 4
+	var instances uint64 = 2
 	serviceName := "TestService" + t.Name()
 
 	serviceID := swarm.CreateService(t, d,
 		swarm.ServiceWithReplicas(instances),
 		swarm.ServiceWithName(serviceName),
-		swarm.ServiceWithNetwork(overlayName),
-	)
-
-	poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, instances), swarm.ServicePoll)
-
-	_, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
-	assert.NilError(t, err)
-
-	// Test inspect verbose with full NetworkID
-	networkVerbose, err := client.NetworkInspect(context.Background(), overlayID, types.NetworkInspectOptions{
-		Verbose: true,
-	})
-	assert.NilError(t, err)
-	assert.Assert(t, validNetworkVerbose(networkVerbose, serviceName, instances))
-
-	// Test inspect verbose with partial NetworkID
-	networkVerbose, err = client.NetworkInspect(context.Background(), overlayID[0:11], types.NetworkInspectOptions{
-		Verbose: true,
-	})
-	assert.NilError(t, err)
-	assert.Assert(t, validNetworkVerbose(networkVerbose, serviceName, instances))
-
-	// Test inspect verbose with Network name and swarm scope
-	networkVerbose, err = client.NetworkInspect(context.Background(), overlayName, types.NetworkInspectOptions{
-		Verbose: true,
-		Scope:   "swarm",
-	})
-	assert.NilError(t, err)
-	assert.Assert(t, validNetworkVerbose(networkVerbose, serviceName, instances))
-
-	err = client.ServiceRemove(context.Background(), serviceID)
-	assert.NilError(t, err)
-
-	poll.WaitOn(t, serviceIsRemoved(client, serviceID), swarm.ServicePoll)
-	poll.WaitOn(t, noTasks(client), swarm.ServicePoll)
-
-	serviceID2 := swarm.CreateService(t, d,
-		swarm.ServiceWithReplicas(instances),
-		swarm.ServiceWithName(serviceName),
-		swarm.ServiceWithNetwork(overlayName),
+		swarm.ServiceWithNetwork(networkName),
 	)
 
-	poll.WaitOn(t, serviceRunningTasksCount(client, serviceID2, instances), swarm.ServicePoll)
-
-	err = client.ServiceRemove(context.Background(), serviceID2)
-	assert.NilError(t, err)
-
-	poll.WaitOn(t, serviceIsRemoved(client, serviceID2), swarm.ServicePoll)
-	poll.WaitOn(t, noTasks(client), swarm.ServicePoll)
-
-	err = client.NetworkRemove(context.Background(), overlayID)
-	assert.NilError(t, err)
-
-	poll.WaitOn(t, networkIsRemoved(client, overlayID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
-}
-
-func serviceRunningTasksCount(client client.ServiceAPIClient, serviceID string, instances uint64) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		filter := filters.NewArgs()
-		filter.Add("service", serviceID)
-		tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
-			Filters: filter,
-		})
-		switch {
-		case err != nil:
-			return poll.Error(err)
-		case len(tasks) == int(instances):
-			for _, task := range tasks {
-				if task.Status.State != swarmtypes.TaskStateRunning {
-					return poll.Continue("waiting for tasks to enter run state")
-				}
-			}
-			return poll.Success()
-		default:
-			return poll.Continue("task count at %d waiting for %d", len(tasks), instances)
-		}
+	poll.WaitOn(t, swarm.RunningTasksCount(c, serviceID, instances), swarm.ServicePoll)
+
+	tests := []struct {
+		name    string
+		network string
+		opts    types.NetworkInspectOptions
+	}{
+		{
+			name:    "full network id",
+			network: overlayID,
+			opts: types.NetworkInspectOptions{
+				Verbose: true,
+			},
+		},
+		{
+			name:    "partial network id",
+			network: overlayID[0:11],
+			opts: types.NetworkInspectOptions{
+				Verbose: true,
+			},
+		},
+		{
+			name:    "network name",
+			network: networkName,
+			opts: types.NetworkInspectOptions{
+				Verbose: true,
+			},
+		},
+		{
+			name:    "network name and swarm scope",
+			network: networkName,
+			opts: types.NetworkInspectOptions{
+				Verbose: true,
+				Scope:   "swarm",
+			},
+		},
 	}
-}
-
-func networkIsRemoved(client client.NetworkAPIClient, networkID string) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		_, err := client.NetworkInspect(context.Background(), networkID, types.NetworkInspectOptions{})
-		if err == nil {
-			return poll.Continue("waiting for network %s to be removed", networkID)
-		}
-		return poll.Success()
-	}
-}
+	ctx := context.Background()
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			nw, err := c.NetworkInspect(ctx, tc.network, tc.opts)
+			assert.NilError(t, err)
+
+			if service, ok := nw.Services[serviceName]; ok {
+				assert.Equal(t, len(service.Tasks), int(instances))
+			}
 
-func serviceIsRemoved(client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		filter := filters.NewArgs()
-		filter.Add("service", serviceID)
-		_, err := client.TaskList(context.Background(), types.TaskListOptions{
-			Filters: filter,
-		})
-		if err == nil {
-			return poll.Continue("waiting for service %s to be deleted", serviceID)
-		}
-		return poll.Success()
-	}
-}
+			assert.Assert(t, nw.IPAM.Config != nil)
 
-func noTasks(client client.ServiceAPIClient) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		filter := filters.NewArgs()
-		tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
-			Filters: filter,
+			for _, cfg := range nw.IPAM.Config {
+				assert.Assert(t, cfg.Gateway != "")
+				assert.Assert(t, cfg.Subnet != "")
+			}
 		})
-		switch {
-		case err != nil:
-			return poll.Error(err)
-		case len(tasks) == 0:
-			return poll.Success()
-		default:
-			return poll.Continue("task count at %d waiting for 0", len(tasks))
-		}
-	}
-}
-
-// Check to see if Service and Tasks info are part of the inspect verbose response
-func validNetworkVerbose(network types.NetworkResource, service string, instances uint64) bool {
-	if service, ok := network.Services[service]; ok {
-		if len(service.Tasks) != int(instances) {
-			return false
-		}
 	}
 
-	if network.IPAM.Config == nil {
-		return false
-	}
-
-	for _, cfg := range network.IPAM.Config {
-		if cfg.Gateway == "" || cfg.Subnet == "" {
-			return false
-		}
-	}
-	return true
+	// TODO find out why removing networks is needed; other tests fail if the network is not removed, even though they run on a new daemon.
+	err := c.ServiceRemove(ctx, serviceID)
+	assert.NilError(t, err)
+	poll.WaitOn(t, swarm.NoTasksForService(ctx, c, serviceID), swarm.ServicePoll)
+	err = c.NetworkRemove(ctx, overlayID)
+	assert.NilError(t, err)
+	poll.WaitOn(t, network.IsRemoved(ctx, c, overlayID), swarm.NetworkPoll)
 }
diff --git a/integration/network/ipvlan/ipvlan_test.go b/integration/network/ipvlan/ipvlan_test.go
index b14649429231b..969772fe96cb4 100644
--- a/integration/network/ipvlan/ipvlan_test.go
+++ b/integration/network/ipvlan/ipvlan_test.go
@@ -1,29 +1,31 @@
+//go:build !windows
 // +build !windows
 
-package ipvlan
+package ipvlan // import "github.com/docker/docker/integration/network/ipvlan"
 
 import (
 	"context"
+	"os"
+	"os/exec"
 	"strings"
+	"sync"
 	"testing"
-	"time"
 
 	dclient "github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
 	net "github.com/docker/docker/integration/internal/network"
 	n "github.com/docker/docker/integration/network"
-	"github.com/docker/docker/internal/test/daemon"
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
 func TestDockerNetworkIpvlanPersistance(t *testing.T) {
 	// verify the driver automatically provisions the 802.1q link (di-dummy0.70)
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
-	skip.If(t, testEnv.IsRemoteDaemon())
-	skip.If(t, !ipvlanKernelSupport(), "Kernel doesn't support ipvlan")
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, !ipvlanKernelSupport(t), "Kernel doesn't support ipvlan")
 
-	d := daemon.New(t, daemon.WithExperimental)
+	d := daemon.New(t)
 	d.StartWithBusybox(t)
 	defer d.Stop(t)
 
@@ -32,25 +34,23 @@ func TestDockerNetworkIpvlanPersistance(t *testing.T) {
 	n.CreateMasterDummy(t, master)
 	defer n.DeleteInterface(t, master)
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
 	// create a network specifying the desired sub-interface name
 	netName := "di-persist"
-	net.CreateNoError(t, context.Background(), client, netName,
+	net.CreateNoError(context.Background(), t, c, netName,
 		net.WithIPvlan("di-dummy0.70", ""),
 	)
 
-	assert.Check(t, n.IsNetworkAvailable(client, netName))
+	assert.Check(t, n.IsNetworkAvailable(c, netName))
 	// Restart docker daemon to test the config has persisted to disk
 	d.Restart(t)
-	assert.Check(t, n.IsNetworkAvailable(client, netName))
+	assert.Check(t, n.IsNetworkAvailable(c, netName))
 }
 
 func TestDockerNetworkIpvlan(t *testing.T) {
-	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
-	skip.If(t, testEnv.IsRemoteDaemon())
-	skip.If(t, !ipvlanKernelSupport(), "Kernel doesn't support ipvlan")
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, !ipvlanKernelSupport(t), "Kernel doesn't support ipvlan")
 
 	for _, tc := range []struct {
 		name string
@@ -85,13 +85,11 @@ func TestDockerNetworkIpvlan(t *testing.T) {
 			test: testIpvlanAddressing,
 		},
 	} {
-		d := daemon.New(t, daemon.WithExperimental)
+		d := daemon.New(t)
 		d.StartWithBusybox(t)
+		c := d.NewClientT(t)
 
-		client, err := d.NewClient()
-		assert.NilError(t, err)
-
-		t.Run(tc.name, tc.test(client))
+		t.Run(tc.name, tc.test(c))
 
 		d.Stop(t)
 		// FIXME(vdemeester) clean network
@@ -105,7 +103,7 @@ func testIpvlanSubinterface(client dclient.APIClient) func(*testing.T) {
 		defer n.DeleteInterface(t, master)
 
 		netName := "di-subinterface"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithIPvlan("di-dummy0.60", ""),
 		)
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
@@ -130,7 +128,7 @@ func testIpvlanOverlapParent(client dclient.APIClient) func(*testing.T) {
 		n.CreateVlanInterface(t, master, parent, "30")
 
 		netName := "di-subinterface"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithIPvlan(parent, ""),
 		)
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
@@ -147,14 +145,14 @@ func testIpvlanL2NilParent(client dclient.APIClient) func(*testing.T) {
 	return func(t *testing.T) {
 		// ipvlan l2 mode - dummy parent interface is provisioned dynamically
 		netName := "di-nil-parent"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithIPvlan("", ""),
 		)
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
 
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
-		id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
+		id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
+		id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
 
 		_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
 		assert.NilError(t, err)
@@ -164,24 +162,20 @@ func testIpvlanL2NilParent(client dclient.APIClient) func(*testing.T) {
 func testIpvlanL2InternalMode(client dclient.APIClient) func(*testing.T) {
 	return func(t *testing.T) {
 		netName := "di-internal"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithIPvlan("", ""),
 			net.WithInternal(),
 		)
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
 
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
-		id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
+		id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
+		id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
 
-		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
-		defer cancel()
-		_, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
-		// FIXME(vdemeester) check the time of error ?
-		assert.Check(t, err != nil)
-		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
+		result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"})
+		assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable"))
 
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
+		_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
 		assert.NilError(t, err)
 	}
 }
@@ -189,7 +183,7 @@ func testIpvlanL2InternalMode(client dclient.APIClient) func(*testing.T) {
 func testIpvlanL3NilParent(client dclient.APIClient) func(*testing.T) {
 	return func(t *testing.T) {
 		netName := "di-nil-parent-l3"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithIPvlan("", "l3"),
 			net.WithIPAM("172.28.230.0/24", ""),
 			net.WithIPAM("172.28.220.0/24", ""),
@@ -197,11 +191,11 @@ func testIpvlanL3NilParent(client dclient.APIClient) func(*testing.T) {
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
 
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client,
+		id1 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.220.10"),
 		)
-		id2 := container.Run(t, ctx, client,
+		id2 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.230.10"),
 		)
@@ -214,7 +208,7 @@ func testIpvlanL3NilParent(client dclient.APIClient) func(*testing.T) {
 func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) {
 	return func(t *testing.T) {
 		netName := "di-internal-l3"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithIPvlan("", "l3"),
 			net.WithInternal(),
 			net.WithIPAM("172.28.230.0/24", ""),
@@ -223,23 +217,19 @@ func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) {
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
 
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client,
+		id1 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.220.10"),
 		)
-		id2 := container.Run(t, ctx, client,
+		id2 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.230.10"),
 		)
 
-		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
-		defer cancel()
-		_, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
-		// FIXME(vdemeester) check the time of error ?
-		assert.Check(t, err != nil)
-		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
+		result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"})
+		assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable"))
 
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
+		_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
 		assert.NilError(t, err)
 	}
 }
@@ -247,7 +237,7 @@ func testIpvlanL3InternalMode(client dclient.APIClient) func(*testing.T) {
 func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) {
 	return func(t *testing.T) {
 		netName := "dualstackl2"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithIPvlan("", ""),
 			net.WithIPv6(),
 			net.WithIPAM("172.28.200.0/24", ""),
@@ -259,12 +249,12 @@ func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) {
 
 		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client,
+		id1 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.200.20"),
 			container.WithIPv6(netName, "2001:db8:abc8::20"),
 		)
-		id2 := container.Run(t, ctx, client,
+		id2 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.200.21"),
 			container.WithIPv6(netName, "2001:db8:abc8::21"),
@@ -280,12 +270,12 @@ func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) {
 		assert.NilError(t, err)
 
 		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
-		id3 := container.Run(t, ctx, client,
+		id3 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.202.20"),
 			container.WithIPv6(netName, "2001:db8:abc6::20"),
 		)
-		id4 := container.Run(t, ctx, client,
+		id4 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.202.21"),
 			container.WithIPv6(netName, "2001:db8:abc6::21"),
@@ -314,7 +304,7 @@ func testIpvlanL2MultiSubnet(client dclient.APIClient) func(*testing.T) {
 func testIpvlanL3MultiSubnet(client dclient.APIClient) func(*testing.T) {
 	return func(t *testing.T) {
 		netName := "dualstackl3"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithIPvlan("", "l3"),
 			net.WithIPv6(),
 			net.WithIPAM("172.28.10.0/24", ""),
@@ -326,12 +316,12 @@ func testIpvlanL3MultiSubnet(client dclient.APIClient) func(*testing.T) {
 
 		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client,
+		id1 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.10.20"),
 			container.WithIPv6(netName, "2001:db8:abc9::20"),
 		)
-		id2 := container.Run(t, ctx, client,
+		id2 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.10.21"),
 			container.WithIPv6(netName, "2001:db8:abc9::21"),
@@ -347,12 +337,12 @@ func testIpvlanL3MultiSubnet(client dclient.APIClient) func(*testing.T) {
 		assert.NilError(t, err)
 
 		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
-		id3 := container.Run(t, ctx, client,
+		id3 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.12.20"),
 			container.WithIPv6(netName, "2001:db8:abc7::20"),
 		)
-		id4 := container.Run(t, ctx, client,
+		id4 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netName),
 			container.WithIPv4(netName, "172.28.12.21"),
 			container.WithIPv6(netName, "2001:db8:abc7::21"),
@@ -383,7 +373,7 @@ func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) {
 		// Verify ipvlan l2 mode sets the proper default gateway routes via netlink
 		// for either an explicitly set route by the user or inferred via default IPAM
 		netNameL2 := "dualstackl2"
-		net.CreateNoError(t, context.Background(), client, netNameL2,
+		net.CreateNoError(context.Background(), t, client, netNameL2,
 			net.WithIPvlan("", "l2"),
 			net.WithIPv6(),
 			net.WithIPAM("172.28.140.0/24", "172.28.140.254"),
@@ -392,7 +382,7 @@ func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) {
 		assert.Check(t, n.IsNetworkAvailable(client, netNameL2))
 
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client,
+		id1 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netNameL2),
 		)
 		// Validate ipvlan l2 mode defaults gateway sets the default IPAM next-hop inferred from the subnet
@@ -406,7 +396,7 @@ func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) {
 
 		// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
 		netNameL3 := "dualstackl3"
-		net.CreateNoError(t, context.Background(), client, netNameL3,
+		net.CreateNoError(context.Background(), t, client, netNameL3,
 			net.WithIPvlan("", "l3"),
 			net.WithIPv6(),
 			net.WithIPAM("172.28.160.0/24", "172.28.160.254"),
@@ -414,7 +404,7 @@ func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) {
 		)
 		assert.Check(t, n.IsNetworkAvailable(client, netNameL3))
 
-		id2 := container.Run(t, ctx, client,
+		id2 := container.Run(ctx, t, client,
 			container.WithNetworkMode(netNameL3),
 		)
 		// Validate ipvlan l3 mode sets the v4 gateway to dev eth0 and disregards any explicit or inferred next-hops
@@ -428,7 +418,23 @@ func testIpvlanAddressing(client dclient.APIClient) func(*testing.T) {
 	}
 }
 
-// ensure Kernel version is >= v4.2 for ipvlan support
-func ipvlanKernelSupport() bool {
-	return n.CheckKernelMajorVersionGreaterOrEqualThen(4, 2)
+var (
+	once            sync.Once
+	ipvlanSupported bool
+)
+
+// figure out if ipvlan is supported by the kernel
+func ipvlanKernelSupport(t *testing.T) bool {
+	once.Do(func() {
+		// this may have the side effect of enabling the ipvlan module
+		exec.Command("modprobe", "ipvlan").Run()
+		_, err := os.Stat("/sys/module/ipvlan")
+		if err == nil {
+			ipvlanSupported = true
+		} else if !os.IsNotExist(err) {
+			t.Logf("WARNING: ipvlanKernelSupport: stat failed: %v\n", err)
+		}
+	})
+
+	return ipvlanSupported
 }
diff --git a/integration/network/ipvlan/main_test.go b/integration/network/ipvlan/main_test.go
index 2d5f62453c4ba..470a75a90fbff 100644
--- a/integration/network/ipvlan/main_test.go
+++ b/integration/network/ipvlan/main_test.go
@@ -1,3 +1,6 @@
+//go:build !windows
+// +build !windows
+
 package ipvlan // import "github.com/docker/docker/integration/network/ipvlan"
 
 import (
@@ -5,7 +8,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
@@ -26,8 +29,3 @@ func TestMain(m *testing.M) {
 	testEnv.Print()
 	os.Exit(m.Run())
 }
-
-func setupTest(t *testing.T) func() {
-	environment.ProtectAll(t, testEnv)
-	return func() { testEnv.Clean(t) }
-}
diff --git a/integration/network/ipvlan/main_windows_test.go b/integration/network/ipvlan/main_windows_test.go
new file mode 100644
index 0000000000000..d016a04b66905
--- /dev/null
+++ b/integration/network/ipvlan/main_windows_test.go
@@ -0,0 +1 @@
+package ipvlan // import "github.com/docker/docker/integration/network/ipvlan"
diff --git a/integration/network/macvlan/macvlan_test.go b/integration/network/macvlan/macvlan_test.go
index a81b9014b4c0d..d7dae5c86e2c8 100644
--- a/integration/network/macvlan/macvlan_test.go
+++ b/integration/network/macvlan/macvlan_test.go
@@ -1,26 +1,26 @@
+//go:build !windows
 // +build !windows
 
-package macvlan
+package macvlan // import "github.com/docker/docker/integration/network/macvlan"
 
 import (
 	"context"
 	"strings"
 	"testing"
-	"time"
 
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
 	net "github.com/docker/docker/integration/internal/network"
 	n "github.com/docker/docker/integration/network"
-	"github.com/docker/docker/internal/test/daemon"
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
 func TestDockerNetworkMacvlanPersistance(t *testing.T) {
 	// verify the driver automatically provisions the 802.1q link (dm-dummy0.60)
-	skip.If(t, testEnv.IsRemoteDaemon())
-	skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
 
 	d := daemon.New(t)
 	d.StartWithBusybox(t)
@@ -30,21 +30,20 @@ func TestDockerNetworkMacvlanPersistance(t *testing.T) {
 	n.CreateMasterDummy(t, master)
 	defer n.DeleteInterface(t, master)
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
 	netName := "dm-persist"
-	net.CreateNoError(t, context.Background(), client, netName,
+	net.CreateNoError(context.Background(), t, c, netName,
 		net.WithMacvlan("dm-dummy0.60"),
 	)
-	assert.Check(t, n.IsNetworkAvailable(client, netName))
+	assert.Check(t, n.IsNetworkAvailable(c, netName))
 	d.Restart(t)
-	assert.Check(t, n.IsNetworkAvailable(client, netName))
+	assert.Check(t, n.IsNetworkAvailable(c, netName))
 }
 
 func TestDockerNetworkMacvlan(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon())
-	skip.If(t, !macvlanKernelSupport(), "Kernel doesn't support macvlan")
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
 
 	for _, tc := range []struct {
 		name string
@@ -62,6 +61,9 @@ func TestDockerNetworkMacvlan(t *testing.T) {
 		}, {
 			name: "InternalMode",
 			test: testMacvlanInternalMode,
+		}, {
+			name: "MultiSubnet",
+			test: testMacvlanMultiSubnet,
 		}, {
 			name: "Addressing",
 			test: testMacvlanAddressing,
@@ -69,11 +71,9 @@ func TestDockerNetworkMacvlan(t *testing.T) {
 	} {
 		d := daemon.New(t)
 		d.StartWithBusybox(t)
+		c := d.NewClientT(t)
 
-		client, err := d.NewClient()
-		assert.NilError(t, err)
-
-		t.Run(tc.name, tc.test(client))
+		t.Run(tc.name, tc.test(c))
 
 		d.Stop(t)
 		// FIXME(vdemeester) clean network
@@ -89,7 +89,7 @@ func testMacvlanOverlapParent(client client.APIClient) func(*testing.T) {
 
 		netName := "dm-subinterface"
 		parentName := "dm-dummy0.40"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithMacvlan(parentName),
 		)
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
@@ -119,7 +119,7 @@ func testMacvlanSubinterface(client client.APIClient) func(*testing.T) {
 		n.CreateVlanInterface(t, master, parentName, "20")
 
 		netName := "dm-subinterface"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithMacvlan(parentName),
 		)
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
@@ -138,14 +138,14 @@ func testMacvlanNilParent(client client.APIClient) func(*testing.T) {
 	return func(t *testing.T) {
 		// macvlan bridge mode - dummy parent interface is provisioned dynamically
 		netName := "dm-nil-parent"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithMacvlan(""),
 		)
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
 
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
-		id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
+		id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
+		id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
 
 		_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
 		assert.Check(t, err == nil)
@@ -156,24 +156,20 @@ func testMacvlanInternalMode(client client.APIClient) func(*testing.T) {
 	return func(t *testing.T) {
 		// macvlan bridge mode - dummy parent interface is provisioned dynamically
 		netName := "dm-internal"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithMacvlan(""),
 			net.WithInternal(),
 		)
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
 
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
-		id2 := container.Run(t, ctx, client, container.WithNetworkMode(netName))
+		id1 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
+		id2 := container.Run(ctx, t, client, container.WithNetworkMode(netName))
 
-		timeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
-		defer cancel()
-		_, err := container.Exec(timeoutCtx, client, id1, []string{"ping", "-c", "1", "-w", "1", "8.8.8.8"})
-		// FIXME(vdemeester) check the time of error ?
-		assert.Check(t, err != nil)
-		assert.Check(t, timeoutCtx.Err() == context.DeadlineExceeded)
+		result, _ := container.Exec(ctx, client, id1, []string{"ping", "-c", "1", "8.8.8.8"})
+		assert.Check(t, strings.Contains(result.Combined(), "Network is unreachable"))
 
-		_, err = container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
+		_, err := container.Exec(ctx, client, id2, []string{"ping", "-c", "1", id1})
 		assert.Check(t, err == nil)
 	}
 }
@@ -181,7 +177,7 @@ func testMacvlanInternalMode(client client.APIClient) func(*testing.T) {
 func testMacvlanMultiSubnet(client client.APIClient) func(*testing.T) {
 	return func(t *testing.T) {
 		netName := "dualstackbridge"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithMacvlan(""),
 			net.WithIPv6(),
 			net.WithIPAM("172.28.100.0/24", ""),
@@ -194,12 +190,12 @@ func testMacvlanMultiSubnet(client client.APIClient) func(*testing.T) {
 
 		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.100.0/24 and 2001:db8:abc2::/64
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client,
+		id1 := container.Run(ctx, t, client,
 			container.WithNetworkMode("dualstackbridge"),
 			container.WithIPv4("dualstackbridge", "172.28.100.20"),
 			container.WithIPv6("dualstackbridge", "2001:db8:abc2::20"),
 		)
-		id2 := container.Run(t, ctx, client,
+		id2 := container.Run(ctx, t, client,
 			container.WithNetworkMode("dualstackbridge"),
 			container.WithIPv4("dualstackbridge", "172.28.100.21"),
 			container.WithIPv6("dualstackbridge", "2001:db8:abc2::21"),
@@ -215,12 +211,12 @@ func testMacvlanMultiSubnet(client client.APIClient) func(*testing.T) {
 		assert.NilError(t, err)
 
 		// start dual stack containers and verify the user specified --ip and --ip6 addresses on subnets 172.28.102.0/24 and 2001:db8:abc4::/64
-		id3 := container.Run(t, ctx, client,
+		id3 := container.Run(ctx, t, client,
 			container.WithNetworkMode("dualstackbridge"),
 			container.WithIPv4("dualstackbridge", "172.28.102.20"),
 			container.WithIPv6("dualstackbridge", "2001:db8:abc4::20"),
 		)
-		id4 := container.Run(t, ctx, client,
+		id4 := container.Run(ctx, t, client,
 			container.WithNetworkMode("dualstackbridge"),
 			container.WithIPv4("dualstackbridge", "172.28.102.21"),
 			container.WithIPv6("dualstackbridge", "2001:db8:abc4::21"),
@@ -242,7 +238,7 @@ func testMacvlanMultiSubnet(client client.APIClient) func(*testing.T) {
 		// Inspect the v4 gateway to ensure the proper explicitly assigned default GW was assigned
 		assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].Gateway, "172.28.102.254")
 		// Inspect the v6 gateway to ensure the proper explicitly assigned default GW was assigned
-		assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8.abc4::254")
+		assert.Equal(t, c3.NetworkSettings.Networks["dualstackbridge"].IPv6Gateway, "2001:db8:abc4::254")
 	}
 }
 
@@ -250,7 +246,7 @@ func testMacvlanAddressing(client client.APIClient) func(*testing.T) {
 	return func(t *testing.T) {
 		// Ensure the default gateways, next-hops and default dev devices are properly set
 		netName := "dualstackbridge"
-		net.CreateNoError(t, context.Background(), client, netName,
+		net.CreateNoError(context.Background(), t, client, netName,
 			net.WithMacvlan(""),
 			net.WithIPv6(),
 			net.WithOption("macvlan_mode", "bridge"),
@@ -260,7 +256,7 @@ func testMacvlanAddressing(client client.APIClient) func(*testing.T) {
 		assert.Check(t, n.IsNetworkAvailable(client, netName))
 
 		ctx := context.Background()
-		id1 := container.Run(t, ctx, client,
+		id1 := container.Run(ctx, t, client,
 			container.WithNetworkMode("dualstackbridge"),
 		)
 
@@ -274,8 +270,3 @@ func testMacvlanAddressing(client client.APIClient) func(*testing.T) {
 		assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abca::254 dev eth0"))
 	}
 }
-
-// ensure Kernel version is >= v3.9 for macvlan support
-func macvlanKernelSupport() bool {
-	return n.CheckKernelMajorVersionGreaterOrEqualThen(3, 9)
-}
diff --git a/integration/network/macvlan/main_test.go b/integration/network/macvlan/main_test.go
index 31cf111b22af7..c7adc4f9072ec 100644
--- a/integration/network/macvlan/main_test.go
+++ b/integration/network/macvlan/main_test.go
@@ -1,3 +1,6 @@
+//go:build !windows
+// +build !windows
+
 package macvlan // import "github.com/docker/docker/integration/network/macvlan"
 
 import (
@@ -5,7 +8,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
@@ -26,8 +29,3 @@ func TestMain(m *testing.M) {
 	testEnv.Print()
 	os.Exit(m.Run())
 }
-
-func setupTest(t *testing.T) func() {
-	environment.ProtectAll(t, testEnv)
-	return func() { testEnv.Clean(t) }
-}
diff --git a/integration/network/macvlan/main_windows_test.go b/integration/network/macvlan/main_windows_test.go
new file mode 100644
index 0000000000000..367cd2c540679
--- /dev/null
+++ b/integration/network/macvlan/main_windows_test.go
@@ -0,0 +1 @@
+package macvlan // import "github.com/docker/docker/integration/network/macvlan"
diff --git a/integration/network/main_test.go b/integration/network/main_test.go
index 36ed19ca67add..80354dcf0e61b 100644
--- a/integration/network/main_test.go
+++ b/integration/network/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
diff --git a/integration/network/network_test.go b/integration/network/network_test.go
index 3829dd728e25f..ec0546aa0e866 100644
--- a/integration/network/network_test.go
+++ b/integration/network/network_test.go
@@ -3,42 +3,47 @@ package network // import "github.com/docker/docker/integration/network"
 import (
 	"bytes"
 	"context"
+	"encoding/json"
+	"net/http"
 	"os/exec"
 	"strings"
 	"testing"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/daemon"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"github.com/docker/docker/integration/internal/network"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/skip"
 )
 
 func TestRunContainerWithBridgeNone(t *testing.T) {
 	skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run")
 	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
-	skip.If(t, IsUserNamespace())
+	skip.If(t, testEnv.IsUserNamespace)
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
 
 	d := daemon.New(t)
 	d.StartWithBusybox(t, "-b", "none")
 	defer d.Stop(t)
 
-	client, err := d.NewClient()
-	assert.Check(t, err, "error creating client")
-
+	c := d.NewClientT(t)
 	ctx := context.Background()
-	id1 := container.Run(t, ctx, client)
-	defer client.ContainerRemove(ctx, id1, types.ContainerRemoveOptions{Force: true})
 
-	result, err := container.Exec(ctx, client, id1, []string{"ip", "l"})
+	id1 := container.Run(ctx, t, c)
+	defer c.ContainerRemove(ctx, id1, types.ContainerRemoveOptions{Force: true})
+
+	result, err := container.Exec(ctx, c, id1, []string{"ip", "l"})
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(false, strings.Contains(result.Combined(), "eth0")), "There shouldn't be eth0 in container in default(bridge) mode when bridge network is disabled")
 
-	id2 := container.Run(t, ctx, client, container.WithNetworkMode("bridge"))
-	defer client.ContainerRemove(ctx, id2, types.ContainerRemoveOptions{Force: true})
+	id2 := container.Run(ctx, t, c, container.WithNetworkMode("bridge"))
+	defer c.ContainerRemove(ctx, id2, types.ContainerRemoveOptions{Force: true})
 
-	result, err = container.Exec(ctx, client, id2, []string{"ip", "l"})
+	result, err = container.Exec(ctx, c, id2, []string{"ip", "l"})
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(false, strings.Contains(result.Combined(), "eth0")), "There shouldn't be eth0 in container in bridge mode when bridge network is disabled")
 
@@ -49,10 +54,98 @@ func TestRunContainerWithBridgeNone(t *testing.T) {
 	err = cmd.Run()
 	assert.NilError(t, err, "Failed to get current process network namespace: %+v", err)
 
-	id3 := container.Run(t, ctx, client, container.WithNetworkMode("host"))
-	defer client.ContainerRemove(ctx, id3, types.ContainerRemoveOptions{Force: true})
+	id3 := container.Run(ctx, t, c, container.WithNetworkMode("host"))
+	defer c.ContainerRemove(ctx, id3, types.ContainerRemoveOptions{Force: true})
+
+	result, err = container.Exec(ctx, c, id3, []string{"sh", "-c", nsCommand})
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(stdout.String(), result.Combined()), "The network namespace of container should be the same with host when --net=host and bridge network is disabled")
+}
+
+func TestNetworkInvalidJSON(t *testing.T) {
+	defer setupTest(t)()
+
+	endpoints := []string{
+		"/networks/create",
+		"/networks/bridge/connect",
+		"/networks/bridge/disconnect",
+	}
+
+	for _, ep := range endpoints {
+		ep := ep
+		t.Run(ep, func(t *testing.T) {
+			t.Parallel()
+
+			res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
+			assert.NilError(t, err)
+			assert.Equal(t, res.StatusCode, http.StatusBadRequest)
+
+			buf, err := request.ReadBody(body)
+			assert.NilError(t, err)
+			assert.Check(t, is.Contains(string(buf), "invalid character 'i' looking for beginning of object key string"))
+
+			res, body, err = request.Post(ep, request.JSON)
+			assert.NilError(t, err)
+			assert.Equal(t, res.StatusCode, http.StatusBadRequest)
+
+			buf, err = request.ReadBody(body)
+			assert.NilError(t, err)
+			assert.Check(t, is.Contains(string(buf), "got EOF while reading request body"))
+		})
+	}
+}
+
+// TestNetworkList verifies that /networks returns a list of networks either
+// with, or without a trailing slash (/networks/). Regression test for https://github.com/moby/moby/issues/24595
+func TestNetworkList(t *testing.T) {
+	defer setupTest(t)()
+
+	endpoints := []string{
+		"/networks",
+		"/networks/",
+	}
+
+	for _, ep := range endpoints {
+		ep := ep
+		t.Run(ep, func(t *testing.T) {
+			t.Parallel()
+
+			res, body, err := request.Get(ep, request.JSON)
+			assert.NilError(t, err)
+			assert.Equal(t, res.StatusCode, http.StatusOK)
+
+			buf, err := request.ReadBody(body)
+			assert.NilError(t, err)
+			var nws []types.NetworkResource
+			err = json.Unmarshal(buf, &nws)
+			assert.NilError(t, err)
+			assert.Assert(t, len(nws) > 0)
+		})
+	}
+}
+
+func TestHostIPv4BridgeLabel(t *testing.T) {
+	skip.If(t, testEnv.OSType == "windows")
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
+	d := daemon.New(t)
+	d.Start(t)
+	defer d.Stop(t)
+	c := d.NewClientT(t)
+	defer c.Close()
+	ctx := context.Background()
 
-	result, err = container.Exec(ctx, client, id3, []string{"sh", "-c", nsCommand})
+	ipv4SNATAddr := "172.0.0.172"
+	// Create a bridge network with --opt com.docker.network.host_ipv4=172.0.0.172
+	bridgeName := "hostIPv4Bridge"
+	network.CreateNoError(ctx, t, c, bridgeName,
+		network.WithDriver("bridge"),
+		network.WithOption("com.docker.network.host_ipv4", ipv4SNATAddr),
+		network.WithOption("com.docker.network.bridge.name", bridgeName),
+	)
+	out, err := c.NetworkInspect(ctx, bridgeName, types.NetworkInspectOptions{Verbose: true})
 	assert.NilError(t, err)
-	assert.Check(t, is.Equal(stdout.String(), result.Combined()), "The network namspace of container should be the same with host when --net=host and bridge network is disabled")
+	assert.Assert(t, len(out.IPAM.Config) > 0)
+	// Make sure the SNAT rule exists
+	icmd.RunCommand("iptables", "-t", "nat", "-C", "POSTROUTING", "-s", out.IPAM.Config[0].Subnet, "!", "-o", bridgeName, "-j", "SNAT", "--to-source", ipv4SNATAddr).Assert(t, icmd.Success)
 }
diff --git a/integration/network/service_test.go b/integration/network/service_test.go
index 4762b577ac9a7..e3cbb0ff17544 100644
--- a/integration/network/service_test.go
+++ b/integration/network/service_test.go
@@ -11,15 +11,16 @@ import (
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/network"
 	"github.com/docker/docker/integration/internal/swarm"
-	"github.com/docker/docker/internal/test/daemon"
-	"gotest.tools/assert"
-	"gotest.tools/icmd"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/icmd"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 // delInterface removes given network interface
 func delInterface(t *testing.T, ifName string) {
+	t.Helper()
 	icmd.RunCommand("ip", "link", "delete", ifName).Assert(t, icmd.Success)
 	icmd.RunCommand("iptables", "-t", "nat", "--flush").Assert(t, icmd.Success)
 	icmd.RunCommand("iptables", "--flush").Assert(t, icmd.Success)
@@ -27,61 +28,71 @@ func delInterface(t *testing.T, ifName string) {
 
 func TestDaemonRestartWithLiveRestore(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows")
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "skip test from new feature")
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
 	d := daemon.New(t)
 	defer d.Stop(t)
 	d.Start(t)
-	d.Restart(t, "--live-restore=true",
-		"--default-address-pool", "base=175.30.0.0/16,size=16",
-		"--default-address-pool", "base=175.33.0.0/16,size=24")
+
+	c := d.NewClientT(t)
+	defer c.Close()
 
 	// Verify bridge network's subnet
-	cli, err := d.NewClient()
-	assert.Assert(t, err)
-	defer cli.Close()
-	out, err := cli.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
+	out, err := c.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
+	assert.NilError(t, err)
+	subnet := out.IPAM.Config[0].Subnet
+
+	d.Restart(t,
+		"--live-restore=true",
+		"--default-address-pool", "base=175.30.0.0/16,size=16",
+		"--default-address-pool", "base=175.33.0.0/16,size=24",
+	)
+
+	out1, err := c.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
 	assert.NilError(t, err)
 	// Make sure docker0 doesn't get override with new IP in live restore case
-	assert.Equal(t, out.IPAM.Config[0].Subnet, "172.18.0.0/16")
+	assert.Equal(t, out1.IPAM.Config[0].Subnet, subnet)
 }
 
 func TestDaemonDefaultNetworkPools(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows")
 	// Remove docker0 bridge and the start daemon defining the predefined address pools
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "skip test from new feature")
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
 	defaultNetworkBridge := "docker0"
 	delInterface(t, defaultNetworkBridge)
 	d := daemon.New(t)
 	defer d.Stop(t)
 	d.Start(t,
 		"--default-address-pool", "base=175.30.0.0/16,size=16",
-		"--default-address-pool", "base=175.33.0.0/16,size=24")
+		"--default-address-pool", "base=175.33.0.0/16,size=24",
+	)
+
+	c := d.NewClientT(t)
+	defer c.Close()
 
 	// Verify bridge network's subnet
-	cli, err := d.NewClient()
-	assert.Assert(t, err)
-	defer cli.Close()
-	out, err := cli.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
+	out, err := c.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
 	assert.NilError(t, err)
 	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.30.0.0/16")
 
 	// Create a bridge network and verify its subnet is the second default pool
 	name := "elango" + t.Name()
-	network.CreateNoError(t, context.Background(), cli, name,
+	network.CreateNoError(context.Background(), t, c, name,
 		network.WithDriver("bridge"),
 	)
-	out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	out, err = c.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
 	assert.NilError(t, err)
 	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.33.0.0/24")
 
 	// Create a bridge network and verify its subnet is the third default pool
 	name = "saanvi" + t.Name()
-	network.CreateNoError(t, context.Background(), cli, name,
+	network.CreateNoError(context.Background(), t, c, name,
 		network.WithDriver("bridge"),
 	)
-	out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	out, err = c.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
 	assert.NilError(t, err)
 	assert.Equal(t, out.IPAM.Config[0].Subnet, "175.33.1.0/24")
 	delInterface(t, defaultNetworkBridge)
@@ -90,23 +101,24 @@ func TestDaemonDefaultNetworkPools(t *testing.T) {
 
 func TestDaemonRestartWithExistingNetwork(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows")
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "skip test from new feature")
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
 	defaultNetworkBridge := "docker0"
 	d := daemon.New(t)
 	d.Start(t)
 	defer d.Stop(t)
-	// Verify bridge network's subnet
-	cli, err := d.NewClient()
-	assert.Assert(t, err)
-	defer cli.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 
 	// Create a bridge network
 	name := "elango" + t.Name()
-	network.CreateNoError(t, context.Background(), cli, name,
+	network.CreateNoError(context.Background(), t, c, name,
 		network.WithDriver("bridge"),
 	)
-	out, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+
+	// Verify bridge network's subnet
+	out, err := c.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
 	assert.NilError(t, err)
 	networkip := out.IPAM.Config[0].Subnet
 
@@ -115,7 +127,7 @@ func TestDaemonRestartWithExistingNetwork(t *testing.T) {
 		"--default-address-pool", "base=175.30.0.0/16,size=16",
 		"--default-address-pool", "base=175.33.0.0/16,size=24")
 
-	out1, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	out1, err := c.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
 	assert.NilError(t, err)
 	assert.Equal(t, out1.IPAM.Config[0].Subnet, networkip)
 	delInterface(t, defaultNetworkBridge)
@@ -123,46 +135,48 @@ func TestDaemonRestartWithExistingNetwork(t *testing.T) {
 
 func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows")
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "skip test from new feature")
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
 	defaultNetworkBridge := "docker0"
 	d := daemon.New(t)
 	d.Start(t)
 	defer d.Stop(t)
-	// Verify bridge network's subnet
-	cli, err := d.NewClient()
-	assert.Assert(t, err)
-	defer cli.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 
 	// Create a bridge network
 	name := "elango" + t.Name()
-	network.CreateNoError(t, context.Background(), cli, name,
+	network.CreateNoError(context.Background(), t, c, name,
 		network.WithDriver("bridge"),
 	)
-	out, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+
+	// Verify bridge network's subnet
+	out, err := c.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
 	assert.NilError(t, err)
 	networkip := out.IPAM.Config[0].Subnet
 
 	// Create a bridge network
 	name = "sthira" + t.Name()
-	network.CreateNoError(t, context.Background(), cli, name,
+	network.CreateNoError(context.Background(), t, c, name,
 		network.WithDriver("bridge"),
 	)
-	out, err = cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	out, err = c.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
 	assert.NilError(t, err)
 	networkip2 := out.IPAM.Config[0].Subnet
 
 	// Restart daemon with default address pool option
 	d.Restart(t,
 		"--default-address-pool", "base=175.18.0.0/16,size=16",
-		"--default-address-pool", "base=175.19.0.0/16,size=24")
+		"--default-address-pool", "base=175.19.0.0/16,size=24",
+	)
 
 	// Create a bridge network
 	name = "saanvi" + t.Name()
-	network.CreateNoError(t, context.Background(), cli, name,
+	network.CreateNoError(context.Background(), t, c, name,
 		network.WithDriver("bridge"),
 	)
-	out1, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	out1, err := c.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
 	assert.NilError(t, err)
 
 	assert.Check(t, out1.IPAM.Config[0].Subnet != networkip)
@@ -172,33 +186,37 @@ func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) {
 
 func TestDaemonWithBipAndDefaultNetworkPool(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows")
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.38"), "skip test from new feature")
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
 	defaultNetworkBridge := "docker0"
 	d := daemon.New(t)
 	defer d.Stop(t)
-	d.Start(t, "--bip=172.60.0.1/16",
+	d.Start(t,
+		"--bip=172.60.0.1/16",
 		"--default-address-pool", "base=175.30.0.0/16,size=16",
-		"--default-address-pool", "base=175.33.0.0/16,size=24")
+		"--default-address-pool", "base=175.33.0.0/16,size=24",
+	)
+
+	c := d.NewClientT(t)
+	defer c.Close()
 
 	// Verify bridge network's subnet
-	cli, err := d.NewClient()
-	assert.Assert(t, err)
-	defer cli.Close()
-	out, err := cli.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
+	out, err := c.NetworkInspect(context.Background(), "bridge", types.NetworkInspectOptions{})
 	assert.NilError(t, err)
 	// Make sure BIP IP doesn't get override with new default address pool .
-	assert.Equal(t, out.IPAM.Config[0].Subnet, "172.60.0.1/16")
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "172.60.0.0/16")
 	delInterface(t, defaultNetworkBridge)
 }
 
 func TestServiceWithPredefinedNetwork(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows")
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 
 	hostName := "host"
 	var instances uint64 = 1
@@ -210,26 +228,29 @@ func TestServiceWithPredefinedNetwork(t *testing.T) {
 		swarm.ServiceWithNetwork(hostName),
 	)
 
-	poll.WaitOn(t, serviceRunningCount(client, serviceID, instances), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.RunningTasksCount(c, serviceID, instances), swarm.ServicePoll)
 
-	_, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
+	_, _, err := c.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
 	assert.NilError(t, err)
 
-	err = client.ServiceRemove(context.Background(), serviceID)
+	err = c.ServiceRemove(context.Background(), serviceID)
 	assert.NilError(t, err)
 }
 
 const ingressNet = "ingress"
 
 func TestServiceRemoveKeepsIngressNetwork(t *testing.T) {
+	t.Skip("FLAKY_TEST")
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
+
 	skip.If(t, testEnv.OSType == "windows")
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 
-	poll.WaitOn(t, swarmIngressReady(client), swarm.NetworkPoll)
+	poll.WaitOn(t, swarmIngressReady(c), swarm.NetworkPoll)
 
 	var instances uint64 = 1
 
@@ -247,20 +268,21 @@ func TestServiceRemoveKeepsIngressNetwork(t *testing.T) {
 		}),
 	)
 
-	poll.WaitOn(t, serviceRunningCount(client, serviceID, instances), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.RunningTasksCount(c, serviceID, instances), swarm.ServicePoll)
 
-	_, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
+	ctx := context.Background()
+	_, _, err := c.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
 	assert.NilError(t, err)
 
-	err = client.ServiceRemove(context.Background(), serviceID)
+	err = c.ServiceRemove(ctx, serviceID)
 	assert.NilError(t, err)
 
-	poll.WaitOn(t, serviceIsRemoved(client, serviceID), swarm.ServicePoll)
-	poll.WaitOn(t, noServices(client), swarm.ServicePoll)
+	poll.WaitOn(t, noServices(ctx, c), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.NoTasks(ctx, c), swarm.ServicePoll)
 
 	// Ensure that "ingress" is not removed or corrupted
 	time.Sleep(10 * time.Second)
-	netInfo, err := client.NetworkInspect(context.Background(), ingressNet, types.NetworkInspectOptions{
+	netInfo, err := c.NetworkInspect(ctx, ingressNet, types.NetworkInspectOptions{
 		Verbose: true,
 		Scope:   "swarm",
 	})
@@ -271,20 +293,7 @@ func TestServiceRemoveKeepsIngressNetwork(t *testing.T) {
 	assert.Assert(t, ok, "ingress-sbox not present in ingress network")
 }
 
-func serviceRunningCount(client client.ServiceAPIClient, serviceID string, instances uint64) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		services, err := client.ServiceList(context.Background(), types.ServiceListOptions{})
-		if err != nil {
-			return poll.Error(err)
-		}
-
-		if len(services) != int(instances) {
-			return poll.Continue("Service count at %d waiting for %d", len(services), instances)
-		}
-		return poll.Success()
-	}
-}
-
+//nolint:unused // for some reason, the "unused" linter marks this function as "unused"
 func swarmIngressReady(client client.NetworkAPIClient) func(log poll.LogT) poll.Result {
 	return func(log poll.LogT) poll.Result {
 		netInfo, err := client.NetworkInspect(context.Background(), ingressNet, types.NetworkInspectOptions{
@@ -307,36 +316,106 @@ func swarmIngressReady(client client.NetworkAPIClient) func(log poll.LogT) poll.
 	}
 }
 
-func noServices(client client.ServiceAPIClient) func(log poll.LogT) poll.Result {
+func noServices(ctx context.Context, client client.ServiceAPIClient) func(log poll.LogT) poll.Result {
 	return func(log poll.LogT) poll.Result {
-		services, err := client.ServiceList(context.Background(), types.ServiceListOptions{})
+		services, err := client.ServiceList(ctx, types.ServiceListOptions{})
 		switch {
 		case err != nil:
 			return poll.Error(err)
 		case len(services) == 0:
 			return poll.Success()
 		default:
-			return poll.Continue("Service count at %d waiting for 0", len(services))
+			return poll.Continue("waiting for all services to be removed: service count at %d", len(services))
 		}
 	}
 }
 
-func TestServiceWithDefaultAddressPoolInit(t *testing.T) {
+func TestServiceWithDataPathPortInit(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows")
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "DataPathPort was added in API v1.40")
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
 	defer setupTest(t)()
-	var ops = []func(*daemon.Daemon){}
-	ipAddr := []string{"20.20.0.0/16"}
-	ops = append(ops, daemon.WithSwarmDefaultAddrPool(ipAddr))
-	ops = append(ops, daemon.WithSwarmDefaultAddrPoolSubnetSize(24))
-	d := swarm.NewSwarm(t, testEnv, ops...)
+	var datapathPort uint32 = 7777
+	d := swarm.NewSwarm(t, testEnv, daemon.WithSwarmDataPathPort(datapathPort))
+	c := d.NewClientT(t)
+	ctx := context.Background()
+	// Create a overlay network
+	name := "saanvisthira" + t.Name()
+	overlayID := network.CreateNoError(context.Background(), t, c, name,
+		network.WithDriver("overlay"))
+
+	var instances uint64 = 1
+	serviceID := swarm.CreateService(t, d,
+		swarm.ServiceWithReplicas(instances),
+		swarm.ServiceWithName(name),
+		swarm.ServiceWithNetwork(name),
+	)
+
+	poll.WaitOn(t, swarm.RunningTasksCount(c, serviceID, instances), swarm.ServicePoll)
+
+	info := d.Info(t)
+	assert.Equal(t, info.Swarm.Cluster.DataPathPort, datapathPort)
+	err := c.ServiceRemove(ctx, serviceID)
+	assert.NilError(t, err)
+	poll.WaitOn(t, noServices(ctx, c), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.NoTasks(ctx, c), swarm.ServicePoll)
+	err = c.NetworkRemove(ctx, overlayID)
+	assert.NilError(t, err)
+	c.Close()
+	err = d.SwarmLeave(t, true)
+	assert.NilError(t, err)
+	d.Stop(t)
+
+	// Clean up , set it back to original one to make sure other tests don't fail
+	// call without datapath port option.
+	d = swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	nc := d.NewClientT(t)
+	defer nc.Close()
+	// Create a overlay network
+	name = "not-saanvisthira" + t.Name()
+	overlayID = network.CreateNoError(ctx, t, nc, name,
+		network.WithDriver("overlay"))
+
+	serviceID = swarm.CreateService(t, d,
+		swarm.ServiceWithReplicas(instances),
+		swarm.ServiceWithName(name),
+		swarm.ServiceWithNetwork(name),
+	)
+
+	poll.WaitOn(t, swarm.RunningTasksCount(nc, serviceID, instances), swarm.ServicePoll)
 
+	info = d.Info(t)
+	var defaultDataPathPort uint32 = 4789
+	assert.Equal(t, info.Swarm.Cluster.DataPathPort, defaultDataPathPort)
+	err = nc.ServiceRemove(ctx, serviceID)
+	assert.NilError(t, err)
+	poll.WaitOn(t, noServices(ctx, nc), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.NoTasks(ctx, nc), swarm.ServicePoll)
+	err = nc.NetworkRemove(ctx, overlayID)
+	assert.NilError(t, err)
+	err = d.SwarmLeave(t, true)
+	assert.NilError(t, err)
+}
+
+func TestServiceWithDefaultAddressPoolInit(t *testing.T) {
+	skip.If(t, testEnv.OSType == "windows")
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support Swarm-mode")
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv,
+		daemon.WithSwarmDefaultAddrPool([]string{"20.20.0.0/16"}),
+		daemon.WithSwarmDefaultAddrPoolSubnetSize(24))
+	defer d.Stop(t)
 	cli := d.NewClientT(t)
 	defer cli.Close()
+	ctx := context.Background()
 
 	// Create a overlay network
-	name := "saanvisthira" + t.Name()
-	network.CreateNoError(t, context.Background(), cli, name,
-		network.WithDriver("overlay"))
+	name := "sthira" + t.Name()
+	overlayID := network.CreateNoError(ctx, t, cli, name,
+		network.WithDriver("overlay"),
+		network.WithCheckDuplicate(),
+	)
 
 	var instances uint64 = 1
 	serviceName := "TestService" + t.Name()
@@ -346,27 +425,34 @@ func TestServiceWithDefaultAddressPoolInit(t *testing.T) {
 		swarm.ServiceWithNetwork(name),
 	)
 
-	poll.WaitOn(t, serviceRunningCount(cli, serviceID, instances), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.RunningTasksCount(cli, serviceID, instances), swarm.ServicePoll)
 
-	_, _, err := cli.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
+	_, _, err := cli.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
 	assert.NilError(t, err)
 
-	out, err := cli.NetworkInspect(context.Background(), name, types.NetworkInspectOptions{})
+	out, err := cli.NetworkInspect(ctx, overlayID, types.NetworkInspectOptions{Verbose: true})
 	assert.NilError(t, err)
 	t.Logf("%s: NetworkInspect: %+v", t.Name(), out)
 	assert.Assert(t, len(out.IPAM.Config) > 0)
+	// As of docker/swarmkit#2890, the ingress network uses the default address
+	// pool (whereas before, the subnet for the ingress network was hard-coded.
+	// This means that the ingress network gets the subnet 20.20.0.0/24, and
+	// the network we just created gets subnet 20.20.1.0/24.
+	assert.Equal(t, out.IPAM.Config[0].Subnet, "20.20.1.0/24")
+
+	// Also inspect ingress network and make sure its in the same subnet
+	out, err = cli.NetworkInspect(ctx, "ingress", types.NetworkInspectOptions{Verbose: true})
+	assert.NilError(t, err)
+	assert.Assert(t, len(out.IPAM.Config) > 0)
 	assert.Equal(t, out.IPAM.Config[0].Subnet, "20.20.0.0/24")
 
-	err = cli.ServiceRemove(context.Background(), serviceID)
+	err = cli.ServiceRemove(ctx, serviceID)
+	poll.WaitOn(t, noServices(ctx, cli), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.NoTasks(ctx, cli), swarm.ServicePoll)
+	assert.NilError(t, err)
+	err = cli.NetworkRemove(ctx, overlayID)
+	assert.NilError(t, err)
+	err = d.SwarmLeave(t, true)
 	assert.NilError(t, err)
-	d.SwarmLeave(true)
-	d.Stop(t)
 
-	// Clean up , set it back to original one to make sure other tests don't fail
-	ipAddr = []string{"10.0.0.0/8"}
-	ops = append(ops, daemon.WithSwarmDefaultAddrPool(ipAddr))
-	ops = append(ops, daemon.WithSwarmDefaultAddrPoolSubnetSize(24))
-	d = swarm.NewSwarm(t, testEnv, ops...)
-	d.SwarmLeave(true)
-	defer d.Stop(t)
 }
diff --git a/integration/plugin/authz/authz_plugin_test.go b/integration/plugin/authz/authz_plugin_test.go
index d0f5d8a783a60..3c8cfc6a65f6b 100644
--- a/integration/plugin/authz/authz_plugin_test.go
+++ b/integration/plugin/authz/authz_plugin_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package authz // import "github.com/docker/docker/integration/plugin/authz"
@@ -6,7 +7,6 @@ import (
 	"context"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net"
 	"net/http"
 	"net/http/httputil"
@@ -22,11 +22,12 @@ import (
 	eventtypes "github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/environment"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/authorization"
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/environment"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 const (
@@ -59,7 +60,7 @@ func setupTestV1(t *testing.T) func() {
 	assert.NilError(t, err)
 
 	fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", testAuthZPlugin)
-	err = ioutil.WriteFile(fileName, []byte(server.URL), 0644)
+	err = os.WriteFile(fileName, []byte(server.URL), 0644)
 	assert.NilError(t, err)
 
 	return func() {
@@ -87,18 +88,16 @@ func TestAuthZPluginAllowRequest(t *testing.T) {
 	ctrl.resRes.Allow = true
 	d.StartWithBusybox(t, "--authorization-plugin="+testAuthZPlugin)
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
-
+	c := d.NewClientT(t)
 	ctx := context.Background()
 
 	// Ensure command successful
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, c)
 
 	assertURIRecorded(t, ctrl.requestsURIs, "/containers/create")
 	assertURIRecorded(t, ctrl.requestsURIs, fmt.Sprintf("/containers/%s/start", cID))
 
-	_, err = client.ServerVersion(ctx)
+	_, err := c.ServerVersion(ctx)
 	assert.NilError(t, err)
 	assert.Equal(t, 1, ctrl.versionReqCount)
 	assert.Equal(t, 1, ctrl.versionResCount)
@@ -126,10 +125,10 @@ func TestAuthZPluginTLS(t *testing.T) {
 	ctrl.reqRes.Allow = true
 	ctrl.resRes.Allow = true
 
-	client, err := newTLSAPIClient(testDaemonHTTPSAddr, cacertPath, clientCertPath, clientKeyPath)
+	c, err := newTLSAPIClient(testDaemonHTTPSAddr, cacertPath, clientCertPath, clientKeyPath)
 	assert.NilError(t, err)
 
-	_, err = client.ServerVersion(context.Background())
+	_, err = c.ServerVersion(context.Background())
 	assert.NilError(t, err)
 
 	assert.Equal(t, "client", ctrl.reqUser)
@@ -143,7 +142,7 @@ func newTLSAPIClient(host, cacertPath, certPath, keyPath string) (client.APIClie
 	}
 	return client.NewClientWithOpts(
 		client.WithTLSClientConfig(cacertPath, certPath, keyPath),
-		client.WithDialer(dialer),
+		client.WithDialContext(dialer.DialContext),
 		client.WithHost(host))
 }
 
@@ -153,11 +152,10 @@ func TestAuthZPluginDenyRequest(t *testing.T) {
 	ctrl.reqRes.Allow = false
 	ctrl.reqRes.Msg = unauthorizedMessage
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
 	// Ensure command is blocked
-	_, err = client.ServerVersion(context.Background())
+	_, err := c.ServerVersion(context.Background())
 	assert.Assert(t, err != nil)
 	assert.Equal(t, 1, ctrl.versionReqCount)
 	assert.Equal(t, 0, ctrl.versionResCount)
@@ -179,10 +177,10 @@ func TestAuthZPluginAPIDenyResponse(t *testing.T) {
 
 	conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10)
 	assert.NilError(t, err)
-	client := httputil.NewClientConn(conn, nil)
-	req, err := http.NewRequest("GET", "/version", nil)
+	c := httputil.NewClientConn(conn, nil)
+	req, err := http.NewRequest(http.MethodGet, "/version", nil)
 	assert.NilError(t, err)
-	resp, err := client.Do(req)
+	resp, err := c.Do(req)
 
 	assert.NilError(t, err)
 	assert.DeepEqual(t, http.StatusForbidden, resp.StatusCode)
@@ -195,11 +193,10 @@ func TestAuthZPluginDenyResponse(t *testing.T) {
 	ctrl.resRes.Allow = false
 	ctrl.resRes.Msg = unauthorizedMessage
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
 	// Ensure command is blocked
-	_, err = client.ServerVersion(context.Background())
+	_, err := c.ServerVersion(context.Background())
 	assert.Assert(t, err != nil)
 	assert.Equal(t, 1, ctrl.versionReqCount)
 	assert.Equal(t, 1, ctrl.versionResCount)
@@ -219,29 +216,16 @@ func TestAuthZPluginAllowEventStream(t *testing.T) {
 	ctrl.resRes.Allow = true
 	d.StartWithBusybox(t, "--authorization-plugin="+testAuthZPlugin)
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
-
+	c := d.NewClientT(t)
 	ctx := context.Background()
 
-	startTime := strconv.FormatInt(systemTime(t, client, testEnv).Unix(), 10)
-	events, errs, cancel := systemEventsSince(client, startTime)
+	startTime := strconv.FormatInt(systemTime(t, c, testEnv).Unix(), 10)
+	events, errs, cancel := systemEventsSince(c, startTime)
 	defer cancel()
 
 	// Create a container and wait for the creation events
-	cID := container.Run(t, ctx, client)
-
-	for i := 0; i < 100; i++ {
-		c, err := client.ContainerInspect(ctx, cID)
-		assert.NilError(t, err)
-		if c.State.Running {
-			break
-		}
-		if i == 99 {
-			t.Fatal("Container didn't run within 10s")
-		}
-		time.Sleep(100 * time.Millisecond)
-	}
+	cID := container.Run(ctx, t, c)
+	poll.WaitOn(t, container.IsInState(ctx, c, cID, "running"))
 
 	created := false
 	started := false
@@ -304,11 +288,10 @@ func TestAuthZPluginErrorResponse(t *testing.T) {
 	ctrl.reqRes.Allow = true
 	ctrl.resRes.Err = errorMessage
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
 	// Ensure command is blocked
-	_, err = client.ServerVersion(context.Background())
+	_, err := c.ServerVersion(context.Background())
 	assert.Assert(t, err != nil)
 	assert.Equal(t, fmt.Sprintf("Error response from daemon: plugin %s failed with error: %s: %s", testAuthZPlugin, authorization.AuthZApiResponse, errorMessage), err.Error())
 }
@@ -318,11 +301,10 @@ func TestAuthZPluginErrorRequest(t *testing.T) {
 	d.Start(t, "--authorization-plugin="+testAuthZPlugin)
 	ctrl.reqRes.Err = errorMessage
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
 	// Ensure command is blocked
-	_, err = client.ServerVersion(context.Background())
+	_, err := c.ServerVersion(context.Background())
 	assert.Assert(t, err != nil)
 	assert.Equal(t, fmt.Sprintf("Error response from daemon: plugin %s failed with error: %s: %s", testAuthZPlugin, authorization.AuthZApiRequest, errorMessage), err.Error())
 }
@@ -334,10 +316,9 @@ func TestAuthZPluginEnsureNoDuplicatePluginRegistration(t *testing.T) {
 	ctrl.reqRes.Allow = true
 	ctrl.resRes.Allow = true
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
-	_, err = client.ServerVersion(context.Background())
+	_, err := c.ServerVersion(context.Background())
 	assert.NilError(t, err)
 
 	// assert plugin is only called once..
@@ -351,27 +332,25 @@ func TestAuthZPluginEnsureLoadImportWorking(t *testing.T) {
 	ctrl.resRes.Allow = true
 	d.StartWithBusybox(t, "--authorization-plugin="+testAuthZPlugin, "--authorization-plugin="+testAuthZPlugin)
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
-
+	c := d.NewClientT(t)
 	ctx := context.Background()
 
-	tmp, err := ioutil.TempDir("", "test-authz-load-import")
+	tmp, err := os.MkdirTemp("", "test-authz-load-import")
 	assert.NilError(t, err)
 	defer os.RemoveAll(tmp)
 
 	savedImagePath := filepath.Join(tmp, "save.tar")
 
-	err = imageSave(client, savedImagePath, "busybox")
+	err = imageSave(c, savedImagePath, "busybox")
 	assert.NilError(t, err)
-	err = imageLoad(client, savedImagePath)
+	err = imageLoad(c, savedImagePath)
 	assert.NilError(t, err)
 
 	exportedImagePath := filepath.Join(tmp, "export.tar")
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, c)
 
-	responseReader, err := client.ContainerExport(context.Background(), cID)
+	responseReader, err := c.ContainerExport(context.Background(), cID)
 	assert.NilError(t, err)
 	defer responseReader.Close()
 	file, err := os.Create(exportedImagePath)
@@ -380,7 +359,7 @@ func TestAuthZPluginEnsureLoadImportWorking(t *testing.T) {
 	_, err = io.Copy(file, responseReader)
 	assert.NilError(t, err)
 
-	err = imageImport(client, exportedImagePath)
+	err = imageImport(c, exportedImagePath)
 	assert.NilError(t, err)
 }
 
@@ -390,48 +369,47 @@ func TestAuthzPluginEnsureContainerCopyToFrom(t *testing.T) {
 	ctrl.resRes.Allow = true
 	d.StartWithBusybox(t, "--authorization-plugin="+testAuthZPlugin, "--authorization-plugin="+testAuthZPlugin)
 
-	dir, err := ioutil.TempDir("", t.Name())
-	assert.Assert(t, err)
+	dir, err := os.MkdirTemp("", t.Name())
+	assert.NilError(t, err)
 	defer os.RemoveAll(dir)
 
-	f, err := ioutil.TempFile(dir, "send")
-	assert.Assert(t, err)
+	f, err := os.CreateTemp(dir, "send")
+	assert.NilError(t, err)
 	defer f.Close()
 
 	buf := make([]byte, 1024)
 	fileSize := len(buf) * 1024 * 10
 	for written := 0; written < fileSize; {
 		n, err := f.Write(buf)
-		assert.Assert(t, err)
+		assert.NilError(t, err)
 		written += n
 	}
 
+	c := d.NewClientT(t)
 	ctx := context.Background()
-	client, err := d.NewClient()
-	assert.Assert(t, err)
 
-	cID := container.Run(t, ctx, client)
-	defer client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true})
+	cID := container.Run(ctx, t, c)
+	defer c.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true})
 
 	_, err = f.Seek(0, io.SeekStart)
-	assert.Assert(t, err)
+	assert.NilError(t, err)
 
 	srcInfo, err := archive.CopyInfoSourcePath(f.Name(), false)
-	assert.Assert(t, err)
+	assert.NilError(t, err)
 	srcArchive, err := archive.TarResource(srcInfo)
-	assert.Assert(t, err)
+	assert.NilError(t, err)
 	defer srcArchive.Close()
 
 	dstDir, preparedArchive, err := archive.PrepareArchiveCopy(srcArchive, srcInfo, archive.CopyInfo{Path: "/test"})
-	assert.Assert(t, err)
+	assert.NilError(t, err)
 
-	err = client.CopyToContainer(ctx, cID, dstDir, preparedArchive, types.CopyToContainerOptions{})
-	assert.Assert(t, err)
+	err = c.CopyToContainer(ctx, cID, dstDir, preparedArchive, types.CopyToContainerOptions{})
+	assert.NilError(t, err)
 
-	rdr, _, err := client.CopyFromContainer(ctx, cID, "/test")
-	assert.Assert(t, err)
-	_, err = io.Copy(ioutil.Discard, rdr)
-	assert.Assert(t, err)
+	rdr, _, err := c.CopyFromContainer(ctx, cID, "/test")
+	assert.NilError(t, err)
+	_, err = io.Copy(io.Discard, rdr)
+	assert.NilError(t, err)
 }
 
 func imageSave(client client.APIClient, path, image string) error {
@@ -499,7 +477,7 @@ func TestAuthZPluginHeader(t *testing.T) {
 	conn, err := net.DialTimeout(daemonURL.Scheme, daemonURL.Path, time.Second*10)
 	assert.NilError(t, err)
 	client := httputil.NewClientConn(conn, nil)
-	req, err := http.NewRequest("GET", "/version", nil)
+	req, err := http.NewRequest(http.MethodGet, "/version", nil)
 	assert.NilError(t, err)
 	resp, err := client.Do(req)
 	assert.NilError(t, err)
diff --git a/integration/plugin/authz/authz_plugin_v2_test.go b/integration/plugin/authz/authz_plugin_v2_test.go
index 6b5115146b242..173d0a5df6a32 100644
--- a/integration/plugin/authz/authz_plugin_v2_test.go
+++ b/integration/plugin/authz/authz_plugin_v2_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package authz // import "github.com/docker/docker/integration/plugin/authz"
@@ -5,7 +6,7 @@ package authz // import "github.com/docker/docker/integration/plugin/authz"
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"os"
 	"strings"
 	"testing"
@@ -16,8 +17,8 @@ import (
 	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/container"
 	"github.com/docker/docker/integration/internal/requirement"
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
 var (
@@ -43,13 +44,11 @@ func TestAuthZPluginV2AllowNonVolumeRequest(t *testing.T) {
 	skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
 	defer setupTestV2(t)()
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
-
+	c := d.NewClientT(t)
 	ctx := context.Background()
 
 	// Install authz plugin
-	err = pluginInstallGrantAllPermissions(client, authzPluginNameWithTag)
+	err := pluginInstallGrantAllPermissions(c, authzPluginNameWithTag)
 	assert.NilError(t, err)
 	// start the daemon with the plugin and load busybox, --net=none build fails otherwise
 	// because it needs to pull busybox
@@ -57,9 +56,9 @@ func TestAuthZPluginV2AllowNonVolumeRequest(t *testing.T) {
 	d.LoadBusybox(t)
 
 	// Ensure docker run command and accompanying docker ps are successful
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, c)
 
-	_, err = client.ContainerInspect(ctx, cID)
+	_, err = c.ContainerInspect(ctx, cID)
 	assert.NilError(t, err)
 }
 
@@ -67,26 +66,25 @@ func TestAuthZPluginV2Disable(t *testing.T) {
 	skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
 	defer setupTestV2(t)()
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
 	// Install authz plugin
-	err = pluginInstallGrantAllPermissions(client, authzPluginNameWithTag)
+	err := pluginInstallGrantAllPermissions(c, authzPluginNameWithTag)
 	assert.NilError(t, err)
 
 	d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag)
 	d.LoadBusybox(t)
 
-	_, err = client.VolumeCreate(context.Background(), volumetypes.VolumeCreateBody{Driver: "local"})
+	_, err = c.VolumeCreate(context.Background(), volumetypes.VolumeCreateBody{Driver: "local"})
 	assert.Assert(t, err != nil)
 	assert.Assert(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
 
 	// disable the plugin
-	err = client.PluginDisable(context.Background(), authzPluginNameWithTag, types.PluginDisableOptions{})
+	err = c.PluginDisable(context.Background(), authzPluginNameWithTag, types.PluginDisableOptions{})
 	assert.NilError(t, err)
 
 	// now test to see if the docker api works.
-	_, err = client.VolumeCreate(context.Background(), volumetypes.VolumeCreateBody{Driver: "local"})
+	_, err = c.VolumeCreate(context.Background(), volumetypes.VolumeCreateBody{Driver: "local"})
 	assert.NilError(t, err)
 }
 
@@ -94,34 +92,33 @@ func TestAuthZPluginV2RejectVolumeRequests(t *testing.T) {
 	skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
 	defer setupTestV2(t)()
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
 	// Install authz plugin
-	err = pluginInstallGrantAllPermissions(client, authzPluginNameWithTag)
+	err := pluginInstallGrantAllPermissions(c, authzPluginNameWithTag)
 	assert.NilError(t, err)
 
 	// restart the daemon with the plugin
 	d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag)
 
-	_, err = client.VolumeCreate(context.Background(), volumetypes.VolumeCreateBody{Driver: "local"})
+	_, err = c.VolumeCreate(context.Background(), volumetypes.VolumeCreateBody{Driver: "local"})
 	assert.Assert(t, err != nil)
 	assert.Assert(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
 
-	_, err = client.VolumeList(context.Background(), filters.Args{})
+	_, err = c.VolumeList(context.Background(), filters.Args{})
 	assert.Assert(t, err != nil)
 	assert.Assert(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
 
 	// The plugin will block the command before it can determine the volume does not exist
-	err = client.VolumeRemove(context.Background(), "test", false)
+	err = c.VolumeRemove(context.Background(), "test", false)
 	assert.Assert(t, err != nil)
 	assert.Assert(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
 
-	_, err = client.VolumeInspect(context.Background(), "test")
+	_, err = c.VolumeInspect(context.Background(), "test")
 	assert.Assert(t, err != nil)
 	assert.Assert(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
 
-	_, err = client.VolumesPrune(context.Background(), filters.Args{})
+	_, err = c.VolumesPrune(context.Background(), filters.Args{})
 	assert.Assert(t, err != nil)
 	assert.Assert(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
 }
@@ -130,11 +127,10 @@ func TestAuthZPluginV2BadManifestFailsDaemonStart(t *testing.T) {
 	skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
 	defer setupTestV2(t)()
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
 
 	// Install authz plugin with bad manifest
-	err = pluginInstallGrantAllPermissions(client, authzPluginBadManifestName)
+	err := pluginInstallGrantAllPermissions(c, authzPluginBadManifestName)
 	assert.NilError(t, err)
 
 	// start the daemon with the plugin, it will error
@@ -170,6 +166,6 @@ func pluginInstallGrantAllPermissions(client client.APIClient, name string) erro
 	// we have to read the response out here because the client API
 	// actually starts a goroutine which we can only be sure has
 	// completed when we get EOF from reading responseBody
-	_, err = ioutil.ReadAll(responseReader)
+	_, err = io.ReadAll(responseReader)
 	return err
 }
diff --git a/integration/plugin/authz/main_test.go b/integration/plugin/authz/main_test.go
index 96c5901073f0f..b4e65640819be 100644
--- a/integration/plugin/authz/main_test.go
+++ b/integration/plugin/authz/main_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package authz // import "github.com/docker/docker/integration/plugin/authz"
@@ -5,18 +6,18 @@ package authz // import "github.com/docker/docker/integration/plugin/authz"
 import (
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"net/http/httptest"
 	"os"
 	"strings"
 	"testing"
 
-	"github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/environment"
 	"github.com/docker/docker/pkg/authorization"
 	"github.com/docker/docker/pkg/plugins"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/environment"
+	"gotest.tools/v3/skip"
 )
 
 var (
@@ -49,9 +50,10 @@ func TestMain(m *testing.M) {
 func setupTest(t *testing.T) func() {
 	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of localhost")
 	environment.ProtectAll(t, testEnv)
 
-	d = daemon.New(t, daemon.WithExperimental)
+	d = daemon.New(t, daemon.WithExperimental())
 
 	return func() {
 		if d != nil {
@@ -75,7 +77,7 @@ func setupSuite() {
 
 	mux.HandleFunc("/AuthZPlugin.AuthZReq", func(w http.ResponseWriter, r *http.Request) {
 		defer r.Body.Close()
-		body, err := ioutil.ReadAll(r.Body)
+		body, err := io.ReadAll(r.Body)
 		if err != nil {
 			panic("could not read body for /AuthZPlugin.AuthZReq: " + err.Error())
 		}
@@ -113,7 +115,7 @@ func setupSuite() {
 
 	mux.HandleFunc("/AuthZPlugin.AuthZRes", func(w http.ResponseWriter, r *http.Request) {
 		defer r.Body.Close()
-		body, err := ioutil.ReadAll(r.Body)
+		body, err := io.ReadAll(r.Body)
 		if err != nil {
 			panic("could not read body for /AuthZPlugin.AuthZRes: " + err.Error())
 		}
diff --git a/integration/plugin/common/main_test.go b/integration/plugin/common/main_test.go
new file mode 100644
index 0000000000000..cd42c8f76153e
--- /dev/null
+++ b/integration/plugin/common/main_test.go
@@ -0,0 +1,31 @@
+package common // import "github.com/docker/docker/integration/plugin/common"
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/docker/docker/pkg/reexec"
+	"github.com/docker/docker/testutil/environment"
+)
+
+var testEnv *environment.Execution
+
+func TestMain(m *testing.M) {
+	if reexec.Init() {
+		return
+	}
+	var err error
+	testEnv, err = environment.New()
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+	testEnv.Print()
+	os.Exit(m.Run())
+}
+
+func setupTest(t *testing.T) func() {
+	environment.ProtectAll(t, testEnv)
+	return func() { testEnv.Clean(t) }
+}
diff --git a/integration/plugin/common/plugin_test.go b/integration/plugin/common/plugin_test.go
new file mode 100644
index 0000000000000..fb37a5e174616
--- /dev/null
+++ b/integration/plugin/common/plugin_test.go
@@ -0,0 +1,284 @@
+package common // import "github.com/docker/docker/integration/plugin/common"
+
+import (
+	"context"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net"
+	"net/http"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"github.com/containerd/containerd/images"
+	"github.com/containerd/containerd/remotes/docker"
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/pkg/jsonmessage"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/fixtures/plugin"
+	"github.com/docker/docker/testutil/registry"
+	"github.com/docker/docker/testutil/request"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/assert/cmp"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
+)
+
+func TestPluginInvalidJSON(t *testing.T) {
+	defer setupTest(t)()
+
+	endpoints := []string{"/plugins/foobar/set"}
+
+	for _, ep := range endpoints {
+		ep := ep
+		t.Run(ep, func(t *testing.T) {
+			t.Parallel()
+
+			res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
+			assert.NilError(t, err)
+			assert.Equal(t, res.StatusCode, http.StatusBadRequest)
+
+			buf, err := request.ReadBody(body)
+			assert.NilError(t, err)
+			assert.Check(t, is.Contains(string(buf), "invalid character 'i' looking for beginning of object key string"))
+
+			res, body, err = request.Post(ep, request.JSON)
+			assert.NilError(t, err)
+			assert.Equal(t, res.StatusCode, http.StatusBadRequest)
+
+			buf, err = request.ReadBody(body)
+			assert.NilError(t, err)
+			assert.Check(t, is.Contains(string(buf), "got EOF while reading request body"))
+		})
+	}
+}
+
+func TestPluginInstall(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+	skip.If(t, testEnv.OSType == "windows")
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of localhost")
+
+	ctx := context.Background()
+	client := testEnv.APIClient()
+
+	t.Run("no auth", func(t *testing.T) {
+		defer setupTest(t)()
+
+		reg := registry.NewV2(t)
+		defer reg.Close()
+
+		name := "test-" + strings.ToLower(t.Name())
+		repo := path.Join(registry.DefaultURL, name+":latest")
+		assert.NilError(t, plugin.CreateInRegistry(ctx, repo, nil))
+
+		rdr, err := client.PluginInstall(ctx, repo, types.PluginInstallOptions{Disabled: true, RemoteRef: repo})
+		assert.NilError(t, err)
+		defer rdr.Close()
+
+		_, err = io.Copy(io.Discard, rdr)
+		assert.NilError(t, err)
+
+		_, _, err = client.PluginInspectWithRaw(ctx, repo)
+		assert.NilError(t, err)
+	})
+
+	t.Run("with htpasswd", func(t *testing.T) {
+		defer setupTest(t)()
+
+		reg := registry.NewV2(t, registry.Htpasswd)
+		defer reg.Close()
+
+		name := "test-" + strings.ToLower(t.Name())
+		repo := path.Join(registry.DefaultURL, name+":latest")
+		auth := &types.AuthConfig{ServerAddress: registry.DefaultURL, Username: "testuser", Password: "testpassword"}
+		assert.NilError(t, plugin.CreateInRegistry(ctx, repo, auth))
+
+		authEncoded, err := json.Marshal(auth)
+		assert.NilError(t, err)
+
+		rdr, err := client.PluginInstall(ctx, repo, types.PluginInstallOptions{
+			RegistryAuth: base64.URLEncoding.EncodeToString(authEncoded),
+			Disabled:     true,
+			RemoteRef:    repo,
+		})
+		assert.NilError(t, err)
+		defer rdr.Close()
+
+		_, err = io.Copy(io.Discard, rdr)
+		assert.NilError(t, err)
+
+		_, _, err = client.PluginInspectWithRaw(ctx, repo)
+		assert.NilError(t, err)
+	})
+	t.Run("with insecure", func(t *testing.T) {
+		skip.If(t, !testEnv.IsLocalDaemon())
+
+		addrs, err := net.InterfaceAddrs()
+		assert.NilError(t, err)
+
+		var bindTo string
+		for _, addr := range addrs {
+			ip, ok := addr.(*net.IPNet)
+			if !ok {
+				continue
+			}
+			if ip.IP.IsLoopback() || ip.IP.To4() == nil {
+				continue
+			}
+			bindTo = ip.IP.String()
+		}
+
+		if bindTo == "" {
+			t.Skip("No suitable interface to bind registry to")
+		}
+
+		regURL := bindTo + ":5000"
+
+		d := daemon.New(t)
+		defer d.Stop(t)
+
+		d.Start(t, "--insecure-registry="+regURL)
+		defer d.Stop(t)
+
+		reg := registry.NewV2(t, registry.URL(regURL))
+		defer reg.Close()
+
+		name := "test-" + strings.ToLower(t.Name())
+		repo := path.Join(regURL, name+":latest")
+		assert.NilError(t, plugin.CreateInRegistry(ctx, repo, nil, plugin.WithInsecureRegistry(regURL)))
+
+		client := d.NewClientT(t)
+		rdr, err := client.PluginInstall(ctx, repo, types.PluginInstallOptions{Disabled: true, RemoteRef: repo})
+		assert.NilError(t, err)
+		defer rdr.Close()
+
+		_, err = io.Copy(io.Discard, rdr)
+		assert.NilError(t, err)
+
+		_, _, err = client.PluginInspectWithRaw(ctx, repo)
+		assert.NilError(t, err)
+	})
+	// TODO: test insecure registry with https
+}
+
+func TestPluginsWithRuntimes(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+	skip.If(t, testEnv.IsRootless, "Test not supported on rootless due to buggy daemon setup in rootless mode due to daemon restart")
+	skip.If(t, testEnv.OSType == "windows")
+
+	dir, err := os.MkdirTemp("", t.Name())
+	assert.NilError(t, err)
+	defer os.RemoveAll(dir)
+
+	d := daemon.New(t)
+	defer d.Cleanup(t)
+
+	d.Start(t)
+	defer d.Stop(t)
+
+	ctx := context.Background()
+	client := d.NewClientT(t)
+
+	assert.NilError(t, plugin.Create(ctx, client, "test:latest"))
+	defer client.PluginRemove(ctx, "test:latest", types.PluginRemoveOptions{Force: true})
+
+	assert.NilError(t, client.PluginEnable(ctx, "test:latest", types.PluginEnableOptions{Timeout: 30}))
+
+	p := filepath.Join(dir, "myrt")
+	script := fmt.Sprintf(`#!/bin/sh
+	file="%s/success"
+	if [ "$1" = "someArg" ]; then
+		shift
+		file="${file}_someArg"
+	fi
+
+	touch $file
+	exec runc $@
+	`, dir)
+
+	assert.NilError(t, os.WriteFile(p, []byte(script), 0777))
+
+	type config struct {
+		Runtimes map[string]types.Runtime `json:"runtimes"`
+	}
+
+	cfg, err := json.Marshal(config{
+		Runtimes: map[string]types.Runtime{
+			"myrt":     {Path: p},
+			"myrtArgs": {Path: p, Args: []string{"someArg"}},
+		},
+	})
+	configPath := filepath.Join(dir, "config.json")
+	os.WriteFile(configPath, cfg, 0644)
+
+	t.Run("No Args", func(t *testing.T) {
+		d.Restart(t, "--default-runtime=myrt", "--config-file="+configPath)
+		_, err = os.Stat(filepath.Join(dir, "success"))
+		assert.NilError(t, err)
+	})
+
+	t.Run("With Args", func(t *testing.T) {
+		d.Restart(t, "--default-runtime=myrtArgs", "--config-file="+configPath)
+		_, err = os.Stat(filepath.Join(dir, "success_someArg"))
+		assert.NilError(t, err)
+	})
+}
+
+func TestPluginBackCompatMediaTypes(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+	skip.If(t, testEnv.OSType == "windows")
+	skip.If(t, testEnv.IsRootless, "Rootless has a different view of localhost (needed for test registry access)")
+
+	defer setupTest(t)()
+
+	reg := registry.NewV2(t)
+	defer reg.Close()
+	reg.WaitReady(t)
+
+	repo := path.Join(registry.DefaultURL, strings.ToLower(t.Name())+":latest")
+
+	client := testEnv.APIClient()
+
+	ctx := context.Background()
+	assert.NilError(t, plugin.Create(ctx, client, repo))
+
+	rdr, err := client.PluginPush(ctx, repo, "")
+	assert.NilError(t, err)
+	defer rdr.Close()
+
+	buf := &strings.Builder{}
+	assert.NilError(t, jsonmessage.DisplayJSONMessagesStream(rdr, buf, 0, false, nil), buf)
+
+	// Use custom header here because older versions of the registry do not
+	// parse the accept header correctly and does not like the accept header
+	// that the default resolver code uses. "Older registries" here would be
+	// like the one currently included in the test suite.
+	headers := http.Header{}
+	headers.Add("Accept", images.MediaTypeDockerSchema2Manifest)
+
+	resolver := docker.NewResolver(docker.ResolverOptions{
+		Headers: headers,
+	})
+	assert.NilError(t, err)
+
+	n, desc, err := resolver.Resolve(ctx, repo)
+	assert.NilError(t, err, repo)
+
+	fetcher, err := resolver.Fetcher(ctx, n)
+	assert.NilError(t, err)
+
+	rdr, err = fetcher.Fetch(ctx, desc)
+	assert.NilError(t, err)
+	defer rdr.Close()
+
+	var m v1.Manifest
+	assert.NilError(t, json.NewDecoder(rdr).Decode(&m))
+	assert.Check(t, cmp.Equal(m.MediaType, images.MediaTypeDockerSchema2Manifest))
+	assert.Check(t, cmp.Len(m.Layers, 1))
+	assert.Check(t, cmp.Equal(m.Layers[0].MediaType, images.MediaTypeDockerSchema2LayerGzip))
+}
diff --git a/integration/plugin/graphdriver/external_test.go b/integration/plugin/graphdriver/external_test.go
index 99ce60ceef0e1..57fb27856d0d3 100644
--- a/integration/plugin/graphdriver/external_test.go
+++ b/integration/plugin/graphdriver/external_test.go
@@ -5,7 +5,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net/http"
 	"net/http/httptest"
 	"os"
@@ -19,12 +18,12 @@ import (
 	"github.com/docker/docker/daemon/graphdriver/vfs"
 	"github.com/docker/docker/integration/internal/container"
 	"github.com/docker/docker/integration/internal/requirement"
-	"github.com/docker/docker/internal/test/daemon"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/plugins"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 type graphEventsCounter struct {
@@ -48,13 +47,14 @@ func TestExternalGraphDriver(t *testing.T) {
 	skip.If(t, runtime.GOOS == "windows")
 	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
 	skip.If(t, !requirement.HasHubConnectivity(t))
+	skip.If(t, testEnv.IsRootless, "rootless mode doesn't support external graph driver")
 
 	// Setup plugin(s)
 	ec := make(map[string]*graphEventsCounter)
 	sserver := setupPluginViaSpecFile(t, ec)
 	jserver := setupPluginViaJSONFile(t, ec)
 	// Create daemon
-	d := daemon.New(t, daemon.WithExperimental)
+	d := daemon.New(t, daemon.WithExperimental())
 	c := d.NewClientT(t)
 
 	for _, tc := range []struct {
@@ -128,7 +128,7 @@ func setupPlugin(t *testing.T, ec map[string]*graphEventsCounter, ext string, mu
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		switch t := data.(type) {
 		case error:
-			fmt.Fprintln(w, fmt.Sprintf(`{"Err": %q}`, t.Error()))
+			fmt.Fprintf(w, "{\"Err\": %q}\n", t.Error())
 		case string:
 			fmt.Fprintln(w, t)
 		default:
@@ -144,7 +144,7 @@ func setupPlugin(t *testing.T, ec map[string]*graphEventsCounter, ext string, mu
 		return nil
 	}
 
-	base, err := ioutil.TempDir("", name)
+	base, err := os.MkdirTemp("", name)
 	assert.NilError(t, err)
 	vfsProto, err := vfs.Init(base, []string{}, nil, nil)
 	assert.NilError(t, err, "error initializing graph driver")
@@ -317,7 +317,7 @@ func setupPlugin(t *testing.T, ec map[string]*graphEventsCounter, ext string, mu
 		parent := r.URL.Query().Get("parent")
 
 		if id == "" {
-			http.Error(w, fmt.Sprintf("missing id"), 409)
+			http.Error(w, "missing id", 409)
 		}
 
 		size, err := driver.ApplyDiff(id, parent, diff)
@@ -348,7 +348,7 @@ func setupPlugin(t *testing.T, ec map[string]*graphEventsCounter, ext string, mu
 	assert.NilError(t, err)
 
 	specFile := "/etc/docker/plugins/" + name + "." + ext
-	err = ioutil.WriteFile(specFile, b, 0644)
+	err = os.WriteFile(specFile, b, 0644)
 	assert.NilError(t, err)
 }
 
@@ -360,7 +360,7 @@ func testExternalGraphDriver(ext string, ec map[string]*graphEventsCounter) func
 
 			ctx := context.Background()
 
-			testGraphDriver(t, c, ctx, driverName, func(t *testing.T) {
+			testGraphDriver(ctx, t, c, driverName, func(t *testing.T) {
 				d.Restart(t, "-s", driverName)
 			})
 
@@ -394,12 +394,12 @@ func testGraphDriverPull(c client.APIClient, d *daemon.Daemon) func(*testing.T)
 		defer d.Stop(t)
 		ctx := context.Background()
 
-		r, err := c.ImagePull(ctx, "busybox:latest@sha256:bbc3a03235220b170ba48a157dd097dd1379299370e1ed99ce976df0355d24f0", types.ImagePullOptions{})
+		r, err := c.ImagePull(ctx, "busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209", types.ImagePullOptions{})
 		assert.NilError(t, err)
-		_, err = io.Copy(ioutil.Discard, r)
+		_, err = io.Copy(io.Discard, r)
 		assert.NilError(t, err)
 
-		container.Run(t, ctx, c, container.WithImage("busybox:latest@sha256:bbc3a03235220b170ba48a157dd097dd1379299370e1ed99ce976df0355d24f0"))
+		container.Run(ctx, t, c, container.WithImage("busybox:latest@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209"))
 	}
 }
 
@@ -410,7 +410,7 @@ func TestGraphdriverPluginV2(t *testing.T) {
 	skip.If(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
 	skip.If(t, !requirement.Overlay2Supported(testEnv.DaemonInfo.KernelVersion))
 
-	d := daemon.New(t, daemon.WithExperimental)
+	d := daemon.New(t, daemon.WithExperimental())
 	d.Start(t)
 	defer d.Stop(t)
 
@@ -424,22 +424,21 @@ func TestGraphdriverPluginV2(t *testing.T) {
 		RemoteRef:            plugin,
 		AcceptAllPermissions: true,
 	})
-	defer responseReader.Close()
 	assert.NilError(t, err)
+	defer responseReader.Close()
 	// ensure it's done by waiting for EOF on the response
-	_, err = io.Copy(ioutil.Discard, responseReader)
+	_, err = io.Copy(io.Discard, responseReader)
 	assert.NilError(t, err)
 
 	// restart the daemon with the plugin set as the storage driver
 	d.Stop(t)
 	d.StartWithBusybox(t, "-s", plugin, "--storage-opt", "overlay2.override_kernel_check=1")
 
-	testGraphDriver(t, client, ctx, plugin, nil)
+	testGraphDriver(ctx, t, client, plugin, nil)
 }
 
-// nolint: golint
-func testGraphDriver(t *testing.T, c client.APIClient, ctx context.Context, driverName string, afterContainerRunFn func(*testing.T)) { //nolint: golint
-	id := container.Run(t, ctx, c, container.WithCmd("sh", "-c", "echo hello > /hello"))
+func testGraphDriver(ctx context.Context, t *testing.T, c client.APIClient, driverName string, afterContainerRunFn func(*testing.T)) {
+	id := container.Run(ctx, t, c, container.WithCmd("sh", "-c", "echo hello > /hello"))
 
 	if afterContainerRunFn != nil {
 		afterContainerRunFn(t)
diff --git a/integration/plugin/graphdriver/main_test.go b/integration/plugin/graphdriver/main_test.go
index 6b6c1a12323aa..68fa02c81e9bf 100644
--- a/integration/plugin/graphdriver/main_test.go
+++ b/integration/plugin/graphdriver/main_test.go
@@ -5,8 +5,8 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
 	"github.com/docker/docker/pkg/reexec"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var (
@@ -17,8 +17,6 @@ func init() {
 	reexec.Init() // This is required for external graphdriver tests
 }
 
-const dockerdBinary = "dockerd"
-
 func TestMain(m *testing.M) {
 	var err error
 	testEnv, err = environment.New()
diff --git a/integration/plugin/logging/cmd/cmd_test.go b/integration/plugin/logging/cmd/cmd_test.go
deleted file mode 100644
index 1d619dd05e2ae..0000000000000
--- a/integration/plugin/logging/cmd/cmd_test.go
+++ /dev/null
@@ -1 +0,0 @@
-package cmd
diff --git a/integration/plugin/logging/cmd/discard/driver.go b/integration/plugin/logging/cmd/discard/driver.go
new file mode 100644
index 0000000000000..bbdebaf443bdc
--- /dev/null
+++ b/integration/plugin/logging/cmd/discard/driver.go
@@ -0,0 +1,85 @@
+package main
+
+import (
+	"encoding/json"
+	"io"
+	"net/http"
+	"os"
+	"sync"
+	"syscall"
+)
+
+type startLoggingRequest struct {
+	File string
+}
+
+type capabilitiesResponse struct {
+	Cap struct {
+		ReadLogs bool
+	}
+}
+
+type driver struct {
+	mu   sync.Mutex
+	logs map[string]io.Closer
+}
+
+type stopLoggingRequest struct {
+	File string
+}
+
+func handle(mux *http.ServeMux) {
+	d := &driver{logs: make(map[string]io.Closer)}
+	mux.HandleFunc("/LogDriver.StartLogging", func(w http.ResponseWriter, r *http.Request) {
+		var req startLoggingRequest
+		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+			http.Error(w, err.Error(), http.StatusBadRequest)
+			return
+		}
+
+		f, err := os.OpenFile(req.File, syscall.O_RDONLY, 0700)
+		if err != nil {
+			respond(err, w)
+		}
+
+		d.mu.Lock()
+		d.logs[req.File] = f
+		d.mu.Unlock()
+
+		go io.Copy(io.Discard, f)
+		respond(err, w)
+	})
+
+	mux.HandleFunc("/LogDriver.StopLogging", func(w http.ResponseWriter, r *http.Request) {
+		var req stopLoggingRequest
+		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+			http.Error(w, err.Error(), http.StatusBadRequest)
+			return
+		}
+
+		d.mu.Lock()
+		if f := d.logs[req.File]; f != nil {
+			f.Close()
+		}
+		d.mu.Unlock()
+		respond(nil, w)
+	})
+
+	mux.HandleFunc("/LogDriver.Capabilities", func(w http.ResponseWriter, r *http.Request) {
+		json.NewEncoder(w).Encode(&capabilitiesResponse{
+			Cap: struct{ ReadLogs bool }{ReadLogs: false},
+		})
+	})
+}
+
+type response struct {
+	Err string
+}
+
+func respond(err error, w io.Writer) {
+	var res response
+	if err != nil {
+		res.Err = err.Error()
+	}
+	json.NewEncoder(w).Encode(&res)
+}
diff --git a/integration/plugin/logging/cmd/discard/main.go b/integration/plugin/logging/cmd/discard/main.go
new file mode 100644
index 0000000000000..15577ca0a9d12
--- /dev/null
+++ b/integration/plugin/logging/cmd/discard/main.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+	"net"
+	"net/http"
+)
+
+func main() {
+	l, err := net.Listen("unix", "/run/docker/plugins/plugin.sock")
+	if err != nil {
+		panic(err)
+	}
+
+	mux := http.NewServeMux()
+	handle(mux)
+
+	server := http.Server{
+		Addr:    l.Addr().String(),
+		Handler: mux,
+	}
+	server.Serve(l)
+}
diff --git a/integration/plugin/logging/cmd/close_on_start/main_test.go b/integration/plugin/logging/cmd/discard/main_test.go
similarity index 100%
rename from integration/plugin/logging/cmd/close_on_start/main_test.go
rename to integration/plugin/logging/cmd/discard/main_test.go
diff --git a/integration/plugin/logging/cmd/dummy/main_test.go b/integration/plugin/logging/cmd/dummy/main_test.go
deleted file mode 100644
index 06ab7d0f9a35a..0000000000000
--- a/integration/plugin/logging/cmd/dummy/main_test.go
+++ /dev/null
@@ -1 +0,0 @@
-package main
diff --git a/integration/plugin/logging/helpers_test.go b/integration/plugin/logging/helpers_test.go
index dbdd36b90512f..7a9f146c9d3f4 100644
--- a/integration/plugin/logging/helpers_test.go
+++ b/integration/plugin/logging/helpers_test.go
@@ -9,8 +9,8 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/internal/test/fixtures/plugin"
-	"github.com/docker/docker/pkg/locker"
+	"github.com/docker/docker/testutil/fixtures/plugin"
+	"github.com/moby/locker"
 	"github.com/pkg/errors"
 )
 
@@ -31,7 +31,7 @@ func ensurePlugin(t *testing.T, name string) string {
 	}
 
 	cmd := exec.Command(goBin, "build", "-o", installPath, "./"+filepath.Join("cmd", name))
-	cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
+	cmd.Env = append(os.Environ(), "CGO_ENABLED=0", "GO111MODULE=off")
 	if out, err := cmd.CombinedOutput(); err != nil {
 		t.Fatal(errors.Wrapf(err, "error building basic plugin bin: %s", string(out)))
 	}
diff --git a/integration/plugin/logging/logging_linux_test.go b/integration/plugin/logging/logging_linux_test.go
index 3921fa6e6985c..2af0237f7cfb5 100644
--- a/integration/plugin/logging/logging_linux_test.go
+++ b/integration/plugin/logging/logging_linux_test.go
@@ -10,13 +10,13 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/daemon"
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
 func TestContinueAfterPluginCrash(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon(), "test requires daemon on the same host")
+	skip.If(t, testEnv.IsRemoteDaemon, "test requires daemon on the same host")
 	t.Parallel()
 
 	d := daemon.New(t)
@@ -33,7 +33,7 @@ func TestContinueAfterPluginCrash(t *testing.T) {
 
 	ctx, cancel = context.WithTimeout(context.Background(), 60*time.Second)
 
-	id := container.Run(t, ctx, client,
+	id := container.Run(ctx, t, client,
 		container.WithAutoRemove,
 		container.WithLogDriver("test"),
 		container.WithCmd(
@@ -45,9 +45,9 @@ func TestContinueAfterPluginCrash(t *testing.T) {
 
 	// Attach to the container to make sure it's written a few times to stdout
 	attach, err := client.ContainerAttach(context.Background(), id, types.ContainerAttachOptions{Stream: true, Stdout: true})
-	assert.Assert(t, err)
+	assert.NilError(t, err)
 
-	chErr := make(chan error)
+	chErr := make(chan error, 1)
 	go func() {
 		defer close(chErr)
 		rdr := bufio.NewReader(attach.Reader)
@@ -62,7 +62,7 @@ func TestContinueAfterPluginCrash(t *testing.T) {
 
 	select {
 	case err := <-chErr:
-		assert.Assert(t, err)
+		assert.NilError(t, err)
 	case <-time.After(60 * time.Second):
 		t.Fatal("timeout waiting for container i/o")
 	}
@@ -71,7 +71,7 @@ func TestContinueAfterPluginCrash(t *testing.T) {
 	// TODO(@cpuguy83): This is horribly hacky but is the only way to really test this case right now.
 	// It would be nice if there was a way to know that a broken pipe has occurred without looking through the logs.
 	log, err := os.Open(d.LogFileName())
-	assert.Assert(t, err)
+	assert.NilError(t, err)
 	scanner := bufio.NewScanner(log)
 	for scanner.Scan() {
 		assert.Assert(t, !strings.Contains(scanner.Text(), "broken pipe"))
diff --git a/integration/plugin/logging/main_test.go b/integration/plugin/logging/main_test.go
index e1292a5718d80..40a41b9b2c0b0 100644
--- a/integration/plugin/logging/main_test.go
+++ b/integration/plugin/logging/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var (
diff --git a/integration/plugin/logging/read_test.go b/integration/plugin/logging/read_test.go
new file mode 100644
index 0000000000000..e84738a72e314
--- /dev/null
+++ b/integration/plugin/logging/read_test.go
@@ -0,0 +1,92 @@
+package logging
+
+import (
+	"bytes"
+	"context"
+	"runtime"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/pkg/stdcopy"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+)
+
+// TestReadPluginNoRead tests that reads are supported even if the plugin isn't capable.
+func TestReadPluginNoRead(t *testing.T) {
+	if runtime.GOOS == "windows" {
+		t.Skip("no unix domain sockets on Windows")
+	}
+	t.Parallel()
+	d := daemon.New(t)
+	d.StartWithBusybox(t, "--iptables=false")
+	defer d.Stop(t)
+
+	client, err := d.NewClient()
+	assert.Assert(t, err)
+	createPlugin(t, client, "test", "discard", asLogDriver)
+
+	ctx := context.Background()
+
+	err = client.PluginEnable(ctx, "test", types.PluginEnableOptions{Timeout: 30})
+	assert.Check(t, err)
+	d.Stop(t)
+
+	cfg := &container.Config{
+		Image: "busybox",
+		Cmd:   []string{"/bin/echo", "hello world"},
+	}
+	for desc, test := range map[string]struct {
+		dOpts         []string
+		logsSupported bool
+	}{
+		"default":                    {logsSupported: true},
+		"disabled caching":           {[]string{"--log-opt=cache-disabled=true"}, false},
+		"explicitly enabled caching": {[]string{"--log-opt=cache-disabled=false"}, true},
+	} {
+		t.Run(desc, func(t *testing.T) {
+			d.Start(t, append([]string{"--iptables=false"}, test.dOpts...)...)
+			defer d.Stop(t)
+			c, err := client.ContainerCreate(ctx,
+				cfg,
+				&container.HostConfig{LogConfig: container.LogConfig{Type: "test"}},
+				nil,
+				nil,
+				"",
+			)
+			assert.Assert(t, err)
+			defer client.ContainerRemove(ctx, c.ID, types.ContainerRemoveOptions{Force: true})
+
+			err = client.ContainerStart(ctx, c.ID, types.ContainerStartOptions{})
+			assert.Assert(t, err)
+
+			logs, err := client.ContainerLogs(ctx, c.ID, types.ContainerLogsOptions{ShowStdout: true})
+			if !test.logsSupported {
+				assert.Assert(t, err != nil)
+				return
+			}
+			assert.Assert(t, err)
+			defer logs.Close()
+
+			buf := bytes.NewBuffer(nil)
+
+			errCh := make(chan error, 1)
+			go func() {
+				_, err := stdcopy.StdCopy(buf, buf, logs)
+				errCh <- err
+			}()
+
+			select {
+			case <-time.After(60 * time.Second):
+				t.Fatal("timeout waiting for IO to complete")
+			case err := <-errCh:
+				assert.Assert(t, err)
+			}
+			assert.Assert(t, strings.TrimSpace(buf.String()) == "hello world", buf.Bytes())
+		})
+	}
+
+}
diff --git a/integration/plugin/logging/validation_test.go b/integration/plugin/logging/validation_test.go
index 200eb98759591..9a0a46350df3d 100644
--- a/integration/plugin/logging/validation_test.go
+++ b/integration/plugin/logging/validation_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package logging
@@ -7,9 +8,9 @@ import (
 	"testing"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/internal/test/daemon"
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
 // Regression test for #35553
@@ -24,14 +25,13 @@ func TestDaemonStartWithLogOpt(t *testing.T) {
 	d.Start(t, "--iptables=false")
 	defer d.Stop(t)
 
-	client, err := d.NewClient()
-	assert.Check(t, err)
+	c := d.NewClientT(t)
 	ctx := context.Background()
 
-	createPlugin(t, client, "test", "dummy", asLogDriver)
-	err = client.PluginEnable(ctx, "test", types.PluginEnableOptions{Timeout: 30})
+	createPlugin(t, c, "test", "dummy", asLogDriver)
+	err := c.PluginEnable(ctx, "test", types.PluginEnableOptions{Timeout: 30})
 	assert.Check(t, err)
-	defer client.PluginRemove(ctx, "test", types.PluginRemoveOptions{Force: true})
+	defer c.PluginRemove(ctx, "test", types.PluginRemoveOptions{Force: true})
 
 	d.Stop(t)
 	d.Start(t, "--iptables=false", "--log-driver=test", "--log-opt=foo=bar")
diff --git a/integration/plugin/volumes/cmd/cmd_test.go b/integration/plugin/volumes/cmd/cmd_test.go
deleted file mode 100644
index 1d619dd05e2ae..0000000000000
--- a/integration/plugin/volumes/cmd/cmd_test.go
+++ /dev/null
@@ -1 +0,0 @@
-package cmd
diff --git a/integration/plugin/volumes/cmd/dummy/main_test.go b/integration/plugin/volumes/cmd/dummy/main_test.go
deleted file mode 100644
index 06ab7d0f9a35a..0000000000000
--- a/integration/plugin/volumes/cmd/dummy/main_test.go
+++ /dev/null
@@ -1 +0,0 @@
-package main
diff --git a/integration/plugin/volumes/helpers_test.go b/integration/plugin/volumes/helpers_test.go
index 36aafd59c2000..96cefead92df0 100644
--- a/integration/plugin/volumes/helpers_test.go
+++ b/integration/plugin/volumes/helpers_test.go
@@ -9,9 +9,10 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/internal/test/fixtures/plugin"
-	"github.com/docker/docker/pkg/locker"
+	"github.com/docker/docker/testutil/fixtures/plugin"
+	"github.com/moby/locker"
 	"github.com/pkg/errors"
+	"gotest.tools/v3/assert"
 )
 
 var pluginBuildLock = locker.New()
@@ -32,12 +33,10 @@ func ensurePlugin(t *testing.T, name string) string {
 	}
 
 	goBin, err := exec.LookPath("go")
-	if err != nil {
-		t.Fatal(err)
-	}
+	assert.NilError(t, err)
 
 	cmd := exec.Command(goBin, "build", "-o", installPath, "./"+filepath.Join("cmd", name))
-	cmd.Env = append(cmd.Env, "CGO_ENABLED=0")
+	cmd.Env = append(os.Environ(), "CGO_ENABLED=0", "GO111MODULE=off")
 	if out, err := cmd.CombinedOutput(); err != nil {
 		t.Fatal(errors.Wrapf(err, "error building basic plugin bin: %s", string(out)))
 	}
@@ -61,9 +60,7 @@ func createPlugin(t *testing.T, client plugin.CreateClient, alias, bin string, o
 	err := plugin.Create(ctx, client, alias, opts...)
 	cancel()
 
-	if err != nil {
-		t.Fatal(err)
-	}
+	assert.NilError(t, err)
 }
 
 func asVolumeDriver(cfg *plugin.Config) {
diff --git a/integration/plugin/volumes/main_test.go b/integration/plugin/volumes/main_test.go
index 5a5678d9c4555..a8ca6a82a18e2 100644
--- a/integration/plugin/volumes/main_test.go
+++ b/integration/plugin/volumes/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var (
@@ -19,9 +19,6 @@ func TestMain(m *testing.M) {
 		fmt.Println(err)
 		os.Exit(1)
 	}
-	if testEnv.OSType != "linux" {
-		os.Exit(0)
-	}
 	err = environment.EnsureFrozenImagesLinux(testEnv)
 	if err != nil {
 		fmt.Println(err)
diff --git a/integration/plugin/volumes/mounts_test.go b/integration/plugin/volumes/mounts_test.go
index fb4b492c3a1f5..c80c4cd79c767 100644
--- a/integration/plugin/volumes/mounts_test.go
+++ b/integration/plugin/volumes/mounts_test.go
@@ -2,15 +2,14 @@ package volumes
 
 import (
 	"context"
-	"io/ioutil"
 	"os"
 	"testing"
 
 	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/fixtures/plugin"
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/fixtures/plugin"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
 // TestPluginWithDevMounts tests very specific regression caused by mounts ordering
@@ -18,21 +17,21 @@ import (
 func TestPluginWithDevMounts(t *testing.T) {
 	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, testEnv.IsRootless)
 	t.Parallel()
 
 	d := daemon.New(t)
 	d.Start(t, "--iptables=false")
 	defer d.Stop(t)
 
-	client, err := d.NewClient()
-	assert.Assert(t, err)
+	c := d.NewClientT(t)
 	ctx := context.Background()
 
-	testDir, err := ioutil.TempDir("", "test-dir")
-	assert.Assert(t, err)
+	testDir, err := os.MkdirTemp("", "test-dir")
+	assert.NilError(t, err)
 	defer os.RemoveAll(testDir)
 
-	createPlugin(t, client, "test", "dummy", asVolumeDriver, func(c *plugin.Config) {
+	createPlugin(t, c, "test", "dummy", asVolumeDriver, func(c *plugin.Config) {
 		root := "/"
 		dev := "/dev"
 		mounts := []types.PluginMount{
@@ -46,14 +45,14 @@ func TestPluginWithDevMounts(t *testing.T) {
 		c.IpcHost = true
 	})
 
-	err = client.PluginEnable(ctx, "test", types.PluginEnableOptions{Timeout: 30})
-	assert.Assert(t, err)
+	err = c.PluginEnable(ctx, "test", types.PluginEnableOptions{Timeout: 30})
+	assert.NilError(t, err)
 	defer func() {
-		err := client.PluginRemove(ctx, "test", types.PluginRemoveOptions{Force: true})
+		err := c.PluginRemove(ctx, "test", types.PluginRemoveOptions{Force: true})
 		assert.Check(t, err)
 	}()
 
-	p, _, err := client.PluginInspectWithRaw(ctx, "test")
-	assert.Assert(t, err)
+	p, _, err := c.PluginInspectWithRaw(ctx, "test")
+	assert.NilError(t, err)
 	assert.Assert(t, p.Enabled)
 }
diff --git a/integration/secret/main_test.go b/integration/secret/main_test.go
index abd4eef9f0bde..a0cc4d889c50e 100644
--- a/integration/secret/main_test.go
+++ b/integration/secret/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
diff --git a/integration/secret/secret_test.go b/integration/secret/secret_test.go
index 565de2ba09bf7..fb307bb60ed26 100644
--- a/integration/secret/secret_test.go
+++ b/integration/secret/secret_test.go
@@ -3,6 +3,7 @@ package secret // import "github.com/docker/docker/integration/secret"
 import (
 	"bytes"
 	"context"
+	"encoding/json"
 	"sort"
 	"testing"
 	"time"
@@ -11,11 +12,13 @@ import (
 	"github.com/docker/docker/api/types/filters"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/client"
+	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/integration/internal/swarm"
 	"github.com/docker/docker/pkg/stdcopy"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestSecretInspect(t *testing.T) {
@@ -24,21 +27,22 @@ func TestSecretInspect(t *testing.T) {
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 
 	ctx := context.Background()
 
-	testName := "test_secret_" + t.Name()
-	secretID := createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
+	testName := t.Name()
+	secretID := createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
 
-	secret, _, err := client.SecretInspectWithRaw(context.Background(), secretID)
+	insp, body, err := c.SecretInspectWithRaw(ctx, secretID)
 	assert.NilError(t, err)
-	assert.Check(t, is.Equal(secret.Spec.Name, testName))
+	assert.Check(t, is.Equal(insp.Spec.Name, testName))
 
-	secret, _, err = client.SecretInspectWithRaw(context.Background(), testName)
+	var secret swarmtypes.Secret
+	err = json.Unmarshal(body, &secret)
 	assert.NilError(t, err)
-	assert.Check(t, is.Equal(secretID, secretID))
+	assert.Check(t, is.DeepEqual(secret, insp))
 }
 
 func TestSecretList(t *testing.T) {
@@ -47,23 +51,27 @@ func TestSecretList(t *testing.T) {
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 	ctx := context.Background()
 
+	configs, err := c.SecretList(ctx, types.SecretListOptions{})
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(len(configs), 0))
+
 	testName0 := "test0_" + t.Name()
 	testName1 := "test1_" + t.Name()
 	testNames := []string{testName0, testName1}
 	sort.Strings(testNames)
 
 	// create secret test0
-	createSecret(ctx, t, client, testName0, []byte("TESTINGDATA0"), map[string]string{"type": "test"})
+	createSecret(ctx, t, c, testName0, []byte("TESTINGDATA0"), map[string]string{"type": "test"})
 
 	// create secret test1
-	secret1ID := createSecret(ctx, t, client, testName1, []byte("TESTINGDATA1"), map[string]string{"type": "production"})
+	secret1ID := createSecret(ctx, t, c, testName1, []byte("TESTINGDATA1"), map[string]string{"type": "production"})
 
 	// test by `secret ls`
-	entries, err := client.SecretList(ctx, types.SecretListOptions{})
+	entries, err := c.SecretList(ctx, types.SecretListOptions{})
 	assert.NilError(t, err)
 	assert.Check(t, is.DeepEqual(secretNamesFromList(entries), testNames))
 
@@ -96,7 +104,7 @@ func TestSecretList(t *testing.T) {
 		},
 	}
 	for _, tc := range testCases {
-		entries, err = client.SecretList(ctx, types.SecretListOptions{
+		entries, err = c.SecretList(ctx, types.SecretListOptions{
 			Filters: tc.filters,
 		})
 		assert.NilError(t, err)
@@ -124,40 +132,41 @@ func TestSecretsCreateAndDelete(t *testing.T) {
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 	ctx := context.Background()
 
 	testName := "test_secret_" + t.Name()
-	secretID := createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
+	secretID := createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
 
-	// create an already existin secret, daemon should return a status code of 409
-	_, err := client.SecretCreate(ctx, swarmtypes.SecretSpec{
+	// create an already existing secret, daemon should return a status code of 409
+	_, err := c.SecretCreate(ctx, swarmtypes.SecretSpec{
 		Annotations: swarmtypes.Annotations{
 			Name: testName,
 		},
 		Data: []byte("TESTINGDATA"),
 	})
-	assert.Check(t, is.ErrorContains(err, "already exists"))
+	assert.Check(t, errdefs.IsConflict(err))
+	assert.Check(t, is.ErrorContains(err, testName))
 
-	// Ported from original TestSecretsDelete
-	err = client.SecretRemove(ctx, secretID)
+	err = c.SecretRemove(ctx, secretID)
 	assert.NilError(t, err)
 
-	_, _, err = client.SecretInspectWithRaw(ctx, secretID)
-	assert.Check(t, is.ErrorContains(err, "No such secret"))
+	_, _, err = c.SecretInspectWithRaw(ctx, secretID)
+	assert.Check(t, errdefs.IsNotFound(err))
+	assert.Check(t, is.ErrorContains(err, secretID))
 
-	err = client.SecretRemove(ctx, "non-existin")
-	assert.Check(t, is.ErrorContains(err, "No such secret: non-existin"))
+	err = c.SecretRemove(ctx, "non-existing")
+	assert.Check(t, errdefs.IsNotFound(err))
+	assert.Check(t, is.ErrorContains(err, "non-existing"))
 
-	// Ported from original TestSecretsCreteaWithLabels
 	testName = "test_secret_with_labels_" + t.Name()
-	secretID = createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), map[string]string{
+	secretID = createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), map[string]string{
 		"key1": "value1",
 		"key2": "value2",
 	})
 
-	insp, _, err := client.SecretInspectWithRaw(ctx, secretID)
+	insp, _, err := c.SecretInspectWithRaw(ctx, secretID)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(insp.Spec.Name, testName))
 	assert.Check(t, is.Equal(len(insp.Spec.Labels), 2))
@@ -171,48 +180,49 @@ func TestSecretsUpdate(t *testing.T) {
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 	ctx := context.Background()
 
 	testName := "test_secret_" + t.Name()
-	secretID := createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), nil)
+	secretID := createSecret(ctx, t, c, testName, []byte("TESTINGDATA"), nil)
 
-	insp, _, err := client.SecretInspectWithRaw(ctx, secretID)
+	insp, _, err := c.SecretInspectWithRaw(ctx, secretID)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(insp.ID, secretID))
 
 	// test UpdateSecret with full ID
 	insp.Spec.Labels = map[string]string{"test": "test1"}
-	err = client.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
+	err = c.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
 	assert.NilError(t, err)
 
-	insp, _, err = client.SecretInspectWithRaw(ctx, secretID)
+	insp, _, err = c.SecretInspectWithRaw(ctx, secretID)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test1"))
 
 	// test UpdateSecret with full name
 	insp.Spec.Labels = map[string]string{"test": "test2"}
-	err = client.SecretUpdate(ctx, testName, insp.Version, insp.Spec)
+	err = c.SecretUpdate(ctx, testName, insp.Version, insp.Spec)
 	assert.NilError(t, err)
 
-	insp, _, err = client.SecretInspectWithRaw(ctx, secretID)
+	insp, _, err = c.SecretInspectWithRaw(ctx, secretID)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test2"))
 
 	// test UpdateSecret with prefix ID
 	insp.Spec.Labels = map[string]string{"test": "test3"}
-	err = client.SecretUpdate(ctx, secretID[:1], insp.Version, insp.Spec)
+	err = c.SecretUpdate(ctx, secretID[:1], insp.Version, insp.Spec)
 	assert.NilError(t, err)
 
-	insp, _, err = client.SecretInspectWithRaw(ctx, secretID)
+	insp, _, err = c.SecretInspectWithRaw(ctx, secretID)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(insp.Spec.Labels["test"], "test3"))
 
 	// test UpdateSecret in updating Data which is not supported in daemon
 	// this test will produce an error in func UpdateSecret
 	insp.Spec.Data = []byte("TESTINGDATA2")
-	err = client.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
+	err = c.SecretUpdate(ctx, secretID, insp.Version, insp.Spec)
+	assert.Check(t, errdefs.IsInvalidParameter(err))
 	assert.Check(t, is.ErrorContains(err, "only updates to Labels are allowed"))
 }
 
@@ -220,8 +230,8 @@ func TestTemplatedSecret(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 	ctx := context.Background()
 
 	referencedSecretName := "referencedsecret_" + t.Name()
@@ -231,7 +241,7 @@ func TestTemplatedSecret(t *testing.T) {
 		},
 		Data: []byte("this is a secret"),
 	}
-	referencedSecret, err := client.SecretCreate(ctx, referencedSecretSpec)
+	referencedSecret, err := c.SecretCreate(ctx, referencedSecretSpec)
 	assert.Check(t, err)
 
 	referencedConfigName := "referencedconfig_" + t.Name()
@@ -241,7 +251,7 @@ func TestTemplatedSecret(t *testing.T) {
 		},
 		Data: []byte("this is a config"),
 	}
-	referencedConfig, err := client.ConfigCreate(ctx, referencedConfigSpec)
+	referencedConfig, err := c.ConfigCreate(ctx, referencedConfigSpec)
 	assert.Check(t, err)
 
 	templatedSecretName := "templated_secret_" + t.Name()
@@ -257,7 +267,7 @@ func TestTemplatedSecret(t *testing.T) {
 			"{{config \"referencedconfigtarget\"}}\n"),
 	}
 
-	templatedSecret, err := client.SecretCreate(ctx, secretSpec)
+	templatedSecret, err := c.SecretCreate(ctx, secretSpec)
 	assert.Check(t, err)
 
 	serviceName := "svc_" + t.Name()
@@ -301,21 +311,12 @@ func TestTemplatedSecret(t *testing.T) {
 		swarm.ServiceWithName(serviceName),
 	)
 
-	var tasks []swarmtypes.Task
-	waitAndAssert(t, 60*time.Second, func(t *testing.T) bool {
-		tasks = swarm.GetRunningTasks(t, d, serviceID)
-		return len(tasks) > 0
-	})
+	poll.WaitOn(t, swarm.RunningTasksCount(c, serviceID, 1), swarm.ServicePoll, poll.WithTimeout(1*time.Minute))
 
-	task := tasks[0]
-	waitAndAssert(t, 60*time.Second, func(t *testing.T) bool {
-		if task.NodeID == "" || (task.Status.ContainerStatus == nil || task.Status.ContainerStatus.ContainerID == "") {
-			task, _, _ = client.TaskInspectWithRaw(context.Background(), task.ID)
-		}
-		return task.NodeID != "" && task.Status.ContainerStatus != nil && task.Status.ContainerStatus.ContainerID != ""
-	})
+	tasks := swarm.GetRunningTasks(t, c, serviceID)
+	assert.Assert(t, len(tasks) > 0, "no running tasks found for service %s", serviceID)
 
-	attach := swarm.ExecTask(t, d, task, types.ExecConfig{
+	attach := swarm.ExecTask(t, d, tasks[0], types.ExecConfig{
 		Cmd:          []string{"/bin/cat", "/run/secrets/templated_secret"},
 		AttachStdout: true,
 		AttachStderr: true,
@@ -326,7 +327,7 @@ func TestTemplatedSecret(t *testing.T) {
 		"this is a config\n"
 	assertAttachedStream(t, attach, expect)
 
-	attach = swarm.ExecTask(t, d, task, types.ExecConfig{
+	attach = swarm.ExecTask(t, d, tasks[0], types.ExecConfig{
 		Cmd:          []string{"mount"},
 		AttachStdout: true,
 		AttachStderr: true,
@@ -341,27 +342,27 @@ func TestSecretCreateResolve(t *testing.T) {
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
 	defer d.Stop(t)
-	client := d.NewClientT(t)
-	defer client.Close()
+	c := d.NewClientT(t)
+	defer c.Close()
 
 	ctx := context.Background()
 
 	testName := "test_secret_" + t.Name()
-	secretID := createSecret(ctx, t, client, testName, []byte("foo"), nil)
+	secretID := createSecret(ctx, t, c, testName, []byte("foo"), nil)
 
 	fakeName := secretID
-	fakeID := createSecret(ctx, t, client, fakeName, []byte("fake foo"), nil)
+	fakeID := createSecret(ctx, t, c, fakeName, []byte("fake foo"), nil)
 
-	entries, err := client.SecretList(ctx, types.SecretListOptions{})
+	entries, err := c.SecretList(ctx, types.SecretListOptions{})
 	assert.NilError(t, err)
 	assert.Check(t, is.Contains(secretNamesFromList(entries), testName))
 	assert.Check(t, is.Contains(secretNamesFromList(entries), fakeName))
 
-	err = client.SecretRemove(ctx, secretID)
+	err = c.SecretRemove(ctx, secretID)
 	assert.NilError(t, err)
 
 	// Fake one will remain
-	entries, err = client.SecretList(ctx, types.SecretListOptions{})
+	entries, err = c.SecretList(ctx, types.SecretListOptions{})
 	assert.NilError(t, err)
 	assert.Assert(t, is.DeepEqual(secretNamesFromList(entries), []string{fakeName}))
 
@@ -370,16 +371,16 @@ func TestSecretCreateResolve(t *testing.T) {
 	// - Full ID
 	// - Full Name
 	// - Partial ID (prefix)
-	err = client.SecretRemove(ctx, fakeName[:5])
+	err = c.SecretRemove(ctx, fakeName[:5])
 	assert.Assert(t, nil != err)
-	entries, err = client.SecretList(ctx, types.SecretListOptions{})
+	entries, err = c.SecretList(ctx, types.SecretListOptions{})
 	assert.NilError(t, err)
 	assert.Assert(t, is.DeepEqual(secretNamesFromList(entries), []string{fakeName}))
 
 	// Remove based on ID prefix of the fake one should succeed
-	err = client.SecretRemove(ctx, fakeID[:5])
+	err = c.SecretRemove(ctx, fakeID[:5])
 	assert.NilError(t, err)
-	entries, err = client.SecretList(ctx, types.SecretListOptions{})
+	entries, err = c.SecretList(ctx, types.SecretListOptions{})
 	assert.NilError(t, err)
 	assert.Assert(t, is.Equal(0, len(entries)))
 }
@@ -391,22 +392,6 @@ func assertAttachedStream(t *testing.T, attach types.HijackedResponse, expect st
 	assert.Check(t, is.Contains(buf.String(), expect))
 }
 
-func waitAndAssert(t *testing.T, timeout time.Duration, f func(*testing.T) bool) {
-	t.Helper()
-	after := time.After(timeout)
-	for {
-		select {
-		case <-after:
-			t.Fatalf("timed out waiting for condition")
-		default:
-		}
-		if f(t) {
-			return
-		}
-		time.Sleep(100 * time.Millisecond)
-	}
-}
-
 func secretNamesFromList(entries []swarmtypes.Secret) []string {
 	var values []string
 	for _, entry := range entries {
diff --git a/integration/service/create_test.go b/integration/service/create_test.go
index f5a5a6dc566f2..8335f894b8f00 100644
--- a/integration/service/create_test.go
+++ b/integration/service/create_test.go
@@ -3,22 +3,25 @@ package service // import "github.com/docker/docker/integration/service"
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
+	"io"
+	"strings"
 	"testing"
 	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/api/types/strslice"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/versions"
 	"github.com/docker/docker/client"
+	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/integration/internal/network"
 	"github.com/docker/docker/integration/internal/swarm"
-	"github.com/docker/docker/internal/test/daemon"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestServiceCreateInit(t *testing.T) {
@@ -29,10 +32,10 @@ func TestServiceCreateInit(t *testing.T) {
 
 func testServiceCreateInit(daemonEnabled bool) func(t *testing.T) {
 	return func(t *testing.T) {
-		var ops = []func(*daemon.Daemon){}
+		var ops = []daemon.Option{}
 
 		if daemonEnabled {
-			ops = append(ops, daemon.WithInit)
+			ops = append(ops, daemon.WithInit())
 		}
 		d := swarm.NewSwarm(t, testEnv, ops...)
 		defer d.Stop(t)
@@ -43,18 +46,18 @@ func testServiceCreateInit(daemonEnabled bool) func(t *testing.T) {
 		booleanFalse := false
 
 		serviceID := swarm.CreateService(t, d)
-		poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
+		poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
 		i := inspectServiceContainer(t, client, serviceID)
 		// HostConfig.Init == nil means that it delegates to daemon configuration
 		assert.Check(t, i.HostConfig.Init == nil)
 
 		serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanTrue))
-		poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
+		poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
 		i = inspectServiceContainer(t, client, serviceID)
 		assert.Check(t, is.Equal(true, *i.HostConfig.Init))
 
 		serviceID = swarm.CreateService(t, d, swarm.ServiceWithInit(&booleanFalse))
-		poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
+		poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, 1), swarm.ServicePoll)
 		i = inspectServiceContainer(t, client, serviceID)
 		assert.Check(t, is.Equal(false, *i.HostConfig.Init))
 	}
@@ -80,9 +83,10 @@ func TestCreateServiceMultipleTimes(t *testing.T) {
 	defer d.Stop(t)
 	client := d.NewClientT(t)
 	defer client.Close()
+	ctx := context.Background()
 
 	overlayName := "overlay1_" + t.Name()
-	overlayID := network.CreateNoError(t, context.Background(), client, overlayName,
+	overlayID := network.CreateNoError(ctx, t, client, overlayName,
 		network.WithCheckDuplicate(),
 		network.WithDriver("overlay"),
 	)
@@ -97,7 +101,7 @@ func TestCreateServiceMultipleTimes(t *testing.T) {
 	}
 
 	serviceID := swarm.CreateService(t, d, serviceSpec...)
-	poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, instances), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances), swarm.ServicePoll)
 
 	_, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
 	assert.NilError(t, err)
@@ -105,22 +109,86 @@ func TestCreateServiceMultipleTimes(t *testing.T) {
 	err = client.ServiceRemove(context.Background(), serviceID)
 	assert.NilError(t, err)
 
-	poll.WaitOn(t, serviceIsRemoved(client, serviceID), swarm.ServicePoll)
-	poll.WaitOn(t, noTasks(client), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.NoTasksForService(ctx, client, serviceID), swarm.ServicePoll)
 
 	serviceID2 := swarm.CreateService(t, d, serviceSpec...)
-	poll.WaitOn(t, serviceRunningTasksCount(client, serviceID2, instances), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID2, instances), swarm.ServicePoll)
 
 	err = client.ServiceRemove(context.Background(), serviceID2)
 	assert.NilError(t, err)
 
-	poll.WaitOn(t, serviceIsRemoved(client, serviceID2), swarm.ServicePoll)
-	poll.WaitOn(t, noTasks(client), swarm.ServicePoll)
-
-	err = client.NetworkRemove(context.Background(), overlayID)
+	// we can't just wait on no tasks for the service, counter-intuitively.
+	// Tasks may briefly exist but not show up, if they are are in the process
+	// of being deallocated. To avoid this case, we should retry network remove
+	// a few times, to give tasks time to be deallcoated
+	poll.WaitOn(t, swarm.NoTasksForService(ctx, client, serviceID2), swarm.ServicePoll)
+
+	for retry := 0; retry < 5; retry++ {
+		err = client.NetworkRemove(context.Background(), overlayID)
+		// TODO(dperny): using strings.Contains for error checking is awful,
+		// but so is the fact that swarm functions don't return errdefs errors.
+		// I don't have time at this moment to fix the latter, so I guess I'll
+		// go with the former.
+		//
+		// The full error we're looking for is something like this:
+		//
+		// Error response from daemon: rpc error: code = FailedPrecondition desc = network %v is in use by task %v
+		//
+		// The safest way to catch this, I think, will be to match on "is in
+		// use by", as this is an uninterrupted string that best identifies
+		// this error.
+		if err == nil || !strings.Contains(err.Error(), "is in use by") {
+			// if there is no error, or the error isn't this kind of error,
+			// then we'll break the loop body, and either fail the test or
+			// continue.
+			break
+		}
+	}
 	assert.NilError(t, err)
 
-	poll.WaitOn(t, networkIsRemoved(client, overlayID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
+	poll.WaitOn(t, network.IsRemoved(context.Background(), client, overlayID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
+}
+
+func TestCreateServiceConflict(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	c := d.NewClientT(t)
+	defer c.Close()
+	ctx := context.Background()
+
+	serviceName := "TestService_" + t.Name()
+	serviceSpec := []swarm.ServiceSpecOpt{
+		swarm.ServiceWithName(serviceName),
+	}
+
+	swarm.CreateService(t, d, serviceSpec...)
+
+	spec := swarm.CreateServiceSpec(t, serviceSpec...)
+	_, err := c.ServiceCreate(ctx, spec, types.ServiceCreateOptions{})
+	assert.Check(t, errdefs.IsConflict(err))
+	assert.ErrorContains(t, err, "service "+serviceName+" already exists")
+}
+
+func TestCreateServiceMaxReplicas(t *testing.T) {
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	client := d.NewClientT(t)
+	defer client.Close()
+
+	var maxReplicas uint64 = 2
+	serviceSpec := []swarm.ServiceSpecOpt{
+		swarm.ServiceWithReplicas(maxReplicas),
+		swarm.ServiceWithMaxReplicas(maxReplicas),
+	}
+
+	serviceID := swarm.CreateService(t, d, serviceSpec...)
+	poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, maxReplicas), swarm.ServicePoll)
+
+	_, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
+	assert.NilError(t, err)
 }
 
 func TestCreateWithDuplicateNetworkNames(t *testing.T) {
@@ -130,19 +198,14 @@ func TestCreateWithDuplicateNetworkNames(t *testing.T) {
 	defer d.Stop(t)
 	client := d.NewClientT(t)
 	defer client.Close()
+	ctx := context.Background()
 
 	name := "foo_" + t.Name()
-	n1 := network.CreateNoError(t, context.Background(), client, name,
-		network.WithDriver("bridge"),
-	)
-	n2 := network.CreateNoError(t, context.Background(), client, name,
-		network.WithDriver("bridge"),
-	)
+	n1 := network.CreateNoError(ctx, t, client, name, network.WithDriver("bridge"))
+	n2 := network.CreateNoError(ctx, t, client, name, network.WithDriver("bridge"))
 
 	// Duplicates with name but with different driver
-	n3 := network.CreateNoError(t, context.Background(), client, name,
-		network.WithDriver("overlay"),
-	)
+	n3 := network.CreateNoError(ctx, t, client, name, network.WithDriver("overlay"))
 
 	// Create Service with the same name
 	var instances uint64 = 1
@@ -154,18 +217,16 @@ func TestCreateWithDuplicateNetworkNames(t *testing.T) {
 		swarm.ServiceWithNetwork(name),
 	)
 
-	poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, instances), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances), swarm.ServicePoll)
 
-	resp, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
+	resp, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal(n3, resp.Spec.TaskTemplate.Networks[0].Target))
 
-	// Remove Service
-	err = client.ServiceRemove(context.Background(), serviceID)
+	// Remove Service, and wait for its tasks to be removed
+	err = client.ServiceRemove(ctx, serviceID)
 	assert.NilError(t, err)
-
-	// Make sure task has been destroyed.
-	poll.WaitOn(t, serviceIsRemoved(client, serviceID), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.NoTasksForService(ctx, client, serviceID), swarm.ServicePoll)
 
 	// Remove networks
 	err = client.NetworkRemove(context.Background(), n3)
@@ -178,9 +239,9 @@ func TestCreateWithDuplicateNetworkNames(t *testing.T) {
 	assert.NilError(t, err)
 
 	// Make sure networks have been destroyed.
-	poll.WaitOn(t, networkIsRemoved(client, n3), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
-	poll.WaitOn(t, networkIsRemoved(client, n2), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
-	poll.WaitOn(t, networkIsRemoved(client, n1), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
+	poll.WaitOn(t, network.IsRemoved(context.Background(), client, n3), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
+	poll.WaitOn(t, network.IsRemoved(context.Background(), client, n2), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
+	poll.WaitOn(t, network.IsRemoved(context.Background(), client, n1), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second))
 }
 
 func TestCreateServiceSecretFileMode(t *testing.T) {
@@ -206,7 +267,7 @@ func TestCreateServiceSecretFileMode(t *testing.T) {
 	serviceID := swarm.CreateService(t, d,
 		swarm.ServiceWithReplicas(instances),
 		swarm.ServiceWithName(serviceName),
-		swarm.ServiceWithCommand([]string{"/bin/sh", "-c", "ls -l /etc/secret || /bin/top"}),
+		swarm.ServiceWithCommand([]string{"/bin/sh", "-c", "ls -l /etc/secret && sleep inf"}),
 		swarm.ServiceWithSecret(&swarmtypes.SecretReference{
 			File: &swarmtypes.SecretReferenceFileTarget{
 				Name: "/etc/secret",
@@ -219,31 +280,22 @@ func TestCreateServiceSecretFileMode(t *testing.T) {
 		}),
 	)
 
-	poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, instances), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances), swarm.ServicePoll)
 
-	filter := filters.NewArgs()
-	filter.Add("service", serviceID)
-	tasks, err := client.TaskList(ctx, types.TaskListOptions{
-		Filters: filter,
-	})
-	assert.NilError(t, err)
-	assert.Check(t, is.Equal(len(tasks), 1))
-
-	body, err := client.ContainerLogs(ctx, tasks[0].Status.ContainerStatus.ContainerID, types.ContainerLogsOptions{
+	body, err := client.ServiceLogs(ctx, serviceID, types.ContainerLogsOptions{
+		Tail:       "1",
 		ShowStdout: true,
 	})
 	assert.NilError(t, err)
 	defer body.Close()
 
-	content, err := ioutil.ReadAll(body)
+	content, err := io.ReadAll(body)
 	assert.NilError(t, err)
 	assert.Check(t, is.Contains(string(content), "-rwxrwxrwx"))
 
 	err = client.ServiceRemove(ctx, serviceID)
 	assert.NilError(t, err)
-
-	poll.WaitOn(t, serviceIsRemoved(client, serviceID), swarm.ServicePoll)
-	poll.WaitOn(t, noTasks(client), swarm.ServicePoll)
+	poll.WaitOn(t, swarm.NoTasksForService(ctx, client, serviceID), swarm.ServicePoll)
 
 	err = client.SecretRemove(ctx, secretName)
 	assert.NilError(t, err)
@@ -271,7 +323,7 @@ func TestCreateServiceConfigFileMode(t *testing.T) {
 	serviceName := "TestService_" + t.Name()
 	serviceID := swarm.CreateService(t, d,
 		swarm.ServiceWithName(serviceName),
-		swarm.ServiceWithCommand([]string{"/bin/sh", "-c", "ls -l /etc/config || /bin/top"}),
+		swarm.ServiceWithCommand([]string{"/bin/sh", "-c", "ls -l /etc/config && sleep inf"}),
 		swarm.ServiceWithReplicas(instances),
 		swarm.ServiceWithConfig(&swarmtypes.ConfigReference{
 			File: &swarmtypes.ConfigReferenceFileTarget{
@@ -285,31 +337,22 @@ func TestCreateServiceConfigFileMode(t *testing.T) {
 		}),
 	)
 
-	poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, instances))
-
-	filter := filters.NewArgs()
-	filter.Add("service", serviceID)
-	tasks, err := client.TaskList(ctx, types.TaskListOptions{
-		Filters: filter,
-	})
-	assert.NilError(t, err)
-	assert.Check(t, is.Equal(len(tasks), 1))
+	poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances))
 
-	body, err := client.ContainerLogs(ctx, tasks[0].Status.ContainerStatus.ContainerID, types.ContainerLogsOptions{
+	body, err := client.ServiceLogs(ctx, serviceID, types.ContainerLogsOptions{
+		Tail:       "1",
 		ShowStdout: true,
 	})
 	assert.NilError(t, err)
 	defer body.Close()
 
-	content, err := ioutil.ReadAll(body)
+	content, err := io.ReadAll(body)
 	assert.NilError(t, err)
 	assert.Check(t, is.Contains(string(content), "-rwxrwxrwx"))
 
 	err = client.ServiceRemove(ctx, serviceID)
 	assert.NilError(t, err)
-
-	poll.WaitOn(t, serviceIsRemoved(client, serviceID))
-	poll.WaitOn(t, noTasks(client))
+	poll.WaitOn(t, swarm.NoTasksForService(ctx, client, serviceID))
 
 	err = client.ConfigRemove(ctx, configName)
 	assert.NilError(t, err)
@@ -339,8 +382,8 @@ func TestCreateServiceConfigFileMode(t *testing.T) {
 // anything up in the test environment
 func TestCreateServiceSysctls(t *testing.T) {
 	skip.If(
-		t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"),
-		"setting service sysctls is unsupported before api v1.39",
+		t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"),
+		"setting service sysctls is unsupported before api v1.40",
 	)
 
 	defer setupTest(t)()
@@ -366,7 +409,7 @@ func TestCreateServiceSysctls(t *testing.T) {
 		)
 
 		// wait for the service to converge to 1 running task as expected
-		poll.WaitOn(t, serviceRunningTasksCount(client, serviceID, instances))
+		poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances))
 
 		// we're going to check 3 things:
 		//
@@ -410,66 +453,80 @@ func TestCreateServiceSysctls(t *testing.T) {
 	}
 }
 
-func serviceRunningTasksCount(client client.ServiceAPIClient, serviceID string, instances uint64) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		filter := filters.NewArgs()
-		filter.Add("service", serviceID)
-		tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
-			Filters: filter,
-		})
-		switch {
-		case err != nil:
-			return poll.Error(err)
-		case len(tasks) == int(instances):
-			for _, task := range tasks {
-				if task.Status.State != swarmtypes.TaskStateRunning {
-					return poll.Continue("waiting for tasks to enter run state")
-				}
-			}
-			return poll.Success()
-		default:
-			return poll.Continue("task count at %d waiting for %d", len(tasks), instances)
-		}
-	}
-}
+// TestServiceCreateCapabilities tests that a service created with capabilities options in
+// the ContainerSpec correctly applies those options.
+//
+// To test this, we're going to create a service with the capabilities option
+//
+//   []string{"CAP_NET_RAW", "CAP_SYS_CHROOT"}
+//
+// We'll get the service's tasks to get the container ID, and then we'll
+// inspect the container. If the output of the container inspect contains the
+// capabilities option with the correct value, we can assume that the capabilities has been
+// plumbed correctly.
+func TestCreateServiceCapabilities(t *testing.T) {
+	skip.If(
+		t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.41"),
+		"setting service capabilities is unsupported before api v1.41",
+	)
 
-func noTasks(client client.ServiceAPIClient) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		filter := filters.NewArgs()
-		tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
-			Filters: filter,
-		})
-		switch {
-		case err != nil:
-			return poll.Error(err)
-		case len(tasks) == 0:
-			return poll.Success()
-		default:
-			return poll.Continue("task count at %d waiting for 0", len(tasks))
-		}
-	}
-}
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	client := d.NewClientT(t)
+	defer client.Close()
 
-func serviceIsRemoved(client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		filter := filters.NewArgs()
-		filter.Add("service", serviceID)
-		_, err := client.TaskList(context.Background(), types.TaskListOptions{
-			Filters: filter,
-		})
-		if err == nil {
-			return poll.Continue("waiting for service %s to be deleted", serviceID)
-		}
-		return poll.Success()
-	}
-}
+	ctx := context.Background()
 
-func networkIsRemoved(client client.NetworkAPIClient, networkID string) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		_, err := client.NetworkInspect(context.Background(), networkID, types.NetworkInspectOptions{})
-		if err == nil {
-			return poll.Continue("waiting for network %s to be removed", networkID)
-		}
-		return poll.Success()
-	}
+	// store the map we're going to be using everywhere.
+	capAdd := []string{"CAP_SYS_CHROOT"}
+	capDrop := []string{"CAP_NET_RAW"}
+
+	// Create the service with the capabilities options
+	var instances uint64 = 1
+	serviceID := swarm.CreateService(t, d,
+		swarm.ServiceWithCapabilities(capAdd, capDrop),
+	)
+
+	// wait for the service to converge to 1 running task as expected
+	poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances))
+
+	// we're going to check 3 things:
+	//
+	//   1. Does the container, when inspected, have the capabilities option set?
+	//   2. Does the task have the capabilities in the spec?
+	//   3. Does the service have the capabilities in the spec?
+	//
+	// if all 3 of these things are true, we know that the capabilities has been
+	// plumbed correctly through the engine.
+	//
+	// We don't actually have to get inside the container and check its
+	// logs or anything. If we see the capabilities set on the container inspect,
+	// we know that the capabilities is plumbed correctly. everything below that
+	// level has been tested elsewhere.
+
+	// get all of the tasks of the service, so we can get the container
+	filter := filters.NewArgs()
+	filter.Add("service", serviceID)
+	tasks, err := client.TaskList(ctx, types.TaskListOptions{
+		Filters: filter,
+	})
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal(len(tasks), 1))
+
+	// verify that the container has the capabilities option set
+	ctnr, err := client.ContainerInspect(ctx, tasks[0].Status.ContainerStatus.ContainerID)
+	assert.NilError(t, err)
+	assert.DeepEqual(t, ctnr.HostConfig.CapAdd, strslice.StrSlice(capAdd))
+	assert.DeepEqual(t, ctnr.HostConfig.CapDrop, strslice.StrSlice(capDrop))
+
+	// verify that the task has the capabilities option set in the task object
+	assert.DeepEqual(t, tasks[0].Spec.ContainerSpec.CapabilityAdd, capAdd)
+	assert.DeepEqual(t, tasks[0].Spec.ContainerSpec.CapabilityDrop, capDrop)
+
+	// verify that the service also has the capabilities set in the spec.
+	service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
+	assert.NilError(t, err)
+	assert.DeepEqual(t, service.Spec.TaskTemplate.ContainerSpec.CapabilityAdd, capAdd)
+	assert.DeepEqual(t, service.Spec.TaskTemplate.ContainerSpec.CapabilityDrop, capDrop)
 }
diff --git a/integration/service/inspect_test.go b/integration/service/inspect_test.go
index 33e90938bdeac..5d8f12dbc34a3 100644
--- a/integration/service/inspect_test.go
+++ b/integration/service/inspect_test.go
@@ -7,19 +7,17 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
-	"github.com/docker/docker/api/types/filters"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
-	"github.com/docker/docker/client"
 	"github.com/docker/docker/integration/internal/swarm"
 	"github.com/google/go-cmp/cmp"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestInspect(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon())
+	skip.If(t, testEnv.IsRemoteDaemon)
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
 	defer setupTest(t)()
 	d := swarm.NewSwarm(t, testEnv)
@@ -29,7 +27,7 @@ func TestInspect(t *testing.T) {
 
 	var now = time.Now()
 	var instances uint64 = 2
-	serviceSpec := fullSwarmServiceSpec("test-service-inspect", instances)
+	serviceSpec := fullSwarmServiceSpec("test-service-inspect"+t.Name(), instances)
 
 	ctx := context.Background()
 	resp, err := client.ServiceCreate(ctx, serviceSpec, types.ServiceCreateOptions{
@@ -38,7 +36,7 @@ func TestInspect(t *testing.T) {
 	assert.NilError(t, err)
 
 	id := resp.ID
-	poll.WaitOn(t, serviceContainerCount(client, id, instances))
+	poll.WaitOn(t, swarm.RunningTasksCount(client, id, instances))
 
 	service, _, err := client.ServiceInspectWithRaw(ctx, id, types.ServiceInspectOptions{})
 	assert.NilError(t, err)
@@ -90,7 +88,7 @@ func fullSwarmServiceSpec(name string, replicas uint64) swarmtypes.ServiceSpec {
 				Image:           "busybox:latest",
 				Labels:          map[string]string{"container-label": "container-value"},
 				Command:         []string{"/bin/top"},
-				Args:            []string{"-u", "root"},
+				Args:            []string{"-d", "5"},
 				Hostname:        "hostname",
 				Env:             []string{"envvar=envvalue"},
 				Dir:             "/work",
@@ -134,21 +132,3 @@ func fullSwarmServiceSpec(name string, replicas uint64) swarmtypes.ServiceSpec {
 		},
 	}
 }
-
-func serviceContainerCount(client client.ServiceAPIClient, id string, count uint64) func(log poll.LogT) poll.Result {
-	return func(log poll.LogT) poll.Result {
-		filter := filters.NewArgs()
-		filter.Add("service", id)
-		tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
-			Filters: filter,
-		})
-		switch {
-		case err != nil:
-			return poll.Error(err)
-		case len(tasks) == int(count):
-			return poll.Success()
-		default:
-			return poll.Continue("task count at %d waiting for %d", len(tasks), count)
-		}
-	}
-}
diff --git a/integration/service/jobs_test.go b/integration/service/jobs_test.go
new file mode 100644
index 0000000000000..6379b7cad09fb
--- /dev/null
+++ b/integration/service/jobs_test.go
@@ -0,0 +1,143 @@
+package service
+
+import (
+	"context"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	swarmtypes "github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/integration/internal/swarm"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
+)
+
+// The file jobs_test.go contains tests that verify that services which are in
+// the mode ReplicatedJob or GlobalJob.
+
+// TestCreateJob tests that a Service can be created and run with
+// mode ReplicatedJob
+func TestCreateJob(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+
+	defer setupTest(t)
+
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+
+	client := d.NewClientT(t)
+	defer client.Close()
+
+	for _, mode := range []swarmtypes.ServiceMode{
+		{ReplicatedJob: &swarmtypes.ReplicatedJob{}},
+		{GlobalJob: &swarmtypes.GlobalJob{}},
+	} {
+		id := swarm.CreateService(t, d, swarm.ServiceWithMode(mode))
+
+		poll.WaitOn(t, swarm.RunningTasksCount(client, id, 1), swarm.ServicePoll)
+	}
+}
+
+// TestReplicatedJob tests that running a replicated job starts the requisite
+// number of tasks,
+func TestReplicatedJob(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+
+	// we need variables, because the replicas field takes a pointer
+	maxConcurrent := uint64(2)
+	// there is overhead, especially in the test environment, associated with
+	// starting tasks. if total is set too high, then the time needed to
+	// complete the test, even if everything is proceeding ideally, may exceed
+	// the time the test has to execute
+	//
+	// in CI,the test has been seen to time out with as few as 7 completions
+	// after 15 seconds. this means 7 completions ought not be too many.
+	total := uint64(7)
+
+	defer setupTest(t)
+
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+
+	client := d.NewClientT(t)
+	defer client.Close()
+
+	id := swarm.CreateService(t, d,
+		swarm.ServiceWithMode(swarmtypes.ServiceMode{
+			ReplicatedJob: &swarmtypes.ReplicatedJob{
+				MaxConcurrent:    &maxConcurrent,
+				TotalCompletions: &total,
+			},
+		}),
+		// just run a command to execute and exit peacefully.
+		swarm.ServiceWithCommand([]string{"true"}),
+	)
+
+	service, _, err := client.ServiceInspectWithRaw(
+		context.Background(), id, types.ServiceInspectOptions{},
+	)
+	assert.NilError(t, err)
+
+	poll.WaitOn(t, swarm.JobComplete(client, service), swarm.ServicePoll)
+}
+
+// TestUpdateJob tests that a job can be updated, and that it runs with the
+// correct parameters.
+func TestUpdateReplicatedJob(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+
+	defer setupTest(t)()
+
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+
+	client := d.NewClientT(t)
+	defer client.Close()
+
+	// avoid writing "context.Background()" over and over again
+	ctx := context.Background()
+
+	// Create the job service
+	id := swarm.CreateService(t, d,
+		swarm.ServiceWithMode(swarmtypes.ServiceMode{
+			ReplicatedJob: &swarmtypes.ReplicatedJob{
+				// use the default, empty values.
+			},
+		}),
+		// run "true" so the task exits with 0
+		swarm.ServiceWithCommand([]string{"true"}),
+	)
+
+	service, _, err := client.ServiceInspectWithRaw(
+		ctx, id, types.ServiceInspectOptions{},
+	)
+	assert.NilError(t, err)
+
+	// wait for the job to completed
+	poll.WaitOn(t, swarm.JobComplete(client, service), swarm.ServicePoll)
+
+	// update the job.
+	spec := service.Spec
+	spec.TaskTemplate.ForceUpdate++
+
+	_, err = client.ServiceUpdate(
+		ctx, id, service.Version, spec, types.ServiceUpdateOptions{},
+	)
+	assert.NilError(t, err)
+
+	service2, _, err := client.ServiceInspectWithRaw(
+		ctx, id, types.ServiceInspectOptions{},
+	)
+	assert.NilError(t, err)
+
+	// assert that the job iteration has increased
+	assert.Assert(t,
+		service.JobStatus.JobIteration.Index < service2.JobStatus.JobIteration.Index,
+	)
+
+	// now wait for the service to complete a second time.
+	poll.WaitOn(t, swarm.JobComplete(client, service2), swarm.ServicePoll)
+}
diff --git a/integration/service/list_test.go b/integration/service/list_test.go
new file mode 100644
index 0000000000000..5ea5ba01c9383
--- /dev/null
+++ b/integration/service/list_test.go
@@ -0,0 +1,108 @@
+package service // import "github.com/docker/docker/integration/service"
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	swarmtypes "github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/integration/internal/swarm"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
+)
+
+// TestServiceListWithStatuses tests that performing a ServiceList operation
+// correctly uses the Status parameter, and that the resulting response
+// contains correct service statuses.
+//
+// NOTE(dperny): because it's a pain to elicit the behavior of an unconverged
+// service reliably, I'm not testing that an unconverged service returns X
+// running and Y desired tasks. Instead, I'm just going to trust that I can
+// successfully assign a value to another value without screwing it up. The
+// logic for computing service statuses is in swarmkit anyway, not in the
+// engine, and is well-tested there, so this test just needs to make sure that
+// statuses get correctly associated with the right services.
+func TestServiceListWithStatuses(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon)
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	// statuses were added in API version 1.41
+	skip.If(t, versions.LessThan(testEnv.DaemonInfo.ServerVersion, "1.41"))
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	client := d.NewClientT(t)
+	defer client.Close()
+
+	ctx := context.Background()
+
+	serviceCount := 3
+	// create some services.
+	for i := 0; i < serviceCount; i++ {
+		spec := fullSwarmServiceSpec(fmt.Sprintf("test-list-%d", i), uint64(i+1))
+		// for whatever reason, the args "-u root", when included, cause these
+		// tasks to fail and exit. instead, we'll just pass no args, which
+		// works.
+		spec.TaskTemplate.ContainerSpec.Args = []string{}
+		resp, err := client.ServiceCreate(ctx, spec, types.ServiceCreateOptions{
+			QueryRegistry: false,
+		})
+		assert.NilError(t, err)
+		id := resp.ID
+		// we need to wait specifically for the tasks to be running, which the
+		// serviceContainerCount function does not do. instead, we'll use a
+		// bespoke closure right here.
+		poll.WaitOn(t, func(log poll.LogT) poll.Result {
+			filter := filters.NewArgs()
+			filter.Add("service", id)
+			tasks, err := client.TaskList(context.Background(), types.TaskListOptions{
+				Filters: filter,
+			})
+
+			running := 0
+			for _, task := range tasks {
+				if task.Status.State == swarmtypes.TaskStateRunning {
+					running++
+				}
+			}
+
+			switch {
+			case err != nil:
+				return poll.Error(err)
+			case running == i+1:
+				return poll.Success()
+			default:
+				return poll.Continue(
+					"running task count %d (%d total), waiting for %d",
+					running, len(tasks), i+1,
+				)
+			}
+		})
+	}
+
+	// now, let's do the list operation with no status arg set.
+	resp, err := client.ServiceList(ctx, types.ServiceListOptions{})
+	assert.NilError(t, err)
+	assert.Check(t, is.Len(resp, serviceCount))
+	for _, service := range resp {
+		assert.Check(t, is.Nil(service.ServiceStatus))
+	}
+
+	// now try again, but with Status: true. This time, we should have statuses
+	resp, err = client.ServiceList(ctx, types.ServiceListOptions{Status: true})
+	assert.NilError(t, err)
+	assert.Check(t, is.Len(resp, serviceCount))
+	for _, service := range resp {
+		replicas := *service.Spec.Mode.Replicated.Replicas
+
+		assert.Assert(t, service.ServiceStatus != nil)
+		// Use assert.Check to not fail out of the test if this fails
+		assert.Check(t, is.Equal(service.ServiceStatus.DesiredTasks, replicas))
+		assert.Check(t, is.Equal(service.ServiceStatus.RunningTasks, replicas))
+	}
+
+}
diff --git a/integration/service/main_test.go b/integration/service/main_test.go
index 28fd19df4debf..939c5ac897065 100644
--- a/integration/service/main_test.go
+++ b/integration/service/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
diff --git a/integration/service/network_test.go b/integration/service/network_test.go
index 4e8f90fe0ed7a..e0c2103f6064c 100644
--- a/integration/service/network_test.go
+++ b/integration/service/network_test.go
@@ -9,9 +9,9 @@ import (
 	"github.com/docker/docker/integration/internal/container"
 	net "github.com/docker/docker/integration/internal/network"
 	"github.com/docker/docker/integration/internal/swarm"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestDockerNetworkConnectAlias(t *testing.T) {
@@ -24,12 +24,12 @@ func TestDockerNetworkConnectAlias(t *testing.T) {
 	ctx := context.Background()
 
 	name := t.Name() + "test-alias"
-	net.CreateNoError(t, ctx, client, name,
+	net.CreateNoError(ctx, t, client, name,
 		net.WithDriver("overlay"),
 		net.WithAttachable(),
 	)
 
-	cID1 := container.Create(t, ctx, client, func(c *container.TestContainerConfig) {
+	cID1 := container.Create(ctx, t, client, func(c *container.TestContainerConfig) {
 		c.NetworkingConfig = &network.NetworkingConfig{
 			EndpointsConfig: map[string]*network.EndpointSettings{
 				name: {},
@@ -52,7 +52,7 @@ func TestDockerNetworkConnectAlias(t *testing.T) {
 	assert.Check(t, is.Equal(len(ng1.NetworkSettings.Networks[name].Aliases), 2))
 	assert.Check(t, is.Equal(ng1.NetworkSettings.Networks[name].Aliases[0], "aaa"))
 
-	cID2 := container.Create(t, ctx, client, func(c *container.TestContainerConfig) {
+	cID2 := container.Create(ctx, t, client, func(c *container.TestContainerConfig) {
 		c.NetworkingConfig = &network.NetworkingConfig{
 			EndpointsConfig: map[string]*network.EndpointSettings{
 				name: {},
@@ -75,3 +75,43 @@ func TestDockerNetworkConnectAlias(t *testing.T) {
 	assert.Check(t, is.Equal(len(ng2.NetworkSettings.Networks[name].Aliases), 2))
 	assert.Check(t, is.Equal(ng2.NetworkSettings.Networks[name].Aliases[0], "bbb"))
 }
+
+func TestDockerNetworkReConnect(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	client := d.NewClientT(t)
+	defer client.Close()
+	ctx := context.Background()
+
+	name := t.Name() + "dummyNet"
+	net.CreateNoError(ctx, t, client, name,
+		net.WithDriver("overlay"),
+		net.WithAttachable(),
+	)
+
+	c1 := container.Create(ctx, t, client, func(c *container.TestContainerConfig) {
+		c.NetworkingConfig = &network.NetworkingConfig{
+			EndpointsConfig: map[string]*network.EndpointSettings{
+				name: {},
+			},
+		}
+	})
+
+	err := client.NetworkConnect(ctx, name, c1, &network.EndpointSettings{})
+	assert.NilError(t, err)
+
+	err = client.ContainerStart(ctx, c1, types.ContainerStartOptions{})
+	assert.NilError(t, err)
+
+	n1, err := client.ContainerInspect(ctx, c1)
+	assert.NilError(t, err)
+
+	err = client.NetworkConnect(ctx, name, c1, &network.EndpointSettings{})
+	assert.ErrorContains(t, err, "is already attached to network")
+
+	n2, err := client.ContainerInspect(ctx, c1)
+	assert.NilError(t, err)
+	assert.Check(t, is.DeepEqual(n1, n2))
+}
diff --git a/integration/service/plugin_test.go b/integration/service/plugin_test.go
index e476c2f1f4707..33505990caaf0 100644
--- a/integration/service/plugin_test.go
+++ b/integration/service/plugin_test.go
@@ -3,21 +3,22 @@ package service
 import (
 	"context"
 	"io"
-	"io/ioutil"
 	"os"
 	"path"
+	"strings"
 	"testing"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
 	swarmtypes "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm/runtime"
 	"github.com/docker/docker/integration/internal/swarm"
-	"github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/fixtures/plugin"
-	"github.com/docker/docker/internal/test/registry"
-	"gotest.tools/assert"
-	"gotest.tools/poll"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/daemon"
+	"github.com/docker/docker/testutil/fixtures/plugin"
+	"github.com/docker/docker/testutil/registry"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
 )
 
 func TestServicePlugin(t *testing.T) {
@@ -29,9 +30,9 @@ func TestServicePlugin(t *testing.T) {
 	reg := registry.NewV2(t)
 	defer reg.Close()
 
-	repo := path.Join(registry.DefaultURL, "swarm", "test:v1")
-	repo2 := path.Join(registry.DefaultURL, "swarm", "test:v2")
-	name := "test"
+	name := "test-" + strings.ToLower(t.Name())
+	repo := path.Join(registry.DefaultURL, "swarm", name+":v1")
+	repo2 := path.Join(registry.DefaultURL, "swarm", name+":v2")
 
 	d := daemon.New(t)
 	d.StartWithBusybox(t)
@@ -40,7 +41,7 @@ func TestServicePlugin(t *testing.T) {
 	assert.NilError(t, err)
 	r, err := apiclient.PluginPush(context.Background(), repo, "")
 	assert.NilError(t, err)
-	_, err = io.Copy(ioutil.Discard, r)
+	_, err = io.Copy(io.Discard, r)
 	assert.NilError(t, err)
 	err = apiclient.PluginRemove(context.Background(), repo, types.PluginRemoveOptions{})
 	assert.NilError(t, err)
@@ -48,61 +49,79 @@ func TestServicePlugin(t *testing.T) {
 	assert.NilError(t, err)
 	r, err = apiclient.PluginPush(context.Background(), repo2, "")
 	assert.NilError(t, err)
-	_, err = io.Copy(ioutil.Discard, r)
+	_, err = io.Copy(io.Discard, r)
 	assert.NilError(t, err)
 	err = apiclient.PluginRemove(context.Background(), repo2, types.PluginRemoveOptions{})
 	assert.NilError(t, err)
 	d.Stop(t)
 
-	d1 := swarm.NewSwarm(t, testEnv, daemon.WithExperimental)
+	d1 := swarm.NewSwarm(t, testEnv, daemon.WithExperimental())
 	defer d1.Stop(t)
-	d2 := daemon.New(t, daemon.WithExperimental, daemon.WithSwarmPort(daemon.DefaultSwarmPort+1))
+	d2 := daemon.New(t, daemon.WithExperimental(), daemon.WithSwarmPort(daemon.DefaultSwarmPort+1))
 	d2.StartAndSwarmJoin(t, d1, true)
 	defer d2.Stop(t)
-	d3 := daemon.New(t, daemon.WithExperimental, daemon.WithSwarmPort(daemon.DefaultSwarmPort+2))
+	d3 := daemon.New(t, daemon.WithExperimental(), daemon.WithSwarmPort(daemon.DefaultSwarmPort+2))
 	d3.StartAndSwarmJoin(t, d1, false)
 	defer d3.Stop(t)
 
 	id := d1.CreateService(t, makePlugin(repo, name, nil))
-	poll.WaitOn(t, d1.PluginIsRunning(name), swarm.ServicePoll)
-	poll.WaitOn(t, d2.PluginIsRunning(name), swarm.ServicePoll)
-	poll.WaitOn(t, d3.PluginIsRunning(name), swarm.ServicePoll)
+	poll.WaitOn(t, d1.PluginIsRunning(t, name), swarm.ServicePoll)
+	poll.WaitOn(t, d2.PluginIsRunning(t, name), swarm.ServicePoll)
+	poll.WaitOn(t, d3.PluginIsRunning(t, name), swarm.ServicePoll)
 
+	// test that environment variables are passed from plugin service to plugin instance
 	service := d1.GetService(t, id)
+	tasks := d1.GetServiceTasks(t, service.Spec.Annotations.Name, filters.Arg("runtime", "plugin"))
+	if len(tasks) == 0 {
+		t.Log("No tasks found for plugin service")
+		t.Fail()
+	}
+	plugin, _, err := d1.NewClientT(t).PluginInspectWithRaw(context.Background(), name)
+	assert.NilError(t, err, "Error inspecting service plugin")
+	found := false
+	for _, env := range plugin.Settings.Env {
+		assert.Equal(t, strings.HasPrefix(env, "baz"), false, "Environment variable entry %q is invalid and should not be present", "baz")
+		if strings.HasPrefix(env, "foo=") {
+			found = true
+			assert.Equal(t, env, "foo=bar")
+		}
+	}
+	assert.Equal(t, true, found, "Environment variable %q not found in plugin", "foo")
+
 	d1.UpdateService(t, service, makePlugin(repo2, name, nil))
-	poll.WaitOn(t, d1.PluginReferenceIs(name, repo2), swarm.ServicePoll)
-	poll.WaitOn(t, d2.PluginReferenceIs(name, repo2), swarm.ServicePoll)
-	poll.WaitOn(t, d3.PluginReferenceIs(name, repo2), swarm.ServicePoll)
-	poll.WaitOn(t, d1.PluginIsRunning(name), swarm.ServicePoll)
-	poll.WaitOn(t, d2.PluginIsRunning(name), swarm.ServicePoll)
-	poll.WaitOn(t, d3.PluginIsRunning(name), swarm.ServicePoll)
+	poll.WaitOn(t, d1.PluginReferenceIs(t, name, repo2), swarm.ServicePoll)
+	poll.WaitOn(t, d2.PluginReferenceIs(t, name, repo2), swarm.ServicePoll)
+	poll.WaitOn(t, d3.PluginReferenceIs(t, name, repo2), swarm.ServicePoll)
+	poll.WaitOn(t, d1.PluginIsRunning(t, name), swarm.ServicePoll)
+	poll.WaitOn(t, d2.PluginIsRunning(t, name), swarm.ServicePoll)
+	poll.WaitOn(t, d3.PluginIsRunning(t, name), swarm.ServicePoll)
 
 	d1.RemoveService(t, id)
-	poll.WaitOn(t, d1.PluginIsNotPresent(name), swarm.ServicePoll)
-	poll.WaitOn(t, d2.PluginIsNotPresent(name), swarm.ServicePoll)
-	poll.WaitOn(t, d3.PluginIsNotPresent(name), swarm.ServicePoll)
+	poll.WaitOn(t, d1.PluginIsNotPresent(t, name), swarm.ServicePoll)
+	poll.WaitOn(t, d2.PluginIsNotPresent(t, name), swarm.ServicePoll)
+	poll.WaitOn(t, d3.PluginIsNotPresent(t, name), swarm.ServicePoll)
 
 	// constrain to managers only
 	id = d1.CreateService(t, makePlugin(repo, name, []string{"node.role==manager"}))
-	poll.WaitOn(t, d1.PluginIsRunning(name), swarm.ServicePoll)
-	poll.WaitOn(t, d2.PluginIsRunning(name), swarm.ServicePoll)
-	poll.WaitOn(t, d3.PluginIsNotPresent(name), swarm.ServicePoll)
+	poll.WaitOn(t, d1.PluginIsRunning(t, name), swarm.ServicePoll)
+	poll.WaitOn(t, d2.PluginIsRunning(t, name), swarm.ServicePoll)
+	poll.WaitOn(t, d3.PluginIsNotPresent(t, name), swarm.ServicePoll)
 
 	d1.RemoveService(t, id)
-	poll.WaitOn(t, d1.PluginIsNotPresent(name), swarm.ServicePoll)
-	poll.WaitOn(t, d2.PluginIsNotPresent(name), swarm.ServicePoll)
-	poll.WaitOn(t, d3.PluginIsNotPresent(name), swarm.ServicePoll)
+	poll.WaitOn(t, d1.PluginIsNotPresent(t, name), swarm.ServicePoll)
+	poll.WaitOn(t, d2.PluginIsNotPresent(t, name), swarm.ServicePoll)
+	poll.WaitOn(t, d3.PluginIsNotPresent(t, name), swarm.ServicePoll)
 
 	// with no name
 	id = d1.CreateService(t, makePlugin(repo, "", nil))
-	poll.WaitOn(t, d1.PluginIsRunning(repo), swarm.ServicePoll)
-	poll.WaitOn(t, d2.PluginIsRunning(repo), swarm.ServicePoll)
-	poll.WaitOn(t, d3.PluginIsRunning(repo), swarm.ServicePoll)
+	poll.WaitOn(t, d1.PluginIsRunning(t, repo), swarm.ServicePoll)
+	poll.WaitOn(t, d2.PluginIsRunning(t, repo), swarm.ServicePoll)
+	poll.WaitOn(t, d3.PluginIsRunning(t, repo), swarm.ServicePoll)
 
 	d1.RemoveService(t, id)
-	poll.WaitOn(t, d1.PluginIsNotPresent(repo), swarm.ServicePoll)
-	poll.WaitOn(t, d2.PluginIsNotPresent(repo), swarm.ServicePoll)
-	poll.WaitOn(t, d3.PluginIsNotPresent(repo), swarm.ServicePoll)
+	poll.WaitOn(t, d1.PluginIsNotPresent(t, repo), swarm.ServicePoll)
+	poll.WaitOn(t, d2.PluginIsNotPresent(t, repo), swarm.ServicePoll)
+	poll.WaitOn(t, d3.PluginIsNotPresent(t, repo), swarm.ServicePoll)
 }
 
 func makePlugin(repo, name string, constraints []string) func(*swarmtypes.Service) {
@@ -111,6 +130,10 @@ func makePlugin(repo, name string, constraints []string) func(*swarmtypes.Servic
 		s.Spec.TaskTemplate.PluginSpec = &runtime.PluginSpec{
 			Name:   name,
 			Remote: repo,
+			Env: []string{
+				"baz",     // invalid environment variable entries are ignored
+				"foo=bar", // "foo" will be the single environment variable
+			},
 		}
 		if constraints != nil {
 			s.Spec.TaskTemplate.Placement = &swarmtypes.Placement{
diff --git a/integration/service/update_test.go b/integration/service/update_test.go
new file mode 100644
index 0000000000000..db12ec210de3d
--- /dev/null
+++ b/integration/service/update_test.go
@@ -0,0 +1,380 @@
+package service // import "github.com/docker/docker/integration/service"
+
+import (
+	"context"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	swarmtypes "github.com/docker/docker/api/types/swarm"
+	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/client"
+	"github.com/docker/docker/integration/internal/network"
+	"github.com/docker/docker/integration/internal/swarm"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/poll"
+	"gotest.tools/v3/skip"
+)
+
+func TestServiceUpdateLabel(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	cli := d.NewClientT(t)
+	defer cli.Close()
+
+	ctx := context.Background()
+	serviceName := "TestService_" + t.Name()
+	serviceID := swarm.CreateService(t, d, swarm.ServiceWithName(serviceName))
+	service := getService(t, cli, serviceID)
+	assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{}))
+
+	// add label to empty set
+	service.Spec.Labels["foo"] = "bar"
+	_, err := cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(t, err)
+	poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll)
+	service = getService(t, cli, serviceID)
+	assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
+
+	// add label to non-empty set
+	service.Spec.Labels["foo2"] = "bar"
+	_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(t, err)
+	poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll)
+	service = getService(t, cli, serviceID)
+	assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar", "foo2": "bar"}))
+
+	delete(service.Spec.Labels, "foo2")
+	_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(t, err)
+	poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll)
+	service = getService(t, cli, serviceID)
+	assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
+
+	delete(service.Spec.Labels, "foo")
+	_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(t, err)
+	poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll)
+	service = getService(t, cli, serviceID)
+	assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{}))
+
+	// now make sure we can add again
+	service.Spec.Labels["foo"] = "bar"
+	_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(t, err)
+	poll.WaitOn(t, serviceSpecIsUpdated(cli, serviceID, service.Version.Index), swarm.ServicePoll)
+	service = getService(t, cli, serviceID)
+	assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
+
+	err = cli.ServiceRemove(context.Background(), serviceID)
+	assert.NilError(t, err)
+}
+
+func TestServiceUpdateSecrets(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	cli := d.NewClientT(t)
+	defer cli.Close()
+
+	ctx := context.Background()
+	secretName := "TestSecret_" + t.Name()
+	secretTarget := "targetName"
+	resp, err := cli.SecretCreate(ctx, swarmtypes.SecretSpec{
+		Annotations: swarmtypes.Annotations{
+			Name: secretName,
+		},
+		Data: []byte("TESTINGDATA"),
+	})
+	assert.NilError(t, err)
+	assert.Check(t, resp.ID != "")
+
+	serviceName := "TestService_" + t.Name()
+	serviceID := swarm.CreateService(t, d, swarm.ServiceWithName(serviceName))
+	service := getService(t, cli, serviceID)
+
+	// add secret
+	service.Spec.TaskTemplate.ContainerSpec.Secrets = append(service.Spec.TaskTemplate.ContainerSpec.Secrets,
+		&swarmtypes.SecretReference{
+			File: &swarmtypes.SecretReferenceFileTarget{
+				Name: secretTarget,
+				UID:  "0",
+				GID:  "0",
+				Mode: 0600,
+			},
+			SecretID:   resp.ID,
+			SecretName: secretName,
+		},
+	)
+	_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(t, err)
+	poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
+
+	service = getService(t, cli, serviceID)
+	secrets := service.Spec.TaskTemplate.ContainerSpec.Secrets
+	assert.Assert(t, is.Equal(1, len(secrets)))
+
+	secret := *secrets[0]
+	assert.Check(t, is.Equal(secretName, secret.SecretName))
+	assert.Check(t, nil != secret.File)
+	assert.Check(t, is.Equal(secretTarget, secret.File.Name))
+
+	// remove
+	service.Spec.TaskTemplate.ContainerSpec.Secrets = []*swarmtypes.SecretReference{}
+	_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(t, err)
+	poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
+	service = getService(t, cli, serviceID)
+	assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Secrets)))
+
+	err = cli.ServiceRemove(context.Background(), serviceID)
+	assert.NilError(t, err)
+}
+
+func TestServiceUpdateConfigs(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	cli := d.NewClientT(t)
+	defer cli.Close()
+
+	ctx := context.Background()
+	configName := "TestConfig_" + t.Name()
+	configTarget := "targetName"
+	resp, err := cli.ConfigCreate(ctx, swarmtypes.ConfigSpec{
+		Annotations: swarmtypes.Annotations{
+			Name: configName,
+		},
+		Data: []byte("TESTINGDATA"),
+	})
+	assert.NilError(t, err)
+	assert.Check(t, resp.ID != "")
+
+	serviceName := "TestService_" + t.Name()
+	serviceID := swarm.CreateService(t, d, swarm.ServiceWithName(serviceName))
+	service := getService(t, cli, serviceID)
+
+	// add config
+	service.Spec.TaskTemplate.ContainerSpec.Configs = append(service.Spec.TaskTemplate.ContainerSpec.Configs,
+		&swarmtypes.ConfigReference{
+			File: &swarmtypes.ConfigReferenceFileTarget{
+				Name: configTarget,
+				UID:  "0",
+				GID:  "0",
+				Mode: 0600,
+			},
+			ConfigID:   resp.ID,
+			ConfigName: configName,
+		},
+	)
+	_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(t, err)
+	poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
+
+	service = getService(t, cli, serviceID)
+	configs := service.Spec.TaskTemplate.ContainerSpec.Configs
+	assert.Assert(t, is.Equal(1, len(configs)))
+
+	config := *configs[0]
+	assert.Check(t, is.Equal(configName, config.ConfigName))
+	assert.Check(t, nil != config.File)
+	assert.Check(t, is.Equal(configTarget, config.File.Name))
+
+	// remove
+	service.Spec.TaskTemplate.ContainerSpec.Configs = []*swarmtypes.ConfigReference{}
+	_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(t, err)
+	poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
+	service = getService(t, cli, serviceID)
+	assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Configs)))
+
+	err = cli.ServiceRemove(context.Background(), serviceID)
+	assert.NilError(t, err)
+}
+
+func TestServiceUpdateNetwork(t *testing.T) {
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	cli := d.NewClientT(t)
+	defer cli.Close()
+
+	ctx := context.Background()
+
+	// Create a overlay network
+	testNet := "testNet" + t.Name()
+	overlayID := network.CreateNoError(ctx, t, cli, testNet,
+		network.WithDriver("overlay"))
+
+	var instances uint64 = 1
+	// Create service with the overlay network
+	serviceName := "TestServiceUpdateNetworkRM_" + t.Name()
+	serviceID := swarm.CreateService(t, d,
+		swarm.ServiceWithReplicas(instances),
+		swarm.ServiceWithName(serviceName),
+		swarm.ServiceWithNetwork(testNet))
+
+	poll.WaitOn(t, swarm.RunningTasksCount(cli, serviceID, instances), swarm.ServicePoll)
+	service := getService(t, cli, serviceID)
+	netInfo, err := cli.NetworkInspect(ctx, testNet, types.NetworkInspectOptions{
+		Verbose: true,
+		Scope:   "swarm",
+	})
+	assert.NilError(t, err)
+	assert.Assert(t, len(netInfo.Containers) == 2, "Expected 2 endpoints, one for container and one for LB Sandbox")
+
+	// Remove network from service
+	service.Spec.TaskTemplate.Networks = []swarmtypes.NetworkAttachmentConfig{}
+	_, err = cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+	assert.NilError(t, err)
+	poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
+
+	netInfo, err = cli.NetworkInspect(ctx, testNet, types.NetworkInspectOptions{
+		Verbose: true,
+		Scope:   "swarm",
+	})
+
+	assert.NilError(t, err)
+	assert.Assert(t, len(netInfo.Containers) == 0, "Load balancing endpoint still exists in network")
+
+	err = cli.NetworkRemove(ctx, overlayID)
+	assert.NilError(t, err)
+
+	err = cli.ServiceRemove(ctx, serviceID)
+	assert.NilError(t, err)
+}
+
+// TestServiceUpdatePidsLimit tests creating and updating a service with PidsLimit
+func TestServiceUpdatePidsLimit(t *testing.T) {
+	skip.If(
+		t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.41"),
+		"setting pidslimit for services is not supported before api v1.41",
+	)
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
+	tests := []struct {
+		name      string
+		pidsLimit int64
+		expected  int64
+	}{
+		{
+			name:      "create service with PidsLimit 300",
+			pidsLimit: 300,
+			expected:  300,
+		},
+		{
+			name:      "unset PidsLimit to 0",
+			pidsLimit: 0,
+			expected:  0,
+		},
+		{
+			name:      "update PidsLimit to 100",
+			pidsLimit: 100,
+			expected:  100,
+		},
+	}
+
+	defer setupTest(t)()
+	d := swarm.NewSwarm(t, testEnv)
+	defer d.Stop(t)
+	cli := d.NewClientT(t)
+	defer func() { _ = cli.Close() }()
+	ctx := context.Background()
+	var (
+		serviceID string
+		service   swarmtypes.Service
+	)
+	for i, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			if i == 0 {
+				serviceID = swarm.CreateService(t, d, swarm.ServiceWithPidsLimit(tc.pidsLimit))
+			} else {
+				service = getService(t, cli, serviceID)
+				if service.Spec.TaskTemplate.Resources == nil {
+					service.Spec.TaskTemplate.Resources = &swarmtypes.ResourceRequirements{}
+				}
+				if service.Spec.TaskTemplate.Resources.Limits == nil {
+					service.Spec.TaskTemplate.Resources.Limits = &swarmtypes.Limit{}
+				}
+				service.Spec.TaskTemplate.Resources.Limits.Pids = tc.pidsLimit
+				_, err := cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
+				assert.NilError(t, err)
+				poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
+			}
+
+			poll.WaitOn(t, swarm.RunningTasksCount(cli, serviceID, 1), swarm.ServicePoll)
+			service = getService(t, cli, serviceID)
+			container := getServiceTaskContainer(ctx, t, cli, serviceID)
+			assert.Equal(t, service.Spec.TaskTemplate.Resources.Limits.Pids, tc.expected)
+			if tc.expected == 0 {
+				if container.HostConfig.Resources.PidsLimit != nil {
+					t.Fatalf("Expected container.HostConfig.Resources.PidsLimit to be nil")
+				}
+			} else {
+				assert.Assert(t, container.HostConfig.Resources.PidsLimit != nil)
+				assert.Equal(t, *container.HostConfig.Resources.PidsLimit, tc.expected)
+			}
+		})
+	}
+
+	err := cli.ServiceRemove(ctx, serviceID)
+	assert.NilError(t, err)
+}
+
+func getServiceTaskContainer(ctx context.Context, t *testing.T, cli client.APIClient, serviceID string) types.ContainerJSON {
+	t.Helper()
+	filter := filters.NewArgs()
+	filter.Add("service", serviceID)
+	filter.Add("desired-state", "running")
+	tasks, err := cli.TaskList(ctx, types.TaskListOptions{Filters: filter})
+	assert.NilError(t, err)
+	assert.Assert(t, len(tasks) > 0)
+
+	ctr, err := cli.ContainerInspect(ctx, tasks[0].Status.ContainerStatus.ContainerID)
+	assert.NilError(t, err)
+	assert.Equal(t, ctr.State.Running, true)
+	return ctr
+}
+
+func getService(t *testing.T, cli client.ServiceAPIClient, serviceID string) swarmtypes.Service {
+	t.Helper()
+	service, _, err := cli.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
+	assert.NilError(t, err)
+	return service
+}
+
+func serviceIsUpdated(client client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
+	return func(log poll.LogT) poll.Result {
+		service, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
+		switch {
+		case err != nil:
+			return poll.Error(err)
+		case service.UpdateStatus != nil && service.UpdateStatus.State == swarmtypes.UpdateStateCompleted:
+			return poll.Success()
+		default:
+			if service.UpdateStatus != nil {
+				return poll.Continue("waiting for service %s to be updated, state: %s, message: %s", serviceID, service.UpdateStatus.State, service.UpdateStatus.Message)
+			}
+			return poll.Continue("waiting for service %s to be updated", serviceID)
+		}
+	}
+}
+
+func serviceSpecIsUpdated(client client.ServiceAPIClient, serviceID string, serviceOldVersion uint64) func(log poll.LogT) poll.Result {
+	return func(log poll.LogT) poll.Result {
+		service, _, err := client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
+		switch {
+		case err != nil:
+			return poll.Error(err)
+		case service.Version.Index > serviceOldVersion:
+			return poll.Success()
+		default:
+			return poll.Continue("waiting for service %s to be updated", serviceID)
+		}
+	}
+}
diff --git a/integration/session/main_test.go b/integration/session/main_test.go
index fc33025efe667..766bd512b3885 100644
--- a/integration/session/main_test.go
+++ b/integration/session/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
diff --git a/integration/session/session_test.go b/integration/session/session_test.go
index 23464fc1a140e..b5fdf0b32d9ce 100644
--- a/integration/session/session_test.go
+++ b/integration/session/session_test.go
@@ -4,23 +4,28 @@ import (
 	"net/http"
 	"testing"
 
-	req "github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"github.com/docker/docker/api/types/versions"
+	req "github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestSessionCreate(t *testing.T) {
-	skip.If(t, !testEnv.DaemonInfo.ExperimentalBuild)
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "experimental in older versions")
 
 	defer setupTest(t)()
-
-	res, body, err := req.Post("/session", req.With(func(r *http.Request) error {
-		r.Header.Set("X-Docker-Expose-Session-Uuid", "testsessioncreate") // so we don't block default name if something else is using it
-		r.Header.Set("Upgrade", "h2c")
-		return nil
-	}))
+	daemonHost := req.DaemonHost()
+
+	res, body, err := req.Post("/session",
+		req.Host(daemonHost),
+		req.With(func(r *http.Request) error {
+			r.Header.Set("X-Docker-Expose-Session-Uuid", "testsessioncreate") // so we don't block default name if something else is using it
+			r.Header.Set("Upgrade", "h2c")
+			return nil
+		}),
+	)
 	assert.NilError(t, err)
 	assert.NilError(t, body.Close())
 	assert.Check(t, is.DeepEqual(res.StatusCode, http.StatusSwitchingProtocols))
@@ -28,20 +33,26 @@ func TestSessionCreate(t *testing.T) {
 }
 
 func TestSessionCreateWithBadUpgrade(t *testing.T) {
-	skip.If(t, !testEnv.DaemonInfo.ExperimentalBuild)
 	skip.If(t, testEnv.OSType == "windows", "FIXME")
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.39"), "experimental in older versions")
+
+	defer setupTest(t)()
+	daemonHost := req.DaemonHost()
 
-	res, body, err := req.Post("/session")
+	res, body, err := req.Post("/session", req.Host(daemonHost))
 	assert.NilError(t, err)
 	assert.Check(t, is.DeepEqual(res.StatusCode, http.StatusBadRequest))
 	buf, err := req.ReadBody(body)
 	assert.NilError(t, err)
 	assert.Check(t, is.Contains(string(buf), "no upgrade"))
 
-	res, body, err = req.Post("/session", req.With(func(r *http.Request) error {
-		r.Header.Set("Upgrade", "foo")
-		return nil
-	}))
+	res, body, err = req.Post("/session",
+		req.Host(daemonHost),
+		req.With(func(r *http.Request) error {
+			r.Header.Set("Upgrade", "foo")
+			return nil
+		}),
+	)
 	assert.NilError(t, err)
 	assert.Check(t, is.DeepEqual(res.StatusCode, http.StatusBadRequest))
 	buf, err = req.ReadBody(body)
diff --git a/integration/system/cgroupdriver_systemd_test.go b/integration/system/cgroupdriver_systemd_test.go
index b955dd30257f2..c1b998abae228 100644
--- a/integration/system/cgroupdriver_systemd_test.go
+++ b/integration/system/cgroupdriver_systemd_test.go
@@ -7,10 +7,10 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/daemon"
+	"github.com/docker/docker/testutil/daemon"
 
-	"gotest.tools/assert"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
 )
 
 // hasSystemd checks whether the host was booted with systemd as its init
@@ -29,30 +29,27 @@ func hasSystemd() bool {
 //  https://github.com/moby/moby/issues/35123
 func TestCgroupDriverSystemdMemoryLimit(t *testing.T) {
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows")
+	skip.If(t, !hasSystemd())
 	t.Parallel()
 
-	if !hasSystemd() {
-		t.Skip("systemd not available")
-	}
-
 	d := daemon.New(t)
-	client, err := d.NewClient()
-	assert.NilError(t, err)
+	c := d.NewClientT(t)
+
 	d.StartWithBusybox(t, "--exec-opt", "native.cgroupdriver=systemd", "--iptables=false")
 	defer d.Stop(t)
 
-	const mem = 64 * 1024 * 1024 // 64 MB
+	const mem = int64(64 * 1024 * 1024) // 64 MB
 
 	ctx := context.Background()
-	ctrID := container.Create(t, ctx, client, func(c *container.TestContainerConfig) {
-		c.HostConfig.Resources.Memory = mem
+	ctrID := container.Create(ctx, t, c, func(ctr *container.TestContainerConfig) {
+		ctr.HostConfig.Resources.Memory = mem
 	})
-	defer client.ContainerRemove(ctx, ctrID, types.ContainerRemoveOptions{Force: true})
+	defer c.ContainerRemove(ctx, ctrID, types.ContainerRemoveOptions{Force: true})
 
-	err = client.ContainerStart(ctx, ctrID, types.ContainerStartOptions{})
+	err := c.ContainerStart(ctx, ctrID, types.ContainerStartOptions{})
 	assert.NilError(t, err)
 
-	s, err := client.ContainerInspect(ctx, ctrID)
+	s, err := c.ContainerInspect(ctx, ctrID)
 	assert.NilError(t, err)
 	assert.Equal(t, s.HostConfig.Memory, mem)
 }
diff --git a/integration/system/disk_usage_test.go b/integration/system/disk_usage_test.go
new file mode 100644
index 0000000000000..6c40a9b2fddfe
--- /dev/null
+++ b/integration/system/disk_usage_test.go
@@ -0,0 +1,274 @@
+package system // import "github.com/docker/docker/integration/system"
+
+import (
+	"context"
+	"testing"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
+)
+
+func TestDiskUsage(t *testing.T) {
+	skip.If(t, testEnv.OSType == "windows") // d.Start fails on Windows with `protocol not available`
+
+	t.Parallel()
+
+	d := daemon.New(t)
+	defer d.Cleanup(t)
+	d.Start(t, "--iptables=false")
+	defer d.Stop(t)
+	client := d.NewClientT(t)
+
+	ctx := context.Background()
+
+	var stepDU types.DiskUsage
+	for _, step := range []struct {
+		doc  string
+		next func(t *testing.T, prev types.DiskUsage) types.DiskUsage
+	}{
+		{
+			doc: "empty",
+			next: func(t *testing.T, _ types.DiskUsage) types.DiskUsage {
+				du, err := client.DiskUsage(ctx, types.DiskUsageOptions{})
+				assert.NilError(t, err)
+				assert.DeepEqual(t, du, types.DiskUsage{
+					Images:     []*types.ImageSummary{},
+					Containers: []*types.Container{},
+					Volumes:    []*types.Volume{},
+					BuildCache: []*types.BuildCache{},
+				})
+				return du
+			},
+		},
+		{
+			doc: "after LoadBusybox",
+			next: func(t *testing.T, _ types.DiskUsage) types.DiskUsage {
+				d.LoadBusybox(t)
+
+				du, err := client.DiskUsage(ctx, types.DiskUsageOptions{})
+				assert.NilError(t, err)
+				assert.Assert(t, du.LayersSize > 0)
+				assert.Equal(t, len(du.Images), 1)
+				assert.DeepEqual(t, du, types.DiskUsage{
+					LayersSize: du.LayersSize,
+					Images: []*types.ImageSummary{
+						{
+							Created:     du.Images[0].Created,
+							ID:          du.Images[0].ID,
+							RepoTags:    []string{"busybox:latest"},
+							Size:        du.LayersSize,
+							VirtualSize: du.LayersSize,
+						},
+					},
+					Containers: []*types.Container{},
+					Volumes:    []*types.Volume{},
+					BuildCache: []*types.BuildCache{},
+				})
+				return du
+			},
+		},
+		{
+			doc: "after container.Run",
+			next: func(t *testing.T, prev types.DiskUsage) types.DiskUsage {
+				cID := container.Run(ctx, t, client)
+
+				du, err := client.DiskUsage(ctx, types.DiskUsageOptions{})
+				assert.NilError(t, err)
+				assert.Equal(t, len(du.Containers), 1)
+				assert.Equal(t, len(du.Containers[0].Names), 1)
+				assert.Assert(t, du.Containers[0].Created >= prev.Images[0].Created)
+				assert.DeepEqual(t, du, types.DiskUsage{
+					LayersSize: prev.LayersSize,
+					Images: []*types.ImageSummary{
+						func() *types.ImageSummary {
+							sum := *prev.Images[0]
+							sum.Containers++
+							return &sum
+						}(),
+					},
+					Containers: []*types.Container{
+						{
+							ID:              cID,
+							Names:           du.Containers[0].Names,
+							Image:           "busybox",
+							ImageID:         prev.Images[0].ID,
+							Command:         du.Containers[0].Command, // not relevant for the test
+							Created:         du.Containers[0].Created,
+							Ports:           du.Containers[0].Ports, // not relevant for the test
+							SizeRootFs:      prev.Images[0].Size,
+							Labels:          du.Containers[0].Labels,          // not relevant for the test
+							State:           du.Containers[0].State,           // not relevant for the test
+							Status:          du.Containers[0].Status,          // not relevant for the test
+							HostConfig:      du.Containers[0].HostConfig,      // not relevant for the test
+							NetworkSettings: du.Containers[0].NetworkSettings, // not relevant for the test
+							Mounts:          du.Containers[0].Mounts,          // not relevant for the test
+						},
+					},
+					Volumes:    []*types.Volume{},
+					BuildCache: []*types.BuildCache{},
+				})
+				return du
+			},
+		},
+	} {
+		t.Run(step.doc, func(t *testing.T) {
+			stepDU = step.next(t, stepDU)
+
+			for _, tc := range []struct {
+				doc      string
+				options  types.DiskUsageOptions
+				expected types.DiskUsage
+			}{
+				{
+					doc: "container types",
+					options: types.DiskUsageOptions{
+						Types: []types.DiskUsageObject{
+							types.ContainerObject,
+						},
+					},
+					expected: types.DiskUsage{
+						Containers: stepDU.Containers,
+					},
+				},
+				{
+					doc: "image types",
+					options: types.DiskUsageOptions{
+						Types: []types.DiskUsageObject{
+							types.ImageObject,
+						},
+					},
+					expected: types.DiskUsage{
+						LayersSize: stepDU.LayersSize,
+						Images:     stepDU.Images,
+					},
+				},
+				{
+					doc: "volume types",
+					options: types.DiskUsageOptions{
+						Types: []types.DiskUsageObject{
+							types.VolumeObject,
+						},
+					},
+					expected: types.DiskUsage{
+						Volumes: stepDU.Volumes,
+					},
+				},
+				{
+					doc: "build-cache types",
+					options: types.DiskUsageOptions{
+						Types: []types.DiskUsageObject{
+							types.BuildCacheObject,
+						},
+					},
+					expected: types.DiskUsage{
+						BuildCache: stepDU.BuildCache,
+					},
+				},
+				{
+					doc: "container, volume types",
+					options: types.DiskUsageOptions{
+						Types: []types.DiskUsageObject{
+							types.ContainerObject,
+							types.VolumeObject,
+						},
+					},
+					expected: types.DiskUsage{
+						Containers: stepDU.Containers,
+						Volumes:    stepDU.Volumes,
+					},
+				},
+				{
+					doc: "image, build-cache types",
+					options: types.DiskUsageOptions{
+						Types: []types.DiskUsageObject{
+							types.ImageObject,
+							types.BuildCacheObject,
+						},
+					},
+					expected: types.DiskUsage{
+						LayersSize: stepDU.LayersSize,
+						Images:     stepDU.Images,
+						BuildCache: stepDU.BuildCache,
+					},
+				},
+				{
+					doc: "container, volume, build-cache types",
+					options: types.DiskUsageOptions{
+						Types: []types.DiskUsageObject{
+							types.ContainerObject,
+							types.VolumeObject,
+							types.BuildCacheObject,
+						},
+					},
+					expected: types.DiskUsage{
+						Containers: stepDU.Containers,
+						Volumes:    stepDU.Volumes,
+						BuildCache: stepDU.BuildCache,
+					},
+				},
+				{
+					doc: "image, volume, build-cache types",
+					options: types.DiskUsageOptions{
+						Types: []types.DiskUsageObject{
+							types.ImageObject,
+							types.VolumeObject,
+							types.BuildCacheObject,
+						},
+					},
+					expected: types.DiskUsage{
+						LayersSize: stepDU.LayersSize,
+						Images:     stepDU.Images,
+						Volumes:    stepDU.Volumes,
+						BuildCache: stepDU.BuildCache,
+					},
+				},
+				{
+					doc: "container, image, volume types",
+					options: types.DiskUsageOptions{
+						Types: []types.DiskUsageObject{
+							types.ContainerObject,
+							types.ImageObject,
+							types.VolumeObject,
+						},
+					},
+					expected: types.DiskUsage{
+						LayersSize: stepDU.LayersSize,
+						Containers: stepDU.Containers,
+						Images:     stepDU.Images,
+						Volumes:    stepDU.Volumes,
+					},
+				},
+				{
+					doc: "container, image, volume, build-cache types",
+					options: types.DiskUsageOptions{
+						Types: []types.DiskUsageObject{
+							types.ContainerObject,
+							types.ImageObject,
+							types.VolumeObject,
+							types.BuildCacheObject,
+						},
+					},
+					expected: types.DiskUsage{
+						LayersSize: stepDU.LayersSize,
+						Containers: stepDU.Containers,
+						Images:     stepDU.Images,
+						Volumes:    stepDU.Volumes,
+						BuildCache: stepDU.BuildCache,
+					},
+				},
+			} {
+				tc := tc
+				t.Run(tc.doc, func(t *testing.T) {
+					// TODO: Run in parallel once https://github.com/moby/moby/pull/42560 is merged.
+
+					du, err := client.DiskUsage(ctx, tc.options)
+					assert.NilError(t, err)
+					assert.DeepEqual(t, du, tc.expected)
+				})
+			}
+		})
+	}
+}
diff --git a/integration/system/event_test.go b/integration/system/event_test.go
index 60d10407f6b2a..592f4432ef6ff 100644
--- a/integration/system/event_test.go
+++ b/integration/system/event_test.go
@@ -3,6 +3,7 @@ package system // import "github.com/docker/docker/integration/system"
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"io"
 	"net/http"
 	"net/url"
@@ -11,16 +12,19 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/events"
 	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/api/types/volume"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
-	req "github.com/docker/docker/internal/test/request"
 	"github.com/docker/docker/pkg/jsonmessage"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"github.com/docker/docker/testutil/request"
+	req "github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestEventsExecDie(t *testing.T) {
@@ -28,9 +32,9 @@ func TestEventsExecDie(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows", "FIXME. Suspect may need to wait until container is running before exec")
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
-	cID := container.Run(t, ctx, client)
+	cID := container.Run(ctx, t, client)
 
 	id, err := client.ContainerExecCreate(ctx, cID,
 		types.ExecConfig{
@@ -63,7 +67,7 @@ func TestEventsExecDie(t *testing.T) {
 		assert.Equal(t, m.Actor.Attributes["execID"], id.ID)
 		assert.Equal(t, m.Actor.Attributes["exitCode"], "0")
 	case err = <-errors:
-		t.Fatal(err)
+		assert.NilError(t, err)
 	case <-time.After(time.Second * 3):
 		t.Fatal("timeout hit")
 	}
@@ -78,12 +82,12 @@ func TestEventsBackwardsCompatible(t *testing.T) {
 	skip.If(t, testEnv.OSType == "windows", "Windows doesn't support back-compat messages")
 	defer setupTest(t)()
 	ctx := context.Background()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 
 	since := request.DaemonTime(ctx, t, client, testEnv)
 	ts := strconv.FormatInt(since.Unix(), 10)
 
-	cID := container.Create(t, ctx, client)
+	cID := container.Create(ctx, t, client)
 
 	// In case there is no events, the API should have responded immediately (not blocking),
 	// The test here makes sure the response time is less than 3 sec.
@@ -109,7 +113,7 @@ func TestEventsBackwardsCompatible(t *testing.T) {
 			if err == io.EOF {
 				break
 			}
-			t.Fatal(err)
+			assert.NilError(t, err)
 		}
 		if event.Status == "create" && event.ID == cID {
 			containerCreateEvent = &event
@@ -122,3 +126,69 @@ func TestEventsBackwardsCompatible(t *testing.T) {
 	assert.Check(t, is.Equal(cID, containerCreateEvent.ID))
 	assert.Check(t, is.Equal("busybox", containerCreateEvent.From))
 }
+
+// TestEventsVolumeCreate verifies that volume create events are only fired
+// once: when creating the volume, and not when attaching to a container.
+func TestEventsVolumeCreate(t *testing.T) {
+	skip.If(t, testEnv.OSType == "windows", "FIXME: Windows doesn't trigger the events? Could be a race")
+
+	defer setupTest(t)()
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	client := testEnv.APIClient()
+
+	since := request.DaemonUnixTime(ctx, t, client, testEnv)
+	volName := t.Name()
+	getEvents := func(messages <-chan events.Message, errs <-chan error) ([]events.Message, error) {
+		var evts []events.Message
+
+		for {
+			select {
+			case m := <-messages:
+				evts = append(evts, m)
+			case err := <-errs:
+				if err == io.EOF {
+					return evts, nil
+				}
+				return nil, err
+			case <-time.After(time.Second * 3):
+				return nil, errors.New("timeout hit")
+			}
+		}
+	}
+
+	_, err := client.VolumeCreate(ctx, volume.VolumeCreateBody{Name: volName})
+	assert.NilError(t, err)
+
+	filter := filters.NewArgs(
+		filters.Arg("type", "volume"),
+		filters.Arg("event", "create"),
+		filters.Arg("volume", volName),
+	)
+	messages, errs := client.Events(ctx, types.EventsOptions{
+		Since:   since,
+		Until:   request.DaemonUnixTime(ctx, t, client, testEnv),
+		Filters: filter,
+	})
+
+	volEvents, err := getEvents(messages, errs)
+	assert.NilError(t, err)
+	assert.Equal(t, len(volEvents), 1, "expected volume create event when creating a volume")
+
+	container.Create(ctx, t, client, container.WithMount(mount.Mount{
+		Type:   mount.TypeVolume,
+		Source: volName,
+		Target: "/tmp/foo",
+	}))
+
+	messages, errs = client.Events(ctx, types.EventsOptions{
+		Since:   since,
+		Until:   request.DaemonUnixTime(ctx, t, client, testEnv),
+		Filters: filter,
+	})
+
+	volEvents, err = getEvents(messages, errs)
+	assert.NilError(t, err)
+	assert.Equal(t, len(volEvents), 1, "expected volume create event to be fired only once")
+}
diff --git a/integration/system/info_linux_test.go b/integration/system/info_linux_test.go
index f1a60ef6d5312..87e691f526f20 100644
--- a/integration/system/info_linux_test.go
+++ b/integration/system/info_linux_test.go
@@ -1,20 +1,25 @@
+//go:build !windows
 // +build !windows
 
 package system // import "github.com/docker/docker/integration/system"
 
 import (
 	"context"
+	"fmt"
+	"net"
 	"net/http"
 	"testing"
 
-	"github.com/docker/docker/internal/test/request"
-	req "github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
+	"github.com/docker/docker/testutil/daemon"
+	req "github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestInfoBinaryCommits(t *testing.T) {
-	client := request.NewAPIClient(t)
+	defer setupTest(t)()
+	client := testEnv.APIClient()
 
 	info, err := client.Info(context.Background())
 	assert.NilError(t, err)
@@ -43,3 +48,72 @@ func TestInfoAPIVersioned(t *testing.T) {
 	assert.Check(t, is.Contains(out, "ExecutionDriver"))
 	assert.Check(t, is.Contains(out, "not supported"))
 }
+
+// TestInfoDiscoveryBackend verifies that a daemon run with `--cluster-advertise` and
+// `--cluster-store` properly returns the backend's endpoint in info output.
+func TestInfoDiscoveryBackend(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+
+	const (
+		discoveryBackend   = "consul://consuladdr:consulport/some/path"
+		discoveryAdvertise = "1.1.1.1:2375"
+	)
+
+	d := daemon.New(t)
+	d.Start(t, "--cluster-store="+discoveryBackend, "--cluster-advertise="+discoveryAdvertise)
+	defer d.Stop(t)
+
+	info := d.Info(t)
+	assert.Equal(t, info.ClusterStore, discoveryBackend)
+	assert.Equal(t, info.ClusterAdvertise, discoveryAdvertise)
+}
+
+// TestInfoDiscoveryInvalidAdvertise verifies that a daemon run with
+// an invalid `--cluster-advertise` configuration
+func TestInfoDiscoveryInvalidAdvertise(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+	d := daemon.New(t)
+
+	// --cluster-advertise with an invalid string is an error
+	err := d.StartWithError("--cluster-store=consul://consuladdr:consulport/some/path", "--cluster-advertise=invalid")
+	if err == nil {
+		d.Stop(t)
+	}
+	assert.ErrorContains(t, err, "", "expected error when starting daemon")
+
+	// --cluster-advertise without --cluster-store is also an error
+	err = d.StartWithError("--cluster-advertise=1.1.1.1:2375")
+	if err == nil {
+		d.Stop(t)
+	}
+	assert.ErrorContains(t, err, "", "expected error when starting daemon")
+}
+
+// TestInfoDiscoveryAdvertiseInterfaceName verifies that a daemon run with `--cluster-advertise`
+// configured with interface name properly show the advertise ip-address in info output.
+func TestInfoDiscoveryAdvertiseInterfaceName(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+	skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
+	// TODO should we check for networking availability (integration-cli suite checks for networking through `Network()`)
+
+	d := daemon.New(t)
+	const (
+		discoveryStore     = "consul://consuladdr:consulport/some/path"
+		discoveryInterface = "eth0"
+	)
+
+	d.Start(t, "--cluster-store="+discoveryStore, fmt.Sprintf("--cluster-advertise=%s:2375", discoveryInterface))
+	defer d.Stop(t)
+
+	iface, err := net.InterfaceByName(discoveryInterface)
+	assert.NilError(t, err)
+	addrs, err := iface.Addrs()
+	assert.NilError(t, err)
+	assert.Assert(t, len(addrs) > 0)
+	ip, _, err := net.ParseCIDR(addrs[0].String())
+	assert.NilError(t, err)
+
+	info := d.Info(t)
+	assert.Equal(t, info.ClusterStore, discoveryStore)
+	assert.Equal(t, info.ClusterAdvertise, ip.String()+":2375")
+}
diff --git a/integration/system/info_test.go b/integration/system/info_test.go
index fa2157b8517b8..d2f6dc4344380 100644
--- a/integration/system/info_test.go
+++ b/integration/system/info_test.go
@@ -3,17 +3,19 @@ package system // import "github.com/docker/docker/integration/system"
 import (
 	"context"
 	"fmt"
+	"sort"
 	"testing"
 
-	"github.com/docker/docker/internal/test/daemon"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"github.com/docker/docker/api/types/registry"
+	"github.com/docker/docker/testutil/daemon"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 func TestInfoAPI(t *testing.T) {
-	client := request.NewAPIClient(t)
+	defer setupTest(t)()
+	client := testEnv.APIClient()
 
 	info, err := client.Info(context.Background())
 	assert.NilError(t, err)
@@ -44,16 +46,15 @@ func TestInfoAPI(t *testing.T) {
 }
 
 func TestInfoAPIWarnings(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
 	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME")
 	d := daemon.New(t)
+	c := d.NewClientT(t)
 
-	client, err := d.NewClient()
-	assert.NilError(t, err)
-
-	d.StartWithBusybox(t, "--iptables=false", "-H=0.0.0.0:23756", "-H=unix://"+d.Sock())
+	d.Start(t, "-H=0.0.0.0:23756", "-H="+d.Sock())
 	defer d.Stop(t)
 
-	info, err := client.Info(context.Background())
+	info, err := c.Info(context.Background())
 	assert.NilError(t, err)
 
 	stringsToCheck := []string{
@@ -66,3 +67,67 @@ func TestInfoAPIWarnings(t *testing.T) {
 		assert.Check(t, is.Contains(out, linePrefix))
 	}
 }
+
+func TestInfoDebug(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME: test starts daemon with -H unix://.....")
+
+	d := daemon.New(t)
+	d.Start(t, "--debug")
+	defer d.Stop(t)
+
+	info := d.Info(t)
+	assert.Equal(t, info.Debug, true)
+
+	// Note that the information below is not tied to debug-mode being enabled.
+	assert.Check(t, info.NFd != 0)
+
+	// TODO need a stable way to generate event listeners
+	// assert.Check(t, info.NEventsListener != 0)
+	assert.Check(t, info.NGoroutines != 0)
+	assert.Check(t, info.SystemTime != "")
+	assert.Equal(t, info.DockerRootDir, d.Root)
+}
+
+func TestInfoInsecureRegistries(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME: test starts daemon with -H unix://.....")
+
+	const (
+		registryCIDR = "192.168.1.0/24"
+		registryHost = "insecurehost.com:5000"
+	)
+
+	d := daemon.New(t)
+	d.Start(t, "--insecure-registry="+registryCIDR, "--insecure-registry="+registryHost)
+	defer d.Stop(t)
+
+	info := d.Info(t)
+	assert.Assert(t, is.Len(info.RegistryConfig.InsecureRegistryCIDRs, 2))
+	cidrs := []string{
+		info.RegistryConfig.InsecureRegistryCIDRs[0].String(),
+		info.RegistryConfig.InsecureRegistryCIDRs[1].String(),
+	}
+	assert.Assert(t, is.Contains(cidrs, registryCIDR))
+	assert.Assert(t, is.Contains(cidrs, "127.0.0.0/8"))
+	assert.DeepEqual(t, *info.RegistryConfig.IndexConfigs["docker.io"], registry.IndexInfo{Name: "docker.io", Mirrors: []string{}, Secure: true, Official: true})
+	assert.DeepEqual(t, *info.RegistryConfig.IndexConfigs[registryHost], registry.IndexInfo{Name: registryHost, Mirrors: []string{}, Secure: false, Official: false})
+}
+
+func TestInfoRegistryMirrors(t *testing.T) {
+	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME: test starts daemon with -H unix://.....")
+
+	const (
+		registryMirror1 = "https://192.168.1.2"
+		registryMirror2 = "http://registry-mirror.example.com:5000"
+	)
+
+	d := daemon.New(t)
+	d.Start(t, "--registry-mirror="+registryMirror1, "--registry-mirror="+registryMirror2)
+	defer d.Stop(t)
+
+	info := d.Info(t)
+	sort.Strings(info.RegistryConfig.Mirrors)
+	assert.DeepEqual(t, info.RegistryConfig.Mirrors, []string{registryMirror2 + "/", registryMirror1 + "/"})
+}
diff --git a/integration/system/login_test.go b/integration/system/login_test.go
index ad1a8756dc173..1284911020d80 100644
--- a/integration/system/login_test.go
+++ b/integration/system/login_test.go
@@ -6,23 +6,24 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/integration/internal/requirement"
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+	"gotest.tools/v3/skip"
 )
 
 // Test case for GitHub 22244
 func TestLoginFailsWithBadCredentials(t *testing.T) {
 	skip.If(t, !requirement.HasHubConnectivity(t))
 
-	client := request.NewAPIClient(t)
+	defer setupTest(t)()
+	client := testEnv.APIClient()
 
 	config := types.AuthConfig{
 		Username: "no-user",
 		Password: "no-password",
 	}
 	_, err := client.RegistryLogin(context.Background(), config)
-	expected := "Error response from daemon: Get https://registry-1.docker.io/v2/: unauthorized: incorrect username or password"
-	assert.Check(t, is.Error(err, expected))
+	assert.Assert(t, err != nil)
+	assert.Check(t, is.ErrorContains(err, "unauthorized: incorrect username or password"))
+	assert.Check(t, is.ErrorContains(err, "https://registry-1.docker.io/v2/"))
 }
diff --git a/integration/system/main_test.go b/integration/system/main_test.go
index f19a3157aa393..6dd4761cd5f68 100644
--- a/integration/system/main_test.go
+++ b/integration/system/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
diff --git a/integration/system/ping_test.go b/integration/system/ping_test.go
new file mode 100644
index 0000000000000..cd7a1c8eff3f4
--- /dev/null
+++ b/integration/system/ping_test.go
@@ -0,0 +1,59 @@
+package system // import "github.com/docker/docker/integration/system"
+
+import (
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/docker/docker/api/types/versions"
+	"github.com/docker/docker/testutil/request"
+	"gotest.tools/v3/assert"
+	"gotest.tools/v3/skip"
+)
+
+func TestPingCacheHeaders(t *testing.T) {
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "skip test from new feature")
+	defer setupTest(t)()
+
+	res, _, err := request.Get("/_ping")
+	assert.NilError(t, err)
+	assert.Equal(t, res.StatusCode, http.StatusOK)
+
+	assert.Equal(t, hdr(res, "Cache-Control"), "no-cache, no-store, must-revalidate")
+	assert.Equal(t, hdr(res, "Pragma"), "no-cache")
+}
+
+func TestPingGet(t *testing.T) {
+	defer setupTest(t)()
+
+	res, body, err := request.Get("/_ping")
+	assert.NilError(t, err)
+
+	b, err := request.ReadBody(body)
+	assert.NilError(t, err)
+	assert.Equal(t, string(b), "OK")
+	assert.Equal(t, res.StatusCode, http.StatusOK)
+	assert.Check(t, hdr(res, "API-Version") != "")
+}
+
+func TestPingHead(t *testing.T) {
+	skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "skip test from new feature")
+	defer setupTest(t)()
+
+	res, body, err := request.Head("/_ping")
+	assert.NilError(t, err)
+
+	b, err := request.ReadBody(body)
+	assert.NilError(t, err)
+	assert.Equal(t, 0, len(b))
+	assert.Equal(t, res.StatusCode, http.StatusOK)
+	assert.Check(t, hdr(res, "API-Version") != "")
+}
+
+func hdr(res *http.Response, name string) string {
+	val, ok := res.Header[http.CanonicalHeaderKey(name)]
+	if !ok || len(val) == 0 {
+		return ""
+	}
+	return strings.Join(val, ", ")
+}
diff --git a/integration/system/version_test.go b/integration/system/version_test.go
index 8904c09b26c6f..98a3c8ed44c04 100644
--- a/integration/system/version_test.go
+++ b/integration/system/version_test.go
@@ -4,13 +4,13 @@ import (
 	"context"
 	"testing"
 
-	"github.com/docker/docker/internal/test/request"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 func TestVersion(t *testing.T) {
-	client := request.NewAPIClient(t)
+	defer setupTest(t)()
+	client := testEnv.APIClient()
 
 	version, err := client.ServerVersion(context.Background())
 	assert.NilError(t, err)
diff --git a/integration/testdata/https/ca.pem b/integration/testdata/https/ca.pem
index 6825d6d1bd15e..61285ec8e31ab 100644
--- a/integration/testdata/https/ca.pem
+++ b/integration/testdata/https/ca.pem
@@ -1,23 +1,82 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            45:9c:ce:13:92:42:39:2e:90:f5:93:05:f1:03:92:17:5d:e4:89:8d
+        Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=US, ST=CA, L=SanFrancisco, O=Moby-project, OU=ci, CN=moby-ci/name=moby/emailAddress=moby@example.org
+        Validity
+            Not Before: May 17 19:49:34 2021 GMT
+            Not After : May 17 19:49:34 2031 GMT
+        Subject: C=US, ST=CA, L=SanFrancisco, O=Moby-project, OU=ci, CN=moby-ci/name=moby/emailAddress=moby@example.org
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                RSA Public-Key: (2048 bit)
+                Modulus:
+                    00:c2:5a:af:10:15:fb:c8:46:c4:31:d7:ee:ec:d9:
+                    c4:1e:c3:b3:b6:4c:ec:e1:2b:57:40:a2:74:cd:d5:
+                    8e:7d:69:b6:22:60:21:05:be:a5:92:40:4c:43:2b:
+                    eb:c9:00:32:5f:59:1c:59:50:e2:98:df:ff:9b:2d:
+                    16:9f:c6:a0:57:78:bc:ae:a5:8d:b3:7d:98:73:7a:
+                    6f:d2:05:52:15:89:89:22:ec:9d:9a:e7:c7:35:8f:
+                    6b:38:a3:33:54:c5:74:2a:05:ad:af:a0:8a:54:7b:
+                    7d:d4:6a:9b:2b:90:cb:9a:e7:6e:94:bd:a2:f3:5b:
+                    40:d1:fa:4d:ec:fd:6f:14:1d:89:5b:fc:35:c2:1c:
+                    98:0b:c4:53:7a:25:16:3f:02:e9:e8:46:20:4d:e8:
+                    1e:25:0d:0d:10:e9:36:42:2a:88:d9:91:b3:fa:9e:
+                    07:c0:a9:b1:44:db:2c:e5:cb:85:bf:4a:38:a0:cf:
+                    7e:2c:20:e5:a9:cf:49:2a:6f:e3:b8:93:fd:38:9b:
+                    2a:c2:ea:c3:0f:3b:f5:f3:30:c8:f7:51:d5:8b:d0:
+                    5e:97:75:21:e4:d2:47:ca:1d:66:4a:36:b2:81:13:
+                    d9:13:19:0d:35:04:84:ca:35:f4:47:f9:47:37:21:
+                    64:95:a1:cb:8a:01:d3:e6:50:e2:01:17:e5:0e:64:
+                    89:0d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                85:57:D0:FF:A9:B4:1E:1F:80:33:FB:B8:34:ED:7D:06:39:CD:34:98
+            X509v3 Authority Key Identifier: 
+                keyid:85:57:D0:FF:A9:B4:1E:1F:80:33:FB:B8:34:ED:7D:06:39:CD:34:98
+
+            X509v3 Basic Constraints: critical
+                CA:TRUE
+    Signature Algorithm: sha256WithRSAEncryption
+         46:73:2d:4b:ce:b0:c2:13:19:85:97:67:95:d9:15:6f:cf:e0:
+         89:e4:42:90:4e:a3:5a:64:8c:e9:92:6f:b4:cb:56:e6:ec:6e:
+         91:04:18:12:79:ca:70:bb:e5:ba:5d:ed:fe:8c:47:7e:8f:8b:
+         bd:9f:40:5a:63:51:b8:80:6f:b2:7b:ff:c1:43:68:7d:21:0c:
+         0a:a4:ea:b7:2d:0a:31:e4:3e:5e:bb:72:bd:63:6b:a1:2d:d3:
+         ca:6a:e0:af:17:52:12:71:73:77:41:11:f1:24:32:54:b4:67:
+         c9:5e:b1:f1:cf:bd:95:91:c8:9c:43:4f:3f:c3:f6:3c:0e:41:
+         2b:f9:c7:25:3f:17:4d:4a:e7:27:36:bc:9e:d4:30:e6:6e:29:
+         95:e4:33:66:b4:2e:11:ac:97:61:df:3f:4d:03:8e:96:04:10:
+         a5:d8:5f:85:a3:4b:6c:d5:1c:7d:17:8c:4c:8a:cb:9d:27:65:
+         2c:ee:dd:2b:19:27:1a:57:3c:68:2d:eb:6e:e8:b2:59:8c:0a:
+         17:75:ba:fc:89:d8:fc:c0:45:44:8a:a1:9c:52:b0:f3:b7:6d:
+         f2:2e:24:ee:50:d9:27:4d:33:89:5c:97:34:b0:47:81:94:4b:
+         c1:b4:aa:d9:65:b5:4f:98:0b:a9:76:30:a0:ef:f1:71:23:0f:
+         04:dc:83:fd
 -----BEGIN CERTIFICATE-----
-MIID0TCCAzqgAwIBAgIJAP2r7GqEJwSnMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD
-VQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMG
-A1UEChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTERMA8GA1UEAxMI
-Y2hhbmdlbWUxETAPBgNVBCkTCGNoYW5nZW1lMR8wHQYJKoZIhvcNAQkBFhBtYWls
-QGhvc3QuZG9tYWluMB4XDTEzMTIwMzE2NTYzMFoXDTIzMTIwMTE2NTYzMFowgaIx
-CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2Nv
-MRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYD
-VQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEW
-EG1haWxAaG9zdC5kb21haW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALAn
-0xDw+5y7ZptQacq66pUhRu82JP2WU6IDgo5QUtNU6/CX5PwQATe/OnYTZQFbksxp
-AU9boG0FCkgxfsgPYXEuZxVEGKI2fxfKHOZZI8mrkWmj6eWU/0cvCjGVc9rTITP5
-sNQvg+hORyVDdNp2IdsbMJayiB3AQYMFx3vSDOMTAgMBAAGjggELMIIBBzAdBgNV
-HQ4EFgQUZu7DFz09q0QBa2+ymRm9qgK1NPswgdcGA1UdIwSBzzCBzIAUZu7DFz09
-q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD
-QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x
-ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI
-Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq
-hCcEpzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAF8fJKKM+/oOdnNi
-zEd0M1+PmZOyqvjYQn/2ZR8UHH6Imgc/OPQKZXf0bVE1Txc/DaUNn9Isd1SuCuaE
-ic3vAIYYU7PmgeNN6vwec48V96T7jr+GAi6AVMhQEc2hHCfVtx11Xx+x6aHDZzJt
-Zxtf5lL6KSO9Y+EFwM+rju6hm5hW
+MIIEETCCAvmgAwIBAgIURZzOE5JCOS6Q9ZMF8QOSF13kiY0wDQYJKoZIhvcNAQEL
+BQAwgZcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEVMBMGA1UEBwwMU2FuRnJh
+bmNpc2NvMRUwEwYDVQQKDAxNb2J5LXByb2plY3QxCzAJBgNVBAsMAmNpMRAwDgYD
+VQQDDAdtb2J5LWNpMQ0wCwYDVQQpDARtb2J5MR8wHQYJKoZIhvcNAQkBFhBtb2J5
+QGV4YW1wbGUub3JnMB4XDTIxMDUxNzE5NDkzNFoXDTMxMDUxNzE5NDkzNFowgZcx
+CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEVMBMGA1UEBwwMU2FuRnJhbmNpc2Nv
+MRUwEwYDVQQKDAxNb2J5LXByb2plY3QxCzAJBgNVBAsMAmNpMRAwDgYDVQQDDAdt
+b2J5LWNpMQ0wCwYDVQQpDARtb2J5MR8wHQYJKoZIhvcNAQkBFhBtb2J5QGV4YW1w
+bGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwlqvEBX7yEbE
+Mdfu7NnEHsOztkzs4StXQKJ0zdWOfWm2ImAhBb6lkkBMQyvryQAyX1kcWVDimN//
+my0Wn8agV3i8rqWNs32Yc3pv0gVSFYmJIuydmufHNY9rOKMzVMV0KgWtr6CKVHt9
+1GqbK5DLmudulL2i81tA0fpN7P1vFB2JW/w1whyYC8RTeiUWPwLp6EYgTegeJQ0N
+EOk2QiqI2ZGz+p4HwKmxRNss5cuFv0o4oM9+LCDlqc9JKm/juJP9OJsqwurDDzv1
+8zDI91HVi9Bel3Uh5NJHyh1mSjaygRPZExkNNQSEyjX0R/lHNyFklaHLigHT5lDi
+ARflDmSJDQIDAQABo1MwUTAdBgNVHQ4EFgQUhVfQ/6m0Hh+AM/u4NO19BjnNNJgw
+HwYDVR0jBBgwFoAUhVfQ/6m0Hh+AM/u4NO19BjnNNJgwDwYDVR0TAQH/BAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEARnMtS86wwhMZhZdnldkVb8/gieRCkE6jWmSM
+6ZJvtMtW5uxukQQYEnnKcLvlul3t/oxHfo+LvZ9AWmNRuIBvsnv/wUNofSEMCqTq
+ty0KMeQ+XrtyvWNroS3TymrgrxdSEnFzd0ER8SQyVLRnyV6x8c+9lZHInENPP8P2
+PA5BK/nHJT8XTUrnJza8ntQw5m4pleQzZrQuEayXYd8/TQOOlgQQpdhfhaNLbNUc
+fReMTIrLnSdlLO7dKxknGlc8aC3rbuiyWYwKF3W6/InY/MBFRIqhnFKw87dt8i4k
+7lDZJ00ziVyXNLBHgZRLwbSq2WW1T5gLqXYwoO/xcSMPBNyD/Q==
 -----END CERTIFICATE-----
diff --git a/integration/testdata/https/client-cert.pem b/integration/testdata/https/client-cert.pem
index c05ed47c2c5eb..77809c28815a5 100644
--- a/integration/testdata/https/client-cert.pem
+++ b/integration/testdata/https/client-cert.pem
@@ -2,72 +2,85 @@ Certificate:
     Data:
         Version: 3 (0x2)
         Serial Number: 3 (0x3)
-    Signature Algorithm: sha1WithRSAEncryption
-        Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain
+        Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=US, ST=CA, L=SanFrancisco, O=Moby-project, OU=ci, CN=moby-ci/name=moby/emailAddress=moby@example.org
         Validity
-            Not Before: Dec  4 14:17:54 2013 GMT
-            Not After : Dec  2 14:17:54 2023 GMT
-        Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=client/name=changeme/emailAddress=mail@host.domain
+            Not Before: May 17 19:49:34 2021 GMT
+            Not After : May 17 19:49:34 2031 GMT
+        Subject: C=US, ST=CA, L=SanFrancisco, O=Moby-project, OU=ci, CN=client/name=moby/emailAddress=moby@example.org
         Subject Public Key Info:
             Public Key Algorithm: rsaEncryption
-                Public-Key: (1024 bit)
+                RSA Public-Key: (2048 bit)
                 Modulus:
-                    00:ca:c9:05:d0:09:4e:3e:a4:fc:d5:14:f4:a5:e8:
-                    34:d3:6b:51:e3:f3:62:ea:a1:f0:e8:ed:c4:2a:bc:
-                    f0:4f:ca:07:df:e3:88:fa:f4:21:99:35:0e:3d:ea:
-                    b0:86:e7:c4:d2:8a:83:2b:42:b8:ec:a3:99:62:70:
-                    81:46:cc:fc:a5:1d:d2:63:e8:eb:07:25:9a:e2:25:
-                    6d:11:56:f2:1a:51:a1:b6:3e:1c:57:32:e9:7b:2c:
-                    aa:1b:cc:97:2d:89:2d:b1:c9:5e:35:28:4d:7c:fa:
-                    65:31:3e:f7:70:dd:6e:0b:3c:58:af:a8:2e:24:c0:
-                    7e:4e:78:7d:0a:9e:8f:42:43
+                    00:e3:20:9f:c9:63:fe:29:a9:0e:21:e0:4d:4c:42:
+                    cb:cc:9f:29:8c:73:5d:f7:88:bd:81:62:1f:b2:a3:
+                    95:4d:3a:58:28:af:f0:3e:aa:a7:c2:c6:52:b9:94:
+                    9f:6b:58:d6:9a:08:b4:5f:60:fb:f1:ea:e7:49:8d:
+                    46:35:e2:e9:82:9f:20:44:41:82:a7:fa:ab:82:1b:
+                    03:7f:f0:4e:78:38:37:20:9d:67:43:c0:e2:8f:09:
+                    07:3f:7f:96:13:7a:64:c5:90:13:87:71:6d:ed:e7:
+                    28:3a:05:48:eb:d6:e6:27:da:46:f9:a4:5c:66:49:
+                    56:5f:88:87:4e:0a:8b:fe:ea:05:a6:c1:72:b9:94:
+                    d5:8e:d4:9a:18:58:ac:56:1b:34:3e:c3:50:06:5d:
+                    f3:3d:85:93:2c:8b:3f:33:e6:32:14:92:9e:fd:fc:
+                    5d:8a:71:1b:20:67:43:e0:72:fc:4e:31:c6:b7:03:
+                    98:99:e7:95:ef:7c:5a:30:cf:c1:a4:43:42:fb:be:
+                    1b:a7:08:d5:e0:b5:b2:10:ff:0f:e1:0d:ee:3e:b2:
+                    04:05:86:1e:72:a9:d6:16:84:37:73:28:5d:d9:3c:
+                    fd:f3:99:18:dc:90:83:59:23:90:bc:25:33:0f:23:
+                    48:9d:d2:97:a0:ac:94:4f:8e:31:22:cc:74:83:f7:
+                    31:9d
                 Exponent: 65537 (0x10001)
         X509v3 extensions:
             X509v3 Basic Constraints: 
                 CA:FALSE
-            Netscape Comment: 
-                Easy-RSA Generated Certificate
             X509v3 Subject Key Identifier: 
-                DE:42:EF:2D:98:A3:6C:A8:AA:E0:8C:71:2C:9D:64:23:A9:E2:7E:81
+                23:1C:5A:99:1A:2B:BC:FD:39:97:8D:1F:5A:49:BF:4F:33:0F:26:C1
             X509v3 Authority Key Identifier: 
-                keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB
-                DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain
-                serial:FD:AB:EC:6A:84:27:04:A7
+                keyid:85:57:D0:FF:A9:B4:1E:1F:80:33:FB:B8:34:ED:7D:06:39:CD:34:98
 
             X509v3 Extended Key Usage: 
                 TLS Web Client Authentication
-            X509v3 Key Usage: 
-                Digital Signature
-    Signature Algorithm: sha1WithRSAEncryption
-         1c:44:26:ea:e1:66:25:cb:e4:8e:57:1c:f6:b9:17:22:62:40:
-         12:90:8f:3b:b2:61:7a:54:94:8f:b1:20:0b:bf:a3:51:e3:fa:
-         1c:a1:be:92:3a:d0:76:44:c0:57:83:ab:6a:e4:1a:45:49:a4:
-         af:39:0d:60:32:fc:3a:be:d7:fb:5d:99:7a:1f:87:e7:d5:ab:
-         84:a2:5e:90:d8:bf:fa:89:6d:32:26:02:5e:31:35:68:7f:31:
-         f5:6b:51:46:bc:af:70:ed:5a:09:7d:ec:b2:48:4f:fe:c5:2f:
-         56:04:ad:f6:c1:d2:2a:e4:6a:c4:87:fe:08:35:c5:38:cb:5e:
-         4a:c4
+            X509v3 Subject Alternative Name: 
+                DNS:*, DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
+    Signature Algorithm: sha256WithRSAEncryption
+         4d:79:c0:07:ac:13:51:00:5c:4b:70:6d:9f:bf:87:c8:ac:31:
+         c9:37:5e:4e:4c:9f:c6:cd:a4:e3:df:72:b5:06:28:9d:f6:3e:
+         32:b5:01:81:43:78:6d:93:b2:b2:0a:0b:95:64:f2:25:a4:5e:
+         d1:4b:b1:11:5c:54:17:21:a7:f7:e6:73:af:f2:53:54:b3:69:
+         40:8c:26:5b:1b:a7:63:07:26:c4:d2:c4:7a:64:b3:ab:f1:23:
+         fa:58:9c:b2:b7:17:35:34:91:dd:84:bb:b0:ee:a6:cd:78:cf:
+         32:39:d8:5f:23:ad:62:ef:82:38:88:cd:34:1b:7d:3b:02:a8:
+         75:70:72:50:33:44:a4:65:01:14:ef:78:46:3b:27:4e:82:e6:
+         01:1b:5c:65:97:2c:08:f7:4f:e6:ee:dd:1c:40:0c:48:59:33:
+         5e:c7:da:bf:40:ce:b0:e9:03:95:6f:a8:07:b3:7f:6b:15:cd:
+         c0:6f:57:e3:73:99:67:aa:fd:90:6c:a7:6f:ff:b9:5f:f6:8a:
+         8c:93:f1:c3:75:34:10:c6:6c:0e:ae:0a:22:6b:16:6c:56:41:
+         0a:b5:e6:74:52:b8:3e:f2:e4:fc:f1:54:a0:84:90:d5:97:70:
+         25:4b:28:2c:8a:ec:46:0a:63:ac:32:c6:cd:96:71:ee:f6:17:
+         2c:e9:60:5e
 -----BEGIN CERTIFICATE-----
-MIIEFTCCA36gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx
-CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv
-cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l
-MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv
-bWFpbjAeFw0xMzEyMDQxNDE3NTRaFw0yMzEyMDIxNDE3NTRaMIGgMQswCQYDVQQG
-EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE
-ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEPMA0GA1UEAxMGY2xp
-ZW50MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0
-LmRvbWFpbjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyskF0AlOPqT81RT0
-peg002tR4/Ni6qHw6O3EKrzwT8oH3+OI+vQhmTUOPeqwhufE0oqDK0K47KOZYnCB
-Rsz8pR3SY+jrByWa4iVtEVbyGlGhtj4cVzLpeyyqG8yXLYktscleNShNfPplMT73
-cN1uCzxYr6guJMB+Tnh9Cp6PQkMCAwEAAaOCAVkwggFVMAkGA1UdEwQCMAAwLQYJ
-YIZIAYb4QgENBCAWHkVhc3ktUlNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV
-HQ4EFgQU3kLvLZijbKiq4IxxLJ1kI6nifoEwgdcGA1UdIwSBzzCBzIAUZu7DFz09
-q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD
-QTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUwEwYDVQQKEwxGb3J0LUZ1bnN0b24x
-ETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQDEwhjaGFuZ2VtZTERMA8GA1UEKRMI
-Y2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21haW6CCQD9q+xq
-hCcEpzATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcN
-AQEFBQADgYEAHEQm6uFmJcvkjlcc9rkXImJAEpCPO7JhelSUj7EgC7+jUeP6HKG+
-kjrQdkTAV4OrauQaRUmkrzkNYDL8Or7X+12Zeh+H59WrhKJekNi/+oltMiYCXjE1
-aH8x9WtRRryvcO1aCX3sskhP/sUvVgSt9sHSKuRqxIf+CDXFOMteSsQ=
+MIIEPzCCAyegAwIBAgIBAzANBgkqhkiG9w0BAQsFADCBlzELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xFTATBgNVBAoMDE1v
+YnktcHJvamVjdDELMAkGA1UECwwCY2kxEDAOBgNVBAMMB21vYnktY2kxDTALBgNV
+BCkMBG1vYnkxHzAdBgkqhkiG9w0BCQEWEG1vYnlAZXhhbXBsZS5vcmcwHhcNMjEw
+NTE3MTk0OTM0WhcNMzEwNTE3MTk0OTM0WjCBljELMAkGA1UEBhMCVVMxCzAJBgNV
+BAgMAkNBMRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xFTATBgNVBAoMDE1vYnktcHJv
+amVjdDELMAkGA1UECwwCY2kxDzANBgNVBAMMBmNsaWVudDENMAsGA1UEKQwEbW9i
+eTEfMB0GCSqGSIb3DQEJARYQbW9ieUBleGFtcGxlLm9yZzCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAOMgn8lj/impDiHgTUxCy8yfKYxzXfeIvYFiH7Kj
+lU06WCiv8D6qp8LGUrmUn2tY1poItF9g+/Hq50mNRjXi6YKfIERBgqf6q4IbA3/w
+Tng4NyCdZ0PA4o8JBz9/lhN6ZMWQE4dxbe3nKDoFSOvW5ifaRvmkXGZJVl+Ih04K
+i/7qBabBcrmU1Y7UmhhYrFYbND7DUAZd8z2FkyyLPzPmMhSSnv38XYpxGyBnQ+By
+/E4xxrcDmJnnle98WjDPwaRDQvu+G6cI1eC1shD/D+EN7j6yBAWGHnKp1haEN3Mo
+Xdk8/fOZGNyQg1kjkLwlMw8jSJ3Sl6CslE+OMSLMdIP3MZ0CAwEAAaOBlDCBkTAJ
+BgNVHRMEAjAAMB0GA1UdDgQWBBQjHFqZGiu8/TmXjR9aSb9PMw8mwTAfBgNVHSME
+GDAWgBSFV9D/qbQeH4Az+7g07X0GOc00mDATBgNVHSUEDDAKBggrBgEFBQcDAjAv
+BgNVHREEKDAmggEqgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEw
+DQYJKoZIhvcNAQELBQADggEBAE15wAesE1EAXEtwbZ+/h8isMck3Xk5Mn8bNpOPf
+crUGKJ32PjK1AYFDeG2TsrIKC5Vk8iWkXtFLsRFcVBchp/fmc6/yU1SzaUCMJlsb
+p2MHJsTSxHpks6vxI/pYnLK3FzU0kd2Eu7Dups14zzI52F8jrWLvgjiIzTQbfTsC
+qHVwclAzRKRlARTveEY7J06C5gEbXGWXLAj3T+bu3RxADEhZM17H2r9AzrDpA5Vv
+qAezf2sVzcBvV+NzmWeq/ZBsp2//uV/2ioyT8cN1NBDGbA6uCiJrFmxWQQq15nRS
+uD7y5PzxVKCEkNWXcCVLKCyK7EYKY6wyxs2Wce72FyzpYF4=
 -----END CERTIFICATE-----
diff --git a/integration/testdata/https/client-key.pem b/integration/testdata/https/client-key.pem
index b5c15f8dc7d8e..47bedbe39b5ab 100644
--- a/integration/testdata/https/client-key.pem
+++ b/integration/testdata/https/client-key.pem
@@ -1,16 +1,27 @@
------BEGIN PRIVATE KEY-----
-MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMrJBdAJTj6k/NUU
-9KXoNNNrUePzYuqh8OjtxCq88E/KB9/jiPr0IZk1Dj3qsIbnxNKKgytCuOyjmWJw
-gUbM/KUd0mPo6wclmuIlbRFW8hpRobY+HFcy6XssqhvMly2JLbHJXjUoTXz6ZTE+
-93Ddbgs8WK+oLiTAfk54fQqej0JDAgMBAAECgYBOFEzKp2qbMEexe9ofL2N3rDDh
-xkrl8OijpzkLA6i78BxMFn4dsnZlWUpciMrjhsYAExkiRRSS+QMMJimAq1jzQqc3
-FAQV2XGYwkd0cUn7iZGvfNnEPysjsfyYQM+m+sT0ATj4BZjVShC6kkSjTdm1leLN
-OSvcHdcu3Xxg9ufF0QJBAPYdnNt5sIndt2WECePuRVi+uF4mlxTobFY0fjn26yhC
-4RsnhhD3Vldygo9gvnkwrAZYaALGSPBewes2InxvjA8CQQDS7erKiNXpwoqz5XiU
-SVEsIIVTdWzBjGbIqMOu/hUwM5FK4j6JTBks0aTGMyh0YV9L1EzM0X79J29JahCe
-iQKNAkBKNMOGqTpBV0hko1sYDk96YobUXG5RL4L6uvkUIQ7mJMQam+AgXXL7Ctuy
-v0iu4a38e8tgisiTMP7nHHtpaXihAkAOiN54/lzfMsykANgCP9scE1GcoqbP34Dl
-qttxH4kOPT9xzY1JoLjLYdbc4YGUI3GRpBt2sajygNkmUey7P+2xAkBBsVCZFvTw
-qHvOpPS2kX5ml5xoc/QAHK9N7kR+X7XFYx82RTVSqJEK4lPb+aEWn+CjiIewO4Q5
-ksDFuNxAzbhl
------END PRIVATE KEY-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpgIBAAKCAQEA4yCfyWP+KakOIeBNTELLzJ8pjHNd94i9gWIfsqOVTTpYKK/w
+PqqnwsZSuZSfa1jWmgi0X2D78ernSY1GNeLpgp8gREGCp/qrghsDf/BOeDg3IJ1n
+Q8DijwkHP3+WE3pkxZATh3Ft7ecoOgVI69bmJ9pG+aRcZklWX4iHTgqL/uoFpsFy
+uZTVjtSaGFisVhs0PsNQBl3zPYWTLIs/M+YyFJKe/fxdinEbIGdD4HL8TjHGtwOY
+meeV73xaMM/BpENC+74bpwjV4LWyEP8P4Q3uPrIEBYYecqnWFoQ3cyhd2Tz985kY
+3JCDWSOQvCUzDyNIndKXoKyUT44xIsx0g/cxnQIDAQABAoIBAQCZ0oGFIlyDGISC
+uud+64oc9fpsrcGJIKm/k5YGJTW7jPUh8S4TMv7VMf3aw+ZIDG2i+pw2MHfRepbT
+wIM5gYlGNsDimT+ExocbYXI4Vqa+Usw7IX9LarnFx4aKIb2hSXYwOwiO5WpfAfvD
+d8rQNsW/XdxNvFv7xlVh9BQ27Xus0sjz7dNBSt+LQ4hSyfZgFwbXh1+E9k6PDhnX
+oYFz4/U/1G+HwXKivvKcRIkYZpMyD80H/M4+bB9x6btFvb4+R3K6Ii8wh+VMz5pX
+Nm+mN8d3W/7Mhyof8EbbQpJMdwemzI7lM6wf1FPfSEeKXAclJ3+BnjOuh295Jv4Z
+u+YWhzDhAoGBAPcRWiVyU7US7K4dhbVo2zyM6mGO3gFBXgeSwFFby8kMsbi8aewt
+m90WMdWjvITw/sNsIRye7sCUVXOmGgz+5UfxRKtcFB5JnfLymrmQS7y1+TZ1WRak
+T0400U+VEE4Cw3vkd4lFbyu94P4iDmn816Ix6tR8UTt11wMG3NgVoFSFAoGBAOtW
+uKYN58BXWA3nU9rPEKq7n1cx7ML/xFvIPPNWp+6Pc1EJ2yX7tnyhPzvPdm3+XdTz
+PU0oIBVfKNToPqYJRX8kK4hCYPvgOOAccZkSrxe3jBupKW72BwVvl/wN6Yb7ggda
+NMbsQ85XyF4K9bvIFxROrR1K/nsowO0UaLvtEOA5AoGBALf2pabIT+e95Zlnxg1j
+vAqD6mkl1cwdfgQpkyWBMmXLG/Gv6TbAZxPh2M14k4BxaWDdfHIxLRkb2dy4yyDu
+Eo7U6QqnDxvWONOTLP4KoTosTRntmp4vThWvYkLdfTx49lGjthXyK2rogUT42r60
+U2Mjw/TfdCTQA37vdzU2NSF5AoGBAOZQpsEMVsRsNqbUv8IiZ8NPf2+MUpO8T+Ur
+IEtdgVf9V/P1W13e7Acon4PfU53uFNJ1gobiQBPqX0GOUNGZvUPimB/wJo4aME9U
+RvBx0p25agsgEIahjNmLDwkEbIlH10duxrvvOaTVUCiJPVibR8r9/HnwjQDnL3hW
+QvG33o4xAoGBAOsX3ABiBnXzqotDlqgoofgmv7zGkjZByiGQz3d+nL74ucRmZRgX
+aeYb14YbJ1I1sGj2u2fPa4P1EJ0RnjgYkaQ7c0ZyTXceS+2/LtJ56RvRepKKs+Yg
+fX1EruZYZvoDW+AViWF784CzpIpmedgB7dbXJPahTh0Q76OWQdP3T/uh
+-----END RSA PRIVATE KEY-----
diff --git a/integration/testdata/https/server-cert.pem b/integration/testdata/https/server-cert.pem
index 08abfd1a3b08f..0616daba71e0a 100644
--- a/integration/testdata/https/server-cert.pem
+++ b/integration/testdata/https/server-cert.pem
@@ -1,76 +1,86 @@
 Certificate:
     Data:
         Version: 3 (0x2)
-        Serial Number: 4 (0x4)
-    Signature Algorithm: sha1WithRSAEncryption
-        Issuer: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=changeme/name=changeme/emailAddress=mail@host.domain
+        Serial Number: 2 (0x2)
+        Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=US, ST=CA, L=SanFrancisco, O=Moby-project, OU=ci, CN=moby-ci/name=moby/emailAddress=moby@example.org
         Validity
-            Not Before: Dec  4 15:01:20 2013 GMT
-            Not After : Dec  2 15:01:20 2023 GMT
-        Subject: C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=changeme, CN=*/name=changeme/emailAddress=mail@host.domain
+            Not Before: May 17 19:49:34 2021 GMT
+            Not After : May 17 19:49:34 2031 GMT
+        Subject: C=US, ST=CA, L=SanFrancisco, O=Moby-project, OU=ci, CN=server/name=moby/emailAddress=moby@example.org
         Subject Public Key Info:
             Public Key Algorithm: rsaEncryption
-                Public-Key: (1024 bit)
+                RSA Public-Key: (2048 bit)
                 Modulus:
-                    00:c1:ff:7d:30:6f:64:4a:b1:92:b1:71:d1:c1:74:
-                    e2:1d:db:2d:11:24:e1:00:d4:00:ae:6f:c8:9e:ae:
-                    67:b3:4a:bd:f7:e6:9e:57:6d:19:4c:3c:23:94:2d:
-                    3d:d6:63:84:d8:fa:76:2b:38:12:c1:ed:20:9d:32:
-                    e0:e8:c2:bf:9a:77:70:04:3f:7f:ca:8c:2c:82:d6:
-                    3d:25:5c:02:1a:4f:64:93:03:dd:9c:42:97:5e:09:
-                    49:af:f0:c2:e1:30:08:0e:21:46:95:d1:13:59:c0:
-                    c8:76:be:94:0d:8b:43:67:21:33:b2:08:60:9d:76:
-                    a8:05:32:1e:f9:95:09:14:75
+                    00:f2:23:b2:a3:22:03:a2:0b:cd:71:de:19:29:14:
+                    92:7f:e8:9d:30:7f:e3:0e:13:da:de:f9:9b:5a:65:
+                    ec:22:c5:ce:73:e7:2f:c2:ae:c3:04:eb:72:43:77:
+                    87:46:d2:63:e2:3a:08:85:9f:58:1f:fc:f3:82:4f:
+                    5e:4e:5a:92:0f:ac:a1:16:a0:7e:92:a3:8e:aa:93:
+                    fd:4c:e0:ed:f0:96:09:43:b8:e6:ec:72:1b:aa:aa:
+                    76:3f:79:00:89:26:c4:2f:ff:99:01:95:f2:8e:39:
+                    a0:4f:13:63:bf:6b:6c:40:0f:7c:ed:ee:a8:2b:90:
+                    11:94:d8:a9:15:c1:91:40:89:13:eb:49:ec:0d:fe:
+                    4f:cd:41:8f:a6:e0:ab:15:db:45:86:28:23:79:98:
+                    42:bb:52:a8:96:c3:aa:91:df:5a:67:24:09:4b:2e:
+                    ce:9a:ba:fc:97:4e:89:5e:c3:18:08:4e:31:e4:1c:
+                    b6:65:c2:7e:93:ef:52:e7:92:ee:25:88:07:4a:d5:
+                    3d:86:44:31:07:e5:1a:f5:63:dc:c3:11:b5:4d:10:
+                    a0:9c:6a:99:7a:d9:b4:22:07:97:e2:f4:0e:5a:10:
+                    bc:90:09:c1:0f:5a:65:e8:f3:9c:e0:e2:04:29:24:
+                    ee:a7:ee:aa:fa:02:7f:80:ac:9d:ca:9f:0f:8d:f5:
+                    c5:b3
                 Exponent: 65537 (0x10001)
         X509v3 extensions:
             X509v3 Basic Constraints: 
                 CA:FALSE
-            Netscape Cert Type: 
-                SSL Server
-            Netscape Comment: 
-                Easy-RSA Generated Server Certificate
             X509v3 Subject Key Identifier: 
-                14:02:FD:FD:DD:13:38:E0:71:EA:D1:BE:C0:0E:89:1A:2D:B6:19:06
+                82:DD:B4:72:E4:DB:12:4E:9A:3B:45:75:F0:1B:4E:7F:43:2C:10:BF
             X509v3 Authority Key Identifier: 
-                keyid:66:EE:C3:17:3D:3D:AB:44:01:6B:6F:B2:99:19:BD:AA:02:B5:34:FB
-                DirName:/C=US/ST=CA/L=SanFrancisco/O=Fort-Funston/OU=changeme/CN=changeme/name=changeme/emailAddress=mail@host.domain
-                serial:FD:AB:EC:6A:84:27:04:A7
+                keyid:85:57:D0:FF:A9:B4:1E:1F:80:33:FB:B8:34:ED:7D:06:39:CD:34:98
 
             X509v3 Extended Key Usage: 
                 TLS Web Server Authentication
-            X509v3 Key Usage: 
-                Digital Signature, Key Encipherment
-    Signature Algorithm: sha1WithRSAEncryption
-         40:0f:10:39:c4:b7:0f:0d:2f:bf:d2:16:cc:8e:d3:9a:fb:8b:
-         ce:4b:7b:0d:48:77:ce:f1:fe:d5:8f:ea:b1:71:ed:49:1d:9f:
-         23:3a:16:d4:70:7c:c5:29:bf:e4:90:34:d0:f0:00:24:f4:e4:
-         df:2c:c3:83:01:66:61:c9:a8:ab:29:e7:98:6d:27:89:4a:76:
-         c9:2e:19:8e:fe:6e:d5:f8:99:11:0e:97:67:4b:34:e3:1e:e3:
-         9f:35:00:a5:32:f9:b5:2c:f2:e0:c5:2e:cc:81:bd:18:dd:5c:
-         12:c8:6b:fa:0c:17:74:30:55:f6:6e:20:9a:6c:1e:09:b4:0c:
-         15:42
+            X509v3 Subject Alternative Name: 
+                DNS:*, DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
+    Signature Algorithm: sha256WithRSAEncryption
+         1e:a5:f6:ed:f9:8b:a4:c8:1d:11:e3:03:3b:ec:6a:a2:59:44:
+         35:d1:28:0a:0e:b5:84:3c:17:3b:38:6f:e5:8c:03:4c:70:13:
+         b8:cf:40:3c:4a:5d:bf:96:a6:ca:26:9d:ce:00:13:10:a9:eb:
+         91:b4:50:98:a2:68:6f:6b:95:54:46:39:97:74:d6:fd:bb:54:
+         f4:27:91:b7:4e:9f:bc:85:5f:51:69:59:87:86:7e:1d:06:10:
+         74:f5:c3:e3:81:09:e6:77:f5:b7:ed:ae:1c:b0:56:2e:8d:31:
+         60:ff:ef:f5:ab:03:fb:da:9a:69:d8:8a:ca:e7:00:99:d5:9f:
+         39:f7:d5:19:4c:57:a1:90:23:c8:21:a3:9b:ab:05:d4:b7:a8:
+         7c:12:a9:6e:d5:c3:ae:e0:c0:2c:08:95:da:16:c4:35:e0:89:
+         3b:01:f1:f7:b2:d8:15:b6:05:7f:ec:09:fd:0a:5f:a9:48:16:
+         11:c1:30:0a:fd:98:71:69:03:91:19:5f:02:14:d7:42:75:fb:
+         b7:01:af:c2:09:08:4c:7b:c9:d2:bc:0f:2d:de:57:84:9d:8e:
+         a8:f0:22:7e:eb:05:6e:f3:5b:cd:2f:1f:67:b4:3a:2f:b4:b1:
+         a6:bd:78:0f:c4:65:c5:01:7a:06:b2:63:3e:a0:de:a7:ef:84:
+         cc:17:4b:22
 -----BEGIN CERTIFICATE-----
-MIIEKjCCA5OgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMCVVMx
-CzAJBgNVBAgTAkNBMRUwEwYDVQQHEwxTYW5GcmFuY2lzY28xFTATBgNVBAoTDEZv
-cnQtRnVuc3RvbjERMA8GA1UECxMIY2hhbmdlbWUxETAPBgNVBAMTCGNoYW5nZW1l
-MREwDwYDVQQpEwhjaGFuZ2VtZTEfMB0GCSqGSIb3DQEJARYQbWFpbEBob3N0LmRv
-bWFpbjAeFw0xMzEyMDQxNTAxMjBaFw0yMzEyMDIxNTAxMjBaMIGbMQswCQYDVQQG
-EwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UE
-ChMMRm9ydC1GdW5zdG9uMREwDwYDVQQLEwhjaGFuZ2VtZTEKMAgGA1UEAxQBKjER
-MA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1haWxAaG9zdC5kb21h
-aW4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMH/fTBvZEqxkrFx0cF04h3b
-LREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y4OjCv5p3
-cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+lA2LQ2ch
-M7IIYJ12qAUyHvmVCRR1AgMBAAGjggFzMIIBbzAJBgNVHRMEAjAAMBEGCWCGSAGG
-+EIBAQQEAwIGQDA0BglghkgBhvhCAQ0EJxYlRWFzeS1SU0EgR2VuZXJhdGVkIFNl
-cnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUFAL9/d0TOOBx6tG+wA6JGi22GQYw
-gdcGA1UdIwSBzzCBzIAUZu7DFz09q0QBa2+ymRm9qgK1NPuhgaikgaUwgaIxCzAJ
-BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMRUw
-EwYDVQQKEwxGb3J0LUZ1bnN0b24xETAPBgNVBAsTCGNoYW5nZW1lMREwDwYDVQQD
-EwhjaGFuZ2VtZTERMA8GA1UEKRMIY2hhbmdlbWUxHzAdBgkqhkiG9w0BCQEWEG1h
-aWxAaG9zdC5kb21haW6CCQD9q+xqhCcEpzATBgNVHSUEDDAKBggrBgEFBQcDATAL
-BgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAQA8QOcS3Dw0vv9IWzI7TmvuL
-zkt7DUh3zvH+1Y/qsXHtSR2fIzoW1HB8xSm/5JA00PAAJPTk3yzDgwFmYcmoqynn
-mG0niUp2yS4Zjv5u1fiZEQ6XZ0s04x7jnzUApTL5tSzy4MUuzIG9GN1cEshr+gwX
-dDBV9m4gmmweCbQMFUI=
+MIIEPzCCAyegAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBlzELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgMAkNBMRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xFTATBgNVBAoMDE1v
+YnktcHJvamVjdDELMAkGA1UECwwCY2kxEDAOBgNVBAMMB21vYnktY2kxDTALBgNV
+BCkMBG1vYnkxHzAdBgkqhkiG9w0BCQEWEG1vYnlAZXhhbXBsZS5vcmcwHhcNMjEw
+NTE3MTk0OTM0WhcNMzEwNTE3MTk0OTM0WjCBljELMAkGA1UEBhMCVVMxCzAJBgNV
+BAgMAkNBMRUwEwYDVQQHDAxTYW5GcmFuY2lzY28xFTATBgNVBAoMDE1vYnktcHJv
+amVjdDELMAkGA1UECwwCY2kxDzANBgNVBAMMBnNlcnZlcjENMAsGA1UEKQwEbW9i
+eTEfMB0GCSqGSIb3DQEJARYQbW9ieUBleGFtcGxlLm9yZzCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAPIjsqMiA6ILzXHeGSkUkn/onTB/4w4T2t75m1pl
+7CLFznPnL8KuwwTrckN3h0bSY+I6CIWfWB/884JPXk5akg+soRagfpKjjqqT/Uzg
+7fCWCUO45uxyG6qqdj95AIkmxC//mQGV8o45oE8TY79rbEAPfO3uqCuQEZTYqRXB
+kUCJE+tJ7A3+T81Bj6bgqxXbRYYoI3mYQrtSqJbDqpHfWmckCUsuzpq6/JdOiV7D
+GAhOMeQctmXCfpPvUueS7iWIB0rVPYZEMQflGvVj3MMRtU0QoJxqmXrZtCIHl+L0
+DloQvJAJwQ9aZejznODiBCkk7qfuqvoCf4CsncqfD431xbMCAwEAAaOBlDCBkTAJ
+BgNVHRMEAjAAMB0GA1UdDgQWBBSC3bRy5NsSTpo7RXXwG05/QywQvzAfBgNVHSME
+GDAWgBSFV9D/qbQeH4Az+7g07X0GOc00mDATBgNVHSUEDDAKBggrBgEFBQcDATAv
+BgNVHREEKDAmggEqgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEw
+DQYJKoZIhvcNAQELBQADggEBAB6l9u35i6TIHRHjAzvsaqJZRDXRKAoOtYQ8Fzs4
+b+WMA0xwE7jPQDxKXb+Wpsomnc4AExCp65G0UJiiaG9rlVRGOZd01v27VPQnkbdO
+n7yFX1FpWYeGfh0GEHT1w+OBCeZ39bftrhywVi6NMWD/7/WrA/vammnYisrnAJnV
+nzn31RlMV6GQI8gho5urBdS3qHwSqW7Vw67gwCwIldoWxDXgiTsB8fey2BW2BX/s
+Cf0KX6lIFhHBMAr9mHFpA5EZXwIU10J1+7cBr8IJCEx7ydK8Dy3eV4SdjqjwIn7r
+BW7zW80vH2e0Oi+0saa9eA/EZcUBegayYz6g3qfvhMwXSyI=
 -----END CERTIFICATE-----
diff --git a/integration/testdata/https/server-key.pem b/integration/testdata/https/server-key.pem
index c269320ef0f75..1ba75cd137bdf 100644
--- a/integration/testdata/https/server-key.pem
+++ b/integration/testdata/https/server-key.pem
@@ -1,16 +1,27 @@
------BEGIN PRIVATE KEY-----
-MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMH/fTBvZEqxkrFx
-0cF04h3bLREk4QDUAK5vyJ6uZ7NKvffmnldtGUw8I5QtPdZjhNj6dis4EsHtIJ0y
-4OjCv5p3cAQ/f8qMLILWPSVcAhpPZJMD3ZxCl14JSa/wwuEwCA4hRpXRE1nAyHa+
-lA2LQ2chM7IIYJ12qAUyHvmVCRR1AgMBAAECgYAmwckb9RUfSwyYgLm8IYLPHiuJ
-wkllZfVg5Bo7gXJcQnFjZmJ56uTj8xvUjZlODIHM63TSO5ibv6kFXtXKCqZGd2M+
-wGbhZ0f+2GvKcwMmJERnIQjuoNaYSQLT0tM0VB9Iz0rJlZC+tzPZ+5pPqEumRdsS
-IzWNXfF42AhcbwAQYQJBAPVXtMYIJc9EZsz86ZcQiMPWUpCX5vnRmtwL8kKyR8D5
-4KfYeiowyFffSRMMcclwNHq7TgSXN+nIXM9WyzyzwikCQQDKbNA28AgZp9aT54HP
-WnbeE2pmt+uk/zl/BtxJSoK6H+69Jec+lf7EgL7HgOWYRSNot4uQWu8IhsHLTiUq
-+0FtAkEAqwlRxRy4/x24bP+D+QRV0/D97j93joFJbE4Hved7jlSlAV4xDGilwlyv
-HNB4Iu5OJ6Gcaibhm+FKkmD3noHSwQJBAIpu3fokLzX0bS+bDFBU6qO3HXX/47xj
-+tsfQvkwZrSI8AkU6c8IX0HdVhsz0FBRQAT2ORDQz1XCarfxykNZrwUCQQCGCBIc
-BBCWzhHlswlGidWJg3HqqO6hPPClEr3B5G87oCsdeYwiO23XT6rUnoJXfJHp6oCW
-5nCwDu5ZTP+khltg
------END PRIVATE KEY-----
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA8iOyoyIDogvNcd4ZKRSSf+idMH/jDhPa3vmbWmXsIsXOc+cv
+wq7DBOtyQ3eHRtJj4joIhZ9YH/zzgk9eTlqSD6yhFqB+kqOOqpP9TODt8JYJQ7jm
+7HIbqqp2P3kAiSbEL/+ZAZXyjjmgTxNjv2tsQA987e6oK5ARlNipFcGRQIkT60ns
+Df5PzUGPpuCrFdtFhigjeZhCu1KolsOqkd9aZyQJSy7Omrr8l06JXsMYCE4x5By2
+ZcJ+k+9S55LuJYgHStU9hkQxB+Ua9WPcwxG1TRCgnGqZetm0IgeX4vQOWhC8kAnB
+D1pl6POc4OIEKSTup+6q+gJ/gKydyp8PjfXFswIDAQABAoIBAEKIvJVGy2jDhXg8
+Zv16waaT7F1fRqyfmAyc2atFRlVntQr0A5OjIcNATu1q8qjrNrb660yMNFLV1rN/
+y5IMIQZdkQX+o8j3WERW1ctCIx9wmqsZK5rc3+1NWaCnRxZoqI/n08szwKqD+yC/
+WzFF+0C/AL0ATwVpWOtlfVCVF6x7dTkTqtc+65C/nw86ymp6cDq7Fh39d0deD8HB
+h4zxGnvTr4jFGkFWg6Vq4XdzKdVeVz/Njw19wJjdi/6Q3aC90APlJ6nuX3OWv6VV
+/Xs9rXqIUS4bhYyRrzCQ5Y/vINZCx3ekKynfghul4NDE4zo1GQm3E+7tVYZ6ll+9
++uHeUwECgYEA81NxVe5ViOWa4NFXucAgiNGl7KkN9/gDh77weXzGPhZDBULTv4OC
+yKokQOnn41qF5eq+YRKr+B733fGEhwJRoEyoyXSDpgTxdJHKmohdxGfsRoOEMO4Z
+ALm9+XmJYTq11l01M5Jqn36Smz5+iXAD7QVQdZMnA++IaBs2/uTuEQMCgYEA/sBv
+GweGKfdM11ZckNG8ocrAhkttq+5V9uFcGcpBmw08vu/Woy+L1dFvyUa7Hc/l6fe1
+PLdTvNaSK6mP/gfeevwNlS1NUVLtdnOq9cl/1/xfqi8Cj46VUTqRaEQMKHCeXyuA
+A3N1k06hMuW/bYstspyWvGyjsWth5QT7MNjkYZECgYEAoWcoNqfxdO1Y3uf+GOio
+rBjkxyccbO/G57RwLyXlGioKKuM5MkA58IFrquN9PgI971TXE+0exWdFY6NhFW0k
+WACBtZ/j86wzve83RWpPSIjm4Z87gHlvfFu4+FL2Hdij5Z3OPHdS4plDBldd+Cyl
+bgOoa1VA/AtXoDbtNAcHI6ECgYB7b6ymMSgd73jpIixp82ZuErrkl2nFlA9NN3cT
++/977JcRgU7D9UbRTNDYexAxasnhaygDCmVlq6ZZx6hAk3mGp9jA/plnHUJ4UaV1
+wLPUaLHF2U9pVdId8L4CAm7NrXvfa0l04p4GyWOsMMxnfLegwuT62b0bO4fMm3RG
+/+DxMQKBgQDTz8wKQFRWuDYshcRKJ8GB7Czgb4aFU0iTPFC4ql1HXkiGePDXyWjC
++6JQ3dTadn8HA540nCOWWvpgWeSCec9Q5YslpIdupMKpCx/zUfZZ2Y2/y+yJdQCr
+gGDf7LxhxBHQFyCLM9KuTdCzhQPsHVFHFrfr1UdDEe/e9QaJAyUw7Q==
+-----END RSA PRIVATE KEY-----
diff --git a/integration/volume/main_test.go b/integration/volume/main_test.go
index 206f7377ae06f..e3e99352078ce 100644
--- a/integration/volume/main_test.go
+++ b/integration/volume/main_test.go
@@ -5,7 +5,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/docker/docker/internal/test/environment"
+	"github.com/docker/docker/testutil/environment"
 )
 
 var testEnv *environment.Execution
diff --git a/integration/volume/volume_test.go b/integration/volume/volume_test.go
index 0906c535f7515..f465e47b05c84 100644
--- a/integration/volume/volume_test.go
+++ b/integration/volume/volume_test.go
@@ -2,7 +2,8 @@ package volume
 
 import (
 	"context"
-	"fmt"
+	"net/http"
+	"path/filepath"
 	"strings"
 	"testing"
 	"time"
@@ -11,21 +12,22 @@ import (
 	"github.com/docker/docker/api/types/filters"
 	volumetypes "github.com/docker/docker/api/types/volume"
 	"github.com/docker/docker/integration/internal/container"
-	"github.com/docker/docker/internal/test/request"
+	"github.com/docker/docker/testutil/request"
 	"github.com/google/go-cmp/cmp/cmpopts"
-	"gotest.tools/assert"
-	is "gotest.tools/assert/cmp"
-	"gotest.tools/skip"
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
 )
 
 func TestVolumesCreateAndList(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
-	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	name := t.Name()
+	// Windows file system is case insensitive
+	if testEnv.OSType == "windows" {
+		name = strings.ToLower(name)
+	}
 	vol, err := client.VolumeCreate(ctx, volumetypes.VolumeCreateBody{
 		Name: name,
 	})
@@ -37,27 +39,34 @@ func TestVolumesCreateAndList(t *testing.T) {
 		Driver:     "local",
 		Scope:      "local",
 		Name:       name,
-		Mountpoint: fmt.Sprintf("%s/volumes/%s/_data", testEnv.DaemonInfo.DockerRootDir, name),
+		Mountpoint: filepath.Join(testEnv.DaemonInfo.DockerRootDir, "volumes", name, "_data"),
 	}
 	assert.Check(t, is.DeepEqual(vol, expected, cmpopts.EquateEmpty()))
 
-	volumes, err := client.VolumeList(ctx, filters.Args{})
+	volList, err := client.VolumeList(ctx, filters.Args{})
 	assert.NilError(t, err)
+	assert.Assert(t, len(volList.Volumes) > 0)
+
+	volumes := volList.Volumes[:0]
+	for _, v := range volList.Volumes {
+		if v.Name == vol.Name {
+			volumes = append(volumes, v)
+		}
+	}
 
-	assert.Check(t, is.Equal(len(volumes.Volumes), 1))
-	assert.Check(t, volumes.Volumes[0] != nil)
-	assert.Check(t, is.DeepEqual(*volumes.Volumes[0], expected, cmpopts.EquateEmpty()))
+	assert.Check(t, is.Equal(len(volumes), 1))
+	assert.Check(t, volumes[0] != nil)
+	assert.Check(t, is.DeepEqual(*volumes[0], expected, cmpopts.EquateEmpty()))
 }
 
 func TestVolumesRemove(t *testing.T) {
-	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 
-	id := container.Create(t, ctx, client, container.WithVolume(prefix+slash+"foo"))
+	id := container.Create(ctx, t, client, container.WithVolume(prefix+slash+"foo"))
 
 	c, err := client.ContainerInspect(ctx, id)
 	assert.NilError(t, err)
@@ -76,39 +85,52 @@ func TestVolumesRemove(t *testing.T) {
 }
 
 func TestVolumesInspect(t *testing.T) {
-	skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
-	skip.If(t, testEnv.OSType == "windows", "FIXME")
 	defer setupTest(t)()
-	client := request.NewAPIClient(t)
+	client := testEnv.APIClient()
 	ctx := context.Background()
 
-	// sampling current time minus a minute so to now have false positive in case of delays
-	now := time.Now().Truncate(time.Minute)
-
-	name := t.Name()
-	_, err := client.VolumeCreate(ctx, volumetypes.VolumeCreateBody{
-		Name: name,
-	})
+	now := time.Now()
+	vol, err := client.VolumeCreate(ctx, volumetypes.VolumeCreateBody{})
 	assert.NilError(t, err)
 
-	vol, err := client.VolumeInspect(ctx, name)
+	inspected, err := client.VolumeInspect(ctx, vol.Name)
 	assert.NilError(t, err)
 
-	expected := types.Volume{
-		// Ignore timestamp of CreatedAt
-		CreatedAt:  vol.CreatedAt,
-		Driver:     "local",
-		Scope:      "local",
-		Name:       name,
-		Mountpoint: fmt.Sprintf("%s/volumes/%s/_data", testEnv.DaemonInfo.DockerRootDir, name),
-	}
-	assert.Check(t, is.DeepEqual(vol, expected, cmpopts.EquateEmpty()))
+	assert.Check(t, is.DeepEqual(inspected, vol, cmpopts.EquateEmpty()))
 
-	// comparing CreatedAt field time for the new volume to now. Removing a minute from both to avoid false positive
-	testCreatedAt, err := time.Parse(time.RFC3339, strings.TrimSpace(vol.CreatedAt))
+	// comparing CreatedAt field time for the new volume to now. Truncate to 1 minute precision to avoid false positive
+	createdAt, err := time.Parse(time.RFC3339, strings.TrimSpace(inspected.CreatedAt))
 	assert.NilError(t, err)
-	testCreatedAt = testCreatedAt.Truncate(time.Minute)
-	assert.Check(t, is.Equal(testCreatedAt.Equal(now), true), "Time Volume is CreatedAt not equal to current time")
+	assert.Check(t, createdAt.Unix()-now.Unix() < 60, "CreatedAt (%s) exceeds creation time (%s) 60s", createdAt, now)
+}
+
+func TestVolumesInvalidJSON(t *testing.T) {
+	defer setupTest(t)()
+
+	endpoints := []string{"/volumes/create"}
+
+	for _, ep := range endpoints {
+		ep := ep
+		t.Run(ep, func(t *testing.T) {
+			t.Parallel()
+
+			res, body, err := request.Post(ep, request.RawString("{invalid json"), request.JSON)
+			assert.NilError(t, err)
+			assert.Equal(t, res.StatusCode, http.StatusBadRequest)
+
+			buf, err := request.ReadBody(body)
+			assert.NilError(t, err)
+			assert.Check(t, is.Contains(string(buf), "invalid character 'i' looking for beginning of object key string"))
+
+			res, body, err = request.Post(ep, request.JSON)
+			assert.NilError(t, err)
+			assert.Equal(t, res.StatusCode, http.StatusBadRequest)
+
+			buf, err = request.ReadBody(body)
+			assert.NilError(t, err)
+			assert.Check(t, is.Contains(string(buf), "got EOF while reading request body"))
+		})
+	}
 }
 
 func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
diff --git a/internal/procfs/procfs_linux.go b/internal/procfs/procfs_linux.go
deleted file mode 100644
index 8a681108789ff..0000000000000
--- a/internal/procfs/procfs_linux.go
+++ /dev/null
@@ -1,105 +0,0 @@
-package procfs
-
-/*
-Copyright 2015 The Kubernetes Authors.
-
-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.
-*/
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"regexp"
-	"strconv"
-	"strings"
-	"unicode"
-
-	"github.com/sirupsen/logrus"
-)
-
-// PidOf finds process(es) with a specified name (regexp match)
-// and return their pid(s)
-func PidOf(name string) ([]int, error) {
-	if len(name) == 0 {
-		return []int{}, fmt.Errorf("name should not be empty")
-	}
-	re, err := regexp.Compile("(^|/)" + name + "$")
-	if err != nil {
-		return []int{}, err
-	}
-	return getPids(re), nil
-}
-
-func getPids(re *regexp.Regexp) []int {
-	pids := []int{}
-
-	dirFD, err := os.Open("/proc")
-	if err != nil {
-		return nil
-	}
-	defer dirFD.Close()
-
-	for {
-		// Read a small number at a time in case there are many entries, we don't want to
-		// allocate a lot here.
-		ls, err := dirFD.Readdir(10)
-		if err == io.EOF {
-			break
-		}
-		if err != nil {
-			return nil
-		}
-
-		for _, entry := range ls {
-			if !entry.IsDir() {
-				continue
-			}
-
-			// If the directory is not a number (i.e. not a PID), skip it
-			pid, err := strconv.Atoi(entry.Name())
-			if err != nil {
-				continue
-			}
-
-			cmdline, err := ioutil.ReadFile(filepath.Join("/proc", entry.Name(), "cmdline"))
-			if err != nil {
-				logrus.Infof("Error reading file %s: %+v", filepath.Join("/proc", entry.Name(), "cmdline"), err)
-				continue
-			}
-
-			// The bytes we read have '\0' as a separator for the command line
-			parts := bytes.SplitN(cmdline, []byte{0}, 2)
-			if len(parts) == 0 {
-				continue
-			}
-			// Split the command line itself we are interested in just the first part
-			exe := strings.FieldsFunc(string(parts[0]), func(c rune) bool {
-				return unicode.IsSpace(c) || c == ':'
-			})
-			if len(exe) == 0 {
-				continue
-			}
-			// Check if the name of the executable is what we are looking for
-			if re.MatchString(exe[0]) {
-				// Grab the PID from the directory path
-				pids = append(pids, pid)
-			}
-		}
-	}
-
-	return pids
-}
diff --git a/internal/procfs/procfs_linux_test.go b/internal/procfs/procfs_linux_test.go
deleted file mode 100644
index 4c5d822f76e03..0000000000000
--- a/internal/procfs/procfs_linux_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package procfs
-
-import (
-	"os"
-	"path/filepath"
-	"regexp"
-	"runtime"
-	"testing"
-
-	"gotest.tools/assert"
-)
-
-func TestPidOf(t *testing.T) {
-	pids, err := PidOf(filepath.Base(os.Args[0]))
-	assert.NilError(t, err)
-	assert.Check(t, len(pids) == 1)
-	assert.DeepEqual(t, pids[0], os.Getpid())
-}
-
-func BenchmarkGetPids(b *testing.B) {
-	if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
-		b.Skipf("not supported on GOOS=%s", runtime.GOOS)
-	}
-
-	re, err := regexp.Compile("(^|/)" + filepath.Base(os.Args[0]) + "$")
-	assert.Check(b, err == nil)
-
-	for i := 0; i < b.N; i++ {
-		pids := getPids(re)
-
-		b.StopTimer()
-		assert.Check(b, len(pids) > 0)
-		assert.Check(b, pids[0] == os.Getpid())
-		b.StartTimer()
-	}
-}
diff --git a/internal/test/daemon/config.go b/internal/test/daemon/config.go
deleted file mode 100644
index ce99222b37eac..0000000000000
--- a/internal/test/daemon/config.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package daemon
-
-import (
-	"context"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/swarm"
-	"github.com/docker/docker/internal/test"
-	"gotest.tools/assert"
-)
-
-// ConfigConstructor defines a swarm config constructor
-type ConfigConstructor func(*swarm.Config)
-
-// CreateConfig creates a config given the specified spec
-func (d *Daemon) CreateConfig(t assert.TestingT, configSpec swarm.ConfigSpec) string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	scr, err := cli.ConfigCreate(context.Background(), configSpec)
-	assert.NilError(t, err)
-	return scr.ID
-}
-
-// ListConfigs returns the list of the current swarm configs
-func (d *Daemon) ListConfigs(t assert.TestingT) []swarm.Config {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	configs, err := cli.ConfigList(context.Background(), types.ConfigListOptions{})
-	assert.NilError(t, err)
-	return configs
-}
-
-// GetConfig returns a swarm config identified by the specified id
-func (d *Daemon) GetConfig(t assert.TestingT, id string) *swarm.Config {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	config, _, err := cli.ConfigInspectWithRaw(context.Background(), id)
-	assert.NilError(t, err)
-	return &config
-}
-
-// DeleteConfig removes the swarm config identified by the specified id
-func (d *Daemon) DeleteConfig(t assert.TestingT, id string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	err := cli.ConfigRemove(context.Background(), id)
-	assert.NilError(t, err)
-}
-
-// UpdateConfig updates the swarm config identified by the specified id
-// Currently, only label update is supported.
-func (d *Daemon) UpdateConfig(t assert.TestingT, id string, f ...ConfigConstructor) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	config := d.GetConfig(t, id)
-	for _, fn := range f {
-		fn(config)
-	}
-
-	err := cli.ConfigUpdate(context.Background(), config.ID, config.Version, config.Spec)
-	assert.NilError(t, err)
-}
diff --git a/internal/test/daemon/container.go b/internal/test/daemon/container.go
deleted file mode 100644
index 3aa69e195cdc9..0000000000000
--- a/internal/test/daemon/container.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package daemon
-
-import (
-	"context"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/internal/test"
-	"gotest.tools/assert"
-)
-
-// ActiveContainers returns the list of ids of the currently running containers
-func (d *Daemon) ActiveContainers(t assert.TestingT) []string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{})
-	assert.NilError(t, err)
-
-	ids := make([]string, len(containers))
-	for i, c := range containers {
-		ids[i] = c.ID
-	}
-	return ids
-}
-
-// FindContainerIP returns the ip of the specified container
-func (d *Daemon) FindContainerIP(t assert.TestingT, id string) string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	i, err := cli.ContainerInspect(context.Background(), id)
-	assert.NilError(t, err)
-	return i.NetworkSettings.IPAddress
-}
diff --git a/internal/test/daemon/daemon.go b/internal/test/daemon/daemon.go
deleted file mode 100644
index d9126f9f0cc4f..0000000000000
--- a/internal/test/daemon/daemon.go
+++ /dev/null
@@ -1,684 +0,0 @@
-package daemon // import "github.com/docker/docker/internal/test/daemon"
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/events"
-	"github.com/docker/docker/client"
-	"github.com/docker/docker/internal/test"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/docker/docker/opts"
-	"github.com/docker/docker/pkg/ioutils"
-	"github.com/docker/docker/pkg/stringid"
-	"github.com/docker/go-connections/sockets"
-	"github.com/docker/go-connections/tlsconfig"
-	"github.com/pkg/errors"
-	"gotest.tools/assert"
-)
-
-type testingT interface {
-	assert.TestingT
-	logT
-	Fatalf(string, ...interface{})
-}
-
-type logT interface {
-	Logf(string, ...interface{})
-}
-
-const defaultDockerdBinary = "dockerd"
-const containerdSocket = "/var/run/docker/containerd/containerd.sock"
-
-var errDaemonNotStarted = errors.New("daemon not started")
-
-// SockRoot holds the path of the default docker integration daemon socket
-var SockRoot = filepath.Join(os.TempDir(), "docker-integration")
-
-type clientConfig struct {
-	transport *http.Transport
-	scheme    string
-	addr      string
-}
-
-// Daemon represents a Docker daemon for the testing framework
-type Daemon struct {
-	GlobalFlags       []string
-	Root              string
-	Folder            string
-	Wait              chan error
-	UseDefaultHost    bool
-	UseDefaultTLSHost bool
-
-	id            string
-	logFile       *os.File
-	cmd           *exec.Cmd
-	storageDriver string
-	userlandProxy bool
-	execRoot      string
-	experimental  bool
-	init          bool
-	dockerdBinary string
-	log           logT
-
-	// swarm related field
-	swarmListenAddr string
-	SwarmPort       int // FIXME(vdemeester) should probably not be exported
-	DefaultAddrPool []string
-	SubnetSize      uint32
-	// cached information
-	CachedInfo types.Info
-}
-
-// New returns a Daemon instance to be used for testing.
-// This will create a directory such as d123456789 in the folder specified by $DOCKER_INTEGRATION_DAEMON_DEST or $DEST.
-// The daemon will not automatically start.
-func New(t testingT, ops ...func(*Daemon)) *Daemon {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	dest := os.Getenv("DOCKER_INTEGRATION_DAEMON_DEST")
-	if dest == "" {
-		dest = os.Getenv("DEST")
-	}
-	assert.Check(t, dest != "", "Please set the DOCKER_INTEGRATION_DAEMON_DEST or the DEST environment variable")
-
-	storageDriver := os.Getenv("DOCKER_GRAPHDRIVER")
-
-	assert.NilError(t, os.MkdirAll(SockRoot, 0700), "could not create daemon socket root")
-
-	id := fmt.Sprintf("d%s", stringid.TruncateID(stringid.GenerateRandomID()))
-	dir := filepath.Join(dest, id)
-	daemonFolder, err := filepath.Abs(dir)
-	assert.NilError(t, err, "Could not make %q an absolute path", dir)
-	daemonRoot := filepath.Join(daemonFolder, "root")
-
-	assert.NilError(t, os.MkdirAll(daemonRoot, 0755), "Could not create daemon root %q", dir)
-
-	userlandProxy := true
-	if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" {
-		if val, err := strconv.ParseBool(env); err != nil {
-			userlandProxy = val
-		}
-	}
-	d := &Daemon{
-		id:            id,
-		Folder:        daemonFolder,
-		Root:          daemonRoot,
-		storageDriver: storageDriver,
-		userlandProxy: userlandProxy,
-		// dxr stands for docker-execroot (shortened for avoiding unix(7) path length limitation)
-		execRoot:        filepath.Join(os.TempDir(), "dxr", id),
-		dockerdBinary:   defaultDockerdBinary,
-		swarmListenAddr: defaultSwarmListenAddr,
-		SwarmPort:       DefaultSwarmPort,
-		log:             t,
-	}
-
-	for _, op := range ops {
-		op(d)
-	}
-
-	return d
-}
-
-// RootDir returns the root directory of the daemon.
-func (d *Daemon) RootDir() string {
-	return d.Root
-}
-
-// ID returns the generated id of the daemon
-func (d *Daemon) ID() string {
-	return d.id
-}
-
-// StorageDriver returns the configured storage driver of the daemon
-func (d *Daemon) StorageDriver() string {
-	return d.storageDriver
-}
-
-// Sock returns the socket path of the daemon
-func (d *Daemon) Sock() string {
-	return fmt.Sprintf("unix://" + d.sockPath())
-}
-
-func (d *Daemon) sockPath() string {
-	return filepath.Join(SockRoot, d.id+".sock")
-}
-
-// LogFileName returns the path the daemon's log file
-func (d *Daemon) LogFileName() string {
-	return d.logFile.Name()
-}
-
-// ReadLogFile returns the content of the daemon log file
-func (d *Daemon) ReadLogFile() ([]byte, error) {
-	return ioutil.ReadFile(d.logFile.Name())
-}
-
-// NewClient creates new client based on daemon's socket path
-// FIXME(vdemeester): replace NewClient with NewClientT
-func (d *Daemon) NewClient() (*client.Client, error) {
-	return client.NewClientWithOpts(
-		client.FromEnv,
-		client.WithHost(d.Sock()))
-}
-
-// NewClientT creates new client based on daemon's socket path
-// FIXME(vdemeester): replace NewClient with NewClientT
-func (d *Daemon) NewClientT(t assert.TestingT) *client.Client {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	c, err := client.NewClientWithOpts(
-		client.FromEnv,
-		client.WithHost(d.Sock()))
-	assert.NilError(t, err, "cannot create daemon client")
-	return c
-}
-
-// Cleanup cleans the daemon files : exec root (network namespaces, ...), swarmkit files
-func (d *Daemon) Cleanup(t testingT) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	// Cleanup swarmkit wal files if present
-	cleanupRaftDir(t, d.Root)
-	cleanupNetworkNamespace(t, d.execRoot)
-}
-
-// Start starts the daemon and return once it is ready to receive requests.
-func (d *Daemon) Start(t testingT, args ...string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	if err := d.StartWithError(args...); err != nil {
-		t.Fatalf("failed to start daemon with arguments %v : %v", args, err)
-	}
-}
-
-// StartWithError starts the daemon and return once it is ready to receive requests.
-// It returns an error in case it couldn't start.
-func (d *Daemon) StartWithError(args ...string) error {
-	logFile, err := os.OpenFile(filepath.Join(d.Folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
-	if err != nil {
-		return errors.Wrapf(err, "[%s] Could not create %s/docker.log", d.id, d.Folder)
-	}
-
-	return d.StartWithLogFile(logFile, args...)
-}
-
-// StartWithLogFile will start the daemon and attach its streams to a given file.
-func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error {
-	d.handleUserns()
-	dockerdBinary, err := exec.LookPath(d.dockerdBinary)
-	if err != nil {
-		return errors.Wrapf(err, "[%s] could not find docker binary in $PATH", d.id)
-	}
-	args := append(d.GlobalFlags,
-		"--containerd", containerdSocket,
-		"--data-root", d.Root,
-		"--exec-root", d.execRoot,
-		"--pidfile", fmt.Sprintf("%s/docker.pid", d.Folder),
-		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
-	)
-	if d.experimental {
-		args = append(args, "--experimental")
-	}
-	if d.init {
-		args = append(args, "--init")
-	}
-	if !(d.UseDefaultHost || d.UseDefaultTLSHost) {
-		args = append(args, []string{"--host", d.Sock()}...)
-	}
-	if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" {
-		args = append(args, []string{"--userns-remap", root}...)
-	}
-
-	// If we don't explicitly set the log-level or debug flag(-D) then
-	// turn on debug mode
-	foundLog := false
-	foundSd := false
-	for _, a := range providedArgs {
-		if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") {
-			foundLog = true
-		}
-		if strings.Contains(a, "--storage-driver") {
-			foundSd = true
-		}
-	}
-	if !foundLog {
-		args = append(args, "--debug")
-	}
-	if d.storageDriver != "" && !foundSd {
-		args = append(args, "--storage-driver", d.storageDriver)
-	}
-
-	args = append(args, providedArgs...)
-	d.cmd = exec.Command(dockerdBinary, args...)
-	d.cmd.Env = append(os.Environ(), "DOCKER_SERVICE_PREFER_OFFLINE_IMAGE=1")
-	d.cmd.Stdout = out
-	d.cmd.Stderr = out
-	d.logFile = out
-
-	if err := d.cmd.Start(); err != nil {
-		return errors.Errorf("[%s] could not start daemon container: %v", d.id, err)
-	}
-
-	wait := make(chan error)
-
-	go func() {
-		wait <- d.cmd.Wait()
-		d.log.Logf("[%s] exiting daemon", d.id)
-		close(wait)
-	}()
-
-	d.Wait = wait
-
-	tick := time.Tick(500 * time.Millisecond)
-	// make sure daemon is ready to receive requests
-	startTime := time.Now().Unix()
-	for {
-		d.log.Logf("[%s] waiting for daemon to start", d.id)
-		if time.Now().Unix()-startTime > 5 {
-			// After 5 seconds, give up
-			return errors.Errorf("[%s] Daemon exited and never started", d.id)
-		}
-		select {
-		case <-time.After(2 * time.Second):
-			return errors.Errorf("[%s] timeout: daemon does not respond", d.id)
-		case <-tick:
-			clientConfig, err := d.getClientConfig()
-			if err != nil {
-				return err
-			}
-
-			client := &http.Client{
-				Transport: clientConfig.transport,
-			}
-
-			req, err := http.NewRequest("GET", "/_ping", nil)
-			if err != nil {
-				return errors.Wrapf(err, "[%s] could not create new request", d.id)
-			}
-			req.URL.Host = clientConfig.addr
-			req.URL.Scheme = clientConfig.scheme
-			resp, err := client.Do(req)
-			if err != nil {
-				continue
-			}
-			resp.Body.Close()
-			if resp.StatusCode != http.StatusOK {
-				d.log.Logf("[%s] received status != 200 OK: %s\n", d.id, resp.Status)
-			}
-			d.log.Logf("[%s] daemon started\n", d.id)
-			d.Root, err = d.queryRootDir()
-			if err != nil {
-				return errors.Errorf("[%s] error querying daemon for root directory: %v", d.id, err)
-			}
-			return nil
-		case err := <-d.Wait:
-			return errors.Errorf("[%s] Daemon exited during startup: %v", d.id, err)
-		}
-	}
-}
-
-// StartWithBusybox will first start the daemon with Daemon.Start()
-// then save the busybox image from the main daemon and load it into this Daemon instance.
-func (d *Daemon) StartWithBusybox(t testingT, arg ...string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	d.Start(t, arg...)
-	d.LoadBusybox(t)
-}
-
-// Kill will send a SIGKILL to the daemon
-func (d *Daemon) Kill() error {
-	if d.cmd == nil || d.Wait == nil {
-		return errDaemonNotStarted
-	}
-
-	defer func() {
-		d.logFile.Close()
-		d.cmd = nil
-	}()
-
-	if err := d.cmd.Process.Kill(); err != nil {
-		return err
-	}
-
-	return os.Remove(fmt.Sprintf("%s/docker.pid", d.Folder))
-}
-
-// Pid returns the pid of the daemon
-func (d *Daemon) Pid() int {
-	return d.cmd.Process.Pid
-}
-
-// Interrupt stops the daemon by sending it an Interrupt signal
-func (d *Daemon) Interrupt() error {
-	return d.Signal(os.Interrupt)
-}
-
-// Signal sends the specified signal to the daemon if running
-func (d *Daemon) Signal(signal os.Signal) error {
-	if d.cmd == nil || d.Wait == nil {
-		return errDaemonNotStarted
-	}
-	return d.cmd.Process.Signal(signal)
-}
-
-// DumpStackAndQuit sends SIGQUIT to the daemon, which triggers it to dump its
-// stack to its log file and exit
-// This is used primarily for gathering debug information on test timeout
-func (d *Daemon) DumpStackAndQuit() {
-	if d.cmd == nil || d.cmd.Process == nil {
-		return
-	}
-	SignalDaemonDump(d.cmd.Process.Pid)
-}
-
-// Stop will send a SIGINT every second and wait for the daemon to stop.
-// If it times out, a SIGKILL is sent.
-// Stop will not delete the daemon directory. If a purged daemon is needed,
-// instantiate a new one with NewDaemon.
-// If an error occurs while starting the daemon, the test will fail.
-func (d *Daemon) Stop(t testingT) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	err := d.StopWithError()
-	if err != nil {
-		if err != errDaemonNotStarted {
-			t.Fatalf("Error while stopping the daemon %s : %v", d.id, err)
-		} else {
-			t.Logf("Daemon %s is not started", d.id)
-		}
-	}
-}
-
-// StopWithError will send a SIGINT every second and wait for the daemon to stop.
-// If it timeouts, a SIGKILL is sent.
-// Stop will not delete the daemon directory. If a purged daemon is needed,
-// instantiate a new one with NewDaemon.
-func (d *Daemon) StopWithError() error {
-	if d.cmd == nil || d.Wait == nil {
-		return errDaemonNotStarted
-	}
-
-	defer func() {
-		d.logFile.Close()
-		d.cmd = nil
-	}()
-
-	i := 1
-	tick := time.Tick(time.Second)
-
-	if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
-		if strings.Contains(err.Error(), "os: process already finished") {
-			return errDaemonNotStarted
-		}
-		return errors.Errorf("could not send signal: %v", err)
-	}
-out1:
-	for {
-		select {
-		case err := <-d.Wait:
-			return err
-		case <-time.After(20 * time.Second):
-			// time for stopping jobs and run onShutdown hooks
-			d.log.Logf("[%s] daemon stop timeout", d.id)
-			break out1
-		}
-	}
-
-out2:
-	for {
-		select {
-		case err := <-d.Wait:
-			return err
-		case <-tick:
-			i++
-			if i > 5 {
-				d.log.Logf("tried to interrupt daemon for %d times, now try to kill it", i)
-				break out2
-			}
-			d.log.Logf("Attempt #%d: daemon is still running with pid %d", i, d.cmd.Process.Pid)
-			if err := d.cmd.Process.Signal(os.Interrupt); err != nil {
-				return errors.Errorf("could not send signal: %v", err)
-			}
-		}
-	}
-
-	if err := d.cmd.Process.Kill(); err != nil {
-		d.log.Logf("Could not kill daemon: %v", err)
-		return err
-	}
-
-	d.cmd.Wait()
-
-	return os.Remove(fmt.Sprintf("%s/docker.pid", d.Folder))
-}
-
-// Restart will restart the daemon by first stopping it and the starting it.
-// If an error occurs while starting the daemon, the test will fail.
-func (d *Daemon) Restart(t testingT, args ...string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	d.Stop(t)
-	d.Start(t, args...)
-}
-
-// RestartWithError will restart the daemon by first stopping it and then starting it.
-func (d *Daemon) RestartWithError(arg ...string) error {
-	if err := d.StopWithError(); err != nil {
-		return err
-	}
-	return d.StartWithError(arg...)
-}
-
-func (d *Daemon) handleUserns() {
-	// in the case of tests running a user namespace-enabled daemon, we have resolved
-	// d.Root to be the actual final path of the graph dir after the "uid.gid" of
-	// remapped root is added--we need to subtract it from the path before calling
-	// start or else we will continue making subdirectories rather than truly restarting
-	// with the same location/root:
-	if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" {
-		d.Root = filepath.Dir(d.Root)
-	}
-}
-
-// ReloadConfig asks the daemon to reload its configuration
-func (d *Daemon) ReloadConfig() error {
-	if d.cmd == nil || d.cmd.Process == nil {
-		return errors.New("daemon is not running")
-	}
-
-	errCh := make(chan error)
-	started := make(chan struct{})
-	go func() {
-		_, body, err := request.Get("/events", request.Host(d.Sock()))
-		close(started)
-		if err != nil {
-			errCh <- err
-		}
-		defer body.Close()
-		dec := json.NewDecoder(body)
-		for {
-			var e events.Message
-			if err := dec.Decode(&e); err != nil {
-				errCh <- err
-				return
-			}
-			if e.Type != events.DaemonEventType {
-				continue
-			}
-			if e.Action != "reload" {
-				continue
-			}
-			close(errCh) // notify that we are done
-			return
-		}
-	}()
-
-	<-started
-	if err := signalDaemonReload(d.cmd.Process.Pid); err != nil {
-		return errors.Errorf("error signaling daemon reload: %v", err)
-	}
-	select {
-	case err := <-errCh:
-		if err != nil {
-			return errors.Errorf("error waiting for daemon reload event: %v", err)
-		}
-	case <-time.After(30 * time.Second):
-		return errors.New("timeout waiting for daemon reload event")
-	}
-	return nil
-}
-
-// LoadBusybox image into the daemon
-func (d *Daemon) LoadBusybox(t assert.TestingT) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	clientHost, err := client.NewEnvClient()
-	assert.NilError(t, err, "failed to create client")
-	defer clientHost.Close()
-
-	ctx := context.Background()
-	reader, err := clientHost.ImageSave(ctx, []string{"busybox:latest"})
-	assert.NilError(t, err, "failed to download busybox")
-	defer reader.Close()
-
-	client, err := d.NewClient()
-	assert.NilError(t, err, "failed to create client")
-	defer client.Close()
-
-	resp, err := client.ImageLoad(ctx, reader, true)
-	assert.NilError(t, err, "failed to load busybox")
-	defer resp.Body.Close()
-}
-
-func (d *Daemon) getClientConfig() (*clientConfig, error) {
-	var (
-		transport *http.Transport
-		scheme    string
-		addr      string
-		proto     string
-	)
-	if d.UseDefaultTLSHost {
-		option := &tlsconfig.Options{
-			CAFile:   "fixtures/https/ca.pem",
-			CertFile: "fixtures/https/client-cert.pem",
-			KeyFile:  "fixtures/https/client-key.pem",
-		}
-		tlsConfig, err := tlsconfig.Client(*option)
-		if err != nil {
-			return nil, err
-		}
-		transport = &http.Transport{
-			TLSClientConfig: tlsConfig,
-		}
-		addr = fmt.Sprintf("%s:%d", opts.DefaultHTTPHost, opts.DefaultTLSHTTPPort)
-		scheme = "https"
-		proto = "tcp"
-	} else if d.UseDefaultHost {
-		addr = opts.DefaultUnixSocket
-		proto = "unix"
-		scheme = "http"
-		transport = &http.Transport{}
-	} else {
-		addr = d.sockPath()
-		proto = "unix"
-		scheme = "http"
-		transport = &http.Transport{}
-	}
-
-	if err := sockets.ConfigureTransport(transport, proto, addr); err != nil {
-		return nil, err
-	}
-	transport.DisableKeepAlives = true
-
-	return &clientConfig{
-		transport: transport,
-		scheme:    scheme,
-		addr:      addr,
-	}, nil
-}
-
-func (d *Daemon) queryRootDir() (string, error) {
-	// update daemon root by asking /info endpoint (to support user
-	// namespaced daemon with root remapped uid.gid directory)
-	clientConfig, err := d.getClientConfig()
-	if err != nil {
-		return "", err
-	}
-
-	client := &http.Client{
-		Transport: clientConfig.transport,
-	}
-
-	req, err := http.NewRequest("GET", "/info", nil)
-	if err != nil {
-		return "", err
-	}
-	req.Header.Set("Content-Type", "application/json")
-	req.URL.Host = clientConfig.addr
-	req.URL.Scheme = clientConfig.scheme
-
-	resp, err := client.Do(req)
-	if err != nil {
-		return "", err
-	}
-	body := ioutils.NewReadCloserWrapper(resp.Body, func() error {
-		return resp.Body.Close()
-	})
-
-	type Info struct {
-		DockerRootDir string
-	}
-	var b []byte
-	var i Info
-	b, err = request.ReadBody(body)
-	if err == nil && resp.StatusCode == http.StatusOK {
-		// read the docker root dir
-		if err = json.Unmarshal(b, &i); err == nil {
-			return i.DockerRootDir, nil
-		}
-	}
-	return "", err
-}
-
-// Info returns the info struct for this daemon
-func (d *Daemon) Info(t assert.TestingT) types.Info {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	apiclient, err := d.NewClient()
-	assert.NilError(t, err)
-	info, err := apiclient.Info(context.Background())
-	assert.NilError(t, err)
-	return info
-}
-
-func cleanupRaftDir(t testingT, rootPath string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	walDir := filepath.Join(rootPath, "swarm/raft/wal")
-	if err := os.RemoveAll(walDir); err != nil {
-		t.Logf("error removing %v: %v", walDir, err)
-	}
-}
diff --git a/internal/test/daemon/daemon_unix.go b/internal/test/daemon/daemon_unix.go
deleted file mode 100644
index 9dd9e36f0c0c9..0000000000000
--- a/internal/test/daemon/daemon_unix.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// +build !windows
-
-package daemon // import "github.com/docker/docker/internal/test/daemon"
-
-import (
-	"os"
-	"path/filepath"
-
-	"github.com/docker/docker/internal/test"
-	"golang.org/x/sys/unix"
-)
-
-func cleanupNetworkNamespace(t testingT, execRoot string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	// Cleanup network namespaces in the exec root of this
-	// daemon because this exec root is specific to this
-	// daemon instance and has no chance of getting
-	// cleaned up when a new daemon is instantiated with a
-	// new exec root.
-	netnsPath := filepath.Join(execRoot, "netns")
-	filepath.Walk(netnsPath, func(path string, info os.FileInfo, err error) error {
-		if err := unix.Unmount(path, unix.MNT_FORCE); err != nil {
-			t.Logf("unmount of %s failed: %v", path, err)
-		}
-		os.Remove(path)
-		return nil
-	})
-}
-
-// SignalDaemonDump sends a signal to the daemon to write a dump file
-func SignalDaemonDump(pid int) {
-	unix.Kill(pid, unix.SIGQUIT)
-}
-
-func signalDaemonReload(pid int) error {
-	return unix.Kill(pid, unix.SIGHUP)
-}
diff --git a/internal/test/daemon/daemon_windows.go b/internal/test/daemon/daemon_windows.go
deleted file mode 100644
index cb6bb6a4cb16c..0000000000000
--- a/internal/test/daemon/daemon_windows.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package daemon // import "github.com/docker/docker/internal/test/daemon"
-
-import (
-	"fmt"
-	"strconv"
-
-	"golang.org/x/sys/windows"
-)
-
-// SignalDaemonDump sends a signal to the daemon to write a dump file
-func SignalDaemonDump(pid int) {
-	ev, _ := windows.UTF16PtrFromString("Global\\docker-daemon-" + strconv.Itoa(pid))
-	h2, err := windows.OpenEvent(0x0002, false, ev)
-	if h2 == 0 || err != nil {
-		return
-	}
-	windows.PulseEvent(h2)
-}
-
-func signalDaemonReload(pid int) error {
-	return fmt.Errorf("daemon reload not supported")
-}
-
-func cleanupNetworkNamespace(t testingT, execRoot string) {
-}
diff --git a/internal/test/daemon/node.go b/internal/test/daemon/node.go
deleted file mode 100644
index d9263a7f29dc8..0000000000000
--- a/internal/test/daemon/node.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package daemon
-
-import (
-	"context"
-	"strings"
-	"time"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/swarm"
-	"github.com/docker/docker/internal/test"
-	"gotest.tools/assert"
-)
-
-// NodeConstructor defines a swarm node constructor
-type NodeConstructor func(*swarm.Node)
-
-// GetNode returns a swarm node identified by the specified id
-func (d *Daemon) GetNode(t assert.TestingT, id string) *swarm.Node {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	node, _, err := cli.NodeInspectWithRaw(context.Background(), id)
-	assert.NilError(t, err)
-	assert.Check(t, node.ID == id)
-	return &node
-}
-
-// RemoveNode removes the specified node
-func (d *Daemon) RemoveNode(t assert.TestingT, id string, force bool) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	options := types.NodeRemoveOptions{
-		Force: force,
-	}
-	err := cli.NodeRemove(context.Background(), id, options)
-	assert.NilError(t, err)
-}
-
-// UpdateNode updates a swarm node with the specified node constructor
-func (d *Daemon) UpdateNode(t assert.TestingT, id string, f ...NodeConstructor) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	for i := 0; ; i++ {
-		node := d.GetNode(t, id)
-		for _, fn := range f {
-			fn(node)
-		}
-
-		err := cli.NodeUpdate(context.Background(), node.ID, node.Version, node.Spec)
-		if i < 10 && err != nil && strings.Contains(err.Error(), "update out of sequence") {
-			time.Sleep(100 * time.Millisecond)
-			continue
-		}
-		assert.NilError(t, err)
-		return
-	}
-}
-
-// ListNodes returns the list of the current swarm nodes
-func (d *Daemon) ListNodes(t assert.TestingT) []swarm.Node {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	nodes, err := cli.NodeList(context.Background(), types.NodeListOptions{})
-	assert.NilError(t, err)
-
-	return nodes
-}
diff --git a/internal/test/daemon/ops.go b/internal/test/daemon/ops.go
deleted file mode 100644
index 4154a77977cc1..0000000000000
--- a/internal/test/daemon/ops.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package daemon
-
-import "github.com/docker/docker/internal/test/environment"
-
-// WithExperimental sets the daemon in experimental mode
-func WithExperimental(d *Daemon) {
-	d.experimental = true
-	d.init = true
-}
-
-// WithInit sets the daemon init
-func WithInit(d *Daemon) {
-	d.init = true
-}
-
-// WithDockerdBinary sets the dockerd binary to the specified one
-func WithDockerdBinary(dockerdBinary string) func(*Daemon) {
-	return func(d *Daemon) {
-		d.dockerdBinary = dockerdBinary
-	}
-}
-
-// WithSwarmPort sets the swarm port to use for swarm mode
-func WithSwarmPort(port int) func(*Daemon) {
-	return func(d *Daemon) {
-		d.SwarmPort = port
-	}
-}
-
-// WithSwarmListenAddr sets the swarm listen addr to use for swarm mode
-func WithSwarmListenAddr(listenAddr string) func(*Daemon) {
-	return func(d *Daemon) {
-		d.swarmListenAddr = listenAddr
-	}
-}
-
-// WithSwarmDefaultAddrPool sets the swarm default address pool to use for swarm mode
-func WithSwarmDefaultAddrPool(defaultAddrPool []string) func(*Daemon) {
-	return func(d *Daemon) {
-		d.DefaultAddrPool = defaultAddrPool
-	}
-}
-
-// WithSwarmDefaultAddrPoolSubnetSize sets the subnet length mask of swarm default address pool to use for swarm mode
-func WithSwarmDefaultAddrPoolSubnetSize(subnetSize uint32) func(*Daemon) {
-	return func(d *Daemon) {
-		d.SubnetSize = subnetSize
-	}
-}
-
-// WithEnvironment sets options from internal/test/environment.Execution struct
-func WithEnvironment(e environment.Execution) func(*Daemon) {
-	return func(d *Daemon) {
-		if e.DaemonInfo.ExperimentalBuild {
-			d.experimental = true
-		}
-	}
-}
diff --git a/internal/test/daemon/plugin.go b/internal/test/daemon/plugin.go
deleted file mode 100644
index 63bbeed219327..0000000000000
--- a/internal/test/daemon/plugin.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package daemon
-
-import (
-	"context"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/client"
-	"gotest.tools/poll"
-)
-
-// PluginIsRunning provides a poller to check if the specified plugin is running
-func (d *Daemon) PluginIsRunning(name string) func(poll.LogT) poll.Result {
-	return withClient(d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result {
-		if plugin.Enabled {
-			return poll.Success()
-		}
-		return poll.Continue("plugin %q is not enabled", name)
-	}))
-}
-
-// PluginIsNotRunning provides a poller to check if the specified plugin is not running
-func (d *Daemon) PluginIsNotRunning(name string) func(poll.LogT) poll.Result {
-	return withClient(d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result {
-		if !plugin.Enabled {
-			return poll.Success()
-		}
-		return poll.Continue("plugin %q is enabled", name)
-	}))
-}
-
-// PluginIsNotPresent provides a poller to check if the specified plugin is not present
-func (d *Daemon) PluginIsNotPresent(name string) func(poll.LogT) poll.Result {
-	return withClient(d, func(c client.APIClient, t poll.LogT) poll.Result {
-		_, _, err := c.PluginInspectWithRaw(context.Background(), name)
-		if client.IsErrNotFound(err) {
-			return poll.Success()
-		}
-		if err != nil {
-			return poll.Error(err)
-		}
-		return poll.Continue("plugin %q exists", name)
-	})
-}
-
-// PluginReferenceIs provides a poller to check if the specified plugin has the specified reference
-func (d *Daemon) PluginReferenceIs(name, expectedRef string) func(poll.LogT) poll.Result {
-	return withClient(d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result {
-		if plugin.PluginReference == expectedRef {
-			return poll.Success()
-		}
-		return poll.Continue("plugin %q reference is not %q", name, expectedRef)
-	}))
-}
-
-func withPluginInspect(name string, f func(*types.Plugin, poll.LogT) poll.Result) func(client.APIClient, poll.LogT) poll.Result {
-	return func(c client.APIClient, t poll.LogT) poll.Result {
-		plugin, _, err := c.PluginInspectWithRaw(context.Background(), name)
-		if client.IsErrNotFound(err) {
-			return poll.Continue("plugin %q not found", name)
-		}
-		if err != nil {
-			return poll.Error(err)
-		}
-		return f(plugin, t)
-	}
-
-}
-
-func withClient(d *Daemon, f func(client.APIClient, poll.LogT) poll.Result) func(poll.LogT) poll.Result {
-	return func(t poll.LogT) poll.Result {
-		c, err := d.NewClient()
-		if err != nil {
-			poll.Error(err)
-		}
-		return f(c, t)
-	}
-}
diff --git a/internal/test/daemon/secret.go b/internal/test/daemon/secret.go
deleted file mode 100644
index f3db7a42601da..0000000000000
--- a/internal/test/daemon/secret.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package daemon
-
-import (
-	"context"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/swarm"
-	"github.com/docker/docker/internal/test"
-	"gotest.tools/assert"
-)
-
-// SecretConstructor defines a swarm secret constructor
-type SecretConstructor func(*swarm.Secret)
-
-// CreateSecret creates a secret given the specified spec
-func (d *Daemon) CreateSecret(t assert.TestingT, secretSpec swarm.SecretSpec) string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	scr, err := cli.SecretCreate(context.Background(), secretSpec)
-	assert.NilError(t, err)
-
-	return scr.ID
-}
-
-// ListSecrets returns the list of the current swarm secrets
-func (d *Daemon) ListSecrets(t assert.TestingT) []swarm.Secret {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	secrets, err := cli.SecretList(context.Background(), types.SecretListOptions{})
-	assert.NilError(t, err)
-	return secrets
-}
-
-// GetSecret returns a swarm secret identified by the specified id
-func (d *Daemon) GetSecret(t assert.TestingT, id string) *swarm.Secret {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	secret, _, err := cli.SecretInspectWithRaw(context.Background(), id)
-	assert.NilError(t, err)
-	return &secret
-}
-
-// DeleteSecret removes the swarm secret identified by the specified id
-func (d *Daemon) DeleteSecret(t assert.TestingT, id string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	err := cli.SecretRemove(context.Background(), id)
-	assert.NilError(t, err)
-}
-
-// UpdateSecret updates the swarm secret identified by the specified id
-// Currently, only label update is supported.
-func (d *Daemon) UpdateSecret(t assert.TestingT, id string, f ...SecretConstructor) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	secret := d.GetSecret(t, id)
-	for _, fn := range f {
-		fn(secret)
-	}
-
-	err := cli.SecretUpdate(context.Background(), secret.ID, secret.Version, secret.Spec)
-
-	assert.NilError(t, err)
-}
diff --git a/internal/test/daemon/service.go b/internal/test/daemon/service.go
deleted file mode 100644
index 0f88ca786b32d..0000000000000
--- a/internal/test/daemon/service.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package daemon
-
-import (
-	"context"
-	"time"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/filters"
-	"github.com/docker/docker/api/types/swarm"
-	"github.com/docker/docker/internal/test"
-	"gotest.tools/assert"
-)
-
-// ServiceConstructor defines a swarm service constructor function
-type ServiceConstructor func(*swarm.Service)
-
-func (d *Daemon) createServiceWithOptions(t assert.TestingT, opts types.ServiceCreateOptions, f ...ServiceConstructor) string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	var service swarm.Service
-	for _, fn := range f {
-		fn(&service)
-	}
-
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
-	defer cancel()
-
-	res, err := cli.ServiceCreate(ctx, service.Spec, opts)
-	assert.NilError(t, err)
-	return res.ID
-}
-
-// CreateService creates a swarm service given the specified service constructor
-func (d *Daemon) CreateService(t assert.TestingT, f ...ServiceConstructor) string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	return d.createServiceWithOptions(t, types.ServiceCreateOptions{}, f...)
-}
-
-// GetService returns the swarm service corresponding to the specified id
-func (d *Daemon) GetService(t assert.TestingT, id string) *swarm.Service {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	service, _, err := cli.ServiceInspectWithRaw(context.Background(), id, types.ServiceInspectOptions{})
-	assert.NilError(t, err)
-	return &service
-}
-
-// GetServiceTasks returns the swarm tasks for the specified service
-func (d *Daemon) GetServiceTasks(t assert.TestingT, service string) []swarm.Task {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	filterArgs := filters.NewArgs()
-	filterArgs.Add("desired-state", "running")
-	filterArgs.Add("service", service)
-
-	options := types.TaskListOptions{
-		Filters: filterArgs,
-	}
-
-	tasks, err := cli.TaskList(context.Background(), options)
-	assert.NilError(t, err)
-	return tasks
-}
-
-// UpdateService updates a swarm service with the specified service constructor
-func (d *Daemon) UpdateService(t assert.TestingT, service *swarm.Service, f ...ServiceConstructor) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	for _, fn := range f {
-		fn(service)
-	}
-
-	_, err := cli.ServiceUpdate(context.Background(), service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{})
-	assert.NilError(t, err)
-}
-
-// RemoveService removes the specified service
-func (d *Daemon) RemoveService(t assert.TestingT, id string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	err := cli.ServiceRemove(context.Background(), id)
-	assert.NilError(t, err)
-}
-
-// ListServices returns the list of the current swarm services
-func (d *Daemon) ListServices(t assert.TestingT) []swarm.Service {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	services, err := cli.ServiceList(context.Background(), types.ServiceListOptions{})
-	assert.NilError(t, err)
-	return services
-}
-
-// GetTask returns the swarm task identified by the specified id
-func (d *Daemon) GetTask(t assert.TestingT, id string) swarm.Task {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	task, _, err := cli.TaskInspectWithRaw(context.Background(), id)
-	assert.NilError(t, err)
-	return task
-}
diff --git a/internal/test/daemon/swarm.go b/internal/test/daemon/swarm.go
deleted file mode 100644
index ae6a62c9eb1d4..0000000000000
--- a/internal/test/daemon/swarm.go
+++ /dev/null
@@ -1,198 +0,0 @@
-package daemon
-
-import (
-	"context"
-	"fmt"
-
-	"github.com/docker/docker/api/types/swarm"
-	"github.com/docker/docker/internal/test"
-	"github.com/pkg/errors"
-	"gotest.tools/assert"
-)
-
-const (
-	// DefaultSwarmPort is the default port use for swarm in the tests
-	DefaultSwarmPort       = 2477
-	defaultSwarmListenAddr = "0.0.0.0"
-)
-
-// StartAndSwarmInit starts the daemon (with busybox) and init the swarm
-func (d *Daemon) StartAndSwarmInit(t testingT) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	// avoid networking conflicts
-	args := []string{"--iptables=false", "--swarm-default-advertise-addr=lo"}
-	d.StartWithBusybox(t, args...)
-
-	d.SwarmInit(t, swarm.InitRequest{})
-}
-
-// StartAndSwarmJoin starts the daemon (with busybox) and join the specified swarm as worker or manager
-func (d *Daemon) StartAndSwarmJoin(t testingT, leader *Daemon, manager bool) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	// avoid networking conflicts
-	args := []string{"--iptables=false", "--swarm-default-advertise-addr=lo"}
-	d.StartWithBusybox(t, args...)
-
-	tokens := leader.JoinTokens(t)
-	token := tokens.Worker
-	if manager {
-		token = tokens.Manager
-	}
-	d.SwarmJoin(t, swarm.JoinRequest{
-		RemoteAddrs: []string{leader.SwarmListenAddr()},
-		JoinToken:   token,
-	})
-}
-
-// SpecConstructor defines a swarm spec constructor
-type SpecConstructor func(*swarm.Spec)
-
-// SwarmListenAddr returns the listen-addr used for the daemon
-func (d *Daemon) SwarmListenAddr() string {
-	return fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort)
-}
-
-// NodeID returns the swarm mode node ID
-func (d *Daemon) NodeID() string {
-	return d.CachedInfo.Swarm.NodeID
-}
-
-// SwarmInit initializes a new swarm cluster.
-func (d *Daemon) SwarmInit(t assert.TestingT, req swarm.InitRequest) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	if req.ListenAddr == "" {
-		req.ListenAddr = fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort)
-	}
-	if req.DefaultAddrPool == nil {
-		req.DefaultAddrPool = d.DefaultAddrPool
-		req.SubnetSize = d.SubnetSize
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-	_, err := cli.SwarmInit(context.Background(), req)
-	assert.NilError(t, err, "initializing swarm")
-	d.CachedInfo = d.Info(t)
-}
-
-// SwarmJoin joins a daemon to an existing cluster.
-func (d *Daemon) SwarmJoin(t assert.TestingT, req swarm.JoinRequest) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	if req.ListenAddr == "" {
-		req.ListenAddr = fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort)
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-	err := cli.SwarmJoin(context.Background(), req)
-	assert.NilError(t, err, "initializing swarm")
-	d.CachedInfo = d.Info(t)
-}
-
-// SwarmLeave forces daemon to leave current cluster.
-func (d *Daemon) SwarmLeave(force bool) error {
-	cli, err := d.NewClient()
-	if err != nil {
-		return fmt.Errorf("leaving swarm: failed to create client %v", err)
-	}
-	defer cli.Close()
-	err = cli.SwarmLeave(context.Background(), force)
-	if err != nil {
-		err = fmt.Errorf("leaving swarm: %v", err)
-	}
-	return err
-}
-
-// SwarmInfo returns the swarm information of the daemon
-func (d *Daemon) SwarmInfo(t assert.TestingT) swarm.Info {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	info, err := cli.Info(context.Background())
-	assert.NilError(t, err, "get swarm info")
-	return info.Swarm
-}
-
-// SwarmUnlock tries to unlock a locked swarm
-func (d *Daemon) SwarmUnlock(req swarm.UnlockRequest) error {
-	cli, err := d.NewClient()
-	if err != nil {
-		return fmt.Errorf("unlocking swarm: failed to create client %v", err)
-	}
-	defer cli.Close()
-	err = cli.SwarmUnlock(context.Background(), req)
-	if err != nil {
-		err = errors.Wrap(err, "unlocking swarm")
-	}
-	return err
-}
-
-// GetSwarm returns the current swarm object
-func (d *Daemon) GetSwarm(t assert.TestingT) swarm.Swarm {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	sw, err := cli.SwarmInspect(context.Background())
-	assert.NilError(t, err)
-	return sw
-}
-
-// UpdateSwarm updates the current swarm object with the specified spec constructors
-func (d *Daemon) UpdateSwarm(t assert.TestingT, f ...SpecConstructor) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	sw := d.GetSwarm(t)
-	for _, fn := range f {
-		fn(&sw.Spec)
-	}
-
-	err := cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, swarm.UpdateFlags{})
-	assert.NilError(t, err)
-}
-
-// RotateTokens update the swarm to rotate tokens
-func (d *Daemon) RotateTokens(t assert.TestingT) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	sw, err := cli.SwarmInspect(context.Background())
-	assert.NilError(t, err)
-
-	flags := swarm.UpdateFlags{
-		RotateManagerToken: true,
-		RotateWorkerToken:  true,
-	}
-
-	err = cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, flags)
-	assert.NilError(t, err)
-}
-
-// JoinTokens returns the current swarm join tokens
-func (d *Daemon) JoinTokens(t assert.TestingT) swarm.JoinTokens {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	cli := d.NewClientT(t)
-	defer cli.Close()
-
-	sw, err := cli.SwarmInspect(context.Background())
-	assert.NilError(t, err)
-	return sw.JoinTokens
-}
diff --git a/internal/test/environment/clean.go b/internal/test/environment/clean.go
deleted file mode 100644
index 93dee593f2817..0000000000000
--- a/internal/test/environment/clean.go
+++ /dev/null
@@ -1,217 +0,0 @@
-package environment // import "github.com/docker/docker/internal/test/environment"
-
-import (
-	"context"
-	"regexp"
-	"strings"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/filters"
-	"github.com/docker/docker/client"
-	"github.com/docker/docker/internal/test"
-	"gotest.tools/assert"
-)
-
-type testingT interface {
-	assert.TestingT
-	logT
-	Fatalf(string, ...interface{})
-}
-
-type logT interface {
-	Logf(string, ...interface{})
-}
-
-// Clean the environment, preserving protected objects (images, containers, ...)
-// and removing everything else. It's meant to run after any tests so that they don't
-// depend on each others.
-func (e *Execution) Clean(t assert.TestingT) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	client := e.APIClient()
-
-	platform := e.OSType
-	if (platform != "windows") || (platform == "windows" && e.DaemonInfo.Isolation == "hyperv") {
-		unpauseAllContainers(t, client)
-	}
-	deleteAllContainers(t, client, e.protectedElements.containers)
-	deleteAllImages(t, client, e.protectedElements.images)
-	deleteAllVolumes(t, client, e.protectedElements.volumes)
-	deleteAllNetworks(t, client, platform, e.protectedElements.networks)
-	if platform == "linux" {
-		deleteAllPlugins(t, client, e.protectedElements.plugins)
-	}
-}
-
-func unpauseAllContainers(t assert.TestingT, client client.ContainerAPIClient) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	ctx := context.Background()
-	containers := getPausedContainers(ctx, t, client)
-	if len(containers) > 0 {
-		for _, container := range containers {
-			err := client.ContainerUnpause(ctx, container.ID)
-			assert.Check(t, err, "failed to unpause container %s", container.ID)
-		}
-	}
-}
-
-func getPausedContainers(ctx context.Context, t assert.TestingT, client client.ContainerAPIClient) []types.Container {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	filter := filters.NewArgs()
-	filter.Add("status", "paused")
-	containers, err := client.ContainerList(ctx, types.ContainerListOptions{
-		Filters: filter,
-		Quiet:   true,
-		All:     true,
-	})
-	assert.Check(t, err, "failed to list containers")
-	return containers
-}
-
-var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`)
-
-func deleteAllContainers(t assert.TestingT, apiclient client.ContainerAPIClient, protectedContainers map[string]struct{}) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	ctx := context.Background()
-	containers := getAllContainers(ctx, t, apiclient)
-	if len(containers) == 0 {
-		return
-	}
-
-	for _, container := range containers {
-		if _, ok := protectedContainers[container.ID]; ok {
-			continue
-		}
-		err := apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
-			Force:         true,
-			RemoveVolumes: true,
-		})
-		if err == nil || client.IsErrNotFound(err) || alreadyExists.MatchString(err.Error()) || isErrNotFoundSwarmClassic(err) {
-			continue
-		}
-		assert.Check(t, err, "failed to remove %s", container.ID)
-	}
-}
-
-func getAllContainers(ctx context.Context, t assert.TestingT, client client.ContainerAPIClient) []types.Container {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	containers, err := client.ContainerList(ctx, types.ContainerListOptions{
-		Quiet: true,
-		All:   true,
-	})
-	assert.Check(t, err, "failed to list containers")
-	return containers
-}
-
-func deleteAllImages(t assert.TestingT, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	images, err := apiclient.ImageList(context.Background(), types.ImageListOptions{})
-	assert.Check(t, err, "failed to list images")
-
-	ctx := context.Background()
-	for _, image := range images {
-		tags := tagsFromImageSummary(image)
-		if len(tags) == 0 {
-			removeImage(ctx, t, apiclient, image.ID)
-			continue
-		}
-		for _, tag := range tags {
-			if _, ok := protectedImages[tag]; !ok {
-				removeImage(ctx, t, apiclient, tag)
-			}
-		}
-	}
-}
-
-func removeImage(ctx context.Context, t assert.TestingT, apiclient client.ImageAPIClient, ref string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	_, err := apiclient.ImageRemove(ctx, ref, types.ImageRemoveOptions{
-		Force: true,
-	})
-	if client.IsErrNotFound(err) {
-		return
-	}
-	assert.Check(t, err, "failed to remove image %s", ref)
-}
-
-func deleteAllVolumes(t assert.TestingT, c client.VolumeAPIClient, protectedVolumes map[string]struct{}) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	volumes, err := c.VolumeList(context.Background(), filters.Args{})
-	assert.Check(t, err, "failed to list volumes")
-
-	for _, v := range volumes.Volumes {
-		if _, ok := protectedVolumes[v.Name]; ok {
-			continue
-		}
-		err := c.VolumeRemove(context.Background(), v.Name, true)
-		// Docker EE may list volumes that no longer exist.
-		if isErrNotFoundSwarmClassic(err) {
-			continue
-		}
-		assert.Check(t, err, "failed to remove volume %s", v.Name)
-	}
-}
-
-func deleteAllNetworks(t assert.TestingT, c client.NetworkAPIClient, daemonPlatform string, protectedNetworks map[string]struct{}) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
-	assert.Check(t, err, "failed to list networks")
-
-	for _, n := range networks {
-		if n.Name == "bridge" || n.Name == "none" || n.Name == "host" {
-			continue
-		}
-		if _, ok := protectedNetworks[n.ID]; ok {
-			continue
-		}
-		if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" {
-			// nat is a pre-defined network on Windows and cannot be removed
-			continue
-		}
-		err := c.NetworkRemove(context.Background(), n.ID)
-		assert.Check(t, err, "failed to remove network %s", n.ID)
-	}
-}
-
-func deleteAllPlugins(t assert.TestingT, c client.PluginAPIClient, protectedPlugins map[string]struct{}) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	plugins, err := c.PluginList(context.Background(), filters.Args{})
-	// Docker EE does not allow cluster-wide plugin management.
-	if client.IsErrNotImplemented(err) {
-		return
-	}
-	assert.Check(t, err, "failed to list plugins")
-
-	for _, p := range plugins {
-		if _, ok := protectedPlugins[p.Name]; ok {
-			continue
-		}
-		err := c.PluginRemove(context.Background(), p.Name, types.PluginRemoveOptions{Force: true})
-		assert.Check(t, err, "failed to remove plugin %s", p.ID)
-	}
-}
-
-// Swarm classic aggregates node errors and returns a 500 so we need to check
-// the error string instead of just IsErrNotFound().
-func isErrNotFoundSwarmClassic(err error) bool {
-	return err != nil && strings.Contains(strings.ToLower(err.Error()), "no such")
-}
diff --git a/internal/test/environment/environment.go b/internal/test/environment/environment.go
deleted file mode 100644
index 5538d2097e9b0..0000000000000
--- a/internal/test/environment/environment.go
+++ /dev/null
@@ -1,164 +0,0 @@
-package environment // import "github.com/docker/docker/internal/test/environment"
-
-import (
-	"context"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/client"
-	"github.com/docker/docker/internal/test/fixtures/load"
-	"github.com/pkg/errors"
-)
-
-// Execution contains information about the current test execution and daemon
-// under test
-type Execution struct {
-	client            client.APIClient
-	DaemonInfo        types.Info
-	OSType            string
-	PlatformDefaults  PlatformDefaults
-	protectedElements protectedElements
-}
-
-// PlatformDefaults are defaults values for the platform of the daemon under test
-type PlatformDefaults struct {
-	BaseImage            string
-	VolumesConfigPath    string
-	ContainerStoragePath string
-}
-
-// New creates a new Execution struct
-func New() (*Execution, error) {
-	client, err := client.NewClientWithOpts(client.FromEnv)
-	if err != nil {
-		return nil, errors.Wrapf(err, "failed to create client")
-	}
-
-	info, err := client.Info(context.Background())
-	if err != nil {
-		return nil, errors.Wrapf(err, "failed to get info from daemon")
-	}
-
-	osType := getOSType(info)
-
-	return &Execution{
-		client:            client,
-		DaemonInfo:        info,
-		OSType:            osType,
-		PlatformDefaults:  getPlatformDefaults(info, osType),
-		protectedElements: newProtectedElements(),
-	}, nil
-}
-
-func getOSType(info types.Info) string {
-	// Docker EE does not set the OSType so allow the user to override this value.
-	userOsType := os.Getenv("TEST_OSTYPE")
-	if userOsType != "" {
-		return userOsType
-	}
-	return info.OSType
-}
-
-func getPlatformDefaults(info types.Info, osType string) PlatformDefaults {
-	volumesPath := filepath.Join(info.DockerRootDir, "volumes")
-	containersPath := filepath.Join(info.DockerRootDir, "containers")
-
-	switch osType {
-	case "linux":
-		return PlatformDefaults{
-			BaseImage:            "scratch",
-			VolumesConfigPath:    toSlash(volumesPath),
-			ContainerStoragePath: toSlash(containersPath),
-		}
-	case "windows":
-		baseImage := "microsoft/windowsservercore"
-		if override := os.Getenv("WINDOWS_BASE_IMAGE"); override != "" {
-			baseImage = override
-			fmt.Println("INFO: Windows Base image is ", baseImage)
-		}
-		return PlatformDefaults{
-			BaseImage:            baseImage,
-			VolumesConfigPath:    filepath.FromSlash(volumesPath),
-			ContainerStoragePath: filepath.FromSlash(containersPath),
-		}
-	default:
-		panic(fmt.Sprintf("unknown OSType for daemon: %s", osType))
-	}
-}
-
-// Make sure in context of daemon, not the local platform. Note we can't
-// use filepath.FromSlash or ToSlash here as they are a no-op on Unix.
-func toSlash(path string) string {
-	return strings.Replace(path, `\`, `/`, -1)
-}
-
-// IsLocalDaemon is true if the daemon under test is on the same
-// host as the test process.
-//
-// Deterministically working out the environment in which CI is running
-// to evaluate whether the daemon is local or remote is not possible through
-// a build tag.
-//
-// For example Windows to Linux CI under Jenkins tests the 64-bit
-// Windows binary build with the daemon build tag, but calls a remote
-// Linux daemon.
-//
-// We can't just say if Windows then assume the daemon is local as at
-// some point, we will be testing the Windows CLI against a Windows daemon.
-//
-// Similarly, it will be perfectly valid to also run CLI tests from
-// a Linux CLI (built with the daemon tag) against a Windows daemon.
-func (e *Execution) IsLocalDaemon() bool {
-	return os.Getenv("DOCKER_REMOTE_DAEMON") == ""
-}
-
-// IsRemoteDaemon is true if the daemon under test is on different host
-// as the test process.
-func (e *Execution) IsRemoteDaemon() bool {
-	return !e.IsLocalDaemon()
-}
-
-// DaemonAPIVersion returns the negotiated daemon api version
-func (e *Execution) DaemonAPIVersion() string {
-	version, err := e.APIClient().ServerVersion(context.TODO())
-	if err != nil {
-		return ""
-	}
-	return version.APIVersion
-}
-
-// Print the execution details to stdout
-// TODO: print everything
-func (e *Execution) Print() {
-	if e.IsLocalDaemon() {
-		fmt.Println("INFO: Testing against a local daemon")
-	} else {
-		fmt.Println("INFO: Testing against a remote daemon")
-	}
-}
-
-// APIClient returns an APIClient connected to the daemon under test
-func (e *Execution) APIClient() client.APIClient {
-	return e.client
-}
-
-// IsUserNamespace returns whether the user namespace remapping is enabled
-func (e *Execution) IsUserNamespace() bool {
-	root := os.Getenv("DOCKER_REMAP_ROOT")
-	return root != ""
-}
-
-// EnsureFrozenImagesLinux loads frozen test images into the daemon
-// if they aren't already loaded
-func EnsureFrozenImagesLinux(testEnv *Execution) error {
-	if testEnv.OSType == "linux" {
-		err := load.FrozenImagesLinux(testEnv.APIClient(), frozenImages...)
-		if err != nil {
-			return errors.Wrap(err, "error loading frozen images")
-		}
-	}
-	return nil
-}
diff --git a/internal/test/environment/protect.go b/internal/test/environment/protect.go
deleted file mode 100644
index b5b27d2dd4f4c..0000000000000
--- a/internal/test/environment/protect.go
+++ /dev/null
@@ -1,254 +0,0 @@
-package environment // import "github.com/docker/docker/internal/test/environment"
-
-import (
-	"context"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/filters"
-	dclient "github.com/docker/docker/client"
-	"github.com/docker/docker/internal/test"
-	"gotest.tools/assert"
-)
-
-var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:jessie"}
-
-type protectedElements struct {
-	containers map[string]struct{}
-	images     map[string]struct{}
-	networks   map[string]struct{}
-	plugins    map[string]struct{}
-	volumes    map[string]struct{}
-}
-
-func newProtectedElements() protectedElements {
-	return protectedElements{
-		containers: map[string]struct{}{},
-		images:     map[string]struct{}{},
-		networks:   map[string]struct{}{},
-		plugins:    map[string]struct{}{},
-		volumes:    map[string]struct{}{},
-	}
-}
-
-// ProtectAll protects the existing environment (containers, images, networks,
-// volumes, and, on Linux, plugins) from being cleaned up at the end of test
-// runs
-func ProtectAll(t testingT, testEnv *Execution) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	ProtectContainers(t, testEnv)
-	ProtectImages(t, testEnv)
-	ProtectNetworks(t, testEnv)
-	ProtectVolumes(t, testEnv)
-	if testEnv.OSType == "linux" {
-		ProtectPlugins(t, testEnv)
-	}
-}
-
-// ProtectContainer adds the specified container(s) to be protected in case of
-// clean
-func (e *Execution) ProtectContainer(t testingT, containers ...string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	for _, container := range containers {
-		e.protectedElements.containers[container] = struct{}{}
-	}
-}
-
-// ProtectContainers protects existing containers from being cleaned up at the
-// end of test runs
-func ProtectContainers(t testingT, testEnv *Execution) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	containers := getExistingContainers(t, testEnv)
-	testEnv.ProtectContainer(t, containers...)
-}
-
-func getExistingContainers(t assert.TestingT, testEnv *Execution) []string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	client := testEnv.APIClient()
-	containerList, err := client.ContainerList(context.Background(), types.ContainerListOptions{
-		All: true,
-	})
-	assert.NilError(t, err, "failed to list containers")
-
-	var containers []string
-	for _, container := range containerList {
-		containers = append(containers, container.ID)
-	}
-	return containers
-}
-
-// ProtectImage adds the specified image(s) to be protected in case of clean
-func (e *Execution) ProtectImage(t testingT, images ...string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	for _, image := range images {
-		e.protectedElements.images[image] = struct{}{}
-	}
-}
-
-// ProtectImages protects existing images and on linux frozen images from being
-// cleaned up at the end of test runs
-func ProtectImages(t testingT, testEnv *Execution) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	images := getExistingImages(t, testEnv)
-
-	if testEnv.OSType == "linux" {
-		images = append(images, frozenImages...)
-	}
-	testEnv.ProtectImage(t, images...)
-}
-
-func getExistingImages(t assert.TestingT, testEnv *Execution) []string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	client := testEnv.APIClient()
-	filter := filters.NewArgs()
-	filter.Add("dangling", "false")
-	imageList, err := client.ImageList(context.Background(), types.ImageListOptions{
-		All:     true,
-		Filters: filter,
-	})
-	assert.NilError(t, err, "failed to list images")
-
-	var images []string
-	for _, image := range imageList {
-		images = append(images, tagsFromImageSummary(image)...)
-	}
-	return images
-}
-
-func tagsFromImageSummary(image types.ImageSummary) []string {
-	var result []string
-	for _, tag := range image.RepoTags {
-		if tag != ":" {
-			result = append(result, tag)
-		}
-	}
-	for _, digest := range image.RepoDigests {
-		if digest != "@" {
-			result = append(result, digest)
-		}
-	}
-	return result
-}
-
-// ProtectNetwork adds the specified network(s) to be protected in case of
-// clean
-func (e *Execution) ProtectNetwork(t testingT, networks ...string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	for _, network := range networks {
-		e.protectedElements.networks[network] = struct{}{}
-	}
-}
-
-// ProtectNetworks protects existing networks from being cleaned up at the end
-// of test runs
-func ProtectNetworks(t testingT, testEnv *Execution) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	networks := getExistingNetworks(t, testEnv)
-	testEnv.ProtectNetwork(t, networks...)
-}
-
-func getExistingNetworks(t assert.TestingT, testEnv *Execution) []string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	client := testEnv.APIClient()
-	networkList, err := client.NetworkList(context.Background(), types.NetworkListOptions{})
-	assert.NilError(t, err, "failed to list networks")
-
-	var networks []string
-	for _, network := range networkList {
-		networks = append(networks, network.ID)
-	}
-	return networks
-}
-
-// ProtectPlugin adds the specified plugin(s) to be protected in case of clean
-func (e *Execution) ProtectPlugin(t testingT, plugins ...string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	for _, plugin := range plugins {
-		e.protectedElements.plugins[plugin] = struct{}{}
-	}
-}
-
-// ProtectPlugins protects existing plugins from being cleaned up at the end of
-// test runs
-func ProtectPlugins(t testingT, testEnv *Execution) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	plugins := getExistingPlugins(t, testEnv)
-	testEnv.ProtectPlugin(t, plugins...)
-}
-
-func getExistingPlugins(t assert.TestingT, testEnv *Execution) []string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	client := testEnv.APIClient()
-	pluginList, err := client.PluginList(context.Background(), filters.Args{})
-	// Docker EE does not allow cluster-wide plugin management.
-	if dclient.IsErrNotImplemented(err) {
-		return []string{}
-	}
-	assert.NilError(t, err, "failed to list plugins")
-
-	var plugins []string
-	for _, plugin := range pluginList {
-		plugins = append(plugins, plugin.Name)
-	}
-	return plugins
-}
-
-// ProtectVolume adds the specified volume(s) to be protected in case of clean
-func (e *Execution) ProtectVolume(t testingT, volumes ...string) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	for _, volume := range volumes {
-		e.protectedElements.volumes[volume] = struct{}{}
-	}
-}
-
-// ProtectVolumes protects existing volumes from being cleaned up at the end of
-// test runs
-func ProtectVolumes(t testingT, testEnv *Execution) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	volumes := getExistingVolumes(t, testEnv)
-	testEnv.ProtectVolume(t, volumes...)
-}
-
-func getExistingVolumes(t assert.TestingT, testEnv *Execution) []string {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	client := testEnv.APIClient()
-	volumeList, err := client.VolumeList(context.Background(), filters.Args{})
-	assert.NilError(t, err, "failed to list volumes")
-
-	var volumes []string
-	for _, volume := range volumeList.Volumes {
-		volumes = append(volumes, volume.Name)
-	}
-	return volumes
-}
diff --git a/internal/test/fakecontext/context.go b/internal/test/fakecontext/context.go
deleted file mode 100644
index 8b11da207ed05..0000000000000
--- a/internal/test/fakecontext/context.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package fakecontext // import "github.com/docker/docker/internal/test/fakecontext"
-
-import (
-	"bytes"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-
-	"github.com/docker/docker/internal/test"
-	"github.com/docker/docker/pkg/archive"
-)
-
-type testingT interface {
-	Fatal(args ...interface{})
-	Fatalf(string, ...interface{})
-}
-
-// New creates a fake build context
-func New(t testingT, dir string, modifiers ...func(*Fake) error) *Fake {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	fakeContext := &Fake{Dir: dir}
-	if dir == "" {
-		if err := newDir(fakeContext); err != nil {
-			t.Fatal(err)
-		}
-	}
-
-	for _, modifier := range modifiers {
-		if err := modifier(fakeContext); err != nil {
-			t.Fatal(err)
-		}
-	}
-
-	return fakeContext
-}
-
-func newDir(fake *Fake) error {
-	tmp, err := ioutil.TempDir("", "fake-context")
-	if err != nil {
-		return err
-	}
-	if err := os.Chmod(tmp, 0755); err != nil {
-		return err
-	}
-	fake.Dir = tmp
-	return nil
-}
-
-// WithFile adds the specified file (with content) in the build context
-func WithFile(name, content string) func(*Fake) error {
-	return func(ctx *Fake) error {
-		return ctx.Add(name, content)
-	}
-}
-
-// WithDockerfile adds the specified content as Dockerfile in the build context
-func WithDockerfile(content string) func(*Fake) error {
-	return WithFile("Dockerfile", content)
-}
-
-// WithFiles adds the specified files in the build context, content is a string
-func WithFiles(files map[string]string) func(*Fake) error {
-	return func(fakeContext *Fake) error {
-		for file, content := range files {
-			if err := fakeContext.Add(file, content); err != nil {
-				return err
-			}
-		}
-		return nil
-	}
-}
-
-// WithBinaryFiles adds the specified files in the build context, content is binary
-func WithBinaryFiles(files map[string]*bytes.Buffer) func(*Fake) error {
-	return func(fakeContext *Fake) error {
-		for file, content := range files {
-			if err := fakeContext.Add(file, content.String()); err != nil {
-				return err
-			}
-		}
-		return nil
-	}
-}
-
-// Fake creates directories that can be used as a build context
-type Fake struct {
-	Dir string
-}
-
-// Add a file at a path, creating directories where necessary
-func (f *Fake) Add(file, content string) error {
-	return f.addFile(file, []byte(content))
-}
-
-func (f *Fake) addFile(file string, content []byte) error {
-	fp := filepath.Join(f.Dir, filepath.FromSlash(file))
-	dirpath := filepath.Dir(fp)
-	if dirpath != "." {
-		if err := os.MkdirAll(dirpath, 0755); err != nil {
-			return err
-		}
-	}
-	return ioutil.WriteFile(fp, content, 0644)
-
-}
-
-// Delete a file at a path
-func (f *Fake) Delete(file string) error {
-	fp := filepath.Join(f.Dir, filepath.FromSlash(file))
-	return os.RemoveAll(fp)
-}
-
-// Close deletes the context
-func (f *Fake) Close() error {
-	return os.RemoveAll(f.Dir)
-}
-
-// AsTarReader returns a ReadCloser with the contents of Dir as a tar archive.
-func (f *Fake) AsTarReader(t testingT) io.ReadCloser {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	reader, err := archive.TarWithOptions(f.Dir, &archive.TarOptions{})
-	if err != nil {
-		t.Fatalf("Failed to create tar from %s: %s", f.Dir, err)
-	}
-	return reader
-}
diff --git a/internal/test/fakestorage/storage.go b/internal/test/fakestorage/storage.go
deleted file mode 100644
index b091cbc3f1c85..0000000000000
--- a/internal/test/fakestorage/storage.go
+++ /dev/null
@@ -1,200 +0,0 @@
-package fakestorage // import "github.com/docker/docker/internal/test/fakestorage"
-
-import (
-	"context"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"net/http/httptest"
-	"net/url"
-	"os"
-	"strings"
-
-	"github.com/docker/docker/api/types"
-	containertypes "github.com/docker/docker/api/types/container"
-	"github.com/docker/docker/client"
-	"github.com/docker/docker/internal/test"
-	"github.com/docker/docker/internal/test/environment"
-	"github.com/docker/docker/internal/test/fakecontext"
-	"github.com/docker/docker/internal/test/request"
-	"github.com/docker/docker/internal/testutil"
-	"github.com/docker/go-connections/nat"
-	"gotest.tools/assert"
-)
-
-var testEnv *environment.Execution
-
-type testingT interface {
-	assert.TestingT
-	logT
-	skipT
-	Fatal(args ...interface{})
-	Fatalf(string, ...interface{})
-}
-
-type logT interface {
-	Logf(string, ...interface{})
-}
-
-type skipT interface {
-	Skip(reason string)
-}
-
-// Fake is a static file server. It might be running locally or remotely
-// on test host.
-type Fake interface {
-	Close() error
-	URL() string
-	CtxDir() string
-}
-
-// SetTestEnvironment sets a static test environment
-// TODO: decouple this package from environment
-func SetTestEnvironment(env *environment.Execution) {
-	testEnv = env
-}
-
-// New returns a static file server that will be use as build context.
-func New(t testingT, dir string, modifiers ...func(*fakecontext.Fake) error) Fake {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	if testEnv == nil {
-		t.Fatal("fakstorage package requires SetTestEnvironment() to be called before use.")
-	}
-	ctx := fakecontext.New(t, dir, modifiers...)
-	switch {
-	case testEnv.IsRemoteDaemon() && strings.HasPrefix(request.DaemonHost(), "unix:///"):
-		t.Skip(fmt.Sprintf("e2e run : daemon is remote but docker host points to a unix socket"))
-	case testEnv.IsLocalDaemon():
-		return newLocalFakeStorage(ctx)
-	default:
-		return newRemoteFileServer(t, ctx, testEnv.APIClient())
-	}
-	return nil
-}
-
-// localFileStorage is a file storage on the running machine
-type localFileStorage struct {
-	*fakecontext.Fake
-	*httptest.Server
-}
-
-func (s *localFileStorage) URL() string {
-	return s.Server.URL
-}
-
-func (s *localFileStorage) CtxDir() string {
-	return s.Fake.Dir
-}
-
-func (s *localFileStorage) Close() error {
-	defer s.Server.Close()
-	return s.Fake.Close()
-}
-
-func newLocalFakeStorage(ctx *fakecontext.Fake) *localFileStorage {
-	handler := http.FileServer(http.Dir(ctx.Dir))
-	server := httptest.NewServer(handler)
-	return &localFileStorage{
-		Fake:   ctx,
-		Server: server,
-	}
-}
-
-// remoteFileServer is a containerized static file server started on the remote
-// testing machine to be used in URL-accepting docker build functionality.
-type remoteFileServer struct {
-	host      string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712
-	container string
-	image     string
-	client    client.APIClient
-	ctx       *fakecontext.Fake
-}
-
-func (f *remoteFileServer) URL() string {
-	u := url.URL{
-		Scheme: "http",
-		Host:   f.host}
-	return u.String()
-}
-
-func (f *remoteFileServer) CtxDir() string {
-	return f.ctx.Dir
-}
-
-func (f *remoteFileServer) Close() error {
-	defer func() {
-		if f.ctx != nil {
-			f.ctx.Close()
-		}
-		if f.image != "" {
-			if _, err := f.client.ImageRemove(context.Background(), f.image, types.ImageRemoveOptions{
-				Force: true,
-			}); err != nil {
-				fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err)
-			}
-		}
-		if err := f.client.Close(); err != nil {
-			fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err)
-		}
-	}()
-	if f.container == "" {
-		return nil
-	}
-	return f.client.ContainerRemove(context.Background(), f.container, types.ContainerRemoveOptions{
-		Force:         true,
-		RemoveVolumes: true,
-	})
-}
-
-func newRemoteFileServer(t testingT, ctx *fakecontext.Fake, c client.APIClient) *remoteFileServer {
-	var (
-		image     = fmt.Sprintf("fileserver-img-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10)))
-		container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10)))
-	)
-
-	ensureHTTPServerImage(t)
-
-	// Build the image
-	if err := ctx.Add("Dockerfile", `FROM httpserver
-COPY . /static`); err != nil {
-		t.Fatal(err)
-	}
-	resp, err := c.ImageBuild(context.Background(), ctx.AsTarReader(t), types.ImageBuildOptions{
-		NoCache: true,
-		Tags:    []string{image},
-	})
-	assert.NilError(t, err)
-	_, err = io.Copy(ioutil.Discard, resp.Body)
-	assert.NilError(t, err)
-
-	// Start the container
-	b, err := c.ContainerCreate(context.Background(), &containertypes.Config{
-		Image: image,
-	}, &containertypes.HostConfig{}, nil, container)
-	assert.NilError(t, err)
-	err = c.ContainerStart(context.Background(), b.ID, types.ContainerStartOptions{})
-	assert.NilError(t, err)
-
-	// Find out the system assigned port
-	i, err := c.ContainerInspect(context.Background(), b.ID)
-	assert.NilError(t, err)
-	newP, err := nat.NewPort("tcp", "80")
-	assert.NilError(t, err)
-	ports, exists := i.NetworkSettings.Ports[newP]
-	if !exists || len(ports) != 1 {
-		t.Fatalf("unable to find port 80/tcp for %s", container)
-	}
-	host := ports[0].HostIP
-	port := ports[0].HostPort
-
-	return &remoteFileServer{
-		container: container,
-		image:     image,
-		host:      fmt.Sprintf("%s:%s", host, port),
-		ctx:       ctx,
-		client:    c,
-	}
-}
diff --git a/internal/test/fixtures/plugin/plugin.go b/internal/test/fixtures/plugin/plugin.go
deleted file mode 100644
index 71ad1281848d7..0000000000000
--- a/internal/test/fixtures/plugin/plugin.go
+++ /dev/null
@@ -1,216 +0,0 @@
-package plugin // import "github.com/docker/docker/internal/test/fixtures/plugin"
-
-import (
-	"context"
-	"encoding/json"
-	"io"
-	"io/ioutil"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"time"
-
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/pkg/archive"
-	"github.com/docker/docker/plugin"
-	"github.com/docker/docker/registry"
-	"github.com/pkg/errors"
-)
-
-// CreateOpt is passed used to change the default plugin config before
-// creating it
-type CreateOpt func(*Config)
-
-// Config wraps types.PluginConfig to provide some extra state for options
-// extra customizations on the plugin details, such as using a custom binary to
-// create the plugin with.
-type Config struct {
-	*types.PluginConfig
-	binPath string
-}
-
-// WithBinary is a CreateOpt to set an custom binary to create the plugin with.
-// This binary must be statically compiled.
-func WithBinary(bin string) CreateOpt {
-	return func(cfg *Config) {
-		cfg.binPath = bin
-	}
-}
-
-// CreateClient is the interface used for `BuildPlugin` to interact with the
-// daemon.
-type CreateClient interface {
-	PluginCreate(context.Context, io.Reader, types.PluginCreateOptions) error
-}
-
-// Create creates a new plugin with the specified name
-func Create(ctx context.Context, c CreateClient, name string, opts ...CreateOpt) error {
-	tmpDir, err := ioutil.TempDir("", "create-test-plugin")
-	if err != nil {
-		return err
-	}
-	defer os.RemoveAll(tmpDir)
-
-	tar, err := makePluginBundle(tmpDir, opts...)
-	if err != nil {
-		return err
-	}
-	defer tar.Close()
-
-	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
-	defer cancel()
-
-	return c.PluginCreate(ctx, tar, types.PluginCreateOptions{RepoName: name})
-}
-
-// CreateInRegistry makes a plugin (locally) and pushes it to a registry.
-// This does not use a dockerd instance to create or push the plugin.
-// If you just want to create a plugin in some daemon, use `Create`.
-//
-// This can be useful when testing plugins on swarm where you don't really want
-// the plugin to exist on any of the daemons (immediately) and there needs to be
-// some way to distribute the plugin.
-func CreateInRegistry(ctx context.Context, repo string, auth *types.AuthConfig, opts ...CreateOpt) error {
-	tmpDir, err := ioutil.TempDir("", "create-test-plugin-local")
-	if err != nil {
-		return err
-	}
-	defer os.RemoveAll(tmpDir)
-
-	inPath := filepath.Join(tmpDir, "plugin")
-	if err := os.MkdirAll(inPath, 0755); err != nil {
-		return errors.Wrap(err, "error creating plugin root")
-	}
-
-	tar, err := makePluginBundle(inPath, opts...)
-	if err != nil {
-		return err
-	}
-	defer tar.Close()
-
-	dummyExec := func(m *plugin.Manager) (plugin.Executor, error) {
-		return nil, nil
-	}
-
-	regService, err := registry.NewService(registry.ServiceOptions{V2Only: true})
-	if err != nil {
-		return err
-	}
-
-	managerConfig := plugin.ManagerConfig{
-		Store:           plugin.NewStore(),
-		RegistryService: regService,
-		Root:            filepath.Join(tmpDir, "root"),
-		ExecRoot:        "/run/docker", // manager init fails if not set
-		CreateExecutor:  dummyExec,
-		LogPluginEvent:  func(id, name, action string) {}, // panics when not set
-	}
-	manager, err := plugin.NewManager(managerConfig)
-	if err != nil {
-		return errors.Wrap(err, "error creating plugin manager")
-	}
-
-	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
-	defer cancel()
-	if err := manager.CreateFromContext(ctx, tar, &types.PluginCreateOptions{RepoName: repo}); err != nil {
-		return err
-	}
-
-	if auth == nil {
-		auth = &types.AuthConfig{}
-	}
-	err = manager.Push(ctx, repo, nil, auth, ioutil.Discard)
-	return errors.Wrap(err, "error pushing plugin")
-}
-
-func makePluginBundle(inPath string, opts ...CreateOpt) (io.ReadCloser, error) {
-	p := &types.PluginConfig{
-		Interface: types.PluginConfigInterface{
-			Socket: "basic.sock",
-			Types:  []types.PluginInterfaceType{{Capability: "docker.dummy/1.0"}},
-		},
-		Entrypoint: []string{"/basic"},
-	}
-	cfg := &Config{
-		PluginConfig: p,
-	}
-	for _, o := range opts {
-		o(cfg)
-	}
-	if cfg.binPath == "" {
-		binPath, err := ensureBasicPluginBin()
-		if err != nil {
-			return nil, err
-		}
-		cfg.binPath = binPath
-	}
-
-	configJSON, err := json.Marshal(p)
-	if err != nil {
-		return nil, err
-	}
-	if err := ioutil.WriteFile(filepath.Join(inPath, "config.json"), configJSON, 0644); err != nil {
-		return nil, err
-	}
-	if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(p.Entrypoint[0])), 0755); err != nil {
-		return nil, errors.Wrap(err, "error creating plugin rootfs dir")
-	}
-
-	// Ensure the mount target paths exist
-	for _, m := range p.Mounts {
-		var stat os.FileInfo
-		if m.Source != nil {
-			stat, err = os.Stat(*m.Source)
-			if err != nil && !os.IsNotExist(err) {
-				return nil, err
-			}
-		}
-
-		if stat == nil || stat.IsDir() {
-			var mode os.FileMode = 0755
-			if stat != nil {
-				mode = stat.Mode()
-			}
-			if err := os.MkdirAll(filepath.Join(inPath, "rootfs", m.Destination), mode); err != nil {
-				return nil, errors.Wrap(err, "error preparing plugin mount destination path")
-			}
-		} else {
-			if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(m.Destination)), 0755); err != nil {
-				return nil, errors.Wrap(err, "error preparing plugin mount destination dir")
-			}
-			f, err := os.Create(filepath.Join(inPath, "rootfs", m.Destination))
-			if err != nil && !os.IsExist(err) {
-				return nil, errors.Wrap(err, "error preparing plugin mount destination file")
-			}
-			if f != nil {
-				f.Close()
-			}
-		}
-	}
-	if err := archive.NewDefaultArchiver().CopyFileWithTar(cfg.binPath, filepath.Join(inPath, "rootfs", p.Entrypoint[0])); err != nil {
-		return nil, errors.Wrap(err, "error copying plugin binary to rootfs path")
-	}
-	tar, err := archive.Tar(inPath, archive.Uncompressed)
-	return tar, errors.Wrap(err, "error making plugin archive")
-}
-
-func ensureBasicPluginBin() (string, error) {
-	name := "docker-basic-plugin"
-	p, err := exec.LookPath(name)
-	if err == nil {
-		return p, nil
-	}
-
-	goBin, err := exec.LookPath("go")
-	if err != nil {
-		return "", err
-	}
-	installPath := filepath.Join(os.Getenv("GOPATH"), "bin", name)
-	sourcePath := filepath.Join("github.com", "docker", "docker", "internal", "test", "fixtures", "plugin", "basic")
-	cmd := exec.Command(goBin, "build", "-o", installPath, sourcePath)
-	cmd.Env = append(cmd.Env, "GOPATH="+os.Getenv("GOPATH"), "CGO_ENABLED=0")
-	if out, err := cmd.CombinedOutput(); err != nil {
-		return "", errors.Wrapf(err, "error building basic plugin bin: %s", string(out))
-	}
-	return installPath, nil
-}
diff --git a/internal/test/helper.go b/internal/test/helper.go
deleted file mode 100644
index 1b9fd75090eb0..0000000000000
--- a/internal/test/helper.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package test
-
-// HelperT is a subset of testing.T that implements the Helper function
-type HelperT interface {
-	Helper()
-}
diff --git a/internal/test/registry/ops.go b/internal/test/registry/ops.go
deleted file mode 100644
index c004f37424fe5..0000000000000
--- a/internal/test/registry/ops.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package registry
-
-// Schema1 sets the registry to serve v1 api
-func Schema1(c *Config) {
-	c.schema1 = true
-}
-
-// Htpasswd sets the auth method with htpasswd
-func Htpasswd(c *Config) {
-	c.auth = "htpasswd"
-}
-
-// Token sets the auth method to token, with the specified token url
-func Token(tokenURL string) func(*Config) {
-	return func(c *Config) {
-		c.auth = "token"
-		c.tokenURL = tokenURL
-	}
-}
-
-// URL sets the registry url
-func URL(registryURL string) func(*Config) {
-	return func(c *Config) {
-		c.registryURL = registryURL
-	}
-}
diff --git a/internal/test/registry/registry.go b/internal/test/registry/registry.go
deleted file mode 100644
index b6128d3ba4d3c..0000000000000
--- a/internal/test/registry/registry.go
+++ /dev/null
@@ -1,255 +0,0 @@
-package registry // import "github.com/docker/docker/internal/test/registry"
-
-import (
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"time"
-
-	"github.com/docker/docker/internal/test"
-	"github.com/opencontainers/go-digest"
-	"gotest.tools/assert"
-)
-
-const (
-	// V2binary is the name of the registry v2 binary
-	V2binary = "registry-v2"
-	// V2binarySchema1 is the name of the registry that serve schema1
-	V2binarySchema1 = "registry-v2-schema1"
-	// DefaultURL is the default url that will be used by the registry (if not specified otherwise)
-	DefaultURL = "127.0.0.1:5000"
-)
-
-type testingT interface {
-	assert.TestingT
-	logT
-	Fatal(...interface{})
-	Fatalf(string, ...interface{})
-}
-
-type logT interface {
-	Logf(string, ...interface{})
-}
-
-// V2 represent a registry version 2
-type V2 struct {
-	cmd         *exec.Cmd
-	registryURL string
-	dir         string
-	auth        string
-	username    string
-	password    string
-	email       string
-}
-
-// Config contains the test registry configuration
-type Config struct {
-	schema1     bool
-	auth        string
-	tokenURL    string
-	registryURL string
-}
-
-// NewV2 creates a v2 registry server
-func NewV2(t testingT, ops ...func(*Config)) *V2 {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	c := &Config{
-		registryURL: DefaultURL,
-	}
-	for _, op := range ops {
-		op(c)
-	}
-	tmp, err := ioutil.TempDir("", "registry-test-")
-	assert.NilError(t, err)
-	template := `version: 0.1
-loglevel: debug
-storage:
-    filesystem:
-        rootdirectory: %s
-http:
-    addr: %s
-%s`
-	var (
-		authTemplate string
-		username     string
-		password     string
-		email        string
-	)
-	switch c.auth {
-	case "htpasswd":
-		htpasswdPath := filepath.Join(tmp, "htpasswd")
-		// generated with: htpasswd -Bbn testuser testpassword
-		userpasswd := "testuser:$2y$05$sBsSqk0OpSD1uTZkHXc4FeJ0Z70wLQdAX/82UiHuQOKbNbBrzs63m"
-		username = "testuser"
-		password = "testpassword"
-		email = "test@test.org"
-		err := ioutil.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644))
-		assert.NilError(t, err)
-		authTemplate = fmt.Sprintf(`auth:
-    htpasswd:
-        realm: basic-realm
-        path: %s
-`, htpasswdPath)
-	case "token":
-		authTemplate = fmt.Sprintf(`auth:
-    token:
-        realm: %s
-        service: "registry"
-        issuer: "auth-registry"
-        rootcertbundle: "fixtures/registry/cert.pem"
-`, c.tokenURL)
-	}
-
-	confPath := filepath.Join(tmp, "config.yaml")
-	config, err := os.Create(confPath)
-	assert.NilError(t, err)
-	defer config.Close()
-
-	if _, err := fmt.Fprintf(config, template, tmp, c.registryURL, authTemplate); err != nil {
-		// FIXME(vdemeester) use a defer/clean func
-		os.RemoveAll(tmp)
-		t.Fatal(err)
-	}
-
-	binary := V2binary
-	if c.schema1 {
-		binary = V2binarySchema1
-	}
-	cmd := exec.Command(binary, confPath)
-	if err := cmd.Start(); err != nil {
-		// FIXME(vdemeester) use a defer/clean func
-		os.RemoveAll(tmp)
-		t.Fatal(err)
-	}
-	return &V2{
-		cmd:         cmd,
-		dir:         tmp,
-		auth:        c.auth,
-		username:    username,
-		password:    password,
-		email:       email,
-		registryURL: c.registryURL,
-	}
-}
-
-// WaitReady waits for the registry to be ready to serve requests (or fail after a while)
-func (r *V2) WaitReady(t testingT) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	var err error
-	for i := 0; i != 50; i++ {
-		if err = r.Ping(); err == nil {
-			return
-		}
-		time.Sleep(100 * time.Millisecond)
-	}
-	t.Fatalf("timeout waiting for test registry to become available: %v", err)
-}
-
-// Ping sends an http request to the current registry, and fail if it doesn't respond correctly
-func (r *V2) Ping() error {
-	// We always ping through HTTP for our test registry.
-	resp, err := http.Get(fmt.Sprintf("http://%s/v2/", r.registryURL))
-	if err != nil {
-		return err
-	}
-	resp.Body.Close()
-
-	fail := resp.StatusCode != http.StatusOK
-	if r.auth != "" {
-		// unauthorized is a _good_ status when pinging v2/ and it needs auth
-		fail = fail && resp.StatusCode != http.StatusUnauthorized
-	}
-	if fail {
-		return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode)
-	}
-	return nil
-}
-
-// Close kills the registry server
-func (r *V2) Close() {
-	r.cmd.Process.Kill()
-	r.cmd.Process.Wait()
-	os.RemoveAll(r.dir)
-}
-
-func (r *V2) getBlobFilename(blobDigest digest.Digest) string {
-	// Split the digest into its algorithm and hex components.
-	dgstAlg, dgstHex := blobDigest.Algorithm(), blobDigest.Hex()
-
-	// The path to the target blob data looks something like:
-	//   baseDir + "docker/registry/v2/blobs/sha256/a3/a3ed...46d4/data"
-	return fmt.Sprintf("%s/docker/registry/v2/blobs/%s/%s/%s/data", r.dir, dgstAlg, dgstHex[:2], dgstHex)
-}
-
-// ReadBlobContents read the file corresponding to the specified digest
-func (r *V2) ReadBlobContents(t assert.TestingT, blobDigest digest.Digest) []byte {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	// Load the target manifest blob.
-	manifestBlob, err := ioutil.ReadFile(r.getBlobFilename(blobDigest))
-	assert.NilError(t, err, "unable to read blob")
-	return manifestBlob
-}
-
-// WriteBlobContents write the file corresponding to the specified digest with the given content
-func (r *V2) WriteBlobContents(t assert.TestingT, blobDigest digest.Digest, data []byte) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	err := ioutil.WriteFile(r.getBlobFilename(blobDigest), data, os.FileMode(0644))
-	assert.NilError(t, err, "unable to write malicious data blob")
-}
-
-// TempMoveBlobData moves the existing data file aside, so that we can replace it with a
-// malicious blob of data for example.
-func (r *V2) TempMoveBlobData(t testingT, blobDigest digest.Digest) (undo func()) {
-	if ht, ok := t.(test.HelperT); ok {
-		ht.Helper()
-	}
-	tempFile, err := ioutil.TempFile("", "registry-temp-blob-")
-	assert.NilError(t, err, "unable to get temporary blob file")
-	tempFile.Close()
-
-	blobFilename := r.getBlobFilename(blobDigest)
-
-	// Move the existing data file aside, so that we can replace it with a
-	// another blob of data.
-	if err := os.Rename(blobFilename, tempFile.Name()); err != nil {
-		// FIXME(vdemeester) use a defer/clean func
-		os.Remove(tempFile.Name())
-		t.Fatalf("unable to move data blob: %s", err)
-	}
-
-	return func() {
-		os.Rename(tempFile.Name(), blobFilename)
-		os.Remove(tempFile.Name())
-	}
-}
-
-// Username returns the configured user name of the server
-func (r *V2) Username() string {
-	return r.username
-}
-
-// Password returns the configured password of the server
-func (r *V2) Password() string {
-	return r.password
-}
-
-// Email returns the configured email of the server
-func (r *V2) Email() string {
-	return r.email
-}
-
-// Path returns the path where the registry write data
-func (r *V2) Path() string {
-	return filepath.Join(r.dir, "docker", "registry", "v2")
-}
diff --git a/internal/test/request/ops.go b/internal/test/request/ops.go
deleted file mode 100644
index c85308c476068..0000000000000
--- a/internal/test/request/ops.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package request
-
-import (
-	"bytes"
-	"encoding/json"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"strings"
-)
-
-// Options defines request options, like request modifiers and which host to target
-type Options struct {
-	host             string
-	requestModifiers []func(*http.Request) error
-}
-
-// Host creates a modifier that sets the specified host as the request URL host
-func Host(host string) func(*Options) {
-	return func(o *Options) {
-		o.host = host
-	}
-}
-
-// With adds a request modifier to the options
-func With(f func(*http.Request) error) func(*Options) {
-	return func(o *Options) {
-		o.requestModifiers = append(o.requestModifiers, f)
-	}
-}
-
-// Method creates a modifier that sets the specified string as the request method
-func Method(method string) func(*Options) {
-	return With(func(req *http.Request) error {
-		req.Method = method
-		return nil
-	})
-}
-
-// RawString sets the specified string as body for the request
-func RawString(content string) func(*Options) {
-	return RawContent(ioutil.NopCloser(strings.NewReader(content)))
-}
-
-// RawContent sets the specified reader as body for the request
-func RawContent(reader io.ReadCloser) func(*Options) {
-	return With(func(req *http.Request) error {
-		req.Body = reader
-		return nil
-	})
-}
-
-// ContentType sets the specified Content-Type request header
-func ContentType(contentType string) func(*Options) {
-	return With(func(req *http.Request) error {
-		req.Header.Set("Content-Type", contentType)
-		return nil
-	})
-}
-
-// JSON sets the Content-Type request header to json
-func JSON(o *Options) {
-	ContentType("application/json")(o)
-}
-
-// JSONBody creates a modifier that encodes the specified data to a JSON string and set it as request body. It also sets
-// the Content-Type header of the request.
-func JSONBody(data interface{}) func(*Options) {
-	return With(func(req *http.Request) error {
-		jsonData := bytes.NewBuffer(nil)
-		if err := json.NewEncoder(jsonData).Encode(data); err != nil {
-			return err
-		}
-		req.Body = ioutil.NopCloser(jsonData)
-		req.Header.Set("Content-Type", "application/json")
-		return nil
-	})
-}
diff --git a/internal/test/suite/interfaces.go b/internal/test/suite/interfaces.go
new file mode 100644
index 0000000000000..263de86ab8cd7
--- /dev/null
+++ b/internal/test/suite/interfaces.go
@@ -0,0 +1,33 @@
+package suite
+
+import "testing"
+
+// SetupAllSuite has a SetupSuite method, which will run before the
+// tests in the suite are run.
+type SetupAllSuite interface {
+	SetUpSuite(t *testing.T)
+}
+
+// SetupTestSuite has a SetupTest method, which will run before each
+// test in the suite.
+type SetupTestSuite interface {
+	SetUpTest(t *testing.T)
+}
+
+// TearDownAllSuite has a TearDownSuite method, which will run after
+// all the tests in the suite have been run.
+type TearDownAllSuite interface {
+	TearDownSuite(t *testing.T)
+}
+
+// TearDownTestSuite has a TearDownTest method, which will run after
+// each test in the suite.
+type TearDownTestSuite interface {
+	TearDownTest(t *testing.T)
+}
+
+// TimeoutTestSuite has a OnTimeout method, which will run after
+// a single test times out after a period specified by -timeout flag.
+type TimeoutTestSuite interface {
+	OnTimeout()
+}
diff --git a/internal/test/suite/suite.go b/internal/test/suite/suite.go
new file mode 100644
index 0000000000000..edb6e40c1c779
--- /dev/null
+++ b/internal/test/suite/suite.go
@@ -0,0 +1,72 @@
+// Package suite is a simplified version of testify's suite package which has unnecessary dependencies.
+// Please remove this package whenever possible.
+package suite
+
+import (
+	"flag"
+	"reflect"
+	"runtime/debug"
+	"strings"
+	"testing"
+)
+
+// TimeoutFlag is the flag to set a per-test timeout when running tests. Defaults to `-timeout`.
+var TimeoutFlag = flag.Duration("timeout", 0, "DO NOT USE")
+
+var typTestingT = reflect.TypeOf(new(testing.T))
+
+// Run takes a testing suite and runs all of the tests attached to it.
+func Run(t *testing.T, suite interface{}) {
+	defer failOnPanic(t)
+
+	suiteSetupDone := false
+
+	defer func() {
+		if suiteSetupDone {
+			if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
+				tearDownAllSuite.TearDownSuite(t)
+			}
+		}
+	}()
+
+	methodFinder := reflect.TypeOf(suite)
+	for index := 0; index < methodFinder.NumMethod(); index++ {
+		method := methodFinder.Method(index)
+		if !methodFilter(method.Name, method.Type) {
+			continue
+		}
+		t.Run(method.Name, func(t *testing.T) {
+			defer failOnPanic(t)
+
+			if !suiteSetupDone {
+				if setupAllSuite, ok := suite.(SetupAllSuite); ok {
+					setupAllSuite.SetUpSuite(t)
+				}
+				suiteSetupDone = true
+			}
+
+			if setupTestSuite, ok := suite.(SetupTestSuite); ok {
+				setupTestSuite.SetUpTest(t)
+			}
+			defer func() {
+				if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
+					tearDownTestSuite.TearDownTest(t)
+				}
+			}()
+
+			method.Func.Call([]reflect.Value{reflect.ValueOf(suite), reflect.ValueOf(t)})
+		})
+	}
+}
+
+func failOnPanic(t *testing.T) {
+	r := recover()
+	if r != nil {
+		t.Errorf("test suite panicked: %v\n%s", r, debug.Stack())
+		t.FailNow()
+	}
+}
+
+func methodFilter(name string, typ reflect.Type) bool {
+	return strings.HasPrefix(name, "Test") && typ.NumIn() == 2 && typ.In(1) == typTestingT // 2 params: method receiver and *testing.T
+}
diff --git a/internal/test/suite/testify.LICENSE b/internal/test/suite/testify.LICENSE
new file mode 100644
index 0000000000000..f38ec5956b640
--- /dev/null
+++ b/internal/test/suite/testify.LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/internal/testutil/helpers.go b/internal/testutil/helpers.go
deleted file mode 100644
index 38cd1693f5f2c..0000000000000
--- a/internal/testutil/helpers.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package testutil // import "github.com/docker/docker/internal/testutil"
-
-import (
-	"io"
-)
-
-// DevZero acts like /dev/zero but in an OS-independent fashion.
-var DevZero io.Reader = devZero{}
-
-type devZero struct{}
-
-func (d devZero) Read(p []byte) (n int, err error) {
-	for i := range p {
-		p[i] = 0
-	}
-	return len(p), nil
-}
diff --git a/internal/testutil/stringutils.go b/internal/testutil/stringutils.go
deleted file mode 100644
index 574aeb51f2a82..0000000000000
--- a/internal/testutil/stringutils.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package testutil // import "github.com/docker/docker/internal/testutil"
-
-import "math/rand"
-
-// GenerateRandomAlphaOnlyString generates an alphabetical random string with length n.
-func GenerateRandomAlphaOnlyString(n int) string {
-	// make a really long string
-	letters := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
-	b := make([]byte, n)
-	for i := range b {
-		b[i] = letters[rand.Intn(len(letters))]
-	}
-	return string(b)
-}
diff --git a/layer/empty.go b/layer/empty.go
index c81c702140280..46fc571255bd2 100644
--- a/layer/empty.go
+++ b/layer/empty.go
@@ -5,7 +5,6 @@ import (
 	"bytes"
 	"fmt"
 	"io"
-	"io/ioutil"
 )
 
 // DigestSHA256EmptyTar is the canonical sha256 digest of empty tar file -
@@ -21,7 +20,7 @@ func (el *emptyLayer) TarStream() (io.ReadCloser, error) {
 	buf := new(bytes.Buffer)
 	tarWriter := tar.NewWriter(buf)
 	tarWriter.Close()
-	return ioutil.NopCloser(buf), nil
+	return io.NopCloser(buf), nil
 }
 
 func (el *emptyLayer) TarStreamFrom(p ChainID) (io.ReadCloser, error) {
diff --git a/layer/empty_test.go b/layer/empty_test.go
index ec9fbc1a3c352..d4671a1b07e1b 100644
--- a/layer/empty_test.go
+++ b/layer/empty_test.go
@@ -4,7 +4,7 @@ import (
 	"io"
 	"testing"
 
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
 )
 
 func TestEmptyLayer(t *testing.T) {
diff --git a/layer/filestore.go b/layer/filestore.go
index 208a0c3a859a4..37bc41d514255 100644
--- a/layer/filestore.go
+++ b/layer/filestore.go
@@ -5,7 +5,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"regexp"
@@ -14,7 +13,7 @@ import (
 
 	"github.com/docker/distribution"
 	"github.com/docker/docker/pkg/ioutils"
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
@@ -143,7 +142,7 @@ func (fm *fileMetadataTransaction) String() string {
 }
 
 func (fms *fileMetadataStore) GetSize(layer ChainID) (int64, error) {
-	content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "size"))
+	content, err := os.ReadFile(fms.getLayerFilename(layer, "size"))
 	if err != nil {
 		return 0, err
 	}
@@ -157,7 +156,7 @@ func (fms *fileMetadataStore) GetSize(layer ChainID) (int64, error) {
 }
 
 func (fms *fileMetadataStore) GetParent(layer ChainID) (ChainID, error) {
-	content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "parent"))
+	content, err := os.ReadFile(fms.getLayerFilename(layer, "parent"))
 	if err != nil {
 		if os.IsNotExist(err) {
 			return "", nil
@@ -174,7 +173,7 @@ func (fms *fileMetadataStore) GetParent(layer ChainID) (ChainID, error) {
 }
 
 func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) {
-	content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "diff"))
+	content, err := os.ReadFile(fms.getLayerFilename(layer, "diff"))
 	if err != nil {
 		return "", err
 	}
@@ -188,7 +187,7 @@ func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) {
 }
 
 func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) {
-	contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "cache-id"))
+	contentBytes, err := os.ReadFile(fms.getLayerFilename(layer, "cache-id"))
 	if err != nil {
 		return "", err
 	}
@@ -202,7 +201,7 @@ func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) {
 }
 
 func (fms *fileMetadataStore) GetDescriptor(layer ChainID) (distribution.Descriptor, error) {
-	content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "descriptor.json"))
+	content, err := os.ReadFile(fms.getLayerFilename(layer, "descriptor.json"))
 	if err != nil {
 		if os.IsNotExist(err) {
 			// only return empty descriptor to represent what is stored
@@ -240,25 +239,25 @@ func (fms *fileMetadataStore) SetMountID(mount string, mountID string) error {
 	if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
 		return err
 	}
-	return ioutil.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0644)
+	return os.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0644)
 }
 
 func (fms *fileMetadataStore) SetInitID(mount string, init string) error {
 	if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
 		return err
 	}
-	return ioutil.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0644)
+	return os.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0644)
 }
 
 func (fms *fileMetadataStore) SetMountParent(mount string, parent ChainID) error {
 	if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
 		return err
 	}
-	return ioutil.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0644)
+	return os.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0644)
 }
 
 func (fms *fileMetadataStore) GetMountID(mount string) (string, error) {
-	contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "mount-id"))
+	contentBytes, err := os.ReadFile(fms.getMountFilename(mount, "mount-id"))
 	if err != nil {
 		return "", err
 	}
@@ -272,7 +271,7 @@ func (fms *fileMetadataStore) GetMountID(mount string) (string, error) {
 }
 
 func (fms *fileMetadataStore) GetInitID(mount string) (string, error) {
-	contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "init-id"))
+	contentBytes, err := os.ReadFile(fms.getMountFilename(mount, "init-id"))
 	if err != nil {
 		if os.IsNotExist(err) {
 			return "", nil
@@ -289,7 +288,7 @@ func (fms *fileMetadataStore) GetInitID(mount string) (string, error) {
 }
 
 func (fms *fileMetadataStore) GetMountParent(mount string) (ChainID, error) {
-	content, err := ioutil.ReadFile(fms.getMountFilename(mount, "parent"))
+	content, err := os.ReadFile(fms.getMountFilename(mount, "parent"))
 	if err != nil {
 		if os.IsNotExist(err) {
 			return "", nil
@@ -305,10 +304,59 @@ func (fms *fileMetadataStore) GetMountParent(mount string) (ChainID, error) {
 	return ChainID(dgst), nil
 }
 
+func (fms *fileMetadataStore) getOrphan() ([]roLayer, error) {
+	var orphanLayers []roLayer
+	for _, algorithm := range supportedAlgorithms {
+		fileInfos, err := os.ReadDir(filepath.Join(fms.root, string(algorithm)))
+		if err != nil {
+			if os.IsNotExist(err) {
+				continue
+			}
+			return nil, err
+		}
+
+		for _, fi := range fileInfos {
+			if !fi.IsDir() || !strings.HasSuffix(fi.Name(), "-removing") {
+				continue
+			}
+			// At this stage, fi.Name value looks like --removing
+			// Split on '-' to get the digest value.
+			nameSplit := strings.Split(fi.Name(), "-")
+			dgst := digest.NewDigestFromEncoded(algorithm, nameSplit[0])
+			if err := dgst.Validate(); err != nil {
+				logrus.WithError(err).WithField("digest", string(algorithm)+":"+nameSplit[0]).Debug("ignoring invalid digest")
+				continue
+			}
+
+			chainFile := filepath.Join(fms.root, string(algorithm), fi.Name(), "cache-id")
+			contentBytes, err := os.ReadFile(chainFile)
+			if err != nil {
+				if !os.IsNotExist(err) {
+					logrus.WithError(err).WithField("digest", dgst).Error("failed to read cache ID")
+				}
+				continue
+			}
+			cacheID := strings.TrimSpace(string(contentBytes))
+			if cacheID == "" {
+				logrus.Error("invalid cache ID")
+				continue
+			}
+
+			l := &roLayer{
+				chainID: ChainID(dgst),
+				cacheID: cacheID,
+			}
+			orphanLayers = append(orphanLayers, *l)
+		}
+	}
+
+	return orphanLayers, nil
+}
+
 func (fms *fileMetadataStore) List() ([]ChainID, []string, error) {
 	var ids []ChainID
 	for _, algorithm := range supportedAlgorithms {
-		fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, string(algorithm)))
+		fileInfos, err := os.ReadDir(filepath.Join(fms.root, string(algorithm)))
 		if err != nil {
 			if os.IsNotExist(err) {
 				continue
@@ -328,7 +376,7 @@ func (fms *fileMetadataStore) List() ([]ChainID, []string, error) {
 		}
 	}
 
-	fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, "mounts"))
+	fileInfos, err := os.ReadDir(filepath.Join(fms.root, "mounts"))
 	if err != nil {
 		if os.IsNotExist(err) {
 			return ids, []string{}, nil
@@ -346,8 +394,39 @@ func (fms *fileMetadataStore) List() ([]ChainID, []string, error) {
 	return ids, mounts, nil
 }
 
-func (fms *fileMetadataStore) Remove(layer ChainID) error {
-	return os.RemoveAll(fms.getLayerDirectory(layer))
+// Remove layerdb folder if that is marked for removal
+func (fms *fileMetadataStore) Remove(layer ChainID, cache string) error {
+	dgst := digest.Digest(layer)
+	files, err := os.ReadDir(filepath.Join(fms.root, string(dgst.Algorithm())))
+	if err != nil {
+		return err
+	}
+	for _, f := range files {
+		if !strings.HasSuffix(f.Name(), "-removing") || !strings.HasPrefix(f.Name(), dgst.Encoded()) {
+			continue
+		}
+
+		// Make sure that we only remove layerdb folder which points to
+		// requested cacheID
+		dir := filepath.Join(fms.root, string(dgst.Algorithm()), f.Name())
+		chainFile := filepath.Join(dir, "cache-id")
+		contentBytes, err := os.ReadFile(chainFile)
+		if err != nil {
+			logrus.WithError(err).WithField("file", chainFile).Error("cannot get cache ID")
+			continue
+		}
+		cacheID := strings.TrimSpace(string(contentBytes))
+		if cacheID != cache {
+			continue
+		}
+		logrus.Debugf("Removing folder: %s", dir)
+		err = os.RemoveAll(dir)
+		if err != nil && !os.IsNotExist(err) {
+			logrus.WithError(err).WithField("name", f.Name()).Error("cannot remove layer")
+			continue
+		}
+	}
+	return nil
 }
 
 func (fms *fileMetadataStore) RemoveMount(mount string) error {
diff --git a/layer/filestore_test.go b/layer/filestore_test.go
index 498379e37fe2e..926260aca0221 100644
--- a/layer/filestore_test.go
+++ b/layer/filestore_test.go
@@ -2,7 +2,6 @@ package layer // import "github.com/docker/docker/layer"
 
 import (
 	"fmt"
-	"io/ioutil"
 	"math/rand"
 	"os"
 	"path/filepath"
@@ -10,7 +9,8 @@ import (
 	"syscall"
 	"testing"
 
-	"github.com/opencontainers/go-digest"
+	"github.com/docker/docker/pkg/stringid"
+	digest "github.com/opencontainers/go-digest"
 )
 
 func randomLayerID(seed int64) ChainID {
@@ -20,7 +20,7 @@ func randomLayerID(seed int64) ChainID {
 }
 
 func newFileMetadataStore(t *testing.T) (*fileMetadataStore, string, func()) {
-	td, err := ioutil.TempDir("", "layers-")
+	td, err := os.MkdirTemp("", "layers-")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -51,7 +51,7 @@ func TestCommitFailure(t *testing.T) {
 	fms, td, cleanup := newFileMetadataStore(t)
 	defer cleanup()
 
-	if err := ioutil.WriteFile(filepath.Join(td, "sha256"), []byte("was here first!"), 0644); err != nil {
+	if err := os.WriteFile(filepath.Join(td, "sha256"), []byte("was here first!"), 0644); err != nil {
 		t.Fatal(err)
 	}
 
@@ -75,7 +75,7 @@ func TestStartTransactionFailure(t *testing.T) {
 	fms, td, cleanup := newFileMetadataStore(t)
 	defer cleanup()
 
-	if err := ioutil.WriteFile(filepath.Join(td, "tmp"), []byte("was here first!"), 0644); err != nil {
+	if err := os.WriteFile(filepath.Join(td, "tmp"), []byte("was here first!"), 0644); err != nil {
 		t.Fatal(err)
 	}
 
@@ -102,3 +102,50 @@ func TestStartTransactionFailure(t *testing.T) {
 		t.Fatal(err)
 	}
 }
+
+func TestGetOrphan(t *testing.T) {
+	fms, td, cleanup := newFileMetadataStore(t)
+	defer cleanup()
+
+	layerRoot := filepath.Join(td, "sha256")
+	if err := os.MkdirAll(layerRoot, 0755); err != nil {
+		t.Fatal(err)
+	}
+
+	tx, err := fms.StartTransaction()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	layerid := randomLayerID(5)
+	err = tx.Commit(layerid)
+	if err != nil {
+		t.Fatal(err)
+	}
+	layerPath := fms.getLayerDirectory(layerid)
+	if err := os.WriteFile(filepath.Join(layerPath, "cache-id"), []byte(stringid.GenerateRandomID()), 0644); err != nil {
+		t.Fatal(err)
+	}
+
+	orphanLayers, err := fms.getOrphan()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(orphanLayers) != 0 {
+		t.Fatalf("Expected to have zero orphan layers")
+	}
+
+	layeridSplit := strings.Split(layerid.String(), ":")
+	newPath := filepath.Join(layerRoot, fmt.Sprintf("%s-%s-removing", layeridSplit[1], stringid.GenerateRandomID()))
+	err = os.Rename(layerPath, newPath)
+	if err != nil {
+		t.Fatal(err)
+	}
+	orphanLayers, err = fms.getOrphan()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(orphanLayers) != 1 {
+		t.Fatalf("Expected to have one orphan layer")
+	}
+}
diff --git a/layer/filestore_unix.go b/layer/filestore_unix.go
index 68e7f90779a09..88a2a85595c30 100644
--- a/layer/filestore_unix.go
+++ b/layer/filestore_unix.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package layer // import "github.com/docker/docker/layer"
diff --git a/layer/filestore_windows.go b/layer/filestore_windows.go
index cecad426c8ba9..325d68b63cfb4 100644
--- a/layer/filestore_windows.go
+++ b/layer/filestore_windows.go
@@ -2,7 +2,6 @@ package layer // import "github.com/docker/docker/layer"
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"strings"
 )
@@ -17,7 +16,7 @@ func (fm *fileMetadataTransaction) setOS(os string) error {
 
 // getOS reads the "os" file from the layer filestore
 func (fms *fileMetadataStore) getOS(layer ChainID) (string, error) {
-	contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "os"))
+	contentBytes, err := os.ReadFile(fms.getLayerFilename(layer, "os"))
 	if err != nil {
 		// For backwards compatibility, the os file may not exist. Default to "windows" if missing.
 		if os.IsNotExist(err) {
diff --git a/layer/layer.go b/layer/layer.go
index d0c7fa8608372..e7e3619ddd31c 100644
--- a/layer/layer.go
+++ b/layer/layer.go
@@ -16,7 +16,7 @@ import (
 	"github.com/docker/distribution"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/containerfs"
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
 	"github.com/sirupsen/logrus"
 )
 
@@ -126,7 +126,7 @@ type RWLayer interface {
 	Parent() Layer
 
 	// Mount mounts the RWLayer and returns the filesystem path
-	// the to the writable layer.
+	// to the writable layer.
 	Mount(mountLabel string) (containerfs.ContainerFS, error)
 
 	// Unmount unmounts the RWLayer. This should be called
@@ -145,6 +145,9 @@ type RWLayer interface {
 
 	// Metadata returns the low level metadata for the mutable layer
 	Metadata() (map[string]string, error)
+
+	// ApplyDiff applies the diff to the RW layer
+	ApplyDiff(diff io.Reader) (int64, error)
 }
 
 // Metadata holds information about a
diff --git a/layer/layer_store.go b/layer/layer_store.go
index bc3e8719fc5bc..5520899b8d231 100644
--- a/layer/layer_store.go
+++ b/layer/layer_store.go
@@ -4,7 +4,8 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
+	"os"
+	"path/filepath"
 	"sync"
 
 	"github.com/docker/distribution"
@@ -13,7 +14,8 @@ import (
 	"github.com/docker/docker/pkg/plugingetter"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/system"
-	"github.com/opencontainers/go-digest"
+	"github.com/moby/locker"
+	digest "github.com/opencontainers/go-digest"
 	"github.com/sirupsen/logrus"
 	"github.com/vbatts/tar-split/tar/asm"
 	"github.com/vbatts/tar-split/tar/storage"
@@ -36,7 +38,11 @@ type layerStore struct {
 
 	mounts map[string]*mountedLayer
 	mountL sync.Mutex
-	os     string
+
+	// protect *RWLayer() methods from operating on the same name/id
+	locker *locker.Locker
+
+	os string
 }
 
 // StoreOptions are the options used to create a new Store instance
@@ -92,6 +98,7 @@ func newStoreFromGraphDriver(root string, driver graphdriver.Driver, os string)
 		driver:      driver,
 		layerMap:    map[ChainID]*roLayer{},
 		mounts:      map[string]*mountedLayer{},
+		locker:      locker.New(),
 		useTarSplit: !caps.ReproducesExactDiffs,
 		os:          os,
 	}
@@ -189,6 +196,8 @@ func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) {
 }
 
 func (ls *layerStore) loadMount(mount string) error {
+	ls.mountL.Lock()
+	defer ls.mountL.Unlock()
 	if _, ok := ls.mounts[mount]; ok {
 		return nil
 	}
@@ -253,13 +262,14 @@ func (ls *layerStore) applyTar(tx *fileMetadataTransaction, ts io.Reader, parent
 	}
 
 	applySize, err := ls.driver.ApplyDiff(layer.cacheID, parent, rdr)
+	// discard trailing data but ensure metadata is picked up to reconstruct stream
+	// unconditionally call io.Copy here before checking err to ensure the resources
+	// allocated by NewInputTarStream above are always released
+	io.Copy(io.Discard, rdr) // ignore error as reader may be closed
 	if err != nil {
 		return err
 	}
 
-	// Discard trailing data but ensure metadata is picked up to reconstruct stream
-	io.Copy(ioutil.Discard, rdr) // ignore error as reader may be closed
-
 	layer.size = applySize
 	layer.diffID = DiffID(digester.Digest())
 
@@ -406,11 +416,24 @@ func (ls *layerStore) Map() map[ChainID]Layer {
 }
 
 func (ls *layerStore) deleteLayer(layer *roLayer, metadata *Metadata) error {
+	// Rename layer digest folder first so we detect orphan layer(s)
+	// if ls.driver.Remove fails
+	var dir string
+	for {
+		dgst := digest.Digest(layer.chainID)
+		tmpID := fmt.Sprintf("%s-%s-removing", dgst.Hex(), stringid.GenerateRandomID())
+		dir = filepath.Join(ls.store.root, string(dgst.Algorithm()), tmpID)
+		err := os.Rename(ls.store.getLayerDirectory(layer.chainID), dir)
+		if os.IsExist(err) {
+			continue
+		}
+		break
+	}
 	err := ls.driver.Remove(layer.cacheID)
 	if err != nil {
 		return err
 	}
-	err = ls.store.Remove(layer.chainID)
+	err = os.RemoveAll(dir)
 	if err != nil {
 		return err
 	}
@@ -443,12 +466,14 @@ func (ls *layerStore) releaseLayer(l *roLayer) ([]Metadata, error) {
 		if l.hasReferences() {
 			panic("cannot delete referenced layer")
 		}
+		// Remove layer from layer map first so it is not considered to exist
+		// when if ls.deleteLayer fails.
+		delete(ls.layerMap, l.chainID)
+
 		var metadata Metadata
 		if err := ls.deleteLayer(l, &metadata); err != nil {
 			return nil, err
 		}
-
-		delete(ls.layerMap, l.chainID)
 		removed = append(removed, metadata)
 
 		if l.parent == nil {
@@ -476,7 +501,7 @@ func (ls *layerStore) Release(l Layer) ([]Metadata, error) {
 	return ls.releaseLayer(layer)
 }
 
-func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWLayerOpts) (RWLayer, error) {
+func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWLayerOpts) (_ RWLayer, err error) {
 	var (
 		storageOpt map[string]string
 		initFunc   MountInit
@@ -489,14 +514,16 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWL
 		initFunc = opts.InitFunc
 	}
 
+	ls.locker.Lock(name)
+	defer ls.locker.Unlock(name)
+
 	ls.mountL.Lock()
-	defer ls.mountL.Unlock()
-	m, ok := ls.mounts[name]
+	_, ok := ls.mounts[name]
+	ls.mountL.Unlock()
 	if ok {
 		return nil, ErrMountNameConflict
 	}
 
-	var err error
 	var pid string
 	var p *roLayer
 	if string(parent) != "" {
@@ -516,7 +543,7 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWL
 		}()
 	}
 
-	m = &mountedLayer{
+	m := &mountedLayer{
 		name:       name,
 		parent:     p,
 		mountID:    ls.mountID(name),
@@ -527,7 +554,7 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWL
 	if initFunc != nil {
 		pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc, storageOpt)
 		if err != nil {
-			return nil, err
+			return
 		}
 		m.initID = pid
 	}
@@ -537,20 +564,23 @@ func (ls *layerStore) CreateRWLayer(name string, parent ChainID, opts *CreateRWL
 	}
 
 	if err = ls.driver.CreateReadWrite(m.mountID, pid, createOpts); err != nil {
-		return nil, err
+		return
 	}
 	if err = ls.saveMount(m); err != nil {
-		return nil, err
+		return
 	}
 
 	return m.getReference(), nil
 }
 
 func (ls *layerStore) GetRWLayer(id string) (RWLayer, error) {
+	ls.locker.Lock(id)
+	defer ls.locker.Unlock(id)
+
 	ls.mountL.Lock()
-	defer ls.mountL.Unlock()
-	mount, ok := ls.mounts[id]
-	if !ok {
+	mount := ls.mounts[id]
+	ls.mountL.Unlock()
+	if mount == nil {
 		return nil, ErrMountDoesNotExist
 	}
 
@@ -559,9 +589,10 @@ func (ls *layerStore) GetRWLayer(id string) (RWLayer, error) {
 
 func (ls *layerStore) GetMountID(id string) (string, error) {
 	ls.mountL.Lock()
-	defer ls.mountL.Unlock()
-	mount, ok := ls.mounts[id]
-	if !ok {
+	mount := ls.mounts[id]
+	ls.mountL.Unlock()
+
+	if mount == nil {
 		return "", ErrMountDoesNotExist
 	}
 	logrus.Debugf("GetMountID id: %s -> mountID: %s", id, mount.mountID)
@@ -570,10 +601,14 @@ func (ls *layerStore) GetMountID(id string) (string, error) {
 }
 
 func (ls *layerStore) ReleaseRWLayer(l RWLayer) ([]Metadata, error) {
+	name := l.Name()
+	ls.locker.Lock(name)
+	defer ls.locker.Unlock(name)
+
 	ls.mountL.Lock()
-	defer ls.mountL.Unlock()
-	m, ok := ls.mounts[l.Name()]
-	if !ok {
+	m := ls.mounts[name]
+	ls.mountL.Unlock()
+	if m == nil {
 		return []Metadata{}, nil
 	}
 
@@ -605,7 +640,9 @@ func (ls *layerStore) ReleaseRWLayer(l RWLayer) ([]Metadata, error) {
 		return nil, err
 	}
 
-	delete(ls.mounts, m.Name())
+	ls.mountL.Lock()
+	delete(ls.mounts, name)
+	ls.mountL.Unlock()
 
 	ls.layerL.Lock()
 	defer ls.layerL.Unlock()
@@ -633,7 +670,9 @@ func (ls *layerStore) saveMount(mount *mountedLayer) error {
 		}
 	}
 
+	ls.mountL.Lock()
 	ls.mounts[mount.name] = mount
+	ls.mountL.Unlock()
 
 	return nil
 }
@@ -720,6 +759,23 @@ func (ls *layerStore) assembleTarTo(graphID string, metadata io.ReadCloser, size
 }
 
 func (ls *layerStore) Cleanup() error {
+	orphanLayers, err := ls.store.getOrphan()
+	if err != nil {
+		logrus.Errorf("Cannot get orphan layers: %v", err)
+	}
+	logrus.Debugf("found %v orphan layers", len(orphanLayers))
+	for _, orphan := range orphanLayers {
+		logrus.Debugf("removing orphan layer, chain ID: %v , cache ID: %v", orphan.chainID, orphan.cacheID)
+		err = ls.driver.Remove(orphan.cacheID)
+		if err != nil && !os.IsNotExist(err) {
+			logrus.WithError(err).WithField("cache-id", orphan.cacheID).Error("cannot remove orphan layer")
+			continue
+		}
+		err = ls.store.Remove(orphan.chainID, orphan.cacheID)
+		if err != nil {
+			logrus.WithError(err).WithField("chain-id", orphan.chainID).Error("cannot remove orphan layer metadata")
+		}
+	}
 	return ls.driver.Cleanup()
 }
 
diff --git a/layer/layer_test.go b/layer/layer_test.go
index 5c4e8fab190ad..4a4bcfb90e9ce 100644
--- a/layer/layer_test.go
+++ b/layer/layer_test.go
@@ -3,7 +3,6 @@ package layer // import "github.com/docker/docker/layer"
 import (
 	"bytes"
 	"io"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime"
@@ -17,7 +16,7 @@ import (
 	"github.com/docker/docker/pkg/containerfs"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/stringid"
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
 )
 
 func init() {
@@ -47,7 +46,7 @@ func newVFSGraphDriver(td string) (graphdriver.Driver, error) {
 }
 
 func newTestGraphDriver(t *testing.T) (graphdriver.Driver, func()) {
-	td, err := ioutil.TempDir("", "graph-")
+	td, err := os.MkdirTemp("", "graph-")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -63,7 +62,7 @@ func newTestGraphDriver(t *testing.T) (graphdriver.Driver, func()) {
 }
 
 func newTestStore(t *testing.T) (Store, string, func()) {
-	td, err := ioutil.TempDir("", "layerstore-")
+	td, err := os.MkdirTemp("", "layerstore-")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -171,10 +170,6 @@ func getCachedLayer(l Layer) *roLayer {
 	return l.(*roLayer)
 }
 
-func getMountLayer(l RWLayer) *mountedLayer {
-	return l.(*referencedRWLayer).mountedLayer
-}
-
 func createMetadata(layers ...Layer) []Metadata {
 	metadata := make([]Metadata, len(layers))
 	for i := range layers {
@@ -559,7 +554,7 @@ func assertLayerDiff(t *testing.T, expected []byte, layer Layer) {
 	}
 	defer ts.Close()
 
-	actual, err := ioutil.ReadAll(ts)
+	actual, err := io.ReadAll(ts)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -607,7 +602,7 @@ func byteDiff(b1, b2 []byte) ([]byte, []byte) {
 }
 
 func tarFromFiles(files ...FileApplier) ([]byte, error) {
-	td, err := ioutil.TempDir("", "tar-")
+	td, err := os.MkdirTemp("", "tar-")
 	if err != nil {
 		return nil, err
 	}
@@ -758,7 +753,7 @@ func TestTarStreamVerification(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	_, err = io.Copy(ioutil.Discard, ts)
+	_, err = io.Copy(io.Discard, ts)
 	if err == nil {
 		t.Fatal("expected data verification to fail")
 	}
diff --git a/layer/layer_unix.go b/layer/layer_unix.go
index 002c7ff838b91..24cb88009220c 100644
--- a/layer/layer_unix.go
+++ b/layer/layer_unix.go
@@ -1,3 +1,4 @@
+//go:build linux || freebsd || darwin || openbsd
 // +build linux freebsd darwin openbsd
 
 package layer // import "github.com/docker/docker/layer"
diff --git a/layer/layer_unix_test.go b/layer/layer_unix_test.go
index 68301581311ce..c3e4bdaf6bf0d 100644
--- a/layer/layer_unix_test.go
+++ b/layer/layer_unix_test.go
@@ -1,3 +1,4 @@
+//go:build !windows
 // +build !windows
 
 package layer // import "github.com/docker/docker/layer"
diff --git a/layer/layer_windows.go b/layer/layer_windows.go
index 25ef26afc1f7c..3d079a9af6222 100644
--- a/layer/layer_windows.go
+++ b/layer/layer_windows.go
@@ -7,7 +7,7 @@ import (
 // Getter is an interface to get the path to a layer on the host.
 type Getter interface {
 	// GetLayerPath gets the path for the layer. This is different from Get()
-	// since that returns an interface to account for umountable layers.
+	// since that returns an interface to account for unmountable layers.
 	GetLayerPath(id string) (string, error)
 }
 
diff --git a/layer/migration.go b/layer/migration.go
index 2668ea96bb863..80f0ff7ff4677 100644
--- a/layer/migration.go
+++ b/layer/migration.go
@@ -3,78 +3,18 @@ package layer // import "github.com/docker/docker/layer"
 import (
 	"compress/gzip"
 	"errors"
-	"fmt"
 	"io"
 	"os"
 
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
 	"github.com/sirupsen/logrus"
 	"github.com/vbatts/tar-split/tar/asm"
 	"github.com/vbatts/tar-split/tar/storage"
 )
 
-// CreateRWLayerByGraphID creates a RWLayer in the layer store using
-// the provided name with the given graphID. To get the RWLayer
-// after migration the layer may be retrieved by the given name.
-func (ls *layerStore) CreateRWLayerByGraphID(name, graphID string, parent ChainID) (err error) {
-	ls.mountL.Lock()
-	defer ls.mountL.Unlock()
-	m, ok := ls.mounts[name]
-	if ok {
-		if m.parent.chainID != parent {
-			return errors.New("name conflict, mismatched parent")
-		}
-		if m.mountID != graphID {
-			return errors.New("mount already exists")
-		}
-
-		return nil
-	}
-
-	if !ls.driver.Exists(graphID) {
-		return fmt.Errorf("graph ID does not exist: %q", graphID)
-	}
-
-	var p *roLayer
-	if string(parent) != "" {
-		p = ls.get(parent)
-		if p == nil {
-			return ErrLayerDoesNotExist
-		}
-
-		// Release parent chain if error
-		defer func() {
-			if err != nil {
-				ls.layerL.Lock()
-				ls.releaseLayer(p)
-				ls.layerL.Unlock()
-			}
-		}()
-	}
-
-	// TODO: Ensure graphID has correct parent
-
-	m = &mountedLayer{
-		name:       name,
-		parent:     p,
-		mountID:    graphID,
-		layerStore: ls,
-		references: map[RWLayer]*referencedRWLayer{},
-	}
-
-	// Check for existing init layer
-	initID := fmt.Sprintf("%s-init", graphID)
-	if ls.driver.Exists(initID) {
-		m.initID = initID
-	}
-
-	return ls.saveMount(m)
-}
-
 func (ls *layerStore) ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataPath string) (diffID DiffID, size int64, err error) {
 	defer func() {
 		if err != nil {
-			logrus.Debugf("could not get checksum for %q with tar-split: %q", id, err)
 			diffID, size, err = ls.checksumForGraphIDNoTarsplit(id, parent, newTarDataPath)
 		}
 	}()
diff --git a/layer/migration_test.go b/layer/migration_test.go
index 923166371cf78..374d8cdcdc2ed 100644
--- a/layer/migration_test.go
+++ b/layer/migration_test.go
@@ -3,16 +3,13 @@ package layer // import "github.com/docker/docker/layer"
 import (
 	"bytes"
 	"compress/gzip"
-	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime"
 	"testing"
 
 	"github.com/docker/docker/daemon/graphdriver"
-	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/vbatts/tar-split/tar/asm"
 	"github.com/vbatts/tar-split/tar/storage"
@@ -35,7 +32,7 @@ func writeTarSplitFile(name string, tarContent []byte) error {
 		return err
 	}
 
-	if _, err := io.Copy(ioutil.Discard, rdr); err != nil {
+	if _, err := io.Copy(io.Discard, rdr); err != nil {
 		return err
 	}
 
@@ -47,7 +44,7 @@ func TestLayerMigration(t *testing.T) {
 	if runtime.GOOS == "windows" {
 		t.Skip("Failing on Windows")
 	}
-	td, err := ioutil.TempDir("", "migration-test-")
+	td, err := os.MkdirTemp("", "migration-test-")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -175,7 +172,7 @@ func tarFromFilesInGraph(graph graphdriver.Driver, graphID, parentID string, fil
 	}
 	defer ar.Close()
 
-	return ioutil.ReadAll(ar)
+	return io.ReadAll(ar)
 }
 
 func TestLayerMigrationNoTarsplit(t *testing.T) {
@@ -183,7 +180,7 @@ func TestLayerMigrationNoTarsplit(t *testing.T) {
 	if runtime.GOOS == "windows" {
 		t.Skip("Failing on Windows")
 	}
-	td, err := ioutil.TempDir("", "migration-test-")
+	td, err := os.MkdirTemp("", "migration-test-")
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -269,161 +266,3 @@ func TestLayerMigrationNoTarsplit(t *testing.T) {
 
 	assertMetadata(t, metadata, createMetadata(layer2a))
 }
-
-func TestMountMigration(t *testing.T) {
-	// TODO Windows: Figure out why this is failing (obvious - paths... needs porting)
-	if runtime.GOOS == "windows" {
-		t.Skip("Failing on Windows")
-	}
-	ls, _, cleanup := newTestStore(t)
-	defer cleanup()
-
-	baseFiles := []FileApplier{
-		newTestFile("/root/.bashrc", []byte("# Boring configuration"), 0644),
-		newTestFile("/etc/profile", []byte("# Base configuration"), 0644),
-	}
-	initFiles := []FileApplier{
-		newTestFile("/etc/hosts", []byte{}, 0644),
-		newTestFile("/etc/resolv.conf", []byte{}, 0644),
-	}
-	mountFiles := []FileApplier{
-		newTestFile("/etc/hosts", []byte("localhost 127.0.0.1"), 0644),
-		newTestFile("/root/.bashrc", []byte("# Updated configuration"), 0644),
-		newTestFile("/root/testfile1.txt", []byte("nothing valuable"), 0644),
-	}
-
-	initTar, err := tarFromFiles(initFiles...)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	mountTar, err := tarFromFiles(mountFiles...)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	graph := ls.(*layerStore).driver
-
-	layer1, err := createLayer(ls, "", initWithFiles(baseFiles...))
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	graphID1 := layer1.(*referencedCacheLayer).cacheID
-
-	containerID := stringid.GenerateRandomID()
-	containerInit := fmt.Sprintf("%s-init", containerID)
-
-	if err := graph.Create(containerInit, graphID1, nil); err != nil {
-		t.Fatal(err)
-	}
-	if _, err := graph.ApplyDiff(containerInit, graphID1, bytes.NewReader(initTar)); err != nil {
-		t.Fatal(err)
-	}
-
-	if err := graph.Create(containerID, containerInit, nil); err != nil {
-		t.Fatal(err)
-	}
-	if _, err := graph.ApplyDiff(containerID, containerInit, bytes.NewReader(mountTar)); err != nil {
-		t.Fatal(err)
-	}
-
-	if err := ls.(*layerStore).CreateRWLayerByGraphID("migration-mount", containerID, layer1.ChainID()); err != nil {
-		t.Fatal(err)
-	}
-
-	rwLayer1, err := ls.GetRWLayer("migration-mount")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if _, err := rwLayer1.Mount(""); err != nil {
-		t.Fatal(err)
-	}
-
-	changes, err := rwLayer1.Changes()
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if expected := 5; len(changes) != expected {
-		t.Logf("Changes %#v", changes)
-		t.Fatalf("Wrong number of changes %d, expected %d", len(changes), expected)
-	}
-
-	sortChanges(changes)
-
-	assertChange(t, changes[0], archive.Change{
-		Path: "/etc",
-		Kind: archive.ChangeModify,
-	})
-	assertChange(t, changes[1], archive.Change{
-		Path: "/etc/hosts",
-		Kind: archive.ChangeModify,
-	})
-	assertChange(t, changes[2], archive.Change{
-		Path: "/root",
-		Kind: archive.ChangeModify,
-	})
-	assertChange(t, changes[3], archive.Change{
-		Path: "/root/.bashrc",
-		Kind: archive.ChangeModify,
-	})
-	assertChange(t, changes[4], archive.Change{
-		Path: "/root/testfile1.txt",
-		Kind: archive.ChangeAdd,
-	})
-
-	if _, err := ls.CreateRWLayer("migration-mount", layer1.ChainID(), nil); err == nil {
-		t.Fatal("Expected error creating mount with same name")
-	} else if err != ErrMountNameConflict {
-		t.Fatal(err)
-	}
-
-	rwLayer2, err := ls.GetRWLayer("migration-mount")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if getMountLayer(rwLayer1) != getMountLayer(rwLayer2) {
-		t.Fatal("Expected same layer from get with same name as from migrate")
-	}
-
-	if _, err := rwLayer2.Mount(""); err != nil {
-		t.Fatal(err)
-	}
-
-	if _, err := rwLayer2.Mount(""); err != nil {
-		t.Fatal(err)
-	}
-
-	if metadata, err := ls.Release(layer1); err != nil {
-		t.Fatal(err)
-	} else if len(metadata) > 0 {
-		t.Fatalf("Expected no layers to be deleted, deleted %#v", metadata)
-	}
-
-	if err := rwLayer1.Unmount(); err != nil {
-		t.Fatal(err)
-	}
-
-	if _, err := ls.ReleaseRWLayer(rwLayer1); err != nil {
-		t.Fatal(err)
-	}
-
-	if err := rwLayer2.Unmount(); err != nil {
-		t.Fatal(err)
-	}
-	if err := rwLayer2.Unmount(); err != nil {
-		t.Fatal(err)
-	}
-	metadata, err := ls.ReleaseRWLayer(rwLayer2)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(metadata) == 0 {
-		t.Fatal("Expected base layer to be deleted when deleting mount")
-	}
-
-	assertMetadata(t, metadata, createMetadata(layer1))
-}
diff --git a/layer/mount_test.go b/layer/mount_test.go
index 1cfc370eed11f..20de6e0c7c0a9 100644
--- a/layer/mount_test.go
+++ b/layer/mount_test.go
@@ -1,7 +1,7 @@
 package layer // import "github.com/docker/docker/layer"
 
 import (
-	"io/ioutil"
+	"io"
 	"runtime"
 	"sort"
 	"testing"
@@ -56,7 +56,7 @@ func TestMountInit(t *testing.T) {
 	}
 	defer f.Close()
 
-	b, err := ioutil.ReadAll(f)
+	b, err := io.ReadAll(f)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -206,6 +206,64 @@ func TestMountChanges(t *testing.T) {
 	})
 }
 
+func TestMountApply(t *testing.T) {
+	// TODO Windows: Figure out why this is failing
+	if runtime.GOOS == "windows" {
+		t.Skip("Failing on Windows")
+	}
+	ls, _, cleanup := newTestStore(t)
+	defer cleanup()
+
+	basefile := newTestFile("testfile.txt", []byte("base data!"), 0644)
+	newfile := newTestFile("newfile.txt", []byte("new data!"), 0755)
+
+	li := initWithFiles(basefile)
+	layer, err := createLayer(ls, "", li)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	di := initWithFiles(newfile)
+	diffLayer, err := createLayer(ls, "", di)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	r, err := diffLayer.TarStream()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err := m.ApplyDiff(r); err != nil {
+		t.Fatal(err)
+	}
+
+	pathFS, err := m.Mount("")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	f, err := pathFS.Open(pathFS.Join(pathFS.Path(), "newfile.txt"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer f.Close()
+
+	b, err := io.ReadAll(f)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if expected := "new data!"; string(b) != expected {
+		t.Fatalf("Unexpected test file contents %q, expected %q", string(b), expected)
+	}
+}
+
 func assertChange(t *testing.T, actual, expected archive.Change) {
 	if actual.Path != expected.Path {
 		t.Fatalf("Unexpected change path %s, expected %s", actual.Path, expected.Path)
diff --git a/layer/mounted_layer.go b/layer/mounted_layer.go
index d6858c662ccfa..f614fd571df2a 100644
--- a/layer/mounted_layer.go
+++ b/layer/mounted_layer.go
@@ -2,6 +2,7 @@ package layer // import "github.com/docker/docker/layer"
 
 import (
 	"io"
+	"sync"
 
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/containerfs"
@@ -12,9 +13,9 @@ type mountedLayer struct {
 	mountID    string
 	initID     string
 	parent     *roLayer
-	path       string
 	layerStore *layerStore
 
+	sync.Mutex
 	references map[RWLayer]*referencedRWLayer
 }
 
@@ -62,16 +63,24 @@ func (ml *mountedLayer) getReference() RWLayer {
 	ref := &referencedRWLayer{
 		mountedLayer: ml,
 	}
+	ml.Lock()
 	ml.references[ref] = ref
+	ml.Unlock()
 
 	return ref
 }
 
 func (ml *mountedLayer) hasReferences() bool {
-	return len(ml.references) > 0
+	ml.Lock()
+	ret := len(ml.references) > 0
+	ml.Unlock()
+
+	return ret
 }
 
 func (ml *mountedLayer) deleteReference(ref RWLayer) error {
+	ml.Lock()
+	defer ml.Unlock()
 	if _, ok := ml.references[ref]; !ok {
 		return ErrLayerNotRetained
 	}
@@ -81,7 +90,9 @@ func (ml *mountedLayer) deleteReference(ref RWLayer) error {
 
 func (ml *mountedLayer) retakeReference(r RWLayer) {
 	if ref, ok := r.(*referencedRWLayer); ok {
+		ml.Lock()
 		ml.references[ref] = ref
+		ml.Unlock()
 	}
 }
 
@@ -98,3 +109,8 @@ func (rl *referencedRWLayer) Mount(mountLabel string) (containerfs.ContainerFS,
 func (rl *referencedRWLayer) Unmount() error {
 	return rl.layerStore.driver.Put(rl.mountedLayer.mountID)
 }
+
+// ApplyDiff applies specified diff to the layer
+func (rl *referencedRWLayer) ApplyDiff(diff io.Reader) (int64, error) {
+	return rl.layerStore.driver.ApplyDiff(rl.mountID, rl.cacheParent(), diff)
+}
diff --git a/layer/ro_layer.go b/layer/ro_layer.go
index 3555e8b027994..15841d5bd2447 100644
--- a/layer/ro_layer.go
+++ b/layer/ro_layer.go
@@ -5,7 +5,7 @@ import (
 	"io"
 
 	"github.com/docker/distribution"
-	"github.com/opencontainers/go-digest"
+	digest "github.com/opencontainers/go-digest"
 )
 
 type roLayer struct {
diff --git a/libcontainerd/client_daemon.go b/libcontainerd/client_daemon.go
deleted file mode 100644
index cb9cb43a7316f..0000000000000
--- a/libcontainerd/client_daemon.go
+++ /dev/null
@@ -1,913 +0,0 @@
-// +build !windows
-
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"io"
-	"os"
-	"path/filepath"
-	"reflect"
-	"runtime"
-	"strings"
-	"sync"
-	"syscall"
-	"time"
-
-	"github.com/containerd/containerd"
-	apievents "github.com/containerd/containerd/api/events"
-	"github.com/containerd/containerd/api/types"
-	"github.com/containerd/containerd/archive"
-	"github.com/containerd/containerd/cio"
-	"github.com/containerd/containerd/content"
-	containerderrors "github.com/containerd/containerd/errdefs"
-	"github.com/containerd/containerd/events"
-	"github.com/containerd/containerd/images"
-	"github.com/containerd/containerd/runtime/linux/runctypes"
-	"github.com/containerd/typeurl"
-	"github.com/docker/docker/errdefs"
-	"github.com/docker/docker/pkg/ioutils"
-	"github.com/opencontainers/image-spec/specs-go/v1"
-	specs "github.com/opencontainers/runtime-spec/specs-go"
-	"github.com/pkg/errors"
-	"github.com/sirupsen/logrus"
-	"google.golang.org/grpc/codes"
-	"google.golang.org/grpc/status"
-)
-
-// InitProcessName is the name given to the first process of a
-// container
-const InitProcessName = "init"
-
-type container struct {
-	mu sync.Mutex
-
-	bundleDir string
-	ctr       containerd.Container
-	task      containerd.Task
-	execs     map[string]containerd.Process
-	oomKilled bool
-}
-
-func (c *container) setTask(t containerd.Task) {
-	c.mu.Lock()
-	c.task = t
-	c.mu.Unlock()
-}
-
-func (c *container) getTask() containerd.Task {
-	c.mu.Lock()
-	t := c.task
-	c.mu.Unlock()
-	return t
-}
-
-func (c *container) addProcess(id string, p containerd.Process) {
-	c.mu.Lock()
-	if c.execs == nil {
-		c.execs = make(map[string]containerd.Process)
-	}
-	c.execs[id] = p
-	c.mu.Unlock()
-}
-
-func (c *container) deleteProcess(id string) {
-	c.mu.Lock()
-	delete(c.execs, id)
-	c.mu.Unlock()
-}
-
-func (c *container) getProcess(id string) containerd.Process {
-	c.mu.Lock()
-	p := c.execs[id]
-	c.mu.Unlock()
-	return p
-}
-
-func (c *container) setOOMKilled(killed bool) {
-	c.mu.Lock()
-	c.oomKilled = killed
-	c.mu.Unlock()
-}
-
-func (c *container) getOOMKilled() bool {
-	c.mu.Lock()
-	killed := c.oomKilled
-	c.mu.Unlock()
-	return killed
-}
-
-type client struct {
-	sync.RWMutex // protects containers map
-
-	client   *containerd.Client
-	stateDir string
-	logger   *logrus.Entry
-	ns       string
-
-	backend    Backend
-	eventQ     queue
-	containers map[string]*container
-}
-
-// NewClient creates a new libcontainerd client from a containerd client
-func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b Backend) (Client, error) {
-	c := &client{
-		client:     cli,
-		stateDir:   stateDir,
-		logger:     logrus.WithField("module", "libcontainerd").WithField("namespace", ns),
-		ns:         ns,
-		backend:    b,
-		containers: make(map[string]*container),
-	}
-
-	go c.processEventStream(ctx, ns)
-
-	return c, nil
-}
-
-func (c *client) Version(ctx context.Context) (containerd.Version, error) {
-	return c.client.Version(ctx)
-}
-
-// Restore loads the containerd container.
-// It should not be called concurrently with any other operation for the given ID.
-func (c *client) Restore(ctx context.Context, id string, attachStdio StdioCallback) (alive bool, pid int, err error) {
-	c.Lock()
-	_, ok := c.containers[id]
-	if ok {
-		c.Unlock()
-		return false, 0, errors.WithStack(newConflictError("id already in use"))
-	}
-
-	cntr := &container{}
-	c.containers[id] = cntr
-	cntr.mu.Lock()
-	defer cntr.mu.Unlock()
-
-	c.Unlock()
-
-	defer func() {
-		if err != nil {
-			c.Lock()
-			delete(c.containers, id)
-			c.Unlock()
-		}
-	}()
-
-	var dio *cio.DirectIO
-	defer func() {
-		if err != nil && dio != nil {
-			dio.Cancel()
-			dio.Close()
-		}
-		err = wrapError(err)
-	}()
-
-	ctr, err := c.client.LoadContainer(ctx, id)
-	if err != nil {
-		return false, -1, errors.WithStack(wrapError(err))
-	}
-
-	attachIO := func(fifos *cio.FIFOSet) (cio.IO, error) {
-		// dio must be assigned to the previously defined dio for the defer above
-		// to handle cleanup
-		dio, err = cio.NewDirectIO(ctx, fifos)
-		if err != nil {
-			return nil, err
-		}
-		return attachStdio(dio)
-	}
-	t, err := ctr.Task(ctx, attachIO)
-	if err != nil && !containerderrors.IsNotFound(err) {
-		return false, -1, errors.Wrap(wrapError(err), "error getting containerd task for container")
-	}
-
-	if t != nil {
-		s, err := t.Status(ctx)
-		if err != nil {
-			return false, -1, errors.Wrap(wrapError(err), "error getting task status")
-		}
-
-		alive = s.Status != containerd.Stopped
-		pid = int(t.Pid())
-	}
-
-	cntr.bundleDir = filepath.Join(c.stateDir, id)
-	cntr.ctr = ctr
-	cntr.task = t
-	// TODO(mlaventure): load execs
-
-	c.logger.WithFields(logrus.Fields{
-		"container": id,
-		"alive":     alive,
-		"pid":       pid,
-	}).Debug("restored container")
-
-	return alive, pid, nil
-}
-
-func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, runtimeOptions interface{}) error {
-	if ctr := c.getContainer(id); ctr != nil {
-		return errors.WithStack(newConflictError("id already in use"))
-	}
-
-	bdir, err := prepareBundleDir(filepath.Join(c.stateDir, id), ociSpec)
-	if err != nil {
-		return errdefs.System(errors.Wrap(err, "prepare bundle dir failed"))
-	}
-
-	c.logger.WithField("bundle", bdir).WithField("root", ociSpec.Root.Path).Debug("bundle dir created")
-
-	cdCtr, err := c.client.NewContainer(ctx, id,
-		containerd.WithSpec(ociSpec),
-		// TODO(mlaventure): when containerd support lcow, revisit runtime value
-		containerd.WithRuntime(fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS), runtimeOptions))
-	if err != nil {
-		return wrapError(err)
-	}
-
-	c.Lock()
-	c.containers[id] = &container{
-		bundleDir: bdir,
-		ctr:       cdCtr,
-	}
-	c.Unlock()
-
-	return nil
-}
-
-// Start create and start a task for the specified containerd id
-func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin bool, attachStdio StdioCallback) (int, error) {
-	ctr := c.getContainer(id)
-	if ctr == nil {
-		return -1, errors.WithStack(newNotFoundError("no such container"))
-	}
-	if t := ctr.getTask(); t != nil {
-		return -1, errors.WithStack(newConflictError("container already started"))
-	}
-
-	var (
-		cp             *types.Descriptor
-		t              containerd.Task
-		rio            cio.IO
-		err            error
-		stdinCloseSync = make(chan struct{})
-	)
-
-	if checkpointDir != "" {
-		// write checkpoint to the content store
-		tar := archive.Diff(ctx, "", checkpointDir)
-		cp, err = c.writeContent(ctx, images.MediaTypeContainerd1Checkpoint, checkpointDir, tar)
-		// remove the checkpoint when we're done
-		defer func() {
-			if cp != nil {
-				err := c.client.ContentStore().Delete(context.Background(), cp.Digest)
-				if err != nil {
-					c.logger.WithError(err).WithFields(logrus.Fields{
-						"ref":    checkpointDir,
-						"digest": cp.Digest,
-					}).Warnf("failed to delete temporary checkpoint entry")
-				}
-			}
-		}()
-		if err := tar.Close(); err != nil {
-			return -1, errors.Wrap(err, "failed to close checkpoint tar stream")
-		}
-		if err != nil {
-			return -1, errors.Wrapf(err, "failed to upload checkpoint to containerd")
-		}
-	}
-
-	spec, err := ctr.ctr.Spec(ctx)
-	if err != nil {
-		return -1, errors.Wrap(err, "failed to retrieve spec")
-	}
-	uid, gid := getSpecUser(spec)
-	t, err = ctr.ctr.NewTask(ctx,
-		func(id string) (cio.IO, error) {
-			fifos := newFIFOSet(ctr.bundleDir, InitProcessName, withStdin, spec.Process.Terminal)
-
-			rio, err = c.createIO(fifos, id, InitProcessName, stdinCloseSync, attachStdio)
-			return rio, err
-		},
-		func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error {
-			info.Checkpoint = cp
-			info.Options = &runctypes.CreateOptions{
-				IoUid:       uint32(uid),
-				IoGid:       uint32(gid),
-				NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
-			}
-			return nil
-		})
-	if err != nil {
-		close(stdinCloseSync)
-		if rio != nil {
-			rio.Cancel()
-			rio.Close()
-		}
-		return -1, wrapError(err)
-	}
-
-	ctr.setTask(t)
-
-	// Signal c.createIO that it can call CloseIO
-	close(stdinCloseSync)
-
-	if err := t.Start(ctx); err != nil {
-		if _, err := t.Delete(ctx); err != nil {
-			c.logger.WithError(err).WithField("container", id).
-				Error("failed to delete task after fail start")
-		}
-		ctr.setTask(nil)
-		return -1, wrapError(err)
-	}
-
-	return int(t.Pid()), nil
-}
-
-// Exec creates exec process.
-//
-// The containerd client calls Exec to register the exec config in the shim side.
-// When the client calls Start, the shim will create stdin fifo if needs. But
-// for the container main process, the stdin fifo will be created in Create not
-// the Start call. stdinCloseSync channel should be closed after Start exec
-// process.
-func (c *client) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error) {
-	ctr := c.getContainer(containerID)
-	if ctr == nil {
-		return -1, errors.WithStack(newNotFoundError("no such container"))
-	}
-	t := ctr.getTask()
-	if t == nil {
-		return -1, errors.WithStack(newInvalidParameterError("container is not running"))
-	}
-
-	if p := ctr.getProcess(processID); p != nil {
-		return -1, errors.WithStack(newConflictError("id already in use"))
-	}
-
-	var (
-		p              containerd.Process
-		rio            cio.IO
-		err            error
-		stdinCloseSync = make(chan struct{})
-	)
-
-	fifos := newFIFOSet(ctr.bundleDir, processID, withStdin, spec.Terminal)
-
-	defer func() {
-		if err != nil {
-			if rio != nil {
-				rio.Cancel()
-				rio.Close()
-			}
-		}
-	}()
-
-	p, err = t.Exec(ctx, processID, spec, func(id string) (cio.IO, error) {
-		rio, err = c.createIO(fifos, containerID, processID, stdinCloseSync, attachStdio)
-		return rio, err
-	})
-	if err != nil {
-		close(stdinCloseSync)
-		return -1, wrapError(err)
-	}
-
-	ctr.addProcess(processID, p)
-
-	// Signal c.createIO that it can call CloseIO
-	//
-	// the stdin of exec process will be created after p.Start in containerd
-	defer close(stdinCloseSync)
-
-	if err = p.Start(ctx); err != nil {
-		p.Delete(context.Background())
-		ctr.deleteProcess(processID)
-		return -1, wrapError(err)
-	}
-
-	return int(p.Pid()), nil
-}
-
-func (c *client) SignalProcess(ctx context.Context, containerID, processID string, signal int) error {
-	p, err := c.getProcess(containerID, processID)
-	if err != nil {
-		return err
-	}
-	return wrapError(p.Kill(ctx, syscall.Signal(signal)))
-}
-
-func (c *client) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error {
-	p, err := c.getProcess(containerID, processID)
-	if err != nil {
-		return err
-	}
-
-	return p.Resize(ctx, uint32(width), uint32(height))
-}
-
-func (c *client) CloseStdin(ctx context.Context, containerID, processID string) error {
-	p, err := c.getProcess(containerID, processID)
-	if err != nil {
-		return err
-	}
-
-	return p.CloseIO(ctx, containerd.WithStdinCloser)
-}
-
-func (c *client) Pause(ctx context.Context, containerID string) error {
-	p, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return err
-	}
-
-	return wrapError(p.(containerd.Task).Pause(ctx))
-}
-
-func (c *client) Resume(ctx context.Context, containerID string) error {
-	p, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return err
-	}
-
-	return p.(containerd.Task).Resume(ctx)
-}
-
-func (c *client) Stats(ctx context.Context, containerID string) (*Stats, error) {
-	p, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return nil, err
-	}
-
-	m, err := p.(containerd.Task).Metrics(ctx)
-	if err != nil {
-		return nil, err
-	}
-
-	v, err := typeurl.UnmarshalAny(m.Data)
-	if err != nil {
-		return nil, err
-	}
-	return interfaceToStats(m.Timestamp, v), nil
-}
-
-func (c *client) ListPids(ctx context.Context, containerID string) ([]uint32, error) {
-	p, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return nil, err
-	}
-
-	pis, err := p.(containerd.Task).Pids(ctx)
-	if err != nil {
-		return nil, err
-	}
-
-	var pids []uint32
-	for _, i := range pis {
-		pids = append(pids, i.Pid)
-	}
-
-	return pids, nil
-}
-
-func (c *client) Summary(ctx context.Context, containerID string) ([]Summary, error) {
-	p, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return nil, err
-	}
-
-	pis, err := p.(containerd.Task).Pids(ctx)
-	if err != nil {
-		return nil, err
-	}
-
-	var infos []Summary
-	for _, pi := range pis {
-		i, err := typeurl.UnmarshalAny(pi.Info)
-		if err != nil {
-			return nil, errors.Wrap(err, "unable to decode process details")
-		}
-		s, err := summaryFromInterface(i)
-		if err != nil {
-			return nil, err
-		}
-		infos = append(infos, *s)
-	}
-
-	return infos, nil
-}
-
-func (c *client) DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) {
-	p, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return 255, time.Now(), nil
-	}
-
-	status, err := p.(containerd.Task).Delete(ctx)
-	if err != nil {
-		return 255, time.Now(), nil
-	}
-
-	if ctr := c.getContainer(containerID); ctr != nil {
-		ctr.setTask(nil)
-	}
-	return status.ExitCode(), status.ExitTime(), nil
-}
-
-func (c *client) Delete(ctx context.Context, containerID string) error {
-	ctr := c.getContainer(containerID)
-	if ctr == nil {
-		return errors.WithStack(newNotFoundError("no such container"))
-	}
-
-	if err := ctr.ctr.Delete(ctx); err != nil {
-		return wrapError(err)
-	}
-
-	if os.Getenv("LIBCONTAINERD_NOCLEAN") != "1" {
-		if err := os.RemoveAll(ctr.bundleDir); err != nil {
-			c.logger.WithError(err).WithFields(logrus.Fields{
-				"container": containerID,
-				"bundle":    ctr.bundleDir,
-			}).Error("failed to remove state dir")
-		}
-	}
-
-	c.removeContainer(containerID)
-
-	return nil
-}
-
-func (c *client) Status(ctx context.Context, containerID string) (Status, error) {
-	ctr := c.getContainer(containerID)
-	if ctr == nil {
-		return StatusUnknown, errors.WithStack(newNotFoundError("no such container"))
-	}
-
-	t := ctr.getTask()
-	if t == nil {
-		return StatusUnknown, errors.WithStack(newNotFoundError("no such task"))
-	}
-
-	s, err := t.Status(ctx)
-	if err != nil {
-		return StatusUnknown, wrapError(err)
-	}
-
-	return Status(s.Status), nil
-}
-
-func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error {
-	p, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return err
-	}
-
-	opts := []containerd.CheckpointTaskOpts{}
-	if exit {
-		opts = append(opts, func(r *containerd.CheckpointTaskInfo) error {
-			if r.Options == nil {
-				r.Options = &runctypes.CheckpointOptions{
-					Exit: true,
-				}
-			} else {
-				opts, _ := r.Options.(*runctypes.CheckpointOptions)
-				opts.Exit = true
-			}
-			return nil
-		})
-	}
-	img, err := p.(containerd.Task).Checkpoint(ctx, opts...)
-	if err != nil {
-		return wrapError(err)
-	}
-	// Whatever happens, delete the checkpoint from containerd
-	defer func() {
-		err := c.client.ImageService().Delete(context.Background(), img.Name())
-		if err != nil {
-			c.logger.WithError(err).WithField("digest", img.Target().Digest).
-				Warnf("failed to delete checkpoint image")
-		}
-	}()
-
-	b, err := content.ReadBlob(ctx, c.client.ContentStore(), img.Target())
-	if err != nil {
-		return errdefs.System(errors.Wrapf(err, "failed to retrieve checkpoint data"))
-	}
-	var index v1.Index
-	if err := json.Unmarshal(b, &index); err != nil {
-		return errdefs.System(errors.Wrapf(err, "failed to decode checkpoint data"))
-	}
-
-	var cpDesc *v1.Descriptor
-	for _, m := range index.Manifests {
-		if m.MediaType == images.MediaTypeContainerd1Checkpoint {
-			cpDesc = &m
-			break
-		}
-	}
-	if cpDesc == nil {
-		return errdefs.System(errors.Wrapf(err, "invalid checkpoint"))
-	}
-
-	rat, err := c.client.ContentStore().ReaderAt(ctx, *cpDesc)
-	if err != nil {
-		return errdefs.System(errors.Wrapf(err, "failed to get checkpoint reader"))
-	}
-	defer rat.Close()
-	_, err = archive.Apply(ctx, checkpointDir, content.NewReader(rat))
-	if err != nil {
-		return errdefs.System(errors.Wrapf(err, "failed to read checkpoint reader"))
-	}
-
-	return err
-}
-
-func (c *client) getContainer(id string) *container {
-	c.RLock()
-	ctr := c.containers[id]
-	c.RUnlock()
-
-	return ctr
-}
-
-func (c *client) removeContainer(id string) {
-	c.Lock()
-	delete(c.containers, id)
-	c.Unlock()
-}
-
-func (c *client) getProcess(containerID, processID string) (containerd.Process, error) {
-	ctr := c.getContainer(containerID)
-	if ctr == nil {
-		return nil, errors.WithStack(newNotFoundError("no such container"))
-	}
-
-	t := ctr.getTask()
-	if t == nil {
-		return nil, errors.WithStack(newNotFoundError("container is not running"))
-	}
-	if processID == InitProcessName {
-		return t, nil
-	}
-
-	p := ctr.getProcess(processID)
-	if p == nil {
-		return nil, errors.WithStack(newNotFoundError("no such exec"))
-	}
-	return p, nil
-}
-
-// createIO creates the io to be used by a process
-// This needs to get a pointer to interface as upon closure the process may not have yet been registered
-func (c *client) createIO(fifos *cio.FIFOSet, containerID, processID string, stdinCloseSync chan struct{}, attachStdio StdioCallback) (cio.IO, error) {
-	var (
-		io  *cio.DirectIO
-		err error
-	)
-
-	io, err = cio.NewDirectIO(context.Background(), fifos)
-	if err != nil {
-		return nil, err
-	}
-
-	if io.Stdin != nil {
-		var (
-			err       error
-			stdinOnce sync.Once
-		)
-		pipe := io.Stdin
-		io.Stdin = ioutils.NewWriteCloserWrapper(pipe, func() error {
-			stdinOnce.Do(func() {
-				err = pipe.Close()
-				// Do the rest in a new routine to avoid a deadlock if the
-				// Exec/Start call failed.
-				go func() {
-					<-stdinCloseSync
-					p, err := c.getProcess(containerID, processID)
-					if err == nil {
-						err = p.CloseIO(context.Background(), containerd.WithStdinCloser)
-						if err != nil && strings.Contains(err.Error(), "transport is closing") {
-							err = nil
-						}
-					}
-				}()
-			})
-			return err
-		})
-	}
-
-	rio, err := attachStdio(io)
-	if err != nil {
-		io.Cancel()
-		io.Close()
-	}
-	return rio, err
-}
-
-func (c *client) processEvent(ctr *container, et EventType, ei EventInfo) {
-	c.eventQ.append(ei.ContainerID, func() {
-		err := c.backend.ProcessEvent(ei.ContainerID, et, ei)
-		if err != nil {
-			c.logger.WithError(err).WithFields(logrus.Fields{
-				"container":  ei.ContainerID,
-				"event":      et,
-				"event-info": ei,
-			}).Error("failed to process event")
-		}
-
-		if et == EventExit && ei.ProcessID != ei.ContainerID {
-			p := ctr.getProcess(ei.ProcessID)
-			if p == nil {
-				c.logger.WithError(errors.New("no such process")).
-					WithFields(logrus.Fields{
-						"container": ei.ContainerID,
-						"process":   ei.ProcessID,
-					}).Error("exit event")
-				return
-			}
-			_, err = p.Delete(context.Background())
-			if err != nil {
-				c.logger.WithError(err).WithFields(logrus.Fields{
-					"container": ei.ContainerID,
-					"process":   ei.ProcessID,
-				}).Warn("failed to delete process")
-			}
-			ctr.deleteProcess(ei.ProcessID)
-
-			ctr := c.getContainer(ei.ContainerID)
-			if ctr == nil {
-				c.logger.WithFields(logrus.Fields{
-					"container": ei.ContainerID,
-				}).Error("failed to find container")
-			} else {
-				newFIFOSet(ctr.bundleDir, ei.ProcessID, true, false).Close()
-			}
-		}
-	})
-}
-
-func (c *client) processEventStream(ctx context.Context, ns string) {
-	var (
-		err error
-		ev  *events.Envelope
-		et  EventType
-		ei  EventInfo
-		ctr *container
-	)
-
-	// Filter on both namespace *and* topic. To create an "and" filter,
-	// this must be a single, comma-separated string
-	eventStream, errC := c.client.EventService().Subscribe(ctx, "namespace=="+ns+",topic~=|^/tasks/|")
-
-	c.logger.Debug("processing event stream")
-
-	var oomKilled bool
-	for {
-		select {
-		case err = <-errC:
-			if err != nil {
-				errStatus, ok := status.FromError(err)
-				if !ok || errStatus.Code() != codes.Canceled {
-					c.logger.WithError(err).Error("failed to get event")
-					go c.processEventStream(ctx, ns)
-				} else {
-					c.logger.WithError(ctx.Err()).Info("stopping event stream following graceful shutdown")
-				}
-			}
-			return
-		case ev = <-eventStream:
-			if ev.Event == nil {
-				c.logger.WithField("event", ev).Warn("invalid event")
-				continue
-			}
-
-			v, err := typeurl.UnmarshalAny(ev.Event)
-			if err != nil {
-				c.logger.WithError(err).WithField("event", ev).Warn("failed to unmarshal event")
-				continue
-			}
-
-			c.logger.WithField("topic", ev.Topic).Debug("event")
-
-			switch t := v.(type) {
-			case *apievents.TaskCreate:
-				et = EventCreate
-				ei = EventInfo{
-					ContainerID: t.ContainerID,
-					ProcessID:   t.ContainerID,
-					Pid:         t.Pid,
-				}
-			case *apievents.TaskStart:
-				et = EventStart
-				ei = EventInfo{
-					ContainerID: t.ContainerID,
-					ProcessID:   t.ContainerID,
-					Pid:         t.Pid,
-				}
-			case *apievents.TaskExit:
-				et = EventExit
-				ei = EventInfo{
-					ContainerID: t.ContainerID,
-					ProcessID:   t.ID,
-					Pid:         t.Pid,
-					ExitCode:    t.ExitStatus,
-					ExitedAt:    t.ExitedAt,
-				}
-			case *apievents.TaskOOM:
-				et = EventOOM
-				ei = EventInfo{
-					ContainerID: t.ContainerID,
-					OOMKilled:   true,
-				}
-				oomKilled = true
-			case *apievents.TaskExecAdded:
-				et = EventExecAdded
-				ei = EventInfo{
-					ContainerID: t.ContainerID,
-					ProcessID:   t.ExecID,
-				}
-			case *apievents.TaskExecStarted:
-				et = EventExecStarted
-				ei = EventInfo{
-					ContainerID: t.ContainerID,
-					ProcessID:   t.ExecID,
-					Pid:         t.Pid,
-				}
-			case *apievents.TaskPaused:
-				et = EventPaused
-				ei = EventInfo{
-					ContainerID: t.ContainerID,
-				}
-			case *apievents.TaskResumed:
-				et = EventResumed
-				ei = EventInfo{
-					ContainerID: t.ContainerID,
-				}
-			default:
-				c.logger.WithFields(logrus.Fields{
-					"topic": ev.Topic,
-					"type":  reflect.TypeOf(t)},
-				).Info("ignoring event")
-				continue
-			}
-
-			ctr = c.getContainer(ei.ContainerID)
-			if ctr == nil {
-				c.logger.WithField("container", ei.ContainerID).Warn("unknown container")
-				continue
-			}
-
-			if oomKilled {
-				ctr.setOOMKilled(true)
-				oomKilled = false
-			}
-			ei.OOMKilled = ctr.getOOMKilled()
-
-			c.processEvent(ctr, et, ei)
-		}
-	}
-}
-
-func (c *client) writeContent(ctx context.Context, mediaType, ref string, r io.Reader) (*types.Descriptor, error) {
-	writer, err := c.client.ContentStore().Writer(ctx, content.WithRef(ref))
-	if err != nil {
-		return nil, err
-	}
-	defer writer.Close()
-	size, err := io.Copy(writer, r)
-	if err != nil {
-		return nil, err
-	}
-	labels := map[string]string{
-		"containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339),
-	}
-	if err := writer.Commit(ctx, 0, "", content.WithLabels(labels)); err != nil {
-		return nil, err
-	}
-	return &types.Descriptor{
-		MediaType: mediaType,
-		Digest:    writer.Digest(),
-		Size_:     size,
-	}, nil
-}
-
-func wrapError(err error) error {
-	switch {
-	case err == nil:
-		return nil
-	case containerderrors.IsNotFound(err):
-		return errdefs.NotFound(err)
-	}
-
-	msg := err.Error()
-	for _, s := range []string{"container does not exist", "not found", "no such container"} {
-		if strings.Contains(msg, s) {
-			return errdefs.NotFound(err)
-		}
-	}
-	return err
-}
diff --git a/libcontainerd/client_daemon_linux.go b/libcontainerd/client_daemon_linux.go
deleted file mode 100644
index bebe5f7ae8812..0000000000000
--- a/libcontainerd/client_daemon_linux.go
+++ /dev/null
@@ -1,108 +0,0 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import (
-	"context"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"github.com/containerd/containerd"
-	"github.com/containerd/containerd/cio"
-	"github.com/docker/docker/pkg/idtools"
-	"github.com/opencontainers/runtime-spec/specs-go"
-	"github.com/sirupsen/logrus"
-)
-
-func summaryFromInterface(i interface{}) (*Summary, error) {
-	return &Summary{}, nil
-}
-
-func (c *client) UpdateResources(ctx context.Context, containerID string, resources *Resources) error {
-	p, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return err
-	}
-
-	// go doesn't like the alias in 1.8, this means this need to be
-	// platform specific
-	return p.(containerd.Task).Update(ctx, containerd.WithResources((*specs.LinuxResources)(resources)))
-}
-
-func hostIDFromMap(id uint32, mp []specs.LinuxIDMapping) int {
-	for _, m := range mp {
-		if id >= m.ContainerID && id <= m.ContainerID+m.Size-1 {
-			return int(m.HostID + id - m.ContainerID)
-		}
-	}
-	return 0
-}
-
-func getSpecUser(ociSpec *specs.Spec) (int, int) {
-	var (
-		uid int
-		gid int
-	)
-
-	for _, ns := range ociSpec.Linux.Namespaces {
-		if ns.Type == specs.UserNamespace {
-			uid = hostIDFromMap(0, ociSpec.Linux.UIDMappings)
-			gid = hostIDFromMap(0, ociSpec.Linux.GIDMappings)
-			break
-		}
-	}
-
-	return uid, gid
-}
-
-func prepareBundleDir(bundleDir string, ociSpec *specs.Spec) (string, error) {
-	uid, gid := getSpecUser(ociSpec)
-	if uid == 0 && gid == 0 {
-		return bundleDir, idtools.MkdirAllAndChownNew(bundleDir, 0755, idtools.Identity{UID: 0, GID: 0})
-	}
-
-	p := string(filepath.Separator)
-	components := strings.Split(bundleDir, string(filepath.Separator))
-	for _, d := range components[1:] {
-		p = filepath.Join(p, d)
-		fi, err := os.Stat(p)
-		if err != nil && !os.IsNotExist(err) {
-			return "", err
-		}
-		if os.IsNotExist(err) || fi.Mode()&1 == 0 {
-			p = fmt.Sprintf("%s.%d.%d", p, uid, gid)
-			if err := idtools.MkdirAndChown(p, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil && !os.IsExist(err) {
-				return "", err
-			}
-		}
-	}
-
-	return p, nil
-}
-
-func newFIFOSet(bundleDir, processID string, withStdin, withTerminal bool) *cio.FIFOSet {
-	config := cio.Config{
-		Terminal: withTerminal,
-		Stdout:   filepath.Join(bundleDir, processID+"-stdout"),
-	}
-	paths := []string{config.Stdout}
-
-	if withStdin {
-		config.Stdin = filepath.Join(bundleDir, processID+"-stdin")
-		paths = append(paths, config.Stdin)
-	}
-	if !withTerminal {
-		config.Stderr = filepath.Join(bundleDir, processID+"-stderr")
-		paths = append(paths, config.Stderr)
-	}
-	closer := func() error {
-		for _, path := range paths {
-			if err := os.RemoveAll(path); err != nil {
-				logrus.Warnf("libcontainerd: failed to remove fifo %v: %v", path, err)
-			}
-		}
-		return nil
-	}
-
-	return cio.NewFIFOSet(config, closer)
-}
diff --git a/libcontainerd/client_daemon_windows.go b/libcontainerd/client_daemon_windows.go
deleted file mode 100644
index 4aba33e18cf4a..0000000000000
--- a/libcontainerd/client_daemon_windows.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import (
-	"fmt"
-	"path/filepath"
-
-	"github.com/containerd/containerd/cio"
-	"github.com/containerd/containerd/windows/hcsshimtypes"
-	specs "github.com/opencontainers/runtime-spec/specs-go"
-	"github.com/pkg/errors"
-)
-
-func summaryFromInterface(i interface{}) (*Summary, error) {
-	switch pd := i.(type) {
-	case *hcsshimtypes.ProcessDetails:
-		return &Summary{
-			CreateTimestamp:              pd.CreatedAt,
-			ImageName:                    pd.ImageName,
-			KernelTime100ns:              pd.KernelTime_100Ns,
-			MemoryCommitBytes:            pd.MemoryCommitBytes,
-			MemoryWorkingSetPrivateBytes: pd.MemoryWorkingSetPrivateBytes,
-			MemoryWorkingSetSharedBytes:  pd.MemoryWorkingSetSharedBytes,
-			ProcessId:                    pd.ProcessID,
-			UserTime100ns:                pd.UserTime_100Ns,
-		}, nil
-	default:
-		return nil, errors.Errorf("Unknown process details type %T", pd)
-	}
-}
-
-func prepareBundleDir(bundleDir string, ociSpec *specs.Spec) (string, error) {
-	return bundleDir, nil
-}
-
-func pipeName(containerID, processID, name string) string {
-	return fmt.Sprintf(`\\.\pipe\containerd-%s-%s-%s`, containerID, processID, name)
-}
-
-func newFIFOSet(bundleDir, processID string, withStdin, withTerminal bool) *cio.FIFOSet {
-	containerID := filepath.Base(bundleDir)
-	config := cio.Config{
-		Terminal: withTerminal,
-		Stdout:   pipeName(containerID, processID, "stdout"),
-	}
-
-	if withStdin {
-		config.Stdin = pipeName(containerID, processID, "stdin")
-	}
-
-	if !config.Terminal {
-		config.Stderr = pipeName(containerID, processID, "stderr")
-	}
-
-	return cio.NewFIFOSet(config, nil)
-}
diff --git a/libcontainerd/client_local_windows.go b/libcontainerd/client_local_windows.go
deleted file mode 100644
index 80b7f4c879cc9..0000000000000
--- a/libcontainerd/client_local_windows.go
+++ /dev/null
@@ -1,1354 +0,0 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path"
-	"path/filepath"
-	"regexp"
-	"strings"
-	"sync"
-	"syscall"
-	"time"
-
-	"github.com/Microsoft/hcsshim"
-	opengcs "github.com/Microsoft/opengcs/client"
-	"github.com/containerd/containerd"
-	"github.com/containerd/containerd/cio"
-	"github.com/docker/docker/pkg/sysinfo"
-	"github.com/docker/docker/pkg/system"
-	specs "github.com/opencontainers/runtime-spec/specs-go"
-	"github.com/pkg/errors"
-	"github.com/sirupsen/logrus"
-	"golang.org/x/sys/windows"
-)
-
-const InitProcessName = "init"
-
-type process struct {
-	id         string
-	pid        int
-	hcsProcess hcsshim.Process
-}
-
-type container struct {
-	sync.Mutex
-
-	// The ociSpec is required, as client.Create() needs a spec, but can
-	// be called from the RestartManager context which does not otherwise
-	// have access to the Spec
-	ociSpec *specs.Spec
-
-	isWindows    bool
-	hcsContainer hcsshim.Container
-
-	id               string
-	status           Status
-	exitedAt         time.Time
-	exitCode         uint32
-	waitCh           chan struct{}
-	init             *process
-	execs            map[string]*process
-	terminateInvoked bool
-}
-
-// Win32 error codes that are used for various workarounds
-// These really should be ALL_CAPS to match golangs syscall library and standard
-// Win32 error conventions, but golint insists on CamelCase.
-const (
-	CoEClassstring     = syscall.Errno(0x800401F3) // Invalid class string
-	ErrorNoNetwork     = syscall.Errno(1222)       // The network is not present or not started
-	ErrorBadPathname   = syscall.Errno(161)        // The specified path is invalid
-	ErrorInvalidObject = syscall.Errno(0x800710D8) // The object identifier does not represent a valid object
-)
-
-// defaultOwner is a tag passed to HCS to allow it to differentiate between
-// container creator management stacks. We hard code "docker" in the case
-// of docker.
-const defaultOwner = "docker"
-
-type client struct {
-	sync.Mutex
-
-	stateDir   string
-	backend    Backend
-	logger     *logrus.Entry
-	eventQ     queue
-	containers map[string]*container
-}
-
-// NewClient creates a new local executor for windows
-func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b Backend) (Client, error) {
-	c := &client{
-		stateDir:   stateDir,
-		backend:    b,
-		logger:     logrus.WithField("module", "libcontainerd").WithField("module", "libcontainerd").WithField("namespace", ns),
-		containers: make(map[string]*container),
-	}
-
-	return c, nil
-}
-
-func (c *client) Version(ctx context.Context) (containerd.Version, error) {
-	return containerd.Version{}, errors.New("not implemented on Windows")
-}
-
-// Create is the entrypoint to create a container from a spec.
-// Table below shows the fields required for HCS JSON calling parameters,
-// where if not populated, is omitted.
-// +-----------------+--------------------------------------------+---------------------------------------------------+
-// |                 | Isolation=Process                          | Isolation=Hyper-V                                 |
-// +-----------------+--------------------------------------------+---------------------------------------------------+
-// | VolumePath      | \\?\\Volume{GUIDa}                         |                                                   |
-// | LayerFolderPath | %root%\windowsfilter\containerID           |                                                   |
-// | Layers[]        | ID=GUIDb;Path=%root%\windowsfilter\layerID | ID=GUIDb;Path=%root%\windowsfilter\layerID        |
-// | HvRuntime       |                                            | ImagePath=%root%\BaseLayerID\UtilityVM            |
-// +-----------------+--------------------------------------------+---------------------------------------------------+
-//
-// Isolation=Process example:
-//
-// {
-//	"SystemType": "Container",
-//	"Name": "5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
-//	"Owner": "docker",
-//	"VolumePath": "\\\\\\\\?\\\\Volume{66d1ef4c-7a00-11e6-8948-00155ddbef9d}",
-//	"IgnoreFlushesDuringBoot": true,
-//	"LayerFolderPath": "C:\\\\control\\\\windowsfilter\\\\5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
-//	"Layers": [{
-//		"ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
-//		"Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
-//	}],
-//	"HostName": "5e0055c814a6",
-//	"MappedDirectories": [],
-//	"HvPartition": false,
-//	"EndpointList": ["eef2649d-bb17-4d53-9937-295a8efe6f2c"],
-//}
-//
-// Isolation=Hyper-V example:
-//
-//{
-//	"SystemType": "Container",
-//	"Name": "475c2c58933b72687a88a441e7e0ca4bd72d76413c5f9d5031fee83b98f6045d",
-//	"Owner": "docker",
-//	"IgnoreFlushesDuringBoot": true,
-//	"Layers": [{
-//		"ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
-//		"Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
-//	}],
-//	"HostName": "475c2c58933b",
-//	"MappedDirectories": [],
-//	"HvPartition": true,
-//	"EndpointList": ["e1bb1e61-d56f-405e-b75d-fd520cefa0cb"],
-//	"DNSSearchList": "a.com,b.com,c.com",
-//	"HvRuntime": {
-//		"ImagePath": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c\\\\UtilityVM"
-//	},
-//}
-func (c *client) Create(_ context.Context, id string, spec *specs.Spec, runtimeOptions interface{}) error {
-	if ctr := c.getContainer(id); ctr != nil {
-		return errors.WithStack(newConflictError("id already in use"))
-	}
-
-	// spec.Linux must be nil for Windows containers, but spec.Windows
-	// will be filled in regardless of container platform.  This is a
-	// temporary workaround due to LCOW requiring layer folder paths,
-	// which are stored under spec.Windows.
-	//
-	// TODO: @darrenstahlmsft fix this once the OCI spec is updated to
-	// support layer folder paths for LCOW
-	if spec.Linux == nil {
-		return c.createWindows(id, spec, runtimeOptions)
-	}
-	return c.createLinux(id, spec, runtimeOptions)
-}
-
-func (c *client) createWindows(id string, spec *specs.Spec, runtimeOptions interface{}) error {
-	logger := c.logger.WithField("container", id)
-	configuration := &hcsshim.ContainerConfig{
-		SystemType:              "Container",
-		Name:                    id,
-		Owner:                   defaultOwner,
-		IgnoreFlushesDuringBoot: spec.Windows.IgnoreFlushesDuringBoot,
-		HostName:                spec.Hostname,
-		HvPartition:             false,
-	}
-
-	if spec.Windows.Resources != nil {
-		if spec.Windows.Resources.CPU != nil {
-			if spec.Windows.Resources.CPU.Count != nil {
-				// This check is being done here rather than in adaptContainerSettings
-				// because we don't want to update the HostConfig in case this container
-				// is moved to a host with more CPUs than this one.
-				cpuCount := *spec.Windows.Resources.CPU.Count
-				hostCPUCount := uint64(sysinfo.NumCPU())
-				if cpuCount > hostCPUCount {
-					c.logger.Warnf("Changing requested CPUCount of %d to current number of processors, %d", cpuCount, hostCPUCount)
-					cpuCount = hostCPUCount
-				}
-				configuration.ProcessorCount = uint32(cpuCount)
-			}
-			if spec.Windows.Resources.CPU.Shares != nil {
-				configuration.ProcessorWeight = uint64(*spec.Windows.Resources.CPU.Shares)
-			}
-			if spec.Windows.Resources.CPU.Maximum != nil {
-				configuration.ProcessorMaximum = int64(*spec.Windows.Resources.CPU.Maximum)
-			}
-		}
-		if spec.Windows.Resources.Memory != nil {
-			if spec.Windows.Resources.Memory.Limit != nil {
-				configuration.MemoryMaximumInMB = int64(*spec.Windows.Resources.Memory.Limit) / 1024 / 1024
-			}
-		}
-		if spec.Windows.Resources.Storage != nil {
-			if spec.Windows.Resources.Storage.Bps != nil {
-				configuration.StorageBandwidthMaximum = *spec.Windows.Resources.Storage.Bps
-			}
-			if spec.Windows.Resources.Storage.Iops != nil {
-				configuration.StorageIOPSMaximum = *spec.Windows.Resources.Storage.Iops
-			}
-		}
-	}
-
-	if spec.Windows.HyperV != nil {
-		configuration.HvPartition = true
-	}
-
-	if spec.Windows.Network != nil {
-		configuration.EndpointList = spec.Windows.Network.EndpointList
-		configuration.AllowUnqualifiedDNSQuery = spec.Windows.Network.AllowUnqualifiedDNSQuery
-		if spec.Windows.Network.DNSSearchList != nil {
-			configuration.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",")
-		}
-		configuration.NetworkSharedContainerName = spec.Windows.Network.NetworkSharedContainerName
-	}
-
-	if cs, ok := spec.Windows.CredentialSpec.(string); ok {
-		configuration.Credentials = cs
-	}
-
-	// We must have least two layers in the spec, the bottom one being a
-	// base image, the top one being the RW layer.
-	if spec.Windows.LayerFolders == nil || len(spec.Windows.LayerFolders) < 2 {
-		return fmt.Errorf("OCI spec is invalid - at least two LayerFolders must be supplied to the runtime")
-	}
-
-	// Strip off the top-most layer as that's passed in separately to HCS
-	configuration.LayerFolderPath = spec.Windows.LayerFolders[len(spec.Windows.LayerFolders)-1]
-	layerFolders := spec.Windows.LayerFolders[:len(spec.Windows.LayerFolders)-1]
-
-	if configuration.HvPartition {
-		// We don't currently support setting the utility VM image explicitly.
-		// TODO @swernli/jhowardmsft circa RS5, this may be re-locatable.
-		if spec.Windows.HyperV.UtilityVMPath != "" {
-			return errors.New("runtime does not support an explicit utility VM path for Hyper-V containers")
-		}
-
-		// Find the upper-most utility VM image.
-		var uvmImagePath string
-		for _, path := range layerFolders {
-			fullPath := filepath.Join(path, "UtilityVM")
-			_, err := os.Stat(fullPath)
-			if err == nil {
-				uvmImagePath = fullPath
-				break
-			}
-			if !os.IsNotExist(err) {
-				return err
-			}
-		}
-		if uvmImagePath == "" {
-			return errors.New("utility VM image could not be found")
-		}
-		configuration.HvRuntime = &hcsshim.HvRuntime{ImagePath: uvmImagePath}
-
-		if spec.Root.Path != "" {
-			return errors.New("OCI spec is invalid - Root.Path must be omitted for a Hyper-V container")
-		}
-	} else {
-		const volumeGUIDRegex = `^\\\\\?\\(Volume)\{{0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}\}\\$`
-		if _, err := regexp.MatchString(volumeGUIDRegex, spec.Root.Path); err != nil {
-			return fmt.Errorf(`OCI spec is invalid - Root.Path '%s' must be a volume GUID path in the format '\\?\Volume{GUID}\'`, spec.Root.Path)
-		}
-		// HCS API requires the trailing backslash to be removed
-		configuration.VolumePath = spec.Root.Path[:len(spec.Root.Path)-1]
-	}
-
-	if spec.Root.Readonly {
-		return errors.New(`OCI spec is invalid - Root.Readonly must not be set on Windows`)
-	}
-
-	for _, layerPath := range layerFolders {
-		_, filename := filepath.Split(layerPath)
-		g, err := hcsshim.NameToGuid(filename)
-		if err != nil {
-			return err
-		}
-		configuration.Layers = append(configuration.Layers, hcsshim.Layer{
-			ID:   g.ToString(),
-			Path: layerPath,
-		})
-	}
-
-	// Add the mounts (volumes, bind mounts etc) to the structure
-	var mds []hcsshim.MappedDir
-	var mps []hcsshim.MappedPipe
-	for _, mount := range spec.Mounts {
-		const pipePrefix = `\\.\pipe\`
-		if mount.Type != "" {
-			return fmt.Errorf("OCI spec is invalid - Mount.Type '%s' must not be set", mount.Type)
-		}
-		if strings.HasPrefix(mount.Destination, pipePrefix) {
-			mp := hcsshim.MappedPipe{
-				HostPath:          mount.Source,
-				ContainerPipeName: mount.Destination[len(pipePrefix):],
-			}
-			mps = append(mps, mp)
-		} else {
-			md := hcsshim.MappedDir{
-				HostPath:      mount.Source,
-				ContainerPath: mount.Destination,
-				ReadOnly:      false,
-			}
-			for _, o := range mount.Options {
-				if strings.ToLower(o) == "ro" {
-					md.ReadOnly = true
-				}
-			}
-			mds = append(mds, md)
-		}
-	}
-	configuration.MappedDirectories = mds
-	if len(mps) > 0 && system.GetOSVersion().Build < 16299 { // RS3
-		return errors.New("named pipe mounts are not supported on this version of Windows")
-	}
-	configuration.MappedPipes = mps
-
-	hcsContainer, err := hcsshim.CreateContainer(id, configuration)
-	if err != nil {
-		return err
-	}
-
-	// Construct a container object for calling start on it.
-	ctr := &container{
-		id:           id,
-		execs:        make(map[string]*process),
-		isWindows:    true,
-		ociSpec:      spec,
-		hcsContainer: hcsContainer,
-		status:       StatusCreated,
-		waitCh:       make(chan struct{}),
-	}
-
-	logger.Debug("starting container")
-	if err = hcsContainer.Start(); err != nil {
-		c.logger.WithError(err).Error("failed to start container")
-		ctr.Lock()
-		if err := c.terminateContainer(ctr); err != nil {
-			c.logger.WithError(err).Error("failed to cleanup after a failed Start")
-		} else {
-			c.logger.Debug("cleaned up after failed Start by calling Terminate")
-		}
-		ctr.Unlock()
-		return err
-	}
-
-	c.Lock()
-	c.containers[id] = ctr
-	c.Unlock()
-
-	logger.Debug("createWindows() completed successfully")
-	return nil
-
-}
-
-func (c *client) createLinux(id string, spec *specs.Spec, runtimeOptions interface{}) error {
-	logrus.Debugf("libcontainerd: createLinux(): containerId %s ", id)
-	logger := c.logger.WithField("container", id)
-
-	if runtimeOptions == nil {
-		return fmt.Errorf("lcow option must be supplied to the runtime")
-	}
-	lcowConfig, ok := runtimeOptions.(*opengcs.Config)
-	if !ok {
-		return fmt.Errorf("lcow option must be supplied to the runtime")
-	}
-
-	configuration := &hcsshim.ContainerConfig{
-		HvPartition:                 true,
-		Name:                        id,
-		SystemType:                  "container",
-		ContainerType:               "linux",
-		Owner:                       defaultOwner,
-		TerminateOnLastHandleClosed: true,
-	}
-
-	if lcowConfig.ActualMode == opengcs.ModeActualVhdx {
-		configuration.HvRuntime = &hcsshim.HvRuntime{
-			ImagePath:          lcowConfig.Vhdx,
-			BootSource:         "Vhd",
-			WritableBootSource: false,
-		}
-	} else {
-		configuration.HvRuntime = &hcsshim.HvRuntime{
-			ImagePath:           lcowConfig.KirdPath,
-			LinuxKernelFile:     lcowConfig.KernelFile,
-			LinuxInitrdFile:     lcowConfig.InitrdFile,
-			LinuxBootParameters: lcowConfig.BootParameters,
-		}
-	}
-
-	if spec.Windows == nil {
-		return fmt.Errorf("spec.Windows must not be nil for LCOW containers")
-	}
-
-	// We must have least one layer in the spec
-	if spec.Windows.LayerFolders == nil || len(spec.Windows.LayerFolders) == 0 {
-		return fmt.Errorf("OCI spec is invalid - at least one LayerFolders must be supplied to the runtime")
-	}
-
-	// Strip off the top-most layer as that's passed in separately to HCS
-	configuration.LayerFolderPath = spec.Windows.LayerFolders[len(spec.Windows.LayerFolders)-1]
-	layerFolders := spec.Windows.LayerFolders[:len(spec.Windows.LayerFolders)-1]
-
-	for _, layerPath := range layerFolders {
-		_, filename := filepath.Split(layerPath)
-		g, err := hcsshim.NameToGuid(filename)
-		if err != nil {
-			return err
-		}
-		configuration.Layers = append(configuration.Layers, hcsshim.Layer{
-			ID:   g.ToString(),
-			Path: filepath.Join(layerPath, "layer.vhd"),
-		})
-	}
-
-	if spec.Windows.Network != nil {
-		configuration.EndpointList = spec.Windows.Network.EndpointList
-		configuration.AllowUnqualifiedDNSQuery = spec.Windows.Network.AllowUnqualifiedDNSQuery
-		if spec.Windows.Network.DNSSearchList != nil {
-			configuration.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",")
-		}
-		configuration.NetworkSharedContainerName = spec.Windows.Network.NetworkSharedContainerName
-	}
-
-	// Add the mounts (volumes, bind mounts etc) to the structure. We have to do
-	// some translation for both the mapped directories passed into HCS and in
-	// the spec.
-	//
-	// For HCS, we only pass in the mounts from the spec which are type "bind".
-	// Further, the "ContainerPath" field (which is a little mis-leadingly
-	// named when it applies to the utility VM rather than the container in the
-	// utility VM) is moved to under /tmp/gcs//binds, where this is passed
-	// by the caller through a 'uvmpath' option.
-	//
-	// We do similar translation for the mounts in the spec by stripping out
-	// the uvmpath option, and translating the Source path to the location in the
-	// utility VM calculated above.
-	//
-	// From inside the utility VM, you would see a 9p mount such as in the following
-	// where a host folder has been mapped to /target. The line with /tmp/gcs//binds
-	// specifically:
-	//
-	//	/ # mount
-	//	rootfs on / type rootfs (rw,size=463736k,nr_inodes=115934)
-	//	proc on /proc type proc (rw,relatime)
-	//	sysfs on /sys type sysfs (rw,relatime)
-	//	udev on /dev type devtmpfs (rw,relatime,size=498100k,nr_inodes=124525,mode=755)
-	//	tmpfs on /run type tmpfs (rw,relatime)
-	//	cgroup on /sys/fs/cgroup type cgroup (rw,relatime,cpuset,cpu,cpuacct,blkio,memory,devices,freezer,net_cls,perf_event,net_prio,hugetlb,pids,rdma)
-	//	mqueue on /dev/mqueue type mqueue (rw,relatime)
-	//	devpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)
-	//	/binds/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/target on /binds/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/target type 9p (rw,sync,dirsync,relatime,trans=fd,rfdno=6,wfdno=6)
-	//	/dev/pmem0 on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/layer0 type ext4 (ro,relatime,block_validity,delalloc,norecovery,barrier,dax,user_xattr,acl)
-	//	/dev/sda on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/scratch type ext4 (rw,relatime,block_validity,delalloc,barrier,user_xattr,acl)
-	//	overlay on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/rootfs type overlay (rw,relatime,lowerdir=/tmp/base/:/tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/layer0,upperdir=/tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/scratch/upper,workdir=/tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/scratch/work)
-	//
-	//  /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc # ls -l
-	//	total 16
-	//	drwx------    3 0        0               60 Sep  7 18:54 binds
-	//	-rw-r--r--    1 0        0             3345 Sep  7 18:54 config.json
-	//	drwxr-xr-x   10 0        0             4096 Sep  6 17:26 layer0
-	//	drwxr-xr-x    1 0        0             4096 Sep  7 18:54 rootfs
-	//	drwxr-xr-x    5 0        0             4096 Sep  7 18:54 scratch
-	//
-	//	/tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc # ls -l binds
-	//	total 0
-	//	drwxrwxrwt    2 0        0             4096 Sep  7 16:51 target
-
-	mds := []hcsshim.MappedDir{}
-	specMounts := []specs.Mount{}
-	for _, mount := range spec.Mounts {
-		specMount := mount
-		if mount.Type == "bind" {
-			// Strip out the uvmpath from the options
-			updatedOptions := []string{}
-			uvmPath := ""
-			readonly := false
-			for _, opt := range mount.Options {
-				dropOption := false
-				elements := strings.SplitN(opt, "=", 2)
-				switch elements[0] {
-				case "uvmpath":
-					uvmPath = elements[1]
-					dropOption = true
-				case "rw":
-				case "ro":
-					readonly = true
-				case "rbind":
-				default:
-					return fmt.Errorf("unsupported option %q", opt)
-				}
-				if !dropOption {
-					updatedOptions = append(updatedOptions, opt)
-				}
-			}
-			mount.Options = updatedOptions
-			if uvmPath == "" {
-				return fmt.Errorf("no uvmpath for bind mount %+v", mount)
-			}
-			md := hcsshim.MappedDir{
-				HostPath:          mount.Source,
-				ContainerPath:     path.Join(uvmPath, mount.Destination),
-				CreateInUtilityVM: true,
-				ReadOnly:          readonly,
-			}
-			// If we are 1803/RS4+ enable LinuxMetadata support by default
-			if system.GetOSVersion().Build >= 17134 {
-				md.LinuxMetadata = true
-			}
-			mds = append(mds, md)
-			specMount.Source = path.Join(uvmPath, mount.Destination)
-		}
-		specMounts = append(specMounts, specMount)
-	}
-	configuration.MappedDirectories = mds
-
-	hcsContainer, err := hcsshim.CreateContainer(id, configuration)
-	if err != nil {
-		return err
-	}
-
-	spec.Mounts = specMounts
-
-	// Construct a container object for calling start on it.
-	ctr := &container{
-		id:           id,
-		execs:        make(map[string]*process),
-		isWindows:    false,
-		ociSpec:      spec,
-		hcsContainer: hcsContainer,
-		status:       StatusCreated,
-		waitCh:       make(chan struct{}),
-	}
-
-	// Start the container.
-	logger.Debug("starting container")
-	if err = hcsContainer.Start(); err != nil {
-		c.logger.WithError(err).Error("failed to start container")
-		ctr.debugGCS()
-		ctr.Lock()
-		if err := c.terminateContainer(ctr); err != nil {
-			c.logger.WithError(err).Error("failed to cleanup after a failed Start")
-		} else {
-			c.logger.Debug("cleaned up after failed Start by calling Terminate")
-		}
-		ctr.Unlock()
-		return err
-	}
-	ctr.debugGCS()
-
-	c.Lock()
-	c.containers[id] = ctr
-	c.Unlock()
-
-	c.eventQ.append(id, func() {
-		ei := EventInfo{
-			ContainerID: id,
-		}
-		c.logger.WithFields(logrus.Fields{
-			"container": ctr.id,
-			"event":     EventCreate,
-		}).Info("sending event")
-		err := c.backend.ProcessEvent(id, EventCreate, ei)
-		if err != nil {
-			c.logger.WithError(err).WithFields(logrus.Fields{
-				"container": id,
-				"event":     EventCreate,
-			}).Error("failed to process event")
-		}
-	})
-
-	logger.Debug("createLinux() completed successfully")
-	return nil
-}
-
-func (c *client) Start(_ context.Context, id, _ string, withStdin bool, attachStdio StdioCallback) (int, error) {
-	ctr := c.getContainer(id)
-	switch {
-	case ctr == nil:
-		return -1, errors.WithStack(newNotFoundError("no such container"))
-	case ctr.init != nil:
-		return -1, errors.WithStack(newConflictError("container already started"))
-	}
-
-	logger := c.logger.WithField("container", id)
-
-	// Note we always tell HCS to create stdout as it's required
-	// regardless of '-i' or '-t' options, so that docker can always grab
-	// the output through logs. We also tell HCS to always create stdin,
-	// even if it's not used - it will be closed shortly. Stderr is only
-	// created if it we're not -t.
-	var (
-		emulateConsole   bool
-		createStdErrPipe bool
-	)
-	if ctr.ociSpec.Process != nil {
-		emulateConsole = ctr.ociSpec.Process.Terminal
-		createStdErrPipe = !ctr.ociSpec.Process.Terminal
-	}
-
-	createProcessParms := &hcsshim.ProcessConfig{
-		EmulateConsole:   emulateConsole,
-		WorkingDirectory: ctr.ociSpec.Process.Cwd,
-		CreateStdInPipe:  true,
-		CreateStdOutPipe: true,
-		CreateStdErrPipe: createStdErrPipe,
-	}
-
-	if ctr.ociSpec.Process != nil && ctr.ociSpec.Process.ConsoleSize != nil {
-		createProcessParms.ConsoleSize[0] = uint(ctr.ociSpec.Process.ConsoleSize.Height)
-		createProcessParms.ConsoleSize[1] = uint(ctr.ociSpec.Process.ConsoleSize.Width)
-	}
-
-	// Configure the environment for the process
-	createProcessParms.Environment = setupEnvironmentVariables(ctr.ociSpec.Process.Env)
-	if ctr.isWindows {
-		createProcessParms.CommandLine = strings.Join(ctr.ociSpec.Process.Args, " ")
-	} else {
-		createProcessParms.CommandArgs = ctr.ociSpec.Process.Args
-	}
-	createProcessParms.User = ctr.ociSpec.Process.User.Username
-
-	// LCOW requires the raw OCI spec passed through HCS and onwards to
-	// GCS for the utility VM.
-	if !ctr.isWindows {
-		ociBuf, err := json.Marshal(ctr.ociSpec)
-		if err != nil {
-			return -1, err
-		}
-		ociRaw := json.RawMessage(ociBuf)
-		createProcessParms.OCISpecification = &ociRaw
-	}
-
-	ctr.Lock()
-	defer ctr.Unlock()
-
-	// Start the command running in the container.
-	newProcess, err := ctr.hcsContainer.CreateProcess(createProcessParms)
-	if err != nil {
-		logger.WithError(err).Error("CreateProcess() failed")
-		return -1, err
-	}
-	defer func() {
-		if err != nil {
-			if err := newProcess.Kill(); err != nil {
-				logger.WithError(err).Error("failed to kill process")
-			}
-			go func() {
-				if err := newProcess.Wait(); err != nil {
-					logger.WithError(err).Error("failed to wait for process")
-				}
-				if err := newProcess.Close(); err != nil {
-					logger.WithError(err).Error("failed to clean process resources")
-				}
-			}()
-		}
-	}()
-	p := &process{
-		hcsProcess: newProcess,
-		id:         InitProcessName,
-		pid:        newProcess.Pid(),
-	}
-	logger.WithField("pid", p.pid).Debug("init process started")
-
-	dio, err := newIOFromProcess(newProcess, ctr.ociSpec.Process.Terminal)
-	if err != nil {
-		logger.WithError(err).Error("failed to get stdio pipes")
-		return -1, err
-	}
-	_, err = attachStdio(dio)
-	if err != nil {
-		logger.WithError(err).Error("failed to attache stdio")
-		return -1, err
-	}
-	ctr.status = StatusRunning
-	ctr.init = p
-
-	// Spin up a go routine waiting for exit to handle cleanup
-	go c.reapProcess(ctr, p)
-
-	// Generate the associated event
-	c.eventQ.append(id, func() {
-		ei := EventInfo{
-			ContainerID: id,
-			ProcessID:   InitProcessName,
-			Pid:         uint32(p.pid),
-		}
-		c.logger.WithFields(logrus.Fields{
-			"container":  ctr.id,
-			"event":      EventStart,
-			"event-info": ei,
-		}).Info("sending event")
-		err := c.backend.ProcessEvent(ei.ContainerID, EventStart, ei)
-		if err != nil {
-			c.logger.WithError(err).WithFields(logrus.Fields{
-				"container":  id,
-				"event":      EventStart,
-				"event-info": ei,
-			}).Error("failed to process event")
-		}
-	})
-	logger.Debug("start() completed")
-	return p.pid, nil
-}
-
-func newIOFromProcess(newProcess hcsshim.Process, terminal bool) (*cio.DirectIO, error) {
-	stdin, stdout, stderr, err := newProcess.Stdio()
-	if err != nil {
-		return nil, err
-	}
-
-	dio := cio.NewDirectIO(createStdInCloser(stdin, newProcess), nil, nil, terminal)
-
-	// Convert io.ReadClosers to io.Readers
-	if stdout != nil {
-		dio.Stdout = ioutil.NopCloser(&autoClosingReader{ReadCloser: stdout})
-	}
-	if stderr != nil {
-		dio.Stderr = ioutil.NopCloser(&autoClosingReader{ReadCloser: stderr})
-	}
-	return dio, nil
-}
-
-// Exec adds a process in an running container
-func (c *client) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error) {
-	ctr := c.getContainer(containerID)
-	switch {
-	case ctr == nil:
-		return -1, errors.WithStack(newNotFoundError("no such container"))
-	case ctr.hcsContainer == nil:
-		return -1, errors.WithStack(newInvalidParameterError("container is not running"))
-	case ctr.execs != nil && ctr.execs[processID] != nil:
-		return -1, errors.WithStack(newConflictError("id already in use"))
-	}
-	logger := c.logger.WithFields(logrus.Fields{
-		"container": containerID,
-		"exec":      processID,
-	})
-
-	// Note we always tell HCS to
-	// create stdout as it's required regardless of '-i' or '-t' options, so that
-	// docker can always grab the output through logs. We also tell HCS to always
-	// create stdin, even if it's not used - it will be closed shortly. Stderr
-	// is only created if it we're not -t.
-	createProcessParms := hcsshim.ProcessConfig{
-		CreateStdInPipe:  true,
-		CreateStdOutPipe: true,
-		CreateStdErrPipe: !spec.Terminal,
-	}
-	if spec.Terminal {
-		createProcessParms.EmulateConsole = true
-		if spec.ConsoleSize != nil {
-			createProcessParms.ConsoleSize[0] = uint(spec.ConsoleSize.Height)
-			createProcessParms.ConsoleSize[1] = uint(spec.ConsoleSize.Width)
-		}
-	}
-
-	// Take working directory from the process to add if it is defined,
-	// otherwise take from the first process.
-	if spec.Cwd != "" {
-		createProcessParms.WorkingDirectory = spec.Cwd
-	} else {
-		createProcessParms.WorkingDirectory = ctr.ociSpec.Process.Cwd
-	}
-
-	// Configure the environment for the process
-	createProcessParms.Environment = setupEnvironmentVariables(spec.Env)
-	if ctr.isWindows {
-		createProcessParms.CommandLine = strings.Join(spec.Args, " ")
-	} else {
-		createProcessParms.CommandArgs = spec.Args
-	}
-	createProcessParms.User = spec.User.Username
-
-	logger.Debugf("exec commandLine: %s", createProcessParms.CommandLine)
-
-	// Start the command running in the container.
-	newProcess, err := ctr.hcsContainer.CreateProcess(&createProcessParms)
-	if err != nil {
-		logger.WithError(err).Errorf("exec's CreateProcess() failed")
-		return -1, err
-	}
-	pid := newProcess.Pid()
-	defer func() {
-		if err != nil {
-			if err := newProcess.Kill(); err != nil {
-				logger.WithError(err).Error("failed to kill process")
-			}
-			go func() {
-				if err := newProcess.Wait(); err != nil {
-					logger.WithError(err).Error("failed to wait for process")
-				}
-				if err := newProcess.Close(); err != nil {
-					logger.WithError(err).Error("failed to clean process resources")
-				}
-			}()
-		}
-	}()
-
-	dio, err := newIOFromProcess(newProcess, spec.Terminal)
-	if err != nil {
-		logger.WithError(err).Error("failed to get stdio pipes")
-		return -1, err
-	}
-	// Tell the engine to attach streams back to the client
-	_, err = attachStdio(dio)
-	if err != nil {
-		return -1, err
-	}
-
-	p := &process{
-		id:         processID,
-		pid:        pid,
-		hcsProcess: newProcess,
-	}
-
-	// Add the process to the container's list of processes
-	ctr.Lock()
-	ctr.execs[processID] = p
-	ctr.Unlock()
-
-	// Spin up a go routine waiting for exit to handle cleanup
-	go c.reapProcess(ctr, p)
-
-	c.eventQ.append(ctr.id, func() {
-		ei := EventInfo{
-			ContainerID: ctr.id,
-			ProcessID:   p.id,
-			Pid:         uint32(p.pid),
-		}
-		c.logger.WithFields(logrus.Fields{
-			"container":  ctr.id,
-			"event":      EventExecAdded,
-			"event-info": ei,
-		}).Info("sending event")
-		err := c.backend.ProcessEvent(ctr.id, EventExecAdded, ei)
-		if err != nil {
-			c.logger.WithError(err).WithFields(logrus.Fields{
-				"container":  ctr.id,
-				"event":      EventExecAdded,
-				"event-info": ei,
-			}).Error("failed to process event")
-		}
-		err = c.backend.ProcessEvent(ctr.id, EventExecStarted, ei)
-		if err != nil {
-			c.logger.WithError(err).WithFields(logrus.Fields{
-				"container":  ctr.id,
-				"event":      EventExecStarted,
-				"event-info": ei,
-			}).Error("failed to process event")
-		}
-	})
-
-	return pid, nil
-}
-
-// Signal handles `docker stop` on Windows. While Linux has support for
-// the full range of signals, signals aren't really implemented on Windows.
-// We fake supporting regular stop and -9 to force kill.
-func (c *client) SignalProcess(_ context.Context, containerID, processID string, signal int) error {
-	ctr, p, err := c.getProcess(containerID, processID)
-	if err != nil {
-		return err
-	}
-
-	logger := c.logger.WithFields(logrus.Fields{
-		"container": containerID,
-		"process":   processID,
-		"pid":       p.pid,
-		"signal":    signal,
-	})
-	logger.Debug("Signal()")
-
-	if processID == InitProcessName {
-		if syscall.Signal(signal) == syscall.SIGKILL {
-			// Terminate the compute system
-			ctr.Lock()
-			ctr.terminateInvoked = true
-			if err := ctr.hcsContainer.Terminate(); err != nil {
-				if !hcsshim.IsPending(err) {
-					logger.WithError(err).Error("failed to terminate hccshim container")
-				}
-			}
-			ctr.Unlock()
-		} else {
-			// Shut down the container
-			if err := ctr.hcsContainer.Shutdown(); err != nil {
-				if !hcsshim.IsPending(err) && !hcsshim.IsAlreadyStopped(err) {
-					// ignore errors
-					logger.WithError(err).Error("failed to shutdown hccshim container")
-				}
-			}
-		}
-	} else {
-		return p.hcsProcess.Kill()
-	}
-
-	return nil
-}
-
-// Resize handles a CLI event to resize an interactive docker run or docker
-// exec window.
-func (c *client) ResizeTerminal(_ context.Context, containerID, processID string, width, height int) error {
-	_, p, err := c.getProcess(containerID, processID)
-	if err != nil {
-		return err
-	}
-
-	c.logger.WithFields(logrus.Fields{
-		"container": containerID,
-		"process":   processID,
-		"height":    height,
-		"width":     width,
-		"pid":       p.pid,
-	}).Debug("resizing")
-	return p.hcsProcess.ResizeConsole(uint16(width), uint16(height))
-}
-
-func (c *client) CloseStdin(_ context.Context, containerID, processID string) error {
-	_, p, err := c.getProcess(containerID, processID)
-	if err != nil {
-		return err
-	}
-
-	return p.hcsProcess.CloseStdin()
-}
-
-// Pause handles pause requests for containers
-func (c *client) Pause(_ context.Context, containerID string) error {
-	ctr, _, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return err
-	}
-
-	if ctr.ociSpec.Windows.HyperV == nil {
-		return errors.New("cannot pause Windows Server Containers")
-	}
-
-	ctr.Lock()
-	defer ctr.Unlock()
-
-	if err = ctr.hcsContainer.Pause(); err != nil {
-		return err
-	}
-
-	ctr.status = StatusPaused
-
-	c.eventQ.append(containerID, func() {
-		err := c.backend.ProcessEvent(containerID, EventPaused, EventInfo{
-			ContainerID: containerID,
-			ProcessID:   InitProcessName,
-		})
-		c.logger.WithFields(logrus.Fields{
-			"container": ctr.id,
-			"event":     EventPaused,
-		}).Info("sending event")
-		if err != nil {
-			c.logger.WithError(err).WithFields(logrus.Fields{
-				"container": containerID,
-				"event":     EventPaused,
-			}).Error("failed to process event")
-		}
-	})
-
-	return nil
-}
-
-// Resume handles resume requests for containers
-func (c *client) Resume(_ context.Context, containerID string) error {
-	ctr, _, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return err
-	}
-
-	if ctr.ociSpec.Windows.HyperV == nil {
-		return errors.New("cannot resume Windows Server Containers")
-	}
-
-	ctr.Lock()
-	defer ctr.Unlock()
-
-	if err = ctr.hcsContainer.Resume(); err != nil {
-		return err
-	}
-
-	ctr.status = StatusRunning
-
-	c.eventQ.append(containerID, func() {
-		err := c.backend.ProcessEvent(containerID, EventResumed, EventInfo{
-			ContainerID: containerID,
-			ProcessID:   InitProcessName,
-		})
-		c.logger.WithFields(logrus.Fields{
-			"container": ctr.id,
-			"event":     EventResumed,
-		}).Info("sending event")
-		if err != nil {
-			c.logger.WithError(err).WithFields(logrus.Fields{
-				"container": containerID,
-				"event":     EventResumed,
-			}).Error("failed to process event")
-		}
-	})
-
-	return nil
-}
-
-// Stats handles stats requests for containers
-func (c *client) Stats(_ context.Context, containerID string) (*Stats, error) {
-	ctr, _, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return nil, err
-	}
-
-	readAt := time.Now()
-	s, err := ctr.hcsContainer.Statistics()
-	if err != nil {
-		return nil, err
-	}
-	return &Stats{
-		Read:     readAt,
-		HCSStats: &s,
-	}, nil
-}
-
-// Restore is the handler for restoring a container
-func (c *client) Restore(ctx context.Context, id string, attachStdio StdioCallback) (bool, int, error) {
-	c.logger.WithField("container", id).Debug("restore()")
-
-	// TODO Windows: On RS1, a re-attach isn't possible.
-	// However, there is a scenario in which there is an issue.
-	// Consider a background container. The daemon dies unexpectedly.
-	// HCS will still have the compute service alive and running.
-	// For consistence, we call in to shoot it regardless if HCS knows about it
-	// We explicitly just log a warning if the terminate fails.
-	// Then we tell the backend the container exited.
-	if hc, err := hcsshim.OpenContainer(id); err == nil {
-		const terminateTimeout = time.Minute * 2
-		err := hc.Terminate()
-
-		if hcsshim.IsPending(err) {
-			err = hc.WaitTimeout(terminateTimeout)
-		} else if hcsshim.IsAlreadyStopped(err) {
-			err = nil
-		}
-
-		if err != nil {
-			c.logger.WithField("container", id).WithError(err).Debug("terminate failed on restore")
-			return false, -1, err
-		}
-	}
-	return false, -1, nil
-}
-
-// GetPidsForContainer returns a list of process IDs running in a container.
-// Not used on Windows.
-func (c *client) ListPids(_ context.Context, _ string) ([]uint32, error) {
-	return nil, errors.New("not implemented on Windows")
-}
-
-// Summary returns a summary of the processes running in a container.
-// This is present in Windows to support docker top. In linux, the
-// engine shells out to ps to get process information. On Windows, as
-// the containers could be Hyper-V containers, they would not be
-// visible on the container host. However, libcontainerd does have
-// that information.
-func (c *client) Summary(_ context.Context, containerID string) ([]Summary, error) {
-	ctr, _, err := c.getProcess(containerID, InitProcessName)
-	if err != nil {
-		return nil, err
-	}
-
-	p, err := ctr.hcsContainer.ProcessList()
-	if err != nil {
-		return nil, err
-	}
-
-	pl := make([]Summary, len(p))
-	for i := range p {
-		pl[i] = Summary(p[i])
-	}
-	return pl, nil
-}
-
-func (c *client) DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) {
-	ec := -1
-	ctr := c.getContainer(containerID)
-	if ctr == nil {
-		return uint32(ec), time.Now(), errors.WithStack(newNotFoundError("no such container"))
-	}
-
-	select {
-	case <-ctx.Done():
-		return uint32(ec), time.Now(), errors.WithStack(ctx.Err())
-	case <-ctr.waitCh:
-	default:
-		return uint32(ec), time.Now(), errors.New("container is not stopped")
-	}
-
-	ctr.Lock()
-	defer ctr.Unlock()
-	return ctr.exitCode, ctr.exitedAt, nil
-}
-
-func (c *client) Delete(_ context.Context, containerID string) error {
-	c.Lock()
-	defer c.Unlock()
-	ctr := c.containers[containerID]
-	if ctr == nil {
-		return errors.WithStack(newNotFoundError("no such container"))
-	}
-
-	ctr.Lock()
-	defer ctr.Unlock()
-
-	switch ctr.status {
-	case StatusCreated:
-		if err := c.shutdownContainer(ctr); err != nil {
-			return err
-		}
-		fallthrough
-	case StatusStopped:
-		delete(c.containers, containerID)
-		return nil
-	}
-
-	return errors.WithStack(newInvalidParameterError("container is not stopped"))
-}
-
-func (c *client) Status(ctx context.Context, containerID string) (Status, error) {
-	c.Lock()
-	defer c.Unlock()
-	ctr := c.containers[containerID]
-	if ctr == nil {
-		return StatusUnknown, errors.WithStack(newNotFoundError("no such container"))
-	}
-
-	ctr.Lock()
-	defer ctr.Unlock()
-	return ctr.status, nil
-}
-
-func (c *client) UpdateResources(ctx context.Context, containerID string, resources *Resources) error {
-	// Updating resource isn't supported on Windows
-	// but we should return nil for enabling updating container
-	return nil
-}
-
-func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error {
-	return errors.New("Windows: Containers do not support checkpoints")
-}
-
-func (c *client) getContainer(id string) *container {
-	c.Lock()
-	ctr := c.containers[id]
-	c.Unlock()
-
-	return ctr
-}
-
-func (c *client) getProcess(containerID, processID string) (*container, *process, error) {
-	ctr := c.getContainer(containerID)
-	switch {
-	case ctr == nil:
-		return nil, nil, errors.WithStack(newNotFoundError("no such container"))
-	case ctr.init == nil:
-		return nil, nil, errors.WithStack(newNotFoundError("container is not running"))
-	case processID == InitProcessName:
-		return ctr, ctr.init, nil
-	default:
-		ctr.Lock()
-		defer ctr.Unlock()
-		if ctr.execs == nil {
-			return nil, nil, errors.WithStack(newNotFoundError("no execs"))
-		}
-	}
-
-	p := ctr.execs[processID]
-	if p == nil {
-		return nil, nil, errors.WithStack(newNotFoundError("no such exec"))
-	}
-
-	return ctr, p, nil
-}
-
-// ctr mutex must be held when calling this function.
-func (c *client) shutdownContainer(ctr *container) error {
-	var err error
-	const waitTimeout = time.Minute * 5
-
-	if !ctr.terminateInvoked {
-		err = ctr.hcsContainer.Shutdown()
-	}
-
-	if hcsshim.IsPending(err) || ctr.terminateInvoked {
-		err = ctr.hcsContainer.WaitTimeout(waitTimeout)
-	} else if hcsshim.IsAlreadyStopped(err) {
-		err = nil
-	}
-
-	if err != nil {
-		c.logger.WithError(err).WithField("container", ctr.id).
-			Debug("failed to shutdown container, terminating it")
-		terminateErr := c.terminateContainer(ctr)
-		if terminateErr != nil {
-			c.logger.WithError(terminateErr).WithField("container", ctr.id).
-				Error("failed to shutdown container, and subsequent terminate also failed")
-			return fmt.Errorf("%s: subsequent terminate failed %s", err, terminateErr)
-		}
-		return err
-	}
-
-	return nil
-}
-
-// ctr mutex must be held when calling this function.
-func (c *client) terminateContainer(ctr *container) error {
-	const terminateTimeout = time.Minute * 5
-	ctr.terminateInvoked = true
-	err := ctr.hcsContainer.Terminate()
-
-	if hcsshim.IsPending(err) {
-		err = ctr.hcsContainer.WaitTimeout(terminateTimeout)
-	} else if hcsshim.IsAlreadyStopped(err) {
-		err = nil
-	}
-
-	if err != nil {
-		c.logger.WithError(err).WithField("container", ctr.id).
-			Debug("failed to terminate container")
-		return err
-	}
-
-	return nil
-}
-
-func (c *client) reapProcess(ctr *container, p *process) int {
-	logger := c.logger.WithFields(logrus.Fields{
-		"container": ctr.id,
-		"process":   p.id,
-	})
-
-	var eventErr error
-
-	// Block indefinitely for the process to exit.
-	if err := p.hcsProcess.Wait(); err != nil {
-		if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != windows.ERROR_BROKEN_PIPE {
-			logger.WithError(err).Warnf("Wait() failed (container may have been killed)")
-		}
-		// Fall through here, do not return. This ensures we attempt to
-		// continue the shutdown in HCS and tell the docker engine that the
-		// process/container has exited to avoid a container being dropped on
-		// the floor.
-	}
-	exitedAt := time.Now()
-
-	exitCode, err := p.hcsProcess.ExitCode()
-	if err != nil {
-		if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != windows.ERROR_BROKEN_PIPE {
-			logger.WithError(err).Warnf("unable to get exit code for process")
-		}
-		// Since we got an error retrieving the exit code, make sure that the
-		// code we return doesn't incorrectly indicate success.
-		exitCode = -1
-
-		// Fall through here, do not return. This ensures we attempt to
-		// continue the shutdown in HCS and tell the docker engine that the
-		// process/container has exited to avoid a container being dropped on
-		// the floor.
-	}
-
-	if err := p.hcsProcess.Close(); err != nil {
-		logger.WithError(err).Warnf("failed to cleanup hcs process resources")
-		exitCode = -1
-		eventErr = fmt.Errorf("hcsProcess.Close() failed %s", err)
-	}
-
-	if p.id == InitProcessName {
-		// Update container status
-		ctr.Lock()
-		ctr.status = StatusStopped
-		ctr.exitedAt = exitedAt
-		ctr.exitCode = uint32(exitCode)
-		close(ctr.waitCh)
-
-		if err := c.shutdownContainer(ctr); err != nil {
-			exitCode = -1
-			logger.WithError(err).Warn("failed to shutdown container")
-			thisErr := fmt.Errorf("failed to shutdown container: %s", err)
-			if eventErr != nil {
-				eventErr = fmt.Errorf("%s: %s", eventErr, thisErr)
-			} else {
-				eventErr = thisErr
-			}
-		} else {
-			logger.Debug("completed container shutdown")
-		}
-		ctr.Unlock()
-
-		if err := ctr.hcsContainer.Close(); err != nil {
-			exitCode = -1
-			logger.WithError(err).Error("failed to clean hcs container resources")
-			thisErr := fmt.Errorf("failed to terminate container: %s", err)
-			if eventErr != nil {
-				eventErr = fmt.Errorf("%s: %s", eventErr, thisErr)
-			} else {
-				eventErr = thisErr
-			}
-		}
-	}
-
-	c.eventQ.append(ctr.id, func() {
-		ei := EventInfo{
-			ContainerID: ctr.id,
-			ProcessID:   p.id,
-			Pid:         uint32(p.pid),
-			ExitCode:    uint32(exitCode),
-			ExitedAt:    exitedAt,
-			Error:       eventErr,
-		}
-		c.logger.WithFields(logrus.Fields{
-			"container":  ctr.id,
-			"event":      EventExit,
-			"event-info": ei,
-		}).Info("sending event")
-		err := c.backend.ProcessEvent(ctr.id, EventExit, ei)
-		if err != nil {
-			c.logger.WithError(err).WithFields(logrus.Fields{
-				"container":  ctr.id,
-				"event":      EventExit,
-				"event-info": ei,
-			}).Error("failed to process event")
-		}
-		if p.id != InitProcessName {
-			ctr.Lock()
-			delete(ctr.execs, p.id)
-			ctr.Unlock()
-		}
-	})
-
-	return exitCode
-}
diff --git a/libcontainerd/errors.go b/libcontainerd/errors.go
deleted file mode 100644
index bdc26715bc798..0000000000000
--- a/libcontainerd/errors.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import (
-	"errors"
-
-	"github.com/docker/docker/errdefs"
-)
-
-func newNotFoundError(err string) error { return errdefs.NotFound(errors.New(err)) }
-
-func newInvalidParameterError(err string) error { return errdefs.InvalidParameter(errors.New(err)) }
-
-func newConflictError(err string) error { return errdefs.Conflict(errors.New(err)) }
diff --git a/libcontainerd/libcontainerd_linux.go b/libcontainerd/libcontainerd_linux.go
new file mode 100644
index 0000000000000..ec195a7905e6f
--- /dev/null
+++ b/libcontainerd/libcontainerd_linux.go
@@ -0,0 +1,14 @@
+package libcontainerd // import "github.com/docker/docker/libcontainerd"
+
+import (
+	"context"
+
+	"github.com/containerd/containerd"
+	"github.com/docker/docker/libcontainerd/remote"
+	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
+)
+
+// NewClient creates a new libcontainerd client from a containerd client
+func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend) (libcontainerdtypes.Client, error) {
+	return remote.NewClient(ctx, cli, stateDir, ns, b)
+}
diff --git a/libcontainerd/libcontainerd_windows.go b/libcontainerd/libcontainerd_windows.go
new file mode 100644
index 0000000000000..61f19ba087a3c
--- /dev/null
+++ b/libcontainerd/libcontainerd_windows.go
@@ -0,0 +1,19 @@
+package libcontainerd // import "github.com/docker/docker/libcontainerd"
+
+import (
+	"context"
+
+	"github.com/containerd/containerd"
+	"github.com/docker/docker/libcontainerd/local"
+	"github.com/docker/docker/libcontainerd/remote"
+	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
+	"github.com/docker/docker/pkg/system"
+)
+
+// NewClient creates a new libcontainerd client from a containerd client
+func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend) (libcontainerdtypes.Client, error) {
+	if !system.ContainerdRuntimeSupported() {
+		return local.NewClient(ctx, cli, stateDir, ns, b)
+	}
+	return remote.NewClient(ctx, cli, stateDir, ns, b)
+}
diff --git a/libcontainerd/local/local_windows.go b/libcontainerd/local/local_windows.go
new file mode 100644
index 0000000000000..1cc35c08132f4
--- /dev/null
+++ b/libcontainerd/local/local_windows.go
@@ -0,0 +1,1204 @@
+package local // import "github.com/docker/docker/libcontainerd/local"
+
+// This package contains the legacy in-proc calls in HCS using the v1 schema
+// for Windows runtime purposes.
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"sync"
+	"syscall"
+	"time"
+
+	"github.com/Microsoft/hcsshim"
+	"github.com/Microsoft/hcsshim/osversion"
+	"github.com/containerd/containerd"
+	"github.com/containerd/containerd/cio"
+	containerderrdefs "github.com/containerd/containerd/errdefs"
+
+	"github.com/docker/docker/errdefs"
+	"github.com/docker/docker/libcontainerd/queue"
+	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
+	"github.com/docker/docker/pkg/sysinfo"
+	"github.com/docker/docker/pkg/system"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+	"golang.org/x/sys/windows"
+)
+
+type process struct {
+	id         string
+	pid        int
+	hcsProcess hcsshim.Process
+}
+
+type container struct {
+	sync.Mutex
+
+	// The ociSpec is required, as client.Create() needs a spec, but can
+	// be called from the RestartManager context which does not otherwise
+	// have access to the Spec
+	ociSpec *specs.Spec
+
+	hcsContainer hcsshim.Container
+
+	id               string
+	status           containerd.ProcessStatus
+	exitedAt         time.Time
+	exitCode         uint32
+	waitCh           chan struct{}
+	init             *process
+	execs            map[string]*process
+	terminateInvoked bool
+}
+
+// defaultOwner is a tag passed to HCS to allow it to differentiate between
+// container creator management stacks. We hard code "docker" in the case
+// of docker.
+const defaultOwner = "docker"
+
+type client struct {
+	sync.Mutex
+
+	stateDir   string
+	backend    libcontainerdtypes.Backend
+	logger     *logrus.Entry
+	eventQ     queue.Queue
+	containers map[string]*container
+}
+
+// NewClient creates a new local executor for windows
+func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend) (libcontainerdtypes.Client, error) {
+	c := &client{
+		stateDir:   stateDir,
+		backend:    b,
+		logger:     logrus.WithField("module", "libcontainerd").WithField("module", "libcontainerd").WithField("namespace", ns),
+		containers: make(map[string]*container),
+	}
+
+	return c, nil
+}
+
+func (c *client) Version(ctx context.Context) (containerd.Version, error) {
+	return containerd.Version{}, errors.New("not implemented on Windows")
+}
+
+// Create is the entrypoint to create a container from a spec.
+// Table below shows the fields required for HCS JSON calling parameters,
+// where if not populated, is omitted.
+// +-----------------+--------------------------------------------+---------------------------------------------------+
+// |                 | Isolation=Process                          | Isolation=Hyper-V                                 |
+// +-----------------+--------------------------------------------+---------------------------------------------------+
+// | VolumePath      | \\?\\Volume{GUIDa}                         |                                                   |
+// | LayerFolderPath | %root%\windowsfilter\containerID           |                                                   |
+// | Layers[]        | ID=GUIDb;Path=%root%\windowsfilter\layerID | ID=GUIDb;Path=%root%\windowsfilter\layerID        |
+// | HvRuntime       |                                            | ImagePath=%root%\BaseLayerID\UtilityVM            |
+// +-----------------+--------------------------------------------+---------------------------------------------------+
+//
+// Isolation=Process example:
+//
+// {
+// 	"SystemType": "Container",
+// 	"Name": "5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
+// 	"Owner": "docker",
+// 	"VolumePath": "\\\\\\\\?\\\\Volume{66d1ef4c-7a00-11e6-8948-00155ddbef9d}",
+// 	"IgnoreFlushesDuringBoot": true,
+// 	"LayerFolderPath": "C:\\\\control\\\\windowsfilter\\\\5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
+// 	"Layers": [{
+// 		"ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
+// 		"Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
+// 	}],
+// 	"HostName": "5e0055c814a6",
+// 	"MappedDirectories": [],
+// 	"HvPartition": false,
+// 	"EndpointList": ["eef2649d-bb17-4d53-9937-295a8efe6f2c"],
+// }
+//
+// Isolation=Hyper-V example:
+//
+// {
+// 	"SystemType": "Container",
+// 	"Name": "475c2c58933b72687a88a441e7e0ca4bd72d76413c5f9d5031fee83b98f6045d",
+// 	"Owner": "docker",
+// 	"IgnoreFlushesDuringBoot": true,
+// 	"Layers": [{
+// 		"ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
+// 		"Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
+// 	}],
+// 	"HostName": "475c2c58933b",
+// 	"MappedDirectories": [],
+// 	"HvPartition": true,
+// 	"EndpointList": ["e1bb1e61-d56f-405e-b75d-fd520cefa0cb"],
+// 	"DNSSearchList": "a.com,b.com,c.com",
+// 	"HvRuntime": {
+// 		"ImagePath": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c\\\\UtilityVM"
+// 	},
+// }
+func (c *client) Create(_ context.Context, id string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error {
+	if ctr := c.getContainer(id); ctr != nil {
+		return errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
+	}
+
+	var err error
+	if spec.Linux != nil {
+		return errors.New("linux containers are not supported on this platform")
+	}
+	err = c.createWindows(id, spec, runtimeOptions)
+
+	if err == nil {
+		c.eventQ.Append(id, func() {
+			ei := libcontainerdtypes.EventInfo{
+				ContainerID: id,
+			}
+			c.logger.WithFields(logrus.Fields{
+				"container": id,
+				"event":     libcontainerdtypes.EventCreate,
+			}).Info("sending event")
+			err := c.backend.ProcessEvent(id, libcontainerdtypes.EventCreate, ei)
+			if err != nil {
+				c.logger.WithError(err).WithFields(logrus.Fields{
+					"container": id,
+					"event":     libcontainerdtypes.EventCreate,
+				}).Error("failed to process event")
+			}
+		})
+	}
+	return err
+}
+
+func (c *client) createWindows(id string, spec *specs.Spec, runtimeOptions interface{}) error {
+	logger := c.logger.WithField("container", id)
+	configuration := &hcsshim.ContainerConfig{
+		SystemType:              "Container",
+		Name:                    id,
+		Owner:                   defaultOwner,
+		IgnoreFlushesDuringBoot: spec.Windows.IgnoreFlushesDuringBoot,
+		HostName:                spec.Hostname,
+		HvPartition:             false,
+	}
+
+	c.extractResourcesFromSpec(spec, configuration)
+
+	if spec.Windows.Resources != nil {
+		if spec.Windows.Resources.Storage != nil {
+			if spec.Windows.Resources.Storage.Bps != nil {
+				configuration.StorageBandwidthMaximum = *spec.Windows.Resources.Storage.Bps
+			}
+			if spec.Windows.Resources.Storage.Iops != nil {
+				configuration.StorageIOPSMaximum = *spec.Windows.Resources.Storage.Iops
+			}
+		}
+	}
+
+	if spec.Windows.HyperV != nil {
+		configuration.HvPartition = true
+	}
+
+	if spec.Windows.Network != nil {
+		configuration.EndpointList = spec.Windows.Network.EndpointList
+		configuration.AllowUnqualifiedDNSQuery = spec.Windows.Network.AllowUnqualifiedDNSQuery
+		if spec.Windows.Network.DNSSearchList != nil {
+			configuration.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",")
+		}
+		configuration.NetworkSharedContainerName = spec.Windows.Network.NetworkSharedContainerName
+	}
+
+	if cs, ok := spec.Windows.CredentialSpec.(string); ok {
+		configuration.Credentials = cs
+	}
+
+	// We must have least two layers in the spec, the bottom one being a
+	// base image, the top one being the RW layer.
+	if spec.Windows.LayerFolders == nil || len(spec.Windows.LayerFolders) < 2 {
+		return fmt.Errorf("OCI spec is invalid - at least two LayerFolders must be supplied to the runtime")
+	}
+
+	// Strip off the top-most layer as that's passed in separately to HCS
+	configuration.LayerFolderPath = spec.Windows.LayerFolders[len(spec.Windows.LayerFolders)-1]
+	layerFolders := spec.Windows.LayerFolders[:len(spec.Windows.LayerFolders)-1]
+
+	if configuration.HvPartition {
+		// We don't currently support setting the utility VM image explicitly.
+		// TODO circa RS5, this may be re-locatable.
+		if spec.Windows.HyperV.UtilityVMPath != "" {
+			return errors.New("runtime does not support an explicit utility VM path for Hyper-V containers")
+		}
+
+		// Find the upper-most utility VM image.
+		var uvmImagePath string
+		for _, path := range layerFolders {
+			fullPath := filepath.Join(path, "UtilityVM")
+			_, err := os.Stat(fullPath)
+			if err == nil {
+				uvmImagePath = fullPath
+				break
+			}
+			if !os.IsNotExist(err) {
+				return err
+			}
+		}
+		if uvmImagePath == "" {
+			return errors.New("utility VM image could not be found")
+		}
+		configuration.HvRuntime = &hcsshim.HvRuntime{ImagePath: uvmImagePath}
+
+		if spec.Root.Path != "" {
+			return errors.New("OCI spec is invalid - Root.Path must be omitted for a Hyper-V container")
+		}
+	} else {
+		const volumeGUIDRegex = `^\\\\\?\\(Volume)\{{0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}\}\\$`
+		if _, err := regexp.MatchString(volumeGUIDRegex, spec.Root.Path); err != nil {
+			return fmt.Errorf(`OCI spec is invalid - Root.Path '%s' must be a volume GUID path in the format '\\?\Volume{GUID}\'`, spec.Root.Path)
+		}
+		// HCS API requires the trailing backslash to be removed
+		configuration.VolumePath = spec.Root.Path[:len(spec.Root.Path)-1]
+	}
+
+	if spec.Root.Readonly {
+		return errors.New(`OCI spec is invalid - Root.Readonly must not be set on Windows`)
+	}
+
+	for _, layerPath := range layerFolders {
+		_, filename := filepath.Split(layerPath)
+		g, err := hcsshim.NameToGuid(filename)
+		if err != nil {
+			return err
+		}
+		configuration.Layers = append(configuration.Layers, hcsshim.Layer{
+			ID:   g.ToString(),
+			Path: layerPath,
+		})
+	}
+
+	// Add the mounts (volumes, bind mounts etc) to the structure
+	var mds []hcsshim.MappedDir
+	var mps []hcsshim.MappedPipe
+	for _, mount := range spec.Mounts {
+		const pipePrefix = `\\.\pipe\`
+		if mount.Type != "" {
+			return fmt.Errorf("OCI spec is invalid - Mount.Type '%s' must not be set", mount.Type)
+		}
+		if strings.HasPrefix(mount.Destination, pipePrefix) {
+			mp := hcsshim.MappedPipe{
+				HostPath:          mount.Source,
+				ContainerPipeName: mount.Destination[len(pipePrefix):],
+			}
+			mps = append(mps, mp)
+		} else {
+			md := hcsshim.MappedDir{
+				HostPath:      mount.Source,
+				ContainerPath: mount.Destination,
+				ReadOnly:      false,
+			}
+			for _, o := range mount.Options {
+				if strings.ToLower(o) == "ro" {
+					md.ReadOnly = true
+				}
+			}
+			mds = append(mds, md)
+		}
+	}
+	configuration.MappedDirectories = mds
+	if len(mps) > 0 && osversion.Build() < osversion.RS3 {
+		return errors.New("named pipe mounts are not supported on this version of Windows")
+	}
+	configuration.MappedPipes = mps
+
+	if len(spec.Windows.Devices) > 0 {
+		// Add any device assignments
+		if configuration.HvPartition {
+			return errors.New("device assignment is not supported for HyperV containers")
+		}
+		if osversion.Build() < osversion.RS5 {
+			return errors.New("device assignment requires Windows builds RS5 (17763+) or later")
+		}
+		for _, d := range spec.Windows.Devices {
+			configuration.AssignedDevices = append(configuration.AssignedDevices, hcsshim.AssignedDevice{InterfaceClassGUID: d.ID})
+		}
+	}
+
+	hcsContainer, err := hcsshim.CreateContainer(id, configuration)
+	if err != nil {
+		return err
+	}
+
+	// Construct a container object for calling start on it.
+	ctr := &container{
+		id:           id,
+		execs:        make(map[string]*process),
+		ociSpec:      spec,
+		hcsContainer: hcsContainer,
+		status:       containerd.Created,
+		waitCh:       make(chan struct{}),
+	}
+
+	logger.Debug("starting container")
+	if err = hcsContainer.Start(); err != nil {
+		c.logger.WithError(err).Error("failed to start container")
+		ctr.Lock()
+		if err := c.terminateContainer(ctr); err != nil {
+			c.logger.WithError(err).Error("failed to cleanup after a failed Start")
+		} else {
+			c.logger.Debug("cleaned up after failed Start by calling Terminate")
+		}
+		ctr.Unlock()
+		return err
+	}
+
+	c.Lock()
+	c.containers[id] = ctr
+	c.Unlock()
+
+	logger.Debug("createWindows() completed successfully")
+	return nil
+
+}
+
+func (c *client) extractResourcesFromSpec(spec *specs.Spec, configuration *hcsshim.ContainerConfig) {
+	if spec.Windows.Resources != nil {
+		if spec.Windows.Resources.CPU != nil {
+			if spec.Windows.Resources.CPU.Count != nil {
+				// This check is being done here rather than in adaptContainerSettings
+				// because we don't want to update the HostConfig in case this container
+				// is moved to a host with more CPUs than this one.
+				cpuCount := *spec.Windows.Resources.CPU.Count
+				hostCPUCount := uint64(sysinfo.NumCPU())
+				if cpuCount > hostCPUCount {
+					c.logger.Warnf("Changing requested CPUCount of %d to current number of processors, %d", cpuCount, hostCPUCount)
+					cpuCount = hostCPUCount
+				}
+				configuration.ProcessorCount = uint32(cpuCount)
+			}
+			if spec.Windows.Resources.CPU.Shares != nil {
+				configuration.ProcessorWeight = uint64(*spec.Windows.Resources.CPU.Shares)
+			}
+			if spec.Windows.Resources.CPU.Maximum != nil {
+				configuration.ProcessorMaximum = int64(*spec.Windows.Resources.CPU.Maximum)
+			}
+		}
+		if spec.Windows.Resources.Memory != nil {
+			if spec.Windows.Resources.Memory.Limit != nil {
+				configuration.MemoryMaximumInMB = int64(*spec.Windows.Resources.Memory.Limit) / 1024 / 1024
+			}
+		}
+	}
+}
+
+func (c *client) Start(_ context.Context, id, _ string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) {
+	ctr := c.getContainer(id)
+	switch {
+	case ctr == nil:
+		return -1, errors.WithStack(errdefs.NotFound(errors.New("no such container")))
+	case ctr.init != nil:
+		return -1, errors.WithStack(errdefs.NotModified(errors.New("container already started")))
+	}
+
+	logger := c.logger.WithField("container", id)
+
+	// Note we always tell HCS to create stdout as it's required
+	// regardless of '-i' or '-t' options, so that docker can always grab
+	// the output through logs. We also tell HCS to always create stdin,
+	// even if it's not used - it will be closed shortly. Stderr is only
+	// created if it we're not -t.
+	var (
+		emulateConsole   bool
+		createStdErrPipe bool
+	)
+	if ctr.ociSpec.Process != nil {
+		emulateConsole = ctr.ociSpec.Process.Terminal
+		createStdErrPipe = !ctr.ociSpec.Process.Terminal
+	}
+
+	createProcessParms := &hcsshim.ProcessConfig{
+		EmulateConsole:   emulateConsole,
+		WorkingDirectory: ctr.ociSpec.Process.Cwd,
+		CreateStdInPipe:  true,
+		CreateStdOutPipe: true,
+		CreateStdErrPipe: createStdErrPipe,
+	}
+
+	if ctr.ociSpec.Process != nil && ctr.ociSpec.Process.ConsoleSize != nil {
+		createProcessParms.ConsoleSize[0] = uint(ctr.ociSpec.Process.ConsoleSize.Height)
+		createProcessParms.ConsoleSize[1] = uint(ctr.ociSpec.Process.ConsoleSize.Width)
+	}
+
+	// Configure the environment for the process
+	createProcessParms.Environment = setupEnvironmentVariables(ctr.ociSpec.Process.Env)
+
+	// Configure the CommandLine/CommandArgs
+	setCommandLineAndArgs(ctr.ociSpec.Process, createProcessParms)
+	logger.Debugf("start commandLine: %s", createProcessParms.CommandLine)
+
+	createProcessParms.User = ctr.ociSpec.Process.User.Username
+
+	ctr.Lock()
+
+	// Start the command running in the container.
+	newProcess, err := ctr.hcsContainer.CreateProcess(createProcessParms)
+	if err != nil {
+		logger.WithError(err).Error("CreateProcess() failed")
+		// Fix for https://github.com/moby/moby/issues/38719.
+		// If the init process failed to launch, we still need to reap the
+		// container to avoid leaking it.
+		//
+		// Note we use the explicit exit code of 127 which is the
+		// Linux shell equivalent of "command not found". Windows cannot
+		// know ahead of time whether or not the command exists, especially
+		// in the case of Hyper-V containers.
+		ctr.Unlock()
+		exitedAt := time.Now()
+		p := &process{
+			id:  libcontainerdtypes.InitProcessName,
+			pid: 0,
+		}
+		c.reapContainer(ctr, p, 127, exitedAt, nil, logger)
+		return -1, err
+	}
+
+	defer ctr.Unlock()
+
+	defer func() {
+		if err != nil {
+			if err := newProcess.Kill(); err != nil {
+				logger.WithError(err).Error("failed to kill process")
+			}
+			go func() {
+				if err := newProcess.Wait(); err != nil {
+					logger.WithError(err).Error("failed to wait for process")
+				}
+				if err := newProcess.Close(); err != nil {
+					logger.WithError(err).Error("failed to clean process resources")
+				}
+			}()
+		}
+	}()
+	p := &process{
+		hcsProcess: newProcess,
+		id:         libcontainerdtypes.InitProcessName,
+		pid:        newProcess.Pid(),
+	}
+	logger.WithField("pid", p.pid).Debug("init process started")
+
+	ctr.status = containerd.Running
+	ctr.init = p
+
+	// Spin up a go routine waiting for exit to handle cleanup
+	go c.reapProcess(ctr, p)
+
+	// Don't shadow err here due to our deferred clean-up.
+	var dio *cio.DirectIO
+	dio, err = newIOFromProcess(newProcess, ctr.ociSpec.Process.Terminal)
+	if err != nil {
+		logger.WithError(err).Error("failed to get stdio pipes")
+		return -1, err
+	}
+	_, err = attachStdio(dio)
+	if err != nil {
+		logger.WithError(err).Error("failed to attach stdio")
+		return -1, err
+	}
+
+	// Generate the associated event
+	c.eventQ.Append(id, func() {
+		ei := libcontainerdtypes.EventInfo{
+			ContainerID: id,
+			ProcessID:   libcontainerdtypes.InitProcessName,
+			Pid:         uint32(p.pid),
+		}
+		c.logger.WithFields(logrus.Fields{
+			"container":  ctr.id,
+			"event":      libcontainerdtypes.EventStart,
+			"event-info": ei,
+		}).Info("sending event")
+		err := c.backend.ProcessEvent(ei.ContainerID, libcontainerdtypes.EventStart, ei)
+		if err != nil {
+			c.logger.WithError(err).WithFields(logrus.Fields{
+				"container":  id,
+				"event":      libcontainerdtypes.EventStart,
+				"event-info": ei,
+			}).Error("failed to process event")
+		}
+	})
+	logger.Debug("start() completed")
+	return p.pid, nil
+}
+
+// setCommandLineAndArgs configures the HCS ProcessConfig based on an OCI process spec
+func setCommandLineAndArgs(process *specs.Process, createProcessParms *hcsshim.ProcessConfig) {
+	if process.CommandLine != "" {
+		createProcessParms.CommandLine = process.CommandLine
+	} else {
+		createProcessParms.CommandLine = system.EscapeArgs(process.Args)
+	}
+}
+
+func newIOFromProcess(newProcess hcsshim.Process, terminal bool) (*cio.DirectIO, error) {
+	stdin, stdout, stderr, err := newProcess.Stdio()
+	if err != nil {
+		return nil, err
+	}
+
+	dio := cio.NewDirectIO(createStdInCloser(stdin, newProcess), nil, nil, terminal)
+
+	// Convert io.ReadClosers to io.Readers
+	if stdout != nil {
+		dio.Stdout = io.NopCloser(&autoClosingReader{ReadCloser: stdout})
+	}
+	if stderr != nil {
+		dio.Stderr = io.NopCloser(&autoClosingReader{ReadCloser: stderr})
+	}
+	return dio, nil
+}
+
+// Exec adds a process in an running container
+func (c *client) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) {
+	ctr := c.getContainer(containerID)
+	switch {
+	case ctr == nil:
+		return -1, errors.WithStack(errdefs.NotFound(errors.New("no such container")))
+	case ctr.hcsContainer == nil:
+		return -1, errors.WithStack(errdefs.InvalidParameter(errors.New("container is not running")))
+	case ctr.execs != nil && ctr.execs[processID] != nil:
+		return -1, errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
+	}
+	logger := c.logger.WithFields(logrus.Fields{
+		"container": containerID,
+		"exec":      processID,
+	})
+
+	// Note we always tell HCS to
+	// create stdout as it's required regardless of '-i' or '-t' options, so that
+	// docker can always grab the output through logs. We also tell HCS to always
+	// create stdin, even if it's not used - it will be closed shortly. Stderr
+	// is only created if it we're not -t.
+	createProcessParms := &hcsshim.ProcessConfig{
+		CreateStdInPipe:  true,
+		CreateStdOutPipe: true,
+		CreateStdErrPipe: !spec.Terminal,
+	}
+	if spec.Terminal {
+		createProcessParms.EmulateConsole = true
+		if spec.ConsoleSize != nil {
+			createProcessParms.ConsoleSize[0] = uint(spec.ConsoleSize.Height)
+			createProcessParms.ConsoleSize[1] = uint(spec.ConsoleSize.Width)
+		}
+	}
+
+	// Take working directory from the process to add if it is defined,
+	// otherwise take from the first process.
+	if spec.Cwd != "" {
+		createProcessParms.WorkingDirectory = spec.Cwd
+	} else {
+		createProcessParms.WorkingDirectory = ctr.ociSpec.Process.Cwd
+	}
+
+	// Configure the environment for the process
+	createProcessParms.Environment = setupEnvironmentVariables(spec.Env)
+
+	// Configure the CommandLine/CommandArgs
+	setCommandLineAndArgs(spec, createProcessParms)
+	logger.Debugf("exec commandLine: %s", createProcessParms.CommandLine)
+
+	createProcessParms.User = spec.User.Username
+
+	// Start the command running in the container.
+	newProcess, err := ctr.hcsContainer.CreateProcess(createProcessParms)
+	if err != nil {
+		logger.WithError(err).Errorf("exec's CreateProcess() failed")
+		return -1, err
+	}
+	pid := newProcess.Pid()
+	defer func() {
+		if err != nil {
+			if err := newProcess.Kill(); err != nil {
+				logger.WithError(err).Error("failed to kill process")
+			}
+			go func() {
+				if err := newProcess.Wait(); err != nil {
+					logger.WithError(err).Error("failed to wait for process")
+				}
+				if err := newProcess.Close(); err != nil {
+					logger.WithError(err).Error("failed to clean process resources")
+				}
+			}()
+		}
+	}()
+
+	dio, err := newIOFromProcess(newProcess, spec.Terminal)
+	if err != nil {
+		logger.WithError(err).Error("failed to get stdio pipes")
+		return -1, err
+	}
+	// Tell the engine to attach streams back to the client
+	_, err = attachStdio(dio)
+	if err != nil {
+		return -1, err
+	}
+
+	p := &process{
+		id:         processID,
+		pid:        pid,
+		hcsProcess: newProcess,
+	}
+
+	// Add the process to the container's list of processes
+	ctr.Lock()
+	ctr.execs[processID] = p
+	ctr.Unlock()
+
+	// Spin up a go routine waiting for exit to handle cleanup
+	go c.reapProcess(ctr, p)
+
+	c.eventQ.Append(ctr.id, func() {
+		ei := libcontainerdtypes.EventInfo{
+			ContainerID: ctr.id,
+			ProcessID:   p.id,
+			Pid:         uint32(p.pid),
+		}
+		c.logger.WithFields(logrus.Fields{
+			"container":  ctr.id,
+			"event":      libcontainerdtypes.EventExecAdded,
+			"event-info": ei,
+		}).Info("sending event")
+		err := c.backend.ProcessEvent(ctr.id, libcontainerdtypes.EventExecAdded, ei)
+		if err != nil {
+			c.logger.WithError(err).WithFields(logrus.Fields{
+				"container":  ctr.id,
+				"event":      libcontainerdtypes.EventExecAdded,
+				"event-info": ei,
+			}).Error("failed to process event")
+		}
+		err = c.backend.ProcessEvent(ctr.id, libcontainerdtypes.EventExecStarted, ei)
+		if err != nil {
+			c.logger.WithError(err).WithFields(logrus.Fields{
+				"container":  ctr.id,
+				"event":      libcontainerdtypes.EventExecStarted,
+				"event-info": ei,
+			}).Error("failed to process event")
+		}
+	})
+
+	return pid, nil
+}
+
+// SignalProcess handles `docker stop` on Windows. While Linux has support for
+// the full range of signals, signals aren't really implemented on Windows.
+// We fake supporting regular stop and -9 to force kill.
+func (c *client) SignalProcess(_ context.Context, containerID, processID string, signal int) error {
+	ctr, p, err := c.getProcess(containerID, processID)
+	if err != nil {
+		return err
+	}
+
+	logger := c.logger.WithFields(logrus.Fields{
+		"container": containerID,
+		"process":   processID,
+		"pid":       p.pid,
+		"signal":    signal,
+	})
+	logger.Debug("Signal()")
+
+	if processID == libcontainerdtypes.InitProcessName {
+		if syscall.Signal(signal) == syscall.SIGKILL {
+			// Terminate the compute system
+			ctr.Lock()
+			ctr.terminateInvoked = true
+			if err := ctr.hcsContainer.Terminate(); err != nil {
+				if !hcsshim.IsPending(err) {
+					logger.WithError(err).Error("failed to terminate hccshim container")
+				}
+			}
+			ctr.Unlock()
+		} else {
+			// Shut down the container
+			if err := ctr.hcsContainer.Shutdown(); err != nil {
+				if !hcsshim.IsPending(err) && !hcsshim.IsAlreadyStopped(err) {
+					// ignore errors
+					logger.WithError(err).Error("failed to shutdown hccshim container")
+				}
+			}
+		}
+	} else {
+		return p.hcsProcess.Kill()
+	}
+
+	return nil
+}
+
+// ResizeTerminal handles a CLI event to resize an interactive docker run or docker
+// exec window.
+func (c *client) ResizeTerminal(_ context.Context, containerID, processID string, width, height int) error {
+	_, p, err := c.getProcess(containerID, processID)
+	if err != nil {
+		return err
+	}
+
+	c.logger.WithFields(logrus.Fields{
+		"container": containerID,
+		"process":   processID,
+		"height":    height,
+		"width":     width,
+		"pid":       p.pid,
+	}).Debug("resizing")
+	return p.hcsProcess.ResizeConsole(uint16(width), uint16(height))
+}
+
+func (c *client) CloseStdin(_ context.Context, containerID, processID string) error {
+	_, p, err := c.getProcess(containerID, processID)
+	if err != nil {
+		return err
+	}
+
+	return p.hcsProcess.CloseStdin()
+}
+
+// Pause handles pause requests for containers
+func (c *client) Pause(_ context.Context, containerID string) error {
+	ctr, _, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return err
+	}
+
+	if ctr.ociSpec.Windows.HyperV == nil {
+		return containerderrdefs.ErrNotImplemented
+	}
+
+	ctr.Lock()
+	defer ctr.Unlock()
+
+	if err = ctr.hcsContainer.Pause(); err != nil {
+		return err
+	}
+
+	ctr.status = containerd.Paused
+
+	c.eventQ.Append(containerID, func() {
+		err := c.backend.ProcessEvent(containerID, libcontainerdtypes.EventPaused, libcontainerdtypes.EventInfo{
+			ContainerID: containerID,
+			ProcessID:   libcontainerdtypes.InitProcessName,
+		})
+		c.logger.WithFields(logrus.Fields{
+			"container": ctr.id,
+			"event":     libcontainerdtypes.EventPaused,
+		}).Info("sending event")
+		if err != nil {
+			c.logger.WithError(err).WithFields(logrus.Fields{
+				"container": containerID,
+				"event":     libcontainerdtypes.EventPaused,
+			}).Error("failed to process event")
+		}
+	})
+
+	return nil
+}
+
+// Resume handles resume requests for containers
+func (c *client) Resume(_ context.Context, containerID string) error {
+	ctr, _, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return err
+	}
+
+	if ctr.ociSpec.Windows.HyperV == nil {
+		return errors.New("cannot resume Windows Server Containers")
+	}
+
+	ctr.Lock()
+	defer ctr.Unlock()
+
+	if err = ctr.hcsContainer.Resume(); err != nil {
+		return err
+	}
+
+	ctr.status = containerd.Running
+
+	c.eventQ.Append(containerID, func() {
+		err := c.backend.ProcessEvent(containerID, libcontainerdtypes.EventResumed, libcontainerdtypes.EventInfo{
+			ContainerID: containerID,
+			ProcessID:   libcontainerdtypes.InitProcessName,
+		})
+		c.logger.WithFields(logrus.Fields{
+			"container": ctr.id,
+			"event":     libcontainerdtypes.EventResumed,
+		}).Info("sending event")
+		if err != nil {
+			c.logger.WithError(err).WithFields(logrus.Fields{
+				"container": containerID,
+				"event":     libcontainerdtypes.EventResumed,
+			}).Error("failed to process event")
+		}
+	})
+
+	return nil
+}
+
+// Stats handles stats requests for containers
+func (c *client) Stats(_ context.Context, containerID string) (*libcontainerdtypes.Stats, error) {
+	ctr, _, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return nil, err
+	}
+
+	readAt := time.Now()
+	s, err := ctr.hcsContainer.Statistics()
+	if err != nil {
+		return nil, err
+	}
+	return &libcontainerdtypes.Stats{
+		Read:     readAt,
+		HCSStats: &s,
+	}, nil
+}
+
+// Restore is the handler for restoring a container
+func (c *client) Restore(ctx context.Context, id string, attachStdio libcontainerdtypes.StdioCallback) (bool, int, libcontainerdtypes.Process, error) {
+	c.logger.WithField("container", id).Debug("restore()")
+
+	// TODO Windows: On RS1, a re-attach isn't possible.
+	// However, there is a scenario in which there is an issue.
+	// Consider a background container. The daemon dies unexpectedly.
+	// HCS will still have the compute service alive and running.
+	// For consistence, we call in to shoot it regardless if HCS knows about it
+	// We explicitly just log a warning if the terminate fails.
+	// Then we tell the backend the container exited.
+	if hc, err := hcsshim.OpenContainer(id); err == nil {
+		const terminateTimeout = time.Minute * 2
+		err := hc.Terminate()
+
+		if hcsshim.IsPending(err) {
+			err = hc.WaitTimeout(terminateTimeout)
+		} else if hcsshim.IsAlreadyStopped(err) {
+			err = nil
+		}
+
+		if err != nil {
+			c.logger.WithField("container", id).WithError(err).Debug("terminate failed on restore")
+			return false, -1, nil, err
+		}
+	}
+	return false, -1, &restoredProcess{
+		c:  c,
+		id: id,
+	}, nil
+}
+
+// ListPids returns a list of process IDs running in a container. It is not
+// implemented on Windows.
+func (c *client) ListPids(_ context.Context, _ string) ([]uint32, error) {
+	return nil, errors.New("not implemented on Windows")
+}
+
+// Summary returns a summary of the processes running in a container.
+// This is present in Windows to support docker top. In linux, the
+// engine shells out to ps to get process information. On Windows, as
+// the containers could be Hyper-V containers, they would not be
+// visible on the container host. However, libcontainerd does have
+// that information.
+func (c *client) Summary(_ context.Context, containerID string) ([]libcontainerdtypes.Summary, error) {
+	ctr, _, err := c.getProcess(containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return nil, err
+	}
+
+	p, err := ctr.hcsContainer.ProcessList()
+	if err != nil {
+		return nil, err
+	}
+
+	pl := make([]libcontainerdtypes.Summary, len(p))
+	for i := range p {
+		pl[i] = libcontainerdtypes.Summary{
+			ImageName:                    p[i].ImageName,
+			CreatedAt:                    p[i].CreateTimestamp,
+			KernelTime_100Ns:             p[i].KernelTime100ns,
+			MemoryCommitBytes:            p[i].MemoryCommitBytes,
+			MemoryWorkingSetPrivateBytes: p[i].MemoryWorkingSetPrivateBytes,
+			MemoryWorkingSetSharedBytes:  p[i].MemoryWorkingSetSharedBytes,
+			ProcessID:                    p[i].ProcessId,
+			UserTime_100Ns:               p[i].UserTime100ns,
+			ExecID:                       "",
+		}
+	}
+	return pl, nil
+}
+
+type restoredProcess struct {
+	id string
+	c  *client
+}
+
+func (p *restoredProcess) Delete(ctx context.Context) (uint32, time.Time, error) {
+	return p.c.DeleteTask(ctx, p.id)
+}
+
+func (c *client) DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) {
+	ec := -1
+	ctr := c.getContainer(containerID)
+	if ctr == nil {
+		return uint32(ec), time.Now(), errors.WithStack(errdefs.NotFound(errors.New("no such container")))
+	}
+
+	select {
+	case <-ctx.Done():
+		return uint32(ec), time.Now(), errors.WithStack(ctx.Err())
+	case <-ctr.waitCh:
+	default:
+		return uint32(ec), time.Now(), errors.New("container is not stopped")
+	}
+
+	ctr.Lock()
+	defer ctr.Unlock()
+	return ctr.exitCode, ctr.exitedAt, nil
+}
+
+func (c *client) Delete(_ context.Context, containerID string) error {
+	c.Lock()
+	defer c.Unlock()
+	ctr := c.containers[containerID]
+	if ctr == nil {
+		return errors.WithStack(errdefs.NotFound(errors.New("no such container")))
+	}
+
+	ctr.Lock()
+	defer ctr.Unlock()
+
+	switch ctr.status {
+	case containerd.Created:
+		if err := c.shutdownContainer(ctr); err != nil {
+			return err
+		}
+		fallthrough
+	case containerd.Stopped:
+		delete(c.containers, containerID)
+		return nil
+	}
+
+	return errors.WithStack(errdefs.InvalidParameter(errors.New("container is not stopped")))
+}
+
+func (c *client) Status(ctx context.Context, containerID string) (containerd.ProcessStatus, error) {
+	c.Lock()
+	defer c.Unlock()
+	ctr := c.containers[containerID]
+	if ctr == nil {
+		return containerd.Unknown, errors.WithStack(errdefs.NotFound(errors.New("no such container")))
+	}
+
+	ctr.Lock()
+	defer ctr.Unlock()
+	return ctr.status, nil
+}
+
+func (c *client) UpdateResources(ctx context.Context, containerID string, resources *libcontainerdtypes.Resources) error {
+	// Updating resource isn't supported on Windows
+	// but we should return nil for enabling updating container
+	return nil
+}
+
+func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error {
+	return errors.New("Windows: Containers do not support checkpoints")
+}
+
+func (c *client) getContainer(id string) *container {
+	c.Lock()
+	ctr := c.containers[id]
+	c.Unlock()
+
+	return ctr
+}
+
+func (c *client) getProcess(containerID, processID string) (*container, *process, error) {
+	ctr := c.getContainer(containerID)
+	switch {
+	case ctr == nil:
+		return nil, nil, errors.WithStack(errdefs.NotFound(errors.New("no such container")))
+	case ctr.init == nil:
+		return nil, nil, errors.WithStack(errdefs.NotFound(errors.New("container is not running")))
+	case processID == libcontainerdtypes.InitProcessName:
+		return ctr, ctr.init, nil
+	default:
+		ctr.Lock()
+		defer ctr.Unlock()
+		if ctr.execs == nil {
+			return nil, nil, errors.WithStack(errdefs.NotFound(errors.New("no execs")))
+		}
+	}
+
+	p := ctr.execs[processID]
+	if p == nil {
+		return nil, nil, errors.WithStack(errdefs.NotFound(errors.New("no such exec")))
+	}
+
+	return ctr, p, nil
+}
+
+// ctr mutex must be held when calling this function.
+func (c *client) shutdownContainer(ctr *container) error {
+	var err error
+	const waitTimeout = time.Minute * 5
+
+	if !ctr.terminateInvoked {
+		err = ctr.hcsContainer.Shutdown()
+	}
+
+	if hcsshim.IsPending(err) || ctr.terminateInvoked {
+		err = ctr.hcsContainer.WaitTimeout(waitTimeout)
+	} else if hcsshim.IsAlreadyStopped(err) {
+		err = nil
+	}
+
+	if err != nil {
+		c.logger.WithError(err).WithField("container", ctr.id).
+			Debug("failed to shutdown container, terminating it")
+		terminateErr := c.terminateContainer(ctr)
+		if terminateErr != nil {
+			c.logger.WithError(terminateErr).WithField("container", ctr.id).
+				Error("failed to shutdown container, and subsequent terminate also failed")
+			return fmt.Errorf("%s: subsequent terminate failed %s", err, terminateErr)
+		}
+		return err
+	}
+
+	return nil
+}
+
+// ctr mutex must be held when calling this function.
+func (c *client) terminateContainer(ctr *container) error {
+	const terminateTimeout = time.Minute * 5
+	ctr.terminateInvoked = true
+	err := ctr.hcsContainer.Terminate()
+
+	if hcsshim.IsPending(err) {
+		err = ctr.hcsContainer.WaitTimeout(terminateTimeout)
+	} else if hcsshim.IsAlreadyStopped(err) {
+		err = nil
+	}
+
+	if err != nil {
+		c.logger.WithError(err).WithField("container", ctr.id).
+			Debug("failed to terminate container")
+		return err
+	}
+
+	return nil
+}
+
+func (c *client) reapProcess(ctr *container, p *process) int {
+	logger := c.logger.WithFields(logrus.Fields{
+		"container": ctr.id,
+		"process":   p.id,
+	})
+
+	var eventErr error
+
+	// Block indefinitely for the process to exit.
+	if err := p.hcsProcess.Wait(); err != nil {
+		if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != windows.ERROR_BROKEN_PIPE {
+			logger.WithError(err).Warnf("Wait() failed (container may have been killed)")
+		}
+		// Fall through here, do not return. This ensures we attempt to
+		// continue the shutdown in HCS and tell the docker engine that the
+		// process/container has exited to avoid a container being dropped on
+		// the floor.
+	}
+	exitedAt := time.Now()
+
+	exitCode, err := p.hcsProcess.ExitCode()
+	if err != nil {
+		if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != windows.ERROR_BROKEN_PIPE {
+			logger.WithError(err).Warnf("unable to get exit code for process")
+		}
+		// Since we got an error retrieving the exit code, make sure that the
+		// code we return doesn't incorrectly indicate success.
+		exitCode = -1
+
+		// Fall through here, do not return. This ensures we attempt to
+		// continue the shutdown in HCS and tell the docker engine that the
+		// process/container has exited to avoid a container being dropped on
+		// the floor.
+	}
+
+	if err := p.hcsProcess.Close(); err != nil {
+		logger.WithError(err).Warnf("failed to cleanup hcs process resources")
+		exitCode = -1
+		eventErr = fmt.Errorf("hcsProcess.Close() failed %s", err)
+	}
+
+	if p.id == libcontainerdtypes.InitProcessName {
+		exitCode, eventErr = c.reapContainer(ctr, p, exitCode, exitedAt, eventErr, logger)
+	}
+
+	c.eventQ.Append(ctr.id, func() {
+		ei := libcontainerdtypes.EventInfo{
+			ContainerID: ctr.id,
+			ProcessID:   p.id,
+			Pid:         uint32(p.pid),
+			ExitCode:    uint32(exitCode),
+			ExitedAt:    exitedAt,
+			Error:       eventErr,
+		}
+		c.logger.WithFields(logrus.Fields{
+			"container":  ctr.id,
+			"event":      libcontainerdtypes.EventExit,
+			"event-info": ei,
+		}).Info("sending event")
+		err := c.backend.ProcessEvent(ctr.id, libcontainerdtypes.EventExit, ei)
+		if err != nil {
+			c.logger.WithError(err).WithFields(logrus.Fields{
+				"container":  ctr.id,
+				"event":      libcontainerdtypes.EventExit,
+				"event-info": ei,
+			}).Error("failed to process event")
+		}
+		if p.id != libcontainerdtypes.InitProcessName {
+			ctr.Lock()
+			delete(ctr.execs, p.id)
+			ctr.Unlock()
+		}
+	})
+
+	return exitCode
+}
+
+// reapContainer shuts down the container and releases associated resources. It returns
+// the error to be logged in the eventInfo sent back to the monitor.
+func (c *client) reapContainer(ctr *container, p *process, exitCode int, exitedAt time.Time, eventErr error, logger *logrus.Entry) (int, error) {
+	// Update container status
+	ctr.Lock()
+	ctr.status = containerd.Stopped
+	ctr.exitedAt = exitedAt
+	ctr.exitCode = uint32(exitCode)
+	close(ctr.waitCh)
+
+	if err := c.shutdownContainer(ctr); err != nil {
+		exitCode = -1
+		logger.WithError(err).Warn("failed to shutdown container")
+		thisErr := errors.Wrap(err, "failed to shutdown container")
+		if eventErr != nil {
+			eventErr = errors.Wrap(eventErr, thisErr.Error())
+		} else {
+			eventErr = thisErr
+		}
+	} else {
+		logger.Debug("completed container shutdown")
+	}
+	ctr.Unlock()
+
+	if err := ctr.hcsContainer.Close(); err != nil {
+		exitCode = -1
+		logger.WithError(err).Error("failed to clean hcs container resources")
+		thisErr := errors.Wrap(err, "failed to terminate container")
+		if eventErr != nil {
+			eventErr = errors.Wrap(eventErr, thisErr.Error())
+		} else {
+			eventErr = thisErr
+		}
+	}
+	return exitCode, eventErr
+}
diff --git a/libcontainerd/process_windows.go b/libcontainerd/local/process_windows.go
similarity index 92%
rename from libcontainerd/process_windows.go
rename to libcontainerd/local/process_windows.go
index 8cdf1daca8d56..6ff9f7e83ec75 100644
--- a/libcontainerd/process_windows.go
+++ b/libcontainerd/local/process_windows.go
@@ -1,4 +1,4 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
+package local // import "github.com/docker/docker/libcontainerd/local"
 
 import (
 	"io"
diff --git a/libcontainerd/local/utils_windows.go b/libcontainerd/local/utils_windows.go
new file mode 100644
index 0000000000000..ccb52ba4f2611
--- /dev/null
+++ b/libcontainerd/local/utils_windows.go
@@ -0,0 +1,16 @@
+package local // import "github.com/docker/docker/libcontainerd/local"
+
+import "strings"
+
+// setupEnvironmentVariables converts a string array of environment variables
+// into a map as required by the HCS. Source array is in format [v1=k1] [v2=k2] etc.
+func setupEnvironmentVariables(a []string) map[string]string {
+	r := make(map[string]string)
+	for _, s := range a {
+		arr := strings.SplitN(s, "=", 2)
+		if len(arr) == 2 {
+			r[arr[0]] = arr[1]
+		}
+	}
+	return r
+}
diff --git a/libcontainerd/utils_windows_test.go b/libcontainerd/local/utils_windows_test.go
similarity index 81%
rename from libcontainerd/utils_windows_test.go
rename to libcontainerd/local/utils_windows_test.go
index 2e0c260ecad45..4fc837e326581 100644
--- a/libcontainerd/utils_windows_test.go
+++ b/libcontainerd/local/utils_windows_test.go
@@ -1,4 +1,4 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
+package local // import "github.com/docker/docker/libcontainerd/local"
 
 import (
 	"testing"
diff --git a/libcontainerd/queue.go b/libcontainerd/queue.go
deleted file mode 100644
index 207722c4419a3..0000000000000
--- a/libcontainerd/queue.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import "sync"
-
-type queue struct {
-	sync.Mutex
-	fns map[string]chan struct{}
-}
-
-func (q *queue) append(id string, f func()) {
-	q.Lock()
-	defer q.Unlock()
-
-	if q.fns == nil {
-		q.fns = make(map[string]chan struct{})
-	}
-
-	done := make(chan struct{})
-
-	fn, ok := q.fns[id]
-	q.fns[id] = done
-	go func() {
-		if ok {
-			<-fn
-		}
-		f()
-		close(done)
-
-		q.Lock()
-		if q.fns[id] == done {
-			delete(q.fns, id)
-		}
-		q.Unlock()
-	}()
-}
diff --git a/libcontainerd/queue/queue.go b/libcontainerd/queue/queue.go
new file mode 100644
index 0000000000000..71ff0c79cd09a
--- /dev/null
+++ b/libcontainerd/queue/queue.go
@@ -0,0 +1,37 @@
+package queue // import "github.com/docker/docker/libcontainerd/queue"
+
+import "sync"
+
+// Queue is the structure used for holding functions in a queue.
+type Queue struct {
+	sync.Mutex
+	fns map[string]chan struct{}
+}
+
+// Append adds an item to a queue.
+func (q *Queue) Append(id string, f func()) {
+	q.Lock()
+	defer q.Unlock()
+
+	if q.fns == nil {
+		q.fns = make(map[string]chan struct{})
+	}
+
+	done := make(chan struct{})
+
+	fn, ok := q.fns[id]
+	q.fns[id] = done
+	go func() {
+		if ok {
+			<-fn
+		}
+		f()
+		close(done)
+
+		q.Lock()
+		if q.fns[id] == done {
+			delete(q.fns, id)
+		}
+		q.Unlock()
+	}()
+}
diff --git a/libcontainerd/queue/queue_test.go b/libcontainerd/queue/queue_test.go
new file mode 100644
index 0000000000000..66381bf8e8d6b
--- /dev/null
+++ b/libcontainerd/queue/queue_test.go
@@ -0,0 +1,31 @@
+package queue // import "github.com/docker/docker/libcontainerd/queue"
+
+import (
+	"testing"
+	"time"
+
+	"gotest.tools/v3/assert"
+)
+
+func TestSerialization(t *testing.T) {
+	var (
+		q             Queue
+		serialization = 1
+	)
+
+	q.Append("aaa", func() {
+		// simulate a long time task
+		time.Sleep(10 * time.Millisecond)
+		assert.Equal(t, serialization, 1)
+		serialization = 2
+	})
+	q.Append("aaa", func() {
+		assert.Equal(t, serialization, 2)
+		serialization = 3
+	})
+	q.Append("aaa", func() {
+		assert.Equal(t, serialization, 3)
+		serialization = 4
+	})
+	time.Sleep(20 * time.Millisecond)
+}
diff --git a/libcontainerd/queue_test.go b/libcontainerd/queue_test.go
deleted file mode 100644
index e13afca89a25a..0000000000000
--- a/libcontainerd/queue_test.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import (
-	"testing"
-	"time"
-
-	"gotest.tools/assert"
-)
-
-func TestSerialization(t *testing.T) {
-	var (
-		q             queue
-		serialization = 1
-	)
-
-	q.append("aaa", func() {
-		//simulate a long time task
-		time.Sleep(10 * time.Millisecond)
-		assert.Equal(t, serialization, 1)
-		serialization = 2
-	})
-	q.append("aaa", func() {
-		assert.Equal(t, serialization, 2)
-		serialization = 3
-	})
-	q.append("aaa", func() {
-		assert.Equal(t, serialization, 3)
-		serialization = 4
-	})
-	time.Sleep(20 * time.Millisecond)
-}
diff --git a/libcontainerd/remote/client.go b/libcontainerd/remote/client.go
new file mode 100644
index 0000000000000..f82911884e5b4
--- /dev/null
+++ b/libcontainerd/remote/client.go
@@ -0,0 +1,935 @@
+package remote // import "github.com/docker/docker/libcontainerd/remote"
+
+import (
+	"context"
+	"encoding/json"
+	"io"
+	"os"
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"strings"
+	"sync"
+	"syscall"
+	"time"
+
+	"github.com/containerd/containerd"
+	apievents "github.com/containerd/containerd/api/events"
+	"github.com/containerd/containerd/api/types"
+	"github.com/containerd/containerd/archive"
+	"github.com/containerd/containerd/cio"
+	"github.com/containerd/containerd/content"
+	containerderrors "github.com/containerd/containerd/errdefs"
+	"github.com/containerd/containerd/events"
+	"github.com/containerd/containerd/images"
+	"github.com/containerd/containerd/runtime/linux/runctypes"
+	v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
+	"github.com/containerd/typeurl"
+	"github.com/docker/docker/errdefs"
+	"github.com/docker/docker/libcontainerd/queue"
+	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
+	"github.com/docker/docker/pkg/ioutils"
+	v1 "github.com/opencontainers/image-spec/specs-go/v1"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+// DockerContainerBundlePath is the label key pointing to the container's bundle path
+const DockerContainerBundlePath = "com.docker/engine.bundle.path"
+
+type client struct {
+	client   *containerd.Client
+	stateDir string
+	logger   *logrus.Entry
+	ns       string
+
+	backend         libcontainerdtypes.Backend
+	eventQ          queue.Queue
+	oomMu           sync.Mutex
+	oom             map[string]bool
+	v2runcoptionsMu sync.Mutex
+	// v2runcoptions is used for copying options specified on Create() to Start()
+	v2runcoptions map[string]v2runcoptions.Options
+}
+
+// NewClient creates a new libcontainerd client from a containerd client
+func NewClient(ctx context.Context, cli *containerd.Client, stateDir, ns string, b libcontainerdtypes.Backend) (libcontainerdtypes.Client, error) {
+	c := &client{
+		client:        cli,
+		stateDir:      stateDir,
+		logger:        logrus.WithField("module", "libcontainerd").WithField("namespace", ns),
+		ns:            ns,
+		backend:       b,
+		oom:           make(map[string]bool),
+		v2runcoptions: make(map[string]v2runcoptions.Options),
+	}
+
+	go c.processEventStream(ctx, ns)
+
+	return c, nil
+}
+
+func (c *client) Version(ctx context.Context) (containerd.Version, error) {
+	return c.client.Version(ctx)
+}
+
+// Restore loads the containerd container.
+// It should not be called concurrently with any other operation for the given ID.
+func (c *client) Restore(ctx context.Context, id string, attachStdio libcontainerdtypes.StdioCallback) (alive bool, pid int, p libcontainerdtypes.Process, err error) {
+	var dio *cio.DirectIO
+	defer func() {
+		if err != nil && dio != nil {
+			dio.Cancel()
+			dio.Close()
+		}
+		err = wrapError(err)
+	}()
+
+	ctr, err := c.client.LoadContainer(ctx, id)
+	if err != nil {
+		return false, -1, nil, errors.WithStack(wrapError(err))
+	}
+
+	attachIO := func(fifos *cio.FIFOSet) (cio.IO, error) {
+		// dio must be assigned to the previously defined dio for the defer above
+		// to handle cleanup
+		dio, err = c.newDirectIO(ctx, fifos)
+		if err != nil {
+			return nil, err
+		}
+		return attachStdio(dio)
+	}
+	t, err := ctr.Task(ctx, attachIO)
+	if err != nil && !containerderrors.IsNotFound(err) {
+		return false, -1, nil, errors.Wrap(wrapError(err), "error getting containerd task for container")
+	}
+
+	if t != nil {
+		s, err := t.Status(ctx)
+		if err != nil {
+			return false, -1, nil, errors.Wrap(wrapError(err), "error getting task status")
+		}
+		alive = s.Status != containerd.Stopped
+		pid = int(t.Pid())
+	}
+
+	c.logger.WithFields(logrus.Fields{
+		"container": id,
+		"alive":     alive,
+		"pid":       pid,
+	}).Debug("restored container")
+
+	return alive, pid, &restoredProcess{
+		p: t,
+	}, nil
+}
+
+func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error {
+	bdir := c.bundleDir(id)
+	c.logger.WithField("bundle", bdir).WithField("root", ociSpec.Root.Path).Debug("bundle dir created")
+
+	newOpts := []containerd.NewContainerOpts{
+		containerd.WithSpec(ociSpec),
+		containerd.WithRuntime(shim, runtimeOptions),
+		WithBundle(bdir, ociSpec),
+	}
+	opts = append(opts, newOpts...)
+
+	_, err := c.client.NewContainer(ctx, id, opts...)
+	if err != nil {
+		if containerderrors.IsAlreadyExists(err) {
+			return errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
+		}
+		return wrapError(err)
+	}
+	if x, ok := runtimeOptions.(*v2runcoptions.Options); ok {
+		c.v2runcoptionsMu.Lock()
+		c.v2runcoptions[id] = *x
+		c.v2runcoptionsMu.Unlock()
+	}
+	return nil
+}
+
+// Start create and start a task for the specified containerd id
+func (c *client) Start(ctx context.Context, id, checkpointDir string, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) {
+	ctr, err := c.getContainer(ctx, id)
+	if err != nil {
+		return -1, err
+	}
+	var (
+		cp             *types.Descriptor
+		t              containerd.Task
+		rio            cio.IO
+		stdinCloseSync = make(chan struct{})
+	)
+
+	if checkpointDir != "" {
+		// write checkpoint to the content store
+		tar := archive.Diff(ctx, "", checkpointDir)
+		cp, err = c.writeContent(ctx, images.MediaTypeContainerd1Checkpoint, checkpointDir, tar)
+		// remove the checkpoint when we're done
+		defer func() {
+			if cp != nil {
+				err := c.client.ContentStore().Delete(context.Background(), cp.Digest)
+				if err != nil {
+					c.logger.WithError(err).WithFields(logrus.Fields{
+						"ref":    checkpointDir,
+						"digest": cp.Digest,
+					}).Warnf("failed to delete temporary checkpoint entry")
+				}
+			}
+		}()
+		if err := tar.Close(); err != nil {
+			return -1, errors.Wrap(err, "failed to close checkpoint tar stream")
+		}
+		if err != nil {
+			return -1, errors.Wrapf(err, "failed to upload checkpoint to containerd")
+		}
+	}
+
+	spec, err := ctr.Spec(ctx)
+	if err != nil {
+		return -1, errors.Wrap(err, "failed to retrieve spec")
+	}
+	labels, err := ctr.Labels(ctx)
+	if err != nil {
+		return -1, errors.Wrap(err, "failed to retrieve labels")
+	}
+	bundle := labels[DockerContainerBundlePath]
+	uid, gid := getSpecUser(spec)
+
+	taskOpts := []containerd.NewTaskOpts{
+		func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error {
+			info.Checkpoint = cp
+			return nil
+		},
+	}
+
+	if runtime.GOOS != "windows" {
+		taskOpts = append(taskOpts, func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error {
+			c.v2runcoptionsMu.Lock()
+			opts, ok := c.v2runcoptions[id]
+			c.v2runcoptionsMu.Unlock()
+			if ok {
+				opts.IoUid = uint32(uid)
+				opts.IoGid = uint32(gid)
+				info.Options = &opts
+			} else {
+				info.Options = &runctypes.CreateOptions{
+					IoUid:       uint32(uid),
+					IoGid:       uint32(gid),
+					NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
+				}
+			}
+			return nil
+		})
+	} else {
+		taskOpts = append(taskOpts, withLogLevel(c.logger.Level))
+	}
+
+	t, err = ctr.NewTask(ctx,
+		func(id string) (cio.IO, error) {
+			fifos := newFIFOSet(bundle, libcontainerdtypes.InitProcessName, withStdin, spec.Process.Terminal)
+
+			rio, err = c.createIO(fifos, id, libcontainerdtypes.InitProcessName, stdinCloseSync, attachStdio)
+			return rio, err
+		},
+		taskOpts...,
+	)
+	if err != nil {
+		close(stdinCloseSync)
+		if rio != nil {
+			rio.Cancel()
+			rio.Close()
+		}
+		return -1, wrapError(err)
+	}
+
+	// Signal c.createIO that it can call CloseIO
+	close(stdinCloseSync)
+
+	if err := t.Start(ctx); err != nil {
+		if _, err := t.Delete(ctx); err != nil {
+			c.logger.WithError(err).WithField("container", id).
+				Error("failed to delete task after fail start")
+		}
+		return -1, wrapError(err)
+	}
+
+	return int(t.Pid()), nil
+}
+
+// Exec creates exec process.
+//
+// The containerd client calls Exec to register the exec config in the shim side.
+// When the client calls Start, the shim will create stdin fifo if needs. But
+// for the container main process, the stdin fifo will be created in Create not
+// the Start call. stdinCloseSync channel should be closed after Start exec
+// process.
+func (c *client) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio libcontainerdtypes.StdioCallback) (int, error) {
+	ctr, err := c.getContainer(ctx, containerID)
+	if err != nil {
+		return -1, err
+	}
+	t, err := ctr.Task(ctx, nil)
+	if err != nil {
+		if containerderrors.IsNotFound(err) {
+			return -1, errors.WithStack(errdefs.InvalidParameter(errors.New("container is not running")))
+		}
+		return -1, wrapError(err)
+	}
+
+	var (
+		p              containerd.Process
+		rio            cio.IO
+		stdinCloseSync = make(chan struct{})
+	)
+
+	labels, err := ctr.Labels(ctx)
+	if err != nil {
+		return -1, wrapError(err)
+	}
+
+	fifos := newFIFOSet(labels[DockerContainerBundlePath], processID, withStdin, spec.Terminal)
+
+	defer func() {
+		if err != nil {
+			if rio != nil {
+				rio.Cancel()
+				rio.Close()
+			}
+		}
+	}()
+
+	p, err = t.Exec(ctx, processID, spec, func(id string) (cio.IO, error) {
+		rio, err = c.createIO(fifos, containerID, processID, stdinCloseSync, attachStdio)
+		return rio, err
+	})
+	if err != nil {
+		close(stdinCloseSync)
+		if containerderrors.IsAlreadyExists(err) {
+			return -1, errors.WithStack(errdefs.Conflict(errors.New("id already in use")))
+		}
+		return -1, wrapError(err)
+	}
+
+	// Signal c.createIO that it can call CloseIO
+	//
+	// the stdin of exec process will be created after p.Start in containerd
+	defer close(stdinCloseSync)
+
+	if err = p.Start(ctx); err != nil {
+		// use new context for cleanup because old one may be cancelled by user, but leave a timeout to make sure
+		// we are not waiting forever if containerd is unresponsive or to work around fifo cancelling issues in
+		// older containerd-shim
+		ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
+		defer cancel()
+		p.Delete(ctx)
+		return -1, wrapError(err)
+	}
+	return int(p.Pid()), nil
+}
+
+func (c *client) SignalProcess(ctx context.Context, containerID, processID string, signal int) error {
+	p, err := c.getProcess(ctx, containerID, processID)
+	if err != nil {
+		return err
+	}
+	return wrapError(p.Kill(ctx, syscall.Signal(signal)))
+}
+
+func (c *client) ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error {
+	p, err := c.getProcess(ctx, containerID, processID)
+	if err != nil {
+		return err
+	}
+
+	return p.Resize(ctx, uint32(width), uint32(height))
+}
+
+func (c *client) CloseStdin(ctx context.Context, containerID, processID string) error {
+	p, err := c.getProcess(ctx, containerID, processID)
+	if err != nil {
+		return err
+	}
+
+	return p.CloseIO(ctx, containerd.WithStdinCloser)
+}
+
+func (c *client) Pause(ctx context.Context, containerID string) error {
+	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return err
+	}
+
+	return wrapError(p.(containerd.Task).Pause(ctx))
+}
+
+func (c *client) Resume(ctx context.Context, containerID string) error {
+	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return err
+	}
+
+	return p.(containerd.Task).Resume(ctx)
+}
+
+func (c *client) Stats(ctx context.Context, containerID string) (*libcontainerdtypes.Stats, error) {
+	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return nil, err
+	}
+
+	m, err := p.(containerd.Task).Metrics(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	v, err := typeurl.UnmarshalAny(m.Data)
+	if err != nil {
+		return nil, err
+	}
+	return libcontainerdtypes.InterfaceToStats(m.Timestamp, v), nil
+}
+
+func (c *client) ListPids(ctx context.Context, containerID string) ([]uint32, error) {
+	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return nil, err
+	}
+
+	pis, err := p.(containerd.Task).Pids(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	var pids []uint32
+	for _, i := range pis {
+		pids = append(pids, i.Pid)
+	}
+
+	return pids, nil
+}
+
+func (c *client) Summary(ctx context.Context, containerID string) ([]libcontainerdtypes.Summary, error) {
+	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return nil, err
+	}
+
+	pis, err := p.(containerd.Task).Pids(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	var infos []libcontainerdtypes.Summary
+	for _, pi := range pis {
+		i, err := typeurl.UnmarshalAny(pi.Info)
+		if err != nil {
+			return nil, errors.Wrap(err, "unable to decode process details")
+		}
+		s, err := summaryFromInterface(i)
+		if err != nil {
+			return nil, err
+		}
+		infos = append(infos, *s)
+	}
+
+	return infos, nil
+}
+
+type restoredProcess struct {
+	p containerd.Process
+}
+
+func (p *restoredProcess) Delete(ctx context.Context) (uint32, time.Time, error) {
+	if p.p == nil {
+		return 255, time.Now(), nil
+	}
+	status, err := p.p.Delete(ctx)
+	if err != nil {
+		return 255, time.Now(), nil
+	}
+	return status.ExitCode(), status.ExitTime(), nil
+}
+
+func (c *client) DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) {
+	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return 255, time.Now(), nil
+	}
+
+	status, err := p.Delete(ctx)
+	if err != nil {
+		return 255, time.Now(), nil
+	}
+	return status.ExitCode(), status.ExitTime(), nil
+}
+
+func (c *client) Delete(ctx context.Context, containerID string) error {
+	ctr, err := c.getContainer(ctx, containerID)
+	if err != nil {
+		return err
+	}
+	labels, err := ctr.Labels(ctx)
+	if err != nil {
+		return err
+	}
+	bundle := labels[DockerContainerBundlePath]
+	if err := ctr.Delete(ctx); err != nil {
+		return wrapError(err)
+	}
+	c.oomMu.Lock()
+	delete(c.oom, containerID)
+	c.oomMu.Unlock()
+	c.v2runcoptionsMu.Lock()
+	delete(c.v2runcoptions, containerID)
+	c.v2runcoptionsMu.Unlock()
+	if os.Getenv("LIBCONTAINERD_NOCLEAN") != "1" {
+		if err := os.RemoveAll(bundle); err != nil {
+			c.logger.WithError(err).WithFields(logrus.Fields{
+				"container": containerID,
+				"bundle":    bundle,
+			}).Error("failed to remove state dir")
+		}
+	}
+	return nil
+}
+
+func (c *client) Status(ctx context.Context, containerID string) (containerd.ProcessStatus, error) {
+	t, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return containerd.Unknown, err
+	}
+	s, err := t.Status(ctx)
+	if err != nil {
+		return containerd.Unknown, wrapError(err)
+	}
+	return s.Status, nil
+}
+
+func (c *client) getCheckpointOptions(id string, exit bool) containerd.CheckpointTaskOpts {
+	return func(r *containerd.CheckpointTaskInfo) error {
+		if r.Options == nil {
+			c.v2runcoptionsMu.Lock()
+			_, isV2 := c.v2runcoptions[id]
+			c.v2runcoptionsMu.Unlock()
+
+			if isV2 {
+				r.Options = &v2runcoptions.CheckpointOptions{Exit: exit}
+			} else {
+				r.Options = &runctypes.CheckpointOptions{Exit: exit}
+			}
+			return nil
+		}
+
+		switch opts := r.Options.(type) {
+		case *v2runcoptions.CheckpointOptions:
+			opts.Exit = exit
+		case *runctypes.CheckpointOptions:
+			opts.Exit = exit
+		}
+
+		return nil
+	}
+}
+
+func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error {
+	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return err
+	}
+
+	opts := []containerd.CheckpointTaskOpts{c.getCheckpointOptions(containerID, exit)}
+	img, err := p.(containerd.Task).Checkpoint(ctx, opts...)
+	if err != nil {
+		return wrapError(err)
+	}
+	// Whatever happens, delete the checkpoint from containerd
+	defer func() {
+		err := c.client.ImageService().Delete(context.Background(), img.Name())
+		if err != nil {
+			c.logger.WithError(err).WithField("digest", img.Target().Digest).
+				Warnf("failed to delete checkpoint image")
+		}
+	}()
+
+	b, err := content.ReadBlob(ctx, c.client.ContentStore(), img.Target())
+	if err != nil {
+		return errdefs.System(errors.Wrapf(err, "failed to retrieve checkpoint data"))
+	}
+	var index v1.Index
+	if err := json.Unmarshal(b, &index); err != nil {
+		return errdefs.System(errors.Wrapf(err, "failed to decode checkpoint data"))
+	}
+
+	var cpDesc *v1.Descriptor
+	for _, m := range index.Manifests {
+		m := m
+		if m.MediaType == images.MediaTypeContainerd1Checkpoint {
+			cpDesc = &m // nolint:gosec
+			break
+		}
+	}
+	if cpDesc == nil {
+		return errdefs.System(errors.Wrapf(err, "invalid checkpoint"))
+	}
+
+	rat, err := c.client.ContentStore().ReaderAt(ctx, *cpDesc)
+	if err != nil {
+		return errdefs.System(errors.Wrapf(err, "failed to get checkpoint reader"))
+	}
+	defer rat.Close()
+	_, err = archive.Apply(ctx, checkpointDir, content.NewReader(rat))
+	if err != nil {
+		return errdefs.System(errors.Wrapf(err, "failed to read checkpoint reader"))
+	}
+
+	return err
+}
+
+func (c *client) getContainer(ctx context.Context, id string) (containerd.Container, error) {
+	ctr, err := c.client.LoadContainer(ctx, id)
+	if err != nil {
+		if containerderrors.IsNotFound(err) {
+			return nil, errors.WithStack(errdefs.NotFound(errors.New("no such container")))
+		}
+		return nil, wrapError(err)
+	}
+	return ctr, nil
+}
+
+func (c *client) getProcess(ctx context.Context, containerID, processID string) (containerd.Process, error) {
+	ctr, err := c.getContainer(ctx, containerID)
+	if err != nil {
+		return nil, err
+	}
+	t, err := ctr.Task(ctx, nil)
+	if err != nil {
+		if containerderrors.IsNotFound(err) {
+			return nil, errors.WithStack(errdefs.NotFound(errors.New("container is not running")))
+		}
+		return nil, wrapError(err)
+	}
+	if processID == libcontainerdtypes.InitProcessName {
+		return t, nil
+	}
+	p, err := t.LoadProcess(ctx, processID, nil)
+	if err != nil {
+		if containerderrors.IsNotFound(err) {
+			return nil, errors.WithStack(errdefs.NotFound(errors.New("no such exec")))
+		}
+		return nil, wrapError(err)
+	}
+	return p, nil
+}
+
+// createIO creates the io to be used by a process
+// This needs to get a pointer to interface as upon closure the process may not have yet been registered
+func (c *client) createIO(fifos *cio.FIFOSet, containerID, processID string, stdinCloseSync chan struct{}, attachStdio libcontainerdtypes.StdioCallback) (cio.IO, error) {
+	var (
+		io  *cio.DirectIO
+		err error
+	)
+	io, err = c.newDirectIO(context.Background(), fifos)
+	if err != nil {
+		return nil, err
+	}
+
+	if io.Stdin != nil {
+		var (
+			err       error
+			stdinOnce sync.Once
+		)
+		pipe := io.Stdin
+		io.Stdin = ioutils.NewWriteCloserWrapper(pipe, func() error {
+			stdinOnce.Do(func() {
+				err = pipe.Close()
+				// Do the rest in a new routine to avoid a deadlock if the
+				// Exec/Start call failed.
+				go func() {
+					<-stdinCloseSync
+					p, err := c.getProcess(context.Background(), containerID, processID)
+					if err == nil {
+						err = p.CloseIO(context.Background(), containerd.WithStdinCloser)
+						if err != nil && strings.Contains(err.Error(), "transport is closing") {
+							err = nil
+						}
+					}
+				}()
+			})
+			return err
+		})
+	}
+
+	rio, err := attachStdio(io)
+	if err != nil {
+		io.Cancel()
+		io.Close()
+	}
+	return rio, err
+}
+
+func (c *client) processEvent(ctx context.Context, et libcontainerdtypes.EventType, ei libcontainerdtypes.EventInfo) {
+	c.eventQ.Append(ei.ContainerID, func() {
+		err := c.backend.ProcessEvent(ei.ContainerID, et, ei)
+		if err != nil {
+			c.logger.WithError(err).WithFields(logrus.Fields{
+				"container":  ei.ContainerID,
+				"event":      et,
+				"event-info": ei,
+			}).Error("failed to process event")
+		}
+
+		if et == libcontainerdtypes.EventExit && ei.ProcessID != ei.ContainerID {
+			p, err := c.getProcess(ctx, ei.ContainerID, ei.ProcessID)
+			if err != nil {
+
+				c.logger.WithError(errors.New("no such process")).
+					WithFields(logrus.Fields{
+						"error":     err,
+						"container": ei.ContainerID,
+						"process":   ei.ProcessID,
+					}).Error("exit event")
+				return
+			}
+
+			ctr, err := c.getContainer(ctx, ei.ContainerID)
+			if err != nil {
+				c.logger.WithFields(logrus.Fields{
+					"container": ei.ContainerID,
+					"error":     err,
+				}).Error("failed to find container")
+			} else {
+				labels, err := ctr.Labels(ctx)
+				if err != nil {
+					c.logger.WithFields(logrus.Fields{
+						"container": ei.ContainerID,
+						"error":     err,
+					}).Error("failed to get container labels")
+					return
+				}
+				newFIFOSet(labels[DockerContainerBundlePath], ei.ProcessID, true, false).Close()
+			}
+			_, err = p.Delete(context.Background())
+			if err != nil {
+				c.logger.WithError(err).WithFields(logrus.Fields{
+					"container": ei.ContainerID,
+					"process":   ei.ProcessID,
+				}).Warn("failed to delete process")
+			}
+		}
+	})
+}
+
+func (c *client) waitServe(ctx context.Context) bool {
+	t := 100 * time.Millisecond
+	delay := time.NewTimer(t)
+	if !delay.Stop() {
+		<-delay.C
+	}
+	defer delay.Stop()
+
+	// `IsServing` will actually block until the service is ready.
+	// However it can return early, so we'll loop with a delay to handle it.
+	for {
+		serving, err := c.client.IsServing(ctx)
+		if err != nil {
+			if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
+				return false
+			}
+			logrus.WithError(err).Warn("Error while testing if containerd API is ready")
+		}
+
+		if serving {
+			return true
+		}
+
+		delay.Reset(t)
+		select {
+		case <-ctx.Done():
+			return false
+		case <-delay.C:
+		}
+	}
+}
+
+func (c *client) processEventStream(ctx context.Context, ns string) {
+	var (
+		err error
+		ev  *events.Envelope
+		et  libcontainerdtypes.EventType
+		ei  libcontainerdtypes.EventInfo
+	)
+
+	// Create a new context specifically for this subscription.
+	// The context must be cancelled to cancel the subscription.
+	// In cases where we have to restart event stream processing,
+	//   we'll need the original context b/c this one will be cancelled
+	subCtx, cancel := context.WithCancel(ctx)
+	defer cancel()
+
+	// Filter on both namespace *and* topic. To create an "and" filter,
+	// this must be a single, comma-separated string
+	eventStream, errC := c.client.EventService().Subscribe(subCtx, "namespace=="+ns+",topic~=|^/tasks/|")
+
+	c.logger.Debug("processing event stream")
+
+	for {
+		var oomKilled bool
+		select {
+		case err = <-errC:
+			if err != nil {
+				errStatus, ok := status.FromError(err)
+				if !ok || errStatus.Code() != codes.Canceled {
+					c.logger.WithError(err).Error("Failed to get event")
+					c.logger.Info("Waiting for containerd to be ready to restart event processing")
+					if c.waitServe(ctx) {
+						go c.processEventStream(ctx, ns)
+						return
+					}
+				}
+				c.logger.WithError(ctx.Err()).Info("stopping event stream following graceful shutdown")
+			}
+			return
+		case ev = <-eventStream:
+			if ev.Event == nil {
+				c.logger.WithField("event", ev).Warn("invalid event")
+				continue
+			}
+
+			v, err := typeurl.UnmarshalAny(ev.Event)
+			if err != nil {
+				c.logger.WithError(err).WithField("event", ev).Warn("failed to unmarshal event")
+				continue
+			}
+
+			c.logger.WithField("topic", ev.Topic).Debug("event")
+
+			switch t := v.(type) {
+			case *apievents.TaskCreate:
+				et = libcontainerdtypes.EventCreate
+				ei = libcontainerdtypes.EventInfo{
+					ContainerID: t.ContainerID,
+					ProcessID:   t.ContainerID,
+					Pid:         t.Pid,
+				}
+			case *apievents.TaskStart:
+				et = libcontainerdtypes.EventStart
+				ei = libcontainerdtypes.EventInfo{
+					ContainerID: t.ContainerID,
+					ProcessID:   t.ContainerID,
+					Pid:         t.Pid,
+				}
+			case *apievents.TaskExit:
+				et = libcontainerdtypes.EventExit
+				ei = libcontainerdtypes.EventInfo{
+					ContainerID: t.ContainerID,
+					ProcessID:   t.ID,
+					Pid:         t.Pid,
+					ExitCode:    t.ExitStatus,
+					ExitedAt:    t.ExitedAt,
+				}
+			case *apievents.TaskOOM:
+				et = libcontainerdtypes.EventOOM
+				ei = libcontainerdtypes.EventInfo{
+					ContainerID: t.ContainerID,
+					OOMKilled:   true,
+				}
+				oomKilled = true
+			case *apievents.TaskExecAdded:
+				et = libcontainerdtypes.EventExecAdded
+				ei = libcontainerdtypes.EventInfo{
+					ContainerID: t.ContainerID,
+					ProcessID:   t.ExecID,
+				}
+			case *apievents.TaskExecStarted:
+				et = libcontainerdtypes.EventExecStarted
+				ei = libcontainerdtypes.EventInfo{
+					ContainerID: t.ContainerID,
+					ProcessID:   t.ExecID,
+					Pid:         t.Pid,
+				}
+			case *apievents.TaskPaused:
+				et = libcontainerdtypes.EventPaused
+				ei = libcontainerdtypes.EventInfo{
+					ContainerID: t.ContainerID,
+				}
+			case *apievents.TaskResumed:
+				et = libcontainerdtypes.EventResumed
+				ei = libcontainerdtypes.EventInfo{
+					ContainerID: t.ContainerID,
+				}
+			case *apievents.TaskDelete:
+				c.logger.WithFields(logrus.Fields{
+					"topic":     ev.Topic,
+					"type":      reflect.TypeOf(t),
+					"container": t.ContainerID},
+				).Info("ignoring event")
+				continue
+			default:
+				c.logger.WithFields(logrus.Fields{
+					"topic": ev.Topic,
+					"type":  reflect.TypeOf(t)},
+				).Info("ignoring event")
+				continue
+			}
+
+			c.oomMu.Lock()
+			if oomKilled {
+				c.oom[ei.ContainerID] = true
+			}
+			ei.OOMKilled = c.oom[ei.ContainerID]
+			c.oomMu.Unlock()
+
+			c.processEvent(ctx, et, ei)
+		}
+	}
+}
+
+func (c *client) writeContent(ctx context.Context, mediaType, ref string, r io.Reader) (*types.Descriptor, error) {
+	writer, err := c.client.ContentStore().Writer(ctx, content.WithRef(ref))
+	if err != nil {
+		return nil, err
+	}
+	defer writer.Close()
+	size, err := io.Copy(writer, r)
+	if err != nil {
+		return nil, err
+	}
+	labels := map[string]string{
+		"containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339),
+	}
+	if err := writer.Commit(ctx, 0, "", content.WithLabels(labels)); err != nil {
+		return nil, err
+	}
+	return &types.Descriptor{
+		MediaType: mediaType,
+		Digest:    writer.Digest(),
+		Size_:     size,
+	}, nil
+}
+
+func (c *client) bundleDir(id string) string {
+	return filepath.Join(c.stateDir, id)
+}
+
+func wrapError(err error) error {
+	switch {
+	case err == nil:
+		return nil
+	case containerderrors.IsNotFound(err):
+		return errdefs.NotFound(err)
+	}
+
+	msg := err.Error()
+	for _, s := range []string{"container does not exist", "not found", "no such container"} {
+		if strings.Contains(msg, s) {
+			return errdefs.NotFound(err)
+		}
+	}
+	return err
+}
diff --git a/libcontainerd/remote/client_io_windows.go b/libcontainerd/remote/client_io_windows.go
new file mode 100644
index 0000000000000..b437fb68986f1
--- /dev/null
+++ b/libcontainerd/remote/client_io_windows.go
@@ -0,0 +1,161 @@
+package remote // import "github.com/docker/docker/libcontainerd/remote"
+
+import (
+	"io"
+	"net"
+	"sync"
+
+	winio "github.com/Microsoft/go-winio"
+	"github.com/containerd/containerd/cio"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+	//	"golang.org/x/net/context"
+)
+
+type delayedConnection struct {
+	l    net.Listener
+	con  net.Conn
+	wg   sync.WaitGroup
+	once sync.Once
+}
+
+func (dc *delayedConnection) Write(p []byte) (int, error) {
+	dc.wg.Wait()
+	if dc.con != nil {
+		return dc.con.Write(p)
+	}
+	return 0, errors.New("use of closed network connection")
+}
+
+func (dc *delayedConnection) Read(p []byte) (int, error) {
+	dc.wg.Wait()
+	if dc.con != nil {
+		return dc.con.Read(p)
+	}
+	return 0, errors.New("use of closed network connection")
+}
+
+func (dc *delayedConnection) unblockConnectionWaiters() {
+	defer dc.once.Do(func() {
+		dc.wg.Done()
+	})
+}
+
+func (dc *delayedConnection) Close() error {
+	dc.l.Close()
+	if dc.con != nil {
+		return dc.con.Close()
+	}
+	dc.unblockConnectionWaiters()
+	return nil
+}
+
+type stdioPipes struct {
+	stdin  io.WriteCloser
+	stdout io.ReadCloser
+	stderr io.ReadCloser
+}
+
+// newStdioPipes creates actual fifos for stdio.
+func (c *client) newStdioPipes(fifos *cio.FIFOSet) (_ *stdioPipes, err error) {
+	p := &stdioPipes{}
+	if fifos.Stdin != "" {
+		c.logger.WithFields(logrus.Fields{"stdin": fifos.Stdin}).Debug("listen")
+		l, err := winio.ListenPipe(fifos.Stdin, nil)
+		if err != nil {
+			return nil, errors.Wrapf(err, "failed to create stdin pipe %s", fifos.Stdin)
+		}
+		dc := &delayedConnection{
+			l: l,
+		}
+		dc.wg.Add(1)
+		defer func() {
+			if err != nil {
+				dc.Close()
+			}
+		}()
+		p.stdin = dc
+
+		go func() {
+			c.logger.WithFields(logrus.Fields{"stdin": fifos.Stdin}).Debug("accept")
+			conn, err := l.Accept()
+			if err != nil {
+				dc.Close()
+				if err != winio.ErrPipeListenerClosed {
+					c.logger.WithError(err).Errorf("failed to accept stdin connection on %s", fifos.Stdin)
+				}
+				return
+			}
+			c.logger.WithFields(logrus.Fields{"stdin": fifos.Stdin}).Debug("connected")
+			dc.con = conn
+			dc.unblockConnectionWaiters()
+		}()
+	}
+
+	if fifos.Stdout != "" {
+		c.logger.WithFields(logrus.Fields{"stdout": fifos.Stdout}).Debug("listen")
+		l, err := winio.ListenPipe(fifos.Stdout, nil)
+		if err != nil {
+			return nil, errors.Wrapf(err, "failed to create stdout pipe %s", fifos.Stdout)
+		}
+		dc := &delayedConnection{
+			l: l,
+		}
+		dc.wg.Add(1)
+		defer func() {
+			if err != nil {
+				dc.Close()
+			}
+		}()
+		p.stdout = dc
+
+		go func() {
+			c.logger.WithFields(logrus.Fields{"stdout": fifos.Stdout}).Debug("accept")
+			conn, err := l.Accept()
+			if err != nil {
+				dc.Close()
+				if err != winio.ErrPipeListenerClosed {
+					c.logger.WithError(err).Errorf("failed to accept stdout connection on %s", fifos.Stdout)
+				}
+				return
+			}
+			c.logger.WithFields(logrus.Fields{"stdout": fifos.Stdout}).Debug("connected")
+			dc.con = conn
+			dc.unblockConnectionWaiters()
+		}()
+	}
+
+	if fifos.Stderr != "" {
+		c.logger.WithFields(logrus.Fields{"stderr": fifos.Stderr}).Debug("listen")
+		l, err := winio.ListenPipe(fifos.Stderr, nil)
+		if err != nil {
+			return nil, errors.Wrapf(err, "failed to create stderr pipe %s", fifos.Stderr)
+		}
+		dc := &delayedConnection{
+			l: l,
+		}
+		dc.wg.Add(1)
+		defer func() {
+			if err != nil {
+				dc.Close()
+			}
+		}()
+		p.stderr = dc
+
+		go func() {
+			c.logger.WithFields(logrus.Fields{"stderr": fifos.Stderr}).Debug("accept")
+			conn, err := l.Accept()
+			if err != nil {
+				dc.Close()
+				if err != winio.ErrPipeListenerClosed {
+					c.logger.WithError(err).Errorf("failed to accept stderr connection on %s", fifos.Stderr)
+				}
+				return
+			}
+			c.logger.WithFields(logrus.Fields{"stderr": fifos.Stderr}).Debug("connected")
+			dc.con = conn
+			dc.unblockConnectionWaiters()
+		}()
+	}
+	return p, nil
+}
diff --git a/libcontainerd/remote/client_linux.go b/libcontainerd/remote/client_linux.go
new file mode 100644
index 0000000000000..e45d140b2f079
--- /dev/null
+++ b/libcontainerd/remote/client_linux.go
@@ -0,0 +1,128 @@
+package remote // import "github.com/docker/docker/libcontainerd/remote"
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/containerd/containerd"
+	"github.com/containerd/containerd/cio"
+	"github.com/containerd/containerd/containers"
+	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
+	"github.com/docker/docker/pkg/idtools"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/sirupsen/logrus"
+)
+
+func summaryFromInterface(i interface{}) (*libcontainerdtypes.Summary, error) {
+	return &libcontainerdtypes.Summary{}, nil
+}
+
+func (c *client) UpdateResources(ctx context.Context, containerID string, resources *libcontainerdtypes.Resources) error {
+	p, err := c.getProcess(ctx, containerID, libcontainerdtypes.InitProcessName)
+	if err != nil {
+		return err
+	}
+
+	// go doesn't like the alias in 1.8, this means this need to be
+	// platform specific
+	return p.(containerd.Task).Update(ctx, containerd.WithResources((*specs.LinuxResources)(resources)))
+}
+
+func hostIDFromMap(id uint32, mp []specs.LinuxIDMapping) int {
+	for _, m := range mp {
+		if id >= m.ContainerID && id <= m.ContainerID+m.Size-1 {
+			return int(m.HostID + id - m.ContainerID)
+		}
+	}
+	return 0
+}
+
+func getSpecUser(ociSpec *specs.Spec) (int, int) {
+	var (
+		uid int
+		gid int
+	)
+
+	for _, ns := range ociSpec.Linux.Namespaces {
+		if ns.Type == specs.UserNamespace {
+			uid = hostIDFromMap(0, ociSpec.Linux.UIDMappings)
+			gid = hostIDFromMap(0, ociSpec.Linux.GIDMappings)
+			break
+		}
+	}
+
+	return uid, gid
+}
+
+// WithBundle creates the bundle for the container
+func WithBundle(bundleDir string, ociSpec *specs.Spec) containerd.NewContainerOpts {
+	return func(ctx context.Context, client *containerd.Client, c *containers.Container) error {
+		if c.Labels == nil {
+			c.Labels = make(map[string]string)
+		}
+		uid, gid := getSpecUser(ociSpec)
+		if uid == 0 && gid == 0 {
+			c.Labels[DockerContainerBundlePath] = bundleDir
+			return idtools.MkdirAllAndChownNew(bundleDir, 0755, idtools.Identity{UID: 0, GID: 0})
+		}
+
+		p := string(filepath.Separator)
+		components := strings.Split(bundleDir, string(filepath.Separator))
+		for _, d := range components[1:] {
+			p = filepath.Join(p, d)
+			fi, err := os.Stat(p)
+			if err != nil && !os.IsNotExist(err) {
+				return err
+			}
+			if os.IsNotExist(err) || fi.Mode()&1 == 0 {
+				p = fmt.Sprintf("%s.%d.%d", p, uid, gid)
+				if err := idtools.MkdirAndChown(p, 0700, idtools.Identity{UID: uid, GID: gid}); err != nil && !os.IsExist(err) {
+					return err
+				}
+			}
+		}
+		if c.Labels == nil {
+			c.Labels = make(map[string]string)
+		}
+		c.Labels[DockerContainerBundlePath] = p
+		return nil
+	}
+}
+
+func withLogLevel(_ logrus.Level) containerd.NewTaskOpts {
+	panic("Not implemented")
+}
+
+func newFIFOSet(bundleDir, processID string, withStdin, withTerminal bool) *cio.FIFOSet {
+	config := cio.Config{
+		Terminal: withTerminal,
+		Stdout:   filepath.Join(bundleDir, processID+"-stdout"),
+	}
+	paths := []string{config.Stdout}
+
+	if withStdin {
+		config.Stdin = filepath.Join(bundleDir, processID+"-stdin")
+		paths = append(paths, config.Stdin)
+	}
+	if !withTerminal {
+		config.Stderr = filepath.Join(bundleDir, processID+"-stderr")
+		paths = append(paths, config.Stderr)
+	}
+	closer := func() error {
+		for _, path := range paths {
+			if err := os.RemoveAll(path); err != nil {
+				logrus.Warnf("libcontainerd: failed to remove fifo %v: %v", path, err)
+			}
+		}
+		return nil
+	}
+
+	return cio.NewFIFOSet(config, closer)
+}
+
+func (c *client) newDirectIO(ctx context.Context, fifos *cio.FIFOSet) (*cio.DirectIO, error) {
+	return cio.NewDirectIO(ctx, fifos)
+}
diff --git a/libcontainerd/remote/client_windows.go b/libcontainerd/remote/client_windows.go
new file mode 100644
index 0000000000000..87fb0e119d064
--- /dev/null
+++ b/libcontainerd/remote/client_windows.go
@@ -0,0 +1,98 @@
+package remote // import "github.com/docker/docker/libcontainerd/remote"
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+
+	"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
+	"github.com/containerd/containerd"
+	"github.com/containerd/containerd/cio"
+	"github.com/containerd/containerd/containers"
+	libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+)
+
+func summaryFromInterface(i interface{}) (*libcontainerdtypes.Summary, error) {
+	switch pd := i.(type) {
+	case *options.ProcessDetails:
+		return &libcontainerdtypes.Summary{
+			ImageName:                    pd.ImageName,
+			CreatedAt:                    pd.CreatedAt,
+			KernelTime_100Ns:             pd.KernelTime_100Ns,
+			MemoryCommitBytes:            pd.MemoryCommitBytes,
+			MemoryWorkingSetPrivateBytes: pd.MemoryWorkingSetPrivateBytes,
+			MemoryWorkingSetSharedBytes:  pd.MemoryWorkingSetSharedBytes,
+			ProcessID:                    pd.ProcessID,
+			UserTime_100Ns:               pd.UserTime_100Ns,
+			ExecID:                       pd.ExecID,
+		}, nil
+	default:
+		return nil, errors.Errorf("Unknown process details type %T", pd)
+	}
+}
+
+// WithBundle creates the bundle for the container
+func WithBundle(bundleDir string, ociSpec *specs.Spec) containerd.NewContainerOpts {
+	return func(ctx context.Context, client *containerd.Client, c *containers.Container) error {
+		// TODO: (containerd) Determine if we need to use system.MkdirAllWithACL here
+		if c.Labels == nil {
+			c.Labels = make(map[string]string)
+		}
+		c.Labels[DockerContainerBundlePath] = bundleDir
+		return os.MkdirAll(bundleDir, 0755)
+	}
+}
+
+func withLogLevel(level logrus.Level) containerd.NewTaskOpts {
+	// Make sure we set the runhcs options to debug if we are at debug level.
+	return func(_ context.Context, _ *containerd.Client, info *containerd.TaskInfo) error {
+		if level == logrus.DebugLevel {
+			info.Options = &options.Options{Debug: true}
+		}
+		return nil
+	}
+}
+
+func pipeName(containerID, processID, name string) string {
+	return fmt.Sprintf(`\\.\pipe\containerd-%s-%s-%s`, containerID, processID, name)
+}
+
+func newFIFOSet(bundleDir, processID string, withStdin, withTerminal bool) *cio.FIFOSet {
+	containerID := filepath.Base(bundleDir)
+	config := cio.Config{
+		Terminal: withTerminal,
+		Stdout:   pipeName(containerID, processID, "stdout"),
+	}
+
+	if withStdin {
+		config.Stdin = pipeName(containerID, processID, "stdin")
+	}
+
+	if !config.Terminal {
+		config.Stderr = pipeName(containerID, processID, "stderr")
+	}
+
+	return cio.NewFIFOSet(config, nil)
+}
+
+func (c *client) newDirectIO(ctx context.Context, fifos *cio.FIFOSet) (*cio.DirectIO, error) {
+	pipes, err := c.newStdioPipes(fifos)
+	if err != nil {
+		return nil, err
+	}
+	return cio.NewDirectIOFromFIFOSet(ctx, pipes.stdin, pipes.stdout, pipes.stderr, fifos), nil
+}
+
+func (c *client) UpdateResources(ctx context.Context, containerID string, resources *libcontainerdtypes.Resources) error {
+	// TODO: (containerd): Not implemented, but don't error.
+	return nil
+}
+
+func getSpecUser(ociSpec *specs.Spec) (int, int) {
+	// TODO: (containerd): Not implemented, but don't error.
+	return 0, 0
+}
diff --git a/libcontainerd/supervisor/remote_daemon.go b/libcontainerd/supervisor/remote_daemon.go
index 01764068900ad..2f38f1505ccd6 100644
--- a/libcontainerd/supervisor/remote_daemon.go
+++ b/libcontainerd/supervisor/remote_daemon.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -13,10 +12,10 @@ import (
 	"sync"
 	"time"
 
-	"github.com/BurntSushi/toml"
 	"github.com/containerd/containerd"
-	"github.com/containerd/containerd/services/server"
+	"github.com/containerd/containerd/services/server/config"
 	"github.com/docker/docker/pkg/system"
+	"github.com/pelletier/go-toml"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
@@ -31,13 +30,11 @@ const (
 	pidFile                 = "containerd.pid"
 )
 
-type pluginConfigs struct {
-	Plugins map[string]interface{} `toml:"plugins"`
-}
-
 type remote struct {
 	sync.RWMutex
-	server.Config
+	config.Config
+	// Plugins overrides `Plugins map[string]toml.Tree` in config config.
+	Plugins map[string]interface{} `toml:"plugins"`
 
 	daemonPid int
 	logger    *logrus.Entry
@@ -46,9 +43,8 @@ type remote struct {
 	daemonStartCh chan error
 	daemonStopCh  chan struct{}
 
-	rootDir     string
-	stateDir    string
-	pluginConfs pluginConfigs
+	rootDir  string
+	stateDir string
 }
 
 // Daemon represents a running containerd daemon
@@ -65,11 +61,11 @@ func Start(ctx context.Context, rootDir, stateDir string, opts ...DaemonOpt) (Da
 	r := &remote{
 		rootDir:  rootDir,
 		stateDir: stateDir,
-		Config: server.Config{
+		Config: config.Config{
 			Root:  filepath.Join(rootDir, "daemon"),
 			State: filepath.Join(stateDir, "daemon"),
 		},
-		pluginConfs:   pluginConfigs{make(map[string]interface{})},
+		Plugins:       make(map[string]interface{}),
 		daemonPid:     -1,
 		logger:        logrus.WithField("module", "libcontainerd"),
 		daemonStartCh: make(chan error, 1),
@@ -83,14 +79,17 @@ func Start(ctx context.Context, rootDir, stateDir string, opts ...DaemonOpt) (Da
 	}
 	r.setDefaults()
 
-	if err := system.MkdirAll(stateDir, 0700, ""); err != nil {
+	if err := system.MkdirAll(stateDir, 0700); err != nil {
 		return nil, err
 	}
 
 	go r.monitorDaemon(ctx)
 
+	timeout := time.NewTimer(startupTimeout)
+	defer timeout.Stop()
+
 	select {
-	case <-time.After(startupTimeout):
+	case <-timeout.C:
 		return nil, errors.New("timeout waiting for containerd to start")
 	case err := <-r.daemonStartCh:
 		if err != nil {
@@ -101,8 +100,11 @@ func Start(ctx context.Context, rootDir, stateDir string, opts ...DaemonOpt) (Da
 	return r, nil
 }
 func (r *remote) WaitTimeout(d time.Duration) error {
+	timeout := time.NewTimer(d)
+	defer timeout.Stop()
+
 	select {
-	case <-time.After(d):
+	case <-timeout.C:
 		return errors.New("timeout waiting for containerd to stop")
 	case <-r.daemonStopCh:
 	}
@@ -151,14 +153,9 @@ func (r *remote) getContainerdConfig() (string, error) {
 	}
 	defer f.Close()
 
-	enc := toml.NewEncoder(f)
-	if err = enc.Encode(r.Config); err != nil {
-		return "", errors.Wrapf(err, "failed to encode general config")
-	}
-	if err = enc.Encode(r.pluginConfs); err != nil {
-		return "", errors.Wrapf(err, "failed to encode plugin configs")
+	if err := toml.NewEncoder(f).Encode(r); err != nil {
+		return "", errors.Wrapf(err, "failed to write containerd config file (%s)", path)
 	}
-
 	return path, nil
 }
 
@@ -213,7 +210,7 @@ func (r *remote) startContainerd() error {
 
 	r.daemonPid = cmd.Process.Pid
 
-	err = ioutil.WriteFile(filepath.Join(r.stateDir, pidFile), []byte(fmt.Sprintf("%d", r.daemonPid)), 0660)
+	err = os.WriteFile(filepath.Join(r.stateDir, pidFile), []byte(fmt.Sprintf("%d", r.daemonPid)), 0660)
 	if err != nil {
 		system.KillProcess(r.daemonPid)
 		return errors.Wrap(err, "libcontainerd: failed to save daemon pid to disk")
@@ -230,7 +227,8 @@ func (r *remote) monitorDaemon(ctx context.Context) {
 		transientFailureCount = 0
 		client                *containerd.Client
 		err                   error
-		delay                 <-chan time.Time
+		delay                 time.Duration
+		timer                 = time.NewTimer(0)
 		started               bool
 	)
 
@@ -245,19 +243,25 @@ func (r *remote) monitorDaemon(ctx context.Context) {
 		r.platformCleanup()
 
 		close(r.daemonStopCh)
+		timer.Stop()
 	}()
 
+	// ensure no races on sending to timer.C even though there is a 0 duration.
+	if !timer.Stop() {
+		<-timer.C
+	}
+
 	for {
-		if delay != nil {
-			select {
-			case <-ctx.Done():
-				r.logger.Info("stopping healthcheck following graceful shutdown")
-				if client != nil {
-					client.Close()
-				}
-				return
-			case <-delay:
+		timer.Reset(delay)
+
+		select {
+		case <-ctx.Done():
+			r.logger.Info("stopping healthcheck following graceful shutdown")
+			if client != nil {
+				client.Close()
 			}
+			return
+		case <-timer.C:
 		}
 
 		if r.daemonPid == -1 {
@@ -277,16 +281,17 @@ func (r *remote) monitorDaemon(ctx context.Context) {
 					return
 				}
 				r.logger.WithError(err).Error("failed restarting containerd")
-				delay = time.After(50 * time.Millisecond)
+				delay = 50 * time.Millisecond
 				continue
 			}
 
 			client, err = containerd.New(r.GRPC.Address, containerd.WithTimeout(60*time.Second))
 			if err != nil {
 				r.logger.WithError(err).Error("failed connecting to containerd")
-				delay = time.After(100 * time.Millisecond)
+				delay = 100 * time.Millisecond
 				continue
 			}
+			logrus.WithField("address", r.GRPC.Address).Debug("Created containerd monitoring client")
 		}
 
 		if client != nil {
@@ -300,7 +305,15 @@ func (r *remote) monitorDaemon(ctx context.Context) {
 				}
 
 				transientFailureCount = 0
-				delay = time.After(500 * time.Millisecond)
+
+				select {
+				case <-r.daemonWaitCh:
+				case <-ctx.Done():
+				}
+
+				// Set a small delay in case there is a recurring failure (or bug in this code)
+				// to ensure we don't end up in a super tight loop.
+				delay = 500 * time.Millisecond
 				continue
 			}
 
@@ -308,9 +321,11 @@ func (r *remote) monitorDaemon(ctx context.Context) {
 
 			transientFailureCount++
 			if transientFailureCount < maxConnectionRetryCount || system.IsProcessAlive(r.daemonPid) {
-				delay = time.After(time.Duration(transientFailureCount) * 200 * time.Millisecond)
+				delay = time.Duration(transientFailureCount) * 200 * time.Millisecond
 				continue
 			}
+			client.Close()
+			client = nil
 		}
 
 		if system.IsProcessAlive(r.daemonPid) {
@@ -318,10 +333,8 @@ func (r *remote) monitorDaemon(ctx context.Context) {
 			r.killDaemon()
 		}
 
-		client.Close()
-		client = nil
 		r.daemonPid = -1
-		delay = nil
+		delay = 0
 		transientFailureCount = 0
 	}
 }
diff --git a/libcontainerd/supervisor/remote_daemon_linux.go b/libcontainerd/supervisor/remote_daemon_linux.go
index 799399c07bc58..3f019a162f8d5 100644
--- a/libcontainerd/supervisor/remote_daemon_linux.go
+++ b/libcontainerd/supervisor/remote_daemon_linux.go
@@ -28,14 +28,11 @@ func (r *remote) setDefaults() {
 	if r.Debug.Address == "" {
 		r.Debug.Address = filepath.Join(r.stateDir, debugSockFile)
 	}
-	if r.OOMScore == 0 {
-		r.OOMScore = -999
-	}
 
-	for key, conf := range r.pluginConfs.Plugins {
+	for key, conf := range r.Plugins {
 		if conf == nil {
 			r.DisabledPlugins = append(r.DisabledPlugins, key)
-			delete(r.pluginConfs.Plugins, key)
+			delete(r.Plugins, key)
 		}
 	}
 }
diff --git a/libcontainerd/supervisor/remote_daemon_options.go b/libcontainerd/supervisor/remote_daemon_options.go
index 4c9387d6ce75a..fe4cb351671b5 100644
--- a/libcontainerd/supervisor/remote_daemon_options.go
+++ b/libcontainerd/supervisor/remote_daemon_options.go
@@ -49,7 +49,7 @@ func WithMetricsAddress(addr string) DaemonOpt {
 // the toml format.
 func WithPlugin(name string, conf interface{}) DaemonOpt {
 	return func(r *remote) error {
-		r.pluginConfs.Plugins[name] = conf
+		r.Plugins[name] = conf
 		return nil
 	}
 }
diff --git a/libcontainerd/types.go b/libcontainerd/types.go
deleted file mode 100644
index c4de5e674d9a6..0000000000000
--- a/libcontainerd/types.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import (
-	"context"
-	"time"
-
-	"github.com/containerd/containerd"
-	"github.com/containerd/containerd/cio"
-	"github.com/opencontainers/runtime-spec/specs-go"
-)
-
-// EventType represents a possible event from libcontainerd
-type EventType string
-
-// Event constants used when reporting events
-const (
-	EventUnknown     EventType = "unknown"
-	EventExit        EventType = "exit"
-	EventOOM         EventType = "oom"
-	EventCreate      EventType = "create"
-	EventStart       EventType = "start"
-	EventExecAdded   EventType = "exec-added"
-	EventExecStarted EventType = "exec-started"
-	EventPaused      EventType = "paused"
-	EventResumed     EventType = "resumed"
-)
-
-// Status represents the current status of a container
-type Status string
-
-// Possible container statuses
-const (
-	// Running indicates the process is currently executing
-	StatusRunning Status = "running"
-	// Created indicates the process has been created within containerd but the
-	// user's defined process has not started
-	StatusCreated Status = "created"
-	// Stopped indicates that the process has ran and exited
-	StatusStopped Status = "stopped"
-	// Paused indicates that the process is currently paused
-	StatusPaused Status = "paused"
-	// Pausing indicates that the process is currently switching from a
-	// running state into a paused state
-	StatusPausing Status = "pausing"
-	// Unknown indicates that we could not determine the status from the runtime
-	StatusUnknown Status = "unknown"
-)
-
-// EventInfo contains the event info
-type EventInfo struct {
-	ContainerID string
-	ProcessID   string
-	Pid         uint32
-	ExitCode    uint32
-	ExitedAt    time.Time
-	OOMKilled   bool
-	Error       error
-}
-
-// Backend defines callbacks that the client of the library needs to implement.
-type Backend interface {
-	ProcessEvent(containerID string, event EventType, ei EventInfo) error
-}
-
-// Client provides access to containerd features.
-type Client interface {
-	Version(ctx context.Context) (containerd.Version, error)
-
-	Restore(ctx context.Context, containerID string, attachStdio StdioCallback) (alive bool, pid int, err error)
-
-	Create(ctx context.Context, containerID string, spec *specs.Spec, runtimeOptions interface{}) error
-	Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio StdioCallback) (pid int, err error)
-	SignalProcess(ctx context.Context, containerID, processID string, signal int) error
-	Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error)
-	ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error
-	CloseStdin(ctx context.Context, containerID, processID string) error
-	Pause(ctx context.Context, containerID string) error
-	Resume(ctx context.Context, containerID string) error
-	Stats(ctx context.Context, containerID string) (*Stats, error)
-	ListPids(ctx context.Context, containerID string) ([]uint32, error)
-	Summary(ctx context.Context, containerID string) ([]Summary, error)
-	DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error)
-	Delete(ctx context.Context, containerID string) error
-	Status(ctx context.Context, containerID string) (Status, error)
-
-	UpdateResources(ctx context.Context, containerID string, resources *Resources) error
-	CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error
-}
-
-// StdioCallback is called to connect a container or process stdio.
-type StdioCallback func(io *cio.DirectIO) (cio.IO, error)
diff --git a/libcontainerd/types/types.go b/libcontainerd/types/types.go
new file mode 100644
index 0000000000000..1d1a420e794c9
--- /dev/null
+++ b/libcontainerd/types/types.go
@@ -0,0 +1,78 @@
+package types // import "github.com/docker/docker/libcontainerd/types"
+
+import (
+	"context"
+	"time"
+
+	"github.com/containerd/containerd"
+	"github.com/containerd/containerd/cio"
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// EventType represents a possible event from libcontainerd
+type EventType string
+
+// Event constants used when reporting events
+const (
+	EventUnknown     EventType = "unknown"
+	EventExit        EventType = "exit"
+	EventOOM         EventType = "oom"
+	EventCreate      EventType = "create"
+	EventStart       EventType = "start"
+	EventExecAdded   EventType = "exec-added"
+	EventExecStarted EventType = "exec-started"
+	EventPaused      EventType = "paused"
+	EventResumed     EventType = "resumed"
+)
+
+// EventInfo contains the event info
+type EventInfo struct {
+	ContainerID string
+	ProcessID   string
+	Pid         uint32
+	ExitCode    uint32
+	ExitedAt    time.Time
+	OOMKilled   bool
+	Error       error
+}
+
+// Backend defines callbacks that the client of the library needs to implement.
+type Backend interface {
+	ProcessEvent(containerID string, event EventType, ei EventInfo) error
+}
+
+// Process of a container
+type Process interface {
+	Delete(context.Context) (uint32, time.Time, error)
+}
+
+// Client provides access to containerd features.
+type Client interface {
+	Version(ctx context.Context) (containerd.Version, error)
+
+	Restore(ctx context.Context, containerID string, attachStdio StdioCallback) (alive bool, pid int, p Process, err error)
+
+	Create(ctx context.Context, containerID string, spec *specs.Spec, shim string, runtimeOptions interface{}, opts ...containerd.NewContainerOpts) error
+	Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio StdioCallback) (pid int, err error)
+	SignalProcess(ctx context.Context, containerID, processID string, signal int) error
+	Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error)
+	ResizeTerminal(ctx context.Context, containerID, processID string, width, height int) error
+	CloseStdin(ctx context.Context, containerID, processID string) error
+	Pause(ctx context.Context, containerID string) error
+	Resume(ctx context.Context, containerID string) error
+	Stats(ctx context.Context, containerID string) (*Stats, error)
+	ListPids(ctx context.Context, containerID string) ([]uint32, error)
+	Summary(ctx context.Context, containerID string) ([]Summary, error)
+	DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error)
+	Delete(ctx context.Context, containerID string) error
+	Status(ctx context.Context, containerID string) (containerd.ProcessStatus, error)
+
+	UpdateResources(ctx context.Context, containerID string, resources *Resources) error
+	CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error
+}
+
+// StdioCallback is called to connect a container or process stdio.
+type StdioCallback func(io *cio.DirectIO) (cio.IO, error)
+
+// InitProcessName is the name given to the first process of a container
+const InitProcessName = "init"
diff --git a/libcontainerd/types/types_linux.go b/libcontainerd/types/types_linux.go
new file mode 100644
index 0000000000000..6b05385221641
--- /dev/null
+++ b/libcontainerd/types/types_linux.go
@@ -0,0 +1,33 @@
+package types // import "github.com/docker/docker/libcontainerd/types"
+
+import (
+	"time"
+
+	specs "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// Summary is not used on linux
+type Summary struct{}
+
+// Stats holds metrics properties as returned by containerd
+type Stats struct {
+	Read time.Time
+	// Metrics is expected to be either one of:
+	// * github.com/containerd/cgroups/stats/v1.Metrics
+	// * github.com/containerd/cgroups/stats/v2.Metrics
+	Metrics interface{}
+}
+
+// InterfaceToStats returns a stats object from the platform-specific interface.
+func InterfaceToStats(read time.Time, v interface{}) *Stats {
+	return &Stats{
+		Metrics: v,
+		Read:    read,
+	}
+}
+
+// Resources defines updatable container resource values. TODO: it must match containerd upcoming API
+type Resources specs.LinuxResources
+
+// Checkpoints contains the details of a checkpoint
+type Checkpoints struct{}
diff --git a/libcontainerd/types/types_windows.go b/libcontainerd/types/types_windows.go
new file mode 100644
index 0000000000000..3b521f1342208
--- /dev/null
+++ b/libcontainerd/types/types_windows.go
@@ -0,0 +1,37 @@
+package types // import "github.com/docker/docker/libcontainerd/types"
+
+import (
+	"time"
+
+	"github.com/Microsoft/hcsshim"
+	"github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
+)
+
+type Summary options.ProcessDetails
+
+// Stats contains statistics from HCS
+type Stats struct {
+	Read     time.Time
+	HCSStats *hcsshim.Statistics
+}
+
+// InterfaceToStats returns a stats object from the platform-specific interface.
+func InterfaceToStats(read time.Time, v interface{}) *Stats {
+	return &Stats{
+		HCSStats: v.(*hcsshim.Statistics),
+		Read:     read,
+	}
+}
+
+// Resources defines updatable container resource values.
+type Resources struct{}
+
+// Checkpoint holds the details of a checkpoint (not supported in windows)
+type Checkpoint struct {
+	Name string
+}
+
+// Checkpoints contains the details of a checkpoint
+type Checkpoints struct {
+	Checkpoints []*Checkpoint
+}
diff --git a/libcontainerd/types_linux.go b/libcontainerd/types_linux.go
deleted file mode 100644
index 943382b9b005d..0000000000000
--- a/libcontainerd/types_linux.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import (
-	"time"
-
-	"github.com/containerd/cgroups"
-	"github.com/opencontainers/runtime-spec/specs-go"
-)
-
-// Summary is not used on linux
-type Summary struct{}
-
-// Stats holds metrics properties as returned by containerd
-type Stats struct {
-	Read    time.Time
-	Metrics *cgroups.Metrics
-}
-
-func interfaceToStats(read time.Time, v interface{}) *Stats {
-	return &Stats{
-		Metrics: v.(*cgroups.Metrics),
-		Read:    read,
-	}
-}
-
-// Resources defines updatable container resource values. TODO: it must match containerd upcoming API
-type Resources specs.LinuxResources
-
-// Checkpoints contains the details of a checkpoint
-type Checkpoints struct{}
diff --git a/libcontainerd/types_windows.go b/libcontainerd/types_windows.go
deleted file mode 100644
index 9041a2e8d53bf..0000000000000
--- a/libcontainerd/types_windows.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import (
-	"time"
-
-	"github.com/Microsoft/hcsshim"
-	opengcs "github.com/Microsoft/opengcs/client"
-)
-
-// Summary contains a ProcessList item from HCS to support `top`
-type Summary hcsshim.ProcessListItem
-
-// Stats contains statistics from HCS
-type Stats struct {
-	Read     time.Time
-	HCSStats *hcsshim.Statistics
-}
-
-func interfaceToStats(read time.Time, v interface{}) *Stats {
-	return &Stats{
-		HCSStats: v.(*hcsshim.Statistics),
-		Read:     read,
-	}
-}
-
-// Resources defines updatable container resource values.
-type Resources struct{}
-
-// LCOWOption is a CreateOption required for LCOW configuration
-type LCOWOption struct {
-	Config *opengcs.Config
-}
-
-// Checkpoint holds the details of a checkpoint (not supported in windows)
-type Checkpoint struct {
-	Name string
-}
-
-// Checkpoints contains the details of a checkpoint
-type Checkpoints struct {
-	Checkpoints []*Checkpoint
-}
diff --git a/libcontainerd/utils_windows.go b/libcontainerd/utils_windows.go
deleted file mode 100644
index aabb9aeaaa9d3..0000000000000
--- a/libcontainerd/utils_windows.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package libcontainerd // import "github.com/docker/docker/libcontainerd"
-
-import (
-	"strings"
-
-	opengcs "github.com/Microsoft/opengcs/client"
-)
-
-// setupEnvironmentVariables converts a string array of environment variables
-// into a map as required by the HCS. Source array is in format [v1=k1] [v2=k2] etc.
-func setupEnvironmentVariables(a []string) map[string]string {
-	r := make(map[string]string)
-	for _, s := range a {
-		arr := strings.SplitN(s, "=", 2)
-		if len(arr) == 2 {
-			r[arr[0]] = arr[1]
-		}
-	}
-	return r
-}
-
-// Apply for the LCOW option is a no-op.
-func (s *LCOWOption) Apply(interface{}) error {
-	return nil
-}
-
-// debugGCS is a dirty hack for debugging for Linux Utility VMs. It simply
-// runs a bunch of commands inside the UVM, but seriously aides in advanced debugging.
-func (c *container) debugGCS() {
-	if c == nil || c.isWindows || c.hcsContainer == nil {
-		return
-	}
-	cfg := opengcs.Config{
-		Uvm:               c.hcsContainer,
-		UvmTimeoutSeconds: 600,
-	}
-	cfg.DebugGCS()
-}
diff --git a/libnetwork/.dockerignore b/libnetwork/.dockerignore
new file mode 100644
index 0000000000000..31de392ac1e97
--- /dev/null
+++ b/libnetwork/.dockerignore
@@ -0,0 +1,5 @@
+.git
+.dockerignore
+Dockerfile
+bin
+tags
diff --git a/libnetwork/.gitignore b/libnetwork/.gitignore
new file mode 100644
index 0000000000000..81dff39251277
--- /dev/null
+++ b/libnetwork/.gitignore
@@ -0,0 +1,45 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+*~
+.gtm
+bin/
+tags
+.DS_Store
+
+# Folders
+integration-tmp/
+_obj
+_test
+.vagrant
+
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+cmd/dnet/dnet
+
+# Coverage
+*.tmp
+*.coverprofile
+
+# IDE files and folders
+.project
+.settings/
+
+libnetworkbuild.created
+test/networkDb/testMain
+test/networkDb/gossipdb
diff --git a/libnetwork/CHANGELOG.md b/libnetwork/CHANGELOG.md
new file mode 100644
index 0000000000000..77de7665a51e2
--- /dev/null
+++ b/libnetwork/CHANGELOG.md
@@ -0,0 +1,199 @@
+# Changelog
+
+## 0.8.0-dev.2 (2016-05-07)
+- Fix an issue which may arise during sandbox cleanup (https://github.com/docker/libnetwork/pull/1157)
+- Fix cleanup logic in case of ipv6 allocation failure
+- Don't add /etc/hosts record if container's ip is empty (--net=none)
+- Fix default gw logic for internal networks
+- Error when updating IPv6 gateway (https://github.com/docker/libnetwork/issues/1142)
+- Fixes https://github.com/docker/libnetwork/issues/1113
+- Fixes https://github.com/docker/libnetwork/issues/1069
+- Fxies https://github.com/docker/libnetwork/issues/1117
+- Increase the concurrent query rate-limit count
+- Changes to build libnetwork in Solaris
+
+## 0.8.0-dev.1 (2016-04-16)
+- Fixes docker/docker#16964
+- Added maximum egress bandwidth qos for Windows
+
+## 0.7.0-rc.6 (2016-04-10)
+- Flush cached resolver socket on default gateway change
+
+## 0.7.0-rc.5 (2016-04-08)
+- Persist ipam driver options
+- Fixes https://github.com/docker/libnetwork/issues/1087
+- Use go vet from go tool
+- Godep update to pick up latest docker/docker packages
+- Validate remote driver response using docker plugins package method.
+
+## 0.7.0-rc.4 (2016-04-06)
+- Fix the handling for default gateway Endpoint join/leave.
+
+## 0.7.0-rc.3 (2016-04-05)
+- Revert fix for default gateway endpoint join/leave. Needs to be reworked.
+- Persist the network internal mode for bridge networks
+
+## 0.7.0-rc.2 (2016-04-05)
+- Fixes https://github.com/docker/libnetwork/issues/1070
+- Move IPAM resource initialization out of init()
+- Initialize overlay driver before network delete
+- Fix the handling for default gateway Endpoint join/lean
+
+## 0.7.0-rc.1 (2016-03-30)
+- Fixes https://github.com/docker/libnetwork/issues/985
+- Fixes https://github.com/docker/libnetwork/issues/945
+- Log time taken to set sandbox key
+- Limit number of concurrent DNS queries
+
+## 0.7.0-dev.10 (2016-03-21)
+- Add IPv6 service discovery (AAAA records) in embedded DNS server
+- Honor enableIPv6 flag in network create for the IP allocation
+- Avoid V6 queries in docker domain going to external nameservers
+
+## 0.7.0-dev.9 (2016-03-18)
+- Support labels on networks
+
+## 0.7.0-dev.8 (2016-03-16)
+- Windows driver to respect user set MAC address.
+- Fix possible nil pointer reference in ServeDNS() with concurrent go routines.
+- Fix netns path setting from hook (for containerd integration)
+- Clear cached udp connections on resolver Stop()
+- Avoid network/endpoint count inconsistences and remove stale networks after ungraceful shutdown
+- Fix possible endpoint count inconsistency after ungraceful shutdown
+- Reject a null v4 IPAM slice in exp vlan drivers
+- Removed experimental drivers modprobe check
+
+## 0.7.0-dev.7 (2016-03-11)
+- Bumped up the minimum kernel version for ipvlan to 4.2
+- Removed modprobe from macvlan/ipvlan drivers to resolve docker IT failures
+- Close dbus connection if firewalld is not started
+
+## 0.7.0-dev.6 (2016-03-10)
+- Experimental support for macvlan and ipvlan drivers
+
+## 0.7.0-dev.5 (2016-03-08)
+- Fixes https://github.com/docker/docker/issues/20847
+- Fixes https://github.com/docker/docker/issues/20997
+- Fixes issues unveiled by docker integ test over 0.7.0-dev.4
+
+## 0.7.0-dev.4 (2016-03-07)
+- Changed ownership of exposed ports and port-mapping options from Endpoint to Sandbox
+- Implement DNS RR in the Docker embedded DNS server
+- Fixes https://github.com/docker/libnetwork/issues/984 (multi container overlay veth leak)
+- Libnetwork to program container's interface MAC address
+- Fixed bug in iptables.Exists() logic
+- Fixes https://github.com/docker/docker/issues/20694
+- Source external DNS queries from container namespace
+- Added inbuilt nil IPAM driver
+- Windows drivers integration fixes
+- Extract hostname from (hostname.domainname). Related to https://github.com/docker/docker/issues/14282
+- Fixed race in sandbox statistics read
+- Fixes https://github.com/docker/libnetwork/issues/892 (docker start fails when ipv6.disable=1)
+- Fixed error message on bridge network creation conflict
+
+## 0.7.0-dev.3 (2016-02-17)
+- Fixes https://github.com/docker/docker/issues/20350
+- Fixes https://github.com/docker/docker/issues/20145
+- Initial Windows HNS integration
+- Allow passing global datastore config to libnetwork after boot
+- Set Recursion Available bit in DNS query responses
+- Make sure iptables chains are recreated on firewalld reload
+
+## 0.7.0-dev.2 (2016-02-11)
+- Fixes https://github.com/docker/docker/issues/20140
+
+## 0.7.0-dev.1 (2016-02-10)
+- Expose EnableIPV6 option
+- discoverapi refactoring
+- Fixed a few typos & docs update
+
+## 0.6.1-rc2 (2016-02-09)
+- Fixes https://github.com/docker/docker/issues/20132
+- Fixes https://github.com/docker/docker/issues/20140
+- Fixes https://github.com/docker/docker/issues/20019
+
+## 0.6.1-rc1 (2016-02-05)
+- Fixes https://github.com/docker/docker/issues/20026
+
+## 0.6.0-rc7 (2016-02-01)
+- Allow inter-network connections via exposed ports
+
+## 0.6.0-rc6 (2016-01-30)
+- Properly fixes https://github.com/docker/docker/issues/18814
+
+## 0.6.0-rc5 (2016-01-26)
+- Cleanup stale overlay sandboxes
+
+## 0.6.0-rc4 (2016-01-25)
+- Add Endpoints() API to Sandbox interface
+- Fixed a race-condition in default gateway network creation
+
+## 0.6.0-rc3 (2016-01-25)
+- Fixes docker/docker#19576
+- Fixed embedded DNS to listen in TCP as well
+- Fixed a race-condition in IPAM to choose non-overlapping subnet for concurrent requests
+
+## 0.6.0-rc2 (2016-01-21)
+- Fixes docker/docker#19376
+- Fixes docker/docker#15819
+- Fixes libnetwork/#885, Not filter v6 DNS servers from resolv.conf
+- Fixes docker/docker #19448, also handles the . in service and network names correctly.
+
+## 0.6.0-rc1 (2016-01-14)
+- Fixes docker/docker#19404
+- Fixes the ungraceful daemon restart issue in systemd with remote network plugin
+  (https://github.com/docker/libnetwork/issues/813)
+
+## 0.5.6 (2016-01-14)
+- Setup embedded DNS server correctly on container restart. Fixes docker/docker#19354
+
+## 0.5.5 (2016-01-14)
+- Allow network-scoped alias to be resolved for anonymous endpoint
+- Self repair corrupted IP database that could happen in 1.9.0 & 1.9.1
+- Skip IPTables cleanup if --iptables=false is set. Fixes docker/docker#19063
+
+## 0.5.4 (2016-01-12)
+- Removed the isNodeAlive protection when user forces an endpoint delete
+
+## 0.5.3 (2016-01-12)
+- Bridge driver supporting internal network option
+- Backend implementation to support "force" option to network disconnect
+- Fixing a regex in etchosts package to fix docker/docker#19080
+
+## 0.5.2 (2016-01-08)
+- Embedded DNS replacing /etc/hosts based Service Discovery
+- Container local alias and Network-scoped alias support
+- Backend support for internal network mode
+- Support for IPAM driver options
+- Fixes overlay veth cleanup issue : docker/docker#18814
+- fixes docker/docker#19139
+- disable IPv6 Duplicate Address Detection
+
+## 0.5.1 (2015-12-07)
+- Allowing user to assign IP Address for containers
+- Fixes docker/docker#18214
+- Fixes docker/docker#18380
+
+## 0.5.0 (2015-10-30)
+
+- Docker multi-host networking exiting experimental channel
+- Introduced IP Address Management and IPAM drivers
+- DEPRECATE service discovery from default bridge network
+- Introduced new network UX
+- Support for multiple networks in bridge driver
+- Local persistence with boltdb
+
+## 0.4.0 (2015-07-24)
+
+- Introduce experimental version of Overlay driver
+- Introduce experimental version of network plugins
+- Introduce experimental version of network & service UX
+- Introduced experimental /etc/hosts based service discovery
+- Integrated with libkv
+- Improving test coverage
+- Fixed a bunch of issues with osl namespace mgmt
+
+## 0.3.0 (2015-05-27)
+ 
+- Introduce CNM (Container Networking Model)
+- Replace docker networking with CNM & Bridge driver
diff --git a/libnetwork/README.md b/libnetwork/README.md
new file mode 100644
index 0000000000000..c48c5b5794e35
--- /dev/null
+++ b/libnetwork/README.md
@@ -0,0 +1,97 @@
+# libnetwork - networking for containers
+
+Libnetwork provides a native Go implementation for connecting containers
+
+The goal of libnetwork is to deliver a robust Container Network Model that provides a consistent programming interface and the required network abstractions for applications.
+
+#### Design
+Please refer to the [design](docs/design.md) for more information.
+
+#### Using libnetwork
+
+There are many networking solutions available to suit a broad range of use-cases. libnetwork uses a driver / plugin model to support all of these solutions while abstracting the complexity of the driver implementations by exposing a simple and consistent Network Model to users.
+
+
+```go
+package main
+
+import (
+	"fmt"
+	"log"
+
+	"github.com/docker/docker/pkg/reexec"
+	"github.com/docker/docker/libnetwork"
+	"github.com/docker/docker/libnetwork/config"
+	"github.com/docker/docker/libnetwork/netlabel"
+	"github.com/docker/docker/libnetwork/options"
+)
+
+func main() {
+	if reexec.Init() {
+		return
+	}
+
+	// Select and configure the network driver
+	networkType := "bridge"
+
+	// Create a new controller instance
+	driverOptions := options.Generic{}
+	genericOption := make(map[string]interface{})
+	genericOption[netlabel.GenericData] = driverOptions
+	controller, err := libnetwork.New(config.OptionDriverConfig(networkType, genericOption))
+	if err != nil {
+		log.Fatalf("libnetwork.New: %s", err)
+	}
+
+	// Create a network for containers to join.
+	// NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can use.
+	network, err := controller.NewNetwork(networkType, "network1", "")
+	if err != nil {
+		log.Fatalf("controller.NewNetwork: %s", err)
+	}
+
+	// For each new container: allocate IP and interfaces. The returned network
+	// settings will be used for container infos (inspect and such), as well as
+	// iptables rules for port publishing. This info is contained or accessible
+	// from the returned endpoint.
+	ep, err := network.CreateEndpoint("Endpoint1")
+	if err != nil {
+		log.Fatalf("network.CreateEndpoint: %s", err)
+	}
+
+	// Create the sandbox for the container.
+	// NewSandbox accepts Variadic optional arguments which libnetwork can use.
+	sbx, err := controller.NewSandbox("container1",
+		libnetwork.OptionHostname("test"),
+		libnetwork.OptionDomainname("docker.io"))
+	if err != nil {
+		log.Fatalf("controller.NewSandbox: %s", err)
+	}
+
+	// A sandbox can join the endpoint via the join api.
+	err = ep.Join(sbx)
+	if err != nil {
+		log.Fatalf("ep.Join: %s", err)
+	}
+
+	// libnetwork client can check the endpoint's operational data via the Info() API
+	epInfo, err := ep.DriverInfo()
+	if err != nil {
+		log.Fatalf("ep.DriverInfo: %s", err)
+	}
+
+	macAddress, ok := epInfo[netlabel.MacAddress]
+	if !ok {
+		log.Fatalf("failed to get mac address from endpoint info")
+	}
+
+	fmt.Printf("Joined endpoint %s (%s) to sandbox %s (%s)\n", ep.Name(), macAddress, sbx.ContainerID(), sbx.Key())
+}
+```
+
+## Contributing
+
+Want to hack on libnetwork? [Docker's contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) apply.
+
+## Copyright and license
+Code and documentation copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons.
diff --git a/vendor/github.com/docker/libnetwork/agent.go b/libnetwork/agent.go
similarity index 95%
rename from vendor/github.com/docker/libnetwork/agent.go
rename to libnetwork/agent.go
index a9d77e26701d4..feca147ea840b 100644
--- a/vendor/github.com/docker/libnetwork/agent.go
+++ b/libnetwork/agent.go
@@ -1,6 +1,6 @@
 package libnetwork
 
-//go:generate protoc -I.:Godeps/_workspace/src/github.com/gogo/protobuf  --gogo_out=import_path=github.com/docker/libnetwork,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. agent.proto
+//go:generate protoc -I.:Godeps/_workspace/src/github.com/gogo/protobuf  --gogo_out=import_path=github.com/docker/docker/libnetwork,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. agent.proto
 
 import (
 	"encoding/json"
@@ -9,13 +9,13 @@ import (
 	"sort"
 	"sync"
 
+	"github.com/docker/docker/libnetwork/cluster"
+	"github.com/docker/docker/libnetwork/datastore"
+	"github.com/docker/docker/libnetwork/discoverapi"
+	"github.com/docker/docker/libnetwork/driverapi"
+	"github.com/docker/docker/libnetwork/networkdb"
+	"github.com/docker/docker/libnetwork/types"
 	"github.com/docker/go-events"
-	"github.com/docker/libnetwork/cluster"
-	"github.com/docker/libnetwork/datastore"
-	"github.com/docker/libnetwork/discoverapi"
-	"github.com/docker/libnetwork/driverapi"
-	"github.com/docker/libnetwork/networkdb"
-	"github.com/docker/libnetwork/types"
 	"github.com/gogo/protobuf/proto"
 	"github.com/sirupsen/logrus"
 )
@@ -184,6 +184,16 @@ func (c *controller) handleKeyChange(keys []*types.EncryptionKey) error {
 		err := driver.DiscoverNew(discoverapi.EncryptionKeysUpdate, drvEnc)
 		if err != nil {
 			logrus.Warnf("Failed to update datapath keys in driver %s: %v", name, err)
+			// Attempt to reconfigure keys in case of a update failure
+			// which can arise due to a mismatch of keys
+			// if worker nodes get temporarily disconnected
+			logrus.Warnf("Reconfiguring datapath keys for  %s", name)
+			drvCfgEnc := discoverapi.DriverEncryptionConfig{}
+			drvCfgEnc.Keys, drvCfgEnc.Tags = c.getKeys(subsysIPSec)
+			err = driver.DiscoverNew(discoverapi.EncryptionKeysConfig, drvCfgEnc)
+			if err != nil {
+				logrus.Warnf("Failed to reset datapath keys in driver %s: %v", name, err)
+			}
 		}
 		return false
 	})
@@ -378,6 +388,9 @@ func (c *controller) agentClose() {
 	c.agent = nil
 	c.Unlock()
 
+	// when the agent is closed the cluster provider should be cleaned up
+	c.SetClusterProvider(nil)
+
 	if agent == nil {
 		return
 	}
@@ -583,7 +596,7 @@ func (ep *endpoint) deleteDriverInfoFromCluster() error {
 }
 
 func (ep *endpoint) addServiceInfoToCluster(sb *sandbox) error {
-	if ep.isAnonymous() && len(ep.myAliases) == 0 || ep.Iface().Address() == nil {
+	if ep.isAnonymous() && len(ep.myAliases) == 0 || ep.Iface() == nil || ep.Iface().Address() == nil {
 		return nil
 	}
 
@@ -706,7 +719,7 @@ func (ep *endpoint) deleteServiceInfoFromCluster(sb *sandbox, fullRemove bool, m
 		}
 	}
 
-	if ep.Iface().Address() != nil {
+	if ep.Iface() != nil && ep.Iface().Address() != nil {
 		if ep.svcID != "" {
 			// This is a task part of a service
 			var ingressPorts []*PortConfig
@@ -780,7 +793,7 @@ func (n *network) addDriverWatches() {
 			return
 		}
 
-		agent.networkDB.WalkTable(table.name, func(nid, key string, value []byte, deleted bool) bool {
+		err = agent.networkDB.WalkTable(table.name, func(nid, key string, value []byte, deleted bool) bool {
 			// skip the entries that are mark for deletion, this is safe because this function is
 			// called at initialization time so there is no state to delete
 			if nid == n.ID() && !deleted {
@@ -788,6 +801,9 @@ func (n *network) addDriverWatches() {
 			}
 			return false
 		})
+		if err != nil {
+			logrus.WithError(err).Warn("Error while walking networkdb")
+		}
 	}
 }
 
diff --git a/vendor/github.com/docker/libnetwork/agent.pb.go b/libnetwork/agent.pb.go
similarity index 100%
rename from vendor/github.com/docker/libnetwork/agent.pb.go
rename to libnetwork/agent.pb.go
diff --git a/vendor/github.com/docker/libnetwork/agent.proto b/libnetwork/agent.proto
similarity index 100%
rename from vendor/github.com/docker/libnetwork/agent.proto
rename to libnetwork/agent.proto
diff --git a/libnetwork/bitseq/sequence.go b/libnetwork/bitseq/sequence.go
new file mode 100644
index 0000000000000..b53721d2120d8
--- /dev/null
+++ b/libnetwork/bitseq/sequence.go
@@ -0,0 +1,731 @@
+// Package bitseq provides a structure and utilities for representing long bitmask
+// as sequence of run-length encoded blocks. It operates directly on the encoded
+// representation, it does not decode/encode.
+package bitseq
+
+import (
+	"encoding/binary"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"sync"
+
+	"github.com/docker/docker/libnetwork/datastore"
+	"github.com/docker/docker/libnetwork/types"
+	"github.com/sirupsen/logrus"
+)
+
+// block sequence constants
+// If needed we can think of making these configurable
+const (
+	blockLen      = uint32(32)
+	blockBytes    = uint64(blockLen / 8)
+	blockMAX      = uint32(1<%s", s.block, s.count, nextBlock)
+}
+
+// GetAvailableBit returns the position of the first unset bit in the bitmask represented by this sequence
+func (s *sequence) getAvailableBit(from uint64) (uint64, uint64, error) {
+	if s.block == blockMAX || s.count == 0 {
+		return invalidPos, invalidPos, ErrNoBitAvailable
+	}
+	bits := from
+	bitSel := blockFirstBit >> from
+	for bitSel > 0 && s.block&bitSel != 0 {
+		bitSel >>= 1
+		bits++
+	}
+	// Check if the loop exited because it could not
+	// find any available bit int block  starting from
+	// "from". Return invalid pos in that case.
+	if bitSel == 0 {
+		return invalidPos, invalidPos, ErrNoBitAvailable
+	}
+	return bits / 8, bits % 8, nil
+}
+
+// GetCopy returns a copy of the linked list rooted at this node
+func (s *sequence) getCopy() *sequence {
+	n := &sequence{block: s.block, count: s.count}
+	pn := n
+	ps := s.next
+	for ps != nil {
+		pn.next = &sequence{block: ps.block, count: ps.count}
+		pn = pn.next
+		ps = ps.next
+	}
+	return n
+}
+
+// Equal checks if this sequence is equal to the passed one
+func (s *sequence) equal(o *sequence) bool {
+	this := s
+	other := o
+	for this != nil {
+		if other == nil {
+			return false
+		}
+		if this.block != other.block || this.count != other.count {
+			return false
+		}
+		this = this.next
+		other = other.next
+	}
+	return other == nil
+}
+
+// ToByteArray converts the sequence into a byte array
+func (s *sequence) toByteArray() ([]byte, error) {
+	var bb []byte
+
+	p := s
+	for p != nil {
+		b := make([]byte, 12)
+		binary.BigEndian.PutUint32(b[0:], p.block)
+		binary.BigEndian.PutUint64(b[4:], p.count)
+		bb = append(bb, b...)
+		p = p.next
+	}
+
+	return bb, nil
+}
+
+// fromByteArray construct the sequence from the byte array
+func (s *sequence) fromByteArray(data []byte) error {
+	l := len(data)
+	if l%12 != 0 {
+		return fmt.Errorf("cannot deserialize byte sequence of length %d (%v)", l, data)
+	}
+
+	p := s
+	i := 0
+	for {
+		p.block = binary.BigEndian.Uint32(data[i : i+4])
+		p.count = binary.BigEndian.Uint64(data[i+4 : i+12])
+		i += 12
+		if i == l {
+			break
+		}
+		p.next = &sequence{}
+		p = p.next
+	}
+
+	return nil
+}
+
+func (h *Handle) getCopy() *Handle {
+	return &Handle{
+		bits:       h.bits,
+		unselected: h.unselected,
+		head:       h.head.getCopy(),
+		app:        h.app,
+		id:         h.id,
+		dbIndex:    h.dbIndex,
+		dbExists:   h.dbExists,
+		store:      h.store,
+		curr:       h.curr,
+	}
+}
+
+// SetAnyInRange atomically sets the first unset bit in the specified range in the sequence and returns the corresponding ordinal
+func (h *Handle) SetAnyInRange(start, end uint64, serial bool) (uint64, error) {
+	if end < start || end >= h.bits {
+		return invalidPos, fmt.Errorf("invalid bit range [%d, %d]", start, end)
+	}
+	if h.Unselected() == 0 {
+		return invalidPos, ErrNoBitAvailable
+	}
+	return h.set(0, start, end, true, false, serial)
+}
+
+// SetAny atomically sets the first unset bit in the sequence and returns the corresponding ordinal
+func (h *Handle) SetAny(serial bool) (uint64, error) {
+	if h.Unselected() == 0 {
+		return invalidPos, ErrNoBitAvailable
+	}
+	return h.set(0, 0, h.bits-1, true, false, serial)
+}
+
+// Set atomically sets the corresponding bit in the sequence
+func (h *Handle) Set(ordinal uint64) error {
+	if err := h.validateOrdinal(ordinal); err != nil {
+		return err
+	}
+	_, err := h.set(ordinal, 0, 0, false, false, false)
+	return err
+}
+
+// Unset atomically unsets the corresponding bit in the sequence
+func (h *Handle) Unset(ordinal uint64) error {
+	if err := h.validateOrdinal(ordinal); err != nil {
+		return err
+	}
+	_, err := h.set(ordinal, 0, 0, false, true, false)
+	return err
+}
+
+// IsSet atomically checks if the ordinal bit is set. In case ordinal
+// is outside of the bit sequence limits, false is returned.
+func (h *Handle) IsSet(ordinal uint64) bool {
+	if err := h.validateOrdinal(ordinal); err != nil {
+		return false
+	}
+	h.Lock()
+	_, _, err := checkIfAvailable(h.head, ordinal)
+	h.Unlock()
+	return err != nil
+}
+
+func (h *Handle) runConsistencyCheck() bool {
+	corrupted := false
+	for p, c := h.head, h.head.next; c != nil; c = c.next {
+		if c.count == 0 {
+			corrupted = true
+			p.next = c.next
+			continue // keep same p
+		}
+		p = c
+	}
+	return corrupted
+}
+
+// CheckConsistency checks if the bit sequence is in an inconsistent state and attempts to fix it.
+// It looks for a corruption signature that may happen in docker 1.9.0 and 1.9.1.
+func (h *Handle) CheckConsistency() error {
+	for {
+		h.Lock()
+		store := h.store
+		h.Unlock()
+
+		if store != nil {
+			if err := store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound {
+				return err
+			}
+		}
+
+		h.Lock()
+		nh := h.getCopy()
+		h.Unlock()
+
+		if !nh.runConsistencyCheck() {
+			return nil
+		}
+
+		if err := nh.writeToStore(); err != nil {
+			if _, ok := err.(types.RetryError); !ok {
+				return fmt.Errorf("internal failure while fixing inconsistent bitsequence: %v", err)
+			}
+			continue
+		}
+
+		logrus.Infof("Fixed inconsistent bit sequence in datastore:\n%s\n%s", h, nh)
+
+		h.Lock()
+		h.head = nh.head
+		h.Unlock()
+
+		return nil
+	}
+}
+
+// set/reset the bit
+func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial bool) (uint64, error) {
+	var (
+		bitPos  uint64
+		bytePos uint64
+		ret     uint64
+		err     error
+	)
+
+	for {
+		var store datastore.DataStore
+		curr := uint64(0)
+		h.Lock()
+		store = h.store
+		if store != nil {
+			h.Unlock() // The lock is acquired in the GetObject
+			if err := store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound {
+				return ret, err
+			}
+			h.Lock() // Acquire the lock back
+		}
+		if serial {
+			curr = h.curr
+		}
+		// Get position if available
+		if release {
+			bytePos, bitPos = ordinalToPos(ordinal)
+		} else {
+			if any {
+				bytePos, bitPos, err = getAvailableFromCurrent(h.head, start, curr, end)
+				ret = posToOrdinal(bytePos, bitPos)
+				if err == nil {
+					h.curr = ret + 1
+				}
+			} else {
+				bytePos, bitPos, err = checkIfAvailable(h.head, ordinal)
+				ret = ordinal
+			}
+		}
+		if err != nil {
+			h.Unlock()
+			return ret, err
+		}
+
+		// Create a private copy of h and work on it
+		nh := h.getCopy()
+
+		nh.head = pushReservation(bytePos, bitPos, nh.head, release)
+		if release {
+			nh.unselected++
+		} else {
+			nh.unselected--
+		}
+
+		if h.store != nil {
+			h.Unlock()
+			// Attempt to write private copy to store
+			if err := nh.writeToStore(); err != nil {
+				if _, ok := err.(types.RetryError); !ok {
+					return ret, fmt.Errorf("internal failure while setting the bit: %v", err)
+				}
+				// Retry
+				continue
+			}
+			h.Lock()
+		}
+
+		// Previous atomic push was successful. Save private copy to local copy
+		h.unselected = nh.unselected
+		h.head = nh.head
+		h.dbExists = nh.dbExists
+		h.dbIndex = nh.dbIndex
+		h.Unlock()
+		return ret, nil
+	}
+}
+
+// checks is needed because to cover the case where the number of bits is not a multiple of blockLen
+func (h *Handle) validateOrdinal(ordinal uint64) error {
+	h.Lock()
+	defer h.Unlock()
+	if ordinal >= h.bits {
+		return errors.New("bit does not belong to the sequence")
+	}
+	return nil
+}
+
+// Destroy removes from the datastore the data belonging to this handle
+func (h *Handle) Destroy() error {
+	for {
+		if err := h.deleteFromStore(); err != nil {
+			if _, ok := err.(types.RetryError); !ok {
+				return fmt.Errorf("internal failure while destroying the sequence: %v", err)
+			}
+			// Fetch latest
+			if err := h.store.GetObject(datastore.Key(h.Key()...), h); err != nil {
+				if err == datastore.ErrKeyNotFound { // already removed
+					return nil
+				}
+				return fmt.Errorf("failed to fetch from store when destroying the sequence: %v", err)
+			}
+			continue
+		}
+		return nil
+	}
+}
+
+// ToByteArray converts this handle's data into a byte array
+func (h *Handle) ToByteArray() ([]byte, error) {
+
+	h.Lock()
+	defer h.Unlock()
+	ba := make([]byte, 16)
+	binary.BigEndian.PutUint64(ba[0:], h.bits)
+	binary.BigEndian.PutUint64(ba[8:], h.unselected)
+	bm, err := h.head.toByteArray()
+	if err != nil {
+		return nil, fmt.Errorf("failed to serialize head: %s", err.Error())
+	}
+	ba = append(ba, bm...)
+
+	return ba, nil
+}
+
+// FromByteArray reads his handle's data from a byte array
+func (h *Handle) FromByteArray(ba []byte) error {
+	if ba == nil {
+		return errors.New("nil byte array")
+	}
+
+	nh := &sequence{}
+	err := nh.fromByteArray(ba[16:])
+	if err != nil {
+		return fmt.Errorf("failed to deserialize head: %s", err.Error())
+	}
+
+	h.Lock()
+	h.head = nh
+	h.bits = binary.BigEndian.Uint64(ba[0:8])
+	h.unselected = binary.BigEndian.Uint64(ba[8:16])
+	h.Unlock()
+
+	return nil
+}
+
+// Bits returns the length of the bit sequence
+func (h *Handle) Bits() uint64 {
+	return h.bits
+}
+
+// Unselected returns the number of bits which are not selected
+func (h *Handle) Unselected() uint64 {
+	h.Lock()
+	defer h.Unlock()
+	return h.unselected
+}
+
+func (h *Handle) String() string {
+	h.Lock()
+	defer h.Unlock()
+	return fmt.Sprintf("App: %s, ID: %s, DBIndex: 0x%x, Bits: %d, Unselected: %d, Sequence: %s Curr:%d",
+		h.app, h.id, h.dbIndex, h.bits, h.unselected, h.head.toString(), h.curr)
+}
+
+// MarshalJSON encodes Handle into json message
+func (h *Handle) MarshalJSON() ([]byte, error) {
+	m := map[string]interface{}{
+		"id": h.id,
+	}
+
+	b, err := h.ToByteArray()
+	if err != nil {
+		return nil, err
+	}
+	m["sequence"] = b
+	return json.Marshal(m)
+}
+
+// UnmarshalJSON decodes json message into Handle
+func (h *Handle) UnmarshalJSON(data []byte) error {
+	var (
+		m   map[string]interface{}
+		b   []byte
+		err error
+	)
+	if err = json.Unmarshal(data, &m); err != nil {
+		return err
+	}
+	h.id = m["id"].(string)
+	bi, _ := json.Marshal(m["sequence"])
+	if err := json.Unmarshal(bi, &b); err != nil {
+		return err
+	}
+	return h.FromByteArray(b)
+}
+
+// getFirstAvailable looks for the first unset bit in passed mask starting from start
+func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) {
+	// Find sequence which contains the start bit
+	byteStart, bitStart := ordinalToPos(start)
+	current, _, precBlocks, inBlockBytePos := findSequence(head, byteStart)
+	// Derive the this sequence offsets
+	byteOffset := byteStart - inBlockBytePos
+	bitOffset := inBlockBytePos*8 + bitStart
+	for current != nil {
+		if current.block != blockMAX {
+			// If the current block is not full, check if there is any bit
+			// from the current bit in the current block. If not, before proceeding to the
+			// next block node, make sure we check for available bit in the next
+			// instance of the same block. Due to RLE same block signature will be
+			// compressed.
+		retry:
+			bytePos, bitPos, err := current.getAvailableBit(bitOffset)
+			if err != nil && precBlocks == current.count-1 {
+				// This is the last instance in the same block node,
+				// so move to the next block.
+				goto next
+			}
+			if err != nil {
+				// There are some more instances of the same block, so add the offset
+				// and be optimistic that you will find the available bit in the next
+				// instance of the same block.
+				bitOffset = 0
+				byteOffset += blockBytes
+				precBlocks++
+				goto retry
+			}
+			return byteOffset + bytePos, bitPos, err
+		}
+		// Moving to next block: Reset bit offset.
+	next:
+		bitOffset = 0
+		byteOffset += (current.count * blockBytes) - (precBlocks * blockBytes)
+		precBlocks = 0
+		current = current.next
+	}
+	return invalidPos, invalidPos, ErrNoBitAvailable
+}
+
+// getAvailableFromCurrent will look for available ordinal from the current ordinal.
+// If none found then it will loop back to the start to check of the available bit.
+// This can be further optimized to check from start till curr in case of a rollover
+func getAvailableFromCurrent(head *sequence, start, curr, end uint64) (uint64, uint64, error) {
+	var bytePos, bitPos uint64
+	var err error
+	if curr != 0 && curr > start {
+		bytePos, bitPos, err = getFirstAvailable(head, curr)
+		ret := posToOrdinal(bytePos, bitPos)
+		if end < ret || err != nil {
+			goto begin
+		}
+		return bytePos, bitPos, nil
+	}
+
+begin:
+	bytePos, bitPos, err = getFirstAvailable(head, start)
+	ret := posToOrdinal(bytePos, bitPos)
+	if end < ret || err != nil {
+		return invalidPos, invalidPos, ErrNoBitAvailable
+	}
+	return bytePos, bitPos, nil
+}
+
+// checkIfAvailable checks if the bit correspondent to the specified ordinal is unset
+// If the ordinal is beyond the sequence limits, a negative response is returned
+func checkIfAvailable(head *sequence, ordinal uint64) (uint64, uint64, error) {
+	bytePos, bitPos := ordinalToPos(ordinal)
+
+	// Find the sequence containing this byte
+	current, _, _, inBlockBytePos := findSequence(head, bytePos)
+	if current != nil {
+		// Check whether the bit corresponding to the ordinal address is unset
+		bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos)
+		if current.block&bitSel == 0 {
+			return bytePos, bitPos, nil
+		}
+	}
+
+	return invalidPos, invalidPos, ErrBitAllocated
+}
+
+// Given the byte position and the sequences list head, return the pointer to the
+// sequence containing the byte (current), the pointer to the previous sequence,
+// the number of blocks preceding the block containing the byte inside the current sequence.
+// If bytePos is outside of the list, function will return (nil, nil, 0, invalidPos)
+func findSequence(head *sequence, bytePos uint64) (*sequence, *sequence, uint64, uint64) {
+	// Find the sequence containing this byte
+	previous := head
+	current := head
+	n := bytePos
+	for current.next != nil && n >= (current.count*blockBytes) { // Nil check for less than 32 addresses masks
+		n -= (current.count * blockBytes)
+		previous = current
+		current = current.next
+	}
+
+	// If byte is outside of the list, let caller know
+	if n >= (current.count * blockBytes) {
+		return nil, nil, 0, invalidPos
+	}
+
+	// Find the byte position inside the block and the number of blocks
+	// preceding the block containing the byte inside this sequence
+	precBlocks := n / blockBytes
+	inBlockBytePos := bytePos % blockBytes
+
+	return current, previous, precBlocks, inBlockBytePos
+}
+
+// PushReservation pushes the bit reservation inside the bitmask.
+// Given byte and bit positions, identify the sequence (current) which holds the block containing the affected bit.
+// Create a new block with the modified bit according to the operation (allocate/release).
+// Create a new sequence containing the new block and insert it in the proper position.
+// Remove current sequence if empty.
+// Check if new sequence can be merged with neighbour (previous/next) sequences.
+//
+//
+// Identify "current" sequence containing block:
+//                                      [prev seq] [current seq] [next seq]
+//
+// Based on block position, resulting list of sequences can be any of three forms:
+//
+//        block position                        Resulting list of sequences
+// A) block is first in current:         [prev seq] [new] [modified current seq] [next seq]
+// B) block is last in current:          [prev seq] [modified current seq] [new] [next seq]
+// C) block is in the middle of current: [prev seq] [curr pre] [new] [curr post] [next seq]
+func pushReservation(bytePos, bitPos uint64, head *sequence, release bool) *sequence {
+	// Store list's head
+	newHead := head
+
+	// Find the sequence containing this byte
+	current, previous, precBlocks, inBlockBytePos := findSequence(head, bytePos)
+	if current == nil {
+		return newHead
+	}
+
+	// Construct updated block
+	bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos)
+	newBlock := current.block
+	if release {
+		newBlock &^= bitSel
+	} else {
+		newBlock |= bitSel
+	}
+
+	// Quit if it was a redundant request
+	if current.block == newBlock {
+		return newHead
+	}
+
+	// Current sequence inevitably looses one block, upadate count
+	current.count--
+
+	// Create new sequence
+	newSequence := &sequence{block: newBlock, count: 1}
+
+	// Insert the new sequence in the list based on block position
+	if precBlocks == 0 { // First in sequence (A)
+		newSequence.next = current
+		if current == head {
+			newHead = newSequence
+			previous = newHead
+		} else {
+			previous.next = newSequence
+		}
+		removeCurrentIfEmpty(&newHead, newSequence, current)
+		mergeSequences(previous)
+	} else if precBlocks == current.count { // Last in sequence (B)
+		newSequence.next = current.next
+		current.next = newSequence
+		mergeSequences(current)
+	} else { // In between the sequence (C)
+		currPre := &sequence{block: current.block, count: precBlocks, next: newSequence}
+		currPost := current
+		currPost.count -= precBlocks
+		newSequence.next = currPost
+		if currPost == head {
+			newHead = currPre
+		} else {
+			previous.next = currPre
+		}
+		// No merging or empty current possible here
+	}
+
+	return newHead
+}
+
+// Removes the current sequence from the list if empty, adjusting the head pointer if needed
+func removeCurrentIfEmpty(head **sequence, previous, current *sequence) {
+	if current.count == 0 {
+		if current == *head {
+			*head = current.next
+		} else {
+			previous.next = current.next
+		}
+	}
+}
+
+// Given a pointer to a sequence, it checks if it can be merged with any following sequences
+// It stops when no more merging is possible.
+// TODO: Optimization: only attempt merge from start to end sequence, no need to scan till the end of the list
+func mergeSequences(seq *sequence) {
+	if seq != nil {
+		// Merge all what possible from seq
+		for seq.next != nil && seq.block == seq.next.block {
+			seq.count += seq.next.count
+			seq.next = seq.next.next
+		}
+		// Move to next
+		mergeSequences(seq.next)
+	}
+}
+
+func getNumBlocks(numBits uint64) uint64 {
+	numBlocks := numBits / uint64(blockLen)
+	if numBits%uint64(blockLen) != 0 {
+		numBlocks++
+	}
+	return numBlocks
+}
+
+func ordinalToPos(ordinal uint64) (uint64, uint64) {
+	return ordinal / 8, ordinal % 8
+}
+
+func posToOrdinal(bytePos, bitPos uint64) uint64 {
+	return bytePos*8 + bitPos
+}
diff --git a/libnetwork/bitseq/sequence_test.go b/libnetwork/bitseq/sequence_test.go
new file mode 100644
index 0000000000000..6ba671ce62019
--- /dev/null
+++ b/libnetwork/bitseq/sequence_test.go
@@ -0,0 +1,1361 @@
+package bitseq
+
+import (
+	"fmt"
+	"math/rand"
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/libnetwork/datastore"
+	"github.com/docker/libkv/store"
+	"github.com/docker/libkv/store/boltdb"
+)
+
+var (
+	defaultPrefix = filepath.Join(os.TempDir(), "libnetwork", "test", "bitseq")
+)
+
+func init() {
+	boltdb.Register()
+}
+
+func randomLocalStore() (datastore.DataStore, error) {
+	tmp, err := os.CreateTemp("", "libnetwork-")
+	if err != nil {
+		return nil, fmt.Errorf("Error creating temp file: %v", err)
+	}
+	if err := tmp.Close(); err != nil {
+		return nil, fmt.Errorf("Error closing temp file: %v", err)
+	}
+	return datastore.NewDataStore(datastore.LocalScope, &datastore.ScopeCfg{
+		Client: datastore.ScopeClientCfg{
+			Provider: "boltdb",
+			Address:  filepath.Join(defaultPrefix, filepath.Base(tmp.Name())),
+			Config: &store.Config{
+				Bucket:            "libnetwork",
+				ConnectionTimeout: 3 * time.Second,
+			},
+		},
+	})
+}
+
+func TestSequenceGetAvailableBit(t *testing.T) {
+	input := []struct {
+		head    *sequence
+		from    uint64
+		bytePos uint64
+		bitPos  uint64
+	}{
+		{&sequence{block: 0x0, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0x0, count: 1}, 0, 0, 0},
+		{&sequence{block: 0x0, count: 100}, 0, 0, 0},
+
+		{&sequence{block: 0x80000000, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0x80000000, count: 1}, 0, 0, 1},
+		{&sequence{block: 0x80000000, count: 100}, 0, 0, 1},
+
+		{&sequence{block: 0xFF000000, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFF000000, count: 1}, 0, 1, 0},
+		{&sequence{block: 0xFF000000, count: 100}, 0, 1, 0},
+
+		{&sequence{block: 0xFF800000, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFF800000, count: 1}, 0, 1, 1},
+		{&sequence{block: 0xFF800000, count: 100}, 0, 1, 1},
+
+		{&sequence{block: 0xFFC0FF00, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFFC0FF00, count: 1}, 0, 1, 2},
+		{&sequence{block: 0xFFC0FF00, count: 100}, 0, 1, 2},
+
+		{&sequence{block: 0xFFE0FF00, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFFE0FF00, count: 1}, 0, 1, 3},
+		{&sequence{block: 0xFFE0FF00, count: 100}, 0, 1, 3},
+
+		{&sequence{block: 0xFFFEFF00, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFFFEFF00, count: 1}, 0, 1, 7},
+		{&sequence{block: 0xFFFEFF00, count: 100}, 0, 1, 7},
+
+		{&sequence{block: 0xFFFFC0FF, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFFFFC0FF, count: 1}, 0, 2, 2},
+		{&sequence{block: 0xFFFFC0FF, count: 100}, 0, 2, 2},
+
+		{&sequence{block: 0xFFFFFF00, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFFFFFF00, count: 1}, 0, 3, 0},
+		{&sequence{block: 0xFFFFFF00, count: 100}, 0, 3, 0},
+
+		{&sequence{block: 0xFFFFFFFE, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFFFFFFFE, count: 1}, 0, 3, 7},
+		{&sequence{block: 0xFFFFFFFE, count: 100}, 0, 3, 7},
+
+		{&sequence{block: 0xFFFFFFFF, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFFFFFFFF, count: 1}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFFFFFFFF, count: 100}, 0, invalidPos, invalidPos},
+
+		// now test with offset
+		{&sequence{block: 0x0, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0x0, count: 0}, 31, invalidPos, invalidPos},
+		{&sequence{block: 0x0, count: 0}, 32, invalidPos, invalidPos},
+		{&sequence{block: 0x0, count: 1}, 0, 0, 0},
+		{&sequence{block: 0x0, count: 1}, 1, 0, 1},
+		{&sequence{block: 0x0, count: 1}, 31, 3, 7},
+		{&sequence{block: 0xF0FF0000, count: 1}, 0, 0, 4},
+		{&sequence{block: 0xF0FF0000, count: 1}, 8, 2, 0},
+		{&sequence{block: 0xFFFFFFFF, count: 1}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFFFFFFFF, count: 1}, 16, invalidPos, invalidPos},
+		{&sequence{block: 0xFFFFFFFF, count: 1}, 31, invalidPos, invalidPos},
+		{&sequence{block: 0xFFFFFFFE, count: 1}, 0, 3, 7},
+		{&sequence{block: 0xFFFFFFFF, count: 2}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xFFFFFFFF, count: 2}, 32, invalidPos, invalidPos},
+	}
+
+	for n, i := range input {
+		b, bb, err := i.head.getAvailableBit(i.from)
+		if b != i.bytePos || bb != i.bitPos {
+			t.Fatalf("Error in sequence.getAvailableBit(%d) (%d).\nExp: (%d, %d)\nGot: (%d, %d), err: %v", i.from, n, i.bytePos, i.bitPos, b, bb, err)
+		}
+	}
+}
+
+func TestSequenceEqual(t *testing.T) {
+	input := []struct {
+		first    *sequence
+		second   *sequence
+		areEqual bool
+	}{
+		{&sequence{block: 0x0, count: 8, next: nil}, &sequence{block: 0x0, count: 8}, true},
+		{&sequence{block: 0x0, count: 0, next: nil}, &sequence{block: 0x0, count: 0}, true},
+		{&sequence{block: 0x0, count: 2, next: nil}, &sequence{block: 0x0, count: 1, next: &sequence{block: 0x0, count: 1}}, false},
+		{&sequence{block: 0x0, count: 2, next: &sequence{block: 0x1, count: 1}}, &sequence{block: 0x0, count: 2}, false},
+
+		{&sequence{block: 0x12345678, count: 8, next: nil}, &sequence{block: 0x12345678, count: 8}, true},
+		{&sequence{block: 0x12345678, count: 8, next: nil}, &sequence{block: 0x12345678, count: 9}, false},
+		{&sequence{block: 0x12345678, count: 1, next: &sequence{block: 0xFFFFFFFF, count: 1}}, &sequence{block: 0x12345678, count: 1}, false},
+		{&sequence{block: 0x12345678, count: 1}, &sequence{block: 0x12345678, count: 1, next: &sequence{block: 0xFFFFFFFF, count: 1}}, false},
+	}
+
+	for n, i := range input {
+		if i.areEqual != i.first.equal(i.second) {
+			t.Fatalf("Error in sequence.equal() (%d).\nExp: %t\nGot: %t,", n, i.areEqual, !i.areEqual)
+		}
+	}
+}
+
+func TestSequenceCopy(t *testing.T) {
+	s := getTestSequence()
+	n := s.getCopy()
+	if !s.equal(n) {
+		t.Fatal("copy of s failed")
+	}
+	if n == s {
+		t.Fatal("not true copy of s")
+	}
+}
+
+func TestGetFirstAvailable(t *testing.T) {
+	input := []struct {
+		mask    *sequence
+		bytePos uint64
+		bitPos  uint64
+		start   uint64
+	}{
+		{&sequence{block: 0xffffffff, count: 2048}, invalidPos, invalidPos, 0},
+		{&sequence{block: 0x0, count: 8}, 0, 0, 0},
+		{&sequence{block: 0x80000000, count: 8}, 0, 1, 0},
+		{&sequence{block: 0xC0000000, count: 8}, 0, 2, 0},
+		{&sequence{block: 0xE0000000, count: 8}, 0, 3, 0},
+		{&sequence{block: 0xF0000000, count: 8}, 0, 4, 0},
+		{&sequence{block: 0xF8000000, count: 8}, 0, 5, 0},
+		{&sequence{block: 0xFC000000, count: 8}, 0, 6, 0},
+		{&sequence{block: 0xFE000000, count: 8}, 0, 7, 0},
+		{&sequence{block: 0xFE000000, count: 8}, 3, 0, 24},
+
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x00000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 0, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 1, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 2, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xE0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 3, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 4, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF8000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 5, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFC000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 6, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFE000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 7, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x0E000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 0, 16},
+
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 0, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF800000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 1, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFC00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 2, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFE00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 3, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 4, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF80000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 5, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFC0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 6, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFE0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 7, 0},
+
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 7, 7, 0},
+
+		{&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0x0, count: 6}}, 8, 0, 0},
+		{&sequence{block: 0xfffcffff, count: 1, next: &sequence{block: 0x0, count: 6}}, 4, 0, 16},
+		{&sequence{block: 0xfffcffff, count: 1, next: &sequence{block: 0x0, count: 6}}, 1, 7, 15},
+		{&sequence{block: 0xfffcffff, count: 1, next: &sequence{block: 0x0, count: 6}}, 1, 6, 10},
+		{&sequence{block: 0xfffcfffe, count: 1, next: &sequence{block: 0x0, count: 6}}, 3, 7, 31},
+		{&sequence{block: 0xfffcffff, count: 1, next: &sequence{block: 0xffffffff, count: 6}}, invalidPos, invalidPos, 31},
+	}
+
+	for n, i := range input {
+		bytePos, bitPos, _ := getFirstAvailable(i.mask, i.start)
+		if bytePos != i.bytePos || bitPos != i.bitPos {
+			t.Fatalf("Error in (%d) getFirstAvailable(). Expected (%d, %d). Got (%d, %d)", n, i.bytePos, i.bitPos, bytePos, bitPos)
+		}
+	}
+}
+
+func TestFindSequence(t *testing.T) {
+	input := []struct {
+		head           *sequence
+		bytePos        uint64
+		precBlocks     uint64
+		inBlockBytePos uint64
+	}{
+		{&sequence{block: 0xffffffff, count: 0}, 0, 0, invalidPos},
+		{&sequence{block: 0xffffffff, count: 0}, 31, 0, invalidPos},
+		{&sequence{block: 0xffffffff, count: 0}, 100, 0, invalidPos},
+
+		{&sequence{block: 0x0, count: 1}, 0, 0, 0},
+		{&sequence{block: 0x0, count: 1}, 1, 0, 1},
+		{&sequence{block: 0x0, count: 1}, 31, 0, invalidPos},
+		{&sequence{block: 0x0, count: 1}, 60, 0, invalidPos},
+
+		{&sequence{block: 0xffffffff, count: 10}, 0, 0, 0},
+		{&sequence{block: 0xffffffff, count: 10}, 3, 0, 3},
+		{&sequence{block: 0xffffffff, count: 10}, 4, 1, 0},
+		{&sequence{block: 0xffffffff, count: 10}, 7, 1, 3},
+		{&sequence{block: 0xffffffff, count: 10}, 8, 2, 0},
+		{&sequence{block: 0xffffffff, count: 10}, 39, 9, 3},
+
+		{&sequence{block: 0xffffffff, count: 10, next: &sequence{block: 0xcc000000, count: 10}}, 79, 9, 3},
+		{&sequence{block: 0xffffffff, count: 10, next: &sequence{block: 0xcc000000, count: 10}}, 80, 0, invalidPos},
+	}
+
+	for n, i := range input {
+		_, _, precBlocks, inBlockBytePos := findSequence(i.head, i.bytePos)
+		if precBlocks != i.precBlocks || inBlockBytePos != i.inBlockBytePos {
+			t.Fatalf("Error in (%d) findSequence(). Expected (%d, %d). Got (%d, %d)", n, i.precBlocks, i.inBlockBytePos, precBlocks, inBlockBytePos)
+		}
+	}
+}
+
+func TestCheckIfAvailable(t *testing.T) {
+	input := []struct {
+		head    *sequence
+		ordinal uint64
+		bytePos uint64
+		bitPos  uint64
+	}{
+		{&sequence{block: 0xffffffff, count: 0}, 0, invalidPos, invalidPos},
+		{&sequence{block: 0xffffffff, count: 0}, 31, invalidPos, invalidPos},
+		{&sequence{block: 0xffffffff, count: 0}, 100, invalidPos, invalidPos},
+
+		{&sequence{block: 0x0, count: 1}, 0, 0, 0},
+		{&sequence{block: 0x0, count: 1}, 1, 0, 1},
+		{&sequence{block: 0x0, count: 1}, 31, 3, 7},
+		{&sequence{block: 0x0, count: 1}, 60, invalidPos, invalidPos},
+
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x800000ff, count: 1}}, 31, invalidPos, invalidPos},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x800000ff, count: 1}}, 32, invalidPos, invalidPos},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x800000ff, count: 1}}, 33, 4, 1},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1}}, 33, invalidPos, invalidPos},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1}}, 34, 4, 2},
+
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 55, 6, 7},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 56, invalidPos, invalidPos},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 63, invalidPos, invalidPos},
+
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 64, 8, 0},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 95, 11, 7},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC00000ff, count: 1, next: &sequence{block: 0x0, count: 1}}}, 96, invalidPos, invalidPos},
+	}
+
+	for n, i := range input {
+		bytePos, bitPos, err := checkIfAvailable(i.head, i.ordinal)
+		if bytePos != i.bytePos || bitPos != i.bitPos {
+			t.Fatalf("Error in (%d) checkIfAvailable(ord:%d). Expected (%d, %d). Got (%d, %d). err: %v", n, i.ordinal, i.bytePos, i.bitPos, bytePos, bitPos, err)
+		}
+	}
+}
+
+func TestMergeSequences(t *testing.T) {
+	input := []struct {
+		original *sequence
+		merged   *sequence
+	}{
+		{&sequence{block: 0xFE000000, count: 8, next: &sequence{block: 0xFE000000, count: 2}}, &sequence{block: 0xFE000000, count: 10}},
+		{&sequence{block: 0xFFFFFFFF, count: 8, next: &sequence{block: 0xFFFFFFFF, count: 1}}, &sequence{block: 0xFFFFFFFF, count: 9}},
+		{&sequence{block: 0xFFFFFFFF, count: 1, next: &sequence{block: 0xFFFFFFFF, count: 8}}, &sequence{block: 0xFFFFFFFF, count: 9}},
+
+		{&sequence{block: 0xFFFFFFF0, count: 8, next: &sequence{block: 0xFFFFFFF0, count: 1}}, &sequence{block: 0xFFFFFFF0, count: 9}},
+		{&sequence{block: 0xFFFFFFF0, count: 1, next: &sequence{block: 0xFFFFFFF0, count: 8}}, &sequence{block: 0xFFFFFFF0, count: 9}},
+
+		{&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xFE, count: 1, next: &sequence{block: 0xFE, count: 5}}}, &sequence{block: 0xFE, count: 14}},
+		{&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xFE, count: 1, next: &sequence{block: 0xFE, count: 5, next: &sequence{block: 0xFF, count: 1}}}},
+			&sequence{block: 0xFE, count: 14, next: &sequence{block: 0xFF, count: 1}}},
+
+		// No merge
+		{&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xF8, count: 1, next: &sequence{block: 0xFE, count: 5}}},
+			&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xF8, count: 1, next: &sequence{block: 0xFE, count: 5}}}},
+
+		// No merge from head: // Merge function tries to merge from passed head. If it can't merge with next, it does not reattempt with next as head
+		{&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xFF, count: 1, next: &sequence{block: 0xFF, count: 5}}},
+			&sequence{block: 0xFE, count: 8, next: &sequence{block: 0xFF, count: 6}}},
+	}
+
+	for n, i := range input {
+		mergeSequences(i.original)
+		for !i.merged.equal(i.original) {
+			t.Fatalf("Error in (%d) mergeSequences().\nExp: %s\nGot: %s,", n, i.merged.toString(), i.original.toString())
+		}
+	}
+}
+
+func TestPushReservation(t *testing.T) {
+	input := []struct {
+		mask    *sequence
+		bytePos uint64
+		bitPos  uint64
+		newMask *sequence
+	}{
+		// Create first sequence and fill in 8 addresses starting from address 0
+		{&sequence{block: 0x0, count: 8, next: nil}, 0, 0, &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 7, next: nil}}},
+		{&sequence{block: 0x80000000, count: 8}, 0, 1, &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0x80000000, count: 7, next: nil}}},
+		{&sequence{block: 0xC0000000, count: 8}, 0, 2, &sequence{block: 0xE0000000, count: 1, next: &sequence{block: 0xC0000000, count: 7, next: nil}}},
+		{&sequence{block: 0xE0000000, count: 8}, 0, 3, &sequence{block: 0xF0000000, count: 1, next: &sequence{block: 0xE0000000, count: 7, next: nil}}},
+		{&sequence{block: 0xF0000000, count: 8}, 0, 4, &sequence{block: 0xF8000000, count: 1, next: &sequence{block: 0xF0000000, count: 7, next: nil}}},
+		{&sequence{block: 0xF8000000, count: 8}, 0, 5, &sequence{block: 0xFC000000, count: 1, next: &sequence{block: 0xF8000000, count: 7, next: nil}}},
+		{&sequence{block: 0xFC000000, count: 8}, 0, 6, &sequence{block: 0xFE000000, count: 1, next: &sequence{block: 0xFC000000, count: 7, next: nil}}},
+		{&sequence{block: 0xFE000000, count: 8}, 0, 7, &sequence{block: 0xFF000000, count: 1, next: &sequence{block: 0xFE000000, count: 7, next: nil}}},
+
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 7}}, 0, 1, &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0x0, count: 7, next: nil}}},
+
+		// Create second sequence and fill in 8 addresses starting from address 32
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x00000000, count: 1, next: &sequence{block: 0xffffffff, count: 6, next: nil}}}, 4, 0,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 1,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 2,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xE0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xE0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 3,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF0000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 4,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF8000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xF8000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 5,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFC000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFC000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 6,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFE000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFE000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 4, 7,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		// fill in 8 addresses starting from address 40
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF000000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 0,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF800000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFF800000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 1,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFC00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFC00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 2,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFE00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFE00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 3,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF00000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 4,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF80000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFF80000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 5,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFC0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFC0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 6,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFE0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFE0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}, 5, 7,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xFFFF0000, count: 1, next: &sequence{block: 0xffffffff, count: 6}}}},
+
+		// Insert new sequence
+		{&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0x0, count: 6}}, 8, 0,
+			&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 5}}}},
+		{&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 5}}}, 8, 1,
+			&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0xC0000000, count: 1, next: &sequence{block: 0x0, count: 5}}}},
+
+		// Merge affected with next
+		{&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 2, next: &sequence{block: 0xffffffff, count: 1}}}, 31, 7,
+			&sequence{block: 0xffffffff, count: 8, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 1}}}},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xfffffffc, count: 1, next: &sequence{block: 0xfffffffe, count: 6}}}, 7, 6,
+			&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xfffffffe, count: 7}}},
+
+		// Merge affected with next and next.next
+		{&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 1}}}, 31, 7,
+			&sequence{block: 0xffffffff, count: 9}},
+		{&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 1}}, 31, 7,
+			&sequence{block: 0xffffffff, count: 8}},
+
+		// Merge affected with previous and next
+		{&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 1}}}, 31, 7,
+			&sequence{block: 0xffffffff, count: 9}},
+
+		// Redundant push: No change
+		{&sequence{block: 0xffff0000, count: 1}, 0, 0, &sequence{block: 0xffff0000, count: 1}},
+		{&sequence{block: 0xffff0000, count: 7}, 25, 7, &sequence{block: 0xffff0000, count: 7}},
+		{&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 1}}}, 7, 7,
+			&sequence{block: 0xffffffff, count: 7, next: &sequence{block: 0xfffffffe, count: 1, next: &sequence{block: 0xffffffff, count: 1}}}},
+
+		// Set last bit
+		{&sequence{block: 0x0, count: 8}, 31, 7, &sequence{block: 0x0, count: 7, next: &sequence{block: 0x1, count: 1}}},
+
+		// Set bit in a middle sequence in the first block, first bit
+		{&sequence{block: 0x40000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x1, count: 1}}}, 4, 0,
+			&sequence{block: 0x40000000, count: 1, next: &sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 5,
+				next: &sequence{block: 0x1, count: 1}}}}},
+
+		// Set bit in a middle sequence in the first block, first bit (merge involved)
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x1, count: 1}}}, 4, 0,
+			&sequence{block: 0x80000000, count: 2, next: &sequence{block: 0x0, count: 5, next: &sequence{block: 0x1, count: 1}}}},
+
+		// Set bit in a middle sequence in the first block, last bit
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x1, count: 1}}}, 4, 31,
+			&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x1, count: 1, next: &sequence{block: 0x0, count: 5,
+				next: &sequence{block: 0x1, count: 1}}}}},
+
+		// Set bit in a middle sequence in the first block, middle bit
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x1, count: 1}}}, 4, 16,
+			&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x8000, count: 1, next: &sequence{block: 0x0, count: 5,
+				next: &sequence{block: 0x1, count: 1}}}}},
+
+		// Set bit in a middle sequence in a middle block, first bit
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x1, count: 1}}}, 16, 0,
+			&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 3, next: &sequence{block: 0x80000000, count: 1,
+				next: &sequence{block: 0x0, count: 2, next: &sequence{block: 0x1, count: 1}}}}}},
+
+		// Set bit in a middle sequence in a middle block, last bit
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x1, count: 1}}}, 16, 31,
+			&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 3, next: &sequence{block: 0x1, count: 1,
+				next: &sequence{block: 0x0, count: 2, next: &sequence{block: 0x1, count: 1}}}}}},
+
+		// Set bit in a middle sequence in a middle block, middle bit
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x1, count: 1}}}, 16, 15,
+			&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 3, next: &sequence{block: 0x10000, count: 1,
+				next: &sequence{block: 0x0, count: 2, next: &sequence{block: 0x1, count: 1}}}}}},
+
+		// Set bit in a middle sequence in the last block, first bit
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x1, count: 1}}}, 24, 0,
+			&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 5, next: &sequence{block: 0x80000000, count: 1,
+				next: &sequence{block: 0x1, count: 1}}}}},
+
+		// Set bit in a middle sequence in the last block, last bit
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x4, count: 1}}}, 24, 31,
+			&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 5, next: &sequence{block: 0x1, count: 1,
+				next: &sequence{block: 0x4, count: 1}}}}},
+
+		// Set bit in a middle sequence in the last block, last bit (merge involved)
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x1, count: 1}}}, 24, 31,
+			&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 5, next: &sequence{block: 0x1, count: 2}}}},
+
+		// Set bit in a middle sequence in the last block, middle bit
+		{&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 6, next: &sequence{block: 0x1, count: 1}}}, 24, 16,
+			&sequence{block: 0x80000000, count: 1, next: &sequence{block: 0x0, count: 5, next: &sequence{block: 0x8000, count: 1,
+				next: &sequence{block: 0x1, count: 1}}}}},
+	}
+
+	for n, i := range input {
+		mask := pushReservation(i.bytePos, i.bitPos, i.mask, false)
+		if !mask.equal(i.newMask) {
+			t.Fatalf("Error in (%d) pushReservation():\n%s + (%d,%d):\nExp: %s\nGot: %s,",
+				n, i.mask.toString(), i.bytePos, i.bitPos, i.newMask.toString(), mask.toString())
+		}
+	}
+}
+
+func TestSerializeDeserialize(t *testing.T) {
+	s := getTestSequence()
+
+	data, err := s.toByteArray()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	r := &sequence{}
+	err = r.fromByteArray(data)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !s.equal(r) {
+		t.Fatalf("Sequences are different: \n%v\n%v", s, r)
+	}
+}
+
+func getTestSequence() *sequence {
+	// Returns a custom sequence of 1024 * 32 bits
+	return &sequence{
+		block: 0xFFFFFFFF,
+		count: 100,
+		next: &sequence{
+			block: 0xFFFFFFFE,
+			count: 1,
+			next: &sequence{
+				block: 0xFF000000,
+				count: 10,
+				next: &sequence{
+					block: 0xFFFFFFFF,
+					count: 50,
+					next: &sequence{
+						block: 0xFFFFFFFC,
+						count: 1,
+						next: &sequence{
+							block: 0xFF800000,
+							count: 1,
+							next: &sequence{
+								block: 0xFFFFFFFF,
+								count: 87,
+								next: &sequence{
+									block: 0x0,
+									count: 150,
+									next: &sequence{
+										block: 0xFFFFFFFF,
+										count: 200,
+										next: &sequence{
+											block: 0x0000FFFF,
+											count: 1,
+											next: &sequence{
+												block: 0x0,
+												count: 399,
+												next: &sequence{
+													block: 0xFFFFFFFF,
+													count: 23,
+													next: &sequence{
+														block: 0x1,
+														count: 1,
+													},
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+}
+
+func TestSet(t *testing.T) {
+	hnd, err := NewHandle("", nil, "", 1024*32)
+	if err != nil {
+		t.Fatal(err)
+	}
+	hnd.head = getTestSequence()
+
+	firstAv := uint64(32*100 + 31)
+	last := uint64(1024*32 - 1)
+
+	if hnd.IsSet(100000) {
+		t.Fatal("IsSet() returned wrong result")
+	}
+
+	if !hnd.IsSet(0) {
+		t.Fatal("IsSet() returned wrong result")
+	}
+
+	if hnd.IsSet(firstAv) {
+		t.Fatal("IsSet() returned wrong result")
+	}
+
+	if !hnd.IsSet(last) {
+		t.Fatal("IsSet() returned wrong result")
+	}
+
+	if err := hnd.Set(0); err == nil {
+		t.Fatal("Expected failure, but succeeded")
+	}
+
+	os, err := hnd.SetAny(false)
+	if err != nil {
+		t.Fatalf("Unexpected failure: %v", err)
+	}
+	if os != firstAv {
+		t.Fatalf("SetAny returned unexpected ordinal. Expected %d. Got %d.", firstAv, os)
+	}
+	if !hnd.IsSet(firstAv) {
+		t.Fatal("IsSet() returned wrong result")
+	}
+
+	if err := hnd.Unset(firstAv); err != nil {
+		t.Fatalf("Unexpected failure: %v", err)
+	}
+
+	if hnd.IsSet(firstAv) {
+		t.Fatal("IsSet() returned wrong result")
+	}
+
+	if err := hnd.Set(firstAv); err != nil {
+		t.Fatalf("Unexpected failure: %v", err)
+	}
+
+	if err := hnd.Set(last); err == nil {
+		t.Fatal("Expected failure, but succeeded")
+	}
+}
+
+func TestSetUnset(t *testing.T) {
+	numBits := uint64(32 * blockLen)
+	hnd, err := NewHandle("", nil, "", numBits)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err := hnd.Set(uint64(32 * blockLen)); err == nil {
+		t.Fatal("Expected failure, but succeeded")
+	}
+	if err := hnd.Unset(uint64(32 * blockLen)); err == nil {
+		t.Fatal("Expected failure, but succeeded")
+	}
+
+	// set and unset all one by one
+	for hnd.Unselected() > 0 {
+		if _, err := hnd.SetAny(false); err != nil {
+			t.Fatal(err)
+		}
+	}
+	if _, err := hnd.SetAny(false); err != ErrNoBitAvailable {
+		t.Fatal("Expected error. Got success")
+	}
+	if _, err := hnd.SetAnyInRange(10, 20, false); err != ErrNoBitAvailable {
+		t.Fatal("Expected error. Got success")
+	}
+	if err := hnd.Set(50); err != ErrBitAllocated {
+		t.Fatalf("Expected error. Got %v: %s", err, hnd)
+	}
+	i := uint64(0)
+	for hnd.Unselected() < numBits {
+		if err := hnd.Unset(i); err != nil {
+			t.Fatal(err)
+		}
+		i++
+	}
+}
+
+func TestOffsetSetUnset(t *testing.T) {
+	numBits := uint64(32 * blockLen)
+	var o uint64
+	hnd, err := NewHandle("", nil, "", numBits)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// set and unset all one by one
+	for hnd.Unselected() > 0 {
+		if _, err := hnd.SetAny(false); err != nil {
+			t.Fatal(err)
+		}
+	}
+
+	if _, err := hnd.SetAny(false); err != ErrNoBitAvailable {
+		t.Fatal("Expected error. Got success")
+	}
+
+	if _, err := hnd.SetAnyInRange(10, 20, false); err != ErrNoBitAvailable {
+		t.Fatal("Expected error. Got success")
+	}
+
+	if err := hnd.Unset(288); err != nil {
+		t.Fatal(err)
+	}
+
+	//At this point sequence is (0xffffffff, 9)->(0x7fffffff, 1)->(0xffffffff, 22)->end
+	if o, err = hnd.SetAnyInRange(32, 500, false); err != nil {
+		t.Fatal(err)
+	}
+
+	if o != 288 {
+		t.Fatalf("Expected ordinal not received, Received:%d", o)
+	}
+}
+
+func TestSetInRange(t *testing.T) {
+	numBits := uint64(1024 * blockLen)
+	hnd, err := NewHandle("", nil, "", numBits)
+	if err != nil {
+		t.Fatal(err)
+	}
+	hnd.head = getTestSequence()
+
+	firstAv := uint64(100*blockLen + blockLen - 1)
+
+	if o, err := hnd.SetAnyInRange(4, 3, false); err == nil {
+		t.Fatalf("Expected failure. Got success with ordinal:%d", o)
+	}
+
+	if o, err := hnd.SetAnyInRange(0, numBits, false); err == nil {
+		t.Fatalf("Expected failure. Got success with ordinal:%d", o)
+	}
+
+	o, err := hnd.SetAnyInRange(100*uint64(blockLen), 101*uint64(blockLen), false)
+	if err != nil {
+		t.Fatalf("Unexpected failure: (%d, %v)", o, err)
+	}
+	if o != firstAv {
+		t.Fatalf("Unexpected ordinal: %d", o)
+	}
+
+	if o, err := hnd.SetAnyInRange(0, uint64(blockLen), false); err == nil {
+		t.Fatalf("Expected failure. Got success with ordinal:%d", o)
+	}
+
+	if o, err := hnd.SetAnyInRange(0, firstAv-1, false); err == nil {
+		t.Fatalf("Expected failure. Got success with ordinal:%d", o)
+	}
+
+	if o, err := hnd.SetAnyInRange(111*uint64(blockLen), 161*uint64(blockLen), false); err == nil {
+		t.Fatalf("Expected failure. Got success with ordinal:%d", o)
+	}
+
+	o, err = hnd.SetAnyInRange(161*uint64(blockLen), 162*uint64(blockLen), false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if o != 161*uint64(blockLen)+30 {
+		t.Fatalf("Unexpected ordinal: %d", o)
+	}
+
+	o, err = hnd.SetAnyInRange(161*uint64(blockLen), 162*uint64(blockLen), false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if o != 161*uint64(blockLen)+31 {
+		t.Fatalf("Unexpected ordinal: %d", o)
+	}
+
+	o, err = hnd.SetAnyInRange(161*uint64(blockLen), 162*uint64(blockLen), false)
+	if err == nil {
+		t.Fatalf("Expected failure. Got success with ordinal:%d", o)
+	}
+
+	if _, err := hnd.SetAnyInRange(0, numBits-1, false); err != nil {
+		t.Fatalf("Unexpected failure: %v", err)
+	}
+
+	// set one bit using the set range with 1 bit size range
+	if _, err := hnd.SetAnyInRange(uint64(163*blockLen-1), uint64(163*blockLen-1), false); err != nil {
+		t.Fatal(err)
+	}
+
+	// create a non multiple of 32 mask
+	hnd, err = NewHandle("", nil, "", 30)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// set all bit in the first range
+	for hnd.Unselected() > 22 {
+		if o, err := hnd.SetAnyInRange(0, 7, false); err != nil {
+			t.Fatalf("Unexpected failure: (%d, %v)", o, err)
+		}
+	}
+	// try one more set, which should fail
+	o, err = hnd.SetAnyInRange(0, 7, false)
+	if err == nil {
+		t.Fatalf("Expected failure. Got success with ordinal:%d", o)
+	}
+	if err != ErrNoBitAvailable {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+
+	// set all bit in a second range
+	for hnd.Unselected() > 14 {
+		if o, err := hnd.SetAnyInRange(8, 15, false); err != nil {
+			t.Fatalf("Unexpected failure: (%d, %v)", o, err)
+		}
+	}
+
+	// try one more set, which should fail
+	o, err = hnd.SetAnyInRange(0, 15, false)
+	if err == nil {
+		t.Fatalf("Expected failure. Got success with ordinal:%d", o)
+	}
+	if err != ErrNoBitAvailable {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+
+	// set all bit in a range which includes the last bit
+	for hnd.Unselected() > 12 {
+		if o, err := hnd.SetAnyInRange(28, 29, false); err != nil {
+			t.Fatalf("Unexpected failure: (%d, %v)", o, err)
+		}
+	}
+	o, err = hnd.SetAnyInRange(28, 29, false)
+	if err == nil {
+		t.Fatalf("Expected failure. Got success with ordinal:%d", o)
+	}
+	if err != ErrNoBitAvailable {
+		t.Fatalf("Unexpected error: %v", err)
+	}
+}
+
+// This one tests an allocation pattern which unveiled an issue in pushReservation
+// Specifically a failure in detecting when we are in the (B) case (the bit to set
+// belongs to the last block of the current sequence). Because of a bug, code
+// was assuming the bit belonged to a block in the middle of the current sequence.
+// Which in turn caused an incorrect allocation when requesting a bit which is not
+// in the first or last sequence block.
+func TestSetAnyInRange(t *testing.T) {
+	numBits := uint64(8 * blockLen)
+	hnd, err := NewHandle("", nil, "", numBits)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err := hnd.Set(0); err != nil {
+		t.Fatal(err)
+	}
+
+	if err := hnd.Set(255); err != nil {
+		t.Fatal(err)
+	}
+
+	o, err := hnd.SetAnyInRange(128, 255, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if o != 128 {
+		t.Fatalf("Unexpected ordinal: %d", o)
+	}
+
+	o, err = hnd.SetAnyInRange(128, 255, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if o != 129 {
+		t.Fatalf("Unexpected ordinal: %d", o)
+	}
+
+	o, err = hnd.SetAnyInRange(246, 255, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if o != 246 {
+		t.Fatalf("Unexpected ordinal: %d", o)
+	}
+
+	o, err = hnd.SetAnyInRange(246, 255, false)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if o != 247 {
+		t.Fatalf("Unexpected ordinal: %d", o)
+	}
+}
+
+func TestMethods(t *testing.T) {
+	numBits := uint64(256 * blockLen)
+	hnd, err := NewHandle("path/to/data", nil, "sequence1", numBits)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if hnd.Bits() != numBits {
+		t.Fatalf("Unexpected bit number: %d", hnd.Bits())
+	}
+
+	if hnd.Unselected() != numBits {
+		t.Fatalf("Unexpected bit number: %d", hnd.Unselected())
+	}
+
+	exp := "(0x0, 256)->end"
+	if hnd.head.toString() != exp {
+		t.Fatalf("Unexpected sequence string: %s", hnd.head.toString())
+	}
+
+	for i := 0; i < 192; i++ {
+		_, err := hnd.SetAny(false)
+		if err != nil {
+			t.Fatal(err)
+		}
+	}
+
+	exp = "(0xffffffff, 6)->(0x0, 250)->end"
+	if hnd.head.toString() != exp {
+		t.Fatalf("Unexpected sequence string: %s", hnd.head.toString())
+	}
+}
+
+func TestRandomAllocateDeallocate(t *testing.T) {
+	ds, err := randomLocalStore()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	numBits := int(16 * blockLen)
+	hnd, err := NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	seed := time.Now().Unix()
+	rand.Seed(seed)
+
+	// Allocate all bits using a random pattern
+	pattern := rand.Perm(numBits)
+	for _, bit := range pattern {
+		err := hnd.Set(uint64(bit))
+		if err != nil {
+			t.Fatalf("Unexpected failure on allocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd)
+		}
+	}
+	if hnd.Unselected() != 0 {
+		t.Fatalf("Expected full sequence. Instead found %d free bits. Seed: %d.\n%s", hnd.unselected, seed, hnd)
+	}
+	if hnd.head.toString() != "(0xffffffff, 16)->end" {
+		t.Fatalf("Unexpected db: %s", hnd.head.toString())
+	}
+
+	// Deallocate all bits using a random pattern
+	pattern = rand.Perm(numBits)
+	for _, bit := range pattern {
+		err := hnd.Unset(uint64(bit))
+		if err != nil {
+			t.Fatalf("Unexpected failure on deallocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd)
+		}
+	}
+	if hnd.Unselected() != uint64(numBits) {
+		t.Fatalf("Expected full sequence. Instead found %d free bits. Seed: %d.\n%s", hnd.unselected, seed, hnd)
+	}
+	if hnd.head.toString() != "(0x0, 16)->end" {
+		t.Fatalf("Unexpected db: %s", hnd.head.toString())
+	}
+
+	err = hnd.Destroy()
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestAllocateRandomDeallocate(t *testing.T) {
+	ds, err := randomLocalStore()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	numBlocks := uint32(8)
+	numBits := int(numBlocks * blockLen)
+	hnd, err := NewHandle(filepath.Join("bitseq", "test", "data"), ds, "test1", uint64(numBits))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expected := &sequence{block: 0xffffffff, count: uint64(numBlocks / 2), next: &sequence{block: 0x0, count: uint64(numBlocks / 2)}}
+
+	// Allocate first half of the bits
+	for i := 0; i < numBits/2; i++ {
+		_, err := hnd.SetAny(false)
+		if err != nil {
+			t.Fatalf("Unexpected failure on allocation %d: %v\n%s", i, err, hnd)
+		}
+	}
+	if hnd.Unselected() != uint64(numBits/2) {
+		t.Fatalf("Expected full sequence. Instead found %d free bits. %s", hnd.unselected, hnd)
+	}
+	if !hnd.head.equal(expected) {
+		t.Fatalf("Unexpected sequence. Got:\n%s", hnd)
+	}
+
+	seed := time.Now().Unix()
+	rand.Seed(seed)
+
+	// Deallocate half of the allocated bits following a random pattern
+	pattern := rand.Perm(numBits / 2)
+	for i := 0; i < numBits/4; i++ {
+		bit := pattern[i]
+		err := hnd.Unset(uint64(bit))
+		if err != nil {
+			t.Fatalf("Unexpected failure on deallocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd)
+		}
+	}
+	if hnd.Unselected() != uint64(3*numBits/4) {
+		t.Fatalf("Expected full sequence. Instead found %d free bits.\nSeed: %d.\n%s", hnd.unselected, seed, hnd)
+	}
+
+	// Request a quarter of bits
+	for i := 0; i < numBits/4; i++ {
+		_, err := hnd.SetAny(false)
+		if err != nil {
+			t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd)
+		}
+	}
+	if hnd.Unselected() != uint64(numBits/2) {
+		t.Fatalf("Expected half sequence. Instead found %d free bits.\nSeed: %d\n%s", hnd.unselected, seed, hnd)
+	}
+	if !hnd.head.equal(expected) {
+		t.Fatalf("Unexpected sequence. Got:\n%s", hnd)
+	}
+
+	err = hnd.Destroy()
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestAllocateRandomDeallocateSerialize(t *testing.T) {
+	ds, err := randomLocalStore()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	numBlocks := uint32(8)
+	numBits := int(numBlocks * blockLen)
+	hnd, err := NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expected := &sequence{block: 0xffffffff, count: uint64(numBlocks / 2), next: &sequence{block: 0x0, count: uint64(numBlocks / 2)}}
+
+	// Allocate first half of the bits
+	for i := 0; i < numBits/2; i++ {
+		_, err := hnd.SetAny(true)
+		if err != nil {
+			t.Fatalf("Unexpected failure on allocation %d: %v\n%s", i, err, hnd)
+		}
+	}
+
+	if hnd.Unselected() != uint64(numBits/2) {
+		t.Fatalf("Expected full sequence. Instead found %d free bits. %s", hnd.unselected, hnd)
+	}
+	if !hnd.head.equal(expected) {
+		t.Fatalf("Unexpected sequence. Got:\n%s", hnd)
+	}
+
+	seed := time.Now().Unix()
+	rand.Seed(seed)
+
+	// Deallocate half of the allocated bits following a random pattern
+	pattern := rand.Perm(numBits / 2)
+	for i := 0; i < numBits/4; i++ {
+		bit := pattern[i]
+		err := hnd.Unset(uint64(bit))
+		if err != nil {
+			t.Fatalf("Unexpected failure on deallocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd)
+		}
+	}
+	if hnd.Unselected() != uint64(3*numBits/4) {
+		t.Fatalf("Expected full sequence. Instead found %d free bits.\nSeed: %d.\n%s", hnd.unselected, seed, hnd)
+	}
+
+	// Request a quarter of bits
+	for i := 0; i < numBits/4; i++ {
+		_, err := hnd.SetAny(true)
+		if err != nil {
+			t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd)
+		}
+	}
+	if hnd.Unselected() != uint64(numBits/2) {
+		t.Fatalf("Expected half sequence. Instead found %d free bits.\nSeed: %d\n%s", hnd.unselected, seed, hnd)
+	}
+
+	err = hnd.Destroy()
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestRetrieveFromStore(t *testing.T) {
+	ds, err := randomLocalStore()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	numBits := int(8 * blockLen)
+	hnd, err := NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Allocate first half of the bits
+	for i := 0; i < numBits/2; i++ {
+		_, err := hnd.SetAny(false)
+		if err != nil {
+			t.Fatalf("Unexpected failure on allocation %d: %v\n%s", i, err, hnd)
+		}
+	}
+	hnd0 := hnd.String()
+
+	// Retrieve same handle
+	hnd, err = NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits))
+	if err != nil {
+		t.Fatal(err)
+	}
+	hnd1 := hnd.String()
+
+	if hnd1 != hnd0 {
+		t.Fatalf("%v\n%v", hnd0, hnd1)
+	}
+
+	err = hnd.Destroy()
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestIsCorrupted(t *testing.T) {
+	ds, err := randomLocalStore()
+	if err != nil {
+		t.Fatal(err)
+	}
+	// Negative test
+	hnd, err := NewHandle("bitseq-test/data/", ds, "test_corrupted", 1024)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if hnd.runConsistencyCheck() {
+		t.Fatalf("Unexpected corrupted for %s", hnd)
+	}
+
+	if err := hnd.CheckConsistency(); err != nil {
+		t.Fatal(err)
+	}
+
+	hnd.Set(0)
+	if hnd.runConsistencyCheck() {
+		t.Fatalf("Unexpected corrupted for %s", hnd)
+	}
+
+	hnd.Set(1023)
+	if hnd.runConsistencyCheck() {
+		t.Fatalf("Unexpected corrupted for %s", hnd)
+	}
+
+	if err := hnd.CheckConsistency(); err != nil {
+		t.Fatal(err)
+	}
+
+	// Try real corrupted ipam handles found in the local store files reported by three docker users,
+	// plus a generic ipam handle from docker 1.9.1. This last will fail as well, because of how the
+	// last node in the sequence is expressed (This is true for IPAM handle only, because of the broadcast
+	// address reservation: last bit). This will allow an application using bitseq that runs a consistency
+	// check to detect and replace the 1.9.0/1 old vulnerable handle with the new one.
+	input := []*Handle{
+		{
+			id:         "LocalDefault/172.17.0.0/16",
+			bits:       65536,
+			unselected: 65412,
+			head: &sequence{
+				block: 0xffffffff,
+				count: 3,
+				next: &sequence{
+					block: 0xffffffbf,
+					count: 0,
+					next: &sequence{
+						block: 0xfe98816e,
+						count: 1,
+						next: &sequence{
+							block: 0xffffffff,
+							count: 0,
+							next: &sequence{
+								block: 0xe3bc0000,
+								count: 1,
+								next: &sequence{
+									block: 0x0,
+									count: 2042,
+									next: &sequence{
+										block: 0x1, count: 1,
+										next: &sequence{
+											block: 0x0, count: 0,
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			id:         "LocalDefault/172.17.0.0/16",
+			bits:       65536,
+			unselected: 65319,
+			head: &sequence{
+				block: 0xffffffff,
+				count: 7,
+				next: &sequence{
+					block: 0xffffff7f,
+					count: 0,
+					next: &sequence{
+						block: 0xffffffff,
+						count: 0,
+						next: &sequence{
+							block: 0x2000000,
+							count: 1,
+							next: &sequence{
+								block: 0x0,
+								count: 2039,
+								next: &sequence{
+									block: 0x1,
+									count: 1,
+									next: &sequence{
+										block: 0x0,
+										count: 0,
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			id:         "LocalDefault/172.17.0.0/16",
+			bits:       65536,
+			unselected: 65456,
+			head: &sequence{
+				block: 0xffffffff, count: 2,
+				next: &sequence{
+					block: 0xfffbffff, count: 0,
+					next: &sequence{
+						block: 0xffd07000, count: 1,
+						next: &sequence{
+							block: 0x0, count: 333,
+							next: &sequence{
+								block: 0x40000000, count: 1,
+								next: &sequence{
+									block: 0x0, count: 1710,
+									next: &sequence{
+										block: 0x1, count: 1,
+										next: &sequence{
+											block: 0x0, count: 0,
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	for idx, hnd := range input {
+		if !hnd.runConsistencyCheck() {
+			t.Fatalf("Expected corrupted for (%d): %s", idx, hnd)
+		}
+		if hnd.runConsistencyCheck() {
+			t.Fatalf("Sequence still marked corrupted (%d): %s", idx, hnd)
+		}
+	}
+}
+
+func testSetRollover(t *testing.T, serial bool) {
+	ds, err := randomLocalStore()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	numBlocks := uint32(8)
+	numBits := int(numBlocks * blockLen)
+	hnd, err := NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Allocate first half of the bits
+	for i := 0; i < numBits/2; i++ {
+		_, err := hnd.SetAny(serial)
+		if err != nil {
+			t.Fatalf("Unexpected failure on allocation %d: %v\n%s", i, err, hnd)
+		}
+	}
+
+	if hnd.Unselected() != uint64(numBits/2) {
+		t.Fatalf("Expected full sequence. Instead found %d free bits. %s", hnd.unselected, hnd)
+	}
+
+	seed := time.Now().Unix()
+	rand.Seed(seed)
+
+	// Deallocate half of the allocated bits following a random pattern
+	pattern := rand.Perm(numBits / 2)
+	for i := 0; i < numBits/4; i++ {
+		bit := pattern[i]
+		err := hnd.Unset(uint64(bit))
+		if err != nil {
+			t.Fatalf("Unexpected failure on deallocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd)
+		}
+	}
+	if hnd.Unselected() != uint64(3*numBits/4) {
+		t.Fatalf("Unexpected free bits: found %d free bits.\nSeed: %d.\n%s", hnd.unselected, seed, hnd)
+	}
+
+	//request to allocate for remaining half of the bits
+	for i := 0; i < numBits/2; i++ {
+		_, err := hnd.SetAny(serial)
+		if err != nil {
+			t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd)
+		}
+	}
+
+	//At this point all the bits must be allocated except the randomly unallocated bits
+	//which were unallocated in the first half of the bit sequence
+	if hnd.Unselected() != uint64(numBits/4) {
+		t.Fatalf("Unexpected number of unselected bits %d, Expected %d", hnd.Unselected(), numBits/4)
+	}
+
+	for i := 0; i < numBits/4; i++ {
+		_, err := hnd.SetAny(serial)
+		if err != nil {
+			t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd)
+		}
+	}
+	//Now requesting to allocate the unallocated random bits (qurter of the number of bits) should
+	//leave no more bits that can be allocated.
+	if hnd.Unselected() != 0 {
+		t.Fatalf("Unexpected number of unselected bits %d, Expected %d", hnd.Unselected(), 0)
+	}
+
+	err = hnd.Destroy()
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestSetRollover(t *testing.T) {
+	testSetRollover(t, false)
+}
+
+func TestSetRolloverSerial(t *testing.T) {
+	testSetRollover(t, true)
+}
+
+func TestGetFirstAvailableFromCurrent(t *testing.T) {
+	input := []struct {
+		mask    *sequence
+		bytePos uint64
+		bitPos  uint64
+		start   uint64
+		curr    uint64
+		end     uint64
+	}{
+		{&sequence{block: 0xffffffff, count: 2048}, invalidPos, invalidPos, 0, 0, 65536},
+		{&sequence{block: 0x0, count: 8}, 0, 0, 0, 0, 256},
+		{&sequence{block: 0x80000000, count: 8}, 1, 0, 0, 8, 256},
+		{&sequence{block: 0xC0000000, count: 8}, 0, 2, 0, 2, 256},
+		{&sequence{block: 0xE0000000, count: 8}, 0, 3, 0, 0, 256},
+		{&sequence{block: 0xFFFB1FFF, count: 8}, 2, 0, 14, 0, 256},
+		{&sequence{block: 0xFFFFFFFE, count: 8}, 3, 7, 0, 0, 256},
+
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0x00000000, count: 1, next: &sequence{block: 0xffffffff, count: 14}}}, 4, 0, 0, 32, 512},
+		{&sequence{block: 0xfffeffff, count: 1, next: &sequence{block: 0xffffffff, count: 15}}, 1, 7, 0, 16, 512},
+		{&sequence{block: 0xfffeffff, count: 15, next: &sequence{block: 0xffffffff, count: 1}}, 5, 7, 0, 16, 512},
+		{&sequence{block: 0xfffeffff, count: 15, next: &sequence{block: 0xffffffff, count: 1}}, 9, 7, 0, 48, 512},
+		{&sequence{block: 0xffffffff, count: 2, next: &sequence{block: 0xffffffef, count: 14}}, 19, 3, 0, 124, 512},
+		{&sequence{block: 0xfffeffff, count: 15, next: &sequence{block: 0x0fffffff, count: 1}}, 60, 0, 0, 480, 512},
+		{&sequence{block: 0xffffffff, count: 1, next: &sequence{block: 0xfffeffff, count: 14, next: &sequence{block: 0xffffffff, count: 1}}}, 17, 7, 0, 124, 512},
+		{&sequence{block: 0xfffffffb, count: 1, next: &sequence{block: 0xffffffff, count: 14, next: &sequence{block: 0xffffffff, count: 1}}}, 3, 5, 0, 124, 512},
+		{&sequence{block: 0xfffffffb, count: 1, next: &sequence{block: 0xfffeffff, count: 14, next: &sequence{block: 0xffffffff, count: 1}}}, 13, 7, 0, 80, 512},
+	}
+
+	for n, i := range input {
+		bytePos, bitPos, _ := getAvailableFromCurrent(i.mask, i.start, i.curr, i.end)
+		if bytePos != i.bytePos || bitPos != i.bitPos {
+			t.Fatalf("Error in (%d) getFirstAvailable(). Expected (%d, %d). Got (%d, %d)", n, i.bytePos, i.bitPos, bytePos, bitPos)
+		}
+	}
+}
diff --git a/libnetwork/bitseq/store.go b/libnetwork/bitseq/store.go
new file mode 100644
index 0000000000000..30657123e5f71
--- /dev/null
+++ b/libnetwork/bitseq/store.go
@@ -0,0 +1,130 @@
+package bitseq
+
+import (
+	"encoding/json"
+
+	"github.com/docker/docker/libnetwork/datastore"
+	"github.com/docker/docker/libnetwork/types"
+)
+
+// Key provides the Key to be used in KV Store
+func (h *Handle) Key() []string {
+	h.Lock()
+	defer h.Unlock()
+	return []string{h.app, h.id}
+}
+
+// KeyPrefix returns the immediate parent key that can be used for tree walk
+func (h *Handle) KeyPrefix() []string {
+	h.Lock()
+	defer h.Unlock()
+	return []string{h.app}
+}
+
+// Value marshals the data to be stored in the KV store
+func (h *Handle) Value() []byte {
+	b, err := json.Marshal(h)
+	if err != nil {
+		return nil
+	}
+	return b
+}
+
+// SetValue unmarshals the data from the KV store
+func (h *Handle) SetValue(value []byte) error {
+	return json.Unmarshal(value, h)
+}
+
+// Index returns the latest DB Index as seen by this object
+func (h *Handle) Index() uint64 {
+	h.Lock()
+	defer h.Unlock()
+	return h.dbIndex
+}
+
+// SetIndex method allows the datastore to store the latest DB Index into this object
+func (h *Handle) SetIndex(index uint64) {
+	h.Lock()
+	h.dbIndex = index
+	h.dbExists = true
+	h.Unlock()
+}
+
+// Exists method is true if this object has been stored in the DB.
+func (h *Handle) Exists() bool {
+	h.Lock()
+	defer h.Unlock()
+	return h.dbExists
+}
+
+// New method returns a handle based on the receiver handle
+func (h *Handle) New() datastore.KVObject {
+	h.Lock()
+	defer h.Unlock()
+
+	return &Handle{
+		app:   h.app,
+		store: h.store,
+	}
+}
+
+// CopyTo deep copies the handle into the passed destination object
+func (h *Handle) CopyTo(o datastore.KVObject) error {
+	h.Lock()
+	defer h.Unlock()
+
+	dstH := o.(*Handle)
+	if h == dstH {
+		return nil
+	}
+	dstH.Lock()
+	dstH.bits = h.bits
+	dstH.unselected = h.unselected
+	dstH.head = h.head.getCopy()
+	dstH.app = h.app
+	dstH.id = h.id
+	dstH.dbIndex = h.dbIndex
+	dstH.dbExists = h.dbExists
+	dstH.store = h.store
+	dstH.curr = h.curr
+	dstH.Unlock()
+
+	return nil
+}
+
+// Skip provides a way for a KV Object to avoid persisting it in the KV Store
+func (h *Handle) Skip() bool {
+	return false
+}
+
+// DataScope method returns the storage scope of the datastore
+func (h *Handle) DataScope() string {
+	h.Lock()
+	defer h.Unlock()
+
+	return h.store.Scope()
+}
+
+func (h *Handle) writeToStore() error {
+	h.Lock()
+	store := h.store
+	h.Unlock()
+	if store == nil {
+		return nil
+	}
+	err := store.PutObjectAtomic(h)
+	if err == datastore.ErrKeyModified {
+		return types.RetryErrorf("failed to perform atomic write (%v). Retry might fix the error", err)
+	}
+	return err
+}
+
+func (h *Handle) deleteFromStore() error {
+	h.Lock()
+	store := h.store
+	h.Unlock()
+	if store == nil {
+		return nil
+	}
+	return store.DeleteObjectAtomic(h)
+}
diff --git a/vendor/github.com/docker/libnetwork/cluster/provider.go b/libnetwork/cluster/provider.go
similarity index 100%
rename from vendor/github.com/docker/libnetwork/cluster/provider.go
rename to libnetwork/cluster/provider.go
diff --git a/libnetwork/cmd/diagnostic/Dockerfile.client b/libnetwork/cmd/diagnostic/Dockerfile.client
new file mode 100644
index 0000000000000..ee8771517a441
--- /dev/null
+++ b/libnetwork/cmd/diagnostic/Dockerfile.client
@@ -0,0 +1,4 @@
+FROM alpine
+RUN apk add --no-cache curl
+COPY diagnosticClient /usr/local/bin/diagnosticClient
+ENTRYPOINT ["/usr/local/bin/diagnosticClient"]
diff --git a/libnetwork/cmd/diagnostic/Dockerfile.dind b/libnetwork/cmd/diagnostic/Dockerfile.dind
new file mode 100644
index 0000000000000..e0629fa666048
--- /dev/null
+++ b/libnetwork/cmd/diagnostic/Dockerfile.dind
@@ -0,0 +1,5 @@
+FROM docker:17.12-dind
+RUN apk add --no-cache curl
+ENV DIND_CLIENT=true
+COPY daemon.json /etc/docker/daemon.json
+COPY diagnosticClient /usr/local/bin/diagnosticClient
diff --git a/libnetwork/cmd/diagnostic/README.md b/libnetwork/cmd/diagnostic/README.md
new file mode 100644
index 0000000000000..d21efea3340ff
--- /dev/null
+++ b/libnetwork/cmd/diagnostic/README.md
@@ -0,0 +1,262 @@
+---
+description: Learn to use the built-in network debugger to debug overlay networking problems
+keywords: network, troubleshooting, debug
+title: Debug overlay or swarm networking issues
+---
+
+**WARNING**
+This tool can change the internal state of the libnetwork API, be really mindful
+on its use and read carefully the following guide. Improper use of it will damage
+or permanently destroy the network configuration.
+
+
+Docker CE 17.12 and higher introduce a network debugging tool designed to help
+debug issues with overlay networks and swarm services running on Linux hosts.
+When enabled, a network diagnostic server listens on the specified port and
+provides diagnostic information. The network debugging tool should only be
+started to debug specific issues, and should not be left running all the time.
+
+Information about networks is stored in the database, which can be examined using
+the API. Currently the database contains information about the overlay network
+as well as the service discovery data.
+
+The Docker API exposes endpoints to query and control the network debugging
+tool. CLI integration is provided as a preview, but the implementation is not
+yet considered stable and commands and options may change without notice.
+
+The tool is available into 2 forms:
+1) client only: dockereng/network-diagnostic:onlyclient
+2) docker in docker version: dockereng/network-diagnostic:17.12-dind
+The latter allows to use the tool with a cluster running an engine older than 17.12
+
+## Enable the diagnostic server
+
+The tool currently only works on Docker hosts running on Linux. To enable it on a node
+follow the step below.
+
+1.  Set the `network-diagnostic-port` to a port which is free on the Docker
+    host, in the `/etc/docker/daemon.json` configuration file.
+
+    ```json
+    “network-diagnostic-port”: 
+    ```
+
+2.  Get the process ID (PID) of the `dockerd` process. It is the second field in
+    the output, and is typically a number from 2 to 6 digits long.
+
+    ```bash
+    $ ps aux |grep dockerd | grep -v grep
+    ```
+
+3.  Reload the Docker configuration without restarting Docker, by sending the
+    `HUP` signal to the PID you found in the previous step.
+
+    ```bash
+    kill -HUP 
+    ```
+
+If systemd is used the command `systemctl reload docker` will be enough
+
+
+A message like the following will appear in the Docker host logs:
+
+```none
+Starting the diagnostic server listening on  for commands
+```
+
+## Disable the diagnostic tool
+
+Repeat these steps for each node participating in the swarm.
+
+1.  Remove the `network-diagnostic-port` key from the `/etc/docker/daemon.json`
+    configuration file.
+
+2.  Get the process ID (PID) of the `dockerd` process. It is the second field in
+    the output, and is typically a number from 2 to 6 digits long.
+
+    ```bash
+    $ ps aux |grep dockerd | grep -v grep
+    ```
+
+3.  Reload the Docker configuration without restarting Docker, by sending the
+    `HUP` signal to the PID you found in the previous step.
+
+    ```bash
+    kill -HUP 
+    ```
+
+A message like the following will appear in the Docker host logs:
+
+```none
+Disabling the diagnostic server
+```
+
+## Access the diagnostic tool's API
+
+The network diagnostic tool exposes its own RESTful API. To access the API,
+send a HTTP request to the port where the tool is listening. The following
+commands assume the tool is listening on port 2000.
+
+Examples are not given for every endpoint.
+
+### Get help
+
+```bash
+$ curl localhost:2000/help
+
+OK
+/updateentry
+/getentry
+/gettable
+/leavenetwork
+/createentry
+/help
+/clusterpeers
+/ready
+/joinnetwork
+/deleteentry
+/networkpeers
+/
+/join
+```
+
+### Join or leave the network database cluster
+
+```bash
+$ curl localhost:2000/join?members=ip1,ip2,...
+```
+
+```bash
+$ curl localhost:2000/leave?members=ip1,ip2,...
+```
+
+`ip1`, `ip2`, ... are the swarm node ips (usually one is enough)
+
+### Join or leave a network
+
+```bash
+$ curl localhost:2000/joinnetwork?nid=
+```
+
+```bash
+$ curl localhost:2000/leavenetwork?nid=
+```
+
+`network id` can be retrieved on the manager with `docker network ls --no-trunc` and has
+to be the full length identifier
+
+### List cluster peers
+
+```bash
+$ curl localhost:2000/clusterpeers
+```
+
+### List nodes connected to a given network
+
+```bash
+$ curl localhost:2000/networkpeers?nid=
+```
+`network id` can be retrieved on the manager with `docker network ls --no-trunc` and has
+to be the full length identifier
+
+### Dump database tables
+
+The tables are called `endpoint_table` and `overlay_peer_table`.
+The `overlay_peer_table` contains all the overlay forwarding information
+The `endpoint_table` contains all the service discovery information
+
+```bash
+$ curl localhost:2000/gettable?nid=&tname=
+```
+
+### Interact with a specific database table
+
+The tables are called `endpoint_table` and `overlay_peer_table`.
+
+```bash
+$ curl localhost:2000/?nid=&tname=
&key=[&value=] +``` + +Note: +operations on tables have node ownership, this means that are going to remain persistent till +the node that inserted them is part of the cluster + +## Access the diagnostic tool's CLI + +The CLI is provided as a preview and is not yet stable. Commands or options may +change at any time. + +The CLI executable is called `diagnosticClient` and is made available using a +standalone container. + +`docker run --net host dockereng/network-diagnostic:onlyclient -v -net -t sd` + +The following flags are supported: + +| Flag | Description | +|---------------|-------------------------------------------------| +| -t | Table one of `sd` or `overlay`. | +| -ip | The IP address to query. Defaults to 127.0.0.1. | +| -net | The target network ID. | +| -port | The target port. (default port is 2000) | +| -a | Join/leave network | +| -v | Enable verbose output. | + +*NOTE* +By default the tool won't try to join the network. This is following the intent to not change +the state on which the node is when the diagnostic client is run. This means that it is safe +to run the diagnosticClient against a running daemon because it will just dump the current state. +When using instead the diagnosticClient in the containerized version the flag `-a` MUST be passed +to avoid retrieving empty results. On the other side using the `-a` flag against a loaded daemon +will have the undesirable side effect to leave the network and so cutting down the data path for +that daemon. + +### Container version of the diagnostic tool + +The CLI is provided as a container with a 17.12 engine that needs to run using privileged mode. +*NOTE* +Remember that table operations have ownership, so any `create entry` will be persistent till +the diagnostic container is part of the swarm. + +1. Make sure that the node where the diagnostic client will run is not part of the swarm, if so do `docker swarm leave -f` + +2. To run the container, use a command like the following: + + ```bash + $ docker container run --name net-diagnostic -d --privileged --network host dockereng/network-diagnostic:17.12-dind + ``` + +3. Connect to the container using `docker exec -it sh`, + and start the server using the following command: + + ```bash + $ kill -HUP 1 + ``` + +4. Join the diagnostic container to the swarm, then run the diagnostic CLI within the container. + + ```bash + $ ./diagnosticClient ... + ``` + +4. When finished debugging, leave the swarm and stop the container. + +### Examples + +The following commands dump the service discovery table and verify node +ownership. + +*NOTE* +Remember to use the full network ID, you can easily find that with `docker network ls --no-trunc` + +**Service discovery and load balancer:** + +```bash +$ diagnostiClient -t sd -v -net n8a8ie6tb3wr2e260vxj8ncy4 -a +``` + +**Overlay network:** + +```bash +$ diagnostiClient -port 2001 -t overlay -v -net n8a8ie6tb3wr2e260vxj8ncy4 -a +``` diff --git a/libnetwork/cmd/diagnostic/daemon.json b/libnetwork/cmd/diagnostic/daemon.json new file mode 100644 index 0000000000000..b5eb9889b868b --- /dev/null +++ b/libnetwork/cmd/diagnostic/daemon.json @@ -0,0 +1,4 @@ +{ + "debug": true, + "network-diagnostic-port": 2000 +} diff --git a/libnetwork/cmd/diagnostic/main.go b/libnetwork/cmd/diagnostic/main.go new file mode 100644 index 0000000000000..01b0a6422f8a5 --- /dev/null +++ b/libnetwork/cmd/diagnostic/main.go @@ -0,0 +1,208 @@ +package main + +import ( + "bufio" + "encoding/base64" + "encoding/json" + "flag" + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/docker/docker/libnetwork" + "github.com/docker/docker/libnetwork/diagnostic" + "github.com/docker/docker/libnetwork/drivers/overlay" + "github.com/sirupsen/logrus" +) + +const ( + readyPath = "http://%s:%d/ready" + joinNetwork = "http://%s:%d/joinnetwork?nid=%s" + leaveNetwork = "http://%s:%d/leavenetwork?nid=%s" + clusterPeers = "http://%s:%d/clusterpeers?json" + networkPeers = "http://%s:%d/networkpeers?nid=%s&json" + dumpTable = "http://%s:%d/gettable?nid=%s&tname=%s&json" + deleteEntry = "http://%s:%d/deleteentry?nid=%s&tname=%s&key=%s&json" +) + +func httpIsOk(body io.ReadCloser) { + b, err := io.ReadAll(body) + if err != nil { + logrus.Fatalf("Failed the body parse %s", err) + } + if !strings.Contains(string(b), "OK") { + logrus.Fatalf("Server not ready %s", b) + } + body.Close() +} + +func main() { + ipPtr := flag.String("ip", "127.0.0.1", "ip address") + portPtr := flag.Int("port", 2000, "port") + networkPtr := flag.String("net", "", "target network") + tablePtr := flag.String("t", "", "table to process ") + remediatePtr := flag.Bool("r", false, "perform remediation deleting orphan entries") + joinPtr := flag.Bool("a", false, "join/leave network") + verbosePtr := flag.Bool("v", false, "verbose output") + + flag.Parse() + + if *verbosePtr { + logrus.SetLevel(logrus.DebugLevel) + } + + if _, ok := os.LookupEnv("DIND_CLIENT"); !ok && *joinPtr { + logrus.Fatal("you are not using the client in docker in docker mode, the use of the -a flag can be disruptive, " + + "please remove it (doc:https://github.com/docker/docker/libnetwork/blob/master/cmd/diagnostic/README.md)") + } + + logrus.Infof("Connecting to %s:%d checking ready", *ipPtr, *portPtr) + resp, err := http.Get(fmt.Sprintf(readyPath, *ipPtr, *portPtr)) + if err != nil { + logrus.WithError(err).Fatalf("The connection failed") + } + httpIsOk(resp.Body) + + clusterPeers := fetchNodePeers(*ipPtr, *portPtr, "") + var networkPeers map[string]string + var joinedNetwork bool + if *networkPtr != "" { + if *joinPtr { + logrus.Infof("Joining the network:%q", *networkPtr) + resp, err = http.Get(fmt.Sprintf(joinNetwork, *ipPtr, *portPtr, *networkPtr)) + if err != nil { + logrus.WithError(err).Fatalf("Failed joining the network") + } + httpIsOk(resp.Body) + joinedNetwork = true + } + + networkPeers = fetchNodePeers(*ipPtr, *portPtr, *networkPtr) + if len(networkPeers) == 0 { + logrus.Warnf("There is no peer on network %q, check the network ID, and verify that is the non truncated version", *networkPtr) + } + } + + switch *tablePtr { + case "sd": + fetchTable(*ipPtr, *portPtr, *networkPtr, "endpoint_table", clusterPeers, networkPeers, *remediatePtr) + case "overlay": + fetchTable(*ipPtr, *portPtr, *networkPtr, "overlay_peer_table", clusterPeers, networkPeers, *remediatePtr) + } + + if joinedNetwork { + logrus.Infof("Leaving the network:%q", *networkPtr) + resp, err = http.Get(fmt.Sprintf(leaveNetwork, *ipPtr, *portPtr, *networkPtr)) + if err != nil { + logrus.WithError(err).Fatalf("Failed leaving the network") + } + httpIsOk(resp.Body) + } +} + +func fetchNodePeers(ip string, port int, network string) map[string]string { + if network == "" { + logrus.Infof("Fetch cluster peers") + } else { + logrus.Infof("Fetch peers network:%q", network) + } + + var path string + if network != "" { + path = fmt.Sprintf(networkPeers, ip, port, network) + } else { + path = fmt.Sprintf(clusterPeers, ip, port) + } + + resp, err := http.Get(path) //nolint:gosec // G107: Potential HTTP request made with variable url + if err != nil { + logrus.WithError(err).Fatalf("Failed fetching path") + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + logrus.WithError(err).Fatalf("Failed the body parse") + } + + output := diagnostic.HTTPResult{Details: &diagnostic.TablePeersResult{}} + err = json.Unmarshal(body, &output) + if err != nil { + logrus.WithError(err).Fatalf("Failed the json unmarshalling") + } + + logrus.Debugf("Parsing JSON response") + result := make(map[string]string, output.Details.(*diagnostic.TablePeersResult).Length) + for _, v := range output.Details.(*diagnostic.TablePeersResult).Elements { + logrus.Debugf("name:%s ip:%s", v.Name, v.IP) + result[v.Name] = v.IP + } + return result +} + +func fetchTable(ip string, port int, network, tableName string, clusterPeers, networkPeers map[string]string, remediate bool) { + logrus.Infof("Fetch %s table and check owners", tableName) + resp, err := http.Get(fmt.Sprintf(dumpTable, ip, port, network, tableName)) + if err != nil { + logrus.WithError(err).Fatalf("Failed fetching endpoint table") + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + logrus.WithError(err).Fatalf("Failed the body parse") + } + + output := diagnostic.HTTPResult{Details: &diagnostic.TableEndpointsResult{}} + err = json.Unmarshal(body, &output) + if err != nil { + logrus.WithError(err).Fatalf("Failed the json unmarshalling") + } + + logrus.Debug("Parsing data structures") + var orphanKeys []string + for _, v := range output.Details.(*diagnostic.TableEndpointsResult).Elements { + decoded, err := base64.StdEncoding.DecodeString(v.Value) + if err != nil { + logrus.WithError(err).Errorf("Failed decoding entry") + continue + } + switch tableName { + case "endpoint_table": + var elem libnetwork.EndpointRecord + elem.Unmarshal(decoded) + logrus.Debugf("key:%s value:%+v owner:%s", v.Key, elem, v.Owner) + case "overlay_peer_table": + var elem overlay.PeerRecord + elem.Unmarshal(decoded) + logrus.Debugf("key:%s value:%+v owner:%s", v.Key, elem, v.Owner) + } + + if _, ok := networkPeers[v.Owner]; !ok { + logrus.Warnf("The element with key:%s does not belong to any node on this network", v.Key) + orphanKeys = append(orphanKeys, v.Key) + } + if _, ok := clusterPeers[v.Owner]; !ok { + logrus.Warnf("The element with key:%s does not belong to any node on this cluster", v.Key) + } + } + + if len(orphanKeys) > 0 && remediate { + logrus.Warnf("The following keys:%v results as orphan, do you want to proceed with the deletion (this operation is irreversible)? [Yes/No]", orphanKeys) + reader := bufio.NewReader(os.Stdin) + text, _ := reader.ReadString('\n') + text = strings.Replace(text, "\n", "", -1) + if strings.Compare(text, "Yes") == 0 { + for _, k := range orphanKeys { + resp, err := http.Get(fmt.Sprintf(deleteEntry, ip, port, network, tableName, k)) + if err != nil { + logrus.WithError(err).Errorf("Failed deleting entry k:%s", k) + break + } + resp.Body.Close() + } + } else { + logrus.Infof("Deletion skipped") + } + } +} diff --git a/libnetwork/cmd/networkdb-test/Dockerfile b/libnetwork/cmd/networkdb-test/Dockerfile new file mode 100644 index 0000000000000..f30a6b423c794 --- /dev/null +++ b/libnetwork/cmd/networkdb-test/Dockerfile @@ -0,0 +1,9 @@ +FROM alpine + +RUN apk --no-cache add curl + +COPY testMain /app/ + +WORKDIR app + +ENTRYPOINT ["/app/testMain"] diff --git a/libnetwork/cmd/networkdb-test/README b/libnetwork/cmd/networkdb-test/README new file mode 100644 index 0000000000000..18227f94aaf71 --- /dev/null +++ b/libnetwork/cmd/networkdb-test/README @@ -0,0 +1,15 @@ +SERVER + +cd test/networkdb +env GOOS=linux go build -v testMain.go && docker build -t dockereng/e2e-networkdb . +(only for testkit case) docker push dockereng/e2e-networkdb + +Run server: docker service create --name testdb --network net1 --replicas 3 --env TASK_ID="{{.Task.ID}}" -p mode=host,target=8000 dockereng/e2e-networkdb server 8000 + +CLIENT + +cd test/networkdb +Join cluster: docker run -it --network net1 dockereng/e2e-networkdb client join testdb 8000 +Join network: docker run -it --network net1 dockereng/e2e-networkdb client join-network testdb 8000 test +Run test: docker run -it --network net1 dockereng/e2e-networkdb client write-delete-unique-keys testdb 8000 test tableBla 3 10 +check table: curl "localhost:32768/gettable?nid=test&tname=table_name" diff --git a/libnetwork/cmd/networkdb-test/dbclient/ndbClient.go b/libnetwork/cmd/networkdb-test/dbclient/ndbClient.go new file mode 100644 index 0000000000000..56f9dfea2c730 --- /dev/null +++ b/libnetwork/cmd/networkdb-test/dbclient/ndbClient.go @@ -0,0 +1,861 @@ +package dbclient + +import ( + "context" + "fmt" + "io" + "log" + "net" + "net/http" + "os" + "regexp" + "strconv" + "strings" + "time" + + "github.com/sirupsen/logrus" +) + +var servicePort string + +const totalWrittenKeys string = "totalKeys" + +type resultTuple struct { + id string + result int +} + +func httpGetFatalError(ip, port, path string) { + body, err := httpGet(ip, port, path) + if err != nil || !strings.Contains(string(body), "OK") { + log.Fatalf("[%s] error %s %s", path, err, body) + } +} + +func httpGet(ip, port, path string) ([]byte, error) { + resp, err := http.Get("http://" + ip + ":" + port + path) + if err != nil { + logrus.Errorf("httpGet error:%s", err) + return nil, err + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + return body, err +} + +func joinCluster(ip, port string, members []string, doneCh chan resultTuple) { + httpGetFatalError(ip, port, "/join?members="+strings.Join(members, ",")) + + if doneCh != nil { + doneCh <- resultTuple{id: ip, result: 0} + } +} + +func joinNetwork(ip, port, network string, doneCh chan resultTuple) { + httpGetFatalError(ip, port, "/joinnetwork?nid="+network) + + if doneCh != nil { + doneCh <- resultTuple{id: ip, result: 0} + } +} + +func leaveNetwork(ip, port, network string, doneCh chan resultTuple) { + httpGetFatalError(ip, port, "/leavenetwork?nid="+network) + + if doneCh != nil { + doneCh <- resultTuple{id: ip, result: 0} + } +} + +func writeTableKey(ip, port, networkName, tableName, key string) { + createPath := "/createentry?unsafe&nid=" + networkName + "&tname=" + tableName + "&value=v&key=" + httpGetFatalError(ip, port, createPath+key) +} + +func deleteTableKey(ip, port, networkName, tableName, key string) { + deletePath := "/deleteentry?nid=" + networkName + "&tname=" + tableName + "&key=" + httpGetFatalError(ip, port, deletePath+key) +} + +func clusterPeersNumber(ip, port string, doneCh chan resultTuple) { + body, err := httpGet(ip, port, "/clusterpeers") + + if err != nil { + logrus.Errorf("clusterPeers %s there was an error: %s", ip, err) + doneCh <- resultTuple{id: ip, result: -1} + return + } + peersRegexp := regexp.MustCompile(`total entries: ([0-9]+)`) + peersNum, _ := strconv.Atoi(peersRegexp.FindStringSubmatch(string(body))[1]) + + doneCh <- resultTuple{id: ip, result: peersNum} +} + +func networkPeersNumber(ip, port, networkName string, doneCh chan resultTuple) { + body, err := httpGet(ip, port, "/networkpeers?nid="+networkName) + + if err != nil { + logrus.Errorf("networkPeersNumber %s there was an error: %s", ip, err) + doneCh <- resultTuple{id: ip, result: -1} + return + } + peersRegexp := regexp.MustCompile(`total entries: ([0-9]+)`) + peersNum, _ := strconv.Atoi(peersRegexp.FindStringSubmatch(string(body))[1]) + + doneCh <- resultTuple{id: ip, result: peersNum} +} + +func dbTableEntriesNumber(ip, port, networkName, tableName string, doneCh chan resultTuple) { + body, err := httpGet(ip, port, "/gettable?nid="+networkName+"&tname="+tableName) + + if err != nil { + logrus.Errorf("tableEntriesNumber %s there was an error: %s", ip, err) + doneCh <- resultTuple{id: ip, result: -1} + return + } + elementsRegexp := regexp.MustCompile(`total entries: ([0-9]+)`) + entriesNum, _ := strconv.Atoi(elementsRegexp.FindStringSubmatch(string(body))[1]) + doneCh <- resultTuple{id: ip, result: entriesNum} +} + +func dbQueueLength(ip, port, networkName string, doneCh chan resultTuple) { + body, err := httpGet(ip, port, "/networkstats?nid="+networkName) + + if err != nil { + logrus.Errorf("queueLength %s there was an error: %s", ip, err) + doneCh <- resultTuple{id: ip, result: -1} + return + } + elementsRegexp := regexp.MustCompile(`qlen: ([0-9]+)`) + entriesNum, _ := strconv.Atoi(elementsRegexp.FindStringSubmatch(string(body))[1]) + doneCh <- resultTuple{id: ip, result: entriesNum} +} + +func clientWatchTable(ip, port, networkName, tableName string, doneCh chan resultTuple) { + httpGetFatalError(ip, port, "/watchtable?nid="+networkName+"&tname="+tableName) + if doneCh != nil { + doneCh <- resultTuple{id: ip, result: 0} + } +} + +func clientTableEntriesNumber(ip, port, networkName, tableName string, doneCh chan resultTuple) { + body, err := httpGet(ip, port, "/watchedtableentries?nid="+networkName+"&tname="+tableName) + + if err != nil { + logrus.Errorf("clientTableEntriesNumber %s there was an error: %s", ip, err) + doneCh <- resultTuple{id: ip, result: -1} + return + } + elementsRegexp := regexp.MustCompile(`total elements: ([0-9]+)`) + entriesNum, _ := strconv.Atoi(elementsRegexp.FindStringSubmatch(string(body))[1]) + doneCh <- resultTuple{id: ip, result: entriesNum} +} + +func writeKeysNumber(ip, port, networkName, tableName, key string, number int, doneCh chan resultTuple) { + x := 0 + for ; x < number; x++ { + k := key + strconv.Itoa(x) + // write key + writeTableKey(ip, port, networkName, tableName, k) + } + doneCh <- resultTuple{id: ip, result: x} +} + +func deleteKeysNumber(ip, port, networkName, tableName, key string, number int, doneCh chan resultTuple) { + x := 0 + for ; x < number; x++ { + k := key + strconv.Itoa(x) + // write key + deleteTableKey(ip, port, networkName, tableName, k) + } + doneCh <- resultTuple{id: ip, result: x} +} + +func writeUniqueKeys(ctx context.Context, ip, port, networkName, tableName, key string, doneCh chan resultTuple) { + for x := 0; ; x++ { + select { + case <-ctx.Done(): + doneCh <- resultTuple{id: ip, result: x} + return + default: + k := key + strconv.Itoa(x) + // write key + writeTableKey(ip, port, networkName, tableName, k) + // give time to send out key writes + time.Sleep(100 * time.Millisecond) + } + } +} + +func writeDeleteUniqueKeys(ctx context.Context, ip, port, networkName, tableName, key string, doneCh chan resultTuple) { + for x := 0; ; x++ { + select { + case <-ctx.Done(): + doneCh <- resultTuple{id: ip, result: x} + return + default: + k := key + strconv.Itoa(x) + // write key + writeTableKey(ip, port, networkName, tableName, k) + // give time to send out key writes + time.Sleep(100 * time.Millisecond) + // delete key + deleteTableKey(ip, port, networkName, tableName, k) + } + } +} + +func writeDeleteLeaveJoin(ctx context.Context, ip, port, networkName, tableName, key string, doneCh chan resultTuple) { + for x := 0; ; x++ { + select { + case <-ctx.Done(): + doneCh <- resultTuple{id: ip, result: x} + return + default: + k := key + strconv.Itoa(x) + // write key + writeTableKey(ip, port, networkName, tableName, k) + time.Sleep(100 * time.Millisecond) + // delete key + deleteTableKey(ip, port, networkName, tableName, k) + // give some time + time.Sleep(100 * time.Millisecond) + // leave network + leaveNetwork(ip, port, networkName, nil) + // join network + joinNetwork(ip, port, networkName, nil) + } + } +} + +func ready(ip, port string, doneCh chan resultTuple) { + for { + body, err := httpGet(ip, port, "/ready") + if err != nil || !strings.Contains(string(body), "OK") { + time.Sleep(500 * time.Millisecond) + continue + } + // success + break + } + // notify the completion + doneCh <- resultTuple{id: ip, result: 0} +} + +func checkTable(ctx context.Context, ips []string, port, networkName, tableName string, expectedEntries int, fn func(string, string, string, string, chan resultTuple)) (opTime time.Duration) { + startTime := time.Now().UnixNano() + var successTime int64 + + // Loop for 2 minutes to guarantee that the result is stable + for { + select { + case <-ctx.Done(): + // Validate test success, if the time is set means that all the tables are empty + if successTime != 0 { + opTime = time.Duration(successTime-startTime) / time.Millisecond + logrus.Infof("Check table passed, the cluster converged in %d msec", opTime) + return + } + log.Fatal("Test failed, there is still entries in the tables of the nodes") + default: + logrus.Infof("Checking table %s expected %d", tableName, expectedEntries) + doneCh := make(chan resultTuple, len(ips)) + for _, ip := range ips { + go fn(ip, servicePort, networkName, tableName, doneCh) + } + + nodesWithCorrectEntriesNum := 0 + for i := len(ips); i > 0; i-- { + tableEntries := <-doneCh + logrus.Infof("Node %s has %d entries", tableEntries.id, tableEntries.result) + if tableEntries.result == expectedEntries { + nodesWithCorrectEntriesNum++ + } + } + close(doneCh) + if nodesWithCorrectEntriesNum == len(ips) { + if successTime == 0 { + successTime = time.Now().UnixNano() + logrus.Infof("Success after %d msec", time.Duration(successTime-startTime)/time.Millisecond) + } + } else { + successTime = 0 + } + time.Sleep(10 * time.Second) + } + } +} + +func waitWriters(parallelWriters int, mustWrite bool, doneCh chan resultTuple) map[string]int { + var totalKeys int + resultTable := make(map[string]int) + for i := 0; i < parallelWriters; i++ { + logrus.Infof("Waiting for %d workers", parallelWriters-i) + workerReturn := <-doneCh + totalKeys += workerReturn.result + if mustWrite && workerReturn.result == 0 { + log.Fatalf("The worker %s did not write any key %d == 0", workerReturn.id, workerReturn.result) + } + if !mustWrite && workerReturn.result != 0 { + log.Fatalf("The worker %s was supposed to return 0 instead %d != 0", workerReturn.id, workerReturn.result) + } + if mustWrite { + resultTable[workerReturn.id] = workerReturn.result + logrus.Infof("The worker %s wrote %d keys", workerReturn.id, workerReturn.result) + } + } + resultTable[totalWrittenKeys] = totalKeys + return resultTable +} + +// ready +func doReady(ips []string) { + doneCh := make(chan resultTuple, len(ips)) + // check all the nodes + for _, ip := range ips { + go ready(ip, servicePort, doneCh) + } + // wait for the readiness of all nodes + for i := len(ips); i > 0; i-- { + <-doneCh + } + close(doneCh) +} + +// join +func doJoin(ips []string) { + doneCh := make(chan resultTuple, len(ips)) + // check all the nodes + for i, ip := range ips { + members := append([]string(nil), ips[:i]...) + members = append(members, ips[i+1:]...) + go joinCluster(ip, servicePort, members, doneCh) + } + // wait for the readiness of all nodes + for i := len(ips); i > 0; i-- { + <-doneCh + } + close(doneCh) +} + +// cluster-peers expectedNumberPeers maxRetry +func doClusterPeers(ips []string, args []string) { + doneCh := make(chan resultTuple, len(ips)) + expectedPeers, _ := strconv.Atoi(args[0]) + maxRetry, _ := strconv.Atoi(args[1]) + for retry := 0; retry < maxRetry; retry++ { + // check all the nodes + for _, ip := range ips { + go clusterPeersNumber(ip, servicePort, doneCh) + } + var failed bool + // wait for the readiness of all nodes + for i := len(ips); i > 0; i-- { + node := <-doneCh + if node.result != expectedPeers { + failed = true + if retry == maxRetry-1 { + log.Fatalf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result) + } else { + logrus.Warnf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result) + } + time.Sleep(1 * time.Second) + } + } + // check if needs retry + if !failed { + break + } + } + close(doneCh) +} + +// join-network networkName +func doJoinNetwork(ips []string, args []string) { + doneCh := make(chan resultTuple, len(ips)) + // check all the nodes + for _, ip := range ips { + go joinNetwork(ip, servicePort, args[0], doneCh) + } + // wait for the readiness of all nodes + for i := len(ips); i > 0; i-- { + <-doneCh + } + close(doneCh) +} + +// leave-network networkName +func doLeaveNetwork(ips []string, args []string) { + doneCh := make(chan resultTuple, len(ips)) + // check all the nodes + for _, ip := range ips { + go leaveNetwork(ip, servicePort, args[0], doneCh) + } + // wait for the readiness of all nodes + for i := len(ips); i > 0; i-- { + <-doneCh + } + close(doneCh) +} + +// network-peers networkName expectedNumberPeers maxRetry +func doNetworkPeers(ips []string, args []string) { + doneCh := make(chan resultTuple, len(ips)) + networkName := args[0] + expectedPeers, _ := strconv.Atoi(args[1]) + maxRetry, _ := strconv.Atoi(args[2]) + for retry := 0; retry < maxRetry; retry++ { + // check all the nodes + for _, ip := range ips { + go networkPeersNumber(ip, servicePort, networkName, doneCh) + } + var failed bool + // wait for the readiness of all nodes + for i := len(ips); i > 0; i-- { + node := <-doneCh + if node.result != expectedPeers { + failed = true + if retry == maxRetry-1 { + log.Fatalf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result) + } else { + logrus.Warnf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result) + } + time.Sleep(1 * time.Second) + } + } + // check if needs retry + if !failed { + break + } + } + close(doneCh) +} + +// network-stats-queue networkName queueSize +func doNetworkStatsQueue(ips []string, args []string) { + doneCh := make(chan resultTuple, len(ips)) + networkName := args[0] + comparison := args[1] + size, _ := strconv.Atoi(args[2]) + + // check all the nodes + for _, ip := range ips { + go dbQueueLength(ip, servicePort, networkName, doneCh) + } + + var avgQueueSize int + // wait for the readiness of all nodes + for i := len(ips); i > 0; i-- { + node := <-doneCh + switch comparison { + case "lt": + if node.result > size { + log.Fatalf("Expected queue size from %s to be %d < %d", node.id, node.result, size) + } + case "gt": + if node.result < size { + log.Fatalf("Expected queue size from %s to be %d > %d", node.id, node.result, size) + } + default: + log.Fatal("unknown comparison operator") + } + avgQueueSize += node.result + } + close(doneCh) + avgQueueSize /= len(ips) + fmt.Fprintf(os.Stderr, "doNetworkStatsQueue succeeded with avg queue:%d", avgQueueSize) +} + +// write-keys networkName tableName parallelWriters numberOfKeysEach +func doWriteKeys(ips []string, args []string) { + networkName := args[0] + tableName := args[1] + parallelWriters, _ := strconv.Atoi(args[2]) + numberOfKeys, _ := strconv.Atoi(args[3]) + + doneCh := make(chan resultTuple, parallelWriters) + // Enable watch of tables from clients + for i := 0; i < parallelWriters; i++ { + go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh) + } + waitWriters(parallelWriters, false, doneCh) + + // Start parallel writers that will create and delete unique keys + defer close(doneCh) + for i := 0; i < parallelWriters; i++ { + key := "key-" + strconv.Itoa(i) + "-" + logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i]) + go writeKeysNumber(ips[i], servicePort, networkName, tableName, key, numberOfKeys, doneCh) + } + + // Sync with all the writers + keyMap := waitWriters(parallelWriters, true, doneCh) + logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys]) + + // check table entries for 2 minutes + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + opTime := checkTable(ctx, ips, servicePort, networkName, tableName, keyMap[totalWrittenKeys], dbTableEntriesNumber) + cancel() + fmt.Fprintf(os.Stderr, "doWriteKeys succeeded in %d msec", opTime) +} + +// delete-keys networkName tableName parallelWriters numberOfKeysEach +func doDeleteKeys(ips []string, args []string) { + networkName := args[0] + tableName := args[1] + parallelWriters, _ := strconv.Atoi(args[2]) + numberOfKeys, _ := strconv.Atoi(args[3]) + + doneCh := make(chan resultTuple, parallelWriters) + // Enable watch of tables from clients + for i := 0; i < parallelWriters; i++ { + go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh) + } + waitWriters(parallelWriters, false, doneCh) + + // Start parallel writers that will create and delete unique keys + defer close(doneCh) + for i := 0; i < parallelWriters; i++ { + key := "key-" + strconv.Itoa(i) + "-" + logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i]) + go deleteKeysNumber(ips[i], servicePort, networkName, tableName, key, numberOfKeys, doneCh) + } + + // Sync with all the writers + keyMap := waitWriters(parallelWriters, true, doneCh) + logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys]) + + // check table entries for 2 minutes + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber) + cancel() + fmt.Fprintf(os.Stderr, "doDeletekeys succeeded in %d msec", opTime) +} + +// write-delete-unique-keys networkName tableName numParallelWriters writeTimeSec +func doWriteDeleteUniqueKeys(ips []string, args []string) { + networkName := args[0] + tableName := args[1] + parallelWriters, _ := strconv.Atoi(args[2]) + writeTimeSec, _ := strconv.Atoi(args[3]) + + doneCh := make(chan resultTuple, parallelWriters) + // Enable watch of tables from clients + for i := 0; i < parallelWriters; i++ { + go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh) + } + waitWriters(parallelWriters, false, doneCh) + + // Start parallel writers that will create and delete unique keys + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second) + for i := 0; i < parallelWriters; i++ { + key := "key-" + strconv.Itoa(i) + "-" + logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i]) + go writeDeleteUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh) + } + + // Sync with all the writers + keyMap := waitWriters(parallelWriters, true, doneCh) + cancel() + logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys]) + + // check table entries for 2 minutes + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute) + opDBTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber) + cancel() + ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second) + opClientTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, clientTableEntriesNumber) + cancel() + fmt.Fprintf(os.Stderr, "doWriteDeleteUniqueKeys succeeded in %d msec and client %d msec", opDBTime, opClientTime) +} + +// write-unique-keys networkName tableName numParallelWriters writeTimeSec +func doWriteUniqueKeys(ips []string, args []string) { + networkName := args[0] + tableName := args[1] + parallelWriters, _ := strconv.Atoi(args[2]) + writeTimeSec, _ := strconv.Atoi(args[3]) + + doneCh := make(chan resultTuple, parallelWriters) + // Enable watch of tables from clients + for i := 0; i < parallelWriters; i++ { + go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh) + } + waitWriters(parallelWriters, false, doneCh) + + // Start parallel writers that will create and delete unique keys + defer close(doneCh) + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second) + for i := 0; i < parallelWriters; i++ { + key := "key-" + strconv.Itoa(i) + "-" + logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i]) + go writeUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh) + } + + // Sync with all the writers + keyMap := waitWriters(parallelWriters, true, doneCh) + cancel() + logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys]) + + // check table entries for 2 minutes + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute) + opTime := checkTable(ctx, ips, servicePort, networkName, tableName, keyMap[totalWrittenKeys], dbTableEntriesNumber) + cancel() + fmt.Fprintf(os.Stderr, "doWriteUniqueKeys succeeded in %d msec", opTime) +} + +// write-delete-leave-join networkName tableName numParallelWriters writeTimeSec +func doWriteDeleteLeaveJoin(ips []string, args []string) { + networkName := args[0] + tableName := args[1] + parallelWriters, _ := strconv.Atoi(args[2]) + writeTimeSec, _ := strconv.Atoi(args[3]) + + // Start parallel writers that will create and delete unique keys + doneCh := make(chan resultTuple, parallelWriters) + defer close(doneCh) + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second) + for i := 0; i < parallelWriters; i++ { + key := "key-" + strconv.Itoa(i) + "-" + logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i]) + go writeDeleteLeaveJoin(ctx, ips[i], servicePort, networkName, tableName, key, doneCh) + } + + // Sync with all the writers + keyMap := waitWriters(parallelWriters, true, doneCh) + cancel() + logrus.Infof("Written a total of %d keys on the cluster", keyMap["totalKeys"]) + + // check table entries for 2 minutes + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute) + opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber) + cancel() + fmt.Fprintf(os.Stderr, "doWriteDeleteLeaveJoin succeeded in %d msec", opTime) +} + +// write-delete-wait-leave-join networkName tableName numParallelWriters writeTimeSec +func doWriteDeleteWaitLeaveJoin(ips []string, args []string) { + networkName := args[0] + tableName := args[1] + parallelWriters, _ := strconv.Atoi(args[2]) + writeTimeSec, _ := strconv.Atoi(args[3]) + + // Start parallel writers that will create and delete unique keys + doneCh := make(chan resultTuple, parallelWriters) + defer close(doneCh) + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second) + for i := 0; i < parallelWriters; i++ { + key := "key-" + strconv.Itoa(i) + "-" + logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i]) + go writeDeleteUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh) + } + + // Sync with all the writers + keyMap := waitWriters(parallelWriters, true, doneCh) + cancel() + logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys]) + + // The writers will leave the network + for i := 0; i < parallelWriters; i++ { + logrus.Infof("worker leaveNetwork: %d on IP:%s", i, ips[i]) + go leaveNetwork(ips[i], servicePort, networkName, doneCh) + } + waitWriters(parallelWriters, false, doneCh) + + // Give some time + time.Sleep(100 * time.Millisecond) + + // The writers will join the network + for i := 0; i < parallelWriters; i++ { + logrus.Infof("worker joinNetwork: %d on IP:%s", i, ips[i]) + go joinNetwork(ips[i], servicePort, networkName, doneCh) + } + waitWriters(parallelWriters, false, doneCh) + + // check table entries for 2 minutes + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute) + opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber) + cancel() + fmt.Fprintf(os.Stderr, "doWriteDeleteWaitLeaveJoin succeeded in %d msec", opTime) +} + +// write-wait-leave networkName tableName numParallelWriters writeTimeSec +func doWriteWaitLeave(ips []string, args []string) { + networkName := args[0] + tableName := args[1] + parallelWriters, _ := strconv.Atoi(args[2]) + writeTimeSec, _ := strconv.Atoi(args[3]) + + // Start parallel writers that will create and delete unique keys + doneCh := make(chan resultTuple, parallelWriters) + defer close(doneCh) + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second) + for i := 0; i < parallelWriters; i++ { + key := "key-" + strconv.Itoa(i) + "-" + logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i]) + go writeUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh) + } + + // Sync with all the writers + keyMap := waitWriters(parallelWriters, true, doneCh) + cancel() + logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys]) + + // The writers will leave the network + for i := 0; i < parallelWriters; i++ { + logrus.Infof("worker leaveNetwork: %d on IP:%s", i, ips[i]) + go leaveNetwork(ips[i], servicePort, networkName, doneCh) + } + waitWriters(parallelWriters, false, doneCh) + + // check table entries for 2 minutes + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute) + opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber) + cancel() + fmt.Fprintf(os.Stderr, "doWriteLeaveJoin succeeded in %d msec", opTime) +} + +// write-wait-leave-join networkName tableName numParallelWriters writeTimeSec numParallelLeaver +func doWriteWaitLeaveJoin(ips []string, args []string) { + networkName := args[0] + tableName := args[1] + parallelWriters, _ := strconv.Atoi(args[2]) + writeTimeSec, _ := strconv.Atoi(args[3]) + parallelLeaver, _ := strconv.Atoi(args[4]) + + // Start parallel writers that will create and delete unique keys + doneCh := make(chan resultTuple, parallelWriters) + defer close(doneCh) + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second) + for i := 0; i < parallelWriters; i++ { + key := "key-" + strconv.Itoa(i) + "-" + logrus.Infof("Spawn worker: %d on IP:%s", i, ips[i]) + go writeUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh) + } + + // Sync with all the writers + keyMap := waitWriters(parallelWriters, true, doneCh) + cancel() + logrus.Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys]) + + keysExpected := keyMap[totalWrittenKeys] + // The Leavers will leave the network + for i := 0; i < parallelLeaver; i++ { + logrus.Infof("worker leaveNetwork: %d on IP:%s", i, ips[i]) + go leaveNetwork(ips[i], servicePort, networkName, doneCh) + // Once a node leave all the keys written previously will be deleted, so the expected keys will consider that as removed + keysExpected -= keyMap[ips[i]] + } + waitWriters(parallelLeaver, false, doneCh) + + // Give some time + time.Sleep(100 * time.Millisecond) + + // The writers will join the network + for i := 0; i < parallelLeaver; i++ { + logrus.Infof("worker joinNetwork: %d on IP:%s", i, ips[i]) + go joinNetwork(ips[i], servicePort, networkName, doneCh) + } + waitWriters(parallelLeaver, false, doneCh) + + // check table entries for 2 minutes + ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute) + opTime := checkTable(ctx, ips, servicePort, networkName, tableName, keysExpected, dbTableEntriesNumber) + cancel() + fmt.Fprintf(os.Stderr, "doWriteWaitLeaveJoin succeeded in %d msec", opTime) +} + +var cmdArgChec = map[string]int{ + "debug": 0, + "fail": 0, + "ready": 2, + "join": 2, + "leave": 2, + "join-network": 3, + "leave-network": 3, + "cluster-peers": 5, + "network-peers": 5, + "write-delete-unique-keys": 7, +} + +// Client is a client +func Client(args []string) { + logrus.Infof("[CLIENT] Starting with arguments %v", args) + command := args[0] + + if len(args) < cmdArgChec[command] { + log.Fatalf("Command %s requires %d arguments, passed %d, aborting...", command, cmdArgChec[command], len(args)) + } + + switch command { + case "debug": + time.Sleep(1 * time.Hour) + os.Exit(0) + case "fail": + log.Fatalf("Test error condition with message: error error error") + } + + serviceName := args[1] + ips, _ := net.LookupHost("tasks." + serviceName) + logrus.Infof("got the ips %v", ips) + if len(ips) == 0 { + log.Fatalf("Cannot resolve any IP for the service tasks.%s", serviceName) + } + servicePort = args[2] + commandArgs := args[3:] + logrus.Infof("Executing %s with args:%v", command, commandArgs) + switch command { + case "ready": + doReady(ips) + case "join": + doJoin(ips) + case "leave": + + case "cluster-peers": + // cluster-peers maxRetry + doClusterPeers(ips, commandArgs) + + case "join-network": + // join-network networkName + doJoinNetwork(ips, commandArgs) + case "leave-network": + // leave-network networkName + doLeaveNetwork(ips, commandArgs) + case "network-peers": + // network-peers networkName expectedNumberPeers maxRetry + doNetworkPeers(ips, commandArgs) + // case "network-stats-entries": + // // network-stats-entries networkName maxRetry + // doNetworkPeers(ips, commandArgs) + case "network-stats-queue": + // network-stats-queue networkName queueSize + doNetworkStatsQueue(ips, commandArgs) + + case "write-keys": + // write-keys networkName tableName parallelWriters numberOfKeysEach + doWriteKeys(ips, commandArgs) + case "delete-keys": + // delete-keys networkName tableName parallelWriters numberOfKeysEach + doDeleteKeys(ips, commandArgs) + case "write-unique-keys": + // write-delete-unique-keys networkName tableName numParallelWriters writeTimeSec + doWriteUniqueKeys(ips, commandArgs) + case "write-delete-unique-keys": + // write-delete-unique-keys networkName tableName numParallelWriters writeTimeSec + doWriteDeleteUniqueKeys(ips, commandArgs) + case "write-delete-leave-join": + // write-delete-leave-join networkName tableName numParallelWriters writeTimeSec + doWriteDeleteLeaveJoin(ips, commandArgs) + case "write-delete-wait-leave-join": + // write-delete-wait-leave-join networkName tableName numParallelWriters writeTimeSec + doWriteDeleteWaitLeaveJoin(ips, commandArgs) + case "write-wait-leave": + // write-wait-leave networkName tableName numParallelWriters writeTimeSec + doWriteWaitLeave(ips, commandArgs) + case "write-wait-leave-join": + // write-wait-leave networkName tableName numParallelWriters writeTimeSec + doWriteWaitLeaveJoin(ips, commandArgs) + default: + log.Fatalf("Command %s not recognized", command) + } +} diff --git a/libnetwork/cmd/networkdb-test/dbserver/ndbServer.go b/libnetwork/cmd/networkdb-test/dbserver/ndbServer.go new file mode 100644 index 0000000000000..fc836bd22e5d7 --- /dev/null +++ b/libnetwork/cmd/networkdb-test/dbserver/ndbServer.go @@ -0,0 +1,111 @@ +package dbserver + +import ( + "errors" + "fmt" + "log" + "net" + "net/http" + "os" + "strconv" + + "github.com/docker/docker/libnetwork/cmd/networkdb-test/dummyclient" + "github.com/docker/docker/libnetwork/diagnostic" + "github.com/docker/docker/libnetwork/networkdb" + "github.com/sirupsen/logrus" +) + +var nDB *networkdb.NetworkDB +var server *diagnostic.Server +var ipAddr string + +var testerPaths2Func = map[string]diagnostic.HTTPHandlerFunc{ + "/myip": ipaddress, +} + +func ipaddress(ctx interface{}, w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "%s\n", ipAddr) +} + +// Server starts the server +func Server(args []string) { + logrus.Infof("[SERVER] Starting with arguments %v", args) + if len(args) < 1 { + log.Fatal("Port number is a mandatory argument, aborting...") + } + port, _ := strconv.Atoi(args[0]) + var localNodeName string + var ok bool + if localNodeName, ok = os.LookupEnv("TASK_ID"); !ok { + log.Fatal("TASK_ID environment variable not set, aborting...") + } + logrus.Infof("[SERVER] Starting node %s on port %d", localNodeName, port) + + ip, err := getIPInterface("eth0") + if err != nil { + logrus.Errorf("%s There was a problem with the IP %s\n", localNodeName, err) + return + } + ipAddr = ip + logrus.Infof("%s uses IP %s\n", localNodeName, ipAddr) + + server = diagnostic.New() + server.Init() + conf := networkdb.DefaultConfig() + conf.Hostname = localNodeName + conf.AdvertiseAddr = ipAddr + conf.BindAddr = ipAddr + nDB, err = networkdb.New(conf) + if err != nil { + logrus.Infof("%s error in the DB init %s\n", localNodeName, err) + return + } + + // Register network db handlers + server.RegisterHandler(nDB, networkdb.NetDbPaths2Func) + server.RegisterHandler(nil, testerPaths2Func) + server.RegisterHandler(nDB, dummyclient.DummyClientPaths2Func) + server.EnableDiagnostic("", port) + // block here + select {} +} + +func getIPInterface(name string) (string, error) { + ifaces, err := net.Interfaces() + if err != nil { + return "", err + } + for _, iface := range ifaces { + if iface.Name != name { + continue // not the name specified + } + + if iface.Flags&net.FlagUp == 0 { + return "", errors.New("Interfaces is down") + } + + addrs, err := iface.Addrs() + if err != nil { + return "", err + } + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if ip == nil || ip.IsLoopback() { + continue + } + ip = ip.To4() + if ip == nil { + continue + } + return ip.String(), nil + } + return "", errors.New("Interfaces does not have a valid IPv4") + } + return "", errors.New("Interface not found") +} diff --git a/libnetwork/cmd/networkdb-test/dummyclient/dummyClient.go b/libnetwork/cmd/networkdb-test/dummyclient/dummyClient.go new file mode 100644 index 0000000000000..20c2d4132807b --- /dev/null +++ b/libnetwork/cmd/networkdb-test/dummyclient/dummyClient.go @@ -0,0 +1,120 @@ +package dummyclient + +import ( + "fmt" + "log" + "net/http" + + "github.com/docker/docker/libnetwork/diagnostic" + "github.com/docker/docker/libnetwork/networkdb" + events "github.com/docker/go-events" + "github.com/sirupsen/logrus" +) + +// DummyClientPaths2Func exported paths for the client +var DummyClientPaths2Func = map[string]diagnostic.HTTPHandlerFunc{ + "/watchtable": watchTable, + "/watchedtableentries": watchTableEntries, +} + +const ( + missingParameter = "missing parameter" +) + +type tableHandler struct { + cancelWatch func() + entries map[string]string +} + +var clientWatchTable = map[string]tableHandler{} + +func watchTable(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() // nolint:errcheck + diagnostic.DebugHTTPForm(r) + if len(r.Form["tname"]) < 1 { + rsp := diagnostic.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name", r.URL.Path)) + diagnostic.HTTPReply(w, rsp, &diagnostic.JSONOutput{}) // nolint:errcheck + return + } + + tableName := r.Form["tname"][0] + if _, ok := clientWatchTable[tableName]; ok { + fmt.Fprintf(w, "OK\n") + return + } + + nDB, ok := ctx.(*networkdb.NetworkDB) + if ok { + ch, cancel := nDB.Watch(tableName, "", "") + clientWatchTable[tableName] = tableHandler{cancelWatch: cancel, entries: make(map[string]string)} + go handleTableEvents(tableName, ch) + + fmt.Fprintf(w, "OK\n") + } +} + +func watchTableEntries(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() // nolint:errcheck + diagnostic.DebugHTTPForm(r) + if len(r.Form["tname"]) < 1 { + rsp := diagnostic.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name", r.URL.Path)) + diagnostic.HTTPReply(w, rsp, &diagnostic.JSONOutput{}) // nolint:errcheck + return + } + + tableName := r.Form["tname"][0] + table, ok := clientWatchTable[tableName] + if !ok { + fmt.Fprintf(w, "Table %s not watched\n", tableName) + return + } + + fmt.Fprintf(w, "total elements: %d\n", len(table.entries)) + i := 0 + for k, v := range table.entries { + fmt.Fprintf(w, "%d) k:`%s` -> v:`%s`\n", i, k, v) + i++ + } +} + +func handleTableEvents(tableName string, ch *events.Channel) { + var ( + // nid string + eid string + value []byte + isAdd bool + ) + + logrus.Infof("Started watching table:%s", tableName) + for { + select { + case <-ch.Done(): + logrus.Infof("End watching %s", tableName) + return + + case evt := <-ch.C: + logrus.Infof("Recevied new event on:%s", tableName) + switch event := evt.(type) { + case networkdb.CreateEvent: + // nid = event.NetworkID + eid = event.Key + value = event.Value + isAdd = true + case networkdb.DeleteEvent: + // nid = event.NetworkID + eid = event.Key + value = event.Value + isAdd = false + default: + log.Fatalf("Unexpected table event = %#v", event) + } + if isAdd { + // logrus.Infof("Add %s %s", tableName, eid) + clientWatchTable[tableName].entries[eid] = string(value) + } else { + // logrus.Infof("Del %s %s", tableName, eid) + delete(clientWatchTable[tableName].entries, eid) + } + } + } +} diff --git a/libnetwork/cmd/networkdb-test/testMain.go b/libnetwork/cmd/networkdb-test/testMain.go new file mode 100644 index 0000000000000..8731e3440d33e --- /dev/null +++ b/libnetwork/cmd/networkdb-test/testMain.go @@ -0,0 +1,28 @@ +package main + +import ( + "log" + "os" + + "github.com/docker/docker/libnetwork/cmd/networkdb-test/dbclient" + "github.com/docker/docker/libnetwork/cmd/networkdb-test/dbserver" + "github.com/sirupsen/logrus" +) + +func main() { + formatter := &logrus.TextFormatter{ + FullTimestamp: true, + } + logrus.SetFormatter(formatter) + logrus.Infof("Starting the image with these args: %v", os.Args) + if len(os.Args) < 1 { + log.Fatal("You need at least 1 argument [client/server]") + } + + switch os.Args[1] { + case "server": + dbserver.Server(os.Args[2:]) + case "client": + dbclient.Client(os.Args[2:]) + } +} diff --git a/libnetwork/cmd/ovrouter/ovrouter.go b/libnetwork/cmd/ovrouter/ovrouter.go new file mode 100644 index 0000000000000..bca34d504f6c6 --- /dev/null +++ b/libnetwork/cmd/ovrouter/ovrouter.go @@ -0,0 +1,176 @@ +//go:build linux +// +build linux + +package main + +import ( + "fmt" + "net" + "os" + "os/signal" + + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/drivers/overlay" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" + "github.com/docker/docker/pkg/plugingetter" + "github.com/docker/docker/pkg/reexec" + "github.com/vishvananda/netlink" +) + +type router struct { + d driverapi.Driver +} + +type endpoint struct { + addr *net.IPNet + mac net.HardwareAddr + name string +} + +func (r *router) GetPluginGetter() plugingetter.PluginGetter { + return nil +} + +func (r *router) RegisterDriver(name string, driver driverapi.Driver, c driverapi.Capability) error { + r.d = driver + return nil +} + +func (ep *endpoint) Interface() driverapi.InterfaceInfo { + return nil +} + +func (ep *endpoint) SetMacAddress(mac net.HardwareAddr) error { + if ep.mac != nil { + return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", ep.mac, mac) + } + if mac == nil { + return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface") + } + ep.mac = types.GetMacCopy(mac) + return nil +} + +func (ep *endpoint) SetIPAddress(address *net.IPNet) error { + if address.IP == nil { + return types.BadRequestErrorf("tried to set nil IP address to endpoint interface") + } + if address.IP.To4() == nil { + return types.NotImplementedErrorf("do not support ipv6 yet") + } + if ep.addr != nil { + return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with %s.", ep.addr, address) + } + ep.addr = types.GetIPNetCopy(address) + return nil +} + +func (ep *endpoint) MacAddress() net.HardwareAddr { + return types.GetMacCopy(ep.mac) +} + +func (ep *endpoint) Address() *net.IPNet { + return types.GetIPNetCopy(ep.addr) +} + +func (ep *endpoint) AddressIPv6() *net.IPNet { + return nil +} + +func (ep *endpoint) InterfaceName() driverapi.InterfaceNameInfo { + return ep +} + +func (ep *endpoint) SetNames(srcName, dstPrefix string) error { + ep.name = srcName + return nil +} + +func (ep *endpoint) SetGateway(net.IP) error { + return nil +} + +func (ep *endpoint) SetGatewayIPv6(net.IP) error { + return nil +} + +func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int, + nextHop net.IP) error { + return nil +} + +func (ep *endpoint) AddTableEntry(tableName string, key string, value []byte) error { + return nil +} + +func (ep *endpoint) DisableGatewayService() {} + +func main() { + if reexec.Init() { + return + } + + opt := make(map[string]interface{}) + if len(os.Args) > 1 { + opt[netlabel.OverlayBindInterface] = os.Args[1] + } + if len(os.Args) > 2 { + opt[netlabel.OverlayNeighborIP] = os.Args[2] + } + if len(os.Args) > 3 { + opt[netlabel.GlobalKVProvider] = os.Args[3] + } + if len(os.Args) > 4 { + opt[netlabel.GlobalKVProviderURL] = os.Args[4] + } + + r := &router{} + if err := overlay.Init(r, opt); err != nil { + fmt.Printf("Failed to initialize overlay driver: %v\n", err) + os.Exit(1) + } + + if err := r.d.CreateNetwork("testnetwork", + map[string]interface{}{}, nil, nil, nil); err != nil { + fmt.Printf("Failed to create network in the driver: %v\n", err) + os.Exit(1) + } + + ep := &endpoint{} + if err := r.d.CreateEndpoint("testnetwork", "testep", + ep, map[string]interface{}{}); err != nil { + fmt.Printf("Failed to create endpoint in the driver: %v\n", err) + os.Exit(1) + } + + if err := r.d.Join("testnetwork", "testep", + "", ep, map[string]interface{}{}); err != nil { + fmt.Printf("Failed to join an endpoint in the driver: %v\n", err) + os.Exit(1) + } + + link, err := netlink.LinkByName(ep.name) + if err != nil { + fmt.Printf("Failed to find the container interface with name %s: %v\n", + ep.name, err) + os.Exit(1) + } + + ipAddr := &netlink.Addr{IPNet: ep.addr, Label: ""} + if err := netlink.AddrAdd(link, ipAddr); err != nil { + fmt.Printf("Failed to add address to the interface: %v\n", err) + os.Exit(1) + } + + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, os.Interrupt) + + for range sigCh { + if err := r.d.Leave("testnetwork", "testep"); err != nil { + fmt.Printf("Error leaving: %v", err) + } + overlay.Fini(r.d) + os.Exit(0) + } +} diff --git a/libnetwork/cmd/readme_test/readme.go b/libnetwork/cmd/readme_test/readme.go new file mode 100644 index 0000000000000..8be24ab9b6e1e --- /dev/null +++ b/libnetwork/cmd/readme_test/readme.go @@ -0,0 +1,74 @@ +package main + +import ( + "fmt" + "log" + + "github.com/docker/docker/libnetwork" + "github.com/docker/docker/libnetwork/config" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/pkg/reexec" +) + +func main() { + if reexec.Init() { + return + } + + // Select and configure the network driver + networkType := "bridge" + + // Create a new controller instance + driverOptions := options.Generic{} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = driverOptions + controller, err := libnetwork.New(config.OptionDriverConfig(networkType, genericOption)) + if err != nil { + log.Fatalf("libnetwork.New: %s", err) + } + + // Create a network for containers to join. + // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can use. + network, err := controller.NewNetwork(networkType, "network1", "") + if err != nil { + log.Fatalf("controller.NewNetwork: %s", err) + } + + // For each new container: allocate IP and interfaces. The returned network + // settings will be used for container infos (inspect and such), as well as + // iptables rules for port publishing. This info is contained or accessible + // from the returned endpoint. + ep, err := network.CreateEndpoint("Endpoint1") + if err != nil { + log.Fatalf("network.CreateEndpoint: %s", err) + } + + // Create the sandbox for the container. + // NewSandbox accepts Variadic optional arguments which libnetwork can use. + sbx, err := controller.NewSandbox("container1", + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io")) + if err != nil { + log.Fatalf("controller.NewSandbox: %s", err) + } + + // A sandbox can join the endpoint via the join api. + err = ep.Join(sbx) + if err != nil { + log.Fatalf("ep.Join: %s", err) + } + + // libnetwork client can check the endpoint's operational data via the Info() API + epInfo, err := ep.DriverInfo() + if err != nil { + log.Fatalf("ep.DriverInfo: %s", err) + } + + macAddress, ok := epInfo[netlabel.MacAddress] + if !ok { + log.Fatal("failed to get mac address from endpoint info") + } + + fmt.Printf("Joined endpoint %s (%s) to sandbox %s (%s)\n", ep.Name(), macAddress, sbx.ContainerID(), sbx.Key()) +} diff --git a/libnetwork/cmd/ssd/Dockerfile b/libnetwork/cmd/ssd/Dockerfile new file mode 100755 index 0000000000000..05422479207a2 --- /dev/null +++ b/libnetwork/cmd/ssd/Dockerfile @@ -0,0 +1,34 @@ +FROM alpine:3.7 +ENV PACKAGES="\ + musl \ + linux-headers \ + build-base \ + util-linux \ + bash \ + git \ + ca-certificates \ + python2 \ + python2-dev \ + py-setuptools \ + iproute2 \ + curl \ + strace \ + drill \ + ipvsadm \ + iperf \ + ethtool \ +" + +RUN echo \ + && apk add --no-cache $PACKAGES \ + && if [[ ! -e /usr/bin/python ]]; then ln -sf /usr/bin/python2.7 /usr/bin/python; fi \ + && if [[ ! -e /usr/bin/python-config ]]; then ln -sf /usr/bin/python2.7-config /usr/bin/python-config; fi \ + && if [[ ! -e /usr/bin/easy_install ]]; then ln -sf /usr/bin/easy_install-2.7 /usr/bin/easy_install; fi \ + && easy_install pip \ + && pip install --upgrade pip \ + && if [[ ! -e /usr/bin/pip ]]; then ln -sf /usr/bin/pip2.7 /usr/bin/pip; fi \ + && echo + +ADD ssd.py / +RUN pip install git+git://github.com/docker/docker-py.git +ENTRYPOINT [ "python", "/ssd.py"] diff --git a/libnetwork/cmd/ssd/README.md b/libnetwork/cmd/ssd/README.md new file mode 100755 index 0000000000000..a0a0048da5ed3 --- /dev/null +++ b/libnetwork/cmd/ssd/README.md @@ -0,0 +1,47 @@ +# Docker Swarm Service Driller(ssd) + +ssd is a troubleshooting utility for Docker swarm networks. + +### control-plane and datapath consistency check on a node +ssd checks for the consistency between docker network control-plane (from the docker daemon in-memory state) and kernel data path programming. Currently the tool checks only for the consistency of the Load balancer (implemented using IPVS). + +In a three node swarm cluser ssd status for a overlay network `ov2` which has three services running, each replicated to 3 instances. + +````bash +vagrant@net-1:~/code/go/src/github.com/docker/docker-e2e/tests$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v /var/run/docker/netns:/var/run/docker/netns --privileged --net=host sanimej/ssd ov2 +Verifying LB programming for containers on network ov2 +Verifying container /s2.3.ltrdwef0iqf90rqauw3ehcs56... +service s2... OK +service s3... OK +service s1... OK +Verifying container /s3.3.nyhwvdvnocb4wftyhb8dr4fj8... +service s2... OK +service s3... OK +service s1... OK +Verifying container /s1.3.wwx5tuxhnvoz5vrb8ohphby0r... +service s2... OK +service s3... OK +service s1... OK +Verifying LB programming for containers on network ingress +Verifying container Ingress... +service web... OK +```` + +ssd checks the required iptables programming to direct an incoming packet with the : to the right : + +### control-plane consistency check across nodes in a cluster + +Docker networking uses a gossip protocol to synchronize networking state across nodes in a cluster. ssd's `gossip-consistency` command verifies if the state maintained by all the nodes are consistent. + +````bash +In a three node cluster with services running on an overlay network ov2 ssd consistency-checker shows + +vagrant@net-1:~/code/go/src/github.com/docker/docker-e2e/tests$ docker run -v /var/run/docker.sock:/var/run/docker.sock -v /var/run/docker/netns:/var/run/docker/netns --privileged sanimej/ssd ov2 gossip-consistency +Node id: sjfp0ca8f43rvnab6v7f21gq0 gossip hash c57d89094dbb574a37930393278dc282 + +Node id: bg228r3q9095grj4wxkqs80oe gossip hash c57d89094dbb574a37930393278dc282 + +Node id: 6jylcraipcv2pxdricqe77j5q gossip hash c57d89094dbb574a37930393278dc282 +```` + +This is hash digest of the control-plane state for the network `ov2` from all the cluster nodes. If the values have a mismatch `docker network inspect --verbose` on the individual nodes can help in identifying what the specific difference is. diff --git a/libnetwork/cmd/ssd/ssd.py b/libnetwork/cmd/ssd/ssd.py new file mode 100755 index 0000000000000..ad58536ff88d2 --- /dev/null +++ b/libnetwork/cmd/ssd/ssd.py @@ -0,0 +1,193 @@ +#!/usr/bin/python + +import sys, signal, time, os +import docker +import re +import subprocess +import json +import hashlib + +ipv4match = re.compile( + r'(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9]).' + + r'(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9]).' + + r'(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9]).' + + r'(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])' +) + +def which(name, defaultPath=""): + if defaultPath and os.path.exists(defaultPath): + return defaultPath + for path in os.getenv("PATH").split(os.path.pathsep): + fullPath = path + os.sep + name + if os.path.exists(fullPath): + return fullPath + +def check_iptables(name, plist): + replace = (':', ',') + ports = [] + for port in plist: + for r in replace: + port = port.replace(r, ' ') + + p = port.split() + ports.append((p[1], p[3])) + + # get the ingress sandbox's docker_gwbridge network IP. + # published ports get DNAT'ed to this IP. + ip = subprocess.check_output([ which("nsenter","/usr/bin/nsenter"), '--net=/var/run/docker/netns/ingress_sbox', which("bash", "/bin/bash"), '-c', 'ifconfig eth1 | grep \"inet\\ addr\" | cut -d: -f2 | cut -d\" \" -f1']) + ip = ip.rstrip() + + for p in ports: + rule = which("iptables", "/sbin/iptables") + '-t nat -C DOCKER-INGRESS -p tcp --dport {0} -j DNAT --to {1}:{2}'.format(p[1], ip, p[1]) + try: + subprocess.check_output([which("bash", "/bin/bash"), "-c", rule]) + except subprocess.CalledProcessError as e: + print "Service {0}: host iptables DNAT rule for port {1} -> ingress sandbox {2}:{3} missing".format(name, p[1], ip, p[1]) + +def get_namespaces(data, ingress=False): + if ingress is True: + return {"Ingress":"/var/run/docker/netns/ingress_sbox"} + else: + spaces =[] + for c in data["Containers"]: + sandboxes = {str(c) for c in data["Containers"]} + + containers = {} + for s in sandboxes: + spaces.append(str(cli.inspect_container(s)["NetworkSettings"]["SandboxKey"])) + inspect = cli.inspect_container(s) + containers[str(inspect["Name"])] = str(inspect["NetworkSettings"]["SandboxKey"]) + return containers + + +def check_network(nw_name, ingress=False): + + print "Verifying LB programming for containers on network %s" % nw_name + + data = cli.inspect_network(nw_name, verbose=True) + + if "Services" in data.keys(): + services = data["Services"] + else: + print "Network %s has no services. Skipping check" % nw_name + return + + fwmarks = {str(service): str(svalue["LocalLBIndex"]) for service, svalue in services.items()} + + stasks = {} + for service, svalue in services.items(): + if service == "": + continue + tasks = [] + for task in svalue["Tasks"]: + tasks.append(str(task["EndpointIP"])) + stasks[fwmarks[str(service)]] = tasks + + # for services in ingress network verify the iptables rules + # that direct ingress (published port) to backend (target port) + if ingress is True: + check_iptables(service, svalue["Ports"]) + + containers = get_namespaces(data, ingress) + for container, namespace in containers.items(): + print "Verifying container %s..." % container + ipvs = subprocess.check_output([which("nsenter","/usr/bin/nsenter"), '--net=%s' % namespace, which("ipvsadm","/usr/sbin/ipvsadm"), '-ln']) + + mark = "" + realmark = {} + for line in ipvs.splitlines(): + if "FWM" in line: + mark = re.findall("[0-9]+", line)[0] + realmark[str(mark)] = [] + elif "->" in line: + if mark == "": + continue + ip = ipv4match.search(line) + if ip is not None: + realmark[mark].append(format(ip.group(0))) + else: + mark = "" + for key in realmark.keys(): + if key not in stasks: + print "LB Index %s" % key, "present in IPVS but missing in docker daemon" + del realmark[key] + + for key in stasks.keys(): + if key not in realmark: + print "LB Index %s" % key, "present in docker daemon but missing in IPVS" + del stasks[key] + + for key in realmark: + service = "--Invalid--" + for sname, idx in fwmarks.items(): + if key == idx: + service = sname + if len(set(realmark[key])) != len(set(stasks[key])): + print "Incorrect LB Programming for service %s" % service + print "control-plane backend tasks:" + for task in stasks[key]: + print task + print "kernel IPVS backend tasks:" + for task in realmark[key]: + print task + else: + print "service %s... OK" % service + +if __name__ == '__main__': + if len(sys.argv) < 2: + print 'Usage: ssd.py network-name [gossip-consistency]' + sys.exit() + + cli = docker.APIClient(base_url='unix://var/run/docker.sock', version='auto') + if len(sys.argv) == 3: + command = sys.argv[2] + else: + command = 'default' + + if command == 'gossip-consistency': + cspec = docker.types.ContainerSpec( + image='docker/ssd', + args=[sys.argv[1], 'gossip-hash'], + mounts=[docker.types.Mount('/var/run/docker.sock', '/var/run/docker.sock', type='bind')] + ) + mode = docker.types.ServiceMode( + mode='global' + ) + task_template = docker.types.TaskTemplate(cspec) + + cli.create_service(task_template, name='gossip-hash', mode=mode) + #TODO change to a deterministic way to check if the service is up. + time.sleep(5) + output = cli.service_logs('gossip-hash', stdout=True, stderr=True, details=True) + for line in output: + print("Node id: %s gossip hash %s" % (line[line.find("=")+1:line.find(",")], line[line.find(" ")+1:])) + if cli.remove_service('gossip-hash') is not True: + print("Deleting gossip-hash service failed") + elif command == 'gossip-hash': + data = cli.inspect_network(sys.argv[1], verbose=True) + services = data["Services"] + md5 = hashlib.md5() + entries = [] + for service, value in services.items(): + entries.append(service) + entries.append(value["VIP"]) + for task in value["Tasks"]: + for key, val in task.items(): + if isinstance(val, dict): + for k, v in val.items(): + entries.append(v) + else: + entries.append(val) + entries.sort() + for e in entries: + md5.update(e) + print(md5.hexdigest()) + sys.stdout.flush() + while True: + signal.pause() + elif command == 'default': + if sys.argv[1] == "ingress": + check_network("ingress", ingress=True) + else: + check_network(sys.argv[1]) + check_network("ingress", ingress=True) diff --git a/libnetwork/config/config.go b/libnetwork/config/config.go new file mode 100644 index 0000000000000..c5efbd2bcf9ab --- /dev/null +++ b/libnetwork/config/config.go @@ -0,0 +1,332 @@ +package config + +import ( + "fmt" + "os" + "strings" + + "github.com/docker/docker/libnetwork/cluster" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/ipamutils" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/portallocator" + "github.com/docker/docker/pkg/discovery" + "github.com/docker/docker/pkg/plugingetter" + "github.com/docker/go-connections/tlsconfig" + "github.com/docker/libkv/store" + "github.com/pelletier/go-toml" + "github.com/sirupsen/logrus" +) + +const ( + warningThNetworkControlPlaneMTU = 1500 + minimumNetworkControlPlaneMTU = 500 +) + +// Config encapsulates configurations of various Libnetwork components +type Config struct { + Daemon DaemonCfg + Cluster ClusterCfg + Scopes map[string]*datastore.ScopeCfg + ActiveSandboxes map[string]interface{} + PluginGetter plugingetter.PluginGetter +} + +// DaemonCfg represents libnetwork core configuration +type DaemonCfg struct { + Debug bool + Experimental bool + DataDir string + ExecRoot string + DefaultNetwork string + DefaultDriver string + Labels []string + DriverCfg map[string]interface{} + ClusterProvider cluster.Provider + NetworkControlPlaneMTU int + DefaultAddressPool []*ipamutils.NetworkToSplit +} + +// ClusterCfg represents cluster configuration +type ClusterCfg struct { + Watcher discovery.Watcher + Address string + Discovery string + Heartbeat uint64 +} + +// LoadDefaultScopes loads default scope configs for scopes which +// doesn't have explicit user specified configs. +func (c *Config) LoadDefaultScopes(dataDir string) { + for k, v := range datastore.DefaultScopes(dataDir) { + if _, ok := c.Scopes[k]; !ok { + c.Scopes[k] = v + } + } +} + +// ParseConfig parses the libnetwork configuration file +func ParseConfig(tomlCfgFile string) (*Config, error) { + cfg := &Config{ + Scopes: map[string]*datastore.ScopeCfg{}, + } + data, err := os.ReadFile(tomlCfgFile) + if err != nil { + return nil, err + } + if err := toml.Unmarshal(data, cfg); err != nil { + return nil, err + } + + cfg.LoadDefaultScopes(cfg.Daemon.DataDir) + return cfg, nil +} + +// ParseConfigOptions parses the configuration options and returns +// a reference to the corresponding Config structure +func ParseConfigOptions(cfgOptions ...Option) *Config { + cfg := &Config{ + Daemon: DaemonCfg{ + DriverCfg: make(map[string]interface{}), + }, + Scopes: make(map[string]*datastore.ScopeCfg), + } + + cfg.ProcessOptions(cfgOptions...) + cfg.LoadDefaultScopes(cfg.Daemon.DataDir) + + return cfg +} + +// Option is an option setter function type used to pass various configurations +// to the controller +type Option func(c *Config) + +// OptionDefaultNetwork function returns an option setter for a default network +func OptionDefaultNetwork(dn string) Option { + return func(c *Config) { + logrus.Debugf("Option DefaultNetwork: %s", dn) + c.Daemon.DefaultNetwork = strings.TrimSpace(dn) + } +} + +// OptionDefaultDriver function returns an option setter for default driver +func OptionDefaultDriver(dd string) Option { + return func(c *Config) { + logrus.Debugf("Option DefaultDriver: %s", dd) + c.Daemon.DefaultDriver = strings.TrimSpace(dd) + } +} + +// OptionDefaultAddressPoolConfig function returns an option setter for default address pool +func OptionDefaultAddressPoolConfig(addressPool []*ipamutils.NetworkToSplit) Option { + return func(c *Config) { + c.Daemon.DefaultAddressPool = addressPool + } +} + +// OptionDriverConfig returns an option setter for driver configuration. +func OptionDriverConfig(networkType string, config map[string]interface{}) Option { + return func(c *Config) { + c.Daemon.DriverCfg[networkType] = config + } +} + +// OptionLabels function returns an option setter for labels +func OptionLabels(labels []string) Option { + return func(c *Config) { + for _, label := range labels { + if strings.HasPrefix(label, netlabel.Prefix) { + c.Daemon.Labels = append(c.Daemon.Labels, label) + } + } + } +} + +// OptionKVProvider function returns an option setter for kvstore provider +func OptionKVProvider(provider string) Option { + return func(c *Config) { + logrus.Debugf("Option OptionKVProvider: %s", provider) + if _, ok := c.Scopes[datastore.GlobalScope]; !ok { + c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{} + } + c.Scopes[datastore.GlobalScope].Client.Provider = strings.TrimSpace(provider) + } +} + +// OptionKVProviderURL function returns an option setter for kvstore url +func OptionKVProviderURL(url string) Option { + return func(c *Config) { + logrus.Debugf("Option OptionKVProviderURL: %s", url) + if _, ok := c.Scopes[datastore.GlobalScope]; !ok { + c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{} + } + c.Scopes[datastore.GlobalScope].Client.Address = strings.TrimSpace(url) + } +} + +// OptionKVOpts function returns an option setter for kvstore options +func OptionKVOpts(opts map[string]string) Option { + return func(c *Config) { + if opts["kv.cacertfile"] != "" && opts["kv.certfile"] != "" && opts["kv.keyfile"] != "" { + logrus.Info("Option Initializing KV with TLS") + tlsConfig, err := tlsconfig.Client(tlsconfig.Options{ + CAFile: opts["kv.cacertfile"], + CertFile: opts["kv.certfile"], + KeyFile: opts["kv.keyfile"], + }) + if err != nil { + logrus.Errorf("Unable to set up TLS: %s", err) + return + } + if _, ok := c.Scopes[datastore.GlobalScope]; !ok { + c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{} + } + if c.Scopes[datastore.GlobalScope].Client.Config == nil { + c.Scopes[datastore.GlobalScope].Client.Config = &store.Config{TLS: tlsConfig} + } else { + c.Scopes[datastore.GlobalScope].Client.Config.TLS = tlsConfig + } + // Workaround libkv/etcd bug for https + c.Scopes[datastore.GlobalScope].Client.Config.ClientTLS = &store.ClientTLSConfig{ + CACertFile: opts["kv.cacertfile"], + CertFile: opts["kv.certfile"], + KeyFile: opts["kv.keyfile"], + } + } else { + logrus.Info("Option Initializing KV without TLS") + } + } +} + +// OptionDiscoveryWatcher function returns an option setter for discovery watcher +func OptionDiscoveryWatcher(watcher discovery.Watcher) Option { + return func(c *Config) { + c.Cluster.Watcher = watcher + } +} + +// OptionDiscoveryAddress function returns an option setter for self discovery address +func OptionDiscoveryAddress(address string) Option { + return func(c *Config) { + c.Cluster.Address = address + } +} + +// OptionDataDir function returns an option setter for data folder +func OptionDataDir(dataDir string) Option { + return func(c *Config) { + c.Daemon.DataDir = dataDir + } +} + +// OptionExecRoot function returns an option setter for exec root folder +func OptionExecRoot(execRoot string) Option { + return func(c *Config) { + c.Daemon.ExecRoot = execRoot + osl.SetBasePath(execRoot) + } +} + +// OptionPluginGetter returns a plugingetter for remote drivers. +func OptionPluginGetter(pg plugingetter.PluginGetter) Option { + return func(c *Config) { + c.PluginGetter = pg + } +} + +// OptionExperimental function returns an option setter for experimental daemon +func OptionExperimental(exp bool) Option { + return func(c *Config) { + logrus.Debugf("Option Experimental: %v", exp) + c.Daemon.Experimental = exp + } +} + +// OptionDynamicPortRange function returns an option setter for service port allocation range +func OptionDynamicPortRange(in string) Option { + return func(c *Config) { + start, end := 0, 0 + if len(in) > 0 { + n, err := fmt.Sscanf(in, "%d-%d", &start, &end) + if n != 2 || err != nil { + logrus.Errorf("Failed to parse range string with err %v", err) + return + } + } + if err := portallocator.Get().SetPortRange(start, end); err != nil { + logrus.Errorf("Failed to set port range with err %v", err) + } + } +} + +// OptionNetworkControlPlaneMTU function returns an option setter for control plane MTU +func OptionNetworkControlPlaneMTU(exp int) Option { + return func(c *Config) { + logrus.Debugf("Network Control Plane MTU: %d", exp) + if exp < warningThNetworkControlPlaneMTU { + logrus.Warnf("Received a MTU of %d, this value is very low, the network control plane can misbehave,"+ + " defaulting to minimum value (%d)", exp, minimumNetworkControlPlaneMTU) + if exp < minimumNetworkControlPlaneMTU { + exp = minimumNetworkControlPlaneMTU + } + } + c.Daemon.NetworkControlPlaneMTU = exp + } +} + +// ProcessOptions processes options and stores it in config +func (c *Config) ProcessOptions(options ...Option) { + for _, opt := range options { + if opt != nil { + opt(c) + } + } +} + +// IsValidName validates configuration objects supported by libnetwork +func IsValidName(name string) bool { + return strings.TrimSpace(name) != "" +} + +// OptionLocalKVProvider function returns an option setter for kvstore provider +func OptionLocalKVProvider(provider string) Option { + return func(c *Config) { + logrus.Debugf("Option OptionLocalKVProvider: %s", provider) + if _, ok := c.Scopes[datastore.LocalScope]; !ok { + c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{} + } + c.Scopes[datastore.LocalScope].Client.Provider = strings.TrimSpace(provider) + } +} + +// OptionLocalKVProviderURL function returns an option setter for kvstore url +func OptionLocalKVProviderURL(url string) Option { + return func(c *Config) { + logrus.Debugf("Option OptionLocalKVProviderURL: %s", url) + if _, ok := c.Scopes[datastore.LocalScope]; !ok { + c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{} + } + c.Scopes[datastore.LocalScope].Client.Address = strings.TrimSpace(url) + } +} + +// OptionLocalKVProviderConfig function returns an option setter for kvstore config +func OptionLocalKVProviderConfig(config *store.Config) Option { + return func(c *Config) { + logrus.Debugf("Option OptionLocalKVProviderConfig: %v", config) + if _, ok := c.Scopes[datastore.LocalScope]; !ok { + c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{} + } + c.Scopes[datastore.LocalScope].Client.Config = config + } +} + +// OptionActiveSandboxes function returns an option setter for passing the sandboxes +// which were active during previous daemon life +func OptionActiveSandboxes(sandboxes map[string]interface{}) Option { + return func(c *Config) { + c.ActiveSandboxes = sandboxes + } +} diff --git a/libnetwork/config/config_test.go b/libnetwork/config/config_test.go new file mode 100644 index 0000000000000..93e3e99c5f6ad --- /dev/null +++ b/libnetwork/config/config_test.go @@ -0,0 +1,143 @@ +package config + +import ( + "os" + "strings" + "testing" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/netlabel" +) + +func TestInvalidConfig(t *testing.T) { + _, err := ParseConfig("invalid.toml") + if err == nil { + t.Fatal("Invalid Configuration file must fail") + } +} + +func TestConfig(t *testing.T) { + _, err := ParseConfig("libnetwork.toml") + if err != nil { + t.Fatal("Error parsing a valid configuration file :", err) + } +} + +func TestOptionsLabels(t *testing.T) { + c := &Config{} + l := []string{ + "com.docker.network.key1=value1", + "com.docker.storage.key1=value1", + "com.docker.network.driver.key1=value1", + "com.docker.network.driver.key2=value2", + } + f := OptionLabels(l) + f(c) + if len(c.Daemon.Labels) != 3 { + t.Fatalf("Expecting 3 labels, seen %d", len(c.Daemon.Labels)) + } + for _, l := range c.Daemon.Labels { + if !strings.HasPrefix(l, netlabel.Prefix) { + t.Fatalf("config must accept only libnetwork labels. Not : %s", l) + } + } +} + +func TestValidName(t *testing.T) { + if !IsValidName("test") { + t.Fatal("Name validation fails for a name that must be accepted") + } + if IsValidName("") { + t.Fatal("Name validation succeeds for a case when it is expected to fail") + } + if IsValidName(" ") { + t.Fatal("Name validation succeeds for a case when it is expected to fail") + } +} + +func TestTLSConfiguration(t *testing.T) { + cert := `-----BEGIN CERTIFICATE----- +MIIDCDCCAfKgAwIBAgIICifG7YeiQOEwCwYJKoZIhvcNAQELMBIxEDAOBgNVBAMT +B1Rlc3QgQ0EwHhcNMTUxMDAxMjMwMDAwWhcNMjAwOTI5MjMwMDAwWjASMRAwDgYD +VQQDEwdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wRC +O+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4+zE9h80aC4hz+6caRpds ++J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhRSoSi3nY+B7F2E8cuz14q +V2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZrpXUyXxAvzXfpFXo1RhSb +UywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUerVYrCPq8vqfn//01qz55 +Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHojxOpXTBepUCIJLbtNnWFT +V44t9gh5IqIWtoBReQIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAAYwEgYDVR0TAQH/ +BAgwBgEB/wIBAjAdBgNVHQ4EFgQUZKUI8IIjIww7X/6hvwggQK4bD24wHwYDVR0j +BBgwFoAUZKUI8IIjIww7X/6hvwggQK4bD24wCwYJKoZIhvcNAQELA4IBAQDES2cz +7sCQfDCxCIWH7X8kpi/JWExzUyQEJ0rBzN1m3/x8ySRxtXyGekimBqQwQdFqlwMI +xzAQKkh3ue8tNSzRbwqMSyH14N1KrSxYS9e9szJHfUasoTpQGPmDmGIoRJuq1h6M +ej5x1SCJ7GWCR6xEXKUIE9OftXm9TdFzWa7Ja3OHz/mXteii8VXDuZ5ACq6EE5bY +8sP4gcICfJ5fTrpTlk9FIqEWWQrCGa5wk95PGEj+GJpNogjXQ97wVoo/Y3p1brEn +t5zjN9PAq4H1fuCMdNNA+p1DHNwd+ELTxcMAnb2ajwHvV6lKPXutrTFc4umJToBX +FpTxDmJHEV4bzUzh +-----END CERTIFICATE----- +` + key := `-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA1wRCO+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4 ++zE9h80aC4hz+6caRpds+J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhR +SoSi3nY+B7F2E8cuz14qV2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZr +pXUyXxAvzXfpFXo1RhSbUywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUe +rVYrCPq8vqfn//01qz55Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHoj +xOpXTBepUCIJLbtNnWFTV44t9gh5IqIWtoBReQIDAQABAoIBAHSWipORGp/uKFXj +i/mut776x8ofsAxhnLBARQr93ID+i49W8H7EJGkOfaDjTICYC1dbpGrri61qk8sx +qX7p3v/5NzKwOIfEpirgwVIqSNYe/ncbxnhxkx6tXtUtFKmEx40JskvSpSYAhmmO +1XSx0E/PWaEN/nLgX/f1eWJIlxlQkk3QeqL+FGbCXI48DEtlJ9+MzMu4pAwZTpj5 +5qtXo5JJ0jRGfJVPAOznRsYqv864AhMdMIWguzk6EGnbaCWwPcfcn+h9a5LMdony +MDHfBS7bb5tkF3+AfnVY3IBMVx7YlsD9eAyajlgiKu4zLbwTRHjXgShy+4Oussz0 +ugNGnkECgYEA/hi+McrZC8C4gg6XqK8+9joD8tnyDZDz88BQB7CZqABUSwvjDqlP +L8hcwo/lzvjBNYGkqaFPUICGWKjeCtd8pPS2DCVXxDQX4aHF1vUur0uYNncJiV3N +XQz4Iemsa6wnKf6M67b5vMXICw7dw0HZCdIHD1hnhdtDz0uVpeevLZ8CgYEA2KCT +Y43lorjrbCgMqtlefkr3GJA9dey+hTzCiWEOOqn9RqGoEGUday0sKhiLofOgmN2B +LEukpKIey8s+Q/cb6lReajDVPDsMweX8i7hz3Wa4Ugp4Xa5BpHqu8qIAE2JUZ7bU +t88aQAYE58pUF+/Lq1QzAQdrjjzQBx6SrBxieecCgYEAvukoPZEC8mmiN1VvbTX+ +QFHmlZha3QaDxChB+QUe7bMRojEUL/fVnzkTOLuVFqSfxevaI/km9n0ac5KtAchV +xjp2bTnBb5EUQFqjopYktWA+xO07JRJtMfSEmjZPbbay1kKC7rdTfBm961EIHaRj +xZUf6M+rOE8964oGrdgdLlECgYEA046GQmx6fh7/82FtdZDRQp9tj3SWQUtSiQZc +qhO59Lq8mjUXz+MgBuJXxkiwXRpzlbaFB0Bca1fUoYw8o915SrDYf/Zu2OKGQ/qa +V81sgiVmDuEgycR7YOlbX6OsVUHrUlpwhY3hgfMe6UtkMvhBvHF/WhroBEIJm1pV +PXZ/CbMCgYEApNWVktFBjOaYfY6SNn4iSts1jgsQbbpglg3kT7PLKjCAhI6lNsbk +dyT7ut01PL6RaW4SeQWtrJIVQaM6vF3pprMKqlc5XihOGAmVqH7rQx9rtQB5TicL +BFrwkQE4HQtQBV60hYQUzzlSk44VFDz+jxIEtacRHaomDRh2FtOTz+I= +-----END RSA PRIVATE KEY----- +` + certFile, err := os.CreateTemp("", "cert") + if err != nil { + t.Fatalf("Failed to setup temp file: %s", err) + } + defer os.Remove(certFile.Name()) + certFile.Write([]byte(cert)) + certFile.Close() + keyFile, err := os.CreateTemp("", "key") + if err != nil { + t.Fatalf("Failed to setup temp file: %s", err) + } + defer os.Remove(keyFile.Name()) + keyFile.Write([]byte(key)) + keyFile.Close() + + c := &Config{Scopes: map[string]*datastore.ScopeCfg{}} + l := map[string]string{ + "kv.cacertfile": certFile.Name(), + "kv.certfile": certFile.Name(), + "kv.keyfile": keyFile.Name(), + } + f := OptionKVOpts(l) + f(c) + if _, ok := c.Scopes[datastore.GlobalScope]; !ok { + t.Fatal("GlobalScope not established") + } + + if c.Scopes[datastore.GlobalScope].Client.Config.TLS == nil { + t.Fatal("TLS is nil") + } + if c.Scopes[datastore.GlobalScope].Client.Config.TLS.RootCAs == nil { + t.Fatal("TLS.RootCAs is nil") + } + if len(c.Scopes[datastore.GlobalScope].Client.Config.TLS.Certificates) != 1 { + t.Fatal("TLS.Certificates is not length 1") + } +} diff --git a/libnetwork/config/libnetwork.toml b/libnetwork/config/libnetwork.toml new file mode 100644 index 0000000000000..93a2ff4756fcb --- /dev/null +++ b/libnetwork/config/libnetwork.toml @@ -0,0 +1,12 @@ +title = "LibNetwork Configuration file" + +[daemon] + debug = false +[cluster] + discovery = "token://swarm-discovery-token" + Address = "Cluster-wide reachable Host IP" +[datastore] + embedded = false +[datastore.client] + provider = "consul" + Address = "localhost:8500" diff --git a/vendor/github.com/docker/libnetwork/controller.go b/libnetwork/controller.go similarity index 92% rename from vendor/github.com/docker/libnetwork/controller.go rename to libnetwork/controller.go index 2896011dbf297..b5d8f6375fcef 100644 --- a/vendor/github.com/docker/libnetwork/controller.go +++ b/libnetwork/controller.go @@ -52,23 +52,24 @@ import ( "sync" "time" + "github.com/docker/docker/libnetwork/cluster" + "github.com/docker/docker/libnetwork/config" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/diagnostic" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/drvregistry" + "github.com/docker/docker/libnetwork/hostdiscovery" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/pkg/discovery" - "github.com/docker/docker/pkg/locker" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" "github.com/docker/docker/pkg/stringid" - "github.com/docker/libnetwork/cluster" - "github.com/docker/libnetwork/config" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/diagnostic" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/drvregistry" - "github.com/docker/libnetwork/hostdiscovery" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" + "github.com/moby/locker" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -155,28 +156,27 @@ type SandboxWalker func(sb Sandbox) bool type sandboxTable map[string]*sandbox type controller struct { - id string - drvRegistry *drvregistry.DrvRegistry - sandboxes sandboxTable - cfg *config.Config - stores []datastore.DataStore - discovery hostdiscovery.HostDiscovery - extKeyListener net.Listener - watchCh chan *endpoint - unWatchCh chan *endpoint - svcRecords map[string]svcInfo - nmap map[string]*netWatch - serviceBindings map[serviceKey]*service - defOsSbox osl.Sandbox - ingressSandbox *sandbox - sboxOnce sync.Once - agent *agent - networkLocker *locker.Locker - agentInitDone chan struct{} - agentStopDone chan struct{} - keys []*types.EncryptionKey - clusterConfigAvailable bool - DiagnosticServer *diagnostic.Server + id string + drvRegistry *drvregistry.DrvRegistry + sandboxes sandboxTable + cfg *config.Config + stores []datastore.DataStore + discovery hostdiscovery.HostDiscovery + extKeyListener net.Listener + watchCh chan *endpoint + unWatchCh chan *endpoint + svcRecords map[string]svcInfo + nmap map[string]*netWatch + serviceBindings map[serviceKey]*service + defOsSbox osl.Sandbox + ingressSandbox *sandbox + sboxOnce sync.Once + agent *agent + networkLocker *locker.Locker + agentInitDone chan struct{} + agentStopDone chan struct{} + keys []*types.EncryptionKey + DiagnosticServer *diagnostic.Server sync.Mutex } @@ -252,6 +252,7 @@ func New(cfgOptions ...config.Option) (NetworkController, error) { return nil, err } + setupArrangeUserFilterRule(c) return c, nil } @@ -276,10 +277,6 @@ func (c *controller) SetClusterProvider(provider cluster.Provider) { go c.clusterAgentInit() } -func isValidClusteringIP(addr string) bool { - return addr != "" && !net.ParseIP(addr).IsLoopback() && !net.ParseIP(addr).IsUnspecified() -} - // libnetwork side of agent depends on the keys. On the first receipt of // keys setup the agent. For subsequent key set handle the key change func (c *controller) SetKeys(keys []*types.EncryptionKey) error { @@ -339,7 +336,6 @@ func (c *controller) clusterAgentInit() { } } case cluster.EventNodeLeave: - keysAvailable = false c.agentOperationStart() c.Lock() c.keys = nil @@ -558,13 +554,6 @@ func (c *controller) BuiltinIPAMDrivers() []string { return drivers } -func (c *controller) validateHostDiscoveryConfig() bool { - if c.cfg == nil || c.cfg.Cluster.Discovery == "" || c.cfg.Cluster.Address == "" { - return false - } - return true -} - func (c *controller) clusterHostID() string { c.Lock() defer c.Unlock() @@ -575,21 +564,6 @@ func (c *controller) clusterHostID() string { return addr[0] } -func (c *controller) isNodeAlive(node string) bool { - if c.discovery == nil { - return false - } - - nodes := c.discovery.Fetch() - for _, n := range nodes { - if n.String() == node { - return true - } - } - - return false -} - func (c *controller) initDiscovery(watcher discovery.Watcher) error { if c.cfg == nil { return fmt.Errorf("discovery initialization requires a valid configuration") @@ -706,11 +680,18 @@ const overlayDSROptionString = "dsr" // NewNetwork creates a new network of the specified network type. The options // are network specific and modeled in a generic way. func (c *controller) NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) { + var ( + cap *driverapi.Capability + err error + t *network + skipCfgEpCount bool + ) + if id != "" { c.networkLocker.Lock(id) - defer c.networkLocker.Unlock(id) + defer c.networkLocker.Unlock(id) // nolint:errcheck - if _, err := c.NetworkByID(id); err == nil { + if _, err = c.NetworkByID(id); err == nil { return nil, NetworkNameError(id) } } @@ -739,15 +720,10 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ... } network.processOptions(options...) - if err := network.validateConfiguration(); err != nil { + if err = network.validateConfiguration(); err != nil { return nil, err } - var ( - cap *driverapi.Capability - err error - ) - // Reset network types, force local scope and skip allocation and // plumbing for configuration networks. Reset of the config-only // network drivers is needed so that this special network is not @@ -794,15 +770,16 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ... // From this point on, we need the network specific configuration, // which may come from a configuration-only network if network.configFrom != "" { - t, err := c.getConfigNetwork(network.configFrom) + t, err = c.getConfigNetwork(network.configFrom) if err != nil { return nil, types.NotFoundErrorf("configuration network %q does not exist", network.configFrom) } - if err := t.applyConfigurationTo(network); err != nil { + if err = t.applyConfigurationTo(network); err != nil { return nil, types.InternalErrorf("Failed to apply configuration: %v", err) } + network.generic[netlabel.Internal] = network.internal defer func() { - if err == nil { + if err == nil && !skipCfgEpCount { if err := t.getEpCnt().IncEndpointCnt(); err != nil { logrus.Warnf("Failed to update reference count for configuration network %q on creation of network %q: %v", t.Name(), network.Name(), err) @@ -823,7 +800,13 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ... err = c.addNetwork(network) if err != nil { - return nil, err + if _, ok := err.(types.MaskableError); ok { // nolint:gosimple + // This error can be ignored and set this boolean + // value to skip a refcount increment for configOnly networks + skipCfgEpCount = true + } else { + return nil, err + } } defer func() { if err != nil { @@ -901,8 +884,7 @@ addToStore: arrangeIngressFilterRule() c.Unlock() } - - c.arrangeUserFilterRule() + arrangeUserFilterRule() return network, nil } @@ -971,6 +953,10 @@ func (c *controller) reservePools() { continue } for _, ep := range epl { + if ep.Iface() == nil { + logrus.Warnf("endpoint interface is empty for %q (%s)", ep.Name(), ep.ID()) + continue + } if err := ep.assignAddress(ipam, true, ep.Iface().AddressIPv6() != nil); err != nil { logrus.Warnf("Failed to reserve current address for endpoint %q (%s) on network %q (%s)", ep.Name(), ep.ID(), n.Name(), n.ID()) @@ -1007,12 +993,7 @@ func (c *controller) addNetwork(n *network) error { func (c *controller) Networks() []Network { var list []Network - networks, err := c.getNetworksFromStore() - if err != nil { - logrus.Error(err) - } - - for _, n := range networks { + for _, n := range c.getNetworksFromStore() { if n.inDelete { continue } @@ -1298,7 +1279,7 @@ func (c *controller) loadIPAMDriver(name string) error { } if err != nil { - if err == plugins.ErrNotFound { + if errors.Cause(err) == plugins.ErrNotFound { return types.NotFoundErrorf(err.Error()) } return err @@ -1355,3 +1336,27 @@ func (c *controller) IsDiagnosticEnabled() bool { defer c.Unlock() return c.DiagnosticServer.IsDiagnosticEnabled() } + +func (c *controller) iptablesEnabled() bool { + c.Lock() + defer c.Unlock() + + if c.cfg == nil { + return false + } + // parse map cfg["bridge"]["generic"]["EnableIPTable"] + cfgBridge, ok := c.cfg.Daemon.DriverCfg["bridge"].(map[string]interface{}) + if !ok { + return false + } + cfgGeneric, ok := cfgBridge[netlabel.GenericData].(options.Generic) + if !ok { + return false + } + enabled, ok := cfgGeneric["EnableIPTables"].(bool) + if !ok { + // unless user explicitly stated, assume iptable is enabled + enabled = true + } + return enabled +} diff --git a/vendor/github.com/docker/libnetwork/datastore/cache.go b/libnetwork/datastore/cache.go similarity index 100% rename from vendor/github.com/docker/libnetwork/datastore/cache.go rename to libnetwork/datastore/cache.go diff --git a/vendor/github.com/docker/libnetwork/datastore/datastore.go b/libnetwork/datastore/datastore.go similarity index 99% rename from vendor/github.com/docker/libnetwork/datastore/datastore.go rename to libnetwork/datastore/datastore.go index e35dc43b2e59c..4e176724d0b0c 100644 --- a/vendor/github.com/docker/libnetwork/datastore/datastore.go +++ b/libnetwork/datastore/datastore.go @@ -8,10 +8,10 @@ import ( "sync" "time" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/types" "github.com/docker/libkv" "github.com/docker/libkv/store" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/types" ) //DataStore exported diff --git a/libnetwork/datastore/datastore_test.go b/libnetwork/datastore/datastore_test.go new file mode 100644 index 0000000000000..534940a12a704 --- /dev/null +++ b/libnetwork/datastore/datastore_test.go @@ -0,0 +1,256 @@ +package datastore + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/docker/docker/libnetwork/options" + "gotest.tools/v3/assert" +) + +var dummyKey = "dummy" + +// NewCustomDataStore can be used by other Tests in order to use custom datastore +func NewTestDataStore() DataStore { + return &datastore{scope: LocalScope, store: NewMockStore()} +} + +func TestKey(t *testing.T) { + eKey := []string{"hello", "world"} + sKey := Key(eKey...) + if sKey != "docker/network/v1.0/hello/world/" { + t.Fatalf("unexpected key : %s", sKey) + } +} + +func TestParseKey(t *testing.T) { + keySlice, err := ParseKey("/docker/network/v1.0/hello/world/") + if err != nil { + t.Fatal(err) + } + eKey := []string{"hello", "world"} + if len(keySlice) < 2 || !reflect.DeepEqual(eKey, keySlice) { + t.Fatalf("unexpected unkey : %s", keySlice) + } +} + +func TestInvalidDataStore(t *testing.T) { + config := &ScopeCfg{} + config.Client.Provider = "invalid" + config.Client.Address = "localhost:8500" + _, err := NewDataStore(GlobalScope, config) + if err == nil { + t.Fatal("Invalid Datastore connection configuration must result in a failure") + } +} + +func TestKVObjectFlatKey(t *testing.T) { + store := NewTestDataStore() + expected := dummyKVObject("1000", true) + err := store.PutObject(expected) + if err != nil { + t.Fatal(err) + } + keychain := []string{dummyKey, "1000"} + data, err := store.KVStore().Get(Key(keychain...)) + if err != nil { + t.Fatal(err) + } + var n dummyObject + json.Unmarshal(data.Value, &n) + if n.Name != expected.Name { + t.Fatal("Dummy object doesn't match the expected object") + } +} + +func TestAtomicKVObjectFlatKey(t *testing.T) { + store := NewTestDataStore() + expected := dummyKVObject("1111", true) + assert.Check(t, !expected.Exists()) + err := store.PutObjectAtomic(expected) + if err != nil { + t.Fatal(err) + } + assert.Check(t, expected.Exists()) + + // PutObjectAtomic automatically sets the Index again. Hence the following must pass. + + err = store.PutObjectAtomic(expected) + if err != nil { + t.Fatal("Atomic update should succeed.") + } + + // Get the latest index and try PutObjectAtomic again for the same Key + // This must succeed as well + data, err := store.KVStore().Get(Key(expected.Key()...)) + if err != nil { + t.Fatal(err) + } + n := dummyObject{} + json.Unmarshal(data.Value, &n) + n.ID = "1111" + n.SetIndex(data.LastIndex) + n.ReturnValue = true + err = store.PutObjectAtomic(&n) + if err != nil { + t.Fatal(err) + } + + // Get the Object using GetObject, then set again. + newObj := dummyObject{} + err = store.GetObject(Key(expected.Key()...), &newObj) + if err != nil { + t.Fatal(err) + } + assert.Check(t, newObj.Exists()) + err = store.PutObjectAtomic(&n) + if err != nil { + t.Fatal(err) + } + +} + +// dummy data used to test the datastore +type dummyObject struct { + Name string `kv:"leaf"` + NetworkType string `kv:"leaf"` + EnableIPv6 bool `kv:"leaf"` + Rec *recStruct `kv:"recursive"` + Dict map[string]*recStruct `kv:"iterative"` + Generic options.Generic `kv:"iterative"` + ID string + DBIndex uint64 + DBExists bool + SkipSave bool + ReturnValue bool +} + +func (n *dummyObject) Key() []string { + return []string{dummyKey, n.ID} +} + +func (n *dummyObject) KeyPrefix() []string { + return []string{dummyKey} +} + +func (n *dummyObject) Value() []byte { + if !n.ReturnValue { + return nil + } + + b, err := json.Marshal(n) + if err != nil { + return nil + } + return b +} + +func (n *dummyObject) SetValue(value []byte) error { + return json.Unmarshal(value, n) +} + +func (n *dummyObject) Index() uint64 { + return n.DBIndex +} + +func (n *dummyObject) SetIndex(index uint64) { + n.DBIndex = index + n.DBExists = true +} + +func (n *dummyObject) Exists() bool { + return n.DBExists +} + +func (n *dummyObject) Skip() bool { + return n.SkipSave +} + +func (n *dummyObject) DataScope() string { + return LocalScope +} + +func (n *dummyObject) MarshalJSON() ([]byte, error) { + netMap := make(map[string]interface{}) + netMap["name"] = n.Name + netMap["networkType"] = n.NetworkType + netMap["enableIPv6"] = n.EnableIPv6 + netMap["generic"] = n.Generic + return json.Marshal(netMap) +} + +func (n *dummyObject) UnmarshalJSON(b []byte) (err error) { + var netMap map[string]interface{} + if err := json.Unmarshal(b, &netMap); err != nil { + return err + } + n.Name = netMap["name"].(string) + n.NetworkType = netMap["networkType"].(string) + n.EnableIPv6 = netMap["enableIPv6"].(bool) + n.Generic = netMap["generic"].(map[string]interface{}) + return nil +} + +// dummy structure to test "recursive" cases +type recStruct struct { + Name string `kv:"leaf"` + Field1 int `kv:"leaf"` + Dict map[string]string `kv:"iterative"` + DBIndex uint64 + DBExists bool + SkipSave bool +} + +func (r *recStruct) Key() []string { + return []string{"recStruct"} +} +func (r *recStruct) Value() []byte { + b, err := json.Marshal(r) + if err != nil { + return nil + } + return b +} + +func (r *recStruct) SetValue(value []byte) error { + return json.Unmarshal(value, r) +} + +func (r *recStruct) Index() uint64 { + return r.DBIndex +} + +func (r *recStruct) SetIndex(index uint64) { + r.DBIndex = index + r.DBExists = true +} + +func (r *recStruct) Exists() bool { + return r.DBExists +} + +func (r *recStruct) Skip() bool { + return r.SkipSave +} + +func dummyKVObject(id string, retValue bool) *dummyObject { + cDict := make(map[string]string) + cDict["foo"] = "bar" + cDict["hello"] = "world" + n := dummyObject{ + Name: "testNw", + NetworkType: "bridge", + EnableIPv6: true, + Rec: &recStruct{"gen", 5, cDict, 0, false, false}, + ID: id, + DBIndex: 0, + ReturnValue: retValue, + DBExists: false, + SkipSave: false} + generic := make(map[string]interface{}) + generic["label1"] = &recStruct{"value1", 1, cDict, 0, false, false} + generic["label2"] = "subnet=10.1.1.0/16" + n.Generic = generic + return &n +} diff --git a/vendor/github.com/docker/libnetwork/datastore/mock_store.go b/libnetwork/datastore/mock_store.go similarity index 98% rename from vendor/github.com/docker/libnetwork/datastore/mock_store.go rename to libnetwork/datastore/mock_store.go index 215cc4fd01ea2..80e43db53db9b 100644 --- a/vendor/github.com/docker/libnetwork/datastore/mock_store.go +++ b/libnetwork/datastore/mock_store.go @@ -3,8 +3,8 @@ package datastore import ( "errors" + "github.com/docker/docker/libnetwork/types" "github.com/docker/libkv/store" - "github.com/docker/libnetwork/types" ) var ( @@ -125,5 +125,4 @@ func (s *MockStore) AtomicDelete(key string, previous *store.KVPair) (bool, erro // Close closes the client connection func (s *MockStore) Close() { - return } diff --git a/vendor/github.com/docker/libnetwork/default_gateway.go b/libnetwork/default_gateway.go similarity index 91% rename from vendor/github.com/docker/libnetwork/default_gateway.go rename to libnetwork/default_gateway.go index cf094b39eec80..44431a017a6b1 100644 --- a/vendor/github.com/docker/libnetwork/default_gateway.go +++ b/libnetwork/default_gateway.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -162,18 +162,6 @@ func (ep *endpoint) endpointInGWNetwork() bool { return false } -func (sb *sandbox) getEPwithoutGateway() *endpoint { - for _, ep := range sb.getConnectedEndpoints() { - if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" { - continue - } - if len(ep.Gateway()) == 0 { - return ep - } - } - return nil -} - // Looks for the default gw network and creates it if not there. // Parallel executions are serialized. func (c *controller) defaultGwNetwork() (Network, error) { @@ -181,10 +169,8 @@ func (c *controller) defaultGwNetwork() (Network, error) { defer func() { <-procGwNetwork }() n, err := c.NetworkByName(libnGWNetwork) - if err != nil { - if _, ok := err.(types.NotFoundError); ok { - n, err = c.createGWNetwork() - } + if _, ok := err.(types.NotFoundError); ok { + n, err = c.createGWNetwork() } return n, err } diff --git a/vendor/github.com/docker/libnetwork/default_gateway_freebsd.go b/libnetwork/default_gateway_freebsd.go similarity index 84% rename from vendor/github.com/docker/libnetwork/default_gateway_freebsd.go rename to libnetwork/default_gateway_freebsd.go index dc4b1bd592a48..164900d66ee6d 100644 --- a/vendor/github.com/docker/libnetwork/default_gateway_freebsd.go +++ b/libnetwork/default_gateway_freebsd.go @@ -1,6 +1,6 @@ package libnetwork -import "github.com/docker/libnetwork/types" +import "github.com/docker/docker/libnetwork/types" const libnGWNetwork = "docker_gwbridge" diff --git a/vendor/github.com/docker/libnetwork/default_gateway_linux.go b/libnetwork/default_gateway_linux.go similarity index 92% rename from vendor/github.com/docker/libnetwork/default_gateway_linux.go rename to libnetwork/default_gateway_linux.go index 60df85672260e..25bec287f9123 100644 --- a/vendor/github.com/docker/libnetwork/default_gateway_linux.go +++ b/libnetwork/default_gateway_linux.go @@ -4,7 +4,7 @@ import ( "fmt" "strconv" - "github.com/docker/libnetwork/drivers/bridge" + "github.com/docker/docker/libnetwork/drivers/bridge" ) const libnGWNetwork = "docker_gwbridge" diff --git a/libnetwork/default_gateway_windows.go b/libnetwork/default_gateway_windows.go new file mode 100644 index 0000000000000..9f59ef076bb03 --- /dev/null +++ b/libnetwork/default_gateway_windows.go @@ -0,0 +1,22 @@ +package libnetwork + +import ( + windriver "github.com/docker/docker/libnetwork/drivers/windows" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/types" +) + +const libnGWNetwork = "nat" + +func getPlatformOption() EndpointOption { + + epOption := options.Generic{ + windriver.DisableICC: true, + windriver.DisableDNS: true, + } + return EndpointOptionGeneric(epOption) +} + +func (c *controller) createGWNetwork() (Network, error) { + return nil, types.NotImplementedErrorf("default gateway functionality is not implemented in windows") +} diff --git a/libnetwork/diagnostic/server.go b/libnetwork/diagnostic/server.go new file mode 100644 index 0000000000000..08c3e875af9ca --- /dev/null +++ b/libnetwork/diagnostic/server.go @@ -0,0 +1,227 @@ +package diagnostic + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "sync" + "sync/atomic" + + "github.com/docker/docker/libnetwork/internal/caller" + "github.com/docker/docker/pkg/stack" + "github.com/sirupsen/logrus" +) + +// HTTPHandlerFunc TODO +type HTTPHandlerFunc func(interface{}, http.ResponseWriter, *http.Request) + +type httpHandlerCustom struct { + ctx interface{} + F func(interface{}, http.ResponseWriter, *http.Request) +} + +// ServeHTTP TODO +func (h httpHandlerCustom) ServeHTTP(w http.ResponseWriter, r *http.Request) { + h.F(h.ctx, w, r) +} + +var diagPaths2Func = map[string]HTTPHandlerFunc{ + "/": notImplemented, + "/help": help, + "/ready": ready, + "/stackdump": stackTrace, +} + +// Server when the debug is enabled exposes a +// This data structure is protected by the Agent mutex so does not require and additional mutex here +type Server struct { + enable int32 + srv *http.Server + port int + mux *http.ServeMux + registeredHanders map[string]bool + sync.Mutex +} + +// New creates a new diagnostic server +func New() *Server { + return &Server{ + registeredHanders: make(map[string]bool), + } +} + +// Init initialize the mux for the http handling and register the base hooks +func (s *Server) Init() { + s.mux = http.NewServeMux() + + // Register local handlers + s.RegisterHandler(s, diagPaths2Func) +} + +// RegisterHandler allows to register new handlers to the mux and to a specific path +func (s *Server) RegisterHandler(ctx interface{}, hdlrs map[string]HTTPHandlerFunc) { + s.Lock() + defer s.Unlock() + for path, fun := range hdlrs { + if _, ok := s.registeredHanders[path]; ok { + continue + } + s.mux.Handle(path, httpHandlerCustom{ctx, fun}) + s.registeredHanders[path] = true + } +} + +// ServeHTTP this is the method called bu the ListenAndServe, and is needed to allow us to +// use our custom mux +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.mux.ServeHTTP(w, r) +} + +// EnableDiagnostic opens a TCP socket to debug the passed network DB +func (s *Server) EnableDiagnostic(ip string, port int) { + s.Lock() + defer s.Unlock() + + s.port = port + + if s.enable == 1 { + logrus.Info("The server is already up and running") + return + } + + logrus.Infof("Starting the diagnostic server listening on %d for commands", port) + srv := &http.Server{Addr: fmt.Sprintf("%s:%d", ip, port), Handler: s} + s.srv = srv + s.enable = 1 + go func(n *Server) { + // Ignore ErrServerClosed that is returned on the Shutdown call + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + logrus.Errorf("ListenAndServe error: %s", err) + atomic.SwapInt32(&n.enable, 0) + } + }(s) +} + +// DisableDiagnostic stop the dubug and closes the tcp socket +func (s *Server) DisableDiagnostic() { + s.Lock() + defer s.Unlock() + + s.srv.Shutdown(context.Background()) // nolint:errcheck + s.srv = nil + s.enable = 0 + logrus.Info("Disabling the diagnostic server") +} + +// IsDiagnosticEnabled returns true when the debug is enabled +func (s *Server) IsDiagnosticEnabled() bool { + s.Lock() + defer s.Unlock() + return s.enable == 1 +} + +func notImplemented(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() // nolint:errcheck + _, json := ParseHTTPFormOptions(r) + rsp := WrongCommand("not implemented", fmt.Sprintf("URL path: %s no method implemented check /help\n", r.URL.Path)) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()}) + log.Info("command not implemented done") + + HTTPReply(w, rsp, json) // nolint:errcheck +} + +func help(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() // nolint:errcheck + _, json := ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()}) + log.Info("help done") + + n, ok := ctx.(*Server) + var result string + if ok { + for path := range n.registeredHanders { + result += fmt.Sprintf("%s\n", path) + } + HTTPReply(w, CommandSucceed(&StringCmd{Info: result}), json) // nolint:errcheck + } +} + +func ready(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() // nolint:errcheck + _, json := ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()}) + log.Info("ready done") + HTTPReply(w, CommandSucceed(&StringCmd{Info: "OK"}), json) // nolint:errcheck +} + +func stackTrace(ctx interface{}, w http.ResponseWriter, r *http.Request) { + r.ParseForm() // nolint:errcheck + _, json := ParseHTTPFormOptions(r) + + // audit logs + log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()}) + log.Info("stack trace") + + path, err := stack.DumpToFile("/tmp/") + if err != nil { + log.WithError(err).Error("failed to write goroutines dump") + HTTPReply(w, FailCommand(err), json) // nolint:errcheck + } else { + log.Info("stack trace done") + HTTPReply(w, CommandSucceed(&StringCmd{Info: fmt.Sprintf("goroutine stacks written to %s", path)}), json) // nolint:errcheck + } +} + +// DebugHTTPForm helper to print the form url parameters +func DebugHTTPForm(r *http.Request) { + for k, v := range r.Form { + logrus.Debugf("Form[%q] = %q\n", k, v) + } +} + +// JSONOutput contains details on JSON output printing +type JSONOutput struct { + enable bool + prettyPrint bool +} + +// ParseHTTPFormOptions easily parse the JSON printing options +func ParseHTTPFormOptions(r *http.Request) (bool, *JSONOutput) { + _, unsafe := r.Form["unsafe"] + v, json := r.Form["json"] + var pretty bool + if len(v) > 0 { + pretty = v[0] == "pretty" + } + return unsafe, &JSONOutput{enable: json, prettyPrint: pretty} +} + +// HTTPReply helper function that takes care of sending the message out +func HTTPReply(w http.ResponseWriter, r *HTTPResult, j *JSONOutput) (int, error) { + var response []byte + if j.enable { + w.Header().Set("Content-Type", "application/json") + var err error + if j.prettyPrint { + response, err = json.MarshalIndent(r, "", " ") + if err != nil { + response, _ = json.MarshalIndent(FailCommand(err), "", " ") + } + } else { + response, err = json.Marshal(r) + if err != nil { + response, _ = json.Marshal(FailCommand(err)) + } + } + } else { + response = []byte(r.String()) + } + return fmt.Fprint(w, string(response)) +} diff --git a/vendor/github.com/docker/libnetwork/diagnostic/types.go b/libnetwork/diagnostic/types.go similarity index 100% rename from vendor/github.com/docker/libnetwork/diagnostic/types.go rename to libnetwork/diagnostic/types.go diff --git a/vendor/github.com/docker/libnetwork/discoverapi/discoverapi.go b/libnetwork/discoverapi/discoverapi.go similarity index 100% rename from vendor/github.com/docker/libnetwork/discoverapi/discoverapi.go rename to libnetwork/discoverapi/discoverapi.go diff --git a/libnetwork/docs/bridge.md b/libnetwork/docs/bridge.md new file mode 100644 index 0000000000000..49e4e335a2bef --- /dev/null +++ b/libnetwork/docs/bridge.md @@ -0,0 +1,13 @@ +Bridge Driver +============= + +The bridge driver is an implementation that uses Linux Bridging and iptables to provide connectivity for containers +It creates a single bridge, called `docker0` by default, and attaches a `veth pair` between the bridge and every endpoint. + +## Configuration + +The bridge driver supports configuration through the Docker Daemon flags. + +## Usage + +This driver is supported for the default "bridge" network only and it cannot be used for any other networks. diff --git a/libnetwork/docs/cnm-model.jpg b/libnetwork/docs/cnm-model.jpg new file mode 100644 index 0000000000000..9bc45d0d9d845 Binary files /dev/null and b/libnetwork/docs/cnm-model.jpg differ diff --git a/libnetwork/docs/design.md b/libnetwork/docs/design.md new file mode 100644 index 0000000000000..4967290bfcccc --- /dev/null +++ b/libnetwork/docs/design.md @@ -0,0 +1,158 @@ +Design +====== + +This document describes how libnetwork has been designed in order to achieve this. +Requirements for individual releases can be found on the [Project Page](https://github.com/docker/libnetwork/wiki). + +Many of the design decisions are inspired by the learnings from the Docker networking design as of Docker v1.6. +Please refer to this [Docker v1.6 Design](legacy.md) document for more information on networking design as of Docker v1.6. + +## Goal + +libnetwork project will follow Docker and Linux philosophy of developing small, highly modular and composable tools that work well independently. +Libnetwork aims to satisfy that composable need for Networking in Containers. + +## The Container Network Model + +Libnetwork implements Container Network Model (CNM) which formalizes the steps required to provide networking for containers while providing an abstraction that can be used to support multiple network drivers. The CNM is built on 3 main components (shown below) + +![](/docs/cnm-model.jpg?raw=true) + +**Sandbox** + +A Sandbox contains the configuration of a container's network stack. +This includes management of the container's interfaces, routing table and DNS settings. +An implementation of a Sandbox could be a Linux Network Namespace, a FreeBSD Jail or other similar concept. +A Sandbox may contain *many* endpoints from *multiple* networks. + +**Endpoint** + +An Endpoint joins a Sandbox to a Network. +An implementation of an Endpoint could be a `veth` pair, an Open vSwitch internal port or similar. +An Endpoint can belong to only one network and it can belong to only one Sandbox, if connected. + +**Network** + +A Network is a group of Endpoints that are able to communicate with each-other directly. +An implementation of a Network could be a Linux bridge, a VLAN, etc. +Networks consist of *many* endpoints. + +## CNM Objects + +**NetworkController** +`NetworkController` object provides the entry-point into libnetwork that exposes simple APIs for the users (such as Docker Engine) to allocate and manage Networks. libnetwork supports multiple active drivers (both inbuilt and remote). `NetworkController` allows user to bind a particular driver to a given network. + +**Driver** +`Driver` is not a user visible object, but drivers provide the actual network implementation. `NetworkController` provides an API to configure a driver with driver-specific options/labels that is transparent to libnetwork, but can be handled by the drivers directly. Drivers can be both inbuilt (such as Bridge, Host, None & overlay) and remote (from plugin providers) to satisfy various use cases & deployment scenarios. At this point, the Driver owns a network and is responsible for managing the network (including IPAM, etc.). This can be improved in the future by having multiple drivers participating in handling various network management functionalities. + +**Network** +`Network` object is an implementation of the `CNM : Network` as defined above. `NetworkController` provides APIs to create and manage `Network` object. Whenever a `Network` is created or updated, the corresponding `Driver` will be notified of the event. LibNetwork treats `Network` objects at an abstract level to provide connectivity between a group of endpoints that belong to the same network and isolation from the rest. The `Driver` performs the actual work of providing the required connectivity and isolation. The connectivity can be within the same host or across multiple hosts. Hence `Network` has a global scope within a cluster. + +**Endpoint** +`Endpoint` represents a Service Endpoint. It provides the connectivity for services exposed by a container in a network with other services provided by other containers in the network. `Network` object provides APIs to create and manage an endpoint. An endpoint can be attached to only one network. `Endpoint` creation calls are made to the corresponding `Driver` which is responsible for allocating resources for the corresponding `Sandbox`. Since `Endpoint` represents a Service and not necessarily a particular container, `Endpoint` has a global scope within a cluster. + +**Sandbox** +`Sandbox` object represents container's network configuration such as IP address, MAC address, routes, DNS entries. A `Sandbox` object is created when the user requests to create an endpoint on a network. The `Driver` that handles the `Network` is responsible for allocating the required network resources (such as the IP address) and passing the info called `SandboxInfo` back to libnetwork. libnetwork will make use of OS specific constructs (example: netns for Linux) to populate the network configuration into the containers that is represented by the `Sandbox`. A `Sandbox` can have multiple endpoints attached to different networks. Since `Sandbox` is associated with a particular container in a given host, it has a local scope that represents the Host that the Container belong to. + +**CNM Attributes** + +***Options*** +`Options` provides a generic and flexible mechanism to pass `Driver` specific configuration options from the user to the `Driver` directly. `Options` are just key-value pairs of data with `key` represented by a string and `value` represented by a generic object (such as a Go `interface{}`). Libnetwork will operate on the `Options` ONLY if the `key` matches any of the well-known `Labels` defined in the `net-labels` package. `Options` also encompasses `Labels` as explained below. `Options` are generally NOT end-user visible (in UI), while `Labels` are. + +***Labels*** +`Labels` are very similar to `Options` and are in fact just a subset of `Options`. `Labels` are typically end-user visible and are represented in the UI explicitly using the `--labels` option. They are passed from the UI to the `Driver` so that `Driver` can make use of it and perform any `Driver` specific operation (such as a subnet to allocate IP-Addresses from in a Network). + +## CNM Lifecycle + +Consumers of the CNM, like Docker, interact through the CNM Objects and its APIs to network the containers that they manage. + +1. `Drivers` register with `NetworkController`. Built-in drivers register inside of libnetwork, while remote drivers register with libnetwork via the Plugin mechanism (*plugin-mechanism is WIP*). Each `driver` handles a particular `networkType`. + +2. `NetworkController` object is created using `libnetwork.New()` API to manage the allocation of Networks and optionally configure a `Driver` with driver specific `Options`. + +3. `Network` is created using the controller's `NewNetwork()` API by providing a `name` and `networkType`. `networkType` parameter helps to choose a corresponding `Driver` and binds the created `Network` to that `Driver`. From this point, any operation on `Network` will be handled by that `Driver`. + +4. `controller.NewNetwork()` API also takes in optional `options` parameter which carries Driver-specific options and `Labels`, which the Drivers can make use of for its purpose. + +5. `network.CreateEndpoint()` can be called to create a new Endpoint in a given network. This API also accepts optional `options` parameter which drivers can make use of. These 'options' carry both well-known labels and driver-specific labels. Drivers will in turn be called with `driver.CreateEndpoint` and it can choose to reserve IPv4/IPv6 addresses when an `Endpoint` is created in a `Network`. The `Driver` will assign these addresses using `InterfaceInfo` interface defined in the `driverapi`. The IP/IPv6 are needed to complete the endpoint as service definition along with the ports the endpoint exposes since essentially a service endpoint is nothing but a network address and the port number that the application container is listening on. + +6. `endpoint.Join()` can be used to attach a container to an `Endpoint`. The Join operation will create a `Sandbox` if it doesn't exist already for that container. The Drivers can make use of the Sandbox Key to identify multiple endpoints attached to a same container. This API also accepts optional `options` parameter which drivers can make use of. + * Though it is not a direct design issue of LibNetwork, it is highly encouraged to have users like `Docker` to call the endpoint.Join() during Container's `Start()` lifecycle that is invoked *before* the container is made operational. As part of Docker integration, this will be taken care of. + * One of a FAQ on endpoint join() API is that, why do we need an API to create an Endpoint and another to join the endpoint. + - The answer is based on the fact that Endpoint represents a Service which may or may not be backed by a Container. When an Endpoint is created, it will have its resources reserved so that any container can get attached to the endpoint later and get a consistent networking behaviour. + +7. `endpoint.Leave()` can be invoked when a container is stopped. The `Driver` can cleanup the states that it allocated during the `Join()` call. LibNetwork will delete the `Sandbox` when the last referencing endpoint leaves the network. But LibNetwork keeps hold of the IP addresses as long as the endpoint is still present and will be reused when the container(or any container) joins again. This ensures that the container's resources are reused when they are Stopped and Started again. + +8. `endpoint.Delete()` is used to delete an endpoint from a network. This results in deleting an endpoint and cleaning up the cached `sandbox.Info`. + +9. `network.Delete()` is used to delete a network. LibNetwork will not allow the delete to proceed if there are any existing endpoints attached to the Network. + + +## Implementation Details + +### Networks & Endpoints + +LibNetwork's Network and Endpoint APIs are primarily for managing the corresponding Objects and book-keeping them to provide a level of abstraction as required by the CNM. It delegates the actual implementation to the drivers which realize the functionality as promised in the CNM. For more information on these details, please see [the drivers section](#drivers) + +### Sandbox + +Libnetwork provides a framework to implement of a Sandbox in multiple operating systems. Currently we have implemented Sandbox for Linux using `namespace_linux.go` and `configure_linux.go` in `sandbox` package. +This creates a Network Namespace for each sandbox which is uniquely identified by a path on the host filesystem. +Netlink calls are used to move interfaces from the global namespace to the Sandbox namespace. +Netlink is also used to manage the routing table in the namespace. + +## Drivers + +## API + +Drivers are essentially an extension of libnetwork and provide the actual implementation for all of the LibNetwork APIs defined above. Hence there is an 1-1 correspondence for all the `Network` and `Endpoint` APIs, which includes : +* `driver.Config` +* `driver.CreateNetwork` +* `driver.DeleteNetwork` +* `driver.CreateEndpoint` +* `driver.DeleteEndpoint` +* `driver.Join` +* `driver.Leave` + +These Driver facing APIs make use of unique identifiers (`networkid`,`endpointid`,...) instead of names (as seen in user-facing APIs). + +The APIs are still work in progress and there can be changes to these based on the driver requirements especially when it comes to Multi-host networking. + +### Driver semantics + + * `Driver.CreateEndpoint` + +This method is passed an interface `EndpointInfo`, with methods `Interface` and `AddInterface`. + +If the value returned by `Interface` is non-nil, the driver is expected to make use of the interface information therein (e.g., treating the address or addresses as statically supplied), and must return an error if it cannot. If the value is `nil`, the driver should allocate exactly one _fresh_ interface, and use `AddInterface` to record them; or return an error if it cannot. + +It is forbidden to use `AddInterface` if `Interface` is non-nil. + +## Implementations + +Libnetwork includes the following driver packages: + +- null +- bridge +- overlay +- remote + +### Null + +The null driver is a `noop` implementation of the driver API, used only in cases where no networking is desired. This is to provide backward compatibility to the Docker's `--net=none` option. + +### Bridge + +The `bridge` driver provides a Linux-specific bridging implementation based on the Linux Bridge. +For more details, please [see the Bridge Driver documentation](bridge.md). + +### Overlay + +The `overlay` driver implements networking that can span multiple hosts using overlay network encapsulations such as VXLAN. +For more details on its design, please see the [Overlay Driver Design](overlay.md). + +### Remote + +The `remote` package does not provide a driver, but provides a means of supporting drivers over a remote transport. +This allows a driver to be written in a language of your choice. +For further details, please see the [Remote Driver Design](remote.md). diff --git a/libnetwork/docs/images/macvlan_bridge_simple.gliffy b/libnetwork/docs/images/macvlan_bridge_simple.gliffy new file mode 100644 index 0000000000000..9d77e1f6fc0f8 --- /dev/null +++ b/libnetwork/docs/images/macvlan_bridge_simple.gliffy @@ -0,0 +1 @@ +{"contentType":"application/gliffy+json","version":"1.3","stage":{"background":"#ffffff","width":328,"height":292,"nodeIndex":215,"autoFit":true,"exportBorder":false,"gridOn":true,"snapToGrid":false,"drawingGuidesOn":false,"pageBreaksOn":false,"printGridOn":false,"printPaper":"LETTER","printShrinkToFit":false,"printPortrait":true,"maxWidth":5000,"maxHeight":5000,"themeData":null,"viewportType":"default","fitBB":{"min":{"x":16,"y":21.51999694824218},"max":{"x":328,"y":291.5}},"printModel":{"pageSize":"a4","portrait":false,"fitToOnePage":false,"displayPageBreaks":false},"objects":[{"x":241.0,"y":36.0,"rotation":0.0,"id":199,"width":73.00000000000003,"height":40.150000000000006,"uid":"com.gliffy.shape.network.network_v4.business.router","order":42,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.network.network_v4.business.router","strokeWidth":1.0,"strokeColor":"#000000","fillColor":"#3966A0","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":85.0,"y":50.0,"rotation":0.0,"id":150,"width":211.0,"height":31.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":38,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":6.0,"strokeColor":"#999999","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[3.1159999999999997,6.359996948242184],[85.55799999999999,6.359996948242184],[85.55799999999999,62.0],[84.0,62.0]],"lockSegments":{"1":true},"ortho":true}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":22.803646598905374,"y":21.51999694824218,"rotation":0.0,"id":134,"width":64.31235340109463,"height":90.0,"uid":"com.gliffy.shape.cisco.cisco_v1.servers.standard_host","order":44,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.cisco.cisco_v1.servers.standard_host","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#3d85c6","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":89.0,"y":22.199996948242188,"rotation":0.0,"id":187,"width":105.0,"height":28.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":40,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

eth1 172.16.86.0/24

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":147.0,"y":50.0,"rotation":0.0,"id":196,"width":211.0,"height":31.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":41,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":199,"py":0.5,"px":0.0}}},"graphic":{"type":"Line","Line":{"strokeWidth":6.0,"strokeColor":"#999999","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-82.00001598011289,6.075000000000003],[94.0,6.075000000000003]],"lockSegments":{"1":true},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":210.0,"y":80.19999694824219,"rotation":0.0,"id":207,"width":120.00000000000001,"height":28.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":43,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Network Router

172.16.86.1/24

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":27.38636363636374,"y":108.14285409109937,"rotation":0.0,"id":129,"width":262.0,"height":124.0,"uid":"com.gliffy.shape.iphone.iphone_ios7.icons_glyphs.glyph_cloud","order":0,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.iphone.iphone_ios7.icons_glyphs.glyph_cloud","strokeWidth":1.0,"strokeColor":"#000000","fillColor":"#929292","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":33.0,"y":157.96785409109907,"rotation":0.0,"id":114,"width":150.0,"height":60.0,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":1,"lockAspectRatio":false,"lockShape":false,"children":[{"x":44.0,"y":2.9951060358893704,"rotation":0.0,"id":95,"width":62.0,"height":36.17618270799329,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":6,"lockAspectRatio":false,"lockShape":false,"children":[{"x":29.139999999999997,"y":3.2300163132136848,"rotation":0.0,"id":96,"width":3.719999999999998,"height":29.7161500815659,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":15,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":99,"py":0.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":99,"py":1.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[1.8599999999999994,-1.2920065252854727],[1.8599999999999994,31.0081566068514]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":51.46,"y":3.2300163132136848,"rotation":0.0,"id":97,"width":1.2156862745098034,"height":31.008156606851365,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":12,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-1.4193795664340882,-1.292006525285804],[-1.4193795664340882,31.008156606851536]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":9.919999999999993,"y":1.5073409461663854,"rotation":0.0,"id":98,"width":1.239999999999999,"height":31.008156606851365,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":9,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[2.0393795664339223,0.4306688417619762],[2.0393795664339223,32.73083197389853]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":1.9380097879282103,"rotation":0.0,"id":99,"width":62.0,"height":32.300163132136866,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":4,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#6fa8dc","fillColor":"#3d85c6","gradient":true,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":38.326264274062034,"rotation":0.0,"id":112,"width":150.0,"height":28.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":17,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

container1

172.16.86.2/24

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":124.0,"y":157.96785409109907,"rotation":0.0,"id":115,"width":150.0,"height":58.99999999999999,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":34,"lockAspectRatio":false,"lockShape":false,"children":[{"x":44.0,"y":2.94518760195788,"rotation":0.0,"id":116,"width":62.0,"height":35.573246329526725,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":22,"lockAspectRatio":false,"lockShape":false,"children":[{"x":29.139999999999997,"y":3.1761827079934557,"rotation":0.0,"id":117,"width":3.719999999999998,"height":29.220880913539798,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":31,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":120,"py":0.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":120,"py":1.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[1.8600000000000136,-1.2704730831974018],[1.8600000000000136,30.49135399673719]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":51.46,"y":3.1761827079934557,"rotation":0.0,"id":118,"width":1.2156862745098034,"height":30.49135399673717,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":28,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-1.4193795664340882,-1.2704730831977067],[-1.4193795664340882,30.491353996737335]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":9.919999999999993,"y":1.482218597063612,"rotation":0.0,"id":119,"width":1.239999999999999,"height":30.49135399673717,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":25,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[2.0393795664339223,0.42349102773260977],[2.0393795664339223,32.185318107666895]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":1.9057096247960732,"rotation":0.0,"id":120,"width":62.0,"height":31.76182707993458,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":20,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#6fa8dc","fillColor":"#3d85c6","gradient":true,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":37.45415986949433,"rotation":0.0,"id":121,"width":150.0,"height":28.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":33,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

container2

172.16.86.3/24

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":102.0,"y":130.1999969482422,"rotation":0.0,"id":130,"width":150.0,"height":14.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":35,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

pub_net (eth0)

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":93.0,"y":92.69999694824219,"rotation":0.0,"id":140,"width":150.0,"height":14.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":36,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"


","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":14.0,"y":114.19999694824219,"rotation":0.0,"id":142,"width":78.0,"height":14.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":37,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Docker Host

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":71.0,"y":235.5,"rotation":0.0,"id":184,"width":196.0,"height":56.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":39,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

docker network create -d macvlan \\

    --subnet=172.16.86.0/24 \\

    --gateway=172.16.86.1  \\

    -o parent=eth1 pub_net

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"layers":[{"guid":"9wom3rMkTrb3","order":0,"name":"Layer 0","active":true,"locked":false,"visible":true,"nodeIndex":45}],"shapeStyles":{},"lineStyles":{"global":{"stroke":"#999999","strokeWidth":6,"orthoMode":1}},"textStyles":{"global":{"bold":true,"face":"Arial","size":"12px","color":"#000000"}}},"metadata":{"title":"untitled","revision":0,"exportBorder":false,"loadPosition":"default","libraries":["com.gliffy.libraries.network.network_v4.home","com.gliffy.libraries.network.network_v4.business","com.gliffy.libraries.network.network_v4.rack","com.gliffy.libraries.network.network_v3.home","com.gliffy.libraries.network.network_v3.business","com.gliffy.libraries.network.network_v3.rack"],"lastSerialized":1457586216662,"analyticsProduct":"Confluence"},"embeddedResources":{"index":0,"resources":[]}} \ No newline at end of file diff --git a/libnetwork/docs/images/macvlan_bridge_simple.png b/libnetwork/docs/images/macvlan_bridge_simple.png new file mode 100644 index 0000000000000..51fa66e263793 Binary files /dev/null and b/libnetwork/docs/images/macvlan_bridge_simple.png differ diff --git a/libnetwork/docs/images/macvlan_bridge_simple.svg b/libnetwork/docs/images/macvlan_bridge_simple.svg new file mode 100644 index 0000000000000..bce931f0002ab --- /dev/null +++ b/libnetwork/docs/images/macvlan_bridge_simple.svg @@ -0,0 +1 @@ +container1172.16.86.2/24container2172.16.86.3/24pub_net (eth0)DockerHostdockernetworkcreate -dmacvlan \--subnet=172.16.86.0/24 \--gateway=172.16.86.1  \-oparent=eth1pub_neteth1172.16.86.0/24NetworkRouter172.16.86.1/24 \ No newline at end of file diff --git a/libnetwork/docs/images/multi_tenant_8021q_vlans.gliffy b/libnetwork/docs/images/multi_tenant_8021q_vlans.gliffy new file mode 100644 index 0000000000000..40eed17270d0d --- /dev/null +++ b/libnetwork/docs/images/multi_tenant_8021q_vlans.gliffy @@ -0,0 +1 @@ +{"contentType":"application/gliffy+json","version":"1.3","stage":{"background":"#ffffff","width":389,"height":213,"nodeIndex":276,"autoFit":true,"exportBorder":false,"gridOn":true,"snapToGrid":false,"drawingGuidesOn":false,"pageBreaksOn":false,"printGridOn":false,"printPaper":"LETTER","printShrinkToFit":false,"printPortrait":true,"maxWidth":5000,"maxHeight":5000,"themeData":null,"viewportType":"default","fitBB":{"min":{"x":5,"y":6.6999969482421875},"max":{"x":389,"y":212.14285409109937}},"printModel":{"pageSize":"a4","portrait":false,"fitToOnePage":false,"displayPageBreaks":false},"objects":[{"x":64.0,"y":36.0,"rotation":0.0,"id":216,"width":211.0,"height":31.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":10,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":5.0,"strokeColor":"#e69138","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[-12.0,33.0],[84.0,33.0],[84.0,86.0],[120.0,86.0]],"lockSegments":{"1":true},"ortho":true}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":190.0,"y":32.0,"rotation":0.0,"id":254,"width":211.0,"height":31.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":11,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":5.0,"strokeColor":"#f1c232","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[-142.0,16.0],[54.0,16.0],[54.0,115.0],[87.0,115.0]],"lockSegments":{"1":true},"ortho":true}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":133.38636363636374,"y":108.14285409109937,"rotation":0.0,"id":226,"width":123.00000000000001,"height":104.0,"uid":"com.gliffy.shape.iphone.iphone_ios7.icons_glyphs.glyph_cloud","order":12,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.iphone.iphone_ios7.icons_glyphs.glyph_cloud","strokeWidth":1.0,"strokeColor":"#000000","fillColor":"#999999","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":15.147567221510933,"y":139.96785409109907,"rotation":0.0,"id":115,"width":107.40845070422536,"height":49.0,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":29,"lockAspectRatio":false,"lockShape":false,"children":[{"x":31.506478873239438,"y":2.4460032626429853,"rotation":0.0,"id":116,"width":44.395492957746484,"height":29.54388254486117,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":17,"lockAspectRatio":false,"lockShape":false,"children":[{"x":20.86588169014084,"y":2.637846655791175,"rotation":0.0,"id":117,"width":2.663729577464789,"height":24.268189233278818,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":26,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":120,"py":0.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":120,"py":1.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[1.3318647887324033,-1.055138662316466],[1.3318647887324033,25.3233278955953]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":36.84825915492961,"y":2.637846655791175,"rotation":0.0,"id":118,"width":1.0000000000000002,"height":25.323327895595277,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":23,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-0.8875219090985048,-1.0551386623167391],[-0.8875219090985048,25.323327895595412]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":7.103278873239435,"y":1.230995106035881,"rotation":0.0,"id":119,"width":1.0000000000000002,"height":25.323327895595277,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":20,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[1.2752008616871728,0.3517128874389471],[1.2752008616871728,26.73017944535047]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":1.5827079934747048,"rotation":0.0,"id":120,"width":44.395492957746484,"height":26.378466557911768,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":15,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#6fa8dc","fillColor":"#3d85c6","gradient":true,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":37.199347471451986,"rotation":0.0,"id":121,"width":107.40845070422536,"height":28.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":28,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

container1 - vlan10

192.168.1.2/24

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":68.0,"y":82.69999694824219,"rotation":0.0,"id":140,"width":150.0,"height":14.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":30,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"


","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":71.0,"y":4.1999969482421875,"rotation":0.0,"id":187,"width":108.99999999999999,"height":19.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":31,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

eth0 - 802.1q trunk

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":282.0,"y":8.0,"rotation":0.0,"id":199,"width":73.00000000000003,"height":40.150000000000006,"uid":"com.gliffy.shape.network.network_v4.business.router","order":32,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.network.network_v4.business.router","strokeWidth":1.0,"strokeColor":"#000000","fillColor":"#3966A0","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":62.0,"y":55.0,"rotation":0.0,"id":210,"width":211.0,"height":31.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":34,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":5.0,"strokeColor":"#e06666","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[-8.0,11.0],[-8.0,34.0],[26.0,34.0],[26.0,57.0]],"lockSegments":{},"ortho":true}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":12.805718530101615,"y":11.940280333547719,"rotation":0.0,"id":134,"width":59.31028146989837,"height":83.0,"uid":"com.gliffy.shape.cisco.cisco_v1.servers.standard_host","order":35,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.cisco.cisco_v1.servers.standard_host","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#3d85c6","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":64.0,"y":73.19999694824219,"rotation":0.0,"id":211,"width":60.0,"height":14.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":36,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

eth0.10

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":65.0,"y":52.19999694824219,"rotation":0.0,"id":212,"width":60.0,"height":14.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":37,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

eth0.20

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":7.386363636363733,"y":108.14285409109937,"rotation":0.0,"id":219,"width":123.00000000000001,"height":104.0,"uid":"com.gliffy.shape.iphone.iphone_ios7.icons_glyphs.glyph_cloud","order":38,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.iphone.iphone_ios7.icons_glyphs.glyph_cloud","strokeWidth":1.0,"strokeColor":"#000000","fillColor":"#999999","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":139.1475672215109,"y":139.96785409109907,"rotation":0.0,"id":227,"width":107.40845070422536,"height":49.0,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":55,"lockAspectRatio":false,"lockShape":false,"children":[{"x":31.506478873239438,"y":2.4460032626429853,"rotation":0.0,"id":228,"width":44.395492957746484,"height":29.54388254486117,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":43,"lockAspectRatio":false,"lockShape":false,"children":[{"x":20.86588169014084,"y":2.637846655791175,"rotation":0.0,"id":229,"width":2.663729577464789,"height":24.268189233278818,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":52,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":232,"py":0.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":232,"py":1.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[1.3318647887323891,-1.055138662316466],[1.3318647887323891,25.3233278955953]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":36.84825915492961,"y":2.637846655791175,"rotation":0.0,"id":230,"width":1.0000000000000002,"height":25.323327895595277,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":49,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-0.8875219090985048,-1.0551386623167391],[-0.8875219090985048,25.323327895595412]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":7.103278873239435,"y":1.230995106035881,"rotation":0.0,"id":231,"width":1.0000000000000002,"height":25.323327895595277,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":46,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[1.2752008616871728,0.3517128874389471],[1.2752008616871728,26.73017944535047]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":1.5827079934747048,"rotation":0.0,"id":232,"width":44.395492957746484,"height":26.378466557911768,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":41,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#6fa8dc","fillColor":"#3d85c6","gradient":true,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":37.199347471451986,"rotation":0.0,"id":233,"width":107.40845070422536,"height":28.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":54,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

container2 - vlan20

172.16.1.2/24

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":259.38636363636374,"y":108.14285409109937,"rotation":0.0,"id":248,"width":123.00000000000001,"height":104.0,"uid":"com.gliffy.shape.iphone.iphone_ios7.icons_glyphs.glyph_cloud","order":56,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.iphone.iphone_ios7.icons_glyphs.glyph_cloud","strokeWidth":1.0,"strokeColor":"#000000","fillColor":"#999999","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":265.14756722151094,"y":139.96785409109907,"rotation":0.0,"id":241,"width":107.40845070422536,"height":49.0,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":73,"lockAspectRatio":false,"lockShape":false,"children":[{"x":31.506478873239438,"y":2.4460032626429853,"rotation":0.0,"id":242,"width":44.395492957746484,"height":29.54388254486117,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":61,"lockAspectRatio":false,"lockShape":false,"children":[{"x":20.86588169014084,"y":2.637846655791175,"rotation":0.0,"id":243,"width":2.663729577464789,"height":24.268189233278818,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":70,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":246,"py":0.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":246,"py":1.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[1.3318647887323891,-1.055138662316466],[1.3318647887323891,25.3233278955953]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":36.84825915492961,"y":2.637846655791175,"rotation":0.0,"id":244,"width":1.0000000000000002,"height":25.323327895595277,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":67,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-0.8875219090985048,-1.0551386623167391],[-0.8875219090985048,25.323327895595412]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":7.103278873239435,"y":1.230995106035881,"rotation":0.0,"id":245,"width":1.0000000000000002,"height":25.323327895595277,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":64,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#0b5394","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[1.2752008616871728,0.3517128874389471],[1.2752008616871728,26.73017944535047]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":1.5827079934747048,"rotation":0.0,"id":246,"width":44.395492957746484,"height":26.378466557911768,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":59,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#6fa8dc","fillColor":"#3d85c6","gradient":true,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":37.199347471451986,"rotation":0.0,"id":247,"width":107.40845070422536,"height":28.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":72,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

container3 - vlan30

10.1.1.2/16

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":65.0,"y":31.199996948242188,"rotation":0.0,"id":253,"width":60.0,"height":14.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":74,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

eth0.30

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":44.49612211422149,"y":17.874999999999943,"rotation":0.0,"id":266,"width":275.00609168449375,"height":15.70000000000006,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":75,"lockAspectRatio":false,"lockShape":false,"children":[{"x":68.50387788577851,"y":43.12500000000006,"rotation":0.0,"id":258,"width":211.0,"height":31.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":9,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#999999","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-64.00387788577851,-31.924999999999997],[197.00221379871527,-31.925000000000153]],"lockSegments":{"1":true},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":68.50387788577851,"y":38.55333333333314,"rotation":0.0,"id":262,"width":211.0,"height":33.06666666666631,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":7,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#999999","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-64.00387788577851,-34.053333333332965],[197.00221379871527,-34.05333333333314]],"lockSegments":{"1":true},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":70.50387788577851,"y":40.7533333333331,"rotation":0.0,"id":261,"width":211.0,"height":33.06666666666631,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":5,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#e06666","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-64.00387788577851,-34.053333333332965],[197.00221379871527,-34.05333333333314]],"lockSegments":{"1":true},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":70.50387788577851,"y":42.88666666666643,"rotation":0.0,"id":260,"width":211.0,"height":33.06666666666631,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":3,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#e69138","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-64.00387788577851,-34.053333333332965],[197.00221379871527,-34.05333333333314]],"lockSegments":{"1":true},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":73.50387788577851,"y":43.95333333333309,"rotation":0.0,"id":259,"width":211.0,"height":33.06666666666631,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":1,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":2.0,"strokeColor":"#ffe599","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-64.00387788577851,-34.053333333332965],[197.00221379871527,-34.05333333333314]],"lockSegments":{"1":true},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":248.0,"y":51.19999694824219,"rotation":0.0,"id":207,"width":143.0,"height":70.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":33,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Network Router (gateway)

vlan10 - 192.168.1.1/24

vlan20 - 172.16.1.1/24

vlan30 - 10.1.1.1/16

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":3.0,"y":88.19999694824219,"rotation":0.0,"id":272,"width":77.99999999999999,"height":28.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":76,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Docker Host

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"layers":[{"guid":"9wom3rMkTrb3","order":0,"name":"Layer 0","active":true,"locked":false,"visible":true,"nodeIndex":80}],"shapeStyles":{},"lineStyles":{"global":{"stroke":"#e06666","strokeWidth":2,"orthoMode":1}},"textStyles":{"global":{"bold":true,"face":"Arial","size":"12px","color":"#000000"}}},"metadata":{"title":"untitled","revision":0,"exportBorder":false,"loadPosition":"default","libraries":["com.gliffy.libraries.network.network_v4.home","com.gliffy.libraries.network.network_v4.business","com.gliffy.libraries.network.network_v4.rack","com.gliffy.libraries.network.network_v3.home","com.gliffy.libraries.network.network_v3.business","com.gliffy.libraries.network.network_v3.rack"],"lastSerialized":1457586821719,"analyticsProduct":"Confluence"},"embeddedResources":{"index":0,"resources":[]}} \ No newline at end of file diff --git a/libnetwork/docs/images/multi_tenant_8021q_vlans.png b/libnetwork/docs/images/multi_tenant_8021q_vlans.png new file mode 100644 index 0000000000000..a38633cdbc230 Binary files /dev/null and b/libnetwork/docs/images/multi_tenant_8021q_vlans.png differ diff --git a/libnetwork/docs/images/multi_tenant_8021q_vlans.svg b/libnetwork/docs/images/multi_tenant_8021q_vlans.svg new file mode 100644 index 0000000000000..5c6c6070a669c --- /dev/null +++ b/libnetwork/docs/images/multi_tenant_8021q_vlans.svg @@ -0,0 +1 @@ +container1 -vlan10192.168.1.2/24eth0 -802.1qtrunkNetworkRouter (gateway)vlan10 -192.168.1.1/24vlan20172.16.1.1/24vlan3010.1.1.1/16eth0.10eth0.20container2 -vlan20172.16.1.2/24container3 -vlan3010.1.1.2/16eth0.30DockerHost \ No newline at end of file diff --git a/libnetwork/docs/images/network_flow_bridge.png b/libnetwork/docs/images/network_flow_bridge.png new file mode 100644 index 0000000000000..4660f9fe34c1b Binary files /dev/null and b/libnetwork/docs/images/network_flow_bridge.png differ diff --git a/libnetwork/docs/images/network_flow_overlay.png b/libnetwork/docs/images/network_flow_overlay.png new file mode 100644 index 0000000000000..6606acb1c33a8 Binary files /dev/null and b/libnetwork/docs/images/network_flow_overlay.png differ diff --git a/libnetwork/docs/images/vlans-deeper-look.gliffy b/libnetwork/docs/images/vlans-deeper-look.gliffy new file mode 100644 index 0000000000000..4d9f2761c444a --- /dev/null +++ b/libnetwork/docs/images/vlans-deeper-look.gliffy @@ -0,0 +1 @@ +{"contentType":"application/gliffy+json","version":"1.3","stage":{"background":"#FFFFFF","width":566,"height":581,"nodeIndex":500,"autoFit":true,"exportBorder":false,"gridOn":true,"snapToGrid":false,"drawingGuidesOn":false,"pageBreaksOn":false,"printGridOn":false,"printPaper":"LETTER","printShrinkToFit":false,"printPortrait":true,"maxWidth":5000,"maxHeight":5000,"themeData":{"uid":"com.gliffy.theme.beach_day","name":"Beach Day","shape":{"primary":{"strokeWidth":2,"strokeColor":"#00A4DA","fillColor":"#AEE4F4","gradient":false,"dropShadow":false,"opacity":1,"text":{"color":"#004257"}},"secondary":{"strokeWidth":2,"strokeColor":"#CDB25E","fillColor":"#EACF81","gradient":false,"dropShadow":false,"opacity":1,"text":{"color":"#332D1A"}},"tertiary":{"strokeWidth":2,"strokeColor":"#FFBE00","fillColor":"#FFF1CB","gradient":false,"dropShadow":false,"opacity":1,"text":{"color":"#000000"}},"highlight":{"strokeWidth":2,"strokeColor":"#00A4DA","fillColor":"#00A4DA","gradient":false,"dropShadow":false,"opacity":1,"text":{"color":"#ffffff"}}},"line":{"strokeWidth":2,"strokeColor":"#00A4DA","fillColor":"none","arrowType":2,"interpolationType":"quadratic","cornerRadius":0,"text":{"color":"#002248"}},"text":{"color":"#002248"},"stage":{"color":"#FFFFFF"}},"viewportType":"default","fitBB":{"min":{"x":-3,"y":-1.0100878848684474},"max":{"x":566,"y":581}},"printModel":{"pageSize":"a4","portrait":false,"fitToOnePage":false,"displayPageBreaks":false},"objects":[{"x":-5.0,"y":-1.0100878848684474,"rotation":0.0,"id":499,"width":569.0,"height":582.0100878848684,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":103,"lockAspectRatio":false,"lockShape":false,"children":[{"x":374.0,"y":44.510087884868476,"rotation":0.0,"id":497,"width":145.0,"height":32.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":101,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Network & other

Docker Hosts

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":157.40277777777783,"y":108.18042331083174,"rotation":0.0,"id":492,"width":121.19444444444446,"height":256.03113588084784,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":99,"lockAspectRatio":false,"lockShape":false,"children":[{"x":-126.13675213675185,"y":31.971494223140525,"rotation":180.0,"id":453,"width":11.1452323717951,"height":61.19357171974171,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":57,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":6.0,"strokeColor":"#38761d","fillColor":"#38761d","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[-121.4915197649562,-156.36606993796556],[-121.49151976495622,-99.52846483047983],[-229.68596420939843,-99.52846483047591],[-229.68596420939843,-34.22088765589871]],"lockSegments":{"1":true},"ortho":true}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":289.82598824786317,"y":137.23816896148608,"rotation":180.0,"id":454,"width":11.1452323717951,"height":61.19357171974171,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":55,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":6.0,"strokeColor":"#38761d","fillColor":"#38761d","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[291.05455395299924,191.93174068122784],[291.05455395299924,106.06051735724502],[186.27677617521402,106.06051735724502],[186.27677617521402,69.78655839914467]],"lockSegments":{},"ortho":true}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":372.0,"y":332.0100878848684,"rotation":0.0,"id":490,"width":144.0,"height":60.0,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":97,"lockAspectRatio":false,"lockShape":false,"children":[{"x":0.0,"y":9.5,"rotation":0.0,"id":365,"width":141.0,"height":40.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":98,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

 Parent: eth0.30

VLAN: 30

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":0.0,"rotation":0.0,"id":342,"width":144.0,"height":60.0,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":96,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#eb6c6c","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.99,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":52.0,"y":332.0100878848684,"rotation":0.0,"id":489,"width":144.0,"height":60.0,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":92,"lockAspectRatio":false,"lockShape":false,"children":[{"x":1.0,"y":10.5,"rotation":0.0,"id":367,"width":138.0,"height":40.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":93,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Parent: eth0.10

VLAN ID: 10

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":0.0,"rotation":0.0,"id":340,"width":144.0,"height":60.0,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":91,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#5fcc5a","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.99,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":289.40277777777794,"y":126.43727235088903,"rotation":0.0,"id":486,"width":121.19444444444446,"height":250.0,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":88,"lockAspectRatio":false,"lockShape":false,"children":[{"x":236.18596420940128,"y":158.89044937932732,"rotation":0.0,"id":449,"width":11.1452323717951,"height":59.50782702798556,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":53,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":6.0,"strokeColor":"#cc0000","fillColor":"#cc0000","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[-121.49151976495682,-152.05853787273531],[-121.49151976495682,-81.64750068755309],[-229.68596420940125,-81.64750068755139],[-229.68596420940125,-33.27817949077674]],"lockSegments":{},"ortho":true}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":-179.77677617521388,"y":56.523633779319084,"rotation":0.0,"id":450,"width":11.1452323717951,"height":59.50782702798556,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":51,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":6.0,"strokeColor":"#cc0000","fillColor":"#cc0000","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":10.0,"controlPath":[[291.0545539529992,186.6444547140887],[291.0545539529992,117.79470574474337],[186.276776175214,117.79470574474337],[186.276776175214,67.8640963321146]],"lockSegments":{"1":true},"ortho":true}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":447.0,"y":150.01008788486848,"rotation":0.0,"id":472,"width":46.99999999999994,"height":27.0,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":87,"lockAspectRatio":false,"lockShape":false,"children":[{"x":0.0,"y":0.0,"rotation":0.0,"id":473,"width":37.09803921568625,"height":18.000000000000004,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":86,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#666666","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":5.485490196078445,"y":5.153846153846132,"rotation":0.0,"id":474,"width":37.09803921568625,"height":18.000000000000004,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":84,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#666666","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":9.901960784313701,"y":9.0,"rotation":0.0,"id":475,"width":37.09803921568625,"height":18.000000000000004,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":82,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#666666","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":368.0,"y":101.71008483311067,"rotation":0.0,"id":477,"width":140.0,"height":56.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":80,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Gateway 10.1.30.1

  and other containers on the same VLAN/subnet

 

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":350.51767083236393,"y":87.47159983339776,"rotation":0.0,"id":478,"width":175.20345848455912,"height":73.0,"uid":"com.gliffy.shape.cisco.cisco_v1.storage.cloud","order":79,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.cisco.cisco_v1.storage.cloud","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#cc0000","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":94.0,"y":155.01008788486848,"rotation":0.0,"id":463,"width":46.99999999999994,"height":27.0,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":78,"lockAspectRatio":false,"lockShape":false,"children":[{"x":0.0,"y":0.0,"rotation":0.0,"id":464,"width":37.09803921568625,"height":18.000000000000004,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":77,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#666666","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":5.485490196078445,"y":5.153846153846132,"rotation":0.0,"id":465,"width":37.09803921568625,"height":18.000000000000004,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":75,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#666666","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":9.901960784313701,"y":9.0,"rotation":0.0,"id":466,"width":37.09803921568625,"height":18.000000000000004,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":73,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#666666","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":80.0,"y":109.71008483311067,"rotation":0.0,"id":468,"width":140.0,"height":56.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":71,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Gateway 10.1.10.1

  and other containers on the same VLAN/subnet

 

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":62.51767083236396,"y":95.47159983339776,"rotation":0.0,"id":469,"width":175.20345848455912,"height":73.0,"uid":"com.gliffy.shape.cisco.cisco_v1.storage.cloud","order":70,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.cisco.cisco_v1.storage.cloud","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#38761d","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":341.0,"y":40.010087884868476,"rotation":0.0,"id":460,"width":46.99999999999994,"height":27.0,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":69,"lockAspectRatio":false,"lockShape":false,"children":[{"x":0.0,"y":0.0,"rotation":0.0,"id":417,"width":37.09803921568625,"height":18.000000000000004,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":68,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#666666","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":5.485490196078445,"y":5.153846153846132,"rotation":0.0,"id":418,"width":37.09803921568625,"height":18.000000000000004,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":66,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#666666","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":9.901960784313701,"y":9.0,"rotation":0.0,"id":419,"width":37.09803921568625,"height":18.000000000000004,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","order":64,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#666666","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":198.51767083236396,"y":41.471599833397754,"rotation":0.0,"id":459,"width":175.20345848455912,"height":79.73848499971291,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":62,"lockAspectRatio":false,"lockShape":false,"children":[{"x":17.482329167636067,"y":14.23848499971291,"rotation":0.0,"id":458,"width":140.0,"height":56.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":61,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Gateway 10.1.20.1

  and other containers on the same VLAN/subnet

 

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":0.0,"rotation":0.0,"id":330,"width":175.20345848455912,"height":73.0,"uid":"com.gliffy.shape.cisco.cisco_v1.storage.cloud","order":59,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.cisco.cisco_v1.storage.cloud","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#ff9900","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":279.0,"y":129.01008788486848,"rotation":0.0,"id":440,"width":5.0,"height":227.0,"uid":"com.gliffy.shape.basic.basic_v1.default.line","order":49,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":6.0,"strokeColor":"#ff9900","fillColor":"#ff9900","dashStyle":"1.0,1.0","startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[4.000000000000057,-25.08952732449731],[4.000000000000114,176.01117206537933]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":56.0,"y":503.0913886978766,"rotation":0.0,"id":386,"width":135.0,"height":20.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":48,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Frontend

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":62.0,"y":420.0100878848684,"rotation":0.0,"id":381,"width":120.0,"height":74.18803418803415,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":41,"lockAspectRatio":false,"lockShape":false,"children":[{"x":0.0,"y":0.0,"rotation":0.0,"id":382,"width":102.08955223880598,"height":54.91841491841488,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":44,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.97,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":2.0417910447761187,"y":0.0,"rotation":0.0,"id":383,"width":98.00597014925374,"height":44.0,"uid":null,"order":47,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":8,"paddingRight":8,"paddingBottom":8,"paddingLeft":8,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Container(s)

Eth0 10.1.10.0/24

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":8.955223880597016,"y":9.634809634809635,"rotation":0.0,"id":384,"width":102.08955223880598,"height":54.91841491841488,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":42,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.97,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":17.910447761194032,"y":19.26961926961927,"rotation":0.0,"id":385,"width":102.08955223880598,"height":54.91841491841488,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":40,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.97,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":382.0,"y":420.0100878848684,"rotation":0.0,"id":376,"width":120.0,"height":74.18803418803415,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":31,"lockAspectRatio":false,"lockShape":false,"children":[{"x":0.0,"y":0.0,"rotation":0.0,"id":377,"width":102.08955223880598,"height":54.91841491841488,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":34,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.97,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":2.0417910447761187,"y":0.0,"rotation":0.0,"id":378,"width":98.00597014925374,"height":44.0,"uid":null,"order":37,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":8,"paddingRight":8,"paddingBottom":8,"paddingLeft":8,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Container(s)

Eth0 10.1.30.0/24

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":8.955223880597016,"y":9.634809634809635,"rotation":0.0,"id":379,"width":102.08955223880598,"height":54.91841491841488,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":32,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.97,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":17.910447761194032,"y":19.26961926961927,"rotation":0.0,"id":380,"width":102.08955223880598,"height":54.91841491841488,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":30,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.97,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":214.0,"y":503.0100878848685,"rotation":0.0,"id":374,"width":135.0,"height":20.162601626016258,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":27,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Backend

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":376.0,"y":502.0100878848684,"rotation":0.0,"id":373,"width":135.0,"height":20.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":26,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Credit Cards

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":627.0,"y":99.94304076572786,"rotation":0.0,"id":364,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.uml.uml_v2.sequence.anchor_line","order":25,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":363,"py":0.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":342,"py":1.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":1.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-183.0,310.0670471191406],[-183.0,292.0670471191406]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":372.0,"y":410.0100878848684,"rotation":0.0,"id":363,"width":144.0,"height":117.0,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":24,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#eb6c6c","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.99,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":218.0,"y":341.5100878848684,"rotation":0.0,"id":366,"width":132.0,"height":40.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":23,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Parent: eth0.20

VLAN ID: 20

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":297.0,"y":89.94304076572786,"rotation":0.0,"id":356,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.uml.uml_v2.sequence.anchor_line","order":22,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":353,"py":0.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":343,"py":1.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":1.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[-13.0,320.0670471191406],[-13.0,302.0670471191406]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":222.0,"y":420.0100878848684,"rotation":0.0,"id":348,"width":120.0,"height":74.18803418803415,"uid":"com.gliffy.shape.basic.basic_v1.default.group","order":21,"lockAspectRatio":false,"lockShape":false,"children":[{"x":0.0,"y":0.0,"rotation":0.0,"id":349,"width":102.08955223880598,"height":54.91841491841488,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":17,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.97,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[{"x":2.0417910447761187,"y":0.0,"rotation":0.0,"id":350,"width":98.00597014925374,"height":44.0,"uid":null,"order":20,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":8,"paddingRight":8,"paddingBottom":8,"paddingLeft":8,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Container(s)

Eth0 10.1.20.0/24

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":8.955223880597016,"y":9.634809634809635,"rotation":0.0,"id":351,"width":102.08955223880598,"height":54.91841491841488,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":15,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.97,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":17.910447761194032,"y":19.26961926961927,"rotation":0.0,"id":352,"width":102.08955223880598,"height":54.91841491841488,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":13,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#4cacf5","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.97,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":212.0,"y":410.0100878848684,"rotation":0.0,"id":353,"width":144.0,"height":119.0,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":11,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#fca13f","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.99,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":212.0,"y":332.0100878848684,"rotation":0.0,"id":343,"width":144.0,"height":60.0,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":10,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#fca13f","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.99,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":203.0,"y":307.5100878848684,"rotation":0.0,"id":333,"width":160.0,"height":22.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":9,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

eth0 Interface

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":303.0,"y":240.51008788486845,"rotation":0.0,"id":323,"width":261.0,"height":48.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":8,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

802.1Q Trunk - can be a single Ethernet link or Multiple Bonded Ethernet links

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":36.0,"y":291.0100878848684,"rotation":0.0,"id":290,"width":497.0,"height":80.0,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":7,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#cccccc","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":1.0,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":0.0,"y":543.5100878848684,"rotation":0.0,"id":282,"width":569.0,"height":32.0,"uid":"com.gliffy.shape.basic.basic_v1.default.text","order":6,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Text","Text":{"overflow":"none","paddingTop":2,"paddingRight":2,"paddingBottom":2,"paddingLeft":2,"outerPaddingTop":6,"outerPaddingRight":6,"outerPaddingBottom":2,"outerPaddingLeft":6,"type":"fixed","lineTValue":null,"linePerpValue":null,"cardinalityType":null,"html":"

Docker Host: Frontend, Backend & Credit Card App Tiers are Isolated but can still communicate inside parent interface or any other Docker hosts using the VLAN ID

","tid":null,"valign":"middle","vposition":"none","hposition":"none"}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":-33.0,"y":79.94304076572786,"rotation":0.0,"id":269,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.uml.uml_v2.sequence.anchor_line","order":5,"lockAspectRatio":false,"lockShape":false,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":345,"py":0.0,"px":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":340,"py":1.0,"px":0.5}}},"graphic":{"type":"Line","Line":{"strokeWidth":1.0,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[157.0,330.0670471191406],[157.0,312.0670471191406]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":52.0,"y":410.0100878848684,"rotation":0.0,"id":345,"width":144.0,"height":119.0,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":4,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":1.0,"strokeColor":"#434343","fillColor":"#5fcc5a","gradient":false,"dashStyle":null,"dropShadow":true,"state":0,"opacity":0.99,"shadowX":4.0,"shadowY":4.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":20.0,"y":323.0100878848684,"rotation":0.0,"id":276,"width":531.0,"height":259.0,"uid":"com.gliffy.shape.basic.basic_v1.default.round_rectangle","order":3,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":2.0,"strokeColor":"#434343","fillColor":"#c5e4fc","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":0.93,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":19.609892022503004,"y":20.27621073737908,"rotation":355.62347411485274,"id":246,"width":540.0106597126834,"height":225.00000000000003,"uid":"com.gliffy.shape.cisco.cisco_v1.storage.cloud","order":2,"lockAspectRatio":true,"lockShape":false,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.cisco.cisco_v1.storage.cloud","strokeWidth":2.0,"strokeColor":"#333333","fillColor":"#999999","gradient":false,"dashStyle":null,"dropShadow":false,"state":0,"opacity":1.0,"shadowX":0.0,"shadowY":0.0}},"linkMap":[],"children":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":1.0,"y":99.94304076572786,"rotation":0.0,"id":394,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.uml.uml_v2.sequence.anchor_line","order":1,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":3.0,"strokeColor":"#666666","fillColor":"#999999","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[261.0,233.5670471191406],[261.0,108.05111187584177]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"},{"x":44.0,"y":90.94304076572786,"rotation":0.0,"id":481,"width":100.0,"height":100.0,"uid":"com.gliffy.shape.uml.uml_v2.sequence.anchor_line","order":0,"lockAspectRatio":false,"lockShape":false,"graphic":{"type":"Line","Line":{"strokeWidth":3.0,"strokeColor":"#666666","fillColor":"#999999","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","interpolationType":"linear","cornerRadius":null,"controlPath":[[261.0,233.56704711914062],[261.0,108.05111187584174]],"lockSegments":{},"ortho":false}},"linkMap":[],"hidden":false,"layerId":"9wom3rMkTrb3"}],"hidden":false,"layerId":"9wom3rMkTrb3"}],"layers":[{"guid":"9wom3rMkTrb3","order":0,"name":"Layer 0","active":true,"locked":false,"visible":true,"nodeIndex":104}],"shapeStyles":{},"lineStyles":{"global":{"fill":"#999999","stroke":"#38761d","strokeWidth":3,"dashStyle":"1.0,1.0","orthoMode":2}},"textStyles":{"global":{"bold":true,"face":"Arial","size":"14px","color":"#000000"}}},"metadata":{"title":"untitled","revision":0,"exportBorder":false,"loadPosition":"default","libraries":["com.gliffy.libraries.basic.basic_v1.default","com.gliffy.libraries.flowchart.flowchart_v1.default","com.gliffy.libraries.swimlanes.swimlanes_v1.default","com.gliffy.libraries.images","com.gliffy.libraries.network.network_v4.home","com.gliffy.libraries.network.network_v4.business","com.gliffy.libraries.network.network_v4.rack","com.gliffy.libraries.network.network_v3.home","com.gliffy.libraries.network.network_v3.business","com.gliffy.libraries.network.network_v3.rack"],"lastSerialized":1458117295143,"analyticsProduct":"Confluence"},"embeddedResources":{"index":0,"resources":[]}} \ No newline at end of file diff --git a/libnetwork/docs/images/vlans-deeper-look.png b/libnetwork/docs/images/vlans-deeper-look.png new file mode 100644 index 0000000000000..32d95f600e1d0 Binary files /dev/null and b/libnetwork/docs/images/vlans-deeper-look.png differ diff --git a/libnetwork/docs/images/vlans-deeper-look.svg b/libnetwork/docs/images/vlans-deeper-look.svg new file mode 100644 index 0000000000000..96cd21d52ff3c --- /dev/null +++ b/libnetwork/docs/images/vlans-deeper-look.svg @@ -0,0 +1 @@ +DockerHost:Frontend,Backend &CreditCardAppTiersareIsolatedbutcanstillcommunicateinsideinterfaceoranyotherDockerhostsusingtheparentVLANID802.1QTrunk -canbeasingleEthernetlinkorMultipleBondedEthernetlinksInterfaceeth0Container(s)Eth010.1.20.0/24Parent:eth0.20VLANID:20CreditCardsBackendContainer(s)Eth010.1.30.0/24Container(s)Eth010.1.10.0/24FrontendGateway10.1.20.1andothercontainersonthesameVLAN/subnetGateway10.1.10.1andothercontainersonthesameVLAN/subnetGateway10.1.30.1andothercontainersonthesameVLAN/subnet:Parenteth0.10VLANID:10Parent:eth0.30VLAN:30NetworkotherDockerHosts \ No newline at end of file diff --git a/libnetwork/docs/ipam.md b/libnetwork/docs/ipam.md new file mode 100644 index 0000000000000..ba36eaebe3282 --- /dev/null +++ b/libnetwork/docs/ipam.md @@ -0,0 +1,274 @@ +# IPAM Driver + +During the Network and Endpoints lifecycle, the CNM model controls the IP address assignment for network and endpoint interfaces via the IPAM driver(s). +Libnetwork has a default, built-in IPAM driver and allows third party IPAM drivers to be dynamically plugged. On network creation, the user can specify which IPAM driver libnetwork needs to use for the network's IP address management. This document explains the APIs with which the IPAM driver needs to comply, and the corresponding HTTPS request/response body relevant for remote drivers. + + +## Remote IPAM driver + +On the same line of remote network driver registration (see [remote.md](./remote.md) for more details), libnetwork initializes the `ipams.remote` package with the `Init()` function. It passes a `ipamapi.Callback` as a parameter, which implements `RegisterIpamDriver()`. The remote driver package uses this interface to register remote drivers with libnetwork's `NetworkController`, by supplying it in a `plugins.Handle` callback. The remote drivers register and communicate with libnetwork via the Docker plugin package. The `ipams.remote` provides the proxy for the remote driver processes. + + +## Protocol + +Communication protocol is the same as the remote network driver. + +## Handshake + +During driver registration, libnetwork will query the remote driver about the default local and global address spaces strings, and about the driver capabilities. +More detailed information can be found in the respective section in this document. + +## Datastore Requirements + +It is the remote driver's responsibility to manage its database. + +## Ipam Contract + +The remote IPAM driver must serve the following requests: + +- **GetDefaultAddressSpaces** + +- **RequestPool** + +- **ReleasePool** + +- **Request address** + +- **Release address** + + +The following sections explain each of the above requests' semantic, when they are called during network/endpoint lifecycle, and the corresponding payload for remote driver HTTP request/responses. + + +## IPAM Configuration and flow + +A libnetwork user can provide IPAM related configuration when creating a network, via the `NetworkOptionIpam` setter function. + +```go +func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf, opts map[string]string) NetworkOption +``` + +The caller has to provide the IPAM driver name and may provide the address space and a list of `IpamConf` structures for IPv4 and a list for IPv6. The IPAM driver name is the only mandatory field. If not provided, network creation will fail. + +In the list of configurations, each element has the following form: + +```go +// IpamConf contains all the ipam related configurations for a network +type IpamConf struct { + // The master address pool for containers and network interfaces + PreferredPool string + // A subset of the master pool. If specified, + // this becomes the container pool + SubPool string + // Input options for IPAM Driver (optional) + Options map[string]string + // Preferred Network Gateway address (optional) + Gateway string + // Auxiliary addresses for network driver. Must be within the master pool. + // libnetwork will reserve them if they fall into the container pool + AuxAddresses map[string]string +} +``` + +On network creation, libnetwork will iterate the list and perform the following requests to the IPAM driver: + +1. Request the address pool and pass the options along via `RequestPool()`. +2. Request the network gateway address if specified. Otherwise request any address from the pool to be used as network gateway. This is done via `RequestAddress()`. +3. Request each of the specified auxiliary addresses via `RequestAddress()`. + +If the list of IPv4 configurations is empty, libnetwork will automatically add one empty `IpamConf` structure. This will cause libnetwork to request IPAM driver an IPv4 address pool of the driver's choice on the configured address space, if specified, or on the IPAM driver default address space otherwise. If the IPAM driver is not able to provide an address pool, network creation will fail. +If the list of IPv6 configurations is empty, libnetwork will not take any action. +The data retrieved from the IPAM driver during the execution of point 1) to 3) will be stored in the network structure as a list of `IpamInfo` structures for IPv4 and a list for IPv6. + +On endpoint creation, libnetwork will iterate over the list of configs and perform the following operation: + +1. Request an IPv4 address from the IPv4 pool and assign it to the endpoint interface IPv4 address. If successful, stop iterating. +2. Request an IPv6 address from the IPv6 pool (if exists) and assign it to the endpoint interface IPv6 address. If successful, stop iterating. + +Endpoint creation will fail if any of the above operation does not succeed + +On endpoint deletion, libnetwork will perform the following operations: + +1. Release the endpoint interface IPv4 address +2. Release the endpoint interface IPv6 address if present + +On network deletion, libnetwork will iterate the list of `IpamData` structures and perform the following requests to ipam driver: + +1. Release the network gateway address via `ReleaseAddress()` +2. Release each of the auxiliary addresses via `ReleaseAddress()` +3. Release the pool via `ReleasePool()` + +### GetDefaultAddressSpaces + +GetDefaultAddressSpaces returns the default local and global address space names for this IPAM. An address space is a set of non-overlapping address pools isolated from other address spaces' pools. In other words, same pool can exist on N different address spaces. An address space naturally maps to a tenant name. +In libnetwork, the meaning associated to `local` or `global` address space is that a local address space doesn't need to get synchronized across the +cluster whereas the global address spaces does. Unless specified otherwise in the IPAM configuration, libnetwork will request address pools from the default local or default global address space based on the scope of the network being created. For example, if not specified otherwise in the configuration, libnetwork will request address pool from the default local address space for a bridge network, whereas from the default global address space for an overlay network. + +During registration, the remote driver will receive a POST message to the URL `/IpamDriver.GetDefaultAddressSpaces` with no payload. The driver's response should have the form: + + + { + "LocalDefaultAddressSpace": string + "GlobalDefaultAddressSpace": string + } + + + +### RequestPool + +This API is for registering an address pool with the IPAM driver. Multiple identical calls must return the same result. +It is the IPAM driver's responsibility to keep a reference count for the pool. + +```go +RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) +``` + + +For this API, the remote driver will receive a POST message to the URL `/IpamDriver.RequestPool` with the following payload: + + { + "AddressSpace": string + "Pool": string + "SubPool": string + "Options": map[string]string + "V6": bool + } + + +Where: + + * `AddressSpace` the IP address space. It denotes a set of non-overlapping pools. + * `Pool` The IPv4 or IPv6 address pool in CIDR format + * `SubPool` An optional subset of the address pool, an ip range in CIDR format + * `Options` A map of IPAM driver specific options + * `V6` Whether an IPAM self-chosen pool should be IPv6 + +AddressSpace is the only mandatory field. If no `Pool` is specified IPAM driver may choose to return a self chosen address pool. In such case, `V6` flag must be set if caller wants an IPAM-chosen IPv6 pool. A request with empty `Pool` and non-empty `SubPool` should be rejected as invalid. +If a `Pool` is not specified IPAM will allocate one of the default pools. When `Pool` is not specified, the `V6` flag should be set if the network needs IPv6 addresses to be allocated. + +A successful response is in the form: + + + { + "PoolID": string + "Pool": string + "Data": map[string]string + } + + +Where: + +* `PoolID` is an identifier for this pool. Same pools must have same pool id. +* `Pool` is the pool in CIDR format +* `Data` is the IPAM driver supplied metadata for this pool + + +### ReleasePool + +This API is for releasing a previously registered address pool. + +```go +ReleasePool(poolID string) error +``` + +For this API, the remote driver will receive a POST message to the URL `/IpamDriver.ReleasePool` with the following payload: + + { + "PoolID": string + } + +Where: + +* `PoolID` is the pool identifier + +A successful response is empty: + + {} + +### RequestAddress + +This API is for reserving an ip address. + +```go +RequestAddress(string, net.IP, map[string]string) (*net.IPNet, map[string]string, error) +``` + +For this API, the remote driver will receive a POST message to the URL `/IpamDriver.RequestAddress` with the following payload: + + { + "PoolID": string + "Address": string + "Options": map[string]string + } + +Where: + +* `PoolID` is the pool identifier +* `Address` is the required address in regular IP form (A.B.C.D). If this address cannot be satisfied, the request fails. If empty, the IPAM driver chooses any available address on the pool +* `Options` are IPAM driver specific options + + +A successful response is in the form: + + + { + "Address": string + "Data": map[string]string + } + + +Where: + +* `Address` is the allocated address in CIDR format (A.B.C.D/MM) +* `Data` is some IPAM driver specific metadata + +### ReleaseAddress + +This API is for releasing an IP address. + +For this API, the remote driver will receive a POST message to the URL `/IpamDriver.ReleaseAddress` with the following payload: + + { + "PoolID": string + "Address": string + } + +Where: + +* `PoolID` is the pool identifier +* `Address` is the IP address to release + + + +### GetCapabilities + +During the driver registration, libnetwork will query the driver about its capabilities. It is not mandatory for the driver to support this URL endpoint. If driver does not support it, registration will succeed with empty capabilities automatically added to the internal driver handle. + +During registration, the remote driver will receive a POST message to the URL `/IpamDriver.GetCapabilities` with no payload. The driver's response should have the form: + + + { + "RequiresMACAddress": bool + "RequiresRequestReplay": bool + } + + + +## Capabilities + +Capabilities are requirements, features the remote ipam driver can express during registration with libnetwork. +As of now libnetwork accepts the following capabilities: + +### RequiresMACAddress + +It is a boolean value which tells libnetwork whether the ipam driver needs to know the interface MAC address in order to properly process the `RequestAddress()` call. +If true, on `CreateEndpoint()` request, libnetwork will generate a random MAC address for the endpoint (if an explicit MAC address was not already provided by the user) and pass it to `RequestAddress()` when requesting the IP address inside the options map. The key will be the `netlabel.MacAddress` constant: `"com.docker.network.endpoint.macaddress"`. + +### RequiresRequestReplay + +It is a boolean value which tells libnetwork whether the ipam driver needs to receive the replay of the `RequestPool()` and `RequestAddress()` requests on daemon reload. When libnetwork controller is initializing, it retrieves from local store the list of current local scope networks and, if this capability flag is set, it allows the IPAM driver to reconstruct the database of pools by replaying the `RequestPool()` requests for each pool and the `RequestAddress()` for each network gateway owned by the local networks. This can be useful to ipam drivers which decide not to persist the pools allocated to local scope networks. + + +## Appendix + +A Go extension for the IPAM remote API is available at [docker/go-plugins-helpers/ipam](https://github.com/docker/go-plugins-helpers/tree/master/ipam) diff --git a/libnetwork/docs/legacy.md b/libnetwork/docs/legacy.md new file mode 100644 index 0000000000000..7a19dcdff9e57 --- /dev/null +++ b/libnetwork/docs/legacy.md @@ -0,0 +1,15 @@ + +This document provides a TLD&R version of https://docs.docker.com/v1.6/articles/networking/. +If more interested in detailed operational design, please refer to this link. + +## Docker Networking design as of Docker v1.6 + +Prior to libnetwork, Docker Networking was handled in both Docker Engine and libcontainer. +Docker Engine makes use of the Bridge Driver to provide single-host networking solution with the help of linux bridge and IPTables. +Docker Engine provides simple configurations such as `--link`, `--expose`,... to enable container connectivity within the same host by abstracting away networking configuration completely from the Containers. +For external connectivity, it relied upon NAT & Port-mapping + +Docker Engine was responsible for providing the configuration for the container's networking stack. + +Libcontainer would then use this information to create the necessary networking devices and move them in to a network namespace. +This namespace would then be used when the container is started. diff --git a/libnetwork/docs/macvlan.md b/libnetwork/docs/macvlan.md new file mode 100644 index 0000000000000..b6b02fd2fb4a2 --- /dev/null +++ b/libnetwork/docs/macvlan.md @@ -0,0 +1,427 @@ + +# Macvlan Driver + +### Overview + +The Macvlan driver provides operators the ability to integrate Docker networking in a simple and lightweight fashion into the underlying network. Macvlan is supported by the Linux kernel and is a well known Linux network type. The Macvlan built-in driver does not require any port mapping and supports VLAN trunking (Virtual Local Area Network). VLANs are a traditional method of network virtualization and layer 2 datapath isolation that is prevalent in some form or fashion in most data centers. + +The Linux implementation is considered lightweight because it eliminates the need for using a Linux bridge for isolating containers on the Docker host. The VLAN driver requires full access to the underlying host making it suitable for Enterprise data centers that have administrative access to the host. + +Instead of attaching container network interfaces to a Docker host Linux bridge for a network, the driver simply connects the container interface to the Docker Host Ethernet interface (or sub-interface). Each network is attached to a unique parent interface. Containers in a network share a common broadcast domain and intra-network connectivity is permitted. Two separate networks will each have a unique parent interface and that parent is what enforces datapath isolation between two networks. In order for inter-network communications to occur, an IP router, external to the Docker host, is required to route between the two networks by hair-pining into the physical network and then back to the Docker host. While hairpinning traffic can be less efficient then east/west traffic staying local to the host, there is often more complexity associated with disaggregating services to the host. It can be practical for some users to leverage existing network services, such firewalls and load balancers that already exist in a data center architecture. + +When using traditional Linux bridges there are two common techniques to get traffic out of a container and into the physical network and vice versa. The first method to connect containers to the underlying network is to use Iptable rules which perform a NAT translation from a bridge that represents the Docker network to the physical Ethernet connection such as `eth0`. The upside of Iptables using the Docker built-in bridge driver is that the NIC does not have to be in promiscuous mode. The second bridge driver method is to move a host's external Ethernet connection into the bridge. Moving the host Ethernet connection can at times be unforgiving. Common mistakes such as cutting oneself off from the host, or worse, creating bridging loops that can cripple a VLAN throughout a data center can open a network design up to potential risks as the infrastructure grows. + +Connecting containers without any NATing is where the VLAN drivers accel. Rather than having to manage a bridge for each Docker network containers are connected directly to a `parent` interface such as `eth0` that attaches the container to the same broadcast domain as the parent interface. A simple example is if a host's `eth0` is on the network `192.168.1.0/24` with a gateway of `192.168.1.1` then a Macvlan Docker network can start containers on the addresses `192.168.1.2 - 192.168.1.254`. Containers use the same network as the parent `-o parent` that is specified in the `docker network create` command. + +There are positive performance implication as a result of bypassing the Linux bridge, along with the simplicity of less moving parts, which is also attractive. Macvlan containers are easy to troubleshoot. The actual MAC and IP address of the container is bridged into the upstream network making a problematic application easy for operators to trace from the network. Existing underlay network management and monitoring tools remain relevant. + +### Pre-Requisites + +- The examples on this page are all single host and require Docker v1.12 or greater running on Linux. + +- Any examples using a sub-interface like `eth0.10` can be replaced with `eth0` or any other valid parent interface on the Docker host. Sub-interfaces with a `.` are dynamically created. The parent `-o parent` interface parameter can also be left out of the `docker network create` all together and the driver will create a `dummy` Linux type interface that will enable local host connectivity to perform the examples. + +- Kernel requirements: + + - To check your current kernel version, use `uname -r` to display your kernel version. + - Macvlan Linux kernel v3.9–3.19 and 4.0+. + +### MacVlan Bridge Mode Example Usage + +- Macvlan driver networks are attached to a parent Docker host interface. Examples are a physical interface such as `eth0`, a sub-interface for 802.1q VLAN tagging like `eth0.10` (`.10` representing VLAN `10`) or even bonded `bond0` host adapters which bundle two Ethernet interfaces into a single logical interface and provide diversity in the server connection. + +- The specified gateway is external to the host that is expected to be provided by the network infrastructure. If a gateway is not specified using the `--gateway` parameter, then Libnetwork will infer the first usable address of a subnet. For example, if a network's subnet is `--subnet 10.1.100.0/24` and no gateway is specified, Libnetwork will assign a gateway of `10.1.100.1` to the container. A second example would be a subnet of `--subnet 10.1.100.128/25` would receive a gateway of `10.1.100.129`. + +- Containers on separate networks cannot reach one another without an external process routing between the two networks/subnets. + +- Each Macvlan Bridge mode Docker network is isolated from one another and there can be only one network attached to a parent interface at a time. There is a theoretical limit of 4,094 sub-interfaces per host adapter that a Docker network could be attached to. + +- The driver limits one network per parent interface. The driver does however accommodate secondary subnets to be allocated in a single Docker network for a multi-subnet requirement. The upstream router is responsible for proxy-arping between the two subnets. + +- Any Macvlan container sharing the same subnet can communicate via IP to any other container in the same subnet without a gateway. It is important to note, that the parent will go into promiscuous mode when a container is attached to the parent since each container has a unique MAC address. Alternatively, Ipvlan which is currently an experimental driver uses the same MAC address as the parent interface and thus precluding the need for the parent being promiscuous. + +In the following example, `eth0` on the docker host has an IP on the `172.16.86.0/24` network and a default gateway of `172.16.86.1`. The gateway is an external router with an address of `172.16.86.1`. An IP address is not required on the Docker host interface `eth0` in `bridge` mode, it merely needs to be on the proper upstream network to get forwarded by a network switch or network router. + +![Simple Macvlan Bridge Mode Example](images/macvlan_bridge_simple.png) + +**Note** The Docker network subnet specified needs to match the network that parent interface of the Docker host for external communications. For example, use the same subnet and gateway of the Docker host ethernet interface specified by the `-o parent=` option. The parent interface is not required to have a IP address assigned to it, since this is simply L2 flooding and learning. + +- The parent interface used in this example is `eth0` and it is on the subnet `172.16.86.0/24`. The containers in the `docker network` will also need to be on this same subnet as the parent `-o parent=`. The gateway is an external router on the network. + +- Libnetwork driver types are specified with the `-d ` option. In this case `-d macvlan` + +- The parent interface `-o parent=eth0` is configured as followed: + +``` +ip addr show eth0 +3: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 + inet 172.16.86.250/24 brd 172.16.86.255 scope global eth0 +``` + +Create the macvlan network and run a couple of containers attached to it: + +``` +# Macvlan (-o macvlan_mode= Defaults to Bridge mode if not specified) +docker network create -d macvlan \ + --subnet=172.16.86.0/24 \ + --gateway=172.16.86.1 \ + -o parent=eth0 pub_net + +# Run a container on the new network specifying the --ip address. +docker run --net=pub_net --ip=172.16.86.10 -itd alpine /bin/sh + +# Start a second container and ping the first +docker run --net=pub_net -it --rm alpine /bin/sh +ping -c 4 172.16.86.10 + +``` + + Take a look at the containers ip and routing table: + +``` + +ip a show eth0 + eth0@if3: mtu 1500 qdisc noqueue state UNKNOWN + link/ether 46:b2:6b:26:2f:69 brd ff:ff:ff:ff:ff:ff + inet 172.16.86.2/24 scope global eth0 + +ip route + default via 172.16.86.1 dev eth0 + 172.16.86.0/24 dev eth0 src 172.16.86.2 + +# NOTE: the containers can NOT ping the underlying host interfaces as +# they are intentionally filtered by Linux for additional isolation. +# In this case the containers cannot ping the -o parent=172.16.86.250 +``` + + +Users can explicitly specify the `bridge` mode option `-o macvlan_mode=bridge` or leave the mode option out since the most common mode of `bridge` is the driver default. + +While the `eth0` interface does not need to have an IP address, it is not uncommon to have an IP address on the interface. Addresses can be excluded from getting an address from the default built in IPAM by using the `--aux-address=x.x.x.x` argument. This will blacklist the specified address from being handed out to containers from the built-in Libnetwork IPAM. + +- The following is the same network example as above, but blacklisting the `-o parent=eth0` address from being handed out to a container. + +``` +docker network create -d macvlan \ + --subnet=172.16.86.0/24 \ + --gateway=172.16.86.1 \ + --aux-address="exclude_host=172.16.86.250" \ + -o parent=eth0 pub_net +``` + +Another option for specifying what subpool or range of usable addresses is used by the default Docker IPAM driver is to use the argument `--ip-range=`. This instructs the driver to allocate container addresses from the specific range, rather then the broader range from the `--subnet=` argument. + +- The network create in the following example, allocates addresses beginning at `192.168.32.128` and increments n+1 upwards from there. + +``` +docker network create -d macvlan \ + --subnet=192.168.32.0/24 \ + --ip-range=192.168.32.128/25 \ + --gateway=192.168.32.254 \ + -o parent=eth0 macnet32 + +# Start a container and verify the address is 192.168.32.128 +docker run --net=macnet32 -it --rm alpine /bin/sh +``` + +The network can then be deleted with: + +``` +docker network rm +``` + +- **Note:** Linux Macvlan interface types are not able to ping or communicate with the default namespace IP address. For example, if you create a container and try to ping the Docker host's `eth0` it will **not** work. That traffic is explicitly filtered by the kernel to offer additional provider isolation and security. This is a common gotcha when a user first uses those Linux interface types since it is natural to ping local addresses when testing. + +For more on Docker networking commands see: [Working with Docker network commands](https://docs.docker.com/engine/userguide/networking/work-with-networks/) + +### Macvlan 802.1q Trunk Bridge Mode Example Usage + +VLANs have long been a primary means of virtualizing data center networks and are still in virtually all existing networks today. VLANs work by tagging a Layer-2 isolation domain with a 12-bit identifier ranging from 1-4094. The VLAN tag is inserted into a packet header that enables a logical grouping of a single subnet or multiple subnets of IPv4 and/or IPv6. It is very common for network operators to separate traffic using VLANs based on a subnet(s) function or security profile such as `web`, `db` or any other isolation requirements. + +It is very common to have a compute host requirement of running multiple virtual networks concurrently on a host. Linux networking has long supported VLAN tagging, also known by its standard 802.1Q, for maintaining datapath isolation between networks. The Ethernet link connected to a Docker host can be configured to support the 802.1q VLAN IDs by creating Linux sub-interfaces, each sub-interface being allocated a unique VLAN ID. + +![Simple Macvlan Mode Example](images/multi_tenant_8021q_vlans.png) + +Trunking 802.1q to a Linux host is notoriously painful for operations. It requires configuration file changes in order to be persistent through a reboot. If a bridge is involved, a physical NIC needs to be moved into the bridge and the bridge then gets the IP address. This has lead to many a stranded servers since the risk of cutting off access or misconfiguration is relatively high. + +Like all of the Docker network drivers, the overarching goal is to alleviate the operational pains of managing network resources. To that end, when a network receives a sub-interface as the parent that does not exist, the drivers create the VLAN tagged interfaces while creating the network. If the sub-interface already exists it is simply used as is. + +In the case of a host reboot, instead of needing to modify often complex network configuration files the driver will recreate all network links when the Docker daemon restarts. The driver tracks if it created the VLAN tagged sub-interface originally with the network create and will **only** recreate the sub-interface after a restart if it created the link in the first place. + +The same holds true if the network is deleted `docker network rm`. If driver created the sub-interface with `docker network create` it will remove the sub-interface link for the operator. + +If the user doesn't want Docker to create and delete the `-o parent` sub-interface, then you simply pass an interface that already exists as the parent link. Parent interfaces such as `eth0` are not deleted, only interfaces that are slave links. + +For the driver to add/delete the vlan sub-interfaces the format needs to be `-o parent interface_name.vlan_tag`. + +For example: `-o parent eth0.50` denotes a parent interface of `eth0` with a slave of `eth0.50` tagged with vlan id `50`. The equivalent `ip link` command would be `ip link add link eth0 name eth0.50 type vlan id 50`. + +Replace the `macvlan` with `ipvlan` in the `-d` driver argument to create macvlan 802.1q trunks. + +**Vlan ID 50** + +In the next example, the network is tagged and isolated by the Docker host. A parent of `eth0.50` will tag the Ethernet traffic with the vlan id `50` specified by the parent nomenclature `-o parent=eth0.50`. Other naming formats can be used, but the links need to be added and deleted manually using `ip link` or Linux configuration files. As long as the `-o parent` exists, anything can be used if compliant with Linux netlink. + +``` +# now add networks and hosts as you would normally by attaching to the master (sub)interface that is tagged +docker network create -d macvlan \ + --subnet=192.168.50.0/24 \ + --gateway=192.168.50.1 \ + -o parent=eth0.50 macvlan50 + +# In two separate terminals, start a Docker container and the containers can now ping one another. +docker run --net=macvlan50 -it --name macvlan_test5 --rm alpine /bin/sh +docker run --net=macvlan50 -it --name macvlan_test6 --rm alpine /bin/sh +``` + +**Vlan ID 60** + +In the second network, tagged and isolated by the Docker host, `eth0.60` is the parent interface tagged with vlan id `60` specified with `-o parent=eth0.60`. The `macvlan_mode=` defaults to `macvlan_mode=bridge`. It can also be explicitly set with the same result, as shown in the next example. + +``` +# now add networks and hosts as you would normally by attaching to the master (sub)interface that is tagged. +docker network create -d macvlan \ + --subnet=192.168.60.0/24 \ + --gateway=192.168.60.1 \ + -o parent=eth0.60 -o \ + -o macvlan_mode=bridge macvlan60 + +# In two separate terminals, start a Docker container and the containers can now ping one another. +docker run --net=macvlan60 -it --name macvlan_test7 --rm alpine /bin/sh +docker run --net=macvlan60 -it --name macvlan_test8 --rm alpine /bin/sh +``` + +**Example:** Multi-Subnet Macvlan 802.1q Trunking + +The same as the example before except there is an additional subnet bound to the network that the user can choose to provision containers on. In MacVlan/Bridge mode, containers can only ping one another if they are on the same subnet/broadcast domain unless there is an external router that routes the traffic (answers ARP etc) between the two subnets. Multiple subnets assigned to a network require a gateway external to the host that falls within the subnet range to hairpin the traffic back to the host. + + +``` +docker network create -d macvlan \ + --subnet=10.1.20.0/24 --subnet=10.1.10.0/24 \ + --gateway=10.1.20.1 --gateway=10.1.10.1 \ + -o parent=eth0.101 mcv101 + +# View Links after to network create `ip link` +$ ip link + +# Test 10.1.20.10.0/24 connectivity +docker run --net=mcv101 --ip=10.1.20.9 -itd alpine /bin/sh +docker run --net=mcv101 --ip=10.1.20.10 -it --rm alpine ping -c 4 10.1.20.10 + +# Test 10.1.10.10.0/24 connectivity +docker run --net=mcv101 --ip=10.1.10.10 -itd alpine /bin/sh +docker run --net=mcv101 --ip=10.1.10.9 -it --rm alpine ping -c 4 10.1.10.10 + +# Delete All Containers +docker rm -f `docker ps -qa` + +# Delete all Networks +docker network rm $(docker network ls -q) + +# Run ip links again and verify the links are cleaned up +ip link +``` + +Hosts on the same VLAN are typically on the same subnet and almost always are grouped together based on their security policy. In most scenarios, a multi-tier application is tiered into different subnets because the security profile of each process requires some form of isolation. For example, hosting your credit card processing on the same virtual network as the front-end web-server would be a regulatory compliance issue, along with circumventing the long standing best practice of layered defense in depth architectures. VLANs or the equivalent VNI (Virtual Network Identifier) when using the built-in Overlay driver, are the first step in isolating tenant traffic. + +![Docker VLANs in Depth](images/vlans-deeper-look.png) + + +### Dual Stack IPv4 IPv6 Macvlan Bridge Mode + +The following specifies both v4 and v6 addresses. An address from each family will be assigned to each container. You can specify either family type explicitly or allow the Libnetwork IPAM to assign them from the subnet pool. + +*Note on IPv6:* When declaring a v6 subnet with a `docker network create`, the flag `--ipv6` is required along with the subnet (in the following example `--subnet=2001:db8:abc8::/64`). Similar to IPv4 functionality, if a IPv6 `--gateway` is not specified, the first usable address in the v6 subnet is inferred and assigned as the gateway for the broadcast domain. + +The following example creates a network with multiple IPv4 and IPv6 subnets. The network is attached to a sub-interface of `eth0.218`. By specifying `eth0.218` as the parent, the driver will create the sub-interface (if it does not already exist) and tag all traffic for containers in the network with a VLAN ID of 218. The physical switch port on the ToR (top of rack) network port needs to have 802.1Q trunking enabled for communications in and out of the host to work. + +``` +# Create multiple subnets w/ dual stacks: +docker network create -d macvlan \ + --subnet=192.168.216.0/24 --subnet=192.168.218.0/24 \ + --gateway=192.168.216.1 --gateway=192.168.218.1 \ + --ipv6 --subnet=2001:db8:abc8::/64 --gateway=2001:db8:abc8::10 \ + -o parent=eth0.218 \ + -o macvlan_mode=bridge macvlan216 + +# Start a container on the first subnet 192.168.216.0/24 +docker run --net=macvlan216 --name=macnet216_test --ip=192.168.216.10 -itd alpine /bin/sh + +# Start a container on the second subnet 192.168.218.0/24 +docker run --net=macvlan216 --name=macnet218_test --ip=192.168.218.10 -itd alpine /bin/sh + +# Ping the first container started on the 192.168.216.0/24 subnet +docker run --net=macvlan216 --ip=192.168.216.11 -it --rm alpine /bin/sh + +# From inside the container shell ping the other host on the same subnet and then exit +$ ping -c4 192.168.216.10 +$ exit + +# Ping the first container started on the 192.168.218.0/24 subnet +docker run --net=macvlan216 --ip=192.168.218.11 -it --rm alpine /bin/sh + +# From inside the container shell ping the other host on the same subnet and then exit +$ ping -c4 192.168.218.10 +$ exit + +# Start a container in the back explicitly declaring the v6 address +docker run --net=macvlan216 --ip6=2001:db8:abc8::20 -itd alpine /bin/sh + +# Start another container pinging the v6 address of the previous container started in the background +docker run --net=macvlan216 -it --rm alpine /bin/sh +$ ping6 -c4 2001:db8:abc8::20 +$ exit +# Or, run the ping as a explicit process +docker run --net=macvlan216 -it --rm alpine ping6 -c4 2001:db8:abc8::20 +``` + +View the details of one of the containers: + +``` +docker run --net=macvlan216 --ip=192.168.216.11 -it --rm alpine /bin/sh + +root@526f3060d759:/# ip a show eth0 +25: eth0@if19: mtu 1500 qdisc noqueue state UNKNOWN + link/ether 02:42:c0:a8:d8:0b brd ff:ff:ff:ff:ff:ff + inet 192.168.216.11/24 scope global eth0 + valid_lft forever preferred_lft forever + inet6 2001:db8:abc8::1/64 scope link + valid_lft forever preferred_lft forever + +# The default gateway is a network gateway external to the Docker host +$ ip route +default via 192.168.216.1 dev eth0 +192.168.216.0/24 dev eth0 src 192.168.216.11 + +# Specified v6 gateway of 2001:db8:abc8::10 +$ ip -6 route + 2001:db8:abc4::/64 dev eth0 proto kernel metric 256 + 2001:db8:abc8::/64 dev eth0 proto kernel metric 256 + default via 2001:db8:abc8::10 dev eth0 metric 1024 + +#Containers can have both v4 and v6 addresses assigned to their interfaces or +# Both v4 and v6 addresses can be assigned to the container's interface +docker run --net=macvlan216 --ip=192.168.216.50 --ip6=2001:db8:abc8::50 -it --rm alpine /bin/sh + +# View the details of the dual stack eth0 interface from inside of the container +$ ip a show eth0 +95: eth0@if91: mtu 1500 qdisc noqueue state UNKNOWN + link/ether 02:42:c0:a8:d8:32 brd ff:ff:ff:ff:ff:ff + inet 192.168.216.50/24 scope global eth0 + valid_lft forever preferred_lft forever + inet6 2001:db8:abc8::50/64 scope global flags 02 + valid_lft forever preferred_lft forever +``` + +The next example demonstrates how default gateways are inferred if the `--gateway` option is not specified for a subnet in the `docker network create ...` command. If the gateway is not specified, the first usable address in the subnet is selected. It also demonstrates how `--ip-range` and `--aux-address` are used in conjunction to exclude address assignments within a network and reserve sub-pools of usable addresses within a network's subnet. All traffic is untagged since `eth0` is used rather then a sub-interface. + +``` +docker network create -d macvlan \ + --subnet=192.168.136.0/24 \ + --subnet=192.168.138.0/24 \ + --ipv6 --subnet=fd11::/64 \ + --ip-range=192.168.136.0/25 \ + --ip-range=192.168.138.0/25 \ + --aux-address="reserved1=fd11::2" \ + --aux-address="reserved2=192.168.136.2" \ + --aux-address="reserved3=192.168.138.2" \ + -o parent=eth0 mcv0 + +docker run --net=mcv0 -it --rm alpine /bin/sh +``` + +Next is the output from a running container provisioned on the example network named `mcv0`. + +``` +# Container eth0 output (the fe80::42:c0ff:fea8:8803/64 address is the local link addr) +ip address show eth0 +100: eth0@if2: mtu 1500 qdisc noqueue state UNKNOWN + link/ether 02:42:c0:a8:88:03 brd ff:ff:ff:ff:ff:ff + inet 192.168.136.3/24 scope global eth0 + valid_lft forever preferred_lft forever + inet6 fd11::3/64 scope global flags 02 + valid_lft forever preferred_lft forever + inet6 fe80::42:c0ff:fea8:8803/64 scope link + valid_lft forever preferred_lft forever + +# IPv4 routing table from within the container +$ ip route +default via 192.168.136.1 dev eth0 +192.168.136.0/24 dev eth0 src 192.168.136.3 + +# IPv6 routing table from within the container (the second v6 addresses is the local link addr) +$ ip -6 route +fd11::/64 dev eth0 metric 256 +fe80::/64 dev eth0 metric 256 +default via fd11::1 dev eth0 metric 1024 +``` + +- After the examples, `docker rm -f `docker ps -qa`` can be used to remove all existing containers on the host, both running and stopped. + +A key takeaway is, operators have the ability to map their physical network into their virtual network for integrating containers into their environment with no operational overhauls required. NetOps simply drops an 802.1q trunk into the Docker host. That virtual link would be the `-o parent=` passed in the network creation. For untagged (non-VLAN) links, it is as simple as `-o parent=eth0` or for 802.1q trunks with VLAN IDs each network gets mapped to the corresponding VLAN/Subnet from the network. + +An example being, NetOps provides VLAN ID and the associated subnets for VLANs being passed on the Ethernet link to the Docker host server. Those values are simply plugged into the `docker network create` commands when provisioning the Docker networks. These are persistent configurations that are applied every time the Docker engine starts which alleviates having to manage often complex configuration files. The network interfaces can also be managed manually by being pre-created and docker networking will never modify them, simply use them as parent interfaces. Example mappings from NetOps to Docker network commands are as follows: + +- VLAN: 10, Subnet: 172.16.80.0/24, Gateway: 172.16.80.1 + + - `--subnet=172.16.80.0/24 --gateway=172.16.80.1 -o parent=eth0.10` + +- VLAN: 20, IP subnet: 172.16.50.0/22, Gateway: 172.16.50.1 + + - `--subnet=172.16.50.0/22 --gateway=172.16.50.1 -o parent=eth0.20` + +- VLAN: 30, Subnet: 10.1.100.0/16, Gateway: 10.1.100.1 + + - `--subnet=10.1.100.0/16 --gateway=10.1.100.1 -o parent=eth0.30` + +### Manually Creating 802.1q Links + +If a user does not want the driver to create the vlan sub-interface it simply needs to exist prior to the `docker network create`. If you have sub-interface naming that is not `interface.vlan_id` it is honored in the `-o parent=` option again as long as the interface exists and us up. + +Links if manually created can be named anything you want. As long as the exist when the network is created that is all that matters. Manually created links do not get deleted regardless of the name when the network is deleted with `docker network rm`. + +``` +# create a new sub-interface tied to dot1q vlan 40 +ip link add link eth0 name eth0.40 type vlan id 40 + +# enable the new sub-interface +ip link set eth0.40 up + +# now add networks and hosts as you would normally by attaching to the master (sub)interface that is tagged +docker network create -d macvlan \ + --subnet=192.168.40.0/24 \ + --gateway=192.168.40.1 \ + -o parent=eth0.40 macvlan40 + +# in two separate terminals, start a Docker container and the containers can now ping one another. +docker run --net=macvlan40 -it --name mcv_test5 --rm alpine /bin/sh +docker run --net=macvlan40 -it --name mcv_test6 --rm alpine /bin/sh +``` + +**Example:** Vlan sub-interface manually created with any name: + +``` +# create a new sub interface tied to dot1q vlan 40 +ip link add link eth0 name foo type vlan id 40 + +# enable the new sub-interface +ip link set foo up + +# now add networks and hosts as you would normally by attaching to the master (sub)interface that is tagged +docker network create -d macvlan \ + --subnet=192.168.40.0/24 --gateway=192.168.40.1 \ + -o parent=foo macvlan40 + +# in two separate terminals, start a Docker container and the containers can now ping one another. +docker run --net=macvlan40 -it --name mcv_test5 --rm alpine /bin/sh +docker run --net=macvlan40 -it --name mcv_test6 --rm alpine /bin/sh +``` + +Manually created links can be cleaned up with: + +``` +ip link del foo +``` + +As with all of the Libnetwork drivers, networks of various driver types can be mixed and matched. This even applies to 3rd party ecosystem drivers that can be run in parallel with built-in drivers for maximum flexibility to the user. diff --git a/libnetwork/docs/network.md b/libnetwork/docs/network.md new file mode 100644 index 0000000000000..9f79d9c04592f --- /dev/null +++ b/libnetwork/docs/network.md @@ -0,0 +1,143 @@ + +# Docker Networking With Libnetwork + +This document describes docker networking in bridge and overlay mode delivered via libnetwork. Libnetwork uses iptables extensively to configure NATting and forwarding rules. [https://wiki.archlinux.org/index.php/iptables](https://wiki.archlinux.org/index.php/iptables) provides a good introduction to iptables and its default chains. More details may be found in [http://ipset.netfilter.org/iptables-extensions.man.html](http://ipset.netfilter.org/iptables-extensions.man.html) + + +# Bridge Mode + +![alt_text](images/network_flow_bridge.png "image_tooltip") + + +The above diagram illustrates the network topology when a container instantiated with network mode set to bridge by docker engine. In this case, the libnetwork does the following + + + +1. Creates a new network namespace container NS for this container +2. Creates a veth-pair, attaching one end to docker0 bridge on host NS, and move the other end to the new container NS. +3. In the new NS, assigns an IP address from docker0 subnet, sets default route gateway to docker0 IP address. + +This completes network setup for container running in bridge mode. Outbound traffic from container flows through routing (Container NS) ? veth-pair ? docker0 bridge (Host NS) ---> docker0 interface (HOST NS) ? routing (HostNS) ? eth0 (HostNS) and out of host. And inbound traffic to container flows through the reverse direction. + +Note that the container?s assigned IP (172.17.0.2 in above example) address is on docker0 subnet, and is not visible to externally to host. For this reason, a default masquerading rule is added to nat iptable?s POSTROUTING chain in host NS at docker engine initialization time. It states that for request traffic flow that has gone through the routing stage and the srcIP is within docker0 subnet (172.17.0.0/16), the traffic request must be originated from docker containers, therefore its srcIP is replaced with IP of outbound interface determined by routing. In the above diagram eth0?s IP 172.31.2.1 is used by replacement IP. In another word, masquerade is same as SNAT with replacement srcIP set to outbound interface?s IP. + +If the container backends a service and has a listening targetPort in the container NS, it also must also have a corresponding publishedPort in host NS to receive the request and forward it to the container. Two rules are created in host NS for this purpose: + + + +1. In nat iptable, a DOCKER(nat) chain is inserted to PREROUTING chain. And a rule such as ?DNAT tcp any any dport:45999 to conrts? is added to DOCKER chain, it does a DNAT for any traffic arriving at eth0 of host NS with dstIP=172.17.0.2 and dst Port=80, so that the DNATted request become routable to backend container listening on port 80. +2. In filter iptable, a DOCKER(filter) chain is inserted to FORWARD chain. And a rule such as ?ACCEPT tcp any containerIP dport:targetPort? is added. This allows request that is DNATted in 1) to be forwarded container. + + +# Swarm/Overlay Mode + +Libnetwork use completely different set of namespaces, bridges, and iptables to forward container traffic in swarm/overlay mode. + + +![alt_text](images/network_flow_overlay.png "image_tooltip") + + +As depicted in the above diagram, when a host joinis a swarm cluster, the docker engine creates following network topology. + +Initial Setup ( before any services are created) + + + +1. In host NS, creates a docker_gwbridge bridge, assigning a subnet range to this bridge. In this above diagram 172.18.0.1/16. This subnet is local, and does not leak outside of the host. +2. In host NS, adds masquerading rule in nat iptable (5) PREROUTING chain for any request with srcIP within 172.18.0.0/16 subnet. +3. Creates a new network namespace ingress_sbox NS, creates two veth-pairs, one eth1 connects to docker_gwbridge bridge with fixed IP 172.18.0.2, and other (eth0) connected to ingress NS bridge br0. The eth0 is assigned an IP address, in this example, 10.255.0.2. +4. In ingress_sbox NS, adds to nat iptable(2)?s PREROUTING chain a rule that snat and redirect service request to ipvs for load-balancing. For instance, + + ?SNAT all -- anywhere 10.255.0.0/16 ipvs to:10.255.0.2? + +5. In ingress_sbox NS, adds to nat iptable(2)?s POSTROUTING and OUTPUT chains rules that allows DNS lookup to be redirected to/from docker engine as is required by swarm service discovery. +6. Creates a new network namespace ingress NS, and creates a bridge br0 that has two links, one attaches to eth0 interface on ingress_sbox NS, and other to vxlan interface that in essence makes bridge br0 span across all hosts on the same swarm cluster. Each eth0 interface in ingress_sbox, each container instance of a service, and each service itself are given a unique IP 10.255.xx.xx, and is attached to br0, so that on the same swarm cluster, services, container instances of services, and ingress_sbox?s eth0 are all connected via bridge br0. + +When a service is created, say with targetPort=80 and publishedPort=30000. The following are added to the existing network topology. + +Service Setup + + + +1. A container backends the service has its own namespace container NS. And two vether-pairs are created, eth1 is attached to docker_gwbridge in host NS, is given an IP assigned from docker_gwbridge subnet, in the example, 172.18.0.2, eth0 is attached to br0 in ingress NS, is given an IP of 10.255.0.5 in this example. +2. In container NS, adds rules to filter iptable (3)?s INPUT and OUTPUT chain that only allows targetPortt =80 traffic. +3. In container NS, adds rule to nat iptables (4)?s PREROUTING chain that changes publishedPort to targetPort. For instance, + + ?REDIRECT tcp -- anywhere 10.255.0.11 tcp dpt:30000 redir ports 80? + +4. In container NS, adds rules to nat iptable(4)?s INPUT/OUTPUT chain that allow DNS lookup in this container to be redirected to docker engine. +5. In host NS, in filter iptable (5)?s FORWARD chain, inserts DOCKER-INGRESS chain, and adds a rule to allow service request to port 30000 and its reply. + + I.e ?ACCEPT tcp -- anywhere anywhere tcp dpt:30000? and + + + ?ACCEPT tcp -- anywhere anywhere state RELATED,ESTABLISHED tcp spt:30000? + +6. In host NS, in nat iptable(6)?s PREROUTING chain, inserts (different) DOCKER-INGRESS chain, and adds a rule to dnat service request to ingress NS?s eth1?s IP (172.18.0.2). i.e + + ?DNAT tcp -- anywhere anywhere tcp dpt:30000 to:172.18.0.2:30000? + +7. In ingress_sbox NS, in mangle iptable(1)?s PREROUTING chain, adds a rule to mark service request, i.e + + ?MARK tcp -- anywhere anywhere tcp dpt:30000 MARK set 0x100? + +8. In ingress_sbox NS, in nat iptable(2)?s POSTROUTING chain, adds a rule to snat request?s srcIP to eth1?s IP, and forward to ipvs to load-balancing, i.e + + ?SNAT all -- 0.0.0.0/0 10.255.0.0/16 ipvs to:10.255.0.2? + +9. In ingress_sbox, configures ipvs LB policy for marked traffic, i.e + + FWM 256 rr + + + -> 10.255.0.5:0 Masq 1 0 0 + + + -> 10.255.0.7:0 Masq 1 0 0 + + + -> 10.255.0.8:0 Masq 1 0 0 + + + Here each of 10.255.0.x represents IP address of of container instance backending the service. + + + +## Service Traffic Flow + +This section describes traffic flow of request and reply to/from a service with publishedPort = 30000, targetPort = 80 + + + +1. Request arrives at eth0 in host NS, with dstIP=172.31.2.1, dstPort=30000, srcIP=CLIENT_IP, srcPort=CLIENT_PORT. Before routing, It first goes through NAT rule in service setup (6) that dnats request with dstIP=172.18.0.2; It then go through FORWARD rule in service setup (5) during routing that allows request with dstPort=30000 to go through. The routing then forward request to docker_gwbridge, and in turn ... +2. The request arrives at eth1 in ingress_sbox NS with dstIP=172.18.0.2, dstPort=30000, srcIP=CLIENT_IP, srcPort=CLIENT_PORT. Before routing, the request is marked before by mangle iptable rule in service setup (7). After routing, it is snated with eth1?s IP 10.255.0.2, forwarded to ipvs for LB by nat iptable rule in service setup (8). The ipvs policy in setup (9) picks one container instance of the service, in this example 10.255.0.5 and dnats it. +3. The request arrives at br0 in ingress NS with dstIP=10.255.0.5, dstPort=30000, srcIP=10.255.0.2, srcPort=EPHEMERAL_PORT. For simplicity, we assume the container instance 10.255.0,5 is the local host, therefore simply forwards it. Note since br0 spans across all hosts in the cluster via vxlan, with all services instances latching onto it, so whether to pick remote or local container instance, it does not change the routing policy configuration. +4. The request arrives at eth0 of container NS with dstIP=10.255.0.5, dstPort=30000, srcIP=10.255.0.x (eth1 IP in ingress_sbox NS), srcPort=EMPHEMERAL_PORT. Before routing, it?s dstPort is changed to 80 via nat rule in service setup (3), and is allowed to be forwarded to local process by INPUT rule in service setup (2) post routig. The process listening on tcp:80 receives request with dstIP=10.255.0.5, dstPort=80, srcIP=10.255.0.2, , srcPort=EPHEMERAL_PORT. +5. The process replies, The reply has dstIP=10.255.0.2, dstPort=EPHEMERAL_PORT, srcIp=not_known, srcPort=80. It goes through filter rule in OUTPUT chain in service setup(2), which allows it to pass. It goes through routing that determines outbound interface is eth1, and srcIP=10.255.0.5; and it ?un-dnats? srcPort=80 to 30000 via nat table rule in service setup (3). +6. The reply arrives at br0 in ingress NS with dstIP=10.255.0.2, dstPort=EPHEMERAL_PORT, srcIP=10.255.0.5, srcPort=30000, which duly forwarded it to ... +7. The eh0 interface in sb_ingress NS. The reply first go through ipvs LB that ?un-dnats? srcIP from 10.255.0.5 to 172.18.0.2; then ?un-snats? via nat rule in service setup (8) dstIP from 10.255.0.2 to CLIENT_IP, dstPort from EMPHERAL_PORT to CLIENT_PORT. +8. The reply arrives at docker_gwbridge0 interface of host NS with dstIP=CLIENT_IP, dstPort=CLIENT_PORT, srcIP=172.18.0.2, srcPort=30000. The reply ?un-snats? with nat rule in service setup(6) with srcIP changes to 172.31.2.1. And is then forwarded out of eth0 interface, and complete the traffic flow. From external view, request enters host with dstIP=172.31.2.1, dstPort=30000, srcIP=CLIENT_IP, srcPort=CLIENT_PORT; and reply exits with dstIP=CLIENT_IP, dstPort=CLIENT_PORT, srcIP=172.31.2.1, srcPort=30000. + + +## Other Flows + +**Northbound traffic originated from a container instance, for example, ping [www.cnn.com](www.cnn.com):** + +The traffic flow is exactly the same as in bridge mode, except it is via docker_gwbridge in host NS, and traffic is masqueraded with nat rule in initial setup (2). + +**DNS traffic** + +DNS lookup traffic is routed to docker engine from container instance for service discovery, filling the blank. + + +# Other IPTable Chain and Rules + +Other iptable chains and rules created and/or managed by docker engine/libnetwork. + +**DOCKER-USER**: inserted as the first rule to FORWARD chain of filter iptable in host NS. So that user can independently managed traffic that may or may not be related docker containers. + +**DOCKER-ISOLATION-STAGE-1** / 2: Filling in the blank + + + + diff --git a/libnetwork/docs/networkdb.md b/libnetwork/docs/networkdb.md new file mode 100644 index 0000000000000..dbd17e4df7d25 --- /dev/null +++ b/libnetwork/docs/networkdb.md @@ -0,0 +1,66 @@ +NetworkDB +========= + +There are two databases used in libnetwork: + +- A persistent database that stores the network configuration requested by the user. This is typically the SwarmKit managers' raft store. +- A non-persistent peer-to-peer gossip-based database that keeps track of the current runtime state. This is NetworkDB. + +NetworkDB is based on the [SWIM][] protocol, which is implemented by the [memberlist][] library. +`memberlist` manages cluster membership (nodes can join and leave), as well as message encryption. +Members of the cluster send each other ping messages from time to time, allowing the cluster to detect when a node has become unavailable. + +The information held by each node in NetworkDB is: + +- The set of nodes currently in the cluster (plus nodes that have recently left or failed). +- For each peer node, the set of networks to which that node is connected. +- For each of the node's currently-in-use networks, a set of named tables of key/value pairs. + Note that nodes only keep track of tables for networks to which they belong. + +Updates spread through the cluster from node to node, and nodes may have inconsistent views at any given time. +They will eventually converge (quickly, if the network is operating well). +Nodes look up information using their local networkdb instance. Queries are not sent to remote nodes. + +NetworkDB does not impose any structure on the tables; they are just maps from `string` keys to `[]byte` values. +Other components in libnetwork use the tables for their own purposes. +For example, there are tables for service discovery and load balancing, +and the [overlay](overlay.md) driver uses NetworkDB to store routing information. +Updates to a network's tables are only shared between nodes that are on that network. + +All libnetwork nodes join the gossip cluster. +To do this, they need the IP address and port of at least one other member of the cluster. +In the case of a SwarmKit cluster, for example, each Docker engine will use the IP addresses of the swarm managers as the initial join addresses. +The `Join` method can be used to update these bootstrap IPs if they change while the system is running. + +When joining the cluster, the new node will initially synchronise its cluster-wide state (known nodes and networks, but not tables) with at least one other node. +The state will be mostly kept up-to-date by small UDP gossip messages, but each node will also periodically perform a push-pull TCP sync with another random node. +In a push-pull sync, the initiator sends all of its cluster-wide state to the target, and the target then sends all of its own state back in response. + +Once part of the gossip cluster, a node will also send a `NodeEventTypeJoin` message, which is a custom message defined by NetworkDB. +This is not actually needed now, but keeping it is useful for backwards compatibility with nodes running previous versions. + +While a node is active in the cluster, it can join and leave networks. +When a node wants to join a network, it will send a `NetworkEventTypeJoin` message via gossip to the whole cluster. +It will also perform a bulk-sync of the network-specific state (the tables) with every other node on the network being joined. +This will allow it to get all the network-specific information quickly. +The tables will mostly be kept up-to-date by UDP gossip messages between the nodes on that network, but +each node in the network will also periodically do a full TCP bulk sync of the tables with another random node on the same network. + +Note that there are two similar, but separate, gossip-and-periodic-sync mechanisms here: + +1. memberlist-provided gossip and push-pull sync of cluster-wide state, involving all nodes in the cluster. +2. networkdb-provided gossip and bulk sync of network tables, for each network, involving just those nodes in that network. + +When a node wishes to leave a network, it will send a `NetworkEventTypeLeave` via gossip. It will then delete the network's table data. +When a node hears that another node is leaving a network, it deletes all table entries belonging to the leaving node. +Deleting an entry in this case means marking it for deletion for a while, so that we can detect and ignore any older events that may arrive about it. + +When a node wishes to leave the cluster, it will send a `NodeEventTypeLeave` message via gossip. +Nodes receiving this will mark the node as "left". +The leaving node will then send a memberlist leave message too. +If we receive the memberlist leave message without first getting the `NodeEventTypeLeave` one, we mark the node as failed (for a while). +Every node periodically attempts to reconnect to failed nodes, and will do a push-pull sync of cluster-wide state on success. +On success we also send the node a `NodeEventTypeJoin` and then do a bulk sync of network-specific state for all networks that we have in common. + +[SWIM]: http://ieeexplore.ieee.org/document/1028914/ +[memberlist]: https://github.com/hashicorp/memberlist diff --git a/libnetwork/docs/overlay.md b/libnetwork/docs/overlay.md new file mode 100644 index 0000000000000..7e9094d3722a8 --- /dev/null +++ b/libnetwork/docs/overlay.md @@ -0,0 +1,153 @@ +# Overlay Driver + +### Design +TODO + +### Multi-Host Overlay Driver Quick Start + +This example is to provision two Docker Hosts with the **experimental** Libnetwork overlay network driver. + +### Pre-Requisites + +- Kernel >= 3.16 +- Experimental Docker client + +### Install Docker Experimental + +Follow Docker experimental installation instructions at: [https://github.com/docker/docker/tree/master/experimental](https://github.com/docker/docker/tree/master/experimental) + +To ensure you are running the experimental Docker branch, check the version and look for the experimental tag: + +``` +$ docker -v +Docker version 1.8.0-dev, build f39b9a0, experimental +``` + +### Install and Bootstrap K/V Store + + +Multi-host networking uses a pluggable Key-Value store backend to distribute states using `libkv`. +`libkv` supports multiple pluggable backends such as `consul`, `etcd` & `zookeeper` (more to come). + +In this example we will use `consul` + +Install: + +``` +$ curl -OL https://dl.bintray.com/mitchellh/consul/0.5.2_linux_amd64.zip +$ unzip 0.5.2_linux_amd64.zip +$ mv consul /usr/local/bin/ +``` + +**host-1** Start Consul as a server in bootstrap mode: + +``` +$ consul agent -server -bootstrap -data-dir /tmp/consul -bind= +``` + +**host-2** Start the Consul agent: + +``` +$ consul agent -data-dir /tmp/consul -bind= +$ consul join +``` + + +### Start the Docker Daemon with the Network Driver Daemon Flags + +**host-1** Docker daemon: + +``` +$ docker -d --kv-store=consul:localhost:8500 --label=com.docker.network.driver.overlay.bind_interface=eth0 +``` + +**host-2** Start the Docker Daemon with the neighbor ID configuration: + +``` +$ docker -d --kv-store=consul:localhost:8500 --label=com.docker.network.driver.overlay.bind_interface=eth0 --label=com.docker.network.driver.overlay.neighbor_ip= +``` + +### QuickStart Containers Attached to a Network + +**host-1** Start a container that publishes a service svc1 in the network dev that is managed by overlay driver. + +``` +$ docker run -i -t --publish-service=svc1.dev.overlay debian +root@21578ff721a9:/# ip add show eth0 +34: eth0: mtu 1500 qdisc noqueue state UP group default + link/ether 02:42:ec:41:35:bf brd ff:ff:ff:ff:ff:ff + inet 172.21.0.16/16 scope global eth0 + valid_lft forever preferred_lft forever + inet6 fe80::42:ecff:fe41:35bf/64 scope link + valid_lft forever preferred_lft forever +``` + +**host-2** Start a container that publishes a service svc2 in the network dev that is managed by overlay driver. + +``` +$ docker run -i -t --publish-service=svc2.dev.overlay debian +root@d217828eb876:/# ping svc1 +PING svc1 (172.21.0.16): 56 data bytes +64 bytes from 172.21.0.16: icmp_seq=0 ttl=64 time=0.706 ms +64 bytes from 172.21.0.16: icmp_seq=1 ttl=64 time=0.687 ms +64 bytes from 172.21.0.16: icmp_seq=2 ttl=64 time=0.841 ms +``` +### Detailed Setup + +You can also setup networks and services and then attach a running container to them. + +**host-1**: + +``` +docker network create -d overlay prod +docker network ls +docker network info prod +docker service publish db1.prod +cid=$(docker run -itd -p 8000:8000 ubuntu) +docker service attach $cid db1.prod +``` + +**host-2**: + +``` +docker network ls +docker network info prod +docker service publish db2.prod +cid=$(docker run -itd -p 8000:8000 ubuntu) +docker service attach $cid db2.prod +``` + +Once a container is started, a container on `host-1` and `host-2` both containers should be able to ping one another via IP, service name, \.\ + + +View information about the networks and services using `ls` and `info` subcommands like so: + +``` +$ docker service ls +SERVICE ID NAME NETWORK CONTAINER +0771deb5f84b db2 prod 0e54a527f22c +aea23b224acf db1 prod 4b0a309ca311 + +$ docker network info prod +Network Id: 5ac68be2518959b48ad102e9ec3d8f42fb2ec72056aa9592eb5abd0252203012 + Name: prod + Type: overlay + +$ docker service info db1.prod +Service Id: aea23b224acfd2da9b893870e0d632499188a1a4b3881515ba042928a9d3f465 + Name: db1 + Network: prod +``` + +To detach and unpublish a service: + +``` +$ docker service detach $cid . +$ docker service unpublish . + +# Example: +$ docker service detach $cid db2.prod +$ docker service unpublish db2.prod +``` + +To reiterate, this is experimental, and will be under active development. diff --git a/libnetwork/docs/remote.md b/libnetwork/docs/remote.md new file mode 100644 index 0000000000000..120f74abf10ba --- /dev/null +++ b/libnetwork/docs/remote.md @@ -0,0 +1,304 @@ +Remote Drivers +============== + +The `drivers.remote` package provides the integration point for dynamically-registered drivers. Unlike the other driver packages, it does not provide a single implementation of a driver; rather, it provides a proxy for remote driver processes, which are registered and communicate with LibNetwork via the Docker plugin package. + +For the semantics of driver methods, which correspond to the protocol below, please see the [overall design](design.md). + +## LibNetwork integration with the Docker `plugins` package + +When LibNetwork initializes the `drivers.remote` package with the `Init()` function, it passes a `DriverCallback` as a parameter, which implements `RegisterDriver()`. The remote driver package uses this interface to register remote drivers with LibNetwork's `NetworkController`, by supplying it in a `plugins.Handle` callback. + +The callback is invoked when a driver is loaded with the `plugins.Get` API call. How that comes about is out of scope here (but it might be, for instance, when that driver is mentioned by the user). + +This design ensures that the details of driver registration mechanism are owned by the remote driver package, and it doesn't expose any of the driver layer to the North of LibNetwork. + +## Implementation + +The remote driver implementation uses a `plugins.Client` to communicate with the remote driver process. The `driverapi.Driver` methods are implemented as RPCs over the plugin client. + +The payloads of these RPCs are mostly direct translations into JSON of the arguments given to the method. There are some exceptions to account for the use of the interfaces `InterfaceInfo` and `JoinInfo`, and data types that do not serialise to JSON well (e.g., `net.IPNet`). The protocol is detailed below under "Protocol". + +## Usage + +A remote driver proxy follows all the rules of any other in-built driver and has exactly the same `Driver` interface exposed. LibNetwork will also support driver-specific `options` and user-supplied `labels` which may influence the behaviour of a remote driver process. + +## Protocol + +The remote driver protocol is a set of RPCs, issued as HTTP POSTs with JSON payloads. The proxy issues requests, and the remote driver process is expected to respond usually with a JSON payload of its own, although in some cases these are empty maps. + +### Errors + +If the remote process cannot decode, or otherwise detects a syntactic problem with the HTTP request or payload, it must respond with an HTTP error status (4xx or 5xx). + +If the remote process http server receives a request for an unknown URI, it should respond with the HTTP StatusCode `404 Not Found`. This allows LibNetwork to detect when a remote driver does not implement yet a newly added method, therefore not to deem the request as failed. + +If the remote process can decode the request, but cannot complete the operation, it must send a response in the form + + { + "Err": string + } + +The string value supplied may appear in logs, so should not include confidential information. + +### Handshake + +When loaded, a remote driver process receives an HTTP POST on the URL `/Plugin.Activate` with no payload. It must respond with a manifest of the form + + { + "Implements": ["NetworkDriver"] + } + +Other entries in the list value are allowed; `"NetworkDriver"` indicates that the plugin should be registered with LibNetwork as a driver. + +### Set capability + +After Handshake, the remote driver will receive another POST message to the URL `/NetworkDriver.GetCapabilities` with no payload. The driver's response should have the form: + + { + "Scope": "local" + "ConnectivityScope": "global" + } + +Value of "Scope" should be either "local" or "global" which indicates whether the resource allocations for this driver's network can be done only locally to the node or globally across the cluster of nodes. Any other value will fail driver's registration and return an error to the caller. +Similarly, value of "ConnectivityScope" should be either "local" or "global" which indicates whether the driver's network can provide connectivity only locally to this node or globally across the cluster of nodes. If the value is missing, libnetwork will set it to the value of "Scope". should be either "local" or "global" which indicates + +### Create network + +When the proxy is asked to create a network, the remote process shall receive a POST to the URL `/NetworkDriver.CreateNetwork` of the form + + { + "NetworkID": string, + "IPv4Data" : [ + { + "AddressSpace": string, + "Pool": ipv4-cidr-string, + "Gateway" : ipv4-cidr-string, + "AuxAddresses": { + "" : "", + "" : "", + ... + } + }, + ], + "IPv6Data" : [ + { + "AddressSpace": string, + "Pool": ipv6-cidr-string, + "Gateway" : ipv6-cidr-string, + "AuxAddresses": { + "" : "", + "" : "", + ... + } + }, + ], + "Options": { + ... + } + } + +* `NetworkID` value is generated by LibNetwork which represents a unique network. +* `Options` value is the arbitrary map given to the proxy by LibNetwork. +* `IPv4Data` and `IPv6Data` are the ip-addressing data configured by the user and managed by IPAM driver. The network driver is expected to honor the ip-addressing data supplied by IPAM driver. The data include, +* `AddressSpace` : A unique string represents an isolated space for IP Addressing +* `Pool` : A range of IP Addresses represented in CIDR format address/mask. Since, the IPAM driver is responsible for allocating container ip-addresses, the network driver can make use of this information for the network plumbing purposes. +* `Gateway` : Optionally, the IPAM driver may provide a Gateway IP address in CIDR format for the subnet represented by the Pool. The network driver can make use of this information for the network plumbing purposes. +* `AuxAddresses` : A list of pre-allocated ip-addresses with an associated identifier as provided by the user to assist network driver if it requires specific ip-addresses for its operation. + +The response indicating success is empty: + + {} + +### Delete network + +When a network owned by the remote driver is deleted, the remote process shall receive a POST to the URL `/NetworkDriver.DeleteNetwork` of the form + + { + "NetworkID": string + } + +The success response is empty: + + {} + +### Create endpoint + +When the proxy is asked to create an endpoint, the remote process shall receive a POST to the URL `/NetworkDriver.CreateEndpoint` of the form + + { + "NetworkID": string, + "EndpointID": string, + "Options": { + ... + }, + "Interface": { + "Address": string, + "AddressIPv6": string, + "MacAddress": string + } + } + +The `NetworkID` is the generated identifier for the network to which the endpoint belongs; the `EndpointID` is a generated identifier for the endpoint. + +`Options` is an arbitrary map as supplied to the proxy. + +The `Interface` value is of the form given. The fields in the `Interface` may be empty; and the `Interface` itself may be empty. If supplied, `Address` is an IPv4 address and subnet in CIDR notation; e.g., `"192.168.34.12/16"`. If supplied, `AddressIPv6` is an IPv6 address and subnet in CIDR notation. `MacAddress` is a MAC address as a string; e.g., `"6e:75:32:60:44:c9"`. + +A success response is of the form + + { + "Interface": { + "Address": string, + "AddressIPv6": string, + "MacAddress": string + } + } + +with values in the `Interface` as above. As far as the value of `Interface` is concerned, `MacAddress` and either or both of `Address` and `AddressIPv6` must be given. + +If the remote process was supplied a non-empty value in `Interface`, it must respond with an empty `Interface` value. LibNetwork will treat it as an error if it supplies a non-empty value and receives a non-empty value back, and roll back the operation. + +### Endpoint operational info + +The proxy may be asked for "operational info" on an endpoint. When this happens, the remote process shall receive a POST to `/NetworkDriver.EndpointOperInfo` of the form + + { + "NetworkID": string, + "EndpointID": string + } + +where `NetworkID` and `EndpointID` have meanings as above. It must send a response of the form + + { + "Value": { ... } + } + +where the value of the `Value` field is an arbitrary (possibly empty) map. + +### Delete endpoint + +When an endpoint is deleted, the remote process shall receive a POST to the URL `/NetworkDriver.DeleteEndpoint` with a body of the form + + { + "NetworkID": string, + "EndpointID": string + } + +where `NetworkID` and `EndpointID` have meanings as above. A success response is empty: + + {} + +### Join + +When a sandbox is given an endpoint, the remote process shall receive a POST to the URL `NetworkDriver.Join` of the form + + { + "NetworkID": string, + "EndpointID": string, + "SandboxKey": string, + "Options": { ... } + } + +The `NetworkID` and `EndpointID` have meanings as above. The `SandboxKey` identifies the sandbox. `Options` is an arbitrary map as supplied to the proxy. + +The response must have the form + + { + "InterfaceName": { + SrcName: string, + DstPrefix: string + }, + "Gateway": string, + "GatewayIPv6": string, + "StaticRoutes": [{ + "Destination": string, + "RouteType": int, + "NextHop": string, + }, ...] + } + +`Gateway` is optional and if supplied is an IP address as a string; e.g., `"192.168.0.1"`. `GatewayIPv6` is optional and if supplied is an IPv6 address as a string; e.g., `"fe80::7809:baff:fec6:7744"`. + +The entries in `InterfaceName` represent actual OS level interfaces that should be moved by LibNetwork into the sandbox; the `SrcName` is the name of the OS level interface that the remote process created, and the `DstPrefix` is a prefix for the name the OS level interface should have after it has been moved into the sandbox (LibNetwork will append an index to make sure the actual name does not collide with others). + +The entries in `"StaticRoutes"` represent routes that should be added to an interface once it has been moved into the sandbox. Since there may be zero or more routes for an interface, unlike the interface name they can be supplied in any order. + +Routes are either given a `RouteType` of `0` and a value for `NextHop`; or, a `RouteType` of `1` and no value for `NextHop`, meaning a connected route. + +If no gateway and no default static route is set by the driver in the Join response, LibNetwork will add an additional interface to the sandbox connecting to a default gateway network (a bridge network named *docker_gwbridge*) and program the default gateway into the sandbox accordingly, pointing to the interface address of the bridge *docker_gwbridge*. + +### Leave + +If the proxy is asked to remove an endpoint from a sandbox, the remote process shall receive a POST to the URL `/NetworkDriver.Leave` of the form + + { + "NetworkID": string, + "EndpointID": string + } + +where `NetworkID` and `EndpointID` have meanings as above. The success response is empty: + + {} + +### DiscoverNew Notification + +LibNetwork listens to inbuilt docker discovery notifications and passes it along to the interested drivers. + +When the proxy receives a DiscoverNew notification, the remote process shall receive a POST to the URL `/NetworkDriver.DiscoverNew` of the form + + { + "DiscoveryType": int, + "DiscoveryData": { + ... + } + } + +`DiscoveryType` represents the discovery type. Each Discovery Type is represented by a number. +`DiscoveryData` carries discovery data the structure of which is determined by the DiscoveryType + +The response indicating success is empty: + + {} + +* Node Discovery + +Node Discovery is represented by a `DiscoveryType` value of `1` and the corresponding `DiscoveryData` will carry Node discovery data. + + { + "DiscoveryType": int, + "DiscoveryData": { + "Address" : string + "self" : bool + } + } + +### DiscoverDelete Notification + +When the proxy receives a DiscoverDelete notification, the remote process shall receive a POST to the URL `/NetworkDriver.DiscoverDelete` of the form + + { + "DiscoveryType": int, + "DiscoveryData": { + ... + } + } + +`DiscoveryType` represents the discovery type. Each Discovery Type is represented by a number. +`DiscoveryData` carries discovery data the structure of which is determined by the DiscoveryType + +The response indicating success is empty: + + {} + +* Node Discovery + +Similar to the DiscoverNew call, Node Discovery is represented by a `DiscoveryType` value of `1` and the corresponding `DiscoveryData` will carry Node discovery data to be deleted. + + { + "DiscoveryType": int, + "DiscoveryData": { + "Address" : string + "self" : bool + } + } diff --git a/libnetwork/docs/vagrant-systemd/docker.service b/libnetwork/docs/vagrant-systemd/docker.service new file mode 100644 index 0000000000000..f17b7848f5df5 --- /dev/null +++ b/libnetwork/docs/vagrant-systemd/docker.service @@ -0,0 +1,16 @@ +[Unit] +Description=Docker Application Container Engine +Documentation=https://docs.docker.com +After=network.target docker.socket +Requires=docker.socket + +[Service] +EnvironmentFile=-/etc/default/docker +ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS +MountFlags=slave +LimitNOFILE=1048576 +LimitNPROC=1048576 +LimitCORE=infinity + +[Install] +WantedBy=multi-user.target diff --git a/libnetwork/docs/vagrant.md b/libnetwork/docs/vagrant.md new file mode 100644 index 0000000000000..0d2d3daa0967e --- /dev/null +++ b/libnetwork/docs/vagrant.md @@ -0,0 +1,185 @@ +# Vagrant Setup to Test the Overlay Driver + +This documentation highlights how to use Vagrant to start a three nodes setup to test Docker network. + +## Pre-requisites + +This was tested on: + +- Vagrant 1.7.2 +- VirtualBox 4.3.26 + +## Machine Setup + +The Vagrantfile provided will start three virtual machines. One will act as a consul server, and the other two will act as Docker host. +The experimental version of Docker is installed. + +- `consul-server` is the Consul server node, based on Ubuntu 14.04, this has IP 192.168.33.10 +- `net-1` is the first Docker host based on Ubuntu 14.10, this has IP 192.168.33.11 +- `net-2` is the second Docker host based on Ubuntu 14.10, this has IP 192.168.33.12 + +## Getting Started + +Clone this repo, change to the `docs` directory and let Vagrant do the work. + + $ vagrant up + $ vagrant status + Current machine states: + + consul-server running (virtualbox) + net-1 running (virtualbox) + net-2 running (virtualbox) + +You are now ready to SSH to the Docker hosts and start containers. + + $ vagrant ssh net-1 + vagrant@net-1:~$ docker version + Client version: 1.8.0-dev + ...... + +Check that Docker network is functional by listing the default networks: + + vagrant@net-1:~$ docker network ls + NETWORK ID NAME TYPE + 4275f8b3a821 none null + 80eba28ed4a7 host host + 64322973b4aa bridge bridge + +No services has been published so far, so the `docker service ls` will return an empty list: + + $ docker service ls + SERVICE ID NAME NETWORK CONTAINER + +Start a container and check the content of `/etc/hosts`. + + $ docker run -it --rm ubuntu:14.04 bash + root@df479e660658:/# cat /etc/hosts + 172.21.0.3 df479e660658 + 127.0.0.1 localhost + ::1 localhost ip6-localhost ip6-loopback + fe00::0 ip6-localnet + ff00::0 ip6-mcastprefix + ff02::1 ip6-allnodes + ff02::2 ip6-allrouters + 172.21.0.3 distracted_bohr + 172.21.0.3 distracted_bohr.multihost + +In a separate terminal on `net-1` list the networks again. You will see that the _multihost_ overlay now appears. +The overlay network _multihost_ is your default network. This was setup by the Docker daemon during the Vagrant provisioning. Check `/etc/default/docker` to see the options that were set. + + vagrant@net-1:~$ docker network ls + NETWORK ID NAME TYPE + 4275f8b3a821 none null + 80eba28ed4a7 host host + 64322973b4aa bridge bridge + b5c9f05f1f8f multihost overlay + +Now in a separate terminal, SSH to `net-2`, check the network and services. The networks will be the same, and the default network will also be _multihost_ of type overlay. But the service will show the container started on `net-1`: + + $ vagrant ssh net-2 + vagrant@net-2:~$ docker service ls + SERVICE ID NAME NETWORK CONTAINER + b00f2bfd81ac distracted_bohr multihost df479e660658 + +Start a container on `net-2` and check the `/etc/hosts`. + + vagrant@net-2:~$ docker run -ti --rm ubuntu:14.04 bash + root@2ac726b4ce60:/# cat /etc/hosts + 172.21.0.4 2ac726b4ce60 + 127.0.0.1 localhost + ::1 localhost ip6-localhost ip6-loopback + fe00::0 ip6-localnet + ff00::0 ip6-mcastprefix + ff02::1 ip6-allnodes + ff02::2 ip6-allrouters + 172.21.0.3 distracted_bohr + 172.21.0.3 distracted_bohr.multihost + 172.21.0.4 modest_curie + 172.21.0.4 modest_curie.multihost + +You will see not only the container that you just started on `net-2` but also the container that you started earlier on `net-1`. +And of course you will be able to ping each container. + +## Creating a Non Default Overlay Network + +In the previous test we started containers with regular options `-ti --rm` and these containers got placed automatically in the default network which was set to be the _multihost_ network of type overlay. + +But you could create your own overlay network and start containers in it. Let's create a new overlay network. +On one of your Docker hosts, `net-1` or `net-2` do: + + $ docker network create -d overlay foobar + 8805e22ad6e29cd7abb95597c91420fdcac54f33fcdd6fbca6dd4ec9710dd6a4 + $ docker network ls + NETWORK ID NAME TYPE + a77e16a1e394 host host + 684a4bb4c471 bridge bridge + 8805e22ad6e2 foobar overlay + b5c9f05f1f8f multihost overlay + 67d5a33a2e54 none null + +Automatically, the second host will also see this network. To start a container on this new network, simply use the `--publish-service` option of `docker run` like so: + + $ docker run -it --rm --publish-service=bar.foobar.overlay ubuntu:14.04 bash + +Note, that you could directly start a container with a new overlay using the `--publish-service` option and it will create the network automatically. + +Check the docker services now: + + $ docker service ls + SERVICE ID NAME NETWORK CONTAINER + b1ffdbfb1ac6 bar foobar 6635a3822135 + +Repeat the getting started steps, by starting another container in this new overlay on the other host, check the `/etc/hosts` file and try to ping each container. + +## A look at the interfaces + +This new Docker multihost networking is made possible via VXLAN tunnels and the use of network namespaces. +Check the [design](design.md) documentation for all the details. But to explore these concepts a bit, nothing beats an example. + +With a running container in one overlay, check the network namespace: + + $ docker inspect -f '{{ .NetworkSettings.SandboxKey}}' 6635a3822135 + /var/run/docker/netns/6635a3822135 + +This is a none default location for network namespaces which might confuse things a bit. So let's become root, head over to this directory that contains the network namespaces of the containers and check the interfaces: + + $ sudo su + root@net-2:/home/vagrant# cd /var/run/docker/ + root@net-2:/var/run/docker# ls netns + 6635a3822135 + 8805e22ad6e2 + +To be able to check the interfaces in those network namespace using `ip` command, just create a symlink for `netns` that points to `/var/run/docker/netns`: + + root@net-2:/var/run# ln -s /var/run/docker/netns netns + root@net-2:/var/run# ip netns show + 6635a3822135 + 8805e22ad6e2 + +The two namespace ID return are the ones of the running container on that host and the one of the actual overlay network the container is in. +Let's check the interfaces in the container: + + root@net-2:/var/run/docker# ip netns exec 6635a3822135 ip addr show eth0 + 15: eth0: mtu 1500 qdisc noqueue state UP group default + link/ether 02:42:b3:91:22:c3 brd ff:ff:ff:ff:ff:ff + inet 172.21.0.5/16 scope global eth0 + valid_lft forever preferred_lft forever + inet6 fe80::42:b3ff:fe91:22c3/64 scope link + valid_lft forever preferred_lft forever + +Indeed we get back the network interface of our running container, same MAC address, same IP. +If we check the links of the overlay namespace we see our vxlan interface and the VLAN ID being used. + + root@net-2:/var/run/docker# ip netns exec 8805e22ad6e2 ip -d link show + ...... + 14: vxlan1: mtu 1500 qdisc noqueue master br0 state UNKNOWN mode DEFAULT group default + link/ether 7a:af:20:ee:e3:81 brd ff:ff:ff:ff:ff:ff promiscuity 1 + vxlan id 256 srcport 32768 61000 dstport 8472 proxy l2miss l3miss ageing 300 + bridge_slave + 16: veth2: mtu 1500 qdisc pfifo_fast master br0 state UP mode DEFAULT group default qlen 1000 + link/ether 46:b1:e2:5c:48:a8 brd ff:ff:ff:ff:ff:ff promiscuity 1 + veth + bridge_slave + +If you sniff packets on these interfaces you will see the traffic between your containers. + diff --git a/vendor/github.com/docker/libnetwork/driverapi/driverapi.go b/libnetwork/driverapi/driverapi.go similarity index 96% rename from vendor/github.com/docker/libnetwork/driverapi/driverapi.go rename to libnetwork/driverapi/driverapi.go index 3d667ceb7e792..e9a0b5b1d31fe 100644 --- a/vendor/github.com/docker/libnetwork/driverapi/driverapi.go +++ b/libnetwork/driverapi/driverapi.go @@ -3,8 +3,8 @@ package driverapi import ( "net" + "github.com/docker/docker/libnetwork/discoverapi" "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/libnetwork/discoverapi" ) // NetworkPluginEndpointType represents the Endpoint Type used by Plugin system @@ -95,6 +95,11 @@ type NetworkInfo interface { // TableEventRegister registers driver interest in a given // table name. TableEventRegister(tableName string, objType ObjectType) error + + // UpdateIPamConfig updates the networks IPAM configuration + // based on information from the driver. In windows, the OS (HNS) chooses + // the IP address space if the user does not specify an address space. + UpdateIpamConfig(ipV4Data []IPAMData) } // InterfaceInfo provides a go interface for drivers to retrieve diff --git a/libnetwork/driverapi/driverapi_test.go b/libnetwork/driverapi/driverapi_test.go new file mode 100644 index 0000000000000..e26da27e148aa --- /dev/null +++ b/libnetwork/driverapi/driverapi_test.go @@ -0,0 +1,118 @@ +package driverapi + +import ( + "encoding/json" + "net" + "testing" + + "github.com/docker/docker/libnetwork/types" +) + +func TestIPDataMarshalling(t *testing.T) { + i := &IPAMData{ + AddressSpace: "giallo", + Pool: &net.IPNet{IP: net.IP{10, 10, 10, 8}, Mask: net.IPMask{255, 255, 255, 0}}, + Gateway: &net.IPNet{IP: net.IP{10, 10, 10, 254}, Mask: net.IPMask{255, 255, 255, 0}}, + AuxAddresses: map[string]*net.IPNet{ + "ip1": {IP: net.IP{10, 10, 10, 1}, Mask: net.IPMask{255, 255, 255, 0}}, + "ip2": {IP: net.IP{10, 10, 10, 2}, Mask: net.IPMask{255, 255, 255, 0}}, + }, + } + + b, err := json.Marshal(i) + if err != nil { + t.Fatal(err) + } + + ii := &IPAMData{} + err = json.Unmarshal(b, &ii) + if err != nil { + t.Fatal(err) + } + + if i.AddressSpace != ii.AddressSpace || !types.CompareIPNet(i.Pool, ii.Pool) || + !types.CompareIPNet(i.Gateway, ii.Gateway) || + !compareAddresses(i.AuxAddresses, ii.AuxAddresses) { + t.Fatalf("JSON marsh/unmarsh failed.\nOriginal:\n%s\nDecoded:\n%s", i, ii) + } +} + +func compareAddresses(a, b map[string]*net.IPNet) bool { + if len(a) != len(b) { + return false + } + if len(a) > 0 { + for k := range a { + if !types.CompareIPNet(a[k], b[k]) { + return false + } + } + } + return true +} + +func TestValidateAndIsV6(t *testing.T) { + var err error + + i := &IPAMData{ + Pool: &net.IPNet{IP: net.IP{10, 10, 10, 8}, Mask: net.IPMask{255, 255, 255, 0}}, + Gateway: &net.IPNet{IP: net.IP{10, 10, 10, 254}, Mask: net.IPMask{255, 255, 255, 0}}, + AuxAddresses: map[string]*net.IPNet{ + "ip1": {IP: net.IP{10, 10, 10, 1}, Mask: net.IPMask{255, 255, 255, 0}}, + "ip2": {IP: net.IP{10, 10, 10, 2}, Mask: net.IPMask{255, 255, 255, 0}}, + }, + } + + // Check ip version + if i.IsV6() { + t.Fatal("incorrect ip version returned") + } + orig := i.Pool + if i.Pool, err = types.ParseCIDR("2001:db8::33/64"); err != nil { + t.Fatal(err) + } + if !i.IsV6() { + t.Fatal("incorrect ip version returned") + } + i.Pool = orig + + // valid ip data + if err = i.Validate(); err != nil { + t.Fatal(err) + } + + // incongruent gw ver + if i.Gateway, err = types.ParseCIDR("2001:db8::45/65"); err != nil { + t.Fatal(err) + } + if err = i.Validate(); err == nil { + t.Fatal("expected error but succeeded") + } + i.Gateway = nil + + // incongruent secondary ip ver + if i.AuxAddresses["ip2"], err = types.ParseCIDR("2001:db8::44/80"); err != nil { + t.Fatal(err) + } + if err = i.Validate(); err == nil { + t.Fatal("expected error but succeeded") + } + delete(i.AuxAddresses, "ip2") + + // gw outside pool + if i.Gateway, err = types.ParseCIDR("10.10.15.254/24"); err != nil { + t.Fatal(err) + } + if err = i.Validate(); err == nil { + t.Fatal("expected error but succeeded") + } + i.Gateway = nil + + // sec ip outside of pool + if i.AuxAddresses["ip1"], err = types.ParseCIDR("10.10.2.1/24"); err != nil { + t.Fatal(err) + } + if err = i.Validate(); err == nil { + t.Fatal("expected error but succeeded") + } +} diff --git a/vendor/github.com/docker/libnetwork/driverapi/errors.go b/libnetwork/driverapi/errors.go similarity index 100% rename from vendor/github.com/docker/libnetwork/driverapi/errors.go rename to libnetwork/driverapi/errors.go diff --git a/vendor/github.com/docker/libnetwork/driverapi/ipamdata.go b/libnetwork/driverapi/ipamdata.go similarity index 98% rename from vendor/github.com/docker/libnetwork/driverapi/ipamdata.go rename to libnetwork/driverapi/ipamdata.go index fc1c2af441afb..5d47122e2f46c 100644 --- a/vendor/github.com/docker/libnetwork/driverapi/ipamdata.go +++ b/libnetwork/driverapi/ipamdata.go @@ -5,7 +5,7 @@ import ( "fmt" "net" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/types" ) // MarshalJSON encodes IPAMData into json message diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go similarity index 88% rename from vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go rename to libnetwork/drivers/bridge/bridge.go index b617ea7bc445e..0c7811c1ef143 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -1,9 +1,11 @@ +//go:build linux +// +build linux + package bridge import ( "errors" "fmt" - "io/ioutil" "net" "os" "os/exec" @@ -12,17 +14,17 @@ import ( "sync" "syscall" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/iptables" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/options" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/portmapper" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/iptables" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/portmapper" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" ) @@ -57,6 +59,7 @@ type iptablesCleanFuncs []iptableCleanFunc type configuration struct { EnableIPForwarding bool EnableIPTables bool + EnableIP6Tables bool EnableUserlandProxy bool UserlandProxyPath string } @@ -68,9 +71,11 @@ type networkConfiguration struct { EnableIPv6 bool EnableIPMasquerade bool EnableICC bool + InhibitIPv4 bool Mtu int DefaultBindingIP net.IP DefaultBridge bool + HostIP net.IP ContainerIfacePrefix string // Internal fields set after ipam data parsing AddressIPv4 *net.IPNet @@ -131,22 +136,26 @@ type bridgeNetwork struct { config *networkConfiguration endpoints map[string]*bridgeEndpoint // key: endpoint id portMapper *portmapper.PortMapper + portMapperV6 *portmapper.PortMapper driver *driver // The network's driver iptCleanFuncs iptablesCleanFuncs sync.Mutex } type driver struct { - config *configuration - network *bridgeNetwork - natChain *iptables.ChainInfo - filterChain *iptables.ChainInfo - isolationChain1 *iptables.ChainInfo - isolationChain2 *iptables.ChainInfo - networks map[string]*bridgeNetwork - store datastore.DataStore - nlh *netlink.Handle - configNetwork sync.Mutex + config *configuration + natChain *iptables.ChainInfo + filterChain *iptables.ChainInfo + isolationChain1 *iptables.ChainInfo + isolationChain2 *iptables.ChainInfo + natChainV6 *iptables.ChainInfo + filterChainV6 *iptables.ChainInfo + isolationChain1V6 *iptables.ChainInfo + isolationChain2V6 *iptables.ChainInfo + networks map[string]*bridgeNetwork + store datastore.DataStore + nlh *netlink.Handle + configNetwork sync.Mutex sync.Mutex } @@ -243,6 +252,10 @@ func (c *networkConfiguration) fromLabels(labels map[string]string) error { if c.EnableICC, err = strconv.ParseBool(value); err != nil { return parseErr(label, value, err.Error()) } + case InhibitIPv4: + if c.InhibitIPv4, err = strconv.ParseBool(value); err != nil { + return parseErr(label, value, err.Error()) + } case DefaultBridge: if c.DefaultBridge, err = strconv.ParseBool(value); err != nil { return parseErr(label, value, err.Error()) @@ -253,6 +266,10 @@ func (c *networkConfiguration) fromLabels(labels map[string]string) error { } case netlabel.ContainerIfacePrefix: c.ContainerIfacePrefix = value + case netlabel.HostIP: + if c.HostIP = net.ParseIP(value); c.HostIP == nil { + return parseErr(label, value, "nil ip") + } } } @@ -267,7 +284,7 @@ func (n *bridgeNetwork) registerIptCleanFunc(clean iptableCleanFunc) { n.iptCleanFuncs = append(n.iptCleanFuncs, clean) } -func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) { +func (n *bridgeNetwork) getDriverChains(version iptables.IPVersion) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) { n.Lock() defer n.Unlock() @@ -275,6 +292,10 @@ func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainI return nil, nil, nil, nil, types.BadRequestErrorf("no driver found") } + if version == iptables.IPv6 { + return n.driver.natChainV6, n.driver.filterChainV6, n.driver.isolationChain1V6, n.driver.isolationChain2V6, nil + } + return n.driver.natChain, n.driver.filterChain, n.driver.isolationChain1, n.driver.isolationChain2, nil } @@ -303,7 +324,7 @@ func (n *bridgeNetwork) getEndpoint(eid string) (*bridgeEndpoint, error) { // Install/Removes the iptables rules needed to isolate this network // from each of the other networks -func (n *bridgeNetwork) isolateNetwork(others []*bridgeNetwork, enable bool) error { +func (n *bridgeNetwork) isolateNetwork(enable bool) error { n.Lock() thisConfig := n.config n.Unlock() @@ -313,17 +334,31 @@ func (n *bridgeNetwork) isolateNetwork(others []*bridgeNetwork, enable bool) err } // Install the rules to isolate this network against each of the other networks - return setINC(thisConfig.BridgeName, enable) + if n.driver.config.EnableIP6Tables { + err := setINC(iptables.IPv6, thisConfig.BridgeName, enable) + if err != nil { + return err + } + } + + if n.driver.config.EnableIPTables { + return setINC(iptables.IPv4, thisConfig.BridgeName, enable) + } + return nil } func (d *driver) configure(option map[string]interface{}) error { var ( - config *configuration - err error - natChain *iptables.ChainInfo - filterChain *iptables.ChainInfo - isolationChain1 *iptables.ChainInfo - isolationChain2 *iptables.ChainInfo + config *configuration + err error + natChain *iptables.ChainInfo + filterChain *iptables.ChainInfo + isolationChain1 *iptables.ChainInfo + isolationChain2 *iptables.ChainInfo + natChainV6 *iptables.ChainInfo + filterChainV6 *iptables.ChainInfo + isolationChain1V6 *iptables.ChainInfo + isolationChain2V6 *iptables.ChainInfo ) genericData, ok := option[netlabel.GenericData] @@ -344,23 +379,50 @@ func (d *driver) configure(option map[string]interface{}) error { return &ErrInvalidDriverConfig{} } - if config.EnableIPTables { + if config.EnableIPTables || config.EnableIP6Tables { if _, err := os.Stat("/proc/sys/net/bridge"); err != nil { if out, err := exec.Command("modprobe", "-va", "bridge", "br_netfilter").CombinedOutput(); err != nil { logrus.Warnf("Running modprobe bridge br_netfilter failed with message: %s, error: %v", out, err) } } - removeIPChains() - natChain, filterChain, isolationChain1, isolationChain2, err = setupIPChains(config) + } + + if config.EnableIPTables { + removeIPChains(iptables.IPv4) + + natChain, filterChain, isolationChain1, isolationChain2, err = setupIPChains(config, iptables.IPv4) + if err != nil { + return err + } + + // Make sure on firewall reload, first thing being re-played is chains creation + iptables.OnReloaded(func() { + logrus.Debugf("Recreating iptables chains on firewall reload") + if _, _, _, _, err := setupIPChains(config, iptables.IPv4); err != nil { + logrus.WithError(err).Error("Error reloading iptables chains") + } + }) + } + + if config.EnableIP6Tables { + removeIPChains(iptables.IPv6) + + natChainV6, filterChainV6, isolationChain1V6, isolationChain2V6, err = setupIPChains(config, iptables.IPv6) if err != nil { return err } + // Make sure on firewall reload, first thing being re-played is chains creation - iptables.OnReloaded(func() { logrus.Debugf("Recreating iptables chains on firewall reload"); setupIPChains(config) }) + iptables.OnReloaded(func() { + logrus.Debugf("Recreating ip6tables chains on firewall reload") + if _, _, _, _, err := setupIPChains(config, iptables.IPv6); err != nil { + logrus.WithError(err).Error("Error reloading ip6tables chains") + } + }) } if config.EnableIPForwarding { - err = setupIPForwarding(config.EnableIPTables) + err = setupIPForwarding(config.EnableIPTables, config.EnableIP6Tables) if err != nil { logrus.Warn(err) return err @@ -372,6 +434,10 @@ func (d *driver) configure(option map[string]interface{}) error { d.filterChain = filterChain d.isolationChain1 = isolationChain1 d.isolationChain2 = isolationChain2 + d.natChainV6 = natChainV6 + d.filterChainV6 = filterChainV6 + d.isolationChain1V6 = isolationChain1V6 + d.isolationChain2V6 = isolationChain2V6 d.config = config d.Unlock() @@ -486,7 +552,7 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati return nil, err } - if config.BridgeName == "" && config.DefaultBridge == false { + if config.BridgeName == "" && !config.DefaultBridge { config.BridgeName = "br-" + id[:12] } @@ -505,18 +571,6 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati return config, nil } -// Returns the non link-local IPv6 subnet for the containers attached to this bridge if found, nil otherwise -func getV6Network(config *networkConfiguration, i *bridgeInterface) *net.IPNet { - if config.AddressIPv6 != nil { - return config.AddressIPv6 - } - if i.bridgeIPv6 != nil && i.bridgeIPv6.IP != nil && !i.bridgeIPv6.IP.IsLinkLocalUnicast() { - return i.bridgeIPv6 - } - - return nil -} - // Return a slice of networks over which caller can iterate safely func (d *driver) getNetworks() []*bridgeNetwork { d.Lock() @@ -580,7 +634,9 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d } // Got a conflict with a stale default network, clean that up and continue logrus.Warn(nerr) - d.deleteNetwork(nerr.ID) + if err := d.deleteNetwork(nerr.ID); err != nil { + logrus.WithError(err).Debug("Error while cleaning up network on conflict") + } } // there is no conflict, now create the network @@ -617,8 +673,6 @@ func (d *driver) checkConflict(config *networkConfiguration) error { func (d *driver) createNetwork(config *networkConfiguration) (err error) { defer osl.InitOSContext()() - networkList := d.getNetworks() - // Initialize handle when needed d.Lock() if d.nlh == nil { @@ -634,12 +688,13 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) { // Create and set network handler in driver network := &bridgeNetwork{ - id: config.ID, - endpoints: make(map[string]*bridgeEndpoint), - config: config, - portMapper: portmapper.New(d.config.UserlandProxyPath), - bridge: bridgeIface, - driver: d, + id: config.ID, + endpoints: make(map[string]*bridgeEndpoint), + config: config, + portMapper: portmapper.New(d.config.UserlandProxyPath), + portMapperV6: portmapper.New(d.config.UserlandProxyPath), + bridge: bridgeIface, + driver: d, } d.Lock() @@ -657,16 +712,15 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) { // Add inter-network communication rules. setupNetworkIsolationRules := func(config *networkConfiguration, i *bridgeInterface) error { - if err := network.isolateNetwork(networkList, true); err != nil { - if err = network.isolateNetwork(networkList, false); err != nil { + if err := network.isolateNetwork(true); err != nil { + if err = network.isolateNetwork(false); err != nil { logrus.Warnf("Failed on removing the inter-network iptables rules on cleanup: %v", err) } return err } // register the cleanup function network.registerIptCleanFunc(func() error { - nwList := d.getNetworks() - return network.isolateNetwork(nwList, false) + return network.isolateNetwork(false) }) return nil } @@ -679,6 +733,12 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) { bridgeAlreadyExists := bridgeIface.exists() if !bridgeAlreadyExists { bridgeSetup.queueStep(setupDevice) + bridgeSetup.queueStep(setupDefaultSysctl) + } + + // For the default bridge, set expected sysctls + if config.DefaultBridge { + bridgeSetup.queueStep(setupDefaultSysctl) } // Even if a bridge exists try to setup IPv4. @@ -699,7 +759,7 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) { // We ensure that the bridge has the expectedIPv4 and IPv6 addresses in // the case of a previously existing device. - {bridgeAlreadyExists, setupVerifyAndReconcile}, + {bridgeAlreadyExists && !config.InhibitIPv4, setupVerifyAndReconcile}, // Enable IPv6 Forwarding {enableIPv6Forwarding, setupIPv6Forwarding}, @@ -708,11 +768,16 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) { {!d.config.EnableUserlandProxy, setupLoopbackAddressesRouting}, // Setup IPTables. - {d.config.EnableIPTables, network.setupIPTables}, + {d.config.EnableIPTables, network.setupIP4Tables}, + + // Setup IP6Tables. + {config.EnableIPv6 && d.config.EnableIP6Tables, network.setupIP6Tables}, - //We want to track firewalld configuration so that - //if it is started/reloaded, the rules can be applied correctly + // We want to track firewalld configuration so that + // if it is started/reloaded, the rules can be applied correctly {d.config.EnableIPTables, network.setupFirewalld}, + // same for IPv6 + {config.EnableIPv6 && d.config.EnableIP6Tables, network.setupFirewalld6}, // Setup DefaultGatewayIPv4 {config.DefaultGatewayIPv4 != nil, setupGatewayIPv4}, @@ -723,7 +788,7 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) { // Add inter-network communication rules. {d.config.EnableIPTables, setupNetworkIsolationRules}, - //Configure bridge networking filtering if ICC is off and IP tables are enabled + // Configure bridge networking filtering if ICC is off and IP tables are enabled {!config.EnableICC && d.config.EnableIPTables, setupBridgeNetFiltering}, } { if step.Condition { @@ -862,7 +927,7 @@ func setHairpinMode(nlh *netlink.Handle, link netlink.Link, enable bool) error { val = []byte{'0', '\n'} } - if err := ioutil.WriteFile(path, val, 0644); err != nil { + if err := os.WriteFile(path, val, 0644); err != nil { return fmt.Errorf("unable to set hairpin mode on %s via sysfs: %v", link.Attrs().Name, err) } diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go b/libnetwork/drivers/bridge/bridge_store.go similarity index 95% rename from vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go rename to libnetwork/drivers/bridge/bridge_store.go index 2988c34fa58bf..7578f964b9d4e 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/bridge_store.go +++ b/libnetwork/drivers/bridge/bridge_store.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package bridge import ( @@ -5,10 +8,10 @@ import ( "fmt" "net" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -137,10 +140,12 @@ func (ncfg *networkConfiguration) MarshalJSON() ([]byte, error) { nMap["EnableIPv6"] = ncfg.EnableIPv6 nMap["EnableIPMasquerade"] = ncfg.EnableIPMasquerade nMap["EnableICC"] = ncfg.EnableICC + nMap["InhibitIPv4"] = ncfg.InhibitIPv4 nMap["Mtu"] = ncfg.Mtu nMap["Internal"] = ncfg.Internal nMap["DefaultBridge"] = ncfg.DefaultBridge nMap["DefaultBindingIP"] = ncfg.DefaultBindingIP.String() + nMap["HostIP"] = ncfg.HostIP.String() nMap["DefaultGatewayIPv4"] = ncfg.DefaultGatewayIPv4.String() nMap["DefaultGatewayIPv6"] = ncfg.DefaultGatewayIPv6.String() nMap["ContainerIfacePrefix"] = ncfg.ContainerIfacePrefix @@ -183,6 +188,10 @@ func (ncfg *networkConfiguration) UnmarshalJSON(b []byte) error { ncfg.ContainerIfacePrefix = v.(string) } + if v, ok := nMap["HostIP"]; ok { + ncfg.HostIP = net.ParseIP(v.(string)) + } + ncfg.DefaultBridge = nMap["DefaultBridge"].(bool) ncfg.DefaultBindingIP = net.ParseIP(nMap["DefaultBindingIP"].(string)) ncfg.DefaultGatewayIPv4 = net.ParseIP(nMap["DefaultGatewayIPv4"].(string)) @@ -192,6 +201,10 @@ func (ncfg *networkConfiguration) UnmarshalJSON(b []byte) error { ncfg.EnableIPv6 = nMap["EnableIPv6"].(bool) ncfg.EnableIPMasquerade = nMap["EnableIPMasquerade"].(bool) ncfg.EnableICC = nMap["EnableICC"].(bool) + if v, ok := nMap["InhibitIPv4"]; ok { + ncfg.InhibitIPv4 = v.(bool) + } + ncfg.Mtu = int(nMap["Mtu"].(float64)) if v, ok := nMap["Internal"]; ok { ncfg.Internal = v.(bool) diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go new file mode 100644 index 0000000000000..c4ada1630ada1 --- /dev/null +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -0,0 +1,1136 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "regexp" + "strconv" + "testing" + + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/iptables" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/testutils" + "github.com/docker/docker/libnetwork/types" + "github.com/vishvananda/netlink" +) + +func TestEndpointMarshalling(t *testing.T) { + ip1, _ := types.ParseCIDR("172.22.0.9/16") + ip2, _ := types.ParseCIDR("2001:db8::9") + mac, _ := net.ParseMAC("ac:bd:24:57:66:77") + e := &bridgeEndpoint{ + id: "d2c015a1fe5930650cbcd50493efba0500bcebd8ee1f4401a16319f8a567de33", + nid: "ee33fbb43c323f1920b6b35a0101552ac22ede960d0e5245e9738bccc68b2415", + addr: ip1, + addrv6: ip2, + macAddress: mac, + srcName: "veth123456", + config: &endpointConfiguration{MacAddress: mac}, + containerConfig: &containerConfiguration{ + ParentEndpoints: []string{"one", "due", "three"}, + ChildEndpoints: []string{"four", "five", "six"}, + }, + extConnConfig: &connectivityConfiguration{ + ExposedPorts: []types.TransportPort{ + { + Proto: 6, + Port: uint16(18), + }, + }, + PortBindings: []types.PortBinding{ + { + Proto: 6, + IP: net.ParseIP("17210.33.9.56"), + Port: uint16(18), + HostPort: uint16(3000), + HostPortEnd: uint16(14000), + }, + }, + }, + portMapping: []types.PortBinding{ + { + Proto: 17, + IP: net.ParseIP("172.33.9.56"), + Port: uint16(99), + HostIP: net.ParseIP("10.10.100.2"), + HostPort: uint16(9900), + HostPortEnd: uint16(10000), + }, + { + Proto: 6, + IP: net.ParseIP("171.33.9.56"), + Port: uint16(55), + HostIP: net.ParseIP("10.11.100.2"), + HostPort: uint16(5500), + HostPortEnd: uint16(55000), + }, + }, + } + + b, err := json.Marshal(e) + if err != nil { + t.Fatal(err) + } + + ee := &bridgeEndpoint{} + err = json.Unmarshal(b, ee) + if err != nil { + t.Fatal(err) + } + + if e.id != ee.id || e.nid != ee.nid || e.srcName != ee.srcName || !bytes.Equal(e.macAddress, ee.macAddress) || + !types.CompareIPNet(e.addr, ee.addr) || !types.CompareIPNet(e.addrv6, ee.addrv6) || + !compareEpConfig(e.config, ee.config) || + !compareContainerConfig(e.containerConfig, ee.containerConfig) || + !compareConnConfig(e.extConnConfig, ee.extConnConfig) || + !compareBindings(e.portMapping, ee.portMapping) { + t.Fatalf("JSON marsh/unmarsh failed.\nOriginal:\n%#v\nDecoded:\n%#v", e, ee) + } +} + +func compareEpConfig(a, b *endpointConfiguration) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + return bytes.Equal(a.MacAddress, b.MacAddress) +} + +func compareContainerConfig(a, b *containerConfiguration) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + if len(a.ParentEndpoints) != len(b.ParentEndpoints) || + len(a.ChildEndpoints) != len(b.ChildEndpoints) { + return false + } + for i := 0; i < len(a.ParentEndpoints); i++ { + if a.ParentEndpoints[i] != b.ParentEndpoints[i] { + return false + } + } + for i := 0; i < len(a.ChildEndpoints); i++ { + if a.ChildEndpoints[i] != b.ChildEndpoints[i] { + return false + } + } + return true +} + +func compareConnConfig(a, b *connectivityConfiguration) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + if len(a.ExposedPorts) != len(b.ExposedPorts) || + len(a.PortBindings) != len(b.PortBindings) { + return false + } + for i := 0; i < len(a.ExposedPorts); i++ { + if !a.ExposedPorts[i].Equal(&b.ExposedPorts[i]) { + return false + } + } + for i := 0; i < len(a.PortBindings); i++ { + if !a.PortBindings[i].Equal(&b.PortBindings[i]) { + return false + } + } + return true +} + +func compareBindings(a, b []types.PortBinding) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if !a[i].Equal(&b[i]) { + return false + } + } + return true +} + +func getIPv4Data(t *testing.T, iface string) []driverapi.IPAMData { + ipd := driverapi.IPAMData{AddressSpace: "full"} + nws, _, err := netutils.ElectInterfaceAddresses(iface) + if err != nil { + t.Fatal(err) + } + ipd.Pool = nws[0] + // Set network gateway to X.X.X.1 + ipd.Gateway = types.GetIPNetCopy(nws[0]) + ipd.Gateway.IP[len(ipd.Gateway.IP)-1] = 1 + return []driverapi.IPAMData{ipd} +} + +func TestCreateFullOptions(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + d := newDriver() + + config := &configuration{ + EnableIPForwarding: true, + EnableIPTables: true, + } + + // Test this scenario: Default gw address does not belong to + // container network and it's greater than bridge address + cnw, _ := types.ParseCIDR("172.16.122.0/24") + bnw, _ := types.ParseCIDR("172.16.0.0/24") + br, _ := types.ParseCIDR("172.16.0.1/16") + defgw, _ := types.ParseCIDR("172.16.0.100/16") + + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + if err := d.configure(genericOption); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + netOption := make(map[string]interface{}) + netOption[netlabel.EnableIPv6] = true + netOption[netlabel.GenericData] = &networkConfiguration{ + BridgeName: DefaultBridgeName, + } + + ipdList := []driverapi.IPAMData{ + { + Pool: bnw, + Gateway: br, + AuxAddresses: map[string]*net.IPNet{DefaultGatewayV4AuxKey: defgw}, + }, + } + err := d.CreateNetwork("dummy", netOption, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + // Verify the IP address allocated for the endpoint belongs to the container network + epOptions := make(map[string]interface{}) + te := newTestEndpoint(cnw, 10) + err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions) + if err != nil { + t.Fatalf("Failed to create an endpoint : %s", err.Error()) + } + + if !cnw.Contains(te.Interface().Address().IP) { + t.Fatalf("endpoint got assigned address outside of container network(%s): %s", cnw.String(), te.Interface().Address()) + } +} + +func TestCreateNoConfig(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + d := newDriver() + + netconfig := &networkConfiguration{BridgeName: DefaultBridgeName} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = netconfig + + if err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil); err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } +} + +func TestCreateFullOptionsLabels(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + d := newDriver() + + config := &configuration{ + EnableIPForwarding: true, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + if err := d.configure(genericOption); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + bndIPs := "127.0.0.1" + testHostIP := "1.2.3.4" + nwV6s := "2001:db8:2600:2700:2800::/80" + gwV6s := "2001:db8:2600:2700:2800::25/80" + nwV6, _ := types.ParseCIDR(nwV6s) + gwV6, _ := types.ParseCIDR(gwV6s) + + labels := map[string]string{ + BridgeName: DefaultBridgeName, + DefaultBridge: "true", + EnableICC: "true", + EnableIPMasquerade: "true", + DefaultBindingIP: bndIPs, + netlabel.HostIP: testHostIP, + } + + netOption := make(map[string]interface{}) + netOption[netlabel.EnableIPv6] = true + netOption[netlabel.GenericData] = labels + + ipdList := getIPv4Data(t, "") + ipd6List := []driverapi.IPAMData{ + { + Pool: nwV6, + AuxAddresses: map[string]*net.IPNet{ + DefaultGatewayV6AuxKey: gwV6, + }, + }, + } + + err := d.CreateNetwork("dummy", netOption, nil, ipdList, ipd6List) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + nw, ok := d.networks["dummy"] + if !ok { + t.Fatal("Cannot find dummy network in bridge driver") + } + + if nw.config.BridgeName != DefaultBridgeName { + t.Fatal("incongruent name in bridge network") + } + + if !nw.config.EnableIPv6 { + t.Fatal("incongruent EnableIPv6 in bridge network") + } + + if !nw.config.EnableICC { + t.Fatal("incongruent EnableICC in bridge network") + } + + if !nw.config.EnableIPMasquerade { + t.Fatal("incongruent EnableIPMasquerade in bridge network") + } + + bndIP := net.ParseIP(bndIPs) + if !bndIP.Equal(nw.config.DefaultBindingIP) { + t.Fatalf("Unexpected: %v", nw.config.DefaultBindingIP) + } + + hostIP := net.ParseIP(testHostIP) + if !hostIP.Equal(nw.config.HostIP) { + t.Fatalf("Unexpected: %v", nw.config.HostIP) + } + + if !types.CompareIPNet(nw.config.AddressIPv6, nwV6) { + t.Fatalf("Unexpected: %v", nw.config.AddressIPv6) + } + + if !gwV6.IP.Equal(nw.config.DefaultGatewayIPv6) { + t.Fatalf("Unexpected: %v", nw.config.DefaultGatewayIPv6) + } + + // In short here we are testing --fixed-cidr-v6 daemon option + // plus --mac-address run option + mac, _ := net.ParseMAC("aa:bb:cc:dd:ee:ff") + epOptions := map[string]interface{}{netlabel.MacAddress: mac} + te := newTestEndpoint(ipdList[0].Pool, 20) + err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions) + if err != nil { + t.Fatal(err) + } + + if !nwV6.Contains(te.Interface().AddressIPv6().IP) { + t.Fatalf("endpoint got assigned address outside of container network(%s): %s", nwV6.String(), te.Interface().AddressIPv6()) + } + if te.Interface().AddressIPv6().IP.String() != "2001:db8:2600:2700:2800:aabb:ccdd:eeff" { + t.Fatalf("Unexpected endpoint IPv6 address: %v", te.Interface().AddressIPv6().IP) + } +} + +func TestCreate(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + d := newDriver() + + if err := d.configure(nil); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + netconfig := &networkConfiguration{BridgeName: DefaultBridgeName} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = netconfig + + if err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil); err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil) + if err == nil { + t.Fatal("Expected bridge driver to refuse creation of second network with default name") + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatal("Creation of second network with default name failed with unexpected error type") + } +} + +func TestCreateFail(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + d := newDriver() + + if err := d.configure(nil); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + netconfig := &networkConfiguration{BridgeName: "dummy0", DefaultBridge: true} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = netconfig + + if err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil); err == nil { + t.Fatal("Bridge creation was expected to fail") + } +} + +func TestCreateMultipleNetworks(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + d := newDriver() + + config := &configuration{ + EnableIPTables: true, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + if err := d.configure(genericOption); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + config1 := &networkConfiguration{BridgeName: "net_test_1"} + genericOption = make(map[string]interface{}) + genericOption[netlabel.GenericData] = config1 + if err := d.CreateNetwork("1", genericOption, nil, getIPv4Data(t, ""), nil); err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + verifyV4INCEntries(d.networks, t) + + config2 := &networkConfiguration{BridgeName: "net_test_2"} + genericOption[netlabel.GenericData] = config2 + if err := d.CreateNetwork("2", genericOption, nil, getIPv4Data(t, ""), nil); err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + verifyV4INCEntries(d.networks, t) + + config3 := &networkConfiguration{BridgeName: "net_test_3"} + genericOption[netlabel.GenericData] = config3 + if err := d.CreateNetwork("3", genericOption, nil, getIPv4Data(t, ""), nil); err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + verifyV4INCEntries(d.networks, t) + + config4 := &networkConfiguration{BridgeName: "net_test_4"} + genericOption[netlabel.GenericData] = config4 + if err := d.CreateNetwork("4", genericOption, nil, getIPv4Data(t, ""), nil); err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + verifyV4INCEntries(d.networks, t) + + if err := d.DeleteNetwork("1"); err != nil { + t.Log(err) + } + verifyV4INCEntries(d.networks, t) + + if err := d.DeleteNetwork("2"); err != nil { + t.Log(err) + } + verifyV4INCEntries(d.networks, t) + + if err := d.DeleteNetwork("3"); err != nil { + t.Log(err) + } + verifyV4INCEntries(d.networks, t) + + if err := d.DeleteNetwork("4"); err != nil { + t.Log(err) + } + verifyV4INCEntries(d.networks, t) +} + +// Verify the network isolation rules are installed for each network +func verifyV4INCEntries(networks map[string]*bridgeNetwork, t *testing.T) { + iptable := iptables.GetIptable(iptables.IPv4) + out1, err := iptable.Raw("-S", IsolationChain1) + if err != nil { + t.Fatal(err) + } + out2, err := iptable.Raw("-S", IsolationChain2) + if err != nil { + t.Fatal(err) + } + + for _, n := range networks { + re := regexp.MustCompile(fmt.Sprintf("-i %s ! -o %s -j %s", n.config.BridgeName, n.config.BridgeName, IsolationChain2)) + matches := re.FindAllString(string(out1[:]), -1) + if len(matches) != 1 { + t.Fatalf("Cannot find expected inter-network isolation rules in IP Tables for network %s:\n%s.", n.id, string(out1[:])) + } + re = regexp.MustCompile(fmt.Sprintf("-o %s -j DROP", n.config.BridgeName)) + matches = re.FindAllString(string(out2[:]), -1) + if len(matches) != 1 { + t.Fatalf("Cannot find expected inter-network isolation rules in IP Tables for network %s:\n%s.", n.id, string(out2[:])) + } + } +} + +type testInterface struct { + mac net.HardwareAddr + addr *net.IPNet + addrv6 *net.IPNet + srcName string + dstName string +} + +type testEndpoint struct { + iface *testInterface + gw net.IP + gw6 net.IP + routes []types.StaticRoute +} + +func newTestEndpoint(nw *net.IPNet, ordinal byte) *testEndpoint { + addr := types.GetIPNetCopy(nw) + addr.IP[len(addr.IP)-1] = ordinal + return &testEndpoint{iface: &testInterface{addr: addr}} +} + +func (te *testEndpoint) Interface() driverapi.InterfaceInfo { + if te.iface != nil { + return te.iface + } + + return nil +} + +func (i *testInterface) MacAddress() net.HardwareAddr { + return i.mac +} + +func (i *testInterface) Address() *net.IPNet { + return i.addr +} + +func (i *testInterface) AddressIPv6() *net.IPNet { + return i.addrv6 +} + +func (i *testInterface) SetMacAddress(mac net.HardwareAddr) error { + if i.mac != nil { + return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", i.mac, mac) + } + if mac == nil { + return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface") + } + i.mac = types.GetMacCopy(mac) + return nil +} + +func (i *testInterface) SetIPAddress(address *net.IPNet) error { + if address.IP == nil { + return types.BadRequestErrorf("tried to set nil IP address to endpoint interface") + } + if address.IP.To4() == nil { + return setAddress(&i.addrv6, address) + } + return setAddress(&i.addr, address) +} + +func setAddress(ifaceAddr **net.IPNet, address *net.IPNet) error { + if *ifaceAddr != nil { + return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with (%s).", *ifaceAddr, address) + } + *ifaceAddr = types.GetIPNetCopy(address) + return nil +} + +func (i *testInterface) SetNames(srcName string, dstName string) error { + i.srcName = srcName + i.dstName = dstName + return nil +} + +func (te *testEndpoint) InterfaceName() driverapi.InterfaceNameInfo { + if te.iface != nil { + return te.iface + } + + return nil +} + +func (te *testEndpoint) SetGateway(gw net.IP) error { + te.gw = gw + return nil +} + +func (te *testEndpoint) SetGatewayIPv6(gw6 net.IP) error { + te.gw6 = gw6 + return nil +} + +func (te *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error { + te.routes = append(te.routes, types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop}) + return nil +} + +func (te *testEndpoint) AddTableEntry(tableName string, key string, value []byte) error { + return nil +} + +func (te *testEndpoint) DisableGatewayService() {} + +func TestQueryEndpointInfo(t *testing.T) { + testQueryEndpointInfo(t, true) +} + +func TestQueryEndpointInfoHairpin(t *testing.T) { + testQueryEndpointInfo(t, false) +} + +func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + d := newDriver() + + config := &configuration{ + EnableIPTables: true, + EnableUserlandProxy: ulPxyEnabled, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + if err := d.configure(genericOption); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + netconfig := &networkConfiguration{ + BridgeName: DefaultBridgeName, + EnableICC: false, + } + genericOption = make(map[string]interface{}) + genericOption[netlabel.GenericData] = netconfig + + ipdList := getIPv4Data(t, "") + err := d.CreateNetwork("net1", genericOption, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + sbOptions := make(map[string]interface{}) + sbOptions[netlabel.PortMap] = getPortMapping() + + te := newTestEndpoint(ipdList[0].Pool, 11) + err = d.CreateEndpoint("net1", "ep1", te.Interface(), nil) + if err != nil { + t.Fatalf("Failed to create an endpoint : %s", err.Error()) + } + + err = d.Join("net1", "ep1", "sbox", te, sbOptions) + if err != nil { + t.Fatalf("Failed to join the endpoint: %v", err) + } + + err = d.ProgramExternalConnectivity("net1", "ep1", sbOptions) + if err != nil { + t.Fatalf("Failed to program external connectivity: %v", err) + } + + network, ok := d.networks["net1"] + if !ok { + t.Fatalf("Cannot find network %s inside driver", "net1") + } + ep := network.endpoints["ep1"] + data, err := d.EndpointOperInfo(network.id, ep.id) + if err != nil { + t.Fatalf("Failed to ask for endpoint operational data: %v", err) + } + pmd, ok := data[netlabel.PortMap] + if !ok { + t.Fatal("Endpoint operational data does not contain port mapping data") + } + pm, ok := pmd.([]types.PortBinding) + if !ok { + t.Fatal("Unexpected format for port mapping in endpoint operational data") + } + if len(ep.portMapping) != len(pm) { + t.Fatal("Incomplete data for port mapping in endpoint operational data") + } + for i, pb := range ep.portMapping { + if !pb.Equal(&pm[i]) { + t.Fatal("Unexpected data for port mapping in endpoint operational data") + } + } + + err = d.RevokeExternalConnectivity("net1", "ep1") + if err != nil { + t.Fatal(err) + } + + // release host mapped ports + err = d.Leave("net1", "ep1") + if err != nil { + t.Fatal(err) + } +} + +func getExposedPorts() []types.TransportPort { + return []types.TransportPort{ + {Proto: types.TCP, Port: uint16(5000)}, + {Proto: types.UDP, Port: uint16(400)}, + {Proto: types.TCP, Port: uint16(600)}, + } +} + +func getPortMapping() []types.PortBinding { + return []types.PortBinding{ + {Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)}, + {Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)}, + {Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)}, + } +} + +func TestLinkContainers(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + d := newDriver() + iptable := iptables.GetIptable(iptables.IPv4) + + config := &configuration{ + EnableIPTables: true, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + if err := d.configure(genericOption); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + netconfig := &networkConfiguration{ + BridgeName: DefaultBridgeName, + EnableICC: false, + } + genericOption = make(map[string]interface{}) + genericOption[netlabel.GenericData] = netconfig + + ipdList := getIPv4Data(t, "") + err := d.CreateNetwork("net1", genericOption, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te1 := newTestEndpoint(ipdList[0].Pool, 11) + err = d.CreateEndpoint("net1", "ep1", te1.Interface(), nil) + if err != nil { + t.Fatalf("Failed to create an endpoint : %s", err.Error()) + } + + exposedPorts := getExposedPorts() + sbOptions := make(map[string]interface{}) + sbOptions[netlabel.ExposedPorts] = exposedPorts + + err = d.Join("net1", "ep1", "sbox", te1, sbOptions) + if err != nil { + t.Fatalf("Failed to join the endpoint: %v", err) + } + + err = d.ProgramExternalConnectivity("net1", "ep1", sbOptions) + if err != nil { + t.Fatalf("Failed to program external connectivity: %v", err) + } + + addr1 := te1.iface.addr + if addr1.IP.To4() == nil { + t.Fatal("No Ipv4 address assigned to the endpoint: ep1") + } + + te2 := newTestEndpoint(ipdList[0].Pool, 22) + err = d.CreateEndpoint("net1", "ep2", te2.Interface(), nil) + if err != nil { + t.Fatalf("Failed to create an endpoint : %s", err.Error()) + } + + addr2 := te2.iface.addr + if addr2.IP.To4() == nil { + t.Fatal("No Ipv4 address assigned to the endpoint: ep2") + } + + sbOptions = make(map[string]interface{}) + sbOptions[netlabel.GenericData] = options.Generic{ + "ChildEndpoints": []string{"ep1"}, + } + + err = d.Join("net1", "ep2", "", te2, sbOptions) + if err != nil { + t.Fatal("Failed to link ep1 and ep2") + } + + err = d.ProgramExternalConnectivity("net1", "ep2", sbOptions) + if err != nil { + t.Fatalf("Failed to program external connectivity: %v", err) + } + + out, _ := iptable.Raw("-L", DockerChain) + for _, pm := range exposedPorts { + regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port) + re := regexp.MustCompile(regex) + matches := re.FindAllString(string(out[:]), -1) + if len(matches) != 1 { + t.Fatalf("IP Tables programming failed %s", string(out[:])) + } + + regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port) + matched, _ := regexp.MatchString(regex, string(out[:])) + if !matched { + t.Fatalf("IP Tables programming failed %s", string(out[:])) + } + } + + err = d.RevokeExternalConnectivity("net1", "ep2") + if err != nil { + t.Fatalf("Failed to revoke external connectivity: %v", err) + } + + err = d.Leave("net1", "ep2") + if err != nil { + t.Fatal("Failed to unlink ep1 and ep2") + } + + out, _ = iptable.Raw("-L", DockerChain) + for _, pm := range exposedPorts { + regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port) + re := regexp.MustCompile(regex) + matches := re.FindAllString(string(out[:]), -1) + if len(matches) != 0 { + t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:])) + } + + regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port) + matched, _ := regexp.MatchString(regex, string(out[:])) + if matched { + t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:])) + } + } + + // Error condition test with an invalid endpoint-id "ep4" + sbOptions = make(map[string]interface{}) + sbOptions[netlabel.GenericData] = options.Generic{ + "ChildEndpoints": []string{"ep1", "ep4"}, + } + + err = d.Join("net1", "ep2", "", te2, sbOptions) + if err != nil { + t.Fatal(err) + } + err = d.ProgramExternalConnectivity("net1", "ep2", sbOptions) + if err != nil { + out, _ = iptable.Raw("-L", DockerChain) + for _, pm := range exposedPorts { + regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port) + re := regexp.MustCompile(regex) + matches := re.FindAllString(string(out[:]), -1) + if len(matches) != 0 { + t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:])) + } + + regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port) + matched, _ := regexp.MatchString(regex, string(out[:])) + if matched { + t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:])) + } + } + } else { + t.Fatal("Expected Join to fail given link conditions are not satisfied") + } +} + +func TestValidateConfig(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + // Test mtu + c := networkConfiguration{Mtu: -2} + err := c.Validate() + if err == nil { + t.Fatal("Failed to detect invalid MTU number") + } + + c.Mtu = 9000 + err = c.Validate() + if err != nil { + t.Fatal("unexpected validation error on MTU number") + } + + // Bridge network + _, network, _ := net.ParseCIDR("172.28.0.0/16") + c = networkConfiguration{ + AddressIPv4: network, + } + + err = c.Validate() + if err != nil { + t.Fatal(err) + } + + // Test v4 gw + c.DefaultGatewayIPv4 = net.ParseIP("172.27.30.234") + err = c.Validate() + if err == nil { + t.Fatal("Failed to detect invalid default gateway") + } + + c.DefaultGatewayIPv4 = net.ParseIP("172.28.30.234") + err = c.Validate() + if err != nil { + t.Fatal("Unexpected validation error on default gateway") + } + + // Test v6 gw + _, v6nw, _ := net.ParseCIDR("2001:db8:ae:b004::/64") + c = networkConfiguration{ + EnableIPv6: true, + AddressIPv6: v6nw, + DefaultGatewayIPv6: net.ParseIP("2001:db8:ac:b004::bad:a55"), + } + err = c.Validate() + if err == nil { + t.Fatal("Failed to detect invalid v6 default gateway") + } + + c.DefaultGatewayIPv6 = net.ParseIP("2001:db8:ae:b004::bad:a55") + err = c.Validate() + if err != nil { + t.Fatal("Unexpected validation error on v6 default gateway") + } + + c.AddressIPv6 = nil + err = c.Validate() + if err == nil { + t.Fatal("Failed to detect invalid v6 default gateway") + } + + c.AddressIPv6 = nil + err = c.Validate() + if err == nil { + t.Fatal("Failed to detect invalid v6 default gateway") + } +} + +func TestSetDefaultGw(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + d := newDriver() + + if err := d.configure(nil); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + _, subnetv6, _ := net.ParseCIDR("2001:db8:ea9:9abc:b0c4::/80") + + ipdList := getIPv4Data(t, "") + gw4 := types.GetIPCopy(ipdList[0].Pool.IP).To4() + gw4[3] = 254 + gw6 := net.ParseIP("2001:db8:ea9:9abc:b0c4::254") + + config := &networkConfiguration{ + BridgeName: DefaultBridgeName, + AddressIPv6: subnetv6, + DefaultGatewayIPv4: gw4, + DefaultGatewayIPv6: gw6, + } + + genericOption := make(map[string]interface{}) + genericOption[netlabel.EnableIPv6] = true + genericOption[netlabel.GenericData] = config + + err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te := newTestEndpoint(ipdList[0].Pool, 10) + err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil) + if err != nil { + t.Fatalf("Failed to create endpoint: %v", err) + } + + err = d.Join("dummy", "ep", "sbox", te, nil) + if err != nil { + t.Fatalf("Failed to join endpoint: %v", err) + } + + if !gw4.Equal(te.gw) { + t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw4, te.gw) + } + + if !gw6.Equal(te.gw6) { + t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw6, te.gw6) + } +} + +func TestCleanupIptableRules(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + bridgeChain := []iptables.ChainInfo{ + {Name: DockerChain, Table: iptables.Nat}, + {Name: DockerChain, Table: iptables.Filter}, + {Name: IsolationChain1, Table: iptables.Filter}, + } + + ipVersions := []iptables.IPVersion{iptables.IPv4, iptables.IPv6} + + for _, version := range ipVersions { + if _, _, _, _, err := setupIPChains(&configuration{EnableIPTables: true}, version); err != nil { + t.Fatalf("Error setting up ip chains for %s: %v", version, err) + } + + iptable := iptables.GetIptable(version) + for _, chainInfo := range bridgeChain { + if !iptable.ExistChain(chainInfo.Name, chainInfo.Table) { + t.Fatalf("iptables version %s chain %s of %s table should have been created", version, chainInfo.Name, chainInfo.Table) + } + } + removeIPChains(version) + for _, chainInfo := range bridgeChain { + if iptable.ExistChain(chainInfo.Name, chainInfo.Table) { + t.Fatalf("iptables version %s chain %s of %s table should have been deleted", version, chainInfo.Name, chainInfo.Table) + } + } + } +} + +func TestCreateWithExistingBridge(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + d := newDriver() + + if err := d.configure(nil); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + brName := "br111" + br := &netlink.Bridge{ + LinkAttrs: netlink.LinkAttrs{ + Name: brName, + }, + } + if err := netlink.LinkAdd(br); err != nil { + t.Fatalf("Failed to create bridge interface: %v", err) + } + defer netlink.LinkDel(br) + if err := netlink.LinkSetUp(br); err != nil { + t.Fatalf("Failed to set bridge interface up: %v", err) + } + + ip := net.IP{192, 168, 122, 1} + addr := &netlink.Addr{IPNet: &net.IPNet{ + IP: ip, + Mask: net.IPv4Mask(255, 255, 255, 0), + }} + if err := netlink.AddrAdd(br, addr); err != nil { + t.Fatalf("Failed to add IP address to bridge: %v", err) + } + + netconfig := &networkConfiguration{BridgeName: brName} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = netconfig + + if err := d.CreateNetwork(brName, genericOption, nil, getIPv4Data(t, brName), nil); err != nil { + t.Fatalf("Failed to create bridge network: %v", err) + } + + nw, err := d.getNetwork(brName) + if err != nil { + t.Fatalf("Failed to getNetwork(%s): %v", brName, err) + } + + addrs4, _, err := nw.bridge.addresses() + if err != nil { + t.Fatalf("Failed to get the bridge network's address: %v", err) + } + + if !addrs4[0].IP.Equal(ip) { + t.Fatal("Creating bridge network with existing bridge interface unexpectedly modified the IP address of the bridge") + } + + if err := d.DeleteNetwork(brName); err != nil { + t.Fatalf("Failed to delete network %s: %v", brName, err) + } + + if _, err := netlink.LinkByName(brName); err != nil { + t.Fatal("Deleting bridge network that using existing bridge interface unexpectedly deleted the bridge interface") + } +} + +func TestCreateParallel(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + d := newDriver() + + if err := d.configure(nil); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + ch := make(chan error, 100) + for i := 0; i < 100; i++ { + go func(name string, ch chan<- error) { + config := &networkConfiguration{BridgeName: name} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + if err := d.CreateNetwork(name, genericOption, nil, getIPv4Data(t, "docker0"), nil); err != nil { + ch <- fmt.Errorf("failed to create %s", name) + return + } + if err := d.CreateNetwork(name, genericOption, nil, getIPv4Data(t, "docker0"), nil); err == nil { + ch <- fmt.Errorf("failed was able to create overlap %s", name) + return + } + ch <- nil + }("net"+strconv.Itoa(i), ch) + } + // wait for the go routines + var success int + for i := 0; i < 100; i++ { + val := <-ch + if val == nil { + success++ + } + } + if success != 1 { + t.Fatalf("Success should be 1 instead: %d", success) + } +} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/brmanager/brmanager.go b/libnetwork/drivers/bridge/brmanager/brmanager.go similarity index 93% rename from vendor/github.com/docker/libnetwork/drivers/bridge/brmanager/brmanager.go rename to libnetwork/drivers/bridge/brmanager/brmanager.go index 74bb95c001806..1bcddee85f580 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/brmanager/brmanager.go +++ b/libnetwork/drivers/bridge/brmanager/brmanager.go @@ -1,10 +1,10 @@ package brmanager import ( - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/types" ) const networkType = "bridge" diff --git a/libnetwork/drivers/bridge/errors.go b/libnetwork/drivers/bridge/errors.go new file mode 100644 index 0000000000000..5cb0692b564d0 --- /dev/null +++ b/libnetwork/drivers/bridge/errors.go @@ -0,0 +1,344 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "fmt" + "net" +) + +// ErrConfigExists error is returned when driver already has a config applied. +type ErrConfigExists struct{} + +func (ece *ErrConfigExists) Error() string { + return "configuration already exists, bridge configuration can be applied only once" +} + +// Forbidden denotes the type of this error +func (ece *ErrConfigExists) Forbidden() {} + +// ErrInvalidDriverConfig error is returned when Bridge Driver is passed an invalid config +type ErrInvalidDriverConfig struct{} + +func (eidc *ErrInvalidDriverConfig) Error() string { + return "Invalid configuration passed to Bridge Driver" +} + +// BadRequest denotes the type of this error +func (eidc *ErrInvalidDriverConfig) BadRequest() {} + +// ErrInvalidNetworkConfig error is returned when a network is created on a driver without valid config. +type ErrInvalidNetworkConfig struct{} + +func (einc *ErrInvalidNetworkConfig) Error() string { + return "trying to create a network on a driver without valid config" +} + +// Forbidden denotes the type of this error +func (einc *ErrInvalidNetworkConfig) Forbidden() {} + +// ErrInvalidContainerConfig error is returned when an endpoint create is attempted with an invalid configuration. +type ErrInvalidContainerConfig struct{} + +func (eicc *ErrInvalidContainerConfig) Error() string { + return "Error in joining a container due to invalid configuration" +} + +// BadRequest denotes the type of this error +func (eicc *ErrInvalidContainerConfig) BadRequest() {} + +// ErrInvalidEndpointConfig error is returned when an endpoint create is attempted with an invalid endpoint configuration. +type ErrInvalidEndpointConfig struct{} + +func (eiec *ErrInvalidEndpointConfig) Error() string { + return "trying to create an endpoint with an invalid endpoint configuration" +} + +// BadRequest denotes the type of this error +func (eiec *ErrInvalidEndpointConfig) BadRequest() {} + +// ErrNetworkExists error is returned when a network already exists and another network is created. +type ErrNetworkExists struct{} + +func (ene *ErrNetworkExists) Error() string { + return "network already exists, bridge can only have one network" +} + +// Forbidden denotes the type of this error +func (ene *ErrNetworkExists) Forbidden() {} + +// ErrIfaceName error is returned when a new name could not be generated. +type ErrIfaceName struct{} + +func (ein *ErrIfaceName) Error() string { + return "failed to find name for new interface" +} + +// InternalError denotes the type of this error +func (ein *ErrIfaceName) InternalError() {} + +// ErrNoIPAddr error is returned when bridge has no IPv4 address configured. +type ErrNoIPAddr struct{} + +func (enip *ErrNoIPAddr) Error() string { + return "bridge has no IPv4 address configured" +} + +// InternalError denotes the type of this error +func (enip *ErrNoIPAddr) InternalError() {} + +// ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid. +type ErrInvalidGateway struct{} + +func (eig *ErrInvalidGateway) Error() string { + return "default gateway ip must be part of the network" +} + +// BadRequest denotes the type of this error +func (eig *ErrInvalidGateway) BadRequest() {} + +// ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid. +type ErrInvalidContainerSubnet struct{} + +func (eis *ErrInvalidContainerSubnet) Error() string { + return "container subnet must be a subset of bridge network" +} + +// BadRequest denotes the type of this error +func (eis *ErrInvalidContainerSubnet) BadRequest() {} + +// ErrInvalidMtu is returned when the user provided MTU is not valid. +type ErrInvalidMtu int + +func (eim ErrInvalidMtu) Error() string { + return fmt.Sprintf("invalid MTU number: %d", int(eim)) +} + +// BadRequest denotes the type of this error +func (eim ErrInvalidMtu) BadRequest() {} + +// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid. +type ErrInvalidPort string + +func (ip ErrInvalidPort) Error() string { + return fmt.Sprintf("invalid transport port: %s", string(ip)) +} + +// BadRequest denotes the type of this error +func (ip ErrInvalidPort) BadRequest() {} + +// ErrUnsupportedAddressType is returned when the specified address type is not supported. +type ErrUnsupportedAddressType string + +func (uat ErrUnsupportedAddressType) Error() string { + return fmt.Sprintf("unsupported address type: %s", string(uat)) +} + +// BadRequest denotes the type of this error +func (uat ErrUnsupportedAddressType) BadRequest() {} + +// ErrInvalidAddressBinding is returned when the host address specified in the port binding is not valid. +type ErrInvalidAddressBinding string + +func (iab ErrInvalidAddressBinding) Error() string { + return fmt.Sprintf("invalid host address in port binding: %s", string(iab)) +} + +// BadRequest denotes the type of this error +func (iab ErrInvalidAddressBinding) BadRequest() {} + +// ActiveEndpointsError is returned when there are +// still active endpoints in the network being deleted. +type ActiveEndpointsError string + +func (aee ActiveEndpointsError) Error() string { + return fmt.Sprintf("network %s has active endpoint", string(aee)) +} + +// Forbidden denotes the type of this error +func (aee ActiveEndpointsError) Forbidden() {} + +// InvalidNetworkIDError is returned when the passed +// network id for an existing network is not a known id. +type InvalidNetworkIDError string + +func (inie InvalidNetworkIDError) Error() string { + return fmt.Sprintf("invalid network id %s", string(inie)) +} + +// NotFound denotes the type of this error +func (inie InvalidNetworkIDError) NotFound() {} + +// InvalidEndpointIDError is returned when the passed +// endpoint id is not valid. +type InvalidEndpointIDError string + +func (ieie InvalidEndpointIDError) Error() string { + return fmt.Sprintf("invalid endpoint id: %s", string(ieie)) +} + +// BadRequest denotes the type of this error +func (ieie InvalidEndpointIDError) BadRequest() {} + +// InvalidSandboxIDError is returned when the passed +// sandbox id is not valid. +type InvalidSandboxIDError string + +func (isie InvalidSandboxIDError) Error() string { + return fmt.Sprintf("invalid sandbox id: %s", string(isie)) +} + +// BadRequest denotes the type of this error +func (isie InvalidSandboxIDError) BadRequest() {} + +// EndpointNotFoundError is returned when the no endpoint +// with the passed endpoint id is found. +type EndpointNotFoundError string + +func (enfe EndpointNotFoundError) Error() string { + return fmt.Sprintf("endpoint not found: %s", string(enfe)) +} + +// NotFound denotes the type of this error +func (enfe EndpointNotFoundError) NotFound() {} + +// NonDefaultBridgeExistError is returned when a non-default +// bridge config is passed but it does not already exist. +type NonDefaultBridgeExistError string + +func (ndbee NonDefaultBridgeExistError) Error() string { + return fmt.Sprintf("bridge device with non default name %s must be created manually", string(ndbee)) +} + +// Forbidden denotes the type of this error +func (ndbee NonDefaultBridgeExistError) Forbidden() {} + +// NonDefaultBridgeNeedsIPError is returned when a non-default +// bridge config is passed but it has no ip configured +type NonDefaultBridgeNeedsIPError string + +func (ndbee NonDefaultBridgeNeedsIPError) Error() string { + return fmt.Sprintf("bridge device with non default name %s must have a valid IP address", string(ndbee)) +} + +// Forbidden denotes the type of this error +func (ndbee NonDefaultBridgeNeedsIPError) Forbidden() {} + +// FixedCIDRv4Error is returned when fixed-cidrv4 configuration +// failed. +type FixedCIDRv4Error struct { + Net *net.IPNet + Subnet *net.IPNet + Err error +} + +func (fcv4 *FixedCIDRv4Error) Error() string { + return fmt.Sprintf("setup FixedCIDRv4 failed for subnet %s in %s: %v", fcv4.Subnet, fcv4.Net, fcv4.Err) +} + +// InternalError denotes the type of this error +func (fcv4 *FixedCIDRv4Error) InternalError() {} + +// FixedCIDRv6Error is returned when fixed-cidrv6 configuration +// failed. +type FixedCIDRv6Error struct { + Net *net.IPNet + Err error +} + +func (fcv6 *FixedCIDRv6Error) Error() string { + return fmt.Sprintf("setup FixedCIDRv6 failed for subnet %s in %s: %v", fcv6.Net, fcv6.Net, fcv6.Err) +} + +// InternalError denotes the type of this error +func (fcv6 *FixedCIDRv6Error) InternalError() {} + +// IPTableCfgError is returned when an unexpected ip tables configuration is entered +type IPTableCfgError string + +func (name IPTableCfgError) Error() string { + return fmt.Sprintf("unexpected request to set IP tables for interface: %s", string(name)) +} + +// BadRequest denotes the type of this error +func (name IPTableCfgError) BadRequest() {} + +// InvalidIPTablesCfgError is returned when an invalid ip tables configuration is entered +type InvalidIPTablesCfgError string + +func (action InvalidIPTablesCfgError) Error() string { + return fmt.Sprintf("Invalid IPTables action '%s'", string(action)) +} + +// BadRequest denotes the type of this error +func (action InvalidIPTablesCfgError) BadRequest() {} + +// IPv4AddrRangeError is returned when a valid IP address range couldn't be found. +type IPv4AddrRangeError string + +func (name IPv4AddrRangeError) Error() string { + return fmt.Sprintf("can't find an address range for interface %q", string(name)) +} + +// BadRequest denotes the type of this error +func (name IPv4AddrRangeError) BadRequest() {} + +// IPv4AddrAddError is returned when IPv4 address could not be added to the bridge. +type IPv4AddrAddError struct { + IP *net.IPNet + Err error +} + +func (ipv4 *IPv4AddrAddError) Error() string { + return fmt.Sprintf("failed to add IPv4 address %s to bridge: %v", ipv4.IP, ipv4.Err) +} + +// InternalError denotes the type of this error +func (ipv4 *IPv4AddrAddError) InternalError() {} + +// IPv6AddrAddError is returned when IPv6 address could not be added to the bridge. +type IPv6AddrAddError struct { + IP *net.IPNet + Err error +} + +func (ipv6 *IPv6AddrAddError) Error() string { + return fmt.Sprintf("failed to add IPv6 address %s to bridge: %v", ipv6.IP, ipv6.Err) +} + +// InternalError denotes the type of this error +func (ipv6 *IPv6AddrAddError) InternalError() {} + +// IPv4AddrNoMatchError is returned when the bridge's IPv4 address does not match configured. +type IPv4AddrNoMatchError struct { + IP net.IP + CfgIP net.IP +} + +func (ipv4 *IPv4AddrNoMatchError) Error() string { + return fmt.Sprintf("bridge IPv4 (%s) does not match requested configuration %s", ipv4.IP, ipv4.CfgIP) +} + +// BadRequest denotes the type of this error +func (ipv4 *IPv4AddrNoMatchError) BadRequest() {} + +// IPv6AddrNoMatchError is returned when the bridge's IPv6 address does not match configured. +type IPv6AddrNoMatchError net.IPNet + +func (ipv6 *IPv6AddrNoMatchError) Error() string { + return fmt.Sprintf("bridge IPv6 addresses do not match the expected bridge configuration %s", (*net.IPNet)(ipv6).String()) +} + +// BadRequest denotes the type of this error +func (ipv6 *IPv6AddrNoMatchError) BadRequest() {} + +// InvalidLinkIPAddrError is returned when a link is configured to a container with an invalid ip address +type InvalidLinkIPAddrError string + +func (address InvalidLinkIPAddrError) Error() string { + return fmt.Sprintf("Cannot link to a container with Invalid IP Address '%s'", string(address)) +} + +// BadRequest denotes the type of this error +func (address InvalidLinkIPAddrError) BadRequest() {} diff --git a/libnetwork/drivers/bridge/interface.go b/libnetwork/drivers/bridge/interface.go new file mode 100644 index 0000000000000..1c53e38d9d640 --- /dev/null +++ b/libnetwork/drivers/bridge/interface.go @@ -0,0 +1,89 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "fmt" + "net" + + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +const ( + // DefaultBridgeName is the default name for the bridge interface managed + // by the driver when unspecified by the caller. + DefaultBridgeName = "docker0" +) + +// Interface models the bridge network device. +type bridgeInterface struct { + Link netlink.Link + bridgeIPv4 *net.IPNet + bridgeIPv6 *net.IPNet + gatewayIPv4 net.IP + gatewayIPv6 net.IP + nlh *netlink.Handle +} + +// newInterface creates a new bridge interface structure. It attempts to find +// an already existing device identified by the configuration BridgeName field, +// or the default bridge name when unspecified, but doesn't attempt to create +// one when missing +func newInterface(nlh *netlink.Handle, config *networkConfiguration) (*bridgeInterface, error) { + var err error + i := &bridgeInterface{nlh: nlh} + + // Initialize the bridge name to the default if unspecified. + if config.BridgeName == "" { + config.BridgeName = DefaultBridgeName + } + + // Attempt to find an existing bridge named with the specified name. + i.Link, err = nlh.LinkByName(config.BridgeName) + if err != nil { + logrus.Debugf("Did not find any interface with name %s: %v", config.BridgeName, err) + } else if _, ok := i.Link.(*netlink.Bridge); !ok { + return nil, fmt.Errorf("existing interface %s is not a bridge", i.Link.Attrs().Name) + } + return i, nil +} + +// exists indicates if the existing bridge interface exists on the system. +func (i *bridgeInterface) exists() bool { + return i.Link != nil +} + +// addresses returns all IPv4 addresses and all IPv6 addresses for the bridge interface. +func (i *bridgeInterface) addresses() ([]netlink.Addr, []netlink.Addr, error) { + v4addr, err := i.nlh.AddrList(i.Link, netlink.FAMILY_V4) + if err != nil { + return nil, nil, fmt.Errorf("Failed to retrieve V4 addresses: %v", err) + } + + v6addr, err := i.nlh.AddrList(i.Link, netlink.FAMILY_V6) + if err != nil { + return nil, nil, fmt.Errorf("Failed to retrieve V6 addresses: %v", err) + } + + if len(v4addr) == 0 { + return nil, v6addr, nil + } + return v4addr, v6addr, nil +} + +func (i *bridgeInterface) programIPv6Address() error { + _, nlAddressList, err := i.addresses() + if err != nil { + return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: fmt.Errorf("failed to retrieve address list: %v", err)} + } + nlAddr := netlink.Addr{IPNet: i.bridgeIPv6} + if findIPv6Address(nlAddr, nlAddressList) { + return nil + } + if err := i.nlh.AddrAdd(i.Link, &nlAddr); err != nil { + return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: err} + } + return nil +} diff --git a/libnetwork/drivers/bridge/interface_test.go b/libnetwork/drivers/bridge/interface_test.go new file mode 100644 index 0000000000000..0d4e58bd2e717 --- /dev/null +++ b/libnetwork/drivers/bridge/interface_test.go @@ -0,0 +1,53 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "testing" + + "github.com/docker/docker/libnetwork/testutils" + "github.com/vishvananda/netlink" +) + +func TestInterfaceDefaultName(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + config := &networkConfiguration{} + _, err = newInterface(nh, config) + if err != nil { + t.Fatalf("newInterface() failed: %v", err) + } + + if config.BridgeName != DefaultBridgeName { + t.Fatalf("Expected default interface name %q, got %q", DefaultBridgeName, config.BridgeName) + } +} + +func TestAddressesEmptyInterface(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + inf, err := newInterface(nh, &networkConfiguration{}) + if err != nil { + t.Fatalf("newInterface() failed: %v", err) + } + + addrsv4, addrsv6, err := inf.addresses() + if err != nil { + t.Fatalf("Failed to get addresses of default interface: %v", err) + } + if len(addrsv4) != 0 { + t.Fatalf("Default interface has unexpected IPv4: %s", addrsv4) + } + if len(addrsv6) != 0 { + t.Fatalf("Default interface has unexpected IPv6: %v", addrsv6) + } +} diff --git a/libnetwork/drivers/bridge/labels.go b/libnetwork/drivers/bridge/labels.go new file mode 100644 index 0000000000000..b938a75477850 --- /dev/null +++ b/libnetwork/drivers/bridge/labels.go @@ -0,0 +1,21 @@ +package bridge + +const ( + // BridgeName label for bridge driver + BridgeName = "com.docker.network.bridge.name" + + // EnableIPMasquerade label for bridge driver + EnableIPMasquerade = "com.docker.network.bridge.enable_ip_masquerade" + + // EnableICC label + EnableICC = "com.docker.network.bridge.enable_icc" + + // InhibitIPv4 label + InhibitIPv4 = "com.docker.network.bridge.inhibit_ipv4" + + // DefaultBindingIP label + DefaultBindingIP = "com.docker.network.bridge.host_binding_ipv4" + + // DefaultBridge label + DefaultBridge = "com.docker.network.bridge.default_bridge" +) diff --git a/libnetwork/drivers/bridge/link.go b/libnetwork/drivers/bridge/link.go new file mode 100644 index 0000000000000..7be183975af1c --- /dev/null +++ b/libnetwork/drivers/bridge/link.go @@ -0,0 +1,88 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "fmt" + "net" + + "github.com/docker/docker/libnetwork/iptables" + "github.com/docker/docker/libnetwork/types" + "github.com/sirupsen/logrus" +) + +type link struct { + parentIP string + childIP string + ports []types.TransportPort + bridge string +} + +func (l *link) String() string { + return fmt.Sprintf("%s <-> %s [%v] on %s", l.parentIP, l.childIP, l.ports, l.bridge) +} + +func newLink(parentIP, childIP string, ports []types.TransportPort, bridge string) *link { + return &link{ + childIP: childIP, + parentIP: parentIP, + ports: ports, + bridge: bridge, + } + +} + +func (l *link) Enable() error { + // -A == iptables append flag + linkFunction := func() error { + return linkContainers("-A", l.parentIP, l.childIP, l.ports, l.bridge, false) + } + + iptables.OnReloaded(func() { linkFunction() }) + return linkFunction() +} + +func (l *link) Disable() { + // -D == iptables delete flag + err := linkContainers("-D", l.parentIP, l.childIP, l.ports, l.bridge, true) + if err != nil { + logrus.Errorf("Error removing IPTables rules for a link %s due to %s", l.String(), err.Error()) + } + // Return proper error once we move to use a proper iptables package + // that returns typed errors +} + +func linkContainers(action, parentIP, childIP string, ports []types.TransportPort, bridge string, + ignoreErrors bool) error { + var nfAction iptables.Action + + switch action { + case "-A": + nfAction = iptables.Append + case "-I": + nfAction = iptables.Insert + case "-D": + nfAction = iptables.Delete + default: + return InvalidIPTablesCfgError(action) + } + + ip1 := net.ParseIP(parentIP) + if ip1 == nil { + return InvalidLinkIPAddrError(parentIP) + } + ip2 := net.ParseIP(childIP) + if ip2 == nil { + return InvalidLinkIPAddrError(childIP) + } + + chain := iptables.ChainInfo{Name: DockerChain} + for _, port := range ports { + err := chain.Link(nfAction, ip1, ip2, int(port.Port), port.Proto.String(), bridge) + if !ignoreErrors && err != nil { + return err + } + } + return nil +} diff --git a/libnetwork/drivers/bridge/link_test.go b/libnetwork/drivers/bridge/link_test.go new file mode 100644 index 0000000000000..3450292abd636 --- /dev/null +++ b/libnetwork/drivers/bridge/link_test.go @@ -0,0 +1,42 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "testing" + + "github.com/docker/docker/libnetwork/types" +) + +func getPorts() []types.TransportPort { + return []types.TransportPort{ + {Proto: types.TCP, Port: uint16(5000)}, + {Proto: types.UDP, Port: uint16(400)}, + {Proto: types.TCP, Port: uint16(600)}, + } +} + +func TestLinkNew(t *testing.T) { + ports := getPorts() + + link := newLink("172.0.17.3", "172.0.17.2", ports, "docker0") + + if link == nil { + t.FailNow() + } + if link.parentIP != "172.0.17.3" { + t.Fail() + } + if link.childIP != "172.0.17.2" { + t.Fail() + } + for i, p := range link.ports { + if p != ports[i] { + t.Fail() + } + } + if link.bridge != "docker0" { + t.Fail() + } +} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_linux.go b/libnetwork/drivers/bridge/netlink_deprecated_linux.go similarity index 89% rename from vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_linux.go rename to libnetwork/drivers/bridge/netlink_deprecated_linux.go index 6b49efa166648..fd25c9f376aa3 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_linux.go +++ b/libnetwork/drivers/bridge/netlink_deprecated_linux.go @@ -2,13 +2,9 @@ package bridge import ( "fmt" - "math/rand" "net" "syscall" - "time" "unsafe" - - "github.com/docker/libnetwork/netutils" ) const ( @@ -27,8 +23,6 @@ type ifreqHwaddr struct { IfruHwaddr syscall.RawSockaddr } -var rnd = rand.New(rand.NewSource(time.Now().UnixNano())) - // THIS CODE DOES NOT COMMUNICATE WITH KERNEL VIA RTNETLINK INTERFACE // IT IS HERE FOR BACKWARDS COMPATIBILITY WITH OLDER LINUX KERNELS // WHICH SHIP WITH OLDER NOT ENTIRELY FUNCTIONAL VERSION OF NETLINK @@ -106,7 +100,7 @@ func ioctlSetMacAddress(name, addr string) error { return nil } -func ioctlCreateBridge(name string, setMacAddr bool) error { +func ioctlCreateBridge(name, macAddr string) error { if len(name) >= ifNameSize { return fmt.Errorf("Interface name %s too long", name) } @@ -124,8 +118,5 @@ func ioctlCreateBridge(name string, setMacAddr bool) error { if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(s), ioctlBrAdd, uintptr(unsafe.Pointer(nameBytePtr))); err != 0 { return err } - if setMacAddr { - return ioctlSetMacAddress(name, netutils.GenerateRandomMAC().String()) - } - return nil + return ioctlSetMacAddress(name, macAddr) } diff --git a/libnetwork/drivers/bridge/netlink_deprecated_linux_rawsockaddr_data_int8.go b/libnetwork/drivers/bridge/netlink_deprecated_linux_rawsockaddr_data_int8.go new file mode 100644 index 0000000000000..5a0763efdf7c3 --- /dev/null +++ b/libnetwork/drivers/bridge/netlink_deprecated_linux_rawsockaddr_data_int8.go @@ -0,0 +1,8 @@ +//go:build !arm && !ppc64 && !ppc64le && !riscv64 +// +build !arm,!ppc64,!ppc64le,!riscv64 + +package bridge + +func ifrDataByte(b byte) int8 { + return int8(b) +} diff --git a/libnetwork/drivers/bridge/netlink_deprecated_linux_rawsockaddr_data_uint8.go b/libnetwork/drivers/bridge/netlink_deprecated_linux_rawsockaddr_data_uint8.go new file mode 100644 index 0000000000000..e177146077608 --- /dev/null +++ b/libnetwork/drivers/bridge/netlink_deprecated_linux_rawsockaddr_data_uint8.go @@ -0,0 +1,8 @@ +//go:build arm || ppc64 || ppc64le || riscv64 +// +build arm ppc64 ppc64le riscv64 + +package bridge + +func ifrDataByte(b byte) uint8 { + return uint8(b) +} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_unsupported.go b/libnetwork/drivers/bridge/netlink_deprecated_unsupported.go similarity index 95% rename from vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_unsupported.go rename to libnetwork/drivers/bridge/netlink_deprecated_unsupported.go index 7e2d57b660eee..d4d34bef4451d 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_unsupported.go +++ b/libnetwork/drivers/bridge/netlink_deprecated_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package bridge diff --git a/libnetwork/drivers/bridge/network_test.go b/libnetwork/drivers/bridge/network_test.go new file mode 100644 index 0000000000000..0c4525a56373e --- /dev/null +++ b/libnetwork/drivers/bridge/network_test.go @@ -0,0 +1,220 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "testing" + + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/testutils" + "github.com/vishvananda/netlink" +) + +func TestLinkCreate(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + d := newDriver() + + if err := d.configure(nil); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + mtu := 1490 + config := &networkConfiguration{ + BridgeName: DefaultBridgeName, + Mtu: mtu, + EnableIPv6: true, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + ipdList := getIPv4Data(t, "") + err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te := newTestEndpoint(ipdList[0].Pool, 10) + err = d.CreateEndpoint("dummy", "", te.Interface(), nil) + if err != nil { + if _, ok := err.(InvalidEndpointIDError); !ok { + t.Fatalf("Failed with a wrong error :%s", err.Error()) + } + } else { + t.Fatal("Failed to detect invalid config") + } + + // Good endpoint creation + err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil) + if err != nil { + t.Fatalf("Failed to create a link: %s", err.Error()) + } + + err = d.Join("dummy", "ep", "sbox", te, nil) + if err != nil { + t.Fatalf("Failed to create a link: %s", err.Error()) + } + + // Verify sbox endpoint interface inherited MTU value from bridge config + sboxLnk, err := netlink.LinkByName(te.iface.srcName) + if err != nil { + t.Fatal(err) + } + if mtu != sboxLnk.Attrs().MTU { + t.Fatal("Sandbox endpoint interface did not inherit bridge interface MTU config") + } + // TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName + // then we could check the MTU on hostLnk as well. + + te1 := newTestEndpoint(ipdList[0].Pool, 11) + err = d.CreateEndpoint("dummy", "ep", te1.Interface(), nil) + if err == nil { + t.Fatal("Failed to detect duplicate endpoint id on same network") + } + + if te.iface.dstName == "" { + t.Fatal("Invalid Dstname returned") + } + + _, err = netlink.LinkByName(te.iface.srcName) + if err != nil { + t.Fatalf("Could not find source link %s: %v", te.iface.srcName, err) + } + + n, ok := d.networks["dummy"] + if !ok { + t.Fatalf("Cannot find network %s inside driver", "dummy") + } + ip := te.iface.addr.IP + if !n.bridge.bridgeIPv4.Contains(ip) { + t.Fatalf("IP %s is not a valid ip in the subnet %s", ip.String(), n.bridge.bridgeIPv4.String()) + } + + ip6 := te.iface.addrv6.IP + if !n.bridge.bridgeIPv6.Contains(ip6) { + t.Fatalf("IP %s is not a valid ip in the subnet %s", ip6.String(), bridgeIPv6.String()) + } + + if !te.gw.Equal(n.bridge.bridgeIPv4.IP) { + t.Fatalf("Invalid default gateway. Expected %s. Got %s", n.bridge.bridgeIPv4.IP.String(), + te.gw.String()) + } + + if !te.gw6.Equal(n.bridge.bridgeIPv6.IP) { + t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", n.bridge.bridgeIPv6.IP.String(), + te.gw6.String()) + } +} + +func TestLinkCreateTwo(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + d := newDriver() + + if err := d.configure(nil); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + config := &networkConfiguration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + ipdList := getIPv4Data(t, "") + err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te1 := newTestEndpoint(ipdList[0].Pool, 11) + err = d.CreateEndpoint("dummy", "ep", te1.Interface(), nil) + if err != nil { + t.Fatalf("Failed to create a link: %s", err.Error()) + } + + te2 := newTestEndpoint(ipdList[0].Pool, 12) + err = d.CreateEndpoint("dummy", "ep", te2.Interface(), nil) + if err != nil { + if _, ok := err.(driverapi.ErrEndpointExists); !ok { + t.Fatalf("Failed with a wrong error: %s", err.Error()) + } + } else { + t.Fatal("Expected to fail while trying to add same endpoint twice") + } +} + +func TestLinkCreateNoEnableIPv6(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + d := newDriver() + + if err := d.configure(nil); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + config := &networkConfiguration{ + BridgeName: DefaultBridgeName} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + ipdList := getIPv4Data(t, "") + err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + te := newTestEndpoint(ipdList[0].Pool, 30) + err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil) + if err != nil { + t.Fatalf("Failed to create a link: %s", err.Error()) + } + + iface := te.iface + if iface.addrv6 != nil && iface.addrv6.IP.To16() != nil { + t.Fatalf("Expected IPv6 address to be nil when IPv6 is not enabled. Got IPv6 = %s", iface.addrv6.String()) + } + + if te.gw6.To16() != nil { + t.Fatalf("Expected GatewayIPv6 to be nil when IPv6 is not enabled. Got GatewayIPv6 = %s", te.gw6.String()) + } +} + +func TestLinkDelete(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + d := newDriver() + + if err := d.configure(nil); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + config := &networkConfiguration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + ipdList := getIPv4Data(t, "") + err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te := newTestEndpoint(ipdList[0].Pool, 30) + err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil) + if err != nil { + t.Fatalf("Failed to create a link: %s", err.Error()) + } + + err = d.DeleteEndpoint("dummy", "") + if err != nil { + if _, ok := err.(InvalidEndpointIDError); !ok { + t.Fatalf("Failed with a wrong error :%s", err.Error()) + } + } else { + t.Fatal("Failed to detect invalid config") + } + + err = d.DeleteEndpoint("dummy", "ep1") + if err != nil { + t.Fatal(err) + } +} diff --git a/libnetwork/drivers/bridge/port_mapping.go b/libnetwork/drivers/bridge/port_mapping.go new file mode 100644 index 0000000000000..afff4b404585c --- /dev/null +++ b/libnetwork/drivers/bridge/port_mapping.go @@ -0,0 +1,247 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "bytes" + "errors" + "fmt" + "net" + "sync" + + "github.com/docker/docker/libnetwork/types" + "github.com/ishidawataru/sctp" + "github.com/sirupsen/logrus" +) + +func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { + if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil { + return nil, nil + } + + defHostIP := net.IPv4zero // 0.0.0.0 + if reqDefBindIP != nil { + defHostIP = reqDefBindIP + } + + var containerIPv6 net.IP + if ep.addrv6 != nil { + containerIPv6 = ep.addrv6.IP + } + + pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, containerIPv6, defHostIP, ulPxyEnabled) + if err != nil { + return nil, err + } + return pb, nil +} + +func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIPv4, containerIPv6, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { + bs := make([]types.PortBinding, 0, len(bindings)) + for _, c := range bindings { + bIPv4 := c.GetCopy() + bIPv6 := c.GetCopy() + // Allocate IPv4 Port mappings + if ok := n.validatePortBindingIPv4(&bIPv4, containerIPv4, defHostIP); ok { + if err := n.allocatePort(&bIPv4, ulPxyEnabled); err != nil { + // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message + if cuErr := n.releasePortsInternal(bs); cuErr != nil { + logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv4 port bindings: %v", bIPv4, cuErr) + } + return nil, err + } + bs = append(bs, bIPv4) + } + + // skip adding implicit v6 addr, when the kernel was booted with `ipv6.disable=1` + // https://github.com/moby/moby/issues/42288 + isV6Binding := c.HostIP != nil && c.HostIP.To4() == nil + if !isV6Binding && !IsV6Listenable() { + continue + } + + // Allocate IPv6 Port mappings + // If the container has no IPv6 address, allow proxying host IPv6 traffic to it + // by setting up the binding with the IPv4 interface if the userland proxy is enabled + // This change was added to keep backward compatibility + containerIP := containerIPv6 + if ulPxyEnabled && (containerIPv6 == nil) { + containerIP = containerIPv4 + } + if ok := n.validatePortBindingIPv6(&bIPv6, containerIP, defHostIP); ok { + if err := n.allocatePort(&bIPv6, ulPxyEnabled); err != nil { + // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message + if cuErr := n.releasePortsInternal(bs); cuErr != nil { + logrus.Warnf("allocation failure for %v, failed to clear previously allocated ipv6 port bindings: %v", bIPv6, cuErr) + } + return nil, err + } + bs = append(bs, bIPv6) + } + } + return bs, nil +} + +// validatePortBindingIPv4 validates the port binding, populates the missing Host IP field and returns true +// if this is a valid IPv4 binding, else returns false +func (n *bridgeNetwork) validatePortBindingIPv4(bnd *types.PortBinding, containerIPv4, defHostIP net.IP) bool { + //Return early if there is a valid Host IP, but its not a IPv4 address + if len(bnd.HostIP) > 0 && bnd.HostIP.To4() == nil { + return false + } + // Adjust the host address in the operational binding + if len(bnd.HostIP) == 0 { + // Return early if the default binding address is an IPv6 address + if defHostIP.To4() == nil { + return false + } + bnd.HostIP = defHostIP + } + bnd.IP = containerIPv4 + return true + +} + +// validatePortBindingIPv6 validates the port binding, populates the missing Host IP field and returns true +// if this is a valid IPv6 binding, else returns false +func (n *bridgeNetwork) validatePortBindingIPv6(bnd *types.PortBinding, containerIP, defHostIP net.IP) bool { + // Return early if there is no container endpoint + if containerIP == nil { + return false + } + // Return early if there is a valid Host IP, which is a IPv4 address + if len(bnd.HostIP) > 0 && bnd.HostIP.To4() != nil { + return false + } + + // Setup a binding to "::" if Host IP is empty and the default binding IP is 0.0.0.0 + if len(bnd.HostIP) == 0 { + if defHostIP.Equal(net.IPv4zero) { + bnd.HostIP = net.IPv6zero + // If the default binding IP is an IPv6 address, use it + } else if defHostIP.To4() == nil { + bnd.HostIP = defHostIP + // Return false if default binding ip is an IPv4 address + } else { + return false + } + } + bnd.IP = containerIP + return true +} + +func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, ulPxyEnabled bool) error { + var ( + host net.Addr + err error + ) + + // Adjust HostPortEnd if this is not a range. + if bnd.HostPortEnd == 0 { + bnd.HostPortEnd = bnd.HostPort + } + + // Construct the container side transport address + container, err := bnd.ContainerAddr() + if err != nil { + return err + } + + portmapper := n.portMapper + + if bnd.HostIP.To4() == nil { + portmapper = n.portMapperV6 + } + + // Try up to maxAllocatePortAttempts times to get a port that's not already allocated. + for i := 0; i < maxAllocatePortAttempts; i++ { + if host, err = portmapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), ulPxyEnabled); err == nil { + break + } + // There is no point in immediately retrying to map an explicitly chosen port. + if bnd.HostPort != 0 { + logrus.Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err) + break + } + logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1) + } + if err != nil { + return err + } + + // Save the host port (regardless it was or not specified in the binding) + switch netAddr := host.(type) { + case *net.TCPAddr: + bnd.HostPort = uint16(host.(*net.TCPAddr).Port) + return nil + case *net.UDPAddr: + bnd.HostPort = uint16(host.(*net.UDPAddr).Port) + return nil + case *sctp.SCTPAddr: + bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port) + return nil + default: + // For completeness + return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr)) + } +} + +func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error { + return n.releasePortsInternal(ep.portMapping) +} + +func (n *bridgeNetwork) releasePortsInternal(bindings []types.PortBinding) error { + var errorBuf bytes.Buffer + + // Attempt to release all port bindings, do not stop on failure + for _, m := range bindings { + if err := n.releasePort(m); err != nil { + errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err)) + } + } + + if errorBuf.Len() != 0 { + return errors.New(errorBuf.String()) + } + return nil +} + +func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error { + // Construct the host side transport address + host, err := bnd.HostAddr() + if err != nil { + return err + } + + portmapper := n.portMapper + + if bnd.HostIP.To4() == nil { + portmapper = n.portMapperV6 + } + + return portmapper.Unmap(host) +} + +var ( + v6ListenableCached bool + v6ListenableOnce sync.Once +) + +// IsV6Listenable returns true when `[::1]:0` is listenable. +// IsV6Listenable returns false mostly when the kernel was booted with `ipv6.disable=1` option. +func IsV6Listenable() bool { + v6ListenableOnce.Do(func() { + ln, err := net.Listen("tcp6", "[::1]:0") + if err != nil { + // When the kernel was booted with `ipv6.disable=1`, + // we get err "listen tcp6 [::1]:0: socket: address family not supported by protocol" + // https://github.com/moby/moby/issues/42288 + logrus.Debugf("port_mapping: v6Listenable=false (%v)", err) + } else { + v6ListenableCached = true + ln.Close() + } + }) + return v6ListenableCached +} diff --git a/libnetwork/drivers/bridge/port_mapping_test.go b/libnetwork/drivers/bridge/port_mapping_test.go new file mode 100644 index 0000000000000..dab375e96b96a --- /dev/null +++ b/libnetwork/drivers/bridge/port_mapping_test.go @@ -0,0 +1,185 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "os" + "testing" + + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/testutils" + "github.com/docker/docker/libnetwork/types" + "github.com/docker/docker/pkg/reexec" +) + +func TestMain(m *testing.M) { + if reexec.Init() { + return + } + os.Exit(m.Run()) +} + +func TestPortMappingConfig(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + d := newDriver() + + config := &configuration{ + EnableIPTables: true, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + if err := d.configure(genericOption); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + binding1 := types.PortBinding{Proto: types.UDP, Port: uint16(400), HostPort: uint16(54000)} + binding2 := types.PortBinding{Proto: types.TCP, Port: uint16(500), HostPort: uint16(65000)} + binding3 := types.PortBinding{Proto: types.SCTP, Port: uint16(300), HostPort: uint16(65000)} + portBindings := []types.PortBinding{binding1, binding2, binding3} + + sbOptions := make(map[string]interface{}) + sbOptions[netlabel.PortMap] = portBindings + + netConfig := &networkConfiguration{ + BridgeName: DefaultBridgeName, + } + netOptions := make(map[string]interface{}) + netOptions[netlabel.GenericData] = netConfig + + ipdList := getIPv4Data(t, "") + err := d.CreateNetwork("dummy", netOptions, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te := newTestEndpoint(ipdList[0].Pool, 11) + err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil) + if err != nil { + t.Fatalf("Failed to create the endpoint: %s", err.Error()) + } + + if err = d.Join("dummy", "ep1", "sbox", te, sbOptions); err != nil { + t.Fatalf("Failed to join the endpoint: %v", err) + } + + if err = d.ProgramExternalConnectivity("dummy", "ep1", sbOptions); err != nil { + t.Fatalf("Failed to program external connectivity: %v", err) + } + + network, ok := d.networks["dummy"] + if !ok { + t.Fatalf("Cannot find network %s inside driver", "dummy") + } + ep := network.endpoints["ep1"] + if len(ep.portMapping) != 3 { + t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping) + } + if ep.portMapping[0].Proto != binding1.Proto || ep.portMapping[0].Port != binding1.Port || + ep.portMapping[1].Proto != binding2.Proto || ep.portMapping[1].Port != binding2.Port || + ep.portMapping[2].Proto != binding3.Proto || ep.portMapping[2].Port != binding3.Port { + t.Fatal("bridgeEndpoint has incorrect port mapping values") + } + if ep.portMapping[0].HostIP == nil || ep.portMapping[0].HostPort == 0 || + ep.portMapping[1].HostIP == nil || ep.portMapping[1].HostPort == 0 || + ep.portMapping[2].HostIP == nil || ep.portMapping[2].HostPort == 0 { + t.Fatal("operational port mapping data not found on bridgeEndpoint") + } + + // release host mapped ports + err = d.Leave("dummy", "ep1") + if err != nil { + t.Fatal(err) + } + + err = d.RevokeExternalConnectivity("dummy", "ep1") + if err != nil { + t.Fatal(err) + } +} + +func TestPortMappingV6Config(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + if err := loopbackUp(); err != nil { + t.Fatalf("Could not bring loopback iface up: %v", err) + } + + d := newDriver() + + config := &configuration{ + EnableIPTables: true, + EnableIP6Tables: true, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = config + + if err := d.configure(genericOption); err != nil { + t.Fatalf("Failed to setup driver config: %v", err) + } + + portBindings := []types.PortBinding{ + {Proto: types.UDP, Port: uint16(400), HostPort: uint16(54000)}, + {Proto: types.TCP, Port: uint16(500), HostPort: uint16(65000)}, + {Proto: types.SCTP, Port: uint16(500), HostPort: uint16(65000)}, + } + + sbOptions := make(map[string]interface{}) + sbOptions[netlabel.PortMap] = portBindings + netConfig := &networkConfiguration{ + BridgeName: DefaultBridgeName, + EnableIPv6: true, + } + netOptions := make(map[string]interface{}) + netOptions[netlabel.GenericData] = netConfig + + ipdList := getIPv4Data(t, "") + err := d.CreateNetwork("dummy", netOptions, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + + te := newTestEndpoint(ipdList[0].Pool, 11) + err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil) + if err != nil { + t.Fatalf("Failed to create the endpoint: %s", err.Error()) + } + + if err = d.Join("dummy", "ep1", "sbox", te, sbOptions); err != nil { + t.Fatalf("Failed to join the endpoint: %v", err) + } + + if err = d.ProgramExternalConnectivity("dummy", "ep1", sbOptions); err != nil { + t.Fatalf("Failed to program external connectivity: %v", err) + } + + network, ok := d.networks["dummy"] + if !ok { + t.Fatalf("Cannot find network %s inside driver", "dummy") + } + ep := network.endpoints["ep1"] + if len(ep.portMapping) != 6 { + t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping) + } + + // release host mapped ports + err = d.Leave("dummy", "ep1") + if err != nil { + t.Fatal(err) + } + + err = d.RevokeExternalConnectivity("dummy", "ep1") + if err != nil { + t.Fatal(err) + } +} + +func loopbackUp() error { + nlHandle := ns.NlHandle() + iface, err := nlHandle.LinkByName("lo") + if err != nil { + return err + } + return nlHandle.LinkSetUp(iface) +} diff --git a/libnetwork/drivers/bridge/setup.go b/libnetwork/drivers/bridge/setup.go new file mode 100644 index 0000000000000..aa6b1fe9fbdfe --- /dev/null +++ b/libnetwork/drivers/bridge/setup.go @@ -0,0 +1,29 @@ +//go:build linux +// +build linux + +package bridge + +type setupStep func(*networkConfiguration, *bridgeInterface) error + +type bridgeSetup struct { + config *networkConfiguration + bridge *bridgeInterface + steps []setupStep +} + +func newBridgeSetup(c *networkConfiguration, i *bridgeInterface) *bridgeSetup { + return &bridgeSetup{config: c, bridge: i} +} + +func (b *bridgeSetup) apply() error { + for _, fn := range b.steps { + if err := fn(b.config, b.bridge); err != nil { + return err + } + } + return nil +} + +func (b *bridgeSetup) queueStep(step setupStep) { + b.steps = append(b.steps, step) +} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go b/libnetwork/drivers/bridge/setup_bridgenetfiltering.go similarity index 97% rename from vendor/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go rename to libnetwork/drivers/bridge/setup_bridgenetfiltering.go index 9b90acfac2c12..755a00a7797e8 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go +++ b/libnetwork/drivers/bridge/setup_bridgenetfiltering.go @@ -1,9 +1,11 @@ +//go:build linux +// +build linux + package bridge import ( "errors" "fmt" - "io/ioutil" "os" "syscall" @@ -122,7 +124,7 @@ func getBridgeNFKernelParam(ipVer ipVersion) string { //Gets the value of the kernel parameters located at the given path func getKernelBoolParam(path string) (bool, error) { enabled := false - line, err := ioutil.ReadFile(path) + line, err := os.ReadFile(path) if err != nil { return false, err } @@ -138,7 +140,7 @@ func setKernelBoolParam(path string, on bool) error { if on { value = byte('1') } - return ioutil.WriteFile(path, []byte{value, '\n'}, 0644) + return os.WriteFile(path, []byte{value, '\n'}, 0644) } //Checks to see if packet forwarding is enabled diff --git a/libnetwork/drivers/bridge/setup_bridgenetfiltering_test.go b/libnetwork/drivers/bridge/setup_bridgenetfiltering_test.go new file mode 100644 index 0000000000000..67290bde892d6 --- /dev/null +++ b/libnetwork/drivers/bridge/setup_bridgenetfiltering_test.go @@ -0,0 +1,15 @@ +//go:build linux +// +build linux + +package bridge + +import "testing" + +func TestIPConstantValues(t *testing.T) { + if ipv4|ipv6 != ipvboth { + t.Fatalf("bitwise or of ipv4(%04b) and ipv6(%04b) must yield ipvboth(%04b)", ipv4, ipv6, ipvboth) + } + if ipvboth&(^(ipv4 | ipv6)) != ipvnone { + t.Fatalf("ipvboth(%04b) with unset ipv4(%04b) and ipv6(%04b) bits shall equal to ipvnone", ipvboth, ipv4, ipv6) + } +} diff --git a/libnetwork/drivers/bridge/setup_device.go b/libnetwork/drivers/bridge/setup_device.go new file mode 100644 index 0000000000000..a9d9bc1fdfc6c --- /dev/null +++ b/libnetwork/drivers/bridge/setup_device.go @@ -0,0 +1,75 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/docker/docker/libnetwork/netutils" + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +// SetupDevice create a new bridge interface/ +func setupDevice(config *networkConfiguration, i *bridgeInterface) error { + // We only attempt to create the bridge when the requested device name is + // the default one. + if config.BridgeName != DefaultBridgeName && config.DefaultBridge { + return NonDefaultBridgeExistError(config.BridgeName) + } + + // Set the bridgeInterface netlink.Bridge. + i.Link = &netlink.Bridge{ + LinkAttrs: netlink.LinkAttrs{ + Name: config.BridgeName, + }, + } + + // Set the bridge's MAC address. Requires kernel version 3.3 or up. + hwAddr := netutils.GenerateRandomMAC() + i.Link.Attrs().HardwareAddr = hwAddr + logrus.Debugf("Setting bridge mac address to %s", hwAddr) + + if err := i.nlh.LinkAdd(i.Link); err != nil { + logrus.Debugf("Failed to create bridge %s via netlink. Trying ioctl", config.BridgeName) + return ioctlCreateBridge(config.BridgeName, hwAddr.String()) + } + + return nil +} + +func setupDefaultSysctl(config *networkConfiguration, i *bridgeInterface) error { + // Disable IPv6 router advertisements originating on the bridge + sysPath := filepath.Join("/proc/sys/net/ipv6/conf/", config.BridgeName, "accept_ra") + if _, err := os.Stat(sysPath); err != nil { + logrus. + WithField("bridge", config.BridgeName). + WithField("syspath", sysPath). + Info("failed to read ipv6 net.ipv6.conf..accept_ra") + return nil + } + if err := os.WriteFile(sysPath, []byte{'0', '\n'}, 0644); err != nil { + logrus.WithError(err).Warn("unable to disable IPv6 router advertisement") + } + return nil +} + +// SetupDeviceUp ups the given bridge interface. +func setupDeviceUp(config *networkConfiguration, i *bridgeInterface) error { + err := i.nlh.LinkSetUp(i.Link) + if err != nil { + return fmt.Errorf("Failed to set link up for %s: %v", config.BridgeName, err) + } + + // Attempt to update the bridge interface to refresh the flags status, + // ignoring any failure to do so. + if lnk, err := i.nlh.LinkByName(config.BridgeName); err == nil { + i.Link = lnk + } else { + logrus.Warnf("Failed to retrieve link for interface (%s): %v", config.BridgeName, err) + } + return nil +} diff --git a/libnetwork/drivers/bridge/setup_device_test.go b/libnetwork/drivers/bridge/setup_device_test.go new file mode 100644 index 0000000000000..7526fda6fbb8a --- /dev/null +++ b/libnetwork/drivers/bridge/setup_device_test.go @@ -0,0 +1,97 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "bytes" + "net" + "testing" + + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/testutils" + "github.com/vishvananda/netlink" +) + +func TestSetupNewBridge(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + config := &networkConfiguration{BridgeName: DefaultBridgeName} + br := &bridgeInterface{nlh: nh} + + if err := setupDevice(config, br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + if br.Link == nil { + t.Fatal("bridgeInterface link is nil (expected valid link)") + } + if _, err := nh.LinkByName(DefaultBridgeName); err != nil { + t.Fatalf("Failed to retrieve bridge device: %v", err) + } + if br.Link.Attrs().Flags&net.FlagUp == net.FlagUp { + t.Fatal("bridgeInterface should be created down") + } +} + +func TestSetupNewNonDefaultBridge(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + config := &networkConfiguration{BridgeName: "test0", DefaultBridge: true} + br := &bridgeInterface{nlh: nh} + + err = setupDevice(config, br) + if err == nil { + t.Fatal("Expected bridge creation failure with \"non default name\", succeeded") + } + + if _, ok := err.(NonDefaultBridgeExistError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestSetupDeviceUp(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + config := &networkConfiguration{BridgeName: DefaultBridgeName} + br := &bridgeInterface{nlh: nh} + + if err := setupDevice(config, br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + if err := setupDeviceUp(config, br); err != nil { + t.Fatalf("Failed to up bridge device: %v", err) + } + + lnk, _ := nh.LinkByName(DefaultBridgeName) + if lnk.Attrs().Flags&net.FlagUp != net.FlagUp { + t.Fatal("bridgeInterface should be up") + } +} + +func TestGenerateRandomMAC(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + mac1 := netutils.GenerateRandomMAC() + mac2 := netutils.GenerateRandomMAC() + if bytes.Equal(mac1, mac2) { + t.Fatalf("Generated twice the same MAC address %v", mac1) + } +} diff --git a/libnetwork/drivers/bridge/setup_firewalld.go b/libnetwork/drivers/bridge/setup_firewalld.go new file mode 100644 index 0000000000000..b0a1ebcaf99c8 --- /dev/null +++ b/libnetwork/drivers/bridge/setup_firewalld.go @@ -0,0 +1,38 @@ +//go:build linux +// +build linux + +package bridge + +import "github.com/docker/docker/libnetwork/iptables" + +func (n *bridgeNetwork) setupFirewalld(config *networkConfiguration, i *bridgeInterface) error { + d := n.driver + d.Lock() + driverConfig := d.config + d.Unlock() + + // Sanity check. + if !driverConfig.EnableIPTables { + return IPTableCfgError(config.BridgeName) + } + + iptables.OnReloaded(func() { n.setupIP4Tables(config, i) }) + iptables.OnReloaded(n.portMapper.ReMapAll) + return nil +} + +func (n *bridgeNetwork) setupFirewalld6(config *networkConfiguration, i *bridgeInterface) error { + d := n.driver + d.Lock() + driverConfig := d.config + d.Unlock() + + // Sanity check. + if !driverConfig.EnableIP6Tables { + return IPTableCfgError(config.BridgeName) + } + + iptables.OnReloaded(func() { n.setupIP6Tables(config, i) }) + iptables.OnReloaded(n.portMapperV6.ReMapAll) + return nil +} diff --git a/libnetwork/drivers/bridge/setup_ip_forwarding.go b/libnetwork/drivers/bridge/setup_ip_forwarding.go new file mode 100644 index 0000000000000..1932b773beab6 --- /dev/null +++ b/libnetwork/drivers/bridge/setup_ip_forwarding.go @@ -0,0 +1,74 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "fmt" + "os" + + "github.com/docker/docker/libnetwork/iptables" + "github.com/sirupsen/logrus" +) + +const ( + ipv4ForwardConf = "/proc/sys/net/ipv4/ip_forward" + ipv4ForwardConfPerm = 0644 +) + +func configureIPForwarding(enable bool) error { + var val byte + if enable { + val = '1' + } + return os.WriteFile(ipv4ForwardConf, []byte{val, '\n'}, ipv4ForwardConfPerm) +} + +func setupIPForwarding(enableIPTables bool, enableIP6Tables bool) error { + // Get current IPv4 forward setup + ipv4ForwardData, err := os.ReadFile(ipv4ForwardConf) + if err != nil { + return fmt.Errorf("Cannot read IP forwarding setup: %v", err) + } + + // Enable IPv4 forwarding only if it is not already enabled + if ipv4ForwardData[0] != '1' { + // Enable IPv4 forwarding + if err := configureIPForwarding(true); err != nil { + return fmt.Errorf("Enabling IP forwarding failed: %v", err) + } + // When enabling ip_forward set the default policy on forward chain to + // drop only if the daemon option iptables is not set to false. + if enableIPTables { + iptable := iptables.GetIptable(iptables.IPv4) + if err := iptable.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil { + if err := configureIPForwarding(false); err != nil { + logrus.Errorf("Disabling IP forwarding failed, %v", err) + } + return err + } + iptables.OnReloaded(func() { + logrus.Debug("Setting the default DROP policy on firewall reload") + if err := iptable.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil { + logrus.Warnf("Setting the default DROP policy on firewall reload failed, %v", err) + } + }) + } + } + + // add only iptables rules - forwarding is handled by setupIPv6Forwarding in setup_ipv6 + if enableIP6Tables { + iptable := iptables.GetIptable(iptables.IPv6) + if err := iptable.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil { + logrus.Warnf("Setting the default DROP policy on firewall reload failed, %v", err) + } + iptables.OnReloaded(func() { + logrus.Debug("Setting the default DROP policy on firewall reload") + if err := iptable.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil { + logrus.Warnf("Setting the default DROP policy on firewall reload failed, %v", err) + } + }) + } + + return nil +} diff --git a/libnetwork/drivers/bridge/setup_ip_forwarding_test.go b/libnetwork/drivers/bridge/setup_ip_forwarding_test.go new file mode 100644 index 0000000000000..da0cd07d6292b --- /dev/null +++ b/libnetwork/drivers/bridge/setup_ip_forwarding_test.go @@ -0,0 +1,54 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "bytes" + "os" + "testing" +) + +func TestSetupIPForwarding(t *testing.T) { + // Read current setting and ensure the original value gets restored + procSetting := readCurrentIPForwardingSetting(t) + defer reconcileIPForwardingSetting(t, procSetting) + + // Disable IP Forwarding if enabled + if bytes.Equal(procSetting, []byte("1\n")) { + writeIPForwardingSetting(t, []byte{'0', '\n'}) + } + + // Set IP Forwarding + if err := setupIPForwarding(true, true); err != nil { + t.Fatalf("Failed to setup IP forwarding: %v", err) + } + + // Read new setting + procSetting = readCurrentIPForwardingSetting(t) + if !bytes.Equal(procSetting, []byte("1\n")) { + t.Fatal("Failed to effectively setup IP forwarding") + } +} + +func readCurrentIPForwardingSetting(t *testing.T) []byte { + procSetting, err := os.ReadFile(ipv4ForwardConf) + if err != nil { + t.Fatalf("Can't execute test: Failed to read current IP forwarding setting: %v", err) + } + return procSetting +} + +func writeIPForwardingSetting(t *testing.T, chars []byte) { + err := os.WriteFile(ipv4ForwardConf, chars, ipv4ForwardConfPerm) + if err != nil { + t.Fatalf("Can't execute or cleanup after test: Failed to reset IP forwarding: %v", err) + } +} + +func reconcileIPForwardingSetting(t *testing.T, original []byte) { + current := readCurrentIPForwardingSetting(t) + if !bytes.Equal(original, current) { + writeIPForwardingSetting(t, original) + } +} diff --git a/libnetwork/drivers/bridge/setup_ip_tables.go b/libnetwork/drivers/bridge/setup_ip_tables.go new file mode 100644 index 0000000000000..a9d39b30ad197 --- /dev/null +++ b/libnetwork/drivers/bridge/setup_ip_tables.go @@ -0,0 +1,432 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "errors" + "fmt" + "net" + + "github.com/docker/docker/libnetwork/iptables" + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +// DockerChain: DOCKER iptable chain name +const ( + DockerChain = "DOCKER" + // Isolation between bridge networks is achieved in two stages by means + // of the following two chains in the filter table. The first chain matches + // on the source interface being a bridge network's bridge and the + // destination being a different interface. A positive match leads to the + // second isolation chain. No match returns to the parent chain. The second + // isolation chain matches on destination interface being a bridge network's + // bridge. A positive match identifies a packet originated from one bridge + // network's bridge destined to another bridge network's bridge and will + // result in the packet being dropped. No match returns to the parent chain. + IsolationChain1 = "DOCKER-ISOLATION-STAGE-1" + IsolationChain2 = "DOCKER-ISOLATION-STAGE-2" +) + +func setupIPChains(config *configuration, version iptables.IPVersion) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) { + // Sanity check. + if !config.EnableIPTables { + return nil, nil, nil, nil, errors.New("cannot create new chains, EnableIPTable is disabled") + } + + hairpinMode := !config.EnableUserlandProxy + + iptable := iptables.GetIptable(version) + + natChain, err := iptable.NewChain(DockerChain, iptables.Nat, hairpinMode) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("failed to create NAT chain %s: %v", DockerChain, err) + } + defer func() { + if err != nil { + if err := iptable.RemoveExistingChain(DockerChain, iptables.Nat); err != nil { + logrus.Warnf("failed on removing iptables NAT chain %s on cleanup: %v", DockerChain, err) + } + } + }() + + filterChain, err := iptable.NewChain(DockerChain, iptables.Filter, false) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER chain %s: %v", DockerChain, err) + } + defer func() { + if err != nil { + if err := iptable.RemoveExistingChain(DockerChain, iptables.Filter); err != nil { + logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", DockerChain, err) + } + } + }() + + isolationChain1, err := iptable.NewChain(IsolationChain1, iptables.Filter, false) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err) + } + defer func() { + if err != nil { + if err := iptable.RemoveExistingChain(IsolationChain1, iptables.Filter); err != nil { + logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain1, err) + } + } + }() + + isolationChain2, err := iptable.NewChain(IsolationChain2, iptables.Filter, false) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err) + } + defer func() { + if err != nil { + if err := iptable.RemoveExistingChain(IsolationChain2, iptables.Filter); err != nil { + logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain2, err) + } + } + }() + + if err := iptable.AddReturnRule(IsolationChain1); err != nil { + return nil, nil, nil, nil, err + } + + if err := iptable.AddReturnRule(IsolationChain2); err != nil { + return nil, nil, nil, nil, err + } + + return natChain, filterChain, isolationChain1, isolationChain2, nil +} + +func (n *bridgeNetwork) setupIP4Tables(config *networkConfiguration, i *bridgeInterface) error { + d := n.driver + d.Lock() + driverConfig := d.config + d.Unlock() + + // Sanity check. + if !driverConfig.EnableIPTables { + return errors.New("Cannot program chains, EnableIPTable is disabled") + } + + maskedAddrv4 := &net.IPNet{ + IP: i.bridgeIPv4.IP.Mask(i.bridgeIPv4.Mask), + Mask: i.bridgeIPv4.Mask, + } + return n.setupIPTables(iptables.IPv4, maskedAddrv4, config, i) +} + +func (n *bridgeNetwork) setupIP6Tables(config *networkConfiguration, i *bridgeInterface) error { + d := n.driver + d.Lock() + driverConfig := d.config + d.Unlock() + + // Sanity check. + if !driverConfig.EnableIP6Tables { + return errors.New("Cannot program chains, EnableIP6Tables is disabled") + } + + maskedAddrv6 := &net.IPNet{ + IP: i.bridgeIPv6.IP.Mask(i.bridgeIPv6.Mask), + Mask: i.bridgeIPv6.Mask, + } + + return n.setupIPTables(iptables.IPv6, maskedAddrv6, config, i) +} + +func (n *bridgeNetwork) setupIPTables(ipVersion iptables.IPVersion, maskedAddr *net.IPNet, config *networkConfiguration, i *bridgeInterface) error { + var err error + + d := n.driver + d.Lock() + driverConfig := d.config + d.Unlock() + + // Pickup this configuration option from driver + hairpinMode := !driverConfig.EnableUserlandProxy + + iptable := iptables.GetIptable(ipVersion) + + if config.Internal { + if err = setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, true); err != nil { + return fmt.Errorf("Failed to Setup IP tables: %s", err.Error()) + } + n.registerIptCleanFunc(func() error { + return setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, false) + }) + } else { + if err = setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil { + return fmt.Errorf("Failed to Setup IP tables: %s", err.Error()) + } + n.registerIptCleanFunc(func() error { + return setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false) + }) + natChain, filterChain, _, _, err := n.getDriverChains(ipVersion) + if err != nil { + return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error()) + } + + err = iptable.ProgramChain(natChain, config.BridgeName, hairpinMode, true) + if err != nil { + return fmt.Errorf("Failed to program NAT chain: %s", err.Error()) + } + + err = iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, true) + if err != nil { + return fmt.Errorf("Failed to program FILTER chain: %s", err.Error()) + } + + n.registerIptCleanFunc(func() error { + return iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, false) + }) + + if ipVersion == iptables.IPv4 { + n.portMapper.SetIptablesChain(natChain, n.getNetworkBridgeName()) + } else { + n.portMapperV6.SetIptablesChain(natChain, n.getNetworkBridgeName()) + } + } + + d.Lock() + err = iptable.EnsureJumpRule("FORWARD", IsolationChain1) + d.Unlock() + return err +} + +type iptRule struct { + table iptables.Table + chain string + preArgs []string + args []string +} + +func setupIPTablesInternal(hostIP net.IP, bridgeIface string, addr *net.IPNet, icc, ipmasq, hairpin, enable bool) error { + + var ( + address = addr.String() + skipDNAT = iptRule{table: iptables.Nat, chain: DockerChain, preArgs: []string{"-t", "nat"}, args: []string{"-i", bridgeIface, "-j", "RETURN"}} + outRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}} + natArgs []string + hpNatArgs []string + ) + // if hostIP is set use this address as the src-ip during SNAT + if hostIP != nil { + hostAddr := hostIP.String() + natArgs = []string{"-s", address, "!", "-o", bridgeIface, "-j", "SNAT", "--to-source", hostAddr} + hpNatArgs = []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "SNAT", "--to-source", hostAddr} + // Else use MASQUERADE which picks the src-ip based on NH from the route table + } else { + natArgs = []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"} + hpNatArgs = []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"} + } + + natRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: natArgs} + hpNatRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: hpNatArgs} + + ipVersion := iptables.IPv4 + + if addr.IP.To4() == nil { + ipVersion = iptables.IPv6 + } + + // Set NAT. + if ipmasq { + if err := programChainRule(ipVersion, natRule, "NAT", enable); err != nil { + return err + } + } + + if ipmasq && !hairpin { + if err := programChainRule(ipVersion, skipDNAT, "SKIP DNAT", enable); err != nil { + return err + } + } + + // In hairpin mode, masquerade traffic from localhost + if hairpin { + if err := programChainRule(ipVersion, hpNatRule, "MASQ LOCAL HOST", enable); err != nil { + return err + } + } + + // Set Inter Container Communication. + if err := setIcc(ipVersion, bridgeIface, icc, enable); err != nil { + return err + } + + // Set Accept on all non-intercontainer outgoing packets. + return programChainRule(ipVersion, outRule, "ACCEPT NON_ICC OUTGOING", enable) +} + +func programChainRule(version iptables.IPVersion, rule iptRule, ruleDescr string, insert bool) error { + + iptable := iptables.GetIptable(version) + + var ( + prefix []string + operation string + condition bool + doesExist = iptable.Exists(rule.table, rule.chain, rule.args...) + ) + + if insert { + condition = !doesExist + prefix = []string{"-I", rule.chain} + operation = "enable" + } else { + condition = doesExist + prefix = []string{"-D", rule.chain} + operation = "disable" + } + if rule.preArgs != nil { + prefix = append(rule.preArgs, prefix...) + } + + if condition { + if err := iptable.RawCombinedOutput(append(prefix, rule.args...)...); err != nil { + return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error()) + } + } + + return nil +} + +func setIcc(version iptables.IPVersion, bridgeIface string, iccEnable, insert bool) error { + iptable := iptables.GetIptable(version) + var ( + table = iptables.Filter + chain = "FORWARD" + args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"} + acceptArgs = append(args, "ACCEPT") + dropArgs = append(args, "DROP") + ) + + if insert { + if !iccEnable { + iptable.Raw(append([]string{"-D", chain}, acceptArgs...)...) + + if !iptable.Exists(table, chain, dropArgs...) { + if err := iptable.RawCombinedOutput(append([]string{"-A", chain}, dropArgs...)...); err != nil { + return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error()) + } + } + } else { + iptable.Raw(append([]string{"-D", chain}, dropArgs...)...) + + if !iptable.Exists(table, chain, acceptArgs...) { + if err := iptable.RawCombinedOutput(append([]string{"-I", chain}, acceptArgs...)...); err != nil { + return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error()) + } + } + } + } else { + // Remove any ICC rule. + if !iccEnable { + if iptable.Exists(table, chain, dropArgs...) { + iptable.Raw(append([]string{"-D", chain}, dropArgs...)...) + } + } else { + if iptable.Exists(table, chain, acceptArgs...) { + iptable.Raw(append([]string{"-D", chain}, acceptArgs...)...) + } + } + } + + return nil +} + +// Control Inter Network Communication. Install[Remove] only if it is [not] present. +func setINC(version iptables.IPVersion, iface string, enable bool) error { + iptable := iptables.GetIptable(version) + var ( + action = iptables.Insert + actionMsg = "add" + chains = []string{IsolationChain1, IsolationChain2} + rules = [][]string{ + {"-i", iface, "!", "-o", iface, "-j", IsolationChain2}, + {"-o", iface, "-j", "DROP"}, + } + ) + + if !enable { + action = iptables.Delete + actionMsg = "remove" + } + + for i, chain := range chains { + if err := iptable.ProgramRule(iptables.Filter, chain, action, rules[i]); err != nil { + msg := fmt.Sprintf("unable to %s inter-network communication rule: %v", actionMsg, err) + if enable { + if i == 1 { + // Rollback the rule installed on first chain + if err2 := iptable.ProgramRule(iptables.Filter, chains[0], iptables.Delete, rules[0]); err2 != nil { + logrus.Warnf("Failed to rollback iptables rule after failure (%v): %v", err, err2) + } + } + return fmt.Errorf(msg) + } + logrus.Warn(msg) + } + } + + return nil +} + +// Obsolete chain from previous docker versions +const oldIsolationChain = "DOCKER-ISOLATION" + +func removeIPChains(version iptables.IPVersion) { + ipt := iptables.IPTable{Version: version} + + // Remove obsolete rules from default chains + ipt.ProgramRule(iptables.Filter, "FORWARD", iptables.Delete, []string{"-j", oldIsolationChain}) + + // Remove chains + for _, chainInfo := range []iptables.ChainInfo{ + {Name: DockerChain, Table: iptables.Nat, IPTable: ipt}, + {Name: DockerChain, Table: iptables.Filter, IPTable: ipt}, + {Name: IsolationChain1, Table: iptables.Filter, IPTable: ipt}, + {Name: IsolationChain2, Table: iptables.Filter, IPTable: ipt}, + {Name: oldIsolationChain, Table: iptables.Filter, IPTable: ipt}, + } { + + if err := chainInfo.Remove(); err != nil { + logrus.Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err) + } + } +} + +func setupInternalNetworkRules(bridgeIface string, addr *net.IPNet, icc, insert bool) error { + var ( + inDropRule = iptRule{table: iptables.Filter, chain: IsolationChain1, args: []string{"-i", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"}} + outDropRule = iptRule{table: iptables.Filter, chain: IsolationChain1, args: []string{"-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"}} + ) + + version := iptables.IPv4 + + if addr.IP.To4() == nil { + version = iptables.IPv6 + } + + if err := programChainRule(version, inDropRule, "DROP INCOMING", insert); err != nil { + return err + } + if err := programChainRule(version, outDropRule, "DROP OUTGOING", insert); err != nil { + return err + } + // Set Inter Container Communication. + return setIcc(version, bridgeIface, icc, insert) +} + +func clearEndpointConnections(nlh *netlink.Handle, ep *bridgeEndpoint) { + var ipv4List []net.IP + var ipv6List []net.IP + if ep.addr != nil { + ipv4List = append(ipv4List, ep.addr.IP) + } + if ep.addrv6 != nil { + ipv6List = append(ipv6List, ep.addrv6.IP) + } + iptables.DeleteConntrackEntries(nlh, ipv4List, ipv6List) +} diff --git a/libnetwork/drivers/bridge/setup_ip_tables_test.go b/libnetwork/drivers/bridge/setup_ip_tables_test.go new file mode 100644 index 0000000000000..1eb781f3e9591 --- /dev/null +++ b/libnetwork/drivers/bridge/setup_ip_tables_test.go @@ -0,0 +1,141 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "net" + "testing" + + "github.com/docker/docker/libnetwork/iptables" + "github.com/docker/docker/libnetwork/portmapper" + "github.com/docker/docker/libnetwork/testutils" + "github.com/vishvananda/netlink" +) + +const ( + iptablesTestBridgeIP = "192.168.42.1" +) + +func TestProgramIPTable(t *testing.T) { + // Create a test bridge with a basic bridge configuration (name + IPv4). + defer testutils.SetupTestOSContext(t)() + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + + createTestBridge(getBasicTestConfig(), &bridgeInterface{nlh: nh}, t) + + // Store various iptables chain rules we care for. + rules := []struct { + rule iptRule + descr string + }{ + {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-d", "127.1.2.3", "-i", "lo", "-o", "lo", "-j", "DROP"}}, "Test Loopback"}, + {iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", iptablesTestBridgeIP, "!", "-o", DefaultBridgeName, "-j", "MASQUERADE"}}, "NAT Test"}, + {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", DefaultBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}, "Test ACCEPT INCOMING"}, + {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "!", "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test ACCEPT NON_ICC OUTGOING"}, + {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test enable ICC"}, + {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "DROP"}}, "Test disable ICC"}, + } + + // Assert the chain rules' insertion and removal. + for _, c := range rules { + assertIPTableChainProgramming(c.rule, c.descr, t) + } +} + +func TestSetupIPChains(t *testing.T) { + // Create a test bridge with a basic bridge configuration (name + IPv4). + defer testutils.SetupTestOSContext(t)() + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + + driverconfig := &configuration{ + EnableIPTables: true, + } + d := &driver{ + config: driverconfig, + } + assertChainConfig(d, t) + + config := getBasicTestConfig() + br := &bridgeInterface{nlh: nh} + createTestBridge(config, br, t) + + assertBridgeConfig(config, br, d, t) + + config.EnableIPMasquerade = true + assertBridgeConfig(config, br, d, t) + + config.EnableICC = true + assertBridgeConfig(config, br, d, t) + + config.EnableIPMasquerade = false + assertBridgeConfig(config, br, d, t) +} + +func getBasicTestConfig() *networkConfiguration { + config := &networkConfiguration{ + BridgeName: DefaultBridgeName, + AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)}} + return config +} + +func createTestBridge(config *networkConfiguration, br *bridgeInterface, t *testing.T) { + if err := setupDevice(config, br); err != nil { + t.Fatalf("Failed to create the testing Bridge: %s", err.Error()) + } + if err := setupBridgeIPv4(config, br); err != nil { + t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error()) + } +} + +// Assert base function which pushes iptables chain rules on insertion and removal. +func assertIPTableChainProgramming(rule iptRule, descr string, t *testing.T) { + // Add + if err := programChainRule(iptables.IPv4, rule, descr, true); err != nil { + t.Fatalf("Failed to program iptable rule %s: %s", descr, err.Error()) + } + + iptable := iptables.GetIptable(iptables.IPv4) + if iptable.Exists(rule.table, rule.chain, rule.args...) == false { + t.Fatalf("Failed to effectively program iptable rule: %s", descr) + } + + // Remove + if err := programChainRule(iptables.IPv4, rule, descr, false); err != nil { + t.Fatalf("Failed to remove iptable rule %s: %s", descr, err.Error()) + } + if iptable.Exists(rule.table, rule.chain, rule.args...) == true { + t.Fatalf("Failed to effectively remove iptable rule: %s", descr) + } +} + +// Assert function which create chains. +func assertChainConfig(d *driver, t *testing.T) { + var err error + + d.natChain, d.filterChain, d.isolationChain1, d.isolationChain2, err = setupIPChains(d.config, iptables.IPv4) + if err != nil { + t.Fatal(err) + } +} + +// Assert function which pushes chains based on bridge config parameters. +func assertBridgeConfig(config *networkConfiguration, br *bridgeInterface, d *driver, t *testing.T) { + nw := bridgeNetwork{portMapper: portmapper.New(""), + config: config} + nw.driver = d + + // Attempt programming of ip tables. + err := nw.setupIP4Tables(config, br) + if err != nil { + t.Fatalf("%v", err) + } +} diff --git a/libnetwork/drivers/bridge/setup_ipv4.go b/libnetwork/drivers/bridge/setup_ipv4.go new file mode 100644 index 0000000000000..fc814e81d4d52 --- /dev/null +++ b/libnetwork/drivers/bridge/setup_ipv4.go @@ -0,0 +1,85 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "errors" + "fmt" + "net" + "os" + "path/filepath" + + "github.com/docker/docker/libnetwork/types" + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +func selectIPv4Address(addresses []netlink.Addr, selector *net.IPNet) (netlink.Addr, error) { + if len(addresses) == 0 { + return netlink.Addr{}, errors.New("unable to select an address as the address pool is empty") + } + if selector != nil { + for _, addr := range addresses { + if selector.Contains(addr.IP) { + return addr, nil + } + } + } + return addresses[0], nil +} + +func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error { + if !config.InhibitIPv4 { + addrv4List, _, err := i.addresses() + if err != nil { + return fmt.Errorf("failed to retrieve bridge interface addresses: %v", err) + } + + addrv4, _ := selectIPv4Address(addrv4List, config.AddressIPv4) + + if !types.CompareIPNet(addrv4.IPNet, config.AddressIPv4) { + if addrv4.IPNet != nil { + if err := i.nlh.AddrDel(i.Link, &addrv4); err != nil { + return fmt.Errorf("failed to remove current ip address from bridge: %v", err) + } + } + logrus.Debugf("Assigning address to bridge interface %s: %s", config.BridgeName, config.AddressIPv4) + if err := i.nlh.AddrAdd(i.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil { + return &IPv4AddrAddError{IP: config.AddressIPv4, Err: err} + } + } + } + + // Store bridge network and default gateway + i.bridgeIPv4 = config.AddressIPv4 + i.gatewayIPv4 = config.AddressIPv4.IP + + return nil +} + +func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error { + if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) { + return &ErrInvalidGateway{} + } + + // Store requested default gateway + i.gatewayIPv4 = config.DefaultGatewayIPv4 + + return nil +} + +func setupLoopbackAddressesRouting(config *networkConfiguration, i *bridgeInterface) error { + sysPath := filepath.Join("/proc/sys/net/ipv4/conf", config.BridgeName, "route_localnet") + ipv4LoRoutingData, err := os.ReadFile(sysPath) + if err != nil { + return fmt.Errorf("Cannot read IPv4 local routing setup: %v", err) + } + // Enable loopback addresses routing only if it isn't already enabled + if ipv4LoRoutingData[0] != '1' { + if err := os.WriteFile(sysPath, []byte{'1', '\n'}, 0644); err != nil { + return fmt.Errorf("Unable to enable local routing for hairpin mode: %v", err) + } + } + return nil +} diff --git a/libnetwork/drivers/bridge/setup_ipv4_test.go b/libnetwork/drivers/bridge/setup_ipv4_test.go new file mode 100644 index 0000000000000..1d513ab91083a --- /dev/null +++ b/libnetwork/drivers/bridge/setup_ipv4_test.go @@ -0,0 +1,89 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "net" + "testing" + + "github.com/docker/docker/libnetwork/testutils" + "github.com/vishvananda/netlink" +) + +func setupTestInterface(t *testing.T, nh *netlink.Handle) (*networkConfiguration, *bridgeInterface) { + config := &networkConfiguration{ + BridgeName: DefaultBridgeName} + br := &bridgeInterface{nlh: nh} + + if err := setupDevice(config, br); err != nil { + t.Fatalf("Bridge creation failed: %v", err) + } + return config, br +} + +func TestSetupBridgeIPv4Fixed(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + ip, netw, err := net.ParseCIDR("192.168.1.1/24") + if err != nil { + t.Fatalf("Failed to parse bridge IPv4: %v", err) + } + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + config, br := setupTestInterface(t, nh) + config.AddressIPv4 = &net.IPNet{IP: ip, Mask: netw.Mask} + if err := setupBridgeIPv4(config, br); err != nil { + t.Fatalf("Failed to setup bridge IPv4: %v", err) + } + + addrsv4, err := nh.AddrList(br.Link, netlink.FAMILY_V4) + if err != nil { + t.Fatalf("Failed to list device IPv4 addresses: %v", err) + } + + var found bool + for _, addr := range addrsv4 { + if config.AddressIPv4.String() == addr.IPNet.String() { + found = true + break + } + } + + if !found { + t.Fatalf("Bridge device does not have requested IPv4 address %v", config.AddressIPv4) + } +} + +func TestSetupGatewayIPv4(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + ip, nw, _ := net.ParseCIDR("192.168.0.24/16") + nw.IP = ip + gw := net.ParseIP("192.168.2.254") + + config := &networkConfiguration{ + BridgeName: DefaultBridgeName, + DefaultGatewayIPv4: gw} + + br := &bridgeInterface{bridgeIPv4: nw, nlh: nh} + + if err := setupGatewayIPv4(config, br); err != nil { + t.Fatalf("Set Default Gateway failed: %v", err) + } + + if !gw.Equal(br.gatewayIPv4) { + t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv4) + } +} diff --git a/libnetwork/drivers/bridge/setup_ipv6.go b/libnetwork/drivers/bridge/setup_ipv6.go new file mode 100644 index 0000000000000..0f1380ce55ac4 --- /dev/null +++ b/libnetwork/drivers/bridge/setup_ipv6.go @@ -0,0 +1,111 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "fmt" + "net" + "os" + + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +// bridgeIPv6 is the default, link-local IPv6 address for the bridge (fe80::1/64) +var bridgeIPv6 = &net.IPNet{IP: net.ParseIP("fe80::1"), Mask: net.CIDRMask(64, 128)} + +const ( + ipv6ForwardConfPerm = 0644 + ipv6ForwardConfDefault = "/proc/sys/net/ipv6/conf/default/forwarding" + ipv6ForwardConfAll = "/proc/sys/net/ipv6/conf/all/forwarding" +) + +func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error { + procFile := "/proc/sys/net/ipv6/conf/" + config.BridgeName + "/disable_ipv6" + ipv6BridgeData, err := os.ReadFile(procFile) + if err != nil { + return fmt.Errorf("Cannot read IPv6 setup for bridge %v: %v", config.BridgeName, err) + } + // Enable IPv6 on the bridge only if it isn't already enabled + if ipv6BridgeData[0] != '0' { + if err := os.WriteFile(procFile, []byte{'0', '\n'}, ipv6ForwardConfPerm); err != nil { + return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err) + } + } + + // Store bridge network and default gateway + i.bridgeIPv6 = bridgeIPv6 + i.gatewayIPv6 = i.bridgeIPv6.IP + + if err := i.programIPv6Address(); err != nil { + return err + } + + if config.AddressIPv6 == nil { + return nil + } + + // Store the user specified bridge network and network gateway and program it + i.bridgeIPv6 = config.AddressIPv6 + i.gatewayIPv6 = config.AddressIPv6.IP + + if err := i.programIPv6Address(); err != nil { + return err + } + + // Setting route to global IPv6 subnet + logrus.Debugf("Adding route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName) + err = i.nlh.RouteAdd(&netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + LinkIndex: i.Link.Attrs().Index, + Dst: config.AddressIPv6, + }) + if err != nil && !os.IsExist(err) { + logrus.Errorf("Could not add route to IPv6 network %s via device %s: %s", config.AddressIPv6.String(), config.BridgeName, err) + } + + return nil +} + +func setupGatewayIPv6(config *networkConfiguration, i *bridgeInterface) error { + if config.AddressIPv6 == nil { + return &ErrInvalidContainerSubnet{} + } + if !config.AddressIPv6.Contains(config.DefaultGatewayIPv6) { + return &ErrInvalidGateway{} + } + + // Store requested default gateway + i.gatewayIPv6 = config.DefaultGatewayIPv6 + + return nil +} + +func setupIPv6Forwarding(config *networkConfiguration, i *bridgeInterface) error { + // Get current IPv6 default forwarding setup + ipv6ForwardDataDefault, err := os.ReadFile(ipv6ForwardConfDefault) + if err != nil { + return fmt.Errorf("Cannot read IPv6 default forwarding setup: %v", err) + } + // Enable IPv6 default forwarding only if it is not already enabled + if ipv6ForwardDataDefault[0] != '1' { + if err := os.WriteFile(ipv6ForwardConfDefault, []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil { + logrus.Warnf("Unable to enable IPv6 default forwarding: %v", err) + } + } + + // Get current IPv6 all forwarding setup + ipv6ForwardDataAll, err := os.ReadFile(ipv6ForwardConfAll) + if err != nil { + return fmt.Errorf("Cannot read IPv6 all forwarding setup: %v", err) + } + // Enable IPv6 all forwarding only if it is not already enabled + if ipv6ForwardDataAll[0] != '1' { + if err := os.WriteFile(ipv6ForwardConfAll, []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil { + logrus.Warnf("Unable to enable IPv6 all forwarding: %v", err) + } + } + + return nil +} diff --git a/libnetwork/drivers/bridge/setup_ipv6_test.go b/libnetwork/drivers/bridge/setup_ipv6_test.go new file mode 100644 index 0000000000000..bc34be87fe05a --- /dev/null +++ b/libnetwork/drivers/bridge/setup_ipv6_test.go @@ -0,0 +1,85 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "bytes" + "fmt" + "net" + "os" + "testing" + + "github.com/docker/docker/libnetwork/testutils" + "github.com/vishvananda/netlink" +) + +func TestSetupIPv6(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + config, br := setupTestInterface(t, nh) + if err := setupBridgeIPv6(config, br); err != nil { + t.Fatalf("Failed to setup bridge IPv6: %v", err) + } + + procSetting, err := os.ReadFile(fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/disable_ipv6", config.BridgeName)) + if err != nil { + t.Fatalf("Failed to read disable_ipv6 kernel setting: %v", err) + } + + if expected := []byte("0\n"); !bytes.Equal(expected, procSetting) { + t.Fatalf("Invalid kernel setting disable_ipv6: expected %q, got %q", string(expected), string(procSetting)) + } + + addrsv6, err := nh.AddrList(br.Link, netlink.FAMILY_V6) + if err != nil { + t.Fatalf("Failed to list device IPv6 addresses: %v", err) + } + + var found bool + for _, addr := range addrsv6 { + if bridgeIPv6.String() == addr.IPNet.String() { + found = true + break + } + } + + if !found { + t.Fatalf("Bridge device does not have requested IPv6 address %v", bridgeIPv6) + } + +} + +func TestSetupGatewayIPv6(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + _, nw, _ := net.ParseCIDR("2001:db8:ea9:9abc:ffff::/80") + gw := net.ParseIP("2001:db8:ea9:9abc:ffff::254") + + config := &networkConfiguration{ + BridgeName: DefaultBridgeName, + AddressIPv6: nw, + DefaultGatewayIPv6: gw} + + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + br := &bridgeInterface{nlh: nh} + + if err := setupGatewayIPv6(config, br); err != nil { + t.Fatalf("Set Default Gateway failed: %v", err) + } + + if !gw.Equal(br.gatewayIPv6) { + t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv6) + } +} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_verify.go b/libnetwork/drivers/bridge/setup_verify.go similarity index 93% rename from vendor/github.com/docker/libnetwork/drivers/bridge/setup_verify.go rename to libnetwork/drivers/bridge/setup_verify.go index de77c38a667d8..e7fd1bf23cce0 100644 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_verify.go +++ b/libnetwork/drivers/bridge/setup_verify.go @@ -1,11 +1,14 @@ +//go:build linux +// +build linux + package bridge import ( "fmt" "strings" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" ) @@ -37,6 +40,7 @@ func setupVerifyAndReconcile(config *networkConfiguration, i *bridgeInterface) e // Release any residual IPv6 address that might be there because of older daemon instances for _, addrv6 := range addrsv6 { + addrv6 := addrv6 if addrv6.IP.IsGlobalUnicast() && !types.CompareIPNet(addrv6.IPNet, i.bridgeIPv6) { if err := i.nlh.AddrDel(i.Link, &addrv6); err != nil { logrus.Warnf("Failed to remove residual IPv6 address %s from bridge: %v", addrv6.IPNet, err) diff --git a/libnetwork/drivers/bridge/setup_verify_test.go b/libnetwork/drivers/bridge/setup_verify_test.go new file mode 100644 index 0000000000000..b949918b64f0d --- /dev/null +++ b/libnetwork/drivers/bridge/setup_verify_test.go @@ -0,0 +1,117 @@ +//go:build linux +// +build linux + +package bridge + +import ( + "net" + "testing" + + "github.com/docker/docker/libnetwork/testutils" + "github.com/vishvananda/netlink" +) + +func setupVerifyTest(t *testing.T) *bridgeInterface { + nh, err := netlink.NewHandle() + if err != nil { + t.Fatal(err) + } + inf := &bridgeInterface{nlh: nh} + + br := netlink.Bridge{} + br.LinkAttrs.Name = "default0" + if err := nh.LinkAdd(&br); err == nil { + inf.Link = &br + } else { + t.Fatalf("Failed to create bridge interface: %v", err) + } + + return inf +} + +func TestSetupVerify(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + config := &networkConfiguration{} + config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err) + } + + if err := setupVerifyAndReconcile(config, inf); err != nil { + t.Fatalf("Address verification failed: %v", err) + } +} + +func TestSetupVerifyBad(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + config := &networkConfiguration{} + config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + + ipnet := &net.IPNet{IP: net.IPv4(192, 168, 1, 2), Mask: addrv4.DefaultMask()} + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: ipnet}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", ipnet, err) + } + + if err := setupVerifyAndReconcile(config, inf); err == nil { + t.Fatal("Address verification was expected to fail") + } +} + +func TestSetupVerifyMissing(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + config := &networkConfiguration{} + config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + + if err := setupVerifyAndReconcile(config, inf); err == nil { + t.Fatal("Address verification was expected to fail") + } +} + +func TestSetupVerifyIPv6(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + config := &networkConfiguration{} + config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + config.EnableIPv6 = true + + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil { + t.Fatalf("Failed to assign IPv6 %s to interface: %v", bridgeIPv6, err) + } + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err) + } + + if err := setupVerifyAndReconcile(config, inf); err != nil { + t.Fatalf("Address verification failed: %v", err) + } +} + +func TestSetupVerifyIPv6Missing(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + addrv4 := net.IPv4(192, 168, 1, 1) + inf := setupVerifyTest(t) + config := &networkConfiguration{} + config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()} + config.EnableIPv6 = true + + if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil { + t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err) + } + + if err := setupVerifyAndReconcile(config, inf); err == nil { + t.Fatal("Address verification was expected to fail") + } +} diff --git a/libnetwork/drivers/host/host.go b/libnetwork/drivers/host/host.go new file mode 100644 index 0000000000000..b99049ff374d5 --- /dev/null +++ b/libnetwork/drivers/host/host.go @@ -0,0 +1,106 @@ +package host + +import ( + "sync" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/types" +) + +const networkType = "host" + +type driver struct { + network string + sync.Mutex +} + +// Init registers a new instance of host driver +func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { + c := driverapi.Capability{ + DataScope: datastore.LocalScope, + ConnectivityScope: datastore.LocalScope, + } + return dc.RegisterDriver(networkType, &driver{}, c) +} + +func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { + return nil, types.NotImplementedErrorf("not implemented") +} + +func (d *driver) NetworkFree(id string) error { + return types.NotImplementedErrorf("not implemented") +} + +func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { +} + +func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { + return "", nil +} + +func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { + d.Lock() + defer d.Unlock() + + if d.network != "" { + return types.ForbiddenErrorf("only one instance of \"%s\" network is allowed", networkType) + } + + d.network = id + + return nil +} + +func (d *driver) DeleteNetwork(nid string) error { + return types.ForbiddenErrorf("network of type \"%s\" cannot be deleted", networkType) +} + +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { + return nil +} + +func (d *driver) DeleteEndpoint(nid, eid string) error { + return nil +} + +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { + return make(map[string]interface{}), nil +} + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + return nil +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid string) error { + return nil +} + +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + +func (d *driver) Type() string { + return networkType +} + +func (d *driver) IsBuiltIn() bool { + return true +} + +// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster +func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster +func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} diff --git a/libnetwork/drivers/host/host_test.go b/libnetwork/drivers/host/host_test.go new file mode 100644 index 0000000000000..caa77df7c5e2c --- /dev/null +++ b/libnetwork/drivers/host/host_test.go @@ -0,0 +1,49 @@ +package host + +import ( + "testing" + + "github.com/docker/docker/libnetwork/types" +) + +func TestDriver(t *testing.T) { + d := &driver{} + + if d.Type() != networkType { + t.Fatal("Unexpected network type returned by driver") + } + + err := d.CreateNetwork("first", nil, nil, nil, nil) + if err != nil { + t.Fatal(err) + } + + if d.network != "first" { + t.Fatal("Unexpected network id stored") + } + + err = d.CreateNetwork("second", nil, nil, nil, nil) + if err == nil { + t.Fatal("Second network creation should fail on this driver") + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatal("Second network creation failed with unexpected error type") + } + + err = d.DeleteNetwork("first") + if err == nil { + t.Fatal("network deletion should fail on this driver") + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatal("network deletion failed with unexpected error type") + } + + // we don't really check if it is there or not, delete is not allowed for this driver, period. + err = d.DeleteNetwork("unknown") + if err == nil { + t.Fatal("any network deletion should fail on this driver") + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatal("any network deletion failed with unexpected error type") + } +} diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan.go b/libnetwork/drivers/ipvlan/ipvlan.go similarity index 88% rename from vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan.go rename to libnetwork/drivers/ipvlan/ipvlan.go index c64ad555a31cf..4e2abcf0ddd56 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan.go +++ b/libnetwork/drivers/ipvlan/ipvlan.go @@ -1,14 +1,16 @@ +//go:build linux +// +build linux + package ipvlan import ( "net" "sync" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/types" ) const ( @@ -48,7 +50,6 @@ type endpoint struct { type network struct { id string - sbox osl.Sandbox endpoints endpointTable driver *driver config *configuration @@ -64,7 +65,9 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { d := &driver{ networks: networkTable{}, } - d.initStore(config) + if err := d.initStore(config); err != nil { + return err + } return dc.RegisterDriver(ipvlanType, d, c) } @@ -78,7 +81,7 @@ func (d *driver) NetworkFree(id string) error { } func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { - return make(map[string]interface{}, 0), nil + return make(map[string]interface{}), nil } func (d *driver) Type() string { diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go b/libnetwork/drivers/ipvlan/ipvlan_endpoint.go similarity index 89% rename from vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go rename to libnetwork/drivers/ipvlan/ipvlan_endpoint.go index 336b681baba3b..7d4c42a44279b 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_endpoint.go +++ b/libnetwork/drivers/ipvlan/ipvlan_endpoint.go @@ -1,13 +1,16 @@ +//go:build linux +// +build linux + package ipvlan import ( "fmt" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) diff --git a/libnetwork/drivers/ipvlan/ipvlan_joinleave.go b/libnetwork/drivers/ipvlan/ipvlan_joinleave.go new file mode 100644 index 0000000000000..99e7b1fa93ec8 --- /dev/null +++ b/libnetwork/drivers/ipvlan/ipvlan_joinleave.go @@ -0,0 +1,213 @@ +//go:build linux +// +build linux + +package ipvlan + +import ( + "fmt" + "net" + + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/types" + "github.com/sirupsen/logrus" +) + +type staticRoute struct { + Destination *net.IPNet + RouteType int + NextHop net.IP +} + +const ( + defaultV4RouteCidr = "0.0.0.0/0" + defaultV6RouteCidr = "::/0" +) + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + defer osl.InitOSContext()() + n, err := d.getNetwork(nid) + if err != nil { + return err + } + endpoint := n.endpoint(eid) + if endpoint == nil { + return fmt.Errorf("could not find endpoint with id %s", eid) + } + // generate a name for the iface that will be renamed to eth0 in the sbox + containerIfName, err := netutils.GenerateIfaceName(ns.NlHandle(), vethPrefix, vethLen) + if err != nil { + return fmt.Errorf("error generating an interface name: %v", err) + } + // create the netlink ipvlan interface + vethName, err := createIPVlan(containerIfName, n.config.Parent, n.config.IpvlanMode) + if err != nil { + return err + } + // bind the generated iface name to the endpoint + endpoint.srcName = vethName + ep := n.endpoint(eid) + if ep == nil { + return fmt.Errorf("could not find endpoint with id %s", eid) + } + if !n.config.Internal { + if n.config.IpvlanMode == modeL3 { + // disable gateway services to add a default gw using dev eth0 only + jinfo.DisableGatewayService() + defaultRoute, err := ifaceGateway(defaultV4RouteCidr) + if err != nil { + return err + } + if err := jinfo.AddStaticRoute(defaultRoute.Destination, defaultRoute.RouteType, defaultRoute.NextHop); err != nil { + return fmt.Errorf("failed to set an ipvlan l3 mode ipv4 default gateway: %v", err) + } + logrus.Debugf("Ipvlan Endpoint Joined with IPv4_Addr: %s, Ipvlan_Mode: %s, Parent: %s", + ep.addr.IP.String(), n.config.IpvlanMode, n.config.Parent) + // If the endpoint has a v6 address, set a v6 default route + if ep.addrv6 != nil { + default6Route, err := ifaceGateway(defaultV6RouteCidr) + if err != nil { + return err + } + if err = jinfo.AddStaticRoute(default6Route.Destination, default6Route.RouteType, default6Route.NextHop); err != nil { + return fmt.Errorf("failed to set an ipvlan l3 mode ipv6 default gateway: %v", err) + } + logrus.Debugf("Ipvlan Endpoint Joined with IPv6_Addr: %s, Ipvlan_Mode: %s, Parent: %s", + ep.addrv6.IP.String(), n.config.IpvlanMode, n.config.Parent) + } + } + if n.config.IpvlanMode == modeL2 { + // parse and correlate the endpoint v4 address with the available v4 subnets + if len(n.config.Ipv4Subnets) > 0 { + s := n.getSubnetforIPv4(ep.addr) + if s == nil { + return fmt.Errorf("could not find a valid ipv4 subnet for endpoint %s", eid) + } + v4gw, _, err := net.ParseCIDR(s.GwIP) + if err != nil { + return fmt.Errorf("gateway %s is not a valid ipv4 address: %v", s.GwIP, err) + } + err = jinfo.SetGateway(v4gw) + if err != nil { + return err + } + logrus.Debugf("Ipvlan Endpoint Joined with IPv4_Addr: %s, Gateway: %s, Ipvlan_Mode: %s, Parent: %s", + ep.addr.IP.String(), v4gw.String(), n.config.IpvlanMode, n.config.Parent) + } + // parse and correlate the endpoint v6 address with the available v6 subnets + if len(n.config.Ipv6Subnets) > 0 { + s := n.getSubnetforIPv6(ep.addrv6) + if s == nil { + return fmt.Errorf("could not find a valid ipv6 subnet for endpoint %s", eid) + } + v6gw, _, err := net.ParseCIDR(s.GwIP) + if err != nil { + return fmt.Errorf("gateway %s is not a valid ipv6 address: %v", s.GwIP, err) + } + err = jinfo.SetGatewayIPv6(v6gw) + if err != nil { + return err + } + logrus.Debugf("Ipvlan Endpoint Joined with IPv6_Addr: %s, Gateway: %s, Ipvlan_Mode: %s, Parent: %s", + ep.addrv6.IP.String(), v6gw.String(), n.config.IpvlanMode, n.config.Parent) + } + } + } else { + if len(n.config.Ipv4Subnets) > 0 { + logrus.Debugf("Ipvlan Endpoint Joined with IPv4_Addr: %s, IpVlan_Mode: %s, Parent: %s", + ep.addr.IP.String(), n.config.IpvlanMode, n.config.Parent) + } + if len(n.config.Ipv6Subnets) > 0 { + logrus.Debugf("Ipvlan Endpoint Joined with IPv6_Addr: %s IpVlan_Mode: %s, Parent: %s", + ep.addrv6.IP.String(), n.config.IpvlanMode, n.config.Parent) + } + } + iNames := jinfo.InterfaceName() + err = iNames.SetNames(vethName, containerVethPrefix) + if err != nil { + return err + } + if err = d.storeUpdate(ep); err != nil { + return fmt.Errorf("failed to save ipvlan endpoint %.7s to store: %v", ep.id, err) + } + + return nil +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid string) error { + defer osl.InitOSContext()() + network, err := d.getNetwork(nid) + if err != nil { + return err + } + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + if endpoint == nil { + return fmt.Errorf("could not find endpoint with id %s", eid) + } + + return nil +} + +// ifaceGateway returns a static route for either v4/v6 to be set to the container eth0 +func ifaceGateway(dfNet string) (*staticRoute, error) { + nh, dst, err := net.ParseCIDR(dfNet) + if err != nil { + return nil, fmt.Errorf("unable to parse default route %v", err) + } + defaultRoute := &staticRoute{ + Destination: dst, + RouteType: types.CONNECTED, + NextHop: nh, + } + + return defaultRoute, nil +} + +// getSubnetforIPv4 returns the ipv4 subnet to which the given IP belongs +func (n *network) getSubnetforIPv4(ip *net.IPNet) *ipv4Subnet { + for _, s := range n.config.Ipv4Subnets { + _, snet, err := net.ParseCIDR(s.SubnetIP) + if err != nil { + return nil + } + // first check if the mask lengths are the same + i, _ := snet.Mask.Size() + j, _ := ip.Mask.Size() + if i != j { + continue + } + if snet.Contains(ip.IP) { + return s + } + } + + return nil +} + +// getSubnetforIPv6 returns the ipv6 subnet to which the given IP belongs +func (n *network) getSubnetforIPv6(ip *net.IPNet) *ipv6Subnet { + for _, s := range n.config.Ipv6Subnets { + _, snet, err := net.ParseCIDR(s.SubnetIP) + if err != nil { + return nil + } + // first check if the mask lengths are the same + i, _ := snet.Mask.Size() + j, _ := ip.Mask.Size() + if i != j { + continue + } + if snet.Contains(ip.IP) { + return s + } + } + + return nil +} diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_network.go b/libnetwork/drivers/ipvlan/ipvlan_network.go similarity index 81% rename from vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_network.go rename to libnetwork/drivers/ipvlan/ipvlan_network.go index 8825e1e117248..cde03a721b7a3 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_network.go +++ b/libnetwork/drivers/ipvlan/ipvlan_network.go @@ -1,16 +1,19 @@ +//go:build linux +// +build linux + package ipvlan import ( "fmt" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/stringid" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/options" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -57,13 +60,15 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo // if parent interface not specified, create a dummy type link to use named dummy+net_id if config.Parent == "" { config.Parent = getDummyName(stringid.TruncateID(config.ID)) - // empty parent and --internal are handled the same. Set here to update k/v - config.Internal = true } - err = d.createNetwork(config) + foundExisting, err := d.createNetwork(config) if err != nil { return err } + + if foundExisting { + return types.InternalMaskableErrorf("restoring existing network %s", config.ID) + } // update persistent db, rollback on fail err = d.storeUpdate(config) if err != nil { @@ -76,48 +81,55 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo } // createNetwork is used by new network callbacks and persistent network cache -func (d *driver) createNetwork(config *configuration) error { +func (d *driver) createNetwork(config *configuration) (bool, error) { + foundExisting := false networkList := d.getNetworks() for _, nw := range networkList { if config.Parent == nw.config.Parent { - return fmt.Errorf("network %s is already using parent interface %s", - getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent) + if config.ID != nw.config.ID { + return false, fmt.Errorf("network %s is already using parent interface %s", + getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent) + } + logrus.Debugf("Create Network for the same ID %s\n", config.ID) + foundExisting = true + break } } if !parentExists(config.Parent) { - // if the --internal flag is set, create a dummy link - if config.Internal { - err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID))) + // Create a dummy link if a dummy name is set for parent + if dummyName := getDummyName(stringid.TruncateID(config.ID)); dummyName == config.Parent { + err := createDummyLink(config.Parent, dummyName) if err != nil { - return err + return false, err } config.CreatedSlaveLink = true + // notify the user in logs they have limited communications - if config.Parent == getDummyName(stringid.TruncateID(config.ID)) { - logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s", - config.Parent) - } + logrus.Debugf("Empty -o parent= flags limit communications to other containers inside of network: %s", + config.Parent) } else { // if the subinterface parent_iface.vlan_id checks do not pass, return err. // a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10' err := createVlanLink(config.Parent) if err != nil { - return err + return false, err } // if driver created the networks slave link, record it for future deletion config.CreatedSlaveLink = true } } - n := &network{ - id: config.ID, - driver: d, - endpoints: endpointTable{}, - config: config, + if !foundExisting { + n := &network{ + id: config.ID, + driver: d, + endpoints: endpointTable{}, + config: config, + } + // add the network + d.addNetwork(n) } - // add the *network - d.addNetwork(n) - return nil + return foundExisting, nil } // DeleteNetwork the network for the specified driver type @@ -181,11 +193,10 @@ func parseNetworkOptions(id string, option options.Generic) (*configuration, err return nil, err } } - // setting the parent to "" will trigger an isolated network dummy parent link - if _, ok := option[netlabel.Internal]; ok { - config.Internal = true - // empty --parent= and --internal are handled the same. - config.Parent = "" + if val, ok := option[netlabel.Internal]; ok { + if internal, ok := val.(bool); ok && internal { + config.Internal = true + } } return config, nil } diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_setup.go b/libnetwork/drivers/ipvlan/ipvlan_setup.go similarity index 98% rename from vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_setup.go rename to libnetwork/drivers/ipvlan/ipvlan_setup.go index da8d8faeb8534..d7e037aaf7f67 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_setup.go +++ b/libnetwork/drivers/ipvlan/ipvlan_setup.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package ipvlan import ( @@ -5,7 +8,7 @@ import ( "strconv" "strings" - "github.com/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/ns" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" ) @@ -63,11 +66,7 @@ func setIPVlanMode(mode string) (netlink.IPVlanMode, error) { // parentExists check if the specified interface exists in the default namespace func parentExists(ifaceStr string) bool { _, err := ns.NlHandle().LinkByName(ifaceStr) - if err != nil { - return false - } - - return true + return err == nil } // createVlanLink parses sub-interfaces and vlan id for creation diff --git a/libnetwork/drivers/ipvlan/ipvlan_setup_test.go b/libnetwork/drivers/ipvlan/ipvlan_setup_test.go new file mode 100644 index 0000000000000..0bc2bbd785335 --- /dev/null +++ b/libnetwork/drivers/ipvlan/ipvlan_setup_test.go @@ -0,0 +1,90 @@ +//go:build linux +// +build linux + +package ipvlan + +import ( + "testing" + + "github.com/vishvananda/netlink" +) + +// TestValidateLink tests the parentExists function +func TestValidateLink(t *testing.T) { + validIface := "lo" + invalidIface := "foo12345" + + // test a valid parent interface validation + if ok := parentExists(validIface); !ok { + t.Fatalf("failed validating loopback %s", validIface) + } + // test an invalid parent interface validation + if ok := parentExists(invalidIface); ok { + t.Fatalf("failed to invalidate interface %s", invalidIface) + } +} + +// TestValidateSubLink tests valid 802.1q naming convention +func TestValidateSubLink(t *testing.T) { + validSubIface := "lo.10" + invalidSubIface1 := "lo" + invalidSubIface2 := "lo:10" + invalidSubIface3 := "foo123.456" + + // test a valid parent_iface.vlan_id + _, _, err := parseVlan(validSubIface) + if err != nil { + t.Fatalf("failed subinterface validation: %v", err) + } + // test an invalid vid with a valid parent link + _, _, err = parseVlan(invalidSubIface1) + if err == nil { + t.Fatalf("failed subinterface validation test: %s", invalidSubIface1) + } + // test a valid vid with a valid parent link with an invalid delimiter + _, _, err = parseVlan(invalidSubIface2) + if err == nil { + t.Fatalf("failed subinterface validation test: %v", invalidSubIface2) + } + // test an invalid parent link with a valid vid + _, _, err = parseVlan(invalidSubIface3) + if err == nil { + t.Fatalf("failed subinterface validation test: %v", invalidSubIface3) + } +} + +// TestSetIPVlanMode tests the ipvlan mode setter +func TestSetIPVlanMode(t *testing.T) { + // test ipvlan l2 mode + mode, err := setIPVlanMode(modeL2) + if err != nil { + t.Fatalf("error parsing %v vlan mode: %v", mode, err) + } + if mode != netlink.IPVLAN_MODE_L2 { + t.Fatalf("expected %d got %d", netlink.IPVLAN_MODE_L2, mode) + } + // test ipvlan l3 mode + mode, err = setIPVlanMode(modeL3) + if err != nil { + t.Fatalf("error parsing %v vlan mode: %v", mode, err) + } + if mode != netlink.IPVLAN_MODE_L3 { + t.Fatalf("expected %d got %d", netlink.IPVLAN_MODE_L3, mode) + } + // test invalid mode + mode, err = setIPVlanMode("foo") + if err == nil { + t.Fatal("invalid ipvlan mode should have returned an error") + } + if mode != 0 { + t.Fatalf("expected 0 got %d", mode) + } + // test null mode + mode, err = setIPVlanMode("") + if err == nil { + t.Fatal("invalid ipvlan mode should have returned an error") + } + if mode != 0 { + t.Fatalf("expected 0 got %d", mode) + } +} diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_state.go b/libnetwork/drivers/ipvlan/ipvlan_state.go similarity index 87% rename from vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_state.go rename to libnetwork/drivers/ipvlan/ipvlan_state.go index dc73b6893d36b..fc26f1613e6e3 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_state.go +++ b/libnetwork/drivers/ipvlan/ipvlan_state.go @@ -1,10 +1,12 @@ +//go:build linux +// +build linux + package ipvlan import ( "fmt" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -87,19 +89,6 @@ func validateID(nid, eid string) error { return nil } -func (n *network) sandbox() osl.Sandbox { - n.Lock() - defer n.Unlock() - - return n.sbox -} - -func (n *network) setSandbox(sbox osl.Sandbox) { - n.Lock() - n.sbox = sbox - n.Unlock() -} - func (d *driver) getNetwork(id string) (*network, error) { d.Lock() defer d.Unlock() diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go b/libnetwork/drivers/ipvlan/ipvlan_store.go similarity index 95% rename from vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go rename to libnetwork/drivers/ipvlan/ipvlan_store.go index 72eb3fc4fffdf..9f0eab6fc9c9b 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_store.go +++ b/libnetwork/drivers/ipvlan/ipvlan_store.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package ipvlan import ( @@ -5,10 +8,10 @@ import ( "fmt" "net" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -55,7 +58,14 @@ func (d *driver) initStore(option map[string]interface{}) error { return types.InternalErrorf("ipvlan driver failed to initialize data store: %v", err) } - return d.populateNetworks() + err = d.populateNetworks() + if err != nil { + return err + } + err = d.populateEndpoints() + if err != nil { + return err + } } return nil @@ -73,7 +83,7 @@ func (d *driver) populateNetworks() error { } for _, kvo := range kvol { config := kvo.(*configuration) - if err = d.createNetwork(config); err != nil { + if _, err = d.createNetwork(config); err != nil { logrus.Warnf("could not create ipvlan network for id %s from persistent state", config.ID) } } diff --git a/libnetwork/drivers/ipvlan/ipvlan_test.go b/libnetwork/drivers/ipvlan/ipvlan_test.go new file mode 100644 index 0000000000000..b0f4de9cd7504 --- /dev/null +++ b/libnetwork/drivers/ipvlan/ipvlan_test.go @@ -0,0 +1,67 @@ +//go:build linux +// +build linux + +package ipvlan + +import ( + "testing" + + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/pkg/plugingetter" +) + +const testNetworkType = "ipvlan" + +type driverTester struct { + t *testing.T + d *driver +} + +func (dt *driverTester) GetPluginGetter() plugingetter.PluginGetter { + return nil +} + +func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver, + cap driverapi.Capability) error { + if name != testNetworkType { + dt.t.Fatalf("Expected driver register name to be %q. Instead got %q", + testNetworkType, name) + } + + if _, ok := drv.(*driver); !ok { + dt.t.Fatalf("Expected driver type to be %T. Instead got %T", + &driver{}, drv) + } + + dt.d = drv.(*driver) + return nil +} + +func TestIpvlanInit(t *testing.T) { + if err := Init(&driverTester{t: t}, nil); err != nil { + t.Fatal(err) + } +} + +func TestIpvlanNilConfig(t *testing.T) { + dt := &driverTester{t: t} + if err := Init(dt, nil); err != nil { + t.Fatal(err) + } + + if err := dt.d.initStore(nil); err != nil { + t.Fatal(err) + } +} + +func TestIpvlanType(t *testing.T) { + dt := &driverTester{t: t} + if err := Init(dt, nil); err != nil { + t.Fatal(err) + } + + if dt.d.Type() != testNetworkType { + t.Fatalf("Expected Type() to return %q. Instead got %q", testNetworkType, + dt.d.Type()) + } +} diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ivmanager/ivmanager.go b/libnetwork/drivers/ipvlan/ivmanager/ivmanager.go similarity index 93% rename from vendor/github.com/docker/libnetwork/drivers/ipvlan/ivmanager/ivmanager.go rename to libnetwork/drivers/ipvlan/ivmanager/ivmanager.go index 519f1e8795d45..2c8f71c639f9e 100644 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ivmanager/ivmanager.go +++ b/libnetwork/drivers/ipvlan/ivmanager/ivmanager.go @@ -1,10 +1,10 @@ package ivmanager import ( - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/types" ) const networkType = "ipvlan" diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan.go b/libnetwork/drivers/macvlan/macvlan.go similarity index 89% rename from vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan.go rename to libnetwork/drivers/macvlan/macvlan.go index 872e6f3ec1637..030323c6a0b99 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan.go +++ b/libnetwork/drivers/macvlan/macvlan.go @@ -1,14 +1,16 @@ +//go:build linux +// +build linux + package macvlan import ( "net" "sync" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/types" ) const ( @@ -50,7 +52,6 @@ type endpoint struct { type network struct { id string - sbox osl.Sandbox endpoints endpointTable driver *driver config *configuration @@ -66,7 +67,9 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { d := &driver{ networks: networkTable{}, } - d.initStore(config) + if err := d.initStore(config); err != nil { + return err + } return dc.RegisterDriver(macvlanType, d, c) } @@ -80,7 +83,7 @@ func (d *driver) NetworkFree(id string) error { } func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { - return make(map[string]interface{}, 0), nil + return make(map[string]interface{}), nil } func (d *driver) Type() string { diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_endpoint.go b/libnetwork/drivers/macvlan/macvlan_endpoint.go similarity index 88% rename from vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_endpoint.go rename to libnetwork/drivers/macvlan/macvlan_endpoint.go index dc3ce365433dc..8d4390f779f17 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_endpoint.go +++ b/libnetwork/drivers/macvlan/macvlan_endpoint.go @@ -1,14 +1,17 @@ +//go:build linux +// +build linux + package macvlan import ( "fmt" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) diff --git a/libnetwork/drivers/macvlan/macvlan_joinleave.go b/libnetwork/drivers/macvlan/macvlan_joinleave.go new file mode 100644 index 0000000000000..69f083328b691 --- /dev/null +++ b/libnetwork/drivers/macvlan/macvlan_joinleave.go @@ -0,0 +1,158 @@ +//go:build linux +// +build linux + +package macvlan + +import ( + "fmt" + "net" + + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/osl" + "github.com/sirupsen/logrus" +) + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + defer osl.InitOSContext()() + n, err := d.getNetwork(nid) + if err != nil { + return err + } + endpoint := n.endpoint(eid) + if endpoint == nil { + return fmt.Errorf("could not find endpoint with id %s", eid) + } + // generate a name for the iface that will be renamed to eth0 in the sbox + containerIfName, err := netutils.GenerateIfaceName(ns.NlHandle(), vethPrefix, vethLen) + if err != nil { + return fmt.Errorf("error generating an interface name: %s", err) + } + // create the netlink macvlan interface + vethName, err := createMacVlan(containerIfName, n.config.Parent, n.config.MacvlanMode) + if err != nil { + return err + } + // bind the generated iface name to the endpoint + endpoint.srcName = vethName + ep := n.endpoint(eid) + if ep == nil { + return fmt.Errorf("could not find endpoint with id %s", eid) + } + // parse and match the endpoint address with the available v4 subnets + if !n.config.Internal { + if len(n.config.Ipv4Subnets) > 0 { + s := n.getSubnetforIPv4(ep.addr) + if s == nil { + return fmt.Errorf("could not find a valid ipv4 subnet for endpoint %s", eid) + } + v4gw, _, err := net.ParseCIDR(s.GwIP) + if err != nil { + return fmt.Errorf("gateway %s is not a valid ipv4 address: %v", s.GwIP, err) + } + err = jinfo.SetGateway(v4gw) + if err != nil { + return err + } + logrus.Debugf("Macvlan Endpoint Joined with IPv4_Addr: %s, Gateway: %s, MacVlan_Mode: %s, Parent: %s", + ep.addr.IP.String(), v4gw.String(), n.config.MacvlanMode, n.config.Parent) + } + // parse and match the endpoint address with the available v6 subnets + if len(n.config.Ipv6Subnets) > 0 { + s := n.getSubnetforIPv6(ep.addrv6) + if s == nil { + return fmt.Errorf("could not find a valid ipv6 subnet for endpoint %s", eid) + } + v6gw, _, err := net.ParseCIDR(s.GwIP) + if err != nil { + return fmt.Errorf("gateway %s is not a valid ipv6 address: %v", s.GwIP, err) + } + err = jinfo.SetGatewayIPv6(v6gw) + if err != nil { + return err + } + logrus.Debugf("Macvlan Endpoint Joined with IPv6_Addr: %s Gateway: %s MacVlan_Mode: %s, Parent: %s", + ep.addrv6.IP.String(), v6gw.String(), n.config.MacvlanMode, n.config.Parent) + } + } else { + if len(n.config.Ipv4Subnets) > 0 { + logrus.Debugf("Macvlan Endpoint Joined with IPv4_Addr: %s, MacVlan_Mode: %s, Parent: %s", + ep.addr.IP.String(), n.config.MacvlanMode, n.config.Parent) + } + if len(n.config.Ipv6Subnets) > 0 { + logrus.Debugf("Macvlan Endpoint Joined with IPv6_Addr: %s MacVlan_Mode: %s, Parent: %s", + ep.addrv6.IP.String(), n.config.MacvlanMode, n.config.Parent) + } + } + iNames := jinfo.InterfaceName() + err = iNames.SetNames(vethName, containerVethPrefix) + if err != nil { + return err + } + if err := d.storeUpdate(ep); err != nil { + return fmt.Errorf("failed to save macvlan endpoint %.7s to store: %v", ep.id, err) + } + return nil +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid string) error { + defer osl.InitOSContext()() + network, err := d.getNetwork(nid) + if err != nil { + return err + } + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + if endpoint == nil { + return fmt.Errorf("could not find endpoint with id %s", eid) + } + + return nil +} + +// getSubnetforIP returns the ipv4 subnet to which the given IP belongs +func (n *network) getSubnetforIPv4(ip *net.IPNet) *ipv4Subnet { + for _, s := range n.config.Ipv4Subnets { + _, snet, err := net.ParseCIDR(s.SubnetIP) + if err != nil { + return nil + } + // first check if the mask lengths are the same + i, _ := snet.Mask.Size() + j, _ := ip.Mask.Size() + if i != j { + continue + } + if snet.Contains(ip.IP) { + return s + } + } + + return nil +} + +// getSubnetforIPv6 returns the ipv6 subnet to which the given IP belongs +func (n *network) getSubnetforIPv6(ip *net.IPNet) *ipv6Subnet { + for _, s := range n.config.Ipv6Subnets { + _, snet, err := net.ParseCIDR(s.SubnetIP) + if err != nil { + return nil + } + // first check if the mask lengths are the same + i, _ := snet.Mask.Size() + j, _ := ip.Mask.Size() + if i != j { + continue + } + if snet.Contains(ip.IP) { + return s + } + } + + return nil +} diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_network.go b/libnetwork/drivers/macvlan/macvlan_network.go similarity index 76% rename from vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_network.go rename to libnetwork/drivers/macvlan/macvlan_network.go index beeed41638dac..943e90df8550a 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_network.go +++ b/libnetwork/drivers/macvlan/macvlan_network.go @@ -1,31 +1,25 @@ +//go:build linux +// +build linux + package macvlan import ( "fmt" - "github.com/docker/docker/pkg/parsers/kernel" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/pkg/stringid" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/options" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" "github.com/sirupsen/logrus" ) // CreateNetwork the network for the specified driver type func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { defer osl.InitOSContext()() - kv, err := kernel.GetKernelVersion() - if err != nil { - return fmt.Errorf("failed to check kernel version for %s driver support: %v", macvlanType, err) - } - // ensure Kernel version is >= v3.9 for macvlan support - if kv.Kernel < macvlanKernelVer || (kv.Kernel == macvlanKernelVer && kv.Major < macvlanMajorVer) { - return fmt.Errorf("kernel version failed to meet the minimum macvlan kernel requirement of %d.%d, found %d.%d.%d", - macvlanKernelVer, macvlanMajorVer, kv.Kernel, kv.Major, kv.Minor) - } + // reject a null v4 network if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" { return fmt.Errorf("ipv4 pool is empty") @@ -61,13 +55,16 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo // if parent interface not specified, create a dummy type link to use named dummy+net_id if config.Parent == "" { config.Parent = getDummyName(stringid.TruncateID(config.ID)) - // empty parent and --internal are handled the same. Set here to update k/v - config.Internal = true } - err = d.createNetwork(config) + foundExisting, err := d.createNetwork(config) if err != nil { return err } + + if foundExisting { + return types.InternalMaskableErrorf("restoring existing network %s", config.ID) + } + // update persistent db, rollback on fail err = d.storeUpdate(config) if err != nil { @@ -80,48 +77,54 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo } // createNetwork is used by new network callbacks and persistent network cache -func (d *driver) createNetwork(config *configuration) error { +func (d *driver) createNetwork(config *configuration) (bool, error) { + foundExisting := false networkList := d.getNetworks() for _, nw := range networkList { if config.Parent == nw.config.Parent { - return fmt.Errorf("network %s is already using parent interface %s", - getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent) + if config.ID != nw.config.ID { + return false, fmt.Errorf("network %s is already using parent interface %s", + getDummyName(stringid.TruncateID(nw.config.ID)), config.Parent) + } + logrus.Debugf("Create Network for the same ID %s\n", config.ID) + foundExisting = true + break } } if !parentExists(config.Parent) { - // if the --internal flag is set, create a dummy link - if config.Internal { - err := createDummyLink(config.Parent, getDummyName(stringid.TruncateID(config.ID))) + // Create a dummy link if a dummy name is set for parent + if dummyName := getDummyName(stringid.TruncateID(config.ID)); dummyName == config.Parent { + err := createDummyLink(config.Parent, dummyName) if err != nil { - return err + return false, err } config.CreatedSlaveLink = true - // notify the user in logs they have limited communications - if config.Parent == getDummyName(stringid.TruncateID(config.ID)) { - logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s", - config.Parent) - } + // notify the user in logs that they have limited communications + logrus.Debugf("Empty -o parent= limit communications to other containers inside of network: %s", + config.Parent) } else { // if the subinterface parent_iface.vlan_id checks do not pass, return err. // a valid example is 'eth0.10' for a parent iface 'eth0' with a vlan id '10' err := createVlanLink(config.Parent) if err != nil { - return err + return false, err } // if driver created the networks slave link, record it for future deletion config.CreatedSlaveLink = true } } - n := &network{ - id: config.ID, - driver: d, - endpoints: endpointTable{}, - config: config, + if !foundExisting { + n := &network{ + id: config.ID, + driver: d, + endpoints: endpointTable{}, + config: config, + } + // add the network + d.addNetwork(n) } - // add the *network - d.addNetwork(n) - return nil + return foundExisting, nil } // DeleteNetwork deletes the network for the specified driver type @@ -185,11 +188,10 @@ func parseNetworkOptions(id string, option options.Generic) (*configuration, err return nil, err } } - // setting the parent to "" will trigger an isolated network dummy parent link - if _, ok := option[netlabel.Internal]; ok { - config.Internal = true - // empty --parent= and --internal are handled the same. - config.Parent = "" + if val, ok := option[netlabel.Internal]; ok { + if internal, ok := val.(bool); ok && internal { + config.Internal = true + } } return config, nil diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_setup.go b/libnetwork/drivers/macvlan/macvlan_setup.go similarity index 94% rename from vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_setup.go rename to libnetwork/drivers/macvlan/macvlan_setup.go index fc33ebd707e48..7415a07a2739d 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_setup.go +++ b/libnetwork/drivers/macvlan/macvlan_setup.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package macvlan import ( @@ -5,15 +8,13 @@ import ( "strconv" "strings" - "github.com/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/ns" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" ) const ( - dummyPrefix = "dm-" // macvlan prefix for dummy parent interface - macvlanKernelVer = 3 // minimum macvlan kernel support - macvlanMajorVer = 9 // minimum macvlan major kernel support + dummyPrefix = "dm-" // macvlan prefix for dummy parent interface ) // Create the macvlan slave specifying the source name @@ -67,11 +68,7 @@ func setMacVlanMode(mode string) (netlink.MacvlanMode, error) { // parentExists checks if the specified interface exists in the default namespace func parentExists(ifaceStr string) bool { _, err := ns.NlHandle().LinkByName(ifaceStr) - if err != nil { - return false - } - - return true + return err == nil } // createVlanLink parses sub-interfaces and vlan id for creation @@ -154,7 +151,7 @@ func parseVlan(linkName string) (string, int, error) { } // Check if the interface exists if !parentExists(parent) { - return "", 0, fmt.Errorf("-o parent interface does was not found on the host: %s", parent) + return "", 0, fmt.Errorf("-o parent interface was not found on the host: %s", parent) } return parent, vidInt, nil diff --git a/libnetwork/drivers/macvlan/macvlan_setup_test.go b/libnetwork/drivers/macvlan/macvlan_setup_test.go new file mode 100644 index 0000000000000..f2053c16796fb --- /dev/null +++ b/libnetwork/drivers/macvlan/macvlan_setup_test.go @@ -0,0 +1,106 @@ +//go:build linux +// +build linux + +package macvlan + +import ( + "testing" + + "github.com/vishvananda/netlink" +) + +// TestValidateLink tests the parentExists function +func TestValidateLink(t *testing.T) { + validIface := "lo" + invalidIface := "foo12345" + + // test a valid parent interface validation + if ok := parentExists(validIface); !ok { + t.Fatalf("failed validating loopback %s", validIface) + } + // test an invalid parent interface validation + if ok := parentExists(invalidIface); ok { + t.Fatalf("failed to invalidate interface %s", invalidIface) + } +} + +// TestValidateSubLink tests valid 802.1q naming convention +func TestValidateSubLink(t *testing.T) { + validSubIface := "lo.10" + invalidSubIface1 := "lo" + invalidSubIface2 := "lo:10" + invalidSubIface3 := "foo123.456" + + // test a valid parent_iface.vlan_id + _, _, err := parseVlan(validSubIface) + if err != nil { + t.Fatalf("failed subinterface validation: %v", err) + } + // test an invalid vid with a valid parent link + _, _, err = parseVlan(invalidSubIface1) + if err == nil { + t.Fatalf("failed subinterface validation test: %s", invalidSubIface1) + } + // test a valid vid with a valid parent link with an invalid delimiter + _, _, err = parseVlan(invalidSubIface2) + if err == nil { + t.Fatalf("failed subinterface validation test: %v", invalidSubIface2) + } + // test an invalid parent link with a valid vid + _, _, err = parseVlan(invalidSubIface3) + if err == nil { + t.Fatalf("failed subinterface validation test: %v", invalidSubIface3) + } +} + +// TestSetMacVlanMode tests the macvlan mode setter +func TestSetMacVlanMode(t *testing.T) { + // test macvlan bridge mode + mode, err := setMacVlanMode(modeBridge) + if err != nil { + t.Fatalf("error parsing %v vlan mode: %v", mode, err) + } + if mode != netlink.MACVLAN_MODE_BRIDGE { + t.Fatalf("expected %d got %d", netlink.MACVLAN_MODE_BRIDGE, mode) + } + // test macvlan passthrough mode + mode, err = setMacVlanMode(modePassthru) + if err != nil { + t.Fatalf("error parsing %v vlan mode: %v", mode, err) + } + if mode != netlink.MACVLAN_MODE_PASSTHRU { + t.Fatalf("expected %d got %d", netlink.MACVLAN_MODE_PASSTHRU, mode) + } + // test macvlan private mode + mode, err = setMacVlanMode(modePrivate) + if err != nil { + t.Fatalf("error parsing %v vlan mode: %v", mode, err) + } + if mode != netlink.MACVLAN_MODE_PRIVATE { + t.Fatalf("expected %d got %d", netlink.MACVLAN_MODE_PRIVATE, mode) + } + // test macvlan vepa mode + mode, err = setMacVlanMode(modeVepa) + if err != nil { + t.Fatalf("error parsing %v vlan mode: %v", mode, err) + } + if mode != netlink.MACVLAN_MODE_VEPA { + t.Fatalf("expected %d got %d", netlink.MACVLAN_MODE_VEPA, mode) + } + // test invalid mode + mode, err = setMacVlanMode("foo") + if err == nil { + t.Fatal("invalid macvlan mode should have returned an error") + } + if mode != 0 { + t.Fatalf("expected 0 got %d", mode) + } + // test null mode + mode, err = setMacVlanMode("") + if err == nil { + t.Fatal("invalid macvlan mode should have returned an error") + } + if mode != 0 { + t.Fatalf("expected 0 got %d", mode) + } +} diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_state.go b/libnetwork/drivers/macvlan/macvlan_state.go similarity index 87% rename from vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_state.go rename to libnetwork/drivers/macvlan/macvlan_state.go index 8fd1a9e4dc299..bb3e326867b56 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_state.go +++ b/libnetwork/drivers/macvlan/macvlan_state.go @@ -1,10 +1,12 @@ +//go:build linux +// +build linux + package macvlan import ( "fmt" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -86,19 +88,6 @@ func validateID(nid, eid string) error { return nil } -func (n *network) sandbox() osl.Sandbox { - n.Lock() - defer n.Unlock() - - return n.sbox -} - -func (n *network) setSandbox(sbox osl.Sandbox) { - n.Lock() - n.sbox = sbox - n.Unlock() -} - func (d *driver) getNetwork(id string) (*network, error) { d.Lock() defer d.Unlock() diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go b/libnetwork/drivers/macvlan/macvlan_store.go similarity index 95% rename from vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go rename to libnetwork/drivers/macvlan/macvlan_store.go index 8683cacd02c82..5cc04eac8e04b 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_store.go +++ b/libnetwork/drivers/macvlan/macvlan_store.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package macvlan import ( @@ -5,10 +8,10 @@ import ( "fmt" "net" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -55,7 +58,15 @@ func (d *driver) initStore(option map[string]interface{}) error { return types.InternalErrorf("macvlan driver failed to initialize data store: %v", err) } - return d.populateNetworks() + err = d.populateNetworks() + if err != nil { + return err + } + err = d.populateEndpoints() + if err != nil { + return err + } + } return nil @@ -73,7 +84,7 @@ func (d *driver) populateNetworks() error { } for _, kvo := range kvol { config := kvo.(*configuration) - if err = d.createNetwork(config); err != nil { + if _, err = d.createNetwork(config); err != nil { logrus.Warnf("Could not create macvlan network for id %s from persistent state", config.ID) } } diff --git a/libnetwork/drivers/macvlan/macvlan_test.go b/libnetwork/drivers/macvlan/macvlan_test.go new file mode 100644 index 0000000000000..0439ffdc0d230 --- /dev/null +++ b/libnetwork/drivers/macvlan/macvlan_test.go @@ -0,0 +1,67 @@ +//go:build linux +// +build linux + +package macvlan + +import ( + "testing" + + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/pkg/plugingetter" +) + +const testNetworkType = "macvlan" + +type driverTester struct { + t *testing.T + d *driver +} + +func (dt *driverTester) GetPluginGetter() plugingetter.PluginGetter { + return nil +} + +func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver, + cap driverapi.Capability) error { + if name != testNetworkType { + dt.t.Fatalf("Expected driver register name to be %q. Instead got %q", + testNetworkType, name) + } + + if _, ok := drv.(*driver); !ok { + dt.t.Fatalf("Expected driver type to be %T. Instead got %T", + &driver{}, drv) + } + + dt.d = drv.(*driver) + return nil +} + +func TestMacvlanInit(t *testing.T) { + if err := Init(&driverTester{t: t}, nil); err != nil { + t.Fatal(err) + } +} + +func TestMacvlanNilConfig(t *testing.T) { + dt := &driverTester{t: t} + if err := Init(dt, nil); err != nil { + t.Fatal(err) + } + + if err := dt.d.initStore(nil); err != nil { + t.Fatal(err) + } +} + +func TestMacvlanType(t *testing.T) { + dt := &driverTester{t: t} + if err := Init(dt, nil); err != nil { + t.Fatal(err) + } + + if dt.d.Type() != testNetworkType { + t.Fatalf("Expected Type() to return %q. Instead got %q", testNetworkType, + dt.d.Type()) + } +} diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/mvmanager/mvmanager.go b/libnetwork/drivers/macvlan/mvmanager/mvmanager.go similarity index 93% rename from vendor/github.com/docker/libnetwork/drivers/macvlan/mvmanager/mvmanager.go rename to libnetwork/drivers/macvlan/mvmanager/mvmanager.go index 0f811ac36f857..1e3d3473d43ef 100644 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/mvmanager/mvmanager.go +++ b/libnetwork/drivers/macvlan/mvmanager/mvmanager.go @@ -1,10 +1,10 @@ package mvmanager import ( - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/types" ) const networkType = "macvlan" diff --git a/libnetwork/drivers/null/null.go b/libnetwork/drivers/null/null.go new file mode 100644 index 0000000000000..c54db37513ec5 --- /dev/null +++ b/libnetwork/drivers/null/null.go @@ -0,0 +1,105 @@ +package null + +import ( + "sync" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/types" +) + +const networkType = "null" + +type driver struct { + network string + sync.Mutex +} + +// Init registers a new instance of null driver +func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { + c := driverapi.Capability{ + DataScope: datastore.LocalScope, + } + return dc.RegisterDriver(networkType, &driver{}, c) +} + +func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { + return nil, types.NotImplementedErrorf("not implemented") +} + +func (d *driver) NetworkFree(id string) error { + return types.NotImplementedErrorf("not implemented") +} + +func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { +} + +func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { + return "", nil +} + +func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { + d.Lock() + defer d.Unlock() + + if d.network != "" { + return types.ForbiddenErrorf("only one instance of \"%s\" network is allowed", networkType) + } + + d.network = id + + return nil +} + +func (d *driver) DeleteNetwork(nid string) error { + return types.ForbiddenErrorf("network of type \"%s\" cannot be deleted", networkType) +} + +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { + return nil +} + +func (d *driver) DeleteEndpoint(nid, eid string) error { + return nil +} + +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { + return make(map[string]interface{}), nil +} + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + return nil +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid string) error { + return nil +} + +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + +func (d *driver) Type() string { + return networkType +} + +func (d *driver) IsBuiltIn() bool { + return true +} + +// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster +func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster +func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} diff --git a/libnetwork/drivers/null/null_test.go b/libnetwork/drivers/null/null_test.go new file mode 100644 index 0000000000000..1c76d3760be77 --- /dev/null +++ b/libnetwork/drivers/null/null_test.go @@ -0,0 +1,49 @@ +package null + +import ( + "testing" + + "github.com/docker/docker/libnetwork/types" +) + +func TestDriver(t *testing.T) { + d := &driver{} + + if d.Type() != networkType { + t.Fatalf("Unexpected network type returned by driver") + } + + err := d.CreateNetwork("first", nil, nil, nil, nil) + if err != nil { + t.Fatal(err) + } + + if d.network != "first" { + t.Fatalf("Unexpected network id stored") + } + + err = d.CreateNetwork("second", nil, nil, nil, nil) + if err == nil { + t.Fatalf("Second network creation should fail on this driver") + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Second network creation failed with unexpected error type") + } + + err = d.DeleteNetwork("first") + if err == nil { + t.Fatalf("network deletion should fail on this driver") + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("network deletion failed with unexpected error type") + } + + // we don't really check if it is there or not, delete is not allowed for this driver, period. + err = d.DeleteNetwork("unknown") + if err == nil { + t.Fatalf("any network deletion should fail on this driver") + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("any network deletion failed with unexpected error type") + } +} diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go b/libnetwork/drivers/overlay/encryption.go similarity index 94% rename from vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go rename to libnetwork/drivers/overlay/encryption.go index a97e73df82bc9..c5ab835cf31cc 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go +++ b/libnetwork/drivers/overlay/encryption.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package overlay import ( @@ -12,9 +15,10 @@ import ( "strconv" - "github.com/docker/libnetwork/iptables" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/drivers/overlay/overlayutils" + "github.com/docker/docker/libnetwork/iptables" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" ) @@ -200,7 +204,7 @@ func removeEncryption(localIP, remoteIP net.IP, em *encrMap) error { func programMangle(vni uint32, add bool) (err error) { var ( - p = strconv.FormatUint(uint64(vxlanPort), 10) + p = strconv.FormatUint(uint64(overlayutils.VXLANUDPPort()), 10) c = fmt.Sprintf("0>>22&0x3C@12&0xFFFFFF00=%d", int(vni)<<8) m = strconv.FormatUint(uint64(r), 10) chain = "OUTPUT" @@ -209,7 +213,10 @@ func programMangle(vni uint32, add bool) (err error) { action = "install" ) - if add == iptables.Exists(iptables.Mangle, chain, rule...) { + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) + + if add == iptable.Exists(iptables.Mangle, chain, rule...) { return } @@ -218,7 +225,7 @@ func programMangle(vni uint32, add bool) (err error) { action = "remove" } - if err = iptables.RawCombinedOutput(append([]string{"-t", string(iptables.Mangle), a, chain}, rule...)...); err != nil { + if err = iptable.RawCombinedOutput(append([]string{"-t", string(iptables.Mangle), a, chain}, rule...)...); err != nil { logrus.Warnf("could not %s mangle rule: %v", action, err) } @@ -227,7 +234,7 @@ func programMangle(vni uint32, add bool) (err error) { func programInput(vni uint32, add bool) (err error) { var ( - port = strconv.FormatUint(uint64(vxlanPort), 10) + port = strconv.FormatUint(uint64(overlayutils.VXLANUDPPort()), 10) vniMatch = fmt.Sprintf("0>>22&0x3C@12&0xFFFFFF00=%d", int(vni)<<8) plainVxlan = []string{"-p", "udp", "--dport", port, "-m", "u32", "--u32", vniMatch, "-j"} ipsecVxlan = append([]string{"-m", "policy", "--dir", "in", "--pol", "ipsec"}, plainVxlan...) @@ -238,16 +245,19 @@ func programInput(vni uint32, add bool) (err error) { msg = "add" ) + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) + if !add { action = iptables.Delete msg = "remove" } - if err := iptables.ProgramRule(iptables.Filter, chain, action, accept); err != nil { + if err := iptable.ProgramRule(iptables.Filter, chain, action, accept); err != nil { logrus.Errorf("could not %s input rule: %v. Please do it manually.", msg, err) } - if err := iptables.ProgramRule(iptables.Filter, chain, action, block); err != nil { + if err := iptable.ProgramRule(iptables.Filter, chain, action, block); err != nil { logrus.Errorf("could not %s input rule: %v. Please do it manually.", msg, err) } @@ -619,6 +629,7 @@ func clearEncryptionStates() { logrus.Warnf("Failed to retrieve SA list for cleanup: %v", err) } for _, sp := range spList { + sp := sp if sp.Mark != nil && sp.Mark.Value == spMark.Value { if err := nlh.XfrmPolicyDel(&sp); err != nil { logrus.Warnf("Failed to delete stale SP %s: %v", sp, err) @@ -628,6 +639,7 @@ func clearEncryptionStates() { } } for _, sa := range saList { + sa := sa if sa.Reqid == r { if err := nlh.XfrmStateDel(&sa); err != nil { logrus.Warnf("Failed to delete stale SA %s: %v", sa, err) diff --git a/libnetwork/drivers/overlay/filter.go b/libnetwork/drivers/overlay/filter.go new file mode 100644 index 0000000000000..31a8c1f43f1c9 --- /dev/null +++ b/libnetwork/drivers/overlay/filter.go @@ -0,0 +1,153 @@ +//go:build linux +// +build linux + +package overlay + +import ( + "fmt" + "sync" + + "github.com/docker/docker/libnetwork/iptables" + "github.com/sirupsen/logrus" +) + +const globalChain = "DOCKER-OVERLAY" + +var filterOnce sync.Once + +var filterChan = make(chan struct{}, 1) + +func filterWait() func() { + filterChan <- struct{}{} + return func() { <-filterChan } +} + +func chainExists(cname string) bool { + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) + if _, err := iptable.Raw("-L", cname); err != nil { + return false + } + + return true +} + +func setupGlobalChain() { + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) + // Because of an ungraceful shutdown, chain could already be present + if !chainExists(globalChain) { + if err := iptable.RawCombinedOutput("-N", globalChain); err != nil { + logrus.Errorf("could not create global overlay chain: %v", err) + return + } + } + + if !iptable.Exists(iptables.Filter, globalChain, "-j", "RETURN") { + if err := iptable.RawCombinedOutput("-A", globalChain, "-j", "RETURN"); err != nil { + logrus.Errorf("could not install default return chain in the overlay global chain: %v", err) + } + } +} + +func setNetworkChain(cname string, remove bool) error { + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) + // Initialize the onetime global overlay chain + filterOnce.Do(setupGlobalChain) + + exists := chainExists(cname) + + opt := "-N" + // In case of remove, make sure to flush the rules in the chain + if remove && exists { + if err := iptable.RawCombinedOutput("-F", cname); err != nil { + return fmt.Errorf("failed to flush overlay network chain %s rules: %v", cname, err) + } + opt = "-X" + } + + if (!remove && !exists) || (remove && exists) { + if err := iptable.RawCombinedOutput(opt, cname); err != nil { + return fmt.Errorf("failed network chain operation %q for chain %s: %v", opt, cname, err) + } + } + + if !remove { + if !iptable.Exists(iptables.Filter, cname, "-j", "DROP") { + if err := iptable.RawCombinedOutput("-A", cname, "-j", "DROP"); err != nil { + return fmt.Errorf("failed adding default drop rule to overlay network chain %s: %v", cname, err) + } + } + } + + return nil +} + +func addNetworkChain(cname string) error { + defer filterWait()() + + return setNetworkChain(cname, false) +} + +func removeNetworkChain(cname string) error { + defer filterWait()() + + return setNetworkChain(cname, true) +} + +func setFilters(cname, brName string, remove bool) error { + opt := "-I" + if remove { + opt = "-D" + } + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) + + // Every time we set filters for a new subnet make sure to move the global overlay hook to the top of the both the OUTPUT and forward chains + if !remove { + for _, chain := range []string{"OUTPUT", "FORWARD"} { + exists := iptable.Exists(iptables.Filter, chain, "-j", globalChain) + if exists { + if err := iptable.RawCombinedOutput("-D", chain, "-j", globalChain); err != nil { + return fmt.Errorf("failed to delete overlay hook in chain %s while moving the hook: %v", chain, err) + } + } + + if err := iptable.RawCombinedOutput("-I", chain, "-j", globalChain); err != nil { + return fmt.Errorf("failed to insert overlay hook in chain %s: %v", chain, err) + } + } + } + + // Insert/Delete the rule to jump to per-bridge chain + exists := iptable.Exists(iptables.Filter, globalChain, "-o", brName, "-j", cname) + if (!remove && !exists) || (remove && exists) { + if err := iptable.RawCombinedOutput(opt, globalChain, "-o", brName, "-j", cname); err != nil { + return fmt.Errorf("failed to add per-bridge filter rule for bridge %s, network chain %s: %v", brName, cname, err) + } + } + + exists = iptable.Exists(iptables.Filter, cname, "-i", brName, "-j", "ACCEPT") + if (!remove && exists) || (remove && !exists) { + return nil + } + + if err := iptable.RawCombinedOutput(opt, cname, "-i", brName, "-j", "ACCEPT"); err != nil { + return fmt.Errorf("failed to add overlay filter rile for network chain %s, bridge %s: %v", cname, brName, err) + } + + return nil +} + +func addFilters(cname, brName string) error { + defer filterWait()() + + return setFilters(cname, brName, false) +} + +func removeFilters(cname, brName string) error { + defer filterWait()() + + return setFilters(cname, brName, true) +} diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go b/libnetwork/drivers/overlay/joinleave.go similarity index 97% rename from vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go rename to libnetwork/drivers/overlay/joinleave.go index a51bcd8985f95..69b6d87068e17 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go +++ b/libnetwork/drivers/overlay/joinleave.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package overlay import ( @@ -5,9 +8,9 @@ import ( "net" "syscall" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/types" "github.com/gogo/protobuf/proto" "github.com/sirupsen/logrus" ) diff --git a/libnetwork/drivers/overlay/ostweaks_linux.go b/libnetwork/drivers/overlay/ostweaks_linux.go new file mode 100644 index 0000000000000..15f2a533eaada --- /dev/null +++ b/libnetwork/drivers/overlay/ostweaks_linux.go @@ -0,0 +1,23 @@ +package overlay + +import ( + "strconv" + + "github.com/docker/docker/libnetwork/osl/kernel" +) + +var ovConfig = map[string]*kernel.OSValue{ + "net.ipv4.neigh.default.gc_thresh1": {Value: "8192", CheckFn: checkHigher}, + "net.ipv4.neigh.default.gc_thresh2": {Value: "49152", CheckFn: checkHigher}, + "net.ipv4.neigh.default.gc_thresh3": {Value: "65536", CheckFn: checkHigher}, +} + +func checkHigher(val1, val2 string) bool { + val1Int, _ := strconv.ParseInt(val1, 10, 32) + val2Int, _ := strconv.ParseInt(val2, 10, 32) + return val1Int < val2Int +} + +func applyOStweaks() { + kernel.ApplyOSTweaks(ovConfig) +} diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/ostweaks_unsupported.go b/libnetwork/drivers/overlay/ostweaks_unsupported.go similarity index 76% rename from vendor/github.com/docker/libnetwork/drivers/overlay/ostweaks_unsupported.go rename to libnetwork/drivers/overlay/ostweaks_unsupported.go index a5e8d910839d3..a90019e2d6dea 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/ostweaks_unsupported.go +++ b/libnetwork/drivers/overlay/ostweaks_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package overlay diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go b/libnetwork/drivers/overlay/ov_endpoint.go similarity index 94% rename from vendor/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go rename to libnetwork/drivers/overlay/ov_endpoint.go index 0aaaac59d9007..76619966d31fe 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go +++ b/libnetwork/drivers/overlay/ov_endpoint.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package overlay import ( @@ -5,11 +8,11 @@ import ( "fmt" "net" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -136,7 +139,7 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { } func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { - return make(map[string]interface{}, 0), nil + return make(map[string]interface{}), nil } func (d *driver) deleteEndpointFromStore(e *endpoint) error { diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go b/libnetwork/drivers/overlay/ov_network.go similarity index 94% rename from vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go rename to libnetwork/drivers/overlay/ov_network.go index cf32e45951fc5..529f5297d6161 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go +++ b/libnetwork/drivers/overlay/ov_network.go @@ -1,9 +1,11 @@ +//go:build linux +// +build linux + package overlay import ( "encoding/json" "fmt" - "io/ioutil" "net" "os" "os/exec" @@ -12,21 +14,21 @@ import ( "strconv" "strings" "sync" - "syscall" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/resolvconf" + "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/pkg/reexec" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/resolvconf" - "github.com/docker/libnetwork/types" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" "github.com/vishvananda/netlink/nl" "github.com/vishvananda/netns" + "golang.org/x/sys/unix" ) var ( @@ -97,18 +99,18 @@ func setDefaultVlan() { } // make sure the sysfs mount doesn't propagate back - if err = syscall.Unshare(syscall.CLONE_NEWNS); err != nil { + if err = unix.Unshare(unix.CLONE_NEWNS); err != nil { logrus.Errorf("unshare failed, %v", err) os.Exit(1) } - flag := syscall.MS_PRIVATE | syscall.MS_REC - if err = syscall.Mount("", "/", "", uintptr(flag), ""); err != nil { + flag := unix.MS_PRIVATE | unix.MS_REC + if err = unix.Mount("", "/", "", uintptr(flag), ""); err != nil { logrus.Errorf("root mount failed, %v", err) os.Exit(1) } - if err = syscall.Mount("sysfs", "/sys", "sysfs", 0, ""); err != nil { + if err = unix.Mount("sysfs", "/sys", "sysfs", 0, ""); err != nil { logrus.Errorf("mounting sysfs failed, %v", err) os.Exit(1) } @@ -117,7 +119,7 @@ func setDefaultVlan() { path := filepath.Join("/sys/class/net", brName, "bridge/default_pvid") data := []byte{'0', '\n'} - if err = ioutil.WriteFile(path, data, 0644); err != nil { + if err = os.WriteFile(path, data, 0644); err != nil { logrus.Errorf("enabling default vlan on bridge %s failed %v", brName, err) os.Exit(1) } @@ -413,7 +415,9 @@ func (n *network) destroySandbox() { func populateVNITbl() { filepath.Walk(filepath.Dir(osl.GenerateKey("walk")), - func(path string, info os.FileInfo, err error) error { + // NOTE(cpuguy83): The linter picked up on the fact that this walk function was not using this error argument + // That seems wrong... however I'm not familiar with this code or if that error matters + func(path string, info os.FileInfo, _ error) error { _, fname := filepath.Split(path) if len(strings.Split(fname, "-")) <= 1 { @@ -427,7 +431,7 @@ func populateVNITbl() { } defer ns.Close() - nlh, err := netlink.NewHandleAt(ns, syscall.NETLINK_ROUTE) + nlh, err := netlink.NewHandleAt(ns, unix.NETLINK_ROUTE) if err != nil { logrus.Errorf("Could not open netlink handle during vni population for ns %s: %v", path, err) return nil @@ -583,7 +587,7 @@ func (n *network) setupSubnetSandbox(s *subnet, brName, vxlanName string) error if ok { deleteVxlanByVNI(path, s.vni) - if err := syscall.Unmount(path, syscall.MNT_FORCE); err != nil { + if err := unix.Unmount(path, unix.MNT_FORCE); err != nil { logrus.Errorf("unmount of %s failed: %v", path, err) } os.Remove(path) @@ -693,7 +697,7 @@ func (n *network) cleanupStaleSandboxes() { if strings.Contains(n.id, pattern) { // Delete all vnis deleteVxlanByVNI(path, 0) - syscall.Unmount(path, syscall.MNT_DETACH) + unix.Unmount(path, unix.MNT_DETACH) os.Remove(path) // Now that we have destroyed this @@ -755,12 +759,12 @@ func (n *network) initSandbox(restore bool) error { var nlSock *nl.NetlinkSocket sbox.InvokeFunc(func() { - nlSock, err = nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH) + nlSock, err = nl.Subscribe(unix.NETLINK_ROUTE, unix.RTNLGRP_NEIGH) if err != nil { return } // set the receive timeout to not remain stuck on the RecvFrom if the fd gets closed - tv := syscall.NsecToTimeval(soTimeout.Nanoseconds()) + tv := unix.NsecToTimeval(soTimeout.Nanoseconds()) err = nlSock.SetReceiveTimeout(&tv) }) n.nlSocket = nlSock @@ -793,7 +797,7 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket, nsPath string) { return } for { - msgs, err := nlSock.Receive() + msgs, _, err := nlSock.Receive() if err != nil { n.Lock() nlFd := nlSock.GetFd() @@ -803,7 +807,7 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket, nsPath string) { return } // When the receive timeout expires the receive will return EAGAIN - if err == syscall.EAGAIN { + if err == unix.EAGAIN { // we continue here to avoid spam for timeouts continue } @@ -812,7 +816,7 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket, nsPath string) { } for _, msg := range msgs { - if msg.Header.Type != syscall.RTM_GETNEIGH && msg.Header.Type != syscall.RTM_NEWNEIGH { + if msg.Header.Type != unix.RTM_GETNEIGH && msg.Header.Type != unix.RTM_NEWNEIGH { continue } diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_serf.go b/libnetwork/drivers/overlay/ov_serf.go similarity index 98% rename from vendor/github.com/docker/libnetwork/drivers/overlay/ov_serf.go rename to libnetwork/drivers/overlay/ov_serf.go index f644799afd181..07b955227ad2e 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_serf.go +++ b/libnetwork/drivers/overlay/ov_serf.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package overlay import ( @@ -149,7 +152,7 @@ func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.I return nil, nil, nil, fmt.Errorf("could not resolve peer: serf instance not initialized") } - qPayload := fmt.Sprintf("%s %s", string(nid), peerIP.String()) + qPayload := fmt.Sprintf("%s %s", nid, peerIP.String()) resp, err := d.serfInstance.Query("peerlookup", []byte(qPayload), nil) if err != nil { return nil, nil, nil, fmt.Errorf("resolving peer by querying the cluster failed: %v", err) diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_utils.go b/libnetwork/drivers/overlay/ov_utils.go similarity index 93% rename from vendor/github.com/docker/libnetwork/drivers/overlay/ov_utils.go rename to libnetwork/drivers/overlay/ov_utils.go index 27f57c1fe228d..0db0102d04e4f 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/ov_utils.go +++ b/libnetwork/drivers/overlay/ov_utils.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package overlay import ( @@ -5,9 +8,10 @@ import ( "strings" "syscall" - "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/drivers/overlay/overlayutils" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/osl" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" @@ -61,7 +65,7 @@ func createVxlan(name string, vni uint32, mtu int) error { LinkAttrs: netlink.LinkAttrs{Name: name, MTU: mtu}, VxlanId: int(vni), Learning: true, - Port: vxlanPort, + Port: int(overlayutils.VXLANUDPPort()), Proxy: true, L3miss: true, L2miss: true, diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.go b/libnetwork/drivers/overlay/overlay.go similarity index 94% rename from vendor/github.com/docker/libnetwork/drivers/overlay/overlay.go rename to libnetwork/drivers/overlay/overlay.go index 1bbd761c2f996..50263c3d79f29 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.go +++ b/libnetwork/drivers/overlay/overlay.go @@ -1,6 +1,9 @@ +//go:build linux +// +build linux + package overlay -//go:generate protoc -I.:../../Godeps/_workspace/src/github.com/gogo/protobuf --gogo_out=import_path=github.com/docker/libnetwork/drivers/overlay,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. overlay.proto +//go:generate protoc -I.:../../Godeps/_workspace/src/github.com/gogo/protobuf --gogo_out=import_path=github.com/docker/docker/libnetwork/drivers/overlay,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. overlay.proto import ( "context" @@ -8,13 +11,13 @@ import ( "net" "sync" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/idm" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/idm" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/types" "github.com/hashicorp/serf/serf" "github.com/sirupsen/logrus" ) @@ -25,7 +28,6 @@ const ( vethLen = 7 vxlanIDStart = 256 vxlanIDEnd = (1 << 24) - 1 - vxlanPort = 4789 vxlanEncap = 50 secureOption = "encrypted" ) @@ -379,7 +381,7 @@ func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) } } if err := d.updateKeys(newKey, priKey, delKey); err != nil { - logrus.Warn(err) + return err } default: } diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.pb.go b/libnetwork/drivers/overlay/overlay.pb.go similarity index 100% rename from vendor/github.com/docker/libnetwork/drivers/overlay/overlay.pb.go rename to libnetwork/drivers/overlay/overlay.pb.go diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/overlay.proto b/libnetwork/drivers/overlay/overlay.proto similarity index 100% rename from vendor/github.com/docker/libnetwork/drivers/overlay/overlay.proto rename to libnetwork/drivers/overlay/overlay.proto diff --git a/libnetwork/drivers/overlay/overlay_test.go b/libnetwork/drivers/overlay/overlay_test.go new file mode 100644 index 0000000000000..55194c5730257 --- /dev/null +++ b/libnetwork/drivers/overlay/overlay_test.go @@ -0,0 +1,179 @@ +//go:build linux +// +build linux + +package overlay + +import ( + "context" + "fmt" + "net" + "os" + "syscall" + "testing" + "time" + + "golang.org/x/sys/unix" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/pkg/plugingetter" + "github.com/docker/libkv/store/consul" + "github.com/vishvananda/netlink/nl" +) + +func init() { + consul.Register() +} + +type driverTester struct { + t *testing.T + d *driver +} + +const testNetworkType = "overlay" + +func setupDriver(t *testing.T) *driverTester { + dt := &driverTester{t: t} + config := make(map[string]interface{}) + config[netlabel.GlobalKVClient] = discoverapi.DatastoreConfigData{ + Scope: datastore.GlobalScope, + Provider: "consul", + Address: "127.0.0.01:8500", + } + + if err := Init(dt, config); err != nil { + t.Fatal(err) + } + + iface, err := net.InterfaceByName("eth0") + if err != nil { + t.Fatal(err) + } + addrs, err := iface.Addrs() + if err != nil || len(addrs) == 0 { + t.Fatal(err) + } + data := discoverapi.NodeDiscoveryData{ + Address: addrs[0].String(), + Self: true, + } + dt.d.DiscoverNew(discoverapi.NodeDiscovery, data) + return dt +} + +func cleanupDriver(t *testing.T, dt *driverTester) { + ch := make(chan struct{}) + go func() { + Fini(dt.d) + close(ch) + }() + + select { + case <-ch: + case <-time.After(10 * time.Second): + t.Fatal("test timed out because Fini() did not return on time") + } +} + +func (dt *driverTester) GetPluginGetter() plugingetter.PluginGetter { + return nil +} + +func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver, + cap driverapi.Capability) error { + if name != testNetworkType { + dt.t.Fatalf("Expected driver register name to be %q. Instead got %q", + testNetworkType, name) + } + + if _, ok := drv.(*driver); !ok { + dt.t.Fatalf("Expected driver type to be %T. Instead got %T", + &driver{}, drv) + } + + dt.d = drv.(*driver) + return nil +} + +func TestOverlayInit(t *testing.T) { + if err := Init(&driverTester{t: t}, nil); err != nil { + t.Fatal(err) + } +} + +func TestOverlayFiniWithoutConfig(t *testing.T) { + dt := &driverTester{t: t} + if err := Init(dt, nil); err != nil { + t.Fatal(err) + } + + cleanupDriver(t, dt) +} + +func TestOverlayConfig(t *testing.T) { + dt := setupDriver(t) + + time.Sleep(1 * time.Second) + + d := dt.d + if d.notifyCh == nil { + t.Fatal("Driver notify channel wasn't initialized after Config method") + } + + if d.exitCh == nil { + t.Fatal("Driver serfloop exit channel wasn't initialized after Config method") + } + + if d.serfInstance == nil { + t.Fatal("Driver serfinstance hasn't been initialized after Config method") + } + + cleanupDriver(t, dt) +} + +func TestOverlayType(t *testing.T) { + dt := &driverTester{t: t} + if err := Init(dt, nil); err != nil { + t.Fatal(err) + } + + if dt.d.Type() != testNetworkType { + t.Fatalf("Expected Type() to return %q. Instead got %q", testNetworkType, + dt.d.Type()) + } +} + +// Test that the netlink socket close unblock the watchMiss to avoid deadlock +func TestNetlinkSocket(t *testing.T) { + // This is the same code used by the overlay driver to create the netlink interface + // for the watch miss + nlSock, err := nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH) + if err != nil { + t.Fatal() + } + // set the receive timeout to not remain stuck on the RecvFrom if the fd gets closed + tv := unix.NsecToTimeval(soTimeout.Nanoseconds()) + err = nlSock.SetReceiveTimeout(&tv) + if err != nil { + t.Fatal() + } + n := &network{id: "testnetid"} + ch := make(chan error) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + go func() { + n.watchMiss(nlSock, fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid())) + ch <- nil + }() + time.Sleep(5 * time.Second) + nlSock.Close() + select { + case <-ch: + case <-ctx.Done(): + { + t.Fatalf("Timeout expired") + } + } +} diff --git a/libnetwork/drivers/overlay/overlayutils/utils.go b/libnetwork/drivers/overlay/overlayutils/utils.go new file mode 100644 index 0000000000000..73136e8e2a788 --- /dev/null +++ b/libnetwork/drivers/overlay/overlayutils/utils.go @@ -0,0 +1,46 @@ +// Package overlayutils provides utility functions for overlay networks +package overlayutils + +import ( + "fmt" + "sync" +) + +var ( + mutex sync.RWMutex + vxlanUDPPort uint32 +) + +const defaultVXLANUDPPort = 4789 + +func init() { + vxlanUDPPort = defaultVXLANUDPPort +} + +// ConfigVXLANUDPPort configures the VXLAN UDP port (data path port) number. +// If no port is set, the default (4789) is returned. Valid port numbers are +// between 1024 and 49151. +func ConfigVXLANUDPPort(vxlanPort uint32) error { + if vxlanPort == 0 { + vxlanPort = defaultVXLANUDPPort + } + // IANA procedures for each range in detail + // The Well Known Ports, aka the System Ports, from 0-1023 + // The Registered Ports, aka the User Ports, from 1024-49151 + // The Dynamic Ports, aka the Private Ports, from 49152-65535 + // So we can allow range between 1024 to 49151 + if vxlanPort < 1024 || vxlanPort > 49151 { + return fmt.Errorf("VXLAN UDP port number is not in valid range (1024-49151): %d", vxlanPort) + } + mutex.Lock() + vxlanUDPPort = vxlanPort + mutex.Unlock() + return nil +} + +// VXLANUDPPort returns Vxlan UDP port number +func VXLANUDPPort() uint32 { + mutex.RLock() + defer mutex.RUnlock() + return vxlanUDPPort +} diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go b/libnetwork/drivers/overlay/ovmanager/ovmanager.go similarity index 95% rename from vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go rename to libnetwork/drivers/overlay/ovmanager/ovmanager.go index 12deb22e4416d..fcb0ea9cff4af 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go +++ b/libnetwork/drivers/overlay/ovmanager/ovmanager.go @@ -7,12 +7,12 @@ import ( "strings" "sync" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/idm" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/idm" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -27,7 +27,6 @@ type networkTable map[string]*network type driver struct { config map[string]interface{} networks networkTable - store datastore.DataStore vxlanIdm *idm.Idm sync.Mutex } diff --git a/libnetwork/drivers/overlay/ovmanager/ovmanager_test.go b/libnetwork/drivers/overlay/ovmanager/ovmanager_test.go new file mode 100644 index 0000000000000..319387345f7a5 --- /dev/null +++ b/libnetwork/drivers/overlay/ovmanager/ovmanager_test.go @@ -0,0 +1,87 @@ +package ovmanager + +import ( + "fmt" + "net" + "strings" + "testing" + + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/idm" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func newDriver(t *testing.T) *driver { + d := &driver{ + networks: networkTable{}, + } + + vxlanIdm, err := idm.New(nil, "vxlan-id", vxlanIDStart, vxlanIDEnd) + assert.NilError(t, err) + + d.vxlanIdm = vxlanIdm + return d +} + +func parseCIDR(t *testing.T, ipnet string) *net.IPNet { + subnet, err := types.ParseCIDR(ipnet) + assert.NilError(t, err) + return subnet +} + +func TestNetworkAllocateFree(t *testing.T) { + d := newDriver(t) + + ipamData := []driverapi.IPAMData{ + { + Pool: parseCIDR(t, "10.1.1.0/24"), + }, + { + Pool: parseCIDR(t, "10.1.2.0/24"), + }, + } + + vals, err := d.NetworkAllocate("testnetwork", nil, ipamData, nil) + assert.NilError(t, err) + + vxlanIDs, ok := vals[netlabel.OverlayVxlanIDList] + assert.Check(t, is.Equal(true, ok)) + assert.Check(t, is.Len(strings.Split(vxlanIDs, ","), 2)) + + err = d.NetworkFree("testnetwork") + assert.NilError(t, err) +} + +func TestNetworkAllocateUserDefinedVNIs(t *testing.T) { + d := newDriver(t) + + ipamData := []driverapi.IPAMData{ + { + Pool: parseCIDR(t, "10.1.1.0/24"), + }, + { + Pool: parseCIDR(t, "10.1.2.0/24"), + }, + } + + options := make(map[string]string) + // Intentionally add mode vnis than subnets + options[netlabel.OverlayVxlanIDList] = fmt.Sprintf("%d,%d,%d", vxlanIDStart, vxlanIDStart+1, vxlanIDStart+2) + + vals, err := d.NetworkAllocate("testnetwork", options, ipamData, nil) + assert.NilError(t, err) + + vxlanIDs, ok := vals[netlabel.OverlayVxlanIDList] + assert.Check(t, is.Equal(true, ok)) + + // We should only get exactly the same number of vnis as + // subnets. No more, no less, even if we passed more vnis. + assert.Check(t, is.Len(strings.Split(vxlanIDs, ","), 2)) + assert.Check(t, is.Equal(fmt.Sprintf("%d,%d", vxlanIDStart, vxlanIDStart+1), vxlanIDs)) + + err = d.NetworkFree("testnetwork") + assert.NilError(t, err) +} diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/peerdb.go b/libnetwork/drivers/overlay/peerdb.go similarity index 98% rename from vendor/github.com/docker/libnetwork/drivers/overlay/peerdb.go rename to libnetwork/drivers/overlay/peerdb.go index 58d70d04d8dd3..e812d3068cbff 100644 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/peerdb.go +++ b/libnetwork/drivers/overlay/peerdb.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package overlay import ( @@ -7,9 +10,9 @@ import ( "sync" "syscall" - "github.com/docker/libnetwork/internal/caller" - "github.com/docker/libnetwork/internal/setmatrix" - "github.com/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/internal/caller" + "github.com/docker/docker/libnetwork/internal/setmatrix" + "github.com/docker/docker/libnetwork/osl" "github.com/sirupsen/logrus" ) @@ -129,6 +132,7 @@ func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool for pKeyStr, pEntry := range mp { var pKey peerKey + pEntry := pEntry if _, err := fmt.Sscan(pKeyStr, &pKey); err != nil { logrus.Warnf("Peer key scan on network %s failed: %v", nid, err) } diff --git a/libnetwork/drivers/overlay/peerdb_test.go b/libnetwork/drivers/overlay/peerdb_test.go new file mode 100644 index 0000000000000..1c924f61cf5cc --- /dev/null +++ b/libnetwork/drivers/overlay/peerdb_test.go @@ -0,0 +1,31 @@ +//go:build linux +// +build linux + +package overlay + +import ( + "net" + "testing" +) + +func TestPeerMarshal(t *testing.T) { + _, ipNet, _ := net.ParseCIDR("192.168.0.1/24") + p := &peerEntry{eid: "eid", + isLocal: true, + peerIPMask: ipNet.Mask, + vtep: ipNet.IP} + entryDB := p.MarshalDB() + x := entryDB.UnMarshalDB() + if x.eid != p.eid { + t.Fatalf("Incorrect Unmarshalling for eid: %v != %v", x.eid, p.eid) + } + if x.isLocal != p.isLocal { + t.Fatalf("Incorrect Unmarshalling for isLocal: %v != %v", x.isLocal, p.isLocal) + } + if x.peerIPMask.String() != p.peerIPMask.String() { + t.Fatalf("Incorrect Unmarshalling for eid: %v != %v", x.peerIPMask, p.peerIPMask) + } + if x.vtep.String() != p.vtep.String() { + t.Fatalf("Incorrect Unmarshalling for eid: %v != %v", x.vtep, p.vtep) + } +} diff --git a/libnetwork/drivers/remote/api/api.go b/libnetwork/drivers/remote/api/api.go new file mode 100644 index 0000000000000..d6d1757be09ed --- /dev/null +++ b/libnetwork/drivers/remote/api/api.go @@ -0,0 +1,221 @@ +/* +Package api represents all requests and responses suitable for conversation +with a remote driver. +*/ +package api + +import ( + "net" + + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" +) + +// Response is the basic response structure used in all responses. +type Response struct { + Err string +} + +// GetError returns the error from the response, if any. +func (r *Response) GetError() string { + return r.Err +} + +// GetCapabilityResponse is the response of GetCapability request +type GetCapabilityResponse struct { + Response + Scope string + ConnectivityScope string +} + +// AllocateNetworkRequest requests allocation of new network by manager +type AllocateNetworkRequest struct { + // A network ID that remote plugins are expected to store for future + // reference. + NetworkID string + + // A free form map->object interface for communication of options. + Options map[string]string + + // IPAMData contains the address pool information for this network + IPv4Data, IPv6Data []driverapi.IPAMData +} + +// AllocateNetworkResponse is the response to the AllocateNetworkRequest. +type AllocateNetworkResponse struct { + Response + // A free form plugin specific string->string object to be sent in + // CreateNetworkRequest call in the libnetwork agents + Options map[string]string +} + +// FreeNetworkRequest is the request to free allocated network in the manager +type FreeNetworkRequest struct { + // The ID of the network to be freed. + NetworkID string +} + +// FreeNetworkResponse is the response to a request for freeing a network. +type FreeNetworkResponse struct { + Response +} + +// CreateNetworkRequest requests a new network. +type CreateNetworkRequest struct { + // A network ID that remote plugins are expected to store for future + // reference. + NetworkID string + + // A free form map->object interface for communication of options. + Options map[string]interface{} + + // IPAMData contains the address pool information for this network + IPv4Data, IPv6Data []driverapi.IPAMData +} + +// CreateNetworkResponse is the response to the CreateNetworkRequest. +type CreateNetworkResponse struct { + Response +} + +// DeleteNetworkRequest is the request to delete an existing network. +type DeleteNetworkRequest struct { + // The ID of the network to delete. + NetworkID string +} + +// DeleteNetworkResponse is the response to a request for deleting a network. +type DeleteNetworkResponse struct { + Response +} + +// CreateEndpointRequest is the request to create an endpoint within a network. +type CreateEndpointRequest struct { + // Provided at create time, this will be the network id referenced. + NetworkID string + // The ID of the endpoint for later reference. + EndpointID string + Interface *EndpointInterface + Options map[string]interface{} +} + +// EndpointInterface represents an interface endpoint. +type EndpointInterface struct { + Address string + AddressIPv6 string + MacAddress string +} + +// CreateEndpointResponse is the response to the CreateEndpoint action. +type CreateEndpointResponse struct { + Response + Interface *EndpointInterface +} + +// Interface is the representation of a linux interface. +type Interface struct { + Address *net.IPNet + AddressIPv6 *net.IPNet + MacAddress net.HardwareAddr +} + +// DeleteEndpointRequest describes the API for deleting an endpoint. +type DeleteEndpointRequest struct { + NetworkID string + EndpointID string +} + +// DeleteEndpointResponse is the response to the DeleteEndpoint action. +type DeleteEndpointResponse struct { + Response +} + +// EndpointInfoRequest retrieves information about the endpoint from the network driver. +type EndpointInfoRequest struct { + NetworkID string + EndpointID string +} + +// EndpointInfoResponse is the response to an EndpointInfoRequest. +type EndpointInfoResponse struct { + Response + Value map[string]interface{} +} + +// JoinRequest describes the API for joining an endpoint to a sandbox. +type JoinRequest struct { + NetworkID string + EndpointID string + SandboxKey string + Options map[string]interface{} +} + +// InterfaceName is the struct representation of a pair of devices with source +// and destination, for the purposes of putting an endpoint into a container. +type InterfaceName struct { + SrcName string + DstName string + DstPrefix string +} + +// StaticRoute is the plain JSON representation of a static route. +type StaticRoute struct { + Destination string + RouteType int + NextHop string +} + +// JoinResponse is the response to a JoinRequest. +type JoinResponse struct { + Response + InterfaceName *InterfaceName + Gateway string + GatewayIPv6 string + StaticRoutes []StaticRoute + DisableGatewayService bool +} + +// LeaveRequest describes the API for detaching an endpoint from a sandbox. +type LeaveRequest struct { + NetworkID string + EndpointID string +} + +// LeaveResponse is the answer to LeaveRequest. +type LeaveResponse struct { + Response +} + +// ProgramExternalConnectivityRequest describes the API for programming the external connectivity for the given endpoint. +type ProgramExternalConnectivityRequest struct { + NetworkID string + EndpointID string + Options map[string]interface{} +} + +// ProgramExternalConnectivityResponse is the answer to ProgramExternalConnectivityRequest. +type ProgramExternalConnectivityResponse struct { + Response +} + +// RevokeExternalConnectivityRequest describes the API for revoking the external connectivity for the given endpoint. +type RevokeExternalConnectivityRequest struct { + NetworkID string + EndpointID string +} + +// RevokeExternalConnectivityResponse is the answer to RevokeExternalConnectivityRequest. +type RevokeExternalConnectivityResponse struct { + Response +} + +// DiscoveryNotification represents a discovery notification +type DiscoveryNotification struct { + DiscoveryType discoverapi.DiscoveryType + DiscoveryData interface{} +} + +// DiscoveryResponse is used by libnetwork to log any plugin error processing the discovery notifications +type DiscoveryResponse struct { + Response +} diff --git a/libnetwork/drivers/remote/driver.go b/libnetwork/drivers/remote/driver.go new file mode 100644 index 0000000000000..a21a34e6b7f25 --- /dev/null +++ b/libnetwork/drivers/remote/driver.go @@ -0,0 +1,436 @@ +package remote + +import ( + "fmt" + "net" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/drivers/remote/api" + "github.com/docker/docker/libnetwork/types" + "github.com/docker/docker/pkg/plugingetter" + "github.com/docker/docker/pkg/plugins" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type driver struct { + endpoint *plugins.Client + networkType string +} + +type maybeError interface { + GetError() string +} + +func newDriver(name string, client *plugins.Client) driverapi.Driver { + return &driver{networkType: name, endpoint: client} +} + +// Init makes sure a remote driver is registered when a network driver +// plugin is activated. +func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { + newPluginHandler := func(name string, client *plugins.Client) { + // negotiate driver capability with client + d := newDriver(name, client) + c, err := d.(*driver).getCapabilities() + if err != nil { + logrus.Errorf("error getting capability for %s due to %v", name, err) + return + } + if err = dc.RegisterDriver(name, d, *c); err != nil { + logrus.Errorf("error registering driver for %s due to %v", name, err) + } + } + + // Unit test code is unaware of a true PluginStore. So we fall back to v1 plugins. + handleFunc := plugins.Handle + if pg := dc.GetPluginGetter(); pg != nil { + handleFunc = pg.Handle + activePlugins := pg.GetAllManagedPluginsByCap(driverapi.NetworkPluginEndpointType) + for _, ap := range activePlugins { + client, err := getPluginClient(ap) + if err != nil { + return err + } + newPluginHandler(ap.Name(), client) + } + } + handleFunc(driverapi.NetworkPluginEndpointType, newPluginHandler) + + return nil +} + +func getPluginClient(p plugingetter.CompatPlugin) (*plugins.Client, error) { + if v1, ok := p.(plugingetter.PluginWithV1Client); ok { + return v1.Client(), nil + } + + pa, ok := p.(plugingetter.PluginAddr) + if !ok { + return nil, errors.Errorf("unknown plugin type %T", p) + } + + if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 { + return nil, errors.Errorf("unsupported plugin protocol %s", pa.Protocol()) + } + + addr := pa.Addr() + client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout()) + if err != nil { + return nil, errors.Wrap(err, "error creating plugin client") + } + return client, nil +} + +// Get capability from client +func (d *driver) getCapabilities() (*driverapi.Capability, error) { + var capResp api.GetCapabilityResponse + if err := d.call("GetCapabilities", nil, &capResp); err != nil { + return nil, err + } + + c := &driverapi.Capability{} + switch capResp.Scope { + case "global": + c.DataScope = datastore.GlobalScope + case "local": + c.DataScope = datastore.LocalScope + default: + return nil, fmt.Errorf("invalid capability: expecting 'local' or 'global', got %s", capResp.Scope) + } + + switch capResp.ConnectivityScope { + case "global": + c.ConnectivityScope = datastore.GlobalScope + case "local": + c.ConnectivityScope = datastore.LocalScope + case "": + c.ConnectivityScope = c.DataScope + default: + return nil, fmt.Errorf("invalid capability: expecting 'local' or 'global', got %s", capResp.Scope) + } + + return c, nil +} + +// Config is not implemented for remote drivers, since it is assumed +// to be supplied to the remote process out-of-band (e.g., as command +// line arguments). +func (d *driver) Config(option map[string]interface{}) error { + return &driverapi.ErrNotImplemented{} +} + +func (d *driver) call(methodName string, arg interface{}, retVal maybeError) error { + method := driverapi.NetworkPluginEndpointType + "." + methodName + err := d.endpoint.Call(method, arg, retVal) + if err != nil { + return err + } + if e := retVal.GetError(); e != "" { + return fmt.Errorf("remote: %s", e) + } + return nil +} + +func (d *driver) NetworkAllocate(id string, options map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { + create := &api.AllocateNetworkRequest{ + NetworkID: id, + Options: options, + IPv4Data: ipV4Data, + IPv6Data: ipV6Data, + } + retVal := api.AllocateNetworkResponse{} + err := d.call("AllocateNetwork", create, &retVal) + return retVal.Options, err +} + +func (d *driver) NetworkFree(id string) error { + fr := &api.FreeNetworkRequest{NetworkID: id} + return d.call("FreeNetwork", fr, &api.FreeNetworkResponse{}) +} + +func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { +} + +func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { + return "", nil +} + +func (d *driver) CreateNetwork(id string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { + create := &api.CreateNetworkRequest{ + NetworkID: id, + Options: options, + IPv4Data: ipV4Data, + IPv6Data: ipV6Data, + } + return d.call("CreateNetwork", create, &api.CreateNetworkResponse{}) +} + +func (d *driver) DeleteNetwork(nid string) error { + delete := &api.DeleteNetworkRequest{NetworkID: nid} + return d.call("DeleteNetwork", delete, &api.DeleteNetworkResponse{}) +} + +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { + if ifInfo == nil { + return errors.New("must not be called with nil InterfaceInfo") + } + + reqIface := &api.EndpointInterface{} + if ifInfo.Address() != nil { + reqIface.Address = ifInfo.Address().String() + } + if ifInfo.AddressIPv6() != nil { + reqIface.AddressIPv6 = ifInfo.AddressIPv6().String() + } + if ifInfo.MacAddress() != nil { + reqIface.MacAddress = ifInfo.MacAddress().String() + } + + create := &api.CreateEndpointRequest{ + NetworkID: nid, + EndpointID: eid, + Interface: reqIface, + Options: epOptions, + } + var res api.CreateEndpointResponse + if err := d.call("CreateEndpoint", create, &res); err != nil { + return err + } + + inIface, err := parseInterface(res) + if err != nil { + return err + } + if inIface == nil { + // Remote driver did not set any field + return nil + } + + if inIface.MacAddress != nil { + if err := ifInfo.SetMacAddress(inIface.MacAddress); err != nil { + return errorWithRollback(fmt.Sprintf("driver modified interface MAC address: %v", err), d.DeleteEndpoint(nid, eid)) + } + } + if inIface.Address != nil { + if err := ifInfo.SetIPAddress(inIface.Address); err != nil { + return errorWithRollback(fmt.Sprintf("driver modified interface address: %v", err), d.DeleteEndpoint(nid, eid)) + } + } + if inIface.AddressIPv6 != nil { + if err := ifInfo.SetIPAddress(inIface.AddressIPv6); err != nil { + return errorWithRollback(fmt.Sprintf("driver modified interface address: %v", err), d.DeleteEndpoint(nid, eid)) + } + } + + return nil +} + +func errorWithRollback(msg string, err error) error { + rollback := "rolled back" + if err != nil { + rollback = "failed to roll back: " + err.Error() + } + return fmt.Errorf("%s; %s", msg, rollback) +} + +func (d *driver) DeleteEndpoint(nid, eid string) error { + delete := &api.DeleteEndpointRequest{ + NetworkID: nid, + EndpointID: eid, + } + return d.call("DeleteEndpoint", delete, &api.DeleteEndpointResponse{}) +} + +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { + info := &api.EndpointInfoRequest{ + NetworkID: nid, + EndpointID: eid, + } + var res api.EndpointInfoResponse + if err := d.call("EndpointOperInfo", info, &res); err != nil { + return nil, err + } + return res.Value, nil +} + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + join := &api.JoinRequest{ + NetworkID: nid, + EndpointID: eid, + SandboxKey: sboxKey, + Options: options, + } + var ( + res api.JoinResponse + err error + ) + if err = d.call("Join", join, &res); err != nil { + return err + } + + ifaceName := res.InterfaceName + if iface := jinfo.InterfaceName(); iface != nil && ifaceName != nil { + if err := iface.SetNames(ifaceName.SrcName, ifaceName.DstPrefix); err != nil { + return errorWithRollback(fmt.Sprintf("failed to set interface name: %s", err), d.Leave(nid, eid)) + } + } + + var addr net.IP + if res.Gateway != "" { + if addr = net.ParseIP(res.Gateway); addr == nil { + return fmt.Errorf(`unable to parse Gateway "%s"`, res.Gateway) + } + if jinfo.SetGateway(addr) != nil { + return errorWithRollback(fmt.Sprintf("failed to set gateway: %v", addr), d.Leave(nid, eid)) + } + } + if res.GatewayIPv6 != "" { + if addr = net.ParseIP(res.GatewayIPv6); addr == nil { + return fmt.Errorf(`unable to parse GatewayIPv6 "%s"`, res.GatewayIPv6) + } + if jinfo.SetGatewayIPv6(addr) != nil { + return errorWithRollback(fmt.Sprintf("failed to set gateway IPv6: %v", addr), d.Leave(nid, eid)) + } + } + if len(res.StaticRoutes) > 0 { + routes, err := parseStaticRoutes(res) + if err != nil { + return err + } + for _, route := range routes { + if jinfo.AddStaticRoute(route.Destination, route.RouteType, route.NextHop) != nil { + return errorWithRollback(fmt.Sprintf("failed to set static route: %v", route), d.Leave(nid, eid)) + } + } + } + if res.DisableGatewayService { + jinfo.DisableGatewayService() + } + return nil +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid string) error { + leave := &api.LeaveRequest{ + NetworkID: nid, + EndpointID: eid, + } + return d.call("Leave", leave, &api.LeaveResponse{}) +} + +// ProgramExternalConnectivity is invoked to program the rules to allow external connectivity for the endpoint. +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + data := &api.ProgramExternalConnectivityRequest{ + NetworkID: nid, + EndpointID: eid, + Options: options, + } + err := d.call("ProgramExternalConnectivity", data, &api.ProgramExternalConnectivityResponse{}) + if err != nil && plugins.IsNotFound(err) { + // It is not mandatory yet to support this method + return nil + } + return err +} + +// RevokeExternalConnectivity method is invoked to remove any external connectivity programming related to the endpoint. +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + data := &api.RevokeExternalConnectivityRequest{ + NetworkID: nid, + EndpointID: eid, + } + err := d.call("RevokeExternalConnectivity", data, &api.RevokeExternalConnectivityResponse{}) + if err != nil && plugins.IsNotFound(err) { + // It is not mandatory yet to support this method + return nil + } + return err +} + +func (d *driver) Type() string { + return d.networkType +} + +func (d *driver) IsBuiltIn() bool { + return false +} + +// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster +func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + if dType != discoverapi.NodeDiscovery { + return nil + } + notif := &api.DiscoveryNotification{ + DiscoveryType: dType, + DiscoveryData: data, + } + return d.call("DiscoverNew", notif, &api.DiscoveryResponse{}) +} + +// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster +func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + if dType != discoverapi.NodeDiscovery { + return nil + } + notif := &api.DiscoveryNotification{ + DiscoveryType: dType, + DiscoveryData: data, + } + return d.call("DiscoverDelete", notif, &api.DiscoveryResponse{}) +} + +func parseStaticRoutes(r api.JoinResponse) ([]*types.StaticRoute, error) { + var routes = make([]*types.StaticRoute, len(r.StaticRoutes)) + for i, inRoute := range r.StaticRoutes { + var err error + outRoute := &types.StaticRoute{RouteType: inRoute.RouteType} + + if inRoute.Destination != "" { + if outRoute.Destination, err = types.ParseCIDR(inRoute.Destination); err != nil { + return nil, err + } + } + + if inRoute.NextHop != "" { + outRoute.NextHop = net.ParseIP(inRoute.NextHop) + if outRoute.NextHop == nil { + return nil, fmt.Errorf("failed to parse nexthop IP %s", inRoute.NextHop) + } + } + + routes[i] = outRoute + } + return routes, nil +} + +// parseInterfaces validates all the parameters of an Interface and returns them. +func parseInterface(r api.CreateEndpointResponse) (*api.Interface, error) { + var outIf *api.Interface + + inIf := r.Interface + if inIf != nil { + var err error + outIf = &api.Interface{} + if inIf.Address != "" { + if outIf.Address, err = types.ParseCIDR(inIf.Address); err != nil { + return nil, err + } + } + if inIf.AddressIPv6 != "" { + if outIf.AddressIPv6, err = types.ParseCIDR(inIf.AddressIPv6); err != nil { + return nil, err + } + } + if inIf.MacAddress != "" { + if outIf.MacAddress, err = net.ParseMAC(inIf.MacAddress); err != nil { + return nil, err + } + } + } + + return outIf, nil +} diff --git a/libnetwork/drivers/remote/driver_test.go b/libnetwork/drivers/remote/driver_test.go new file mode 100644 index 0000000000000..0a390b9dabb8a --- /dev/null +++ b/libnetwork/drivers/remote/driver_test.go @@ -0,0 +1,620 @@ +package remote + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/types" + "github.com/docker/docker/pkg/plugins" +) + +func decodeToMap(r *http.Request) (res map[string]interface{}, err error) { + err = json.NewDecoder(r.Body).Decode(&res) + return +} + +func handle(t *testing.T, mux *http.ServeMux, method string, h func(map[string]interface{}) interface{}) { + mux.HandleFunc(fmt.Sprintf("/%s.%s", driverapi.NetworkPluginEndpointType, method), func(w http.ResponseWriter, r *http.Request) { + ask, err := decodeToMap(r) + if err != nil && err != io.EOF { + t.Fatal(err) + } + answer := h(ask) + err = json.NewEncoder(w).Encode(&answer) + if err != nil { + t.Fatal(err) + } + }) +} + +func setupPlugin(t *testing.T, name string, mux *http.ServeMux) func() { + specPath := "/etc/docker/plugins" + if runtime.GOOS == "windows" { + specPath = filepath.Join(os.Getenv("programdata"), "docker", "plugins") + } + + if err := os.MkdirAll(specPath, 0755); err != nil { + t.Fatal(err) + } + + defer func() { + if t.Failed() { + os.RemoveAll(specPath) + } + }() + + server := httptest.NewServer(mux) + if server == nil { + t.Fatal("Failed to start an HTTP Server") + } + + if err := os.WriteFile(filepath.Join(specPath, name+".spec"), []byte(server.URL), 0644); err != nil { + t.Fatal(err) + } + + mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType) + }) + + return func() { + if err := os.RemoveAll(specPath); err != nil { + t.Fatal(err) + } + server.Close() + } +} + +type testEndpoint struct { + t *testing.T + src string + dst string + address string + addressIPv6 string + macAddress string + gateway string + gatewayIPv6 string + resolvConfPath string + hostsPath string + nextHop string + destination string + routeType int + disableGatewayService bool +} + +func (test *testEndpoint) Interface() driverapi.InterfaceInfo { + return test +} + +func (test *testEndpoint) Address() *net.IPNet { + if test.address == "" { + return nil + } + nw, _ := types.ParseCIDR(test.address) + return nw +} + +func (test *testEndpoint) AddressIPv6() *net.IPNet { + if test.addressIPv6 == "" { + return nil + } + nw, _ := types.ParseCIDR(test.addressIPv6) + return nw +} + +func (test *testEndpoint) MacAddress() net.HardwareAddr { + if test.macAddress == "" { + return nil + } + mac, _ := net.ParseMAC(test.macAddress) + return mac +} + +func (test *testEndpoint) SetMacAddress(mac net.HardwareAddr) error { + if test.macAddress != "" { + return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", test.macAddress, mac) + } + if mac == nil { + return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface") + } + test.macAddress = mac.String() + return nil +} + +func (test *testEndpoint) SetIPAddress(address *net.IPNet) error { + if address.IP == nil { + return types.BadRequestErrorf("tried to set nil IP address to endpoint interface") + } + if address.IP.To4() == nil { + return setAddress(&test.addressIPv6, address) + } + return setAddress(&test.address, address) +} + +func setAddress(ifaceAddr *string, address *net.IPNet) error { + if *ifaceAddr != "" { + return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with (%s).", *ifaceAddr, address) + } + *ifaceAddr = address.String() + return nil +} + +func (test *testEndpoint) InterfaceName() driverapi.InterfaceNameInfo { + return test +} + +func compareIPs(t *testing.T, kind string, shouldBe string, supplied net.IP) { + ip := net.ParseIP(shouldBe) + if ip == nil { + t.Fatalf(`Invalid IP to test against: "%s"`, shouldBe) + } + if !ip.Equal(supplied) { + t.Fatalf(`%s IPs are not equal: expected "%s", got %v`, kind, shouldBe, supplied) + } +} + +func compareIPNets(t *testing.T, kind string, shouldBe string, supplied net.IPNet) { + _, net, _ := net.ParseCIDR(shouldBe) + if net == nil { + t.Fatalf(`Invalid IP network to test against: "%s"`, shouldBe) + } + if !types.CompareIPNet(net, &supplied) { + t.Fatalf(`%s IP networks are not equal: expected "%s", got %v`, kind, shouldBe, supplied) + } +} + +func (test *testEndpoint) SetGateway(ipv4 net.IP) error { + compareIPs(test.t, "Gateway", test.gateway, ipv4) + return nil +} + +func (test *testEndpoint) SetGatewayIPv6(ipv6 net.IP) error { + compareIPs(test.t, "GatewayIPv6", test.gatewayIPv6, ipv6) + return nil +} + +func (test *testEndpoint) SetNames(src string, dst string) error { + if test.src != src { + test.t.Fatalf(`Wrong SrcName; expected "%s", got "%s"`, test.src, src) + } + if test.dst != dst { + test.t.Fatalf(`Wrong DstPrefix; expected "%s", got "%s"`, test.dst, dst) + } + return nil +} + +func (test *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error { + compareIPNets(test.t, "Destination", test.destination, *destination) + compareIPs(test.t, "NextHop", test.nextHop, nextHop) + + if test.routeType != routeType { + test.t.Fatalf(`Wrong RouteType; expected "%d", got "%d"`, test.routeType, routeType) + } + + return nil +} + +func (test *testEndpoint) DisableGatewayService() { + test.disableGatewayService = true +} + +func (test *testEndpoint) AddTableEntry(tableName string, key string, value []byte) error { + return nil +} + +func TestGetEmptyCapabilities(t *testing.T) { + var plugin = "test-net-driver-empty-cap" + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{} + }) + + p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + d := newDriver(plugin, client) + if d.Type() != plugin { + t.Fatal("Driver type does not match that given") + } + + _, err = d.(*driver).getCapabilities() + if err == nil { + t.Fatal("There should be error reported when get empty capability") + } +} + +func TestGetExtraCapabilities(t *testing.T) { + var plugin = "test-net-driver-extra-cap" + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{ + "Scope": "local", + "foo": "bar", + "ConnectivityScope": "global", + } + }) + + p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + d := newDriver(plugin, client) + if d.Type() != plugin { + t.Fatal("Driver type does not match that given") + } + + c, err := d.(*driver).getCapabilities() + if err != nil { + t.Fatal(err) + } else if c.DataScope != datastore.LocalScope { + t.Fatalf("get capability '%s', expecting 'local'", c.DataScope) + } else if c.ConnectivityScope != datastore.GlobalScope { + t.Fatalf("get capability '%s', expecting %q", c.ConnectivityScope, datastore.GlobalScope) + } +} + +func TestGetInvalidCapabilities(t *testing.T) { + var plugin = "test-net-driver-invalid-cap" + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{ + "Scope": "fake", + } + }) + + p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + d := newDriver(plugin, client) + if d.Type() != plugin { + t.Fatal("Driver type does not match that given") + } + + _, err = d.(*driver).getCapabilities() + if err == nil { + t.Fatal("There should be error reported when get invalid capability") + } +} + +func TestRemoteDriver(t *testing.T) { + var plugin = "test-net-driver" + + ep := &testEndpoint{ + t: t, + src: "vethsrc", + dst: "vethdst", + address: "192.168.5.7/16", + addressIPv6: "2001:DB8::5:7/48", + macAddress: "ab:cd:ef:ee:ee:ee", + gateway: "192.168.0.1", + gatewayIPv6: "2001:DB8::1", + hostsPath: "/here/comes/the/host/path", + resolvConfPath: "/there/goes/the/resolv/conf", + destination: "10.0.0.0/8", + nextHop: "10.0.0.1", + routeType: 1, + } + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + var networkID string + + handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{ + "Scope": "global", + } + }) + handle(t, mux, "CreateNetwork", func(msg map[string]interface{}) interface{} { + nid := msg["NetworkID"] + var ok bool + if networkID, ok = nid.(string); !ok { + t.Fatal("RPC did not include network ID string") + } + return map[string]interface{}{} + }) + handle(t, mux, "DeleteNetwork", func(msg map[string]interface{}) interface{} { + if nid, ok := msg["NetworkID"]; !ok || nid != networkID { + t.Fatal("Network ID missing or does not match that created") + } + return map[string]interface{}{} + }) + handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} { + iface := map[string]interface{}{ + "MacAddress": ep.macAddress, + "Address": ep.address, + "AddressIPv6": ep.addressIPv6, + } + return map[string]interface{}{ + "Interface": iface, + } + }) + handle(t, mux, "Join", func(msg map[string]interface{}) interface{} { + options := msg["Options"].(map[string]interface{}) + foo, ok := options["foo"].(string) + if !ok || foo != "fooValue" { + t.Fatalf("Did not receive expected foo string in request options: %+v", msg) + } + return map[string]interface{}{ + "Gateway": ep.gateway, + "GatewayIPv6": ep.gatewayIPv6, + "HostsPath": ep.hostsPath, + "ResolvConfPath": ep.resolvConfPath, + "InterfaceName": map[string]interface{}{ + "SrcName": ep.src, + "DstPrefix": ep.dst, + }, + "StaticRoutes": []map[string]interface{}{ + { + "Destination": ep.destination, + "RouteType": ep.routeType, + "NextHop": ep.nextHop, + }, + }, + } + }) + handle(t, mux, "Leave", func(msg map[string]interface{}) interface{} { + return map[string]string{} + }) + handle(t, mux, "DeleteEndpoint", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{} + }) + handle(t, mux, "EndpointOperInfo", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{ + "Value": map[string]string{ + "Arbitrary": "key", + "Value": "pairs?", + }, + } + }) + handle(t, mux, "DiscoverNew", func(msg map[string]interface{}) interface{} { + return map[string]string{} + }) + handle(t, mux, "DiscoverDelete", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{} + }) + + p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + d := newDriver(plugin, client) + if d.Type() != plugin { + t.Fatal("Driver type does not match that given") + } + + c, err := d.(*driver).getCapabilities() + if err != nil { + t.Fatal(err) + } else if c.DataScope != datastore.GlobalScope { + t.Fatalf("get capability '%s', expecting 'global'", c.DataScope) + } + + netID := "dummy-network" + err = d.CreateNetwork(netID, map[string]interface{}{}, nil, nil, nil) + if err != nil { + t.Fatal(err) + } + + endID := "dummy-endpoint" + ifInfo := &testEndpoint{} + err = d.CreateEndpoint(netID, endID, ifInfo, map[string]interface{}{}) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(ep.MacAddress(), ifInfo.MacAddress()) || !types.CompareIPNet(ep.Address(), ifInfo.Address()) || + !types.CompareIPNet(ep.AddressIPv6(), ifInfo.AddressIPv6()) { + t.Fatalf("Unexpected InterfaceInfo data. Expected (%s, %s, %s). Got (%v, %v, %v)", + ep.MacAddress(), ep.Address(), ep.AddressIPv6(), + ifInfo.MacAddress(), ifInfo.Address(), ifInfo.AddressIPv6()) + } + + joinOpts := map[string]interface{}{"foo": "fooValue"} + err = d.Join(netID, endID, "sandbox-key", ep, joinOpts) + if err != nil { + t.Fatal(err) + } + if _, err = d.EndpointOperInfo(netID, endID); err != nil { + t.Fatal(err) + } + if err = d.Leave(netID, endID); err != nil { + t.Fatal(err) + } + if err = d.DeleteEndpoint(netID, endID); err != nil { + t.Fatal(err) + } + if err = d.DeleteNetwork(netID); err != nil { + t.Fatal(err) + } + + data := discoverapi.NodeDiscoveryData{ + Address: "192.168.1.1", + } + if err = d.DiscoverNew(discoverapi.NodeDiscovery, data); err != nil { + t.Fatal(err) + } + if err = d.DiscoverDelete(discoverapi.NodeDiscovery, data); err != nil { + t.Fatal(err) + } +} + +func TestDriverError(t *testing.T) { + var plugin = "test-net-driver-error" + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{ + "Err": "this should get raised as an error", + } + }) + + p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + driver := newDriver(plugin, client) + + if err := driver.CreateEndpoint("dummy", "dummy", &testEndpoint{t: t}, map[string]interface{}{}); err == nil { + t.Fatal("Expected error from driver") + } +} + +func TestMissingValues(t *testing.T) { + var plugin = "test-net-driver-missing" + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + ep := &testEndpoint{ + t: t, + } + + handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} { + iface := map[string]interface{}{ + "Address": ep.address, + "AddressIPv6": ep.addressIPv6, + "MacAddress": ep.macAddress, + } + return map[string]interface{}{ + "Interface": iface, + } + }) + + p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + driver := newDriver(plugin, client) + + if err := driver.CreateEndpoint("dummy", "dummy", ep, map[string]interface{}{}); err != nil { + t.Fatal(err) + } +} + +type rollbackEndpoint struct { +} + +func (r *rollbackEndpoint) Interface() driverapi.InterfaceInfo { + return r +} + +func (r *rollbackEndpoint) MacAddress() net.HardwareAddr { + return nil +} + +func (r *rollbackEndpoint) Address() *net.IPNet { + return nil +} + +func (r *rollbackEndpoint) AddressIPv6() *net.IPNet { + return nil +} + +func (r *rollbackEndpoint) SetMacAddress(mac net.HardwareAddr) error { + return errors.New("invalid mac") +} + +func (r *rollbackEndpoint) SetIPAddress(ip *net.IPNet) error { + return errors.New("invalid ip") +} + +func TestRollback(t *testing.T) { + var plugin = "test-net-driver-rollback" + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + rolledback := false + + handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} { + iface := map[string]interface{}{ + "Address": "192.168.4.5/16", + "AddressIPv6": "", + "MacAddress": "7a:12:34:56:78:90", + } + return map[string]interface{}{ + "Interface": interface{}(iface), + } + }) + handle(t, mux, "DeleteEndpoint", func(msg map[string]interface{}) interface{} { + rolledback = true + return map[string]interface{}{} + }) + + p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + driver := newDriver(plugin, client) + + ep := &rollbackEndpoint{} + + if err := driver.CreateEndpoint("dummy", "dummy", ep.Interface(), map[string]interface{}{}); err == nil { + t.Fatal("Expected error from driver") + } + if !rolledback { + t.Fatal("Expected to have had DeleteEndpoint called") + } +} diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/labels.go b/libnetwork/drivers/windows/labels.go similarity index 100% rename from vendor/github.com/docker/libnetwork/drivers/windows/labels.go rename to libnetwork/drivers/windows/labels.go diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/joinleave_windows.go b/libnetwork/drivers/windows/overlay/joinleave_windows.go similarity index 89% rename from vendor/github.com/docker/libnetwork/drivers/windows/overlay/joinleave_windows.go rename to libnetwork/drivers/windows/overlay/joinleave_windows.go index 83bee5ad93cb7..44b132cc90ba9 100644 --- a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/joinleave_windows.go +++ b/libnetwork/drivers/windows/overlay/joinleave_windows.go @@ -4,8 +4,8 @@ import ( "fmt" "net" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/types" "github.com/gogo/protobuf/proto" "github.com/sirupsen/logrus" ) @@ -95,7 +95,10 @@ func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key stri return } - d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true) + err = d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true) + if err != nil { + logrus.Errorf("peerAdd failed (%v) for ip %s with mac %s", err, addr.IP.String(), mac.String()) + } } func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go b/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go similarity index 76% rename from vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go rename to libnetwork/drivers/windows/overlay/ov_endpoint_windows.go index c990357922c40..5280e7ef396b9 100644 --- a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go +++ b/libnetwork/drivers/windows/overlay/ov_endpoint_windows.go @@ -4,13 +4,14 @@ import ( "encoding/json" "fmt" "net" + "sync" "github.com/Microsoft/hcsshim" - "github.com/docker/docker/pkg/system" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/drivers/windows" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" + "github.com/Microsoft/hcsshim/osversion" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/drivers/windows" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -29,6 +30,13 @@ type endpoint struct { portMapping []types.PortBinding // Operation port bindings } +var ( + //Server 2016 (RS1) does not support concurrent add/delete of endpoints. Therefore, we need + //to use this mutex and serialize the add/delete of endpoints on RS1. + endpointMu sync.Mutex + windowsBuild = osversion.Build() +) + func validateID(nid, eid string) error { if nid == "" { return fmt.Errorf("invalid network id") @@ -77,8 +85,7 @@ func (n *network) removeEndpointWithAddress(addr *net.IPNet) { if networkEndpoint != nil { logrus.Debugf("Removing stale endpoint from HNS") - _, err := hcsshim.HNSEndpointRequest("DELETE", networkEndpoint.profileID, "") - + _, err := endpointRequest("DELETE", networkEndpoint.profileID, "") if err != nil { logrus.Debugf("Failed to delete stale overlay endpoint (%.7s) from hns", networkEndpoint.id) } @@ -101,8 +108,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, if ep != nil { logrus.Debugf("Deleting stale endpoint %s", eid) n.deleteEndpoint(eid) - - _, err := hcsshim.HNSEndpointRequest("DELETE", ep.profileID, "") + _, err := endpointRequest("DELETE", ep.profileID, "") if err != nil { return err } @@ -149,7 +155,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, hnsEndpoint.Policies = append(hnsEndpoint.Policies, paPolicy) - if system.GetOSVersion().Build > 16236 { + if osversion.Build() > 16236 { natPolicy, err := json.Marshal(hcsshim.PaPolicy{ Type: "OutBoundNAT", }) @@ -165,7 +171,19 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, return err } - pbPolicy, err := windows.ConvertPortBindings(epConnectivity.PortBindings) + ep.portMapping = epConnectivity.PortBindings + ep.portMapping, err = windows.AllocatePorts(n.portMapper, ep.portMapping, ep.addr.IP) + if err != nil { + return err + } + + defer func() { + if err != nil { + windows.ReleasePorts(n.portMapper, ep.portMapping) + } + }() + + pbPolicy, err := windows.ConvertPortBindings(ep.portMapping) if err != nil { return err } @@ -179,7 +197,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, return err } - hnsresponse, err := hcsshim.HNSEndpointRequest("POST", "", string(configurationb)) + hnsresponse, err := endpointRequest("POST", "", string(configurationb)) if err != nil { return err } @@ -199,7 +217,7 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, ep.portMapping, err = windows.ParsePortBindingPolicies(hnsresponse.Policies) if err != nil { - hcsshim.HNSEndpointRequest("DELETE", hnsresponse.Id, "") + endpointRequest("DELETE", hnsresponse.Id, "") return err } @@ -223,9 +241,11 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { return fmt.Errorf("endpoint id %q not found", eid) } + windows.ReleasePorts(n.portMapper, ep.portMapping) + n.deleteEndpoint(eid) - _, err := hcsshim.HNSEndpointRequest("DELETE", ep.profileID, "") + _, err := endpointRequest("DELETE", ep.profileID, "") if err != nil { return err } @@ -263,3 +283,14 @@ func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, erro return data, nil } + +func endpointRequest(method, path, request string) (*hcsshim.HNSEndpoint, error) { + if windowsBuild == 14393 { + endpointMu.Lock() + } + hnsresponse, err := hcsshim.HNSEndpointRequest(method, path, request) + if windowsBuild == 14393 { + endpointMu.Unlock() + } + return hnsresponse, err +} diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_network_windows.go b/libnetwork/drivers/windows/overlay/ov_network_windows.go similarity index 95% rename from vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_network_windows.go rename to libnetwork/drivers/windows/overlay/ov_network_windows.go index 9cc46f8cfe985..4dec6b56a348d 100644 --- a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_network_windows.go +++ b/libnetwork/drivers/windows/overlay/ov_network_windows.go @@ -9,9 +9,10 @@ import ( "sync" "github.com/Microsoft/hcsshim" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/portmapper" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -46,6 +47,7 @@ type network struct { initErr error subnets []*subnet secure bool + portMapper *portmapper.PortMapper sync.Mutex } @@ -89,10 +91,11 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d } n := &network{ - id: id, - driver: d, - endpoints: endpointTable{}, - subnets: []*subnet{}, + id: id, + driver: d, + endpoints: endpointTable{}, + subnets: []*subnet{}, + portMapper: portmapper.New(""), } genData, ok := option[netlabel.GenericData].(map[string]string) diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay.pb.go b/libnetwork/drivers/windows/overlay/overlay.pb.go similarity index 100% rename from vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay.pb.go rename to libnetwork/drivers/windows/overlay/overlay.pb.go diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay.proto b/libnetwork/drivers/windows/overlay/overlay.proto similarity index 100% rename from vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay.proto rename to libnetwork/drivers/windows/overlay/overlay.proto diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay_windows.go b/libnetwork/drivers/windows/overlay/overlay_windows.go similarity index 90% rename from vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay_windows.go rename to libnetwork/drivers/windows/overlay/overlay_windows.go index 65ad62ae0d7bc..d31370246c38f 100644 --- a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/overlay_windows.go +++ b/libnetwork/drivers/windows/overlay/overlay_windows.go @@ -1,6 +1,6 @@ package overlay -//go:generate protoc -I.:../../Godeps/_workspace/src/github.com/gogo/protobuf --gogo_out=import_path=github.com/docker/libnetwork/drivers/overlay,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. overlay.proto +//go:generate protoc -I.:../../Godeps/_workspace/src/github.com/gogo/protobuf --gogo_out=import_path=github.com/docker/docker/libnetwork/drivers/overlay,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. overlay.proto import ( "encoding/json" @@ -8,11 +8,11 @@ import ( "sync" "github.com/Microsoft/hcsshim" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/peerdb_windows.go b/libnetwork/drivers/windows/overlay/peerdb_windows.go similarity index 92% rename from vendor/github.com/docker/libnetwork/drivers/windows/overlay/peerdb_windows.go rename to libnetwork/drivers/windows/overlay/peerdb_windows.go index 159bfd6ed1e36..983acf172f910 100644 --- a/vendor/github.com/docker/libnetwork/drivers/windows/overlay/peerdb_windows.go +++ b/libnetwork/drivers/windows/overlay/peerdb_windows.go @@ -6,7 +6,7 @@ import ( "encoding/json" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" "github.com/Microsoft/hcsshim" @@ -67,8 +67,7 @@ func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, } n.removeEndpointWithAddress(addr) - - hnsresponse, err := hcsshim.HNSEndpointRequest("POST", "", string(configurationb)) + hnsresponse, err := endpointRequest("POST", "", string(configurationb)) if err != nil { return err } @@ -108,7 +107,7 @@ func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMas } if updateDb { - _, err := hcsshim.HNSEndpointRequest("DELETE", ep.profileID, "") + _, err := endpointRequest("DELETE", ep.profileID, "") if err != nil { return err } diff --git a/libnetwork/drivers/windows/port_mapping.go b/libnetwork/drivers/windows/port_mapping.go new file mode 100644 index 0000000000000..56933b62e8060 --- /dev/null +++ b/libnetwork/drivers/windows/port_mapping.go @@ -0,0 +1,132 @@ +//go:build windows +// +build windows + +package windows + +import ( + "bytes" + "errors" + "fmt" + "net" + + "github.com/docker/docker/libnetwork/portmapper" + "github.com/docker/docker/libnetwork/types" + "github.com/ishidawataru/sctp" + "github.com/sirupsen/logrus" +) + +const ( + maxAllocatePortAttempts = 10 +) + +// ErrUnsupportedAddressType is returned when the specified address type is not supported. +type ErrUnsupportedAddressType string + +func (uat ErrUnsupportedAddressType) Error() string { + return fmt.Sprintf("unsupported address type: %s", string(uat)) +} + +// AllocatePorts allocates ports specified in bindings from the portMapper +func AllocatePorts(portMapper *portmapper.PortMapper, bindings []types.PortBinding, containerIP net.IP) ([]types.PortBinding, error) { + bs := make([]types.PortBinding, 0, len(bindings)) + for _, c := range bindings { + b := c.GetCopy() + if err := allocatePort(portMapper, &b, containerIP); err != nil { + // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message + if cuErr := ReleasePorts(portMapper, bs); cuErr != nil { + logrus.Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr) + } + return nil, err + } + bs = append(bs, b) + } + return bs, nil +} + +func allocatePort(portMapper *portmapper.PortMapper, bnd *types.PortBinding, containerIP net.IP) error { + var ( + host net.Addr + err error + ) + + // Windows does not support a host ip for port bindings (this is validated in ConvertPortBindings()). + // If the HostIP is nil, force it to be 0.0.0.0 for use as the key in portMapper. + if bnd.HostIP == nil { + bnd.HostIP = net.IPv4zero + } + + // Store the container interface address in the operational binding + bnd.IP = containerIP + + // Adjust HostPortEnd if this is not a range. + if bnd.HostPortEnd == 0 { + bnd.HostPortEnd = bnd.HostPort + } + + // Construct the container side transport address + container, err := bnd.ContainerAddr() + if err != nil { + return err + } + + // Try up to maxAllocatePortAttempts times to get a port that's not already allocated. + for i := 0; i < maxAllocatePortAttempts; i++ { + if host, err = portMapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), false); err == nil { + break + } + // There is no point in immediately retrying to map an explicitly chosen port. + if bnd.HostPort != 0 { + logrus.Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err) + break + } + logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1) + } + if err != nil { + return err + } + + // Save the host port (regardless it was or not specified in the binding) + switch netAddr := host.(type) { + case *net.TCPAddr: + bnd.HostPort = uint16(host.(*net.TCPAddr).Port) + break + case *net.UDPAddr: + bnd.HostPort = uint16(host.(*net.UDPAddr).Port) + break + case *sctp.SCTPAddr: + bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port) + break + default: + // For completeness + return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr)) + } + //Windows does not support host port ranges. + bnd.HostPortEnd = bnd.HostPort + return nil +} + +// ReleasePorts releases ports specified in bindings from the portMapper +func ReleasePorts(portMapper *portmapper.PortMapper, bindings []types.PortBinding) error { + var errorBuf bytes.Buffer + + // Attempt to release all port bindings, do not stop on failure + for _, m := range bindings { + if err := releasePort(portMapper, m); err != nil { + errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err)) + } + } + + if errorBuf.Len() != 0 { + return errors.New(errorBuf.String()) + } + return nil +} + +func releasePort(portMapper *portmapper.PortMapper, bnd types.PortBinding) error { + // Construct the host side transport address + host, err := bnd.HostAddr() + if err != nil { + return err + } + return portMapper.Unmap(host) +} diff --git a/libnetwork/drivers/windows/windows.go b/libnetwork/drivers/windows/windows.go new file mode 100644 index 0000000000000..86a4691db48c8 --- /dev/null +++ b/libnetwork/drivers/windows/windows.go @@ -0,0 +1,921 @@ +//go:build windows +// +build windows + +// Shim for the Host Network Service (HNS) to manage networking for +// Windows Server containers and Hyper-V containers. This module +// is a basic libnetwork driver that passes all the calls to HNS +// It implements the 4 networking modes supported by HNS L2Bridge, +// L2Tunnel, NAT and Transparent(DHCP) +// +// The network are stored in memory and docker daemon ensures discovering +// and loading these networks on startup + +package windows + +import ( + "encoding/json" + "fmt" + "net" + "strconv" + "strings" + "sync" + + "github.com/Microsoft/hcsshim" + "github.com/Microsoft/hcsshim/osversion" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/portmapper" + "github.com/docker/docker/libnetwork/types" + "github.com/sirupsen/logrus" +) + +// networkConfiguration for network specific configuration +type networkConfiguration struct { + ID string + Type string + Name string + HnsID string + RDID string + VLAN uint + VSID uint + DNSServers string + MacPools []hcsshim.MacPool + DNSSuffix string + SourceMac string + NetworkAdapterName string + dbIndex uint64 + dbExists bool + DisableGatewayDNS bool + EnableOutboundNat bool + OutboundNatExceptions []string +} + +// endpointConfiguration represents the user specified configuration for the sandbox endpoint +type endpointOption struct { + MacAddress net.HardwareAddr + QosPolicies []types.QosPolicy + DNSServers []string + DisableDNS bool + DisableICC bool +} + +// EndpointConnectivity stores the port bindings and exposed ports that the user has specified in epOptions. +type EndpointConnectivity struct { + PortBindings []types.PortBinding + ExposedPorts []types.TransportPort +} + +type hnsEndpoint struct { + id string + nid string + profileID string + Type string + //Note: Currently, the sandboxID is the same as the containerID since windows does + //not expose the sandboxID. + //In the future, windows will support a proper sandboxID that is different + //than the containerID. + //Therefore, we are using sandboxID now, so that we won't have to change this code + //when windows properly supports a sandboxID. + sandboxID string + macAddress net.HardwareAddr + epOption *endpointOption // User specified parameters + epConnectivity *EndpointConnectivity // User specified parameters + portMapping []types.PortBinding // Operation port bindings + addr *net.IPNet + gateway net.IP + dbIndex uint64 + dbExists bool +} + +type hnsNetwork struct { + id string + created bool + config *networkConfiguration + endpoints map[string]*hnsEndpoint // key: endpoint id + driver *driver // The network's driver + portMapper *portmapper.PortMapper + sync.Mutex +} + +type driver struct { + name string + networks map[string]*hnsNetwork + store datastore.DataStore + sync.Mutex +} + +const ( + errNotFound = "HNS failed with error : The object identifier does not represent a valid object. " +) + +// IsBuiltinLocalDriver validates if network-type is a builtin local-scoped driver +func IsBuiltinLocalDriver(networkType string) bool { + if "l2bridge" == networkType || "l2tunnel" == networkType || + "nat" == networkType || "ics" == networkType || + "transparent" == networkType || "internal" == networkType || + "private" == networkType { + return true + } + + return false +} + +// New constructs a new bridge driver +func newDriver(networkType string) *driver { + return &driver{name: networkType, networks: map[string]*hnsNetwork{}} +} + +// GetInit returns an initializer for the given network type +func GetInit(networkType string) func(dc driverapi.DriverCallback, config map[string]interface{}) error { + return func(dc driverapi.DriverCallback, config map[string]interface{}) error { + if !IsBuiltinLocalDriver(networkType) { + return types.BadRequestErrorf("Network type not supported: %s", networkType) + } + + d := newDriver(networkType) + + err := d.initStore(config) + if err != nil { + return err + } + + return dc.RegisterDriver(networkType, d, driverapi.Capability{ + DataScope: datastore.LocalScope, + ConnectivityScope: datastore.LocalScope, + }) + } +} + +func (d *driver) getNetwork(id string) (*hnsNetwork, error) { + d.Lock() + defer d.Unlock() + + if nw, ok := d.networks[id]; ok { + return nw, nil + } + + return nil, types.NotFoundErrorf("network not found: %s", id) +} + +func (n *hnsNetwork) getEndpoint(eid string) (*hnsEndpoint, error) { + n.Lock() + defer n.Unlock() + + if ep, ok := n.endpoints[eid]; ok { + return ep, nil + } + + return nil, types.NotFoundErrorf("Endpoint not found: %s", eid) +} + +func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string) (*networkConfiguration, error) { + config := &networkConfiguration{Type: d.name} + + for label, value := range genericOptions { + switch label { + case NetworkName: + config.Name = value + case HNSID: + config.HnsID = value + case RoutingDomain: + config.RDID = value + case Interface: + config.NetworkAdapterName = value + case DNSSuffix: + config.DNSSuffix = value + case DNSServers: + config.DNSServers = value + case DisableGatewayDNS: + b, err := strconv.ParseBool(value) + if err != nil { + return nil, err + } + config.DisableGatewayDNS = b + case MacPool: + config.MacPools = make([]hcsshim.MacPool, 0) + s := strings.Split(value, ",") + if len(s)%2 != 0 { + return nil, types.BadRequestErrorf("Invalid mac pool. You must specify both a start range and an end range") + } + for i := 0; i < len(s)-1; i += 2 { + config.MacPools = append(config.MacPools, hcsshim.MacPool{ + StartMacAddress: s[i], + EndMacAddress: s[i+1], + }) + } + case VLAN: + vlan, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return nil, err + } + config.VLAN = uint(vlan) + case VSID: + vsid, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return nil, err + } + config.VSID = uint(vsid) + case EnableOutboundNat: + if osversion.Build() <= 16236 { + return nil, fmt.Errorf("Invalid network option. OutboundNat is not supported on this OS version") + } + b, err := strconv.ParseBool(value) + if err != nil { + return nil, err + } + config.EnableOutboundNat = b + case OutboundNatExceptions: + s := strings.Split(value, ",") + config.OutboundNatExceptions = s + } + } + + config.ID = id + config.Type = d.name + return config, nil +} + +func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error { + if len(ipamV6Data) > 0 { + return types.ForbiddenErrorf("windowsshim driver doesn't support v6 subnets") + } + + if len(ipamV4Data) == 0 { + return types.BadRequestErrorf("network %s requires ipv4 configuration", id) + } + + return nil +} + +func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { +} + +func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { + return "", nil +} + +func (d *driver) createNetwork(config *networkConfiguration) *hnsNetwork { + network := &hnsNetwork{ + id: config.ID, + endpoints: make(map[string]*hnsEndpoint), + config: config, + driver: d, + portMapper: portmapper.New(""), + } + + d.Lock() + d.networks[config.ID] = network + d.Unlock() + + return network +} + +// Create a new network +func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { + if _, err := d.getNetwork(id); err == nil { + return types.ForbiddenErrorf("network %s exists", id) + } + + genData, ok := option[netlabel.GenericData].(map[string]string) + if !ok { + return fmt.Errorf("Unknown generic data option") + } + + // Parse and validate the config. It should not conflict with existing networks' config + config, err := d.parseNetworkOptions(id, genData) + if err != nil { + return err + } + + err = config.processIPAM(id, ipV4Data, ipV6Data) + if err != nil { + return err + } + + n := d.createNetwork(config) + + // A non blank hnsid indicates that the network was discovered + // from HNS. No need to call HNS if this network was discovered + // from HNS + if config.HnsID == "" { + subnets := []hcsshim.Subnet{} + + for _, ipData := range ipV4Data { + subnet := hcsshim.Subnet{ + AddressPrefix: ipData.Pool.String(), + } + + if ipData.Gateway != nil { + subnet.GatewayAddress = ipData.Gateway.IP.String() + } + + subnets = append(subnets, subnet) + } + + network := &hcsshim.HNSNetwork{ + Name: config.Name, + Type: d.name, + Subnets: subnets, + DNSServerList: config.DNSServers, + DNSSuffix: config.DNSSuffix, + MacPools: config.MacPools, + SourceMac: config.SourceMac, + NetworkAdapterName: config.NetworkAdapterName, + } + + if config.VLAN != 0 { + vlanPolicy, err := json.Marshal(hcsshim.VlanPolicy{ + Type: "VLAN", + VLAN: config.VLAN, + }) + + if err != nil { + return err + } + network.Policies = append(network.Policies, vlanPolicy) + } + + if config.VSID != 0 { + vsidPolicy, err := json.Marshal(hcsshim.VsidPolicy{ + Type: "VSID", + VSID: config.VSID, + }) + + if err != nil { + return err + } + network.Policies = append(network.Policies, vsidPolicy) + } + + if network.Name == "" { + network.Name = id + } + + configurationb, err := json.Marshal(network) + if err != nil { + return err + } + + configuration := string(configurationb) + logrus.Debugf("HNSNetwork Request =%v Address Space=%v", configuration, subnets) + + hnsresponse, err := hcsshim.HNSNetworkRequest("POST", "", configuration) + if err != nil { + return err + } + + config.HnsID = hnsresponse.Id + genData[HNSID] = config.HnsID + n.created = true + + defer func() { + if err != nil { + d.DeleteNetwork(n.id) + } + }() + + hnsIPv4Data := make([]driverapi.IPAMData, len(hnsresponse.Subnets)) + + for i, subnet := range hnsresponse.Subnets { + var gwIP, subnetIP *net.IPNet + + //The gateway returned from HNS is an IPAddress. + //We need to convert it to an IPNet to use as the Gateway of driverapi.IPAMData struct + gwCIDR := subnet.GatewayAddress + "/32" + _, gwIP, err = net.ParseCIDR(gwCIDR) + if err != nil { + return err + } + + hnsIPv4Data[i].Gateway = gwIP + _, subnetIP, err = net.ParseCIDR(subnet.AddressPrefix) + if err != nil { + return err + } + hnsIPv4Data[i].Pool = subnetIP + } + + nInfo.UpdateIpamConfig(hnsIPv4Data) + + } else { + // Delete any stale HNS endpoints for this network. + if endpoints, err := hcsshim.HNSListEndpointRequest(); err == nil { + for _, ep := range endpoints { + if ep.VirtualNetwork == config.HnsID { + logrus.Infof("Removing stale HNS endpoint %s", ep.Id) + _, err = hcsshim.HNSEndpointRequest("DELETE", ep.Id, "") + if err != nil { + logrus.Warnf("Error removing HNS endpoint %s", ep.Id) + } + } + } + } else { + logrus.Warnf("Error listing HNS endpoints for network %s", config.HnsID) + } + + n.created = true + } + + return d.storeUpdate(config) +} + +func (d *driver) DeleteNetwork(nid string) error { + n, err := d.getNetwork(nid) + if err != nil { + return types.InternalMaskableErrorf("%s", err) + } + + n.Lock() + config := n.config + n.Unlock() + + if n.created { + _, err = hcsshim.HNSNetworkRequest("DELETE", config.HnsID, "") + if err != nil && err.Error() != errNotFound { + return types.ForbiddenErrorf(err.Error()) + } + } + + d.Lock() + delete(d.networks, nid) + d.Unlock() + + // delele endpoints belong to this network + for _, ep := range n.endpoints { + if err := d.storeDelete(ep); err != nil { + logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err) + } + } + + return d.storeDelete(config) +} + +func convertQosPolicies(qosPolicies []types.QosPolicy) ([]json.RawMessage, error) { + var qps []json.RawMessage + + // Enumerate through the qos policies specified by the user and convert + // them into the internal structure matching the JSON blob that can be + // understood by the HCS. + for _, elem := range qosPolicies { + encodedPolicy, err := json.Marshal(hcsshim.QosPolicy{ + Type: "QOS", + MaximumOutgoingBandwidthInBytes: elem.MaxEgressBandwidth, + }) + + if err != nil { + return nil, err + } + qps = append(qps, encodedPolicy) + } + return qps, nil +} + +// ConvertPortBindings converts PortBindings to JSON for HNS request +func ConvertPortBindings(portBindings []types.PortBinding) ([]json.RawMessage, error) { + var pbs []json.RawMessage + + // Enumerate through the port bindings specified by the user and convert + // them into the internal structure matching the JSON blob that can be + // understood by the HCS. + for _, elem := range portBindings { + proto := strings.ToUpper(elem.Proto.String()) + if proto != "TCP" && proto != "UDP" { + return nil, fmt.Errorf("invalid protocol %s", elem.Proto.String()) + } + + if elem.HostPort != elem.HostPortEnd { + return nil, fmt.Errorf("Windows does not support more than one host port in NAT settings") + } + + if len(elem.HostIP) != 0 && !elem.HostIP.IsUnspecified() { + return nil, fmt.Errorf("Windows does not support host IP addresses in NAT settings") + } + + encodedPolicy, err := json.Marshal(hcsshim.NatPolicy{ + Type: "NAT", + ExternalPort: elem.HostPort, + InternalPort: elem.Port, + Protocol: elem.Proto.String(), + }) + + if err != nil { + return nil, err + } + pbs = append(pbs, encodedPolicy) + } + return pbs, nil +} + +// ParsePortBindingPolicies parses HNS endpoint response message to PortBindings +func ParsePortBindingPolicies(policies []json.RawMessage) ([]types.PortBinding, error) { + var bindings []types.PortBinding + hcsPolicy := &hcsshim.NatPolicy{} + + for _, elem := range policies { + + if err := json.Unmarshal([]byte(elem), &hcsPolicy); err != nil || hcsPolicy.Type != "NAT" { + continue + } + + binding := types.PortBinding{ + HostPort: hcsPolicy.ExternalPort, + HostPortEnd: hcsPolicy.ExternalPort, + Port: hcsPolicy.InternalPort, + Proto: types.ParseProtocol(hcsPolicy.Protocol), + HostIP: net.IPv4(0, 0, 0, 0), + } + + bindings = append(bindings, binding) + } + + return bindings, nil +} + +func parseEndpointOptions(epOptions map[string]interface{}) (*endpointOption, error) { + if epOptions == nil { + return nil, nil + } + + ec := &endpointOption{} + + if opt, ok := epOptions[netlabel.MacAddress]; ok { + if mac, ok := opt.(net.HardwareAddr); ok { + ec.MacAddress = mac + } else { + return nil, fmt.Errorf("Invalid endpoint configuration") + } + } + + if opt, ok := epOptions[QosPolicies]; ok { + if policies, ok := opt.([]types.QosPolicy); ok { + ec.QosPolicies = policies + } else { + return nil, fmt.Errorf("Invalid endpoint configuration") + } + } + + if opt, ok := epOptions[netlabel.DNSServers]; ok { + if dns, ok := opt.([]string); ok { + ec.DNSServers = dns + } else { + return nil, fmt.Errorf("Invalid endpoint configuration") + } + } + + if opt, ok := epOptions[DisableICC]; ok { + if disableICC, ok := opt.(bool); ok { + ec.DisableICC = disableICC + } else { + return nil, fmt.Errorf("Invalid endpoint configuration") + } + } + + if opt, ok := epOptions[DisableDNS]; ok { + if disableDNS, ok := opt.(bool); ok { + ec.DisableDNS = disableDNS + } else { + return nil, fmt.Errorf("Invalid endpoint configuration") + } + } + + return ec, nil +} + +// ParseEndpointConnectivity parses options passed to CreateEndpoint, specifically port bindings, and store in a endpointConnectivity object. +func ParseEndpointConnectivity(epOptions map[string]interface{}) (*EndpointConnectivity, error) { + if epOptions == nil { + return nil, nil + } + + ec := &EndpointConnectivity{} + + if opt, ok := epOptions[netlabel.PortMap]; ok { + if bs, ok := opt.([]types.PortBinding); ok { + ec.PortBindings = bs + } else { + return nil, fmt.Errorf("Invalid endpoint configuration") + } + } + + if opt, ok := epOptions[netlabel.ExposedPorts]; ok { + if ports, ok := opt.([]types.TransportPort); ok { + ec.ExposedPorts = ports + } else { + return nil, fmt.Errorf("Invalid endpoint configuration") + } + } + return ec, nil +} + +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { + n, err := d.getNetwork(nid) + if err != nil { + return err + } + + // Check if endpoint id is good and retrieve corresponding endpoint + ep, err := n.getEndpoint(eid) + if err == nil && ep != nil { + return driverapi.ErrEndpointExists(eid) + } + + endpointStruct := &hcsshim.HNSEndpoint{ + VirtualNetwork: n.config.HnsID, + } + + epOption, err := parseEndpointOptions(epOptions) + if err != nil { + return err + } + epConnectivity, err := ParseEndpointConnectivity(epOptions) + if err != nil { + return err + } + + macAddress := ifInfo.MacAddress() + // Use the macaddress if it was provided + if macAddress != nil { + endpointStruct.MacAddress = strings.Replace(macAddress.String(), ":", "-", -1) + } + + portMapping := epConnectivity.PortBindings + + if n.config.Type == "l2bridge" || n.config.Type == "l2tunnel" { + ip := net.IPv4(0, 0, 0, 0) + if ifInfo.Address() != nil { + ip = ifInfo.Address().IP + } + + portMapping, err = AllocatePorts(n.portMapper, portMapping, ip) + if err != nil { + return err + } + + defer func() { + if err != nil { + ReleasePorts(n.portMapper, portMapping) + } + }() + } + + endpointStruct.Policies, err = ConvertPortBindings(portMapping) + if err != nil { + return err + } + + qosPolicies, err := convertQosPolicies(epOption.QosPolicies) + if err != nil { + return err + } + endpointStruct.Policies = append(endpointStruct.Policies, qosPolicies...) + + if ifInfo.Address() != nil { + endpointStruct.IPAddress = ifInfo.Address().IP + } + + endpointStruct.DNSServerList = strings.Join(epOption.DNSServers, ",") + + // overwrite the ep DisableDNS option if DisableGatewayDNS was set to true during the network creation option + if n.config.DisableGatewayDNS { + logrus.Debugf("n.config.DisableGatewayDNS[%v] overwrites epOption.DisableDNS[%v]", n.config.DisableGatewayDNS, epOption.DisableDNS) + epOption.DisableDNS = n.config.DisableGatewayDNS + } + + if n.driver.name == "nat" && !epOption.DisableDNS { + logrus.Debugf("endpointStruct.EnableInternalDNS =[%v]", endpointStruct.EnableInternalDNS) + endpointStruct.EnableInternalDNS = true + } + + endpointStruct.DisableICC = epOption.DisableICC + + // Inherit OutboundNat policy from the network + if n.config.EnableOutboundNat { + outboundNatPolicy, err := json.Marshal(hcsshim.OutboundNatPolicy{ + Policy: hcsshim.Policy{Type: hcsshim.OutboundNat}, + Exceptions: n.config.OutboundNatExceptions, + }) + + if err != nil { + return err + } + endpointStruct.Policies = append(endpointStruct.Policies, outboundNatPolicy) + } + + configurationb, err := json.Marshal(endpointStruct) + if err != nil { + return err + } + + hnsresponse, err := hcsshim.HNSEndpointRequest("POST", "", string(configurationb)) + if err != nil { + return err + } + + mac, err := net.ParseMAC(hnsresponse.MacAddress) + if err != nil { + return err + } + + // TODO For now the ip mask is not in the info generated by HNS + endpoint := &hnsEndpoint{ + id: eid, + nid: n.id, + Type: d.name, + addr: &net.IPNet{IP: hnsresponse.IPAddress, Mask: hnsresponse.IPAddress.DefaultMask()}, + macAddress: mac, + } + + if hnsresponse.GatewayAddress != "" { + endpoint.gateway = net.ParseIP(hnsresponse.GatewayAddress) + } + + endpoint.profileID = hnsresponse.Id + endpoint.epConnectivity = epConnectivity + endpoint.epOption = epOption + endpoint.portMapping, err = ParsePortBindingPolicies(hnsresponse.Policies) + + if err != nil { + hcsshim.HNSEndpointRequest("DELETE", hnsresponse.Id, "") + return err + } + + n.Lock() + n.endpoints[eid] = endpoint + n.Unlock() + + if ifInfo.Address() == nil { + ifInfo.SetIPAddress(endpoint.addr) + } + + if macAddress == nil { + ifInfo.SetMacAddress(endpoint.macAddress) + } + + if err = d.storeUpdate(endpoint); err != nil { + logrus.Errorf("Failed to save endpoint %.7s to store: %v", endpoint.id, err) + } + + return nil +} + +func (d *driver) DeleteEndpoint(nid, eid string) error { + n, err := d.getNetwork(nid) + if err != nil { + return types.InternalMaskableErrorf("%s", err) + } + + ep, err := n.getEndpoint(eid) + if err != nil { + return err + } + + if n.config.Type == "l2bridge" || n.config.Type == "l2tunnel" { + ReleasePorts(n.portMapper, ep.portMapping) + } + + n.Lock() + delete(n.endpoints, eid) + n.Unlock() + + _, err = hcsshim.HNSEndpointRequest("DELETE", ep.profileID, "") + if err != nil && err.Error() != errNotFound { + return err + } + + if err := d.storeDelete(ep); err != nil { + logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err) + } + return nil +} + +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { + network, err := d.getNetwork(nid) + if err != nil { + return nil, err + } + + ep, err := network.getEndpoint(eid) + if err != nil { + return nil, err + } + + data := make(map[string]interface{}, 1) + if network.driver.name == "nat" { + data["AllowUnqualifiedDNSQuery"] = true + } + + data["hnsid"] = ep.profileID + if ep.epConnectivity.ExposedPorts != nil { + // Return a copy of the config data + epc := make([]types.TransportPort, 0, len(ep.epConnectivity.ExposedPorts)) + for _, tp := range ep.epConnectivity.ExposedPorts { + epc = append(epc, tp.GetCopy()) + } + data[netlabel.ExposedPorts] = epc + } + + if ep.portMapping != nil { + // Return a copy of the operational data + pmc := make([]types.PortBinding, 0, len(ep.portMapping)) + for _, pm := range ep.portMapping { + pmc = append(pmc, pm.GetCopy()) + } + data[netlabel.PortMap] = pmc + } + + if len(ep.macAddress) != 0 { + data[netlabel.MacAddress] = ep.macAddress + } + return data, nil +} + +// Join method is invoked when a Sandbox is attached to an endpoint. +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + network, err := d.getNetwork(nid) + if err != nil { + return err + } + + // Ensure that the endpoint exists + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + + err = jinfo.SetGateway(endpoint.gateway) + if err != nil { + return err + } + + endpoint.sandboxID = sboxKey + + err = hcsshim.HotAttachEndpoint(endpoint.sandboxID, endpoint.profileID) + if err != nil { + // If container doesn't exists in hcs, do not throw error for hot add/remove + if err != hcsshim.ErrComputeSystemDoesNotExist { + return err + } + } + + jinfo.DisableGatewayService() + return nil +} + +// Leave method is invoked when a Sandbox detaches from an endpoint. +func (d *driver) Leave(nid, eid string) error { + network, err := d.getNetwork(nid) + if err != nil { + return types.InternalMaskableErrorf("%s", err) + } + + // Ensure that the endpoint exists + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + + err = hcsshim.HotDetachEndpoint(endpoint.sandboxID, endpoint.profileID) + if err != nil { + // If container doesn't exists in hcs, do not throw error for hot add/remove + if err != hcsshim.ErrComputeSystemDoesNotExist { + return err + } + } + return nil +} + +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + +func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { + return nil, types.NotImplementedErrorf("not implemented") +} + +func (d *driver) NetworkFree(id string) error { + return types.NotImplementedErrorf("not implemented") +} + +func (d *driver) Type() string { + return d.name +} + +func (d *driver) IsBuiltIn() bool { + return true +} + +// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster +func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster +func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/windows_store.go b/libnetwork/drivers/windows/windows_store.go similarity index 95% rename from vendor/github.com/docker/libnetwork/drivers/windows/windows_store.go rename to libnetwork/drivers/windows/windows_store.go index eae1edc6f1c2e..e5810443d00ec 100644 --- a/vendor/github.com/docker/libnetwork/drivers/windows/windows_store.go +++ b/libnetwork/drivers/windows/windows_store.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package windows @@ -7,10 +8,10 @@ import ( "fmt" "net" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -61,9 +62,7 @@ func (d *driver) populateNetworks() error { if ncfg.Type != d.name { continue } - if err = d.createNetwork(ncfg); err != nil { - logrus.Warnf("could not create windows network for id %s hnsid %s while booting up from persistent state: %v", ncfg.ID, ncfg.HnsID, err) - } + d.createNetwork(ncfg) logrus.Debugf("Network %v (%.7s) restored", d.name, ncfg.ID) } diff --git a/libnetwork/drivers/windows/windows_test.go b/libnetwork/drivers/windows/windows_test.go new file mode 100644 index 0000000000000..ea248e9d793b6 --- /dev/null +++ b/libnetwork/drivers/windows/windows_test.go @@ -0,0 +1,144 @@ +//go:build windows +// +build windows + +package windows + +import ( + "net" + "testing" + + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" +) + +func testNetwork(networkType string, t *testing.T) { + d := newDriver(networkType) + bnw, _ := types.ParseCIDR("172.16.0.0/24") + br, _ := types.ParseCIDR("172.16.0.1/16") + + netOption := make(map[string]interface{}) + networkOptions := map[string]string{ + NetworkName: "TestNetwork", + } + + netOption[netlabel.GenericData] = networkOptions + ipdList := []driverapi.IPAMData{ + { + Pool: bnw, + Gateway: br, + }, + } + + err := d.CreateNetwork("dummy", netOption, nil, ipdList, nil) + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + defer func() { + err = d.DeleteNetwork("dummy") + if err != nil { + t.Fatalf("Failed to create bridge: %v", err) + } + }() + + epOptions := make(map[string]interface{}) + te := &testEndpoint{} + err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions) + if err != nil { + t.Fatalf("Failed to create an endpoint : %s", err.Error()) + } + + err = d.DeleteEndpoint("dummy", "ep1") + if err != nil { + t.Fatalf("Failed to delete an endpoint : %s", err.Error()) + } +} + +func TestNAT(t *testing.T) { + t.Skip("Test does not work on CI and was never running to begin with") + testNetwork("nat", t) +} + +func TestTransparent(t *testing.T) { + t.Skip("Test does not work on CI and was never running to begin with") + testNetwork("transparent", t) +} + +type testEndpoint struct { + t *testing.T + src string + dst string + address string + macAddress string + gateway string + disableGatewayService bool +} + +func (test *testEndpoint) Interface() driverapi.InterfaceInfo { + return test +} + +func (test *testEndpoint) Address() *net.IPNet { + if test.address == "" { + return nil + } + nw, _ := types.ParseCIDR(test.address) + return nw +} + +func (test *testEndpoint) AddressIPv6() *net.IPNet { + return nil +} + +func (test *testEndpoint) MacAddress() net.HardwareAddr { + if test.macAddress == "" { + return nil + } + mac, _ := net.ParseMAC(test.macAddress) + return mac +} + +func (test *testEndpoint) SetMacAddress(mac net.HardwareAddr) error { + if test.macAddress != "" { + return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", test.macAddress, mac) + } + + if mac == nil { + return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface") + } + test.macAddress = mac.String() + return nil +} + +func (test *testEndpoint) SetIPAddress(address *net.IPNet) error { + if address.IP == nil { + return types.BadRequestErrorf("tried to set nil IP address to endpoint interface") + } + + test.address = address.String() + return nil +} + +func (test *testEndpoint) InterfaceName() driverapi.InterfaceNameInfo { + return test +} + +func (test *testEndpoint) SetGateway(ipv4 net.IP) error { + return nil +} + +func (test *testEndpoint) SetGatewayIPv6(ipv6 net.IP) error { + return nil +} + +func (test *testEndpoint) SetNames(src string, dst string) error { + return nil +} + +func (test *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error { + return nil +} + +func (test *testEndpoint) DisableGatewayService() { + test.disableGatewayService = true +} diff --git a/libnetwork/drivers_freebsd.go b/libnetwork/drivers_freebsd.go new file mode 100644 index 0000000000000..30ac1d50adee0 --- /dev/null +++ b/libnetwork/drivers_freebsd.go @@ -0,0 +1,13 @@ +package libnetwork + +import ( + "github.com/docker/docker/libnetwork/drivers/null" + "github.com/docker/docker/libnetwork/drivers/remote" +) + +func getInitializers(experimental bool) []initializer { + return []initializer{ + {null.Init, "null"}, + {remote.Init, "remote"}, + } +} diff --git a/libnetwork/drivers_ipam.go b/libnetwork/drivers_ipam.go new file mode 100644 index 0000000000000..2fce9f55bcacd --- /dev/null +++ b/libnetwork/drivers_ipam.go @@ -0,0 +1,25 @@ +package libnetwork + +import ( + "github.com/docker/docker/libnetwork/drvregistry" + "github.com/docker/docker/libnetwork/ipamapi" + builtinIpam "github.com/docker/docker/libnetwork/ipams/builtin" + nullIpam "github.com/docker/docker/libnetwork/ipams/null" + remoteIpam "github.com/docker/docker/libnetwork/ipams/remote" + "github.com/docker/docker/libnetwork/ipamutils" +) + +func initIPAMDrivers(r *drvregistry.DrvRegistry, lDs, gDs interface{}, addressPool []*ipamutils.NetworkToSplit) error { + builtinIpam.SetDefaultIPAddressPool(addressPool) + for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){ + builtinIpam.Init, + remoteIpam.Init, + nullIpam.Init, + } { + if err := fn(r, lDs, gDs); err != nil { + return err + } + } + + return nil +} diff --git a/libnetwork/drivers_linux.go b/libnetwork/drivers_linux.go new file mode 100644 index 0000000000000..63571492457c1 --- /dev/null +++ b/libnetwork/drivers_linux.go @@ -0,0 +1,24 @@ +package libnetwork + +import ( + "github.com/docker/docker/libnetwork/drivers/bridge" + "github.com/docker/docker/libnetwork/drivers/host" + "github.com/docker/docker/libnetwork/drivers/ipvlan" + "github.com/docker/docker/libnetwork/drivers/macvlan" + "github.com/docker/docker/libnetwork/drivers/null" + "github.com/docker/docker/libnetwork/drivers/overlay" + "github.com/docker/docker/libnetwork/drivers/remote" +) + +func getInitializers(experimental bool) []initializer { + in := []initializer{ + {bridge.Init, "bridge"}, + {host.Init, "host"}, + {ipvlan.Init, "ipvlan"}, + {macvlan.Init, "macvlan"}, + {null.Init, "null"}, + {overlay.Init, "overlay"}, + {remote.Init, "remote"}, + } + return in +} diff --git a/libnetwork/drivers_windows.go b/libnetwork/drivers_windows.go new file mode 100644 index 0000000000000..7dbf34ccbb860 --- /dev/null +++ b/libnetwork/drivers_windows.go @@ -0,0 +1,23 @@ +package libnetwork + +import ( + "github.com/docker/docker/libnetwork/drivers/null" + "github.com/docker/docker/libnetwork/drivers/remote" + "github.com/docker/docker/libnetwork/drivers/windows" + "github.com/docker/docker/libnetwork/drivers/windows/overlay" +) + +func getInitializers(experimental bool) []initializer { + return []initializer{ + {null.Init, "null"}, + {overlay.Init, "overlay"}, + {remote.Init, "remote"}, + {windows.GetInit("transparent"), "transparent"}, + {windows.GetInit("l2bridge"), "l2bridge"}, + {windows.GetInit("l2tunnel"), "l2tunnel"}, + {windows.GetInit("nat"), "nat"}, + {windows.GetInit("internal"), "internal"}, + {windows.GetInit("private"), "private"}, + {windows.GetInit("ics"), "ics"}, + } +} diff --git a/vendor/github.com/docker/libnetwork/drvregistry/drvregistry.go b/libnetwork/drvregistry/drvregistry.go similarity index 97% rename from vendor/github.com/docker/libnetwork/drvregistry/drvregistry.go rename to libnetwork/drvregistry/drvregistry.go index cc336fa5a82be..9063c472bf9f1 100644 --- a/vendor/github.com/docker/libnetwork/drvregistry/drvregistry.go +++ b/libnetwork/drvregistry/drvregistry.go @@ -6,10 +6,10 @@ import ( "strings" "sync" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/types" ) type driverData struct { diff --git a/libnetwork/drvregistry/drvregistry_test.go b/libnetwork/drvregistry/drvregistry_test.go new file mode 100644 index 0000000000000..ead157ef5ea88 --- /dev/null +++ b/libnetwork/drvregistry/drvregistry_test.go @@ -0,0 +1,202 @@ +package drvregistry + +import ( + "runtime" + "sort" + "testing" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/ipamapi" + builtinIpam "github.com/docker/docker/libnetwork/ipams/builtin" + nullIpam "github.com/docker/docker/libnetwork/ipams/null" + remoteIpam "github.com/docker/docker/libnetwork/ipams/remote" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +const mockDriverName = "mock-driver" + +type mockDriver struct{} + +var md = mockDriver{} + +func mockDriverInit(reg driverapi.DriverCallback, opt map[string]interface{}) error { + return reg.RegisterDriver(mockDriverName, &md, driverapi.Capability{DataScope: datastore.LocalScope}) +} + +func (m *mockDriver) CreateNetwork(nid string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { + return nil +} + +func (m *mockDriver) DeleteNetwork(nid string) error { + return nil +} + +func (m *mockDriver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, options map[string]interface{}) error { + return nil +} + +func (m *mockDriver) DeleteEndpoint(nid, eid string) error { + return nil +} + +func (m *mockDriver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { + return nil, nil +} + +func (m *mockDriver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + return nil +} + +func (m *mockDriver) Leave(nid, eid string) error { + return nil +} + +func (m *mockDriver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +func (m *mockDriver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +func (m *mockDriver) Type() string { + return mockDriverName +} + +func (m *mockDriver) IsBuiltIn() bool { + return true +} + +func (m *mockDriver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (m *mockDriver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + +func (m *mockDriver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { + return nil, nil +} + +func (m *mockDriver) NetworkFree(id string) error { + return nil +} + +func (m *mockDriver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { +} + +func (m *mockDriver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { + return "", nil +} + +func getNew(t *testing.T) *DrvRegistry { + reg, err := New(nil, nil, nil, nil, nil) + if err != nil { + t.Fatal(err) + } + + err = initIPAMDrivers(reg, nil, nil) + if err != nil { + t.Fatal(err) + } + return reg +} + +func initIPAMDrivers(r *DrvRegistry, lDs, gDs interface{}) error { + for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){ + builtinIpam.Init, + remoteIpam.Init, + nullIpam.Init, + } { + if err := fn(r, lDs, gDs); err != nil { + return err + } + } + + return nil +} +func TestNew(t *testing.T) { + getNew(t) +} + +func TestAddDriver(t *testing.T) { + reg := getNew(t) + + err := reg.AddDriver(mockDriverName, mockDriverInit, nil) + assert.NilError(t, err) +} + +func TestAddDuplicateDriver(t *testing.T) { + reg := getNew(t) + + err := reg.AddDriver(mockDriverName, mockDriverInit, nil) + assert.NilError(t, err) + + // Try adding the same driver + err = reg.AddDriver(mockDriverName, mockDriverInit, nil) + assert.Check(t, is.ErrorContains(err, "")) +} + +func TestIPAMDefaultAddressSpaces(t *testing.T) { + reg := getNew(t) + + as1, as2, err := reg.IPAMDefaultAddressSpaces("default") + assert.NilError(t, err) + assert.Check(t, as1 != "") + assert.Check(t, as2 != "") +} + +func TestDriver(t *testing.T) { + reg := getNew(t) + + err := reg.AddDriver(mockDriverName, mockDriverInit, nil) + assert.NilError(t, err) + + d, cap := reg.Driver(mockDriverName) + assert.Check(t, d != nil) + assert.Check(t, cap != nil) +} + +func TestIPAM(t *testing.T) { + reg := getNew(t) + + i, cap := reg.IPAM("default") + assert.Check(t, i != nil) + assert.Check(t, cap != nil) +} + +func TestWalkIPAMs(t *testing.T) { + reg := getNew(t) + + ipams := make([]string, 0, 2) + reg.WalkIPAMs(func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool { + ipams = append(ipams, name) + return false + }) + + sort.Strings(ipams) + expected := []string{"default", "null"} + if runtime.GOOS == "windows" { + expected = append(expected, "windows") + } + assert.Check(t, is.DeepEqual(ipams, expected)) +} + +func TestWalkDrivers(t *testing.T) { + reg := getNew(t) + + err := reg.AddDriver(mockDriverName, mockDriverInit, nil) + assert.NilError(t, err) + + var driverName string + reg.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool { + driverName = name + return false + }) + + assert.Check(t, is.Equal(driverName, mockDriverName)) +} diff --git a/vendor/github.com/docker/libnetwork/endpoint.go b/libnetwork/endpoint.go similarity index 92% rename from vendor/github.com/docker/libnetwork/endpoint.go rename to libnetwork/endpoint.go index 822f88bd3ef2c..4a9d02cfc8121 100644 --- a/vendor/github.com/docker/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -4,14 +4,13 @@ import ( "encoding/json" "fmt" "net" - "strings" "sync" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/options" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -60,7 +59,6 @@ type endpoint struct { anonymous bool disableResolution bool generic map[string]interface{} - joinLeaveDone chan struct{} prefAddress net.IP prefAddressV6 net.IP ipamOptions map[string]string @@ -117,19 +115,25 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) { ep.name = epMap["name"].(string) ep.id = epMap["id"].(string) + // TODO(cpuguy83): So yeah, this isn't checking any errors anywhere. + // Seems like we should be checking errors even because of memory related issues that can arise. + // Alas it seems like given the nature of this data we could introduce problems if we start checking these errors. + // + // If anyone ever comes here and figures out one way or another if we can/should be checking these errors and it turns out we can't... then please document *why* + ib, _ := json.Marshal(epMap["ep_iface"]) - json.Unmarshal(ib, &ep.iface) + json.Unmarshal(ib, &ep.iface) // nolint:errcheck jb, _ := json.Marshal(epMap["joinInfo"]) - json.Unmarshal(jb, &ep.joinInfo) + json.Unmarshal(jb, &ep.joinInfo) // nolint:errcheck tb, _ := json.Marshal(epMap["exposed_ports"]) var tPorts []types.TransportPort - json.Unmarshal(tb, &tPorts) + json.Unmarshal(tb, &tPorts) // nolint:errcheck ep.exposedPorts = tPorts cb, _ := json.Marshal(epMap["sandbox"]) - json.Unmarshal(cb, &ep.sandboxID) + json.Unmarshal(cb, &ep.sandboxID) // nolint:errcheck if v, ok := epMap["generic"]; ok { ep.generic = v.(map[string]interface{}) @@ -208,17 +212,17 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) { sal, _ := json.Marshal(epMap["svcAliases"]) var svcAliases []string - json.Unmarshal(sal, &svcAliases) + json.Unmarshal(sal, &svcAliases) // nolint:errcheck ep.svcAliases = svcAliases pc, _ := json.Marshal(epMap["ingressPorts"]) var ingressPorts []*PortConfig - json.Unmarshal(pc, &ingressPorts) + json.Unmarshal(pc, &ingressPorts) // nolint:errcheck ep.ingressPorts = ingressPorts ma, _ := json.Marshal(epMap["myAliases"]) var myAliases []string - json.Unmarshal(ma, &myAliases) + json.Unmarshal(ma, &myAliases) // nolint:errcheck ep.myAliases = myAliases return nil } @@ -253,12 +257,16 @@ func (ep *endpoint) CopyTo(o datastore.KVObject) error { if ep.iface != nil { dstEp.iface = &endpointInterface{} - ep.iface.CopyTo(dstEp.iface) + if err := ep.iface.CopyTo(dstEp.iface); err != nil { + return err + } } if ep.joinInfo != nil { dstEp.joinInfo = &endpointJoinInfo{} - ep.joinInfo.CopyTo(dstEp.joinInfo) + if err := ep.joinInfo.CopyTo(dstEp.joinInfo); err != nil { + return err + } } dstEp.exposedPorts = make([]types.TransportPort, len(ep.exposedPorts)) @@ -354,17 +362,6 @@ func (ep *endpoint) KeyPrefix() []string { return []string{datastore.EndpointKeyPrefix, ep.network.id} } -func (ep *endpoint) networkIDFromKey(key string) (string, error) { - // endpoint Key structure : docker/libnetwork/endpoint/${network-id}/${endpoint-id} - // it's an invalid key if the key doesn't have all the 5 key elements above - keyElements := strings.Split(key, "/") - if !strings.HasPrefix(key, datastore.Key(datastore.EndpointKeyPrefix)) || len(keyElements) < 5 { - return "", fmt.Errorf("invalid endpoint key : %v", key) - } - // network-id is placed at index=3. pls refer to endpoint.Key() method - return strings.Split(key, "/")[3], nil -} - func (ep *endpoint) Value() []byte { b, err := json.Marshal(ep) if err != nil { @@ -497,12 +494,16 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) (err error) { n.getController().watchSvcRecord(ep) } - if doUpdateHostsFile(n, sb) { - address := "" - if ip := ep.getFirstInterfaceAddress(); ip != nil { - address = ip.String() + // Do not update hosts file with internal networks endpoint IP + if !n.ingress && n.Name() != libnGWNetwork { + var addresses []string + if ip := ep.getFirstInterfaceIPv4Address(); ip != nil { + addresses = append(addresses, ip.String()) + } + if ip := ep.getFirstInterfaceIPv6Address(); ip != nil { + addresses = append(addresses, ip.String()) } - if err = sb.updateHostsFile(address); err != nil { + if err = sb.updateHostsFile(addresses); err != nil { return err } } @@ -598,10 +599,6 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) (err error) { return nil } -func doUpdateHostsFile(n *network, sb *sandbox) bool { - return !n.ingress && n.Name() != libnGWNetwork -} - func (ep *endpoint) rename(name string) error { var ( err error @@ -647,10 +644,14 @@ func (ep *endpoint) rename(name string) error { } defer func() { if err != nil { - ep.deleteServiceInfoFromCluster(sb, true, "rename") + if err2 := ep.deleteServiceInfoFromCluster(sb, true, "rename"); err2 != nil { + logrus.WithField("main error", err).WithError(err2).Debug("Error during cleanup due deleting service info from cluster while cleaning up due to other error") + } ep.name = oldName ep.anonymous = oldAnonymous - ep.addServiceInfoToCluster(sb) + if err2 := ep.addServiceInfoToCluster(sb); err2 != nil { + logrus.WithField("main error", err).WithError(err2).Debug("Error during cleanup due adding service to from cluster while cleaning up due to other error") + } } }() } else { @@ -676,7 +677,7 @@ func (ep *endpoint) rename(name string) error { // benign error. Besides there is no meaningful recovery that // we can do. When the cluster recovers subsequent EpCnt update // will force the peers to get the correct EP name. - n.getEpCnt().updateStore() + _ = n.getEpCnt().updateStore() return err } @@ -912,7 +913,7 @@ func (ep *endpoint) getSandbox() (*sandbox, bool) { return ps, ok } -func (ep *endpoint) getFirstInterfaceAddress() net.IP { +func (ep *endpoint) getFirstInterfaceIPv4Address() net.IP { ep.Lock() defer ep.Unlock() @@ -923,6 +924,17 @@ func (ep *endpoint) getFirstInterfaceAddress() net.IP { return nil } +func (ep *endpoint) getFirstInterfaceIPv6Address() net.IP { + ep.Lock() + defer ep.Unlock() + + if ep.iface.addrv6 != nil { + return ep.iface.addrv6.IP + } + + return nil +} + // EndpointOptionGeneric function returns an option setter for a Generic option defined // in a Dictionary of Key-Value pair func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption { @@ -1041,7 +1053,7 @@ func CreateOptionLoadBalancer() EndpointOption { // JoinOptionPriority function returns an option setter for priority option to // be passed to the endpoint.Join() method. -func JoinOptionPriority(ep Endpoint, prio int) EndpointOption { +func JoinOptionPriority(prio int) EndpointOption { return func(ep *endpoint) { // ep lock already acquired c := ep.network.getController() @@ -1212,7 +1224,9 @@ func (c *controller) cleanupLocalEndpoints() { epCnt := n.getEpCnt().EndpointCnt() if epCnt != uint64(len(epl)) { logrus.Infof("Fixing inconsistent endpoint_cnt for network %s. Expected=%d, Actual=%d", n.name, len(epl), epCnt) - n.getEpCnt().setCnt(uint64(len(epl))) + if err := n.getEpCnt().setCnt(uint64(len(epl))); err != nil { + logrus.WithField("network", n.name).WithError(err).Warn("Error while fixing inconsistent endpoint_cnt for network") + } } } } diff --git a/vendor/github.com/docker/libnetwork/endpoint_cnt.go b/libnetwork/endpoint_cnt.go similarity index 98% rename from vendor/github.com/docker/libnetwork/endpoint_cnt.go rename to libnetwork/endpoint_cnt.go index 7b7527426dc7b..c4670335cef3b 100644 --- a/vendor/github.com/docker/libnetwork/endpoint_cnt.go +++ b/libnetwork/endpoint_cnt.go @@ -5,7 +5,7 @@ import ( "fmt" "sync" - "github.com/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/datastore" ) type endpointCnt struct { diff --git a/vendor/github.com/docker/libnetwork/endpoint_info.go b/libnetwork/endpoint_info.go similarity index 94% rename from vendor/github.com/docker/libnetwork/endpoint_info.go rename to libnetwork/endpoint_info.go index 80b662defa478..7c04f9438bff5 100644 --- a/vendor/github.com/docker/libnetwork/endpoint_info.go +++ b/libnetwork/endpoint_info.go @@ -5,8 +5,8 @@ import ( "fmt" "net" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/types" ) // EndpointInfo provides an interface to retrieve network resources bound to the endpoint. @@ -135,7 +135,10 @@ func (epi *endpointInterface) UnmarshalJSON(b []byte) error { rb, _ := json.Marshal(epMap["routes"]) var routes []string - json.Unmarshal(rb, &routes) + + // TODO(cpuguy83): linter noticed we don't check the error here... no idea why but it seems like it could introduce problems if we start checking + json.Unmarshal(rb, &routes) // nolint:errcheck + epi.routes = make([]*net.IPNet, 0) for _, route := range routes { ip, ipr, err := net.ParseCIDR(route) @@ -436,10 +439,16 @@ func (epj *endpointJoinInfo) UnmarshalJSON(b []byte) error { if v, ok := epMap["StaticRoutes"]; ok { tb, _ := json.Marshal(v) var tStaticRoute []types.StaticRoute - json.Unmarshal(tb, &tStaticRoute) + // TODO(cpuguy83): Linter caught that we aren't checking errors here + // I don't know why we aren't other than potentially the data is not always expected to be right? + // This is why I'm not adding the error check. + // + // In any case for posterity please if you figure this out document it or check the error + json.Unmarshal(tb, &tStaticRoute) // nolint:errcheck } var StaticRoutes []*types.StaticRoute for _, r := range tStaticRoute { + r := r StaticRoutes = append(StaticRoutes, &r) } epj.StaticRoutes = StaticRoutes diff --git a/vendor/github.com/docker/libnetwork/endpoint_info_unix.go b/libnetwork/endpoint_info_unix.go similarity index 97% rename from vendor/github.com/docker/libnetwork/endpoint_info_unix.go rename to libnetwork/endpoint_info_unix.go index f2534f490473e..018f02b367c42 100644 --- a/vendor/github.com/docker/libnetwork/endpoint_info_unix.go +++ b/libnetwork/endpoint_info_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package libnetwork diff --git a/vendor/github.com/docker/libnetwork/endpoint_info_windows.go b/libnetwork/endpoint_info_windows.go similarity index 97% rename from vendor/github.com/docker/libnetwork/endpoint_info_windows.go rename to libnetwork/endpoint_info_windows.go index 93ad8330e9d82..378cf8454da98 100644 --- a/vendor/github.com/docker/libnetwork/endpoint_info_windows.go +++ b/libnetwork/endpoint_info_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package libnetwork diff --git a/libnetwork/endpoint_test.go b/libnetwork/endpoint_test.go new file mode 100644 index 0000000000000..22cb160b9bea2 --- /dev/null +++ b/libnetwork/endpoint_test.go @@ -0,0 +1,76 @@ +//go:build !windows +// +build !windows + +package libnetwork + +import ( + "os" + "testing" + + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/testutils" +) + +func TestHostsEntries(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + expectedHostsFile := `127.0.0.1 localhost +::1 localhost ip6-localhost ip6-loopback +fe00::0 ip6-localnet +ff00::0 ip6-mcastprefix +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters +192.168.222.2 somehost.example.com somehost +fe90::2 somehost.example.com somehost +` + + opts := []NetworkOption{NetworkOptionEnableIPv6(true), NetworkOptionIpam(ipamapi.DefaultIPAM, "", + []*IpamConf{{PreferredPool: "192.168.222.0/24", Gateway: "192.168.222.1"}}, + []*IpamConf{{PreferredPool: "fe90::/64", Gateway: "fe90::1"}}, + nil)} + + c, nws := getTestEnv(t, opts) + ctrlr := c.(*controller) + + hostsFile, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(hostsFile.Name()) + + sbx, err := ctrlr.NewSandbox("sandbox1", OptionHostsPath(hostsFile.Name()), OptionHostname("somehost.example.com")) + if err != nil { + t.Fatal(err) + } + + ep1, err := nws[0].CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + + if err := ep1.Join(sbx, JoinOptionPriority(1)); err != nil { + t.Fatal(err) + } + + data, err := os.ReadFile(hostsFile.Name()) + if err != nil { + t.Fatal(err) + } + + if string(data) != expectedHostsFile { + t.Fatalf("expected the hosts file to read:\n%q\nbut instead got the following:\n%q\n", expectedHostsFile, string(data)) + } + + if err := sbx.Delete(); err != nil { + t.Fatal(err) + } + + if len(ctrlr.sandboxes) != 0 { + t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes)) + } + + osl.GC() +} diff --git a/vendor/github.com/docker/libnetwork/error.go b/libnetwork/error.go similarity index 100% rename from vendor/github.com/docker/libnetwork/error.go rename to libnetwork/error.go diff --git a/libnetwork/errors_test.go b/libnetwork/errors_test.go new file mode 100644 index 0000000000000..195ad4e8f8fd6 --- /dev/null +++ b/libnetwork/errors_test.go @@ -0,0 +1,51 @@ +package libnetwork + +import ( + "testing" + + "github.com/docker/docker/libnetwork/types" +) + +func TestErrorInterfaces(t *testing.T) { + + badRequestErrorList := []error{ErrInvalidID(""), ErrInvalidName(""), ErrInvalidJoin{}, ErrInvalidNetworkDriver(""), InvalidContainerIDError(""), ErrNoSuchNetwork(""), ErrNoSuchEndpoint("")} + for _, err := range badRequestErrorList { + switch u := err.(type) { + case types.BadRequestError: + return + default: + t.Fatalf("Failed to detect err %v is of type BadRequestError. Got type: %T", err, u) + } + } + + maskableErrorList := []error{ErrNoContainer{}} + for _, err := range maskableErrorList { + switch u := err.(type) { + case types.MaskableError: + return + default: + t.Fatalf("Failed to detect err %v is of type MaskableError. Got type: %T", err, u) + } + } + + notFoundErrorList := []error{NetworkTypeError(""), &UnknownNetworkError{}, &UnknownEndpointError{}} + for _, err := range notFoundErrorList { + switch u := err.(type) { + case types.NotFoundError: + return + default: + t.Fatalf("Failed to detect err %v is of type NotFoundError. Got type: %T", err, u) + } + } + + forbiddenErrorList := []error{NetworkTypeError(""), &UnknownNetworkError{}, &UnknownEndpointError{}} + for _, err := range forbiddenErrorList { + switch u := err.(type) { + case types.ForbiddenError: + return + default: + t.Fatalf("Failed to detect err %v is of type ForbiddenError. Got type: %T", err, u) + } + } + +} diff --git a/vendor/github.com/docker/libnetwork/etchosts/etchosts.go b/libnetwork/etchosts/etchosts.go similarity index 93% rename from vendor/github.com/docker/libnetwork/etchosts/etchosts.go rename to libnetwork/etchosts/etchosts.go index d55298af456af..52063eeefd326 100644 --- a/vendor/github.com/docker/libnetwork/etchosts/etchosts.go +++ b/libnetwork/etchosts/etchosts.go @@ -5,7 +5,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "os" "regexp" "strings" @@ -108,7 +107,7 @@ func Build(path, IP, hostname, domainname string, extraContent []Record) error { } } - return ioutil.WriteFile(path, content.Bytes(), 0644) + return os.WriteFile(path, content.Bytes(), 0644) } // Add adds an arbitrary number of Records to an already existing /etc/hosts file @@ -124,7 +123,7 @@ func Add(path string, recs []Record) error { return err } - return ioutil.WriteFile(path, b, 0644) + return os.WriteFile(path, b, 0644) } func mergeRecords(path string, recs []Record) ([]byte, error) { @@ -189,7 +188,7 @@ loop: if err := s.Err(); err != nil { return err } - return ioutil.WriteFile(path, buf.Bytes(), 0644) + return os.WriteFile(path, buf.Bytes(), 0644) } // Update all IP addresses where hostname matches. @@ -199,10 +198,10 @@ loop: func Update(path, IP, hostname string) error { defer pathLock(path)() - old, err := ioutil.ReadFile(path) + old, err := os.ReadFile(path) if err != nil { return err } var re = regexp.MustCompile(fmt.Sprintf("(\\S*)(\\t%s)(\\s|\\.)", regexp.QuoteMeta(hostname))) - return ioutil.WriteFile(path, re.ReplaceAll(old, []byte(IP+"$2"+"$3")), 0644) + return os.WriteFile(path, re.ReplaceAll(old, []byte(IP+"$2"+"$3")), 0644) } diff --git a/libnetwork/etchosts/etchosts_test.go b/libnetwork/etchosts/etchosts_test.go new file mode 100644 index 0000000000000..4b494d84d3fff --- /dev/null +++ b/libnetwork/etchosts/etchosts_test.go @@ -0,0 +1,515 @@ +package etchosts + +import ( + "bytes" + "fmt" + "os" + "testing" + + "golang.org/x/sync/errgroup" +) + +func TestBuildDefault(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + // check that /etc/hosts has consistent ordering + for i := 0; i <= 5; i++ { + err = Build(file.Name(), "", "", "", nil) + if err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + expected := "127.0.0.1\tlocalhost\n::1\tlocalhost ip6-localhost ip6-loopback\nfe00::0\tip6-localnet\nff00::0\tip6-mcastprefix\nff02::1\tip6-allnodes\nff02::2\tip6-allrouters\n" + + if expected != string(content) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } + } +} + +func TestBuildHostnameDomainname(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + err = Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", nil) + if err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "10.11.12.13\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } +} + +func TestBuildHostname(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + err = Build(file.Name(), "10.11.12.13", "testhostname", "", nil) + if err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "10.11.12.13\ttesthostname\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } +} + +func TestBuildHostnameFQDN(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + err = Build(file.Name(), "10.11.12.13", "testhostname.testdomainname.com", "", nil) + if err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "10.11.12.13\ttesthostname.testdomainname.com testhostname\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } +} + +func TestBuildNoIP(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + err = Build(file.Name(), "", "testhostname", "", nil) + if err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := ""; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } +} + +func TestUpdate(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + if err := Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", nil); err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "10.11.12.13\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } + + if err := Update(file.Name(), "1.1.1.1", "testhostname"); err != nil { + t.Fatal(err) + } + + content, err = os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "1.1.1.1\ttesthostname.testdomainname testhostname\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } +} + +// This regression test ensures that when a host is given a new IP +// via the Update function that other hosts which start with the +// same name as the targeted host are not erroneously updated as well. +// In the test example, if updating a host called "prefix", unrelated +// hosts named "prefixAndMore" or "prefix2" or anything else starting +// with "prefix" should not be changed. For more information see +// GitHub issue #603. +func TestUpdateIgnoresPrefixedHostname(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + if err := Build(file.Name(), "10.11.12.13", "testhostname", "testdomainname", []Record{ + { + Hosts: "prefix", + IP: "2.2.2.2", + }, + { + Hosts: "prefixAndMore", + IP: "3.3.3.3", + }, + { + Hosts: "unaffectedHost", + IP: "4.4.4.4", + }, + }); err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "2.2.2.2\tprefix\n3.3.3.3\tprefixAndMore\n4.4.4.4\tunaffectedHost\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } + + if err := Update(file.Name(), "5.5.5.5", "prefix"); err != nil { + t.Fatal(err) + } + + content, err = os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "5.5.5.5\tprefix\n3.3.3.3\tprefixAndMore\n4.4.4.4\tunaffectedHost\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } + +} + +// This regression test covers the host prefix issue for the +// Delete function. In the test example, if deleting a host called +// "prefix", an unrelated host called "prefixAndMore" should not +// be deleted. For more information see GitHub issue #603. +func TestDeleteIgnoresPrefixedHostname(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + err = Build(file.Name(), "", "", "", nil) + if err != nil { + t.Fatal(err) + } + + if err := Add(file.Name(), []Record{ + { + Hosts: "prefix", + IP: "1.1.1.1", + }, + { + Hosts: "prefixAndMore", + IP: "2.2.2.2", + }, + }); err != nil { + t.Fatal(err) + } + + if err := Delete(file.Name(), []Record{ + { + Hosts: "prefix", + IP: "1.1.1.1", + }, + }); err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "2.2.2.2\tprefixAndMore\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } + + if expected := "1.1.1.1\tprefix\n"; bytes.Contains(content, []byte(expected)) { + t.Fatalf("Did not expect to find '%s' got '%s'", expected, content) + } +} + +func TestAddEmpty(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + err = Build(file.Name(), "", "", "", nil) + if err != nil { + t.Fatal(err) + } + + if err := Add(file.Name(), []Record{}); err != nil { + t.Fatal(err) + } +} + +func TestAdd(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + err = Build(file.Name(), "", "", "", nil) + if err != nil { + t.Fatal(err) + } + + if err := Add(file.Name(), []Record{ + { + Hosts: "testhostname", + IP: "2.2.2.2", + }, + }); err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "2.2.2.2\ttesthostname\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } +} + +func TestDeleteEmpty(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + err = Build(file.Name(), "", "", "", nil) + if err != nil { + t.Fatal(err) + } + + if err := Delete(file.Name(), []Record{}); err != nil { + t.Fatal(err) + } +} + +func TestDeleteNewline(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + b := []byte("\n") + if _, err := file.Write(b); err != nil { + t.Fatal(err) + } + + rec := []Record{ + { + Hosts: "prefix", + IP: "2.2.2.2", + }, + } + if err := Delete(file.Name(), rec); err != nil { + t.Fatal(err) + } +} + +func TestDelete(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + err = Build(file.Name(), "", "", "", nil) + if err != nil { + t.Fatal(err) + } + + if err := Add(file.Name(), []Record{ + { + Hosts: "testhostname1", + IP: "1.1.1.1", + }, + { + Hosts: "testhostname2", + IP: "2.2.2.2", + }, + { + Hosts: "testhostname3", + IP: "3.3.3.3", + }, + }); err != nil { + t.Fatal(err) + } + + if err := Delete(file.Name(), []Record{ + { + Hosts: "testhostname1", + IP: "1.1.1.1", + }, + { + Hosts: "testhostname3", + IP: "3.3.3.3", + }, + }); err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "2.2.2.2\ttesthostname2\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } + + if expected := "1.1.1.1\ttesthostname1\n"; bytes.Contains(content, []byte(expected)) { + t.Fatalf("Did not expect to find '%s' got '%s'", expected, content) + } +} + +func TestConcurrentWrites(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + err = Build(file.Name(), "", "", "", nil) + if err != nil { + t.Fatal(err) + } + + if err := Add(file.Name(), []Record{ + { + Hosts: "inithostname", + IP: "172.17.0.1", + }, + }); err != nil { + t.Fatal(err) + } + + group := new(errgroup.Group) + for i := 0; i < 10; i++ { + i := i + group.Go(func() error { + rec := []Record{ + { + IP: fmt.Sprintf("%d.%d.%d.%d", i, i, i, i), + Hosts: fmt.Sprintf("testhostname%d", i), + }, + } + + for j := 0; j < 25; j++ { + if err := Add(file.Name(), rec); err != nil { + return err + } + + if err := Delete(file.Name(), rec); err != nil { + return err + } + } + return nil + }) + } + + if err := group.Wait(); err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "172.17.0.1\tinithostname\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } +} + +func benchDelete(b *testing.B) { + b.StopTimer() + file, err := os.CreateTemp("", "") + if err != nil { + b.Fatal(err) + } + defer func() { + b.StopTimer() + file.Close() + os.Remove(file.Name()) + b.StartTimer() + }() + + err = Build(file.Name(), "", "", "", nil) + if err != nil { + b.Fatal(err) + } + + var records []Record + var toDelete []Record + for i := 0; i < 255; i++ { + record := Record{ + Hosts: fmt.Sprintf("testhostname%d", i), + IP: fmt.Sprintf("%d.%d.%d.%d", i, i, i, i), + } + records = append(records, record) + if i%2 == 0 { + toDelete = append(records, record) + } + } + + if err := Add(file.Name(), records); err != nil { + b.Fatal(err) + } + + b.StartTimer() + if err := Delete(file.Name(), toDelete); err != nil { + b.Fatal(err) + } +} + +func BenchmarkDelete(b *testing.B) { + for i := 0; i < b.N; i++ { + benchDelete(b) + } +} diff --git a/libnetwork/firewall_linux.go b/libnetwork/firewall_linux.go new file mode 100644 index 0000000000000..590e61f2af0bc --- /dev/null +++ b/libnetwork/firewall_linux.go @@ -0,0 +1,46 @@ +package libnetwork + +import ( + "github.com/docker/docker/libnetwork/iptables" + "github.com/sirupsen/logrus" +) + +const userChain = "DOCKER-USER" + +var ( + ctrl *controller = nil +) + +func setupArrangeUserFilterRule(c *controller) { + ctrl = c + iptables.OnReloaded(arrangeUserFilterRule) +} + +// This chain allow users to configure firewall policies in a way that persists +// docker operations/restarts. Docker will not delete or modify any pre-existing +// rules from the DOCKER-USER filter chain. +// Note once DOCKER-USER chain is created, docker engine does not remove it when +// IPTableForwarding is disabled, because it contains rules configured by user that +// are beyond docker engine's control. +func arrangeUserFilterRule() { + if ctrl == nil || !ctrl.iptablesEnabled() { + return + } + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) + _, err := iptable.NewChain(userChain, iptables.Filter, false) + if err != nil { + logrus.Warnf("Failed to create %s chain: %v", userChain, err) + return + } + + if err = iptable.AddReturnRule(userChain); err != nil { + logrus.Warnf("Failed to add the RETURN rule for %s: %v", userChain, err) + return + } + + err = iptable.EnsureJumpRule("FORWARD", userChain) + if err != nil { + logrus.Warnf("Failed to ensure the jump rule for %s: %v", userChain, err) + } +} diff --git a/libnetwork/firewall_linux_test.go b/libnetwork/firewall_linux_test.go new file mode 100644 index 0000000000000..e712900aafbe3 --- /dev/null +++ b/libnetwork/firewall_linux_test.go @@ -0,0 +1,103 @@ +package libnetwork + +import ( + "fmt" + "strings" + "testing" + + "github.com/docker/docker/libnetwork/iptables" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/options" + "gotest.tools/v3/assert" +) + +const ( + fwdChainName = "FORWARD" + usrChainName = userChain +) + +func TestUserChain(t *testing.T) { + iptable := iptables.GetIptable(iptables.IPv4) + + nc, err := New() + assert.NilError(t, err) + + tests := []struct { + iptables bool + insert bool // insert other rules to FORWARD + fwdChain []string + userChain []string + }{ + { + iptables: false, + insert: false, + fwdChain: []string{"-P FORWARD ACCEPT"}, + }, + { + iptables: true, + insert: false, + fwdChain: []string{"-P FORWARD ACCEPT", "-A FORWARD -j DOCKER-USER"}, + userChain: []string{"-N DOCKER-USER", "-A DOCKER-USER -j RETURN"}, + }, + { + iptables: true, + insert: true, + fwdChain: []string{"-P FORWARD ACCEPT", "-A FORWARD -j DOCKER-USER", "-A FORWARD -j DROP"}, + userChain: []string{"-N DOCKER-USER", "-A DOCKER-USER -j RETURN"}, + }, + } + + resetIptables(t) + for _, tc := range tests { + tc := tc + t.Run(fmt.Sprintf("iptables=%v,insert=%v", tc.iptables, tc.insert), func(t *testing.T) { + c := nc.(*controller) + c.cfg.Daemon.DriverCfg["bridge"] = map[string]interface{}{ + netlabel.GenericData: options.Generic{ + "EnableIPTables": tc.iptables, + }, + } + + // init. condition, FORWARD chain empty DOCKER-USER not exist + assert.DeepEqual(t, getRules(t, fwdChainName), []string{"-P FORWARD ACCEPT"}) + + if tc.insert { + _, err = iptable.Raw("-A", fwdChainName, "-j", "DROP") + assert.NilError(t, err) + } + arrangeUserFilterRule() + + assert.DeepEqual(t, getRules(t, fwdChainName), tc.fwdChain) + if tc.userChain != nil { + assert.DeepEqual(t, getRules(t, usrChainName), tc.userChain) + } else { + _, err := iptable.Raw("-S", usrChainName) + assert.Assert(t, err != nil, "chain %v: created unexpectedly", usrChainName) + } + }) + resetIptables(t) + } +} + +func getRules(t *testing.T, chain string) []string { + iptable := iptables.GetIptable(iptables.IPv4) + + t.Helper() + output, err := iptable.Raw("-S", chain) + assert.NilError(t, err, "chain %s: failed to get rules", chain) + + rules := strings.Split(string(output), "\n") + if len(rules) > 0 { + rules = rules[:len(rules)-1] + } + return rules +} + +func resetIptables(t *testing.T) { + iptable := iptables.GetIptable(iptables.IPv4) + + t.Helper() + _, err := iptable.Raw("-F", fwdChainName) + assert.NilError(t, err) + _ = iptable.RemoveExistingChain(usrChainName, "") +} diff --git a/libnetwork/firewall_others.go b/libnetwork/firewall_others.go new file mode 100644 index 0000000000000..c5a1fbac9fe28 --- /dev/null +++ b/libnetwork/firewall_others.go @@ -0,0 +1,7 @@ +//go:build !linux +// +build !linux + +package libnetwork + +func setupArrangeUserFilterRule(c *controller) {} +func arrangeUserFilterRule() {} diff --git a/vendor/github.com/docker/libnetwork/hostdiscovery/hostdiscovery.go b/libnetwork/hostdiscovery/hostdiscovery.go similarity index 95% rename from vendor/github.com/docker/libnetwork/hostdiscovery/hostdiscovery.go rename to libnetwork/hostdiscovery/hostdiscovery.go index 452b5628c1365..a5dea04c2976d 100644 --- a/vendor/github.com/docker/libnetwork/hostdiscovery/hostdiscovery.go +++ b/libnetwork/hostdiscovery/hostdiscovery.go @@ -8,12 +8,13 @@ import ( mapset "github.com/deckarep/golang-set" "github.com/docker/docker/pkg/discovery" + // Including KV - _ "github.com/docker/docker/pkg/discovery/kv" + "github.com/docker/docker/libnetwork/types" + _ "github.com/docker/docker/pkg/discovery/kv" // register all the things with host discovery "github.com/docker/libkv/store/consul" "github.com/docker/libkv/store/etcd" "github.com/docker/libkv/store/zookeeper" - "github.com/docker/libnetwork/types" ) type hostDiscovery struct { diff --git a/vendor/github.com/docker/libnetwork/hostdiscovery/hostdiscovery_api.go b/libnetwork/hostdiscovery/hostdiscovery_api.go similarity index 100% rename from vendor/github.com/docker/libnetwork/hostdiscovery/hostdiscovery_api.go rename to libnetwork/hostdiscovery/hostdiscovery_api.go diff --git a/libnetwork/hostdiscovery/hostdiscovery_test.go b/libnetwork/hostdiscovery/hostdiscovery_test.go new file mode 100644 index 0000000000000..2a6b0e45845cb --- /dev/null +++ b/libnetwork/hostdiscovery/hostdiscovery_test.go @@ -0,0 +1,82 @@ +package hostdiscovery + +import ( + "net" + "testing" + + mapset "github.com/deckarep/golang-set" + + "github.com/docker/docker/pkg/discovery" +) + +func TestDiff(t *testing.T) { + existing := mapset.NewSetFromSlice([]interface{}{"1.1.1.1", "2.2.2.2"}) + addedIP := "3.3.3.3" + updated := existing.Clone() + updated.Add(addedIP) + + added, removed := diff(existing, updated) + if len(added) != 1 { + t.Fatalf("Diff failed for an Add update. Expecting 1 element, but got %d elements", len(added)) + } + if added[0].String() != addedIP { + t.Fatalf("Expecting : %v, Got : %v", addedIP, added[0]) + } + if len(removed) > 0 { + t.Fatalf("Diff failed for remove use-case. Expecting 0 element, but got %d elements", len(removed)) + } + + updated = mapset.NewSetFromSlice([]interface{}{addedIP}) + added, removed = diff(existing, updated) + if len(removed) != 2 { + t.Fatalf("Diff failed for a remove update. Expecting 2 element, but got %d elements", len(removed)) + } + if len(added) != 1 { + t.Fatalf("Diff failed for add use-case. Expecting 1 element, but got %d elements", len(added)) + } +} + +func TestAddedCallback(t *testing.T) { + hd := hostDiscovery{} + hd.nodes = mapset.NewSetFromSlice([]interface{}{"1.1.1.1"}) + update := []*discovery.Entry{{Host: "1.1.1.1", Port: "0"}, {Host: "2.2.2.2", Port: "0"}} + + added := false + removed := false + hd.processCallback(update, func() {}, func(hosts []net.IP) { added = true }, func(hosts []net.IP) { removed = true }) + if !added { + t.Fatal("Expecting an Added callback notification. But none received") + } + if removed { + t.Fatal("Not expecting a Removed callback notification. But received a callback") + } +} + +func TestRemovedCallback(t *testing.T) { + hd := hostDiscovery{} + hd.nodes = mapset.NewSetFromSlice([]interface{}{"1.1.1.1", "2.2.2.2"}) + update := []*discovery.Entry{{Host: "1.1.1.1", Port: "0"}} + + added := false + removed := false + hd.processCallback(update, func() {}, func(hosts []net.IP) { added = true }, func(hosts []net.IP) { removed = true }) + if added { + t.Fatal("Not expecting an Added callback notification. But received a callback") + } + if !removed { + t.Fatal("Expecting a Removed callback notification. But none received") + } +} + +func TestNoCallback(t *testing.T) { + hd := hostDiscovery{} + hd.nodes = mapset.NewSetFromSlice([]interface{}{"1.1.1.1", "2.2.2.2"}) + update := []*discovery.Entry{{Host: "1.1.1.1", Port: "0"}, {Host: "2.2.2.2", Port: "0"}} + + added := false + removed := false + hd.processCallback(update, func() {}, func(hosts []net.IP) { added = true }, func(hosts []net.IP) { removed = true }) + if added || removed { + t.Fatal("Not expecting any callback notification. But received a callback") + } +} diff --git a/libnetwork/hostdiscovery/libnetwork.toml b/libnetwork/hostdiscovery/libnetwork.toml new file mode 100644 index 0000000000000..7839d1e3a8b30 --- /dev/null +++ b/libnetwork/hostdiscovery/libnetwork.toml @@ -0,0 +1,6 @@ +title = "LibNetwork Configuration file" + +[cluster] + discovery = "consul://localhost:8500" + Address = "6.5.5.5" + Heartbeat = 3 diff --git a/vendor/github.com/docker/libnetwork/idm/idm.go b/libnetwork/idm/idm.go similarity index 95% rename from vendor/github.com/docker/libnetwork/idm/idm.go rename to libnetwork/idm/idm.go index d5843d4a580fd..49d16037a985e 100644 --- a/vendor/github.com/docker/libnetwork/idm/idm.go +++ b/libnetwork/idm/idm.go @@ -5,8 +5,8 @@ import ( "errors" "fmt" - "github.com/docker/libnetwork/bitseq" - "github.com/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/bitseq" + "github.com/docker/docker/libnetwork/datastore" ) // Idm manages the reservation/release of numerical ids from a contiguous set diff --git a/libnetwork/idm/idm_test.go b/libnetwork/idm/idm_test.go new file mode 100644 index 0000000000000..54055787bab1b --- /dev/null +++ b/libnetwork/idm/idm_test.go @@ -0,0 +1,294 @@ +package idm + +import ( + "testing" +) + +func TestNew(t *testing.T) { + _, err := New(nil, "", 0, 1) + if err == nil { + t.Fatal("Expected failure, but succeeded") + } + + _, err = New(nil, "myset", 1<<10, 0) + if err == nil { + t.Fatal("Expected failure, but succeeded") + } + + i, err := New(nil, "myset", 0, 10) + if err != nil { + t.Fatalf("Unexpected failure: %v", err) + } + if i.handle == nil { + t.Fatal("set is not initialized") + } + if i.start != 0 { + t.Fatal("unexpected start") + } + if i.end != 10 { + t.Fatal("unexpected end") + } +} + +func TestAllocate(t *testing.T) { + i, err := New(nil, "myids", 50, 52) + if err != nil { + t.Fatal(err) + } + + if err = i.GetSpecificID(49); err == nil { + t.Fatal("Expected failure but succeeded") + } + + if err = i.GetSpecificID(53); err == nil { + t.Fatal("Expected failure but succeeded") + } + + o, err := i.GetID(false) + if err != nil { + t.Fatal(err) + } + if o != 50 { + t.Fatalf("Unexpected first id returned: %d", o) + } + + err = i.GetSpecificID(50) + if err == nil { + t.Fatal(err) + } + + o, err = i.GetID(false) + if err != nil { + t.Fatal(err) + } + if o != 51 { + t.Fatalf("Unexpected id returned: %d", o) + } + + o, err = i.GetID(false) + if err != nil { + t.Fatal(err) + } + if o != 52 { + t.Fatalf("Unexpected id returned: %d", o) + } + + o, err = i.GetID(false) + if err == nil { + t.Fatalf("Expected failure but succeeded: %d", o) + } + + i.Release(50) + + o, err = i.GetID(false) + if err != nil { + t.Fatal(err) + } + if o != 50 { + t.Fatal("Unexpected id returned") + } + + i.Release(52) + err = i.GetSpecificID(52) + if err != nil { + t.Fatal(err) + } +} + +func TestUninitialized(t *testing.T) { + i := &Idm{} + + if _, err := i.GetID(false); err == nil { + t.Fatal("Expected failure but succeeded") + } + + if err := i.GetSpecificID(44); err == nil { + t.Fatal("Expected failure but succeeded") + } +} + +func TestAllocateInRange(t *testing.T) { + i, err := New(nil, "myset", 5, 10) + if err != nil { + t.Fatal(err) + } + + o, err := i.GetIDInRange(6, 6, false) + if err != nil { + t.Fatal(err) + } + if o != 6 { + t.Fatalf("Unexpected id returned. Expected: 6. Got: %d", o) + } + + if err = i.GetSpecificID(6); err == nil { + t.Fatalf("Expected failure but succeeded") + } + + o, err = i.GetID(false) + if err != nil { + t.Fatal(err) + } + if o != 5 { + t.Fatalf("Unexpected id returned. Expected: 5. Got: %d", o) + } + + i.Release(6) + + o, err = i.GetID(false) + if err != nil { + t.Fatal(err) + } + if o != 6 { + t.Fatalf("Unexpected id returned. Expected: 6. Got: %d", o) + } + + for n := 7; n <= 10; n++ { + o, err := i.GetIDInRange(7, 10, false) + if err != nil { + t.Fatal(err) + } + if o != uint64(n) { + t.Fatalf("Unexpected id returned. Expected: %d. Got: %d", n, o) + } + } + + if err = i.GetSpecificID(7); err == nil { + t.Fatalf("Expected failure but succeeded") + } + + if err = i.GetSpecificID(10); err == nil { + t.Fatalf("Expected failure but succeeded") + } + + i.Release(10) + + o, err = i.GetIDInRange(5, 10, false) + if err != nil { + t.Fatal(err) + } + if o != 10 { + t.Fatalf("Unexpected id returned. Expected: 10. Got: %d", o) + } + + i.Release(5) + + o, err = i.GetIDInRange(5, 10, false) + if err != nil { + t.Fatal(err) + } + if o != 5 { + t.Fatalf("Unexpected id returned. Expected: 5. Got: %d", o) + } + + for n := 5; n <= 10; n++ { + i.Release(uint64(n)) + } + + for n := 5; n <= 10; n++ { + o, err := i.GetIDInRange(5, 10, false) + if err != nil { + t.Fatal(err) + } + if o != uint64(n) { + t.Fatalf("Unexpected id returned. Expected: %d. Got: %d", n, o) + } + } + + for n := 5; n <= 10; n++ { + if err = i.GetSpecificID(uint64(n)); err == nil { + t.Fatalf("Expected failure but succeeded for id: %d", n) + } + } + + // New larger set + ul := uint64((1 << 24) - 1) + i, err = New(nil, "newset", 0, ul) + if err != nil { + t.Fatal(err) + } + + o, err = i.GetIDInRange(4096, ul, false) + if err != nil { + t.Fatal(err) + } + if o != 4096 { + t.Fatalf("Unexpected id returned. Expected: 4096. Got: %d", o) + } + + o, err = i.GetIDInRange(4096, ul, false) + if err != nil { + t.Fatal(err) + } + if o != 4097 { + t.Fatalf("Unexpected id returned. Expected: 4097. Got: %d", o) + } + + o, err = i.GetIDInRange(4096, ul, false) + if err != nil { + t.Fatal(err) + } + if o != 4098 { + t.Fatalf("Unexpected id returned. Expected: 4098. Got: %d", o) + } +} + +func TestAllocateSerial(t *testing.T) { + i, err := New(nil, "myids", 50, 55) + if err != nil { + t.Fatal(err) + } + + if err = i.GetSpecificID(49); err == nil { + t.Fatal("Expected failure but succeeded") + } + + if err = i.GetSpecificID(56); err == nil { + t.Fatal("Expected failure but succeeded") + } + + o, err := i.GetID(true) + if err != nil { + t.Fatal(err) + } + if o != 50 { + t.Fatalf("Unexpected first id returned: %d", o) + } + + err = i.GetSpecificID(50) + if err == nil { + t.Fatal(err) + } + + o, err = i.GetID(true) + if err != nil { + t.Fatal(err) + } + if o != 51 { + t.Fatalf("Unexpected id returned: %d", o) + } + + o, err = i.GetID(true) + if err != nil { + t.Fatal(err) + } + if o != 52 { + t.Fatalf("Unexpected id returned: %d", o) + } + + i.Release(50) + + o, err = i.GetID(true) + if err != nil { + t.Fatal(err) + } + if o != 53 { + t.Fatal("Unexpected id returned") + } + + i.Release(52) + err = i.GetSpecificID(52) + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/docker/libnetwork/internal/caller/caller.go b/libnetwork/internal/caller/caller.go similarity index 100% rename from vendor/github.com/docker/libnetwork/internal/caller/caller.go rename to libnetwork/internal/caller/caller.go diff --git a/libnetwork/internal/caller/caller_test.go b/libnetwork/internal/caller/caller_test.go new file mode 100644 index 0000000000000..684cf5c1a1899 --- /dev/null +++ b/libnetwork/internal/caller/caller_test.go @@ -0,0 +1,51 @@ +package caller + +import ( + "testing" +) + +func fun1() string { + return Name(0) +} + +func fun2() string { + return Name(1) +} + +func fun3() string { + return fun4() +} + +func fun4() string { + return Name(0) +} + +func fun5() string { + return fun6() +} + +func fun6() string { + return Name(1) +} + +func TestCaller(t *testing.T) { + funName := fun1() + if funName != "fun1" { + t.Fatalf("error on fun1 caller %s", funName) + } + + funName = fun2() + if funName != "TestCaller" { + t.Fatalf("error on fun2 caller %s", funName) + } + + funName = fun3() + if funName != "fun4" { + t.Fatalf("error on fun2 caller %s", funName) + } + + funName = fun5() + if funName != "fun5" { + t.Fatalf("error on fun5 caller %s", funName) + } +} diff --git a/vendor/github.com/docker/libnetwork/internal/setmatrix/setmatrix.go b/libnetwork/internal/setmatrix/setmatrix.go similarity index 100% rename from vendor/github.com/docker/libnetwork/internal/setmatrix/setmatrix.go rename to libnetwork/internal/setmatrix/setmatrix.go diff --git a/libnetwork/internal/setmatrix/setmatrix_test.go b/libnetwork/internal/setmatrix/setmatrix_test.go new file mode 100644 index 0000000000000..058a0f07cfa8a --- /dev/null +++ b/libnetwork/internal/setmatrix/setmatrix_test.go @@ -0,0 +1,183 @@ +package setmatrix + +import ( + "context" + "strconv" + "strings" + "testing" + "time" +) + +func TestSetSerialInsertDelete(t *testing.T) { + s := NewSetMatrix() + + b, i := s.Insert("a", "1") + if !b || i != 1 { + t.Fatalf("error in insert %t %d", b, i) + } + b, i = s.Insert("a", "1") + if b || i != 1 { + t.Fatalf("error in insert %t %d", b, i) + } + b, i = s.Insert("a", "2") + if !b || i != 2 { + t.Fatalf("error in insert %t %d", b, i) + } + b, i = s.Insert("a", "1") + if b || i != 2 { + t.Fatalf("error in insert %t %d", b, i) + } + b, i = s.Insert("a", "3") + if !b || i != 3 { + t.Fatalf("error in insert %t %d", b, i) + } + b, i = s.Insert("a", "2") + if b || i != 3 { + t.Fatalf("error in insert %t %d", b, i) + } + b, i = s.Insert("a", "3") + if b || i != 3 { + t.Fatalf("error in insert %t %d", b, i) + } + b, i = s.Insert("a", "4") + if !b || i != 4 { + t.Fatalf("error in insert %t %d", b, i) + } + + b, p := s.Contains("a", "1") + if !b || !p { + t.Fatalf("error in contains %t %t", b, p) + } + b, p = s.Contains("a", "2") + if !b || !p { + t.Fatalf("error in contains %t %t", b, p) + } + b, p = s.Contains("a", "3") + if !b || !p { + t.Fatalf("error in contains %t %t", b, p) + } + b, p = s.Contains("a", "4") + if !b || !p { + t.Fatalf("error in contains %t %t", b, p) + } + + i, b = s.Cardinality("a") + if !b || i != 4 { + t.Fatalf("error in cardinality count %t %d", b, i) + } + keys := s.Keys() + if len(keys) != 1 { + t.Fatalf("error in keys %v", keys) + } + str, b := s.String("a") + if !b || + !strings.Contains(str, "1") || + !strings.Contains(str, "2") || + !strings.Contains(str, "3") || + !strings.Contains(str, "4") { + t.Fatalf("error in string %t %s", b, str) + } + + _, b = s.Get("a") + if !b { + t.Fatalf("error in get %t", b) + } + + b, i = s.Remove("a", "1") + if !b || i != 3 { + t.Fatalf("error in remove %t %d", b, i) + } + b, i = s.Remove("a", "3") + if !b || i != 2 { + t.Fatalf("error in remove %t %d", b, i) + } + b, i = s.Remove("a", "1") + if b || i != 2 { + t.Fatalf("error in remove %t %d", b, i) + } + b, i = s.Remove("a", "4") + if !b || i != 1 { + t.Fatalf("error in remove %t %d", b, i) + } + b, i = s.Remove("a", "2") + if !b || i != 0 { + t.Fatalf("error in remove %t %d", b, i) + } + b, i = s.Remove("a", "2") + if b || i != 0 { + t.Fatalf("error in remove %t %d", b, i) + } + + i, b = s.Cardinality("a") + if b || i != 0 { + t.Fatalf("error in cardinality count %t %d", b, i) + } + + str, b = s.String("a") + if b || str != "" { + t.Fatalf("error in string %t %s", b, str) + } + + keys = s.Keys() + if len(keys) > 0 { + t.Fatalf("error in keys %v", keys) + } + + // Negative tests + _, b = s.Get("not exists") + if b { + t.Fatalf("error should not happen %t", b) + } + + b1, b := s.Contains("not exists", "a") + if b1 || b { + t.Fatalf("error should not happen %t %t", b1, b) + } +} + +func insertDeleteRotuine(ctx context.Context, endCh chan int, s SetMatrix, key, value string) { + for { + select { + case <-ctx.Done(): + endCh <- 0 + return + default: + b, _ := s.Insert(key, value) + if !b { + endCh <- 1 + return + } + + b, _ = s.Remove(key, value) + if !b { + endCh <- 2 + return + } + } + } +} + +func TestSetParallelInsertDelete(t *testing.T) { + s := NewSetMatrix() + parallelRoutines := 6 + endCh := make(chan int) + // Let the routines running and competing for 10s + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + for i := 0; i < parallelRoutines; i++ { + go insertDeleteRotuine(ctx, endCh, s, "key-"+strconv.Itoa(i%3), strconv.Itoa(i)) + } + for parallelRoutines > 0 { + v := <-endCh + if v == 1 { + t.Fatalf("error one goroutine failed on the insert") + } + if v == 2 { + t.Fatalf("error one goroutine failed on the remove") + } + parallelRoutines-- + } + if i, b := s.Cardinality("key"); b || i > 0 { + t.Fatalf("error the set should be empty %t %d", b, i) + } +} diff --git a/vendor/github.com/docker/libnetwork/ipam/allocator.go b/libnetwork/ipam/allocator.go similarity index 97% rename from vendor/github.com/docker/libnetwork/ipam/allocator.go rename to libnetwork/ipam/allocator.go index 73a682aba03c8..d7ed917637419 100644 --- a/vendor/github.com/docker/libnetwork/ipam/allocator.go +++ b/libnetwork/ipam/allocator.go @@ -6,21 +6,18 @@ import ( "sort" "sync" - "github.com/docker/libnetwork/bitseq" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/ipamutils" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/bitseq" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/ipamutils" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) const ( localAddressSpace = "LocalDefault" globalAddressSpace = "GlobalDefault" - // The biggest configurable host subnets - minNetSize = 8 - minNetSizeV6 = 64 // datastore keyes for ipam objects dsConfigKey = "ipam/" + ipamapi.DefaultIPAM + "/config" dsDataKey = "ipam/" + ipamapi.DefaultIPAM + "/data" @@ -103,11 +100,9 @@ func (a *Allocator) updateBitMasks(aSpace *addrSpace) error { aSpace.Unlock() // Add the bitmasks (data could come from datastore) - if inserterList != nil { - for _, f := range inserterList { - if err := f(); err != nil { - return err - } + for _, f := range inserterList { + if err := f(); err != nil { + return err } } @@ -572,7 +567,7 @@ func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddres logrus.Debugf("Request address PoolID:%v %s Serial:%v PrefAddress:%v ", nw, bitmask.String(), serial, prefAddress) base = types.GetIPNetCopy(nw) - if bitmask.Unselected() <= 0 { + if bitmask.Unselected() == 0 { return nil, ipamapi.ErrNoAvailableIPs } if ipr == nil && prefAddress == nil { diff --git a/libnetwork/ipam/allocator_test.go b/libnetwork/ipam/allocator_test.go new file mode 100644 index 0000000000000..8b05d32371a03 --- /dev/null +++ b/libnetwork/ipam/allocator_test.go @@ -0,0 +1,1554 @@ +package ipam + +import ( + "encoding/json" + "flag" + "fmt" + "math/rand" + "net" + "os" + "path/filepath" + "strconv" + "sync" + "testing" + "time" + + "github.com/docker/docker/libnetwork/bitseq" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/types" + "github.com/docker/libkv/store" + "github.com/docker/libkv/store/boltdb" + "golang.org/x/sync/errgroup" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +var ( + defaultPrefix = filepath.Join(os.TempDir(), "libnetwork", "test", "ipam") +) + +func init() { + boltdb.Register() +} + +// OptionBoltdbWithRandomDBFile function returns a random dir for local store backend +func randomLocalStore(needStore bool) (datastore.DataStore, error) { + if !needStore { + return nil, nil + } + tmp, err := os.CreateTemp("", "libnetwork-") + if err != nil { + return nil, fmt.Errorf("Error creating temp file: %v", err) + } + if err := tmp.Close(); err != nil { + return nil, fmt.Errorf("Error closing temp file: %v", err) + } + return datastore.NewDataStore(datastore.LocalScope, &datastore.ScopeCfg{ + Client: datastore.ScopeClientCfg{ + Provider: "boltdb", + Address: filepath.Join(defaultPrefix, filepath.Base(tmp.Name())), + Config: &store.Config{ + Bucket: "libnetwork", + ConnectionTimeout: 3 * time.Second, + }, + }, + }) +} + +func getAllocator(store bool) (*Allocator, error) { + ds, err := randomLocalStore(store) + if err != nil { + return nil, err + } + return NewAllocator(ds, nil) +} + +func TestInt2IP2IntConversion(t *testing.T) { + for i := uint64(0); i < 256*256*256; i++ { + var array [4]byte // new array at each cycle + addIntToIP(array[:], i) + j := ipToUint64(array[:]) + if j != i { + t.Fatalf("Failed to convert ordinal %d to IP % x and back to ordinal. Got %d", i, array, j) + } + } +} + +func TestGetAddressVersion(t *testing.T) { + if v4 != getAddressVersion(net.ParseIP("172.28.30.112")) { + t.Fatal("Failed to detect IPv4 version") + } + if v4 != getAddressVersion(net.ParseIP("0.0.0.1")) { + t.Fatal("Failed to detect IPv4 version") + } + if v6 != getAddressVersion(net.ParseIP("ff01::1")) { + t.Fatal("Failed to detect IPv6 version") + } + if v6 != getAddressVersion(net.ParseIP("2001:db8::76:51")) { + t.Fatal("Failed to detect IPv6 version") + } +} + +func TestKeyString(t *testing.T) { + k := &SubnetKey{AddressSpace: "default", Subnet: "172.27.0.0/16"} + expected := "default/172.27.0.0/16" + if expected != k.String() { + t.Fatalf("Unexpected key string: %s", k.String()) + } + + k2 := &SubnetKey{} + err := k2.FromString(expected) + if err != nil { + t.Fatal(err) + } + if k2.AddressSpace != k.AddressSpace || k2.Subnet != k.Subnet { + t.Fatalf("SubnetKey.FromString() failed. Expected %v. Got %v", k, k2) + } + + expected = fmt.Sprintf("%s/%s", expected, "172.27.3.0/24") + k.ChildSubnet = "172.27.3.0/24" + if expected != k.String() { + t.Fatalf("Unexpected key string: %s", k.String()) + } + + err = k2.FromString(expected) + if err != nil { + t.Fatal(err) + } + if k2.AddressSpace != k.AddressSpace || k2.Subnet != k.Subnet || k2.ChildSubnet != k.ChildSubnet { + t.Fatalf("SubnetKey.FromString() failed. Expected %v. Got %v", k, k2) + } +} + +func TestPoolDataMarshal(t *testing.T) { + _, nw, err := net.ParseCIDR("172.28.30.1/24") + if err != nil { + t.Fatal(err) + } + + p := &PoolData{ + ParentKey: SubnetKey{AddressSpace: "Blue", Subnet: "172.28.0.0/16"}, + Pool: nw, + Range: &AddressRange{Sub: &net.IPNet{IP: net.IP{172, 28, 20, 0}, Mask: net.IPMask{255, 255, 255, 0}}, Start: 0, End: 255}, + RefCount: 4, + } + + ba, err := json.Marshal(p) + if err != nil { + t.Fatal(err) + } + var q PoolData + err = json.Unmarshal(ba, &q) + if err != nil { + t.Fatal(err) + } + + if p.ParentKey != q.ParentKey || !types.CompareIPNet(p.Range.Sub, q.Range.Sub) || + p.Range.Start != q.Range.Start || p.Range.End != q.Range.End || p.RefCount != q.RefCount || + !types.CompareIPNet(p.Pool, q.Pool) { + t.Fatalf("\n%#v\n%#v", p, &q) + } + + p = &PoolData{ + ParentKey: SubnetKey{AddressSpace: "Blue", Subnet: "172.28.0.0/16"}, + Pool: nw, + RefCount: 4, + } + + ba, err = json.Marshal(p) + if err != nil { + t.Fatal(err) + } + err = json.Unmarshal(ba, &q) + if err != nil { + t.Fatal(err) + } + + if q.Range != nil { + t.Fatal("Unexpected Range") + } +} + +func TestSubnetsMarshal(t *testing.T) { + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + if err != nil { + t.Fatal(err) + } + pid0, _, _, err := a.RequestPool(localAddressSpace, "192.168.0.0/16", "", nil, false) + if err != nil { + t.Fatal(err) + } + pid1, _, _, err := a.RequestPool(localAddressSpace, "192.169.0.0/16", "", nil, false) + if err != nil { + t.Fatal(err) + } + _, _, err = a.RequestAddress(pid0, nil, nil) + if err != nil { + t.Fatal(err) + } + + cfg, err := a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } + + ba := cfg.Value() + if err := cfg.SetValue(ba); err != nil { + t.Fatal(err) + } + + expIP := &net.IPNet{IP: net.IP{192, 168, 0, 2}, Mask: net.IPMask{255, 255, 0, 0}} + ip, _, err := a.RequestAddress(pid0, nil, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(expIP, ip) { + t.Fatalf("Got unexpected ip after pool config restore: %s", ip) + } + + expIP = &net.IPNet{IP: net.IP{192, 169, 0, 1}, Mask: net.IPMask{255, 255, 0, 0}} + ip, _, err = a.RequestAddress(pid1, nil, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(expIP, ip) { + t.Fatalf("Got unexpected ip after pool config restore: %s", ip) + } + } +} + +func TestAddSubnets(t *testing.T) { + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + if err != nil { + t.Fatal(err) + } + a.addrSpaces["abc"] = a.addrSpaces[localAddressSpace] + + pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatal("Unexpected failure in adding subnet") + } + + pid1, _, _, err := a.RequestPool("abc", "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatalf("Unexpected failure in adding overlapping subnets to different address spaces: %v", err) + } + + if pid0 == pid1 { + t.Fatal("returned same pool id for same subnets in different namespaces") + } + + _, _, _, err = a.RequestPool("abc", "10.0.0.0/8", "", nil, false) + if err == nil { + t.Fatalf("Expected failure requesting existing subnet") + } + + _, _, _, err = a.RequestPool("abc", "10.128.0.0/9", "", nil, false) + if err == nil { + t.Fatal("Expected failure on adding overlapping base subnet") + } + + _, _, _, err = a.RequestPool("abc", "10.0.0.0/8", "10.128.0.0/9", nil, false) + if err != nil { + t.Fatalf("Unexpected failure on adding sub pool: %v", err) + } + _, _, _, err = a.RequestPool("abc", "10.0.0.0/8", "10.128.0.0/9", nil, false) + if err == nil { + t.Fatalf("Expected failure on adding overlapping sub pool") + } + + _, _, _, err = a.RequestPool(localAddressSpace, "10.20.2.0/24", "", nil, false) + if err == nil { + t.Fatal("Failed to detect overlapping subnets") + } + + _, _, _, err = a.RequestPool(localAddressSpace, "10.128.0.0/9", "", nil, false) + if err == nil { + t.Fatal("Failed to detect overlapping subnets") + } + + _, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3:4:5:6::/112", "", nil, false) + if err != nil { + t.Fatalf("Failed to add v6 subnet: %s", err.Error()) + } + + _, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3::/64", "", nil, false) + if err == nil { + t.Fatal("Failed to detect overlapping v6 subnet") + } + } +} + +// TestDoublePoolRelease tests that releasing a pool which has already +// been released raises an error. +func TestDoublePoolRelease(t *testing.T) { + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) + assert.NilError(t, err) + + err = a.ReleasePool(pid0) + assert.NilError(t, err) + + err = a.ReleasePool(pid0) + assert.Check(t, is.ErrorContains(err, "")) + } +} + +func TestAddReleasePoolID(t *testing.T) { + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + var k0, k1 SubnetKey + _, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } + + pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatal("Unexpected failure in adding pool") + } + if err := k0.FromString(pid0); err != nil { + t.Fatal(err) + } + + aSpace, err := a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } + + subnets := aSpace.subnets + + if subnets[k0].RefCount != 1 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } + + pid1, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) + if err != nil { + t.Fatal("Unexpected failure in adding sub pool") + } + if err := k1.FromString(pid1); err != nil { + t.Fatal(err) + } + + if pid0 == pid1 { + t.Fatalf("Incorrect poolIDs returned %s, %s", pid0, pid1) + } + + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } + + subnets = aSpace.subnets + if subnets[k1].RefCount != 1 { + t.Fatalf("Unexpected ref count for %s: %d", k1, subnets[k1].RefCount) + } + + _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) + if err == nil { + t.Fatal("Expected failure in adding sub pool") + } + + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } + + subnets = aSpace.subnets + + if subnets[k0].RefCount != 2 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } + + if err := a.ReleasePool(pid1); err != nil { + t.Fatal(err) + } + + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } + + subnets = aSpace.subnets + if subnets[k0].RefCount != 1 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } + if err := a.ReleasePool(pid0); err != nil { + t.Fatal(err) + } + + pid00, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatal("Unexpected failure in adding pool") + } + if pid00 != pid0 { + t.Fatal("main pool should still exist") + } + + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } + + subnets = aSpace.subnets + if subnets[k0].RefCount != 1 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } + + if err := a.ReleasePool(pid00); err != nil { + t.Fatal(err) + } + + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } + + subnets = aSpace.subnets + if bp, ok := subnets[k0]; ok { + t.Fatalf("Base pool %s is still present: %v", k0, bp) + } + + _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) + if err != nil { + t.Fatal("Unexpected failure in adding pool") + } + + aSpace, err = a.getAddrSpace(localAddressSpace) + if err != nil { + t.Fatal(err) + } + + subnets = aSpace.subnets + if subnets[k0].RefCount != 1 { + t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount) + } + } +} + +func TestPredefinedPool(t *testing.T) { + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + if _, err := a.getPredefinedPool("blue", false); err == nil { + t.Fatal("Expected failure for non default addr space") + } + + pid, nw, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) + if err != nil { + t.Fatal(err) + } + + nw2, err := a.getPredefinedPool(localAddressSpace, false) + if err != nil { + t.Fatal(err) + } + if types.CompareIPNet(nw, nw2) { + t.Fatalf("Unexpected default network returned: %s = %s", nw2, nw) + } + + if err := a.ReleasePool(pid); err != nil { + t.Fatal(err) + } + } +} + +func TestRemoveSubnet(t *testing.T) { + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + a.addrSpaces["splane"] = &addrSpace{ + id: dsConfigKey + "/" + "splane", + ds: a.addrSpaces[localAddressSpace].ds, + alloc: a.addrSpaces[localAddressSpace].alloc, + scope: a.addrSpaces[localAddressSpace].scope, + subnets: map[SubnetKey]*PoolData{}, + } + + input := []struct { + addrSpace string + subnet string + v6 bool + }{ + {localAddressSpace, "192.168.0.0/16", false}, + {localAddressSpace, "172.17.0.0/16", false}, + {localAddressSpace, "10.0.0.0/8", false}, + {localAddressSpace, "2001:db8:1:2:3:4:ffff::/112", false}, + {"splane", "172.17.0.0/16", false}, + {"splane", "10.0.0.0/8", false}, + {"splane", "2001:db8:1:2:3:4:5::/112", true}, + {"splane", "2001:db8:1:2:3:4:ffff::/112", true}, + } + + poolIDs := make([]string, len(input)) + + for ind, i := range input { + if poolIDs[ind], _, _, err = a.RequestPool(i.addrSpace, i.subnet, "", nil, i.v6); err != nil { + t.Fatalf("Failed to apply input. Can't proceed: %s", err.Error()) + } + } + + for ind, id := range poolIDs { + if err := a.ReleasePool(id); err != nil { + t.Fatalf("Failed to release poolID %s (%d)", id, ind) + } + } + } +} + +func TestGetSameAddress(t *testing.T) { + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + a.addrSpaces["giallo"] = &addrSpace{ + id: dsConfigKey + "/" + "giallo", + ds: a.addrSpaces[localAddressSpace].ds, + alloc: a.addrSpaces[localAddressSpace].alloc, + scope: a.addrSpaces[localAddressSpace].scope, + subnets: map[SubnetKey]*PoolData{}, + } + + pid, _, _, err := a.RequestPool("giallo", "192.168.100.0/24", "", nil, false) + if err != nil { + t.Fatal(err) + } + + ip := net.ParseIP("192.168.100.250") + _, _, err = a.RequestAddress(pid, ip, nil) + if err != nil { + t.Fatal(err) + } + + _, _, err = a.RequestAddress(pid, ip, nil) + if err == nil { + t.Fatal(err) + } + } +} + +func TestPoolAllocationReuse(t *testing.T) { + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + // First get all pools until they are exhausted to + pList := []string{} + pool, _, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) + for err == nil { + pList = append(pList, pool) + pool, _, _, err = a.RequestPool(localAddressSpace, "", "", nil, false) + } + nPools := len(pList) + for _, pool := range pList { + if err := a.ReleasePool(pool); err != nil { + t.Fatal(err) + } + } + + // Now try to allocate then free nPool pools sequentially. + // Verify that we don't see any repeat networks even though + // we have freed them. + seen := map[string]bool{} + for i := 0; i < nPools; i++ { + pool, nw, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) + if err != nil { + t.Fatal(err) + } + if _, ok := seen[nw.String()]; ok { + t.Fatalf("Network %s was reused before exhausing the pool list", nw.String()) + } + seen[nw.String()] = true + if err := a.ReleasePool(pool); err != nil { + t.Fatal(err) + } + } + } +} + +func TestGetAddressSubPoolEqualPool(t *testing.T) { + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + // Requesting a subpool of same size of the master pool should not cause any problem on ip allocation + pid, _, _, err := a.RequestPool(localAddressSpace, "172.18.0.0/16", "172.18.0.0/16", nil, false) + if err != nil { + t.Fatal(err) + } + + _, _, err = a.RequestAddress(pid, nil, nil) + if err != nil { + t.Fatal(err) + } + } +} + +func TestRequestReleaseAddressFromSubPool(t *testing.T) { + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + a.addrSpaces["rosso"] = &addrSpace{ + id: dsConfigKey + "/" + "rosso", + ds: a.addrSpaces[localAddressSpace].ds, + alloc: a.addrSpaces[localAddressSpace].alloc, + scope: a.addrSpaces[localAddressSpace].scope, + subnets: map[SubnetKey]*PoolData{}, + } + + poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false) + if err != nil { + t.Fatal(err) + } + + var ip *net.IPNet + expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}} + for err == nil { + var c *net.IPNet + if c, _, err = a.RequestAddress(poolID, nil, nil); err == nil { + ip = c + } + } + if err != ipamapi.ErrNoAvailableIPs { + t.Fatal(err) + } + if !types.CompareIPNet(expected, ip) { + t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) + } + rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}} + if err = a.ReleaseAddress(poolID, rp.IP); err != nil { + t.Fatal(err) + } + if ip, _, err = a.RequestAddress(poolID, nil, nil); err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(rp, ip) { + t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + } + + _, _, _, err = a.RequestPool("rosso", "10.0.0.0/8", "10.0.0.0/16", nil, false) + if err != nil { + t.Fatal(err) + } + poolID, _, _, err = a.RequestPool("rosso", "10.0.0.0/16", "10.0.0.0/24", nil, false) + if err != nil { + t.Fatal(err) + } + expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}} + for err == nil { + var c *net.IPNet + if c, _, err = a.RequestAddress(poolID, nil, nil); err == nil { + ip = c + } + } + if err != ipamapi.ErrNoAvailableIPs { + t.Fatal(err) + } + if !types.CompareIPNet(expected, ip) { + t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) + } + rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}} + if err = a.ReleaseAddress(poolID, rp.IP); err != nil { + t.Fatal(err) + } + if ip, _, err = a.RequestAddress(poolID, nil, nil); err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(rp, ip) { + t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + } + + // Request any addresses from subpool after explicit address request + unoExp, _ := types.ParseCIDR("10.2.2.0/16") + dueExp, _ := types.ParseCIDR("10.2.2.2/16") + treExp, _ := types.ParseCIDR("10.2.2.1/16") + + if poolID, _, _, err = a.RequestPool("rosso", "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil { + t.Fatal(err) + } + tre, _, err := a.RequestAddress(poolID, treExp.IP, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(tre, treExp) { + t.Fatalf("Unexpected address: %v", tre) + } + + uno, _, err := a.RequestAddress(poolID, nil, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(uno, unoExp) { + t.Fatalf("Unexpected address: %v", uno) + } + + due, _, err := a.RequestAddress(poolID, nil, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(due, dueExp) { + t.Fatalf("Unexpected address: %v", due) + } + + if err = a.ReleaseAddress(poolID, uno.IP); err != nil { + t.Fatal(err) + } + uno, _, err = a.RequestAddress(poolID, nil, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(uno, unoExp) { + t.Fatalf("Unexpected address: %v", uno) + } + + if err = a.ReleaseAddress(poolID, tre.IP); err != nil { + t.Fatal(err) + } + tre, _, err = a.RequestAddress(poolID, nil, nil) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(tre, treExp) { + t.Fatalf("Unexpected address: %v", tre) + } + } +} + +func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) { + opts := map[string]string{ + ipamapi.AllocSerialPrefix: "true"} + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + a.addrSpaces["rosso"] = &addrSpace{ + id: dsConfigKey + "/" + "rosso", + ds: a.addrSpaces[localAddressSpace].ds, + alloc: a.addrSpaces[localAddressSpace].alloc, + scope: a.addrSpaces[localAddressSpace].scope, + subnets: map[SubnetKey]*PoolData{}, + } + + poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false) + if err != nil { + t.Fatal(err) + } + + var ip *net.IPNet + expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}} + for err == nil { + var c *net.IPNet + if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil { + ip = c + } + } + if err != ipamapi.ErrNoAvailableIPs { + t.Fatal(err) + } + if !types.CompareIPNet(expected, ip) { + t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) + } + rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}} + if err = a.ReleaseAddress(poolID, rp.IP); err != nil { + t.Fatal(err) + } + if ip, _, err = a.RequestAddress(poolID, nil, opts); err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(rp, ip) { + t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + } + + _, _, _, err = a.RequestPool("rosso", "10.0.0.0/8", "10.0.0.0/16", nil, false) + if err != nil { + t.Fatal(err) + } + poolID, _, _, err = a.RequestPool("rosso", "10.0.0.0/16", "10.0.0.0/24", nil, false) + if err != nil { + t.Fatal(err) + } + expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}} + for err == nil { + var c *net.IPNet + if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil { + ip = c + } + } + if err != ipamapi.ErrNoAvailableIPs { + t.Fatal(err) + } + if !types.CompareIPNet(expected, ip) { + t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) + } + rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}} + if err = a.ReleaseAddress(poolID, rp.IP); err != nil { + t.Fatal(err) + } + if ip, _, err = a.RequestAddress(poolID, nil, opts); err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(rp, ip) { + t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) + } + + // Request any addresses from subpool after explicit address request + unoExp, _ := types.ParseCIDR("10.2.2.0/16") + dueExp, _ := types.ParseCIDR("10.2.2.2/16") + treExp, _ := types.ParseCIDR("10.2.2.1/16") + quaExp, _ := types.ParseCIDR("10.2.2.3/16") + fivExp, _ := types.ParseCIDR("10.2.2.4/16") + if poolID, _, _, err = a.RequestPool("rosso", "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil { + t.Fatal(err) + } + tre, _, err := a.RequestAddress(poolID, treExp.IP, opts) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(tre, treExp) { + t.Fatalf("Unexpected address: %v", tre) + } + + uno, _, err := a.RequestAddress(poolID, nil, opts) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(uno, unoExp) { + t.Fatalf("Unexpected address: %v", uno) + } + + due, _, err := a.RequestAddress(poolID, nil, opts) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(due, dueExp) { + t.Fatalf("Unexpected address: %v", due) + } + + if err = a.ReleaseAddress(poolID, uno.IP); err != nil { + t.Fatal(err) + } + uno, _, err = a.RequestAddress(poolID, nil, opts) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(uno, quaExp) { + t.Fatalf("Unexpected address: %v", uno) + } + + if err = a.ReleaseAddress(poolID, tre.IP); err != nil { + t.Fatal(err) + } + tre, _, err = a.RequestAddress(poolID, nil, opts) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(tre, fivExp) { + t.Fatalf("Unexpected address: %v", tre) + } + } +} + +func TestGetAddress(t *testing.T) { + input := []string{ + /*"10.0.0.0/8", "10.0.0.0/9", "10.0.0.0/10",*/ "10.0.0.0/11", "10.0.0.0/12", "10.0.0.0/13", "10.0.0.0/14", + "10.0.0.0/15", "10.0.0.0/16", "10.0.0.0/17", "10.0.0.0/18", "10.0.0.0/19", "10.0.0.0/20", "10.0.0.0/21", + "10.0.0.0/22", "10.0.0.0/23", "10.0.0.0/24", "10.0.0.0/25", "10.0.0.0/26", "10.0.0.0/27", "10.0.0.0/28", + "10.0.0.0/29", "10.0.0.0/30", "10.0.0.0/31"} + + for _, subnet := range input { + assertGetAddress(t, subnet) + } +} + +func TestRequestSyntaxCheck(t *testing.T) { + var ( + pool = "192.168.0.0/16" + subPool = "192.168.0.0/24" + as = "green" + ) + + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + a.addrSpaces[as] = &addrSpace{ + id: dsConfigKey + "/" + as, + ds: a.addrSpaces[localAddressSpace].ds, + alloc: a.addrSpaces[localAddressSpace].alloc, + scope: a.addrSpaces[localAddressSpace].scope, + subnets: map[SubnetKey]*PoolData{}, + } + + _, _, _, err = a.RequestPool("", pool, "", nil, false) + if err == nil { + t.Fatal("Failed to detect wrong request: empty address space") + } + + _, _, _, err = a.RequestPool("", pool, subPool, nil, false) + if err == nil { + t.Fatal("Failed to detect wrong request: empty address space") + } + + _, _, _, err = a.RequestPool(as, "", subPool, nil, false) + if err == nil { + t.Fatal("Failed to detect wrong request: subPool specified and no pool") + } + + pid, _, _, err := a.RequestPool(as, pool, subPool, nil, false) + if err != nil { + t.Fatalf("Unexpected failure: %v", err) + } + + _, _, err = a.RequestAddress("", nil, nil) + if err == nil { + t.Fatal("Failed to detect wrong request: no pool id specified") + } + + ip := net.ParseIP("172.17.0.23") + _, _, err = a.RequestAddress(pid, ip, nil) + if err == nil { + t.Fatal("Failed to detect wrong request: requested IP from different subnet") + } + + ip = net.ParseIP("192.168.0.50") + _, _, err = a.RequestAddress(pid, ip, nil) + if err != nil { + t.Fatalf("Unexpected failure: %v", err) + } + + err = a.ReleaseAddress("", ip) + if err == nil { + t.Fatal("Failed to detect wrong request: no pool id specified") + } + + err = a.ReleaseAddress(pid, nil) + if err == nil { + t.Fatal("Failed to detect wrong request: no pool id specified") + } + + err = a.ReleaseAddress(pid, ip) + if err != nil { + t.Fatalf("Unexpected failure: %v: %s, %s", err, pid, ip) + } + } +} + +func TestRequest(t *testing.T) { + // Request N addresses from different size subnets, verifying last request + // returns expected address. Internal subnet host size is Allocator's default, 16 + input := []struct { + subnet string + numReq int + lastIP string + }{ + {"192.168.59.0/24", 254, "192.168.59.254"}, + {"192.168.240.0/20", 255, "192.168.240.255"}, + {"192.168.0.0/16", 255, "192.168.0.255"}, + {"192.168.0.0/16", 256, "192.168.1.0"}, + {"10.16.0.0/16", 255, "10.16.0.255"}, + {"10.128.0.0/12", 255, "10.128.0.255"}, + {"10.0.0.0/8", 256, "10.0.1.0"}, + + {"192.168.128.0/18", 4*256 - 1, "192.168.131.255"}, + /* + {"192.168.240.0/20", 16*256 - 2, "192.168.255.254"}, + + {"192.168.0.0/16", 256*256 - 2, "192.168.255.254"}, + {"10.0.0.0/8", 2 * 256, "10.0.2.0"}, + {"10.0.0.0/8", 5 * 256, "10.0.5.0"}, + {"10.0.0.0/8", 100 * 256 * 254, "10.99.255.254"}, + */ + } + + for _, d := range input { + assertNRequests(t, d.subnet, d.numReq, d.lastIP) + } +} + +// TestOverlappingRequests tests that overlapping subnets cannot be allocated. +// Requests for subnets which are supersets or subsets of existing allocations, +// or which overlap at the beginning or end, should not be permitted. +func TestOverlappingRequests(t *testing.T) { + input := []struct { + environment []string + subnet string + ok bool + }{ + // IPv4 + // Previously allocated network does not overlap with request + {[]string{"10.0.0.0/8"}, "11.0.0.0/8", true}, + {[]string{"74.0.0.0/7"}, "9.111.99.72/30", true}, + {[]string{"110.192.0.0/10"}, "16.0.0.0/10", true}, + + // Previously allocated network entirely contains request + {[]string{"10.0.0.0/8"}, "10.0.0.0/8", false}, // exact overlap + {[]string{"0.0.0.0/1"}, "16.182.0.0/15", false}, + {[]string{"16.0.0.0/4"}, "17.11.66.0/23", false}, + + // Previously allocated network overlaps beginning of request + {[]string{"0.0.0.0/1"}, "0.0.0.0/0", false}, + {[]string{"64.0.0.0/6"}, "64.0.0.0/3", false}, + {[]string{"112.0.0.0/6"}, "112.0.0.0/4", false}, + + // Previously allocated network overlaps end of request + {[]string{"96.0.0.0/3"}, "0.0.0.0/1", false}, + {[]string{"192.0.0.0/2"}, "128.0.0.0/1", false}, + {[]string{"95.0.0.0/8"}, "92.0.0.0/6", false}, + + // Previously allocated network entirely contained within request + {[]string{"10.0.0.0/8"}, "10.0.0.0/6", false}, // non-canonical + {[]string{"10.0.0.0/8"}, "8.0.0.0/6", false}, // canonical + {[]string{"25.173.144.0/20"}, "0.0.0.0/0", false}, + + // IPv6 + // Previously allocated network entirely contains request + {[]string{"::/0"}, "f656:3484:c878:a05:e540:a6ed:4d70:3740/123", false}, + {[]string{"8000::/1"}, "8fe8:e7c4:5779::/49", false}, + {[]string{"f000::/4"}, "ffc7:6000::/19", false}, + + // Previously allocated network overlaps beginning of request + {[]string{"::/2"}, "::/0", false}, + {[]string{"::/3"}, "::/1", false}, + {[]string{"::/6"}, "::/5", false}, + + // Previously allocated network overlaps end of request + {[]string{"c000::/2"}, "8000::/1", false}, + {[]string{"7c00::/6"}, "::/1", false}, + {[]string{"cf80::/9"}, "c000::/4", false}, + + // Previously allocated network entirely contained within request + {[]string{"ff77:93f8::/29"}, "::/0", false}, + {[]string{"9287:2e20:5134:fab6:9061:a0c6:bfe3:9400/119"}, "8000::/1", false}, + {[]string{"3ea1:bfa9:8691:d1c6:8c46:519b:db6d:e700/120"}, "3000::/4", false}, + } + + for _, store := range []bool{false, true} { + for _, tc := range input { + a, err := getAllocator(store) + assert.NilError(t, err) + + // Set up some existing allocations. This should always succeed. + for _, env := range tc.environment { + _, _, _, err = a.RequestPool(localAddressSpace, env, "", nil, false) + assert.NilError(t, err) + } + + // Make the test allocation. + _, _, _, err = a.RequestPool(localAddressSpace, tc.subnet, "", nil, false) + if tc.ok { + assert.NilError(t, err) + } else { + assert.Check(t, is.ErrorContains(err, "")) + } + } + } +} + +func TestRelease(t *testing.T) { + var ( + subnet = "192.168.0.0/23" + ) + + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) + if err != nil { + t.Fatal(err) + } + + // Allocate all addresses + for err != ipamapi.ErrNoAvailableIPs { + _, _, err = a.RequestAddress(pid, nil, nil) + } + + toRelease := []struct { + address string + }{ + {"192.168.0.1"}, + {"192.168.0.2"}, + {"192.168.0.3"}, + {"192.168.0.4"}, + {"192.168.0.5"}, + {"192.168.0.6"}, + {"192.168.0.7"}, + {"192.168.0.8"}, + {"192.168.0.9"}, + {"192.168.0.10"}, + {"192.168.0.30"}, + {"192.168.0.31"}, + {"192.168.1.32"}, + + {"192.168.0.254"}, + {"192.168.1.1"}, + {"192.168.1.2"}, + + {"192.168.1.3"}, + + {"192.168.1.253"}, + {"192.168.1.254"}, + } + + // One by one, release the address and request again. We should get the same IP + for i, inp := range toRelease { + ip0 := net.ParseIP(inp.address) + a.ReleaseAddress(pid, ip0) + bm := a.addresses[SubnetKey{localAddressSpace, subnet, ""}] + if bm.Unselected() != 1 { + t.Fatalf("Failed to update free address count after release. Expected %d, Found: %d", i+1, bm.Unselected()) + } + + nw, _, err := a.RequestAddress(pid, nil, nil) + if err != nil { + t.Fatalf("Failed to obtain the address: %s", err.Error()) + } + ip := nw.IP + if !ip0.Equal(ip) { + t.Fatalf("Failed to obtain the same address. Expected: %s, Got: %s", ip0, ip) + } + } + } +} + +func assertGetAddress(t *testing.T, subnet string) { + var ( + err error + printTime = false + a = &Allocator{} + ) + + _, sub, _ := net.ParseCIDR(subnet) + ones, bits := sub.Mask.Size() + zeroes := bits - ones + numAddresses := 1 << uint(zeroes) + + bm, err := bitseq.NewHandle("ipam_test", nil, "default/"+subnet, uint64(numAddresses)) + if err != nil { + t.Fatal(err) + } + + start := time.Now() + run := 0 + for err != ipamapi.ErrNoAvailableIPs { + _, err = a.getAddress(sub, bm, nil, nil, false) + run++ + } + if printTime { + fmt.Printf("\nTaken %v, to allocate all addresses on %s. (nemAddresses: %d. Runs: %d)", time.Since(start), subnet, numAddresses, run) + } + if bm.Unselected() != 0 { + t.Fatalf("Unexpected free count after reserving all addresses: %d", bm.Unselected()) + } + /* + if bm.Head.Block != expectedMax || bm.Head.Count != numBlocks { + t.Fatalf("Failed to effectively reserve all addresses on %s. Expected (0x%x, %d) as first sequence. Found (0x%x,%d)", + subnet, expectedMax, numBlocks, bm.Head.Block, bm.Head.Count) + } + */ +} + +func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP string) { + var ( + nw *net.IPNet + printTime = false + ) + + lastIP := net.ParseIP(lastExpectedIP) + for _, store := range []bool{false, true} { + a, err := getAllocator(store) + assert.NilError(t, err) + + pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) + if err != nil { + t.Fatal(err) + } + + i := 0 + start := time.Now() + for ; i < numReq; i++ { + nw, _, err = a.RequestAddress(pid, nil, nil) + } + if printTime { + fmt.Printf("\nTaken %v, to allocate %d addresses on %s\n", time.Since(start), numReq, subnet) + } + + if !lastIP.Equal(nw.IP) { + t.Fatalf("Wrong last IP. Expected %s. Got: %s (err: %v, ind: %d)", lastExpectedIP, nw.IP.String(), err, i) + } + } +} + +func benchmarkRequest(b *testing.B, a *Allocator, subnet string) { + pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) + for err != ipamapi.ErrNoAvailableIPs { + _, _, err = a.RequestAddress(pid, nil, nil) + } +} + +func BenchmarkRequest(b *testing.B) { + + subnets := []string{ + "10.0.0.0/24", + "10.0.0.0/16", + "10.0.0.0/8", + } + + for _, subnet := range subnets { + name := fmt.Sprintf("%vSubnet", subnet) + b.Run(name, func(b *testing.B) { + a, _ := getAllocator(true) + benchmarkRequest(b, a, subnet) + }) + } +} + +func TestAllocateRandomDeallocate(t *testing.T) { + for _, store := range []bool{false, true} { + testAllocateRandomDeallocate(t, "172.25.0.0/16", "", 384, store) + testAllocateRandomDeallocate(t, "172.25.0.0/16", "172.25.252.0/22", 384, store) + } +} + +func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int, store bool) { + ds, err := randomLocalStore(store) + assert.NilError(t, err) + + a, err := NewAllocator(ds, nil) + if err != nil { + t.Fatal(err) + } + + pid, _, _, err := a.RequestPool(localAddressSpace, pool, subPool, nil, false) + if err != nil { + t.Fatal(err) + } + + // Allocate num ip addresses + indices := make(map[int]*net.IPNet, num) + allocated := make(map[string]bool, num) + for i := 0; i < num; i++ { + ip, _, err := a.RequestAddress(pid, nil, nil) + if err != nil { + t.Fatal(err) + } + ips := ip.String() + if _, ok := allocated[ips]; ok { + t.Fatalf("Address %s is already allocated", ips) + } + allocated[ips] = true + indices[i] = ip + } + if len(indices) != len(allocated) || len(indices) != num { + t.Fatalf("Unexpected number of allocated addresses: (%d,%d).", len(indices), len(allocated)) + } + + seed := time.Now().Unix() + rand.Seed(seed) + + // Deallocate half of the allocated addresses following a random pattern + pattern := rand.Perm(num) + for i := 0; i < num/2; i++ { + idx := pattern[i] + ip := indices[idx] + err := a.ReleaseAddress(pid, ip.IP) + if err != nil { + t.Fatalf("Unexpected failure on deallocation of %s: %v.\nSeed: %d.", ip, err, seed) + } + delete(indices, idx) + delete(allocated, ip.String()) + } + + // Request a quarter of addresses + for i := 0; i < num/2; i++ { + ip, _, err := a.RequestAddress(pid, nil, nil) + if err != nil { + t.Fatal(err) + } + ips := ip.String() + if _, ok := allocated[ips]; ok { + t.Fatalf("\nAddress %s is already allocated.\nSeed: %d.", ips, seed) + } + allocated[ips] = true + } + if len(allocated) != num { + t.Fatalf("Unexpected number of allocated addresses: %d.\nSeed: %d.", len(allocated), seed) + } +} + +func TestRetrieveFromStore(t *testing.T) { + num := 200 + ds, err := randomLocalStore(true) + if err != nil { + t.Fatal(err) + } + a, err := NewAllocator(ds, nil) + if err != nil { + t.Fatal(err) + } + pid, _, _, err := a.RequestPool(localAddressSpace, "172.25.0.0/16", "", nil, false) + if err != nil { + t.Fatal(err) + } + for i := 0; i < num; i++ { + if _, _, err := a.RequestAddress(pid, nil, nil); err != nil { + t.Fatal(err) + } + } + + // Restore + a1, err := NewAllocator(ds, nil) + if err != nil { + t.Fatal(err) + } + a1.refresh(localAddressSpace) + db := a.DumpDatabase() + db1 := a1.DumpDatabase() + if db != db1 { + t.Fatalf("Unexpected db change.\nExpected:%s\nGot:%s", db, db1) + } + checkDBEquality(a, a1, t) + pid, _, _, err = a1.RequestPool(localAddressSpace, "172.25.0.0/16", "172.25.1.0/24", nil, false) + if err != nil { + t.Fatal(err) + } + for i := 0; i < num/2; i++ { + if _, _, err := a1.RequestAddress(pid, nil, nil); err != nil { + t.Fatal(err) + } + } + + // Restore + a2, err := NewAllocator(ds, nil) + if err != nil { + t.Fatal(err) + } + a2.refresh(localAddressSpace) + checkDBEquality(a1, a2, t) + pid, _, _, err = a2.RequestPool(localAddressSpace, "172.25.0.0/16", "172.25.2.0/24", nil, false) + if err != nil { + t.Fatal(err) + } + for i := 0; i < num/2; i++ { + if _, _, err := a2.RequestAddress(pid, nil, nil); err != nil { + t.Fatal(err) + } + } + + // Restore + a3, err := NewAllocator(ds, nil) + if err != nil { + t.Fatal(err) + } + a3.refresh(localAddressSpace) + checkDBEquality(a2, a3, t) + pid, _, _, err = a3.RequestPool(localAddressSpace, "172.26.0.0/16", "", nil, false) + if err != nil { + t.Fatal(err) + } + for i := 0; i < num/2; i++ { + if _, _, err := a3.RequestAddress(pid, nil, nil); err != nil { + t.Fatal(err) + } + } + + // Restore + a4, err := NewAllocator(ds, nil) + if err != nil { + t.Fatal(err) + } + a4.refresh(localAddressSpace) + checkDBEquality(a3, a4, t) +} + +func checkDBEquality(a1, a2 *Allocator, t *testing.T) { + for k, cnf1 := range a1.addrSpaces[localAddressSpace].subnets { + cnf2 := a2.addrSpaces[localAddressSpace].subnets[k] + if cnf1.String() != cnf2.String() { + t.Fatalf("%s\n%s", cnf1, cnf2) + } + if cnf1.Range == nil { + a2.retrieveBitmask(k, cnf1.Pool) + } + } + + for k, bm1 := range a1.addresses { + bm2 := a2.addresses[k] + if bm1.String() != bm2.String() { + t.Fatalf("%s\n%s", bm1, bm2) + } + } +} + +const ( + numInstances = 5 + first = 0 + last = numInstances - 1 +) + +var ( + allocator *Allocator + start = make(chan struct{}) + done = make(chan chan struct{}, numInstances-1) + pools = make([]*net.IPNet, numInstances) +) + +func runParallelTests(t *testing.T, instance int) { + var err error + + t.Parallel() + + pTest := flag.Lookup("test.parallel") + if pTest == nil { + t.Skip("Skipped because test.parallel flag not set;") + } + numParallel, err := strconv.Atoi(pTest.Value.String()) + if err != nil { + t.Fatal(err) + } + if numParallel < numInstances { + t.Skip("Skipped because t.parallel was less than ", numInstances) + } + + // The first instance creates the allocator, gives the start + // and finally checks the pools each instance was assigned + if instance == first { + allocator, err = getAllocator(true) + if err != nil { + t.Fatal(err) + } + close(start) + } + + if instance != first { + <-start + instDone := make(chan struct{}) + done <- instDone + defer close(instDone) + + if instance == last { + defer close(done) + } + } + + _, pools[instance], _, err = allocator.RequestPool(localAddressSpace, "", "", nil, false) + if err != nil { + t.Fatal(err) + } + + if instance == first { + for instDone := range done { + <-instDone + } + // Now check each instance got a different pool + for i := 0; i < numInstances; i++ { + for j := i + 1; j < numInstances; j++ { + if types.CompareIPNet(pools[i], pools[j]) { + t.Fatalf("Instance %d and %d were given the same predefined pool: %v", i, j, pools) + } + } + } + } +} + +func TestRequestReleaseAddressDuplicate(t *testing.T) { + a, err := getAllocator(false) + if err != nil { + t.Fatal(err) + } + type IP struct { + ip *net.IPNet + ref int + } + ips := []IP{} + allocatedIPs := []*net.IPNet{} + a.addrSpaces["rosso"] = &addrSpace{ + id: dsConfigKey + "/" + "rosso", + ds: a.addrSpaces[localAddressSpace].ds, + alloc: a.addrSpaces[localAddressSpace].alloc, + scope: a.addrSpaces[localAddressSpace].scope, + subnets: map[SubnetKey]*PoolData{}, + } + + opts := map[string]string{ + ipamapi.AllocSerialPrefix: "true", + } + var l sync.Mutex + + poolID, _, _, err := a.RequestPool("rosso", "198.168.0.0/23", "", nil, false) + if err != nil { + t.Fatal(err) + } + + group := new(errgroup.Group) + for err == nil { + var c *net.IPNet + if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil { + l.Lock() + ips = append(ips, IP{c, 1}) + l.Unlock() + allocatedIPs = append(allocatedIPs, c) + if len(allocatedIPs) > 500 { + i := rand.Intn(len(allocatedIPs) - 1) + ip := allocatedIPs[i] + group.Go(func() error { + if err = a.ReleaseAddress(poolID, ip.IP); err != nil { + return err + } + l.Lock() + ips = append(ips, IP{ip, -1}) + l.Unlock() + return nil + }) + + allocatedIPs = append(allocatedIPs[:i], allocatedIPs[i+1:]...) + } + } + } + + if err := group.Wait(); err != nil { + t.Fatal(err) + } + + refMap := make(map[string]int) + for _, ip := range ips { + refMap[ip.ip.String()] = refMap[ip.ip.String()] + ip.ref + if refMap[ip.ip.String()] < 0 { + t.Fatalf("IP %s was previously released", ip.ip.String()) + } + if refMap[ip.ip.String()] > 1 { + t.Fatalf("IP %s was previously allocated", ip.ip.String()) + } + } +} + +func TestParallelPredefinedRequest1(t *testing.T) { + runParallelTests(t, 0) +} + +func TestParallelPredefinedRequest2(t *testing.T) { + runParallelTests(t, 1) +} + +func TestParallelPredefinedRequest3(t *testing.T) { + runParallelTests(t, 2) +} + +func TestParallelPredefinedRequest4(t *testing.T) { + runParallelTests(t, 3) +} + +func TestParallelPredefinedRequest5(t *testing.T) { + runParallelTests(t, 4) +} diff --git a/libnetwork/ipam/parallel_test.go b/libnetwork/ipam/parallel_test.go new file mode 100644 index 0000000000000..52e1f83365938 --- /dev/null +++ b/libnetwork/ipam/parallel_test.go @@ -0,0 +1,318 @@ +package ipam + +import ( + "context" + "fmt" + "math/rand" + "net" + "sort" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/docker/docker/libnetwork/ipamapi" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +const ( + all = iota + even + odd +) + +type releaseMode uint + +type testContext struct { + a *Allocator + opts map[string]string + ipList []*net.IPNet + ipMap map[string]bool + pid string + maxIP int +} + +func newTestContext(t *testing.T, mask int, options map[string]string) *testContext { + a, err := getAllocator(false) + if err != nil { + t.Fatal(err) + } + a.addrSpaces["giallo"] = &addrSpace{ + id: dsConfigKey + "/" + "giallo", + ds: a.addrSpaces[localAddressSpace].ds, + alloc: a.addrSpaces[localAddressSpace].alloc, + scope: a.addrSpaces[localAddressSpace].scope, + subnets: map[SubnetKey]*PoolData{}, + } + + network := fmt.Sprintf("192.168.100.0/%d", mask) + // total ips 2^(32-mask) - 2 (network and broadcast) + totalIps := 1< 0 { + length++ + } + default: + t.Fatal("unsupported mode yet") + } + + ipIndex := make([]int, 0, length) + // calculate the index to release from the ipList + for i := startIndex; ; i += increment { + ipIndex = append(ipIndex, i) + if i+increment > stopIndex { + break + } + } + + var id int + parallelExec := semaphore.NewWeighted(parallel) + ch := make(chan *net.IPNet, len(ipIndex)) + group := new(errgroup.Group) + for index := range ipIndex { + index := index + group.Go(func() error { + parallelExec.Acquire(context.Background(), 1) + err := tctx.a.ReleaseAddress(tctx.pid, tctx.ipList[index].IP) + if err != nil { + return fmt.Errorf("routine %d got %v", id, err) + } + ch <- tctx.ipList[index] + parallelExec.Release(1) + return nil + }) + id++ + } + + if err := group.Wait(); err != nil { + t.Fatal(err) + } + + for i := 0; i < len(ipIndex); i++ { + ip := <-ch + + // check if it is really free + _, _, err := tctx.a.RequestAddress(tctx.pid, ip.IP, nil) + assert.Check(t, err, "ip %v not properly released", ip) + if err != nil { + t.Fatalf("ip %v not properly released, error:%v", ip, err) + } + err = tctx.a.ReleaseAddress(tctx.pid, ip.IP) + assert.NilError(t, err) + + if there, ok := tctx.ipMap[ip.String()]; !ok || !there { + t.Fatalf("ip %v got double deallocated", ip) + } + tctx.ipMap[ip.String()] = false + for j, v := range tctx.ipList { + if v == ip { + tctx.ipList = append(tctx.ipList[:j], tctx.ipList[j+1:]...) + break + } + } + } + + assert.Check(t, is.Len(tctx.ipList, tctx.maxIP-length)) +} diff --git a/libnetwork/ipam/store.go b/libnetwork/ipam/store.go new file mode 100644 index 0000000000000..e00401616b778 --- /dev/null +++ b/libnetwork/ipam/store.go @@ -0,0 +1,125 @@ +package ipam + +import ( + "encoding/json" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/types" + "github.com/sirupsen/logrus" +) + +// Key provides the Key to be used in KV Store +func (aSpace *addrSpace) Key() []string { + aSpace.Lock() + defer aSpace.Unlock() + return []string{aSpace.id} +} + +// KeyPrefix returns the immediate parent key that can be used for tree walk +func (aSpace *addrSpace) KeyPrefix() []string { + aSpace.Lock() + defer aSpace.Unlock() + return []string{dsConfigKey} +} + +// Value marshals the data to be stored in the KV store +func (aSpace *addrSpace) Value() []byte { + b, err := json.Marshal(aSpace) + if err != nil { + logrus.Warnf("Failed to marshal ipam configured pools: %v", err) + return nil + } + return b +} + +// SetValue unmarshalls the data from the KV store. +func (aSpace *addrSpace) SetValue(value []byte) error { + rc := &addrSpace{subnets: make(map[SubnetKey]*PoolData)} + if err := json.Unmarshal(value, rc); err != nil { + return err + } + aSpace.subnets = rc.subnets + return nil +} + +// Index returns the latest DB Index as seen by this object +func (aSpace *addrSpace) Index() uint64 { + aSpace.Lock() + defer aSpace.Unlock() + return aSpace.dbIndex +} + +// SetIndex method allows the datastore to store the latest DB Index into this object +func (aSpace *addrSpace) SetIndex(index uint64) { + aSpace.Lock() + aSpace.dbIndex = index + aSpace.dbExists = true + aSpace.Unlock() +} + +// Exists method is true if this object has been stored in the DB. +func (aSpace *addrSpace) Exists() bool { + aSpace.Lock() + defer aSpace.Unlock() + return aSpace.dbExists +} + +// Skip provides a way for a KV Object to avoid persisting it in the KV Store +func (aSpace *addrSpace) Skip() bool { + return false +} + +func (a *Allocator) getStore(as string) datastore.DataStore { + a.Lock() + defer a.Unlock() + + if aSpace, ok := a.addrSpaces[as]; ok { + return aSpace.ds + } + + return nil +} + +func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) { + store := a.getStore(as) + + // IPAM may not have a valid store. In such cases it is just in-memory state. + if store == nil { + return nil, nil + } + + pc := &addrSpace{id: dsConfigKey + "/" + as, ds: store, alloc: a} + if err := store.GetObject(datastore.Key(pc.Key()...), pc); err != nil { + if err == datastore.ErrKeyNotFound { + return nil, nil + } + + return nil, types.InternalErrorf("could not get pools config from store: %v", err) + } + + return pc, nil +} + +func (a *Allocator) writeToStore(aSpace *addrSpace) error { + store := aSpace.store() + + // IPAM may not have a valid store. In such cases it is just in-memory state. + if store == nil { + return nil + } + + err := store.PutObjectAtomic(aSpace) + if err == datastore.ErrKeyModified { + return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err) + } + + return err +} + +// DataScope method returns the storage scope of the datastore +func (aSpace *addrSpace) DataScope() string { + aSpace.Lock() + defer aSpace.Unlock() + + return aSpace.scope +} diff --git a/vendor/github.com/docker/libnetwork/ipam/structures.go b/libnetwork/ipam/structures.go similarity index 98% rename from vendor/github.com/docker/libnetwork/ipam/structures.go rename to libnetwork/ipam/structures.go index 2e6d75eaa4db1..de4130f83d2bb 100644 --- a/vendor/github.com/docker/libnetwork/ipam/structures.go +++ b/libnetwork/ipam/structures.go @@ -7,9 +7,9 @@ import ( "strings" "sync" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/types" ) // SubnetKey is the pointer to the configured pools in each address space @@ -159,7 +159,7 @@ func (aSpace *addrSpace) MarshalJSON() ([]byte, error) { defer aSpace.Unlock() m := map[string]interface{}{ - "Scope": string(aSpace.scope), + "Scope": aSpace.scope, } if aSpace.subnets != nil { diff --git a/libnetwork/ipam/utils.go b/libnetwork/ipam/utils.go new file mode 100644 index 0000000000000..b0bdaedd9ac27 --- /dev/null +++ b/libnetwork/ipam/utils.go @@ -0,0 +1,81 @@ +package ipam + +import ( + "fmt" + "net" + + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/types" +) + +type ipVersion int + +const ( + v4 = 4 + v6 = 6 +) + +func getAddressRange(pool string, masterNw *net.IPNet) (*AddressRange, error) { + ip, nw, err := net.ParseCIDR(pool) + if err != nil { + return nil, ipamapi.ErrInvalidSubPool + } + lIP, e := types.GetHostPartIP(nw.IP, masterNw.Mask) + if e != nil { + return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e) + } + bIP, e := types.GetBroadcastIP(nw.IP, nw.Mask) + if e != nil { + return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e) + } + hIP, e := types.GetHostPartIP(bIP, masterNw.Mask) + if e != nil { + return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e) + } + nw.IP = ip + return &AddressRange{nw, ipToUint64(types.GetMinimalIP(lIP)), ipToUint64(types.GetMinimalIP(hIP))}, nil +} + +// It generates the ip address in the passed subnet specified by +// the passed host address ordinal +func generateAddress(ordinal uint64, network *net.IPNet) net.IP { + var address [16]byte + + // Get network portion of IP + if getAddressVersion(network.IP) == v4 { + copy(address[:], network.IP.To4()) + } else { + copy(address[:], network.IP) + } + + end := len(network.Mask) + addIntToIP(address[:end], ordinal) + + return net.IP(address[:end]) +} + +func getAddressVersion(ip net.IP) ipVersion { + if ip.To4() == nil { + return v6 + } + return v4 +} + +// Adds the ordinal IP to the current array +// 192.168.0.0 + 53 => 192.168.0.53 +func addIntToIP(array []byte, ordinal uint64) { + for i := len(array) - 1; i >= 0; i-- { + array[i] |= (byte)(ordinal & 0xff) + ordinal >>= 8 + } +} + +// Convert an ordinal to the respective IP address +func ipToUint64(ip []byte) (value uint64) { + cip := types.GetMinimalIP(ip) + for i := 0; i < len(cip); i++ { + j := len(cip) - 1 - i + value += uint64(cip[i]) << uint(j*8) + } + return value +} diff --git a/vendor/github.com/docker/libnetwork/ipamapi/contract.go b/libnetwork/ipamapi/contract.go similarity index 89% rename from vendor/github.com/docker/libnetwork/ipamapi/contract.go rename to libnetwork/ipamapi/contract.go index 7f967863d8168..5e75bdfbd44ca 100644 --- a/vendor/github.com/docker/libnetwork/ipamapi/contract.go +++ b/libnetwork/ipamapi/contract.go @@ -4,15 +4,12 @@ package ipamapi import ( "net" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/types" ) -/******************** - * IPAM plugin types - ********************/ - +// IPAM plugin types const ( // DefaultIPAM is the name of the built-in default ipam driver DefaultIPAM = "default" @@ -34,10 +31,6 @@ type Callback interface { RegisterIpamDriverWithCapabilities(name string, driver Ipam, capability *Capability) error } -/************** - * IPAM Errors - **************/ - // Well-known errors returned by IPAM var ( ErrIpamInternalError = types.InternalErrorf("IPAM Internal Error") @@ -56,10 +49,6 @@ var ( ErrBadPool = types.BadRequestErrorf("Address space does not contain specified address pool") ) -/******************************* - * IPAM Service Interface - *******************************/ - // Ipam represents the interface the IPAM service plugins must implement // in order to allow injection/modification of IPAM database. type Ipam interface { @@ -76,12 +65,12 @@ type Ipam interface { RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) // ReleasePool releases the address pool identified by the passed id ReleasePool(poolID string) error - // Request address from the specified pool ID. Input options or required IP can be passed. + // RequestAddress request an address from the specified pool ID. Input options or required IP can be passed. RequestAddress(string, net.IP, map[string]string) (*net.IPNet, map[string]string, error) - // Release the address from the specified pool ID + // ReleaseAddress releases the address from the specified pool ID. ReleaseAddress(string, net.IP) error - //IsBuiltIn returns true if it is a built-in driver. + // IsBuiltIn returns true if it is a built-in driver. IsBuiltIn() bool } diff --git a/vendor/github.com/docker/libnetwork/ipamapi/labels.go b/libnetwork/ipamapi/labels.go similarity index 100% rename from vendor/github.com/docker/libnetwork/ipamapi/labels.go rename to libnetwork/ipamapi/labels.go diff --git a/vendor/github.com/docker/libnetwork/ipams/builtin/builtin_unix.go b/libnetwork/ipams/builtin/builtin_unix.go similarity index 78% rename from vendor/github.com/docker/libnetwork/ipams/builtin/builtin_unix.go rename to libnetwork/ipams/builtin/builtin_unix.go index b07fb4b632897..e5d142121a9a9 100644 --- a/vendor/github.com/docker/libnetwork/ipams/builtin/builtin_unix.go +++ b/libnetwork/ipams/builtin/builtin_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd || darwin // +build linux freebsd darwin package builtin @@ -5,10 +6,10 @@ package builtin import ( "errors" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/ipam" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/ipamutils" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/ipam" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/ipamutils" ) var ( @@ -35,7 +36,10 @@ func Init(ic ipamapi.Callback, l, g interface{}) error { } } - ipamutils.ConfigLocalScopeDefaultNetworks(GetDefaultIPAddressPool()) + err := ipamutils.ConfigLocalScopeDefaultNetworks(GetDefaultIPAddressPool()) + if err != nil { + return err + } a, err := ipam.NewAllocator(localDs, globalDs) if err != nil { diff --git a/vendor/github.com/docker/libnetwork/ipams/builtin/builtin_windows.go b/libnetwork/ipams/builtin/builtin_windows.go similarity index 84% rename from vendor/github.com/docker/libnetwork/ipams/builtin/builtin_windows.go rename to libnetwork/ipams/builtin/builtin_windows.go index 7975981eec9d6..730e2d1f59e18 100644 --- a/vendor/github.com/docker/libnetwork/ipams/builtin/builtin_windows.go +++ b/libnetwork/ipams/builtin/builtin_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package builtin @@ -5,12 +6,12 @@ package builtin import ( "errors" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/ipam" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/ipamutils" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/ipam" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/ipamutils" - windowsipam "github.com/docker/libnetwork/ipams/windowsipam" + windowsipam "github.com/docker/docker/libnetwork/ipams/windowsipam" ) var ( diff --git a/libnetwork/ipams/null/null.go b/libnetwork/ipams/null/null.go new file mode 100644 index 0000000000000..3d8a028944985 --- /dev/null +++ b/libnetwork/ipams/null/null.go @@ -0,0 +1,75 @@ +// Package null implements the null ipam driver. Null ipam driver satisfies ipamapi contract, +// but does not effectively reserve/allocate any address pool or address +package null + +import ( + "fmt" + "net" + + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/types" +) + +var ( + defaultAS = "null" + defaultPool, _ = types.ParseCIDR("0.0.0.0/0") + defaultPoolID = fmt.Sprintf("%s/%s", defaultAS, defaultPool.String()) +) + +type allocator struct{} + +func (a *allocator) GetDefaultAddressSpaces() (string, string, error) { + return defaultAS, defaultAS, nil +} + +func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { + if addressSpace != defaultAS { + return "", nil, nil, types.BadRequestErrorf("unknown address space: %s", addressSpace) + } + if pool != "" { + return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle specific address pool requests") + } + if subPool != "" { + return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle specific address subpool requests") + } + if v6 { + return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle IPv6 address pool pool requests") + } + return defaultPoolID, defaultPool, nil, nil +} + +func (a *allocator) ReleasePool(poolID string) error { + return nil +} + +func (a *allocator) RequestAddress(poolID string, ip net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) { + if poolID != defaultPoolID { + return nil, nil, types.BadRequestErrorf("unknown pool id: %s", poolID) + } + return nil, nil, nil +} + +func (a *allocator) ReleaseAddress(poolID string, ip net.IP) error { + if poolID != defaultPoolID { + return types.BadRequestErrorf("unknown pool id: %s", poolID) + } + return nil +} + +func (a *allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +func (a *allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +func (a *allocator) IsBuiltIn() bool { + return true +} + +// Init registers a remote ipam when its plugin is activated +func Init(ic ipamapi.Callback, l, g interface{}) error { + return ic.RegisterIpamDriver(ipamapi.NullIPAM, &allocator{}) +} diff --git a/libnetwork/ipams/null/null_test.go b/libnetwork/ipams/null/null_test.go new file mode 100644 index 0000000000000..d66e00853e2ea --- /dev/null +++ b/libnetwork/ipams/null/null_test.go @@ -0,0 +1,60 @@ +package null + +import ( + "testing" + + "github.com/docker/docker/libnetwork/types" +) + +func TestPoolRequest(t *testing.T) { + a := allocator{} + + pid, pool, _, err := a.RequestPool(defaultAS, "", "", nil, false) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(defaultPool, pool) { + t.Fatalf("Unexpected pool returned. Expected %v. Got: %v", defaultPool, pool) + } + if pid != defaultPoolID { + t.Fatalf("Unexpected pool id returned. Expected: %s. Got: %s", defaultPoolID, pid) + } + + _, _, _, err = a.RequestPool("default", "", "", nil, false) + if err == nil { + t.Fatal("Unexpected success") + } + + _, _, _, err = a.RequestPool(defaultAS, "192.168.0.0/16", "", nil, false) + if err == nil { + t.Fatal("Unexpected success") + } + + _, _, _, err = a.RequestPool(defaultAS, "", "192.168.0.0/24", nil, false) + if err == nil { + t.Fatal("Unexpected success") + } + + _, _, _, err = a.RequestPool(defaultAS, "", "", nil, true) + if err == nil { + t.Fatal("Unexpected success") + } +} + +func TestOtherRequests(t *testing.T) { + a := allocator{} + + ip, _, err := a.RequestAddress(defaultPoolID, nil, nil) + if err != nil { + t.Fatal(err) + } + if ip != nil { + t.Fatalf("Unexpected address returned: %v", ip) + } + + _, _, err = a.RequestAddress("anypid", nil, nil) + if err == nil { + t.Fatal("Unexpected success") + } + +} diff --git a/libnetwork/ipams/remote/api/api.go b/libnetwork/ipams/remote/api/api.go new file mode 100644 index 0000000000000..b78f6c5a288f0 --- /dev/null +++ b/libnetwork/ipams/remote/api/api.go @@ -0,0 +1,94 @@ +// Package api defines the data structure to be used in the request/response +// messages between libnetwork and the remote ipam plugin +package api + +import "github.com/docker/docker/libnetwork/ipamapi" + +// Response is the basic response structure used in all responses +type Response struct { + Error string +} + +// IsSuccess returns whether the plugin response is successful +func (r *Response) IsSuccess() bool { + return r.Error == "" +} + +// GetError returns the error from the response, if any. +func (r *Response) GetError() string { + return r.Error +} + +// GetCapabilityResponse is the response of GetCapability request +type GetCapabilityResponse struct { + Response + RequiresMACAddress bool + RequiresRequestReplay bool +} + +// ToCapability converts the capability response into the internal ipam driver capability structure +func (capRes GetCapabilityResponse) ToCapability() *ipamapi.Capability { + return &ipamapi.Capability{ + RequiresMACAddress: capRes.RequiresMACAddress, + RequiresRequestReplay: capRes.RequiresRequestReplay, + } +} + +// GetAddressSpacesResponse is the response to the ``get default address spaces`` request message +type GetAddressSpacesResponse struct { + Response + LocalDefaultAddressSpace string + GlobalDefaultAddressSpace string +} + +// RequestPoolRequest represents the expected data in a ``request address pool`` request message +type RequestPoolRequest struct { + AddressSpace string + Pool string + SubPool string + Options map[string]string + V6 bool +} + +// RequestPoolResponse represents the response message to a ``request address pool`` request +type RequestPoolResponse struct { + Response + PoolID string + Pool string // CIDR format + Data map[string]string +} + +// ReleasePoolRequest represents the expected data in a ``release address pool`` request message +type ReleasePoolRequest struct { + PoolID string +} + +// ReleasePoolResponse represents the response message to a ``release address pool`` request +type ReleasePoolResponse struct { + Response +} + +// RequestAddressRequest represents the expected data in a ``request address`` request message +type RequestAddressRequest struct { + PoolID string + Address string + Options map[string]string +} + +// RequestAddressResponse represents the expected data in the response message to a ``request address`` request +type RequestAddressResponse struct { + Response + Address string // in CIDR format + Data map[string]string +} + +// ReleaseAddressRequest represents the expected data in a ``release address`` request message +type ReleaseAddressRequest struct { + PoolID string + Address string +} + +// ReleaseAddressResponse represents the response message to a ``release address`` request +type ReleaseAddressResponse struct { + Response +} diff --git a/libnetwork/ipams/remote/remote.go b/libnetwork/ipams/remote/remote.go new file mode 100644 index 0000000000000..4b8c7c5014240 --- /dev/null +++ b/libnetwork/ipams/remote/remote.go @@ -0,0 +1,183 @@ +package remote + +import ( + "fmt" + "net" + + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/ipams/remote/api" + "github.com/docker/docker/libnetwork/types" + "github.com/docker/docker/pkg/plugingetter" + "github.com/docker/docker/pkg/plugins" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type allocator struct { + endpoint *plugins.Client + name string +} + +// PluginResponse is the interface for the plugin request responses +type PluginResponse interface { + IsSuccess() bool + GetError() string +} + +func newAllocator(name string, client *plugins.Client) ipamapi.Ipam { + a := &allocator{name: name, endpoint: client} + return a +} + +// Init registers a remote ipam when its plugin is activated +func Init(cb ipamapi.Callback, l, g interface{}) error { + + newPluginHandler := func(name string, client *plugins.Client) { + a := newAllocator(name, client) + if cps, err := a.(*allocator).getCapabilities(); err == nil { + if err := cb.RegisterIpamDriverWithCapabilities(name, a, cps); err != nil { + logrus.Errorf("error registering remote ipam driver %s due to %v", name, err) + } + } else { + logrus.Infof("remote ipam driver %s does not support capabilities", name) + logrus.Debug(err) + if err := cb.RegisterIpamDriver(name, a); err != nil { + logrus.Errorf("error registering remote ipam driver %s due to %v", name, err) + } + } + } + + // Unit test code is unaware of a true PluginStore. So we fall back to v1 plugins. + handleFunc := plugins.Handle + if pg := cb.GetPluginGetter(); pg != nil { + handleFunc = pg.Handle + activePlugins := pg.GetAllManagedPluginsByCap(ipamapi.PluginEndpointType) + for _, ap := range activePlugins { + client, err := getPluginClient(ap) + if err != nil { + return err + } + newPluginHandler(ap.Name(), client) + } + } + handleFunc(ipamapi.PluginEndpointType, newPluginHandler) + return nil +} + +func getPluginClient(p plugingetter.CompatPlugin) (*plugins.Client, error) { + if v1, ok := p.(plugingetter.PluginWithV1Client); ok { + return v1.Client(), nil + } + + pa, ok := p.(plugingetter.PluginAddr) + if !ok { + return nil, errors.Errorf("unknown plugin type %T", p) + } + + if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 { + return nil, errors.Errorf("unsupported plugin protocol %s", pa.Protocol()) + } + + addr := pa.Addr() + client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout()) + if err != nil { + return nil, errors.Wrap(err, "error creating plugin client") + } + return client, nil +} + +func (a *allocator) call(methodName string, arg interface{}, retVal PluginResponse) error { + method := ipamapi.PluginEndpointType + "." + methodName + err := a.endpoint.Call(method, arg, retVal) + if err != nil { + return err + } + if !retVal.IsSuccess() { + return fmt.Errorf("remote: %s", retVal.GetError()) + } + return nil +} + +func (a *allocator) getCapabilities() (*ipamapi.Capability, error) { + var res api.GetCapabilityResponse + if err := a.call("GetCapabilities", nil, &res); err != nil { + return nil, err + } + return res.ToCapability(), nil +} + +// GetDefaultAddressSpaces returns the local and global default address spaces +func (a *allocator) GetDefaultAddressSpaces() (string, string, error) { + res := &api.GetAddressSpacesResponse{} + if err := a.call("GetDefaultAddressSpaces", nil, res); err != nil { + return "", "", err + } + return res.LocalDefaultAddressSpace, res.GlobalDefaultAddressSpace, nil +} + +// RequestPool requests an address pool in the specified address space +func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { + req := &api.RequestPoolRequest{AddressSpace: addressSpace, Pool: pool, SubPool: subPool, Options: options, V6: v6} + res := &api.RequestPoolResponse{} + if err := a.call("RequestPool", req, res); err != nil { + return "", nil, nil, err + } + retPool, err := types.ParseCIDR(res.Pool) + return res.PoolID, retPool, res.Data, err +} + +// ReleasePool removes an address pool from the specified address space +func (a *allocator) ReleasePool(poolID string) error { + req := &api.ReleasePoolRequest{PoolID: poolID} + res := &api.ReleasePoolResponse{} + return a.call("ReleasePool", req, res) +} + +// RequestAddress requests an address from the address pool +func (a *allocator) RequestAddress(poolID string, address net.IP, options map[string]string) (*net.IPNet, map[string]string, error) { + var ( + prefAddress string + retAddress *net.IPNet + err error + ) + if address != nil { + prefAddress = address.String() + } + req := &api.RequestAddressRequest{PoolID: poolID, Address: prefAddress, Options: options} + res := &api.RequestAddressResponse{} + if err := a.call("RequestAddress", req, res); err != nil { + return nil, nil, err + } + if res.Address != "" { + retAddress, err = types.ParseCIDR(res.Address) + } else { + return nil, nil, ipamapi.ErrNoIPReturned + } + return retAddress, res.Data, err +} + +// ReleaseAddress releases the address from the specified address pool +func (a *allocator) ReleaseAddress(poolID string, address net.IP) error { + var relAddress string + if address != nil { + relAddress = address.String() + } + req := &api.ReleaseAddressRequest{PoolID: poolID, Address: relAddress} + res := &api.ReleaseAddressResponse{} + return a.call("ReleaseAddress", req, res) +} + +// DiscoverNew is a notification for a new discovery event, such as a new global datastore +func (a *allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster +func (a *allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} + +func (a *allocator) IsBuiltIn() bool { + return false +} diff --git a/libnetwork/ipams/remote/remote_test.go b/libnetwork/ipams/remote/remote_test.go new file mode 100644 index 0000000000000..79ee22c0d113d --- /dev/null +++ b/libnetwork/ipams/remote/remote_test.go @@ -0,0 +1,320 @@ +package remote + +import ( + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/pkg/plugins" +) + +func decodeToMap(r *http.Request) (res map[string]interface{}, err error) { + err = json.NewDecoder(r.Body).Decode(&res) + return +} + +func handle(t *testing.T, mux *http.ServeMux, method string, h func(map[string]interface{}) interface{}) { + mux.HandleFunc(fmt.Sprintf("/%s.%s", ipamapi.PluginEndpointType, method), func(w http.ResponseWriter, r *http.Request) { + ask, err := decodeToMap(r) + if err != nil && err != io.EOF { + t.Fatal(err) + } + answer := h(ask) + err = json.NewEncoder(w).Encode(&answer) + if err != nil { + t.Fatal(err) + } + }) +} + +func setupPlugin(t *testing.T, name string, mux *http.ServeMux) func() { + specPath := "/etc/docker/plugins" + if runtime.GOOS == "windows" { + specPath = filepath.Join(os.Getenv("programdata"), "docker", "plugins") + } + + if err := os.MkdirAll(specPath, 0755); err != nil { + t.Fatal(err) + } + + defer func() { + if t.Failed() { + os.RemoveAll(specPath) + } + }() + + server := httptest.NewServer(mux) + if server == nil { + t.Fatal("Failed to start an HTTP Server") + } + + if err := os.WriteFile(filepath.Join(specPath, name+".spec"), []byte(server.URL), 0644); err != nil { + t.Fatal(err) + } + + mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, `{"Implements": ["%s"]}`, ipamapi.PluginEndpointType) + }) + + return func() { + if err := os.RemoveAll(specPath); err != nil { + t.Fatal(err) + } + server.Close() + } +} + +func TestGetCapabilities(t *testing.T) { + var plugin = "test-ipam-driver-capabilities" + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + handle(t, mux, "GetCapabilities", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{ + "RequiresMACAddress": true, + } + }) + + p, err := plugins.Get(plugin, ipamapi.PluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + d := newAllocator(plugin, client) + + caps, err := d.(*allocator).getCapabilities() + if err != nil { + t.Fatal(err) + } + + if !caps.RequiresMACAddress || caps.RequiresRequestReplay { + t.Fatalf("Unexpected capability: %v", caps) + } +} + +func TestGetCapabilitiesFromLegacyDriver(t *testing.T) { + var plugin = "test-ipam-legacy-driver" + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + p, err := plugins.Get(plugin, ipamapi.PluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + + d := newAllocator(plugin, client) + + if _, err := d.(*allocator).getCapabilities(); err == nil { + t.Fatalf("Expected error, but got Success %v", err) + } +} + +func TestGetDefaultAddressSpaces(t *testing.T) { + var plugin = "test-ipam-driver-addr-spaces" + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + handle(t, mux, "GetDefaultAddressSpaces", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{ + "LocalDefaultAddressSpace": "white", + "GlobalDefaultAddressSpace": "blue", + } + }) + + p, err := plugins.Get(plugin, ipamapi.PluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + d := newAllocator(plugin, client) + + l, g, err := d.(*allocator).GetDefaultAddressSpaces() + if err != nil { + t.Fatal(err) + } + + if l != "white" || g != "blue" { + t.Fatalf("Unexpected default local and global address spaces: %s, %s", l, g) + } +} + +func TestRemoteDriver(t *testing.T) { + var plugin = "test-ipam-driver" + + mux := http.NewServeMux() + defer setupPlugin(t, plugin, mux)() + + handle(t, mux, "GetDefaultAddressSpaces", func(msg map[string]interface{}) interface{} { + return map[string]interface{}{ + "LocalDefaultAddressSpace": "white", + "GlobalDefaultAddressSpace": "blue", + } + }) + + handle(t, mux, "RequestPool", func(msg map[string]interface{}) interface{} { + as := "white" + if v, ok := msg["AddressSpace"]; ok && v.(string) != "" { + as = v.(string) + } + + pl := "172.18.0.0/16" + sp := "" + if v, ok := msg["Pool"]; ok && v.(string) != "" { + pl = v.(string) + } + if v, ok := msg["SubPool"]; ok && v.(string) != "" { + sp = v.(string) + } + pid := fmt.Sprintf("%s/%s", as, pl) + if sp != "" { + pid = fmt.Sprintf("%s/%s", pid, sp) + } + return map[string]interface{}{ + "PoolID": pid, + "Pool": pl, + "Data": map[string]string{"DNS": "8.8.8.8"}, + } + }) + + handle(t, mux, "ReleasePool", func(msg map[string]interface{}) interface{} { + if _, ok := msg["PoolID"]; !ok { + t.Fatal("Missing PoolID in Release request") + } + return map[string]interface{}{} + }) + + handle(t, mux, "RequestAddress", func(msg map[string]interface{}) interface{} { + if _, ok := msg["PoolID"]; !ok { + t.Fatal("Missing PoolID in address request") + } + prefAddr := "" + if v, ok := msg["Address"]; ok { + prefAddr = v.(string) + } + ip := prefAddr + if ip == "" { + ip = "172.20.0.34" + } + ip = fmt.Sprintf("%s/16", ip) + return map[string]interface{}{ + "Address": ip, + } + }) + + handle(t, mux, "ReleaseAddress", func(msg map[string]interface{}) interface{} { + if _, ok := msg["PoolID"]; !ok { + t.Fatal("Missing PoolID in address request") + } + if _, ok := msg["Address"]; !ok { + t.Fatal("Missing Address in release address request") + } + return map[string]interface{}{} + }) + + p, err := plugins.Get(plugin, ipamapi.PluginEndpointType) + if err != nil { + t.Fatal(err) + } + + client, err := getPluginClient(p) + if err != nil { + t.Fatal(err) + } + d := newAllocator(plugin, client) + + l, g, err := d.(*allocator).GetDefaultAddressSpaces() + if err != nil { + t.Fatal(err) + } + if l != "white" || g != "blue" { + t.Fatalf("Unexpected default local/global address spaces: %s, %s", l, g) + } + + // Request any pool + poolID, pool, _, err := d.RequestPool("white", "", "", nil, false) + if err != nil { + t.Fatal(err) + } + if poolID != "white/172.18.0.0/16" { + t.Fatalf("Unexpected pool id: %s", poolID) + } + if pool == nil || pool.String() != "172.18.0.0/16" { + t.Fatalf("Unexpected pool: %s", pool) + } + + // Request specific pool + poolID2, pool2, ops, err := d.RequestPool("white", "172.20.0.0/16", "", nil, false) + if err != nil { + t.Fatal(err) + } + if poolID2 != "white/172.20.0.0/16" { + t.Fatalf("Unexpected pool id: %s", poolID2) + } + if pool2 == nil || pool2.String() != "172.20.0.0/16" { + t.Fatalf("Unexpected pool: %s", pool2) + } + if dns, ok := ops["DNS"]; !ok || dns != "8.8.8.8" { + t.Fatal("Missing options") + } + + // Request specific pool and subpool + poolID3, pool3, _, err := d.RequestPool("white", "172.20.0.0/16", "172.20.3.0/24" /*nil*/, map[string]string{"culo": "yes"}, false) + if err != nil { + t.Fatal(err) + } + if poolID3 != "white/172.20.0.0/16/172.20.3.0/24" { + t.Fatalf("Unexpected pool id: %s", poolID3) + } + if pool3 == nil || pool3.String() != "172.20.0.0/16" { + t.Fatalf("Unexpected pool: %s", pool3) + } + + // Request any address + addr, _, err := d.RequestAddress(poolID2, nil, nil) + if err != nil { + t.Fatal(err) + } + if addr == nil || addr.String() != "172.20.0.34/16" { + t.Fatalf("Unexpected address: %s", addr) + } + + // Request specific address + addr2, _, err := d.RequestAddress(poolID2, net.ParseIP("172.20.1.45"), nil) + if err != nil { + t.Fatal(err) + } + if addr2 == nil || addr2.String() != "172.20.1.45/16" { + t.Fatalf("Unexpected address: %s", addr2) + } + + // Release address + err = d.ReleaseAddress(poolID, net.ParseIP("172.18.1.45")) + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/docker/libnetwork/ipams/windowsipam/windowsipam.go b/libnetwork/ipams/windowsipam/windowsipam.go similarity index 95% rename from vendor/github.com/docker/libnetwork/ipams/windowsipam/windowsipam.go rename to libnetwork/ipams/windowsipam/windowsipam.go index 5c7b1f5411da0..cdca9450269e9 100644 --- a/vendor/github.com/docker/libnetwork/ipams/windowsipam/windowsipam.go +++ b/libnetwork/ipams/windowsipam/windowsipam.go @@ -3,9 +3,9 @@ package windowsipam import ( "net" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) diff --git a/libnetwork/ipams/windowsipam/windowsipam_test.go b/libnetwork/ipams/windowsipam/windowsipam_test.go new file mode 100644 index 0000000000000..f09cdbd9436b3 --- /dev/null +++ b/libnetwork/ipams/windowsipam/windowsipam_test.go @@ -0,0 +1,83 @@ +package windowsipam + +import ( + "net" + "testing" + + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/types" +) + +func TestWindowsIPAM(t *testing.T) { + a := &allocator{} + requestPool, _ := types.ParseCIDR("192.168.0.0/16") + requestAddress := net.ParseIP("192.168.1.1") + + pid, pool, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(defaultPool, pool) || + pid != pool.String() { + t.Fatalf("Unexpected data returned. Expected %v : %s. Got: %v : %s", defaultPool, pid, pool, pool.String()) + } + + pid, pool, _, err = a.RequestPool(localAddressSpace, requestPool.String(), "", nil, false) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(requestPool, pool) || + pid != requestPool.String() { + t.Fatalf("Unexpected data returned. Expected %v : %s. Got: %v : %s", requestPool, requestPool.String(), pool, pool.String()) + } + + _, _, _, err = a.RequestPool(localAddressSpace, requestPool.String(), requestPool.String(), nil, false) + if err == nil { + t.Fatal("Unexpected success for subpool request") + } + + _, _, _, err = a.RequestPool(localAddressSpace, requestPool.String(), "", nil, true) + if err == nil { + t.Fatal("Unexpected success for v6 request") + } + + err = a.ReleasePool(requestPool.String()) + if err != nil { + t.Fatal(err) + } + + ip, _, err := a.RequestAddress(requestPool.String(), nil, map[string]string{}) + if err != nil { + t.Fatal(err) + } + + if ip != nil { + t.Fatalf("Unexpected data returned. Expected %v . Got: %v ", requestPool, ip) + } + + ip, _, err = a.RequestAddress(requestPool.String(), requestAddress, map[string]string{}) + if err != nil { + t.Fatal(err) + } + + if !ip.IP.Equal(requestAddress) { + t.Fatalf("Unexpected data returned. Expected %v . Got: %v ", requestAddress, ip.IP) + } + + requestOptions := map[string]string{} + requestOptions[ipamapi.RequestAddressType] = netlabel.Gateway + ip, _, err = a.RequestAddress(requestPool.String(), requestAddress, requestOptions) + if err != nil { + t.Fatal(err) + } + + if !ip.IP.Equal(requestAddress) { + t.Fatalf("Unexpected data returned. Expected %v . Got: %v ", requestAddress, ip.IP) + } + + err = a.ReleaseAddress(requestPool.String(), requestAddress) + if err != nil { + t.Fatal(err) + } +} diff --git a/vendor/github.com/docker/libnetwork/ipamutils/utils.go b/libnetwork/ipamutils/utils.go similarity index 100% rename from vendor/github.com/docker/libnetwork/ipamutils/utils.go rename to libnetwork/ipamutils/utils.go diff --git a/libnetwork/ipamutils/utils_test.go b/libnetwork/ipamutils/utils_test.go new file mode 100644 index 0000000000000..658e5f2cdb127 --- /dev/null +++ b/libnetwork/ipamutils/utils_test.go @@ -0,0 +1,117 @@ +package ipamutils + +import ( + "net" + "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func initBroadPredefinedNetworks() []*net.IPNet { + pl := make([]*net.IPNet, 0, 31) + mask := []byte{255, 255, 0, 0} + for i := 17; i < 32; i++ { + pl = append(pl, &net.IPNet{IP: []byte{172, byte(i), 0, 0}, Mask: mask}) + } + mask20 := []byte{255, 255, 240, 0} + for i := 0; i < 16; i++ { + pl = append(pl, &net.IPNet{IP: []byte{192, 168, byte(i << 4), 0}, Mask: mask20}) + } + return pl +} + +func initGranularPredefinedNetworks() []*net.IPNet { + pl := make([]*net.IPNet, 0, 256*256) + mask := []byte{255, 255, 255, 0} + for i := 0; i < 256; i++ { + for j := 0; j < 256; j++ { + pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), byte(j), 0}, Mask: mask}) + } + } + return pl +} + +func initGlobalScopeNetworks() []*net.IPNet { + pl := make([]*net.IPNet, 0, 256*256) + mask := []byte{255, 255, 255, 0} + for i := 0; i < 256; i++ { + for j := 0; j < 256; j++ { + pl = append(pl, &net.IPNet{IP: []byte{30, byte(i), byte(j), 0}, Mask: mask}) + } + } + return pl +} + +func TestDefaultNetwork(t *testing.T) { + for _, nw := range PredefinedGlobalScopeDefaultNetworks { + if ones, bits := nw.Mask.Size(); bits != 32 || ones != 24 { + t.Fatalf("Unexpected size for network in granular list: %v", nw) + } + } + + for _, nw := range PredefinedLocalScopeDefaultNetworks { + if ones, bits := nw.Mask.Size(); bits != 32 || (ones != 20 && ones != 16) { + t.Fatalf("Unexpected size for network in broad list: %v", nw) + } + } + + originalBroadNets := initBroadPredefinedNetworks() + m := make(map[string]bool) + for _, v := range originalBroadNets { + m[v.String()] = true + } + for _, nw := range PredefinedLocalScopeDefaultNetworks { + _, ok := m[nw.String()] + assert.Check(t, ok) + delete(m, nw.String()) + } + + assert.Check(t, is.Len(m, 0)) + + originalGranularNets := initGranularPredefinedNetworks() + + m = make(map[string]bool) + for _, v := range originalGranularNets { + m[v.String()] = true + } + for _, nw := range PredefinedGlobalScopeDefaultNetworks { + _, ok := m[nw.String()] + assert.Check(t, ok) + delete(m, nw.String()) + } + + assert.Check(t, is.Len(m, 0)) +} + +func TestConfigGlobalScopeDefaultNetworks(t *testing.T) { + err := ConfigGlobalScopeDefaultNetworks([]*NetworkToSplit{{"30.0.0.0/8", 24}}) + assert.NilError(t, err) + + originalGlobalScopeNetworks := initGlobalScopeNetworks() + m := make(map[string]bool) + for _, v := range originalGlobalScopeNetworks { + m[v.String()] = true + } + for _, nw := range PredefinedGlobalScopeDefaultNetworks { + _, ok := m[nw.String()] + assert.Check(t, ok) + delete(m, nw.String()) + } + + assert.Check(t, is.Len(m, 0)) +} + +func TestInitAddressPools(t *testing.T) { + err := ConfigLocalScopeDefaultNetworks([]*NetworkToSplit{{"172.80.0.0/16", 24}, {"172.90.0.0/16", 24}}) + assert.NilError(t, err) + + // Check for Random IPAddresses in PredefinedLocalScopeDefaultNetworks ex: first , last and middle + assert.Check(t, is.Len(PredefinedLocalScopeDefaultNetworks, 512), "Failed to find PredefinedLocalScopeDefaultNetworks") + assert.Check(t, is.Equal(PredefinedLocalScopeDefaultNetworks[0].String(), "172.80.0.0/24")) + assert.Check(t, is.Equal(PredefinedLocalScopeDefaultNetworks[127].String(), "172.80.127.0/24")) + assert.Check(t, is.Equal(PredefinedLocalScopeDefaultNetworks[255].String(), "172.80.255.0/24")) + assert.Check(t, is.Equal(PredefinedLocalScopeDefaultNetworks[256].String(), "172.90.0.0/24")) + assert.Check(t, is.Equal(PredefinedLocalScopeDefaultNetworks[383].String(), "172.90.127.0/24")) + assert.Check(t, is.Equal(PredefinedLocalScopeDefaultNetworks[511].String(), "172.90.255.0/24")) +} diff --git a/vendor/github.com/docker/libnetwork/iptables/conntrack.go b/libnetwork/iptables/conntrack.go similarity index 94% rename from vendor/github.com/docker/libnetwork/iptables/conntrack.go rename to libnetwork/iptables/conntrack.go index 08317c33eeddd..5abae4eede2d6 100644 --- a/vendor/github.com/docker/libnetwork/iptables/conntrack.go +++ b/libnetwork/iptables/conntrack.go @@ -1,3 +1,6 @@ +//go:build linux +// +build linux + package iptables import ( @@ -54,6 +57,8 @@ func purgeConntrackState(nlh *netlink.Handle, family netlink.InetFamily, ipAddre filter := &netlink.ConntrackFilter{} // NOTE: doing the flush using the ipAddress is safe because today there cannot be multiple networks with the same subnet // so it will not be possible to flush flows that are of other containers - filter.AddIP(netlink.ConntrackNatAnyIP, ipAddress) + if err := filter.AddIP(netlink.ConntrackNatAnyIP, ipAddress); err != nil { + return 0, err + } return nlh.ConntrackDeleteFilter(netlink.ConntrackTable, family, filter) } diff --git a/libnetwork/iptables/firewalld.go b/libnetwork/iptables/firewalld.go new file mode 100644 index 0000000000000..3cc5422d4a1b0 --- /dev/null +++ b/libnetwork/iptables/firewalld.go @@ -0,0 +1,306 @@ +//go:build linux +// +build linux + +package iptables + +import ( + "fmt" + "strings" + + dbus "github.com/godbus/dbus/v5" + "github.com/sirupsen/logrus" +) + +// IPV defines the table string +type IPV string + +const ( + // Iptables point ipv4 table + Iptables IPV = "ipv4" + // IP6Tables point to ipv6 table + IP6Tables IPV = "ipv6" + // Ebtables point to bridge table + Ebtables IPV = "eb" +) + +const ( + dbusInterface = "org.fedoraproject.FirewallD1" + dbusPath = "/org/fedoraproject/FirewallD1" + dbusConfigPath = "/org/fedoraproject/FirewallD1/config" + dockerZone = "docker" +) + +// Conn is a connection to firewalld dbus endpoint. +type Conn struct { + sysconn *dbus.Conn + sysObj dbus.BusObject + sysConfObj dbus.BusObject + signal chan *dbus.Signal +} + +// ZoneSettings holds the firewalld zone settings, documented in +// https://firewalld.org/documentation/man-pages/firewalld.dbus.html +type ZoneSettings struct { + version string + name string + description string + unused bool + target string + services []string + ports [][]interface{} + icmpBlocks []string + masquerade bool + forwardPorts [][]interface{} + interfaces []string + sourceAddresses []string + richRules []string + protocols []string + sourcePorts [][]interface{} + icmpBlockInversion bool +} + +var ( + connection *Conn + + firewalldRunning bool // is Firewalld service running + onReloaded []*func() // callbacks when Firewalld has been reloaded +) + +// FirewalldInit initializes firewalld management code. +func FirewalldInit() error { + var err error + + if connection, err = newConnection(); err != nil { + return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err) + } + firewalldRunning = checkRunning() + if !firewalldRunning { + connection.sysconn.Close() + connection = nil + } + if connection != nil { + go signalHandler() + if err := setupDockerZone(); err != nil { + return err + } + } + + return nil +} + +// New() establishes a connection to the system bus. +func newConnection() (*Conn, error) { + c := new(Conn) + if err := c.initConnection(); err != nil { + return nil, err + } + + return c, nil +} + +// Initialize D-Bus connection. +func (c *Conn) initConnection() error { + var err error + + c.sysconn, err = dbus.SystemBus() + if err != nil { + return err + } + + // This never fails, even if the service is not running atm. + c.sysObj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath)) + c.sysConfObj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusConfigPath)) + rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'", + dbusPath, dbusInterface, dbusInterface) + c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) + + rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", + dbusInterface) + c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) + + c.signal = make(chan *dbus.Signal, 10) + c.sysconn.Signal(c.signal) + + return nil +} + +func signalHandler() { + for signal := range connection.signal { + if strings.Contains(signal.Name, "NameOwnerChanged") { + firewalldRunning = checkRunning() + dbusConnectionChanged(signal.Body) + } else if strings.Contains(signal.Name, "Reloaded") { + reloaded() + } + } +} + +func dbusConnectionChanged(args []interface{}) { + name := args[0].(string) + oldOwner := args[1].(string) + newOwner := args[2].(string) + + if name != dbusInterface { + return + } + + if len(newOwner) > 0 { + connectionEstablished() + } else if len(oldOwner) > 0 { + connectionLost() + } +} + +func connectionEstablished() { + reloaded() +} + +func connectionLost() { + // Doesn't do anything for now. Libvirt also doesn't react to this. +} + +// call all callbacks +func reloaded() { + for _, pf := range onReloaded { + (*pf)() + } +} + +// OnReloaded add callback +func OnReloaded(callback func()) { + for _, pf := range onReloaded { + if pf == &callback { + return + } + } + onReloaded = append(onReloaded, &callback) +} + +// Call some remote method to see whether the service is actually running. +func checkRunning() bool { + var zone string + var err error + + if connection != nil { + err = connection.sysObj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone) + return err == nil + } + return false +} + +// Passthrough method simply passes args through to iptables/ip6tables +func Passthrough(ipv IPV, args ...string) ([]byte, error) { + var output string + logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args) + if err := connection.sysObj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output); err != nil { + return nil, err + } + return []byte(output), nil +} + +// getDockerZoneSettings converts the ZoneSettings struct into a interface slice +func getDockerZoneSettings() []interface{} { + settings := ZoneSettings{ + version: "1.0", + name: dockerZone, + description: "zone for docker bridge network interfaces", + target: "ACCEPT", + } + slice := []interface{}{ + settings.version, + settings.name, + settings.description, + settings.unused, + settings.target, + settings.services, + settings.ports, + settings.icmpBlocks, + settings.masquerade, + settings.forwardPorts, + settings.interfaces, + settings.sourceAddresses, + settings.richRules, + settings.protocols, + settings.sourcePorts, + settings.icmpBlockInversion, + } + return slice + +} + +// setupDockerZone creates a zone called docker in firewalld which includes docker interfaces to allow +// container networking +func setupDockerZone() error { + var zones []string + // Check if zone exists + if err := connection.sysObj.Call(dbusInterface+".zone.getZones", 0).Store(&zones); err != nil { + return err + } + if contains(zones, dockerZone) { + logrus.Infof("Firewalld: %s zone already exists, returning", dockerZone) + return nil + } + logrus.Debugf("Firewalld: creating %s zone", dockerZone) + + settings := getDockerZoneSettings() + // Permanent + if err := connection.sysConfObj.Call(dbusInterface+".config.addZone", 0, dockerZone, settings).Err; err != nil { + return err + } + // Reload for change to take effect + if err := connection.sysObj.Call(dbusInterface+".reload", 0).Err; err != nil { + return err + } + + return nil +} + +// AddInterfaceFirewalld adds the interface to the trusted zone +func AddInterfaceFirewalld(intf string) error { + var intfs []string + // Check if interface is already added to the zone + if err := connection.sysObj.Call(dbusInterface+".zone.getInterfaces", 0, dockerZone).Store(&intfs); err != nil { + return err + } + // Return if interface is already part of the zone + if contains(intfs, intf) { + logrus.Infof("Firewalld: interface %s already part of %s zone, returning", intf, dockerZone) + return nil + } + + logrus.Debugf("Firewalld: adding %s interface to %s zone", intf, dockerZone) + // Runtime + if err := connection.sysObj.Call(dbusInterface+".zone.addInterface", 0, dockerZone, intf).Err; err != nil { + return err + } + return nil +} + +// DelInterfaceFirewalld removes the interface from the trusted zone +func DelInterfaceFirewalld(intf string) error { + var intfs []string + // Check if interface is part of the zone + if err := connection.sysObj.Call(dbusInterface+".zone.getInterfaces", 0, dockerZone).Store(&intfs); err != nil { + return err + } + // Remove interface if it exists + if !contains(intfs, intf) { + return fmt.Errorf("Firewalld: unable to find interface %s in %s zone", intf, dockerZone) + } + + logrus.Debugf("Firewalld: removing %s interface from %s zone", intf, dockerZone) + // Runtime + if err := connection.sysObj.Call(dbusInterface+".zone.removeInterface", 0, dockerZone, intf).Err; err != nil { + return err + } + return nil +} + +func contains(list []string, val string) bool { + for _, v := range list { + if v == val { + return true + } + } + return false +} diff --git a/libnetwork/iptables/firewalld_test.go b/libnetwork/iptables/firewalld_test.go new file mode 100644 index 0000000000000..1b47221196988 --- /dev/null +++ b/libnetwork/iptables/firewalld_test.go @@ -0,0 +1,94 @@ +//go:build linux +// +build linux + +package iptables + +import ( + "net" + "strconv" + "testing" +) + +func TestFirewalldInit(t *testing.T) { + if !checkRunning() { + t.Skip("firewalld is not running") + } + if err := FirewalldInit(); err != nil { + t.Fatal(err) + } +} + +func TestReloaded(t *testing.T) { + var err error + var fwdChain *ChainInfo + + iptable := GetIptable(IPv4) + fwdChain, err = iptable.NewChain("FWD", Filter, false) + if err != nil { + t.Fatal(err) + } + bridgeName := "lo" + + err = iptable.ProgramChain(fwdChain, bridgeName, false, true) + if err != nil { + t.Fatal(err) + } + defer fwdChain.Remove() + + // copy-pasted from iptables_test:TestLink + ip1 := net.ParseIP("192.168.1.1") + ip2 := net.ParseIP("192.168.1.2") + port := 1234 + proto := "tcp" + + err = fwdChain.Link(Append, ip1, ip2, port, proto, bridgeName) + if err != nil { + t.Fatal(err) + } else { + // to be re-called again later + OnReloaded(func() { fwdChain.Link(Append, ip1, ip2, port, proto, bridgeName) }) + } + + rule1 := []string{ + "-i", bridgeName, + "-o", bridgeName, + "-p", proto, + "-s", ip1.String(), + "-d", ip2.String(), + "--dport", strconv.Itoa(port), + "-j", "ACCEPT"} + + if !iptable.Exists(fwdChain.Table, fwdChain.Name, rule1...) { + t.Fatal("rule1 does not exist") + } + + // flush all rules + fwdChain.Remove() + + reloaded() + + // make sure the rules have been recreated + if !iptable.Exists(fwdChain.Table, fwdChain.Name, rule1...) { + t.Fatal("rule1 hasn't been recreated") + } +} + +func TestPassthrough(t *testing.T) { + rule1 := []string{ + "-i", "lo", + "-p", "udp", + "--dport", "123", + "-j", "ACCEPT"} + + iptable := GetIptable(IPv4) + if firewalldRunning { + _, err := Passthrough(Iptables, append([]string{"-A"}, rule1...)...) + if err != nil { + t.Fatal(err) + } + if !iptable.Exists(Filter, "INPUT", rule1...) { + t.Fatal("rule1 does not exist") + } + } + +} diff --git a/libnetwork/iptables/iptables.go b/libnetwork/iptables/iptables.go new file mode 100644 index 0000000000000..51a3e2bdc22a1 --- /dev/null +++ b/libnetwork/iptables/iptables.go @@ -0,0 +1,657 @@ +//go:build linux +// +build linux + +package iptables + +import ( + "errors" + "fmt" + "net" + "os/exec" + "regexp" + "strconv" + "strings" + "sync" + "time" + + "github.com/sirupsen/logrus" +) + +// Action signifies the iptable action. +type Action string + +// Policy is the default iptable policies +type Policy string + +// Table refers to Nat, Filter or Mangle. +type Table string + +// IPVersion refers to IP version, v4 or v6 +type IPVersion string + +const ( + // Append appends the rule at the end of the chain. + Append Action = "-A" + // Delete deletes the rule from the chain. + Delete Action = "-D" + // Insert inserts the rule at the top of the chain. + Insert Action = "-I" + // Nat table is used for nat translation rules. + Nat Table = "nat" + // Filter table is used for filter rules. + Filter Table = "filter" + // Mangle table is used for mangling the packet. + Mangle Table = "mangle" + // Drop is the default iptables DROP policy + Drop Policy = "DROP" + // Accept is the default iptables ACCEPT policy + Accept Policy = "ACCEPT" + // IPv4 is version 4 + IPv4 IPVersion = "IPV4" + // IPv6 is version 6 + IPv6 IPVersion = "IPV6" +) + +var ( + iptablesPath string + ip6tablesPath string + supportsXlock = false + supportsCOpt = false + xLockWaitMsg = "Another app is currently holding the xtables lock" + // used to lock iptables commands if xtables lock is not supported + bestEffortLock sync.Mutex + // ErrIptablesNotFound is returned when the rule is not found. + ErrIptablesNotFound = errors.New("Iptables not found") + initOnce sync.Once +) + +// IPTable defines struct with IPVersion +type IPTable struct { + Version IPVersion +} + +// ChainInfo defines the iptables chain. +type ChainInfo struct { + Name string + Table Table + HairpinMode bool + IPTable IPTable +} + +// ChainError is returned to represent errors during ip table operation. +type ChainError struct { + Chain string + Output []byte +} + +func (e ChainError) Error() string { + return fmt.Sprintf("Error iptables %s: %s", e.Chain, string(e.Output)) +} + +func probe() { + path, err := exec.LookPath("iptables") + if err != nil { + logrus.Warnf("Failed to find iptables: %v", err) + return + } + if out, err := exec.Command(path, "--wait", "-t", "nat", "-L", "-n").CombinedOutput(); err != nil { + logrus.Warnf("Running iptables --wait -t nat -L -n failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err) + } + _, err = exec.LookPath("ip6tables") + if err != nil { + logrus.Warnf("Failed to find ip6tables: %v", err) + return + } +} + +func initFirewalld() { + if err := FirewalldInit(); err != nil { + logrus.Debugf("Fail to initialize firewalld: %v, using raw iptables instead", err) + } +} + +func detectIptables() { + path, err := exec.LookPath("iptables") + if err != nil { + return + } + iptablesPath = path + path, err = exec.LookPath("ip6tables") + if err != nil { + return + } + ip6tablesPath = path + supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil + mj, mn, mc, err := GetVersion() + if err != nil { + logrus.Warnf("Failed to read iptables version: %v", err) + return + } + supportsCOpt = supportsCOption(mj, mn, mc) +} + +func initDependencies() { + probe() + initFirewalld() + detectIptables() +} + +func initCheck() error { + initOnce.Do(initDependencies) + + if iptablesPath == "" { + return ErrIptablesNotFound + } + return nil +} + +// GetIptable returns an instance of IPTable with specified version +func GetIptable(version IPVersion) *IPTable { + return &IPTable{Version: version} +} + +// NewChain adds a new chain to ip table. +func (iptable IPTable) NewChain(name string, table Table, hairpinMode bool) (*ChainInfo, error) { + c := &ChainInfo{ + Name: name, + Table: table, + HairpinMode: hairpinMode, + IPTable: iptable, + } + if string(c.Table) == "" { + c.Table = Filter + } + + // Add chain if it doesn't exist + if _, err := iptable.Raw("-t", string(c.Table), "-n", "-L", c.Name); err != nil { + if output, err := iptable.Raw("-t", string(c.Table), "-N", c.Name); err != nil { + return nil, err + } else if len(output) != 0 { + return nil, fmt.Errorf("Could not create %s/%s chain: %s", c.Table, c.Name, output) + } + } + return c, nil +} + +// LoopbackByVersion returns loopback address by version +func (iptable IPTable) LoopbackByVersion() string { + if iptable.Version == IPv6 { + return "::1/128" + } + return "127.0.0.0/8" +} + +// ProgramChain is used to add rules to a chain +func (iptable IPTable) ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) error { + if c.Name == "" { + return errors.New("Could not program chain, missing chain name") + } + + // Either add or remove the interface from the firewalld zone + if firewalldRunning { + if enable { + if err := AddInterfaceFirewalld(bridgeName); err != nil { + return err + } + } else { + if err := DelInterfaceFirewalld(bridgeName); err != nil { + return err + } + } + } + + switch c.Table { + case Nat: + preroute := []string{ + "-m", "addrtype", + "--dst-type", "LOCAL", + "-j", c.Name} + if !iptable.Exists(Nat, "PREROUTING", preroute...) && enable { + if err := c.Prerouting(Append, preroute...); err != nil { + return fmt.Errorf("Failed to inject %s in PREROUTING chain: %s", c.Name, err) + } + } else if iptable.Exists(Nat, "PREROUTING", preroute...) && !enable { + if err := c.Prerouting(Delete, preroute...); err != nil { + return fmt.Errorf("Failed to remove %s in PREROUTING chain: %s", c.Name, err) + } + } + output := []string{ + "-m", "addrtype", + "--dst-type", "LOCAL", + "-j", c.Name} + if !hairpinMode { + output = append(output, "!", "--dst", iptable.LoopbackByVersion()) + } + if !iptable.Exists(Nat, "OUTPUT", output...) && enable { + if err := c.Output(Append, output...); err != nil { + return fmt.Errorf("Failed to inject %s in OUTPUT chain: %s", c.Name, err) + } + } else if iptable.Exists(Nat, "OUTPUT", output...) && !enable { + if err := c.Output(Delete, output...); err != nil { + return fmt.Errorf("Failed to inject %s in OUTPUT chain: %s", c.Name, err) + } + } + case Filter: + if bridgeName == "" { + return fmt.Errorf("Could not program chain %s/%s, missing bridge name", + c.Table, c.Name) + } + link := []string{ + "-o", bridgeName, + "-j", c.Name} + if !iptable.Exists(Filter, "FORWARD", link...) && enable { + insert := append([]string{string(Insert), "FORWARD"}, link...) + if output, err := iptable.Raw(insert...); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output) + } + } else if iptable.Exists(Filter, "FORWARD", link...) && !enable { + del := append([]string{string(Delete), "FORWARD"}, link...) + if output, err := iptable.Raw(del...); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Could not delete linking rule from %s/%s: %s", c.Table, c.Name, output) + } + + } + establish := []string{ + "-o", bridgeName, + "-m", "conntrack", + "--ctstate", "RELATED,ESTABLISHED", + "-j", "ACCEPT"} + if !iptable.Exists(Filter, "FORWARD", establish...) && enable { + insert := append([]string{string(Insert), "FORWARD"}, establish...) + if output, err := iptable.Raw(insert...); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Could not create establish rule to %s: %s", c.Table, output) + } + } else if iptable.Exists(Filter, "FORWARD", establish...) && !enable { + del := append([]string{string(Delete), "FORWARD"}, establish...) + if output, err := iptable.Raw(del...); err != nil { + return err + } else if len(output) != 0 { + return fmt.Errorf("Could not delete establish rule from %s: %s", c.Table, output) + } + } + } + return nil +} + +// RemoveExistingChain removes existing chain from the table. +func (iptable IPTable) RemoveExistingChain(name string, table Table) error { + c := &ChainInfo{ + Name: name, + Table: table, + IPTable: iptable, + } + if string(c.Table) == "" { + c.Table = Filter + } + return c.Remove() +} + +// Forward adds forwarding rule to 'filter' table and corresponding nat rule to 'nat' table. +func (c *ChainInfo) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int, bridgeName string) error { + + iptable := GetIptable(c.IPTable.Version) + daddr := ip.String() + if ip.IsUnspecified() { + // iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we + // want "0.0.0.0/0". "0/0" is correctly interpreted as "any + // value" by both iptables and ip6tables. + daddr = "0/0" + } + + args := []string{ + "-p", proto, + "-d", daddr, + "--dport", strconv.Itoa(port), + "-j", "DNAT", + "--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))} + + if !c.HairpinMode { + args = append(args, "!", "-i", bridgeName) + } + if err := iptable.ProgramRule(Nat, c.Name, action, args); err != nil { + return err + } + + args = []string{ + "!", "-i", bridgeName, + "-o", bridgeName, + "-p", proto, + "-d", destAddr, + "--dport", strconv.Itoa(destPort), + "-j", "ACCEPT", + } + if err := iptable.ProgramRule(Filter, c.Name, action, args); err != nil { + return err + } + + args = []string{ + "-p", proto, + "-s", destAddr, + "-d", destAddr, + "--dport", strconv.Itoa(destPort), + "-j", "MASQUERADE", + } + + if err := iptable.ProgramRule(Nat, "POSTROUTING", action, args); err != nil { + return err + } + + if proto == "sctp" { + // Linux kernel v4.9 and below enables NETIF_F_SCTP_CRC for veth by + // the following commit. + // This introduces a problem when conbined with a physical NIC without + // NETIF_F_SCTP_CRC. As for a workaround, here we add an iptables entry + // to fill the checksum. + // + // https://github.com/torvalds/linux/commit/c80fafbbb59ef9924962f83aac85531039395b18 + args = []string{ + "-p", proto, + "--sport", strconv.Itoa(destPort), + "-j", "CHECKSUM", + "--checksum-fill", + } + if err := iptable.ProgramRule(Mangle, "POSTROUTING", action, args); err != nil { + return err + } + } + + return nil +} + +// Link adds reciprocal ACCEPT rule for two supplied IP addresses. +// Traffic is allowed from ip1 to ip2 and vice-versa +func (c *ChainInfo) Link(action Action, ip1, ip2 net.IP, port int, proto string, bridgeName string) error { + iptable := GetIptable(c.IPTable.Version) + // forward + args := []string{ + "-i", bridgeName, "-o", bridgeName, + "-p", proto, + "-s", ip1.String(), + "-d", ip2.String(), + "--dport", strconv.Itoa(port), + "-j", "ACCEPT", + } + + if err := iptable.ProgramRule(Filter, c.Name, action, args); err != nil { + return err + } + // reverse + args[7], args[9] = args[9], args[7] + args[10] = "--sport" + return iptable.ProgramRule(Filter, c.Name, action, args) +} + +// ProgramRule adds the rule specified by args only if the +// rule is not already present in the chain. Reciprocally, +// it removes the rule only if present. +func (iptable IPTable) ProgramRule(table Table, chain string, action Action, args []string) error { + if iptable.Exists(table, chain, args...) != (action == Delete) { + return nil + } + return iptable.RawCombinedOutput(append([]string{"-t", string(table), string(action), chain}, args...)...) +} + +// Prerouting adds linking rule to nat/PREROUTING chain. +func (c *ChainInfo) Prerouting(action Action, args ...string) error { + iptable := GetIptable(c.IPTable.Version) + a := []string{"-t", string(Nat), string(action), "PREROUTING"} + if len(args) > 0 { + a = append(a, args...) + } + if output, err := iptable.Raw(a...); err != nil { + return err + } else if len(output) != 0 { + return ChainError{Chain: "PREROUTING", Output: output} + } + return nil +} + +// Output adds linking rule to an OUTPUT chain. +func (c *ChainInfo) Output(action Action, args ...string) error { + iptable := GetIptable(c.IPTable.Version) + a := []string{"-t", string(c.Table), string(action), "OUTPUT"} + if len(args) > 0 { + a = append(a, args...) + } + if output, err := iptable.Raw(a...); err != nil { + return err + } else if len(output) != 0 { + return ChainError{Chain: "OUTPUT", Output: output} + } + return nil +} + +// Remove removes the chain. +func (c *ChainInfo) Remove() error { + iptable := GetIptable(c.IPTable.Version) + // Ignore errors - This could mean the chains were never set up + if c.Table == Nat { + c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name) + c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", iptable.LoopbackByVersion(), "-j", c.Name) + c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name) // Created in versions <= 0.1.6 + + c.Prerouting(Delete) + c.Output(Delete) + } + iptable.Raw("-t", string(c.Table), "-F", c.Name) + iptable.Raw("-t", string(c.Table), "-X", c.Name) + return nil +} + +// Exists checks if a rule exists +func (iptable IPTable) Exists(table Table, chain string, rule ...string) bool { + return iptable.exists(false, table, chain, rule...) +} + +// ExistsNative behaves as Exists with the difference it +// will always invoke `iptables` binary. +func (iptable IPTable) ExistsNative(table Table, chain string, rule ...string) bool { + return iptable.exists(true, table, chain, rule...) +} + +func (iptable IPTable) exists(native bool, table Table, chain string, rule ...string) bool { + f := iptable.Raw + if native { + f = iptable.raw + } + + if string(table) == "" { + table = Filter + } + + if err := initCheck(); err != nil { + // The exists() signature does not allow us to return an error, but at least + // we can skip the (likely invalid) exec invocation. + return false + } + + if supportsCOpt { + // if exit status is 0 then return true, the rule exists + _, err := f(append([]string{"-t", string(table), "-C", chain}, rule...)...) + return err == nil + } + + // parse "iptables -S" for the rule (it checks rules in a specific chain + // in a specific table and it is very unreliable) + return iptable.existsRaw(table, chain, rule...) +} + +func (iptable IPTable) existsRaw(table Table, chain string, rule ...string) bool { + path := iptablesPath + if iptable.Version == IPv6 { + path = ip6tablesPath + } + ruleString := fmt.Sprintf("%s %s\n", chain, strings.Join(rule, " ")) + existingRules, _ := exec.Command(path, "-t", string(table), "-S", chain).Output() + + return strings.Contains(string(existingRules), ruleString) +} + +// Maximum duration that an iptables operation can take +// before flagging a warning. +const opWarnTime = 2 * time.Second + +func filterOutput(start time.Time, output []byte, args ...string) []byte { + // Flag operations that have taken a long time to complete + opTime := time.Since(start) + if opTime > opWarnTime { + logrus.Warnf("xtables contention detected while running [%s]: Waited for %.2f seconds and received %q", strings.Join(args, " "), float64(opTime)/float64(time.Second), string(output)) + } + // ignore iptables' message about xtables lock: + // it is a warning, not an error. + if strings.Contains(string(output), xLockWaitMsg) { + output = []byte("") + } + // Put further filters here if desired + return output +} + +// Raw calls 'iptables' system command, passing supplied arguments. +func (iptable IPTable) Raw(args ...string) ([]byte, error) { + if firewalldRunning { + // select correct IP version for firewalld + ipv := Iptables + if iptable.Version == IPv6 { + ipv = IP6Tables + } + + startTime := time.Now() + output, err := Passthrough(ipv, args...) + if err == nil || !strings.Contains(err.Error(), "was not provided by any .service files") { + return filterOutput(startTime, output, args...), err + } + } + return iptable.raw(args...) +} + +func (iptable IPTable) raw(args ...string) ([]byte, error) { + if err := initCheck(); err != nil { + return nil, err + } + if supportsXlock { + args = append([]string{"--wait"}, args...) + } else { + bestEffortLock.Lock() + defer bestEffortLock.Unlock() + } + + path := iptablesPath + commandName := "iptables" + if iptable.Version == IPv6 { + path = ip6tablesPath + commandName = "ip6tables" + } + + logrus.Debugf("%s, %v", path, args) + + startTime := time.Now() + output, err := exec.Command(path, args...).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("iptables failed: %s %v: %s (%s)", commandName, strings.Join(args, " "), output, err) + } + + return filterOutput(startTime, output, args...), err +} + +// RawCombinedOutput internally calls the Raw function and returns a non nil +// error if Raw returned a non nil error or a non empty output +func (iptable IPTable) RawCombinedOutput(args ...string) error { + if output, err := iptable.Raw(args...); err != nil || len(output) != 0 { + return fmt.Errorf("%s (%v)", string(output), err) + } + return nil +} + +// RawCombinedOutputNative behave as RawCombinedOutput with the difference it +// will always invoke `iptables` binary +func (iptable IPTable) RawCombinedOutputNative(args ...string) error { + if output, err := iptable.raw(args...); err != nil || len(output) != 0 { + return fmt.Errorf("%s (%v)", string(output), err) + } + return nil +} + +// ExistChain checks if a chain exists +func (iptable IPTable) ExistChain(chain string, table Table) bool { + if _, err := iptable.Raw("-t", string(table), "-nL", chain); err == nil { + return true + } + return false +} + +// GetVersion reads the iptables version numbers during initialization +func GetVersion() (major, minor, micro int, err error) { + out, err := exec.Command(iptablesPath, "--version").CombinedOutput() + if err == nil { + major, minor, micro = parseVersionNumbers(string(out)) + } + return +} + +// SetDefaultPolicy sets the passed default policy for the table/chain +func (iptable IPTable) SetDefaultPolicy(table Table, chain string, policy Policy) error { + if err := iptable.RawCombinedOutput("-t", string(table), "-P", chain, string(policy)); err != nil { + return fmt.Errorf("setting default policy to %v in %v chain failed: %v", policy, chain, err) + } + return nil +} + +func parseVersionNumbers(input string) (major, minor, micro int) { + re := regexp.MustCompile(`v\d*.\d*.\d*`) + line := re.FindString(input) + fmt.Sscanf(line, "v%d.%d.%d", &major, &minor, µ) + return +} + +// iptables -C, --check option was added in v.1.4.11 +// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt +func supportsCOption(mj, mn, mc int) bool { + return mj > 1 || (mj == 1 && (mn > 4 || (mn == 4 && mc >= 11))) +} + +// AddReturnRule adds a return rule for the chain in the filter table +func (iptable IPTable) AddReturnRule(chain string) error { + var ( + table = Filter + args = []string{"-j", "RETURN"} + ) + + if iptable.Exists(table, chain, args...) { + return nil + } + + err := iptable.RawCombinedOutput(append([]string{"-A", chain}, args...)...) + if err != nil { + return fmt.Errorf("unable to add return rule in %s chain: %s", chain, err.Error()) + } + + return nil +} + +// EnsureJumpRule ensures the jump rule is on top +func (iptable IPTable) EnsureJumpRule(fromChain, toChain string) error { + var ( + table = Filter + args = []string{"-j", toChain} + ) + + if iptable.Exists(table, fromChain, args...) { + err := iptable.RawCombinedOutput(append([]string{"-D", fromChain}, args...)...) + if err != nil { + return fmt.Errorf("unable to remove jump to %s rule in %s chain: %s", toChain, fromChain, err.Error()) + } + } + + err := iptable.RawCombinedOutput(append([]string{"-I", fromChain}, args...)...) + if err != nil { + return fmt.Errorf("unable to insert jump to %s rule in %s chain: %s", toChain, fromChain, err.Error()) + } + + return nil +} diff --git a/libnetwork/iptables/iptables_test.go b/libnetwork/iptables/iptables_test.go new file mode 100644 index 0000000000000..d46e5153cc36a --- /dev/null +++ b/libnetwork/iptables/iptables_test.go @@ -0,0 +1,327 @@ +//go:build linux +// +build linux + +package iptables + +import ( + "net" + "os/exec" + "strconv" + "strings" + "testing" + + "golang.org/x/sync/errgroup" +) + +const chainName = "DOCKEREST" + +var natChain *ChainInfo +var filterChain *ChainInfo +var bridgeName string + +func TestNewChain(t *testing.T) { + var err error + + bridgeName = "lo" + iptable := GetIptable(IPv4) + + natChain, err = iptable.NewChain(chainName, Nat, false) + if err != nil { + t.Fatal(err) + } + err = iptable.ProgramChain(natChain, bridgeName, false, true) + if err != nil { + t.Fatal(err) + } + + filterChain, err = iptable.NewChain(chainName, Filter, false) + if err != nil { + t.Fatal(err) + } + err = iptable.ProgramChain(filterChain, bridgeName, false, true) + if err != nil { + t.Fatal(err) + } +} + +func TestForward(t *testing.T) { + ip := net.ParseIP("192.168.1.1") + port := 1234 + dstAddr := "172.17.0.1" + dstPort := 4321 + proto := "tcp" + + bridgeName := "lo" + iptable := GetIptable(IPv4) + + err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort, bridgeName) + if err != nil { + t.Fatal(err) + } + + dnatRule := []string{ + "-d", ip.String(), + "-p", proto, + "--dport", strconv.Itoa(port), + "-j", "DNAT", + "--to-destination", dstAddr + ":" + strconv.Itoa(dstPort), + "!", "-i", bridgeName, + } + + if !iptable.Exists(natChain.Table, natChain.Name, dnatRule...) { + t.Fatal("DNAT rule does not exist") + } + + filterRule := []string{ + "!", "-i", bridgeName, + "-o", bridgeName, + "-d", dstAddr, + "-p", proto, + "--dport", strconv.Itoa(dstPort), + "-j", "ACCEPT", + } + + if !iptable.Exists(filterChain.Table, filterChain.Name, filterRule...) { + t.Fatal("filter rule does not exist") + } + + masqRule := []string{ + "-d", dstAddr, + "-s", dstAddr, + "-p", proto, + "--dport", strconv.Itoa(dstPort), + "-j", "MASQUERADE", + } + + if !iptable.Exists(natChain.Table, "POSTROUTING", masqRule...) { + t.Fatal("MASQUERADE rule does not exist") + } +} + +func TestLink(t *testing.T) { + var err error + + bridgeName := "lo" + iptable := GetIptable(IPv4) + ip1 := net.ParseIP("192.168.1.1") + ip2 := net.ParseIP("192.168.1.2") + port := 1234 + proto := "tcp" + + err = filterChain.Link(Append, ip1, ip2, port, proto, bridgeName) + if err != nil { + t.Fatal(err) + } + + rule1 := []string{ + "-i", bridgeName, + "-o", bridgeName, + "-p", proto, + "-s", ip1.String(), + "-d", ip2.String(), + "--dport", strconv.Itoa(port), + "-j", "ACCEPT"} + + if !iptable.Exists(filterChain.Table, filterChain.Name, rule1...) { + t.Fatal("rule1 does not exist") + } + + rule2 := []string{ + "-i", bridgeName, + "-o", bridgeName, + "-p", proto, + "-s", ip2.String(), + "-d", ip1.String(), + "--sport", strconv.Itoa(port), + "-j", "ACCEPT"} + + if !iptable.Exists(filterChain.Table, filterChain.Name, rule2...) { + t.Fatal("rule2 does not exist") + } +} + +func TestPrerouting(t *testing.T) { + args := []string{ + "-i", "lo", + "-d", "192.168.1.1"} + iptable := GetIptable(IPv4) + + err := natChain.Prerouting(Insert, args...) + if err != nil { + t.Fatal(err) + } + + if !iptable.Exists(natChain.Table, "PREROUTING", args...) { + t.Fatal("rule does not exist") + } + + delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, args...) + if _, err = iptable.Raw(delRule...); err != nil { + t.Fatal(err) + } +} + +func TestOutput(t *testing.T) { + args := []string{ + "-o", "lo", + "-d", "192.168.1.1"} + iptable := GetIptable(IPv4) + + err := natChain.Output(Insert, args...) + if err != nil { + t.Fatal(err) + } + + if !iptable.Exists(natChain.Table, "OUTPUT", args...) { + t.Fatal("rule does not exist") + } + + delRule := append([]string{"-D", "OUTPUT", "-t", + string(natChain.Table)}, args...) + if _, err = iptable.Raw(delRule...); err != nil { + t.Fatal(err) + } +} + +func TestConcurrencyWithWait(t *testing.T) { + RunConcurrencyTest(t, true) +} + +func TestConcurrencyNoWait(t *testing.T) { + RunConcurrencyTest(t, false) +} + +// Runs 10 concurrent rule additions. This will fail if iptables +// is actually invoked simultaneously without --wait. +// Note that if iptables does not support the xtable lock on this +// system, then allowXlock has no effect -- it will always be off. +func RunConcurrencyTest(t *testing.T, allowXlock bool) { + if !allowXlock && supportsXlock { + supportsXlock = false + defer func() { supportsXlock = true }() + } + + ip := net.ParseIP("192.168.1.1") + port := 1234 + dstAddr := "172.17.0.1" + dstPort := 4321 + proto := "tcp" + + group := new(errgroup.Group) + for i := 0; i < 10; i++ { + group.Go(func() error { + return natChain.Forward(Append, ip, port, proto, dstAddr, dstPort, "lo") + }) + } + if err := group.Wait(); err != nil { + t.Fatal(err) + } +} + +func TestCleanup(t *testing.T) { + var err error + var rules []byte + + // Cleanup filter/FORWARD first otherwise output of iptables-save is dirty + link := []string{"-t", string(filterChain.Table), + string(Delete), "FORWARD", + "-o", bridgeName, + "-j", filterChain.Name} + iptable := GetIptable(IPv4) + + if _, err = iptable.Raw(link...); err != nil { + t.Fatal(err) + } + filterChain.Remove() + + err = iptable.RemoveExistingChain(chainName, Nat) + if err != nil { + t.Fatal(err) + } + + rules, err = exec.Command("iptables-save").Output() + if err != nil { + t.Fatal(err) + } + if strings.Contains(string(rules), chainName) { + t.Fatalf("Removing chain failed. %s found in iptables-save", chainName) + } +} + +func TestExistsRaw(t *testing.T) { + testChain1 := "ABCD" + testChain2 := "EFGH" + + iptable := GetIptable(IPv4) + + _, err := iptable.NewChain(testChain1, Filter, false) + if err != nil { + t.Fatal(err) + } + defer func() { + iptable.RemoveExistingChain(testChain1, Filter) + }() + + _, err = iptable.NewChain(testChain2, Filter, false) + if err != nil { + t.Fatal(err) + } + defer func() { + iptable.RemoveExistingChain(testChain2, Filter) + }() + + // Test detection over full and truncated rule string + input := []struct{ rule []string }{ + {[]string{"-s", "172.8.9.9/32", "-j", "ACCEPT"}}, + {[]string{"-d", "172.8.9.0/24", "-j", "DROP"}}, + {[]string{"-s", "172.0.3.0/24", "-d", "172.17.0.0/24", "-p", "tcp", "-m", "tcp", "--dport", "80", "-j", testChain2}}, + {[]string{"-j", "RETURN"}}, + } + + for i, r := range input { + ruleAdd := append([]string{"-t", string(Filter), "-A", testChain1}, r.rule...) + err = iptable.RawCombinedOutput(ruleAdd...) + if err != nil { + t.Fatalf("i=%d, err: %v", i, err) + } + if !iptable.existsRaw(Filter, testChain1, r.rule...) { + t.Fatalf("Failed to detect rule. i=%d", i) + } + // Truncate the rule + trg := r.rule[len(r.rule)-1] + trg = trg[:len(trg)-2] + r.rule[len(r.rule)-1] = trg + if iptable.existsRaw(Filter, testChain1, r.rule...) { + t.Fatalf("Invalid detection. i=%d", i) + } + } +} + +func TestGetVersion(t *testing.T) { + mj, mn, mc := parseVersionNumbers("iptables v1.4.19.1-alpha") + if mj != 1 || mn != 4 || mc != 19 { + t.Fatal("Failed to parse version numbers") + } +} + +func TestSupportsCOption(t *testing.T) { + input := []struct { + mj int + mn int + mc int + ok bool + }{ + {1, 4, 11, true}, + {1, 4, 12, true}, + {1, 5, 0, true}, + {0, 4, 11, false}, + {0, 5, 12, false}, + {1, 3, 12, false}, + {1, 4, 10, false}, + } + for ind, inp := range input { + if inp.ok != supportsCOption(inp.mj, inp.mn, inp.mc) { + t.Fatalf("Incorrect check: %d", ind) + } + } +} diff --git a/libnetwork/libnetwork_internal_test.go b/libnetwork/libnetwork_internal_test.go new file mode 100644 index 0000000000000..2992ca57ee21a --- /dev/null +++ b/libnetwork/libnetwork_internal_test.go @@ -0,0 +1,709 @@ +package libnetwork + +import ( + "encoding/json" + "fmt" + "net" + "runtime" + "testing" + "time" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/discoverapi" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/internal/setmatrix" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/testutils" + "github.com/docker/docker/libnetwork/types" + "gotest.tools/v3/skip" +) + +func TestNetworkMarshalling(t *testing.T) { + n := &network{ + name: "Miao", + id: "abccba", + ipamType: "default", + addrSpace: "viola", + networkType: "bridge", + enableIPv6: true, + persist: true, + configOnly: true, + configFrom: "configOnlyX", + ipamOptions: map[string]string{ + netlabel.MacAddress: "a:b:c:d:e:f", + "primary": "", + }, + ipamV4Config: []*IpamConf{ + { + PreferredPool: "10.2.0.0/16", + SubPool: "10.2.0.0/24", + Gateway: "", + AuxAddresses: nil, + }, + { + PreferredPool: "10.2.0.0/16", + SubPool: "10.2.1.0/24", + Gateway: "10.2.1.254", + }, + }, + ipamV6Config: []*IpamConf{ + { + PreferredPool: "abcd::/64", + SubPool: "abcd:abcd:abcd:abcd:abcd::/80", + Gateway: "abcd::29/64", + AuxAddresses: nil, + }, + }, + ipamV4Info: []*IpamInfo{ + { + PoolID: "ipoolverde123", + Meta: map[string]string{ + netlabel.Gateway: "10.2.1.255/16", + }, + IPAMData: driverapi.IPAMData{ + AddressSpace: "viola", + Pool: &net.IPNet{ + IP: net.IP{10, 2, 0, 0}, + Mask: net.IPMask{255, 255, 255, 0}, + }, + Gateway: nil, + AuxAddresses: nil, + }, + }, + { + PoolID: "ipoolblue345", + Meta: map[string]string{ + netlabel.Gateway: "10.2.1.255/16", + }, + IPAMData: driverapi.IPAMData{ + AddressSpace: "viola", + Pool: &net.IPNet{ + IP: net.IP{10, 2, 1, 0}, + Mask: net.IPMask{255, 255, 255, 0}, + }, + Gateway: &net.IPNet{IP: net.IP{10, 2, 1, 254}, Mask: net.IPMask{255, 255, 255, 0}}, + AuxAddresses: map[string]*net.IPNet{ + "ip3": {IP: net.IP{10, 2, 1, 3}, Mask: net.IPMask{255, 255, 255, 0}}, + "ip5": {IP: net.IP{10, 2, 1, 55}, Mask: net.IPMask{255, 255, 255, 0}}, + }, + }, + }, + { + PoolID: "weirdinfo", + IPAMData: driverapi.IPAMData{ + Gateway: &net.IPNet{ + IP: net.IP{11, 2, 1, 255}, + Mask: net.IPMask{255, 0, 0, 0}, + }, + }, + }, + }, + ipamV6Info: []*IpamInfo{ + { + PoolID: "ipoolv6", + IPAMData: driverapi.IPAMData{ + AddressSpace: "viola", + Pool: &net.IPNet{ + IP: net.IP{0xab, 0xcd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Mask: net.IPMask{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0}, + }, + Gateway: &net.IPNet{ + IP: net.IP{0xab, 0xcd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29}, + Mask: net.IPMask{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0}, + }, + AuxAddresses: nil, + }, + }, + }, + labels: map[string]string{ + "color": "blue", + "superimposed": "", + }, + created: time.Now(), + } + + b, err := json.Marshal(n) + if err != nil { + t.Fatal(err) + } + + nn := &network{} + err = json.Unmarshal(b, nn) + if err != nil { + t.Fatal(err) + } + + if n.name != nn.name || n.id != nn.id || n.networkType != nn.networkType || n.ipamType != nn.ipamType || + n.addrSpace != nn.addrSpace || n.enableIPv6 != nn.enableIPv6 || + n.persist != nn.persist || !compareIpamConfList(n.ipamV4Config, nn.ipamV4Config) || + !compareIpamInfoList(n.ipamV4Info, nn.ipamV4Info) || !compareIpamConfList(n.ipamV6Config, nn.ipamV6Config) || + !compareIpamInfoList(n.ipamV6Info, nn.ipamV6Info) || + !compareStringMaps(n.ipamOptions, nn.ipamOptions) || + !compareStringMaps(n.labels, nn.labels) || + !n.created.Equal(nn.created) || + n.configOnly != nn.configOnly || n.configFrom != nn.configFrom { + t.Fatalf("JSON marsh/unmarsh failed."+ + "\nOriginal:\n%#v\nDecoded:\n%#v"+ + "\nOriginal ipamV4Conf: %#v\n\nDecoded ipamV4Conf: %#v"+ + "\nOriginal ipamV4Info: %s\n\nDecoded ipamV4Info: %s"+ + "\nOriginal ipamV6Conf: %#v\n\nDecoded ipamV6Conf: %#v"+ + "\nOriginal ipamV6Info: %s\n\nDecoded ipamV6Info: %s", + n, nn, printIpamConf(n.ipamV4Config), printIpamConf(nn.ipamV4Config), + printIpamInfo(n.ipamV4Info), printIpamInfo(nn.ipamV4Info), + printIpamConf(n.ipamV6Config), printIpamConf(nn.ipamV6Config), + printIpamInfo(n.ipamV6Info), printIpamInfo(nn.ipamV6Info)) + } +} + +func printIpamConf(list []*IpamConf) string { + s := "\n[]*IpamConfig{" + for _, i := range list { + s = fmt.Sprintf("%s %v,", s, i) + } + s = fmt.Sprintf("%s}", s) + return s +} + +func printIpamInfo(list []*IpamInfo) string { + s := "\n[]*IpamInfo{" + for _, i := range list { + s = fmt.Sprintf("%s\n{\n%s\n}", s, i) + } + s = fmt.Sprintf("%s\n}", s) + return s +} + +func TestEndpointMarshalling(t *testing.T) { + ip, nw6, err := net.ParseCIDR("2001:db8:4003::122/64") + if err != nil { + t.Fatal(err) + } + nw6.IP = ip + + var lla []*net.IPNet + for _, nw := range []string{"169.254.0.1/16", "169.254.1.1/16", "169.254.2.2/16"} { + ll, _ := types.ParseCIDR(nw) + lla = append(lla, ll) + } + + e := &endpoint{ + name: "Bau", + id: "efghijklmno", + sandboxID: "ambarabaciccicocco", + anonymous: true, + iface: &endpointInterface{ + mac: []byte{11, 12, 13, 14, 15, 16}, + addr: &net.IPNet{ + IP: net.IP{10, 0, 1, 23}, + Mask: net.IPMask{255, 255, 255, 0}, + }, + addrv6: nw6, + srcName: "veth12ab1314", + dstPrefix: "eth", + v4PoolID: "poolpool", + v6PoolID: "poolv6", + llAddrs: lla, + }, + } + + b, err := json.Marshal(e) + if err != nil { + t.Fatal(err) + } + + ee := &endpoint{} + err = json.Unmarshal(b, ee) + if err != nil { + t.Fatal(err) + } + + if e.name != ee.name || e.id != ee.id || e.sandboxID != ee.sandboxID || !compareEndpointInterface(e.iface, ee.iface) || e.anonymous != ee.anonymous { + t.Fatalf("JSON marsh/unmarsh failed.\nOriginal:\n%#v\nDecoded:\n%#v\nOriginal iface: %#v\nDecodediface:\n%#v", e, ee, e.iface, ee.iface) + } +} + +func compareEndpointInterface(a, b *endpointInterface) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + return a.srcName == b.srcName && a.dstPrefix == b.dstPrefix && a.v4PoolID == b.v4PoolID && a.v6PoolID == b.v6PoolID && + types.CompareIPNet(a.addr, b.addr) && types.CompareIPNet(a.addrv6, b.addrv6) && compareNwLists(a.llAddrs, b.llAddrs) +} + +func compareIpamConfList(listA, listB []*IpamConf) bool { + var a, b *IpamConf + if len(listA) != len(listB) { + return false + } + for i := 0; i < len(listA); i++ { + a = listA[i] + b = listB[i] + if a.PreferredPool != b.PreferredPool || + a.SubPool != b.SubPool || + a.Gateway != b.Gateway || !compareStringMaps(a.AuxAddresses, b.AuxAddresses) { + return false + } + } + return true +} + +func compareIpamInfoList(listA, listB []*IpamInfo) bool { + var a, b *IpamInfo + if len(listA) != len(listB) { + return false + } + for i := 0; i < len(listA); i++ { + a = listA[i] + b = listB[i] + if a.PoolID != b.PoolID || !compareStringMaps(a.Meta, b.Meta) || + !types.CompareIPNet(a.Gateway, b.Gateway) || + a.AddressSpace != b.AddressSpace || + !types.CompareIPNet(a.Pool, b.Pool) || + !compareAddresses(a.AuxAddresses, b.AuxAddresses) { + return false + } + } + return true +} + +func compareStringMaps(a, b map[string]string) bool { + if len(a) != len(b) { + return false + } + if len(a) > 0 { + for k := range a { + if a[k] != b[k] { + return false + } + } + } + return true +} + +func compareAddresses(a, b map[string]*net.IPNet) bool { + if len(a) != len(b) { + return false + } + if len(a) > 0 { + for k := range a { + if !types.CompareIPNet(a[k], b[k]) { + return false + } + } + } + return true +} + +func compareNwLists(a, b []*net.IPNet) bool { + if len(a) != len(b) { + return false + } + for k := range a { + if !types.CompareIPNet(a[k], b[k]) { + return false + } + } + return true +} + +func TestAuxAddresses(t *testing.T) { + c, err := New() + if err != nil { + t.Fatal(err) + } + defer c.Stop() + + n := &network{ipamType: ipamapi.DefaultIPAM, networkType: "bridge", ctrlr: c.(*controller)} + + input := []struct { + masterPool string + subPool string + auxAddresses map[string]string + good bool + }{ + {"192.168.0.0/16", "", map[string]string{"goodOne": "192.168.2.2"}, true}, + {"192.168.0.0/16", "", map[string]string{"badOne": "192.169.2.3"}, false}, + {"192.168.0.0/16", "192.168.1.0/24", map[string]string{"goodOne": "192.168.1.2"}, true}, + {"192.168.0.0/16", "192.168.1.0/24", map[string]string{"stillGood": "192.168.2.4"}, true}, + {"192.168.0.0/16", "192.168.1.0/24", map[string]string{"badOne": "192.169.2.4"}, false}, + } + + for _, i := range input { + + n.ipamV4Config = []*IpamConf{{PreferredPool: i.masterPool, SubPool: i.subPool, AuxAddresses: i.auxAddresses}} + + err = n.ipamAllocate() + + if i.good != (err == nil) { + t.Fatalf("Unexpected result for %v: %v", i, err) + } + + n.ipamRelease() + } +} + +func TestSRVServiceQuery(t *testing.T) { + skip.If(t, runtime.GOOS == "windows", "test only works on linux") + + c, err := New() + if err != nil { + t.Fatal(err) + } + defer c.Stop() + + n, err := c.NewNetwork("bridge", "net1", "", nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := n.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + sb, err := c.NewSandbox("c1") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep.Join(sb) + if err != nil { + t.Fatal(err) + } + + sr := svcInfo{ + svcMap: setmatrix.NewSetMatrix(), + svcIPv6Map: setmatrix.NewSetMatrix(), + ipMap: setmatrix.NewSetMatrix(), + service: make(map[string][]servicePorts), + } + // backing container for the service + cTarget := serviceTarget{ + name: "task1.web.swarm", + ip: net.ParseIP("192.168.10.2"), + port: 80, + } + // backing host for the service + hTarget := serviceTarget{ + name: "node1.docker-cluster", + ip: net.ParseIP("10.10.10.2"), + port: 45321, + } + httpPort := servicePorts{ + portName: "_http", + proto: "_tcp", + target: []serviceTarget{cTarget}, + } + + extHTTPPort := servicePorts{ + portName: "_host_http", + proto: "_tcp", + target: []serviceTarget{hTarget}, + } + sr.service["web.swarm"] = append(sr.service["web.swarm"], httpPort) + sr.service["web.swarm"] = append(sr.service["web.swarm"], extHTTPPort) + + c.(*controller).svcRecords[n.ID()] = sr + + _, ip := ep.Info().Sandbox().ResolveService("_http._tcp.web.swarm") + + if len(ip) == 0 { + t.Fatal(err) + } + if ip[0].String() != "192.168.10.2" { + t.Fatal(err) + } + + _, ip = ep.Info().Sandbox().ResolveService("_host_http._tcp.web.swarm") + + if len(ip) == 0 { + t.Fatal(err) + } + if ip[0].String() != "10.10.10.2" { + t.Fatal(err) + } + + // Service name with invalid protocol name. Should fail without error + _, ip = ep.Info().Sandbox().ResolveService("_http._icmp.web.swarm") + if len(ip) != 0 { + t.Fatal("Valid response for invalid service name") + } +} + +func TestServiceVIPReuse(t *testing.T) { + skip.If(t, runtime.GOOS == "windows", "test only works on linux") + + c, err := New() + if err != nil { + t.Fatal(err) + } + defer c.Stop() + + n, err := c.NewNetwork("bridge", "net1", "", nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := n.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + sb, err := c.NewSandbox("c1") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep.Join(sb) + if err != nil { + t.Fatal(err) + } + + // Add 2 services with same name but different service ID to share the same VIP + n.(*network).addSvcRecords("ep1", "service_test", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test") + n.(*network).addSvcRecords("ep2", "service_test", "serviceID2", net.ParseIP("192.168.0.1"), net.IP{}, true, "test") + + ipToResolve := netutils.ReverseIP("192.168.0.1") + + ipList, _ := n.(*network).ResolveName("service_test", types.IPv4) + if len(ipList) == 0 { + t.Fatal("There must be the VIP") + } + if len(ipList) != 1 { + t.Fatal("It must return only 1 VIP") + } + if ipList[0].String() != "192.168.0.1" { + t.Fatal("The service VIP is 192.168.0.1") + } + name := n.(*network).ResolveIP(ipToResolve) + if name == "" { + t.Fatal("It must return a name") + } + if name != "service_test.net1" { + t.Fatalf("It must return the service_test.net1 != %s", name) + } + + // Delete service record for one of the services, the IP should remain because one service is still associated with it + n.(*network).deleteSvcRecords("ep1", "service_test", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test") + ipList, _ = n.(*network).ResolveName("service_test", types.IPv4) + if len(ipList) == 0 { + t.Fatal("There must be the VIP") + } + if len(ipList) != 1 { + t.Fatal("It must return only 1 VIP") + } + if ipList[0].String() != "192.168.0.1" { + t.Fatal("The service VIP is 192.168.0.1") + } + name = n.(*network).ResolveIP(ipToResolve) + if name == "" { + t.Fatal("It must return a name") + } + if name != "service_test.net1" { + t.Fatalf("It must return the service_test.net1 != %s", name) + } + + // Delete again the service using the previous service ID, nothing should happen + n.(*network).deleteSvcRecords("ep2", "service_test", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test") + ipList, _ = n.(*network).ResolveName("service_test", types.IPv4) + if len(ipList) == 0 { + t.Fatal("There must be the VIP") + } + if len(ipList) != 1 { + t.Fatal("It must return only 1 VIP") + } + if ipList[0].String() != "192.168.0.1" { + t.Fatal("The service VIP is 192.168.0.1") + } + name = n.(*network).ResolveIP(ipToResolve) + if name == "" { + t.Fatal("It must return a name") + } + if name != "service_test.net1" { + t.Fatalf("It must return the service_test.net1 != %s", name) + } + + // Delete now using the second service ID, now all the entries should be gone + n.(*network).deleteSvcRecords("ep2", "service_test", "serviceID2", net.ParseIP("192.168.0.1"), net.IP{}, true, "test") + ipList, _ = n.(*network).ResolveName("service_test", types.IPv4) + if len(ipList) != 0 { + t.Fatal("All the VIPs should be gone now") + } + name = n.(*network).ResolveIP(ipToResolve) + if name != "" { + t.Fatalf("It must return empty no more services associated, instead:%s", name) + } +} + +func TestIpamReleaseOnNetDriverFailures(t *testing.T) { + skip.If(t, runtime.GOOS == "windows", "test only works on linux") + + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + cfgOptions, err := OptionBoltdbWithRandomDBFile() + if err != nil { + t.Fatal(err) + } + c, err := New(cfgOptions...) + if err != nil { + t.Fatal(err) + } + defer c.Stop() + + cc := c.(*controller) + + if err := cc.drvRegistry.AddDriver(badDriverName, badDriverInit, nil); err != nil { + t.Fatal(err) + } + + // Test whether ipam state release is invoked on network create failure from net driver + // by checking whether subsequent network creation requesting same gateway IP succeeds + ipamOpt := NetworkOptionIpam(ipamapi.DefaultIPAM, "", []*IpamConf{{PreferredPool: "10.34.0.0/16", Gateway: "10.34.255.254"}}, nil, nil) + if _, err := c.NewNetwork(badDriverName, "badnet1", "", ipamOpt); err == nil { + t.Fatalf("bad network driver should have failed network creation") + } + + gnw, err := c.NewNetwork("bridge", "goodnet1", "", ipamOpt) + if err != nil { + t.Fatal(err) + } + if err := gnw.Delete(); err != nil { + t.Fatal(err) + } + + // Now check whether ipam release works on endpoint creation failure + bd.failNetworkCreation = false + bnw, err := c.NewNetwork(badDriverName, "badnet2", "", ipamOpt) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := bnw.Delete(); err != nil { + t.Fatal(err) + } + }() + + if _, err := bnw.CreateEndpoint("ep0"); err == nil { + t.Fatalf("bad network driver should have failed endpoint creation") + } + + // Now create good bridge network with different gateway + ipamOpt2 := NetworkOptionIpam(ipamapi.DefaultIPAM, "", []*IpamConf{{PreferredPool: "10.35.0.0/16", Gateway: "10.35.255.253"}}, nil, nil) + gnw, err = c.NewNetwork("bridge", "goodnet2", "", ipamOpt2) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := gnw.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := gnw.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + defer ep.Delete(false) // nolint:errcheck + + expectedIP, _ := types.ParseCIDR("10.35.0.1/16") + if !types.CompareIPNet(ep.Info().Iface().Address(), expectedIP) { + t.Fatalf("Ipam release must have failed, endpoint has unexpected address: %v", ep.Info().Iface().Address()) + } +} + +var badDriverName = "bad network driver" + +type badDriver struct { + failNetworkCreation bool +} + +var bd = badDriver{failNetworkCreation: true} + +func badDriverInit(reg driverapi.DriverCallback, opt map[string]interface{}) error { + return reg.RegisterDriver(badDriverName, &bd, driverapi.Capability{DataScope: datastore.LocalScope}) +} + +func (b *badDriver) CreateNetwork(nid string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { + if b.failNetworkCreation { + return fmt.Errorf("I will not create any network") + } + return nil +} +func (b *badDriver) DeleteNetwork(nid string) error { + return nil +} +func (b *badDriver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, options map[string]interface{}) error { + return fmt.Errorf("I will not create any endpoint") +} +func (b *badDriver) DeleteEndpoint(nid, eid string) error { + return nil +} +func (b *badDriver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { + return nil, nil +} +func (b *badDriver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + return fmt.Errorf("I will not allow any join") +} +func (b *badDriver) Leave(nid, eid string) error { + return nil +} +func (b *badDriver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} +func (b *badDriver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { + return nil +} +func (b *badDriver) Type() string { + return badDriverName +} +func (b *badDriver) IsBuiltIn() bool { + return false +} +func (b *badDriver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} +func (b *badDriver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + +func (b *badDriver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { + return nil, types.NotImplementedErrorf("not implemented") +} + +func (b *badDriver) NetworkFree(id string) error { + return types.NotImplementedErrorf("not implemented") +} + +func (b *badDriver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { +} + +func (b *badDriver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { + return "", nil +} diff --git a/libnetwork/libnetwork_linux_test.go b/libnetwork/libnetwork_linux_test.go new file mode 100644 index 0000000000000..b243017d82844 --- /dev/null +++ b/libnetwork/libnetwork_linux_test.go @@ -0,0 +1,1174 @@ +package libnetwork_test + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "net" + "os" + "os/exec" + "runtime" + "strconv" + "strings" + "sync" + "testing" + + "github.com/docker/docker/libnetwork" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/testutils" + "github.com/docker/docker/libnetwork/types" + "github.com/docker/docker/pkg/reexec" + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" + "github.com/vishvananda/netns" +) + +const ( + bridgeNetType = "bridge" +) + +var ( + origins = netns.None() + testns = netns.None() +) + +func createGlobalInstance(t *testing.T) { + var err error + defer close(start) + + origins, err = netns.Get() + if err != nil { + t.Fatal(err) + } + + if testutils.IsRunningInContainer() { + testns = origins + } else { + testns, err = netns.New() + if err != nil { + t.Fatal(err) + } + } + + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "network", + }, + } + + net1, err := controller.NetworkByName("testhost") + if err != nil { + t.Fatal(err) + } + + net2, err := createTestNetwork("bridge", "network2", netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + + _, err = net1.CreateEndpoint("pep1") + if err != nil { + t.Fatal(err) + } + + _, err = net2.CreateEndpoint("pep2") + if err != nil { + t.Fatal(err) + } + + _, err = net2.CreateEndpoint("pep3") + if err != nil { + t.Fatal(err) + } + + if sboxes[first-1], err = controller.NewSandbox(fmt.Sprintf("%drace", first), libnetwork.OptionUseDefaultSandbox()); err != nil { + t.Fatal(err) + } + for thd := first + 1; thd <= last; thd++ { + if sboxes[thd-1], err = controller.NewSandbox(fmt.Sprintf("%drace", thd)); err != nil { + t.Fatal(err) + } + } +} + +func TestHost(t *testing.T) { + sbx1, err := controller.NewSandbox("host_c1", + libnetwork.OptionHostname("test1"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1"), + libnetwork.OptionUseDefaultSandbox()) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sbx1.Delete(); err != nil { + t.Fatal(err) + } + }() + + sbx2, err := controller.NewSandbox("host_c2", + libnetwork.OptionHostname("test2"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1"), + libnetwork.OptionUseDefaultSandbox()) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sbx2.Delete(); err != nil { + t.Fatal(err) + } + }() + + network, err := createTestNetwork("host", "testhost", options.Generic{}, nil, nil) + if err != nil { + t.Fatal(err) + } + + ep1, err := network.CreateEndpoint("testep1") + if err != nil { + t.Fatal(err) + } + + if err := ep1.Join(sbx1); err != nil { + t.Fatal(err) + } + + ep2, err := network.CreateEndpoint("testep2") + if err != nil { + t.Fatal(err) + } + + if err := ep2.Join(sbx2); err != nil { + t.Fatal(err) + } + + if err := ep1.Leave(sbx1); err != nil { + t.Fatal(err) + } + + if err := ep2.Leave(sbx2); err != nil { + t.Fatal(err) + } + + if err := ep1.Delete(false); err != nil { + t.Fatal(err) + } + + if err := ep2.Delete(false); err != nil { + t.Fatal(err) + } + + // Try to create another host endpoint and join/leave that. + cnt3, err := controller.NewSandbox("host_c3", + libnetwork.OptionHostname("test3"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1"), + libnetwork.OptionUseDefaultSandbox()) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := cnt3.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep3, err := network.CreateEndpoint("testep3") + if err != nil { + t.Fatal(err) + } + + if err := ep3.Join(sbx2); err != nil { + t.Fatal(err) + } + + if err := ep3.Leave(sbx2); err != nil { + t.Fatal(err) + } + + if err := ep3.Delete(false); err != nil { + t.Fatal(err) + } +} + +// Testing IPV6 from MAC address +func TestBridgeIpv6FromMac(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testipv6mac", + "EnableICC": true, + "EnableIPMasquerade": true, + }, + } + ipamV4ConfList := []*libnetwork.IpamConf{{PreferredPool: "192.168.100.0/24", Gateway: "192.168.100.1"}} + ipamV6ConfList := []*libnetwork.IpamConf{{PreferredPool: "fe90::/64", Gateway: "fe90::22"}} + + network, err := controller.NewNetwork(bridgeNetType, "testipv6mac", "", + libnetwork.NetworkOptionGeneric(netOption), + libnetwork.NetworkOptionEnableIPv6(true), + libnetwork.NetworkOptionIpam(ipamapi.DefaultIPAM, "", ipamV4ConfList, ipamV6ConfList, nil), + libnetwork.NetworkOptionDeferIPv6Alloc(true)) + if err != nil { + t.Fatal(err) + } + + mac := net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff} + epOption := options.Generic{netlabel.MacAddress: mac} + + ep, err := network.CreateEndpoint("testep", libnetwork.EndpointOptionGeneric(epOption)) + if err != nil { + t.Fatal(err) + } + + iface := ep.Info().Iface() + if !bytes.Equal(iface.MacAddress(), mac) { + t.Fatalf("Unexpected mac address: %v", iface.MacAddress()) + } + + ip, expIP, _ := net.ParseCIDR("fe90::aabb:ccdd:eeff/64") + expIP.IP = ip + if !types.CompareIPNet(expIP, iface.AddressIPv6()) { + t.Fatalf("Expected %v. Got: %v", expIP, iface.AddressIPv6()) + } + + if err := ep.Delete(false); err != nil { + t.Fatal(err) + } + + if err := network.Delete(); err != nil { + t.Fatal(err) + } +} + +func checkSandbox(t *testing.T, info libnetwork.EndpointInfo) { + key := info.Sandbox().Key() + sbNs, err := netns.GetFromPath(key) + if err != nil { + t.Fatalf("Failed to get network namespace path %q: %v", key, err) + } + defer sbNs.Close() + + nh, err := netlink.NewHandleAt(sbNs) + if err != nil { + t.Fatal(err) + } + + _, err = nh.LinkByName("eth0") + if err != nil { + t.Fatalf("Could not find the interface eth0 inside the sandbox: %v", err) + } + + _, err = nh.LinkByName("eth1") + if err != nil { + t.Fatalf("Could not find the interface eth1 inside the sandbox: %v", err) + } +} + +func TestEndpointJoin(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + // Create network 1 and add 2 endpoint: ep11, ep12 + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork1", + "EnableICC": true, + "EnableIPMasquerade": true, + }, + } + ipamV6ConfList := []*libnetwork.IpamConf{{PreferredPool: "fe90::/64", Gateway: "fe90::22"}} + n1, err := controller.NewNetwork(bridgeNetType, "testnetwork1", "", + libnetwork.NetworkOptionGeneric(netOption), + libnetwork.NetworkOptionEnableIPv6(true), + libnetwork.NetworkOptionIpam(ipamapi.DefaultIPAM, "", nil, ipamV6ConfList, nil), + libnetwork.NetworkOptionDeferIPv6Alloc(true)) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n1.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep1, err := n1.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := ep1.Delete(false); err != nil { + t.Fatal(err) + } + }() + + // Validate if ep.Info() only gives me IP address info and not names and gateway during CreateEndpoint() + info := ep1.Info() + iface := info.Iface() + if iface.Address() != nil && iface.Address().IP.To4() == nil { + t.Fatalf("Invalid IP address returned: %v", iface.Address()) + } + if iface.AddressIPv6() != nil && iface.AddressIPv6().IP == nil { + t.Fatalf("Invalid IPv6 address returned: %v", iface.Address()) + } + + if len(info.Gateway()) != 0 { + t.Fatalf("Expected empty gateway for an empty endpoint. Instead found a gateway: %v", info.Gateway()) + } + if len(info.GatewayIPv6()) != 0 { + t.Fatalf("Expected empty gateway for an empty ipv6 endpoint. Instead found a gateway: %v", info.GatewayIPv6()) + } + + if info.Sandbox() != nil { + t.Fatalf("Expected an empty sandbox key for an empty endpoint. Instead found a non-empty sandbox key: %s", info.Sandbox().Key()) + } + + // test invalid joins + err = ep1.Join(nil) + if err == nil { + t.Fatalf("Expected to fail join with nil Sandbox") + } + if _, ok := err.(types.BadRequestError); !ok { + t.Fatalf("Unexpected error type returned: %T", err) + } + + fsbx := &fakeSandbox{} + if err = ep1.Join(fsbx); err == nil { + t.Fatalf("Expected to fail join with invalid Sandbox") + } + if _, ok := err.(types.BadRequestError); !ok { + t.Fatalf("Unexpected error type returned: %T", err) + } + + sb, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + if err != nil { + t.Fatal(err) + } + + defer func() { + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep1.Join(sb) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep1.Leave(sb) + if err != nil { + t.Fatal(err) + } + }() + + // Validate if ep.Info() only gives valid gateway and sandbox key after has container has joined. + info = ep1.Info() + if len(info.Gateway()) == 0 { + t.Fatalf("Expected a valid gateway for a joined endpoint. Instead found an invalid gateway: %v", info.Gateway()) + } + if len(info.GatewayIPv6()) == 0 { + t.Fatalf("Expected a valid ipv6 gateway for a joined endpoint. Instead found an invalid gateway: %v", info.GatewayIPv6()) + } + + if info.Sandbox() == nil { + t.Fatalf("Expected an non-empty sandbox key for a joined endpoint. Instead found an empty sandbox key") + } + + // Check endpoint provided container information + if ep1.Info().Sandbox().Key() != sb.Key() { + t.Fatalf("Endpoint Info returned unexpected sandbox key: %s", sb.Key()) + } + + // Attempt retrieval of endpoint interfaces statistics + stats, err := sb.Statistics() + if err != nil { + t.Fatal(err) + } + if _, ok := stats["eth0"]; !ok { + t.Fatalf("Did not find eth0 statistics") + } + + // Now test the container joining another network + n2, err := createTestNetwork(bridgeNetType, "testnetwork2", + options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork2", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n2.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep2, err := n2.CreateEndpoint("ep2") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := ep2.Delete(false); err != nil { + t.Fatal(err) + } + }() + + err = ep2.Join(sb) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep2.Leave(sb) + if err != nil { + t.Fatal(err) + } + }() + + if ep1.Info().Sandbox().Key() != ep2.Info().Sandbox().Key() { + t.Fatalf("ep1 and ep2 returned different container sandbox key") + } + + checkSandbox(t, info) +} + +func TestExternalKey(t *testing.T) { + externalKeyTest(t, false) +} + +func externalKeyTest(t *testing.T, reexec bool) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + n2, err := createTestNetwork(bridgeNetType, "testnetwork2", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork2", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n2.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep.Delete(false) + if err != nil { + t.Fatal(err) + } + }() + + ep2, err := n2.CreateEndpoint("ep2") + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep2.Delete(false) + if err != nil { + t.Fatal(err) + } + }() + + cnt, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionUseExternalKey(), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + defer func() { + if err := cnt.Delete(); err != nil { + t.Fatal(err) + } + osl.GC() + }() + + // Join endpoint to sandbox before SetKey + err = ep.Join(cnt) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep.Leave(cnt) + if err != nil { + t.Fatal(err) + } + }() + + sbox := ep.Info().Sandbox() + if sbox == nil { + t.Fatalf("Expected to have a valid Sandbox") + } + + if reexec { + err := reexecSetKey("this-must-fail", containerID, controller.ID()) + if err == nil { + t.Fatalf("SetExternalKey must fail if the corresponding namespace is not created") + } + } else { + // Setting an non-existing key (namespace) must fail + if err := sbox.SetKey("this-must-fail"); err == nil { + t.Fatalf("Setkey must fail if the corresponding namespace is not created") + } + } + + // Create a new OS sandbox using the osl API before using it in SetKey + if extOsBox, err := osl.NewSandbox("ValidKey", true, false); err != nil { + t.Fatalf("Failed to create new osl sandbox") + } else { + defer func() { + if err := extOsBox.Destroy(); err != nil { + logrus.Warnf("Failed to remove os sandbox: %v", err) + } + }() + } + + if reexec { + err := reexecSetKey("ValidKey", containerID, controller.ID()) + if err != nil { + t.Fatalf("SetExternalKey failed with %v", err) + } + } else { + if err := sbox.SetKey("ValidKey"); err != nil { + t.Fatalf("Setkey failed with %v", err) + } + } + + // Join endpoint to sandbox after SetKey + err = ep2.Join(sbox) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep2.Leave(sbox) + if err != nil { + t.Fatal(err) + } + }() + + if ep.Info().Sandbox().Key() != ep2.Info().Sandbox().Key() { + t.Fatalf("ep1 and ep2 returned different container sandbox key") + } + + checkSandbox(t, ep.Info()) +} + +func reexecSetKey(key string, containerID string, controllerID string) error { + type libcontainerState struct { + NamespacePaths map[string]string + } + var ( + state libcontainerState + b []byte + err error + ) + + state.NamespacePaths = make(map[string]string) + state.NamespacePaths["NEWNET"] = key + if b, err = json.Marshal(state); err != nil { + return err + } + cmd := &exec.Cmd{ + Path: reexec.Self(), + Args: append([]string{"libnetwork-setkey"}, containerID, controllerID), + Stdin: strings.NewReader(string(b)), + Stdout: os.Stdout, + Stderr: os.Stderr, + } + return cmd.Run() +} + +func TestEnableIPv6(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n") + expectedResolvConf := []byte("search pommesfrites.fr\nnameserver 127.0.0.11\nnameserver 2001:4860:4860::8888\noptions ndots:0\n") + //take a copy of resolv.conf for restoring after test completes + resolvConfSystem, err := os.ReadFile("/etc/resolv.conf") + if err != nil { + t.Fatal(err) + } + //cleanup + defer func() { + if err := os.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil { + t.Fatal(err) + } + }() + + netOption := options.Generic{ + netlabel.EnableIPv6: true, + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + } + ipamV6ConfList := []*libnetwork.IpamConf{{PreferredPool: "fe99::/64", Gateway: "fe99::9"}} + + n, err := createTestNetwork("bridge", "testnetwork", netOption, nil, ipamV6ConfList) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep1, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + + if err := os.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil { + t.Fatal(err) + } + + resolvConfPath := "/tmp/libnetwork_test/resolv.conf" + defer os.Remove(resolvConfPath) + + sb, err := controller.NewSandbox(containerID, libnetwork.OptionResolvConfPath(resolvConfPath)) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep1.Join(sb) + if err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(content, expectedResolvConf) { + t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf), string(content)) + } + + if err != nil { + t.Fatal(err) + } +} + +func TestResolvConfHost(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + tmpResolvConf := []byte("search localhost.net\nnameserver 127.0.0.1\nnameserver 2001:4860:4860::8888\n") + + //take a copy of resolv.conf for restoring after test completes + resolvConfSystem, err := os.ReadFile("/etc/resolv.conf") + if err != nil { + t.Fatal(err) + } + //cleanup + defer func() { + if err := os.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil { + t.Fatal(err) + } + }() + + n, err := controller.NetworkByName("testhost") + if err != nil { + t.Fatal(err) + } + + ep1, err := n.CreateEndpoint("ep1", libnetwork.CreateOptionDisableResolution()) + if err != nil { + t.Fatal(err) + } + + if err := os.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil { + t.Fatal(err) + } + + resolvConfPath := "/tmp/libnetwork_test/resolv.conf" + defer os.Remove(resolvConfPath) + + sb, err := controller.NewSandbox(containerID, + libnetwork.OptionUseDefaultSandbox(), + libnetwork.OptionResolvConfPath(resolvConfPath), + libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep1.Join(sb) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep1.Leave(sb) + if err != nil { + t.Fatal(err) + } + }() + + finfo, err := os.Stat(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + fmode := (os.FileMode)(0644) + if finfo.Mode() != fmode { + t.Fatalf("Expected file mode %s, got %s", fmode.String(), finfo.Mode().String()) + } + + content, err := os.ReadFile(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(content, tmpResolvConf) { + t.Fatalf("Expected:\n%s\nGot:\n%s", string(tmpResolvConf), string(content)) + } +} + +func TestResolvConf(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + tmpResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n") + tmpResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\nnameserver 2001:4860:4860::8888\n") + expectedResolvConf1 := []byte("search pommesfrites.fr\nnameserver 127.0.0.11\noptions ndots:0\n") + tmpResolvConf3 := []byte("search pommesfrites.fr\nnameserver 113.34.56.78\n") + + //take a copy of resolv.conf for restoring after test completes + resolvConfSystem, err := os.ReadFile("/etc/resolv.conf") + if err != nil { + t.Fatal(err) + } + //cleanup + defer func() { + if err := os.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil { + t.Fatal(err) + } + }() + + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + } + n, err := createTestNetwork("bridge", "testnetwork", netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := n.CreateEndpoint("ep") + if err != nil { + t.Fatal(err) + } + + if err := os.WriteFile("/etc/resolv.conf", tmpResolvConf1, 0644); err != nil { + t.Fatal(err) + } + + resolvConfPath := "/tmp/libnetwork_test/resolv.conf" + defer os.Remove(resolvConfPath) + + sb1, err := controller.NewSandbox(containerID, libnetwork.OptionResolvConfPath(resolvConfPath)) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sb1.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep.Join(sb1) + if err != nil { + t.Fatal(err) + } + + finfo, err := os.Stat(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + fmode := (os.FileMode)(0644) + if finfo.Mode() != fmode { + t.Fatalf("Expected file mode %s, got %s", fmode.String(), finfo.Mode().String()) + } + + content, err := os.ReadFile(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(content, expectedResolvConf1) { + fmt.Printf("\n%v\n%v\n", expectedResolvConf1, content) + t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf1), string(content)) + } + + err = ep.Leave(sb1) + if err != nil { + t.Fatal(err) + } + + if err := os.WriteFile("/etc/resolv.conf", tmpResolvConf2, 0644); err != nil { + t.Fatal(err) + } + + sb2, err := controller.NewSandbox(containerID+"_2", libnetwork.OptionResolvConfPath(resolvConfPath)) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sb2.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep.Join(sb2) + if err != nil { + t.Fatal(err) + } + + content, err = os.ReadFile(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(content, expectedResolvConf1) { + t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf1), string(content)) + } + + if err := os.WriteFile(resolvConfPath, tmpResolvConf3, 0644); err != nil { + t.Fatal(err) + } + + err = ep.Leave(sb2) + if err != nil { + t.Fatal(err) + } + + err = ep.Join(sb2) + if err != nil { + t.Fatal(err) + } + + content, err = os.ReadFile(resolvConfPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(content, tmpResolvConf3) { + t.Fatalf("Expected:\n%s\nGot:\n%s", string(tmpResolvConf3), string(content)) + } +} + +func parallelJoin(t *testing.T, rc libnetwork.Sandbox, ep libnetwork.Endpoint, thrNumber int) { + debugf("J%d.", thrNumber) + var err error + + sb := sboxes[thrNumber-1] + err = ep.Join(sb) + + runtime.LockOSThread() + if err != nil { + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("thread %d: %v", thrNumber, err) + } + debugf("JE%d(%v).", thrNumber, err) + } + debugf("JD%d.", thrNumber) +} + +func parallelLeave(t *testing.T, rc libnetwork.Sandbox, ep libnetwork.Endpoint, thrNumber int) { + debugf("L%d.", thrNumber) + var err error + + sb := sboxes[thrNumber-1] + + err = ep.Leave(sb) + runtime.LockOSThread() + if err != nil { + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("thread %d: %v", thrNumber, err) + } + debugf("LE%d(%v).", thrNumber, err) + } + debugf("LD%d.", thrNumber) +} + +func runParallelTests(t *testing.T, thrNumber int) { + var ( + ep libnetwork.Endpoint + sb libnetwork.Sandbox + err error + ) + + t.Parallel() + + pTest := flag.Lookup("test.parallel") + if pTest == nil { + t.Skip("Skipped because test.parallel flag not set;") + } + numParallel, err := strconv.Atoi(pTest.Value.String()) + if err != nil { + t.Fatal(err) + } + if numParallel < numThreads { + t.Skip("Skipped because t.parallel was less than ", numThreads) + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if thrNumber == first { + createGlobalInstance(t) + } + + if thrNumber != first { + <-start + + thrdone := make(chan struct{}) + done <- thrdone + defer close(thrdone) + + if thrNumber == last { + defer close(done) + } + + err = netns.Set(testns) + if err != nil { + t.Fatal(err) + } + } + defer func() { + if err := netns.Set(origins); err != nil { + // NOTE(@cpuguy83): This... + // I touched this code because the linter found that we weren't checking the error... + // It returns an error because "origins" is a closed file handle *unless* createGlobalInstance is called. + // Which... this test is run in parallel and `createGlobalInstance` modifies `origins` without synchronization. + // I'm not sure what exactly the *intent* of this code was, but it looks very broken. + // Anyway that's why I'm only logging the error and not failing the test. + t.Log(err) + } + }() + + net1, err := controller.NetworkByName("testhost") + if err != nil { + t.Fatal(err) + } + if net1 == nil { + t.Fatal("Could not find testhost") + } + + net2, err := controller.NetworkByName("network2") + if err != nil { + t.Fatal(err) + } + if net2 == nil { + t.Fatal("Could not find network2") + } + + epName := fmt.Sprintf("pep%d", thrNumber) + + if thrNumber == first { + ep, err = net1.EndpointByName(epName) + } else { + ep, err = net2.EndpointByName(epName) + } + + if err != nil { + t.Fatal(err) + } + if ep == nil { + t.Fatal("Got nil ep with no error") + } + + cid := fmt.Sprintf("%drace", thrNumber) + controller.WalkSandboxes(libnetwork.SandboxContainerWalker(&sb, cid)) + if sb == nil { + t.Fatalf("Got nil sandbox for container: %s", cid) + } + + for i := 0; i < iterCnt; i++ { + parallelJoin(t, sb, ep, thrNumber) + parallelLeave(t, sb, ep, thrNumber) + } + + debugf("\n") + + err = sb.Delete() + if err != nil { + t.Fatal(err) + } + if thrNumber == first { + for thrdone := range done { + <-thrdone + } + + testns.Close() + if err := net2.Delete(); err != nil { + t.Fatal(err) + } + } else { + err = ep.Delete(false) + if err != nil { + t.Fatal(err) + } + } +} + +func TestParallel1(t *testing.T) { + runParallelTests(t, 1) +} + +func TestParallel2(t *testing.T) { + runParallelTests(t, 2) +} + +func TestBridge(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + netOption := options.Generic{ + netlabel.EnableIPv6: true, + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + "EnableICC": true, + "EnableIPMasquerade": true, + }, + } + ipamV4ConfList := []*libnetwork.IpamConf{{PreferredPool: "192.168.100.0/24", Gateway: "192.168.100.1"}} + ipamV6ConfList := []*libnetwork.IpamConf{{PreferredPool: "fe90::/64", Gateway: "fe90::22"}} + + network, err := createTestNetwork(bridgeNetType, "testnetwork", netOption, ipamV4ConfList, ipamV6ConfList) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := network.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := network.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + sb, err := controller.NewSandbox(containerID, libnetwork.OptionPortMapping(getPortMapping())) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep.Join(sb) + if err != nil { + t.Fatal(err) + } + + epInfo, err := ep.DriverInfo() + if err != nil { + t.Fatal(err) + } + pmd, ok := epInfo[netlabel.PortMap] + if !ok { + t.Fatalf("Could not find expected info in endpoint data") + } + pm, ok := pmd.([]types.PortBinding) + if !ok { + t.Fatalf("Unexpected format for port mapping in endpoint operational data") + } + expectedLen := 10 + if !isV6Listenable() { + expectedLen = 5 + } + if len(pm) != expectedLen { + t.Fatalf("Incomplete data for port mapping in endpoint operational data: %d", len(pm)) + } +} + +var ( + v6ListenableCached bool + v6ListenableOnce sync.Once +) + +// This is copied from the bridge driver package b/c the bridge driver is not platform agnostic. +func isV6Listenable() bool { + v6ListenableOnce.Do(func() { + ln, err := net.Listen("tcp6", "[::1]:0") + if err != nil { + // When the kernel was booted with `ipv6.disable=1`, + // we get err "listen tcp6 [::1]:0: socket: address family not supported by protocol" + // https://github.com/moby/moby/issues/42288 + logrus.Debugf("port_mapping: v6Listenable=false (%v)", err) + } else { + v6ListenableCached = true + ln.Close() + } + }) + return v6ListenableCached +} + +func TestParallel3(t *testing.T) { + runParallelTests(t, 3) +} + +func TestNullIpam(t *testing.T) { + _, err := controller.NewNetwork(bridgeNetType, "testnetworkinternal", "", libnetwork.NetworkOptionIpam(ipamapi.NullIPAM, "", nil, nil, nil)) + if err == nil || err.Error() != "ipv4 pool is empty" { + t.Fatal("bridge network should complain empty pool") + } +} diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go new file mode 100644 index 0000000000000..e64f957d748fd --- /dev/null +++ b/libnetwork/libnetwork_test.go @@ -0,0 +1,1366 @@ +//go:build linux +// +build linux + +package libnetwork_test + +import ( + "errors" + "fmt" + "net" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/docker/docker/libnetwork" + "github.com/docker/docker/libnetwork/config" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/testutils" + "github.com/docker/docker/libnetwork/types" + "github.com/docker/docker/pkg/plugins" + "github.com/docker/docker/pkg/reexec" + "github.com/sirupsen/logrus" +) + +var controller libnetwork.NetworkController + +func TestMain(m *testing.M) { + if runtime.GOOS == "windows" { + logrus.Info("Test suite does not currently support windows") + os.Exit(0) + } + if reexec.Init() { + return + } + + if err := createController(); err != nil { + logrus.Errorf("Error creating controller: %v", err) + os.Exit(1) + } + + x := m.Run() + controller.Stop() + os.Exit(x) +} + +func createController() error { + var err error + + // Cleanup local datastore file + os.Remove(datastore.DefaultScopes("")[datastore.LocalScope].Client.Address) + + option := options.Generic{ + "EnableIPForwarding": true, + } + + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = option + + cfgOptions, err := libnetwork.OptionBoltdbWithRandomDBFile() + if err != nil { + return err + } + controller, err = libnetwork.New(append(cfgOptions, config.OptionDriverConfig(bridgeNetType, genericOption))...) + return err +} + +func createTestNetwork(networkType, networkName string, netOption options.Generic, ipamV4Configs, ipamV6Configs []*libnetwork.IpamConf) (libnetwork.Network, error) { + return controller.NewNetwork(networkType, networkName, "", + libnetwork.NetworkOptionGeneric(netOption), + libnetwork.NetworkOptionIpam(ipamapi.DefaultIPAM, "", ipamV4Configs, ipamV6Configs, nil)) +} + +func getEmptyGenericOption() map[string]interface{} { + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = map[string]string{} + return genericOption +} + +func getPortMapping() []types.PortBinding { + return []types.PortBinding{ + {Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)}, + {Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)}, + {Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)}, + {Proto: types.TCP, Port: uint16(320), HostPort: uint16(32000), HostPortEnd: uint16(32999)}, + {Proto: types.UDP, Port: uint16(420), HostPort: uint16(42000), HostPortEnd: uint16(42001)}, + } +} + +func isNotFound(err error) bool { + _, ok := (err).(types.NotFoundError) + return ok +} + +func TestNull(t *testing.T) { + cnt, err := controller.NewSandbox("null_container", + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + if err != nil { + t.Fatal(err) + } + + network, err := createTestNetwork("null", "testnull", options.Generic{}, nil, nil) + if err != nil { + t.Fatal(err) + } + + ep, err := network.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + err = ep.Join(cnt) + if err != nil { + t.Fatal(err) + } + + err = ep.Leave(cnt) + if err != nil { + t.Fatal(err) + } + + if err := ep.Delete(false); err != nil { + t.Fatal(err) + } + + if err := cnt.Delete(); err != nil { + t.Fatal(err) + } + + // host type is special network. Cannot be removed. + err = network.Delete() + if err == nil { + t.Fatal(err) + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Unexpected error type") + } +} + +func TestUnknownDriver(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + _, err := createTestNetwork("unknowndriver", "testnetwork", options.Generic{}, nil, nil) + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if !isNotFound(err) { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestNilRemoteDriver(t *testing.T) { + _, err := controller.NewNetwork("framerelay", "dummy", "", + libnetwork.NetworkOptionGeneric(getEmptyGenericOption())) + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if !isNotFound(err) { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestNetworkName(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + } + + _, err := createTestNetwork(bridgeNetType, "", netOption, nil, nil) + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(libnetwork.ErrInvalidName); !ok { + t.Fatalf("Expected to fail with ErrInvalidName error. Got %v", err) + } + + networkName := "testnetwork" + n, err := createTestNetwork(bridgeNetType, networkName, netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + if n.Name() != networkName { + t.Fatalf("Expected network name %s, got %s", networkName, n.Name()) + } +} + +func TestNetworkType(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + if n.Type() != bridgeNetType { + t.Fatalf("Expected network type %s, got %s", bridgeNetType, n.Type()) + } +} + +func TestNetworkID(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + if n.ID() == "" { + t.Fatal("Expected non-empty network id") + } +} + +func TestDeleteNetworkWithActiveEndpoints(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + netOption := options.Generic{ + "BridgeName": "testnetwork", + } + option := options.Generic{ + netlabel.GenericData: netOption, + } + + network, err := createTestNetwork(bridgeNetType, "testnetwork", option, nil, nil) + if err != nil { + t.Fatal(err) + } + + ep, err := network.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + err = network.Delete() + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(*libnetwork.ActiveEndpointsError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } + + // Done testing. Now cleanup. + if err := ep.Delete(false); err != nil { + t.Fatal(err) + } + + if err := network.Delete(); err != nil { + t.Fatal(err) + } +} + +func TestNetworkConfig(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + // Verify config network cannot inherit another config network + _, err := controller.NewNetwork("bridge", "config_network0", "", + libnetwork.NetworkOptionConfigOnly(), + libnetwork.NetworkOptionConfigFrom("anotherConfigNw")) + + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } + + // Create supported config network + netOption := options.Generic{ + "EnableICC": false, + } + option := options.Generic{ + netlabel.GenericData: netOption, + } + ipamV4ConfList := []*libnetwork.IpamConf{{PreferredPool: "192.168.100.0/24", SubPool: "192.168.100.128/25", Gateway: "192.168.100.1"}} + ipamV6ConfList := []*libnetwork.IpamConf{{PreferredPool: "2001:db8:abcd::/64", SubPool: "2001:db8:abcd::ef99/80", Gateway: "2001:db8:abcd::22"}} + + netOptions := []libnetwork.NetworkOption{ + libnetwork.NetworkOptionConfigOnly(), + libnetwork.NetworkOptionEnableIPv6(true), + libnetwork.NetworkOptionGeneric(option), + libnetwork.NetworkOptionIpam("default", "", ipamV4ConfList, ipamV6ConfList, nil), + } + + configNetwork, err := controller.NewNetwork(bridgeNetType, "config_network0", "", netOptions...) + if err != nil { + t.Fatal(err) + } + + // Verify a config-only network cannot be created with network operator configurations + for i, opt := range []libnetwork.NetworkOption{ + libnetwork.NetworkOptionInternalNetwork(), + libnetwork.NetworkOptionAttachable(true), + libnetwork.NetworkOptionIngress(true), + } { + _, err = controller.NewNetwork(bridgeNetType, "testBR", "", + libnetwork.NetworkOptionConfigOnly(), opt) + if err == nil { + t.Fatalf("Expected to fail. But instead succeeded for option: %d", i) + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } + } + + // Verify a network cannot be created with both config-from and network specific configurations + for i, opt := range []libnetwork.NetworkOption{ + libnetwork.NetworkOptionEnableIPv6(true), + libnetwork.NetworkOptionIpam("my-ipam", "", nil, nil, nil), + libnetwork.NetworkOptionIpam("", "", ipamV4ConfList, nil, nil), + libnetwork.NetworkOptionIpam("", "", nil, ipamV6ConfList, nil), + libnetwork.NetworkOptionLabels(map[string]string{"number": "two"}), + libnetwork.NetworkOptionDriverOpts(map[string]string{"com.docker.network.driver.mtu": "1600"}), + } { + _, err = controller.NewNetwork(bridgeNetType, "testBR", "", + libnetwork.NetworkOptionConfigFrom("config_network0"), opt) + if err == nil { + t.Fatalf("Expected to fail. But instead succeeded for option: %d", i) + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } + } + + // Create a valid network + network, err := controller.NewNetwork(bridgeNetType, "testBR", "", + libnetwork.NetworkOptionConfigFrom("config_network0")) + if err != nil { + t.Fatal(err) + } + + // Verify the config network cannot be removed + err = configNetwork.Delete() + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } + + // Delete network + if err := network.Delete(); err != nil { + t.Fatal(err) + } + + // Verify the config network can now be removed + if err := configNetwork.Delete(); err != nil { + t.Fatal(err) + } + +} + +func TestUnknownNetwork(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + netOption := options.Generic{ + "BridgeName": "testnetwork", + } + option := options.Generic{ + netlabel.GenericData: netOption, + } + + network, err := createTestNetwork(bridgeNetType, "testnetwork", option, nil, nil) + if err != nil { + t.Fatal(err) + } + + err = network.Delete() + if err != nil { + t.Fatal(err) + } + + err = network.Delete() + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(*libnetwork.UnknownNetworkError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestUnknownEndpoint(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + netOption := options.Generic{ + "BridgeName": "testnetwork", + } + option := options.Generic{ + netlabel.GenericData: netOption, + } + ipamV4ConfList := []*libnetwork.IpamConf{{PreferredPool: "192.168.100.0/24"}} + + network, err := createTestNetwork(bridgeNetType, "testnetwork", option, ipamV4ConfList, nil) + if err != nil { + t.Fatal(err) + } + + _, err = network.CreateEndpoint("") + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + if _, ok := err.(libnetwork.ErrInvalidName); !ok { + t.Fatalf("Expected to fail with ErrInvalidName error. Actual error: %v", err) + } + + ep, err := network.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + err = ep.Delete(false) + if err != nil { + t.Fatal(err) + } + + // Done testing. Now cleanup + if err := network.Delete(); err != nil { + t.Fatal(err) + } +} + +func TestNetworkEndpointsWalkers(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + // Create network 1 and add 2 endpoint: ep11, ep12 + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "network1", + }, + } + + net1, err := createTestNetwork(bridgeNetType, "network1", netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := net1.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep11, err := net1.CreateEndpoint("ep11") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := ep11.Delete(false); err != nil { + t.Fatal(err) + } + }() + + ep12, err := net1.CreateEndpoint("ep12") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := ep12.Delete(false); err != nil { + t.Fatal(err) + } + }() + + // Test list methods on net1 + epList1 := net1.Endpoints() + if len(epList1) != 2 { + t.Fatalf("Endpoints() returned wrong number of elements: %d instead of 2", len(epList1)) + } + // endpoint order is not guaranteed + for _, e := range epList1 { + if e != ep11 && e != ep12 { + t.Fatal("Endpoints() did not return all the expected elements") + } + } + + // Test Endpoint Walk method + var epName string + var epWanted libnetwork.Endpoint + wlk := func(ep libnetwork.Endpoint) bool { + if ep.Name() == epName { + epWanted = ep + return true + } + return false + } + + // Look for ep1 on network1 + epName = "ep11" + net1.WalkEndpoints(wlk) + if epWanted == nil { + t.Fatal(err) + } + if ep11 != epWanted { + t.Fatal(err) + } + + current := len(controller.Networks()) + + // Create network 2 + netOption = options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "network2", + }, + } + + net2, err := createTestNetwork(bridgeNetType, "network2", netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := net2.Delete(); err != nil { + t.Fatal(err) + } + }() + + // Test Networks method + if len(controller.Networks()) != current+1 { + t.Fatalf("Did not find the expected number of networks") + } + + // Test Network Walk method + var netName string + var netWanted libnetwork.Network + nwWlk := func(nw libnetwork.Network) bool { + if nw.Name() == netName { + netWanted = nw + return true + } + return false + } + + // Look for network named "network1" and "network2" + netName = "network1" + controller.WalkNetworks(nwWlk) + if netWanted == nil { + t.Fatal(err) + } + if net1.ID() != netWanted.ID() { + t.Fatal(err) + } + + netName = "network2" + controller.WalkNetworks(nwWlk) + if netWanted == nil { + t.Fatal(err) + } + if net2.ID() != netWanted.ID() { + t.Fatal(err) + } +} + +func TestDuplicateEndpoint(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + } + n, err := createTestNetwork(bridgeNetType, "testnetwork", netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := ep.Delete(false); err != nil { + t.Fatal(err) + } + }() + + ep2, err := n.CreateEndpoint("ep1") + defer func() { + // Cleanup ep2 as well, else network cleanup might fail for failure cases + if ep2 != nil { + if err := ep2.Delete(false); err != nil { + t.Fatal(err) + } + } + }() + + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestControllerQuery(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + // Create network 1 + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "network1", + }, + } + net1, err := createTestNetwork(bridgeNetType, "network1", netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := net1.Delete(); err != nil { + t.Fatal(err) + } + }() + + // Create network 2 + netOption = options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "network2", + }, + } + net2, err := createTestNetwork(bridgeNetType, "network2", netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := net2.Delete(); err != nil { + t.Fatal(err) + } + }() + + _, err = controller.NetworkByName("") + if err == nil { + t.Fatalf("NetworkByName() succeeded with invalid target name") + } + if _, ok := err.(libnetwork.ErrInvalidName); !ok { + t.Fatalf("Expected NetworkByName() to fail with ErrInvalidName error. Got: %v", err) + } + + _, err = controller.NetworkByID("") + if err == nil { + t.Fatalf("NetworkByID() succeeded with invalid target id") + } + if _, ok := err.(libnetwork.ErrInvalidID); !ok { + t.Fatalf("NetworkByID() failed with unexpected error: %v", err) + } + + g, err := controller.NetworkByID("network1") + if err == nil { + t.Fatalf("Unexpected success for NetworkByID(): %v", g) + } + if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok { + t.Fatalf("NetworkByID() failed with unexpected error: %v", err) + } + + g, err = controller.NetworkByName("network1") + if err != nil { + t.Fatalf("Unexpected failure for NetworkByName(): %v", err) + } + if g == nil { + t.Fatalf("NetworkByName() did not find the network") + } + + if g != net1 { + t.Fatalf("NetworkByName() returned the wrong network") + } + + g, err = controller.NetworkByID(net1.ID()) + if err != nil { + t.Fatalf("Unexpected failure for NetworkByID(): %v", err) + } + if net1.ID() != g.ID() { + t.Fatalf("NetworkByID() returned unexpected element: %v", g) + } + + g, err = controller.NetworkByName("network2") + if err != nil { + t.Fatalf("Unexpected failure for NetworkByName(): %v", err) + } + if g == nil { + t.Fatalf("NetworkByName() did not find the network") + } + + if g != net2 { + t.Fatalf("NetworkByName() returned the wrong network") + } + + g, err = controller.NetworkByID(net2.ID()) + if err != nil { + t.Fatalf("Unexpected failure for NetworkByID(): %v", err) + } + if net2.ID() != g.ID() { + t.Fatalf("NetworkByID() returned unexpected element: %v", g) + } +} + +func TestNetworkQuery(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + // Create network 1 and add 2 endpoint: ep11, ep12 + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "network1", + }, + } + net1, err := createTestNetwork(bridgeNetType, "network1", netOption, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := net1.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep11, err := net1.CreateEndpoint("ep11") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := ep11.Delete(false); err != nil { + t.Fatal(err) + } + }() + + ep12, err := net1.CreateEndpoint("ep12") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := ep12.Delete(false); err != nil { + t.Fatal(err) + } + }() + + e, err := net1.EndpointByName("ep11") + if err != nil { + t.Fatal(err) + } + if ep11 != e { + t.Fatalf("EndpointByName() returned %v instead of %v", e, ep11) + } + + _, err = net1.EndpointByName("") + if err == nil { + t.Fatalf("EndpointByName() succeeded with invalid target name") + } + if _, ok := err.(libnetwork.ErrInvalidName); !ok { + t.Fatalf("Expected EndpointByName() to fail with ErrInvalidName error. Got: %v", err) + } + + e, err = net1.EndpointByName("IamNotAnEndpoint") + if err == nil { + t.Fatalf("EndpointByName() succeeded with unknown target name") + } + if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok { + t.Fatal(err) + } + if e != nil { + t.Fatalf("EndpointByName(): expected nil, got %v", e) + } + + e, err = net1.EndpointByID(ep12.ID()) + if err != nil { + t.Fatal(err) + } + if ep12.ID() != e.ID() { + t.Fatalf("EndpointByID() returned %v instead of %v", e, ep12) + } + + _, err = net1.EndpointByID("") + if err == nil { + t.Fatalf("EndpointByID() succeeded with invalid target id") + } + if _, ok := err.(libnetwork.ErrInvalidID); !ok { + t.Fatalf("EndpointByID() failed with unexpected error: %v", err) + } +} + +const containerID = "valid_c" + +type fakeSandbox struct{} + +func (f *fakeSandbox) ID() string { + return "fake sandbox" +} + +func (f *fakeSandbox) ContainerID() string { + return "" +} + +func (f *fakeSandbox) Key() string { + return "fake key" +} + +func (f *fakeSandbox) Labels() map[string]interface{} { + return nil +} + +func (f *fakeSandbox) Statistics() (map[string]*types.InterfaceStatistics, error) { + return nil, nil +} + +func (f *fakeSandbox) Refresh(opts ...libnetwork.SandboxOption) error { + return nil +} + +func (f *fakeSandbox) Delete() error { + return nil +} + +func (f *fakeSandbox) Rename(name string) error { + return nil +} + +func (f *fakeSandbox) SetKey(key string) error { + return nil +} + +func (f *fakeSandbox) ResolveName(name string, ipType int) ([]net.IP, bool) { + return nil, false +} + +func (f *fakeSandbox) ResolveIP(ip string) string { + return "" +} + +func (f *fakeSandbox) ResolveService(name string) ([]*net.SRV, []net.IP) { + return nil, nil +} + +func (f *fakeSandbox) Endpoints() []libnetwork.Endpoint { + return nil +} + +func (f *fakeSandbox) EnableService() error { + return nil +} + +func (f *fakeSandbox) DisableService() error { + return nil +} + +func TestEndpointDeleteWithActiveContainer(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + n2, err := createTestNetwork(bridgeNetType, "testnetwork2", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork2", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n2.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep.Delete(false) + if err != nil { + t.Fatal(err) + } + }() + + cnt, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + defer func() { + if err := cnt.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep.Join(cnt) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep.Leave(cnt) + if err != nil { + t.Fatal(err) + } + }() + + err = ep.Delete(false) + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if _, ok := err.(*libnetwork.ActiveContainerError); !ok { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestEndpointMultipleJoins(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testmultiple", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testmultiple", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := ep.Delete(false); err != nil { + t.Fatal(err) + } + }() + + sbx1, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + defer func() { + if err := sbx1.Delete(); err != nil { + t.Fatal(err) + } + }() + + sbx2, err := controller.NewSandbox("c2") + defer func() { + if err := sbx2.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep.Join(sbx1) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep.Leave(sbx1) + if err != nil { + t.Fatal(err) + } + }() + + err = ep.Join(sbx2) + if err == nil { + t.Fatal("Expected to fail multiple joins for the same endpoint") + } + + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Failed with unexpected error type: %T. Desc: %s", err, err.Error()) + } + +} + +func TestLeaveAll(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + // If this goes through, it means cnt.Delete() effectively detached from all the endpoints + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + n2, err := createTestNetwork(bridgeNetType, "testnetwork2", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork2", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n2.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep1, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + + ep2, err := n2.CreateEndpoint("ep2") + if err != nil { + t.Fatal(err) + } + + cnt, err := controller.NewSandbox("leaveall") + if err != nil { + t.Fatal(err) + } + + err = ep1.Join(cnt) + if err != nil { + t.Fatalf("Failed to join ep1: %v", err) + } + + err = ep2.Join(cnt) + if err != nil { + t.Fatalf("Failed to join ep2: %v", err) + } + + err = cnt.Delete() + if err != nil { + t.Fatal(err) + } +} + +func TestContainerInvalidLeave(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := ep.Delete(false); err != nil { + t.Fatal(err) + } + }() + + cnt, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := cnt.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep.Leave(cnt) + if err == nil { + t.Fatal("Expected to fail leave from an endpoint which has no active join") + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Failed with unexpected error type: %T. Desc: %s", err, err.Error()) + } + + if err = ep.Leave(nil); err == nil { + t.Fatalf("Expected to fail leave nil Sandbox") + } + if _, ok := err.(types.BadRequestError); !ok { + t.Fatalf("Unexpected error type returned: %T. Desc: %s", err, err.Error()) + } + + fsbx := &fakeSandbox{} + if err = ep.Leave(fsbx); err == nil { + t.Fatalf("Expected to fail leave with invalid Sandbox") + } + if _, ok := err.(types.BadRequestError); !ok { + t.Fatalf("Unexpected error type returned: %T. Desc: %s", err, err.Error()) + } +} + +func TestEndpointUpdateParent(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": "testnetwork", + }, + }, nil, nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep1, err := n.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + + ep2, err := n.CreateEndpoint("ep2") + if err != nil { + t.Fatal(err) + } + + sbx1, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sbx1.Delete(); err != nil { + t.Fatal(err) + } + }() + + sbx2, err := controller.NewSandbox("c2", + libnetwork.OptionHostname("test2"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionHostsPath("/var/lib/docker/test_network/container2/hosts"), + libnetwork.OptionExtraHost("web", "192.168.0.2")) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sbx2.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep1.Join(sbx1) + if err != nil { + t.Fatal(err) + } + + err = ep2.Join(sbx2) + if err != nil { + t.Fatal(err) + } +} + +func TestInvalidRemoteDriver(t *testing.T) { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + if server == nil { + t.Fatal("Failed to start an HTTP Server") + } + defer server.Close() + + mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintln(w, `{"Implements": ["InvalidDriver"]}`) + }) + + if err := os.MkdirAll(specPath, 0755); err != nil { + t.Fatal(err) + } + defer func() { + if err := os.RemoveAll(specPath); err != nil { + t.Fatal(err) + } + }() + + if err := os.WriteFile(filepath.Join(specPath, "invalid-network-driver.spec"), []byte(server.URL), 0644); err != nil { + t.Fatal(err) + } + + ctrlr, err := libnetwork.New() + if err != nil { + t.Fatal(err) + } + defer ctrlr.Stop() + + _, err = ctrlr.NewNetwork("invalid-network-driver", "dummy", "", + libnetwork.NetworkOptionGeneric(getEmptyGenericOption())) + if err == nil { + t.Fatal("Expected to fail. But instead succeeded") + } + + if !errors.Is(err, plugins.ErrNotImplements) { + t.Fatalf("Did not fail with expected error. Actual error: %v", err) + } +} + +func TestValidRemoteDriver(t *testing.T) { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + if server == nil { + t.Fatal("Failed to start an HTTP Server") + } + defer server.Close() + + mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType) + }) + mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, `{"Scope":"local"}`) + }) + mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, "null") + }) + mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, "null") + }) + + if err := os.MkdirAll(specPath, 0755); err != nil { + t.Fatal(err) + } + defer func() { + if err := os.RemoveAll(specPath); err != nil { + t.Fatal(err) + } + }() + + if err := os.WriteFile(filepath.Join(specPath, "valid-network-driver.spec"), []byte(server.URL), 0644); err != nil { + t.Fatal(err) + } + + n, err := controller.NewNetwork("valid-network-driver", "dummy", "", + libnetwork.NetworkOptionGeneric(getEmptyGenericOption())) + if err != nil { + // Only fail if we could not find the plugin driver + if isNotFound(err) { + t.Fatal(err) + } + return + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() +} + +var ( + start = make(chan struct{}) + done = make(chan chan struct{}, numThreads-1) + sboxes = make([]libnetwork.Sandbox, numThreads) +) + +const ( + iterCnt = 25 + numThreads = 3 + first = 1 + last = numThreads + debug = false +) + +func debugf(format string, a ...interface{}) { + if debug { + fmt.Printf(format, a...) + } +} diff --git a/libnetwork/libnetwork_unix_test.go b/libnetwork/libnetwork_unix_test.go new file mode 100644 index 0000000000000..e039fa9065613 --- /dev/null +++ b/libnetwork/libnetwork_unix_test.go @@ -0,0 +1,6 @@ +//go:build !windows +// +build !windows + +package libnetwork_test + +var specPath = "/etc/docker/plugins" diff --git a/libnetwork/libnetwork_windows_test.go b/libnetwork/libnetwork_windows_test.go new file mode 100644 index 0000000000000..b1a1fa667bc7a --- /dev/null +++ b/libnetwork/libnetwork_windows_test.go @@ -0,0 +1,12 @@ +package libnetwork_test + +import ( + "os" + "path/filepath" +) + +const bridgeNetType = "nat" + +var ( + specPath = filepath.Join(os.Getenv("programdata"), "docker", "plugins") +) diff --git a/libnetwork/netlabel/labels.go b/libnetwork/netlabel/labels.go new file mode 100644 index 0000000000000..f5075a6c34909 --- /dev/null +++ b/libnetwork/netlabel/labels.go @@ -0,0 +1,132 @@ +package netlabel + +import ( + "strings" +) + +const ( + // Prefix constant marks the reserved label space for libnetwork + Prefix = "com.docker.network" + + // DriverPrefix constant marks the reserved label space for libnetwork drivers + DriverPrefix = Prefix + ".driver" + + // DriverPrivatePrefix constant marks the reserved label space + // for internal libnetwork drivers + DriverPrivatePrefix = DriverPrefix + ".private" + + // GenericData constant that helps to identify an option as a Generic constant + GenericData = Prefix + ".generic" + + // PortMap constant represents Port Mapping + PortMap = Prefix + ".portmap" + + // MacAddress constant represents Mac Address config of a Container + MacAddress = Prefix + ".endpoint.macaddress" + + // ExposedPorts constant represents the container's Exposed Ports + ExposedPorts = Prefix + ".endpoint.exposedports" + + // DNSServers A list of DNS servers associated with the endpoint + DNSServers = Prefix + ".endpoint.dnsservers" + + //EnableIPv6 constant represents enabling IPV6 at network level + EnableIPv6 = Prefix + ".enable_ipv6" + + // DriverMTU constant represents the MTU size for the network driver + DriverMTU = DriverPrefix + ".mtu" + + // OverlayBindInterface constant represents overlay driver bind interface + OverlayBindInterface = DriverPrefix + ".overlay.bind_interface" + + // OverlayNeighborIP constant represents overlay driver neighbor IP + OverlayNeighborIP = DriverPrefix + ".overlay.neighbor_ip" + + // OverlayVxlanIDList constant represents a list of VXLAN Ids as csv + OverlayVxlanIDList = DriverPrefix + ".overlay.vxlanid_list" + + // Gateway represents the gateway for the network + Gateway = Prefix + ".gateway" + + // Internal constant represents that the network is internal which disables default gateway service + Internal = Prefix + ".internal" + + // ContainerIfacePrefix can be used to override the interface prefix used inside the container + ContainerIfacePrefix = Prefix + ".container_iface_prefix" + + // HostIP is the Source-IP Address used to SNAT container traffic + HostIP = Prefix + ".host_ipv4" +) + +var ( + // GlobalKVProvider constant represents the KV provider backend + GlobalKVProvider = MakeKVProvider("global") + + // GlobalKVProviderURL constant represents the KV provider URL + GlobalKVProviderURL = MakeKVProviderURL("global") + + // GlobalKVProviderConfig constant represents the KV provider Config + GlobalKVProviderConfig = MakeKVProviderConfig("global") + + // GlobalKVClient constants represents the global kv store client + GlobalKVClient = MakeKVClient("global") + + // LocalKVProvider constant represents the KV provider backend + LocalKVProvider = MakeKVProvider("local") + + // LocalKVProviderURL constant represents the KV provider URL + LocalKVProviderURL = MakeKVProviderURL("local") + + // LocalKVProviderConfig constant represents the KV provider Config + LocalKVProviderConfig = MakeKVProviderConfig("local") + + // LocalKVClient constants represents the local kv store client + LocalKVClient = MakeKVClient("local") +) + +// MakeKVProvider returns the kvprovider label for the scope +func MakeKVProvider(scope string) string { + return DriverPrivatePrefix + scope + "kv_provider" +} + +// MakeKVProviderURL returns the kvprovider url label for the scope +func MakeKVProviderURL(scope string) string { + return DriverPrivatePrefix + scope + "kv_provider_url" +} + +// MakeKVProviderConfig returns the kvprovider config label for the scope +func MakeKVProviderConfig(scope string) string { + return DriverPrivatePrefix + scope + "kv_provider_config" +} + +// MakeKVClient returns the kv client label for the scope +func MakeKVClient(scope string) string { + return DriverPrivatePrefix + scope + "kv_client" +} + +// Key extracts the key portion of the label +func Key(label string) (key string) { + if kv := strings.SplitN(label, "=", 2); len(kv) > 0 { + key = kv[0] + } + return +} + +// Value extracts the value portion of the label +func Value(label string) (value string) { + if kv := strings.SplitN(label, "=", 2); len(kv) > 1 { + value = kv[1] + } + return +} + +// KeyValue decomposes the label in the (key,value) pair +func KeyValue(label string) (key string, value string) { + if kv := strings.SplitN(label, "=", 2); len(kv) > 0 { + key = kv[0] + if len(kv) > 1 { + value = kv[1] + } + } + return +} diff --git a/libnetwork/netlabel/labels_test.go b/libnetwork/netlabel/labels_test.go new file mode 100644 index 0000000000000..01a660bc73bb6 --- /dev/null +++ b/libnetwork/netlabel/labels_test.go @@ -0,0 +1,28 @@ +package netlabel + +import ( + "testing" +) + +var input = []struct { + label string + key string + value string +}{ + {"com.directory.person.name=joe", "com.directory.person.name", "joe"}, + {"com.directory.person.age=24", "com.directory.person.age", "24"}, + {"com.directory.person.address=1234 First st.", "com.directory.person.address", "1234 First st."}, + {"com.directory.person.friends=", "com.directory.person.friends", ""}, + {"com.directory.person.nickname=o=u=8", "com.directory.person.nickname", "o=u=8"}, + {"", "", ""}, + {"com.directory.person.student", "com.directory.person.student", ""}, +} + +func TestKeyValue(t *testing.T) { + for _, i := range input { + k, v := KeyValue(i.label) + if k != i.key || v != i.value { + t.Fatalf("unexpected: %s, %s", k, v) + } + } +} diff --git a/libnetwork/netutils/utils.go b/libnetwork/netutils/utils.go new file mode 100644 index 0000000000000..76b2478cb7b13 --- /dev/null +++ b/libnetwork/netutils/utils.go @@ -0,0 +1,191 @@ +// Network utility functions. + +package netutils + +import ( + "crypto/rand" + "encoding/hex" + "errors" + "fmt" + "io" + "net" + "strings" + + "github.com/docker/docker/libnetwork/types" +) + +var ( + // ErrNetworkOverlapsWithNameservers preformatted error + ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver") + // ErrNetworkOverlaps preformatted error + ErrNetworkOverlaps = errors.New("requested network overlaps with existing network") +) + +// CheckNameserverOverlaps checks whether the passed network overlaps with any of the nameservers +func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error { + if len(nameservers) > 0 { + for _, ns := range nameservers { + _, nsNetwork, err := net.ParseCIDR(ns) + if err != nil { + return err + } + if NetworkOverlaps(toCheck, nsNetwork) { + return ErrNetworkOverlapsWithNameservers + } + } + } + return nil +} + +// NetworkOverlaps detects overlap between one IPNet and another +func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { + return netX.Contains(netY.IP) || netY.Contains(netX.IP) +} + +// NetworkRange calculates the first and last IP addresses in an IPNet +func NetworkRange(network *net.IPNet) (net.IP, net.IP) { + if network == nil { + return nil, nil + } + + firstIP := network.IP.Mask(network.Mask) + lastIP := types.GetIPCopy(firstIP) + for i := 0; i < len(firstIP); i++ { + lastIP[i] = firstIP[i] | ^network.Mask[i] + } + + if network.IP.To4() != nil { + firstIP = firstIP.To4() + lastIP = lastIP.To4() + } + + return firstIP, lastIP +} + +// GetIfaceAddr returns the first IPv4 address and slice of IPv6 addresses for the specified network interface +func GetIfaceAddr(name string) (net.Addr, []net.Addr, error) { + iface, err := net.InterfaceByName(name) + if err != nil { + return nil, nil, err + } + addrs, err := iface.Addrs() + if err != nil { + return nil, nil, err + } + var addrs4, addrs6 []net.Addr + for _, addr := range addrs { + ip := (addr.(*net.IPNet)).IP + if ip4 := ip.To4(); ip4 != nil { + addrs4 = append(addrs4, addr) + } else if ip6 := ip.To16(); len(ip6) == net.IPv6len { + addrs6 = append(addrs6, addr) + } + } + switch { + case len(addrs4) == 0: + return nil, nil, fmt.Errorf("interface %v has no IPv4 addresses", name) + case len(addrs4) > 1: + fmt.Printf("Interface %v has more than 1 IPv4 address. Defaulting to using %v\n", + name, (addrs4[0].(*net.IPNet)).IP) + } + return addrs4[0], addrs6, nil +} + +func genMAC(ip net.IP) net.HardwareAddr { + hw := make(net.HardwareAddr, 6) + // The first byte of the MAC address has to comply with these rules: + // 1. Unicast: Set the least-significant bit to 0. + // 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1. + hw[0] = 0x02 + // The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI). + // Since this address is locally administered, we can do whatever we want as long as + // it doesn't conflict with other addresses. + hw[1] = 0x42 + // Fill the remaining 4 bytes based on the input + if ip == nil { + rand.Read(hw[2:]) + } else { + copy(hw[2:], ip.To4()) + } + return hw +} + +// GenerateRandomMAC returns a new 6-byte(48-bit) hardware address (MAC) +func GenerateRandomMAC() net.HardwareAddr { + return genMAC(nil) +} + +// GenerateMACFromIP returns a locally administered MAC address where the 4 least +// significant bytes are derived from the IPv4 address. +func GenerateMACFromIP(ip net.IP) net.HardwareAddr { + return genMAC(ip) +} + +// GenerateRandomName returns a new name joined with a prefix. This size +// specified is used to truncate the randomly generated value +func GenerateRandomName(prefix string, size int) (string, error) { + id := make([]byte, 32) + if _, err := io.ReadFull(rand.Reader, id); err != nil { + return "", err + } + return prefix + hex.EncodeToString(id)[:size], nil +} + +// ReverseIP accepts a V4 or V6 IP string in the canonical form and returns a reversed IP in +// the dotted decimal form . This is used to setup the IP to service name mapping in the optimal +// way for the DNS PTR queries. +func ReverseIP(IP string) string { + var reverseIP []string + + if net.ParseIP(IP).To4() != nil { + reverseIP = strings.Split(IP, ".") + l := len(reverseIP) + for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 { + reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i] + } + } else { + reverseIP = strings.Split(IP, ":") + + // Reversed IPv6 is represented in dotted decimal instead of the typical + // colon hex notation + for key := range reverseIP { + if len(reverseIP[key]) == 0 { // expand the compressed 0s + reverseIP[key] = strings.Repeat("0000", 8-strings.Count(IP, ":")) + } else if len(reverseIP[key]) < 4 { // 0-padding needed + reverseIP[key] = strings.Repeat("0", 4-len(reverseIP[key])) + reverseIP[key] + } + } + + reverseIP = strings.Split(strings.Join(reverseIP, ""), "") + + l := len(reverseIP) + for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 { + reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i] + } + } + + return strings.Join(reverseIP, ".") +} + +// ParseAlias parses and validates the specified string as an alias format (name:alias) +func ParseAlias(val string) (string, string, error) { + if val == "" { + return "", "", errors.New("empty string specified for alias") + } + arr := strings.SplitN(val, ":", 3) + if len(arr) > 2 { + return "", "", errors.New("bad format for alias: " + val) + } + if len(arr) == 1 { + return val, val, nil + } + return arr[0], arr[1], nil +} + +// ValidateAlias validates that the specified string has a valid alias format (containerName:alias). +func ValidateAlias(val string) (string, error) { + if _, _, err := ParseAlias(val); err != nil { + return val, err + } + return val, nil +} diff --git a/vendor/github.com/docker/libnetwork/netutils/utils_freebsd.go b/libnetwork/netutils/utils_freebsd.go similarity index 95% rename from vendor/github.com/docker/libnetwork/netutils/utils_freebsd.go rename to libnetwork/netutils/utils_freebsd.go index 02bcd32aa8e23..b703d73b17340 100644 --- a/vendor/github.com/docker/libnetwork/netutils/utils_freebsd.go +++ b/libnetwork/netutils/utils_freebsd.go @@ -3,7 +3,7 @@ package netutils import ( "net" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/types" ) // ElectInterfaceAddresses looks for an interface on the OS with the specified name diff --git a/libnetwork/netutils/utils_linux.go b/libnetwork/netutils/utils_linux.go new file mode 100644 index 0000000000000..a418c64044401 --- /dev/null +++ b/libnetwork/netutils/utils_linux.go @@ -0,0 +1,127 @@ +//go:build linux +// +build linux + +// Network utility functions. + +package netutils + +import ( + "fmt" + "net" + "strings" + + "github.com/docker/docker/libnetwork/ipamutils" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/resolvconf" + "github.com/docker/docker/libnetwork/types" + "github.com/pkg/errors" + "github.com/vishvananda/netlink" +) + +var ( + networkGetRoutesFct func(netlink.Link, int) ([]netlink.Route, error) +) + +// CheckRouteOverlaps checks whether the passed network overlaps with any existing routes +func CheckRouteOverlaps(toCheck *net.IPNet) error { + if networkGetRoutesFct == nil { + networkGetRoutesFct = ns.NlHandle().RouteList + } + networks, err := networkGetRoutesFct(nil, netlink.FAMILY_V4) + if err != nil { + return err + } + for _, network := range networks { + if network.Dst != nil && network.Scope == netlink.SCOPE_LINK && NetworkOverlaps(toCheck, network.Dst) { + return ErrNetworkOverlaps + } + } + return nil +} + +// GenerateIfaceName returns an interface name using the passed in +// prefix and the length of random bytes. The api ensures that the +// there are is no interface which exists with that name. +func GenerateIfaceName(nlh *netlink.Handle, prefix string, len int) (string, error) { + linkByName := netlink.LinkByName + if nlh != nil { + linkByName = nlh.LinkByName + } + for i := 0; i < 3; i++ { + name, err := GenerateRandomName(prefix, len) + if err != nil { + continue + } + _, err = linkByName(name) + if err != nil { + if strings.Contains(err.Error(), "not found") { + return name, nil + } + return "", err + } + } + return "", types.InternalErrorf("could not generate interface name") +} + +// ElectInterfaceAddresses looks for an interface on the OS with the +// specified name and returns returns all its IPv4 and IPv6 addresses in CIDR notation. +// If a failure in retrieving the addresses or no IPv4 address is found, an error is returned. +// If the interface does not exist, it chooses from a predefined +// list the first IPv4 address which does not conflict with other +// interfaces on the system. +func ElectInterfaceAddresses(name string) ([]*net.IPNet, []*net.IPNet, error) { + var v4Nets, v6Nets []*net.IPNet + + defer osl.InitOSContext()() + + link, _ := ns.NlHandle().LinkByName(name) + if link != nil { + v4addr, err := ns.NlHandle().AddrList(link, netlink.FAMILY_V4) + if err != nil { + return nil, nil, err + } + v6addr, err := ns.NlHandle().AddrList(link, netlink.FAMILY_V6) + if err != nil { + return nil, nil, err + } + for _, nlAddr := range v4addr { + v4Nets = append(v4Nets, nlAddr.IPNet) + } + for _, nlAddr := range v6addr { + v6Nets = append(v6Nets, nlAddr.IPNet) + } + } + + if link == nil || len(v4Nets) == 0 { + // Choose from predefined local scope networks + v4Net, err := FindAvailableNetwork(ipamutils.PredefinedLocalScopeDefaultNetworks) + if err != nil { + return nil, nil, errors.Wrapf(err, "PredefinedLocalScopeDefaultNetworks List: %+v", + ipamutils.PredefinedLocalScopeDefaultNetworks) + } + v4Nets = append(v4Nets, v4Net) + } + + return v4Nets, v6Nets, nil +} + +// FindAvailableNetwork returns a network from the passed list which does not +// overlap with existing interfaces in the system +func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) { + // We don't check for an error here, because we don't really care if we + // can't read /etc/resolv.conf. So instead we skip the append if resolvConf + // is nil. It either doesn't exist, or we can't read it for some reason. + var nameservers []string + if rc, err := resolvconf.Get(); err == nil { + nameservers = resolvconf.GetNameserversAsCIDR(rc.Content) + } + for _, nw := range list { + if err := CheckNameserverOverlaps(nameservers, nw); err == nil { + if err := CheckRouteOverlaps(nw); err == nil { + return nw, nil + } + } + } + return nil, fmt.Errorf("no available network") +} diff --git a/libnetwork/netutils/utils_linux_test.go b/libnetwork/netutils/utils_linux_test.go new file mode 100644 index 0000000000000..ae9c42bc8a042 --- /dev/null +++ b/libnetwork/netutils/utils_linux_test.go @@ -0,0 +1,360 @@ +package netutils + +import ( + "bytes" + "net" + "sort" + "testing" + + "github.com/docker/docker/libnetwork/ipamutils" + "github.com/docker/docker/libnetwork/testutils" + "github.com/docker/docker/libnetwork/types" + "github.com/vishvananda/netlink" +) + +func TestNonOverlappingNameservers(t *testing.T) { + network := &net.IPNet{ + IP: []byte{192, 168, 0, 1}, + Mask: []byte{255, 255, 255, 0}, + } + nameservers := []string{ + "127.0.0.1/32", + } + + if err := CheckNameserverOverlaps(nameservers, network); err != nil { + t.Fatal(err) + } +} + +func TestOverlappingNameservers(t *testing.T) { + network := &net.IPNet{ + IP: []byte{192, 168, 0, 1}, + Mask: []byte{255, 255, 255, 0}, + } + nameservers := []string{ + "192.168.0.1/32", + } + + if err := CheckNameserverOverlaps(nameservers, network); err == nil { + t.Fatalf("Expected error %s got %s", ErrNetworkOverlapsWithNameservers, err) + } +} + +func TestCheckRouteOverlaps(t *testing.T) { + networkGetRoutesFct = func(netlink.Link, int) ([]netlink.Route, error) { + routesData := []string{"10.0.2.0/32", "10.0.3.0/24", "10.0.42.0/24", "172.16.42.0/24", "192.168.142.0/24"} + routes := []netlink.Route{} + for _, addr := range routesData { + _, netX, _ := net.ParseCIDR(addr) + routes = append(routes, netlink.Route{Dst: netX, Scope: netlink.SCOPE_LINK}) + } + // Add a route with a scope which should not overlap + _, netX, _ := net.ParseCIDR("10.0.5.0/24") + routes = append(routes, netlink.Route{Dst: netX, Scope: netlink.SCOPE_UNIVERSE}) + return routes, nil + } + defer func() { networkGetRoutesFct = nil }() + + _, netX, _ := net.ParseCIDR("172.16.0.1/24") + if err := CheckRouteOverlaps(netX); err != nil { + t.Fatal(err) + } + + _, netX, _ = net.ParseCIDR("10.0.2.0/24") + if err := CheckRouteOverlaps(netX); err == nil { + t.Fatal("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't") + } + + _, netX, _ = net.ParseCIDR("10.0.5.0/24") + if err := CheckRouteOverlaps(netX); err != nil { + t.Fatal("10.0.5.0/24 and 10.0.5.0 with scope UNIVERSE should not overlap but it does") + } +} + +func TestCheckNameserverOverlaps(t *testing.T) { + nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"} + + _, netX, _ := net.ParseCIDR("10.0.2.3/32") + + if err := CheckNameserverOverlaps(nameservers, netX); err == nil { + t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX) + } + + _, netX, _ = net.ParseCIDR("192.168.102.2/32") + + if err := CheckNameserverOverlaps(nameservers, netX); err != nil { + t.Fatalf("%s should not overlap %v but it does", netX, nameservers) + } +} + +func AssertOverlap(CIDRx string, CIDRy string, t *testing.T) { + _, netX, _ := net.ParseCIDR(CIDRx) + _, netY, _ := net.ParseCIDR(CIDRy) + if !NetworkOverlaps(netX, netY) { + t.Errorf("%v and %v should overlap", netX, netY) + } +} + +func AssertNoOverlap(CIDRx string, CIDRy string, t *testing.T) { + _, netX, _ := net.ParseCIDR(CIDRx) + _, netY, _ := net.ParseCIDR(CIDRy) + if NetworkOverlaps(netX, netY) { + t.Errorf("%v and %v should not overlap", netX, netY) + } +} + +func TestNetworkOverlaps(t *testing.T) { + //netY starts at same IP and ends within netX + AssertOverlap("172.16.0.1/24", "172.16.0.1/25", t) + //netY starts within netX and ends at same IP + AssertOverlap("172.16.0.1/24", "172.16.0.128/25", t) + //netY starts and ends within netX + AssertOverlap("172.16.0.1/24", "172.16.0.64/25", t) + //netY starts at same IP and ends outside of netX + AssertOverlap("172.16.0.1/24", "172.16.0.1/23", t) + //netY starts before and ends at same IP of netX + AssertOverlap("172.16.1.1/24", "172.16.0.1/23", t) + //netY starts before and ends outside of netX + AssertOverlap("172.16.1.1/24", "172.16.0.1/22", t) + //netY starts and ends before netX + AssertNoOverlap("172.16.1.1/25", "172.16.0.1/24", t) + //netX starts and ends before netY + AssertNoOverlap("172.16.1.1/25", "172.16.2.1/24", t) +} + +func TestNetworkRange(t *testing.T) { + // Simple class C test + _, network, _ := net.ParseCIDR("192.168.0.1/24") + first, last := NetworkRange(network) + if !first.Equal(net.ParseIP("192.168.0.0")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("192.168.0.255")) { + t.Error(last.String()) + } + + // Class A test + _, network, _ = net.ParseCIDR("10.0.0.1/8") + first, last = NetworkRange(network) + if !first.Equal(net.ParseIP("10.0.0.0")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("10.255.255.255")) { + t.Error(last.String()) + } + + // Class A, random IP address + _, network, _ = net.ParseCIDR("10.1.2.3/8") + first, last = NetworkRange(network) + if !first.Equal(net.ParseIP("10.0.0.0")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("10.255.255.255")) { + t.Error(last.String()) + } + + // 32bit mask + _, network, _ = net.ParseCIDR("10.1.2.3/32") + first, last = NetworkRange(network) + if !first.Equal(net.ParseIP("10.1.2.3")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("10.1.2.3")) { + t.Error(last.String()) + } + + // 31bit mask + _, network, _ = net.ParseCIDR("10.1.2.3/31") + first, last = NetworkRange(network) + if !first.Equal(net.ParseIP("10.1.2.2")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("10.1.2.3")) { + t.Error(last.String()) + } + + // 26bit mask + _, network, _ = net.ParseCIDR("10.1.2.3/26") + first, last = NetworkRange(network) + if !first.Equal(net.ParseIP("10.1.2.0")) { + t.Error(first.String()) + } + if !last.Equal(net.ParseIP("10.1.2.63")) { + t.Error(last.String()) + } +} + +// Test veth name generation "veth"+rand (e.g.veth0f60e2c) +func TestGenerateRandomName(t *testing.T) { + name1, err := GenerateRandomName("veth", 7) + if err != nil { + t.Fatal(err) + } + // veth plus generated append equals a len of 11 + if len(name1) != 11 { + t.Fatalf("Expected 11 characters, instead received %d characters", len(name1)) + } + name2, err := GenerateRandomName("veth", 7) + if err != nil { + t.Fatal(err) + } + // Fail if the random generated names equal one another + if name1 == name2 { + t.Fatalf("Expected differing values but received %s and %s", name1, name2) + } +} + +// Test mac generation. +func TestUtilGenerateRandomMAC(t *testing.T) { + mac1 := GenerateRandomMAC() + mac2 := GenerateRandomMAC() + // ensure bytes are unique + if bytes.Equal(mac1, mac2) { + t.Fatalf("mac1 %s should not equal mac2 %s", mac1, mac2) + } + // existing tests check string functionality so keeping the pattern + if mac1.String() == mac2.String() { + t.Fatalf("mac1 %s should not equal mac2 %s", mac1, mac2) + } +} + +func TestNetworkRequest(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + nw, err := FindAvailableNetwork(ipamutils.PredefinedLocalScopeDefaultNetworks) + if err != nil { + t.Fatal(err) + } + + var found bool + for _, exp := range ipamutils.PredefinedLocalScopeDefaultNetworks { + if types.CompareIPNet(exp, nw) { + found = true + break + } + } + + if !found { + t.Fatalf("Found unexpected broad network %s", nw) + } + + nw, err = FindAvailableNetwork(ipamutils.PredefinedGlobalScopeDefaultNetworks) + if err != nil { + t.Fatal(err) + } + + found = false + for _, exp := range ipamutils.PredefinedGlobalScopeDefaultNetworks { + if types.CompareIPNet(exp, nw) { + found = true + break + } + } + + if !found { + t.Fatalf("Found unexpected granular network %s", nw) + } + + // Add iface and ssert returned address on request + createInterface(t, "test", "172.17.42.1/16") + + _, exp, err := net.ParseCIDR("172.18.0.0/16") + if err != nil { + t.Fatal(err) + } + nw, err = FindAvailableNetwork(ipamutils.PredefinedLocalScopeDefaultNetworks) + if err != nil { + t.Fatal(err) + } + if !types.CompareIPNet(exp, nw) { + t.Fatalf("expected %s. got %s", exp, nw) + } +} + +func TestElectInterfaceAddressMultipleAddresses(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + nws := []string{"172.101.202.254/16", "172.102.202.254/16"} + createInterface(t, "test", nws...) + + ipv4NwList, ipv6NwList, err := ElectInterfaceAddresses("test") + if err != nil { + t.Fatal(err) + } + + if len(ipv4NwList) == 0 { + t.Fatal("unexpected empty ipv4 network addresses") + } + + if len(ipv6NwList) == 0 { + t.Fatal("unexpected empty ipv6 network addresses") + } + + nwList := []string{} + for _, ipv4Nw := range ipv4NwList { + nwList = append(nwList, ipv4Nw.String()) + } + sort.Strings(nws) + sort.Strings(nwList) + + if len(nws) != len(nwList) { + t.Fatalf("expected %v. got %v", nws, nwList) + } + for i, nw := range nws { + if nw != nwList[i] { + t.Fatalf("expected %v. got %v", nw, nwList[i]) + } + } +} + +func TestElectInterfaceAddress(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + nws := "172.101.202.254/16" + createInterface(t, "test", nws) + + ipv4Nw, ipv6Nw, err := ElectInterfaceAddresses("test") + if err != nil { + t.Fatal(err) + } + + if len(ipv4Nw) == 0 { + t.Fatal("unexpected empty ipv4 network addresses") + } + + if len(ipv6Nw) == 0 { + t.Fatal("unexpected empty ipv6 network addresses") + } + + if nws != ipv4Nw[0].String() { + t.Fatalf("expected %s. got %s", nws, ipv4Nw[0]) + } +} + +func createInterface(t *testing.T, name string, nws ...string) { + // Add interface + link := &netlink.Bridge{ + LinkAttrs: netlink.LinkAttrs{ + Name: "test", + }, + } + bips := []*net.IPNet{} + for _, nw := range nws { + bip, err := types.ParseCIDR(nw) + if err != nil { + t.Fatal(err) + } + bips = append(bips, bip) + } + if err := netlink.LinkAdd(link); err != nil { + t.Fatalf("Failed to create interface via netlink: %v", err) + } + for _, bip := range bips { + if err := netlink.AddrAdd(link, &netlink.Addr{IPNet: bip}); err != nil { + t.Fatal(err) + } + } + if err := netlink.LinkSetUp(link); err != nil { + t.Fatal(err) + } +} diff --git a/libnetwork/netutils/utils_windows.go b/libnetwork/netutils/utils_windows.go new file mode 100644 index 0000000000000..0773799029bd1 --- /dev/null +++ b/libnetwork/netutils/utils_windows.go @@ -0,0 +1,25 @@ +package netutils + +import ( + "net" + + "github.com/docker/docker/libnetwork/types" +) + +// ElectInterfaceAddresses looks for an interface on the OS with the specified name +// and returns returns all its IPv4 and IPv6 addresses in CIDR notation. +// If a failure in retrieving the addresses or no IPv4 address is found, an error is returned. +// If the interface does not exist, it chooses from a predefined +// list the first IPv4 address which does not conflict with other +// interfaces on the system. +func ElectInterfaceAddresses(name string) ([]*net.IPNet, []*net.IPNet, error) { + return nil, nil, types.NotImplementedErrorf("not supported on windows") +} + +// FindAvailableNetwork returns a network from the passed list which does not +// overlap with existing interfaces in the system + +// TODO : Use appropriate windows APIs to identify non-overlapping subnets +func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) { + return nil, nil +} diff --git a/vendor/github.com/docker/libnetwork/network.go b/libnetwork/network.go similarity index 92% rename from vendor/github.com/docker/libnetwork/network.go rename to libnetwork/network.go index 0a4a2277b018f..207138f674adb 100644 --- a/vendor/github.com/docker/libnetwork/network.go +++ b/libnetwork/network.go @@ -8,34 +8,34 @@ import ( "sync" "time" + "github.com/docker/docker/libnetwork/config" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/etchosts" + "github.com/docker/docker/libnetwork/internal/setmatrix" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/netutils" + "github.com/docker/docker/libnetwork/networkdb" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/pkg/stringid" - "github.com/docker/libnetwork/config" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/etchosts" - "github.com/docker/libnetwork/internal/setmatrix" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/networkdb" - "github.com/docker/libnetwork/options" - "github.com/docker/libnetwork/types" "github.com/sirupsen/logrus" ) // A Network represents a logical connectivity zone that containers may // join using the Link method. A Network is managed by a specific driver. type Network interface { - // A user chosen name for this network. + // Name returns a user chosen name for this network. Name() string - // A system generated id for this network. + // ID returns a system generated id for this network. ID() string - // The type of network, which corresponds to its managing driver. + // Type returns the type of network, which corresponds to its managing driver. Type() string - // Create a new endpoint to this network symbolically identified by the + // CreateEndpoint creates a new endpoint to this network symbolically identified by the // specified unique name. The options parameter carries driver specific options. CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) @@ -45,7 +45,7 @@ type Network interface { // Endpoints returns the list of Endpoint(s) in this network. Endpoints() []Endpoint - // WalkEndpoints uses the provided function to walk the Endpoints + // WalkEndpoints uses the provided function to walk the Endpoints. WalkEndpoints(walker EndpointWalker) // EndpointByName returns the Endpoint which has the passed name. If not found, the error ErrNoSuchEndpoint is returned. @@ -54,7 +54,7 @@ type Network interface { // EndpointByID returns the Endpoint which has the passed id. If not found, the error ErrNoSuchEndpoint is returned. EndpointByID(id string) (Endpoint, error) - // Return certain operational data belonging to this network + // Info returns certain operational data belonging to this network. Info() NetworkInfo } @@ -78,8 +78,8 @@ type NetworkInfo interface { // gossip cluster. For non-dynamic overlay networks and bridge networks it returns an // empty slice Peers() []networkdb.PeerInfo - //Services returns a map of services keyed by the service name with the details - //of all the tasks that belong to the service. Applicable only in swarm mode. + // Services returns a map of services keyed by the service name with the details + // of all the tasks that belong to the service. Applicable only in swarm mode. Services() map[string]ServiceInfo } @@ -89,7 +89,7 @@ type EndpointWalker func(ep Endpoint) bool // ipInfo is the reverse mapping from IP to service name to serve the PTR query. // extResolver is set if an external server resolves a service name to this IP. -// Its an indication to defer PTR queries also to that external server. +// It's an indication to defer PTR queries also to that external server. type ipInfo struct { name string serviceID string @@ -130,15 +130,15 @@ type networkDBTable struct { // IpamConf contains all the ipam related configurations for a network type IpamConf struct { - // The master address pool for containers and network interfaces + // PreferredPool is the master address pool for containers and network interfaces. PreferredPool string - // A subset of the master pool. If specified, - // this becomes the container pool + // SubPool is a subset of the master pool. If specified, + // this becomes the container pool. SubPool string - // Preferred Network Gateway address (optional) + // Gateway is the preferred Network Gateway address (optional). Gateway string - // Auxiliary addresses for network driver. Must be within the master pool. - // libnetwork will reserve them if they fall into the container pool + // AuxAddresses contains auxiliary addresses for network driver. Must be within the master pool. + // libnetwork will reserve them if they fall into the container pool. AuxAddresses map[string]string } @@ -220,7 +220,6 @@ type network struct { dbIndex uint64 dbExists bool persist bool - stopWatchCh chan struct{} drvOnce *sync.Once resolverOnce sync.Once resolver []Resolver @@ -396,11 +395,9 @@ func (n *network) validateConfiguration() error { driverOptions map[string]string opts interface{} ) - switch data.(type) { - case map[string]interface{}: - opts = data.(map[string]interface{}) - case map[string]string: - opts = data.(map[string]string) + switch t := data.(type) { + case map[string]interface{}, map[string]string: + opts = t } ba, err := json.Marshal(opts) if err != nil { @@ -418,7 +415,7 @@ func (n *network) validateConfiguration() error { return nil } -// Applies network specific configurations +// applyConfigurationTo applies network specific configurations. func (n *network) applyConfigurationTo(to *network) error { to.enableIPv6 = n.enableIPv6 if len(n.labels) > 0 { @@ -501,25 +498,33 @@ func (n *network) CopyTo(o datastore.KVObject) error { for _, v4conf := range n.ipamV4Config { dstV4Conf := &IpamConf{} - v4conf.CopyTo(dstV4Conf) + if err := v4conf.CopyTo(dstV4Conf); err != nil { + return err + } dstN.ipamV4Config = append(dstN.ipamV4Config, dstV4Conf) } for _, v4info := range n.ipamV4Info { dstV4Info := &IpamInfo{} - v4info.CopyTo(dstV4Info) + if err := v4info.CopyTo(dstV4Info); err != nil { + return err + } dstN.ipamV4Info = append(dstN.ipamV4Info, dstV4Info) } for _, v6conf := range n.ipamV6Config { dstV6Conf := &IpamConf{} - v6conf.CopyTo(dstV6Conf) + if err := v6conf.CopyTo(dstV6Conf); err != nil { + return err + } dstN.ipamV6Config = append(dstN.ipamV6Config, dstV6Conf) } for _, v6info := range n.ipamV6Info { dstV6Info := &IpamInfo{} - v6info.CopyTo(dstV6Info) + if err := v6info.CopyTo(dstV6Info); err != nil { + return err + } dstN.ipamV6Info = append(dstN.ipamV6Info, dstV6Info) } @@ -935,16 +940,6 @@ func (n *network) resolveDriver(name string, load bool) (driverapi.Driver, *driv return d, cap, nil } -func (n *network) driverScope() string { - _, cap, err := n.resolveDriver(n.networkType, true) - if err != nil { - // If driver could not be resolved simply return an empty string - return "" - } - - return cap.DataScope -} - func (n *network) driverIsMultihost() bool { _, cap, err := n.resolveDriver(n.networkType, true) if err != nil { @@ -997,7 +992,7 @@ func (n *network) delete(force bool, rmLBEndpoint bool) error { n.Unlock() c.networkLocker.Lock(id) - defer c.networkLocker.Unlock(id) + defer c.networkLocker.Unlock(id) // nolint:errcheck n, err := c.getNetworkFromStore(id) if err != nil { @@ -1056,7 +1051,7 @@ func (n *network) delete(force bool, rmLBEndpoint bool) error { t.Name(), n.Name(), err) } } else { - logrus.Warnf("Could not find configuration network %q during removal of network %q", n.configOnly, n.Name()) + logrus.Warnf("Could not find configuration network %q during removal of network %q", n.configFrom, n.Name()) } } @@ -1090,6 +1085,10 @@ func (n *network) delete(force bool, rmLBEndpoint bool) error { // Cleanup the service discovery for this network c.cleanupServiceDiscovery(n.ID()) + // Cleanup the load balancer. On Windows this call is required + // to remove remote loadbalancers in VFP. + c.cleanupServiceBindings(n.ID()) + removeFromStore: // deleteFromStore performs an atomic delete operation and the // network.epCnt will help prevent any possible @@ -1161,7 +1160,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi } n.ctrlr.networkLocker.Lock(n.id) - defer n.ctrlr.networkLocker.Unlock(n.id) + defer n.ctrlr.networkLocker.Unlock(n.id) // nolint:errcheck return n.createEndpoint(name, options...) @@ -1179,7 +1178,8 @@ func (n *network) createEndpoint(name string, options ...EndpointOption) (Endpoi ep.locator = n.getController().clusterHostID() ep.network, err = ep.getNetworkFromStore() if err != nil { - return nil, fmt.Errorf("failed to get network during CreateEndpoint: %v", err) + logrus.Errorf("failed to get network during CreateEndpoint: %v", err) + return nil, err } n = ep.network @@ -1197,12 +1197,12 @@ func (n *network) createEndpoint(name string, options ...EndpointOption) (Endpoi } } - ipam, cap, err := n.getController().getIPAMDriver(n.ipamType) + ipam, capability, err := n.getController().getIPAMDriver(n.ipamType) if err != nil { return nil, err } - if cap.RequiresMACAddress { + if capability.RequiresMACAddress { if ep.iface.mac == nil { ep.iface.mac = netutils.GenerateRandomMAC() } @@ -1327,7 +1327,7 @@ func (n *network) EndpointByID(id string) (Endpoint, error) { func (n *network) updateSvcRecord(ep *endpoint, localEps []*endpoint, isAdd bool) { var ipv6 net.IP epName := ep.Name() - if iface := ep.Iface(); iface.Address() != nil { + if iface := ep.Iface(); iface != nil && iface.Address() != nil { myAliases := ep.MyAliases() if iface.AddressIPv6() != nil { ipv6 = iface.AddressIPv6().IP @@ -1383,14 +1383,18 @@ func delIPToName(ipMap setmatrix.SetMatrix, name, serviceID string, ip net.IP) { } func addNameToIP(svcMap setmatrix.SetMatrix, name, serviceID string, epIP net.IP) { - svcMap.Insert(name, svcMapEntry{ + // Since DNS name resolution is case-insensitive, Use the lower-case form + // of the name as the key into svcMap + lowerCaseName := strings.ToLower(name) + svcMap.Insert(lowerCaseName, svcMapEntry{ ip: epIP.String(), serviceID: serviceID, }) } func delNameToIP(svcMap setmatrix.SetMatrix, name, serviceID string, epIP net.IP) { - svcMap.Remove(name, svcMapEntry{ + lowerCaseName := strings.ToLower(name) + svcMap.Remove(lowerCaseName, svcMapEntry{ ip: epIP.String(), serviceID: serviceID, }) @@ -1402,21 +1406,21 @@ func (n *network) addSvcRecords(eID, name, serviceID string, epIP, epIPv6 net.IP if n.ingress { return } - - logrus.Debugf("%s (%.7s).addSvcRecords(%s, %s, %s, %t) %s sid:%s", eID, n.ID(), name, epIP, epIPv6, ipMapUpdate, method, serviceID) + networkID := n.ID() + logrus.Debugf("%s (%.7s).addSvcRecords(%s, %s, %s, %t) %s sid:%s", eID, networkID, name, epIP, epIPv6, ipMapUpdate, method, serviceID) c := n.getController() c.Lock() defer c.Unlock() - sr, ok := c.svcRecords[n.ID()] + sr, ok := c.svcRecords[networkID] if !ok { sr = svcInfo{ svcMap: setmatrix.NewSetMatrix(), svcIPv6Map: setmatrix.NewSetMatrix(), ipMap: setmatrix.NewSetMatrix(), } - c.svcRecords[n.ID()] = sr + c.svcRecords[networkID] = sr } if ipMapUpdate { @@ -1438,14 +1442,14 @@ func (n *network) deleteSvcRecords(eID, name, serviceID string, epIP net.IP, epI if n.ingress { return } - - logrus.Debugf("%s (%.7s).deleteSvcRecords(%s, %s, %s, %t) %s sid:%s ", eID, n.ID(), name, epIP, epIPv6, ipMapUpdate, method, serviceID) + networkID := n.ID() + logrus.Debugf("%s (%.7s).deleteSvcRecords(%s, %s, %s, %t) %s sid:%s ", eID, networkID, name, epIP, epIPv6, ipMapUpdate, method, serviceID) c := n.getController() c.Lock() defer c.Unlock() - sr, ok := c.svcRecords[n.ID()] + sr, ok := c.svcRecords[networkID] if !ok { return } @@ -1825,13 +1829,17 @@ func (n *network) IpamConfig() (string, map[string]string, []*IpamConf, []*IpamC for i, c := range n.ipamV4Config { cc := &IpamConf{} - c.CopyTo(cc) + if err := c.CopyTo(cc); err != nil { + logrus.WithError(err).Error("Error copying ipam ipv4 config") + } v4L[i] = cc } for i, c := range n.ipamV6Config { cc := &IpamConf{} - c.CopyTo(cc) + if err := c.CopyTo(cc); err != nil { + logrus.WithError(err).Debug("Error copying ipam ipv6 config") + } v6L[i] = cc } @@ -1847,13 +1855,17 @@ func (n *network) IpamInfo() ([]*IpamInfo, []*IpamInfo) { for i, info := range n.ipamV4Info { ic := &IpamInfo{} - info.CopyTo(ic) + if err := info.CopyTo(ic); err != nil { + logrus.WithError(err).Error("Error copying ipv4 ipam config") + } v4Info[i] = ic } for i, info := range n.ipamV6Info { ic := &IpamInfo{} - info.CopyTo(ic) + if err := info.CopyTo(ic); err != nil { + logrus.WithError(err).Error("Error copying ipv6 ipam config") + } v6Info[i] = ic } @@ -1936,6 +1948,22 @@ func (n *network) TableEventRegister(tableName string, objType driverapi.ObjectT return nil } +func (n *network) UpdateIpamConfig(ipV4Data []driverapi.IPAMData) { + + ipamV4Config := make([]*IpamConf, len(ipV4Data)) + + for i, data := range ipV4Data { + ic := &IpamConf{} + ic.PreferredPool = data.Pool.String() + ic.Gateway = data.Gateway.IP.String() + ipamV4Config[i] = ic + } + + n.Lock() + defer n.Unlock() + n.ipamV4Config = ipamV4Config +} + // Special drivers are ones which do not need to perform any network plumbing func (n *network) hasSpecialDriver() bool { return n.Type() == "host" || n.Type() == "null" @@ -1949,15 +1977,17 @@ func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) { var ipv6Miss bool c := n.getController() + networkID := n.ID() c.Lock() defer c.Unlock() - sr, ok := c.svcRecords[n.ID()] + sr, ok := c.svcRecords[networkID] if !ok { return nil, false } req = strings.TrimSuffix(req, ".") + req = strings.ToLower(req) ipSet, ok := sr.svcMap.Get(req) if ipType == types.IPv6 { @@ -1988,10 +2018,11 @@ func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) { } func (n *network) HandleQueryResp(name string, ip net.IP) { + networkID := n.ID() c := n.getController() c.Lock() defer c.Unlock() - sr, ok := c.svcRecords[n.ID()] + sr, ok := c.svcRecords[networkID] if !ok { return @@ -2007,10 +2038,11 @@ func (n *network) HandleQueryResp(name string, ip net.IP) { } func (n *network) ResolveIP(ip string) string { + networkID := n.ID() c := n.getController() c.Lock() defer c.Unlock() - sr, ok := c.svcRecords[n.ID()] + sr, ok := c.svcRecords[networkID] if !ok { return "" @@ -2061,9 +2093,10 @@ func (n *network) ResolveService(name string) ([]*net.SRV, []net.IP) { proto := parts[1] svcName := strings.Join(parts[2:], ".") + networkID := n.ID() c.Lock() defer c.Unlock() - sr, ok := c.svcRecords[n.ID()] + sr, ok := c.svcRecords[networkID] if !ok { return nil, nil @@ -2203,14 +2236,14 @@ func (n *network) deleteLoadBalancerSandbox() error { if sb != nil { if err := sb.DisableService(); err != nil { logrus.Warnf("Failed to disable service on sandbox %s: %v", sandboxName, err) - //Ignore error and attempt to delete the load balancer endpoint + // Ignore error and attempt to delete the load balancer endpoint } } } if err := endpoint.Delete(true); err != nil { logrus.Warnf("Failed to delete endpoint %s (%s) in %s: %v", endpoint.Name(), endpoint.ID(), sandboxName, err) - //Ignore error and attempt to delete the sandbox. + // Ignore error and attempt to delete the sandbox. } } diff --git a/vendor/github.com/docker/libnetwork/network_unix.go b/libnetwork/network_unix.go similarity index 75% rename from vendor/github.com/docker/libnetwork/network_unix.go rename to libnetwork/network_unix.go index 585261ece0db6..8b529b864468e 100644 --- a/vendor/github.com/docker/libnetwork/network_unix.go +++ b/libnetwork/network_unix.go @@ -1,8 +1,9 @@ +//go:build !windows // +build !windows package libnetwork -import "github.com/docker/libnetwork/ipamapi" +import "github.com/docker/docker/libnetwork/ipamapi" // Stub implementations for DNS related functions diff --git a/libnetwork/network_windows.go b/libnetwork/network_windows.go new file mode 100644 index 0000000000000..fda8e436d9be6 --- /dev/null +++ b/libnetwork/network_windows.go @@ -0,0 +1,76 @@ +//go:build windows +// +build windows + +package libnetwork + +import ( + "runtime" + "time" + + "github.com/Microsoft/hcsshim" + "github.com/docker/docker/libnetwork/drivers/windows" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/ipams/windowsipam" + "github.com/sirupsen/logrus" +) + +func executeInCompartment(compartmentID uint32, x func()) { + runtime.LockOSThread() + + if err := hcsshim.SetCurrentThreadCompartmentId(compartmentID); err != nil { + logrus.Error(err) + } + defer func() { + hcsshim.SetCurrentThreadCompartmentId(0) + runtime.UnlockOSThread() + }() + + x() +} + +func (n *network) startResolver() { + if n.networkType == "ics" { + return + } + n.resolverOnce.Do(func() { + logrus.Debugf("Launching DNS server for network %q", n.Name()) + options := n.Info().DriverOptions() + hnsid := options[windows.HNSID] + + if hnsid == "" { + return + } + + hnsresponse, err := hcsshim.HNSNetworkRequest("GET", hnsid, "") + if err != nil { + logrus.Errorf("Resolver Setup/Start failed for container %s, %q", n.Name(), err) + return + } + + for _, subnet := range hnsresponse.Subnets { + if subnet.GatewayAddress != "" { + for i := 0; i < 3; i++ { + resolver := NewResolver(subnet.GatewayAddress, false, "", n) + logrus.Debugf("Binding a resolver on network %s gateway %s", n.Name(), subnet.GatewayAddress) + executeInCompartment(hnsresponse.DNSServerCompartment, resolver.SetupFunc(53)) + + if err = resolver.Start(); err != nil { + logrus.Errorf("Resolver Setup/Start failed for container %s, %q", n.Name(), err) + time.Sleep(1 * time.Second) + } else { + logrus.Debugf("Resolver bound successfully for network %s", n.Name()) + n.resolver = append(n.resolver, resolver) + break + } + } + } + } + }) +} + +func defaultIpamForNetworkType(networkType string) string { + if windows.IsBuiltinLocalDriver(networkType) { + return windowsipam.DefaultIPAM + } + return ipamapi.DefaultIPAM +} diff --git a/vendor/github.com/docker/libnetwork/networkdb/broadcast.go b/libnetwork/networkdb/broadcast.go similarity index 100% rename from vendor/github.com/docker/libnetwork/networkdb/broadcast.go rename to libnetwork/networkdb/broadcast.go diff --git a/vendor/github.com/docker/libnetwork/networkdb/cluster.go b/libnetwork/networkdb/cluster.go similarity index 95% rename from vendor/github.com/docker/libnetwork/networkdb/cluster.go rename to libnetwork/networkdb/cluster.go index c9895708483b3..ce9ad35932f55 100644 --- a/vendor/github.com/docker/libnetwork/networkdb/cluster.go +++ b/libnetwork/networkdb/cluster.go @@ -18,12 +18,10 @@ import ( ) const ( - reapPeriod = 5 * time.Second - rejoinClusterDuration = 10 * time.Second - rejoinInterval = 60 * time.Second - retryInterval = 1 * time.Second - nodeReapInterval = 24 * time.Hour - nodeReapPeriod = 2 * time.Hour + reapPeriod = 5 * time.Second + retryInterval = 1 * time.Second + nodeReapInterval = 24 * time.Hour + nodeReapPeriod = 2 * time.Hour // considering a cluster with > 20 nodes and a drain speed of 100 msg/s // the following is roughly 1 minute maxQueueLenBroadcastOnSync = 500 @@ -172,7 +170,7 @@ func (nDB *NetworkDB) clusterInit() error { {config.PushPullInterval, nDB.bulkSyncTables}, {retryInterval, nDB.reconnectNode}, {nodeReapPeriod, nDB.reapDeadNode}, - {rejoinInterval, nDB.rejoinClusterBootStrap}, + {nDB.config.rejoinClusterInterval, nDB.rejoinClusterBootStrap}, } { t := time.NewTicker(trigger.interval) go nDB.triggerFunc(trigger.interval, t.C, trigger.fn) @@ -210,7 +208,8 @@ func (nDB *NetworkDB) clusterJoin(members []string) error { if _, err := mlist.Join(members); err != nil { // In case of failure, we no longer need to explicitly call retryJoin. - // rejoinClusterBootStrap, which runs every minute, will retryJoin for 10sec + // rejoinClusterBootStrap, which runs every nDB.config.rejoinClusterInterval, + // will retryJoin for nDB.config.rejoinClusterDuration. return fmt.Errorf("could not join node to memberlist: %v", err) } @@ -244,7 +243,7 @@ func (nDB *NetworkDB) clusterLeave() error { func (nDB *NetworkDB) triggerFunc(stagger time.Duration, C <-chan time.Time, f func()) { // Use a random stagger to avoid synchronizing - randStagger := time.Duration(uint64(rnd.Int63()) % uint64(stagger)) + randStagger := time.Duration(uint64(rnd.Int63()) % uint64(stagger)) //nolint:gosec // gosec complains about the use of rand here. It should be fine. select { case <-time.After(randStagger): case <-nDB.ctx.Done(): @@ -288,7 +287,12 @@ func (nDB *NetworkDB) rejoinClusterBootStrap() { return } - myself, _ := nDB.nodes[nDB.config.NodeID] + myself, ok := nDB.nodes[nDB.config.NodeID] + if !ok { + nDB.RUnlock() + logrus.Warnf("rejoinClusterBootstrap unable to find local node info using ID:%v", nDB.config.NodeID) + return + } bootStrapIPs := make([]string, 0, len(nDB.bootStrapIP)) for _, bootIP := range nDB.bootStrapIP { // botostrap IPs are usually IP:port from the Join @@ -319,7 +323,7 @@ func (nDB *NetworkDB) rejoinClusterBootStrap() { } // None of the bootStrap nodes are in the cluster, call memberlist join logrus.Debugf("rejoinClusterBootStrap, calling cluster join with bootStrap %v", bootStrapIPs) - ctx, cancel := context.WithTimeout(nDB.ctx, rejoinClusterDuration) + ctx, cancel := context.WithTimeout(nDB.ctx, nDB.config.rejoinClusterDuration) defer cancel() nDB.retryJoin(ctx, bootStrapIPs) } @@ -352,7 +356,7 @@ func (nDB *NetworkDB) reconnectNode() { nDB.bulkSync([]string{node.Name}, true) } -// For timing the entry deletion in the repaer APIs that doesn't use monotonic clock +// For timing the entry deletion in the reaper APIs that doesn't use monotonic clock // source (time.Now, Sub etc.) should be avoided. Hence we use reapTime in every // entry which is set initially to reapInterval and decremented by reapPeriod every time // the reaper runs. NOTE nDB.reapTableEntries updates the reapTime with a readlock. This @@ -415,10 +419,10 @@ func (nDB *NetworkDB) reapTableEntries() { okTable, okNetwork := nDB.deleteEntry(nid, tname, key) if !okTable { - logrus.Errorf("Table tree delete failed, entry with key:%s does not exists in the table:%s network:%s", key, tname, nid) + logrus.Errorf("Table tree delete failed, entry with key:%s does not exist in the table:%s network:%s", key, tname, nid) } if !okNetwork { - logrus.Errorf("Network tree delete failed, entry with key:%s does not exists in the network:%s table:%s", key, nid, tname) + logrus.Errorf("Network tree delete failed, entry with key:%s does not exist in the network:%s table:%s", key, nid, tname) } return false diff --git a/vendor/github.com/docker/libnetwork/networkdb/delegate.go b/libnetwork/networkdb/delegate.go similarity index 100% rename from vendor/github.com/docker/libnetwork/networkdb/delegate.go rename to libnetwork/networkdb/delegate.go diff --git a/vendor/github.com/docker/libnetwork/networkdb/event_delegate.go b/libnetwork/networkdb/event_delegate.go similarity index 100% rename from vendor/github.com/docker/libnetwork/networkdb/event_delegate.go rename to libnetwork/networkdb/event_delegate.go diff --git a/vendor/github.com/docker/libnetwork/networkdb/message.go b/libnetwork/networkdb/message.go similarity index 100% rename from vendor/github.com/docker/libnetwork/networkdb/message.go rename to libnetwork/networkdb/message.go diff --git a/vendor/github.com/docker/libnetwork/networkdb/networkdb.go b/libnetwork/networkdb/networkdb.go similarity index 96% rename from vendor/github.com/docker/libnetwork/networkdb/networkdb.go rename to libnetwork/networkdb/networkdb.go index d8c3107baacb4..bc78e480ae80f 100644 --- a/vendor/github.com/docker/libnetwork/networkdb/networkdb.go +++ b/libnetwork/networkdb/networkdb.go @@ -1,6 +1,6 @@ package networkdb -//go:generate protoc -I.:../vendor/github.com/gogo/protobuf --gogo_out=import_path=github.com/docker/libnetwork/networkdb,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. networkdb.proto +//go:generate protoc -I.:../vendor/github.com/gogo/protobuf --gogo_out=import_path=github.com/docker/docker/libnetwork/networkdb,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. networkdb.proto import ( "context" @@ -11,9 +11,9 @@ import ( "time" "github.com/armon/go-radix" + "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/pkg/stringid" "github.com/docker/go-events" - "github.com/docker/libnetwork/types" "github.com/hashicorp/memberlist" "github.com/hashicorp/serf/serf" "github.com/sirupsen/logrus" @@ -192,6 +192,14 @@ type Config struct { // NOTE this MUST always be higher than reapEntryInterval reapNetworkInterval time.Duration + // rejoinClusterDuration represents retryJoin timeout used by rejoinClusterBootStrap. + // Default is 10sec. + rejoinClusterDuration time.Duration + + // rejoinClusterInterval represents interval on which rejoinClusterBootStrap runs. + // Default is 60sec. + rejoinClusterInterval time.Duration + // StatsPrintPeriod the period to use to print queue stats // Default is 5min StatsPrintPeriod time.Duration @@ -225,13 +233,15 @@ type entry struct { func DefaultConfig() *Config { hostname, _ := os.Hostname() return &Config{ - NodeID: stringid.TruncateID(stringid.GenerateRandomID()), - Hostname: hostname, - BindAddr: "0.0.0.0", - PacketBufferSize: 1400, - StatsPrintPeriod: 5 * time.Minute, - HealthPrintPeriod: 1 * time.Minute, - reapEntryInterval: 30 * time.Minute, + NodeID: stringid.TruncateID(stringid.GenerateRandomID()), + Hostname: hostname, + BindAddr: "0.0.0.0", + PacketBufferSize: 1400, + StatsPrintPeriod: 5 * time.Minute, + HealthPrintPeriod: 1 * time.Minute, + reapEntryInterval: 30 * time.Minute, + rejoinClusterDuration: 10 * time.Second, + rejoinClusterInterval: 60 * time.Second, } } diff --git a/vendor/github.com/docker/libnetwork/networkdb/networkdb.pb.go b/libnetwork/networkdb/networkdb.pb.go similarity index 98% rename from vendor/github.com/docker/libnetwork/networkdb/networkdb.pb.go rename to libnetwork/networkdb/networkdb.pb.go index 971e868aa4a47..58faed98160bc 100644 --- a/vendor/github.com/docker/libnetwork/networkdb/networkdb.pb.go +++ b/libnetwork/networkdb/networkdb.pb.go @@ -145,7 +145,9 @@ var NetworkEvent_Type_value = map[string]int32{ func (x NetworkEvent_Type) String() string { return proto.EnumName(NetworkEvent_Type_name, int32(x)) } -func (NetworkEvent_Type) EnumDescriptor() ([]byte, []int) { return fileDescriptorNetworkdb, []int{2, 0} } +func (NetworkEvent_Type) EnumDescriptor() ([]byte, []int) { + return fileDescriptorNetworkdb, []int{2, 0} +} type TableEvent_Type int32 @@ -476,7 +478,7 @@ func (m *CompoundMessage) GetMessages() []*CompoundMessage_SimpleMessage { type CompoundMessage_SimpleMessage struct { // Bytestring payload of a message constructed using // other message type definitions. - Payload []byte `protobuf:"bytes,1,opt,name=Payload,json=payload,proto3" json:"Payload,omitempty"` + Payload []byte `protobuf:"bytes,1,opt,name=Payload,proto3" json:"Payload,omitempty"` } func (m *CompoundMessage_SimpleMessage) Reset() { *m = CompoundMessage_SimpleMessage{} } @@ -2651,7 +2653,7 @@ var ( func init() { proto.RegisterFile("networkdb/networkdb.proto", fileDescriptorNetworkdb) } var fileDescriptorNetworkdb = []byte{ - // 955 bytes of a gzipped FileDescriptorProto + // 956 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x96, 0xcd, 0x6e, 0xe3, 0x54, 0x14, 0xc7, 0x7b, 0xf3, 0xd1, 0x26, 0xa7, 0x29, 0x35, 0x77, 0x3a, 0x53, 0xd7, 0x03, 0x89, 0x31, 0x33, 0x55, 0xa6, 0x82, 0x14, 0x75, 0x9e, 0xa0, 0x49, 0x2c, 0xc8, 0x4c, 0xc6, 0x89, 0xdc, 0xa4, @@ -2700,16 +2702,16 @@ var fileDescriptorNetworkdb = []byte{ 0x11, 0xb6, 0x5c, 0x32, 0xb6, 0x1c, 0x62, 0xb0, 0x57, 0x5e, 0xd0, 0x93, 0xa9, 0xf2, 0x2d, 0x82, 0xdd, 0x9a, 0x33, 0x70, 0x9d, 0xa1, 0x6d, 0x24, 0x35, 0xd5, 0x21, 0x37, 0x88, 0x86, 0xbe, 0x88, 0x58, 0x63, 0x95, 0x39, 0xb7, 0xaf, 0xd1, 0x95, 0x33, 0x73, 0xe0, 0x5a, 0x34, 0x9e, 0xe9, 0x8b, - 0x5f, 0x4a, 0x4f, 0x60, 0x67, 0x65, 0x2b, 0x4c, 0xa2, 0x1d, 0x27, 0x81, 0x56, 0x92, 0x38, 0xfa, - 0x39, 0x05, 0xdb, 0xdc, 0x5d, 0x8d, 0x3f, 0xe4, 0x0d, 0xc1, 0xae, 0x27, 0x6e, 0x37, 0x71, 0x43, - 0x05, 0x76, 0x34, 0xb5, 0xf3, 0x79, 0x4b, 0x7f, 0xde, 0x53, 0xcf, 0x55, 0xad, 0x23, 0xa0, 0xe8, - 0xd0, 0xe6, 0xd0, 0x95, 0xfb, 0xea, 0x08, 0xb6, 0x3b, 0xa7, 0xd5, 0xa6, 0x1a, 0xd3, 0xf1, 0xb1, - 0xcc, 0xd1, 0x5c, 0xaf, 0x1f, 0x42, 0xbe, 0xdd, 0x3d, 0xfb, 0xac, 0xd7, 0xee, 0x36, 0x9b, 0x42, - 0x5a, 0xda, 0x9f, 0x4c, 0xe5, 0x7b, 0x1c, 0xb9, 0x38, 0xcd, 0x0e, 0x21, 0x5f, 0xed, 0x36, 0x9f, - 0xf7, 0xce, 0xbe, 0xd0, 0x6a, 0x42, 0xe6, 0x16, 0x97, 0x98, 0x05, 0x3f, 0x86, 0x5c, 0xad, 0xf5, - 0xa2, 0xdd, 0xea, 0x6a, 0x75, 0x21, 0x7b, 0x0b, 0x4b, 0x14, 0xc5, 0x65, 0x00, 0xad, 0x55, 0x4f, - 0x32, 0xdc, 0x8c, 0x8c, 0xc9, 0xd7, 0x93, 0x5c, 0xd2, 0xd2, 0xbd, 0xd8, 0x98, 0xbc, 0x6c, 0x55, - 0xf1, 0xf7, 0x9b, 0xe2, 0xc6, 0x5f, 0x37, 0x45, 0xf4, 0xcd, 0xbc, 0x88, 0x5e, 0xcf, 0x8b, 0xe8, - 0xd7, 0x79, 0x11, 0xfd, 0x39, 0x2f, 0xa2, 0x8b, 0x4d, 0xf6, 0xd7, 0xe9, 0xe9, 0xdf, 0x01, 0x00, - 0x00, 0xff, 0xff, 0x02, 0x9d, 0x53, 0x72, 0x78, 0x09, 0x00, 0x00, + 0x5f, 0x4a, 0x4f, 0x60, 0x67, 0x65, 0x2b, 0x4c, 0xa2, 0x1d, 0x27, 0x81, 0xa2, 0x24, 0xe2, 0xe9, + 0xd1, 0xcf, 0x29, 0xd8, 0xe6, 0xee, 0x6a, 0xfc, 0x21, 0x6f, 0x08, 0x76, 0x3d, 0x71, 0xbb, 0x89, + 0x1b, 0x2a, 0xb0, 0xa3, 0xa9, 0x9d, 0xcf, 0x5b, 0xfa, 0xf3, 0x9e, 0x7a, 0xae, 0x6a, 0x1d, 0x01, + 0x45, 0x87, 0x36, 0x87, 0xae, 0xdc, 0x57, 0x47, 0xb0, 0xdd, 0x39, 0xad, 0x36, 0xd5, 0x98, 0x8e, + 0x8f, 0x65, 0x8e, 0xe6, 0x7a, 0xfd, 0x10, 0xf2, 0xed, 0xee, 0xd9, 0x67, 0xbd, 0x76, 0xb7, 0xd9, + 0x14, 0xd2, 0xd2, 0xfe, 0x64, 0x2a, 0xdf, 0xe3, 0xc8, 0xc5, 0x69, 0x76, 0x08, 0xf9, 0x6a, 0xb7, + 0xf9, 0xbc, 0x77, 0xf6, 0x85, 0x56, 0x13, 0x32, 0xb7, 0xb8, 0xc4, 0x2c, 0xf8, 0x31, 0xe4, 0x6a, + 0xad, 0x17, 0xed, 0x56, 0x57, 0xab, 0x0b, 0xd9, 0x5b, 0x58, 0xa2, 0x28, 0x2e, 0x03, 0x68, 0xad, + 0x7a, 0x92, 0xe1, 0x66, 0x64, 0x4c, 0xbe, 0x9e, 0xe4, 0x92, 0x96, 0xee, 0xc5, 0xc6, 0xe4, 0x65, + 0xab, 0x8a, 0xbf, 0xdf, 0x14, 0x37, 0xfe, 0xba, 0x29, 0xa2, 0x6f, 0xe6, 0x45, 0xf4, 0x7a, 0x5e, + 0x44, 0xbf, 0xce, 0x8b, 0xe8, 0xcf, 0x79, 0x11, 0x5d, 0x6c, 0xb2, 0xbf, 0x4e, 0x4f, 0xff, 0x0e, + 0x00, 0x00, 0xff, 0xff, 0x0b, 0x8d, 0x70, 0xa7, 0x78, 0x09, 0x00, 0x00, } diff --git a/vendor/github.com/docker/libnetwork/networkdb/networkdb.proto b/libnetwork/networkdb/networkdb.proto similarity index 100% rename from vendor/github.com/docker/libnetwork/networkdb/networkdb.proto rename to libnetwork/networkdb/networkdb.proto diff --git a/libnetwork/networkdb/networkdb_test.go b/libnetwork/networkdb/networkdb_test.go new file mode 100644 index 0000000000000..682d9fd53ceee --- /dev/null +++ b/libnetwork/networkdb/networkdb_test.go @@ -0,0 +1,914 @@ +package networkdb + +import ( + "fmt" + "log" + "net" + "os" + "strconv" + "sync/atomic" + "testing" + "time" + + "github.com/docker/docker/pkg/stringid" + "github.com/docker/go-events" + "github.com/hashicorp/memberlist" + "github.com/sirupsen/logrus" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/poll" +) + +var dbPort int32 = 10000 + +func TestMain(m *testing.M) { + os.WriteFile("/proc/sys/net/ipv6/conf/lo/disable_ipv6", []byte{'0', '\n'}, 0644) + logrus.SetLevel(logrus.ErrorLevel) + os.Exit(m.Run()) +} + +func launchNode(t *testing.T, conf Config) *NetworkDB { + t.Helper() + db, err := New(&conf) + assert.NilError(t, err) + return db +} + +func createNetworkDBInstances(t *testing.T, num int, namePrefix string, conf *Config) []*NetworkDB { + t.Helper() + var dbs []*NetworkDB + for i := 0; i < num; i++ { + localConfig := *conf + localConfig.Hostname = fmt.Sprintf("%s%d", namePrefix, i+1) + localConfig.NodeID = stringid.TruncateID(stringid.GenerateRandomID()) + localConfig.BindPort = int(atomic.AddInt32(&dbPort, 1)) + db := launchNode(t, localConfig) + if i != 0 { + assert.Check(t, db.Join([]string{fmt.Sprintf("localhost:%d", db.config.BindPort-1)})) + } + + dbs = append(dbs, db) + } + + // Wait till the cluster creation is successful + check := func(t poll.LogT) poll.Result { + // Check that the cluster is properly created + for i := 0; i < num; i++ { + if num != len(dbs[i].ClusterPeers()) { + return poll.Continue("%s:Waiting for cluser peers to be established", dbs[i].config.Hostname) + } + } + return poll.Success() + } + poll.WaitOn(t, check, poll.WithDelay(2*time.Second), poll.WithTimeout(20*time.Second)) + + return dbs +} + +func closeNetworkDBInstances(t *testing.T, dbs []*NetworkDB) { + t.Helper() + log.Print("Closing DB instances...") + for _, db := range dbs { + db.Close() + } +} + +func (db *NetworkDB) verifyNodeExistence(t *testing.T, node string, present bool) { + t.Helper() + for i := 0; i < 80; i++ { + db.RLock() + _, ok := db.nodes[node] + db.RUnlock() + if present && ok { + return + } + + if !present && !ok { + return + } + + time.Sleep(50 * time.Millisecond) + } + + t.Errorf("%v(%v): Node existence verification for node %s failed", db.config.Hostname, db.config.NodeID, node) +} + +func (db *NetworkDB) verifyNetworkExistence(t *testing.T, node string, id string, present bool) { + t.Helper() + + const sleepInterval = 50 * time.Millisecond + var maxRetries int64 + if dl, ok := t.Deadline(); ok { + maxRetries = int64(time.Until(dl) / sleepInterval) + } else { + maxRetries = 80 + } + for i := int64(0); i < maxRetries; i++ { + db.RLock() + nn, nnok := db.networks[node] + db.RUnlock() + if nnok { + n, ok := nn[id] + if present && ok { + return + } + + if !present && + ((ok && n.leaving) || + !ok) { + return + } + } + + time.Sleep(sleepInterval) + } + + t.Error("Network existence verification failed") +} + +func (db *NetworkDB) verifyEntryExistence(t *testing.T, tname, nid, key, value string, present bool) { + t.Helper() + n := 80 + for i := 0; i < n; i++ { + entry, err := db.getEntry(tname, nid, key) + if present && err == nil && string(entry.value) == value { + return + } + + if !present && + ((err == nil && entry.deleting) || + (err != nil)) { + return + } + + if i == n-1 && !present && err != nil { + return + } + + time.Sleep(50 * time.Millisecond) + } + + t.Errorf("Entry existence verification test failed for %v(%v)", db.config.Hostname, db.config.NodeID) +} + +func testWatch(t *testing.T, ch chan events.Event, ev interface{}, tname, nid, key, value string) { + t.Helper() + select { + case rcvdEv := <-ch: + assert.Check(t, is.Equal(fmt.Sprintf("%T", rcvdEv), fmt.Sprintf("%T", ev))) + switch typ := rcvdEv.(type) { + case CreateEvent: + assert.Check(t, is.Equal(tname, typ.Table)) + assert.Check(t, is.Equal(nid, typ.NetworkID)) + assert.Check(t, is.Equal(key, typ.Key)) + assert.Check(t, is.Equal(value, string(typ.Value))) + case UpdateEvent: + assert.Check(t, is.Equal(tname, typ.Table)) + assert.Check(t, is.Equal(nid, typ.NetworkID)) + assert.Check(t, is.Equal(key, typ.Key)) + assert.Check(t, is.Equal(value, string(typ.Value))) + case DeleteEvent: + assert.Check(t, is.Equal(tname, typ.Table)) + assert.Check(t, is.Equal(nid, typ.NetworkID)) + assert.Check(t, is.Equal(key, typ.Key)) + } + case <-time.After(time.Second): + t.Fail() + return + } +} + +func TestNetworkDBSimple(t *testing.T) { + dbs := createNetworkDBInstances(t, 2, "node", DefaultConfig()) + closeNetworkDBInstances(t, dbs) +} + +func TestNetworkDBJoinLeaveNetwork(t *testing.T) { + dbs := createNetworkDBInstances(t, 2, "node", DefaultConfig()) + + err := dbs[0].JoinNetwork("network1") + assert.NilError(t, err) + + dbs[1].verifyNetworkExistence(t, dbs[0].config.NodeID, "network1", true) + + err = dbs[0].LeaveNetwork("network1") + assert.NilError(t, err) + + dbs[1].verifyNetworkExistence(t, dbs[0].config.NodeID, "network1", false) + closeNetworkDBInstances(t, dbs) +} + +func TestNetworkDBJoinLeaveNetworks(t *testing.T) { + dbs := createNetworkDBInstances(t, 2, "node", DefaultConfig()) + + n := 10 + for i := 1; i <= n; i++ { + err := dbs[0].JoinNetwork(fmt.Sprintf("network0%d", i)) + assert.NilError(t, err) + } + + for i := 1; i <= n; i++ { + err := dbs[1].JoinNetwork(fmt.Sprintf("network1%d", i)) + assert.NilError(t, err) + } + + for i := 1; i <= n; i++ { + dbs[1].verifyNetworkExistence(t, dbs[0].config.NodeID, fmt.Sprintf("network0%d", i), true) + } + + for i := 1; i <= n; i++ { + dbs[0].verifyNetworkExistence(t, dbs[1].config.NodeID, fmt.Sprintf("network1%d", i), true) + } + + for i := 1; i <= n; i++ { + err := dbs[0].LeaveNetwork(fmt.Sprintf("network0%d", i)) + assert.NilError(t, err) + } + + for i := 1; i <= n; i++ { + err := dbs[1].LeaveNetwork(fmt.Sprintf("network1%d", i)) + assert.NilError(t, err) + } + + for i := 1; i <= n; i++ { + dbs[1].verifyNetworkExistence(t, dbs[0].config.NodeID, fmt.Sprintf("network0%d", i), false) + } + + for i := 1; i <= n; i++ { + dbs[0].verifyNetworkExistence(t, dbs[1].config.NodeID, fmt.Sprintf("network1%d", i), false) + } + + closeNetworkDBInstances(t, dbs) +} + +func TestNetworkDBCRUDTableEntry(t *testing.T) { + dbs := createNetworkDBInstances(t, 3, "node", DefaultConfig()) + + err := dbs[0].JoinNetwork("network1") + assert.NilError(t, err) + + dbs[1].verifyNetworkExistence(t, dbs[0].config.NodeID, "network1", true) + + err = dbs[1].JoinNetwork("network1") + assert.NilError(t, err) + + err = dbs[0].CreateEntry("test_table", "network1", "test_key", []byte("test_value")) + assert.NilError(t, err) + + dbs[1].verifyEntryExistence(t, "test_table", "network1", "test_key", "test_value", true) + dbs[2].verifyEntryExistence(t, "test_table", "network1", "test_key", "test_value", false) + + err = dbs[0].UpdateEntry("test_table", "network1", "test_key", []byte("test_updated_value")) + assert.NilError(t, err) + + dbs[1].verifyEntryExistence(t, "test_table", "network1", "test_key", "test_updated_value", true) + + err = dbs[0].DeleteEntry("test_table", "network1", "test_key") + assert.NilError(t, err) + + dbs[1].verifyEntryExistence(t, "test_table", "network1", "test_key", "", false) + + closeNetworkDBInstances(t, dbs) +} + +func TestNetworkDBCRUDTableEntries(t *testing.T) { + dbs := createNetworkDBInstances(t, 2, "node", DefaultConfig()) + + err := dbs[0].JoinNetwork("network1") + assert.NilError(t, err) + + dbs[1].verifyNetworkExistence(t, dbs[0].config.NodeID, "network1", true) + + err = dbs[1].JoinNetwork("network1") + assert.NilError(t, err) + + dbs[0].verifyNetworkExistence(t, dbs[1].config.NodeID, "network1", true) + + n := 10 + for i := 1; i <= n; i++ { + err = dbs[0].CreateEntry("test_table", "network1", + fmt.Sprintf("test_key0%d", i), + []byte(fmt.Sprintf("test_value0%d", i))) + assert.NilError(t, err) + } + + for i := 1; i <= n; i++ { + err = dbs[1].CreateEntry("test_table", "network1", + fmt.Sprintf("test_key1%d", i), + []byte(fmt.Sprintf("test_value1%d", i))) + assert.NilError(t, err) + } + + for i := 1; i <= n; i++ { + dbs[0].verifyEntryExistence(t, "test_table", "network1", + fmt.Sprintf("test_key1%d", i), + fmt.Sprintf("test_value1%d", i), true) + assert.NilError(t, err) + } + + for i := 1; i <= n; i++ { + dbs[1].verifyEntryExistence(t, "test_table", "network1", + fmt.Sprintf("test_key0%d", i), + fmt.Sprintf("test_value0%d", i), true) + assert.NilError(t, err) + } + + // Verify deletes + for i := 1; i <= n; i++ { + err = dbs[0].DeleteEntry("test_table", "network1", + fmt.Sprintf("test_key0%d", i)) + assert.NilError(t, err) + } + + for i := 1; i <= n; i++ { + err = dbs[1].DeleteEntry("test_table", "network1", + fmt.Sprintf("test_key1%d", i)) + assert.NilError(t, err) + } + + for i := 1; i <= n; i++ { + dbs[0].verifyEntryExistence(t, "test_table", "network1", + fmt.Sprintf("test_key1%d", i), "", false) + assert.NilError(t, err) + } + + for i := 1; i <= n; i++ { + dbs[1].verifyEntryExistence(t, "test_table", "network1", + fmt.Sprintf("test_key0%d", i), "", false) + assert.NilError(t, err) + } + + closeNetworkDBInstances(t, dbs) +} + +func TestNetworkDBNodeLeave(t *testing.T) { + dbs := createNetworkDBInstances(t, 2, "node", DefaultConfig()) + + err := dbs[0].JoinNetwork("network1") + assert.NilError(t, err) + + err = dbs[1].JoinNetwork("network1") + assert.NilError(t, err) + + err = dbs[0].CreateEntry("test_table", "network1", "test_key", []byte("test_value")) + assert.NilError(t, err) + + dbs[1].verifyEntryExistence(t, "test_table", "network1", "test_key", "test_value", true) + + dbs[0].Close() + dbs[1].verifyEntryExistence(t, "test_table", "network1", "test_key", "test_value", false) + dbs[1].Close() +} + +func TestNetworkDBWatch(t *testing.T) { + dbs := createNetworkDBInstances(t, 2, "node", DefaultConfig()) + err := dbs[0].JoinNetwork("network1") + assert.NilError(t, err) + + err = dbs[1].JoinNetwork("network1") + assert.NilError(t, err) + + ch, cancel := dbs[1].Watch("", "", "") + + err = dbs[0].CreateEntry("test_table", "network1", "test_key", []byte("test_value")) + assert.NilError(t, err) + + testWatch(t, ch.C, CreateEvent{}, "test_table", "network1", "test_key", "test_value") + + err = dbs[0].UpdateEntry("test_table", "network1", "test_key", []byte("test_updated_value")) + assert.NilError(t, err) + + testWatch(t, ch.C, UpdateEvent{}, "test_table", "network1", "test_key", "test_updated_value") + + err = dbs[0].DeleteEntry("test_table", "network1", "test_key") + assert.NilError(t, err) + + testWatch(t, ch.C, DeleteEvent{}, "test_table", "network1", "test_key", "") + + cancel() + closeNetworkDBInstances(t, dbs) +} + +func TestNetworkDBBulkSync(t *testing.T) { + dbs := createNetworkDBInstances(t, 2, "node", DefaultConfig()) + + err := dbs[0].JoinNetwork("network1") + assert.NilError(t, err) + + dbs[1].verifyNetworkExistence(t, dbs[0].config.NodeID, "network1", true) + + n := 1000 + for i := 1; i <= n; i++ { + err = dbs[0].CreateEntry("test_table", "network1", + fmt.Sprintf("test_key0%d", i), + []byte(fmt.Sprintf("test_value0%d", i))) + assert.NilError(t, err) + } + + err = dbs[1].JoinNetwork("network1") + assert.NilError(t, err) + + dbs[0].verifyNetworkExistence(t, dbs[1].config.NodeID, "network1", true) + + for i := 1; i <= n; i++ { + dbs[1].verifyEntryExistence(t, "test_table", "network1", + fmt.Sprintf("test_key0%d", i), + fmt.Sprintf("test_value0%d", i), true) + assert.NilError(t, err) + } + + closeNetworkDBInstances(t, dbs) +} + +func TestNetworkDBCRUDMediumCluster(t *testing.T) { + n := 5 + + dbs := createNetworkDBInstances(t, n, "node", DefaultConfig()) + + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + if i == j { + continue + } + + dbs[i].verifyNodeExistence(t, dbs[j].config.NodeID, true) + } + } + + for i := 0; i < n; i++ { + err := dbs[i].JoinNetwork("network1") + assert.NilError(t, err) + } + + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + dbs[i].verifyNetworkExistence(t, dbs[j].config.NodeID, "network1", true) + } + } + + err := dbs[0].CreateEntry("test_table", "network1", "test_key", []byte("test_value")) + assert.NilError(t, err) + + for i := 1; i < n; i++ { + dbs[i].verifyEntryExistence(t, "test_table", "network1", "test_key", "test_value", true) + } + + err = dbs[0].UpdateEntry("test_table", "network1", "test_key", []byte("test_updated_value")) + assert.NilError(t, err) + + for i := 1; i < n; i++ { + dbs[i].verifyEntryExistence(t, "test_table", "network1", "test_key", "test_updated_value", true) + } + + err = dbs[0].DeleteEntry("test_table", "network1", "test_key") + assert.NilError(t, err) + + for i := 1; i < n; i++ { + dbs[i].verifyEntryExistence(t, "test_table", "network1", "test_key", "", false) + } + + for i := 1; i < n; i++ { + _, err = dbs[i].GetEntry("test_table", "network1", "test_key") + assert.Check(t, is.ErrorContains(err, "")) + assert.Check(t, is.Contains(err.Error(), "deleted and pending garbage collection"), err) + } + + closeNetworkDBInstances(t, dbs) +} + +func TestNetworkDBNodeJoinLeaveIteration(t *testing.T) { + dbs := createNetworkDBInstances(t, 2, "node", DefaultConfig()) + + // Single node Join/Leave + err := dbs[0].JoinNetwork("network1") + assert.NilError(t, err) + + if len(dbs[0].networkNodes["network1"]) != 1 { + t.Fatalf("The networkNodes list has to have be 1 instead of %d", len(dbs[0].networkNodes["network1"])) + } + + err = dbs[0].LeaveNetwork("network1") + assert.NilError(t, err) + + if len(dbs[0].networkNodes["network1"]) != 0 { + t.Fatalf("The networkNodes list has to have be 0 instead of %d", len(dbs[0].networkNodes["network1"])) + } + + // Multiple nodes Join/Leave + err = dbs[0].JoinNetwork("network1") + assert.NilError(t, err) + + err = dbs[1].JoinNetwork("network1") + assert.NilError(t, err) + + // Wait for the propagation on db[0] + dbs[0].verifyNetworkExistence(t, dbs[1].config.NodeID, "network1", true) + if len(dbs[0].networkNodes["network1"]) != 2 { + t.Fatalf("The networkNodes list has to have be 2 instead of %d - %v", len(dbs[0].networkNodes["network1"]), dbs[0].networkNodes["network1"]) + } + if n, ok := dbs[0].networks[dbs[0].config.NodeID]["network1"]; !ok || n.leaving { + t.Fatalf("The network should not be marked as leaving:%t", n.leaving) + } + + // Wait for the propagation on db[1] + dbs[1].verifyNetworkExistence(t, dbs[0].config.NodeID, "network1", true) + if len(dbs[1].networkNodes["network1"]) != 2 { + t.Fatalf("The networkNodes list has to have be 2 instead of %d - %v", len(dbs[1].networkNodes["network1"]), dbs[1].networkNodes["network1"]) + } + if n, ok := dbs[1].networks[dbs[1].config.NodeID]["network1"]; !ok || n.leaving { + t.Fatalf("The network should not be marked as leaving:%t", n.leaving) + } + + // Try a quick leave/join + err = dbs[0].LeaveNetwork("network1") + assert.NilError(t, err) + err = dbs[0].JoinNetwork("network1") + assert.NilError(t, err) + + dbs[0].verifyNetworkExistence(t, dbs[1].config.NodeID, "network1", true) + if len(dbs[0].networkNodes["network1"]) != 2 { + t.Fatalf("The networkNodes list has to have be 2 instead of %d - %v", len(dbs[0].networkNodes["network1"]), dbs[0].networkNodes["network1"]) + } + + dbs[1].verifyNetworkExistence(t, dbs[0].config.NodeID, "network1", true) + if len(dbs[1].networkNodes["network1"]) != 2 { + t.Fatalf("The networkNodes list has to have be 2 instead of %d - %v", len(dbs[1].networkNodes["network1"]), dbs[1].networkNodes["network1"]) + } + + closeNetworkDBInstances(t, dbs) +} + +func TestNetworkDBGarbageCollection(t *testing.T) { + keysWriteDelete := 5 + config := DefaultConfig() + config.reapEntryInterval = 30 * time.Second + config.StatsPrintPeriod = 15 * time.Second + + dbs := createNetworkDBInstances(t, 3, "node", config) + + // 2 Nodes join network + err := dbs[0].JoinNetwork("network1") + assert.NilError(t, err) + + err = dbs[1].JoinNetwork("network1") + assert.NilError(t, err) + + for i := 0; i < keysWriteDelete; i++ { + err = dbs[i%2].CreateEntry("testTable", "network1", "key-"+strconv.Itoa(i), []byte("value")) + assert.NilError(t, err) + } + time.Sleep(time.Second) + for i := 0; i < keysWriteDelete; i++ { + err = dbs[i%2].DeleteEntry("testTable", "network1", "key-"+strconv.Itoa(i)) + assert.NilError(t, err) + } + for i := 0; i < 2; i++ { + assert.Check(t, is.Equal(keysWriteDelete, dbs[i].networks[dbs[i].config.NodeID]["network1"].entriesNumber), "entries number should match") + } + + // from this point the timer for the garbage collection started, wait 5 seconds and then join a new node + time.Sleep(5 * time.Second) + + err = dbs[2].JoinNetwork("network1") + assert.NilError(t, err) + for i := 0; i < 3; i++ { + assert.Check(t, is.Equal(keysWriteDelete, dbs[i].networks[dbs[i].config.NodeID]["network1"].entriesNumber), "entries number should match") + } + // at this point the entries should had been all deleted + time.Sleep(30 * time.Second) + for i := 0; i < 3; i++ { + assert.Check(t, is.Equal(0, dbs[i].networks[dbs[i].config.NodeID]["network1"].entriesNumber), "entries should had been garbage collected") + } + + // make sure that entries are not coming back + time.Sleep(15 * time.Second) + for i := 0; i < 3; i++ { + assert.Check(t, is.Equal(0, dbs[i].networks[dbs[i].config.NodeID]["network1"].entriesNumber), "entries should had been garbage collected") + } + + closeNetworkDBInstances(t, dbs) +} + +func TestFindNode(t *testing.T) { + dbs := createNetworkDBInstances(t, 1, "node", DefaultConfig()) + + dbs[0].nodes["active"] = &node{Node: memberlist.Node{Name: "active"}} + dbs[0].failedNodes["failed"] = &node{Node: memberlist.Node{Name: "failed"}} + dbs[0].leftNodes["left"] = &node{Node: memberlist.Node{Name: "left"}} + + // active nodes is 2 because the testing node is in the list + assert.Check(t, is.Len(dbs[0].nodes, 2)) + assert.Check(t, is.Len(dbs[0].failedNodes, 1)) + assert.Check(t, is.Len(dbs[0].leftNodes, 1)) + + n, currState, m := dbs[0].findNode("active") + assert.Check(t, n != nil) + assert.Check(t, is.Equal("active", n.Name)) + assert.Check(t, is.Equal(nodeActiveState, currState)) + assert.Check(t, m != nil) + // delete the entry manually + delete(m, "active") + + // test if can be still find + n, currState, m = dbs[0].findNode("active") + assert.Check(t, is.Nil(n)) + assert.Check(t, is.Equal(nodeNotFound, currState)) + assert.Check(t, is.Nil(m)) + + n, currState, m = dbs[0].findNode("failed") + assert.Check(t, n != nil) + assert.Check(t, is.Equal("failed", n.Name)) + assert.Check(t, is.Equal(nodeFailedState, currState)) + assert.Check(t, m != nil) + + // find and remove + n, currState, m = dbs[0].findNode("left") + assert.Check(t, n != nil) + assert.Check(t, is.Equal("left", n.Name)) + assert.Check(t, is.Equal(nodeLeftState, currState)) + assert.Check(t, m != nil) + delete(m, "left") + + n, currState, m = dbs[0].findNode("left") + assert.Check(t, is.Nil(n)) + assert.Check(t, is.Equal(nodeNotFound, currState)) + assert.Check(t, is.Nil(m)) + + closeNetworkDBInstances(t, dbs) +} + +func TestChangeNodeState(t *testing.T) { + dbs := createNetworkDBInstances(t, 1, "node", DefaultConfig()) + + dbs[0].nodes["node1"] = &node{Node: memberlist.Node{Name: "node1"}} + dbs[0].nodes["node2"] = &node{Node: memberlist.Node{Name: "node2"}} + dbs[0].nodes["node3"] = &node{Node: memberlist.Node{Name: "node3"}} + + // active nodes is 4 because the testing node is in the list + assert.Check(t, is.Len(dbs[0].nodes, 4)) + + n, currState, m := dbs[0].findNode("node1") + assert.Check(t, n != nil) + assert.Check(t, is.Equal(nodeActiveState, currState)) + assert.Check(t, is.Equal("node1", n.Name)) + assert.Check(t, m != nil) + + // node1 to failed + dbs[0].changeNodeState("node1", nodeFailedState) + + n, currState, m = dbs[0].findNode("node1") + assert.Check(t, n != nil) + assert.Check(t, is.Equal(nodeFailedState, currState)) + assert.Check(t, is.Equal("node1", n.Name)) + assert.Check(t, m != nil) + assert.Check(t, time.Duration(0) != n.reapTime) + + // node1 back to active + dbs[0].changeNodeState("node1", nodeActiveState) + + n, currState, m = dbs[0].findNode("node1") + assert.Check(t, n != nil) + assert.Check(t, is.Equal(nodeActiveState, currState)) + assert.Check(t, is.Equal("node1", n.Name)) + assert.Check(t, m != nil) + assert.Check(t, is.Equal(time.Duration(0), n.reapTime)) + + // node1 to left + dbs[0].changeNodeState("node1", nodeLeftState) + dbs[0].changeNodeState("node2", nodeLeftState) + dbs[0].changeNodeState("node3", nodeLeftState) + + n, currState, m = dbs[0].findNode("node1") + assert.Check(t, n != nil) + assert.Check(t, is.Equal(nodeLeftState, currState)) + assert.Check(t, is.Equal("node1", n.Name)) + assert.Check(t, m != nil) + assert.Check(t, time.Duration(0) != n.reapTime) + + n, currState, m = dbs[0].findNode("node2") + assert.Check(t, n != nil) + assert.Check(t, is.Equal(nodeLeftState, currState)) + assert.Check(t, is.Equal("node2", n.Name)) + assert.Check(t, m != nil) + assert.Check(t, time.Duration(0) != n.reapTime) + + n, currState, m = dbs[0].findNode("node3") + assert.Check(t, n != nil) + assert.Check(t, is.Equal(nodeLeftState, currState)) + assert.Check(t, is.Equal("node3", n.Name)) + assert.Check(t, m != nil) + assert.Check(t, time.Duration(0) != n.reapTime) + + // active nodes is 1 because the testing node is in the list + assert.Check(t, is.Len(dbs[0].nodes, 1)) + assert.Check(t, is.Len(dbs[0].failedNodes, 0)) + assert.Check(t, is.Len(dbs[0].leftNodes, 3)) + + closeNetworkDBInstances(t, dbs) +} + +func TestNodeReincarnation(t *testing.T) { + dbs := createNetworkDBInstances(t, 1, "node", DefaultConfig()) + + dbs[0].nodes["node1"] = &node{Node: memberlist.Node{Name: "node1", Addr: net.ParseIP("192.168.1.1")}} + dbs[0].leftNodes["node2"] = &node{Node: memberlist.Node{Name: "node2", Addr: net.ParseIP("192.168.1.2")}} + dbs[0].failedNodes["node3"] = &node{Node: memberlist.Node{Name: "node3", Addr: net.ParseIP("192.168.1.3")}} + + // active nodes is 2 because the testing node is in the list + assert.Check(t, is.Len(dbs[0].nodes, 2)) + assert.Check(t, is.Len(dbs[0].failedNodes, 1)) + assert.Check(t, is.Len(dbs[0].leftNodes, 1)) + + b := dbs[0].purgeReincarnation(&memberlist.Node{Name: "node4", Addr: net.ParseIP("192.168.1.1")}) + assert.Check(t, b) + dbs[0].nodes["node4"] = &node{Node: memberlist.Node{Name: "node4", Addr: net.ParseIP("192.168.1.1")}} + + b = dbs[0].purgeReincarnation(&memberlist.Node{Name: "node5", Addr: net.ParseIP("192.168.1.2")}) + assert.Check(t, b) + dbs[0].nodes["node5"] = &node{Node: memberlist.Node{Name: "node5", Addr: net.ParseIP("192.168.1.1")}} + + b = dbs[0].purgeReincarnation(&memberlist.Node{Name: "node6", Addr: net.ParseIP("192.168.1.3")}) + assert.Check(t, b) + dbs[0].nodes["node6"] = &node{Node: memberlist.Node{Name: "node6", Addr: net.ParseIP("192.168.1.1")}} + + b = dbs[0].purgeReincarnation(&memberlist.Node{Name: "node6", Addr: net.ParseIP("192.168.1.10")}) + assert.Check(t, !b) + + // active nodes is 1 because the testing node is in the list + assert.Check(t, is.Len(dbs[0].nodes, 4)) + assert.Check(t, is.Len(dbs[0].failedNodes, 0)) + assert.Check(t, is.Len(dbs[0].leftNodes, 3)) + + closeNetworkDBInstances(t, dbs) +} + +func TestParallelCreate(t *testing.T) { + dbs := createNetworkDBInstances(t, 1, "node", DefaultConfig()) + + startCh := make(chan int) + doneCh := make(chan error) + var success int32 + for i := 0; i < 20; i++ { + go func() { + <-startCh + err := dbs[0].CreateEntry("testTable", "testNetwork", "key", []byte("value")) + if err == nil { + atomic.AddInt32(&success, 1) + } + doneCh <- err + }() + } + + close(startCh) + + for i := 0; i < 20; i++ { + <-doneCh + } + close(doneCh) + // Only 1 write should have succeeded + assert.Check(t, is.Equal(int32(1), success)) + + closeNetworkDBInstances(t, dbs) +} + +func TestParallelDelete(t *testing.T) { + dbs := createNetworkDBInstances(t, 1, "node", DefaultConfig()) + + err := dbs[0].CreateEntry("testTable", "testNetwork", "key", []byte("value")) + assert.NilError(t, err) + + startCh := make(chan int) + doneCh := make(chan error) + var success int32 + for i := 0; i < 20; i++ { + go func() { + <-startCh + err := dbs[0].DeleteEntry("testTable", "testNetwork", "key") + if err == nil { + atomic.AddInt32(&success, 1) + } + doneCh <- err + }() + } + + close(startCh) + + for i := 0; i < 20; i++ { + <-doneCh + } + close(doneCh) + // Only 1 write should have succeeded + assert.Check(t, is.Equal(int32(1), success)) + + closeNetworkDBInstances(t, dbs) +} + +func TestNetworkDBIslands(t *testing.T) { + pollTimeout := func() time.Duration { + const defaultTimeout = 120 * time.Second + dl, ok := t.Deadline() + if !ok { + return defaultTimeout + } + if d := time.Until(dl); d <= defaultTimeout { + return d + } + return defaultTimeout + } + + logrus.SetLevel(logrus.DebugLevel) + conf := DefaultConfig() + // Shorten durations to speed up test execution. + conf.rejoinClusterDuration = conf.rejoinClusterDuration / 10 + conf.rejoinClusterInterval = conf.rejoinClusterInterval / 10 + dbs := createNetworkDBInstances(t, 5, "node", conf) + + // Get the node IP used currently + node := dbs[0].nodes[dbs[0].config.NodeID] + baseIPStr := node.Addr.String() + // Node 0,1,2 are going to be the 3 bootstrap nodes + members := []string{fmt.Sprintf("%s:%d", baseIPStr, dbs[0].config.BindPort), + fmt.Sprintf("%s:%d", baseIPStr, dbs[1].config.BindPort), + fmt.Sprintf("%s:%d", baseIPStr, dbs[2].config.BindPort)} + // Rejoining will update the list of the bootstrap members + for i := 3; i < 5; i++ { + t.Logf("Re-joining: %d", i) + assert.Check(t, dbs[i].Join(members)) + } + + // Now the 3 bootstrap nodes will cleanly leave, and will be properly removed from the other 2 nodes + for i := 0; i < 3; i++ { + logrus.Infof("node %d leaving", i) + dbs[i].Close() + } + + checkDBs := make(map[string]*NetworkDB) + for i := 3; i < 5; i++ { + db := dbs[i] + checkDBs[db.config.Hostname] = db + } + + // Give some time to let the system propagate the messages and free up the ports + check := func(t poll.LogT) poll.Result { + // Verify that the nodes are actually all gone and marked appropiately + for name, db := range checkDBs { + db.RLock() + if (len(db.leftNodes) != 3) || (len(db.failedNodes) != 0) { + for name := range db.leftNodes { + t.Logf("%s: Node %s left", db.config.Hostname, name) + } + for name := range db.failedNodes { + t.Logf("%s: Node %s failed", db.config.Hostname, name) + } + db.RUnlock() + return poll.Continue("%s:Waiting for all nodes to cleanly leave, left: %d, failed nodes: %d", name, len(db.leftNodes), len(db.failedNodes)) + } + db.RUnlock() + t.Logf("%s: OK", name) + delete(checkDBs, name) + } + return poll.Success() + } + poll.WaitOn(t, check, poll.WithDelay(time.Second), poll.WithTimeout(pollTimeout())) + + // Spawn again the first 3 nodes with different names but same IP:port + for i := 0; i < 3; i++ { + logrus.Infof("node %d coming back", i) + dbs[i].config.NodeID = stringid.TruncateID(stringid.GenerateRandomID()) + dbs[i] = launchNode(t, *dbs[i].config) + } + + // Give some time for the reconnect routine to run, it runs every 6s. + check = func(t poll.LogT) poll.Result { + // Verify that the cluster is again all connected. Note that the 3 previous node did not do any join + for i := 0; i < 5; i++ { + db := dbs[i] + db.RLock() + if len(db.nodes) != 5 { + db.RUnlock() + return poll.Continue("%s:Waiting to connect to all nodes", dbs[i].config.Hostname) + } + if len(db.failedNodes) != 0 { + db.RUnlock() + return poll.Continue("%s:Waiting for 0 failedNodes", dbs[i].config.Hostname) + } + if i < 3 { + // nodes from 0 to 3 has no left nodes + if len(db.leftNodes) != 0 { + db.RUnlock() + return poll.Continue("%s:Waiting to have no leftNodes", dbs[i].config.Hostname) + } + } else { + // nodes from 4 to 5 has the 3 previous left nodes + if len(db.leftNodes) != 3 { + db.RUnlock() + return poll.Continue("%s:Waiting to have 3 leftNodes", dbs[i].config.Hostname) + } + } + db.RUnlock() + } + return poll.Success() + } + poll.WaitOn(t, check, poll.WithDelay(time.Second), poll.WithTimeout(pollTimeout())) + closeNetworkDBInstances(t, dbs) +} diff --git a/vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnostic.go b/libnetwork/networkdb/networkdbdiagnostic.go similarity index 99% rename from vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnostic.go rename to libnetwork/networkdb/networkdbdiagnostic.go index ea90c5a0e8df9..f7299303149a1 100644 --- a/vendor/github.com/docker/libnetwork/networkdb/networkdbdiagnostic.go +++ b/libnetwork/networkdb/networkdbdiagnostic.go @@ -6,8 +6,8 @@ import ( "net/http" "strings" - "github.com/docker/libnetwork/diagnostic" - "github.com/docker/libnetwork/internal/caller" + "github.com/docker/docker/libnetwork/diagnostic" + "github.com/docker/docker/libnetwork/internal/caller" "github.com/sirupsen/logrus" ) diff --git a/vendor/github.com/docker/libnetwork/networkdb/nodemgmt.go b/libnetwork/networkdb/nodemgmt.go similarity index 100% rename from vendor/github.com/docker/libnetwork/networkdb/nodemgmt.go rename to libnetwork/networkdb/nodemgmt.go diff --git a/vendor/github.com/docker/libnetwork/networkdb/watch.go b/libnetwork/networkdb/watch.go similarity index 100% rename from vendor/github.com/docker/libnetwork/networkdb/watch.go rename to libnetwork/networkdb/watch.go diff --git a/vendor/github.com/docker/libnetwork/ns/init_linux.go b/libnetwork/ns/init_linux.go similarity index 82% rename from vendor/github.com/docker/libnetwork/ns/init_linux.go rename to libnetwork/ns/init_linux.go index 567a6242ac197..1d08a02f52991 100644 --- a/vendor/github.com/docker/libnetwork/ns/init_linux.go +++ b/libnetwork/ns/init_linux.go @@ -76,12 +76,8 @@ func NlHandle() *netlink.Handle { func getSupportedNlFamilies() []int { fams := []int{syscall.NETLINK_ROUTE} // NETLINK_XFRM test - if err := loadXfrmModules(); err != nil { - if checkXfrmSocket() != nil { - logrus.Warnf("Could not load necessary modules for IPSEC rules: %v", err) - } else { - fams = append(fams, syscall.NETLINK_XFRM) - } + if err := checkXfrmSocket(); err != nil { + logrus.Warnf("Could not load necessary modules for IPSEC rules: %v", err) } else { fams = append(fams, syscall.NETLINK_XFRM) } @@ -99,16 +95,6 @@ func getSupportedNlFamilies() []int { return fams } -func loadXfrmModules() error { - if out, err := exec.Command("modprobe", "-va", "xfrm_user").CombinedOutput(); err != nil { - return fmt.Errorf("Running modprobe xfrm_user failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err) - } - if out, err := exec.Command("modprobe", "-va", "xfrm_algo").CombinedOutput(); err != nil { - return fmt.Errorf("Running modprobe xfrm_algo failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err) - } - return nil -} - // API check on required xfrm modules (xfrm_user, xfrm_algo) func checkXfrmSocket() error { fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_XFRM) diff --git a/vendor/github.com/docker/libnetwork/ns/init_windows.go b/libnetwork/ns/init_windows.go similarity index 100% rename from vendor/github.com/docker/libnetwork/ns/init_windows.go rename to libnetwork/ns/init_windows.go diff --git a/vendor/github.com/docker/libnetwork/options/options.go b/libnetwork/options/options.go similarity index 100% rename from vendor/github.com/docker/libnetwork/options/options.go rename to libnetwork/options/options.go diff --git a/libnetwork/options/options_test.go b/libnetwork/options/options_test.go new file mode 100644 index 0000000000000..bff0c023e906a --- /dev/null +++ b/libnetwork/options/options_test.go @@ -0,0 +1,106 @@ +package options + +import ( + "reflect" + "strings" + "testing" +) + +func TestGenerate(t *testing.T) { + gen := NewGeneric() + gen["Int"] = 1 + gen["Rune"] = 'b' + gen["Float64"] = 2.0 + + type Model struct { + Int int + Rune rune + Float64 float64 + } + + result, err := GenerateFromModel(gen, Model{}) + + if err != nil { + t.Fatal(err) + } + + cast, ok := result.(Model) + if !ok { + t.Fatalf("result has unexpected type %s", reflect.TypeOf(result)) + } + if expected := 1; cast.Int != expected { + t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Int) + } + if expected := 'b'; cast.Rune != expected { + t.Fatalf("wrong value for field Rune: expected %v, got %v", expected, cast.Rune) + } + if expected := 2.0; cast.Float64 != expected { + t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Float64) + } +} + +func TestGeneratePtr(t *testing.T) { + gen := NewGeneric() + gen["Int"] = 1 + gen["Rune"] = 'b' + gen["Float64"] = 2.0 + + type Model struct { + Int int + Rune rune + Float64 float64 + } + + result, err := GenerateFromModel(gen, &Model{}) + + if err != nil { + t.Fatal(err) + } + + cast, ok := result.(*Model) + if !ok { + t.Fatalf("result has unexpected type %s", reflect.TypeOf(result)) + } + if expected := 1; cast.Int != expected { + t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Int) + } + if expected := 'b'; cast.Rune != expected { + t.Fatalf("wrong value for field Rune: expected %v, got %v", expected, cast.Rune) + } + if expected := 2.0; cast.Float64 != expected { + t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Float64) + } +} + +func TestGenerateMissingField(t *testing.T) { + type Model struct{} + _, err := GenerateFromModel(Generic{"foo": "bar"}, Model{}) + + if _, ok := err.(NoSuchFieldError); !ok { + t.Fatalf("expected NoSuchFieldError, got %#v", err) + } else if expected := "no field"; !strings.Contains(err.Error(), expected) { + t.Fatalf("expected %q in error message, got %s", expected, err.Error()) + } +} + +func TestFieldCannotBeSet(t *testing.T) { + type Model struct{ foo int } // nolint:structcheck + _, err := GenerateFromModel(Generic{"foo": "bar"}, Model{}) + + if _, ok := err.(CannotSetFieldError); !ok { + t.Fatalf("expected CannotSetFieldError, got %#v", err) + } else if expected := "cannot set field"; !strings.Contains(err.Error(), expected) { + t.Fatalf("expected %q in error message, got %s", expected, err.Error()) + } +} + +func TestTypeMismatchError(t *testing.T) { + type Model struct{ Foo int } + _, err := GenerateFromModel(Generic{"Foo": "bar"}, Model{}) + + if _, ok := err.(TypeMismatchError); !ok { + t.Fatalf("expected TypeMismatchError, got %#v", err) + } else if expected := "type mismatch"; !strings.Contains(err.Error(), expected) { + t.Fatalf("expected %q in error message, got %s", expected, err.Error()) + } +} diff --git a/vendor/github.com/docker/libnetwork/osl/interface_freebsd.go b/libnetwork/osl/interface_freebsd.go similarity index 100% rename from vendor/github.com/docker/libnetwork/osl/interface_freebsd.go rename to libnetwork/osl/interface_freebsd.go diff --git a/vendor/github.com/docker/libnetwork/osl/interface_linux.go b/libnetwork/osl/interface_linux.go similarity index 96% rename from vendor/github.com/docker/libnetwork/osl/interface_linux.go rename to libnetwork/osl/interface_linux.go index 4f8ff3d639108..1cfe83c57c630 100644 --- a/vendor/github.com/docker/libnetwork/osl/interface_linux.go +++ b/libnetwork/osl/interface_linux.go @@ -8,8 +8,8 @@ import ( "syscall" "time" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" @@ -193,12 +193,12 @@ func (i *nwIface) Statistics() (*types.InterfaceStatistics, error) { } return &types.InterfaceStatistics{ - RxBytes: uint64(stats.RxBytes), - TxBytes: uint64(stats.TxBytes), - RxPackets: uint64(stats.RxPackets), - TxPackets: uint64(stats.TxPackets), - RxDropped: uint64(stats.RxDropped), - TxDropped: uint64(stats.TxDropped), + RxBytes: stats.RxBytes, + TxBytes: stats.TxBytes, + RxPackets: stats.RxPackets, + TxPackets: stats.TxPackets, + RxDropped: stats.RxDropped, + TxDropped: stats.TxDropped, }, nil } @@ -422,8 +422,7 @@ func setInterfaceRoutes(nlh *netlink.Handle, iface netlink.Link, i *nwIface) err // we cannot gather the statistics from /sys/class/net//statistics/ files. Per-netns stats // are naturally found in /proc/net/dev in kernels which support netns (ifconfig relies on that). const ( - netStatsFile = "/proc/net/dev" - base = "[ ]*%s:([ ]+[0-9]+){16}" + base = "[ ]*%s:([ ]+[0-9]+){16}" ) func scanInterfaceStats(data, ifName string, i *types.InterfaceStatistics) error { diff --git a/vendor/github.com/docker/libnetwork/osl/interface_windows.go b/libnetwork/osl/interface_windows.go similarity index 100% rename from vendor/github.com/docker/libnetwork/osl/interface_windows.go rename to libnetwork/osl/interface_windows.go diff --git a/vendor/github.com/docker/libnetwork/osl/kernel/knobs.go b/libnetwork/osl/kernel/knobs.go similarity index 100% rename from vendor/github.com/docker/libnetwork/osl/kernel/knobs.go rename to libnetwork/osl/kernel/knobs.go diff --git a/vendor/github.com/docker/libnetwork/osl/kernel/knobs_linux.go b/libnetwork/osl/kernel/knobs_linux.go similarity index 89% rename from vendor/github.com/docker/libnetwork/osl/kernel/knobs_linux.go rename to libnetwork/osl/kernel/knobs_linux.go index 964280650c580..163dad746e1a2 100644 --- a/vendor/github.com/docker/libnetwork/osl/kernel/knobs_linux.go +++ b/libnetwork/osl/kernel/knobs_linux.go @@ -1,7 +1,7 @@ package kernel import ( - "io/ioutil" + "os" "path" "strings" @@ -12,13 +12,13 @@ import ( // For e.g. net.ipv4.ip_forward translated to /proc/sys/net/ipv4/ip_forward. func writeSystemProperty(key, value string) error { keyPath := strings.Replace(key, ".", "/", -1) - return ioutil.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644) + return os.WriteFile(path.Join("/proc/sys", keyPath), []byte(value), 0644) } // readSystemProperty reads the value from the path under /proc/sys and returns it func readSystemProperty(key string) (string, error) { keyPath := strings.Replace(key, ".", "/", -1) - value, err := ioutil.ReadFile(path.Join("/proc/sys", keyPath)) + value, err := os.ReadFile(path.Join("/proc/sys", keyPath)) if err != nil { return "", err } diff --git a/libnetwork/osl/kernel/knobs_linux_test.go b/libnetwork/osl/kernel/knobs_linux_test.go new file mode 100644 index 0000000000000..b6b5d856f8726 --- /dev/null +++ b/libnetwork/osl/kernel/knobs_linux_test.go @@ -0,0 +1,32 @@ +package kernel + +import ( + "testing" + + "github.com/sirupsen/logrus" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestReadWriteKnobs(t *testing.T) { + for _, k := range []string{ + "net.ipv4.neigh.default.gc_thresh1", + "net.ipv4.neigh.default.gc_thresh2", + "net.ipv4.neigh.default.gc_thresh3", + } { + // Check if the test is able to read the value + v, err := readSystemProperty(k) + if err != nil { + logrus.WithError(err).Warnf("Path %v not readable", k) + // the path is not there, skip this key + continue + } + // Test the write + assert.Check(t, writeSystemProperty(k, "10000")) + newV, err := readSystemProperty(k) + assert.NilError(t, err) + assert.Check(t, is.Equal(newV, "10000")) + // Restore value + assert.Check(t, writeSystemProperty(k, v)) + } +} diff --git a/vendor/github.com/docker/libnetwork/osl/kernel/knobs_unsupported.go b/libnetwork/osl/kernel/knobs_unsupported.go similarity index 89% rename from vendor/github.com/docker/libnetwork/osl/kernel/knobs_unsupported.go rename to libnetwork/osl/kernel/knobs_unsupported.go index 32f258f41625d..f0403b7ce0698 100644 --- a/vendor/github.com/docker/libnetwork/osl/kernel/knobs_unsupported.go +++ b/libnetwork/osl/kernel/knobs_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package kernel diff --git a/vendor/github.com/docker/libnetwork/osl/namespace_linux.go b/libnetwork/osl/namespace_linux.go similarity index 93% rename from vendor/github.com/docker/libnetwork/osl/namespace_linux.go rename to libnetwork/osl/namespace_linux.go index 3879b03444a61..1a8eeaec97493 100644 --- a/vendor/github.com/docker/libnetwork/osl/namespace_linux.go +++ b/libnetwork/osl/namespace_linux.go @@ -1,8 +1,8 @@ package osl import ( + "errors" "fmt" - "io/ioutil" "net" "os" "os/exec" @@ -14,13 +14,14 @@ import ( "syscall" "time" + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/osl/kernel" + "github.com/docker/docker/libnetwork/types" "github.com/docker/docker/pkg/reexec" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/osl/kernel" - "github.com/docker/libnetwork/types" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" + "golang.org/x/sys/unix" ) const defaultPrefix = "/var/run/docker" @@ -38,9 +39,15 @@ var ( gpmChan = make(chan chan struct{}) prefix = defaultPrefix loadBalancerConfig = map[string]*kernel.OSValue{ + // disables any special handling on port reuse of existing IPVS connection table entries + // more info: https://github.com/torvalds/linux/blob/master/Documentation/networking/ipvs-sysctl.txt#L25:1 + "net.ipv4.vs.conn_reuse_mode": {Value: "0", CheckFn: nil}, // expires connection from the IPVS connection table when the backend is not available // more info: https://github.com/torvalds/linux/blob/master/Documentation/networking/ipvs-sysctl.txt#L126:1 - "net.ipv4.vs.expire_nodest_conn": {"1", nil}, + "net.ipv4.vs.expire_nodest_conn": {Value: "1", CheckFn: nil}, + // expires persistent connections to destination servers with weights set to 0 + // more info: https://github.com/torvalds/linux/blob/master/Documentation/networking/ipvs-sysctl.txt#L144:1 + "net.ipv4.vs.expire_quiescent_template": {Value: "1", CheckFn: nil}, } ) @@ -164,7 +171,7 @@ func GenerateKey(containerID string) string { indexStr string tmpkey string ) - dir, err := ioutil.ReadDir(basePath()) + dir, err := os.ReadDir(basePath()) if err != nil { return "" } @@ -331,7 +338,9 @@ func createNetworkNamespace(path string, osCreate bool) error { func unmountNamespaceFile(path string) { if _, err := os.Stat(path); err == nil { - syscall.Unmount(path, syscall.MNT_DETACH) + if err := syscall.Unmount(path, syscall.MNT_DETACH); err != nil && !errors.Is(err, unix.EINVAL) { + logrus.WithError(err).Error("Error unmounting namespace file") + } } } @@ -398,12 +407,12 @@ func (n *networkNamespace) DisableARPForVIP(srcName string) (Err error) { err := n.InvokeFunc(func() { path := filepath.Join("/proc/sys/net/ipv4/conf", dstName, "arp_ignore") - if err := ioutil.WriteFile(path, []byte{'1', '\n'}, 0644); err != nil { + if err := os.WriteFile(path, []byte{'1', '\n'}, 0644); err != nil { Err = fmt.Errorf("Failed to set %s to 1: %v", path, err) return } path = filepath.Join("/proc/sys/net/ipv4/conf", dstName, "arp_announce") - if err := ioutil.WriteFile(path, []byte{'2', '\n'}, 0644); err != nil { + if err := os.WriteFile(path, []byte{'2', '\n'}, 0644); err != nil { Err = fmt.Errorf("Failed to set %s to 2: %v", path, err) return } @@ -655,7 +664,7 @@ func reexecSetIPv6() { os.Exit(5) } - if err = ioutil.WriteFile(path, []byte{value, '\n'}, 0644); err != nil { + if err = os.WriteFile(path, []byte{value, '\n'}, 0644); err != nil { logrus.Errorf("failed to %s IPv6 forwarding for container's interface %s: %v", action, os.Args[2], err) os.Exit(4) } diff --git a/vendor/github.com/docker/libnetwork/osl/namespace_unsupported.go b/libnetwork/osl/namespace_unsupported.go similarity index 90% rename from vendor/github.com/docker/libnetwork/osl/namespace_unsupported.go rename to libnetwork/osl/namespace_unsupported.go index 74372e2492e5f..4179459c136bd 100644 --- a/vendor/github.com/docker/libnetwork/osl/namespace_unsupported.go +++ b/libnetwork/osl/namespace_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows && !freebsd // +build !linux,!windows,!freebsd package osl diff --git a/vendor/github.com/docker/libnetwork/osl/namespace_windows.go b/libnetwork/osl/namespace_windows.go similarity index 81% rename from vendor/github.com/docker/libnetwork/osl/namespace_windows.go rename to libnetwork/osl/namespace_windows.go index 49503c00ffd23..2c30fa4110a73 100644 --- a/vendor/github.com/docker/libnetwork/osl/namespace_windows.go +++ b/libnetwork/osl/namespace_windows.go @@ -1,7 +1,5 @@ package osl -import "testing" - // GenerateKey generates a sandbox key based on the passed // container id. func GenerateKey(containerID string) string { @@ -28,11 +26,6 @@ func InitOSContext() func() { return func() {} } -// SetupTestOSContext sets up a separate test OS context in which tests will be executed. -func SetupTestOSContext(t *testing.T) func() { - return func() {} -} - // SetBasePath sets the base url prefix for the ns path func SetBasePath(path string) { } diff --git a/vendor/github.com/docker/libnetwork/osl/neigh_freebsd.go b/libnetwork/osl/neigh_freebsd.go similarity index 100% rename from vendor/github.com/docker/libnetwork/osl/neigh_freebsd.go rename to libnetwork/osl/neigh_freebsd.go diff --git a/vendor/github.com/docker/libnetwork/osl/neigh_linux.go b/libnetwork/osl/neigh_linux.go similarity index 97% rename from vendor/github.com/docker/libnetwork/osl/neigh_linux.go rename to libnetwork/osl/neigh_linux.go index 6bf1c16dc5acc..7105bf6ddea0d 100644 --- a/vendor/github.com/docker/libnetwork/osl/neigh_linux.go +++ b/libnetwork/osl/neigh_linux.go @@ -102,7 +102,9 @@ func (n *networkNamespace) DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr, if nh.linkDst != "" { nlnh.LinkIndex = iface.Attrs().Index } - nlh.NeighDel(nlnh) + if err := nlh.NeighDel(nlnh); err != nil { + logrus.WithError(err).Warn("error while deleting neighbor entry") + } } } diff --git a/vendor/github.com/docker/libnetwork/osl/neigh_windows.go b/libnetwork/osl/neigh_windows.go similarity index 100% rename from vendor/github.com/docker/libnetwork/osl/neigh_windows.go rename to libnetwork/osl/neigh_windows.go diff --git a/vendor/github.com/docker/libnetwork/osl/options_linux.go b/libnetwork/osl/options_linux.go similarity index 100% rename from vendor/github.com/docker/libnetwork/osl/options_linux.go rename to libnetwork/osl/options_linux.go diff --git a/vendor/github.com/docker/libnetwork/osl/route_linux.go b/libnetwork/osl/route_linux.go similarity index 98% rename from vendor/github.com/docker/libnetwork/osl/route_linux.go rename to libnetwork/osl/route_linux.go index a9ff191b373d0..c1cc543b2df57 100644 --- a/vendor/github.com/docker/libnetwork/osl/route_linux.go +++ b/libnetwork/osl/route_linux.go @@ -4,7 +4,7 @@ import ( "fmt" "net" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/types" "github.com/vishvananda/netlink" ) diff --git a/libnetwork/osl/sandbox.go b/libnetwork/osl/sandbox.go new file mode 100644 index 0000000000000..e738231d361d6 --- /dev/null +++ b/libnetwork/osl/sandbox.go @@ -0,0 +1,191 @@ +// Package osl describes structures and interfaces which abstract os entities +package osl + +import ( + "net" + + "github.com/docker/docker/libnetwork/types" +) + +// SandboxType specify the time of the sandbox, this can be used to apply special configs +type SandboxType int + +const ( + // SandboxTypeIngress indicates that the sandbox is for the ingress + SandboxTypeIngress = iota + // SandboxTypeLoadBalancer indicates that the sandbox is a load balancer + SandboxTypeLoadBalancer = iota +) + +// Sandbox represents a network sandbox, identified by a specific key. It +// holds a list of Interfaces, routes etc, and more can be added dynamically. +type Sandbox interface { + // The path where the network namespace is mounted. + Key() string + + // Add an existing Interface to this sandbox. The operation will rename + // from the Interface SrcName to DstName as it moves, and reconfigure the + // interface according to the specified settings. The caller is expected + // to only provide a prefix for DstName. The AddInterface api will auto-generate + // an appropriate suffix for the DstName to disambiguate. + AddInterface(SrcName string, DstPrefix string, options ...IfaceOption) error + + // Set default IPv4 gateway for the sandbox + SetGateway(gw net.IP) error + + // Set default IPv6 gateway for the sandbox + SetGatewayIPv6(gw net.IP) error + + // Unset the previously set default IPv4 gateway in the sandbox + UnsetGateway() error + + // Unset the previously set default IPv6 gateway in the sandbox + UnsetGatewayIPv6() error + + // GetLoopbackIfaceName returns the name of the loopback interface + GetLoopbackIfaceName() string + + // AddAliasIP adds the passed IP address to the named interface + AddAliasIP(ifName string, ip *net.IPNet) error + + // RemoveAliasIP removes the passed IP address from the named interface + RemoveAliasIP(ifName string, ip *net.IPNet) error + + // DisableARPForVIP disables ARP replies and requests for VIP addresses + // on a particular interface + DisableARPForVIP(ifName string) error + + // Add a static route to the sandbox. + AddStaticRoute(*types.StaticRoute) error + + // Remove a static route from the sandbox. + RemoveStaticRoute(*types.StaticRoute) error + + // AddNeighbor adds a neighbor entry into the sandbox. + AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, force bool, option ...NeighOption) error + + // DeleteNeighbor deletes neighbor entry from the sandbox. + DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr, osDelete bool) error + + // Returns an interface with methods to set neighbor options. + NeighborOptions() NeighborOptionSetter + + // Returns an interface with methods to set interface options. + InterfaceOptions() IfaceOptionSetter + + //Invoke + InvokeFunc(func()) error + + // Returns an interface with methods to get sandbox state. + Info() Info + + // Destroy the sandbox + Destroy() error + + // restore sandbox + Restore(ifsopt map[string][]IfaceOption, routes []*types.StaticRoute, gw net.IP, gw6 net.IP) error + + // ApplyOSTweaks applies operating system specific knobs on the sandbox + ApplyOSTweaks([]SandboxType) +} + +// NeighborOptionSetter interface defines the option setter methods for interface options +type NeighborOptionSetter interface { + // LinkName returns an option setter to set the srcName of the link that should + // be used in the neighbor entry + LinkName(string) NeighOption + + // Family returns an option setter to set the address family for the neighbor + // entry. eg. AF_BRIDGE + Family(int) NeighOption +} + +// IfaceOptionSetter interface defines the option setter methods for interface options. +type IfaceOptionSetter interface { + // Bridge returns an option setter to set if the interface is a bridge. + Bridge(bool) IfaceOption + + // MacAddress returns an option setter to set the MAC address. + MacAddress(net.HardwareAddr) IfaceOption + + // Address returns an option setter to set IPv4 address. + Address(*net.IPNet) IfaceOption + + // Address returns an option setter to set IPv6 address. + AddressIPv6(*net.IPNet) IfaceOption + + // LinkLocalAddresses returns an option setter to set the link-local IP addresses. + LinkLocalAddresses([]*net.IPNet) IfaceOption + + // Master returns an option setter to set the master interface if any for this + // interface. The master interface name should refer to the srcname of a + // previously added interface of type bridge. + Master(string) IfaceOption + + // Address returns an option setter to set interface routes. + Routes([]*net.IPNet) IfaceOption +} + +// Info represents all possible information that +// the driver wants to place in the sandbox which includes +// interfaces, routes and gateway +type Info interface { + // The collection of Interface previously added with the AddInterface + // method. Note that this doesn't include network interfaces added in any + // other way (such as the default loopback interface which is automatically + // created on creation of a sandbox). + Interfaces() []Interface + + // IPv4 gateway for the sandbox. + Gateway() net.IP + + // IPv6 gateway for the sandbox. + GatewayIPv6() net.IP + + // Additional static routes for the sandbox. (Note that directly + // connected routes are stored on the particular interface they refer to.) + StaticRoutes() []*types.StaticRoute + + // TODO: Add ip tables etc. +} + +// Interface represents the settings and identity of a network device. It is +// used as a return type for Network.Link, and it is common practice for the +// caller to use this information when moving interface SrcName from host +// namespace to DstName in a different net namespace with the appropriate +// network settings. +type Interface interface { + // The name of the interface in the origin network namespace. + SrcName() string + + // The name that will be assigned to the interface once moves inside a + // network namespace. When the caller passes in a DstName, it is only + // expected to pass a prefix. The name will modified with an appropriately + // auto-generated suffix. + DstName() string + + // IPv4 address for the interface. + Address() *net.IPNet + + // IPv6 address for the interface. + AddressIPv6() *net.IPNet + + // LinkLocalAddresses returns the link-local IP addresses assigned to the interface. + LinkLocalAddresses() []*net.IPNet + + // IP routes for the interface. + Routes() []*net.IPNet + + // Bridge returns true if the interface is a bridge + Bridge() bool + + // Master returns the srcname of the master interface for this interface. + Master() string + + // Remove an interface from the sandbox by renaming to original name + // and moving it out of the sandbox. + Remove() error + + // Statistics returns the statistics for this interface + Statistics() (*types.InterfaceStatistics, error) +} diff --git a/vendor/github.com/docker/libnetwork/osl/sandbox_freebsd.go b/libnetwork/osl/sandbox_freebsd.go similarity index 84% rename from vendor/github.com/docker/libnetwork/osl/sandbox_freebsd.go rename to libnetwork/osl/sandbox_freebsd.go index e5bc6278ee90c..c09b388a07d9e 100644 --- a/vendor/github.com/docker/libnetwork/osl/sandbox_freebsd.go +++ b/libnetwork/osl/sandbox_freebsd.go @@ -1,7 +1,5 @@ package osl -import "testing" - // GenerateKey generates a sandbox key based on the passed // container id. func GenerateKey(containerID string) string { @@ -34,11 +32,6 @@ func InitOSContext() func() { return func() {} } -// SetupTestOSContext sets up a separate test OS context in which tests will be executed. -func SetupTestOSContext(t *testing.T) func() { - return func() {} -} - // SetBasePath sets the base url prefix for the ns path func SetBasePath(path string) { } diff --git a/libnetwork/osl/sandbox_linux_test.go b/libnetwork/osl/sandbox_linux_test.go new file mode 100644 index 0000000000000..ed800828a7eb3 --- /dev/null +++ b/libnetwork/osl/sandbox_linux_test.go @@ -0,0 +1,583 @@ +package osl + +import ( + "crypto/rand" + "encoding/hex" + "io" + "net" + "os" + "path/filepath" + "runtime" + "strings" + "syscall" + "testing" + "time" + + "github.com/docker/docker/libnetwork/ns" + "github.com/docker/docker/libnetwork/testutils" + "github.com/docker/docker/libnetwork/types" + "github.com/docker/docker/pkg/reexec" + "github.com/vishvananda/netlink" + "github.com/vishvananda/netlink/nl" + "github.com/vishvananda/netns" +) + +const ( + vethName1 = "wierdlongname1" + vethName2 = "wierdlongname2" + vethName3 = "wierdlongname3" + vethName4 = "wierdlongname4" + sboxIfaceName = "containername" +) + +func generateRandomName(prefix string, size int) (string, error) { + id := make([]byte, 32) + if _, err := io.ReadFull(rand.Reader, id); err != nil { + return "", err + } + return prefix + hex.EncodeToString(id)[:size], nil +} + +func newKey(t *testing.T) (string, error) { + name, err := generateRandomName("netns", 12) + if err != nil { + return "", err + } + + name = filepath.Join("/tmp", name) + if _, err := os.Create(name); err != nil { + return "", err + } + + // Set the rpmCleanupPeriod to be low to make the test run quicker + gpmLock.Lock() + gpmCleanupPeriod = 2 * time.Second + gpmLock.Unlock() + + return name, nil +} + +func newInfo(hnd *netlink.Handle, t *testing.T) (Sandbox, error) { + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{Name: vethName1, TxQLen: 0}, + PeerName: vethName2} + if err := hnd.LinkAdd(veth); err != nil { + return nil, err + } + + // Store the sandbox side pipe interface + // This is needed for cleanup on DeleteEndpoint() + intf1 := &nwIface{} + intf1.srcName = vethName2 + intf1.dstName = sboxIfaceName + + ip4, addr, err := net.ParseCIDR("192.168.1.100/24") + if err != nil { + return nil, err + } + intf1.address = addr + intf1.address.IP = ip4 + + ip6, addrv6, err := net.ParseCIDR("fe80::2/64") + if err != nil { + return nil, err + } + intf1.addressIPv6 = addrv6 + intf1.addressIPv6.IP = ip6 + + _, route, err := net.ParseCIDR("192.168.2.1/32") + if err != nil { + return nil, err + } + + intf1.routes = []*net.IPNet{route} + + intf2 := &nwIface{} + intf2.srcName = "testbridge" + intf2.dstName = sboxIfaceName + intf2.bridge = true + + veth = &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{Name: vethName3, TxQLen: 0}, + PeerName: vethName4} + + if err := hnd.LinkAdd(veth); err != nil { + return nil, err + } + + intf3 := &nwIface{} + intf3.srcName = vethName4 + intf3.dstName = sboxIfaceName + intf3.master = "testbridge" + + info := &networkNamespace{iFaces: []*nwIface{intf1, intf2, intf3}} + + info.gw = net.ParseIP("192.168.1.1") + info.gwv6 = net.ParseIP("fe80::1") + + return info, nil +} + +func verifySandbox(t *testing.T, s Sandbox, ifaceSuffixes []string) { + _, ok := s.(*networkNamespace) + if !ok { + t.Fatalf("The sandbox interface returned is not of type networkNamespace") + } + + sbNs, err := netns.GetFromPath(s.Key()) + if err != nil { + t.Fatalf("Failed top open network namespace path %q: %v", s.Key(), err) + } + defer sbNs.Close() + + nh, err := netlink.NewHandleAt(sbNs) + if err != nil { + t.Fatal(err) + } + defer nh.Delete() + + for _, suffix := range ifaceSuffixes { + _, err = nh.LinkByName(sboxIfaceName + suffix) + if err != nil { + t.Fatalf("Could not find the interface %s inside the sandbox: %v", + sboxIfaceName+suffix, err) + } + } +} + +func verifyCleanup(t *testing.T, s Sandbox, wait bool) { + if wait { + time.Sleep(gpmCleanupPeriod * 2) + } + + if _, err := os.Stat(s.Key()); err == nil { + if wait { + t.Fatalf("The sandbox path %s is not getting cleaned up even after twice the cleanup period", s.Key()) + } else { + t.Fatalf("The sandbox path %s is not cleaned up after running gc", s.Key()) + } + } +} + +func TestScanStatistics(t *testing.T) { + data := + "Inter-| Receive | Transmit\n" + + " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n" + + " eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" + + " wlan0: 7787685 11141 0 0 0 0 0 0 1681390 7220 0 0 0 0 0 0\n" + + " lo: 783782 1853 0 0 0 0 0 0 783782 1853 0 0 0 0 0 0\n" + + "lxcbr0: 0 0 0 0 0 0 0 0 9006 61 0 0 0 0 0 0\n" + + i := &types.InterfaceStatistics{} + + if err := scanInterfaceStats(data, "wlan0", i); err != nil { + t.Fatal(err) + } + if i.TxBytes != 1681390 || i.TxPackets != 7220 || i.RxBytes != 7787685 || i.RxPackets != 11141 { + t.Fatalf("Error scanning the statistics") + } + + if err := scanInterfaceStats(data, "lxcbr0", i); err != nil { + t.Fatal(err) + } + if i.TxBytes != 9006 || i.TxPackets != 61 || i.RxBytes != 0 || i.RxPackets != 0 { + t.Fatalf("Error scanning the statistics") + } +} + +func TestDisableIPv6DAD(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + key, err := newKey(t) + if err != nil { + t.Fatalf("Failed to obtain a key: %v", err) + } + + s, err := NewSandbox(key, true, false) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + runtime.LockOSThread() + defer destroyTest(t, s) + + n, ok := s.(*networkNamespace) + if !ok { + t.Fatal(ok) + } + nlh := n.nlHandle + + ipv6, _ := types.ParseCIDR("2001:db8::44/64") + iface := &nwIface{addressIPv6: ipv6, ns: n, dstName: "sideA"} + + veth := &netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{Name: "sideA"}, + PeerName: "sideB", + } + + err = nlh.LinkAdd(veth) + if err != nil { + t.Fatal(err) + } + + link, err := nlh.LinkByName("sideA") + if err != nil { + t.Fatal(err) + } + + err = setInterfaceIPv6(nlh, link, iface) + if err != nil { + t.Fatal(err) + } + + addrList, err := nlh.AddrList(link, nl.FAMILY_V6) + if err != nil { + t.Fatal(err) + } + + if addrList[0].Flags&syscall.IFA_F_NODAD == 0 { + t.Fatalf("Unexpected interface flags: 0x%x. Expected to contain 0x%x", addrList[0].Flags, syscall.IFA_F_NODAD) + } +} + +func destroyTest(t *testing.T, s Sandbox) { + if err := s.Destroy(); err != nil { + t.Log(err) + } +} + +func TestSetInterfaceIP(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + key, err := newKey(t) + if err != nil { + t.Fatalf("Failed to obtain a key: %v", err) + } + + s, err := NewSandbox(key, true, false) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + runtime.LockOSThread() + defer destroyTest(t, s) + + n, ok := s.(*networkNamespace) + if !ok { + t.Fatal(ok) + } + nlh := n.nlHandle + + ipv4, _ := types.ParseCIDR("172.30.0.33/24") + ipv6, _ := types.ParseCIDR("2001:db8::44/64") + iface := &nwIface{address: ipv4, addressIPv6: ipv6, ns: n, dstName: "sideA"} + + if err := nlh.LinkAdd(&netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{Name: "sideA"}, + PeerName: "sideB", + }); err != nil { + t.Fatal(err) + } + + linkA, err := nlh.LinkByName("sideA") + if err != nil { + t.Fatal(err) + } + + linkB, err := nlh.LinkByName("sideB") + if err != nil { + t.Fatal(err) + } + + if err := nlh.LinkSetUp(linkA); err != nil { + t.Fatal(err) + } + + if err := nlh.LinkSetUp(linkB); err != nil { + t.Fatal(err) + } + + if err := setInterfaceIP(nlh, linkA, iface); err != nil { + t.Fatal(err) + } + + if err := setInterfaceIPv6(nlh, linkA, iface); err != nil { + t.Fatal(err) + } + + err = setInterfaceIP(nlh, linkB, iface) + if err == nil { + t.Fatalf("Expected route conflict error, but succeeded") + } + if !strings.Contains(err.Error(), "conflicts with existing route") { + t.Fatalf("Unexpected error: %v", err) + } + + err = setInterfaceIPv6(nlh, linkB, iface) + if err == nil { + t.Fatalf("Expected route conflict error, but succeeded") + } + if !strings.Contains(err.Error(), "conflicts with existing route") { + t.Fatalf("Unexpected error: %v", err) + } +} + +func TestLiveRestore(t *testing.T) { + + defer testutils.SetupTestOSContext(t)() + + key, err := newKey(t) + if err != nil { + t.Fatalf("Failed to obtain a key: %v", err) + } + + s, err := NewSandbox(key, true, false) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + runtime.LockOSThread() + defer destroyTest(t, s) + + n, ok := s.(*networkNamespace) + if !ok { + t.Fatal(ok) + } + nlh := n.nlHandle + + ipv4, _ := types.ParseCIDR("172.30.0.33/24") + ipv6, _ := types.ParseCIDR("2001:db8::44/64") + iface := &nwIface{address: ipv4, addressIPv6: ipv6, ns: n, dstName: "sideA"} + + if err := nlh.LinkAdd(&netlink.Veth{ + LinkAttrs: netlink.LinkAttrs{Name: "sideA"}, + PeerName: "sideB", + }); err != nil { + t.Fatal(err) + } + + linkA, err := nlh.LinkByName("sideA") + if err != nil { + t.Fatal(err) + } + + linkB, err := nlh.LinkByName("sideB") + if err != nil { + t.Fatal(err) + } + + if err := nlh.LinkSetUp(linkA); err != nil { + t.Fatal(err) + } + + if err := nlh.LinkSetUp(linkB); err != nil { + t.Fatal(err) + } + + if err := setInterfaceIP(nlh, linkA, iface); err != nil { + t.Fatal(err) + } + + if err := setInterfaceIPv6(nlh, linkA, iface); err != nil { + t.Fatal(err) + } + + err = setInterfaceIP(nlh, linkB, iface) + if err == nil { + t.Fatalf("Expected route conflict error, but succeeded") + } + if !strings.Contains(err.Error(), "conflicts with existing route") { + t.Fatalf("Unexpected error: %v", err) + } + + err = setInterfaceIPv6(nlh, linkB, iface) + if err == nil { + t.Fatalf("Expected route conflict error, but succeeded") + } + if !strings.Contains(err.Error(), "conflicts with existing route") { + t.Fatalf("Unexpected error: %v", err) + } + + // Create newsandbox with Restore - TRUE + s, err = NewSandbox(key, true, true) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + defer destroyTest(t, s) + + // Check if the IPV4 & IPV6 entry present + // If present , we should get error in below call + // It shows us , we don't delete any config in live-restore case + if err := setInterfaceIPv6(nlh, linkA, iface); err == nil { + t.Fatalf("Expected route conflict error, but succeeded for IPV6 ") + } + if err := setInterfaceIP(nlh, linkA, iface); err == nil { + t.Fatalf("Expected route conflict error, but succeeded for IPV4 ") + } +} + +func TestMain(m *testing.M) { + if reexec.Init() { + return + } + os.Exit(m.Run()) +} + +func TestSandboxCreate(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + key, err := newKey(t) + if err != nil { + t.Fatalf("Failed to obtain a key: %v", err) + } + + s, err := NewSandbox(key, true, false) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + + if s.Key() != key { + t.Fatalf("s.Key() returned %s. Expected %s", s.Key(), key) + } + + tbox, err := newInfo(ns.NlHandle(), t) + if err != nil { + t.Fatalf("Failed to generate new sandbox info: %v", err) + } + + for _, i := range tbox.Info().Interfaces() { + err = s.AddInterface(i.SrcName(), i.DstName(), + tbox.InterfaceOptions().Bridge(i.Bridge()), + tbox.InterfaceOptions().Address(i.Address()), + tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6())) + if err != nil { + t.Fatalf("Failed to add interfaces to sandbox: %v", err) + } + } + + err = s.SetGateway(tbox.Info().Gateway()) + if err != nil { + t.Fatalf("Failed to set gateway to sandbox: %v", err) + } + + err = s.SetGatewayIPv6(tbox.Info().GatewayIPv6()) + if err != nil { + t.Fatalf("Failed to set ipv6 gateway to sandbox: %v", err) + } + + verifySandbox(t, s, []string{"0", "1", "2"}) + + err = s.Destroy() + if err != nil { + t.Fatal(err) + } + verifyCleanup(t, s, true) +} + +func TestSandboxCreateTwice(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + key, err := newKey(t) + if err != nil { + t.Fatalf("Failed to obtain a key: %v", err) + } + + _, err = NewSandbox(key, true, false) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + runtime.LockOSThread() + + // Create another sandbox with the same key to see if we handle it + // gracefully. + s, err := NewSandbox(key, true, false) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + runtime.LockOSThread() + + err = s.Destroy() + if err != nil { + t.Fatal(err) + } + GC() + verifyCleanup(t, s, false) +} + +func TestSandboxGC(t *testing.T) { + key, err := newKey(t) + if err != nil { + t.Fatalf("Failed to obtain a key: %v", err) + } + + s, err := NewSandbox(key, true, false) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + + err = s.Destroy() + if err != nil { + t.Fatal(err) + } + + GC() + verifyCleanup(t, s, false) +} + +func TestAddRemoveInterface(t *testing.T) { + defer testutils.SetupTestOSContext(t)() + + key, err := newKey(t) + if err != nil { + t.Fatalf("Failed to obtain a key: %v", err) + } + + s, err := NewSandbox(key, true, false) + if err != nil { + t.Fatalf("Failed to create a new sandbox: %v", err) + } + runtime.LockOSThread() + + if s.Key() != key { + t.Fatalf("s.Key() returned %s. Expected %s", s.Key(), key) + } + + tbox, err := newInfo(ns.NlHandle(), t) + if err != nil { + t.Fatalf("Failed to generate new sandbox info: %v", err) + } + + for _, i := range tbox.Info().Interfaces() { + err = s.AddInterface(i.SrcName(), i.DstName(), + tbox.InterfaceOptions().Bridge(i.Bridge()), + tbox.InterfaceOptions().Address(i.Address()), + tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6())) + if err != nil { + t.Fatalf("Failed to add interfaces to sandbox: %v", err) + } + } + + verifySandbox(t, s, []string{"0", "1", "2"}) + + interfaces := s.Info().Interfaces() + if err := interfaces[0].Remove(); err != nil { + t.Fatalf("Failed to remove interfaces from sandbox: %v", err) + } + + verifySandbox(t, s, []string{"1", "2"}) + + i := tbox.Info().Interfaces()[0] + if err := s.AddInterface(i.SrcName(), i.DstName(), + tbox.InterfaceOptions().Bridge(i.Bridge()), + tbox.InterfaceOptions().Address(i.Address()), + tbox.InterfaceOptions().AddressIPv6(i.AddressIPv6())); err != nil { + t.Fatalf("Failed to add interfaces to sandbox: %v", err) + } + + verifySandbox(t, s, []string{"1", "2", "3"}) + + err = s.Destroy() + if err != nil { + t.Fatal(err) + } + + GC() + verifyCleanup(t, s, false) +} diff --git a/vendor/github.com/docker/libnetwork/osl/sandbox_unsupported.go b/libnetwork/osl/sandbox_unsupported.go similarity index 93% rename from vendor/github.com/docker/libnetwork/osl/sandbox_unsupported.go rename to libnetwork/osl/sandbox_unsupported.go index 51a656c806fdd..8e811a4617dbc 100644 --- a/vendor/github.com/docker/libnetwork/osl/sandbox_unsupported.go +++ b/libnetwork/osl/sandbox_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows && !freebsd // +build !linux,!windows,!freebsd package osl diff --git a/libnetwork/osl/sandbox_unsupported_test.go b/libnetwork/osl/sandbox_unsupported_test.go new file mode 100644 index 0000000000000..32b4657f2b529 --- /dev/null +++ b/libnetwork/osl/sandbox_unsupported_test.go @@ -0,0 +1,19 @@ +//go:build !linux +// +build !linux + +package osl + +import ( + "errors" + "testing" +) + +var ErrNotImplemented = errors.New("not implemented") + +func newKey(t *testing.T) (string, error) { + return "", ErrNotImplemented +} + +func verifySandbox(t *testing.T, s Sandbox) { + return +} diff --git a/vendor/github.com/docker/libnetwork/portallocator/portallocator.go b/libnetwork/portallocator/portallocator.go similarity index 77% rename from vendor/github.com/docker/libnetwork/portallocator/portallocator.go rename to libnetwork/portallocator/portallocator.go index 191b478ccd40f..1dc935cbd744d 100644 --- a/vendor/github.com/docker/libnetwork/portallocator/portallocator.go +++ b/libnetwork/portallocator/portallocator.go @@ -1,5 +1,3 @@ -// +build !windows - package portallocator import ( @@ -7,15 +5,26 @@ import ( "fmt" "net" "sync" -) -const ( - // DefaultPortRangeStart indicates the first port in port range - DefaultPortRangeStart = 49153 - // DefaultPortRangeEnd indicates the last port in port range - DefaultPortRangeEnd = 65535 + "github.com/sirupsen/logrus" ) +func sanitizePortRange(start int, end int) (newStart, newEnd int, err error) { + if start > defaultPortRangeEnd || end < defaultPortRangeStart || start > end { + return 0, 0, fmt.Errorf("Request out allowed range [%v, %v]", + defaultPortRangeStart, defaultPortRangeEnd) + } + err = nil + newStart, newEnd = start, end + if start < defaultPortRangeStart { + newStart = defaultPortRangeStart + } + if end > defaultPortRangeEnd { + newEnd = defaultPortRangeEnd + } + return +} + type ipMapping map[string]protoMap var ( @@ -26,7 +35,6 @@ var ( defaultIP = net.ParseIP("0.0.0.0") once sync.Once instance *PortAllocator - createInstance = func() { instance = newInstance() } ) // ErrPortAlreadyAllocated is the returned error information when a requested port is already being used @@ -90,15 +98,26 @@ func Get() *PortAllocator { // the OS so that it can have up to date view of the OS port allocation. // When this happens singleton behavior will be removed. Clients do not // need to worry about this, they will not see a change in behavior. - once.Do(createInstance) + once.Do(func() { + instance = newInstance() + }) return instance } -func newInstance() *PortAllocator { +func getDefaultPortRange() (int, int) { start, end, err := getDynamicPortRange() + if err == nil { + start, end, err = sanitizePortRange(start, end) + } if err != nil { - start, end = DefaultPortRangeStart, DefaultPortRangeEnd + logrus.WithError(err).Infof("falling back to default port range %d-%d", defaultPortRangeStart, defaultPortRangeEnd) + start, end = defaultPortRangeStart, defaultPortRangeEnd } + return start, end +} + +func newInstance() *PortAllocator { + start, end := getDefaultPortRange() return &PortAllocator{ ipMap: ipMapping{}, Begin: start, @@ -172,6 +191,30 @@ func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error { return nil } +// SetPortRange sets dynamic port allocation range. +// if both portBegin and portEnd are 0, the port range reverts to default +// value. Otherwise they are sanitized against the default values to +// ensure their validity. +func (p *PortAllocator) SetPortRange(portBegin, portEnd int) error { + // if begin and end is zero, revert to default values + var begin, end int + var err error + if portBegin == 0 && portEnd == 0 { + begin, end = getDefaultPortRange() + } else if begin, end, err = sanitizePortRange(portBegin, portEnd); err != nil { + return err + } + logrus.Debugf("Setting up port allocator to range %v-%v, current %v-%v", begin, end, p.Begin, p.End) + p.mutex.Lock() + defer p.mutex.Unlock() + if p.Begin == begin && p.End == end { + return nil + } + p.ipMap = ipMapping{} + p.Begin, p.End = begin, end + return nil +} + func (p *PortAllocator) newPortMap() *portMap { defaultKey := getRangeKey(p.Begin, p.End) pm := &portMap{ diff --git a/libnetwork/portallocator/portallocator_freebsd.go b/libnetwork/portallocator/portallocator_freebsd.go new file mode 100644 index 0000000000000..c23912b6fc5f0 --- /dev/null +++ b/libnetwork/portallocator/portallocator_freebsd.go @@ -0,0 +1,41 @@ +package portallocator + +import ( + "bytes" + "fmt" + "os/exec" +) + +func getDynamicPortRange() (start int, end int, err error) { + portRangeKernelSysctl := []string{"net.inet.ip.portrange.hifirst", "net.ip.portrange.hilast"} + portRangeLowCmd := exec.Command("/sbin/sysctl", portRangeKernelSysctl[0]) + var portRangeLowOut bytes.Buffer + portRangeLowCmd.Stdout = &portRangeLowOut + cmdErr := portRangeLowCmd.Run() + if cmdErr != nil { + return 0, 0, fmt.Errorf("port allocator - sysctl net.inet.ip.portrange.hifirst failed: %v", err) + } + n, err := fmt.Sscanf(portRangeLowOut.String(), "%d", &start) + if n != 1 || err != nil { + if err == nil { + err = fmt.Errorf("unexpected count of parsed numbers (%d)", n) + } + return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range start from %s: %v", portRangeLowOut.String(), err) + } + + portRangeHighCmd := exec.Command("/sbin/sysctl", portRangeKernelSysctl[1]) + var portRangeHighOut bytes.Buffer + portRangeHighCmd.Stdout = &portRangeHighOut + cmdErr = portRangeHighCmd.Run() + if cmdErr != nil { + return 0, 0, fmt.Errorf("port allocator - sysctl net.inet.ip.portrange.hilast failed: %v", err) + } + n, err = fmt.Sscanf(portRangeHighOut.String(), "%d", &end) + if n != 1 || err != nil { + if err == nil { + err = fmt.Errorf("unexpected count of parsed numbers (%d)", n) + } + return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range end from %s: %v", portRangeHighOut.String(), err) + } + return start, end, nil +} diff --git a/libnetwork/portallocator/portallocator_linux.go b/libnetwork/portallocator/portallocator_linux.go new file mode 100644 index 0000000000000..ea991fad98cb2 --- /dev/null +++ b/libnetwork/portallocator/portallocator_linux.go @@ -0,0 +1,25 @@ +package portallocator + +import ( + "bufio" + "fmt" + "os" +) + +func getDynamicPortRange() (start int, end int, err error) { + const portRangeKernelParam = "/proc/sys/net/ipv4/ip_local_port_range" + file, err := os.Open(portRangeKernelParam) + if err != nil { + return 0, 0, err + } + defer file.Close() + + n, err := fmt.Fscanf(bufio.NewReader(file), "%d\t%d", &start, &end) + if n != 2 || err != nil { + if err == nil { + err = fmt.Errorf("unexpected count of parsed numbers (%d)", n) + } + return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range from %s: %v", portRangeKernelParam, err) + } + return start, end, nil +} diff --git a/libnetwork/portallocator/portallocator_test.go b/libnetwork/portallocator/portallocator_test.go new file mode 100644 index 0000000000000..8a34c3cbbbd48 --- /dev/null +++ b/libnetwork/portallocator/portallocator_test.go @@ -0,0 +1,368 @@ +package portallocator + +import ( + "fmt" + "net" + "testing" +) + +func resetPortAllocator() { + instance = newInstance() +} + +func TestRequestNewPort(t *testing.T) { + p := Get() + defer resetPortAllocator() + + port, err := p.RequestPort(defaultIP, "tcp", 0) + if err != nil { + t.Fatal(err) + } + + if expected := p.Begin; port != expected { + t.Fatalf("Expected port %d got %d", expected, port) + } +} + +func TestRequestSpecificPort(t *testing.T) { + p := Get() + defer resetPortAllocator() + + port, err := p.RequestPort(defaultIP, "tcp", 5000) + if err != nil { + t.Fatal(err) + } + + if port != 5000 { + t.Fatalf("Expected port 5000 got %d", port) + } +} + +func TestReleasePort(t *testing.T) { + p := Get() + + port, err := p.RequestPort(defaultIP, "tcp", 5000) + if err != nil { + t.Fatal(err) + } + if port != 5000 { + t.Fatalf("Expected port 5000 got %d", port) + } + + if err := p.ReleasePort(defaultIP, "tcp", 5000); err != nil { + t.Fatal(err) + } +} + +func TestReuseReleasedPort(t *testing.T) { + p := Get() + defer resetPortAllocator() + + port, err := p.RequestPort(defaultIP, "tcp", 5000) + if err != nil { + t.Fatal(err) + } + if port != 5000 { + t.Fatalf("Expected port 5000 got %d", port) + } + + if err := p.ReleasePort(defaultIP, "tcp", 5000); err != nil { + t.Fatal(err) + } + + port, err = p.RequestPort(defaultIP, "tcp", 5000) + if err != nil { + t.Fatal(err) + } + if port != 5000 { + t.Fatalf("Expected port 5000 got %d", port) + } +} + +func TestReleaseUnreadledPort(t *testing.T) { + p := Get() + defer resetPortAllocator() + + port, err := p.RequestPort(defaultIP, "tcp", 5000) + if err != nil { + t.Fatal(err) + } + if port != 5000 { + t.Fatalf("Expected port 5000 got %d", port) + } + + _, err = p.RequestPort(defaultIP, "tcp", 5000) + + switch err.(type) { + case ErrPortAlreadyAllocated: + default: + t.Fatalf("Expected port allocation error got %s", err) + } +} + +func TestUnknowProtocol(t *testing.T) { + if _, err := Get().RequestPort(defaultIP, "tcpp", 0); err != ErrUnknownProtocol { + t.Fatalf("Expected error %s got %s", ErrUnknownProtocol, err) + } +} + +func TestAllocateAllPorts(t *testing.T) { + p := Get() + defer resetPortAllocator() + + for i := 0; i <= p.End-p.Begin; i++ { + port, err := p.RequestPort(defaultIP, "tcp", 0) + if err != nil { + t.Fatal(err) + } + + if expected := p.Begin + i; port != expected { + t.Fatalf("Expected port %d got %d", expected, port) + } + } + + if _, err := p.RequestPort(defaultIP, "tcp", 0); err != ErrAllPortsAllocated { + t.Fatalf("Expected error %s got %s", ErrAllPortsAllocated, err) + } + + _, err := p.RequestPort(defaultIP, "udp", 0) + if err != nil { + t.Fatal(err) + } + + // release a port in the middle and ensure we get another tcp port + port := p.Begin + 5 + if err := p.ReleasePort(defaultIP, "tcp", port); err != nil { + t.Fatal(err) + } + newPort, err := p.RequestPort(defaultIP, "tcp", 0) + if err != nil { + t.Fatal(err) + } + if newPort != port { + t.Fatalf("Expected port %d got %d", port, newPort) + } + + // now pm.last == newPort, release it so that it's the only free port of + // the range, and ensure we get it back + if err := p.ReleasePort(defaultIP, "tcp", newPort); err != nil { + t.Fatal(err) + } + port, err = p.RequestPort(defaultIP, "tcp", 0) + if err != nil { + t.Fatal(err) + } + if newPort != port { + t.Fatalf("Expected port %d got %d", newPort, port) + } +} + +func BenchmarkAllocatePorts(b *testing.B) { + p := Get() + defer resetPortAllocator() + + for i := 0; i < b.N; i++ { + for i := 0; i <= p.End-p.Begin; i++ { + port, err := p.RequestPort(defaultIP, "tcp", 0) + if err != nil { + b.Fatal(err) + } + + if expected := p.Begin + i; port != expected { + b.Fatalf("Expected port %d got %d", expected, port) + } + } + if err := p.ReleaseAll(); err != nil { + b.Fatal(err) + } + } +} + +func TestPortAllocation(t *testing.T) { + p := Get() + defer resetPortAllocator() + + ip := net.ParseIP("192.168.0.1") + ip2 := net.ParseIP("192.168.0.2") + if port, err := p.RequestPort(ip, "tcp", 80); err != nil { + t.Fatal(err) + } else if port != 80 { + t.Fatalf("Acquire(80) should return 80, not %d", port) + } + port, err := p.RequestPort(ip, "tcp", 0) + if err != nil { + t.Fatal(err) + } + if port <= 0 { + t.Fatalf("Acquire(0) should return a non-zero port") + } + + if _, err := p.RequestPort(ip, "tcp", port); err == nil { + t.Fatalf("Acquiring a port already in use should return an error") + } + + if newPort, err := p.RequestPort(ip, "tcp", 0); err != nil { + t.Fatal(err) + } else if newPort == port { + t.Fatalf("Acquire(0) allocated the same port twice: %d", port) + } + + if _, err := p.RequestPort(ip, "tcp", 80); err == nil { + t.Fatalf("Acquiring a port already in use should return an error") + } + if _, err := p.RequestPort(ip2, "tcp", 80); err != nil { + t.Fatalf("It should be possible to allocate the same port on a different interface") + } + if _, err := p.RequestPort(ip2, "tcp", 80); err == nil { + t.Fatalf("Acquiring a port already in use should return an error") + } + if err := p.ReleasePort(ip, "tcp", 80); err != nil { + t.Fatal(err) + } + if _, err := p.RequestPort(ip, "tcp", 80); err != nil { + t.Fatal(err) + } + + port, err = p.RequestPort(ip, "tcp", 0) + if err != nil { + t.Fatal(err) + } + port2, err := p.RequestPort(ip, "tcp", port+1) + if err != nil { + t.Fatal(err) + } + port3, err := p.RequestPort(ip, "tcp", 0) + if err != nil { + t.Fatal(err) + } + if port3 == port2 { + t.Fatal("Requesting a dynamic port should never allocate a used port") + } +} + +func TestPortAllocationWithCustomRange(t *testing.T) { + p := Get() + defer resetPortAllocator() + + start, end := 8081, 8082 + specificPort := 8000 + + //get an ephemeral port. + port1, err := p.RequestPortInRange(defaultIP, "tcp", 0, 0) + if err != nil { + t.Fatal(err) + } + + //request invalid ranges + if _, err := p.RequestPortInRange(defaultIP, "tcp", 0, end); err == nil { + t.Fatalf("Expected error for invalid range %d-%d", 0, end) + } + if _, err := p.RequestPortInRange(defaultIP, "tcp", start, 0); err == nil { + t.Fatalf("Expected error for invalid range %d-%d", 0, end) + } + if _, err := p.RequestPortInRange(defaultIP, "tcp", 8081, 8080); err == nil { + t.Fatalf("Expected error for invalid range %d-%d", 0, end) + } + + //request a single port + port, err := p.RequestPortInRange(defaultIP, "tcp", specificPort, specificPort) + if err != nil { + t.Fatal(err) + } + if port != specificPort { + t.Fatalf("Expected port %d, got %d", specificPort, port) + } + + //get a port from the range + port2, err := p.RequestPortInRange(defaultIP, "tcp", start, end) + if err != nil { + t.Fatal(err) + } + if port2 < start || port2 > end { + t.Fatalf("Expected a port between %d and %d, got %d", start, end, port2) + } + //get another ephemeral port (should be > port1) + port3, err := p.RequestPortInRange(defaultIP, "tcp", 0, 0) + if err != nil { + t.Fatal(err) + } + if port3 < port1 { + t.Fatalf("Expected new port > %d in the ephemeral range, got %d", port1, port3) + } + //get another (and in this case the only other) port from the range + port4, err := p.RequestPortInRange(defaultIP, "tcp", start, end) + if err != nil { + t.Fatal(err) + } + if port4 < start || port4 > end { + t.Fatalf("Expected a port between %d and %d, got %d", start, end, port4) + } + if port4 == port2 { + t.Fatal("Allocated the same port from a custom range") + } + //request 3rd port from the range of 2 + if _, err := p.RequestPortInRange(defaultIP, "tcp", start, end); err != ErrAllPortsAllocated { + t.Fatalf("Expected error %s got %s", ErrAllPortsAllocated, err) + } +} + +func TestNoDuplicateBPR(t *testing.T) { + p := Get() + defer resetPortAllocator() + + if port, err := p.RequestPort(defaultIP, "tcp", p.Begin); err != nil { + t.Fatal(err) + } else if port != p.Begin { + t.Fatalf("Expected port %d got %d", p.Begin, port) + } + + if port, err := p.RequestPort(defaultIP, "tcp", 0); err != nil { + t.Fatal(err) + } else if port == p.Begin { + t.Fatalf("Acquire(0) allocated the same port twice: %d", port) + } +} + +func TestChangePortRange(t *testing.T) { + var tests = []struct { + begin int + end int + setErr error + reqRlt int + }{ + {defaultPortRangeEnd + 1, defaultPortRangeEnd + 10, fmt.Errorf("begin out of range"), 0}, + {defaultPortRangeStart - 10, defaultPortRangeStart - 1, fmt.Errorf("end out of range"), 0}, + {defaultPortRangeEnd, defaultPortRangeStart, fmt.Errorf("out of order"), 0}, + {defaultPortRangeStart + 100, defaultPortRangeEnd + 10, nil, defaultPortRangeStart + 100}, + {0, 0, nil, defaultPortRangeStart}, // revert to default if no value given + {defaultPortRangeStart - 100, defaultPortRangeEnd, nil, defaultPortRangeStart + 1}, + } + p := Get() + port := 0 + for _, c := range tests { + t.Logf("test: port allocate range %v-%v, setErr=%v, reqPort=%v", + c.begin, c.end, c.setErr, c.reqRlt) + err := p.SetPortRange(c.begin, c.end) + if (c.setErr == nil && c.setErr != err) || + (c.setErr != nil && err == nil) { + t.Fatalf("Unexpected set range result, expected=%v, actual=%v", c.setErr, err) + } + if err != nil { + continue + } + if port > 0 { + err := p.ReleasePort(defaultIP, "tcp", port) + if err != nil { + t.Fatalf("Releasing port %v failed, err=%v", port, err) + } + } + + port, err = p.RequestPort(defaultIP, "tcp", 0) + if err != nil { + t.Fatalf("Request failed, err %v", err) + } + if port != c.reqRlt { + t.Fatalf("Incorrect port returned, expected=%v, actual=%v", c.reqRlt, port) + } + } +} diff --git a/libnetwork/portallocator/portallocator_unix.go b/libnetwork/portallocator/portallocator_unix.go new file mode 100644 index 0000000000000..21adb62e0c890 --- /dev/null +++ b/libnetwork/portallocator/portallocator_unix.go @@ -0,0 +1,13 @@ +//go:build !windows +// +build !windows + +package portallocator + +const ( + // defaultPortRangeStart indicates the first port in port range + defaultPortRangeStart = 49153 + // defaultPortRangeEnd indicates the last port in port range + // consistent with default /proc/sys/net/ipv4/ip_local_port_range + // upper bound on linux + defaultPortRangeEnd = 60999 +) diff --git a/libnetwork/portallocator/portallocator_windows.go b/libnetwork/portallocator/portallocator_windows.go new file mode 100644 index 0000000000000..07c4411eae9c6 --- /dev/null +++ b/libnetwork/portallocator/portallocator_windows.go @@ -0,0 +1,12 @@ +package portallocator + +const ( + // defaultPortRangeStart indicates the first port in port range + defaultPortRangeStart = 60000 + // defaultPortRangeEnd indicates the last port in port range + defaultPortRangeEnd = 65000 +) + +func getDynamicPortRange() (start int, end int, err error) { + return defaultPortRangeStart, defaultPortRangeEnd, nil +} diff --git a/libnetwork/portmapper/mapper.go b/libnetwork/portmapper/mapper.go new file mode 100644 index 0000000000000..3315158c97437 --- /dev/null +++ b/libnetwork/portmapper/mapper.go @@ -0,0 +1,262 @@ +package portmapper + +import ( + "errors" + "fmt" + "net" + + "github.com/docker/docker/libnetwork/portallocator" + "github.com/ishidawataru/sctp" + "github.com/sirupsen/logrus" +) + +type mapping struct { + proto string + userlandProxy userlandProxy + host net.Addr + container net.Addr +} + +// newProxy is used to mock out the proxy server in tests +var newProxy = newProxyCommand + +var ( + // ErrUnknownBackendAddressType refers to an unknown container or unsupported address type + ErrUnknownBackendAddressType = errors.New("unknown container address type not supported") + // ErrPortMappedForIP refers to a port already mapped to an ip address + ErrPortMappedForIP = errors.New("port is already mapped to ip") + // ErrPortNotMapped refers to an unmapped port + ErrPortNotMapped = errors.New("port is not mapped") + // ErrSCTPAddrNoIP refers to a SCTP address without IP address. + ErrSCTPAddrNoIP = errors.New("sctp address does not contain any IP address") +) + +// New returns a new instance of PortMapper +func New(proxyPath string) *PortMapper { + return NewWithPortAllocator(portallocator.Get(), proxyPath) +} + +// NewWithPortAllocator returns a new instance of PortMapper which will use the specified PortAllocator +func NewWithPortAllocator(allocator *portallocator.PortAllocator, proxyPath string) *PortMapper { + return &PortMapper{ + currentMappings: make(map[string]*mapping), + Allocator: allocator, + proxyPath: proxyPath, + } +} + +// Map maps the specified container transport address to the host's network address and transport port +func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) { + return pm.MapRange(container, hostIP, hostPort, hostPort, useProxy) +} + +// MapRange maps the specified container transport address to the host's network address and transport port range +func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, hostPortEnd int, useProxy bool) (host net.Addr, err error) { + pm.lock.Lock() + defer pm.lock.Unlock() + + var ( + m *mapping + proto string + allocatedHostPort int + ) + + switch t := container.(type) { + case *net.TCPAddr: + proto = "tcp" + if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { + return nil, err + } + + m = &mapping{ + proto: proto, + host: &net.TCPAddr{IP: hostIP, Port: allocatedHostPort}, + container: container, + } + + if useProxy { + m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, t.IP, t.Port, pm.proxyPath) + if err != nil { + return nil, err + } + } else { + m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort) + if err != nil { + return nil, err + } + } + case *net.UDPAddr: + proto = "udp" + if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { + return nil, err + } + + m = &mapping{ + proto: proto, + host: &net.UDPAddr{IP: hostIP, Port: allocatedHostPort}, + container: container, + } + + if useProxy { + m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, t.IP, t.Port, pm.proxyPath) + if err != nil { + return nil, err + } + } else { + m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort) + if err != nil { + return nil, err + } + } + case *sctp.SCTPAddr: + proto = "sctp" + if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { + return nil, err + } + + m = &mapping{ + proto: proto, + host: &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: allocatedHostPort}, + container: container, + } + + if useProxy { + sctpAddr := container.(*sctp.SCTPAddr) + if len(sctpAddr.IPAddrs) == 0 { + return nil, ErrSCTPAddrNoIP + } + m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IPAddrs[0].IP, sctpAddr.Port, pm.proxyPath) + if err != nil { + return nil, err + } + } else { + m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort) + if err != nil { + return nil, err + } + } + default: + return nil, ErrUnknownBackendAddressType + } + + // release the allocated port on any further error during return. + defer func() { + if err != nil { + pm.Allocator.ReleasePort(hostIP, proto, allocatedHostPort) + } + }() + + key := getKey(m.host) + if _, exists := pm.currentMappings[key]; exists { + return nil, ErrPortMappedForIP + } + + containerIP, containerPort := getIPAndPort(m.container) + if err := pm.AppendForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil { + return nil, err + } + + cleanup := func() error { + // need to undo the iptables rules before we return + m.userlandProxy.Stop() + pm.DeleteForwardingTableEntry(m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) + if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil { + return err + } + + return nil + } + + if err := m.userlandProxy.Start(); err != nil { + if err := cleanup(); err != nil { + return nil, fmt.Errorf("Error during port allocation cleanup: %v", err) + } + return nil, err + } + + pm.currentMappings[key] = m + return m.host, nil +} + +// Unmap removes stored mapping for the specified host transport address +func (pm *PortMapper) Unmap(host net.Addr) error { + pm.lock.Lock() + defer pm.lock.Unlock() + + key := getKey(host) + data, exists := pm.currentMappings[key] + if !exists { + return ErrPortNotMapped + } + + if data.userlandProxy != nil { + data.userlandProxy.Stop() + } + + delete(pm.currentMappings, key) + + containerIP, containerPort := getIPAndPort(data.container) + hostIP, hostPort := getIPAndPort(data.host) + if err := pm.DeleteForwardingTableEntry(data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { + logrus.Errorf("Error on iptables delete: %s", err) + } + + switch a := host.(type) { + case *net.TCPAddr: + return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port) + case *net.UDPAddr: + return pm.Allocator.ReleasePort(a.IP, "udp", a.Port) + case *sctp.SCTPAddr: + if len(a.IPAddrs) == 0 { + return ErrSCTPAddrNoIP + } + return pm.Allocator.ReleasePort(a.IPAddrs[0].IP, "sctp", a.Port) + } + return ErrUnknownBackendAddressType +} + +// ReMapAll re-applies all port mappings +func (pm *PortMapper) ReMapAll() { + pm.lock.Lock() + defer pm.lock.Unlock() + logrus.Debugln("Re-applying all port mappings.") + for _, data := range pm.currentMappings { + containerIP, containerPort := getIPAndPort(data.container) + hostIP, hostPort := getIPAndPort(data.host) + if err := pm.AppendForwardingTableEntry(data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { + logrus.Errorf("Error on iptables add: %s", err) + } + } +} + +func getKey(a net.Addr) string { + switch t := a.(type) { + case *net.TCPAddr: + return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp") + case *net.UDPAddr: + return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp") + case *sctp.SCTPAddr: + if len(t.IPAddrs) == 0 { + logrus.Error(ErrSCTPAddrNoIP) + return "" + } + return fmt.Sprintf("%s:%d/%s", t.IPAddrs[0].IP.String(), t.Port, "sctp") + } + return "" +} + +func getIPAndPort(a net.Addr) (net.IP, int) { + switch t := a.(type) { + case *net.TCPAddr: + return t.IP, t.Port + case *net.UDPAddr: + return t.IP, t.Port + case *sctp.SCTPAddr: + if len(t.IPAddrs) == 0 { + logrus.Error(ErrSCTPAddrNoIP) + return nil, 0 + } + return t.IPAddrs[0].IP, t.Port + } + return nil, 0 +} diff --git a/libnetwork/portmapper/mapper_linux.go b/libnetwork/portmapper/mapper_linux.go new file mode 100644 index 0000000000000..1d537cbd9cb60 --- /dev/null +++ b/libnetwork/portmapper/mapper_linux.go @@ -0,0 +1,46 @@ +package portmapper + +import ( + "net" + "sync" + + "github.com/docker/docker/libnetwork/iptables" + "github.com/docker/docker/libnetwork/portallocator" +) + +// PortMapper manages the network address translation +type PortMapper struct { + bridgeName string + + // udp:ip:port + currentMappings map[string]*mapping + lock sync.Mutex + + proxyPath string + + Allocator *portallocator.PortAllocator + chain *iptables.ChainInfo +} + +// SetIptablesChain sets the specified chain into portmapper +func (pm *PortMapper) SetIptablesChain(c *iptables.ChainInfo, bridgeName string) { + pm.chain = c + pm.bridgeName = bridgeName +} + +// AppendForwardingTableEntry adds a port mapping to the forwarding table +func (pm *PortMapper) AppendForwardingTableEntry(proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { + return pm.forward(iptables.Append, proto, sourceIP, sourcePort, containerIP, containerPort) +} + +// DeleteForwardingTableEntry removes a port mapping from the forwarding table +func (pm *PortMapper) DeleteForwardingTableEntry(proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { + return pm.forward(iptables.Delete, proto, sourceIP, sourcePort, containerIP, containerPort) +} + +func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { + if pm.chain == nil { + return nil + } + return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName) +} diff --git a/libnetwork/portmapper/mapper_linux_test.go b/libnetwork/portmapper/mapper_linux_test.go new file mode 100644 index 0000000000000..b39376749f2d9 --- /dev/null +++ b/libnetwork/portmapper/mapper_linux_test.go @@ -0,0 +1,269 @@ +package portmapper + +import ( + "net" + "strings" + "testing" + + "github.com/docker/docker/libnetwork/iptables" +) + +func init() { + // override this func to mock out the proxy server + newProxy = newMockProxyCommand +} + +func TestSetIptablesChain(t *testing.T) { + pm := New("") + + c := &iptables.ChainInfo{ + Name: "TEST", + } + + if pm.chain != nil { + t.Fatal("chain should be nil at init") + } + + pm.SetIptablesChain(c, "lo") + if pm.chain == nil { + t.Fatal("chain should not be nil after set") + } +} + +func TestMapTCPPorts(t *testing.T) { + pm := New("") + dstIP1 := net.ParseIP("192.168.0.1") + dstIP2 := net.ParseIP("192.168.0.2") + dstAddr1 := &net.TCPAddr{IP: dstIP1, Port: 80} + dstAddr2 := &net.TCPAddr{IP: dstIP2, Port: 80} + + srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} + srcAddr2 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.2")} + + addrEqual := func(addr1, addr2 net.Addr) bool { + return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String()) + } + + if host, err := pm.Map(srcAddr1, dstIP1, 80, true); err != nil { + t.Fatalf("Failed to allocate port: %s", err) + } else if !addrEqual(dstAddr1, host) { + t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s", + dstAddr1.String(), dstAddr1.Network(), host.String(), host.Network()) + } + + if _, err := pm.Map(srcAddr1, dstIP1, 80, true); err == nil { + t.Fatalf("Port is in use - mapping should have failed") + } + + if _, err := pm.Map(srcAddr2, dstIP1, 80, true); err == nil { + t.Fatalf("Port is in use - mapping should have failed") + } + + if _, err := pm.Map(srcAddr2, dstIP2, 80, true); err != nil { + t.Fatalf("Failed to allocate port: %s", err) + } + + if pm.Unmap(dstAddr1) != nil { + t.Fatalf("Failed to release port") + } + + if pm.Unmap(dstAddr2) != nil { + t.Fatalf("Failed to release port") + } + + if pm.Unmap(dstAddr2) == nil { + t.Fatalf("Port already released, but no error reported") + } +} + +func TestGetUDPKey(t *testing.T) { + addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53} + + key := getKey(addr) + + if expected := "192.168.1.5:53/udp"; key != expected { + t.Fatalf("expected key %s got %s", expected, key) + } +} + +func TestGetTCPKey(t *testing.T) { + addr := &net.TCPAddr{IP: net.ParseIP("192.168.1.5"), Port: 80} + + key := getKey(addr) + + if expected := "192.168.1.5:80/tcp"; key != expected { + t.Fatalf("expected key %s got %s", expected, key) + } +} + +func TestGetUDPIPAndPort(t *testing.T) { + addr := &net.UDPAddr{IP: net.ParseIP("192.168.1.5"), Port: 53} + + ip, port := getIPAndPort(addr) + if expected := "192.168.1.5"; ip.String() != expected { + t.Fatalf("expected ip %s got %s", expected, ip) + } + + if ep := 53; port != ep { + t.Fatalf("expected port %d got %d", ep, port) + } +} + +func TestMapUDPPorts(t *testing.T) { + pm := New("") + dstIP1 := net.ParseIP("192.168.0.1") + dstIP2 := net.ParseIP("192.168.0.2") + dstAddr1 := &net.UDPAddr{IP: dstIP1, Port: 80} + dstAddr2 := &net.UDPAddr{IP: dstIP2, Port: 80} + + srcAddr1 := &net.UDPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} + srcAddr2 := &net.UDPAddr{Port: 1080, IP: net.ParseIP("172.16.0.2")} + + addrEqual := func(addr1, addr2 net.Addr) bool { + return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String()) + } + + if host, err := pm.Map(srcAddr1, dstIP1, 80, true); err != nil { + t.Fatalf("Failed to allocate port: %s", err) + } else if !addrEqual(dstAddr1, host) { + t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s", + dstAddr1.String(), dstAddr1.Network(), host.String(), host.Network()) + } + + if _, err := pm.Map(srcAddr1, dstIP1, 80, true); err == nil { + t.Fatalf("Port is in use - mapping should have failed") + } + + if _, err := pm.Map(srcAddr2, dstIP1, 80, true); err == nil { + t.Fatalf("Port is in use - mapping should have failed") + } + + if _, err := pm.Map(srcAddr2, dstIP2, 80, true); err != nil { + t.Fatalf("Failed to allocate port: %s", err) + } + + if pm.Unmap(dstAddr1) != nil { + t.Fatalf("Failed to release port") + } + + if pm.Unmap(dstAddr2) != nil { + t.Fatalf("Failed to release port") + } + + if pm.Unmap(dstAddr2) == nil { + t.Fatalf("Port already released, but no error reported") + } +} + +func TestMapAllPortsSingleInterface(t *testing.T) { + pm := New("") + dstIP1 := net.ParseIP("0.0.0.0") + srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} + + hosts := []net.Addr{} + var host net.Addr + var err error + + defer func() { + for _, val := range hosts { + pm.Unmap(val) + } + }() + + for i := 0; i < 10; i++ { + start, end := pm.Allocator.Begin, pm.Allocator.End + for i := start; i < end; i++ { + if host, err = pm.Map(srcAddr1, dstIP1, 0, true); err != nil { + t.Fatal(err) + } + + hosts = append(hosts, host) + } + + if _, err := pm.Map(srcAddr1, dstIP1, start, true); err == nil { + t.Fatalf("Port %d should be bound but is not", start) + } + + for _, val := range hosts { + if err := pm.Unmap(val); err != nil { + t.Fatal(err) + } + } + + hosts = []net.Addr{} + } +} + +func TestMapTCPDummyListen(t *testing.T) { + pm := New("") + dstIP := net.ParseIP("0.0.0.0") + dstAddr := &net.TCPAddr{IP: dstIP, Port: 80} + + // no-op for dummy + srcAddr := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} + + addrEqual := func(addr1, addr2 net.Addr) bool { + return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String()) + } + + if host, err := pm.Map(srcAddr, dstIP, 80, false); err != nil { + t.Fatalf("Failed to allocate port: %s", err) + } else if !addrEqual(dstAddr, host) { + t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s", + dstAddr.String(), dstAddr.Network(), host.String(), host.Network()) + } + if _, err := net.Listen("tcp", "0.0.0.0:80"); err == nil { + t.Fatal("Listen on mapped port without proxy should fail") + } else { + if !strings.Contains(err.Error(), "address already in use") { + t.Fatalf("Error should be about address already in use, got %v", err) + } + } + if _, err := net.Listen("tcp", "0.0.0.0:81"); err != nil { + t.Fatal(err) + } + if host, err := pm.Map(srcAddr, dstIP, 81, false); err == nil { + t.Fatalf("Bound port shouldn't be allocated, but it was on: %v", host) + } else { + if !strings.Contains(err.Error(), "address already in use") { + t.Fatalf("Error should be about address already in use, got %v", err) + } + } +} + +func TestMapUDPDummyListen(t *testing.T) { + pm := New("") + dstIP := net.ParseIP("0.0.0.0") + dstAddr := &net.UDPAddr{IP: dstIP, Port: 80} + + // no-op for dummy + srcAddr := &net.UDPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")} + + addrEqual := func(addr1, addr2 net.Addr) bool { + return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String()) + } + + if host, err := pm.Map(srcAddr, dstIP, 80, false); err != nil { + t.Fatalf("Failed to allocate port: %s", err) + } else if !addrEqual(dstAddr, host) { + t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s", + dstAddr.String(), dstAddr.Network(), host.String(), host.Network()) + } + if _, err := net.ListenUDP("udp", &net.UDPAddr{IP: dstIP, Port: 80}); err == nil { + t.Fatal("Listen on mapped port without proxy should fail") + } else { + if !strings.Contains(err.Error(), "address already in use") { + t.Fatalf("Error should be about address already in use, got %v", err) + } + } + if _, err := net.ListenUDP("udp", &net.UDPAddr{IP: dstIP, Port: 81}); err != nil { + t.Fatal(err) + } + if host, err := pm.Map(srcAddr, dstIP, 81, false); err == nil { + t.Fatalf("Bound port shouldn't be allocated, but it was on: %v", host) + } else { + if !strings.Contains(err.Error(), "address already in use") { + t.Fatalf("Error should be about address already in use, got %v", err) + } + } +} diff --git a/libnetwork/portmapper/mapper_windows.go b/libnetwork/portmapper/mapper_windows.go new file mode 100644 index 0000000000000..7be0eb12e6fc1 --- /dev/null +++ b/libnetwork/portmapper/mapper_windows.go @@ -0,0 +1,37 @@ +package portmapper + +import ( + "net" + "sync" + + "github.com/docker/docker/libnetwork/portallocator" +) + +// PortMapper manages the network address translation +type PortMapper struct { + bridgeName string + + // udp:ip:port + currentMappings map[string]*mapping + lock sync.Mutex + + proxyPath string + + Allocator *portallocator.PortAllocator +} + +// AppendForwardingTableEntry adds a port mapping to the forwarding table +func (pm *PortMapper) AppendForwardingTableEntry(proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { + return nil +} + +// DeleteForwardingTableEntry removes a port mapping from the forwarding table +func (pm *PortMapper) DeleteForwardingTableEntry(proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { + return nil +} + +// checkIP checks if IP is valid and matching to chain version +func (pm *PortMapper) checkIP(ip net.IP) bool { + // no IPv6 for port mapper on windows -> only IPv4 valid + return ip.To4() != nil +} diff --git a/vendor/github.com/docker/libnetwork/portmapper/mock_proxy.go b/libnetwork/portmapper/mock_proxy_test.go similarity index 100% rename from vendor/github.com/docker/libnetwork/portmapper/mock_proxy.go rename to libnetwork/portmapper/mock_proxy_test.go diff --git a/libnetwork/portmapper/proxy.go b/libnetwork/portmapper/proxy.go new file mode 100644 index 0000000000000..9ab70ffa0e488 --- /dev/null +++ b/libnetwork/portmapper/proxy.go @@ -0,0 +1,87 @@ +package portmapper + +import ( + "fmt" + "io" + "net" + + "github.com/ishidawataru/sctp" +) + +type userlandProxy interface { + Start() error + Stop() error +} + +// ipVersion refers to IP version - v4 or v6 +type ipVersion string + +const ( + // IPv4 is version 4 + ipv4 ipVersion = "4" + // IPv4 is version 6 + ipv6 ipVersion = "6" +) + +// dummyProxy just listen on some port, it is needed to prevent accidental +// port allocations on bound port, because without userland proxy we using +// iptables rules and not net.Listen +type dummyProxy struct { + listener io.Closer + addr net.Addr + ipVersion ipVersion +} + +func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, error) { + // detect version of hostIP to bind only to correct version + version := ipv4 + if hostIP.To4() == nil { + version = ipv6 + } + switch proto { + case "tcp": + addr := &net.TCPAddr{IP: hostIP, Port: hostPort} + return &dummyProxy{addr: addr, ipVersion: version}, nil + case "udp": + addr := &net.UDPAddr{IP: hostIP, Port: hostPort} + return &dummyProxy{addr: addr, ipVersion: version}, nil + case "sctp": + addr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: hostPort} + return &dummyProxy{addr: addr, ipVersion: version}, nil + default: + return nil, fmt.Errorf("Unknown addr type: %s", proto) + } +} + +func (p *dummyProxy) Start() error { + switch addr := p.addr.(type) { + case *net.TCPAddr: + l, err := net.ListenTCP("tcp"+string(p.ipVersion), addr) + if err != nil { + return err + } + p.listener = l + case *net.UDPAddr: + l, err := net.ListenUDP("udp"+string(p.ipVersion), addr) + if err != nil { + return err + } + p.listener = l + case *sctp.SCTPAddr: + l, err := sctp.ListenSCTP("sctp"+string(p.ipVersion), addr) + if err != nil { + return err + } + p.listener = l + default: + return fmt.Errorf("Unknown addr type: %T", p.addr) + } + return nil +} + +func (p *dummyProxy) Stop() error { + if p.listener != nil { + return p.listener.Close() + } + return nil +} diff --git a/libnetwork/portmapper/proxy_linux.go b/libnetwork/portmapper/proxy_linux.go new file mode 100644 index 0000000000000..fa0d11f88456b --- /dev/null +++ b/libnetwork/portmapper/proxy_linux.go @@ -0,0 +1,98 @@ +package portmapper + +import ( + "fmt" + "io" + "net" + "os" + "os/exec" + "strconv" + "syscall" + "time" +) + +const userlandProxyCommandName = "docker-proxy" + +func newProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int, proxyPath string) (userlandProxy, error) { + path := proxyPath + if proxyPath == "" { + cmd, err := exec.LookPath(userlandProxyCommandName) + if err != nil { + return nil, err + } + path = cmd + } + + args := []string{ + path, + "-proto", proto, + "-host-ip", hostIP.String(), + "-host-port", strconv.Itoa(hostPort), + "-container-ip", containerIP.String(), + "-container-port", strconv.Itoa(containerPort), + } + + return &proxyCommand{ + cmd: &exec.Cmd{ + Path: path, + Args: args, + SysProcAttr: &syscall.SysProcAttr{ + Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies + }, + }, + }, nil +} + +// proxyCommand wraps an exec.Cmd to run the userland TCP and UDP +// proxies as separate processes. +type proxyCommand struct { + cmd *exec.Cmd +} + +func (p *proxyCommand) Start() error { + r, w, err := os.Pipe() + if err != nil { + return fmt.Errorf("proxy unable to open os.Pipe %s", err) + } + defer r.Close() + p.cmd.ExtraFiles = []*os.File{w} + if err := p.cmd.Start(); err != nil { + return err + } + w.Close() + + errchan := make(chan error, 1) + go func() { + buf := make([]byte, 2) + r.Read(buf) + + if string(buf) != "0\n" { + errStr, err := io.ReadAll(r) + if err != nil { + errchan <- fmt.Errorf("Error reading exit status from userland proxy: %v", err) + return + } + + errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr) + return + } + errchan <- nil + }() + + select { + case err := <-errchan: + return err + case <-time.After(16 * time.Second): + return fmt.Errorf("Timed out proxy starting the userland proxy") + } +} + +func (p *proxyCommand) Stop() error { + if p.cmd.Process != nil { + if err := p.cmd.Process.Signal(os.Interrupt); err != nil { + return err + } + return p.cmd.Wait() + } + return nil +} diff --git a/libnetwork/portmapper/proxy_windows.go b/libnetwork/portmapper/proxy_windows.go new file mode 100644 index 0000000000000..06a9e2462cc62 --- /dev/null +++ b/libnetwork/portmapper/proxy_windows.go @@ -0,0 +1,10 @@ +package portmapper + +import ( + "errors" + "net" +) + +func newProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int, proxyPath string) (userlandProxy, error) { + return nil, errors.New("proxy is unsupported on windows") +} diff --git a/libnetwork/resolvconf/resolvconf.go b/libnetwork/resolvconf/resolvconf.go new file mode 100644 index 0000000000000..a9d8027886d55 --- /dev/null +++ b/libnetwork/resolvconf/resolvconf.go @@ -0,0 +1,295 @@ +// Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf +package resolvconf + +import ( + "bytes" + "os" + "regexp" + "strings" + "sync" + + "github.com/sirupsen/logrus" +) + +const ( + // defaultPath is the default path to the resolv.conf that contains information to resolve DNS. See Path(). + defaultPath = "/etc/resolv.conf" + // alternatePath is a path different from defaultPath, that may be used to resolve DNS. See Path(). + alternatePath = "/run/systemd/resolve/resolv.conf" +) + +// constants for the IP address type +const ( + IP = iota // IPv4 and IPv6 + IPv4 + IPv6 +) + +var ( + detectSystemdResolvConfOnce sync.Once + pathAfterSystemdDetection = defaultPath +) + +// Path returns the path to the resolv.conf file that libnetwork should use. +// +// When /etc/resolv.conf contains 127.0.0.53 as the only nameserver, then +// it is assumed systemd-resolved manages DNS. Because inside the container 127.0.0.53 +// is not a valid DNS server, Path() returns /run/systemd/resolve/resolv.conf +// which is the resolv.conf that systemd-resolved generates and manages. +// Otherwise Path() returns /etc/resolv.conf. +// +// Errors are silenced as they will inevitably resurface at future open/read calls. +// +// More information at https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf +func Path() string { + detectSystemdResolvConfOnce.Do(func() { + candidateResolvConf, err := os.ReadFile(defaultPath) + if err != nil { + // silencing error as it will resurface at next calls trying to read defaultPath + return + } + ns := GetNameservers(candidateResolvConf, IP) + if len(ns) == 1 && ns[0] == "127.0.0.53" { + pathAfterSystemdDetection = alternatePath + logrus.Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath) + } + }) + return pathAfterSystemdDetection +} + +const ( + // ipLocalhost is a regex pattern for IPv4 or IPv6 loopback range. + ipLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)` + ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` + ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock + + // This is not an IPv6 address verifier as it will accept a super-set of IPv6, and also + // will *not match* IPv4-Embedded IPv6 Addresses (RFC6052), but that and other variants + // -- e.g. other link-local types -- either won't work in containers or are unnecessary. + // For readability and sufficiency for Docker purposes this seemed more reasonable than a + // 1000+ character regexp with exact and complete IPv6 validation + ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})(%\w+)?` +) + +var ( + // Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS + defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"} + defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"} + + localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipLocalhost + `\s*\n*`) + nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`) + nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) + nsIPv6Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv6Address + `))\s*$`) + nsIPv4Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `))\s*$`) + searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) + optionsRegexp = regexp.MustCompile(`^\s*options\s*(([^\s]+\s*)*)$`) +) + +var lastModified struct { + sync.Mutex + sha256 string + contents []byte +} + +// File contains the resolv.conf content and its hash +type File struct { + Content []byte + Hash string +} + +// Get returns the contents of /etc/resolv.conf and its hash +func Get() (*File, error) { + return GetSpecific(Path()) +} + +// GetSpecific returns the contents of the user specified resolv.conf file and its hash +func GetSpecific(path string) (*File, error) { + resolv, err := os.ReadFile(path) + if err != nil { + return nil, err + } + hash, err := hashData(bytes.NewReader(resolv)) + if err != nil { + return nil, err + } + return &File{Content: resolv, Hash: hash}, nil +} + +// GetIfChanged retrieves the host /etc/resolv.conf file, checks against the last hash +// and, if modified since last check, returns the bytes and new hash. +// This feature is used by the resolv.conf updater for containers +func GetIfChanged() (*File, error) { + lastModified.Lock() + defer lastModified.Unlock() + + resolv, err := os.ReadFile(Path()) + if err != nil { + return nil, err + } + newHash, err := hashData(bytes.NewReader(resolv)) + if err != nil { + return nil, err + } + if lastModified.sha256 != newHash { + lastModified.sha256 = newHash + lastModified.contents = resolv + return &File{Content: resolv, Hash: newHash}, nil + } + // nothing changed, so return no data + return nil, nil +} + +// GetLastModified retrieves the last used contents and hash of the host resolv.conf. +// Used by containers updating on restart +func GetLastModified() *File { + lastModified.Lock() + defer lastModified.Unlock() + + return &File{Content: lastModified.contents, Hash: lastModified.sha256} +} + +// FilterResolvDNS cleans up the config in resolvConf. It has two main jobs: +// 1. It looks for localhost (127.*|::1) entries in the provided +// resolv.conf, removing local nameserver entries, and, if the resulting +// cleaned config has no defined nameservers left, adds default DNS entries +// 2. Given the caller provides the enable/disable state of IPv6, the filter +// code will remove all IPv6 nameservers if it is not enabled for containers +// +func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) { + cleanedResolvConf := localhostNSRegexp.ReplaceAll(resolvConf, []byte{}) + // if IPv6 is not enabled, also clean out any IPv6 address nameserver + if !ipv6Enabled { + cleanedResolvConf = nsIPv6Regexp.ReplaceAll(cleanedResolvConf, []byte{}) + } + // if the resulting resolvConf has no more nameservers defined, add appropriate + // default DNS servers for IPv4 and (optionally) IPv6 + if len(GetNameservers(cleanedResolvConf, IP)) == 0 { + logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers: %v", defaultIPv4Dns) + dns := defaultIPv4Dns + if ipv6Enabled { + logrus.Infof("IPv6 enabled; Adding default IPv6 external servers: %v", defaultIPv6Dns) + dns = append(dns, defaultIPv6Dns...) + } + cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...) + } + hash, err := hashData(bytes.NewReader(cleanedResolvConf)) + if err != nil { + return nil, err + } + return &File{Content: cleanedResolvConf, Hash: hash}, nil +} + +// getLines parses input into lines and strips away comments. +func getLines(input []byte, commentMarker []byte) [][]byte { + lines := bytes.Split(input, []byte("\n")) + var output [][]byte + for _, currentLine := range lines { + var commentIndex = bytes.Index(currentLine, commentMarker) + if commentIndex == -1 { + output = append(output, currentLine) + } else { + output = append(output, currentLine[:commentIndex]) + } + } + return output +} + +// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf +func GetNameservers(resolvConf []byte, kind int) []string { + nameservers := []string{} + for _, line := range getLines(resolvConf, []byte("#")) { + var ns [][]byte + if kind == IP { + ns = nsRegexp.FindSubmatch(line) + } else if kind == IPv4 { + ns = nsIPv4Regexpmatch.FindSubmatch(line) + } else if kind == IPv6 { + ns = nsIPv6Regexpmatch.FindSubmatch(line) + } + if len(ns) > 0 { + nameservers = append(nameservers, string(ns[1])) + } + } + return nameservers +} + +// GetNameserversAsCIDR returns nameservers (if any) listed in +// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") +// This function's output is intended for net.ParseCIDR +func GetNameserversAsCIDR(resolvConf []byte) []string { + nameservers := []string{} + for _, nameserver := range GetNameservers(resolvConf, IP) { + var address string + // If IPv6, strip zone if present + if strings.Contains(nameserver, ":") { + address = strings.Split(nameserver, "%")[0] + "/128" + } else { + address = nameserver + "/32" + } + nameservers = append(nameservers, address) + } + return nameservers +} + +// GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf +// If more than one search line is encountered, only the contents of the last +// one is returned. +func GetSearchDomains(resolvConf []byte) []string { + domains := []string{} + for _, line := range getLines(resolvConf, []byte("#")) { + match := searchRegexp.FindSubmatch(line) + if match == nil { + continue + } + domains = strings.Fields(string(match[1])) + } + return domains +} + +// GetOptions returns options (if any) listed in /etc/resolv.conf +// If more than one options line is encountered, only the contents of the last +// one is returned. +func GetOptions(resolvConf []byte) []string { + options := []string{} + for _, line := range getLines(resolvConf, []byte("#")) { + match := optionsRegexp.FindSubmatch(line) + if match == nil { + continue + } + options = strings.Fields(string(match[1])) + } + return options +} + +// Build writes a configuration file to path containing a "nameserver" entry +// for every element in dns, a "search" entry for every element in +// dnsSearch, and an "options" entry for every element in dnsOptions. +func Build(path string, dns, dnsSearch, dnsOptions []string) (*File, error) { + content := bytes.NewBuffer(nil) + if len(dnsSearch) > 0 { + if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." { + if _, err := content.WriteString("search " + searchString + "\n"); err != nil { + return nil, err + } + } + } + for _, dns := range dns { + if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil { + return nil, err + } + } + if len(dnsOptions) > 0 { + if optsString := strings.Join(dnsOptions, " "); strings.Trim(optsString, " ") != "" { + if _, err := content.WriteString("options " + optsString + "\n"); err != nil { + return nil, err + } + } + } + + hash, err := hashData(bytes.NewReader(content.Bytes())) + if err != nil { + return nil, err + } + + return &File{Content: content.Bytes(), Hash: hash}, os.WriteFile(path, content.Bytes(), 0644) +} diff --git a/libnetwork/resolvconf/resolvconf_linux_test.go b/libnetwork/resolvconf/resolvconf_linux_test.go new file mode 100644 index 0000000000000..1d7b9a86eddd5 --- /dev/null +++ b/libnetwork/resolvconf/resolvconf_linux_test.go @@ -0,0 +1,303 @@ +package resolvconf + +import ( + "bytes" + "os" + "testing" +) + +func TestGet(t *testing.T) { + resolvConfUtils, err := Get() + if err != nil { + t.Fatal(err) + } + resolvConfSystem, err := os.ReadFile("/etc/resolv.conf") + if err != nil { + t.Fatal(err) + } + if string(resolvConfUtils.Content) != string(resolvConfSystem) { + t.Fatalf("/etc/resolv.conf and GetResolvConf have different content.") + } + hashSystem, err := hashData(bytes.NewReader(resolvConfSystem)) + if err != nil { + t.Fatal(err) + } + if resolvConfUtils.Hash != hashSystem { + t.Fatalf("/etc/resolv.conf and GetResolvConf have different hashes.") + } +} + +func TestGetNameservers(t *testing.T) { + for resolv, result := range map[string][]string{` +nameserver 1.2.3.4 +nameserver 40.3.200.10 +search example.com`: {"1.2.3.4", "40.3.200.10"}, + `search example.com`: {}, + `nameserver 1.2.3.4 +search example.com +nameserver 4.30.20.100`: {"1.2.3.4", "4.30.20.100"}, + ``: {}, + ` nameserver 1.2.3.4 `: {"1.2.3.4"}, + `search example.com +nameserver 1.2.3.4 +#nameserver 4.3.2.1`: {"1.2.3.4"}, + `search example.com +nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4"}, + } { + test := GetNameservers([]byte(resolv), IP) + if !strSlicesEqual(test, result) { + t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv) + } + } +} + +func TestGetNameserversAsCIDR(t *testing.T) { + for resolv, result := range map[string][]string{` +nameserver 1.2.3.4 +nameserver 40.3.200.10 +search example.com`: {"1.2.3.4/32", "40.3.200.10/32"}, + `search example.com`: {}, + `nameserver 1.2.3.4 +search example.com +nameserver 4.30.20.100`: {"1.2.3.4/32", "4.30.20.100/32"}, + ``: {}, + ` nameserver 1.2.3.4 `: {"1.2.3.4/32"}, + `search example.com +nameserver 1.2.3.4 +#nameserver 4.3.2.1`: {"1.2.3.4/32"}, + `search example.com +nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4/32"}, + } { + test := GetNameserversAsCIDR([]byte(resolv)) + if !strSlicesEqual(test, result) { + t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv) + } + } +} + +func TestGetSearchDomains(t *testing.T) { + for resolv, result := range map[string][]string{ + `search example.com`: {"example.com"}, + `search example.com # ignored`: {"example.com"}, + ` search example.com `: {"example.com"}, + ` search example.com # ignored`: {"example.com"}, + `search foo.example.com example.com`: {"foo.example.com", "example.com"}, + ` search foo.example.com example.com `: {"foo.example.com", "example.com"}, + ` search foo.example.com example.com # ignored`: {"foo.example.com", "example.com"}, + ``: {}, + `# ignored`: {}, + `nameserver 1.2.3.4 +search foo.example.com example.com`: {"foo.example.com", "example.com"}, + `nameserver 1.2.3.4 +search dup1.example.com dup2.example.com +search foo.example.com example.com`: {"foo.example.com", "example.com"}, + `nameserver 1.2.3.4 +search foo.example.com example.com +nameserver 4.30.20.100`: {"foo.example.com", "example.com"}, + } { + test := GetSearchDomains([]byte(resolv)) + if !strSlicesEqual(test, result) { + t.Fatalf("Wrong search domain string {%s} should be %v. Input: %s", test, result, resolv) + } + } +} + +func TestGetOptions(t *testing.T) { + for resolv, result := range map[string][]string{ + `options opt1`: {"opt1"}, + `options opt1 # ignored`: {"opt1"}, + ` options opt1 `: {"opt1"}, + ` options opt1 # ignored`: {"opt1"}, + `options opt1 opt2 opt3`: {"opt1", "opt2", "opt3"}, + `options opt1 opt2 opt3 # ignored`: {"opt1", "opt2", "opt3"}, + ` options opt1 opt2 opt3 `: {"opt1", "opt2", "opt3"}, + ` options opt1 opt2 opt3 # ignored`: {"opt1", "opt2", "opt3"}, + ``: {}, + `# ignored`: {}, + `nameserver 1.2.3.4`: {}, + `nameserver 1.2.3.4 +options opt1 opt2 opt3`: {"opt1", "opt2", "opt3"}, + `nameserver 1.2.3.4 +options opt1 opt2 +options opt3 opt4`: {"opt3", "opt4"}, + } { + test := GetOptions([]byte(resolv)) + if !strSlicesEqual(test, result) { + t.Fatalf("Wrong options string {%s} should be %v. Input: %s", test, result, resolv) + } + } +} + +func strSlicesEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + + for i, v := range a { + if v != b[i] { + return false + } + } + + return true +} + +func TestBuild(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + _, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{"opt1"}) + if err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } +} + +func TestBuildWithZeroLengthDomainSearch(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + _, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."}, []string{"opt1"}) + if err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "nameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } + if notExpected := "search ."; bytes.Contains(content, []byte(notExpected)) { + t.Fatalf("Expected to not find '%s' got '%s'", notExpected, content) + } +} + +func TestBuildWithNoOptions(t *testing.T) { + file, err := os.CreateTemp("", "") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + + _, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{}) + if err != nil { + t.Fatal(err) + } + + content, err := os.ReadFile(file.Name()) + if err != nil { + t.Fatal(err) + } + + if expected := "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\n"; !bytes.Contains(content, []byte(expected)) { + t.Fatalf("Expected to find '%s' got '%s'", expected, content) + } + if notExpected := "search ."; bytes.Contains(content, []byte(notExpected)) { + t.Fatalf("Expected to not find '%s' got '%s'", notExpected, content) + } +} + +func TestFilterResolvDns(t *testing.T) { + ns0 := "nameserver 10.16.60.14\nnameserver 10.16.60.21\n" + + if result, _ := FilterResolvDNS([]byte(ns0), false); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed No Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } + + ns1 := "nameserver 10.16.60.14\nnameserver 10.16.60.21\nnameserver 127.0.0.1\n" + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } + + ns1 = "nameserver 10.16.60.14\nnameserver 127.0.0.1\nnameserver 10.16.60.21\n" + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } + + ns1 = "nameserver 127.0.1.1\nnameserver 10.16.60.14\nnameserver 10.16.60.21\n" + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } + + ns1 = "nameserver ::1\nnameserver 10.16.60.14\nnameserver 127.0.2.1\nnameserver 10.16.60.21\n" + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } + + ns1 = "nameserver 10.16.60.14\nnameserver ::1\nnameserver 10.16.60.21\nnameserver ::1" + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed Localhost: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } + + // with IPv6 disabled (false param), the IPv6 nameserver should be removed + ns1 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1" + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed Localhost+IPv6 off: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } + + // with IPv6 disabled (false param), the IPv6 link-local nameserver with zone ID should be removed + ns1 = "nameserver 10.16.60.14\nnameserver FE80::BB1%1\nnameserver FE80::BB1%eth0\nnameserver 10.16.60.21\n" + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed Localhost+IPv6 off: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } + + // with IPv6 enabled, the IPv6 nameserver should be preserved + ns0 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\n" + ns1 = "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1" + if result, _ := FilterResolvDNS([]byte(ns1), true); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed Localhost+IPv6 on: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } + + // with IPv6 enabled, and no non-localhost servers, Google defaults (both IPv4+IPv6) should be added + ns0 = "\nnameserver 8.8.8.8\nnameserver 8.8.4.4\nnameserver 2001:4860:4860::8888\nnameserver 2001:4860:4860::8844" + ns1 = "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1" + if result, _ := FilterResolvDNS([]byte(ns1), true); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed no Localhost+IPv6 enabled: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } + + // with IPv6 disabled, and no non-localhost servers, Google defaults (only IPv4) should be added + ns0 = "\nnameserver 8.8.8.8\nnameserver 8.8.4.4" + ns1 = "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1" + if result, _ := FilterResolvDNS([]byte(ns1), false); result != nil { + if ns0 != string(result.Content) { + t.Fatalf("Failed no Localhost+IPv6 enabled: expected \n<%s> got \n<%s>", ns0, string(result.Content)) + } + } +} diff --git a/libnetwork/resolvconf/utils.go b/libnetwork/resolvconf/utils.go new file mode 100644 index 0000000000000..7d4d095793dde --- /dev/null +++ b/libnetwork/resolvconf/utils.go @@ -0,0 +1,16 @@ +package resolvconf + +import ( + "crypto/sha256" + "encoding/hex" + "io" +) + +// hashData returns the sha256 sum of src. +func hashData(src io.Reader) (string, error) { + h := sha256.New() + if _, err := io.Copy(h, src); err != nil { + return "", err + } + return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil +} diff --git a/libnetwork/resolvconf/utils_test.go b/libnetwork/resolvconf/utils_test.go new file mode 100644 index 0000000000000..fa57a1d33df68 --- /dev/null +++ b/libnetwork/resolvconf/utils_test.go @@ -0,0 +1,18 @@ +package resolvconf + +import ( + "strings" + "testing" +) + +func TestHashData(t *testing.T) { + reader := strings.NewReader("hash-me") + actual, err := hashData(reader) + if err != nil { + t.Fatal(err) + } + expected := "sha256:4d11186aed035cc624d553e10db358492c84a7cd6b9670d92123c144930450aa" + if actual != expected { + t.Fatalf("Expecting %s, got %s", expected, actual) + } +} diff --git a/vendor/github.com/docker/libnetwork/resolver.go b/libnetwork/resolver.go similarity index 82% rename from vendor/github.com/docker/libnetwork/resolver.go rename to libnetwork/resolver.go index 4f5f71897cd06..5530bbff900a2 100644 --- a/vendor/github.com/docker/libnetwork/resolver.go +++ b/libnetwork/resolver.go @@ -8,7 +8,7 @@ import ( "sync" "time" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/types" "github.com/miekg/dns" "github.com/sirupsen/logrus" ) @@ -21,10 +21,10 @@ type Resolver interface { // Stop stops the name server for the container. Stopped resolver // can be reused after running the SetupFunc again. Stop() - // SetupFunc() provides the setup function that should be run + // SetupFunc provides the setup function that should be run // in the container's network namespace. SetupFunc(int) func() - // NameServer() returns the IP of the DNS resolver for the + // NameServer returns the IP of the DNS resolver for the // containers. NameServer() string // SetExtServers configures the external nameservers the resolver @@ -52,7 +52,7 @@ type DNSBackend interface { // ExecFunc allows a function to be executed in the context of the backend // on behalf of the resolver. ExecFunc(f func()) error - //NdotsSet queries the backends ndots dns option settings + // NdotsSet queries the backends ndots dns option settings NdotsSet() bool // HandleQueryResp passes the name & IP from a response to the backend. backend // can use it to maintain any required state about the resolution @@ -157,13 +157,17 @@ func (r *resolver) Start() error { s := &dns.Server{Handler: r, PacketConn: r.conn} r.server = s go func() { - s.ActivateAndServe() + if err := s.ActivateAndServe(); err != nil { + logrus.WithError(err).Error("[resolver] failed to start PacketConn DNS server") + } }() tcpServer := &dns.Server{Handler: r, Listener: r.tcpListen} r.tcpServer = tcpServer go func() { - tcpServer.ActivateAndServe() + if err := tcpServer.ActivateAndServe(); err != nil { + logrus.WithError(err).Error("[resolver] failed to start TCP DNS server") + } }() return nil } @@ -173,10 +177,10 @@ func (r *resolver) Stop() { defer func() { <-r.startCh }() if r.server != nil { - r.server.Shutdown() + r.server.Shutdown() // nolint:errcheck } if r.tcpServer != nil { - r.tcpServer.Shutdown() + r.tcpServer.Shutdown() // nolint:errcheck } r.conn = nil r.tcpServer = nil @@ -210,7 +214,7 @@ func setCommonFlags(msg *dns.Msg) { func shuffleAddr(addr []net.IP) []net.IP { for i := len(addr) - 1; i > 0; i-- { - r := rand.Intn(i + 1) + r := rand.Intn(i + 1) // nolint:gosec // gosec complains about the use of rand here. It should be fine. addr[i], addr[r] = addr[r], addr[i] } return addr @@ -224,7 +228,8 @@ func createRespMsg(query *dns.Msg) *dns.Msg { return resp } -func (r *resolver) handleMXQuery(name string, query *dns.Msg) (*dns.Msg, error) { +func (r *resolver) handleMXQuery(query *dns.Msg) (*dns.Msg, error) { + name := query.Question[0].Name addrv4, _ := r.backend.ResolveName(name, types.IPv4) addrv6, _ := r.backend.ResolveName(name, types.IPv6) @@ -240,9 +245,12 @@ func (r *resolver) handleMXQuery(name string, query *dns.Msg) (*dns.Msg, error) return resp, nil } -func (r *resolver) handleIPQuery(name string, query *dns.Msg, ipType int) (*dns.Msg, error) { - var addr []net.IP - var ipv6Miss bool +func (r *resolver) handleIPQuery(query *dns.Msg, ipType int) (*dns.Msg, error) { + var ( + addr []net.IP + ipv6Miss bool + name = query.Question[0].Name + ) addr, ipv6Miss = r.backend.ResolveName(name, ipType) if addr == nil && ipv6Miss { @@ -279,8 +287,11 @@ func (r *resolver) handleIPQuery(name string, query *dns.Msg, ipType int) (*dns. return resp, nil } -func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) { - var parts []string +func (r *resolver) handlePTRQuery(query *dns.Msg) (*dns.Msg, error) { + var ( + parts []string + ptr = query.Question[0].Name + ) if strings.HasSuffix(ptr, ptrIPv4domain) { parts = strings.Split(ptr, ptrIPv4domain) @@ -310,8 +321,8 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) return resp, nil } -func (r *resolver) handleSRVQuery(svc string, query *dns.Msg) (*dns.Msg, error) { - +func (r *resolver) handleSRVQuery(query *dns.Msg) (*dns.Msg, error) { + svc := query.Question[0].Name srv, ip := r.backend.ResolveService(svc) if len(srv) == 0 { @@ -366,23 +377,27 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { if query == nil || len(query.Question) == 0 { return } - name := query.Question[0].Name - switch query.Question[0].Qtype { + queryName := query.Question[0].Name + queryType := query.Question[0].Qtype + + switch queryType { case dns.TypeA: - resp, err = r.handleIPQuery(name, query, types.IPv4) + resp, err = r.handleIPQuery(query, types.IPv4) case dns.TypeAAAA: - resp, err = r.handleIPQuery(name, query, types.IPv6) + resp, err = r.handleIPQuery(query, types.IPv6) case dns.TypeMX: - resp, err = r.handleMXQuery(name, query) + resp, err = r.handleMXQuery(query) case dns.TypePTR: - resp, err = r.handlePTRQuery(name, query) + resp, err = r.handlePTRQuery(query) case dns.TypeSRV: - resp, err = r.handleSRVQuery(name, query) + resp, err = r.handleSRVQuery(query) + default: + logrus.Debugf("[resolver] query type %s is not supported by the embedded DNS and will be forwarded to external DNS", dns.TypeToString[queryType]) } if err != nil { - logrus.Error(err) + logrus.WithError(err).Errorf("[resolver] failed to handle query: %s (%s) from %s", queryName, dns.TypeToString[queryType], extConn.LocalAddr().String()) return } @@ -392,7 +407,9 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { if !r.proxyDNS { resp = new(dns.Msg) resp.SetRcode(query, dns.RcodeServerFailure) - w.WriteMsg(resp) + if err := w.WriteMsg(resp); err != nil { + logrus.WithError(err).Error("[resolver] error writing dns response") + } return } @@ -400,11 +417,9 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { // in the root domain don't forward it out. We will return // failure and let the client retry with the search domain // attached - switch query.Question[0].Qtype { - case dns.TypeA: - fallthrough - case dns.TypeAAAA: - if r.backend.NdotsSet() && !strings.Contains(strings.TrimSuffix(name, "."), ".") { + switch queryType { + case dns.TypeA, dns.TypeAAAA: + if r.backend.NdotsSet() && !strings.Contains(strings.TrimSuffix(queryName, "."), ".") { resp = createRespMsg(query) } } @@ -449,15 +464,16 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { } } if err != nil { - logrus.Warnf("[resolver] connect failed: %s", err) + logrus.WithField("retries", i).Warnf("[resolver] connect failed: %s", err) continue } - queryType := dns.TypeToString[query.Question[0].Qtype] - logrus.Debugf("[resolver] query %s (%s) from %s, forwarding to %s:%s", name, queryType, + logrus.Debugf("[resolver] query %s (%s) from %s, forwarding to %s:%s", queryName, dns.TypeToString[queryType], extConn.LocalAddr().String(), proto, extDNS.IPStr) // Timeout has to be set for every IO operation. - extConn.SetDeadline(time.Now().Add(extIOTimeout)) + if err := extConn.SetDeadline(time.Now().Add(extIOTimeout)); err != nil { + logrus.WithError(err).Error("[resolver] error setting conn deadline") + } co := &dns.Conn{ Conn: extConn, UDPSize: uint16(maxSize), @@ -484,26 +500,26 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { resp, err = co.ReadMsg() // Truncated DNS replies should be sent to the client so that the // client can retry over TCP - if err != nil && err != dns.ErrTruncated { + if err != nil && (resp == nil || !resp.Truncated) { r.forwardQueryEnd() - logrus.Debugf("[resolver] read from DNS server failed, %s", err) + logrus.WithError(err).Debugf("[resolver] failed to read from DNS server") continue } r.forwardQueryEnd() if resp == nil { - logrus.Debugf("[resolver] external DNS %s:%s returned empty response for %q", proto, extDNS.IPStr, name) + logrus.Debugf("[resolver] external DNS %s:%s returned empty response for %q", proto, extDNS.IPStr, queryName) break } switch resp.Rcode { case dns.RcodeServerFailure, dns.RcodeRefused: // Server returned FAILURE: continue with the next external DNS server // Server returned REFUSED: this can be a transitional status, so continue with the next external DNS server - logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), name) + logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), queryName) continue case dns.RcodeNameError: // Server returned NXDOMAIN. Stop resolution if it's an authoritative answer (see RFC 8020: https://tools.ietf.org/html/rfc8020#section-2) - logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), name) + logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), queryName) if resp.Authoritative { break } @@ -512,7 +528,7 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { // All is well default: // Server gave some error. Log the error, and continue with the next external DNS server - logrus.Debugf("[resolver] external DNS %s:%s responded with %s (code %d) for %q", proto, extDNS.IPStr, statusString(resp.Rcode), resp.Rcode, name) + logrus.Debugf("[resolver] external DNS %s:%s responded with %s (code %d) for %q", proto, extDNS.IPStr, statusString(resp.Rcode), resp.Rcode, queryName) continue } answers := 0 @@ -532,7 +548,7 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { } } if resp.Answer == nil || answers == 0 { - logrus.Debugf("[resolver] external DNS %s:%s did not return any %s records for %q", proto, extDNS.IPStr, queryType, name) + logrus.Debugf("[resolver] external DNS %s:%s did not return any %s records for %q", proto, extDNS.IPStr, dns.TypeToString[queryType], queryName) } resp.Compress = true break @@ -543,7 +559,7 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) { } if err = w.WriteMsg(resp); err != nil { - logrus.Errorf("[resolver] error writing resolver resp, %s", err) + logrus.WithError(err).Errorf("[resolver] failed to write response") } } diff --git a/libnetwork/resolver_test.go b/libnetwork/resolver_test.go new file mode 100644 index 0000000000000..f6911b009f41b --- /dev/null +++ b/libnetwork/resolver_test.go @@ -0,0 +1,283 @@ +package libnetwork + +import ( + "net" + "runtime" + "syscall" + "testing" + "time" + + "github.com/miekg/dns" + "github.com/sirupsen/logrus" + "gotest.tools/v3/skip" +) + +// a simple/null address type that will be used to fake a local address for unit testing +type tstaddr struct { +} + +func (a *tstaddr) Network() string { return "tcp" } + +func (a *tstaddr) String() string { return "127.0.0.1" } + +// a simple writer that implements dns.ResponseWriter for unit testing purposes +type tstwriter struct { + msg *dns.Msg +} + +func (w *tstwriter) WriteMsg(m *dns.Msg) (err error) { + w.msg = m + return nil +} + +func (w *tstwriter) Write(m []byte) (int, error) { return 0, nil } + +func (w *tstwriter) LocalAddr() net.Addr { return new(tstaddr) } + +func (w *tstwriter) RemoteAddr() net.Addr { return new(tstaddr) } + +func (w *tstwriter) TsigStatus() error { return nil } + +func (w *tstwriter) TsigTimersOnly(b bool) {} + +func (w *tstwriter) Hijack() {} + +func (w *tstwriter) Close() error { return nil } + +func (w *tstwriter) GetResponse() *dns.Msg { return w.msg } + +func (w *tstwriter) ClearResponse() { w.msg = nil } + +func checkNonNullResponse(t *testing.T, m *dns.Msg) { + if m == nil { + t.Fatal("Null DNS response found. Non Null response msg expected.") + } +} + +func checkDNSAnswersCount(t *testing.T, m *dns.Msg, expected int) { + answers := len(m.Answer) + if answers != expected { + t.Fatalf("Expected number of answers in response: %d. Found: %d", expected, answers) + } +} + +func checkDNSResponseCode(t *testing.T, m *dns.Msg, expected int) { + if m.MsgHdr.Rcode != expected { + t.Fatalf("Expected DNS response code: %d. Found: %d", expected, m.MsgHdr.Rcode) + } +} + +func checkDNSRRType(t *testing.T, actual, expected uint16) { + if actual != expected { + t.Fatalf("Expected DNS Rrtype: %d. Found: %d", expected, actual) + } +} + +func TestDNSIPQuery(t *testing.T) { + skip.If(t, runtime.GOOS == "windows", "test only works on linux") + + c, err := New() + if err != nil { + t.Fatal(err) + } + defer c.Stop() + + n, err := c.NewNetwork("bridge", "dtnet1", "", nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + ep, err := n.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + sb, err := c.NewSandbox("c1") + if err != nil { + t.Fatal(err) + } + + defer func() { + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + // we need the endpoint only to populate ep_list for the sandbox as part of resolve_name + // it is not set as a target for name resolution and does not serve any other purpose + err = ep.Join(sb) + if err != nil { + t.Fatal(err) + } + + // add service records which are used to resolve names. These are the real targets for the DNS querries + n.(*network).addSvcRecords("ep1", "name1", "svc1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test") + + w := new(tstwriter) + // the unit tests right now will focus on non-proxyed DNS requests + r := NewResolver(resolverIPSandbox, false, sb.Key(), sb.(*sandbox)) + + // test name1's IP is resolved correctly with the default A type query + // Also make sure DNS lookups are case insensitive + names := []string{"name1", "NaMe1"} + for _, name := range names { + q := new(dns.Msg) + q.SetQuestion(name, dns.TypeA) + r.(*resolver).ServeDNS(w, q) + resp := w.GetResponse() + checkNonNullResponse(t, resp) + t.Log("Response: ", resp.String()) + checkDNSResponseCode(t, resp, dns.RcodeSuccess) + checkDNSAnswersCount(t, resp, 1) + checkDNSRRType(t, resp.Answer[0].Header().Rrtype, dns.TypeA) + if answer, ok := resp.Answer[0].(*dns.A); ok { + if !answer.A.Equal(net.ParseIP("192.168.0.1")) { + t.Fatalf("IP response in Answer %v does not match 192.168.0.1", answer.A) + } + } else { + t.Fatal("Answer of type A not found") + } + w.ClearResponse() + } + + // test MX query with name1 results in Success response with 0 answer records + q := new(dns.Msg) + q.SetQuestion("name1", dns.TypeMX) + r.(*resolver).ServeDNS(w, q) + resp := w.GetResponse() + checkNonNullResponse(t, resp) + t.Log("Response: ", resp.String()) + checkDNSResponseCode(t, resp, dns.RcodeSuccess) + checkDNSAnswersCount(t, resp, 0) + w.ClearResponse() + + // test MX query with non existent name results in ServFail response with 0 answer records + // since this is a unit test env, we disable proxying DNS above which results in ServFail rather than NXDOMAIN + q = new(dns.Msg) + q.SetQuestion("nonexistent", dns.TypeMX) + r.(*resolver).ServeDNS(w, q) + resp = w.GetResponse() + checkNonNullResponse(t, resp) + t.Log("Response: ", resp.String()) + checkDNSResponseCode(t, resp, dns.RcodeServerFailure) + w.ClearResponse() + +} + +func newDNSHandlerServFailOnce(requests *int) func(w dns.ResponseWriter, r *dns.Msg) { + return func(w dns.ResponseWriter, r *dns.Msg) { + m := new(dns.Msg) + m.SetReply(r) + m.Compress = false + if *requests == 0 { + m.SetRcode(r, dns.RcodeServerFailure) + } + *requests = *requests + 1 + if err := w.WriteMsg(m); err != nil { + logrus.WithError(err).Error("Error writing dns response") + } + } +} + +func waitForLocalDNSServer(t *testing.T) { + retries := 0 + maxRetries := 10 + + for retries < maxRetries { + t.Log("Try connecting to DNS server ...") + // this test and retry mechanism only works for TCP. With UDP there is no + // connection and the test becomes inaccurate leading to unpredictable results + tconn, err := net.DialTimeout("tcp", "127.0.0.1:53", 10*time.Second) + retries = retries + 1 + if err != nil { + if oerr, ok := err.(*net.OpError); ok { + // server is probably initializing + if oerr.Err == syscall.ECONNREFUSED { + continue + } + } else { + // something is wrong: we should stop for analysis + t.Fatal(err) + } + } + if tconn != nil { + tconn.Close() + break + } + } +} + +func TestDNSProxyServFail(t *testing.T) { + skip.If(t, runtime.GOOS == "windows", "test only works on linux") + + c, err := New() + if err != nil { + t.Fatal(err) + } + defer c.Stop() + + n, err := c.NewNetwork("bridge", "dtnet2", "", nil) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := n.Delete(); err != nil { + t.Fatal(err) + } + }() + + sb, err := c.NewSandbox("c1") + if err != nil { + t.Fatal(err) + } + + defer func() { + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + var nRequests int + // initialize a local DNS server and configure it to fail the first query + dns.HandleFunc(".", newDNSHandlerServFailOnce(&nRequests)) + // use TCP for predictable results. Connection tests (to figure out DNS server initialization) don't work with UDP + server := &dns.Server{Addr: "127.0.0.1:53", Net: "tcp"} + srvErrCh := make(chan error, 1) + go func() { + srvErrCh <- server.ListenAndServe() + }() + defer func() { + server.Shutdown() // nolint:errcheck + if err := <-srvErrCh; err != nil { + t.Error(err) + } + }() + + waitForLocalDNSServer(t) + t.Log("DNS Server can be reached") + + w := new(tstwriter) + r := NewResolver(resolverIPSandbox, true, sb.Key(), sb.(*sandbox)) + q := new(dns.Msg) + q.SetQuestion("name1.", dns.TypeA) + + var localDNSEntries []extDNSEntry + extTestDNSEntry := extDNSEntry{IPStr: "127.0.0.1", HostLoopback: true} + + // configure two external DNS entries and point both to local DNS server thread + localDNSEntries = append(localDNSEntries, extTestDNSEntry) + localDNSEntries = append(localDNSEntries, extTestDNSEntry) + + // this should generate two requests: the first will fail leading to a retry + r.(*resolver).SetExtServers(localDNSEntries) + r.(*resolver).ServeDNS(w, q) + if nRequests != 2 { + t.Fatalf("Expected 2 DNS querries. Found: %d", nRequests) + } + t.Logf("Expected number of DNS requests generated") +} diff --git a/libnetwork/resolver_unix.go b/libnetwork/resolver_unix.go new file mode 100644 index 0000000000000..ff00f3af6bb87 --- /dev/null +++ b/libnetwork/resolver_unix.go @@ -0,0 +1,105 @@ +//go:build !windows +// +build !windows + +package libnetwork + +import ( + "fmt" + "net" + "os" + "os/exec" + "runtime" + + "github.com/docker/docker/libnetwork/iptables" + "github.com/docker/docker/pkg/reexec" + "github.com/sirupsen/logrus" + "github.com/vishvananda/netns" +) + +func init() { + reexec.Register("setup-resolver", reexecSetupResolver) +} + +const ( + // outputChain used for docker embed dns + outputChain = "DOCKER_OUTPUT" + //postroutingchain used for docker embed dns + postroutingchain = "DOCKER_POSTROUTING" +) + +func reexecSetupResolver() { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if len(os.Args) < 4 { + logrus.Error("invalid number of arguments..") + os.Exit(1) + } + + resolverIP, ipPort, _ := net.SplitHostPort(os.Args[2]) + _, tcpPort, _ := net.SplitHostPort(os.Args[3]) + rules := [][]string{ + {"-t", "nat", "-I", outputChain, "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", os.Args[2]}, + {"-t", "nat", "-I", postroutingchain, "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort}, + {"-t", "nat", "-I", outputChain, "-d", resolverIP, "-p", "tcp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", os.Args[3]}, + {"-t", "nat", "-I", postroutingchain, "-s", resolverIP, "-p", "tcp", "--sport", tcpPort, "-j", "SNAT", "--to-source", ":" + dnsPort}, + } + + f, err := os.OpenFile(os.Args[1], os.O_RDONLY, 0) + if err != nil { + logrus.Errorf("failed get network namespace %q: %v", os.Args[1], err) + os.Exit(2) + } + defer f.Close() //nolint:gosec + + nsFD := f.Fd() + if err = netns.Set(netns.NsHandle(nsFD)); err != nil { + logrus.Errorf("setting into container net ns %v failed, %v", os.Args[1], err) + os.Exit(3) + } + + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) + + // insert outputChain and postroutingchain + err = iptable.RawCombinedOutputNative("-t", "nat", "-C", "OUTPUT", "-d", resolverIP, "-j", outputChain) + if err == nil { + iptable.RawCombinedOutputNative("-t", "nat", "-F", outputChain) + } else { + iptable.RawCombinedOutputNative("-t", "nat", "-N", outputChain) + iptable.RawCombinedOutputNative("-t", "nat", "-I", "OUTPUT", "-d", resolverIP, "-j", outputChain) + } + + err = iptable.RawCombinedOutputNative("-t", "nat", "-C", "POSTROUTING", "-d", resolverIP, "-j", postroutingchain) + if err == nil { + iptable.RawCombinedOutputNative("-t", "nat", "-F", postroutingchain) + } else { + iptable.RawCombinedOutputNative("-t", "nat", "-N", postroutingchain) + iptable.RawCombinedOutputNative("-t", "nat", "-I", "POSTROUTING", "-d", resolverIP, "-j", postroutingchain) + } + + for _, rule := range rules { + if iptable.RawCombinedOutputNative(rule...) != nil { + logrus.Errorf("set up rule failed, %v", rule) + } + } +} + +func (r *resolver) setupIPTable() error { + if r.err != nil { + return r.err + } + laddr := r.conn.LocalAddr().String() + ltcpaddr := r.tcpListen.Addr().String() + + cmd := &exec.Cmd{ + Path: reexec.Self(), + Args: append([]string{"setup-resolver"}, r.resolverKey, laddr, ltcpaddr), + Stdout: os.Stdout, + Stderr: os.Stderr, + } + if err := cmd.Run(); err != nil { + return fmt.Errorf("reexec failed: %v", err) + } + return nil +} diff --git a/vendor/github.com/docker/libnetwork/resolver_windows.go b/libnetwork/resolver_windows.go similarity index 83% rename from vendor/github.com/docker/libnetwork/resolver_windows.go rename to libnetwork/resolver_windows.go index aa33b1a2ec75f..3d422fcd06ff3 100644 --- a/vendor/github.com/docker/libnetwork/resolver_windows.go +++ b/libnetwork/resolver_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package libnetwork diff --git a/libnetwork/sandbox.go b/libnetwork/sandbox.go new file mode 100644 index 0000000000000..31de696c25ed3 --- /dev/null +++ b/libnetwork/sandbox.go @@ -0,0 +1,1265 @@ +package libnetwork + +import ( + "encoding/json" + "fmt" + "net" + "sort" + "strings" + "sync" + "time" + + "github.com/docker/docker/libnetwork/etchosts" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/types" + "github.com/sirupsen/logrus" +) + +// Sandbox provides the control over the network container entity. It is a one to one mapping with the container. +type Sandbox interface { + // ID returns the ID of the sandbox + ID() string + // Key returns the sandbox's key + Key() string + // ContainerID returns the container id associated to this sandbox + ContainerID() string + // Labels returns the sandbox's labels + Labels() map[string]interface{} + // Statistics retrieves the interfaces' statistics for the sandbox + Statistics() (map[string]*types.InterfaceStatistics, error) + // Refresh leaves all the endpoints, resets and re-applies the options, + // re-joins all the endpoints without destroying the osl sandbox + Refresh(options ...SandboxOption) error + // SetKey updates the Sandbox Key + SetKey(key string) error + // Rename changes the name of all attached Endpoints + Rename(name string) error + // Delete destroys this container after detaching it from all connected endpoints. + Delete() error + // Endpoints returns all the endpoints connected to the sandbox + Endpoints() []Endpoint + // ResolveService returns all the backend details about the containers or hosts + // backing a service. Its purpose is to satisfy an SRV query + ResolveService(name string) ([]*net.SRV, []net.IP) + // EnableService makes a managed container's service available by adding the + // endpoint to the service load balancer and service discovery + EnableService() error + // DisableService removes a managed container's endpoints from the load balancer + // and service discovery + DisableService() error +} + +// SandboxOption is an option setter function type used to pass various options to +// NewNetContainer method. The various setter functions of type SandboxOption are +// provided by libnetwork, they look like ContainerOptionXXXX(...) +type SandboxOption func(sb *sandbox) + +func (sb *sandbox) processOptions(options ...SandboxOption) { + for _, opt := range options { + if opt != nil { + opt(sb) + } + } +} + +type sandbox struct { + id string + containerID string + config containerConfig + extDNS []extDNSEntry + osSbox osl.Sandbox + controller *controller + resolver Resolver + resolverOnce sync.Once + endpoints []*endpoint + epPriority map[string]int + populatedEndpoints map[string]struct{} + joinLeaveDone chan struct{} + dbIndex uint64 + dbExists bool + isStub bool + inDelete bool + ingress bool + ndotsSet bool + oslTypes []osl.SandboxType // slice of properties of this sandbox + loadBalancerNID string // NID that this SB is a load balancer for + sync.Mutex + // This mutex is used to serialize service related operation for an endpoint + // The lock is here because the endpoint is saved into the store so is not unique + Service sync.Mutex +} + +// These are the container configs used to customize container /etc/hosts file. +type hostsPathConfig struct { + // Note(cpuguy83): The linter is drunk and says none of these fields are used while they are + hostName string // nolint:structcheck + domainName string // nolint:structcheck + hostsPath string // nolint:structcheck + originHostsPath string // nolint:structcheck + extraHosts []extraHost // nolint:structcheck + parentUpdates []parentUpdate // nolint:structcheck +} + +type parentUpdate struct { + cid string + name string + ip string +} + +type extraHost struct { + name string + IP string +} + +// These are the container configs used to customize container /etc/resolv.conf file. +type resolvConfPathConfig struct { + // Note(cpuguy83): The linter is drunk and says none of these fields are used while they are + resolvConfPath string // nolint:structcheck + originResolvConfPath string // nolint:structcheck + resolvConfHashFile string // nolint:structcheck + dnsList []string // nolint:structcheck + dnsSearchList []string // nolint:structcheck + dnsOptionsList []string // nolint:structcheck +} + +type containerConfig struct { + hostsPathConfig + resolvConfPathConfig + generic map[string]interface{} + useDefaultSandBox bool + useExternalKey bool + exposedPorts []types.TransportPort +} + +const ( + resolverIPSandbox = "127.0.0.11" +) + +func (sb *sandbox) ID() string { + return sb.id +} + +func (sb *sandbox) ContainerID() string { + return sb.containerID +} + +func (sb *sandbox) Key() string { + if sb.config.useDefaultSandBox { + return osl.GenerateKey("default") + } + return osl.GenerateKey(sb.id) +} + +func (sb *sandbox) Labels() map[string]interface{} { + sb.Lock() + defer sb.Unlock() + opts := make(map[string]interface{}, len(sb.config.generic)) + for k, v := range sb.config.generic { + opts[k] = v + } + return opts +} + +func (sb *sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) { + m := make(map[string]*types.InterfaceStatistics) + + sb.Lock() + osb := sb.osSbox + sb.Unlock() + if osb == nil { + return m, nil + } + + var err error + for _, i := range osb.Info().Interfaces() { + if m[i.DstName()], err = i.Statistics(); err != nil { + return m, err + } + } + + return m, nil +} + +func (sb *sandbox) Delete() error { + return sb.delete(false) +} + +func (sb *sandbox) delete(force bool) error { + sb.Lock() + if sb.inDelete { + sb.Unlock() + return types.ForbiddenErrorf("another sandbox delete in progress") + } + // Set the inDelete flag. This will ensure that we don't + // update the store until we have completed all the endpoint + // leaves and deletes. And when endpoint leaves and deletes + // are completed then we can finally delete the sandbox object + // altogether from the data store. If the daemon exits + // ungracefully in the middle of a sandbox delete this way we + // will have all the references to the endpoints in the + // sandbox so that we can clean them up when we restart + sb.inDelete = true + sb.Unlock() + + c := sb.controller + + // Detach from all endpoints + retain := false + for _, ep := range sb.getConnectedEndpoints() { + // gw network endpoint detach and removal are automatic + if ep.endpointInGWNetwork() && !force { + continue + } + // Retain the sanbdox if we can't obtain the network from store. + if _, err := c.getNetworkFromStore(ep.getNetwork().ID()); err != nil { + if c.isDistributedControl() { + retain = true + } + logrus.Warnf("Failed getting network for ep %s during sandbox %s delete: %v", ep.ID(), sb.ID(), err) + continue + } + + if !force { + if err := ep.Leave(sb); err != nil { + logrus.Warnf("Failed detaching sandbox %s from endpoint %s: %v\n", sb.ID(), ep.ID(), err) + } + } + + if err := ep.Delete(force); err != nil { + logrus.Warnf("Failed deleting endpoint %s: %v\n", ep.ID(), err) + } + } + + if retain { + sb.Lock() + sb.inDelete = false + sb.Unlock() + return fmt.Errorf("could not cleanup all the endpoints in container %s / sandbox %s", sb.containerID, sb.id) + } + // Container is going away. Path cache in etchosts is most + // likely not required any more. Drop it. + etchosts.Drop(sb.config.hostsPath) + + if sb.resolver != nil { + sb.resolver.Stop() + } + + if sb.osSbox != nil && !sb.config.useDefaultSandBox { + if err := sb.osSbox.Destroy(); err != nil { + logrus.WithError(err).Warn("error destroying network sandbox") + } + } + + if err := sb.storeDelete(); err != nil { + logrus.Warnf("Failed to delete sandbox %s from store: %v", sb.ID(), err) + } + + c.Lock() + if sb.ingress { + c.ingressSandbox = nil + } + delete(c.sandboxes, sb.ID()) + c.Unlock() + + return nil +} + +func (sb *sandbox) Rename(name string) error { + var err error + + for _, ep := range sb.getConnectedEndpoints() { + if ep.endpointInGWNetwork() { + continue + } + + oldName := ep.Name() + lEp := ep + if err = ep.rename(name); err != nil { + break + } + + defer func() { + if err != nil { + if err2 := lEp.rename(oldName); err2 != nil { + logrus.WithField("old", oldName).WithField("origError", err).WithError(err2).Error("error renaming sandbox") + } + } + }() + } + + return err +} + +func (sb *sandbox) Refresh(options ...SandboxOption) error { + // Store connected endpoints + epList := sb.getConnectedEndpoints() + + // Detach from all endpoints + for _, ep := range epList { + if err := ep.Leave(sb); err != nil { + logrus.Warnf("Failed detaching sandbox %s from endpoint %s: %v\n", sb.ID(), ep.ID(), err) + } + } + + // Re-apply options + sb.config = containerConfig{} + sb.processOptions(options...) + + // Setup discovery files + if err := sb.setupResolutionFiles(); err != nil { + return err + } + + // Re-connect to all endpoints + for _, ep := range epList { + if err := ep.Join(sb); err != nil { + logrus.Warnf("Failed attach sandbox %s to endpoint %s: %v\n", sb.ID(), ep.ID(), err) + } + } + + return nil +} + +func (sb *sandbox) MarshalJSON() ([]byte, error) { + sb.Lock() + defer sb.Unlock() + + // We are just interested in the container ID. This can be expanded to include all of containerInfo if there is a need + return json.Marshal(sb.id) +} + +func (sb *sandbox) UnmarshalJSON(b []byte) (err error) { + sb.Lock() + defer sb.Unlock() + + var id string + if err := json.Unmarshal(b, &id); err != nil { + return err + } + sb.id = id + return nil +} + +func (sb *sandbox) Endpoints() []Endpoint { + sb.Lock() + defer sb.Unlock() + + endpoints := make([]Endpoint, len(sb.endpoints)) + for i, ep := range sb.endpoints { + endpoints[i] = ep + } + return endpoints +} + +func (sb *sandbox) getConnectedEndpoints() []*endpoint { + sb.Lock() + defer sb.Unlock() + + eps := make([]*endpoint, len(sb.endpoints)) + copy(eps, sb.endpoints) + + return eps +} + +func (sb *sandbox) addEndpoint(ep *endpoint) { + sb.Lock() + defer sb.Unlock() + + l := len(sb.endpoints) + i := sort.Search(l, func(j int) bool { + return ep.Less(sb.endpoints[j]) + }) + + sb.endpoints = append(sb.endpoints, nil) + copy(sb.endpoints[i+1:], sb.endpoints[i:]) + sb.endpoints[i] = ep +} + +func (sb *sandbox) removeEndpoint(ep *endpoint) { + sb.Lock() + defer sb.Unlock() + + sb.removeEndpointRaw(ep) +} + +func (sb *sandbox) removeEndpointRaw(ep *endpoint) { + for i, e := range sb.endpoints { + if e == ep { + sb.endpoints = append(sb.endpoints[:i], sb.endpoints[i+1:]...) + return + } + } +} + +func (sb *sandbox) getEndpoint(id string) *endpoint { + sb.Lock() + defer sb.Unlock() + + for _, ep := range sb.endpoints { + if ep.id == id { + return ep + } + } + + return nil +} + +func (sb *sandbox) updateGateway(ep *endpoint) error { + sb.Lock() + osSbox := sb.osSbox + sb.Unlock() + if osSbox == nil { + return nil + } + osSbox.UnsetGateway() // nolint:errcheck + osSbox.UnsetGatewayIPv6() // nolint:errcheck + + if ep == nil { + return nil + } + + ep.Lock() + joinInfo := ep.joinInfo + ep.Unlock() + + if err := osSbox.SetGateway(joinInfo.gw); err != nil { + return fmt.Errorf("failed to set gateway while updating gateway: %v", err) + } + + if err := osSbox.SetGatewayIPv6(joinInfo.gw6); err != nil { + return fmt.Errorf("failed to set IPv6 gateway while updating gateway: %v", err) + } + + return nil +} + +func (sb *sandbox) HandleQueryResp(name string, ip net.IP) { + for _, ep := range sb.getConnectedEndpoints() { + n := ep.getNetwork() + n.HandleQueryResp(name, ip) + } +} + +func (sb *sandbox) ResolveIP(ip string) string { + var svc string + logrus.Debugf("IP To resolve %v", ip) + + for _, ep := range sb.getConnectedEndpoints() { + n := ep.getNetwork() + svc = n.ResolveIP(ip) + if len(svc) != 0 { + return svc + } + } + + return svc +} + +func (sb *sandbox) ExecFunc(f func()) error { + sb.Lock() + osSbox := sb.osSbox + sb.Unlock() + if osSbox != nil { + return osSbox.InvokeFunc(f) + } + return fmt.Errorf("osl sandbox unavailable in ExecFunc for %v", sb.ContainerID()) +} + +func (sb *sandbox) ResolveService(name string) ([]*net.SRV, []net.IP) { + srv := []*net.SRV{} + ip := []net.IP{} + + logrus.Debugf("Service name To resolve: %v", name) + + // There are DNS implementations that allow SRV queries for names not in + // the format defined by RFC 2782. Hence specific validations checks are + // not done + parts := strings.Split(name, ".") + if len(parts) < 3 { + return nil, nil + } + + for _, ep := range sb.getConnectedEndpoints() { + n := ep.getNetwork() + + srv, ip = n.ResolveService(name) + if len(srv) > 0 { + break + } + } + return srv, ip +} + +func getDynamicNwEndpoints(epList []*endpoint) []*endpoint { + eps := []*endpoint{} + for _, ep := range epList { + n := ep.getNetwork() + if n.dynamic && !n.ingress { + eps = append(eps, ep) + } + } + return eps +} + +func getIngressNwEndpoint(epList []*endpoint) *endpoint { + for _, ep := range epList { + n := ep.getNetwork() + if n.ingress { + return ep + } + } + return nil +} + +func getLocalNwEndpoints(epList []*endpoint) []*endpoint { + eps := []*endpoint{} + for _, ep := range epList { + n := ep.getNetwork() + if !n.dynamic && !n.ingress { + eps = append(eps, ep) + } + } + return eps +} + +func (sb *sandbox) ResolveName(name string, ipType int) ([]net.IP, bool) { + // Embedded server owns the docker network domain. Resolution should work + // for both container_name and container_name.network_name + // We allow '.' in service name and network name. For a name a.b.c.d the + // following have to tried; + // {a.b.c.d in the networks container is connected to} + // {a.b.c in network d}, + // {a.b in network c.d}, + // {a in network b.c.d}, + + logrus.Debugf("Name To resolve: %v", name) + name = strings.TrimSuffix(name, ".") + reqName := []string{name} + networkName := []string{""} + + if strings.Contains(name, ".") { + var i int + dup := name + for { + if i = strings.LastIndex(dup, "."); i == -1 { + break + } + networkName = append(networkName, name[i+1:]) + reqName = append(reqName, name[:i]) + + dup = dup[:i] + } + } + + epList := sb.getConnectedEndpoints() + + // In swarm mode services with exposed ports are connected to user overlay + // network, ingress network and docker_gwbridge network. Name resolution + // should prioritize returning the VIP/IPs on user overlay network. + newList := []*endpoint{} + if !sb.controller.isDistributedControl() { + newList = append(newList, getDynamicNwEndpoints(epList)...) + ingressEP := getIngressNwEndpoint(epList) + if ingressEP != nil { + newList = append(newList, ingressEP) + } + newList = append(newList, getLocalNwEndpoints(epList)...) + epList = newList + } + + for i := 0; i < len(reqName); i++ { + + // First check for local container alias + ip, ipv6Miss := sb.resolveName(reqName[i], networkName[i], epList, true, ipType) + if ip != nil { + return ip, false + } + if ipv6Miss { + return ip, ipv6Miss + } + + // Resolve the actual container name + ip, ipv6Miss = sb.resolveName(reqName[i], networkName[i], epList, false, ipType) + if ip != nil { + return ip, false + } + if ipv6Miss { + return ip, ipv6Miss + } + } + return nil, false +} + +func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool, ipType int) ([]net.IP, bool) { + var ipv6Miss bool + + for _, ep := range epList { + name := req + n := ep.getNetwork() + + if networkName != "" && networkName != n.Name() { + continue + } + + if alias { + if ep.aliases == nil { + continue + } + + var ok bool + ep.Lock() + name, ok = ep.aliases[req] + ep.Unlock() + if !ok { + continue + } + } else { + // If it is a regular lookup and if the requested name is an alias + // don't perform a svc lookup for this endpoint. + ep.Lock() + if _, ok := ep.aliases[req]; ok { + ep.Unlock() + continue + } + ep.Unlock() + } + + ip, miss := n.ResolveName(name, ipType) + + if ip != nil { + return ip, false + } + + if miss { + ipv6Miss = miss + } + } + return nil, ipv6Miss +} + +func (sb *sandbox) SetKey(basePath string) error { + start := time.Now() + defer func() { + logrus.Debugf("sandbox set key processing took %s for container %s", time.Since(start), sb.ContainerID()) + }() + + if basePath == "" { + return types.BadRequestErrorf("invalid sandbox key") + } + + sb.Lock() + if sb.inDelete { + sb.Unlock() + return types.ForbiddenErrorf("failed to SetKey: sandbox %q delete in progress", sb.id) + } + oldosSbox := sb.osSbox + sb.Unlock() + + if oldosSbox != nil { + // If we already have an OS sandbox, release the network resources from that + // and destroy the OS snab. We are moving into a new home further down. Note that none + // of the network resources gets destroyed during the move. + sb.releaseOSSbox() + } + + osSbox, err := osl.GetSandboxForExternalKey(basePath, sb.Key()) + if err != nil { + return err + } + + sb.Lock() + sb.osSbox = osSbox + sb.Unlock() + + // If the resolver was setup before stop it and set it up in the + // new osl sandbox. + if oldosSbox != nil && sb.resolver != nil { + sb.resolver.Stop() + + if err := sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0)); err == nil { + if err := sb.resolver.Start(); err != nil { + logrus.Errorf("Resolver Start failed for container %s, %q", sb.ContainerID(), err) + } + } else { + logrus.Errorf("Resolver Setup Function failed for container %s, %q", sb.ContainerID(), err) + } + } + + for _, ep := range sb.getConnectedEndpoints() { + if err = sb.populateNetworkResources(ep); err != nil { + return err + } + } + return nil +} + +func (sb *sandbox) EnableService() (err error) { + logrus.Debugf("EnableService %s START", sb.containerID) + defer func() { + if err != nil { + if err2 := sb.DisableService(); err2 != nil { + logrus.WithError(err2).WithField("origError", err).Error("Error while disabling service after original error") + } + } + }() + for _, ep := range sb.getConnectedEndpoints() { + if !ep.isServiceEnabled() { + if err := ep.addServiceInfoToCluster(sb); err != nil { + return fmt.Errorf("could not update state for endpoint %s into cluster: %v", ep.Name(), err) + } + ep.enableService() + } + } + logrus.Debugf("EnableService %s DONE", sb.containerID) + return nil +} + +func (sb *sandbox) DisableService() (err error) { + logrus.Debugf("DisableService %s START", sb.containerID) + failedEps := []string{} + defer func() { + if len(failedEps) > 0 { + err = fmt.Errorf("failed to disable service on sandbox:%s, for endpoints %s", sb.ID(), strings.Join(failedEps, ",")) + } + }() + for _, ep := range sb.getConnectedEndpoints() { + if ep.isServiceEnabled() { + if err := ep.deleteServiceInfoFromCluster(sb, false, "DisableService"); err != nil { + failedEps = append(failedEps, ep.Name()) + logrus.Warnf("failed update state for endpoint %s into cluster: %v", ep.Name(), err) + } + ep.disableService() + } + } + logrus.Debugf("DisableService %s DONE", sb.containerID) + return nil +} + +func releaseOSSboxResources(osSbox osl.Sandbox, ep *endpoint) { + for _, i := range osSbox.Info().Interfaces() { + // Only remove the interfaces owned by this endpoint from the sandbox. + if ep.hasInterface(i.SrcName()) { + if err := i.Remove(); err != nil { + logrus.Debugf("Remove interface %s failed: %v", i.SrcName(), err) + } + } + } + + ep.Lock() + joinInfo := ep.joinInfo + vip := ep.virtualIP + lbModeIsDSR := ep.network.loadBalancerMode == loadBalancerModeDSR + ep.Unlock() + + if len(vip) > 0 && lbModeIsDSR { + ipNet := &net.IPNet{IP: vip, Mask: net.CIDRMask(32, 32)} + if err := osSbox.RemoveAliasIP(osSbox.GetLoopbackIfaceName(), ipNet); err != nil { + logrus.WithError(err).Debugf("failed to remove virtual ip %v to loopback", ipNet) + } + } + + if joinInfo == nil { + return + } + + // Remove non-interface routes. + for _, r := range joinInfo.StaticRoutes { + if err := osSbox.RemoveStaticRoute(r); err != nil { + logrus.Debugf("Remove route failed: %v", err) + } + } +} + +func (sb *sandbox) releaseOSSbox() { + sb.Lock() + osSbox := sb.osSbox + sb.osSbox = nil + sb.Unlock() + + if osSbox == nil { + return + } + + for _, ep := range sb.getConnectedEndpoints() { + releaseOSSboxResources(osSbox, ep) + } + + if err := osSbox.Destroy(); err != nil { + logrus.WithError(err).Error("Error destroying os sandbox") + } +} + +func (sb *sandbox) restoreOslSandbox() error { + var routes []*types.StaticRoute + + // restore osl sandbox + Ifaces := make(map[string][]osl.IfaceOption) + for _, ep := range sb.endpoints { + var ifaceOptions []osl.IfaceOption + ep.Lock() + joinInfo := ep.joinInfo + i := ep.iface + ep.Unlock() + + if i == nil { + logrus.Errorf("error restoring endpoint %s for container %s", ep.Name(), sb.ContainerID()) + continue + } + + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes)) + if i.addrv6 != nil && i.addrv6.IP.To16() != nil { + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(i.addrv6)) + } + if i.mac != nil { + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().MacAddress(i.mac)) + } + if len(i.llAddrs) != 0 { + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().LinkLocalAddresses(i.llAddrs)) + } + Ifaces[fmt.Sprintf("%s+%s", i.srcName, i.dstPrefix)] = ifaceOptions + if joinInfo != nil { + routes = append(routes, joinInfo.StaticRoutes...) + } + if ep.needResolver() { + sb.startResolver(true) + } + } + + gwep := sb.getGatewayEndpoint() + if gwep == nil { + return nil + } + + // restore osl sandbox + err := sb.osSbox.Restore(Ifaces, routes, gwep.joinInfo.gw, gwep.joinInfo.gw6) + return err +} + +func (sb *sandbox) populateNetworkResources(ep *endpoint) error { + sb.Lock() + if sb.osSbox == nil { + sb.Unlock() + return nil + } + inDelete := sb.inDelete + sb.Unlock() + + ep.Lock() + joinInfo := ep.joinInfo + i := ep.iface + lbModeIsDSR := ep.network.loadBalancerMode == loadBalancerModeDSR + ep.Unlock() + + if ep.needResolver() { + sb.startResolver(false) + } + + if i != nil && i.srcName != "" { + var ifaceOptions []osl.IfaceOption + + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes)) + if i.addrv6 != nil && i.addrv6.IP.To16() != nil { + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(i.addrv6)) + } + if len(i.llAddrs) != 0 { + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().LinkLocalAddresses(i.llAddrs)) + } + if i.mac != nil { + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().MacAddress(i.mac)) + } + + if err := sb.osSbox.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil { + return fmt.Errorf("failed to add interface %s to sandbox: %v", i.srcName, err) + } + + if len(ep.virtualIP) > 0 && lbModeIsDSR { + if sb.loadBalancerNID == "" { + if err := sb.osSbox.DisableARPForVIP(i.srcName); err != nil { + return fmt.Errorf("failed disable ARP for VIP: %v", err) + } + } + ipNet := &net.IPNet{IP: ep.virtualIP, Mask: net.CIDRMask(32, 32)} + if err := sb.osSbox.AddAliasIP(sb.osSbox.GetLoopbackIfaceName(), ipNet); err != nil { + return fmt.Errorf("failed to add virtual ip %v to loopback: %v", ipNet, err) + } + } + } + + if joinInfo != nil { + // Set up non-interface routes. + for _, r := range joinInfo.StaticRoutes { + if err := sb.osSbox.AddStaticRoute(r); err != nil { + return fmt.Errorf("failed to add static route %s: %v", r.Destination.String(), err) + } + } + } + + if ep == sb.getGatewayEndpoint() { + if err := sb.updateGateway(ep); err != nil { + return err + } + } + + // Make sure to add the endpoint to the populated endpoint set + // before populating loadbalancers. + sb.Lock() + sb.populatedEndpoints[ep.ID()] = struct{}{} + sb.Unlock() + + // Populate load balancer only after updating all the other + // information including gateway and other routes so that + // loadbalancers are populated all the network state is in + // place in the sandbox. + sb.populateLoadBalancers(ep) + + // Only update the store if we did not come here as part of + // sandbox delete. If we came here as part of delete then do + // not bother updating the store. The sandbox object will be + // deleted anyway + if !inDelete { + return sb.storeUpdate() + } + + return nil +} + +func (sb *sandbox) clearNetworkResources(origEp *endpoint) error { + ep := sb.getEndpoint(origEp.id) + if ep == nil { + return fmt.Errorf("could not find the sandbox endpoint data for endpoint %s", + origEp.id) + } + + sb.Lock() + osSbox := sb.osSbox + inDelete := sb.inDelete + sb.Unlock() + if osSbox != nil { + releaseOSSboxResources(osSbox, ep) + } + + sb.Lock() + delete(sb.populatedEndpoints, ep.ID()) + + if len(sb.endpoints) == 0 { + // sb.endpoints should never be empty and this is unexpected error condition + // We log an error message to note this down for debugging purposes. + logrus.Errorf("No endpoints in sandbox while trying to remove endpoint %s", ep.Name()) + sb.Unlock() + return nil + } + + var ( + gwepBefore, gwepAfter *endpoint + index = -1 + ) + for i, e := range sb.endpoints { + if e == ep { + index = i + } + if len(e.Gateway()) > 0 && gwepBefore == nil { + gwepBefore = e + } + if index != -1 && gwepBefore != nil { + break + } + } + + if index == -1 { + logrus.Warnf("Endpoint %s has already been deleted", ep.Name()) + sb.Unlock() + return nil + } + + sb.removeEndpointRaw(ep) + for _, e := range sb.endpoints { + if len(e.Gateway()) > 0 { + gwepAfter = e + break + } + } + delete(sb.epPriority, ep.ID()) + sb.Unlock() + + if gwepAfter != nil && gwepBefore != gwepAfter { + if err := sb.updateGateway(gwepAfter); err != nil { + return err + } + } + + // Only update the store if we did not come here as part of + // sandbox delete. If we came here as part of delete then do + // not bother updating the store. The sandbox object will be + // deleted anyway + if !inDelete { + return sb.storeUpdate() + } + + return nil +} + +// joinLeaveStart waits to ensure there are no joins or leaves in progress and +// marks this join/leave in progress without race +func (sb *sandbox) joinLeaveStart() { + sb.Lock() + defer sb.Unlock() + + for sb.joinLeaveDone != nil { + joinLeaveDone := sb.joinLeaveDone + sb.Unlock() + + <-joinLeaveDone + + sb.Lock() + } + + sb.joinLeaveDone = make(chan struct{}) +} + +// joinLeaveEnd marks the end of this join/leave operation and +// signals the same without race to other join and leave waiters +func (sb *sandbox) joinLeaveEnd() { + sb.Lock() + defer sb.Unlock() + + if sb.joinLeaveDone != nil { + close(sb.joinLeaveDone) + sb.joinLeaveDone = nil + } +} + +// OptionHostname function returns an option setter for hostname option to +// be passed to NewSandbox method. +func OptionHostname(name string) SandboxOption { + return func(sb *sandbox) { + sb.config.hostName = name + } +} + +// OptionDomainname function returns an option setter for domainname option to +// be passed to NewSandbox method. +func OptionDomainname(name string) SandboxOption { + return func(sb *sandbox) { + sb.config.domainName = name + } +} + +// OptionHostsPath function returns an option setter for hostspath option to +// be passed to NewSandbox method. +func OptionHostsPath(path string) SandboxOption { + return func(sb *sandbox) { + sb.config.hostsPath = path + } +} + +// OptionOriginHostsPath function returns an option setter for origin hosts file path +// to be passed to NewSandbox method. +func OptionOriginHostsPath(path string) SandboxOption { + return func(sb *sandbox) { + sb.config.originHostsPath = path + } +} + +// OptionExtraHost function returns an option setter for extra /etc/hosts options +// which is a name and IP as strings. +func OptionExtraHost(name string, IP string) SandboxOption { + return func(sb *sandbox) { + sb.config.extraHosts = append(sb.config.extraHosts, extraHost{name: name, IP: IP}) + } +} + +// OptionParentUpdate function returns an option setter for parent container +// which needs to update the IP address for the linked container. +func OptionParentUpdate(cid string, name, ip string) SandboxOption { + return func(sb *sandbox) { + sb.config.parentUpdates = append(sb.config.parentUpdates, parentUpdate{cid: cid, name: name, ip: ip}) + } +} + +// OptionResolvConfPath function returns an option setter for resolvconfpath option to +// be passed to net container methods. +func OptionResolvConfPath(path string) SandboxOption { + return func(sb *sandbox) { + sb.config.resolvConfPath = path + } +} + +// OptionOriginResolvConfPath function returns an option setter to set the path to the +// origin resolv.conf file to be passed to net container methods. +func OptionOriginResolvConfPath(path string) SandboxOption { + return func(sb *sandbox) { + sb.config.originResolvConfPath = path + } +} + +// OptionDNS function returns an option setter for dns entry option to +// be passed to container Create method. +func OptionDNS(dns string) SandboxOption { + return func(sb *sandbox) { + sb.config.dnsList = append(sb.config.dnsList, dns) + } +} + +// OptionDNSSearch function returns an option setter for dns search entry option to +// be passed to container Create method. +func OptionDNSSearch(search string) SandboxOption { + return func(sb *sandbox) { + sb.config.dnsSearchList = append(sb.config.dnsSearchList, search) + } +} + +// OptionDNSOptions function returns an option setter for dns options entry option to +// be passed to container Create method. +func OptionDNSOptions(options string) SandboxOption { + return func(sb *sandbox) { + sb.config.dnsOptionsList = append(sb.config.dnsOptionsList, options) + } +} + +// OptionUseDefaultSandbox function returns an option setter for using default sandbox +// (host namespace) to be passed to container Create method. +func OptionUseDefaultSandbox() SandboxOption { + return func(sb *sandbox) { + sb.config.useDefaultSandBox = true + } +} + +// OptionUseExternalKey function returns an option setter for using provided namespace +// instead of creating one. +func OptionUseExternalKey() SandboxOption { + return func(sb *sandbox) { + sb.config.useExternalKey = true + } +} + +// OptionGeneric function returns an option setter for Generic configuration +// that is not managed by libNetwork but can be used by the Drivers during the call to +// net container creation method. Container Labels are a good example. +func OptionGeneric(generic map[string]interface{}) SandboxOption { + return func(sb *sandbox) { + if sb.config.generic == nil { + sb.config.generic = make(map[string]interface{}, len(generic)) + } + for k, v := range generic { + sb.config.generic[k] = v + } + } +} + +// OptionExposedPorts function returns an option setter for the container exposed +// ports option to be passed to container Create method. +func OptionExposedPorts(exposedPorts []types.TransportPort) SandboxOption { + return func(sb *sandbox) { + if sb.config.generic == nil { + sb.config.generic = make(map[string]interface{}) + } + // Defensive copy + eps := make([]types.TransportPort, len(exposedPorts)) + copy(eps, exposedPorts) + // Store endpoint label and in generic because driver needs it + sb.config.exposedPorts = eps + sb.config.generic[netlabel.ExposedPorts] = eps + } +} + +// OptionPortMapping function returns an option setter for the mapping +// ports option to be passed to container Create method. +func OptionPortMapping(portBindings []types.PortBinding) SandboxOption { + return func(sb *sandbox) { + if sb.config.generic == nil { + sb.config.generic = make(map[string]interface{}) + } + // Store a copy of the bindings as generic data to pass to the driver + pbs := make([]types.PortBinding, len(portBindings)) + copy(pbs, portBindings) + sb.config.generic[netlabel.PortMap] = pbs + } +} + +// OptionIngress function returns an option setter for marking a +// sandbox as the controller's ingress sandbox. +func OptionIngress() SandboxOption { + return func(sb *sandbox) { + sb.ingress = true + sb.oslTypes = append(sb.oslTypes, osl.SandboxTypeIngress) + } +} + +// OptionLoadBalancer function returns an option setter for marking a +// sandbox as a load balancer sandbox. +func OptionLoadBalancer(nid string) SandboxOption { + return func(sb *sandbox) { + sb.loadBalancerNID = nid + sb.oslTypes = append(sb.oslTypes, osl.SandboxTypeLoadBalancer) + } +} + +// <=> Returns true if a < b, false if a > b and advances to next level if a == b +// epi.prio <=> epj.prio # 2 < 1 +// epi.gw <=> epj.gw # non-gw < gw +// epi.internal <=> epj.internal # non-internal < internal +// epi.joininfo <=> epj.joininfo # ipv6 < ipv4 +// epi.name <=> epj.name # bar < foo +func (epi *endpoint) Less(epj *endpoint) bool { + var ( + prioi, prioj int + ) + + sbi, _ := epi.getSandbox() + sbj, _ := epj.getSandbox() + + // Prio defaults to 0 + if sbi != nil { + prioi = sbi.epPriority[epi.ID()] + } + if sbj != nil { + prioj = sbj.epPriority[epj.ID()] + } + + if prioi != prioj { + return prioi > prioj + } + + gwi := epi.endpointInGWNetwork() + gwj := epj.endpointInGWNetwork() + if gwi != gwj { + return gwj + } + + inti := epi.getNetwork().Internal() + intj := epj.getNetwork().Internal() + if inti != intj { + return intj + } + + jii := 0 + if epi.joinInfo != nil { + if epi.joinInfo.gw != nil { + jii = jii + 1 + } + if epi.joinInfo.gw6 != nil { + jii = jii + 2 + } + } + + jij := 0 + if epj.joinInfo != nil { + if epj.joinInfo.gw != nil { + jij = jij + 1 + } + if epj.joinInfo.gw6 != nil { + jij = jij + 2 + } + } + + if jii != jij { + return jii > jij + } + + return epi.network.Name() < epj.network.Name() +} + +func (sb *sandbox) NdotsSet() bool { + return sb.ndotsSet +} diff --git a/vendor/github.com/docker/libnetwork/sandbox_dns_unix.go b/libnetwork/sandbox_dns_unix.go similarity index 87% rename from vendor/github.com/docker/libnetwork/sandbox_dns_unix.go rename to libnetwork/sandbox_dns_unix.go index db1b66b190dc8..ae67ab08c83b0 100644 --- a/vendor/github.com/docker/libnetwork/sandbox_dns_unix.go +++ b/libnetwork/sandbox_dns_unix.go @@ -1,20 +1,20 @@ +//go:build !windows // +build !windows package libnetwork import ( "fmt" - "io/ioutil" + "net" "os" "path" "path/filepath" "strconv" "strings" - "github.com/docker/libnetwork/etchosts" - "github.com/docker/libnetwork/resolvconf" - "github.com/docker/libnetwork/resolvconf/dns" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/etchosts" + "github.com/docker/docker/libnetwork/resolvconf" + "github.com/docker/docker/libnetwork/types" "github.com/sirupsen/logrus" ) @@ -98,8 +98,8 @@ func (sb *sandbox) buildHostsFile() error { return etchosts.Build(sb.config.hostsPath, "", sb.config.hostName, sb.config.domainName, extraContent) } -func (sb *sandbox) updateHostsFile(ifaceIP string) error { - if ifaceIP == "" { +func (sb *sandbox) updateHostsFile(ifaceIPs []string) error { + if len(ifaceIPs) == 0 { return nil } @@ -120,7 +120,10 @@ func (sb *sandbox) updateHostsFile(ifaceIP string) error { mhost = fmt.Sprintf("%s %s", fqdn, parts[0]) } - extraContent := []etchosts.Record{{Hosts: mhost, IP: ifaceIP}} + var extraContent []etchosts.Record + for _, ip := range ifaceIPs { + extraContent = append(extraContent, etchosts.Record{Hosts: mhost, IP: ip}) + } sb.addHostsEntries(extraContent) return nil @@ -168,8 +171,8 @@ func (sb *sandbox) setExternalResolvers(content []byte, addrType int, checkLoopb servers := resolvconf.GetNameservers(content, addrType) for _, ip := range servers { hostLoopback := false - if checkLoopback { - hostLoopback = dns.IsIPv4Localhost(ip) + if checkLoopback && isIPv4Loopback(ip) { + hostLoopback = true } sb.extDNS = append(sb.extDNS, extDNSEntry{ IPStr: ip, @@ -178,6 +181,18 @@ func (sb *sandbox) setExternalResolvers(content []byte, addrType int, checkLoopb } } +// isIPv4Loopback checks if the given IP address is an IPv4 loopback address. +// It's based on the logic in Go's net.IP.IsLoopback(), but only the IPv4 part: +// https://github.com/golang/go/blob/go1.16.6/src/net/ip.go#L120-L126 +func isIPv4Loopback(ipAddress string) bool { + if ip := net.ParseIP(ipAddress); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + return ip4[0] == 127 + } + } + return false +} + func (sb *sandbox) setupDNS() error { var newRC *resolvconf.File @@ -213,8 +228,8 @@ func (sb *sandbox) setupDNS() error { originResolvConfPath := sb.config.originResolvConfPath if originResolvConfPath == "" { - // if not specified fallback to default /etc/resolv.conf - originResolvConfPath = resolvconf.DefaultResolvConf + // fallback if not specified + originResolvConfPath = resolvconf.Path() } currRC, err := resolvconf.GetSpecific(originResolvConfPath) if err != nil { @@ -230,7 +245,7 @@ func (sb *sandbox) setupDNS() error { if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 { var ( err error - dnsList = resolvconf.GetNameservers(currRC.Content, types.IP) + dnsList = resolvconf.GetNameservers(currRC.Content, resolvconf.IP) dnsSearchList = resolvconf.GetSearchDomains(currRC.Content) dnsOptionsList = resolvconf.GetOptions(currRC.Content) ) @@ -250,26 +265,26 @@ func (sb *sandbox) setupDNS() error { // After building the resolv.conf from the user config save the // external resolvers in the sandbox. Note that --dns 127.0.0.x // config refers to the loopback in the container namespace - sb.setExternalResolvers(newRC.Content, types.IPv4, false) + sb.setExternalResolvers(newRC.Content, resolvconf.IPv4, false) } else { // If the host resolv.conf file has 127.0.0.x container should // use the host resolver for queries. This is supported by the // docker embedded DNS server. Hence save the external resolvers // before filtering it out. - sb.setExternalResolvers(currRC.Content, types.IPv4, true) + sb.setExternalResolvers(currRC.Content, resolvconf.IPv4, true) // Replace any localhost/127.* (at this point we have no info about ipv6, pass it as true) if newRC, err = resolvconf.FilterResolvDNS(currRC.Content, true); err != nil { return err } // No contention on container resolv.conf file at sandbox creation - if err := ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, filePerm); err != nil { + if err := os.WriteFile(sb.config.resolvConfPath, newRC.Content, filePerm); err != nil { return types.InternalErrorf("failed to write unhaltered resolv.conf file content when setting up dns for sandbox %s: %v", sb.ID(), err) } } // Write hash - if err := ioutil.WriteFile(sb.config.resolvConfHashFile, []byte(newRC.Hash), filePerm); err != nil { + if err := os.WriteFile(sb.config.resolvConfHashFile, []byte(newRC.Hash), filePerm); err != nil { return types.InternalErrorf("failed to write resolv.conf hash file when setting up dns for sandbox %s: %v", sb.ID(), err) } @@ -297,7 +312,7 @@ func (sb *sandbox) updateDNS(ipv6Enabled bool) error { return err } } else { - h, err := ioutil.ReadFile(hashFile) + h, err := os.ReadFile(hashFile) if err != nil { if !os.IsNotExist(err) { return err @@ -319,14 +334,14 @@ func (sb *sandbox) updateDNS(ipv6Enabled bool) error { if err != nil { return err } - err = ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, 0644) + err = os.WriteFile(sb.config.resolvConfPath, newRC.Content, 0644) //nolint:gosec // gosec complains about perms here, which must be 0644 in this case if err != nil { return err } // write the new hash in a temp file and rename it to make the update atomic dir := path.Dir(sb.config.resolvConfPath) - tmpHashFile, err := ioutil.TempFile(dir, "hash") + tmpHashFile, err := os.CreateTemp(dir, "hash") if err != nil { return err } @@ -355,7 +370,7 @@ func (sb *sandbox) rebuildDNS() error { } if len(sb.extDNS) == 0 { - sb.setExternalResolvers(currRC.Content, types.IPv4, false) + sb.setExternalResolvers(currRC.Content, resolvconf.IPv4, false) } var ( dnsList = []string{sb.resolver.NameServer()} @@ -364,7 +379,7 @@ func (sb *sandbox) rebuildDNS() error { ) // external v6 DNS servers has to be listed in resolv.conf - dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, types.IPv6)...) + dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, resolvconf.IPv6)...) // If the user config and embedded DNS server both have ndots option set, // remember the user's config so that unqualified names not in the docker @@ -426,9 +441,9 @@ func createFile(path string) error { } func copyFile(src, dst string) error { - sBytes, err := ioutil.ReadFile(src) + sBytes, err := os.ReadFile(src) if err != nil { return err } - return ioutil.WriteFile(dst, sBytes, filePerm) + return os.WriteFile(dst, sBytes, filePerm) } diff --git a/libnetwork/sandbox_dns_windows.go b/libnetwork/sandbox_dns_windows.go new file mode 100644 index 0000000000000..62af20b6bf039 --- /dev/null +++ b/libnetwork/sandbox_dns_windows.go @@ -0,0 +1,44 @@ +//go:build windows +// +build windows + +package libnetwork + +import ( + "github.com/docker/docker/libnetwork/etchosts" +) + +// Stub implementations for DNS related functions + +func (sb *sandbox) startResolver(bool) { +} + +func (sb *sandbox) setupResolutionFiles() error { + return nil +} + +func (sb *sandbox) restorePath() { +} + +func (sb *sandbox) updateHostsFile(ifaceIP []string) error { + return nil +} + +func (sb *sandbox) addHostsEntries(recs []etchosts.Record) { + +} + +func (sb *sandbox) deleteHostsEntries(recs []etchosts.Record) { + +} + +func (sb *sandbox) updateDNS(ipv6Enabled bool) error { + return nil +} + +func (sb *sandbox) setupDNS() error { + return nil +} + +func (sb *sandbox) rebuildDNS() error { + return nil +} diff --git a/vendor/github.com/docker/libnetwork/sandbox_externalkey.go b/libnetwork/sandbox_externalkey.go similarity index 100% rename from vendor/github.com/docker/libnetwork/sandbox_externalkey.go rename to libnetwork/sandbox_externalkey.go diff --git a/vendor/github.com/docker/libnetwork/sandbox_externalkey_unix.go b/libnetwork/sandbox_externalkey_unix.go similarity index 82% rename from vendor/github.com/docker/libnetwork/sandbox_externalkey_unix.go rename to libnetwork/sandbox_externalkey_unix.go index 7601db9049b8a..963deecb817c0 100644 --- a/vendor/github.com/docker/libnetwork/sandbox_externalkey_unix.go +++ b/libnetwork/sandbox_externalkey_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package libnetwork @@ -7,13 +8,13 @@ import ( "flag" "fmt" "io" - "io/ioutil" "net" "os" "path/filepath" - "github.com/docker/libnetwork/types" - "github.com/opencontainers/runc/libcontainer/configs" + "github.com/docker/docker/libnetwork/types" + "github.com/docker/docker/pkg/stringid" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" ) @@ -24,8 +25,8 @@ const ( ) // processSetKeyReexec is a private function that must be called only on an reexec path -// It expects 3 args { [0] = "libnetwork-setkey", [1] = , [2] = } -// It also expects configs.HookState as a json string in +// It expects 3 args { [0] = "libnetwork-setkey", [1] = , [2] = } +// It also expects specs.State as a json string in // Refer to https://github.com/opencontainers/runc/pull/160/ for more information // The docker exec-root can be specified as "-exec-root" flag. The default value is "/run/docker". func processSetKeyReexec() { @@ -41,35 +42,35 @@ func processSetKeyReexec() { execRoot := flag.String("exec-root", defaultExecRoot, "docker exec root") flag.Parse() - // expecting 3 os.Args {[0]="libnetwork-setkey", [1]=, [2]= } + // expecting 3 os.Args {[0]="libnetwork-setkey", [1]=, [2]= } // (i.e. expecting 2 flag.Args()) args := flag.Args() if len(args) < 2 { err = fmt.Errorf("Re-exec expects 2 args (after parsing flags), received : %d", len(args)) return } - containerID, controllerID := args[0], args[1] + containerID, shortCtlrID := args[0], args[1] - // We expect configs.HookState as a json string in - stateBuf, err := ioutil.ReadAll(os.Stdin) + // We expect specs.State as a json string in + stateBuf, err := io.ReadAll(os.Stdin) if err != nil { return } - var state configs.HookState + var state specs.State if err = json.Unmarshal(stateBuf, &state); err != nil { return } - err = SetExternalKey(controllerID, containerID, fmt.Sprintf("/proc/%d/ns/net", state.Pid), *execRoot) + err = SetExternalKey(shortCtlrID, containerID, fmt.Sprintf("/proc/%d/ns/net", state.Pid), *execRoot) } // SetExternalKey provides a convenient way to set an External key to a sandbox -func SetExternalKey(controllerID string, containerID string, key string, execRoot string) error { +func SetExternalKey(shortCtlrID string, containerID string, key string, execRoot string) error { keyData := setKeyData{ ContainerID: containerID, Key: key} - uds := filepath.Join(execRoot, execSubdir, controllerID+".sock") + uds := filepath.Join(execRoot, execSubdir, shortCtlrID+".sock") c, err := net.Dial("unix", uds) if err != nil { return err @@ -120,7 +121,8 @@ func (c *controller) startExternalKeyListener() error { if err := os.MkdirAll(udsBase, 0600); err != nil { return err } - uds := filepath.Join(udsBase, c.id+".sock") + shortCtlrID := stringid.TruncateID(c.id) + uds := filepath.Join(udsBase, shortCtlrID+".sock") l, err := net.Listen("unix", uds) if err != nil { return err diff --git a/vendor/github.com/docker/libnetwork/sandbox_externalkey_windows.go b/libnetwork/sandbox_externalkey_windows.go similarity index 95% rename from vendor/github.com/docker/libnetwork/sandbox_externalkey_windows.go rename to libnetwork/sandbox_externalkey_windows.go index 340cd1735fba9..c866942abb58a 100644 --- a/vendor/github.com/docker/libnetwork/sandbox_externalkey_windows.go +++ b/libnetwork/sandbox_externalkey_windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package libnetwork @@ -6,7 +7,7 @@ import ( "io" "net" - "github.com/docker/libnetwork/types" + "github.com/docker/docker/libnetwork/types" ) // processSetKeyReexec is a private function that must be called only on an reexec path diff --git a/vendor/github.com/docker/libnetwork/sandbox_store.go b/libnetwork/sandbox_store.go similarity index 98% rename from vendor/github.com/docker/libnetwork/sandbox_store.go rename to libnetwork/sandbox_store.go index 1e53815aeea77..31cce58db71b8 100644 --- a/vendor/github.com/docker/libnetwork/sandbox_store.go +++ b/libnetwork/sandbox_store.go @@ -4,8 +4,8 @@ import ( "encoding/json" "sync" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/osl" "github.com/sirupsen/logrus" ) diff --git a/libnetwork/sandbox_test.go b/libnetwork/sandbox_test.go new file mode 100644 index 0000000000000..0da6766d24abd --- /dev/null +++ b/libnetwork/sandbox_test.go @@ -0,0 +1,265 @@ +package libnetwork + +import ( + "fmt" + "runtime" + "testing" + + "github.com/docker/docker/libnetwork/config" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/docker/libnetwork/osl" + "github.com/docker/docker/libnetwork/testutils" + "gotest.tools/v3/skip" +) + +func getTestEnv(t *testing.T, opts ...[]NetworkOption) (NetworkController, []Network) { + skip.If(t, runtime.GOOS == "windows", "test only works on linux") + + netType := "bridge" + + option := options.Generic{ + "EnableIPForwarding": true, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = option + + cfgOptions, err := OptionBoltdbWithRandomDBFile() + if err != nil { + t.Fatal(err) + } + c, err := New(append(cfgOptions, config.OptionDriverConfig(netType, genericOption))...) + if err != nil { + t.Fatal(err) + } + + if len(opts) == 0 { + return c, nil + } + + nwList := make([]Network, 0, len(opts)) + for i, opt := range opts { + name := fmt.Sprintf("test_nw_%d", i) + netOption := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": name, + }, + } + newOptions := make([]NetworkOption, 1, len(opt)+1) + newOptions[0] = NetworkOptionGeneric(netOption) + newOptions = append(newOptions, opt...) + n, err := c.NewNetwork(netType, name, "", newOptions...) + if err != nil { + t.Fatal(err) + } + + nwList = append(nwList, n) + } + + return c, nwList +} + +func TestSandboxAddEmpty(t *testing.T) { + c, _ := getTestEnv(t) + ctrlr := c.(*controller) + + sbx, err := ctrlr.NewSandbox("sandbox0") + if err != nil { + t.Fatal(err) + } + + if err := sbx.Delete(); err != nil { + t.Fatal(err) + } + + if len(ctrlr.sandboxes) != 0 { + t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes)) + } + + osl.GC() +} + +// // If different priorities are specified, internal option and ipv6 addresses mustn't influence endpoint order +func TestSandboxAddMultiPrio(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + opts := [][]NetworkOption{ + {NetworkOptionEnableIPv6(true), NetworkOptionIpam(ipamapi.DefaultIPAM, "", nil, []*IpamConf{{PreferredPool: "fe90::/64"}}, nil)}, + {NetworkOptionInternalNetwork()}, + {}, + } + + c, nws := getTestEnv(t, opts...) + ctrlr := c.(*controller) + + sbx, err := ctrlr.NewSandbox("sandbox1") + if err != nil { + t.Fatal(err) + } + sid := sbx.ID() + + ep1, err := nws[0].CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + ep2, err := nws[1].CreateEndpoint("ep2") + if err != nil { + t.Fatal(err) + } + ep3, err := nws[2].CreateEndpoint("ep3") + if err != nil { + t.Fatal(err) + } + + if err := ep1.Join(sbx, JoinOptionPriority(1)); err != nil { + t.Fatal(err) + } + + if err := ep2.Join(sbx, JoinOptionPriority(2)); err != nil { + t.Fatal(err) + } + + if err := ep3.Join(sbx, JoinOptionPriority(3)); err != nil { + t.Fatal(err) + } + + if ctrlr.sandboxes[sid].endpoints[0].ID() != ep3.ID() { + t.Fatal("Expected ep3 to be at the top of the heap. But did not find ep3 at the top of the heap") + } + + if len(sbx.Endpoints()) != 3 { + t.Fatal("Expected 3 endpoints to be connected to the sandbox.") + } + + if err := ep3.Leave(sbx); err != nil { + t.Fatal(err) + } + if ctrlr.sandboxes[sid].endpoints[0].ID() != ep2.ID() { + t.Fatal("Expected ep2 to be at the top of the heap after removing ep3. But did not find ep2 at the top of the heap") + } + + if err := ep2.Leave(sbx); err != nil { + t.Fatal(err) + } + if ctrlr.sandboxes[sid].endpoints[0].ID() != ep1.ID() { + t.Fatal("Expected ep1 to be at the top of the heap after removing ep2. But did not find ep1 at the top of the heap") + } + + // Re-add ep3 back + if err := ep3.Join(sbx, JoinOptionPriority(3)); err != nil { + t.Fatal(err) + } + + if ctrlr.sandboxes[sid].endpoints[0].ID() != ep3.ID() { + t.Fatal("Expected ep3 to be at the top of the heap after adding ep3 back. But did not find ep3 at the top of the heap") + } + + if err := sbx.Delete(); err != nil { + t.Fatal(err) + } + + if len(ctrlr.sandboxes) != 0 { + t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes)) + } + + osl.GC() +} + +func TestSandboxAddSamePrio(t *testing.T) { + if !testutils.IsRunningInContainer() { + defer testutils.SetupTestOSContext(t)() + } + + opts := [][]NetworkOption{ + {}, + {}, + {NetworkOptionEnableIPv6(true), NetworkOptionIpam(ipamapi.DefaultIPAM, "", nil, []*IpamConf{{PreferredPool: "fe90::/64"}}, nil)}, + {NetworkOptionInternalNetwork()}, + } + + c, nws := getTestEnv(t, opts...) + + ctrlr := c.(*controller) + + sbx, err := ctrlr.NewSandbox("sandbox1") + if err != nil { + t.Fatal(err) + } + sid := sbx.ID() + + epNw1, err := nws[1].CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + epIPv6, err := nws[2].CreateEndpoint("ep2") + if err != nil { + t.Fatal(err) + } + + epInternal, err := nws[3].CreateEndpoint("ep3") + if err != nil { + t.Fatal(err) + } + + epNw0, err := nws[0].CreateEndpoint("ep4") + if err != nil { + t.Fatal(err) + } + + if err := epNw1.Join(sbx); err != nil { + t.Fatal(err) + } + + if err := epIPv6.Join(sbx); err != nil { + t.Fatal(err) + } + + if err := epInternal.Join(sbx); err != nil { + t.Fatal(err) + } + + if err := epNw0.Join(sbx); err != nil { + t.Fatal(err) + } + + // order should now be: epIPv6, epNw0, epNw1, epInternal + if len(sbx.Endpoints()) != 4 { + t.Fatal("Expected 4 endpoints to be connected to the sandbox.") + } + + // IPv6 has precedence over IPv4 + if ctrlr.sandboxes[sid].endpoints[0].ID() != epIPv6.ID() { + t.Fatal("Expected epIPv6 to be at the top of the heap. But did not find epIPv6 at the top of the heap") + } + + // internal network has lowest precedence + if ctrlr.sandboxes[sid].endpoints[3].ID() != epInternal.ID() { + t.Fatal("Expected epInternal to be at the bottom of the heap. But did not find epInternal at the bottom of the heap") + } + + if err := epIPv6.Leave(sbx); err != nil { + t.Fatal(err) + } + + // 'test_nw_0' has precedence over 'test_nw_1' + if ctrlr.sandboxes[sid].endpoints[0].ID() != epNw0.ID() { + t.Fatal("Expected epNw0 to be at the top of the heap after removing epIPv6. But did not find epNw0 at the top of the heap") + } + + if err := epNw1.Leave(sbx); err != nil { + t.Fatal(err) + } + + if err := sbx.Delete(); err != nil { + t.Fatal(err) + } + + if len(ctrlr.sandboxes) != 0 { + t.Fatalf("controller containers is not empty. len = %d", len(ctrlr.sandboxes)) + } + + osl.GC() +} diff --git a/libnetwork/service.go b/libnetwork/service.go new file mode 100644 index 0000000000000..abe80204220ab --- /dev/null +++ b/libnetwork/service.go @@ -0,0 +1,98 @@ +package libnetwork + +import ( + "fmt" + "net" + "sync" + + "github.com/docker/docker/libnetwork/internal/setmatrix" +) + +var ( + // A global monotonic counter to assign firewall marks to + // services. + fwMarkCtr uint32 = 256 + fwMarkCtrMu sync.Mutex +) + +type portConfigs []*PortConfig + +func (p portConfigs) String() string { + if len(p) == 0 { + return "" + } + + pc := p[0] + str := fmt.Sprintf("%d:%d/%s", pc.PublishedPort, pc.TargetPort, PortConfig_Protocol_name[int32(pc.Protocol)]) + for _, pc := range p[1:] { + str = str + fmt.Sprintf(",%d:%d/%s", pc.PublishedPort, pc.TargetPort, PortConfig_Protocol_name[int32(pc.Protocol)]) + } + + return str +} + +type serviceKey struct { + id string + ports string +} + +type service struct { + name string // Service Name + id string // Service ID + + // Map of loadbalancers for the service one-per attached + // network. It is keyed with network ID. + loadBalancers map[string]*loadBalancer + + // List of ingress ports exposed by the service + ingressPorts portConfigs + + // Service aliases + aliases []string + + // This maps tracks for each IP address the list of endpoints ID + // associated with it. At stable state the endpoint ID expected is 1 + // but during transition and service change it is possible to have + // temporary more than 1 + ipToEndpoint setmatrix.SetMatrix + + deleted bool + + sync.Mutex +} + +// assignIPToEndpoint inserts the mapping between the IP and the endpoint identifier +// returns true if the mapping was not present, false otherwise +// returns also the number of endpoints associated to the IP +func (s *service) assignIPToEndpoint(ip, eID string) (bool, int) { + return s.ipToEndpoint.Insert(ip, eID) +} + +// removeIPToEndpoint removes the mapping between the IP and the endpoint identifier +// returns true if the mapping was deleted, false otherwise +// returns also the number of endpoints associated to the IP +func (s *service) removeIPToEndpoint(ip, eID string) (bool, int) { + return s.ipToEndpoint.Remove(ip, eID) +} + +func (s *service) printIPToEndpoint(ip string) (string, bool) { + return s.ipToEndpoint.String(ip) +} + +type lbBackend struct { + ip net.IP + disabled bool +} + +type loadBalancer struct { + vip net.IP + fwMark uint32 + + // Map of backend IPs backing this loadbalancer on this + // network. It is keyed with endpoint ID. + backEnds map[string]*lbBackend + + // Back pointer to service to which the loadbalancer belongs. + service *service + sync.Mutex +} diff --git a/vendor/github.com/docker/libnetwork/service_common.go b/libnetwork/service_common.go similarity index 90% rename from vendor/github.com/docker/libnetwork/service_common.go rename to libnetwork/service_common.go index 78986c5424f4f..a2973d7e398cd 100644 --- a/vendor/github.com/docker/libnetwork/service_common.go +++ b/libnetwork/service_common.go @@ -1,3 +1,4 @@ +//go:build linux || windows // +build linux windows package libnetwork @@ -5,7 +6,7 @@ package libnetwork import ( "net" - "github.com/docker/libnetwork/internal/setmatrix" + "github.com/docker/docker/libnetwork/internal/setmatrix" "github.com/sirupsen/logrus" ) @@ -20,7 +21,9 @@ func (c *controller) addEndpointNameResolution(svcName, svcID, nID, eID, contain logrus.Debugf("addEndpointNameResolution %s %s add_service:%t sAliases:%v tAliases:%v", eID, svcName, addService, serviceAliases, taskAliases) // Add container resolution mappings - c.addContainerNameResolution(nID, eID, containerName, taskAliases, ip, method) + if err := c.addContainerNameResolution(nID, eID, containerName, taskAliases, ip, method); err != nil { + return err + } serviceID := svcID if serviceID == "" { @@ -64,7 +67,7 @@ func (c *controller) addContainerNameResolution(nID, eID, containerName string, // Add resolution for taskaliases for _, alias := range taskAliases { - n.(*network).addSvcRecords(eID, alias, eID, ip, nil, true, method) + n.(*network).addSvcRecords(eID, alias, eID, ip, nil, false, method) } return nil @@ -79,7 +82,9 @@ func (c *controller) deleteEndpointNameResolution(svcName, svcID, nID, eID, cont logrus.Debugf("deleteEndpointNameResolution %s %s rm_service:%t suppress:%t sAliases:%v tAliases:%v", eID, svcName, rmService, multipleEntries, serviceAliases, taskAliases) // Delete container resolution mappings - c.delContainerNameResolution(nID, eID, containerName, taskAliases, ip, method) + if err := c.delContainerNameResolution(nID, eID, containerName, taskAliases, ip, method); err != nil { + logrus.WithError(err).Warn("Error delting container from resolver") + } serviceID := svcID if serviceID == "" { @@ -230,7 +235,7 @@ func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName s // state in the c.serviceBindings map and it's sub-maps. Also, // always lock network ID before services to avoid deadlock. c.networkLocker.Lock(nID) - defer c.networkLocker.Unlock(nID) + defer c.networkLocker.Unlock(nID) // nolint:errcheck n, err := c.NetworkByID(nID) if err != nil { @@ -300,7 +305,9 @@ func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName s n.(*network).addLBBackend(ip, lb) // Add the appropriate name resolutions - c.addEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, taskAliases, ip, addService, "addServiceBinding") + if err := c.addEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, taskAliases, ip, addService, "addServiceBinding"); err != nil { + return err + } logrus.Debugf("addServiceBinding from %s END for %s %s", method, svcName, eID) @@ -369,9 +376,11 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st // sandboxes in the network only if the vip is valid. if entries == 0 { // The network may well have been deleted before the last - // of the service bindings. That's ok, because removing - // the network sandbox implicitly removes the backend - // service bindings. + // of the service bindings. That's ok on Linux because + // removing the network sandbox implicitly removes the + // backend service bindings. Windows VFP cleanup requires + // calling cleanupServiceBindings on the network prior to + // deleting the network, performed by network.delete. n, err := c.NetworkByID(nID) if err == nil { n.(*network).rmLBBackend(ip, lb, rmService, fullRemove) @@ -380,7 +389,9 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st // Delete the name resolutions if deleteSvcRecords { - c.deleteEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, taskAliases, ip, rmService, entries > 0, "rmServiceBinding") + if err := c.deleteEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, taskAliases, ip, rmService, entries > 0, "rmServiceBinding"); err != nil { + return err + } } if len(s.loadBalancers) == 0 { diff --git a/libnetwork/service_common_test.go b/libnetwork/service_common_test.go new file mode 100644 index 0000000000000..1b3d7cf42efa8 --- /dev/null +++ b/libnetwork/service_common_test.go @@ -0,0 +1,130 @@ +package libnetwork + +import ( + "net" + "runtime" + "testing" + + "github.com/docker/docker/libnetwork/resolvconf" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/skip" +) + +func TestCleanupServiceDiscovery(t *testing.T) { + skip.If(t, runtime.GOOS == "windows", "test only works on linux") + + c, err := New() + assert.NilError(t, err) + defer c.Stop() + + cleanup := func(n Network) { + if err := n.Delete(); err != nil { + t.Error(err) + } + } + n1, err := c.NewNetwork("bridge", "net1", "", nil) + assert.NilError(t, err) + defer cleanup(n1) + + n2, err := c.NewNetwork("bridge", "net2", "", nil) + assert.NilError(t, err) + defer cleanup(n2) + + n1.(*network).addSvcRecords("N1ep1", "service_test", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test") + n1.(*network).addSvcRecords("N2ep2", "service_test", "serviceID2", net.ParseIP("192.168.0.2"), net.IP{}, true, "test") + + n2.(*network).addSvcRecords("N2ep1", "service_test", "serviceID1", net.ParseIP("192.168.1.1"), net.IP{}, true, "test") + n2.(*network).addSvcRecords("N2ep2", "service_test", "serviceID2", net.ParseIP("192.168.1.2"), net.IP{}, true, "test") + + if len(c.(*controller).svcRecords) != 2 { + t.Fatalf("Service record not added correctly:%v", c.(*controller).svcRecords) + } + + // cleanup net1 + c.(*controller).cleanupServiceDiscovery(n1.ID()) + + if len(c.(*controller).svcRecords) != 1 { + t.Fatalf("Service record not cleaned correctly:%v", c.(*controller).svcRecords) + } + + c.(*controller).cleanupServiceDiscovery("") + + if len(c.(*controller).svcRecords) != 0 { + t.Fatalf("Service record not cleaned correctly:%v", c.(*controller).svcRecords) + } +} + +func TestDNSOptions(t *testing.T) { + skip.If(t, runtime.GOOS == "windows", "test only works on linux") + + c, err := New() + assert.NilError(t, err) + + sb, err := c.(*controller).NewSandbox("cnt1", nil) + assert.NilError(t, err) + + cleanup := func(s Sandbox) { + if err := s.Delete(); err != nil { + t.Error(err) + } + } + + defer cleanup(sb) + sb.(*sandbox).startResolver(false) + + err = sb.(*sandbox).setupDNS() + assert.NilError(t, err) + err = sb.(*sandbox).rebuildDNS() + assert.NilError(t, err) + currRC, err := resolvconf.GetSpecific(sb.(*sandbox).config.resolvConfPath) + assert.NilError(t, err) + dnsOptionsList := resolvconf.GetOptions(currRC.Content) + assert.Check(t, is.Len(dnsOptionsList, 1)) + assert.Check(t, is.Equal("ndots:0", dnsOptionsList[0])) + + sb.(*sandbox).config.dnsOptionsList = []string{"ndots:5"} + err = sb.(*sandbox).setupDNS() + assert.NilError(t, err) + currRC, err = resolvconf.GetSpecific(sb.(*sandbox).config.resolvConfPath) + assert.NilError(t, err) + dnsOptionsList = resolvconf.GetOptions(currRC.Content) + assert.Check(t, is.Len(dnsOptionsList, 1)) + assert.Check(t, is.Equal("ndots:5", dnsOptionsList[0])) + + err = sb.(*sandbox).rebuildDNS() + assert.NilError(t, err) + currRC, err = resolvconf.GetSpecific(sb.(*sandbox).config.resolvConfPath) + assert.NilError(t, err) + dnsOptionsList = resolvconf.GetOptions(currRC.Content) + assert.Check(t, is.Len(dnsOptionsList, 1)) + assert.Check(t, is.Equal("ndots:5", dnsOptionsList[0])) + + sb2, err := c.(*controller).NewSandbox("cnt2", nil) + assert.NilError(t, err) + defer cleanup(sb2) + sb2.(*sandbox).startResolver(false) + + sb2.(*sandbox).config.dnsOptionsList = []string{"ndots:0"} + err = sb2.(*sandbox).setupDNS() + assert.NilError(t, err) + err = sb2.(*sandbox).rebuildDNS() + assert.NilError(t, err) + currRC, err = resolvconf.GetSpecific(sb2.(*sandbox).config.resolvConfPath) + assert.NilError(t, err) + dnsOptionsList = resolvconf.GetOptions(currRC.Content) + assert.Check(t, is.Len(dnsOptionsList, 1)) + assert.Check(t, is.Equal("ndots:0", dnsOptionsList[0])) + + sb2.(*sandbox).config.dnsOptionsList = []string{"ndots:foobar"} + err = sb2.(*sandbox).setupDNS() + assert.NilError(t, err) + err = sb2.(*sandbox).rebuildDNS() + assert.Error(t, err, "invalid number for ndots option: foobar") + + sb2.(*sandbox).config.dnsOptionsList = []string{"ndots:-1"} + err = sb2.(*sandbox).setupDNS() + assert.NilError(t, err) + err = sb2.(*sandbox).rebuildDNS() + assert.Error(t, err, "invalid number for ndots option: -1") +} diff --git a/vendor/github.com/docker/libnetwork/service_linux.go b/libnetwork/service_linux.go similarity index 86% rename from vendor/github.com/docker/libnetwork/service_linux.go rename to libnetwork/service_linux.go index 451f760b6117c..2954d04ae990d 100644 --- a/vendor/github.com/docker/libnetwork/service_linux.go +++ b/libnetwork/service_linux.go @@ -3,7 +3,6 @@ package libnetwork import ( "fmt" "io" - "io/ioutil" "net" "os" "os/exec" @@ -14,12 +13,12 @@ import ( "sync" "syscall" + "github.com/docker/docker/libnetwork/iptables" + "github.com/docker/docker/libnetwork/ns" "github.com/docker/docker/pkg/reexec" - "github.com/docker/libnetwork/iptables" - "github.com/docker/libnetwork/ipvs" - "github.com/docker/libnetwork/ns" "github.com/gogo/protobuf/proto" "github.com/ishidawataru/sctp" + "github.com/moby/ipvs" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink/nl" "github.com/vishvananda/netns" @@ -67,11 +66,11 @@ func (n *network) findLBEndpointSandbox() (*endpoint, *sandbox, error) { if !ok { return nil, nil, fmt.Errorf("Unable to get sandbox for %s(%s) in for %s", ep.Name(), ep.ID(), n.ID()) } - ep = sb.getEndpoint(ep.ID()) - if ep == nil { + sep := sb.getEndpoint(ep.ID()) + if sep == nil { return nil, nil, fmt.Errorf("Load balancing endpoint %s(%s) removed from %s", ep.Name(), ep.ID(), n.ID()) } - return ep, sb, nil + return sep, sb, nil } // Searches the OS sandbox for the name of the endpoint interface @@ -302,6 +301,9 @@ func filterPortConfigs(ingressPorts []*PortConfig, isDelete bool) []*PortConfig } func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) error { + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) + addDelOpt := "-I" rollbackAddDelOpt := "-D" if isDelete { @@ -312,19 +314,19 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro ingressMu.Lock() defer ingressMu.Unlock() - chainExists := iptables.ExistChain(ingressChain, iptables.Nat) - filterChainExists := iptables.ExistChain(ingressChain, iptables.Filter) + chainExists := iptable.ExistChain(ingressChain, iptables.Nat) + filterChainExists := iptable.ExistChain(ingressChain, iptables.Filter) ingressOnce.Do(func() { // Flush nat table and filter table ingress chain rules during init if it // exists. It might contain stale rules from previous life. if chainExists { - if err := iptables.RawCombinedOutput("-t", "nat", "-F", ingressChain); err != nil { + if err := iptable.RawCombinedOutput("-t", "nat", "-F", ingressChain); err != nil { logrus.Errorf("Could not flush nat table ingress chain rules during init: %v", err) } } if filterChainExists { - if err := iptables.RawCombinedOutput("-F", ingressChain); err != nil { + if err := iptable.RawCombinedOutput("-F", ingressChain); err != nil { logrus.Errorf("Could not flush filter table ingress chain rules during init: %v", err) } } @@ -332,38 +334,38 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro if !isDelete { if !chainExists { - if err := iptables.RawCombinedOutput("-t", "nat", "-N", ingressChain); err != nil { + if err := iptable.RawCombinedOutput("-t", "nat", "-N", ingressChain); err != nil { return fmt.Errorf("failed to create ingress chain: %v", err) } } if !filterChainExists { - if err := iptables.RawCombinedOutput("-N", ingressChain); err != nil { + if err := iptable.RawCombinedOutput("-N", ingressChain); err != nil { return fmt.Errorf("failed to create filter table ingress chain: %v", err) } } - if !iptables.Exists(iptables.Nat, ingressChain, "-j", "RETURN") { - if err := iptables.RawCombinedOutput("-t", "nat", "-A", ingressChain, "-j", "RETURN"); err != nil { + if !iptable.Exists(iptables.Nat, ingressChain, "-j", "RETURN") { + if err := iptable.RawCombinedOutput("-t", "nat", "-A", ingressChain, "-j", "RETURN"); err != nil { return fmt.Errorf("failed to add return rule in nat table ingress chain: %v", err) } } - if !iptables.Exists(iptables.Filter, ingressChain, "-j", "RETURN") { - if err := iptables.RawCombinedOutput("-A", ingressChain, "-j", "RETURN"); err != nil { + if !iptable.Exists(iptables.Filter, ingressChain, "-j", "RETURN") { + if err := iptable.RawCombinedOutput("-A", ingressChain, "-j", "RETURN"); err != nil { return fmt.Errorf("failed to add return rule to filter table ingress chain: %v", err) } } for _, chain := range []string{"OUTPUT", "PREROUTING"} { - if !iptables.Exists(iptables.Nat, chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain) { - if err := iptables.RawCombinedOutput("-t", "nat", "-I", chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain); err != nil { + if !iptable.Exists(iptables.Nat, chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain) { + if err := iptable.RawCombinedOutput("-t", "nat", "-I", chain, "-m", "addrtype", "--dst-type", "LOCAL", "-j", ingressChain); err != nil { return fmt.Errorf("failed to add jump rule in %s to ingress chain: %v", chain, err) } } } - if !iptables.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) { - if err := iptables.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil { + if !iptable.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) { + if err := iptable.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil { return fmt.Errorf("failed to add jump rule to %s in filter table forward chain: %v", ingressChain, err) } arrangeUserFilterRule() @@ -375,13 +377,13 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro } path := filepath.Join("/proc/sys/net/ipv4/conf", oifName, "route_localnet") - if err := ioutil.WriteFile(path, []byte{'1', '\n'}, 0644); err != nil { + if err := os.WriteFile(path, []byte{'1', '\n'}, 0644); err != nil { //nolint:gosec // gosec complains about perms here, which must be 0644 in this case return fmt.Errorf("could not write to %s: %v", path, err) } ruleArgs := strings.Fields(fmt.Sprintf("-m addrtype --src-type LOCAL -o %s -j MASQUERADE", oifName)) - if !iptables.Exists(iptables.Nat, "POSTROUTING", ruleArgs...) { - if err := iptables.RawCombinedOutput(append([]string{"-t", "nat", "-I", "POSTROUTING"}, ruleArgs...)...); err != nil { + if !iptable.Exists(iptables.Nat, "POSTROUTING", ruleArgs...) { + if err := iptable.RawCombinedOutput(append([]string{"-t", "nat", "-I", "POSTROUTING"}, ruleArgs...)...); err != nil { return fmt.Errorf("failed to add ingress localhost POSTROUTING rule for %s: %v", oifName, err) } } @@ -395,7 +397,7 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro if portErr != nil && !isDelete { filterPortConfigs(filteredPorts, !isDelete) for _, rule := range rollbackRules { - if err := iptables.RawCombinedOutput(rule...); err != nil { + if err := iptable.RawCombinedOutput(rule...); err != nil { logrus.Warnf("roll back rule failed, %v: %v", rule, err) } } @@ -403,10 +405,10 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro }() for _, iPort := range filteredPorts { - if iptables.ExistChain(ingressChain, iptables.Nat) { + if iptable.ExistChain(ingressChain, iptables.Nat) { rule := strings.Fields(fmt.Sprintf("-t nat %s %s -p %s --dport %d -j DNAT --to-destination %s:%d", addDelOpt, ingressChain, strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)]), iPort.PublishedPort, gwIP, iPort.PublishedPort)) - if portErr = iptables.RawCombinedOutput(rule...); portErr != nil { + if portErr = iptable.RawCombinedOutput(rule...); portErr != nil { errStr := fmt.Sprintf("set up rule failed, %v: %v", rule, portErr) if !isDelete { return fmt.Errorf("%s", errStr) @@ -423,7 +425,7 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro // 2) unmanaged containers on bridge networks rule := strings.Fields(fmt.Sprintf("%s %s -m state -p %s --sport %d --state ESTABLISHED,RELATED -j ACCEPT", addDelOpt, ingressChain, strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)]), iPort.PublishedPort)) - if portErr = iptables.RawCombinedOutput(rule...); portErr != nil { + if portErr = iptable.RawCombinedOutput(rule...); portErr != nil { errStr := fmt.Sprintf("set up rule failed, %v: %v", rule, portErr) if !isDelete { return fmt.Errorf("%s", errStr) @@ -436,7 +438,7 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro rule = strings.Fields(fmt.Sprintf("%s %s -p %s --dport %d -j ACCEPT", addDelOpt, ingressChain, strings.ToLower(PortConfig_Protocol_name[int32(iPort.Protocol)]), iPort.PublishedPort)) - if portErr = iptables.RawCombinedOutput(rule...); portErr != nil { + if portErr = iptable.RawCombinedOutput(rule...); portErr != nil { errStr := fmt.Sprintf("set up rule failed, %v: %v", rule, portErr) if !isDelete { return fmt.Errorf("%s", errStr) @@ -461,13 +463,15 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro // This chain has the rules to allow access to the published ports for swarm tasks // from local bridge networks and docker_gwbridge (ie:taks on other swarm networks) func arrangeIngressFilterRule() { - if iptables.ExistChain(ingressChain, iptables.Filter) { - if iptables.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) { - if err := iptables.RawCombinedOutput("-D", "FORWARD", "-j", ingressChain); err != nil { + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) + if iptable.ExistChain(ingressChain, iptables.Filter) { + if iptable.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) { + if err := iptable.RawCombinedOutput("-D", "FORWARD", "-j", ingressChain); err != nil { logrus.Warnf("failed to delete jump rule to ingressChain in filter table: %v", err) } } - if err := iptables.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil { + if err := iptable.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil { logrus.Warnf("failed to add jump rule to ingressChain in filter table: %v", err) } } @@ -533,11 +537,11 @@ func plumbProxy(iPort *PortConfig, isDelete bool) error { } func writePortsToFile(ports []*PortConfig) (string, error) { - f, err := ioutil.TempFile("", "port_configs") + f, err := os.CreateTemp("", "port_configs") if err != nil { return "", err } - defer f.Close() + defer f.Close() //nolint:gosec buf, _ := proto.Marshal(&EndpointRecord{ IngressPorts: ports, @@ -556,7 +560,7 @@ func writePortsToFile(ports []*PortConfig) (string, error) { } func readPortsFromFile(fileName string) ([]*PortConfig, error) { - buf, err := ioutil.ReadFile(fileName) + buf, err := os.ReadFile(fileName) if err != nil { return nil, err } @@ -606,6 +610,8 @@ func invokeFWMarker(path string, vip net.IP, fwMark uint32, ingressPorts []*Port // Firewall marker reexec function. func fwMarker() { + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -660,11 +666,11 @@ func fwMarker() { } ruleParams := strings.Fields(fmt.Sprintf("-m ipvs --ipvs -d %s -j SNAT --to-source %s", subnet, eIP)) - if !iptables.Exists("nat", "POSTROUTING", ruleParams...) { + if !iptable.Exists("nat", "POSTROUTING", ruleParams...) { rule := append(strings.Fields("-t nat -A POSTROUTING"), ruleParams...) rules = append(rules, rule) - err := ioutil.WriteFile("/proc/sys/net/ipv4/vs/conntrack", []byte{'1', '\n'}, 0644) + err := os.WriteFile("/proc/sys/net/ipv4/vs/conntrack", []byte{'1', '\n'}, 0644) if err != nil { logrus.Errorf("Failed to write to /proc/sys/net/ipv4/vs/conntrack: %v", err) os.Exit(7) @@ -676,7 +682,7 @@ func fwMarker() { rules = append(rules, rule) for _, rule := range rules { - if err := iptables.RawCombinedOutputNative(rule...); err != nil { + if err := iptable.RawCombinedOutputNative(rule...); err != nil { logrus.Errorf("set up rule failed, %v: %v", rule, err) os.Exit(8) } @@ -711,6 +717,8 @@ func addRedirectRules(path string, eIP *net.IPNet, ingressPorts []*PortConfig) e // Redirector reexec function. func redirector() { + // TODO IPv6 support + iptable := iptables.GetIptable(iptables.IPv4) runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -763,7 +771,7 @@ func redirector() { } for _, rule := range rules { - if err := iptables.RawCombinedOutputNative(rule...); err != nil { + if err := iptable.RawCombinedOutputNative(rule...); err != nil { logrus.Errorf("set up rule failed, %v: %v", rule, err) os.Exit(6) } @@ -779,15 +787,15 @@ func redirector() { {"-d", eIP.String(), "-p", "udp", "-j", "DROP"}, {"-d", eIP.String(), "-p", "tcp", "-j", "DROP"}, } { - if !iptables.ExistsNative(iptables.Filter, "INPUT", rule...) { - if err := iptables.RawCombinedOutputNative(append([]string{"-A", "INPUT"}, rule...)...); err != nil { + if !iptable.ExistsNative(iptables.Filter, "INPUT", rule...) { + if err := iptable.RawCombinedOutputNative(append([]string{"-A", "INPUT"}, rule...)...); err != nil { logrus.Errorf("set up rule failed, %v: %v", rule, err) os.Exit(7) } } rule[0] = "-s" - if !iptables.ExistsNative(iptables.Filter, "OUTPUT", rule...) { - if err := iptables.RawCombinedOutputNative(append([]string{"-A", "OUTPUT"}, rule...)...); err != nil { + if !iptable.ExistsNative(iptables.Filter, "OUTPUT", rule...) { + if err := iptable.RawCombinedOutputNative(append([]string{"-A", "OUTPUT"}, rule...)...); err != nil { logrus.Errorf("set up rule failed, %v: %v", rule, err) os.Exit(8) } diff --git a/vendor/github.com/docker/libnetwork/service_unsupported.go b/libnetwork/service_unsupported.go similarity index 95% rename from vendor/github.com/docker/libnetwork/service_unsupported.go rename to libnetwork/service_unsupported.go index ee9750600c05b..a2ca3ea769125 100644 --- a/vendor/github.com/docker/libnetwork/service_unsupported.go +++ b/libnetwork/service_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows // +build !linux,!windows package libnetwork diff --git a/vendor/github.com/docker/libnetwork/service_windows.go b/libnetwork/service_windows.go similarity index 94% rename from vendor/github.com/docker/libnetwork/service_windows.go rename to libnetwork/service_windows.go index 944f3d30cae92..d8b86331e7415 100644 --- a/vendor/github.com/docker/libnetwork/service_windows.go +++ b/libnetwork/service_windows.go @@ -4,7 +4,7 @@ import ( "net" "github.com/Microsoft/hcsshim" - "github.com/docker/docker/pkg/system" + "github.com/Microsoft/hcsshim/osversion" "github.com/sirupsen/logrus" ) @@ -13,11 +13,7 @@ type policyLists struct { elb *hcsshim.PolicyList } -var lbPolicylistMap map[*loadBalancer]*policyLists - -func init() { - lbPolicylistMap = make(map[*loadBalancer]*policyLists) -} +var lbPolicylistMap = make(map[*loadBalancer]*policyLists) func (n *network) addLBBackend(ip net.IP, lb *loadBalancer) { if len(lb.vip) == 0 { @@ -27,7 +23,7 @@ func (n *network) addLBBackend(ip net.IP, lb *loadBalancer) { vip := lb.vip ingressPorts := lb.service.ingressPorts - if system.GetOSVersion().Build > 16236 { + if osversion.Build() > 16236 { lb.Lock() defer lb.Unlock() //find the load balancer IP for the network. @@ -128,7 +124,7 @@ func (n *network) rmLBBackend(ip net.IP, lb *loadBalancer, rmService bool, fullR return } - if system.GetOSVersion().Build > 16236 { + if osversion.Build() > 16236 { if numEnabledBackends(lb) > 0 { //Reprogram HNS (actually VFP) with the existing backends. n.addLBBackend(ip, lb) diff --git a/libnetwork/store.go b/libnetwork/store.go new file mode 100644 index 0000000000000..e8d4c078e32cd --- /dev/null +++ b/libnetwork/store.go @@ -0,0 +1,474 @@ +package libnetwork + +import ( + "fmt" + "strings" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/libkv/store/boltdb" + "github.com/docker/libkv/store/consul" + "github.com/docker/libkv/store/etcd" + "github.com/docker/libkv/store/zookeeper" + "github.com/sirupsen/logrus" +) + +func registerKVStores() { + consul.Register() + zookeeper.Register() + etcd.Register() + boltdb.Register() +} + +func (c *controller) initScopedStore(scope string, scfg *datastore.ScopeCfg) error { + store, err := datastore.NewDataStore(scope, scfg) + if err != nil { + return err + } + c.Lock() + c.stores = append(c.stores, store) + c.Unlock() + + return nil +} + +func (c *controller) initStores() error { + registerKVStores() + + c.Lock() + if c.cfg == nil { + c.Unlock() + return nil + } + scopeConfigs := c.cfg.Scopes + c.stores = nil + c.Unlock() + + for scope, scfg := range scopeConfigs { + if err := c.initScopedStore(scope, scfg); err != nil { + return err + } + } + + c.startWatch() + return nil +} + +func (c *controller) closeStores() { + for _, store := range c.getStores() { + store.Close() + } +} + +func (c *controller) getStore(scope string) datastore.DataStore { + c.Lock() + defer c.Unlock() + + for _, store := range c.stores { + if store.Scope() == scope { + return store + } + } + + return nil +} + +func (c *controller) getStores() []datastore.DataStore { + c.Lock() + defer c.Unlock() + + return c.stores +} + +func (c *controller) getNetworkFromStore(nid string) (*network, error) { + for _, n := range c.getNetworksFromStore() { + if n.id == nid { + return n, nil + } + } + return nil, ErrNoSuchNetwork(nid) +} + +func (c *controller) getNetworksForScope(scope string) ([]*network, error) { + var nl []*network + + store := c.getStore(scope) + if store == nil { + return nil, nil + } + + kvol, err := store.List(datastore.Key(datastore.NetworkKeyPrefix), + &network{ctrlr: c}) + if err != nil && err != datastore.ErrKeyNotFound { + return nil, fmt.Errorf("failed to get networks for scope %s: %v", + scope, err) + } + + for _, kvo := range kvol { + n := kvo.(*network) + n.ctrlr = c + + ec := &endpointCnt{n: n} + err = store.GetObject(datastore.Key(ec.Key()...), ec) + if err != nil && !n.inDelete { + logrus.Warnf("Could not find endpoint count key %s for network %s while listing: %v", datastore.Key(ec.Key()...), n.Name(), err) + continue + } + + n.epCnt = ec + if n.scope == "" { + n.scope = scope + } + nl = append(nl, n) + } + + return nl, nil +} + +func (c *controller) getNetworksFromStore() []*network { + var nl []*network + + for _, store := range c.getStores() { + kvol, err := store.List(datastore.Key(datastore.NetworkKeyPrefix), &network{ctrlr: c}) + // Continue searching in the next store if no keys found in this store + if err != nil { + if err != datastore.ErrKeyNotFound { + logrus.Debugf("failed to get networks for scope %s: %v", store.Scope(), err) + } + continue + } + + kvep, err := store.Map(datastore.Key(epCntKeyPrefix), &endpointCnt{}) + if err != nil && err != datastore.ErrKeyNotFound { + logrus.Warnf("failed to get endpoint_count map for scope %s: %v", store.Scope(), err) + } + + for _, kvo := range kvol { + n := kvo.(*network) + n.Lock() + n.ctrlr = c + ec := &endpointCnt{n: n} + // Trim the leading & trailing "/" to make it consistent across all stores + if val, ok := kvep[strings.Trim(datastore.Key(ec.Key()...), "/")]; ok { + ec = val.(*endpointCnt) + ec.n = n + n.epCnt = ec + } + if n.scope == "" { + n.scope = store.Scope() + } + n.Unlock() + nl = append(nl, n) + } + } + + return nl +} + +func (n *network) getEndpointFromStore(eid string) (*endpoint, error) { + var errors []string + for _, store := range n.ctrlr.getStores() { + ep := &endpoint{id: eid, network: n} + err := store.GetObject(datastore.Key(ep.Key()...), ep) + // Continue searching in the next store if the key is not found in this store + if err != nil { + if err != datastore.ErrKeyNotFound { + errors = append(errors, fmt.Sprintf("{%s:%v}, ", store.Scope(), err)) + logrus.Debugf("could not find endpoint %s in %s: %v", eid, store.Scope(), err) + } + continue + } + return ep, nil + } + return nil, fmt.Errorf("could not find endpoint %s: %v", eid, errors) +} + +func (n *network) getEndpointsFromStore() ([]*endpoint, error) { + var epl []*endpoint + + tmp := endpoint{network: n} + for _, store := range n.getController().getStores() { + kvol, err := store.List(datastore.Key(tmp.KeyPrefix()...), &endpoint{network: n}) + // Continue searching in the next store if no keys found in this store + if err != nil { + if err != datastore.ErrKeyNotFound { + logrus.Debugf("failed to get endpoints for network %s scope %s: %v", + n.Name(), store.Scope(), err) + } + continue + } + + for _, kvo := range kvol { + ep := kvo.(*endpoint) + epl = append(epl, ep) + } + } + + return epl, nil +} + +func (c *controller) updateToStore(kvObject datastore.KVObject) error { + cs := c.getStore(kvObject.DataScope()) + if cs == nil { + return ErrDataStoreNotInitialized(kvObject.DataScope()) + } + + if err := cs.PutObjectAtomic(kvObject); err != nil { + if err == datastore.ErrKeyModified { + return err + } + return fmt.Errorf("failed to update store for object type %T: %v", kvObject, err) + } + + return nil +} + +func (c *controller) deleteFromStore(kvObject datastore.KVObject) error { + cs := c.getStore(kvObject.DataScope()) + if cs == nil { + return ErrDataStoreNotInitialized(kvObject.DataScope()) + } + +retry: + if err := cs.DeleteObjectAtomic(kvObject); err != nil { + if err == datastore.ErrKeyModified { + if err := cs.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil { + return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err) + } + logrus.Warnf("Error (%v) deleting object %v, retrying....", err, kvObject.Key()) + goto retry + } + return err + } + + return nil +} + +type netWatch struct { + localEps map[string]*endpoint + remoteEps map[string]*endpoint + stopCh chan struct{} +} + +func (c *controller) getLocalEps(nw *netWatch) []*endpoint { + c.Lock() + defer c.Unlock() + + var epl []*endpoint + for _, ep := range nw.localEps { + epl = append(epl, ep) + } + + return epl +} + +func (c *controller) watchSvcRecord(ep *endpoint) { + c.watchCh <- ep +} + +func (c *controller) unWatchSvcRecord(ep *endpoint) { + c.unWatchCh <- ep +} + +func (c *controller) networkWatchLoop(nw *netWatch, ep *endpoint, ecCh <-chan datastore.KVObject) { + for { + select { + case <-nw.stopCh: + return + case o := <-ecCh: + ec := o.(*endpointCnt) + + epl, err := ec.n.getEndpointsFromStore() + if err != nil { + break + } + + c.Lock() + var addEp []*endpoint + + delEpMap := make(map[string]*endpoint) + renameEpMap := make(map[string]bool) + for k, v := range nw.remoteEps { + delEpMap[k] = v + } + + for _, lEp := range epl { + if _, ok := nw.localEps[lEp.ID()]; ok { + continue + } + + if ep, ok := nw.remoteEps[lEp.ID()]; ok { + // On a container rename EP ID will remain + // the same but the name will change. service + // records should reflect the change. + // Keep old EP entry in the delEpMap and add + // EP from the store (which has the new name) + // into the new list + if lEp.name == ep.name { + delete(delEpMap, lEp.ID()) + continue + } + renameEpMap[lEp.ID()] = true + } + nw.remoteEps[lEp.ID()] = lEp + addEp = append(addEp, lEp) + } + + // EPs whose name are to be deleted from the svc records + // should also be removed from nw's remote EP list, except + // the ones that are getting renamed. + for _, lEp := range delEpMap { + if !renameEpMap[lEp.ID()] { + delete(nw.remoteEps, lEp.ID()) + } + } + c.Unlock() + + for _, lEp := range delEpMap { + ep.getNetwork().updateSvcRecord(lEp, c.getLocalEps(nw), false) + + } + for _, lEp := range addEp { + ep.getNetwork().updateSvcRecord(lEp, c.getLocalEps(nw), true) + } + } + } +} + +func (c *controller) processEndpointCreate(nmap map[string]*netWatch, ep *endpoint) { + n := ep.getNetwork() + if !c.isDistributedControl() && n.Scope() == datastore.SwarmScope && n.driverIsMultihost() { + return + } + + networkID := n.ID() + endpointID := ep.ID() + + c.Lock() + nw, ok := nmap[networkID] + c.Unlock() + + if ok { + // Update the svc db for the local endpoint join right away + n.updateSvcRecord(ep, c.getLocalEps(nw), true) + + c.Lock() + nw.localEps[endpointID] = ep + + // If we had learned that from the kv store remove it + // from remote ep list now that we know that this is + // indeed a local endpoint + delete(nw.remoteEps, endpointID) + c.Unlock() + return + } + + nw = &netWatch{ + localEps: make(map[string]*endpoint), + remoteEps: make(map[string]*endpoint), + } + + // Update the svc db for the local endpoint join right away + // Do this before adding this ep to localEps so that we don't + // try to update this ep's container's svc records + n.updateSvcRecord(ep, c.getLocalEps(nw), true) + + c.Lock() + nw.localEps[endpointID] = ep + nmap[networkID] = nw + nw.stopCh = make(chan struct{}) + c.Unlock() + + store := c.getStore(n.DataScope()) + if store == nil { + return + } + + if !store.Watchable() { + return + } + + ch, err := store.Watch(n.getEpCnt(), nw.stopCh) + if err != nil { + logrus.Warnf("Error creating watch for network: %v", err) + return + } + + go c.networkWatchLoop(nw, ep, ch) +} + +func (c *controller) processEndpointDelete(nmap map[string]*netWatch, ep *endpoint) { + n := ep.getNetwork() + if !c.isDistributedControl() && n.Scope() == datastore.SwarmScope && n.driverIsMultihost() { + return + } + + networkID := n.ID() + endpointID := ep.ID() + + c.Lock() + nw, ok := nmap[networkID] + + if ok { + delete(nw.localEps, endpointID) + c.Unlock() + + // Update the svc db about local endpoint leave right away + // Do this after we remove this ep from localEps so that we + // don't try to remove this svc record from this ep's container. + n.updateSvcRecord(ep, c.getLocalEps(nw), false) + + c.Lock() + if len(nw.localEps) == 0 { + close(nw.stopCh) + + // This is the last container going away for the network. Destroy + // this network's svc db entry + delete(c.svcRecords, networkID) + + delete(nmap, networkID) + } + } + c.Unlock() +} + +func (c *controller) watchLoop() { + for { + select { + case ep := <-c.watchCh: + c.processEndpointCreate(c.nmap, ep) + case ep := <-c.unWatchCh: + c.processEndpointDelete(c.nmap, ep) + } + } +} + +func (c *controller) startWatch() { + if c.watchCh != nil { + return + } + c.watchCh = make(chan *endpoint) + c.unWatchCh = make(chan *endpoint) + c.nmap = make(map[string]*netWatch) + + go c.watchLoop() +} + +func (c *controller) networkCleanup() { + for _, n := range c.getNetworksFromStore() { + if n.inDelete { + logrus.Infof("Removing stale network %s (%s)", n.Name(), n.ID()) + if err := n.delete(true, true); err != nil { + logrus.Debugf("Error while removing stale network: %v", err) + } + } + } +} + +var populateSpecial NetworkWalker = func(nw Network) bool { + if n := nw.(*network); n.hasSpecialDriver() && !n.ConfigOnly() { + if err := n.getController().addNetwork(n); err != nil { + logrus.Warnf("Failed to populate network %q with driver %q", nw.Name(), nw.Type()) + } + } + return false +} diff --git a/libnetwork/store_linux_test.go b/libnetwork/store_linux_test.go new file mode 100644 index 0000000000000..b11a0fbb606f4 --- /dev/null +++ b/libnetwork/store_linux_test.go @@ -0,0 +1,45 @@ +package libnetwork + +import ( + "os" + "testing" + + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/libkv/store" +) + +func TestBoltdbBackend(t *testing.T) { + defer os.Remove(datastore.DefaultScopes("")[datastore.LocalScope].Client.Address) + testLocalBackend(t, "", "", nil) + defer os.Remove("/tmp/boltdb.db") + config := &store.Config{Bucket: "testBackend"} + testLocalBackend(t, "boltdb", "/tmp/boltdb.db", config) + +} + +func TestNoPersist(t *testing.T) { + cfgOptions, err := OptionBoltdbWithRandomDBFile() + if err != nil { + t.Fatalf("Error creating random boltdb file : %v", err) + } + ctrl, err := New(cfgOptions...) + if err != nil { + t.Fatalf("Error new controller: %v", err) + } + nw, err := ctrl.NewNetwork("host", "host", "", NetworkOptionPersist(false)) + if err != nil { + t.Fatalf("Error creating default \"host\" network: %v", err) + } + ep, err := nw.CreateEndpoint("newendpoint", []EndpointOption{}...) + if err != nil { + t.Fatalf("Error creating endpoint: %v", err) + } + store := ctrl.(*controller).getStore(datastore.LocalScope).KVStore() + if exists, _ := store.Exists(datastore.Key(datastore.NetworkKeyPrefix, nw.ID())); exists { + t.Fatalf("Network with persist=false should not be stored in KV Store") + } + if exists, _ := store.Exists(datastore.Key([]string{datastore.EndpointKeyPrefix, nw.ID(), ep.ID()}...)); exists { + t.Fatalf("Endpoint in Network with persist=false should not be stored in KV Store") + } + store.Close() +} diff --git a/libnetwork/store_test.go b/libnetwork/store_test.go new file mode 100644 index 0000000000000..668662ffcb773 --- /dev/null +++ b/libnetwork/store_test.go @@ -0,0 +1,89 @@ +package libnetwork + +import ( + "fmt" + "os" + "testing" + + "github.com/docker/docker/libnetwork/config" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/netlabel" + "github.com/docker/docker/libnetwork/options" + "github.com/docker/libkv/store" +) + +func testLocalBackend(t *testing.T, provider, url string, storeConfig *store.Config) { + cfgOptions := []config.Option{} + cfgOptions = append(cfgOptions, config.OptionLocalKVProvider(provider)) + cfgOptions = append(cfgOptions, config.OptionLocalKVProviderURL(url)) + cfgOptions = append(cfgOptions, config.OptionLocalKVProviderConfig(storeConfig)) + + driverOptions := options.Generic{} + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = driverOptions + cfgOptions = append(cfgOptions, config.OptionDriverConfig("host", genericOption)) + + ctrl, err := New(cfgOptions...) + if err != nil { + t.Fatalf("Error new controller: %v", err) + } + nw, err := ctrl.NewNetwork("host", "host", "") + if err != nil { + t.Fatalf("Error creating default \"host\" network: %v", err) + } + ep, err := nw.CreateEndpoint("newendpoint", []EndpointOption{}...) + if err != nil { + t.Fatalf("Error creating endpoint: %v", err) + } + store := ctrl.(*controller).getStore(datastore.LocalScope).KVStore() + if exists, err := store.Exists(datastore.Key(datastore.NetworkKeyPrefix, nw.ID())); !exists || err != nil { + t.Fatalf("Network key should have been created.") + } + if exists, err := store.Exists(datastore.Key([]string{datastore.EndpointKeyPrefix, nw.ID(), ep.ID()}...)); !exists || err != nil { + t.Fatalf("Endpoint key should have been created.") + } + store.Close() + + // test restore of local store + ctrl, err = New(cfgOptions...) + if err != nil { + t.Fatalf("Error creating controller: %v", err) + } + if _, err = ctrl.NetworkByID(nw.ID()); err != nil { + t.Fatalf("Error getting network %v", err) + } +} + +// OptionBoltdbWithRandomDBFile function returns a random dir for local store backend +func OptionBoltdbWithRandomDBFile() ([]config.Option, error) { + tmp, err := os.CreateTemp("", "libnetwork-") + if err != nil { + return nil, fmt.Errorf("Error creating temp file: %v", err) + } + if err := tmp.Close(); err != nil { + return nil, fmt.Errorf("Error closing temp file: %v", err) + } + cfgOptions := []config.Option{} + cfgOptions = append(cfgOptions, config.OptionLocalKVProvider("boltdb")) + cfgOptions = append(cfgOptions, config.OptionLocalKVProviderURL(tmp.Name())) + sCfg := &store.Config{Bucket: "testBackend"} + cfgOptions = append(cfgOptions, config.OptionLocalKVProviderConfig(sCfg)) + return cfgOptions, nil +} + +func TestMultipleControllersWithSameStore(t *testing.T) { + cfgOptions, err := OptionBoltdbWithRandomDBFile() + if err != nil { + t.Fatalf("Error getting random boltdb configs %v", err) + } + ctrl1, err := New(cfgOptions...) + if err != nil { + t.Fatalf("Error new controller: %v", err) + } + defer ctrl1.Stop() + // Use the same boltdb file without closing the previous controller + _, err = New(cfgOptions...) + if err != nil { + t.Fatalf("Local store must support concurrent controllers") + } +} diff --git a/libnetwork/support/Dockerfile b/libnetwork/support/Dockerfile new file mode 100644 index 0000000000000..e70d529edb6a6 --- /dev/null +++ b/libnetwork/support/Dockerfile @@ -0,0 +1,20 @@ +FROM docker:18-dind + +RUN set -ex \ + && echo "http://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \ + && echo "http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories +RUN apk add --no-cache \ + util-linux \ + bridge-utils \ + iptables \ + iputils \ + iproute2 \ + ipvsadm \ + conntrack-tools \ + jq \ + bash + +WORKDIR /bin +COPY *.sh /bin/ + +CMD /bin/run.sh diff --git a/libnetwork/support/README b/libnetwork/support/README new file mode 100644 index 0000000000000..225df3a8b8fc2 --- /dev/null +++ b/libnetwork/support/README @@ -0,0 +1 @@ +Usage: docker run -v /var/run:/var/run --network host --privileged dockereng/network-diagnostic:support.sh diff --git a/libnetwork/support/run.sh b/libnetwork/support/run.sh new file mode 100755 index 0000000000000..fa460eeb3d37a --- /dev/null +++ b/libnetwork/support/run.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# try to fetch the latest version from github +wget -O support.sh.new https://raw.githubusercontent.com/docker/libnetwork/master/support/support.sh + +if [ "$?" -eq "0" ]; then + mv support.sh.new support.sh + chmod +x support.sh +else + echo "issue fetching the latest support.sh, will use the container version" +fi + +echo "run the support script" +./support.sh diff --git a/libnetwork/support/support.sh b/libnetwork/support/support.sh new file mode 100755 index 0000000000000..fdd1ff6e7dd85 --- /dev/null +++ b/libnetwork/support/support.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash + +while getopts ":s" opt; do + case $opt in + s) + SSD="true" + ;; + esac +done + +SSD="${SSD:-false}" + +# Required tools +DOCKER="${DOCKER:-docker}" +NSENTER="${NSENTER:-nsenter}" +BRIDGE="${BRIDGE:-bridge}" +IPTABLES="${IPTABLES:-iptables}" +IPVSADM="${IPVSADM:-ipvsadm}" +IP="${IP:-ip}" +SSDBIN="${SSDBIN:-ssd}" +JQ="${JQ:-jq}" + +networks=0 +containers=0 +ip_overlap=0 + +NSDIR=/var/run/docker/netns + +function die() { + echo $* + exit 1 +} + +function echo_and_run() { + echo "#" "$@" + eval $(printf '%q ' "$@") < /dev/stdout +} + +function check_ip_overlap() { + inspect=$1 + overlap=$(echo "$inspect_output" | grep "EndpointIP\|VIP" | cut -d':' -f2 | sort | uniq -c | grep -v "1 ") + if [ ! -z "$overlap" ]; then + echo -e "\n\n*** OVERLAP on Network ${networkID} ***" + echo -e "${overlap} \n\n" + ((ip_overlap++)) + else + echo "No overlap" + fi +} + +type -P ${DOCKER} > /dev/null || echo "This tool requires the docker binary" +type -P ${NSENTER} > /dev/null || echo "This tool requires nsenter" +type -P ${BRIDGE} > /dev/null || echo "This tool requires bridge" +type -P ${IPTABLES} > /dev/null || echo "This tool requires iptables" +type -P ${IPVSADM} > /dev/null || echo "This tool requires ipvsadm" +type -P ${IP} > /dev/null || echo "This tool requires ip" +type -P ${JQ} > /dev/null || echo "This tool requires jq" + +if ${DOCKER} network inspect --help | grep -q -- --verbose; then + NETINSPECT_VERBOSE_SUPPORT="--verbose" +else + NETINSPECT_VERBOSE_SUPPORT="" +fi + +echo "Host iptables" +echo_and_run ${IPTABLES} -w1 -n -v -L -t filter | grep -v '^$' +echo_and_run ${IPTABLES} -w1 -n -v -L -t nat | grep -v '^$' +echo_and_run ${IPTABLES} -w1 -n -v -L -t mangle | grep -v '^$' +printf "\n" + +echo "Host links addresses and routes" +echo_and_run ${IP} -o link show +echo_and_run ${IP} -o -4 address show +echo_and_run ${IP} -4 route show +printf "\n" + +echo "Overlay network configuration" +for networkID in $(${DOCKER} network ls --no-trunc --filter driver=overlay -q) "ingress_sbox"; do + echo "nnn Network ${networkID}" + if [ "${networkID}" != "ingress_sbox" ]; then + nspath=($(ls ${NSDIR}/*${networkID:0:9}*)) + inspect_output=$(${DOCKER} network inspect ${NETINSPECT_VERBOSE_SUPPORT} ${networkID}) + echo "$inspect_output" + check_ip_overlap $inspect_output + else + nspath=(${NSDIR}/${networkID}) + fi + + for i in "${nspath[@]}"; do + echo_and_run ${NSENTER} --net=${i} ${IP} -o -4 address show + echo_and_run ${NSENTER} --net=${i} ${IP} -4 route show + echo_and_run ${NSENTER} --net=${i} ${IP} -4 neigh show + bridges=$(${NSENTER} --net=${i} ${IP} -j link show type bridge | ${JQ} -r '.[].ifname') + # break string to array + bridges=(${bridges}) + for b in "${bridges[@]}"; do + if [ -z ${b} ] || [ ${b} == "null" ]; then + continue + fi + echo_and_run ${NSENTER} --net=${i} ${BRIDGE} fdb show br ${b} + done + echo_and_run ${NSENTER} --net=${i} ${IPTABLES} -w1 -n -v -L -t filter | grep -v '^$' + echo_and_run ${NSENTER} --net=${i} ${IPTABLES} -w1 -n -v -L -t nat | grep -v '^$' + echo_and_run ${NSENTER} --net=${i} ${IPTABLES} -w1 -n -v -L -t mangle | grep -v '^$' + echo_and_run ${NSENTER} --net=${i} ${IPVSADM} -l -n + printf "\n" + ((networks++)) + done +done + +echo "Container network configuration" +while read containerID status; do + echo "ccc Container ${containerID} state: ${status}" + ${DOCKER} container inspect ${containerID} --format 'Name:{{json .Name | printf "%s\n"}}Id:{{json .Id | printf "%s\n"}}Hostname:{{json .Config.Hostname | printf "%s\n"}}CreatedAt:{{json .Created | printf "%s\n"}}State:{{json .State|printf "%s\n"}}RestartCount:{{json .RestartCount | printf "%s\n" }}Labels:{{json .Config.Labels | printf "%s\n"}}NetworkSettings:{{json .NetworkSettings}}' | sed '/^State:/ {s/\\"/QUOTE/g; s/,"Output":"[^"]*"//g;}' + if [ ${status} = "Up" ]; then + nspath=$(docker container inspect --format {{.NetworkSettings.SandboxKey}} ${containerID}) + echo_and_run ${NSENTER} --net=${nspath[0]} ${IP} -o -4 address show + echo_and_run ${NSENTER} --net=${nspath[0]} ${IP} -4 route show + echo_and_run ${NSENTER} --net=${nspath[0]} ${IP} -4 neigh show + echo_and_run ${NSENTER} --net=${nspath[0]} ${IPTABLES} -w1 -n -v -L -t nat | grep -v '^$' + echo_and_run ${NSENTER} --net=${nspath[0]} ${IPTABLES} -w1 -n -v -L -t mangle | grep -v '^$' + echo_and_run ${NSENTER} --net=${nspath[0]} ${IPVSADM} -l -n + ((containers++)) + fi + printf "\n" +done < <(${DOCKER} container ls -a --format '{{.ID}} {{.Status}}' | cut -d' ' -f1,2) + +if [ "true" == ${SSD} ]; then + echo "" + echo "#### SSD control-plane and datapath consistency check on a node ####" + for netName in $(docker network ls -f driver=overlay --format "{{.Name}}"); do + echo "## $netName ##" + ${SSDBIN} $netName + echo "" + done +fi + +echo -e "\n\n==SUMMARY==" +echo -e "\t Processed $networks networks" +echo -e "\t IP overlap found: $ip_overlap" +echo -e "\t Processed $containers running containers" diff --git a/libnetwork/test/integration/README.md b/libnetwork/test/integration/README.md new file mode 100644 index 0000000000000..777b1cfa46523 --- /dev/null +++ b/libnetwork/test/integration/README.md @@ -0,0 +1,34 @@ +# LibNetwork Integration Tests + +Integration tests provide end-to-end testing of LibNetwork and Drivers. + +While unit tests verify the code is working as expected by relying on mocks and +artificially created fixtures, integration tests actually use real docker +engines and communicate to it through the CLI. + +Note that integration tests do **not** replace unit tests and Docker is used as a good use-case. + +As a rule of thumb, code should be tested thoroughly with unit tests. +Integration tests on the other hand are meant to test a specific feature end to end. + +Integration tests are written in *bash* using the +[bats](https://github.com/sstephenson/bats) framework. + +## Pre-Requisites + +1. Bats (https://github.com/sstephenson/bats#installing-bats-from-source) +2. Docker Machine (https://github.com/docker/machine) +3. Virtualbox (as a Docker machine driver) + +## Running integration tests + +* Start by [installing] (https://github.com/sstephenson/bats#installing-bats-from-source) *bats* on your system. +* If not done already, [install](https://docs.docker.com/machine/) *docker-machine* into /usr/bin +* Make sure Virtualbox is installed as well, which will be used by docker-machine as a driver to launch VMs + +In order to run all integration tests, pass *bats* the test path: +``` +$ bats test/integration/daemon-configs.bats +``` + + diff --git a/libnetwork/test/integration/daemon-configs.bats b/libnetwork/test/integration/daemon-configs.bats new file mode 100644 index 0000000000000..fd48fbe199e7e --- /dev/null +++ b/libnetwork/test/integration/daemon-configs.bats @@ -0,0 +1,104 @@ +#!/usr/bin/env bats + +load helpers + +export DRIVER=virtualbox +export NAME="bats-$DRIVER-daemon-configs" +export MACHINE_STORAGE_PATH=/tmp/machine-bats-daemon-test-$DRIVER +# Default memsize is 1024MB and disksize is 20000MB +# These values are defined in drivers/virtualbox/virtualbox.go +export DEFAULT_MEMSIZE=1024 +export DEFAULT_DISKSIZE=20000 +export CUSTOM_MEMSIZE=1536 +export CUSTOM_DISKSIZE=10000 +export CUSTOM_CPUCOUNT=1 +export BAD_URL="http://dev.null:9111/bad.iso" + +function setup() { + # add sleep because vbox; ugh + sleep 1 +} + +findDiskSize() { + # SATA-0-0 is usually the boot2disk.iso image + # We assume that SATA 1-0 is root disk VMDK and grab this UUID + # e.g. "SATA-ImageUUID-1-0"="fb5f33a7-e4e3-4cb9-877c-f9415ae2adea" + # TODO(slashk): does this work on Windows ? + run bash -c "VBoxManage showvminfo --machinereadable $NAME | grep SATA-ImageUUID-1-0 | cut -d'=' -f2" + run bash -c "VBoxManage showhdinfo $output | grep "Capacity:" | awk -F' ' '{ print $2 }'" +} + +findMemorySize() { + run bash -c "VBoxManage showvminfo --machinereadable $NAME | grep memory= | cut -d'=' -f2" +} + +findCPUCount() { + run bash -c "VBoxManage showvminfo --machinereadable $NAME | grep cpus= | cut -d'=' -f2" +} + +buildMachineWithOldIsoCheckUpgrade() { + run wget https://github.com/boot2docker/boot2docker/releases/download/v1.4.1/boot2docker.iso -O $MACHINE_STORAGE_PATH/cache/boot2docker.iso + run machine create -d virtualbox $NAME + run machine upgrade $NAME +} + +@test "$DRIVER: machine should not exist" { + run machine active $NAME + [ "$status" -eq 1 ] +} + +@test "$DRIVER: VM should not exist" { + run VBoxManage showvminfo $NAME + [ "$status" -eq 1 ] +} + +@test "$DRIVER: create" { + run machine create -d $DRIVER $NAME + [ "$status" -eq 0 ] +} + +@test "$DRIVER: active" { + run machine active $NAME + [ "$status" -eq 0 ] +} + +@test "$DRIVER: check default machine memory size" { + findMemorySize + [[ ${output} == "${DEFAULT_MEMSIZE}" ]] +} + +@test "$DRIVER: check default machine disksize" { + findDiskSize + [[ ${output} == *"$DEFAULT_DISKSIZE"* ]] +} + +@test "$DRIVER: test bridge-ip" { + run machine ssh $NAME sudo /etc/init.d/docker stop + run machine ssh $NAME sudo ifconfig docker0 down + run machine ssh $NAME sudo ip link delete docker0 + BIP='--bip=172.168.45.1/24' + set_extra_config $BIP + cat ${TMP_EXTRA_ARGS_FILE} | machine ssh $NAME sudo tee /var/lib/boot2docker/profile + cat ${DAEMON_CFG_FILE} | machine ssh $NAME "sudo tee -a /var/lib/boot2docker/profile" + run machine ssh $NAME sudo /etc/init.d/docker start + run machine ssh $NAME ifconfig docker0 + [ "$status" -eq 0 ] + [[ ${lines[1]} =~ "172.168.45.1" ]] +} + +@test "$DRIVER: run busybox container" { + run machine ssh $NAME sudo cat /var/lib/boot2docker/profile + run docker $(machine config $NAME) run busybox echo hello world + [ "$status" -eq 0 ] +} + +@test "$DRIVER: remove machine" { + run machine rm -f $NAME +} + +# Cleanup of machine store should always be the last 'test' +@test "$DRIVER: cleanup" { + run rm -rf $MACHINE_STORAGE_PATH + [ "$status" -eq 0 ] +} + diff --git a/libnetwork/test/integration/daemon.cfg b/libnetwork/test/integration/daemon.cfg new file mode 100644 index 0000000000000..fc93dbd6041be --- /dev/null +++ b/libnetwork/test/integration/daemon.cfg @@ -0,0 +1,4 @@ +CACERT=/var/lib/boot2docker/ca.pem +SERVERCERT=/var/lib/boot2docker/server-key.pem +SERVERKEY=/var/lib/boot2docker/server.pem +DOCKER_TLS=no diff --git a/libnetwork/test/integration/dnet/bridge.bats b/libnetwork/test/integration/dnet/bridge.bats new file mode 100644 index 0000000000000..e0648b526a5d2 --- /dev/null +++ b/libnetwork/test/integration/dnet/bridge.bats @@ -0,0 +1,287 @@ +# -*- mode: sh -*- +#!/usr/bin/env bats + +load helpers + +function test_single_network_connectivity() { + local nw_name start end + + nw_name=${1} + start=1 + end=${2} + + # Create containers and connect them to the network + for i in `seq ${start} ${end}`; + do + dnet_cmd $(inst_id2port 1) container create container_${i} + net_connect 1 container_${i} ${nw_name} + done + + # Now test connectivity between all the containers using service names + for i in `seq ${start} ${end}`; + do + if [ "${nw_name}" != "internal" ]; then + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) \ + "ping -c 1 www.google.com" + fi + for j in `seq ${start} ${end}`; + do + if [ "$i" -eq "$j" ]; then + continue + fi + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) \ + "ping -c 1 container_${j}" + done + done + + if [ -n "$3" ]; then + return + fi + + # Teardown the container connections and the network + for i in `seq ${start} ${end}`; + do + net_disconnect 1 container_${i} ${nw_name} + dnet_cmd $(inst_id2port 1) container rm container_${i} + done +} + +@test "Test default bridge network" { + echo $(docker ps) + test_single_network_connectivity bridge 3 +} + + +@test "Test default network dnet restart" { + echo $(docker ps) + + for iter in `seq 1 2`; + do + test_single_network_connectivity bridge 3 + if [ "$iter" -eq 1 ]; then + docker restart dnet-1-bridge + wait_for_dnet $(inst_id2port 1) dnet-1-bridge + fi + done +} + +@test "Test default network dnet ungraceful restart" { + echo $(docker ps) + + for iter in `seq 1 2`; + do + if [ "$iter" -eq 1 ]; then + test_single_network_connectivity bridge 3 skip + docker restart dnet-1-bridge + wait_for_dnet $(inst_id2port 1) dnet-1-bridge + else + test_single_network_connectivity bridge 3 + fi + done +} + +@test "Test bridge network" { + echo $(docker ps) + dnet_cmd $(inst_id2port 1) network create -d bridge singlehost + test_single_network_connectivity singlehost 3 + dnet_cmd $(inst_id2port 1) network rm singlehost +} + +@test "Test bridge network dnet restart" { + echo $(docker ps) + dnet_cmd $(inst_id2port 1) network create -d bridge singlehost + + for iter in `seq 1 2`; + do + test_single_network_connectivity singlehost 3 + if [ "$iter" -eq 1 ]; then + docker restart dnet-1-bridge + wait_for_dnet $(inst_id2port 1) dnet-1-bridge + fi + done + + dnet_cmd $(inst_id2port 1) network rm singlehost +} + +@test "Test bridge network dnet ungraceful restart" { + echo $(docker ps) + dnet_cmd $(inst_id2port 1) network create -d bridge singlehost + + for iter in `seq 1 2`; + do + if [ "$iter" -eq 1 ]; then + test_single_network_connectivity singlehost 3 skip + docker restart dnet-1-bridge + wait_for_dnet $(inst_id2port 1) dnet-1-bridge + else + test_single_network_connectivity singlehost 3 + fi + done + + dnet_cmd $(inst_id2port 1) network rm singlehost +} + +@test "Test multiple bridge networks" { + echo $(docker ps) + + start=1 + end=3 + + for i in `seq ${start} ${end}`; + do + dnet_cmd $(inst_id2port 1) container create container_${i} + for j in `seq ${start} ${end}`; + do + if [ "$i" -eq "$j" ]; then + continue + fi + + if [ "$i" -lt "$j" ]; then + dnet_cmd $(inst_id2port 1) network create -d bridge sh${i}${j} + nw=sh${i}${j} + else + nw=sh${j}${i} + fi + + osvc="svc${i}${j}" + dnet_cmd $(inst_id2port 1) service publish ${osvc}.${nw} + dnet_cmd $(inst_id2port 1) service attach container_${i} ${osvc}.${nw} + done + done + + for i in `seq ${start} ${end}`; + do + echo ${i1} + for j in `seq ${start} ${end}`; + do + echo ${j1} + if [ "$i" -eq "$j" ]; then + continue + fi + + osvc="svc${j}${i}" + echo "pinging ${osvc}" + dnet_cmd $(inst_id2port 1) service ls + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) "cat /etc/hosts" + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) "ping -c 1 ${osvc}" + done + done + + svcs=( + 0,0 + 2,3 + 1,3 + 1,2 + ) + + echo "Test connectivity failure" + for i in `seq ${start} ${end}`; + do + IFS=, read a b <<<"${svcs[$i]}" + osvc="svc${a}${b}" + echo "pinging ${osvc}" + runc_nofail $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_${i}) "ping -c 1 ${osvc}" + [ "${status}" -ne 0 ] + done + + for i in `seq ${start} ${end}`; + do + for j in `seq ${start} ${end}`; + do + if [ "$i" -eq "$j" ]; then + continue + fi + + if [ "$i" -lt "$j" ]; then + nw=sh${i}${j} + else + nw=sh${j}${i} + fi + + osvc="svc${i}${j}" + dnet_cmd $(inst_id2port 1) service detach container_${i} ${osvc}.${nw} + dnet_cmd $(inst_id2port 1) service unpublish ${osvc}.${nw} + + done + dnet_cmd $(inst_id2port 1) container rm container_${i} + done + + for i in `seq ${start} ${end}`; + do + for j in `seq ${start} ${end}`; + do + if [ "$i" -eq "$j" ]; then + continue + fi + + if [ "$i" -lt "$j" ]; then + dnet_cmd $(inst_id2port 1) network rm sh${i}${j} + fi + done + done + +} + +@test "Test bridge network alias support" { + dnet_cmd $(inst_id2port 1) network create -d bridge br1 + dnet_cmd $(inst_id2port 1) container create container_1 + net_connect 1 container_1 br1 container_2:c2 + dnet_cmd $(inst_id2port 1) container create container_2 + net_connect 1 container_2 br1 + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 container_2" + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 c2" + net_disconnect 1 container_1 br1 + net_disconnect 1 container_2 br1 + dnet_cmd $(inst_id2port 1) container rm container_1 + dnet_cmd $(inst_id2port 1) container rm container_2 + dnet_cmd $(inst_id2port 1) network rm br1 +} + + +@test "Test bridge network global alias support" { + dnet_cmd $(inst_id2port 1) network create -d bridge br1 + dnet_cmd $(inst_id2port 1) network create -d bridge br2 + dnet_cmd $(inst_id2port 1) container create container_1 + net_connect 1 container_1 br1 : c1 + dnet_cmd $(inst_id2port 1) container create container_2 + net_connect 1 container_2 br1 : shared + dnet_cmd $(inst_id2port 1) container create container_3 + net_connect 1 container_3 br1 : shared + + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_2) "ping -c 1 container_1" + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_2) "ping -c 1 c1" + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 container_2" + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 shared" + + net_disconnect 1 container_2 br1 + dnet_cmd $(inst_id2port 1) container rm container_2 + + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 container_3" + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 shared" + + net_disconnect 1 container_1 br1 + dnet_cmd $(inst_id2port 1) container rm container_1 + net_disconnect 1 container_3 br1 + dnet_cmd $(inst_id2port 1) container rm container_3 + + dnet_cmd $(inst_id2port 1) network rm br1 +} + +@test "Test bridge network internal network" { + echo $(docker ps) + dnet_cmd $(inst_id2port 1) network create -d bridge --internal internal + dnet_cmd $(inst_id2port 1) container create container_1 + # connects to internal network, confirm it can't communicate with outside world + net_connect 1 container_1 internal + run runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 8.8.8.8" + [[ "$output" == *"1 packets transmitted, 0 packets received, 100% packet loss"* ]] + net_disconnect 1 container_1 internal + # connects to bridge network, confirm it can communicate with outside world + net_connect 1 container_1 bridge + runc $(dnet_container_name 1 bridge) $(get_sbox_id 1 container_1) "ping -c 1 8.8.8.8" + net_disconnect 1 container_1 bridge + dnet_cmd $(inst_id2port 1) container rm container_1 + # test communications within internal network + test_single_network_connectivity internal 3 + dnet_cmd $(inst_id2port 1) network rm internal +} diff --git a/libnetwork/test/integration/dnet/dnet.bats b/libnetwork/test/integration/dnet/dnet.bats new file mode 100644 index 0000000000000..cf73eab5bb0e5 --- /dev/null +++ b/libnetwork/test/integration/dnet/dnet.bats @@ -0,0 +1,31 @@ +#!/usr/bin/env bats + +load helpers + +@test "Test dnet custom port" { + start_dnet 1 a 4567 + dnet_cmd 4567 network ls + stop_dnet 1 a +} + +@test "Test dnet invalid custom port" { + start_dnet 1 b 4567 + run dnet_cmd 4568 network ls + echo ${output} + [ "$status" -ne 0 ] + stop_dnet 1 b +} + +@test "Test dnet invalid params" { + start_dnet 1 c + run dnet_cmd 8080 network ls + echo ${output} + [ "$status" -ne 0 ] + run ./bin/dnet -H=unix://var/run/dnet.sock network ls + echo ${output} + [ "$status" -ne 0 ] + run ./bin/dnet -H= -l=invalid network ls + echo ${output} + [ "$status" -ne 0 ] + stop_dnet 1 c +} diff --git a/libnetwork/test/integration/dnet/helpers.bash b/libnetwork/test/integration/dnet/helpers.bash new file mode 100644 index 0000000000000..274473ecdf7ec --- /dev/null +++ b/libnetwork/test/integration/dnet/helpers.bash @@ -0,0 +1,535 @@ +function get_docker_bridge_ip() { + echo $(docker run --rm -it busybox ip route show | grep default | cut -d" " -f3) +} + +function inst_id2port() { + echo $((41000+${1}-1)) +} + +function dnet_container_name() { + echo dnet-$1-$2 +} + +function dnet_container_ip() { + docker inspect --format '{{.NetworkSettings.IPAddress}}' dnet-$1-$2 +} + +function get_sbox_id() { + local line + + line=$(dnet_cmd $(inst_id2port ${1}) service ls | grep ${2}) + echo ${line} | cut -d" " -f5 +} + +function net_connect() { + local al gl + if [ -n "$4" ]; then + if [ "${4}" != ":" ]; then + al="--alias=${4}" + fi + fi + if [ -n "$5" ]; then + gl="--alias=${5}" + fi + dnet_cmd $(inst_id2port ${1}) service publish $gl ${2}.${3} + dnet_cmd $(inst_id2port ${1}) service attach $al ${2} ${2}.${3} +} + +function net_disconnect() { + dnet_cmd $(inst_id2port ${1}) service detach ${2} ${2}.${3} + dnet_cmd $(inst_id2port ${1}) service unpublish ${2}.${3} +} + +function start_consul() { + stop_consul + docker run -d \ + --name=pr_consul \ + -p 8500:8500 \ + -p 8300-8302:8300-8302/tcp \ + -p 8300-8302:8300-8302/udp \ + -h consul \ + progrium/consul -server -bootstrap + sleep 2 +} + +function stop_consul() { + echo "consul started" + docker rm -f pr_consul || true +} + +hrun() { + local e E T oldIFS + [[ ! "$-" =~ e ]] || e=1 + [[ ! "$-" =~ E ]] || E=1 + [[ ! "$-" =~ T ]] || T=1 + set +e + set +E + set +T + output="$("$@" 2>&1)" + status="$?" + oldIFS=$IFS + IFS=$'\n' lines=($output) + [ -z "$e" ] || set -e + [ -z "$E" ] || set -E + [ -z "$T" ] || set -T + IFS=$oldIFS +} + +function wait_for_dnet() { + local hport + + hport=$1 + echo "waiting on dnet to come up ..." + for i in `seq 1 10`; + do + hrun ./bin/dnet -H tcp://127.0.0.1:${hport} network ls + echo ${output} + if [ "$status" -eq 0 ]; then + return + fi + + if [[ "${lines[1]}" =~ .*EOF.* ]] + then + docker logs ${2} + fi + echo "still waiting after ${i} seconds" + sleep 1 + done +} + +function parse_discovery_str() { + local d provider address + discovery=$1 + provider=$(echo ${discovery} | cut -d":" -f1) + address=$(echo ${discovery} | cut -d":" -f2):$(echo ${discovery} | cut -d":" -f3) + address=${address:2} + echo "${discovery} ${provider} ${address}" +} + +function start_dnet() { + local inst suffix name hport cport hopt store bridge_ip labels tomlfile nip + local discovery provider address + + inst=$1 + shift + suffix=$1 + shift + + store=$(echo $suffix | cut -d":" -f1) + nip=$(echo $suffix | cut -s -d":" -f2) + + + stop_dnet ${inst} ${store} + name=$(dnet_container_name ${inst} ${store}) + + hport=$((41000+${inst}-1)) + cport=2385 + hopt="" + + while [ -n "$1" ] + do + if [[ "$1" =~ ^[0-9]+$ ]] + then + hport=$1 + cport=$1 + hopt="-H tcp://0.0.0.0:${cport}" + else + store=$1 + fi + shift + done + + bridge_ip=$(get_docker_bridge_ip) + + echo "start_dnet parsed values: " ${inst} ${suffix} ${name} ${hport} ${cport} ${hopt} ${store} + + mkdir -p /tmp/dnet/${name} + tomlfile="/tmp/dnet/${name}/libnetwork.toml" + + # Try discovery URLs with or without path + neigh_ip="" + neighbors="" + if [ "$store" = "zookeeper" ]; then + read discovery provider address < <(parse_discovery_str zk://${bridge_ip}:2182) + elif [ "$store" = "etcd" ]; then + read discovery provider address < <(parse_discovery_str etcd://${bridge_ip}:42000/custom_prefix) + elif [ "$store" = "consul" ]; then + read discovery provider address < <(parse_discovery_str consul://${bridge_ip}:8500/custom_prefix) + else + if [ "$nip" != "" ]; then + neighbors=${nip} + fi + + discovery="" + provider="" + address="" + fi + + if [ "$discovery" != "" ]; then + cat > ${tomlfile} < ${tomlfile} <> ${INTEGRATION_ROOT}/test.log 2>&1 || true + done + + unset cmap +} + +function run_bridge_tests() { + ## Setup + start_dnet 1 bridge 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 1 - bridge]=dnet-1-bridge + + ## Run the test cases + ./integration-tmp/bin/bats ./test/integration/dnet/bridge.bats + + ## Teardown + stop_dnet 1 bridge 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-1-bridge] +} + +function run_overlay_local_tests() { + ## Test overlay network in local scope + ## Setup + start_dnet 1 local 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 1 - local]=dnet-1-local + start_dnet 2 local:$(dnet_container_ip 1 local) 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 2 - local]=dnet-2-local + start_dnet 3 local:$(dnet_container_ip 1 local) 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 3 - local]=dnet-3-local + + ## Run the test cases + ./integration-tmp/bin/bats ./test/integration/dnet/overlay-local.bats + + ## Teardown + stop_dnet 1 local 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-1-local] + stop_dnet 2 local 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-2-local] + stop_dnet 3 local 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-3-local] +} + +function run_overlay_consul_tests() { + ## Test overlay network with consul + ## Setup + start_dnet 1 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 1 - consul]=dnet-1-consul + start_dnet 2 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 2 - consul]=dnet-2-consul + start_dnet 3 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 3 - consul]=dnet-3-consul + + ## Run the test cases + ./integration-tmp/bin/bats ./test/integration/dnet/overlay-consul.bats + + ## Teardown + stop_dnet 1 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-1-consul] + stop_dnet 2 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-2-consul] + stop_dnet 3 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-3-consul] +} + +function run_overlay_consul_host_tests() { + export _OVERLAY_HOST_MODE="true" + ## Setup + start_dnet 1 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 1 - consul]=dnet-1-consul + + ## Run the test cases + ./integration-tmp/bin/bats ./test/integration/dnet/overlay-consul-host.bats + + ## Teardown + stop_dnet 1 consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-1-consul] + unset _OVERLAY_HOST_MODE +} + +function run_overlay_zk_tests() { + ## Test overlay network with zookeeper + start_dnet 1 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 1 - zookeeper]=dnet-1-zookeeper + start_dnet 2 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 2 - zookeeper]=dnet-2-zookeeper + start_dnet 3 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 3 - zookeeper]=dnet-3-zookeeper + + ./integration-tmp/bin/bats ./test/integration/dnet/overlay-zookeeper.bats + + stop_dnet 1 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-1-zookeeper] + stop_dnet 2 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-2-zookeeper] + stop_dnet 3 zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-3-zookeeper] +} + +function run_overlay_etcd_tests() { + ## Test overlay network with etcd + start_dnet 1 etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 1 - etcd]=dnet-1-etcd + start_dnet 2 etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 2 - etcd]=dnet-2-etcd + start_dnet 3 etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 3 - etcd]=dnet-3-etcd + + ./integration-tmp/bin/bats ./test/integration/dnet/overlay-etcd.bats + + stop_dnet 1 etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-1-etcd] + stop_dnet 2 etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-2-etcd] + stop_dnet 3 etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-3-etcd] +} + +function run_dnet_tests() { + # Test dnet configuration options + ./integration-tmp/bin/bats ./test/integration/dnet/dnet.bats +} + +function run_multi_consul_tests() { + # Test multi node configuration with a global scope test driver backed by consul + + ## Setup + start_dnet 1 multi_consul consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 1 - multi_consul]=dnet-1-multi_consul + start_dnet 2 multi_consul consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 2 - multi_consul]=dnet-2-multi_consul + start_dnet 3 multi_consul consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 3 - multi_consul]=dnet-3-multi_consul + + ## Run the test cases + ./integration-tmp/bin/bats ./test/integration/dnet/multi.bats + + ## Teardown + stop_dnet 1 multi_consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-1-multi_consul] + stop_dnet 2 multi_consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-2-multi_consul] + stop_dnet 3 multi_consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-3-multi_consul] +} + +function run_multi_zk_tests() { + # Test multi node configuration with a global scope test driver backed by zookeeper + + ## Setup + start_dnet 1 multi_zk zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 1 - multi_zk]=dnet-1-multi_zk + start_dnet 2 multi_zk zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 2 - multi_zk]=dnet-2-multi_zk + start_dnet 3 multi_zk zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 3 - multi_zk]=dnet-3-multi_zk + + ## Run the test cases + ./integration-tmp/bin/bats ./test/integration/dnet/multi.bats + + ## Teardown + stop_dnet 1 multi_zk 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-1-multi_zk] + stop_dnet 2 multi_zk 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-2-multi_zk] + stop_dnet 3 multi_zk 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-3-multi_zk] +} + +function run_multi_etcd_tests() { + # Test multi node configuration with a global scope test driver backed by etcd + + ## Setup + start_dnet 1 multi_etcd etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 1 - multi_etcd]=dnet-1-multi_etcd + start_dnet 2 multi_etcd etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 2 - multi_etcd]=dnet-2-multi_etcd + start_dnet 3 multi_etcd etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dnet - 3 - multi_etcd]=dnet-3-multi_etcd + + ## Run the test cases + ./integration-tmp/bin/bats ./test/integration/dnet/multi.bats + + ## Teardown + stop_dnet 1 multi_etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-1-multi_etcd] + stop_dnet 2 multi_etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-2-multi_etcd] + stop_dnet 3 multi_etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + unset cmap[dnet-3-multi_etcd] +} + +source ./test/integration/dnet/helpers.bash + +if [ ! -d ${INTEGRATION_ROOT} ]; then + mkdir -p ${INTEGRATION_ROOT} + git clone https://github.com/sstephenson/bats.git ${INTEGRATION_ROOT}/bats + ./integration-tmp/bats/install.sh ./integration-tmp +fi + +if [ ! -d ${TMPC_ROOT} ]; then + mkdir -p ${TMPC_ROOT} + docker pull busybox:ubuntu + docker export $(docker create busybox:ubuntu) > ${TMPC_ROOT}/busybox.tar + mkdir -p ${TMPC_ROOT}/rootfs + tar -C ${TMPC_ROOT}/rootfs -xf ${TMPC_ROOT}/busybox.tar +fi + +# Suite setup + +if [ -z "$SUITES" ]; then + suites="dnet multi_consul multi_zk multi_etcd bridge overlay_consul overlay_consul_host overlay_zk overlay_etcd" +else + suites="$SUITES" +fi + +if [[ ("$suites" =~ .*consul.*) || ("$suites" =~ .*bridge.*) ]]; then + echo "Starting consul ..." + start_consul 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[pr_consul]=pr_consul +fi + +if [[ "$suites" =~ .*zk.* ]]; then + echo "Starting zookeeper ..." + start_zookeeper 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[zookeeper_server]=zookeeper_server +fi + +if [[ "$suites" =~ .*etcd.* ]]; then + echo "Starting etcd ..." + start_etcd 1>> ${INTEGRATION_ROOT}/test.log 2>&1 + cmap[dn_etcd]=dn_etcd +fi + +echo "" + +for suite in ${suites}; do + suite_func=run_${suite}_tests + echo "Running ${suite}_tests ..." + declare -F $suite_func > /dev/null && $suite_func + echo "" +done diff --git a/libnetwork/test/integration/dnet/simple.bats b/libnetwork/test/integration/dnet/simple.bats new file mode 100644 index 0000000000000..96365eaae3b2d --- /dev/null +++ b/libnetwork/test/integration/dnet/simple.bats @@ -0,0 +1,80 @@ +#!/usr/bin/env bats + +load helpers + +@test "Test network create" { + echo $(docker ps) + run dnet_cmd $(inst_id2port 1) network create -d test mh1 + echo ${output} + [ "$status" -eq 0 ] + run dnet_cmd $(inst_id2port 1) network ls + echo ${output} + line=$(dnet_cmd $(inst_id2port 1) network ls | grep mh1) + echo ${line} + name=$(echo ${line} | cut -d" " -f2) + driver=$(echo ${line} | cut -d" " -f3) + echo ${name} ${driver} + [ "$name" = "mh1" ] + [ "$driver" = "test" ] + dnet_cmd $(inst_id2port 1) network rm mh1 +} + +@test "Test network delete with id" { + echo $(docker ps) + run dnet_cmd $(inst_id2port 1) network create -d test mh1 + [ "$status" -eq 0 ] + echo ${output} + dnet_cmd $(inst_id2port 1) network rm ${output} +} + +@test "Test service create" { + echo $(docker ps) + dnet_cmd $(inst_id2port 1) network create -d test multihost + run dnet_cmd $(inst_id2port 1) service publish svc1.multihost + echo ${output} + [ "$status" -eq 0 ] + run dnet_cmd $(inst_id2port 1) service ls + echo ${output} + echo ${lines[1]} + [ "$status" -eq 0 ] + svc=$(echo ${lines[1]} | cut -d" " -f2) + network=$(echo ${lines[1]} | cut -d" " -f3) + echo ${svc} ${network} + [ "$network" = "multihost" ] + [ "$svc" = "svc1" ] + dnet_cmd $(inst_id2port 1) service unpublish svc1.multihost + dnet_cmd $(inst_id2port 1) network rm multihost +} + +@test "Test service delete with id" { + echo $(docker ps) + dnet_cmd $(inst_id2port 1) network create -d test multihost + run dnet_cmd $(inst_id2port 1) service publish svc1.multihost + [ "$status" -eq 0 ] + echo ${output} + run dnet_cmd $(inst_id2port 1) service ls + [ "$status" -eq 0 ] + echo ${output} + echo ${lines[1]} + id=$(echo ${lines[1]} | cut -d" " -f1) + dnet_cmd $(inst_id2port 1) service unpublish ${id}.multihost + dnet_cmd $(inst_id2port 1) network rm multihost +} + +@test "Test service attach" { + echo $(docker ps) + dnet_cmd $(inst_id2port 1) network create -d test multihost + dnet_cmd $(inst_id2port 1) service publish svc1.multihost + dnet_cmd $(inst_id2port 1) container create container_1 + dnet_cmd $(inst_id2port 1) service attach container_1 svc1.multihost + run dnet_cmd $(inst_id2port 1) service ls + [ "$status" -eq 0 ] + echo ${output} + echo ${lines[1]} + container=$(echo ${lines[1]} | cut -d" " -f4) + [ "$container" = "container_1" ] + dnet_cmd $(inst_id2port 1) service detach container_1 svc1.multihost + dnet_cmd $(inst_id2port 1) container rm container_1 + dnet_cmd $(inst_id2port 1) service unpublish svc1.multihost + dnet_cmd $(inst_id2port 1) network rm multihost +} diff --git a/libnetwork/test/integration/helpers.bash b/libnetwork/test/integration/helpers.bash new file mode 100644 index 0000000000000..8ca3a3c635300 --- /dev/null +++ b/libnetwork/test/integration/helpers.bash @@ -0,0 +1,49 @@ +#!/bin/bash + +# Root directory of the repository. +MACHINE_ROOT=/usr/bin + +PLATFORM=$(uname -s | tr '[:upper:]' '[:lower:]') +ARCH=$(uname -m) + +if [ "$ARCH" = "x86_64" ]; then + ARCH="amd64" +else + ARCH="386" +fi +MACHINE_BIN_NAME=docker-machine_$PLATFORM-$ARCH +BATS_LOG=/tmp/bats.log + +touch ${BATS_LOG} +rm ${BATS_LOG} + +teardown() { + echo "$BATS_TEST_NAME +---------- +$output +---------- + +" >> ${BATS_LOG} +} + +EXTRA_ARGS_CFG='EXTRA_ARGS' +EXTRA_ARGS='--tlsverify --tlscacert=/var/lib/boot2docker/ca.pem --tlskey=/var/lib/boot2docker/server-key.pem --tlscert=/var/lib/boot2docker/server.pem --label=provider=virtualbox -H tcp://0.0.0.0:2376' +TMP_EXTRA_ARGS_FILE=/tmp/tmp_extra_args +DAEMON_CFG_FILE=${BATS_TEST_DIRNAME}/daemon.cfg +set_extra_config() { + if [ -f ${TMP_EXTRA_ARGS_FILE} ]; then + rm ${TMP_EXTRA_ARGS_FILE} + fi + echo -n "${EXTRA_ARGS_CFG}='" > ${TMP_EXTRA_ARGS_FILE} + echo -n "$1 " >> ${TMP_EXTRA_ARGS_FILE} + echo "${EXTRA_ARGS}'" >> ${TMP_EXTRA_ARGS_FILE} +} + +if [ ! -e $MACHINE_ROOT/$MACHINE_BIN_NAME ]; then + echo "${MACHINE_ROOT}/${MACHINE_BIN_NAME} not found" + exit 1 +fi + +function machine() { + ${MACHINE_ROOT}/$MACHINE_BIN_NAME "$@" +} diff --git a/libnetwork/testutils/context_unix.go b/libnetwork/testutils/context_unix.go new file mode 100644 index 0000000000000..601c00f91dcd4 --- /dev/null +++ b/libnetwork/testutils/context_unix.go @@ -0,0 +1,44 @@ +//go:build linux || freebsd +// +build linux freebsd + +package testutils + +import ( + "runtime" + "syscall" + "testing" + + "github.com/docker/docker/libnetwork/ns" +) + +// SetupTestOSContext joins a new network namespace, and returns its associated +// teardown function. +// +// Example usage: +// +// defer SetupTestOSContext(t)() +// +func SetupTestOSContext(t *testing.T) func() { + runtime.LockOSThread() + if err := syscall.Unshare(syscall.CLONE_NEWNET); err != nil { + t.Fatalf("Failed to enter netns: %v", err) + } + + fd, err := syscall.Open("/proc/self/ns/net", syscall.O_RDONLY, 0) + if err != nil { + t.Fatal("Failed to open netns file") + } + + // Since we are switching to a new test namespace make + // sure to re-initialize initNs context + ns.Init() + + runtime.LockOSThread() + + return func() { + if err := syscall.Close(fd); err != nil { + t.Logf("Warning: netns closing failed (%v)", err) + } + runtime.UnlockOSThread() + } +} diff --git a/libnetwork/testutils/context_windows.go b/libnetwork/testutils/context_windows.go new file mode 100644 index 0000000000000..a770596f3ec4e --- /dev/null +++ b/libnetwork/testutils/context_windows.go @@ -0,0 +1,14 @@ +package testutils + +import "testing" + +// SetupTestOSContext joins a new network namespace, and returns its associated +// teardown function. +// +// Example usage: +// +// defer SetupTestOSContext(t)() +// +func SetupTestOSContext(t *testing.T) func() { + return func() {} +} diff --git a/libnetwork/testutils/net.go b/libnetwork/testutils/net.go new file mode 100644 index 0000000000000..65572ce2eb6da --- /dev/null +++ b/libnetwork/testutils/net.go @@ -0,0 +1,11 @@ +package testutils + +import ( + "os" +) + +// IsRunningInContainer returns whether the test is running inside a container. +func IsRunningInContainer() bool { + _, err := os.Stat("/.dockerenv") + return err == nil +} diff --git a/libnetwork/types/types.go b/libnetwork/types/types.go new file mode 100644 index 0000000000000..e4ade05902f74 --- /dev/null +++ b/libnetwork/types/types.go @@ -0,0 +1,576 @@ +// Package types contains types that are common across libnetwork project +package types + +import ( + "bytes" + "fmt" + "net" + "strings" + + "github.com/ishidawataru/sctp" +) + +// constants for the IP address type +// Deprecated: use the consts defined in github.com/docker/docker/libnetwork/resolvconf +const ( + IP = iota // IPv4 and IPv6 + IPv4 + IPv6 +) + +// EncryptionKey is the libnetwork representation of the key distributed by the lead +// manager. +type EncryptionKey struct { + Subsystem string + Algorithm int32 + Key []byte + LamportTime uint64 +} + +// UUID represents a globally unique ID of various resources like network and endpoint +type UUID string + +// QosPolicy represents a quality of service policy on an endpoint +type QosPolicy struct { + MaxEgressBandwidth uint64 +} + +// TransportPort represents a local Layer 4 endpoint +type TransportPort struct { + Proto Protocol + Port uint16 +} + +// Equal checks if this instance of Transportport is equal to the passed one +func (t *TransportPort) Equal(o *TransportPort) bool { + if t == o { + return true + } + + if o == nil { + return false + } + + if t.Proto != o.Proto || t.Port != o.Port { + return false + } + + return true +} + +// GetCopy returns a copy of this TransportPort structure instance +func (t *TransportPort) GetCopy() TransportPort { + return TransportPort{Proto: t.Proto, Port: t.Port} +} + +// String returns the TransportPort structure in string form +func (t *TransportPort) String() string { + return fmt.Sprintf("%s/%d", t.Proto.String(), t.Port) +} + +// PortBinding represents a port binding between the container and the host +type PortBinding struct { + Proto Protocol + IP net.IP + Port uint16 + HostIP net.IP + HostPort uint16 + HostPortEnd uint16 +} + +// HostAddr returns the host side transport address +func (p PortBinding) HostAddr() (net.Addr, error) { + switch p.Proto { + case UDP: + return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil + case TCP: + return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil + case SCTP: + return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.HostIP}}, Port: int(p.HostPort)}, nil + default: + return nil, ErrInvalidProtocolBinding(p.Proto.String()) + } +} + +// ContainerAddr returns the container side transport address +func (p PortBinding) ContainerAddr() (net.Addr, error) { + switch p.Proto { + case UDP: + return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil + case TCP: + return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil + case SCTP: + return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.IP}}, Port: int(p.Port)}, nil + default: + return nil, ErrInvalidProtocolBinding(p.Proto.String()) + } +} + +// GetCopy returns a copy of this PortBinding structure instance +func (p *PortBinding) GetCopy() PortBinding { + return PortBinding{ + Proto: p.Proto, + IP: GetIPCopy(p.IP), + Port: p.Port, + HostIP: GetIPCopy(p.HostIP), + HostPort: p.HostPort, + HostPortEnd: p.HostPortEnd, + } +} + +// String returns the PortBinding structure in string form +func (p *PortBinding) String() string { + ret := fmt.Sprintf("%s/", p.Proto) + if p.IP != nil { + ret += p.IP.String() + } + ret = fmt.Sprintf("%s:%d/", ret, p.Port) + if p.HostIP != nil { + ret += p.HostIP.String() + } + ret = fmt.Sprintf("%s:%d", ret, p.HostPort) + return ret +} + +// Equal checks if this instance of PortBinding is equal to the passed one +func (p *PortBinding) Equal(o *PortBinding) bool { + if p == o { + return true + } + + if o == nil { + return false + } + + if p.Proto != o.Proto || p.Port != o.Port || + p.HostPort != o.HostPort || p.HostPortEnd != o.HostPortEnd { + return false + } + + if p.IP != nil { + if !p.IP.Equal(o.IP) { + return false + } + } else { + if o.IP != nil { + return false + } + } + + if p.HostIP != nil { + if !p.HostIP.Equal(o.HostIP) { + return false + } + } else { + if o.HostIP != nil { + return false + } + } + + return true +} + +// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid. +type ErrInvalidProtocolBinding string + +func (ipb ErrInvalidProtocolBinding) Error() string { + return fmt.Sprintf("invalid transport protocol: %s", string(ipb)) +} + +const ( + // ICMP is for the ICMP ip protocol + ICMP = 1 + // TCP is for the TCP ip protocol + TCP = 6 + // UDP is for the UDP ip protocol + UDP = 17 + // SCTP is for the SCTP ip protocol + SCTP = 132 +) + +// Protocol represents an IP protocol number +type Protocol uint8 + +func (p Protocol) String() string { + switch p { + case ICMP: + return "icmp" + case TCP: + return "tcp" + case UDP: + return "udp" + case SCTP: + return "sctp" + default: + return fmt.Sprintf("%d", p) + } +} + +// ParseProtocol returns the respective Protocol type for the passed string +func ParseProtocol(s string) Protocol { + switch strings.ToLower(s) { + case "icmp": + return ICMP + case "udp": + return UDP + case "tcp": + return TCP + case "sctp": + return SCTP + default: + return 0 + } +} + +// GetMacCopy returns a copy of the passed MAC address +func GetMacCopy(from net.HardwareAddr) net.HardwareAddr { + if from == nil { + return nil + } + to := make(net.HardwareAddr, len(from)) + copy(to, from) + return to +} + +// GetIPCopy returns a copy of the passed IP address +func GetIPCopy(from net.IP) net.IP { + if from == nil { + return nil + } + to := make(net.IP, len(from)) + copy(to, from) + return to +} + +// GetIPNetCopy returns a copy of the passed IP Network +func GetIPNetCopy(from *net.IPNet) *net.IPNet { + if from == nil { + return nil + } + bm := make(net.IPMask, len(from.Mask)) + copy(bm, from.Mask) + return &net.IPNet{IP: GetIPCopy(from.IP), Mask: bm} +} + +// GetIPNetCanonical returns the canonical form for the passed network +func GetIPNetCanonical(nw *net.IPNet) *net.IPNet { + if nw == nil { + return nil + } + c := GetIPNetCopy(nw) + c.IP = c.IP.Mask(nw.Mask) + return c +} + +// CompareIPNet returns equal if the two IP Networks are equal +func CompareIPNet(a, b *net.IPNet) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask) +} + +// GetMinimalIP returns the address in its shortest form +// If ip contains an IPv4-mapped IPv6 address, the 4-octet form of the IPv4 address will be returned. +// Otherwise ip is returned unchanged. +func GetMinimalIP(ip net.IP) net.IP { + if ip != nil && ip.To4() != nil { + return ip.To4() + } + return ip +} + +// IsIPNetValid returns true if the ipnet is a valid network/mask +// combination. Otherwise returns false. +func IsIPNetValid(nw *net.IPNet) bool { + return nw.String() != "0.0.0.0/0" +} + +var v4inV6MaskPrefix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + +// compareIPMask checks if the passed ip and mask are semantically compatible. +// It returns the byte indexes for the address and mask so that caller can +// do bitwise operations without modifying address representation. +func compareIPMask(ip net.IP, mask net.IPMask) (is int, ms int, err error) { + // Find the effective starting of address and mask + if len(ip) == net.IPv6len && ip.To4() != nil { + is = 12 + } + if len(ip[is:]) == net.IPv4len && len(mask) == net.IPv6len && bytes.Equal(mask[:12], v4inV6MaskPrefix) { + ms = 12 + } + // Check if address and mask are semantically compatible + if len(ip[is:]) != len(mask[ms:]) { + err = fmt.Errorf("ip and mask are not compatible: (%#v, %#v)", ip, mask) + } + return +} + +// GetHostPartIP returns the host portion of the ip address identified by the mask. +// IP address representation is not modified. If address and mask are not compatible +// an error is returned. +func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) { + // Find the effective starting of address and mask + is, ms, err := compareIPMask(ip, mask) + if err != nil { + return nil, fmt.Errorf("cannot compute host portion ip address because %s", err) + } + + // Compute host portion + out := GetIPCopy(ip) + for i := 0; i < len(mask[ms:]); i++ { + out[is+i] &= ^mask[ms+i] + } + + return out, nil +} + +// GetBroadcastIP returns the broadcast ip address for the passed network (ip and mask). +// IP address representation is not modified. If address and mask are not compatible +// an error is returned. +func GetBroadcastIP(ip net.IP, mask net.IPMask) (net.IP, error) { + // Find the effective starting of address and mask + is, ms, err := compareIPMask(ip, mask) + if err != nil { + return nil, fmt.Errorf("cannot compute broadcast ip address because %s", err) + } + + // Compute broadcast address + out := GetIPCopy(ip) + for i := 0; i < len(mask[ms:]); i++ { + out[is+i] |= ^mask[ms+i] + } + + return out, nil +} + +// ParseCIDR returns the *net.IPNet represented by the passed CIDR notation +func ParseCIDR(cidr string) (n *net.IPNet, e error) { + var i net.IP + if i, n, e = net.ParseCIDR(cidr); e == nil { + n.IP = i + } + return +} + +const ( + // NEXTHOP indicates a StaticRoute with an IP next hop. + NEXTHOP = iota + + // CONNECTED indicates a StaticRoute with an interface for directly connected peers. + CONNECTED +) + +// StaticRoute is a statically-provisioned IP route. +type StaticRoute struct { + Destination *net.IPNet + + RouteType int // NEXT_HOP or CONNECTED + + // NextHop will be resolved by the kernel (i.e. as a loose hop). + NextHop net.IP +} + +// GetCopy returns a copy of this StaticRoute structure +func (r *StaticRoute) GetCopy() *StaticRoute { + d := GetIPNetCopy(r.Destination) + nh := GetIPCopy(r.NextHop) + return &StaticRoute{Destination: d, + RouteType: r.RouteType, + NextHop: nh, + } +} + +// InterfaceStatistics represents the interface's statistics +type InterfaceStatistics struct { + RxBytes uint64 + RxPackets uint64 + RxErrors uint64 + RxDropped uint64 + TxBytes uint64 + TxPackets uint64 + TxErrors uint64 + TxDropped uint64 +} + +func (is *InterfaceStatistics) String() string { + return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d", + is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped) +} + +/****************************** + * Well-known Error Interfaces + ******************************/ + +// MaskableError is an interface for errors which can be ignored by caller +type MaskableError interface { + // Maskable makes implementer into MaskableError type + Maskable() +} + +// RetryError is an interface for errors which might get resolved through retry +type RetryError interface { + // Retry makes implementer into RetryError type + Retry() +} + +// BadRequestError is an interface for errors originated by a bad request +type BadRequestError interface { + // BadRequest makes implementer into BadRequestError type + BadRequest() +} + +// NotFoundError is an interface for errors raised because a needed resource is not available +type NotFoundError interface { + // NotFound makes implementer into NotFoundError type + NotFound() +} + +// ForbiddenError is an interface for errors which denote a valid request that cannot be honored +type ForbiddenError interface { + // Forbidden makes implementer into ForbiddenError type + Forbidden() +} + +// NoServiceError is an interface for errors returned when the required service is not available +type NoServiceError interface { + // NoService makes implementer into NoServiceError type + NoService() +} + +// TimeoutError is an interface for errors raised because of timeout +type TimeoutError interface { + // Timeout makes implementer into TimeoutError type + Timeout() +} + +// NotImplementedError is an interface for errors raised because of requested functionality is not yet implemented +type NotImplementedError interface { + // NotImplemented makes implementer into NotImplementedError type + NotImplemented() +} + +// InternalError is an interface for errors raised because of an internal error +type InternalError interface { + // Internal makes implementer into InternalError type + Internal() +} + +/****************************** + * Well-known Error Formatters + ******************************/ + +// BadRequestErrorf creates an instance of BadRequestError +func BadRequestErrorf(format string, params ...interface{}) error { + return badRequest(fmt.Sprintf(format, params...)) +} + +// NotFoundErrorf creates an instance of NotFoundError +func NotFoundErrorf(format string, params ...interface{}) error { + return notFound(fmt.Sprintf(format, params...)) +} + +// ForbiddenErrorf creates an instance of ForbiddenError +func ForbiddenErrorf(format string, params ...interface{}) error { + return forbidden(fmt.Sprintf(format, params...)) +} + +// NoServiceErrorf creates an instance of NoServiceError +func NoServiceErrorf(format string, params ...interface{}) error { + return noService(fmt.Sprintf(format, params...)) +} + +// NotImplementedErrorf creates an instance of NotImplementedError +func NotImplementedErrorf(format string, params ...interface{}) error { + return notImpl(fmt.Sprintf(format, params...)) +} + +// TimeoutErrorf creates an instance of TimeoutError +func TimeoutErrorf(format string, params ...interface{}) error { + return timeout(fmt.Sprintf(format, params...)) +} + +// InternalErrorf creates an instance of InternalError +func InternalErrorf(format string, params ...interface{}) error { + return internal(fmt.Sprintf(format, params...)) +} + +// InternalMaskableErrorf creates an instance of InternalError and MaskableError +func InternalMaskableErrorf(format string, params ...interface{}) error { + return maskInternal(fmt.Sprintf(format, params...)) +} + +// RetryErrorf creates an instance of RetryError +func RetryErrorf(format string, params ...interface{}) error { + return retry(fmt.Sprintf(format, params...)) +} + +/*********************** + * Internal Error Types + ***********************/ +type badRequest string + +func (br badRequest) Error() string { + return string(br) +} +func (br badRequest) BadRequest() {} + +type notFound string + +func (nf notFound) Error() string { + return string(nf) +} +func (nf notFound) NotFound() {} + +type forbidden string + +func (frb forbidden) Error() string { + return string(frb) +} +func (frb forbidden) Forbidden() {} + +type noService string + +func (ns noService) Error() string { + return string(ns) +} +func (ns noService) NoService() {} + +type timeout string + +func (to timeout) Error() string { + return string(to) +} +func (to timeout) Timeout() {} + +type notImpl string + +func (ni notImpl) Error() string { + return string(ni) +} +func (ni notImpl) NotImplemented() {} + +type internal string + +func (nt internal) Error() string { + return string(nt) +} +func (nt internal) Internal() {} + +type maskInternal string + +func (mnt maskInternal) Error() string { + return string(mnt) +} +func (mnt maskInternal) Internal() {} +func (mnt maskInternal) Maskable() {} + +type retry string + +func (r retry) Error() string { + return string(r) +} +func (r retry) Retry() {} diff --git a/libnetwork/types/types_test.go b/libnetwork/types/types_test.go new file mode 100644 index 0000000000000..b32a0007dde09 --- /dev/null +++ b/libnetwork/types/types_test.go @@ -0,0 +1,339 @@ +package types + +import ( + "net" + "testing" +) + +func TestErrorConstructors(t *testing.T) { + var err error + + err = BadRequestErrorf("Io ho %d uccello", 1) + if err.Error() != "Io ho 1 uccello" { + t.Fatal(err) + } + if _, ok := err.(BadRequestError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = RetryErrorf("Incy wincy %s went up the spout again", "spider") + if err.Error() != "Incy wincy spider went up the spout again" { + t.Fatal(err) + } + if _, ok := err.(RetryError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = NotFoundErrorf("Can't find the %s", "keys") + if err.Error() != "Can't find the keys" { + t.Fatal(err) + } + if _, ok := err.(NotFoundError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = ForbiddenErrorf("Can't open door %d", 2) + if err.Error() != "Can't open door 2" { + t.Fatal(err) + } + if _, ok := err.(ForbiddenError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = NotImplementedErrorf("Functionality %s is not implemented", "x") + if err.Error() != "Functionality x is not implemented" { + t.Fatal(err) + } + if _, ok := err.(NotImplementedError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = TimeoutErrorf("Process %s timed out", "abc") + if err.Error() != "Process abc timed out" { + t.Fatal(err) + } + if _, ok := err.(TimeoutError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = NoServiceErrorf("Driver %s is not available", "mh") + if err.Error() != "Driver mh is not available" { + t.Fatal(err) + } + if _, ok := err.(NoServiceError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = InternalErrorf("Not sure what happened") + if err.Error() != "Not sure what happened" { + t.Fatal(err) + } + if _, ok := err.(InternalError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = InternalMaskableErrorf("Minor issue, it can be ignored") + if err.Error() != "Minor issue, it can be ignored" { + t.Fatal(err) + } + if _, ok := err.(InternalError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); !ok { + t.Fatal(err) + } +} + +func TestCompareIPMask(t *testing.T) { + input := []struct { + ip net.IP + mask net.IPMask + is int + ms int + isErr bool + }{ + { // ip in v4Inv6 representation, mask in v4 representation + ip: net.IPv4(172, 28, 30, 1), + mask: []byte{0xff, 0xff, 0xff, 0}, + is: 12, + ms: 0, + }, + { // ip and mask in v4Inv6 representation + ip: net.IPv4(172, 28, 30, 2), + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}, + is: 12, + ms: 12, + }, + { // ip in v4 representation, mask in v4Inv6 representation + ip: net.IPv4(172, 28, 30, 3)[12:], + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}, + is: 0, + ms: 12, + }, + { // ip and mask in v4 representation + ip: net.IPv4(172, 28, 30, 4)[12:], + mask: []byte{0xff, 0xff, 0xff, 0}, + is: 0, + ms: 0, + }, + { // ip and mask as v6 + ip: net.ParseIP("2001:DB8:2002:2001:FFFF:ABCD:EEAB:00CD"), + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0}, + is: 0, + ms: 0, + }, + { + ip: net.ParseIP("2001:DB8:2002:2001:FFFF:ABCD:EEAB:00CD"), + mask: []byte{0xff, 0xff, 0xff, 0}, + isErr: true, + }, + { + ip: net.ParseIP("173.32.4.5"), + mask: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0}, + isErr: true, + }, + { + ip: net.ParseIP("173.32.4.5"), + mask: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0}, + isErr: true, + }, + } + + for ind, i := range input { + is, ms, err := compareIPMask(i.ip, i.mask) + if i.isErr { + if err == nil { + t.Fatalf("Incorrect error condition for element %d. is: %d, ms: %d, err: %v", ind, is, ms, err) + } + } else { + if i.is != is || i.ms != ms { + t.Fatalf("expected is: %d, ms: %d. Got is: %d, ms: %d for element %d", i.is, i.ms, is, ms, ind) + } + } + } +} + +func TestUtilGetHostPartIP(t *testing.T) { + input := []struct { + ip net.IP + mask net.IPMask + host net.IP + err error + }{ + { // ip in v4Inv6 representation, mask in v4 representation + ip: net.IPv4(172, 28, 30, 1), + mask: []byte{0xff, 0xff, 0xff, 0}, + host: net.IPv4(0, 0, 0, 1), + }, + { // ip and mask in v4Inv6 representation + ip: net.IPv4(172, 28, 30, 2), + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}, + host: net.IPv4(0, 0, 0, 2), + }, + { // ip in v4 representation, mask in v4Inv6 representation + ip: net.IPv4(172, 28, 30, 3)[12:], + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}, + host: net.IPv4(0, 0, 0, 3)[12:], + }, + { // ip and mask in v4 representation + ip: net.IPv4(172, 28, 30, 4)[12:], + mask: []byte{0xff, 0xff, 0xff, 0}, + host: net.IPv4(0, 0, 0, 4)[12:], + }, + { // ip and mask as v6 + ip: net.ParseIP("2001:DB8:2002:2001:FFFF:ABCD:EEAB:00CD"), + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0}, + host: net.ParseIP("0::AB:00CD"), + }, + } + + for _, i := range input { + h, err := GetHostPartIP(i.ip, i.mask) + if err != nil { + t.Fatal(err) + } + if !i.host.Equal(h) { + t.Fatalf("Failed to return expected host ip. Expected: %s. Got: %s", i.host, h) + } + } + + // ip as v6 and mask as v4 are not compatible + if _, err := GetHostPartIP(net.ParseIP("2001:DB8:2002:2001:FFFF:ABCD:EEAB:00CD"), []byte{0xff, 0xff, 0xff, 0}); err == nil { + t.Fatalf("Unexpected success") + } + // ip as v4 and non conventional mask + if _, err := GetHostPartIP(net.ParseIP("173.32.4.5"), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0}); err == nil { + t.Fatalf("Unexpected success") + } + // ip as v4 and non conventional mask + if _, err := GetHostPartIP(net.ParseIP("173.32.4.5"), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0}); err == nil { + t.Fatalf("Unexpected success") + } +} + +func TestUtilGetBroadcastIP(t *testing.T) { + input := []struct { + ip net.IP + mask net.IPMask + bcast net.IP + err error + }{ + // ip in v4Inv6 representation, mask in v4 representation + { + ip: net.IPv4(172, 28, 30, 1), + mask: []byte{0xff, 0xff, 0xff, 0}, + bcast: net.IPv4(172, 28, 30, 255), + }, + { + ip: net.IPv4(10, 28, 30, 1), + mask: []byte{0xff, 0, 0, 0}, + bcast: net.IPv4(10, 255, 255, 255), + }, + // ip and mask in v4Inv6 representation + { + ip: net.IPv4(172, 28, 30, 2), + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}, + bcast: net.IPv4(172, 28, 30, 255), + }, + { + ip: net.IPv4(172, 28, 30, 2), + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0}, + bcast: net.IPv4(172, 28, 255, 255), + }, + // ip in v4 representation, mask in v4Inv6 representation + { + ip: net.IPv4(172, 28, 30, 3)[12:], + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}, + bcast: net.IPv4(172, 28, 30, 255)[12:], + }, + { + ip: net.IPv4(172, 28, 30, 3)[12:], + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0}, + bcast: net.IPv4(172, 255, 255, 255)[12:], + }, + // ip and mask in v4 representation + { + ip: net.IPv4(172, 28, 30, 4)[12:], + mask: []byte{0xff, 0xff, 0xff, 0}, + bcast: net.IPv4(172, 28, 30, 255)[12:], + }, + { + ip: net.IPv4(172, 28, 30, 4)[12:], + mask: []byte{0xff, 0xff, 0, 0}, + bcast: net.IPv4(172, 28, 255, 255)[12:], + }, + { // ip and mask as v6 + ip: net.ParseIP("2001:DB8:2002:2001:FFFF:ABCD:EEAB:00CD"), + mask: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0}, + bcast: net.ParseIP("2001:DB8:2002:2001:FFFF:ABCD:EEFF:FFFF"), + }, + } + + for _, i := range input { + h, err := GetBroadcastIP(i.ip, i.mask) + if err != nil { + t.Fatal(err) + } + if !i.bcast.Equal(h) { + t.Fatalf("Failed to return expected host ip. Expected: %s. Got: %s", i.bcast, h) + } + } + + // ip as v6 and mask as v4 are not compatible + if _, err := GetBroadcastIP(net.ParseIP("2001:DB8:2002:2001:FFFF:ABCD:EEAB:00CD"), []byte{0xff, 0xff, 0xff, 0}); err == nil { + t.Fatalf("Unexpected success") + } + // ip as v4 and non conventional mask + if _, err := GetBroadcastIP(net.ParseIP("173.32.4.5"), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0}); err == nil { + t.Fatalf("Unexpected success") + } + // ip as v4 and non conventional mask + if _, err := GetBroadcastIP(net.ParseIP("173.32.4.5"), []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0}); err == nil { + t.Fatalf("Unexpected success") + } +} + +func TestParseCIDR(t *testing.T) { + input := []struct { + cidr string + ipnw *net.IPNet + }{ + {"192.168.22.44/16", &net.IPNet{IP: net.IP{192, 168, 22, 44}, Mask: net.IPMask{255, 255, 0, 0}}}, + {"10.10.2.0/24", &net.IPNet{IP: net.IP{10, 10, 2, 0}, Mask: net.IPMask{255, 255, 255, 0}}}, + {"10.0.0.100/17", &net.IPNet{IP: net.IP{10, 0, 0, 100}, Mask: net.IPMask{255, 255, 128, 0}}}, + } + + for _, i := range input { + nw, err := ParseCIDR(i.cidr) + if err != nil { + t.Fatal(err) + } + if !CompareIPNet(nw, i.ipnw) { + t.Fatalf("network differ. Expected %v. Got: %v", i.ipnw, nw) + } + } +} diff --git a/migrate/v1/migratev1.go b/migrate/v1/migratev1.go deleted file mode 100644 index 9cd759a3b8e6a..0000000000000 --- a/migrate/v1/migratev1.go +++ /dev/null @@ -1,501 +0,0 @@ -package v1 // import "github.com/docker/docker/migrate/v1" - -import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "runtime" - "strconv" - "sync" - "time" - - "github.com/docker/distribution/reference" - "github.com/docker/docker/distribution/metadata" - "github.com/docker/docker/image" - imagev1 "github.com/docker/docker/image/v1" - "github.com/docker/docker/layer" - "github.com/docker/docker/pkg/ioutils" - refstore "github.com/docker/docker/reference" - "github.com/opencontainers/go-digest" - "github.com/sirupsen/logrus" -) - -type graphIDRegistrar interface { - RegisterByGraphID(string, layer.ChainID, layer.DiffID, string, int64) (layer.Layer, error) - Release(layer.Layer) ([]layer.Metadata, error) -} - -type graphIDMounter interface { - CreateRWLayerByGraphID(string, string, layer.ChainID) error -} - -type checksumCalculator interface { - ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataPath string) (diffID layer.DiffID, size int64, err error) -} - -const ( - graphDirName = "graph" - tarDataFileName = "tar-data.json.gz" - migrationFileName = ".migration-v1-images.json" - migrationTagsFileName = ".migration-v1-tags" - migrationDiffIDFileName = ".migration-diffid" - migrationSizeFileName = ".migration-size" - migrationTarDataFileName = ".migration-tardata" - containersDirName = "containers" - configFileNameLegacy = "config.json" - configFileName = "config.v2.json" - repositoriesFilePrefixLegacy = "repositories-" -) - -var ( - errUnsupported = errors.New("migration is not supported") -) - -// Migrate takes an old graph directory and transforms the metadata into the -// new format. -func Migrate(root, driverName string, ls layer.Store, is image.Store, rs refstore.Store, ms metadata.Store) error { - graphDir := filepath.Join(root, graphDirName) - if _, err := os.Lstat(graphDir); os.IsNotExist(err) { - return nil - } - - mappings, err := restoreMappings(root) - if err != nil { - return err - } - - if cc, ok := ls.(checksumCalculator); ok { - CalculateLayerChecksums(root, cc, mappings) - } - - if registrar, ok := ls.(graphIDRegistrar); !ok { - return errUnsupported - } else if err := migrateImages(root, registrar, is, ms, mappings); err != nil { - return err - } - - err = saveMappings(root, mappings) - if err != nil { - return err - } - - if mounter, ok := ls.(graphIDMounter); !ok { - return errUnsupported - } else if err := migrateContainers(root, mounter, is, mappings); err != nil { - return err - } - - return migrateRefs(root, driverName, rs, mappings) -} - -// CalculateLayerChecksums walks an old graph directory and calculates checksums -// for each layer. These checksums are later used for migration. -func CalculateLayerChecksums(root string, ls checksumCalculator, mappings map[string]image.ID) { - graphDir := filepath.Join(root, graphDirName) - // spawn some extra workers also for maximum performance because the process is bounded by both cpu and io - workers := runtime.NumCPU() * 3 - workQueue := make(chan string, workers) - - wg := sync.WaitGroup{} - - for i := 0; i < workers; i++ { - wg.Add(1) - go func() { - for id := range workQueue { - start := time.Now() - if err := calculateLayerChecksum(graphDir, id, ls); err != nil { - logrus.Errorf("could not calculate checksum for %q, %q", id, err) - } - elapsed := time.Since(start) - logrus.Debugf("layer %s took %.2f seconds", id, elapsed.Seconds()) - } - wg.Done() - }() - } - - dir, err := ioutil.ReadDir(graphDir) - if err != nil { - logrus.Errorf("could not read directory %q", graphDir) - return - } - for _, v := range dir { - v1ID := v.Name() - if err := imagev1.ValidateID(v1ID); err != nil { - continue - } - if _, ok := mappings[v1ID]; ok { // support old migrations without helper files - continue - } - workQueue <- v1ID - } - close(workQueue) - wg.Wait() -} - -func calculateLayerChecksum(graphDir, id string, ls checksumCalculator) error { - diffIDFile := filepath.Join(graphDir, id, migrationDiffIDFileName) - if _, err := os.Lstat(diffIDFile); err == nil { - return nil - } else if !os.IsNotExist(err) { - return err - } - - parent, err := getParent(filepath.Join(graphDir, id)) - if err != nil { - return err - } - - diffID, size, err := ls.ChecksumForGraphID(id, parent, filepath.Join(graphDir, id, tarDataFileName), filepath.Join(graphDir, id, migrationTarDataFileName)) - if err != nil { - return err - } - - if err := ioutil.WriteFile(filepath.Join(graphDir, id, migrationSizeFileName), []byte(strconv.Itoa(int(size))), 0600); err != nil { - return err - } - - if err := ioutils.AtomicWriteFile(filepath.Join(graphDir, id, migrationDiffIDFileName), []byte(diffID), 0600); err != nil { - return err - } - - logrus.Infof("calculated checksum for layer %s: %s", id, diffID) - return nil -} - -func restoreMappings(root string) (map[string]image.ID, error) { - mappings := make(map[string]image.ID) - - mfile := filepath.Join(root, migrationFileName) - f, err := os.Open(mfile) - if err != nil && !os.IsNotExist(err) { - return nil, err - } else if err == nil { - err := json.NewDecoder(f).Decode(&mappings) - if err != nil { - f.Close() - return nil, err - } - f.Close() - } - - return mappings, nil -} - -func saveMappings(root string, mappings map[string]image.ID) error { - mfile := filepath.Join(root, migrationFileName) - f, err := os.OpenFile(mfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return err - } - defer f.Close() - return json.NewEncoder(f).Encode(mappings) -} - -func migrateImages(root string, ls graphIDRegistrar, is image.Store, ms metadata.Store, mappings map[string]image.ID) error { - graphDir := filepath.Join(root, graphDirName) - - dir, err := ioutil.ReadDir(graphDir) - if err != nil { - return err - } - for _, v := range dir { - v1ID := v.Name() - if err := imagev1.ValidateID(v1ID); err != nil { - continue - } - if _, exists := mappings[v1ID]; exists { - continue - } - if err := migrateImage(v1ID, root, ls, is, ms, mappings); err != nil { - continue - } - } - - return nil -} - -func migrateContainers(root string, ls graphIDMounter, is image.Store, imageMappings map[string]image.ID) error { - containersDir := filepath.Join(root, containersDirName) - dir, err := ioutil.ReadDir(containersDir) - if err != nil { - return err - } - for _, v := range dir { - id := v.Name() - - if _, err := os.Stat(filepath.Join(containersDir, id, configFileName)); err == nil { - continue - } - - containerJSON, err := ioutil.ReadFile(filepath.Join(containersDir, id, configFileNameLegacy)) - if err != nil { - logrus.Errorf("migrate container error: %v", err) - continue - } - - var c map[string]*json.RawMessage - if err := json.Unmarshal(containerJSON, &c); err != nil { - logrus.Errorf("migrate container error: %v", err) - continue - } - - imageStrJSON, ok := c["Image"] - if !ok { - return fmt.Errorf("invalid container configuration for %v", id) - } - - var image string - if err := json.Unmarshal([]byte(*imageStrJSON), &image); err != nil { - logrus.Errorf("migrate container error: %v", err) - continue - } - - imageID, ok := imageMappings[image] - if !ok { - logrus.Errorf("image not migrated %v", imageID) // non-fatal error - continue - } - - c["Image"] = rawJSON(imageID) - - containerJSON, err = json.Marshal(c) - if err != nil { - return err - } - - if err := ioutil.WriteFile(filepath.Join(containersDir, id, configFileName), containerJSON, 0600); err != nil { - return err - } - - img, err := is.Get(imageID) - if err != nil { - return err - } - - if err := ls.CreateRWLayerByGraphID(id, id, img.RootFS.ChainID()); err != nil { - logrus.Errorf("migrate container error: %v", err) - continue - } - - logrus.Infof("migrated container %s to point to %s", id, imageID) - - } - return nil -} - -type refAdder interface { - AddTag(ref reference.Named, id digest.Digest, force bool) error - AddDigest(ref reference.Canonical, id digest.Digest, force bool) error -} - -func migrateRefs(root, driverName string, rs refAdder, mappings map[string]image.ID) error { - migrationFile := filepath.Join(root, migrationTagsFileName) - if _, err := os.Lstat(migrationFile); !os.IsNotExist(err) { - return err - } - - type repositories struct { - Repositories map[string]map[string]string - } - - var repos repositories - - f, err := os.Open(filepath.Join(root, repositoriesFilePrefixLegacy+driverName)) - if err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - defer f.Close() - if err := json.NewDecoder(f).Decode(&repos); err != nil { - return err - } - - for name, repo := range repos.Repositories { - for tag, id := range repo { - if strongID, exists := mappings[id]; exists { - ref, err := reference.ParseNormalizedNamed(name) - if err != nil { - logrus.Errorf("migrate tags: invalid name %q, %q", name, err) - continue - } - if !reference.IsNameOnly(ref) { - logrus.Errorf("migrate tags: invalid name %q, unexpected tag or digest", name) - continue - } - if dgst, err := digest.Parse(tag); err == nil { - canonical, err := reference.WithDigest(reference.TrimNamed(ref), dgst) - if err != nil { - logrus.Errorf("migrate tags: invalid digest %q, %q", dgst, err) - continue - } - if err := rs.AddDigest(canonical, strongID.Digest(), false); err != nil { - logrus.Errorf("can't migrate digest %q for %q, err: %q", reference.FamiliarString(ref), strongID, err) - } - } else { - tagRef, err := reference.WithTag(ref, tag) - if err != nil { - logrus.Errorf("migrate tags: invalid tag %q, %q", tag, err) - continue - } - if err := rs.AddTag(tagRef, strongID.Digest(), false); err != nil { - logrus.Errorf("can't migrate tag %q for %q, err: %q", reference.FamiliarString(ref), strongID, err) - } - } - logrus.Infof("migrated tag %s:%s to point to %s", name, tag, strongID) - } - } - } - - mf, err := os.Create(migrationFile) - if err != nil { - return err - } - mf.Close() - - return nil -} - -func getParent(confDir string) (string, error) { - jsonFile := filepath.Join(confDir, "json") - imageJSON, err := ioutil.ReadFile(jsonFile) - if err != nil { - return "", err - } - var parent struct { - Parent string - ParentID digest.Digest `json:"parent_id"` - } - if err := json.Unmarshal(imageJSON, &parent); err != nil { - return "", err - } - if parent.Parent == "" && parent.ParentID != "" { // v1.9 - parent.Parent = parent.ParentID.Hex() - } - // compatibilityID for parent - parentCompatibilityID, err := ioutil.ReadFile(filepath.Join(confDir, "parent")) - if err == nil && len(parentCompatibilityID) > 0 { - parent.Parent = string(parentCompatibilityID) - } - return parent.Parent, nil -} - -func migrateImage(id, root string, ls graphIDRegistrar, is image.Store, ms metadata.Store, mappings map[string]image.ID) (err error) { - defer func() { - if err != nil { - logrus.Errorf("migration failed for %v, err: %v", id, err) - } - }() - - parent, err := getParent(filepath.Join(root, graphDirName, id)) - if err != nil { - return err - } - - var parentID image.ID - if parent != "" { - var exists bool - if parentID, exists = mappings[parent]; !exists { - if err := migrateImage(parent, root, ls, is, ms, mappings); err != nil { - // todo: fail or allow broken chains? - return err - } - parentID = mappings[parent] - } - } - - rootFS := image.NewRootFS() - var history []image.History - - if parentID != "" { - parentImg, err := is.Get(parentID) - if err != nil { - return err - } - - rootFS = parentImg.RootFS - history = parentImg.History - } - - diffIDData, err := ioutil.ReadFile(filepath.Join(root, graphDirName, id, migrationDiffIDFileName)) - if err != nil { - return err - } - diffID, err := digest.Parse(string(diffIDData)) - if err != nil { - return err - } - - sizeStr, err := ioutil.ReadFile(filepath.Join(root, graphDirName, id, migrationSizeFileName)) - if err != nil { - return err - } - size, err := strconv.ParseInt(string(sizeStr), 10, 64) - if err != nil { - return err - } - - layer, err := ls.RegisterByGraphID(id, rootFS.ChainID(), layer.DiffID(diffID), filepath.Join(root, graphDirName, id, migrationTarDataFileName), size) - if err != nil { - return err - } - logrus.Infof("migrated layer %s to %s", id, layer.DiffID()) - - jsonFile := filepath.Join(root, graphDirName, id, "json") - imageJSON, err := ioutil.ReadFile(jsonFile) - if err != nil { - return err - } - - h, err := imagev1.HistoryFromConfig(imageJSON, false) - if err != nil { - return err - } - history = append(history, h) - - rootFS.Append(layer.DiffID()) - - config, err := imagev1.MakeConfigFromV1Config(imageJSON, rootFS, history) - if err != nil { - return err - } - strongID, err := is.Create(config) - if err != nil { - return err - } - logrus.Infof("migrated image %s to %s", id, strongID) - - if parentID != "" { - if err := is.SetParent(strongID, parentID); err != nil { - return err - } - } - - checksum, err := ioutil.ReadFile(filepath.Join(root, graphDirName, id, "checksum")) - if err == nil { // best effort - dgst, err := digest.Parse(string(checksum)) - if err == nil { - V2MetadataService := metadata.NewV2MetadataService(ms) - V2MetadataService.Add(layer.DiffID(), metadata.V2Metadata{Digest: dgst}) - } - } - _, err = ls.Release(layer) - if err != nil { - return err - } - - mappings[id] = strongID - return -} - -func rawJSON(value interface{}) *json.RawMessage { - jsonval, err := json.Marshal(value) - if err != nil { - return nil - } - return (*json.RawMessage)(&jsonval) -} diff --git a/migrate/v1/migratev1_test.go b/migrate/v1/migratev1_test.go deleted file mode 100644 index ef601d0882349..0000000000000 --- a/migrate/v1/migratev1_test.go +++ /dev/null @@ -1,437 +0,0 @@ -package v1 // import "github.com/docker/docker/migrate/v1" - -import ( - "crypto/rand" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "runtime" - "testing" - - "github.com/docker/distribution/reference" - "github.com/docker/docker/distribution/metadata" - "github.com/docker/docker/image" - "github.com/docker/docker/layer" - "github.com/opencontainers/go-digest" -) - -func TestMigrateRefs(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "migrate-tags") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - ioutil.WriteFile(filepath.Join(tmpdir, "repositories-generic"), []byte(`{"Repositories":{"busybox":{"latest":"b3ca410aa2c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108","sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a":"b3ca410aa2c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108"},"registry":{"2":"5d165b8e4b203685301c815e95663231691d383fd5e3d3185d1ce3f8dddead3d","latest":"8d5547a9f329b1d3f93198cd661fb5117e5a96b721c5cf9a2c389e7dd4877128"}}}`), 0600) - - ta := &mockTagAdder{} - err = migrateRefs(tmpdir, "generic", ta, map[string]image.ID{ - "5d165b8e4b203685301c815e95663231691d383fd5e3d3185d1ce3f8dddead3d": image.ID("sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"), - "b3ca410aa2c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108": image.ID("sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9"), - "abcdef3434c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108": image.ID("sha256:56434342345ae68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"), - }) - if err != nil { - t.Fatal(err) - } - - expected := map[string]string{ - "docker.io/library/busybox:latest": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9", - "docker.io/library/busybox@sha256:16a2a52884c2a9481ed267c2d46483eac7693b813a63132368ab098a71303f8a": "sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9", - "docker.io/library/registry:2": "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", - } - - if !reflect.DeepEqual(expected, ta.refs) { - t.Fatalf("Invalid migrated tags: expected %q, got %q", expected, ta.refs) - } - - // second migration is no-op - ioutil.WriteFile(filepath.Join(tmpdir, "repositories-generic"), []byte(`{"Repositories":{"busybox":{"latest":"b3ca410aa2c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108"`), 0600) - err = migrateRefs(tmpdir, "generic", ta, map[string]image.ID{ - "b3ca410aa2c115c05969a7b2c8cf8a9fcf62c1340ed6a601c9ee50df337ec108": image.ID("sha256:fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9"), - }) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(expected, ta.refs) { - t.Fatalf("Invalid migrated tags: expected %q, got %q", expected, ta.refs) - } -} - -func TestMigrateContainers(t *testing.T) { - // TODO Windows: Figure out why this is failing - if runtime.GOOS == "windows" { - t.Skip("Failing on Windows") - } - if runtime.GOARCH != "amd64" { - t.Skip("Test tailored to amd64 architecture") - } - tmpdir, err := ioutil.TempDir("", "migrate-containers") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - err = addContainer(tmpdir, `{"State":{"Running":false,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":0,"ExitCode":0,"Error":"","StartedAt":"2015-11-10T21:42:40.604267436Z","FinishedAt":"2015-11-10T21:42:41.869265487Z"},"ID":"f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c","Created":"2015-11-10T21:42:40.433831551Z","Path":"sh","Args":[],"Config":{"Hostname":"f780ee3f80e6","Domainname":"","User":"","AttachStdin":true,"AttachStdout":true,"AttachStderr":true,"Tty":true,"OpenStdin":true,"StdinOnce":true,"Env":null,"Cmd":["sh"],"Image":"busybox","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"Image":"2c5ac3f849df8627fcf2822727f87c57f38b7129d3604fbc11d861fe856ff093","NetworkSettings":{"Bridge":"","EndpointID":"","Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"HairpinMode":false,"IPAddress":"","IPPrefixLen":0,"IPv6Gateway":"","LinkLocalIPv6Address":"","LinkLocalIPv6PrefixLen":0,"MacAddress":"","NetworkID":"","PortMapping":null,"Ports":null,"SandboxKey":"","SecondaryIPAddresses":null,"SecondaryIPv6Addresses":null},"ResolvConfPath":"/var/lib/docker/containers/f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c/resolv.conf","HostnamePath":"/var/lib/docker/containers/f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c/hostname","HostsPath":"/var/lib/docker/containers/f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c/hosts","LogPath":"/var/lib/docker/containers/f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c/f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c-json.log","Name":"/determined_euclid","Driver":"overlay","ExecDriver":"native-0.2","MountLabel":"","ProcessLabel":"","RestartCount":0,"UpdateDns":false,"HasBeenStartedBefore":false,"MountPoints":{},"Volumes":{},"VolumesRW":{},"AppArmorProfile":""}`) - if err != nil { - t.Fatal(err) - } - - // container with invalid image - err = addContainer(tmpdir, `{"State":{"Running":false,"Paused":false,"Restarting":false,"OOMKilled":false,"Dead":false,"Pid":0,"ExitCode":0,"Error":"","StartedAt":"2015-11-10T21:42:40.604267436Z","FinishedAt":"2015-11-10T21:42:41.869265487Z"},"ID":"e780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c","Created":"2015-11-10T21:42:40.433831551Z","Path":"sh","Args":[],"Config":{"Hostname":"f780ee3f80e6","Domainname":"","User":"","AttachStdin":true,"AttachStdout":true,"AttachStderr":true,"Tty":true,"OpenStdin":true,"StdinOnce":true,"Env":null,"Cmd":["sh"],"Image":"busybox","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"Image":"4c5ac3f849df8627fcf2822727f87c57f38b7129d3604fbc11d861fe856ff093","NetworkSettings":{"Bridge":"","EndpointID":"","Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"HairpinMode":false,"IPAddress":"","IPPrefixLen":0,"IPv6Gateway":"","LinkLocalIPv6Address":"","LinkLocalIPv6PrefixLen":0,"MacAddress":"","NetworkID":"","PortMapping":null,"Ports":null,"SandboxKey":"","SecondaryIPAddresses":null,"SecondaryIPv6Addresses":null},"ResolvConfPath":"/var/lib/docker/containers/f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c/resolv.conf","HostnamePath":"/var/lib/docker/containers/f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c/hostname","HostsPath":"/var/lib/docker/containers/f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c/hosts","LogPath":"/var/lib/docker/containers/f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c/f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c-json.log","Name":"/determined_euclid","Driver":"overlay","ExecDriver":"native-0.2","MountLabel":"","ProcessLabel":"","RestartCount":0,"UpdateDns":false,"HasBeenStartedBefore":false,"MountPoints":{},"Volumes":{},"VolumesRW":{},"AppArmorProfile":""}`) - if err != nil { - t.Fatal(err) - } - - ifs, err := image.NewFSStoreBackend(filepath.Join(tmpdir, "imagedb")) - if err != nil { - t.Fatal(err) - } - - ls := &mockMounter{} - mmMap := make(map[string]image.LayerGetReleaser) - mmMap[runtime.GOOS] = ls - is, err := image.NewImageStore(ifs, mmMap) - if err != nil { - t.Fatal(err) - } - - imgID, err := is.Create([]byte(`{"architecture":"amd64","config":{"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Cmd":["sh"],"Entrypoint":null,"Env":null,"Hostname":"23304fc829f9","Image":"d1592a710ac323612bd786fa8ac20727c58d8a67847e5a65177c594f43919498","Labels":null,"OnBuild":null,"OpenStdin":false,"StdinOnce":false,"Tty":false,"Volumes":null,"WorkingDir":"","Domainname":"","User":""},"container":"349b014153779e30093d94f6df2a43c7a0a164e05aa207389917b540add39b51","container_config":{"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Entrypoint":null,"Env":null,"Hostname":"23304fc829f9","Image":"d1592a710ac323612bd786fa8ac20727c58d8a67847e5a65177c594f43919498","Labels":null,"OnBuild":null,"OpenStdin":false,"StdinOnce":false,"Tty":false,"Volumes":null,"WorkingDir":"","Domainname":"","User":""},"created":"2015-10-31T22:22:55.613815829Z","docker_version":"1.8.2","history":[{"created":"2015-10-31T22:22:54.690851953Z","created_by":"/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"},{"created":"2015-10-31T22:22:55.613815829Z","created_by":"/bin/sh -c #(nop) CMD [\"sh\"]"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1","sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"]}}`)) - if err != nil { - t.Fatal(err) - } - - err = migrateContainers(tmpdir, ls, is, map[string]image.ID{ - "2c5ac3f849df8627fcf2822727f87c57f38b7129d3604fbc11d861fe856ff093": imgID, - }) - if err != nil { - t.Fatal(err) - } - - expected := []mountInfo{{ - "f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c", - "f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c", - "sha256:c3191d32a37d7159b2e30830937d2e30268ad6c375a773a8994911a3aba9b93f", - }} - if !reflect.DeepEqual(expected, ls.mounts) { - t.Fatalf("invalid mounts: expected %q, got %q", expected, ls.mounts) - } - - if actual, expected := ls.count, 0; actual != expected { - t.Fatalf("invalid active mounts: expected %d, got %d", expected, actual) - } - - config2, err := ioutil.ReadFile(filepath.Join(tmpdir, "containers", "f780ee3f80e66e9b432a57049597118a66aab8932be88e5628d4c824edbee37c", "config.v2.json")) - if err != nil { - t.Fatal(err) - } - var config struct{ Image string } - err = json.Unmarshal(config2, &config) - if err != nil { - t.Fatal(err) - } - - if actual, expected := config.Image, string(imgID); actual != expected { - t.Fatalf("invalid image pointer in migrated config: expected %q, got %q", expected, actual) - } - -} - -func TestMigrateImages(t *testing.T) { - // TODO Windows: Figure out why this is failing - if runtime.GOOS == "windows" { - t.Skip("Failing on Windows") - } - if runtime.GOARCH != "amd64" { - t.Skip("Test tailored to amd64 architecture") - } - tmpdir, err := ioutil.TempDir("", "migrate-images") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - // busybox from 1.9 - id1, err := addImage(tmpdir, `{"architecture":"amd64","config":{"Hostname":"23304fc829f9","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":null,"Image":"","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"23304fc829f9b9349416f6eb1afec162907eba3a328f51d53a17f8986f865d65","container_config":{"Hostname":"23304fc829f9","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"],"Image":"","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"created":"2015-10-31T22:22:54.690851953Z","docker_version":"1.8.2","layer_id":"sha256:55dc925c23d1ed82551fd018c27ac3ee731377b6bad3963a2a4e76e753d70e57","os":"linux"}`, "", "") - if err != nil { - t.Fatal(err) - } - - id2, err := addImage(tmpdir, `{"architecture":"amd64","config":{"Hostname":"23304fc829f9","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["sh"],"Image":"d1592a710ac323612bd786fa8ac20727c58d8a67847e5a65177c594f43919498","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"349b014153779e30093d94f6df2a43c7a0a164e05aa207389917b540add39b51","container_config":{"Hostname":"23304fc829f9","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Image":"d1592a710ac323612bd786fa8ac20727c58d8a67847e5a65177c594f43919498","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"created":"2015-10-31T22:22:55.613815829Z","docker_version":"1.8.2","layer_id":"sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4","os":"linux","parent_id":"sha256:039b63dd2cbaa10d6015ea574392530571ed8d7b174090f032211285a71881d0"}`, id1, "") - if err != nil { - t.Fatal(err) - } - - ifs, err := image.NewFSStoreBackend(filepath.Join(tmpdir, "imagedb")) - if err != nil { - t.Fatal(err) - } - - ls := &mockRegistrar{} - mrMap := make(map[string]image.LayerGetReleaser) - mrMap[runtime.GOOS] = ls - is, err := image.NewImageStore(ifs, mrMap) - if err != nil { - t.Fatal(err) - } - - ms, err := metadata.NewFSMetadataStore(filepath.Join(tmpdir, "distribution")) - if err != nil { - t.Fatal(err) - } - mappings := make(map[string]image.ID) - - err = migrateImages(tmpdir, ls, is, ms, mappings) - if err != nil { - t.Fatal(err) - } - - expected := map[string]image.ID{ - id1: image.ID("sha256:ca406eaf9c26898414ff5b7b3a023c33310759d6203be0663dbf1b3a712f432d"), - id2: image.ID("sha256:a488bec94bb96b26a968f913d25ef7d8d204d727ca328b52b4b059c7d03260b6"), - } - - if !reflect.DeepEqual(mappings, expected) { - t.Fatalf("invalid image mappings: expected %q, got %q", expected, mappings) - } - - if actual, expected := ls.count, 2; actual != expected { - t.Fatalf("invalid register count: expected %q, got %q", expected, actual) - } - ls.count = 0 - - // next images are busybox from 1.8.2 - _, err = addImage(tmpdir, `{"id":"17583c7dd0dae6244203b8029733bdb7d17fccbb2b5d93e2b24cf48b8bfd06e2","parent":"d1592a710ac323612bd786fa8ac20727c58d8a67847e5a65177c594f43919498","created":"2015-10-31T22:22:55.613815829Z","container":"349b014153779e30093d94f6df2a43c7a0a164e05aa207389917b540add39b51","container_config":{"Hostname":"23304fc829f9","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":null,"PublishService":"","Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Image":"d1592a710ac323612bd786fa8ac20727c58d8a67847e5a65177c594f43919498","Volumes":null,"VolumeDriver":"","WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":null},"docker_version":"1.8.2","config":{"Hostname":"23304fc829f9","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":null,"PublishService":"","Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["sh"],"Image":"d1592a710ac323612bd786fa8ac20727c58d8a67847e5a65177c594f43919498","Volumes":null,"VolumeDriver":"","WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":null},"architecture":"amd64","os":"linux","Size":0}`, "", "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4") - if err != nil { - t.Fatal(err) - } - - _, err = addImage(tmpdir, `{"id":"d1592a710ac323612bd786fa8ac20727c58d8a67847e5a65177c594f43919498","created":"2015-10-31T22:22:54.690851953Z","container":"23304fc829f9b9349416f6eb1afec162907eba3a328f51d53a17f8986f865d65","container_config":{"Hostname":"23304fc829f9","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":null,"PublishService":"","Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"],"Image":"","Volumes":null,"VolumeDriver":"","WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":null},"docker_version":"1.8.2","config":{"Hostname":"23304fc829f9","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":null,"PublishService":"","Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":null,"Image":"","Volumes":null,"VolumeDriver":"","WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"MacAddress":"","OnBuild":null,"Labels":null},"architecture":"amd64","os":"linux","Size":1108935}`, "", "sha256:55dc925c23d1ed82551fd018c27ac3ee731377b6bad3963a2a4e76e753d70e57") - if err != nil { - t.Fatal(err) - } - - err = migrateImages(tmpdir, ls, is, ms, mappings) - if err != nil { - t.Fatal(err) - } - - expected["d1592a710ac323612bd786fa8ac20727c58d8a67847e5a65177c594f43919498"] = image.ID("sha256:c091bb33854e57e6902b74c08719856d30b5593c7db6143b2b48376b8a588395") - expected["17583c7dd0dae6244203b8029733bdb7d17fccbb2b5d93e2b24cf48b8bfd06e2"] = image.ID("sha256:d963020e755ff2715b936065949472c1f8a6300144b922992a1a421999e71f07") - - if actual, expected := ls.count, 2; actual != expected { - t.Fatalf("invalid register count: expected %q, got %q", expected, actual) - } - - v2MetadataService := metadata.NewV2MetadataService(ms) - receivedMetadata, err := v2MetadataService.GetMetadata(layer.EmptyLayer.DiffID()) - if err != nil { - t.Fatal(err) - } - - expectedMetadata := []metadata.V2Metadata{ - {Digest: digest.Digest("sha256:55dc925c23d1ed82551fd018c27ac3ee731377b6bad3963a2a4e76e753d70e57")}, - {Digest: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4")}, - } - - if !reflect.DeepEqual(expectedMetadata, receivedMetadata) { - t.Fatalf("invalid metadata: expected %q, got %q", expectedMetadata, receivedMetadata) - } - -} - -func TestMigrateUnsupported(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "migrate-empty") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - err = os.MkdirAll(filepath.Join(tmpdir, "graph"), 0700) - if err != nil { - t.Fatal(err) - } - - err = Migrate(tmpdir, "generic", nil, nil, nil, nil) - if err != errUnsupported { - t.Fatalf("expected unsupported error, got %q", err) - } -} - -func TestMigrateEmptyDir(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "migrate-empty") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - err = Migrate(tmpdir, "generic", nil, nil, nil, nil) - if err != nil { - t.Fatal(err) - } -} - -func addImage(dest, jsonConfig, parent, checksum string) (string, error) { - var config struct{ ID string } - if err := json.Unmarshal([]byte(jsonConfig), &config); err != nil { - return "", err - } - if config.ID == "" { - b := make([]byte, 32) - rand.Read(b) - config.ID = hex.EncodeToString(b) - } - contDir := filepath.Join(dest, "graph", config.ID) - if err := os.MkdirAll(contDir, 0700); err != nil { - return "", err - } - if err := ioutil.WriteFile(filepath.Join(contDir, "json"), []byte(jsonConfig), 0600); err != nil { - return "", err - } - if checksum != "" { - if err := ioutil.WriteFile(filepath.Join(contDir, "checksum"), []byte(checksum), 0600); err != nil { - return "", err - } - } - if err := ioutil.WriteFile(filepath.Join(contDir, ".migration-diffid"), []byte(layer.EmptyLayer.DiffID()), 0600); err != nil { - return "", err - } - if err := ioutil.WriteFile(filepath.Join(contDir, ".migration-size"), []byte("0"), 0600); err != nil { - return "", err - } - if parent != "" { - if err := ioutil.WriteFile(filepath.Join(contDir, "parent"), []byte(parent), 0600); err != nil { - return "", err - } - } - if checksum != "" { - if err := ioutil.WriteFile(filepath.Join(contDir, "checksum"), []byte(checksum), 0600); err != nil { - return "", err - } - } - return config.ID, nil -} - -func addContainer(dest, jsonConfig string) error { - var config struct{ ID string } - if err := json.Unmarshal([]byte(jsonConfig), &config); err != nil { - return err - } - contDir := filepath.Join(dest, "containers", config.ID) - if err := os.MkdirAll(contDir, 0700); err != nil { - return err - } - return ioutil.WriteFile(filepath.Join(contDir, "config.json"), []byte(jsonConfig), 0600) -} - -type mockTagAdder struct { - refs map[string]string -} - -func (t *mockTagAdder) AddTag(ref reference.Named, id digest.Digest, force bool) error { - if t.refs == nil { - t.refs = make(map[string]string) - } - t.refs[ref.String()] = id.String() - return nil -} -func (t *mockTagAdder) AddDigest(ref reference.Canonical, id digest.Digest, force bool) error { - return t.AddTag(ref, id, force) -} - -type mockRegistrar struct { - layers map[layer.ChainID]*mockLayer - count int -} - -func (r *mockRegistrar) RegisterByGraphID(graphID string, parent layer.ChainID, diffID layer.DiffID, tarDataFile string, size int64) (layer.Layer, error) { - r.count++ - l := &mockLayer{} - if parent != "" { - p, exists := r.layers[parent] - if !exists { - return nil, fmt.Errorf("invalid parent %q", parent) - } - l.parent = p - l.diffIDs = append(l.diffIDs, p.diffIDs...) - } - l.diffIDs = append(l.diffIDs, diffID) - if r.layers == nil { - r.layers = make(map[layer.ChainID]*mockLayer) - } - r.layers[l.ChainID()] = l - return l, nil -} -func (r *mockRegistrar) Release(l layer.Layer) ([]layer.Metadata, error) { - return nil, nil -} -func (r *mockRegistrar) Get(layer.ChainID) (layer.Layer, error) { - return nil, nil -} - -type mountInfo struct { - name, graphID, parent string -} -type mockMounter struct { - mounts []mountInfo - count int -} - -func (r *mockMounter) CreateRWLayerByGraphID(name string, graphID string, parent layer.ChainID) error { - r.mounts = append(r.mounts, mountInfo{name, graphID, string(parent)}) - return nil -} -func (r *mockMounter) Unmount(string) error { - r.count-- - return nil -} -func (r *mockMounter) Get(layer.ChainID) (layer.Layer, error) { - return nil, nil -} - -func (r *mockMounter) Release(layer.Layer) ([]layer.Metadata, error) { - return nil, nil -} - -type mockLayer struct { - diffIDs []layer.DiffID - parent *mockLayer -} - -func (l *mockLayer) TarStream() (io.ReadCloser, error) { - return nil, nil -} -func (l *mockLayer) TarStreamFrom(layer.ChainID) (io.ReadCloser, error) { - return nil, nil -} - -func (l *mockLayer) ChainID() layer.ChainID { - return layer.CreateChainID(l.diffIDs) -} - -func (l *mockLayer) DiffID() layer.DiffID { - return l.diffIDs[len(l.diffIDs)-1] -} - -func (l *mockLayer) Parent() layer.Layer { - if l.parent == nil { - return nil - } - return l.parent -} - -func (l *mockLayer) Size() (int64, error) { - return 0, nil -} - -func (l *mockLayer) DiffSize() (int64, error) { - return 0, nil -} - -func (l *mockLayer) Metadata() (map[string]string, error) { - return nil, nil -} diff --git a/oci/caps/defaults.go b/oci/caps/defaults.go new file mode 100644 index 0000000000000..242ee5811d2ac --- /dev/null +++ b/oci/caps/defaults.go @@ -0,0 +1,21 @@ +package caps // import "github.com/docker/docker/oci/caps" + +// DefaultCapabilities returns a Linux kernel default capabilities +func DefaultCapabilities() []string { + return []string{ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE", + } +} diff --git a/oci/caps/utils.go b/oci/caps/utils.go new file mode 100644 index 0000000000000..c61f6b49e93e5 --- /dev/null +++ b/oci/caps/utils.go @@ -0,0 +1,126 @@ +package caps // import "github.com/docker/docker/oci/caps" + +import ( + "fmt" + "strings" + + "github.com/docker/docker/errdefs" +) + +var ( + allCaps []string + + // knownCapabilities is a map of all known capabilities, using capability + // name as index. Nil values indicate that the capability is known, but either + // not supported by the Kernel, or not available in the current environment, + // for example, when running Docker-in-Docker with restricted capabilities. + // + // Capabilities are one of the security systems in Linux Security Module (LSM) + // framework provided by the kernel. + // For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html + knownCaps map[string]*struct{} +) + +// GetAllCapabilities returns all capabilities that are availeble in the current +// environment. +func GetAllCapabilities() []string { + initCaps() + return allCaps +} + +// knownCapabilities returns a map of all known capabilities, using capability +// name as index. Nil values indicate that the capability is known, but either +// not supported by the Kernel, or not available in the current environment, for +// example, when running Docker-in-Docker with restricted capabilities. +func knownCapabilities() map[string]*struct{} { + initCaps() + return knownCaps +} + +// inSlice tests whether a string is contained in a slice of strings or not. +func inSlice(slice []string, s string) bool { + for _, ss := range slice { + if s == ss { + return true + } + } + return false +} + +const allCapabilities = "ALL" + +// NormalizeLegacyCapabilities normalizes, and validates CapAdd/CapDrop capabilities +// by upper-casing them, and adding a CAP_ prefix (if not yet present). +// +// This function also accepts the "ALL" magic-value, that's used by CapAdd/CapDrop. +func NormalizeLegacyCapabilities(caps []string) ([]string, error) { + var ( + normalized []string + capabilityList = knownCapabilities() + ) + + for _, c := range caps { + c = strings.ToUpper(c) + if c == allCapabilities { + normalized = append(normalized, c) + continue + } + if !strings.HasPrefix(c, "CAP_") { + c = "CAP_" + c + } + if v, ok := capabilityList[c]; !ok { + return nil, errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c)) + } else if v == nil { + return nil, errdefs.InvalidParameter(fmt.Errorf("capability not supported by your kernel or not available in the current environment: %q", c)) + } + normalized = append(normalized, c) + } + return normalized, nil +} + +// TweakCapabilities tweaks capabilities by adding, dropping, or overriding +// capabilities in the basics capabilities list. +func TweakCapabilities(basics, adds, drops []string, privileged bool) ([]string, error) { + switch { + case privileged: + // Privileged containers get all capabilities + return GetAllCapabilities(), nil + case len(adds) == 0 && len(drops) == 0: + // Nothing to tweak; we're done + return basics, nil + } + + capDrop, err := NormalizeLegacyCapabilities(drops) + if err != nil { + return nil, err + } + capAdd, err := NormalizeLegacyCapabilities(adds) + if err != nil { + return nil, err + } + + var caps []string + + switch { + case inSlice(capAdd, allCapabilities): + // Add all capabilities except ones on capDrop + for _, c := range GetAllCapabilities() { + if !inSlice(capDrop, c) { + caps = append(caps, c) + } + } + case inSlice(capDrop, allCapabilities): + // "Drop" all capabilities; use what's in capAdd instead + caps = capAdd + default: + // First drop some capabilities + for _, c := range basics { + if !inSlice(capDrop, c) { + caps = append(caps, c) + } + } + // Then add the list of capabilities from capAdd + caps = append(caps, capAdd...) + } + return caps, nil +} diff --git a/oci/caps/utils_linux.go b/oci/caps/utils_linux.go new file mode 100644 index 0000000000000..06dc3410fc772 --- /dev/null +++ b/oci/caps/utils_linux.go @@ -0,0 +1,35 @@ +package caps // import "github.com/docker/docker/oci/caps" +import ( + "sync" + + ccaps "github.com/containerd/containerd/pkg/cap" + "github.com/sirupsen/logrus" +) + +var initCapsOnce sync.Once + +func initCaps() { + initCapsOnce.Do(func() { + rawCaps := ccaps.Known() + curCaps, err := ccaps.Current() + if err != nil { + logrus.WithError(err).Error("failed to get capabilities from current environment") + allCaps = rawCaps + } else { + allCaps = curCaps + } + knownCaps = make(map[string]*struct{}, len(rawCaps)) + for _, capName := range rawCaps { + // For now, we assume the capability is available if we failed to + // get the capabilities from the current environment. This keeps the + // old (pre-detection) behavior, and prevents creating containers with + // no capabilities. The OCI runtime or kernel may still refuse capa- + // bilities that are not available, and produce an error in that case. + if len(curCaps) > 0 && !inSlice(curCaps, capName) { + knownCaps[capName] = nil + continue + } + knownCaps[capName] = &struct{}{} + } + }) +} diff --git a/oci/caps/utils_other.go b/oci/caps/utils_other.go new file mode 100644 index 0000000000000..5634a65720398 --- /dev/null +++ b/oci/caps/utils_other.go @@ -0,0 +1,8 @@ +//go:build !linux +// +build !linux + +package caps // import "github.com/docker/docker/oci/caps" + +func initCaps() { + // no capabilities on Windows +} diff --git a/oci/defaults.go b/oci/defaults.go index 992157b0f5771..55a1d7a99ef6b 100644 --- a/oci/defaults.go +++ b/oci/defaults.go @@ -4,40 +4,17 @@ import ( "os" "runtime" - "github.com/opencontainers/runtime-spec/specs-go" + "github.com/docker/docker/oci/caps" + specs "github.com/opencontainers/runtime-spec/specs-go" ) func iPtr(i int64) *int64 { return &i } func u32Ptr(i int64) *uint32 { u := uint32(i); return &u } func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm } -func defaultCapabilities() []string { - return []string{ - "CAP_CHOWN", - "CAP_DAC_OVERRIDE", - "CAP_FSETID", - "CAP_FOWNER", - "CAP_MKNOD", - "CAP_NET_RAW", - "CAP_SETGID", - "CAP_SETUID", - "CAP_SETFCAP", - "CAP_SETPCAP", - "CAP_NET_BIND_SERVICE", - "CAP_SYS_CHROOT", - "CAP_KILL", - "CAP_AUDIT_WRITE", - } -} - // DefaultSpec returns the default spec used by docker for the current Platform func DefaultSpec() specs.Spec { - return DefaultOSSpec(runtime.GOOS) -} - -// DefaultOSSpec returns the spec for a given OS -func DefaultOSSpec(osName string) specs.Spec { - if osName == "windows" { + if runtime.GOOS == "windows" { return DefaultWindowsSpec() } return DefaultLinuxSpec() @@ -55,158 +32,150 @@ func DefaultWindowsSpec() specs.Spec { // DefaultLinuxSpec create a default spec for running Linux containers func DefaultLinuxSpec() specs.Spec { - s := specs.Spec{ + return specs.Spec{ Version: specs.Version, Process: &specs.Process{ Capabilities: &specs.LinuxCapabilities{ - Bounding: defaultCapabilities(), - Permitted: defaultCapabilities(), - Inheritable: defaultCapabilities(), - Effective: defaultCapabilities(), + Bounding: caps.DefaultCapabilities(), + Permitted: caps.DefaultCapabilities(), + Inheritable: caps.DefaultCapabilities(), + Effective: caps.DefaultCapabilities(), }, }, Root: &specs.Root{}, - } - s.Mounts = []specs.Mount{ - { - Destination: "/proc", - Type: "proc", - Source: "proc", - Options: []string{"nosuid", "noexec", "nodev"}, - }, - { - Destination: "/dev", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - { - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, - }, - { - Destination: "/sys", - Type: "sysfs", - Source: "sysfs", - Options: []string{"nosuid", "noexec", "nodev", "ro"}, - }, - { - Destination: "/sys/fs/cgroup", - Type: "cgroup", - Source: "cgroup", - Options: []string{"ro", "nosuid", "noexec", "nodev"}, - }, - { - Destination: "/dev/mqueue", - Type: "mqueue", - Source: "mqueue", - Options: []string{"nosuid", "noexec", "nodev"}, - }, - { - Destination: "/dev/shm", - Type: "tmpfs", - Source: "shm", - Options: []string{"nosuid", "noexec", "nodev", "mode=1777"}, - }, - } - - s.Linux = &specs.Linux{ - MaskedPaths: []string{ - "/proc/acpi", - "/proc/kcore", - "/proc/keys", - "/proc/latency_stats", - "/proc/timer_list", - "/proc/timer_stats", - "/proc/sched_debug", - "/proc/scsi", - "/sys/firmware", - }, - ReadonlyPaths: []string{ - "/proc/asound", - "/proc/bus", - "/proc/fs", - "/proc/irq", - "/proc/sys", - "/proc/sysrq-trigger", - }, - Namespaces: []specs.LinuxNamespace{ - {Type: "mount"}, - {Type: "network"}, - {Type: "uts"}, - {Type: "pid"}, - {Type: "ipc"}, + Mounts: []specs.Mount{ + { + Destination: "/proc", + Type: "proc", + Source: "proc", + Options: []string{"nosuid", "noexec", "nodev"}, + }, + { + Destination: "/dev", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, + }, + { + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, + }, + { + Destination: "/sys", + Type: "sysfs", + Source: "sysfs", + Options: []string{"nosuid", "noexec", "nodev", "ro"}, + }, + { + Destination: "/sys/fs/cgroup", + Type: "cgroup", + Source: "cgroup", + Options: []string{"ro", "nosuid", "noexec", "nodev"}, + }, + { + Destination: "/dev/mqueue", + Type: "mqueue", + Source: "mqueue", + Options: []string{"nosuid", "noexec", "nodev"}, + }, + { + Destination: "/dev/shm", + Type: "tmpfs", + Source: "shm", + Options: []string{"nosuid", "noexec", "nodev", "mode=1777"}, + }, }, - // Devices implicitly contains the following devices: - // null, zero, full, random, urandom, tty, console, and ptmx. - // ptmx is a bind mount or symlink of the container's ptmx. - // See also: https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#default-devices - Devices: []specs.LinuxDevice{}, - Resources: &specs.LinuxResources{ - Devices: []specs.LinuxDeviceCgroup{ - { - Allow: false, - Access: "rwm", - }, - { - Allow: true, - Type: "c", - Major: iPtr(1), - Minor: iPtr(5), - Access: "rwm", - }, - { - Allow: true, - Type: "c", - Major: iPtr(1), - Minor: iPtr(3), - Access: "rwm", - }, - { - Allow: true, - Type: "c", - Major: iPtr(1), - Minor: iPtr(9), - Access: "rwm", - }, - { - Allow: true, - Type: "c", - Major: iPtr(1), - Minor: iPtr(8), - Access: "rwm", - }, - { - Allow: true, - Type: "c", - Major: iPtr(5), - Minor: iPtr(0), - Access: "rwm", - }, - { - Allow: true, - Type: "c", - Major: iPtr(5), - Minor: iPtr(1), - Access: "rwm", - }, - { - Allow: false, - Type: "c", - Major: iPtr(10), - Minor: iPtr(229), - Access: "rwm", + Linux: &specs.Linux{ + MaskedPaths: []string{ + "/proc/asound", + "/proc/acpi", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/proc/scsi", + "/sys/firmware", + }, + ReadonlyPaths: []string{ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger", + }, + Namespaces: []specs.LinuxNamespace{ + {Type: "mount"}, + {Type: "network"}, + {Type: "uts"}, + {Type: "pid"}, + {Type: "ipc"}, + }, + // Devices implicitly contains the following devices: + // null, zero, full, random, urandom, tty, console, and ptmx. + // ptmx is a bind mount or symlink of the container's ptmx. + // See also: https://github.com/opencontainers/runtime-spec/blob/master/config-linux.md#default-devices + Devices: []specs.LinuxDevice{}, + Resources: &specs.LinuxResources{ + Devices: []specs.LinuxDeviceCgroup{ + { + Allow: false, + Access: "rwm", + }, + { + Allow: true, + Type: "c", + Major: iPtr(1), + Minor: iPtr(5), + Access: "rwm", + }, + { + Allow: true, + Type: "c", + Major: iPtr(1), + Minor: iPtr(3), + Access: "rwm", + }, + { + Allow: true, + Type: "c", + Major: iPtr(1), + Minor: iPtr(9), + Access: "rwm", + }, + { + Allow: true, + Type: "c", + Major: iPtr(1), + Minor: iPtr(8), + Access: "rwm", + }, + { + Allow: true, + Type: "c", + Major: iPtr(5), + Minor: iPtr(0), + Access: "rwm", + }, + { + Allow: true, + Type: "c", + Major: iPtr(5), + Minor: iPtr(1), + Access: "rwm", + }, + { + Allow: false, + Type: "c", + Major: iPtr(10), + Minor: iPtr(229), + Access: "rwm", + }, }, }, }, } - - // For LCOW support, populate a blank Windows spec - if runtime.GOOS == "windows" { - s.Windows = &specs.Windows{} - } - - return s } diff --git a/oci/devices_linux.go b/oci/devices_linux.go index 46d4e1d32d43d..ca1c4886b9678 100644 --- a/oci/devices_linux.go +++ b/oci/devices_linux.go @@ -6,32 +6,31 @@ import ( "path/filepath" "strings" - "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/devices" - "github.com/opencontainers/runtime-spec/specs-go" + specs "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/unix" ) -// Device transforms a libcontainer configs.Device to a specs.LinuxDevice object. -func Device(d *configs.Device) specs.LinuxDevice { +// Device transforms a libcontainer devices.Device to a specs.LinuxDevice object. +func Device(d *devices.Device) specs.LinuxDevice { return specs.LinuxDevice{ Type: string(d.Type), Path: d.Path, Major: d.Major, Minor: d.Minor, - FileMode: fmPtr(int64(d.FileMode)), + FileMode: fmPtr(int64(d.FileMode &^ unix.S_IFMT)), // strip file type, as OCI spec only expects file-mode to be included UID: u32Ptr(int64(d.Uid)), GID: u32Ptr(int64(d.Gid)), } } -func deviceCgroup(d *configs.Device) specs.LinuxDeviceCgroup { - t := string(d.Type) +func deviceCgroup(d *devices.Device) specs.LinuxDeviceCgroup { return specs.LinuxDeviceCgroup{ Allow: true, - Type: t, + Type: string(d.Type), Major: &d.Major, Minor: &d.Minor, - Access: d.Permissions, + Access: string(d.Permissions), } } @@ -61,7 +60,8 @@ func DevicesFromPath(pathOnHost, pathInContainer, cgroupPermissions string) (dev if src, e := os.Stat(resolvedPathOnHost); e == nil && src.IsDir() { // mount the internal devices recursively - filepath.Walk(resolvedPathOnHost, func(dpath string, f os.FileInfo, e error) error { + // TODO check if additional errors should be handled or logged + _ = filepath.Walk(resolvedPathOnHost, func(dpath string, f os.FileInfo, _ error) error { childDevice, e := devices.DeviceFromPath(dpath, cgroupPermissions) if e != nil { // ignore the device diff --git a/oci/devices_linux_test.go b/oci/devices_linux_test.go new file mode 100644 index 0000000000000..42ef2a6151018 --- /dev/null +++ b/oci/devices_linux_test.go @@ -0,0 +1,31 @@ +package oci + +import ( + "os" + "testing" + + "github.com/opencontainers/runc/libcontainer/devices" + "golang.org/x/sys/unix" + "gotest.tools/v3/assert" +) + +func TestDeviceMode(t *testing.T) { + tests := []struct { + name string + in os.FileMode + out os.FileMode + }{ + {name: "regular permissions", in: 0777, out: 0777}, + {name: "block device", in: 0777 | unix.S_IFBLK, out: 0777}, + {name: "character device", in: 0777 | unix.S_IFCHR, out: 0777}, + {name: "fifo device", in: 0777 | unix.S_IFIFO, out: 0777}, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + d := Device(&devices.Device{FileMode: tc.in}) + assert.Equal(t, *d.FileMode, tc.out) + }) + } +} diff --git a/oci/devices_unsupported.go b/oci/devices_unsupported.go deleted file mode 100644 index af6dd3bda27b2..0000000000000 --- a/oci/devices_unsupported.go +++ /dev/null @@ -1,20 +0,0 @@ -// +build !linux - -package oci // import "github.com/docker/docker/oci" - -import ( - "errors" - - "github.com/opencontainers/runc/libcontainer/configs" - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -// Device transforms a libcontainer configs.Device to a specs.Device object. -// Not implemented -func Device(d *configs.Device) specs.LinuxDevice { return specs.LinuxDevice{} } - -// DevicesFromPath computes a list of devices and device permissions from paths (pathOnHost and pathInContainer) and cgroup permissions. -// Not implemented -func DevicesFromPath(pathOnHost, pathInContainer, cgroupPermissions string) (devs []specs.LinuxDevice, devPermissions []specs.LinuxDeviceCgroup, err error) { - return nil, nil, errors.New("oci/devices: unsupported platform") -} diff --git a/oci/fixtures/default-old-format.json b/oci/fixtures/default-old-format.json new file mode 100644 index 0000000000000..0e52bf95ec346 --- /dev/null +++ b/oci/fixtures/default-old-format.json @@ -0,0 +1,1593 @@ +{ + "defaultAction": "SCMP_ACT_ERRNO", + "architectures": [ + "SCMP_ARCH_X86_64", + "SCMP_ARCH_X86", + "SCMP_ARCH_X32" + ], + "syscalls": [ + { + "name": "accept", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "accept4", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "access", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "alarm", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "bind", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "brk", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "capget", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "capset", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chmod", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chown", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chown32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clock_getres", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clock_gettime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clock_nanosleep", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "close", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "connect", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "copy_file_range", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "creat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "dup", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "dup2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "dup3", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_create", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_create1", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_ctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_ctl_old", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_pwait", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_wait", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_wait_old", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "eventfd", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "eventfd2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "execve", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "execveat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "exit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "exit_group", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "faccessat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fadvise64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fadvise64_64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fallocate", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fanotify_mark", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchmod", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchmodat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchown", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchown32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchownat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fcntl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fcntl64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fdatasync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fgetxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "flistxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "flock", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fork", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fremovexattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fsetxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstatat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstatfs", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstatfs64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fsync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ftruncate", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ftruncate64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "futex", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "futimesat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getcpu", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getcwd", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getdents", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getdents64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getegid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getegid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "geteuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "geteuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgroups", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgroups32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getitimer", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpeername", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpgrp", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getppid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpriority", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getrandom", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getrlimit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "get_robust_list", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getrusage", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getsid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getsockname", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getsockopt", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "get_thread_area", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "gettid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "gettimeofday", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "inotify_add_watch", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "inotify_init", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "inotify_init1", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "inotify_rm_watch", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "io_cancel", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ioctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "io_destroy", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "io_getevents", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ioprio_get", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ioprio_set", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "io_setup", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "io_submit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ipc", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "kill", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lchown", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lchown32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lgetxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "link", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "linkat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "listen", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "listxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "llistxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "_llseek", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lremovexattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lseek", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lsetxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lstat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lstat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "madvise", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "memfd_create", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mincore", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mkdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mkdirat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mknod", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mknodat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mlock", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mlock2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mlockall", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mmap", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mmap2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mprotect", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_getsetattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_notify", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_open", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_timedreceive", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_timedsend", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_unlink", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mremap", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "msgctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "msgget", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "msgrcv", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "msgsnd", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "msync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "munlock", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "munlockall", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "munmap", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "nanosleep", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "newfstatat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "_newselect", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "open", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "openat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pause", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "personality", + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 0, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "name": "personality", + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 8, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "name": "personality", + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 4294967295, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "name": "pipe", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pipe2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "poll", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ppoll", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "prctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pread64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "preadv", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "prlimit64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pselect6", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pwrite64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pwritev", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "read", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readahead", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readlink", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readlinkat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readv", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "recv", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "recvfrom", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "recvmmsg", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "recvmsg", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "remap_file_pages", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "removexattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rename", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "renameat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "renameat2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "restart_syscall", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rmdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigaction", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigpending", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigprocmask", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigqueueinfo", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigreturn", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigsuspend", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigtimedwait", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_tgsigqueueinfo", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getaffinity", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getparam", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_get_priority_max", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_get_priority_min", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getscheduler", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_rr_get_interval", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_setaffinity", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_setattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_setparam", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_setscheduler", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_yield", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "seccomp", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "select", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "semctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "semget", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "semop", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "semtimedop", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "send", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendfile", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendfile64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendmmsg", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendmsg", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendto", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setfsgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setfsgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setfsuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setfsuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgroups", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgroups32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setitimer", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setpgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setpriority", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setregid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setregid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setresgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setresgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setresuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setresuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setreuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setreuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setrlimit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "set_robust_list", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setsid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setsockopt", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "set_thread_area", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "set_tid_address", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "shmat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "shmctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "shmdt", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "shmget", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "shutdown", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sigaltstack", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "signalfd", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "signalfd4", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sigreturn", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "socket", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "socketcall", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "socketpair", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "splice", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "stat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "stat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "statfs", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "statfs64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "symlink", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "symlinkat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sync_file_range", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "syncfs", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sysinfo", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "syslog", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "tee", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "tgkill", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "time", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_create", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_delete", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timerfd_create", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timerfd_gettime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timerfd_settime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_getoverrun", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_gettime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_settime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "times", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "tkill", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "truncate", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "truncate64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ugetrlimit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "umask", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "uname", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "unlink", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "unlinkat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "utime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "utimensat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "utimes", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "vfork", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "vmsplice", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "wait4", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "waitid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "waitpid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "write", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "writev", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "arch_prctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "modify_ldt", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chroot", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clone", + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 2080505856, + "valueTwo": 0, + "op": "SCMP_CMP_MASKED_EQ" + } + ] + } + ] +} \ No newline at end of file diff --git a/oci/fixtures/default.json b/oci/fixtures/default.json new file mode 100644 index 0000000000000..8d4d21145eef2 --- /dev/null +++ b/oci/fixtures/default.json @@ -0,0 +1,813 @@ +{ + "defaultAction": "SCMP_ACT_ERRNO", + "archMap": [ + { + "architecture": "SCMP_ARCH_X86_64", + "subArchitectures": [ + "SCMP_ARCH_X86", + "SCMP_ARCH_X32" + ] + }, + { + "architecture": "SCMP_ARCH_AARCH64", + "subArchitectures": [ + "SCMP_ARCH_ARM" + ] + }, + { + "architecture": "SCMP_ARCH_MIPS64", + "subArchitectures": [ + "SCMP_ARCH_MIPS", + "SCMP_ARCH_MIPS64N32" + ] + }, + { + "architecture": "SCMP_ARCH_MIPS64N32", + "subArchitectures": [ + "SCMP_ARCH_MIPS", + "SCMP_ARCH_MIPS64" + ] + }, + { + "architecture": "SCMP_ARCH_MIPSEL64", + "subArchitectures": [ + "SCMP_ARCH_MIPSEL", + "SCMP_ARCH_MIPSEL64N32" + ] + }, + { + "architecture": "SCMP_ARCH_MIPSEL64N32", + "subArchitectures": [ + "SCMP_ARCH_MIPSEL", + "SCMP_ARCH_MIPSEL64" + ] + }, + { + "architecture": "SCMP_ARCH_S390X", + "subArchitectures": [ + "SCMP_ARCH_S390" + ] + } + ], + "syscalls": [ + { + "names": [ + "accept", + "accept4", + "access", + "adjtimex", + "alarm", + "bind", + "brk", + "capget", + "capset", + "chdir", + "chmod", + "chown", + "chown32", + "clock_getres", + "clock_getres_time64", + "clock_gettime", + "clock_gettime64", + "clock_nanosleep", + "clock_nanosleep_time64", + "close", + "connect", + "copy_file_range", + "creat", + "dup", + "dup2", + "dup3", + "epoll_create", + "epoll_create1", + "epoll_ctl", + "epoll_ctl_old", + "epoll_pwait", + "epoll_wait", + "epoll_wait_old", + "eventfd", + "eventfd2", + "execve", + "execveat", + "exit", + "exit_group", + "faccessat", + "fadvise64", + "fadvise64_64", + "fallocate", + "fanotify_mark", + "fchdir", + "fchmod", + "fchmodat", + "fchown", + "fchown32", + "fchownat", + "fcntl", + "fcntl64", + "fdatasync", + "fgetxattr", + "flistxattr", + "flock", + "fork", + "fremovexattr", + "fsetxattr", + "fstat", + "fstat64", + "fstatat64", + "fstatfs", + "fstatfs64", + "fsync", + "ftruncate", + "ftruncate64", + "futex", + "futex_time64", + "futimesat", + "getcpu", + "getcwd", + "getdents", + "getdents64", + "getegid", + "getegid32", + "geteuid", + "geteuid32", + "getgid", + "getgid32", + "getgroups", + "getgroups32", + "getitimer", + "getpeername", + "getpgid", + "getpgrp", + "getpid", + "getppid", + "getpriority", + "getrandom", + "getresgid", + "getresgid32", + "getresuid", + "getresuid32", + "getrlimit", + "get_robust_list", + "getrusage", + "getsid", + "getsockname", + "getsockopt", + "get_thread_area", + "gettid", + "gettimeofday", + "getuid", + "getuid32", + "getxattr", + "inotify_add_watch", + "inotify_init", + "inotify_init1", + "inotify_rm_watch", + "io_cancel", + "ioctl", + "io_destroy", + "io_getevents", + "io_pgetevents", + "io_pgetevents_time64", + "ioprio_get", + "ioprio_set", + "io_setup", + "io_submit", + "ipc", + "kill", + "lchown", + "lchown32", + "lgetxattr", + "link", + "linkat", + "listen", + "listxattr", + "llistxattr", + "_llseek", + "lremovexattr", + "lseek", + "lsetxattr", + "lstat", + "lstat64", + "madvise", + "memfd_create", + "mincore", + "mkdir", + "mkdirat", + "mknod", + "mknodat", + "mlock", + "mlock2", + "mlockall", + "mmap", + "mmap2", + "mprotect", + "mq_getsetattr", + "mq_notify", + "mq_open", + "mq_timedreceive", + "mq_timedreceive_time64", + "mq_timedsend", + "mq_timedsend_time64", + "mq_unlink", + "mremap", + "msgctl", + "msgget", + "msgrcv", + "msgsnd", + "msync", + "munlock", + "munlockall", + "munmap", + "nanosleep", + "newfstatat", + "_newselect", + "open", + "openat", + "pause", + "pipe", + "pipe2", + "poll", + "ppoll", + "ppoll_time64", + "prctl", + "pread64", + "preadv", + "preadv2", + "prlimit64", + "pselect6", + "pselect6_time64", + "pwrite64", + "pwritev", + "pwritev2", + "read", + "readahead", + "readlink", + "readlinkat", + "readv", + "recv", + "recvfrom", + "recvmmsg", + "recvmmsg_time64", + "recvmsg", + "remap_file_pages", + "removexattr", + "rename", + "renameat", + "renameat2", + "restart_syscall", + "rmdir", + "rt_sigaction", + "rt_sigpending", + "rt_sigprocmask", + "rt_sigqueueinfo", + "rt_sigreturn", + "rt_sigsuspend", + "rt_sigtimedwait", + "rt_sigtimedwait_time64", + "rt_tgsigqueueinfo", + "sched_getaffinity", + "sched_getattr", + "sched_getparam", + "sched_get_priority_max", + "sched_get_priority_min", + "sched_getscheduler", + "sched_rr_get_interval", + "sched_rr_get_interval_time64", + "sched_setaffinity", + "sched_setattr", + "sched_setparam", + "sched_setscheduler", + "sched_yield", + "seccomp", + "select", + "semctl", + "semget", + "semop", + "semtimedop", + "semtimedop_time64", + "send", + "sendfile", + "sendfile64", + "sendmmsg", + "sendmsg", + "sendto", + "setfsgid", + "setfsgid32", + "setfsuid", + "setfsuid32", + "setgid", + "setgid32", + "setgroups", + "setgroups32", + "setitimer", + "setpgid", + "setpriority", + "setregid", + "setregid32", + "setresgid", + "setresgid32", + "setresuid", + "setresuid32", + "setreuid", + "setreuid32", + "setrlimit", + "set_robust_list", + "setsid", + "setsockopt", + "set_thread_area", + "set_tid_address", + "setuid", + "setuid32", + "setxattr", + "shmat", + "shmctl", + "shmdt", + "shmget", + "shutdown", + "sigaltstack", + "signalfd", + "signalfd4", + "sigprocmask", + "sigreturn", + "socket", + "socketcall", + "socketpair", + "splice", + "stat", + "stat64", + "statfs", + "statfs64", + "statx", + "symlink", + "symlinkat", + "sync", + "sync_file_range", + "syncfs", + "sysinfo", + "tee", + "tgkill", + "time", + "timer_create", + "timer_delete", + "timer_getoverrun", + "timer_gettime", + "timer_gettime64", + "timer_settime", + "timer_settime64", + "timerfd_create", + "timerfd_gettime", + "timerfd_gettime64", + "timerfd_settime", + "timerfd_settime64", + "times", + "tkill", + "truncate", + "truncate64", + "ugetrlimit", + "umask", + "uname", + "unlink", + "unlinkat", + "utime", + "utimensat", + "utimensat_time64", + "utimes", + "vfork", + "vmsplice", + "wait4", + "waitid", + "waitpid", + "write", + "writev" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "ptrace" + ], + "action": "SCMP_ACT_ALLOW", + "args": null, + "comment": "", + "includes": { + "minKernel": "4.8" + }, + "excludes": {} + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 0, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 8, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 131072, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 131080, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "personality" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 4294967295, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": {} + }, + { + "names": [ + "sync_file_range2" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "arches": [ + "ppc64le" + ] + }, + "excludes": {} + }, + { + "names": [ + "arm_fadvise64_64", + "arm_sync_file_range", + "sync_file_range2", + "breakpoint", + "cacheflush", + "set_tls" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "arches": [ + "arm", + "arm64" + ] + }, + "excludes": {} + }, + { + "names": [ + "arch_prctl" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "arches": [ + "amd64", + "x32" + ] + }, + "excludes": {} + }, + { + "names": [ + "modify_ldt" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "arches": [ + "amd64", + "x32", + "x86" + ] + }, + "excludes": {} + }, + { + "names": [ + "s390_pci_mmio_read", + "s390_pci_mmio_write", + "s390_runtime_instr" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "arches": [ + "s390", + "s390x" + ] + }, + "excludes": {} + }, + { + "names": [ + "open_by_handle_at" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_DAC_READ_SEARCH" + ] + }, + "excludes": {} + }, + { + "names": [ + "bpf", + "clone", + "fanotify_init", + "lookup_dcookie", + "mount", + "name_to_handle_at", + "perf_event_open", + "quotactl", + "setdomainname", + "sethostname", + "setns", + "syslog", + "umount", + "umount2", + "unshare" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_ADMIN" + ] + }, + "excludes": {} + }, + { + "names": [ + "clone" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 2080505856, + "valueTwo": 0, + "op": "SCMP_CMP_MASKED_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_SYS_ADMIN" + ], + "arches": [ + "s390", + "s390x" + ] + } + }, + { + "names": [ + "clone" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 1, + "value": 2080505856, + "valueTwo": 0, + "op": "SCMP_CMP_MASKED_EQ" + } + ], + "comment": "s390 parameter ordering for clone is different", + "includes": { + "arches": [ + "s390", + "s390x" + ] + }, + "excludes": { + "caps": [ + "CAP_SYS_ADMIN" + ] + } + }, + { + "names": [ + "reboot" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_BOOT" + ] + }, + "excludes": {} + }, + { + "names": [ + "chroot" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_CHROOT" + ] + }, + "excludes": {} + }, + { + "names": [ + "delete_module", + "init_module", + "finit_module", + "query_module" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_MODULE" + ] + }, + "excludes": {} + }, + { + "names": [ + "acct" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_PACCT" + ] + }, + "excludes": {} + }, + { + "names": [ + "kcmp", + "process_vm_readv", + "process_vm_writev", + "ptrace" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_PTRACE" + ] + }, + "excludes": {} + }, + { + "names": [ + "iopl", + "ioperm" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_RAWIO" + ] + }, + "excludes": {} + }, + { + "names": [ + "settimeofday", + "stime", + "clock_settime" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_TIME" + ] + }, + "excludes": {} + }, + { + "names": [ + "vhangup" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_TTY_CONFIG" + ] + }, + "excludes": {} + }, + { + "names": [ + "get_mempolicy", + "mbind", + "set_mempolicy" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYS_NICE" + ] + }, + "excludes": {} + }, + { + "names": [ + "syslog" + ], + "action": "SCMP_ACT_ALLOW", + "args": [], + "comment": "", + "includes": { + "caps": [ + "CAP_SYSLOG" + ] + }, + "excludes": {} + } + ] +} \ No newline at end of file diff --git a/oci/fixtures/example.json b/oci/fixtures/example.json new file mode 100644 index 0000000000000..674ca50fd9734 --- /dev/null +++ b/oci/fixtures/example.json @@ -0,0 +1,27 @@ +{ + "defaultAction": "SCMP_ACT_ERRNO", + "syscalls": [ + { + "name": "clone", + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 2080505856, + "valueTwo": 0, + "op": "SCMP_CMP_MASKED_EQ" + } + ] + }, + { + "name": "open", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "close", + "action": "SCMP_ACT_ALLOW", + "args": [] + } + ] +} diff --git a/oci/namespaces.go b/oci/namespaces.go index 5a2d8f2087362..f32e489b4a27f 100644 --- a/oci/namespaces.go +++ b/oci/namespaces.go @@ -1,6 +1,6 @@ package oci // import "github.com/docker/docker/oci" -import "github.com/opencontainers/runtime-spec/specs-go" +import specs "github.com/opencontainers/runtime-spec/specs-go" // RemoveNamespace removes the `nsType` namespace from OCI spec `s` func RemoveNamespace(s *specs.Spec, nsType specs.LinuxNamespaceType) { diff --git a/oci/oci.go b/oci/oci.go new file mode 100644 index 0000000000000..2a4c0d79aa3b1 --- /dev/null +++ b/oci/oci.go @@ -0,0 +1,72 @@ +package oci // import "github.com/docker/docker/oci" + +import ( + "fmt" + "regexp" + "strconv" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// TODO verify if this regex is correct for "a" (all); the docs (https://github.com/torvalds/linux/blob/v5.10/Documentation/admin-guide/cgroup-v1/devices.rst) describe: +// "'all' means it applies to all types and all major and minor numbers", and shows an example +// that *only* passes `a` as value: `echo a > /sys/fs/cgroup/1/devices.allow, which would be +// the "implicit" equivalent of "a *:* rwm". Source-code also looks to confirm this, and returns +// early for "a" (all); https://github.com/torvalds/linux/blob/v5.10/security/device_cgroup.c#L614-L642 +//nolint: gosimple +var deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\*) ([rwm]{1,3})$") + +// SetCapabilities sets the provided capabilities on the spec +// All capabilities are added if privileged is true +func SetCapabilities(s *specs.Spec, caplist []string) error { + s.Process.Capabilities.Effective = caplist + s.Process.Capabilities.Bounding = caplist + s.Process.Capabilities.Permitted = caplist + s.Process.Capabilities.Inheritable = caplist + // setUser has already been executed here + // if non root drop capabilities in the way execve does + if s.Process.User.UID != 0 { + s.Process.Capabilities.Effective = []string{} + s.Process.Capabilities.Permitted = []string{} + } + return nil +} + +// AppendDevicePermissionsFromCgroupRules takes rules for the devices cgroup to append to the default set +func AppendDevicePermissionsFromCgroupRules(devPermissions []specs.LinuxDeviceCgroup, rules []string) ([]specs.LinuxDeviceCgroup, error) { + for _, deviceCgroupRule := range rules { + ss := deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1) + if len(ss) == 0 || len(ss[0]) != 5 { + return nil, fmt.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule) + } + matches := ss[0] + + dPermissions := specs.LinuxDeviceCgroup{ + Allow: true, + Type: matches[1], + Access: matches[4], + } + if matches[2] == "*" { + major := int64(-1) + dPermissions.Major = &major + } else { + major, err := strconv.ParseInt(matches[2], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule) + } + dPermissions.Major = &major + } + if matches[3] == "*" { + minor := int64(-1) + dPermissions.Minor = &minor + } else { + minor, err := strconv.ParseInt(matches[3], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid minor value in device cgroup rule format: '%s'", deviceCgroupRule) + } + dPermissions.Minor = &minor + } + devPermissions = append(devPermissions, dPermissions) + } + return devPermissions, nil +} diff --git a/oci/oci_test.go b/oci/oci_test.go new file mode 100644 index 0000000000000..37bc7c202eb62 --- /dev/null +++ b/oci/oci_test.go @@ -0,0 +1,168 @@ +package oci + +import ( + "testing" + + "github.com/opencontainers/runtime-spec/specs-go" + "gotest.tools/v3/assert" +) + +func TestAppendDevicePermissionsFromCgroupRules(t *testing.T) { + ptr := func(i int64) *int64 { return &i } + + tests := []struct { + doc string + rule string + expected specs.LinuxDeviceCgroup + expectedErr string + }{ + { + doc: "empty rule", + rule: "", + expectedErr: `invalid device cgroup rule format: ''`, + }, + { + doc: "multiple spaces after first column", + rule: "c 1:1 rwm", + expectedErr: `invalid device cgroup rule format: 'c 1:1 rwm'`, + }, + { + doc: "multiple spaces after second column", + rule: "c 1:1 rwm", + expectedErr: `invalid device cgroup rule format: 'c 1:1 rwm'`, + }, + { + doc: "leading spaces", + rule: " c 1:1 rwm", + expectedErr: `invalid device cgroup rule format: ' c 1:1 rwm'`, + }, + { + doc: "trailing spaces", + rule: "c 1:1 rwm ", + expectedErr: `invalid device cgroup rule format: 'c 1:1 rwm '`, + }, + { + doc: "unknown device type", + rule: "z 1:1 rwm", + expectedErr: `invalid device cgroup rule format: 'z 1:1 rwm'`, + }, + { + doc: "invalid device type", + rule: "zz 1:1 rwm", + expectedErr: `invalid device cgroup rule format: 'zz 1:1 rwm'`, + }, + { + doc: "missing colon", + rule: "c 11 rwm", + expectedErr: `invalid device cgroup rule format: 'c 11 rwm'`, + }, + { + doc: "invalid device major-minor", + rule: "c a:a rwm", + expectedErr: `invalid device cgroup rule format: 'c a:a rwm'`, + }, + { + doc: "negative major device", + rule: "c -1:1 rwm", + expectedErr: `invalid device cgroup rule format: 'c -1:1 rwm'`, + }, + { + doc: "negative minor device", + rule: "c 1:-1 rwm", + expectedErr: `invalid device cgroup rule format: 'c 1:-1 rwm'`, + }, + { + doc: "missing permissions", + rule: "c 1:1", + expectedErr: `invalid device cgroup rule format: 'c 1:1'`, + }, + { + doc: "invalid permissions", + rule: "c 1:1 x", + expectedErr: `invalid device cgroup rule format: 'c 1:1 x'`, + }, + { + doc: "too many permissions", + rule: "c 1:1 rwmrwm", + expectedErr: `invalid device cgroup rule format: 'c 1:1 rwmrwm'`, + }, + { + doc: "major out of range", + rule: "c 18446744073709551616:1 rwm", + expectedErr: `invalid major value in device cgroup rule format: 'c 18446744073709551616:1 rwm'`, + }, + { + doc: "minor out of range", + rule: "c 1:18446744073709551616 rwm", + expectedErr: `invalid minor value in device cgroup rule format: 'c 1:18446744073709551616 rwm'`, + }, + { + doc: "all (a) devices", + rule: "a 1:1 rwm", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "a", Major: ptr(1), Minor: ptr(1), Access: "rwm"}, + }, + { + doc: "char (c) devices", + rule: "c 1:1 rwm", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: ptr(1), Minor: ptr(1), Access: "rwm"}, + }, + { + doc: "block (b) devices", + rule: "b 1:1 rwm", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "b", Major: ptr(1), Minor: ptr(1), Access: "rwm"}, + }, + { + doc: "char device with rwm permissions", + rule: "c 7:128 rwm", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: ptr(7), Minor: ptr(128), Access: "rwm"}, + }, + { + doc: "wildcard major", + rule: "c *:1 rwm", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: ptr(-1), Minor: ptr(1), Access: "rwm"}, + }, + { + doc: "wildcard minor", + rule: "c 1:* rwm", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: ptr(1), Minor: ptr(-1), Access: "rwm"}, + }, + { + doc: "wildcard major and minor", + rule: "c *:* rwm", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: ptr(-1), Minor: ptr(-1), Access: "rwm"}, + }, + { + doc: "read (r) permission", + rule: "c 1:1 r", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: ptr(1), Minor: ptr(1), Access: "r"}, + }, + { + doc: "write (w) permission", + rule: "c 1:1 w", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: ptr(1), Minor: ptr(1), Access: "w"}, + }, + { + doc: "mknod (m) permission", + rule: "c 1:1 m", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: ptr(1), Minor: ptr(1), Access: "m"}, + }, + { + doc: "mknod (m) and read (r) permission", + rule: "c 1:1 mr", + expected: specs.LinuxDeviceCgroup{Allow: true, Type: "c", Major: ptr(1), Minor: ptr(1), Access: "mr"}, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.doc, func(t *testing.T) { + out, err := AppendDevicePermissionsFromCgroupRules([]specs.LinuxDeviceCgroup{}, []string{tc.rule}) + if tc.expectedErr != "" { + assert.Error(t, err, tc.expectedErr) + return + } + assert.NilError(t, err) + assert.DeepEqual(t, out, []specs.LinuxDeviceCgroup{tc.expected}) + }) + } +} diff --git a/oci/seccomp_test.go b/oci/seccomp_test.go new file mode 100644 index 0000000000000..814cdaa7c0e32 --- /dev/null +++ b/oci/seccomp_test.go @@ -0,0 +1,40 @@ +//go:build linux +// +build linux + +package oci + +import ( + "encoding/json" + "os" + "testing" + + "github.com/docker/docker/profiles/seccomp" +) + +func TestSeccompLoadProfile(t *testing.T) { + profiles := []string{"default.json", "default-old-format.json", "example.json"} + + for _, p := range profiles { + t.Run(p, func(t *testing.T) { + f, err := os.ReadFile("fixtures/" + p) + if err != nil { + t.Fatal(err) + } + rs := DefaultLinuxSpec() + if _, err := seccomp.LoadProfile(string(f), &rs); err != nil { + t.Fatal(err) + } + }) + } +} + +func TestSeccompLoadDefaultProfile(t *testing.T) { + b, err := json.Marshal(seccomp.DefaultProfile()) + if err != nil { + t.Fatal(err) + } + rs := DefaultLinuxSpec() + if _, err := seccomp.LoadProfile(string(b), &rs); err != nil { + t.Fatal(err) + } +} diff --git a/opts/address_pools.go b/opts/address_pools.go index 9b27a62853c68..6274b35a871d3 100644 --- a/opts/address_pools.go +++ b/opts/address_pools.go @@ -7,17 +7,17 @@ import ( "strconv" "strings" - types "github.com/docker/libnetwork/ipamutils" + types "github.com/docker/docker/libnetwork/ipamutils" ) // PoolsOpt is a Value type for parsing the default address pools definitions type PoolsOpt struct { - values []*types.NetworkToSplit + Values []*types.NetworkToSplit } // UnmarshalJSON fills values structure info from JSON input func (p *PoolsOpt) UnmarshalJSON(raw []byte) error { - return json.Unmarshal(raw, &(p.values)) + return json.Unmarshal(raw, &(p.Values)) } // Set predefined pools @@ -53,7 +53,7 @@ func (p *PoolsOpt) Set(value string) error { } } - p.values = append(p.values, &poolsDef) + p.Values = append(p.Values, &poolsDef) return nil } @@ -66,7 +66,7 @@ func (p *PoolsOpt) Type() string { // String returns a string repr of this option func (p *PoolsOpt) String() string { var pools []string - for _, pool := range p.values { + for _, pool := range p.Values { repr := fmt.Sprintf("%s %d", pool.Base, pool.Size) pools = append(pools, repr) } @@ -75,7 +75,7 @@ func (p *PoolsOpt) String() string { // Value returns the mounts func (p *PoolsOpt) Value() []*types.NetworkToSplit { - return p.values + return p.Values } // Name returns the flag name of this option diff --git a/opts/env.go b/opts/env.go index f6e5e9074d670..97e1a8c8a26d8 100644 --- a/opts/env.go +++ b/opts/env.go @@ -1,48 +1,30 @@ package opts // import "github.com/docker/docker/opts" import ( - "fmt" "os" - "runtime" "strings" "github.com/pkg/errors" ) // ValidateEnv validates an environment variable and returns it. -// If no value is specified, it returns the current value using os.Getenv. +// If no value is specified, it obtains its value from the current environment // // As on ParseEnvFile and related to #16585, environment variable names -// are not validate what so ever, it's up to application inside docker +// are not validate whatsoever, it's up to application inside docker // to validate them or not. // // The only validation here is to check if name is empty, per #25099 func ValidateEnv(val string) (string, error) { - arr := strings.Split(val, "=") + arr := strings.SplitN(val, "=", 2) if arr[0] == "" { - return "", errors.Errorf("invalid environment variable: %s", val) + return "", errors.New("invalid environment variable: " + val) } if len(arr) > 1 { return val, nil } - if !doesEnvExist(val) { - return val, nil - } - return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil -} - -func doesEnvExist(name string) bool { - for _, entry := range os.Environ() { - parts := strings.SplitN(entry, "=", 2) - if runtime.GOOS == "windows" { - // Environment variable are case-insensitive on Windows. PaTh, path and PATH are equivalent. - if strings.EqualFold(parts[0], name) { - return true - } - } - if parts[0] == name { - return true - } + if envVal, ok := os.LookupEnv(arr[0]); ok { + return arr[0] + "=" + envVal, nil } - return false + return val, nil } diff --git a/opts/env_test.go b/opts/env_test.go index 1ecf1e2b945d6..df60408cc42a0 100644 --- a/opts/env_test.go +++ b/opts/env_test.go @@ -5,14 +5,17 @@ import ( "os" "runtime" "testing" + + "gotest.tools/v3/assert" ) func TestValidateEnv(t *testing.T) { - testcase := []struct { + type testCase struct { value string expected string err error - }{ + } + tests := []testCase{ { value: "a", expected: "a", @@ -51,7 +54,11 @@ func TestValidateEnv(t *testing.T) { }, { value: "=a", - err: fmt.Errorf(fmt.Sprintf("invalid environment variable: %s", "=a")), + err: fmt.Errorf("invalid environment variable: =a"), + }, + { + value: "PATH=", + expected: "PATH=", }, { value: "PATH=something", @@ -83,42 +90,30 @@ func TestValidateEnv(t *testing.T) { }, { value: "=", - err: fmt.Errorf(fmt.Sprintf("invalid environment variable: %s", "=")), + err: fmt.Errorf("invalid environment variable: ="), }, } - // Environment variables are case in-sensitive on Windows if runtime.GOOS == "windows" { - tmp := struct { - value string - expected string - err error - }{ + // Environment variables are case in-sensitive on Windows + tests = append(tests, testCase{ value: "PaTh", expected: fmt.Sprintf("PaTh=%v", os.Getenv("PATH")), - } - testcase = append(testcase, tmp) - + err: nil, + }) } - for _, r := range testcase { - actual, err := ValidateEnv(r.value) + for _, tc := range tests { + tc := tc + t.Run(tc.value, func(t *testing.T) { + actual, err := ValidateEnv(tc.value) - if err != nil { - if r.err == nil { - t.Fatalf("Expected err is nil, got err[%v]", err) - } - if err.Error() != r.err.Error() { - t.Fatalf("Expected err[%v], got err[%v]", r.err, err) + if tc.err == nil { + assert.NilError(t, err) + } else { + assert.Error(t, err, tc.err.Error()) } - } - - if err == nil && r.err != nil { - t.Fatalf("Expected err[%v], but err is nil", r.err) - } - - if actual != r.expected { - t.Fatalf("Expected [%v], got [%v]", r.expected, actual) - } + assert.Equal(t, actual, tc.expected) + }) } } diff --git a/opts/hosts.go b/opts/hosts.go index 2adf4211d5716..a3123adefe705 100644 --- a/opts/hosts.go +++ b/opts/hosts.go @@ -4,11 +4,14 @@ import ( "fmt" "net" "net/url" + "path/filepath" "strconv" "strings" + + "github.com/docker/docker/pkg/homedir" ) -var ( +const ( // DefaultHTTPPort Default HTTP Port used if only the protocol is provided to -H flag e.g. dockerd -H tcp:// // These are the IANA registered port numbers for use with Docker // see http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=docker @@ -19,11 +22,15 @@ var ( // Docker daemon by default always listens on the default unix socket DefaultUnixSocket = "/var/run/docker.sock" // DefaultTCPHost constant defines the default host string used by docker on Windows - DefaultTCPHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort) + DefaultTCPHost = "tcp://" + DefaultHTTPHost + ":2375" // DefaultTLSHost constant defines the default host string used by docker for TLS sockets - DefaultTLSHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultTLSHTTPPort) + DefaultTLSHost = "tcp://" + DefaultHTTPHost + ":2376" // DefaultNamedPipe defines the default named pipe used by docker on Windows DefaultNamedPipe = `//./pipe/docker_engine` + // HostGatewayName is the string value that can be passed + // to the IPAddr section in --add-host that is replaced by + // the value of HostGatewayIP daemon config value + HostGatewayName = "host-gateway" ) // ValidateHost validates that the specified string is a valid host and returns it. @@ -41,12 +48,20 @@ func ValidateHost(val string) (string, error) { return val, nil } -// ParseHost and set defaults for a Daemon host string -func ParseHost(defaultToTLS bool, val string) (string, error) { +// ParseHost and set defaults for a Daemon host string. +// defaultToTLS is preferred over defaultToUnixXDG. +func ParseHost(defaultToTLS, defaultToUnixXDG bool, val string) (string, error) { host := strings.TrimSpace(val) if host == "" { if defaultToTLS { host = DefaultTLSHost + } else if defaultToUnixXDG { + runtimeDir, err := homedir.GetRuntimeDir() + if err != nil { + return "", err + } + socket := filepath.Join(runtimeDir, "docker.sock") + host = "unix://" + socket } else { host = DefaultHost } @@ -158,8 +173,11 @@ func ValidateExtraHost(val string) (string, error) { if len(arr) != 2 || len(arr[0]) == 0 { return "", fmt.Errorf("bad format for add-host: %q", val) } - if _, err := ValidateIPAddress(arr[1]); err != nil { - return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1]) + // Skip IPaddr validation for special "host-gateway" string + if arr[1] != HostGatewayName { + if _, err := ValidateIPAddress(arr[1]); err != nil { + return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1]) + } } return val, nil } diff --git a/opts/hosts_test.go b/opts/hosts_test.go index e46326a5be3a8..7a0a943adf3cb 100644 --- a/opts/hosts_test.go +++ b/opts/hosts_test.go @@ -38,13 +38,13 @@ func TestParseHost(t *testing.T) { } for _, value := range invalid { - if _, err := ParseHost(false, value); err == nil { + if _, err := ParseHost(false, false, value); err == nil { t.Errorf("Expected an error for %v, got [nil]", value) } } for value, expected := range valid { - if actual, err := ParseHost(false, value); err != nil || actual != expected { + if actual, err := ParseHost(false, false, value); err != nil || actual != expected { t.Errorf("Expected for %v [%v], got [%v, %v]", value, expected, actual, err) } } @@ -53,8 +53,8 @@ func TestParseHost(t *testing.T) { func TestParseDockerDaemonHost(t *testing.T) { invalids := map[string]string{ - "tcp:a.b.c.d": "Invalid bind address format: tcp:a.b.c.d", - "tcp:a.b.c.d/path": "Invalid bind address format: tcp:a.b.c.d/path", + "tcp:a.b.c.d": "", + "tcp:a.b.c.d/path": "", "udp://127.0.0.1": "Invalid bind address format: udp://127.0.0.1", "udp://127.0.0.1:2375": "Invalid bind address format: udp://127.0.0.1:2375", "tcp://unix:///run/docker.sock": "Invalid proto, expected tcp: unix:///run/docker.sock", @@ -83,7 +83,7 @@ func TestParseDockerDaemonHost(t *testing.T) { "localhost:5555/path": "tcp://localhost:5555/path", } for invalidAddr, expectedError := range invalids { - if addr, err := parseDaemonHost(invalidAddr); err == nil || err.Error() != expectedError { + if addr, err := parseDaemonHost(invalidAddr); err == nil || expectedError != "" && err.Error() != expectedError { t.Errorf("tcp %v address expected error %q return, got %q and addr %v", invalidAddr, expectedError, err, addr) } } @@ -99,8 +99,8 @@ func TestParseTCP(t *testing.T) { defaultHTTPHost = "tcp://127.0.0.1:2376" ) invalids := map[string]string{ - "tcp:a.b.c.d": "Invalid bind address format: tcp:a.b.c.d", - "tcp:a.b.c.d/path": "Invalid bind address format: tcp:a.b.c.d/path", + "tcp:a.b.c.d": "", + "tcp:a.b.c.d/path": "", "udp://127.0.0.1": "Invalid proto, expected tcp: udp://127.0.0.1", "udp://127.0.0.1:2375": "Invalid proto, expected tcp: udp://127.0.0.1:2375", } @@ -125,7 +125,7 @@ func TestParseTCP(t *testing.T) { "localhost:5555/path": "tcp://localhost:5555/path", } for invalidAddr, expectedError := range invalids { - if addr, err := ParseTCPAddr(invalidAddr, defaultHTTPHost); err == nil || err.Error() != expectedError { + if addr, err := ParseTCPAddr(invalidAddr, defaultHTTPHost); err == nil || expectedError != "" && err.Error() != expectedError { t.Errorf("tcp %v address expected error %v return, got %s and addr %v", invalidAddr, expectedError, err, addr) } } diff --git a/opts/hosts_unix.go b/opts/hosts_unix.go index 9d5bb64565e5b..4b1c8512e238a 100644 --- a/opts/hosts_unix.go +++ b/opts/hosts_unix.go @@ -1,8 +1,12 @@ +//go:build !windows // +build !windows package opts // import "github.com/docker/docker/opts" -import "fmt" +const ( + // DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080 + DefaultHTTPHost = "localhost" -// DefaultHost constant defines the default host string used by docker on other hosts than Windows -var DefaultHost = fmt.Sprintf("unix://%s", DefaultUnixSocket) + // DefaultHost constant defines the default host string used by docker on other hosts than Windows + DefaultHost = "unix://" + DefaultUnixSocket +) diff --git a/opts/hosts_windows.go b/opts/hosts_windows.go index 906eba53ee2a7..576236ba42b0e 100644 --- a/opts/hosts_windows.go +++ b/opts/hosts_windows.go @@ -1,4 +1,60 @@ package opts // import "github.com/docker/docker/opts" -// DefaultHost constant defines the default host string used by docker on Windows -var DefaultHost = "npipe://" + DefaultNamedPipe +const ( + // TODO Windows. Identify bug in GOLang 1.5.1+ and/or Windows Server 2016 TP5. + // + // On Windows, this mitigates a problem with the default options of running + // a docker client against a local docker daemon on TP5. + // + // What was found that if the default host is "localhost", even if the client + // (and daemon as this is local) is not physically on a network, and the DNS + // cache is flushed (ipconfig /flushdns), then the client will pause for + // exactly one second when connecting to the daemon for calls. For example + // using docker run windowsservercore cmd, the CLI will send a create followed + // by an attach. You see the delay between the attach finishing and the attach + // being seen by the daemon. + // + // Here's some daemon debug logs with additional debug spew put in. The + // AfterWriteJSON log is the very last thing the daemon does as part of the + // create call. The POST /attach is the second CLI call. Notice the second + // time gap. + // + // time="2015-11-06T13:38:37.259627400-08:00" level=debug msg="After createRootfs" + // time="2015-11-06T13:38:37.263626300-08:00" level=debug msg="After setHostConfig" + // time="2015-11-06T13:38:37.267631200-08:00" level=debug msg="before createContainerPl...." + // time="2015-11-06T13:38:37.271629500-08:00" level=debug msg=ToDiskLocking.... + // time="2015-11-06T13:38:37.275643200-08:00" level=debug msg="loggin event...." + // time="2015-11-06T13:38:37.277627600-08:00" level=debug msg="logged event...." + // time="2015-11-06T13:38:37.279631800-08:00" level=debug msg="In defer func" + // time="2015-11-06T13:38:37.282628100-08:00" level=debug msg="After daemon.create" + // time="2015-11-06T13:38:37.286651700-08:00" level=debug msg="return 2" + // time="2015-11-06T13:38:37.289629500-08:00" level=debug msg="Returned from daemon.ContainerCreate" + // time="2015-11-06T13:38:37.311629100-08:00" level=debug msg="After WriteJSON" + // ... 1 second gap here.... + // time="2015-11-06T13:38:38.317866200-08:00" level=debug msg="Calling POST /v1.22/containers/984758282b842f779e805664b2c95d563adc9a979c8a3973e68c807843ee4757/attach" + // time="2015-11-06T13:38:38.326882500-08:00" level=info msg="POST /v1.22/containers/984758282b842f779e805664b2c95d563adc9a979c8a3973e68c807843ee4757/attach?stderr=1&stdin=1&stdout=1&stream=1" + // + // We suspect this is either a bug introduced in GOLang 1.5.1, or that a change + // in GOLang 1.5.1 (from 1.4.3) is exposing a bug in Windows. In theory, + // the Windows networking stack is supposed to resolve "localhost" internally, + // without hitting DNS, or even reading the hosts file (which is why localhost + // is commented out in the hosts file on Windows). + // + // We have validated that working around this using the actual IPv4 localhost + // address does not cause the delay. + // + // This does not occur with the docker client built with 1.4.3 on the same + // Windows build, regardless of whether the daemon is built using 1.5.1 + // or 1.4.3. It does not occur on Linux. We also verified we see the same thing + // on a cross-compiled Windows binary (from Linux). + // + // Final note: This is a mitigation, not a 'real' fix. It is still susceptible + // to the delay if a user were to do 'docker run -H=tcp://localhost:2375...' + // explicitly. + + // DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080 + DefaultHTTPHost = "127.0.0.1" + + // DefaultHost constant defines the default host string used by docker on Windows + DefaultHost = "npipe://" + DefaultNamedPipe +) diff --git a/opts/opts.go b/opts/opts.go index de8aacb806d7a..60a093f28c9e0 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -7,7 +7,7 @@ import ( "regexp" "strings" - "github.com/docker/go-units" + units "github.com/docker/go-units" ) var ( @@ -254,12 +254,23 @@ func validateDomain(val string) (string, error) { return "", fmt.Errorf("%s is not a valid domain", val) } -// ValidateLabel validates that the specified string is a valid label, and returns it. +// ValidateLabel validates that the specified string is a valid label, +// it does not use the reserved namespaces com.docker.*, io.docker.*, org.dockerproject.* +// and returns it. // Labels are in the form on key=value. func ValidateLabel(val string) (string, error) { if strings.Count(val, "=") < 1 { return "", fmt.Errorf("bad attribute format: %s", val) } + + lowered := strings.ToLower(val) + if strings.HasPrefix(lowered, "com.docker.") || strings.HasPrefix(lowered, "io.docker.") || + strings.HasPrefix(lowered, "org.dockerproject.") { + return "", fmt.Errorf( + "label %s is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use", + val) + } + return val, nil } diff --git a/opts/opts_test.go b/opts/opts_test.go index 577395edcb31a..2249cc10546a0 100644 --- a/opts/opts_test.go +++ b/opts/opts_test.go @@ -4,6 +4,9 @@ import ( "fmt" "strings" "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestValidateIPAddress(t *testing.T) { @@ -174,19 +177,94 @@ func TestValidateDNSSearch(t *testing.T) { } func TestValidateLabel(t *testing.T) { - if _, err := ValidateLabel("label"); err == nil || err.Error() != "bad attribute format: label" { - t.Fatalf("Expected an error [bad attribute format: label], go %v", err) - } - if actual, err := ValidateLabel("key1=value1"); err != nil || actual != "key1=value1" { - t.Fatalf("Expected [key1=value1], got [%v,%v]", actual, err) - } - // Validate it's working with more than one = - if actual, err := ValidateLabel("key1=value1=value2"); err != nil { - t.Fatalf("Expected [key1=value1=value2], got [%v,%v]", actual, err) + testCases := []struct { + name string + label string + expectedResult string + expectedErr string + }{ + { + name: "lable with bad attribute format", + label: "label", + expectedErr: "bad attribute format: label", + }, + { + name: "label with general format", + label: "key1=value1", + expectedResult: "key1=value1", + }, + { + name: "label with more than one =", + label: "key1=value1=value2", + expectedResult: "key1=value1=value2", + }, + { + name: "label with one more", + label: "key1=value1=value2=value3", + expectedResult: "key1=value1=value2=value3", + }, + { + name: "label with no reserved com.docker.*", + label: "com.dockerpsychnotreserved.label=value", + expectedResult: "com.dockerpsychnotreserved.label=value", + }, + { + name: "label with no reserved io.docker.*", + label: "io.dockerproject.not=reserved", + expectedResult: "io.dockerproject.not=reserved", + }, + { + name: "label with no reserved org.dockerproject.*", + label: "org.docker.not=reserved", + expectedResult: "org.docker.not=reserved", + }, + { + name: "label with reserved com.docker.*", + label: "com.docker.feature=enabled", + expectedErr: "label com.docker.feature=enabled is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use", + }, + { + name: "label with reserved upcase com.docker.* ", + label: "COM.docker.feature=enabled", + expectedErr: "label COM.docker.feature=enabled is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use", + }, + { + name: "label with reserved io.docker.*", + label: "io.docker.configuration=0", + expectedErr: "label io.docker.configuration=0 is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use", + }, + { + name: "label with reserved upcase io.docker.*", + label: "io.DOCKER.CONFIGURATion=0", + expectedErr: "label io.DOCKER.CONFIGURATion=0 is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use", + }, + { + name: "label with reserved org.dockerproject.*", + label: "org.dockerproject.setting=on", + expectedErr: "label org.dockerproject.setting=on is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use", + }, + { + name: "label with reserved upcase org.dockerproject.*", + label: "Org.Dockerproject.Setting=on", + expectedErr: "label Org.Dockerproject.Setting=on is not allowed: the namespaces com.docker.*, io.docker.*, and org.dockerproject.* are reserved for internal use", + }, } - // Validate it's working with one more - if actual, err := ValidateLabel("key1=value1=value2=value3"); err != nil { - t.Fatalf("Expected [key1=value1=value2=value2], got [%v,%v]", actual, err) + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.name, func(t *testing.T) { + result, err := ValidateLabel(testCase.label) + + if testCase.expectedErr != "" { + assert.Error(t, err, testCase.expectedErr) + } else { + assert.NilError(t, err) + } + if testCase.expectedResult != "" { + assert.Check(t, is.Equal(result, testCase.expectedResult)) + } + }) + } } diff --git a/opts/opts_unix.go b/opts/opts_unix.go deleted file mode 100644 index 0c32367cb22d7..0000000000000 --- a/opts/opts_unix.go +++ /dev/null @@ -1,6 +0,0 @@ -// +build !windows - -package opts // import "github.com/docker/docker/opts" - -// DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080 -const DefaultHTTPHost = "localhost" diff --git a/opts/opts_windows.go b/opts/opts_windows.go deleted file mode 100644 index 0e1b6c6d18e80..0000000000000 --- a/opts/opts_windows.go +++ /dev/null @@ -1,56 +0,0 @@ -package opts // import "github.com/docker/docker/opts" - -// TODO Windows. Identify bug in GOLang 1.5.1+ and/or Windows Server 2016 TP5. -// @jhowardmsft, @swernli. -// -// On Windows, this mitigates a problem with the default options of running -// a docker client against a local docker daemon on TP5. -// -// What was found that if the default host is "localhost", even if the client -// (and daemon as this is local) is not physically on a network, and the DNS -// cache is flushed (ipconfig /flushdns), then the client will pause for -// exactly one second when connecting to the daemon for calls. For example -// using docker run windowsservercore cmd, the CLI will send a create followed -// by an attach. You see the delay between the attach finishing and the attach -// being seen by the daemon. -// -// Here's some daemon debug logs with additional debug spew put in. The -// AfterWriteJSON log is the very last thing the daemon does as part of the -// create call. The POST /attach is the second CLI call. Notice the second -// time gap. -// -// time="2015-11-06T13:38:37.259627400-08:00" level=debug msg="After createRootfs" -// time="2015-11-06T13:38:37.263626300-08:00" level=debug msg="After setHostConfig" -// time="2015-11-06T13:38:37.267631200-08:00" level=debug msg="before createContainerPl...." -// time="2015-11-06T13:38:37.271629500-08:00" level=debug msg=ToDiskLocking.... -// time="2015-11-06T13:38:37.275643200-08:00" level=debug msg="loggin event...." -// time="2015-11-06T13:38:37.277627600-08:00" level=debug msg="logged event...." -// time="2015-11-06T13:38:37.279631800-08:00" level=debug msg="In defer func" -// time="2015-11-06T13:38:37.282628100-08:00" level=debug msg="After daemon.create" -// time="2015-11-06T13:38:37.286651700-08:00" level=debug msg="return 2" -// time="2015-11-06T13:38:37.289629500-08:00" level=debug msg="Returned from daemon.ContainerCreate" -// time="2015-11-06T13:38:37.311629100-08:00" level=debug msg="After WriteJSON" -// ... 1 second gap here.... -// time="2015-11-06T13:38:38.317866200-08:00" level=debug msg="Calling POST /v1.22/containers/984758282b842f779e805664b2c95d563adc9a979c8a3973e68c807843ee4757/attach" -// time="2015-11-06T13:38:38.326882500-08:00" level=info msg="POST /v1.22/containers/984758282b842f779e805664b2c95d563adc9a979c8a3973e68c807843ee4757/attach?stderr=1&stdin=1&stdout=1&stream=1" -// -// We suspect this is either a bug introduced in GOLang 1.5.1, or that a change -// in GOLang 1.5.1 (from 1.4.3) is exposing a bug in Windows. In theory, -// the Windows networking stack is supposed to resolve "localhost" internally, -// without hitting DNS, or even reading the hosts file (which is why localhost -// is commented out in the hosts file on Windows). -// -// We have validated that working around this using the actual IPv4 localhost -// address does not cause the delay. -// -// This does not occur with the docker client built with 1.4.3 on the same -// Windows build, regardless of whether the daemon is built using 1.5.1 -// or 1.4.3. It does not occur on Linux. We also verified we see the same thing -// on a cross-compiled Windows binary (from Linux). -// -// Final note: This is a mitigation, not a 'real' fix. It is still susceptible -// to the delay if a user were to do 'docker run -H=tcp://localhost:2375...' -// explicitly. - -// DefaultHTTPHost Default HTTP Host used if only port is provided to -H flag e.g. dockerd -H tcp://:8080 -const DefaultHTTPHost = "127.0.0.1" diff --git a/opts/quotedstring_test.go b/opts/quotedstring_test.go index 89fed6cfa6ac3..8ee603adace29 100644 --- a/opts/quotedstring_test.go +++ b/opts/quotedstring_test.go @@ -3,8 +3,8 @@ package opts // import "github.com/docker/docker/opts" import ( "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestQuotedStringSetWithQuotes(t *testing.T) { diff --git a/opts/ulimit.go b/opts/ulimit.go index 0e2a36236c1bf..61cc58d4d3293 100644 --- a/opts/ulimit.go +++ b/opts/ulimit.go @@ -3,7 +3,7 @@ package opts // import "github.com/docker/docker/opts" import ( "fmt" - "github.com/docker/go-units" + units "github.com/docker/go-units" ) // UlimitOpt defines a map of Ulimits diff --git a/opts/ulimit_test.go b/opts/ulimit_test.go index 41e12627c8a3d..90b15d61e87b0 100644 --- a/opts/ulimit_test.go +++ b/opts/ulimit_test.go @@ -3,12 +3,12 @@ package opts // import "github.com/docker/docker/opts" import ( "testing" - "github.com/docker/go-units" + units "github.com/docker/go-units" ) func TestUlimitOpt(t *testing.T) { ulimitMap := map[string]*units.Ulimit{ - "nofile": {"nofile", 1024, 512}, + "nofile": {Name: "nofile", Hard: 1024, Soft: 512}, } ulimitOpt := NewUlimitOpt(&ulimitMap) diff --git a/patches/0001-archive-tar-do-not-populate-user-group-names.patch b/patches/0001-archive-tar-do-not-populate-user-group-names.patch new file mode 100644 index 0000000000000..c5599b6102d66 --- /dev/null +++ b/patches/0001-archive-tar-do-not-populate-user-group-names.patch @@ -0,0 +1,75 @@ +From bc0de86b495ae014b209431ed7cb90fc3d5e4d1f Mon Sep 17 00:00:00 2001 +From: Kir Kolyshkin +Date: Mon, 9 Apr 2018 15:58:40 -0700 +Subject: [PATCH] archive/tar: do not populate user/group names + +This reverts part of commit 29a18899379c ("archive/tar: populate +uname/gname/devmajor/devminor in FileInfoHeader"). The reason is +using os/user functions to resolved uids/gids to names breaks +the static build for Linux/glibc case (the resulting binary panics +on NULL pointer dereference). + +For much more details, see https://github.com/golang/go/issues/23265 + +Signed-off-by: Kir Kolyshkin +--- + src/archive/tar/stat_unix.go | 26 +++----------------------- + 1 file changed, 3 insertions(+), 23 deletions(-) + +diff --git a/src/archive/tar/stat_unix.go b/src/archive/tar/stat_unix.go +index 868105f338..9640ed4bab 100644 +--- a/src/archive/tar/stat_unix.go ++++ b/src/archive/tar/stat_unix.go +@@ -8,10 +8,7 @@ package tar + + import ( + "io/fs" +- "os/user" + "runtime" +- "strconv" +- "sync" + "syscall" + ) + +@@ -19,10 +16,6 @@ func init() { + sysStat = statUnix + } + +-// userMap and groupMap caches UID and GID lookups for performance reasons. +-// The downside is that renaming uname or gname by the OS never takes effect. +-var userMap, groupMap sync.Map // map[int]string +- + func statUnix(fi fs.FileInfo, h *Header) error { + sys, ok := fi.Sys().(*syscall.Stat_t) + if !ok { +@@ -31,22 +24,9 @@ func statUnix(fi os.FileInfo, h *Header) error { + h.Uid = int(sys.Uid) + h.Gid = int(sys.Gid) + +- // Best effort at populating Uname and Gname. +- // The os/user functions may fail for any number of reasons +- // (not implemented on that platform, cgo not enabled, etc). +- if u, ok := userMap.Load(h.Uid); ok { +- h.Uname = u.(string) +- } else if u, err := user.LookupId(strconv.Itoa(h.Uid)); err == nil { +- h.Uname = u.Username +- userMap.Store(h.Uid, h.Uname) +- } +- if g, ok := groupMap.Load(h.Gid); ok { +- h.Gname = g.(string) +- } else if g, err := user.LookupGroupId(strconv.Itoa(h.Gid)); err == nil { +- h.Gname = g.Name +- groupMap.Store(h.Gid, h.Gname) +- } +- ++ // TODO(bradfitz): populate username & group. os/user ++ // doesn't cache LookupId lookups, and lacks group ++ // lookup functions. + h.AccessTime = statAtime(sys) + h.ChangeTime = statCtime(sys) + + +base-commit: 4af1337d1e9eb9e7b766c9deb787c78413bb25c4 +-- +2.24.1 + diff --git a/pkg/aaparser/aaparser.go b/pkg/aaparser/aaparser.go index 9c12e8db8d20d..2b5a2605f9c12 100644 --- a/pkg/aaparser/aaparser.go +++ b/pkg/aaparser/aaparser.go @@ -56,6 +56,11 @@ func parseVersion(output string) (int, error) { words := strings.Split(lines[0], " ") version := words[len(words)-1] + // trim "-beta1" suffix from version="3.0.0-beta1" if exists + version = strings.SplitN(version, "-", 2)[0] + // also trim "~..." suffix used historically (https://gitlab.com/apparmor/apparmor/-/commit/bca67d3d27d219d11ce8c9cc70612bd637f88c10) + version = strings.SplitN(version, "~", 2)[0] + // split by major minor version v := strings.Split(version, ".") if len(v) == 0 || len(v) > 3 { diff --git a/pkg/aaparser/aaparser_test.go b/pkg/aaparser/aaparser_test.go index 6d1f737702f4b..cf9280f5f56e1 100644 --- a/pkg/aaparser/aaparser_test.go +++ b/pkg/aaparser/aaparser_test.go @@ -43,6 +43,14 @@ Copyright 2009-2012 Canonical Ltd. `, version: 205000, }, + { + output: `AppArmor parser version 2.2.0~rc2 +Copyright (C) 1999-2008 Novell Inc. +Copyright 2009-2012 Canonical Ltd. + +`, + version: 202000, + }, { output: `AppArmor parser version 2.9.95 Copyright (C) 1999-2008 Novell Inc. @@ -59,6 +67,20 @@ Copyright 2009-2012 Canonical Ltd. `, version: 314159, }, + { + output: `AppArmor parser version 3.0.0-beta1 +Copyright (C) 1999-2008 Novell Inc. +Copyright 2009-2018 Canonical Ltd. +`, + version: 300000, + }, + { + output: `AppArmor parser version 3.0.0-beta1-foo-bar +Copyright (C) 1999-2008 Novell Inc. +Copyright 2009-2018 Canonical Ltd. +`, + version: 300000, + }, } for _, v := range versions { diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index 32132fcfed816..bf63d1727cf4d 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -7,11 +7,10 @@ import ( "compress/bzip2" "compress/gzip" "context" + "encoding/binary" "fmt" "io" - "io/ioutil" "os" - "os/exec" "path/filepath" "runtime" "strconv" @@ -24,20 +23,11 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/system" + "github.com/klauspost/compress/zstd" "github.com/sirupsen/logrus" + exec "golang.org/x/sys/execabs" ) -var unpigzPath string - -func init() { - if path, err := exec.LookPath("unpigz"); err != nil { - logrus.Debug("unpigz binary not found in PATH, falling back to go gzip library") - } else { - logrus.Debugf("Using unpigz binary found at path %s", path) - unpigzPath = path - } -} - type ( // Compression is the state represents if compressed or not. Compression int @@ -95,6 +85,8 @@ const ( Gzip // Xz is xz compression algorithm. Xz + // Zstd is zstd compression algorithm. + Zstd ) const ( @@ -133,18 +125,59 @@ func IsArchivePath(path string) bool { return err == nil } +const ( + zstdMagicSkippableStart = 0x184D2A50 + zstdMagicSkippableMask = 0xFFFFFFF0 +) + +var ( + bzip2Magic = []byte{0x42, 0x5A, 0x68} + gzipMagic = []byte{0x1F, 0x8B, 0x08} + xzMagic = []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00} + zstdMagic = []byte{0x28, 0xb5, 0x2f, 0xfd} +) + +type matcher = func([]byte) bool + +func magicNumberMatcher(m []byte) matcher { + return func(source []byte) bool { + return bytes.HasPrefix(source, m) + } +} + +// zstdMatcher detects zstd compression algorithm. +// Zstandard compressed data is made of one or more frames. +// There are two frame formats defined by Zstandard: Zstandard frames and Skippable frames. +// See https://tools.ietf.org/id/draft-kucherawy-dispatch-zstd-00.html#rfc.section.2 for more details. +func zstdMatcher() matcher { + return func(source []byte) bool { + if bytes.HasPrefix(source, zstdMagic) { + // Zstandard frame + return true + } + // skippable frame + if len(source) < 8 { + return false + } + // magic number from 0x184D2A50 to 0x184D2A5F. + if binary.LittleEndian.Uint32(source[:4])&zstdMagicSkippableMask == zstdMagicSkippableStart { + return true + } + return false + } +} + // DetectCompression detects the compression algorithm of the source. func DetectCompression(source []byte) Compression { - for compression, m := range map[Compression][]byte{ - Bzip2: {0x42, 0x5A, 0x68}, - Gzip: {0x1F, 0x8B, 0x08}, - Xz: {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, - } { - if len(source) < len(m) { - logrus.Debug("Len too short") - continue - } - if bytes.Equal(m, source[:len(m)]) { + compressionMap := map[Compression]matcher{ + Bzip2: magicNumberMatcher(bzip2Magic), + Gzip: magicNumberMatcher(gzipMagic), + Xz: magicNumberMatcher(xzMagic), + Zstd: zstdMatcher(), + } + for _, compression := range []Compression{Bzip2, Gzip, Xz, Zstd} { + fn := compressionMap[compression] + if fn(source) { return compression } } @@ -158,19 +191,25 @@ func xzDecompress(ctx context.Context, archive io.Reader) (io.ReadCloser, error) } func gzDecompress(ctx context.Context, buf io.Reader) (io.ReadCloser, error) { - if unpigzPath == "" { - return gzip.NewReader(buf) - } - - disablePigzEnv := os.Getenv("MOBY_DISABLE_PIGZ") - if disablePigzEnv != "" { - if disablePigz, err := strconv.ParseBool(disablePigzEnv); err != nil { - return nil, err - } else if disablePigz { + if noPigzEnv := os.Getenv("MOBY_DISABLE_PIGZ"); noPigzEnv != "" { + noPigz, err := strconv.ParseBool(noPigzEnv) + if err != nil { + logrus.WithError(err).Warn("invalid value in MOBY_DISABLE_PIGZ env var") + } + if noPigz { + logrus.Debugf("Use of pigz is disabled due to MOBY_DISABLE_PIGZ=%s", noPigzEnv) return gzip.NewReader(buf) } } + unpigzPath, err := exec.LookPath("unpigz") + if err != nil { + logrus.Debugf("unpigz binary not found, falling back to go gzip library") + return gzip.NewReader(buf) + } + + logrus.Debugf("Using %s to decompress", unpigzPath) + return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d", "-c"), buf) } @@ -225,6 +264,13 @@ func DecompressStream(archive io.Reader) (io.ReadCloser, error) { } readBufWrapper := p.NewReadCloserWrapper(buf, xzReader) return wrapReadCloser(readBufWrapper, cancel), nil + case Zstd: + zstdReader, err := zstd.NewReader(buf) + if err != nil { + return nil, err + } + readBufWrapper := p.NewReadCloserWrapper(buf, zstdReader) + return readBufWrapper, nil default: return nil, fmt.Errorf("Unsupported compression format %s", (&compression).Extension()) } @@ -278,7 +324,9 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi return nil } - header.Name = name + if header.Name == "" { + header.Name = name + } header.Size = int64(len(data)) if err := tarWriter.WriteHeader(header); err != nil { return err @@ -349,6 +397,8 @@ func (compression *Compression) Extension() string { return "tar.gz" case Xz: return "tar.xz" + case Zstd: + return "tar.zst" } return "" } @@ -402,10 +452,24 @@ func fillGo18FileTypeBits(mode int64, fi os.FileInfo) int64 { // ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem // to a tar header func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error { + const ( + // Values based on linux/include/uapi/linux/capability.h + xattrCapsSz2 = 20 + versionOffset = 3 + vfsCapRevision2 = 2 + vfsCapRevision3 = 3 + ) capability, _ := system.Lgetxattr(path, "security.capability") if capability != nil { + length := len(capability) + if capability[versionOffset] == vfsCapRevision3 { + // Convert VFS_CAP_REVISION_3 to VFS_CAP_REVISION_2 as root UID makes no + // sense outside the user namespace the archive is built in. + capability[versionOffset] = vfsCapRevision2 + length = xattrCapsSz2 + } hdr.Xattrs = make(map[string]string) - hdr.Xattrs["security.capability"] = string(capability) + hdr.Xattrs["security.capability"] = string(capability[:length]) } return nil } @@ -442,7 +506,7 @@ func newTarAppender(idMapping *idtools.IdentityMapping, writer io.Writer, chownO } // canonicalTarName provides a platform-independent and consistent posix-style -//path for files and directories to be archived regardless of the platform. +// path for files and directories to be archived regardless of the platform. func canonicalTarName(name string, isDir bool) string { name = CanonicalTarNameForPath(name) @@ -495,13 +559,13 @@ func (ta *tarAppender) addTarFile(path, name string) error { } } - //check whether the file is overlayfs whiteout - //if yes, skip re-mapping container ID mappings. + // check whether the file is overlayfs whiteout + // if yes, skip re-mapping container ID mappings. isOverlayWhiteout := fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 - //handle re-mapping container ID mappings back to host ID mappings before - //writing tar headers/files. We skip whiteout files because they were written - //by the kernel and already have proper ownership relative to the host + // handle re-mapping container ID mappings back to host ID mappings before + // writing tar headers/files. We skip whiteout files because they were written + // by the kernel and already have proper ownership relative to the host if !isOverlayWhiteout && !strings.HasPrefix(filepath.Base(hdr.Name), WhiteoutPrefix) && !ta.IdentityMapping.Empty() { fileIDPair, err := getFileUIDGID(fi.Sys()) if err != nil { @@ -739,13 +803,18 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) return nil, err } + whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS) + if err != nil { + return nil, err + } + go func() { ta := newTarAppender( idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps), compressWriter, options.ChownOpts, ) - ta.WhiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat) + ta.WhiteoutConverter = whiteoutConverter defer func() { // Make sure to check the error on Close. @@ -796,6 +865,11 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) for _, include := range options.IncludeFiles { rebaseName := options.RebaseNames[include] + var ( + parentMatchInfo []fileutils.MatchInfo + parentDirs []string + ) + walkRoot := getWalkRoot(srcPath, include) filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error { if err != nil { @@ -822,11 +896,30 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) // is asking for that file no matter what - which is true // for some files, like .dockerignore and Dockerfile (sometimes) if include != relFilePath { - skip, err = pm.Matches(relFilePath) + for len(parentDirs) != 0 { + lastParentDir := parentDirs[len(parentDirs)-1] + if strings.HasPrefix(relFilePath, lastParentDir+string(os.PathSeparator)) { + break + } + parentDirs = parentDirs[:len(parentDirs)-1] + parentMatchInfo = parentMatchInfo[:len(parentMatchInfo)-1] + } + + var matchInfo fileutils.MatchInfo + if len(parentMatchInfo) != 0 { + skip, matchInfo, err = pm.MatchesUsingParentResults(relFilePath, parentMatchInfo[len(parentMatchInfo)-1]) + } else { + skip, matchInfo, err = pm.MatchesUsingParentResults(relFilePath, fileutils.MatchInfo{}) + } if err != nil { logrus.Errorf("Error matching %s: %v", relFilePath, err) return err } + + if f.IsDir() { + parentDirs = append(parentDirs, relFilePath) + parentMatchInfo = append(parentMatchInfo, matchInfo) + } } if skip { @@ -903,7 +996,10 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err var dirs []*tar.Header idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) rootIDs := idMapping.RootPair() - whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat) + whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS) + if err != nil { + return err + } // Iterate through the files in the archive. loop: @@ -917,6 +1013,12 @@ loop: return err } + // ignore XGlobalHeader early to avoid creating parent directories for them + if hdr.Typeflag == tar.TypeXGlobalHeader { + logrus.Debugf("PAX Global Extended Headers found for %s and ignored", hdr.Name) + continue + } + // Normalize name, for safety and for a simple is-root check // This keeps "../" as-is, but normalizes "/../" to "/". Or Windows: // This keeps "..\" as-is, but normalizes "\..\" to "\". @@ -936,7 +1038,7 @@ loop: parent := filepath.Dir(hdr.Name) parentPath := filepath.Join(dest, parent) if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) { - err = idtools.MkdirAllAndChownNew(parentPath, 0777, rootIDs) + err = idtools.MkdirAllAndChownNew(parentPath, 0755, rootIDs) if err != nil { return err } @@ -1061,7 +1163,6 @@ func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decomp // TarUntar is a convenience function which calls Tar and Untar, with the output of one piped into the other. // If either Tar or Untar fails, TarUntar aborts and returns the error. func (archiver *Archiver) TarUntar(src, dst string) error { - logrus.Debugf("TarUntar(%s %s)", src, dst) archive, err := TarWithOptions(src, &TarOptions{Compression: Uncompressed}) if err != nil { return err @@ -1106,11 +1207,9 @@ func (archiver *Archiver) CopyWithTar(src, dst string) error { // as owner rootIDs := archiver.IDMapping.RootPair() // Create dst, copy src's content into it - logrus.Debugf("Creating dest directory: %s", dst) if err := idtools.MkdirAllAndChownNew(dst, 0755, rootIDs); err != nil { return err } - logrus.Debugf("Calling TarUntar(%s, %s)", src, dst) return archiver.TarUntar(src, dst) } @@ -1118,7 +1217,6 @@ func (archiver *Archiver) CopyWithTar(src, dst string) error { // for a single file. It copies a regular file from path `src` to // path `dst`, and preserves all its metadata. func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) { - logrus.Debugf("CopyFileWithTar(%s, %s)", src, dst) srcSt, err := os.Stat(src) if err != nil { return err @@ -1134,7 +1232,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) { dst = filepath.Join(dst, filepath.Base(src)) } // Create the holding directory if necessary - if err := system.MkdirAll(filepath.Dir(dst), 0700, ""); err != nil { + if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil { return err } @@ -1218,6 +1316,9 @@ func cmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, error) { return nil, err } + // Ensure the command has exited before we clean anything up + done := make(chan struct{}) + // Copy stdout to the returned pipe go func() { if err := cmd.Wait(); err != nil { @@ -1225,16 +1326,23 @@ func cmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, error) { } else { pipeW.Close() } + close(done) }() - return pipeR, nil + return ioutils.NewReadCloserWrapper(pipeR, func() error { + // Close pipeR, and then wait for the command to complete before returning. We have to close pipeR first, as + // cmd.Wait waits for any non-file stdout/stderr/stdin to close. + err := pipeR.Close() + <-done + return err + }), nil } // NewTempArchive reads the content of src into a temporary file, and returns the contents // of that file as an archive. The archive can only be read once - as soon as reading completes, // the file will be deleted. func NewTempArchive(src io.Reader, dir string) (*TempArchive, error) { - f, err := ioutil.TempFile(dir, "") + f, err := os.CreateTemp(dir, "") if err != nil { return nil, err } diff --git a/pkg/archive/archive_linux.go b/pkg/archive/archive_linux.go index 970d4d06800d9..0a3cc1f92bcc2 100644 --- a/pkg/archive/archive_linux.go +++ b/pkg/archive/archive_linux.go @@ -7,17 +7,22 @@ import ( "strings" "github.com/docker/docker/pkg/system" + "github.com/pkg/errors" "golang.org/x/sys/unix" ) -func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter { +func getWhiteoutConverter(format WhiteoutFormat, inUserNS bool) (tarWhiteoutConverter, error) { if format == OverlayWhiteoutFormat { - return overlayWhiteoutConverter{} + if inUserNS { + return nil, errors.New("specifying OverlayWhiteoutFormat is not allowed in userns") + } + return overlayWhiteoutConverter{}, nil } - return nil + return nil, nil } -type overlayWhiteoutConverter struct{} +type overlayWhiteoutConverter struct { +} func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) { // convert whiteouts to AUFS format @@ -61,13 +66,16 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os return } -func (overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) { +func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) { base := filepath.Base(path) dir := filepath.Dir(path) // if a directory is marked as opaque by the AUFS special file, we need to translate that to overlay if base == WhiteoutOpaqueDir { err := unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0) + if err != nil { + return false, errors.Wrapf(err, "setxattr(%q, trusted.overlay.opaque=y)", dir) + } // don't write the file itself return false, err } @@ -78,7 +86,7 @@ func (overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, originalPath := filepath.Join(dir, originalBase) if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil { - return false, err + return false, errors.Wrapf(err, "failed to mknod(%q, S_IFCHR, 0)", originalPath) } if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil { return false, err diff --git a/pkg/archive/archive_linux_test.go b/pkg/archive/archive_linux_test.go index 9422269dffe71..51a9a0f6d4393 100644 --- a/pkg/archive/archive_linux_test.go +++ b/pkg/archive/archive_linux_test.go @@ -1,16 +1,16 @@ package archive // import "github.com/docker/docker/pkg/archive" import ( - "io/ioutil" "os" "path/filepath" "syscall" "testing" + "github.com/containerd/containerd/pkg/userns" "github.com/docker/docker/pkg/system" "golang.org/x/sys/unix" - "gotest.tools/assert" - "gotest.tools/skip" + "gotest.tools/v3/assert" + "gotest.tools/v3/skip" ) // setupOverlayTestDir creates files in a directory with overlay whiteouts @@ -24,6 +24,7 @@ import ( // └── f1 # whiteout, 0644 func setupOverlayTestDir(t *testing.T, src string) { skip.If(t, os.Getuid() != 0, "skipping test that requires root") + skip.If(t, userns.RunningInUserNS(), "skipping test that requires initial userns (trusted.overlay.opaque xattr cannot be set in userns, even with Ubuntu kernel)") // Create opaque directory containing single file and permission 0700 err := os.Mkdir(filepath.Join(src, "d1"), 0700) assert.NilError(t, err) @@ -31,7 +32,7 @@ func setupOverlayTestDir(t *testing.T, src string) { err = system.Lsetxattr(filepath.Join(src, "d1"), "trusted.overlay.opaque", []byte("y"), 0) assert.NilError(t, err) - err = ioutil.WriteFile(filepath.Join(src, "d1", "f1"), []byte{}, 0600) + err = os.WriteFile(filepath.Join(src, "d1", "f1"), []byte{}, 0600) assert.NilError(t, err) // Create another opaque directory containing single file but with permission 0750 @@ -41,7 +42,7 @@ func setupOverlayTestDir(t *testing.T, src string) { err = system.Lsetxattr(filepath.Join(src, "d2"), "trusted.overlay.opaque", []byte("y"), 0) assert.NilError(t, err) - err = ioutil.WriteFile(filepath.Join(src, "d2", "f1"), []byte{}, 0660) + err = os.WriteFile(filepath.Join(src, "d2", "f1"), []byte{}, 0660) assert.NilError(t, err) // Create regular directory with deleted file @@ -89,13 +90,13 @@ func TestOverlayTarUntar(t *testing.T) { assert.NilError(t, err) defer system.Umask(oldmask) - src, err := ioutil.TempDir("", "docker-test-overlay-tar-src") + src, err := os.MkdirTemp("", "docker-test-overlay-tar-src") assert.NilError(t, err) defer os.RemoveAll(src) setupOverlayTestDir(t, src) - dst, err := ioutil.TempDir("", "docker-test-overlay-tar-dst") + dst, err := os.MkdirTemp("", "docker-test-overlay-tar-dst") assert.NilError(t, err) defer os.RemoveAll(dst) @@ -128,13 +129,13 @@ func TestOverlayTarAUFSUntar(t *testing.T) { assert.NilError(t, err) defer system.Umask(oldmask) - src, err := ioutil.TempDir("", "docker-test-overlay-tar-src") + src, err := os.MkdirTemp("", "docker-test-overlay-tar-src") assert.NilError(t, err) defer os.RemoveAll(src) setupOverlayTestDir(t, src) - dst, err := ioutil.TempDir("", "docker-test-overlay-tar-dst") + dst, err := os.MkdirTemp("", "docker-test-overlay-tar-dst") assert.NilError(t, err) defer os.RemoveAll(dst) diff --git a/pkg/archive/archive_other.go b/pkg/archive/archive_other.go index 462dfc6323254..28ae2769c5a8a 100644 --- a/pkg/archive/archive_other.go +++ b/pkg/archive/archive_other.go @@ -1,7 +1,8 @@ +//go:build !linux // +build !linux package archive // import "github.com/docker/docker/pkg/archive" -func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter { - return nil +func getWhiteoutConverter(format WhiteoutFormat, inUserNS bool) (tarWhiteoutConverter, error) { + return nil, nil } diff --git a/pkg/archive/archive_test.go b/pkg/archive/archive_test.go index b448bac49a197..a1b894be3c643 100644 --- a/pkg/archive/archive_test.go +++ b/pkg/archive/archive_test.go @@ -4,9 +4,9 @@ import ( "archive/tar" "bytes" "compress/gzip" + "errors" "fmt" "io" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -16,11 +16,12 @@ import ( "testing" "time" + "github.com/containerd/containerd/pkg/userns" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/ioutils" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/skip" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/skip" ) var tmp string @@ -109,7 +110,7 @@ func testDecompressStream(t *testing.T, ext, compressCommand string) io.Reader { if err != nil { t.Fatalf("Failed to decompress %s: %v", filename, err) } - if _, err = ioutil.ReadAll(r); err != nil { + if _, err = io.ReadAll(r); err != nil { t.Fatalf("Failed to read the decompressed stream: %v ", err) } if err = r.Close(); err != nil { @@ -134,6 +135,13 @@ func TestDecompressStreamXz(t *testing.T) { testDecompressStream(t, "xz", "xz -f") } +func TestDecompressStreamZstd(t *testing.T) { + if _, err := exec.LookPath("zstd"); err != nil { + t.Skip("zstd not installed") + } + testDecompressStream(t, "zst", "zstd -f") +} + func TestCompressStreamXzUnsupported(t *testing.T) { dest, err := os.Create(tmp + "dest") if err != nil { @@ -209,6 +217,13 @@ func TestExtensionXz(t *testing.T) { t.Fatalf("The extension of a xz archive should be 'tar.xz'") } } +func TestExtensionZstd(t *testing.T) { + compression := Zstd + output := compression.Extension() + if output != "tar.zst" { + t.Fatalf("The extension of a zstd archive should be 'tar.zst'") + } +} func TestCmdStreamLargeStderr(t *testing.T) { cmd := exec.Command("sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello") @@ -216,9 +231,9 @@ func TestCmdStreamLargeStderr(t *testing.T) { if err != nil { t.Fatalf("Failed to start command: %s", err) } - errCh := make(chan error) + errCh := make(chan error, 1) go func() { - _, err := io.Copy(ioutil.Discard, out) + _, err := io.Copy(io.Discard, out) errCh <- err }() select { @@ -241,7 +256,7 @@ func TestCmdStreamBad(t *testing.T) { if err != nil { t.Fatalf("Failed to start command: %s", err) } - if output, err := ioutil.ReadAll(out); err == nil { + if output, err := io.ReadAll(out); err == nil { t.Fatalf("Command should have failed") } else if err.Error() != "exit status 1: error couldn't reverse the phase pulser\n" { t.Fatalf("Wrong error value (%s)", err) @@ -256,7 +271,7 @@ func TestCmdStreamGood(t *testing.T) { if err != nil { t.Fatal(err) } - if output, err := ioutil.ReadAll(out); err != nil { + if output, err := io.ReadAll(out); err != nil { t.Fatalf("Command should not have failed (err=%s)", err) } else if s := string(output); s != "hello\n" { t.Fatalf("Command output should be '%s', not '%s'", "hello\\n", output) @@ -264,7 +279,7 @@ func TestCmdStreamGood(t *testing.T) { } func TestUntarPathWithInvalidDest(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-archive-test") + tempFolder, err := os.MkdirTemp("", "docker-archive-test") assert.NilError(t, err) defer os.RemoveAll(tempFolder) invalidDestFolder := filepath.Join(tempFolder, "invalidDest") @@ -293,7 +308,7 @@ func TestUntarPathWithInvalidDest(t *testing.T) { } func TestUntarPathWithInvalidSrc(t *testing.T) { - dest, err := ioutil.TempDir("", "docker-archive-test") + dest, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatalf("Fail to create the destination file") } @@ -305,8 +320,8 @@ func TestUntarPathWithInvalidSrc(t *testing.T) { } func TestUntarPath(t *testing.T) { - skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tmpFolder, err := ioutil.TempDir("", "docker-archive-test") + skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") + tmpFolder, err := os.MkdirTemp("", "docker-archive-test") assert.NilError(t, err) defer os.RemoveAll(tmpFolder) srcFile := filepath.Join(tmpFolder, "src") @@ -343,7 +358,7 @@ func TestUntarPath(t *testing.T) { // Do the same test as above but with the destination as file, it should fail func TestUntarPathWithDestinationFile(t *testing.T) { - tmpFolder, err := ioutil.TempDir("", "docker-archive-test") + tmpFolder, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatal(err) } @@ -379,7 +394,7 @@ func TestUntarPathWithDestinationFile(t *testing.T) { // and the destination file is a directory // It's working, see https://github.com/docker/docker/issues/10040 func TestUntarPathWithDestinationSrcFileAsFolder(t *testing.T) { - tmpFolder, err := ioutil.TempDir("", "docker-archive-test") + tmpFolder, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatal(err) } @@ -419,7 +434,7 @@ func TestUntarPathWithDestinationSrcFileAsFolder(t *testing.T) { } func TestCopyWithTarInvalidSrc(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-archive-test") + tempFolder, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatal(nil) } @@ -436,8 +451,8 @@ func TestCopyWithTarInvalidSrc(t *testing.T) { } func TestCopyWithTarInexistentDestWillCreateIt(t *testing.T) { - skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tempFolder, err := ioutil.TempDir("", "docker-archive-test") + skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") + tempFolder, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatal(nil) } @@ -459,7 +474,7 @@ func TestCopyWithTarInexistentDestWillCreateIt(t *testing.T) { // Test CopyWithTar with a file as src func TestCopyWithTarSrcFile(t *testing.T) { - folder, err := ioutil.TempDir("", "docker-archive-test") + folder, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatal(err) } @@ -475,7 +490,7 @@ func TestCopyWithTarSrcFile(t *testing.T) { if err != nil { t.Fatal(err) } - ioutil.WriteFile(src, []byte("content"), 0777) + os.WriteFile(src, []byte("content"), 0777) err = defaultCopyWithTar(src, dest) if err != nil { t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err) @@ -489,7 +504,7 @@ func TestCopyWithTarSrcFile(t *testing.T) { // Test CopyWithTar with a folder as src func TestCopyWithTarSrcFolder(t *testing.T) { - folder, err := ioutil.TempDir("", "docker-archive-test") + folder, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatal(err) } @@ -504,7 +519,7 @@ func TestCopyWithTarSrcFolder(t *testing.T) { if err != nil { t.Fatal(err) } - ioutil.WriteFile(filepath.Join(src, "file"), []byte("content"), 0777) + os.WriteFile(filepath.Join(src, "file"), []byte("content"), 0777) err = defaultCopyWithTar(src, dest) if err != nil { t.Fatalf("archiver.CopyWithTar shouldn't throw an error, %s.", err) @@ -517,7 +532,7 @@ func TestCopyWithTarSrcFolder(t *testing.T) { } func TestCopyFileWithTarInvalidSrc(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-archive-test") + tempFolder, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatal(err) } @@ -535,7 +550,7 @@ func TestCopyFileWithTarInvalidSrc(t *testing.T) { } func TestCopyFileWithTarInexistentDestWillCreateIt(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-archive-test") + tempFolder, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatal(nil) } @@ -558,7 +573,7 @@ func TestCopyFileWithTarInexistentDestWillCreateIt(t *testing.T) { } func TestCopyFileWithTarSrcFolder(t *testing.T) { - folder, err := ioutil.TempDir("", "docker-archive-copyfilewithtar-test") + folder, err := os.MkdirTemp("", "docker-archive-copyfilewithtar-test") if err != nil { t.Fatal(err) } @@ -580,7 +595,7 @@ func TestCopyFileWithTarSrcFolder(t *testing.T) { } func TestCopyFileWithTarSrcFile(t *testing.T) { - folder, err := ioutil.TempDir("", "docker-archive-test") + folder, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatal(err) } @@ -596,7 +611,7 @@ func TestCopyFileWithTarSrcFile(t *testing.T) { if err != nil { t.Fatal(err) } - ioutil.WriteFile(src, []byte("content"), 0777) + os.WriteFile(src, []byte("content"), 0777) err = defaultCopyWithTar(src, dest+"/") if err != nil { t.Fatalf("archiver.CopyFileWithTar shouldn't throw an error, %s.", err) @@ -608,10 +623,6 @@ func TestCopyFileWithTarSrcFile(t *testing.T) { } func TestTarFiles(t *testing.T) { - // TODO Windows: Figure out how to port this test. - if runtime.GOOS == "windows" { - t.Skip("Failing on Windows") - } // try without hardlinks if err := checkNoChanges(1000, false); err != nil { t.Fatal(err) @@ -623,13 +634,13 @@ func TestTarFiles(t *testing.T) { } func checkNoChanges(fileNum int, hardlinks bool) error { - srcDir, err := ioutil.TempDir("", "docker-test-srcDir") + srcDir, err := os.MkdirTemp("", "docker-test-srcDir") if err != nil { return err } defer os.RemoveAll(srcDir) - destDir, err := ioutil.TempDir("", "docker-test-destDir") + destDir, err := os.MkdirTemp("", "docker-test-destDir") if err != nil { return err } @@ -674,7 +685,7 @@ func tarUntar(t *testing.T, origin string, options *TarOptions) ([]Change, error return nil, fmt.Errorf("Wrong compression detected. Actual compression: %s, found %s", compression.Extension(), detectedCompression.Extension()) } - tmp, err := ioutil.TempDir("", "docker-test-untar") + tmp, err := os.MkdirTemp("", "docker-test-untar") if err != nil { return nil, err } @@ -689,23 +700,47 @@ func tarUntar(t *testing.T, origin string, options *TarOptions) ([]Change, error return ChangesDirs(origin, tmp) } -func TestTarUntar(t *testing.T) { - // TODO Windows: Figure out how to fix this test. - if runtime.GOOS == "windows" { - t.Skip("Failing on Windows") +func TestDetectCompressionZstd(t *testing.T) { + // test zstd compression without skippable frames. + compressedData := []byte{ + 0x28, 0xb5, 0x2f, 0xfd, // magic number of Zstandard frame: 0xFD2FB528 + 0x04, 0x00, 0x31, 0x00, 0x00, // frame header + 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, // data block "docker" + 0x16, 0x0e, 0x21, 0xc3, // content checksum + } + compression := DetectCompression(compressedData) + if compression != Zstd { + t.Fatal("Unexpected compression") + } + // test zstd compression with skippable frames. + hex := []byte{ + 0x50, 0x2a, 0x4d, 0x18, // magic number of skippable frame: 0x184D2A50 to 0x184D2A5F + 0x04, 0x00, 0x00, 0x00, // frame size + 0x5d, 0x00, 0x00, 0x00, // user data + 0x28, 0xb5, 0x2f, 0xfd, // magic number of Zstandard frame: 0xFD2FB528 + 0x04, 0x00, 0x31, 0x00, 0x00, // frame header + 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, // data block "docker" + 0x16, 0x0e, 0x21, 0xc3, // content checksum + } + compression = DetectCompression(hex) + if compression != Zstd { + t.Fatal("Unexpected compression") } - origin, err := ioutil.TempDir("", "docker-test-untar-origin") +} + +func TestTarUntar(t *testing.T) { + origin, err := os.MkdirTemp("", "docker-test-untar-origin") if err != nil { t.Fatal(err) } defer os.RemoveAll(origin) - if err := ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil { + if err := os.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil { t.Fatal(err) } - if err := ioutil.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { + if err := os.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { t.Fatal(err) } - if err := ioutil.WriteFile(filepath.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil { + if err := os.WriteFile(filepath.Join(origin, "3"), []byte("will be ignored"), 0700); err != nil { t.Fatal(err) } @@ -722,19 +757,19 @@ func TestTarUntar(t *testing.T) { t.Fatalf("Error tar/untar for compression %s: %s", c.Extension(), err) } - if len(changes) != 1 || changes[0].Path != "/3" { + if len(changes) != 1 || changes[0].Path != string(filepath.Separator)+"3" { t.Fatalf("Unexpected differences after tarUntar: %v", changes) } } } func TestTarWithOptionsChownOptsAlwaysOverridesIdPair(t *testing.T) { - origin, err := ioutil.TempDir("", "docker-test-tar-chown-opt") + origin, err := os.MkdirTemp("", "docker-test-tar-chown-opt") assert.NilError(t, err) defer os.RemoveAll(origin) filePath := filepath.Join(origin, "1") - err = ioutil.WriteFile(filePath, []byte("hello world"), 0700) + err = os.WriteFile(filePath, []byte("hello world"), 0700) assert.NilError(t, err) idMaps := []idtools.IDMap{ @@ -780,22 +815,18 @@ func TestTarWithOptionsChownOptsAlwaysOverridesIdPair(t *testing.T) { } func TestTarWithOptions(t *testing.T) { - // TODO Windows: Figure out how to fix this test. - if runtime.GOOS == "windows" { - t.Skip("Failing on Windows") - } - origin, err := ioutil.TempDir("", "docker-test-untar-origin") + origin, err := os.MkdirTemp("", "docker-test-untar-origin") if err != nil { t.Fatal(err) } - if _, err := ioutil.TempDir(origin, "folder"); err != nil { + if _, err := os.MkdirTemp(origin, "folder"); err != nil { t.Fatal(err) } defer os.RemoveAll(origin) - if err := ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil { + if err := os.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700); err != nil { t.Fatal(err) } - if err := ioutil.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { + if err := os.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700); err != nil { t.Fatal(err) } @@ -826,7 +857,7 @@ func TestTarWithOptions(t *testing.T) { // Failing prevents the archives from being uncompressed during ADD func TestTypeXGlobalHeaderDoesNotFail(t *testing.T) { hdr := tar.Header{Typeflag: tar.TypeXGlobalHeader} - tmpDir, err := ioutil.TempDir("", "docker-test-archive-pax-test") + tmpDir, err := os.MkdirTemp("", "docker-test-archive-pax-test") if err != nil { t.Fatal(err) } @@ -872,7 +903,7 @@ func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks fileData := []byte("fooo") for n := 0; n < numberOfFiles; n++ { fileName := fmt.Sprintf("file-%d", n) - if err := ioutil.WriteFile(filepath.Join(targetPath, fileName), fileData, 0700); err != nil { + if err := os.WriteFile(filepath.Join(targetPath, fileName), fileData, 0700); err != nil { return 0, err } if makeLinks { @@ -886,11 +917,11 @@ func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks } func BenchmarkTarUntar(b *testing.B) { - origin, err := ioutil.TempDir("", "docker-test-untar-origin") + origin, err := os.MkdirTemp("", "docker-test-untar-origin") if err != nil { b.Fatal(err) } - tempDir, err := ioutil.TempDir("", "docker-test-untar-destination") + tempDir, err := os.MkdirTemp("", "docker-test-untar-destination") if err != nil { b.Fatal(err) } @@ -914,11 +945,11 @@ func BenchmarkTarUntar(b *testing.B) { } func BenchmarkTarUntarWithLinks(b *testing.B) { - origin, err := ioutil.TempDir("", "docker-test-untar-origin") + origin, err := os.MkdirTemp("", "docker-test-untar-origin") if err != nil { b.Fatal(err) } - tempDir, err := ioutil.TempDir("", "docker-test-untar-destination") + tempDir, err := os.MkdirTemp("", "docker-test-untar-destination") if err != nil { b.Fatal(err) } @@ -942,10 +973,6 @@ func BenchmarkTarUntarWithLinks(b *testing.B) { } func TestUntarInvalidFilenames(t *testing.T) { - // TODO Windows: Figure out how to fix this test. - if runtime.GOOS == "windows" { - t.Skip("Passes but hits breakoutError: platform and architecture is not supported") - } for i, headers := range [][]*tar.Header{ { { @@ -970,9 +997,7 @@ func TestUntarInvalidFilenames(t *testing.T) { } func TestUntarHardlinkToSymlink(t *testing.T) { - // TODO Windows. There may be a way of running this, but turning off for now - skip.If(t, runtime.GOOS == "windows", "hardlinks on Windows") - skip.If(t, os.Getuid() != 0, "skipping test that requires root") + skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") for i, headers := range [][]*tar.Header{ { { @@ -1001,10 +1026,6 @@ func TestUntarHardlinkToSymlink(t *testing.T) { } func TestUntarInvalidHardlink(t *testing.T) { - // TODO Windows. There may be a way of running this, but turning off for now - if runtime.GOOS == "windows" { - t.Skip("hardlinks on Windows") - } for i, headers := range [][]*tar.Header{ { // try reading victim/hello (../) { @@ -1085,10 +1106,6 @@ func TestUntarInvalidHardlink(t *testing.T) { } func TestUntarInvalidSymlink(t *testing.T) { - // TODO Windows. There may be a way of running this, but turning off for now - if runtime.GOOS == "windows" { - t.Skip("hardlinks on Windows") - } for i, headers := range [][]*tar.Header{ { // try reading victim/hello (../) { @@ -1183,7 +1200,7 @@ func TestUntarInvalidSymlink(t *testing.T) { } func TestTempArchiveCloseMultipleTimes(t *testing.T) { - reader := ioutil.NopCloser(strings.NewReader("hello")) + reader := io.NopCloser(strings.NewReader("hello")) tempArchive, err := NewTempArchive(reader, "") assert.NilError(t, err) buf := make([]byte, 10) @@ -1199,6 +1216,26 @@ func TestTempArchiveCloseMultipleTimes(t *testing.T) { } } +// TestXGlobalNoParent is a regression test to check parent directories are not crated for PAX headers +func TestXGlobalNoParent(t *testing.T) { + buf := &bytes.Buffer{} + w := tar.NewWriter(buf) + err := w.WriteHeader(&tar.Header{ + Name: "foo/bar", + Typeflag: tar.TypeXGlobalHeader, + }) + assert.NilError(t, err) + tmpDir, err := os.MkdirTemp("", "pax-test") + assert.NilError(t, err) + defer os.RemoveAll(tmpDir) + err = Untar(buf, tmpDir, nil) + assert.NilError(t, err) + + _, err = os.Lstat(filepath.Join(tmpDir, "foo")) + assert.Check(t, err != nil) + assert.Check(t, errors.Is(err, os.ErrNotExist)) +} + func TestReplaceFileTarWrapper(t *testing.T) { filesInArchive := 20 testcases := []struct { @@ -1254,11 +1291,12 @@ func TestReplaceFileTarWrapper(t *testing.T) { // TestPrefixHeaderReadable tests that files that could be created with the // version of this package that was built with <=go17 are still readable. func TestPrefixHeaderReadable(t *testing.T) { - skip.If(t, os.Getuid() != 0, "skipping test that requires root") + skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") + skip.If(t, userns.RunningInUserNS(), "skipping test that requires more than 010000000 UIDs, which is unlikely to be satisfied when running in userns") // https://gist.github.com/stevvooe/e2a790ad4e97425896206c0816e1a882#file-out-go var testFile = []byte("\x1f\x8b\x08\x08\x44\x21\x68\x59\x00\x03\x74\x2e\x74\x61\x72\x00\x4b\xcb\xcf\x67\xa0\x35\x30\x80\x00\x86\x06\x10\x47\x01\xc1\x37\x40\x00\x54\xb6\xb1\xa1\xa9\x99\x09\x48\x25\x1d\x40\x69\x71\x49\x62\x91\x02\xe5\x76\xa1\x79\x84\x21\x91\xd6\x80\x72\xaf\x8f\x82\x51\x30\x0a\x46\x36\x00\x00\xf0\x1c\x1e\x95\x00\x06\x00\x00") - tmpDir, err := ioutil.TempDir("", "prefix-test") + tmpDir, err := os.MkdirTemp("", "prefix-test") assert.NilError(t, err) defer os.RemoveAll(tmpDir) err = Untar(bytes.NewReader(testFile), tmpDir, nil) @@ -1272,7 +1310,7 @@ func TestPrefixHeaderReadable(t *testing.T) { } func buildSourceArchive(t *testing.T, numberOfFiles int) (io.ReadCloser, func()) { - srcDir, err := ioutil.TempDir("", "docker-test-srcDir") + srcDir, err := os.MkdirTemp("", "docker-test-srcDir") assert.NilError(t, err) _, err = prepareUntarSourceDirectory(numberOfFiles, srcDir, false) @@ -1312,18 +1350,18 @@ func appendModifier(path string, header *tar.Header, content io.Reader) (*tar.He } func readFileFromArchive(t *testing.T, archive io.ReadCloser, name string, expectedCount int, doc string) string { - skip.If(t, os.Getuid() != 0, "skipping test that requires root") - destDir, err := ioutil.TempDir("", "docker-test-destDir") + skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") + destDir, err := os.MkdirTemp("", "docker-test-destDir") assert.NilError(t, err) defer os.RemoveAll(destDir) err = Untar(archive, destDir, nil) assert.NilError(t, err) - files, _ := ioutil.ReadDir(destDir) + files, _ := os.ReadDir(destDir) assert.Check(t, is.Len(files, expectedCount), doc) - content, err := ioutil.ReadFile(filepath.Join(destDir, name)) + content, err := os.ReadFile(filepath.Join(destDir, name)) assert.Check(t, err) return string(content) } @@ -1356,7 +1394,9 @@ func TestPigz(t *testing.T) { _, err := exec.LookPath("unpigz") if err == nil { t.Log("Tested whether Pigz is used, as it installed") - assert.Equal(t, reflect.TypeOf(contextReaderCloserWrapper.Reader), reflect.TypeOf(&io.PipeReader{})) + // For the command wait wrapper + cmdWaitCloserWrapper := contextReaderCloserWrapper.Reader.(*ioutils.ReadCloserWrapper) + assert.Equal(t, reflect.TypeOf(cmdWaitCloserWrapper.Reader), reflect.TypeOf(&io.PipeReader{})) } else { t.Log("Tested whether Pigz is not used, as it not installed") assert.Equal(t, reflect.TypeOf(contextReaderCloserWrapper.Reader), reflect.TypeOf(&gzip.Reader{})) diff --git a/pkg/archive/archive_unix.go b/pkg/archive/archive_unix.go index 1eec912b7b0b6..e3e4a5dcb5795 100644 --- a/pkg/archive/archive_unix.go +++ b/pkg/archive/archive_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package archive // import "github.com/docker/docker/pkg/archive" @@ -7,11 +8,12 @@ import ( "errors" "os" "path/filepath" + "strings" "syscall" + "github.com/containerd/containerd/pkg/userns" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/system" - rsystem "github.com/opencontainers/runc/libcontainer/system" "golang.org/x/sys/unix" ) @@ -26,7 +28,7 @@ func fixVolumePathPrefix(srcPath string) string { // can't use filepath.Join(srcPath,include) because this will clean away // a trailing "." or "/" which may be important. func getWalkRoot(srcPath string, include string) string { - return srcPath + string(filepath.Separator) + include + return strings.TrimSuffix(srcPath, string(filepath.Separator)) + string(filepath.Separator) + include } // CanonicalTarNameForPath returns platform-specific filepath @@ -50,8 +52,8 @@ func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) ( // Currently go does not fill in the major/minors if s.Mode&unix.S_IFBLK != 0 || s.Mode&unix.S_IFCHR != 0 { - hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) // nolint: unconvert - hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) // nolint: unconvert + hdr.Devmajor = int64(unix.Major(uint64(s.Rdev))) //nolint: unconvert + hdr.Devminor = int64(unix.Minor(uint64(s.Rdev))) //nolint: unconvert } } @@ -80,11 +82,6 @@ func getFileUIDGID(stat interface{}) (idtools.Identity, error) { // handleTarTypeBlockCharFifo is an OS-specific helper function used by // createTarFile to handle the following types of header: Block; Char; Fifo func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { - if rsystem.RunningInUserNS() { - // cannot create a device if running in user namespace - return nil - } - mode := uint32(hdr.Mode & 07777) switch hdr.Typeflag { case tar.TypeBlock: @@ -95,7 +92,12 @@ func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { mode |= unix.S_IFIFO } - return system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))) + err := system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))) + if errors.Is(err, syscall.EPERM) && userns.RunningInUserNS() { + // In most cases, cannot create a device if running in user namespace + err = nil + } + return err } func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error { diff --git a/pkg/archive/archive_unix_test.go b/pkg/archive/archive_unix_test.go index 83deab0840725..ac3833fd55b0f 100644 --- a/pkg/archive/archive_unix_test.go +++ b/pkg/archive/archive_unix_test.go @@ -1,22 +1,26 @@ +//go:build !windows // +build !windows package archive // import "github.com/docker/docker/pkg/archive" import ( + "archive/tar" "bytes" "fmt" - "io/ioutil" + "io" "os" + "os/exec" "path/filepath" "strings" "syscall" "testing" + "github.com/containerd/containerd/pkg/userns" "github.com/docker/docker/pkg/system" "golang.org/x/sys/unix" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/skip" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/skip" ) func TestCanonicalTarNameForPath(t *testing.T) { @@ -68,11 +72,11 @@ func TestChmodTarEntry(t *testing.T) { } func TestTarWithHardLink(t *testing.T) { - origin, err := ioutil.TempDir("", "docker-test-tar-hardlink") + origin, err := os.MkdirTemp("", "docker-test-tar-hardlink") assert.NilError(t, err) defer os.RemoveAll(origin) - err = ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700) + err = os.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700) assert.NilError(t, err) err = os.Link(filepath.Join(origin, "1"), filepath.Join(origin, "2")) @@ -87,7 +91,7 @@ func TestTarWithHardLink(t *testing.T) { t.Skipf("skipping since hardlinks don't work here; expected 2 links, got %d", i1) } - dest, err := ioutil.TempDir("", "docker-test-tar-hardlink-dest") + dest, err := os.MkdirTemp("", "docker-test-tar-hardlink-dest") assert.NilError(t, err) defer os.RemoveAll(dest) @@ -96,7 +100,7 @@ func TestTarWithHardLink(t *testing.T) { assert.NilError(t, err) // ensure we can read the whole thing with no error, before writing back out - buf, err := ioutil.ReadAll(fh) + buf, err := io.ReadAll(fh) assert.NilError(t, err) bRdr := bytes.NewReader(buf) @@ -113,7 +117,7 @@ func TestTarWithHardLink(t *testing.T) { } func TestTarWithHardLinkAndRebase(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "docker-test-tar-hardlink-rebase") + tmpDir, err := os.MkdirTemp("", "docker-test-tar-hardlink-rebase") assert.NilError(t, err) defer os.RemoveAll(tmpDir) @@ -121,7 +125,7 @@ func TestTarWithHardLinkAndRebase(t *testing.T) { err = os.Mkdir(origin, 0700) assert.NilError(t, err) - err = ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700) + err = os.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700) assert.NilError(t, err) err = os.Link(filepath.Join(origin, "1"), filepath.Join(origin, "2")) @@ -154,6 +158,25 @@ func TestTarWithHardLinkAndRebase(t *testing.T) { assert.Check(t, is.Equal(i1, i2)) } +// TestUntarParentPathPermissions is a regression test to check that missing +// parent directories are created with the expected permissions +func TestUntarParentPathPermissions(t *testing.T) { + skip.If(t, os.Getuid() != 0, "skipping test that requires root") + buf := &bytes.Buffer{} + w := tar.NewWriter(buf) + err := w.WriteHeader(&tar.Header{Name: "foo/bar"}) + assert.NilError(t, err) + tmpDir, err := os.MkdirTemp("", t.Name()) + assert.NilError(t, err) + defer os.RemoveAll(tmpDir) + err = Untar(buf, tmpDir, nil) + assert.NilError(t, err) + + fi, err := os.Lstat(filepath.Join(tmpDir, "foo")) + assert.NilError(t, err) + assert.Equal(t, fi.Mode(), 0755|os.ModeDir) +} + func getNlink(path string) (uint64, error) { stat, err := os.Stat(path) if err != nil { @@ -164,6 +187,7 @@ func getNlink(path string) (uint64, error) { return 0, fmt.Errorf("expected type *syscall.Stat_t, got %t", stat.Sys()) } // We need this conversion on ARM64 + //nolint: unconvert return uint64(statT.Nlink), nil } @@ -181,11 +205,12 @@ func getInode(path string) (uint64, error) { func TestTarWithBlockCharFifo(t *testing.T) { skip.If(t, os.Getuid() != 0, "skipping test that requires root") - origin, err := ioutil.TempDir("", "docker-test-tar-hardlink") + skip.If(t, userns.RunningInUserNS(), "skipping test that requires initial userns") + origin, err := os.MkdirTemp("", "docker-test-tar-hardlink") assert.NilError(t, err) defer os.RemoveAll(origin) - err = ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700) + err = os.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700) assert.NilError(t, err) err = system.Mknod(filepath.Join(origin, "2"), unix.S_IFBLK, int(system.Mkdev(int64(12), int64(5)))) @@ -195,7 +220,7 @@ func TestTarWithBlockCharFifo(t *testing.T) { err = system.Mknod(filepath.Join(origin, "4"), unix.S_IFIFO, int(system.Mkdev(int64(12), int64(5)))) assert.NilError(t, err) - dest, err := ioutil.TempDir("", "docker-test-tar-hardlink-dest") + dest, err := os.MkdirTemp("", "docker-test-tar-hardlink-dest") assert.NilError(t, err) defer os.RemoveAll(dest) @@ -204,7 +229,7 @@ func TestTarWithBlockCharFifo(t *testing.T) { assert.NilError(t, err) // ensure we can read the whole thing with no error, before writing back out - buf, err := ioutil.ReadAll(fh) + buf, err := io.ReadAll(fh) assert.NilError(t, err) bRdr := bytes.NewReader(buf) @@ -222,18 +247,26 @@ func TestTarWithBlockCharFifo(t *testing.T) { // TestTarUntarWithXattr is Unix as Lsetxattr is not supported on Windows func TestTarUntarWithXattr(t *testing.T) { skip.If(t, os.Getuid() != 0, "skipping test that requires root") - origin, err := ioutil.TempDir("", "docker-test-untar-origin") + if _, err := exec.LookPath("setcap"); err != nil { + t.Skip("setcap not installed") + } + if _, err := exec.LookPath("getcap"); err != nil { + t.Skip("getcap not installed") + } + + origin, err := os.MkdirTemp("", "docker-test-untar-origin") assert.NilError(t, err) defer os.RemoveAll(origin) - err = ioutil.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700) + err = os.WriteFile(filepath.Join(origin, "1"), []byte("hello world"), 0700) assert.NilError(t, err) - err = ioutil.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700) - assert.NilError(t, err) - err = ioutil.WriteFile(filepath.Join(origin, "3"), []byte("will be ignored"), 0700) + err = os.WriteFile(filepath.Join(origin, "2"), []byte("welcome!"), 0700) assert.NilError(t, err) - err = system.Lsetxattr(filepath.Join(origin, "2"), "security.capability", []byte{0x00}, 0) + err = os.WriteFile(filepath.Join(origin, "3"), []byte("will be ignored"), 0700) assert.NilError(t, err) + // there is no known Go implementation of setcap/getcap with support for v3 file capability + out, err := exec.Command("setcap", "cap_block_suspend+ep", filepath.Join(origin, "2")).CombinedOutput() + assert.NilError(t, err, string(out)) for _, c := range []Compression{ Uncompressed, @@ -251,10 +284,9 @@ func TestTarUntarWithXattr(t *testing.T) { if len(changes) != 1 || changes[0].Path != "/3" { t.Fatalf("Unexpected differences after tarUntar: %v", changes) } - capability, _ := system.Lgetxattr(filepath.Join(origin, "2"), "security.capability") - if capability == nil && capability[0] != 0x00 { - t.Fatalf("Untar should have kept the 'security.capability' xattr.") - } + out, err := exec.Command("getcap", filepath.Join(origin, "2")).CombinedOutput() + assert.NilError(t, err, string(out)) + assert.Check(t, is.Contains(string(out), "cap_block_suspend=ep"), "untar should have kept the 'security.capability' xattr") } } @@ -271,31 +303,31 @@ func TestCopyInfoDestinationPathSymlink(t *testing.T) { } testData := []FileTestData{ - //Create a directory: /tmp/archive-copy-test*/dir1 - //Test will "copy" file1 to dir1 + // Create a directory: /tmp/archive-copy-test*/dir1 + // Test will "copy" file1 to dir1 {resource: FileData{filetype: Dir, path: "dir1", permissions: 0740}, file: "file1", expected: CopyInfo{Path: root + "dir1/file1", Exists: false, IsDir: false}}, - //Create a symlink directory to dir1: /tmp/archive-copy-test*/dirSymlink -> dir1 - //Test will "copy" file2 to dirSymlink + // Create a symlink directory to dir1: /tmp/archive-copy-test*/dirSymlink -> dir1 + // Test will "copy" file2 to dirSymlink {resource: FileData{filetype: Symlink, path: "dirSymlink", contents: root + "dir1", permissions: 0600}, file: "file2", expected: CopyInfo{Path: root + "dirSymlink/file2", Exists: false, IsDir: false}}, - //Create a file in tmp directory: /tmp/archive-copy-test*/file1 - //Test to cover when the full file path already exists. + // Create a file in tmp directory: /tmp/archive-copy-test*/file1 + // Test to cover when the full file path already exists. {resource: FileData{filetype: Regular, path: "file1", permissions: 0600}, file: "", expected: CopyInfo{Path: root + "file1", Exists: true}}, - //Create a directory: /tmp/archive-copy*/dir2 - //Test to cover when the full directory path already exists + // Create a directory: /tmp/archive-copy*/dir2 + // Test to cover when the full directory path already exists {resource: FileData{filetype: Dir, path: "dir2", permissions: 0740}, file: "", expected: CopyInfo{Path: root + "dir2", Exists: true, IsDir: true}}, - //Create a symlink to a non-existent target: /tmp/archive-copy*/symlink1 -> noSuchTarget - //Negative test to cover symlinking to a target that does not exit + // Create a symlink to a non-existent target: /tmp/archive-copy*/symlink1 -> noSuchTarget + // Negative test to cover symlinking to a target that does not exit {resource: FileData{filetype: Symlink, path: "symlink1", contents: "noSuchTarget", permissions: 0600}, file: "", expected: CopyInfo{Path: root + "noSuchTarget", Exists: false}}, - //Create a file in tmp directory for next test: /tmp/existingfile + // Create a file in tmp directory for next test: /tmp/existingfile {resource: FileData{filetype: Regular, path: "existingfile", permissions: 0600}, file: "", expected: CopyInfo{Path: root + "existingfile", Exists: true}}, - //Create a symlink to an existing file: /tmp/archive-copy*/symlink2 -> /tmp/existingfile - //Test to cover when the parent directory of a new file is a symlink + // Create a symlink to an existing file: /tmp/archive-copy*/symlink2 -> /tmp/existingfile + // Test to cover when the parent directory of a new file is a symlink {resource: FileData{filetype: Symlink, path: "symlink2", contents: "existingfile", permissions: 0600}, file: "", expected: CopyInfo{Path: root + "existingfile", Exists: true}}, } diff --git a/pkg/archive/archive_windows.go b/pkg/archive/archive_windows.go index ae6b89fd719c4..7260174bfbb73 100644 --- a/pkg/archive/archive_windows.go +++ b/pkg/archive/archive_windows.go @@ -31,7 +31,7 @@ func CanonicalTarNameForPath(p string) string { // chmodTarEntry is used to adjust the file permissions used in tar header based // on the platform the archival is done. func chmodTarEntry(perm os.FileMode) os.FileMode { - //perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.) + // perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.) permPart := perm & os.ModePerm noPermPart := perm &^ os.ModePerm // Add the x bit: make everything +x from windows diff --git a/pkg/archive/archive_windows_test.go b/pkg/archive/archive_windows_test.go index 6f0e25ccac534..f03b1e2d827d1 100644 --- a/pkg/archive/archive_windows_test.go +++ b/pkg/archive/archive_windows_test.go @@ -1,9 +1,9 @@ +//go:build windows // +build windows package archive // import "github.com/docker/docker/pkg/archive" import ( - "io/ioutil" "os" "path/filepath" "testing" @@ -14,7 +14,7 @@ func TestCopyFileWithInvalidDest(t *testing.T) { // recently changed in CopyWithTar as used to pass. Further investigation // is required. t.Skip("Currently fails") - folder, err := ioutil.TempDir("", "docker-archive-test") + folder, err := os.MkdirTemp("", "docker-archive-test") if err != nil { t.Fatal(err) } @@ -26,7 +26,7 @@ func TestCopyFileWithInvalidDest(t *testing.T) { if err != nil { t.Fatal(err) } - ioutil.WriteFile(src, []byte("content"), 0777) + os.WriteFile(src, []byte("content"), 0777) err = defaultCopyWithTar(src, dest) if err == nil { t.Fatalf("archiver.CopyWithTar should throw an error on invalid dest.") diff --git a/pkg/archive/changes.go b/pkg/archive/changes.go index 43734db5b117f..a0f25942c14f1 100644 --- a/pkg/archive/changes.go +++ b/pkg/archive/changes.go @@ -5,7 +5,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "os" "path/filepath" "sort" @@ -63,12 +62,16 @@ func (c changesByPath) Less(i, j int) bool { return c[i].Path < c[j].Path } func (c changesByPath) Len() int { return len(c) } func (c changesByPath) Swap(i, j int) { c[j], c[i] = c[i], c[j] } -// Gnu tar and the go tar writer don't have sub-second mtime -// precision, which is problematic when we apply changes via tar -// files, we handle this by comparing for exact times, *or* same +// Gnu tar doesn't have sub-second mtime precision. The go tar +// writer (1.10+) does when using PAX format, but we round times to seconds +// to ensure archives have the same hashes for backwards compatibility. +// See https://github.com/moby/moby/pull/35739/commits/fb170206ba12752214630b269a40ac7be6115ed4. +// +// Non-sub-second is problematic when we apply changes via tar +// files. We handle this by comparing for exact times, *or* same // second count and either a or b having exactly 0 nanoseconds func sameFsTime(a, b time.Time) bool { - return a == b || + return a.Equal(b) || (a.Unix() == b.Unix() && (a.Nanosecond() == 0 || b.Nanosecond() == 0)) } @@ -344,7 +347,7 @@ func ChangesDirs(newDir, oldDir string) ([]Change, error) { oldRoot, newRoot *FileInfo ) if oldDir == "" { - emptyDir, err := ioutil.TempDir("", "empty") + emptyDir, err := os.MkdirTemp("", "empty") if err != nil { return nil, err } diff --git a/pkg/archive/changes_other.go b/pkg/archive/changes_other.go index ba744741cd02b..0e4399a43b26e 100644 --- a/pkg/archive/changes_other.go +++ b/pkg/archive/changes_other.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package archive // import "github.com/docker/docker/pkg/archive" diff --git a/pkg/archive/changes_posix_test.go b/pkg/archive/changes_posix_test.go index 019a0250f3b8f..4c71cb2575d4b 100644 --- a/pkg/archive/changes_posix_test.go +++ b/pkg/archive/changes_posix_test.go @@ -4,7 +4,6 @@ import ( "archive/tar" "fmt" "io" - "io/ioutil" "os" "path" "sort" @@ -16,7 +15,7 @@ func TestHardLinkOrder(t *testing.T) { msg := []byte("Hey y'all") // Create dir - src, err := ioutil.TempDir("", "docker-hardlink-test-src-") + src, err := os.MkdirTemp("", "docker-hardlink-test-src-") if err != nil { t.Fatal(err) } @@ -34,7 +33,7 @@ func TestHardLinkOrder(t *testing.T) { }() } // Create dest, with changes that includes hardlinks - dest, err := ioutil.TempDir("", "docker-hardlink-test-dest-") + dest, err := os.MkdirTemp("", "docker-hardlink-test-dest-") if err != nil { t.Fatal(err) } diff --git a/pkg/archive/changes_test.go b/pkg/archive/changes_test.go index f2527cd93650f..81b87aa12b01f 100644 --- a/pkg/archive/changes_test.go +++ b/pkg/archive/changes_test.go @@ -1,18 +1,23 @@ package archive // import "github.com/docker/docker/pkg/archive" import ( - "io/ioutil" "os" "os/exec" "path" + "path/filepath" "runtime" "sort" + "strconv" + "strings" + "syscall" "testing" "time" + "github.com/Microsoft/hcsshim/osversion" + "github.com/docker/docker/pkg/parsers/kernel" "github.com/docker/docker/pkg/system" - "gotest.tools/assert" - "gotest.tools/skip" + "gotest.tools/v3/assert" + "gotest.tools/v3/skip" ) func max(x, y int) int { @@ -23,7 +28,24 @@ func max(x, y int) int { } func copyDir(src, dst string) error { - return exec.Command("cp", "-a", src, dst).Run() + if runtime.GOOS != "windows" { + return exec.Command("cp", "-a", src, dst).Run() + } + + // Could have used xcopy src dst /E /I /H /Y /B. However, xcopy has the + // unfortunate side effect of not preserving timestamps of newly created + // directories in the target directory, so we don't get accurate changes. + // Use robocopy instead. Note this isn't available in microsoft/nanoserver. + // But it has gotchas. See https://weblogs.sqlteam.com/robv/archive/2010/02/17/61106.aspx + err := exec.Command("robocopy", filepath.FromSlash(src), filepath.FromSlash(dst), "/SL", "/COPYALL", "/MIR").Run() + if exiterr, ok := err.(*exec.ExitError); ok { + if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { + if status.ExitStatus()&24 == 0 { + return nil + } + } + } + return err } type FileType uint32 @@ -79,7 +101,7 @@ func provisionSampleDir(t *testing.T, root string, files []FileData) { err := os.MkdirAll(p, info.permissions) assert.NilError(t, err) } else if info.filetype == Regular { - err := ioutil.WriteFile(p, []byte(info.contents), info.permissions) + err := os.WriteFile(p, []byte(info.contents), info.permissions) assert.NilError(t, err) } else if info.filetype == Symlink { err := os.Symlink(info.contents, p) @@ -113,15 +135,10 @@ func TestChangeString(t *testing.T) { } func TestChangesWithNoChanges(t *testing.T) { - // TODO Windows. There may be a way of running this, but turning off for now - // as createSampleDir uses symlinks. - if runtime.GOOS == "windows" { - t.Skip("symlinks on Windows") - } - rwLayer, err := ioutil.TempDir("", "docker-changes-test") + rwLayer, err := os.MkdirTemp("", "docker-changes-test") assert.NilError(t, err) defer os.RemoveAll(rwLayer) - layer, err := ioutil.TempDir("", "docker-changes-test-layer") + layer, err := os.MkdirTemp("", "docker-changes-test-layer") assert.NilError(t, err) defer os.RemoveAll(layer) createSampleDir(t, layer) @@ -133,20 +150,15 @@ func TestChangesWithNoChanges(t *testing.T) { } func TestChangesWithChanges(t *testing.T) { - // TODO Windows. There may be a way of running this, but turning off for now - // as createSampleDir uses symlinks. - if runtime.GOOS == "windows" { - t.Skip("symlinks on Windows") - } // Mock the readonly layer - layer, err := ioutil.TempDir("", "docker-changes-test-layer") + layer, err := os.MkdirTemp("", "docker-changes-test-layer") assert.NilError(t, err) defer os.RemoveAll(layer) createSampleDir(t, layer) os.MkdirAll(path.Join(layer, "dir1/subfolder"), 0740) // Mock the RW layer - rwLayer, err := ioutil.TempDir("", "docker-changes-test") + rwLayer, err := os.MkdirTemp("", "docker-changes-test") assert.NilError(t, err) defer os.RemoveAll(rwLayer) @@ -154,36 +166,35 @@ func TestChangesWithChanges(t *testing.T) { dir1 := path.Join(rwLayer, "dir1") os.MkdirAll(dir1, 0740) deletedFile := path.Join(dir1, ".wh.file1-2") - ioutil.WriteFile(deletedFile, []byte{}, 0600) + os.WriteFile(deletedFile, []byte{}, 0600) modifiedFile := path.Join(dir1, "file1-1") - ioutil.WriteFile(modifiedFile, []byte{0x00}, 01444) + os.WriteFile(modifiedFile, []byte{0x00}, 01444) // Let's add a subfolder for a newFile subfolder := path.Join(dir1, "subfolder") os.MkdirAll(subfolder, 0740) newFile := path.Join(subfolder, "newFile") - ioutil.WriteFile(newFile, []byte{}, 0740) + os.WriteFile(newFile, []byte{}, 0740) changes, err := Changes([]string{layer}, rwLayer) assert.NilError(t, err) expectedChanges := []Change{ - {"/dir1", ChangeModify}, - {"/dir1/file1-1", ChangeModify}, - {"/dir1/file1-2", ChangeDelete}, - {"/dir1/subfolder", ChangeModify}, - {"/dir1/subfolder/newFile", ChangeAdd}, + {filepath.FromSlash("/dir1"), ChangeModify}, + {filepath.FromSlash("/dir1/file1-1"), ChangeModify}, + {filepath.FromSlash("/dir1/file1-2"), ChangeDelete}, + {filepath.FromSlash("/dir1/subfolder"), ChangeModify}, + {filepath.FromSlash("/dir1/subfolder/newFile"), ChangeAdd}, } checkChanges(expectedChanges, changes, t) } // See https://github.com/docker/docker/pull/13590 func TestChangesWithChangesGH13590(t *testing.T) { - // TODO Windows. There may be a way of running this, but turning off for now - // as createSampleDir uses symlinks. + // TODO Windows. Needs further investigation to identify the failure if runtime.GOOS == "windows" { - t.Skip("symlinks on Windows") + t.Skip("needs more investigation") } - baseLayer, err := ioutil.TempDir("", "docker-changes-test.") + baseLayer, err := os.MkdirTemp("", "docker-changes-test.") assert.NilError(t, err) defer os.RemoveAll(baseLayer) @@ -191,9 +202,9 @@ func TestChangesWithChangesGH13590(t *testing.T) { os.MkdirAll(dir3, 07400) file := path.Join(dir3, "file.txt") - ioutil.WriteFile(file, []byte("hello"), 0666) + os.WriteFile(file, []byte("hello"), 0666) - layer, err := ioutil.TempDir("", "docker-changes-test2.") + layer, err := os.MkdirTemp("", "docker-changes-test2.") assert.NilError(t, err) defer os.RemoveAll(layer) @@ -204,7 +215,7 @@ func TestChangesWithChangesGH13590(t *testing.T) { os.Remove(path.Join(layer, "dir1/dir2/dir3/file.txt")) file = path.Join(layer, "dir1/dir2/dir3/file1.txt") - ioutil.WriteFile(file, []byte("bye"), 0666) + os.WriteFile(file, []byte("bye"), 0666) changes, err := Changes([]string{baseLayer}, layer) assert.NilError(t, err) @@ -216,7 +227,7 @@ func TestChangesWithChangesGH13590(t *testing.T) { checkChanges(expectedChanges, changes, t) // Now test changing a file - layer, err = ioutil.TempDir("", "docker-changes-test3.") + layer, err = os.MkdirTemp("", "docker-changes-test3.") assert.NilError(t, err) defer os.RemoveAll(layer) @@ -225,7 +236,7 @@ func TestChangesWithChangesGH13590(t *testing.T) { } file = path.Join(layer, "dir1/dir2/dir3/file.txt") - ioutil.WriteFile(file, []byte("bye"), 0666) + os.WriteFile(file, []byte("bye"), 0666) changes, err = Changes([]string{baseLayer}, layer) assert.NilError(t, err) @@ -238,12 +249,19 @@ func TestChangesWithChangesGH13590(t *testing.T) { // Create a directory, copy it, make sure we report no changes between the two func TestChangesDirsEmpty(t *testing.T) { - // TODO Windows. There may be a way of running this, but turning off for now - // as createSampleDir uses symlinks. + // Note we parse kernel.GetKernelVersion rather than system.GetOSVersion + // as test binaries aren't manifested, so would otherwise report the wrong + // build number. if runtime.GOOS == "windows" { - t.Skip("symlinks on Windows") + v, err := kernel.GetKernelVersion() + assert.NilError(t, err) + build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0]) + if build >= osversion.V19H1 { + t.Skip("FIXME: broken on Windows 1903 and up; see #39846") + } } - src, err := ioutil.TempDir("", "docker-changes-test") + + src, err := os.MkdirTemp("", "docker-changes-test") assert.NilError(t, err) defer os.RemoveAll(src) createSampleDir(t, src) @@ -275,13 +293,13 @@ func mutateSampleDir(t *testing.T, root string) { assert.NilError(t, err) // Rewrite a file - err = ioutil.WriteFile(path.Join(root, "file2"), []byte("fileNN\n"), 0777) + err = os.WriteFile(path.Join(root, "file2"), []byte("fileNN\n"), 0777) assert.NilError(t, err) // Replace a file err = os.RemoveAll(path.Join(root, "file3")) assert.NilError(t, err) - err = ioutil.WriteFile(path.Join(root, "file3"), []byte("fileMM\n"), 0404) + err = os.WriteFile(path.Join(root, "file3"), []byte("fileMM\n"), 0404) assert.NilError(t, err) // Touch file @@ -295,7 +313,7 @@ func mutateSampleDir(t *testing.T, root string) { assert.NilError(t, err) // Create new file - err = ioutil.WriteFile(path.Join(root, "filenew"), []byte("filenew\n"), 0777) + err = os.WriteFile(path.Join(root, "filenew"), []byte("filenew\n"), 0777) assert.NilError(t, err) // Create new dir @@ -316,7 +334,7 @@ func mutateSampleDir(t *testing.T, root string) { // Replace dir with file err = os.RemoveAll(path.Join(root, "dir2")) assert.NilError(t, err) - err = ioutil.WriteFile(path.Join(root, "dir2"), []byte("dir2\n"), 0777) + err = os.WriteFile(path.Join(root, "dir2"), []byte("dir2\n"), 0777) assert.NilError(t, err) // Touch dir @@ -325,12 +343,19 @@ func mutateSampleDir(t *testing.T, root string) { } func TestChangesDirsMutated(t *testing.T) { - // TODO Windows. There may be a way of running this, but turning off for now - // as createSampleDir uses symlinks. + // Note we parse kernel.GetKernelVersion rather than system.GetOSVersion + // as test binaries aren't manifested, so would otherwise report the wrong + // build number. if runtime.GOOS == "windows" { - t.Skip("symlinks on Windows") + v, err := kernel.GetKernelVersion() + assert.NilError(t, err) + build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0]) + if build >= osversion.V19H1 { + t.Skip("FIXME: broken on Windows 1903 and up; see #39846") + } } - src, err := ioutil.TempDir("", "docker-changes-test") + + src, err := os.MkdirTemp("", "docker-changes-test") assert.NilError(t, err) createSampleDir(t, src) dst := src + "-copy" @@ -347,20 +372,37 @@ func TestChangesDirsMutated(t *testing.T) { sort.Sort(changesByPath(changes)) expectedChanges := []Change{ - {"/dir1", ChangeDelete}, - {"/dir2", ChangeModify}, - {"/dirnew", ChangeAdd}, - {"/file1", ChangeDelete}, - {"/file2", ChangeModify}, - {"/file3", ChangeModify}, - {"/file4", ChangeModify}, - {"/file5", ChangeModify}, - {"/filenew", ChangeAdd}, - {"/symlink1", ChangeDelete}, - {"/symlink2", ChangeModify}, - {"/symlinknew", ChangeAdd}, + {filepath.FromSlash("/dir1"), ChangeDelete}, + {filepath.FromSlash("/dir2"), ChangeModify}, } + // Note there is slight difference between the Linux and Windows + // implementations here. Due to https://github.com/moby/moby/issues/9874, + // and the fix at https://github.com/moby/moby/pull/11422, Linux does not + // consider a change to the directory time as a change. Windows on NTFS + // does. See https://github.com/moby/moby/pull/37982 for more information. + // + // Note also: https://github.com/moby/moby/pull/37982#discussion_r223523114 + // that differences are ordered in the way the test is currently written, hence + // this is in the middle of the list of changes rather than at the start or + // end. Potentially can be addressed later. + if runtime.GOOS == "windows" { + expectedChanges = append(expectedChanges, Change{filepath.FromSlash("/dir3"), ChangeModify}) + } + + expectedChanges = append(expectedChanges, []Change{ + {filepath.FromSlash("/dirnew"), ChangeAdd}, + {filepath.FromSlash("/file1"), ChangeDelete}, + {filepath.FromSlash("/file2"), ChangeModify}, + {filepath.FromSlash("/file3"), ChangeModify}, + {filepath.FromSlash("/file4"), ChangeModify}, + {filepath.FromSlash("/file5"), ChangeModify}, + {filepath.FromSlash("/filenew"), ChangeAdd}, + {filepath.FromSlash("/symlink1"), ChangeDelete}, + {filepath.FromSlash("/symlink2"), ChangeModify}, + {filepath.FromSlash("/symlinknew"), ChangeAdd}, + }...) + for i := 0; i < max(len(changes), len(expectedChanges)); i++ { if i >= len(expectedChanges) { t.Fatalf("unexpected change %s\n", changes[i].String()) @@ -373,7 +415,7 @@ func TestChangesDirsMutated(t *testing.T) { t.Fatalf("Wrong change for %s, expected %s, got %s\n", changes[i].Path, changes[i].String(), expectedChanges[i].String()) } } else if changes[i].Path < expectedChanges[i].Path { - t.Fatalf("unexpected change %s\n", changes[i].String()) + t.Fatalf("unexpected change %q %q\n", changes[i].String(), expectedChanges[i].Path) } else { t.Fatalf("no change for expected change %s != %s\n", expectedChanges[i].String(), changes[i].String()) } @@ -381,12 +423,15 @@ func TestChangesDirsMutated(t *testing.T) { } func TestApplyLayer(t *testing.T) { - // TODO Windows. There may be a way of running this, but turning off for now - // as createSampleDir uses symlinks. + // TODO Windows. This is very close to working, but it fails with changes + // to \symlinknew and \symlink2. The destination has an updated + // Access/Modify/Change/Birth date to the source (~3/100th sec different). + // Needs further investigation as to why, but I currently believe this is + // just the way NTFS works. I don't think it's a bug in this test or archive. if runtime.GOOS == "windows" { - t.Skip("symlinks on Windows") + t.Skip("needs further investigation") } - src, err := ioutil.TempDir("", "docker-changes-test") + src, err := os.MkdirTemp("", "docker-changes-test") assert.NilError(t, err) createSampleDir(t, src) defer os.RemoveAll(src) @@ -417,16 +462,16 @@ func TestApplyLayer(t *testing.T) { } func TestChangesSizeWithHardlinks(t *testing.T) { - // TODO Windows. There may be a way of running this, but turning off for now - // as createSampleDir uses symlinks. + // TODO Windows. Needs further investigation. Likely in ChangeSizes not + // coping correctly with hardlinks on Windows. if runtime.GOOS == "windows" { - t.Skip("hardlinks on Windows") + t.Skip("needs further investigation") } - srcDir, err := ioutil.TempDir("", "docker-test-srcDir") + srcDir, err := os.MkdirTemp("", "docker-test-srcDir") assert.NilError(t, err) defer os.RemoveAll(srcDir) - destDir, err := ioutil.TempDir("", "docker-test-destDir") + destDir, err := os.MkdirTemp("", "docker-test-destDir") assert.NilError(t, err) defer os.RemoveAll(destDir) @@ -460,14 +505,14 @@ func TestChangesSizeWithOnlyDeleteChanges(t *testing.T) { } func TestChangesSize(t *testing.T) { - parentPath, err := ioutil.TempDir("", "docker-changes-test") + parentPath, err := os.MkdirTemp("", "docker-changes-test") assert.NilError(t, err) defer os.RemoveAll(parentPath) addition := path.Join(parentPath, "addition") - err = ioutil.WriteFile(addition, []byte{0x01, 0x01, 0x01}, 0744) + err = os.WriteFile(addition, []byte{0x01, 0x01, 0x01}, 0744) assert.NilError(t, err) modification := path.Join(parentPath, "modification") - err = ioutil.WriteFile(modification, []byte{0x01, 0x01, 0x01}, 0744) + err = os.WriteFile(modification, []byte{0x01, 0x01, 0x01}, 0744) assert.NilError(t, err) changes := []Change{ @@ -481,7 +526,7 @@ func TestChangesSize(t *testing.T) { } func checkChanges(expectedChanges, changes []Change, t *testing.T) { - skip.If(t, os.Getuid() != 0, "skipping test that requires root") + skip.If(t, runtime.GOOS != "windows" && os.Getuid() != 0, "skipping test that requires root") sort.Sort(changesByPath(expectedChanges)) sort.Sort(changesByPath(changes)) for i := 0; i < max(len(changes), len(expectedChanges)); i++ { diff --git a/pkg/archive/changes_unix.go b/pkg/archive/changes_unix.go index c06a209d8ed10..54aace970ed03 100644 --- a/pkg/archive/changes_unix.go +++ b/pkg/archive/changes_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package archive // import "github.com/docker/docker/pkg/archive" @@ -16,7 +17,13 @@ func statDifferent(oldStat *system.StatT, newStat *system.StatT) bool { oldStat.UID() != newStat.UID() || oldStat.GID() != newStat.GID() || oldStat.Rdev() != newStat.Rdev() || - // Don't look at size for dirs, its not a good measure of change + // Don't look at size or modification time for dirs, its not a good + // measure of change. See https://github.com/moby/moby/issues/9874 + // for a description of the issue with modification time, and + // https://github.com/moby/moby/pull/11422 for the change. + // (Note that in the Windows implementation of this function, + // modification time IS taken as a change). See + // https://github.com/moby/moby/pull/37982 for more information. (oldStat.Mode()&unix.S_IFDIR != unix.S_IFDIR && (!sameFsTimeSpec(oldStat.Mtim(), newStat.Mtim()) || (oldStat.Size() != newStat.Size()))) { return true diff --git a/pkg/archive/changes_windows.go b/pkg/archive/changes_windows.go index 6555c01368c68..9906685e4b0ea 100644 --- a/pkg/archive/changes_windows.go +++ b/pkg/archive/changes_windows.go @@ -7,9 +7,13 @@ import ( ) func statDifferent(oldStat *system.StatT, newStat *system.StatT) bool { + // Note there is slight difference between the Linux and Windows + // implementations here. Due to https://github.com/moby/moby/issues/9874, + // and the fix at https://github.com/moby/moby/pull/11422, Linux does not + // consider a change to the directory time as a change. Windows on NTFS + // does. See https://github.com/moby/moby/pull/37982 for more information. - // Don't look at size for dirs, its not a good measure of change - if oldStat.Mtim() != newStat.Mtim() || + if !sameFsTime(oldStat.Mtim(), newStat.Mtim()) || oldStat.Mode() != newStat.Mode() || oldStat.Size() != newStat.Size() && !oldStat.Mode().IsDir() { return true diff --git a/pkg/archive/copy.go b/pkg/archive/copy.go index d0f13ca79beb3..801b844b786cb 100644 --- a/pkg/archive/copy.go +++ b/pkg/archive/copy.go @@ -4,7 +4,6 @@ import ( "archive/tar" "errors" "io" - "io/ioutil" "os" "path/filepath" "strings" @@ -261,7 +260,7 @@ func PrepareArchiveCopy(srcContent io.Reader, srcInfo, dstInfo CopyInfo) (dstDir // The destination exists as a directory. No alteration // to srcContent is needed as its contents can be // simply extracted to the destination directory. - return dstInfo.Path, ioutil.NopCloser(srcContent), nil + return dstInfo.Path, io.NopCloser(srcContent), nil case dstInfo.Exists && srcInfo.IsDir: // The destination exists as some type of file and the source // content is a directory. This is an error condition since @@ -336,6 +335,14 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read return } + // srcContent tar stream, as served by TarWithOptions(), is + // definitely in PAX format, but tar.Next() mistakenly guesses it + // as USTAR, which creates a problem: if the newBase is >100 + // characters long, WriteHeader() returns an error like + // "archive/tar: cannot encode header: Format specifies USTAR; and USTAR cannot encode Name=...". + // + // To fix, set the format to PAX here. See docker/for-linux issue #484. + hdr.Format = tar.FormatPAX hdr.Name = strings.Replace(hdr.Name, oldBase, newBase, 1) if hdr.Typeflag == tar.TypeLink { hdr.Linkname = strings.Replace(hdr.Linkname, oldBase, newBase, 1) @@ -346,6 +353,16 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read return } + // Ignoring GoSec G110. See https://github.com/securego/gosec/pull/433 + // and https://cure53.de/pentest-report_opa.pdf, which recommends to + // replace io.Copy with io.CopyN7. The latter allows to specify the + // maximum number of bytes that should be read. By properly defining + // the limit, it can be assured that a GZip compression bomb cannot + // easily cause a Denial-of-Service. + // After reviewing with @tonistiigi and @cpuguy83, this should not + // affect us, because here we do not read into memory, hence should + // not be vulnerable to this code consuming memory. + //nolint:gosec // G110: Potential DoS vulnerability via decompression bomb (gosec) if _, err = io.Copy(rebasedTar, srcTar); err != nil { w.CloseWithError(err) return @@ -356,9 +373,6 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read return rebased } -// TODO @gupta-ak. These might have to be changed in the future to be -// continuity driver aware as well to support LCOW. - // CopyResource performs an archive copy from the given source path to the // given destination path. The source path MUST exist and the destination // path's parent directory must exist. diff --git a/pkg/archive/copy_unix.go b/pkg/archive/copy_unix.go index 3958364f5ba07..2ac7729f4cf59 100644 --- a/pkg/archive/copy_unix.go +++ b/pkg/archive/copy_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package archive // import "github.com/docker/docker/pkg/archive" diff --git a/pkg/archive/copy_unix_test.go b/pkg/archive/copy_unix_test.go index 739ad0e3eff4e..efb20e225d115 100644 --- a/pkg/archive/copy_unix_test.go +++ b/pkg/archive/copy_unix_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows // TODO Windows: Some of these tests may be salvageable and portable to Windows. @@ -10,13 +11,12 @@ import ( "encoding/hex" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strings" "testing" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func removeAllPaths(paths ...string) { @@ -28,10 +28,10 @@ func removeAllPaths(paths ...string) { func getTestTempDirs(t *testing.T) (tmpDirA, tmpDirB string) { var err error - tmpDirA, err = ioutil.TempDir("", "archive-copy-test") + tmpDirA, err = os.MkdirTemp("", "archive-copy-test") assert.NilError(t, err) - tmpDirB, err = ioutil.TempDir("", "archive-copy-test") + tmpDirB, err = os.MkdirTemp("", "archive-copy-test") assert.NilError(t, err) return @@ -257,6 +257,30 @@ func TestCopyErrDstNotDir(t *testing.T) { } } +// Test to check if CopyTo works with a long (>100 characters) destination file name. +// This is a regression (see https://github.com/docker/for-linux/issues/484). +func TestCopyLongDstFilename(t *testing.T) { + const longName = "a_very_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_long_filename_that_is_101_characters" + tmpDirA, tmpDirB := getTestTempDirs(t) + defer removeAllPaths(tmpDirA, tmpDirB) + + // Load A with some sample files and directories. + createSampleDir(t, tmpDirA) + + srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} + + content, err := TarResource(srcInfo) + if err != nil { + t.Fatalf("unexpected error %T: %s", err, err) + } + defer content.Close() + + err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, longName)) + if err != nil { + t.Fatalf("unexpected error %T: %s", err, err) + } +} + // Possibilities are reduced to the remaining 10 cases: // // case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action diff --git a/pkg/archive/diff.go b/pkg/archive/diff.go index 6883ba13269cd..e095104bd96c4 100644 --- a/pkg/archive/diff.go +++ b/pkg/archive/diff.go @@ -4,7 +4,6 @@ import ( "archive/tar" "fmt" "io" - "io/ioutil" "os" "path/filepath" "runtime" @@ -84,7 +83,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, parentPath := filepath.Join(dest, parent) if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) { - err = system.MkdirAll(parentPath, 0600, "") + err = system.MkdirAll(parentPath, 0600) if err != nil { return 0, err } @@ -100,7 +99,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, basename := filepath.Base(hdr.Name) aufsHardlinks[basename] = hdr if aufsTempdir == "" { - if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil { + if aufsTempdir, err = os.MkdirTemp("", "dockerplnk"); err != nil { return 0, err } defer os.RemoveAll(aufsTempdir) @@ -196,7 +195,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64, return 0, err } - if err := createTarFile(path, dest, srcHdr, srcData, true, nil, options.InUserNS); err != nil { + if err := createTarFile(path, dest, srcHdr, srcData, !options.NoLchown, nil, options.InUserNS); err != nil { return 0, err } @@ -240,11 +239,13 @@ func applyLayerHandler(dest string, layer io.Reader, options *TarOptions, decomp dest = filepath.Clean(dest) // We need to be able to set any perms - oldmask, err := system.Umask(0) - if err != nil { - return 0, err + if runtime.GOOS != "windows" { + oldmask, err := system.Umask(0) + if err != nil { + return 0, err + } + defer system.Umask(oldmask) } - defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform if decompress { decompLayer, err := DecompressStream(layer) diff --git a/pkg/archive/diff_test.go b/pkg/archive/diff_test.go index 19f2555e1ab5b..aac7b7c34087f 100644 --- a/pkg/archive/diff_test.go +++ b/pkg/archive/diff_test.go @@ -3,21 +3,15 @@ package archive // import "github.com/docker/docker/pkg/archive" import ( "archive/tar" "io" - "io/ioutil" "os" "path/filepath" "reflect" - "runtime" "testing" "github.com/docker/docker/pkg/ioutils" ) func TestApplyLayerInvalidFilenames(t *testing.T) { - // TODO Windows: Figure out how to fix this test. - if runtime.GOOS == "windows" { - t.Skip("Passes but hits breakoutError: platform and architecture is not supported") - } for i, headers := range [][]*tar.Header{ { { @@ -42,9 +36,6 @@ func TestApplyLayerInvalidFilenames(t *testing.T) { } func TestApplyLayerInvalidHardlink(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("TypeLink support on Windows") - } for i, headers := range [][]*tar.Header{ { // try reading victim/hello (../) { @@ -125,9 +116,6 @@ func TestApplyLayerInvalidHardlink(t *testing.T) { } func TestApplyLayerInvalidSymlink(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("TypeSymLink support on Windows") - } for i, headers := range [][]*tar.Header{ { // try reading victim/hello (../) { @@ -208,12 +196,7 @@ func TestApplyLayerInvalidSymlink(t *testing.T) { } func TestApplyLayerWhiteouts(t *testing.T) { - // TODO Windows: Figure out why this test fails - if runtime.GOOS == "windows" { - t.Skip("Failing on Windows") - } - - wd, err := ioutil.TempDir("", "graphdriver-test-whiteouts") + wd, err := os.MkdirTemp("", "graphdriver-test-whiteouts") if err != nil { return } @@ -329,7 +312,7 @@ func TestApplyLayerWhiteouts(t *testing.T) { } func makeTestLayer(paths []string) (rc io.ReadCloser, err error) { - tmpDir, err := ioutil.TempDir("", "graphdriver-test-mklayer") + tmpDir, err := os.MkdirTemp("", "graphdriver-test-mklayer") if err != nil { return } @@ -339,12 +322,14 @@ func makeTestLayer(paths []string) (rc io.ReadCloser, err error) { } }() for _, p := range paths { - if p[len(p)-1] == filepath.Separator { + // Source files are always in Unix format. But we use filepath on + // creation to be platform agnostic. + if p[len(p)-1] == '/' { if err = os.MkdirAll(filepath.Join(tmpDir, p), 0700); err != nil { return } } else { - if err = ioutil.WriteFile(filepath.Join(tmpDir, p), nil, 0600); err != nil { + if err = os.WriteFile(filepath.Join(tmpDir, p), nil, 0600); err != nil { return } } @@ -374,9 +359,10 @@ func readDirContents(root string) ([]string, error) { return err } if info.IsDir() { - rel = rel + "/" + rel = rel + string(filepath.Separator) } - files = append(files, rel) + // Append in Unix semantics + files = append(files, filepath.ToSlash(rel)) return nil }) if err != nil { diff --git a/pkg/archive/example_changes.go b/pkg/archive/example_changes.go index 495db809e9f69..36cb6c3cb5bd2 100644 --- a/pkg/archive/example_changes.go +++ b/pkg/archive/example_changes.go @@ -1,3 +1,4 @@ +//go:build ignore // +build ignore // Simple tool to create an archive stream from an old and new directory @@ -9,7 +10,6 @@ import ( "flag" "fmt" "io" - "io/ioutil" "os" "path" @@ -39,7 +39,7 @@ func main() { if len(*flNewDir) == 0 { var err error - newDir, err = ioutil.TempDir("", "docker-test-newDir") + newDir, err = os.MkdirTemp("", "docker-test-newDir") if err != nil { log.Fatal(err) } @@ -52,7 +52,7 @@ func main() { } if len(*flOldDir) == 0 { - oldDir, err := ioutil.TempDir("", "docker-test-oldDir") + oldDir, err := os.MkdirTemp("", "docker-test-oldDir") if err != nil { log.Fatal(err) } @@ -83,7 +83,7 @@ func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks fileData := []byte("fooo") for n := 0; n < numberOfFiles; n++ { fileName := fmt.Sprintf("file-%d", n) - if err := ioutil.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil { + if err := os.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil { return 0, err } if makeLinks { diff --git a/pkg/archive/time_unsupported.go b/pkg/archive/time_unsupported.go index f58bf227fd33f..d0877968617e2 100644 --- a/pkg/archive/time_unsupported.go +++ b/pkg/archive/time_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package archive // import "github.com/docker/docker/pkg/archive" diff --git a/pkg/archive/utils_test.go b/pkg/archive/utils_test.go index a20f58ddabb4a..a00ae680c0ebf 100644 --- a/pkg/archive/utils_test.go +++ b/pkg/archive/utils_test.go @@ -5,7 +5,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "os" "path/filepath" "time" @@ -34,7 +33,7 @@ var testUntarFns = map[string]func(string, io.Reader) error{ // // When using testBreakout make sure you cover one of the scenarios listed above. func testBreakout(untarFn string, tmpdir string, headers []*tar.Header) error { - tmpdir, err := ioutil.TempDir("", tmpdir) + tmpdir, err := os.MkdirTemp("", tmpdir) if err != nil { return err } @@ -54,7 +53,7 @@ func testBreakout(untarFn string, tmpdir string, headers []*tar.Header) error { if err != nil { return err } - if err := ioutil.WriteFile(hello, helloData, 0644); err != nil { + if err := os.WriteFile(hello, helloData, 0644); err != nil { return err } helloStat, err := os.Stat(hello) @@ -119,7 +118,7 @@ func testBreakout(untarFn string, tmpdir string, headers []*tar.Header) error { return fmt.Errorf("archive breakout: could not lstat %q: %v", hello, err) } defer f.Close() - b, err := ioutil.ReadAll(f) + b, err := io.ReadAll(f) if err != nil { return err } @@ -153,7 +152,7 @@ func testBreakout(untarFn string, tmpdir string, headers []*tar.Header) error { // skip file if error return nil } - b, err := ioutil.ReadFile(path) + b, err := os.ReadFile(path) if err != nil { // Houston, we have a problem. Aborting (space)walk. return err diff --git a/pkg/archive/wrap_test.go b/pkg/archive/wrap_test.go index 1faa7aed75393..764ef304d1cb0 100644 --- a/pkg/archive/wrap_test.go +++ b/pkg/archive/wrap_test.go @@ -6,7 +6,7 @@ import ( "io" "testing" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestGenerateEmptyFile(t *testing.T) { diff --git a/pkg/authorization/api_test.go b/pkg/authorization/api_test.go index 9d51e9e66ecbc..8e05c917d1688 100644 --- a/pkg/authorization/api_test.go +++ b/pkg/authorization/api_test.go @@ -11,8 +11,8 @@ import ( "testing" "time" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestPeerCertificateMarshalJSON(t *testing.T) { @@ -46,7 +46,7 @@ func TestPeerCertificateMarshalJSON(t *testing.T) { var certs = []*x509.Certificate{cert} addr := "www.authz.com/auth" - req, err := http.NewRequest("GET", addr, nil) + req, err := http.NewRequest(http.MethodGet, addr, nil) assert.NilError(t, err) req.RequestURI = addr diff --git a/pkg/authorization/authz.go b/pkg/authorization/authz.go index a1edbcd89d51d..590ac8dddd883 100644 --- a/pkg/authorization/authz.go +++ b/pkg/authorization/authz.go @@ -21,7 +21,7 @@ const maxBodySize = 1048576 // 1MB // Authenticate Request: // Call authZ plugins with current REST request and AuthN response // Request contains full HTTP packet sent to the docker daemon -// https://docs.docker.com/engine/reference/api/ +// https://docs.docker.com/engine/api/ // // Authenticate Response: // Call authZ plugins with full info about current REST request, REST response and AuthN response diff --git a/pkg/authorization/authz_unix_test.go b/pkg/authorization/authz_unix_test.go index c38b5e156667c..835cb703839be 100644 --- a/pkg/authorization/authz_unix_test.go +++ b/pkg/authorization/authz_unix_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows // TODO Windows: This uses a Unix socket for testing. This might be possible @@ -8,7 +9,7 @@ package authorization // import "github.com/docker/docker/pkg/authorization" import ( "bytes" "encoding/json" - "io/ioutil" + "io" "net" "net/http" "net/http/httptest" @@ -32,13 +33,13 @@ func TestAuthZRequestPluginError(t *testing.T) { server.start() defer server.stop() - authZPlugin := createTestPlugin(t) + authZPlugin := createTestPlugin(t, server.socketAddress()) request := Request{ User: "user", RequestBody: []byte("sample body"), RequestURI: "www.authz.com/auth", - RequestMethod: "GET", + RequestMethod: http.MethodGet, RequestHeaders: map[string]string{"header": "value"}, } server.replayResponse = Response{ @@ -63,13 +64,13 @@ func TestAuthZRequestPlugin(t *testing.T) { server.start() defer server.stop() - authZPlugin := createTestPlugin(t) + authZPlugin := createTestPlugin(t, server.socketAddress()) request := Request{ User: "user", RequestBody: []byte("sample body"), RequestURI: "www.authz.com/auth", - RequestMethod: "GET", + RequestMethod: http.MethodGet, RequestHeaders: map[string]string{"header": "value"}, } server.replayResponse = Response{ @@ -95,7 +96,7 @@ func TestAuthZResponsePlugin(t *testing.T) { server.start() defer server.stop() - authZPlugin := createTestPlugin(t) + authZPlugin := createTestPlugin(t, server.socketAddress()) request := Request{ User: "user", @@ -152,7 +153,7 @@ func TestDrainBody(t *testing.T) { for _, test := range tests { msg := strings.Repeat("a", test.length) - body, closer, err := drainBody(ioutil.NopCloser(bytes.NewReader([]byte(msg)))) + body, closer, err := drainBody(io.NopCloser(bytes.NewReader([]byte(msg)))) if err != nil { t.Fatal(err) } @@ -162,7 +163,7 @@ func TestDrainBody(t *testing.T) { if closer == nil { t.Fatal("Closer must not be nil") } - modified, err := ioutil.ReadAll(closer) + modified, err := io.ReadAll(closer) if err != nil { t.Fatalf("Error must not be nil: '%v'", err) } @@ -262,13 +263,8 @@ func TestResponseModifierOverride(t *testing.T) { } // createTestPlugin creates a new sample authorization plugin -func createTestPlugin(t *testing.T) *authorizationPlugin { - pwd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - client, err := plugins.NewClient("unix:///"+path.Join(pwd, pluginAddress), &tlsconfig.Options{InsecureSkipVerify: true}) +func createTestPlugin(t *testing.T, socketAddress string) *authorizationPlugin { + client, err := plugins.NewClient("unix:///"+socketAddress, &tlsconfig.Options{InsecureSkipVerify: true}) if err != nil { t.Fatalf("Failed to create client %v", err) } @@ -285,12 +281,23 @@ type authZPluginTestServer struct { // response stores the response sent from the plugin to the daemon replayResponse Response server *httptest.Server + tmpDir string +} + +func (t *authZPluginTestServer) socketAddress() string { + return path.Join(t.tmpDir, pluginAddress) } // start starts the test server that implements the plugin func (t *authZPluginTestServer) start() { + var err error + t.tmpDir, err = os.MkdirTemp("", "authz") + if err != nil { + t.t.Fatal(err) + } + r := mux.NewRouter() - l, err := net.Listen("unix", pluginAddress) + l, err := net.Listen("unix", t.socketAddress()) if err != nil { t.t.Fatal(err) } @@ -311,7 +318,7 @@ func (t *authZPluginTestServer) start() { // stop stops the test server that implements the plugin func (t *authZPluginTestServer) stop() { t.server.Close() - os.Remove(pluginAddress) + _ = os.RemoveAll(t.tmpDir) if t.listener != nil { t.listener.Close() } @@ -320,7 +327,7 @@ func (t *authZPluginTestServer) stop() { // auth is a used to record/replay the authentication api messages func (t *authZPluginTestServer) auth(w http.ResponseWriter, r *http.Request) { t.recordedRequest = Request{} - body, err := ioutil.ReadAll(r.Body) + body, err := io.ReadAll(r.Body) if err != nil { t.t.Fatal(err) } diff --git a/pkg/authorization/middleware_test.go b/pkg/authorization/middleware_test.go index 6afafe082d3ba..c7597d35c6497 100644 --- a/pkg/authorization/middleware_test.go +++ b/pkg/authorization/middleware_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/docker/docker/pkg/plugingetter" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestMiddleware(t *testing.T) { diff --git a/pkg/authorization/middleware_unix_test.go b/pkg/authorization/middleware_unix_test.go index 450e7fbbb7375..2587f9dc2a3de 100644 --- a/pkg/authorization/middleware_unix_test.go +++ b/pkg/authorization/middleware_unix_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package authorization // import "github.com/docker/docker/pkg/authorization" @@ -9,8 +10,8 @@ import ( "testing" "github.com/docker/docker/pkg/plugingetter" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestMiddlewareWrapHandler(t *testing.T) { @@ -18,7 +19,7 @@ func TestMiddlewareWrapHandler(t *testing.T) { server.start() defer server.stop() - authZPlugin := createTestPlugin(t) + authZPlugin := createTestPlugin(t, server.socketAddress()) pluginNames := []string{authZPlugin.name} var pluginGetter plugingetter.PluginGetter @@ -34,7 +35,7 @@ func TestMiddlewareWrapHandler(t *testing.T) { assert.Assert(t, mdHandler != nil) addr := "www.example.com/auth" - req, _ := http.NewRequest("GET", addr, nil) + req, _ := http.NewRequest(http.MethodGet, addr, nil) req.RequestURI = addr req.Header.Add("header", "value") diff --git a/pkg/authorization/response.go b/pkg/authorization/response.go index 6b674bc29555f..82beb5be800d5 100644 --- a/pkg/authorization/response.go +++ b/pkg/authorization/response.go @@ -15,7 +15,6 @@ import ( type ResponseModifier interface { http.ResponseWriter http.Flusher - http.CloseNotifier // RawBody returns the current http content RawBody() []byte @@ -155,16 +154,6 @@ func (rm *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) { return hijacker.Hijack() } -// CloseNotify uses the internal close notify API of the wrapped http.ResponseWriter -func (rm *responseModifier) CloseNotify() <-chan bool { - closeNotifier, ok := rm.rw.(http.CloseNotifier) - if !ok { - logrus.Error("Internal response writer doesn't support the CloseNotifier interface") - return nil - } - return closeNotifier.CloseNotify() -} - // Flush uses the internal flush API of the wrapped http.ResponseWriter func (rm *responseModifier) Flush() { flusher, ok := rm.rw.(http.Flusher) diff --git a/pkg/capabilities/caps.go b/pkg/capabilities/caps.go new file mode 100644 index 0000000000000..a35d4204b0cdc --- /dev/null +++ b/pkg/capabilities/caps.go @@ -0,0 +1,24 @@ +// Package capabilities allows to generically handle capabilities. +package capabilities // import "github.com/docker/docker/pkg/capabilities" + +// Set represents a set of capabilities. +type Set map[string]struct{} + +// Match tries to match set with caps, which is an OR list of AND lists of capabilities. +// The matched AND list of capabilities is returned; or nil if none are matched. +func (set Set) Match(caps [][]string) []string { + if set == nil { + return nil + } +anyof: + for _, andList := range caps { + for _, cap := range andList { + if _, ok := set[cap]; !ok { + continue anyof + } + } + return andList + } + // match anything + return nil +} diff --git a/pkg/capabilities/caps_test.go b/pkg/capabilities/caps_test.go new file mode 100644 index 0000000000000..072f230369cdc --- /dev/null +++ b/pkg/capabilities/caps_test.go @@ -0,0 +1,72 @@ +package capabilities // import "github.com/docker/docker/pkg/capabilities" + +import ( + "fmt" + "testing" +) + +func TestMatch(t *testing.T) { + set := Set{ + "foo": struct{}{}, + "bar": struct{}{}, + } + type testcase struct { + caps [][]string + expected []string + } + var testcases = []testcase{ + // matches + { + caps: [][]string{{}}, + expected: []string{}, + }, + { + caps: [][]string{{"foo"}}, + expected: []string{"foo"}, + }, + { + caps: [][]string{{"bar"}, {"foo"}}, + expected: []string{"bar"}, + }, + { + caps: [][]string{{"foo", "bar"}}, + expected: []string{"foo", "bar"}, + }, + { + caps: [][]string{{"qux"}, {"foo"}}, + expected: []string{"foo"}, + }, + { + caps: [][]string{{"foo", "bar"}, {"baz"}, {"bar"}}, + expected: []string{"foo", "bar"}, + }, + + // non matches + {caps: nil}, + {caps: [][]string{}}, + {caps: [][]string{{"qux"}}}, + {caps: [][]string{{"foo", "bar", "qux"}}}, + {caps: [][]string{{"qux"}, {"baz"}}}, + {caps: [][]string{{"foo", "baz"}}}, + } + + for _, m := range testcases { + t.Run(fmt.Sprintf("%v", m.caps), func(t *testing.T) { + selected := set.Match(m.caps) + if m.expected == nil || selected == nil { + if m.expected == nil && selected == nil { + return + } + t.Fatalf("selected = %v, expected = %v", selected, m.expected) + } + if len(selected) != len(m.expected) { + t.Fatalf("len(selected) = %d, len(expected) = %d", len(selected), len(m.expected)) + } + for i, s := range selected { + if m.expected[i] != s { + t.Fatalf("selected[%d] = %s, expected[%d] = %s", i, s, i, m.expected[i]) + } + } + }) + } +} diff --git a/pkg/chrootarchive/archive.go b/pkg/chrootarchive/archive.go index 2d9d662830b7b..427abee7e82f6 100644 --- a/pkg/chrootarchive/archive.go +++ b/pkg/chrootarchive/archive.go @@ -3,14 +3,22 @@ package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" import ( "fmt" "io" - "io/ioutil" + "net" "os" + "os/user" "path/filepath" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/idtools" ) +func init() { + // initialize nss libraries in Glibc so that the dynamic libraries are loaded in the host + // environment not in the chroot from untrusted files. + _, _ = user.Lookup("docker") + _, _ = net.LookupHost("localhost") +} + // NewArchiver returns a new Archiver which uses chrootarchive.Untar func NewArchiver(idMapping *idtools.IdentityMapping) *archive.Archiver { if idMapping == nil { @@ -27,18 +35,34 @@ func NewArchiver(idMapping *idtools.IdentityMapping) *archive.Archiver { // The archive may be compressed with one of the following algorithms: // identity (uncompressed), gzip, bzip2, xz. func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error { - return untarHandler(tarArchive, dest, options, true) + return untarHandler(tarArchive, dest, options, true, dest) +} + +// UntarWithRoot is the same as `Untar`, but allows you to pass in a root directory +// The root directory is the directory that will be chrooted to. +// `dest` must be a path within `root`, if it is not an error will be returned. +// +// `root` should set to a directory which is not controlled by any potentially +// malicious process. +// +// This should be used to prevent a potential attacker from manipulating `dest` +// such that it would provide access to files outside of `dest` through things +// like symlinks. Normally `ResolveSymlinksInScope` would handle this, however +// sanitizing symlinks in this manner is inherrently racey: +// ref: CVE-2018-15664 +func UntarWithRoot(tarArchive io.Reader, dest string, options *archive.TarOptions, root string) error { + return untarHandler(tarArchive, dest, options, true, root) } // UntarUncompressed reads a stream of bytes from `archive`, parses it as a tar archive, // and unpacks it into the directory at `dest`. // The archive must be an uncompressed stream. func UntarUncompressed(tarArchive io.Reader, dest string, options *archive.TarOptions) error { - return untarHandler(tarArchive, dest, options, false) + return untarHandler(tarArchive, dest, options, false, dest) } // Handler for teasing out the automatic decompression -func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool) error { +func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions, decompress bool, root string) error { if tarArchive == nil { return fmt.Errorf("Empty archive") } @@ -49,17 +73,21 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions options.ExcludePatterns = []string{} } - idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) - rootIDs := idMapping.RootPair() + // If dest is inside a root then directory is created within chroot by extractor. + // This case is only currently used by cp. + if dest == root { + idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) + rootIDs := idMapping.RootPair() - dest = filepath.Clean(dest) - if _, err := os.Stat(dest); os.IsNotExist(err) { - if err := idtools.MkdirAllAndChownNew(dest, 0755, rootIDs); err != nil { - return err + dest = filepath.Clean(dest) + if _, err := os.Stat(dest); os.IsNotExist(err) { + if err := idtools.MkdirAllAndChownNew(dest, 0755, rootIDs); err != nil { + return err + } } } - r := ioutil.NopCloser(tarArchive) + r := io.NopCloser(tarArchive) if decompress { decompressedArchive, err := archive.DecompressStream(tarArchive) if err != nil { @@ -69,5 +97,13 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions r = decompressedArchive } - return invokeUnpack(r, dest, options) + return invokeUnpack(r, dest, options, root) +} + +// Tar tars the requested path while chrooted to the specified root. +func Tar(srcPath string, options *archive.TarOptions, root string) (io.ReadCloser, error) { + if options == nil { + options = &archive.TarOptions{} + } + return invokePack(srcPath, options, root) } diff --git a/pkg/chrootarchive/archive_test.go b/pkg/chrootarchive/archive_test.go index 5911a36158bf6..c96c83b072f32 100644 --- a/pkg/chrootarchive/archive_test.go +++ b/pkg/chrootarchive/archive_test.go @@ -5,7 +5,6 @@ import ( "fmt" "hash/crc32" "io" - "io/ioutil" "os" "path/filepath" "runtime" @@ -16,7 +15,7 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/system" - "gotest.tools/skip" + "gotest.tools/v3/skip" ) func init() { @@ -43,19 +42,19 @@ func CopyWithTar(src, dst string) error { func TestChrootTarUntar(t *testing.T) { skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tmpdir, err := ioutil.TempDir("", "docker-TestChrootTarUntar") + tmpdir, err := os.MkdirTemp("", "docker-TestChrootTarUntar") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) src := filepath.Join(tmpdir, "src") - if err := system.MkdirAll(src, 0700, ""); err != nil { + if err := system.MkdirAll(src, 0700); err != nil { t.Fatal(err) } - if err := ioutil.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil { t.Fatal(err) } - if err := ioutil.WriteFile(filepath.Join(src, "lolo"), []byte("hello lolo"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(src, "lolo"), []byte("hello lolo"), 0644); err != nil { t.Fatal(err) } stream, err := archive.Tar(src, archive.Uncompressed) @@ -63,7 +62,7 @@ func TestChrootTarUntar(t *testing.T) { t.Fatal(err) } dest := filepath.Join(tmpdir, "src") - if err := system.MkdirAll(dest, 0700, ""); err != nil { + if err := system.MkdirAll(dest, 0700); err != nil { t.Fatal(err) } if err := Untar(stream, dest, &archive.TarOptions{ExcludePatterns: []string{"lolo"}}); err != nil { @@ -75,16 +74,16 @@ func TestChrootTarUntar(t *testing.T) { // local images) func TestChrootUntarWithHugeExcludesList(t *testing.T) { skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarHugeExcludes") + tmpdir, err := os.MkdirTemp("", "docker-TestChrootUntarHugeExcludes") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) src := filepath.Join(tmpdir, "src") - if err := system.MkdirAll(src, 0700, ""); err != nil { + if err := system.MkdirAll(src, 0700); err != nil { t.Fatal(err) } - if err := ioutil.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(src, "toto"), []byte("hello toto"), 0644); err != nil { t.Fatal(err) } stream, err := archive.Tar(src, archive.Uncompressed) @@ -92,14 +91,15 @@ func TestChrootUntarWithHugeExcludesList(t *testing.T) { t.Fatal(err) } dest := filepath.Join(tmpdir, "dest") - if err := system.MkdirAll(dest, 0700, ""); err != nil { + if err := system.MkdirAll(dest, 0700); err != nil { t.Fatal(err) } options := &archive.TarOptions{} - //65534 entries of 64-byte strings ~= 4MB of environment space which should overflow - //on most systems when passed via environment or command line arguments + // 65534 entries of 64-byte strings ~= 4MB of environment space which should overflow + // on most systems when passed via environment or command line arguments excludes := make([]string, 65534) - for i := 0; i < 65534; i++ { + var i rune + for i = 0; i < 65534; i++ { excludes[i] = strings.Repeat(string(i), 64) } options.ExcludePatterns = excludes @@ -109,7 +109,7 @@ func TestChrootUntarWithHugeExcludesList(t *testing.T) { } func TestChrootUntarEmptyArchive(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarEmptyArchive") + tmpdir, err := os.MkdirTemp("", "docker-TestChrootUntarEmptyArchive") if err != nil { t.Fatal(err) } @@ -123,7 +123,7 @@ func prepareSourceDirectory(numberOfFiles int, targetPath string, makeSymLinks b fileData := []byte("fooo") for n := 0; n < numberOfFiles; n++ { fileName := fmt.Sprintf("file-%d", n) - if err := ioutil.WriteFile(filepath.Join(targetPath, fileName), fileData, 0700); err != nil { + if err := os.WriteFile(filepath.Join(targetPath, fileName), fileData, 0700); err != nil { return 0, err } if makeSymLinks { @@ -137,7 +137,7 @@ func prepareSourceDirectory(numberOfFiles int, targetPath string, makeSymLinks b } func getHash(filename string) (uint32, error) { - stream, err := ioutil.ReadFile(filename) + stream, err := os.ReadFile(filename) if err != nil { return 0, err } @@ -175,13 +175,13 @@ func compareFiles(src string, dest string) error { func TestChrootTarUntarWithSymlink(t *testing.T) { skip.If(t, runtime.GOOS == "windows", "FIXME: figure out why this is failing") skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tmpdir, err := ioutil.TempDir("", "docker-TestChrootTarUntarWithSymlink") + tmpdir, err := os.MkdirTemp("", "docker-TestChrootTarUntarWithSymlink") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) src := filepath.Join(tmpdir, "src") - if err := system.MkdirAll(src, 0700, ""); err != nil { + if err := system.MkdirAll(src, 0700); err != nil { t.Fatal(err) } if _, err := prepareSourceDirectory(10, src, false); err != nil { @@ -199,13 +199,13 @@ func TestChrootTarUntarWithSymlink(t *testing.T) { func TestChrootCopyWithTar(t *testing.T) { skip.If(t, runtime.GOOS == "windows", "FIXME: figure out why this is failing") skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tmpdir, err := ioutil.TempDir("", "docker-TestChrootCopyWithTar") + tmpdir, err := os.MkdirTemp("", "docker-TestChrootCopyWithTar") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) src := filepath.Join(tmpdir, "src") - if err := system.MkdirAll(src, 0700, ""); err != nil { + if err := system.MkdirAll(src, 0700); err != nil { t.Fatal(err) } if _, err := prepareSourceDirectory(10, src, true); err != nil { @@ -246,13 +246,13 @@ func TestChrootCopyWithTar(t *testing.T) { func TestChrootCopyFileWithTar(t *testing.T) { skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tmpdir, err := ioutil.TempDir("", "docker-TestChrootCopyFileWithTar") + tmpdir, err := os.MkdirTemp("", "docker-TestChrootCopyFileWithTar") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) src := filepath.Join(tmpdir, "src") - if err := system.MkdirAll(src, 0700, ""); err != nil { + if err := system.MkdirAll(src, 0700); err != nil { t.Fatal(err) } if _, err := prepareSourceDirectory(10, src, true); err != nil { @@ -291,13 +291,13 @@ func TestChrootCopyFileWithTar(t *testing.T) { func TestChrootUntarPath(t *testing.T) { skip.If(t, runtime.GOOS == "windows", "FIXME: figure out why this is failing") skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarPath") + tmpdir, err := os.MkdirTemp("", "docker-TestChrootUntarPath") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) src := filepath.Join(tmpdir, "src") - if err := system.MkdirAll(src, 0700, ""); err != nil { + if err := system.MkdirAll(src, 0700); err != nil { t.Fatal(err) } if _, err := prepareSourceDirectory(10, src, false); err != nil { @@ -317,7 +317,7 @@ func TestChrootUntarPath(t *testing.T) { buf := new(bytes.Buffer) buf.ReadFrom(stream) tarfile := filepath.Join(tmpdir, "src.tar") - if err := ioutil.WriteFile(tarfile, buf.Bytes(), 0644); err != nil { + if err := os.WriteFile(tarfile, buf.Bytes(), 0644); err != nil { t.Fatal(err) } if err := UntarPath(tarfile, dest); err != nil { @@ -353,13 +353,13 @@ func (s *slowEmptyTarReader) Read(p []byte) (int, error) { func TestChrootUntarEmptyArchiveFromSlowReader(t *testing.T) { skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tmpdir, err := ioutil.TempDir("", "docker-TestChrootUntarEmptyArchiveFromSlowReader") + tmpdir, err := os.MkdirTemp("", "docker-TestChrootUntarEmptyArchiveFromSlowReader") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) dest := filepath.Join(tmpdir, "dest") - if err := system.MkdirAll(dest, 0700, ""); err != nil { + if err := system.MkdirAll(dest, 0700); err != nil { t.Fatal(err) } stream := &slowEmptyTarReader{size: 10240, chunkSize: 1024} @@ -370,13 +370,13 @@ func TestChrootUntarEmptyArchiveFromSlowReader(t *testing.T) { func TestChrootApplyEmptyArchiveFromSlowReader(t *testing.T) { skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tmpdir, err := ioutil.TempDir("", "docker-TestChrootApplyEmptyArchiveFromSlowReader") + tmpdir, err := os.MkdirTemp("", "docker-TestChrootApplyEmptyArchiveFromSlowReader") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) dest := filepath.Join(tmpdir, "dest") - if err := system.MkdirAll(dest, 0700, ""); err != nil { + if err := system.MkdirAll(dest, 0700); err != nil { t.Fatal(err) } stream := &slowEmptyTarReader{size: 10240, chunkSize: 1024} @@ -387,16 +387,16 @@ func TestChrootApplyEmptyArchiveFromSlowReader(t *testing.T) { func TestChrootApplyDotDotFile(t *testing.T) { skip.If(t, os.Getuid() != 0, "skipping test that requires root") - tmpdir, err := ioutil.TempDir("", "docker-TestChrootApplyDotDotFile") + tmpdir, err := os.MkdirTemp("", "docker-TestChrootApplyDotDotFile") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpdir) src := filepath.Join(tmpdir, "src") - if err := system.MkdirAll(src, 0700, ""); err != nil { + if err := system.MkdirAll(src, 0700); err != nil { t.Fatal(err) } - if err := ioutil.WriteFile(filepath.Join(src, "..gitme"), []byte(""), 0644); err != nil { + if err := os.WriteFile(filepath.Join(src, "..gitme"), []byte(""), 0644); err != nil { t.Fatal(err) } stream, err := archive.Tar(src, archive.Uncompressed) @@ -404,7 +404,7 @@ func TestChrootApplyDotDotFile(t *testing.T) { t.Fatal(err) } dest := filepath.Join(tmpdir, "dest") - if err := system.MkdirAll(dest, 0700, ""); err != nil { + if err := system.MkdirAll(dest, 0700); err != nil { t.Fatal(err) } if _, err := ApplyLayer(dest, stream); err != nil { diff --git a/pkg/chrootarchive/archive_unix.go b/pkg/chrootarchive/archive_unix.go index 5df8afd662055..b3a8ae1135ab4 100644 --- a/pkg/chrootarchive/archive_unix.go +++ b/pkg/chrootarchive/archive_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" @@ -8,12 +9,14 @@ import ( "flag" "fmt" "io" - "io/ioutil" "os" + "path/filepath" "runtime" + "strings" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" + "github.com/pkg/errors" ) // untar is the entry-point for docker-untar on re-exec. This is not used on @@ -23,18 +26,28 @@ func untar() { runtime.LockOSThread() flag.Parse() - var options *archive.TarOptions + var options archive.TarOptions - //read the options from the pipe "ExtraFiles" + // read the options from the pipe "ExtraFiles" if err := json.NewDecoder(os.NewFile(3, "options")).Decode(&options); err != nil { fatal(err) } - if err := chroot(flag.Arg(0)); err != nil { + dst := flag.Arg(0) + var root string + if len(flag.Args()) > 1 { + root = flag.Arg(1) + } + + if root == "" { + root = dst + } + + if err := chroot(root); err != nil { fatal(err) } - if err := archive.Unpack(os.Stdin, "/", options); err != nil { + if err := archive.Unpack(os.Stdin, dst, &options); err != nil { fatal(err) } // fully consume stdin in case it is zero padded @@ -45,7 +58,10 @@ func untar() { os.Exit(0) } -func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.TarOptions) error { +func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.TarOptions, root string) error { + if root == "" { + return errors.New("must specify a root to chroot to") + } // We can't pass a potentially large exclude list directly via cmd line // because we easily overrun the kernel's max argument/environment size @@ -57,7 +73,21 @@ func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.T return fmt.Errorf("Untar pipe failure: %v", err) } - cmd := reexec.Command("docker-untar", dest) + if root != "" { + relDest, err := filepath.Rel(root, dest) + if err != nil { + return err + } + if relDest == "." { + relDest = "/" + } + if relDest[0] != '/' { + relDest = "/" + relDest + } + dest = relDest + } + + cmd := reexec.Command("docker-untar", dest, root) cmd.Stdin = decompressedArchive cmd.ExtraFiles = append(cmd.ExtraFiles, r) @@ -69,7 +99,8 @@ func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.T w.Close() return fmt.Errorf("Untar error on re-exec cmd: %v", err) } - //write the options to the pipe for the untar exec to read + + // write the options to the pipe for the untar exec to read if err := json.NewEncoder(w).Encode(options); err != nil { w.Close() return fmt.Errorf("Untar json encode to pipe failed: %v", err) @@ -80,9 +111,98 @@ func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.T // when `xz -d -c -q | docker-untar ...` failed on docker-untar side, // we need to exhaust `xz`'s output, otherwise the `xz` side will be // pending on write pipe forever - io.Copy(ioutil.Discard, decompressedArchive) + io.Copy(io.Discard, decompressedArchive) return fmt.Errorf("Error processing tar file(%v): %s", err, output) } return nil } + +func tar() { + runtime.LockOSThread() + flag.Parse() + + src := flag.Arg(0) + var root string + if len(flag.Args()) > 1 { + root = flag.Arg(1) + } + + if root == "" { + root = src + } + + if err := realChroot(root); err != nil { + fatal(err) + } + + var options archive.TarOptions + if err := json.NewDecoder(os.Stdin).Decode(&options); err != nil { + fatal(err) + } + + rdr, err := archive.TarWithOptions(src, &options) + if err != nil { + fatal(err) + } + defer rdr.Close() + + if _, err := io.Copy(os.Stdout, rdr); err != nil { + fatal(err) + } + + os.Exit(0) +} + +func invokePack(srcPath string, options *archive.TarOptions, root string) (io.ReadCloser, error) { + if root == "" { + return nil, errors.New("root path must not be empty") + } + + relSrc, err := filepath.Rel(root, srcPath) + if err != nil { + return nil, err + } + if relSrc == "." { + relSrc = "/" + } + if relSrc[0] != '/' { + relSrc = "/" + relSrc + } + + // make sure we didn't trim a trailing slash with the call to `Rel` + if strings.HasSuffix(srcPath, "/") && !strings.HasSuffix(relSrc, "/") { + relSrc += "/" + } + + cmd := reexec.Command("docker-tar", relSrc, root) + + errBuff := bytes.NewBuffer(nil) + cmd.Stderr = errBuff + + tarR, tarW := io.Pipe() + cmd.Stdout = tarW + + stdin, err := cmd.StdinPipe() + if err != nil { + return nil, errors.Wrap(err, "error getting options pipe for tar process") + } + + if err := cmd.Start(); err != nil { + return nil, errors.Wrap(err, "tar error on re-exec cmd") + } + + go func() { + err := cmd.Wait() + err = errors.Wrapf(err, "error processing tar file: %s", errBuff) + tarW.CloseWithError(err) + }() + + if err := json.NewEncoder(stdin).Encode(options); err != nil { + stdin.Close() + return nil, errors.Wrap(err, "tar json encode to pipe failed") + } + stdin.Close() + + return tarR, nil +} diff --git a/pkg/chrootarchive/archive_unix_test.go b/pkg/chrootarchive/archive_unix_test.go new file mode 100644 index 0000000000000..cd557bc5cf99a --- /dev/null +++ b/pkg/chrootarchive/archive_unix_test.go @@ -0,0 +1,174 @@ +//go:build !windows +// +build !windows + +package chrootarchive + +import ( + gotar "archive/tar" + "bytes" + "io" + "os" + "path" + "path/filepath" + "strings" + "testing" + + "github.com/docker/docker/pkg/archive" + "golang.org/x/sys/unix" + "gotest.tools/v3/assert" + "gotest.tools/v3/skip" +) + +// Test for CVE-2018-15664 +// Assures that in the case where an "attacker" controlled path is a symlink to +// some path outside of a container's rootfs that we do not copy data to a +// container path that will actually overwrite data on the host +func TestUntarWithMaliciousSymlinks(t *testing.T) { + skip.If(t, os.Getuid() != 0, "skipping test that requires root") + dir, err := os.MkdirTemp("", t.Name()) + assert.NilError(t, err) + defer os.RemoveAll(dir) + + root := filepath.Join(dir, "root") + + err = os.MkdirAll(root, 0755) + assert.NilError(t, err) + + // Add a file into a directory above root + // Ensure that we can't access this file while tarring. + err = os.WriteFile(filepath.Join(dir, "host-file"), []byte("I am a host file"), 0644) + assert.NilError(t, err) + + // Create some data which which will be copied into the "container" root into + // the symlinked path. + // Before this change, the copy would overwrite the "host" content. + // With this change it should not. + data := filepath.Join(dir, "data") + err = os.MkdirAll(data, 0755) + assert.NilError(t, err) + err = os.WriteFile(filepath.Join(data, "local-file"), []byte("pwn3d"), 0644) + assert.NilError(t, err) + + safe := filepath.Join(root, "safe") + err = unix.Symlink(dir, safe) + assert.NilError(t, err) + + rdr, err := archive.TarWithOptions(data, &archive.TarOptions{IncludeFiles: []string{"local-file"}, RebaseNames: map[string]string{"local-file": "host-file"}}) + assert.NilError(t, err) + + // Use tee to test both the good case and the bad case w/o recreating the archive + bufRdr := bytes.NewBuffer(nil) + tee := io.TeeReader(rdr, bufRdr) + + err = UntarWithRoot(tee, safe, nil, root) + assert.Assert(t, err != nil) + assert.ErrorContains(t, err, "open /safe/host-file: no such file or directory") + + // Make sure the "host" file is still in tact + // Before the fix the host file would be overwritten + hostData, err := os.ReadFile(filepath.Join(dir, "host-file")) + assert.NilError(t, err) + assert.Equal(t, string(hostData), "I am a host file") + + // Now test by chrooting to an attacker controlled path + // This should succeed as is and overwrite a "host" file + // Note that this would be a mis-use of this function. + err = UntarWithRoot(bufRdr, safe, nil, safe) + assert.NilError(t, err) + + hostData, err = os.ReadFile(filepath.Join(dir, "host-file")) + assert.NilError(t, err) + assert.Equal(t, string(hostData), "pwn3d") +} + +// Test for CVE-2018-15664 +// Assures that in the case where an "attacker" controlled path is a symlink to +// some path outside of a container's rootfs that we do not unwittingly leak +// host data into the archive. +func TestTarWithMaliciousSymlinks(t *testing.T) { + skip.If(t, os.Getuid() != 0, "skipping test that requires root") + dir, err := os.MkdirTemp("", t.Name()) + assert.NilError(t, err) + // defer os.RemoveAll(dir) + t.Log(dir) + + root := filepath.Join(dir, "root") + + err = os.MkdirAll(root, 0755) + assert.NilError(t, err) + + hostFileData := []byte("I am a host file") + + // Add a file into a directory above root + // Ensure that we can't access this file while tarring. + err = os.WriteFile(filepath.Join(dir, "host-file"), hostFileData, 0644) + assert.NilError(t, err) + + safe := filepath.Join(root, "safe") + err = unix.Symlink(dir, safe) + assert.NilError(t, err) + + data := filepath.Join(dir, "data") + err = os.MkdirAll(data, 0755) + assert.NilError(t, err) + + type testCase struct { + p string + includes []string + } + + cases := []testCase{ + {p: safe, includes: []string{"host-file"}}, + {p: safe + "/", includes: []string{"host-file"}}, + {p: safe, includes: nil}, + {p: safe + "/", includes: nil}, + {p: root, includes: []string{"safe/host-file"}}, + {p: root, includes: []string{"/safe/host-file"}}, + {p: root, includes: nil}, + } + + maxBytes := len(hostFileData) + + for _, tc := range cases { + t.Run(path.Join(tc.p+"_"+strings.Join(tc.includes, "_")), func(t *testing.T) { + // Here if we use archive.TarWithOptions directly or change the "root" parameter + // to be the same as "safe", data from the host will be leaked into the archive + var opts *archive.TarOptions + if tc.includes != nil { + opts = &archive.TarOptions{ + IncludeFiles: tc.includes, + } + } + rdr, err := Tar(tc.p, opts, root) + assert.NilError(t, err) + defer rdr.Close() + + tr := gotar.NewReader(rdr) + assert.Assert(t, !isDataInTar(t, tr, hostFileData, int64(maxBytes)), "host data leaked to archive") + }) + } +} + +func isDataInTar(t *testing.T, tr *gotar.Reader, compare []byte, maxBytes int64) bool { + for { + h, err := tr.Next() + if err == io.EOF { + break + } + assert.NilError(t, err) + + if h.Size == 0 { + continue + } + assert.Assert(t, h.Size <= maxBytes, "%s: file size exceeds max expected size %d: %d", h.Name, maxBytes, h.Size) + + data := make([]byte, int(h.Size)) + _, err = io.ReadFull(tr, data) + assert.NilError(t, err) + if bytes.Contains(data, compare) { + return true + } + } + + return false +} diff --git a/pkg/chrootarchive/archive_windows.go b/pkg/chrootarchive/archive_windows.go index f2973132a3916..de87113e95448 100644 --- a/pkg/chrootarchive/archive_windows.go +++ b/pkg/chrootarchive/archive_windows.go @@ -14,9 +14,16 @@ func chroot(path string) error { func invokeUnpack(decompressedArchive io.ReadCloser, dest string, - options *archive.TarOptions) error { + options *archive.TarOptions, root string) error { // Windows is different to Linux here because Windows does not support // chroot. Hence there is no point sandboxing a chrooted process to // do the unpack. We call inline instead within the daemon process. return archive.Unpack(decompressedArchive, longpath.AddPrefix(dest), options) } + +func invokePack(srcPath string, options *archive.TarOptions, root string) (io.ReadCloser, error) { + // Windows is different to Linux here because Windows does not support + // chroot. Hence there is no point sandboxing a chrooted process to + // do the pack. We call inline instead within the daemon process. + return archive.TarWithOptions(srcPath, options) +} diff --git a/pkg/chrootarchive/chroot_linux.go b/pkg/chrootarchive/chroot_linux.go index 9802fad5145fc..85c291cdb2e91 100644 --- a/pkg/chrootarchive/chroot_linux.go +++ b/pkg/chrootarchive/chroot_linux.go @@ -2,12 +2,12 @@ package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" import ( "fmt" - "io/ioutil" "os" "path/filepath" - "github.com/docker/docker/pkg/mount" - rsystem "github.com/opencontainers/runc/libcontainer/system" + "github.com/containerd/containerd/pkg/userns" + "github.com/moby/sys/mount" + "github.com/moby/sys/mountinfo" "golang.org/x/sys/unix" ) @@ -19,7 +19,7 @@ import ( // This is similar to how libcontainer sets up a container's rootfs func chroot(path string) (err error) { // if the engine is running in a user namespace we need to use actual chroot - if rsystem.RunningInUserNS() { + if userns.RunningInUserNS() { return realChroot(path) } if err := unix.Unshare(unix.CLONE_NEWNS); err != nil { @@ -36,14 +36,14 @@ func chroot(path string) (err error) { return err } - if mounted, _ := mount.Mounted(path); !mounted { + if mounted, _ := mountinfo.Mounted(path); !mounted { if err := mount.Mount(path, path, "bind", "rbind,rw"); err != nil { return realChroot(path) } } // setup oldRoot for pivot_root - pivotDir, err := ioutil.TempDir(path, ".pivot_root") + pivotDir, err := os.MkdirTemp(path, ".pivot_root") if err != nil { return fmt.Errorf("Error setting up pivot dir: %v", err) } diff --git a/pkg/chrootarchive/chroot_unix.go b/pkg/chrootarchive/chroot_unix.go index 9a1ee58754900..c35aa9166930c 100644 --- a/pkg/chrootarchive/chroot_unix.go +++ b/pkg/chrootarchive/chroot_unix.go @@ -1,3 +1,4 @@ +//go:build !windows && !linux // +build !windows,!linux package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" @@ -10,3 +11,7 @@ func chroot(path string) error { } return unix.Chdir("/") } + +func realChroot(path string) error { + return chroot(path) +} diff --git a/pkg/chrootarchive/diff_unix.go b/pkg/chrootarchive/diff_unix.go index d96a09f8fa7ac..e1bf74d1d5edd 100644 --- a/pkg/chrootarchive/diff_unix.go +++ b/pkg/chrootarchive/diff_unix.go @@ -1,4 +1,5 @@ -//+build !windows +//go:build !windows +// +build !windows package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" @@ -8,15 +9,14 @@ import ( "flag" "fmt" "io" - "io/ioutil" "os" "path/filepath" "runtime" + "github.com/containerd/containerd/pkg/userns" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/system" - rsystem "github.com/opencontainers/runc/libcontainer/system" ) type applyLayerResponse struct { @@ -36,7 +36,7 @@ func applyLayer() { runtime.LockOSThread() flag.Parse() - inUserns := rsystem.RunningInUserNS() + inUserns := userns.RunningInUserNS() if err := chroot(flag.Arg(0)); err != nil { fatal(err) } @@ -56,7 +56,7 @@ func applyLayer() { options.InUserNS = true } - if tmpDir, err = ioutil.TempDir("/", "temp-docker-extract"); err != nil { + if tmpDir, err = os.MkdirTemp("/", "temp-docker-extract"); err != nil { fatal(err) } @@ -95,7 +95,7 @@ func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions } if options == nil { options = &archive.TarOptions{} - if rsystem.RunningInUserNS() { + if userns.RunningInUserNS() { options.InUserNS = true } } diff --git a/pkg/chrootarchive/diff_windows.go b/pkg/chrootarchive/diff_windows.go index 8f3f3a4a8aae8..f423419d3c8e1 100644 --- a/pkg/chrootarchive/diff_windows.go +++ b/pkg/chrootarchive/diff_windows.go @@ -3,7 +3,6 @@ package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" import ( "fmt" "io" - "io/ioutil" "os" "path/filepath" @@ -30,7 +29,7 @@ func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions layer = decompressed } - tmpDir, err := ioutil.TempDir(os.Getenv("temp"), "temp-docker-extract") + tmpDir, err := os.MkdirTemp(os.Getenv("temp"), "temp-docker-extract") if err != nil { return 0, fmt.Errorf("ApplyLayer failed to create temp-docker-extract under %s. %s", dest, err) } diff --git a/pkg/chrootarchive/init_unix.go b/pkg/chrootarchive/init_unix.go index a15e4bb83c40e..0746c1cb97376 100644 --- a/pkg/chrootarchive/init_unix.go +++ b/pkg/chrootarchive/init_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" @@ -5,7 +6,6 @@ package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" import ( "fmt" "io" - "io/ioutil" "os" "github.com/docker/docker/pkg/reexec" @@ -14,6 +14,7 @@ import ( func init() { reexec.Register("docker-applyLayer", applyLayer) reexec.Register("docker-untar", untar) + reexec.Register("docker-tar", tar) } func fatal(err error) { @@ -24,5 +25,5 @@ func fatal(err error) { // flush consumes all the bytes from the reader discarding // any errors func flush(r io.Reader) (bytes int64, err error) { - return io.Copy(ioutil.Discard, r) + return io.Copy(io.Discard, r) } diff --git a/pkg/containerfs/archiver.go b/pkg/containerfs/archiver.go index fed0a07d7b9f8..308e2b88d3af1 100644 --- a/pkg/containerfs/archiver.go +++ b/pkg/containerfs/archiver.go @@ -89,14 +89,14 @@ func (archiver *Archiver) CopyWithTar(src, dst string) error { // CopyFileWithTar emulates the behavior of the 'cp' command-line // for a single file. It copies a regular file from path `src` to // path `dst`, and preserves all its metadata. -func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) { +func (archiver *Archiver) CopyFileWithTar(src, dst string) (retErr error) { logrus.Debugf("CopyFileWithTar(%s, %s)", src, dst) srcDriver := archiver.SrcDriver dstDriver := archiver.DstDriver - srcSt, err := srcDriver.Stat(src) - if err != nil { - return err + srcSt, retErr := srcDriver.Stat(src) + if retErr != nil { + return retErr } if srcSt.IsDir() { @@ -113,7 +113,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) { // os.MkdirAll on not-Windows and changed for Windows. if dstDriver.OS() == "windows" { // Now we are WCOW - if err := system.MkdirAll(filepath.Dir(dst), 0700, ""); err != nil { + if err := system.MkdirAll(filepath.Dir(dst), 0700); err != nil { return err } } else { @@ -168,16 +168,16 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) { }() }() defer func() { - if er := <-errC; err == nil && er != nil { - err = er + if err := <-errC; retErr == nil && err != nil { + retErr = err } }() - err = archiver.Untar(r, dstDriver.Dir(dst), nil) - if err != nil { - r.CloseWithError(err) + retErr = archiver.Untar(r, dstDriver.Dir(dst), nil) + if retErr != nil { + r.CloseWithError(retErr) } - return err + return retErr } // IdentityMapping returns the IdentityMapping of the archiver. @@ -194,7 +194,7 @@ func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error { // chmodTarEntry is used to adjust the file permissions used in tar header based // on the platform the archival is done. func chmodTarEntry(perm os.FileMode) os.FileMode { - //perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.) + // perm &= 0755 // this 0-ed out tar flags (like link, regular file, directory marker etc.) permPart := perm & os.ModePerm noPermPart := perm &^ os.ModePerm // Add the x bit: make everything +x from windows diff --git a/pkg/containerfs/containerfs.go b/pkg/containerfs/containerfs.go index 7bb1d8c3697f8..cf2d39c82ea7a 100644 --- a/pkg/containerfs/containerfs.go +++ b/pkg/containerfs/containerfs.go @@ -6,7 +6,7 @@ import ( "github.com/containerd/continuity/driver" "github.com/containerd/continuity/pathdriver" - "github.com/docker/docker/pkg/symlink" + "github.com/moby/sys/symlink" ) // ContainerFS is that represents a root file system @@ -28,8 +28,7 @@ type ContainerFS interface { // Driver combines both continuity's Driver and PathDriver interfaces with a Platform // field to determine the OS. type Driver interface { - // OS returns the OS where the rootfs is located. Essentially, - // runtime.GOOS for everything aside from LCOW, which is "linux" + // OS returns the OS where the rootfs is located. Essentially, runtime.GOOS. OS() string // Architecture returns the hardware architecture where the diff --git a/pkg/containerfs/containerfs_unix.go b/pkg/containerfs/containerfs_unix.go index 6a99459517dd8..5a7ab97e58c3f 100644 --- a/pkg/containerfs/containerfs_unix.go +++ b/pkg/containerfs/containerfs_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package containerfs // import "github.com/docker/docker/pkg/containerfs" diff --git a/pkg/devicemapper/devmapper.go b/pkg/devicemapper/devmapper.go index 63243637a77ca..05456c67eeac4 100644 --- a/pkg/devicemapper/devmapper.go +++ b/pkg/devicemapper/devmapper.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package devicemapper // import "github.com/docker/docker/pkg/devicemapper" @@ -14,7 +15,7 @@ import ( ) // Same as DM_DEVICE_* enum values from libdevmapper.h -// nolint: deadcode +//nolint: deadcode,unused,varcheck const ( deviceCreate TaskType = iota deviceReload @@ -48,7 +49,6 @@ var ( ErrTaskSetName = errors.New("dm_task_set_name failed") ErrTaskSetMessage = errors.New("dm_task_set_message failed") ErrTaskSetAddNode = errors.New("dm_task_set_add_node failed") - ErrTaskSetRo = errors.New("dm_task_set_ro failed") ErrTaskAddTarget = errors.New("dm_task_add_target failed") ErrTaskSetSector = errors.New("dm_task_set_sector failed") ErrTaskGetDeps = errors.New("dm_task_get_deps failed") @@ -61,13 +61,10 @@ var ( ErrUdevWait = errors.New("wait on udev cookie failed") ErrSetDevDir = errors.New("dm_set_dev_dir failed") ErrGetLibraryVersion = errors.New("dm_get_library_version failed") - ErrCreateRemoveTask = errors.New("Can't create task of type deviceRemove") - ErrRunRemoveDevice = errors.New("running RemoveDevice failed") ErrInvalidAddNode = errors.New("Invalid AddNode type") ErrBusy = errors.New("Device is Busy") ErrDeviceIDExists = errors.New("Device Id Exists") ErrEnxio = errors.New("No such device or address") - ErrEnoData = errors.New("No data available") ) var ( @@ -199,13 +196,6 @@ func (t *Task) setAddNode(addNode AddNodeType) error { return nil } -func (t *Task) setRo() error { - if res := DmTaskSetRo(t.unmanaged); res != 1 { - return ErrTaskSetRo - } - return nil -} - func (t *Task) addTarget(start, size uint64, ttype, params string) error { if res := DmTaskAddTarget(t.unmanaged, start, size, ttype, params); res != 1 { @@ -391,7 +381,7 @@ func CancelDeferredRemove(deviceName string) error { return fmt.Errorf("devicemapper: Can't set sector %s", err) } - if err := task.setMessage(fmt.Sprintf("@cancel_deferred_remove")); err != nil { + if err := task.setMessage("@cancel_deferred_remove"); err != nil { return fmt.Errorf("devicemapper: Can't set message %s", err) } diff --git a/pkg/devicemapper/devmapper_log.go b/pkg/devicemapper/devmapper_log.go index 5a5773d44f71b..aa47368258dcf 100644 --- a/pkg/devicemapper/devmapper_log.go +++ b/pkg/devicemapper/devmapper_log.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package devicemapper // import "github.com/docker/docker/pkg/devicemapper" diff --git a/pkg/devicemapper/devmapper_wrapper.go b/pkg/devicemapper/devmapper_wrapper.go index 0b88f49695cab..103d54e7d56ff 100644 --- a/pkg/devicemapper/devmapper_wrapper.go +++ b/pkg/devicemapper/devmapper_wrapper.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package devicemapper // import "github.com/docker/docker/pkg/devicemapper" @@ -74,7 +75,6 @@ var ( DmTaskSetCookie = dmTaskSetCookieFct DmTaskSetMessage = dmTaskSetMessageFct DmTaskSetName = dmTaskSetNameFct - DmTaskSetRo = dmTaskSetRoFct DmTaskSetSector = dmTaskSetSectorFct DmUdevWait = dmUdevWaitFct DmUdevSetSyncSupport = dmUdevSetSyncSupportFct @@ -132,10 +132,6 @@ func dmTaskSetAddNodeFct(task *cdmTask, addNode AddNodeType) int { return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), C.dm_add_node_t(addNode))) } -func dmTaskSetRoFct(task *cdmTask) int { - return int(C.dm_task_set_ro((*C.struct_dm_task)(task))) -} - func dmTaskAddTargetFct(task *cdmTask, start, size uint64, ttype, params string) int { @@ -155,12 +151,11 @@ func dmTaskGetDepsFct(task *cdmTask) *Deps { } // golang issue: https://github.com/golang/go/issues/11925 - hdr := reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(Cdeps)) + unsafe.Sizeof(*Cdeps))), - Len: int(Cdeps.count), - Cap: int(Cdeps.count), - } - devices := *(*[]C.uint64_t)(unsafe.Pointer(&hdr)) + var devices []C.uint64_t + devicesHdr := (*reflect.SliceHeader)(unsafe.Pointer(&devices)) + devicesHdr.Data = uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(Cdeps)) + unsafe.Sizeof(*Cdeps))) + devicesHdr.Len = int(Cdeps.count) + devicesHdr.Cap = int(Cdeps.count) deps := &Deps{ Count: uint32(Cdeps.count), @@ -211,6 +206,7 @@ func dmGetNextTargetFct(task *cdmTask, next unsafe.Pointer, start, length *uint6 *params = C.GoString(Cparams) }() + //lint:ignore SA4000 false positive on (identical expressions on the left and right side of the '==' operator) (staticcheck) nextp := C.dm_get_next_target((*C.struct_dm_task)(task), next, &Cstart, &Clength, &CtargetType, &Cparams) return nextp } diff --git a/pkg/devicemapper/devmapper_wrapper_dynamic.go b/pkg/devicemapper/devmapper_wrapper_dynamic.go index 8a1098f7d5861..a702cd540ae35 100644 --- a/pkg/devicemapper/devmapper_wrapper_dynamic.go +++ b/pkg/devicemapper/devmapper_wrapper_dynamic.go @@ -1,3 +1,4 @@ +//go:build linux && cgo && !static_build // +build linux,cgo,!static_build package devicemapper // import "github.com/docker/docker/pkg/devicemapper" diff --git a/pkg/devicemapper/devmapper_wrapper_dynamic_deferred_remove.go b/pkg/devicemapper/devmapper_wrapper_dynamic_deferred_remove.go index 3d3021c4e119a..4bfbd01aa8037 100644 --- a/pkg/devicemapper/devmapper_wrapper_dynamic_deferred_remove.go +++ b/pkg/devicemapper/devmapper_wrapper_dynamic_deferred_remove.go @@ -1,5 +1,5 @@ -// +build linux,cgo,!static_build -// +build !libdm_dlsym_deferred_remove,!libdm_no_deferred_remove +//go:build linux && cgo && !static_build && !libdm_dlsym_deferred_remove && !libdm_no_deferred_remove +// +build linux,cgo,!static_build,!libdm_dlsym_deferred_remove,!libdm_no_deferred_remove package devicemapper // import "github.com/docker/docker/pkg/devicemapper" diff --git a/pkg/devicemapper/devmapper_wrapper_dynamic_dlsym_deferred_remove.go b/pkg/devicemapper/devmapper_wrapper_dynamic_dlsym_deferred_remove.go index 5dfb369f1ff85..6db3388c5c2ae 100644 --- a/pkg/devicemapper/devmapper_wrapper_dynamic_dlsym_deferred_remove.go +++ b/pkg/devicemapper/devmapper_wrapper_dynamic_dlsym_deferred_remove.go @@ -1,5 +1,5 @@ -// +build linux,cgo,!static_build -// +build libdm_dlsym_deferred_remove,!libdm_no_deferred_remove +//go:build linux && cgo && !static_build && libdm_dlsym_deferred_remove && !libdm_no_deferred_remove +// +build linux,cgo,!static_build,libdm_dlsym_deferred_remove,!libdm_no_deferred_remove package devicemapper diff --git a/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go b/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go index 8889f0f46fe43..f1aee1abccebd 100644 --- a/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go +++ b/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go @@ -1,5 +1,5 @@ -// +build linux,cgo -// +build !libdm_dlsym_deferred_remove,libdm_no_deferred_remove +//go:build linux && cgo && !libdm_dlsym_deferred_remove && libdm_no_deferred_remove +// +build linux,cgo,!libdm_dlsym_deferred_remove,libdm_no_deferred_remove package devicemapper // import "github.com/docker/docker/pkg/devicemapper" diff --git a/pkg/devicemapper/ioctl.go b/pkg/devicemapper/ioctl.go index ec5a0b33bacd3..508f477d05c52 100644 --- a/pkg/devicemapper/ioctl.go +++ b/pkg/devicemapper/ioctl.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package devicemapper // import "github.com/docker/docker/pkg/devicemapper" diff --git a/pkg/directory/directory.go b/pkg/directory/directory.go index 51d4a6ea2266f..71e8cfee773e0 100644 --- a/pkg/directory/directory.go +++ b/pkg/directory/directory.go @@ -1,7 +1,6 @@ package directory // import "github.com/docker/docker/pkg/directory" import ( - "io/ioutil" "os" "path/filepath" ) @@ -9,7 +8,7 @@ import ( // MoveToSubdir moves all contents of a directory to a subdirectory underneath the original path func MoveToSubdir(oldpath, subdir string) error { - infos, err := ioutil.ReadDir(oldpath) + infos, err := os.ReadDir(oldpath) if err != nil { return err } diff --git a/pkg/directory/directory_test.go b/pkg/directory/directory_test.go index ea62bdf2363e5..8477a202fecb7 100644 --- a/pkg/directory/directory_test.go +++ b/pkg/directory/directory_test.go @@ -2,7 +2,6 @@ package directory // import "github.com/docker/docker/pkg/directory" import ( "context" - "io/ioutil" "os" "path/filepath" "reflect" @@ -14,7 +13,7 @@ import ( func TestSizeEmpty(t *testing.T) { var dir string var err error - if dir, err = ioutil.TempDir(os.TempDir(), "testSizeEmptyDirectory"); err != nil { + if dir, err = os.MkdirTemp(os.TempDir(), "testSizeEmptyDirectory"); err != nil { t.Fatalf("failed to create directory: %s", err) } @@ -28,12 +27,12 @@ func TestSizeEmpty(t *testing.T) { func TestSizeEmptyFile(t *testing.T) { var dir string var err error - if dir, err = ioutil.TempDir(os.TempDir(), "testSizeEmptyFile"); err != nil { + if dir, err = os.MkdirTemp(os.TempDir(), "testSizeEmptyFile"); err != nil { t.Fatalf("failed to create directory: %s", err) } var file *os.File - if file, err = ioutil.TempFile(dir, "file"); err != nil { + if file, err = os.CreateTemp(dir, "file"); err != nil { t.Fatalf("failed to create file: %s", err) } @@ -47,12 +46,12 @@ func TestSizeEmptyFile(t *testing.T) { func TestSizeNonemptyFile(t *testing.T) { var dir string var err error - if dir, err = ioutil.TempDir(os.TempDir(), "testSizeNonemptyFile"); err != nil { + if dir, err = os.MkdirTemp(os.TempDir(), "testSizeNonemptyFile"); err != nil { t.Fatalf("failed to create directory: %s", err) } var file *os.File - if file, err = ioutil.TempFile(dir, "file"); err != nil { + if file, err = os.CreateTemp(dir, "file"); err != nil { t.Fatalf("failed to create file: %s", err) } @@ -69,10 +68,10 @@ func TestSizeNonemptyFile(t *testing.T) { func TestSizeNestedDirectoryEmpty(t *testing.T) { var dir string var err error - if dir, err = ioutil.TempDir(os.TempDir(), "testSizeNestedDirectoryEmpty"); err != nil { + if dir, err = os.MkdirTemp(os.TempDir(), "testSizeNestedDirectoryEmpty"); err != nil { t.Fatalf("failed to create directory: %s", err) } - if dir, err = ioutil.TempDir(dir, "nested"); err != nil { + if dir, err = os.MkdirTemp(dir, "nested"); err != nil { t.Fatalf("failed to create nested directory: %s", err) } @@ -86,15 +85,15 @@ func TestSizeNestedDirectoryEmpty(t *testing.T) { func TestSizeFileAndNestedDirectoryEmpty(t *testing.T) { var dir string var err error - if dir, err = ioutil.TempDir(os.TempDir(), "testSizeFileAndNestedDirectoryEmpty"); err != nil { + if dir, err = os.MkdirTemp(os.TempDir(), "testSizeFileAndNestedDirectoryEmpty"); err != nil { t.Fatalf("failed to create directory: %s", err) } - if dir, err = ioutil.TempDir(dir, "nested"); err != nil { + if dir, err = os.MkdirTemp(dir, "nested"); err != nil { t.Fatalf("failed to create nested directory: %s", err) } var file *os.File - if file, err = ioutil.TempFile(dir, "file"); err != nil { + if file, err = os.CreateTemp(dir, "file"); err != nil { t.Fatalf("failed to create file: %s", err) } @@ -111,15 +110,15 @@ func TestSizeFileAndNestedDirectoryEmpty(t *testing.T) { func TestSizeFileAndNestedDirectoryNonempty(t *testing.T) { var dir, dirNested string var err error - if dir, err = ioutil.TempDir(os.TempDir(), "TestSizeFileAndNestedDirectoryNonempty"); err != nil { + if dir, err = os.MkdirTemp(os.TempDir(), "TestSizeFileAndNestedDirectoryNonempty"); err != nil { t.Fatalf("failed to create directory: %s", err) } - if dirNested, err = ioutil.TempDir(dir, "nested"); err != nil { + if dirNested, err = os.MkdirTemp(dir, "nested"); err != nil { t.Fatalf("failed to create nested directory: %s", err) } var file *os.File - if file, err = ioutil.TempFile(dir, "file"); err != nil { + if file, err = os.CreateTemp(dir, "file"); err != nil { t.Fatalf("failed to create file: %s", err) } @@ -127,7 +126,7 @@ func TestSizeFileAndNestedDirectoryNonempty(t *testing.T) { file.Write(data) var nestedFile *os.File - if nestedFile, err = ioutil.TempFile(dirNested, "file"); err != nil { + if nestedFile, err = os.CreateTemp(dirNested, "file"); err != nil { t.Fatalf("failed to create file in nested directory: %s", err) } @@ -145,11 +144,11 @@ func TestMoveToSubdir(t *testing.T) { var outerDir, subDir string var err error - if outerDir, err = ioutil.TempDir(os.TempDir(), "TestMoveToSubdir"); err != nil { + if outerDir, err = os.MkdirTemp(os.TempDir(), "TestMoveToSubdir"); err != nil { t.Fatalf("failed to create directory: %v", err) } - if subDir, err = ioutil.TempDir(outerDir, "testSub"); err != nil { + if subDir, err = os.MkdirTemp(outerDir, "testSub"); err != nil { t.Fatalf("failed to create subdirectory: %v", err) } @@ -168,7 +167,7 @@ func TestMoveToSubdir(t *testing.T) { t.Fatalf("Error during migration of content to subdirectory: %v", err) } // validate that the files were moved to the subdirectory - infos, err := ioutil.ReadDir(subDir) + infos, err := os.ReadDir(subDir) if err != nil { t.Fatal(err) } @@ -179,7 +178,7 @@ func TestMoveToSubdir(t *testing.T) { for _, info := range infos { results = append(results, info.Name()) } - sort.Sort(sort.StringSlice(results)) + sort.Strings(results) if !reflect.DeepEqual(filesList, results) { t.Fatalf("Results after migration do not equal list of files: expected: %v, got: %v", filesList, results) } diff --git a/pkg/directory/directory_unix.go b/pkg/directory/directory_unix.go index f56dd7a8f9dc5..eeedff18a4655 100644 --- a/pkg/directory/directory_unix.go +++ b/pkg/directory/directory_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd || darwin // +build linux freebsd darwin package directory // import "github.com/docker/docker/pkg/directory" diff --git a/pkg/discovery/discovery_test.go b/pkg/discovery/discovery_test.go index ffe8cb9122fa6..ffd9b29352c6b 100644 --- a/pkg/discovery/discovery_test.go +++ b/pkg/discovery/discovery_test.go @@ -3,109 +3,107 @@ package discovery // import "github.com/docker/docker/pkg/discovery" import ( "testing" - "github.com/go-check/check" + "github.com/docker/docker/internal/test/suite" + "gotest.tools/v3/assert" ) // Hook up gocheck into the "go test" runner. -func Test(t *testing.T) { check.TestingT(t) } +func Test(t *testing.T) { + suite.Run(t, &DiscoverySuite{}) +} type DiscoverySuite struct{} -var _ = check.Suite(&DiscoverySuite{}) - -func (s *DiscoverySuite) TestNewEntry(c *check.C) { +func (s *DiscoverySuite) TestNewEntry(c *testing.T) { entry, err := NewEntry("127.0.0.1:2375") - c.Assert(err, check.IsNil) - c.Assert(entry.Equals(&Entry{Host: "127.0.0.1", Port: "2375"}), check.Equals, true) - c.Assert(entry.String(), check.Equals, "127.0.0.1:2375") + assert.Assert(c, err == nil) + assert.Equal(c, entry.Equals(&Entry{Host: "127.0.0.1", Port: "2375"}), true) + assert.Equal(c, entry.String(), "127.0.0.1:2375") entry, err = NewEntry("[2001:db8:0:f101::2]:2375") - c.Assert(err, check.IsNil) - c.Assert(entry.Equals(&Entry{Host: "2001:db8:0:f101::2", Port: "2375"}), check.Equals, true) - c.Assert(entry.String(), check.Equals, "[2001:db8:0:f101::2]:2375") + assert.Assert(c, err == nil) + assert.Equal(c, entry.Equals(&Entry{Host: "2001:db8:0:f101::2", Port: "2375"}), true) + assert.Equal(c, entry.String(), "[2001:db8:0:f101::2]:2375") _, err = NewEntry("127.0.0.1") - c.Assert(err, check.NotNil) + assert.Assert(c, err != nil) } -func (s *DiscoverySuite) TestParse(c *check.C) { +func (s *DiscoverySuite) TestParse(c *testing.T) { scheme, uri := parse("127.0.0.1:2375") - c.Assert(scheme, check.Equals, "nodes") - c.Assert(uri, check.Equals, "127.0.0.1:2375") + assert.Equal(c, scheme, "nodes") + assert.Equal(c, uri, "127.0.0.1:2375") scheme, uri = parse("localhost:2375") - c.Assert(scheme, check.Equals, "nodes") - c.Assert(uri, check.Equals, "localhost:2375") + assert.Equal(c, scheme, "nodes") + assert.Equal(c, uri, "localhost:2375") scheme, uri = parse("scheme://127.0.0.1:2375") - c.Assert(scheme, check.Equals, "scheme") - c.Assert(uri, check.Equals, "127.0.0.1:2375") + assert.Equal(c, scheme, "scheme") + assert.Equal(c, uri, "127.0.0.1:2375") scheme, uri = parse("scheme://localhost:2375") - c.Assert(scheme, check.Equals, "scheme") - c.Assert(uri, check.Equals, "localhost:2375") + assert.Equal(c, scheme, "scheme") + assert.Equal(c, uri, "localhost:2375") scheme, uri = parse("") - c.Assert(scheme, check.Equals, "nodes") - c.Assert(uri, check.Equals, "") + assert.Equal(c, scheme, "nodes") + assert.Equal(c, uri, "") } -func (s *DiscoverySuite) TestCreateEntries(c *check.C) { +func (s *DiscoverySuite) TestCreateEntries(c *testing.T) { entries, err := CreateEntries(nil) - c.Assert(entries, check.DeepEquals, Entries{}) - c.Assert(err, check.IsNil) + assert.DeepEqual(c, entries, Entries{}) + assert.Assert(c, err == nil) entries, err = CreateEntries([]string{"127.0.0.1:2375", "127.0.0.2:2375", "[2001:db8:0:f101::2]:2375", ""}) - c.Assert(err, check.IsNil) + assert.Assert(c, err == nil) expected := Entries{ &Entry{Host: "127.0.0.1", Port: "2375"}, &Entry{Host: "127.0.0.2", Port: "2375"}, &Entry{Host: "2001:db8:0:f101::2", Port: "2375"}, } - c.Assert(entries.Equals(expected), check.Equals, true) + assert.Equal(c, entries.Equals(expected), true) _, err = CreateEntries([]string{"127.0.0.1", "127.0.0.2"}) - c.Assert(err, check.NotNil) + assert.Assert(c, err != nil) } -func (s *DiscoverySuite) TestContainsEntry(c *check.C) { +func (s *DiscoverySuite) TestContainsEntry(c *testing.T) { entries, err := CreateEntries([]string{"127.0.0.1:2375", "127.0.0.2:2375", ""}) - c.Assert(err, check.IsNil) - c.Assert(entries.Contains(&Entry{Host: "127.0.0.1", Port: "2375"}), check.Equals, true) - c.Assert(entries.Contains(&Entry{Host: "127.0.0.3", Port: "2375"}), check.Equals, false) + assert.Assert(c, err == nil) + assert.Equal(c, entries.Contains(&Entry{Host: "127.0.0.1", Port: "2375"}), true) + assert.Equal(c, entries.Contains(&Entry{Host: "127.0.0.3", Port: "2375"}), false) } -func (s *DiscoverySuite) TestEntriesEquality(c *check.C) { +func (s *DiscoverySuite) TestEntriesEquality(c *testing.T) { entries := Entries{ &Entry{Host: "127.0.0.1", Port: "2375"}, &Entry{Host: "127.0.0.2", Port: "2375"}, } // Same - c.Assert(entries.Equals(Entries{ + assert.Assert(c, entries.Equals(Entries{ &Entry{Host: "127.0.0.1", Port: "2375"}, &Entry{Host: "127.0.0.2", Port: "2375"}, - }), check. - Equals, true) + })) // Different size - c.Assert(entries.Equals(Entries{ + assert.Assert(c, !entries.Equals(Entries{ &Entry{Host: "127.0.0.1", Port: "2375"}, &Entry{Host: "127.0.0.2", Port: "2375"}, &Entry{Host: "127.0.0.3", Port: "2375"}, - }), check. - Equals, false) + })) // Different content - c.Assert(entries.Equals(Entries{ + assert.Assert(c, !entries.Equals(Entries{ &Entry{Host: "127.0.0.1", Port: "2375"}, &Entry{Host: "127.0.0.42", Port: "2375"}, - }), check. - Equals, false) + })) } -func (s *DiscoverySuite) TestEntriesDiff(c *check.C) { +func (s *DiscoverySuite) TestEntriesDiff(c *testing.T) { entry1 := &Entry{Host: "1.1.1.1", Port: "1111"} entry2 := &Entry{Host: "2.2.2.2", Port: "2222"} entry3 := &Entry{Host: "3.3.3.3", Port: "3333"} @@ -113,25 +111,25 @@ func (s *DiscoverySuite) TestEntriesDiff(c *check.C) { // No diff added, removed := entries.Diff(Entries{entry2, entry1}) - c.Assert(added, check.HasLen, 0) - c.Assert(removed, check.HasLen, 0) + assert.Equal(c, len(added), 0) + assert.Equal(c, len(removed), 0) // Add added, removed = entries.Diff(Entries{entry2, entry3, entry1}) - c.Assert(added, check.HasLen, 1) - c.Assert(added.Contains(entry3), check.Equals, true) - c.Assert(removed, check.HasLen, 0) + assert.Equal(c, len(added), 1) + assert.Equal(c, added.Contains(entry3), true) + assert.Equal(c, len(removed), 0) // Remove added, removed = entries.Diff(Entries{entry2}) - c.Assert(added, check.HasLen, 0) - c.Assert(removed, check.HasLen, 1) - c.Assert(removed.Contains(entry1), check.Equals, true) + assert.Equal(c, len(added), 0) + assert.Equal(c, len(removed), 1) + assert.Equal(c, removed.Contains(entry1), true) // Add and remove added, removed = entries.Diff(Entries{entry1, entry3}) - c.Assert(added, check.HasLen, 1) - c.Assert(added.Contains(entry3), check.Equals, true) - c.Assert(removed, check.HasLen, 1) - c.Assert(removed.Contains(entry2), check.Equals, true) + assert.Equal(c, len(added), 1) + assert.Equal(c, added.Contains(entry3), true) + assert.Equal(c, len(removed), 1) + assert.Equal(c, removed.Contains(entry2), true) } diff --git a/pkg/discovery/file/file.go b/pkg/discovery/file/file.go index 1494af485fe19..49662d15fe5cf 100644 --- a/pkg/discovery/file/file.go +++ b/pkg/discovery/file/file.go @@ -2,7 +2,7 @@ package file // import "github.com/docker/docker/pkg/discovery/file" import ( "fmt" - "io/ioutil" + "os" "strings" "time" @@ -51,7 +51,7 @@ func parseFileContent(content []byte) []string { } func (s *Discovery) fetch() (discovery.Entries, error) { - fileContent, err := ioutil.ReadFile(s.path) + fileContent, err := os.ReadFile(s.path) if err != nil { return nil, fmt.Errorf("failed to read '%s': %v", s.path, err) } @@ -60,8 +60,8 @@ func (s *Discovery) fetch() (discovery.Entries, error) { // Watch is exported func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-chan error) { - ch := make(chan discovery.Entries) - errCh := make(chan error) + ch := make(chan discovery.Entries, 1) + errCh := make(chan error, 1) ticker := time.NewTicker(s.heartbeat) go func() { diff --git a/pkg/discovery/file/file_test.go b/pkg/discovery/file/file_test.go index 010e941c2ac3f..fdd5715a51ab8 100644 --- a/pkg/discovery/file/file_test.go +++ b/pkg/discovery/file/file_test.go @@ -1,54 +1,53 @@ package file // import "github.com/docker/docker/pkg/discovery/file" import ( - "io/ioutil" "os" "testing" + "github.com/docker/docker/internal/test/suite" "github.com/docker/docker/pkg/discovery" - - "github.com/go-check/check" + "gotest.tools/v3/assert" ) // Hook up gocheck into the "go test" runner. -func Test(t *testing.T) { check.TestingT(t) } +func Test(t *testing.T) { + suite.Run(t, &DiscoverySuite{}) +} type DiscoverySuite struct{} -var _ = check.Suite(&DiscoverySuite{}) - -func (s *DiscoverySuite) TestInitialize(c *check.C) { +func (s *DiscoverySuite) TestInitialize(c *testing.T) { d := &Discovery{} d.Initialize("/path/to/file", 1000, 0, nil) - c.Assert(d.path, check.Equals, "/path/to/file") + assert.Equal(c, d.path, "/path/to/file") } -func (s *DiscoverySuite) TestNew(c *check.C) { +func (s *DiscoverySuite) TestNew(c *testing.T) { d, err := discovery.New("file:///path/to/file", 0, 0, nil) - c.Assert(err, check.IsNil) - c.Assert(d.(*Discovery).path, check.Equals, "/path/to/file") + assert.Assert(c, err == nil) + assert.Equal(c, d.(*Discovery).path, "/path/to/file") } -func (s *DiscoverySuite) TestContent(c *check.C) { +func (s *DiscoverySuite) TestContent(c *testing.T) { data := ` 1.1.1.[1:2]:1111 2.2.2.[2:4]:2222 ` ips := parseFileContent([]byte(data)) - c.Assert(ips, check.HasLen, 5) - c.Assert(ips[0], check.Equals, "1.1.1.1:1111") - c.Assert(ips[1], check.Equals, "1.1.1.2:1111") - c.Assert(ips[2], check.Equals, "2.2.2.2:2222") - c.Assert(ips[3], check.Equals, "2.2.2.3:2222") - c.Assert(ips[4], check.Equals, "2.2.2.4:2222") + assert.Equal(c, len(ips), 5) + assert.Equal(c, ips[0], "1.1.1.1:1111") + assert.Equal(c, ips[1], "1.1.1.2:1111") + assert.Equal(c, ips[2], "2.2.2.2:2222") + assert.Equal(c, ips[3], "2.2.2.3:2222") + assert.Equal(c, ips[4], "2.2.2.4:2222") } -func (s *DiscoverySuite) TestRegister(c *check.C) { +func (s *DiscoverySuite) TestRegister(c *testing.T) { discovery := &Discovery{path: "/path/to/file"} - c.Assert(discovery.Register("0.0.0.0"), check.NotNil) + assert.Assert(c, discovery.Register("0.0.0.0") != nil) } -func (s *DiscoverySuite) TestParsingContentsWithComments(c *check.C) { +func (s *DiscoverySuite) TestParsingContentsWithComments(c *testing.T) { data := ` ### test ### 1.1.1.1:1111 # inline comment @@ -58,12 +57,12 @@ func (s *DiscoverySuite) TestParsingContentsWithComments(c *check.C) { ### test ### ` ips := parseFileContent([]byte(data)) - c.Assert(ips, check.HasLen, 2) - c.Assert("1.1.1.1:1111", check.Equals, ips[0]) - c.Assert("3.3.3.3:3333", check.Equals, ips[1]) + assert.Equal(c, len(ips), 2) + assert.Equal(c, "1.1.1.1:1111", ips[0]) + assert.Equal(c, "3.3.3.3:3333", ips[1]) } -func (s *DiscoverySuite) TestWatch(c *check.C) { +func (s *DiscoverySuite) TestWatch(c *testing.T) { data := ` 1.1.1.1:1111 2.2.2.2:2222 @@ -74,10 +73,10 @@ func (s *DiscoverySuite) TestWatch(c *check.C) { } // Create a temporary file and remove it. - tmp, err := ioutil.TempFile(os.TempDir(), "discovery-file-test") - c.Assert(err, check.IsNil) - c.Assert(tmp.Close(), check.IsNil) - c.Assert(os.Remove(tmp.Name()), check.IsNil) + tmp, err := os.CreateTemp(os.TempDir(), "discovery-file-test") + assert.Assert(c, err == nil) + assert.Assert(c, tmp.Close() == nil) + assert.Assert(c, os.Remove(tmp.Name()) == nil) // Set up file discovery. d := &Discovery{} @@ -86,7 +85,7 @@ func (s *DiscoverySuite) TestWatch(c *check.C) { ch, errCh := d.Watch(stopCh) // Make sure it fires errors since the file doesn't exist. - c.Assert(<-errCh, check.NotNil) + assert.Assert(c, <-errCh != nil) // We have to drain the error channel otherwise Watch will get stuck. go func() { for range errCh { @@ -94,21 +93,21 @@ func (s *DiscoverySuite) TestWatch(c *check.C) { }() // Write the file and make sure we get the expected value back. - c.Assert(ioutil.WriteFile(tmp.Name(), []byte(data), 0600), check.IsNil) - c.Assert(<-ch, check.DeepEquals, expected) + assert.Assert(c, os.WriteFile(tmp.Name(), []byte(data), 0600) == nil) + assert.DeepEqual(c, <-ch, expected) // Add a new entry and look it up. expected = append(expected, &discovery.Entry{Host: "3.3.3.3", Port: "3333"}) f, err := os.OpenFile(tmp.Name(), os.O_APPEND|os.O_WRONLY, 0600) - c.Assert(err, check.IsNil) - c.Assert(f, check.NotNil) + assert.Assert(c, err == nil) + assert.Assert(c, f != nil) _, err = f.WriteString("\n3.3.3.3:3333\n") - c.Assert(err, check.IsNil) + assert.Assert(c, err == nil) f.Close() - c.Assert(<-ch, check.DeepEquals, expected) + assert.DeepEqual(c, <-ch, expected) // Stop and make sure it closes all channels. close(stopCh) - c.Assert(<-ch, check.IsNil) - c.Assert(<-errCh, check.IsNil) + assert.Assert(c, <-ch == nil) + assert.Assert(c, <-errCh == nil) } diff --git a/pkg/discovery/generator_test.go b/pkg/discovery/generator_test.go index 5126df576e8a4..e4d79629a16a9 100644 --- a/pkg/discovery/generator_test.go +++ b/pkg/discovery/generator_test.go @@ -1,53 +1,54 @@ package discovery // import "github.com/docker/docker/pkg/discovery" - import ( - "github.com/go-check/check" + "testing" + + "gotest.tools/v3/assert" ) -func (s *DiscoverySuite) TestGeneratorNotGenerate(c *check.C) { +func (s *DiscoverySuite) TestGeneratorNotGenerate(c *testing.T) { ips := Generate("127.0.0.1") - c.Assert(len(ips), check.Equals, 1) - c.Assert(ips[0], check.Equals, "127.0.0.1") + assert.Equal(c, len(ips), 1) + assert.Equal(c, ips[0], "127.0.0.1") } -func (s *DiscoverySuite) TestGeneratorWithPortNotGenerate(c *check.C) { +func (s *DiscoverySuite) TestGeneratorWithPortNotGenerate(c *testing.T) { ips := Generate("127.0.0.1:8080") - c.Assert(len(ips), check.Equals, 1) - c.Assert(ips[0], check.Equals, "127.0.0.1:8080") + assert.Equal(c, len(ips), 1) + assert.Equal(c, ips[0], "127.0.0.1:8080") } -func (s *DiscoverySuite) TestGeneratorMatchFailedNotGenerate(c *check.C) { +func (s *DiscoverySuite) TestGeneratorMatchFailedNotGenerate(c *testing.T) { ips := Generate("127.0.0.[1]") - c.Assert(len(ips), check.Equals, 1) - c.Assert(ips[0], check.Equals, "127.0.0.[1]") + assert.Equal(c, len(ips), 1) + assert.Equal(c, ips[0], "127.0.0.[1]") } -func (s *DiscoverySuite) TestGeneratorWithPort(c *check.C) { +func (s *DiscoverySuite) TestGeneratorWithPort(c *testing.T) { ips := Generate("127.0.0.[1:11]:2375") - c.Assert(len(ips), check.Equals, 11) - c.Assert(ips[0], check.Equals, "127.0.0.1:2375") - c.Assert(ips[1], check.Equals, "127.0.0.2:2375") - c.Assert(ips[2], check.Equals, "127.0.0.3:2375") - c.Assert(ips[3], check.Equals, "127.0.0.4:2375") - c.Assert(ips[4], check.Equals, "127.0.0.5:2375") - c.Assert(ips[5], check.Equals, "127.0.0.6:2375") - c.Assert(ips[6], check.Equals, "127.0.0.7:2375") - c.Assert(ips[7], check.Equals, "127.0.0.8:2375") - c.Assert(ips[8], check.Equals, "127.0.0.9:2375") - c.Assert(ips[9], check.Equals, "127.0.0.10:2375") - c.Assert(ips[10], check.Equals, "127.0.0.11:2375") + assert.Equal(c, len(ips), 11) + assert.Equal(c, ips[0], "127.0.0.1:2375") + assert.Equal(c, ips[1], "127.0.0.2:2375") + assert.Equal(c, ips[2], "127.0.0.3:2375") + assert.Equal(c, ips[3], "127.0.0.4:2375") + assert.Equal(c, ips[4], "127.0.0.5:2375") + assert.Equal(c, ips[5], "127.0.0.6:2375") + assert.Equal(c, ips[6], "127.0.0.7:2375") + assert.Equal(c, ips[7], "127.0.0.8:2375") + assert.Equal(c, ips[8], "127.0.0.9:2375") + assert.Equal(c, ips[9], "127.0.0.10:2375") + assert.Equal(c, ips[10], "127.0.0.11:2375") } -func (s *DiscoverySuite) TestGenerateWithMalformedInputAtRangeStart(c *check.C) { +func (s *DiscoverySuite) TestGenerateWithMalformedInputAtRangeStart(c *testing.T) { malformedInput := "127.0.0.[x:11]:2375" ips := Generate(malformedInput) - c.Assert(len(ips), check.Equals, 1) - c.Assert(ips[0], check.Equals, malformedInput) + assert.Equal(c, len(ips), 1) + assert.Equal(c, ips[0], malformedInput) } -func (s *DiscoverySuite) TestGenerateWithMalformedInputAtRangeEnd(c *check.C) { +func (s *DiscoverySuite) TestGenerateWithMalformedInputAtRangeEnd(c *testing.T) { malformedInput := "127.0.0.[1:x]:2375" ips := Generate(malformedInput) - c.Assert(len(ips), check.Equals, 1) - c.Assert(ips[0], check.Equals, malformedInput) + assert.Equal(c, len(ips), 1) + assert.Equal(c, ips[0], malformedInput) } diff --git a/pkg/discovery/kv/kv_test.go b/pkg/discovery/kv/kv_test.go index 79fd91c61fb7f..a944e3cf3c7a7 100644 --- a/pkg/discovery/kv/kv_test.go +++ b/pkg/discovery/kv/kv_test.go @@ -2,26 +2,26 @@ package kv // import "github.com/docker/docker/pkg/discovery/kv" import ( "errors" - "io/ioutil" "os" "path" "testing" "time" + "github.com/docker/docker/internal/test/suite" "github.com/docker/docker/pkg/discovery" "github.com/docker/libkv" "github.com/docker/libkv/store" - "github.com/go-check/check" + "gotest.tools/v3/assert" ) // Hook up gocheck into the "go test" runner. -func Test(t *testing.T) { check.TestingT(t) } +func Test(t *testing.T) { + suite.Run(t, &DiscoverySuite{}) +} type DiscoverySuite struct{} -var _ = check.Suite(&DiscoverySuite{}) - -func (ds *DiscoverySuite) TestInitialize(c *check.C) { +func (ds *DiscoverySuite) TestInitialize(c *testing.T) { storeMock := &FakeStore{ Endpoints: []string{"127.0.0.1"}, } @@ -30,9 +30,9 @@ func (ds *DiscoverySuite) TestInitialize(c *check.C) { d.store = storeMock s := d.store.(*FakeStore) - c.Assert(s.Endpoints, check.HasLen, 1) - c.Assert(s.Endpoints[0], check.Equals, "127.0.0.1") - c.Assert(d.path, check.Equals, defaultDiscoveryPath) + assert.Equal(c, len(s.Endpoints), 1) + assert.Equal(c, s.Endpoints[0], "127.0.0.1") + assert.Equal(c, d.path, defaultDiscoveryPath) storeMock = &FakeStore{ Endpoints: []string{"127.0.0.1:1234"}, @@ -42,9 +42,9 @@ func (ds *DiscoverySuite) TestInitialize(c *check.C) { d.store = storeMock s = d.store.(*FakeStore) - c.Assert(s.Endpoints, check.HasLen, 1) - c.Assert(s.Endpoints[0], check.Equals, "127.0.0.1:1234") - c.Assert(d.path, check.Equals, "path/"+defaultDiscoveryPath) + assert.Equal(c, len(s.Endpoints), 1) + assert.Equal(c, s.Endpoints[0], "127.0.0.1:1234") + assert.Equal(c, d.path, "path/"+defaultDiscoveryPath) storeMock = &FakeStore{ Endpoints: []string{"127.0.0.1:1234", "127.0.0.2:1234", "127.0.0.3:1234"}, @@ -54,12 +54,12 @@ func (ds *DiscoverySuite) TestInitialize(c *check.C) { d.store = storeMock s = d.store.(*FakeStore) - c.Assert(s.Endpoints, check.HasLen, 3) - c.Assert(s.Endpoints[0], check.Equals, "127.0.0.1:1234") - c.Assert(s.Endpoints[1], check.Equals, "127.0.0.2:1234") - c.Assert(s.Endpoints[2], check.Equals, "127.0.0.3:1234") + assert.Equal(c, len(s.Endpoints), 3) + assert.Equal(c, s.Endpoints[0], "127.0.0.1:1234") + assert.Equal(c, s.Endpoints[1], "127.0.0.2:1234") + assert.Equal(c, s.Endpoints[2], "127.0.0.3:1234") - c.Assert(d.path, check.Equals, "path/"+defaultDiscoveryPath) + assert.Equal(c, d.path, "path/"+defaultDiscoveryPath) } // Extremely limited mock store so we can test initialization @@ -131,7 +131,7 @@ func (s *Mock) AtomicDelete(key string, previous *store.KVPair) (bool, error) { func (s *Mock) Close() { } -func (ds *DiscoverySuite) TestInitializeWithCerts(c *check.C) { +func (ds *DiscoverySuite) TestInitializeWithCerts(c *testing.T) { cert := `-----BEGIN CERTIFICATE----- MIIDCDCCAfKgAwIBAgIICifG7YeiQOEwCwYJKoZIhvcNAQELMBIxEDAOBgNVBAMT B1Rlc3QgQ0EwHhcNMTUxMDAxMjMwMDAwWhcNMjAwOTI5MjMwMDAwWjASMRAwDgYD @@ -180,13 +180,13 @@ dyT7ut01PL6RaW4SeQWtrJIVQaM6vF3pprMKqlc5XihOGAmVqH7rQx9rtQB5TicL BFrwkQE4HQtQBV60hYQUzzlSk44VFDz+jxIEtacRHaomDRh2FtOTz+I= -----END RSA PRIVATE KEY----- ` - certFile, err := ioutil.TempFile("", "cert") - c.Assert(err, check.IsNil) + certFile, err := os.CreateTemp("", "cert") + assert.Assert(c, err == nil) defer os.Remove(certFile.Name()) certFile.Write([]byte(cert)) certFile.Close() - keyFile, err := ioutil.TempFile("", "key") - c.Assert(err, check.IsNil) + keyFile, err := os.CreateTemp("", "key") + assert.Assert(c, err == nil) defer os.Remove(keyFile.Name()) keyFile.Write([]byte(key)) keyFile.Close() @@ -198,14 +198,14 @@ BFrwkQE4HQtQBV60hYQUzzlSk44VFDz+jxIEtacRHaomDRh2FtOTz+I= "kv.certfile": certFile.Name(), "kv.keyfile": keyFile.Name(), }) - c.Assert(err, check.IsNil) + assert.Assert(c, err == nil) s := d.store.(*Mock) - c.Assert(s.Options.TLS, check.NotNil) - c.Assert(s.Options.TLS.RootCAs, check.NotNil) - c.Assert(s.Options.TLS.Certificates, check.HasLen, 1) + assert.Assert(c, s.Options.TLS != nil) + assert.Assert(c, s.Options.TLS.RootCAs != nil) + assert.Equal(c, len(s.Options.TLS.Certificates), 1) } -func (ds *DiscoverySuite) TestWatch(c *check.C) { +func (ds *DiscoverySuite) TestWatch(c *testing.T) { mockCh := make(chan []*store.KVPair) storeMock := &FakeStore{ @@ -230,7 +230,7 @@ func (ds *DiscoverySuite) TestWatch(c *check.C) { ch, errCh := d.Watch(stopCh) // It should fire an error since the first WatchTree call failed. - c.Assert(<-errCh, check.ErrorMatches, "test error") + assert.ErrorContains(c, <-errCh, "test error") // We have to drain the error channel otherwise Watch will get stuck. go func() { for range errCh { @@ -239,13 +239,13 @@ func (ds *DiscoverySuite) TestWatch(c *check.C) { // Push the entries into the store channel and make sure discovery emits. mockCh <- kvs - c.Assert(<-ch, check.DeepEquals, expected) + assert.DeepEqual(c, <-ch, expected) // Add a new entry. expected = append(expected, &discovery.Entry{Host: "3.3.3.3", Port: "3333"}) kvs = append(kvs, &store.KVPair{Key: path.Join("path", defaultDiscoveryPath, "3.3.3.3"), Value: []byte("3.3.3.3:3333")}) mockCh <- kvs - c.Assert(<-ch, check.DeepEquals, expected) + assert.DeepEqual(c, <-ch, expected) close(mockCh) // Give it enough time to call WatchTree. @@ -253,8 +253,8 @@ func (ds *DiscoverySuite) TestWatch(c *check.C) { // Stop and make sure it closes all channels. close(stopCh) - c.Assert(<-ch, check.IsNil) - c.Assert(<-errCh, check.IsNil) + assert.Assert(c, <-ch == nil) + assert.Assert(c, <-errCh == nil) } // FakeStore implements store.Store methods. It mocks all store diff --git a/pkg/discovery/memory/memory.go b/pkg/discovery/memory/memory.go index 81f973e285a3e..08b2ef1c342f4 100644 --- a/pkg/discovery/memory/memory.go +++ b/pkg/discovery/memory/memory.go @@ -33,8 +33,8 @@ func (s *Discovery) Initialize(_ string, heartbeat time.Duration, _ time.Duratio // Watch sends periodic discovery updates to a channel. func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-chan error) { - ch := make(chan discovery.Entries) - errCh := make(chan error) + ch := make(chan discovery.Entries, 1) + errCh := make(chan error, 1) ticker := time.NewTicker(s.heartbeat) go func() { diff --git a/pkg/discovery/memory/memory_test.go b/pkg/discovery/memory/memory_test.go index 1d937f01607d3..9907505dcc24e 100644 --- a/pkg/discovery/memory/memory_test.go +++ b/pkg/discovery/memory/memory_test.go @@ -3,18 +3,19 @@ package memory // import "github.com/docker/docker/pkg/discovery/memory" import ( "testing" + "github.com/docker/docker/internal/test/suite" "github.com/docker/docker/pkg/discovery" - "github.com/go-check/check" + "gotest.tools/v3/assert" ) // Hook up gocheck into the "go test" runner. -func Test(t *testing.T) { check.TestingT(t) } +func Test(t *testing.T) { + suite.Run(t, &discoverySuite{}) +} type discoverySuite struct{} -var _ = check.Suite(&discoverySuite{}) - -func (s *discoverySuite) TestWatch(c *check.C) { +func (s *discoverySuite) TestWatch(c *testing.T) { d := &Discovery{} d.Initialize("foo", 1000, 0, nil) stopCh := make(chan struct{}) @@ -30,19 +31,19 @@ func (s *discoverySuite) TestWatch(c *check.C) { &discovery.Entry{Host: "1.1.1.1", Port: "1111"}, } - c.Assert(d.Register("1.1.1.1:1111"), check.IsNil) - c.Assert(<-ch, check.DeepEquals, expected) + assert.Assert(c, d.Register("1.1.1.1:1111") == nil) + assert.DeepEqual(c, <-ch, expected) expected = discovery.Entries{ &discovery.Entry{Host: "1.1.1.1", Port: "1111"}, &discovery.Entry{Host: "2.2.2.2", Port: "2222"}, } - c.Assert(d.Register("2.2.2.2:2222"), check.IsNil) - c.Assert(<-ch, check.DeepEquals, expected) + assert.Assert(c, d.Register("2.2.2.2:2222") == nil) + assert.DeepEqual(c, <-ch, expected) // Stop and make sure it closes all channels. close(stopCh) - c.Assert(<-ch, check.IsNil) - c.Assert(<-errCh, check.IsNil) + assert.Assert(c, <-ch == nil) + assert.Assert(c, <-errCh == nil) } diff --git a/pkg/discovery/nodes/nodes.go b/pkg/discovery/nodes/nodes.go index b1d45aa2e6c41..299ca46f2eb6e 100644 --- a/pkg/discovery/nodes/nodes.go +++ b/pkg/discovery/nodes/nodes.go @@ -39,7 +39,7 @@ func (s *Discovery) Initialize(uris string, _ time.Duration, _ time.Duration, _ // Watch is exported func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-chan error) { - ch := make(chan discovery.Entries) + ch := make(chan discovery.Entries, 1) go func() { defer close(ch) ch <- s.entries diff --git a/pkg/discovery/nodes/nodes_test.go b/pkg/discovery/nodes/nodes_test.go index f9b43ab00bb44..a5846e0111155 100644 --- a/pkg/discovery/nodes/nodes_test.go +++ b/pkg/discovery/nodes/nodes_test.go @@ -3,38 +3,38 @@ package nodes // import "github.com/docker/docker/pkg/discovery/nodes" import ( "testing" + "github.com/docker/docker/internal/test/suite" "github.com/docker/docker/pkg/discovery" - - "github.com/go-check/check" + "gotest.tools/v3/assert" ) // Hook up gocheck into the "go test" runner. -func Test(t *testing.T) { check.TestingT(t) } +func Test(t *testing.T) { + suite.Run(t, &DiscoverySuite{}) +} type DiscoverySuite struct{} -var _ = check.Suite(&DiscoverySuite{}) - -func (s *DiscoverySuite) TestInitialize(c *check.C) { +func (s *DiscoverySuite) TestInitialize(c *testing.T) { d := &Discovery{} d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0, nil) - c.Assert(len(d.entries), check.Equals, 2) - c.Assert(d.entries[0].String(), check.Equals, "1.1.1.1:1111") - c.Assert(d.entries[1].String(), check.Equals, "2.2.2.2:2222") + assert.Equal(c, len(d.entries), 2) + assert.Equal(c, d.entries[0].String(), "1.1.1.1:1111") + assert.Equal(c, d.entries[1].String(), "2.2.2.2:2222") } -func (s *DiscoverySuite) TestInitializeWithPattern(c *check.C) { +func (s *DiscoverySuite) TestInitializeWithPattern(c *testing.T) { d := &Discovery{} d.Initialize("1.1.1.[1:2]:1111,2.2.2.[2:4]:2222", 0, 0, nil) - c.Assert(len(d.entries), check.Equals, 5) - c.Assert(d.entries[0].String(), check.Equals, "1.1.1.1:1111") - c.Assert(d.entries[1].String(), check.Equals, "1.1.1.2:1111") - c.Assert(d.entries[2].String(), check.Equals, "2.2.2.2:2222") - c.Assert(d.entries[3].String(), check.Equals, "2.2.2.3:2222") - c.Assert(d.entries[4].String(), check.Equals, "2.2.2.4:2222") + assert.Equal(c, len(d.entries), 5) + assert.Equal(c, d.entries[0].String(), "1.1.1.1:1111") + assert.Equal(c, d.entries[1].String(), "1.1.1.2:1111") + assert.Equal(c, d.entries[2].String(), "2.2.2.2:2222") + assert.Equal(c, d.entries[3].String(), "2.2.2.3:2222") + assert.Equal(c, d.entries[4].String(), "2.2.2.4:2222") } -func (s *DiscoverySuite) TestWatch(c *check.C) { +func (s *DiscoverySuite) TestWatch(c *testing.T) { d := &Discovery{} d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0, nil) expected := discovery.Entries{ @@ -42,10 +42,10 @@ func (s *DiscoverySuite) TestWatch(c *check.C) { &discovery.Entry{Host: "2.2.2.2", Port: "2222"}, } ch, _ := d.Watch(nil) - c.Assert(expected.Equals(<-ch), check.Equals, true) + assert.Equal(c, expected.Equals(<-ch), true) } -func (s *DiscoverySuite) TestRegister(c *check.C) { +func (s *DiscoverySuite) TestRegister(c *testing.T) { d := &Discovery{} - c.Assert(d.Register("0.0.0.0"), check.NotNil) + assert.Assert(c, d.Register("0.0.0.0") != nil) } diff --git a/pkg/dmesg/dmesg_linux.go b/pkg/dmesg/dmesg_linux.go index bc71b5b31fba6..524c35a883b5d 100644 --- a/pkg/dmesg/dmesg_linux.go +++ b/pkg/dmesg/dmesg_linux.go @@ -1,17 +1,15 @@ package dmesg // import "github.com/docker/docker/pkg/dmesg" import ( - "unsafe" - "golang.org/x/sys/unix" ) // Dmesg returns last messages from the kernel log, up to size bytes func Dmesg(size int) []byte { - t := uintptr(3) // SYSLOG_ACTION_READ_ALL + t := 3 // SYSLOG_ACTION_READ_ALL b := make([]byte, size) - amt, _, err := unix.Syscall(unix.SYS_SYSLOG, t, uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))) - if err != 0 { + amt, err := unix.Klogctl(t, b) + if err != nil { return []byte{} } return b[:amt] diff --git a/pkg/filenotify/poller.go b/pkg/filenotify/poller.go index 6161d4ab73a89..01ef057981f02 100644 --- a/pkg/filenotify/poller.go +++ b/pkg/filenotify/poller.go @@ -146,9 +146,18 @@ func (w *filePoller) sendErr(e error, chClose <-chan struct{}) error { // upon finding changes to a file or errors, sendEvent/sendErr is called func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{}) { defer f.Close() + + timer := time.NewTimer(watchWaitTime) + if !timer.Stop() { + <-timer.C + } + defer timer.Stop() + for { + timer.Reset(watchWaitTime) + select { - case <-time.After(watchWaitTime): + case <-timer.C: case <-chClose: logrus.Debugf("watch for %s closed", f.Name()) return diff --git a/pkg/filenotify/poller_test.go b/pkg/filenotify/poller_test.go index a46b60d94f8ce..53ec7b85084cf 100644 --- a/pkg/filenotify/poller_test.go +++ b/pkg/filenotify/poller_test.go @@ -2,7 +2,6 @@ package filenotify // import "github.com/docker/docker/pkg/filenotify" import ( "fmt" - "io/ioutil" "os" "runtime" "testing" @@ -21,7 +20,7 @@ func TestPollerAddRemove(t *testing.T) { t.Fatal("should have gotten error when removing non-existent watch") } - f, err := ioutil.TempFile("", "asdf") + f, err := os.CreateTemp("", "asdf") if err != nil { t.Fatal(err) } @@ -42,7 +41,7 @@ func TestPollerEvent(t *testing.T) { } w := NewPollingWatcher() - f, err := ioutil.TempFile("", "test-poller") + f, err := os.CreateTemp("", "test-poller") if err != nil { t.Fatal("error creating temp file") } @@ -61,16 +60,18 @@ func TestPollerEvent(t *testing.T) { default: } - if err := ioutil.WriteFile(f.Name(), []byte("hello"), 0644); err != nil { + if err := os.WriteFile(f.Name(), []byte("hello"), 0600); err != nil { t.Fatal(err) } + assertFileMode(t, f.Name(), 0600) if err := assertEvent(w, fsnotify.Write); err != nil { t.Fatal(err) } - if err := os.Chmod(f.Name(), 600); err != nil { + if err := os.Chmod(f.Name(), 0644); err != nil { t.Fatal(err) } + assertFileMode(t, f.Name(), 0644) if err := assertEvent(w, fsnotify.Chmod); err != nil { t.Fatal(err) } @@ -93,7 +94,7 @@ func TestPollerClose(t *testing.T) { t.Fatal(err) } - f, err := ioutil.TempFile("", "asdf") + f, err := os.CreateTemp("", "asdf") if err != nil { t.Fatal(err) } @@ -103,6 +104,17 @@ func TestPollerClose(t *testing.T) { } } +func assertFileMode(t *testing.T, fileName string, mode uint32) { + t.Helper() + f, err := os.Stat(fileName) + if err != nil { + t.Fatal(err) + } + if f.Mode() != os.FileMode(mode) { + t.Fatalf("expected file %s to have mode %#o, but got %#o", fileName, mode, f.Mode()) + } +} + func assertEvent(w FileWatcher, eType fsnotify.Op) error { var err error select { diff --git a/pkg/fileutils/fileutils.go b/pkg/fileutils/fileutils.go index 34f1c726fb5a9..77152a6678edb 100644 --- a/pkg/fileutils/fileutils.go +++ b/pkg/fileutils/fileutils.go @@ -9,10 +9,30 @@ import ( "regexp" "strings" "text/scanner" - - "github.com/sirupsen/logrus" + "unicode/utf8" ) +// escapeBytes is a bitmap used to check whether a character should be escaped when creating the regex. +var escapeBytes [8]byte + +// shouldEscape reports whether a rune should be escaped as part of the regex. +// +// This only includes characters that require escaping in regex but are also NOT valid filepath pattern characters. +// Additionally, '\' is not excluded because there is specific logic to properly handle this, as it's a path separator +// on Windows. +// +// Adapted from regexp::QuoteMeta in go stdlib. +// See https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/regexp/regexp.go;l=703-715;drc=refs%2Ftags%2Fgo1.17.2 +func shouldEscape(b rune) bool { + return b < utf8.RuneSelf && escapeBytes[b%8]&(1<<(b/8)) != 0 +} + +func init() { + for _, b := range []byte(`.+()|{}$`) { + escapeBytes[b%8] |= 1 << (b / 8) + } +} + // PatternMatcher allows checking paths against a list of patterns type PatternMatcher struct { patterns []*Pattern @@ -57,8 +77,16 @@ func NewPatternMatcher(patterns []string) (*PatternMatcher, error) { return pm, nil } -// Matches matches path against all the patterns. Matches is not safe to be -// called concurrently +// Matches returns true if "file" matches any of the patterns +// and isn't excluded by any of the subsequent patterns. +// +// The "file" argument should be a slash-delimited path. +// +// Matches is not safe to call concurrently. +// +// Deprecated: This implementation is buggy (it only checks a single parent dir +// against the pattern) and will be removed soon. Use either +// MatchesOrParentMatches or MatchesUsingParentResults instead. func (pm *PatternMatcher) Matches(file string) (bool, error) { matched := false file = filepath.FromSlash(file) @@ -66,10 +94,11 @@ func (pm *PatternMatcher) Matches(file string) (bool, error) { parentPathDirs := strings.Split(parentPath, string(os.PathSeparator)) for _, pattern := range pm.patterns { - negative := false - - if pattern.exclusion { - negative = true + // Skip evaluation if this is an inclusion and the filename + // already matched the pattern, or it's an exclusion and it has + // not matched the pattern yet. + if pattern.exclusion != matched { + continue } match, err := pattern.match(file) @@ -85,17 +114,165 @@ func (pm *PatternMatcher) Matches(file string) (bool, error) { } if match { - matched = !negative + matched = !pattern.exclusion } } - if matched { - logrus.Debugf("Skipping excluded path: %s", file) + return matched, nil +} + +// MatchesOrParentMatches returns true if "file" matches any of the patterns +// and isn't excluded by any of the subsequent patterns. +// +// The "file" argument should be a slash-delimited path. +// +// Matches is not safe to call concurrently. +func (pm *PatternMatcher) MatchesOrParentMatches(file string) (bool, error) { + matched := false + file = filepath.FromSlash(file) + parentPath := filepath.Dir(file) + parentPathDirs := strings.Split(parentPath, string(os.PathSeparator)) + + for _, pattern := range pm.patterns { + // Skip evaluation if this is an inclusion and the filename + // already matched the pattern, or it's an exclusion and it has + // not matched the pattern yet. + if pattern.exclusion != matched { + continue + } + + match, err := pattern.match(file) + if err != nil { + return false, err + } + + if !match && parentPath != "." { + // Check to see if the pattern matches one of our parent dirs. + for i := range parentPathDirs { + match, _ = pattern.match(strings.Join(parentPathDirs[:i+1], string(os.PathSeparator))) + if match { + break + } + } + } + + if match { + matched = !pattern.exclusion + } } return matched, nil } +// MatchesUsingParentResult returns true if "file" matches any of the patterns +// and isn't excluded by any of the subsequent patterns. The functionality is +// the same as Matches, but as an optimization, the caller keeps track of +// whether the parent directory matched. +// +// The "file" argument should be a slash-delimited path. +// +// MatchesUsingParentResult is not safe to call concurrently. +// +// Deprecated: this function does behave correctly in some cases (see +// https://github.com/docker/buildx/issues/850). +// +// Use MatchesUsingParentResults instead. +func (pm *PatternMatcher) MatchesUsingParentResult(file string, parentMatched bool) (bool, error) { + matched := parentMatched + file = filepath.FromSlash(file) + + for _, pattern := range pm.patterns { + // Skip evaluation if this is an inclusion and the filename + // already matched the pattern, or it's an exclusion and it has + // not matched the pattern yet. + if pattern.exclusion != matched { + continue + } + + match, err := pattern.match(file) + if err != nil { + return false, err + } + + if match { + matched = !pattern.exclusion + } + } + return matched, nil +} + +// MatchInfo tracks information about parent dir matches while traversing a +// filesystem. +type MatchInfo struct { + parentMatched []bool +} + +// MatchesUsingParentResults returns true if "file" matches any of the patterns +// and isn't excluded by any of the subsequent patterns. The functionality is +// the same as Matches, but as an optimization, the caller passes in +// intermediate results from matching the parent directory. +// +// The "file" argument should be a slash-delimited path. +// +// MatchesUsingParentResults is not safe to call concurrently. +func (pm *PatternMatcher) MatchesUsingParentResults(file string, parentMatchInfo MatchInfo) (bool, MatchInfo, error) { + parentMatched := parentMatchInfo.parentMatched + if len(parentMatched) != 0 && len(parentMatched) != len(pm.patterns) { + return false, MatchInfo{}, errors.New("wrong number of values in parentMatched") + } + + file = filepath.FromSlash(file) + matched := false + + matchInfo := MatchInfo{ + parentMatched: make([]bool, len(pm.patterns)), + } + for i, pattern := range pm.patterns { + match := false + // If the parent matched this pattern, we don't need to recheck. + if len(parentMatched) != 0 { + match = parentMatched[i] + } + + if !match { + // Skip evaluation if this is an inclusion and the filename + // already matched the pattern, or it's an exclusion and it has + // not matched the pattern yet. + if pattern.exclusion != matched { + continue + } + + var err error + match, err = pattern.match(file) + if err != nil { + return false, matchInfo, err + } + + // If the zero value of MatchInfo was passed in, we don't have + // any information about the parent dir's match results, and we + // apply the same logic as MatchesOrParentMatches. + if !match && len(parentMatched) == 0 { + if parentPath := filepath.Dir(file); parentPath != "." { + parentPathDirs := strings.Split(parentPath, string(os.PathSeparator)) + // Check to see if the pattern matches one of our parent dirs. + for i := range parentPathDirs { + match, _ = pattern.match(strings.Join(parentPathDirs[:i+1], string(os.PathSeparator))) + if match { + break + } + } + } + } + } + matchInfo.parentMatched[i] = match + + if match { + matched = !pattern.exclusion + } + } + return matched, matchInfo, nil +} + // Exclusions returns true if any of the patterns define exclusions func (pm *PatternMatcher) Exclusions() bool { return pm.exclusions @@ -124,7 +301,6 @@ func (p *Pattern) Exclusion() bool { } func (p *Pattern) match(path string) (bool, error) { - if p.regexp == nil { if err := p.compile(); err != nil { return false, filepath.ErrBadPattern @@ -179,7 +355,7 @@ func (p *Pattern) compile() error { } else if ch == '?' { // "?" is any char except "/" regStr += "[^" + escSL + "]" - } else if ch == '.' || ch == '$' { + } else if shouldEscape(ch) { // Escape some regexp special chars that have no meaning // in golang's filepath.Match regStr += `\` + string(ch) @@ -216,6 +392,9 @@ func (p *Pattern) compile() error { // Matches returns true if file matches any of the patterns // and isn't excluded by any of the subsequent patterns. +// +// This implementation is buggy (it only checks a single parent dir against the +// pattern) and will be removed soon. Use MatchesOrParentMatches instead. func Matches(file string, patterns []string) (bool, error) { pm, err := NewPatternMatcher(patterns) if err != nil { @@ -231,6 +410,23 @@ func Matches(file string, patterns []string) (bool, error) { return pm.Matches(file) } +// MatchesOrParentMatches returns true if file matches any of the patterns +// and isn't excluded by any of the subsequent patterns. +func MatchesOrParentMatches(file string, patterns []string) (bool, error) { + pm, err := NewPatternMatcher(patterns) + if err != nil { + return false, err + } + file = filepath.Clean(file) + + if file == "." { + // Don't let them exclude everything, kind of silly. + return false, nil + } + + return pm.MatchesOrParentMatches(file) +} + // CopyFile copies from src to dst until either EOF is reached // on src or an error occurs. It verifies src exists and removes // the dst if it exists. diff --git a/pkg/fileutils/fileutils_test.go b/pkg/fileutils/fileutils_test.go index 4b5f129a50afd..b24d82ebd28f3 100644 --- a/pkg/fileutils/fileutils_test.go +++ b/pkg/fileutils/fileutils_test.go @@ -2,7 +2,6 @@ package fileutils // import "github.com/docker/docker/pkg/fileutils" import ( "fmt" - "io/ioutil" "os" "path" "path/filepath" @@ -10,13 +9,13 @@ import ( "strings" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) // CopyFile with invalid src func TestCopyFileWithInvalidSrc(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + tempFolder, err := os.MkdirTemp("", "docker-fileutils-test") // #nosec G303 defer os.RemoveAll(tempFolder) if err != nil { t.Fatal(err) @@ -33,13 +32,13 @@ func TestCopyFileWithInvalidSrc(t *testing.T) { // CopyFile with invalid dest func TestCopyFileWithInvalidDest(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + tempFolder, err := os.MkdirTemp("", "docker-fileutils-test") defer os.RemoveAll(tempFolder) if err != nil { t.Fatal(err) } src := path.Join(tempFolder, "file") - err = ioutil.WriteFile(src, []byte("content"), 0740) + err = os.WriteFile(src, []byte("content"), 0740) if err != nil { t.Fatal(err) } @@ -55,13 +54,13 @@ func TestCopyFileWithInvalidDest(t *testing.T) { // CopyFile with same src and dest func TestCopyFileWithSameSrcAndDest(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + tempFolder, err := os.MkdirTemp("", "docker-fileutils-test") defer os.RemoveAll(tempFolder) if err != nil { t.Fatal(err) } file := path.Join(tempFolder, "file") - err = ioutil.WriteFile(file, []byte("content"), 0740) + err = os.WriteFile(file, []byte("content"), 0740) if err != nil { t.Fatal(err) } @@ -76,7 +75,7 @@ func TestCopyFileWithSameSrcAndDest(t *testing.T) { // CopyFile with same src and dest but path is different and not clean func TestCopyFileWithSameSrcAndDestWithPathNameDifferent(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + tempFolder, err := os.MkdirTemp("", "docker-fileutils-test") defer os.RemoveAll(tempFolder) if err != nil { t.Fatal(err) @@ -88,7 +87,7 @@ func TestCopyFileWithSameSrcAndDestWithPathNameDifferent(t *testing.T) { } file := path.Join(testFolder, "file") sameFile := testFolder + "/../test/file" - err = ioutil.WriteFile(file, []byte("content"), 0740) + err = os.WriteFile(file, []byte("content"), 0740) if err != nil { t.Fatal(err) } @@ -102,15 +101,15 @@ func TestCopyFileWithSameSrcAndDestWithPathNameDifferent(t *testing.T) { } func TestCopyFile(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + tempFolder, err := os.MkdirTemp("", "docker-fileutils-test") defer os.RemoveAll(tempFolder) if err != nil { t.Fatal(err) } src := path.Join(tempFolder, "src") dest := path.Join(tempFolder, "dest") - ioutil.WriteFile(src, []byte("content"), 0777) - ioutil.WriteFile(dest, []byte("destContent"), 0777) + os.WriteFile(src, []byte("content"), 0777) + os.WriteFile(dest, []byte("destContent"), 0777) bytes, err := CopyFile(src, dest) if err != nil { t.Fatal(err) @@ -118,7 +117,7 @@ func TestCopyFile(t *testing.T) { if bytes != 7 { t.Fatalf("Should have written %d bytes but wrote %d", 7, bytes) } - actual, err := ioutil.ReadFile(dest) + actual, err := os.ReadFile(dest) if err != nil { t.Fatal(err) } @@ -182,6 +181,7 @@ func TestReadSymlinkedDirectoryToFile(t *testing.T) { var err error var file *os.File + // #nosec G303 if file, err = os.Create("/tmp/testReadSymlinkToFile"); err != nil { t.Fatalf("failed to create file: %s", err) } @@ -309,6 +309,12 @@ type matchesTestCase struct { pass bool } +type multiPatternTestCase struct { + patterns []string + text string + pass bool +} + func TestMatches(t *testing.T) { tests := []matchesTestCase{ {"**", "file", true}, @@ -327,6 +333,8 @@ func TestMatches(t *testing.T) { {"dir/**", "dir/file/", true}, {"dir/**", "dir/dir2/file", true}, {"dir/**", "dir/dir2/file/", true}, + {"**/dir", "dir", true}, + {"**/dir", "dir/file", true}, {"**/dir2/*", "dir/dir2/file", true}, {"**/dir2/*", "dir/dir2/file/", true}, {"**/dir2/**", "dir/dir2/dir3/file", true}, @@ -371,23 +379,116 @@ func TestMatches(t *testing.T) { {"abc/**", "abc/def/ghi", true}, {"**/.foo", ".foo", true}, {"**/.foo", "bar.foo", false}, + {"a(b)c/def", "a(b)c/def", true}, + {"a(b)c/def", "a(b)c/xyz", false}, + {"a.|)$(}+{bc", "a.|)$(}+{bc", true}, + } + multiPatternTests := []multiPatternTestCase{ + {[]string{"**", "!util/docker/web"}, "util/docker/web/foo", false}, + {[]string{"**", "!util/docker/web", "util/docker/web/foo"}, "util/docker/web/foo", true}, } if runtime.GOOS != "windows" { tests = append(tests, []matchesTestCase{ {"a\\*b", "a*b", true}, - {"a\\", "a", false}, - {"a\\", "a\\", false}, }...) } - for _, test := range tests { - desc := fmt.Sprintf("pattern=%q text=%q", test.pattern, test.text) - pm, err := NewPatternMatcher([]string{test.pattern}) - assert.NilError(t, err, desc) - res, _ := pm.Matches(test.text) - assert.Check(t, is.Equal(test.pass, res), desc) - } + t.Run("MatchesOrParentMatches", func(t *testing.T) { + for _, test := range tests { + desc := fmt.Sprintf("pattern=%q text=%q", test.pattern, test.text) + pm, err := NewPatternMatcher([]string{test.pattern}) + assert.NilError(t, err, desc) + res, _ := pm.MatchesOrParentMatches(test.text) + assert.Check(t, is.Equal(test.pass, res), desc) + } + + for _, test := range multiPatternTests { + desc := fmt.Sprintf("patterns=%q text=%q", test.patterns, test.text) + pm, err := NewPatternMatcher(test.patterns) + assert.NilError(t, err, desc) + res, _ := pm.MatchesOrParentMatches(test.text) + assert.Check(t, is.Equal(test.pass, res), desc) + } + }) + + t.Run("MatchesUsingParentResult", func(t *testing.T) { + for _, test := range tests { + desc := fmt.Sprintf("pattern=%q text=%q", test.pattern, test.text) + pm, err := NewPatternMatcher([]string{test.pattern}) + assert.NilError(t, err, desc) + + parentPath := filepath.Dir(filepath.FromSlash(test.text)) + parentPathDirs := strings.Split(parentPath, string(os.PathSeparator)) + + parentMatched := false + if parentPath != "." { + for i := range parentPathDirs { + parentMatched, _ = pm.MatchesUsingParentResult(strings.Join(parentPathDirs[:i+1], "/"), parentMatched) + } + } + + res, _ := pm.MatchesUsingParentResult(test.text, parentMatched) + assert.Check(t, is.Equal(test.pass, res), desc) + } + }) + + t.Run("MatchesUsingParentResults", func(t *testing.T) { + check := func(pm *PatternMatcher, text string, pass bool, desc string) { + parentPath := filepath.Dir(filepath.FromSlash(text)) + parentPathDirs := strings.Split(parentPath, string(os.PathSeparator)) + + parentMatchInfo := MatchInfo{} + if parentPath != "." { + for i := range parentPathDirs { + _, parentMatchInfo, _ = pm.MatchesUsingParentResults(strings.Join(parentPathDirs[:i+1], "/"), parentMatchInfo) + } + } + + res, _, _ := pm.MatchesUsingParentResults(text, parentMatchInfo) + assert.Check(t, is.Equal(pass, res), desc) + } + + for _, test := range tests { + desc := fmt.Sprintf("pattern=%q text=%q", test.pattern, test.text) + pm, err := NewPatternMatcher([]string{test.pattern}) + assert.NilError(t, err, desc) + + check(pm, test.text, test.pass, desc) + } + + for _, test := range multiPatternTests { + desc := fmt.Sprintf("pattern=%q text=%q", test.patterns, test.text) + pm, err := NewPatternMatcher(test.patterns) + assert.NilError(t, err, desc) + + check(pm, test.text, test.pass, desc) + } + }) + + t.Run("MatchesUsingParentResultsNoContext", func(t *testing.T) { + check := func(pm *PatternMatcher, text string, pass bool, desc string) { + res, _, _ := pm.MatchesUsingParentResults(text, MatchInfo{}) + assert.Check(t, is.Equal(pass, res), desc) + } + + for _, test := range tests { + desc := fmt.Sprintf("pattern=%q text=%q", test.pattern, test.text) + pm, err := NewPatternMatcher([]string{test.pattern}) + assert.NilError(t, err, desc) + + check(pm, test.text, test.pass, desc) + } + + for _, test := range multiPatternTests { + desc := fmt.Sprintf("pattern=%q text=%q", test.patterns, test.text) + pm, err := NewPatternMatcher(test.patterns) + assert.NilError(t, err, desc) + + check(pm, test.text, test.pass, desc) + } + }) + } func TestCleanPatterns(t *testing.T) { @@ -456,7 +557,7 @@ func TestCleanPatternsErrorSingleException(t *testing.T) { } func TestCreateIfNotExistsDir(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + tempFolder, err := os.MkdirTemp("", "docker-fileutils-test") if err != nil { t.Fatal(err) } @@ -478,7 +579,7 @@ func TestCreateIfNotExistsDir(t *testing.T) { } func TestCreateIfNotExistsFile(t *testing.T) { - tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") + tempFolder, err := os.MkdirTemp("", "docker-fileutils-test") if err != nil { t.Fatal(err) } diff --git a/pkg/fileutils/fileutils_unix.go b/pkg/fileutils/fileutils_unix.go index 565396f1c7f42..f782b4266aada 100644 --- a/pkg/fileutils/fileutils_unix.go +++ b/pkg/fileutils/fileutils_unix.go @@ -1,10 +1,10 @@ +//go:build linux || freebsd // +build linux freebsd package fileutils // import "github.com/docker/docker/pkg/fileutils" import ( "fmt" - "io/ioutil" "os" "github.com/sirupsen/logrus" @@ -13,7 +13,7 @@ import ( // GetTotalUsedFds Returns the number of used File Descriptors by // reading it via /proc filesystem. func GetTotalUsedFds() int { - if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil { + if fds, err := os.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil { logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err) } else { return len(fds) diff --git a/pkg/fsutils/fsutils_linux.go b/pkg/fsutils/fsutils_linux.go index 104211adea7e2..d9a7760963130 100644 --- a/pkg/fsutils/fsutils_linux.go +++ b/pkg/fsutils/fsutils_linux.go @@ -2,7 +2,6 @@ package fsutils // import "github.com/docker/docker/pkg/fsutils" import ( "fmt" - "io/ioutil" "os" "unsafe" @@ -10,14 +9,14 @@ import ( ) func locateDummyIfEmpty(path string) (string, error) { - children, err := ioutil.ReadDir(path) + children, err := os.ReadDir(path) if err != nil { return "", err } if len(children) != 0 { return "", nil } - dummyFile, err := ioutil.TempFile(path, "fsutils-dummy") + dummyFile, err := os.CreateTemp(path, "fsutils-dummy") if err != nil { return "", err } diff --git a/pkg/fsutils/fsutils_linux_test.go b/pkg/fsutils/fsutils_linux_test.go index 4e5a78b519089..f7af8e8677aed 100644 --- a/pkg/fsutils/fsutils_linux_test.go +++ b/pkg/fsutils/fsutils_linux_test.go @@ -1,9 +1,9 @@ +//go:build linux // +build linux package fsutils // import "github.com/docker/docker/pkg/fsutils" import ( - "io/ioutil" "os" "os/exec" "testing" @@ -19,7 +19,7 @@ func testSupportsDType(t *testing.T, expected bool, mkfsCommand string, mkfsArg // create a sparse image imageSize := int64(32 * 1024 * 1024) - imageFile, err := ioutil.TempFile("", "fsutils-image") + imageFile, err := os.CreateTemp("", "fsutils-image") if err != nil { t.Fatal(err) } @@ -36,7 +36,7 @@ func testSupportsDType(t *testing.T, expected bool, mkfsCommand string, mkfsArg } // create a mountpoint - mountpoint, err := ioutil.TempDir("", "fsutils-mountpoint") + mountpoint, err := os.MkdirTemp("", "fsutils-mountpoint") if err != nil { t.Fatal(err) } diff --git a/pkg/homedir/homedir_linux.go b/pkg/homedir/homedir_linux.go index ee15ed52b1645..5e6310fdcd6bb 100644 --- a/pkg/homedir/homedir_linux.go +++ b/pkg/homedir/homedir_linux.go @@ -1,21 +1,93 @@ package homedir // import "github.com/docker/docker/pkg/homedir" import ( + "errors" "os" - - "github.com/docker/docker/pkg/idtools" + "path/filepath" + "strings" ) -// GetStatic returns the home directory for the current user without calling -// os/user.Current(). This is useful for static-linked binary on glibc-based -// system, because a call to os/user.Current() in a static binary leads to -// segfault due to a glibc issue that won't be fixed in a short term. -// (#29344, golang/go#13470, https://sourceware.org/bugzilla/show_bug.cgi?id=19341) -func GetStatic() (string, error) { - uid := os.Getuid() - usr, err := idtools.LookupUID(uid) +// GetRuntimeDir returns XDG_RUNTIME_DIR. +// XDG_RUNTIME_DIR is typically configured via pam_systemd. +// GetRuntimeDir returns non-nil error if XDG_RUNTIME_DIR is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func GetRuntimeDir() (string, error) { + if xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR"); xdgRuntimeDir != "" { + return xdgRuntimeDir, nil + } + return "", errors.New("could not get XDG_RUNTIME_DIR") +} + +// StickRuntimeDirContents sets the sticky bit on files that are under +// XDG_RUNTIME_DIR, so that the files won't be periodically removed by the system. +// +// StickyRuntimeDir returns slice of sticked files. +// StickyRuntimeDir returns nil error if XDG_RUNTIME_DIR is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func StickRuntimeDirContents(files []string) ([]string, error) { + runtimeDir, err := GetRuntimeDir() + if err != nil { + // ignore error if runtimeDir is empty + return nil, nil + } + runtimeDir, err = filepath.Abs(runtimeDir) if err != nil { - return "", err + return nil, err + } + var sticked []string + for _, f := range files { + f, err = filepath.Abs(f) + if err != nil { + return sticked, err + } + if strings.HasPrefix(f, runtimeDir+"/") { + if err = stick(f); err != nil { + return sticked, err + } + sticked = append(sticked, f) + } + } + return sticked, nil +} + +func stick(f string) error { + st, err := os.Stat(f) + if err != nil { + return err + } + m := st.Mode() + m |= os.ModeSticky + return os.Chmod(f, m) +} + +// GetDataHome returns XDG_DATA_HOME. +// GetDataHome returns $HOME/.local/share and nil error if XDG_DATA_HOME is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func GetDataHome() (string, error) { + if xdgDataHome := os.Getenv("XDG_DATA_HOME"); xdgDataHome != "" { + return xdgDataHome, nil + } + home := os.Getenv("HOME") + if home == "" { + return "", errors.New("could not get either XDG_DATA_HOME or HOME") + } + return filepath.Join(home, ".local", "share"), nil +} + +// GetConfigHome returns XDG_CONFIG_HOME. +// GetConfigHome returns $HOME/.config and nil error if XDG_CONFIG_HOME is not set. +// +// See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html +func GetConfigHome() (string, error) { + if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" { + return xdgConfigHome, nil + } + home := os.Getenv("HOME") + if home == "" { + return "", errors.New("could not get either XDG_CONFIG_HOME or HOME") } - return usr.Home, nil + return filepath.Join(home, ".config"), nil } diff --git a/pkg/homedir/homedir_others.go b/pkg/homedir/homedir_others.go index 75ada2fe54607..fc48e674c11d7 100644 --- a/pkg/homedir/homedir_others.go +++ b/pkg/homedir/homedir_others.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package homedir // import "github.com/docker/docker/pkg/homedir" @@ -6,8 +7,22 @@ import ( "errors" ) -// GetStatic is not needed for non-linux systems. -// (Precisely, it is needed only for glibc-based linux systems.) -func GetStatic() (string, error) { - return "", errors.New("homedir.GetStatic() is not supported on this system") +// GetRuntimeDir is unsupported on non-linux system. +func GetRuntimeDir() (string, error) { + return "", errors.New("homedir.GetRuntimeDir() is not supported on this system") +} + +// StickRuntimeDirContents is unsupported on non-linux system. +func StickRuntimeDirContents(files []string) ([]string, error) { + return nil, errors.New("homedir.StickRuntimeDirContents() is not supported on this system") +} + +// GetDataHome is unsupported on non-linux system. +func GetDataHome() (string, error) { + return "", errors.New("homedir.GetDataHome() is not supported on this system") +} + +// GetConfigHome is unsupported on non-linux system. +func GetConfigHome() (string, error) { + return "", errors.New("homedir.GetConfigHome() is not supported on this system") } diff --git a/pkg/homedir/homedir_unix.go b/pkg/homedir/homedir_unix.go index d85e124488407..d1732dee52f21 100644 --- a/pkg/homedir/homedir_unix.go +++ b/pkg/homedir/homedir_unix.go @@ -1,11 +1,11 @@ +//go:build !windows // +build !windows package homedir // import "github.com/docker/docker/pkg/homedir" import ( "os" - - "github.com/opencontainers/runc/libcontainer/user" + "os/user" ) // Key returns the env var name for the user's home dir based on @@ -17,11 +17,16 @@ func Key() string { // Get returns the home directory of the current user with the help of // environment variables depending on the target operating system. // Returned path should be used with "path/filepath" to form new paths. +// +// If linking statically with cgo enabled against glibc, ensure the +// osusergo build tag is used. +// +// If needing to do nss lookups, do not disable cgo or set osusergo. func Get() string { home := os.Getenv(Key()) if home == "" { - if u, err := user.CurrentUser(); err == nil { - return u.Home + if u, err := user.Current(); err == nil { + return u.HomeDir } } return home diff --git a/pkg/idtools/idtools.go b/pkg/idtools/idtools.go index 230422eac827f..25a57b231e011 100644 --- a/pkg/idtools/idtools.go +++ b/pkg/idtools/idtools.go @@ -4,7 +4,6 @@ import ( "bufio" "fmt" "os" - "sort" "strconv" "strings" ) @@ -36,13 +35,13 @@ const ( // MkdirAllAndChown creates a directory (include any along the path) and then modifies // ownership to the requested uid/gid. If the directory already exists, this -// function will still change ownership to the requested uid/gid pair. +// function will still change ownership and permissions. func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error { return mkdirAs(path, mode, owner, true, true) } // MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid. -// If the directory already exists, this function still changes ownership. +// If the directory already exists, this function still changes ownership and permissions. // Note that unlike os.Mkdir(), this function does not return IsExist error // in case path already exists. func MkdirAndChown(path string, mode os.FileMode, owner Identity) error { @@ -51,7 +50,7 @@ func MkdirAndChown(path string, mode os.FileMode, owner Identity) error { // MkdirAllAndChownNew creates a directory (include any along the path) and then modifies // ownership ONLY of newly created directories to the requested uid/gid. If the -// directories along the path exist, no change of ownership will be performed +// directories along the path exist, no change of ownership or permissions will be performed func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error { return mkdirAs(path, mode, owner, true, false) } @@ -115,31 +114,6 @@ type IdentityMapping struct { gids []IDMap } -// NewIdentityMapping takes a requested user and group name and -// using the data from /etc/sub{uid,gid} ranges, creates the -// proper uid and gid remapping ranges for that user/group pair -func NewIdentityMapping(username, groupname string) (*IdentityMapping, error) { - subuidRanges, err := parseSubuid(username) - if err != nil { - return nil, err - } - subgidRanges, err := parseSubgid(groupname) - if err != nil { - return nil, err - } - if len(subuidRanges) == 0 { - return nil, fmt.Errorf("No subuid ranges found for user %q", username) - } - if len(subgidRanges) == 0 { - return nil, fmt.Errorf("No subgid ranges found for group %q", groupname) - } - - return &IdentityMapping{ - uids: createIDMap(subuidRanges), - gids: createIDMap(subgidRanges), - }, nil -} - // NewIDMappingsFromMaps creates a new mapping from two slices // Deprecated: this is a temporary shim while transitioning to IDMapping func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IdentityMapping { @@ -203,8 +177,6 @@ func (i *IdentityMapping) GIDs() []IDMap { func createIDMap(subidRanges ranges) []IDMap { idMap := []IDMap{} - // sort the ranges by lowest ID first - sort.Sort(subidRanges) containerID := 0 for _, idrange := range subidRanges { idMap = append(idMap, IDMap{ @@ -239,10 +211,6 @@ func parseSubidFile(path, username string) (ranges, error) { s := bufio.NewScanner(subidFile) for s.Scan() { - if err := s.Err(); err != nil { - return rangeList, err - } - text := strings.TrimSpace(s.Text()) if text == "" || strings.HasPrefix(text, "#") { continue @@ -263,5 +231,11 @@ func parseSubidFile(path, username string) (ranges, error) { rangeList = append(rangeList, subIDRange{startid, length}) } } - return rangeList, nil + + return rangeList, s.Err() +} + +// CurrentIdentity returns the identity of the current process +func CurrentIdentity() Identity { + return Identity{UID: os.Getuid(), GID: os.Getegid()} } diff --git a/pkg/idtools/idtools_test.go b/pkg/idtools/idtools_test.go new file mode 100644 index 0000000000000..34b57b6bcdd4f --- /dev/null +++ b/pkg/idtools/idtools_test.go @@ -0,0 +1,28 @@ +package idtools // import "github.com/docker/docker/pkg/idtools" + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestCreateIDMapOrder(t *testing.T) { + subidRanges := ranges{ + {100000, 1000}, + {1000, 1}, + } + + idMap := createIDMap(subidRanges) + assert.DeepEqual(t, idMap, []IDMap{ + { + ContainerID: 0, + HostID: 100000, + Size: 1000, + }, + { + ContainerID: 1000, + HostID: 1000, + Size: 1, + }, + }) +} diff --git a/pkg/idtools/idtools_unix.go b/pkg/idtools/idtools_unix.go index fb239743a01a4..ceec0339b5675 100644 --- a/pkg/idtools/idtools_unix.go +++ b/pkg/idtools/idtools_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package idtools // import "github.com/docker/docker/pkg/idtools" @@ -8,12 +9,13 @@ import ( "io" "os" "path/filepath" - "strings" + "strconv" "sync" "syscall" "github.com/docker/docker/pkg/system" "github.com/opencontainers/runc/libcontainer/user" + "github.com/pkg/errors" ) var ( @@ -39,7 +41,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting } // short-circuit--we were called with an existing directory and chown was requested - return lazyChown(path, owner.UID, owner.GID, stat) + return setPermissions(path, mode, owner.UID, owner.GID, stat) } if os.IsNotExist(err) { @@ -59,7 +61,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting paths = append(paths, dirPath) } } - if err := system.MkdirAll(path, mode, ""); err != nil { + if err := system.MkdirAll(path, mode); err != nil { return err } } else { @@ -70,7 +72,7 @@ func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting // even if it existed, we will chown the requested path + any subpaths that // didn't exist when we called MkdirAll for _, pathComponent := range paths { - if err := lazyChown(pathComponent, owner.UID, owner.GID, nil); err != nil { + if err := setPermissions(pathComponent, mode, owner.UID, owner.GID, nil); err != nil { return err } } @@ -105,14 +107,14 @@ func accessible(isOwner, isGroup bool, perms os.FileMode) bool { // LookupUser uses traditional local system files lookup (from libcontainer/user) on a username, // followed by a call to `getent` for supporting host configured non-files passwd and group dbs -func LookupUser(username string) (user.User, error) { +func LookupUser(name string) (user.User, error) { // first try a local system files lookup using existing capabilities - usr, err := user.LookupUser(username) + usr, err := user.LookupUser(name) if err == nil { return usr, nil } // local files lookup failed; attempt to call `getent` to query configured passwd dbs - usr, err = getentUser(fmt.Sprintf("%s %s", "passwd", username)) + usr, err = getentUser(name) if err != nil { return user.User{}, err } @@ -128,11 +130,11 @@ func LookupUID(uid int) (user.User, error) { return usr, nil } // local files lookup failed; attempt to call `getent` to query configured passwd dbs - return getentUser(fmt.Sprintf("%s %d", "passwd", uid)) + return getentUser(strconv.Itoa(uid)) } -func getentUser(args string) (user.User, error) { - reader, err := callGetent(args) +func getentUser(name string) (user.User, error) { + reader, err := callGetent("passwd", name) if err != nil { return user.User{}, err } @@ -141,21 +143,21 @@ func getentUser(args string) (user.User, error) { return user.User{}, err } if len(users) == 0 { - return user.User{}, fmt.Errorf("getent failed to find passwd entry for %q", strings.Split(args, " ")[1]) + return user.User{}, fmt.Errorf("getent failed to find passwd entry for %q", name) } return users[0], nil } // LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name, // followed by a call to `getent` for supporting host configured non-files passwd and group dbs -func LookupGroup(groupname string) (user.Group, error) { +func LookupGroup(name string) (user.Group, error) { // first try a local system files lookup using existing capabilities - group, err := user.LookupGroup(groupname) + group, err := user.LookupGroup(name) if err == nil { return group, nil } // local files lookup failed; attempt to call `getent` to query configured group dbs - return getentGroup(fmt.Sprintf("%s %s", "group", groupname)) + return getentGroup(name) } // LookupGID uses traditional local system files lookup (from libcontainer/user) on a group ID, @@ -167,11 +169,11 @@ func LookupGID(gid int) (user.Group, error) { return group, nil } // local files lookup failed; attempt to call `getent` to query configured group dbs - return getentGroup(fmt.Sprintf("%s %d", "group", gid)) + return getentGroup(strconv.Itoa(gid)) } -func getentGroup(args string) (user.Group, error) { - reader, err := callGetent(args) +func getentGroup(name string) (user.Group, error) { + reader, err := callGetent("group", name) if err != nil { return user.Group{}, err } @@ -180,18 +182,18 @@ func getentGroup(args string) (user.Group, error) { return user.Group{}, err } if len(groups) == 0 { - return user.Group{}, fmt.Errorf("getent failed to find groups entry for %q", strings.Split(args, " ")[1]) + return user.Group{}, fmt.Errorf("getent failed to find groups entry for %q", name) } return groups[0], nil } -func callGetent(args string) (io.Reader, error) { +func callGetent(database, key string) (io.Reader, error) { entOnce.Do(func() { getentCmd, _ = resolveBinary("getent") }) // if no `getent` command on host, can't do anything else if getentCmd == "" { - return nil, fmt.Errorf("") + return nil, fmt.Errorf("unable to find getent command") } - out, err := execCmd(getentCmd, args) + out, err := execCmd(getentCmd, database, key) if err != nil { exitCode, errC := system.GetExitCode(err) if errC != nil { @@ -201,8 +203,7 @@ func callGetent(args string) (io.Reader, error) { case 1: return nil, fmt.Errorf("getent reported invalid parameters/database unknown") case 2: - terms := strings.Split(args, " ") - return nil, fmt.Errorf("getent unable to find entry %q in %s database", terms[1], terms[0]) + return nil, fmt.Errorf("getent unable to find entry %q in %s database", key, database) case 3: return nil, fmt.Errorf("getent database doesn't support enumeration") default: @@ -213,10 +214,11 @@ func callGetent(args string) (io.Reader, error) { return bytes.NewReader(out), nil } -// lazyChown performs a chown only if the uid/gid don't match what's requested +// setPermissions performs a chown/chmod only if the uid/gid don't match what's requested // Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the // dir is on an NFS share, so don't call chown unless we absolutely must. -func lazyChown(p string, uid, gid int, stat *system.StatT) error { +// Likewise for setting permissions. +func setPermissions(p string, mode os.FileMode, uid, gid int, stat *system.StatT) error { if stat == nil { var err error stat, err = system.Stat(p) @@ -224,8 +226,71 @@ func lazyChown(p string, uid, gid int, stat *system.StatT) error { return err } } + if os.FileMode(stat.Mode()).Perm() != mode.Perm() { + if err := os.Chmod(p, mode.Perm()); err != nil { + return err + } + } if stat.UID() == uint32(uid) && stat.GID() == uint32(gid) { return nil } return os.Chown(p, uid, gid) } + +// NewIdentityMapping takes a requested username and +// using the data from /etc/sub{uid,gid} ranges, creates the +// proper uid and gid remapping ranges for that user/group pair +func NewIdentityMapping(name string) (*IdentityMapping, error) { + usr, err := LookupUser(name) + if err != nil { + return nil, fmt.Errorf("Could not get user for username %s: %v", name, err) + } + + subuidRanges, err := lookupSubUIDRanges(usr) + if err != nil { + return nil, err + } + subgidRanges, err := lookupSubGIDRanges(usr) + if err != nil { + return nil, err + } + + return &IdentityMapping{ + uids: subuidRanges, + gids: subgidRanges, + }, nil +} + +func lookupSubUIDRanges(usr user.User) ([]IDMap, error) { + rangeList, err := parseSubuid(strconv.Itoa(usr.Uid)) + if err != nil { + return nil, err + } + if len(rangeList) == 0 { + rangeList, err = parseSubuid(usr.Name) + if err != nil { + return nil, err + } + } + if len(rangeList) == 0 { + return nil, errors.Errorf("no subuid ranges found for user %q", usr.Name) + } + return createIDMap(rangeList), nil +} + +func lookupSubGIDRanges(usr user.User) ([]IDMap, error) { + rangeList, err := parseSubgid(strconv.Itoa(usr.Uid)) + if err != nil { + return nil, err + } + if len(rangeList) == 0 { + rangeList, err = parseSubgid(usr.Name) + if err != nil { + return nil, err + } + } + if len(rangeList) == 0 { + return nil, errors.Errorf("no subgid ranges found for user %q", usr.Name) + } + return createIDMap(rangeList), nil +} diff --git a/pkg/idtools/idtools_unix_test.go b/pkg/idtools/idtools_unix_test.go index be5d60262247f..c43ed6fc8dd97 100644 --- a/pkg/idtools/idtools_unix_test.go +++ b/pkg/idtools/idtools_unix_test.go @@ -1,19 +1,19 @@ +//go:build !windows // +build !windows package idtools // import "github.com/docker/docker/pkg/idtools" import ( "fmt" - "io/ioutil" "os" "os/user" "path/filepath" "testing" "golang.org/x/sys/unix" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" - "gotest.tools/skip" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/skip" ) const ( @@ -27,7 +27,7 @@ type node struct { func TestMkdirAllAndChown(t *testing.T) { RequiresRoot(t) - dirName, err := ioutil.TempDir("", "mkdirall") + dirName, err := os.MkdirTemp("", "mkdirall") if err != nil { t.Fatalf("Couldn't create temp dir: %v", err) } @@ -88,7 +88,7 @@ func TestMkdirAllAndChown(t *testing.T) { func TestMkdirAllAndChownNew(t *testing.T) { RequiresRoot(t) - dirName, err := ioutil.TempDir("", "mkdirnew") + dirName, err := os.MkdirTemp("", "mkdirnew") assert.NilError(t, err) defer os.RemoveAll(dirName) @@ -129,7 +129,7 @@ func TestMkdirAllAndChownNew(t *testing.T) { func TestMkdirAndChown(t *testing.T) { RequiresRoot(t) - dirName, err := ioutil.TempDir("", "mkdir") + dirName, err := os.MkdirTemp("", "mkdir") if err != nil { t.Fatalf("Couldn't create temp dir: %v", err) } @@ -190,7 +190,7 @@ func buildTree(base string, tree map[string]node) error { func readTree(base, root string) (map[string]node, error) { tree := make(map[string]node) - dirInfos, err := ioutil.ReadDir(base) + dirInfos, err := os.ReadDir(base) if err != nil { return nil, fmt.Errorf("Couldn't read directory entries for %q: %v", base, err) } @@ -239,7 +239,7 @@ func delUser(t *testing.T, name string) { } func TestParseSubidFileWithNewlinesAndComments(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "parsesubid") + tmpDir, err := os.MkdirTemp("", "parsesubid") if err != nil { t.Fatal(err) } @@ -248,7 +248,7 @@ func TestParseSubidFileWithNewlinesAndComments(t *testing.T) { # empty default subuid/subgid file dockremap:231072:65536` - if err := ioutil.WriteFile(fnamePath, []byte(fcontent), 0644); err != nil { + if err := os.WriteFile(fnamePath, []byte(fcontent), 0644); err != nil { t.Fatal(err) } ranges, err := parseSubidFile(fnamePath, "dockremap") @@ -321,18 +321,13 @@ func TestNewIDMappings(t *testing.T) { tempUser, err := user.Lookup(tempUser) assert.Check(t, err) - gids, err := tempUser.GroupIds() - assert.Check(t, err) - group, err := user.LookupGroupId(string(gids[0])) - assert.Check(t, err) - - idMapping, err := NewIdentityMapping(tempUser.Username, group.Name) + idMapping, err := NewIdentityMapping(tempUser.Username) assert.Check(t, err) rootUID, rootGID, err := GetRootUIDGID(idMapping.UIDs(), idMapping.GIDs()) assert.Check(t, err) - dirName, err := ioutil.TempDir("", "mkdirall") + dirName, err := os.MkdirTemp("", "mkdirall") assert.Check(t, err, "Couldn't create temp directory") defer os.RemoveAll(dirName) @@ -382,7 +377,7 @@ func TestLookupUserAndGroupThatDoesNotExist(t *testing.T) { // returns a correct error in case a directory which it is about to create // already exists but is a file (rather than a directory). func TestMkdirIsNotDir(t *testing.T) { - file, err := ioutil.TempFile("", t.Name()) + file, err := os.CreateTemp("", t.Name()) if err != nil { t.Fatalf("Couldn't create temp dir: %v", err) } diff --git a/pkg/idtools/idtools_windows.go b/pkg/idtools/idtools_windows.go index 4ae38a1b17bb0..0f5aadd4961c4 100644 --- a/pkg/idtools/idtools_windows.go +++ b/pkg/idtools/idtools_windows.go @@ -6,12 +6,21 @@ import ( "github.com/docker/docker/pkg/system" ) +const ( + SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege" +) + +const ( + ContainerAdministratorSidString = "S-1-5-93-2-1" + ContainerUserSidString = "S-1-5-93-2-2" +) + // This is currently a wrapper around MkdirAll, however, since currently // permissions aren't set through this path, the identity isn't utilized. // Ownership is handled elsewhere, but in the future could be support here // too. func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error { - if err := system.MkdirAll(path, mode, ""); err != nil { + if err := system.MkdirAll(path, mode); err != nil { return err } return nil diff --git a/pkg/idtools/usergroupadd_linux.go b/pkg/idtools/usergroupadd_linux.go index 6272c5a404710..bf7ae0564ba55 100644 --- a/pkg/idtools/usergroupadd_linux.go +++ b/pkg/idtools/usergroupadd_linux.go @@ -17,18 +17,13 @@ import ( var ( once sync.Once userCommand string - - cmdTemplates = map[string]string{ - "adduser": "--system --shell /bin/false --no-create-home --disabled-login --disabled-password --group %s", - "useradd": "-r -s /bin/false %s", - "usermod": "-%s %d-%d %s", - } - idOutRegexp = regexp.MustCompile(`uid=([0-9]+).*gid=([0-9]+)`) +) + +const ( // default length for a UID/GID subordinate range defaultRangeLen = 65536 defaultRangeStart = 100000 - userMod = "usermod" ) // AddNamespaceRangesUser takes a username and uses the standard system @@ -67,7 +62,7 @@ func AddNamespaceRangesUser(name string) (int, int, error) { return uid, gid, nil } -func addUser(userName string) error { +func addUser(name string) error { once.Do(func() { // set up which commands are used for adding users/groups dependent on distro if _, err := resolveBinary("adduser"); err == nil { @@ -76,13 +71,18 @@ func addUser(userName string) error { userCommand = "useradd" } }) - if userCommand == "" { - return fmt.Errorf("Cannot add user; no useradd/adduser binary found") + var args []string + switch userCommand { + case "adduser": + args = []string{"--system", "--shell", "/bin/false", "--no-create-home", "--disabled-login", "--disabled-password", "--group", name} + case "useradd": + args = []string{"-r", "-s", "/bin/false", name} + default: + return fmt.Errorf("cannot add user; no useradd/adduser binary found") } - args := fmt.Sprintf(cmdTemplates[userCommand], userName) - out, err := execCmd(userCommand, args) - if err != nil { - return fmt.Errorf("Failed to add user with error: %v; output: %q", err, string(out)) + + if out, err := execCmd(userCommand, args...); err != nil { + return fmt.Errorf("failed to add user with error: %v; output: %q", err, string(out)) } return nil } @@ -101,7 +101,7 @@ func createSubordinateRanges(name string) error { if err != nil { return fmt.Errorf("Can't find available subuid range: %v", err) } - out, err := execCmd(userMod, fmt.Sprintf(cmdTemplates[userMod], "v", startID, startID+defaultRangeLen-1, name)) + out, err := execCmd("usermod", "-v", fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1), name) if err != nil { return fmt.Errorf("Unable to add subuid range to user: %q; output: %s, err: %v", name, out, err) } @@ -117,7 +117,7 @@ func createSubordinateRanges(name string) error { if err != nil { return fmt.Errorf("Can't find available subgid range: %v", err) } - out, err := execCmd(userMod, fmt.Sprintf(cmdTemplates[userMod], "w", startID, startID+defaultRangeLen-1, name)) + out, err := execCmd("usermod", "-w", fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1), name) if err != nil { return fmt.Errorf("Unable to add subgid range to user: %q; output: %s, err: %v", name, out, err) } diff --git a/pkg/idtools/usergroupadd_unsupported.go b/pkg/idtools/usergroupadd_unsupported.go index e7c4d63118c35..5e24577e2c26e 100644 --- a/pkg/idtools/usergroupadd_unsupported.go +++ b/pkg/idtools/usergroupadd_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package idtools // import "github.com/docker/docker/pkg/idtools" diff --git a/pkg/idtools/utils_unix.go b/pkg/idtools/utils_unix.go index 903ac4501b4bd..540672af5aff4 100644 --- a/pkg/idtools/utils_unix.go +++ b/pkg/idtools/utils_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package idtools // import "github.com/docker/docker/pkg/idtools" @@ -6,7 +7,6 @@ import ( "fmt" "os/exec" "path/filepath" - "strings" ) func resolveBinary(binname string) (string, error) { @@ -18,15 +18,15 @@ func resolveBinary(binname string) (string, error) { if err != nil { return "", err } - //only return no error if the final resolved binary basename - //matches what was searched for + // only return no error if the final resolved binary basename + // matches what was searched for if filepath.Base(resolvedPath) == binname { return resolvedPath, nil } return "", fmt.Errorf("Binary %q does not resolve to a binary of that name in $PATH (%q)", binname, resolvedPath) } -func execCmd(cmd, args string) ([]byte, error) { - execCmd := exec.Command(cmd, strings.Split(args, " ")...) +func execCmd(cmd string, arg ...string) ([]byte, error) { + execCmd := exec.Command(cmd, arg...) return execCmd.CombinedOutput() } diff --git a/pkg/ioutils/bytespipe.go b/pkg/ioutils/bytespipe.go index d4bbf3c9dcaf6..d1dfdae0cc2ef 100644 --- a/pkg/ioutils/bytespipe.go +++ b/pkg/ioutils/bytespipe.go @@ -50,12 +50,12 @@ func NewBytesPipe() *BytesPipe { // It can allocate new []byte slices in a process of writing. func (bp *BytesPipe) Write(p []byte) (int, error) { bp.mu.Lock() + defer bp.mu.Unlock() written := 0 loop0: for { if bp.closeErr != nil { - bp.mu.Unlock() return written, ErrClosed } @@ -72,7 +72,6 @@ loop0: // errBufferFull is an error we expect to get if the buffer is full if err != nil && err != errBufferFull { bp.wait.Broadcast() - bp.mu.Unlock() return written, err } @@ -100,7 +99,6 @@ loop0: bp.buf = append(bp.buf, getBuffer(nextCap)) } bp.wait.Broadcast() - bp.mu.Unlock() return written, nil } @@ -126,16 +124,14 @@ func (bp *BytesPipe) Close() error { // Data could be read only once. func (bp *BytesPipe) Read(p []byte) (n int, err error) { bp.mu.Lock() + defer bp.mu.Unlock() if bp.bufLen == 0 { if bp.closeErr != nil { - bp.mu.Unlock() return 0, bp.closeErr } bp.wait.Wait() if bp.bufLen == 0 && bp.closeErr != nil { - err := bp.closeErr - bp.mu.Unlock() - return 0, err + return 0, bp.closeErr } } @@ -160,7 +156,6 @@ func (bp *BytesPipe) Read(p []byte) (n int, err error) { } bp.wait.Broadcast() - bp.mu.Unlock() return } diff --git a/pkg/ioutils/bytespipe_test.go b/pkg/ioutils/bytespipe_test.go index 9101f20a21aa9..5e361874821da 100644 --- a/pkg/ioutils/bytespipe_test.go +++ b/pkg/ioutils/bytespipe_test.go @@ -1,7 +1,7 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( - "crypto/sha1" + "crypto/sha256" "encoding/hex" "math/rand" "testing" @@ -77,7 +77,7 @@ func TestBytesPipeWriteRandomChunks(t *testing.T) { for _, c := range cases { // first pass: write directly to hash - hash := sha1.New() + hash := sha256.New() for i := 0; i < c.iterations*c.writesPerLoop; i++ { if _, err := hash.Write(testMessage[:writeChunks[i%len(writeChunks)]]); err != nil { t.Fatal(err) diff --git a/pkg/ioutils/fswriters.go b/pkg/ioutils/fswriters.go index 534d66ac268a0..82671d8cd55c5 100644 --- a/pkg/ioutils/fswriters.go +++ b/pkg/ioutils/fswriters.go @@ -2,7 +2,6 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( "io" - "io/ioutil" "os" "path/filepath" ) @@ -11,7 +10,7 @@ import ( // temporary file and closing it atomically changes the temporary file to // destination path. Writing and closing concurrently is not allowed. func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) { - f, err := ioutil.TempFile(filepath.Dir(filename), ".tmp-"+filepath.Base(filename)) + f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename)) if err != nil { return nil, err } @@ -94,7 +93,7 @@ type AtomicWriteSet struct { // commit. If no temporary directory is given the system // default is used. func NewAtomicWriteSet(tmpDir string) (*AtomicWriteSet, error) { - td, err := ioutil.TempDir(tmpDir, "write-set-") + td, err := os.MkdirTemp(tmpDir, "write-set-") if err != nil { return nil, err } diff --git a/pkg/ioutils/fswriters_test.go b/pkg/ioutils/fswriters_test.go index b283045de5b3d..d63556138850d 100644 --- a/pkg/ioutils/fswriters_test.go +++ b/pkg/ioutils/fswriters_test.go @@ -2,7 +2,6 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( "bytes" - "io/ioutil" "os" "path/filepath" "runtime" @@ -21,7 +20,7 @@ func init() { } func TestAtomicWriteToFile(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "atomic-writers-test") + tmpDir, err := os.MkdirTemp("", "atomic-writers-test") if err != nil { t.Fatalf("Error when creating temporary directory: %s", err) } @@ -32,7 +31,7 @@ func TestAtomicWriteToFile(t *testing.T) { t.Fatalf("Error writing to file: %v", err) } - actual, err := ioutil.ReadFile(filepath.Join(tmpDir, "foo")) + actual, err := os.ReadFile(filepath.Join(tmpDir, "foo")) if err != nil { t.Fatalf("Error reading from file: %v", err) } @@ -45,13 +44,13 @@ func TestAtomicWriteToFile(t *testing.T) { if err != nil { t.Fatalf("Error statting file: %v", err) } - if expected := os.FileMode(testMode); st.Mode() != expected { + if expected := testMode; st.Mode() != expected { t.Fatalf("Mode mismatched, expected %o, got %o", expected, st.Mode()) } } func TestAtomicWriteSetCommit(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "atomic-writerset-test") + tmpDir, err := os.MkdirTemp("", "atomic-writerset-test") if err != nil { t.Fatalf("Error when creating temporary directory: %s", err) } @@ -72,7 +71,7 @@ func TestAtomicWriteSetCommit(t *testing.T) { t.Fatalf("Error writing to file: %v", err) } - if _, err := ioutil.ReadFile(filepath.Join(targetDir, "foo")); err == nil { + if _, err := os.ReadFile(filepath.Join(targetDir, "foo")); err == nil { t.Fatalf("Expected error reading file where should not exist") } @@ -80,7 +79,7 @@ func TestAtomicWriteSetCommit(t *testing.T) { t.Fatalf("Error committing file: %s", err) } - actual, err := ioutil.ReadFile(filepath.Join(targetDir, "foo")) + actual, err := os.ReadFile(filepath.Join(targetDir, "foo")) if err != nil { t.Fatalf("Error reading from file: %v", err) } @@ -93,14 +92,14 @@ func TestAtomicWriteSetCommit(t *testing.T) { if err != nil { t.Fatalf("Error statting file: %v", err) } - if expected := os.FileMode(testMode); st.Mode() != expected { + if expected := testMode; st.Mode() != expected { t.Fatalf("Mode mismatched, expected %o, got %o", expected, st.Mode()) } } func TestAtomicWriteSetCancel(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "atomic-writerset-test") + tmpDir, err := os.MkdirTemp("", "atomic-writerset-test") if err != nil { t.Fatalf("Error when creating temporary directory: %s", err) } @@ -124,7 +123,7 @@ func TestAtomicWriteSetCancel(t *testing.T) { t.Fatalf("Error committing file: %s", err) } - if _, err := ioutil.ReadFile(filepath.Join(tmpDir, "target", "foo")); err == nil { + if _, err := os.ReadFile(filepath.Join(tmpDir, "target", "foo")); err == nil { t.Fatalf("Expected error reading file where should not exist") } else if !os.IsNotExist(err) { t.Fatalf("Unexpected error reading file: %s", err) diff --git a/pkg/ioutils/readers.go b/pkg/ioutils/readers.go index 1f657bd3dcaa9..de00b95e3f64c 100644 --- a/pkg/ioutils/readers.go +++ b/pkg/ioutils/readers.go @@ -2,9 +2,12 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( "context" - "crypto/sha256" - "encoding/hex" "io" + + // make sure crypto.SHA256, crypto.sha512 and crypto.SHA384 are registered + // TODO remove once https://github.com/opencontainers/go-digest/pull/64 is merged. + _ "crypto/sha256" + _ "crypto/sha512" ) // ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser @@ -49,15 +52,6 @@ func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader { } } -// HashData returns the sha256 sum of src. -func HashData(src io.Reader) (string, error) { - h := sha256.New() - if _, err := io.Copy(h, src); err != nil { - return "", err - } - return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil -} - // OnEOFReader wraps an io.ReadCloser and a function // the function will run at the end of file or close the file. type OnEOFReader struct { diff --git a/pkg/ioutils/readers_test.go b/pkg/ioutils/readers_test.go index e645c78d83b1c..854df4c8ebbd5 100644 --- a/pkg/ioutils/readers_test.go +++ b/pkg/ioutils/readers_test.go @@ -3,13 +3,13 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( "context" "fmt" - "io/ioutil" + "io" "strings" "testing" "time" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) // Implement io.Reader @@ -58,18 +58,6 @@ func TestReaderErrWrapperRead(t *testing.T) { } } -func TestHashData(t *testing.T) { - reader := strings.NewReader("hash-me") - actual, err := HashData(reader) - if err != nil { - t.Fatal(err) - } - expected := "sha256:4d11186aed035cc624d553e10db358492c84a7cd6b9670d92123c144930450aa" - if actual != expected { - t.Fatalf("Expecting %s, got %s", expected, actual) - } -} - type perpetualReader struct{} func (p *perpetualReader) Read(buf []byte) (n int, err error) { @@ -82,7 +70,7 @@ func (p *perpetualReader) Read(buf []byte) (n int, err error) { func TestCancelReadCloser(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() - cancelReadCloser := NewCancelReadCloser(ctx, ioutil.NopCloser(&perpetualReader{})) + cancelReadCloser := NewCancelReadCloser(ctx, io.NopCloser(&perpetualReader{})) for { var buf [128]byte _, err := cancelReadCloser.Read(buf[:]) diff --git a/pkg/ioutils/temp_unix.go b/pkg/ioutils/temp_unix.go index dc894f91314ee..7489122309824 100644 --- a/pkg/ioutils/temp_unix.go +++ b/pkg/ioutils/temp_unix.go @@ -1,10 +1,11 @@ +//go:build !windows // +build !windows package ioutils // import "github.com/docker/docker/pkg/ioutils" -import "io/ioutil" +import "os" -// TempDir on Unix systems is equivalent to ioutil.TempDir. +// TempDir on Unix systems is equivalent to os.MkdirTemp. func TempDir(dir, prefix string) (string, error) { - return ioutil.TempDir(dir, prefix) + return os.MkdirTemp(dir, prefix) } diff --git a/pkg/ioutils/temp_windows.go b/pkg/ioutils/temp_windows.go index ecaba2e36d261..a57fd9af6a874 100644 --- a/pkg/ioutils/temp_windows.go +++ b/pkg/ioutils/temp_windows.go @@ -1,14 +1,14 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( - "io/ioutil" + "os" "github.com/docker/docker/pkg/longpath" ) -// TempDir is the equivalent of ioutil.TempDir, except that the result is in Windows longpath format. +// TempDir is the equivalent of os.MkdirTemp, except that the result is in Windows longpath format. func TempDir(dir, prefix string) (string, error) { - tempDir, err := ioutil.TempDir(dir, prefix) + tempDir, err := os.MkdirTemp(dir, prefix) if err != nil { return "", err } diff --git a/pkg/jsonmessage/jsonmessage.go b/pkg/jsonmessage/jsonmessage.go index dd95f367041ec..cf8d04b1b2019 100644 --- a/pkg/jsonmessage/jsonmessage.go +++ b/pkg/jsonmessage/jsonmessage.go @@ -4,13 +4,12 @@ import ( "encoding/json" "fmt" "io" - "os" "strings" "time" - "github.com/Nvveen/Gotty" - "github.com/docker/docker/pkg/term" - "github.com/docker/go-units" + units "github.com/docker/go-units" + "github.com/moby/term" + "github.com/morikuni/aec" ) // RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to @@ -140,71 +139,34 @@ type JSONMessage struct { Stream string `json:"stream,omitempty"` Status string `json:"status,omitempty"` Progress *JSONProgress `json:"progressDetail,omitempty"` - ProgressMessage string `json:"progress,omitempty"` //deprecated + ProgressMessage string `json:"progress,omitempty"` // deprecated ID string `json:"id,omitempty"` From string `json:"from,omitempty"` Time int64 `json:"time,omitempty"` TimeNano int64 `json:"timeNano,omitempty"` Error *JSONError `json:"errorDetail,omitempty"` - ErrorMessage string `json:"error,omitempty"` //deprecated + ErrorMessage string `json:"error,omitempty"` // deprecated // Aux contains out-of-band data, such as digests for push signing and image id after building. Aux *json.RawMessage `json:"aux,omitempty"` } -/* Satisfied by gotty.TermInfo as well as noTermInfo from below */ -type termInfo interface { - Parse(attr string, params ...interface{}) (string, error) +func clearLine(out io.Writer) { + eraseMode := aec.EraseModes.All + cl := aec.EraseLine(eraseMode) + fmt.Fprint(out, cl) } -type noTermInfo struct{} // canary used when no terminfo. - -func (ti *noTermInfo) Parse(attr string, params ...interface{}) (string, error) { - return "", fmt.Errorf("noTermInfo") -} - -func clearLine(out io.Writer, ti termInfo) { - // el2 (clear whole line) is not exposed by terminfo. - - // First clear line from beginning to cursor - if attr, err := ti.Parse("el1"); err == nil { - fmt.Fprintf(out, "%s", attr) - } else { - fmt.Fprintf(out, "\x1b[1K") - } - // Then clear line from cursor to end - if attr, err := ti.Parse("el"); err == nil { - fmt.Fprintf(out, "%s", attr) - } else { - fmt.Fprintf(out, "\x1b[K") - } -} - -func cursorUp(out io.Writer, ti termInfo, l int) { - if l == 0 { // Should never be the case, but be tolerant - return - } - if attr, err := ti.Parse("cuu", l); err == nil { - fmt.Fprintf(out, "%s", attr) - } else { - fmt.Fprintf(out, "\x1b[%dA", l) - } +func cursorUp(out io.Writer, l uint) { + fmt.Fprint(out, aec.Up(l)) } -func cursorDown(out io.Writer, ti termInfo, l int) { - if l == 0 { // Should never be the case, but be tolerant - return - } - if attr, err := ti.Parse("cud", l); err == nil { - fmt.Fprintf(out, "%s", attr) - } else { - fmt.Fprintf(out, "\x1b[%dB", l) - } +func cursorDown(out io.Writer, l uint) { + fmt.Fprint(out, aec.Down(l)) } -// Display displays the JSONMessage to `out`. `termInfo` is non-nil if `out` -// is a terminal. If this is the case, it will erase the entire current line -// when displaying the progressbar. -func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error { +// Display displays the JSONMessage to `out`. If `isTerminal` is true, it will erase the +// entire current line when displaying the progressbar. +func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error { if jm.Error != nil { if jm.Error.Code == 401 { return fmt.Errorf("authentication is required") @@ -212,11 +174,11 @@ func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error { return jm.Error } var endl string - if termInfo != nil && jm.Stream == "" && jm.Progress != nil { - clearLine(out, termInfo) + if isTerminal && jm.Stream == "" && jm.Progress != nil { + clearLine(out) endl = "\r" - fmt.Fprintf(out, endl) - } else if jm.Progress != nil && jm.Progress.String() != "" { //disable progressbar in non-terminal + fmt.Fprint(out, endl) + } else if jm.Progress != nil && jm.Progress.String() != "" { // disable progressbar in non-terminal return nil } if jm.TimeNano != 0 { @@ -230,9 +192,9 @@ func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error { if jm.From != "" { fmt.Fprintf(out, "(from %s) ", jm.From) } - if jm.Progress != nil && termInfo != nil { + if jm.Progress != nil && isTerminal { fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl) - } else if jm.ProgressMessage != "" { //deprecated + } else if jm.ProgressMessage != "" { // deprecated fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl) } else if jm.Stream != "" { fmt.Fprintf(out, "%s%s", jm.Stream, endl) @@ -248,25 +210,11 @@ func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error { func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error { var ( dec = json.NewDecoder(in) - ids = make(map[string]int) + ids = make(map[string]uint) ) - var termInfo termInfo - - if isTerminal { - term := os.Getenv("TERM") - if term == "" { - term = "vt102" - } - - var err error - if termInfo, err = gotty.OpenTermInfo(term); err != nil { - termInfo = &noTermInfo{} - } - } - for { - diff := 0 + var diff uint var jm JSONMessage if err := dec.Decode(&jm); err != nil { if err == io.EOF { @@ -294,15 +242,15 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, // when we output something that's not // accounted for in the map, such as a line // with no ID. - line = len(ids) + line = uint(len(ids)) ids[jm.ID] = line - if termInfo != nil { + if isTerminal { fmt.Fprintf(out, "\n") } } - diff = len(ids) - line - if termInfo != nil { - cursorUp(out, termInfo, diff) + diff = uint(len(ids)) - line + if isTerminal { + cursorUp(out, diff) } } else { // When outputting something that isn't progress @@ -310,11 +258,11 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, // don't want progress entries from some previous // operation to be updated (for example, pull -a // with multiple tags). - ids = make(map[string]int) + ids = make(map[string]uint) } - err := jm.Display(out, termInfo) - if jm.ID != "" && termInfo != nil { - cursorDown(out, termInfo, diff) + err := jm.Display(out, isTerminal) + if jm.ID != "" && isTerminal { + cursorDown(out, diff) } if err != nil { return err diff --git a/pkg/jsonmessage/jsonmessage_test.go b/pkg/jsonmessage/jsonmessage_test.go index 223d9c7f5a821..f1a7be2a7b5e3 100644 --- a/pkg/jsonmessage/jsonmessage_test.go +++ b/pkg/jsonmessage/jsonmessage_test.go @@ -8,9 +8,9 @@ import ( "testing" "time" - "github.com/docker/docker/pkg/term" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/moby/term" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestError(t *testing.T) { @@ -180,7 +180,7 @@ func TestJSONMessageDisplay(t *testing.T) { Progress: &JSONProgress{Current: 1}, }: { "", - fmt.Sprintf("%c[1K%c[K\rstatus 1B\r", 27, 27), + fmt.Sprintf("%c[2K\rstatus 1B\r", 27), }, } @@ -188,7 +188,7 @@ func TestJSONMessageDisplay(t *testing.T) { for jsonMessage, expectedMessages := range messages { // Without terminal data := bytes.NewBuffer([]byte{}) - if err := jsonMessage.Display(data, nil); err != nil { + if err := jsonMessage.Display(data, false); err != nil { t.Fatal(err) } if data.String() != expectedMessages[0] { @@ -196,7 +196,7 @@ func TestJSONMessageDisplay(t *testing.T) { } // With terminal data = bytes.NewBuffer([]byte{}) - if err := jsonMessage.Display(data, &noTermInfo{}); err != nil { + if err := jsonMessage.Display(data, true); err != nil { t.Fatal(err) } if data.String() != expectedMessages[1] { @@ -210,13 +210,13 @@ func TestJSONMessageDisplayWithJSONError(t *testing.T) { data := bytes.NewBuffer([]byte{}) jsonMessage := JSONMessage{Error: &JSONError{404, "Can't find it"}} - err := jsonMessage.Display(data, &noTermInfo{}) + err := jsonMessage.Display(data, true) if err == nil || err.Error() != "Can't find it" { t.Fatalf("Expected a JSONError 404, got %q", err) } jsonMessage = JSONMessage{Error: &JSONError{401, "Anything"}} - err = jsonMessage.Display(data, &noTermInfo{}) + err = jsonMessage.Display(data, true) assert.Check(t, is.Error(err, "authentication is required")) } @@ -228,8 +228,9 @@ func TestDisplayJSONMessagesStreamInvalidJSON(t *testing.T) { reader := strings.NewReader("This is not a 'valid' JSON []") inFd, _ = term.GetFdInfo(reader) - if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err == nil && err.Error()[:17] != "invalid character" { - t.Fatalf("Should have thrown an error (invalid character in ..), got %q", err) + exp := "invalid character " + if err := DisplayJSONMessagesStream(reader, data, inFd, false, nil); err == nil || !strings.HasPrefix(err.Error(), exp) { + t.Fatalf("Expected error (%s...), got %q", exp, err) } } @@ -251,7 +252,7 @@ func TestDisplayJSONMessagesStream(t *testing.T) { // Without progress, with ID "{ \"id\": \"ID\",\"status\": \"status\" }": { "ID: status\n", - fmt.Sprintf("ID: status\n"), + "ID: status\n", }, // With progress "{ \"id\": \"ID\", \"status\": \"status\", \"progress\": \"ProgressMessage\" }": { @@ -261,7 +262,7 @@ func TestDisplayJSONMessagesStream(t *testing.T) { // With progressDetail "{ \"id\": \"ID\", \"status\": \"status\", \"progressDetail\": { \"Current\": 1} }": { "", // progressbar is disabled in non-terminal - fmt.Sprintf("\n%c[%dA%c[1K%c[K\rID: status 1B\r%c[%dB", 27, 1, 27, 27, 27, 1), + fmt.Sprintf("\n%c[%dA%c[2K\rID: status 1B\r%c[%dB", 27, 1, 27, 27, 1), }, } diff --git a/pkg/locker/README.md b/pkg/locker/README.md deleted file mode 100644 index ce787aefb3551..0000000000000 --- a/pkg/locker/README.md +++ /dev/null @@ -1,65 +0,0 @@ -Locker -===== - -locker provides a mechanism for creating finer-grained locking to help -free up more global locks to handle other tasks. - -The implementation looks close to a sync.Mutex, however, the user must provide a -reference to use to refer to the underlying lock when locking and unlocking, -and unlock may generate an error. - -If a lock with a given name does not exist when `Lock` is called, one is -created. -Lock references are automatically cleaned up on `Unlock` if nothing else is -waiting for the lock. - - -## Usage - -```go -package important - -import ( - "sync" - "time" - - "github.com/docker/docker/pkg/locker" -) - -type important struct { - locks *locker.Locker - data map[string]interface{} - mu sync.Mutex -} - -func (i *important) Get(name string) interface{} { - i.locks.Lock(name) - defer i.locks.Unlock(name) - return i.data[name] -} - -func (i *important) Create(name string, data interface{}) { - i.locks.Lock(name) - defer i.locks.Unlock(name) - - i.createImportant(data) - - i.mu.Lock() - i.data[name] = data - i.mu.Unlock() -} - -func (i *important) createImportant(data interface{}) { - time.Sleep(10 * time.Second) -} -``` - -For functions dealing with a given name, always lock at the beginning of the -function (or before doing anything with the underlying state), this ensures any -other function that is dealing with the same name will block. - -When needing to modify the underlying data, use the global lock to ensure nothing -else is modifying it at the same time. -Since name lock is already in place, no reads will occur while the modification -is being performed. - diff --git a/pkg/locker/locker.go b/pkg/locker/locker.go index dbd47fc46558f..4d250ad542a90 100644 --- a/pkg/locker/locker.go +++ b/pkg/locker/locker.go @@ -14,99 +14,17 @@ waiting for the lock. package locker // import "github.com/docker/docker/pkg/locker" import ( - "errors" - "sync" - "sync/atomic" + "github.com/moby/locker" ) // ErrNoSuchLock is returned when the requested lock does not exist -var ErrNoSuchLock = errors.New("no such lock") +// Deprecated: use github.com/moby/locker.ErrNoSuchLock +var ErrNoSuchLock = locker.ErrNoSuchLock // Locker provides a locking mechanism based on the passed in reference name -type Locker struct { - mu sync.Mutex - locks map[string]*lockCtr -} - -// lockCtr is used by Locker to represent a lock with a given name. -type lockCtr struct { - mu sync.Mutex - // waiters is the number of waiters waiting to acquire the lock - // this is int32 instead of uint32 so we can add `-1` in `dec()` - waiters int32 -} - -// inc increments the number of waiters waiting for the lock -func (l *lockCtr) inc() { - atomic.AddInt32(&l.waiters, 1) -} - -// dec decrements the number of waiters waiting on the lock -func (l *lockCtr) dec() { - atomic.AddInt32(&l.waiters, -1) -} - -// count gets the current number of waiters -func (l *lockCtr) count() int32 { - return atomic.LoadInt32(&l.waiters) -} - -// Lock locks the mutex -func (l *lockCtr) Lock() { - l.mu.Lock() -} - -// Unlock unlocks the mutex -func (l *lockCtr) Unlock() { - l.mu.Unlock() -} +// Deprecated: use github.com/moby/locker.Locker +type Locker = locker.Locker // New creates a new Locker -func New() *Locker { - return &Locker{ - locks: make(map[string]*lockCtr), - } -} - -// Lock locks a mutex with the given name. If it doesn't exist, one is created -func (l *Locker) Lock(name string) { - l.mu.Lock() - if l.locks == nil { - l.locks = make(map[string]*lockCtr) - } - - nameLock, exists := l.locks[name] - if !exists { - nameLock = &lockCtr{} - l.locks[name] = nameLock - } - - // increment the nameLock waiters while inside the main mutex - // this makes sure that the lock isn't deleted if `Lock` and `Unlock` are called concurrently - nameLock.inc() - l.mu.Unlock() - - // Lock the nameLock outside the main mutex so we don't block other operations - // once locked then we can decrement the number of waiters for this lock - nameLock.Lock() - nameLock.dec() -} - -// Unlock unlocks the mutex with the given name -// If the given lock is not being waited on by any other callers, it is deleted -func (l *Locker) Unlock(name string) error { - l.mu.Lock() - nameLock, exists := l.locks[name] - if !exists { - l.mu.Unlock() - return ErrNoSuchLock - } - - if nameLock.count() == 0 { - delete(l.locks, name) - } - nameLock.Unlock() - - l.mu.Unlock() - return nil -} +// Deprecated: use github.com/moby/locker.New +var New = locker.New diff --git a/pkg/locker/locker_test.go b/pkg/locker/locker_test.go deleted file mode 100644 index 2b0a8a55d6df6..0000000000000 --- a/pkg/locker/locker_test.go +++ /dev/null @@ -1,161 +0,0 @@ -package locker // import "github.com/docker/docker/pkg/locker" - -import ( - "math/rand" - "strconv" - "sync" - "testing" - "time" -) - -func TestLockCounter(t *testing.T) { - l := &lockCtr{} - l.inc() - - if l.waiters != 1 { - t.Fatal("counter inc failed") - } - - l.dec() - if l.waiters != 0 { - t.Fatal("counter dec failed") - } -} - -func TestLockerLock(t *testing.T) { - l := New() - l.Lock("test") - ctr := l.locks["test"] - - if ctr.count() != 0 { - t.Fatalf("expected waiters to be 0, got :%d", ctr.waiters) - } - - chDone := make(chan struct{}) - go func() { - l.Lock("test") - close(chDone) - }() - - chWaiting := make(chan struct{}) - go func() { - for range time.Tick(1 * time.Millisecond) { - if ctr.count() == 1 { - close(chWaiting) - break - } - } - }() - - select { - case <-chWaiting: - case <-time.After(3 * time.Second): - t.Fatal("timed out waiting for lock waiters to be incremented") - } - - select { - case <-chDone: - t.Fatal("lock should not have returned while it was still held") - default: - } - - if err := l.Unlock("test"); err != nil { - t.Fatal(err) - } - - select { - case <-chDone: - case <-time.After(3 * time.Second): - t.Fatalf("lock should have completed") - } - - if ctr.count() != 0 { - t.Fatalf("expected waiters to be 0, got: %d", ctr.count()) - } -} - -func TestLockerUnlock(t *testing.T) { - l := New() - - l.Lock("test") - l.Unlock("test") - - chDone := make(chan struct{}) - go func() { - l.Lock("test") - close(chDone) - }() - - select { - case <-chDone: - case <-time.After(3 * time.Second): - t.Fatalf("lock should not be blocked") - } -} - -func TestLockerConcurrency(t *testing.T) { - l := New() - - var wg sync.WaitGroup - for i := 0; i <= 10000; i++ { - wg.Add(1) - go func() { - l.Lock("test") - // if there is a concurrency issue, will very likely panic here - l.Unlock("test") - wg.Done() - }() - } - - chDone := make(chan struct{}) - go func() { - wg.Wait() - close(chDone) - }() - - select { - case <-chDone: - case <-time.After(10 * time.Second): - t.Fatal("timeout waiting for locks to complete") - } - - // Since everything has unlocked this should not exist anymore - if ctr, exists := l.locks["test"]; exists { - t.Fatalf("lock should not exist: %v", ctr) - } -} - -func BenchmarkLocker(b *testing.B) { - l := New() - for i := 0; i < b.N; i++ { - l.Lock("test") - l.Unlock("test") - } -} - -func BenchmarkLockerParallel(b *testing.B) { - l := New() - b.SetParallelism(128) - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - l.Lock("test") - l.Unlock("test") - } - }) -} - -func BenchmarkLockerMoreKeys(b *testing.B) { - l := New() - var keys []string - for i := 0; i < 64; i++ { - keys = append(keys, strconv.Itoa(i)) - } - b.SetParallelism(128) - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - k := keys[rand.Intn(len(keys))] - l.Lock(k) - l.Unlock(k) - } - }) -} diff --git a/pkg/loopback/attach_loopback.go b/pkg/loopback/attach_loopback.go index 94feb8fc7d42a..68135d87a82fc 100644 --- a/pkg/loopback/attach_loopback.go +++ b/pkg/loopback/attach_loopback.go @@ -1,4 +1,5 @@ -// +build linux,cgo +//go:build linux +// +build linux package loopback // import "github.com/docker/docker/pkg/loopback" @@ -116,10 +117,10 @@ func AttachLoopDevice(sparseName string) (loop *os.File, err error) { } // Set the status of the loopback device - loopInfo := &loopInfo64{ - loFileName: stringToLoopName(loopFile.Name()), - loOffset: 0, - loFlags: LoFlagsAutoClear, + loopInfo := &unix.LoopInfo64{ + File_name: stringToLoopName(loopFile.Name()), + Offset: 0, + Flags: LoFlagsAutoClear, } if err := ioctlLoopSetStatus64(loopFile.Fd(), loopInfo); err != nil { diff --git a/pkg/loopback/ioctl.go b/pkg/loopback/ioctl.go index 612fd00abee94..8087b187cd616 100644 --- a/pkg/loopback/ioctl.go +++ b/pkg/loopback/ioctl.go @@ -1,4 +1,5 @@ -// +build linux,cgo +//go:build linux +// +build linux package loopback // import "github.com/docker/docker/pkg/loopback" @@ -9,40 +10,44 @@ import ( ) func ioctlLoopCtlGetFree(fd uintptr) (int, error) { - index, err := unix.IoctlGetInt(int(fd), LoopCtlGetFree) - if err != nil { + // The ioctl interface for /dev/loop-control (since Linux 3.1) is a bit + // off compared to what you'd expect: instead of writing an integer to a + // parameter pointer like unix.IoctlGetInt() expects, it returns the first + // available loop device index directly. + ioctlReturn, _, err := unix.Syscall(unix.SYS_IOCTL, fd, LoopCtlGetFree, 0) + if err != 0 { return 0, err } - return index, nil + return int(ioctlReturn), nil } func ioctlLoopSetFd(loopFd, sparseFd uintptr) error { - return unix.IoctlSetInt(int(loopFd), LoopSetFd, int(sparseFd)) + return unix.IoctlSetInt(int(loopFd), unix.LOOP_SET_FD, int(sparseFd)) } -func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *loopInfo64) error { - if _, _, err := unix.Syscall(unix.SYS_IOCTL, loopFd, LoopSetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 { +func ioctlLoopSetStatus64(loopFd uintptr, loopInfo *unix.LoopInfo64) error { + if _, _, err := unix.Syscall(unix.SYS_IOCTL, loopFd, unix.LOOP_SET_STATUS64, uintptr(unsafe.Pointer(loopInfo))); err != 0 { return err } return nil } func ioctlLoopClrFd(loopFd uintptr) error { - if _, _, err := unix.Syscall(unix.SYS_IOCTL, loopFd, LoopClrFd, 0); err != 0 { + if _, _, err := unix.Syscall(unix.SYS_IOCTL, loopFd, unix.LOOP_CLR_FD, 0); err != 0 { return err } return nil } -func ioctlLoopGetStatus64(loopFd uintptr) (*loopInfo64, error) { - loopInfo := &loopInfo64{} +func ioctlLoopGetStatus64(loopFd uintptr) (*unix.LoopInfo64, error) { + loopInfo := &unix.LoopInfo64{} - if _, _, err := unix.Syscall(unix.SYS_IOCTL, loopFd, LoopGetStatus64, uintptr(unsafe.Pointer(loopInfo))); err != 0 { + if _, _, err := unix.Syscall(unix.SYS_IOCTL, loopFd, unix.LOOP_GET_STATUS64, uintptr(unsafe.Pointer(loopInfo))); err != 0 { return nil, err } return loopInfo, nil } func ioctlLoopSetCapacity(loopFd uintptr, value int) error { - return unix.IoctlSetInt(int(loopFd), LoopSetCapacity, value) + return unix.IoctlSetInt(int(loopFd), unix.LOOP_SET_CAPACITY, value) } diff --git a/pkg/loopback/loop_wrapper.go b/pkg/loopback/loop_wrapper.go index 7206bfb95080a..10ef1985e8201 100644 --- a/pkg/loopback/loop_wrapper.go +++ b/pkg/loopback/loop_wrapper.go @@ -1,52 +1,25 @@ -// +build linux,cgo +//go:build linux +// +build linux package loopback // import "github.com/docker/docker/pkg/loopback" -/* -#include // FIXME: present only for defines, maybe we can remove it? - -#ifndef LOOP_CTL_GET_FREE - #define LOOP_CTL_GET_FREE 0x4C82 -#endif - -#ifndef LO_FLAGS_PARTSCAN - #define LO_FLAGS_PARTSCAN 8 -#endif - -*/ -import "C" - -type loopInfo64 struct { - loDevice uint64 /* ioctl r/o */ - loInode uint64 /* ioctl r/o */ - loRdevice uint64 /* ioctl r/o */ - loOffset uint64 - loSizelimit uint64 /* bytes, 0 == max available */ - loNumber uint32 /* ioctl r/o */ - loEncryptType uint32 - loEncryptKeySize uint32 /* ioctl w/o */ - loFlags uint32 /* ioctl r/o */ - loFileName [LoNameSize]uint8 - loCryptName [LoNameSize]uint8 - loEncryptKey [LoKeySize]uint8 /* ioctl w/o */ - loInit [2]uint64 -} +import "golang.org/x/sys/unix" // IOCTL consts const ( - LoopSetFd = C.LOOP_SET_FD - LoopCtlGetFree = C.LOOP_CTL_GET_FREE - LoopGetStatus64 = C.LOOP_GET_STATUS64 - LoopSetStatus64 = C.LOOP_SET_STATUS64 - LoopClrFd = C.LOOP_CLR_FD - LoopSetCapacity = C.LOOP_SET_CAPACITY + LoopSetFd = unix.LOOP_SET_FD + LoopCtlGetFree = unix.LOOP_CTL_GET_FREE + LoopGetStatus64 = unix.LOOP_GET_STATUS64 + LoopSetStatus64 = unix.LOOP_SET_STATUS64 + LoopClrFd = unix.LOOP_CLR_FD + LoopSetCapacity = unix.LOOP_SET_CAPACITY ) // LOOP consts. const ( - LoFlagsAutoClear = C.LO_FLAGS_AUTOCLEAR - LoFlagsReadOnly = C.LO_FLAGS_READ_ONLY - LoFlagsPartScan = C.LO_FLAGS_PARTSCAN - LoKeySize = C.LO_KEY_SIZE - LoNameSize = C.LO_NAME_SIZE + LoFlagsAutoClear = unix.LO_FLAGS_AUTOCLEAR + LoFlagsReadOnly = unix.LO_FLAGS_READ_ONLY + LoFlagsPartScan = unix.LO_FLAGS_PARTSCAN + LoKeySize = unix.LO_KEY_SIZE + LoNameSize = unix.LO_NAME_SIZE ) diff --git a/pkg/loopback/loopback.go b/pkg/loopback/loopback.go index 086655bc1a77f..ecdb398727147 100644 --- a/pkg/loopback/loopback.go +++ b/pkg/loopback/loopback.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package loopback // import "github.com/docker/docker/pkg/loopback" @@ -16,7 +17,7 @@ func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) { logrus.Errorf("Error get loopback backing file: %s", err) return 0, 0, ErrGetLoopbackBackingFile } - return loopInfo.loDevice, loopInfo.loInode, nil + return loopInfo.Device, loopInfo.Inode, nil } // SetCapacity reloads the size for the loopback device. @@ -37,7 +38,8 @@ func FindLoopDeviceFor(file *os.File) *os.File { return nil } targetInode := stat.Ino - targetDevice := stat.Dev + // the type is 32bit on mips + targetDevice := uint64(stat.Dev) //nolint: unconvert for i := 0; true; i++ { path := fmt.Sprintf("/dev/loop%d", i) diff --git a/pkg/mount/deprecated.go b/pkg/mount/deprecated.go new file mode 100644 index 0000000000000..16c8e5952d6c2 --- /dev/null +++ b/pkg/mount/deprecated.go @@ -0,0 +1,67 @@ +package mount // import "github.com/docker/docker/pkg/mount" + +// Deprecated: this package is not maintained and will be removed. +// Use github.com/moby/sys/mount and github.com/moby/sys/mountinfo instead. + +import ( + "github.com/moby/sys/mountinfo" +) + +//nolint:golint +type ( + // FilterFunc is a type. + // Deprecated: use github.com/moby/sys/mountinfo instead. + FilterFunc = func(*Info) (skip, stop bool) + + // Info is a type + // Deprecated: use github.com/moby/sys/mountinfo instead. + Info struct { + ID, Parent, Major, Minor int + Root, Mountpoint, Opts, Optional, Fstype, Source, VfsOpts string + } +) + +// Deprecated: use github.com/moby/sys/mountinfo instead. +//nolint:golint +var ( + Mounted = mountinfo.Mounted + PrefixFilter = mountinfo.PrefixFilter + SingleEntryFilter = mountinfo.SingleEntryFilter + ParentsFilter = mountinfo.ParentsFilter + FstypeFilter = mountinfo.FSTypeFilter +) + +// GetMounts is a function. +// +// Deprecated: use github.com/moby/sys/mountinfo.GetMounts() instead. +//nolint:golint +func GetMounts(f FilterFunc) ([]*Info, error) { + fi := func(i *mountinfo.Info) (skip, stop bool) { + return f(toLegacyInfo(i)) + } + + mounts, err := mountinfo.GetMounts(fi) + if err != nil { + return nil, err + } + mi := make([]*Info, len(mounts)) + for i, m := range mounts { + mi[i] = toLegacyInfo(m) + } + return mi, nil +} + +func toLegacyInfo(m *mountinfo.Info) *Info { + return &Info{ + ID: m.ID, + Parent: m.Parent, + Major: m.Major, + Minor: m.Minor, + Root: m.Root, + Mountpoint: m.Mountpoint, + Opts: m.Options, + Fstype: m.FSType, + Source: m.Source, + VfsOpts: m.VFSOptions, + } +} diff --git a/pkg/mount/deprecated_linux.go b/pkg/mount/deprecated_linux.go new file mode 100644 index 0000000000000..e4a4407a3254a --- /dev/null +++ b/pkg/mount/deprecated_linux.go @@ -0,0 +1,19 @@ +package mount // import "github.com/docker/docker/pkg/mount" + +import ( + sysmount "github.com/moby/sys/mount" +) + +// Deprecated: use github.com/moby/sys/mount instead. +//nolint:golint +var ( + MakeMount = sysmount.MakeMount + MakeShared = sysmount.MakeShared + MakeRShared = sysmount.MakeRShared + MakePrivate = sysmount.MakePrivate + MakeRPrivate = sysmount.MakeRPrivate + MakeSlave = sysmount.MakeSlave + MakeRSlave = sysmount.MakeRSlave + MakeUnbindable = sysmount.MakeUnbindable + MakeRUnbindable = sysmount.MakeRUnbindable +) diff --git a/pkg/mount/deprecated_unix.go b/pkg/mount/deprecated_unix.go new file mode 100644 index 0000000000000..6c79c20bf06f4 --- /dev/null +++ b/pkg/mount/deprecated_unix.go @@ -0,0 +1,53 @@ +//go:build !darwin && !windows +// +build !darwin,!windows + +package mount // import "github.com/docker/docker/pkg/mount" + +// Deprecated: this package is not maintained and will be removed. +// Use github.com/moby/sys/mount and github.com/moby/sys/mountinfo instead. + +import ( + sysmount "github.com/moby/sys/mount" +) + +// Deprecated: use github.com/moby/sys/mount instead. +//nolint:golint +var ( + Mount = sysmount.Mount + ForceMount = sysmount.Mount // a deprecated synonym + Unmount = sysmount.Unmount + RecursiveUnmount = sysmount.RecursiveUnmount +) + +// Deprecated: use github.com/moby/sys/mount instead. +//nolint:golint +const ( + RDONLY = sysmount.RDONLY + NOSUID = sysmount.NOSUID + NOEXEC = sysmount.NOEXEC + SYNCHRONOUS = sysmount.SYNCHRONOUS + NOATIME = sysmount.NOATIME + BIND = sysmount.BIND + DIRSYNC = sysmount.DIRSYNC + MANDLOCK = sysmount.MANDLOCK + NODEV = sysmount.NODEV + NODIRATIME = sysmount.NODIRATIME + UNBINDABLE = sysmount.UNBINDABLE + RUNBINDABLE = sysmount.RUNBINDABLE + PRIVATE = sysmount.PRIVATE + RPRIVATE = sysmount.RPRIVATE + SHARED = sysmount.SHARED + RSHARED = sysmount.RSHARED + SLAVE = sysmount.SLAVE + RSLAVE = sysmount.RSLAVE + RBIND = sysmount.RBIND + RELATIME = sysmount.RELATIME + REMOUNT = sysmount.REMOUNT + STRICTATIME = sysmount.STRICTATIME +) + +// Deprecated: use github.com/moby/sys/mount instead. +//nolint:golint +var ( + MergeTmpfsOptions = sysmount.MergeTmpfsOptions +) diff --git a/pkg/mount/flags.go b/pkg/mount/flags.go deleted file mode 100644 index 272363b68540a..0000000000000 --- a/pkg/mount/flags.go +++ /dev/null @@ -1,149 +0,0 @@ -package mount // import "github.com/docker/docker/pkg/mount" - -import ( - "fmt" - "strings" -) - -var flags = map[string]struct { - clear bool - flag int -}{ - "defaults": {false, 0}, - "ro": {false, RDONLY}, - "rw": {true, RDONLY}, - "suid": {true, NOSUID}, - "nosuid": {false, NOSUID}, - "dev": {true, NODEV}, - "nodev": {false, NODEV}, - "exec": {true, NOEXEC}, - "noexec": {false, NOEXEC}, - "sync": {false, SYNCHRONOUS}, - "async": {true, SYNCHRONOUS}, - "dirsync": {false, DIRSYNC}, - "remount": {false, REMOUNT}, - "mand": {false, MANDLOCK}, - "nomand": {true, MANDLOCK}, - "atime": {true, NOATIME}, - "noatime": {false, NOATIME}, - "diratime": {true, NODIRATIME}, - "nodiratime": {false, NODIRATIME}, - "bind": {false, BIND}, - "rbind": {false, RBIND}, - "unbindable": {false, UNBINDABLE}, - "runbindable": {false, RUNBINDABLE}, - "private": {false, PRIVATE}, - "rprivate": {false, RPRIVATE}, - "shared": {false, SHARED}, - "rshared": {false, RSHARED}, - "slave": {false, SLAVE}, - "rslave": {false, RSLAVE}, - "relatime": {false, RELATIME}, - "norelatime": {true, RELATIME}, - "strictatime": {false, STRICTATIME}, - "nostrictatime": {true, STRICTATIME}, -} - -var validFlags = map[string]bool{ - "": true, - "size": true, - "mode": true, - "uid": true, - "gid": true, - "nr_inodes": true, - "nr_blocks": true, - "mpol": true, -} - -var propagationFlags = map[string]bool{ - "bind": true, - "rbind": true, - "unbindable": true, - "runbindable": true, - "private": true, - "rprivate": true, - "shared": true, - "rshared": true, - "slave": true, - "rslave": true, -} - -// MergeTmpfsOptions merge mount options to make sure there is no duplicate. -func MergeTmpfsOptions(options []string) ([]string, error) { - // We use collisions maps to remove duplicates. - // For flag, the key is the flag value (the key for propagation flag is -1) - // For data=value, the key is the data - flagCollisions := map[int]bool{} - dataCollisions := map[string]bool{} - - var newOptions []string - // We process in reverse order - for i := len(options) - 1; i >= 0; i-- { - option := options[i] - if option == "defaults" { - continue - } - if f, ok := flags[option]; ok && f.flag != 0 { - // There is only one propagation mode - key := f.flag - if propagationFlags[option] { - key = -1 - } - // Check to see if there is collision for flag - if !flagCollisions[key] { - // We prepend the option and add to collision map - newOptions = append([]string{option}, newOptions...) - flagCollisions[key] = true - } - continue - } - opt := strings.SplitN(option, "=", 2) - if len(opt) != 2 || !validFlags[opt[0]] { - return nil, fmt.Errorf("Invalid tmpfs option %q", opt) - } - if !dataCollisions[opt[0]] { - // We prepend the option and add to collision map - newOptions = append([]string{option}, newOptions...) - dataCollisions[opt[0]] = true - } - } - - return newOptions, nil -} - -// Parse fstab type mount options into mount() flags -// and device specific data -func parseOptions(options string) (int, string) { - var ( - flag int - data []string - ) - - for _, o := range strings.Split(options, ",") { - // If the option does not exist in the flags table or the flag - // is not supported on the platform, - // then it is a data value for a specific fs type - if f, exists := flags[o]; exists && f.flag != 0 { - if f.clear { - flag &= ^f.flag - } else { - flag |= f.flag - } - } else { - data = append(data, o) - } - } - return flag, strings.Join(data, ",") -} - -// ParseTmpfsOptions parse fstab type mount options into flags and data -func ParseTmpfsOptions(options string) (int, string, error) { - flags, data := parseOptions(options) - for _, o := range strings.Split(data, ",") { - opt := strings.SplitN(o, "=", 2) - if !validFlags[opt[0]] { - return 0, "", fmt.Errorf("Invalid tmpfs option %q", opt) - } - } - return flags, data, nil -} diff --git a/pkg/mount/flags_freebsd.go b/pkg/mount/flags_freebsd.go deleted file mode 100644 index ef35ef90591ae..0000000000000 --- a/pkg/mount/flags_freebsd.go +++ /dev/null @@ -1,49 +0,0 @@ -// +build freebsd,cgo - -package mount // import "github.com/docker/docker/pkg/mount" - -/* -#include -*/ -import "C" - -const ( - // RDONLY will mount the filesystem as read-only. - RDONLY = C.MNT_RDONLY - - // NOSUID will not allow set-user-identifier or set-group-identifier bits to - // take effect. - NOSUID = C.MNT_NOSUID - - // NOEXEC will not allow execution of any binaries on the mounted file system. - NOEXEC = C.MNT_NOEXEC - - // SYNCHRONOUS will allow any I/O to the file system to be done synchronously. - SYNCHRONOUS = C.MNT_SYNCHRONOUS - - // NOATIME will not update the file access time when reading from a file. - NOATIME = C.MNT_NOATIME -) - -// These flags are unsupported. -const ( - BIND = 0 - DIRSYNC = 0 - MANDLOCK = 0 - NODEV = 0 - NODIRATIME = 0 - UNBINDABLE = 0 - RUNBINDABLE = 0 - PRIVATE = 0 - RPRIVATE = 0 - SHARED = 0 - RSHARED = 0 - SLAVE = 0 - RSLAVE = 0 - RBIND = 0 - RELATIVE = 0 - RELATIME = 0 - REMOUNT = 0 - STRICTATIME = 0 - mntDetach = 0 -) diff --git a/pkg/mount/flags_unsupported.go b/pkg/mount/flags_unsupported.go deleted file mode 100644 index cc6c4759083ad..0000000000000 --- a/pkg/mount/flags_unsupported.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build !linux,!freebsd freebsd,!cgo - -package mount // import "github.com/docker/docker/pkg/mount" - -// These flags are unsupported. -const ( - BIND = 0 - DIRSYNC = 0 - MANDLOCK = 0 - NOATIME = 0 - NODEV = 0 - NODIRATIME = 0 - NOEXEC = 0 - NOSUID = 0 - UNBINDABLE = 0 - RUNBINDABLE = 0 - PRIVATE = 0 - RPRIVATE = 0 - SHARED = 0 - RSHARED = 0 - SLAVE = 0 - RSLAVE = 0 - RBIND = 0 - RELATIME = 0 - RELATIVE = 0 - REMOUNT = 0 - STRICTATIME = 0 - SYNCHRONOUS = 0 - RDONLY = 0 - mntDetach = 0 -) diff --git a/pkg/mount/mount.go b/pkg/mount/mount.go deleted file mode 100644 index 874aff6545cc5..0000000000000 --- a/pkg/mount/mount.go +++ /dev/null @@ -1,141 +0,0 @@ -package mount // import "github.com/docker/docker/pkg/mount" - -import ( - "sort" - "strings" - "syscall" - - "github.com/sirupsen/logrus" -) - -// FilterFunc is a type defining a callback function -// to filter out unwanted entries. It takes a pointer -// to an Info struct (not fully populated, currently -// only Mountpoint is filled in), and returns two booleans: -// - skip: true if the entry should be skipped -// - stop: true if parsing should be stopped after the entry -type FilterFunc func(*Info) (skip, stop bool) - -// PrefixFilter discards all entries whose mount points -// do not start with a prefix specified -func PrefixFilter(prefix string) FilterFunc { - return func(m *Info) (bool, bool) { - skip := !strings.HasPrefix(m.Mountpoint, prefix) - return skip, false - } -} - -// SingleEntryFilter looks for a specific entry -func SingleEntryFilter(mp string) FilterFunc { - return func(m *Info) (bool, bool) { - if m.Mountpoint == mp { - return false, true // don't skip, stop now - } - return true, false // skip, keep going - } -} - -// ParentsFilter returns all entries whose mount points -// can be parents of a path specified, discarding others. -// For example, given `/var/lib/docker/something`, entries -// like `/var/lib/docker`, `/var` and `/` are returned. -func ParentsFilter(path string) FilterFunc { - return func(m *Info) (bool, bool) { - skip := !strings.HasPrefix(path, m.Mountpoint) - return skip, false - } -} - -// GetMounts retrieves a list of mounts for the current running process, -// with an optional filter applied (use nil for no filter). -func GetMounts(f FilterFunc) ([]*Info, error) { - return parseMountTable(f) -} - -// Mounted determines if a specified mountpoint has been mounted. -// On Linux it looks at /proc/self/mountinfo. -func Mounted(mountpoint string) (bool, error) { - entries, err := GetMounts(SingleEntryFilter(mountpoint)) - if err != nil { - return false, err - } - - return len(entries) > 0, nil -} - -// Mount will mount filesystem according to the specified configuration, on the -// condition that the target path is *not* already mounted. Options must be -// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See -// flags.go for supported option flags. -func Mount(device, target, mType, options string) error { - flag, _ := parseOptions(options) - if flag&REMOUNT != REMOUNT { - if mounted, err := Mounted(target); err != nil || mounted { - return err - } - } - return ForceMount(device, target, mType, options) -} - -// ForceMount will mount a filesystem according to the specified configuration, -// *regardless* if the target path is not already mounted. Options must be -// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See -// flags.go for supported option flags. -func ForceMount(device, target, mType, options string) error { - flag, data := parseOptions(options) - return mount(device, target, mType, uintptr(flag), data) -} - -// Unmount lazily unmounts a filesystem on supported platforms, otherwise -// does a normal unmount. -func Unmount(target string) error { - err := unmount(target, mntDetach) - if err == syscall.EINVAL { - // ignore "not mounted" error - err = nil - } - return err -} - -// RecursiveUnmount unmounts the target and all mounts underneath, starting with -// the deepsest mount first. -func RecursiveUnmount(target string) error { - mounts, err := parseMountTable(PrefixFilter(target)) - if err != nil { - return err - } - - // Make the deepest mount be first - sort.Slice(mounts, func(i, j int) bool { - return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint) - }) - - for i, m := range mounts { - logrus.Debugf("Trying to unmount %s", m.Mountpoint) - err = unmount(m.Mountpoint, mntDetach) - if err != nil { - // If the error is EINVAL either this whole package is wrong (invalid flags passed to unmount(2)) or this is - // not a mountpoint (which is ok in this case). - // Meanwhile calling `Mounted()` is very expensive. - // - // We've purposefully used `syscall.EINVAL` here instead of `unix.EINVAL` to avoid platform branching - // Since `EINVAL` is defined for both Windows and Linux in the `syscall` package (and other platforms), - // this is nicer than defining a custom value that we can refer to in each platform file. - if err == syscall.EINVAL { - continue - } - if i == len(mounts)-1 { - if mounted, e := Mounted(m.Mountpoint); e != nil || mounted { - return err - } - continue - } - // This is some submount, we can ignore this error for now, the final unmount will fail if this is a real problem - logrus.WithError(err).Warnf("Failed to unmount submount %s", m.Mountpoint) - continue - } - - logrus.Debugf("Unmounted %s", m.Mountpoint) - } - return nil -} diff --git a/pkg/mount/mount_unix_test.go b/pkg/mount/mount_unix_test.go deleted file mode 100644 index befff9d50c570..0000000000000 --- a/pkg/mount/mount_unix_test.go +++ /dev/null @@ -1,170 +0,0 @@ -// +build !windows - -package mount // import "github.com/docker/docker/pkg/mount" - -import ( - "os" - "path" - "testing" -) - -func TestMountOptionsParsing(t *testing.T) { - options := "noatime,ro,size=10k" - - flag, data := parseOptions(options) - - if data != "size=10k" { - t.Fatalf("Expected size=10 got %s", data) - } - - expectedFlag := NOATIME | RDONLY - - if flag != expectedFlag { - t.Fatalf("Expected %d got %d", expectedFlag, flag) - } -} - -func TestMounted(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("root required") - } - - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - sourcePath = path.Join(sourceDir, "file.txt") - targetPath = path.Join(targetDir, "file.txt") - ) - - os.Mkdir(sourceDir, 0777) - os.Mkdir(targetDir, 0777) - - f, err := os.Create(sourcePath) - if err != nil { - t.Fatal(err) - } - f.WriteString("hello") - f.Close() - - f, err = os.Create(targetPath) - if err != nil { - t.Fatal(err) - } - f.Close() - - if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - mounted, err := Mounted(targetDir) - if err != nil { - t.Fatal(err) - } - if !mounted { - t.Fatalf("Expected %s to be mounted", targetDir) - } - if _, err := os.Stat(targetDir); err != nil { - t.Fatal(err) - } -} - -func TestMountReadonly(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("root required") - } - - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - sourcePath = path.Join(sourceDir, "file.txt") - targetPath = path.Join(targetDir, "file.txt") - ) - - os.Mkdir(sourceDir, 0777) - os.Mkdir(targetDir, 0777) - - f, err := os.Create(sourcePath) - if err != nil { - t.Fatal(err) - } - f.WriteString("hello") - f.Close() - - f, err = os.Create(targetPath) - if err != nil { - t.Fatal(err) - } - f.Close() - - if err := Mount(sourceDir, targetDir, "none", "bind,ro"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - f, err = os.OpenFile(targetPath, os.O_RDWR, 0777) - if err == nil { - t.Fatal("Should not be able to open a ro file as rw") - } -} - -func TestGetMounts(t *testing.T) { - mounts, err := GetMounts(nil) - if err != nil { - t.Fatal(err) - } - - root := false - for _, entry := range mounts { - if entry.Mountpoint == "/" { - root = true - } - } - - if !root { - t.Fatal("/ should be mounted at least") - } -} - -func TestMergeTmpfsOptions(t *testing.T) { - options := []string{"noatime", "ro", "size=10k", "defaults", "atime", "defaults", "rw", "rprivate", "size=1024k", "slave"} - expected := []string{"atime", "rw", "size=1024k", "slave"} - merged, err := MergeTmpfsOptions(options) - if err != nil { - t.Fatal(err) - } - if len(expected) != len(merged) { - t.Fatalf("Expected %s got %s", expected, merged) - } - for index := range merged { - if merged[index] != expected[index] { - t.Fatalf("Expected %s for the %dth option, got %s", expected, index, merged) - } - } - - options = []string{"noatime", "ro", "size=10k", "atime", "rw", "rprivate", "size=1024k", "slave", "size"} - _, err = MergeTmpfsOptions(options) - if err == nil { - t.Fatal("Expected error got nil") - } -} diff --git a/pkg/mount/mounter_linux.go b/pkg/mount/mounter_linux.go deleted file mode 100644 index 631daf10a5ab6..0000000000000 --- a/pkg/mount/mounter_linux.go +++ /dev/null @@ -1,57 +0,0 @@ -package mount // import "github.com/docker/docker/pkg/mount" - -import ( - "golang.org/x/sys/unix" -) - -const ( - // ptypes is the set propagation types. - ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE - - // pflags is the full set valid flags for a change propagation call. - pflags = ptypes | unix.MS_REC | unix.MS_SILENT - - // broflags is the combination of bind and read only - broflags = unix.MS_BIND | unix.MS_RDONLY -) - -// isremount returns true if either device name or flags identify a remount request, false otherwise. -func isremount(device string, flags uintptr) bool { - switch { - // We treat device "" and "none" as a remount request to provide compatibility with - // requests that don't explicitly set MS_REMOUNT such as those manipulating bind mounts. - case flags&unix.MS_REMOUNT != 0, device == "", device == "none": - return true - default: - return false - } -} - -func mount(device, target, mType string, flags uintptr, data string) error { - oflags := flags &^ ptypes - if !isremount(device, flags) || data != "" { - // Initial call applying all non-propagation flags for mount - // or remount with changed data - if err := unix.Mount(device, target, mType, oflags, data); err != nil { - return err - } - } - - if flags&ptypes != 0 { - // Change the propagation type. - if err := unix.Mount("", target, "", flags&pflags, ""); err != nil { - return err - } - } - - if oflags&broflags == broflags { - // Remount the bind to apply read only. - return unix.Mount("", target, "", oflags|unix.MS_REMOUNT, "") - } - - return nil -} - -func unmount(target string, flag int) error { - return unix.Unmount(target, flag) -} diff --git a/pkg/mount/mounter_linux_test.go b/pkg/mount/mounter_linux_test.go deleted file mode 100644 index 336f3d5cdc159..0000000000000 --- a/pkg/mount/mounter_linux_test.go +++ /dev/null @@ -1,228 +0,0 @@ -// +build linux - -package mount // import "github.com/docker/docker/pkg/mount" - -import ( - "fmt" - "io/ioutil" - "os" - "strings" - "testing" -) - -func TestMount(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("root required") - } - - source, err := ioutil.TempDir("", "mount-test-source-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(source) - - // Ensure we have a known start point by mounting tmpfs with given options - if err := Mount("tmpfs", source, "tmpfs", "private"); err != nil { - t.Fatal(err) - } - defer ensureUnmount(t, source) - validateMount(t, source, "", "", "") - if t.Failed() { - t.FailNow() - } - - target, err := ioutil.TempDir("", "mount-test-target-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(target) - - tests := []struct { - source string - ftype string - options string - expectedOpts string - expectedOptional string - expectedVFS string - }{ - // No options - {"tmpfs", "tmpfs", "", "", "", ""}, - // Default rw / ro test - {source, "", "bind", "", "", ""}, - {source, "", "bind,private", "", "", ""}, - {source, "", "bind,shared", "", "shared", ""}, - {source, "", "bind,slave", "", "master", ""}, - {source, "", "bind,unbindable", "", "unbindable", ""}, - // Read Write tests - {source, "", "bind,rw", "rw", "", ""}, - {source, "", "bind,rw,private", "rw", "", ""}, - {source, "", "bind,rw,shared", "rw", "shared", ""}, - {source, "", "bind,rw,slave", "rw", "master", ""}, - {source, "", "bind,rw,unbindable", "rw", "unbindable", ""}, - // Read Only tests - {source, "", "bind,ro", "ro", "", ""}, - {source, "", "bind,ro,private", "ro", "", ""}, - {source, "", "bind,ro,shared", "ro", "shared", ""}, - {source, "", "bind,ro,slave", "ro", "master", ""}, - {source, "", "bind,ro,unbindable", "ro", "unbindable", ""}, - // Remount tests to change per filesystem options - {"", "", "remount,size=128k", "rw", "", "rw,size=128k"}, - {"", "", "remount,ro,size=128k", "ro", "", "ro,size=128k"}, - } - - for _, tc := range tests { - ftype, options := tc.ftype, tc.options - if tc.ftype == "" { - ftype = "none" - } - if tc.options == "" { - options = "none" - } - - t.Run(fmt.Sprintf("%v-%v", ftype, options), func(t *testing.T) { - if strings.Contains(tc.options, "slave") { - // Slave requires a shared source - if err := MakeShared(source); err != nil { - t.Fatal(err) - } - defer func() { - if err := MakePrivate(source); err != nil { - t.Fatal(err) - } - }() - } - if strings.Contains(tc.options, "remount") { - // create a new mount to remount first - if err := Mount("tmpfs", target, "tmpfs", ""); err != nil { - t.Fatal(err) - } - } - if err := Mount(tc.source, target, tc.ftype, tc.options); err != nil { - t.Fatal(err) - } - defer ensureUnmount(t, target) - validateMount(t, target, tc.expectedOpts, tc.expectedOptional, tc.expectedVFS) - }) - } -} - -// ensureUnmount umounts mnt checking for errors -func ensureUnmount(t *testing.T, mnt string) { - if err := Unmount(mnt); err != nil { - t.Error(err) - } -} - -// validateMount checks that mnt has the given options -func validateMount(t *testing.T, mnt string, opts, optional, vfs string) { - info, err := GetMounts(nil) - if err != nil { - t.Fatal(err) - } - - wantedOpts := make(map[string]struct{}) - if opts != "" { - for _, opt := range strings.Split(opts, ",") { - wantedOpts[opt] = struct{}{} - } - } - - wantedOptional := make(map[string]struct{}) - if optional != "" { - for _, opt := range strings.Split(optional, ",") { - wantedOptional[opt] = struct{}{} - } - } - - wantedVFS := make(map[string]struct{}) - if vfs != "" { - for _, opt := range strings.Split(vfs, ",") { - wantedVFS[opt] = struct{}{} - } - } - - mnts := make(map[int]*Info, len(info)) - for _, mi := range info { - mnts[mi.ID] = mi - } - - for _, mi := range info { - if mi.Mountpoint != mnt { - continue - } - - // Use parent info as the defaults - p := mnts[mi.Parent] - pOpts := make(map[string]struct{}) - if p.Opts != "" { - for _, opt := range strings.Split(p.Opts, ",") { - pOpts[clean(opt)] = struct{}{} - } - } - pOptional := make(map[string]struct{}) - if p.Optional != "" { - for _, field := range strings.Split(p.Optional, ",") { - pOptional[clean(field)] = struct{}{} - } - } - - // Validate Opts - if mi.Opts != "" { - for _, opt := range strings.Split(mi.Opts, ",") { - opt = clean(opt) - if !has(wantedOpts, opt) && !has(pOpts, opt) { - t.Errorf("unexpected mount option %q, expected %q", opt, opts) - } - delete(wantedOpts, opt) - } - } - for opt := range wantedOpts { - t.Errorf("missing mount option %q, found %q", opt, mi.Opts) - } - - // Validate Optional - if mi.Optional != "" { - for _, field := range strings.Split(mi.Optional, ",") { - field = clean(field) - if !has(wantedOptional, field) && !has(pOptional, field) { - t.Errorf("unexpected optional field %q, expected %q", field, optional) - } - delete(wantedOptional, field) - } - } - for field := range wantedOptional { - t.Errorf("missing optional field %q, found %q", field, mi.Optional) - } - - // Validate VFS if set - if vfs != "" { - if mi.VfsOpts != "" { - for _, opt := range strings.Split(mi.VfsOpts, ",") { - opt = clean(opt) - if !has(wantedVFS, opt) && opt != "seclabel" { // can be added by selinux - t.Errorf("unexpected vfs option %q, expected %q", opt, vfs) - } - delete(wantedVFS, opt) - } - } - for opt := range wantedVFS { - t.Errorf("missing vfs option %q, found %q", opt, mi.VfsOpts) - } - } - - return - } - - t.Errorf("failed to find mount %q", mnt) -} - -// clean strips off any value param after the colon -func clean(v string) string { - return strings.SplitN(v, ":", 2)[0] -} - -// has returns true if key is a member of m -func has(m map[string]struct{}, key string) bool { - _, ok := m[key] - return ok -} diff --git a/pkg/mount/mounter_unsupported.go b/pkg/mount/mounter_unsupported.go deleted file mode 100644 index 1428dffa52962..0000000000000 --- a/pkg/mount/mounter_unsupported.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build !linux,!freebsd freebsd,!cgo - -package mount // import "github.com/docker/docker/pkg/mount" - -func mount(device, target, mType string, flag uintptr, data string) error { - panic("Not implemented") -} - -func unmount(target string, flag int) error { - panic("Not implemented") -} diff --git a/pkg/mount/mountinfo.go b/pkg/mount/mountinfo.go deleted file mode 100644 index ecd03fc022149..0000000000000 --- a/pkg/mount/mountinfo.go +++ /dev/null @@ -1,40 +0,0 @@ -package mount // import "github.com/docker/docker/pkg/mount" - -// Info reveals information about a particular mounted filesystem. This -// struct is populated from the content in the /proc//mountinfo file. -type Info struct { - // ID is a unique identifier of the mount (may be reused after umount). - ID int - - // Parent indicates the ID of the mount parent (or of self for the top of the - // mount tree). - Parent int - - // Major indicates one half of the device ID which identifies the device class. - Major int - - // Minor indicates one half of the device ID which identifies a specific - // instance of device. - Minor int - - // Root of the mount within the filesystem. - Root string - - // Mountpoint indicates the mount point relative to the process's root. - Mountpoint string - - // Opts represents mount-specific options. - Opts string - - // Optional represents optional fields. - Optional string - - // Fstype indicates the type of filesystem, such as EXT3. - Fstype string - - // Source indicates filesystem specific information or "none". - Source string - - // VfsOpts represents per super block options. - VfsOpts string -} diff --git a/pkg/mount/mountinfo_freebsd.go b/pkg/mount/mountinfo_freebsd.go deleted file mode 100644 index 36c89dc1a2408..0000000000000 --- a/pkg/mount/mountinfo_freebsd.go +++ /dev/null @@ -1,55 +0,0 @@ -package mount // import "github.com/docker/docker/pkg/mount" - -/* -#include -#include -#include -*/ -import "C" - -import ( - "fmt" - "reflect" - "unsafe" -) - -// Parse /proc/self/mountinfo because comparing Dev and ino does not work from -// bind mounts. -func parseMountTable(filter FilterFunc) ([]*Info, error) { - var rawEntries *C.struct_statfs - - count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT)) - if count == 0 { - return nil, fmt.Errorf("Failed to call getmntinfo") - } - - var entries []C.struct_statfs - header := (*reflect.SliceHeader)(unsafe.Pointer(&entries)) - header.Cap = count - header.Len = count - header.Data = uintptr(unsafe.Pointer(rawEntries)) - - var out []*Info - for _, entry := range entries { - var mountinfo Info - var skip, stop bool - mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0]) - - if filter != nil { - // filter out entries we're not interested in - skip, stop = filter(p) - if skip { - continue - } - } - - mountinfo.Source = C.GoString(&entry.f_mntfromname[0]) - mountinfo.Fstype = C.GoString(&entry.f_fstypename[0]) - - out = append(out, &mountinfo) - if stop { - break - } - } - return out, nil -} diff --git a/pkg/mount/mountinfo_linux.go b/pkg/mount/mountinfo_linux.go deleted file mode 100644 index c1dba01fc31c8..0000000000000 --- a/pkg/mount/mountinfo_linux.go +++ /dev/null @@ -1,132 +0,0 @@ -package mount // import "github.com/docker/docker/pkg/mount" - -import ( - "bufio" - "fmt" - "io" - "os" - "strconv" - "strings" -) - -func parseInfoFile(r io.Reader, filter FilterFunc) ([]*Info, error) { - s := bufio.NewScanner(r) - out := []*Info{} - for s.Scan() { - if err := s.Err(); err != nil { - return nil, err - } - /* - 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue - (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) - - (1) mount ID: unique identifier of the mount (may be reused after umount) - (2) parent ID: ID of parent (or of self for the top of the mount tree) - (3) major:minor: value of st_dev for files on filesystem - (4) root: root of the mount within the filesystem - (5) mount point: mount point relative to the process's root - (6) mount options: per mount options - (7) optional fields: zero or more fields of the form "tag[:value]" - (8) separator: marks the end of the optional fields - (9) filesystem type: name of filesystem of the form "type[.subtype]" - (10) mount source: filesystem specific information or "none" - (11) super options: per super block options - */ - - text := s.Text() - fields := strings.Split(text, " ") - numFields := len(fields) - if numFields < 10 { - // should be at least 10 fields - return nil, fmt.Errorf("Parsing '%s' failed: not enough fields (%d)", text, numFields) - } - - p := &Info{} - // ignore any numbers parsing errors, as there should not be any - p.ID, _ = strconv.Atoi(fields[0]) - p.Parent, _ = strconv.Atoi(fields[1]) - mm := strings.Split(fields[2], ":") - if len(mm) != 2 { - return nil, fmt.Errorf("Parsing '%s' failed: unexpected minor:major pair %s", text, mm) - } - p.Major, _ = strconv.Atoi(mm[0]) - p.Minor, _ = strconv.Atoi(mm[1]) - - p.Root = fields[3] - p.Mountpoint = fields[4] - p.Opts = fields[5] - - var skip, stop bool - if filter != nil { - // filter out entries we're not interested in - skip, stop = filter(p) - if skip { - continue - } - } - - // one or more optional fields, when a separator (-) - i := 6 - for ; i < numFields && fields[i] != "-"; i++ { - switch i { - case 6: - p.Optional = fields[6] - default: - /* NOTE there might be more optional fields before the such as - fields[7]...fields[N] (where N < sepIndex), although - as of Linux kernel 4.15 the only known ones are - mount propagation flags in fields[6]. The correct - behavior is to ignore any unknown optional fields. - */ - break - } - } - if i == numFields { - return nil, fmt.Errorf("Parsing '%s' failed: missing separator ('-')", text) - } - - // There should be 3 fields after the separator... - if i+4 > numFields { - return nil, fmt.Errorf("Parsing '%s' failed: not enough fields after a separator", text) - } - // ... but in Linux <= 3.9 mounting a cifs with spaces in a share name - // (like "//serv/My Documents") _may_ end up having a space in the last field - // of mountinfo (like "unc=//serv/My Documents"). Since kernel 3.10-rc1, cifs - // option unc= is ignored, so a space should not appear. In here we ignore - // those "extra" fields caused by extra spaces. - p.Fstype = fields[i+1] - p.Source = fields[i+2] - p.VfsOpts = fields[i+3] - - out = append(out, p) - if stop { - break - } - } - return out, nil -} - -// Parse /proc/self/mountinfo because comparing Dev and ino does not work from -// bind mounts -func parseMountTable(filter FilterFunc) ([]*Info, error) { - f, err := os.Open("/proc/self/mountinfo") - if err != nil { - return nil, err - } - defer f.Close() - - return parseInfoFile(f, filter) -} - -// PidMountInfo collects the mounts for a specific process ID. If the process -// ID is unknown, it is better to use `GetMounts` which will inspect -// "/proc/self/mountinfo" instead. -func PidMountInfo(pid int) ([]*Info, error) { - f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) - if err != nil { - return nil, err - } - defer f.Close() - - return parseInfoFile(f, nil) -} diff --git a/pkg/mount/mountinfo_linux_test.go b/pkg/mount/mountinfo_linux_test.go deleted file mode 100644 index 64411ccaef1df..0000000000000 --- a/pkg/mount/mountinfo_linux_test.go +++ /dev/null @@ -1,508 +0,0 @@ -// +build linux - -package mount // import "github.com/docker/docker/pkg/mount" - -import ( - "bytes" - "testing" - - "gotest.tools/assert" -) - -const ( - fedoraMountinfo = `15 35 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw -16 35 0:14 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel -17 35 0:5 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=8056484k,nr_inodes=2014121,mode=755 -18 16 0:15 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:7 - securityfs securityfs rw -19 16 0:13 / /sys/fs/selinux rw,relatime shared:8 - selinuxfs selinuxfs rw -20 17 0:16 / /dev/shm rw,nosuid,nodev shared:3 - tmpfs tmpfs rw,seclabel -21 17 0:10 / /dev/pts rw,nosuid,noexec,relatime shared:4 - devpts devpts rw,seclabel,gid=5,mode=620,ptmxmode=000 -22 35 0:17 / /run rw,nosuid,nodev shared:21 - tmpfs tmpfs rw,seclabel,mode=755 -23 16 0:18 / /sys/fs/cgroup rw,nosuid,nodev,noexec shared:9 - tmpfs tmpfs rw,seclabel,mode=755 -24 23 0:19 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd -25 16 0:20 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:20 - pstore pstore rw -26 23 0:21 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,cpuset,clone_children -27 23 0:22 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:12 - cgroup cgroup rw,cpuacct,cpu,clone_children -28 23 0:23 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,memory,clone_children -29 23 0:24 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,devices,clone_children -30 23 0:25 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,freezer,clone_children -31 23 0:26 / /sys/fs/cgroup/net_cls rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,net_cls,clone_children -32 23 0:27 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,blkio,clone_children -33 23 0:28 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,perf_event,clone_children -34 23 0:29 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,hugetlb,clone_children -35 1 253:2 / / rw,relatime shared:1 - ext4 /dev/mapper/ssd-root--f20 rw,seclabel,data=ordered -36 15 0:30 / /proc/sys/fs/binfmt_misc rw,relatime shared:22 - autofs systemd-1 rw,fd=38,pgrp=1,timeout=300,minproto=5,maxproto=5,direct -37 17 0:12 / /dev/mqueue rw,relatime shared:23 - mqueue mqueue rw,seclabel -38 35 0:31 / /tmp rw shared:24 - tmpfs tmpfs rw,seclabel -39 17 0:32 / /dev/hugepages rw,relatime shared:25 - hugetlbfs hugetlbfs rw,seclabel -40 16 0:7 / /sys/kernel/debug rw,relatime shared:26 - debugfs debugfs rw -41 16 0:33 / /sys/kernel/config rw,relatime shared:27 - configfs configfs rw -42 35 0:34 / /var/lib/nfs/rpc_pipefs rw,relatime shared:28 - rpc_pipefs sunrpc rw -43 15 0:35 / /proc/fs/nfsd rw,relatime shared:29 - nfsd sunrpc rw -45 35 8:17 / /boot rw,relatime shared:30 - ext4 /dev/sdb1 rw,seclabel,data=ordered -46 35 253:4 / /home rw,relatime shared:31 - ext4 /dev/mapper/ssd-home rw,seclabel,data=ordered -47 35 253:5 / /var/lib/libvirt/images rw,noatime,nodiratime shared:32 - ext4 /dev/mapper/ssd-virt rw,seclabel,discard,data=ordered -48 35 253:12 / /mnt/old rw,relatime shared:33 - ext4 /dev/mapper/HelpDeskRHEL6-FedoraRoot rw,seclabel,data=ordered -121 22 0:36 / /run/user/1000/gvfs rw,nosuid,nodev,relatime shared:104 - fuse.gvfsd-fuse gvfsd-fuse rw,user_id=1000,group_id=1000 -124 16 0:37 / /sys/fs/fuse/connections rw,relatime shared:107 - fusectl fusectl rw -165 38 253:3 / /tmp/mnt rw,relatime shared:147 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered -167 35 253:15 / /var/lib/docker/devicemapper/mnt/aae4076022f0e2b80a2afbf8fc6df450c52080191fcef7fb679a73e6f073e5c2 rw,relatime shared:149 - ext4 /dev/mapper/docker-253:2-425882-aae4076022f0e2b80a2afbf8fc6df450c52080191fcef7fb679a73e6f073e5c2 rw,seclabel,discard,stripe=16,data=ordered -171 35 253:16 / /var/lib/docker/devicemapper/mnt/c71be651f114db95180e472f7871b74fa597ee70a58ccc35cb87139ddea15373 rw,relatime shared:153 - ext4 /dev/mapper/docker-253:2-425882-c71be651f114db95180e472f7871b74fa597ee70a58ccc35cb87139ddea15373 rw,seclabel,discard,stripe=16,data=ordered -175 35 253:17 / /var/lib/docker/devicemapper/mnt/1bac6ab72862d2d5626560df6197cf12036b82e258c53d981fa29adce6f06c3c rw,relatime shared:157 - ext4 /dev/mapper/docker-253:2-425882-1bac6ab72862d2d5626560df6197cf12036b82e258c53d981fa29adce6f06c3c rw,seclabel,discard,stripe=16,data=ordered -179 35 253:18 / /var/lib/docker/devicemapper/mnt/d710a357d77158e80d5b2c55710ae07c94e76d34d21ee7bae65ce5418f739b09 rw,relatime shared:161 - ext4 /dev/mapper/docker-253:2-425882-d710a357d77158e80d5b2c55710ae07c94e76d34d21ee7bae65ce5418f739b09 rw,seclabel,discard,stripe=16,data=ordered -183 35 253:19 / /var/lib/docker/devicemapper/mnt/6479f52366114d5f518db6837254baab48fab39f2ac38d5099250e9a6ceae6c7 rw,relatime shared:165 - ext4 /dev/mapper/docker-253:2-425882-6479f52366114d5f518db6837254baab48fab39f2ac38d5099250e9a6ceae6c7 rw,seclabel,discard,stripe=16,data=ordered -187 35 253:20 / /var/lib/docker/devicemapper/mnt/8d9df91c4cca5aef49eeb2725292aab324646f723a7feab56be34c2ad08268e1 rw,relatime shared:169 - ext4 /dev/mapper/docker-253:2-425882-8d9df91c4cca5aef49eeb2725292aab324646f723a7feab56be34c2ad08268e1 rw,seclabel,discard,stripe=16,data=ordered -191 35 253:21 / /var/lib/docker/devicemapper/mnt/c8240b768603d32e920d365dc9d1dc2a6af46cd23e7ae819947f969e1b4ec661 rw,relatime shared:173 - ext4 /dev/mapper/docker-253:2-425882-c8240b768603d32e920d365dc9d1dc2a6af46cd23e7ae819947f969e1b4ec661 rw,seclabel,discard,stripe=16,data=ordered -195 35 253:22 / /var/lib/docker/devicemapper/mnt/2eb3a01278380bbf3ed12d86ac629eaa70a4351301ee307a5cabe7b5f3b1615f rw,relatime shared:177 - ext4 /dev/mapper/docker-253:2-425882-2eb3a01278380bbf3ed12d86ac629eaa70a4351301ee307a5cabe7b5f3b1615f rw,seclabel,discard,stripe=16,data=ordered -199 35 253:23 / /var/lib/docker/devicemapper/mnt/37a17fb7c9d9b80821235d5f2662879bd3483915f245f9b49cdaa0e38779b70b rw,relatime shared:181 - ext4 /dev/mapper/docker-253:2-425882-37a17fb7c9d9b80821235d5f2662879bd3483915f245f9b49cdaa0e38779b70b rw,seclabel,discard,stripe=16,data=ordered -203 35 253:24 / /var/lib/docker/devicemapper/mnt/aea459ae930bf1de913e2f29428fd80ee678a1e962d4080019d9f9774331ee2b rw,relatime shared:185 - ext4 /dev/mapper/docker-253:2-425882-aea459ae930bf1de913e2f29428fd80ee678a1e962d4080019d9f9774331ee2b rw,seclabel,discard,stripe=16,data=ordered -207 35 253:25 / /var/lib/docker/devicemapper/mnt/928ead0bc06c454bd9f269e8585aeae0a6bd697f46dc8754c2a91309bc810882 rw,relatime shared:189 - ext4 /dev/mapper/docker-253:2-425882-928ead0bc06c454bd9f269e8585aeae0a6bd697f46dc8754c2a91309bc810882 rw,seclabel,discard,stripe=16,data=ordered -211 35 253:26 / /var/lib/docker/devicemapper/mnt/0f284d18481d671644706e7a7244cbcf63d590d634cc882cb8721821929d0420 rw,relatime shared:193 - ext4 /dev/mapper/docker-253:2-425882-0f284d18481d671644706e7a7244cbcf63d590d634cc882cb8721821929d0420 rw,seclabel,discard,stripe=16,data=ordered -215 35 253:27 / /var/lib/docker/devicemapper/mnt/d9dd16722ab34c38db2733e23f69e8f4803ce59658250dd63e98adff95d04919 rw,relatime shared:197 - ext4 /dev/mapper/docker-253:2-425882-d9dd16722ab34c38db2733e23f69e8f4803ce59658250dd63e98adff95d04919 rw,seclabel,discard,stripe=16,data=ordered -219 35 253:28 / /var/lib/docker/devicemapper/mnt/bc4500479f18c2c08c21ad5282e5f826a016a386177d9874c2764751c031d634 rw,relatime shared:201 - ext4 /dev/mapper/docker-253:2-425882-bc4500479f18c2c08c21ad5282e5f826a016a386177d9874c2764751c031d634 rw,seclabel,discard,stripe=16,data=ordered -223 35 253:29 / /var/lib/docker/devicemapper/mnt/7770c8b24eb3d5cc159a065910076938910d307ab2f5d94e1dc3b24c06ee2c8a rw,relatime shared:205 - ext4 /dev/mapper/docker-253:2-425882-7770c8b24eb3d5cc159a065910076938910d307ab2f5d94e1dc3b24c06ee2c8a rw,seclabel,discard,stripe=16,data=ordered -227 35 253:30 / /var/lib/docker/devicemapper/mnt/c280cd3d0bf0aa36b478b292279671624cceafc1a67eaa920fa1082601297adf rw,relatime shared:209 - ext4 /dev/mapper/docker-253:2-425882-c280cd3d0bf0aa36b478b292279671624cceafc1a67eaa920fa1082601297adf rw,seclabel,discard,stripe=16,data=ordered -231 35 253:31 / /var/lib/docker/devicemapper/mnt/8b59a7d9340279f09fea67fd6ad89ddef711e9e7050eb647984f8b5ef006335f rw,relatime shared:213 - ext4 /dev/mapper/docker-253:2-425882-8b59a7d9340279f09fea67fd6ad89ddef711e9e7050eb647984f8b5ef006335f rw,seclabel,discard,stripe=16,data=ordered -235 35 253:32 / /var/lib/docker/devicemapper/mnt/1a28059f29eda821578b1bb27a60cc71f76f846a551abefabce6efd0146dce9f rw,relatime shared:217 - ext4 /dev/mapper/docker-253:2-425882-1a28059f29eda821578b1bb27a60cc71f76f846a551abefabce6efd0146dce9f rw,seclabel,discard,stripe=16,data=ordered -239 35 253:33 / /var/lib/docker/devicemapper/mnt/e9aa60c60128cad1 rw,relatime shared:221 - ext4 /dev/mapper/docker-253:2-425882-e9aa60c60128cad1 rw,seclabel,discard,stripe=16,data=ordered -243 35 253:34 / /var/lib/docker/devicemapper/mnt/5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d-init rw,relatime shared:225 - ext4 /dev/mapper/docker-253:2-425882-5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d-init rw,seclabel,discard,stripe=16,data=ordered -247 35 253:35 / /var/lib/docker/devicemapper/mnt/5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d rw,relatime shared:229 - ext4 /dev/mapper/docker-253:2-425882-5fec11304b6f4713fea7b6ccdcc1adc0a1966187f590fe25a8227428a8df275d rw,seclabel,discard,stripe=16,data=ordered -31 21 0:23 / /DATA/foo_bla_bla rw,relatime - cifs //foo/BLA\040BLA\040BLA/ rw,sec=ntlm,cache=loose,unc=\\foo\BLA BLA BLA,username=my_login,domain=mydomain.com,uid=12345678,forceuid,gid=12345678,forcegid,addr=10.1.30.10,file_mode=0755,dir_mode=0755,nounix,rsize=61440,wsize=65536,actimeo=1` - - ubuntuMountInfo = `15 20 0:14 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw -16 20 0:3 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw -17 20 0:5 / /dev rw,relatime - devtmpfs udev rw,size=1015140k,nr_inodes=253785,mode=755 -18 17 0:11 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000 -19 20 0:15 / /run rw,nosuid,noexec,relatime - tmpfs tmpfs rw,size=205044k,mode=755 -20 1 253:0 / / rw,relatime - ext4 /dev/disk/by-label/DOROOT rw,errors=remount-ro,data=ordered -21 15 0:16 / /sys/fs/cgroup rw,relatime - tmpfs none rw,size=4k,mode=755 -22 15 0:17 / /sys/fs/fuse/connections rw,relatime - fusectl none rw -23 15 0:6 / /sys/kernel/debug rw,relatime - debugfs none rw -24 15 0:10 / /sys/kernel/security rw,relatime - securityfs none rw -25 19 0:18 / /run/lock rw,nosuid,nodev,noexec,relatime - tmpfs none rw,size=5120k -26 21 0:19 / /sys/fs/cgroup/cpuset rw,relatime - cgroup cgroup rw,cpuset,clone_children -27 19 0:20 / /run/shm rw,nosuid,nodev,relatime - tmpfs none rw -28 21 0:21 / /sys/fs/cgroup/cpu rw,relatime - cgroup cgroup rw,cpu -29 19 0:22 / /run/user rw,nosuid,nodev,noexec,relatime - tmpfs none rw,size=102400k,mode=755 -30 15 0:23 / /sys/fs/pstore rw,relatime - pstore none rw -31 21 0:24 / /sys/fs/cgroup/cpuacct rw,relatime - cgroup cgroup rw,cpuacct -32 21 0:25 / /sys/fs/cgroup/memory rw,relatime - cgroup cgroup rw,memory -33 21 0:26 / /sys/fs/cgroup/devices rw,relatime - cgroup cgroup rw,devices -34 21 0:27 / /sys/fs/cgroup/freezer rw,relatime - cgroup cgroup rw,freezer -35 21 0:28 / /sys/fs/cgroup/blkio rw,relatime - cgroup cgroup rw,blkio -36 21 0:29 / /sys/fs/cgroup/perf_event rw,relatime - cgroup cgroup rw,perf_event -37 21 0:30 / /sys/fs/cgroup/hugetlb rw,relatime - cgroup cgroup rw,hugetlb -38 21 0:31 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime - cgroup systemd rw,name=systemd -39 20 0:32 / /var/lib/docker/aufs/mnt/b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc rw,relatime - aufs none rw,si=caafa54fdc06525 -40 20 0:33 / /var/lib/docker/aufs/mnt/2eed44ac7ce7c75af04f088ed6cb4ce9d164801e91d78c6db65d7ef6d572bba8-init rw,relatime - aufs none rw,si=caafa54f882b525 -41 20 0:34 / /var/lib/docker/aufs/mnt/2eed44ac7ce7c75af04f088ed6cb4ce9d164801e91d78c6db65d7ef6d572bba8 rw,relatime - aufs none rw,si=caafa54f8829525 -42 20 0:35 / /var/lib/docker/aufs/mnt/16f4d7e96dd612903f425bfe856762f291ff2e36a8ecd55a2209b7d7cd81c30b rw,relatime - aufs none rw,si=caafa54f882d525 -43 20 0:36 / /var/lib/docker/aufs/mnt/63ca08b75d7438a9469a5954e003f48ffede73541f6286ce1cb4d7dd4811da7e-init rw,relatime - aufs none rw,si=caafa54f882f525 -44 20 0:37 / /var/lib/docker/aufs/mnt/63ca08b75d7438a9469a5954e003f48ffede73541f6286ce1cb4d7dd4811da7e rw,relatime - aufs none rw,si=caafa54f88ba525 -45 20 0:38 / /var/lib/docker/aufs/mnt/283f35a910233c756409313be71ecd8fcfef0df57108b8d740b61b3e88860452 rw,relatime - aufs none rw,si=caafa54f88b8525 -46 20 0:39 / /var/lib/docker/aufs/mnt/2c6c7253d4090faa3886871fb21bd660609daeb0206588c0602007f7d0f254b1-init rw,relatime - aufs none rw,si=caafa54f88be525 -47 20 0:40 / /var/lib/docker/aufs/mnt/2c6c7253d4090faa3886871fb21bd660609daeb0206588c0602007f7d0f254b1 rw,relatime - aufs none rw,si=caafa54f882c525 -48 20 0:41 / /var/lib/docker/aufs/mnt/de2b538c97d6366cc80e8658547c923ea1d042f85580df379846f36a4df7049d rw,relatime - aufs none rw,si=caafa54f85bb525 -49 20 0:42 / /var/lib/docker/aufs/mnt/94a3d8ed7c27e5b0aa71eba46c736bfb2742afda038e74f2dd6035fb28415b49-init rw,relatime - aufs none rw,si=caafa54fdc00525 -50 20 0:43 / /var/lib/docker/aufs/mnt/94a3d8ed7c27e5b0aa71eba46c736bfb2742afda038e74f2dd6035fb28415b49 rw,relatime - aufs none rw,si=caafa54fbaec525 -51 20 0:44 / /var/lib/docker/aufs/mnt/6ac1cace985c9fc9bea32234de8b36dba49bdd5e29a2972b327ff939d78a6274 rw,relatime - aufs none rw,si=caafa54f8e1a525 -52 20 0:45 / /var/lib/docker/aufs/mnt/dff147033e3a0ef061e1de1ad34256b523d4a8c1fa6bba71a0ab538e8628ff0b-init rw,relatime - aufs none rw,si=caafa54f8e1d525 -53 20 0:46 / /var/lib/docker/aufs/mnt/dff147033e3a0ef061e1de1ad34256b523d4a8c1fa6bba71a0ab538e8628ff0b rw,relatime - aufs none rw,si=caafa54f8e1b525 -54 20 0:47 / /var/lib/docker/aufs/mnt/cabb117d997f0f93519185aea58389a9762770b7496ed0b74a3e4a083fa45902 rw,relatime - aufs none rw,si=caafa54f810a525 -55 20 0:48 / /var/lib/docker/aufs/mnt/e1c8a94ffaa9d532bbbdc6ef771ce8a6c2c06757806ecaf8b68e9108fec65f33-init rw,relatime - aufs none rw,si=caafa54f8529525 -56 20 0:49 / /var/lib/docker/aufs/mnt/e1c8a94ffaa9d532bbbdc6ef771ce8a6c2c06757806ecaf8b68e9108fec65f33 rw,relatime - aufs none rw,si=caafa54f852f525 -57 20 0:50 / /var/lib/docker/aufs/mnt/16a1526fa445b84ce84f89506d219e87fa488a814063baf045d88b02f21166b3 rw,relatime - aufs none rw,si=caafa54f9e1d525 -58 20 0:51 / /var/lib/docker/aufs/mnt/57b9c92e1e368fa7dbe5079f7462e917777829caae732828b003c355fe49da9f-init rw,relatime - aufs none rw,si=caafa54f854d525 -59 20 0:52 / /var/lib/docker/aufs/mnt/57b9c92e1e368fa7dbe5079f7462e917777829caae732828b003c355fe49da9f rw,relatime - aufs none rw,si=caafa54f854e525 -60 20 0:53 / /var/lib/docker/aufs/mnt/e370c3e286bea027917baa0e4d251262681a472a87056e880dfd0513516dffd9 rw,relatime - aufs none rw,si=caafa54f840a525 -61 20 0:54 / /var/lib/docker/aufs/mnt/6b00d3b4f32b41997ec07412b5e18204f82fbe643e7122251cdeb3582abd424e-init rw,relatime - aufs none rw,si=caafa54f8408525 -62 20 0:55 / /var/lib/docker/aufs/mnt/6b00d3b4f32b41997ec07412b5e18204f82fbe643e7122251cdeb3582abd424e rw,relatime - aufs none rw,si=caafa54f8409525 -63 20 0:56 / /var/lib/docker/aufs/mnt/abd0b5ea5d355a67f911475e271924a5388ee60c27185fcd60d095afc4a09dc7 rw,relatime - aufs none rw,si=caafa54f9eb1525 -64 20 0:57 / /var/lib/docker/aufs/mnt/336222effc3f7b89867bb39ff7792ae5412c35c749f127c29159d046b6feedd2-init rw,relatime - aufs none rw,si=caafa54f85bf525 -65 20 0:58 / /var/lib/docker/aufs/mnt/336222effc3f7b89867bb39ff7792ae5412c35c749f127c29159d046b6feedd2 rw,relatime - aufs none rw,si=caafa54f85b8525 -66 20 0:59 / /var/lib/docker/aufs/mnt/912e1bf28b80a09644503924a8a1a4fb8ed10b808ca847bda27a369919aa52fa rw,relatime - aufs none rw,si=caafa54fbaea525 -67 20 0:60 / /var/lib/docker/aufs/mnt/386f722875013b4a875118367abc783fc6617a3cb7cf08b2b4dcf550b4b9c576-init rw,relatime - aufs none rw,si=caafa54f8472525 -68 20 0:61 / /var/lib/docker/aufs/mnt/386f722875013b4a875118367abc783fc6617a3cb7cf08b2b4dcf550b4b9c576 rw,relatime - aufs none rw,si=caafa54f8474525 -69 20 0:62 / /var/lib/docker/aufs/mnt/5aaebb79ef3097dfca377889aeb61a0c9d5e3795117d2b08d0751473c671dfb2 rw,relatime - aufs none rw,si=caafa54f8c5e525 -70 20 0:63 / /var/lib/docker/aufs/mnt/5ba3e493279d01277d583600b81c7c079e691b73c3a2bdea8e4b12a35a418be2-init rw,relatime - aufs none rw,si=caafa54f8c3b525 -71 20 0:64 / /var/lib/docker/aufs/mnt/5ba3e493279d01277d583600b81c7c079e691b73c3a2bdea8e4b12a35a418be2 rw,relatime - aufs none rw,si=caafa54f8c3d525 -72 20 0:65 / /var/lib/docker/aufs/mnt/2777f0763da4de93f8bebbe1595cc77f739806a158657b033eca06f827b6028a rw,relatime - aufs none rw,si=caafa54f8c3e525 -73 20 0:66 / /var/lib/docker/aufs/mnt/5d7445562acf73c6f0ae34c3dd0921d7457de1ba92a587d9e06a44fa209eeb3e-init rw,relatime - aufs none rw,si=caafa54f8c39525 -74 20 0:67 / /var/lib/docker/aufs/mnt/5d7445562acf73c6f0ae34c3dd0921d7457de1ba92a587d9e06a44fa209eeb3e rw,relatime - aufs none rw,si=caafa54f854f525 -75 20 0:68 / /var/lib/docker/aufs/mnt/06400b526ec18b66639c96efc41a84f4ae0b117cb28dafd56be420651b4084a0 rw,relatime - aufs none rw,si=caafa54f840b525 -76 20 0:69 / /var/lib/docker/aufs/mnt/e051d45ec42d8e3e1cc57bb39871a40de486dc123522e9c067fbf2ca6a357785-init rw,relatime - aufs none rw,si=caafa54fdddf525 -77 20 0:70 / /var/lib/docker/aufs/mnt/e051d45ec42d8e3e1cc57bb39871a40de486dc123522e9c067fbf2ca6a357785 rw,relatime - aufs none rw,si=caafa54f854b525 -78 20 0:71 / /var/lib/docker/aufs/mnt/1ff414fa93fd61ec81b0ab7b365a841ff6545accae03cceac702833aaeaf718f rw,relatime - aufs none rw,si=caafa54f8d85525 -79 20 0:72 / /var/lib/docker/aufs/mnt/c661b2f871dd5360e46a2aebf8f970f6d39a2ff64e06979aa0361227c88128b8-init rw,relatime - aufs none rw,si=caafa54f8da3525 -80 20 0:73 / /var/lib/docker/aufs/mnt/c661b2f871dd5360e46a2aebf8f970f6d39a2ff64e06979aa0361227c88128b8 rw,relatime - aufs none rw,si=caafa54f8da2525 -81 20 0:74 / /var/lib/docker/aufs/mnt/b68b1d4fe4d30016c552398e78b379a39f651661d8e1fa5f2460c24a5e723420 rw,relatime - aufs none rw,si=caafa54f8d81525 -82 20 0:75 / /var/lib/docker/aufs/mnt/c5c5979c936cd0153a4c626fa9d69ce4fce7d924cc74fa68b025d2f585031739-init rw,relatime - aufs none rw,si=caafa54f8da1525 -83 20 0:76 / /var/lib/docker/aufs/mnt/c5c5979c936cd0153a4c626fa9d69ce4fce7d924cc74fa68b025d2f585031739 rw,relatime - aufs none rw,si=caafa54f8da0525 -84 20 0:77 / /var/lib/docker/aufs/mnt/53e10b0329afc0e0d3322d31efaed4064139dc7027fe6ae445cffd7104bcc94f rw,relatime - aufs none rw,si=caafa54f8c35525 -85 20 0:78 / /var/lib/docker/aufs/mnt/3bfafd09ff2603e2165efacc2215c1f51afabba6c42d04a68cc2df0e8cc31494-init rw,relatime - aufs none rw,si=caafa54f8db8525 -86 20 0:79 / /var/lib/docker/aufs/mnt/3bfafd09ff2603e2165efacc2215c1f51afabba6c42d04a68cc2df0e8cc31494 rw,relatime - aufs none rw,si=caafa54f8dba525 -87 20 0:80 / /var/lib/docker/aufs/mnt/90fdd2c03eeaf65311f88f4200e18aef6d2772482712d9aea01cd793c64781b5 rw,relatime - aufs none rw,si=caafa54f8315525 -88 20 0:81 / /var/lib/docker/aufs/mnt/7bdf2591c06c154ceb23f5e74b1d03b18fbf6fe96e35fbf539b82d446922442f-init rw,relatime - aufs none rw,si=caafa54f8fc6525 -89 20 0:82 / /var/lib/docker/aufs/mnt/7bdf2591c06c154ceb23f5e74b1d03b18fbf6fe96e35fbf539b82d446922442f rw,relatime - aufs none rw,si=caafa54f8468525 -90 20 0:83 / /var/lib/docker/aufs/mnt/8cf9a993f50f3305abad3da268c0fc44ff78a1e7bba595ef9de963497496c3f9 rw,relatime - aufs none rw,si=caafa54f8c59525 -91 20 0:84 / /var/lib/docker/aufs/mnt/ecc896fd74b21840a8d35e8316b92a08b1b9c83d722a12acff847e9f0ff17173-init rw,relatime - aufs none rw,si=caafa54f846a525 -92 20 0:85 / /var/lib/docker/aufs/mnt/ecc896fd74b21840a8d35e8316b92a08b1b9c83d722a12acff847e9f0ff17173 rw,relatime - aufs none rw,si=caafa54f846b525 -93 20 0:86 / /var/lib/docker/aufs/mnt/d8c8288ec920439a48b5796bab5883ee47a019240da65e8d8f33400c31bac5df rw,relatime - aufs none rw,si=caafa54f8dbf525 -94 20 0:87 / /var/lib/docker/aufs/mnt/ecba66710bcd03199b9398e46c005cd6b68d0266ec81dc8b722a29cc417997c6-init rw,relatime - aufs none rw,si=caafa54f810f525 -95 20 0:88 / /var/lib/docker/aufs/mnt/ecba66710bcd03199b9398e46c005cd6b68d0266ec81dc8b722a29cc417997c6 rw,relatime - aufs none rw,si=caafa54fbae9525 -96 20 0:89 / /var/lib/docker/aufs/mnt/befc1c67600df449dddbe796c0d06da7caff1d2bbff64cde1f0ba82d224996b5 rw,relatime - aufs none rw,si=caafa54f8dab525 -97 20 0:90 / /var/lib/docker/aufs/mnt/c9f470e73d2742629cdc4084a1b2c1a8302914f2aa0d0ec4542371df9a050562-init rw,relatime - aufs none rw,si=caafa54fdc02525 -98 20 0:91 / /var/lib/docker/aufs/mnt/c9f470e73d2742629cdc4084a1b2c1a8302914f2aa0d0ec4542371df9a050562 rw,relatime - aufs none rw,si=caafa54f9eb0525 -99 20 0:92 / /var/lib/docker/aufs/mnt/2a31f10029f04ff9d4381167a9b739609853d7220d55a56cb654779a700ee246 rw,relatime - aufs none rw,si=caafa54f8c37525 -100 20 0:93 / /var/lib/docker/aufs/mnt/8c4261b8e3e4b21ebba60389bd64b6261217e7e6b9fd09e201d5a7f6760f6927-init rw,relatime - aufs none rw,si=caafa54fd173525 -101 20 0:94 / /var/lib/docker/aufs/mnt/8c4261b8e3e4b21ebba60389bd64b6261217e7e6b9fd09e201d5a7f6760f6927 rw,relatime - aufs none rw,si=caafa54f8108525 -102 20 0:95 / /var/lib/docker/aufs/mnt/eaa0f57403a3dc685268f91df3fbcd7a8423cee50e1a9ee5c3e1688d9d676bb4 rw,relatime - aufs none rw,si=caafa54f852d525 -103 20 0:96 / /var/lib/docker/aufs/mnt/9cfe69a2cbffd9bfc7f396d4754f6fe5cc457ef417b277797be3762dfe955a6b-init rw,relatime - aufs none rw,si=caafa54f8d80525 -104 20 0:97 / /var/lib/docker/aufs/mnt/9cfe69a2cbffd9bfc7f396d4754f6fe5cc457ef417b277797be3762dfe955a6b rw,relatime - aufs none rw,si=caafa54f8fc3525 -105 20 0:98 / /var/lib/docker/aufs/mnt/d1b322ae17613c6adee84e709641a9244ac56675244a89a64dc0075075fcbb83 rw,relatime - aufs none rw,si=caafa54f8c58525 -106 20 0:99 / /var/lib/docker/aufs/mnt/d46c2a8e9da7e91ab34fd9c192851c246a4e770a46720bda09e55c7554b9dbbd-init rw,relatime - aufs none rw,si=caafa54f8c63525 -107 20 0:100 / /var/lib/docker/aufs/mnt/d46c2a8e9da7e91ab34fd9c192851c246a4e770a46720bda09e55c7554b9dbbd rw,relatime - aufs none rw,si=caafa54f8c67525 -108 20 0:101 / /var/lib/docker/aufs/mnt/bc9d2a264158f83a617a069bf17cbbf2a2ba453db7d3951d9dc63cc1558b1c2b rw,relatime - aufs none rw,si=caafa54f8dbe525 -109 20 0:102 / /var/lib/docker/aufs/mnt/9e6abb8d72bbeb4d5cf24b96018528015ba830ce42b4859965bd482cbd034e99-init rw,relatime - aufs none rw,si=caafa54f9e0d525 -110 20 0:103 / /var/lib/docker/aufs/mnt/9e6abb8d72bbeb4d5cf24b96018528015ba830ce42b4859965bd482cbd034e99 rw,relatime - aufs none rw,si=caafa54f9e1b525 -111 20 0:104 / /var/lib/docker/aufs/mnt/d4dca7b02569c732e740071e1c654d4ad282de5c41edb619af1f0aafa618be26 rw,relatime - aufs none rw,si=caafa54f8dae525 -112 20 0:105 / /var/lib/docker/aufs/mnt/fea63da40fa1c5ffbad430dde0bc64a8fc2edab09a051fff55b673c40a08f6b7-init rw,relatime - aufs none rw,si=caafa54f8c5c525 -113 20 0:106 / /var/lib/docker/aufs/mnt/fea63da40fa1c5ffbad430dde0bc64a8fc2edab09a051fff55b673c40a08f6b7 rw,relatime - aufs none rw,si=caafa54fd172525 -114 20 0:107 / /var/lib/docker/aufs/mnt/e60c57499c0b198a6734f77f660cdbbd950a5b78aa23f470ca4f0cfcc376abef rw,relatime - aufs none rw,si=caafa54909c4525 -115 20 0:108 / /var/lib/docker/aufs/mnt/099c78e7ccd9c8717471bb1bbfff838c0a9913321ba2f214fbeaf92c678e5b35-init rw,relatime - aufs none rw,si=caafa54909c3525 -116 20 0:109 / /var/lib/docker/aufs/mnt/099c78e7ccd9c8717471bb1bbfff838c0a9913321ba2f214fbeaf92c678e5b35 rw,relatime - aufs none rw,si=caafa54909c7525 -117 20 0:110 / /var/lib/docker/aufs/mnt/2997be666d58b9e71469759bcb8bd9608dad0e533a1a7570a896919ba3388825 rw,relatime - aufs none rw,si=caafa54f8557525 -118 20 0:111 / /var/lib/docker/aufs/mnt/730694eff438ef20569df38dfb38a920969d7ff2170cc9aa7cb32a7ed8147a93-init rw,relatime - aufs none rw,si=caafa54c6e88525 -119 20 0:112 / /var/lib/docker/aufs/mnt/730694eff438ef20569df38dfb38a920969d7ff2170cc9aa7cb32a7ed8147a93 rw,relatime - aufs none rw,si=caafa54c6e8e525 -120 20 0:113 / /var/lib/docker/aufs/mnt/a672a1e2f2f051f6e19ed1dfbe80860a2d774174c49f7c476695f5dd1d5b2f67 rw,relatime - aufs none rw,si=caafa54c6e15525 -121 20 0:114 / /var/lib/docker/aufs/mnt/aba3570e17859f76cf29d282d0d150659c6bd80780fdc52a465ba05245c2a420-init rw,relatime - aufs none rw,si=caafa54f8dad525 -122 20 0:115 / /var/lib/docker/aufs/mnt/aba3570e17859f76cf29d282d0d150659c6bd80780fdc52a465ba05245c2a420 rw,relatime - aufs none rw,si=caafa54f8d84525 -123 20 0:116 / /var/lib/docker/aufs/mnt/2abc86007aca46fb4a817a033e2a05ccacae40b78ea4b03f8ea616b9ada40e2e rw,relatime - aufs none rw,si=caafa54c6e8b525 -124 20 0:117 / /var/lib/docker/aufs/mnt/36352f27f7878e648367a135bd1ec3ed497adcb8ac13577ee892a0bd921d2374-init rw,relatime - aufs none rw,si=caafa54c6e8d525 -125 20 0:118 / /var/lib/docker/aufs/mnt/36352f27f7878e648367a135bd1ec3ed497adcb8ac13577ee892a0bd921d2374 rw,relatime - aufs none rw,si=caafa54f8c34525 -126 20 0:119 / /var/lib/docker/aufs/mnt/2f95ca1a629cea8363b829faa727dd52896d5561f2c96ddee4f697ea2fc872c2 rw,relatime - aufs none rw,si=caafa54c6e8a525 -127 20 0:120 / /var/lib/docker/aufs/mnt/f108c8291654f179ef143a3e07de2b5a34adbc0b28194a0ab17742b6db9a7fb2-init rw,relatime - aufs none rw,si=caafa54f8e19525 -128 20 0:121 / /var/lib/docker/aufs/mnt/f108c8291654f179ef143a3e07de2b5a34adbc0b28194a0ab17742b6db9a7fb2 rw,relatime - aufs none rw,si=caafa54fa8c6525 -129 20 0:122 / /var/lib/docker/aufs/mnt/c1d04dfdf8cccb3676d5a91e84e9b0781ce40623d127d038bcfbe4c761b27401 rw,relatime - aufs none rw,si=caafa54f8c30525 -130 20 0:123 / /var/lib/docker/aufs/mnt/3f4898ffd0e1239aeebf1d1412590cdb7254207fa3883663e2c40cf772e5f05a-init rw,relatime - aufs none rw,si=caafa54c6e1a525 -131 20 0:124 / /var/lib/docker/aufs/mnt/3f4898ffd0e1239aeebf1d1412590cdb7254207fa3883663e2c40cf772e5f05a rw,relatime - aufs none rw,si=caafa54c6e1c525 -132 20 0:125 / /var/lib/docker/aufs/mnt/5ae3b6fccb1539fc02d420e86f3e9637bef5b711fed2ca31a2f426c8f5deddbf rw,relatime - aufs none rw,si=caafa54c4fea525 -133 20 0:126 / /var/lib/docker/aufs/mnt/310bfaf80d57020f2e73b06aeffb0b9b0ca2f54895f88bf5e4d1529ccac58fe0-init rw,relatime - aufs none rw,si=caafa54c6e1e525 -134 20 0:127 / /var/lib/docker/aufs/mnt/310bfaf80d57020f2e73b06aeffb0b9b0ca2f54895f88bf5e4d1529ccac58fe0 rw,relatime - aufs none rw,si=caafa54fa8c0525 -135 20 0:128 / /var/lib/docker/aufs/mnt/f382bd5aaccaf2d04a59089ac7cb12ec87efd769fd0c14d623358fbfd2a3f896 rw,relatime - aufs none rw,si=caafa54c4fec525 -136 20 0:129 / /var/lib/docker/aufs/mnt/50d45e9bb2d779bc6362824085564c7578c231af5ae3b3da116acf7e17d00735-init rw,relatime - aufs none rw,si=caafa54c4fef525 -137 20 0:130 / /var/lib/docker/aufs/mnt/50d45e9bb2d779bc6362824085564c7578c231af5ae3b3da116acf7e17d00735 rw,relatime - aufs none rw,si=caafa54c4feb525 -138 20 0:131 / /var/lib/docker/aufs/mnt/a9c5ee0854dc083b6bf62b7eb1e5291aefbb10702289a446471ce73aba0d5d7d rw,relatime - aufs none rw,si=caafa54909c6525 -139 20 0:134 / /var/lib/docker/aufs/mnt/03a613e7bd5078819d1fd92df4e671c0127559a5e0b5a885cc8d5616875162f0-init rw,relatime - aufs none rw,si=caafa54804fe525 -140 20 0:135 / /var/lib/docker/aufs/mnt/03a613e7bd5078819d1fd92df4e671c0127559a5e0b5a885cc8d5616875162f0 rw,relatime - aufs none rw,si=caafa54804fa525 -141 20 0:136 / /var/lib/docker/aufs/mnt/7ec3277e5c04c907051caf9c9c35889f5fcd6463e5485971b25404566830bb70 rw,relatime - aufs none rw,si=caafa54804f9525 -142 20 0:139 / /var/lib/docker/aufs/mnt/26b5b5d71d79a5b2bfcf8bc4b2280ee829f261eb886745dd90997ed410f7e8b8-init rw,relatime - aufs none rw,si=caafa54c6ef6525 -143 20 0:140 / /var/lib/docker/aufs/mnt/26b5b5d71d79a5b2bfcf8bc4b2280ee829f261eb886745dd90997ed410f7e8b8 rw,relatime - aufs none rw,si=caafa54c6ef5525 -144 20 0:356 / /var/lib/docker/aufs/mnt/e6ecde9e2c18cd3c75f424c67b6d89685cfee0fc67abf2cb6bdc0867eb998026 rw,relatime - aufs none rw,si=caafa548068e525` - - gentooMountinfo = `15 1 8:6 / / rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -16 15 0:3 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw -17 15 0:14 / /run rw,nosuid,nodev,relatime - tmpfs tmpfs rw,size=3292172k,mode=755 -18 15 0:5 / /dev rw,nosuid,relatime - devtmpfs udev rw,size=10240k,nr_inodes=4106451,mode=755 -19 18 0:12 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw -20 18 0:10 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=000 -21 18 0:15 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw -22 15 0:16 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw -23 22 0:7 / /sys/kernel/debug rw,nosuid,nodev,noexec,relatime - debugfs debugfs rw -24 22 0:17 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs cgroup_root rw,size=10240k,mode=755 -25 24 0:18 / /sys/fs/cgroup/openrc rw,nosuid,nodev,noexec,relatime - cgroup openrc rw,release_agent=/lib64/rc/sh/cgroup-release-agent.sh,name=openrc -26 24 0:19 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime - cgroup cpuset rw,cpuset,clone_children -27 24 0:20 / /sys/fs/cgroup/cpu rw,nosuid,nodev,noexec,relatime - cgroup cpu rw,cpu,clone_children -28 24 0:21 / /sys/fs/cgroup/cpuacct rw,nosuid,nodev,noexec,relatime - cgroup cpuacct rw,cpuacct,clone_children -29 24 0:22 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup memory rw,memory,clone_children -30 24 0:23 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime - cgroup devices rw,devices,clone_children -31 24 0:24 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup freezer rw,freezer,clone_children -32 24 0:25 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime - cgroup blkio rw,blkio,clone_children -33 15 8:1 / /boot rw,noatime,nodiratime - vfat /dev/sda1 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro -34 15 8:18 / /mnt/xfs rw,noatime,nodiratime - xfs /dev/sdb2 rw,attr2,inode64,noquota -35 15 0:26 / /tmp rw,relatime - tmpfs tmpfs rw -36 16 0:27 / /proc/sys/fs/binfmt_misc rw,nosuid,nodev,noexec,relatime - binfmt_misc binfmt_misc rw -42 15 0:33 / /var/lib/nfs/rpc_pipefs rw,relatime - rpc_pipefs rpc_pipefs rw -43 16 0:34 / /proc/fs/nfsd rw,nosuid,nodev,noexec,relatime - nfsd nfsd rw -44 15 0:35 / /home/tianon/.gvfs rw,nosuid,nodev,relatime - fuse.gvfs-fuse-daemon gvfs-fuse-daemon rw,user_id=1000,group_id=1000 -68 15 0:3336 / /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd rw,relatime - aufs none rw,si=9b4a7640128db39c -86 68 8:6 /var/lib/docker/containers/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/config.env /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/.dockerenv rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -87 68 8:6 /etc/resolv.conf /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/etc/resolv.conf rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -88 68 8:6 /var/lib/docker/containers/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/hostname /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/etc/hostname rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -89 68 8:6 /var/lib/docker/containers/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/hosts /var/lib/docker/aufs/mnt/3597a1a6d6298c1decc339ebb90aad6f7d6ba2e15af3131b1f85e7ee4787a0cd/etc/hosts rw,noatime,nodiratime - ext4 /dev/sda6 rw,data=ordered -38 15 0:3384 / /var/lib/docker/aufs/mnt/0292005a9292401bb5197657f2b682d97d8edcb3b72b5e390d2a680139985b55 rw,relatime - aufs none rw,si=9b4a7642b584939c -39 15 0:3385 / /var/lib/docker/aufs/mnt/59db98c889de5f71b70cfb82c40cbe47b64332f0f56042a2987a9e5df6e5e3aa rw,relatime - aufs none rw,si=9b4a7642b584e39c -40 15 0:3386 / /var/lib/docker/aufs/mnt/0545f0f2b6548eb9601d08f35a08f5a0a385407d36027a28f58e06e9f61e0278 rw,relatime - aufs none rw,si=9b4a7642b584b39c -41 15 0:3387 / /var/lib/docker/aufs/mnt/d882cfa16d1aa8fe0331a36e79be3d80b151e49f24fc39a39c3fed1735d5feb5 rw,relatime - aufs none rw,si=9b4a76453040039c -45 15 0:3388 / /var/lib/docker/aufs/mnt/055ca3befcb1626e74f5344b3398724ff05c0de0e20021683d04305c9e70a3f6 rw,relatime - aufs none rw,si=9b4a76453040739c -46 15 0:3389 / /var/lib/docker/aufs/mnt/b899e4567a351745d4285e7f1c18fdece75d877deb3041981cd290be348b7aa6 rw,relatime - aufs none rw,si=9b4a7647def4039c -47 15 0:3390 / /var/lib/docker/aufs/mnt/067ca040292c58954c5129f953219accfae0d40faca26b4d05e76ca76a998f16 rw,relatime - aufs none rw,si=9b4a7647def4239c -48 15 0:3391 / /var/lib/docker/aufs/mnt/8c995e7cb6e5082742daeea720e340b021d288d25d92e0412c03d200df308a11 rw,relatime - aufs none rw,si=9b4a764479c1639c -49 15 0:3392 / /var/lib/docker/aufs/mnt/07cc54dfae5b45300efdacdd53cc72c01b9044956a86ce7bff42d087e426096d rw,relatime - aufs none rw,si=9b4a764479c1739c -50 15 0:3393 / /var/lib/docker/aufs/mnt/0a9c95cf4c589c05b06baa79150b0cc1d8e7102759fe3ce4afaabb8247ca4f85 rw,relatime - aufs none rw,si=9b4a7644059c839c -51 15 0:3394 / /var/lib/docker/aufs/mnt/468fa98cececcf4e226e8370f18f4f848d63faf287fb8321a07f73086441a3a0 rw,relatime - aufs none rw,si=9b4a7644059ca39c -52 15 0:3395 / /var/lib/docker/aufs/mnt/0b826192231c5ce066fffb5beff4397337b5fc19a377aa7c6282c7c0ce7f111f rw,relatime - aufs none rw,si=9b4a764479c1339c -53 15 0:3396 / /var/lib/docker/aufs/mnt/93b8ba1b772fbe79709b909c43ea4b2c30d712e53548f467db1ffdc7a384f196 rw,relatime - aufs none rw,si=9b4a7640798a739c -54 15 0:3397 / /var/lib/docker/aufs/mnt/0c0d0acfb506859b12ef18cdfef9ebed0b43a611482403564224bde9149d373c rw,relatime - aufs none rw,si=9b4a7640798a039c -55 15 0:3398 / /var/lib/docker/aufs/mnt/33648c39ab6c7c74af0243d6d6a81b052e9e25ad1e04b19892eb2dde013e358b rw,relatime - aufs none rw,si=9b4a7644b439b39c -56 15 0:3399 / /var/lib/docker/aufs/mnt/0c12bea97a1c958a3c739fb148536c1c89351d48e885ecda8f0499b5cc44407e rw,relatime - aufs none rw,si=9b4a7640798a239c -57 15 0:3400 / /var/lib/docker/aufs/mnt/ed443988ce125f172d7512e84a4de2627405990fd767a16adefa8ce700c19ce8 rw,relatime - aufs none rw,si=9b4a7644c8ed339c -59 15 0:3402 / /var/lib/docker/aufs/mnt/f61612c324ff3c924d3f7a82fb00a0f8d8f73c248c41897061949e9f5ab7e3b1 rw,relatime - aufs none rw,si=9b4a76442810c39c -60 15 0:3403 / /var/lib/docker/aufs/mnt/0f1ee55c6c4e25027b80de8e64b8b6fb542b3b41aa0caab9261da75752e22bfd rw,relatime - aufs none rw,si=9b4a76442810e39c -61 15 0:3404 / /var/lib/docker/aufs/mnt/956f6cc4af5785cb3ee6963dcbca668219437d9b28f513290b1453ac64a34f97 rw,relatime - aufs none rw,si=9b4a7644303ec39c -62 15 0:3405 / /var/lib/docker/aufs/mnt/1099769158c4b4773e2569e38024e8717e400f87a002c41d8cf47cb81b051ba6 rw,relatime - aufs none rw,si=9b4a7644303ee39c -63 15 0:3406 / /var/lib/docker/aufs/mnt/11890ceb98d4442595b676085cd7b21550ab85c5df841e0fba997ff54e3d522d rw,relatime - aufs none rw,si=9b4a7644303ed39c -64 15 0:3407 / /var/lib/docker/aufs/mnt/acdb90dc378e8ed2420b43a6d291f1c789a081cd1904018780cc038fcd7aae53 rw,relatime - aufs none rw,si=9b4a76434be2139c -65 15 0:3408 / /var/lib/docker/aufs/mnt/120e716f19d4714fbe63cc1ed246204f2c1106eefebc6537ba2587d7e7711959 rw,relatime - aufs none rw,si=9b4a76434be2339c -66 15 0:3409 / /var/lib/docker/aufs/mnt/b197b7fffb61d89e0ba1c40de9a9fc0d912e778b3c1bd828cf981ff37c1963bc rw,relatime - aufs none rw,si=9b4a76434be2039c -70 15 0:3412 / /var/lib/docker/aufs/mnt/1434b69d2e1bb18a9f0b96b9cdac30132b2688f5d1379f68a39a5e120c2f93eb rw,relatime - aufs none rw,si=9b4a76434be2639c -71 15 0:3413 / /var/lib/docker/aufs/mnt/16006e83caf33ab5eb0cd6afc92ea2ee8edeff897496b0bb3ec3a75b767374b3 rw,relatime - aufs none rw,si=9b4a7644d790439c -72 15 0:3414 / /var/lib/docker/aufs/mnt/55bfa5f44e94d27f91f79ba901b118b15098449165c87abf1b53ffff147ff164 rw,relatime - aufs none rw,si=9b4a7644d790239c -73 15 0:3415 / /var/lib/docker/aufs/mnt/1912b97a07ab21ccd98a2a27bc779bf3cf364a3138afa3c3e6f7f169a3c3eab5 rw,relatime - aufs none rw,si=9b4a76441822739c -76 15 0:3418 / /var/lib/docker/aufs/mnt/1a7c3292e8879bd91ffd9282e954f643b1db5683093574c248ff14a9609f2f56 rw,relatime - aufs none rw,si=9b4a76438cb7239c -77 15 0:3419 / /var/lib/docker/aufs/mnt/bb1faaf0d076ddba82c2318305a85f490dafa4e8a8640a8db8ed657c439120cc rw,relatime - aufs none rw,si=9b4a76438cb7339c -78 15 0:3420 / /var/lib/docker/aufs/mnt/1ab869f21d2241a73ac840c7f988490313f909ac642eba71d092204fec66dd7c rw,relatime - aufs none rw,si=9b4a76438cb7639c -79 15 0:3421 / /var/lib/docker/aufs/mnt/fd7245b2cfe3890fa5f5b452260e4edf9e7fb7746532ed9d83f7a0d7dbaa610e rw,relatime - aufs none rw,si=9b4a7644bdc0139c -80 15 0:3422 / /var/lib/docker/aufs/mnt/1e5686c5301f26b9b3cd24e322c608913465cc6c5d0dcd7c5e498d1314747d61 rw,relatime - aufs none rw,si=9b4a7644bdc0639c -81 15 0:3423 / /var/lib/docker/aufs/mnt/52edf6ee6e40bfec1e9301a4d4a92ab83d144e2ae4ce5099e99df6138cb844bf rw,relatime - aufs none rw,si=9b4a7644bdc0239c -82 15 0:3424 / /var/lib/docker/aufs/mnt/1ea10fb7085d28cda4904657dff0454e52598d28e1d77e4f2965bbc3666e808f rw,relatime - aufs none rw,si=9b4a76438cb7139c -83 15 0:3425 / /var/lib/docker/aufs/mnt/9c03e98c3593946dbd4087f8d83f9ca262f4a2efdc952ce60690838b9ba6c526 rw,relatime - aufs none rw,si=9b4a76443020639c -84 15 0:3426 / /var/lib/docker/aufs/mnt/220a2344d67437602c6d2cee9a98c46be13f82c2a8063919dd2fad52bf2fb7dd rw,relatime - aufs none rw,si=9b4a76434bff339c -94 15 0:3427 / /var/lib/docker/aufs/mnt/3b32876c5b200312c50baa476ff342248e88c8ea96e6a1032cd53a88738a1cf2 rw,relatime - aufs none rw,si=9b4a76434bff139c -95 15 0:3428 / /var/lib/docker/aufs/mnt/23ee2b8b0d4ae8db6f6d1e168e2c6f79f8a18f953b09f65e0d22cc1e67a3a6fa rw,relatime - aufs none rw,si=9b4a7646c305c39c -96 15 0:3429 / /var/lib/docker/aufs/mnt/e86e6daa70b61b57945fa178222615f3c3d6bcef12c9f28e9f8623d44dc2d429 rw,relatime - aufs none rw,si=9b4a7646c305f39c -97 15 0:3430 / /var/lib/docker/aufs/mnt/2413d07623e80860bb2e9e306fbdee699afd07525785c025c591231e864aa162 rw,relatime - aufs none rw,si=9b4a76434bff039c -98 15 0:3431 / /var/lib/docker/aufs/mnt/adfd622eb22340fc80b429e5564b125668e260bf9068096c46dd59f1386a4b7d rw,relatime - aufs none rw,si=9b4a7646a7a1039c -102 15 0:3435 / /var/lib/docker/aufs/mnt/27cd92e7a91d02e2d6b44d16679a00fb6d169b19b88822891084e7fd1a84882d rw,relatime - aufs none rw,si=9b4a7646f25ec39c -103 15 0:3436 / /var/lib/docker/aufs/mnt/27dfdaf94cfbf45055c748293c37dd68d9140240bff4c646cb09216015914a88 rw,relatime - aufs none rw,si=9b4a7646732f939c -104 15 0:3437 / /var/lib/docker/aufs/mnt/5ed7524aff68dfbf0fc601cbaeac01bab14391850a973dabf3653282a627920f rw,relatime - aufs none rw,si=9b4a7646732f839c -105 15 0:3438 / /var/lib/docker/aufs/mnt/2a0d4767e536beb5785b60e071e3ac8e5e812613ab143a9627bee77d0c9ab062 rw,relatime - aufs none rw,si=9b4a7646732fe39c -106 15 0:3439 / /var/lib/docker/aufs/mnt/dea3fc045d9f4ae51ba952450b948a822cf85c39411489ca5224f6d9a8d02bad rw,relatime - aufs none rw,si=9b4a764012ad839c -107 15 0:3440 / /var/lib/docker/aufs/mnt/2d140a787160798da60cb67c21b1210054ad4dafecdcf832f015995b9aa99cfd rw,relatime - aufs none rw,si=9b4a764012add39c -108 15 0:3441 / /var/lib/docker/aufs/mnt/cb190b2a8e984475914430fbad2382e0d20b9b659f8ef83ae8d170cc672e519c rw,relatime - aufs none rw,si=9b4a76454d9c239c -109 15 0:3442 / /var/lib/docker/aufs/mnt/2f4a012d5a7ffd90256a6e9aa479054b3dddbc3c6a343f26dafbf3196890223b rw,relatime - aufs none rw,si=9b4a76454d9c439c -110 15 0:3443 / /var/lib/docker/aufs/mnt/63cc77904b80c4ffbf49cb974c5d8733dc52ad7640d3ae87554b325d7312d87f rw,relatime - aufs none rw,si=9b4a76454d9c339c -111 15 0:3444 / /var/lib/docker/aufs/mnt/30333e872c451482ea2d235ff2192e875bd234006b238ae2bdde3b91a86d7522 rw,relatime - aufs none rw,si=9b4a76422cebf39c -112 15 0:3445 / /var/lib/docker/aufs/mnt/6c54fc1125da3925cae65b5c9a98f3be55b0a2c2666082e5094a4ba71beb5bff rw,relatime - aufs none rw,si=9b4a7646dd5a439c -113 15 0:3446 / /var/lib/docker/aufs/mnt/3087d48cb01cda9d0a83a9ca301e6ea40e8593d18c4921be4794c91a420ab9a3 rw,relatime - aufs none rw,si=9b4a7646dd5a739c -114 15 0:3447 / /var/lib/docker/aufs/mnt/cc2607462a8f55b179a749b144c3fdbb50678e1a4f3065ea04e283e9b1f1d8e2 rw,relatime - aufs none rw,si=9b4a7646dd5a239c -117 15 0:3450 / /var/lib/docker/aufs/mnt/310c5e8392b29e8658a22e08d96d63936633b7e2c38e8d220047928b00a03d24 rw,relatime - aufs none rw,si=9b4a7647932d739c -118 15 0:3451 / /var/lib/docker/aufs/mnt/38a1f0029406ba9c3b6058f2f406d8a1d23c855046cf355c91d87d446fcc1460 rw,relatime - aufs none rw,si=9b4a76445abc939c -119 15 0:3452 / /var/lib/docker/aufs/mnt/42e109ab7914ae997a11ccd860fd18e4d488c50c044c3240423ce15774b8b62e rw,relatime - aufs none rw,si=9b4a76445abca39c -120 15 0:3453 / /var/lib/docker/aufs/mnt/365d832af0402d052b389c1e9c0d353b48487533d20cd4351df8e24ec4e4f9d8 rw,relatime - aufs none rw,si=9b4a7644066aa39c -121 15 0:3454 / /var/lib/docker/aufs/mnt/d3fa8a24d695b6cda9b64f96188f701963d28bef0473343f8b212df1a2cf1d2b rw,relatime - aufs none rw,si=9b4a7644066af39c -122 15 0:3455 / /var/lib/docker/aufs/mnt/37d4f491919abc49a15d0c7a7cc8383f087573525d7d288accd14f0b4af9eae0 rw,relatime - aufs none rw,si=9b4a7644066ad39c -123 15 0:3456 / /var/lib/docker/aufs/mnt/93902707fe12cbdd0068ce73f2baad4b3a299189b1b19cb5f8a2025e106ae3f5 rw,relatime - aufs none rw,si=9b4a76444445f39c -126 15 0:3459 / /var/lib/docker/aufs/mnt/3b49291670a625b9bbb329ffba99bf7fa7abff80cefef040f8b89e2b3aad4f9f rw,relatime - aufs none rw,si=9b4a7640798a339c -127 15 0:3460 / /var/lib/docker/aufs/mnt/8d9c7b943cc8f854f4d0d4ec19f7c16c13b0cc4f67a41472a072648610cecb59 rw,relatime - aufs none rw,si=9b4a76427383039c -128 15 0:3461 / /var/lib/docker/aufs/mnt/3b6c90036526c376307df71d49c9f5fce334c01b926faa6a78186842de74beac rw,relatime - aufs none rw,si=9b4a7644badd439c -130 15 0:3463 / /var/lib/docker/aufs/mnt/7b24158eeddfb5d31b7e932e406ea4899fd728344335ff8e0765e89ddeb351dd rw,relatime - aufs none rw,si=9b4a7644badd539c -131 15 0:3464 / /var/lib/docker/aufs/mnt/3ead6dd5773765c74850cf6c769f21fe65c29d622ffa712664f9f5b80364ce27 rw,relatime - aufs none rw,si=9b4a7642f469939c -132 15 0:3465 / /var/lib/docker/aufs/mnt/3f825573b29547744a37b65597a9d6d15a8350be4429b7038d126a4c9a8e178f rw,relatime - aufs none rw,si=9b4a7642f469c39c -133 15 0:3466 / /var/lib/docker/aufs/mnt/f67aaaeb3681e5dcb99a41f847087370bd1c206680cb8c7b6a9819fd6c97a331 rw,relatime - aufs none rw,si=9b4a7647cc25939c -134 15 0:3467 / /var/lib/docker/aufs/mnt/41afe6cfb3c1fc2280b869db07699da88552786e28793f0bc048a265c01bd942 rw,relatime - aufs none rw,si=9b4a7647cc25c39c -135 15 0:3468 / /var/lib/docker/aufs/mnt/b8092ea59da34a40b120e8718c3ae9fa8436996edc4fc50e4b99c72dfd81e1af rw,relatime - aufs none rw,si=9b4a76445abc439c -136 15 0:3469 / /var/lib/docker/aufs/mnt/42c69d2cc179e2684458bb8596a9da6dad182c08eae9b74d5f0e615b399f75a5 rw,relatime - aufs none rw,si=9b4a76455ddbe39c -137 15 0:3470 / /var/lib/docker/aufs/mnt/ea0871954acd2d62a211ac60e05969622044d4c74597870c4f818fbb0c56b09b rw,relatime - aufs none rw,si=9b4a76455ddbf39c -138 15 0:3471 / /var/lib/docker/aufs/mnt/4307906b275ab3fc971786b3841ae3217ac85b6756ddeb7ad4ba09cd044c2597 rw,relatime - aufs none rw,si=9b4a76455ddb839c -139 15 0:3472 / /var/lib/docker/aufs/mnt/4390b872928c53500a5035634f3421622ed6299dc1472b631fc45de9f56dc180 rw,relatime - aufs none rw,si=9b4a76402f2fd39c -140 15 0:3473 / /var/lib/docker/aufs/mnt/6bb41e78863b85e4aa7da89455314855c8c3bda64e52a583bab15dc1fa2e80c2 rw,relatime - aufs none rw,si=9b4a76402f2fa39c -141 15 0:3474 / /var/lib/docker/aufs/mnt/4444f583c2a79c66608f4673a32c9c812154f027045fbd558c2d69920c53f835 rw,relatime - aufs none rw,si=9b4a764479dbd39c -142 15 0:3475 / /var/lib/docker/aufs/mnt/6f11883af4a05ea362e0c54df89058da4859f977efd07b6f539e1f55c1d2a668 rw,relatime - aufs none rw,si=9b4a76402f30b39c -143 15 0:3476 / /var/lib/docker/aufs/mnt/453490dd32e7c2e9ef906f995d8fb3c2753923d1a5e0ba3fd3296e2e4dc238e7 rw,relatime - aufs none rw,si=9b4a76402f30c39c -144 15 0:3477 / /var/lib/docker/aufs/mnt/45e5945735ee102b5e891c91650c57ec4b52bb53017d68f02d50ea8a6e230610 rw,relatime - aufs none rw,si=9b4a76423260739c -147 15 0:3480 / /var/lib/docker/aufs/mnt/4727a64a5553a1125f315b96bed10d3073d6988225a292cce732617c925b56ab rw,relatime - aufs none rw,si=9b4a76443030339c -150 15 0:3483 / /var/lib/docker/aufs/mnt/4e348b5187b9a567059306afc72d42e0ec5c893b0d4abd547526d5f9b6fb4590 rw,relatime - aufs none rw,si=9b4a7644f5d8c39c -151 15 0:3484 / /var/lib/docker/aufs/mnt/4efc616bfbc3f906718b052da22e4335f8e9f91ee9b15866ed3a8029645189ef rw,relatime - aufs none rw,si=9b4a7644f5d8939c -152 15 0:3485 / /var/lib/docker/aufs/mnt/83e730ae9754d5adb853b64735472d98dfa17136b8812ac9cfcd1eba7f4e7d2d rw,relatime - aufs none rw,si=9b4a76469aa7139c -153 15 0:3486 / /var/lib/docker/aufs/mnt/4fc5ba8a5b333be2b7eefacccb626772eeec0ae8a6975112b56c9fb36c0d342f rw,relatime - aufs none rw,si=9b4a7640128dc39c -154 15 0:3487 / /var/lib/docker/aufs/mnt/50200d5edff5dfe8d1ef3c78b0bbd709793ac6e936aa16d74ff66f7ea577b6f9 rw,relatime - aufs none rw,si=9b4a7640128da39c -155 15 0:3488 / /var/lib/docker/aufs/mnt/51e5e51604361448f0b9777f38329f414bc5ba9cf238f26d465ff479bd574b61 rw,relatime - aufs none rw,si=9b4a76444f68939c -156 15 0:3489 / /var/lib/docker/aufs/mnt/52a142149aa98bba83df8766bbb1c629a97b9799944ead90dd206c4bdf0b8385 rw,relatime - aufs none rw,si=9b4a76444f68b39c -157 15 0:3490 / /var/lib/docker/aufs/mnt/52dd21a94a00f58a1ed489312fcfffb91578089c76c5650364476f1d5de031bc rw,relatime - aufs none rw,si=9b4a76444f68f39c -158 15 0:3491 / /var/lib/docker/aufs/mnt/ee562415ddaad353ed22c88d0ca768a0c74bfba6333b6e25c46849ee22d990da rw,relatime - aufs none rw,si=9b4a7640128d839c -159 15 0:3492 / /var/lib/docker/aufs/mnt/db47a9e87173f7554f550c8a01891de79cf12acdd32e01f95c1a527a08bdfb2c rw,relatime - aufs none rw,si=9b4a764405a1d39c -160 15 0:3493 / /var/lib/docker/aufs/mnt/55e827bf6d44d930ec0b827c98356eb8b68c3301e2d60d1429aa72e05b4c17df rw,relatime - aufs none rw,si=9b4a764405a1a39c -162 15 0:3495 / /var/lib/docker/aufs/mnt/578dc4e0a87fc37ec081ca098430499a59639c09f6f12a8f48de29828a091aa6 rw,relatime - aufs none rw,si=9b4a76406d7d439c -163 15 0:3496 / /var/lib/docker/aufs/mnt/728cc1cb04fa4bc6f7bf7a90980beda6d8fc0beb71630874c0747b994efb0798 rw,relatime - aufs none rw,si=9b4a76444f20e39c -164 15 0:3497 / /var/lib/docker/aufs/mnt/5850cc4bd9b55aea46c7ad598f1785117607974084ea643580f58ce3222e683a rw,relatime - aufs none rw,si=9b4a7644a824239c -165 15 0:3498 / /var/lib/docker/aufs/mnt/89443b3f766d5a37bc8b84e29da8b84e6a3ea8486d3cf154e2aae1816516e4a8 rw,relatime - aufs none rw,si=9b4a7644a824139c -166 15 0:3499 / /var/lib/docker/aufs/mnt/f5ae8fd5a41a337907d16515bc3162525154b59c32314c695ecd092c3b47943d rw,relatime - aufs none rw,si=9b4a7644a824439c -167 15 0:3500 / /var/lib/docker/aufs/mnt/5a430854f2a03a9e5f7cbc9f3fb46a8ebca526a5b3f435236d8295e5998798f5 rw,relatime - aufs none rw,si=9b4a7647fc82439c -168 15 0:3501 / /var/lib/docker/aufs/mnt/eda16901ae4cead35070c39845cbf1e10bd6b8cb0ffa7879ae2d8a186e460f91 rw,relatime - aufs none rw,si=9b4a76441e0df39c -169 15 0:3502 / /var/lib/docker/aufs/mnt/5a593721430c2a51b119ff86a7e06ea2b37e3b4131f8f1344d402b61b0c8d868 rw,relatime - aufs none rw,si=9b4a764248bad39c -170 15 0:3503 / /var/lib/docker/aufs/mnt/d662ad0a30fbfa902e0962108685b9330597e1ee2abb16dc9462eb5a67fdd23f rw,relatime - aufs none rw,si=9b4a764248bae39c -171 15 0:3504 / /var/lib/docker/aufs/mnt/5bc9de5c79812843fb36eee96bef1ddba812407861f572e33242f4ee10da2c15 rw,relatime - aufs none rw,si=9b4a764248ba839c -172 15 0:3505 / /var/lib/docker/aufs/mnt/5e763de8e9b0f7d58d2e12a341e029ab4efb3b99788b175090d8209e971156c1 rw,relatime - aufs none rw,si=9b4a764248baa39c -173 15 0:3506 / /var/lib/docker/aufs/mnt/b4431dc2739936f1df6387e337f5a0c99cf051900c896bd7fd46a870ce61c873 rw,relatime - aufs none rw,si=9b4a76401263539c -174 15 0:3507 / /var/lib/docker/aufs/mnt/5f37830e5a02561ab8c67ea3113137ba69f67a60e41c05cb0e7a0edaa1925b24 rw,relatime - aufs none rw,si=9b4a76401263639c -184 15 0:3508 / /var/lib/docker/aufs/mnt/62ea10b957e6533538a4633a1e1d678502f50ddcdd354b2ca275c54dd7a7793a rw,relatime - aufs none rw,si=9b4a76401263039c -187 15 0:3509 / /var/lib/docker/aufs/mnt/d56ee9d44195fe390e042fda75ec15af5132adb6d5c69468fa8792f4e54a6953 rw,relatime - aufs none rw,si=9b4a76401263239c -188 15 0:3510 / /var/lib/docker/aufs/mnt/6a300930673174549c2b62f36c933f0332a20735978c007c805a301f897146c5 rw,relatime - aufs none rw,si=9b4a76455d4c539c -189 15 0:3511 / /var/lib/docker/aufs/mnt/64496c45c84d348c24d410015456d101601c30cab4d1998c395591caf7e57a70 rw,relatime - aufs none rw,si=9b4a76455d4c639c -190 15 0:3512 / /var/lib/docker/aufs/mnt/65a6a645883fe97a7422cd5e71ebe0bc17c8e6302a5361edf52e89747387e908 rw,relatime - aufs none rw,si=9b4a76455d4c039c -191 15 0:3513 / /var/lib/docker/aufs/mnt/672be40695f7b6e13b0a3ed9fc996c73727dede3481f58155950fcfad57ed616 rw,relatime - aufs none rw,si=9b4a76455d4c239c -192 15 0:3514 / /var/lib/docker/aufs/mnt/d42438acb2bfb2169e1c0d8e917fc824f7c85d336dadb0b0af36dfe0f001b3ba rw,relatime - aufs none rw,si=9b4a7642bfded39c -193 15 0:3515 / /var/lib/docker/aufs/mnt/b48a54abf26d01cb2ddd908b1ed6034d17397c1341bf0eb2b251a3e5b79be854 rw,relatime - aufs none rw,si=9b4a7642bfdee39c -194 15 0:3516 / /var/lib/docker/aufs/mnt/76f27134491f052bfb87f59092126e53ef875d6851990e59195a9da16a9412f8 rw,relatime - aufs none rw,si=9b4a7642bfde839c -195 15 0:3517 / /var/lib/docker/aufs/mnt/6bd626a5462b4f8a8e1cc7d10351326dca97a59b2758e5ea549a4f6350ce8a90 rw,relatime - aufs none rw,si=9b4a7642bfdea39c -196 15 0:3518 / /var/lib/docker/aufs/mnt/f1fe3549dbd6f5ca615e9139d9b53f0c83a3b825565df37628eacc13e70cbd6d rw,relatime - aufs none rw,si=9b4a7642bfdf539c -197 15 0:3519 / /var/lib/docker/aufs/mnt/6d0458c8426a9e93d58d0625737e6122e725c9408488ed9e3e649a9984e15c34 rw,relatime - aufs none rw,si=9b4a7642bfdf639c -198 15 0:3520 / /var/lib/docker/aufs/mnt/6e4c97db83aa82145c9cf2bafc20d500c0b5389643b689e3ae84188c270a48c5 rw,relatime - aufs none rw,si=9b4a7642bfdf039c -199 15 0:3521 / /var/lib/docker/aufs/mnt/eb94d6498f2c5969eaa9fa11ac2934f1ab90ef88e2d002258dca08e5ba74ea27 rw,relatime - aufs none rw,si=9b4a7642bfdf239c -200 15 0:3522 / /var/lib/docker/aufs/mnt/fe3f88f0c511608a2eec5f13a98703aa16e55dbf930309723d8a37101f539fe1 rw,relatime - aufs none rw,si=9b4a7642bfc3539c -201 15 0:3523 / /var/lib/docker/aufs/mnt/6f40c229fb9cad85fabf4b64a2640a5403ec03fe5ac1a57d0609fb8b606b9c83 rw,relatime - aufs none rw,si=9b4a7642bfc3639c -202 15 0:3524 / /var/lib/docker/aufs/mnt/7513e9131f7a8acf58ff15248237feb767c78732ca46e159f4d791e6ef031dbc rw,relatime - aufs none rw,si=9b4a7642bfc3039c -203 15 0:3525 / /var/lib/docker/aufs/mnt/79f48b00aa713cdf809c6bb7c7cb911b66e9a8076c81d6c9d2504139984ea2da rw,relatime - aufs none rw,si=9b4a7642bfc3239c -204 15 0:3526 / /var/lib/docker/aufs/mnt/c3680418350d11358f0a96c676bc5aa74fa00a7c89e629ef5909d3557b060300 rw,relatime - aufs none rw,si=9b4a7642f47cd39c -205 15 0:3527 / /var/lib/docker/aufs/mnt/7a1744dd350d7fcc0cccb6f1757ca4cbe5453f203a5888b0f1014d96ad5a5ef9 rw,relatime - aufs none rw,si=9b4a7642f47ce39c -206 15 0:3528 / /var/lib/docker/aufs/mnt/7fa99662db046be9f03c33c35251afda9ccdc0085636bbba1d90592cec3ff68d rw,relatime - aufs none rw,si=9b4a7642f47c839c -207 15 0:3529 / /var/lib/docker/aufs/mnt/f815021ef20da9c9b056bd1d52d8aaf6e2c0c19f11122fc793eb2b04eb995e35 rw,relatime - aufs none rw,si=9b4a7642f47ca39c -208 15 0:3530 / /var/lib/docker/aufs/mnt/801086ae3110192d601dfcebdba2db92e86ce6b6a9dba6678ea04488e4513669 rw,relatime - aufs none rw,si=9b4a7642dc6dd39c -209 15 0:3531 / /var/lib/docker/aufs/mnt/822ba7db69f21daddda87c01cfbfbf73013fc03a879daf96d16cdde6f9b1fbd6 rw,relatime - aufs none rw,si=9b4a7642dc6de39c -210 15 0:3532 / /var/lib/docker/aufs/mnt/834227c1a950fef8cae3827489129d0dd220541e60c6b731caaa765bf2e6a199 rw,relatime - aufs none rw,si=9b4a7642dc6d839c -211 15 0:3533 / /var/lib/docker/aufs/mnt/83dccbc385299bd1c7cf19326e791b33a544eea7b4cdfb6db70ea94eed4389fb rw,relatime - aufs none rw,si=9b4a7642dc6da39c -212 15 0:3534 / /var/lib/docker/aufs/mnt/f1b8e6f0e7c8928b5dcdab944db89306ebcae3e0b32f9ff40d2daa8329f21600 rw,relatime - aufs none rw,si=9b4a7645a126039c -213 15 0:3535 / /var/lib/docker/aufs/mnt/970efb262c7a020c2404cbcc5b3259efba0d110a786079faeef05bc2952abf3a rw,relatime - aufs none rw,si=9b4a7644c8ed139c -214 15 0:3536 / /var/lib/docker/aufs/mnt/84b6d73af7450f3117a77e15a5ca1255871fea6182cd8e8a7be6bc744be18c2c rw,relatime - aufs none rw,si=9b4a76406559139c -215 15 0:3537 / /var/lib/docker/aufs/mnt/88be2716e026bc681b5e63fe7942068773efbd0b6e901ca7ba441412006a96b6 rw,relatime - aufs none rw,si=9b4a76406559339c -216 15 0:3538 / /var/lib/docker/aufs/mnt/c81939aa166ce50cd8bca5cfbbcc420a78e0318dd5cd7c755209b9166a00a752 rw,relatime - aufs none rw,si=9b4a76406559239c -217 15 0:3539 / /var/lib/docker/aufs/mnt/e0f241645d64b7dc5ff6a8414087cca226be08fb54ce987d1d1f6350c57083aa rw,relatime - aufs none rw,si=9b4a7647cfc0f39c -218 15 0:3540 / /var/lib/docker/aufs/mnt/e10e2bf75234ed51d8a6a4bb39e465404fecbe318e54400d3879cdb2b0679c78 rw,relatime - aufs none rw,si=9b4a7647cfc0939c -219 15 0:3541 / /var/lib/docker/aufs/mnt/8f71d74c8cfc3228b82564aa9f09b2e576cff0083ddfb6aa5cb350346063f080 rw,relatime - aufs none rw,si=9b4a7647cfc0a39c -220 15 0:3542 / /var/lib/docker/aufs/mnt/9159f1eba2aef7f5205cc18d015cda7f5933cd29bba3b1b8aed5ccb5824c69ee rw,relatime - aufs none rw,si=9b4a76468cedd39c -221 15 0:3543 / /var/lib/docker/aufs/mnt/932cad71e652e048e500d9fbb5b8ea4fc9a269d42a3134ce527ceef42a2be56b rw,relatime - aufs none rw,si=9b4a76468cede39c -222 15 0:3544 / /var/lib/docker/aufs/mnt/bf1e1b5f529e8943cc0144ee86dbaaa37885c1ddffcef29537e0078ee7dd316a rw,relatime - aufs none rw,si=9b4a76468ced839c -223 15 0:3545 / /var/lib/docker/aufs/mnt/949d93ecf3322e09f858ce81d5f4b434068ec44ff84c375de03104f7b45ee955 rw,relatime - aufs none rw,si=9b4a76468ceda39c -224 15 0:3546 / /var/lib/docker/aufs/mnt/d65c6087f92dc2a3841b5251d2fe9ca07d4c6e5b021597692479740816e4e2a1 rw,relatime - aufs none rw,si=9b4a7645a126239c -225 15 0:3547 / /var/lib/docker/aufs/mnt/98a0153119d0651c193d053d254f6e16a68345a141baa80c87ae487e9d33f290 rw,relatime - aufs none rw,si=9b4a7640787cf39c -226 15 0:3548 / /var/lib/docker/aufs/mnt/99daf7fe5847c017392f6e59aa9706b3dfdd9e6d1ba11dae0f7fffde0a60b5e5 rw,relatime - aufs none rw,si=9b4a7640787c839c -227 15 0:3549 / /var/lib/docker/aufs/mnt/9ad1f2fe8a5599d4e10c5a6effa7f03d932d4e92ee13149031a372087a359079 rw,relatime - aufs none rw,si=9b4a7640787ca39c -228 15 0:3550 / /var/lib/docker/aufs/mnt/c26d64494da782ddac26f8370d86ac93e7c1666d88a7b99110fc86b35ea6a85d rw,relatime - aufs none rw,si=9b4a7642fc6b539c -229 15 0:3551 / /var/lib/docker/aufs/mnt/a49e4a8275133c230ec640997f35f172312eb0ea5bd2bbe10abf34aae98f30eb rw,relatime - aufs none rw,si=9b4a7642fc6b639c -230 15 0:3552 / /var/lib/docker/aufs/mnt/b5e2740c867ed843025f49d84e8d769de9e8e6039b3c8cb0735b5bf358994bc7 rw,relatime - aufs none rw,si=9b4a7642fc6b039c -231 15 0:3553 / /var/lib/docker/aufs/mnt/a826fdcf3a7039b30570054579b65763db605a314275d7aef31b872c13311b4b rw,relatime - aufs none rw,si=9b4a7642fc6b239c -232 15 0:3554 / /var/lib/docker/aufs/mnt/addf3025babf5e43b5a3f4a0da7ad863dda3c01fb8365c58fd8d28bb61dc11bc rw,relatime - aufs none rw,si=9b4a76407871d39c -233 15 0:3555 / /var/lib/docker/aufs/mnt/c5b6c6813ab3e5ebdc6d22cb2a3d3106a62095f2c298be52b07a3b0fa20ff690 rw,relatime - aufs none rw,si=9b4a76407871e39c -234 15 0:3556 / /var/lib/docker/aufs/mnt/af0609eaaf64e2392060cb46f5a9f3d681a219bb4c651d4f015bf573fbe6c4cf rw,relatime - aufs none rw,si=9b4a76407871839c -235 15 0:3557 / /var/lib/docker/aufs/mnt/e7f20e3c37ecad39cd90a97cd3549466d0d106ce4f0a930b8495442634fa4a1f rw,relatime - aufs none rw,si=9b4a76407871a39c -237 15 0:3559 / /var/lib/docker/aufs/mnt/b57a53d440ffd0c1295804fa68cdde35d2fed5409484627e71b9c37e4249fd5c rw,relatime - aufs none rw,si=9b4a76444445a39c -238 15 0:3560 / /var/lib/docker/aufs/mnt/b5e7d7b8f35e47efbba3d80c5d722f5e7bd43e54c824e54b4a4b351714d36d42 rw,relatime - aufs none rw,si=9b4a7647932d439c -239 15 0:3561 / /var/lib/docker/aufs/mnt/f1b136def157e9465640658f277f3347de593c6ae76412a2e79f7002f091cae2 rw,relatime - aufs none rw,si=9b4a76445abcd39c -240 15 0:3562 / /var/lib/docker/aufs/mnt/b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc rw,relatime - aufs none rw,si=9b4a7644403b339c -241 15 0:3563 / /var/lib/docker/aufs/mnt/b89b140cdbc95063761864e0a23346207fa27ee4c5c63a1ae85c9069a9d9cf1d rw,relatime - aufs none rw,si=9b4a7644aa19739c -242 15 0:3564 / /var/lib/docker/aufs/mnt/bc6a69ed51c07f5228f6b4f161c892e6a949c0e7e86a9c3432049d4c0e5cd298 rw,relatime - aufs none rw,si=9b4a7644aa19139c -243 15 0:3565 / /var/lib/docker/aufs/mnt/be4e2ba3f136933e239f7cf3d136f484fb9004f1fbdfee24a62a2c7b0ab30670 rw,relatime - aufs none rw,si=9b4a7644aa19339c -244 15 0:3566 / /var/lib/docker/aufs/mnt/e04ca1a4a5171e30d20f0c92f90a50b8b6f8600af5459c4b4fb25e42e864dfe1 rw,relatime - aufs none rw,si=9b4a7647932d139c -245 15 0:3567 / /var/lib/docker/aufs/mnt/be61576b31db893129aaffcd3dcb5ce35e49c4b71b30c392a78609a45c7323d8 rw,relatime - aufs none rw,si=9b4a7642d85f739c -246 15 0:3568 / /var/lib/docker/aufs/mnt/dda42c191e56becf672327658ab84fcb563322db3764b91c2fefe4aaef04c624 rw,relatime - aufs none rw,si=9b4a7642d85f139c -247 15 0:3569 / /var/lib/docker/aufs/mnt/c0a7995053330f3d88969247a2e72b07e2dd692133f5668a4a35ea3905561072 rw,relatime - aufs none rw,si=9b4a7642d85f339c -249 15 0:3571 / /var/lib/docker/aufs/mnt/c3594b2e5f08c59ff5ed338a1ba1eceeeb1f7fc5d180068338110c00b1eb8502 rw,relatime - aufs none rw,si=9b4a7642738c739c -250 15 0:3572 / /var/lib/docker/aufs/mnt/c58dce03a0ab0a7588393880379dc3bce9f96ec08ed3f99cf1555260ff0031e8 rw,relatime - aufs none rw,si=9b4a7642738c139c -251 15 0:3573 / /var/lib/docker/aufs/mnt/c73e9f1d109c9d14cb36e1c7489df85649be3911116d76c2fd3648ec8fd94e23 rw,relatime - aufs none rw,si=9b4a7642738c339c -252 15 0:3574 / /var/lib/docker/aufs/mnt/c9eef28c344877cd68aa09e543c0710ab2b305a0ff96dbb859bfa7808c3e8d01 rw,relatime - aufs none rw,si=9b4a7642d85f439c -253 15 0:3575 / /var/lib/docker/aufs/mnt/feb67148f548d70cb7484f2aaad2a86051cd6867a561741a2f13b552457d666e rw,relatime - aufs none rw,si=9b4a76468c55739c -254 15 0:3576 / /var/lib/docker/aufs/mnt/cdf1f96c36d35a96041a896bf398ec0f7dc3b0fb0643612a0f4b6ff96e04e1bb rw,relatime - aufs none rw,si=9b4a76468c55139c -255 15 0:3577 / /var/lib/docker/aufs/mnt/ec6e505872353268451ac4bc034c1df00f3bae4a3ea2261c6e48f7bd5417c1b3 rw,relatime - aufs none rw,si=9b4a76468c55339c -256 15 0:3578 / /var/lib/docker/aufs/mnt/d6dc8aca64efd90e0bc10274001882d0efb310d42ccbf5712b99b169053b8b1a rw,relatime - aufs none rw,si=9b4a7642738c439c -257 15 0:3579 / /var/lib/docker/aufs/mnt/d712594e2ff6eaeb895bfd150d694bd1305fb927e7a186b2dab7df2ea95f8f81 rw,relatime - aufs none rw,si=9b4a76401268f39c -259 15 0:3581 / /var/lib/docker/aufs/mnt/dbfa1174cd78cde2d7410eae442af0b416c4a0e6f87ed4ff1e9f169a0029abc0 rw,relatime - aufs none rw,si=9b4a76401268b39c -260 15 0:3582 / /var/lib/docker/aufs/mnt/e883f5a82316d7856fbe93ee8c0af5a920b7079619dd95c4ffd88bbd309d28dd rw,relatime - aufs none rw,si=9b4a76468c55439c -261 15 0:3583 / /var/lib/docker/aufs/mnt/fdec3eff581c4fc2b09f87befa2fa021f3f2d373bea636a87f1fb5b367d6347a rw,relatime - aufs none rw,si=9b4a7644aa1af39c -262 15 0:3584 / /var/lib/docker/aufs/mnt/ef764e26712184653067ecf7afea18a80854c41331ca0f0ef03e1bacf90a6ffc rw,relatime - aufs none rw,si=9b4a7644aa1a939c -263 15 0:3585 / /var/lib/docker/aufs/mnt/f3176b40c41fce8ce6942936359a2001a6f1b5c1bb40ee224186db0789ec2f76 rw,relatime - aufs none rw,si=9b4a7644aa1ab39c -264 15 0:3586 / /var/lib/docker/aufs/mnt/f5daf06785d3565c6dd18ea7d953d9a8b9606107781e63270fe0514508736e6a rw,relatime - aufs none rw,si=9b4a76401268c39c -58 15 0:3587 / /var/lib/docker/aufs/mnt/cde8c40f6524b7361af4f5ad05bb857dc9ee247c20852ba666195c0739e3a2b8-init rw,relatime - aufs none rw,si=9b4a76444445839c -67 15 0:3588 / /var/lib/docker/aufs/mnt/cde8c40f6524b7361af4f5ad05bb857dc9ee247c20852ba666195c0739e3a2b8 rw,relatime - aufs none rw,si=9b4a7644badd339c -265 15 0:3610 / /var/lib/docker/aufs/mnt/e812472cd2c8c4748d1ef71fac4e77e50d661b9349abe66ce3e23511ed44f414 rw,relatime - aufs none rw,si=9b4a76427937d39c -270 15 0:3615 / /var/lib/docker/aufs/mnt/997636e7c5c9d0d1376a217e295c14c205350b62bc12052804fb5f90abe6f183 rw,relatime - aufs none rw,si=9b4a76406540739c -273 15 0:3618 / /var/lib/docker/aufs/mnt/d5794d080417b6e52e69227c3873e0e4c1ff0d5a845ebe3860ec2f89a47a2a1e rw,relatime - aufs none rw,si=9b4a76454814039c -278 15 0:3623 / /var/lib/docker/aufs/mnt/586bdd48baced671bb19bc4d294ec325f26c55545ae267db426424f157d59c48 rw,relatime - aufs none rw,si=9b4a7644b439f39c -281 15 0:3626 / /var/lib/docker/aufs/mnt/69739d022f89f8586908bbd5edbbdd95ea5256356f177f9ffcc6ef9c0ea752d2 rw,relatime - aufs none rw,si=9b4a7644a0f1b39c -286 15 0:3631 / /var/lib/docker/aufs/mnt/ff28c27d5f894363993622de26d5dd352dba072f219e4691d6498c19bbbc15a9 rw,relatime - aufs none rw,si=9b4a7642265b339c -289 15 0:3634 / /var/lib/docker/aufs/mnt/aa128fe0e64fdede333aa48fd9de39530c91a9244a0f0649a3c411c61e372daa rw,relatime - aufs none rw,si=9b4a764012ada39c -99 15 8:33 / /media/REMOVE\040ME rw,nosuid,nodev,relatime - fuseblk /dev/sdc1 rw,user_id=0,group_id=0,allow_other,blksize=4096` -) - -func TestParseFedoraMountinfo(t *testing.T) { - r := bytes.NewBuffer([]byte(fedoraMountinfo)) - _, err := parseInfoFile(r, nil) - if err != nil { - t.Fatal(err) - } -} - -func TestParseUbuntuMountinfo(t *testing.T) { - r := bytes.NewBuffer([]byte(ubuntuMountInfo)) - _, err := parseInfoFile(r, nil) - if err != nil { - t.Fatal(err) - } -} - -func TestParseGentooMountinfo(t *testing.T) { - r := bytes.NewBuffer([]byte(gentooMountinfo)) - _, err := parseInfoFile(r, nil) - if err != nil { - t.Fatal(err) - } -} - -func TestParseFedoraMountinfoFields(t *testing.T) { - r := bytes.NewBuffer([]byte(fedoraMountinfo)) - infos, err := parseInfoFile(r, nil) - if err != nil { - t.Fatal(err) - } - expectedLength := 58 - if len(infos) != expectedLength { - t.Fatalf("Expected %d entries, got %d", expectedLength, len(infos)) - } - mi := Info{ - ID: 15, - Parent: 35, - Major: 0, - Minor: 3, - Root: "/", - Mountpoint: "/proc", - Opts: "rw,nosuid,nodev,noexec,relatime", - Optional: "shared:5", - Fstype: "proc", - Source: "proc", - VfsOpts: "rw", - } - - if *infos[0] != mi { - t.Fatalf("expected %#v, got %#v", mi, infos[0]) - } -} - -func TestParseMountinfoFilters(t *testing.T) { - r := bytes.NewReader([]byte(fedoraMountinfo)) - - infos, err := parseInfoFile(r, SingleEntryFilter("/sys/fs/cgroup")) - assert.NilError(t, err) - assert.Equal(t, 1, len(infos)) - - r.Reset([]byte(fedoraMountinfo)) - infos, err = parseInfoFile(r, SingleEntryFilter("nonexistent")) - assert.NilError(t, err) - assert.Equal(t, 0, len(infos)) - - r.Reset([]byte(fedoraMountinfo)) - infos, err = parseInfoFile(r, PrefixFilter("/sys")) - assert.NilError(t, err) - // there are 18 entries starting with /sys in fedoraMountinfo - assert.Equal(t, 18, len(infos)) - - r.Reset([]byte(fedoraMountinfo)) - infos, err = parseInfoFile(r, PrefixFilter("nonexistent")) - assert.NilError(t, err) - assert.Equal(t, 0, len(infos)) - - r.Reset([]byte(fedoraMountinfo)) - infos, err = parseInfoFile(r, ParentsFilter("/sys/fs/cgroup/cpu,cpuacct")) - assert.NilError(t, err) - // there should be 4 results returned: /sys/fs/cgroup/cpu,cpuacct /sys/fs/cgroup /sys / - assert.Equal(t, 4, len(infos)) -} diff --git a/pkg/mount/mountinfo_unsupported.go b/pkg/mount/mountinfo_unsupported.go deleted file mode 100644 index fd16d3ed698c0..0000000000000 --- a/pkg/mount/mountinfo_unsupported.go +++ /dev/null @@ -1,12 +0,0 @@ -// +build !windows,!linux,!freebsd freebsd,!cgo - -package mount // import "github.com/docker/docker/pkg/mount" - -import ( - "fmt" - "runtime" -) - -func parseMountTable(f FilterFunc) ([]*Info, error) { - return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) -} diff --git a/pkg/mount/mountinfo_windows.go b/pkg/mount/mountinfo_windows.go deleted file mode 100644 index 27e0f6976ec66..0000000000000 --- a/pkg/mount/mountinfo_windows.go +++ /dev/null @@ -1,6 +0,0 @@ -package mount // import "github.com/docker/docker/pkg/mount" - -func parseMountTable(f FilterFunc) ([]*Info, error) { - // Do NOT return an error! - return nil, nil -} diff --git a/pkg/mount/sharedsubtree_linux.go b/pkg/mount/sharedsubtree_linux.go deleted file mode 100644 index 8a100f0bc85a3..0000000000000 --- a/pkg/mount/sharedsubtree_linux.go +++ /dev/null @@ -1,71 +0,0 @@ -package mount // import "github.com/docker/docker/pkg/mount" - -// MakeShared ensures a mounted filesystem has the SHARED mount option enabled. -// See the supported options in flags.go for further reference. -func MakeShared(mountPoint string) error { - return ensureMountedAs(mountPoint, "shared") -} - -// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled. -// See the supported options in flags.go for further reference. -func MakeRShared(mountPoint string) error { - return ensureMountedAs(mountPoint, "rshared") -} - -// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled. -// See the supported options in flags.go for further reference. -func MakePrivate(mountPoint string) error { - return ensureMountedAs(mountPoint, "private") -} - -// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option -// enabled. See the supported options in flags.go for further reference. -func MakeRPrivate(mountPoint string) error { - return ensureMountedAs(mountPoint, "rprivate") -} - -// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled. -// See the supported options in flags.go for further reference. -func MakeSlave(mountPoint string) error { - return ensureMountedAs(mountPoint, "slave") -} - -// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled. -// See the supported options in flags.go for further reference. -func MakeRSlave(mountPoint string) error { - return ensureMountedAs(mountPoint, "rslave") -} - -// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option -// enabled. See the supported options in flags.go for further reference. -func MakeUnbindable(mountPoint string) error { - return ensureMountedAs(mountPoint, "unbindable") -} - -// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount -// option enabled. See the supported options in flags.go for further reference. -func MakeRUnbindable(mountPoint string) error { - return ensureMountedAs(mountPoint, "runbindable") -} - -// MakeMount ensures that the file or directory given is a mount point, -// bind mounting it to itself it case it is not. -func MakeMount(mnt string) error { - mounted, err := Mounted(mnt) - if err != nil { - return err - } - if mounted { - return nil - } - - return Mount(mnt, mnt, "none", "bind") -} - -func ensureMountedAs(mountPoint, options string) error { - if err := MakeMount(mountPoint); err != nil { - return err - } - - return ForceMount("", mountPoint, "none", options) -} diff --git a/pkg/mount/sharedsubtree_linux_test.go b/pkg/mount/sharedsubtree_linux_test.go deleted file mode 100644 index 019514491f902..0000000000000 --- a/pkg/mount/sharedsubtree_linux_test.go +++ /dev/null @@ -1,348 +0,0 @@ -// +build linux - -package mount // import "github.com/docker/docker/pkg/mount" - -import ( - "os" - "path" - "testing" - - "golang.org/x/sys/unix" -) - -// nothing is propagated in or out -func TestSubtreePrivate(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("root required") - } - - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - outside1Dir = path.Join(tmp, "outside1") - outside2Dir = path.Join(tmp, "outside2") - - outside1Path = path.Join(outside1Dir, "file.txt") - outside2Path = path.Join(outside2Dir, "file.txt") - outside1CheckPath = path.Join(targetDir, "a", "file.txt") - outside2CheckPath = path.Join(sourceDir, "b", "file.txt") - ) - if err := os.MkdirAll(path.Join(sourceDir, "a"), 0777); err != nil { - t.Fatal(err) - } - if err := os.MkdirAll(path.Join(sourceDir, "b"), 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(targetDir, 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(outside1Dir, 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(outside2Dir, 0777); err != nil { - t.Fatal(err) - } - - if err := createFile(outside1Path); err != nil { - t.Fatal(err) - } - if err := createFile(outside2Path); err != nil { - t.Fatal(err) - } - - // mount the shared directory to a target - if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - // next, make the target private - if err := MakePrivate(targetDir); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - // mount in an outside path to a mounted path inside the _source_ - if err := Mount(outside1Dir, path.Join(sourceDir, "a"), "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(path.Join(sourceDir, "a")); err != nil { - t.Fatal(err) - } - }() - - // check that this file _does_not_ show in the _target_ - if _, err := os.Stat(outside1CheckPath); err != nil && !os.IsNotExist(err) { - t.Fatal(err) - } else if err == nil { - t.Fatalf("%q should not be visible, but is", outside1CheckPath) - } - - // next mount outside2Dir into the _target_ - if err := Mount(outside2Dir, path.Join(targetDir, "b"), "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(path.Join(targetDir, "b")); err != nil { - t.Fatal(err) - } - }() - - // check that this file _does_not_ show in the _source_ - if _, err := os.Stat(outside2CheckPath); err != nil && !os.IsNotExist(err) { - t.Fatal(err) - } else if err == nil { - t.Fatalf("%q should not be visible, but is", outside2CheckPath) - } -} - -// Testing that when a target is a shared mount, -// then child mounts propagate to the source -func TestSubtreeShared(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("root required") - } - - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - outsideDir = path.Join(tmp, "outside") - - outsidePath = path.Join(outsideDir, "file.txt") - sourceCheckPath = path.Join(sourceDir, "a", "file.txt") - ) - - if err := os.MkdirAll(path.Join(sourceDir, "a"), 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(targetDir, 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(outsideDir, 0777); err != nil { - t.Fatal(err) - } - - if err := createFile(outsidePath); err != nil { - t.Fatal(err) - } - - // mount the source as shared - if err := MakeShared(sourceDir); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(sourceDir); err != nil { - t.Fatal(err) - } - }() - - // mount the shared directory to a target - if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - // mount in an outside path to a mounted path inside the target - if err := Mount(outsideDir, path.Join(targetDir, "a"), "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(path.Join(targetDir, "a")); err != nil { - t.Fatal(err) - } - }() - - // NOW, check that the file from the outside directory is available in the source directory - if _, err := os.Stat(sourceCheckPath); err != nil { - t.Fatal(err) - } -} - -// testing that mounts to a shared source show up in the slave target, -// and that mounts into a slave target do _not_ show up in the shared source -func TestSubtreeSharedSlave(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("root required") - } - - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - outside1Dir = path.Join(tmp, "outside1") - outside2Dir = path.Join(tmp, "outside2") - - outside1Path = path.Join(outside1Dir, "file.txt") - outside2Path = path.Join(outside2Dir, "file.txt") - outside1CheckPath = path.Join(targetDir, "a", "file.txt") - outside2CheckPath = path.Join(sourceDir, "b", "file.txt") - ) - if err := os.MkdirAll(path.Join(sourceDir, "a"), 0777); err != nil { - t.Fatal(err) - } - if err := os.MkdirAll(path.Join(sourceDir, "b"), 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(targetDir, 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(outside1Dir, 0777); err != nil { - t.Fatal(err) - } - if err := os.Mkdir(outside2Dir, 0777); err != nil { - t.Fatal(err) - } - - if err := createFile(outside1Path); err != nil { - t.Fatal(err) - } - if err := createFile(outside2Path); err != nil { - t.Fatal(err) - } - - // mount the source as shared - if err := MakeShared(sourceDir); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(sourceDir); err != nil { - t.Fatal(err) - } - }() - - // mount the shared directory to a target - if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - // next, make the target slave - if err := MakeSlave(targetDir); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() - - // mount in an outside path to a mounted path inside the _source_ - if err := Mount(outside1Dir, path.Join(sourceDir, "a"), "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(path.Join(sourceDir, "a")); err != nil { - t.Fatal(err) - } - }() - - // check that this file _does_ show in the _target_ - if _, err := os.Stat(outside1CheckPath); err != nil { - t.Fatal(err) - } - - // next mount outside2Dir into the _target_ - if err := Mount(outside2Dir, path.Join(targetDir, "b"), "none", "bind,rw"); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(path.Join(targetDir, "b")); err != nil { - t.Fatal(err) - } - }() - - // check that this file _does_not_ show in the _source_ - if _, err := os.Stat(outside2CheckPath); err != nil && !os.IsNotExist(err) { - t.Fatal(err) - } else if err == nil { - t.Fatalf("%q should not be visible, but is", outside2CheckPath) - } -} - -func TestSubtreeUnbindable(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("root required") - } - - tmp := path.Join(os.TempDir(), "mount-tests") - if err := os.MkdirAll(tmp, 0777); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - var ( - sourceDir = path.Join(tmp, "source") - targetDir = path.Join(tmp, "target") - ) - if err := os.MkdirAll(sourceDir, 0777); err != nil { - t.Fatal(err) - } - if err := os.MkdirAll(targetDir, 0777); err != nil { - t.Fatal(err) - } - - // next, make the source unbindable - if err := MakeUnbindable(sourceDir); err != nil { - t.Fatal(err) - } - defer func() { - if err := Unmount(sourceDir); err != nil { - t.Fatal(err) - } - }() - - // then attempt to mount it to target. It should fail - if err := Mount(sourceDir, targetDir, "none", "bind,rw"); err != nil && err != unix.EINVAL { - t.Fatal(err) - } else if err == nil { - t.Fatalf("%q should not have been bindable", sourceDir) - } - defer func() { - if err := Unmount(targetDir); err != nil { - t.Fatal(err) - } - }() -} - -func createFile(path string) error { - f, err := os.Create(path) - if err != nil { - return err - } - f.WriteString("hello world!") - return f.Close() -} diff --git a/pkg/namesgenerator/names-generator.go b/pkg/namesgenerator/names-generator.go index 15561805d3a43..93c0d75719a86 100644 --- a/pkg/namesgenerator/names-generator.go +++ b/pkg/namesgenerator/names-generator.go @@ -1,8 +1,8 @@ package namesgenerator // import "github.com/docker/docker/pkg/namesgenerator" import ( - "fmt" "math/rand" + "strconv" ) var ( @@ -14,13 +14,14 @@ var ( "amazing", "angry", "awesome", + "beautiful", "blissful", "bold", "boring", "brave", + "busy", "charming", "clever", - "cocky", "cool", "compassionate", "competent", @@ -39,16 +40,19 @@ var ( "elegant", "eloquent", "epic", + "exciting", "fervent", "festive", "flamboyant", "focused", "friendly", "frosty", + "funny", "gallant", "gifted", "goofy", "gracious", + "great", "happy", "hardcore", "heuristic", @@ -56,6 +60,8 @@ var ( "hungry", "infallible", "inspiring", + "interesting", + "intelligent", "jolly", "jovial", "keen", @@ -69,6 +75,7 @@ var ( "musing", "naughty", "nervous", + "nice", "nifty", "nostalgic", "objective", @@ -90,6 +97,7 @@ var ( "silly", "sleepy", "stoic", + "strange", "stupefied", "suspicious", "sweet", @@ -112,6 +120,9 @@ var ( // Docker, starting from 0.7.x, generates names from notable scientists and hackers. // Please, for any amazing man that you add to the list, consider adding an equally amazing woman to it, and vice versa. right = [...]string{ + // Maria Gaetana Agnesi - Italian mathematician, philosopher, theologian and humanitarian. She was the first woman to write a mathematics handbook and the first woman appointed as a Mathematics Professor at a University. https://en.wikipedia.org/wiki/Maria_Gaetana_Agnesi + "agnesi", + // Muhammad ibn Jābir al-Ḥarrānī al-Battānī was a founding father of astronomy. https://en.wikipedia.org/wiki/Mu%E1%B8%A5ammad_ibn_J%C4%81bir_al-%E1%B8%A4arr%C4%81n%C4%AB_al-Batt%C4%81n%C4%AB "albattani", @@ -124,9 +135,6 @@ var ( // Kathleen Antonelli, American computer programmer and one of the six original programmers of the ENIAC - https://en.wikipedia.org/wiki/Kathleen_Antonelli "antonelli", - // Maria Gaetana Agnesi - Italian mathematician, philosopher, theologian and humanitarian. She was the first woman to write a mathematics handbook and the first woman appointed as a Mathematics Professor at a University. https://en.wikipedia.org/wiki/Maria_Gaetana_Agnesi - "agnesi", - // Archimedes was a physicist, engineer and mathematician who invented too many things to list them here. https://en.wikipedia.org/wiki/Archimedes "archimedes", @@ -145,7 +153,7 @@ var ( // Stefan Banach - Polish mathematician, was one of the founders of modern functional analysis. https://en.wikipedia.org/wiki/Stefan_Banach "banach", - // Buckaroo Banzai and his mentor Dr. Hikita perfectd the "oscillation overthruster", a device that allows one to pass through solid matter. - https://en.wikipedia.org/wiki/The_Adventures_of_Buckaroo_Banzai_Across_the_8th_Dimension + // Buckaroo Banzai and his mentor Dr. Hikita perfected the "oscillation overthruster", a device that allows one to pass through solid matter. - https://en.wikipedia.org/wiki/The_Adventures_of_Buckaroo_Banzai_Across_the_8th_Dimension "banzai", // John Bardeen co-invented the transistor - https://en.wikipedia.org/wiki/John_Bardeen @@ -193,6 +201,9 @@ var ( // Satyendra Nath Bose - He provided the foundation for Bose–Einstein statistics and the theory of the Bose–Einstein condensate. - https://en.wikipedia.org/wiki/Satyendra_Nath_Bose "bose", + // Katherine Louise Bouman is an imaging scientist and Assistant Professor of Computer Science at the California Institute of Technology. She researches computational methods for imaging, and developed an algorithm that made possible the picture first visualization of a black hole using the Event Horizon Telescope. - https://en.wikipedia.org/wiki/Katie_Bouman + "bouman", + // Evelyn Boyd Granville - She was one of the first African-American woman to receive a Ph.D. in mathematics; she earned it in 1949 from Yale University. https://en.wikipedia.org/wiki/Evelyn_Boyd_Granville "boyd", @@ -205,9 +216,6 @@ var ( // Emmett Brown invented time travel. https://en.wikipedia.org/wiki/Emmett_Brown (thanks Brian Goff) "brown", - // Dame Susan Jocelyn Bell Burnell - discoverer of pulsars while a graduate student, "one of the most significant scientific achievements of the 20th Century". - https://en.wikipedia.org/wiki/Jocelyn_Bell_Burnell - "burnell", - // Linda Brown Buck - American biologist and Nobel laureate best known for her genetic and molecular analyses of the mechanisms of smell. https://en.wikipedia.org/wiki/Linda_B._Buck "buck", @@ -223,6 +231,12 @@ var ( // Dame Mary Lucy Cartwright - British mathematician who was one of the first to study what is now known as chaos theory. Also known for Cartwright's theorem which finds applications in signal processing. https://en.wikipedia.org/wiki/Mary_Cartwright "cartwright", + // George Washington Carver - American agricultural scientist and inventor. He was the most prominent black scientist of the early 20th century. https://en.wikipedia.org/wiki/George_Washington_Carver + "carver", + + // Vinton Gray Cerf - American Internet pioneer, recognised as one of "the fathers of the Internet". With Robert Elliot Kahn, he designed TCP and IP, the primary data communication protocols of the Internet and other computer networks. https://en.wikipedia.org/wiki/Vint_Cerf + "cerf", + // Subrahmanyan Chandrasekhar - Astrophysicist known for his mathematical theory on different stages and evolution in structures of the stars. He has won nobel prize for physics - https://en.wikipedia.org/wiki/Subrahmanyan_Chandrasekhar "chandrasekhar", @@ -235,21 +249,18 @@ var ( // Asima Chatterjee was an Indian organic chemist noted for her research on vinca alkaloids, development of drugs for treatment of epilepsy and malaria - https://en.wikipedia.org/wiki/Asima_Chatterjee "chatterjee", + // David Lee Chaum - American computer scientist and cryptographer. Known for his seminal contributions in the field of anonymous communication. https://en.wikipedia.org/wiki/David_Chaum + "chaum", + // Pafnuty Chebyshev - Russian mathematician. He is known fo his works on probability, statistics, mechanics, analytical geometry and number theory https://en.wikipedia.org/wiki/Pafnuty_Chebyshev "chebyshev", - // Clifford Christopher Cocks - British mathematician and cryptographer employed by the GCHQ. Invented in 1973 an equivalent of what is now known as the RSA public-key cryptosystem (Rivest, Shamir and Adleman first publicly described RSA in 1978). https://en.wikipedia.org/wiki/Clifford_Cocks - "cocks", + // Joan Clarke - Bletchley Park code breaker during the Second World War who pioneered techniques that remained top secret for decades. Also an accomplished numismatist https://en.wikipedia.org/wiki/Joan_Clarke + "clarke", // Bram Cohen - American computer programmer and author of the BitTorrent peer-to-peer protocol. https://en.wikipedia.org/wiki/Bram_Cohen "cohen", - // David Lee Chaum - American computer scientist and cryptographer. Known for his seminal contributions in the field of anonymous communication. https://en.wikipedia.org/wiki/David_Chaum - "chaum", - - // Joan Clarke - Bletchley Park code breaker during the Second World War who pioneered techniques that remained top secret for decades. Also an accomplished numismatist https://en.wikipedia.org/wiki/Joan_Clarke - "clarke", - // Jane Colden - American botanist widely considered the first female American botanist - https://en.wikipedia.org/wiki/Jane_Colden "colden", @@ -444,6 +455,9 @@ var ( // Yeong-Sil Jang was a Korean scientist and astronomer during the Joseon Dynasty; he invented the first metal printing press and water gauge. https://en.wikipedia.org/wiki/Jang_Yeong-sil "jang", + // Mae Carol Jemison - is an American engineer, physician, and former NASA astronaut. She became the first black woman to travel in space when she served as a mission specialist aboard the Space Shuttle Endeavour - https://en.wikipedia.org/wiki/Mae_Jemison + "jemison", + // Betty Jennings - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Jean_Bartik "jennings", @@ -483,7 +497,7 @@ var ( // Har Gobind Khorana - Indian-American biochemist who shared the 1968 Nobel Prize for Physiology - https://en.wikipedia.org/wiki/Har_Gobind_Khorana "khorana", - // Jack Kilby invented silicone integrated circuits and gave Silicon Valley its name. - https://en.wikipedia.org/wiki/Jack_Kilby + // Jack Kilby invented silicon integrated circuits and gave Silicon Valley its name. - https://en.wikipedia.org/wiki/Jack_Kilby "kilby", // Maria Kirch - German astronomer and first woman to discover a comet - https://en.wikipedia.org/wiki/Maria_Margarethe_Kirch @@ -579,12 +593,12 @@ var ( // Johanna Mestorf - German prehistoric archaeologist and first female museum director in Germany - https://en.wikipedia.org/wiki/Johanna_Mestorf "mestorf", - // Marvin Minsky - Pioneer in Artificial Intelligence, co-founder of the MIT's AI Lab, won the Turing Award in 1969. https://en.wikipedia.org/wiki/Marvin_Minsky - "minsky", - // Maryam Mirzakhani - an Iranian mathematician and the first woman to win the Fields Medal. https://en.wikipedia.org/wiki/Maryam_Mirzakhani "mirzakhani", + // Rita Levi-Montalcini - Won Nobel Prize in Physiology or Medicine jointly with colleague Stanley Cohen for the discovery of nerve growth factor (https://en.wikipedia.org/wiki/Rita_Levi-Montalcini) + "montalcini", + // Gordon Earle Moore - American engineer, Silicon Valley founding father, author of Moore's law. https://en.wikipedia.org/wiki/Gordon_Moore "moore", @@ -621,7 +635,7 @@ var ( // Poppy Northcutt. Poppy Northcutt was the first woman to work as part of NASA’s Mission Control. http://www.businessinsider.com/poppy-northcutt-helped-apollo-astronauts-2014-12?op=1 "northcutt", - // Robert Noyce invented silicone integrated circuits and gave Silicon Valley its name. - https://en.wikipedia.org/wiki/Robert_Noyce + // Robert Noyce invented silicon integrated circuits and gave Silicon Valley its name. - https://en.wikipedia.org/wiki/Robert_Noyce "noyce", // Panini - Ancient Indian linguist and grammarian from 4th century CE who worked on the world's first formal system - https://en.wikipedia.org/wiki/P%C4%81%E1%B9%87ini#Comparison_with_modern_formal_systems @@ -666,9 +680,6 @@ var ( // Sally Kristen Ride was an American physicist and astronaut. She was the first American woman in space, and the youngest American astronaut. https://en.wikipedia.org/wiki/Sally_Ride "ride", - // Rita Levi-Montalcini - Won Nobel Prize in Physiology or Medicine jointly with colleague Stanley Cohen for the discovery of nerve growth factor (https://en.wikipedia.org/wiki/Rita_Levi-Montalcini) - "montalcini", - // Dennis Ritchie - co-creator of UNIX and the C programming language. - https://en.wikipedia.org/wiki/Dennis_Ritchie "ritchie", @@ -696,6 +707,12 @@ var ( // Mildred Sanderson - American mathematician best known for Sanderson's theorem concerning modular invariants. https://en.wikipedia.org/wiki/Mildred_Sanderson "sanderson", + // Satoshi Nakamoto is the name used by the unknown person or group of people who developed bitcoin, authored the bitcoin white paper, and created and deployed bitcoin's original reference implementation. https://en.wikipedia.org/wiki/Satoshi_Nakamoto + "satoshi", + + // Adi Shamir - Israeli cryptographer whose numerous inventions and contributions to cryptography include the Ferge Fiat Shamir identification scheme, the Rivest Shamir Adleman (RSA) public-key cryptosystem, the Shamir's secret sharing scheme, the breaking of the Merkle-Hellman cryptosystem, the TWINKLE and TWIRL factoring devices and the discovery of differential cryptanalysis (with Eli Biham). https://en.wikipedia.org/wiki/Adi_Shamir + "shamir", + // Claude Shannon - The father of information theory and founder of digital circuit design theory. (https://en.wikipedia.org/wiki/Claude_Shannon) "shannon", @@ -723,15 +740,12 @@ var ( // Frances Spence - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Frances_Spence "spence", - // Ivan Edward Sutherland - American computer scientist and Internet pioneer, widely regarded as the father of computer graphics. https://en.wikipedia.org/wiki/Ivan_Sutherland - "sutherland", - - // Richard Matthew Stallman - the founder of the Free Software movement, the GNU project, the Free Software Foundation, and the League for Programming Freedom. He also invented the concept of copyleft to protect the ideals of this movement, and enshrined this concept in the widely-used GPL (General Public License) for software. https://en.wikiquote.org/wiki/Richard_Stallman - "stallman", - // Michael Stonebraker is a database research pioneer and architect of Ingres, Postgres, VoltDB and SciDB. Winner of 2014 ACM Turing Award. https://en.wikipedia.org/wiki/Michael_Stonebraker "stonebraker", + // Ivan Edward Sutherland - American computer scientist and Internet pioneer, widely regarded as the father of computer graphics. https://en.wikipedia.org/wiki/Ivan_Sutherland + "sutherland", + // Janese Swanson (with others) developed the first of the Carmen Sandiego games. She went on to found Girl Tech. https://en.wikipedia.org/wiki/Janese_Swanson "swanson", @@ -771,18 +785,21 @@ var ( // Dorothy Vaughan was a NASA mathematician and computer programmer on the SCOUT launch vehicle program that put America's first satellites into space - https://en.wikipedia.org/wiki/Dorothy_Vaughan "vaughan", + // Cédric Villani - French mathematician, won Fields Medal, Fermat Prize and Poincaré Price for his work in differential geometry and statistical mechanics. https://en.wikipedia.org/wiki/C%C3%A9dric_Villani + "villani", + // Sir Mokshagundam Visvesvaraya - is a notable Indian engineer. He is a recipient of the Indian Republic's highest honour, the Bharat Ratna, in 1955. On his birthday, 15 September is celebrated as Engineer's Day in India in his memory - https://en.wikipedia.org/wiki/Visvesvaraya "visvesvaraya", // Christiane Nüsslein-Volhard - German biologist, won Nobel Prize in Physiology or Medicine in 1995 for research on the genetic control of embryonic development. https://en.wikipedia.org/wiki/Christiane_N%C3%BCsslein-Volhard "volhard", - // Cédric Villani - French mathematician, won Fields Medal, Fermat Prize and Poincaré Price for his work in differential geometry and statistical mechanics. https://en.wikipedia.org/wiki/C%C3%A9dric_Villani - "villani", - // Marlyn Wescoff - one of the original programmers of the ENIAC. https://en.wikipedia.org/wiki/ENIAC - https://en.wikipedia.org/wiki/Marlyn_Meltzer "wescoff", + // Sylvia B. Wilbur - British computer scientist who helped develop the ARPANET, was one of the first to exchange email in the UK and a leading researcher in computer-supported collaborative work. https://en.wikipedia.org/wiki/Sylvia_Wilbur + "wilbur", + // Andrew Wiles - Notable British mathematician who proved the enigmatic Fermat's Last Theorem - https://en.wikipedia.org/wiki/Andrew_Wiles "wiles", @@ -823,13 +840,13 @@ var ( // integer between 0 and 10 will be added to the end of the name, e.g `focused_turing3` func GetRandomName(retry int) string { begin: - name := fmt.Sprintf("%s_%s", left[rand.Intn(len(left))], right[rand.Intn(len(right))]) + name := left[rand.Intn(len(left))] + "_" + right[rand.Intn(len(right))] //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) if name == "boring_wozniak" /* Steve Wozniak is not boring */ { goto begin } if retry > 0 { - name = fmt.Sprintf("%s%d", name, rand.Intn(10)) + name += strconv.Itoa(rand.Intn(10)) //nolint:gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) } return name } diff --git a/pkg/namesgenerator/names-generator_test.go b/pkg/namesgenerator/names-generator_test.go index 6ee31e9c33e2c..8702697745521 100644 --- a/pkg/namesgenerator/names-generator_test.go +++ b/pkg/namesgenerator/names-generator_test.go @@ -25,3 +25,12 @@ func TestNameRetries(t *testing.T) { } } + +func BenchmarkGetRandomName(b *testing.B) { + b.ReportAllocs() + var out string + for n := 0; n < b.N; n++ { + out = GetRandomName(5) + } + b.Log("Last result:", out) +} diff --git a/pkg/parsers/kernel/kernel.go b/pkg/parsers/kernel/kernel.go index 94780ef610014..3245b7416603e 100644 --- a/pkg/parsers/kernel/kernel.go +++ b/pkg/parsers/kernel/kernel.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows // Package kernel provides helper function to get, parse and compare kernel diff --git a/pkg/parsers/kernel/kernel_darwin.go b/pkg/parsers/kernel/kernel_darwin.go index 6e599eebcc21a..afb5b2e98e3ff 100644 --- a/pkg/parsers/kernel/kernel_darwin.go +++ b/pkg/parsers/kernel/kernel_darwin.go @@ -1,3 +1,4 @@ +//go:build darwin // +build darwin // Package kernel provides helper function to get, parse and compare kernel @@ -8,49 +9,51 @@ import ( "fmt" "os/exec" "strings" - - "github.com/mattn/go-shellwords" ) // GetKernelVersion gets the current kernel version. func GetKernelVersion() (*VersionInfo, error) { - release, err := getRelease() + osName, err := getSPSoftwareDataType() + if err != nil { + return nil, err + } + release, err := getRelease(osName) if err != nil { return nil, err } - return ParseRelease(release) } // getRelease uses `system_profiler SPSoftwareDataType` to get OSX kernel version -func getRelease() (string, error) { - cmd := exec.Command("system_profiler", "SPSoftwareDataType") - osName, err := cmd.Output() - if err != nil { - return "", err - } - +func getRelease(osName string) (string, error) { var release string - data := strings.Split(string(osName), "\n") + data := strings.Split(osName, "\n") for _, line := range data { - if strings.Contains(line, "Kernel Version") { - // It has the format like ' Kernel Version: Darwin 14.5.0' - content := strings.SplitN(line, ":", 2) - if len(content) != 2 { - return "", fmt.Errorf("Kernel Version is invalid") - } - - prettyNames, err := shellwords.Parse(content[1]) - if err != nil { - return "", fmt.Errorf("Kernel Version is invalid: %s", err.Error()) - } - - if len(prettyNames) != 2 { - return "", fmt.Errorf("Kernel Version needs to be 'Darwin x.x.x' ") - } - release = prettyNames[1] + if !strings.Contains(line, "Kernel Version") { + continue } + // It has the format like ' Kernel Version: Darwin 14.5.0' + content := strings.SplitN(line, ":", 2) + if len(content) != 2 { + return "", fmt.Errorf("Kernel Version is invalid") + } + + prettyNames := strings.SplitN(strings.TrimSpace(content[1]), " ", 2) + + if len(prettyNames) != 2 { + return "", fmt.Errorf("Kernel Version needs to be 'Darwin x.x.x' ") + } + release = prettyNames[1] } return release, nil } + +func getSPSoftwareDataType() (string, error) { + cmd := exec.Command("system_profiler", "SPSoftwareDataType") + osName, err := cmd.Output() + if err != nil { + return "", err + } + return string(osName), nil +} diff --git a/pkg/parsers/kernel/kernel_darwin_test.go b/pkg/parsers/kernel/kernel_darwin_test.go new file mode 100644 index 0000000000000..8fe58642e9cd6 --- /dev/null +++ b/pkg/parsers/kernel/kernel_darwin_test.go @@ -0,0 +1,28 @@ +package kernel + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestGetRelease(t *testing.T) { + // example output of "system_profiler SPSoftwareDataType" + const spSoftwareDataType = `Software: + + System Software Overview: + + System Version: macOS 10.14.6 (18G4032) + Kernel Version: Darwin 18.7.0 + Boot Volume: fastfood + Boot Mode: Normal + Computer Name: Macintosh + User Name: Foobar (foobar) + Secure Virtual Memory: Enabled + System Integrity Protection: Enabled + Time since boot: 6 days 23:16 +` + release, err := getRelease(spSoftwareDataType) + assert.NilError(t, err) + assert.Equal(t, release, "18.7.0") +} diff --git a/pkg/parsers/kernel/kernel_unix.go b/pkg/parsers/kernel/kernel_unix.go index 8a9aa31225529..b9508d376cd3b 100644 --- a/pkg/parsers/kernel/kernel_unix.go +++ b/pkg/parsers/kernel/kernel_unix.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd || openbsd // +build linux freebsd openbsd // Package kernel provides helper function to get, parse and compare kernel diff --git a/pkg/parsers/kernel/kernel_unix_test.go b/pkg/parsers/kernel/kernel_unix_test.go index 2f36490c53701..1ab3435fa8407 100644 --- a/pkg/parsers/kernel/kernel_unix_test.go +++ b/pkg/parsers/kernel/kernel_unix_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package kernel // import "github.com/docker/docker/pkg/parsers/kernel" @@ -27,6 +28,7 @@ func TestParseRelease(t *testing.T) { assertParseRelease(t, "3.4.54.longterm-1", &VersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0) assertParseRelease(t, "3.4.54.longterm-1", &VersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0) assertParseRelease(t, "3.8.0-19-generic", &VersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0) + assertParseRelease(t, "3.10.0-862.2.3.el7.x86_64", &VersionInfo{Kernel: 3, Major: 10, Minor: 0, Flavor: "-862.2.3.el7.x86_64"}, 0) assertParseRelease(t, "3.12.8tag", &VersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0) assertParseRelease(t, "3.12-1-amd64", &VersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0) assertParseRelease(t, "3.8.0", &VersionInfo{Kernel: 4, Major: 8, Minor: 0}, -1) @@ -40,7 +42,10 @@ func TestParseRelease(t *testing.T) { for _, invalid := range invalids { expectedMessage := fmt.Sprintf("Can't parse kernel version %v", invalid) if _, err := ParseRelease(invalid); err == nil || err.Error() != expectedMessage { - + if err == nil { + t.Fatalf("Expected %q, got nil", expectedMessage) + } + t.Fatalf("Expected %q, got %q", expectedMessage, err.Error()) } } } diff --git a/pkg/parsers/kernel/kernel_windows.go b/pkg/parsers/kernel/kernel_windows.go index b7b15a1fd2135..a04763872aed7 100644 --- a/pkg/parsers/kernel/kernel_windows.go +++ b/pkg/parsers/kernel/kernel_windows.go @@ -36,7 +36,7 @@ func GetKernelVersion() (*VersionInfo, error) { } KVI.kvi = blex - // Important - docker.exe MUST be manifested for this API to return + // Important - dockerd.exe MUST be manifested for this API to return // the correct information. dwVersion, err := windows.GetVersion() if err != nil { @@ -44,7 +44,7 @@ func GetKernelVersion() (*VersionInfo, error) { } KVI.major = int(dwVersion & 0xFF) - KVI.minor = int((dwVersion & 0XFF00) >> 8) + KVI.minor = int((dwVersion & 0xFF00) >> 8) KVI.build = int((dwVersion & 0xFFFF0000) >> 16) return KVI, nil diff --git a/pkg/parsers/kernel/uname_solaris.go b/pkg/parsers/kernel/uname_solaris.go deleted file mode 100644 index b2139b60e8a36..0000000000000 --- a/pkg/parsers/kernel/uname_solaris.go +++ /dev/null @@ -1,14 +0,0 @@ -package kernel // import "github.com/docker/docker/pkg/parsers/kernel" - -import ( - "golang.org/x/sys/unix" -) - -func uname() (*unix.Utsname, error) { - uts := &unix.Utsname{} - - if err := unix.Uname(uts); err != nil { - return nil, err - } - return uts, nil -} diff --git a/pkg/parsers/kernel/uname_unsupported.go b/pkg/parsers/kernel/uname_unsupported.go index 97906e4cd79c7..ed356310c4520 100644 --- a/pkg/parsers/kernel/uname_unsupported.go +++ b/pkg/parsers/kernel/uname_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package kernel // import "github.com/docker/docker/pkg/parsers/kernel" diff --git a/pkg/parsers/operatingsystem/operatingsystem_linux.go b/pkg/parsers/operatingsystem/operatingsystem_linux.go index b251d6aed6f50..1abddad9f4860 100644 --- a/pkg/parsers/operatingsystem/operatingsystem_linux.go +++ b/pkg/parsers/operatingsystem/operatingsystem_linux.go @@ -6,11 +6,8 @@ import ( "bufio" "bytes" "fmt" - "io/ioutil" "os" "strings" - - "github.com/mattn/go-shellwords" ) var ( @@ -26,6 +23,24 @@ var ( // GetOperatingSystem gets the name of the current operating system. func GetOperatingSystem() (string, error) { + if prettyName, err := getValueFromOsRelease("PRETTY_NAME"); err != nil { + return "", err + } else if prettyName != "" { + return prettyName, nil + } + + // If not set, defaults to PRETTY_NAME="Linux" + // c.f. http://www.freedesktop.org/software/systemd/man/os-release.html + return "Linux", nil +} + +// GetOperatingSystemVersion gets the version of the current operating system, as a string. +func GetOperatingSystemVersion() (string, error) { + return getValueFromOsRelease("VERSION_ID") +} + +// parses the os-release file and returns the value associated with `key` +func getValueFromOsRelease(key string) (string, error) { osReleaseFile, err := os.Open(etcOsRelease) if err != nil { if !os.IsNotExist(err) { @@ -38,38 +53,28 @@ func GetOperatingSystem() (string, error) { } defer osReleaseFile.Close() - var prettyName string + var value string + keyWithTrailingEqual := key + "=" scanner := bufio.NewScanner(osReleaseFile) for scanner.Scan() { line := scanner.Text() - if strings.HasPrefix(line, "PRETTY_NAME=") { + if strings.HasPrefix(line, keyWithTrailingEqual) { data := strings.SplitN(line, "=", 2) - prettyNames, err := shellwords.Parse(data[1]) - if err != nil { - return "", fmt.Errorf("PRETTY_NAME is invalid: %s", err.Error()) - } - if len(prettyNames) != 1 { - return "", fmt.Errorf("PRETTY_NAME needs to be enclosed by quotes if they have spaces: %s", data[1]) - } - prettyName = prettyNames[0] + value = strings.Trim(data[1], `"' `) // remove leading/trailing quotes and whitespace } } - if prettyName != "" { - return prettyName, nil - } - // If not set, defaults to PRETTY_NAME="Linux" - // c.f. http://www.freedesktop.org/software/systemd/man/os-release.html - return "Linux", nil + + return value, nil } // IsContainerized returns true if we are running inside a container. func IsContainerized() (bool, error) { - b, err := ioutil.ReadFile(proc1Cgroup) + b, err := os.ReadFile(proc1Cgroup) if err != nil { return false, err } for _, line := range bytes.Split(b, []byte{'\n'}) { - if len(line) > 0 && !bytes.HasSuffix(line, []byte{'/'}) && !bytes.HasSuffix(line, []byte("init.scope")) { + if len(line) > 0 && !bytes.HasSuffix(line, []byte(":/")) && !bytes.HasSuffix(line, []byte(":/init.scope")) { return true, nil } } diff --git a/pkg/parsers/operatingsystem/operatingsystem_linux_test.go b/pkg/parsers/operatingsystem/operatingsystem_linux_test.go new file mode 100644 index 0000000000000..d0ca93e97124c --- /dev/null +++ b/pkg/parsers/operatingsystem/operatingsystem_linux_test.go @@ -0,0 +1,277 @@ +//go:build linux || freebsd +// +build linux freebsd + +package operatingsystem // import "github.com/docker/docker/pkg/parsers/operatingsystem" + +import ( + "os" + "path/filepath" + "testing" + + "gotest.tools/v3/assert" +) + +type EtcReleaseParsingTest struct { + name string + content string + expected string + expectedErr string +} + +func TestGetOperatingSystem(t *testing.T) { + tests := []EtcReleaseParsingTest{ + { + content: `NAME="Ubuntu" +PRETTY_NAME_AGAIN="Ubuntu 14.04.LTS" +VERSION="14.04, Trusty Tahr" +ID=ubuntu +ID_LIKE=debian +VERSION_ID="14.04" +HOME_URL="http://www.ubuntu.com/" +SUPPORT_URL="http://help.ubuntu.com/" +BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`, + expected: "Linux", + }, + { + content: `NAME="Ubuntu" +VERSION="14.04, Trusty Tahr" +ID=ubuntu +ID_LIKE=debian +VERSION_ID="14.04" +HOME_URL="http://www.ubuntu.com/" +SUPPORT_URL="http://help.ubuntu.com/" +BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`, + expected: "Linux", + }, + { + content: `NAME=Gentoo +ID=gentoo +PRETTY_NAME="Gentoo/Linux" +ANSI_COLOR="1;32" +HOME_URL="http://www.gentoo.org/" +SUPPORT_URL="http://www.gentoo.org/main/en/support.xml" +BUG_REPORT_URL="https://bugs.gentoo.org/" +`, + expected: "Gentoo/Linux", + }, + { + content: `NAME="Ubuntu" +VERSION="14.04, Trusty Tahr" +ID=ubuntu +ID_LIKE=debian +PRETTY_NAME="Ubuntu 14.04 LTS" +VERSION_ID="14.04" +HOME_URL="http://www.ubuntu.com/" +SUPPORT_URL="http://help.ubuntu.com/" +BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`, + expected: "Ubuntu 14.04 LTS", + }, + { + content: `NAME="Ubuntu" +VERSION="14.04, Trusty Tahr" +ID=ubuntu +ID_LIKE=debian +PRETTY_NAME='Ubuntu 14.04 LTS'`, + expected: "Ubuntu 14.04 LTS", + }, + { + content: `PRETTY_NAME=Source +NAME="Source Mage"`, + expected: "Source", + }, + { + content: `PRETTY_NAME=Source +PRETTY_NAME="Source Mage"`, + expected: "Source Mage", + }, + } + + runEtcReleaseParsingTests(t, tests, GetOperatingSystem) +} + +func TestGetOperatingSystemVersion(t *testing.T) { + tests := []EtcReleaseParsingTest{ + { + name: "ubuntu 14.04", + content: `NAME="Ubuntu" +PRETTY_NAME="Ubuntu 14.04.LTS" +VERSION="14.04, Trusty Tahr" +ID=ubuntu +ID_LIKE=debian +VERSION_ID="14.04" +HOME_URL="http://www.ubuntu.com/" +SUPPORT_URL="http://help.ubuntu.com/" +BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`, + expected: "14.04", + }, + { + name: "gentoo", + content: `NAME=Gentoo +ID=gentoo +PRETTY_NAME="Gentoo/Linux" +ANSI_COLOR="1;32" +HOME_URL="http://www.gentoo.org/" +SUPPORT_URL="http://www.gentoo.org/main/en/support.xml" +BUG_REPORT_URL="https://bugs.gentoo.org/" +`, + }, + { + name: "dual version id", + content: `VERSION_ID="14.04" +VERSION_ID=18.04`, + expected: "18.04", + }, + } + + runEtcReleaseParsingTests(t, tests, GetOperatingSystemVersion) +} + +func runEtcReleaseParsingTests(t *testing.T, tests []EtcReleaseParsingTest, parsingFunc func() (string, error)) { + var backup = etcOsRelease + + dir := os.TempDir() + etcOsRelease = filepath.Join(dir, "etcOsRelease") + + defer func() { + os.Remove(etcOsRelease) + etcOsRelease = backup + }() + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if err := os.WriteFile(etcOsRelease, []byte(test.content), 0600); err != nil { + t.Fatalf("failed to write to %s: %v", etcOsRelease, err) + } + s, err := parsingFunc() + if test.expectedErr == "" { + assert.NilError(t, err) + } else { + assert.Error(t, err, test.expectedErr) + } + assert.Equal(t, s, test.expected) + }) + } +} + +func TestIsContainerized(t *testing.T) { + var ( + backup = proc1Cgroup + nonContainerizedProc1Cgroupsystemd226 = []byte(`9:memory:/init.scope +8:net_cls,net_prio:/ +7:cpuset:/ +6:freezer:/ +5:devices:/init.scope +4:blkio:/init.scope +3:cpu,cpuacct:/init.scope +2:perf_event:/ +1:name=systemd:/init.scope +`) + nonContainerizedProc1Cgroup = []byte(`14:name=systemd:/ +13:hugetlb:/ +12:net_prio:/ +11:perf_event:/ +10:bfqio:/ +9:blkio:/ +8:net_cls:/ +7:freezer:/ +6:devices:/ +5:memory:/ +4:cpuacct:/ +3:cpu:/ +2:cpuset:/ +`) + containerizedProc1Cgroup = []byte(`9:perf_event:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +8:blkio:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +7:net_cls:/ +6:freezer:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +5:devices:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +4:memory:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +3:cpuacct:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +2:cpu:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +1:cpuset:/`) + nonContainerizedProc1CgroupNotSystemd = []byte(`9:memory:/not/init.scope + 1:name=not_systemd:/not.init.scope +`) + ) + + dir := os.TempDir() + proc1Cgroup = filepath.Join(dir, "proc1Cgroup") + + defer func() { + os.Remove(proc1Cgroup) + proc1Cgroup = backup + }() + + if err := os.WriteFile(proc1Cgroup, nonContainerizedProc1Cgroup, 0600); err != nil { + t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) + } + inContainer, err := IsContainerized() + if err != nil { + t.Fatal(err) + } + if inContainer { + t.Fatal("Wrongly assuming containerized") + } + + if err := os.WriteFile(proc1Cgroup, nonContainerizedProc1Cgroupsystemd226, 0600); err != nil { + t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) + } + inContainer, err = IsContainerized() + if err != nil { + t.Fatal(err) + } + if inContainer { + t.Fatal("Wrongly assuming containerized for systemd /init.scope cgroup layout") + } + + if err := os.WriteFile(proc1Cgroup, nonContainerizedProc1CgroupNotSystemd, 0600); err != nil { + t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) + } + inContainer, err = IsContainerized() + if err != nil { + t.Fatal(err) + } + if !inContainer { + t.Fatal("Wrongly assuming non-containerized") + } + + if err := os.WriteFile(proc1Cgroup, containerizedProc1Cgroup, 0600); err != nil { + t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) + } + inContainer, err = IsContainerized() + if err != nil { + t.Fatal(err) + } + if !inContainer { + t.Fatal("Wrongly assuming non-containerized") + } +} + +func TestOsReleaseFallback(t *testing.T) { + var backup = etcOsRelease + var altBackup = altOsRelease + dir := os.TempDir() + etcOsRelease = filepath.Join(dir, "etcOsRelease") + altOsRelease = filepath.Join(dir, "altOsRelease") + + defer func() { + os.Remove(dir) + etcOsRelease = backup + altOsRelease = altBackup + }() + content := `NAME=Gentoo +ID=gentoo +PRETTY_NAME="Gentoo/Linux" +ANSI_COLOR="1;32" +HOME_URL="http://www.gentoo.org/" +SUPPORT_URL="http://www.gentoo.org/main/en/support.xml" +BUG_REPORT_URL="https://bugs.gentoo.org/" +` + if err := os.WriteFile(altOsRelease, []byte(content), 0600); err != nil { + t.Fatalf("failed to write to %s: %v", etcOsRelease, err) + } + s, err := GetOperatingSystem() + if err != nil || s != "Gentoo/Linux" { + t.Fatalf("Expected %q, got %q (err: %v)", "Gentoo/Linux", s, err) + } +} diff --git a/pkg/parsers/operatingsystem/operatingsystem_unix.go b/pkg/parsers/operatingsystem/operatingsystem_unix.go index f4792d37d5fb2..951f1b4e427b7 100644 --- a/pkg/parsers/operatingsystem/operatingsystem_unix.go +++ b/pkg/parsers/operatingsystem/operatingsystem_unix.go @@ -1,20 +1,28 @@ +//go:build freebsd || darwin // +build freebsd darwin package operatingsystem // import "github.com/docker/docker/pkg/parsers/operatingsystem" import ( + "bytes" "errors" - "os/exec" + + "golang.org/x/sys/unix" ) // GetOperatingSystem gets the name of the current operating system. func GetOperatingSystem() (string, error) { - cmd := exec.Command("uname", "-s") - osName, err := cmd.Output() - if err != nil { + utsname := &unix.Utsname{} + if err := unix.Uname(utsname); err != nil { return "", err } - return string(osName), nil + return string(utsname.Machine[:bytes.IndexByte(utsname.Sysname[:], 0)]), nil +} + +// GetOperatingSystemVersion gets the version of the current operating system, as a string. +func GetOperatingSystemVersion() (string, error) { + // there's no standard unix way of getting this, sadly... + return "", errors.New("Unsupported on generic unix") } // IsContainerized returns true if we are running inside a container. diff --git a/pkg/parsers/operatingsystem/operatingsystem_unix_test.go b/pkg/parsers/operatingsystem/operatingsystem_unix_test.go deleted file mode 100644 index d10ed4cdcd532..0000000000000 --- a/pkg/parsers/operatingsystem/operatingsystem_unix_test.go +++ /dev/null @@ -1,247 +0,0 @@ -// +build linux freebsd - -package operatingsystem // import "github.com/docker/docker/pkg/parsers/operatingsystem" - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" -) - -func TestGetOperatingSystem(t *testing.T) { - var backup = etcOsRelease - - invalids := []struct { - content string - errorExpected string - }{ - { - `PRETTY_NAME=Source Mage GNU/Linux -PRETTY_NAME=Ubuntu 14.04.LTS`, - "PRETTY_NAME needs to be enclosed by quotes if they have spaces: Source Mage GNU/Linux", - }, - { - `PRETTY_NAME="Ubuntu Linux -PRETTY_NAME=Ubuntu 14.04.LTS`, - "PRETTY_NAME is invalid: invalid command line string", - }, - { - `PRETTY_NAME=Ubuntu' -PRETTY_NAME=Ubuntu 14.04.LTS`, - "PRETTY_NAME is invalid: invalid command line string", - }, - { - `PRETTY_NAME' -PRETTY_NAME=Ubuntu 14.04.LTS`, - "PRETTY_NAME needs to be enclosed by quotes if they have spaces: Ubuntu 14.04.LTS", - }, - } - - valids := []struct { - content string - expected string - }{ - { - `NAME="Ubuntu" -PRETTY_NAME_AGAIN="Ubuntu 14.04.LTS" -VERSION="14.04, Trusty Tahr" -ID=ubuntu -ID_LIKE=debian -VERSION_ID="14.04" -HOME_URL="http://www.ubuntu.com/" -SUPPORT_URL="http://help.ubuntu.com/" -BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`, - "Linux", - }, - { - `NAME="Ubuntu" -VERSION="14.04, Trusty Tahr" -ID=ubuntu -ID_LIKE=debian -VERSION_ID="14.04" -HOME_URL="http://www.ubuntu.com/" -SUPPORT_URL="http://help.ubuntu.com/" -BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`, - "Linux", - }, - { - `NAME=Gentoo -ID=gentoo -PRETTY_NAME="Gentoo/Linux" -ANSI_COLOR="1;32" -HOME_URL="http://www.gentoo.org/" -SUPPORT_URL="http://www.gentoo.org/main/en/support.xml" -BUG_REPORT_URL="https://bugs.gentoo.org/" -`, - "Gentoo/Linux", - }, - { - `NAME="Ubuntu" -VERSION="14.04, Trusty Tahr" -ID=ubuntu -ID_LIKE=debian -PRETTY_NAME="Ubuntu 14.04 LTS" -VERSION_ID="14.04" -HOME_URL="http://www.ubuntu.com/" -SUPPORT_URL="http://help.ubuntu.com/" -BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`, - "Ubuntu 14.04 LTS", - }, - { - `NAME="Ubuntu" -VERSION="14.04, Trusty Tahr" -ID=ubuntu -ID_LIKE=debian -PRETTY_NAME='Ubuntu 14.04 LTS'`, - "Ubuntu 14.04 LTS", - }, - { - `PRETTY_NAME=Source -NAME="Source Mage"`, - "Source", - }, - { - `PRETTY_NAME=Source -PRETTY_NAME="Source Mage"`, - "Source Mage", - }, - } - - dir := os.TempDir() - etcOsRelease = filepath.Join(dir, "etcOsRelease") - - defer func() { - os.Remove(etcOsRelease) - etcOsRelease = backup - }() - - for _, elt := range invalids { - if err := ioutil.WriteFile(etcOsRelease, []byte(elt.content), 0600); err != nil { - t.Fatalf("failed to write to %s: %v", etcOsRelease, err) - } - s, err := GetOperatingSystem() - if err == nil || err.Error() != elt.errorExpected { - t.Fatalf("Expected an error %q, got %q (err: %v)", elt.errorExpected, s, err) - } - } - - for _, elt := range valids { - if err := ioutil.WriteFile(etcOsRelease, []byte(elt.content), 0600); err != nil { - t.Fatalf("failed to write to %s: %v", etcOsRelease, err) - } - s, err := GetOperatingSystem() - if err != nil || s != elt.expected { - t.Fatalf("Expected %q, got %q (err: %v)", elt.expected, s, err) - } - } -} - -func TestIsContainerized(t *testing.T) { - var ( - backup = proc1Cgroup - nonContainerizedProc1Cgroupsystemd226 = []byte(`9:memory:/init.scope -8:net_cls,net_prio:/ -7:cpuset:/ -6:freezer:/ -5:devices:/init.scope -4:blkio:/init.scope -3:cpu,cpuacct:/init.scope -2:perf_event:/ -1:name=systemd:/init.scope -`) - nonContainerizedProc1Cgroup = []byte(`14:name=systemd:/ -13:hugetlb:/ -12:net_prio:/ -11:perf_event:/ -10:bfqio:/ -9:blkio:/ -8:net_cls:/ -7:freezer:/ -6:devices:/ -5:memory:/ -4:cpuacct:/ -3:cpu:/ -2:cpuset:/ -`) - containerizedProc1Cgroup = []byte(`9:perf_event:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -8:blkio:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -7:net_cls:/ -6:freezer:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -5:devices:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -4:memory:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -3:cpuacct:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -2:cpu:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d -1:cpuset:/`) - ) - - dir := os.TempDir() - proc1Cgroup = filepath.Join(dir, "proc1Cgroup") - - defer func() { - os.Remove(proc1Cgroup) - proc1Cgroup = backup - }() - - if err := ioutil.WriteFile(proc1Cgroup, nonContainerizedProc1Cgroup, 0600); err != nil { - t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) - } - inContainer, err := IsContainerized() - if err != nil { - t.Fatal(err) - } - if inContainer { - t.Fatal("Wrongly assuming containerized") - } - - if err := ioutil.WriteFile(proc1Cgroup, nonContainerizedProc1Cgroupsystemd226, 0600); err != nil { - t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) - } - inContainer, err = IsContainerized() - if err != nil { - t.Fatal(err) - } - if inContainer { - t.Fatal("Wrongly assuming containerized for systemd /init.scope cgroup layout") - } - - if err := ioutil.WriteFile(proc1Cgroup, containerizedProc1Cgroup, 0600); err != nil { - t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) - } - inContainer, err = IsContainerized() - if err != nil { - t.Fatal(err) - } - if !inContainer { - t.Fatal("Wrongly assuming non-containerized") - } -} - -func TestOsReleaseFallback(t *testing.T) { - var backup = etcOsRelease - var altBackup = altOsRelease - dir := os.TempDir() - etcOsRelease = filepath.Join(dir, "etcOsRelease") - altOsRelease = filepath.Join(dir, "altOsRelease") - - defer func() { - os.Remove(dir) - etcOsRelease = backup - altOsRelease = altBackup - }() - content := `NAME=Gentoo -ID=gentoo -PRETTY_NAME="Gentoo/Linux" -ANSI_COLOR="1;32" -HOME_URL="http://www.gentoo.org/" -SUPPORT_URL="http://www.gentoo.org/main/en/support.xml" -BUG_REPORT_URL="https://bugs.gentoo.org/" -` - if err := ioutil.WriteFile(altOsRelease, []byte(content), 0600); err != nil { - t.Fatalf("failed to write to %s: %v", etcOsRelease, err) - } - s, err := GetOperatingSystem() - if err != nil || s != "Gentoo/Linux" { - t.Fatalf("Expected %q, got %q (err: %v)", "Gentoo/Linux", s, err) - } -} diff --git a/pkg/parsers/operatingsystem/operatingsystem_windows.go b/pkg/parsers/operatingsystem/operatingsystem_windows.go index 372de5146963b..5d58eca4d8b39 100644 --- a/pkg/parsers/operatingsystem/operatingsystem_windows.go +++ b/pkg/parsers/operatingsystem/operatingsystem_windows.go @@ -3,45 +3,57 @@ package operatingsystem // import "github.com/docker/docker/pkg/parsers/operatin import ( "fmt" + "github.com/Microsoft/hcsshim/osversion" "golang.org/x/sys/windows/registry" ) // GetOperatingSystem gets the name of the current operating system. func GetOperatingSystem() (string, error) { - - // Default return value - ret := "Unknown Operating System" - - k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) - if err != nil { - return ret, err + os, err := withCurrentVersionRegistryKey(func(key registry.Key) (os string, err error) { + if os, _, err = key.GetStringValue("ProductName"); err != nil { + return "", err + } + + releaseId, _, err := key.GetStringValue("ReleaseId") + if err != nil { + return + } + os = fmt.Sprintf("%s Version %s", os, releaseId) + + buildNumber, _, err := key.GetStringValue("CurrentBuildNumber") + if err != nil { + return + } + ubr, _, err := key.GetIntegerValue("UBR") + if err != nil { + return + } + os = fmt.Sprintf("%s (OS Build %s.%d)", os, buildNumber, ubr) + + return + }) + + if os == "" { + // Default return value + os = "Unknown Operating System" } - defer k.Close() - pn, _, err := k.GetStringValue("ProductName") - if err != nil { - return ret, err - } - ret = pn - - ri, _, err := k.GetStringValue("ReleaseId") - if err != nil { - return ret, err - } - ret = fmt.Sprintf("%s Version %s", ret, ri) - - cbn, _, err := k.GetStringValue("CurrentBuildNumber") - if err != nil { - return ret, err - } + return os, err +} - ubr, _, err := k.GetIntegerValue("UBR") +func withCurrentVersionRegistryKey(f func(registry.Key) (string, error)) (string, error) { + key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) if err != nil { - return ret, err + return "", err } - ret = fmt.Sprintf("%s (OS Build %s.%d)", ret, cbn, ubr) + defer key.Close() + return f(key) +} - return ret, nil +// GetOperatingSystemVersion gets the version of the current operating system, as a string. +func GetOperatingSystemVersion() (string, error) { + version := osversion.Get() + return fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build), nil } // IsContainerized returns true if we are running inside a container. diff --git a/pkg/pidfile/pidfile.go b/pkg/pidfile/pidfile.go index 0617a89e5ff35..a4dac5d02589b 100644 --- a/pkg/pidfile/pidfile.go +++ b/pkg/pidfile/pidfile.go @@ -5,7 +5,6 @@ package pidfile // import "github.com/docker/docker/pkg/pidfile" import ( "fmt" - "io/ioutil" "os" "path/filepath" "strconv" @@ -20,7 +19,7 @@ type PIDFile struct { } func checkPIDFileAlreadyExists(path string) error { - if pidByte, err := ioutil.ReadFile(path); err == nil { + if pidByte, err := os.ReadFile(path); err == nil { pidString := strings.TrimSpace(string(pidByte)) if pid, err := strconv.Atoi(pidString); err == nil { if processExists(pid) { @@ -37,10 +36,10 @@ func New(path string) (*PIDFile, error) { return nil, err } // Note MkdirAll returns nil if a directory already exists - if err := system.MkdirAll(filepath.Dir(path), os.FileMode(0755), ""); err != nil { + if err := system.MkdirAll(filepath.Dir(path), os.FileMode(0755)); err != nil { return nil, err } - if err := ioutil.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil { + if err := os.WriteFile(path, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil { return nil, err } diff --git a/pkg/pidfile/pidfile_darwin.go b/pkg/pidfile/pidfile_darwin.go index 92746aa7bff25..943183d682096 100644 --- a/pkg/pidfile/pidfile_darwin.go +++ b/pkg/pidfile/pidfile_darwin.go @@ -1,3 +1,4 @@ +//go:build darwin // +build darwin package pidfile // import "github.com/docker/docker/pkg/pidfile" diff --git a/pkg/pidfile/pidfile_test.go b/pkg/pidfile/pidfile_test.go index cd9878e1e4309..59860350a7446 100644 --- a/pkg/pidfile/pidfile_test.go +++ b/pkg/pidfile/pidfile_test.go @@ -1,14 +1,13 @@ package pidfile // import "github.com/docker/docker/pkg/pidfile" import ( - "io/ioutil" "os" "path/filepath" "testing" ) func TestNewAndRemove(t *testing.T) { - dir, err := ioutil.TempDir(os.TempDir(), "test-pidfile") + dir, err := os.MkdirTemp(os.TempDir(), "test-pidfile") if err != nil { t.Fatal("Could not create test directory") } diff --git a/pkg/pidfile/pidfile_unix.go b/pkg/pidfile/pidfile_unix.go index cc6696d211c1c..bcf9ebcac2cd0 100644 --- a/pkg/pidfile/pidfile_unix.go +++ b/pkg/pidfile/pidfile_unix.go @@ -1,3 +1,4 @@ +//go:build !windows && !darwin // +build !windows,!darwin package pidfile // import "github.com/docker/docker/pkg/pidfile" diff --git a/pkg/platform/architecture_linux.go b/pkg/platform/architecture_linux.go deleted file mode 100644 index a260a23f4faff..0000000000000 --- a/pkg/platform/architecture_linux.go +++ /dev/null @@ -1,18 +0,0 @@ -// Package platform provides helper function to get the runtime architecture -// for different platforms. -package platform // import "github.com/docker/docker/pkg/platform" - -import ( - "bytes" - - "golang.org/x/sys/unix" -) - -// runtimeArchitecture gets the name of the current architecture (x86, x86_64, …) -func runtimeArchitecture() (string, error) { - utsname := &unix.Utsname{} - if err := unix.Uname(utsname); err != nil { - return "", err - } - return string(utsname.Machine[:bytes.IndexByte(utsname.Machine[:], 0)]), nil -} diff --git a/pkg/platform/architecture_unix.go b/pkg/platform/architecture_unix.go index d51f68698f364..2585e0620f562 100644 --- a/pkg/platform/architecture_unix.go +++ b/pkg/platform/architecture_unix.go @@ -1,20 +1,21 @@ -// +build freebsd darwin +//go:build !windows +// +build !windows // Package platform provides helper function to get the runtime architecture // for different platforms. package platform // import "github.com/docker/docker/pkg/platform" import ( - "os/exec" - "strings" + "bytes" + + "golang.org/x/sys/unix" ) // runtimeArchitecture gets the name of the current architecture (x86, x86_64, i86pc, sun4v, ...) func runtimeArchitecture() (string, error) { - cmd := exec.Command("/usr/bin/uname", "-m") - machine, err := cmd.Output() - if err != nil { + utsname := &unix.Utsname{} + if err := unix.Uname(utsname); err != nil { return "", err } - return strings.TrimSpace(string(machine)), nil + return string(utsname.Machine[:bytes.IndexByte(utsname.Machine[:], 0)]), nil } diff --git a/pkg/plugins/client.go b/pkg/plugins/client.go index 0353305358fa2..752fecd0ae471 100644 --- a/pkg/plugins/client.go +++ b/pkg/plugins/client.go @@ -5,7 +5,6 @@ import ( "context" "encoding/json" "io" - "io/ioutil" "net/http" "net/url" "time" @@ -187,7 +186,7 @@ func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool, } if resp.StatusCode != http.StatusOK { - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) resp.Body.Close() cancelRequest() if err != nil { diff --git a/pkg/plugins/client_test.go b/pkg/plugins/client_test.go index c3a4892272cd0..f93734d367207 100644 --- a/pkg/plugins/client_test.go +++ b/pkg/plugins/client_test.go @@ -2,7 +2,6 @@ package plugins // import "github.com/docker/docker/pkg/plugins" import ( "bytes" - "context" "encoding/json" "io" "net/http" @@ -15,8 +14,8 @@ import ( "github.com/docker/docker/pkg/plugins/transport" "github.com/docker/go-connections/tlsconfig" "github.com/pkg/errors" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) var ( @@ -71,7 +70,7 @@ func TestEchoInputOutput(t *testing.T) { m := Manifest{[]string{"VolumeDriver", "NetworkDriver"}} mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) { - if r.Method != "POST" { + if r.Method != http.MethodPost { t.Fatalf("Expected POST, got %s\n", r.Method) } @@ -186,7 +185,7 @@ func TestClientStream(t *testing.T) { var output Manifest mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) { - if r.Method != "POST" { + if r.Method != http.MethodPost { t.Fatalf("Expected POST, got %s", r.Method) } @@ -219,7 +218,7 @@ func TestClientSendFile(t *testing.T) { t.Fatal(err) } mux.HandleFunc("/Test.Echo", func(w http.ResponseWriter, r *http.Request) { - if r.Method != "POST" { + if r.Method != http.MethodPost { t.Fatalf("Expected POST, got %s\n", r.Method) } @@ -237,9 +236,13 @@ func TestClientSendFile(t *testing.T) { } func TestClientWithRequestTimeout(t *testing.T) { + type timeoutError interface { + Timeout() bool + } + timeout := 1 * time.Millisecond testHandler := func(w http.ResponseWriter, r *http.Request) { - time.Sleep(timeout + 1*time.Millisecond) + time.Sleep(timeout + 10*time.Millisecond) w.WriteHeader(http.StatusOK) } @@ -250,13 +253,9 @@ func TestClientWithRequestTimeout(t *testing.T) { _, err := client.callWithRetry("/Plugin.Hello", nil, false, WithRequestTimeout(timeout)) assert.Assert(t, is.ErrorContains(err, ""), "expected error") - err = errors.Cause(err) - - switch e := err.(type) { - case *url.Error: - err = e.Err - } - assert.DeepEqual(t, context.DeadlineExceeded, err) + var tErr timeoutError + assert.Assert(t, errors.As(err, &tErr)) + assert.Assert(t, tErr.Timeout()) } type testRequestWrapper struct { @@ -264,7 +263,7 @@ type testRequestWrapper struct { } func (w *testRequestWrapper) NewRequest(path string, data io.Reader) (*http.Request, error) { - req, err := http.NewRequest("POST", path, data) + req, err := http.NewRequest(http.MethodPost, path, data) if err != nil { return nil, err } diff --git a/pkg/plugins/discovery.go b/pkg/plugins/discovery.go index 4b79bd29ad298..f24673ed87c5f 100644 --- a/pkg/plugins/discovery.go +++ b/pkg/plugins/discovery.go @@ -3,7 +3,6 @@ package plugins // import "github.com/docker/docker/pkg/plugins" import ( "encoding/json" "fmt" - "io/ioutil" "net/url" "os" "path/filepath" @@ -29,33 +28,35 @@ func newLocalRegistry() localRegistry { // Scan scans all the plugin paths and returns all the names it found func Scan() ([]string, error) { var names []string - dirEntries, err := ioutil.ReadDir(socketsPath) + dirEntries, err := os.ReadDir(socketsPath) if err != nil && !os.IsNotExist(err) { return nil, errors.Wrap(err, "error reading dir entries") } - for _, fi := range dirEntries { - if fi.IsDir() { - fi, err = os.Stat(filepath.Join(socketsPath, fi.Name(), fi.Name()+".sock")) + for _, entry := range dirEntries { + if entry.IsDir() { + fi, err := os.Stat(filepath.Join(socketsPath, entry.Name(), entry.Name()+".sock")) if err != nil { continue } + + entry = fileInfoToDirEntry(fi) } - if fi.Mode()&os.ModeSocket != 0 { - names = append(names, strings.TrimSuffix(filepath.Base(fi.Name()), filepath.Ext(fi.Name()))) + if entry.Type()&os.ModeSocket != 0 { + names = append(names, strings.TrimSuffix(filepath.Base(entry.Name()), filepath.Ext(entry.Name()))) } } for _, p := range specsPaths { - dirEntries, err := ioutil.ReadDir(p) + dirEntries, err := os.ReadDir(p) if err != nil && !os.IsNotExist(err) { return nil, errors.Wrap(err, "error reading dir entries") } for _, fi := range dirEntries { if fi.IsDir() { - infos, err := ioutil.ReadDir(filepath.Join(p, fi.Name())) + infos, err := os.ReadDir(filepath.Join(p, fi.Name())) if err != nil { continue } @@ -108,7 +109,7 @@ func (l *localRegistry) Plugin(name string) (*Plugin, error) { } func readPluginInfo(name, path string) (*Plugin, error) { - content, err := ioutil.ReadFile(path) + content, err := os.ReadFile(path) if err != nil { return nil, err } diff --git a/pkg/plugins/discovery_test.go b/pkg/plugins/discovery_test.go index 28fda41bad2f1..f162fe662a1d2 100644 --- a/pkg/plugins/discovery_test.go +++ b/pkg/plugins/discovery_test.go @@ -1,14 +1,13 @@ package plugins // import "github.com/docker/docker/pkg/plugins" import ( - "io/ioutil" "os" "path/filepath" "testing" ) func Setup(t *testing.T) (string, func()) { - tmpdir, err := ioutil.TempDir("", "docker-test") + tmpdir, err := os.MkdirTemp("", "docker-test") if err != nil { t.Fatal(err) } @@ -44,7 +43,7 @@ func TestFileSpecPlugin(t *testing.T) { if err := os.MkdirAll(filepath.Dir(c.path), 0755); err != nil { t.Fatal(err) } - if err := ioutil.WriteFile(c.path, []byte(c.addr), 0644); err != nil { + if err := os.WriteFile(c.path, []byte(c.addr), 0644); err != nil { t.Fatal(err) } @@ -87,7 +86,7 @@ func TestFileJSONSpecPlugin(t *testing.T) { } }` - if err := ioutil.WriteFile(p, []byte(spec), 0644); err != nil { + if err := os.WriteFile(p, []byte(spec), 0644); err != nil { t.Fatal(err) } @@ -128,7 +127,7 @@ func TestFileJSONSpecPluginWithoutTLSConfig(t *testing.T) { "Addr": "https://example.com/docker/plugin" }` - if err := ioutil.WriteFile(p, []byte(spec), 0644); err != nil { + if err := os.WriteFile(p, []byte(spec), 0644); err != nil { t.Fatal(err) } diff --git a/pkg/plugins/discovery_unix.go b/pkg/plugins/discovery_unix.go index 58058f2828685..d645da8ce4219 100644 --- a/pkg/plugins/discovery_unix.go +++ b/pkg/plugins/discovery_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package plugins // import "github.com/docker/docker/pkg/plugins" diff --git a/pkg/plugins/discovery_unix_test.go b/pkg/plugins/discovery_unix_test.go index b4aefc83e488a..cb1f204f9156b 100644 --- a/pkg/plugins/discovery_unix_test.go +++ b/pkg/plugins/discovery_unix_test.go @@ -1,17 +1,17 @@ +//go:build !windows // +build !windows package plugins // import "github.com/docker/docker/pkg/plugins" import ( "fmt" - "io/ioutil" "net" "os" "path/filepath" "reflect" "testing" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestLocalSocket(t *testing.T) { @@ -84,7 +84,7 @@ func TestScan(t *testing.T) { t.Fatal(err) } - err = ioutil.WriteFile(path, []byte(addr), 0644) + err = os.WriteFile(path, []byte(addr), 0644) if err != nil { t.Fatal(err) } diff --git a/pkg/plugins/plugin_test.go b/pkg/plugins/plugin_test.go index ce98078f87c17..1252dd17b2228 100644 --- a/pkg/plugins/plugin_test.go +++ b/pkg/plugins/plugin_test.go @@ -4,8 +4,8 @@ import ( "bytes" "encoding/json" "io" - "io/ioutil" "net/http" + "os" "path/filepath" "runtime" "sync" @@ -15,7 +15,7 @@ import ( "github.com/docker/docker/pkg/plugins/transport" "github.com/docker/go-connections/tlsconfig" "github.com/pkg/errors" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) const ( @@ -77,11 +77,11 @@ func TestGet(t *testing.T) { // check negative case where plugin fruit doesn't implement banana _, err = Get("fruit", "banana") - assert.Equal(t, errors.Cause(err), ErrNotImplements) + assert.Assert(t, errors.Is(err, ErrNotImplements)) // check negative case where plugin vegetable doesn't exist _, err = Get("vegetable", "potato") - assert.Equal(t, errors.Cause(err), ErrNotFound) + assert.Assert(t, errors.Is(err, ErrNotFound)) } func TestPluginWithNoManifest(t *testing.T) { @@ -95,7 +95,7 @@ func TestPluginWithNoManifest(t *testing.T) { } mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { - if r.Method != "POST" { + if r.Method != http.MethodPost { t.Fatalf("Expected POST, got %s\n", r.Method) } @@ -132,7 +132,7 @@ func TestGetAll(t *testing.T) { "Addr": "https://example.com/docker/plugin" }` - if err := ioutil.WriteFile(p, []byte(spec), 0644); err != nil { + if err := os.WriteFile(p, []byte(spec), 0644); err != nil { t.Fatal(err) } diff --git a/pkg/plugins/pluginrpc-gen/main.go b/pkg/plugins/pluginrpc-gen/main.go index e77a7d45ff7ce..cd39588340694 100644 --- a/pkg/plugins/pluginrpc-gen/main.go +++ b/pkg/plugins/pluginrpc-gen/main.go @@ -5,7 +5,6 @@ import ( "flag" "fmt" "go/format" - "io/ioutil" "os" "unicode" "unicode/utf8" @@ -79,7 +78,7 @@ func main() { errorOut("parser error", generatedTempl.Execute(&buf, analysis)) src, err := format.Source(buf.Bytes()) errorOut("error formatting generated source:\n"+buf.String(), err) - errorOut("error writing file", ioutil.WriteFile(*outputFile, src, 0644)) + errorOut("error writing file", os.WriteFile(*outputFile, src, 0644)) } func toLower(s string) string { diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index 28c06ff6930e2..86023046faa04 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -24,6 +24,7 @@ package plugins // import "github.com/docker/docker/pkg/plugins" import ( "errors" + "fmt" "sync" "time" @@ -254,6 +255,9 @@ func get(name string) (*Plugin, error) { // Get returns the plugin given the specified name and requested implementation. func Get(name, imp string) (*Plugin, error) { + if name == "" { + return nil, errors.New("Unable to find plugin without name") + } pl, err := get(name) if err != nil { return nil, err @@ -262,7 +266,7 @@ func Get(name, imp string) (*Plugin, error) { logrus.Debugf("%s implements: %s", name, imp) return pl, nil } - return nil, ErrNotImplements + return nil, fmt.Errorf("%w: plugin=%q, requested implementation=%q", ErrNotImplements, name, imp) } // Handle adds the specified function to the extpointHandlers. diff --git a/pkg/plugins/plugins_unix.go b/pkg/plugins/plugins_unix.go index cdfbe9345827a..23e9d5715a58b 100644 --- a/pkg/plugins/plugins_unix.go +++ b/pkg/plugins/plugins_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package plugins // import "github.com/docker/docker/pkg/plugins" diff --git a/pkg/plugins/transport/http_test.go b/pkg/plugins/transport/http_test.go index 78ab23724b88d..2435fbb01d801 100644 --- a/pkg/plugins/transport/http_test.go +++ b/pkg/plugins/transport/http_test.go @@ -5,8 +5,8 @@ import ( "net/http" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestHTTPTransport(t *testing.T) { @@ -17,5 +17,5 @@ func TestHTTPTransport(t *testing.T) { if err != nil { t.Fatal(err) } - assert.Check(t, is.Equal("POST", request.Method)) + assert.Check(t, is.Equal(http.MethodPost, request.Method)) } diff --git a/pkg/plugins/transport/transport.go b/pkg/plugins/transport/transport.go index 9cb13335a8b80..6c66cad66221d 100644 --- a/pkg/plugins/transport/transport.go +++ b/pkg/plugins/transport/transport.go @@ -27,7 +27,7 @@ func newHTTPRequest(path string, data io.Reader) (*http.Request, error) { if !strings.HasPrefix(path, "/") { path = "/" + path } - req, err := http.NewRequest("POST", path, data) + req, err := http.NewRequest(http.MethodPost, path, data) if err != nil { return nil, err } diff --git a/pkg/plugins/utils.go b/pkg/plugins/utils.go new file mode 100644 index 0000000000000..bd2344ff2016f --- /dev/null +++ b/pkg/plugins/utils.go @@ -0,0 +1,8 @@ +//go:build go1.17 +// +build go1.17 + +package plugins + +import "io/fs" + +var fileInfoToDirEntry = fs.FileInfoToDirEntry diff --git a/pkg/plugins/utils_go1.16.go b/pkg/plugins/utils_go1.16.go new file mode 100644 index 0000000000000..ce8c21a039968 --- /dev/null +++ b/pkg/plugins/utils_go1.16.go @@ -0,0 +1,47 @@ +//go:build !go1.17 +// +build !go1.17 + +// This code is taken from https://github.com/golang/go/blob/go1.17/src/io/fs/readdir.go#L49-L77 +// and provides the io/fs.FileInfoToDirEntry() utility for go1.16. Go 1.16 and up +// provide a new implementation of ioutil.ReadDir() (in os.ReadDir()) that returns +// an os.DirEntry instead of fs.FileInfo. go1.17 added the io/fs.FileInfoToDirEntry() +// utility to allow existing uses of ReadDir() to get the old type. This utility +// is not available in go1.16, so we copied it to assist the migration to os.ReadDir(). + +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package plugins + +import "os" + +// dirInfo is a DirEntry based on a FileInfo. +type dirInfo struct { + fileInfo os.FileInfo +} + +func (di dirInfo) IsDir() bool { + return di.fileInfo.IsDir() +} + +func (di dirInfo) Type() os.FileMode { + return di.fileInfo.Mode().Type() +} + +func (di dirInfo) Info() (os.FileInfo, error) { + return di.fileInfo, nil +} + +func (di dirInfo) Name() string { + return di.fileInfo.Name() +} + +// fileInfoToDirEntry returns a DirEntry that returns information from info. +// If info is nil, fileInfoToDirEntry returns nil. +func fileInfoToDirEntry(info os.FileInfo) os.DirEntry { + if info == nil { + return nil + } + return dirInfo{fileInfo: info} +} diff --git a/pkg/pools/pools.go b/pkg/pools/pools.go index 46339c282f115..3792c67a9e454 100644 --- a/pkg/pools/pools.go +++ b/pkg/pools/pools.go @@ -62,23 +62,23 @@ type bufferPool struct { func newBufferPoolWithSize(size int) *bufferPool { return &bufferPool{ pool: sync.Pool{ - New: func() interface{} { return make([]byte, size) }, + New: func() interface{} { s := make([]byte, size); return &s }, }, } } -func (bp *bufferPool) Get() []byte { - return bp.pool.Get().([]byte) +func (bp *bufferPool) Get() *[]byte { + return bp.pool.Get().(*[]byte) } -func (bp *bufferPool) Put(b []byte) { +func (bp *bufferPool) Put(b *[]byte) { bp.pool.Put(b) } // Copy is a convenience wrapper which uses a buffer to avoid allocation in io.Copy. func Copy(dst io.Writer, src io.Reader) (written int64, err error) { buf := buffer32KPool.Get() - written, err = io.CopyBuffer(dst, src, buf) + written, err = io.CopyBuffer(dst, src, *buf) buffer32KPool.Put(buf) return } diff --git a/pkg/pools/pools_test.go b/pkg/pools/pools_test.go index 7ff01ce3d5c85..6d1ca0598a1ea 100644 --- a/pkg/pools/pools_test.go +++ b/pkg/pools/pools_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestBufioReaderPoolGetWithNoReaderShouldCreateOne(t *testing.T) { diff --git a/pkg/progress/progressreader.go b/pkg/progress/progressreader.go index 7ca07dc640af3..07450a2d7084b 100644 --- a/pkg/progress/progressreader.go +++ b/pkg/progress/progressreader.go @@ -34,7 +34,7 @@ func NewProgressReader(in io.ReadCloser, out Output, size int64, id, action stri func (p *Reader) Read(buf []byte) (n int, err error) { read, err := p.in.Read(buf) p.current += int64(read) - updateEvery := int64(1024 * 512) //512kB + updateEvery := int64(1024 * 512) // 512kB if p.size > 0 { // Update progress for every 1% read if 1% < 512kB if increment := int64(0.01 * float64(p.size)); increment < updateEvery { diff --git a/pkg/progress/progressreader_test.go b/pkg/progress/progressreader_test.go index e7081cc1f4400..0aaf398ed08d1 100644 --- a/pkg/progress/progressreader_test.go +++ b/pkg/progress/progressreader_test.go @@ -3,13 +3,12 @@ package progress // import "github.com/docker/docker/pkg/progress" import ( "bytes" "io" - "io/ioutil" "testing" ) func TestOutputOnPrematureClose(t *testing.T) { content := []byte("TESTING") - reader := ioutil.NopCloser(bytes.NewReader(content)) + reader := io.NopCloser(bytes.NewReader(content)) progressChan := make(chan Progress, 10) pr := NewProgressReader(reader, ChanOutput(progressChan), int64(len(content)), "Test", "Read") @@ -41,12 +40,12 @@ drainLoop: func TestCompleteSilently(t *testing.T) { content := []byte("TESTING") - reader := ioutil.NopCloser(bytes.NewReader(content)) + reader := io.NopCloser(bytes.NewReader(content)) progressChan := make(chan Progress, 10) pr := NewProgressReader(reader, ChanOutput(progressChan), int64(len(content)), "Test", "Read") - out, err := ioutil.ReadAll(pr) + out, err := io.ReadAll(pr) if err != nil { pr.Close() t.Fatal(err) diff --git a/pkg/pubsub/publisher.go b/pkg/pubsub/publisher.go index 76033ed9e47a9..e53d17d515ae1 100644 --- a/pkg/pubsub/publisher.go +++ b/pkg/pubsub/publisher.go @@ -66,8 +66,11 @@ func (p *Publisher) SubscribeTopicWithBuffer(topic topicFunc, buffer int) chan i // Evict removes the specified subscriber from receiving any more messages. func (p *Publisher) Evict(sub chan interface{}) { p.m.Lock() - delete(p.subscribers, sub) - close(sub) + _, exists := p.subscribers[sub] + if exists { + delete(p.subscribers, sub) + close(sub) + } p.m.Unlock() } @@ -107,9 +110,12 @@ func (p *Publisher) sendTopic(sub subscriber, topic topicFunc, v interface{}, wg // send under a select as to not block if the receiver is unavailable if p.timeout > 0 { + timeout := time.NewTimer(p.timeout) + defer timeout.Stop() + select { case sub <- v: - case <-time.After(p.timeout): + case <-timeout.C: } return } diff --git a/pkg/reexec/command_unix.go b/pkg/reexec/command_unix.go index ceaabbdeee913..b90043052e330 100644 --- a/pkg/reexec/command_unix.go +++ b/pkg/reexec/command_unix.go @@ -1,3 +1,4 @@ +//go:build freebsd || darwin // +build freebsd darwin package reexec // import "github.com/docker/docker/pkg/reexec" diff --git a/pkg/reexec/command_unsupported.go b/pkg/reexec/command_unsupported.go index e7eed24240c63..7175853a55520 100644 --- a/pkg/reexec/command_unsupported.go +++ b/pkg/reexec/command_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows && !freebsd && !darwin // +build !linux,!windows,!freebsd,!darwin package reexec // import "github.com/docker/docker/pkg/reexec" diff --git a/pkg/reexec/reexec_test.go b/pkg/reexec/reexec_test.go index 44675e7b6378b..8aea0431e31ec 100644 --- a/pkg/reexec/reexec_test.go +++ b/pkg/reexec/reexec_test.go @@ -5,7 +5,7 @@ import ( "os/exec" "testing" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func init() { diff --git a/pkg/signal/README.md b/pkg/signal/README.md deleted file mode 100644 index 2b237a5942e4b..0000000000000 --- a/pkg/signal/README.md +++ /dev/null @@ -1 +0,0 @@ -This package provides helper functions for dealing with signals across various operating systems \ No newline at end of file diff --git a/pkg/signal/signal.go b/pkg/signal/signal.go deleted file mode 100644 index 88ef7b5ea2653..0000000000000 --- a/pkg/signal/signal.go +++ /dev/null @@ -1,54 +0,0 @@ -// Package signal provides helper functions for dealing with signals across -// various operating systems. -package signal // import "github.com/docker/docker/pkg/signal" - -import ( - "fmt" - "os" - "os/signal" - "strconv" - "strings" - "syscall" -) - -// CatchAll catches all signals and relays them to the specified channel. -func CatchAll(sigc chan os.Signal) { - var handledSigs []os.Signal - for _, s := range SignalMap { - handledSigs = append(handledSigs, s) - } - signal.Notify(sigc, handledSigs...) -} - -// StopCatch stops catching the signals and closes the specified channel. -func StopCatch(sigc chan os.Signal) { - signal.Stop(sigc) - close(sigc) -} - -// ParseSignal translates a string to a valid syscall signal. -// It returns an error if the signal map doesn't include the given signal. -func ParseSignal(rawSignal string) (syscall.Signal, error) { - s, err := strconv.Atoi(rawSignal) - if err == nil { - if s == 0 { - return -1, fmt.Errorf("Invalid signal: %s", rawSignal) - } - return syscall.Signal(s), nil - } - signal, ok := SignalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")] - if !ok { - return -1, fmt.Errorf("Invalid signal: %s", rawSignal) - } - return signal, nil -} - -// ValidSignalForPlatform returns true if a signal is valid on the platform -func ValidSignalForPlatform(sig syscall.Signal) bool { - for _, v := range SignalMap { - if v == sig { - return true - } - } - return false -} diff --git a/pkg/signal/signal_deprecated.go b/pkg/signal/signal_deprecated.go new file mode 100644 index 0000000000000..9977cad94ba8e --- /dev/null +++ b/pkg/signal/signal_deprecated.go @@ -0,0 +1,55 @@ +// Package signal provides helper functions for dealing with signals across +// various operating systems. +package signal // import "github.com/docker/docker/pkg/signal" + +import ( + "github.com/docker/docker/pkg/stack" + msignal "github.com/moby/sys/signal" +) + +var ( + // DumpStacks appends the runtime stack into file in dir and returns full path + // to that file. + // Deprecated: use github.com/docker/docker/pkg/stack.Dump instead. + DumpStacks = stack.DumpToFile + + // CatchAll catches all signals and relays them to the specified channel. + // SIGURG is not handled, as it's used by the Go runtime to support + // preemptable system calls. + // Deprecated: use github.com/moby/sys/signal.CatchAll instead + CatchAll = msignal.CatchAll + + // StopCatch stops catching the signals and closes the specified channel. + // Deprecated: use github.com/moby/sys/signal.StopCatch instead + StopCatch = msignal.StopCatch + + // ParseSignal translates a string to a valid syscall signal. + // It returns an error if the signal map doesn't include the given signal. + // Deprecated: use github.com/moby/sys/signal.ParseSignal instead + ParseSignal = msignal.ParseSignal + + // ValidSignalForPlatform returns true if a signal is valid on the platform + // Deprecated: use github.com/moby/sys/signal.ValidSignalForPlatform instead + ValidSignalForPlatform = msignal.ValidSignalForPlatform + + // SignalMap is a map of signals for the current platform. + // Deprecated: use github.com/moby/sys/signal.SignalMap instead + SignalMap = msignal.SignalMap +) + +// Signals used in cli/command +const ( + // SIGCHLD is a signal sent to a process when a child process terminates, is interrupted, or resumes after being interrupted. + // Deprecated: use github.com/moby/sys/signal.SIGCHLD instead + SIGCHLD = msignal.SIGCHLD + // SIGWINCH is a signal sent to a process when its controlling terminal changes its size + // Deprecated: use github.com/moby/sys/signal.SIGWINCH instead + SIGWINCH = msignal.SIGWINCH + // SIGPIPE is a signal sent to a process when a pipe is written to before the other end is open for reading + // Deprecated: use github.com/moby/sys/signal.SIGPIPE instead + SIGPIPE = msignal.SIGPIPE + + // DefaultStopSignal has been deprecated and removed. The default value is + // now defined in github.com/docker/docker/container. Clients should omit + // the container's stop-signal field if the default should be used. +) diff --git a/pkg/signal/signal_linux_test.go b/pkg/signal/signal_linux_test.go deleted file mode 100644 index 9a021e2164319..0000000000000 --- a/pkg/signal/signal_linux_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// +build darwin linux - -package signal // import "github.com/docker/docker/pkg/signal" - -import ( - "os" - "syscall" - "testing" - "time" - - "gotest.tools/assert" - is "gotest.tools/assert/cmp" -) - -func TestCatchAll(t *testing.T) { - sigs := make(chan os.Signal, 1) - CatchAll(sigs) - defer StopCatch(sigs) - - listOfSignals := map[string]string{ - "CONT": syscall.SIGCONT.String(), - "HUP": syscall.SIGHUP.String(), - "CHLD": syscall.SIGCHLD.String(), - "ILL": syscall.SIGILL.String(), - "FPE": syscall.SIGFPE.String(), - "CLD": syscall.SIGCLD.String(), - } - - for sigStr := range listOfSignals { - signal, ok := SignalMap[sigStr] - if ok { - go func() { - time.Sleep(1 * time.Millisecond) - syscall.Kill(syscall.Getpid(), signal) - }() - - s := <-sigs - assert.Check(t, is.Equal(s.String(), signal.String())) - } - - } -} - -func TestStopCatch(t *testing.T) { - signal := SignalMap["HUP"] - channel := make(chan os.Signal, 1) - CatchAll(channel) - go func() { - - time.Sleep(1 * time.Millisecond) - syscall.Kill(syscall.Getpid(), signal) - }() - signalString := <-channel - assert.Check(t, is.Equal(signalString.String(), signal.String())) - - StopCatch(channel) - _, ok := <-channel - assert.Check(t, is.Equal(ok, false)) -} diff --git a/pkg/signal/signal_test.go b/pkg/signal/signal_test.go deleted file mode 100644 index 0bfcf6ce448dd..0000000000000 --- a/pkg/signal/signal_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package signal // import "github.com/docker/docker/pkg/signal" - -import ( - "syscall" - "testing" - - "gotest.tools/assert" - is "gotest.tools/assert/cmp" -) - -func TestParseSignal(t *testing.T) { - _, checkAtoiError := ParseSignal("0") - assert.Check(t, is.Error(checkAtoiError, "Invalid signal: 0")) - - _, error := ParseSignal("SIG") - assert.Check(t, is.Error(error, "Invalid signal: SIG")) - - for sigStr := range SignalMap { - responseSignal, error := ParseSignal(sigStr) - assert.Check(t, error) - signal := SignalMap[sigStr] - assert.Check(t, is.DeepEqual(signal, responseSignal)) - } -} - -func TestValidSignalForPlatform(t *testing.T) { - isValidSignal := ValidSignalForPlatform(syscall.Signal(0)) - assert.Check(t, is.Equal(false, isValidSignal)) - - for _, sigN := range SignalMap { - isValidSignal = ValidSignalForPlatform(syscall.Signal(sigN)) - assert.Check(t, is.Equal(true, isValidSignal)) - } -} diff --git a/pkg/signal/signal_unsupported.go b/pkg/signal/signal_unsupported.go deleted file mode 100644 index 1fd25a83c6334..0000000000000 --- a/pkg/signal/signal_unsupported.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build !linux,!darwin,!freebsd,!windows - -package signal // import "github.com/docker/docker/pkg/signal" - -import ( - "syscall" -) - -// SignalMap is an empty map of signals for unsupported platform. -var SignalMap = map[string]syscall.Signal{} diff --git a/pkg/signal/signal_windows.go b/pkg/signal/signal_windows.go deleted file mode 100644 index 65752f24aaef8..0000000000000 --- a/pkg/signal/signal_windows.go +++ /dev/null @@ -1,26 +0,0 @@ -package signal // import "github.com/docker/docker/pkg/signal" - -import ( - "syscall" -) - -// Signals used in cli/command (no windows equivalent, use -// invalid signals so they don't get handled) -const ( - SIGCHLD = syscall.Signal(0xff) - SIGWINCH = syscall.Signal(0xff) - SIGPIPE = syscall.Signal(0xff) - // DefaultStopSignal is the syscall signal used to stop a container in windows systems. - DefaultStopSignal = "15" -) - -// SignalMap is a map of "supported" signals. As per the comment in GOLang's -// ztypes_windows.go: "More invented values for signals". Windows doesn't -// really support signals in any way, shape or form that Unix does. -// -// We have these so that docker kill can be used to gracefully (TERM) and -// forcibly (KILL) terminate a container on Windows. -var SignalMap = map[string]syscall.Signal{ - "KILL": syscall.SIGKILL, - "TERM": syscall.SIGTERM, -} diff --git a/pkg/signal/testfiles/main.go b/pkg/signal/testfiles/main.go deleted file mode 100644 index e56854c7c3bcc..0000000000000 --- a/pkg/signal/testfiles/main.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "os" - "syscall" - "time" - - "github.com/docker/docker/pkg/signal" - "github.com/sirupsen/logrus" -) - -func main() { - sigmap := map[string]os.Signal{ - "TERM": syscall.SIGTERM, - "QUIT": syscall.SIGQUIT, - "INT": os.Interrupt, - } - signal.Trap(func() { - time.Sleep(time.Second) - os.Exit(99) - }, logrus.StandardLogger()) - go func() { - p, err := os.FindProcess(os.Getpid()) - if err != nil { - panic(err) - } - s := os.Getenv("SIGNAL_TYPE") - multiple := os.Getenv("IF_MULTIPLE") - switch s { - case "TERM", "INT": - if multiple == "1" { - for { - p.Signal(sigmap[s]) - } - } else { - p.Signal(sigmap[s]) - } - case "QUIT": - p.Signal(sigmap[s]) - } - }() - time.Sleep(2 * time.Second) -} diff --git a/pkg/signal/trap.go b/pkg/signal/trap.go deleted file mode 100644 index 2a6e69fb50773..0000000000000 --- a/pkg/signal/trap.go +++ /dev/null @@ -1,104 +0,0 @@ -package signal // import "github.com/docker/docker/pkg/signal" - -import ( - "fmt" - "os" - gosignal "os/signal" - "path/filepath" - "runtime" - "strings" - "sync/atomic" - "syscall" - "time" - - "github.com/pkg/errors" -) - -// Trap sets up a simplified signal "trap", appropriate for common -// behavior expected from a vanilla unix command-line tool in general -// (and the Docker engine in particular). -// -// * If SIGINT or SIGTERM are received, `cleanup` is called, then the process is terminated. -// * If SIGINT or SIGTERM are received 3 times before cleanup is complete, then cleanup is -// skipped and the process is terminated immediately (allows force quit of stuck daemon) -// * A SIGQUIT always causes an exit without cleanup, with a goroutine dump preceding exit. -// * Ignore SIGPIPE events. These are generated by systemd when journald is restarted while -// the docker daemon is not restarted and also running under systemd. -// Fixes https://github.com/docker/docker/issues/19728 -// -func Trap(cleanup func(), logger interface { - Info(args ...interface{}) -}) { - c := make(chan os.Signal, 1) - // we will handle INT, TERM, QUIT, SIGPIPE here - signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGPIPE} - gosignal.Notify(c, signals...) - go func() { - interruptCount := uint32(0) - for sig := range c { - if sig == syscall.SIGPIPE { - continue - } - - go func(sig os.Signal) { - logger.Info(fmt.Sprintf("Processing signal '%v'", sig)) - switch sig { - case os.Interrupt, syscall.SIGTERM: - if atomic.LoadUint32(&interruptCount) < 3 { - // Initiate the cleanup only once - if atomic.AddUint32(&interruptCount, 1) == 1 { - // Call the provided cleanup handler - cleanup() - os.Exit(0) - } else { - return - } - } else { - // 3 SIGTERM/INT signals received; force exit without cleanup - logger.Info("Forcing docker daemon shutdown without cleanup; 3 interrupts received") - } - case syscall.SIGQUIT: - DumpStacks("") - logger.Info("Forcing docker daemon shutdown without cleanup on SIGQUIT") - } - //for the SIGINT/TERM, and SIGQUIT non-clean shutdown case, exit with 128 + signal # - os.Exit(128 + int(sig.(syscall.Signal))) - }(sig) - } - }() -} - -const stacksLogNameTemplate = "goroutine-stacks-%s.log" - -// DumpStacks appends the runtime stack into file in dir and returns full path -// to that file. -func DumpStacks(dir string) (string, error) { - var ( - buf []byte - stackSize int - ) - bufferLen := 16384 - for stackSize == len(buf) { - buf = make([]byte, bufferLen) - stackSize = runtime.Stack(buf, true) - bufferLen *= 2 - } - buf = buf[:stackSize] - var f *os.File - if dir != "" { - path := filepath.Join(dir, fmt.Sprintf(stacksLogNameTemplate, strings.Replace(time.Now().Format(time.RFC3339), ":", "", -1))) - var err error - f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666) - if err != nil { - return "", errors.Wrap(err, "failed to open file to write the goroutine stacks") - } - defer f.Close() - defer f.Sync() - } else { - f = os.Stderr - } - if _, err := f.Write(buf); err != nil { - return "", errors.Wrap(err, "failed to write goroutine stacks") - } - return f.Name(), nil -} diff --git a/pkg/signal/trap_linux_test.go b/pkg/signal/trap_linux_test.go deleted file mode 100644 index 14d1543117b15..0000000000000 --- a/pkg/signal/trap_linux_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// +build linux - -package signal // import "github.com/docker/docker/pkg/signal" - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "syscall" - "testing" - - "gotest.tools/assert" - is "gotest.tools/assert/cmp" -) - -func buildTestBinary(t *testing.T, tmpdir string, prefix string) (string, string) { - tmpDir, err := ioutil.TempDir(tmpdir, prefix) - assert.NilError(t, err) - exePath := tmpDir + "/" + prefix - wd, _ := os.Getwd() - testHelperCode := wd + "/testfiles/main.go" - cmd := exec.Command("go", "build", "-o", exePath, testHelperCode) - err = cmd.Run() - assert.NilError(t, err) - return exePath, tmpDir -} - -func TestTrap(t *testing.T) { - var sigmap = []struct { - name string - signal os.Signal - multiple bool - }{ - {"TERM", syscall.SIGTERM, false}, - {"QUIT", syscall.SIGQUIT, true}, - {"INT", os.Interrupt, false}, - {"TERM", syscall.SIGTERM, true}, - {"INT", os.Interrupt, true}, - } - exePath, tmpDir := buildTestBinary(t, "", "main") - defer os.RemoveAll(tmpDir) - - for _, v := range sigmap { - cmd := exec.Command(exePath) - cmd.Env = append(os.Environ(), fmt.Sprintf("SIGNAL_TYPE=%s", v.name)) - if v.multiple { - cmd.Env = append(cmd.Env, "IF_MULTIPLE=1") - } - err := cmd.Start() - assert.NilError(t, err) - err = cmd.Wait() - if e, ok := err.(*exec.ExitError); ok { - code := e.Sys().(syscall.WaitStatus).ExitStatus() - if v.multiple { - assert.Check(t, is.DeepEqual(128+int(v.signal.(syscall.Signal)), code)) - } else { - assert.Check(t, is.Equal(99, code)) - } - continue - } - t.Fatal("process didn't end with any error") - } - -} - -func TestDumpStacks(t *testing.T) { - directory, err := ioutil.TempDir("", "test-dump-tasks") - assert.Check(t, err) - defer os.RemoveAll(directory) - dumpPath, err := DumpStacks(directory) - assert.Check(t, err) - readFile, _ := ioutil.ReadFile(dumpPath) - fileData := string(readFile) - assert.Check(t, is.Contains(fileData, "goroutine")) -} - -func TestDumpStacksWithEmptyInput(t *testing.T) { - path, err := DumpStacks("") - assert.Check(t, err) - assert.Check(t, is.Equal(os.Stderr.Name(), path)) -} diff --git a/pkg/stack/stackdump.go b/pkg/stack/stackdump.go new file mode 100644 index 0000000000000..f65c186c1421f --- /dev/null +++ b/pkg/stack/stackdump.go @@ -0,0 +1,57 @@ +package stack // import "github.com/docker/docker/pkg/stack" + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/pkg/errors" +) + +const stacksLogNameTemplate = "goroutine-stacks-%s.log" + +// Dump outputs the runtime stack to os.StdErr. +func Dump() { + _ = dump(os.Stderr) +} + +// DumpToFile appends the runtime stack into a file named "goroutine-stacks-.log" +// in dir and returns the full path to that file. If no directory name is +// provided, it outputs to os.Stderr. +func DumpToFile(dir string) (string, error) { + var f *os.File + if dir != "" { + path := filepath.Join(dir, fmt.Sprintf(stacksLogNameTemplate, strings.Replace(time.Now().Format(time.RFC3339), ":", "", -1))) + var err error + f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + return "", errors.Wrap(err, "failed to open file to write the goroutine stacks") + } + defer f.Close() + defer f.Sync() + } else { + f = os.Stderr + } + return f.Name(), dump(f) +} + +func dump(f *os.File) error { + var ( + buf []byte + stackSize int + ) + bufferLen := 16384 + for stackSize == len(buf) { + buf = make([]byte, bufferLen) + stackSize = runtime.Stack(buf, true) + bufferLen *= 2 + } + buf = buf[:stackSize] + if _, err := f.Write(buf); err != nil { + return errors.Wrap(err, "failed to write goroutine stacks") + } + return nil +} diff --git a/pkg/stack/stackdump_test.go b/pkg/stack/stackdump_test.go new file mode 100644 index 0000000000000..b40c560e7e8e2 --- /dev/null +++ b/pkg/stack/stackdump_test.go @@ -0,0 +1,30 @@ +package stack // import "github.com/docker/docker/pkg/stack" + +import ( + "os" + "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestDump(t *testing.T) { + Dump() +} + +func TestDumpToFile(t *testing.T) { + directory, err := os.MkdirTemp("", "test-dump-tasks") + assert.Check(t, err) + defer os.RemoveAll(directory) + dumpPath, err := DumpToFile(directory) + assert.Check(t, err) + readFile, _ := os.ReadFile(dumpPath) + fileData := string(readFile) + assert.Check(t, is.Contains(fileData, "goroutine")) +} + +func TestDumpToFileWithEmptyInput(t *testing.T) { + path, err := DumpToFile("") + assert.Check(t, err) + assert.Check(t, is.Equal(os.Stderr.Name(), path)) +} diff --git a/pkg/stdcopy/stdcopy_test.go b/pkg/stdcopy/stdcopy_test.go index 63edb855e5779..1fe8e83fdfaf7 100644 --- a/pkg/stdcopy/stdcopy_test.go +++ b/pkg/stdcopy/stdcopy_test.go @@ -4,13 +4,12 @@ import ( "bytes" "errors" "io" - "io/ioutil" "strings" "testing" ) func TestNewStdWriter(t *testing.T) { - writer := NewStdWriter(ioutil.Discard, Stdout) + writer := NewStdWriter(io.Discard, Stdout) if writer == nil { t.Fatalf("NewStdWriter with an invalid StdType should not return nil.") } @@ -28,7 +27,7 @@ func TestWriteWithUninitializedStdWriter(t *testing.T) { } func TestWriteWithNilBytes(t *testing.T) { - writer := NewStdWriter(ioutil.Discard, Stdout) + writer := NewStdWriter(io.Discard, Stdout) n, err := writer.Write(nil) if err != nil { t.Fatalf("Shouldn't have fail when given no data") @@ -39,7 +38,7 @@ func TestWriteWithNilBytes(t *testing.T) { } func TestWrite(t *testing.T) { - writer := NewStdWriter(ioutil.Discard, Stdout) + writer := NewStdWriter(io.Discard, Stdout) data := []byte("Test StdWrite.Write") n, err := writer.Write(data) if err != nil { @@ -104,7 +103,7 @@ func TestStdCopyWriteAndRead(t *testing.T) { if err != nil { t.Fatal(err) } - written, err := StdCopy(ioutil.Discard, ioutil.Discard, buffer) + written, err := StdCopy(io.Discard, io.Discard, buffer) if err != nil { t.Fatal(err) } @@ -134,7 +133,7 @@ func TestStdCopyReturnsErrorReadingHeader(t *testing.T) { expectedError := errors.New("error") reader := &customReader{ err: expectedError} - written, err := StdCopy(ioutil.Discard, ioutil.Discard, reader) + written, err := StdCopy(io.Discard, io.Discard, reader) if written != 0 { t.Fatalf("Expected 0 bytes read, got %d", written) } @@ -156,7 +155,7 @@ func TestStdCopyReturnsErrorReadingFrame(t *testing.T) { n: stdWriterPrefixLen + 1, err: expectedError, src: buffer} - written, err := StdCopy(ioutil.Discard, ioutil.Discard, reader) + written, err := StdCopy(io.Discard, io.Discard, reader) if written != 0 { t.Fatalf("Expected 0 bytes read, got %d", written) } @@ -177,7 +176,7 @@ func TestStdCopyDetectsCorruptedFrame(t *testing.T) { n: stdWriterPrefixLen + 1, err: io.EOF, src: buffer} - written, err := StdCopy(ioutil.Discard, ioutil.Discard, reader) + written, err := StdCopy(io.Discard, io.Discard, reader) if written != startingBufLen { t.Fatalf("Expected %d bytes read, got %d", startingBufLen, written) } @@ -187,8 +186,8 @@ func TestStdCopyDetectsCorruptedFrame(t *testing.T) { } func TestStdCopyWithInvalidInputHeader(t *testing.T) { - dstOut := NewStdWriter(ioutil.Discard, Stdout) - dstErr := NewStdWriter(ioutil.Discard, Stderr) + dstOut := NewStdWriter(io.Discard, Stdout) + dstErr := NewStdWriter(io.Discard, Stderr) src := strings.NewReader("Invalid input") _, err := StdCopy(dstOut, dstErr, src) if err == nil { @@ -219,7 +218,7 @@ func TestStdCopyReturnsWriteErrors(t *testing.T) { dstOut := &errWriter{err: expectedError} - written, err := StdCopy(dstOut, ioutil.Discard, buffer) + written, err := StdCopy(dstOut, io.Discard, buffer) if written != 0 { t.Fatalf("StdCopy should have written 0, but has written %d", written) } @@ -237,7 +236,7 @@ func TestStdCopyDetectsNotFullyWrittenFrames(t *testing.T) { } dstOut := &errWriter{n: startingBufLen - 10} - written, err := StdCopy(dstOut, ioutil.Discard, buffer) + written, err := StdCopy(dstOut, io.Discard, buffer) if written != 0 { t.Fatalf("StdCopy should have return 0 written bytes, but returned %d", written) } @@ -266,7 +265,7 @@ func TestStdCopyReturnsErrorFromSystem(t *testing.T) { // now copy and demux. we should expect an error containing the string we // wrote out - _, err = StdCopy(ioutil.Discard, ioutil.Discard, buffer) + _, err = StdCopy(io.Discard, io.Discard, buffer) if err == nil { t.Fatal("expected error, got none") } @@ -276,7 +275,7 @@ func TestStdCopyReturnsErrorFromSystem(t *testing.T) { } func BenchmarkWrite(b *testing.B) { - w := NewStdWriter(ioutil.Discard, Stdout) + w := NewStdWriter(io.Discard, Stdout) data := []byte("Test line for testing stdwriter performance\n") data = bytes.Repeat(data, 100) b.SetBytes(int64(len(data))) diff --git a/pkg/streamformatter/streamformatter_test.go b/pkg/streamformatter/streamformatter_test.go index f630699d73e11..85a80c61ceb13 100644 --- a/pkg/streamformatter/streamformatter_test.go +++ b/pkg/streamformatter/streamformatter_test.go @@ -10,8 +10,8 @@ import ( "github.com/docker/docker/pkg/jsonmessage" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestRawProgressFormatterFormatStatus(t *testing.T) { diff --git a/pkg/streamformatter/streamwriter_test.go b/pkg/streamformatter/streamwriter_test.go index 5b679f2cf4822..f8a37c25ae43b 100644 --- a/pkg/streamformatter/streamwriter_test.go +++ b/pkg/streamformatter/streamwriter_test.go @@ -4,8 +4,8 @@ import ( "bytes" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestStreamWriterStdout(t *testing.T) { diff --git a/pkg/stringid/stringid.go b/pkg/stringid/stringid.go index fa7d9166eb3e6..5fe071d6284ef 100644 --- a/pkg/stringid/stringid.go +++ b/pkg/stringid/stringid.go @@ -2,17 +2,12 @@ package stringid // import "github.com/docker/docker/pkg/stringid" import ( - cryptorand "crypto/rand" + "crypto/rand" "encoding/hex" "fmt" - "io" - "math" - "math/big" - "math/rand" "regexp" "strconv" "strings" - "time" ) const shortLen = 12 @@ -41,10 +36,11 @@ func TruncateID(id string) string { return id } -func generateID(r io.Reader) string { +// GenerateRandomID returns a unique id. +func GenerateRandomID() string { b := make([]byte, 32) for { - if _, err := io.ReadFull(r, b); err != nil { + if _, err := rand.Read(b); err != nil { panic(err) // This shouldn't happen } id := hex.EncodeToString(b) @@ -58,18 +54,6 @@ func generateID(r io.Reader) string { } } -// GenerateRandomID returns a unique id. -func GenerateRandomID() string { - return generateID(cryptorand.Reader) -} - -// GenerateNonCryptoID generates unique id without using cryptographically -// secure sources of random. -// It helps you to save entropy. -func GenerateNonCryptoID() string { - return generateID(readerFunc(rand.Read)) -} - // ValidateID checks whether an ID string is a valid image ID. func ValidateID(id string) error { if ok := validHex.MatchString(id); !ok { @@ -77,23 +61,3 @@ func ValidateID(id string) error { } return nil } - -func init() { - // safely set the seed globally so we generate random ids. Tries to use a - // crypto seed before falling back to time. - var seed int64 - if cryptoseed, err := cryptorand.Int(cryptorand.Reader, big.NewInt(math.MaxInt64)); err != nil { - // This should not happen, but worst-case fallback to time-based seed. - seed = time.Now().UnixNano() - } else { - seed = cryptoseed.Int64() - } - - rand.Seed(seed) -} - -type readerFunc func(p []byte) (int, error) - -func (fn readerFunc) Read(p []byte) (int, error) { - return fn(p) -} diff --git a/pkg/stringid/stringid_test.go b/pkg/stringid/stringid_test.go index a7ccd5faaef20..2660d2e65f4c0 100644 --- a/pkg/stringid/stringid_test.go +++ b/pkg/stringid/stringid_test.go @@ -13,14 +13,6 @@ func TestGenerateRandomID(t *testing.T) { } } -func TestGenerateNonCryptoID(t *testing.T) { - id := GenerateNonCryptoID() - - if len(id) != 64 { - t.Fatalf("Id returned is incorrect: %s", id) - } -} - func TestShortenId(t *testing.T) { id := "90435eec5c4e124e741ef731e118be2fc799a68aba0466ec17717f24ce2ae6a2" truncID := TruncateID(id) diff --git a/pkg/symlink/deprecated.go b/pkg/symlink/deprecated.go new file mode 100644 index 0000000000000..c4f12ef2181e4 --- /dev/null +++ b/pkg/symlink/deprecated.go @@ -0,0 +1,12 @@ +package symlink // import "github.com/docker/docker/pkg/symlink" + +import "github.com/moby/sys/symlink" + +var ( + // EvalSymlinks is deprecated and moved to github.com/moby/sys/symlink + // Deprecated: use github.com/moby/sys/symlink.EvalSymlinks instead + EvalSymlinks = symlink.EvalSymlinks + // FollowSymlinkInScope is deprecated and moved to github.com/moby/sys/symlink + // Deprecated: use github.com/moby/sys/symlink.FollowSymlinkInScope instead + FollowSymlinkInScope = symlink.FollowSymlinkInScope +) diff --git a/pkg/symlink/fs.go b/pkg/symlink/fs.go deleted file mode 100644 index 7b894cde73274..0000000000000 --- a/pkg/symlink/fs.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE.BSD file. - -// This code is a modified version of path/filepath/symlink.go from the Go standard library. - -package symlink // import "github.com/docker/docker/pkg/symlink" - -import ( - "bytes" - "errors" - "os" - "path/filepath" - "strings" - - "github.com/docker/docker/pkg/system" -) - -// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an -// absolute path. This function handles paths in a platform-agnostic manner. -func FollowSymlinkInScope(path, root string) (string, error) { - path, err := filepath.Abs(filepath.FromSlash(path)) - if err != nil { - return "", err - } - root, err = filepath.Abs(filepath.FromSlash(root)) - if err != nil { - return "", err - } - return evalSymlinksInScope(path, root) -} - -// evalSymlinksInScope will evaluate symlinks in `path` within a scope `root` and return -// a result guaranteed to be contained within the scope `root`, at the time of the call. -// Symlinks in `root` are not evaluated and left as-is. -// Errors encountered while attempting to evaluate symlinks in path will be returned. -// Non-existing paths are valid and do not constitute an error. -// `path` has to contain `root` as a prefix, or else an error will be returned. -// Trying to break out from `root` does not constitute an error. -// -// Example: -// If /foo/bar -> /outside, -// FollowSymlinkInScope("/foo/bar", "/foo") == "/foo/outside" instead of "/outside" -// -// IMPORTANT: it is the caller's responsibility to call evalSymlinksInScope *after* relevant symlinks -// are created and not to create subsequently, additional symlinks that could potentially make a -// previously-safe path, unsafe. Example: if /foo/bar does not exist, evalSymlinksInScope("/foo/bar", "/foo") -// would return "/foo/bar". If one makes /foo/bar a symlink to /baz subsequently, then "/foo/bar" should -// no longer be considered safely contained in "/foo". -func evalSymlinksInScope(path, root string) (string, error) { - root = filepath.Clean(root) - if path == root { - return path, nil - } - if !strings.HasPrefix(path, root) { - return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root) - } - const maxIter = 255 - originalPath := path - // given root of "/a" and path of "/a/b/../../c" we want path to be "/b/../../c" - path = path[len(root):] - if root == string(filepath.Separator) { - path = string(filepath.Separator) + path - } - if !strings.HasPrefix(path, string(filepath.Separator)) { - return "", errors.New("evalSymlinksInScope: " + path + " is not in " + root) - } - path = filepath.Clean(path) - // consume path by taking each frontmost path element, - // expanding it if it's a symlink, and appending it to b - var b bytes.Buffer - // b here will always be considered to be the "current absolute path inside - // root" when we append paths to it, we also append a slash and use - // filepath.Clean after the loop to trim the trailing slash - for n := 0; path != ""; n++ { - if n > maxIter { - return "", errors.New("evalSymlinksInScope: too many links in " + originalPath) - } - - // find next path component, p - i := strings.IndexRune(path, filepath.Separator) - var p string - if i == -1 { - p, path = path, "" - } else { - p, path = path[:i], path[i+1:] - } - - if p == "" { - continue - } - - // this takes a b.String() like "b/../" and a p like "c" and turns it - // into "/b/../c" which then gets filepath.Cleaned into "/c" and then - // root gets prepended and we Clean again (to remove any trailing slash - // if the first Clean gave us just "/") - cleanP := filepath.Clean(string(filepath.Separator) + b.String() + p) - if isDriveOrRoot(cleanP) { - // never Lstat "/" itself, or drive letters on Windows - b.Reset() - continue - } - fullP := filepath.Clean(root + cleanP) - - fi, err := os.Lstat(fullP) - if os.IsNotExist(err) { - // if p does not exist, accept it - b.WriteString(p) - b.WriteRune(filepath.Separator) - continue - } - if err != nil { - return "", err - } - if fi.Mode()&os.ModeSymlink == 0 { - b.WriteString(p) - b.WriteRune(filepath.Separator) - continue - } - - // it's a symlink, put it at the front of path - dest, err := os.Readlink(fullP) - if err != nil { - return "", err - } - if system.IsAbs(dest) { - b.Reset() - } - path = dest + string(filepath.Separator) + path - } - - // see note above on "fullP := ..." for why this is double-cleaned and - // what's happening here - return filepath.Clean(root + filepath.Clean(string(filepath.Separator)+b.String())), nil -} - -// EvalSymlinks returns the path name after the evaluation of any symbolic -// links. -// If path is relative the result will be relative to the current directory, -// unless one of the components is an absolute symbolic link. -// This version has been updated to support long paths prepended with `\\?\`. -func EvalSymlinks(path string) (string, error) { - return evalSymlinks(path) -} diff --git a/pkg/symlink/fs_unix_test.go b/pkg/symlink/fs_unix_test.go deleted file mode 100644 index 9ed1dd70db262..0000000000000 --- a/pkg/symlink/fs_unix_test.go +++ /dev/null @@ -1,407 +0,0 @@ -// +build !windows - -// Licensed under the Apache License, Version 2.0; See LICENSE.APACHE - -package symlink // import "github.com/docker/docker/pkg/symlink" - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "testing" -) - -// TODO Windows: This needs some serious work to port to Windows. For now, -// turning off testing in this package. - -type dirOrLink struct { - path string - target string -} - -func makeFs(tmpdir string, fs []dirOrLink) error { - for _, s := range fs { - s.path = filepath.Join(tmpdir, s.path) - if s.target == "" { - os.MkdirAll(s.path, 0755) - continue - } - if err := os.MkdirAll(filepath.Dir(s.path), 0755); err != nil { - return err - } - if err := os.Symlink(s.target, s.path); err != nil && !os.IsExist(err) { - return err - } - } - return nil -} - -func testSymlink(tmpdir, path, expected, scope string) error { - rewrite, err := FollowSymlinkInScope(filepath.Join(tmpdir, path), filepath.Join(tmpdir, scope)) - if err != nil { - return err - } - expected, err = filepath.Abs(filepath.Join(tmpdir, expected)) - if err != nil { - return err - } - if expected != rewrite { - return fmt.Errorf("Expected %q got %q", expected, rewrite) - } - return nil -} - -func TestFollowSymlinkAbsolute(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkAbsolute") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "testdata/fs/a/d/c/data", "testdata/b/c/data", "testdata"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkRelativePath(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativePath") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/i", target: "a"}}); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "testdata/fs/i", "testdata/fs/a", "testdata"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkSkipSymlinksOutsideScope(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkSkipSymlinksOutsideScope") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - if err := makeFs(tmpdir, []dirOrLink{ - {path: "linkdir", target: "realdir"}, - {path: "linkdir/foo/bar"}, - }); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "linkdir/foo/bar", "linkdir/foo/bar", "linkdir/foo"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkInvalidScopePathPair(t *testing.T) { - if _, err := FollowSymlinkInScope("toto", "testdata"); err == nil { - t.Fatal("expected an error") - } -} - -func TestFollowSymlinkLastLink(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkLastLink") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "testdata/fs/a/d", "testdata/b", "testdata"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkRelativeLinkChangeScope(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativeLinkChangeScope") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/e", target: "../b"}}); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "testdata/fs/a/e/c/data", "testdata/fs/b/c/data", "testdata"); err != nil { - t.Fatal(err) - } - // avoid letting allowing symlink e lead us to ../b - // normalize to the "testdata/fs/a" - if err := testSymlink(tmpdir, "testdata/fs/a/e", "testdata/fs/a/b", "testdata/fs/a"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkDeepRelativeLinkChangeScope(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkDeepRelativeLinkChangeScope") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/f", target: "../../../../test"}}); err != nil { - t.Fatal(err) - } - // avoid letting symlink f lead us out of the "testdata" scope - // we don't normalize because symlink f is in scope and there is no - // information leak - if err := testSymlink(tmpdir, "testdata/fs/a/f", "testdata/test", "testdata"); err != nil { - t.Fatal(err) - } - // avoid letting symlink f lead us out of the "testdata/fs" scope - // we don't normalize because symlink f is in scope and there is no - // information leak - if err := testSymlink(tmpdir, "testdata/fs/a/f", "testdata/fs/test", "testdata/fs"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkRelativeLinkChain(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativeLinkChain") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - // avoid letting symlink g (pointed at by symlink h) take out of scope - // TODO: we should probably normalize to scope here because ../[....]/root - // is out of scope and we leak information - if err := makeFs(tmpdir, []dirOrLink{ - {path: "testdata/fs/b/h", target: "../g"}, - {path: "testdata/fs/g", target: "../../../../../../../../../../../../root"}, - }); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "testdata/fs/b/h", "testdata/root", "testdata"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkBreakoutPath(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkBreakoutPath") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - // avoid letting symlink -> ../directory/file escape from scope - // normalize to "testdata/fs/j" - if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/j/k", target: "../i/a"}}); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "testdata/fs/j/k", "testdata/fs/j/i/a", "testdata/fs/j"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkToRoot(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkToRoot") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - // make sure we don't allow escaping to / - // normalize to dir - if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "/"}}); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "foo", "", ""); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkSlashDotdot(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkSlashDotdot") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - tmpdir = filepath.Join(tmpdir, "dir", "subdir") - - // make sure we don't allow escaping to / - // normalize to dir - if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "/../../"}}); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "foo", "", ""); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkDotdot(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkDotdot") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - tmpdir = filepath.Join(tmpdir, "dir", "subdir") - - // make sure we stay in scope without leaking information - // this also checks for escaping to / - // normalize to dir - if err := makeFs(tmpdir, []dirOrLink{{path: "foo", target: "../../"}}); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "foo", "", ""); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkRelativePath2(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRelativePath2") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - if err := makeFs(tmpdir, []dirOrLink{{path: "bar/foo", target: "baz/target"}}); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "bar/foo", "bar/baz/target", ""); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkScopeLink(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkScopeLink") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - if err := makeFs(tmpdir, []dirOrLink{ - {path: "root2"}, - {path: "root", target: "root2"}, - {path: "root2/foo", target: "../bar"}, - }); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "root/foo", "root/bar", "root"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkRootScope(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkRootScope") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - expected, err := filepath.EvalSymlinks(tmpdir) - if err != nil { - t.Fatal(err) - } - rewrite, err := FollowSymlinkInScope(tmpdir, "/") - if err != nil { - t.Fatal(err) - } - if rewrite != expected { - t.Fatalf("expected %q got %q", expected, rewrite) - } -} - -func TestFollowSymlinkEmpty(t *testing.T) { - res, err := FollowSymlinkInScope("", "") - if err != nil { - t.Fatal(err) - } - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - if res != wd { - t.Fatalf("expected %q got %q", wd, res) - } -} - -func TestFollowSymlinkCircular(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkCircular") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - if err := makeFs(tmpdir, []dirOrLink{{path: "root/foo", target: "foo"}}); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "root/foo", "", "root"); err == nil { - t.Fatal("expected an error for foo -> foo") - } - - if err := makeFs(tmpdir, []dirOrLink{ - {path: "root/bar", target: "baz"}, - {path: "root/baz", target: "../bak"}, - {path: "root/bak", target: "/bar"}, - }); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "root/foo", "", "root"); err == nil { - t.Fatal("expected an error for bar -> baz -> bak -> bar") - } -} - -func TestFollowSymlinkComplexChainWithTargetPathsContainingLinks(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkComplexChainWithTargetPathsContainingLinks") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - if err := makeFs(tmpdir, []dirOrLink{ - {path: "root2"}, - {path: "root", target: "root2"}, - {path: "root/a", target: "r/s"}, - {path: "root/r", target: "../root/t"}, - {path: "root/root/t/s/b", target: "/../u"}, - {path: "root/u/c", target: "."}, - {path: "root/u/x/y", target: "../v"}, - {path: "root/u/v", target: "/../w"}, - }); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "root/a/b/c/x/y/z", "root/w/z", "root"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkBreakoutNonExistent(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkBreakoutNonExistent") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - if err := makeFs(tmpdir, []dirOrLink{ - {path: "root/slash", target: "/"}, - {path: "root/sym", target: "/idontexist/../slash"}, - }); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "root/sym/file", "root/file", "root"); err != nil { - t.Fatal(err) - } -} - -func TestFollowSymlinkNoLexicalCleaning(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "TestFollowSymlinkNoLexicalCleaning") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpdir) - - if err := makeFs(tmpdir, []dirOrLink{ - {path: "root/sym", target: "/foo/bar"}, - {path: "root/hello", target: "/sym/../baz"}, - }); err != nil { - t.Fatal(err) - } - if err := testSymlink(tmpdir, "root/hello", "root/foo/baz", "root"); err != nil { - t.Fatal(err) - } -} diff --git a/pkg/sysinfo/cgroup2_linux.go b/pkg/sysinfo/cgroup2_linux.go new file mode 100644 index 0000000000000..1b8a7443efc6a --- /dev/null +++ b/pkg/sysinfo/cgroup2_linux.go @@ -0,0 +1,145 @@ +package sysinfo // import "github.com/docker/docker/pkg/sysinfo" + +import ( + "os" + "path" + "strings" + + cgroupsV2 "github.com/containerd/cgroups/v2" + "github.com/containerd/containerd/pkg/userns" + "github.com/opencontainers/runc/libcontainer/cgroups" + "github.com/sirupsen/logrus" +) + +func newV2(options ...Opt) *SysInfo { + sysInfo := &SysInfo{ + CgroupUnified: true, + cg2GroupPath: "/", + } + for _, o := range options { + o(sysInfo) + } + + ops := []infoCollector{ + applyNetworkingInfo, + applyAppArmorInfo, + applySeccompInfo, + applyCgroupNsInfo, + } + + m, err := cgroupsV2.LoadManager("/sys/fs/cgroup", sysInfo.cg2GroupPath) + if err != nil { + logrus.Warn(err) + } else { + sysInfo.cg2Controllers = make(map[string]struct{}) + controllers, err := m.Controllers() + if err != nil { + logrus.Warn(err) + } + for _, c := range controllers { + sysInfo.cg2Controllers[c] = struct{}{} + } + ops = append(ops, + applyMemoryCgroupInfoV2, + applyCPUCgroupInfoV2, + applyIOCgroupInfoV2, + applyCPUSetCgroupInfoV2, + applyPIDSCgroupInfoV2, + applyDevicesCgroupInfoV2, + ) + } + + for _, o := range ops { + o(sysInfo) + } + return sysInfo +} + +func getSwapLimitV2() bool { + groups, err := cgroups.ParseCgroupFile("/proc/self/cgroup") + if err != nil { + return false + } + + g := groups[""] + if g == "" { + return false + } + + cGroupPath := path.Join("/sys/fs/cgroup", g, "memory.swap.max") + if _, err = os.Stat(cGroupPath); os.IsNotExist(err) { + return false + } + return true +} + +func applyMemoryCgroupInfoV2(info *SysInfo) { + if _, ok := info.cg2Controllers["memory"]; !ok { + info.Warnings = append(info.Warnings, "Unable to find memory controller") + return + } + + info.MemoryLimit = true + info.SwapLimit = getSwapLimitV2() + info.MemoryReservation = true + info.OomKillDisable = false + info.MemorySwappiness = false + info.KernelMemory = false + info.KernelMemoryTCP = false +} + +func applyCPUCgroupInfoV2(info *SysInfo) { + if _, ok := info.cg2Controllers["cpu"]; !ok { + info.Warnings = append(info.Warnings, "Unable to find cpu controller") + return + } + info.CPUShares = true + info.CPUCfs = true + info.CPURealtime = false +} + +func applyIOCgroupInfoV2(info *SysInfo) { + if _, ok := info.cg2Controllers["io"]; !ok { + info.Warnings = append(info.Warnings, "Unable to find io controller") + return + } + + info.BlkioWeight = true + info.BlkioWeightDevice = true + info.BlkioReadBpsDevice = true + info.BlkioWriteBpsDevice = true + info.BlkioReadIOpsDevice = true + info.BlkioWriteIOpsDevice = true +} + +func applyCPUSetCgroupInfoV2(info *SysInfo) { + if _, ok := info.cg2Controllers["cpuset"]; !ok { + info.Warnings = append(info.Warnings, "Unable to find cpuset controller") + return + } + info.Cpuset = true + + cpus, err := os.ReadFile(path.Join("/sys/fs/cgroup", info.cg2GroupPath, "cpuset.cpus.effective")) + if err != nil { + return + } + info.Cpus = strings.TrimSpace(string(cpus)) + + mems, err := os.ReadFile(path.Join("/sys/fs/cgroup", info.cg2GroupPath, "cpuset.mems.effective")) + if err != nil { + return + } + info.Mems = strings.TrimSpace(string(mems)) +} + +func applyPIDSCgroupInfoV2(info *SysInfo) { + if _, ok := info.cg2Controllers["pids"]; !ok { + info.Warnings = append(info.Warnings, "Unable to find pids controller") + return + } + info.PidsLimit = true +} + +func applyDevicesCgroupInfoV2(info *SysInfo) { + info.CgroupDevicesEnabled = !userns.RunningInUserNS() +} diff --git a/pkg/sysinfo/numcpu.go b/pkg/sysinfo/numcpu.go index eea2d25bf94b1..5b5921dff41e3 100644 --- a/pkg/sysinfo/numcpu.go +++ b/pkg/sysinfo/numcpu.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows // +build !linux,!windows package sysinfo // import "github.com/docker/docker/pkg/sysinfo" diff --git a/pkg/sysinfo/numcpu_linux.go b/pkg/sysinfo/numcpu_linux.go index 5f6c6df8c4837..3d9d4cceb3101 100644 --- a/pkg/sysinfo/numcpu_linux.go +++ b/pkg/sysinfo/numcpu_linux.go @@ -2,7 +2,6 @@ package sysinfo // import "github.com/docker/docker/pkg/sysinfo" import ( "runtime" - "unsafe" "golang.org/x/sys/unix" ) @@ -14,23 +13,15 @@ import ( // Returns 0 on errors. Use |runtime.NumCPU| in that case. func numCPU() int { // Gets the affinity mask for a process: The very one invoking this function. - pid, _, _ := unix.RawSyscall(unix.SYS_GETPID, 0, 0, 0) + pid := unix.Getpid() - var mask [1024 / 64]uintptr - _, _, err := unix.RawSyscall(unix.SYS_SCHED_GETAFFINITY, pid, uintptr(len(mask)*8), uintptr(unsafe.Pointer(&mask[0]))) - if err != 0 { + var mask unix.CPUSet + err := unix.SchedGetaffinity(pid, &mask) + if err != nil { return 0 } - // For every available thread a bit is set in the mask. - ncpu := 0 - for _, e := range mask { - if e == 0 { - continue - } - ncpu += int(popcnt(uint64(e))) - } - return ncpu + return mask.Count() } // NumCPU returns the number of CPUs which are currently online diff --git a/pkg/sysinfo/numcpu_windows.go b/pkg/sysinfo/numcpu_windows.go index 13523f671f13c..4135c2a2c38cd 100644 --- a/pkg/sysinfo/numcpu_windows.go +++ b/pkg/sysinfo/numcpu_windows.go @@ -13,6 +13,16 @@ var ( getProcessAffinityMask = kernel32.NewProc("GetProcessAffinityMask") ) +// Returns bit count of 1, used by NumCPU +func popcnt(x uint64) (n byte) { + x -= (x >> 1) & 0x5555555555555555 + x = (x>>2)&0x3333333333333333 + x&0x3333333333333333 + x += x >> 4 + x &= 0x0f0f0f0f0f0f0f0f + x *= 0x0101010101010101 + return byte(x >> 56) +} + func numCPU() int { // Gets the affinity mask for a process var mask, sysmask uintptr diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index 0f327d5068efe..ac8a41758136c 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -2,6 +2,9 @@ package sysinfo // import "github.com/docker/docker/pkg/sysinfo" import "github.com/docker/docker/pkg/parsers" +// Opt for New(). +type Opt func(info *SysInfo) + // SysInfo stores information about which features a kernel supports. // TODO Windows: Factor out platform specific capabilities. type SysInfo struct { @@ -16,6 +19,9 @@ type SysInfo struct { cgroupCpusetInfo cgroupPids + // Whether the kernel supports cgroup namespaces or not + CgroupNamespaces bool + // Whether IPv4 forwarding is supported or not, if this was disabled, networking will not work IPv4ForwardingDisabled bool @@ -27,6 +33,26 @@ type SysInfo struct { // Whether the cgroup has the mountpoint of "devices" or not CgroupDevicesEnabled bool + + // Whether the cgroup is in unified mode (v2). + CgroupUnified bool + + // Warnings contains a slice of warnings that occurred while collecting + // system information. These warnings are intended to be informational + // messages for the user, and can either be logged or returned to the + // client; they are not intended to be parsed / used for other purposes, + // and do not have a fixed format. + Warnings []string + + // cgMounts is the list of cgroup v1 mount paths, indexed by subsystem, to + // inspect availability of subsystems. + cgMounts map[string]string + + // cg2GroupPath is the cgroup v2 group path to inspect availability of the controllers. + cg2GroupPath string + + // cg2Controllers is an index of available cgroup v2 controllers. + cg2Controllers map[string]struct{} } type cgroupMemInfo struct { @@ -47,23 +73,20 @@ type cgroupMemInfo struct { // Whether kernel memory limit is supported or not KernelMemory bool + + // Whether kernel memory TCP limit is supported or not + KernelMemoryTCP bool } type cgroupCPUInfo struct { // Whether CPU shares is supported or not CPUShares bool - // Whether CPU CFS(Completely Fair Scheduler) period is supported or not - CPUCfsPeriod bool - - // Whether CPU CFS(Completely Fair Scheduler) quota is supported or not - CPUCfsQuota bool + // Whether CPU CFS (Completely Fair Scheduler) is supported + CPUCfs bool - // Whether CPU real-time period is supported or not - CPURealtimePeriod bool - - // Whether CPU real-time runtime is supported or not - CPURealtimeRuntime bool + // Whether CPU real-time scheduler is supported + CPURealtime bool } type cgroupBlkioInfo struct { @@ -140,13 +163,3 @@ func isCpusetListAvailable(provided, available string) (bool, error) { } return true, nil } - -// Returns bit count of 1, used by NumCPU -func popcnt(x uint64) (n byte) { - x -= (x >> 1) & 0x5555555555555555 - x = (x>>2)&0x3333333333333333 + x&0x3333333333333333 - x += x >> 4 - x &= 0x0f0f0f0f0f0f0f0f - x *= 0x0101010101010101 - return byte(x >> 56) -} diff --git a/pkg/sysinfo/sysinfo_linux.go b/pkg/sysinfo/sysinfo_linux.go index dde5be19bc67a..5a79075cd4bfc 100644 --- a/pkg/sysinfo/sysinfo_linux.go +++ b/pkg/sysinfo/sysinfo_linux.go @@ -2,14 +2,14 @@ package sysinfo // import "github.com/docker/docker/pkg/sysinfo" import ( "fmt" - "io/ioutil" "os" "path" "strings" + cdcgroups "github.com/containerd/cgroups" + cdseccomp "github.com/containerd/containerd/pkg/seccomp" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" ) func findCgroupMountpoints() (map[string]string, error) { @@ -26,227 +26,236 @@ func findCgroupMountpoints() (map[string]string, error) { return mps, nil } -// New returns a new SysInfo, using the filesystem to detect which features -// the kernel supports. If `quiet` is `false` warnings are printed in logs -// whenever an error occurs or misconfigurations are present. -func New(quiet bool) *SysInfo { - sysInfo := &SysInfo{} - cgMounts, err := findCgroupMountpoints() - if err != nil { - logrus.Warnf("Failed to parse cgroup information: %v", err) - } else { - sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet) - sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet) - sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet) - sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet) - sysInfo.cgroupPids = checkCgroupPids(quiet) +type infoCollector func(info *SysInfo) + +// WithCgroup2GroupPath specifies the cgroup v2 group path to inspect availability +// of the controllers. +// +// WithCgroup2GroupPath is expected to be used for rootless mode with systemd driver. +// +// e.g. g = "/user.slice/user-1000.slice/user@1000.service" +func WithCgroup2GroupPath(g string) Opt { + return func(o *SysInfo) { + if p := path.Clean(g); p != "" { + o.cg2GroupPath = p + } } +} - _, ok := cgMounts["devices"] - sysInfo.CgroupDevicesEnabled = ok +// New returns a new SysInfo, using the filesystem to detect which features +// the kernel supports. +func New(options ...Opt) *SysInfo { + if cdcgroups.Mode() == cdcgroups.Unified { + return newV2(options...) + } + return newV1() +} - sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward") - sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables") - sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables") +func newV1() *SysInfo { + var ( + err error + sysInfo = &SysInfo{} + ) - // Check if AppArmor is supported. - if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) { - sysInfo.AppArmor = true + ops := []infoCollector{ + applyNetworkingInfo, + applyAppArmorInfo, + applySeccompInfo, + applyCgroupNsInfo, } - // Check if Seccomp is supported, via CONFIG_SECCOMP. - if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL { - // Make sure the kernel has CONFIG_SECCOMP_FILTER. - if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL { - sysInfo.Seccomp = true - } + sysInfo.cgMounts, err = findCgroupMountpoints() + if err != nil { + logrus.Warn(err) + } else { + ops = append(ops, + applyMemoryCgroupInfo, + applyCPUCgroupInfo, + applyBlkioCgroupInfo, + applyCPUSetCgroupInfo, + applyPIDSCgroupInfo, + applyDevicesCgroupInfo, + ) } + for _, o := range ops { + o(sysInfo) + } return sysInfo } -// checkCgroupMem reads the memory information from the memory cgroup mount point. -func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo { - mountPoint, ok := cgMounts["memory"] +// applyMemoryCgroupInfo adds the memory cgroup controller information to the info. +func applyMemoryCgroupInfo(info *SysInfo) { + mountPoint, ok := info.cgMounts["memory"] if !ok { - if !quiet { - logrus.Warn("Your kernel does not support cgroup memory limit") - } - return cgroupMemInfo{} + info.Warnings = append(info.Warnings, "Your kernel does not support cgroup memory limit") + return } + info.MemoryLimit = ok - swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes") - if !quiet && !swapLimit { - logrus.Warn("Your kernel does not support swap memory limit") + info.SwapLimit = cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes") + if !info.SwapLimit { + info.Warnings = append(info.Warnings, "Your kernel does not support swap memory limit") } - memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes") - if !quiet && !memoryReservation { - logrus.Warn("Your kernel does not support memory reservation") + info.MemoryReservation = cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes") + if !info.MemoryReservation { + info.Warnings = append(info.Warnings, "Your kernel does not support memory reservation") } - oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control") - if !quiet && !oomKillDisable { - logrus.Warn("Your kernel does not support oom control") + info.OomKillDisable = cgroupEnabled(mountPoint, "memory.oom_control") + if !info.OomKillDisable { + info.Warnings = append(info.Warnings, "Your kernel does not support oom control") } - memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness") - if !quiet && !memorySwappiness { - logrus.Warn("Your kernel does not support memory swappiness") + info.MemorySwappiness = cgroupEnabled(mountPoint, "memory.swappiness") + if !info.MemorySwappiness { + info.Warnings = append(info.Warnings, "Your kernel does not support memory swappiness") } - kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes") - if !quiet && !kernelMemory { - logrus.Warn("Your kernel does not support kernel memory limit") + info.KernelMemory = cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes") + if !info.KernelMemory { + info.Warnings = append(info.Warnings, "Your kernel does not support kernel memory limit") } - - return cgroupMemInfo{ - MemoryLimit: true, - SwapLimit: swapLimit, - MemoryReservation: memoryReservation, - OomKillDisable: oomKillDisable, - MemorySwappiness: memorySwappiness, - KernelMemory: kernelMemory, + info.KernelMemoryTCP = cgroupEnabled(mountPoint, "memory.kmem.tcp.limit_in_bytes") + if !info.KernelMemoryTCP { + info.Warnings = append(info.Warnings, "Your kernel does not support kernel memory TCP limit") } } -// checkCgroupCPU reads the cpu information from the cpu cgroup mount point. -func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo { - mountPoint, ok := cgMounts["cpu"] +// applyCPUCgroupInfo adds the cpu cgroup controller information to the info. +func applyCPUCgroupInfo(info *SysInfo) { + mountPoint, ok := info.cgMounts["cpu"] if !ok { - if !quiet { - logrus.Warn("Unable to find cpu cgroup in mounts") - } - return cgroupCPUInfo{} - } - - cpuShares := cgroupEnabled(mountPoint, "cpu.shares") - if !quiet && !cpuShares { - logrus.Warn("Your kernel does not support cgroup cpu shares") + info.Warnings = append(info.Warnings, "Unable to find cpu cgroup in mounts") + return } - cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us") - if !quiet && !cpuCfsPeriod { - logrus.Warn("Your kernel does not support cgroup cfs period") + info.CPUShares = cgroupEnabled(mountPoint, "cpu.shares") + if !info.CPUShares { + info.Warnings = append(info.Warnings, "Your kernel does not support CPU shares") } - cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us") - if !quiet && !cpuCfsQuota { - logrus.Warn("Your kernel does not support cgroup cfs quotas") + info.CPUCfs = cgroupEnabled(mountPoint, "cpu.cfs_quota_us") + if !info.CPUCfs { + info.Warnings = append(info.Warnings, "Your kernel does not support CPU CFS scheduler") } - cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us") - if !quiet && !cpuRealtimePeriod { - logrus.Warn("Your kernel does not support cgroup rt period") - } - - cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us") - if !quiet && !cpuRealtimeRuntime { - logrus.Warn("Your kernel does not support cgroup rt runtime") - } - - return cgroupCPUInfo{ - CPUShares: cpuShares, - CPUCfsPeriod: cpuCfsPeriod, - CPUCfsQuota: cpuCfsQuota, - CPURealtimePeriod: cpuRealtimePeriod, - CPURealtimeRuntime: cpuRealtimeRuntime, + info.CPURealtime = cgroupEnabled(mountPoint, "cpu.rt_period_us") + if !info.CPURealtime { + info.Warnings = append(info.Warnings, "Your kernel does not support CPU realtime scheduler") } } -// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point. -func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo { - mountPoint, ok := cgMounts["blkio"] +// applyBlkioCgroupInfo adds the blkio cgroup controller information to the info. +func applyBlkioCgroupInfo(info *SysInfo) { + mountPoint, ok := info.cgMounts["blkio"] if !ok { - if !quiet { - logrus.Warn("Unable to find blkio cgroup in mounts") - } - return cgroupBlkioInfo{} + info.Warnings = append(info.Warnings, "Unable to find blkio cgroup in mounts") + return } - weight := cgroupEnabled(mountPoint, "blkio.weight") - if !quiet && !weight { - logrus.Warn("Your kernel does not support cgroup blkio weight") + info.BlkioWeight = cgroupEnabled(mountPoint, "blkio.weight") + if !info.BlkioWeight { + info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio weight") } - weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device") - if !quiet && !weightDevice { - logrus.Warn("Your kernel does not support cgroup blkio weight_device") + info.BlkioWeightDevice = cgroupEnabled(mountPoint, "blkio.weight_device") + if !info.BlkioWeightDevice { + info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio weight_device") } - readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device") - if !quiet && !readBpsDevice { - logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device") + info.BlkioReadBpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device") + if !info.BlkioReadBpsDevice { + info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.read_bps_device") } - writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device") - if !quiet && !writeBpsDevice { - logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device") + info.BlkioWriteBpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device") + if !info.BlkioWriteBpsDevice { + info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.write_bps_device") } - readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device") - if !quiet && !readIOpsDevice { - logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device") + info.BlkioReadIOpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device") + if !info.BlkioReadIOpsDevice { + info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.read_iops_device") } - writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device") - if !quiet && !writeIOpsDevice { - logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device") - } - return cgroupBlkioInfo{ - BlkioWeight: weight, - BlkioWeightDevice: weightDevice, - BlkioReadBpsDevice: readBpsDevice, - BlkioWriteBpsDevice: writeBpsDevice, - BlkioReadIOpsDevice: readIOpsDevice, - BlkioWriteIOpsDevice: writeIOpsDevice, + info.BlkioWriteIOpsDevice = cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device") + if !info.BlkioWriteIOpsDevice { + info.Warnings = append(info.Warnings, "Your kernel does not support cgroup blkio throttle.write_iops_device") } } -// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point. -func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo { - mountPoint, ok := cgMounts["cpuset"] +// applyCPUSetCgroupInfo adds the cpuset cgroup controller information to the info. +func applyCPUSetCgroupInfo(info *SysInfo) { + mountPoint, ok := info.cgMounts["cpuset"] if !ok { - if !quiet { - logrus.Warn("Unable to find cpuset cgroup in mounts") - } - return cgroupCpusetInfo{} + info.Warnings = append(info.Warnings, "Unable to find cpuset cgroup in mounts") + return } + info.Cpuset = ok + + var err error - cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus")) + cpus, err := os.ReadFile(path.Join(mountPoint, "cpuset.cpus")) if err != nil { - return cgroupCpusetInfo{} + return } + info.Cpus = strings.TrimSpace(string(cpus)) - mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems")) + mems, err := os.ReadFile(path.Join(mountPoint, "cpuset.mems")) if err != nil { - return cgroupCpusetInfo{} + return } + info.Mems = strings.TrimSpace(string(mems)) +} - return cgroupCpusetInfo{ - Cpuset: true, - Cpus: strings.TrimSpace(string(cpus)), - Mems: strings.TrimSpace(string(mems)), +// applyPIDSCgroupInfo adds whether the pids cgroup controller is available to the info. +func applyPIDSCgroupInfo(info *SysInfo) { + _, ok := info.cgMounts["pids"] + if !ok { + info.Warnings = append(info.Warnings, "Unable to find pids cgroup in mounts") + return } + info.PidsLimit = true } -// checkCgroupPids reads the pids information from the pids cgroup mount point. -func checkCgroupPids(quiet bool) cgroupPids { - _, err := cgroups.FindCgroupMountpoint("pids") - if err != nil { - if !quiet { - logrus.Warn(err) +// applyDevicesCgroupInfo adds whether the devices cgroup controller is available to the info. +func applyDevicesCgroupInfo(info *SysInfo) { + _, ok := info.cgMounts["devices"] + info.CgroupDevicesEnabled = ok +} + +// applyNetworkingInfo adds networking information to the info. +func applyNetworkingInfo(info *SysInfo) { + info.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward") + info.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables") + info.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables") +} + +// applyAppArmorInfo adds whether AppArmor is enabled to the info. +func applyAppArmorInfo(info *SysInfo) { + if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) { + if _, err := os.ReadFile("/sys/kernel/security/apparmor/profiles"); err == nil { + info.AppArmor = true } - return cgroupPids{} } +} - return cgroupPids{ - PidsLimit: true, +// applyCgroupNsInfo adds whether cgroupns is enabled to the info. +func applyCgroupNsInfo(info *SysInfo) { + if _, err := os.Stat("/proc/self/ns/cgroup"); !os.IsNotExist(err) { + info.CgroupNamespaces = true } } +// applySeccompInfo checks if Seccomp is supported, via CONFIG_SECCOMP. +func applySeccompInfo(info *SysInfo) { + info.Seccomp = cdseccomp.IsEnabled() +} + func cgroupEnabled(mountPoint, name string) bool { _, err := os.Stat(path.Join(mountPoint, name)) return err == nil } func readProcBool(path string) bool { - val, err := ioutil.ReadFile(path) + val, err := os.ReadFile(path) if err != nil { return false } diff --git a/pkg/sysinfo/sysinfo_linux_test.go b/pkg/sysinfo/sysinfo_linux_test.go index 13a07fbce93cf..f84b416297f52 100644 --- a/pkg/sysinfo/sysinfo_linux_test.go +++ b/pkg/sysinfo/sysinfo_linux_test.go @@ -1,30 +1,29 @@ package sysinfo // import "github.com/docker/docker/pkg/sysinfo" import ( - "io/ioutil" "os" "path" "path/filepath" "testing" "golang.org/x/sys/unix" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestReadProcBool(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "test-sysinfo-proc") + tmpDir, err := os.MkdirTemp("", "test-sysinfo-proc") assert.NilError(t, err) defer os.RemoveAll(tmpDir) procFile := filepath.Join(tmpDir, "read-proc-bool") - err = ioutil.WriteFile(procFile, []byte("1"), 0644) + err = os.WriteFile(procFile, []byte("1"), 0644) assert.NilError(t, err) if !readProcBool(procFile) { t.Fatal("expected proc bool to be true, got false") } - if err := ioutil.WriteFile(procFile, []byte("0"), 0644); err != nil { + if err := os.WriteFile(procFile, []byte("0"), 0644); err != nil { t.Fatal(err) } if readProcBool(procFile) { @@ -38,7 +37,7 @@ func TestReadProcBool(t *testing.T) { } func TestCgroupEnabled(t *testing.T) { - cgroupDir, err := ioutil.TempDir("", "cgroup-test") + cgroupDir, err := os.MkdirTemp("", "cgroup-test") assert.NilError(t, err) defer os.RemoveAll(cgroupDir) @@ -46,7 +45,7 @@ func TestCgroupEnabled(t *testing.T) { t.Fatal("cgroupEnabled should be false") } - err = ioutil.WriteFile(path.Join(cgroupDir, "test"), []byte{}, 0644) + err = os.WriteFile(path.Join(cgroupDir, "test"), []byte{}, 0644) assert.NilError(t, err) if !cgroupEnabled(cgroupDir, "test") { @@ -55,11 +54,7 @@ func TestCgroupEnabled(t *testing.T) { } func TestNew(t *testing.T) { - sysInfo := New(false) - assert.Assert(t, sysInfo != nil) - checkSysInfo(t, sysInfo) - - sysInfo = New(true) + sysInfo := New() assert.Assert(t, sysInfo != nil) checkSysInfo(t, sysInfo) } @@ -79,23 +74,43 @@ func checkSysInfo(t *testing.T, sysInfo *SysInfo) { func TestNewAppArmorEnabled(t *testing.T) { // Check if AppArmor is supported. then it must be TRUE , else FALSE if _, err := os.Stat("/sys/kernel/security/apparmor"); err != nil { - t.Skip("App Armor Must be Enabled") + t.Skip("AppArmor Must be Enabled") } - sysInfo := New(true) + sysInfo := New() assert.Assert(t, sysInfo.AppArmor) } func TestNewAppArmorDisabled(t *testing.T) { // Check if AppArmor is supported. then it must be TRUE , else FALSE if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) { - t.Skip("App Armor Must be Disabled") + t.Skip("AppArmor Must be Disabled") } - sysInfo := New(true) + sysInfo := New() assert.Assert(t, !sysInfo.AppArmor) } +func TestNewCgroupNamespacesEnabled(t *testing.T) { + // If cgroup namespaces are supported in the kernel, then sysInfo.CgroupNamespaces should be TRUE + if _, err := os.Stat("/proc/self/ns/cgroup"); err != nil { + t.Skip("cgroup namespaces must be enabled") + } + + sysInfo := New() + assert.Assert(t, sysInfo.CgroupNamespaces) +} + +func TestNewCgroupNamespacesDisabled(t *testing.T) { + // If cgroup namespaces are *not* supported in the kernel, then sysInfo.CgroupNamespaces should be FALSE + if _, err := os.Stat("/proc/self/ns/cgroup"); !os.IsNotExist(err) { + t.Skip("cgroup namespaces must be disabled") + } + + sysInfo := New() + assert.Assert(t, !sysInfo.CgroupNamespaces) +} + func TestNumCPU(t *testing.T) { cpuNumbers := NumCPU() if cpuNumbers <= 0 { diff --git a/pkg/sysinfo/sysinfo_other.go b/pkg/sysinfo/sysinfo_other.go new file mode 100644 index 0000000000000..aa97c0f29ab9b --- /dev/null +++ b/pkg/sysinfo/sysinfo_other.go @@ -0,0 +1,9 @@ +//go:build !linux +// +build !linux + +package sysinfo // import "github.com/docker/docker/pkg/sysinfo" + +// New returns an empty SysInfo for non linux for now. +func New(options ...Opt) *SysInfo { + return &SysInfo{} +} diff --git a/pkg/sysinfo/sysinfo_unix.go b/pkg/sysinfo/sysinfo_unix.go deleted file mode 100644 index 23cc695fb83d3..0000000000000 --- a/pkg/sysinfo/sysinfo_unix.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build !linux,!windows - -package sysinfo // import "github.com/docker/docker/pkg/sysinfo" - -// New returns an empty SysInfo for non linux for now. -func New(quiet bool) *SysInfo { - sysInfo := &SysInfo{} - return sysInfo -} diff --git a/pkg/sysinfo/sysinfo_windows.go b/pkg/sysinfo/sysinfo_windows.go deleted file mode 100644 index 5f68524e7ed65..0000000000000 --- a/pkg/sysinfo/sysinfo_windows.go +++ /dev/null @@ -1,7 +0,0 @@ -package sysinfo // import "github.com/docker/docker/pkg/sysinfo" - -// New returns an empty SysInfo for windows for now. -func New(quiet bool) *SysInfo { - sysInfo := &SysInfo{} - return sysInfo -} diff --git a/pkg/system/args_windows.go b/pkg/system/args_windows.go new file mode 100644 index 0000000000000..b7c9487a0674a --- /dev/null +++ b/pkg/system/args_windows.go @@ -0,0 +1,16 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "strings" + + "golang.org/x/sys/windows" +) + +// EscapeArgs makes a Windows-style escaped command line from a set of arguments +func EscapeArgs(args []string) string { + escapedArgs := make([]string, len(args)) + for i, a := range args { + escapedArgs[i] = windows.EscapeArg(a) + } + return strings.Join(escapedArgs, " ") +} diff --git a/pkg/system/chtimes_linux_test.go b/pkg/system/chtimes_linux_test.go new file mode 100644 index 0000000000000..075f573059ae2 --- /dev/null +++ b/pkg/system/chtimes_linux_test.go @@ -0,0 +1,89 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "os" + "syscall" + "testing" + "time" +) + +// TestChtimesLinux tests Chtimes access time on a tempfile on Linux +func TestChtimesLinux(t *testing.T) { + file, dir := prepareTempFile(t) + defer os.RemoveAll(dir) + + beforeUnixEpochTime := time.Unix(0, 0).Add(-100 * time.Second) + unixEpochTime := time.Unix(0, 0) + afterUnixEpochTime := time.Unix(100, 0) + unixMaxTime := maxTime + + // Test both aTime and mTime set to Unix Epoch + Chtimes(file, unixEpochTime, unixEpochTime) + + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } + + stat := f.Sys().(*syscall.Stat_t) + aTime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) //nolint: unconvert + if aTime != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) + } + + // Test aTime before Unix Epoch and mTime set to Unix Epoch + Chtimes(file, beforeUnixEpochTime, unixEpochTime) + + f, err = os.Stat(file) + if err != nil { + t.Fatal(err) + } + + stat = f.Sys().(*syscall.Stat_t) + aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) //nolint: unconvert + if aTime != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) + } + + // Test aTime set to Unix Epoch and mTime before Unix Epoch + Chtimes(file, unixEpochTime, beforeUnixEpochTime) + + f, err = os.Stat(file) + if err != nil { + t.Fatal(err) + } + + stat = f.Sys().(*syscall.Stat_t) + aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) //nolint: unconvert + if aTime != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) + } + + // Test both aTime and mTime set to after Unix Epoch (valid time) + Chtimes(file, afterUnixEpochTime, afterUnixEpochTime) + + f, err = os.Stat(file) + if err != nil { + t.Fatal(err) + } + + stat = f.Sys().(*syscall.Stat_t) + aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) //nolint: unconvert + if aTime != afterUnixEpochTime { + t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, aTime) + } + + // Test both aTime and mTime set to Unix max time + Chtimes(file, unixMaxTime, unixMaxTime) + + f, err = os.Stat(file) + if err != nil { + t.Fatal(err) + } + + stat = f.Sys().(*syscall.Stat_t) + aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) //nolint: unconvert + if aTime.Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { + t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), aTime.Truncate(time.Second)) + } +} diff --git a/pkg/system/chtimes_nowindows.go b/pkg/system/chtimes_nowindows.go new file mode 100644 index 0000000000000..84ae1570513b5 --- /dev/null +++ b/pkg/system/chtimes_nowindows.go @@ -0,0 +1,15 @@ +//go:build !windows +// +build !windows + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "time" +) + +// setCTime will set the create time on a file. On Unix, the create +// time is updated as a side effect of setting the modified time, so +// no action is required. +func setCTime(path string, ctime time.Time) error { + return nil +} diff --git a/pkg/system/chtimes_test.go b/pkg/system/chtimes_test.go index 5a3f98e199d69..3bb1fb2a60329 100644 --- a/pkg/system/chtimes_test.go +++ b/pkg/system/chtimes_test.go @@ -1,7 +1,6 @@ package system // import "github.com/docker/docker/pkg/system" import ( - "io/ioutil" "os" "path/filepath" "testing" @@ -10,13 +9,13 @@ import ( // prepareTempFile creates a temporary file in a temporary directory. func prepareTempFile(t *testing.T) (string, string) { - dir, err := ioutil.TempDir("", "docker-system-test") + dir, err := os.MkdirTemp("", "docker-system-test") if err != nil { t.Fatal(err) } file := filepath.Join(dir, "exist") - if err := ioutil.WriteFile(file, []byte("hello"), 0644); err != nil { + if err := os.WriteFile(file, []byte("hello"), 0644); err != nil { t.Fatal(err) } return file, dir diff --git a/pkg/system/chtimes_unix.go b/pkg/system/chtimes_unix.go deleted file mode 100644 index 259138a45b52c..0000000000000 --- a/pkg/system/chtimes_unix.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build !windows - -package system // import "github.com/docker/docker/pkg/system" - -import ( - "time" -) - -//setCTime will set the create time on a file. On Unix, the create -//time is updated as a side effect of setting the modified time, so -//no action is required. -func setCTime(path string, ctime time.Time) error { - return nil -} diff --git a/pkg/system/chtimes_unix_test.go b/pkg/system/chtimes_unix_test.go deleted file mode 100644 index e25232c767556..0000000000000 --- a/pkg/system/chtimes_unix_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// +build !windows - -package system // import "github.com/docker/docker/pkg/system" - -import ( - "os" - "syscall" - "testing" - "time" -) - -// TestChtimesLinux tests Chtimes access time on a tempfile on Linux -func TestChtimesLinux(t *testing.T) { - file, dir := prepareTempFile(t) - defer os.RemoveAll(dir) - - beforeUnixEpochTime := time.Unix(0, 0).Add(-100 * time.Second) - unixEpochTime := time.Unix(0, 0) - afterUnixEpochTime := time.Unix(100, 0) - unixMaxTime := maxTime - - // Test both aTime and mTime set to Unix Epoch - Chtimes(file, unixEpochTime, unixEpochTime) - - f, err := os.Stat(file) - if err != nil { - t.Fatal(err) - } - - stat := f.Sys().(*syscall.Stat_t) - aTime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } - - // Test aTime before Unix Epoch and mTime set to Unix Epoch - Chtimes(file, beforeUnixEpochTime, unixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } - - // Test aTime set to Unix Epoch and mTime before Unix Epoch - Chtimes(file, unixEpochTime, beforeUnixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } - - // Test both aTime and mTime set to after Unix Epoch (valid time) - Chtimes(file, afterUnixEpochTime, afterUnixEpochTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) - if aTime != afterUnixEpochTime { - t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, aTime) - } - - // Test both aTime and mTime set to Unix max time - Chtimes(file, unixMaxTime, unixMaxTime) - - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } - - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) - if aTime.Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { - t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), aTime.Truncate(time.Second)) - } -} diff --git a/pkg/system/chtimes_windows.go b/pkg/system/chtimes_windows.go index d3a115ff42b8e..6664b8bcad76e 100644 --- a/pkg/system/chtimes_windows.go +++ b/pkg/system/chtimes_windows.go @@ -6,8 +6,8 @@ import ( "golang.org/x/sys/windows" ) -//setCTime will set the create time on a file. On Windows, this requires -//calling SetFileTime and explicitly including the create time. +// setCTime will set the create time on a file. On Windows, this requires +// calling SetFileTime and explicitly including the create time. func setCTime(path string, ctime time.Time) error { ctimespec := windows.NsecToTimespec(ctime.UnixNano()) pathp, e := windows.UTF16PtrFromString(path) diff --git a/pkg/system/chtimes_windows_test.go b/pkg/system/chtimes_windows_test.go index d91e4bc6e4bcf..060c5150037d7 100644 --- a/pkg/system/chtimes_windows_test.go +++ b/pkg/system/chtimes_windows_test.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package system // import "github.com/docker/docker/pkg/system" diff --git a/pkg/system/filesys.go b/pkg/system/filesys.go deleted file mode 100644 index adeb163052048..0000000000000 --- a/pkg/system/filesys.go +++ /dev/null @@ -1,67 +0,0 @@ -// +build !windows - -package system // import "github.com/docker/docker/pkg/system" - -import ( - "io/ioutil" - "os" - "path/filepath" -) - -// MkdirAllWithACL is a wrapper for MkdirAll on unix systems. -func MkdirAllWithACL(path string, perm os.FileMode, sddl string) error { - return MkdirAll(path, perm, sddl) -} - -// MkdirAll creates a directory named path along with any necessary parents, -// with permission specified by attribute perm for all dir created. -func MkdirAll(path string, perm os.FileMode, sddl string) error { - return os.MkdirAll(path, perm) -} - -// IsAbs is a platform-specific wrapper for filepath.IsAbs. -func IsAbs(path string) bool { - return filepath.IsAbs(path) -} - -// The functions below here are wrappers for the equivalents in the os and ioutils packages. -// They are passthrough on Unix platforms, and only relevant on Windows. - -// CreateSequential creates the named file with mode 0666 (before umask), truncating -// it if it already exists. If successful, methods on the returned -// File can be used for I/O; the associated file descriptor has mode -// O_RDWR. -// If there is an error, it will be of type *PathError. -func CreateSequential(name string) (*os.File, error) { - return os.Create(name) -} - -// OpenSequential opens the named file for reading. If successful, methods on -// the returned file can be used for reading; the associated file -// descriptor has mode O_RDONLY. -// If there is an error, it will be of type *PathError. -func OpenSequential(name string) (*os.File, error) { - return os.Open(name) -} - -// OpenFileSequential is the generalized open call; most users will use Open -// or Create instead. It opens the named file with specified flag -// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful, -// methods on the returned File can be used for I/O. -// If there is an error, it will be of type *PathError. -func OpenFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) { - return os.OpenFile(name, flag, perm) -} - -// TempFileSequential creates a new temporary file in the directory dir -// with a name beginning with prefix, opens the file for reading -// and writing, and returns the resulting *os.File. -// If dir is the empty string, TempFile uses the default directory -// for temporary files (see os.TempDir). -// Multiple programs calling TempFile simultaneously -// will not choose the same file. The caller can use f.Name() -// to find the pathname of the file. It is the caller's responsibility -// to remove the file when no longer needed. -func TempFileSequential(dir, prefix string) (f *os.File, err error) { - return ioutil.TempFile(dir, prefix) -} diff --git a/pkg/system/filesys_unix.go b/pkg/system/filesys_unix.go new file mode 100644 index 0000000000000..8b991201a98f8 --- /dev/null +++ b/pkg/system/filesys_unix.go @@ -0,0 +1,67 @@ +//go:build !windows +// +build !windows + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "os" + "path/filepath" +) + +// MkdirAllWithACL is a wrapper for os.MkdirAll on unix systems. +func MkdirAllWithACL(path string, perm os.FileMode, sddl string) error { + return os.MkdirAll(path, perm) +} + +// MkdirAll creates a directory named path along with any necessary parents, +// with permission specified by attribute perm for all dir created. +func MkdirAll(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) +} + +// IsAbs is a platform-specific wrapper for filepath.IsAbs. +func IsAbs(path string) bool { + return filepath.IsAbs(path) +} + +// The functions below here are wrappers for the equivalents in the os and ioutils packages. +// They are passthrough on Unix platforms, and only relevant on Windows. + +// CreateSequential creates the named file with mode 0666 (before umask), truncating +// it if it already exists. If successful, methods on the returned +// File can be used for I/O; the associated file descriptor has mode +// O_RDWR. +// If there is an error, it will be of type *PathError. +func CreateSequential(name string) (*os.File, error) { + return os.Create(name) +} + +// OpenSequential opens the named file for reading. If successful, methods on +// the returned file can be used for reading; the associated file +// descriptor has mode O_RDONLY. +// If there is an error, it will be of type *PathError. +func OpenSequential(name string) (*os.File, error) { + return os.Open(name) +} + +// OpenFileSequential is the generalized open call; most users will use Open +// or Create instead. It opens the named file with specified flag +// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful, +// methods on the returned File can be used for I/O. +// If there is an error, it will be of type *PathError. +func OpenFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) { + return os.OpenFile(name, flag, perm) +} + +// TempFileSequential creates a new temporary file in the directory dir +// with a name beginning with prefix, opens the file for reading +// and writing, and returns the resulting *os.File. +// If dir is the empty string, TempFile uses the default directory +// for temporary files (see os.TempDir). +// Multiple programs calling TempFile simultaneously +// will not choose the same file. The caller can use f.Name() +// to find the pathname of the file. It is the caller's responsibility +// to remove the file when no longer needed. +func TempFileSequential(dir, prefix string) (f *os.File, err error) { + return os.CreateTemp(dir, prefix) +} diff --git a/pkg/system/filesys_windows.go b/pkg/system/filesys_windows.go index a1f6013f1397f..8f79dc8fe06c5 100644 --- a/pkg/system/filesys_windows.go +++ b/pkg/system/filesys_windows.go @@ -11,15 +11,12 @@ import ( "time" "unsafe" - winio "github.com/Microsoft/go-winio" "golang.org/x/sys/windows" ) const ( // SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)" - // SddlNtvmAdministratorsLocalSystem is NT VIRTUAL MACHINE\Virtual Machines plus local administrators plus NT AUTHORITY\System - SddlNtvmAdministratorsLocalSystem = "D:P(A;OICI;GA;;;S-1-5-83-0)(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)" ) // MkdirAllWithACL is a wrapper for MkdirAll that creates a directory @@ -28,9 +25,10 @@ func MkdirAllWithACL(path string, perm os.FileMode, sddl string) error { return mkdirall(path, true, sddl) } -// MkdirAll implementation that is volume path aware for Windows. -func MkdirAll(path string, _ os.FileMode, sddl string) error { - return mkdirall(path, false, sddl) +// MkdirAll implementation that is volume path aware for Windows. It can be used +// as a drop-in replacement for os.MkdirAll() +func MkdirAll(path string, _ os.FileMode) error { + return mkdirall(path, false, "") } // mkdirall is a custom version of os.MkdirAll modified for use on Windows @@ -104,13 +102,13 @@ func mkdirall(path string, applyACL bool, sddl string) error { // and Local System. func mkdirWithACL(name string, sddl string) error { sa := windows.SecurityAttributes{Length: 0} - sd, err := winio.SddlToSecurityDescriptor(sddl) + sd, err := windows.SecurityDescriptorFromString(sddl) if err != nil { return &os.PathError{Op: "mkdir", Path: name, Err: err} } sa.Length = uint32(unsafe.Sizeof(sa)) sa.InheritHandle = 1 - sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0])) + sa.SecurityDescriptor = sd namep, err := windows.UTF16PtrFromString(name) if err != nil { @@ -132,12 +130,10 @@ func mkdirWithACL(name string, sddl string) error { // by the daemon. This SHOULD be treated as absolute from a docker processing // perspective. func IsAbs(path string) bool { - if !filepath.IsAbs(path) { - if !strings.HasPrefix(path, string(os.PathSeparator)) { - return false - } + if filepath.IsAbs(path) || strings.HasPrefix(path, string(os.PathSeparator)) { + return true } - return true + return false } // The origin of the functions below here are the golang OS and windows packages, @@ -237,7 +233,7 @@ func windowsOpenSequential(path string, mode int, _ uint32) (fd windows.Handle, createmode = windows.OPEN_EXISTING } // Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang. - //https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0) return h, e @@ -262,7 +258,7 @@ func nextSuffix() string { return strconv.Itoa(int(1e9 + r%1e9))[1:] } -// TempFileSequential is a copy of ioutil.TempFile, modified to use sequential +// TempFileSequential is a copy of os.CreateTemp, modified to use sequential // file access. Below is the original comment from golang: // TempFile creates a new temporary file in the directory dir // with a name beginning with prefix, opens the file for reading diff --git a/pkg/system/image_os.go b/pkg/system/image_os.go new file mode 100644 index 0000000000000..e3de86be29228 --- /dev/null +++ b/pkg/system/image_os.go @@ -0,0 +1,10 @@ +package system // import "github.com/docker/docker/pkg/system" +import ( + "runtime" + "strings" +) + +// IsOSSupported determines if an operating system is supported by the host. +func IsOSSupported(os string) bool { + return strings.EqualFold(runtime.GOOS, os) +} diff --git a/pkg/system/init_unix.go b/pkg/system/init_unix.go deleted file mode 100644 index 4996a67c12e5b..0000000000000 --- a/pkg/system/init_unix.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build !windows - -package system // import "github.com/docker/docker/pkg/system" - -// InitLCOW does nothing since LCOW is a windows only feature -func InitLCOW(experimental bool) { -} diff --git a/pkg/system/init_windows.go b/pkg/system/init_windows.go index 4910ff69d6661..3c2a43ddbd397 100644 --- a/pkg/system/init_windows.go +++ b/pkg/system/init_windows.go @@ -1,12 +1,18 @@ package system // import "github.com/docker/docker/pkg/system" -// lcowSupported determines if Linux Containers on Windows are supported. -var lcowSupported = false +var ( + // containerdRuntimeSupported determines if containerd should be the runtime. + containerdRuntimeSupported = false +) -// InitLCOW sets whether LCOW is supported or not -func InitLCOW(experimental bool) { - v := GetOSVersion() - if experimental && v.Build >= 16299 { - lcowSupported = true +// InitContainerdRuntime sets whether to use containerd for runtime on Windows. +func InitContainerdRuntime(cdPath string) { + if len(cdPath) > 0 { + containerdRuntimeSupported = true } } + +// ContainerdRuntimeSupported returns true if the use of containerd runtime is supported. +func ContainerdRuntimeSupported() bool { + return containerdRuntimeSupported +} diff --git a/pkg/system/lcow.go b/pkg/system/lcow.go deleted file mode 100644 index 5be3e2182ba08..0000000000000 --- a/pkg/system/lcow.go +++ /dev/null @@ -1,32 +0,0 @@ -package system // import "github.com/docker/docker/pkg/system" - -import ( - "runtime" - "strings" - - specs "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" -) - -// IsOSSupported determines if an operating system is supported by the host -func IsOSSupported(os string) bool { - if strings.EqualFold(runtime.GOOS, os) { - return true - } - if LCOWSupported() && strings.EqualFold(os, "linux") { - return true - } - return false -} - -// ValidatePlatform determines if a platform structure is valid. -// TODO This is a temporary windows-only function, should be replaced by -// comparison of worker capabilities -func ValidatePlatform(platform specs.Platform) error { - if runtime.GOOS == "windows" { - if !(platform.OS == runtime.GOOS || (LCOWSupported() && platform.OS == "linux")) { - return errors.Errorf("unsupported os %s", platform.OS) - } - } - return nil -} diff --git a/pkg/system/lcow_unix.go b/pkg/system/lcow_unix.go deleted file mode 100644 index 26397fb8a1764..0000000000000 --- a/pkg/system/lcow_unix.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build !windows - -package system // import "github.com/docker/docker/pkg/system" - -// LCOWSupported returns true if Linux containers on Windows are supported. -func LCOWSupported() bool { - return false -} diff --git a/pkg/system/lcow_windows.go b/pkg/system/lcow_windows.go deleted file mode 100644 index f0139df8f7e03..0000000000000 --- a/pkg/system/lcow_windows.go +++ /dev/null @@ -1,6 +0,0 @@ -package system // import "github.com/docker/docker/pkg/system" - -// LCOWSupported returns true if Linux containers on Windows are supported. -func LCOWSupported() bool { - return lcowSupported -} diff --git a/pkg/system/lstat_unix.go b/pkg/system/lstat_unix.go index 7477995f1bfd7..654b9f2c9e69b 100644 --- a/pkg/system/lstat_unix.go +++ b/pkg/system/lstat_unix.go @@ -1,8 +1,10 @@ +//go:build !windows // +build !windows package system // import "github.com/docker/docker/pkg/system" import ( + "os" "syscall" ) @@ -13,7 +15,7 @@ import ( func Lstat(path string) (*StatT, error) { s := &syscall.Stat_t{} if err := syscall.Lstat(path, s); err != nil { - return nil, err + return nil, &os.PathError{Op: "Lstat", Path: path, Err: err} } return fromStatT(s) } diff --git a/pkg/system/lstat_unix_test.go b/pkg/system/lstat_unix_test.go index 9fb4a191cfe32..943b7d6c638f2 100644 --- a/pkg/system/lstat_unix_test.go +++ b/pkg/system/lstat_unix_test.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package system // import "github.com/docker/docker/pkg/system" diff --git a/pkg/system/meminfo_linux.go b/pkg/system/meminfo_linux.go index d79e8b0765357..cd060eff24de5 100644 --- a/pkg/system/meminfo_linux.go +++ b/pkg/system/meminfo_linux.go @@ -7,7 +7,7 @@ import ( "strconv" "strings" - "github.com/docker/go-units" + units "github.com/docker/go-units" ) // ReadMemInfo retrieves memory statistics of the host system and returns a @@ -27,6 +27,7 @@ func ReadMemInfo() (*MemInfo, error) { func parseMemInfo(reader io.Reader) (*MemInfo, error) { meminfo := &MemInfo{} scanner := bufio.NewScanner(reader) + memAvailable := int64(-1) for scanner.Scan() { // Expected format: ["MemTotal:", "1234", "kB"] parts := strings.Fields(scanner.Text()) @@ -48,6 +49,8 @@ func parseMemInfo(reader io.Reader) (*MemInfo, error) { meminfo.MemTotal = bytes case "MemFree:": meminfo.MemFree = bytes + case "MemAvailable:": + memAvailable = bytes case "SwapTotal:": meminfo.SwapTotal = bytes case "SwapFree:": @@ -55,6 +58,9 @@ func parseMemInfo(reader io.Reader) (*MemInfo, error) { } } + if memAvailable != -1 { + meminfo.MemFree = memAvailable + } // Handle errors that may have occurred during the reading of the file. if err := scanner.Err(); err != nil { diff --git a/pkg/system/meminfo_unix_test.go b/pkg/system/meminfo_unix_test.go index c3690d6311d97..adbc94824728b 100644 --- a/pkg/system/meminfo_unix_test.go +++ b/pkg/system/meminfo_unix_test.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package system // import "github.com/docker/docker/pkg/system" @@ -6,7 +7,7 @@ import ( "strings" "testing" - "github.com/docker/go-units" + units "github.com/docker/go-units" ) // TestMemInfo tests parseMemInfo with a static meminfo string @@ -14,8 +15,9 @@ func TestMemInfo(t *testing.T) { const input = ` MemTotal: 1 kB MemFree: 2 kB - SwapTotal: 3 kB - SwapFree: 4 kB + MemAvailable: 3 kB + SwapTotal: 4 kB + SwapFree: 5 kB Malformed1: Malformed2: 1 Malformed3: 2 MB @@ -28,13 +30,13 @@ func TestMemInfo(t *testing.T) { if meminfo.MemTotal != 1*units.KiB { t.Fatalf("Unexpected MemTotal: %d", meminfo.MemTotal) } - if meminfo.MemFree != 2*units.KiB { + if meminfo.MemFree != 3*units.KiB { t.Fatalf("Unexpected MemFree: %d", meminfo.MemFree) } - if meminfo.SwapTotal != 3*units.KiB { + if meminfo.SwapTotal != 4*units.KiB { t.Fatalf("Unexpected SwapTotal: %d", meminfo.SwapTotal) } - if meminfo.SwapFree != 4*units.KiB { + if meminfo.SwapFree != 5*units.KiB { t.Fatalf("Unexpected SwapFree: %d", meminfo.SwapFree) } } diff --git a/pkg/system/meminfo_unsupported.go b/pkg/system/meminfo_unsupported.go index 56f4494268043..207ee58ee6c50 100644 --- a/pkg/system/meminfo_unsupported.go +++ b/pkg/system/meminfo_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows // +build !linux,!windows package system // import "github.com/docker/docker/pkg/system" diff --git a/pkg/system/mknod.go b/pkg/system/mknod.go index b132482e03818..d27152c0f5b58 100644 --- a/pkg/system/mknod.go +++ b/pkg/system/mknod.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package system // import "github.com/docker/docker/pkg/system" @@ -6,12 +7,6 @@ import ( "golang.org/x/sys/unix" ) -// Mknod creates a filesystem node (file, device special file or named pipe) named path -// with attributes specified by mode and dev. -func Mknod(path string, mode uint32, dev int) error { - return unix.Mknod(path, mode, dev) -} - // Mkdev is used to build the value of linux devices (in /dev/) which specifies major // and minor number of the newly created device special file. // Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes. diff --git a/pkg/system/mknod_freebsd.go b/pkg/system/mknod_freebsd.go new file mode 100644 index 0000000000000..c890be116f796 --- /dev/null +++ b/pkg/system/mknod_freebsd.go @@ -0,0 +1,14 @@ +//go:build freebsd +// +build freebsd + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "golang.org/x/sys/unix" +) + +// Mknod creates a filesystem node (file, device special file or named pipe) named path +// with attributes specified by mode and dev. +func Mknod(path string, mode uint32, dev int) error { + return unix.Mknod(path, mode, uint64(dev)) +} diff --git a/pkg/system/mknod_linux.go b/pkg/system/mknod_linux.go new file mode 100644 index 0000000000000..5c65f8a49c31b --- /dev/null +++ b/pkg/system/mknod_linux.go @@ -0,0 +1,11 @@ +package system // import "github.com/docker/docker/pkg/system" + +import ( + "golang.org/x/sys/unix" +) + +// Mknod creates a filesystem node (file, device special file or named pipe) named path +// with attributes specified by mode and dev. +func Mknod(path string, mode uint32, dev int) error { + return unix.Mknod(path, mode, dev) +} diff --git a/pkg/system/path.go b/pkg/system/path.go index a3d957afab742..4d81906b9d247 100644 --- a/pkg/system/path.go +++ b/pkg/system/path.go @@ -1,32 +1,27 @@ package system // import "github.com/docker/docker/pkg/system" -import ( - "fmt" - "path/filepath" - "runtime" - "strings" - - "github.com/containerd/continuity/pathdriver" -) - const defaultUnixPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" // DefaultPathEnv is unix style list of directories to search for // executables. Each directory is separated from the next by a colon // ':' character . +// For Windows containers, an empty string is returned as the default +// path will be set by the container, and Docker has no context of what the +// default path should be. func DefaultPathEnv(os string) string { - if runtime.GOOS == "windows" { - if os != runtime.GOOS { - return defaultUnixPathEnv - } - // Deliberately empty on Windows containers on Windows as the default path will be set by - // the container. Docker has no context of what the default path should be. + if os == "windows" { return "" } return defaultUnixPathEnv } +// PathVerifier defines the subset of a PathDriver that CheckSystemDriveAndRemoveDriveLetter +// actually uses in order to avoid system depending on containerd/continuity. +type PathVerifier interface { + IsAbs(string) bool +} + // CheckSystemDriveAndRemoveDriveLetter verifies that a path, if it includes a drive letter, // is the system drive. // On Linux: this is a no-op. @@ -42,19 +37,6 @@ func DefaultPathEnv(os string) string { // a --> a // /a --> \a // d:\ --> Fail -func CheckSystemDriveAndRemoveDriveLetter(path string, driver pathdriver.PathDriver) (string, error) { - if runtime.GOOS != "windows" || LCOWSupported() { - return path, nil - } - - if len(path) == 2 && string(path[1]) == ":" { - return "", fmt.Errorf("No relative path specified in %q", path) - } - if !driver.IsAbs(path) || len(path) < 2 { - return filepath.FromSlash(path), nil - } - if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") { - return "", fmt.Errorf("The specified path is not on the system drive (C:)") - } - return filepath.FromSlash(path[2:]), nil +func CheckSystemDriveAndRemoveDriveLetter(path string, driver PathVerifier) (string, error) { + return checkSystemDriveAndRemoveDriveLetter(path, driver) } diff --git a/pkg/system/path_unix.go b/pkg/system/path_unix.go index b0b93196a15c5..197a37a219d18 100644 --- a/pkg/system/path_unix.go +++ b/pkg/system/path_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package system // import "github.com/docker/docker/pkg/system" @@ -8,3 +9,9 @@ package system // import "github.com/docker/docker/pkg/system" func GetLongPathName(path string) (string, error) { return path, nil } + +// checkSystemDriveAndRemoveDriveLetter is the non-Windows implementation +// of CheckSystemDriveAndRemoveDriveLetter +func checkSystemDriveAndRemoveDriveLetter(path string, driver PathVerifier) (string, error) { + return path, nil +} diff --git a/pkg/system/path_windows.go b/pkg/system/path_windows.go index 188f2c2957a48..7d375b0ddc3f3 100644 --- a/pkg/system/path_windows.go +++ b/pkg/system/path_windows.go @@ -1,24 +1,48 @@ package system // import "github.com/docker/docker/pkg/system" -import "syscall" +import ( + "fmt" + "path/filepath" + "strings" + + "golang.org/x/sys/windows" +) // GetLongPathName converts Windows short pathnames to full pathnames. // For example C:\Users\ADMIN~1 --> C:\Users\Administrator. // It is a no-op on non-Windows platforms func GetLongPathName(path string) (string, error) { // See https://groups.google.com/forum/#!topic/golang-dev/1tufzkruoTg - p := syscall.StringToUTF16(path) + p, err := windows.UTF16FromString(path) + if err != nil { + return "", err + } b := p // GetLongPathName says we can reuse buffer - n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b))) + n, err := windows.GetLongPathName(&p[0], &b[0], uint32(len(b))) if err != nil { return "", err } if n > uint32(len(b)) { b = make([]uint16, n) - _, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b))) + _, err = windows.GetLongPathName(&p[0], &b[0], uint32(len(b))) if err != nil { return "", err } } - return syscall.UTF16ToString(b), nil + return windows.UTF16ToString(b), nil +} + +// checkSystemDriveAndRemoveDriveLetter is the Windows implementation +// of CheckSystemDriveAndRemoveDriveLetter +func checkSystemDriveAndRemoveDriveLetter(path string, driver PathVerifier) (string, error) { + if len(path) == 2 && string(path[1]) == ":" { + return "", fmt.Errorf("No relative path specified in %q", path) + } + if !driver.IsAbs(path) || len(path) < 2 { + return filepath.FromSlash(path), nil + } + if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") { + return "", fmt.Errorf("The specified path is not on the system drive (C:)") + } + return filepath.FromSlash(path[2:]), nil } diff --git a/pkg/system/path_windows_test.go b/pkg/system/path_windows_test.go index 974707eb71831..5ba2c84e39bf4 100644 --- a/pkg/system/path_windows_test.go +++ b/pkg/system/path_windows_test.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package system // import "github.com/docker/docker/pkg/system" @@ -12,7 +13,7 @@ import ( func TestCheckSystemDriveAndRemoveDriveLetter(t *testing.T) { // Fails if not C drive. _, err := CheckSystemDriveAndRemoveDriveLetter(`d:\`, pathdriver.LocalPathDriver) - if err == nil || (err != nil && err.Error() != "The specified path is not on the system drive (C:)") { + if err == nil || err.Error() != "The specified path is not on the system drive (C:)" { t.Fatalf("Expected error for d:") } diff --git a/pkg/system/process_unix.go b/pkg/system/process_unix.go index 0195a891b27c0..d2ab9c3d7e03c 100644 --- a/pkg/system/process_unix.go +++ b/pkg/system/process_unix.go @@ -1,8 +1,12 @@ +//go:build linux || freebsd || darwin // +build linux freebsd darwin package system // import "github.com/docker/docker/pkg/system" import ( + "fmt" + "os" + "strings" "syscall" "golang.org/x/sys/unix" @@ -22,3 +26,20 @@ func IsProcessAlive(pid int) bool { func KillProcess(pid int) { unix.Kill(pid, unix.SIGKILL) } + +// IsProcessZombie return true if process has a state with "Z" +// http://man7.org/linux/man-pages/man5/proc.5.html +func IsProcessZombie(pid int) (bool, error) { + statPath := fmt.Sprintf("/proc/%d/stat", pid) + dataBytes, err := os.ReadFile(statPath) + if err != nil { + return false, err + } + data := string(dataBytes) + sdata := strings.SplitN(data, " ", 4) + if len(sdata) >= 3 && sdata[2] == "Z" { + return true, nil + } + + return false, nil +} diff --git a/pkg/system/process_windows.go b/pkg/system/process_windows.go index 4e70c97b18f0f..09bdfa0ca0aa8 100644 --- a/pkg/system/process_windows.go +++ b/pkg/system/process_windows.go @@ -13,6 +13,6 @@ func IsProcessAlive(pid int) bool { func KillProcess(pid int) { p, err := os.FindProcess(pid) if err == nil { - p.Kill() + _ = p.Kill() } } diff --git a/pkg/system/rm.go b/pkg/system/rm.go index b3109918004a6..f2d81597c9dc2 100644 --- a/pkg/system/rm.go +++ b/pkg/system/rm.go @@ -1,3 +1,6 @@ +//go:build !darwin && !windows +// +build !darwin,!windows + package system // import "github.com/docker/docker/pkg/system" import ( @@ -5,7 +8,7 @@ import ( "syscall" "time" - "github.com/docker/docker/pkg/mount" + "github.com/moby/sys/mount" "github.com/pkg/errors" ) @@ -63,12 +66,8 @@ func EnsureRemoveAll(dir string) error { return err } - if mounted, _ := mount.Mounted(pe.Path); mounted { - if e := mount.Unmount(pe.Path); e != nil { - if mounted, _ := mount.Mounted(pe.Path); mounted { - return errors.Wrapf(e, "error while removing %s", dir) - } - } + if e := mount.Unmount(pe.Path); e != nil { + return errors.Wrapf(e, "error while removing %s", dir) } if exitOnErr[pe.Path] == maxRetry { diff --git a/pkg/system/rm_nodarwin_test.go b/pkg/system/rm_nodarwin_test.go new file mode 100644 index 0000000000000..f29137ca879b6 --- /dev/null +++ b/pkg/system/rm_nodarwin_test.go @@ -0,0 +1,37 @@ +//go:build !darwin +// +build !darwin + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "os" + "testing" +) + +func TestEnsureRemoveAllNotExist(t *testing.T) { + // should never return an error for a non-existent path + if err := EnsureRemoveAll("/non/existent/path"); err != nil { + t.Fatal(err) + } +} + +func TestEnsureRemoveAllWithDir(t *testing.T) { + dir, err := os.MkdirTemp("", "test-ensure-removeall-with-dir") + if err != nil { + t.Fatal(err) + } + if err := EnsureRemoveAll(dir); err != nil { + t.Fatal(err) + } +} + +func TestEnsureRemoveAllWithFile(t *testing.T) { + tmp, err := os.CreateTemp("", "test-ensure-removeall-with-dir") + if err != nil { + t.Fatal(err) + } + tmp.Close() + if err := EnsureRemoveAll(tmp.Name()); err != nil { + t.Fatal(err) + } +} diff --git a/pkg/system/rm_test.go b/pkg/system/rm_test.go index 0448aac619de1..f3d0390e394bc 100644 --- a/pkg/system/rm_test.go +++ b/pkg/system/rm_test.go @@ -1,54 +1,26 @@ +//go:build !darwin && !windows +// +build !darwin,!windows + package system // import "github.com/docker/docker/pkg/system" import ( - "io/ioutil" "os" "path/filepath" - "runtime" "testing" "time" - "github.com/docker/docker/pkg/mount" - "gotest.tools/skip" + "github.com/moby/sys/mount" + "gotest.tools/v3/skip" ) -func TestEnsureRemoveAllNotExist(t *testing.T) { - // should never return an error for a non-existent path - if err := EnsureRemoveAll("/non/existent/path"); err != nil { - t.Fatal(err) - } -} - -func TestEnsureRemoveAllWithDir(t *testing.T) { - dir, err := ioutil.TempDir("", "test-ensure-removeall-with-dir") - if err != nil { - t.Fatal(err) - } - if err := EnsureRemoveAll(dir); err != nil { - t.Fatal(err) - } -} - -func TestEnsureRemoveAllWithFile(t *testing.T) { - tmp, err := ioutil.TempFile("", "test-ensure-removeall-with-dir") - if err != nil { - t.Fatal(err) - } - tmp.Close() - if err := EnsureRemoveAll(tmp.Name()); err != nil { - t.Fatal(err) - } -} - func TestEnsureRemoveAllWithMount(t *testing.T) { - skip.If(t, runtime.GOOS == "windows", "mount not supported on Windows") skip.If(t, os.Getuid() != 0, "skipping test that requires root") - dir1, err := ioutil.TempDir("", "test-ensure-removeall-with-dir1") + dir1, err := os.MkdirTemp("", "test-ensure-removeall-with-dir1") if err != nil { t.Fatal(err) } - dir2, err := ioutil.TempDir("", "test-ensure-removeall-with-dir2") + dir2, err := os.MkdirTemp("", "test-ensure-removeall-with-dir2") if err != nil { t.Fatal(err) } @@ -63,7 +35,7 @@ func TestEnsureRemoveAllWithMount(t *testing.T) { t.Fatal(err) } - done := make(chan struct{}) + done := make(chan struct{}, 1) go func() { err = EnsureRemoveAll(dir1) close(done) diff --git a/pkg/system/rm_windows.go b/pkg/system/rm_windows.go new file mode 100644 index 0000000000000..ed9c5dcb8ae9a --- /dev/null +++ b/pkg/system/rm_windows.go @@ -0,0 +1,6 @@ +package system + +import "os" + +// EnsureRemoveAll is an alias to os.RemoveAll on Windows +var EnsureRemoveAll = os.RemoveAll diff --git a/pkg/system/stat_bsd.go b/pkg/system/stat_bsd.go new file mode 100644 index 0000000000000..8e61d820f02bb --- /dev/null +++ b/pkg/system/stat_bsd.go @@ -0,0 +1,16 @@ +//go:build freebsd || netbsd +// +build freebsd netbsd + +package system // import "github.com/docker/docker/pkg/system" + +import "syscall" + +// fromStatT converts a syscall.Stat_t type to a system.Stat_t type +func fromStatT(s *syscall.Stat_t) (*StatT, error) { + return &StatT{size: s.Size, + mode: uint32(s.Mode), + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), + mtim: s.Mtimespec}, nil +} diff --git a/pkg/system/stat_freebsd.go b/pkg/system/stat_freebsd.go deleted file mode 100644 index c1c0ee9f3865c..0000000000000 --- a/pkg/system/stat_freebsd.go +++ /dev/null @@ -1,13 +0,0 @@ -package system // import "github.com/docker/docker/pkg/system" - -import "syscall" - -// fromStatT converts a syscall.Stat_t type to a system.Stat_t type -func fromStatT(s *syscall.Stat_t) (*StatT, error) { - return &StatT{size: s.Size, - mode: uint32(s.Mode), - uid: s.Uid, - gid: s.Gid, - rdev: uint64(s.Rdev), - mtim: s.Mtimespec}, nil -} diff --git a/pkg/system/stat_linux.go b/pkg/system/stat_linux.go index 98c9eb18d18d8..3ac02393f0a0a 100644 --- a/pkg/system/stat_linux.go +++ b/pkg/system/stat_linux.go @@ -8,7 +8,8 @@ func fromStatT(s *syscall.Stat_t) (*StatT, error) { mode: s.Mode, uid: s.Uid, gid: s.Gid, - rdev: s.Rdev, + // the type is 32bit on mips + rdev: uint64(s.Rdev), //nolint: unconvert mtim: s.Mtim}, nil } diff --git a/pkg/system/stat_solaris.go b/pkg/system/stat_solaris.go index 756b92d1e6cf6..6a51ccd64233c 100644 --- a/pkg/system/stat_solaris.go +++ b/pkg/system/stat_solaris.go @@ -5,9 +5,9 @@ import "syscall" // fromStatT converts a syscall.Stat_t type to a system.Stat_t type func fromStatT(s *syscall.Stat_t) (*StatT, error) { return &StatT{size: s.Size, - mode: uint32(s.Mode), + mode: s.Mode, uid: s.Uid, gid: s.Gid, - rdev: uint64(s.Rdev), + rdev: s.Rdev, mtim: s.Mtim}, nil } diff --git a/pkg/system/stat_unix.go b/pkg/system/stat_unix.go index 3d7e2ebbefafe..a45ffddf7500d 100644 --- a/pkg/system/stat_unix.go +++ b/pkg/system/stat_unix.go @@ -1,8 +1,10 @@ +//go:build !windows // +build !windows package system // import "github.com/docker/docker/pkg/system" import ( + "os" "syscall" ) @@ -59,7 +61,7 @@ func (s StatT) IsDir() bool { func Stat(path string) (*StatT, error) { s := &syscall.Stat_t{} if err := syscall.Stat(path, s); err != nil { - return nil, err + return nil, &os.PathError{Op: "Stat", Path: path, Err: err} } return fromStatT(s) } diff --git a/pkg/system/stat_unix_test.go b/pkg/system/stat_unix_test.go index 44e048f2a7230..416b07eaa5d83 100644 --- a/pkg/system/stat_unix_test.go +++ b/pkg/system/stat_unix_test.go @@ -1,3 +1,4 @@ +//go:build linux || freebsd // +build linux freebsd package system // import "github.com/docker/docker/pkg/system" @@ -7,7 +8,7 @@ import ( "syscall" "testing" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) // TestFromStatT tests fromStatT for a tempfile @@ -31,7 +32,8 @@ func TestFromStatT(t *testing.T) { if stat.Gid != s.GID() { t.Fatal("got invalid gid") } - if stat.Rdev != s.Rdev() { + //nolint:unconvert // conversion needed to fix mismatch types on mips64el + if uint64(stat.Rdev) != s.Rdev() { t.Fatal("got invalid rdev") } if stat.Mtim != s.Mtim() { diff --git a/pkg/system/syscall_unix.go b/pkg/system/syscall_unix.go deleted file mode 100644 index 919a412a7b308..0000000000000 --- a/pkg/system/syscall_unix.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build linux freebsd - -package system // import "github.com/docker/docker/pkg/system" - -import "golang.org/x/sys/unix" - -// Unmount is a platform-specific helper function to call -// the unmount syscall. -func Unmount(dest string) error { - return unix.Unmount(dest, 0) -} - -// CommandLineToArgv should not be used on Unix. -// It simply returns commandLine in the only element in the returned array. -func CommandLineToArgv(commandLine string) ([]string, error) { - return []string{commandLine}, nil -} diff --git a/pkg/system/syscall_windows.go b/pkg/system/syscall_windows.go index 4ae92fa6c7d7b..afebed74d7484 100644 --- a/pkg/system/syscall_windows.go +++ b/pkg/system/syscall_windows.go @@ -1,8 +1,6 @@ package system // import "github.com/docker/docker/pkg/system" import ( - "fmt" - "syscall" "unsafe" "github.com/sirupsen/logrus" @@ -10,66 +8,24 @@ import ( ) const ( - OWNER_SECURITY_INFORMATION = 0x00000001 - GROUP_SECURITY_INFORMATION = 0x00000002 - DACL_SECURITY_INFORMATION = 0x00000004 - SACL_SECURITY_INFORMATION = 0x00000008 - LABEL_SECURITY_INFORMATION = 0x00000010 - ATTRIBUTE_SECURITY_INFORMATION = 0x00000020 - SCOPE_SECURITY_INFORMATION = 0x00000040 - PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00000080 - ACCESS_FILTER_SECURITY_INFORMATION = 0x00000100 - BACKUP_SECURITY_INFORMATION = 0x00010000 - PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000 - PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000 - UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000 - UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000 -) - -const ( - SE_UNKNOWN_OBJECT_TYPE = iota - SE_FILE_OBJECT - SE_SERVICE - SE_PRINTER - SE_REGISTRY_KEY - SE_LMSHARE - SE_KERNEL_OBJECT - SE_WINDOW_OBJECT - SE_DS_OBJECT - SE_DS_OBJECT_ALL - SE_PROVIDER_DEFINED_OBJECT - SE_WMIGUID_OBJECT - SE_REGISTRY_WOW64_32KEY -) - -const ( + // Deprecated: use github.com/docker/pkg/idtools.SeTakeOwnershipPrivilege SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege" ) const ( + // Deprecated: use github.com/docker/pkg/idtools.ContainerAdministratorSidString ContainerAdministratorSidString = "S-1-5-93-2-1" - ContainerUserSidString = "S-1-5-93-2-2" + // Deprecated: use github.com/docker/pkg/idtools.ContainerUserSidString + ContainerUserSidString = "S-1-5-93-2-2" ) var ( - ntuserApiset = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0") - modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") - procGetVersionExW = modkernel32.NewProc("GetVersionExW") - procGetProductInfo = modkernel32.NewProc("GetProductInfo") - procSetNamedSecurityInfo = modadvapi32.NewProc("SetNamedSecurityInfoW") - procGetSecurityDescriptorDacl = modadvapi32.NewProc("GetSecurityDescriptorDacl") + ntuserApiset = windows.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0") + procGetVersionExW = modkernel32.NewProc("GetVersionExW") ) -// OSVersion is a wrapper for Windows version information -// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx -type OSVersion struct { - Version uint32 - MajorVersion uint8 - MinorVersion uint8 - Build uint16 -} - -// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa +// TODO: use golang.org/x/sys/windows.OsVersionInfoEx (needs OSVersionInfoSize to be exported) type osVersionInfoEx struct { OSVersionInfoSize uint32 MajorVersion uint32 @@ -84,85 +40,21 @@ type osVersionInfoEx struct { Reserve byte } -// GetOSVersion gets the operating system version on Windows. Note that -// docker.exe must be manifested to get the correct version information. -func GetOSVersion() OSVersion { - var err error - osv := OSVersion{} - osv.Version, err = windows.GetVersion() - if err != nil { - // GetVersion never fails. - panic(err) - } - osv.MajorVersion = uint8(osv.Version & 0xFF) - osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF) - osv.Build = uint16(osv.Version >> 16) - return osv -} - -func (osv OSVersion) ToString() string { - return fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.Build) -} - -// IsWindowsClient returns true if the SKU is client -// @engine maintainers - this function should not be removed or modified as it -// is used to enforce licensing restrictions on Windows. +// IsWindowsClient returns true if the SKU is client. It returns false on +// Windows server, or if an error occurred when making the GetVersionExW +// syscall. func IsWindowsClient() bool { osviex := &osVersionInfoEx{OSVersionInfoSize: 284} r1, _, err := procGetVersionExW.Call(uintptr(unsafe.Pointer(osviex))) if r1 == 0 { - logrus.Warnf("GetVersionExW failed - assuming server SKU: %v", err) + logrus.WithError(err).Warn("GetVersionExW failed - assuming server SKU") return false } - const verNTWorkstation = 0x00000001 + // VER_NT_WORKSTATION, see https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa + const verNTWorkstation = 0x00000001 // VER_NT_WORKSTATION return osviex.ProductType == verNTWorkstation } -// IsIoTCore returns true if the currently running image is based off of -// Windows 10 IoT Core. -// @engine maintainers - this function should not be removed or modified as it -// is used to enforce licensing restrictions on Windows. -func IsIoTCore() bool { - var returnedProductType uint32 - r1, _, err := procGetProductInfo.Call(6, 1, 0, 0, uintptr(unsafe.Pointer(&returnedProductType))) - if r1 == 0 { - logrus.Warnf("GetProductInfo failed - assuming this is not IoT: %v", err) - return false - } - const productIoTUAP = 0x0000007B - const productIoTUAPCommercial = 0x00000083 - return returnedProductType == productIoTUAP || returnedProductType == productIoTUAPCommercial -} - -// Unmount is a platform-specific helper function to call -// the unmount syscall. Not supported on Windows -func Unmount(dest string) error { - return nil -} - -// CommandLineToArgv wraps the Windows syscall to turn a commandline into an argument array. -func CommandLineToArgv(commandLine string) ([]string, error) { - var argc int32 - - argsPtr, err := windows.UTF16PtrFromString(commandLine) - if err != nil { - return nil, err - } - - argv, err := windows.CommandLineToArgv(argsPtr, &argc) - if err != nil { - return nil, err - } - defer windows.LocalFree(windows.Handle(uintptr(unsafe.Pointer(argv)))) - - newArgs := make([]string, argc) - for i, v := range (*argv)[:argc] { - newArgs[i] = string(windows.UTF16ToString((*v)[:])) - } - - return newArgs, nil -} - // HasWin32KSupport determines whether containers that depend on win32k can // run on this machine. Win32k is the driver used to implement windowing. func HasWin32KSupport() bool { @@ -171,23 +63,3 @@ func HasWin32KSupport() bool { // APIs. return ntuserApiset.Load() == nil } - -func SetNamedSecurityInfo(objectName *uint16, objectType uint32, securityInformation uint32, sidOwner *windows.SID, sidGroup *windows.SID, dacl *byte, sacl *byte) (result error) { - r0, _, _ := syscall.Syscall9(procSetNamedSecurityInfo.Addr(), 7, uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(sidOwner)), uintptr(unsafe.Pointer(sidGroup)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), 0, 0) - if r0 != 0 { - result = syscall.Errno(r0) - } - return -} - -func GetSecurityDescriptorDacl(securityDescriptor *byte, daclPresent *uint32, dacl **byte, daclDefaulted *uint32) (result error) { - r1, _, e1 := syscall.Syscall6(procGetSecurityDescriptorDacl.Addr(), 4, uintptr(unsafe.Pointer(securityDescriptor)), uintptr(unsafe.Pointer(daclPresent)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(daclDefaulted)), 0, 0) - if r1 == 0 { - if e1 != 0 { - result = syscall.Errno(e1) - } else { - result = syscall.EINVAL - } - } - return -} diff --git a/pkg/system/umask.go b/pkg/system/umask.go index 9912a2babb3fe..d4a15cbedc33b 100644 --- a/pkg/system/umask.go +++ b/pkg/system/umask.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package system // import "github.com/docker/docker/pkg/system" diff --git a/pkg/system/utimes_freebsd.go b/pkg/system/utimes_freebsd.go deleted file mode 100644 index ed1b9fad59bbf..0000000000000 --- a/pkg/system/utimes_freebsd.go +++ /dev/null @@ -1,24 +0,0 @@ -package system // import "github.com/docker/docker/pkg/system" - -import ( - "syscall" - "unsafe" - - "golang.org/x/sys/unix" -) - -// LUtimesNano is used to change access and modification time of the specified path. -// It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm. -func LUtimesNano(path string, ts []syscall.Timespec) error { - var _path *byte - _path, err := unix.BytePtrFromString(path) - if err != nil { - return err - } - - if _, _, err := unix.Syscall(unix.SYS_LUTIMES, uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), 0); err != 0 && err != unix.ENOSYS { - return err - } - - return nil -} diff --git a/pkg/system/utimes_linux.go b/pkg/system/utimes_linux.go deleted file mode 100644 index 0afe854589f6a..0000000000000 --- a/pkg/system/utimes_linux.go +++ /dev/null @@ -1,25 +0,0 @@ -package system // import "github.com/docker/docker/pkg/system" - -import ( - "syscall" - "unsafe" - - "golang.org/x/sys/unix" -) - -// LUtimesNano is used to change access and modification time of the specified path. -// It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm. -func LUtimesNano(path string, ts []syscall.Timespec) error { - atFdCwd := unix.AT_FDCWD - - var _path *byte - _path, err := unix.BytePtrFromString(path) - if err != nil { - return err - } - if _, _, err := unix.Syscall6(unix.SYS_UTIMENSAT, uintptr(atFdCwd), uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), unix.AT_SYMLINK_NOFOLLOW, 0, 0); err != 0 && err != unix.ENOSYS { - return err - } - - return nil -} diff --git a/pkg/system/utimes_unix.go b/pkg/system/utimes_unix.go new file mode 100644 index 0000000000000..2768750a00bfb --- /dev/null +++ b/pkg/system/utimes_unix.go @@ -0,0 +1,25 @@ +//go:build linux || freebsd +// +build linux freebsd + +package system // import "github.com/docker/docker/pkg/system" + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +// LUtimesNano is used to change access and modification time of the specified path. +// It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm. +func LUtimesNano(path string, ts []syscall.Timespec) error { + uts := []unix.Timespec{ + unix.NsecToTimespec(syscall.TimespecToNsec(ts[0])), + unix.NsecToTimespec(syscall.TimespecToNsec(ts[1])), + } + err := unix.UtimesNanoAt(unix.AT_FDCWD, path, uts, unix.AT_SYMLINK_NOFOLLOW) + if err != nil && err != unix.ENOSYS { + return err + } + + return nil +} diff --git a/pkg/system/utimes_unix_test.go b/pkg/system/utimes_unix_test.go index cc0e7cbf1f1cf..30482b7c074e3 100644 --- a/pkg/system/utimes_unix_test.go +++ b/pkg/system/utimes_unix_test.go @@ -1,9 +1,9 @@ +//go:build linux || freebsd // +build linux freebsd package system // import "github.com/docker/docker/pkg/system" import ( - "io/ioutil" "os" "path/filepath" "syscall" @@ -12,13 +12,13 @@ import ( // prepareFiles creates files for testing in the temp directory func prepareFiles(t *testing.T) (string, string, string, string) { - dir, err := ioutil.TempDir("", "docker-system-test") + dir, err := os.MkdirTemp("", "docker-system-test") if err != nil { t.Fatal(err) } file := filepath.Join(dir, "exist") - if err := ioutil.WriteFile(file, []byte("hello"), 0644); err != nil { + if err := os.WriteFile(file, []byte("hello"), 0644); err != nil { t.Fatal(err) } diff --git a/pkg/system/utimes_unsupported.go b/pkg/system/utimes_unsupported.go index 095e072e1df34..bfed4af0325eb 100644 --- a/pkg/system/utimes_unsupported.go +++ b/pkg/system/utimes_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !freebsd // +build !linux,!freebsd package system // import "github.com/docker/docker/pkg/system" diff --git a/pkg/system/xattrs_linux.go b/pkg/system/xattrs_linux.go index 66d4895b27ab6..95b609fe7a8bf 100644 --- a/pkg/system/xattrs_linux.go +++ b/pkg/system/xattrs_linux.go @@ -6,16 +6,24 @@ import "golang.org/x/sys/unix" // and associated with the given path in the file system. // It will returns a nil slice and nil error if the xattr is not set. func Lgetxattr(path string, attr string) ([]byte, error) { + // Start with a 128 length byte array dest := make([]byte, 128) sz, errno := unix.Lgetxattr(path, attr, dest) - if errno == unix.ENODATA { - return nil, nil - } - if errno == unix.ERANGE { + + for errno == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size + sz, errno = unix.Lgetxattr(path, attr, []byte{}) + if errno != nil { + return nil, errno + } dest = make([]byte, sz) sz, errno = unix.Lgetxattr(path, attr, dest) } - if errno != nil { + + switch { + case errno == unix.ENODATA: + return nil, nil + case errno != nil: return nil, errno } diff --git a/pkg/system/xattrs_unsupported.go b/pkg/system/xattrs_unsupported.go index d780a90cd383c..b165a5dbfe93d 100644 --- a/pkg/system/xattrs_unsupported.go +++ b/pkg/system/xattrs_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package system // import "github.com/docker/docker/pkg/system" diff --git a/pkg/tailfile/tailfile.go b/pkg/tailfile/tailfile.go index c82fe603f663c..90f5a7f360f22 100644 --- a/pkg/tailfile/tailfile.go +++ b/pkg/tailfile/tailfile.go @@ -18,7 +18,7 @@ var eol = []byte("\n") // ErrNonPositiveLinesNumber is an error returned if the lines number was negative. var ErrNonPositiveLinesNumber = errors.New("The number of lines to extract from the file must be positive") -//TailFile returns last n lines of the passed in file. +// TailFile returns last n lines of the passed in file. func TailFile(f *os.File, n int) ([][]byte, error) { size, err := f.Seek(0, io.SeekEnd) if err != nil { @@ -127,7 +127,6 @@ type scanner struct { delim []byte err error idx int - done bool } func (s *scanner) Start(ctx context.Context) int64 { diff --git a/pkg/tailfile/tailfile_test.go b/pkg/tailfile/tailfile_test.go index 4dde90f9e3a30..15c8acfe01762 100644 --- a/pkg/tailfile/tailfile_test.go +++ b/pkg/tailfile/tailfile_test.go @@ -6,16 +6,15 @@ import ( "context" "fmt" "io" - "io/ioutil" "os" "strings" "testing" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) func TestTailFile(t *testing.T) { - f, err := ioutil.TempFile("", "tail-test") + f, err := os.CreateTemp("", "tail-test") if err != nil { t.Fatal(err) } @@ -69,7 +68,7 @@ truncated line`) } func TestTailFileManyLines(t *testing.T) { - f, err := ioutil.TempFile("", "tail-test") + f, err := os.CreateTemp("", "tail-test") if err != nil { t.Fatal(err) } @@ -100,7 +99,7 @@ truncated line`) } func TestTailEmptyFile(t *testing.T) { - f, err := ioutil.TempFile("", "tail-test") + f, err := os.CreateTemp("", "tail-test") if err != nil { t.Fatal(err) } @@ -116,7 +115,7 @@ func TestTailEmptyFile(t *testing.T) { } func TestTailNegativeN(t *testing.T) { - f, err := ioutil.TempFile("", "tail-test") + f, err := os.CreateTemp("", "tail-test") if err != nil { t.Fatal(err) } @@ -140,7 +139,7 @@ truncated line`) } func BenchmarkTail(b *testing.B) { - f, err := ioutil.TempFile("", "tail-test") + f, err := os.CreateTemp("", "tail-test") if err != nil { b.Fatal(err) } @@ -232,11 +231,11 @@ func TestNewTailReader(t *testing.T) { assert.Assert(t, lines == 0) return } - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, lines == i, "%d -- %d", lines, i) - b, err := ioutil.ReadAll(tr) - assert.Assert(t, err) + b, err := io.ReadAll(tr) + assert.NilError(t, err) expectLines := test.data[len(test.data)-i:] assert.Check(t, len(expectLines) == i) @@ -260,10 +259,10 @@ func TestNewTailReader(t *testing.T) { return } - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, lines == len(test.data), "%d -- %d", lines, len(test.data)) - b, err := ioutil.ReadAll(tr) - assert.Assert(t, err) + b, err := io.ReadAll(tr) + assert.NilError(t, err) assert.Check(t, bytes.Equal(b, []byte(s)), "\n%v\n%v", b, []byte(s)) }) }) @@ -273,16 +272,16 @@ func TestNewTailReader(t *testing.T) { t.Run("truncated last line", func(t *testing.T) { t.Run("more than available", func(t *testing.T) { tail, nLines, err := NewTailReader(ctx, strings.NewReader("a\nb\nextra"), 3) - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, nLines == 2, nLines) rdr := bufio.NewReader(tail) data, _, err := rdr.ReadLine() - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, string(data) == "a", string(data)) data, _, err = rdr.ReadLine() - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, string(data) == "b", string(data)) _, _, err = rdr.ReadLine() @@ -292,16 +291,16 @@ func TestNewTailReader(t *testing.T) { t.Run("truncated last line", func(t *testing.T) { t.Run("exact", func(t *testing.T) { tail, nLines, err := NewTailReader(ctx, strings.NewReader("a\nb\nextra"), 2) - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, nLines == 2, nLines) rdr := bufio.NewReader(tail) data, _, err := rdr.ReadLine() - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, string(data) == "a", string(data)) data, _, err = rdr.ReadLine() - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, string(data) == "b", string(data)) _, _, err = rdr.ReadLine() @@ -312,12 +311,12 @@ func TestNewTailReader(t *testing.T) { t.Run("truncated last line", func(t *testing.T) { t.Run("one line", func(t *testing.T) { tail, nLines, err := NewTailReader(ctx, strings.NewReader("a\nb\nextra"), 1) - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, nLines == 1, nLines) rdr := bufio.NewReader(tail) data, _, err := rdr.ReadLine() - assert.Assert(t, err) + assert.NilError(t, err) assert.Check(t, string(data) == "b", string(data)) _, _, err = rdr.ReadLine() diff --git a/pkg/tarsum/builder_context_test.go b/pkg/tarsum/builder_context_test.go index 86adb442d609e..0d69d3127b250 100644 --- a/pkg/tarsum/builder_context_test.go +++ b/pkg/tarsum/builder_context_test.go @@ -2,7 +2,6 @@ package tarsum // import "github.com/docker/docker/pkg/tarsum" import ( "io" - "io/ioutil" "os" "testing" ) @@ -22,7 +21,7 @@ func TestTarSumRemoveNonExistent(t *testing.T) { } // Read and discard bytes so that it populates sums - _, err = io.Copy(ioutil.Discard, ts) + _, err = io.Copy(io.Discard, ts) if err != nil { t.Errorf("failed to read from %s: %s", filename, err) } @@ -52,7 +51,7 @@ func TestTarSumRemove(t *testing.T) { } // Read and discard bytes so that it populates sums - _, err = io.Copy(ioutil.Discard, ts) + _, err = io.Copy(io.Discard, ts) if err != nil { t.Errorf("failed to read from %s: %s", filename, err) } diff --git a/pkg/tarsum/tarsum_test.go b/pkg/tarsum/tarsum_test.go index 97ece8506cb24..1945f4a5cfe8f 100644 --- a/pkg/tarsum/tarsum_test.go +++ b/pkg/tarsum/tarsum_test.go @@ -4,21 +4,20 @@ import ( "archive/tar" "bytes" "compress/gzip" - "crypto/md5" + "crypto/md5" // #nosec G501 "crypto/rand" - "crypto/sha1" + "crypto/sha1" // #nosec G505 "crypto/sha256" "crypto/sha512" "encoding/hex" "fmt" "io" - "io/ioutil" "os" "strings" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) type testLayer struct { @@ -127,7 +126,7 @@ func sizedTar(opts sizedOptions) io.Reader { err error ) if opts.realFile { - fh, err = ioutil.TempFile("", "tarsum") + fh, err = os.CreateTemp("", "tarsum") if err != nil { return nil } @@ -366,7 +365,7 @@ func TestTarSums(t *testing.T) { } // Read and discard remaining bytes - _, err = io.Copy(ioutil.Discard, ts) + _, err = io.Copy(io.Discard, ts) if err != nil { t.Errorf("failed to copy from %s: %s", layer.filename, err) continue @@ -380,7 +379,7 @@ func TestTarSums(t *testing.T) { } defer jfh.Close() - buf, err := ioutil.ReadAll(jfh) + buf, err := io.ReadAll(jfh) if err != nil { t.Errorf("failed to readAll %s: %s", layer.jsonfile, err) continue @@ -545,7 +544,7 @@ func renderSumForHeader(v Version, h *tar.Header, data []byte) (string, error) { if err != nil { return "", err } - if _, err = io.Copy(ioutil.Discard, tr); err != nil { + if _, err = io.Copy(io.Discard, tr); err != nil { return "", err } } @@ -578,7 +577,7 @@ func Benchmark9kTar(b *testing.B) { b.Error(err) return } - io.Copy(ioutil.Discard, ts) + io.Copy(io.Discard, ts) ts.Sum(nil) } } @@ -609,7 +608,7 @@ func Benchmark9kTarGzip(b *testing.B) { b.Error(err) return } - io.Copy(ioutil.Discard, ts) + io.Copy(io.Discard, ts) ts.Sum(nil) } } @@ -651,7 +650,7 @@ func benchmarkTar(b *testing.B, opts sizedOptions, isGzip bool) { b.Error(err) return } - io.Copy(ioutil.Discard, ts) + io.Copy(io.Discard, ts) ts.Sum(nil) fh.Seek(0, 0) } diff --git a/pkg/term/ascii_test.go b/pkg/term/ascii_test.go deleted file mode 100644 index 665ab1552ff04..0000000000000 --- a/pkg/term/ascii_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package term // import "github.com/docker/docker/pkg/term" - -import ( - "testing" - - "gotest.tools/assert" - is "gotest.tools/assert/cmp" -) - -func TestToBytes(t *testing.T) { - codes, err := ToBytes("ctrl-a,a") - assert.NilError(t, err) - assert.Check(t, is.DeepEqual([]byte{1, 97}, codes)) - - _, err = ToBytes("shift-z") - assert.Check(t, is.ErrorContains(err, "")) - - codes, err = ToBytes("ctrl-@,ctrl-[,~,ctrl-o") - assert.NilError(t, err) - assert.Check(t, is.DeepEqual([]byte{0, 27, 126, 15}, codes)) - - codes, err = ToBytes("DEL,+") - assert.NilError(t, err) - assert.Check(t, is.DeepEqual([]byte{127, 43}, codes)) -} diff --git a/pkg/term/deprecated.go b/pkg/term/deprecated.go new file mode 100644 index 0000000000000..4d18168fc28fb --- /dev/null +++ b/pkg/term/deprecated.go @@ -0,0 +1,84 @@ +// Package term provides structures and helper functions to work with +// terminal (state, sizes). +// +// Deprecated: use github.com/moby/term instead +package term // import "github.com/docker/docker/pkg/term" + +import ( + "github.com/moby/term" +) + +// EscapeError is special error which returned by a TTY proxy reader's Read() +// method in case its detach escape sequence is read. +// Deprecated: use github.com/moby/term.EscapeError +type EscapeError = term.EscapeError + +// State represents the state of the terminal. +// Deprecated: use github.com/moby/term.State +type State = term.State + +// Winsize represents the size of the terminal window. +// Deprecated: use github.com/moby/term.Winsize +type Winsize = term.Winsize + +var ( + // ASCII list the possible supported ASCII key sequence + ASCII = term.ASCII + + // ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code. + // Deprecated: use github.com/moby/term.ToBytes + ToBytes = term.ToBytes + + // StdStreams returns the standard streams (stdin, stdout, stderr). + // Deprecated: use github.com/moby/term.StdStreams + StdStreams = term.StdStreams + + // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. + // Deprecated: use github.com/moby/term.GetFdInfo + GetFdInfo = term.GetFdInfo + + // GetWinsize returns the window size based on the specified file descriptor. + // Deprecated: use github.com/moby/term.GetWinsize + GetWinsize = term.GetWinsize + + // IsTerminal returns true if the given file descriptor is a terminal. + // Deprecated: use github.com/moby/term.IsTerminal + IsTerminal = term.IsTerminal + + // RestoreTerminal restores the terminal connected to the given file descriptor + // to a previous state. + // Deprecated: use github.com/moby/term.RestoreTerminal + RestoreTerminal = term.RestoreTerminal + + // SaveState saves the state of the terminal connected to the given file descriptor. + // Deprecated: use github.com/moby/term.SaveState + SaveState = term.SaveState + + // DisableEcho applies the specified state to the terminal connected to the file + // descriptor, with echo disabled. + // Deprecated: use github.com/moby/term.DisableEcho + DisableEcho = term.DisableEcho + + // SetRawTerminal puts the terminal connected to the given file descriptor into + // raw mode and returns the previous state. On UNIX, this puts both the input + // and output into raw mode. On Windows, it only puts the input into raw mode. + // Deprecated: use github.com/moby/term.SetRawTerminal + SetRawTerminal = term.SetRawTerminal + + // SetRawTerminalOutput puts the output of terminal connected to the given file + // descriptor into raw mode. On UNIX, this does nothing and returns nil for the + // state. On Windows, it disables LF -> CRLF translation. + // Deprecated: use github.com/moby/term.SetRawTerminalOutput + SetRawTerminalOutput = term.SetRawTerminalOutput + + // MakeRaw puts the terminal connected to the given file descriptor into raw + // mode and returns the previous state of the terminal so that it can be restored. + // Deprecated: use github.com/moby/term.MakeRaw + MakeRaw = term.MakeRaw + + // NewEscapeProxy returns a new TTY proxy reader which wraps the given reader + // and detects when the specified escape keys are read, in which case the Read + // method will return an error of type EscapeError. + // Deprecated: use github.com/moby/term.NewEscapeProxy + NewEscapeProxy = term.NewEscapeProxy +) diff --git a/pkg/term/deprecated_unix.go b/pkg/term/deprecated_unix.go new file mode 100644 index 0000000000000..2d3641dc158e9 --- /dev/null +++ b/pkg/term/deprecated_unix.go @@ -0,0 +1,21 @@ +//go:build !windows +// +build !windows + +package term // import "github.com/docker/docker/pkg/term" + +import ( + "github.com/moby/term" +) + +// Termios is the Unix API for terminal I/O. +// Deprecated: use github.com/moby/term.Termios +type Termios = term.Termios + +var ( + // ErrInvalidState is returned if the state of the terminal is invalid. + ErrInvalidState = term.ErrInvalidState + + // SetWinsize tries to set the specified window size for the specified file descriptor. + // Deprecated: use github.com/moby/term.GetWinsize + SetWinsize = term.SetWinsize +) diff --git a/pkg/term/proxy.go b/pkg/term/proxy.go deleted file mode 100644 index da733e58484c3..0000000000000 --- a/pkg/term/proxy.go +++ /dev/null @@ -1,78 +0,0 @@ -package term // import "github.com/docker/docker/pkg/term" - -import ( - "io" -) - -// EscapeError is special error which returned by a TTY proxy reader's Read() -// method in case its detach escape sequence is read. -type EscapeError struct{} - -func (EscapeError) Error() string { - return "read escape sequence" -} - -// escapeProxy is used only for attaches with a TTY. It is used to proxy -// stdin keypresses from the underlying reader and look for the passed in -// escape key sequence to signal a detach. -type escapeProxy struct { - escapeKeys []byte - escapeKeyPos int - r io.Reader -} - -// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader -// and detects when the specified escape keys are read, in which case the Read -// method will return an error of type EscapeError. -func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader { - return &escapeProxy{ - escapeKeys: escapeKeys, - r: r, - } -} - -func (r *escapeProxy) Read(buf []byte) (int, error) { - nr, err := r.r.Read(buf) - - if len(r.escapeKeys) == 0 { - return nr, err - } - - preserve := func() { - // this preserves the original key presses in the passed in buffer - nr += r.escapeKeyPos - preserve := make([]byte, 0, r.escapeKeyPos+len(buf)) - preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...) - preserve = append(preserve, buf...) - r.escapeKeyPos = 0 - copy(buf[0:nr], preserve) - } - - if nr != 1 || err != nil { - if r.escapeKeyPos > 0 { - preserve() - } - return nr, err - } - - if buf[0] != r.escapeKeys[r.escapeKeyPos] { - if r.escapeKeyPos > 0 { - preserve() - } - return nr, nil - } - - if r.escapeKeyPos == len(r.escapeKeys)-1 { - return 0, EscapeError{} - } - - // Looks like we've got an escape key, but we need to match again on the next - // read. - // Store the current escape key we found so we can look for the next one on - // the next read. - // Since this is an escape key, make sure we don't let the caller read it - // If later on we find that this is not the escape sequence, we'll add the - // keys back - r.escapeKeyPos++ - return nr - r.escapeKeyPos, nil -} diff --git a/pkg/term/proxy_test.go b/pkg/term/proxy_test.go deleted file mode 100644 index df588fe15bb00..0000000000000 --- a/pkg/term/proxy_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package term // import "github.com/docker/docker/pkg/term" - -import ( - "bytes" - "fmt" - "testing" - - "gotest.tools/assert" - is "gotest.tools/assert/cmp" -) - -func TestEscapeProxyRead(t *testing.T) { - escapeKeys, _ := ToBytes("") - keys, _ := ToBytes("a") - reader := NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - buf := make([]byte, len(keys)) - nr, err := reader.Read(buf) - assert.NilError(t, err) - assert.Equal(t, nr, len(keys), fmt.Sprintf("nr %d should be equal to the number of %d", nr, len(keys))) - assert.DeepEqual(t, keys, buf) - - keys, _ = ToBytes("a,b,c") - reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - buf = make([]byte, len(keys)) - nr, err = reader.Read(buf) - assert.NilError(t, err) - assert.Equal(t, nr, len(keys), fmt.Sprintf("nr %d should be equal to the number of %d", nr, len(keys))) - assert.DeepEqual(t, keys, buf) - - keys, _ = ToBytes("") - reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - buf = make([]byte, len(keys)) - nr, err = reader.Read(buf) - assert.Assert(t, is.ErrorContains(err, ""), "Should throw error when no keys are to read") - assert.Equal(t, nr, 0, "nr should be zero") - assert.Check(t, is.Len(keys, 0)) - assert.Check(t, is.Len(buf, 0)) - - escapeKeys, _ = ToBytes("DEL") - keys, _ = ToBytes("a,b,c,+") - reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - buf = make([]byte, len(keys)) - nr, err = reader.Read(buf) - assert.NilError(t, err) - assert.Equal(t, nr, len(keys), fmt.Sprintf("nr %d should be equal to the number of %d", nr, len(keys))) - assert.DeepEqual(t, keys, buf) - - keys, _ = ToBytes("") - reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - buf = make([]byte, len(keys)) - nr, err = reader.Read(buf) - assert.Assert(t, is.ErrorContains(err, ""), "Should throw error when no keys are to read") - assert.Equal(t, nr, 0, "nr should be zero") - assert.Check(t, is.Len(keys, 0)) - assert.Check(t, is.Len(buf, 0)) - - escapeKeys, _ = ToBytes("ctrl-x,ctrl-@") - keys, _ = ToBytes("DEL") - reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - buf = make([]byte, len(keys)) - nr, err = reader.Read(buf) - assert.NilError(t, err) - assert.Equal(t, nr, 1, fmt.Sprintf("nr %d should be equal to the number of 1", nr)) - assert.DeepEqual(t, keys, buf) - - escapeKeys, _ = ToBytes("ctrl-c") - keys, _ = ToBytes("ctrl-c") - reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - buf = make([]byte, len(keys)) - nr, err = reader.Read(buf) - assert.Error(t, err, "read escape sequence") - assert.Equal(t, nr, 0, "nr should be equal to 0") - assert.DeepEqual(t, keys, buf) - - escapeKeys, _ = ToBytes("ctrl-c,ctrl-z") - keys, _ = ToBytes("ctrl-c,ctrl-z") - reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - buf = make([]byte, 1) - nr, err = reader.Read(buf) - assert.NilError(t, err) - assert.Equal(t, nr, 0, "nr should be equal to 0") - assert.DeepEqual(t, keys[0:1], buf) - nr, err = reader.Read(buf) - assert.Error(t, err, "read escape sequence") - assert.Equal(t, nr, 0, "nr should be equal to 0") - assert.DeepEqual(t, keys[1:], buf) - - escapeKeys, _ = ToBytes("ctrl-c,ctrl-z") - keys, _ = ToBytes("ctrl-c,DEL,+") - reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - buf = make([]byte, 1) - nr, err = reader.Read(buf) - assert.NilError(t, err) - assert.Equal(t, nr, 0, "nr should be equal to 0") - assert.DeepEqual(t, keys[0:1], buf) - buf = make([]byte, len(keys)) - nr, err = reader.Read(buf) - assert.NilError(t, err) - assert.Equal(t, nr, len(keys), fmt.Sprintf("nr should be equal to %d", len(keys))) - assert.DeepEqual(t, keys, buf) - - escapeKeys, _ = ToBytes("ctrl-c,ctrl-z") - keys, _ = ToBytes("ctrl-c,DEL") - reader = NewEscapeProxy(bytes.NewReader(keys), escapeKeys) - buf = make([]byte, 1) - nr, err = reader.Read(buf) - assert.NilError(t, err) - assert.Equal(t, nr, 0, "nr should be equal to 0") - assert.DeepEqual(t, keys[0:1], buf) - buf = make([]byte, len(keys)) - nr, err = reader.Read(buf) - assert.NilError(t, err) - assert.Equal(t, nr, len(keys), fmt.Sprintf("nr should be equal to %d", len(keys))) - assert.DeepEqual(t, keys, buf) -} diff --git a/pkg/term/tc.go b/pkg/term/tc.go deleted file mode 100644 index 01bcaa8abb187..0000000000000 --- a/pkg/term/tc.go +++ /dev/null @@ -1,20 +0,0 @@ -// +build !windows - -package term // import "github.com/docker/docker/pkg/term" - -import ( - "syscall" - "unsafe" - - "golang.org/x/sys/unix" -) - -func tcget(fd uintptr, p *Termios) syscall.Errno { - _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(p))) - return err -} - -func tcset(fd uintptr, p *Termios) syscall.Errno { - _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(p))) - return err -} diff --git a/pkg/term/term_linux_test.go b/pkg/term/term_linux_test.go deleted file mode 100644 index 272395a10ea46..0000000000000 --- a/pkg/term/term_linux_test.go +++ /dev/null @@ -1,117 +0,0 @@ -//+build linux - -package term // import "github.com/docker/docker/pkg/term" - -import ( - "io/ioutil" - "os" - "testing" - - "github.com/google/go-cmp/cmp" - "gotest.tools/assert" -) - -// RequiresRoot skips tests that require root, unless the test.root flag has -// been set -func RequiresRoot(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("skipping test that requires root") - return - } -} - -func newTtyForTest(t *testing.T) (*os.File, error) { - RequiresRoot(t) - return os.OpenFile("/dev/tty", os.O_RDWR, os.ModeDevice) -} - -func newTempFile() (*os.File, error) { - return ioutil.TempFile(os.TempDir(), "temp") -} - -func TestGetWinsize(t *testing.T) { - tty, err := newTtyForTest(t) - defer tty.Close() - assert.NilError(t, err) - winSize, err := GetWinsize(tty.Fd()) - assert.NilError(t, err) - assert.Assert(t, winSize != nil) - - newSize := Winsize{Width: 200, Height: 200, x: winSize.x, y: winSize.y} - err = SetWinsize(tty.Fd(), &newSize) - assert.NilError(t, err) - winSize, err = GetWinsize(tty.Fd()) - assert.NilError(t, err) - assert.DeepEqual(t, *winSize, newSize, cmpWinsize) -} - -var cmpWinsize = cmp.AllowUnexported(Winsize{}) - -func TestSetWinsize(t *testing.T) { - tty, err := newTtyForTest(t) - defer tty.Close() - assert.NilError(t, err) - winSize, err := GetWinsize(tty.Fd()) - assert.NilError(t, err) - assert.Assert(t, winSize != nil) - newSize := Winsize{Width: 200, Height: 200, x: winSize.x, y: winSize.y} - err = SetWinsize(tty.Fd(), &newSize) - assert.NilError(t, err) - winSize, err = GetWinsize(tty.Fd()) - assert.NilError(t, err) - assert.DeepEqual(t, *winSize, newSize, cmpWinsize) -} - -func TestGetFdInfo(t *testing.T) { - tty, err := newTtyForTest(t) - defer tty.Close() - assert.NilError(t, err) - inFd, isTerminal := GetFdInfo(tty) - assert.Equal(t, inFd, tty.Fd()) - assert.Equal(t, isTerminal, true) - tmpFile, err := newTempFile() - assert.NilError(t, err) - defer tmpFile.Close() - inFd, isTerminal = GetFdInfo(tmpFile) - assert.Equal(t, inFd, tmpFile.Fd()) - assert.Equal(t, isTerminal, false) -} - -func TestIsTerminal(t *testing.T) { - tty, err := newTtyForTest(t) - defer tty.Close() - assert.NilError(t, err) - isTerminal := IsTerminal(tty.Fd()) - assert.Equal(t, isTerminal, true) - tmpFile, err := newTempFile() - assert.NilError(t, err) - defer tmpFile.Close() - isTerminal = IsTerminal(tmpFile.Fd()) - assert.Equal(t, isTerminal, false) -} - -func TestSaveState(t *testing.T) { - tty, err := newTtyForTest(t) - defer tty.Close() - assert.NilError(t, err) - state, err := SaveState(tty.Fd()) - assert.NilError(t, err) - assert.Assert(t, state != nil) - tty, err = newTtyForTest(t) - assert.NilError(t, err) - defer tty.Close() - err = RestoreTerminal(tty.Fd(), state) - assert.NilError(t, err) -} - -func TestDisableEcho(t *testing.T) { - tty, err := newTtyForTest(t) - defer tty.Close() - assert.NilError(t, err) - state, err := SetRawTerminal(tty.Fd()) - defer RestoreTerminal(tty.Fd(), state) - assert.NilError(t, err) - assert.Assert(t, state != nil) - err = DisableEcho(tty.Fd(), state) - assert.NilError(t, err) -} diff --git a/pkg/term/term_windows.go b/pkg/term/term_windows.go deleted file mode 100644 index a3c3db1315740..0000000000000 --- a/pkg/term/term_windows.go +++ /dev/null @@ -1,221 +0,0 @@ -package term // import "github.com/docker/docker/pkg/term" - -import ( - "io" - "os" - "os/signal" - "syscall" // used for STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE - - "github.com/Azure/go-ansiterm/winterm" - "github.com/docker/docker/pkg/term/windows" -) - -// State holds the console mode for the terminal. -type State struct { - mode uint32 -} - -// Winsize is used for window size. -type Winsize struct { - Height uint16 - Width uint16 -} - -// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console -var vtInputSupported bool - -// StdStreams returns the standard streams (stdin, stdout, stderr). -func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { - // Turn on VT handling on all std handles, if possible. This might - // fail, in which case we will fall back to terminal emulation. - var emulateStdin, emulateStdout, emulateStderr bool - fd := os.Stdin.Fd() - if mode, err := winterm.GetConsoleMode(fd); err == nil { - // Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. - if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil { - emulateStdin = true - } else { - vtInputSupported = true - } - // Unconditionally set the console mode back even on failure because SetConsoleMode - // remembers invalid bits on input handles. - winterm.SetConsoleMode(fd, mode) - } - - fd = os.Stdout.Fd() - if mode, err := winterm.GetConsoleMode(fd); err == nil { - // Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it. - if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil { - emulateStdout = true - } else { - winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING) - } - } - - fd = os.Stderr.Fd() - if mode, err := winterm.GetConsoleMode(fd); err == nil { - // Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it. - if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil { - emulateStderr = true - } else { - winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING) - } - } - - // Temporarily use STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and - // STD_ERROR_HANDLE from syscall rather than x/sys/windows as long as - // go-ansiterm hasn't switch to x/sys/windows. - // TODO: switch back to x/sys/windows once go-ansiterm has switched - if emulateStdin { - stdIn = windowsconsole.NewAnsiReader(syscall.STD_INPUT_HANDLE) - } else { - stdIn = os.Stdin - } - - if emulateStdout { - stdOut = windowsconsole.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE) - } else { - stdOut = os.Stdout - } - - if emulateStderr { - stdErr = windowsconsole.NewAnsiWriter(syscall.STD_ERROR_HANDLE) - } else { - stdErr = os.Stderr - } - - return -} - -// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. -func GetFdInfo(in interface{}) (uintptr, bool) { - return windowsconsole.GetHandleInfo(in) -} - -// GetWinsize returns the window size based on the specified file descriptor. -func GetWinsize(fd uintptr) (*Winsize, error) { - info, err := winterm.GetConsoleScreenBufferInfo(fd) - if err != nil { - return nil, err - } - - winsize := &Winsize{ - Width: uint16(info.Window.Right - info.Window.Left + 1), - Height: uint16(info.Window.Bottom - info.Window.Top + 1), - } - - return winsize, nil -} - -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal(fd uintptr) bool { - return windowsconsole.IsConsole(fd) -} - -// RestoreTerminal restores the terminal connected to the given file descriptor -// to a previous state. -func RestoreTerminal(fd uintptr, state *State) error { - return winterm.SetConsoleMode(fd, state.mode) -} - -// SaveState saves the state of the terminal connected to the given file descriptor. -func SaveState(fd uintptr) (*State, error) { - mode, e := winterm.GetConsoleMode(fd) - if e != nil { - return nil, e - } - - return &State{mode: mode}, nil -} - -// DisableEcho disables echo for the terminal connected to the given file descriptor. -// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx -func DisableEcho(fd uintptr, state *State) error { - mode := state.mode - mode &^= winterm.ENABLE_ECHO_INPUT - mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT - err := winterm.SetConsoleMode(fd, mode) - if err != nil { - return err - } - - // Register an interrupt handler to catch and restore prior state - restoreAtInterrupt(fd, state) - return nil -} - -// SetRawTerminal puts the terminal connected to the given file descriptor into -// raw mode and returns the previous state. On UNIX, this puts both the input -// and output into raw mode. On Windows, it only puts the input into raw mode. -func SetRawTerminal(fd uintptr) (*State, error) { - state, err := MakeRaw(fd) - if err != nil { - return nil, err - } - - // Register an interrupt handler to catch and restore prior state - restoreAtInterrupt(fd, state) - return state, err -} - -// SetRawTerminalOutput puts the output of terminal connected to the given file -// descriptor into raw mode. On UNIX, this does nothing and returns nil for the -// state. On Windows, it disables LF -> CRLF translation. -func SetRawTerminalOutput(fd uintptr) (*State, error) { - state, err := SaveState(fd) - if err != nil { - return nil, err - } - - // Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this - // version of Windows. - winterm.SetConsoleMode(fd, state.mode|winterm.DISABLE_NEWLINE_AUTO_RETURN) - return state, err -} - -// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw -// mode and returns the previous state of the terminal so that it can be restored. -func MakeRaw(fd uintptr) (*State, error) { - state, err := SaveState(fd) - if err != nil { - return nil, err - } - - mode := state.mode - - // See - // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx - // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx - - // Disable these modes - mode &^= winterm.ENABLE_ECHO_INPUT - mode &^= winterm.ENABLE_LINE_INPUT - mode &^= winterm.ENABLE_MOUSE_INPUT - mode &^= winterm.ENABLE_WINDOW_INPUT - mode &^= winterm.ENABLE_PROCESSED_INPUT - - // Enable these modes - mode |= winterm.ENABLE_EXTENDED_FLAGS - mode |= winterm.ENABLE_INSERT_MODE - mode |= winterm.ENABLE_QUICK_EDIT_MODE - if vtInputSupported { - mode |= winterm.ENABLE_VIRTUAL_TERMINAL_INPUT - } - - err = winterm.SetConsoleMode(fd, mode) - if err != nil { - return nil, err - } - return state, nil -} - -func restoreAtInterrupt(fd uintptr, state *State) { - sigchan := make(chan os.Signal, 1) - signal.Notify(sigchan, os.Interrupt) - - go func() { - _ = <-sigchan - RestoreTerminal(fd, state) - os.Exit(0) - }() -} diff --git a/pkg/term/termios_bsd.go b/pkg/term/termios_bsd.go deleted file mode 100644 index 48b16f52039ca..0000000000000 --- a/pkg/term/termios_bsd.go +++ /dev/null @@ -1,42 +0,0 @@ -// +build darwin freebsd openbsd netbsd - -package term // import "github.com/docker/docker/pkg/term" - -import ( - "unsafe" - - "golang.org/x/sys/unix" -) - -const ( - getTermios = unix.TIOCGETA - setTermios = unix.TIOCSETA -) - -// Termios is the Unix API for terminal I/O. -type Termios unix.Termios - -// MakeRaw put the terminal connected to the given file descriptor into raw -// mode and returns the previous state of the terminal so that it can be -// restored. -func MakeRaw(fd uintptr) (*State, error) { - var oldState State - if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 { - return nil, err - } - - newState := oldState.termios - newState.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON) - newState.Oflag &^= unix.OPOST - newState.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN) - newState.Cflag &^= (unix.CSIZE | unix.PARENB) - newState.Cflag |= unix.CS8 - newState.Cc[unix.VMIN] = 1 - newState.Cc[unix.VTIME] = 0 - - if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 { - return nil, err - } - - return &oldState, nil -} diff --git a/pkg/term/termios_linux.go b/pkg/term/termios_linux.go deleted file mode 100644 index 6d4c63fdb75e5..0000000000000 --- a/pkg/term/termios_linux.go +++ /dev/null @@ -1,39 +0,0 @@ -package term // import "github.com/docker/docker/pkg/term" - -import ( - "golang.org/x/sys/unix" -) - -const ( - getTermios = unix.TCGETS - setTermios = unix.TCSETS -) - -// Termios is the Unix API for terminal I/O. -type Termios unix.Termios - -// MakeRaw put the terminal connected to the given file descriptor into raw -// mode and returns the previous state of the terminal so that it can be -// restored. -func MakeRaw(fd uintptr) (*State, error) { - termios, err := unix.IoctlGetTermios(int(fd), getTermios) - if err != nil { - return nil, err - } - - var oldState State - oldState.termios = Termios(*termios) - - termios.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON) - termios.Oflag &^= unix.OPOST - termios.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN) - termios.Cflag &^= (unix.CSIZE | unix.PARENB) - termios.Cflag |= unix.CS8 - termios.Cc[unix.VMIN] = 1 - termios.Cc[unix.VTIME] = 0 - - if err := unix.IoctlSetTermios(int(fd), setTermios, termios); err != nil { - return nil, err - } - return &oldState, nil -} diff --git a/pkg/term/windows/console.go b/pkg/term/windows/console.go deleted file mode 100644 index 5274019758050..0000000000000 --- a/pkg/term/windows/console.go +++ /dev/null @@ -1,35 +0,0 @@ -// +build windows - -package windowsconsole // import "github.com/docker/docker/pkg/term/windows" - -import ( - "os" - - "github.com/Azure/go-ansiterm/winterm" -) - -// GetHandleInfo returns file descriptor and bool indicating whether the file is a console. -func GetHandleInfo(in interface{}) (uintptr, bool) { - switch t := in.(type) { - case *ansiReader: - return t.Fd(), true - case *ansiWriter: - return t.Fd(), true - } - - var inFd uintptr - var isTerminal bool - - if file, ok := in.(*os.File); ok { - inFd = file.Fd() - isTerminal = IsConsole(inFd) - } - return inFd, isTerminal -} - -// IsConsole returns true if the given file descriptor is a Windows Console. -// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console. -func IsConsole(fd uintptr) bool { - _, e := winterm.GetConsoleMode(fd) - return e == nil -} diff --git a/pkg/term/windows/deprecated.go b/pkg/term/windows/deprecated.go new file mode 100644 index 0000000000000..bebef13991ccf --- /dev/null +++ b/pkg/term/windows/deprecated.go @@ -0,0 +1,35 @@ +//go:build windows +// +build windows + +// Package windowsconsole implements ANSI-aware input and output streams for use +// by the Docker Windows client. When asked for the set of standard streams (e.g., +// stdin, stdout, stderr), the code will create and return pseudo-streams that +// convert ANSI sequences to / from Windows Console API calls. +// +// Deprecated: use github.com/moby/term/windows instead +package windowsconsole // import "github.com/docker/docker/pkg/term/windows" + +import ( + windowsconsole "github.com/moby/term/windows" +) + +var ( + // GetHandleInfo returns file descriptor and bool indicating whether the file is a console. + // Deprecated: use github.com/moby/term/windows.GetHandleInfo + GetHandleInfo = windowsconsole.GetHandleInfo + + // IsConsole returns true if the given file descriptor is a Windows Console. + // The code assumes that GetConsoleMode will return an error for file descriptors that are not a console. + // Deprecated: use github.com/moby/term/windows.IsConsole + IsConsole = windowsconsole.IsConsole + + // NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a + // Windows console input handle. + // Deprecated: use github.com/moby/term/windows.NewAnsiReader + NewAnsiReader = windowsconsole.NewAnsiReader + + // NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a + // Windows console output handle. + // Deprecated: use github.com/moby/term/windows.NewAnsiWriter + NewAnsiWriter = windowsconsole.NewAnsiWriter +) diff --git a/pkg/term/windows/windows.go b/pkg/term/windows/windows.go deleted file mode 100644 index 3e5593ca6a683..0000000000000 --- a/pkg/term/windows/windows.go +++ /dev/null @@ -1,33 +0,0 @@ -// These files implement ANSI-aware input and output streams for use by the Docker Windows client. -// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create -// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls. - -package windowsconsole // import "github.com/docker/docker/pkg/term/windows" - -import ( - "io/ioutil" - "os" - "sync" - - "github.com/Azure/go-ansiterm" - "github.com/sirupsen/logrus" -) - -var logger *logrus.Logger -var initOnce sync.Once - -func initLogger() { - initOnce.Do(func() { - logFile := ioutil.Discard - - if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { - logFile, _ = os.Create("ansiReaderWriter.log") - } - - logger = &logrus.Logger{ - Out: logFile, - Formatter: new(logrus.TextFormatter), - Level: logrus.DebugLevel, - } - }) -} diff --git a/pkg/truncindex/truncindex_test.go b/pkg/truncindex/truncindex_test.go index e259017982a20..6d00a245aa149 100644 --- a/pkg/truncindex/truncindex_test.go +++ b/pkg/truncindex/truncindex_test.go @@ -158,7 +158,7 @@ func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult strin func BenchmarkTruncIndexAdd100(b *testing.B) { var testSet []string for i := 0; i < 100; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -174,7 +174,7 @@ func BenchmarkTruncIndexAdd100(b *testing.B) { func BenchmarkTruncIndexAdd250(b *testing.B) { var testSet []string for i := 0; i < 250; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -190,7 +190,7 @@ func BenchmarkTruncIndexAdd250(b *testing.B) { func BenchmarkTruncIndexAdd500(b *testing.B) { var testSet []string for i := 0; i < 500; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -207,7 +207,7 @@ func BenchmarkTruncIndexGet100(b *testing.B) { var testSet []string var testKeys []string for i := 0; i < 100; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } index := NewTruncIndex([]string{}) for _, id := range testSet { @@ -231,7 +231,7 @@ func BenchmarkTruncIndexGet250(b *testing.B) { var testSet []string var testKeys []string for i := 0; i < 250; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } index := NewTruncIndex([]string{}) for _, id := range testSet { @@ -255,7 +255,7 @@ func BenchmarkTruncIndexGet500(b *testing.B) { var testSet []string var testKeys []string for i := 0; i < 500; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } index := NewTruncIndex([]string{}) for _, id := range testSet { @@ -278,7 +278,7 @@ func BenchmarkTruncIndexGet500(b *testing.B) { func BenchmarkTruncIndexDelete100(b *testing.B) { var testSet []string for i := 0; i < 100; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -301,7 +301,7 @@ func BenchmarkTruncIndexDelete100(b *testing.B) { func BenchmarkTruncIndexDelete250(b *testing.B) { var testSet []string for i := 0; i < 250; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -324,7 +324,7 @@ func BenchmarkTruncIndexDelete250(b *testing.B) { func BenchmarkTruncIndexDelete500(b *testing.B) { var testSet []string for i := 0; i < 500; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -347,7 +347,7 @@ func BenchmarkTruncIndexDelete500(b *testing.B) { func BenchmarkTruncIndexNew100(b *testing.B) { var testSet []string for i := 0; i < 100; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -358,7 +358,7 @@ func BenchmarkTruncIndexNew100(b *testing.B) { func BenchmarkTruncIndexNew250(b *testing.B) { var testSet []string for i := 0; i < 250; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -369,7 +369,7 @@ func BenchmarkTruncIndexNew250(b *testing.B) { func BenchmarkTruncIndexNew500(b *testing.B) { var testSet []string for i := 0; i < 500; i++ { - testSet = append(testSet, stringid.GenerateNonCryptoID()) + testSet = append(testSet, stringid.GenerateRandomID()) } b.ResetTimer() for i := 0; i < b.N; i++ { @@ -381,7 +381,7 @@ func BenchmarkTruncIndexAddGet100(b *testing.B) { var testSet []string var testKeys []string for i := 0; i < 500; i++ { - id := stringid.GenerateNonCryptoID() + id := stringid.GenerateRandomID() testSet = append(testSet, id) l := rand.Intn(12) + 12 testKeys = append(testKeys, id[:l]) @@ -406,7 +406,7 @@ func BenchmarkTruncIndexAddGet250(b *testing.B) { var testSet []string var testKeys []string for i := 0; i < 500; i++ { - id := stringid.GenerateNonCryptoID() + id := stringid.GenerateRandomID() testSet = append(testSet, id) l := rand.Intn(12) + 12 testKeys = append(testKeys, id[:l]) @@ -431,7 +431,7 @@ func BenchmarkTruncIndexAddGet500(b *testing.B) { var testSet []string var testKeys []string for i := 0; i < 500; i++ { - id := stringid.GenerateNonCryptoID() + id := stringid.GenerateRandomID() testSet = append(testSet, id) l := rand.Intn(12) + 12 testKeys = append(testKeys, id[:l]) diff --git a/plugin/backend_linux.go b/plugin/backend_linux.go index 044e14b0cbf81..34313e8fd3fca 100644 --- a/plugin/backend_linux.go +++ b/plugin/backend_linux.go @@ -2,38 +2,38 @@ package plugin // import "github.com/docker/docker/plugin" import ( "archive/tar" + "bytes" "compress/gzip" "context" "encoding/json" "io" - "io/ioutil" "net/http" "os" "path" "path/filepath" - "runtime" "strings" + "time" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/platforms" + "github.com/containerd/containerd/remotes" + "github.com/containerd/containerd/remotes/docker" "github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/distribution" - progressutils "github.com/docker/docker/distribution/utils" - "github.com/docker/docker/distribution/xfer" "github.com/docker/docker/dockerversion" "github.com/docker/docker/errdefs" - "github.com/docker/docker/image" - "github.com/docker/docker/layer" "github.com/docker/docker/pkg/authorization" "github.com/docker/docker/pkg/chrootarchive" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/progress" + "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/system" - "github.com/docker/docker/plugin/v2" - refstore "github.com/docker/docker/reference" - "github.com/opencontainers/go-digest" + v2 "github.com/docker/docker/plugin/v2" + "github.com/moby/sys/mount" + digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -98,64 +98,6 @@ func (pm *Manager) Inspect(refOrID string) (tp *types.Plugin, err error) { return &p.PluginObj, nil } -func (pm *Manager) pull(ctx context.Context, ref reference.Named, config *distribution.ImagePullConfig, outStream io.Writer) error { - if outStream != nil { - // Include a buffer so that slow client connections don't affect - // transfer performance. - progressChan := make(chan progress.Progress, 100) - - writesDone := make(chan struct{}) - - defer func() { - close(progressChan) - <-writesDone - }() - - var cancelFunc context.CancelFunc - ctx, cancelFunc = context.WithCancel(ctx) - - go func() { - progressutils.WriteDistributionProgress(cancelFunc, outStream, progressChan) - close(writesDone) - }() - - config.ProgressOutput = progress.ChanOutput(progressChan) - } else { - config.ProgressOutput = progress.DiscardOutput() - } - return distribution.Pull(ctx, ref, config) -} - -type tempConfigStore struct { - config []byte - configDigest digest.Digest -} - -func (s *tempConfigStore) Put(c []byte) (digest.Digest, error) { - dgst := digest.FromBytes(c) - - s.config = c - s.configDigest = dgst - - return dgst, nil -} - -func (s *tempConfigStore) Get(d digest.Digest) ([]byte, error) { - if d != s.configDigest { - return nil, errNotFound("digest not found") - } - return s.config, nil -} - -func (s *tempConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) { - return configToRootFS(c) -} - -func (s *tempConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error) { - // TODO: LCOW/Plugins. This will need revisiting. For now use the runtime OS - return &specs.Platform{OS: runtime.GOOS}, nil -} - func computePrivileges(c types.PluginConfig) types.PluginPrivileges { var privileges types.PluginPrivileges if c.Network.Type != "null" && c.Network.Type != "bridge" && c.Network.Type != "" { @@ -217,37 +159,53 @@ func computePrivileges(c types.PluginConfig) types.PluginPrivileges { // Privileges pulls a plugin config and computes the privileges required to install it. func (pm *Manager) Privileges(ctx context.Context, ref reference.Named, metaHeader http.Header, authConfig *types.AuthConfig) (types.PluginPrivileges, error) { - // create image store instance - cs := &tempConfigStore{} - - // DownloadManager not defined because only pulling configuration. - pluginPullConfig := &distribution.ImagePullConfig{ - Config: distribution.Config{ - MetaHeaders: metaHeader, - AuthConfig: authConfig, - RegistryService: pm.config.RegistryService, - ImageEventLogger: func(string, string, string) {}, - ImageStore: cs, - }, - Schema2Types: distribution.PluginTypes, - } - - if err := pm.pull(ctx, ref, pluginPullConfig, nil); err != nil { - return nil, err + var ( + config types.PluginConfig + configSeen bool + ) + + h := func(ctx context.Context, desc specs.Descriptor) ([]specs.Descriptor, error) { + switch desc.MediaType { + case schema2.MediaTypeManifest, specs.MediaTypeImageManifest: + data, err := content.ReadBlob(ctx, pm.blobStore, desc) + if err != nil { + return nil, errors.Wrapf(err, "error reading image manifest from blob store for %s", ref) + } + + var m specs.Manifest + if err := json.Unmarshal(data, &m); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling image manifest for %s", ref) + } + return []specs.Descriptor{m.Config}, nil + case schema2.MediaTypePluginConfig: + configSeen = true + data, err := content.ReadBlob(ctx, pm.blobStore, desc) + if err != nil { + return nil, errors.Wrapf(err, "error reading plugin config from blob store for %s", ref) + } + + if err := json.Unmarshal(data, &config); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling plugin config for %s", ref) + } + } + + return nil, nil } - if cs.config == nil { - return nil, errors.New("no configuration pulled") + if err := pm.fetch(ctx, ref, authConfig, progress.DiscardOutput(), metaHeader, images.HandlerFunc(h)); err != nil { + return types.PluginPrivileges{}, nil } - var config types.PluginConfig - if err := json.Unmarshal(cs.config, &config); err != nil { - return nil, errdefs.System(err) + + if !configSeen { + return types.PluginPrivileges{}, errors.Errorf("did not find plugin config for specified reference %s", ref) } return computePrivileges(config), nil } // Upgrade upgrades a plugin +// +// TODO: replace reference package usage with simpler url.Parse semantics func (pm *Manager) Upgrade(ctx context.Context, ref reference.Named, name string, metaHeader http.Header, authConfig *types.AuthConfig, privileges types.PluginPrivileges, outStream io.Writer) (err error) { p, err := pm.config.Store.GetV2Plugin(name) if err != nil { @@ -258,44 +216,35 @@ func (pm *Manager) Upgrade(ctx context.Context, ref reference.Named, name string return errors.Wrap(enabledError(p.Name()), "plugin must be disabled before upgrading") } - pm.muGC.RLock() - defer pm.muGC.RUnlock() - // revalidate because Pull is public if _, err := reference.ParseNormalizedNamed(name); err != nil { return errors.Wrapf(errdefs.InvalidParameter(err), "failed to parse %q", name) } - tmpRootFSDir, err := ioutil.TempDir(pm.tmpDir(), ".rootfs") + pm.muGC.RLock() + defer pm.muGC.RUnlock() + + tmpRootFSDir, err := os.MkdirTemp(pm.tmpDir(), ".rootfs") if err != nil { - return errors.Wrap(errdefs.System(err), "error preparing upgrade") + return errors.Wrap(err, "error creating tmp dir for plugin rootfs") } - defer os.RemoveAll(tmpRootFSDir) - dm := &downloadManager{ - tmpDir: tmpRootFSDir, - blobStore: pm.blobStore, - } + var md fetchMeta - pluginPullConfig := &distribution.ImagePullConfig{ - Config: distribution.Config{ - MetaHeaders: metaHeader, - AuthConfig: authConfig, - RegistryService: pm.config.RegistryService, - ImageEventLogger: pm.config.LogPluginEvent, - ImageStore: dm, - }, - DownloadManager: dm, // todo: reevaluate if possible to substitute distribution/xfer dependencies instead - Schema2Types: distribution.PluginTypes, + ctx, cancel := context.WithCancel(ctx) + out, waitProgress := setupProgressOutput(outStream, cancel) + defer waitProgress() + + if err := pm.fetch(ctx, ref, authConfig, out, metaHeader, storeFetchMetadata(&md), childrenHandler(pm.blobStore), applyLayer(pm.blobStore, tmpRootFSDir, out)); err != nil { + return err } + pm.config.LogPluginEvent(reference.FamiliarString(ref), name, "pull") - err = pm.pull(ctx, ref, pluginPullConfig, outStream) - if err != nil { - go pm.GC() + if err := validateFetchedMetadata(md); err != nil { return err } - if err := pm.upgradePlugin(p, dm.configDigest, dm.blobs, tmpRootFSDir, &privileges); err != nil { + if err := pm.upgradePlugin(p, md.config, md.manifest, md.blobs, tmpRootFSDir, &privileges); err != nil { return err } p.PluginObj.PluginReference = ref.String() @@ -303,6 +252,8 @@ func (pm *Manager) Upgrade(ctx context.Context, ref reference.Named, name string } // Pull pulls a plugin, check if the correct privileges are provided and install the plugin. +// +// TODO: replace reference package usage with simpler url.Parse semantics func (pm *Manager) Pull(ctx context.Context, ref reference.Named, name string, metaHeader http.Header, authConfig *types.AuthConfig, privileges types.PluginPrivileges, outStream io.Writer, opts ...CreateOpt) (err error) { pm.muGC.RLock() defer pm.muGC.RUnlock() @@ -318,32 +269,24 @@ func (pm *Manager) Pull(ctx context.Context, ref reference.Named, name string, m return errdefs.InvalidParameter(err) } - tmpRootFSDir, err := ioutil.TempDir(pm.tmpDir(), ".rootfs") + tmpRootFSDir, err := os.MkdirTemp(pm.tmpDir(), ".rootfs") if err != nil { - return errors.Wrap(errdefs.System(err), "error preparing pull") + return errors.Wrap(errdefs.System(err), "error preparing upgrade") } defer os.RemoveAll(tmpRootFSDir) - dm := &downloadManager{ - tmpDir: tmpRootFSDir, - blobStore: pm.blobStore, - } + var md fetchMeta - pluginPullConfig := &distribution.ImagePullConfig{ - Config: distribution.Config{ - MetaHeaders: metaHeader, - AuthConfig: authConfig, - RegistryService: pm.config.RegistryService, - ImageEventLogger: pm.config.LogPluginEvent, - ImageStore: dm, - }, - DownloadManager: dm, // todo: reevaluate if possible to substitute distribution/xfer dependencies instead - Schema2Types: distribution.PluginTypes, + ctx, cancel := context.WithCancel(ctx) + out, waitProgress := setupProgressOutput(outStream, cancel) + defer waitProgress() + + if err := pm.fetch(ctx, ref, authConfig, out, metaHeader, storeFetchMetadata(&md), childrenHandler(pm.blobStore), applyLayer(pm.blobStore, tmpRootFSDir, out)); err != nil { + return err } + pm.config.LogPluginEvent(reference.FamiliarString(ref), name, "pull") - err = pm.pull(ctx, ref, pluginPullConfig, outStream) - if err != nil { - go pm.GC() + if err := validateFetchedMetadata(md); err != nil { return err } @@ -354,12 +297,14 @@ func (pm *Manager) Pull(ctx context.Context, ref reference.Named, name string, m optsList = append(optsList, opts...) optsList = append(optsList, refOpt) - p, err := pm.createPlugin(name, dm.configDigest, dm.blobs, tmpRootFSDir, &privileges, optsList...) + // TODO: tmpRootFSDir is empty but should have layers in it + p, err := pm.createPlugin(name, md.config, md.manifest, md.blobs, tmpRootFSDir, &privileges, optsList...) if err != nil { return err } pm.publisher.Publish(EventCreate{Plugin: p.PluginObj}) + return nil } @@ -404,7 +349,7 @@ next: return out, nil } -// Push pushes a plugin to the store. +// Push pushes a plugin to the registry. func (pm *Manager) Push(ctx context.Context, name string, metaHeader http.Header, authConfig *types.AuthConfig, outStream io.Writer) error { p, err := pm.config.Store.GetV2Plugin(name) if err != nil { @@ -416,201 +361,197 @@ func (pm *Manager) Push(ctx context.Context, name string, metaHeader http.Header return errors.Wrapf(err, "plugin has invalid name %v for push", p.Name()) } - var po progress.Output - if outStream != nil { - // Include a buffer so that slow client connections don't affect - // transfer performance. - progressChan := make(chan progress.Progress, 100) - - writesDone := make(chan struct{}) + statusTracker := docker.NewInMemoryTracker() - defer func() { - close(progressChan) - <-writesDone - }() - - var cancelFunc context.CancelFunc - ctx, cancelFunc = context.WithCancel(ctx) + resolver, err := pm.newResolver(ctx, statusTracker, authConfig, metaHeader, false) + if err != nil { + return err + } - go func() { - progressutils.WriteDistributionProgress(cancelFunc, outStream, progressChan) - close(writesDone) - }() + pusher, err := resolver.Pusher(ctx, ref.String()) + if err != nil { - po = progress.ChanOutput(progressChan) - } else { - po = progress.DiscardOutput() + return errors.Wrap(err, "error creating plugin pusher") } - // TODO: replace these with manager - is := &pluginConfigStore{ - pm: pm, - plugin: p, - } - lss := make(map[string]distribution.PushLayerProvider) - lss[runtime.GOOS] = &pluginLayerProvider{ - pm: pm, - plugin: p, - } - rs := &pluginReference{ - name: ref, - pluginID: p.Config, - } + pj := newPushJobs(statusTracker) + + ctx, cancel := context.WithCancel(ctx) + out, waitProgress := setupProgressOutput(outStream, cancel) + defer waitProgress() - uploadManager := xfer.NewLayerUploadManager(3) + progressHandler := images.HandlerFunc(func(ctx context.Context, desc specs.Descriptor) ([]specs.Descriptor, error) { + logrus.WithField("mediaType", desc.MediaType).WithField("digest", desc.Digest.String()).Debug("Preparing to push plugin layer") + id := stringid.TruncateID(desc.Digest.String()) + pj.add(remotes.MakeRefKey(ctx, desc), id) + progress.Update(out, id, "Preparing") + return nil, nil + }) - imagePushConfig := &distribution.ImagePushConfig{ - Config: distribution.Config{ - MetaHeaders: metaHeader, - AuthConfig: authConfig, - ProgressOutput: po, - RegistryService: pm.config.RegistryService, - ReferenceStore: rs, - ImageEventLogger: pm.config.LogPluginEvent, - ImageStore: is, - RequireSchema2: true, - }, - ConfigMediaType: schema2.MediaTypePluginConfig, - LayerStores: lss, - UploadManager: uploadManager, + desc, err := pm.getManifestDescriptor(ctx, p) + if err != nil { + return errors.Wrap(err, "error reading plugin manifest") } - return distribution.Push(ctx, ref, imagePushConfig) -} + progress.Messagef(out, "", "The push refers to repository [%s]", reference.FamiliarName(ref)) -type pluginReference struct { - name reference.Named - pluginID digest.Digest -} + // TODO: If a layer already exists on the registry, the progress output just says "Preparing" + go func() { + timer := time.NewTimer(100 * time.Millisecond) + defer timer.Stop() + if !timer.Stop() { + <-timer.C + } + var statuses []contentStatus + for { + timer.Reset(100 * time.Millisecond) + select { + case <-ctx.Done(): + return + case <-timer.C: + statuses = pj.status() + } -func (r *pluginReference) References(id digest.Digest) []reference.Named { - if r.pluginID != id { - return nil - } - return []reference.Named{r.name} -} + for _, s := range statuses { + out.WriteProgress(progress.Progress{ID: s.Ref, Current: s.Offset, Total: s.Total, Action: s.Status, LastUpdate: s.Offset == s.Total}) + } + } + }() -func (r *pluginReference) ReferencesByName(ref reference.Named) []refstore.Association { - return []refstore.Association{ - { - Ref: r.name, - ID: r.pluginID, - }, + // Make sure we can authenticate the request since the auth scope for plugin repos is different than a normal repo. + ctx = docker.WithScope(ctx, scope(ref, true)) + if err := remotes.PushContent(ctx, pusher, desc, pm.blobStore, nil, nil, func(h images.Handler) images.Handler { + return images.Handlers(progressHandler, h) + }); err != nil { + // Try fallback to http. + // This is needed because the containerd pusher will only attempt the first registry config we pass, which would + // typically be https. + // If there are no http-only host configs found we'll error out anyway. + resolver, _ := pm.newResolver(ctx, statusTracker, authConfig, metaHeader, true) + if resolver != nil { + pusher, _ := resolver.Pusher(ctx, ref.String()) + if pusher != nil { + logrus.WithField("ref", ref).Debug("Re-attmpting push with http-fallback") + err2 := remotes.PushContent(ctx, pusher, desc, pm.blobStore, nil, nil, func(h images.Handler) images.Handler { + return images.Handlers(progressHandler, h) + }) + if err2 == nil { + err = nil + } else { + logrus.WithError(err2).WithField("ref", ref).Debug("Error while attempting push with http-fallback") + } + } + } + if err != nil { + return errors.Wrap(err, "error pushing plugin") + } } -} -func (r *pluginReference) Get(ref reference.Named) (digest.Digest, error) { - if r.name.String() != ref.String() { - return digest.Digest(""), refstore.ErrDoesNotExist + // For blobs that already exist in the registry we need to make sure to update the progress otherwise it will just say "pending" + // TODO: How to check if the layer already exists? Is it worth it? + for _, j := range pj.jobs { + progress.Update(out, pj.names[j], "Upload complete") } - return r.pluginID, nil -} -func (r *pluginReference) AddTag(ref reference.Named, id digest.Digest, force bool) error { - // Read only, ignore - return nil -} -func (r *pluginReference) AddDigest(ref reference.Canonical, id digest.Digest, force bool) error { - // Read only, ignore + // Signal the client for content trust verification + progress.Aux(out, types.PushResult{Tag: ref.(reference.Tagged).Tag(), Digest: desc.Digest.String(), Size: int(desc.Size)}) + return nil } -func (r *pluginReference) Delete(ref reference.Named) (bool, error) { - // Read only, ignore - return false, nil -} -type pluginConfigStore struct { - pm *Manager - plugin *v2.Plugin +// manifest wraps an OCI manifest, because... +// Historically the registry does not support plugins unless the media type on the manifest is specifically schema2.MediaTypeManifest +// So the OCI manifest media type is not supported. +// Additionally, there is extra validation for the docker schema2 manifest than there is a mediatype set on the manifest itself +// even though this is set on the descriptor +// The OCI types do not have this field. +type manifest struct { + specs.Manifest + MediaType string `json:"mediaType,omitempty"` } -func (s *pluginConfigStore) Put([]byte) (digest.Digest, error) { - return digest.Digest(""), errors.New("cannot store config on push") -} +func buildManifest(ctx context.Context, s content.Manager, config digest.Digest, layers []digest.Digest) (manifest, error) { + var m manifest + m.MediaType = images.MediaTypeDockerSchema2Manifest + m.SchemaVersion = 2 -func (s *pluginConfigStore) Get(d digest.Digest) ([]byte, error) { - if s.plugin.Config != d { - return nil, errors.New("plugin not found") - } - rwc, err := s.pm.blobStore.Get(d) + configInfo, err := s.Info(ctx, config) if err != nil { - return nil, err + return m, errors.Wrapf(err, "error reading plugin config content for digest %s", config) + } + m.Config = specs.Descriptor{ + MediaType: mediaTypePluginConfig, + Size: configInfo.Size, + Digest: configInfo.Digest, } - defer rwc.Close() - return ioutil.ReadAll(rwc) -} - -func (s *pluginConfigStore) RootFSFromConfig(c []byte) (*image.RootFS, error) { - return configToRootFS(c) -} - -func (s *pluginConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error) { - // TODO: LCOW/Plugins. This will need revisiting. For now use the runtime OS - return &specs.Platform{OS: runtime.GOOS}, nil -} -type pluginLayerProvider struct { - pm *Manager - plugin *v2.Plugin + for _, l := range layers { + info, err := s.Info(ctx, l) + if err != nil { + return m, errors.Wrapf(err, "error fetching info for content digest %s", l) + } + m.Layers = append(m.Layers, specs.Descriptor{ + MediaType: images.MediaTypeDockerSchema2LayerGzip, // TODO: This is assuming everything is a gzip compressed layer, but that may not be true. + Digest: l, + Size: info.Size, + }) + } + return m, nil } -func (p *pluginLayerProvider) Get(id layer.ChainID) (distribution.PushLayer, error) { - rootFS := rootFSFromPlugin(p.plugin.PluginObj.Config.Rootfs) - var i int - for i = 1; i <= len(rootFS.DiffIDs); i++ { - if layer.CreateChainID(rootFS.DiffIDs[:i]) == id { - break +// getManifestDescriptor gets the OCI descriptor for a manifest +// It will generate a manifest if one does not exist +func (pm *Manager) getManifestDescriptor(ctx context.Context, p *v2.Plugin) (specs.Descriptor, error) { + logger := logrus.WithField("plugin", p.Name()).WithField("digest", p.Manifest) + if p.Manifest != "" { + info, err := pm.blobStore.Info(ctx, p.Manifest) + if err == nil { + desc := specs.Descriptor{ + Size: info.Size, + Digest: info.Digest, + MediaType: images.MediaTypeDockerSchema2Manifest, + } + return desc, nil } + logger.WithError(err).Debug("Could not find plugin manifest in content store") + } else { + logger.Info("Plugin does not have manifest digest") } - if i > len(rootFS.DiffIDs) { - return nil, errors.New("layer not found") - } - return &pluginLayer{ - pm: p.pm, - diffIDs: rootFS.DiffIDs[:i], - blobs: p.plugin.Blobsums[:i], - }, nil -} + logger.Info("Building a new plugin manifest") -type pluginLayer struct { - pm *Manager - diffIDs []layer.DiffID - blobs []digest.Digest -} + manifest, err := buildManifest(ctx, pm.blobStore, p.Config, p.Blobsums) + if err != nil { + return specs.Descriptor{}, err + } -func (l *pluginLayer) ChainID() layer.ChainID { - return layer.CreateChainID(l.diffIDs) -} + desc, err := writeManifest(ctx, pm.blobStore, &manifest) + if err != nil { + return desc, err + } -func (l *pluginLayer) DiffID() layer.DiffID { - return l.diffIDs[len(l.diffIDs)-1] + if err := pm.save(p); err != nil { + logger.WithError(err).Error("Could not save plugin with manifest digest") + } + return desc, nil } -func (l *pluginLayer) Parent() distribution.PushLayer { - if len(l.diffIDs) == 1 { - return nil +func writeManifest(ctx context.Context, cs content.Store, m *manifest) (specs.Descriptor, error) { + platform := platforms.DefaultSpec() + desc := specs.Descriptor{ + MediaType: images.MediaTypeDockerSchema2Manifest, + Platform: &platform, } - return &pluginLayer{ - pm: l.pm, - diffIDs: l.diffIDs[:len(l.diffIDs)-1], - blobs: l.blobs[:len(l.diffIDs)-1], + data, err := json.Marshal(m) + if err != nil { + return desc, errors.Wrap(err, "error encoding manifest") } -} - -func (l *pluginLayer) Open() (io.ReadCloser, error) { - return l.pm.blobStore.Get(l.blobs[len(l.diffIDs)-1]) -} - -func (l *pluginLayer) Size() (int64, error) { - return l.pm.blobStore.Size(l.blobs[len(l.diffIDs)-1]) -} - -func (l *pluginLayer) MediaType() string { - return schema2.MediaTypeLayer -} + desc.Digest = digest.FromBytes(data) + desc.Size = int64(len(data)) -func (l *pluginLayer) Release() { - // Nothing needs to be release, no references held + if err := content.WriteBlob(ctx, cs, remotes.MakeRefKey(ctx, desc), bytes.NewReader(data), desc); err != nil { + return desc, errors.Wrap(err, "error writing plugin manifest") + } + return desc, nil } // Remove deletes plugin's root directory. @@ -691,7 +632,7 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser, return err } - tmpRootFSDir, err := ioutil.TempDir(pm.tmpDir(), ".rootfs") + tmpRootFSDir, err := os.MkdirTemp(pm.tmpDir(), ".rootfs") if err != nil { return errors.Wrap(err, "failed to create temp directory") } @@ -700,14 +641,14 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser, var configJSON []byte rootFS := splitConfigRootFSFromTar(tarCtx, &configJSON) - rootFSBlob, err := pm.blobStore.New() + rootFSBlob, err := pm.blobStore.Writer(ctx, content.WithRef(name)) if err != nil { return err } defer rootFSBlob.Close() + gzw := gzip.NewWriter(rootFSBlob) - layerDigester := digest.Canonical.Digester() - rootFSReader := io.TeeReader(rootFS, io.MultiWriter(gzw, layerDigester.Hash())) + rootFSReader := io.TeeReader(rootFS, gzw) if err := chrootarchive.Untar(rootFSReader, tmpRootFSDir, nil); err != nil { return err @@ -736,8 +677,7 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser, pm.mu.Lock() defer pm.mu.Unlock() - rootFSBlobsum, err := rootFSBlob.Commit() - if err != nil { + if err := rootFSBlob.Commit(ctx, 0, ""); err != nil { return err } defer func() { @@ -748,12 +688,12 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser, config.Rootfs = &types.PluginConfigRootfs{ Type: "layers", - DiffIds: []string{layerDigester.Digest().String()}, + DiffIds: []string{rootFSBlob.Digest().String()}, } config.DockerVersion = dockerversion.Version - configBlob, err := pm.blobStore.New() + configBlob, err := pm.blobStore.Writer(ctx, content.WithRef(name+"-config.json")) if err != nil { return err } @@ -761,12 +701,23 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.ReadCloser, if err := json.NewEncoder(configBlob).Encode(config); err != nil { return errors.Wrap(err, "error encoding json config") } - configBlobsum, err := configBlob.Commit() + if err := configBlob.Commit(ctx, 0, ""); err != nil { + return err + } + + configDigest := configBlob.Digest() + layers := []digest.Digest{rootFSBlob.Digest()} + + manifest, err := buildManifest(ctx, pm.blobStore, configDigest, layers) if err != nil { return err } + desc, err := writeManifest(ctx, pm.blobStore, &manifest) + if err != nil { + return + } - p, err := pm.createPlugin(name, configBlobsum, []digest.Digest{rootFSBlobsum}, tmpRootFSDir, nil) + p, err := pm.createPlugin(name, configDigest, desc.Digest, layers, tmpRootFSDir, nil) if err != nil { return err } @@ -814,7 +765,7 @@ func splitConfigRootFSFromTar(in io.ReadCloser, config *[]byte) io.ReadCloser { name = name[1:] } if name == configFileName { - dt, err := ioutil.ReadAll(content) + dt, err := io.ReadAll(content) if err != nil { pw.CloseWithError(errors.Wrapf(err, "failed to read %s", configFileName)) return @@ -836,7 +787,7 @@ func splitConfigRootFSFromTar(in io.ReadCloser, config *[]byte) io.ReadCloser { } hasRootFS = true } else { - io.Copy(ioutil.Discard, content) + io.Copy(io.Discard, content) } } }() diff --git a/plugin/backend_linux_test.go b/plugin/backend_linux_test.go index 81cf2ebb76291..bb4e7172f5990 100644 --- a/plugin/backend_linux_test.go +++ b/plugin/backend_linux_test.go @@ -1,14 +1,13 @@ package plugin // import "github.com/docker/docker/plugin" import ( - "io/ioutil" "os" "path/filepath" "testing" ) func TestAtomicRemoveAllNormal(t *testing.T) { - dir, err := ioutil.TempDir("", "atomic-remove-with-normal") + dir, err := os.MkdirTemp("", "atomic-remove-with-normal") if err != nil { t.Fatal(err) } @@ -27,7 +26,7 @@ func TestAtomicRemoveAllNormal(t *testing.T) { } func TestAtomicRemoveAllAlreadyExists(t *testing.T) { - dir, err := ioutil.TempDir("", "atomic-remove-already-exists") + dir, err := os.MkdirTemp("", "atomic-remove-already-exists") if err != nil { t.Fatal(err) } @@ -55,7 +54,7 @@ func TestAtomicRemoveAllNotExist(t *testing.T) { t.Fatal(err) } - dir, err := ioutil.TempDir("", "atomic-remove-already-exists") + dir, err := os.MkdirTemp("", "atomic-remove-already-exists") if err != nil { t.Fatal(err) } diff --git a/plugin/backend_unsupported.go b/plugin/backend_unsupported.go index c0666e858e535..98d35c1dfa34c 100644 --- a/plugin/backend_unsupported.go +++ b/plugin/backend_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package plugin // import "github.com/docker/docker/plugin" diff --git a/plugin/blobstore.go b/plugin/blobstore.go deleted file mode 100644 index a24e7bdf4f8e4..0000000000000 --- a/plugin/blobstore.go +++ /dev/null @@ -1,190 +0,0 @@ -package plugin // import "github.com/docker/docker/plugin" - -import ( - "context" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "runtime" - - "github.com/docker/docker/distribution/xfer" - "github.com/docker/docker/image" - "github.com/docker/docker/layer" - "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/pkg/chrootarchive" - "github.com/docker/docker/pkg/progress" - "github.com/opencontainers/go-digest" - specs "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -type blobstore interface { - New() (WriteCommitCloser, error) - Get(dgst digest.Digest) (io.ReadCloser, error) - Size(dgst digest.Digest) (int64, error) -} - -type basicBlobStore struct { - path string -} - -func newBasicBlobStore(p string) (*basicBlobStore, error) { - tmpdir := filepath.Join(p, "tmp") - if err := os.MkdirAll(tmpdir, 0700); err != nil { - return nil, errors.Wrapf(err, "failed to mkdir %v", p) - } - return &basicBlobStore{path: p}, nil -} - -func (b *basicBlobStore) New() (WriteCommitCloser, error) { - f, err := ioutil.TempFile(filepath.Join(b.path, "tmp"), ".insertion") - if err != nil { - return nil, errors.Wrap(err, "failed to create temp file") - } - return newInsertion(f), nil -} - -func (b *basicBlobStore) Get(dgst digest.Digest) (io.ReadCloser, error) { - return os.Open(filepath.Join(b.path, string(dgst.Algorithm()), dgst.Hex())) -} - -func (b *basicBlobStore) Size(dgst digest.Digest) (int64, error) { - stat, err := os.Stat(filepath.Join(b.path, string(dgst.Algorithm()), dgst.Hex())) - if err != nil { - return 0, err - } - return stat.Size(), nil -} - -func (b *basicBlobStore) gc(whitelist map[digest.Digest]struct{}) { - for _, alg := range []string{string(digest.Canonical)} { - items, err := ioutil.ReadDir(filepath.Join(b.path, alg)) - if err != nil { - continue - } - for _, fi := range items { - if _, exists := whitelist[digest.Digest(alg+":"+fi.Name())]; !exists { - p := filepath.Join(b.path, alg, fi.Name()) - err := os.RemoveAll(p) - logrus.Debugf("cleaned up blob %v: %v", p, err) - } - } - } - -} - -// WriteCommitCloser defines object that can be committed to blobstore. -type WriteCommitCloser interface { - io.WriteCloser - Commit() (digest.Digest, error) -} - -type insertion struct { - io.Writer - f *os.File - digester digest.Digester - closed bool -} - -func newInsertion(tempFile *os.File) *insertion { - digester := digest.Canonical.Digester() - return &insertion{f: tempFile, digester: digester, Writer: io.MultiWriter(tempFile, digester.Hash())} -} - -func (i *insertion) Commit() (digest.Digest, error) { - p := i.f.Name() - d := filepath.Join(filepath.Join(p, "../../")) - i.f.Sync() - defer os.RemoveAll(p) - if err := i.f.Close(); err != nil { - return "", err - } - i.closed = true - dgst := i.digester.Digest() - if err := os.MkdirAll(filepath.Join(d, string(dgst.Algorithm())), 0700); err != nil { - return "", errors.Wrapf(err, "failed to mkdir %v", d) - } - if err := os.Rename(p, filepath.Join(d, string(dgst.Algorithm()), dgst.Hex())); err != nil { - return "", errors.Wrapf(err, "failed to rename %v", p) - } - return dgst, nil -} - -func (i *insertion) Close() error { - if i.closed { - return nil - } - defer os.RemoveAll(i.f.Name()) - return i.f.Close() -} - -type downloadManager struct { - blobStore blobstore - tmpDir string - blobs []digest.Digest - configDigest digest.Digest -} - -func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os string, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) { - for _, l := range layers { - b, err := dm.blobStore.New() - if err != nil { - return initialRootFS, nil, err - } - defer b.Close() - rc, _, err := l.Download(ctx, progressOutput) - if err != nil { - return initialRootFS, nil, errors.Wrap(err, "failed to download") - } - defer rc.Close() - r := io.TeeReader(rc, b) - inflatedLayerData, err := archive.DecompressStream(r) - if err != nil { - return initialRootFS, nil, err - } - defer inflatedLayerData.Close() - digester := digest.Canonical.Digester() - if _, err := chrootarchive.ApplyLayer(dm.tmpDir, io.TeeReader(inflatedLayerData, digester.Hash())); err != nil { - return initialRootFS, nil, err - } - initialRootFS.Append(layer.DiffID(digester.Digest())) - d, err := b.Commit() - if err != nil { - return initialRootFS, nil, err - } - dm.blobs = append(dm.blobs, d) - } - return initialRootFS, nil, nil -} - -func (dm *downloadManager) Put(dt []byte) (digest.Digest, error) { - b, err := dm.blobStore.New() - if err != nil { - return "", err - } - defer b.Close() - n, err := b.Write(dt) - if err != nil { - return "", err - } - if n != len(dt) { - return "", io.ErrShortWrite - } - d, err := b.Commit() - dm.configDigest = d - return d, err -} - -func (dm *downloadManager) Get(d digest.Digest) ([]byte, error) { - return nil, fmt.Errorf("digest not found") -} -func (dm *downloadManager) RootFSFromConfig(c []byte) (*image.RootFS, error) { - return configToRootFS(c) -} -func (dm *downloadManager) PlatformFromConfig(c []byte) (*specs.Platform, error) { - // TODO: LCOW/Plugins. This will need revisiting. For now use the runtime OS - return &specs.Platform{OS: runtime.GOOS}, nil -} diff --git a/plugin/defs.go b/plugin/defs.go index 31f7c6bcc33ca..9a3577a72b1f5 100644 --- a/plugin/defs.go +++ b/plugin/defs.go @@ -1,11 +1,13 @@ package plugin // import "github.com/docker/docker/plugin" import ( + "fmt" + "strings" "sync" "github.com/docker/docker/pkg/plugins" - "github.com/docker/docker/plugin/v2" - "github.com/opencontainers/runtime-spec/specs-go" + v2 "github.com/docker/docker/plugin/v2" + specs "github.com/opencontainers/runtime-spec/specs-go" ) // Store manages the plugin inventory in memory and on-disk @@ -42,6 +44,31 @@ func WithSwarmService(id string) CreateOpt { } } +// WithEnv is a CreateOpt that passes the user-provided environment variables +// to the plugin container, de-duplicating variables with the same names case +// sensitively and only appends valid key=value pairs +func WithEnv(env []string) CreateOpt { + return func(p *v2.Plugin) { + effectiveEnv := make(map[string]string) + for _, penv := range p.PluginObj.Config.Env { + if penv.Value != nil { + effectiveEnv[penv.Name] = *penv.Value + } + } + for _, line := range env { + if pair := strings.SplitN(line, "=", 2); len(pair) > 1 { + effectiveEnv[pair[0]] = pair[1] + } + } + p.PluginObj.Settings.Env = make([]string, len(effectiveEnv)) + i := 0 + for key, value := range effectiveEnv { + p.PluginObj.Settings.Env[i] = fmt.Sprintf("%s=%s", key, value) + i++ + } + } +} + // WithSpecMounts is a SpecOpt which appends the provided mounts to the runtime spec func WithSpecMounts(mounts []specs.Mount) SpecOpt { return func(s *specs.Spec) { diff --git a/plugin/executor/containerd/containerd.go b/plugin/executor/containerd/containerd.go index a3401dce794ac..8354745990684 100644 --- a/plugin/executor/containerd/containerd.go +++ b/plugin/executor/containerd/containerd.go @@ -3,16 +3,15 @@ package containerd // import "github.com/docker/docker/plugin/executor/container import ( "context" "io" - "path/filepath" "sync" - "time" "github.com/containerd/containerd" "github.com/containerd/containerd/cio" - "github.com/containerd/containerd/runtime/linux/runctypes" + "github.com/docker/docker/api/types" "github.com/docker/docker/errdefs" "github.com/docker/docker/libcontainerd" - "github.com/opencontainers/runtime-spec/specs-go" + libcontainerdtypes "github.com/docker/docker/libcontainerd/types" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -25,27 +24,15 @@ type ExitHandler interface { HandleExitEvent(id string) error } -// Client is used by the exector to perform operations. -// TODO(@cpuguy83): This should really just be based off the containerd client interface. -// However right now this whole package is tied to github.com/docker/docker/libcontainerd -type Client interface { - Create(ctx context.Context, containerID string, spec *specs.Spec, runtimeOptions interface{}) error - Restore(ctx context.Context, containerID string, attachStdio libcontainerd.StdioCallback) (alive bool, pid int, err error) - Status(ctx context.Context, containerID string) (libcontainerd.Status, error) - Delete(ctx context.Context, containerID string) error - DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) - Start(ctx context.Context, containerID, checkpointDir string, withStdin bool, attachStdio libcontainerd.StdioCallback) (pid int, err error) - SignalProcess(ctx context.Context, containerID, processID string, signal int) error -} - // New creates a new containerd plugin executor -func New(ctx context.Context, rootDir string, cli *containerd.Client, exitHandler ExitHandler) (*Executor, error) { +func New(ctx context.Context, rootDir string, cli *containerd.Client, ns string, exitHandler ExitHandler, runtime types.Runtime) (*Executor, error) { e := &Executor{ rootDir: rootDir, exitHandler: exitHandler, + runtime: runtime, } - client, err := libcontainerd.NewClient(ctx, cli, rootDir, PluginNamespace, e) + client, err := libcontainerd.NewClient(ctx, cli, rootDir, ns, e) if err != nil { return nil, errors.Wrap(err, "error creating containerd exec client") } @@ -56,30 +43,32 @@ func New(ctx context.Context, rootDir string, cli *containerd.Client, exitHandle // Executor is the containerd client implementation of a plugin executor type Executor struct { rootDir string - client Client + client libcontainerdtypes.Client exitHandler ExitHandler + runtime types.Runtime } // deleteTaskAndContainer deletes plugin task and then plugin container from containerd -func deleteTaskAndContainer(ctx context.Context, cli Client, id string) { - _, _, err := cli.DeleteTask(ctx, id) - if err != nil && !errdefs.IsNotFound(err) { - logrus.WithError(err).WithField("id", id).Error("failed to delete plugin task from containerd") +func deleteTaskAndContainer(ctx context.Context, cli libcontainerdtypes.Client, id string, p libcontainerdtypes.Process) { + if p != nil { + if _, _, err := p.Delete(ctx); err != nil && !errdefs.IsNotFound(err) { + logrus.WithError(err).WithField("id", id).Error("failed to delete plugin task from containerd") + } + } else { + if _, _, err := cli.DeleteTask(ctx, id); err != nil && !errdefs.IsNotFound(err) { + logrus.WithError(err).WithField("id", id).Error("failed to delete plugin task from containerd") + } } - err = cli.Delete(ctx, id) - if err != nil && !errdefs.IsNotFound(err) { + if err := cli.Delete(ctx, id); err != nil && !errdefs.IsNotFound(err) { logrus.WithError(err).WithField("id", id).Error("failed to delete plugin container from containerd") } } // Create creates a new container func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteCloser) error { - opts := runctypes.RuncOptions{ - RuntimeRoot: filepath.Join(e.rootDir, "runtime-root"), - } ctx := context.Background() - err := e.client.Create(ctx, id, &spec, &opts) + err := e.client.Create(ctx, id, &spec, e.runtime.Shim.Binary, e.runtime.Shim.Opts) if err != nil { status, err2 := e.client.Status(ctx, id) if err2 != nil { @@ -87,11 +76,11 @@ func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteClo logrus.WithError(err2).WithField("id", id).Warn("Received an error while attempting to read plugin status") } } else { - if status != libcontainerd.StatusRunning && status != libcontainerd.StatusUnknown { + if status != containerd.Running && status != containerd.Unknown { if err2 := e.client.Delete(ctx, id); err2 != nil && !errdefs.IsNotFound(err2) { logrus.WithError(err2).WithField("plugin", id).Error("Error cleaning up containerd container") } - err = e.client.Create(ctx, id, &spec, &opts) + err = e.client.Create(ctx, id, &spec, e.runtime.Shim.Binary, e.runtime.Shim.Opts) } } @@ -102,19 +91,19 @@ func (e *Executor) Create(id string, spec specs.Spec, stdout, stderr io.WriteClo _, err = e.client.Start(ctx, id, "", false, attachStreamsFunc(stdout, stderr)) if err != nil { - deleteTaskAndContainer(ctx, e.client, id) + deleteTaskAndContainer(ctx, e.client, id, nil) } return err } // Restore restores a container func (e *Executor) Restore(id string, stdout, stderr io.WriteCloser) (bool, error) { - alive, _, err := e.client.Restore(context.Background(), id, attachStreamsFunc(stdout, stderr)) + alive, _, p, err := e.client.Restore(context.Background(), id, attachStreamsFunc(stdout, stderr)) if err != nil && !errdefs.IsNotFound(err) { return false, err } if !alive { - deleteTaskAndContainer(context.Background(), e.client, id) + deleteTaskAndContainer(context.Background(), e.client, id, p) } return alive, nil } @@ -122,20 +111,20 @@ func (e *Executor) Restore(id string, stdout, stderr io.WriteCloser) (bool, erro // IsRunning returns if the container with the given id is running func (e *Executor) IsRunning(id string) (bool, error) { status, err := e.client.Status(context.Background(), id) - return status == libcontainerd.StatusRunning, err + return status == containerd.Running, err } // Signal sends the specified signal to the container func (e *Executor) Signal(id string, signal int) error { - return e.client.SignalProcess(context.Background(), id, libcontainerd.InitProcessName, signal) + return e.client.SignalProcess(context.Background(), id, libcontainerdtypes.InitProcessName, signal) } // ProcessEvent handles events from containerd // All events are ignored except the exit event, which is sent of to the stored handler -func (e *Executor) ProcessEvent(id string, et libcontainerd.EventType, ei libcontainerd.EventInfo) error { +func (e *Executor) ProcessEvent(id string, et libcontainerdtypes.EventType, ei libcontainerdtypes.EventInfo) error { switch et { - case libcontainerd.EventExit: - deleteTaskAndContainer(context.Background(), e.client, id) + case libcontainerdtypes.EventExit: + deleteTaskAndContainer(context.Background(), e.client, id, nil) return e.exitHandler.HandleExitEvent(ei.ContainerID) } return nil @@ -152,7 +141,7 @@ func (c *rio) Wait() { c.IO.Wait() } -func attachStreamsFunc(stdout, stderr io.WriteCloser) libcontainerd.StdioCallback { +func attachStreamsFunc(stdout, stderr io.WriteCloser) libcontainerdtypes.StdioCallback { return func(iop *cio.DirectIO) (cio.IO, error) { if iop.Stdin != nil { iop.Stdin.Close() diff --git a/plugin/executor/containerd/containerd_test.go b/plugin/executor/containerd/containerd_test.go deleted file mode 100644 index e27063b1d81f9..0000000000000 --- a/plugin/executor/containerd/containerd_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package containerd - -import ( - "context" - "io/ioutil" - "os" - "sync" - "testing" - "time" - - "github.com/docker/docker/libcontainerd" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "gotest.tools/assert" -) - -func TestLifeCycle(t *testing.T) { - t.Parallel() - - mock := newMockClient() - exec, cleanup := setupTest(t, mock, mock) - defer cleanup() - - id := "test-create" - mock.simulateStartError(true, id) - err := exec.Create(id, specs.Spec{}, nil, nil) - assert.Assert(t, err != nil) - mock.simulateStartError(false, id) - - err = exec.Create(id, specs.Spec{}, nil, nil) - assert.Assert(t, err) - running, _ := exec.IsRunning(id) - assert.Assert(t, running) - - // create with the same ID - err = exec.Create(id, specs.Spec{}, nil, nil) - assert.Assert(t, err != nil) - - mock.HandleExitEvent(id) // simulate a plugin that exits - - err = exec.Create(id, specs.Spec{}, nil, nil) - assert.Assert(t, err) -} - -func setupTest(t *testing.T, client Client, eh ExitHandler) (*Executor, func()) { - rootDir, err := ioutil.TempDir("", "test-daemon") - assert.Assert(t, err) - assert.Assert(t, client != nil) - assert.Assert(t, eh != nil) - - return &Executor{ - rootDir: rootDir, - client: client, - exitHandler: eh, - }, func() { - assert.Assert(t, os.RemoveAll(rootDir)) - } -} - -type mockClient struct { - mu sync.Mutex - containers map[string]bool - errorOnStart map[string]bool -} - -func newMockClient() *mockClient { - return &mockClient{ - containers: make(map[string]bool), - errorOnStart: make(map[string]bool), - } -} - -func (c *mockClient) Create(ctx context.Context, id string, _ *specs.Spec, _ interface{}) error { - c.mu.Lock() - defer c.mu.Unlock() - - if _, ok := c.containers[id]; ok { - return errors.New("exists") - } - - c.containers[id] = false - return nil -} - -func (c *mockClient) Restore(ctx context.Context, id string, attachStdio libcontainerd.StdioCallback) (alive bool, pid int, err error) { - return false, 0, nil -} - -func (c *mockClient) Status(ctx context.Context, id string) (libcontainerd.Status, error) { - c.mu.Lock() - defer c.mu.Unlock() - - running, ok := c.containers[id] - if !ok { - return libcontainerd.StatusUnknown, errors.New("not found") - } - if running { - return libcontainerd.StatusRunning, nil - } - return libcontainerd.StatusStopped, nil -} - -func (c *mockClient) Delete(ctx context.Context, id string) error { - c.mu.Lock() - defer c.mu.Unlock() - delete(c.containers, id) - return nil -} - -func (c *mockClient) DeleteTask(ctx context.Context, id string) (uint32, time.Time, error) { - return 0, time.Time{}, nil -} - -func (c *mockClient) Start(ctx context.Context, id, checkpointDir string, withStdin bool, attachStdio libcontainerd.StdioCallback) (pid int, err error) { - c.mu.Lock() - defer c.mu.Unlock() - - if _, ok := c.containers[id]; !ok { - return 0, errors.New("not found") - } - - if c.errorOnStart[id] { - return 0, errors.New("some startup error") - } - c.containers[id] = true - return 1, nil -} - -func (c *mockClient) SignalProcess(ctx context.Context, containerID, processID string, signal int) error { - return nil -} - -func (c *mockClient) simulateStartError(sim bool, id string) { - c.mu.Lock() - defer c.mu.Unlock() - if sim { - c.errorOnStart[id] = sim - return - } - delete(c.errorOnStart, id) -} - -func (c *mockClient) HandleExitEvent(id string) error { - c.mu.Lock() - defer c.mu.Unlock() - delete(c.containers, id) - return nil -} diff --git a/plugin/fetch_linux.go b/plugin/fetch_linux.go new file mode 100644 index 0000000000000..8ec85f9f36118 --- /dev/null +++ b/plugin/fetch_linux.go @@ -0,0 +1,285 @@ +package plugin + +import ( + "context" + "io" + "net/http" + "time" + + "github.com/containerd/containerd/content" + c8derrdefs "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/remotes" + "github.com/containerd/containerd/remotes/docker" + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + progressutils "github.com/docker/docker/distribution/utils" + "github.com/docker/docker/pkg/chrootarchive" + "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/progress" + "github.com/docker/docker/pkg/stringid" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const mediaTypePluginConfig = "application/vnd.docker.plugin.v1+json" + +// setupProgressOutput sets up the passed in writer to stream progress. +// +// The passed in cancel function is used by the progress writer to signal callers that there +// is an issue writing to the stream. +// +// The returned function is used to wait for the progress writer to be finished. +// Call it to make sure the progress writer is done before returning from your function as needed. +func setupProgressOutput(outStream io.Writer, cancel func()) (progress.Output, func()) { + var out progress.Output + f := func() {} + + if outStream != nil { + ch := make(chan progress.Progress, 100) + out = progress.ChanOutput(ch) + + ctx, retCancel := context.WithCancel(context.Background()) + go func() { + progressutils.WriteDistributionProgress(cancel, outStream, ch) + retCancel() + }() + + f = func() { + close(ch) + <-ctx.Done() + } + } else { + out = progress.DiscardOutput() + } + return out, f +} + +// fetch the content related to the passed in reference into the blob store and appends the provided images.Handlers +// There is no need to use remotes.FetchHandler since it already gets set +func (pm *Manager) fetch(ctx context.Context, ref reference.Named, auth *types.AuthConfig, out progress.Output, metaHeader http.Header, handlers ...images.Handler) (err error) { + // We need to make sure we have a domain on the reference + withDomain, err := reference.ParseNormalizedNamed(ref.String()) + if err != nil { + return errors.Wrap(err, "error parsing plugin image reference") + } + + // Make sure we can authenticate the request since the auth scope for plugin repos is different than a normal repo. + ctx = docker.WithScope(ctx, scope(ref, false)) + + // Make sure the fetch handler knows how to set a ref key for the plugin media type. + // Without this the ref key is "unknown" and we see a nasty warning message in the logs + ctx = remotes.WithMediaTypeKeyPrefix(ctx, mediaTypePluginConfig, "docker-plugin") + + resolver, err := pm.newResolver(ctx, nil, auth, metaHeader, false) + if err != nil { + return err + } + resolved, desc, err := resolver.Resolve(ctx, withDomain.String()) + if err != nil { + // This is backwards compatible with older versions of the distribution registry. + // The containerd client will add it's own accept header as a comma separated list of supported manifests. + // This is perfectly fine, unless you are talking to an older registry which does not split the comma separated list, + // so it is never able to match a media type and it falls back to schema1 (yuck) and fails because our manifest the + // fallback does not support plugin configs... + logrus.WithError(err).WithField("ref", withDomain).Debug("Error while resolving reference, falling back to backwards compatible accept header format") + headers := http.Header{} + headers.Add("Accept", images.MediaTypeDockerSchema2Manifest) + headers.Add("Accept", images.MediaTypeDockerSchema2ManifestList) + headers.Add("Accept", specs.MediaTypeImageManifest) + headers.Add("Accept", specs.MediaTypeImageIndex) + resolver, _ = pm.newResolver(ctx, nil, auth, headers, false) + if resolver != nil { + resolved, desc, err = resolver.Resolve(ctx, withDomain.String()) + if err != nil { + logrus.WithError(err).WithField("ref", withDomain).Debug("Failed to resolve reference after falling back to backwards compatible accept header format") + } + } + if err != nil { + return errors.Wrap(err, "error resolving plugin reference") + } + } + + fetcher, err := resolver.Fetcher(ctx, resolved) + if err != nil { + return errors.Wrap(err, "error creating plugin image fetcher") + } + + fp := withFetchProgress(pm.blobStore, out, ref) + handlers = append([]images.Handler{fp, remotes.FetchHandler(pm.blobStore, fetcher)}, handlers...) + return images.Dispatch(ctx, images.Handlers(handlers...), nil, desc) +} + +// applyLayer makes an images.HandlerFunc which applies a fetched image rootfs layer to a directory. +// +// TODO(@cpuguy83) This gets run sequentially after layer pull (makes sense), however +// if there are multiple layers to fetch we may end up extracting layers in the wrong +// order. +func applyLayer(cs content.Store, dir string, out progress.Output) images.HandlerFunc { + return func(ctx context.Context, desc specs.Descriptor) ([]specs.Descriptor, error) { + switch desc.MediaType { + case + specs.MediaTypeImageLayer, + images.MediaTypeDockerSchema2Layer, + specs.MediaTypeImageLayerGzip, + images.MediaTypeDockerSchema2LayerGzip: + default: + return nil, nil + } + + ra, err := cs.ReaderAt(ctx, desc) + if err != nil { + return nil, errors.Wrapf(err, "error getting content from content store for digest %s", desc.Digest) + } + + id := stringid.TruncateID(desc.Digest.String()) + + rc := ioutils.NewReadCloserWrapper(content.NewReader(ra), ra.Close) + pr := progress.NewProgressReader(rc, out, desc.Size, id, "Extracting") + defer pr.Close() + + if _, err := chrootarchive.ApplyLayer(dir, pr); err != nil { + return nil, errors.Wrapf(err, "error applying layer for digest %s", desc.Digest) + } + progress.Update(out, id, "Complete") + return nil, nil + } +} + +func childrenHandler(cs content.Store) images.HandlerFunc { + ch := images.ChildrenHandler(cs) + return func(ctx context.Context, desc specs.Descriptor) ([]specs.Descriptor, error) { + switch desc.MediaType { + case mediaTypePluginConfig: + return nil, nil + default: + return ch(ctx, desc) + } + } +} + +type fetchMeta struct { + blobs []digest.Digest + config digest.Digest + manifest digest.Digest +} + +func storeFetchMetadata(m *fetchMeta) images.HandlerFunc { + return func(ctx context.Context, desc specs.Descriptor) ([]specs.Descriptor, error) { + switch desc.MediaType { + case + images.MediaTypeDockerSchema2LayerForeignGzip, + images.MediaTypeDockerSchema2Layer, + specs.MediaTypeImageLayer, + specs.MediaTypeImageLayerGzip: + m.blobs = append(m.blobs, desc.Digest) + case specs.MediaTypeImageManifest, images.MediaTypeDockerSchema2Manifest: + m.manifest = desc.Digest + case mediaTypePluginConfig: + m.config = desc.Digest + } + return nil, nil + } +} + +func validateFetchedMetadata(md fetchMeta) error { + if md.config == "" { + return errors.New("fetched plugin image but plugin config is missing") + } + if md.manifest == "" { + return errors.New("fetched plugin image but manifest is missing") + } + return nil +} + +// withFetchProgress is a fetch handler which registers a descriptor with a progress +func withFetchProgress(cs content.Store, out progress.Output, ref reference.Named) images.HandlerFunc { + return func(ctx context.Context, desc specs.Descriptor) ([]specs.Descriptor, error) { + switch desc.MediaType { + case specs.MediaTypeImageManifest, images.MediaTypeDockerSchema2Manifest: + tn := reference.TagNameOnly(ref) + tagged := tn.(reference.Tagged) + progress.Messagef(out, tagged.Tag(), "Pulling from %s", reference.FamiliarName(ref)) + progress.Messagef(out, "", "Digest: %s", desc.Digest.String()) + return nil, nil + case + images.MediaTypeDockerSchema2LayerGzip, + images.MediaTypeDockerSchema2Layer, + specs.MediaTypeImageLayer, + specs.MediaTypeImageLayerGzip: + default: + return nil, nil + } + + id := stringid.TruncateID(desc.Digest.String()) + + if _, err := cs.Info(ctx, desc.Digest); err == nil { + out.WriteProgress(progress.Progress{ID: id, Action: "Already exists", LastUpdate: true}) + return nil, nil + } + + progress.Update(out, id, "Waiting") + + key := remotes.MakeRefKey(ctx, desc) + + go func() { + timer := time.NewTimer(100 * time.Millisecond) + if !timer.Stop() { + <-timer.C + } + defer timer.Stop() + + var pulling bool + var ctxErr error + + for { + timer.Reset(100 * time.Millisecond) + + select { + case <-ctx.Done(): + ctxErr = ctx.Err() + // make sure we can still fetch from the content store + // TODO: Might need to add some sort of timeout + ctx = context.Background() + case <-timer.C: + } + + s, err := cs.Status(ctx, key) + if err != nil { + if !c8derrdefs.IsNotFound(err) { + logrus.WithError(err).WithField("layerDigest", desc.Digest.String()).Error("Error looking up status of plugin layer pull") + progress.Update(out, id, err.Error()) + return + } + + if _, err := cs.Info(ctx, desc.Digest); err == nil { + progress.Update(out, id, "Download complete") + return + } + + if ctxErr != nil { + progress.Update(out, id, ctxErr.Error()) + return + } + + continue + } + + if !pulling { + progress.Update(out, id, "Pulling fs layer") + pulling = true + } + + if s.Offset == s.Total { + out.WriteProgress(progress.Progress{ID: id, Action: "Download complete", Current: s.Offset, LastUpdate: true}) + return + } + + out.WriteProgress(progress.Progress{ID: id, Action: "Downloading", Current: s.Offset, Total: s.Total}) + } + }() + return nil, nil + } +} diff --git a/plugin/manager.go b/plugin/manager.go index c6f896129b9b4..cfc49f285e536 100644 --- a/plugin/manager.go +++ b/plugin/manager.go @@ -1,9 +1,9 @@ package plugin // import "github.com/docker/docker/plugin" import ( + "context" "encoding/json" "io" - "io/ioutil" "os" "path/filepath" "reflect" @@ -12,19 +12,18 @@ import ( "strings" "sync" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/content/local" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" - "github.com/docker/docker/image" - "github.com/docker/docker/layer" "github.com/docker/docker/pkg/authorization" "github.com/docker/docker/pkg/ioutils" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/pubsub" "github.com/docker/docker/pkg/system" - "github.com/docker/docker/plugin/v2" + v2 "github.com/docker/docker/plugin/v2" "github.com/docker/docker/registry" - "github.com/opencontainers/go-digest" - "github.com/opencontainers/runtime-spec/specs-go" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -72,7 +71,7 @@ type Manager struct { mu sync.RWMutex // protects cMap muGC sync.RWMutex // protects blobstore deletions cMap map[*v2.Plugin]*controller - blobStore *basicBlobStore + blobStore content.Store publisher *pubsub.Publisher executor Executor } @@ -117,9 +116,9 @@ func NewManager(config ManagerConfig) (*Manager, error) { return nil, err } - manager.blobStore, err = newBasicBlobStore(filepath.Join(manager.config.Root, "storage/blobs")) + manager.blobStore, err = local.NewStore(filepath.Join(manager.config.Root, "storage")) if err != nil { - return nil, err + return nil, errors.Wrap(err, "error creating plugin blob store") } manager.cMap = make(map[*v2.Plugin]*controller) @@ -143,7 +142,7 @@ func (pm *Manager) HandleExitEvent(id string) error { return err } - if err := os.RemoveAll(filepath.Join(pm.config.ExecRoot, id)); err != nil && !os.IsNotExist(err) { + if err := os.RemoveAll(filepath.Join(pm.config.ExecRoot, id)); err != nil { logrus.WithError(err).WithField("id", id).Error("Could not remove plugin bundle dir") } @@ -158,10 +157,8 @@ func (pm *Manager) HandleExitEvent(id string) error { if restart { pm.enable(p, c, true) - } else { - if err := mount.RecursiveUnmount(filepath.Join(pm.config.Root, id)); err != nil { - return errors.Wrap(err, "error cleaning up plugin mounts") - } + } else if err := recursiveUnmount(filepath.Join(pm.config.Root, id)); err != nil { + return errors.Wrap(err, "error cleaning up plugin mounts") } return nil } @@ -171,7 +168,7 @@ func handleLoadError(err error, id string) { return } logger := logrus.WithError(err).WithField("id", id) - if os.IsNotExist(errors.Cause(err)) { + if errors.Is(err, os.ErrNotExist) { // Likely some error while removing on an older version of docker logger.Warn("missing plugin config, skipping: this may be caused due to a failed remove and requires manual cleanup.") return @@ -180,7 +177,7 @@ func handleLoadError(err error, id string) { } func (pm *Manager) reload() error { // todo: restore - dir, err := ioutil.ReadDir(pm.config.Root) + dir, err := os.ReadDir(pm.config.Root) if err != nil { return errors.Wrapf(err, "failed to read %v", pm.config.Root) } @@ -270,7 +267,7 @@ func (pm *Manager) Get(idOrName string) (*v2.Plugin, error) { func (pm *Manager) loadPlugin(id string) (*v2.Plugin, error) { p := filepath.Join(pm.config.Root, id, configFileName) - dt, err := ioutil.ReadFile(p) + dt, err := os.ReadFile(p) if err != nil { return nil, errors.Wrapf(err, "error reading %v", p) } @@ -297,15 +294,23 @@ func (pm *Manager) GC() { pm.muGC.Lock() defer pm.muGC.Unlock() - whitelist := make(map[digest.Digest]struct{}) + used := make(map[digest.Digest]struct{}) for _, p := range pm.config.Store.GetAll() { - whitelist[p.Config] = struct{}{} + used[p.Config] = struct{}{} for _, b := range p.Blobsums { - whitelist[b] = struct{}{} + used[b] = struct{}{} } } - pm.blobStore.gc(whitelist) + ctx := context.TODO() + pm.blobStore.Walk(ctx, func(info content.Info) error { + _, ok := used[info.Digest] + if ok { + return nil + } + + return pm.blobStore.Delete(ctx, info.Digest) + }) } type logHook struct{ id string } @@ -357,28 +362,3 @@ func isEqualPrivilege(a, b types.PluginPrivilege) bool { return reflect.DeepEqual(a.Value, b.Value) } - -func configToRootFS(c []byte) (*image.RootFS, error) { - var pluginConfig types.PluginConfig - if err := json.Unmarshal(c, &pluginConfig); err != nil { - return nil, err - } - // validation for empty rootfs is in distribution code - if pluginConfig.Rootfs == nil { - return nil, nil - } - - return rootFSFromPlugin(pluginConfig.Rootfs), nil -} - -func rootFSFromPlugin(pluginfs *types.PluginConfigRootfs) *image.RootFS { - rootFS := image.RootFS{ - Type: pluginfs.Type, - DiffIDs: make([]layer.DiffID, len(pluginfs.DiffIds)), - } - for i := range pluginfs.DiffIds { - rootFS.DiffIDs[i] = layer.DiffID(pluginfs.DiffIds[i]) - } - - return &rootFS -} diff --git a/plugin/manager_linux.go b/plugin/manager_linux.go index df1fe5b73dfac..4156eadf94582 100644 --- a/plugin/manager_linux.go +++ b/plugin/manager_linux.go @@ -1,22 +1,25 @@ package plugin // import "github.com/docker/docker/plugin" import ( + "context" "encoding/json" "net" "os" "path/filepath" "time" + "github.com/containerd/containerd/content" "github.com/docker/docker/api/types" "github.com/docker/docker/daemon/initlayer" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/containerfs" "github.com/docker/docker/pkg/idtools" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/plugins" "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/plugin/v2" - "github.com/opencontainers/go-digest" + v2 "github.com/docker/docker/plugin/v2" + "github.com/moby/sys/mount" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -61,7 +64,7 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error { if err := pm.executor.Create(p.GetID(), *spec, stdout, stderr); err != nil { if p.PluginObj.Config.PropagatedMount != "" { if err := mount.Unmount(propRoot); err != nil { - logrus.Warnf("Could not unmount %s: %v", propRoot, err) + logrus.WithField("plugin", p.Name()).WithError(err).Warn("Failed to unmount vplugin propagated mount root") } } return errors.WithStack(err) @@ -146,6 +149,8 @@ func (pm *Manager) restore(p *v2.Plugin, c *controller) error { return nil } +const shutdownTimeout = 10 * time.Second + func shutdownPlugin(p *v2.Plugin, ec chan bool, executor Executor) { pluginID := p.GetID() @@ -153,19 +158,26 @@ func shutdownPlugin(p *v2.Plugin, ec chan bool, executor Executor) { if err != nil { logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err) } else { + + timeout := time.NewTimer(shutdownTimeout) + defer timeout.Stop() + select { case <-ec: logrus.Debug("Clean shutdown of plugin") - case <-time.After(time.Second * 10): + case <-timeout.C: logrus.Debug("Force shutdown plugin") if err := executor.Signal(pluginID, int(unix.SIGKILL)); err != nil { logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err) } + + timeout.Reset(shutdownTimeout) + select { case <-ec: logrus.Debug("SIGKILL plugin shutdown") - case <-time.After(time.Second * 10): - logrus.Debug("Force shutdown plugin FAILED") + case <-timeout.C: + logrus.WithField("plugin", p.Name).Warn("Force shutdown plugin FAILED") } } } @@ -204,8 +216,8 @@ func (pm *Manager) Shutdown() { } } -func (pm *Manager) upgradePlugin(p *v2.Plugin, configDigest digest.Digest, blobsums []digest.Digest, tmpRootFSDir string, privileges *types.PluginPrivileges) (err error) { - config, err := pm.setupNewPlugin(configDigest, blobsums, privileges) +func (pm *Manager) upgradePlugin(p *v2.Plugin, configDigest, manifestDigest digest.Digest, blobsums []digest.Digest, tmpRootFSDir string, privileges *types.PluginPrivileges) (err error) { + config, err := pm.setupNewPlugin(configDigest, privileges) if err != nil { return err } @@ -227,7 +239,7 @@ func (pm *Manager) upgradePlugin(p *v2.Plugin, configDigest digest.Digest, blobs defer func() { if err != nil { - if rmErr := os.RemoveAll(orig); rmErr != nil && !os.IsNotExist(rmErr) { + if rmErr := os.RemoveAll(orig); rmErr != nil { logrus.WithError(rmErr).WithField("dir", backup).Error("error cleaning up after failed upgrade") return } @@ -238,7 +250,7 @@ func (pm *Manager) upgradePlugin(p *v2.Plugin, configDigest digest.Digest, blobs logrus.WithError(rmErr).WithField("plugin", p.Name()).Errorf("error cleaning up plugin upgrade dir: %s", tmpRootFSDir) } } else { - if rmErr := os.RemoveAll(backup); rmErr != nil && !os.IsNotExist(rmErr) { + if rmErr := os.RemoveAll(backup); rmErr != nil { logrus.WithError(rmErr).WithField("dir", backup).Error("error cleaning up old plugin root after successful upgrade") } @@ -252,19 +264,22 @@ func (pm *Manager) upgradePlugin(p *v2.Plugin, configDigest digest.Digest, blobs } p.PluginObj.Config = config + p.Manifest = manifestDigest err = pm.save(p) return errors.Wrap(err, "error saving upgraded plugin config") } -func (pm *Manager) setupNewPlugin(configDigest digest.Digest, blobsums []digest.Digest, privileges *types.PluginPrivileges) (types.PluginConfig, error) { - configRC, err := pm.blobStore.Get(configDigest) +func (pm *Manager) setupNewPlugin(configDigest digest.Digest, privileges *types.PluginPrivileges) (types.PluginConfig, error) { + configRA, err := pm.blobStore.ReaderAt(context.TODO(), specs.Descriptor{Digest: configDigest}) if err != nil { return types.PluginConfig{}, err } - defer configRC.Close() + defer configRA.Close() + + configR := content.NewReader(configRA) var config types.PluginConfig - dec := json.NewDecoder(configRC) + dec := json.NewDecoder(configR) if err := dec.Decode(&config); err != nil { return types.PluginConfig{}, errors.Wrapf(err, "failed to parse config") } @@ -273,9 +288,6 @@ func (pm *Manager) setupNewPlugin(configDigest digest.Digest, blobsums []digest. } requiredPrivileges := computePrivileges(config) - if err != nil { - return types.PluginConfig{}, err - } if privileges != nil { if err := validatePrivileges(requiredPrivileges, *privileges); err != nil { return types.PluginConfig{}, err @@ -286,12 +298,12 @@ func (pm *Manager) setupNewPlugin(configDigest digest.Digest, blobsums []digest. } // createPlugin creates a new plugin. take lock before calling. -func (pm *Manager) createPlugin(name string, configDigest digest.Digest, blobsums []digest.Digest, rootFSDir string, privileges *types.PluginPrivileges, opts ...CreateOpt) (p *v2.Plugin, err error) { +func (pm *Manager) createPlugin(name string, configDigest, manifestDigest digest.Digest, blobsums []digest.Digest, rootFSDir string, privileges *types.PluginPrivileges, opts ...CreateOpt) (p *v2.Plugin, err error) { if err := pm.config.Store.validateName(name); err != nil { // todo: this check is wrong. remove store return nil, errdefs.InvalidParameter(err) } - config, err := pm.setupNewPlugin(configDigest, blobsums, privileges) + config, err := pm.setupNewPlugin(configDigest, privileges) if err != nil { return nil, err } @@ -304,6 +316,7 @@ func (pm *Manager) createPlugin(name string, configDigest digest.Digest, blobsum }, Config: configDigest, Blobsums: blobsums, + Manifest: manifestDigest, } p.InitEmptySettings() for _, o := range opts { @@ -333,3 +346,7 @@ func (pm *Manager) createPlugin(name string, configDigest digest.Digest, blobsum return p, nil } + +func recursiveUnmount(target string) error { + return mount.RecursiveUnmount(target) +} diff --git a/plugin/manager_linux_test.go b/plugin/manager_linux_test.go index fd8fa8523c23a..b9f71e3702d8b 100644 --- a/plugin/manager_linux_test.go +++ b/plugin/manager_linux_test.go @@ -2,25 +2,25 @@ package plugin // import "github.com/docker/docker/plugin" import ( "io" - "io/ioutil" "net" "os" "path/filepath" "testing" "github.com/docker/docker/api/types" - "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/system" - "github.com/docker/docker/plugin/v2" - "github.com/opencontainers/runtime-spec/specs-go" + v2 "github.com/docker/docker/plugin/v2" + "github.com/moby/sys/mount" + "github.com/moby/sys/mountinfo" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "gotest.tools/skip" + "gotest.tools/v3/skip" ) func TestManagerWithPluginMounts(t *testing.T) { skip.If(t, os.Getuid() != 0, "skipping test that requires root") - root, err := ioutil.TempDir("", "test-store-with-plugin-mounts") + root, err := os.MkdirTemp("", "test-store-with-plugin-mounts") if err != nil { t.Fatal(err) } @@ -64,13 +64,13 @@ func TestManagerWithPluginMounts(t *testing.T) { if err := m.Remove(p1.GetID(), &types.PluginRmConfig{ForceRemove: true}); err != nil { t.Fatal(err) } - if mounted, err := mount.Mounted(p2Mount); !mounted || err != nil { + if mounted, err := mountinfo.Mounted(p2Mount); !mounted || err != nil { t.Fatalf("expected %s to be mounted, err: %v", p2Mount, err) } } func newTestPlugin(t *testing.T, name, cap, root string) *v2.Plugin { - id := stringid.GenerateNonCryptoID() + id := stringid.GenerateRandomID() rootfs := filepath.Join(root, id) if err := os.MkdirAll(rootfs, 0755); err != nil { t.Fatal(err) @@ -106,7 +106,7 @@ func (e *simpleExecutor) Signal(id string, signal int) error { } func TestCreateFailed(t *testing.T) { - root, err := ioutil.TempDir("", "test-create-failed") + root, err := os.MkdirTemp("", "test-create-failed") if err != nil { t.Fatal(err) } @@ -174,9 +174,10 @@ func (e *executorWithRunning) Signal(id string, signal int) error { } func TestPluginAlreadyRunningOnStartup(t *testing.T) { + skip.If(t, os.Getuid() != 0, "skipping test that requires root") t.Parallel() - root, err := ioutil.TempDir("", t.Name()) + root, err := os.MkdirTemp("", t.Name()) if err != nil { t.Fatal(err) } @@ -209,7 +210,7 @@ func TestPluginAlreadyRunningOnStartup(t *testing.T) { p.PluginObj.Enabled = true // Need a short-ish path here so we don't run into unix socket path length issues. - config.ExecRoot, err = ioutil.TempDir("", "plugintest") + config.ExecRoot, err = os.MkdirTemp("", "plugintest") executor := &executorWithRunning{root: config.ExecRoot} config.CreateExecutor = func(m *Manager) (Executor, error) { executor.m = m; return executor, nil } diff --git a/plugin/manager_windows.go b/plugin/manager_windows.go index 90cc52c99253c..003b2210f9fd1 100644 --- a/plugin/manager_windows.go +++ b/plugin/manager_windows.go @@ -3,7 +3,7 @@ package plugin // import "github.com/docker/docker/plugin" import ( "fmt" - "github.com/docker/docker/plugin/v2" + v2 "github.com/docker/docker/plugin/v2" specs "github.com/opencontainers/runtime-spec/specs-go" ) @@ -26,3 +26,7 @@ func (pm *Manager) restore(p *v2.Plugin, c *controller) error { // Shutdown plugins func (pm *Manager) Shutdown() { } + +func recursiveUnmount(_ string) error { + return nil +} diff --git a/plugin/progress.go b/plugin/progress.go new file mode 100644 index 0000000000000..ffe6c70aaeac3 --- /dev/null +++ b/plugin/progress.go @@ -0,0 +1,74 @@ +package plugin + +import ( + "sync" + "time" + + "github.com/containerd/containerd/remotes/docker" +) + +func newPushJobs(tracker docker.StatusTracker) *pushJobs { + return &pushJobs{ + names: make(map[string]string), + t: tracker, + } +} + +type pushJobs struct { + t docker.StatusTracker + + mu sync.Mutex + jobs []string + // maps job ref to a name + names map[string]string +} + +func (p *pushJobs) add(id, name string) { + p.mu.Lock() + defer p.mu.Unlock() + + if _, ok := p.names[id]; ok { + return + } + p.jobs = append(p.jobs, id) + p.names[id] = name +} + +func (p *pushJobs) status() []contentStatus { + statuses := make([]contentStatus, 0, len(p.jobs)) + + p.mu.Lock() + defer p.mu.Unlock() + + for _, j := range p.jobs { + var s contentStatus + s.Ref = p.names[j] + + status, err := p.t.GetStatus(j) + if err != nil { + s.Status = "Waiting" + } else { + s.Total = status.Total + s.Offset = status.Offset + s.StartedAt = status.StartedAt + s.UpdatedAt = status.UpdatedAt + if status.UploadUUID == "" { + s.Status = "Upload complete" + } else { + s.Status = "Uploading" + } + } + statuses = append(statuses, s) + } + + return statuses +} + +type contentStatus struct { + Status string + Total int64 + Offset int64 + StartedAt time.Time + UpdatedAt time.Time + Ref string +} diff --git a/plugin/registry.go b/plugin/registry.go new file mode 100644 index 0000000000000..ad2a6b7138f50 --- /dev/null +++ b/plugin/registry.go @@ -0,0 +1,111 @@ +package plugin + +import ( + "context" + "crypto/tls" + "net" + "net/http" + "time" + + "github.com/sirupsen/logrus" + + "github.com/docker/docker/dockerversion" + + "github.com/pkg/errors" + + "github.com/containerd/containerd/remotes" + "github.com/containerd/containerd/remotes/docker" + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" +) + +// scope builds the correct auth scope for the registry client to authorize against +// By default the client currently only does a "repository:" scope with out a classifier, e.g. "(plugin)" +// Without this, the client will not be able to authorize the request +func scope(ref reference.Named, push bool) string { + scope := "repository(plugin):" + reference.Path(reference.TrimNamed(ref)) + ":pull" + if push { + scope += ",push" + } + return scope +} + +func (pm *Manager) newResolver(ctx context.Context, tracker docker.StatusTracker, auth *types.AuthConfig, headers http.Header, httpFallback bool) (remotes.Resolver, error) { + if headers == nil { + headers = http.Header{} + } + headers.Add("User-Agent", dockerversion.DockerUserAgent(ctx)) + + return docker.NewResolver(docker.ResolverOptions{ + Tracker: tracker, + Headers: headers, + Hosts: pm.registryHostsFn(auth, httpFallback), + }), nil +} + +func registryHTTPClient(config *tls.Config) *http.Client { + return &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + TLSClientConfig: config, + TLSHandshakeTimeout: 10 * time.Second, + IdleConnTimeout: 30 * time.Second, + }, + } +} + +func (pm *Manager) registryHostsFn(auth *types.AuthConfig, httpFallback bool) docker.RegistryHosts { + return func(hostname string) ([]docker.RegistryHost, error) { + eps, err := pm.config.RegistryService.LookupPullEndpoints(hostname) + if err != nil { + return nil, errors.Wrapf(err, "error resolving repository for %s", hostname) + } + + hosts := make([]docker.RegistryHost, 0, len(eps)) + + for _, ep := range eps { + // forced http fallback is used only for push since the containerd pusher only ever uses the first host we + // pass to it. + // So it is the callers responsibility to retry with this flag set. + if httpFallback && ep.URL.Scheme != "http" { + logrus.WithField("registryHost", hostname).WithField("endpoint", ep).Debugf("Skipping non-http endpoint") + continue + } + + caps := docker.HostCapabilityPull | docker.HostCapabilityResolve + if !ep.Mirror { + caps = caps | docker.HostCapabilityPush + } + + host, err := docker.DefaultHost(ep.URL.Host) + if err != nil { + return nil, err + } + + client := registryHTTPClient(ep.TLSConfig) + hosts = append(hosts, docker.RegistryHost{ + Host: host, + Scheme: ep.URL.Scheme, + Client: client, + Path: "/v2", + Capabilities: caps, + Authorizer: docker.NewDockerAuthorizer( + docker.WithAuthClient(client), + docker.WithAuthCreds(func(_ string) (string, string, error) { + if auth.IdentityToken != "" { + return "", auth.IdentityToken, nil + } + return auth.Username, auth.Password, nil + }), + ), + }) + } + logrus.WithField("registryHost", hostname).WithField("hosts", hosts).Debug("Resolved registry hosts") + + return hosts, nil + } +} diff --git a/plugin/store.go b/plugin/store.go index 8e96c11da4e45..114fcf17a148f 100644 --- a/plugin/store.go +++ b/plugin/store.go @@ -8,8 +8,8 @@ import ( "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" - "github.com/docker/docker/plugin/v2" - "github.com/opencontainers/runtime-spec/specs-go" + v2 "github.com/docker/docker/plugin/v2" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -153,7 +153,8 @@ func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlug // but we should error out right away return nil, errDisabled(name) } - if _, ok := errors.Cause(err).(errNotFound); !ok { + var ierr errNotFound + if !errors.As(err, &ierr) { return nil, err } } @@ -166,7 +167,7 @@ func (ps *Store) Get(name, capability string, mode int) (plugingetter.CompatPlug if err == nil { return p, nil } - if errors.Cause(err) == plugins.ErrNotFound { + if errors.Is(err, plugins.ErrNotFound) { return nil, errNotFound(name) } return nil, errors.Wrap(errdefs.System(err), "legacy plugin") @@ -188,9 +189,7 @@ func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, er * bypassing the daemon. For such tests, this check is necessary. */ if ps != nil { - ps.RLock() result = ps.getAllByCap(capability) - ps.RUnlock() } // Lookup with legacy model @@ -250,10 +249,8 @@ func (ps *Store) CallHandler(p *v2.Plugin) { } } +// resolvePluginID must be protected by ps.RLock func (ps *Store) resolvePluginID(idOrName string) (string, error) { - ps.RLock() // todo: fix - defer ps.RUnlock() - if validFullID.MatchString(idOrName) { return idOrName, nil } diff --git a/plugin/store_test.go b/plugin/store_test.go index 14b484f76c70b..47b4bd7d4ddd6 100644 --- a/plugin/store_test.go +++ b/plugin/store_test.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/docker/plugin/v2" + v2 "github.com/docker/docker/plugin/v2" ) func TestFilterByCapNeg(t *testing.T) { diff --git a/plugin/v2/plugin.go b/plugin/v2/plugin.go index 6852511c5e664..d42e1bd0b94e6 100644 --- a/plugin/v2/plugin.go +++ b/plugin/v2/plugin.go @@ -11,8 +11,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/plugingetter" "github.com/docker/docker/pkg/plugins" - "github.com/opencontainers/go-digest" - "github.com/opencontainers/runtime-spec/specs-go" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/runtime-spec/specs-go" ) // Plugin represents an individual plugin. @@ -25,6 +25,7 @@ type Plugin struct { Config digest.Digest Blobsums []digest.Digest + Manifest digest.Digest modifyRuntimeSpec func(*specs.Spec) @@ -125,7 +126,9 @@ func (p *Plugin) Set(args []string) error { // TODO(vieux): lots of code duplication here, needs to be refactored. next: - for _, s := range sets { + for _, set := range sets { + s := set + // range over all the envs in the config for _, env := range p.PluginObj.Config.Env { // found the env in the config diff --git a/plugin/v2/plugin_linux.go b/plugin/v2/plugin_linux.go index 58c432fcd6254..4ad582cd834d7 100644 --- a/plugin/v2/plugin_linux.go +++ b/plugin/v2/plugin_linux.go @@ -9,7 +9,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/oci" "github.com/docker/docker/pkg/system" - "github.com/opencontainers/runtime-spec/specs-go" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) diff --git a/plugin/v2/plugin_unsupported.go b/plugin/v2/plugin_unsupported.go index 5242fe124ca16..1b08aec171bbc 100644 --- a/plugin/v2/plugin_unsupported.go +++ b/plugin/v2/plugin_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package v2 // import "github.com/docker/docker/plugin/v2" @@ -5,7 +6,7 @@ package v2 // import "github.com/docker/docker/plugin/v2" import ( "errors" - "github.com/opencontainers/runtime-spec/specs-go" + specs "github.com/opencontainers/runtime-spec/specs-go" ) // InitSpec creates an OCI spec from the plugin's config. diff --git a/poule.yml b/poule.yml index d05c764b38ab5..0407a20f20ad3 100644 --- a/poule.yml +++ b/poule.yml @@ -99,36 +99,3 @@ configurations: [ z ], label: "rebuild/z", } - -# Once a day, randomly assign pull requests older than 2 weeks. -- schedule: "@daily" - operations: - - type: random-assign - filters: { - age: "2w", - is: "pr", - } - settings: { - users: [ - "aaronlehmann", - "akihirosuda", - "coolljt0725", - "cpuguy83", - "crosbymichael", - "dnephin", - "duglin", - "fntlnz", - "johnstep", - "justincormack", - "mhbauer", - "mlaventure", - "runcom", - "stevvooe", - "thajeztah", - "tiborvass", - "tonistiigi", - "vdemeester", - "vieux", - "yongtang", - ] - } diff --git a/profiles/apparmor/apparmor.go b/profiles/apparmor/apparmor.go index b021668c8e4ca..b3566b2f7354d 100644 --- a/profiles/apparmor/apparmor.go +++ b/profiles/apparmor/apparmor.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package apparmor // import "github.com/docker/docker/profiles/apparmor" @@ -5,7 +6,6 @@ package apparmor // import "github.com/docker/docker/profiles/apparmor" import ( "bufio" "io" - "io/ioutil" "os" "path" "strings" @@ -23,6 +23,8 @@ var ( type profileData struct { // Name is profile name. Name string + // DaemonProfile is the profile name of our daemon. + DaemonProfile string // Imports defines the apparmor functions to import, before defining the profile. Imports []string // InnerImports defines the apparmor functions to import in the profile. @@ -70,8 +72,27 @@ func InstallDefault(name string) error { Name: name, } + // Figure out the daemon profile. + currentProfile, err := os.ReadFile("/proc/self/attr/current") + if err != nil { + // If we couldn't get the daemon profile, assume we are running + // unconfined which is generally the default. + currentProfile = nil + } + daemonProfile := string(currentProfile) + // Normally profiles are suffixed by " (enforcing)" or similar. AppArmor + // profiles cannot contain spaces so this doesn't restrict daemon profile + // names. + if parts := strings.SplitN(daemonProfile, " ", 2); len(parts) >= 1 { + daemonProfile = parts[0] + } + if daemonProfile == "" { + daemonProfile = "unconfined" + } + p.DaemonProfile = daemonProfile + // Install to a temporary directory. - f, err := ioutil.TempFile("", name) + f, err := os.CreateTemp("", name) if err != nil { return err } diff --git a/profiles/apparmor/template.go b/profiles/apparmor/template.go index c00a3f70e9931..7c8525a71c810 100644 --- a/profiles/apparmor/template.go +++ b/profiles/apparmor/template.go @@ -1,7 +1,14 @@ +//go:build linux // +build linux package apparmor // import "github.com/docker/docker/profiles/apparmor" +// NOTE: This profile is replicated in containerd and libpod. If you make a +// change to this profile, please make follow-up PRs to those projects so +// that these rules can be synchronised (because any issue with this +// profile will likely affect libpod and containerd). +// TODO: Move this to a common project so we can maintain it in one spot. + // baseTemplate defines the default apparmor profile for containers. const baseTemplate = ` {{range $value := .Imports}} @@ -17,6 +24,14 @@ profile {{.Name}} flags=(attach_disconnected,mediate_deleted) { capability, file, umount, +{{if ge .Version 208096}} + # Host (privileged) processes may send signals to container processes. + signal (receive) peer=unconfined, + # dockerd may send signals to container processes (for "docker kill"). + signal (receive) peer={{.DaemonProfile}}, + # Container processes may send signals amongst themselves. + signal (send,receive) peer={{.Name}}, +{{end}} deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) # deny write to files not in /proc//** or /proc/sys/** @@ -38,7 +53,7 @@ profile {{.Name}} flags=(attach_disconnected,mediate_deleted) { {{if ge .Version 208095}} # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container - ptrace (trace,read) peer={{.Name}}, + ptrace (trace,read,tracedby,readby) peer={{.Name}}, {{end}} } ` diff --git a/profiles/seccomp/default.json b/profiles/seccomp/default.json old mode 100755 new mode 100644 index 0d954bb6d018c..fbf3ac8396489 --- a/profiles/seccomp/default.json +++ b/profiles/seccomp/default.json @@ -1,5 +1,6 @@ { "defaultAction": "SCMP_ACT_ERRNO", + "defaultErrnoRet": 1, "archMap": [ { "architecture": "SCMP_ARCH_X86_64", @@ -65,10 +66,16 @@ "chmod", "chown", "chown32", + "clock_adjtime", + "clock_adjtime64", "clock_getres", + "clock_getres_time64", "clock_gettime", + "clock_gettime64", "clock_nanosleep", + "clock_nanosleep_time64", "close", + "close_range", "connect", "copy_file_range", "creat", @@ -80,6 +87,7 @@ "epoll_ctl", "epoll_ctl_old", "epoll_pwait", + "epoll_pwait2", "epoll_wait", "epoll_wait_old", "eventfd", @@ -89,6 +97,7 @@ "exit", "exit_group", "faccessat", + "faccessat2", "fadvise64", "fadvise64_64", "fallocate", @@ -117,6 +126,7 @@ "ftruncate", "ftruncate64", "futex", + "futex_time64", "futimesat", "getcpu", "getcwd", @@ -162,10 +172,15 @@ "ioctl", "io_destroy", "io_getevents", + "io_pgetevents", + "io_pgetevents_time64", "ioprio_get", "ioprio_set", "io_setup", "io_submit", + "io_uring_enter", + "io_uring_register", + "io_uring_setup", "ipc", "kill", "lchown", @@ -183,6 +198,7 @@ "lstat", "lstat64", "madvise", + "membarrier", "memfd_create", "mincore", "mkdir", @@ -199,7 +215,9 @@ "mq_notify", "mq_open", "mq_timedreceive", + "mq_timedreceive_time64", "mq_timedsend", + "mq_timedsend_time64", "mq_unlink", "mremap", "msgctl", @@ -215,17 +233,22 @@ "_newselect", "open", "openat", + "openat2", "pause", + "pidfd_open", + "pidfd_send_signal", "pipe", "pipe2", "poll", "ppoll", + "ppoll_time64", "prctl", "pread64", "preadv", "preadv2", "prlimit64", "pselect6", + "pselect6_time64", "pwrite64", "pwritev", "pwritev2", @@ -237,6 +260,7 @@ "recv", "recvfrom", "recvmmsg", + "recvmmsg_time64", "recvmsg", "remap_file_pages", "removexattr", @@ -245,6 +269,7 @@ "renameat2", "restart_syscall", "rmdir", + "rseq", "rt_sigaction", "rt_sigpending", "rt_sigprocmask", @@ -252,6 +277,7 @@ "rt_sigreturn", "rt_sigsuspend", "rt_sigtimedwait", + "rt_sigtimedwait_time64", "rt_tgsigqueueinfo", "sched_getaffinity", "sched_getattr", @@ -260,6 +286,7 @@ "sched_get_priority_min", "sched_getscheduler", "sched_rr_get_interval", + "sched_rr_get_interval_time64", "sched_setaffinity", "sched_setattr", "sched_setparam", @@ -271,6 +298,7 @@ "semget", "semop", "semtimedop", + "semtimedop_time64", "send", "sendfile", "sendfile64", @@ -313,6 +341,7 @@ "sigaltstack", "signalfd", "signalfd4", + "sigprocmask", "sigreturn", "socket", "socketcall", @@ -334,12 +363,16 @@ "time", "timer_create", "timer_delete", - "timerfd_create", - "timerfd_gettime", - "timerfd_settime", "timer_getoverrun", "timer_gettime", + "timer_gettime64", "timer_settime", + "timer_settime64", + "timerfd_create", + "timerfd_gettime", + "timerfd_gettime64", + "timerfd_settime", + "timerfd_settime64", "times", "tkill", "truncate", @@ -351,6 +384,7 @@ "unlinkat", "utime", "utimensat", + "utimensat_time64", "utimes", "vfork", "vmsplice", @@ -360,11 +394,18 @@ "write", "writev" ], + "action": "SCMP_ACT_ALLOW" + }, + { + "names": [ + "process_vm_readv", + "process_vm_writev", + "ptrace" + ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", - "includes": {}, - "excludes": {} + "includes": { + "minKernel": "4.8" + } }, { "names": [ @@ -375,13 +416,9 @@ { "index": 0, "value": 0, - "valueTwo": 0, "op": "SCMP_CMP_EQ" } - ], - "comment": "", - "includes": {}, - "excludes": {} + ] }, { "names": [ @@ -392,13 +429,9 @@ { "index": 0, "value": 8, - "valueTwo": 0, "op": "SCMP_CMP_EQ" } - ], - "comment": "", - "includes": {}, - "excludes": {} + ] }, { "names": [ @@ -409,13 +442,9 @@ { "index": 0, "value": 131072, - "valueTwo": 0, "op": "SCMP_CMP_EQ" } - ], - "comment": "", - "includes": {}, - "excludes": {} + ] }, { "names": [ @@ -426,13 +455,9 @@ { "index": 0, "value": 131080, - "valueTwo": 0, "op": "SCMP_CMP_EQ" } - ], - "comment": "", - "includes": {}, - "excludes": {} + ] }, { "names": [ @@ -443,27 +468,20 @@ { "index": 0, "value": 4294967295, - "valueTwo": 0, "op": "SCMP_CMP_EQ" } - ], - "comment": "", - "includes": {}, - "excludes": {} + ] }, { "names": [ "sync_file_range2" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "arches": [ "ppc64le" ] - }, - "excludes": {} + } }, { "names": [ @@ -475,46 +493,37 @@ "set_tls" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "arches": [ "arm", "arm64" ] - }, - "excludes": {} + } }, { "names": [ "arch_prctl" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "arches": [ "amd64", "x32" ] - }, - "excludes": {} + } }, { "names": [ "modify_ldt" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "arches": [ "amd64", "x32", "x86" ] - }, - "excludes": {} + } }, { "names": [ @@ -523,38 +532,39 @@ "s390_runtime_instr" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "arches": [ "s390", "s390x" ] - }, - "excludes": {} + } }, { "names": [ "open_by_handle_at" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_DAC_READ_SEARCH" ] - }, - "excludes": {} + } }, { "names": [ "bpf", "clone", + "clone3", "fanotify_init", + "fsconfig", + "fsmount", + "fsopen", + "fspick", "lookup_dcookie", "mount", + "move_mount", "name_to_handle_at", + "open_tree", "perf_event_open", "quotactl", "setdomainname", @@ -566,14 +576,11 @@ "unshare" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYS_ADMIN" ] - }, - "excludes": {} + } }, { "names": [ @@ -583,13 +590,10 @@ "args": [ { "index": 0, - "value": 2080505856, - "valueTwo": 0, + "value": 2114060288, "op": "SCMP_CMP_MASKED_EQ" } ], - "comment": "", - "includes": {}, "excludes": { "caps": [ "CAP_SYS_ADMIN" @@ -608,8 +612,7 @@ "args": [ { "index": 1, - "value": 2080505856, - "valueTwo": 0, + "value": 2114060288, "op": "SCMP_CMP_MASKED_EQ" } ], @@ -626,81 +629,79 @@ ] } }, + { + "names": [ + "clone3" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 38, + "excludes": { + "caps": [ + "CAP_SYS_ADMIN" + ] + } + }, { "names": [ "reboot" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYS_BOOT" ] - }, - "excludes": {} + } }, { "names": [ "chroot" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYS_CHROOT" ] - }, - "excludes": {} + } }, { "names": [ "delete_module", "init_module", - "finit_module", - "query_module" + "finit_module" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYS_MODULE" ] - }, - "excludes": {} + } }, { "names": [ "acct" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYS_PACCT" ] - }, - "excludes": {} + } }, { "names": [ "kcmp", + "pidfd_getfd", + "process_madvise", "process_vm_readv", "process_vm_writev", "ptrace" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYS_PTRACE" ] - }, - "excludes": {} + } }, { "names": [ @@ -708,14 +709,11 @@ "ioperm" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYS_RAWIO" ] - }, - "excludes": {} + } }, { "names": [ @@ -724,28 +722,22 @@ "clock_settime" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYS_TIME" ] - }, - "excludes": {} + } }, { "names": [ "vhangup" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYS_TTY_CONFIG" ] - }, - "excludes": {} + } }, { "names": [ @@ -754,28 +746,22 @@ "set_mempolicy" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYS_NICE" ] - }, - "excludes": {} + } }, { "names": [ "syslog" ], "action": "SCMP_ACT_ALLOW", - "args": [], - "comment": "", "includes": { "caps": [ "CAP_SYSLOG" ] - }, - "excludes": {} + } } ] } \ No newline at end of file diff --git a/profiles/seccomp/default_linux.go b/profiles/seccomp/default_linux.go new file mode 100644 index 0000000000000..f7d93d2cc4f79 --- /dev/null +++ b/profiles/seccomp/default_linux.go @@ -0,0 +1,766 @@ +//go:build seccomp +// +build seccomp + +package seccomp // import "github.com/docker/docker/profiles/seccomp" + +import ( + "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/unix" +) + +func arches() []Architecture { + return []Architecture{ + { + Arch: specs.ArchX86_64, + SubArches: []specs.Arch{specs.ArchX86, specs.ArchX32}, + }, + { + Arch: specs.ArchAARCH64, + SubArches: []specs.Arch{specs.ArchARM}, + }, + { + Arch: specs.ArchMIPS64, + SubArches: []specs.Arch{specs.ArchMIPS, specs.ArchMIPS64N32}, + }, + { + Arch: specs.ArchMIPS64N32, + SubArches: []specs.Arch{specs.ArchMIPS, specs.ArchMIPS64}, + }, + { + Arch: specs.ArchMIPSEL64, + SubArches: []specs.Arch{specs.ArchMIPSEL, specs.ArchMIPSEL64N32}, + }, + { + Arch: specs.ArchMIPSEL64N32, + SubArches: []specs.Arch{specs.ArchMIPSEL, specs.ArchMIPSEL64}, + }, + { + Arch: specs.ArchS390X, + SubArches: []specs.Arch{specs.ArchS390}, + }, + } +} + +// DefaultProfile defines the allowed syscalls for the default seccomp profile. +func DefaultProfile() *Seccomp { + nosys := uint(unix.ENOSYS) + syscalls := []*Syscall{ + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "accept", + "accept4", + "access", + "adjtimex", + "alarm", + "bind", + "brk", + "capget", + "capset", + "chdir", + "chmod", + "chown", + "chown32", + "clock_adjtime", + "clock_adjtime64", + "clock_getres", + "clock_getres_time64", + "clock_gettime", + "clock_gettime64", + "clock_nanosleep", + "clock_nanosleep_time64", + "close", + "close_range", + "connect", + "copy_file_range", + "creat", + "dup", + "dup2", + "dup3", + "epoll_create", + "epoll_create1", + "epoll_ctl", + "epoll_ctl_old", + "epoll_pwait", + "epoll_pwait2", + "epoll_wait", + "epoll_wait_old", + "eventfd", + "eventfd2", + "execve", + "execveat", + "exit", + "exit_group", + "faccessat", + "faccessat2", + "fadvise64", + "fadvise64_64", + "fallocate", + "fanotify_mark", + "fchdir", + "fchmod", + "fchmodat", + "fchown", + "fchown32", + "fchownat", + "fcntl", + "fcntl64", + "fdatasync", + "fgetxattr", + "flistxattr", + "flock", + "fork", + "fremovexattr", + "fsetxattr", + "fstat", + "fstat64", + "fstatat64", + "fstatfs", + "fstatfs64", + "fsync", + "ftruncate", + "ftruncate64", + "futex", + "futex_time64", + "futimesat", + "getcpu", + "getcwd", + "getdents", + "getdents64", + "getegid", + "getegid32", + "geteuid", + "geteuid32", + "getgid", + "getgid32", + "getgroups", + "getgroups32", + "getitimer", + "getpeername", + "getpgid", + "getpgrp", + "getpid", + "getppid", + "getpriority", + "getrandom", + "getresgid", + "getresgid32", + "getresuid", + "getresuid32", + "getrlimit", + "get_robust_list", + "getrusage", + "getsid", + "getsockname", + "getsockopt", + "get_thread_area", + "gettid", + "gettimeofday", + "getuid", + "getuid32", + "getxattr", + "inotify_add_watch", + "inotify_init", + "inotify_init1", + "inotify_rm_watch", + "io_cancel", + "ioctl", + "io_destroy", + "io_getevents", + "io_pgetevents", + "io_pgetevents_time64", + "ioprio_get", + "ioprio_set", + "io_setup", + "io_submit", + "io_uring_enter", + "io_uring_register", + "io_uring_setup", + "ipc", + "kill", + "lchown", + "lchown32", + "lgetxattr", + "link", + "linkat", + "listen", + "listxattr", + "llistxattr", + "_llseek", + "lremovexattr", + "lseek", + "lsetxattr", + "lstat", + "lstat64", + "madvise", + "membarrier", + "memfd_create", + "mincore", + "mkdir", + "mkdirat", + "mknod", + "mknodat", + "mlock", + "mlock2", + "mlockall", + "mmap", + "mmap2", + "mprotect", + "mq_getsetattr", + "mq_notify", + "mq_open", + "mq_timedreceive", + "mq_timedreceive_time64", + "mq_timedsend", + "mq_timedsend_time64", + "mq_unlink", + "mremap", + "msgctl", + "msgget", + "msgrcv", + "msgsnd", + "msync", + "munlock", + "munlockall", + "munmap", + "nanosleep", + "newfstatat", + "_newselect", + "open", + "openat", + "openat2", + "pause", + "pidfd_open", + "pidfd_send_signal", + "pipe", + "pipe2", + "poll", + "ppoll", + "ppoll_time64", + "prctl", + "pread64", + "preadv", + "preadv2", + "prlimit64", + "pselect6", + "pselect6_time64", + "pwrite64", + "pwritev", + "pwritev2", + "read", + "readahead", + "readlink", + "readlinkat", + "readv", + "recv", + "recvfrom", + "recvmmsg", + "recvmmsg_time64", + "recvmsg", + "remap_file_pages", + "removexattr", + "rename", + "renameat", + "renameat2", + "restart_syscall", + "rmdir", + "rseq", + "rt_sigaction", + "rt_sigpending", + "rt_sigprocmask", + "rt_sigqueueinfo", + "rt_sigreturn", + "rt_sigsuspend", + "rt_sigtimedwait", + "rt_sigtimedwait_time64", + "rt_tgsigqueueinfo", + "sched_getaffinity", + "sched_getattr", + "sched_getparam", + "sched_get_priority_max", + "sched_get_priority_min", + "sched_getscheduler", + "sched_rr_get_interval", + "sched_rr_get_interval_time64", + "sched_setaffinity", + "sched_setattr", + "sched_setparam", + "sched_setscheduler", + "sched_yield", + "seccomp", + "select", + "semctl", + "semget", + "semop", + "semtimedop", + "semtimedop_time64", + "send", + "sendfile", + "sendfile64", + "sendmmsg", + "sendmsg", + "sendto", + "setfsgid", + "setfsgid32", + "setfsuid", + "setfsuid32", + "setgid", + "setgid32", + "setgroups", + "setgroups32", + "setitimer", + "setpgid", + "setpriority", + "setregid", + "setregid32", + "setresgid", + "setresgid32", + "setresuid", + "setresuid32", + "setreuid", + "setreuid32", + "setrlimit", + "set_robust_list", + "setsid", + "setsockopt", + "set_thread_area", + "set_tid_address", + "setuid", + "setuid32", + "setxattr", + "shmat", + "shmctl", + "shmdt", + "shmget", + "shutdown", + "sigaltstack", + "signalfd", + "signalfd4", + "sigprocmask", + "sigreturn", + "socket", + "socketcall", + "socketpair", + "splice", + "stat", + "stat64", + "statfs", + "statfs64", + "statx", + "symlink", + "symlinkat", + "sync", + "sync_file_range", + "syncfs", + "sysinfo", + "tee", + "tgkill", + "time", + "timer_create", + "timer_delete", + "timer_getoverrun", + "timer_gettime", + "timer_gettime64", + "timer_settime", + "timer_settime64", + "timerfd_create", + "timerfd_gettime", + "timerfd_gettime64", + "timerfd_settime", + "timerfd_settime64", + "times", + "tkill", + "truncate", + "truncate64", + "ugetrlimit", + "umask", + "uname", + "unlink", + "unlinkat", + "utime", + "utimensat", + "utimensat_time64", + "utimes", + "vfork", + "vmsplice", + "wait4", + "waitid", + "waitpid", + "write", + "writev", + }, + Action: specs.ActAllow, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "process_vm_readv", + "process_vm_writev", + "ptrace", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + MinKernel: &KernelVersion{4, 8}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{"personality"}, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{ + { + Index: 0, + Value: 0x0, + Op: specs.OpEqualTo, + }, + }, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{"personality"}, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{ + { + Index: 0, + Value: 0x0008, + Op: specs.OpEqualTo, + }, + }, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{"personality"}, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{ + { + Index: 0, + Value: 0x20000, + Op: specs.OpEqualTo, + }, + }, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{"personality"}, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{ + { + Index: 0, + Value: 0x20008, + Op: specs.OpEqualTo, + }, + }, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{"personality"}, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{ + { + Index: 0, + Value: 0xffffffff, + Op: specs.OpEqualTo, + }, + }, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "sync_file_range2", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Arches: []string{"ppc64le"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "arm_fadvise64_64", + "arm_sync_file_range", + "sync_file_range2", + "breakpoint", + "cacheflush", + "set_tls", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Arches: []string{"arm", "arm64"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "arch_prctl", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Arches: []string{"amd64", "x32"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "modify_ldt", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Arches: []string{"amd64", "x32", "x86"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "s390_pci_mmio_read", + "s390_pci_mmio_write", + "s390_runtime_instr", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Arches: []string{"s390", "s390x"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "open_by_handle_at", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_DAC_READ_SEARCH"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "bpf", + "clone", + "clone3", + "fanotify_init", + "fsconfig", + "fsmount", + "fsopen", + "fspick", + "lookup_dcookie", + "mount", + "move_mount", + "name_to_handle_at", + "open_tree", + "perf_event_open", + "quotactl", + "setdomainname", + "sethostname", + "setns", + "syslog", + "umount", + "umount2", + "unshare", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYS_ADMIN"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "clone", + }, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{ + { + Index: 0, + Value: unix.CLONE_NEWNS | unix.CLONE_NEWUTS | unix.CLONE_NEWIPC | unix.CLONE_NEWUSER | unix.CLONE_NEWPID | unix.CLONE_NEWNET | unix.CLONE_NEWCGROUP, + ValueTwo: 0, + Op: specs.OpMaskedEqual, + }, + }, + }, + Excludes: &Filter{ + Caps: []string{"CAP_SYS_ADMIN"}, + Arches: []string{"s390", "s390x"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "clone", + }, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{ + { + Index: 1, + Value: unix.CLONE_NEWNS | unix.CLONE_NEWUTS | unix.CLONE_NEWIPC | unix.CLONE_NEWUSER | unix.CLONE_NEWPID | unix.CLONE_NEWNET | unix.CLONE_NEWCGROUP, + ValueTwo: 0, + Op: specs.OpMaskedEqual, + }, + }, + }, + Comment: "s390 parameter ordering for clone is different", + Includes: &Filter{ + Arches: []string{"s390", "s390x"}, + }, + Excludes: &Filter{ + Caps: []string{"CAP_SYS_ADMIN"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "clone3", + }, + Action: specs.ActErrno, + ErrnoRet: &nosys, + }, + Excludes: &Filter{ + Caps: []string{"CAP_SYS_ADMIN"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "reboot", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYS_BOOT"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "chroot", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYS_CHROOT"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "delete_module", + "init_module", + "finit_module", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYS_MODULE"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "acct", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYS_PACCT"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "kcmp", + "pidfd_getfd", + "process_madvise", + "process_vm_readv", + "process_vm_writev", + "ptrace", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYS_PTRACE"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "iopl", + "ioperm", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYS_RAWIO"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "settimeofday", + "stime", + "clock_settime", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYS_TIME"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "vhangup", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYS_TTY_CONFIG"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "get_mempolicy", + "mbind", + "set_mempolicy", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYS_NICE"}, + }, + }, + { + LinuxSyscall: specs.LinuxSyscall{ + Names: []string{ + "syslog", + }, + Action: specs.ActAllow, + }, + Includes: &Filter{ + Caps: []string{"CAP_SYSLOG"}, + }, + }, + } + + errnoRet := uint(unix.EPERM) + return &Seccomp{ + LinuxSeccomp: specs.LinuxSeccomp{ + DefaultAction: specs.ActErrno, + DefaultErrnoRet: &errnoRet, + }, + ArchMap: arches(), + Syscalls: syscalls, + } +} diff --git a/profiles/seccomp/fixtures/conditional_include.json b/profiles/seccomp/fixtures/conditional_include.json new file mode 100644 index 0000000000000..09a3d2a7000fb --- /dev/null +++ b/profiles/seccomp/fixtures/conditional_include.json @@ -0,0 +1,23 @@ +{ + "defaultAction": "SCMP_ACT_ERRNO", + "syscalls": [ + { + "names": ["chmod"], + "action": "SCMP_ACT_ALLOW" + }, + { + "names": ["syslog"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYSLOG"] + } + }, + { + "names": ["ptrace"], + "action": "SCMP_ACT_ALLOW", + "excludes": { + "caps": ["CAP_SYS_ADMIN"] + } + } + ] +} diff --git a/profiles/seccomp/fixtures/default-old-format.json b/profiles/seccomp/fixtures/default-old-format.json new file mode 100644 index 0000000000000..0e52bf95ec346 --- /dev/null +++ b/profiles/seccomp/fixtures/default-old-format.json @@ -0,0 +1,1593 @@ +{ + "defaultAction": "SCMP_ACT_ERRNO", + "architectures": [ + "SCMP_ARCH_X86_64", + "SCMP_ARCH_X86", + "SCMP_ARCH_X32" + ], + "syscalls": [ + { + "name": "accept", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "accept4", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "access", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "alarm", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "bind", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "brk", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "capget", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "capset", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chmod", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chown", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chown32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clock_getres", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clock_gettime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clock_nanosleep", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "close", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "connect", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "copy_file_range", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "creat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "dup", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "dup2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "dup3", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_create", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_create1", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_ctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_ctl_old", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_pwait", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_wait", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "epoll_wait_old", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "eventfd", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "eventfd2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "execve", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "execveat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "exit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "exit_group", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "faccessat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fadvise64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fadvise64_64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fallocate", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fanotify_mark", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchmod", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchmodat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchown", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchown32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fchownat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fcntl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fcntl64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fdatasync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fgetxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "flistxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "flock", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fork", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fremovexattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fsetxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstatat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstatfs", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fstatfs64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "fsync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ftruncate", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ftruncate64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "futex", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "futimesat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getcpu", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getcwd", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getdents", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getdents64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getegid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getegid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "geteuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "geteuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgroups", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getgroups32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getitimer", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpeername", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpgrp", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getppid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getpriority", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getrandom", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getresuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getrlimit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "get_robust_list", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getrusage", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getsid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getsockname", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getsockopt", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "get_thread_area", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "gettid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "gettimeofday", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "getxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "inotify_add_watch", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "inotify_init", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "inotify_init1", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "inotify_rm_watch", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "io_cancel", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ioctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "io_destroy", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "io_getevents", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ioprio_get", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ioprio_set", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "io_setup", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "io_submit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ipc", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "kill", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lchown", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lchown32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lgetxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "link", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "linkat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "listen", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "listxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "llistxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "_llseek", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lremovexattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lseek", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lsetxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lstat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "lstat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "madvise", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "memfd_create", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mincore", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mkdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mkdirat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mknod", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mknodat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mlock", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mlock2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mlockall", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mmap", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mmap2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mprotect", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_getsetattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_notify", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_open", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_timedreceive", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_timedsend", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mq_unlink", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "mremap", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "msgctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "msgget", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "msgrcv", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "msgsnd", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "msync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "munlock", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "munlockall", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "munmap", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "nanosleep", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "newfstatat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "_newselect", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "open", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "openat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pause", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "personality", + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 0, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "name": "personality", + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 8, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "name": "personality", + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 4294967295, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "name": "pipe", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pipe2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "poll", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ppoll", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "prctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pread64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "preadv", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "prlimit64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pselect6", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pwrite64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "pwritev", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "read", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readahead", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readlink", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readlinkat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "readv", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "recv", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "recvfrom", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "recvmmsg", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "recvmsg", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "remap_file_pages", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "removexattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rename", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "renameat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "renameat2", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "restart_syscall", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rmdir", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigaction", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigpending", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigprocmask", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigqueueinfo", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigreturn", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigsuspend", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_sigtimedwait", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "rt_tgsigqueueinfo", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getaffinity", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getparam", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_get_priority_max", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_get_priority_min", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_getscheduler", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_rr_get_interval", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_setaffinity", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_setattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_setparam", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_setscheduler", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sched_yield", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "seccomp", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "select", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "semctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "semget", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "semop", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "semtimedop", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "send", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendfile", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendfile64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendmmsg", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendmsg", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sendto", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setfsgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setfsgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setfsuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setfsuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgroups", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setgroups32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setitimer", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setpgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setpriority", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setregid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setregid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setresgid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setresgid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setresuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setresuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setreuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setreuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setrlimit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "set_robust_list", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setsid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setsockopt", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "set_thread_area", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "set_tid_address", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setuid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setuid32", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "setxattr", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "shmat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "shmctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "shmdt", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "shmget", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "shutdown", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sigaltstack", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "signalfd", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "signalfd4", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sigreturn", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "socket", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "socketcall", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "socketpair", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "splice", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "stat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "stat64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "statfs", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "statfs64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "symlink", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "symlinkat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sync", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sync_file_range", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "syncfs", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "sysinfo", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "syslog", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "tee", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "tgkill", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "time", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_create", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_delete", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timerfd_create", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timerfd_gettime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timerfd_settime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_getoverrun", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_gettime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "timer_settime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "times", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "tkill", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "truncate", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "truncate64", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "ugetrlimit", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "umask", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "uname", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "unlink", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "unlinkat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "utime", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "utimensat", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "utimes", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "vfork", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "vmsplice", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "wait4", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "waitid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "waitpid", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "write", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "writev", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "arch_prctl", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "modify_ldt", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "chroot", + "action": "SCMP_ACT_ALLOW", + "args": [] + }, + { + "name": "clone", + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 2080505856, + "valueTwo": 0, + "op": "SCMP_CMP_MASKED_EQ" + } + ] + } + ] +} \ No newline at end of file diff --git a/profiles/seccomp/fixtures/example.json b/profiles/seccomp/fixtures/example.json old mode 100755 new mode 100644 index 674ca50fd9734..80c5a3152db95 --- a/profiles/seccomp/fixtures/example.json +++ b/profiles/seccomp/fixtures/example.json @@ -1,5 +1,6 @@ { "defaultAction": "SCMP_ACT_ERRNO", + "defaultErrnoRet": 1, "syscalls": [ { "name": "clone", @@ -7,7 +8,7 @@ "args": [ { "index": 0, - "value": 2080505856, + "value": 2114060288, "valueTwo": 0, "op": "SCMP_CMP_MASKED_EQ" } @@ -22,6 +23,12 @@ "name": "close", "action": "SCMP_ACT_ALLOW", "args": [] + }, + { + "name": "syslog", + "action": "SCMP_ACT_ERRNO", + "errnoRet": 12345, + "args": [] } ] } diff --git a/profiles/seccomp/generate.go b/profiles/seccomp/generate.go index 32f22bb375f32..a5d56247ab7cb 100644 --- a/profiles/seccomp/generate.go +++ b/profiles/seccomp/generate.go @@ -1,10 +1,10 @@ +//go:build ignore // +build ignore package main import ( "encoding/json" - "io/ioutil" "os" "path/filepath" @@ -26,7 +26,7 @@ func main() { panic(err) } - if err := ioutil.WriteFile(f, b, 0644); err != nil { + if err := os.WriteFile(f, b, 0644); err != nil { panic(err) } } diff --git a/profiles/seccomp/kernel_linux.go b/profiles/seccomp/kernel_linux.go new file mode 100644 index 0000000000000..558eabda3880c --- /dev/null +++ b/profiles/seccomp/kernel_linux.go @@ -0,0 +1,59 @@ +package seccomp + +import ( + "bytes" + "fmt" + "sync" + + "golang.org/x/sys/unix" +) + +var ( + currentKernelVersion *KernelVersion + kernelVersionError error + once sync.Once +) + +// getKernelVersion gets the current kernel version. +func getKernelVersion() (*KernelVersion, error) { + once.Do(func() { + var uts unix.Utsname + if err := unix.Uname(&uts); err != nil { + return + } + // Remove the \x00 from the release for Atoi to parse correctly + currentKernelVersion, kernelVersionError = parseRelease(string(uts.Release[:bytes.IndexByte(uts.Release[:], 0)])) + }) + return currentKernelVersion, kernelVersionError +} + +// parseRelease parses a string and creates a KernelVersion based on it. +func parseRelease(release string) (*KernelVersion, error) { + var version = KernelVersion{} + + // We're only make sure we get the "kernel" and "major revision". Sometimes we have + // 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64. + _, err := fmt.Sscanf(release, "%d.%d", &version.Kernel, &version.Major) + if err != nil { + return nil, fmt.Errorf("failed to parse kernel version %q: %w", release, err) + } + return &version, nil +} + +// kernelGreaterEqualThan checks if the host's kernel version is greater than, or +// equal to the given kernel version v. Only "kernel version" and "major revision" +// can be specified (e.g., "3.12") and will be taken into account, which means +// that 3.12.25-gentoo and 3.12-1-amd64 are considered equal (kernel: 3, major: 12). +func kernelGreaterEqualThan(minVersion KernelVersion) (bool, error) { + kv, err := getKernelVersion() + if err != nil { + return false, err + } + if kv.Kernel > minVersion.Kernel { + return true, nil + } + if kv.Kernel == minVersion.Kernel && kv.Major >= minVersion.Major { + return true, nil + } + return false, nil +} diff --git a/profiles/seccomp/kernel_linux_test.go b/profiles/seccomp/kernel_linux_test.go new file mode 100644 index 0000000000000..a56a97a5880b1 --- /dev/null +++ b/profiles/seccomp/kernel_linux_test.go @@ -0,0 +1,122 @@ +package seccomp + +import ( + "fmt" + "testing" +) + +func TestGetKernelVersion(t *testing.T) { + version, err := getKernelVersion() + if err != nil { + t.Fatal(err) + } + if version == nil { + t.Fatal("version is nil") + } + if version.Kernel == 0 { + t.Fatal("no kernel version") + } +} + +// TestParseRelease tests the ParseRelease() function +func TestParseRelease(t *testing.T) { + tests := []struct { + in string + out KernelVersion + expectedErr error + }{ + {in: "3.8", out: KernelVersion{Kernel: 3, Major: 8}}, + {in: "3.8.0", out: KernelVersion{Kernel: 3, Major: 8}}, + {in: "3.8.0-19-generic", out: KernelVersion{Kernel: 3, Major: 8}}, + {in: "3.4.54.longterm-1", out: KernelVersion{Kernel: 3, Major: 4}}, + {in: "3.10.0-862.2.3.el7.x86_64", out: KernelVersion{Kernel: 3, Major: 10}}, + {in: "3.12.8tag", out: KernelVersion{Kernel: 3, Major: 12}}, + {in: "3.12-1-amd64", out: KernelVersion{Kernel: 3, Major: 12}}, + {in: "3.12foobar", out: KernelVersion{Kernel: 3, Major: 12}}, + {in: "99.999.999-19-generic", out: KernelVersion{Kernel: 99, Major: 999}}, + {in: "", expectedErr: fmt.Errorf(`failed to parse kernel version "": EOF`)}, + {in: "3", expectedErr: fmt.Errorf(`failed to parse kernel version "3": unexpected EOF`)}, + {in: "3.", expectedErr: fmt.Errorf(`failed to parse kernel version "3.": EOF`)}, + {in: "3a", expectedErr: fmt.Errorf(`failed to parse kernel version "3a": input does not match format`)}, + {in: "3.a", expectedErr: fmt.Errorf(`failed to parse kernel version "3.a": expected integer`)}, + {in: "a", expectedErr: fmt.Errorf(`failed to parse kernel version "a": expected integer`)}, + {in: "a.a", expectedErr: fmt.Errorf(`failed to parse kernel version "a.a": expected integer`)}, + {in: "a.a.a-a", expectedErr: fmt.Errorf(`failed to parse kernel version "a.a.a-a": expected integer`)}, + {in: "-3", expectedErr: fmt.Errorf(`failed to parse kernel version "-3": expected integer`)}, + {in: "-3.", expectedErr: fmt.Errorf(`failed to parse kernel version "-3.": expected integer`)}, + {in: "-3.8", expectedErr: fmt.Errorf(`failed to parse kernel version "-3.8": expected integer`)}, + {in: "-3.-8", expectedErr: fmt.Errorf(`failed to parse kernel version "-3.-8": expected integer`)}, + {in: "3.-8", expectedErr: fmt.Errorf(`failed to parse kernel version "3.-8": expected integer`)}, + } + for _, tc := range tests { + tc := tc + t.Run(tc.in, func(t *testing.T) { + version, err := parseRelease(tc.in) + if tc.expectedErr != nil { + if err == nil { + t.Fatal("expected an error") + } + if err.Error() != tc.expectedErr.Error() { + t.Fatalf("expected: %s, got: %s", tc.expectedErr, err) + } + return + } + if err != nil { + t.Fatal("unexpected error:", err) + } + if version == nil { + t.Fatal("version is nil") + } + if version.Kernel != tc.out.Kernel || version.Major != tc.out.Major { + t.Fatalf("expected: %d.%d, got: %d.%d", tc.out.Kernel, tc.out.Major, version.Kernel, version.Major) + } + }) + } +} + +func TestKernelGreaterEqualThan(t *testing.T) { + // Get the current kernel version, so that we can make test relative to that + v, err := getKernelVersion() + if err != nil { + t.Fatal(err) + } + + tests := []struct { + doc string + in KernelVersion + expected bool + }{ + { + doc: "same version", + in: KernelVersion{v.Kernel, v.Major}, + expected: true, + }, + { + doc: "kernel minus one", + in: KernelVersion{v.Kernel - 1, v.Major}, + expected: true, + }, + { + doc: "kernel plus one", + in: KernelVersion{v.Kernel + 1, v.Major}, + expected: false, + }, + { + doc: "major plus one", + in: KernelVersion{v.Kernel, v.Major + 1}, + expected: false, + }, + } + for _, tc := range tests { + tc := tc + t.Run(tc.doc+": "+tc.in.String(), func(t *testing.T) { + ok, err := kernelGreaterEqualThan(tc.in) + if err != nil { + t.Fatal("unexpected error:", err) + } + if ok != tc.expected { + t.Fatalf("expected: %v, got: %v", tc.expected, ok) + } + }) + } +} diff --git a/profiles/seccomp/seccomp.go b/profiles/seccomp/seccomp.go index 4438670a5817a..45b1e4f76e0bd 100644 --- a/profiles/seccomp/seccomp.go +++ b/profiles/seccomp/seccomp.go @@ -1,160 +1,115 @@ -// +build linux - package seccomp // import "github.com/docker/docker/profiles/seccomp" import ( "encoding/json" - "errors" "fmt" + "strconv" + "strings" - "github.com/docker/docker/api/types" "github.com/opencontainers/runtime-spec/specs-go" - libseccomp "github.com/seccomp/libseccomp-golang" ) -//go:generate go run -tags 'seccomp' generate.go +// Seccomp represents the config for a seccomp profile for syscall restriction. +// It is used to marshal/unmarshal the JSON profiles as accepted by docker, and +// extends the runtime-spec's specs.LinuxSeccomp, overriding some fields to +// provide the ability to define conditional rules based on the host's kernel +// version, architecture, and the container's capabilities. +type Seccomp struct { + specs.LinuxSeccomp + + // ArchMap contains a list of Architectures and Sub-architectures for the + // profile. When generating the profile, this list is expanded to a + // []specs.Arch, to propagate the Architectures field of the profile. + ArchMap []Architecture `json:"archMap,omitempty"` + + // Syscalls contains lists of syscall rules. Rules can define conditions + // for them to be included or excluded in the resulting profile (based on + // on kernel version, architecture, capabilities, etc.). These lists are + // expanded to an specs.Syscall When generating the profile, these lists + // are expanded to a []specs.LinuxSyscall. + Syscalls []*Syscall `json:"syscalls"` +} -// GetDefaultProfile returns the default seccomp profile. -func GetDefaultProfile(rs *specs.Spec) (*specs.LinuxSeccomp, error) { - return setupSeccomp(DefaultProfile(), rs) +// Architecture is used to represent a specific architecture +// and its sub-architectures +type Architecture struct { + Arch specs.Arch `json:"architecture"` + SubArches []specs.Arch `json:"subArchitectures"` } -// LoadProfile takes a json string and decodes the seccomp profile. -func LoadProfile(body string, rs *specs.Spec) (*specs.LinuxSeccomp, error) { - var config types.Seccomp - if err := json.Unmarshal([]byte(body), &config); err != nil { - return nil, fmt.Errorf("Decoding seccomp profile failed: %v", err) - } - return setupSeccomp(&config, rs) +// Filter is used to conditionally apply Seccomp rules +type Filter struct { + Caps []string `json:"caps,omitempty"` + Arches []string `json:"arches,omitempty"` + + // MinKernel describes the minimum kernel version the rule must be applied + // on, in the format "." (e.g. "3.12"). + // + // When matching the kernel version of the host, minor revisions, and distro- + // specific suffixes are ignored, which means that "3.12.25-gentoo", "3.12-1-amd64", + // "3.12", and "3.12-rc5" are considered equal (kernel 3, major revision 12). + MinKernel *KernelVersion `json:"minKernel,omitempty"` } -var nativeToSeccomp = map[string]types.Arch{ - "amd64": types.ArchX86_64, - "arm64": types.ArchAARCH64, - "mips64": types.ArchMIPS64, - "mips64n32": types.ArchMIPS64N32, - "mipsel64": types.ArchMIPSEL64, - "mipsel64n32": types.ArchMIPSEL64N32, - "s390x": types.ArchS390X, +// Syscall is used to match a group of syscalls in Seccomp. It extends the +// runtime-spec Syscall type, adding a "Name" field for backward compatibility +// with older JSON representations, additional "Comment" metadata, and conditional +// rules ("Includes", "Excludes") used to generate a runtime-spec Seccomp profile +// based on the container (capabilities) and host's (arch, kernel) configuration. +type Syscall struct { + specs.LinuxSyscall + // Deprecated: kept for backward compatibility with old JSON profiles, use Names instead + Name string `json:"name,omitempty"` + Comment string `json:"comment,omitempty"` + Includes *Filter `json:"includes,omitempty"` + Excludes *Filter `json:"excludes,omitempty"` } -// inSlice tests whether a string is contained in a slice of strings or not. -// Comparison is case sensitive -func inSlice(slice []string, s string) bool { - for _, ss := range slice { - if s == ss { - return true - } - } - return false +// KernelVersion holds information about the kernel. +type KernelVersion struct { + Kernel uint64 // Version of the Kernel (i.e., the "4" in "4.1.2-generic") + Major uint64 // Major revision of the Kernel (i.e., the "1" in "4.1.2-generic") } -func setupSeccomp(config *types.Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error) { - if config == nil { - return nil, nil +// String implements fmt.Stringer for KernelVersion +func (k *KernelVersion) String() string { + if k.Kernel > 0 || k.Major > 0 { + return fmt.Sprintf("%d.%d", k.Kernel, k.Major) } + return "" +} - // No default action specified, no syscalls listed, assume seccomp disabled - if config.DefaultAction == "" && len(config.Syscalls) == 0 { - return nil, nil - } +// MarshalJSON implements json.Unmarshaler for KernelVersion +func (k *KernelVersion) MarshalJSON() ([]byte, error) { + return json.Marshal(k.String()) +} - newConfig := &specs.LinuxSeccomp{} +// UnmarshalJSON implements json.Marshaler for KernelVersion +func (k *KernelVersion) UnmarshalJSON(version []byte) error { + var ( + ver string + err error + ) - var arch string - var native, err = libseccomp.GetNativeArch() - if err == nil { - arch = native.String() + // make sure we have a string + if err = json.Unmarshal(version, &ver); err != nil { + return fmt.Errorf(`invalid kernel version: %s, expected ".": %v`, string(version), err) } - - if len(config.Architectures) != 0 && len(config.ArchMap) != 0 { - return nil, errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'") + if ver == "" { + return nil } - - // if config.Architectures == 0 then libseccomp will figure out the architecture to use - if len(config.Architectures) != 0 { - for _, a := range config.Architectures { - newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a)) - } + parts := strings.SplitN(ver, ".", 3) + if len(parts) != 2 { + return fmt.Errorf(`invalid kernel version: %s, expected "."`, string(version)) } - - if len(config.ArchMap) != 0 { - for _, a := range config.ArchMap { - seccompArch, ok := nativeToSeccomp[arch] - if ok { - if a.Arch == seccompArch { - newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a.Arch)) - for _, sa := range a.SubArches { - newConfig.Architectures = append(newConfig.Architectures, specs.Arch(sa)) - } - break - } - } - } + if k.Kernel, err = strconv.ParseUint(parts[0], 10, 8); err != nil { + return fmt.Errorf(`invalid kernel version: %s, expected ".": %v`, string(version), err) } - - newConfig.DefaultAction = specs.LinuxSeccompAction(config.DefaultAction) - -Loop: - // Loop through all syscall blocks and convert them to libcontainer format after filtering them - for _, call := range config.Syscalls { - if len(call.Excludes.Arches) > 0 { - if inSlice(call.Excludes.Arches, arch) { - continue Loop - } - } - if len(call.Excludes.Caps) > 0 { - for _, c := range call.Excludes.Caps { - if inSlice(rs.Process.Capabilities.Bounding, c) { - continue Loop - } - } - } - if len(call.Includes.Arches) > 0 { - if !inSlice(call.Includes.Arches, arch) { - continue Loop - } - } - if len(call.Includes.Caps) > 0 { - for _, c := range call.Includes.Caps { - if !inSlice(rs.Process.Capabilities.Bounding, c) { - continue Loop - } - } - } - - if call.Name != "" && len(call.Names) != 0 { - return nil, errors.New("'name' and 'names' were specified in the seccomp profile, use either 'name' or 'names'") - } - - if call.Name != "" { - newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Name, call.Action, call.Args)) - } - - for _, n := range call.Names { - newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(n, call.Action, call.Args)) - } - } - - return newConfig, nil -} - -func createSpecsSyscall(name string, action types.Action, args []*types.Arg) specs.LinuxSyscall { - newCall := specs.LinuxSyscall{ - Names: []string{name}, - Action: specs.LinuxSeccompAction(action), + if k.Major, err = strconv.ParseUint(parts[1], 10, 8); err != nil { + return fmt.Errorf(`invalid kernel version: %s, expected ".": %v`, string(version), err) } - - // Loop through all the arguments of the syscall and convert them - for _, arg := range args { - newArg := specs.LinuxSeccompArg{ - Index: arg.Index, - Value: arg.Value, - ValueTwo: arg.ValueTwo, - Op: specs.LinuxSeccompOperator(arg.Op), - } - - newCall.Args = append(newCall.Args, newArg) + if k.Kernel == 0 && k.Major == 0 { + return fmt.Errorf(`invalid kernel version: %s, expected ".": version cannot be 0.0`, string(version)) } - return newCall + return nil } diff --git a/profiles/seccomp/seccomp_default.go b/profiles/seccomp/seccomp_default.go deleted file mode 100644 index 60550124daa83..0000000000000 --- a/profiles/seccomp/seccomp_default.go +++ /dev/null @@ -1,662 +0,0 @@ -// +build linux,seccomp - -package seccomp // import "github.com/docker/docker/profiles/seccomp" - -import ( - "github.com/docker/docker/api/types" - "golang.org/x/sys/unix" -) - -func arches() []types.Architecture { - return []types.Architecture{ - { - Arch: types.ArchX86_64, - SubArches: []types.Arch{types.ArchX86, types.ArchX32}, - }, - { - Arch: types.ArchAARCH64, - SubArches: []types.Arch{types.ArchARM}, - }, - { - Arch: types.ArchMIPS64, - SubArches: []types.Arch{types.ArchMIPS, types.ArchMIPS64N32}, - }, - { - Arch: types.ArchMIPS64N32, - SubArches: []types.Arch{types.ArchMIPS, types.ArchMIPS64}, - }, - { - Arch: types.ArchMIPSEL64, - SubArches: []types.Arch{types.ArchMIPSEL, types.ArchMIPSEL64N32}, - }, - { - Arch: types.ArchMIPSEL64N32, - SubArches: []types.Arch{types.ArchMIPSEL, types.ArchMIPSEL64}, - }, - { - Arch: types.ArchS390X, - SubArches: []types.Arch{types.ArchS390}, - }, - } -} - -// DefaultProfile defines the whitelist for the default seccomp profile. -func DefaultProfile() *types.Seccomp { - syscalls := []*types.Syscall{ - { - Names: []string{ - "accept", - "accept4", - "access", - "adjtimex", - "alarm", - "bind", - "brk", - "capget", - "capset", - "chdir", - "chmod", - "chown", - "chown32", - "clock_getres", - "clock_gettime", - "clock_nanosleep", - "close", - "connect", - "copy_file_range", - "creat", - "dup", - "dup2", - "dup3", - "epoll_create", - "epoll_create1", - "epoll_ctl", - "epoll_ctl_old", - "epoll_pwait", - "epoll_wait", - "epoll_wait_old", - "eventfd", - "eventfd2", - "execve", - "execveat", - "exit", - "exit_group", - "faccessat", - "fadvise64", - "fadvise64_64", - "fallocate", - "fanotify_mark", - "fchdir", - "fchmod", - "fchmodat", - "fchown", - "fchown32", - "fchownat", - "fcntl", - "fcntl64", - "fdatasync", - "fgetxattr", - "flistxattr", - "flock", - "fork", - "fremovexattr", - "fsetxattr", - "fstat", - "fstat64", - "fstatat64", - "fstatfs", - "fstatfs64", - "fsync", - "ftruncate", - "ftruncate64", - "futex", - "futimesat", - "getcpu", - "getcwd", - "getdents", - "getdents64", - "getegid", - "getegid32", - "geteuid", - "geteuid32", - "getgid", - "getgid32", - "getgroups", - "getgroups32", - "getitimer", - "getpeername", - "getpgid", - "getpgrp", - "getpid", - "getppid", - "getpriority", - "getrandom", - "getresgid", - "getresgid32", - "getresuid", - "getresuid32", - "getrlimit", - "get_robust_list", - "getrusage", - "getsid", - "getsockname", - "getsockopt", - "get_thread_area", - "gettid", - "gettimeofday", - "getuid", - "getuid32", - "getxattr", - "inotify_add_watch", - "inotify_init", - "inotify_init1", - "inotify_rm_watch", - "io_cancel", - "ioctl", - "io_destroy", - "io_getevents", - "ioprio_get", - "ioprio_set", - "io_setup", - "io_submit", - "ipc", - "kill", - "lchown", - "lchown32", - "lgetxattr", - "link", - "linkat", - "listen", - "listxattr", - "llistxattr", - "_llseek", - "lremovexattr", - "lseek", - "lsetxattr", - "lstat", - "lstat64", - "madvise", - "memfd_create", - "mincore", - "mkdir", - "mkdirat", - "mknod", - "mknodat", - "mlock", - "mlock2", - "mlockall", - "mmap", - "mmap2", - "mprotect", - "mq_getsetattr", - "mq_notify", - "mq_open", - "mq_timedreceive", - "mq_timedsend", - "mq_unlink", - "mremap", - "msgctl", - "msgget", - "msgrcv", - "msgsnd", - "msync", - "munlock", - "munlockall", - "munmap", - "nanosleep", - "newfstatat", - "_newselect", - "open", - "openat", - "pause", - "pipe", - "pipe2", - "poll", - "ppoll", - "prctl", - "pread64", - "preadv", - "preadv2", - "prlimit64", - "pselect6", - "pwrite64", - "pwritev", - "pwritev2", - "read", - "readahead", - "readlink", - "readlinkat", - "readv", - "recv", - "recvfrom", - "recvmmsg", - "recvmsg", - "remap_file_pages", - "removexattr", - "rename", - "renameat", - "renameat2", - "restart_syscall", - "rmdir", - "rt_sigaction", - "rt_sigpending", - "rt_sigprocmask", - "rt_sigqueueinfo", - "rt_sigreturn", - "rt_sigsuspend", - "rt_sigtimedwait", - "rt_tgsigqueueinfo", - "sched_getaffinity", - "sched_getattr", - "sched_getparam", - "sched_get_priority_max", - "sched_get_priority_min", - "sched_getscheduler", - "sched_rr_get_interval", - "sched_setaffinity", - "sched_setattr", - "sched_setparam", - "sched_setscheduler", - "sched_yield", - "seccomp", - "select", - "semctl", - "semget", - "semop", - "semtimedop", - "send", - "sendfile", - "sendfile64", - "sendmmsg", - "sendmsg", - "sendto", - "setfsgid", - "setfsgid32", - "setfsuid", - "setfsuid32", - "setgid", - "setgid32", - "setgroups", - "setgroups32", - "setitimer", - "setpgid", - "setpriority", - "setregid", - "setregid32", - "setresgid", - "setresgid32", - "setresuid", - "setresuid32", - "setreuid", - "setreuid32", - "setrlimit", - "set_robust_list", - "setsid", - "setsockopt", - "set_thread_area", - "set_tid_address", - "setuid", - "setuid32", - "setxattr", - "shmat", - "shmctl", - "shmdt", - "shmget", - "shutdown", - "sigaltstack", - "signalfd", - "signalfd4", - "sigreturn", - "socket", - "socketcall", - "socketpair", - "splice", - "stat", - "stat64", - "statfs", - "statfs64", - "statx", - "symlink", - "symlinkat", - "sync", - "sync_file_range", - "syncfs", - "sysinfo", - "tee", - "tgkill", - "time", - "timer_create", - "timer_delete", - "timerfd_create", - "timerfd_gettime", - "timerfd_settime", - "timer_getoverrun", - "timer_gettime", - "timer_settime", - "times", - "tkill", - "truncate", - "truncate64", - "ugetrlimit", - "umask", - "uname", - "unlink", - "unlinkat", - "utime", - "utimensat", - "utimes", - "vfork", - "vmsplice", - "wait4", - "waitid", - "waitpid", - "write", - "writev", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - }, - { - Names: []string{"personality"}, - Action: types.ActAllow, - Args: []*types.Arg{ - { - Index: 0, - Value: 0x0, - Op: types.OpEqualTo, - }, - }, - }, - { - Names: []string{"personality"}, - Action: types.ActAllow, - Args: []*types.Arg{ - { - Index: 0, - Value: 0x0008, - Op: types.OpEqualTo, - }, - }, - }, - { - Names: []string{"personality"}, - Action: types.ActAllow, - Args: []*types.Arg{ - { - Index: 0, - Value: 0x20000, - Op: types.OpEqualTo, - }, - }, - }, - { - Names: []string{"personality"}, - Action: types.ActAllow, - Args: []*types.Arg{ - { - Index: 0, - Value: 0x20008, - Op: types.OpEqualTo, - }, - }, - }, - { - Names: []string{"personality"}, - Action: types.ActAllow, - Args: []*types.Arg{ - { - Index: 0, - Value: 0xffffffff, - Op: types.OpEqualTo, - }, - }, - }, - { - Names: []string{ - "sync_file_range2", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Arches: []string{"ppc64le"}, - }, - }, - { - Names: []string{ - "arm_fadvise64_64", - "arm_sync_file_range", - "sync_file_range2", - "breakpoint", - "cacheflush", - "set_tls", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Arches: []string{"arm", "arm64"}, - }, - }, - { - Names: []string{ - "arch_prctl", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Arches: []string{"amd64", "x32"}, - }, - }, - { - Names: []string{ - "modify_ldt", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Arches: []string{"amd64", "x32", "x86"}, - }, - }, - { - Names: []string{ - "s390_pci_mmio_read", - "s390_pci_mmio_write", - "s390_runtime_instr", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Arches: []string{"s390", "s390x"}, - }, - }, - { - Names: []string{ - "open_by_handle_at", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_DAC_READ_SEARCH"}, - }, - }, - { - Names: []string{ - "bpf", - "clone", - "fanotify_init", - "lookup_dcookie", - "mount", - "name_to_handle_at", - "perf_event_open", - "quotactl", - "setdomainname", - "sethostname", - "setns", - "syslog", - "umount", - "umount2", - "unshare", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYS_ADMIN"}, - }, - }, - { - Names: []string{ - "clone", - }, - Action: types.ActAllow, - Args: []*types.Arg{ - { - Index: 0, - Value: unix.CLONE_NEWNS | unix.CLONE_NEWUTS | unix.CLONE_NEWIPC | unix.CLONE_NEWUSER | unix.CLONE_NEWPID | unix.CLONE_NEWNET, - ValueTwo: 0, - Op: types.OpMaskedEqual, - }, - }, - Excludes: types.Filter{ - Caps: []string{"CAP_SYS_ADMIN"}, - Arches: []string{"s390", "s390x"}, - }, - }, - { - Names: []string{ - "clone", - }, - Action: types.ActAllow, - Args: []*types.Arg{ - { - Index: 1, - Value: unix.CLONE_NEWNS | unix.CLONE_NEWUTS | unix.CLONE_NEWIPC | unix.CLONE_NEWUSER | unix.CLONE_NEWPID | unix.CLONE_NEWNET, - ValueTwo: 0, - Op: types.OpMaskedEqual, - }, - }, - Comment: "s390 parameter ordering for clone is different", - Includes: types.Filter{ - Arches: []string{"s390", "s390x"}, - }, - Excludes: types.Filter{ - Caps: []string{"CAP_SYS_ADMIN"}, - }, - }, - { - Names: []string{ - "reboot", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYS_BOOT"}, - }, - }, - { - Names: []string{ - "chroot", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYS_CHROOT"}, - }, - }, - { - Names: []string{ - "delete_module", - "init_module", - "finit_module", - "query_module", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYS_MODULE"}, - }, - }, - { - Names: []string{ - "acct", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYS_PACCT"}, - }, - }, - { - Names: []string{ - "kcmp", - "process_vm_readv", - "process_vm_writev", - "ptrace", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYS_PTRACE"}, - }, - }, - { - Names: []string{ - "iopl", - "ioperm", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYS_RAWIO"}, - }, - }, - { - Names: []string{ - "settimeofday", - "stime", - "clock_settime", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYS_TIME"}, - }, - }, - { - Names: []string{ - "vhangup", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYS_TTY_CONFIG"}, - }, - }, - { - Names: []string{ - "get_mempolicy", - "mbind", - "set_mempolicy", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYS_NICE"}, - }, - }, - { - Names: []string{ - "syslog", - }, - Action: types.ActAllow, - Args: []*types.Arg{}, - Includes: types.Filter{ - Caps: []string{"CAP_SYSLOG"}, - }, - }, - } - - return &types.Seccomp{ - DefaultAction: types.ActErrno, - ArchMap: arches(), - Syscalls: syscalls, - } -} diff --git a/profiles/seccomp/seccomp_linux.go b/profiles/seccomp/seccomp_linux.go new file mode 100644 index 0000000000000..4d8fed68c6a19 --- /dev/null +++ b/profiles/seccomp/seccomp_linux.go @@ -0,0 +1,166 @@ +//go:generate go run -tags 'seccomp' generate.go + +package seccomp // import "github.com/docker/docker/profiles/seccomp" + +import ( + "encoding/json" + "errors" + "fmt" + "runtime" + + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// GetDefaultProfile returns the default seccomp profile. +func GetDefaultProfile(rs *specs.Spec) (*specs.LinuxSeccomp, error) { + return setupSeccomp(DefaultProfile(), rs) +} + +// LoadProfile takes a json string and decodes the seccomp profile. +func LoadProfile(body string, rs *specs.Spec) (*specs.LinuxSeccomp, error) { + var config Seccomp + if err := json.Unmarshal([]byte(body), &config); err != nil { + return nil, fmt.Errorf("Decoding seccomp profile failed: %v", err) + } + return setupSeccomp(&config, rs) +} + +// libseccomp string => seccomp arch +var nativeToSeccomp = map[string]specs.Arch{ + "x86": specs.ArchX86, + "amd64": specs.ArchX86_64, + "arm": specs.ArchARM, + "arm64": specs.ArchAARCH64, + "mips64": specs.ArchMIPS64, + "mips64n32": specs.ArchMIPS64N32, + "mipsel64": specs.ArchMIPSEL64, + "mips3l64n32": specs.ArchMIPSEL64N32, + "mipsle": specs.ArchMIPSEL, + "ppc": specs.ArchPPC, + "ppc64": specs.ArchPPC64, + "ppc64le": specs.ArchPPC64LE, + "s390": specs.ArchS390, + "s390x": specs.ArchS390X, +} + +// GOARCH => libseccomp string +var goToNative = map[string]string{ + "386": "x86", + "amd64": "amd64", + "arm": "arm", + "arm64": "arm64", + "mips64": "mips64", + "mips64p32": "mips64n32", + "mips64le": "mipsel64", + "mips64p32le": "mips3l64n32", + "mipsle": "mipsel", + "ppc": "ppc", + "ppc64": "ppc64", + "ppc64le": "ppc64le", + "s390": "s390", + "s390x": "s390x", +} + +// inSlice tests whether a string is contained in a slice of strings or not. +// Comparison is case sensitive +func inSlice(slice []string, s string) bool { + for _, ss := range slice { + if s == ss { + return true + } + } + return false +} + +func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error) { + if config == nil { + return nil, nil + } + + // No default action specified, no syscalls listed, assume seccomp disabled + if config.DefaultAction == "" && len(config.Syscalls) == 0 { + return nil, nil + } + + if len(config.Architectures) != 0 && len(config.ArchMap) != 0 { + return nil, errors.New("both 'architectures' and 'archMap' are specified in the seccomp profile, use either 'architectures' or 'archMap'") + } + + if len(config.LinuxSeccomp.Syscalls) != 0 { + // The Seccomp type overrides the LinuxSeccomp.Syscalls field, + // so 'this should never happen' when loaded from JSON, but could + // happen if someone constructs the Config from source. + return nil, errors.New("the LinuxSeccomp.Syscalls field should be empty") + } + + var ( + // Copy all common / standard properties to the output profile + newConfig = &config.LinuxSeccomp + arch = goToNative[runtime.GOARCH] + ) + if seccompArch, ok := nativeToSeccomp[arch]; ok { + for _, a := range config.ArchMap { + if a.Arch == seccompArch { + newConfig.Architectures = append(newConfig.Architectures, a.Arch) + newConfig.Architectures = append(newConfig.Architectures, a.SubArches...) + break + } + } + } + +Loop: + // Convert Syscall to OCI runtimes-spec specs.LinuxSyscall after filtering them. + for _, call := range config.Syscalls { + if call.Name != "" { + if len(call.Names) != 0 { + return nil, errors.New("both 'name' and 'names' are specified in the seccomp profile, use either 'name' or 'names'") + } + call.Names = []string{call.Name} + } + if call.Excludes != nil { + if len(call.Excludes.Arches) > 0 { + if inSlice(call.Excludes.Arches, arch) { + continue Loop + } + } + if len(call.Excludes.Caps) > 0 { + for _, c := range call.Excludes.Caps { + if inSlice(rs.Process.Capabilities.Bounding, c) { + continue Loop + } + } + } + if call.Excludes.MinKernel != nil { + if ok, err := kernelGreaterEqualThan(*call.Excludes.MinKernel); err != nil { + return nil, err + } else if ok { + continue Loop + } + } + } + if call.Includes != nil { + if len(call.Includes.Arches) > 0 { + if !inSlice(call.Includes.Arches, arch) { + continue Loop + } + } + if len(call.Includes.Caps) > 0 { + for _, c := range call.Includes.Caps { + if !inSlice(rs.Process.Capabilities.Bounding, c) { + continue Loop + } + } + } + if call.Includes.MinKernel != nil { + if ok, err := kernelGreaterEqualThan(*call.Includes.MinKernel); err != nil { + return nil, err + } else if !ok { + continue Loop + } + } + } + newConfig.Syscalls = append(newConfig.Syscalls, call.LinuxSyscall) + } + + return newConfig, nil +} diff --git a/profiles/seccomp/seccomp_test.go b/profiles/seccomp/seccomp_test.go index b0b63ea8110ee..3a401db4ecc2e 100644 --- a/profiles/seccomp/seccomp_test.go +++ b/profiles/seccomp/seccomp_test.go @@ -1,32 +1,297 @@ +//go:build linux // +build linux package seccomp // import "github.com/docker/docker/profiles/seccomp" import ( - "io/ioutil" + "encoding/json" + "os" + "strings" "testing" - "github.com/docker/docker/oci" + "github.com/opencontainers/runtime-spec/specs-go" + "gotest.tools/v3/assert" ) func TestLoadProfile(t *testing.T) { - f, err := ioutil.ReadFile("fixtures/example.json") + f, err := os.ReadFile("fixtures/example.json") if err != nil { t.Fatal(err) } - rs := oci.DefaultSpec() - if _, err := LoadProfile(string(f), &rs); err != nil { + rs := createSpec() + p, err := LoadProfile(string(f), &rs) + if err != nil { + t.Fatal(err) + } + var expectedErrno uint = 12345 + var expectedDefaultErrno uint = 1 + expected := specs.LinuxSeccomp{ + DefaultAction: specs.ActErrno, + DefaultErrnoRet: &expectedDefaultErrno, + Syscalls: []specs.LinuxSyscall{ + { + Names: []string{"clone"}, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{{ + Index: 0, + Value: 2114060288, + ValueTwo: 0, + Op: specs.OpMaskedEqual, + }}, + }, + { + + Names: []string{"open"}, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{}, + }, + { + Names: []string{"close"}, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{}, + }, + { + Names: []string{"syslog"}, + Action: specs.ActErrno, + ErrnoRet: &expectedErrno, + Args: []specs.LinuxSeccompArg{}, + }, + }, + } + + assert.DeepEqual(t, expected, *p) +} + +func TestLoadProfileWithDefaultErrnoRet(t *testing.T) { + var profile = []byte(`{ +"defaultAction": "SCMP_ACT_ERRNO", +"defaultErrnoRet": 6 +}`) + rs := createSpec() + p, err := LoadProfile(string(profile), &rs) + if err != nil { + t.Fatal(err) + } + + expectedErrnoRet := uint(6) + expected := specs.LinuxSeccomp{ + DefaultAction: specs.ActErrno, + DefaultErrnoRet: &expectedErrnoRet, + } + + assert.DeepEqual(t, expected, *p) +} + +func TestLoadProfileWithListenerPath(t *testing.T) { + var profile = []byte(`{ +"defaultAction": "SCMP_ACT_ERRNO", +"listenerPath": "/var/run/seccompaget.sock", +"listenerMetadata": "opaque-metadata" +}`) + rs := createSpec() + p, err := LoadProfile(string(profile), &rs) + if err != nil { + t.Fatal(err) + } + + expected := specs.LinuxSeccomp{ + DefaultAction: specs.ActErrno, + ListenerPath: "/var/run/seccompaget.sock", + ListenerMetadata: "opaque-metadata", + } + + assert.DeepEqual(t, expected, *p) +} + +func TestLoadProfileWithFlag(t *testing.T) { + profile := `{"defaultAction": "SCMP_ACT_ERRNO", "flags": ["SECCOMP_FILTER_FLAG_SPEC_ALLOW", "SECCOMP_FILTER_FLAG_LOG"]}` + expected := specs.LinuxSeccomp{ + DefaultAction: specs.ActErrno, + Flags: []specs.LinuxSeccompFlag{"SECCOMP_FILTER_FLAG_SPEC_ALLOW", "SECCOMP_FILTER_FLAG_LOG"}, + } + rs := createSpec() + p, err := LoadProfile(profile, &rs) + assert.NilError(t, err) + assert.DeepEqual(t, expected, *p) +} + +// TestLoadProfileValidation tests that invalid profiles produce the correct error. +func TestLoadProfileValidation(t *testing.T) { + tests := []struct { + doc string + profile string + expected string + }{ + { + doc: "conflicting architectures and archMap", + profile: `{"defaultAction": "SCMP_ACT_ERRNO", "architectures": ["A", "B", "C"], "archMap": [{"architecture": "A", "subArchitectures": ["B", "C"]}]}`, + expected: `use either 'architectures' or 'archMap'`, + }, + { + doc: "conflicting syscall.name and syscall.names", + profile: `{"defaultAction": "SCMP_ACT_ERRNO", "syscalls": [{"name": "accept", "names": ["accept"], "action": "SCMP_ACT_ALLOW"}]}`, + expected: `use either 'name' or 'names'`, + }, + } + for _, tc := range tests { + tc := tc + rs := createSpec() + t.Run(tc.doc, func(t *testing.T) { + _, err := LoadProfile(tc.profile, &rs) + assert.ErrorContains(t, err, tc.expected) + }) + } +} + +// TestLoadLegacyProfile tests loading a seccomp profile in the old format +// (before https://github.com/docker/docker/pull/24510) +func TestLoadLegacyProfile(t *testing.T) { + f, err := os.ReadFile("fixtures/default-old-format.json") + if err != nil { t.Fatal(err) } + rs := createSpec() + p, err := LoadProfile(string(f), &rs) + assert.NilError(t, err) + assert.Equal(t, p.DefaultAction, specs.ActErrno) + assert.DeepEqual(t, p.Architectures, []specs.Arch{"SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"}) + assert.Equal(t, len(p.Syscalls), 311) + expected := specs.LinuxSyscall{ + Names: []string{"accept"}, + Action: specs.ActAllow, + Args: []specs.LinuxSeccompArg{}, + } + assert.DeepEqual(t, p.Syscalls[0], expected) } func TestLoadDefaultProfile(t *testing.T) { - f, err := ioutil.ReadFile("default.json") + f, err := os.ReadFile("default.json") if err != nil { t.Fatal(err) } - rs := oci.DefaultSpec() + rs := createSpec() if _, err := LoadProfile(string(f), &rs); err != nil { t.Fatal(err) } } + +func TestUnmarshalDefaultProfile(t *testing.T) { + expected := DefaultProfile() + if expected == nil { + t.Skip("seccomp not supported") + } + + f, err := os.ReadFile("default.json") + if err != nil { + t.Fatal(err) + } + var profile Seccomp + err = json.Unmarshal(f, &profile) + if err != nil { + t.Fatal(err) + } + assert.DeepEqual(t, expected.Architectures, profile.Architectures) + assert.DeepEqual(t, expected.ArchMap, profile.ArchMap) + assert.DeepEqual(t, expected.DefaultAction, profile.DefaultAction) + assert.DeepEqual(t, expected.Syscalls, profile.Syscalls) +} + +func TestMarshalUnmarshalFilter(t *testing.T) { + t.Parallel() + tests := []struct { + in string + out string + error bool + }{ + {in: `{"arches":["s390x"],"minKernel":3}`, error: true}, + {in: `{"arches":["s390x"],"minKernel":3.12}`, error: true}, + {in: `{"arches":["s390x"],"minKernel":true}`, error: true}, + {in: `{"arches":["s390x"],"minKernel":"0.0"}`, error: true}, + {in: `{"arches":["s390x"],"minKernel":"3"}`, error: true}, + {in: `{"arches":["s390x"],"minKernel":".3"}`, error: true}, + {in: `{"arches":["s390x"],"minKernel":"3."}`, error: true}, + {in: `{"arches":["s390x"],"minKernel":"true"}`, error: true}, + {in: `{"arches":["s390x"],"minKernel":"3.12.1\""}`, error: true}, + {in: `{"arches":["s390x"],"minKernel":"4.15abc"}`, error: true}, + {in: `{"arches":["s390x"],"minKernel":null}`, out: `{"arches":["s390x"]}`}, + {in: `{"arches":["s390x"],"minKernel":""}`, out: `{"arches":["s390x"],"minKernel":""}`}, // FIXME: try to fix omitempty for this + {in: `{"arches":["s390x"],"minKernel":"0.5"}`, out: `{"arches":["s390x"],"minKernel":"0.5"}`}, + {in: `{"arches":["s390x"],"minKernel":"0.50"}`, out: `{"arches":["s390x"],"minKernel":"0.50"}`}, + {in: `{"arches":["s390x"],"minKernel":"5.0"}`, out: `{"arches":["s390x"],"minKernel":"5.0"}`}, + {in: `{"arches":["s390x"],"minKernel":"50.0"}`, out: `{"arches":["s390x"],"minKernel":"50.0"}`}, + {in: `{"arches":["s390x"],"minKernel":"4.15"}`, out: `{"arches":["s390x"],"minKernel":"4.15"}`}, + } + for _, tc := range tests { + tc := tc + t.Run(tc.in, func(t *testing.T) { + var filter Filter + err := json.Unmarshal([]byte(tc.in), &filter) + if tc.error { + if err == nil { + t.Fatal("expected an error") + } else if !strings.Contains(err.Error(), "invalid kernel version") { + t.Fatal("unexpected error:", err) + } + return + } + if err != nil { + t.Fatal(err) + } + out, err := json.Marshal(filter) + if err != nil { + t.Fatal(err) + } + if string(out) != tc.out { + t.Fatalf("expected %s, got %s", tc.out, string(out)) + } + }) + } +} + +func TestLoadConditional(t *testing.T) { + f, err := os.ReadFile("fixtures/conditional_include.json") + if err != nil { + t.Fatal(err) + } + tests := []struct { + doc string + cap string + expected []string + }{ + {doc: "no caps", expected: []string{"chmod", "ptrace"}}, + {doc: "with syslog", cap: "CAP_SYSLOG", expected: []string{"chmod", "syslog", "ptrace"}}, + {doc: "no ptrace", cap: "CAP_SYS_ADMIN", expected: []string{"chmod"}}, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.doc, func(t *testing.T) { + rs := createSpec(tc.cap) + p, err := LoadProfile(string(f), &rs) + if err != nil { + t.Fatal(err) + } + if len(p.Syscalls) != len(tc.expected) { + t.Fatalf("expected %d syscalls in profile, have %d", len(tc.expected), len(p.Syscalls)) + } + for i, v := range p.Syscalls { + if v.Names[0] != tc.expected[i] { + t.Fatalf("expected %s syscall, have %s", tc.expected[i], v.Names[0]) + } + } + }) + } +} + +// createSpec() creates a minimum spec for testing +func createSpec(caps ...string) specs.Spec { + rs := specs.Spec{ + Process: &specs.Process{ + Capabilities: &specs.LinuxCapabilities{}, + }, + } + if caps != nil { + rs.Process.Capabilities.Bounding = append(rs.Process.Capabilities.Bounding, caps...) + } + return rs +} diff --git a/profiles/seccomp/seccomp_unsupported.go b/profiles/seccomp/seccomp_unsupported.go index 67e06401f1173..d337695e10b5a 100644 --- a/profiles/seccomp/seccomp_unsupported.go +++ b/profiles/seccomp/seccomp_unsupported.go @@ -1,12 +1,9 @@ +//go:build linux && !seccomp // +build linux,!seccomp package seccomp // import "github.com/docker/docker/profiles/seccomp" -import ( - "github.com/docker/docker/api/types" -) - // DefaultProfile returns a nil pointer on unsupported systems. -func DefaultProfile() *types.Seccomp { +func DefaultProfile() *Seccomp { return nil } diff --git a/project/ARM.md b/project/ARM.md deleted file mode 100644 index c876231d1eba3..0000000000000 --- a/project/ARM.md +++ /dev/null @@ -1,45 +0,0 @@ -# ARM support - -The ARM support should be considered experimental. It will be extended step by step in the coming weeks. - -Building a Docker Development Image works in the same fashion as for Intel platform (x86-64). -Currently we have initial support for 32bit ARMv7 devices. - -To work with the Docker Development Image you have to clone the Docker/Docker repo on a supported device. -It needs to have a Docker Engine installed to build the Docker Development Image. - -From the root of the Docker/Docker repo one can use make to execute the following make targets: -- make validate -- make binary -- make build -- make deb -- make bundles -- make default -- make shell -- make test-unit -- make test-integration -- make - -The Makefile does include logic to determine on which OS and architecture the Docker Development Image is built. -Based on OS and architecture it chooses the correct Dockerfile. -For the ARM 32bit architecture it uses `Dockerfile.armhf`. - -So for example in order to build a Docker binary one has to: -1. clone the Docker/Docker repository on an ARM device `git clone https://github.com/docker/docker.git` -2. change into the checked out repository with `cd docker` -3. execute `make binary` to create a Docker Engine binary for ARM - -## Kernel modules -A few libnetwork integration tests require that the kernel be -configured with "dummy" network interface and has the module -loaded. However, the dummy module may be not loaded automatically. - -To load the kernel module permanently, run these commands as `root`. - - modprobe dummy - echo "dummy" >> /etc/modules - -On some systems you also have to sync your kernel modules. - - oc-sync-kernel-modules - depmod diff --git a/project/CONTRIBUTING.md b/project/CONTRIBUTING.md deleted file mode 120000 index 44fcc63439371..0000000000000 --- a/project/CONTRIBUTING.md +++ /dev/null @@ -1 +0,0 @@ -../CONTRIBUTING.md \ No newline at end of file diff --git a/project/GOVERNANCE.md b/project/GOVERNANCE.md index 4b52989a642e5..588429b313b74 100644 --- a/project/GOVERNANCE.md +++ b/project/GOVERNANCE.md @@ -113,7 +113,7 @@ the same steps: * **Step 3**: Maintainers merge, close or reject the pull request. Pull requests are reviewed by the current maintainers of the moby/moby -repository. Weekly meetings are organized to are organized to synchronously +repository. Weekly meetings are organized to synchronously discuss tricky PRs, as well as design and architecture decisions.. When technical agreement cannot be reached among the maintainers of the project, escalation or concerns can be raised by opening an issue to be handled diff --git a/project/IRC-ADMINISTRATION.md b/project/IRC-ADMINISTRATION.md deleted file mode 100644 index 824a14bd51fd7..0000000000000 --- a/project/IRC-ADMINISTRATION.md +++ /dev/null @@ -1,37 +0,0 @@ -# Freenode IRC Administration Guidelines and Tips - -This is not meant to be a general "Here's how to IRC" document, so if you're -looking for that, check Google instead. ♥ - -If you've been charged with helping maintain one of Docker's now many IRC -channels, this might turn out to be useful. If there's information that you -wish you'd known about how a particular channel is organized, you should add -deets here! :) - -## `ChanServ` - -Most channel maintenance happens by talking to Freenode's `ChanServ` bot. For -example, `/msg ChanServ ACCESS LIST` will show you a list of everyone -with "access" privileges for a particular channel. - -A similar command is used to give someone a particular access level. For -example, to add a new maintainer to the `#docker-maintainers` access list so -that they can contribute to the discussions (after they've been merged -appropriately in a `MAINTAINERS` file, of course), one would use `/msg ChanServ -ACCESS #docker-maintainers ADD maintainer`. - -To setup a new channel with a similar `maintainer` access template, use a -command like `/msg ChanServ TEMPLATE maintainer +AV` (`+A` for letting -them view the `ACCESS LIST`, `+V` for auto-voice; see `/msg ChanServ HELP FLAGS` -for more details). - -## Troubleshooting - -The most common cause of not-getting-auto-`+v` woes is people not being -`IDENTIFY`ed with `NickServ` (or their current nickname not being `GROUP`ed with -their main nickname) -- often manifested by `ChanServ` responding to an `ACCESS -ADD` request with something like `xyz is not registered.`. - -This is easily fixed by doing `/msg NickServ IDENTIFY OldNick SecretPassword` -followed by `/msg NickServ GROUP` to group the two nicknames together. See -`/msg NickServ HELP GROUP` for more information. diff --git a/project/PACKAGE-REPO-MAINTENANCE.md b/project/PACKAGE-REPO-MAINTENANCE.md deleted file mode 100644 index 458384a3d9244..0000000000000 --- a/project/PACKAGE-REPO-MAINTENANCE.md +++ /dev/null @@ -1,74 +0,0 @@ -# Apt & Yum Repository Maintenance -## A maintainer's guide to managing Docker's package repos - -### How to clean up old experimental debs and rpms - -We release debs and rpms for experimental nightly, so these can build up. -To remove old experimental debs and rpms, and _ONLY_ keep the latest, follow the -steps below. - -1. Checkout docker master - -2. Run clean scripts - -```bash -docker build --rm --force-rm -t docker-dev:master . -docker run --rm -it --privileged \ - -v /path/to/your/repos/dir:/volumes/repos \ - -v $HOME/.gnupg:/root/.gnupg \ - -e GPG_PASSPHRASE \ - -e DOCKER_RELEASE_DIR=/volumes/repos \ - docker-dev:master hack/make.sh clean-apt-repo clean-yum-repo generate-index-listing sign-repos -``` - -3. Upload the changed repos to `s3` (if you host on s3) - -4. Purge the cache, PURGE the cache, PURGE THE CACHE! - -### How to get out of a sticky situation - -Sh\*t happens. We know. Below are steps to get out of any "hash-sum mismatch" or -"gpg sig error" or the likes error that might happen to the apt repo. - -**NOTE:** These are apt repo specific, have had no experience with anything similar -happening to the yum repo in the past so you can rest easy. - -For each step listed below, move on to the next if the previous didn't work. -Otherwise CELEBRATE! - -1. Purge the cache. - -2. Did you remember to sign the debs after releasing? - -Re-sign the repo with your gpg key: - -```bash -docker build --rm --force-rm -t docker-dev:master . -docker run --rm -it --privileged \ - -v /path/to/your/repos/dir:/volumes/repos \ - -v $HOME/.gnupg:/root/.gnupg \ - -e GPG_PASSPHRASE \ - -e DOCKER_RELEASE_DIR=/volumes/repos \ - docker-dev:master hack/make.sh sign-repos -``` - -Upload the changed repo to `s3` (if that is where you host) - -PURGE THE CACHE. - -3. Run Jess' magical, save all, only in case of extreme emergencies, "you are -going to have to break this glass to get it" script. - -```bash -docker build --rm --force-rm -t docker-dev:master . -docker run --rm -it --privileged \ - -v /path/to/your/repos/dir:/volumes/repos \ - -v $HOME/.gnupg:/root/.gnupg \ - -e GPG_PASSPHRASE \ - -e DOCKER_RELEASE_DIR=/volumes/repos \ - docker-dev:master hack/make.sh update-apt-repo generate-index-listing sign-repos -``` - -4. Upload the changed repo to `s3` (if that is where you host) - -PURGE THE CACHE. diff --git a/project/PACKAGERS.md b/project/PACKAGERS.md index a5b0018b5a0cf..23c963c725262 100644 --- a/project/PACKAGERS.md +++ b/project/PACKAGERS.md @@ -4,24 +4,6 @@ If you are looking to make Docker available on your favorite software distribution, this document is for you. It summarizes the requirements for building and running the Docker client and the Docker daemon. -## Getting Started - -We want to help you package Docker successfully. Before doing any packaging, a -good first step is to introduce yourself on the [docker-dev mailing -list](https://groups.google.com/d/forum/docker-dev), explain what you're trying -to achieve, and tell us how we can help. Don't worry, we don't bite! There might -even be someone already working on packaging for the same distro! - -You can also join the IRC channel - #docker and #docker-dev on Freenode are both -active and friendly. - -We like to refer to Tianon ("@tianon" on GitHub and "tianon" on IRC) as our -"Packagers Relations", since he's always working to make sure our packagers have -a good, healthy upstream to work with (both in our communication and in our -build scripts). If you're having any kind of trouble, feel free to ping him -directly. He also likes to keep track of what distributions we have packagers -for, so feel free to reach out to him even just to say "Hi!" - ## Package Name If possible, your package should be called "docker". If that name is already @@ -41,27 +23,7 @@ need to package Docker your way, without denaturing it in the process. ## Build Dependencies -To build Docker, you will need the following: - -* A recent version of Git and Mercurial -* Go version 1.6 or later -* A clean checkout of the source added to a valid [Go - workspace](https://golang.org/doc/code.html#Workspaces) under the path - *src/github.com/docker/docker* (unless you plan to use `AUTO_GOPATH`, - explained in more detail below) - -To build the Docker daemon, you will additionally need: - -* An amd64/x86_64 machine running Linux -* SQLite version 3.7.9 or later -* libdevmapper version 1.02.68-cvs (2012-01-26) or later from lvm2 version - 2.02.89 or later -* btrfs-progs version 3.16.1 or later (unless using an older version is - absolutely necessary, in which case 3.8 is the minimum) -* libseccomp version 2.2.1 or later (for build tag seccomp) - -Be sure to also check out Docker's Dockerfile for the most up-to-date list of -these build-time dependencies. +The Dockerfile contains the most up-to-date list of build-time dependencies. ### Go Dependencies @@ -69,18 +31,10 @@ All Go dependencies are vendored under "./vendor". They are used by the official build, so the source of truth for the current version of each dependency is whatever is in "./vendor". -To use the vendored dependencies, simply make sure the path to "./vendor" is -included in `GOPATH` (or use `AUTO_GOPATH`, as explained below). - If you would rather (or must, due to distro policy) package these dependencies yourself, take a look at "vendor.conf" for an easy-to-parse list of the exact version for each. -NOTE: if you're not able to package the exact version (to the exact commit) of a -given dependency, please get in touch so we can remediate! Who knows what -discrepancies can be caused by even the slightest deviation. We promise to do -our best to make everybody happy. - ## Stripping Binaries Please, please, please do not strip any compiled binaries. This is really @@ -123,46 +77,11 @@ from the upstream Golang perspective. ## Building Docker -Please use our build script ("./hack/make.sh") for all your compilation of -Docker. If there's something you need that it isn't doing, or something it could -be doing to make your life as a packager easier, please get in touch with Tianon -and help us rectify the situation. Chances are good that other packagers have -probably run into the same problems and a fix might already be in the works, but -none of us will know for sure unless you harass Tianon about it. :) - -All the commands listed within this section should be run with the Docker source -checkout as the current working directory. - -### `AUTO_GOPATH` - -If you'd rather not be bothered with the hassles that setting up `GOPATH` -appropriately can be, and prefer to just get a "build that works", you should -add something similar to this to whatever script or process you're using to -build Docker: - -```bash -export AUTO_GOPATH=1 -``` - -This will cause the build scripts to set up a reasonable `GOPATH` that -automatically and properly includes both docker/docker from the local -directory, and the local "./vendor" directory as necessary. +Please use our build script ("./hack/make.sh") for compilation. ### `DOCKER_BUILDTAGS` -If you're building a binary that may need to be used on platforms that include -AppArmor, you will need to set `DOCKER_BUILDTAGS` as follows: -```bash -export DOCKER_BUILDTAGS='apparmor' -``` - -If you're building a binary that may need to be used on platforms that include -SELinux, you will need to use the `selinux` build tag: -```bash -export DOCKER_BUILDTAGS='selinux' -``` - -If you're building a binary that may need to be used on platforms that include +If you're building a binary that might be used on platforms that include seccomp, you will need to use the `seccomp` build tag: ```bash export DOCKER_BUILDTAGS='seccomp' @@ -188,51 +107,9 @@ export DOCKER_BUILDTAGS='exclude_graphdriver_aufs' NOTE: if you need to set more than one build tag, space separate them: ```bash -export DOCKER_BUILDTAGS='apparmor selinux exclude_graphdriver_aufs' +export DOCKER_BUILDTAGS='apparmor exclude_graphdriver_aufs' ``` -### Static Daemon - -If it is feasible within the constraints of your distribution, you should -seriously consider packaging Docker as a single static binary. A good comparison -is Busybox, which is often packaged statically as a feature to enable mass -portability. Because of the unique way Docker operates, being similarly static -is a "feature". - -To build a static Docker daemon binary, run the following command (first -ensuring that all the necessary libraries are available in static form for -linking - see the "Build Dependencies" section above, and the relevant lines -within Docker's own Dockerfile that set up our official build environment): - -```bash -./hack/make.sh binary -``` - -This will create a static binary under -"./bundles/$VERSION/binary/docker-$VERSION", where "$VERSION" is the contents of -the file "./VERSION". This binary is usually installed somewhere like -"/usr/bin/docker". - -### Dynamic Daemon / Client-only Binary - -If you are only interested in a Docker client binary, you can build using: - -```bash -./hack/make.sh binary-client -``` - -If you need to (due to distro policy, distro library availability, or for other -reasons) create a dynamically compiled daemon binary, or if you are only -interested in creating a client binary for Docker, use something similar to the -following: - -```bash -./hack/make.sh dynbinary-client -``` - -This will create "./bundles/$VERSION/dynbinary-client/docker-$VERSION", which for -client-only builds is the important file to grab and install as appropriate. - ## System Dependencies ### Runtime Dependencies @@ -245,12 +122,7 @@ installed and available at runtime: * e2fsprogs version 1.4.12 or later (in use: mkfs.ext4, tune2fs) * xfsprogs (in use: mkfs.xfs) * XZ Utils version 4.9 or later -* a [properly - mounted](https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount) - cgroupfs hierarchy (having a single, all-encompassing "cgroup" mount point - [is](https://github.com/docker/docker/issues/2683) - [not](https://github.com/docker/docker/issues/3485) - [sufficient](https://github.com/docker/docker/issues/4568)) +* pigz (optional) Additionally, the Docker client needs the following software to be installed and available at runtime: @@ -261,11 +133,7 @@ available at runtime: The Docker daemon has very specific kernel requirements. Most pre-packaged kernels already include the necessary options enabled. If you are building your -own kernel, you will either need to discover the options necessary via trial and -error, or check out the [Gentoo -ebuild](https://github.com/tianon/docker-overlay/blob/master/app-emulation/docker/docker-9999.ebuild), -in which a list is maintained (and if there are any issues or discrepancies in -that list, please contact Tianon so they can be rectified). +own kernel, you should check out `contrib/check-config.sh`. Note that in client mode, there are no specific kernel requirements, and that the client will even run on alternative platforms such as Mac OS X / Darwin. @@ -286,8 +154,7 @@ by having support for them in the kernel or userspace. A few examples include: Docker expects to run as a daemon at machine startup. Your package will need to include a script for your distro's process supervisor of choice. Be sure to check out the "contrib/init" folder in case a suitable init script already -exists (and if one does not, contact Tianon about whether it might be -appropriate for your distro's init script to live there too!). +exists. In general, Docker should be run as root, similar to the following: @@ -295,13 +162,5 @@ In general, Docker should be run as root, similar to the following: dockerd ``` -Generally, a `DOCKER_OPTS` variable of some kind is available for adding more -flags (such as changing the graph driver to use BTRFS, switching the location of -"/var/lib/docker", etc). - -## Communicate - -As a final note, please do feel free to reach out to Tianon at any time for -pretty much anything. He really does love hearing from our packagers and wants -to make sure we're not being a "hostile upstream". As should be a given, we -appreciate the work our packagers do to make sure we have broad distribution! +Generally, it is encouraged that additional configuration be placed in +`/etc/docker/daemon.json`. diff --git a/project/REVIEWING.md b/project/REVIEWING.md index cac3f5d7d4e46..d29f217086ed8 100644 --- a/project/REVIEWING.md +++ b/project/REVIEWING.md @@ -21,7 +21,7 @@ exist on the repository should apply to issues. * `status/1-design-review` * `status/2-code-review` * `status/3-docs-review` - * `status/4-ready-to-merge` + * `status/4-merge` Special status labels: diff --git a/project/TOOLS.md b/project/TOOLS.md deleted file mode 100644 index dda0fc034233f..0000000000000 --- a/project/TOOLS.md +++ /dev/null @@ -1,63 +0,0 @@ -# Tools - -This page describes the tools we use and infrastructure that is in place for -the Docker project. - -### CI - -The Docker project uses [Jenkins](https://jenkins.dockerproject.org/) as our -continuous integration server. Each Pull Request to Docker is tested by running the -equivalent of `make all`. We chose Jenkins because we can host it ourselves and -we run Docker in Docker to test. - -#### Leeroy - -Leeroy is a Go application which integrates Jenkins with -GitHub pull requests. Leeroy uses -[GitHub hooks](https://developer.github.com/v3/repos/hooks/) -to listen for pull request notifications and starts jobs on your Jenkins -server. Using the Jenkins -[notification plugin](https://wiki.jenkins-ci.org/display/JENKINS/Notification+Plugin), -Leeroy updates the pull request using GitHub's -[status API](https://developer.github.com/v3/repos/statuses/) -with pending, success, failure, or error statuses. - -The leeroy repository is maintained at -[github.com/docker/leeroy](https://github.com/docker/leeroy). - -#### GordonTheTurtle IRC Bot - -The GordonTheTurtle IRC Bot lives in the -[#docker-maintainers](https://botbot.me/freenode/docker-maintainers/) channel -on Freenode. He is built in Go and is based off the project at -[github.com/fabioxgn/go-bot](https://github.com/fabioxgn/go-bot). - -His main command is `!rebuild`, which rebuilds a given Pull Request for a repository. -This command works by integrating with Leroy. He has a few other commands too, such -as `!gif` or `!godoc`, but we are always looking for more fun commands to add. - -The gordon-bot repository is maintained at -[github.com/docker/gordon-bot](https://github.com/docker/gordon-bot) - -### NSQ - -We use [NSQ](https://github.com/bitly/nsq) for various aspects of the project -infrastructure. - -#### Hooks - -The hooks project, -[github.com/crosbymichael/hooks](https://github.com/crosbymichael/hooks), -is a small Go application that manages web hooks from github, hub.docker.com, or -other third party services. - -It can be used for listening to github webhooks & pushing them to a queue, -archiving hooks to rethinkdb for processing, and broadcasting hooks to various -jobs. - -#### Docker Master Binaries - -One of the things queued from the Hooks are the building of the Master -Binaries. This happens on every push to the master branch of Docker. The -repository for this is maintained at -[github.com/docker/docker-bb](https://github.com/docker/docker-bb). diff --git a/quota/errors.go b/quota/errors.go new file mode 100644 index 0000000000000..37f3438bd02a0 --- /dev/null +++ b/quota/errors.go @@ -0,0 +1,19 @@ +package quota // import "github.com/docker/docker/quota" + +import "github.com/docker/docker/errdefs" + +var ( + _ errdefs.ErrNotImplemented = (*errQuotaNotSupported)(nil) +) + +// ErrQuotaNotSupported indicates if were found the FS didn't have projects quotas available +var ErrQuotaNotSupported = errQuotaNotSupported{} + +type errQuotaNotSupported struct { +} + +func (e errQuotaNotSupported) NotImplemented() {} + +func (e errQuotaNotSupported) Error() string { + return "Filesystem does not support, or has not enabled quotas" +} diff --git a/quota/projectquota.go b/quota/projectquota.go new file mode 100644 index 0000000000000..13d2efefe57d4 --- /dev/null +++ b/quota/projectquota.go @@ -0,0 +1,443 @@ +//go:build linux && !exclude_disk_quota && cgo +// +build linux,!exclude_disk_quota,cgo + +// +// projectquota.go - implements XFS project quota controls +// for setting quota limits on a newly created directory. +// It currently supports the legacy XFS specific ioctls. +// +// TODO: use generic quota control ioctl FS_IOC_FS{GET,SET}XATTR +// for both xfs/ext4 for kernel version >= v4.5 +// + +package quota // import "github.com/docker/docker/quota" + +/* +#include +#include +#include +#include +#include + +#ifndef FS_XFLAG_PROJINHERIT +struct fsxattr { + __u32 fsx_xflags; + __u32 fsx_extsize; + __u32 fsx_nextents; + __u32 fsx_projid; + unsigned char fsx_pad[12]; +}; +#define FS_XFLAG_PROJINHERIT 0x00000200 +#endif +#ifndef FS_IOC_FSGETXATTR +#define FS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr) +#endif +#ifndef FS_IOC_FSSETXATTR +#define FS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr) +#endif + +#ifndef PRJQUOTA +#define PRJQUOTA 2 +#endif +#ifndef XFS_PROJ_QUOTA +#define XFS_PROJ_QUOTA 2 +#endif +#ifndef Q_XSETPQLIM +#define Q_XSETPQLIM QCMD(Q_XSETQLIM, PRJQUOTA) +#endif +#ifndef Q_XGETPQUOTA +#define Q_XGETPQUOTA QCMD(Q_XGETQUOTA, PRJQUOTA) +#endif + +const int Q_XGETQSTAT_PRJQUOTA = QCMD(Q_XGETQSTAT, PRJQUOTA); +*/ +import "C" +import ( + "os" + "path" + "path/filepath" + "sync" + "unsafe" + + "github.com/containerd/containerd/pkg/userns" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +) + +type pquotaState struct { + sync.Mutex + nextProjectID uint32 +} + +var pquotaStateInst *pquotaState +var pquotaStateOnce sync.Once + +// getPquotaState - get global pquota state tracker instance +func getPquotaState() *pquotaState { + pquotaStateOnce.Do(func() { + pquotaStateInst = &pquotaState{ + nextProjectID: 1, + } + }) + return pquotaStateInst +} + +// registerBasePath - register a new base path and update nextProjectID +func (state *pquotaState) updateMinProjID(minProjectID uint32) { + state.Lock() + defer state.Unlock() + if state.nextProjectID <= minProjectID { + state.nextProjectID = minProjectID + 1 + } +} + +// NewControl - initialize project quota support. +// Test to make sure that quota can be set on a test dir and find +// the first project id to be used for the next container create. +// +// Returns nil (and error) if project quota is not supported. +// +// First get the project id of the home directory. +// This test will fail if the backing fs is not xfs. +// +// xfs_quota tool can be used to assign a project id to the driver home directory, e.g.: +// echo 999:/var/lib/docker/overlay2 >> /etc/projects +// echo docker:999 >> /etc/projid +// xfs_quota -x -c 'project -s docker' / +// +// In that case, the home directory project id will be used as a "start offset" +// and all containers will be assigned larger project ids (e.g. >= 1000). +// This is a way to prevent xfs_quota management from conflicting with docker. +// +// Then try to create a test directory with the next project id and set a quota +// on it. If that works, continue to scan existing containers to map allocated +// project ids. +// +func NewControl(basePath string) (*Control, error) { + // + // If we are running in a user namespace quota won't be supported for + // now since makeBackingFsDev() will try to mknod(). + // + if userns.RunningInUserNS() { + return nil, ErrQuotaNotSupported + } + + // + // create backing filesystem device node + // + backingFsBlockDev, err := makeBackingFsDev(basePath) + if err != nil { + return nil, err + } + + // check if we can call quotactl with project quotas + // as a mechanism to determine (early) if we have support + hasQuotaSupport, err := hasQuotaSupport(backingFsBlockDev) + if err != nil { + return nil, err + } + if !hasQuotaSupport { + return nil, ErrQuotaNotSupported + } + + // + // Get project id of parent dir as minimal id to be used by driver + // + baseProjectID, err := getProjectID(basePath) + if err != nil { + return nil, err + } + minProjectID := baseProjectID + 1 + + // + // Test if filesystem supports project quotas by trying to set + // a quota on the first available project id + // + quota := Quota{ + Size: 0, + } + if err := setProjectQuota(backingFsBlockDev, minProjectID, quota); err != nil { + return nil, err + } + + q := Control{ + backingFsBlockDev: backingFsBlockDev, + quotas: make(map[string]uint32), + } + + // + // update minimum project ID + // + state := getPquotaState() + state.updateMinProjID(minProjectID) + + // + // get first project id to be used for next container + // + err = q.findNextProjectID(basePath, baseProjectID) + if err != nil { + return nil, err + } + + logrus.Debugf("NewControl(%s): nextProjectID = %d", basePath, state.nextProjectID) + return &q, nil +} + +// SetQuota - assign a unique project id to directory and set the quota limits +// for that project id +func (q *Control) SetQuota(targetPath string, quota Quota) error { + q.RLock() + projectID, ok := q.quotas[targetPath] + q.RUnlock() + if !ok { + state := getPquotaState() + state.Lock() + projectID = state.nextProjectID + + // + // assign project id to new container directory + // + err := setProjectID(targetPath, projectID) + if err != nil { + state.Unlock() + return err + } + + state.nextProjectID++ + state.Unlock() + + q.Lock() + q.quotas[targetPath] = projectID + q.Unlock() + } + + // + // set the quota limit for the container's project id + // + logrus.Debugf("SetQuota(%s, %d): projectID=%d", targetPath, quota.Size, projectID) + return setProjectQuota(q.backingFsBlockDev, projectID, quota) +} + +// setProjectQuota - set the quota for project id on xfs block device +func setProjectQuota(backingFsBlockDev string, projectID uint32, quota Quota) error { + var d C.fs_disk_quota_t + d.d_version = C.FS_DQUOT_VERSION + d.d_id = C.__u32(projectID) + d.d_flags = C.XFS_PROJ_QUOTA + + d.d_fieldmask = C.FS_DQ_BHARD | C.FS_DQ_BSOFT + d.d_blk_hardlimit = C.__u64(quota.Size / 512) + d.d_blk_softlimit = d.d_blk_hardlimit + + var cs = C.CString(backingFsBlockDev) + defer C.free(unsafe.Pointer(cs)) + + _, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, C.Q_XSETPQLIM, + uintptr(unsafe.Pointer(cs)), uintptr(d.d_id), + uintptr(unsafe.Pointer(&d)), 0, 0) + if errno != 0 { + return errors.Wrapf(errno, "failed to set quota limit for projid %d on %s", + projectID, backingFsBlockDev) + } + + return nil +} + +// GetQuota - get the quota limits of a directory that was configured with SetQuota +func (q *Control) GetQuota(targetPath string, quota *Quota) error { + q.RLock() + projectID, ok := q.quotas[targetPath] + q.RUnlock() + if !ok { + return errors.Errorf("quota not found for path: %s", targetPath) + } + + // + // get the quota limit for the container's project id + // + var d C.fs_disk_quota_t + + var cs = C.CString(q.backingFsBlockDev) + defer C.free(unsafe.Pointer(cs)) + + _, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, C.Q_XGETPQUOTA, + uintptr(unsafe.Pointer(cs)), uintptr(C.__u32(projectID)), + uintptr(unsafe.Pointer(&d)), 0, 0) + if errno != 0 { + return errors.Wrapf(errno, "Failed to get quota limit for projid %d on %s", + projectID, q.backingFsBlockDev) + } + quota.Size = uint64(d.d_blk_hardlimit) * 512 + + return nil +} + +// getProjectID - get the project id of path on xfs +func getProjectID(targetPath string) (uint32, error) { + dir, err := openDir(targetPath) + if err != nil { + return 0, err + } + defer closeDir(dir) + + var fsx C.struct_fsxattr + _, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.FS_IOC_FSGETXATTR, + uintptr(unsafe.Pointer(&fsx))) + if errno != 0 { + return 0, errors.Wrapf(errno, "failed to get projid for %s", targetPath) + } + + return uint32(fsx.fsx_projid), nil +} + +// setProjectID - set the project id of path on xfs +func setProjectID(targetPath string, projectID uint32) error { + dir, err := openDir(targetPath) + if err != nil { + return err + } + defer closeDir(dir) + + var fsx C.struct_fsxattr + _, _, errno := unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.FS_IOC_FSGETXATTR, + uintptr(unsafe.Pointer(&fsx))) + if errno != 0 { + return errors.Wrapf(errno, "failed to get projid for %s", targetPath) + } + fsx.fsx_projid = C.__u32(projectID) + fsx.fsx_xflags |= C.FS_XFLAG_PROJINHERIT + _, _, errno = unix.Syscall(unix.SYS_IOCTL, getDirFd(dir), C.FS_IOC_FSSETXATTR, + uintptr(unsafe.Pointer(&fsx))) + if errno != 0 { + return errors.Wrapf(errno, "failed to set projid for %s", targetPath) + } + + return nil +} + +// findNextProjectID - find the next project id to be used for containers +// by scanning driver home directory to find used project ids +func (q *Control) findNextProjectID(home string, baseID uint32) error { + state := getPquotaState() + state.Lock() + defer state.Unlock() + + checkProjID := func(path string) (uint32, error) { + projid, err := getProjectID(path) + if err != nil { + return projid, err + } + if projid > 0 { + q.quotas[path] = projid + } + if state.nextProjectID <= projid { + state.nextProjectID = projid + 1 + } + return projid, nil + } + + files, err := os.ReadDir(home) + if err != nil { + return errors.Errorf("read directory failed: %s", home) + } + for _, file := range files { + if !file.IsDir() { + continue + } + path := filepath.Join(home, file.Name()) + projid, err := checkProjID(path) + if err != nil { + return err + } + if projid > 0 && projid != baseID { + continue + } + subfiles, err := os.ReadDir(path) + if err != nil { + return errors.Errorf("read directory failed: %s", path) + } + for _, subfile := range subfiles { + if !subfile.IsDir() { + continue + } + subpath := filepath.Join(path, subfile.Name()) + _, err := checkProjID(subpath) + if err != nil { + return err + } + } + } + + return nil +} + +func free(p *C.char) { + C.free(unsafe.Pointer(p)) +} + +func openDir(path string) (*C.DIR, error) { + Cpath := C.CString(path) + defer free(Cpath) + + dir := C.opendir(Cpath) + if dir == nil { + return nil, errors.Errorf("failed to open dir: %s", path) + } + return dir, nil +} + +func closeDir(dir *C.DIR) { + if dir != nil { + C.closedir(dir) + } +} + +func getDirFd(dir *C.DIR) uintptr { + return uintptr(C.dirfd(dir)) +} + +// makeBackingFsDev gets the backing block device of the driver home directory +// and creates a block device node under the home directory to be used by +// quotactl commands. +func makeBackingFsDev(home string) (string, error) { + var stat unix.Stat_t + if err := unix.Stat(home, &stat); err != nil { + return "", err + } + + backingFsBlockDev := path.Join(home, "backingFsBlockDev") + // Re-create just in case someone copied the home directory over to a new device + unix.Unlink(backingFsBlockDev) + err := unix.Mknod(backingFsBlockDev, unix.S_IFBLK|0600, int(stat.Dev)) + switch err { + case nil: + return backingFsBlockDev, nil + + case unix.ENOSYS, unix.EPERM: + return "", ErrQuotaNotSupported + + default: + return "", errors.Wrapf(err, "failed to mknod %s", backingFsBlockDev) + } +} + +func hasQuotaSupport(backingFsBlockDev string) (bool, error) { + var cs = C.CString(backingFsBlockDev) + defer free(cs) + var qstat C.fs_quota_stat_t + + _, _, errno := unix.Syscall6(unix.SYS_QUOTACTL, uintptr(C.Q_XGETQSTAT_PRJQUOTA), uintptr(unsafe.Pointer(cs)), 0, uintptr(unsafe.Pointer(&qstat)), 0, 0) + if errno == 0 && qstat.qs_flags&C.FS_QUOTA_PDQ_ENFD > 0 && qstat.qs_flags&C.FS_QUOTA_PDQ_ACCT > 0 { + return true, nil + } + + switch errno { + // These are the known fatal errors, consider all other errors (ENOTTY, etc.. not supporting quota) + case unix.EFAULT, unix.ENOENT, unix.ENOTBLK, unix.EPERM: + default: + return false, nil + } + + return false, errno +} diff --git a/quota/projectquota_test.go b/quota/projectquota_test.go new file mode 100644 index 0000000000000..e25fe99fb431e --- /dev/null +++ b/quota/projectquota_test.go @@ -0,0 +1,78 @@ +//go:build linux +// +build linux + +package quota // import "github.com/docker/docker/quota" + +import ( + "io" + "os" + "path/filepath" + "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +// 10MB +const testQuotaSize = 10 * 1024 * 1024 + +func TestBlockDev(t *testing.T) { + if msg, ok := CanTestQuota(); !ok { + t.Skip(msg) + } + + // get sparse xfs test image + imageFileName, err := PrepareQuotaTestImage(t) + if err != nil { + t.Fatal(err) + } + defer os.Remove(imageFileName) + + t.Run("testBlockDevQuotaDisabled", WrapMountTest(imageFileName, false, testBlockDevQuotaDisabled)) + t.Run("testBlockDevQuotaEnabled", WrapMountTest(imageFileName, true, testBlockDevQuotaEnabled)) + t.Run("testSmallerThanQuota", WrapMountTest(imageFileName, true, WrapQuotaTest(testSmallerThanQuota))) + t.Run("testBiggerThanQuota", WrapMountTest(imageFileName, true, WrapQuotaTest(testBiggerThanQuota))) + t.Run("testRetrieveQuota", WrapMountTest(imageFileName, true, WrapQuotaTest(testRetrieveQuota))) +} + +func testBlockDevQuotaDisabled(t *testing.T, mountPoint, backingFsDev, testDir string) { + hasSupport, err := hasQuotaSupport(backingFsDev) + assert.NilError(t, err) + assert.Check(t, !hasSupport) +} + +func testBlockDevQuotaEnabled(t *testing.T, mountPoint, backingFsDev, testDir string) { + hasSupport, err := hasQuotaSupport(backingFsDev) + assert.NilError(t, err) + assert.Check(t, hasSupport) +} + +func testSmallerThanQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) { + assert.NilError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize})) + smallerThanQuotaFile := filepath.Join(testSubDir, "smaller-than-quota") + assert.NilError(t, os.WriteFile(smallerThanQuotaFile, make([]byte, testQuotaSize/2), 0644)) + assert.NilError(t, os.Remove(smallerThanQuotaFile)) +} + +func testBiggerThanQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) { + // Make sure the quota is being enforced + // TODO: When we implement this under EXT4, we need to shed CAP_SYS_RESOURCE, otherwise + // we're able to violate quota without issue + assert.NilError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize})) + + biggerThanQuotaFile := filepath.Join(testSubDir, "bigger-than-quota") + err := os.WriteFile(biggerThanQuotaFile, make([]byte, testQuotaSize+1), 0644) + assert.Assert(t, is.ErrorContains(err, "")) + if err == io.ErrShortWrite { + assert.NilError(t, os.Remove(biggerThanQuotaFile)) + } +} + +func testRetrieveQuota(t *testing.T, ctrl *Control, homeDir, testDir, testSubDir string) { + // Validate that we can retrieve quota + assert.NilError(t, ctrl.SetQuota(testSubDir, Quota{testQuotaSize})) + + var q Quota + assert.NilError(t, ctrl.GetQuota(testSubDir, &q)) + assert.Check(t, is.Equal(uint64(testQuotaSize), q.Size)) +} diff --git a/quota/projectquota_unsupported.go b/quota/projectquota_unsupported.go new file mode 100644 index 0000000000000..ed21055c2eece --- /dev/null +++ b/quota/projectquota_unsupported.go @@ -0,0 +1,19 @@ +//go:build (linux && exclude_disk_quota) || (linux && !cgo) || !linux +// +build linux,exclude_disk_quota linux,!cgo !linux + +package quota // import "github.com/docker/docker/quota" + +func NewControl(basePath string) (*Control, error) { + return nil, ErrQuotaNotSupported +} + +// SetQuota - assign a unique project id to directory and set the quota limits +// for that project id +func (q *Control) SetQuota(targetPath string, quota Quota) error { + return ErrQuotaNotSupported +} + +// GetQuota - get the quota limits of a directory that was configured with SetQuota +func (q *Control) GetQuota(targetPath string, quota *Quota) error { + return ErrQuotaNotSupported +} diff --git a/quota/testhelpers.go b/quota/testhelpers.go new file mode 100644 index 0000000000000..6087162e2ae78 --- /dev/null +++ b/quota/testhelpers.go @@ -0,0 +1,122 @@ +//go:build linux && !exclude_disk_quota && cgo +// +build linux,!exclude_disk_quota,cgo + +package quota // import "github.com/docker/docker/quota" + +import ( + "os" + "os/exec" + "testing" + + "golang.org/x/sys/unix" + "gotest.tools/v3/assert" + "gotest.tools/v3/fs" +) + +const imageSize = 64 * 1024 * 1024 + +// CanTestQuota - checks if xfs prjquota can be tested +// returns a reason if not +func CanTestQuota() (string, bool) { + if os.Getuid() != 0 { + return "requires mounts", false + } + _, err := exec.LookPath("mkfs.xfs") + if err != nil { + return "mkfs.xfs not found in PATH", false + } + return "", true +} + +// PrepareQuotaTestImage - prepares an xfs prjquota test image +// returns the path the the image on success +func PrepareQuotaTestImage(t *testing.T) (string, error) { + mkfs, err := exec.LookPath("mkfs.xfs") + if err != nil { + return "", err + } + + // create a sparse image + imageFile, err := os.CreateTemp("", "xfs-image") + if err != nil { + return "", err + } + imageFileName := imageFile.Name() + if _, err = imageFile.Seek(imageSize-1, 0); err != nil { + os.Remove(imageFileName) + return "", err + } + if _, err = imageFile.Write([]byte{0}); err != nil { + os.Remove(imageFileName) + return "", err + } + if err = imageFile.Close(); err != nil { + os.Remove(imageFileName) + return "", err + } + + // The reason for disabling these options is sometimes people run with a newer userspace + // than kernelspace + out, err := exec.Command(mkfs, "-m", "crc=0,finobt=0", imageFileName).CombinedOutput() + if len(out) > 0 { + t.Log(string(out)) + } + if err != nil { + os.Remove(imageFileName) + return "", err + } + + return imageFileName, nil +} + +// WrapMountTest - wraps a test function such that it has easy access to a mountPoint and testDir +// with guaranteed prjquota or guaranteed no prjquota support. +func WrapMountTest(imageFileName string, enableQuota bool, testFunc func(t *testing.T, mountPoint, backingFsDev, testDir string)) func(*testing.T) { + return func(t *testing.T) { + mountOptions := "loop" + + if enableQuota { + mountOptions = mountOptions + ",prjquota" + } + + mountPointDir := fs.NewDir(t, "xfs-mountPoint") + defer mountPointDir.Remove() + mountPoint := mountPointDir.Path() + + out, err := exec.Command("mount", "-o", mountOptions, imageFileName, mountPoint).CombinedOutput() + if err != nil { + _, err := os.Stat("/proc/fs/xfs") + if os.IsNotExist(err) { + t.Skip("no /proc/fs/xfs") + } + } + + assert.NilError(t, err, "mount failed: %s", out) + + defer func() { + assert.NilError(t, unix.Unmount(mountPoint, 0)) + }() + + backingFsDev, err := makeBackingFsDev(mountPoint) + assert.NilError(t, err) + + testDir, err := os.MkdirTemp(mountPoint, "per-test") + assert.NilError(t, err) + defer os.RemoveAll(testDir) + + testFunc(t, mountPoint, backingFsDev, testDir) + } +} + +// WrapQuotaTest - wraps a test function such that is has easy and guaranteed access to a quota Control +// instance with a quota test dir under its control. +func WrapQuotaTest(testFunc func(t *testing.T, ctrl *Control, mountPoint, testDir, testSubDir string)) func(t *testing.T, mountPoint, backingFsDev, testDir string) { + return func(t *testing.T, mountPoint, backingFsDev, testDir string) { + ctrl, err := NewControl(testDir) + assert.NilError(t, err) + + testSubDir, err := os.MkdirTemp(testDir, "quota-test") + assert.NilError(t, err) + testFunc(t, ctrl, mountPoint, testDir, testSubDir) + } +} diff --git a/quota/types.go b/quota/types.go new file mode 100644 index 0000000000000..4661bc74e0383 --- /dev/null +++ b/quota/types.go @@ -0,0 +1,16 @@ +package quota // import "github.com/docker/docker/quota" + +import "sync" + +// Quota limit params - currently we only control blocks hard limit +type Quota struct { + Size uint64 +} + +// Control - Context to be used by storage driver (e.g. overlay) +// who wants to apply project quotas to container dirs +type Control struct { + backingFsBlockDev string + sync.RWMutex // protect nextProjectID and quotas map + quotas map[string]uint32 +} diff --git a/reference/store.go b/reference/store.go index b01051bf58922..d6ef6697f535f 100644 --- a/reference/store.go +++ b/reference/store.go @@ -10,7 +10,7 @@ import ( "github.com/docker/distribution/reference" "github.com/docker/docker/pkg/ioutils" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" ) @@ -149,6 +149,11 @@ func (store *store) addReference(ref reference.Named, id digest.Digest, force bo oldID, exists := repository[refStr] if exists { + if oldID == id { + // Nothing to do. The caller may have checked for this using store.Get in advance, but store.mu was unlocked in the meantime, so this can legitimately happen nevertheless. + return nil + } + // force only works for tags if digested, isDigest := ref.(reference.Canonical); isDigest { return errors.WithStack(conflictingTagError("Cannot overwrite digest " + digested.Digest().String())) diff --git a/reference/store_test.go b/reference/store_test.go index e8ccd962678dc..a2fdfdb75ea51 100644 --- a/reference/store_test.go +++ b/reference/store_test.go @@ -2,16 +2,15 @@ package reference // import "github.com/docker/docker/reference" import ( "bytes" - "io/ioutil" "os" "path/filepath" "strings" "testing" "github.com/docker/distribution/reference" - "github.com/opencontainers/go-digest" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + digest "github.com/opencontainers/go-digest" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) var ( @@ -29,7 +28,7 @@ var ( ) func TestLoad(t *testing.T) { - jsonFile, err := ioutil.TempFile("", "tag-store-test") + jsonFile, err := os.CreateTemp("", "tag-store-test") if err != nil { t.Fatalf("error creating temp file: %v", err) } @@ -63,7 +62,7 @@ func TestLoad(t *testing.T) { } func TestSave(t *testing.T) { - jsonFile, err := ioutil.TempFile("", "tag-store-test") + jsonFile, err := os.CreateTemp("", "tag-store-test") assert.NilError(t, err) _, err = jsonFile.Write([]byte(`{}`)) @@ -94,7 +93,7 @@ func TestSave(t *testing.T) { } } - jsonBytes, err := ioutil.ReadFile(jsonFile.Name()) + jsonBytes, err := os.ReadFile(jsonFile.Name()) if err != nil { t.Fatalf("could not read json file: %v", err) } @@ -105,13 +104,14 @@ func TestSave(t *testing.T) { } func TestAddDeleteGet(t *testing.T) { - jsonFile, err := ioutil.TempFile("", "tag-store-test") + jsonFile, err := os.CreateTemp("", "tag-store-test") if err != nil { t.Fatalf("error creating temp file: %v", err) } _, err = jsonFile.Write([]byte(`{}`)) - jsonFile.Close() - defer os.RemoveAll(jsonFile.Name()) + assert.NilError(t, err) + _ = jsonFile.Close() + defer func() { _ = os.RemoveAll(jsonFile.Name()) }() store, err := NewReferenceStore(jsonFile.Name()) if err != nil { @@ -163,6 +163,10 @@ func TestAddDeleteGet(t *testing.T) { if err = store.AddTag(ref4, testImageID2, false); err != nil { t.Fatalf("error adding to store: %v", err) } + // Write the same values again; should silently succeed + if err = store.AddTag(ref4, testImageID2, false); err != nil { + t.Fatalf("error redundantly adding to store: %v", err) + } ref5, err := reference.ParseNormalizedNamed("username/repo3@sha256:58153dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c") if err != nil { @@ -171,6 +175,10 @@ func TestAddDeleteGet(t *testing.T) { if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil { t.Fatalf("error adding to store: %v", err) } + // Write the same values again; should silently succeed + if err = store.AddDigest(ref5.(reference.Canonical), testImageID2, false); err != nil { + t.Fatalf("error redundantly adding to store: %v", err) + } // Attempt to overwrite with force == false if err = store.AddTag(ref4, testImageID3, false); err == nil || !strings.HasPrefix(err.Error(), "Conflict:") { @@ -327,7 +335,7 @@ func TestAddDeleteGet(t *testing.T) { } func TestInvalidTags(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "tag-store-test") + tmpDir, err := os.MkdirTemp("", "tag-store-test") assert.NilError(t, err) defer os.RemoveAll(tmpDir) diff --git a/registry/auth.go b/registry/auth.go index 1f2043a0d9622..81533d269fbd7 100644 --- a/registry/auth.go +++ b/registry/auth.go @@ -1,7 +1,6 @@ package registry // import "github.com/docker/docker/registry" import ( - "io/ioutil" "net/http" "net/url" "strings" @@ -12,7 +11,6 @@ import ( "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" - "github.com/docker/docker/errdefs" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -22,51 +20,6 @@ const ( AuthClientID = "docker" ) -// loginV1 tries to register/login to the v1 registry server. -func loginV1(authConfig *types.AuthConfig, apiEndpoint APIEndpoint, userAgent string) (string, string, error) { - registryEndpoint := apiEndpoint.ToV1Endpoint(userAgent, nil) - serverAddress := registryEndpoint.String() - - logrus.Debugf("attempting v1 login to registry endpoint %s", serverAddress) - - if serverAddress == "" { - return "", "", errdefs.System(errors.New("server Error: Server Address not set")) - } - - req, err := http.NewRequest("GET", serverAddress+"users/", nil) - if err != nil { - return "", "", err - } - req.SetBasicAuth(authConfig.Username, authConfig.Password) - resp, err := registryEndpoint.client.Do(req) - if err != nil { - // fallback when request could not be completed - return "", "", fallbackError{ - err: err, - } - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", "", errdefs.System(err) - } - - switch resp.StatusCode { - case http.StatusOK: - return "Login Succeeded", "", nil - case http.StatusUnauthorized: - return "", "", errdefs.Unauthorized(errors.New("Wrong login/password, please try again")) - case http.StatusForbidden: - // *TODO: Use registry configuration to determine what this says, if anything? - return "", "", errdefs.Forbidden(errors.Errorf("Login: Account is not active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)) - case http.StatusInternalServerError: - logrus.Errorf("%s returned status code %d. Response Body :\n%s", req.URL.String(), resp.StatusCode, body) - return "", "", errdefs.System(errors.New("Internal Server Error")) - } - return "", "", errdefs.System(errors.Errorf("Login: %s (Code: %d; Headers: %s)", body, - resp.StatusCode, resp.Header)) -} - type loginCredentialStore struct { authConfig *types.AuthConfig } @@ -124,37 +77,29 @@ func (err fallbackError) Error() string { // endpoint will be pinged to get authorization challenges. These challenges // will be used to authenticate against the registry to validate credentials. func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent string) (string, string, error) { - logrus.Debugf("attempting v2 login to registry endpoint %s", strings.TrimRight(endpoint.URL.String(), "/")+"/v2/") - - modifiers := Headers(userAgent, nil) - authTransport := transport.NewTransport(NewTransport(endpoint.TLSConfig), modifiers...) + var ( + endpointStr = strings.TrimRight(endpoint.URL.String(), "/") + "/v2/" + modifiers = Headers(userAgent, nil) + authTransport = transport.NewTransport(NewTransport(endpoint.TLSConfig), modifiers...) + credentialAuthConfig = *authConfig + creds = loginCredentialStore{authConfig: &credentialAuthConfig} + ) - credentialAuthConfig := *authConfig - creds := loginCredentialStore{ - authConfig: &credentialAuthConfig, - } + logrus.Debugf("attempting v2 login to registry endpoint %s", endpointStr) - loginClient, foundV2, err := v2AuthHTTPClient(endpoint.URL, authTransport, modifiers, creds, nil) + loginClient, err := v2AuthHTTPClient(endpoint.URL, authTransport, modifiers, creds, nil) if err != nil { return "", "", err } - endpointStr := strings.TrimRight(endpoint.URL.String(), "/") + "/v2/" - req, err := http.NewRequest("GET", endpointStr, nil) + req, err := http.NewRequest(http.MethodGet, endpointStr, nil) if err != nil { - if !foundV2 { - err = fallbackError{err: err} - } return "", "", err } resp, err := loginClient.Do(req) if err != nil { err = translateV2AuthError(err) - if !foundV2 { - err = fallbackError{err: err} - } - return "", "", err } defer resp.Body.Close() @@ -165,19 +110,13 @@ func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent strin // TODO(dmcgowan): Attempt to further interpret result, status code and error code string err = errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode)) - if !foundV2 { - err = fallbackError{err: err} - } return "", "", err } -func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, bool, error) { - challengeManager, foundV2, err := PingV2Registry(endpoint, authTransport) +func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, error) { + challengeManager, err := PingV2Registry(endpoint, authTransport) if err != nil { - if !foundV2 { - err = fallbackError{err: err} - } - return nil, foundV2, err + return nil, err } tokenHandlerOptions := auth.TokenHandlerOptions{ @@ -195,8 +134,7 @@ func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifi return &http.Client{ Transport: tr, Timeout: 15 * time.Second, - }, foundV2, nil - + }, nil } // ConvertToHostname converts a registry url which has http|https prepended @@ -245,52 +183,30 @@ func (err PingResponseError) Error() string { } // PingV2Registry attempts to ping a v2 registry and on success return a -// challenge manager for the supported authentication types and -// whether v2 was confirmed by the response. If a response is received but -// cannot be interpreted a PingResponseError will be returned. -// nolint: interfacer -func PingV2Registry(endpoint *url.URL, transport http.RoundTripper) (challenge.Manager, bool, error) { - var ( - foundV2 = false - v2Version = auth.APIVersion{ - Type: "registry", - Version: "2.0", - } - ) - +// challenge manager for the supported authentication types. +// If a response is received but cannot be interpreted, a PingResponseError will be returned. +func PingV2Registry(endpoint *url.URL, transport http.RoundTripper) (challenge.Manager, error) { pingClient := &http.Client{ Transport: transport, Timeout: 15 * time.Second, } endpointStr := strings.TrimRight(endpoint.String(), "/") + "/v2/" - req, err := http.NewRequest("GET", endpointStr, nil) + req, err := http.NewRequest(http.MethodGet, endpointStr, nil) if err != nil { - return nil, false, err + return nil, err } resp, err := pingClient.Do(req) if err != nil { - return nil, false, err + return nil, err } defer resp.Body.Close() - versions := auth.APIVersions(resp, DefaultRegistryVersionHeader) - for _, pingVersion := range versions { - if pingVersion == v2Version { - // The version header indicates we're definitely - // talking to a v2 registry. So don't allow future - // fallbacks to the v1 protocol. - - foundV2 = true - break - } - } - challengeManager := challenge.NewSimpleManager() if err := challengeManager.AddResponse(resp); err != nil { - return nil, foundV2, PingResponseError{ + return nil, PingResponseError{ Err: err, } } - return challengeManager, foundV2, nil + return challengeManager, nil } diff --git a/registry/auth_test.go b/registry/auth_test.go index f8f3e1997be1d..511b2ef8bde1c 100644 --- a/registry/auth_test.go +++ b/registry/auth_test.go @@ -20,20 +20,6 @@ func buildAuthConfigs() map[string]types.AuthConfig { return authConfigs } -func TestSameAuthDataPostSave(t *testing.T) { - authConfigs := buildAuthConfigs() - authConfig := authConfigs["testIndex"] - if authConfig.Username != "docker-user" { - t.Fail() - } - if authConfig.Password != "docker-pass" { - t.Fail() - } - if authConfig.Auth != "" { - t.Fail() - } -} - func TestResolveAuthConfigIndexServer(t *testing.T) { authConfigs := buildAuthConfigs() indexConfig := authConfigs[IndexServer] @@ -72,7 +58,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) { expectedAuths := map[string]types.AuthConfig{ "registry.example.com": registryAuth, "localhost:8000": localAuth, - "registry.com": localAuth, + "example.com": localAuth, } validRegistries := map[string][]string{ @@ -88,11 +74,11 @@ func TestResolveAuthConfigFullURL(t *testing.T) { "localhost:8000", "localhost:8000/v1/", }, - "registry.com": { - "https://registry.com/v1/", - "http://registry.com/v1/", - "registry.com", - "registry.com/v1/", + "example.com": { + "https://example.com/v1/", + "http://example.com/v1/", + "example.com", + "example.com/v1/", }, } diff --git a/registry/config.go b/registry/config.go index de5a526b694d3..cfb87f0d0d9b6 100644 --- a/registry/config.go +++ b/registry/config.go @@ -19,54 +19,42 @@ type ServiceOptions struct { AllowNondistributableArtifacts []string `json:"allow-nondistributable-artifacts,omitempty"` Mirrors []string `json:"registry-mirrors,omitempty"` InsecureRegistries []string `json:"insecure-registries,omitempty"` - - // V2Only controls access to legacy registries. If it is set to true via the - // command line flag the daemon will not attempt to contact v1 legacy registries - V2Only bool `json:"disable-legacy-registry,omitempty"` } // serviceConfig holds daemon configuration for the registry service. type serviceConfig struct { registrytypes.ServiceConfig - V2Only bool } -var ( +const ( // DefaultNamespace is the default namespace DefaultNamespace = "docker.io" - // DefaultRegistryVersionHeader is the name of the default HTTP header - // that carries Registry version info - DefaultRegistryVersionHeader = "Docker-Distribution-Api-Version" - // IndexHostname is the index hostname IndexHostname = "index.docker.io" // IndexServer is used for user auth and image search IndexServer = "https://" + IndexHostname + "/v1/" // IndexName is the name of the index IndexName = "docker.io" +) +var ( // DefaultV2Registry is the URI of the default v2 registry DefaultV2Registry = &url.URL{ Scheme: "https", Host: "registry-1.docker.io", } -) -var ( // ErrInvalidRepositoryName is an error returned if the repository name did // not have the correct form ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")") emptyServiceConfig, _ = newServiceConfig(ServiceOptions{}) -) + validHostPortRegex = regexp.MustCompile(`^` + reference.DomainRegexp.String() + `$`) -var ( - validHostPortRegex = regexp.MustCompile(`^` + reference.DomainRegexp.String() + `$`) + // for mocking in unit tests + lookupIP = net.LookupIP ) -// for mocking in unit tests -var lookupIP = net.LookupIP - // newServiceConfig returns a new instance of ServiceConfig func newServiceConfig(options ServiceOptions) (*serviceConfig, error) { config := &serviceConfig{ @@ -76,7 +64,6 @@ func newServiceConfig(options ServiceOptions) (*serviceConfig, error) { // Hack: Bypass setting the mirrors to IndexConfigs since they are going away // and Mirrors are only for the official registry anyways. }, - V2Only: options.V2Only, } if err := config.LoadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts); err != nil { return nil, err diff --git a/registry/config_test.go b/registry/config_test.go index 30a257e325563..c939eff56f51b 100644 --- a/registry/config_test.go +++ b/registry/config_test.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestLoadAllowNondistributableArtifacts(t *testing.T) { @@ -52,20 +52,20 @@ func TestLoadAllowNondistributableArtifacts(t *testing.T) { }, { - registries: []string{"http://mytest.com"}, - err: "allow-nondistributable-artifacts registry http://mytest.com should not contain '://'", + registries: []string{"http://myregistry.example.com"}, + err: "allow-nondistributable-artifacts registry http://myregistry.example.com should not contain '://'", }, { - registries: []string{"https://mytest.com"}, - err: "allow-nondistributable-artifacts registry https://mytest.com should not contain '://'", + registries: []string{"https://myregistry.example.com"}, + err: "allow-nondistributable-artifacts registry https://myregistry.example.com should not contain '://'", }, { - registries: []string{"HTTP://mytest.com"}, - err: "allow-nondistributable-artifacts registry HTTP://mytest.com should not contain '://'", + registries: []string{"HTTP://myregistry.example.com"}, + err: "allow-nondistributable-artifacts registry HTTP://myregistry.example.com should not contain '://'", }, { - registries: []string{"svn://mytest.com"}, - err: "allow-nondistributable-artifacts registry svn://mytest.com should not contain '://'", + registries: []string{"svn://myregistry.example.com"}, + err: "allow-nondistributable-artifacts registry svn://myregistry.example.com should not contain '://'", }, { registries: []string{"-invalid-registry"}, @@ -80,16 +80,16 @@ func TestLoadAllowNondistributableArtifacts(t *testing.T) { err: `allow-nondistributable-artifacts registry 1200:0000:AB00:1234:0000:2552:7777:1313:8080 is not valid: invalid host "1200:0000:AB00:1234:0000:2552:7777:1313:8080"`, }, { - registries: []string{`mytest.com:500000`}, - err: `allow-nondistributable-artifacts registry mytest.com:500000 is not valid: invalid port "500000"`, + registries: []string{`myregistry.example.com:500000`}, + err: `allow-nondistributable-artifacts registry myregistry.example.com:500000 is not valid: invalid port "500000"`, }, { - registries: []string{`"mytest.com"`}, - err: `allow-nondistributable-artifacts registry "mytest.com" is not valid: invalid host "\"mytest.com\""`, + registries: []string{`"myregistry.example.com"`}, + err: `allow-nondistributable-artifacts registry "myregistry.example.com" is not valid: invalid host "\"myregistry.example.com\""`, }, { - registries: []string{`"mytest.com:5000"`}, - err: `allow-nondistributable-artifacts registry "mytest.com:5000" is not valid: invalid host "\"mytest.com"`, + registries: []string{`"myregistry.example.com:5000"`}, + err: `allow-nondistributable-artifacts registry "myregistry.example.com:5000" is not valid: invalid host "\"myregistry.example.com"`, }, } for _, testCase := range testCases { @@ -129,10 +129,10 @@ func TestLoadAllowNondistributableArtifacts(t *testing.T) { func TestValidateMirror(t *testing.T) { valid := []string{ - "http://mirror-1.com", - "http://mirror-1.com/", - "https://mirror-1.com", - "https://mirror-1.com/", + "http://mirror-1.example.com", + "http://mirror-1.example.com/", + "https://mirror-1.example.com", + "https://mirror-1.example.com/", "http://localhost", "https://localhost", "http://localhost:5000", @@ -145,18 +145,18 @@ func TestValidateMirror(t *testing.T) { invalid := []string{ "!invalid!://%as%", - "ftp://mirror-1.com", - "http://mirror-1.com/?q=foo", - "http://mirror-1.com/v1/", - "http://mirror-1.com/v1/?q=foo", - "http://mirror-1.com/v1/?q=foo#frag", - "http://mirror-1.com?q=foo", - "https://mirror-1.com#frag", - "https://mirror-1.com/#frag", - "http://foo:bar@mirror-1.com/", - "https://mirror-1.com/v1/", - "https://mirror-1.com/v1/#", - "https://mirror-1.com?q", + "ftp://mirror-1.example.com", + "http://mirror-1.example.com/?q=foo", + "http://mirror-1.example.com/v1/", + "http://mirror-1.example.com/v1/?q=foo", + "http://mirror-1.example.com/v1/?q=foo#frag", + "http://mirror-1.example.com?q=foo", + "https://mirror-1.example.com#frag", + "https://mirror-1.example.com/#frag", + "http://foo:bar@mirror-1.example.com/", + "https://mirror-1.example.com/v1/", + "https://mirror-1.example.com/v1/#", + "https://mirror-1.example.com?q", } for _, address := range valid { @@ -195,20 +195,20 @@ func TestLoadInsecureRegistries(t *testing.T) { index: "[2001:db8::1]:80", }, { - registries: []string{"http://mytest.com"}, - index: "mytest.com", + registries: []string{"http://myregistry.example.com"}, + index: "myregistry.example.com", }, { - registries: []string{"https://mytest.com"}, - index: "mytest.com", + registries: []string{"https://myregistry.example.com"}, + index: "myregistry.example.com", }, { - registries: []string{"HTTP://mytest.com"}, - index: "mytest.com", + registries: []string{"HTTP://myregistry.example.com"}, + index: "myregistry.example.com", }, { - registries: []string{"svn://mytest.com"}, - err: "insecure registry svn://mytest.com should not contain '://'", + registries: []string{"svn://myregistry.example.com"}, + err: "insecure registry svn://myregistry.example.com should not contain '://'", }, { registries: []string{"-invalid-registry"}, @@ -223,16 +223,16 @@ func TestLoadInsecureRegistries(t *testing.T) { err: `insecure registry 1200:0000:AB00:1234:0000:2552:7777:1313:8080 is not valid: invalid host "1200:0000:AB00:1234:0000:2552:7777:1313:8080"`, }, { - registries: []string{`mytest.com:500000`}, - err: `insecure registry mytest.com:500000 is not valid: invalid port "500000"`, + registries: []string{`myregistry.example.com:500000`}, + err: `insecure registry myregistry.example.com:500000 is not valid: invalid port "500000"`, }, { - registries: []string{`"mytest.com"`}, - err: `insecure registry "mytest.com" is not valid: invalid host "\"mytest.com\""`, + registries: []string{`"myregistry.example.com"`}, + err: `insecure registry "myregistry.example.com" is not valid: invalid host "\"myregistry.example.com\""`, }, { - registries: []string{`"mytest.com:5000"`}, - err: `insecure registry "mytest.com:5000" is not valid: invalid host "\"mytest.com"`, + registries: []string{`"myregistry.example.com:5000"`}, + err: `insecure registry "myregistry.example.com:5000" is not valid: invalid host "\"myregistry.example.com"`, }, } for _, testCase := range testCases { @@ -341,8 +341,8 @@ func TestValidateIndexName(t *testing.T) { expect: "mytest-1.com", }, { - index: "mirror-1.com/v1/?q=foo", - expect: "mirror-1.com/v1/?q=foo", + index: "mirror-1.example.com/v1/?q=foo", + expect: "mirror-1.example.com/v1/?q=foo", }, } @@ -370,8 +370,8 @@ func TestValidateIndexNameWithError(t *testing.T) { err: "invalid index name (-example.com). Cannot begin or end with a hyphen", }, { - index: "mirror-1.com/v1/?q=foo-", - err: "invalid index name (mirror-1.com/v1/?q=foo-). Cannot begin or end with a hyphen", + index: "mirror-1.example.com/v1/?q=foo-", + err: "invalid index name (mirror-1.example.com/v1/?q=foo-). Cannot begin or end with a hyphen", }, } for _, testCase := range invalid { diff --git a/registry/config_unix.go b/registry/config_unix.go index 20fb47bcaedc1..b5bb31cfa6595 100644 --- a/registry/config_unix.go +++ b/registry/config_unix.go @@ -1,12 +1,28 @@ +//go:build !windows // +build !windows package registry // import "github.com/docker/docker/registry" -var ( - // CertsDir is the directory where certificates are stored - CertsDir = "/etc/docker/certs.d" +import ( + "path/filepath" + + "github.com/docker/docker/pkg/homedir" + "github.com/docker/docker/rootless" ) +// CertsDir is the directory where certificates are stored +func CertsDir() string { + d := "/etc/docker/certs.d" + + if rootless.RunningWithRootlessKit() { + configHome, err := homedir.GetConfigHome() + if err == nil { + d = filepath.Join(configHome, "docker/certs.d") + } + } + return d +} + // cleanPath is used to ensure that a directory name is valid on the target // platform. It will be passed in something *similar* to a URL such as // https:/index.docker.io/v1. Not all platforms support directory names diff --git a/registry/config_windows.go b/registry/config_windows.go index 6de0508f87239..4ae1e07abfd42 100644 --- a/registry/config_windows.go +++ b/registry/config_windows.go @@ -7,7 +7,9 @@ import ( ) // CertsDir is the directory where certificates are stored -var CertsDir = os.Getenv("programdata") + `\docker\certs.d` +func CertsDir() string { + return os.Getenv("programdata") + `\docker\certs.d` +} // cleanPath is used to ensure that a directory name is valid on the target // platform. It will be passed in something *similar* to a URL such as diff --git a/registry/endpoint_v1.go b/registry/endpoint_v1.go index 832fdb95a482b..684c330dc305c 100644 --- a/registry/endpoint_v1.go +++ b/registry/endpoint_v1.go @@ -4,7 +4,7 @@ import ( "crypto/tls" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "net/url" "strings" @@ -22,6 +22,7 @@ type V1Endpoint struct { } // NewV1Endpoint parses the given address to return a registry endpoint. +// TODO: remove. This is only used by search. func NewV1Endpoint(index *registrytypes.IndexInfo, userAgent string, metaHeaders http.Header) (*V1Endpoint, error) { tlsConfig, err := newTLSConfig(index.Name, index.Secure) if err != nil { @@ -33,7 +34,8 @@ func NewV1Endpoint(index *registrytypes.IndexInfo, userAgent string, metaHeaders return nil, err } - if err := validateEndpoint(endpoint); err != nil { + err = validateEndpoint(endpoint) + if err != nil { return nil, err } @@ -67,20 +69,6 @@ func validateEndpoint(endpoint *V1Endpoint) error { return nil } -func newV1Endpoint(address url.URL, tlsConfig *tls.Config, userAgent string, metaHeaders http.Header) *V1Endpoint { - endpoint := &V1Endpoint{ - IsSecure: tlsConfig == nil || !tlsConfig.InsecureSkipVerify, - URL: new(url.URL), - } - - *endpoint.URL = address - - // TODO(tiborvass): make sure a ConnectTimeout transport is used - tr := NewTransport(tlsConfig) - endpoint.client = HTTPClient(transport.NewTransport(tr, Headers(userAgent, metaHeaders)...)) - return endpoint -} - // trimV1Address trims the version off the address and returns the // trimmed address or an error if there is a non-V1 version. func trimV1Address(address string) (string, error) { @@ -89,10 +77,7 @@ func trimV1Address(address string) (string, error) { apiVersionStr string ) - if strings.HasSuffix(address, "/") { - address = address[:len(address)-1] - } - + address = strings.TrimSuffix(address, "/") chunks = strings.Split(address, "/") apiVersionStr = chunks[len(chunks)-1] if apiVersionStr == "v1" { @@ -123,12 +108,14 @@ func newV1EndpointFromStr(address string, tlsConfig *tls.Config, userAgent strin return nil, err } - endpoint := newV1Endpoint(*uri, tlsConfig, userAgent, metaHeaders) - if err != nil { - return nil, err - } + // TODO(tiborvass): make sure a ConnectTimeout transport is used + tr := NewTransport(tlsConfig) - return endpoint, nil + return &V1Endpoint{ + IsSecure: tlsConfig == nil || !tlsConfig.InsecureSkipVerify, + URL: uri, + client: HTTPClient(transport.NewTransport(tr, Headers(userAgent, metaHeaders)...)), + }, nil } // Get the formatted URL for the root of this registry Endpoint @@ -144,29 +131,28 @@ func (e *V1Endpoint) Path(path string) string { // Ping returns a PingResult which indicates whether the registry is standalone or not. func (e *V1Endpoint) Ping() (PingResult, error) { - logrus.Debugf("attempting v1 ping for registry endpoint %s", e) - if e.String() == IndexServer { // Skip the check, we know this one is valid // (and we never want to fallback to http in case of error) - return PingResult{Standalone: false}, nil + return PingResult{}, nil } - req, err := http.NewRequest("GET", e.Path("_ping"), nil) + logrus.Debugf("attempting v1 ping for registry endpoint %s", e) + req, err := http.NewRequest(http.MethodGet, e.Path("_ping"), nil) if err != nil { - return PingResult{Standalone: false}, err + return PingResult{}, err } resp, err := e.client.Do(req) if err != nil { - return PingResult{Standalone: false}, err + return PingResult{}, err } defer resp.Body.Close() - jsonString, err := ioutil.ReadAll(resp.Body) + jsonString, err := io.ReadAll(resp.Body) if err != nil { - return PingResult{Standalone: false}, fmt.Errorf("error while reading the http response: %s", err) + return PingResult{}, fmt.Errorf("error while reading the http response: %s", err) } // If the header is absent, we assume true for compatibility with earlier @@ -179,13 +165,12 @@ func (e *V1Endpoint) Ping() (PingResult, error) { // don't stop here. Just assume sane defaults } if hdr := resp.Header.Get("X-Docker-Registry-Version"); hdr != "" { - logrus.Debugf("Registry version header: '%s'", hdr) info.Version = hdr } logrus.Debugf("PingResult.Version: %q", info.Version) standalone := resp.Header.Get("X-Docker-Registry-Standalone") - logrus.Debugf("Registry standalone header: '%s'", standalone) + // Accepted values are "true" (case-insensitive) and "1". if strings.EqualFold(standalone, "true") || standalone == "1" { info.Standalone = true diff --git a/registry/errors.go b/registry/errors.go index 5bab02e5e2918..4906303efcf9e 100644 --- a/registry/errors.go +++ b/registry/errors.go @@ -7,14 +7,6 @@ import ( "github.com/docker/docker/errdefs" ) -type notFoundError string - -func (e notFoundError) Error() string { - return string(e) -} - -func (notFoundError) NotFound() {} - func translateV2AuthError(err error) error { switch e := err.(type) { case *url.Error: diff --git a/registry/registry.go b/registry/registry.go index 7a84bbfb7eef2..55fc117c8276f 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -3,9 +3,7 @@ package registry // import "github.com/docker/docker/registry" import ( "crypto/tls" - "errors" "fmt" - "io/ioutil" "net" "net/http" "os" @@ -14,16 +12,18 @@ import ( "time" "github.com/docker/distribution/registry/client/transport" - "github.com/docker/go-connections/sockets" "github.com/docker/go-connections/tlsconfig" "github.com/sirupsen/logrus" ) -var ( - // ErrAlreadyExists is an error returned if an image being pushed - // already exists on the remote side - ErrAlreadyExists = errors.New("Image already exists") -) +// HostCertsDir returns the config directory for a specific host +func HostCertsDir(hostname string) (string, error) { + certsDir := CertsDir() + + hostDir := filepath.Join(certsDir, cleanPath(hostname)) + + return hostDir, nil +} func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) { // PreferredServerCipherSuites should have no effect @@ -31,8 +31,12 @@ func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) { tlsConfig.InsecureSkipVerify = !isSecure - if isSecure && CertsDir != "" { - hostDir := filepath.Join(CertsDir, cleanPath(hostname)) + if isSecure && CertsDir() != "" { + hostDir, err := HostCertsDir(hostname) + if err != nil { + return nil, err + } + logrus.Debugf("hostDir: %s", hostDir) if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil { return nil, err @@ -42,7 +46,7 @@ func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) { return tlsConfig, nil } -func hasFile(files []os.FileInfo, name string) bool { +func hasFile(files []os.DirEntry, name string) bool { for _, f := range files { if f.Name() == name { return true @@ -55,7 +59,7 @@ func hasFile(files []os.FileInfo, name string) bool { // including roots and certificate pairs and updates the // provided TLS configuration. func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error { - fs, err := ioutil.ReadDir(directory) + fs, err := os.ReadDir(directory) if err != nil && !os.IsNotExist(err) { return err } @@ -70,7 +74,7 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error { tlsConfig.RootCAs = systemPool } logrus.Debugf("crt: %s", filepath.Join(directory, f.Name())) - data, err := ioutil.ReadFile(filepath.Join(directory, f.Name())) + data, err := os.ReadFile(filepath.Join(directory, f.Name())) if err != nil { return err } @@ -145,7 +149,7 @@ func trustedLocation(req *http.Request) bool { // addRequiredHeadersToRedirectedRequests adds the necessary redirection headers // for redirected requests func addRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error { - if via != nil && via[0] != nil { + if len(via) != 0 && via[0] != nil { if trustedLocation(req) && trustedLocation(via[0]) { req.Header = via[0].Header return nil @@ -176,16 +180,12 @@ func NewTransport(tlsConfig *tls.Config) *http.Transport { base := &http.Transport{ Proxy: http.ProxyFromEnvironment, - Dial: direct.Dial, + DialContext: direct.DialContext, TLSHandshakeTimeout: 10 * time.Second, TLSClientConfig: tlsConfig, // TODO(dmcgowan): Call close idle connections when complete and use keep alive DisableKeepAlives: true, } - proxyDialer, err := sockets.DialerFromEnvironment(direct) - if err == nil { - base.Dial = proxyDialer.Dial - } return base } diff --git a/registry/registry_mock_test.go b/registry/registry_mock_test.go index bf17eb9fc7b2d..74c5a2b1689d4 100644 --- a/registry/registry_mock_test.go +++ b/registry/registry_mock_test.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net" "net/http" "net/http/httptest" @@ -97,19 +96,19 @@ func init() { r := mux.NewRouter() // /v1/ - r.HandleFunc("/v1/_ping", handlerGetPing).Methods("GET") - r.HandleFunc("/v1/images/{image_id:[^/]+}/{action:json|layer|ancestry}", handlerGetImage).Methods("GET") - r.HandleFunc("/v1/images/{image_id:[^/]+}/{action:json|layer|checksum}", handlerPutImage).Methods("PUT") - r.HandleFunc("/v1/repositories/{repository:.+}/tags", handlerGetDeleteTags).Methods("GET", "DELETE") - r.HandleFunc("/v1/repositories/{repository:.+}/tags/{tag:.+}", handlerGetTag).Methods("GET") - r.HandleFunc("/v1/repositories/{repository:.+}/tags/{tag:.+}", handlerPutTag).Methods("PUT") - r.HandleFunc("/v1/users{null:.*}", handlerUsers).Methods("GET", "POST", "PUT") - r.HandleFunc("/v1/repositories/{repository:.+}{action:/images|/}", handlerImages).Methods("GET", "PUT", "DELETE") - r.HandleFunc("/v1/repositories/{repository:.+}/auth", handlerAuth).Methods("PUT") - r.HandleFunc("/v1/search", handlerSearch).Methods("GET") + r.HandleFunc("/v1/_ping", handlerGetPing).Methods(http.MethodGet) + r.HandleFunc("/v1/images/{image_id:[^/]+}/{action:json|layer|ancestry}", handlerGetImage).Methods(http.MethodGet) + r.HandleFunc("/v1/images/{image_id:[^/]+}/{action:json|layer|checksum}", handlerPutImage).Methods(http.MethodPut) + r.HandleFunc("/v1/repositories/{repository:.+}/tags", handlerGetDeleteTags).Methods(http.MethodGet, http.MethodDelete) + r.HandleFunc("/v1/repositories/{repository:.+}/tags/{tag:.+}", handlerGetTag).Methods(http.MethodGet) + r.HandleFunc("/v1/repositories/{repository:.+}/tags/{tag:.+}", handlerPutTag).Methods(http.MethodPut) + r.HandleFunc("/v1/users{null:.*}", handlerUsers).Methods(http.MethodGet, http.MethodPost, http.MethodPut) + r.HandleFunc("/v1/repositories/{repository:.+}{action:/images|/}", handlerImages).Methods(http.MethodGet, http.MethodPut, http.MethodDelete) + r.HandleFunc("/v1/repositories/{repository:.+}/auth", handlerAuth).Methods(http.MethodPut) + r.HandleFunc("/v1/search", handlerSearch).Methods(http.MethodGet) // /v2/ - r.HandleFunc("/v2/version", handlerGetPing).Methods("GET") + r.HandleFunc("/v2/version", handlerGetPing).Methods(http.MethodGet) testHTTPServer = httptest.NewServer(handlerAccessLog(r)) testHTTPSServer = httptest.NewTLSServer(handlerAccessLog(r)) @@ -205,7 +204,7 @@ func writeResponse(w http.ResponseWriter, message interface{}, code int) { } func readJSON(r *http.Request, dest interface{}) error { - body, err := ioutil.ReadAll(r.Body) + body, err := io.ReadAll(r.Body) if err != nil { return err } @@ -268,7 +267,7 @@ func requiresAuth(w http.ResponseWriter, r *http.Request) bool { value := fmt.Sprintf("FAKE-SESSION-%d", time.Now().UnixNano()) cookie := &http.Cookie{Name: "session", Value: value, MaxAge: 3600} http.SetCookie(w, cookie) - //FIXME(sam): this should be sent only on Index routes + // FIXME(sam): this should be sent only on Index routes value = fmt.Sprintf("FAKE-TOKEN-%d", time.Now().UnixNano()) w.Header().Add("X-Docker-Token", value) } @@ -281,12 +280,12 @@ func requiresAuth(w http.ResponseWriter, r *http.Request) bool { return true } w.Header().Add("WWW-Authenticate", "token") - apiError(w, "Wrong auth", 401) + apiError(w, "Wrong auth", http.StatusUnauthorized) return false } func handlerGetPing(w http.ResponseWriter, r *http.Request) { - writeResponse(w, true, 200) + writeResponse(w, true, http.StatusOK) } func handlerGetImage(w http.ResponseWriter, r *http.Request) { @@ -323,17 +322,17 @@ func handlerPutImage(w http.ResponseWriter, r *http.Request) { } if checksum := r.Header.Get("X-Docker-Checksum"); checksum != "" { if checksum != layer["checksum_simple"] && checksum != layer["checksum_tarsum"] { - apiError(w, "Wrong checksum", 400) + apiError(w, "Wrong checksum", http.StatusBadRequest) return } } - body, err := ioutil.ReadAll(r.Body) + body, err := io.ReadAll(r.Body) if err != nil { - apiError(w, fmt.Sprintf("Error: %s", err), 500) + apiError(w, fmt.Sprintf("Error: %s", err), http.StatusInternalServerError) return } layer[action] = string(body) - writeResponse(w, true, 200) + writeResponse(w, true, http.StatusOK) } func handlerGetDeleteTags(w http.ResponseWriter, r *http.Request) { @@ -342,20 +341,20 @@ func handlerGetDeleteTags(w http.ResponseWriter, r *http.Request) { } repositoryName, err := reference.WithName(mux.Vars(r)["repository"]) if err != nil { - apiError(w, "Could not parse repository", 400) + apiError(w, "Could not parse repository", http.StatusBadRequest) return } tags, exists := testRepositories[repositoryName.String()] if !exists { - apiError(w, "Repository not found", 404) + apiError(w, "Repository not found", http.StatusNotFound) return } - if r.Method == "DELETE" { + if r.Method == http.MethodDelete { delete(testRepositories, repositoryName.String()) - writeResponse(w, true, 200) + writeResponse(w, true, http.StatusOK) return } - writeResponse(w, tags, 200) + writeResponse(w, tags, http.StatusOK) } func handlerGetTag(w http.ResponseWriter, r *http.Request) { @@ -365,21 +364,21 @@ func handlerGetTag(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) repositoryName, err := reference.WithName(vars["repository"]) if err != nil { - apiError(w, "Could not parse repository", 400) + apiError(w, "Could not parse repository", http.StatusBadRequest) return } tagName := vars["tag"] tags, exists := testRepositories[repositoryName.String()] if !exists { - apiError(w, "Repository not found", 404) + apiError(w, "Repository not found", http.StatusNotFound) return } tag, exists := tags[tagName] if !exists { - apiError(w, "Tag not found", 404) + apiError(w, "Tag not found", http.StatusNotFound) return } - writeResponse(w, tag, 200) + writeResponse(w, tag, http.StatusOK) } func handlerPutTag(w http.ResponseWriter, r *http.Request) { @@ -389,7 +388,7 @@ func handlerPutTag(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) repositoryName, err := reference.WithName(vars["repository"]) if err != nil { - apiError(w, "Could not parse repository", 400) + apiError(w, "Could not parse repository", http.StatusBadRequest) return } tagName := vars["tag"] @@ -401,15 +400,15 @@ func handlerPutTag(w http.ResponseWriter, r *http.Request) { tagValue := "" readJSON(r, tagValue) tags[tagName] = tagValue - writeResponse(w, true, 200) + writeResponse(w, true, http.StatusOK) } func handlerUsers(w http.ResponseWriter, r *http.Request) { - code := 200 - if r.Method == "POST" { - code = 201 - } else if r.Method == "PUT" { - code = 204 + code := http.StatusOK + if r.Method == http.MethodPost { + code = http.StatusCreated + } else if r.Method == http.MethodPut { + code = http.StatusNoContent } writeResponse(w, "", code) } @@ -418,16 +417,16 @@ func handlerImages(w http.ResponseWriter, r *http.Request) { u, _ := url.Parse(testHTTPServer.URL) w.Header().Add("X-Docker-Endpoints", fmt.Sprintf("%s , %s ", u.Host, "test.example.com")) w.Header().Add("X-Docker-Token", fmt.Sprintf("FAKE-SESSION-%d", time.Now().UnixNano())) - if r.Method == "PUT" { + if r.Method == http.MethodPut { if strings.HasSuffix(r.URL.Path, "images") { - writeResponse(w, "", 204) + writeResponse(w, "", http.StatusNoContent) return } - writeResponse(w, "", 200) + writeResponse(w, "", http.StatusOK) return } - if r.Method == "DELETE" { - writeResponse(w, "", 204) + if r.Method == http.MethodDelete { + writeResponse(w, "", http.StatusNoContent) return } var images []map[string]string @@ -438,11 +437,11 @@ func handlerImages(w http.ResponseWriter, r *http.Request) { image["Tag"] = "latest" images = append(images, image) } - writeResponse(w, images, 200) + writeResponse(w, images, http.StatusOK) } func handlerAuth(w http.ResponseWriter, r *http.Request) { - writeResponse(w, "OK", 200) + writeResponse(w, "OK", http.StatusOK) } func handlerSearch(w http.ResponseWriter, r *http.Request) { @@ -451,7 +450,7 @@ func handlerSearch(w http.ResponseWriter, r *http.Request) { NumResults: 1, Results: []registrytypes.SearchResult{{Name: "fakeimage", StarCount: 42}}, } - writeResponse(w, result, 200) + writeResponse(w, result, http.StatusOK) } func TestPing(t *testing.T) { @@ -459,7 +458,7 @@ func TestPing(t *testing.T) { if err != nil { t.Fatal(err) } - assertEqual(t, res.StatusCode, 200, "") + assertEqual(t, res.StatusCode, http.StatusOK, "") assertEqual(t, res.Header.Get("X-Docker-Registry-Config"), "mock", "This is not a Mocked Registry") } diff --git a/registry/registry_test.go b/registry/registry_test.go index b7459471b3f64..03ac260d8df93 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -1,10 +1,8 @@ package registry // import "github.com/docker/docker/registry" import ( - "fmt" "net/http" "net/http/httputil" - "net/url" "os" "strings" "testing" @@ -13,17 +11,8 @@ import ( "github.com/docker/distribution/registry/client/transport" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" - "gotest.tools/assert" - "gotest.tools/skip" -) - -var ( - token = []string{"fake-token"} -) - -const ( - imageID = "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d" - REPO = "foo42/bar" + "gotest.tools/v3/assert" + "gotest.tools/v3/skip" ) func spawnTestRegistrySession(t *testing.T) *Session { @@ -50,7 +39,7 @@ func spawnTestRegistrySession(t *testing.T) *Session { // Because we know that the client's transport is an `*authTransport` we simply cast it, // in order to set the internal cached token to the fake token, and thus send that fake token // upon every subsequent requests. - r.client.Transport.(*authTransport).token = token + r.client.Transport.(*authTransport).token = []string{"fake-token"} return r } @@ -148,153 +137,6 @@ func TestEndpoint(t *testing.T) { } } -func TestGetRemoteHistory(t *testing.T) { - r := spawnTestRegistrySession(t) - hist, err := r.GetRemoteHistory(imageID, makeURL("/v1/")) - if err != nil { - t.Fatal(err) - } - assertEqual(t, len(hist), 2, "Expected 2 images in history") - assertEqual(t, hist[0], imageID, "Expected "+imageID+"as first ancestry") - assertEqual(t, hist[1], "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20", - "Unexpected second ancestry") -} - -func TestLookupRemoteImage(t *testing.T) { - r := spawnTestRegistrySession(t) - err := r.LookupRemoteImage(imageID, makeURL("/v1/")) - assertEqual(t, err, nil, "Expected error of remote lookup to nil") - if err := r.LookupRemoteImage("abcdef", makeURL("/v1/")); err == nil { - t.Fatal("Expected error of remote lookup to not nil") - } -} - -func TestGetRemoteImageJSON(t *testing.T) { - r := spawnTestRegistrySession(t) - json, size, err := r.GetRemoteImageJSON(imageID, makeURL("/v1/")) - if err != nil { - t.Fatal(err) - } - assertEqual(t, size, int64(154), "Expected size 154") - if len(json) == 0 { - t.Fatal("Expected non-empty json") - } - - _, _, err = r.GetRemoteImageJSON("abcdef", makeURL("/v1/")) - if err == nil { - t.Fatal("Expected image not found error") - } -} - -func TestGetRemoteImageLayer(t *testing.T) { - r := spawnTestRegistrySession(t) - data, err := r.GetRemoteImageLayer(imageID, makeURL("/v1/"), 0) - if err != nil { - t.Fatal(err) - } - if data == nil { - t.Fatal("Expected non-nil data result") - } - - _, err = r.GetRemoteImageLayer("abcdef", makeURL("/v1/"), 0) - if err == nil { - t.Fatal("Expected image not found error") - } -} - -func TestGetRemoteTag(t *testing.T) { - r := spawnTestRegistrySession(t) - repoRef, err := reference.ParseNormalizedNamed(REPO) - if err != nil { - t.Fatal(err) - } - tag, err := r.GetRemoteTag([]string{makeURL("/v1/")}, repoRef, "test") - if err != nil { - t.Fatal(err) - } - assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID) - - bazRef, err := reference.ParseNormalizedNamed("foo42/baz") - if err != nil { - t.Fatal(err) - } - _, err = r.GetRemoteTag([]string{makeURL("/v1/")}, bazRef, "foo") - if err != ErrRepoNotFound { - t.Fatal("Expected ErrRepoNotFound error when fetching tag for bogus repo") - } -} - -func TestGetRemoteTags(t *testing.T) { - r := spawnTestRegistrySession(t) - repoRef, err := reference.ParseNormalizedNamed(REPO) - if err != nil { - t.Fatal(err) - } - tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, repoRef) - if err != nil { - t.Fatal(err) - } - assertEqual(t, len(tags), 2, "Expected two tags") - assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID) - assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID) - - bazRef, err := reference.ParseNormalizedNamed("foo42/baz") - if err != nil { - t.Fatal(err) - } - _, err = r.GetRemoteTags([]string{makeURL("/v1/")}, bazRef) - if err != ErrRepoNotFound { - t.Fatal("Expected ErrRepoNotFound error when fetching tags for bogus repo") - } -} - -func TestGetRepositoryData(t *testing.T) { - r := spawnTestRegistrySession(t) - parsedURL, err := url.Parse(makeURL("/v1/")) - if err != nil { - t.Fatal(err) - } - host := "http://" + parsedURL.Host + "/v1/" - repoRef, err := reference.ParseNormalizedNamed(REPO) - if err != nil { - t.Fatal(err) - } - data, err := r.GetRepositoryData(repoRef) - if err != nil { - t.Fatal(err) - } - assertEqual(t, len(data.ImgList), 2, "Expected 2 images in ImgList") - assertEqual(t, len(data.Endpoints), 2, - fmt.Sprintf("Expected 2 endpoints in Endpoints, found %d instead", len(data.Endpoints))) - assertEqual(t, data.Endpoints[0], host, - fmt.Sprintf("Expected first endpoint to be %s but found %s instead", host, data.Endpoints[0])) - assertEqual(t, data.Endpoints[1], "http://test.example.com/v1/", - fmt.Sprintf("Expected first endpoint to be http://test.example.com/v1/ but found %s instead", data.Endpoints[1])) - -} - -func TestPushImageJSONRegistry(t *testing.T) { - r := spawnTestRegistrySession(t) - imgData := &ImgData{ - ID: "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20", - Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37", - } - - err := r.PushImageJSONRegistry(imgData, []byte{0x42, 0xdf, 0x0}, makeURL("/v1/")) - if err != nil { - t.Fatal(err) - } -} - -func TestPushImageLayerRegistry(t *testing.T) { - r := spawnTestRegistrySession(t) - layer := strings.NewReader("") - _, _, err := r.PushImageLayerRegistry(imageID, layer, makeURL("/v1/"), []byte{}) - if err != nil { - t.Fatal(err) - } -} - func TestParseRepositoryInfo(t *testing.T) { type staticRepositoryInfo struct { Index *registrytypes.IndexInfo @@ -701,50 +543,6 @@ func TestMirrorEndpointLookup(t *testing.T) { } } -func TestPushRegistryTag(t *testing.T) { - r := spawnTestRegistrySession(t) - repoRef, err := reference.ParseNormalizedNamed(REPO) - if err != nil { - t.Fatal(err) - } - err = r.PushRegistryTag(repoRef, imageID, "stable", makeURL("/v1/")) - if err != nil { - t.Fatal(err) - } -} - -func TestPushImageJSONIndex(t *testing.T) { - r := spawnTestRegistrySession(t) - imgData := []*ImgData{ - { - ID: "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20", - Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37", - }, - { - ID: "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d", - Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2", - }, - } - repoRef, err := reference.ParseNormalizedNamed(REPO) - if err != nil { - t.Fatal(err) - } - repoData, err := r.PushImageJSONIndex(repoRef, imgData, false, nil) - if err != nil { - t.Fatal(err) - } - if repoData == nil { - t.Fatal("Expected RepositoryData object") - } - repoData, err = r.PushImageJSONIndex(repoRef, imgData, true, []string{r.indexEndpoint.String()}) - if err != nil { - t.Fatal(err) - } - if repoData == nil { - t.Fatal("Expected RepositoryData object") - } -} - func TestSearchRepositories(t *testing.T) { r := spawnTestRegistrySession(t) results, err := r.SearchRepositories("fakequery", 25) @@ -761,12 +559,12 @@ func TestSearchRepositories(t *testing.T) { func TestTrustedLocation(t *testing.T) { for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} { - req, _ := http.NewRequest("GET", url, nil) + req, _ := http.NewRequest(http.MethodGet, url, nil) assert.Check(t, !trustedLocation(req)) } for _, url := range []string{"https://docker.io", "https://test.docker.com:80"} { - req, _ := http.NewRequest("GET", url, nil) + req, _ := http.NewRequest(http.MethodGet, url, nil) assert.Check(t, trustedLocation(req)) } } @@ -777,10 +575,10 @@ func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) { {"https://foo.docker.io:7777", "http://bar.docker.com"}, {"https://foo.docker.io", "https://example.com"}, } { - reqFrom, _ := http.NewRequest("GET", urls[0], nil) + reqFrom, _ := http.NewRequest(http.MethodGet, urls[0], nil) reqFrom.Header.Add("Content-Type", "application/json") reqFrom.Header.Add("Authorization", "super_secret") - reqTo, _ := http.NewRequest("GET", urls[1], nil) + reqTo, _ := http.NewRequest(http.MethodGet, urls[1], nil) addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) @@ -801,10 +599,10 @@ func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) { {"https://docker.io", "https://docker.com"}, {"https://foo.docker.io:7777", "https://bar.docker.com"}, } { - reqFrom, _ := http.NewRequest("GET", urls[0], nil) + reqFrom, _ := http.NewRequest(http.MethodGet, urls[0], nil) reqFrom.Header.Add("Content-Type", "application/json") reqFrom.Header.Add("Authorization", "super_secret") - reqTo, _ := http.NewRequest("GET", urls[1], nil) + reqTo, _ := http.NewRequest(http.MethodGet, urls[1], nil) addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) @@ -849,10 +647,10 @@ func TestAllowNondistributableArtifacts(t *testing.T) { {"example.com:5000", []string{"42.42.42.42/8"}, true}, {"127.0.0.1:5000", []string{"127.0.0.0/8"}, true}, {"42.42.42.42:5000", []string{"42.1.1.1/8"}, true}, - {"invalid.domain.com", []string{"42.42.0.0/16"}, false}, - {"invalid.domain.com", []string{"invalid.domain.com"}, true}, - {"invalid.domain.com:5000", []string{"invalid.domain.com"}, false}, - {"invalid.domain.com:5000", []string{"invalid.domain.com:5000"}, true}, + {"invalid.example.com", []string{"42.42.0.0/16"}, false}, + {"invalid.example.com", []string{"invalid.example.com"}, true}, + {"invalid.example.com:5000", []string{"invalid.example.com"}, false}, + {"invalid.example.com:5000", []string{"invalid.example.com:5000"}, true}, } for _, tt := range tests { config, err := newServiceConfig(ServiceOptions{ @@ -894,10 +692,10 @@ func TestIsSecureIndex(t *testing.T) { {"example.com:5000", []string{"42.42.42.42/8"}, false}, {"127.0.0.1:5000", []string{"127.0.0.0/8"}, false}, {"42.42.42.42:5000", []string{"42.1.1.1/8"}, false}, - {"invalid.domain.com", []string{"42.42.0.0/16"}, true}, - {"invalid.domain.com", []string{"invalid.domain.com"}, false}, - {"invalid.domain.com:5000", []string{"invalid.domain.com"}, true}, - {"invalid.domain.com:5000", []string{"invalid.domain.com:5000"}, false}, + {"invalid.example.com", []string{"42.42.0.0/16"}, true}, + {"invalid.example.com", []string{"invalid.example.com"}, false}, + {"invalid.example.com:5000", []string{"invalid.example.com"}, true}, + {"invalid.example.com:5000", []string{"invalid.example.com:5000"}, false}, } for _, tt := range tests { config, err := makeServiceConfig(nil, tt.insecureRegistries) diff --git a/registry/resumable/resumablerequestreader.go b/registry/resumable/resumablerequestreader.go index 8e97a1a4d1499..3649f36edecce 100644 --- a/registry/resumable/resumablerequestreader.go +++ b/registry/resumable/resumablerequestreader.go @@ -56,10 +56,10 @@ func (r *requestReader) Read(p []byte) (n int, err error) { r.cleanUpResponse() return 0, err } - if r.currentResponse.StatusCode == 416 && r.lastRange == r.totalSize && r.currentResponse.ContentLength == 0 { + if r.currentResponse.StatusCode == http.StatusRequestedRangeNotSatisfiable && r.lastRange == r.totalSize && r.currentResponse.ContentLength == 0 { r.cleanUpResponse() return 0, io.EOF - } else if r.currentResponse.StatusCode != 206 && r.lastRange != 0 && isFreshRequest { + } else if r.currentResponse.StatusCode != http.StatusPartialContent && r.lastRange != 0 && isFreshRequest { r.cleanUpResponse() return 0, fmt.Errorf("the server doesn't support byte ranges") } diff --git a/registry/resumable/resumablerequestreader_test.go b/registry/resumable/resumablerequestreader_test.go index c72c210e776e2..326307ff41efb 100644 --- a/registry/resumable/resumablerequestreader_test.go +++ b/registry/resumable/resumablerequestreader_test.go @@ -3,15 +3,14 @@ package resumable // import "github.com/docker/docker/registry/resumable" import ( "fmt" "io" - "io/ioutil" "net/http" "net/http/httptest" "strings" "testing" "time" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func TestResumableRequestHeaderSimpleErrors(t *testing.T) { @@ -23,7 +22,7 @@ func TestResumableRequestHeaderSimpleErrors(t *testing.T) { client := &http.Client{} var req *http.Request - req, err := http.NewRequest("GET", ts.URL, nil) + req, err := http.NewRequest(http.MethodGet, ts.URL, nil) assert.NilError(t, err) resreq := &requestReader{} @@ -44,7 +43,7 @@ func TestResumableRequestHeaderNotTooMuchFailures(t *testing.T) { client := &http.Client{} var badReq *http.Request - badReq, err := http.NewRequest("GET", "I'm not an url", nil) + badReq, err := http.NewRequest(http.MethodGet, "I'm not an url", nil) assert.NilError(t, err) resreq := &requestReader{ @@ -64,7 +63,7 @@ func TestResumableRequestHeaderTooMuchFailures(t *testing.T) { client := &http.Client{} var badReq *http.Request - badReq, err := http.NewRequest("GET", "I'm not an url", nil) + badReq, err := http.NewRequest(http.MethodGet, "I'm not an url", nil) assert.NilError(t, err) resreq := &requestReader{ @@ -75,9 +74,10 @@ func TestResumableRequestHeaderTooMuchFailures(t *testing.T) { } defer resreq.Close() - expectedError := `Get I%27m%20not%20an%20url: unsupported protocol scheme ""` read, err := resreq.Read([]byte{}) - assert.Check(t, is.Error(err, expectedError)) + assert.Assert(t, err != nil) + assert.Check(t, is.ErrorContains(err, "unsupported protocol scheme")) + assert.Check(t, is.ErrorContains(err, "I%27m%20not%20an%20url")) assert.Check(t, is.Equal(0, read)) } @@ -92,14 +92,14 @@ func (errorReaderCloser) Read(p []byte) (n int, err error) { // If an unknown error is encountered, return 0, nil and log it func TestResumableRequestReaderWithReadError(t *testing.T) { var req *http.Request - req, err := http.NewRequest("GET", "", nil) + req, err := http.NewRequest(http.MethodGet, "", nil) assert.NilError(t, err) client := &http.Client{} response := &http.Response{ Status: "500 Internal Server", - StatusCode: 500, + StatusCode: http.StatusInternalServerError, ContentLength: 0, Close: true, Body: errorReaderCloser{}, @@ -123,17 +123,17 @@ func TestResumableRequestReaderWithReadError(t *testing.T) { func TestResumableRequestReaderWithEOFWith416Response(t *testing.T) { var req *http.Request - req, err := http.NewRequest("GET", "", nil) + req, err := http.NewRequest(http.MethodGet, "", nil) assert.NilError(t, err) client := &http.Client{} response := &http.Response{ Status: "416 Requested Range Not Satisfiable", - StatusCode: 416, + StatusCode: http.StatusRequestedRangeNotSatisfiable, ContentLength: 0, Close: true, - Body: ioutil.NopCloser(strings.NewReader("")), + Body: io.NopCloser(strings.NewReader("")), } resreq := &requestReader{ @@ -159,7 +159,7 @@ func TestResumableRequestReaderWithServerDoesntSupportByteRanges(t *testing.T) { defer ts.Close() var req *http.Request - req, err := http.NewRequest("GET", ts.URL, nil) + req, err := http.NewRequest(http.MethodGet, ts.URL, nil) assert.NilError(t, err) client := &http.Client{} @@ -185,7 +185,7 @@ func TestResumableRequestReaderWithZeroTotalSize(t *testing.T) { defer ts.Close() var req *http.Request - req, err := http.NewRequest("GET", ts.URL, nil) + req, err := http.NewRequest(http.MethodGet, ts.URL, nil) assert.NilError(t, err) client := &http.Client{} @@ -194,7 +194,7 @@ func TestResumableRequestReaderWithZeroTotalSize(t *testing.T) { resreq := NewRequestReader(client, req, retries, 0) defer resreq.Close() - data, err := ioutil.ReadAll(resreq) + data, err := io.ReadAll(resreq) assert.NilError(t, err) resstr := strings.TrimSuffix(string(data), "\n") @@ -210,7 +210,7 @@ func TestResumableRequestReader(t *testing.T) { defer ts.Close() var req *http.Request - req, err := http.NewRequest("GET", ts.URL, nil) + req, err := http.NewRequest(http.MethodGet, ts.URL, nil) assert.NilError(t, err) client := &http.Client{} @@ -220,7 +220,7 @@ func TestResumableRequestReader(t *testing.T) { resreq := NewRequestReader(client, req, retries, imgSize) defer resreq.Close() - data, err := ioutil.ReadAll(resreq) + data, err := io.ReadAll(resreq) assert.NilError(t, err) resstr := strings.TrimSuffix(string(data), "\n") @@ -236,7 +236,7 @@ func TestResumableRequestReaderWithInitialResponse(t *testing.T) { defer ts.Close() var req *http.Request - req, err := http.NewRequest("GET", ts.URL, nil) + req, err := http.NewRequest(http.MethodGet, ts.URL, nil) assert.NilError(t, err) client := &http.Client{} @@ -249,7 +249,7 @@ func TestResumableRequestReaderWithInitialResponse(t *testing.T) { resreq := NewRequestReaderWithInitialResponse(client, req, retries, imgSize, res) defer resreq.Close() - data, err := ioutil.ReadAll(resreq) + data, err := io.ReadAll(resreq) assert.NilError(t, err) resstr := strings.TrimSuffix(string(data), "\n") diff --git a/registry/service.go b/registry/service.go index b441970ff1709..276b04724ab27 100644 --- a/registry/service.go +++ b/registry/service.go @@ -108,40 +108,38 @@ func (s *DefaultService) LoadInsecureRegistries(registries []string) error { // It can be used to verify the validity of a client's credentials. func (s *DefaultService) Auth(ctx context.Context, authConfig *types.AuthConfig, userAgent string) (status, token string, err error) { // TODO Use ctx when searching for repositories - serverAddress := authConfig.ServerAddress - if serverAddress == "" { - serverAddress = IndexServer - } - if !strings.HasPrefix(serverAddress, "https://") && !strings.HasPrefix(serverAddress, "http://") { - serverAddress = "https://" + serverAddress - } - u, err := url.Parse(serverAddress) - if err != nil { - return "", "", errdefs.InvalidParameter(errors.Errorf("unable to parse server address: %v", err)) + var registryHostName = IndexHostname + + if authConfig.ServerAddress != "" { + serverAddress := authConfig.ServerAddress + if !strings.HasPrefix(serverAddress, "https://") && !strings.HasPrefix(serverAddress, "http://") { + serverAddress = "https://" + serverAddress + } + u, err := url.Parse(serverAddress) + if err != nil { + return "", "", errdefs.InvalidParameter(errors.Errorf("unable to parse server address: %v", err)) + } + registryHostName = u.Host } - endpoints, err := s.LookupPushEndpoints(u.Host) + // Lookup endpoints for authentication using "LookupPushEndpoints", which + // excludes mirrors to prevent sending credentials of the upstream registry + // to a mirror. + endpoints, err := s.LookupPushEndpoints(registryHostName) if err != nil { return "", "", errdefs.InvalidParameter(err) } for _, endpoint := range endpoints { - login := loginV2 - if endpoint.Version == APIVersion1 { - login = loginV1 - } - - status, token, err = login(authConfig, endpoint, userAgent) + status, token, err = loginV2(authConfig, endpoint, userAgent) if err == nil { return } - if fErr, ok := err.(fallbackError); ok { - err = fErr.err - logrus.Infof("Error logging in to %s endpoint, trying next endpoint: %v", endpoint.Version, err) - continue + if errdefs.IsUnauthorized(err) { + // Failed to authenticate; don't continue with (non-TLS) endpoints. + return status, token, err } - - return "", "", err + logrus.WithError(err).Infof("Error logging in to endpoint, trying next endpoint") } return "", "", err @@ -150,18 +148,13 @@ func (s *DefaultService) Auth(ctx context.Context, authConfig *types.AuthConfig, // splitReposSearchTerm breaks a search term into an index name and remote name func splitReposSearchTerm(reposName string) (string, string) { nameParts := strings.SplitN(reposName, "/", 2) - var indexName, remoteName string if len(nameParts) == 1 || (!strings.Contains(nameParts[0], ".") && !strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") { - // This is a Docker Index repos (ex: samalba/hipache or ubuntu) - // 'docker.io' - indexName = IndexName - remoteName = reposName - } else { - indexName = nameParts[0] - remoteName = nameParts[1] + // This is a Docker Hub repository (ex: samalba/hipache or ubuntu), + // use the default Docker Hub registry (docker.io) + return IndexName, reposName } - return indexName, remoteName + return nameParts[0], nameParts[1] } // Search queries the public registry for images matching the specified @@ -184,7 +177,7 @@ func (s *DefaultService) Search(ctx context.Context, term string, limit int, aut } // *TODO: Search multiple indexes. - endpoint, err := NewV1Endpoint(index, userAgent, http.Header(headers)) + endpoint, err := NewV1Endpoint(index, userAgent, headers) if err != nil { return nil, err } @@ -200,14 +193,14 @@ func (s *DefaultService) Search(ctx context.Context, term string, limit int, aut } modifiers := Headers(userAgent, nil) - v2Client, foundV2, err := v2AuthHTTPClient(endpoint.URL, endpoint.client.Transport, modifiers, creds, scopes) + v2Client, err := v2AuthHTTPClient(endpoint.URL, endpoint.client.Transport, modifiers, creds, scopes) if err != nil { if fErr, ok := err.(fallbackError); ok { logrus.Errorf("Cannot use identity token for search, v2 auth not supported: %v", fErr.err) } else { return nil, err } - } else if foundV2 { + } else { // Copy non transport http client features v2Client.Timeout = endpoint.client.Timeout v2Client.CheckRedirect = endpoint.client.CheckRedirect @@ -228,13 +221,8 @@ func (s *DefaultService) Search(ctx context.Context, term string, limit int, aut r := newSession(client, authConfig, endpoint) if index.Official { - localName := remoteName - if strings.HasPrefix(localName, "library/") { - // If pull "library/foo", it's stored locally under "foo" - localName = strings.SplitN(localName, "/", 2)[1] - } - - return r.SearchRepositories(localName, limit) + // If pull "library/foo", it's stored locally under "foo" + remoteName = strings.TrimPrefix(remoteName, "library/") } return r.SearchRepositories(remoteName, limit) } @@ -258,17 +246,12 @@ type APIEndpoint struct { TLSConfig *tls.Config } -// ToV1Endpoint returns a V1 API endpoint based on the APIEndpoint -func (e APIEndpoint) ToV1Endpoint(userAgent string, metaHeaders http.Header) *V1Endpoint { - return newV1Endpoint(*e.URL, e.TLSConfig, userAgent, metaHeaders) -} - // TLSConfig constructs a client TLS configuration based on server defaults func (s *DefaultService) TLSConfig(hostname string) (*tls.Config, error) { s.mu.Lock() defer s.mu.Unlock() - return newTLSConfig(hostname, isSecureIndex(s.config, hostname)) + return s.tlsConfig(hostname) } // tlsConfig constructs a client TLS configuration based on server defaults @@ -276,28 +259,22 @@ func (s *DefaultService) tlsConfig(hostname string) (*tls.Config, error) { return newTLSConfig(hostname, isSecureIndex(s.config, hostname)) } -func (s *DefaultService) tlsConfigForMirror(mirrorURL *url.URL) (*tls.Config, error) { - return s.tlsConfig(mirrorURL.Host) -} - -// LookupPullEndpoints creates a list of endpoints to try to pull from, in order of preference. -// It gives preference to v2 endpoints over v1, mirrors over the actual -// registry, and HTTPS over plain HTTP. +// LookupPullEndpoints creates a list of v2 endpoints to try to pull from, in order of preference. +// It gives preference to mirrors over the actual registry, and HTTPS over plain HTTP. func (s *DefaultService) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) { s.mu.Lock() defer s.mu.Unlock() - return s.lookupEndpoints(hostname) + return s.lookupV2Endpoints(hostname) } -// LookupPushEndpoints creates a list of endpoints to try to push to, in order of preference. -// It gives preference to v2 endpoints over v1, and HTTPS over plain HTTP. -// Mirrors are not included. +// LookupPushEndpoints creates a list of v2 endpoints to try to push to, in order of preference. +// It gives preference to HTTPS over plain HTTP. Mirrors are not included. func (s *DefaultService) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) { s.mu.Lock() defer s.mu.Unlock() - allEndpoints, err := s.lookupEndpoints(hostname) + allEndpoints, err := s.lookupV2Endpoints(hostname) if err == nil { for _, endpoint := range allEndpoints { if !endpoint.Mirror { @@ -307,22 +284,3 @@ func (s *DefaultService) LookupPushEndpoints(hostname string) (endpoints []APIEn } return endpoints, err } - -func (s *DefaultService) lookupEndpoints(hostname string) (endpoints []APIEndpoint, err error) { - endpoints, err = s.lookupV2Endpoints(hostname) - if err != nil { - return nil, err - } - - if s.config.V2Only { - return endpoints, nil - } - - legacyEndpoints, err := s.lookupV1Endpoints(hostname) - if err != nil { - return nil, err - } - endpoints = append(endpoints, legacyEndpoints...) - - return endpoints, nil -} diff --git a/registry/service_v1.go b/registry/service_v1.go deleted file mode 100644 index d955ec51fbf82..0000000000000 --- a/registry/service_v1.go +++ /dev/null @@ -1,40 +0,0 @@ -package registry // import "github.com/docker/docker/registry" - -import "net/url" - -func (s *DefaultService) lookupV1Endpoints(hostname string) (endpoints []APIEndpoint, err error) { - if hostname == DefaultNamespace || hostname == DefaultV2Registry.Host || hostname == IndexHostname { - return []APIEndpoint{}, nil - } - - tlsConfig, err := s.tlsConfig(hostname) - if err != nil { - return nil, err - } - - endpoints = []APIEndpoint{ - { - URL: &url.URL{ - Scheme: "https", - Host: hostname, - }, - Version: APIVersion1, - TrimHostname: true, - TLSConfig: tlsConfig, - }, - } - - if tlsConfig.InsecureSkipVerify { - endpoints = append(endpoints, APIEndpoint{ // or this - URL: &url.URL{ - Scheme: "http", - Host: hostname, - }, - Version: APIVersion1, - TrimHostname: true, - // used to check if supposed to be secure via InsecureSkipVerify - TLSConfig: tlsConfig, - }) - } - return endpoints, nil -} diff --git a/registry/service_v1_test.go b/registry/service_v1_test.go deleted file mode 100644 index 11861f7c05296..0000000000000 --- a/registry/service_v1_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package registry // import "github.com/docker/docker/registry" - -import ( - "os" - "testing" - - "gotest.tools/skip" -) - -func TestLookupV1Endpoints(t *testing.T) { - skip.If(t, os.Getuid() != 0, "skipping test that requires root") - s, err := NewService(ServiceOptions{}) - if err != nil { - t.Fatal(err) - } - - cases := []struct { - hostname string - expectedLen int - }{ - {"example.com", 1}, - {DefaultNamespace, 0}, - {DefaultV2Registry.Host, 0}, - {IndexHostname, 0}, - } - - for _, c := range cases { - if ret, err := s.lookupV1Endpoints(c.hostname); err != nil || len(ret) != c.expectedLen { - t.Errorf("lookupV1Endpoints(`"+c.hostname+"`) returned %+v and %+v", ret, err) - } - } -} diff --git a/registry/service_v2.go b/registry/service_v2.go index 1a4c9e310547b..46f28ebccfdf1 100644 --- a/registry/service_v2.go +++ b/registry/service_v2.go @@ -10,7 +10,6 @@ import ( func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) { tlsConfig := tlsconfig.ServerDefault() if hostname == DefaultNamespace || hostname == IndexHostname { - // v2 mirrors for _, mirror := range s.config.Mirrors { if !strings.HasPrefix(mirror, "http://") && !strings.HasPrefix(mirror, "https://") { mirror = "https://" + mirror @@ -19,20 +18,18 @@ func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp if err != nil { return nil, err } - mirrorTLSConfig, err := s.tlsConfigForMirror(mirrorURL) + mirrorTLSConfig, err := s.tlsConfig(mirrorURL.Host) if err != nil { return nil, err } endpoints = append(endpoints, APIEndpoint{ - URL: mirrorURL, - // guess mirrors are v2 + URL: mirrorURL, Version: APIVersion2, Mirror: true, TrimHostname: true, TLSConfig: mirrorTLSConfig, }) } - // v2 registry endpoints = append(endpoints, APIEndpoint{ URL: DefaultV2Registry, Version: APIVersion2, diff --git a/registry/session.go b/registry/session.go index ef142995942f7..d34dc1e58ab45 100644 --- a/registry/session.go +++ b/registry/session.go @@ -1,42 +1,26 @@ package registry // import "github.com/docker/docker/registry" import ( - "bytes" - "crypto/sha256" // this is required for some certificates _ "crypto/sha512" - "encoding/hex" "encoding/json" "fmt" - "io" - "io/ioutil" "net/http" "net/http/cookiejar" "net/url" - "strconv" "strings" "sync" - "github.com/docker/distribution/reference" - "github.com/docker/distribution/registry/api/errcode" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/errdefs" "github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/stringid" - "github.com/docker/docker/pkg/tarsum" - "github.com/docker/docker/registry/resumable" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -var ( - // ErrRepoNotFound is returned if the repository didn't exist on the - // remote side - ErrRepoNotFound notFoundError = "Repository not found" -) - // A Session is used to communicate with a V1 registry type Session struct { indexEndpoint *V1Endpoint @@ -132,7 +116,9 @@ func (tr *authTransport) RoundTrip(orig *http.Request) (*http.Response, error) { } resp, err := tr.RoundTripper.RoundTrip(req) if err != nil { + tr.mu.Lock() delete(tr.modReq, orig) + tr.mu.Unlock() return nil, err } if len(resp.Header["X-Docker-Token"]) > 0 { @@ -211,527 +197,6 @@ func NewSession(client *http.Client, authConfig *types.AuthConfig, endpoint *V1E return newSession(client, authConfig, endpoint), nil } -// ID returns this registry session's ID. -func (r *Session) ID() string { - return r.id -} - -// GetRemoteHistory retrieves the history of a given image from the registry. -// It returns a list of the parent's JSON files (including the requested image). -func (r *Session) GetRemoteHistory(imgID, registry string) ([]string, error) { - res, err := r.client.Get(registry + "images/" + imgID + "/ancestry") - if err != nil { - return nil, err - } - defer res.Body.Close() - if res.StatusCode != 200 { - if res.StatusCode == 401 { - return nil, errcode.ErrorCodeUnauthorized.WithArgs() - } - return nil, newJSONError(fmt.Sprintf("Server error: %d trying to fetch remote history for %s", res.StatusCode, imgID), res) - } - - var history []string - if err := json.NewDecoder(res.Body).Decode(&history); err != nil { - return nil, fmt.Errorf("Error while reading the http response: %v", err) - } - - logrus.Debugf("Ancestry: %v", history) - return history, nil -} - -// LookupRemoteImage checks if an image exists in the registry -func (r *Session) LookupRemoteImage(imgID, registry string) error { - res, err := r.client.Get(registry + "images/" + imgID + "/json") - if err != nil { - return err - } - res.Body.Close() - if res.StatusCode != 200 { - return newJSONError(fmt.Sprintf("HTTP code %d", res.StatusCode), res) - } - return nil -} - -// GetRemoteImageJSON retrieves an image's JSON metadata from the registry. -func (r *Session) GetRemoteImageJSON(imgID, registry string) ([]byte, int64, error) { - res, err := r.client.Get(registry + "images/" + imgID + "/json") - if err != nil { - return nil, -1, fmt.Errorf("Failed to download json: %s", err) - } - defer res.Body.Close() - if res.StatusCode != 200 { - return nil, -1, newJSONError(fmt.Sprintf("HTTP code %d", res.StatusCode), res) - } - // if the size header is not present, then set it to '-1' - imageSize := int64(-1) - if hdr := res.Header.Get("X-Docker-Size"); hdr != "" { - imageSize, err = strconv.ParseInt(hdr, 10, 64) - if err != nil { - return nil, -1, err - } - } - - jsonString, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, -1, fmt.Errorf("Failed to parse downloaded json: %v (%s)", err, jsonString) - } - return jsonString, imageSize, nil -} - -// GetRemoteImageLayer retrieves an image layer from the registry -func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io.ReadCloser, error) { - var ( - statusCode = 0 - res *http.Response - err error - imageURL = fmt.Sprintf("%simages/%s/layer", registry, imgID) - ) - - req, err := http.NewRequest("GET", imageURL, nil) - if err != nil { - return nil, fmt.Errorf("Error while getting from the server: %v", err) - } - - res, err = r.client.Do(req) - if err != nil { - logrus.Debugf("Error contacting registry %s: %v", registry, err) - // the only case err != nil && res != nil is https://golang.org/src/net/http/client.go#L515 - if res != nil { - if res.Body != nil { - res.Body.Close() - } - statusCode = res.StatusCode - } - return nil, fmt.Errorf("Server error: Status %d while fetching image layer (%s)", - statusCode, imgID) - } - - if res.StatusCode != 200 { - res.Body.Close() - return nil, fmt.Errorf("Server error: Status %d while fetching image layer (%s)", - res.StatusCode, imgID) - } - - if res.Header.Get("Accept-Ranges") == "bytes" && imgSize > 0 { - logrus.Debug("server supports resume") - return resumable.NewRequestReaderWithInitialResponse(r.client, req, 5, imgSize, res), nil - } - logrus.Debug("server doesn't support resume") - return res.Body, nil -} - -// GetRemoteTag retrieves the tag named in the askedTag argument from the given -// repository. It queries each of the registries supplied in the registries -// argument, and returns data from the first one that answers the query -// successfully. -func (r *Session) GetRemoteTag(registries []string, repositoryRef reference.Named, askedTag string) (string, error) { - repository := reference.Path(repositoryRef) - - if strings.Count(repository, "/") == 0 { - // This will be removed once the registry supports auto-resolution on - // the "library" namespace - repository = "library/" + repository - } - for _, host := range registries { - endpoint := fmt.Sprintf("%srepositories/%s/tags/%s", host, repository, askedTag) - res, err := r.client.Get(endpoint) - if err != nil { - return "", err - } - - logrus.Debugf("Got status code %d from %s", res.StatusCode, endpoint) - defer res.Body.Close() - - if res.StatusCode == 404 { - return "", ErrRepoNotFound - } - if res.StatusCode != 200 { - continue - } - - var tagID string - if err := json.NewDecoder(res.Body).Decode(&tagID); err != nil { - return "", err - } - return tagID, nil - } - return "", fmt.Errorf("Could not reach any registry endpoint") -} - -// GetRemoteTags retrieves all tags from the given repository. It queries each -// of the registries supplied in the registries argument, and returns data from -// the first one that answers the query successfully. It returns a map with -// tag names as the keys and image IDs as the values. -func (r *Session) GetRemoteTags(registries []string, repositoryRef reference.Named) (map[string]string, error) { - repository := reference.Path(repositoryRef) - - if strings.Count(repository, "/") == 0 { - // This will be removed once the registry supports auto-resolution on - // the "library" namespace - repository = "library/" + repository - } - for _, host := range registries { - endpoint := fmt.Sprintf("%srepositories/%s/tags", host, repository) - res, err := r.client.Get(endpoint) - if err != nil { - return nil, err - } - - logrus.Debugf("Got status code %d from %s", res.StatusCode, endpoint) - defer res.Body.Close() - - if res.StatusCode == 404 { - return nil, ErrRepoNotFound - } - if res.StatusCode != 200 { - continue - } - - result := make(map[string]string) - if err := json.NewDecoder(res.Body).Decode(&result); err != nil { - return nil, err - } - return result, nil - } - return nil, fmt.Errorf("Could not reach any registry endpoint") -} - -func buildEndpointsList(headers []string, indexEp string) ([]string, error) { - var endpoints []string - parsedURL, err := url.Parse(indexEp) - if err != nil { - return nil, err - } - var urlScheme = parsedURL.Scheme - // The registry's URL scheme has to match the Index' - for _, ep := range headers { - epList := strings.Split(ep, ",") - for _, epListElement := range epList { - endpoints = append( - endpoints, - fmt.Sprintf("%s://%s/v1/", urlScheme, strings.TrimSpace(epListElement))) - } - } - return endpoints, nil -} - -// GetRepositoryData returns lists of images and endpoints for the repository -func (r *Session) GetRepositoryData(name reference.Named) (*RepositoryData, error) { - repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.String(), reference.Path(name)) - - logrus.Debugf("[registry] Calling GET %s", repositoryTarget) - - req, err := http.NewRequest("GET", repositoryTarget, nil) - if err != nil { - return nil, err - } - // this will set basic auth in r.client.Transport and send cached X-Docker-Token headers for all subsequent requests - req.Header.Set("X-Docker-Token", "true") - res, err := r.client.Do(req) - if err != nil { - // check if the error is because of i/o timeout - // and return a non-obtuse error message for users - // "Get https://index.docker.io/v1/repositories/library/busybox/images: i/o timeout" - // was a top search on the docker user forum - if isTimeout(err) { - return nil, fmt.Errorf("network timed out while trying to connect to %s. You may want to check your internet connection or if you are behind a proxy", repositoryTarget) - } - return nil, fmt.Errorf("Error while pulling image: %v", err) - } - defer res.Body.Close() - if res.StatusCode == 401 { - return nil, errcode.ErrorCodeUnauthorized.WithArgs() - } - // TODO: Right now we're ignoring checksums in the response body. - // In the future, we need to use them to check image validity. - if res.StatusCode == 404 { - return nil, newJSONError(fmt.Sprintf("HTTP code: %d", res.StatusCode), res) - } else if res.StatusCode != 200 { - errBody, err := ioutil.ReadAll(res.Body) - if err != nil { - logrus.Debugf("Error reading response body: %s", err) - } - return nil, newJSONError(fmt.Sprintf("Error: Status %d trying to pull repository %s: %q", res.StatusCode, reference.Path(name), errBody), res) - } - - var endpoints []string - if res.Header.Get("X-Docker-Endpoints") != "" { - endpoints, err = buildEndpointsList(res.Header["X-Docker-Endpoints"], r.indexEndpoint.String()) - if err != nil { - return nil, err - } - } else { - // Assume the endpoint is on the same host - endpoints = append(endpoints, fmt.Sprintf("%s://%s/v1/", r.indexEndpoint.URL.Scheme, req.URL.Host)) - } - - remoteChecksums := []*ImgData{} - if err := json.NewDecoder(res.Body).Decode(&remoteChecksums); err != nil { - return nil, err - } - - // Forge a better object from the retrieved data - imgsData := make(map[string]*ImgData, len(remoteChecksums)) - for _, elem := range remoteChecksums { - imgsData[elem.ID] = elem - } - - return &RepositoryData{ - ImgList: imgsData, - Endpoints: endpoints, - }, nil -} - -// PushImageChecksumRegistry uploads checksums for an image -func (r *Session) PushImageChecksumRegistry(imgData *ImgData, registry string) error { - u := registry + "images/" + imgData.ID + "/checksum" - - logrus.Debugf("[registry] Calling PUT %s", u) - - req, err := http.NewRequest("PUT", u, nil) - if err != nil { - return err - } - req.Header.Set("X-Docker-Checksum", imgData.Checksum) - req.Header.Set("X-Docker-Checksum-Payload", imgData.ChecksumPayload) - - res, err := r.client.Do(req) - if err != nil { - return fmt.Errorf("Failed to upload metadata: %v", err) - } - defer res.Body.Close() - if len(res.Cookies()) > 0 { - r.client.Jar.SetCookies(req.URL, res.Cookies()) - } - if res.StatusCode != 200 { - errBody, err := ioutil.ReadAll(res.Body) - if err != nil { - return fmt.Errorf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err) - } - var jsonBody map[string]string - if err := json.Unmarshal(errBody, &jsonBody); err != nil { - errBody = []byte(err.Error()) - } else if jsonBody["error"] == "Image already exists" { - return ErrAlreadyExists - } - return fmt.Errorf("HTTP code %d while uploading metadata: %q", res.StatusCode, errBody) - } - return nil -} - -// PushImageJSONRegistry pushes JSON metadata for a local image to the registry -func (r *Session) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, registry string) error { - - u := registry + "images/" + imgData.ID + "/json" - - logrus.Debugf("[registry] Calling PUT %s", u) - - req, err := http.NewRequest("PUT", u, bytes.NewReader(jsonRaw)) - if err != nil { - return err - } - req.Header.Add("Content-type", "application/json") - - res, err := r.client.Do(req) - if err != nil { - return fmt.Errorf("Failed to upload metadata: %s", err) - } - defer res.Body.Close() - if res.StatusCode == 401 && strings.HasPrefix(registry, "http://") { - return newJSONError("HTTP code 401, Docker will not send auth headers over HTTP.", res) - } - if res.StatusCode != 200 { - errBody, err := ioutil.ReadAll(res.Body) - if err != nil { - return newJSONError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res) - } - var jsonBody map[string]string - if err := json.Unmarshal(errBody, &jsonBody); err != nil { - errBody = []byte(err.Error()) - } else if jsonBody["error"] == "Image already exists" { - return ErrAlreadyExists - } - return newJSONError(fmt.Sprintf("HTTP code %d while uploading metadata: %q", res.StatusCode, errBody), res) - } - return nil -} - -// PushImageLayerRegistry sends the checksum of an image layer to the registry -func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry string, jsonRaw []byte) (checksum string, checksumPayload string, err error) { - u := registry + "images/" + imgID + "/layer" - - logrus.Debugf("[registry] Calling PUT %s", u) - - tarsumLayer, err := tarsum.NewTarSum(layer, false, tarsum.Version0) - if err != nil { - return "", "", err - } - h := sha256.New() - h.Write(jsonRaw) - h.Write([]byte{'\n'}) - checksumLayer := io.TeeReader(tarsumLayer, h) - - req, err := http.NewRequest("PUT", u, checksumLayer) - if err != nil { - return "", "", err - } - req.Header.Add("Content-Type", "application/octet-stream") - req.ContentLength = -1 - req.TransferEncoding = []string{"chunked"} - res, err := r.client.Do(req) - if err != nil { - return "", "", fmt.Errorf("Failed to upload layer: %v", err) - } - if rc, ok := layer.(io.Closer); ok { - if err := rc.Close(); err != nil { - return "", "", err - } - } - defer res.Body.Close() - - if res.StatusCode != 200 { - errBody, err := ioutil.ReadAll(res.Body) - if err != nil { - return "", "", newJSONError(fmt.Sprintf("HTTP code %d while uploading metadata and error when trying to parse response body: %s", res.StatusCode, err), res) - } - return "", "", newJSONError(fmt.Sprintf("Received HTTP code %d while uploading layer: %q", res.StatusCode, errBody), res) - } - - checksumPayload = "sha256:" + hex.EncodeToString(h.Sum(nil)) - return tarsumLayer.Sum(jsonRaw), checksumPayload, nil -} - -// PushRegistryTag pushes a tag on the registry. -// Remote has the format '/ -func (r *Session) PushRegistryTag(remote reference.Named, revision, tag, registry string) error { - // "jsonify" the string - revision = "\"" + revision + "\"" - path := fmt.Sprintf("repositories/%s/tags/%s", reference.Path(remote), tag) - - req, err := http.NewRequest("PUT", registry+path, strings.NewReader(revision)) - if err != nil { - return err - } - req.Header.Add("Content-type", "application/json") - req.ContentLength = int64(len(revision)) - res, err := r.client.Do(req) - if err != nil { - return err - } - res.Body.Close() - if res.StatusCode != 200 && res.StatusCode != 201 { - return newJSONError(fmt.Sprintf("Internal server error: %d trying to push tag %s on %s", res.StatusCode, tag, reference.Path(remote)), res) - } - return nil -} - -// PushImageJSONIndex uploads an image list to the repository -func (r *Session) PushImageJSONIndex(remote reference.Named, imgList []*ImgData, validate bool, regs []string) (*RepositoryData, error) { - cleanImgList := []*ImgData{} - if validate { - for _, elem := range imgList { - if elem.Checksum != "" { - cleanImgList = append(cleanImgList, elem) - } - } - } else { - cleanImgList = imgList - } - - imgListJSON, err := json.Marshal(cleanImgList) - if err != nil { - return nil, err - } - var suffix string - if validate { - suffix = "images" - } - u := fmt.Sprintf("%srepositories/%s/%s", r.indexEndpoint.String(), reference.Path(remote), suffix) - logrus.Debugf("[registry] PUT %s", u) - logrus.Debugf("Image list pushed to index:\n%s", imgListJSON) - headers := map[string][]string{ - "Content-type": {"application/json"}, - // this will set basic auth in r.client.Transport and send cached X-Docker-Token headers for all subsequent requests - "X-Docker-Token": {"true"}, - } - if validate { - headers["X-Docker-Endpoints"] = regs - } - - // Redirect if necessary - var res *http.Response - for { - if res, err = r.putImageRequest(u, headers, imgListJSON); err != nil { - return nil, err - } - if !shouldRedirect(res) { - break - } - res.Body.Close() - u = res.Header.Get("Location") - logrus.Debugf("Redirected to %s", u) - } - defer res.Body.Close() - - if res.StatusCode == 401 { - return nil, errcode.ErrorCodeUnauthorized.WithArgs() - } - - var tokens, endpoints []string - if !validate { - if res.StatusCode != 200 && res.StatusCode != 201 { - errBody, err := ioutil.ReadAll(res.Body) - if err != nil { - logrus.Debugf("Error reading response body: %s", err) - } - return nil, newJSONError(fmt.Sprintf("Error: Status %d trying to push repository %s: %q", res.StatusCode, reference.Path(remote), errBody), res) - } - tokens = res.Header["X-Docker-Token"] - logrus.Debugf("Auth token: %v", tokens) - - if res.Header.Get("X-Docker-Endpoints") == "" { - return nil, fmt.Errorf("Index response didn't contain any endpoints") - } - endpoints, err = buildEndpointsList(res.Header["X-Docker-Endpoints"], r.indexEndpoint.String()) - if err != nil { - return nil, err - } - } else { - if res.StatusCode != 204 { - errBody, err := ioutil.ReadAll(res.Body) - if err != nil { - logrus.Debugf("Error reading response body: %s", err) - } - return nil, newJSONError(fmt.Sprintf("Error: Status %d trying to push checksums %s: %q", res.StatusCode, reference.Path(remote), errBody), res) - } - } - - return &RepositoryData{ - Endpoints: endpoints, - }, nil -} - -func (r *Session) putImageRequest(u string, headers map[string][]string, body []byte) (*http.Response, error) { - req, err := http.NewRequest("PUT", u, bytes.NewReader(body)) - if err != nil { - return nil, err - } - req.ContentLength = int64(len(body)) - for k, v := range headers { - req.Header[k] = v - } - response, err := r.client.Do(req) - if err != nil { - return nil, err - } - return response, nil -} - -func shouldRedirect(response *http.Response) bool { - return response.StatusCode >= 300 && response.StatusCode < 400 -} - // SearchRepositories performs a search against the remote repository func (r *Session) SearchRepositories(term string, limit int) (*registrytypes.SearchResults, error) { if limit < 1 || limit > 100 { @@ -740,7 +205,7 @@ func (r *Session) SearchRepositories(term string, limit int) (*registrytypes.Sea logrus.Debugf("Index server: %s", r.indexEndpoint) u := r.indexEndpoint.String() + "search?q=" + url.QueryEscape(term) + "&n=" + url.QueryEscape(fmt.Sprintf("%d", limit)) - req, err := http.NewRequest("GET", u, nil) + req, err := http.NewRequest(http.MethodGet, u, nil) if err != nil { return nil, errors.Wrap(errdefs.InvalidParameter(err), "Error building request") } @@ -751,29 +216,12 @@ func (r *Session) SearchRepositories(term string, limit int) (*registrytypes.Sea return nil, errdefs.System(err) } defer res.Body.Close() - if res.StatusCode != 200 { - return nil, newJSONError(fmt.Sprintf("Unexpected status code %d", res.StatusCode), res) + if res.StatusCode != http.StatusOK { + return nil, &jsonmessage.JSONError{ + Message: fmt.Sprintf("Unexpected status code %d", res.StatusCode), + Code: res.StatusCode, + } } result := new(registrytypes.SearchResults) return result, errors.Wrap(json.NewDecoder(res.Body).Decode(result), "error decoding registry search results") } - -func isTimeout(err error) bool { - type timeout interface { - Timeout() bool - } - e := err - switch urlErr := err.(type) { - case *url.Error: - e = urlErr.Err - } - t, ok := e.(timeout) - return ok && t.Timeout() -} - -func newJSONError(msg string, res *http.Response) error { - return &jsonmessage.JSONError{ - Message: msg, - Code: res.StatusCode, - } -} diff --git a/registry/types.go b/registry/types.go index 28ed2bfa5e88f..073e244ba8b1b 100644 --- a/registry/types.go +++ b/registry/types.go @@ -45,9 +45,8 @@ func (av APIVersion) String() string { // API Version identifiers. const ( - _ = iota - APIVersion1 APIVersion = iota - APIVersion2 + APIVersion1 APIVersion = 1 + APIVersion2 APIVersion = 2 ) var apiVersions = map[APIVersion]string{ diff --git a/restartmanager/restartmanager.go b/restartmanager/restartmanager.go index 6468ccf7e64e1..12094def60dfc 100644 --- a/restartmanager/restartmanager.go +++ b/restartmanager/restartmanager.go @@ -107,11 +107,14 @@ func (rm *restartManager) ShouldRestart(exitCode uint32, hasBeenManuallyStopped ch := make(chan error) go func() { + timeout := time.NewTimer(rm.timeout) + defer timeout.Stop() + select { case <-rm.cancel: ch <- ErrRestartCanceled close(ch) - case <-time.After(rm.timeout): + case <-timeout.C: rm.Lock() close(ch) rm.active = false diff --git a/restartmanager/restartmanager_test.go b/restartmanager/restartmanager_test.go index 4b6f30247902a..82558946bc2f8 100644 --- a/restartmanager/restartmanager_test.go +++ b/restartmanager/restartmanager_test.go @@ -9,7 +9,7 @@ import ( func TestRestartManagerTimeout(t *testing.T) { rm := New(container.RestartPolicy{Name: "always"}, 0).(*restartManager) - var duration = time.Duration(1 * time.Second) + var duration = 1 * time.Second should, _, err := rm.ShouldRestart(0, false, duration) if err != nil { t.Fatal(err) @@ -25,7 +25,7 @@ func TestRestartManagerTimeout(t *testing.T) { func TestRestartManagerTimeoutReset(t *testing.T) { rm := New(container.RestartPolicy{Name: "always"}, 0).(*restartManager) rm.timeout = 5 * time.Second - var duration = time.Duration(10 * time.Second) + var duration = 10 * time.Second _, _, err := rm.ShouldRestart(0, false, duration) if err != nil { t.Fatal(err) diff --git a/rootless/rootless.go b/rootless/rootless.go new file mode 100644 index 0000000000000..376d5263de9fc --- /dev/null +++ b/rootless/rootless.go @@ -0,0 +1,25 @@ +package rootless // import "github.com/docker/docker/rootless" + +import ( + "os" + "sync" +) + +const ( + // RootlessKitDockerProxyBinary is the binary name of rootlesskit-docker-proxy + RootlessKitDockerProxyBinary = "rootlesskit-docker-proxy" +) + +var ( + runningWithRootlessKit bool + runningWithRootlessKitOnce sync.Once +) + +// RunningWithRootlessKit returns true if running under RootlessKit namespaces. +func RunningWithRootlessKit() bool { + runningWithRootlessKitOnce.Do(func() { + u := os.Getenv("ROOTLESSKIT_STATE_DIR") + runningWithRootlessKit = u != "" + }) + return runningWithRootlessKit +} diff --git a/rootless/specconv/specconv_linux.go b/rootless/specconv/specconv_linux.go new file mode 100644 index 0000000000000..4e542818c24d4 --- /dev/null +++ b/rootless/specconv/specconv_linux.go @@ -0,0 +1,138 @@ +package specconv // import "github.com/docker/docker/rootless/specconv" + +import ( + "os" + "path" + "strconv" + "strings" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/sirupsen/logrus" +) + +// ToRootless converts spec to be compatible with "rootless" runc. +// * Remove non-supported cgroups +// * Fix up OOMScoreAdj +// * Fix up /proc if --pid=host +// +// v2Controllers should be non-nil only if running with v2 and systemd. +func ToRootless(spec *specs.Spec, v2Controllers []string) error { + return toRootless(spec, v2Controllers, getCurrentOOMScoreAdj()) +} + +func getCurrentOOMScoreAdj() int { + b, err := os.ReadFile("/proc/self/oom_score_adj") + if err != nil { + logrus.WithError(err).Warn("failed to read /proc/self/oom_score_adj") + return 0 + } + s := string(b) + i, err := strconv.Atoi(strings.TrimSpace(s)) + if err != nil { + logrus.WithError(err).Warnf("failed to parse /proc/self/oom_score_adj (%q)", s) + return 0 + } + return i +} + +func toRootless(spec *specs.Spec, v2Controllers []string, currentOOMScoreAdj int) error { + if len(v2Controllers) == 0 { + // Remove cgroup settings. + spec.Linux.Resources = nil + spec.Linux.CgroupsPath = "" + } else { + if spec.Linux.Resources != nil { + m := make(map[string]struct{}) + for _, s := range v2Controllers { + m[s] = struct{}{} + } + // Remove devices: https://github.com/containers/crun/issues/255 + spec.Linux.Resources.Devices = nil + if _, ok := m["memory"]; !ok { + spec.Linux.Resources.Memory = nil + } + if _, ok := m["cpu"]; !ok { + spec.Linux.Resources.CPU = nil + } + if _, ok := m["cpuset"]; !ok { + if spec.Linux.Resources.CPU != nil { + spec.Linux.Resources.CPU.Cpus = "" + spec.Linux.Resources.CPU.Mems = "" + } + } + if _, ok := m["pids"]; !ok { + spec.Linux.Resources.Pids = nil + } + if _, ok := m["io"]; !ok { + spec.Linux.Resources.BlockIO = nil + } + if _, ok := m["rdma"]; !ok { + spec.Linux.Resources.Rdma = nil + } + spec.Linux.Resources.HugepageLimits = nil + spec.Linux.Resources.Network = nil + } + } + + if spec.Process.OOMScoreAdj != nil && *spec.Process.OOMScoreAdj < currentOOMScoreAdj { + *spec.Process.OOMScoreAdj = currentOOMScoreAdj + } + + // Fix up /proc if --pid=host + pidHost, err := isPidHost(spec) + if err != nil { + return err + } + if !pidHost { + return nil + } + return bindMountHostProcfs(spec) +} + +func isPidHost(spec *specs.Spec) (bool, error) { + for _, ns := range spec.Linux.Namespaces { + if ns.Type == specs.PIDNamespace { + if ns.Path == "" { + return false, nil + } + pidNS, err := os.Readlink(ns.Path) + if err != nil { + return false, err + } + selfPidNS, err := os.Readlink("/proc/self/ns/pid") + if err != nil { + return false, err + } + return pidNS == selfPidNS, nil + } + } + return true, nil +} + +func bindMountHostProcfs(spec *specs.Spec) error { + // Replace procfs mount with rbind + // https://github.com/containers/podman/blob/v3.0.0-rc1/pkg/specgen/generate/oci.go#L248-L257 + for i, m := range spec.Mounts { + if path.Clean(m.Destination) == "/proc" { + newM := specs.Mount{ + Destination: "/proc", + Type: "bind", + Source: "/proc", + Options: []string{"rbind", "nosuid", "noexec", "nodev"}, + } + spec.Mounts[i] = newM + } + } + + // Remove ReadonlyPaths for /proc/* + newROP := spec.Linux.ReadonlyPaths[:0] + for _, s := range spec.Linux.ReadonlyPaths { + s = path.Clean(s) + if !strings.HasPrefix(s, "/proc/") { + newROP = append(newROP, s) + } + } + spec.Linux.ReadonlyPaths = newROP + + return nil +} diff --git a/runconfig/config.go b/runconfig/config.go index cbacf47df341d..1b71b2042f3ad 100644 --- a/runconfig/config.go +++ b/runconfig/config.go @@ -11,11 +11,20 @@ import ( // ContainerDecoder implements httputils.ContainerDecoder // calling DecodeContainerConfig. -type ContainerDecoder struct{} +type ContainerDecoder struct { + GetSysInfo func() *sysinfo.SysInfo +} // DecodeConfig makes ContainerDecoder to implement httputils.ContainerDecoder func (r ContainerDecoder) DecodeConfig(src io.Reader) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) { - return decodeContainerConfig(src) + var si *sysinfo.SysInfo + if r.GetSysInfo != nil { + si = r.GetSysInfo() + } else { + si = sysinfo.New() + } + + return decodeContainerConfig(src, si) } // DecodeHostConfig makes ContainerDecoder to implement httputils.ContainerDecoder @@ -24,58 +33,42 @@ func (r ContainerDecoder) DecodeHostConfig(src io.Reader) (*container.HostConfig } // decodeContainerConfig decodes a json encoded config into a ContainerConfigWrapper -// struct and returns both a Config and a HostConfig struct +// struct and returns both a Config and a HostConfig struct, and performs some +// validation. Certain parameters need daemon-side validation that cannot be done +// on the client, as only the daemon knows what is valid for the platform. // Be aware this function is not checking whether the resulted structs are nil, // it's your business to do so -func decodeContainerConfig(src io.Reader) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) { +func decodeContainerConfig(src io.Reader, si *sysinfo.SysInfo) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig, error) { var w ContainerConfigWrapper - - decoder := json.NewDecoder(src) - if err := decoder.Decode(&w); err != nil { + if err := json.NewDecoder(src).Decode(&w); err != nil { return nil, nil, nil, err } hc := w.getHostConfig() - - // Perform platform-specific processing of Volumes and Binds. - if w.Config != nil && hc != nil { - - // Initialize the volumes map if currently nil - if w.Config.Volumes == nil { - w.Config.Volumes = make(map[string]struct{}) - } + if hc == nil { + // We may not be passed a host config, such as in the case of docker commit + return w.Config, hc, w.NetworkingConfig, nil } - - // Certain parameters need daemon-side validation that cannot be done - // on the client, as only the daemon knows what is valid for the platform. if err := validateNetMode(w.Config, hc); err != nil { return nil, nil, nil, err } - - // Validate isolation if err := validateIsolation(hc); err != nil { return nil, nil, nil, err } - - // Validate QoS if err := validateQoS(hc); err != nil { return nil, nil, nil, err } - - // Validate Resources - if err := validateResources(hc, sysinfo.New(true)); err != nil { + if err := validateResources(hc, si); err != nil { return nil, nil, nil, err } - - // Validate Privileged if err := validatePrivileged(hc); err != nil { return nil, nil, nil, err } - - // Validate ReadonlyRootfs if err := validateReadonlyRootfs(hc); err != nil { return nil, nil, nil, err } - + if w.Config != nil && w.Config.Volumes == nil { + w.Config.Volumes = make(map[string]struct{}) + } return w.Config, hc, w.NetworkingConfig, nil } diff --git a/runconfig/config_test.go b/runconfig/config_test.go index 67d386969f4da..5325f5a754752 100644 --- a/runconfig/config_test.go +++ b/runconfig/config_test.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" + "os" "runtime" "strings" "testing" @@ -12,8 +12,7 @@ import ( "github.com/docker/docker/api/types/container" networktypes "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/strslice" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "github.com/docker/docker/pkg/sysinfo" ) type f struct { @@ -43,12 +42,12 @@ func TestDecodeContainerConfig(t *testing.T) { } for _, f := range fixtures { - b, err := ioutil.ReadFile(f.file) + b, err := os.ReadFile(f.file) if err != nil { t.Fatal(err) } - c, h, _, err := decodeContainerConfig(bytes.NewReader(b)) + c, h, _, err := decodeContainerConfig(bytes.NewReader(b), sysinfo.New()) if err != nil { t.Fatal(fmt.Errorf("Error parsing %s: %v", f, err)) } @@ -132,59 +131,5 @@ func callDecodeContainerConfigIsolation(isolation string) (*container.Config, *c if b, err = json.Marshal(w); err != nil { return nil, nil, nil, fmt.Errorf("Error on marshal %s", err.Error()) } - return decodeContainerConfig(bytes.NewReader(b)) -} - -type decodeConfigTestcase struct { - doc string - wrapper ContainerConfigWrapper - expectedErr string - expectedConfig *container.Config - expectedHostConfig *container.HostConfig - goos string -} - -func runDecodeContainerConfigTestCase(testcase decodeConfigTestcase) func(t *testing.T) { - return func(t *testing.T) { - raw := marshal(t, testcase.wrapper, testcase.doc) - config, hostConfig, _, err := decodeContainerConfig(bytes.NewReader(raw)) - if testcase.expectedErr != "" { - if !assert.Check(t, is.ErrorContains(err, "")) { - return - } - assert.Check(t, is.Contains(err.Error(), testcase.expectedErr)) - return - } - assert.Check(t, err) - assert.Check(t, is.DeepEqual(testcase.expectedConfig, config)) - assert.Check(t, is.DeepEqual(testcase.expectedHostConfig, hostConfig)) - } -} - -func marshal(t *testing.T, w ContainerConfigWrapper, doc string) []byte { - b, err := json.Marshal(w) - assert.NilError(t, err, "%s: failed to encode config wrapper", doc) - return b -} - -func containerWrapperWithVolume(volume string) ContainerConfigWrapper { - return ContainerConfigWrapper{ - Config: &container.Config{ - Volumes: map[string]struct{}{ - volume: {}, - }, - }, - HostConfig: &container.HostConfig{}, - } -} - -func containerWrapperWithBind(bind string) ContainerConfigWrapper { - return ContainerConfigWrapper{ - Config: &container.Config{ - Volumes: map[string]struct{}{}, - }, - HostConfig: &container.HostConfig{ - Binds: []string{bind}, - }, - } + return decodeContainerConfig(bytes.NewReader(b), sysinfo.New()) } diff --git a/runconfig/config_unix.go b/runconfig/config_unix.go index 65e8d6fcd498a..6b2939705156e 100644 --- a/runconfig/config_unix.go +++ b/runconfig/config_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package runconfig // import "github.com/docker/docker/runconfig" @@ -24,7 +25,7 @@ func (w *ContainerConfigWrapper) getHostConfig() *container.HostConfig { if hc == nil && w.InnerHostConfig != nil { hc = w.InnerHostConfig - } else if w.InnerHostConfig != nil { + } else if hc != nil && w.InnerHostConfig != nil { if hc.Memory != 0 && w.InnerHostConfig.Memory == 0 { w.InnerHostConfig.Memory = hc.Memory } diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 7d99e5acfabdd..1d6266d5f719e 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -11,15 +11,11 @@ import ( // DecodeHostConfig creates a HostConfig based on the specified Reader. // It assumes the content of the reader will be JSON, and decodes it. func decodeHostConfig(src io.Reader) (*container.HostConfig, error) { - decoder := json.NewDecoder(src) - var w ContainerConfigWrapper - if err := decoder.Decode(&w); err != nil { + if err := json.NewDecoder(src).Decode(&w); err != nil { return nil, err } - - hc := w.getHostConfig() - return hc, nil + return w.getHostConfig(), nil } // SetDefaultNetModeIfBlank changes the NetworkMode in a HostConfig structure @@ -27,20 +23,14 @@ func decodeHostConfig(src io.Reader) (*container.HostConfig, error) { // the validation of the network mode was moved from the docker CLI to the // docker daemon. func SetDefaultNetModeIfBlank(hc *container.HostConfig) { - if hc != nil { - if hc.NetworkMode == container.NetworkMode("") { - hc.NetworkMode = container.NetworkMode("default") - } + if hc != nil && hc.NetworkMode == "" { + hc.NetworkMode = "default" } } // validateNetContainerMode ensures that the various combinations of requested // network settings wrt container mode are valid. func validateNetContainerMode(c *container.Config, hc *container.HostConfig) error { - // We may not be passed a host config, such as in the case of docker commit - if hc == nil { - return nil - } parts := strings.Split(string(hc.NetworkMode), ":") if parts[0] == "container" { if len(parts) < 2 || parts[1] == "" { diff --git a/runconfig/hostconfig_test.go b/runconfig/hostconfig_test.go index d74d5f1176d48..9039e07f12985 100644 --- a/runconfig/hostconfig_test.go +++ b/runconfig/hostconfig_test.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package runconfig // import "github.com/docker/docker/runconfig" @@ -5,15 +6,41 @@ package runconfig // import "github.com/docker/docker/runconfig" import ( "bytes" "fmt" - "io/ioutil" + "os" "testing" "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/sysinfo" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) +func TestCgroupnsModeTest(t *testing.T) { + cgroupNsModes := map[container.CgroupnsMode][]bool{ + // private, host, empty, valid + "": {false, false, true, true}, + "something:weird": {false, false, false, false}, + "host": {false, true, false, true}, + "host:name": {false, false, false, false}, + "private": {true, false, false, true}, + "private:name": {false, false, false, false}, + } + for cgroupNsMode, state := range cgroupNsModes { + if cgroupNsMode.IsPrivate() != state[0] { + t.Fatalf("CgroupnsMode.IsPrivate for %v should have been %v but was %v", cgroupNsMode, state[0], cgroupNsMode.IsPrivate()) + } + if cgroupNsMode.IsHost() != state[1] { + t.Fatalf("CgroupnsMode.IsHost for %v should have been %v but was %v", cgroupNsMode, state[1], cgroupNsMode.IsHost()) + } + if cgroupNsMode.IsEmpty() != state[2] { + t.Fatalf("CgroupnsMode.Valid for %v should have been %v but was %v", cgroupNsMode, state[2], cgroupNsMode.Valid()) + } + if cgroupNsMode.Valid() != state[3] { + t.Fatalf("CgroupnsMode.Valid for %v should have been %v but was %v", cgroupNsMode, state[2], cgroupNsMode.Valid()) + } + } +} + // TODO Windows: This will need addressing for a Windows daemon. func TestNetworkModeTest(t *testing.T) { networkModes := map[container.NetworkMode][]bool{ @@ -186,7 +213,7 @@ func TestDecodeHostConfig(t *testing.T) { } for _, f := range fixtures { - b, err := ioutil.ReadFile(f.file) + b, err := os.ReadFile(f.file) if err != nil { t.Fatal(err) } @@ -214,46 +241,41 @@ func TestDecodeHostConfig(t *testing.T) { func TestValidateResources(t *testing.T) { type resourceTest struct { - ConfigCPURealtimePeriod int64 - ConfigCPURealtimeRuntime int64 - SysInfoCPURealtimePeriod bool - SysInfoCPURealtimeRuntime bool - ErrorExpected bool - FailureMsg string + ConfigCPURealtimePeriod int64 + ConfigCPURealtimeRuntime int64 + SysInfoCPURealtime bool + ErrorExpected bool + FailureMsg string } tests := []resourceTest{ { - ConfigCPURealtimePeriod: 1000, - ConfigCPURealtimeRuntime: 1000, - SysInfoCPURealtimePeriod: true, - SysInfoCPURealtimeRuntime: true, - ErrorExpected: false, - FailureMsg: "Expected valid configuration", + ConfigCPURealtimePeriod: 1000, + ConfigCPURealtimeRuntime: 1000, + SysInfoCPURealtime: true, + ErrorExpected: false, + FailureMsg: "Expected valid configuration", }, { - ConfigCPURealtimePeriod: 5000, - ConfigCPURealtimeRuntime: 5000, - SysInfoCPURealtimePeriod: false, - SysInfoCPURealtimeRuntime: true, - ErrorExpected: true, - FailureMsg: "Expected failure when cpu-rt-period is set but kernel doesn't support it", + ConfigCPURealtimePeriod: 5000, + ConfigCPURealtimeRuntime: 5000, + SysInfoCPURealtime: false, + ErrorExpected: true, + FailureMsg: "Expected failure when cpu-rt-period is set but kernel doesn't support it", }, { - ConfigCPURealtimePeriod: 5000, - ConfigCPURealtimeRuntime: 5000, - SysInfoCPURealtimePeriod: true, - SysInfoCPURealtimeRuntime: false, - ErrorExpected: true, - FailureMsg: "Expected failure when cpu-rt-runtime is set but kernel doesn't support it", + ConfigCPURealtimePeriod: 5000, + ConfigCPURealtimeRuntime: 5000, + SysInfoCPURealtime: false, + ErrorExpected: true, + FailureMsg: "Expected failure when cpu-rt-runtime is set but kernel doesn't support it", }, { - ConfigCPURealtimePeriod: 5000, - ConfigCPURealtimeRuntime: 10000, - SysInfoCPURealtimePeriod: true, - SysInfoCPURealtimeRuntime: false, - ErrorExpected: true, - FailureMsg: "Expected failure when cpu-rt-runtime is greater than cpu-rt-period", + ConfigCPURealtimePeriod: 5000, + ConfigCPURealtimeRuntime: 10000, + SysInfoCPURealtime: true, + ErrorExpected: true, + FailureMsg: "Expected failure when cpu-rt-runtime is greater than cpu-rt-period", }, } @@ -263,8 +285,7 @@ func TestValidateResources(t *testing.T) { hc.Resources.CPURealtimeRuntime = rt.ConfigCPURealtimeRuntime var si sysinfo.SysInfo - si.CPURealtimePeriod = rt.SysInfoCPURealtimePeriod - si.CPURealtimeRuntime = rt.SysInfoCPURealtimeRuntime + si.CPURealtime = rt.SysInfoCPURealtime if err := validateResources(&hc, &si); (err != nil) != rt.ErrorExpected { t.Fatal(rt.FailureMsg, err) diff --git a/runconfig/hostconfig_unix.go b/runconfig/hostconfig_unix.go index e579b06d9b287..bbe70263499db 100644 --- a/runconfig/hostconfig_unix.go +++ b/runconfig/hostconfig_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package runconfig // import "github.com/docker/docker/runconfig" @@ -13,7 +14,7 @@ import ( // DefaultDaemonNetworkMode returns the default network stack the daemon should // use. func DefaultDaemonNetworkMode() container.NetworkMode { - return container.NetworkMode("bridge") + return "bridge" } // IsPreDefinedNetwork indicates if a network is predefined by the daemon @@ -25,24 +26,16 @@ func IsPreDefinedNetwork(network string) bool { // validateNetMode ensures that the various combinations of requested // network settings are valid. func validateNetMode(c *container.Config, hc *container.HostConfig) error { - // We may not be passed a host config, such as in the case of docker commit - if hc == nil { - return nil - } - err := validateNetContainerMode(c, hc) if err != nil { return err } - if hc.UTSMode.IsHost() && c.Hostname != "" { return ErrConflictUTSHostname } - if hc.NetworkMode.IsHost() && len(hc.Links) > 0 { return ErrConflictHostNetworkAndLinks } - return nil } @@ -50,10 +43,6 @@ func validateNetMode(c *container.Config, hc *container.HostConfig) error { // isolation in the hostconfig structure. Linux only supports "default" // which is LXC container isolation func validateIsolation(hc *container.HostConfig) error { - // We may not be passed a host config, such as in the case of docker commit - if hc == nil { - return nil - } if !hc.Isolation.IsValid() { return fmt.Errorf("Invalid isolation: %q - %s only supports 'default'", hc.Isolation, runtime.GOOS) } @@ -62,15 +51,9 @@ func validateIsolation(hc *container.HostConfig) error { // validateQoS performs platform specific validation of the QoS settings func validateQoS(hc *container.HostConfig) error { - // We may not be passed a host config, such as in the case of docker commit - if hc == nil { - return nil - } - if hc.IOMaximumBandwidth != 0 { return fmt.Errorf("Invalid QoS settings: %s does not support configuration of maximum bandwidth", runtime.GOOS) } - if hc.IOMaximumIOps != 0 { return fmt.Errorf("Invalid QoS settings: %s does not support configuration of maximum IOPs", runtime.GOOS) } @@ -80,19 +63,9 @@ func validateQoS(hc *container.HostConfig) error { // validateResources performs platform specific validation of the resource settings // cpu-rt-runtime and cpu-rt-period can not be greater than their parent, cpu-rt-runtime requires sys_nice func validateResources(hc *container.HostConfig, si *sysinfo.SysInfo) error { - // We may not be passed a host config, such as in the case of docker commit - if hc == nil { - return nil - } - - if hc.Resources.CPURealtimePeriod > 0 && !si.CPURealtimePeriod { - return fmt.Errorf("Your kernel does not support cgroup cpu real-time period") + if (hc.Resources.CPURealtimePeriod != 0 || hc.Resources.CPURealtimeRuntime != 0) && !si.CPURealtime { + return fmt.Errorf("Your kernel does not support CPU real-time scheduler") } - - if hc.Resources.CPURealtimeRuntime > 0 && !si.CPURealtimeRuntime { - return fmt.Errorf("Your kernel does not support cgroup cpu real-time runtime") - } - if hc.Resources.CPURealtimePeriod != 0 && hc.Resources.CPURealtimeRuntime != 0 && hc.Resources.CPURealtimeRuntime > hc.Resources.CPURealtimePeriod { return fmt.Errorf("cpu real-time runtime cannot be higher than cpu real-time period") } @@ -100,11 +73,11 @@ func validateResources(hc *container.HostConfig, si *sysinfo.SysInfo) error { } // validatePrivileged performs platform specific validation of the Privileged setting -func validatePrivileged(hc *container.HostConfig) error { +func validatePrivileged(_ *container.HostConfig) error { return nil } // validateReadonlyRootfs performs platform specific validation of the ReadonlyRootfs setting -func validateReadonlyRootfs(hc *container.HostConfig) error { +func validateReadonlyRootfs(_ *container.HostConfig) error { return nil } diff --git a/runconfig/hostconfig_windows.go b/runconfig/hostconfig_windows.go index 33a4668af1623..91e27eac5e39e 100644 --- a/runconfig/hostconfig_windows.go +++ b/runconfig/hostconfig_windows.go @@ -10,7 +10,7 @@ import ( // DefaultDaemonNetworkMode returns the default network stack the daemon should // use. func DefaultDaemonNetworkMode() container.NetworkMode { - return container.NetworkMode("nat") + return "nat" } // IsPreDefinedNetwork indicates if a network is predefined by the daemon @@ -21,19 +21,12 @@ func IsPreDefinedNetwork(network string) bool { // validateNetMode ensures that the various combinations of requested // network settings are valid. func validateNetMode(c *container.Config, hc *container.HostConfig) error { - if hc == nil { - return nil - } - - err := validateNetContainerMode(c, hc) - if err != nil { + if err := validateNetContainerMode(c, hc); err != nil { return err } - if hc.NetworkMode.IsContainer() && hc.Isolation.IsHyperV() { return fmt.Errorf("Using the network stack of another container is not supported while using Hyper-V Containers") } - return nil } @@ -41,10 +34,6 @@ func validateNetMode(c *container.Config, hc *container.HostConfig) error { // isolation in the hostconfig structure. Windows supports 'default' (or // blank), 'process', or 'hyperv'. func validateIsolation(hc *container.HostConfig) error { - // We may not be passed a host config, such as in the case of docker commit - if hc == nil { - return nil - } if !hc.Isolation.IsValid() { return fmt.Errorf("Invalid isolation: %q. Windows supports 'default', 'process', or 'hyperv'", hc.Isolation) } @@ -52,16 +41,12 @@ func validateIsolation(hc *container.HostConfig) error { } // validateQoS performs platform specific validation of the Qos settings -func validateQoS(hc *container.HostConfig) error { +func validateQoS(_ *container.HostConfig) error { return nil } // validateResources performs platform specific validation of the resource settings -func validateResources(hc *container.HostConfig, si *sysinfo.SysInfo) error { - // We may not be passed a host config, such as in the case of docker commit - if hc == nil { - return nil - } +func validateResources(hc *container.HostConfig, _ *sysinfo.SysInfo) error { if hc.Resources.CPURealtimePeriod != 0 { return fmt.Errorf("Windows does not support CPU real-time period") } @@ -73,10 +58,6 @@ func validateResources(hc *container.HostConfig, si *sysinfo.SysInfo) error { // validatePrivileged performs platform specific validation of the Privileged setting func validatePrivileged(hc *container.HostConfig) error { - // We may not be passed a host config, such as in the case of docker commit - if hc == nil { - return nil - } if hc.Privileged { return fmt.Errorf("Windows does not support privileged mode") } @@ -85,10 +66,6 @@ func validatePrivileged(hc *container.HostConfig) error { // validateReadonlyRootfs performs platform specific validation of the ReadonlyRootfs setting func validateReadonlyRootfs(hc *container.HostConfig) error { - // We may not be passed a host config, such as in the case of docker commit - if hc == nil { - return nil - } if hc.ReadonlyRootfs { return fmt.Errorf("Windows does not support root filesystem in read-only mode") } diff --git a/runconfig/hostconfig_windows_test.go b/runconfig/hostconfig_windows_test.go index d7a480f313f52..ccfe939ca8841 100644 --- a/runconfig/hostconfig_windows_test.go +++ b/runconfig/hostconfig_windows_test.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package runconfig // import "github.com/docker/docker/runconfig" diff --git a/testutil/daemon/config.go b/testutil/daemon/config.go new file mode 100644 index 0000000000000..1bf182ae28908 --- /dev/null +++ b/testutil/daemon/config.go @@ -0,0 +1,72 @@ +package daemon + +import ( + "context" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" + "gotest.tools/v3/assert" +) + +// ConfigConstructor defines a swarm config constructor +type ConfigConstructor func(*swarm.Config) + +// CreateConfig creates a config given the specified spec +func (d *Daemon) CreateConfig(t testing.TB, configSpec swarm.ConfigSpec) string { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + scr, err := cli.ConfigCreate(context.Background(), configSpec) + assert.NilError(t, err) + return scr.ID +} + +// ListConfigs returns the list of the current swarm configs +func (d *Daemon) ListConfigs(t testing.TB) []swarm.Config { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + configs, err := cli.ConfigList(context.Background(), types.ConfigListOptions{}) + assert.NilError(t, err) + return configs +} + +// GetConfig returns a swarm config identified by the specified id +func (d *Daemon) GetConfig(t testing.TB, id string) *swarm.Config { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + config, _, err := cli.ConfigInspectWithRaw(context.Background(), id) + assert.NilError(t, err) + return &config +} + +// DeleteConfig removes the swarm config identified by the specified id +func (d *Daemon) DeleteConfig(t testing.TB, id string) { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + err := cli.ConfigRemove(context.Background(), id) + assert.NilError(t, err) +} + +// UpdateConfig updates the swarm config identified by the specified id +// Currently, only label update is supported. +func (d *Daemon) UpdateConfig(t testing.TB, id string, f ...ConfigConstructor) { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + config := d.GetConfig(t, id) + for _, fn := range f { + fn(config) + } + + err := cli.ConfigUpdate(context.Background(), config.ID, config.Version, config.Spec) + assert.NilError(t, err) +} diff --git a/testutil/daemon/container.go b/testutil/daemon/container.go new file mode 100644 index 0000000000000..8e88e7b202b82 --- /dev/null +++ b/testutil/daemon/container.go @@ -0,0 +1,36 @@ +package daemon + +import ( + "context" + "testing" + + "github.com/docker/docker/api/types" + "gotest.tools/v3/assert" +) + +// ActiveContainers returns the list of ids of the currently running containers +func (d *Daemon) ActiveContainers(t testing.TB) []string { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{}) + assert.NilError(t, err) + + ids := make([]string, len(containers)) + for i, c := range containers { + ids[i] = c.ID + } + return ids +} + +// FindContainerIP returns the ip of the specified container +func (d *Daemon) FindContainerIP(t testing.TB, id string) string { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + i, err := cli.ContainerInspect(context.Background(), id) + assert.NilError(t, err) + return i.NetworkSettings.IPAddress +} diff --git a/testutil/daemon/daemon.go b/testutil/daemon/daemon.go new file mode 100644 index 0000000000000..56998ab4c98d9 --- /dev/null +++ b/testutil/daemon/daemon.go @@ -0,0 +1,868 @@ +package daemon // import "github.com/docker/docker/testutil/daemon" + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "os" + "os/exec" + "os/user" + "path/filepath" + "strconv" + "strings" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/events" + "github.com/docker/docker/client" + "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/pkg/stringid" + "github.com/docker/docker/testutil/request" + "github.com/docker/go-connections/sockets" + "github.com/docker/go-connections/tlsconfig" + "github.com/pkg/errors" + "gotest.tools/v3/assert" +) + +// LogT is the subset of the testing.TB interface used by the daemon. +type LogT interface { + Logf(string, ...interface{}) +} + +// nopLog is a no-op implementation of LogT that is used in daemons created by +// NewDaemon (where no testing.TB is available). +type nopLog struct{} + +func (nopLog) Logf(string, ...interface{}) {} + +const ( + defaultDockerdBinary = "dockerd" + defaultContainerdSocket = "/var/run/docker/containerd/containerd.sock" + defaultDockerdRootlessBinary = "dockerd-rootless.sh" + defaultUnixSocket = "/var/run/docker.sock" + defaultTLSHost = "localhost:2376" +) + +var errDaemonNotStarted = errors.New("daemon not started") + +// SockRoot holds the path of the default docker integration daemon socket +var SockRoot = filepath.Join(os.TempDir(), "docker-integration") + +type clientConfig struct { + transport *http.Transport + scheme string + addr string +} + +// Daemon represents a Docker daemon for the testing framework +type Daemon struct { + Root string + Folder string + Wait chan error + UseDefaultHost bool + UseDefaultTLSHost bool + + id string + logFile *os.File + cmd *exec.Cmd + storageDriver string + userlandProxy bool + defaultCgroupNamespaceMode string + execRoot string + experimental bool + init bool + dockerdBinary string + log LogT + pidFile string + args []string + containerdSocket string + rootlessUser *user.User + rootlessXDGRuntimeDir string + + // swarm related field + swarmListenAddr string + SwarmPort int // FIXME(vdemeester) should probably not be exported + DefaultAddrPool []string + SubnetSize uint32 + DataPathPort uint32 + OOMScoreAdjust int + // cached information + CachedInfo types.Info +} + +// NewDaemon returns a Daemon instance to be used for testing. +// The daemon will not automatically start. +// The daemon will modify and create files under workingDir. +func NewDaemon(workingDir string, ops ...Option) (*Daemon, error) { + storageDriver := os.Getenv("DOCKER_GRAPHDRIVER") + + if err := os.MkdirAll(SockRoot, 0700); err != nil { + return nil, errors.Wrapf(err, "failed to create daemon socket root %q", SockRoot) + } + + id := fmt.Sprintf("d%s", stringid.TruncateID(stringid.GenerateRandomID())) + dir := filepath.Join(workingDir, id) + daemonFolder, err := filepath.Abs(dir) + if err != nil { + return nil, err + } + daemonRoot := filepath.Join(daemonFolder, "root") + if err := os.MkdirAll(daemonRoot, 0755); err != nil { + return nil, errors.Wrapf(err, "failed to create daemon root %q", daemonRoot) + } + + userlandProxy := true + if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" { + if val, err := strconv.ParseBool(env); err != nil { + userlandProxy = val + } + } + d := &Daemon{ + id: id, + Folder: daemonFolder, + Root: daemonRoot, + storageDriver: storageDriver, + userlandProxy: userlandProxy, + // dxr stands for docker-execroot (shortened for avoiding unix(7) path length limitation) + execRoot: filepath.Join(os.TempDir(), "dxr", id), + dockerdBinary: defaultDockerdBinary, + swarmListenAddr: defaultSwarmListenAddr, + SwarmPort: DefaultSwarmPort, + log: nopLog{}, + containerdSocket: defaultContainerdSocket, + } + + for _, op := range ops { + op(d) + } + + if d.rootlessUser != nil { + if err := os.Chmod(SockRoot, 0777); err != nil { + return nil, err + } + uid, err := strconv.Atoi(d.rootlessUser.Uid) + if err != nil { + return nil, err + } + gid, err := strconv.Atoi(d.rootlessUser.Gid) + if err != nil { + return nil, err + } + if err := os.Chown(d.Folder, uid, gid); err != nil { + return nil, err + } + if err := os.Chown(d.Root, uid, gid); err != nil { + return nil, err + } + if err := os.MkdirAll(filepath.Dir(d.execRoot), 0700); err != nil { + return nil, err + } + if err := os.Chown(filepath.Dir(d.execRoot), uid, gid); err != nil { + return nil, err + } + if err := os.MkdirAll(d.execRoot, 0700); err != nil { + return nil, err + } + if err := os.Chown(d.execRoot, uid, gid); err != nil { + return nil, err + } + d.rootlessXDGRuntimeDir = filepath.Join(d.Folder, "xdgrun") + if err := os.MkdirAll(d.rootlessXDGRuntimeDir, 0700); err != nil { + return nil, err + } + if err := os.Chown(d.rootlessXDGRuntimeDir, uid, gid); err != nil { + return nil, err + } + d.containerdSocket = "" + } + + return d, nil +} + +// New returns a Daemon instance to be used for testing. +// This will create a directory such as d123456789 in the folder specified by +// $DOCKER_INTEGRATION_DAEMON_DEST or $DEST. +// The daemon will not automatically start. +func New(t testing.TB, ops ...Option) *Daemon { + t.Helper() + dest := os.Getenv("DOCKER_INTEGRATION_DAEMON_DEST") + if dest == "" { + dest = os.Getenv("DEST") + } + dest = filepath.Join(dest, t.Name()) + + assert.Check(t, dest != "", "Please set the DOCKER_INTEGRATION_DAEMON_DEST or the DEST environment variable") + + if os.Getenv("DOCKER_ROOTLESS") != "" { + if os.Getenv("DOCKER_REMAP_ROOT") != "" { + t.Skip("DOCKER_ROOTLESS doesn't support DOCKER_REMAP_ROOT currently") + } + if env := os.Getenv("DOCKER_USERLANDPROXY"); env != "" { + if val, err := strconv.ParseBool(env); err == nil && !val { + t.Skip("DOCKER_ROOTLESS doesn't support DOCKER_USERLANDPROXY=false") + } + } + ops = append(ops, WithRootlessUser("unprivilegeduser")) + } + ops = append(ops, WithOOMScoreAdjust(-500)) + + d, err := NewDaemon(dest, ops...) + assert.NilError(t, err, "could not create daemon at %q", dest) + if d.rootlessUser != nil && d.dockerdBinary != defaultDockerdBinary { + t.Skipf("DOCKER_ROOTLESS doesn't support specifying non-default dockerd binary path %q", d.dockerdBinary) + } + + return d +} + +// BinaryPath returns the binary and its arguments. +func (d *Daemon) BinaryPath() (string, error) { + dockerdBinary, err := exec.LookPath(d.dockerdBinary) + if err != nil { + return "", errors.Wrapf(err, "[%s] could not find docker binary in $PATH", d.id) + } + return dockerdBinary, nil +} + +// ContainersNamespace returns the containerd namespace used for containers. +func (d *Daemon) ContainersNamespace() string { + return d.id +} + +// RootDir returns the root directory of the daemon. +func (d *Daemon) RootDir() string { + return d.Root +} + +// ID returns the generated id of the daemon +func (d *Daemon) ID() string { + return d.id +} + +// StorageDriver returns the configured storage driver of the daemon +func (d *Daemon) StorageDriver() string { + return d.storageDriver +} + +// Sock returns the socket path of the daemon +func (d *Daemon) Sock() string { + return fmt.Sprintf("unix://" + d.sockPath()) +} + +func (d *Daemon) sockPath() string { + return filepath.Join(SockRoot, d.id+".sock") +} + +// LogFileName returns the path the daemon's log file +func (d *Daemon) LogFileName() string { + return d.logFile.Name() +} + +// ReadLogFile returns the content of the daemon log file +func (d *Daemon) ReadLogFile() ([]byte, error) { + _ = d.logFile.Sync() + return os.ReadFile(d.logFile.Name()) +} + +// NewClientT creates new client based on daemon's socket path +func (d *Daemon) NewClientT(t testing.TB, extraOpts ...client.Opt) *client.Client { + t.Helper() + + c, err := d.NewClient(extraOpts...) + assert.NilError(t, err, "[%s] could not create daemon client", d.id) + return c +} + +// NewClient creates new client based on daemon's socket path +func (d *Daemon) NewClient(extraOpts ...client.Opt) (*client.Client, error) { + clientOpts := []client.Opt{ + client.FromEnv, + client.WithHost(d.Sock()), + } + clientOpts = append(clientOpts, extraOpts...) + + return client.NewClientWithOpts(clientOpts...) +} + +// Cleanup cleans the daemon files : exec root (network namespaces, ...), swarmkit files +func (d *Daemon) Cleanup(t testing.TB) { + t.Helper() + cleanupMount(t, d) + cleanupRaftDir(t, d) + cleanupDaemonStorage(t, d) + cleanupNetworkNamespace(t, d) +} + +// Start starts the daemon and return once it is ready to receive requests. +func (d *Daemon) Start(t testing.TB, args ...string) { + t.Helper() + if err := d.StartWithError(args...); err != nil { + d.DumpStackAndQuit() // in case the daemon is stuck + t.Fatalf("[%s] failed to start daemon with arguments %v : %v", d.id, d.args, err) + } +} + +// StartWithError starts the daemon and return once it is ready to receive requests. +// It returns an error in case it couldn't start. +func (d *Daemon) StartWithError(args ...string) error { + logFile, err := os.OpenFile(filepath.Join(d.Folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) + if err != nil { + return errors.Wrapf(err, "[%s] failed to create logfile", d.id) + } + + return d.StartWithLogFile(logFile, args...) +} + +// StartWithLogFile will start the daemon and attach its streams to a given file. +func (d *Daemon) StartWithLogFile(out *os.File, providedArgs ...string) error { + d.handleUserns() + dockerdBinary, err := d.BinaryPath() + if err != nil { + return err + } + + if d.pidFile == "" { + d.pidFile = filepath.Join(d.Folder, "docker.pid") + } + + d.args = []string{} + if d.rootlessUser != nil { + if d.dockerdBinary != defaultDockerdBinary { + return errors.Errorf("[%s] DOCKER_ROOTLESS doesn't support non-default dockerd binary path %q", d.id, d.dockerdBinary) + } + dockerdBinary = "sudo" + d.args = append(d.args, + "-u", d.rootlessUser.Username, + "-E", "XDG_RUNTIME_DIR="+d.rootlessXDGRuntimeDir, + "-E", "HOME="+d.rootlessUser.HomeDir, + "-E", "PATH="+os.Getenv("PATH"), + "--", + defaultDockerdRootlessBinary, + ) + } + + d.args = append(d.args, + "--data-root", d.Root, + "--exec-root", d.execRoot, + "--pidfile", d.pidFile, + fmt.Sprintf("--userland-proxy=%t", d.userlandProxy), + "--containerd-namespace", d.id, + "--containerd-plugins-namespace", d.id+"p", + ) + if d.containerdSocket != "" { + d.args = append(d.args, "--containerd", d.containerdSocket) + } + + if d.defaultCgroupNamespaceMode != "" { + d.args = append(d.args, "--default-cgroupns-mode", d.defaultCgroupNamespaceMode) + } + if d.experimental { + d.args = append(d.args, "--experimental") + } + if d.init { + d.args = append(d.args, "--init") + } + if !(d.UseDefaultHost || d.UseDefaultTLSHost) { + d.args = append(d.args, "--host", d.Sock()) + } + if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" { + d.args = append(d.args, "--userns-remap", root) + } + + // If we don't explicitly set the log-level or debug flag(-D) then + // turn on debug mode + foundLog := false + foundSd := false + for _, a := range providedArgs { + if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") { + foundLog = true + } + if strings.Contains(a, "--storage-driver") { + foundSd = true + } + } + if !foundLog { + d.args = append(d.args, "--debug") + } + if d.storageDriver != "" && !foundSd { + d.args = append(d.args, "--storage-driver", d.storageDriver) + } + + d.args = append(d.args, providedArgs...) + d.cmd = exec.Command(dockerdBinary, d.args...) + d.cmd.Env = append(os.Environ(), "DOCKER_SERVICE_PREFER_OFFLINE_IMAGE=1") + d.cmd.Stdout = out + d.cmd.Stderr = out + d.logFile = out + if d.rootlessUser != nil { + // sudo requires this for propagating signals + setsid(d.cmd) + } + + if err := d.cmd.Start(); err != nil { + return errors.Wrapf(err, "[%s] could not start daemon container", d.id) + } + + wait := make(chan error, 1) + + go func() { + ret := d.cmd.Wait() + d.log.Logf("[%s] exiting daemon", d.id) + // If we send before logging, we might accidentally log _after_ the test is done. + // As of Go 1.12, this incurs a panic instead of silently being dropped. + wait <- ret + close(wait) + }() + + d.Wait = wait + + clientConfig, err := d.getClientConfig() + if err != nil { + return err + } + client := &http.Client{ + Transport: clientConfig.transport, + } + + req, err := http.NewRequest(http.MethodGet, "/_ping", nil) + if err != nil { + return errors.Wrapf(err, "[%s] could not create new request", d.id) + } + req.URL.Host = clientConfig.addr + req.URL.Scheme = clientConfig.scheme + + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + // make sure daemon is ready to receive requests + for i := 0; ; i++ { + d.log.Logf("[%s] waiting for daemon to start", d.id) + + select { + case <-ctx.Done(): + return errors.Wrapf(ctx.Err(), "[%s] daemon exited and never started", d.id) + case err := <-d.Wait: + return errors.Wrapf(err, "[%s] daemon exited during startup", d.id) + default: + rctx, rcancel := context.WithTimeout(context.TODO(), 2*time.Second) + defer rcancel() + + resp, err := client.Do(req.WithContext(rctx)) + if err != nil { + if i > 2 { // don't log the first couple, this ends up just being noise + d.log.Logf("[%s] error pinging daemon on start: %v", d.id, err) + } + + select { + case <-ctx.Done(): + case <-time.After(500 * time.Millisecond): + } + continue + } + + resp.Body.Close() + if resp.StatusCode != http.StatusOK { + d.log.Logf("[%s] received status != 200 OK: %s\n", d.id, resp.Status) + } + d.log.Logf("[%s] daemon started\n", d.id) + d.Root, err = d.queryRootDir() + if err != nil { + return errors.Wrapf(err, "[%s] error querying daemon for root directory", d.id) + } + return nil + } + } +} + +// StartWithBusybox will first start the daemon with Daemon.Start() +// then save the busybox image from the main daemon and load it into this Daemon instance. +func (d *Daemon) StartWithBusybox(t testing.TB, arg ...string) { + t.Helper() + d.Start(t, arg...) + d.LoadBusybox(t) +} + +// Kill will send a SIGKILL to the daemon +func (d *Daemon) Kill() error { + if d.cmd == nil || d.Wait == nil { + return errDaemonNotStarted + } + + defer func() { + d.logFile.Close() + d.cmd = nil + }() + + if err := d.cmd.Process.Kill(); err != nil { + return err + } + + if d.pidFile != "" { + _ = os.Remove(d.pidFile) + } + return nil +} + +// Pid returns the pid of the daemon +func (d *Daemon) Pid() int { + return d.cmd.Process.Pid +} + +// Interrupt stops the daemon by sending it an Interrupt signal +func (d *Daemon) Interrupt() error { + return d.Signal(os.Interrupt) +} + +// Signal sends the specified signal to the daemon if running +func (d *Daemon) Signal(signal os.Signal) error { + if d.cmd == nil || d.Wait == nil { + return errDaemonNotStarted + } + return d.cmd.Process.Signal(signal) +} + +// DumpStackAndQuit sends SIGQUIT to the daemon, which triggers it to dump its +// stack to its log file and exit +// This is used primarily for gathering debug information on test timeout +func (d *Daemon) DumpStackAndQuit() { + if d.cmd == nil || d.cmd.Process == nil { + return + } + SignalDaemonDump(d.cmd.Process.Pid) +} + +// Stop will send a SIGINT every second and wait for the daemon to stop. +// If it times out, a SIGKILL is sent. +// Stop will not delete the daemon directory. If a purged daemon is needed, +// instantiate a new one with NewDaemon. +// If an error occurs while starting the daemon, the test will fail. +func (d *Daemon) Stop(t testing.TB) { + t.Helper() + err := d.StopWithError() + if err != nil { + if err != errDaemonNotStarted { + t.Fatalf("[%s] error while stopping the daemon: %v", d.id, err) + } else { + t.Logf("[%s] daemon is not started", d.id) + } + } +} + +// StopWithError will send a SIGINT every second and wait for the daemon to stop. +// If it timeouts, a SIGKILL is sent. +// Stop will not delete the daemon directory. If a purged daemon is needed, +// instantiate a new one with NewDaemon. +func (d *Daemon) StopWithError() (err error) { + if d.cmd == nil || d.Wait == nil { + return errDaemonNotStarted + } + defer func() { + if err != nil { + d.log.Logf("[%s] error while stopping daemon: %v", d.id, err) + } else { + d.log.Logf("[%s] daemon stopped", d.id) + if d.pidFile != "" { + _ = os.Remove(d.pidFile) + } + } + if err := d.logFile.Close(); err != nil { + d.log.Logf("[%s] failed to close daemon logfile: %v", d.id, err) + } + d.cmd = nil + }() + + i := 1 + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + tick := ticker.C + + d.log.Logf("[%s] stopping daemon", d.id) + + if err := d.cmd.Process.Signal(os.Interrupt); err != nil { + if strings.Contains(err.Error(), "os: process already finished") { + return errDaemonNotStarted + } + return errors.Wrapf(err, "[%s] could not send signal", d.id) + } + +out1: + for { + select { + case err := <-d.Wait: + return err + case <-time.After(20 * time.Second): + // time for stopping jobs and run onShutdown hooks + d.log.Logf("[%s] daemon stop timed out after 20 seconds", d.id) + break out1 + } + } + +out2: + for { + select { + case err := <-d.Wait: + return err + case <-tick: + i++ + if i > 5 { + d.log.Logf("[%s] tried to interrupt daemon for %d times, now try to kill it", d.id, i) + break out2 + } + d.log.Logf("[%d] attempt #%d/5: daemon is still running with pid %d", i, d.cmd.Process.Pid) + if err := d.cmd.Process.Signal(os.Interrupt); err != nil { + return errors.Wrapf(err, "[%s] attempt #%d/5 could not send signal", d.id, i) + } + } + } + + if err := d.cmd.Process.Kill(); err != nil { + d.log.Logf("[%s] failed to kill daemon: %v", d.id, err) + return err + } + + return nil +} + +// Restart will restart the daemon by first stopping it and the starting it. +// If an error occurs while starting the daemon, the test will fail. +func (d *Daemon) Restart(t testing.TB, args ...string) { + t.Helper() + d.Stop(t) + d.Start(t, args...) +} + +// RestartWithError will restart the daemon by first stopping it and then starting it. +func (d *Daemon) RestartWithError(arg ...string) error { + if err := d.StopWithError(); err != nil { + return err + } + return d.StartWithError(arg...) +} + +func (d *Daemon) handleUserns() { + // in the case of tests running a user namespace-enabled daemon, we have resolved + // d.Root to be the actual final path of the graph dir after the "uid.gid" of + // remapped root is added--we need to subtract it from the path before calling + // start or else we will continue making subdirectories rather than truly restarting + // with the same location/root: + if root := os.Getenv("DOCKER_REMAP_ROOT"); root != "" { + d.Root = filepath.Dir(d.Root) + } +} + +// ReloadConfig asks the daemon to reload its configuration +func (d *Daemon) ReloadConfig() error { + if d.cmd == nil || d.cmd.Process == nil { + return errors.New("daemon is not running") + } + + errCh := make(chan error, 1) + started := make(chan struct{}) + go func() { + _, body, err := request.Get("/events", request.Host(d.Sock())) + close(started) + if err != nil { + errCh <- err + return + } + defer body.Close() + dec := json.NewDecoder(body) + for { + var e events.Message + if err := dec.Decode(&e); err != nil { + errCh <- err + return + } + if e.Type != events.DaemonEventType { + continue + } + if e.Action != "reload" { + continue + } + close(errCh) // notify that we are done + return + } + }() + + <-started + if err := signalDaemonReload(d.cmd.Process.Pid); err != nil { + return errors.Wrapf(err, "[%s] error signaling daemon reload", d.id) + } + select { + case err := <-errCh: + if err != nil { + return errors.Wrapf(err, "[%s] error waiting for daemon reload event", d.id) + } + case <-time.After(30 * time.Second): + return errors.Errorf("[%s] daemon reload event timed out after 30 seconds", d.id) + } + return nil +} + +// LoadBusybox image into the daemon +func (d *Daemon) LoadBusybox(t testing.TB) { + t.Helper() + clientHost, err := client.NewClientWithOpts(client.FromEnv) + assert.NilError(t, err, "[%s] failed to create client", d.id) + defer clientHost.Close() + + ctx := context.Background() + reader, err := clientHost.ImageSave(ctx, []string{"busybox:latest"}) + assert.NilError(t, err, "[%s] failed to download busybox", d.id) + defer reader.Close() + + c := d.NewClientT(t) + defer c.Close() + + resp, err := c.ImageLoad(ctx, reader, true) + assert.NilError(t, err, "[%s] failed to load busybox", d.id) + defer resp.Body.Close() +} + +func (d *Daemon) getClientConfig() (*clientConfig, error) { + var ( + transport *http.Transport + scheme string + addr string + proto string + ) + if d.UseDefaultTLSHost { + option := &tlsconfig.Options{ + CAFile: "fixtures/https/ca.pem", + CertFile: "fixtures/https/client-cert.pem", + KeyFile: "fixtures/https/client-key.pem", + } + tlsConfig, err := tlsconfig.Client(*option) + if err != nil { + return nil, err + } + transport = &http.Transport{ + TLSClientConfig: tlsConfig, + } + addr = defaultTLSHost + scheme = "https" + proto = "tcp" + } else if d.UseDefaultHost { + addr = defaultUnixSocket + proto = "unix" + scheme = "http" + transport = &http.Transport{} + } else { + addr = d.sockPath() + proto = "unix" + scheme = "http" + transport = &http.Transport{} + } + + if err := sockets.ConfigureTransport(transport, proto, addr); err != nil { + return nil, err + } + transport.DisableKeepAlives = true + if proto == "unix" { + addr = filepath.Base(addr) + } + return &clientConfig{ + transport: transport, + scheme: scheme, + addr: addr, + }, nil +} + +func (d *Daemon) queryRootDir() (string, error) { + // update daemon root by asking /info endpoint (to support user + // namespaced daemon with root remapped uid.gid directory) + clientConfig, err := d.getClientConfig() + if err != nil { + return "", err + } + + c := &http.Client{ + Transport: clientConfig.transport, + } + + req, err := http.NewRequest(http.MethodGet, "/info", nil) + if err != nil { + return "", err + } + req.Header.Set("Content-Type", "application/json") + req.URL.Host = clientConfig.addr + req.URL.Scheme = clientConfig.scheme + + resp, err := c.Do(req) + if err != nil { + return "", err + } + body := ioutils.NewReadCloserWrapper(resp.Body, func() error { + return resp.Body.Close() + }) + + type Info struct { + DockerRootDir string + } + var b []byte + var i Info + b, err = request.ReadBody(body) + if err == nil && resp.StatusCode == http.StatusOK { + // read the docker root dir + if err = json.Unmarshal(b, &i); err == nil { + return i.DockerRootDir, nil + } + } + return "", err +} + +// Info returns the info struct for this daemon +func (d *Daemon) Info(t testing.TB) types.Info { + t.Helper() + c := d.NewClientT(t) + info, err := c.Info(context.Background()) + assert.NilError(t, err) + assert.NilError(t, c.Close()) + return info +} + +// cleanupRaftDir removes swarmkit wal files if present +func cleanupRaftDir(t testing.TB, d *Daemon) { + t.Helper() + for _, p := range []string{"wal", "wal-v3-encrypted", "snap-v3-encrypted"} { + dir := filepath.Join(d.Root, "swarm/raft", p) + if err := os.RemoveAll(dir); err != nil { + t.Logf("[%s] error removing %v: %v", d.id, dir, err) + } + } +} + +// cleanupDaemonStorage removes the daemon's storage directory. +// +// Note that we don't delete the whole directory, as some files (e.g. daemon +// logs) are collected for inclusion in the "bundles" that are stored as Jenkins +// artifacts. +// +// We currently do not include container logs in the bundles, so this also +// removes the "containers" sub-directory. +func cleanupDaemonStorage(t testing.TB, d *Daemon) { + t.Helper() + dirs := []string{ + "builder", + "buildkit", + "containers", + "image", + "network", + "plugins", + "tmp", + "trust", + "volumes", + // note: this assumes storage-driver name matches the subdirectory, + // which is currently true, but not guaranteed. + d.storageDriver, + } + + for _, p := range dirs { + dir := filepath.Join(d.Root, p) + if err := os.RemoveAll(dir); err != nil { + t.Logf("[%s] error removing %v: %v", d.id, dir, err) + } + } +} diff --git a/testutil/daemon/daemon_freebsd.go b/testutil/daemon/daemon_freebsd.go new file mode 100644 index 0000000000000..0d182d4fb9a76 --- /dev/null +++ b/testutil/daemon/daemon_freebsd.go @@ -0,0 +1,18 @@ +//go:build freebsd +// +build freebsd + +package daemon // import "github.com/docker/docker/testutil/daemon" + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func cleanupNetworkNamespace(_ testing.TB, _ *Daemon) {} + +// CgroupNamespace returns the cgroup namespace the daemon is running in +func (d *Daemon) CgroupNamespace(t testing.TB) string { + assert.Assert(t, false, "cgroup namespaces are not supported on FreeBSD") + return "" +} diff --git a/testutil/daemon/daemon_linux.go b/testutil/daemon/daemon_linux.go new file mode 100644 index 0000000000000..720c52a4364e8 --- /dev/null +++ b/testutil/daemon/daemon_linux.go @@ -0,0 +1,37 @@ +package daemon // import "github.com/docker/docker/testutil/daemon" + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + "golang.org/x/sys/unix" + "gotest.tools/v3/assert" +) + +func cleanupNetworkNamespace(t testing.TB, d *Daemon) { + t.Helper() + // Cleanup network namespaces in the exec root of this + // daemon because this exec root is specific to this + // daemon instance and has no chance of getting + // cleaned up when a new daemon is instantiated with a + // new exec root. + netnsPath := filepath.Join(d.execRoot, "netns") + filepath.Walk(netnsPath, func(path string, info os.FileInfo, err error) error { + if err := unix.Unmount(path, unix.MNT_DETACH); err != nil && err != unix.EINVAL && err != unix.ENOENT { + t.Logf("[%s] unmount of %s failed: %v", d.id, path, err) + } + os.Remove(path) + return nil + }) +} + +// CgroupNamespace returns the cgroup namespace the daemon is running in +func (d *Daemon) CgroupNamespace(t testing.TB) string { + link, err := os.Readlink(fmt.Sprintf("/proc/%d/ns/cgroup", d.Pid())) + assert.NilError(t, err) + + return strings.TrimSpace(link) +} diff --git a/testutil/daemon/daemon_unix.go b/testutil/daemon/daemon_unix.go new file mode 100644 index 0000000000000..5ad7812b04404 --- /dev/null +++ b/testutil/daemon/daemon_unix.go @@ -0,0 +1,38 @@ +//go:build !windows +// +build !windows + +package daemon // import "github.com/docker/docker/testutil/daemon" + +import ( + "os/exec" + "syscall" + "testing" + + "github.com/moby/sys/mount" + "golang.org/x/sys/unix" +) + +// cleanupMount unmounts the daemon root directory, or logs a message if +// unmounting failed. +func cleanupMount(t testing.TB, d *Daemon) { + t.Helper() + if err := mount.Unmount(d.Root); err != nil { + d.log.Logf("[%s] unable to unmount daemon root (%s): %v", d.id, d.Root, err) + } +} + +// SignalDaemonDump sends a signal to the daemon to write a dump file +func SignalDaemonDump(pid int) { + unix.Kill(pid, unix.SIGQUIT) +} + +func signalDaemonReload(pid int) error { + return unix.Kill(pid, unix.SIGHUP) +} + +func setsid(cmd *exec.Cmd) { + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } + cmd.SysProcAttr.Setsid = true +} diff --git a/testutil/daemon/daemon_windows.go b/testutil/daemon/daemon_windows.go new file mode 100644 index 0000000000000..be94b5283839e --- /dev/null +++ b/testutil/daemon/daemon_windows.go @@ -0,0 +1,38 @@ +package daemon + +import ( + "fmt" + "os/exec" + "strconv" + "testing" + + "golang.org/x/sys/windows" + "gotest.tools/v3/assert" +) + +// SignalDaemonDump sends a signal to the daemon to write a dump file +func SignalDaemonDump(pid int) { + ev, _ := windows.UTF16PtrFromString("Global\\docker-daemon-" + strconv.Itoa(pid)) + h2, err := windows.OpenEvent(0x0002, false, ev) + if h2 == 0 || err != nil { + return + } + windows.PulseEvent(h2) +} + +func signalDaemonReload(pid int) error { + return fmt.Errorf("daemon reload not supported") +} + +func cleanupMount(_ testing.TB, _ *Daemon) {} + +func cleanupNetworkNamespace(_ testing.TB, _ *Daemon) {} + +// CgroupNamespace returns the cgroup namespace the daemon is running in +func (d *Daemon) CgroupNamespace(t testing.TB) string { + assert.Assert(t, false) + return "cgroup namespaces are not supported on Windows" +} + +func setsid(cmd *exec.Cmd) { +} diff --git a/testutil/daemon/doc.go b/testutil/daemon/doc.go new file mode 100644 index 0000000000000..add30e3cfb4a4 --- /dev/null +++ b/testutil/daemon/doc.go @@ -0,0 +1,2 @@ +// Package daemon launches dockerd for testing purposes. +package daemon // import "github.com/docker/docker/testutil/daemon" diff --git a/testutil/daemon/node.go b/testutil/daemon/node.go new file mode 100644 index 0000000000000..89d0817b0030a --- /dev/null +++ b/testutil/daemon/node.go @@ -0,0 +1,81 @@ +package daemon + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" + "gotest.tools/v3/assert" +) + +// NodeConstructor defines a swarm node constructor +type NodeConstructor func(*swarm.Node) + +// GetNode returns a swarm node identified by the specified id +func (d *Daemon) GetNode(t testing.TB, id string, errCheck ...func(error) bool) *swarm.Node { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + node, _, err := cli.NodeInspectWithRaw(context.Background(), id) + if err != nil { + for _, f := range errCheck { + if f(err) { + return nil + } + } + } + assert.NilError(t, err, "[%s] (*Daemon).GetNode: NodeInspectWithRaw(%q) failed", d.id, id) + assert.Check(t, node.ID == id) + return &node +} + +// RemoveNode removes the specified node +func (d *Daemon) RemoveNode(t testing.TB, id string, force bool) { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + options := types.NodeRemoveOptions{ + Force: force, + } + err := cli.NodeRemove(context.Background(), id, options) + assert.NilError(t, err) +} + +// UpdateNode updates a swarm node with the specified node constructor +func (d *Daemon) UpdateNode(t testing.TB, id string, f ...NodeConstructor) { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + for i := 0; ; i++ { + node := d.GetNode(t, id) + for _, fn := range f { + fn(node) + } + + err := cli.NodeUpdate(context.Background(), node.ID, node.Version, node.Spec) + if i < 10 && err != nil && strings.Contains(err.Error(), "update out of sequence") { + time.Sleep(100 * time.Millisecond) + continue + } + assert.NilError(t, err) + return + } +} + +// ListNodes returns the list of the current swarm nodes +func (d *Daemon) ListNodes(t testing.TB) []swarm.Node { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + nodes, err := cli.NodeList(context.Background(), types.NodeListOptions{}) + assert.NilError(t, err) + + return nodes +} diff --git a/testutil/daemon/ops.go b/testutil/daemon/ops.go new file mode 100644 index 0000000000000..c977dcef44f76 --- /dev/null +++ b/testutil/daemon/ops.go @@ -0,0 +1,124 @@ +package daemon + +import ( + "os/user" + + "github.com/docker/docker/testutil/environment" +) + +// Option is used to configure a daemon. +type Option func(*Daemon) + +// WithContainerdSocket sets the --containerd option on the daemon. +// Use an empty string to remove the option. +// +// If unset the --containerd option will be used with a default value. +func WithContainerdSocket(socket string) Option { + return func(d *Daemon) { + d.containerdSocket = socket + } +} + +// WithDefaultCgroupNamespaceMode sets the default cgroup namespace mode for the daemon +func WithDefaultCgroupNamespaceMode(mode string) Option { + return func(d *Daemon) { + d.defaultCgroupNamespaceMode = mode + } +} + +// WithTestLogger causes the daemon to log certain actions to the provided test. +func WithTestLogger(t LogT) Option { + return func(d *Daemon) { + d.log = t + } +} + +// WithExperimental sets the daemon in experimental mode +func WithExperimental() Option { + return func(d *Daemon) { + d.experimental = true + } +} + +// WithInit sets the daemon init +func WithInit() Option { + return func(d *Daemon) { + d.init = true + } +} + +// WithDockerdBinary sets the dockerd binary to the specified one +func WithDockerdBinary(dockerdBinary string) Option { + return func(d *Daemon) { + d.dockerdBinary = dockerdBinary + } +} + +// WithSwarmPort sets the swarm port to use for swarm mode +func WithSwarmPort(port int) Option { + return func(d *Daemon) { + d.SwarmPort = port + } +} + +// WithSwarmListenAddr sets the swarm listen addr to use for swarm mode +func WithSwarmListenAddr(listenAddr string) Option { + return func(d *Daemon) { + d.swarmListenAddr = listenAddr + } +} + +// WithSwarmDefaultAddrPool sets the swarm default address pool to use for swarm mode +func WithSwarmDefaultAddrPool(defaultAddrPool []string) Option { + return func(d *Daemon) { + d.DefaultAddrPool = defaultAddrPool + } +} + +// WithSwarmDefaultAddrPoolSubnetSize sets the subnet length mask of swarm default address pool to use for swarm mode +func WithSwarmDefaultAddrPoolSubnetSize(subnetSize uint32) Option { + return func(d *Daemon) { + d.SubnetSize = subnetSize + } +} + +// WithSwarmDataPathPort sets the swarm datapath port to use for swarm mode +func WithSwarmDataPathPort(datapathPort uint32) Option { + return func(d *Daemon) { + d.DataPathPort = datapathPort + } +} + +// WithEnvironment sets options from testutil/environment.Execution struct +func WithEnvironment(e environment.Execution) Option { + return func(d *Daemon) { + if e.DaemonInfo.ExperimentalBuild { + d.experimental = true + } + } +} + +// WithStorageDriver sets store driver option +func WithStorageDriver(driver string) Option { + return func(d *Daemon) { + d.storageDriver = driver + } +} + +// WithRootlessUser sets the daemon to be rootless +func WithRootlessUser(username string) Option { + return func(d *Daemon) { + u, err := user.Lookup(username) + if err != nil { + panic(err) + } + d.rootlessUser = u + } +} + +// WithOOMScoreAdjust sets OOM score for the daemon +func WithOOMScoreAdjust(score int) Option { + return func(d *Daemon) { + d.OOMScoreAdjust = score + } +} diff --git a/testutil/daemon/plugin.go b/testutil/daemon/plugin.go new file mode 100644 index 0000000000000..98aa6063a902a --- /dev/null +++ b/testutil/daemon/plugin.go @@ -0,0 +1,75 @@ +package daemon + +import ( + "context" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "gotest.tools/v3/poll" +) + +// PluginIsRunning provides a poller to check if the specified plugin is running +func (d *Daemon) PluginIsRunning(t testing.TB, name string) func(poll.LogT) poll.Result { + return withClient(t, d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result { + if plugin.Enabled { + return poll.Success() + } + return poll.Continue("plugin %q is not enabled", name) + })) +} + +// PluginIsNotRunning provides a poller to check if the specified plugin is not running +func (d *Daemon) PluginIsNotRunning(t testing.TB, name string) func(poll.LogT) poll.Result { + return withClient(t, d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result { + if !plugin.Enabled { + return poll.Success() + } + return poll.Continue("plugin %q is enabled", name) + })) +} + +// PluginIsNotPresent provides a poller to check if the specified plugin is not present +func (d *Daemon) PluginIsNotPresent(t testing.TB, name string) func(poll.LogT) poll.Result { + return withClient(t, d, func(c client.APIClient, t poll.LogT) poll.Result { + _, _, err := c.PluginInspectWithRaw(context.Background(), name) + if client.IsErrNotFound(err) { + return poll.Success() + } + if err != nil { + return poll.Error(err) + } + return poll.Continue("plugin %q exists", name) + }) +} + +// PluginReferenceIs provides a poller to check if the specified plugin has the specified reference +func (d *Daemon) PluginReferenceIs(t testing.TB, name, expectedRef string) func(poll.LogT) poll.Result { + return withClient(t, d, withPluginInspect(name, func(plugin *types.Plugin, t poll.LogT) poll.Result { + if plugin.PluginReference == expectedRef { + return poll.Success() + } + return poll.Continue("plugin %q reference is not %q", name, expectedRef) + })) +} + +func withPluginInspect(name string, f func(*types.Plugin, poll.LogT) poll.Result) func(client.APIClient, poll.LogT) poll.Result { + return func(c client.APIClient, t poll.LogT) poll.Result { + plugin, _, err := c.PluginInspectWithRaw(context.Background(), name) + if client.IsErrNotFound(err) { + return poll.Continue("plugin %q not found", name) + } + if err != nil { + return poll.Error(err) + } + return f(plugin, t) + } + +} + +func withClient(t testing.TB, d *Daemon, f func(client.APIClient, poll.LogT) poll.Result) func(poll.LogT) poll.Result { + return func(pt poll.LogT) poll.Result { + c := d.NewClientT(t) + return f(c, pt) + } +} diff --git a/testutil/daemon/secret.go b/testutil/daemon/secret.go new file mode 100644 index 0000000000000..099fdf33f1184 --- /dev/null +++ b/testutil/daemon/secret.go @@ -0,0 +1,74 @@ +package daemon + +import ( + "context" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" + "gotest.tools/v3/assert" +) + +// SecretConstructor defines a swarm secret constructor +type SecretConstructor func(*swarm.Secret) + +// CreateSecret creates a secret given the specified spec +func (d *Daemon) CreateSecret(t testing.TB, secretSpec swarm.SecretSpec) string { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + scr, err := cli.SecretCreate(context.Background(), secretSpec) + assert.NilError(t, err) + + return scr.ID +} + +// ListSecrets returns the list of the current swarm secrets +func (d *Daemon) ListSecrets(t testing.TB) []swarm.Secret { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + secrets, err := cli.SecretList(context.Background(), types.SecretListOptions{}) + assert.NilError(t, err) + return secrets +} + +// GetSecret returns a swarm secret identified by the specified id +func (d *Daemon) GetSecret(t testing.TB, id string) *swarm.Secret { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + secret, _, err := cli.SecretInspectWithRaw(context.Background(), id) + assert.NilError(t, err) + return &secret +} + +// DeleteSecret removes the swarm secret identified by the specified id +func (d *Daemon) DeleteSecret(t testing.TB, id string) { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + err := cli.SecretRemove(context.Background(), id) + assert.NilError(t, err) +} + +// UpdateSecret updates the swarm secret identified by the specified id +// Currently, only label update is supported. +func (d *Daemon) UpdateSecret(t testing.TB, id string, f ...SecretConstructor) { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + secret := d.GetSecret(t, id) + for _, fn := range f { + fn(secret) + } + + err := cli.SecretUpdate(context.Background(), secret.ID, secret.Version, secret.Spec) + + assert.NilError(t, err) +} diff --git a/testutil/daemon/service.go b/testutil/daemon/service.go new file mode 100644 index 0000000000000..0fb49b5f5b395 --- /dev/null +++ b/testutil/daemon/service.go @@ -0,0 +1,118 @@ +package daemon + +import ( + "context" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" + "gotest.tools/v3/assert" +) + +// ServiceConstructor defines a swarm service constructor function +type ServiceConstructor func(*swarm.Service) + +func (d *Daemon) createServiceWithOptions(t testing.TB, opts types.ServiceCreateOptions, f ...ServiceConstructor) string { + t.Helper() + var service swarm.Service + for _, fn := range f { + fn(&service) + } + + cli := d.NewClientT(t) + defer cli.Close() + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + res, err := cli.ServiceCreate(ctx, service.Spec, opts) + assert.NilError(t, err) + return res.ID +} + +// CreateService creates a swarm service given the specified service constructor +func (d *Daemon) CreateService(t testing.TB, f ...ServiceConstructor) string { + t.Helper() + return d.createServiceWithOptions(t, types.ServiceCreateOptions{}, f...) +} + +// GetService returns the swarm service corresponding to the specified id +func (d *Daemon) GetService(t testing.TB, id string) *swarm.Service { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + service, _, err := cli.ServiceInspectWithRaw(context.Background(), id, types.ServiceInspectOptions{}) + assert.NilError(t, err) + return &service +} + +// GetServiceTasks returns the swarm tasks for the specified service +func (d *Daemon) GetServiceTasks(t testing.TB, service string, additionalFilters ...filters.KeyValuePair) []swarm.Task { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + filterArgs := filters.NewArgs() + filterArgs.Add("desired-state", "running") + filterArgs.Add("service", service) + for _, filter := range additionalFilters { + filterArgs.Add(filter.Key, filter.Value) + } + + options := types.TaskListOptions{ + Filters: filterArgs, + } + + tasks, err := cli.TaskList(context.Background(), options) + assert.NilError(t, err) + return tasks +} + +// UpdateService updates a swarm service with the specified service constructor +func (d *Daemon) UpdateService(t testing.TB, service *swarm.Service, f ...ServiceConstructor) { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + for _, fn := range f { + fn(service) + } + + _, err := cli.ServiceUpdate(context.Background(), service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{}) + assert.NilError(t, err) +} + +// RemoveService removes the specified service +func (d *Daemon) RemoveService(t testing.TB, id string) { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + err := cli.ServiceRemove(context.Background(), id) + assert.NilError(t, err) +} + +// ListServices returns the list of the current swarm services +func (d *Daemon) ListServices(t testing.TB) []swarm.Service { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + services, err := cli.ServiceList(context.Background(), types.ServiceListOptions{}) + assert.NilError(t, err) + return services +} + +// GetTask returns the swarm task identified by the specified id +func (d *Daemon) GetTask(t testing.TB, id string) swarm.Task { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + task, _, err := cli.TaskInspectWithRaw(context.Background(), id) + assert.NilError(t, err) + return task +} diff --git a/testutil/daemon/swarm.go b/testutil/daemon/swarm.go new file mode 100644 index 0000000000000..8746a0e8a6fc4 --- /dev/null +++ b/testutil/daemon/swarm.go @@ -0,0 +1,201 @@ +package daemon + +import ( + "context" + "fmt" + "testing" + + "github.com/docker/docker/api/types/swarm" + "github.com/pkg/errors" + "gotest.tools/v3/assert" +) + +const ( + // DefaultSwarmPort is the default port use for swarm in the tests + DefaultSwarmPort = 2477 + defaultSwarmListenAddr = "0.0.0.0" +) + +var ( + startArgs = []string{"--iptables=false", "--swarm-default-advertise-addr=lo"} +) + +// StartNode (re)starts the daemon +func (d *Daemon) StartNode(t testing.TB) { + t.Helper() + d.Start(t, startArgs...) +} + +// StartNodeWithBusybox starts daemon to be used as a swarm node, and loads the busybox image +func (d *Daemon) StartNodeWithBusybox(t testing.TB) { + t.Helper() + d.StartWithBusybox(t, startArgs...) +} + +// RestartNode restarts a daemon to be used as a swarm node +func (d *Daemon) RestartNode(t testing.TB) { + t.Helper() + // avoid networking conflicts + d.Stop(t) + d.Start(t, startArgs...) +} + +// StartAndSwarmInit starts the daemon (with busybox) and init the swarm +func (d *Daemon) StartAndSwarmInit(t testing.TB) { + d.StartNodeWithBusybox(t) + d.SwarmInit(t, swarm.InitRequest{}) +} + +// StartAndSwarmJoin starts the daemon (with busybox) and join the specified swarm as worker or manager +func (d *Daemon) StartAndSwarmJoin(t testing.TB, leader *Daemon, manager bool) { + t.Helper() + d.StartNodeWithBusybox(t) + + tokens := leader.JoinTokens(t) + token := tokens.Worker + if manager { + token = tokens.Manager + } + t.Logf("[%s] joining swarm manager [%s]@%s, swarm listen addr %s", d.id, leader.id, leader.SwarmListenAddr(), d.SwarmListenAddr()) + d.SwarmJoin(t, swarm.JoinRequest{ + RemoteAddrs: []string{leader.SwarmListenAddr()}, + JoinToken: token, + }) +} + +// SpecConstructor defines a swarm spec constructor +type SpecConstructor func(*swarm.Spec) + +// SwarmListenAddr returns the listen-addr used for the daemon +func (d *Daemon) SwarmListenAddr() string { + return fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort) +} + +// NodeID returns the swarm mode node ID +func (d *Daemon) NodeID() string { + return d.CachedInfo.Swarm.NodeID +} + +// SwarmInit initializes a new swarm cluster. +func (d *Daemon) SwarmInit(t testing.TB, req swarm.InitRequest) { + t.Helper() + if req.ListenAddr == "" { + req.ListenAddr = fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort) + } + if req.DefaultAddrPool == nil { + req.DefaultAddrPool = d.DefaultAddrPool + req.SubnetSize = d.SubnetSize + } + if d.DataPathPort > 0 { + req.DataPathPort = d.DataPathPort + } + cli := d.NewClientT(t) + defer cli.Close() + _, err := cli.SwarmInit(context.Background(), req) + assert.NilError(t, err, "initializing swarm") + d.CachedInfo = d.Info(t) +} + +// SwarmJoin joins a daemon to an existing cluster. +func (d *Daemon) SwarmJoin(t testing.TB, req swarm.JoinRequest) { + t.Helper() + if req.ListenAddr == "" { + req.ListenAddr = fmt.Sprintf("%s:%d", d.swarmListenAddr, d.SwarmPort) + } + cli := d.NewClientT(t) + defer cli.Close() + err := cli.SwarmJoin(context.Background(), req) + assert.NilError(t, err, "[%s] joining swarm", d.id) + d.CachedInfo = d.Info(t) +} + +// SwarmLeave forces daemon to leave current cluster. +// +// The passed in testing.TB is only used to validate that the client was successfully created +// Some tests rely on error checking the result of the actual unlock, so allow +// the error to be returned. +func (d *Daemon) SwarmLeave(t testing.TB, force bool) error { + cli := d.NewClientT(t) + defer cli.Close() + return cli.SwarmLeave(context.Background(), force) +} + +// SwarmInfo returns the swarm information of the daemon +func (d *Daemon) SwarmInfo(t testing.TB) swarm.Info { + t.Helper() + cli := d.NewClientT(t) + info, err := cli.Info(context.Background()) + assert.NilError(t, err, "get swarm info") + return info.Swarm +} + +// SwarmUnlock tries to unlock a locked swarm +// +// The passed in testing.TB is only used to validate that the client was successfully created +// Some tests rely on error checking the result of the actual unlock, so allow +// the error to be returned. +func (d *Daemon) SwarmUnlock(t testing.TB, req swarm.UnlockRequest) error { + cli := d.NewClientT(t) + defer cli.Close() + + err := cli.SwarmUnlock(context.Background(), req) + if err != nil { + err = errors.Wrap(err, "unlocking swarm") + } + return err +} + +// GetSwarm returns the current swarm object +func (d *Daemon) GetSwarm(t testing.TB) swarm.Swarm { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + sw, err := cli.SwarmInspect(context.Background()) + assert.NilError(t, err) + return sw +} + +// UpdateSwarm updates the current swarm object with the specified spec constructors +func (d *Daemon) UpdateSwarm(t testing.TB, f ...SpecConstructor) { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + sw := d.GetSwarm(t) + for _, fn := range f { + fn(&sw.Spec) + } + + err := cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, swarm.UpdateFlags{}) + assert.NilError(t, err) +} + +// RotateTokens update the swarm to rotate tokens +func (d *Daemon) RotateTokens(t testing.TB) { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + sw, err := cli.SwarmInspect(context.Background()) + assert.NilError(t, err) + + flags := swarm.UpdateFlags{ + RotateManagerToken: true, + RotateWorkerToken: true, + } + + err = cli.SwarmUpdate(context.Background(), sw.Version, sw.Spec, flags) + assert.NilError(t, err) +} + +// JoinTokens returns the current swarm join tokens +func (d *Daemon) JoinTokens(t testing.TB) swarm.JoinTokens { + t.Helper() + cli := d.NewClientT(t) + defer cli.Close() + + sw, err := cli.SwarmInspect(context.Background()) + assert.NilError(t, err) + return sw.JoinTokens +} diff --git a/testutil/doc.go b/testutil/doc.go new file mode 100644 index 0000000000000..1bf03ef3ff274 --- /dev/null +++ b/testutil/doc.go @@ -0,0 +1,2 @@ +// Package testutil contains common testing tasks like running dockerd. +package testutil // import "github.com/docker/docker/testutil" diff --git a/testutil/environment/clean.go b/testutil/environment/clean.go new file mode 100644 index 0000000000000..415615e8774e0 --- /dev/null +++ b/testutil/environment/clean.go @@ -0,0 +1,185 @@ +package environment // import "github.com/docker/docker/testutil/environment" + +import ( + "context" + "regexp" + "strings" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/client" + "gotest.tools/v3/assert" +) + +// Clean the environment, preserving protected objects (images, containers, ...) +// and removing everything else. It's meant to run after any tests so that they don't +// depend on each others. +func (e *Execution) Clean(t testing.TB) { + t.Helper() + client := e.APIClient() + + platform := e.OSType + if (platform != "windows") || (platform == "windows" && e.DaemonInfo.Isolation == "hyperv") { + unpauseAllContainers(t, client) + } + deleteAllContainers(t, client, e.protectedElements.containers) + deleteAllImages(t, client, e.protectedElements.images) + deleteAllVolumes(t, client, e.protectedElements.volumes) + deleteAllNetworks(t, client, platform, e.protectedElements.networks) + if platform == "linux" { + deleteAllPlugins(t, client, e.protectedElements.plugins) + } +} + +func unpauseAllContainers(t testing.TB, client client.ContainerAPIClient) { + t.Helper() + ctx := context.Background() + containers := getPausedContainers(ctx, t, client) + if len(containers) > 0 { + for _, container := range containers { + err := client.ContainerUnpause(ctx, container.ID) + assert.Check(t, err, "failed to unpause container %s", container.ID) + } + } +} + +func getPausedContainers(ctx context.Context, t testing.TB, client client.ContainerAPIClient) []types.Container { + t.Helper() + filter := filters.NewArgs() + filter.Add("status", "paused") + containers, err := client.ContainerList(ctx, types.ContainerListOptions{ + Filters: filter, + All: true, + }) + assert.Check(t, err, "failed to list containers") + return containers +} + +var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`) + +func deleteAllContainers(t testing.TB, apiclient client.ContainerAPIClient, protectedContainers map[string]struct{}) { + t.Helper() + ctx := context.Background() + containers := getAllContainers(ctx, t, apiclient) + if len(containers) == 0 { + return + } + + for _, container := range containers { + if _, ok := protectedContainers[container.ID]; ok { + continue + } + err := apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{ + Force: true, + RemoveVolumes: true, + }) + if err == nil || client.IsErrNotFound(err) || alreadyExists.MatchString(err.Error()) || isErrNotFoundSwarmClassic(err) { + continue + } + assert.Check(t, err, "failed to remove %s", container.ID) + } +} + +func getAllContainers(ctx context.Context, t testing.TB, client client.ContainerAPIClient) []types.Container { + t.Helper() + containers, err := client.ContainerList(ctx, types.ContainerListOptions{ + All: true, + }) + assert.Check(t, err, "failed to list containers") + return containers +} + +func deleteAllImages(t testing.TB, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) { + t.Helper() + images, err := apiclient.ImageList(context.Background(), types.ImageListOptions{}) + assert.Check(t, err, "failed to list images") + + ctx := context.Background() + for _, image := range images { + tags := tagsFromImageSummary(image) + if len(tags) == 0 { + removeImage(ctx, t, apiclient, image.ID) + continue + } + for _, tag := range tags { + if _, ok := protectedImages[tag]; !ok { + removeImage(ctx, t, apiclient, tag) + } + } + } +} + +func removeImage(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, ref string) { + t.Helper() + _, err := apiclient.ImageRemove(ctx, ref, types.ImageRemoveOptions{ + Force: true, + }) + if client.IsErrNotFound(err) { + return + } + assert.Check(t, err, "failed to remove image %s", ref) +} + +func deleteAllVolumes(t testing.TB, c client.VolumeAPIClient, protectedVolumes map[string]struct{}) { + t.Helper() + volumes, err := c.VolumeList(context.Background(), filters.Args{}) + assert.Check(t, err, "failed to list volumes") + + for _, v := range volumes.Volumes { + if _, ok := protectedVolumes[v.Name]; ok { + continue + } + err := c.VolumeRemove(context.Background(), v.Name, true) + // Docker EE may list volumes that no longer exist. + if isErrNotFoundSwarmClassic(err) { + continue + } + assert.Check(t, err, "failed to remove volume %s", v.Name) + } +} + +func deleteAllNetworks(t testing.TB, c client.NetworkAPIClient, daemonPlatform string, protectedNetworks map[string]struct{}) { + t.Helper() + networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{}) + assert.Check(t, err, "failed to list networks") + + for _, n := range networks { + if n.Name == "bridge" || n.Name == "none" || n.Name == "host" { + continue + } + if _, ok := protectedNetworks[n.ID]; ok { + continue + } + if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" { + // nat is a pre-defined network on Windows and cannot be removed + continue + } + err := c.NetworkRemove(context.Background(), n.ID) + assert.Check(t, err, "failed to remove network %s", n.ID) + } +} + +func deleteAllPlugins(t testing.TB, c client.PluginAPIClient, protectedPlugins map[string]struct{}) { + t.Helper() + plugins, err := c.PluginList(context.Background(), filters.Args{}) + // Docker EE does not allow cluster-wide plugin management. + if client.IsErrNotImplemented(err) { + return + } + assert.Check(t, err, "failed to list plugins") + + for _, p := range plugins { + if _, ok := protectedPlugins[p.Name]; ok { + continue + } + err := c.PluginRemove(context.Background(), p.Name, types.PluginRemoveOptions{Force: true}) + assert.Check(t, err, "failed to remove plugin %s", p.ID) + } +} + +// Swarm classic aggregates node errors and returns a 500 so we need to check +// the error string instead of just IsErrNotFound(). +func isErrNotFoundSwarmClassic(err error) bool { + return err != nil && strings.Contains(strings.ToLower(err.Error()), "no such") +} diff --git a/testutil/environment/environment.go b/testutil/environment/environment.go new file mode 100644 index 0000000000000..8af8ca8d6f1aa --- /dev/null +++ b/testutil/environment/environment.go @@ -0,0 +1,223 @@ +package environment // import "github.com/docker/docker/testutil/environment" + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/client" + "github.com/docker/docker/testutil/fixtures/load" + "github.com/pkg/errors" + "gotest.tools/v3/assert" +) + +// Execution contains information about the current test execution and daemon +// under test +type Execution struct { + client client.APIClient + DaemonInfo types.Info + OSType string + PlatformDefaults PlatformDefaults + protectedElements protectedElements +} + +// PlatformDefaults are defaults values for the platform of the daemon under test +type PlatformDefaults struct { + BaseImage string + VolumesConfigPath string + ContainerStoragePath string +} + +// New creates a new Execution struct +// This is configured using the env client (see client.FromEnv) +func New() (*Execution, error) { + c, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + return nil, errors.Wrapf(err, "failed to create client") + } + return FromClient(c) +} + +// FromClient creates a new Execution environment from the passed in client +func FromClient(c *client.Client) (*Execution, error) { + info, err := c.Info(context.Background()) + if err != nil { + return nil, errors.Wrapf(err, "failed to get info from daemon") + } + + osType := getOSType(info) + + return &Execution{ + client: c, + DaemonInfo: info, + OSType: osType, + PlatformDefaults: getPlatformDefaults(info, osType), + protectedElements: newProtectedElements(), + }, nil +} + +func getOSType(info types.Info) string { + // Docker EE does not set the OSType so allow the user to override this value. + userOsType := os.Getenv("TEST_OSTYPE") + if userOsType != "" { + return userOsType + } + return info.OSType +} + +func getPlatformDefaults(info types.Info, osType string) PlatformDefaults { + volumesPath := filepath.Join(info.DockerRootDir, "volumes") + containersPath := filepath.Join(info.DockerRootDir, "containers") + + switch osType { + case "linux": + return PlatformDefaults{ + BaseImage: "scratch", + VolumesConfigPath: toSlash(volumesPath), + ContainerStoragePath: toSlash(containersPath), + } + case "windows": + baseImage := "microsoft/windowsservercore" + if overrideBaseImage := os.Getenv("WINDOWS_BASE_IMAGE"); overrideBaseImage != "" { + baseImage = overrideBaseImage + if overrideBaseImageTag := os.Getenv("WINDOWS_BASE_IMAGE_TAG"); overrideBaseImageTag != "" { + baseImage = baseImage + ":" + overrideBaseImageTag + } + } + fmt.Println("INFO: Windows Base image is ", baseImage) + return PlatformDefaults{ + BaseImage: baseImage, + VolumesConfigPath: filepath.FromSlash(volumesPath), + ContainerStoragePath: filepath.FromSlash(containersPath), + } + default: + panic(fmt.Sprintf("unknown OSType for daemon: %s", osType)) + } +} + +// Make sure in context of daemon, not the local platform. Note we can't +// use filepath.FromSlash or ToSlash here as they are a no-op on Unix. +func toSlash(path string) string { + return strings.Replace(path, `\`, `/`, -1) +} + +// IsLocalDaemon is true if the daemon under test is on the same +// host as the test process. +// +// Deterministically working out the environment in which CI is running +// to evaluate whether the daemon is local or remote is not possible through +// a build tag. +// +// For example Windows to Linux CI under Jenkins tests the 64-bit +// Windows binary build with the daemon build tag, but calls a remote +// Linux daemon. +// +// We can't just say if Windows then assume the daemon is local as at +// some point, we will be testing the Windows CLI against a Windows daemon. +// +// Similarly, it will be perfectly valid to also run CLI tests from +// a Linux CLI (built with the daemon tag) against a Windows daemon. +func (e *Execution) IsLocalDaemon() bool { + return os.Getenv("DOCKER_REMOTE_DAEMON") == "" +} + +// IsRemoteDaemon is true if the daemon under test is on different host +// as the test process. +func (e *Execution) IsRemoteDaemon() bool { + return !e.IsLocalDaemon() +} + +// DaemonAPIVersion returns the negotiated daemon api version +func (e *Execution) DaemonAPIVersion() string { + version, err := e.APIClient().ServerVersion(context.TODO()) + if err != nil { + return "" + } + return version.APIVersion +} + +// Print the execution details to stdout +// TODO: print everything +func (e *Execution) Print() { + if e.IsLocalDaemon() { + fmt.Println("INFO: Testing against a local daemon") + } else { + fmt.Println("INFO: Testing against a remote daemon") + } +} + +// APIClient returns an APIClient connected to the daemon under test +func (e *Execution) APIClient() client.APIClient { + return e.client +} + +// IsUserNamespace returns whether the user namespace remapping is enabled +func (e *Execution) IsUserNamespace() bool { + root := os.Getenv("DOCKER_REMAP_ROOT") + return root != "" +} + +// RuntimeIsWindowsContainerd returns whether containerd runtime is used on Windows +func (e *Execution) RuntimeIsWindowsContainerd() bool { + return os.Getenv("DOCKER_WINDOWS_CONTAINERD_RUNTIME") == "1" +} + +// IsRootless returns whether the rootless mode is enabled +func (e *Execution) IsRootless() bool { + return os.Getenv("DOCKER_ROOTLESS") != "" +} + +// IsUserNamespaceInKernel returns whether the kernel supports user namespaces +func (e *Execution) IsUserNamespaceInKernel() bool { + if _, err := os.Stat("/proc/self/uid_map"); os.IsNotExist(err) { + /* + * This kernel-provided file only exists if user namespaces are + * supported + */ + return false + } + + // We need extra check on redhat based distributions + if f, err := os.Open("/sys/module/user_namespace/parameters/enable"); err == nil { + defer f.Close() + b := make([]byte, 1) + _, _ = f.Read(b) + return string(b) != "N" + } + + return true +} + +// HasExistingImage checks whether there is an image with the given reference. +// Note that this is done by filtering and then checking whether there were any +// results -- so ambiguous references might result in false-positives. +func (e *Execution) HasExistingImage(t testing.TB, reference string) bool { + client := e.APIClient() + filter := filters.NewArgs() + filter.Add("dangling", "false") + filter.Add("reference", reference) + imageList, err := client.ImageList(context.Background(), types.ImageListOptions{ + All: true, + Filters: filter, + }) + assert.NilError(t, err, "failed to list images") + + return len(imageList) > 0 +} + +// EnsureFrozenImagesLinux loads frozen test images into the daemon +// if they aren't already loaded +func EnsureFrozenImagesLinux(testEnv *Execution) error { + if testEnv.OSType == "linux" { + err := load.FrozenImagesLinux(testEnv.APIClient(), frozenImages...) + if err != nil { + return errors.Wrap(err, "error loading frozen images") + } + } + return nil +} diff --git a/testutil/environment/protect.go b/testutil/environment/protect.go new file mode 100644 index 0000000000000..d790106128fbf --- /dev/null +++ b/testutil/environment/protect.go @@ -0,0 +1,222 @@ +package environment + +import ( + "context" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + dclient "github.com/docker/docker/client" + "gotest.tools/v3/assert" +) + +var frozenImages = []string{"busybox:latest", "busybox:glibc", "hello-world:frozen", "debian:bullseye-slim"} + +type protectedElements struct { + containers map[string]struct{} + images map[string]struct{} + networks map[string]struct{} + plugins map[string]struct{} + volumes map[string]struct{} +} + +func newProtectedElements() protectedElements { + return protectedElements{ + containers: map[string]struct{}{}, + images: map[string]struct{}{}, + networks: map[string]struct{}{}, + plugins: map[string]struct{}{}, + volumes: map[string]struct{}{}, + } +} + +// ProtectAll protects the existing environment (containers, images, networks, +// volumes, and, on Linux, plugins) from being cleaned up at the end of test +// runs +func ProtectAll(t testing.TB, testEnv *Execution) { + t.Helper() + ProtectContainers(t, testEnv) + ProtectImages(t, testEnv) + ProtectNetworks(t, testEnv) + ProtectVolumes(t, testEnv) + if testEnv.OSType == "linux" { + ProtectPlugins(t, testEnv) + } +} + +// ProtectContainer adds the specified container(s) to be protected in case of +// clean +func (e *Execution) ProtectContainer(t testing.TB, containers ...string) { + t.Helper() + for _, container := range containers { + e.protectedElements.containers[container] = struct{}{} + } +} + +// ProtectContainers protects existing containers from being cleaned up at the +// end of test runs +func ProtectContainers(t testing.TB, testEnv *Execution) { + t.Helper() + containers := getExistingContainers(t, testEnv) + testEnv.ProtectContainer(t, containers...) +} + +func getExistingContainers(t testing.TB, testEnv *Execution) []string { + t.Helper() + client := testEnv.APIClient() + containerList, err := client.ContainerList(context.Background(), types.ContainerListOptions{ + All: true, + }) + assert.NilError(t, err, "failed to list containers") + + var containers []string + for _, container := range containerList { + containers = append(containers, container.ID) + } + return containers +} + +// ProtectImage adds the specified image(s) to be protected in case of clean +func (e *Execution) ProtectImage(t testing.TB, images ...string) { + t.Helper() + for _, image := range images { + e.protectedElements.images[image] = struct{}{} + } +} + +// ProtectImages protects existing images and on linux frozen images from being +// cleaned up at the end of test runs +func ProtectImages(t testing.TB, testEnv *Execution) { + t.Helper() + images := getExistingImages(t, testEnv) + + if testEnv.OSType == "linux" { + images = append(images, frozenImages...) + } + testEnv.ProtectImage(t, images...) +} + +func getExistingImages(t testing.TB, testEnv *Execution) []string { + t.Helper() + client := testEnv.APIClient() + filter := filters.NewArgs() + filter.Add("dangling", "false") + imageList, err := client.ImageList(context.Background(), types.ImageListOptions{ + All: true, + Filters: filter, + }) + assert.NilError(t, err, "failed to list images") + + var images []string + for _, image := range imageList { + images = append(images, tagsFromImageSummary(image)...) + } + return images +} + +func tagsFromImageSummary(image types.ImageSummary) []string { + var result []string + for _, tag := range image.RepoTags { + if tag != ":" { + result = append(result, tag) + } + } + for _, digest := range image.RepoDigests { + if digest != "@" { + result = append(result, digest) + } + } + return result +} + +// ProtectNetwork adds the specified network(s) to be protected in case of +// clean +func (e *Execution) ProtectNetwork(t testing.TB, networks ...string) { + t.Helper() + for _, network := range networks { + e.protectedElements.networks[network] = struct{}{} + } +} + +// ProtectNetworks protects existing networks from being cleaned up at the end +// of test runs +func ProtectNetworks(t testing.TB, testEnv *Execution) { + t.Helper() + networks := getExistingNetworks(t, testEnv) + testEnv.ProtectNetwork(t, networks...) +} + +func getExistingNetworks(t testing.TB, testEnv *Execution) []string { + t.Helper() + client := testEnv.APIClient() + networkList, err := client.NetworkList(context.Background(), types.NetworkListOptions{}) + assert.NilError(t, err, "failed to list networks") + + var networks []string + for _, network := range networkList { + networks = append(networks, network.ID) + } + return networks +} + +// ProtectPlugin adds the specified plugin(s) to be protected in case of clean +func (e *Execution) ProtectPlugin(t testing.TB, plugins ...string) { + t.Helper() + for _, plugin := range plugins { + e.protectedElements.plugins[plugin] = struct{}{} + } +} + +// ProtectPlugins protects existing plugins from being cleaned up at the end of +// test runs +func ProtectPlugins(t testing.TB, testEnv *Execution) { + t.Helper() + plugins := getExistingPlugins(t, testEnv) + testEnv.ProtectPlugin(t, plugins...) +} + +func getExistingPlugins(t testing.TB, testEnv *Execution) []string { + t.Helper() + client := testEnv.APIClient() + pluginList, err := client.PluginList(context.Background(), filters.Args{}) + // Docker EE does not allow cluster-wide plugin management. + if dclient.IsErrNotImplemented(err) { + return []string{} + } + assert.NilError(t, err, "failed to list plugins") + + var plugins []string + for _, plugin := range pluginList { + plugins = append(plugins, plugin.Name) + } + return plugins +} + +// ProtectVolume adds the specified volume(s) to be protected in case of clean +func (e *Execution) ProtectVolume(t testing.TB, volumes ...string) { + t.Helper() + for _, volume := range volumes { + e.protectedElements.volumes[volume] = struct{}{} + } +} + +// ProtectVolumes protects existing volumes from being cleaned up at the end of +// test runs +func ProtectVolumes(t testing.TB, testEnv *Execution) { + t.Helper() + volumes := getExistingVolumes(t, testEnv) + testEnv.ProtectVolume(t, volumes...) +} + +func getExistingVolumes(t testing.TB, testEnv *Execution) []string { + t.Helper() + client := testEnv.APIClient() + volumeList, err := client.VolumeList(context.Background(), filters.Args{}) + assert.NilError(t, err, "failed to list volumes") + + var volumes []string + for _, volume := range volumeList.Volumes { + volumes = append(volumes, volume.Name) + } + return volumes +} diff --git a/testutil/fakecontext/context.go b/testutil/fakecontext/context.go new file mode 100644 index 0000000000000..7ab37cfe0ab55 --- /dev/null +++ b/testutil/fakecontext/context.go @@ -0,0 +1,121 @@ +package fakecontext // import "github.com/docker/docker/testutil/fakecontext" + +import ( + "bytes" + "io" + "os" + "path/filepath" + "testing" + + "github.com/docker/docker/pkg/archive" +) + +// New creates a fake build context +func New(t testing.TB, dir string, modifiers ...func(*Fake) error) *Fake { + t.Helper() + fakeContext := &Fake{Dir: dir} + if dir == "" { + if err := newDir(fakeContext); err != nil { + t.Fatal(err) + } + } + + for _, modifier := range modifiers { + if err := modifier(fakeContext); err != nil { + t.Fatal(err) + } + } + + return fakeContext +} + +func newDir(fake *Fake) error { + tmp, err := os.MkdirTemp("", "fake-context") + if err != nil { + return err + } + if err := os.Chmod(tmp, 0755); err != nil { + return err + } + fake.Dir = tmp + return nil +} + +// WithFile adds the specified file (with content) in the build context +func WithFile(name, content string) func(*Fake) error { + return func(ctx *Fake) error { + return ctx.Add(name, content) + } +} + +// WithDockerfile adds the specified content as Dockerfile in the build context +func WithDockerfile(content string) func(*Fake) error { + return WithFile("Dockerfile", content) +} + +// WithFiles adds the specified files in the build context, content is a string +func WithFiles(files map[string]string) func(*Fake) error { + return func(fakeContext *Fake) error { + for file, content := range files { + if err := fakeContext.Add(file, content); err != nil { + return err + } + } + return nil + } +} + +// WithBinaryFiles adds the specified files in the build context, content is binary +func WithBinaryFiles(files map[string]*bytes.Buffer) func(*Fake) error { + return func(fakeContext *Fake) error { + for file, content := range files { + if err := fakeContext.Add(file, content.String()); err != nil { + return err + } + } + return nil + } +} + +// Fake creates directories that can be used as a build context +type Fake struct { + Dir string +} + +// Add a file at a path, creating directories where necessary +func (f *Fake) Add(file, content string) error { + return f.addFile(file, []byte(content)) +} + +func (f *Fake) addFile(file string, content []byte) error { + fp := filepath.Join(f.Dir, filepath.FromSlash(file)) + dirpath := filepath.Dir(fp) + if dirpath != "." { + if err := os.MkdirAll(dirpath, 0755); err != nil { + return err + } + } + return os.WriteFile(fp, content, 0644) + +} + +// Delete a file at a path +func (f *Fake) Delete(file string) error { + fp := filepath.Join(f.Dir, filepath.FromSlash(file)) + return os.RemoveAll(fp) +} + +// Close deletes the context +func (f *Fake) Close() error { + return os.RemoveAll(f.Dir) +} + +// AsTarReader returns a ReadCloser with the contents of Dir as a tar archive. +func (f *Fake) AsTarReader(t testing.TB) io.ReadCloser { + t.Helper() + reader, err := archive.TarWithOptions(f.Dir, &archive.TarOptions{}) + if err != nil { + t.Fatalf("Failed to create tar from %s: %s", f.Dir, err) + } + return reader +} diff --git a/internal/test/fakegit/fakegit.go b/testutil/fakegit/fakegit.go similarity index 80% rename from internal/test/fakegit/fakegit.go rename to testutil/fakegit/fakegit.go index 605d1baaa8337..33594dc8a870a 100644 --- a/internal/test/fakegit/fakegit.go +++ b/testutil/fakegit/fakegit.go @@ -1,36 +1,18 @@ -package fakegit // import "github.com/docker/docker/internal/test/fakegit" +package fakegit // import "github.com/docker/docker/testutil/fakegit" import ( "fmt" - "io/ioutil" "net/http" "net/http/httptest" "os" "os/exec" "path/filepath" + "testing" - "github.com/docker/docker/internal/test" - "github.com/docker/docker/internal/test/fakecontext" - "github.com/docker/docker/internal/test/fakestorage" - "gotest.tools/assert" + "github.com/docker/docker/testutil/fakecontext" + "github.com/docker/docker/testutil/fakestorage" ) -type testingT interface { - assert.TestingT - logT - skipT - Fatal(args ...interface{}) - Fatalf(string, ...interface{}) -} - -type logT interface { - Logf(string, ...interface{}) -} - -type skipT interface { - Skip(reason string) -} - type gitServer interface { URL() string Close() error @@ -63,10 +45,8 @@ func (g *FakeGit) Close() { } // New create a fake git server that can be used for git related tests -func New(c testingT, name string, files map[string]string, enforceLocalServer bool) *FakeGit { - if ht, ok := c.(test.HelperT); ok { - ht.Helper() - } +func New(c testing.TB, name string, files map[string]string, enforceLocalServer bool) *FakeGit { + c.Helper() ctx := fakecontext.New(c, "", fakecontext.WithFiles(files)) defer ctx.Close() curdir, err := os.Getwd() @@ -95,7 +75,7 @@ func New(c testingT, name string, files map[string]string, enforceLocalServer bo c.Fatalf("error trying to commit to repo: %s (%s)", err, output) } - root, err := ioutil.TempDir("", "docker-test-git-repo") + root, err := os.MkdirTemp("", "docker-test-git-repo") if err != nil { c.Fatal(err) } diff --git a/internal/test/fakestorage/fixtures.go b/testutil/fakestorage/fixtures.go similarity index 84% rename from internal/test/fakestorage/fixtures.go rename to testutil/fakestorage/fixtures.go index ad8f76314338a..e11bee3b2815d 100644 --- a/internal/test/fakestorage/fixtures.go +++ b/testutil/fakestorage/fixtures.go @@ -1,26 +1,23 @@ -package fakestorage // import "github.com/docker/docker/internal/test/fakestorage" +package fakestorage // import "github.com/docker/docker/testutil/fakestorage" import ( "context" "io" - "io/ioutil" "os" "os/exec" "path/filepath" "sync" + "testing" "github.com/docker/docker/api/types" - "github.com/docker/docker/internal/test" "github.com/docker/docker/pkg/archive" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) var ensureHTTPServerOnce sync.Once -func ensureHTTPServerImage(t testingT) { - if ht, ok := t.(test.HelperT); ok { - ht.Helper() - } +func ensureHTTPServerImage(t testing.TB) { + t.Helper() var doIt bool ensureHTTPServerOnce.Do(func() { doIt = true @@ -32,7 +29,7 @@ func ensureHTTPServerImage(t testingT) { defer testEnv.ProtectImage(t, "httpserver:latest") - tmp, err := ioutil.TempDir("", "docker-http-server-test") + tmp, err := os.MkdirTemp("", "docker-http-server-test") if err != nil { t.Fatalf("could not build http server: %v", err) } @@ -87,6 +84,6 @@ func ensureHTTPServerImage(t testingT) { Tags: []string{"httpserver"}, }) assert.NilError(t, err) - _, err = io.Copy(ioutil.Discard, resp.Body) + _, err = io.Copy(io.Discard, resp.Body) assert.NilError(t, err) } diff --git a/testutil/fakestorage/storage.go b/testutil/fakestorage/storage.go new file mode 100644 index 0000000000000..8bcf1d8863258 --- /dev/null +++ b/testutil/fakestorage/storage.go @@ -0,0 +1,181 @@ +package fakestorage // import "github.com/docker/docker/testutil/fakestorage" + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "os" + "strings" + "testing" + + "github.com/docker/docker/api/types" + containertypes "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + "github.com/docker/docker/testutil" + "github.com/docker/docker/testutil/environment" + "github.com/docker/docker/testutil/fakecontext" + "github.com/docker/docker/testutil/request" + "github.com/docker/go-connections/nat" + "gotest.tools/v3/assert" +) + +var testEnv *environment.Execution + +// Fake is a static file server. It might be running locally or remotely +// on test host. +type Fake interface { + Close() error + URL() string + CtxDir() string +} + +// SetTestEnvironment sets a static test environment +// TODO: decouple this package from environment +func SetTestEnvironment(env *environment.Execution) { + testEnv = env +} + +// New returns a static file server that will be use as build context. +func New(t testing.TB, dir string, modifiers ...func(*fakecontext.Fake) error) Fake { + t.Helper() + if testEnv == nil { + t.Fatal("fakstorage package requires SetTestEnvironment() to be called before use.") + } + ctx := fakecontext.New(t, dir, modifiers...) + switch { + case testEnv.IsRemoteDaemon() && strings.HasPrefix(request.DaemonHost(), "unix:///"): + t.Skip("e2e run : daemon is remote but docker host points to a unix socket") + case testEnv.IsLocalDaemon(): + return newLocalFakeStorage(ctx) + default: + return newRemoteFileServer(t, ctx, testEnv.APIClient()) + } + return nil +} + +// localFileStorage is a file storage on the running machine +type localFileStorage struct { + *fakecontext.Fake + *httptest.Server +} + +func (s *localFileStorage) URL() string { + return s.Server.URL +} + +func (s *localFileStorage) CtxDir() string { + return s.Fake.Dir +} + +func (s *localFileStorage) Close() error { + defer s.Server.Close() + return s.Fake.Close() +} + +func newLocalFakeStorage(ctx *fakecontext.Fake) *localFileStorage { + handler := http.FileServer(http.Dir(ctx.Dir)) + server := httptest.NewServer(handler) + return &localFileStorage{ + Fake: ctx, + Server: server, + } +} + +// remoteFileServer is a containerized static file server started on the remote +// testing machine to be used in URL-accepting docker build functionality. +type remoteFileServer struct { + host string // hostname/port web server is listening to on docker host e.g. 0.0.0.0:43712 + container string + image string + client client.APIClient + ctx *fakecontext.Fake +} + +func (f *remoteFileServer) URL() string { + u := url.URL{ + Scheme: "http", + Host: f.host} + return u.String() +} + +func (f *remoteFileServer) CtxDir() string { + return f.ctx.Dir +} + +func (f *remoteFileServer) Close() error { + defer func() { + if f.ctx != nil { + f.ctx.Close() + } + if f.image != "" { + if _, err := f.client.ImageRemove(context.Background(), f.image, types.ImageRemoveOptions{ + Force: true, + }); err != nil { + fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err) + } + } + if err := f.client.Close(); err != nil { + fmt.Fprintf(os.Stderr, "Error closing remote file server : %v\n", err) + } + }() + if f.container == "" { + return nil + } + return f.client.ContainerRemove(context.Background(), f.container, types.ContainerRemoveOptions{ + Force: true, + RemoveVolumes: true, + }) +} + +func newRemoteFileServer(t testing.TB, ctx *fakecontext.Fake, c client.APIClient) *remoteFileServer { + var ( + image = fmt.Sprintf("fileserver-img-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10))) + container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(testutil.GenerateRandomAlphaOnlyString(10))) + ) + + ensureHTTPServerImage(t) + + // Build the image + if err := ctx.Add("Dockerfile", `FROM httpserver +COPY . /static`); err != nil { + t.Fatal(err) + } + resp, err := c.ImageBuild(context.Background(), ctx.AsTarReader(t), types.ImageBuildOptions{ + NoCache: true, + Tags: []string{image}, + }) + assert.NilError(t, err) + _, err = io.Copy(io.Discard, resp.Body) + assert.NilError(t, err) + + // Start the container + b, err := c.ContainerCreate(context.Background(), &containertypes.Config{ + Image: image, + }, &containertypes.HostConfig{}, nil, nil, container) + assert.NilError(t, err) + err = c.ContainerStart(context.Background(), b.ID, types.ContainerStartOptions{}) + assert.NilError(t, err) + + // Find out the system assigned port + i, err := c.ContainerInspect(context.Background(), b.ID) + assert.NilError(t, err) + newP, err := nat.NewPort("tcp", "80") + assert.NilError(t, err) + ports, exists := i.NetworkSettings.Ports[newP] + if !exists || len(ports) != 1 { + t.Fatalf("unable to find port 80/tcp for %s", container) + } + host := ports[0].HostIP + port := ports[0].HostPort + + return &remoteFileServer{ + container: container, + image: image, + host: fmt.Sprintf("%s:%s", host, port), + ctx: ctx, + client: c, + } +} diff --git a/internal/test/fixtures/load/frozen.go b/testutil/fixtures/load/frozen.go similarity index 97% rename from internal/test/fixtures/load/frozen.go rename to testutil/fixtures/load/frozen.go index 94f3680f954d3..6ea591b4f224c 100644 --- a/internal/test/fixtures/load/frozen.go +++ b/testutil/fixtures/load/frozen.go @@ -1,4 +1,4 @@ -package load // import "github.com/docker/docker/internal/test/fixtures/load" +package load // import "github.com/docker/docker/testutil/fixtures/load" import ( "bufio" @@ -13,7 +13,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/client" "github.com/docker/docker/pkg/jsonmessage" - "github.com/docker/docker/pkg/term" + "github.com/moby/term" "github.com/pkg/errors" ) diff --git a/internal/test/fixtures/plugin/basic/basic.go b/testutil/fixtures/plugin/basic/basic.go similarity index 100% rename from internal/test/fixtures/plugin/basic/basic.go rename to testutil/fixtures/plugin/basic/basic.go diff --git a/testutil/fixtures/plugin/plugin.go b/testutil/fixtures/plugin/plugin.go new file mode 100644 index 0000000000000..81ebc41b645ff --- /dev/null +++ b/testutil/fixtures/plugin/plugin.go @@ -0,0 +1,229 @@ +package plugin // import "github.com/docker/docker/testutil/fixtures/plugin" + +import ( + "context" + "encoding/json" + "io" + "os" + "os/exec" + "path/filepath" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/archive" + "github.com/docker/docker/plugin" + "github.com/docker/docker/registry" + "github.com/pkg/errors" +) + +// CreateOpt is passed used to change the default plugin config before +// creating it +type CreateOpt func(*Config) + +// Config wraps types.PluginConfig to provide some extra state for options +// extra customizations on the plugin details, such as using a custom binary to +// create the plugin with. +type Config struct { + *types.PluginConfig + binPath string + RegistryConfig registry.ServiceOptions +} + +// WithInsecureRegistry specifies that the given registry can skip host-key checking as well as fall back to plain http +func WithInsecureRegistry(url string) CreateOpt { + return func(cfg *Config) { + cfg.RegistryConfig.InsecureRegistries = append(cfg.RegistryConfig.InsecureRegistries, url) + } +} + +// WithBinary is a CreateOpt to set an custom binary to create the plugin with. +// This binary must be statically compiled. +func WithBinary(bin string) CreateOpt { + return func(cfg *Config) { + cfg.binPath = bin + } +} + +// CreateClient is the interface used for `BuildPlugin` to interact with the +// daemon. +type CreateClient interface { + PluginCreate(context.Context, io.Reader, types.PluginCreateOptions) error +} + +// Create creates a new plugin with the specified name +func Create(ctx context.Context, c CreateClient, name string, opts ...CreateOpt) error { + tmpDir, err := os.MkdirTemp("", "create-test-plugin") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + tar, err := makePluginBundle(tmpDir, opts...) + if err != nil { + return err + } + defer tar.Close() + + ctx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + + return c.PluginCreate(ctx, tar, types.PluginCreateOptions{RepoName: name}) +} + +// CreateInRegistry makes a plugin (locally) and pushes it to a registry. +// This does not use a dockerd instance to create or push the plugin. +// If you just want to create a plugin in some daemon, use `Create`. +// +// This can be useful when testing plugins on swarm where you don't really want +// the plugin to exist on any of the daemons (immediately) and there needs to be +// some way to distribute the plugin. +func CreateInRegistry(ctx context.Context, repo string, auth *types.AuthConfig, opts ...CreateOpt) error { + tmpDir, err := os.MkdirTemp("", "create-test-plugin-local") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + inPath := filepath.Join(tmpDir, "plugin") + if err := os.MkdirAll(inPath, 0755); err != nil { + return errors.Wrap(err, "error creating plugin root") + } + + var cfg Config + cfg.PluginConfig = &types.PluginConfig{} + for _, o := range opts { + o(&cfg) + } + + tar, err := makePluginBundle(inPath, opts...) + if err != nil { + return err + } + defer tar.Close() + + dummyExec := func(m *plugin.Manager) (plugin.Executor, error) { + return nil, nil + } + + regService, err := registry.NewService(cfg.RegistryConfig) + if err != nil { + return err + } + + managerConfig := plugin.ManagerConfig{ + Store: plugin.NewStore(), + RegistryService: regService, + Root: filepath.Join(tmpDir, "root"), + ExecRoot: "/run/docker", // manager init fails if not set + CreateExecutor: dummyExec, + LogPluginEvent: func(id, name, action string) {}, // panics when not set + } + manager, err := plugin.NewManager(managerConfig) + if err != nil { + return errors.Wrap(err, "error creating plugin manager") + } + + ctx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + if err := manager.CreateFromContext(ctx, tar, &types.PluginCreateOptions{RepoName: repo}); err != nil { + return err + } + + if auth == nil { + auth = &types.AuthConfig{} + } + err = manager.Push(ctx, repo, nil, auth, io.Discard) + return errors.Wrap(err, "error pushing plugin") +} + +func makePluginBundle(inPath string, opts ...CreateOpt) (io.ReadCloser, error) { + p := &types.PluginConfig{ + Interface: types.PluginConfigInterface{ + Socket: "basic.sock", + Types: []types.PluginInterfaceType{{Capability: "docker.dummy/1.0"}}, + }, + Entrypoint: []string{"/basic"}, + } + cfg := &Config{ + PluginConfig: p, + } + for _, o := range opts { + o(cfg) + } + if cfg.binPath == "" { + binPath, err := ensureBasicPluginBin() + if err != nil { + return nil, err + } + cfg.binPath = binPath + } + + configJSON, err := json.Marshal(p) + if err != nil { + return nil, err + } + if err := os.WriteFile(filepath.Join(inPath, "config.json"), configJSON, 0644); err != nil { + return nil, err + } + if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(p.Entrypoint[0])), 0755); err != nil { + return nil, errors.Wrap(err, "error creating plugin rootfs dir") + } + + // Ensure the mount target paths exist + for _, m := range p.Mounts { + var stat os.FileInfo + if m.Source != nil { + stat, err = os.Stat(*m.Source) + if err != nil && !os.IsNotExist(err) { + return nil, err + } + } + + if stat == nil || stat.IsDir() { + var mode os.FileMode = 0755 + if stat != nil { + mode = stat.Mode() + } + if err := os.MkdirAll(filepath.Join(inPath, "rootfs", m.Destination), mode); err != nil { + return nil, errors.Wrap(err, "error preparing plugin mount destination path") + } + } else { + if err := os.MkdirAll(filepath.Join(inPath, "rootfs", filepath.Dir(m.Destination)), 0755); err != nil { + return nil, errors.Wrap(err, "error preparing plugin mount destination dir") + } + f, err := os.Create(filepath.Join(inPath, "rootfs", m.Destination)) + if err != nil && !os.IsExist(err) { + return nil, errors.Wrap(err, "error preparing plugin mount destination file") + } + if f != nil { + f.Close() + } + } + } + if err := archive.NewDefaultArchiver().CopyFileWithTar(cfg.binPath, filepath.Join(inPath, "rootfs", p.Entrypoint[0])); err != nil { + return nil, errors.Wrap(err, "error copying plugin binary to rootfs path") + } + tar, err := archive.Tar(inPath, archive.Uncompressed) + return tar, errors.Wrap(err, "error making plugin archive") +} + +func ensureBasicPluginBin() (string, error) { + name := "docker-basic-plugin" + p, err := exec.LookPath(name) + if err == nil { + return p, nil + } + + goBin, err := exec.LookPath("go") + if err != nil { + return "", err + } + installPath := filepath.Join(os.Getenv("GOPATH"), "bin", name) + sourcePath := filepath.Join("github.com", "docker", "docker", "testutil", "fixtures", "plugin", "basic") + cmd := exec.Command(goBin, "build", "-o", installPath, sourcePath) + cmd.Env = append(os.Environ(), "CGO_ENABLED=0", "GO111MODULE=off") + if out, err := cmd.CombinedOutput(); err != nil { + return "", errors.Wrapf(err, "error building basic plugin bin: %s", string(out)) + } + return installPath, nil +} diff --git a/testutil/helper.go b/testutil/helper.go new file mode 100644 index 0000000000000..a8a70e4dfbbfa --- /dev/null +++ b/testutil/helper.go @@ -0,0 +1,6 @@ +package testutil + +// HelperT is a subset of testing.T that implements the Helper function +type HelperT interface { + Helper() +} diff --git a/testutil/helpers.go b/testutil/helpers.go new file mode 100644 index 0000000000000..e522b5a9a4ecc --- /dev/null +++ b/testutil/helpers.go @@ -0,0 +1,17 @@ +package testutil // import "github.com/docker/docker/testutil" + +import ( + "io" +) + +// DevZero acts like /dev/zero but in an OS-independent fashion. +var DevZero io.Reader = devZero{} + +type devZero struct{} + +func (d devZero) Read(p []byte) (n int, err error) { + for i := range p { + p[i] = 0 + } + return len(p), nil +} diff --git a/testutil/registry/ops.go b/testutil/registry/ops.go new file mode 100644 index 0000000000000..7357d5f5098ae --- /dev/null +++ b/testutil/registry/ops.go @@ -0,0 +1,42 @@ +package registry + +import "io" + +// Schema1 sets the registry to serve v1 api +func Schema1(c *Config) { + c.schema1 = true +} + +// Htpasswd sets the auth method with htpasswd +func Htpasswd(c *Config) { + c.auth = "htpasswd" +} + +// Token sets the auth method to token, with the specified token url +func Token(tokenURL string) func(*Config) { + return func(c *Config) { + c.auth = "token" + c.tokenURL = tokenURL + } +} + +// URL sets the registry url +func URL(registryURL string) func(*Config) { + return func(c *Config) { + c.registryURL = registryURL + } +} + +// WithStdout sets the stdout of the registry command to the passed in writer. +func WithStdout(w io.Writer) func(c *Config) { + return func(c *Config) { + c.stdout = w + } +} + +// WithStderr sets the stdout of the registry command to the passed in writer. +func WithStderr(w io.Writer) func(c *Config) { + return func(c *Config) { + c.stderr = w + } +} diff --git a/testutil/registry/registry.go b/testutil/registry/registry.go new file mode 100644 index 0000000000000..5de61620c06f8 --- /dev/null +++ b/testutil/registry/registry.go @@ -0,0 +1,239 @@ +package registry // import "github.com/docker/docker/testutil/registry" + +import ( + "fmt" + "io" + "net/http" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + "github.com/opencontainers/go-digest" + "gotest.tools/v3/assert" +) + +const ( + // V2binary is the name of the registry v2 binary + V2binary = "registry-v2" + // V2binarySchema1 is the name of the registry that serve schema1 + V2binarySchema1 = "registry-v2-schema1" + // DefaultURL is the default url that will be used by the registry (if not specified otherwise) + DefaultURL = "127.0.0.1:5000" +) + +// V2 represent a registry version 2 +type V2 struct { + cmd *exec.Cmd + registryURL string + dir string + auth string + username string + password string + email string +} + +// Config contains the test registry configuration +type Config struct { + schema1 bool + auth string + tokenURL string + registryURL string + stdout io.Writer + stderr io.Writer +} + +// NewV2 creates a v2 registry server +func NewV2(t testing.TB, ops ...func(*Config)) *V2 { + t.Helper() + c := &Config{ + registryURL: DefaultURL, + } + for _, op := range ops { + op(c) + } + tmp, err := os.MkdirTemp("", "registry-test-") + assert.NilError(t, err) + template := `version: 0.1 +loglevel: debug +storage: + filesystem: + rootdirectory: %s +http: + addr: %s +%s` + var ( + authTemplate string + username string + password string + email string + ) + switch c.auth { + case "htpasswd": + htpasswdPath := filepath.Join(tmp, "htpasswd") + // generated with: htpasswd -Bbn testuser testpassword + // #nosec G101 + userpasswd := "testuser:$2y$05$sBsSqk0OpSD1uTZkHXc4FeJ0Z70wLQdAX/82UiHuQOKbNbBrzs63m" + username = "testuser" + password = "testpassword" + email = "test@test.org" + err := os.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644)) + assert.NilError(t, err) + authTemplate = fmt.Sprintf(`auth: + htpasswd: + realm: basic-realm + path: %s +`, htpasswdPath) + case "token": + authTemplate = fmt.Sprintf(`auth: + token: + realm: %s + service: "registry" + issuer: "auth-registry" + rootcertbundle: "fixtures/registry/cert.pem" +`, c.tokenURL) + } + + confPath := filepath.Join(tmp, "config.yaml") + config, err := os.Create(confPath) + assert.NilError(t, err) + defer config.Close() + + if _, err := fmt.Fprintf(config, template, tmp, c.registryURL, authTemplate); err != nil { + // FIXME(vdemeester) use a defer/clean func + os.RemoveAll(tmp) + t.Fatal(err) + } + + binary := V2binary + if c.schema1 { + binary = V2binarySchema1 + } + cmd := exec.Command(binary, confPath) + cmd.Stdout = c.stdout + cmd.Stderr = c.stderr + if err := cmd.Start(); err != nil { + // FIXME(vdemeester) use a defer/clean func + os.RemoveAll(tmp) + t.Fatal(err) + } + return &V2{ + cmd: cmd, + dir: tmp, + auth: c.auth, + username: username, + password: password, + email: email, + registryURL: c.registryURL, + } +} + +// WaitReady waits for the registry to be ready to serve requests (or fail after a while) +func (r *V2) WaitReady(t testing.TB) { + t.Helper() + var err error + for i := 0; i != 50; i++ { + if err = r.Ping(); err == nil { + return + } + time.Sleep(100 * time.Millisecond) + } + t.Fatalf("timeout waiting for test registry to become available: %v", err) +} + +// Ping sends an http request to the current registry, and fail if it doesn't respond correctly +func (r *V2) Ping() error { + // We always ping through HTTP for our test registry. + resp, err := http.Get(fmt.Sprintf("http://%s/v2/", r.registryURL)) + if err != nil { + return err + } + resp.Body.Close() + + fail := resp.StatusCode != http.StatusOK + if r.auth != "" { + // unauthorized is a _good_ status when pinging v2/ and it needs auth + fail = fail && resp.StatusCode != http.StatusUnauthorized + } + if fail { + return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode) + } + return nil +} + +// Close kills the registry server +func (r *V2) Close() { + r.cmd.Process.Kill() + r.cmd.Process.Wait() + os.RemoveAll(r.dir) +} + +func (r *V2) getBlobFilename(blobDigest digest.Digest) string { + // Split the digest into its algorithm and hex components. + dgstAlg, dgstHex := blobDigest.Algorithm(), blobDigest.Hex() + + // The path to the target blob data looks something like: + // baseDir + "docker/registry/v2/blobs/sha256/a3/a3ed...46d4/data" + return fmt.Sprintf("%s/docker/registry/v2/blobs/%s/%s/%s/data", r.dir, dgstAlg, dgstHex[:2], dgstHex) +} + +// ReadBlobContents read the file corresponding to the specified digest +func (r *V2) ReadBlobContents(t testing.TB, blobDigest digest.Digest) []byte { + t.Helper() + // Load the target manifest blob. + manifestBlob, err := os.ReadFile(r.getBlobFilename(blobDigest)) + assert.NilError(t, err, "unable to read blob") + return manifestBlob +} + +// WriteBlobContents write the file corresponding to the specified digest with the given content +func (r *V2) WriteBlobContents(t testing.TB, blobDigest digest.Digest, data []byte) { + t.Helper() + err := os.WriteFile(r.getBlobFilename(blobDigest), data, os.FileMode(0644)) + assert.NilError(t, err, "unable to write malicious data blob") +} + +// TempMoveBlobData moves the existing data file aside, so that we can replace it with a +// malicious blob of data for example. +func (r *V2) TempMoveBlobData(t testing.TB, blobDigest digest.Digest) (undo func()) { + t.Helper() + tempFile, err := os.CreateTemp("", "registry-temp-blob-") + assert.NilError(t, err, "unable to get temporary blob file") + tempFile.Close() + + blobFilename := r.getBlobFilename(blobDigest) + + // Move the existing data file aside, so that we can replace it with a + // another blob of data. + if err := os.Rename(blobFilename, tempFile.Name()); err != nil { + // FIXME(vdemeester) use a defer/clean func + os.Remove(tempFile.Name()) + t.Fatalf("unable to move data blob: %s", err) + } + + return func() { + os.Rename(tempFile.Name(), blobFilename) + os.Remove(tempFile.Name()) + } +} + +// Username returns the configured user name of the server +func (r *V2) Username() string { + return r.username +} + +// Password returns the configured password of the server +func (r *V2) Password() string { + return r.password +} + +// Email returns the configured email of the server +func (r *V2) Email() string { + return r.email +} + +// Path returns the path where the registry write data +func (r *V2) Path() string { + return filepath.Join(r.dir, "docker", "registry", "v2") +} diff --git a/internal/test/registry/registry_mock.go b/testutil/registry/registry_mock.go similarity index 85% rename from internal/test/registry/registry_mock.go rename to testutil/registry/registry_mock.go index d139401a62036..540cef5d41e28 100644 --- a/internal/test/registry/registry_mock.go +++ b/testutil/registry/registry_mock.go @@ -1,4 +1,4 @@ -package registry // import "github.com/docker/docker/internal/test/registry" +package registry // import "github.com/docker/docker/testutil/registry" import ( "net/http" @@ -6,8 +6,7 @@ import ( "regexp" "strings" "sync" - - "github.com/docker/docker/internal/test" + "testing" ) type handlerFunc func(w http.ResponseWriter, r *http.Request) @@ -28,10 +27,8 @@ func (tr *Mock) RegisterHandler(path string, h handlerFunc) { } // NewMock creates a registry mock -func NewMock(t testingT) (*Mock, error) { - if ht, ok := t.(test.HelperT); ok { - ht.Helper() - } +func NewMock(t testing.TB) (*Mock, error) { + t.Helper() testReg := &Mock{handlers: make(map[string]handlerFunc)} ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/internal/test/request/npipe.go b/testutil/request/npipe.go similarity index 90% rename from internal/test/request/npipe.go rename to testutil/request/npipe.go index e6ab03945e2bb..e827ad6b80060 100644 --- a/internal/test/request/npipe.go +++ b/testutil/request/npipe.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package request diff --git a/internal/test/request/npipe_windows.go b/testutil/request/npipe_windows.go similarity index 80% rename from internal/test/request/npipe_windows.go rename to testutil/request/npipe_windows.go index a268aac922f0d..9741ae64c6534 100644 --- a/internal/test/request/npipe_windows.go +++ b/testutil/request/npipe_windows.go @@ -4,7 +4,7 @@ import ( "net" "time" - "github.com/Microsoft/go-winio" + winio "github.com/Microsoft/go-winio" ) func npipeDial(path string, timeout time.Duration) (net.Conn, error) { diff --git a/testutil/request/ops.go b/testutil/request/ops.go new file mode 100644 index 0000000000000..be4e502ccb6bd --- /dev/null +++ b/testutil/request/ops.go @@ -0,0 +1,77 @@ +package request + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "strings" +) + +// Options defines request options, like request modifiers and which host to target +type Options struct { + host string + requestModifiers []func(*http.Request) error +} + +// Host creates a modifier that sets the specified host as the request URL host +func Host(host string) func(*Options) { + return func(o *Options) { + o.host = host + } +} + +// With adds a request modifier to the options +func With(f func(*http.Request) error) func(*Options) { + return func(o *Options) { + o.requestModifiers = append(o.requestModifiers, f) + } +} + +// Method creates a modifier that sets the specified string as the request method +func Method(method string) func(*Options) { + return With(func(req *http.Request) error { + req.Method = method + return nil + }) +} + +// RawString sets the specified string as body for the request +func RawString(content string) func(*Options) { + return RawContent(io.NopCloser(strings.NewReader(content))) +} + +// RawContent sets the specified reader as body for the request +func RawContent(reader io.ReadCloser) func(*Options) { + return With(func(req *http.Request) error { + req.Body = reader + return nil + }) +} + +// ContentType sets the specified Content-Type request header +func ContentType(contentType string) func(*Options) { + return With(func(req *http.Request) error { + req.Header.Set("Content-Type", contentType) + return nil + }) +} + +// JSON sets the Content-Type request header to json +func JSON(o *Options) { + ContentType("application/json")(o) +} + +// JSONBody creates a modifier that encodes the specified data to a JSON string and set it as request body. It also sets +// the Content-Type header of the request. +func JSONBody(data interface{}) func(*Options) { + return With(func(req *http.Request) error { + jsonData := bytes.NewBuffer(nil) + if err := json.NewEncoder(jsonData).Encode(data); err != nil { + return err + } + req.Body = io.NopCloser(jsonData) + req.Header.Set("Content-Type", "application/json") + return nil + }) +} diff --git a/internal/test/request/request.go b/testutil/request/request.go similarity index 85% rename from internal/test/request/request.go rename to testutil/request/request.go index 1986d370f152f..d5f559c666370 100644 --- a/internal/test/request/request.go +++ b/testutil/request/request.go @@ -1,45 +1,39 @@ -package request // import "github.com/docker/docker/internal/test/request" +package request // import "github.com/docker/docker/testutil/request" import ( "context" "crypto/tls" "fmt" "io" - "io/ioutil" "net" "net/http" "net/url" "os" "path/filepath" + "testing" "time" "github.com/docker/docker/client" - "github.com/docker/docker/internal/test" - "github.com/docker/docker/internal/test/environment" - "github.com/docker/docker/opts" "github.com/docker/docker/pkg/ioutils" + "github.com/docker/docker/testutil/environment" "github.com/docker/go-connections/sockets" "github.com/docker/go-connections/tlsconfig" "github.com/pkg/errors" - "gotest.tools/assert" + "gotest.tools/v3/assert" ) // NewAPIClient returns a docker API client configured from environment variables -func NewAPIClient(t assert.TestingT, ops ...func(*client.Client) error) client.APIClient { - if ht, ok := t.(test.HelperT); ok { - ht.Helper() - } - ops = append([]func(*client.Client) error{client.FromEnv}, ops...) +func NewAPIClient(t testing.TB, ops ...client.Opt) client.APIClient { + t.Helper() + ops = append([]client.Opt{client.FromEnv}, ops...) clt, err := client.NewClientWithOpts(ops...) assert.NilError(t, err) return clt } // DaemonTime provides the current time on the daemon host -func DaemonTime(ctx context.Context, t assert.TestingT, client client.APIClient, testEnv *environment.Execution) time.Time { - if ht, ok := t.(test.HelperT); ok { - ht.Helper() - } +func DaemonTime(ctx context.Context, t testing.TB, client client.APIClient, testEnv *environment.Execution) time.Time { + t.Helper() if testEnv.IsLocalDaemon() { return time.Now() } @@ -54,10 +48,8 @@ func DaemonTime(ctx context.Context, t assert.TestingT, client client.APIClient, // DaemonUnixTime returns the current time on the daemon host with nanoseconds precision. // It return the time formatted how the client sends timestamps to the server. -func DaemonUnixTime(ctx context.Context, t assert.TestingT, client client.APIClient, testEnv *environment.Execution) string { - if ht, ok := t.(test.HelperT); ok { - ht.Helper() - } +func DaemonUnixTime(ctx context.Context, t testing.TB, client client.APIClient, testEnv *environment.Execution) string { + t.Helper() dt := DaemonTime(ctx, t, client, testEnv) return fmt.Sprintf("%d.%09d", dt.Unix(), int64(dt.Nanosecond())) } @@ -77,6 +69,11 @@ func Get(endpoint string, modifiers ...func(*Options)) (*http.Response, io.ReadC return Do(endpoint, modifiers...) } +// Head creates and execute a HEAD request on the specified host and endpoint, with the specified request modifiers +func Head(endpoint string, modifiers ...func(*Options)) (*http.Response, io.ReadCloser, error) { + return Do(endpoint, append(modifiers, Method(http.MethodHead))...) +} + // Do creates and execute a request on the specified endpoint, with the specified request modifiers func Do(endpoint string, modifiers ...func(*Options)) (*http.Response, io.ReadCloser, error) { opts := &Options{ @@ -107,7 +104,7 @@ func Do(endpoint string, modifiers ...func(*Options)) (*http.Response, io.ReadCl // ReadBody read the specified ReadCloser content and returns it func ReadBody(b io.ReadCloser) ([]byte, error) { defer b.Close() - return ioutil.ReadAll(b) + return io.ReadAll(b) } // newRequest creates a new http Request to the specified host and endpoint, with the specified request modifiers @@ -116,7 +113,7 @@ func newRequest(endpoint string, opts *Options) (*http.Request, error) { if err != nil { return nil, errors.Wrapf(err, "failed parsing url %q", opts.host) } - req, err := http.NewRequest("GET", endpoint, nil) + req, err := http.NewRequest(http.MethodGet, endpoint, nil) if err != nil { return nil, errors.Wrap(err, "failed to create request") } @@ -181,7 +178,7 @@ func getTLSConfig() (*tls.Config, error) { // DaemonHost return the daemon host string for this test execution func DaemonHost() string { - daemonURLStr := "unix://" + opts.DefaultUnixSocket + daemonURLStr := client.DefaultDockerHost if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" { daemonURLStr = daemonHostVar } diff --git a/testutil/stringutils.go b/testutil/stringutils.go new file mode 100644 index 0000000000000..885ebe560bf92 --- /dev/null +++ b/testutil/stringutils.go @@ -0,0 +1,14 @@ +package testutil // import "github.com/docker/docker/testutil" + +import "math/rand" + +// GenerateRandomAlphaOnlyString generates an alphabetical random string with length n. +func GenerateRandomAlphaOnlyString(n int) string { + // make a really long string + letters := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + b := make([]byte, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] //nolint: gosec // G404: Use of weak random number generator (math/rand instead of crypto/rand) + } + return string(b) +} diff --git a/internal/testutil/stringutils_test.go b/testutil/stringutils_test.go similarity index 86% rename from internal/testutil/stringutils_test.go rename to testutil/stringutils_test.go index 753aac966dc2e..438683664ff26 100644 --- a/internal/testutil/stringutils_test.go +++ b/testutil/stringutils_test.go @@ -1,10 +1,10 @@ -package testutil // import "github.com/docker/docker/internal/testutil" +package testutil // import "github.com/docker/docker/testutil" import ( "testing" - "gotest.tools/assert" - is "gotest.tools/assert/cmp" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" ) func testLengthHelper(generator func(int) string, t *testing.T) { diff --git a/vendor.conf b/vendor.conf index 5d7d2d7d80730..6e22620c32107 100644 --- a/vendor.conf +++ b/vendor.conf @@ -1,159 +1,190 @@ -# the following lines are in sorted order, FYI -github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 -github.com/Microsoft/hcsshim v0.7.6 -github.com/Microsoft/go-winio v0.4.11 -github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a -github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git -github.com/golang/gddo 9b12a26f3fbd7397dee4e20939ddca719d840d2a -github.com/gorilla/context v1.1 -github.com/gorilla/mux v1.1 -github.com/Microsoft/opengcs v0.3.9 -github.com/kr/pty 5cf931ef8f -github.com/mattn/go-shellwords v1.0.3 -github.com/sirupsen/logrus v1.0.6 -github.com/tchap/go-patricia v2.2.6 -github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 -golang.org/x/net a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1 -golang.org/x/sys ac767d655b305d4e9612f5f6e33120b9176c4ad4 -github.com/docker/go-units 47565b4f722fb6ceae66b95f853feed578a4a51c # v0.3.3 -github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4.0 -golang.org/x/text f21a4dfb5e38f5895301dc265a8def02365cc3d0 # v0.3.0 -gotest.tools v2.1.0 -github.com/google/go-cmp v0.2.0 - -github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5 -github.com/imdario/mergo v0.3.6 -golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca +github.com/Azure/go-ansiterm d185dfc1b5a126116ea5a19e148e29d16b4574c9 +github.com/Microsoft/hcsshim 133feb626666d38630976b19f742431f31cee604 # v0.8.23 +github.com/Microsoft/go-winio 6c24dfa01eb5906508a5ad06f4f6534a9be47456 # v0.5.1 +github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a +github.com/golang/gddo 72a348e765d293ed6d1ded7b699591f14d6cd921 +github.com/google/uuid 0cd6bf5da1e1c83f8b45653022c74f71af0538a4 # v1.1.1 +github.com/gorilla/mux 98cb6bf42e086f6af920b965c38cacc07402d51b # v1.8.0 +github.com/moby/locker 281af2d563954745bea9d1487c965f24d30742fe # v1.0.1 +github.com/moby/term 3f7ff695adc6a35abc925370dd0a4dafb48ec64d + +# Note that this dependency uses submodules, providing the github.com/moby/sys/mount, +# github.com/moby/sys/mountinfo, github.com/moby/sys/signal, and github.com/moby/sys/symlink +# modules. Our vendoring tool (vndr) currently does not support submodules / vendoring sub-paths, +# so we vendor the top-level moby/sys repository (which contains both) and pick the most recent tag, +# which could be either `mountinfo/vX.Y.Z`, `mount/vX.Y.Z`, `signal/vX.Y.Z`, or `symlink/vX.Y.Z`. +github.com/moby/sys 03b9f8d59a07f5206a2264105f4903a222aea964 # signal/v0.6.0 + +github.com/creack/pty 2a38352e8b4d7ab6c336eef107e42a55e72e7fbc # v1.1.11 +github.com/sirupsen/logrus bdc0db8ead3853c56b7cd1ac2ba4e11b47d7da6b # v1.8.1 +github.com/tchap/go-patricia a7f0089c6f496e8e70402f61733606daa326cac5 # v2.3.0 +golang.org/x/net e18ecbb051101a46fc263334b127c89bc7bff7ea +golang.org/x/sys 69cdffdb9359ff97d91e4f4fbb6b2714c3898eae +github.com/docker/go-units 519db1ee28dcc9fd2474ae59fca29a810482bfb1 # v0.4.0 +github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4.0 +golang.org/x/text 23ae387dee1f90d29a23c0e87ee0b46038fbed0e # v0.3.3 +gotest.tools/v3 568bc57cc5c19a2ef85e5749870b49a4cc2ab54d # v3.0.3 +github.com/google/go-cmp 3af367b6b30c263d47e8895973edcca9a49cf029 # v0.2.0 + +github.com/RackSec/srslog a4725f04ec91af1a91b380da679d6e0c2f061e59 +github.com/imdario/mergo 29fb3d3bdc5512887f1dc9aedde6a0fed407fa8f # v0.3.12 +golang.org/x/sync 036812b2e83c0ddf193dd5a34e034151da389d09 # buildkit -github.com/moby/buildkit c7bb575343df0cbfeab8b5b28149630b8153fcc6 -github.com/tonistiigi/fsutil f567071bed2416e4d87d260d3162722651182317 -github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746 -github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7 -github.com/google/shlex 6f45313302b9c56850fc17f99e40caebce98c716 -github.com/opentracing-contrib/go-stdlib b1a47cfbdd7543e70e9ef3e73d0802ad306cc1cc -github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b - -#get libnetwork packages - -# When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly -github.com/docker/libnetwork d7b61745d16675c9f548b19f06fda80d422a74f0 -github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 -github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 -github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec -github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b -github.com/hashicorp/memberlist 3d8438da9589e7b608a83ffac1ef8211486bcb7c -github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372 -github.com/hashicorp/go-sockaddr 6d291a969b86c4b633730bfc6b8b9d64c3aafed9 -github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e -github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870 -github.com/docker/libkv 458977154600b9f23984d9f4b82e79570b5ae12b -github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25 -github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e - -# When updating, consider updating TOMLV_COMMIT in hack/dockerfile/install/tomlv accordingly -github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 -github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374 -github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d -github.com/coreos/etcd v3.3.9 -github.com/coreos/go-semver v0.2.0 -github.com/ugorji/go v1.1.1 -github.com/hashicorp/consul v0.5.2 -github.com/miekg/dns v1.0.7 -github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb -go.etcd.io/bbolt v1.3.1-etcd.8 +github.com/moby/buildkit 9f254e18360a24c2ae47b26f772c3c89533bcbb7 # master / v0.9.0-dev +github.com/tonistiigi/fsutil d72af97c0eaf93c1d20360e3cb9c63c223675b83 +github.com/tonistiigi/units 6950e57a87eaf136bbe44ef2ec8e75b9e3569de2 +github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746 +github.com/opentracing/opentracing-go d34af3eaa63c4d08ab54863a4bdd0daa45212e12 # v1.2.0 +github.com/google/shlex e7afc7fbc51079733e9468cdfd1efcd7d196cd1d +github.com/opentracing-contrib/go-stdlib 8a6ff1ad1691a29e4f7b5d46604f97634997c8c4 # v1.0.0 +github.com/mitchellh/hashstructure a38c50148365edc8df43c1580c48fb2b3a1e9cd7 # v1.0.0 +github.com/gofrs/flock 6caa7350c26b838538005fae7dbee4e69d9398db # v0.7.3 +github.com/grpc-ecosystem/go-grpc-middleware 3c51f7f332123e8be5a157c0802a228ac85bf9db # v1.2.0 + +# libnetwork + +github.com/docker/go-events e31b211e4f1cd09aa76fe4ac244571fab96ae47f +github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 +github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec +github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b +github.com/hashicorp/memberlist 619135cdd9e5dda8c12f8ceef39bdade4f5899b6 # v0.2.4 +github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372 +github.com/hashicorp/errwrap 8a6fb523712970c966eefc6b39ed2c5e74880354 # v1.0.0 +github.com/hashicorp/go-sockaddr c7188e74f6acae5a989bdc959aa779f8b9f42faf # v1.0.2 +github.com/hashicorp/go-multierror 886a7fbe3eb1c874d46f623bfa70af45f425b3d1 # v1.0.0 +github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870 +github.com/docker/libkv 458977154600b9f23984d9f4b82e79570b5ae12b +github.com/vishvananda/netns db3c7e526aae966c4ccfa6c8189b693d6ac5d202 +github.com/vishvananda/netlink f049be6f391489d3f374498fe0c8df8449258372 # v1.1.0 +github.com/moby/ipvs 4566ccea0e08d68e9614c3e7a64a23b850c4bb35 # v1.0.1 +github.com/google/btree 479b5e81b0a93ec038d201b0b33d17db599531d3 # v1.0.1 + +github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374 +github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d +github.com/coreos/etcd 2c834459e1aab78a5d5219c7dfe42335fc4b617a # v3.3.25 +github.com/coreos/go-semver 8ab6407b697782a06568d4b7f1db25550ec2e4c6 # v0.2.0 +github.com/hashicorp/consul 9a9cc9341bb487651a0399e3fc5e1e8a42e62dd9 # v0.5.2 +github.com/miekg/dns 6c0c4e6581f8e173cc562c8b3363ab984e4ae071 # v1.1.27 +github.com/ishidawataru/sctp f2269e66cdee387bd321445d5d300893449805be +go.etcd.io/bbolt 232d8fc87f50244f9c808f4745759e08a304c029 # v1.3.5 +github.com/json-iterator/go a1ca0830781e007c66b225121d2cdb3a649421f6 # v1.1.10 +github.com/modern-go/concurrent bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94 # 1.0.3 +github.com/modern-go/reflect2 94122c33edd36123c84d5368cfb2b69df93a0ec8 # v1.0.1 # get graph and distribution packages -github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 -github.com/vbatts/tar-split v0.11.0 -github.com/opencontainers/go-digest v1.0.0-rc1 +github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580 +github.com/vbatts/tar-split 80a436fd6164c557b131f7c59ed69bd81af69761 # v0.11.2 +github.com/opencontainers/go-digest ea51bea511f75cfa3ef6098cc253c5c3609b037a # v1.0.0 # get go-zfs packages -github.com/mistifyio/go-zfs 22c9b32c84eb0d0c6f4043b6e90fc94073de92fa -github.com/pborman/uuid v1.0 +github.com/mistifyio/go-zfs f784269be439d704d3dfa1906f45dd848fed2beb -google.golang.org/grpc v1.12.0 +google.golang.org/grpc f495f5b15ae7ccda3b38c53a1bfcde4c1a58a2bc # v1.27.1 -# This does not need to match RUNC_COMMIT as it is used for helper packages but should be newer or equal -github.com/opencontainers/runc 00dc70017d222b178a002ed30e9321b12647af2d -github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 # v1.0.1-45-geba862d -github.com/opencontainers/image-spec v1.0.1 -github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 +# The version of runc should match the version that is used by the containerd +# version that is used. If you need to update runc, open a pull request in +# the containerd project first, and update both after that is merged. +# This commit does not need to match RUNC_COMMIT as it is used for helper +# packages but should be newer or equal. +github.com/opencontainers/runc f46b6ba2c9314cfc8caae24a32ec5fe9ef1059fe # v1.0.3 +github.com/opencontainers/runtime-spec 1c3f411f041711bbeecf35ff7e93461ea6789220 # v1.0.3-0.20210326190908-1c3f411f0417 +github.com/opencontainers/image-spec 67d2d5658fe0476ab9bf414cec164077ebff3920 # v1.0.2 +github.com/cyphar/filepath-securejoin a261ee33d7a517f054effbf451841abaafe3e0fd # v0.2.2 -# libcontainer deps (see src/github.com/opencontainers/runc/Godeps/Godeps.json) -github.com/coreos/go-systemd v17 -github.com/godbus/dbus v4.0.0 -github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852 -github.com/golang/protobuf v1.1.0 +# go-systemd v17 is required by github.com/coreos/pkg/capnslog/journald_formatter.go +github.com/coreos/go-systemd 39ca1b05acc7ad1220e09f133283b8859a8b71ab # v17 + +# systemd integration (journald, daemon/listeners, containerd/cgroups) +github.com/coreos/go-systemd/v22 777e73a89cef78631ccaa97f53a9bae67e166186 # v22.3.2 +github.com/godbus/dbus/v5 c88335c0b1d28a30e7fc76d526a06154b85e5d97 # v5.0.4 # gelf logging driver deps -github.com/Graylog2/go-gelf 4143646226541087117ff2f83334ea48b3201841 +github.com/Graylog2/go-gelf 1550ee647df0510058c9d67a45c56f18911d80b8 # v2 branch -github.com/fluent/fluent-logger-golang v1.3.0 # fluent-logger-golang deps -github.com/philhofer/fwd 98c11a7a6ec829d672b03833c3d69a7fae1ca972 -github.com/tinylib/msgp 3b556c64540842d4f82967be066a7f7fffc3adad +github.com/fluent/fluent-logger-golang 0b652e850a9140d0b1db6390d8925d0601e952db # v1.8.0 +github.com/philhofer/fwd bb6d471dc95d4fe11e432687f8b70ff496cf3136 # v1.0.0 +github.com/tinylib/msgp af6442a0fcf6e2a1b824f70dd0c734f01e817751 # v1.1.0 # fsnotify -github.com/fsnotify/fsnotify v1.4.7 +github.com/fsnotify/fsnotify 45d7d09e39ef4ac08d493309fa031790c15bfe8a # v1.4.9 # awslogs deps -github.com/aws/aws-sdk-go v1.12.66 -github.com/go-ini/ini v1.25.4 -github.com/jmespath/go-jmespath 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74 +github.com/aws/aws-sdk-go 2590bc875c54c9fda225d8e4e56a9d28d90c6a47 # v1.28.11 +github.com/jmespath/go-jmespath 2d053f87d1d7f9f48196ae04cf3daea4273d207d # v0.3.0 # logentries -github.com/bsphere/le_go 7a984a84b5492ae539b79b62fb4a10afc63c7bcf +github.com/bsphere/le_go 7a984a84b5492ae539b79b62fb4a10afc63c7bcf # gcplogs deps -golang.org/x/oauth2 ec22f46f877b4505e0117eeaab541714644fdd28 -google.golang.org/api de943baf05a022a8f921b544b7827bacaba1aed5 -go.opencensus.io v0.11.0 -cloud.google.com/go v0.23.0 -github.com/googleapis/gax-go v2.0.0 -google.golang.org/genproto 694d95ba50e67b2e363f3483057db5d4910c18f9 +golang.org/x/oauth2 bf48bf16ab8d622ce64ec6ce98d2c98f916b6303 +google.golang.org/api dec2ee309f5b09fc59bc40676447c15736284d78 # v0.8.0 +github.com/golang/groupcache 869f871628b6baa9cfbc11732cdf6546b17c1298 +go.opencensus.io d835ff86be02193d324330acdb7d65546b05f814 # v0.22.3 +cloud.google.com/go ceeb313ad77b789a7fa5287b36a1d127b69b7093 # v0.44.3 +github.com/googleapis/gax-go bd5b16380fd03dc758d11cef74ba2e3bc8b0e8c2 # v2.0.5 +google.golang.org/genproto 3f1135a288c9a07e340ae8ba4cc6c7065a3160e8 # containerd -github.com/containerd/containerd 0c5f8f63c3368856c320ae8a1c125e703b73b51d # v1.2.0-rc.1 -github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c -github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 -github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 -github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 -github.com/containerd/cri 9f39e3289533fc228c5e5fcac0a6dbdd60c6047b # release/1.2 branch -github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 -github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 -github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a -github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef +github.com/containerd/containerd 1e5ef943eb76627a6d3b6de8cd1ef6537f393a71 # v1.5.8 +github.com/containerd/fifo 650e8a8a179d040123db61f016cb133143e7a581 # v1.0.0 +github.com/containerd/continuity bce1c3f9669b6f3e7f6656ee715b0b4d75fa64a6 # v0.1.0 +github.com/containerd/cgroups b9de8a2212026c07cec67baf3323f1fc0121e048 # v1.0.1 +github.com/containerd/console 2f1e3d2b6afd18e8b2077816c711205a0b4d8769 # v1.0.2 +github.com/containerd/go-runc 16b287bc67d069a60fa48db15f330b790b74365b # v1.0.0 +github.com/containerd/typeurl 5e43fb8b75ed2f2305fc04e6918c8d10636771bc # v1.0.2 +github.com/containerd/ttrpc 0247db16a1f98bb76731a12ad72b8d49705b38b3 # v1.1.0 +github.com/gogo/googleapis 01e0f9cca9b92166042241267ee2a5cdf5cff46c # v1.3.2 +github.com/cilium/ebpf ca492085341e0e917f48ec30704d5054c5d42ca8 # v0.6.2 +github.com/klauspost/compress a3b7545c88eea469c2246bee0e6c130525d56190 # v1.11.13 +github.com/pelletier/go-toml 65ca8064882c8c308e5c804c5d5443d409e0738c # v1.8.1 # cluster -github.com/docker/swarmkit 9f271c2963d18a7c60d2c4001fb418ca4037df19 -github.com/gogo/protobuf v1.0.0 -github.com/cloudflare/cfssl 1.3.2 -github.com/fernet/fernet-go 1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2 -github.com/google/certificate-transparency-go v1.0.20 -golang.org/x/crypto 0709b304e793a5edb4a2c0145f281ecdc20838a4 -golang.org/x/time fbb02b2291d28baffd63558aa44b4b56f178d650 -github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad -github.com/hashicorp/go-immutable-radix 826af9ccf0feeee615d546d69b11f8e98da8c8f1 git://github.com/tonistiigi/go-immutable-radix.git -github.com/hashicorp/golang-lru 0fb14efe8c47ae851c0034ed7a448854d3d34cf3 -github.com/coreos/pkg v3 -github.com/pivotal-golang/clock 3fd3c1944c59d9742e1cd333672181cd1a6f9fa0 -github.com/prometheus/client_golang v0.8.0 -github.com/beorn7/perks 3a771d992973f24aa725d07868b467d1ddfceaf -github.com/prometheus/client_model 6f3806018612930941127f2a7c6c453ba2c527d2 -github.com/prometheus/common 7600349dcfe1abd18d72d3a1770870d9800a7801 -github.com/prometheus/procfs 7d6f385de8bea29190f15ba9931442a0eaef9af7 -github.com/matttproud/golang_protobuf_extensions v1.0.0 -github.com/pkg/errors 645ef00459ed84a119197bfb8d8205042c6df63d # v0.8.0 -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 +github.com/docker/swarmkit 3629f50980f6c0dd5ccd7dbfa0956b57ea0cd78d # master +github.com/gogo/protobuf b03c65ea87cdc3521ede29f62fe3ce239267c1bc # v1.3.2 +github.com/golang/protobuf 84668698ea25b64748563aa20726db66a6b8d299 # v1.3.5 +github.com/cloudflare/cfssl 5d63dbd981b5c408effbb58c442d54761ff94fbd # 1.3.2 +github.com/fernet/fernet-go 9eac43b88a5efb8651d24de9b68e87567e029736 +github.com/google/certificate-transparency-go 37a384cd035e722ea46e55029093e26687138edf # v1.0.20 +golang.org/x/crypto 0c34fe9e7dc2486962ef9867e3edb3503537209f +golang.org/x/time 3af7569d3a1e776fc2a3c1cec133b43105ea9c2e +github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad +github.com/hashicorp/go-immutable-radix 826af9ccf0feeee615d546d69b11f8e98da8c8f1 git://github.com/tonistiigi/go-immutable-radix.git +github.com/hashicorp/golang-lru 7f827b33c0f158ec5dfbba01bb0b14a4541fd81d # v0.5.3 +github.com/coreos/pkg 97fdf19511ea361ae1c100dd393cc47f8dcfa1e1 # v4 +code.cloudfoundry.org/clock 02e53af36e6c978af692887ed449b74026d76fec # v1.0.0 + +# prometheus +github.com/prometheus/client_golang 6edbbd9e560190e318cdc5b4d3e630b442858380 # v1.6.0 +github.com/beorn7/perks 37c8de3658fcb183f997c4e13e8337516ab753e6 # v1.0.1 +github.com/prometheus/client_model 7bc5445566f0fe75b15de23e6b93886e982d7bf9 # v0.2.0 +github.com/prometheus/common d978bcb1309602d68bb4ba69cf3f8ed900e07308 # v0.9.1 +github.com/prometheus/procfs 46159f73e74d1cb8dc223deef9b2d049286f46b1 # v0.0.11 +github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c # v1.0.1 +github.com/pkg/errors 614d223910a179a466c1767a985424175c39b465 # v0.9.1 +github.com/grpc-ecosystem/go-grpc-prometheus c225b8c3b01faf2899099b768856a9e916e5087b # v1.2.0 +github.com/cespare/xxhash/v2 d7df74196a9e781ede915320c11c378c1b2f3a1f # v2.1.1 # cli -github.com/spf13/cobra v0.0.3 -github.com/spf13/pflag v1.0.1 -github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 # v1.0 -github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty +github.com/spf13/cobra 8380ddd3132bdf8fd77731725b550c181dda0aa8 # v1.1.3 +github.com/spf13/pflag 2e9d26c8c37aae03e3f9d4e90b7116f5accb7cab # v1.0.5 +github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 # v1.0.0 +github.com/morikuni/aec 39771216ff4c63d11f5e604076f9c45e8be1067b # v1.0.0 # metrics -github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18 +github.com/docker/go-metrics b619b3592b65de4f087d9f16863a7e6ff905973c # v0.0.1 + +github.com/opencontainers/selinux 95d3852f356b31cbb2d84040ffca303911d08273 # v1.9.1 + + +# archive/tar +# rm -rf vendor/archive +# mkdir -p ./vendor/archive +# git clone -b go$GOLANG_VERSION --depth=1 git://github.com/golang/go.git ./go +# git --git-dir ./go/.git --work-tree ./go am ../patches/0001-archive-tar-do-not-populate-user-group-names.patch +# cp -a go/src/archive/tar ./vendor/archive/tar +# rm -rf ./go +# vndr -whitelist=^archive/tar -github.com/opencontainers/selinux b6fa367ed7f534f9ba25391cc2d467085dbb445a +# DO NOT EDIT BELOW THIS LINE -------- reserved for downstream projects -------- diff --git a/vendor/archive/tar/common.go b/vendor/archive/tar/common.go new file mode 100644 index 0000000000000..c667cfc8720b5 --- /dev/null +++ b/vendor/archive/tar/common.go @@ -0,0 +1,723 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package tar implements access to tar archives. +// +// Tape archives (tar) are a file format for storing a sequence of files that +// can be read and written in a streaming manner. +// This package aims to cover most variations of the format, +// including those produced by GNU and BSD tar tools. +package tar + +import ( + "errors" + "fmt" + "io/fs" + "math" + "path" + "reflect" + "strconv" + "strings" + "time" +) + +// BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit +// architectures. If a large value is encountered when decoding, the result +// stored in Header will be the truncated version. + +var ( + ErrHeader = errors.New("archive/tar: invalid tar header") + ErrWriteTooLong = errors.New("archive/tar: write too long") + ErrFieldTooLong = errors.New("archive/tar: header field too long") + ErrWriteAfterClose = errors.New("archive/tar: write after close") + errMissData = errors.New("archive/tar: sparse file references non-existent data") + errUnrefData = errors.New("archive/tar: sparse file contains unreferenced data") + errWriteHole = errors.New("archive/tar: write non-NUL byte in sparse hole") +) + +type headerError []string + +func (he headerError) Error() string { + const prefix = "archive/tar: cannot encode header" + var ss []string + for _, s := range he { + if s != "" { + ss = append(ss, s) + } + } + if len(ss) == 0 { + return prefix + } + return fmt.Sprintf("%s: %v", prefix, strings.Join(ss, "; and ")) +} + +// Type flags for Header.Typeflag. +const ( + // Type '0' indicates a regular file. + TypeReg = '0' + TypeRegA = '\x00' // Deprecated: Use TypeReg instead. + + // Type '1' to '6' are header-only flags and may not have a data body. + TypeLink = '1' // Hard link + TypeSymlink = '2' // Symbolic link + TypeChar = '3' // Character device node + TypeBlock = '4' // Block device node + TypeDir = '5' // Directory + TypeFifo = '6' // FIFO node + + // Type '7' is reserved. + TypeCont = '7' + + // Type 'x' is used by the PAX format to store key-value records that + // are only relevant to the next file. + // This package transparently handles these types. + TypeXHeader = 'x' + + // Type 'g' is used by the PAX format to store key-value records that + // are relevant to all subsequent files. + // This package only supports parsing and composing such headers, + // but does not currently support persisting the global state across files. + TypeXGlobalHeader = 'g' + + // Type 'S' indicates a sparse file in the GNU format. + TypeGNUSparse = 'S' + + // Types 'L' and 'K' are used by the GNU format for a meta file + // used to store the path or link name for the next file. + // This package transparently handles these types. + TypeGNULongName = 'L' + TypeGNULongLink = 'K' +) + +// Keywords for PAX extended header records. +const ( + paxNone = "" // Indicates that no PAX key is suitable + paxPath = "path" + paxLinkpath = "linkpath" + paxSize = "size" + paxUid = "uid" + paxGid = "gid" + paxUname = "uname" + paxGname = "gname" + paxMtime = "mtime" + paxAtime = "atime" + paxCtime = "ctime" // Removed from later revision of PAX spec, but was valid + paxCharset = "charset" // Currently unused + paxComment = "comment" // Currently unused + + paxSchilyXattr = "SCHILY.xattr." + + // Keywords for GNU sparse files in a PAX extended header. + paxGNUSparse = "GNU.sparse." + paxGNUSparseNumBlocks = "GNU.sparse.numblocks" + paxGNUSparseOffset = "GNU.sparse.offset" + paxGNUSparseNumBytes = "GNU.sparse.numbytes" + paxGNUSparseMap = "GNU.sparse.map" + paxGNUSparseName = "GNU.sparse.name" + paxGNUSparseMajor = "GNU.sparse.major" + paxGNUSparseMinor = "GNU.sparse.minor" + paxGNUSparseSize = "GNU.sparse.size" + paxGNUSparseRealSize = "GNU.sparse.realsize" +) + +// basicKeys is a set of the PAX keys for which we have built-in support. +// This does not contain "charset" or "comment", which are both PAX-specific, +// so adding them as first-class features of Header is unlikely. +// Users can use the PAXRecords field to set it themselves. +var basicKeys = map[string]bool{ + paxPath: true, paxLinkpath: true, paxSize: true, paxUid: true, paxGid: true, + paxUname: true, paxGname: true, paxMtime: true, paxAtime: true, paxCtime: true, +} + +// A Header represents a single header in a tar archive. +// Some fields may not be populated. +// +// For forward compatibility, users that retrieve a Header from Reader.Next, +// mutate it in some ways, and then pass it back to Writer.WriteHeader +// should do so by creating a new Header and copying the fields +// that they are interested in preserving. +type Header struct { + // Typeflag is the type of header entry. + // The zero value is automatically promoted to either TypeReg or TypeDir + // depending on the presence of a trailing slash in Name. + Typeflag byte + + Name string // Name of file entry + Linkname string // Target name of link (valid for TypeLink or TypeSymlink) + + Size int64 // Logical file size in bytes + Mode int64 // Permission and mode bits + Uid int // User ID of owner + Gid int // Group ID of owner + Uname string // User name of owner + Gname string // Group name of owner + + // If the Format is unspecified, then Writer.WriteHeader rounds ModTime + // to the nearest second and ignores the AccessTime and ChangeTime fields. + // + // To use AccessTime or ChangeTime, specify the Format as PAX or GNU. + // To use sub-second resolution, specify the Format as PAX. + ModTime time.Time // Modification time + AccessTime time.Time // Access time (requires either PAX or GNU support) + ChangeTime time.Time // Change time (requires either PAX or GNU support) + + Devmajor int64 // Major device number (valid for TypeChar or TypeBlock) + Devminor int64 // Minor device number (valid for TypeChar or TypeBlock) + + // Xattrs stores extended attributes as PAX records under the + // "SCHILY.xattr." namespace. + // + // The following are semantically equivalent: + // h.Xattrs[key] = value + // h.PAXRecords["SCHILY.xattr."+key] = value + // + // When Writer.WriteHeader is called, the contents of Xattrs will take + // precedence over those in PAXRecords. + // + // Deprecated: Use PAXRecords instead. + Xattrs map[string]string + + // PAXRecords is a map of PAX extended header records. + // + // User-defined records should have keys of the following form: + // VENDOR.keyword + // Where VENDOR is some namespace in all uppercase, and keyword may + // not contain the '=' character (e.g., "GOLANG.pkg.version"). + // The key and value should be non-empty UTF-8 strings. + // + // When Writer.WriteHeader is called, PAX records derived from the + // other fields in Header take precedence over PAXRecords. + PAXRecords map[string]string + + // Format specifies the format of the tar header. + // + // This is set by Reader.Next as a best-effort guess at the format. + // Since the Reader liberally reads some non-compliant files, + // it is possible for this to be FormatUnknown. + // + // If the format is unspecified when Writer.WriteHeader is called, + // then it uses the first format (in the order of USTAR, PAX, GNU) + // capable of encoding this Header (see Format). + Format Format +} + +// sparseEntry represents a Length-sized fragment at Offset in the file. +type sparseEntry struct{ Offset, Length int64 } + +func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length } + +// A sparse file can be represented as either a sparseDatas or a sparseHoles. +// As long as the total size is known, they are equivalent and one can be +// converted to the other form and back. The various tar formats with sparse +// file support represent sparse files in the sparseDatas form. That is, they +// specify the fragments in the file that has data, and treat everything else as +// having zero bytes. As such, the encoding and decoding logic in this package +// deals with sparseDatas. +// +// However, the external API uses sparseHoles instead of sparseDatas because the +// zero value of sparseHoles logically represents a normal file (i.e., there are +// no holes in it). On the other hand, the zero value of sparseDatas implies +// that the file has no data in it, which is rather odd. +// +// As an example, if the underlying raw file contains the 10-byte data: +// var compactFile = "abcdefgh" +// +// And the sparse map has the following entries: +// var spd sparseDatas = []sparseEntry{ +// {Offset: 2, Length: 5}, // Data fragment for 2..6 +// {Offset: 18, Length: 3}, // Data fragment for 18..20 +// } +// var sph sparseHoles = []sparseEntry{ +// {Offset: 0, Length: 2}, // Hole fragment for 0..1 +// {Offset: 7, Length: 11}, // Hole fragment for 7..17 +// {Offset: 21, Length: 4}, // Hole fragment for 21..24 +// } +// +// Then the content of the resulting sparse file with a Header.Size of 25 is: +// var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4 +type ( + sparseDatas []sparseEntry + sparseHoles []sparseEntry +) + +// validateSparseEntries reports whether sp is a valid sparse map. +// It does not matter whether sp represents data fragments or hole fragments. +func validateSparseEntries(sp []sparseEntry, size int64) bool { + // Validate all sparse entries. These are the same checks as performed by + // the BSD tar utility. + if size < 0 { + return false + } + var pre sparseEntry + for _, cur := range sp { + switch { + case cur.Offset < 0 || cur.Length < 0: + return false // Negative values are never okay + case cur.Offset > math.MaxInt64-cur.Length: + return false // Integer overflow with large length + case cur.endOffset() > size: + return false // Region extends beyond the actual size + case pre.endOffset() > cur.Offset: + return false // Regions cannot overlap and must be in order + } + pre = cur + } + return true +} + +// alignSparseEntries mutates src and returns dst where each fragment's +// starting offset is aligned up to the nearest block edge, and each +// ending offset is aligned down to the nearest block edge. +// +// Even though the Go tar Reader and the BSD tar utility can handle entries +// with arbitrary offsets and lengths, the GNU tar utility can only handle +// offsets and lengths that are multiples of blockSize. +func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry { + dst := src[:0] + for _, s := range src { + pos, end := s.Offset, s.endOffset() + pos += blockPadding(+pos) // Round-up to nearest blockSize + if end != size { + end -= blockPadding(-end) // Round-down to nearest blockSize + } + if pos < end { + dst = append(dst, sparseEntry{Offset: pos, Length: end - pos}) + } + } + return dst +} + +// invertSparseEntries converts a sparse map from one form to the other. +// If the input is sparseHoles, then it will output sparseDatas and vice-versa. +// The input must have been already validated. +// +// This function mutates src and returns a normalized map where: +// * adjacent fragments are coalesced together +// * only the last fragment may be empty +// * the endOffset of the last fragment is the total size +func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry { + dst := src[:0] + var pre sparseEntry + for _, cur := range src { + if cur.Length == 0 { + continue // Skip empty fragments + } + pre.Length = cur.Offset - pre.Offset + if pre.Length > 0 { + dst = append(dst, pre) // Only add non-empty fragments + } + pre.Offset = cur.endOffset() + } + pre.Length = size - pre.Offset // Possibly the only empty fragment + return append(dst, pre) +} + +// fileState tracks the number of logical (includes sparse holes) and physical +// (actual in tar archive) bytes remaining for the current file. +// +// Invariant: LogicalRemaining >= PhysicalRemaining +type fileState interface { + LogicalRemaining() int64 + PhysicalRemaining() int64 +} + +// allowedFormats determines which formats can be used. +// The value returned is the logical OR of multiple possible formats. +// If the value is FormatUnknown, then the input Header cannot be encoded +// and an error is returned explaining why. +// +// As a by-product of checking the fields, this function returns paxHdrs, which +// contain all fields that could not be directly encoded. +// A value receiver ensures that this method does not mutate the source Header. +func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) { + format = FormatUSTAR | FormatPAX | FormatGNU + paxHdrs = make(map[string]string) + + var whyNoUSTAR, whyNoPAX, whyNoGNU string + var preferPAX bool // Prefer PAX over USTAR + verifyString := func(s string, size int, name, paxKey string) { + // NUL-terminator is optional for path and linkpath. + // Technically, it is required for uname and gname, + // but neither GNU nor BSD tar checks for it. + tooLong := len(s) > size + allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath + if hasNUL(s) || (tooLong && !allowLongGNU) { + whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s) + format.mustNotBe(FormatGNU) + } + if !isASCII(s) || tooLong { + canSplitUSTAR := paxKey == paxPath + if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok { + whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s) + format.mustNotBe(FormatUSTAR) + } + if paxKey == paxNone { + whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s) + format.mustNotBe(FormatPAX) + } else { + paxHdrs[paxKey] = s + } + } + if v, ok := h.PAXRecords[paxKey]; ok && v == s { + paxHdrs[paxKey] = v + } + } + verifyNumeric := func(n int64, size int, name, paxKey string) { + if !fitsInBase256(size, n) { + whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n) + format.mustNotBe(FormatGNU) + } + if !fitsInOctal(size, n) { + whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n) + format.mustNotBe(FormatUSTAR) + if paxKey == paxNone { + whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n) + format.mustNotBe(FormatPAX) + } else { + paxHdrs[paxKey] = strconv.FormatInt(n, 10) + } + } + if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) { + paxHdrs[paxKey] = v + } + } + verifyTime := func(ts time.Time, size int, name, paxKey string) { + if ts.IsZero() { + return // Always okay + } + if !fitsInBase256(size, ts.Unix()) { + whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts) + format.mustNotBe(FormatGNU) + } + isMtime := paxKey == paxMtime + fitsOctal := fitsInOctal(size, ts.Unix()) + if (isMtime && !fitsOctal) || !isMtime { + whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts) + format.mustNotBe(FormatUSTAR) + } + needsNano := ts.Nanosecond() != 0 + if !isMtime || !fitsOctal || needsNano { + preferPAX = true // USTAR may truncate sub-second measurements + if paxKey == paxNone { + whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts) + format.mustNotBe(FormatPAX) + } else { + paxHdrs[paxKey] = formatPAXTime(ts) + } + } + if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) { + paxHdrs[paxKey] = v + } + } + + // Check basic fields. + var blk block + v7 := blk.V7() + ustar := blk.USTAR() + gnu := blk.GNU() + verifyString(h.Name, len(v7.Name()), "Name", paxPath) + verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath) + verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname) + verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname) + verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone) + verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid) + verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid) + verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize) + verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone) + verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone) + verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime) + verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime) + verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime) + + // Check for header-only types. + var whyOnlyPAX, whyOnlyGNU string + switch h.Typeflag { + case TypeReg, TypeChar, TypeBlock, TypeFifo, TypeGNUSparse: + // Exclude TypeLink and TypeSymlink, since they may reference directories. + if strings.HasSuffix(h.Name, "/") { + return FormatUnknown, nil, headerError{"filename may not have trailing slash"} + } + case TypeXHeader, TypeGNULongName, TypeGNULongLink: + return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"} + case TypeXGlobalHeader: + h2 := Header{Name: h.Name, Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format} + if !reflect.DeepEqual(h, h2) { + return FormatUnknown, nil, headerError{"only PAXRecords should be set for TypeXGlobalHeader"} + } + whyOnlyPAX = "only PAX supports TypeXGlobalHeader" + format.mayOnlyBe(FormatPAX) + } + if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 { + return FormatUnknown, nil, headerError{"negative size on header-only type"} + } + + // Check PAX records. + if len(h.Xattrs) > 0 { + for k, v := range h.Xattrs { + paxHdrs[paxSchilyXattr+k] = v + } + whyOnlyPAX = "only PAX supports Xattrs" + format.mayOnlyBe(FormatPAX) + } + if len(h.PAXRecords) > 0 { + for k, v := range h.PAXRecords { + switch _, exists := paxHdrs[k]; { + case exists: + continue // Do not overwrite existing records + case h.Typeflag == TypeXGlobalHeader: + paxHdrs[k] = v // Copy all records + case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse): + paxHdrs[k] = v // Ignore local records that may conflict + } + } + whyOnlyPAX = "only PAX supports PAXRecords" + format.mayOnlyBe(FormatPAX) + } + for k, v := range paxHdrs { + if !validPAXRecord(k, v) { + return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)} + } + } + + // TODO(dsnet): Re-enable this when adding sparse support. + // See https://golang.org/issue/22735 + /* + // Check sparse files. + if len(h.SparseHoles) > 0 || h.Typeflag == TypeGNUSparse { + if isHeaderOnlyType(h.Typeflag) { + return FormatUnknown, nil, headerError{"header-only type cannot be sparse"} + } + if !validateSparseEntries(h.SparseHoles, h.Size) { + return FormatUnknown, nil, headerError{"invalid sparse holes"} + } + if h.Typeflag == TypeGNUSparse { + whyOnlyGNU = "only GNU supports TypeGNUSparse" + format.mayOnlyBe(FormatGNU) + } else { + whyNoGNU = "GNU supports sparse files only with TypeGNUSparse" + format.mustNotBe(FormatGNU) + } + whyNoUSTAR = "USTAR does not support sparse files" + format.mustNotBe(FormatUSTAR) + } + */ + + // Check desired format. + if wantFormat := h.Format; wantFormat != FormatUnknown { + if wantFormat.has(FormatPAX) && !preferPAX { + wantFormat.mayBe(FormatUSTAR) // PAX implies USTAR allowed too + } + format.mayOnlyBe(wantFormat) // Set union of formats allowed and format wanted + } + if format == FormatUnknown { + switch h.Format { + case FormatUSTAR: + err = headerError{"Format specifies USTAR", whyNoUSTAR, whyOnlyPAX, whyOnlyGNU} + case FormatPAX: + err = headerError{"Format specifies PAX", whyNoPAX, whyOnlyGNU} + case FormatGNU: + err = headerError{"Format specifies GNU", whyNoGNU, whyOnlyPAX} + default: + err = headerError{whyNoUSTAR, whyNoPAX, whyNoGNU, whyOnlyPAX, whyOnlyGNU} + } + } + return format, paxHdrs, err +} + +// FileInfo returns an fs.FileInfo for the Header. +func (h *Header) FileInfo() fs.FileInfo { + return headerFileInfo{h} +} + +// headerFileInfo implements fs.FileInfo. +type headerFileInfo struct { + h *Header +} + +func (fi headerFileInfo) Size() int64 { return fi.h.Size } +func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } +func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } +func (fi headerFileInfo) Sys() interface{} { return fi.h } + +// Name returns the base name of the file. +func (fi headerFileInfo) Name() string { + if fi.IsDir() { + return path.Base(path.Clean(fi.h.Name)) + } + return path.Base(fi.h.Name) +} + +// Mode returns the permission and mode bits for the headerFileInfo. +func (fi headerFileInfo) Mode() (mode fs.FileMode) { + // Set file permission bits. + mode = fs.FileMode(fi.h.Mode).Perm() + + // Set setuid, setgid and sticky bits. + if fi.h.Mode&c_ISUID != 0 { + mode |= fs.ModeSetuid + } + if fi.h.Mode&c_ISGID != 0 { + mode |= fs.ModeSetgid + } + if fi.h.Mode&c_ISVTX != 0 { + mode |= fs.ModeSticky + } + + // Set file mode bits; clear perm, setuid, setgid, and sticky bits. + switch m := fs.FileMode(fi.h.Mode) &^ 07777; m { + case c_ISDIR: + mode |= fs.ModeDir + case c_ISFIFO: + mode |= fs.ModeNamedPipe + case c_ISLNK: + mode |= fs.ModeSymlink + case c_ISBLK: + mode |= fs.ModeDevice + case c_ISCHR: + mode |= fs.ModeDevice + mode |= fs.ModeCharDevice + case c_ISSOCK: + mode |= fs.ModeSocket + } + + switch fi.h.Typeflag { + case TypeSymlink: + mode |= fs.ModeSymlink + case TypeChar: + mode |= fs.ModeDevice + mode |= fs.ModeCharDevice + case TypeBlock: + mode |= fs.ModeDevice + case TypeDir: + mode |= fs.ModeDir + case TypeFifo: + mode |= fs.ModeNamedPipe + } + + return mode +} + +// sysStat, if non-nil, populates h from system-dependent fields of fi. +var sysStat func(fi fs.FileInfo, h *Header) error + +const ( + // Mode constants from the USTAR spec: + // See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 + c_ISUID = 04000 // Set uid + c_ISGID = 02000 // Set gid + c_ISVTX = 01000 // Save text (sticky bit) + + // Common Unix mode constants; these are not defined in any common tar standard. + // Header.FileInfo understands these, but FileInfoHeader will never produce these. + c_ISDIR = 040000 // Directory + c_ISFIFO = 010000 // FIFO + c_ISREG = 0100000 // Regular file + c_ISLNK = 0120000 // Symbolic link + c_ISBLK = 060000 // Block special file + c_ISCHR = 020000 // Character special file + c_ISSOCK = 0140000 // Socket +) + +// FileInfoHeader creates a partially-populated Header from fi. +// If fi describes a symlink, FileInfoHeader records link as the link target. +// If fi describes a directory, a slash is appended to the name. +// +// Since fs.FileInfo's Name method only returns the base name of +// the file it describes, it may be necessary to modify Header.Name +// to provide the full path name of the file. +func FileInfoHeader(fi fs.FileInfo, link string) (*Header, error) { + if fi == nil { + return nil, errors.New("archive/tar: FileInfo is nil") + } + fm := fi.Mode() + h := &Header{ + Name: fi.Name(), + ModTime: fi.ModTime(), + Mode: int64(fm.Perm()), // or'd with c_IS* constants later + } + switch { + case fm.IsRegular(): + h.Typeflag = TypeReg + h.Size = fi.Size() + case fi.IsDir(): + h.Typeflag = TypeDir + h.Name += "/" + case fm&fs.ModeSymlink != 0: + h.Typeflag = TypeSymlink + h.Linkname = link + case fm&fs.ModeDevice != 0: + if fm&fs.ModeCharDevice != 0 { + h.Typeflag = TypeChar + } else { + h.Typeflag = TypeBlock + } + case fm&fs.ModeNamedPipe != 0: + h.Typeflag = TypeFifo + case fm&fs.ModeSocket != 0: + return nil, fmt.Errorf("archive/tar: sockets not supported") + default: + return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm) + } + if fm&fs.ModeSetuid != 0 { + h.Mode |= c_ISUID + } + if fm&fs.ModeSetgid != 0 { + h.Mode |= c_ISGID + } + if fm&fs.ModeSticky != 0 { + h.Mode |= c_ISVTX + } + // If possible, populate additional fields from OS-specific + // FileInfo fields. + if sys, ok := fi.Sys().(*Header); ok { + // This FileInfo came from a Header (not the OS). Use the + // original Header to populate all remaining fields. + h.Uid = sys.Uid + h.Gid = sys.Gid + h.Uname = sys.Uname + h.Gname = sys.Gname + h.AccessTime = sys.AccessTime + h.ChangeTime = sys.ChangeTime + if sys.Xattrs != nil { + h.Xattrs = make(map[string]string) + for k, v := range sys.Xattrs { + h.Xattrs[k] = v + } + } + if sys.Typeflag == TypeLink { + // hard link + h.Typeflag = TypeLink + h.Size = 0 + h.Linkname = sys.Linkname + } + if sys.PAXRecords != nil { + h.PAXRecords = make(map[string]string) + for k, v := range sys.PAXRecords { + h.PAXRecords[k] = v + } + } + } + if sysStat != nil { + return h, sysStat(fi, h) + } + return h, nil +} + +// isHeaderOnlyType checks if the given type flag is of the type that has no +// data section even if a size is specified. +func isHeaderOnlyType(flag byte) bool { + switch flag { + case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo: + return true + default: + return false + } +} + +func min(a, b int64) int64 { + if a < b { + return a + } + return b +} diff --git a/vendor/archive/tar/example_test.go b/vendor/archive/tar/example_test.go new file mode 100644 index 0000000000000..a2474b959f65c --- /dev/null +++ b/vendor/archive/tar/example_test.go @@ -0,0 +1,71 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar_test + +import ( + "archive/tar" + "bytes" + "fmt" + "io" + "log" + "os" +) + +func Example_minimal() { + // Create and add some files to the archive. + var buf bytes.Buffer + tw := tar.NewWriter(&buf) + var files = []struct { + Name, Body string + }{ + {"readme.txt", "This archive contains some text files."}, + {"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"}, + {"todo.txt", "Get animal handling license."}, + } + for _, file := range files { + hdr := &tar.Header{ + Name: file.Name, + Mode: 0600, + Size: int64(len(file.Body)), + } + if err := tw.WriteHeader(hdr); err != nil { + log.Fatal(err) + } + if _, err := tw.Write([]byte(file.Body)); err != nil { + log.Fatal(err) + } + } + if err := tw.Close(); err != nil { + log.Fatal(err) + } + + // Open and iterate through the files in the archive. + tr := tar.NewReader(&buf) + for { + hdr, err := tr.Next() + if err == io.EOF { + break // End of archive + } + if err != nil { + log.Fatal(err) + } + fmt.Printf("Contents of %s:\n", hdr.Name) + if _, err := io.Copy(os.Stdout, tr); err != nil { + log.Fatal(err) + } + fmt.Println() + } + + // Output: + // Contents of readme.txt: + // This archive contains some text files. + // Contents of gopher.txt: + // Gopher names: + // George + // Geoffrey + // Gonzo + // Contents of todo.txt: + // Get animal handling license. +} diff --git a/vendor/archive/tar/format.go b/vendor/archive/tar/format.go new file mode 100644 index 0000000000000..cfe24a5e1d339 --- /dev/null +++ b/vendor/archive/tar/format.go @@ -0,0 +1,303 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import "strings" + +// Format represents the tar archive format. +// +// The original tar format was introduced in Unix V7. +// Since then, there have been multiple competing formats attempting to +// standardize or extend the V7 format to overcome its limitations. +// The most common formats are the USTAR, PAX, and GNU formats, +// each with their own advantages and limitations. +// +// The following table captures the capabilities of each format: +// +// | USTAR | PAX | GNU +// ------------------+--------+-----------+---------- +// Name | 256B | unlimited | unlimited +// Linkname | 100B | unlimited | unlimited +// Size | uint33 | unlimited | uint89 +// Mode | uint21 | uint21 | uint57 +// Uid/Gid | uint21 | unlimited | uint57 +// Uname/Gname | 32B | unlimited | 32B +// ModTime | uint33 | unlimited | int89 +// AccessTime | n/a | unlimited | int89 +// ChangeTime | n/a | unlimited | int89 +// Devmajor/Devminor | uint21 | uint21 | uint57 +// ------------------+--------+-----------+---------- +// string encoding | ASCII | UTF-8 | binary +// sub-second times | no | yes | no +// sparse files | no | yes | yes +// +// The table's upper portion shows the Header fields, where each format reports +// the maximum number of bytes allowed for each string field and +// the integer type used to store each numeric field +// (where timestamps are stored as the number of seconds since the Unix epoch). +// +// The table's lower portion shows specialized features of each format, +// such as supported string encodings, support for sub-second timestamps, +// or support for sparse files. +// +// The Writer currently provides no support for sparse files. +type Format int + +// Constants to identify various tar formats. +const ( + // Deliberately hide the meaning of constants from public API. + _ Format = (1 << iota) / 4 // Sequence of 0, 0, 1, 2, 4, 8, etc... + + // FormatUnknown indicates that the format is unknown. + FormatUnknown + + // The format of the original Unix V7 tar tool prior to standardization. + formatV7 + + // FormatUSTAR represents the USTAR header format defined in POSIX.1-1988. + // + // While this format is compatible with most tar readers, + // the format has several limitations making it unsuitable for some usages. + // Most notably, it cannot support sparse files, files larger than 8GiB, + // filenames larger than 256 characters, and non-ASCII filenames. + // + // Reference: + // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 + FormatUSTAR + + // FormatPAX represents the PAX header format defined in POSIX.1-2001. + // + // PAX extends USTAR by writing a special file with Typeflag TypeXHeader + // preceding the original header. This file contains a set of key-value + // records, which are used to overcome USTAR's shortcomings, in addition to + // providing the ability to have sub-second resolution for timestamps. + // + // Some newer formats add their own extensions to PAX by defining their + // own keys and assigning certain semantic meaning to the associated values. + // For example, sparse file support in PAX is implemented using keys + // defined by the GNU manual (e.g., "GNU.sparse.map"). + // + // Reference: + // http://pubs.opengroup.org/onlinepubs/009695399/utilities/pax.html + FormatPAX + + // FormatGNU represents the GNU header format. + // + // The GNU header format is older than the USTAR and PAX standards and + // is not compatible with them. The GNU format supports + // arbitrary file sizes, filenames of arbitrary encoding and length, + // sparse files, and other features. + // + // It is recommended that PAX be chosen over GNU unless the target + // application can only parse GNU formatted archives. + // + // Reference: + // https://www.gnu.org/software/tar/manual/html_node/Standard.html + FormatGNU + + // Schily's tar format, which is incompatible with USTAR. + // This does not cover STAR extensions to the PAX format; these fall under + // the PAX format. + formatSTAR + + formatMax +) + +func (f Format) has(f2 Format) bool { return f&f2 != 0 } +func (f *Format) mayBe(f2 Format) { *f |= f2 } +func (f *Format) mayOnlyBe(f2 Format) { *f &= f2 } +func (f *Format) mustNotBe(f2 Format) { *f &^= f2 } + +var formatNames = map[Format]string{ + formatV7: "V7", FormatUSTAR: "USTAR", FormatPAX: "PAX", FormatGNU: "GNU", formatSTAR: "STAR", +} + +func (f Format) String() string { + var ss []string + for f2 := Format(1); f2 < formatMax; f2 <<= 1 { + if f.has(f2) { + ss = append(ss, formatNames[f2]) + } + } + switch len(ss) { + case 0: + return "" + case 1: + return ss[0] + default: + return "(" + strings.Join(ss, " | ") + ")" + } +} + +// Magics used to identify various formats. +const ( + magicGNU, versionGNU = "ustar ", " \x00" + magicUSTAR, versionUSTAR = "ustar\x00", "00" + trailerSTAR = "tar\x00" +) + +// Size constants from various tar specifications. +const ( + blockSize = 512 // Size of each block in a tar stream + nameSize = 100 // Max length of the name field in USTAR format + prefixSize = 155 // Max length of the prefix field in USTAR format +) + +// blockPadding computes the number of bytes needed to pad offset up to the +// nearest block edge where 0 <= n < blockSize. +func blockPadding(offset int64) (n int64) { + return -offset & (blockSize - 1) +} + +var zeroBlock block + +type block [blockSize]byte + +// Convert block to any number of formats. +func (b *block) V7() *headerV7 { return (*headerV7)(b) } +func (b *block) GNU() *headerGNU { return (*headerGNU)(b) } +func (b *block) STAR() *headerSTAR { return (*headerSTAR)(b) } +func (b *block) USTAR() *headerUSTAR { return (*headerUSTAR)(b) } +func (b *block) Sparse() sparseArray { return sparseArray(b[:]) } + +// GetFormat checks that the block is a valid tar header based on the checksum. +// It then attempts to guess the specific format based on magic values. +// If the checksum fails, then FormatUnknown is returned. +func (b *block) GetFormat() Format { + // Verify checksum. + var p parser + value := p.parseOctal(b.V7().Chksum()) + chksum1, chksum2 := b.ComputeChecksum() + if p.err != nil || (value != chksum1 && value != chksum2) { + return FormatUnknown + } + + // Guess the magic values. + magic := string(b.USTAR().Magic()) + version := string(b.USTAR().Version()) + trailer := string(b.STAR().Trailer()) + switch { + case magic == magicUSTAR && trailer == trailerSTAR: + return formatSTAR + case magic == magicUSTAR: + return FormatUSTAR | FormatPAX + case magic == magicGNU && version == versionGNU: + return FormatGNU + default: + return formatV7 + } +} + +// SetFormat writes the magic values necessary for specified format +// and then updates the checksum accordingly. +func (b *block) SetFormat(format Format) { + // Set the magic values. + switch { + case format.has(formatV7): + // Do nothing. + case format.has(FormatGNU): + copy(b.GNU().Magic(), magicGNU) + copy(b.GNU().Version(), versionGNU) + case format.has(formatSTAR): + copy(b.STAR().Magic(), magicUSTAR) + copy(b.STAR().Version(), versionUSTAR) + copy(b.STAR().Trailer(), trailerSTAR) + case format.has(FormatUSTAR | FormatPAX): + copy(b.USTAR().Magic(), magicUSTAR) + copy(b.USTAR().Version(), versionUSTAR) + default: + panic("invalid format") + } + + // Update checksum. + // This field is special in that it is terminated by a NULL then space. + var f formatter + field := b.V7().Chksum() + chksum, _ := b.ComputeChecksum() // Possible values are 256..128776 + f.formatOctal(field[:7], chksum) // Never fails since 128776 < 262143 + field[7] = ' ' +} + +// ComputeChecksum computes the checksum for the header block. +// POSIX specifies a sum of the unsigned byte values, but the Sun tar used +// signed byte values. +// We compute and return both. +func (b *block) ComputeChecksum() (unsigned, signed int64) { + for i, c := range b { + if 148 <= i && i < 156 { + c = ' ' // Treat the checksum field itself as all spaces. + } + unsigned += int64(c) + signed += int64(int8(c)) + } + return unsigned, signed +} + +// Reset clears the block with all zeros. +func (b *block) Reset() { + *b = block{} +} + +type headerV7 [blockSize]byte + +func (h *headerV7) Name() []byte { return h[000:][:100] } +func (h *headerV7) Mode() []byte { return h[100:][:8] } +func (h *headerV7) UID() []byte { return h[108:][:8] } +func (h *headerV7) GID() []byte { return h[116:][:8] } +func (h *headerV7) Size() []byte { return h[124:][:12] } +func (h *headerV7) ModTime() []byte { return h[136:][:12] } +func (h *headerV7) Chksum() []byte { return h[148:][:8] } +func (h *headerV7) TypeFlag() []byte { return h[156:][:1] } +func (h *headerV7) LinkName() []byte { return h[157:][:100] } + +type headerGNU [blockSize]byte + +func (h *headerGNU) V7() *headerV7 { return (*headerV7)(h) } +func (h *headerGNU) Magic() []byte { return h[257:][:6] } +func (h *headerGNU) Version() []byte { return h[263:][:2] } +func (h *headerGNU) UserName() []byte { return h[265:][:32] } +func (h *headerGNU) GroupName() []byte { return h[297:][:32] } +func (h *headerGNU) DevMajor() []byte { return h[329:][:8] } +func (h *headerGNU) DevMinor() []byte { return h[337:][:8] } +func (h *headerGNU) AccessTime() []byte { return h[345:][:12] } +func (h *headerGNU) ChangeTime() []byte { return h[357:][:12] } +func (h *headerGNU) Sparse() sparseArray { return sparseArray(h[386:][:24*4+1]) } +func (h *headerGNU) RealSize() []byte { return h[483:][:12] } + +type headerSTAR [blockSize]byte + +func (h *headerSTAR) V7() *headerV7 { return (*headerV7)(h) } +func (h *headerSTAR) Magic() []byte { return h[257:][:6] } +func (h *headerSTAR) Version() []byte { return h[263:][:2] } +func (h *headerSTAR) UserName() []byte { return h[265:][:32] } +func (h *headerSTAR) GroupName() []byte { return h[297:][:32] } +func (h *headerSTAR) DevMajor() []byte { return h[329:][:8] } +func (h *headerSTAR) DevMinor() []byte { return h[337:][:8] } +func (h *headerSTAR) Prefix() []byte { return h[345:][:131] } +func (h *headerSTAR) AccessTime() []byte { return h[476:][:12] } +func (h *headerSTAR) ChangeTime() []byte { return h[488:][:12] } +func (h *headerSTAR) Trailer() []byte { return h[508:][:4] } + +type headerUSTAR [blockSize]byte + +func (h *headerUSTAR) V7() *headerV7 { return (*headerV7)(h) } +func (h *headerUSTAR) Magic() []byte { return h[257:][:6] } +func (h *headerUSTAR) Version() []byte { return h[263:][:2] } +func (h *headerUSTAR) UserName() []byte { return h[265:][:32] } +func (h *headerUSTAR) GroupName() []byte { return h[297:][:32] } +func (h *headerUSTAR) DevMajor() []byte { return h[329:][:8] } +func (h *headerUSTAR) DevMinor() []byte { return h[337:][:8] } +func (h *headerUSTAR) Prefix() []byte { return h[345:][:155] } + +type sparseArray []byte + +func (s sparseArray) Entry(i int) sparseElem { return sparseElem(s[i*24:]) } +func (s sparseArray) IsExtended() []byte { return s[24*s.MaxEntries():][:1] } +func (s sparseArray) MaxEntries() int { return len(s) / 24 } + +type sparseElem []byte + +func (s sparseElem) Offset() []byte { return s[00:][:12] } +func (s sparseElem) Length() []byte { return s[12:][:12] } diff --git a/vendor/archive/tar/reader.go b/vendor/archive/tar/reader.go new file mode 100644 index 0000000000000..1b1d5b46891b6 --- /dev/null +++ b/vendor/archive/tar/reader.go @@ -0,0 +1,857 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "bytes" + "io" + "strconv" + "strings" + "time" +) + +// Reader provides sequential access to the contents of a tar archive. +// Reader.Next advances to the next file in the archive (including the first), +// and then Reader can be treated as an io.Reader to access the file's data. +type Reader struct { + r io.Reader + pad int64 // Amount of padding (ignored) after current file entry + curr fileReader // Reader for current file entry + blk block // Buffer to use as temporary local storage + + // err is a persistent error. + // It is only the responsibility of every exported method of Reader to + // ensure that this error is sticky. + err error +} + +type fileReader interface { + io.Reader + fileState + + WriteTo(io.Writer) (int64, error) +} + +// NewReader creates a new Reader reading from r. +func NewReader(r io.Reader) *Reader { + return &Reader{r: r, curr: ®FileReader{r, 0}} +} + +// Next advances to the next entry in the tar archive. +// The Header.Size determines how many bytes can be read for the next file. +// Any remaining data in the current file is automatically discarded. +// +// io.EOF is returned at the end of the input. +func (tr *Reader) Next() (*Header, error) { + if tr.err != nil { + return nil, tr.err + } + hdr, err := tr.next() + tr.err = err + return hdr, err +} + +func (tr *Reader) next() (*Header, error) { + var paxHdrs map[string]string + var gnuLongName, gnuLongLink string + + // Externally, Next iterates through the tar archive as if it is a series of + // files. Internally, the tar format often uses fake "files" to add meta + // data that describes the next file. These meta data "files" should not + // normally be visible to the outside. As such, this loop iterates through + // one or more "header files" until it finds a "normal file". + format := FormatUSTAR | FormatPAX | FormatGNU + for { + // Discard the remainder of the file and any padding. + if err := discard(tr.r, tr.curr.PhysicalRemaining()); err != nil { + return nil, err + } + if _, err := tryReadFull(tr.r, tr.blk[:tr.pad]); err != nil { + return nil, err + } + tr.pad = 0 + + hdr, rawHdr, err := tr.readHeader() + if err != nil { + return nil, err + } + if err := tr.handleRegularFile(hdr); err != nil { + return nil, err + } + format.mayOnlyBe(hdr.Format) + + // Check for PAX/GNU special headers and files. + switch hdr.Typeflag { + case TypeXHeader, TypeXGlobalHeader: + format.mayOnlyBe(FormatPAX) + paxHdrs, err = parsePAX(tr) + if err != nil { + return nil, err + } + if hdr.Typeflag == TypeXGlobalHeader { + mergePAX(hdr, paxHdrs) + return &Header{ + Name: hdr.Name, + Typeflag: hdr.Typeflag, + Xattrs: hdr.Xattrs, + PAXRecords: hdr.PAXRecords, + Format: format, + }, nil + } + continue // This is a meta header affecting the next header + case TypeGNULongName, TypeGNULongLink: + format.mayOnlyBe(FormatGNU) + realname, err := io.ReadAll(tr) + if err != nil { + return nil, err + } + + var p parser + switch hdr.Typeflag { + case TypeGNULongName: + gnuLongName = p.parseString(realname) + case TypeGNULongLink: + gnuLongLink = p.parseString(realname) + } + continue // This is a meta header affecting the next header + default: + // The old GNU sparse format is handled here since it is technically + // just a regular file with additional attributes. + + if err := mergePAX(hdr, paxHdrs); err != nil { + return nil, err + } + if gnuLongName != "" { + hdr.Name = gnuLongName + } + if gnuLongLink != "" { + hdr.Linkname = gnuLongLink + } + if hdr.Typeflag == TypeRegA { + if strings.HasSuffix(hdr.Name, "/") { + hdr.Typeflag = TypeDir // Legacy archives use trailing slash for directories + } else { + hdr.Typeflag = TypeReg + } + } + + // The extended headers may have updated the size. + // Thus, setup the regFileReader again after merging PAX headers. + if err := tr.handleRegularFile(hdr); err != nil { + return nil, err + } + + // Sparse formats rely on being able to read from the logical data + // section; there must be a preceding call to handleRegularFile. + if err := tr.handleSparseFile(hdr, rawHdr); err != nil { + return nil, err + } + + // Set the final guess at the format. + if format.has(FormatUSTAR) && format.has(FormatPAX) { + format.mayOnlyBe(FormatUSTAR) + } + hdr.Format = format + return hdr, nil // This is a file, so stop + } + } +} + +// handleRegularFile sets up the current file reader and padding such that it +// can only read the following logical data section. It will properly handle +// special headers that contain no data section. +func (tr *Reader) handleRegularFile(hdr *Header) error { + nb := hdr.Size + if isHeaderOnlyType(hdr.Typeflag) { + nb = 0 + } + if nb < 0 { + return ErrHeader + } + + tr.pad = blockPadding(nb) + tr.curr = ®FileReader{r: tr.r, nb: nb} + return nil +} + +// handleSparseFile checks if the current file is a sparse format of any type +// and sets the curr reader appropriately. +func (tr *Reader) handleSparseFile(hdr *Header, rawHdr *block) error { + var spd sparseDatas + var err error + if hdr.Typeflag == TypeGNUSparse { + spd, err = tr.readOldGNUSparseMap(hdr, rawHdr) + } else { + spd, err = tr.readGNUSparsePAXHeaders(hdr) + } + + // If sp is non-nil, then this is a sparse file. + // Note that it is possible for len(sp) == 0. + if err == nil && spd != nil { + if isHeaderOnlyType(hdr.Typeflag) || !validateSparseEntries(spd, hdr.Size) { + return ErrHeader + } + sph := invertSparseEntries(spd, hdr.Size) + tr.curr = &sparseFileReader{tr.curr, sph, 0} + } + return err +} + +// readGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. +// If they are found, then this function reads the sparse map and returns it. +// This assumes that 0.0 headers have already been converted to 0.1 headers +// by the PAX header parsing logic. +func (tr *Reader) readGNUSparsePAXHeaders(hdr *Header) (sparseDatas, error) { + // Identify the version of GNU headers. + var is1x0 bool + major, minor := hdr.PAXRecords[paxGNUSparseMajor], hdr.PAXRecords[paxGNUSparseMinor] + switch { + case major == "0" && (minor == "0" || minor == "1"): + is1x0 = false + case major == "1" && minor == "0": + is1x0 = true + case major != "" || minor != "": + return nil, nil // Unknown GNU sparse PAX version + case hdr.PAXRecords[paxGNUSparseMap] != "": + is1x0 = false // 0.0 and 0.1 did not have explicit version records, so guess + default: + return nil, nil // Not a PAX format GNU sparse file. + } + hdr.Format.mayOnlyBe(FormatPAX) + + // Update hdr from GNU sparse PAX headers. + if name := hdr.PAXRecords[paxGNUSparseName]; name != "" { + hdr.Name = name + } + size := hdr.PAXRecords[paxGNUSparseSize] + if size == "" { + size = hdr.PAXRecords[paxGNUSparseRealSize] + } + if size != "" { + n, err := strconv.ParseInt(size, 10, 64) + if err != nil { + return nil, ErrHeader + } + hdr.Size = n + } + + // Read the sparse map according to the appropriate format. + if is1x0 { + return readGNUSparseMap1x0(tr.curr) + } + return readGNUSparseMap0x1(hdr.PAXRecords) +} + +// mergePAX merges paxHdrs into hdr for all relevant fields of Header. +func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) { + for k, v := range paxHdrs { + if v == "" { + continue // Keep the original USTAR value + } + var id64 int64 + switch k { + case paxPath: + hdr.Name = v + case paxLinkpath: + hdr.Linkname = v + case paxUname: + hdr.Uname = v + case paxGname: + hdr.Gname = v + case paxUid: + id64, err = strconv.ParseInt(v, 10, 64) + hdr.Uid = int(id64) // Integer overflow possible + case paxGid: + id64, err = strconv.ParseInt(v, 10, 64) + hdr.Gid = int(id64) // Integer overflow possible + case paxAtime: + hdr.AccessTime, err = parsePAXTime(v) + case paxMtime: + hdr.ModTime, err = parsePAXTime(v) + case paxCtime: + hdr.ChangeTime, err = parsePAXTime(v) + case paxSize: + hdr.Size, err = strconv.ParseInt(v, 10, 64) + default: + if strings.HasPrefix(k, paxSchilyXattr) { + if hdr.Xattrs == nil { + hdr.Xattrs = make(map[string]string) + } + hdr.Xattrs[k[len(paxSchilyXattr):]] = v + } + } + if err != nil { + return ErrHeader + } + } + hdr.PAXRecords = paxHdrs + return nil +} + +// parsePAX parses PAX headers. +// If an extended header (type 'x') is invalid, ErrHeader is returned +func parsePAX(r io.Reader) (map[string]string, error) { + buf, err := io.ReadAll(r) + if err != nil { + return nil, err + } + sbuf := string(buf) + + // For GNU PAX sparse format 0.0 support. + // This function transforms the sparse format 0.0 headers into format 0.1 + // headers since 0.0 headers were not PAX compliant. + var sparseMap []string + + paxHdrs := make(map[string]string) + for len(sbuf) > 0 { + key, value, residual, err := parsePAXRecord(sbuf) + if err != nil { + return nil, ErrHeader + } + sbuf = residual + + switch key { + case paxGNUSparseOffset, paxGNUSparseNumBytes: + // Validate sparse header order and value. + if (len(sparseMap)%2 == 0 && key != paxGNUSparseOffset) || + (len(sparseMap)%2 == 1 && key != paxGNUSparseNumBytes) || + strings.Contains(value, ",") { + return nil, ErrHeader + } + sparseMap = append(sparseMap, value) + default: + paxHdrs[key] = value + } + } + if len(sparseMap) > 0 { + paxHdrs[paxGNUSparseMap] = strings.Join(sparseMap, ",") + } + return paxHdrs, nil +} + +// readHeader reads the next block header and assumes that the underlying reader +// is already aligned to a block boundary. It returns the raw block of the +// header in case further processing is required. +// +// The err will be set to io.EOF only when one of the following occurs: +// * Exactly 0 bytes are read and EOF is hit. +// * Exactly 1 block of zeros is read and EOF is hit. +// * At least 2 blocks of zeros are read. +func (tr *Reader) readHeader() (*Header, *block, error) { + // Two blocks of zero bytes marks the end of the archive. + if _, err := io.ReadFull(tr.r, tr.blk[:]); err != nil { + return nil, nil, err // EOF is okay here; exactly 0 bytes read + } + if bytes.Equal(tr.blk[:], zeroBlock[:]) { + if _, err := io.ReadFull(tr.r, tr.blk[:]); err != nil { + return nil, nil, err // EOF is okay here; exactly 1 block of zeros read + } + if bytes.Equal(tr.blk[:], zeroBlock[:]) { + return nil, nil, io.EOF // normal EOF; exactly 2 block of zeros read + } + return nil, nil, ErrHeader // Zero block and then non-zero block + } + + // Verify the header matches a known format. + format := tr.blk.GetFormat() + if format == FormatUnknown { + return nil, nil, ErrHeader + } + + var p parser + hdr := new(Header) + + // Unpack the V7 header. + v7 := tr.blk.V7() + hdr.Typeflag = v7.TypeFlag()[0] + hdr.Name = p.parseString(v7.Name()) + hdr.Linkname = p.parseString(v7.LinkName()) + hdr.Size = p.parseNumeric(v7.Size()) + hdr.Mode = p.parseNumeric(v7.Mode()) + hdr.Uid = int(p.parseNumeric(v7.UID())) + hdr.Gid = int(p.parseNumeric(v7.GID())) + hdr.ModTime = time.Unix(p.parseNumeric(v7.ModTime()), 0) + + // Unpack format specific fields. + if format > formatV7 { + ustar := tr.blk.USTAR() + hdr.Uname = p.parseString(ustar.UserName()) + hdr.Gname = p.parseString(ustar.GroupName()) + hdr.Devmajor = p.parseNumeric(ustar.DevMajor()) + hdr.Devminor = p.parseNumeric(ustar.DevMinor()) + + var prefix string + switch { + case format.has(FormatUSTAR | FormatPAX): + hdr.Format = format + ustar := tr.blk.USTAR() + prefix = p.parseString(ustar.Prefix()) + + // For Format detection, check if block is properly formatted since + // the parser is more liberal than what USTAR actually permits. + notASCII := func(r rune) bool { return r >= 0x80 } + if bytes.IndexFunc(tr.blk[:], notASCII) >= 0 { + hdr.Format = FormatUnknown // Non-ASCII characters in block. + } + nul := func(b []byte) bool { return int(b[len(b)-1]) == 0 } + if !(nul(v7.Size()) && nul(v7.Mode()) && nul(v7.UID()) && nul(v7.GID()) && + nul(v7.ModTime()) && nul(ustar.DevMajor()) && nul(ustar.DevMinor())) { + hdr.Format = FormatUnknown // Numeric fields must end in NUL + } + case format.has(formatSTAR): + star := tr.blk.STAR() + prefix = p.parseString(star.Prefix()) + hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0) + hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0) + case format.has(FormatGNU): + hdr.Format = format + var p2 parser + gnu := tr.blk.GNU() + if b := gnu.AccessTime(); b[0] != 0 { + hdr.AccessTime = time.Unix(p2.parseNumeric(b), 0) + } + if b := gnu.ChangeTime(); b[0] != 0 { + hdr.ChangeTime = time.Unix(p2.parseNumeric(b), 0) + } + + // Prior to Go1.8, the Writer had a bug where it would output + // an invalid tar file in certain rare situations because the logic + // incorrectly believed that the old GNU format had a prefix field. + // This is wrong and leads to an output file that mangles the + // atime and ctime fields, which are often left unused. + // + // In order to continue reading tar files created by former, buggy + // versions of Go, we skeptically parse the atime and ctime fields. + // If we are unable to parse them and the prefix field looks like + // an ASCII string, then we fallback on the pre-Go1.8 behavior + // of treating these fields as the USTAR prefix field. + // + // Note that this will not use the fallback logic for all possible + // files generated by a pre-Go1.8 toolchain. If the generated file + // happened to have a prefix field that parses as valid + // atime and ctime fields (e.g., when they are valid octal strings), + // then it is impossible to distinguish between a valid GNU file + // and an invalid pre-Go1.8 file. + // + // See https://golang.org/issues/12594 + // See https://golang.org/issues/21005 + if p2.err != nil { + hdr.AccessTime, hdr.ChangeTime = time.Time{}, time.Time{} + ustar := tr.blk.USTAR() + if s := p.parseString(ustar.Prefix()); isASCII(s) { + prefix = s + } + hdr.Format = FormatUnknown // Buggy file is not GNU + } + } + if len(prefix) > 0 { + hdr.Name = prefix + "/" + hdr.Name + } + } + return hdr, &tr.blk, p.err +} + +// readOldGNUSparseMap reads the sparse map from the old GNU sparse format. +// The sparse map is stored in the tar header if it's small enough. +// If it's larger than four entries, then one or more extension headers are used +// to store the rest of the sparse map. +// +// The Header.Size does not reflect the size of any extended headers used. +// Thus, this function will read from the raw io.Reader to fetch extra headers. +// This method mutates blk in the process. +func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, error) { + // Make sure that the input format is GNU. + // Unfortunately, the STAR format also has a sparse header format that uses + // the same type flag but has a completely different layout. + if blk.GetFormat() != FormatGNU { + return nil, ErrHeader + } + hdr.Format.mayOnlyBe(FormatGNU) + + var p parser + hdr.Size = p.parseNumeric(blk.GNU().RealSize()) + if p.err != nil { + return nil, p.err + } + s := blk.GNU().Sparse() + spd := make(sparseDatas, 0, s.MaxEntries()) + for { + for i := 0; i < s.MaxEntries(); i++ { + // This termination condition is identical to GNU and BSD tar. + if s.Entry(i).Offset()[0] == 0x00 { + break // Don't return, need to process extended headers (even if empty) + } + offset := p.parseNumeric(s.Entry(i).Offset()) + length := p.parseNumeric(s.Entry(i).Length()) + if p.err != nil { + return nil, p.err + } + spd = append(spd, sparseEntry{Offset: offset, Length: length}) + } + + if s.IsExtended()[0] > 0 { + // There are more entries. Read an extension header and parse its entries. + if _, err := mustReadFull(tr.r, blk[:]); err != nil { + return nil, err + } + s = blk.Sparse() + continue + } + return spd, nil // Done + } +} + +// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format +// version 1.0. The format of the sparse map consists of a series of +// newline-terminated numeric fields. The first field is the number of entries +// and is always present. Following this are the entries, consisting of two +// fields (offset, length). This function must stop reading at the end +// boundary of the block containing the last newline. +// +// Note that the GNU manual says that numeric values should be encoded in octal +// format. However, the GNU tar utility itself outputs these values in decimal. +// As such, this library treats values as being encoded in decimal. +func readGNUSparseMap1x0(r io.Reader) (sparseDatas, error) { + var ( + cntNewline int64 + buf bytes.Buffer + blk block + ) + + // feedTokens copies data in blocks from r into buf until there are + // at least cnt newlines in buf. It will not read more blocks than needed. + feedTokens := func(n int64) error { + for cntNewline < n { + if _, err := mustReadFull(r, blk[:]); err != nil { + return err + } + buf.Write(blk[:]) + for _, c := range blk { + if c == '\n' { + cntNewline++ + } + } + } + return nil + } + + // nextToken gets the next token delimited by a newline. This assumes that + // at least one newline exists in the buffer. + nextToken := func() string { + cntNewline-- + tok, _ := buf.ReadString('\n') + return strings.TrimRight(tok, "\n") + } + + // Parse for the number of entries. + // Use integer overflow resistant math to check this. + if err := feedTokens(1); err != nil { + return nil, err + } + numEntries, err := strconv.ParseInt(nextToken(), 10, 0) // Intentionally parse as native int + if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) { + return nil, ErrHeader + } + + // Parse for all member entries. + // numEntries is trusted after this since a potential attacker must have + // committed resources proportional to what this library used. + if err := feedTokens(2 * numEntries); err != nil { + return nil, err + } + spd := make(sparseDatas, 0, numEntries) + for i := int64(0); i < numEntries; i++ { + offset, err1 := strconv.ParseInt(nextToken(), 10, 64) + length, err2 := strconv.ParseInt(nextToken(), 10, 64) + if err1 != nil || err2 != nil { + return nil, ErrHeader + } + spd = append(spd, sparseEntry{Offset: offset, Length: length}) + } + return spd, nil +} + +// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format +// version 0.1. The sparse map is stored in the PAX headers. +func readGNUSparseMap0x1(paxHdrs map[string]string) (sparseDatas, error) { + // Get number of entries. + // Use integer overflow resistant math to check this. + numEntriesStr := paxHdrs[paxGNUSparseNumBlocks] + numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0) // Intentionally parse as native int + if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) { + return nil, ErrHeader + } + + // There should be two numbers in sparseMap for each entry. + sparseMap := strings.Split(paxHdrs[paxGNUSparseMap], ",") + if len(sparseMap) == 1 && sparseMap[0] == "" { + sparseMap = sparseMap[:0] + } + if int64(len(sparseMap)) != 2*numEntries { + return nil, ErrHeader + } + + // Loop through the entries in the sparse map. + // numEntries is trusted now. + spd := make(sparseDatas, 0, numEntries) + for len(sparseMap) >= 2 { + offset, err1 := strconv.ParseInt(sparseMap[0], 10, 64) + length, err2 := strconv.ParseInt(sparseMap[1], 10, 64) + if err1 != nil || err2 != nil { + return nil, ErrHeader + } + spd = append(spd, sparseEntry{Offset: offset, Length: length}) + sparseMap = sparseMap[2:] + } + return spd, nil +} + +// Read reads from the current file in the tar archive. +// It returns (0, io.EOF) when it reaches the end of that file, +// until Next is called to advance to the next file. +// +// If the current file is sparse, then the regions marked as a hole +// are read back as NUL-bytes. +// +// Calling Read on special types like TypeLink, TypeSymlink, TypeChar, +// TypeBlock, TypeDir, and TypeFifo returns (0, io.EOF) regardless of what +// the Header.Size claims. +func (tr *Reader) Read(b []byte) (int, error) { + if tr.err != nil { + return 0, tr.err + } + n, err := tr.curr.Read(b) + if err != nil && err != io.EOF { + tr.err = err + } + return n, err +} + +// writeTo writes the content of the current file to w. +// The bytes written matches the number of remaining bytes in the current file. +// +// If the current file is sparse and w is an io.WriteSeeker, +// then writeTo uses Seek to skip past holes defined in Header.SparseHoles, +// assuming that skipped regions are filled with NULs. +// This always writes the last byte to ensure w is the right size. +// +// TODO(dsnet): Re-export this when adding sparse file support. +// See https://golang.org/issue/22735 +func (tr *Reader) writeTo(w io.Writer) (int64, error) { + if tr.err != nil { + return 0, tr.err + } + n, err := tr.curr.WriteTo(w) + if err != nil { + tr.err = err + } + return n, err +} + +// regFileReader is a fileReader for reading data from a regular file entry. +type regFileReader struct { + r io.Reader // Underlying Reader + nb int64 // Number of remaining bytes to read +} + +func (fr *regFileReader) Read(b []byte) (n int, err error) { + if int64(len(b)) > fr.nb { + b = b[:fr.nb] + } + if len(b) > 0 { + n, err = fr.r.Read(b) + fr.nb -= int64(n) + } + switch { + case err == io.EOF && fr.nb > 0: + return n, io.ErrUnexpectedEOF + case err == nil && fr.nb == 0: + return n, io.EOF + default: + return n, err + } +} + +func (fr *regFileReader) WriteTo(w io.Writer) (int64, error) { + return io.Copy(w, struct{ io.Reader }{fr}) +} + +func (fr regFileReader) LogicalRemaining() int64 { + return fr.nb +} + +func (fr regFileReader) PhysicalRemaining() int64 { + return fr.nb +} + +// sparseFileReader is a fileReader for reading data from a sparse file entry. +type sparseFileReader struct { + fr fileReader // Underlying fileReader + sp sparseHoles // Normalized list of sparse holes + pos int64 // Current position in sparse file +} + +func (sr *sparseFileReader) Read(b []byte) (n int, err error) { + finished := int64(len(b)) >= sr.LogicalRemaining() + if finished { + b = b[:sr.LogicalRemaining()] + } + + b0 := b + endPos := sr.pos + int64(len(b)) + for endPos > sr.pos && err == nil { + var nf int // Bytes read in fragment + holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset() + if sr.pos < holeStart { // In a data fragment + bf := b[:min(int64(len(b)), holeStart-sr.pos)] + nf, err = tryReadFull(sr.fr, bf) + } else { // In a hole fragment + bf := b[:min(int64(len(b)), holeEnd-sr.pos)] + nf, err = tryReadFull(zeroReader{}, bf) + } + b = b[nf:] + sr.pos += int64(nf) + if sr.pos >= holeEnd && len(sr.sp) > 1 { + sr.sp = sr.sp[1:] // Ensure last fragment always remains + } + } + + n = len(b0) - len(b) + switch { + case err == io.EOF: + return n, errMissData // Less data in dense file than sparse file + case err != nil: + return n, err + case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: + return n, errUnrefData // More data in dense file than sparse file + case finished: + return n, io.EOF + default: + return n, nil + } +} + +func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) { + ws, ok := w.(io.WriteSeeker) + if ok { + if _, err := ws.Seek(0, io.SeekCurrent); err != nil { + ok = false // Not all io.Seeker can really seek + } + } + if !ok { + return io.Copy(w, struct{ io.Reader }{sr}) + } + + var writeLastByte bool + pos0 := sr.pos + for sr.LogicalRemaining() > 0 && !writeLastByte && err == nil { + var nf int64 // Size of fragment + holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset() + if sr.pos < holeStart { // In a data fragment + nf = holeStart - sr.pos + nf, err = io.CopyN(ws, sr.fr, nf) + } else { // In a hole fragment + nf = holeEnd - sr.pos + if sr.PhysicalRemaining() == 0 { + writeLastByte = true + nf-- + } + _, err = ws.Seek(nf, io.SeekCurrent) + } + sr.pos += nf + if sr.pos >= holeEnd && len(sr.sp) > 1 { + sr.sp = sr.sp[1:] // Ensure last fragment always remains + } + } + + // If the last fragment is a hole, then seek to 1-byte before EOF, and + // write a single byte to ensure the file is the right size. + if writeLastByte && err == nil { + _, err = ws.Write([]byte{0}) + sr.pos++ + } + + n = sr.pos - pos0 + switch { + case err == io.EOF: + return n, errMissData // Less data in dense file than sparse file + case err != nil: + return n, err + case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: + return n, errUnrefData // More data in dense file than sparse file + default: + return n, nil + } +} + +func (sr sparseFileReader) LogicalRemaining() int64 { + return sr.sp[len(sr.sp)-1].endOffset() - sr.pos +} +func (sr sparseFileReader) PhysicalRemaining() int64 { + return sr.fr.PhysicalRemaining() +} + +type zeroReader struct{} + +func (zeroReader) Read(b []byte) (int, error) { + for i := range b { + b[i] = 0 + } + return len(b), nil +} + +// mustReadFull is like io.ReadFull except it returns +// io.ErrUnexpectedEOF when io.EOF is hit before len(b) bytes are read. +func mustReadFull(r io.Reader, b []byte) (int, error) { + n, err := tryReadFull(r, b) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + return n, err +} + +// tryReadFull is like io.ReadFull except it returns +// io.EOF when it is hit before len(b) bytes are read. +func tryReadFull(r io.Reader, b []byte) (n int, err error) { + for len(b) > n && err == nil { + var nn int + nn, err = r.Read(b[n:]) + n += nn + } + if len(b) == n && err == io.EOF { + err = nil + } + return n, err +} + +// discard skips n bytes in r, reporting an error if unable to do so. +func discard(r io.Reader, n int64) error { + // If possible, Seek to the last byte before the end of the data section. + // Do this because Seek is often lazy about reporting errors; this will mask + // the fact that the stream may be truncated. We can rely on the + // io.CopyN done shortly afterwards to trigger any IO errors. + var seekSkipped int64 // Number of bytes skipped via Seek + if sr, ok := r.(io.Seeker); ok && n > 1 { + // Not all io.Seeker can actually Seek. For example, os.Stdin implements + // io.Seeker, but calling Seek always returns an error and performs + // no action. Thus, we try an innocent seek to the current position + // to see if Seek is really supported. + pos1, err := sr.Seek(0, io.SeekCurrent) + if pos1 >= 0 && err == nil { + // Seek seems supported, so perform the real Seek. + pos2, err := sr.Seek(n-1, io.SeekCurrent) + if pos2 < 0 || err != nil { + return err + } + seekSkipped = pos2 - pos1 + } + } + + copySkipped, err := io.CopyN(io.Discard, r, n-seekSkipped) + if err == io.EOF && seekSkipped+copySkipped < n { + err = io.ErrUnexpectedEOF + } + return err +} diff --git a/vendor/archive/tar/reader_test.go b/vendor/archive/tar/reader_test.go new file mode 100644 index 0000000000000..789ddc1bc0345 --- /dev/null +++ b/vendor/archive/tar/reader_test.go @@ -0,0 +1,1610 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "bytes" + "crypto/md5" + "errors" + "fmt" + "io" + "math" + "os" + "path" + "reflect" + "strconv" + "strings" + "testing" + "time" +) + +func TestReader(t *testing.T) { + vectors := []struct { + file string // Test input file + headers []*Header // Expected output headers + chksums []string // MD5 checksum of files, leave as nil if not checked + err error // Expected error to occur + }{{ + file: "testdata/gnu.tar", + headers: []*Header{{ + Name: "small.txt", + Mode: 0640, + Uid: 73025, + Gid: 5000, + Size: 5, + ModTime: time.Unix(1244428340, 0), + Typeflag: '0', + Uname: "dsymonds", + Gname: "eng", + Format: FormatGNU, + }, { + Name: "small2.txt", + Mode: 0640, + Uid: 73025, + Gid: 5000, + Size: 11, + ModTime: time.Unix(1244436044, 0), + Typeflag: '0', + Uname: "dsymonds", + Gname: "eng", + Format: FormatGNU, + }}, + chksums: []string{ + "e38b27eaccb4391bdec553a7f3ae6b2f", + "c65bd2e50a56a2138bf1716f2fd56fe9", + }, + }, { + file: "testdata/sparse-formats.tar", + headers: []*Header{{ + Name: "sparse-gnu", + Mode: 420, + Uid: 1000, + Gid: 1000, + Size: 200, + ModTime: time.Unix(1392395740, 0), + Typeflag: 0x53, + Linkname: "", + Uname: "david", + Gname: "david", + Devmajor: 0, + Devminor: 0, + Format: FormatGNU, + }, { + Name: "sparse-posix-0.0", + Mode: 420, + Uid: 1000, + Gid: 1000, + Size: 200, + ModTime: time.Unix(1392342187, 0), + Typeflag: 0x30, + Linkname: "", + Uname: "david", + Gname: "david", + Devmajor: 0, + Devminor: 0, + PAXRecords: map[string]string{ + "GNU.sparse.size": "200", + "GNU.sparse.numblocks": "95", + "GNU.sparse.map": "1,1,3,1,5,1,7,1,9,1,11,1,13,1,15,1,17,1,19,1,21,1,23,1,25,1,27,1,29,1,31,1,33,1,35,1,37,1,39,1,41,1,43,1,45,1,47,1,49,1,51,1,53,1,55,1,57,1,59,1,61,1,63,1,65,1,67,1,69,1,71,1,73,1,75,1,77,1,79,1,81,1,83,1,85,1,87,1,89,1,91,1,93,1,95,1,97,1,99,1,101,1,103,1,105,1,107,1,109,1,111,1,113,1,115,1,117,1,119,1,121,1,123,1,125,1,127,1,129,1,131,1,133,1,135,1,137,1,139,1,141,1,143,1,145,1,147,1,149,1,151,1,153,1,155,1,157,1,159,1,161,1,163,1,165,1,167,1,169,1,171,1,173,1,175,1,177,1,179,1,181,1,183,1,185,1,187,1,189,1", + }, + Format: FormatPAX, + }, { + Name: "sparse-posix-0.1", + Mode: 420, + Uid: 1000, + Gid: 1000, + Size: 200, + ModTime: time.Unix(1392340456, 0), + Typeflag: 0x30, + Linkname: "", + Uname: "david", + Gname: "david", + Devmajor: 0, + Devminor: 0, + PAXRecords: map[string]string{ + "GNU.sparse.size": "200", + "GNU.sparse.numblocks": "95", + "GNU.sparse.map": "1,1,3,1,5,1,7,1,9,1,11,1,13,1,15,1,17,1,19,1,21,1,23,1,25,1,27,1,29,1,31,1,33,1,35,1,37,1,39,1,41,1,43,1,45,1,47,1,49,1,51,1,53,1,55,1,57,1,59,1,61,1,63,1,65,1,67,1,69,1,71,1,73,1,75,1,77,1,79,1,81,1,83,1,85,1,87,1,89,1,91,1,93,1,95,1,97,1,99,1,101,1,103,1,105,1,107,1,109,1,111,1,113,1,115,1,117,1,119,1,121,1,123,1,125,1,127,1,129,1,131,1,133,1,135,1,137,1,139,1,141,1,143,1,145,1,147,1,149,1,151,1,153,1,155,1,157,1,159,1,161,1,163,1,165,1,167,1,169,1,171,1,173,1,175,1,177,1,179,1,181,1,183,1,185,1,187,1,189,1", + "GNU.sparse.name": "sparse-posix-0.1", + }, + Format: FormatPAX, + }, { + Name: "sparse-posix-1.0", + Mode: 420, + Uid: 1000, + Gid: 1000, + Size: 200, + ModTime: time.Unix(1392337404, 0), + Typeflag: 0x30, + Linkname: "", + Uname: "david", + Gname: "david", + Devmajor: 0, + Devminor: 0, + PAXRecords: map[string]string{ + "GNU.sparse.major": "1", + "GNU.sparse.minor": "0", + "GNU.sparse.realsize": "200", + "GNU.sparse.name": "sparse-posix-1.0", + }, + Format: FormatPAX, + }, { + Name: "end", + Mode: 420, + Uid: 1000, + Gid: 1000, + Size: 4, + ModTime: time.Unix(1392398319, 0), + Typeflag: 0x30, + Linkname: "", + Uname: "david", + Gname: "david", + Devmajor: 0, + Devminor: 0, + Format: FormatGNU, + }}, + chksums: []string{ + "6f53234398c2449fe67c1812d993012f", + "6f53234398c2449fe67c1812d993012f", + "6f53234398c2449fe67c1812d993012f", + "6f53234398c2449fe67c1812d993012f", + "b0061974914468de549a2af8ced10316", + }, + }, { + file: "testdata/star.tar", + headers: []*Header{{ + Name: "small.txt", + Mode: 0640, + Uid: 73025, + Gid: 5000, + Size: 5, + ModTime: time.Unix(1244592783, 0), + Typeflag: '0', + Uname: "dsymonds", + Gname: "eng", + AccessTime: time.Unix(1244592783, 0), + ChangeTime: time.Unix(1244592783, 0), + }, { + Name: "small2.txt", + Mode: 0640, + Uid: 73025, + Gid: 5000, + Size: 11, + ModTime: time.Unix(1244592783, 0), + Typeflag: '0', + Uname: "dsymonds", + Gname: "eng", + AccessTime: time.Unix(1244592783, 0), + ChangeTime: time.Unix(1244592783, 0), + }}, + }, { + file: "testdata/v7.tar", + headers: []*Header{{ + Name: "small.txt", + Mode: 0444, + Uid: 73025, + Gid: 5000, + Size: 5, + ModTime: time.Unix(1244593104, 0), + Typeflag: '0', + }, { + Name: "small2.txt", + Mode: 0444, + Uid: 73025, + Gid: 5000, + Size: 11, + ModTime: time.Unix(1244593104, 0), + Typeflag: '0', + }}, + }, { + file: "testdata/pax.tar", + headers: []*Header{{ + Name: "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", + Mode: 0664, + Uid: 1000, + Gid: 1000, + Uname: "shane", + Gname: "shane", + Size: 7, + ModTime: time.Unix(1350244992, 23960108), + ChangeTime: time.Unix(1350244992, 23960108), + AccessTime: time.Unix(1350244992, 23960108), + Typeflag: TypeReg, + PAXRecords: map[string]string{ + "path": "a/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", + "mtime": "1350244992.023960108", + "atime": "1350244992.023960108", + "ctime": "1350244992.023960108", + }, + Format: FormatPAX, + }, { + Name: "a/b", + Mode: 0777, + Uid: 1000, + Gid: 1000, + Uname: "shane", + Gname: "shane", + Size: 0, + ModTime: time.Unix(1350266320, 910238425), + ChangeTime: time.Unix(1350266320, 910238425), + AccessTime: time.Unix(1350266320, 910238425), + Typeflag: TypeSymlink, + Linkname: "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", + PAXRecords: map[string]string{ + "linkpath": "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100", + "mtime": "1350266320.910238425", + "atime": "1350266320.910238425", + "ctime": "1350266320.910238425", + }, + Format: FormatPAX, + }}, + }, { + file: "testdata/pax-bad-hdr-file.tar", + err: ErrHeader, + }, { + file: "testdata/pax-bad-mtime-file.tar", + err: ErrHeader, + }, { + file: "testdata/pax-pos-size-file.tar", + headers: []*Header{{ + Name: "foo", + Mode: 0640, + Uid: 319973, + Gid: 5000, + Size: 999, + ModTime: time.Unix(1442282516, 0), + Typeflag: '0', + Uname: "joetsai", + Gname: "eng", + PAXRecords: map[string]string{ + "size": "000000000000000000000999", + }, + Format: FormatPAX, + }}, + chksums: []string{ + "0afb597b283fe61b5d4879669a350556", + }, + }, { + file: "testdata/pax-records.tar", + headers: []*Header{{ + Typeflag: TypeReg, + Name: "file", + Uname: strings.Repeat("long", 10), + ModTime: time.Unix(0, 0), + PAXRecords: map[string]string{ + "GOLANG.pkg": "tar", + "comment": "Hello, 世界", + "uname": strings.Repeat("long", 10), + }, + Format: FormatPAX, + }}, + }, { + file: "testdata/pax-global-records.tar", + headers: []*Header{{ + Typeflag: TypeXGlobalHeader, + Name: "global1", + PAXRecords: map[string]string{"path": "global1", "mtime": "1500000000.0"}, + Format: FormatPAX, + }, { + Typeflag: TypeReg, + Name: "file1", + ModTime: time.Unix(0, 0), + Format: FormatUSTAR, + }, { + Typeflag: TypeReg, + Name: "file2", + PAXRecords: map[string]string{"path": "file2"}, + ModTime: time.Unix(0, 0), + Format: FormatPAX, + }, { + Typeflag: TypeXGlobalHeader, + Name: "GlobalHead.0.0", + PAXRecords: map[string]string{"path": ""}, + Format: FormatPAX, + }, { + Typeflag: TypeReg, + Name: "file3", + ModTime: time.Unix(0, 0), + Format: FormatUSTAR, + }, { + Typeflag: TypeReg, + Name: "file4", + ModTime: time.Unix(1400000000, 0), + PAXRecords: map[string]string{"mtime": "1400000000"}, + Format: FormatPAX, + }}, + }, { + file: "testdata/nil-uid.tar", // golang.org/issue/5290 + headers: []*Header{{ + Name: "P1050238.JPG.log", + Mode: 0664, + Uid: 0, + Gid: 0, + Size: 14, + ModTime: time.Unix(1365454838, 0), + Typeflag: TypeReg, + Linkname: "", + Uname: "eyefi", + Gname: "eyefi", + Devmajor: 0, + Devminor: 0, + Format: FormatGNU, + }}, + }, { + file: "testdata/xattrs.tar", + headers: []*Header{{ + Name: "small.txt", + Mode: 0644, + Uid: 1000, + Gid: 10, + Size: 5, + ModTime: time.Unix(1386065770, 448252320), + Typeflag: '0', + Uname: "alex", + Gname: "wheel", + AccessTime: time.Unix(1389782991, 419875220), + ChangeTime: time.Unix(1389782956, 794414986), + Xattrs: map[string]string{ + "user.key": "value", + "user.key2": "value2", + // Interestingly, selinux encodes the terminating null inside the xattr + "security.selinux": "unconfined_u:object_r:default_t:s0\x00", + }, + PAXRecords: map[string]string{ + "mtime": "1386065770.44825232", + "atime": "1389782991.41987522", + "ctime": "1389782956.794414986", + "SCHILY.xattr.user.key": "value", + "SCHILY.xattr.user.key2": "value2", + "SCHILY.xattr.security.selinux": "unconfined_u:object_r:default_t:s0\x00", + }, + Format: FormatPAX, + }, { + Name: "small2.txt", + Mode: 0644, + Uid: 1000, + Gid: 10, + Size: 11, + ModTime: time.Unix(1386065770, 449252304), + Typeflag: '0', + Uname: "alex", + Gname: "wheel", + AccessTime: time.Unix(1389782991, 419875220), + ChangeTime: time.Unix(1386065770, 449252304), + Xattrs: map[string]string{ + "security.selinux": "unconfined_u:object_r:default_t:s0\x00", + }, + PAXRecords: map[string]string{ + "mtime": "1386065770.449252304", + "atime": "1389782991.41987522", + "ctime": "1386065770.449252304", + "SCHILY.xattr.security.selinux": "unconfined_u:object_r:default_t:s0\x00", + }, + Format: FormatPAX, + }}, + }, { + // Matches the behavior of GNU, BSD, and STAR tar utilities. + file: "testdata/gnu-multi-hdrs.tar", + headers: []*Header{{ + Name: "GNU2/GNU2/long-path-name", + Linkname: "GNU4/GNU4/long-linkpath-name", + ModTime: time.Unix(0, 0), + Typeflag: '2', + Format: FormatGNU, + }}, + }, { + // GNU tar file with atime and ctime fields set. + // Created with the GNU tar v1.27.1. + // tar --incremental -S -cvf gnu-incremental.tar test2 + file: "testdata/gnu-incremental.tar", + headers: []*Header{{ + Name: "test2/", + Mode: 16877, + Uid: 1000, + Gid: 1000, + Size: 14, + ModTime: time.Unix(1441973427, 0), + Typeflag: 'D', + Uname: "rawr", + Gname: "dsnet", + AccessTime: time.Unix(1441974501, 0), + ChangeTime: time.Unix(1441973436, 0), + Format: FormatGNU, + }, { + Name: "test2/foo", + Mode: 33188, + Uid: 1000, + Gid: 1000, + Size: 64, + ModTime: time.Unix(1441973363, 0), + Typeflag: '0', + Uname: "rawr", + Gname: "dsnet", + AccessTime: time.Unix(1441974501, 0), + ChangeTime: time.Unix(1441973436, 0), + Format: FormatGNU, + }, { + Name: "test2/sparse", + Mode: 33188, + Uid: 1000, + Gid: 1000, + Size: 536870912, + ModTime: time.Unix(1441973427, 0), + Typeflag: 'S', + Uname: "rawr", + Gname: "dsnet", + AccessTime: time.Unix(1441991948, 0), + ChangeTime: time.Unix(1441973436, 0), + Format: FormatGNU, + }}, + }, { + // Matches the behavior of GNU and BSD tar utilities. + file: "testdata/pax-multi-hdrs.tar", + headers: []*Header{{ + Name: "bar", + Linkname: "PAX4/PAX4/long-linkpath-name", + ModTime: time.Unix(0, 0), + Typeflag: '2', + PAXRecords: map[string]string{ + "linkpath": "PAX4/PAX4/long-linkpath-name", + }, + Format: FormatPAX, + }}, + }, { + // Both BSD and GNU tar truncate long names at first NUL even + // if there is data following that NUL character. + // This is reasonable as GNU long names are C-strings. + file: "testdata/gnu-long-nul.tar", + headers: []*Header{{ + Name: "0123456789", + Mode: 0644, + Uid: 1000, + Gid: 1000, + ModTime: time.Unix(1486082191, 0), + Typeflag: '0', + Uname: "rawr", + Gname: "dsnet", + Format: FormatGNU, + }}, + }, { + // This archive was generated by Writer but is readable by both + // GNU and BSD tar utilities. + // The archive generated by GNU is nearly byte-for-byte identical + // to the Go version except the Go version sets a negative Devminor + // just to force the GNU format. + file: "testdata/gnu-utf8.tar", + headers: []*Header{{ + Name: "☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹", + Mode: 0644, + Uid: 1000, Gid: 1000, + ModTime: time.Unix(0, 0), + Typeflag: '0', + Uname: "☺", + Gname: "⚹", + Format: FormatGNU, + }}, + }, { + // This archive was generated by Writer but is readable by both + // GNU and BSD tar utilities. + // The archive generated by GNU is nearly byte-for-byte identical + // to the Go version except the Go version sets a negative Devminor + // just to force the GNU format. + file: "testdata/gnu-not-utf8.tar", + headers: []*Header{{ + Name: "hi\x80\x81\x82\x83bye", + Mode: 0644, + Uid: 1000, + Gid: 1000, + ModTime: time.Unix(0, 0), + Typeflag: '0', + Uname: "rawr", + Gname: "dsnet", + Format: FormatGNU, + }}, + }, { + // BSD tar v3.1.2 and GNU tar v1.27.1 both rejects PAX records + // with NULs in the key. + file: "testdata/pax-nul-xattrs.tar", + err: ErrHeader, + }, { + // BSD tar v3.1.2 rejects a PAX path with NUL in the value, while + // GNU tar v1.27.1 simply truncates at first NUL. + // We emulate the behavior of BSD since it is strange doing NUL + // truncations since PAX records are length-prefix strings instead + // of NUL-terminated C-strings. + file: "testdata/pax-nul-path.tar", + err: ErrHeader, + }, { + file: "testdata/neg-size.tar", + err: ErrHeader, + }, { + file: "testdata/issue10968.tar", + err: ErrHeader, + }, { + file: "testdata/issue11169.tar", + err: ErrHeader, + }, { + file: "testdata/issue12435.tar", + err: ErrHeader, + }, { + // Ensure that we can read back the original Header as written with + // a buggy pre-Go1.8 tar.Writer. + file: "testdata/invalid-go17.tar", + headers: []*Header{{ + Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo", + Uid: 010000000, + ModTime: time.Unix(0, 0), + Typeflag: '0', + }}, + }, { + // USTAR archive with a regular entry with non-zero device numbers. + file: "testdata/ustar-file-devs.tar", + headers: []*Header{{ + Name: "file", + Mode: 0644, + Typeflag: '0', + ModTime: time.Unix(0, 0), + Devmajor: 1, + Devminor: 1, + Format: FormatUSTAR, + }}, + }, { + // Generated by Go, works on BSD tar v3.1.2 and GNU tar v.1.27.1. + file: "testdata/gnu-nil-sparse-data.tar", + headers: []*Header{{ + Name: "sparse.db", + Typeflag: TypeGNUSparse, + Size: 1000, + ModTime: time.Unix(0, 0), + Format: FormatGNU, + }}, + }, { + // Generated by Go, works on BSD tar v3.1.2 and GNU tar v.1.27.1. + file: "testdata/gnu-nil-sparse-hole.tar", + headers: []*Header{{ + Name: "sparse.db", + Typeflag: TypeGNUSparse, + Size: 1000, + ModTime: time.Unix(0, 0), + Format: FormatGNU, + }}, + }, { + // Generated by Go, works on BSD tar v3.1.2 and GNU tar v.1.27.1. + file: "testdata/pax-nil-sparse-data.tar", + headers: []*Header{{ + Name: "sparse.db", + Typeflag: TypeReg, + Size: 1000, + ModTime: time.Unix(0, 0), + PAXRecords: map[string]string{ + "size": "1512", + "GNU.sparse.major": "1", + "GNU.sparse.minor": "0", + "GNU.sparse.realsize": "1000", + "GNU.sparse.name": "sparse.db", + }, + Format: FormatPAX, + }}, + }, { + // Generated by Go, works on BSD tar v3.1.2 and GNU tar v.1.27.1. + file: "testdata/pax-nil-sparse-hole.tar", + headers: []*Header{{ + Name: "sparse.db", + Typeflag: TypeReg, + Size: 1000, + ModTime: time.Unix(0, 0), + PAXRecords: map[string]string{ + "size": "512", + "GNU.sparse.major": "1", + "GNU.sparse.minor": "0", + "GNU.sparse.realsize": "1000", + "GNU.sparse.name": "sparse.db", + }, + Format: FormatPAX, + }}, + }, { + file: "testdata/trailing-slash.tar", + headers: []*Header{{ + Typeflag: TypeDir, + Name: strings.Repeat("123456789/", 30), + ModTime: time.Unix(0, 0), + PAXRecords: map[string]string{ + "path": strings.Repeat("123456789/", 30), + }, + Format: FormatPAX, + }}, + }} + + for _, v := range vectors { + t.Run(path.Base(v.file), func(t *testing.T) { + f, err := os.Open(v.file) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer f.Close() + + // Capture all headers and checksums. + var ( + tr = NewReader(f) + hdrs []*Header + chksums []string + rdbuf = make([]byte, 8) + ) + for { + var hdr *Header + hdr, err = tr.Next() + if err != nil { + if err == io.EOF { + err = nil // Expected error + } + break + } + hdrs = append(hdrs, hdr) + + if v.chksums == nil { + continue + } + h := md5.New() + _, err = io.CopyBuffer(h, tr, rdbuf) // Effectively an incremental read + if err != nil { + break + } + chksums = append(chksums, fmt.Sprintf("%x", h.Sum(nil))) + } + + for i, hdr := range hdrs { + if i >= len(v.headers) { + t.Fatalf("entry %d: unexpected header:\ngot %+v", i, *hdr) + continue + } + if !reflect.DeepEqual(*hdr, *v.headers[i]) { + t.Fatalf("entry %d: incorrect header:\ngot %+v\nwant %+v", i, *hdr, *v.headers[i]) + } + } + if len(hdrs) != len(v.headers) { + t.Fatalf("got %d headers, want %d headers", len(hdrs), len(v.headers)) + } + + for i, sum := range chksums { + if i >= len(v.chksums) { + t.Fatalf("entry %d: unexpected sum: got %s", i, sum) + continue + } + if sum != v.chksums[i] { + t.Fatalf("entry %d: incorrect checksum: got %s, want %s", i, sum, v.chksums[i]) + } + } + + if err != v.err { + t.Fatalf("unexpected error: got %v, want %v", err, v.err) + } + f.Close() + }) + } +} + +func TestPartialRead(t *testing.T) { + type testCase struct { + cnt int // Number of bytes to read + output string // Expected value of string read + } + vectors := []struct { + file string + cases []testCase + }{{ + file: "testdata/gnu.tar", + cases: []testCase{ + {4, "Kilt"}, + {6, "Google"}, + }, + }, { + file: "testdata/sparse-formats.tar", + cases: []testCase{ + {2, "\x00G"}, + {4, "\x00G\x00o"}, + {6, "\x00G\x00o\x00G"}, + {8, "\x00G\x00o\x00G\x00o"}, + {4, "end\n"}, + }, + }} + + for _, v := range vectors { + t.Run(path.Base(v.file), func(t *testing.T) { + f, err := os.Open(v.file) + if err != nil { + t.Fatalf("Open() error: %v", err) + } + defer f.Close() + + tr := NewReader(f) + for i, tc := range v.cases { + hdr, err := tr.Next() + if err != nil || hdr == nil { + t.Fatalf("entry %d, Next(): got %v, want %v", i, err, nil) + } + buf := make([]byte, tc.cnt) + if _, err := io.ReadFull(tr, buf); err != nil { + t.Fatalf("entry %d, ReadFull(): got %v, want %v", i, err, nil) + } + if string(buf) != tc.output { + t.Fatalf("entry %d, ReadFull(): got %q, want %q", i, string(buf), tc.output) + } + } + + if _, err := tr.Next(); err != io.EOF { + t.Fatalf("Next(): got %v, want EOF", err) + } + }) + } +} + +func TestUninitializedRead(t *testing.T) { + f, err := os.Open("testdata/gnu.tar") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + defer f.Close() + + tr := NewReader(f) + _, err = tr.Read([]byte{}) + if err == nil || err != io.EOF { + t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF) + } + +} + +type reader struct{ io.Reader } +type readSeeker struct{ io.ReadSeeker } +type readBadSeeker struct{ io.ReadSeeker } + +func (rbs *readBadSeeker) Seek(int64, int) (int64, error) { return 0, fmt.Errorf("illegal seek") } + +// TestReadTruncation test the ending condition on various truncated files and +// that truncated files are still detected even if the underlying io.Reader +// satisfies io.Seeker. +func TestReadTruncation(t *testing.T) { + var ss []string + for _, p := range []string{ + "testdata/gnu.tar", + "testdata/ustar-file-reg.tar", + "testdata/pax-path-hdr.tar", + "testdata/sparse-formats.tar", + } { + buf, err := os.ReadFile(p) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + ss = append(ss, string(buf)) + } + + data1, data2, pax, sparse := ss[0], ss[1], ss[2], ss[3] + data2 += strings.Repeat("\x00", 10*512) + trash := strings.Repeat("garbage ", 64) // Exactly 512 bytes + + vectors := []struct { + input string // Input stream + cnt int // Expected number of headers read + err error // Expected error outcome + }{ + {"", 0, io.EOF}, // Empty file is a "valid" tar file + {data1[:511], 0, io.ErrUnexpectedEOF}, + {data1[:512], 1, io.ErrUnexpectedEOF}, + {data1[:1024], 1, io.EOF}, + {data1[:1536], 2, io.ErrUnexpectedEOF}, + {data1[:2048], 2, io.EOF}, + {data1, 2, io.EOF}, + {data1[:2048] + data2[:1536], 3, io.EOF}, + {data2[:511], 0, io.ErrUnexpectedEOF}, + {data2[:512], 1, io.ErrUnexpectedEOF}, + {data2[:1195], 1, io.ErrUnexpectedEOF}, + {data2[:1196], 1, io.EOF}, // Exact end of data and start of padding + {data2[:1200], 1, io.EOF}, + {data2[:1535], 1, io.EOF}, + {data2[:1536], 1, io.EOF}, // Exact end of padding + {data2[:1536] + trash[:1], 1, io.ErrUnexpectedEOF}, + {data2[:1536] + trash[:511], 1, io.ErrUnexpectedEOF}, + {data2[:1536] + trash, 1, ErrHeader}, + {data2[:2048], 1, io.EOF}, // Exactly 1 empty block + {data2[:2048] + trash[:1], 1, io.ErrUnexpectedEOF}, + {data2[:2048] + trash[:511], 1, io.ErrUnexpectedEOF}, + {data2[:2048] + trash, 1, ErrHeader}, + {data2[:2560], 1, io.EOF}, // Exactly 2 empty blocks (normal end-of-stream) + {data2[:2560] + trash[:1], 1, io.EOF}, + {data2[:2560] + trash[:511], 1, io.EOF}, + {data2[:2560] + trash, 1, io.EOF}, + {data2[:3072], 1, io.EOF}, + {pax, 0, io.EOF}, // PAX header without data is a "valid" tar file + {pax + trash[:1], 0, io.ErrUnexpectedEOF}, + {pax + trash[:511], 0, io.ErrUnexpectedEOF}, + {sparse[:511], 0, io.ErrUnexpectedEOF}, + {sparse[:512], 0, io.ErrUnexpectedEOF}, + {sparse[:3584], 1, io.EOF}, + {sparse[:9200], 1, io.EOF}, // Terminate in padding of sparse header + {sparse[:9216], 1, io.EOF}, + {sparse[:9728], 2, io.ErrUnexpectedEOF}, + {sparse[:10240], 2, io.EOF}, + {sparse[:11264], 2, io.ErrUnexpectedEOF}, + {sparse, 5, io.EOF}, + {sparse + trash, 5, io.EOF}, + } + + for i, v := range vectors { + for j := 0; j < 6; j++ { + var tr *Reader + var s1, s2 string + + switch j { + case 0: + tr = NewReader(&reader{strings.NewReader(v.input)}) + s1, s2 = "io.Reader", "auto" + case 1: + tr = NewReader(&reader{strings.NewReader(v.input)}) + s1, s2 = "io.Reader", "manual" + case 2: + tr = NewReader(&readSeeker{strings.NewReader(v.input)}) + s1, s2 = "io.ReadSeeker", "auto" + case 3: + tr = NewReader(&readSeeker{strings.NewReader(v.input)}) + s1, s2 = "io.ReadSeeker", "manual" + case 4: + tr = NewReader(&readBadSeeker{strings.NewReader(v.input)}) + s1, s2 = "ReadBadSeeker", "auto" + case 5: + tr = NewReader(&readBadSeeker{strings.NewReader(v.input)}) + s1, s2 = "ReadBadSeeker", "manual" + } + + var cnt int + var err error + for { + if _, err = tr.Next(); err != nil { + break + } + cnt++ + if s2 == "manual" { + if _, err = tr.writeTo(io.Discard); err != nil { + break + } + } + } + if err != v.err { + t.Errorf("test %d, NewReader(%s) with %s discard: got %v, want %v", + i, s1, s2, err, v.err) + } + if cnt != v.cnt { + t.Errorf("test %d, NewReader(%s) with %s discard: got %d headers, want %d headers", + i, s1, s2, cnt, v.cnt) + } + } + } +} + +// TestReadHeaderOnly tests that Reader does not attempt to read special +// header-only files. +func TestReadHeaderOnly(t *testing.T) { + f, err := os.Open("testdata/hdr-only.tar") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer f.Close() + + var hdrs []*Header + tr := NewReader(f) + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + t.Errorf("Next(): got %v, want %v", err, nil) + continue + } + hdrs = append(hdrs, hdr) + + // If a special flag, we should read nothing. + cnt, _ := io.ReadFull(tr, []byte{0}) + if cnt > 0 && hdr.Typeflag != TypeReg { + t.Errorf("ReadFull(...): got %d bytes, want 0 bytes", cnt) + } + } + + // File is crafted with 16 entries. The later 8 are identical to the first + // 8 except that the size is set. + if len(hdrs) != 16 { + t.Fatalf("len(hdrs): got %d, want %d", len(hdrs), 16) + } + for i := 0; i < 8; i++ { + hdr1, hdr2 := hdrs[i+0], hdrs[i+8] + hdr1.Size, hdr2.Size = 0, 0 + if !reflect.DeepEqual(*hdr1, *hdr2) { + t.Errorf("incorrect header:\ngot %+v\nwant %+v", *hdr1, *hdr2) + } + } +} + +func TestMergePAX(t *testing.T) { + vectors := []struct { + in map[string]string + want *Header + ok bool + }{{ + in: map[string]string{ + "path": "a/b/c", + "uid": "1000", + "mtime": "1350244992.023960108", + }, + want: &Header{ + Name: "a/b/c", + Uid: 1000, + ModTime: time.Unix(1350244992, 23960108), + PAXRecords: map[string]string{ + "path": "a/b/c", + "uid": "1000", + "mtime": "1350244992.023960108", + }, + }, + ok: true, + }, { + in: map[string]string{ + "gid": "gtgergergersagersgers", + }, + ok: false, + }, { + in: map[string]string{ + "missing": "missing", + "SCHILY.xattr.key": "value", + }, + want: &Header{ + Xattrs: map[string]string{"key": "value"}, + PAXRecords: map[string]string{ + "missing": "missing", + "SCHILY.xattr.key": "value", + }, + }, + ok: true, + }} + + for i, v := range vectors { + got := new(Header) + err := mergePAX(got, v.in) + if v.ok && !reflect.DeepEqual(*got, *v.want) { + t.Errorf("test %d, mergePAX(...):\ngot %+v\nwant %+v", i, *got, *v.want) + } + if ok := err == nil; ok != v.ok { + t.Errorf("test %d, mergePAX(...): got %v, want %v", i, ok, v.ok) + } + } +} + +func TestParsePAX(t *testing.T) { + vectors := []struct { + in string + want map[string]string + ok bool + }{ + {"", nil, true}, + {"6 k=1\n", map[string]string{"k": "1"}, true}, + {"10 a=name\n", map[string]string{"a": "name"}, true}, + {"9 a=name\n", map[string]string{"a": "name"}, true}, + {"30 mtime=1350244992.023960108\n", map[string]string{"mtime": "1350244992.023960108"}, true}, + {"3 somelongkey=\n", nil, false}, + {"50 tooshort=\n", nil, false}, + {"13 key1=haha\n13 key2=nana\n13 key3=kaka\n", + map[string]string{"key1": "haha", "key2": "nana", "key3": "kaka"}, true}, + {"13 key1=val1\n13 key2=val2\n8 key1=\n", + map[string]string{"key1": "", "key2": "val2"}, true}, + {"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=2\n" + + "23 GNU.sparse.offset=1\n25 GNU.sparse.numbytes=2\n" + + "23 GNU.sparse.offset=3\n25 GNU.sparse.numbytes=4\n", + map[string]string{paxGNUSparseSize: "10", paxGNUSparseNumBlocks: "2", paxGNUSparseMap: "1,2,3,4"}, true}, + {"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" + + "25 GNU.sparse.numbytes=2\n23 GNU.sparse.offset=1\n", + nil, false}, + {"22 GNU.sparse.size=10\n26 GNU.sparse.numblocks=1\n" + + "25 GNU.sparse.offset=1,2\n25 GNU.sparse.numbytes=2\n", + nil, false}, + } + + for i, v := range vectors { + r := strings.NewReader(v.in) + got, err := parsePAX(r) + if !reflect.DeepEqual(got, v.want) && !(len(got) == 0 && len(v.want) == 0) { + t.Errorf("test %d, parsePAX():\ngot %v\nwant %v", i, got, v.want) + } + if ok := err == nil; ok != v.ok { + t.Errorf("test %d, parsePAX(): got %v, want %v", i, ok, v.ok) + } + } +} + +func TestReadOldGNUSparseMap(t *testing.T) { + populateSparseMap := func(sa sparseArray, sps []string) []string { + for i := 0; len(sps) > 0 && i < sa.MaxEntries(); i++ { + copy(sa.Entry(i), sps[0]) + sps = sps[1:] + } + if len(sps) > 0 { + copy(sa.IsExtended(), "\x80") + } + return sps + } + + makeInput := func(format Format, size string, sps ...string) (out []byte) { + // Write the initial GNU header. + var blk block + gnu := blk.GNU() + sparse := gnu.Sparse() + copy(gnu.RealSize(), size) + sps = populateSparseMap(sparse, sps) + if format != FormatUnknown { + blk.SetFormat(format) + } + out = append(out, blk[:]...) + + // Write extended sparse blocks. + for len(sps) > 0 { + var blk block + sps = populateSparseMap(blk.Sparse(), sps) + out = append(out, blk[:]...) + } + return out + } + + makeSparseStrings := func(sp []sparseEntry) (out []string) { + var f formatter + for _, s := range sp { + var b [24]byte + f.formatNumeric(b[:12], s.Offset) + f.formatNumeric(b[12:], s.Length) + out = append(out, string(b[:])) + } + return out + } + + vectors := []struct { + input []byte + wantMap sparseDatas + wantSize int64 + wantErr error + }{{ + input: makeInput(FormatUnknown, ""), + wantErr: ErrHeader, + }, { + input: makeInput(FormatGNU, "1234", "fewa"), + wantSize: 01234, + wantErr: ErrHeader, + }, { + input: makeInput(FormatGNU, "0031"), + wantSize: 031, + }, { + input: makeInput(FormatGNU, "80"), + wantErr: ErrHeader, + }, { + input: makeInput(FormatGNU, "1234", + makeSparseStrings(sparseDatas{{0, 0}, {1, 1}})...), + wantMap: sparseDatas{{0, 0}, {1, 1}}, + wantSize: 01234, + }, { + input: makeInput(FormatGNU, "1234", + append(makeSparseStrings(sparseDatas{{0, 0}, {1, 1}}), []string{"", "blah"}...)...), + wantMap: sparseDatas{{0, 0}, {1, 1}}, + wantSize: 01234, + }, { + input: makeInput(FormatGNU, "3333", + makeSparseStrings(sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}})...), + wantMap: sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}}, + wantSize: 03333, + }, { + input: makeInput(FormatGNU, "", + append(append( + makeSparseStrings(sparseDatas{{0, 1}, {2, 1}}), + []string{"", ""}...), + makeSparseStrings(sparseDatas{{4, 1}, {6, 1}})...)...), + wantMap: sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}}, + }, { + input: makeInput(FormatGNU, "", + makeSparseStrings(sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}, {8, 1}, {10, 1}})...)[:blockSize], + wantErr: io.ErrUnexpectedEOF, + }, { + input: makeInput(FormatGNU, "", + makeSparseStrings(sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}, {8, 1}, {10, 1}})...)[:3*blockSize/2], + wantErr: io.ErrUnexpectedEOF, + }, { + input: makeInput(FormatGNU, "", + makeSparseStrings(sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}, {8, 1}, {10, 1}})...), + wantMap: sparseDatas{{0, 1}, {2, 1}, {4, 1}, {6, 1}, {8, 1}, {10, 1}}, + }, { + input: makeInput(FormatGNU, "", + makeSparseStrings(sparseDatas{{10 << 30, 512}, {20 << 30, 512}})...), + wantMap: sparseDatas{{10 << 30, 512}, {20 << 30, 512}}, + }} + + for i, v := range vectors { + var blk block + var hdr Header + v.input = v.input[copy(blk[:], v.input):] + tr := Reader{r: bytes.NewReader(v.input)} + got, err := tr.readOldGNUSparseMap(&hdr, &blk) + if !equalSparseEntries(got, v.wantMap) { + t.Errorf("test %d, readOldGNUSparseMap(): got %v, want %v", i, got, v.wantMap) + } + if err != v.wantErr { + t.Errorf("test %d, readOldGNUSparseMap() = %v, want %v", i, err, v.wantErr) + } + if hdr.Size != v.wantSize { + t.Errorf("test %d, Header.Size = %d, want %d", i, hdr.Size, v.wantSize) + } + } +} + +func TestReadGNUSparsePAXHeaders(t *testing.T) { + padInput := func(s string) string { + return s + string(zeroBlock[:blockPadding(int64(len(s)))]) + } + + vectors := []struct { + inputData string + inputHdrs map[string]string + wantMap sparseDatas + wantSize int64 + wantName string + wantErr error + }{{ + inputHdrs: nil, + wantErr: nil, + }, { + inputHdrs: map[string]string{ + paxGNUSparseNumBlocks: strconv.FormatInt(math.MaxInt64, 10), + paxGNUSparseMap: "0,1,2,3", + }, + wantErr: ErrHeader, + }, { + inputHdrs: map[string]string{ + paxGNUSparseNumBlocks: "4\x00", + paxGNUSparseMap: "0,1,2,3", + }, + wantErr: ErrHeader, + }, { + inputHdrs: map[string]string{ + paxGNUSparseNumBlocks: "4", + paxGNUSparseMap: "0,1,2,3", + }, + wantErr: ErrHeader, + }, { + inputHdrs: map[string]string{ + paxGNUSparseNumBlocks: "2", + paxGNUSparseMap: "0,1,2,3", + }, + wantMap: sparseDatas{{0, 1}, {2, 3}}, + }, { + inputHdrs: map[string]string{ + paxGNUSparseNumBlocks: "2", + paxGNUSparseMap: "0, 1,2,3", + }, + wantErr: ErrHeader, + }, { + inputHdrs: map[string]string{ + paxGNUSparseNumBlocks: "2", + paxGNUSparseMap: "0,1,02,3", + paxGNUSparseRealSize: "4321", + }, + wantMap: sparseDatas{{0, 1}, {2, 3}}, + wantSize: 4321, + }, { + inputHdrs: map[string]string{ + paxGNUSparseNumBlocks: "2", + paxGNUSparseMap: "0,one1,2,3", + }, + wantErr: ErrHeader, + }, { + inputHdrs: map[string]string{ + paxGNUSparseMajor: "0", + paxGNUSparseMinor: "0", + paxGNUSparseNumBlocks: "2", + paxGNUSparseMap: "0,1,2,3", + paxGNUSparseSize: "1234", + paxGNUSparseRealSize: "4321", + paxGNUSparseName: "realname", + }, + wantMap: sparseDatas{{0, 1}, {2, 3}}, + wantSize: 1234, + wantName: "realname", + }, { + inputHdrs: map[string]string{ + paxGNUSparseMajor: "0", + paxGNUSparseMinor: "0", + paxGNUSparseNumBlocks: "1", + paxGNUSparseMap: "10737418240,512", + paxGNUSparseSize: "10737418240", + paxGNUSparseName: "realname", + }, + wantMap: sparseDatas{{10737418240, 512}}, + wantSize: 10737418240, + wantName: "realname", + }, { + inputHdrs: map[string]string{ + paxGNUSparseMajor: "0", + paxGNUSparseMinor: "0", + paxGNUSparseNumBlocks: "0", + paxGNUSparseMap: "", + }, + wantMap: sparseDatas{}, + }, { + inputHdrs: map[string]string{ + paxGNUSparseMajor: "0", + paxGNUSparseMinor: "1", + paxGNUSparseNumBlocks: "4", + paxGNUSparseMap: "0,5,10,5,20,5,30,5", + }, + wantMap: sparseDatas{{0, 5}, {10, 5}, {20, 5}, {30, 5}}, + }, { + inputHdrs: map[string]string{ + paxGNUSparseMajor: "1", + paxGNUSparseMinor: "0", + paxGNUSparseNumBlocks: "4", + paxGNUSparseMap: "0,5,10,5,20,5,30,5", + }, + wantErr: io.ErrUnexpectedEOF, + }, { + inputData: padInput("0\n"), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantMap: sparseDatas{}, + }, { + inputData: padInput("0\n")[:blockSize-1] + "#", + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantMap: sparseDatas{}, + }, { + inputData: padInput("0"), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantErr: io.ErrUnexpectedEOF, + }, { + inputData: padInput("ab\n"), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantErr: ErrHeader, + }, { + inputData: padInput("1\n2\n3\n"), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantMap: sparseDatas{{2, 3}}, + }, { + inputData: padInput("1\n2\n"), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantErr: io.ErrUnexpectedEOF, + }, { + inputData: padInput("1\n2\n\n"), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantErr: ErrHeader, + }, { + inputData: string(zeroBlock[:]) + padInput("0\n"), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantErr: ErrHeader, + }, { + inputData: strings.Repeat("0", blockSize) + padInput("1\n5\n1\n"), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantMap: sparseDatas{{5, 1}}, + }, { + inputData: padInput(fmt.Sprintf("%d\n", int64(math.MaxInt64))), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantErr: ErrHeader, + }, { + inputData: padInput(strings.Repeat("0", 300) + "1\n" + strings.Repeat("0", 1000) + "5\n" + strings.Repeat("0", 800) + "2\n"), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantMap: sparseDatas{{5, 2}}, + }, { + inputData: padInput("2\n10737418240\n512\n21474836480\n512\n"), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantMap: sparseDatas{{10737418240, 512}, {21474836480, 512}}, + }, { + inputData: padInput("100\n" + func() string { + var ss []string + for i := 0; i < 100; i++ { + ss = append(ss, fmt.Sprintf("%d\n%d\n", int64(i)<<30, 512)) + } + return strings.Join(ss, "") + }()), + inputHdrs: map[string]string{paxGNUSparseMajor: "1", paxGNUSparseMinor: "0"}, + wantMap: func() (spd sparseDatas) { + for i := 0; i < 100; i++ { + spd = append(spd, sparseEntry{int64(i) << 30, 512}) + } + return spd + }(), + }} + + for i, v := range vectors { + var hdr Header + hdr.PAXRecords = v.inputHdrs + r := strings.NewReader(v.inputData + "#") // Add canary byte + tr := Reader{curr: ®FileReader{r, int64(r.Len())}} + got, err := tr.readGNUSparsePAXHeaders(&hdr) + if !equalSparseEntries(got, v.wantMap) { + t.Errorf("test %d, readGNUSparsePAXHeaders(): got %v, want %v", i, got, v.wantMap) + } + if err != v.wantErr { + t.Errorf("test %d, readGNUSparsePAXHeaders() = %v, want %v", i, err, v.wantErr) + } + if hdr.Size != v.wantSize { + t.Errorf("test %d, Header.Size = %d, want %d", i, hdr.Size, v.wantSize) + } + if hdr.Name != v.wantName { + t.Errorf("test %d, Header.Name = %s, want %s", i, hdr.Name, v.wantName) + } + if v.wantErr == nil && r.Len() == 0 { + t.Errorf("test %d, canary byte unexpectedly consumed", i) + } + } +} + +// testNonEmptyReader wraps an io.Reader and ensures that +// Read is never called with an empty buffer. +type testNonEmptyReader struct{ io.Reader } + +func (r testNonEmptyReader) Read(b []byte) (int, error) { + if len(b) == 0 { + return 0, errors.New("unexpected empty Read call") + } + return r.Reader.Read(b) +} + +func TestFileReader(t *testing.T) { + type ( + testRead struct { // Read(cnt) == (wantStr, wantErr) + cnt int + wantStr string + wantErr error + } + testWriteTo struct { // WriteTo(testFile{ops}) == (wantCnt, wantErr) + ops fileOps + wantCnt int64 + wantErr error + } + testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt + wantLCnt int64 + wantPCnt int64 + } + testFnc interface{} // testRead | testWriteTo | testRemaining + ) + + type ( + makeReg struct { + str string + size int64 + } + makeSparse struct { + makeReg makeReg + spd sparseDatas + size int64 + } + fileMaker interface{} // makeReg | makeSparse + ) + + vectors := []struct { + maker fileMaker + tests []testFnc + }{{ + maker: makeReg{"", 0}, + tests: []testFnc{ + testRemaining{0, 0}, + testRead{0, "", io.EOF}, + testRead{1, "", io.EOF}, + testWriteTo{nil, 0, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeReg{"", 1}, + tests: []testFnc{ + testRemaining{1, 1}, + testRead{5, "", io.ErrUnexpectedEOF}, + testWriteTo{nil, 0, io.ErrUnexpectedEOF}, + testRemaining{1, 1}, + }, + }, { + maker: makeReg{"hello", 5}, + tests: []testFnc{ + testRemaining{5, 5}, + testRead{5, "hello", io.EOF}, + testRemaining{0, 0}, + }, + }, { + maker: makeReg{"hello, world", 50}, + tests: []testFnc{ + testRemaining{50, 50}, + testRead{7, "hello, ", nil}, + testRemaining{43, 43}, + testRead{5, "world", nil}, + testRemaining{38, 38}, + testWriteTo{nil, 0, io.ErrUnexpectedEOF}, + testRead{1, "", io.ErrUnexpectedEOF}, + testRemaining{38, 38}, + }, + }, { + maker: makeReg{"hello, world", 5}, + tests: []testFnc{ + testRemaining{5, 5}, + testRead{0, "", nil}, + testRead{4, "hell", nil}, + testRemaining{1, 1}, + testWriteTo{fileOps{"o"}, 1, nil}, + testRemaining{0, 0}, + testWriteTo{nil, 0, nil}, + testRead{0, "", io.EOF}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{0, 2}, {5, 3}}, 8}, + tests: []testFnc{ + testRemaining{8, 5}, + testRead{3, "ab\x00", nil}, + testRead{10, "\x00\x00cde", io.EOF}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{0, 2}, {5, 3}}, 8}, + tests: []testFnc{ + testRemaining{8, 5}, + testWriteTo{fileOps{"ab", int64(3), "cde"}, 8, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{0, 2}, {5, 3}}, 10}, + tests: []testFnc{ + testRemaining{10, 5}, + testRead{100, "ab\x00\x00\x00cde\x00\x00", io.EOF}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{"abc", 5}, sparseDatas{{0, 2}, {5, 3}}, 10}, + tests: []testFnc{ + testRemaining{10, 5}, + testRead{100, "ab\x00\x00\x00c", io.ErrUnexpectedEOF}, + testRemaining{4, 2}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 2}}, 8}, + tests: []testFnc{ + testRemaining{8, 5}, + testRead{8, "\x00abc\x00\x00de", io.EOF}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 0}, {6, 0}, {6, 2}}, 8}, + tests: []testFnc{ + testRemaining{8, 5}, + testRead{8, "\x00abc\x00\x00de", io.EOF}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 0}, {6, 0}, {6, 2}}, 8}, + tests: []testFnc{ + testRemaining{8, 5}, + testWriteTo{fileOps{int64(1), "abc", int64(2), "de"}, 8, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 2}}, 10}, + tests: []testFnc{ + testRead{100, "\x00abc\x00\x00de\x00\x00", io.EOF}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 2}}, 10}, + tests: []testFnc{ + testWriteTo{fileOps{int64(1), "abc", int64(2), "de", int64(1), "\x00"}, 10, nil}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 2}, {8, 0}, {8, 0}, {8, 0}, {8, 0}}, 10}, + tests: []testFnc{ + testRead{100, "\x00abc\x00\x00de\x00\x00", io.EOF}, + }, + }, { + maker: makeSparse{makeReg{"", 0}, sparseDatas{}, 2}, + tests: []testFnc{ + testRead{100, "\x00\x00", io.EOF}, + }, + }, { + maker: makeSparse{makeReg{"", 8}, sparseDatas{{1, 3}, {6, 5}}, 15}, + tests: []testFnc{ + testRead{100, "\x00", io.ErrUnexpectedEOF}, + }, + }, { + maker: makeSparse{makeReg{"ab", 2}, sparseDatas{{1, 3}, {6, 5}}, 15}, + tests: []testFnc{ + testRead{100, "\x00ab", errMissData}, + }, + }, { + maker: makeSparse{makeReg{"ab", 8}, sparseDatas{{1, 3}, {6, 5}}, 15}, + tests: []testFnc{ + testRead{100, "\x00ab", io.ErrUnexpectedEOF}, + }, + }, { + maker: makeSparse{makeReg{"abc", 3}, sparseDatas{{1, 3}, {6, 5}}, 15}, + tests: []testFnc{ + testRead{100, "\x00abc\x00\x00", errMissData}, + }, + }, { + maker: makeSparse{makeReg{"abc", 8}, sparseDatas{{1, 3}, {6, 5}}, 15}, + tests: []testFnc{ + testRead{100, "\x00abc\x00\x00", io.ErrUnexpectedEOF}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 5}}, 15}, + tests: []testFnc{ + testRead{100, "\x00abc\x00\x00de", errMissData}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 5}, sparseDatas{{1, 3}, {6, 5}}, 15}, + tests: []testFnc{ + testWriteTo{fileOps{int64(1), "abc", int64(2), "de"}, 8, errMissData}, + }, + }, { + maker: makeSparse{makeReg{"abcde", 8}, sparseDatas{{1, 3}, {6, 5}}, 15}, + tests: []testFnc{ + testRead{100, "\x00abc\x00\x00de", io.ErrUnexpectedEOF}, + }, + }, { + maker: makeSparse{makeReg{"abcdefghEXTRA", 13}, sparseDatas{{1, 3}, {6, 5}}, 15}, + tests: []testFnc{ + testRemaining{15, 13}, + testRead{100, "\x00abc\x00\x00defgh\x00\x00\x00\x00", errUnrefData}, + testWriteTo{nil, 0, errUnrefData}, + testRemaining{0, 5}, + }, + }, { + maker: makeSparse{makeReg{"abcdefghEXTRA", 13}, sparseDatas{{1, 3}, {6, 5}}, 15}, + tests: []testFnc{ + testRemaining{15, 13}, + testWriteTo{fileOps{int64(1), "abc", int64(2), "defgh", int64(4)}, 15, errUnrefData}, + testRead{100, "", errUnrefData}, + testRemaining{0, 5}, + }, + }} + + for i, v := range vectors { + var fr fileReader + switch maker := v.maker.(type) { + case makeReg: + r := testNonEmptyReader{strings.NewReader(maker.str)} + fr = ®FileReader{r, maker.size} + case makeSparse: + if !validateSparseEntries(maker.spd, maker.size) { + t.Fatalf("invalid sparse map: %v", maker.spd) + } + sph := invertSparseEntries(maker.spd, maker.size) + r := testNonEmptyReader{strings.NewReader(maker.makeReg.str)} + fr = ®FileReader{r, maker.makeReg.size} + fr = &sparseFileReader{fr, sph, 0} + default: + t.Fatalf("test %d, unknown make operation: %T", i, maker) + } + + for j, tf := range v.tests { + switch tf := tf.(type) { + case testRead: + b := make([]byte, tf.cnt) + n, err := fr.Read(b) + if got := string(b[:n]); got != tf.wantStr || err != tf.wantErr { + t.Errorf("test %d.%d, Read(%d):\ngot (%q, %v)\nwant (%q, %v)", i, j, tf.cnt, got, err, tf.wantStr, tf.wantErr) + } + case testWriteTo: + f := &testFile{ops: tf.ops} + got, err := fr.WriteTo(f) + if _, ok := err.(testError); ok { + t.Errorf("test %d.%d, WriteTo(): %v", i, j, err) + } else if got != tf.wantCnt || err != tf.wantErr { + t.Errorf("test %d.%d, WriteTo() = (%d, %v), want (%d, %v)", i, j, got, err, tf.wantCnt, tf.wantErr) + } + if len(f.ops) > 0 { + t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops)) + } + case testRemaining: + if got := fr.LogicalRemaining(); got != tf.wantLCnt { + t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) + } + if got := fr.PhysicalRemaining(); got != tf.wantPCnt { + t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) + } + default: + t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf) + } + } + } +} diff --git a/vendor/archive/tar/stat_actime1.go b/vendor/archive/tar/stat_actime1.go new file mode 100644 index 0000000000000..4fdf2a04b3df4 --- /dev/null +++ b/vendor/archive/tar/stat_actime1.go @@ -0,0 +1,21 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || linux || dragonfly || openbsd || solaris +// +build aix linux dragonfly openbsd solaris + +package tar + +import ( + "syscall" + "time" +) + +func statAtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Atim.Unix()) +} + +func statCtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Ctim.Unix()) +} diff --git a/vendor/archive/tar/stat_actime2.go b/vendor/archive/tar/stat_actime2.go new file mode 100644 index 0000000000000..5a9a35cbb4e22 --- /dev/null +++ b/vendor/archive/tar/stat_actime2.go @@ -0,0 +1,21 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin || freebsd || netbsd +// +build darwin freebsd netbsd + +package tar + +import ( + "syscall" + "time" +) + +func statAtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Atimespec.Unix()) +} + +func statCtime(st *syscall.Stat_t) time.Time { + return time.Unix(st.Ctimespec.Unix()) +} diff --git a/vendor/archive/tar/stat_unix.go b/vendor/archive/tar/stat_unix.go new file mode 100644 index 0000000000000..4a5bca03121c2 --- /dev/null +++ b/vendor/archive/tar/stat_unix.go @@ -0,0 +1,82 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build aix || linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris +// +build aix linux darwin dragonfly freebsd openbsd netbsd solaris + +package tar + +import ( + "io/fs" + "runtime" + "syscall" +) + +func init() { + sysStat = statUnix +} + +func statUnix(fi fs.FileInfo, h *Header) error { + sys, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return nil + } + h.Uid = int(sys.Uid) + h.Gid = int(sys.Gid) + + // TODO(bradfitz): populate username & group. os/user + // doesn't cache LookupId lookups, and lacks group + // lookup functions. + h.AccessTime = statAtime(sys) + h.ChangeTime = statCtime(sys) + + // Best effort at populating Devmajor and Devminor. + if h.Typeflag == TypeChar || h.Typeflag == TypeBlock { + dev := uint64(sys.Rdev) // May be int32 or uint32 + switch runtime.GOOS { + case "aix": + var major, minor uint32 + major = uint32((dev & 0x3fffffff00000000) >> 32) + minor = uint32((dev & 0x00000000ffffffff) >> 0) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "linux": + // Copied from golang.org/x/sys/unix/dev_linux.go. + major := uint32((dev & 0x00000000000fff00) >> 8) + major |= uint32((dev & 0xfffff00000000000) >> 32) + minor := uint32((dev & 0x00000000000000ff) >> 0) + minor |= uint32((dev & 0x00000ffffff00000) >> 12) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "darwin", "ios": + // Copied from golang.org/x/sys/unix/dev_darwin.go. + major := uint32((dev >> 24) & 0xff) + minor := uint32(dev & 0xffffff) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "dragonfly": + // Copied from golang.org/x/sys/unix/dev_dragonfly.go. + major := uint32((dev >> 8) & 0xff) + minor := uint32(dev & 0xffff00ff) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "freebsd": + // Copied from golang.org/x/sys/unix/dev_freebsd.go. + major := uint32((dev >> 8) & 0xff) + minor := uint32(dev & 0xffff00ff) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "netbsd": + // Copied from golang.org/x/sys/unix/dev_netbsd.go. + major := uint32((dev & 0x000fff00) >> 8) + minor := uint32((dev & 0x000000ff) >> 0) + minor |= uint32((dev & 0xfff00000) >> 12) + h.Devmajor, h.Devminor = int64(major), int64(minor) + case "openbsd": + // Copied from golang.org/x/sys/unix/dev_openbsd.go. + major := uint32((dev & 0x0000ff00) >> 8) + minor := uint32((dev & 0x000000ff) >> 0) + minor |= uint32((dev & 0xffff0000) >> 8) + h.Devmajor, h.Devminor = int64(major), int64(minor) + default: + // TODO: Implement solaris (see https://golang.org/issue/8106) + } + } + return nil +} diff --git a/vendor/archive/tar/strconv.go b/vendor/archive/tar/strconv.go new file mode 100644 index 0000000000000..f0b61e6dba69a --- /dev/null +++ b/vendor/archive/tar/strconv.go @@ -0,0 +1,345 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "time" +) + +// hasNUL reports whether the NUL character exists within s. +func hasNUL(s string) bool { + return strings.IndexByte(s, 0) >= 0 +} + +// isASCII reports whether the input is an ASCII C-style string. +func isASCII(s string) bool { + for _, c := range s { + if c >= 0x80 || c == 0x00 { + return false + } + } + return true +} + +// toASCII converts the input to an ASCII C-style string. +// This is a best effort conversion, so invalid characters are dropped. +func toASCII(s string) string { + if isASCII(s) { + return s + } + b := make([]byte, 0, len(s)) + for _, c := range s { + if c < 0x80 && c != 0x00 { + b = append(b, byte(c)) + } + } + return string(b) +} + +type parser struct { + err error // Last error seen +} + +type formatter struct { + err error // Last error seen +} + +// parseString parses bytes as a NUL-terminated C-style string. +// If a NUL byte is not found then the whole slice is returned as a string. +func (*parser) parseString(b []byte) string { + if i := bytes.IndexByte(b, 0); i >= 0 { + return string(b[:i]) + } + return string(b) +} + +// formatString copies s into b, NUL-terminating if possible. +func (f *formatter) formatString(b []byte, s string) { + if len(s) > len(b) { + f.err = ErrFieldTooLong + } + copy(b, s) + if len(s) < len(b) { + b[len(s)] = 0 + } + + // Some buggy readers treat regular files with a trailing slash + // in the V7 path field as a directory even though the full path + // recorded elsewhere (e.g., via PAX record) contains no trailing slash. + if len(s) > len(b) && b[len(b)-1] == '/' { + n := len(strings.TrimRight(s[:len(b)], "/")) + b[n] = 0 // Replace trailing slash with NUL terminator + } +} + +// fitsInBase256 reports whether x can be encoded into n bytes using base-256 +// encoding. Unlike octal encoding, base-256 encoding does not require that the +// string ends with a NUL character. Thus, all n bytes are available for output. +// +// If operating in binary mode, this assumes strict GNU binary mode; which means +// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is +// equivalent to the sign bit in two's complement form. +func fitsInBase256(n int, x int64) bool { + binBits := uint(n-1) * 8 + return n >= 9 || (x >= -1< 0 && b[0]&0x80 != 0 { + // Handling negative numbers relies on the following identity: + // -a-1 == ^a + // + // If the number is negative, we use an inversion mask to invert the + // data bytes and treat the value as an unsigned number. + var inv byte // 0x00 if positive or zero, 0xff if negative + if b[0]&0x40 != 0 { + inv = 0xff + } + + var x uint64 + for i, c := range b { + c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing + if i == 0 { + c &= 0x7f // Ignore signal bit in first byte + } + if (x >> 56) > 0 { + p.err = ErrHeader // Integer overflow + return 0 + } + x = x<<8 | uint64(c) + } + if (x >> 63) > 0 { + p.err = ErrHeader // Integer overflow + return 0 + } + if inv == 0xff { + return ^int64(x) + } + return int64(x) + } + + // Normal case is base-8 (octal) format. + return p.parseOctal(b) +} + +// formatNumeric encodes x into b using base-8 (octal) encoding if possible. +// Otherwise it will attempt to use base-256 (binary) encoding. +func (f *formatter) formatNumeric(b []byte, x int64) { + if fitsInOctal(len(b), x) { + f.formatOctal(b, x) + return + } + + if fitsInBase256(len(b), x) { + for i := len(b) - 1; i >= 0; i-- { + b[i] = byte(x) + x >>= 8 + } + b[0] |= 0x80 // Highest bit indicates binary format + return + } + + f.formatOctal(b, 0) // Last resort, just write zero + f.err = ErrFieldTooLong +} + +func (p *parser) parseOctal(b []byte) int64 { + // Because unused fields are filled with NULs, we need + // to skip leading NULs. Fields may also be padded with + // spaces or NULs. + // So we remove leading and trailing NULs and spaces to + // be sure. + b = bytes.Trim(b, " \x00") + + if len(b) == 0 { + return 0 + } + x, perr := strconv.ParseUint(p.parseString(b), 8, 64) + if perr != nil { + p.err = ErrHeader + } + return int64(x) +} + +func (f *formatter) formatOctal(b []byte, x int64) { + if !fitsInOctal(len(b), x) { + x = 0 // Last resort, just write zero + f.err = ErrFieldTooLong + } + + s := strconv.FormatInt(x, 8) + // Add leading zeros, but leave room for a NUL. + if n := len(b) - len(s) - 1; n > 0 { + s = strings.Repeat("0", n) + s + } + f.formatString(b, s) +} + +// fitsInOctal reports whether the integer x fits in a field n-bytes long +// using octal encoding with the appropriate NUL terminator. +func fitsInOctal(n int, x int64) bool { + octBits := uint(n-1) * 3 + return x >= 0 && (n >= 22 || x < 1<= 0 { + ss, sn = s[:pos], s[pos+1:] + } + + // Parse the seconds. + secs, err := strconv.ParseInt(ss, 10, 64) + if err != nil { + return time.Time{}, ErrHeader + } + if len(sn) == 0 { + return time.Unix(secs, 0), nil // No sub-second values + } + + // Parse the nanoseconds. + if strings.Trim(sn, "0123456789") != "" { + return time.Time{}, ErrHeader + } + if len(sn) < maxNanoSecondDigits { + sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad + } else { + sn = sn[:maxNanoSecondDigits] // Right truncate + } + nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed + if len(ss) > 0 && ss[0] == '-' { + return time.Unix(secs, -1*nsecs), nil // Negative correction + } + return time.Unix(secs, nsecs), nil +} + +// formatPAXTime converts ts into a time of the form %d.%d as described in the +// PAX specification. This function is capable of negative timestamps. +func formatPAXTime(ts time.Time) (s string) { + secs, nsecs := ts.Unix(), ts.Nanosecond() + if nsecs == 0 { + return strconv.FormatInt(secs, 10) + } + + // If seconds is negative, then perform correction. + sign := "" + if secs < 0 { + sign = "-" // Remember sign + secs = -(secs + 1) // Add a second to secs + nsecs = -(nsecs - 1e9) // Take that second away from nsecs + } + return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0") +} + +// parsePAXRecord parses the input PAX record string into a key-value pair. +// If parsing is successful, it will slice off the currently read record and +// return the remainder as r. +func parsePAXRecord(s string) (k, v, r string, err error) { + // The size field ends at the first space. + sp := strings.IndexByte(s, ' ') + if sp == -1 { + return "", "", s, ErrHeader + } + + // Parse the first token as a decimal integer. + n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int + if perr != nil || n < 5 || int64(len(s)) < n { + return "", "", s, ErrHeader + } + + afterSpace := int64(sp + 1) + beforeLastNewLine := n - 1 + // In some cases, "length" was perhaps padded/malformed, and + // trying to index past where the space supposedly is goes past + // the end of the actual record. + // For example: + // "0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319" + // ^ ^ + // | | + // | afterSpace=35 + // | + // beforeLastNewLine=29 + // yet indexOf(firstSpace) MUST BE before endOfRecord. + // + // See https://golang.org/issues/40196. + if afterSpace >= beforeLastNewLine { + return "", "", s, ErrHeader + } + + // Extract everything between the space and the final newline. + rec, nl, rem := s[afterSpace:beforeLastNewLine], s[beforeLastNewLine:n], s[n:] + if nl != "\n" { + return "", "", s, ErrHeader + } + + // The first equals separates the key from the value. + eq := strings.IndexByte(rec, '=') + if eq == -1 { + return "", "", s, ErrHeader + } + k, v = rec[:eq], rec[eq+1:] + + if !validPAXRecord(k, v) { + return "", "", s, ErrHeader + } + return k, v, rem, nil +} + +// formatPAXRecord formats a single PAX record, prefixing it with the +// appropriate length. +func formatPAXRecord(k, v string) (string, error) { + if !validPAXRecord(k, v) { + return "", ErrHeader + } + + const padding = 3 // Extra padding for ' ', '=', and '\n' + size := len(k) + len(v) + padding + size += len(strconv.Itoa(size)) + record := strconv.Itoa(size) + " " + k + "=" + v + "\n" + + // Final adjustment if adding size field increased the record size. + if len(record) != size { + size = len(record) + record = strconv.Itoa(size) + " " + k + "=" + v + "\n" + } + return record, nil +} + +// validPAXRecord reports whether the key-value pair is valid where each +// record is formatted as: +// "%d %s=%s\n" % (size, key, value) +// +// Keys and values should be UTF-8, but the number of bad writers out there +// forces us to be a more liberal. +// Thus, we only reject all keys with NUL, and only reject NULs in values +// for the PAX version of the USTAR string fields. +// The key must not contain an '=' character. +func validPAXRecord(k, v string) bool { + if k == "" || strings.IndexByte(k, '=') >= 0 { + return false + } + switch k { + case paxPath, paxLinkpath, paxUname, paxGname: + return !hasNUL(v) + default: + return !hasNUL(k) + } +} diff --git a/vendor/archive/tar/strconv_test.go b/vendor/archive/tar/strconv_test.go new file mode 100644 index 0000000000000..add65e272ae6d --- /dev/null +++ b/vendor/archive/tar/strconv_test.go @@ -0,0 +1,441 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "math" + "strings" + "testing" + "time" +) + +func TestFitsInBase256(t *testing.T) { + vectors := []struct { + in int64 + width int + ok bool + }{ + {+1, 8, true}, + {0, 8, true}, + {-1, 8, true}, + {1 << 56, 8, false}, + {(1 << 56) - 1, 8, true}, + {-1 << 56, 8, true}, + {(-1 << 56) - 1, 8, false}, + {121654, 8, true}, + {-9849849, 8, true}, + {math.MaxInt64, 9, true}, + {0, 9, true}, + {math.MinInt64, 9, true}, + {math.MaxInt64, 12, true}, + {0, 12, true}, + {math.MinInt64, 12, true}, + } + + for _, v := range vectors { + ok := fitsInBase256(v.width, v.in) + if ok != v.ok { + t.Errorf("fitsInBase256(%d, %d): got %v, want %v", v.in, v.width, ok, v.ok) + } + } +} + +func TestParseNumeric(t *testing.T) { + vectors := []struct { + in string + want int64 + ok bool + }{ + // Test base-256 (binary) encoded values. + {"", 0, true}, + {"\x80", 0, true}, + {"\x80\x00", 0, true}, + {"\x80\x00\x00", 0, true}, + {"\xbf", (1 << 6) - 1, true}, + {"\xbf\xff", (1 << 14) - 1, true}, + {"\xbf\xff\xff", (1 << 22) - 1, true}, + {"\xff", -1, true}, + {"\xff\xff", -1, true}, + {"\xff\xff\xff", -1, true}, + {"\xc0", -1 * (1 << 6), true}, + {"\xc0\x00", -1 * (1 << 14), true}, + {"\xc0\x00\x00", -1 * (1 << 22), true}, + {"\x87\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true}, + {"\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", 537795476381659745, true}, + {"\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true}, + {"\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", -615126028225187231, true}, + {"\x80\x7f\xff\xff\xff\xff\xff\xff\xff", math.MaxInt64, true}, + {"\x80\x80\x00\x00\x00\x00\x00\x00\x00", 0, false}, + {"\xff\x80\x00\x00\x00\x00\x00\x00\x00", math.MinInt64, true}, + {"\xff\x7f\xff\xff\xff\xff\xff\xff\xff", 0, false}, + {"\xf5\xec\xd1\xc7\x7e\x5f\x26\x48\x81\x9f\x8f\x9b", 0, false}, + + // Test base-8 (octal) encoded values. + {"0000000\x00", 0, true}, + {" \x0000000\x00", 0, true}, + {" \x0000003\x00", 3, true}, + {"00000000227\x00", 0227, true}, + {"032033\x00 ", 032033, true}, + {"320330\x00 ", 0320330, true}, + {"0000660\x00 ", 0660, true}, + {"\x00 0000660\x00 ", 0660, true}, + {"0123456789abcdef", 0, false}, + {"0123456789\x00abcdef", 0, false}, + {"01234567\x0089abcdef", 342391, true}, + {"0123\x7e\x5f\x264123", 0, false}, + } + + for _, v := range vectors { + var p parser + got := p.parseNumeric([]byte(v.in)) + ok := (p.err == nil) + if ok != v.ok { + if v.ok { + t.Errorf("parseNumeric(%q): got parsing failure, want success", v.in) + } else { + t.Errorf("parseNumeric(%q): got parsing success, want failure", v.in) + } + } + if ok && got != v.want { + t.Errorf("parseNumeric(%q): got %d, want %d", v.in, got, v.want) + } + } +} + +func TestFormatNumeric(t *testing.T) { + vectors := []struct { + in int64 + want string + ok bool + }{ + // Test base-8 (octal) encoded values. + {0, "0\x00", true}, + {7, "7\x00", true}, + {8, "\x80\x08", true}, + {077, "77\x00", true}, + {0100, "\x80\x00\x40", true}, + {0, "0000000\x00", true}, + {0123, "0000123\x00", true}, + {07654321, "7654321\x00", true}, + {07777777, "7777777\x00", true}, + {010000000, "\x80\x00\x00\x00\x00\x20\x00\x00", true}, + {0, "00000000000\x00", true}, + {000001234567, "00001234567\x00", true}, + {076543210321, "76543210321\x00", true}, + {012345670123, "12345670123\x00", true}, + {077777777777, "77777777777\x00", true}, + {0100000000000, "\x80\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00", true}, + {math.MaxInt64, "777777777777777777777\x00", true}, + + // Test base-256 (binary) encoded values. + {-1, "\xff", true}, + {-1, "\xff\xff", true}, + {-1, "\xff\xff\xff", true}, + {(1 << 0), "0", false}, + {(1 << 8) - 1, "\x80\xff", true}, + {(1 << 8), "0\x00", false}, + {(1 << 16) - 1, "\x80\xff\xff", true}, + {(1 << 16), "00\x00", false}, + {-1 * (1 << 0), "\xff", true}, + {-1*(1<<0) - 1, "0", false}, + {-1 * (1 << 8), "\xff\x00", true}, + {-1*(1<<8) - 1, "0\x00", false}, + {-1 * (1 << 16), "\xff\x00\x00", true}, + {-1*(1<<16) - 1, "00\x00", false}, + {537795476381659745, "0000000\x00", false}, + {537795476381659745, "\x80\x00\x00\x00\x07\x76\xa2\x22\xeb\x8a\x72\x61", true}, + {-615126028225187231, "0000000\x00", false}, + {-615126028225187231, "\xff\xff\xff\xff\xf7\x76\xa2\x22\xeb\x8a\x72\x61", true}, + {math.MaxInt64, "0000000\x00", false}, + {math.MaxInt64, "\x80\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff", true}, + {math.MinInt64, "0000000\x00", false}, + {math.MinInt64, "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00", true}, + {math.MaxInt64, "\x80\x7f\xff\xff\xff\xff\xff\xff\xff", true}, + {math.MinInt64, "\xff\x80\x00\x00\x00\x00\x00\x00\x00", true}, + } + + for _, v := range vectors { + var f formatter + got := make([]byte, len(v.want)) + f.formatNumeric(got, v.in) + ok := (f.err == nil) + if ok != v.ok { + if v.ok { + t.Errorf("formatNumeric(%d): got formatting failure, want success", v.in) + } else { + t.Errorf("formatNumeric(%d): got formatting success, want failure", v.in) + } + } + if string(got) != v.want { + t.Errorf("formatNumeric(%d): got %q, want %q", v.in, got, v.want) + } + } +} + +func TestFitsInOctal(t *testing.T) { + vectors := []struct { + input int64 + width int + ok bool + }{ + {-1, 1, false}, + {-1, 2, false}, + {-1, 3, false}, + {0, 1, true}, + {0 + 1, 1, false}, + {0, 2, true}, + {07, 2, true}, + {07 + 1, 2, false}, + {0, 4, true}, + {0777, 4, true}, + {0777 + 1, 4, false}, + {0, 8, true}, + {07777777, 8, true}, + {07777777 + 1, 8, false}, + {0, 12, true}, + {077777777777, 12, true}, + {077777777777 + 1, 12, false}, + {math.MaxInt64, 22, true}, + {012345670123, 12, true}, + {01564164, 12, true}, + {-012345670123, 12, false}, + {-01564164, 12, false}, + {-1564164, 30, false}, + } + + for _, v := range vectors { + ok := fitsInOctal(v.width, v.input) + if ok != v.ok { + t.Errorf("checkOctal(%d, %d): got %v, want %v", v.input, v.width, ok, v.ok) + } + } +} + +func TestParsePAXTime(t *testing.T) { + vectors := []struct { + in string + want time.Time + ok bool + }{ + {"1350244992.023960108", time.Unix(1350244992, 23960108), true}, + {"1350244992.02396010", time.Unix(1350244992, 23960100), true}, + {"1350244992.0239601089", time.Unix(1350244992, 23960108), true}, + {"1350244992.3", time.Unix(1350244992, 300000000), true}, + {"1350244992", time.Unix(1350244992, 0), true}, + {"-1.000000001", time.Unix(-1, -1e0+0e0), true}, + {"-1.000001", time.Unix(-1, -1e3+0e0), true}, + {"-1.001000", time.Unix(-1, -1e6+0e0), true}, + {"-1", time.Unix(-1, -0e0+0e0), true}, + {"-1.999000", time.Unix(-1, -1e9+1e6), true}, + {"-1.999999", time.Unix(-1, -1e9+1e3), true}, + {"-1.999999999", time.Unix(-1, -1e9+1e0), true}, + {"0.000000001", time.Unix(0, 1e0+0e0), true}, + {"0.000001", time.Unix(0, 1e3+0e0), true}, + {"0.001000", time.Unix(0, 1e6+0e0), true}, + {"0", time.Unix(0, 0e0), true}, + {"0.999000", time.Unix(0, 1e9-1e6), true}, + {"0.999999", time.Unix(0, 1e9-1e3), true}, + {"0.999999999", time.Unix(0, 1e9-1e0), true}, + {"1.000000001", time.Unix(+1, +1e0-0e0), true}, + {"1.000001", time.Unix(+1, +1e3-0e0), true}, + {"1.001000", time.Unix(+1, +1e6-0e0), true}, + {"1", time.Unix(+1, +0e0-0e0), true}, + {"1.999000", time.Unix(+1, +1e9-1e6), true}, + {"1.999999", time.Unix(+1, +1e9-1e3), true}, + {"1.999999999", time.Unix(+1, +1e9-1e0), true}, + {"-1350244992.023960108", time.Unix(-1350244992, -23960108), true}, + {"-1350244992.02396010", time.Unix(-1350244992, -23960100), true}, + {"-1350244992.0239601089", time.Unix(-1350244992, -23960108), true}, + {"-1350244992.3", time.Unix(-1350244992, -300000000), true}, + {"-1350244992", time.Unix(-1350244992, 0), true}, + {"", time.Time{}, false}, + {"0", time.Unix(0, 0), true}, + {"1.", time.Unix(1, 0), true}, + {"0.0", time.Unix(0, 0), true}, + {".5", time.Time{}, false}, + {"-1.3", time.Unix(-1, -3e8), true}, + {"-1.0", time.Unix(-1, -0e0), true}, + {"-0.0", time.Unix(-0, -0e0), true}, + {"-0.1", time.Unix(-0, -1e8), true}, + {"-0.01", time.Unix(-0, -1e7), true}, + {"-0.99", time.Unix(-0, -99e7), true}, + {"-0.98", time.Unix(-0, -98e7), true}, + {"-1.1", time.Unix(-1, -1e8), true}, + {"-1.01", time.Unix(-1, -1e7), true}, + {"-2.99", time.Unix(-2, -99e7), true}, + {"-5.98", time.Unix(-5, -98e7), true}, + {"-", time.Time{}, false}, + {"+", time.Time{}, false}, + {"-1.-1", time.Time{}, false}, + {"99999999999999999999999999999999999999999999999", time.Time{}, false}, + {"0.123456789abcdef", time.Time{}, false}, + {"foo", time.Time{}, false}, + {"\x00", time.Time{}, false}, + {"𝟵𝟴𝟳𝟲𝟱.𝟰𝟯𝟮𝟭𝟬", time.Time{}, false}, // Unicode numbers (U+1D7EC to U+1D7F5) + {"98765﹒43210", time.Time{}, false}, // Unicode period (U+FE52) + } + + for _, v := range vectors { + ts, err := parsePAXTime(v.in) + ok := (err == nil) + if v.ok != ok { + if v.ok { + t.Errorf("parsePAXTime(%q): got parsing failure, want success", v.in) + } else { + t.Errorf("parsePAXTime(%q): got parsing success, want failure", v.in) + } + } + if ok && !ts.Equal(v.want) { + t.Errorf("parsePAXTime(%q): got (%ds %dns), want (%ds %dns)", + v.in, ts.Unix(), ts.Nanosecond(), v.want.Unix(), v.want.Nanosecond()) + } + } +} + +func TestFormatPAXTime(t *testing.T) { + vectors := []struct { + sec, nsec int64 + want string + }{ + {1350244992, 0, "1350244992"}, + {1350244992, 300000000, "1350244992.3"}, + {1350244992, 23960100, "1350244992.0239601"}, + {1350244992, 23960108, "1350244992.023960108"}, + {+1, +1e9 - 1e0, "1.999999999"}, + {+1, +1e9 - 1e3, "1.999999"}, + {+1, +1e9 - 1e6, "1.999"}, + {+1, +0e0 - 0e0, "1"}, + {+1, +1e6 - 0e0, "1.001"}, + {+1, +1e3 - 0e0, "1.000001"}, + {+1, +1e0 - 0e0, "1.000000001"}, + {0, 1e9 - 1e0, "0.999999999"}, + {0, 1e9 - 1e3, "0.999999"}, + {0, 1e9 - 1e6, "0.999"}, + {0, 0e0, "0"}, + {0, 1e6 + 0e0, "0.001"}, + {0, 1e3 + 0e0, "0.000001"}, + {0, 1e0 + 0e0, "0.000000001"}, + {-1, -1e9 + 1e0, "-1.999999999"}, + {-1, -1e9 + 1e3, "-1.999999"}, + {-1, -1e9 + 1e6, "-1.999"}, + {-1, -0e0 + 0e0, "-1"}, + {-1, -1e6 + 0e0, "-1.001"}, + {-1, -1e3 + 0e0, "-1.000001"}, + {-1, -1e0 + 0e0, "-1.000000001"}, + {-1350244992, 0, "-1350244992"}, + {-1350244992, -300000000, "-1350244992.3"}, + {-1350244992, -23960100, "-1350244992.0239601"}, + {-1350244992, -23960108, "-1350244992.023960108"}, + } + + for _, v := range vectors { + got := formatPAXTime(time.Unix(v.sec, v.nsec)) + if got != v.want { + t.Errorf("formatPAXTime(%ds, %dns): got %q, want %q", + v.sec, v.nsec, got, v.want) + } + } +} + +func TestParsePAXRecord(t *testing.T) { + medName := strings.Repeat("CD", 50) + longName := strings.Repeat("AB", 100) + + vectors := []struct { + in string + wantRes string + wantKey string + wantVal string + ok bool + }{ + {"6 k=v\n\n", "\n", "k", "v", true}, + {"19 path=/etc/hosts\n", "", "path", "/etc/hosts", true}, + {"210 path=" + longName + "\nabc", "abc", "path", longName, true}, + {"110 path=" + medName + "\n", "", "path", medName, true}, + {"9 foo=ba\n", "", "foo", "ba", true}, + {"11 foo=bar\n\x00", "\x00", "foo", "bar", true}, + {"18 foo=b=\nar=\n==\x00\n", "", "foo", "b=\nar=\n==\x00", true}, + {"27 foo=hello9 foo=ba\nworld\n", "", "foo", "hello9 foo=ba\nworld", true}, + {"27 ☺☻☹=日a本b語ç\nmeow mix", "meow mix", "☺☻☹", "日a本b語ç", true}, + {"17 \x00hello=\x00world\n", "17 \x00hello=\x00world\n", "", "", false}, + {"1 k=1\n", "1 k=1\n", "", "", false}, + {"6 k~1\n", "6 k~1\n", "", "", false}, + {"6_k=1\n", "6_k=1\n", "", "", false}, + {"6 k=1 ", "6 k=1 ", "", "", false}, + {"632 k=1\n", "632 k=1\n", "", "", false}, + {"16 longkeyname=hahaha\n", "16 longkeyname=hahaha\n", "", "", false}, + {"3 somelongkey=\n", "3 somelongkey=\n", "", "", false}, + {"50 tooshort=\n", "50 tooshort=\n", "", "", false}, + {"0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319", "0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319", "mtime", "1432668921.098285006", false}, + {"06 k=v\n", "06 k=v\n", "", "", false}, + {"00006 k=v\n", "00006 k=v\n", "", "", false}, + {"000006 k=v\n", "000006 k=v\n", "", "", false}, + {"000000 k=v\n", "000000 k=v\n", "", "", false}, + {"0 k=v\n", "0 k=v\n", "", "", false}, + {"+0000005 x=\n", "+0000005 x=\n", "", "", false}, + } + + for _, v := range vectors { + key, val, res, err := parsePAXRecord(v.in) + ok := (err == nil) + if ok != v.ok { + if v.ok { + t.Errorf("parsePAXRecord(%q): got parsing failure, want success", v.in) + } else { + t.Errorf("parsePAXRecord(%q): got parsing success, want failure", v.in) + } + } + if v.ok && (key != v.wantKey || val != v.wantVal) { + t.Errorf("parsePAXRecord(%q): got (%q: %q), want (%q: %q)", + v.in, key, val, v.wantKey, v.wantVal) + } + if res != v.wantRes { + t.Errorf("parsePAXRecord(%q): got residual %q, want residual %q", + v.in, res, v.wantRes) + } + } +} + +func TestFormatPAXRecord(t *testing.T) { + medName := strings.Repeat("CD", 50) + longName := strings.Repeat("AB", 100) + + vectors := []struct { + inKey string + inVal string + want string + ok bool + }{ + {"k", "v", "6 k=v\n", true}, + {"path", "/etc/hosts", "19 path=/etc/hosts\n", true}, + {"path", longName, "210 path=" + longName + "\n", true}, + {"path", medName, "110 path=" + medName + "\n", true}, + {"foo", "ba", "9 foo=ba\n", true}, + {"foo", "bar", "11 foo=bar\n", true}, + {"foo", "b=\nar=\n==\x00", "18 foo=b=\nar=\n==\x00\n", true}, + {"foo", "hello9 foo=ba\nworld", "27 foo=hello9 foo=ba\nworld\n", true}, + {"☺☻☹", "日a本b語ç", "27 ☺☻☹=日a本b語ç\n", true}, + {"xhello", "\x00world", "17 xhello=\x00world\n", true}, + {"path", "null\x00", "", false}, + {"null\x00", "value", "", false}, + {paxSchilyXattr + "key", "null\x00", "26 SCHILY.xattr.key=null\x00\n", true}, + } + + for _, v := range vectors { + got, err := formatPAXRecord(v.inKey, v.inVal) + ok := (err == nil) + if ok != v.ok { + if v.ok { + t.Errorf("formatPAXRecord(%q, %q): got format failure, want success", v.inKey, v.inVal) + } else { + t.Errorf("formatPAXRecord(%q, %q): got format success, want failure", v.inKey, v.inVal) + } + } + if got != v.want { + t.Errorf("formatPAXRecord(%q, %q): got %q, want %q", + v.inKey, v.inVal, got, v.want) + } + } +} diff --git a/vendor/archive/tar/tar_test.go b/vendor/archive/tar/tar_test.go new file mode 100644 index 0000000000000..e9fafc7cc70df --- /dev/null +++ b/vendor/archive/tar/tar_test.go @@ -0,0 +1,850 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "bytes" + "errors" + "fmt" + "internal/testenv" + "io" + "io/fs" + "math" + "os" + "path" + "path/filepath" + "reflect" + "strings" + "testing" + "time" +) + +type testError struct{ error } + +type fileOps []interface{} // []T where T is (string | int64) + +// testFile is an io.ReadWriteSeeker where the IO operations performed +// on it must match the list of operations in ops. +type testFile struct { + ops fileOps + pos int64 +} + +func (f *testFile) Read(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } + if len(f.ops) == 0 { + return 0, io.EOF + } + s, ok := f.ops[0].(string) + if !ok { + return 0, errors.New("unexpected Read operation") + } + + n := copy(b, s) + if len(s) > n { + f.ops[0] = s[n:] + } else { + f.ops = f.ops[1:] + } + f.pos += int64(len(b)) + return n, nil +} + +func (f *testFile) Write(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } + if len(f.ops) == 0 { + return 0, errors.New("unexpected Write operation") + } + s, ok := f.ops[0].(string) + if !ok { + return 0, errors.New("unexpected Write operation") + } + + if !strings.HasPrefix(s, string(b)) { + return 0, testError{fmt.Errorf("got Write(%q), want Write(%q)", b, s)} + } + if len(s) > len(b) { + f.ops[0] = s[len(b):] + } else { + f.ops = f.ops[1:] + } + f.pos += int64(len(b)) + return len(b), nil +} + +func (f *testFile) Seek(pos int64, whence int) (int64, error) { + if pos == 0 && whence == io.SeekCurrent { + return f.pos, nil + } + if len(f.ops) == 0 { + return 0, errors.New("unexpected Seek operation") + } + s, ok := f.ops[0].(int64) + if !ok { + return 0, errors.New("unexpected Seek operation") + } + + if s != pos || whence != io.SeekCurrent { + return 0, testError{fmt.Errorf("got Seek(%d, %d), want Seek(%d, %d)", pos, whence, s, io.SeekCurrent)} + } + f.pos += s + f.ops = f.ops[1:] + return f.pos, nil +} + +func equalSparseEntries(x, y []sparseEntry) bool { + return (len(x) == 0 && len(y) == 0) || reflect.DeepEqual(x, y) +} + +func TestSparseEntries(t *testing.T) { + vectors := []struct { + in []sparseEntry + size int64 + + wantValid bool // Result of validateSparseEntries + wantAligned []sparseEntry // Result of alignSparseEntries + wantInverted []sparseEntry // Result of invertSparseEntries + }{{ + in: []sparseEntry{}, size: 0, + wantValid: true, + wantInverted: []sparseEntry{{0, 0}}, + }, { + in: []sparseEntry{}, size: 5000, + wantValid: true, + wantInverted: []sparseEntry{{0, 5000}}, + }, { + in: []sparseEntry{{0, 5000}}, size: 5000, + wantValid: true, + wantAligned: []sparseEntry{{0, 5000}}, + wantInverted: []sparseEntry{{5000, 0}}, + }, { + in: []sparseEntry{{1000, 4000}}, size: 5000, + wantValid: true, + wantAligned: []sparseEntry{{1024, 3976}}, + wantInverted: []sparseEntry{{0, 1000}, {5000, 0}}, + }, { + in: []sparseEntry{{0, 3000}}, size: 5000, + wantValid: true, + wantAligned: []sparseEntry{{0, 2560}}, + wantInverted: []sparseEntry{{3000, 2000}}, + }, { + in: []sparseEntry{{3000, 2000}}, size: 5000, + wantValid: true, + wantAligned: []sparseEntry{{3072, 1928}}, + wantInverted: []sparseEntry{{0, 3000}, {5000, 0}}, + }, { + in: []sparseEntry{{2000, 2000}}, size: 5000, + wantValid: true, + wantAligned: []sparseEntry{{2048, 1536}}, + wantInverted: []sparseEntry{{0, 2000}, {4000, 1000}}, + }, { + in: []sparseEntry{{0, 2000}, {8000, 2000}}, size: 10000, + wantValid: true, + wantAligned: []sparseEntry{{0, 1536}, {8192, 1808}}, + wantInverted: []sparseEntry{{2000, 6000}, {10000, 0}}, + }, { + in: []sparseEntry{{0, 2000}, {2000, 2000}, {4000, 0}, {4000, 3000}, {7000, 1000}, {8000, 0}, {8000, 2000}}, size: 10000, + wantValid: true, + wantAligned: []sparseEntry{{0, 1536}, {2048, 1536}, {4096, 2560}, {7168, 512}, {8192, 1808}}, + wantInverted: []sparseEntry{{10000, 0}}, + }, { + in: []sparseEntry{{0, 0}, {1000, 0}, {2000, 0}, {3000, 0}, {4000, 0}, {5000, 0}}, size: 5000, + wantValid: true, + wantInverted: []sparseEntry{{0, 5000}}, + }, { + in: []sparseEntry{{1, 0}}, size: 0, + wantValid: false, + }, { + in: []sparseEntry{{-1, 0}}, size: 100, + wantValid: false, + }, { + in: []sparseEntry{{0, -1}}, size: 100, + wantValid: false, + }, { + in: []sparseEntry{{0, 0}}, size: -100, + wantValid: false, + }, { + in: []sparseEntry{{math.MaxInt64, 3}, {6, -5}}, size: 35, + wantValid: false, + }, { + in: []sparseEntry{{1, 3}, {6, -5}}, size: 35, + wantValid: false, + }, { + in: []sparseEntry{{math.MaxInt64, math.MaxInt64}}, size: math.MaxInt64, + wantValid: false, + }, { + in: []sparseEntry{{3, 3}}, size: 5, + wantValid: false, + }, { + in: []sparseEntry{{2, 0}, {1, 0}, {0, 0}}, size: 3, + wantValid: false, + }, { + in: []sparseEntry{{1, 3}, {2, 2}}, size: 10, + wantValid: false, + }} + + for i, v := range vectors { + gotValid := validateSparseEntries(v.in, v.size) + if gotValid != v.wantValid { + t.Errorf("test %d, validateSparseEntries() = %v, want %v", i, gotValid, v.wantValid) + } + if !v.wantValid { + continue + } + gotAligned := alignSparseEntries(append([]sparseEntry{}, v.in...), v.size) + if !equalSparseEntries(gotAligned, v.wantAligned) { + t.Errorf("test %d, alignSparseEntries():\ngot %v\nwant %v", i, gotAligned, v.wantAligned) + } + gotInverted := invertSparseEntries(append([]sparseEntry{}, v.in...), v.size) + if !equalSparseEntries(gotInverted, v.wantInverted) { + t.Errorf("test %d, inverseSparseEntries():\ngot %v\nwant %v", i, gotInverted, v.wantInverted) + } + } +} + +func TestFileInfoHeader(t *testing.T) { + fi, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + h, err := FileInfoHeader(fi, "") + if err != nil { + t.Fatalf("FileInfoHeader: %v", err) + } + if g, e := h.Name, "small.txt"; g != e { + t.Errorf("Name = %q; want %q", g, e) + } + if g, e := h.Mode, int64(fi.Mode().Perm()); g != e { + t.Errorf("Mode = %#o; want %#o", g, e) + } + if g, e := h.Size, int64(5); g != e { + t.Errorf("Size = %v; want %v", g, e) + } + if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) { + t.Errorf("ModTime = %v; want %v", g, e) + } + // FileInfoHeader should error when passing nil FileInfo + if _, err := FileInfoHeader(nil, ""); err == nil { + t.Fatalf("Expected error when passing nil to FileInfoHeader") + } +} + +func TestFileInfoHeaderDir(t *testing.T) { + fi, err := os.Stat("testdata") + if err != nil { + t.Fatal(err) + } + h, err := FileInfoHeader(fi, "") + if err != nil { + t.Fatalf("FileInfoHeader: %v", err) + } + if g, e := h.Name, "testdata/"; g != e { + t.Errorf("Name = %q; want %q", g, e) + } + // Ignoring c_ISGID for golang.org/issue/4867 + if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm()); g != e { + t.Errorf("Mode = %#o; want %#o", g, e) + } + if g, e := h.Size, int64(0); g != e { + t.Errorf("Size = %v; want %v", g, e) + } + if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) { + t.Errorf("ModTime = %v; want %v", g, e) + } +} + +func TestFileInfoHeaderSymlink(t *testing.T) { + testenv.MustHaveSymlink(t) + + tmpdir := t.TempDir() + + link := filepath.Join(tmpdir, "link") + target := tmpdir + if err := os.Symlink(target, link); err != nil { + t.Fatal(err) + } + fi, err := os.Lstat(link) + if err != nil { + t.Fatal(err) + } + + h, err := FileInfoHeader(fi, target) + if err != nil { + t.Fatal(err) + } + if g, e := h.Name, fi.Name(); g != e { + t.Errorf("Name = %q; want %q", g, e) + } + if g, e := h.Linkname, target; g != e { + t.Errorf("Linkname = %q; want %q", g, e) + } + if g, e := h.Typeflag, byte(TypeSymlink); g != e { + t.Errorf("Typeflag = %v; want %v", g, e) + } +} + +func TestRoundTrip(t *testing.T) { + data := []byte("some file contents") + + var b bytes.Buffer + tw := NewWriter(&b) + hdr := &Header{ + Name: "file.txt", + Uid: 1 << 21, // Too big for 8 octal digits + Size: int64(len(data)), + ModTime: time.Now().Round(time.Second), + PAXRecords: map[string]string{"uid": "2097152"}, + Format: FormatPAX, + Typeflag: TypeReg, + } + if err := tw.WriteHeader(hdr); err != nil { + t.Fatalf("tw.WriteHeader: %v", err) + } + if _, err := tw.Write(data); err != nil { + t.Fatalf("tw.Write: %v", err) + } + if err := tw.Close(); err != nil { + t.Fatalf("tw.Close: %v", err) + } + + // Read it back. + tr := NewReader(&b) + rHdr, err := tr.Next() + if err != nil { + t.Fatalf("tr.Next: %v", err) + } + if !reflect.DeepEqual(rHdr, hdr) { + t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr) + } + rData, err := io.ReadAll(tr) + if err != nil { + t.Fatalf("Read: %v", err) + } + if !bytes.Equal(rData, data) { + t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data) + } +} + +type headerRoundTripTest struct { + h *Header + fm fs.FileMode +} + +func TestHeaderRoundTrip(t *testing.T) { + vectors := []headerRoundTripTest{{ + // regular file. + h: &Header{ + Name: "test.txt", + Mode: 0644, + Size: 12, + ModTime: time.Unix(1360600916, 0), + Typeflag: TypeReg, + }, + fm: 0644, + }, { + // symbolic link. + h: &Header{ + Name: "link.txt", + Mode: 0777, + Size: 0, + ModTime: time.Unix(1360600852, 0), + Typeflag: TypeSymlink, + }, + fm: 0777 | fs.ModeSymlink, + }, { + // character device node. + h: &Header{ + Name: "dev/null", + Mode: 0666, + Size: 0, + ModTime: time.Unix(1360578951, 0), + Typeflag: TypeChar, + }, + fm: 0666 | fs.ModeDevice | fs.ModeCharDevice, + }, { + // block device node. + h: &Header{ + Name: "dev/sda", + Mode: 0660, + Size: 0, + ModTime: time.Unix(1360578954, 0), + Typeflag: TypeBlock, + }, + fm: 0660 | fs.ModeDevice, + }, { + // directory. + h: &Header{ + Name: "dir/", + Mode: 0755, + Size: 0, + ModTime: time.Unix(1360601116, 0), + Typeflag: TypeDir, + }, + fm: 0755 | fs.ModeDir, + }, { + // fifo node. + h: &Header{ + Name: "dev/initctl", + Mode: 0600, + Size: 0, + ModTime: time.Unix(1360578949, 0), + Typeflag: TypeFifo, + }, + fm: 0600 | fs.ModeNamedPipe, + }, { + // setuid. + h: &Header{ + Name: "bin/su", + Mode: 0755 | c_ISUID, + Size: 23232, + ModTime: time.Unix(1355405093, 0), + Typeflag: TypeReg, + }, + fm: 0755 | fs.ModeSetuid, + }, { + // setguid. + h: &Header{ + Name: "group.txt", + Mode: 0750 | c_ISGID, + Size: 0, + ModTime: time.Unix(1360602346, 0), + Typeflag: TypeReg, + }, + fm: 0750 | fs.ModeSetgid, + }, { + // sticky. + h: &Header{ + Name: "sticky.txt", + Mode: 0600 | c_ISVTX, + Size: 7, + ModTime: time.Unix(1360602540, 0), + Typeflag: TypeReg, + }, + fm: 0600 | fs.ModeSticky, + }, { + // hard link. + h: &Header{ + Name: "hard.txt", + Mode: 0644, + Size: 0, + Linkname: "file.txt", + ModTime: time.Unix(1360600916, 0), + Typeflag: TypeLink, + }, + fm: 0644, + }, { + // More information. + h: &Header{ + Name: "info.txt", + Mode: 0600, + Size: 0, + Uid: 1000, + Gid: 1000, + ModTime: time.Unix(1360602540, 0), + Uname: "slartibartfast", + Gname: "users", + Typeflag: TypeReg, + }, + fm: 0600, + }} + + for i, v := range vectors { + fi := v.h.FileInfo() + h2, err := FileInfoHeader(fi, "") + if err != nil { + t.Error(err) + continue + } + if strings.Contains(fi.Name(), "/") { + t.Errorf("FileInfo of %q contains slash: %q", v.h.Name, fi.Name()) + } + name := path.Base(v.h.Name) + if fi.IsDir() { + name += "/" + } + if got, want := h2.Name, name; got != want { + t.Errorf("i=%d: Name: got %v, want %v", i, got, want) + } + if got, want := h2.Size, v.h.Size; got != want { + t.Errorf("i=%d: Size: got %v, want %v", i, got, want) + } + if got, want := h2.Uid, v.h.Uid; got != want { + t.Errorf("i=%d: Uid: got %d, want %d", i, got, want) + } + if got, want := h2.Gid, v.h.Gid; got != want { + t.Errorf("i=%d: Gid: got %d, want %d", i, got, want) + } + if got, want := h2.Uname, v.h.Uname; got != want { + t.Errorf("i=%d: Uname: got %q, want %q", i, got, want) + } + if got, want := h2.Gname, v.h.Gname; got != want { + t.Errorf("i=%d: Gname: got %q, want %q", i, got, want) + } + if got, want := h2.Linkname, v.h.Linkname; got != want { + t.Errorf("i=%d: Linkname: got %v, want %v", i, got, want) + } + if got, want := h2.Typeflag, v.h.Typeflag; got != want { + t.Logf("%#v %#v", v.h, fi.Sys()) + t.Errorf("i=%d: Typeflag: got %q, want %q", i, got, want) + } + if got, want := h2.Mode, v.h.Mode; got != want { + t.Errorf("i=%d: Mode: got %o, want %o", i, got, want) + } + if got, want := fi.Mode(), v.fm; got != want { + t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want) + } + if got, want := h2.AccessTime, v.h.AccessTime; got != want { + t.Errorf("i=%d: AccessTime: got %v, want %v", i, got, want) + } + if got, want := h2.ChangeTime, v.h.ChangeTime; got != want { + t.Errorf("i=%d: ChangeTime: got %v, want %v", i, got, want) + } + if got, want := h2.ModTime, v.h.ModTime; got != want { + t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want) + } + if sysh, ok := fi.Sys().(*Header); !ok || sysh != v.h { + t.Errorf("i=%d: Sys didn't return original *Header", i) + } + } +} + +func TestHeaderAllowedFormats(t *testing.T) { + vectors := []struct { + header *Header // Input header + paxHdrs map[string]string // Expected PAX headers that may be needed + formats Format // Expected formats that can encode the header + }{{ + header: &Header{}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{Size: 077777777777}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{Size: 077777777777, Format: FormatUSTAR}, + formats: FormatUSTAR, + }, { + header: &Header{Size: 077777777777, Format: FormatPAX}, + formats: FormatUSTAR | FormatPAX, + }, { + header: &Header{Size: 077777777777, Format: FormatGNU}, + formats: FormatGNU, + }, { + header: &Header{Size: 077777777777 + 1}, + paxHdrs: map[string]string{paxSize: "8589934592"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{Size: 077777777777 + 1, Format: FormatPAX}, + paxHdrs: map[string]string{paxSize: "8589934592"}, + formats: FormatPAX, + }, { + header: &Header{Size: 077777777777 + 1, Format: FormatGNU}, + paxHdrs: map[string]string{paxSize: "8589934592"}, + formats: FormatGNU, + }, { + header: &Header{Mode: 07777777}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{Mode: 07777777 + 1}, + formats: FormatGNU, + }, { + header: &Header{Devmajor: -123}, + formats: FormatGNU, + }, { + header: &Header{Devmajor: 1<<56 - 1}, + formats: FormatGNU, + }, { + header: &Header{Devmajor: 1 << 56}, + formats: FormatUnknown, + }, { + header: &Header{Devmajor: -1 << 56}, + formats: FormatGNU, + }, { + header: &Header{Devmajor: -1<<56 - 1}, + formats: FormatUnknown, + }, { + header: &Header{Name: "用戶名", Devmajor: -1 << 56}, + formats: FormatGNU, + }, { + header: &Header{Size: math.MaxInt64}, + paxHdrs: map[string]string{paxSize: "9223372036854775807"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{Size: math.MinInt64}, + paxHdrs: map[string]string{paxSize: "-9223372036854775808"}, + formats: FormatUnknown, + }, { + header: &Header{Uname: "0123456789abcdef0123456789abcdef"}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{Uname: "0123456789abcdef0123456789abcdefx"}, + paxHdrs: map[string]string{paxUname: "0123456789abcdef0123456789abcdefx"}, + formats: FormatPAX, + }, { + header: &Header{Name: "foobar"}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{Name: strings.Repeat("a", nameSize)}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{Name: strings.Repeat("a", nameSize+1)}, + paxHdrs: map[string]string{paxPath: strings.Repeat("a", nameSize+1)}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{Linkname: "用戶名"}, + paxHdrs: map[string]string{paxLinkpath: "用戶名"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{Linkname: strings.Repeat("用戶名\x00", nameSize)}, + paxHdrs: map[string]string{paxLinkpath: strings.Repeat("用戶名\x00", nameSize)}, + formats: FormatUnknown, + }, { + header: &Header{Linkname: "\x00hello"}, + paxHdrs: map[string]string{paxLinkpath: "\x00hello"}, + formats: FormatUnknown, + }, { + header: &Header{Uid: 07777777}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{Uid: 07777777 + 1}, + paxHdrs: map[string]string{paxUid: "2097152"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{Xattrs: nil}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{Xattrs: map[string]string{"foo": "bar"}}, + paxHdrs: map[string]string{paxSchilyXattr + "foo": "bar"}, + formats: FormatPAX, + }, { + header: &Header{Xattrs: map[string]string{"foo": "bar"}, Format: FormatGNU}, + paxHdrs: map[string]string{paxSchilyXattr + "foo": "bar"}, + formats: FormatUnknown, + }, { + header: &Header{Xattrs: map[string]string{"用戶名": "\x00hello"}}, + paxHdrs: map[string]string{paxSchilyXattr + "用戶名": "\x00hello"}, + formats: FormatPAX, + }, { + header: &Header{Xattrs: map[string]string{"foo=bar": "baz"}}, + formats: FormatUnknown, + }, { + header: &Header{Xattrs: map[string]string{"foo": ""}}, + paxHdrs: map[string]string{paxSchilyXattr + "foo": ""}, + formats: FormatPAX, + }, { + header: &Header{ModTime: time.Unix(0, 0)}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{ModTime: time.Unix(077777777777, 0)}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{ModTime: time.Unix(077777777777+1, 0)}, + paxHdrs: map[string]string{paxMtime: "8589934592"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{ModTime: time.Unix(math.MaxInt64, 0)}, + paxHdrs: map[string]string{paxMtime: "9223372036854775807"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{ModTime: time.Unix(math.MaxInt64, 0), Format: FormatUSTAR}, + paxHdrs: map[string]string{paxMtime: "9223372036854775807"}, + formats: FormatUnknown, + }, { + header: &Header{ModTime: time.Unix(-1, 0)}, + paxHdrs: map[string]string{paxMtime: "-1"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{ModTime: time.Unix(1, 500)}, + paxHdrs: map[string]string{paxMtime: "1.0000005"}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{ModTime: time.Unix(1, 0)}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{ModTime: time.Unix(1, 0), Format: FormatPAX}, + formats: FormatUSTAR | FormatPAX, + }, { + header: &Header{ModTime: time.Unix(1, 500), Format: FormatUSTAR}, + paxHdrs: map[string]string{paxMtime: "1.0000005"}, + formats: FormatUSTAR, + }, { + header: &Header{ModTime: time.Unix(1, 500), Format: FormatPAX}, + paxHdrs: map[string]string{paxMtime: "1.0000005"}, + formats: FormatPAX, + }, { + header: &Header{ModTime: time.Unix(1, 500), Format: FormatGNU}, + paxHdrs: map[string]string{paxMtime: "1.0000005"}, + formats: FormatGNU, + }, { + header: &Header{ModTime: time.Unix(-1, 500)}, + paxHdrs: map[string]string{paxMtime: "-0.9999995"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{ModTime: time.Unix(-1, 500), Format: FormatGNU}, + paxHdrs: map[string]string{paxMtime: "-0.9999995"}, + formats: FormatGNU, + }, { + header: &Header{AccessTime: time.Unix(0, 0)}, + paxHdrs: map[string]string{paxAtime: "0"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{AccessTime: time.Unix(0, 0), Format: FormatUSTAR}, + paxHdrs: map[string]string{paxAtime: "0"}, + formats: FormatUnknown, + }, { + header: &Header{AccessTime: time.Unix(0, 0), Format: FormatPAX}, + paxHdrs: map[string]string{paxAtime: "0"}, + formats: FormatPAX, + }, { + header: &Header{AccessTime: time.Unix(0, 0), Format: FormatGNU}, + paxHdrs: map[string]string{paxAtime: "0"}, + formats: FormatGNU, + }, { + header: &Header{AccessTime: time.Unix(-123, 0)}, + paxHdrs: map[string]string{paxAtime: "-123"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{AccessTime: time.Unix(-123, 0), Format: FormatPAX}, + paxHdrs: map[string]string{paxAtime: "-123"}, + formats: FormatPAX, + }, { + header: &Header{ChangeTime: time.Unix(123, 456)}, + paxHdrs: map[string]string{paxCtime: "123.000000456"}, + formats: FormatPAX | FormatGNU, + }, { + header: &Header{ChangeTime: time.Unix(123, 456), Format: FormatUSTAR}, + paxHdrs: map[string]string{paxCtime: "123.000000456"}, + formats: FormatUnknown, + }, { + header: &Header{ChangeTime: time.Unix(123, 456), Format: FormatGNU}, + paxHdrs: map[string]string{paxCtime: "123.000000456"}, + formats: FormatGNU, + }, { + header: &Header{ChangeTime: time.Unix(123, 456), Format: FormatPAX}, + paxHdrs: map[string]string{paxCtime: "123.000000456"}, + formats: FormatPAX, + }, { + header: &Header{Name: "foo/", Typeflag: TypeDir}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }, { + header: &Header{Name: "foo/", Typeflag: TypeReg}, + formats: FormatUnknown, + }, { + header: &Header{Name: "foo/", Typeflag: TypeSymlink}, + formats: FormatUSTAR | FormatPAX | FormatGNU, + }} + + for i, v := range vectors { + formats, paxHdrs, err := v.header.allowedFormats() + if formats != v.formats { + t.Errorf("test %d, allowedFormats(): got %v, want %v", i, formats, v.formats) + } + if formats&FormatPAX > 0 && !reflect.DeepEqual(paxHdrs, v.paxHdrs) && !(len(paxHdrs) == 0 && len(v.paxHdrs) == 0) { + t.Errorf("test %d, allowedFormats():\ngot %v\nwant %s", i, paxHdrs, v.paxHdrs) + } + if (formats != FormatUnknown) && (err != nil) { + t.Errorf("test %d, unexpected error: %v", i, err) + } + if (formats == FormatUnknown) && (err == nil) { + t.Errorf("test %d, got nil-error, want non-nil error", i) + } + } +} + +func Benchmark(b *testing.B) { + type file struct { + hdr *Header + body []byte + } + + vectors := []struct { + label string + files []file + }{{ + "USTAR", + []file{{ + &Header{Name: "bar", Mode: 0640, Size: int64(3)}, + []byte("foo"), + }, { + &Header{Name: "world", Mode: 0640, Size: int64(5)}, + []byte("hello"), + }}, + }, { + "GNU", + []file{{ + &Header{Name: "bar", Mode: 0640, Size: int64(3), Devmajor: -1}, + []byte("foo"), + }, { + &Header{Name: "world", Mode: 0640, Size: int64(5), Devmajor: -1}, + []byte("hello"), + }}, + }, { + "PAX", + []file{{ + &Header{Name: "bar", Mode: 0640, Size: int64(3), Xattrs: map[string]string{"foo": "bar"}}, + []byte("foo"), + }, { + &Header{Name: "world", Mode: 0640, Size: int64(5), Xattrs: map[string]string{"foo": "bar"}}, + []byte("hello"), + }}, + }} + + b.Run("Writer", func(b *testing.B) { + for _, v := range vectors { + b.Run(v.label, func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + // Writing to io.Discard because we want to + // test purely the writer code and not bring in disk performance into this. + tw := NewWriter(io.Discard) + for _, file := range v.files { + if err := tw.WriteHeader(file.hdr); err != nil { + b.Errorf("unexpected WriteHeader error: %v", err) + } + if _, err := tw.Write(file.body); err != nil { + b.Errorf("unexpected Write error: %v", err) + } + } + if err := tw.Close(); err != nil { + b.Errorf("unexpected Close error: %v", err) + } + } + }) + } + }) + + b.Run("Reader", func(b *testing.B) { + for _, v := range vectors { + var buf bytes.Buffer + var r bytes.Reader + + // Write the archive to a byte buffer. + tw := NewWriter(&buf) + for _, file := range v.files { + tw.WriteHeader(file.hdr) + tw.Write(file.body) + } + tw.Close() + b.Run(v.label, func(b *testing.B) { + b.ReportAllocs() + // Read from the byte buffer. + for i := 0; i < b.N; i++ { + r.Reset(buf.Bytes()) + tr := NewReader(&r) + if _, err := tr.Next(); err != nil { + b.Errorf("unexpected Next error: %v", err) + } + if _, err := io.Copy(io.Discard, tr); err != nil { + b.Errorf("unexpected Copy error : %v", err) + } + } + }) + } + }) + +} diff --git a/vendor/archive/tar/testdata/file-and-dir.tar b/vendor/archive/tar/testdata/file-and-dir.tar new file mode 100644 index 0000000000000..c18d4283e3809 Binary files /dev/null and b/vendor/archive/tar/testdata/file-and-dir.tar differ diff --git a/vendor/archive/tar/testdata/gnu-incremental.tar b/vendor/archive/tar/testdata/gnu-incremental.tar new file mode 100644 index 0000000000000..4c442e5b82d19 Binary files /dev/null and b/vendor/archive/tar/testdata/gnu-incremental.tar differ diff --git a/vendor/archive/tar/testdata/gnu-long-nul.tar b/vendor/archive/tar/testdata/gnu-long-nul.tar new file mode 100644 index 0000000000000..28bc812aa60e8 Binary files /dev/null and b/vendor/archive/tar/testdata/gnu-long-nul.tar differ diff --git a/vendor/archive/tar/testdata/gnu-multi-hdrs.tar b/vendor/archive/tar/testdata/gnu-multi-hdrs.tar new file mode 100644 index 0000000000000..8bcad55d06e8f Binary files /dev/null and b/vendor/archive/tar/testdata/gnu-multi-hdrs.tar differ diff --git a/vendor/archive/tar/testdata/gnu-nil-sparse-data.tar b/vendor/archive/tar/testdata/gnu-nil-sparse-data.tar new file mode 100644 index 0000000000000..df1aa834538e7 Binary files /dev/null and b/vendor/archive/tar/testdata/gnu-nil-sparse-data.tar differ diff --git a/vendor/archive/tar/testdata/gnu-nil-sparse-hole.tar b/vendor/archive/tar/testdata/gnu-nil-sparse-hole.tar new file mode 100644 index 0000000000000..496abfeb78a5e Binary files /dev/null and b/vendor/archive/tar/testdata/gnu-nil-sparse-hole.tar differ diff --git a/vendor/archive/tar/testdata/gnu-not-utf8.tar b/vendor/archive/tar/testdata/gnu-not-utf8.tar new file mode 100644 index 0000000000000..81cec67d33095 Binary files /dev/null and b/vendor/archive/tar/testdata/gnu-not-utf8.tar differ diff --git a/vendor/archive/tar/testdata/gnu-sparse-big.tar b/vendor/archive/tar/testdata/gnu-sparse-big.tar new file mode 100644 index 0000000000000..1a5cfc96d9298 Binary files /dev/null and b/vendor/archive/tar/testdata/gnu-sparse-big.tar differ diff --git a/vendor/archive/tar/testdata/gnu-utf8.tar b/vendor/archive/tar/testdata/gnu-utf8.tar new file mode 100644 index 0000000000000..2c9c8079cf651 Binary files /dev/null and b/vendor/archive/tar/testdata/gnu-utf8.tar differ diff --git a/vendor/archive/tar/testdata/gnu.tar b/vendor/archive/tar/testdata/gnu.tar new file mode 100644 index 0000000000000..fc899dc8dc2ad Binary files /dev/null and b/vendor/archive/tar/testdata/gnu.tar differ diff --git a/vendor/archive/tar/testdata/hardlink.tar b/vendor/archive/tar/testdata/hardlink.tar new file mode 100644 index 0000000000000..9cd1a26572e44 Binary files /dev/null and b/vendor/archive/tar/testdata/hardlink.tar differ diff --git a/vendor/archive/tar/testdata/hdr-only.tar b/vendor/archive/tar/testdata/hdr-only.tar new file mode 100644 index 0000000000000..f25034083de6e Binary files /dev/null and b/vendor/archive/tar/testdata/hdr-only.tar differ diff --git a/vendor/archive/tar/testdata/invalid-go17.tar b/vendor/archive/tar/testdata/invalid-go17.tar new file mode 100644 index 0000000000000..58f2488e78fb4 Binary files /dev/null and b/vendor/archive/tar/testdata/invalid-go17.tar differ diff --git a/vendor/archive/tar/testdata/issue10968.tar b/vendor/archive/tar/testdata/issue10968.tar new file mode 100644 index 0000000000000..1cc837bcff14c Binary files /dev/null and b/vendor/archive/tar/testdata/issue10968.tar differ diff --git a/vendor/archive/tar/testdata/issue11169.tar b/vendor/archive/tar/testdata/issue11169.tar new file mode 100644 index 0000000000000..4d71fa1526060 Binary files /dev/null and b/vendor/archive/tar/testdata/issue11169.tar differ diff --git a/vendor/archive/tar/testdata/issue12435.tar b/vendor/archive/tar/testdata/issue12435.tar new file mode 100644 index 0000000000000..3542dd8efd5d4 Binary files /dev/null and b/vendor/archive/tar/testdata/issue12435.tar differ diff --git a/vendor/archive/tar/testdata/neg-size.tar b/vendor/archive/tar/testdata/neg-size.tar new file mode 100644 index 0000000000000..21edf38cc3c3d Binary files /dev/null and b/vendor/archive/tar/testdata/neg-size.tar differ diff --git a/vendor/archive/tar/testdata/nil-uid.tar b/vendor/archive/tar/testdata/nil-uid.tar new file mode 100644 index 0000000000000..cc9cfaa33cc5d Binary files /dev/null and b/vendor/archive/tar/testdata/nil-uid.tar differ diff --git a/vendor/archive/tar/testdata/pax-bad-hdr-file.tar b/vendor/archive/tar/testdata/pax-bad-hdr-file.tar new file mode 100644 index 0000000000000..b97cc981f29b8 Binary files /dev/null and b/vendor/archive/tar/testdata/pax-bad-hdr-file.tar differ diff --git a/vendor/archive/tar/testdata/pax-bad-mtime-file.tar b/vendor/archive/tar/testdata/pax-bad-mtime-file.tar new file mode 100644 index 0000000000000..9b22f7e8d9438 Binary files /dev/null and b/vendor/archive/tar/testdata/pax-bad-mtime-file.tar differ diff --git a/vendor/archive/tar/testdata/pax-global-records.tar b/vendor/archive/tar/testdata/pax-global-records.tar new file mode 100644 index 0000000000000..3d3d241e65c3a Binary files /dev/null and b/vendor/archive/tar/testdata/pax-global-records.tar differ diff --git a/vendor/archive/tar/testdata/pax-multi-hdrs.tar b/vendor/archive/tar/testdata/pax-multi-hdrs.tar new file mode 100644 index 0000000000000..14bc759780802 Binary files /dev/null and b/vendor/archive/tar/testdata/pax-multi-hdrs.tar differ diff --git a/vendor/archive/tar/testdata/pax-nil-sparse-data.tar b/vendor/archive/tar/testdata/pax-nil-sparse-data.tar new file mode 100644 index 0000000000000..e59bd94117d9e Binary files /dev/null and b/vendor/archive/tar/testdata/pax-nil-sparse-data.tar differ diff --git a/vendor/archive/tar/testdata/pax-nil-sparse-hole.tar b/vendor/archive/tar/testdata/pax-nil-sparse-hole.tar new file mode 100644 index 0000000000000..b44327bdbfb34 Binary files /dev/null and b/vendor/archive/tar/testdata/pax-nil-sparse-hole.tar differ diff --git a/vendor/archive/tar/testdata/pax-nul-path.tar b/vendor/archive/tar/testdata/pax-nul-path.tar new file mode 100644 index 0000000000000..c78f82b16e853 Binary files /dev/null and b/vendor/archive/tar/testdata/pax-nul-path.tar differ diff --git a/vendor/archive/tar/testdata/pax-nul-xattrs.tar b/vendor/archive/tar/testdata/pax-nul-xattrs.tar new file mode 100644 index 0000000000000..881f51768f987 Binary files /dev/null and b/vendor/archive/tar/testdata/pax-nul-xattrs.tar differ diff --git a/vendor/archive/tar/testdata/pax-path-hdr.tar b/vendor/archive/tar/testdata/pax-path-hdr.tar new file mode 100644 index 0000000000000..ab8fc325b2615 Binary files /dev/null and b/vendor/archive/tar/testdata/pax-path-hdr.tar differ diff --git a/vendor/archive/tar/testdata/pax-pos-size-file.tar b/vendor/archive/tar/testdata/pax-pos-size-file.tar new file mode 100644 index 0000000000000..ea5ccf916426a Binary files /dev/null and b/vendor/archive/tar/testdata/pax-pos-size-file.tar differ diff --git a/vendor/archive/tar/testdata/pax-records.tar b/vendor/archive/tar/testdata/pax-records.tar new file mode 100644 index 0000000000000..276c211baa388 Binary files /dev/null and b/vendor/archive/tar/testdata/pax-records.tar differ diff --git a/vendor/archive/tar/testdata/pax-sparse-big.tar b/vendor/archive/tar/testdata/pax-sparse-big.tar new file mode 100644 index 0000000000000..65d1f8eceb084 Binary files /dev/null and b/vendor/archive/tar/testdata/pax-sparse-big.tar differ diff --git a/vendor/archive/tar/testdata/pax.tar b/vendor/archive/tar/testdata/pax.tar new file mode 100644 index 0000000000000..9bc24b6587d72 Binary files /dev/null and b/vendor/archive/tar/testdata/pax.tar differ diff --git a/vendor/archive/tar/testdata/small.txt b/vendor/archive/tar/testdata/small.txt new file mode 100644 index 0000000000000..b249bfc518a8c --- /dev/null +++ b/vendor/archive/tar/testdata/small.txt @@ -0,0 +1 @@ +Kilts \ No newline at end of file diff --git a/vendor/archive/tar/testdata/small2.txt b/vendor/archive/tar/testdata/small2.txt new file mode 100644 index 0000000000000..394ee3ecd0edf --- /dev/null +++ b/vendor/archive/tar/testdata/small2.txt @@ -0,0 +1 @@ +Google.com diff --git a/vendor/archive/tar/testdata/sparse-formats.tar b/vendor/archive/tar/testdata/sparse-formats.tar new file mode 100644 index 0000000000000..8bd4e74d50f9c Binary files /dev/null and b/vendor/archive/tar/testdata/sparse-formats.tar differ diff --git a/vendor/archive/tar/testdata/star.tar b/vendor/archive/tar/testdata/star.tar new file mode 100644 index 0000000000000..59e2d4e604611 Binary files /dev/null and b/vendor/archive/tar/testdata/star.tar differ diff --git a/vendor/archive/tar/testdata/trailing-slash.tar b/vendor/archive/tar/testdata/trailing-slash.tar new file mode 100644 index 0000000000000..93718b3034879 Binary files /dev/null and b/vendor/archive/tar/testdata/trailing-slash.tar differ diff --git a/vendor/archive/tar/testdata/ustar-file-devs.tar b/vendor/archive/tar/testdata/ustar-file-devs.tar new file mode 100644 index 0000000000000..146e25b79d898 Binary files /dev/null and b/vendor/archive/tar/testdata/ustar-file-devs.tar differ diff --git a/vendor/archive/tar/testdata/ustar-file-reg.tar b/vendor/archive/tar/testdata/ustar-file-reg.tar new file mode 100644 index 0000000000000..c84fa27ffb861 Binary files /dev/null and b/vendor/archive/tar/testdata/ustar-file-reg.tar differ diff --git a/vendor/archive/tar/testdata/ustar.tar b/vendor/archive/tar/testdata/ustar.tar new file mode 100644 index 0000000000000..29679d9a305fc Binary files /dev/null and b/vendor/archive/tar/testdata/ustar.tar differ diff --git a/vendor/archive/tar/testdata/v7.tar b/vendor/archive/tar/testdata/v7.tar new file mode 100644 index 0000000000000..eb65fc9410721 Binary files /dev/null and b/vendor/archive/tar/testdata/v7.tar differ diff --git a/vendor/archive/tar/testdata/writer-big-long.tar b/vendor/archive/tar/testdata/writer-big-long.tar new file mode 100644 index 0000000000000..09fc5dd3dd7fc Binary files /dev/null and b/vendor/archive/tar/testdata/writer-big-long.tar differ diff --git a/vendor/archive/tar/testdata/writer-big.tar b/vendor/archive/tar/testdata/writer-big.tar new file mode 100644 index 0000000000000..435dcbce6abc7 Binary files /dev/null and b/vendor/archive/tar/testdata/writer-big.tar differ diff --git a/vendor/archive/tar/testdata/writer.tar b/vendor/archive/tar/testdata/writer.tar new file mode 100644 index 0000000000000..e6d816ad0775d Binary files /dev/null and b/vendor/archive/tar/testdata/writer.tar differ diff --git a/vendor/archive/tar/testdata/xattrs.tar b/vendor/archive/tar/testdata/xattrs.tar new file mode 100644 index 0000000000000..9701950edd1f0 Binary files /dev/null and b/vendor/archive/tar/testdata/xattrs.tar differ diff --git a/vendor/archive/tar/writer.go b/vendor/archive/tar/writer.go new file mode 100644 index 0000000000000..e80498d03e39c --- /dev/null +++ b/vendor/archive/tar/writer.go @@ -0,0 +1,653 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "fmt" + "io" + "path" + "sort" + "strings" + "time" +) + +// Writer provides sequential writing of a tar archive. +// Write.WriteHeader begins a new file with the provided Header, +// and then Writer can be treated as an io.Writer to supply that file's data. +type Writer struct { + w io.Writer + pad int64 // Amount of padding to write after current file entry + curr fileWriter // Writer for current file entry + hdr Header // Shallow copy of Header that is safe for mutations + blk block // Buffer to use as temporary local storage + + // err is a persistent error. + // It is only the responsibility of every exported method of Writer to + // ensure that this error is sticky. + err error +} + +// NewWriter creates a new Writer writing to w. +func NewWriter(w io.Writer) *Writer { + return &Writer{w: w, curr: ®FileWriter{w, 0}} +} + +type fileWriter interface { + io.Writer + fileState + + ReadFrom(io.Reader) (int64, error) +} + +// Flush finishes writing the current file's block padding. +// The current file must be fully written before Flush can be called. +// +// This is unnecessary as the next call to WriteHeader or Close +// will implicitly flush out the file's padding. +func (tw *Writer) Flush() error { + if tw.err != nil { + return tw.err + } + if nb := tw.curr.LogicalRemaining(); nb > 0 { + return fmt.Errorf("archive/tar: missed writing %d bytes", nb) + } + if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil { + return tw.err + } + tw.pad = 0 + return nil +} + +// WriteHeader writes hdr and prepares to accept the file's contents. +// The Header.Size determines how many bytes can be written for the next file. +// If the current file is not fully written, then this returns an error. +// This implicitly flushes any padding necessary before writing the header. +func (tw *Writer) WriteHeader(hdr *Header) error { + if err := tw.Flush(); err != nil { + return err + } + tw.hdr = *hdr // Shallow copy of Header + + // Avoid usage of the legacy TypeRegA flag, and automatically promote + // it to use TypeReg or TypeDir. + if tw.hdr.Typeflag == TypeRegA { + if strings.HasSuffix(tw.hdr.Name, "/") { + tw.hdr.Typeflag = TypeDir + } else { + tw.hdr.Typeflag = TypeReg + } + } + + // Round ModTime and ignore AccessTime and ChangeTime unless + // the format is explicitly chosen. + // This ensures nominal usage of WriteHeader (without specifying the format) + // does not always result in the PAX format being chosen, which + // causes a 1KiB increase to every header. + if tw.hdr.Format == FormatUnknown { + tw.hdr.ModTime = tw.hdr.ModTime.Round(time.Second) + tw.hdr.AccessTime = time.Time{} + tw.hdr.ChangeTime = time.Time{} + } + + allowedFormats, paxHdrs, err := tw.hdr.allowedFormats() + switch { + case allowedFormats.has(FormatUSTAR): + tw.err = tw.writeUSTARHeader(&tw.hdr) + return tw.err + case allowedFormats.has(FormatPAX): + tw.err = tw.writePAXHeader(&tw.hdr, paxHdrs) + return tw.err + case allowedFormats.has(FormatGNU): + tw.err = tw.writeGNUHeader(&tw.hdr) + return tw.err + default: + return err // Non-fatal error + } +} + +func (tw *Writer) writeUSTARHeader(hdr *Header) error { + // Check if we can use USTAR prefix/suffix splitting. + var namePrefix string + if prefix, suffix, ok := splitUSTARPath(hdr.Name); ok { + namePrefix, hdr.Name = prefix, suffix + } + + // Pack the main header. + var f formatter + blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal) + f.formatString(blk.USTAR().Prefix(), namePrefix) + blk.SetFormat(FormatUSTAR) + if f.err != nil { + return f.err // Should never happen since header is validated + } + return tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag) +} + +func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error { + realName, realSize := hdr.Name, hdr.Size + + // TODO(dsnet): Re-enable this when adding sparse support. + // See https://golang.org/issue/22735 + /* + // Handle sparse files. + var spd sparseDatas + var spb []byte + if len(hdr.SparseHoles) > 0 { + sph := append([]sparseEntry{}, hdr.SparseHoles...) // Copy sparse map + sph = alignSparseEntries(sph, hdr.Size) + spd = invertSparseEntries(sph, hdr.Size) + + // Format the sparse map. + hdr.Size = 0 // Replace with encoded size + spb = append(strconv.AppendInt(spb, int64(len(spd)), 10), '\n') + for _, s := range spd { + hdr.Size += s.Length + spb = append(strconv.AppendInt(spb, s.Offset, 10), '\n') + spb = append(strconv.AppendInt(spb, s.Length, 10), '\n') + } + pad := blockPadding(int64(len(spb))) + spb = append(spb, zeroBlock[:pad]...) + hdr.Size += int64(len(spb)) // Accounts for encoded sparse map + + // Add and modify appropriate PAX records. + dir, file := path.Split(realName) + hdr.Name = path.Join(dir, "GNUSparseFile.0", file) + paxHdrs[paxGNUSparseMajor] = "1" + paxHdrs[paxGNUSparseMinor] = "0" + paxHdrs[paxGNUSparseName] = realName + paxHdrs[paxGNUSparseRealSize] = strconv.FormatInt(realSize, 10) + paxHdrs[paxSize] = strconv.FormatInt(hdr.Size, 10) + delete(paxHdrs, paxPath) // Recorded by paxGNUSparseName + } + */ + _ = realSize + + // Write PAX records to the output. + isGlobal := hdr.Typeflag == TypeXGlobalHeader + if len(paxHdrs) > 0 || isGlobal { + // Sort keys for deterministic ordering. + var keys []string + for k := range paxHdrs { + keys = append(keys, k) + } + sort.Strings(keys) + + // Write each record to a buffer. + var buf strings.Builder + for _, k := range keys { + rec, err := formatPAXRecord(k, paxHdrs[k]) + if err != nil { + return err + } + buf.WriteString(rec) + } + + // Write the extended header file. + var name string + var flag byte + if isGlobal { + name = realName + if name == "" { + name = "GlobalHead.0.0" + } + flag = TypeXGlobalHeader + } else { + dir, file := path.Split(realName) + name = path.Join(dir, "PaxHeaders.0", file) + flag = TypeXHeader + } + data := buf.String() + if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal { + return err // Global headers return here + } + } + + // Pack the main header. + var f formatter // Ignore errors since they are expected + fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) } + blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal) + blk.SetFormat(FormatPAX) + if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { + return err + } + + // TODO(dsnet): Re-enable this when adding sparse support. + // See https://golang.org/issue/22735 + /* + // Write the sparse map and setup the sparse writer if necessary. + if len(spd) > 0 { + // Use tw.curr since the sparse map is accounted for in hdr.Size. + if _, err := tw.curr.Write(spb); err != nil { + return err + } + tw.curr = &sparseFileWriter{tw.curr, spd, 0} + } + */ + return nil +} + +func (tw *Writer) writeGNUHeader(hdr *Header) error { + // Use long-link files if Name or Linkname exceeds the field size. + const longName = "././@LongLink" + if len(hdr.Name) > nameSize { + data := hdr.Name + "\x00" + if err := tw.writeRawFile(longName, data, TypeGNULongName, FormatGNU); err != nil { + return err + } + } + if len(hdr.Linkname) > nameSize { + data := hdr.Linkname + "\x00" + if err := tw.writeRawFile(longName, data, TypeGNULongLink, FormatGNU); err != nil { + return err + } + } + + // Pack the main header. + var f formatter // Ignore errors since they are expected + var spd sparseDatas + var spb []byte + blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric) + if !hdr.AccessTime.IsZero() { + f.formatNumeric(blk.GNU().AccessTime(), hdr.AccessTime.Unix()) + } + if !hdr.ChangeTime.IsZero() { + f.formatNumeric(blk.GNU().ChangeTime(), hdr.ChangeTime.Unix()) + } + // TODO(dsnet): Re-enable this when adding sparse support. + // See https://golang.org/issue/22735 + /* + if hdr.Typeflag == TypeGNUSparse { + sph := append([]sparseEntry{}, hdr.SparseHoles...) // Copy sparse map + sph = alignSparseEntries(sph, hdr.Size) + spd = invertSparseEntries(sph, hdr.Size) + + // Format the sparse map. + formatSPD := func(sp sparseDatas, sa sparseArray) sparseDatas { + for i := 0; len(sp) > 0 && i < sa.MaxEntries(); i++ { + f.formatNumeric(sa.Entry(i).Offset(), sp[0].Offset) + f.formatNumeric(sa.Entry(i).Length(), sp[0].Length) + sp = sp[1:] + } + if len(sp) > 0 { + sa.IsExtended()[0] = 1 + } + return sp + } + sp2 := formatSPD(spd, blk.GNU().Sparse()) + for len(sp2) > 0 { + var spHdr block + sp2 = formatSPD(sp2, spHdr.Sparse()) + spb = append(spb, spHdr[:]...) + } + + // Update size fields in the header block. + realSize := hdr.Size + hdr.Size = 0 // Encoded size; does not account for encoded sparse map + for _, s := range spd { + hdr.Size += s.Length + } + copy(blk.V7().Size(), zeroBlock[:]) // Reset field + f.formatNumeric(blk.V7().Size(), hdr.Size) + f.formatNumeric(blk.GNU().RealSize(), realSize) + } + */ + blk.SetFormat(FormatGNU) + if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { + return err + } + + // Write the extended sparse map and setup the sparse writer if necessary. + if len(spd) > 0 { + // Use tw.w since the sparse map is not accounted for in hdr.Size. + if _, err := tw.w.Write(spb); err != nil { + return err + } + tw.curr = &sparseFileWriter{tw.curr, spd, 0} + } + return nil +} + +type ( + stringFormatter func([]byte, string) + numberFormatter func([]byte, int64) +) + +// templateV7Plus fills out the V7 fields of a block using values from hdr. +// It also fills out fields (uname, gname, devmajor, devminor) that are +// shared in the USTAR, PAX, and GNU formats using the provided formatters. +// +// The block returned is only valid until the next call to +// templateV7Plus or writeRawFile. +func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block { + tw.blk.Reset() + + modTime := hdr.ModTime + if modTime.IsZero() { + modTime = time.Unix(0, 0) + } + + v7 := tw.blk.V7() + v7.TypeFlag()[0] = hdr.Typeflag + fmtStr(v7.Name(), hdr.Name) + fmtStr(v7.LinkName(), hdr.Linkname) + fmtNum(v7.Mode(), hdr.Mode) + fmtNum(v7.UID(), int64(hdr.Uid)) + fmtNum(v7.GID(), int64(hdr.Gid)) + fmtNum(v7.Size(), hdr.Size) + fmtNum(v7.ModTime(), modTime.Unix()) + + ustar := tw.blk.USTAR() + fmtStr(ustar.UserName(), hdr.Uname) + fmtStr(ustar.GroupName(), hdr.Gname) + fmtNum(ustar.DevMajor(), hdr.Devmajor) + fmtNum(ustar.DevMinor(), hdr.Devminor) + + return &tw.blk +} + +// writeRawFile writes a minimal file with the given name and flag type. +// It uses format to encode the header format and will write data as the body. +// It uses default values for all of the other fields (as BSD and GNU tar does). +func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error { + tw.blk.Reset() + + // Best effort for the filename. + name = toASCII(name) + if len(name) > nameSize { + name = name[:nameSize] + } + name = strings.TrimRight(name, "/") + + var f formatter + v7 := tw.blk.V7() + v7.TypeFlag()[0] = flag + f.formatString(v7.Name(), name) + f.formatOctal(v7.Mode(), 0) + f.formatOctal(v7.UID(), 0) + f.formatOctal(v7.GID(), 0) + f.formatOctal(v7.Size(), int64(len(data))) // Must be < 8GiB + f.formatOctal(v7.ModTime(), 0) + tw.blk.SetFormat(format) + if f.err != nil { + return f.err // Only occurs if size condition is violated + } + + // Write the header and data. + if err := tw.writeRawHeader(&tw.blk, int64(len(data)), flag); err != nil { + return err + } + _, err := io.WriteString(tw, data) + return err +} + +// writeRawHeader writes the value of blk, regardless of its value. +// It sets up the Writer such that it can accept a file of the given size. +// If the flag is a special header-only flag, then the size is treated as zero. +func (tw *Writer) writeRawHeader(blk *block, size int64, flag byte) error { + if err := tw.Flush(); err != nil { + return err + } + if _, err := tw.w.Write(blk[:]); err != nil { + return err + } + if isHeaderOnlyType(flag) { + size = 0 + } + tw.curr = ®FileWriter{tw.w, size} + tw.pad = blockPadding(size) + return nil +} + +// splitUSTARPath splits a path according to USTAR prefix and suffix rules. +// If the path is not splittable, then it will return ("", "", false). +func splitUSTARPath(name string) (prefix, suffix string, ok bool) { + length := len(name) + if length <= nameSize || !isASCII(name) { + return "", "", false + } else if length > prefixSize+1 { + length = prefixSize + 1 + } else if name[length-1] == '/' { + length-- + } + + i := strings.LastIndex(name[:length], "/") + nlen := len(name) - i - 1 // nlen is length of suffix + plen := i // plen is length of prefix + if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize { + return "", "", false + } + return name[:i], name[i+1:], true +} + +// Write writes to the current file in the tar archive. +// Write returns the error ErrWriteTooLong if more than +// Header.Size bytes are written after WriteHeader. +// +// Calling Write on special types like TypeLink, TypeSymlink, TypeChar, +// TypeBlock, TypeDir, and TypeFifo returns (0, ErrWriteTooLong) regardless +// of what the Header.Size claims. +func (tw *Writer) Write(b []byte) (int, error) { + if tw.err != nil { + return 0, tw.err + } + n, err := tw.curr.Write(b) + if err != nil && err != ErrWriteTooLong { + tw.err = err + } + return n, err +} + +// readFrom populates the content of the current file by reading from r. +// The bytes read must match the number of remaining bytes in the current file. +// +// If the current file is sparse and r is an io.ReadSeeker, +// then readFrom uses Seek to skip past holes defined in Header.SparseHoles, +// assuming that skipped regions are all NULs. +// This always reads the last byte to ensure r is the right size. +// +// TODO(dsnet): Re-export this when adding sparse file support. +// See https://golang.org/issue/22735 +func (tw *Writer) readFrom(r io.Reader) (int64, error) { + if tw.err != nil { + return 0, tw.err + } + n, err := tw.curr.ReadFrom(r) + if err != nil && err != ErrWriteTooLong { + tw.err = err + } + return n, err +} + +// Close closes the tar archive by flushing the padding, and writing the footer. +// If the current file (from a prior call to WriteHeader) is not fully written, +// then this returns an error. +func (tw *Writer) Close() error { + if tw.err == ErrWriteAfterClose { + return nil + } + if tw.err != nil { + return tw.err + } + + // Trailer: two zero blocks. + err := tw.Flush() + for i := 0; i < 2 && err == nil; i++ { + _, err = tw.w.Write(zeroBlock[:]) + } + + // Ensure all future actions are invalid. + tw.err = ErrWriteAfterClose + return err // Report IO errors +} + +// regFileWriter is a fileWriter for writing data to a regular file entry. +type regFileWriter struct { + w io.Writer // Underlying Writer + nb int64 // Number of remaining bytes to write +} + +func (fw *regFileWriter) Write(b []byte) (n int, err error) { + overwrite := int64(len(b)) > fw.nb + if overwrite { + b = b[:fw.nb] + } + if len(b) > 0 { + n, err = fw.w.Write(b) + fw.nb -= int64(n) + } + switch { + case err != nil: + return n, err + case overwrite: + return n, ErrWriteTooLong + default: + return n, nil + } +} + +func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) { + return io.Copy(struct{ io.Writer }{fw}, r) +} + +func (fw regFileWriter) LogicalRemaining() int64 { + return fw.nb +} +func (fw regFileWriter) PhysicalRemaining() int64 { + return fw.nb +} + +// sparseFileWriter is a fileWriter for writing data to a sparse file entry. +type sparseFileWriter struct { + fw fileWriter // Underlying fileWriter + sp sparseDatas // Normalized list of data fragments + pos int64 // Current position in sparse file +} + +func (sw *sparseFileWriter) Write(b []byte) (n int, err error) { + overwrite := int64(len(b)) > sw.LogicalRemaining() + if overwrite { + b = b[:sw.LogicalRemaining()] + } + + b0 := b + endPos := sw.pos + int64(len(b)) + for endPos > sw.pos && err == nil { + var nf int // Bytes written in fragment + dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() + if sw.pos < dataStart { // In a hole fragment + bf := b[:min(int64(len(b)), dataStart-sw.pos)] + nf, err = zeroWriter{}.Write(bf) + } else { // In a data fragment + bf := b[:min(int64(len(b)), dataEnd-sw.pos)] + nf, err = sw.fw.Write(bf) + } + b = b[nf:] + sw.pos += int64(nf) + if sw.pos >= dataEnd && len(sw.sp) > 1 { + sw.sp = sw.sp[1:] // Ensure last fragment always remains + } + } + + n = len(b0) - len(b) + switch { + case err == ErrWriteTooLong: + return n, errMissData // Not possible; implies bug in validation logic + case err != nil: + return n, err + case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: + return n, errUnrefData // Not possible; implies bug in validation logic + case overwrite: + return n, ErrWriteTooLong + default: + return n, nil + } +} + +func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) { + rs, ok := r.(io.ReadSeeker) + if ok { + if _, err := rs.Seek(0, io.SeekCurrent); err != nil { + ok = false // Not all io.Seeker can really seek + } + } + if !ok { + return io.Copy(struct{ io.Writer }{sw}, r) + } + + var readLastByte bool + pos0 := sw.pos + for sw.LogicalRemaining() > 0 && !readLastByte && err == nil { + var nf int64 // Size of fragment + dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() + if sw.pos < dataStart { // In a hole fragment + nf = dataStart - sw.pos + if sw.PhysicalRemaining() == 0 { + readLastByte = true + nf-- + } + _, err = rs.Seek(nf, io.SeekCurrent) + } else { // In a data fragment + nf = dataEnd - sw.pos + nf, err = io.CopyN(sw.fw, rs, nf) + } + sw.pos += nf + if sw.pos >= dataEnd && len(sw.sp) > 1 { + sw.sp = sw.sp[1:] // Ensure last fragment always remains + } + } + + // If the last fragment is a hole, then seek to 1-byte before EOF, and + // read a single byte to ensure the file is the right size. + if readLastByte && err == nil { + _, err = mustReadFull(rs, []byte{0}) + sw.pos++ + } + + n = sw.pos - pos0 + switch { + case err == io.EOF: + return n, io.ErrUnexpectedEOF + case err == ErrWriteTooLong: + return n, errMissData // Not possible; implies bug in validation logic + case err != nil: + return n, err + case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: + return n, errUnrefData // Not possible; implies bug in validation logic + default: + return n, ensureEOF(rs) + } +} + +func (sw sparseFileWriter) LogicalRemaining() int64 { + return sw.sp[len(sw.sp)-1].endOffset() - sw.pos +} +func (sw sparseFileWriter) PhysicalRemaining() int64 { + return sw.fw.PhysicalRemaining() +} + +// zeroWriter may only be written with NULs, otherwise it returns errWriteHole. +type zeroWriter struct{} + +func (zeroWriter) Write(b []byte) (int, error) { + for i, c := range b { + if c != 0 { + return i, errWriteHole + } + } + return len(b), nil +} + +// ensureEOF checks whether r is at EOF, reporting ErrWriteTooLong if not so. +func ensureEOF(r io.Reader) error { + n, err := tryReadFull(r, []byte{0}) + switch { + case n > 0: + return ErrWriteTooLong + case err == io.EOF: + return nil + default: + return err + } +} diff --git a/vendor/archive/tar/writer_test.go b/vendor/archive/tar/writer_test.go new file mode 100644 index 0000000000000..a00f02d8fab69 --- /dev/null +++ b/vendor/archive/tar/writer_test.go @@ -0,0 +1,1310 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tar + +import ( + "bytes" + "encoding/hex" + "errors" + "io" + "os" + "path" + "reflect" + "sort" + "strings" + "testing" + "testing/iotest" + "time" +) + +func bytediff(a, b []byte) string { + const ( + uniqueA = "- " + uniqueB = "+ " + identity = " " + ) + var ss []string + sa := strings.Split(strings.TrimSpace(hex.Dump(a)), "\n") + sb := strings.Split(strings.TrimSpace(hex.Dump(b)), "\n") + for len(sa) > 0 && len(sb) > 0 { + if sa[0] == sb[0] { + ss = append(ss, identity+sa[0]) + } else { + ss = append(ss, uniqueA+sa[0]) + ss = append(ss, uniqueB+sb[0]) + } + sa, sb = sa[1:], sb[1:] + } + for len(sa) > 0 { + ss = append(ss, uniqueA+sa[0]) + sa = sa[1:] + } + for len(sb) > 0 { + ss = append(ss, uniqueB+sb[0]) + sb = sb[1:] + } + return strings.Join(ss, "\n") +} + +func TestWriter(t *testing.T) { + type ( + testHeader struct { // WriteHeader(hdr) == wantErr + hdr Header + wantErr error + } + testWrite struct { // Write(str) == (wantCnt, wantErr) + str string + wantCnt int + wantErr error + } + testReadFrom struct { // ReadFrom(testFile{ops}) == (wantCnt, wantErr) + ops fileOps + wantCnt int64 + wantErr error + } + testClose struct { // Close() == wantErr + wantErr error + } + testFnc interface{} // testHeader | testWrite | testReadFrom | testClose + ) + + vectors := []struct { + file string // Optional filename of expected output + tests []testFnc + }{{ + // The writer test file was produced with this command: + // tar (GNU tar) 1.26 + // ln -s small.txt link.txt + // tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt + file: "testdata/writer.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "small.txt", + Size: 5, + Mode: 0640, + Uid: 73025, + Gid: 5000, + Uname: "dsymonds", + Gname: "eng", + ModTime: time.Unix(1246508266, 0), + }, nil}, + testWrite{"Kilts", 5, nil}, + + testHeader{Header{ + Typeflag: TypeReg, + Name: "small2.txt", + Size: 11, + Mode: 0640, + Uid: 73025, + Uname: "dsymonds", + Gname: "eng", + Gid: 5000, + ModTime: time.Unix(1245217492, 0), + }, nil}, + testWrite{"Google.com\n", 11, nil}, + + testHeader{Header{ + Typeflag: TypeSymlink, + Name: "link.txt", + Linkname: "small.txt", + Mode: 0777, + Uid: 1000, + Gid: 1000, + Uname: "strings", + Gname: "strings", + ModTime: time.Unix(1314603082, 0), + }, nil}, + testWrite{"", 0, nil}, + + testClose{nil}, + }, + }, { + // The truncated test file was produced using these commands: + // dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt + // tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar + file: "testdata/writer-big.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "tmp/16gig.txt", + Size: 16 << 30, + Mode: 0640, + Uid: 73025, + Gid: 5000, + Uname: "dsymonds", + Gname: "eng", + ModTime: time.Unix(1254699560, 0), + Format: FormatGNU, + }, nil}, + }, + }, { + // This truncated file was produced using this library. + // It was verified to work with GNU tar 1.27.1 and BSD tar 3.1.2. + // dd if=/dev/zero bs=1G count=16 >> writer-big-long.tar + // gnutar -xvf writer-big-long.tar + // bsdtar -xvf writer-big-long.tar + // + // This file is in PAX format. + file: "testdata/writer-big-long.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: strings.Repeat("longname/", 15) + "16gig.txt", + Size: 16 << 30, + Mode: 0644, + Uid: 1000, + Gid: 1000, + Uname: "guillaume", + Gname: "guillaume", + ModTime: time.Unix(1399583047, 0), + }, nil}, + }, + }, { + // This file was produced using GNU tar v1.17. + // gnutar -b 4 --format=ustar (longname/)*15 + file.txt + file: "testdata/ustar.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: strings.Repeat("longname/", 15) + "file.txt", + Size: 6, + Mode: 0644, + Uid: 501, + Gid: 20, + Uname: "shane", + Gname: "staff", + ModTime: time.Unix(1360135598, 0), + }, nil}, + testWrite{"hello\n", 6, nil}, + testClose{nil}, + }, + }, { + // This file was produced using GNU tar v1.26: + // echo "Slartibartfast" > file.txt + // ln file.txt hard.txt + // tar -b 1 --format=ustar -c -f hardlink.tar file.txt hard.txt + file: "testdata/hardlink.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "file.txt", + Size: 15, + Mode: 0644, + Uid: 1000, + Gid: 100, + Uname: "vbatts", + Gname: "users", + ModTime: time.Unix(1425484303, 0), + }, nil}, + testWrite{"Slartibartfast\n", 15, nil}, + + testHeader{Header{ + Typeflag: TypeLink, + Name: "hard.txt", + Linkname: "file.txt", + Mode: 0644, + Uid: 1000, + Gid: 100, + Uname: "vbatts", + Gname: "users", + ModTime: time.Unix(1425484303, 0), + }, nil}, + testWrite{"", 0, nil}, + + testClose{nil}, + }, + }, { + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "bad-null.txt", + Xattrs: map[string]string{"null\x00null\x00": "fizzbuzz"}, + }, headerError{}}, + }, + }, { + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "null\x00.txt", + }, headerError{}}, + }, + }, { + file: "testdata/pax-records.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "file", + Uname: strings.Repeat("long", 10), + PAXRecords: map[string]string{ + "path": "FILE", // Should be ignored + "GNU.sparse.map": "0,0", // Should be ignored + "comment": "Hello, 世界", + "GOLANG.pkg": "tar", + }, + }, nil}, + testClose{nil}, + }, + }, { + // Craft a theoretically valid PAX archive with global headers. + // The GNU and BSD tar tools do not parse these the same way. + // + // BSD tar v3.1.2 parses and ignores all global headers; + // the behavior is verified by researching the source code. + // + // $ bsdtar -tvf pax-global-records.tar + // ---------- 0 0 0 0 Dec 31 1969 file1 + // ---------- 0 0 0 0 Dec 31 1969 file2 + // ---------- 0 0 0 0 Dec 31 1969 file3 + // ---------- 0 0 0 0 May 13 2014 file4 + // + // GNU tar v1.27.1 applies global headers to subsequent records, + // but does not do the following properly: + // * It does not treat an empty record as deletion. + // * It does not use subsequent global headers to update previous ones. + // + // $ gnutar -tvf pax-global-records.tar + // ---------- 0/0 0 2017-07-13 19:40 global1 + // ---------- 0/0 0 2017-07-13 19:40 file2 + // gnutar: Substituting `.' for empty member name + // ---------- 0/0 0 1969-12-31 16:00 + // gnutar: Substituting `.' for empty member name + // ---------- 0/0 0 2014-05-13 09:53 + // + // According to the PAX specification, this should have been the result: + // ---------- 0/0 0 2017-07-13 19:40 global1 + // ---------- 0/0 0 2017-07-13 19:40 file2 + // ---------- 0/0 0 2017-07-13 19:40 file3 + // ---------- 0/0 0 2014-05-13 09:53 file4 + file: "testdata/pax-global-records.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeXGlobalHeader, + PAXRecords: map[string]string{"path": "global1", "mtime": "1500000000.0"}, + }, nil}, + testHeader{Header{ + Typeflag: TypeReg, Name: "file1", + }, nil}, + testHeader{Header{ + Typeflag: TypeReg, + Name: "file2", + PAXRecords: map[string]string{"path": "file2"}, + }, nil}, + testHeader{Header{ + Typeflag: TypeXGlobalHeader, + PAXRecords: map[string]string{"path": ""}, // Should delete "path", but keep "mtime" + }, nil}, + testHeader{Header{ + Typeflag: TypeReg, Name: "file3", + }, nil}, + testHeader{Header{ + Typeflag: TypeReg, + Name: "file4", + ModTime: time.Unix(1400000000, 0), + PAXRecords: map[string]string{"mtime": "1400000000"}, + }, nil}, + testClose{nil}, + }, + }, { + file: "testdata/gnu-utf8.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹☺☻☹", + Mode: 0644, + Uid: 1000, Gid: 1000, + Uname: "☺", + Gname: "⚹", + ModTime: time.Unix(0, 0), + Format: FormatGNU, + }, nil}, + testClose{nil}, + }, + }, { + file: "testdata/gnu-not-utf8.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "hi\x80\x81\x82\x83bye", + Mode: 0644, + Uid: 1000, + Gid: 1000, + Uname: "rawr", + Gname: "dsnet", + ModTime: time.Unix(0, 0), + Format: FormatGNU, + }, nil}, + testClose{nil}, + }, + // TODO(dsnet): Re-enable this test when adding sparse support. + // See https://golang.org/issue/22735 + /* + }, { + file: "testdata/gnu-nil-sparse-data.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeGNUSparse, + Name: "sparse.db", + Size: 1000, + SparseHoles: []sparseEntry{{Offset: 1000, Length: 0}}, + }, nil}, + testWrite{strings.Repeat("0123456789", 100), 1000, nil}, + testClose{}, + }, + }, { + file: "testdata/gnu-nil-sparse-hole.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeGNUSparse, + Name: "sparse.db", + Size: 1000, + SparseHoles: []sparseEntry{{Offset: 0, Length: 1000}}, + }, nil}, + testWrite{strings.Repeat("\x00", 1000), 1000, nil}, + testClose{}, + }, + }, { + file: "testdata/pax-nil-sparse-data.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "sparse.db", + Size: 1000, + SparseHoles: []sparseEntry{{Offset: 1000, Length: 0}}, + }, nil}, + testWrite{strings.Repeat("0123456789", 100), 1000, nil}, + testClose{}, + }, + }, { + file: "testdata/pax-nil-sparse-hole.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "sparse.db", + Size: 1000, + SparseHoles: []sparseEntry{{Offset: 0, Length: 1000}}, + }, nil}, + testWrite{strings.Repeat("\x00", 1000), 1000, nil}, + testClose{}, + }, + }, { + file: "testdata/gnu-sparse-big.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeGNUSparse, + Name: "gnu-sparse", + Size: 6e10, + SparseHoles: []sparseEntry{ + {Offset: 0e10, Length: 1e10 - 100}, + {Offset: 1e10, Length: 1e10 - 100}, + {Offset: 2e10, Length: 1e10 - 100}, + {Offset: 3e10, Length: 1e10 - 100}, + {Offset: 4e10, Length: 1e10 - 100}, + {Offset: 5e10, Length: 1e10 - 100}, + }, + }, nil}, + testReadFrom{fileOps{ + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + }, 6e10, nil}, + testClose{nil}, + }, + }, { + file: "testdata/pax-sparse-big.tar", + tests: []testFnc{ + testHeader{Header{ + Typeflag: TypeReg, + Name: "pax-sparse", + Size: 6e10, + SparseHoles: []sparseEntry{ + {Offset: 0e10, Length: 1e10 - 100}, + {Offset: 1e10, Length: 1e10 - 100}, + {Offset: 2e10, Length: 1e10 - 100}, + {Offset: 3e10, Length: 1e10 - 100}, + {Offset: 4e10, Length: 1e10 - 100}, + {Offset: 5e10, Length: 1e10 - 100}, + }, + }, nil}, + testReadFrom{fileOps{ + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + int64(1e10 - blockSize), + strings.Repeat("\x00", blockSize-100) + strings.Repeat("0123456789", 10), + }, 6e10, nil}, + testClose{nil}, + }, + */ + }, { + file: "testdata/trailing-slash.tar", + tests: []testFnc{ + testHeader{Header{Name: strings.Repeat("123456789/", 30)}, nil}, + testClose{nil}, + }, + }, { + // Automatically promote zero value of Typeflag depending on the name. + file: "testdata/file-and-dir.tar", + tests: []testFnc{ + testHeader{Header{Name: "small.txt", Size: 5}, nil}, + testWrite{"Kilts", 5, nil}, + testHeader{Header{Name: "dir/"}, nil}, + testClose{nil}, + }, + }} + + equalError := func(x, y error) bool { + _, ok1 := x.(headerError) + _, ok2 := y.(headerError) + if ok1 || ok2 { + return ok1 && ok2 + } + return x == y + } + for _, v := range vectors { + t.Run(path.Base(v.file), func(t *testing.T) { + const maxSize = 10 << 10 // 10KiB + buf := new(bytes.Buffer) + tw := NewWriter(iotest.TruncateWriter(buf, maxSize)) + + for i, tf := range v.tests { + switch tf := tf.(type) { + case testHeader: + err := tw.WriteHeader(&tf.hdr) + if !equalError(err, tf.wantErr) { + t.Fatalf("test %d, WriteHeader() = %v, want %v", i, err, tf.wantErr) + } + case testWrite: + got, err := tw.Write([]byte(tf.str)) + if got != tf.wantCnt || !equalError(err, tf.wantErr) { + t.Fatalf("test %d, Write() = (%d, %v), want (%d, %v)", i, got, err, tf.wantCnt, tf.wantErr) + } + case testReadFrom: + f := &testFile{ops: tf.ops} + got, err := tw.readFrom(f) + if _, ok := err.(testError); ok { + t.Errorf("test %d, ReadFrom(): %v", i, err) + } else if got != tf.wantCnt || !equalError(err, tf.wantErr) { + t.Errorf("test %d, ReadFrom() = (%d, %v), want (%d, %v)", i, got, err, tf.wantCnt, tf.wantErr) + } + if len(f.ops) > 0 { + t.Errorf("test %d, expected %d more operations", i, len(f.ops)) + } + case testClose: + err := tw.Close() + if !equalError(err, tf.wantErr) { + t.Fatalf("test %d, Close() = %v, want %v", i, err, tf.wantErr) + } + default: + t.Fatalf("test %d, unknown test operation: %T", i, tf) + } + } + + if v.file != "" { + want, err := os.ReadFile(v.file) + if err != nil { + t.Fatalf("ReadFile() = %v, want nil", err) + } + got := buf.Bytes() + if !bytes.Equal(want, got) { + t.Fatalf("incorrect result: (-got +want)\n%v", bytediff(got, want)) + } + } + }) + } +} + +func TestPax(t *testing.T) { + // Create an archive with a large name + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + hdr, err := FileInfoHeader(fileinfo, "") + if err != nil { + t.Fatalf("os.Stat: %v", err) + } + // Force a PAX long name to be written + longName := strings.Repeat("ab", 100) + contents := strings.Repeat(" ", int(hdr.Size)) + hdr.Name = longName + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if _, err = writer.Write([]byte(contents)); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Simple test to make sure PAX extensions are in effect + if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { + t.Fatal("Expected at least one PAX header to be written.") + } + // Test that we can get a long name back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if hdr.Name != longName { + t.Fatal("Couldn't recover long file name") + } +} + +func TestPaxSymlink(t *testing.T) { + // Create an archive with a large linkname + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + hdr, err := FileInfoHeader(fileinfo, "") + hdr.Typeflag = TypeSymlink + if err != nil { + t.Fatalf("os.Stat:1 %v", err) + } + // Force a PAX long linkname to be written + longLinkname := strings.Repeat("1234567890/1234567890", 10) + hdr.Linkname = longLinkname + + hdr.Size = 0 + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Simple test to make sure PAX extensions are in effect + if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { + t.Fatal("Expected at least one PAX header to be written.") + } + // Test that we can get a long name back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if hdr.Linkname != longLinkname { + t.Fatal("Couldn't recover long link name") + } +} + +func TestPaxNonAscii(t *testing.T) { + // Create an archive with non ascii. These should trigger a pax header + // because pax headers have a defined utf-8 encoding. + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + + hdr, err := FileInfoHeader(fileinfo, "") + if err != nil { + t.Fatalf("os.Stat:1 %v", err) + } + + // some sample data + chineseFilename := "文件名" + chineseGroupname := "組" + chineseUsername := "用戶名" + + hdr.Name = chineseFilename + hdr.Gname = chineseGroupname + hdr.Uname = chineseUsername + + contents := strings.Repeat(" ", int(hdr.Size)) + + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if _, err = writer.Write([]byte(contents)); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Simple test to make sure PAX extensions are in effect + if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { + t.Fatal("Expected at least one PAX header to be written.") + } + // Test that we can get a long name back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if hdr.Name != chineseFilename { + t.Fatal("Couldn't recover unicode name") + } + if hdr.Gname != chineseGroupname { + t.Fatal("Couldn't recover unicode group") + } + if hdr.Uname != chineseUsername { + t.Fatal("Couldn't recover unicode user") + } +} + +func TestPaxXattrs(t *testing.T) { + xattrs := map[string]string{ + "user.key": "value", + } + + // Create an archive with an xattr + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + hdr, err := FileInfoHeader(fileinfo, "") + if err != nil { + t.Fatalf("os.Stat: %v", err) + } + contents := "Kilts" + hdr.Xattrs = xattrs + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if _, err = writer.Write([]byte(contents)); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Test that we can get the xattrs back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(hdr.Xattrs, xattrs) { + t.Fatalf("xattrs did not survive round trip: got %+v, want %+v", + hdr.Xattrs, xattrs) + } +} + +func TestPaxHeadersSorted(t *testing.T) { + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + hdr, err := FileInfoHeader(fileinfo, "") + if err != nil { + t.Fatalf("os.Stat: %v", err) + } + contents := strings.Repeat(" ", int(hdr.Size)) + + hdr.Xattrs = map[string]string{ + "foo": "foo", + "bar": "bar", + "baz": "baz", + "qux": "qux", + } + + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if _, err = writer.Write([]byte(contents)); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Simple test to make sure PAX extensions are in effect + if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.0")) { + t.Fatal("Expected at least one PAX header to be written.") + } + + // xattr bar should always appear before others + indices := []int{ + bytes.Index(buf.Bytes(), []byte("bar=bar")), + bytes.Index(buf.Bytes(), []byte("baz=baz")), + bytes.Index(buf.Bytes(), []byte("foo=foo")), + bytes.Index(buf.Bytes(), []byte("qux=qux")), + } + if !sort.IntsAreSorted(indices) { + t.Fatal("PAX headers are not sorted") + } +} + +func TestUSTARLongName(t *testing.T) { + // Create an archive with a path that failed to split with USTAR extension in previous versions. + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + hdr, err := FileInfoHeader(fileinfo, "") + hdr.Typeflag = TypeDir + if err != nil { + t.Fatalf("os.Stat:1 %v", err) + } + // Force a PAX long name to be written. The name was taken from a practical example + // that fails and replaced ever char through numbers to anonymize the sample. + longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/" + hdr.Name = longName + + hdr.Size = 0 + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Test that we can get a long name back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if hdr.Name != longName { + t.Fatal("Couldn't recover long name") + } +} + +func TestValidTypeflagWithPAXHeader(t *testing.T) { + var buffer bytes.Buffer + tw := NewWriter(&buffer) + + fileName := strings.Repeat("ab", 100) + + hdr := &Header{ + Name: fileName, + Size: 4, + Typeflag: 0, + } + if err := tw.WriteHeader(hdr); err != nil { + t.Fatalf("Failed to write header: %s", err) + } + if _, err := tw.Write([]byte("fooo")); err != nil { + t.Fatalf("Failed to write the file's data: %s", err) + } + tw.Close() + + tr := NewReader(&buffer) + + for { + header, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("Failed to read header: %s", err) + } + if header.Typeflag != TypeReg { + t.Fatalf("Typeflag should've been %d, found %d", TypeReg, header.Typeflag) + } + } +} + +// failOnceWriter fails exactly once and then always reports success. +type failOnceWriter bool + +func (w *failOnceWriter) Write(b []byte) (int, error) { + if !*w { + return 0, io.ErrShortWrite + } + *w = true + return len(b), nil +} + +func TestWriterErrors(t *testing.T) { + t.Run("HeaderOnly", func(t *testing.T) { + tw := NewWriter(new(bytes.Buffer)) + hdr := &Header{Name: "dir/", Typeflag: TypeDir} + if err := tw.WriteHeader(hdr); err != nil { + t.Fatalf("WriteHeader() = %v, want nil", err) + } + if _, err := tw.Write([]byte{0x00}); err != ErrWriteTooLong { + t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong) + } + }) + + t.Run("NegativeSize", func(t *testing.T) { + tw := NewWriter(new(bytes.Buffer)) + hdr := &Header{Name: "small.txt", Size: -1} + if err := tw.WriteHeader(hdr); err == nil { + t.Fatalf("WriteHeader() = nil, want non-nil error") + } + }) + + t.Run("BeforeHeader", func(t *testing.T) { + tw := NewWriter(new(bytes.Buffer)) + if _, err := tw.Write([]byte("Kilts")); err != ErrWriteTooLong { + t.Fatalf("Write() = %v, want %v", err, ErrWriteTooLong) + } + }) + + t.Run("AfterClose", func(t *testing.T) { + tw := NewWriter(new(bytes.Buffer)) + hdr := &Header{Name: "small.txt"} + if err := tw.WriteHeader(hdr); err != nil { + t.Fatalf("WriteHeader() = %v, want nil", err) + } + if err := tw.Close(); err != nil { + t.Fatalf("Close() = %v, want nil", err) + } + if _, err := tw.Write([]byte("Kilts")); err != ErrWriteAfterClose { + t.Fatalf("Write() = %v, want %v", err, ErrWriteAfterClose) + } + if err := tw.Flush(); err != ErrWriteAfterClose { + t.Fatalf("Flush() = %v, want %v", err, ErrWriteAfterClose) + } + if err := tw.Close(); err != nil { + t.Fatalf("Close() = %v, want nil", err) + } + }) + + t.Run("PrematureFlush", func(t *testing.T) { + tw := NewWriter(new(bytes.Buffer)) + hdr := &Header{Name: "small.txt", Size: 5} + if err := tw.WriteHeader(hdr); err != nil { + t.Fatalf("WriteHeader() = %v, want nil", err) + } + if err := tw.Flush(); err == nil { + t.Fatalf("Flush() = %v, want non-nil error", err) + } + }) + + t.Run("PrematureClose", func(t *testing.T) { + tw := NewWriter(new(bytes.Buffer)) + hdr := &Header{Name: "small.txt", Size: 5} + if err := tw.WriteHeader(hdr); err != nil { + t.Fatalf("WriteHeader() = %v, want nil", err) + } + if err := tw.Close(); err == nil { + t.Fatalf("Close() = %v, want non-nil error", err) + } + }) + + t.Run("Persistence", func(t *testing.T) { + tw := NewWriter(new(failOnceWriter)) + if err := tw.WriteHeader(&Header{}); err != io.ErrShortWrite { + t.Fatalf("WriteHeader() = %v, want %v", err, io.ErrShortWrite) + } + if err := tw.WriteHeader(&Header{Name: "small.txt"}); err == nil { + t.Errorf("WriteHeader() = got %v, want non-nil error", err) + } + if _, err := tw.Write(nil); err == nil { + t.Errorf("Write() = %v, want non-nil error", err) + } + if err := tw.Flush(); err == nil { + t.Errorf("Flush() = %v, want non-nil error", err) + } + if err := tw.Close(); err == nil { + t.Errorf("Close() = %v, want non-nil error", err) + } + }) +} + +func TestSplitUSTARPath(t *testing.T) { + sr := strings.Repeat + + vectors := []struct { + input string // Input path + prefix string // Expected output prefix + suffix string // Expected output suffix + ok bool // Split success? + }{ + {"", "", "", false}, + {"abc", "", "", false}, + {"用戶名", "", "", false}, + {sr("a", nameSize), "", "", false}, + {sr("a", nameSize) + "/", "", "", false}, + {sr("a", nameSize) + "/a", sr("a", nameSize), "a", true}, + {sr("a", prefixSize) + "/", "", "", false}, + {sr("a", prefixSize) + "/a", sr("a", prefixSize), "a", true}, + {sr("a", nameSize+1), "", "", false}, + {sr("/", nameSize+1), sr("/", nameSize-1), "/", true}, + {sr("a", prefixSize) + "/" + sr("b", nameSize), + sr("a", prefixSize), sr("b", nameSize), true}, + {sr("a", prefixSize) + "//" + sr("b", nameSize), "", "", false}, + {sr("a/", nameSize), sr("a/", 77) + "a", sr("a/", 22), true}, + } + + for _, v := range vectors { + prefix, suffix, ok := splitUSTARPath(v.input) + if prefix != v.prefix || suffix != v.suffix || ok != v.ok { + t.Errorf("splitUSTARPath(%q):\ngot (%q, %q, %v)\nwant (%q, %q, %v)", + v.input, prefix, suffix, ok, v.prefix, v.suffix, v.ok) + } + } +} + +// TestIssue12594 tests that the Writer does not attempt to populate the prefix +// field when encoding a header in the GNU format. The prefix field is valid +// in USTAR and PAX, but not GNU. +func TestIssue12594(t *testing.T) { + names := []string{ + "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/file.txt", + "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/file.txt", + "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/333/file.txt", + "0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/file.txt", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000/file.txt", + "/home/support/.openoffice.org/3/user/uno_packages/cache/registry/com.sun.star.comp.deployment.executable.PackageRegistryBackend", + } + + for i, name := range names { + var b bytes.Buffer + + tw := NewWriter(&b) + if err := tw.WriteHeader(&Header{ + Name: name, + Uid: 1 << 25, // Prevent USTAR format + }); err != nil { + t.Errorf("test %d, unexpected WriteHeader error: %v", i, err) + } + if err := tw.Close(); err != nil { + t.Errorf("test %d, unexpected Close error: %v", i, err) + } + + // The prefix field should never appear in the GNU format. + var blk block + copy(blk[:], b.Bytes()) + prefix := string(blk.USTAR().Prefix()) + if i := strings.IndexByte(prefix, 0); i >= 0 { + prefix = prefix[:i] // Truncate at the NUL terminator + } + if blk.GetFormat() == FormatGNU && len(prefix) > 0 && strings.HasPrefix(name, prefix) { + t.Errorf("test %d, found prefix in GNU format: %s", i, prefix) + } + + tr := NewReader(&b) + hdr, err := tr.Next() + if err != nil { + t.Errorf("test %d, unexpected Next error: %v", i, err) + } + if hdr.Name != name { + t.Errorf("test %d, hdr.Name = %s, want %s", i, hdr.Name, name) + } + } +} + +// testNonEmptyWriter wraps an io.Writer and ensures that +// Write is never called with an empty buffer. +type testNonEmptyWriter struct{ io.Writer } + +func (w testNonEmptyWriter) Write(b []byte) (int, error) { + if len(b) == 0 { + return 0, errors.New("unexpected empty Write call") + } + return w.Writer.Write(b) +} + +func TestFileWriter(t *testing.T) { + type ( + testWrite struct { // Write(str) == (wantCnt, wantErr) + str string + wantCnt int + wantErr error + } + testReadFrom struct { // ReadFrom(testFile{ops}) == (wantCnt, wantErr) + ops fileOps + wantCnt int64 + wantErr error + } + testRemaining struct { // LogicalRemaining() == wantLCnt, PhysicalRemaining() == wantPCnt + wantLCnt int64 + wantPCnt int64 + } + testFnc interface{} // testWrite | testReadFrom | testRemaining + ) + + type ( + makeReg struct { + size int64 + wantStr string + } + makeSparse struct { + makeReg makeReg + sph sparseHoles + size int64 + } + fileMaker interface{} // makeReg | makeSparse + ) + + vectors := []struct { + maker fileMaker + tests []testFnc + }{{ + maker: makeReg{0, ""}, + tests: []testFnc{ + testRemaining{0, 0}, + testWrite{"", 0, nil}, + testWrite{"a", 0, ErrWriteTooLong}, + testReadFrom{fileOps{""}, 0, nil}, + testReadFrom{fileOps{"a"}, 0, ErrWriteTooLong}, + testRemaining{0, 0}, + }, + }, { + maker: makeReg{1, "a"}, + tests: []testFnc{ + testRemaining{1, 1}, + testWrite{"", 0, nil}, + testWrite{"a", 1, nil}, + testWrite{"bcde", 0, ErrWriteTooLong}, + testWrite{"", 0, nil}, + testReadFrom{fileOps{""}, 0, nil}, + testReadFrom{fileOps{"a"}, 0, ErrWriteTooLong}, + testRemaining{0, 0}, + }, + }, { + maker: makeReg{5, "hello"}, + tests: []testFnc{ + testRemaining{5, 5}, + testWrite{"hello", 5, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeReg{5, "\x00\x00\x00\x00\x00"}, + tests: []testFnc{ + testRemaining{5, 5}, + testReadFrom{fileOps{"\x00\x00\x00\x00\x00"}, 5, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeReg{5, "\x00\x00\x00\x00\x00"}, + tests: []testFnc{ + testRemaining{5, 5}, + testReadFrom{fileOps{"\x00\x00\x00\x00\x00extra"}, 5, ErrWriteTooLong}, + testRemaining{0, 0}, + }, + }, { + maker: makeReg{5, "abc\x00\x00"}, + tests: []testFnc{ + testRemaining{5, 5}, + testWrite{"abc", 3, nil}, + testRemaining{2, 2}, + testReadFrom{fileOps{"\x00\x00"}, 2, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeReg{5, "\x00\x00abc"}, + tests: []testFnc{ + testRemaining{5, 5}, + testWrite{"\x00\x00", 2, nil}, + testRemaining{3, 3}, + testWrite{"abc", 3, nil}, + testReadFrom{fileOps{"z"}, 0, ErrWriteTooLong}, + testWrite{"z", 0, ErrWriteTooLong}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testRemaining{8, 5}, + testWrite{"ab\x00\x00\x00cde", 8, nil}, + testWrite{"a", 0, ErrWriteTooLong}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testWrite{"ab\x00\x00\x00cdez", 8, ErrWriteTooLong}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testWrite{"ab\x00", 3, nil}, + testRemaining{5, 3}, + testWrite{"\x00\x00cde", 5, nil}, + testWrite{"a", 0, ErrWriteTooLong}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testWrite{"ab", 2, nil}, + testRemaining{6, 3}, + testReadFrom{fileOps{int64(3), "cde"}, 6, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testReadFrom{fileOps{"ab", int64(3), "cde"}, 8, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{5, "abcde"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testReadFrom{fileOps{"ab", int64(3), "cdeX"}, 8, ErrWriteTooLong}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testReadFrom{fileOps{"ab", int64(3), "cd"}, 7, io.ErrUnexpectedEOF}, + testRemaining{1, 0}, + }, + }, { + maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testReadFrom{fileOps{"ab", int64(3), "cde"}, 7, errMissData}, + testRemaining{1, 0}, + }, + }, { + maker: makeSparse{makeReg{6, "abcde"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testReadFrom{fileOps{"ab", int64(3), "cde"}, 8, errUnrefData}, + testRemaining{0, 1}, + }, + }, { + maker: makeSparse{makeReg{4, "abcd"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testWrite{"ab", 2, nil}, + testRemaining{6, 2}, + testWrite{"\x00\x00\x00", 3, nil}, + testRemaining{3, 2}, + testWrite{"cde", 2, errMissData}, + testRemaining{1, 0}, + }, + }, { + maker: makeSparse{makeReg{6, "abcde"}, sparseHoles{{2, 3}}, 8}, + tests: []testFnc{ + testWrite{"ab", 2, nil}, + testRemaining{6, 4}, + testWrite{"\x00\x00\x00", 3, nil}, + testRemaining{3, 4}, + testWrite{"cde", 3, errUnrefData}, + testRemaining{0, 1}, + }, + }, { + maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, + tests: []testFnc{ + testRemaining{7, 3}, + testWrite{"\x00\x00abc\x00\x00", 7, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, + tests: []testFnc{ + testRemaining{7, 3}, + testReadFrom{fileOps{int64(2), "abc", int64(1), "\x00"}, 7, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{3, ""}, sparseHoles{{0, 2}, {5, 2}}, 7}, + tests: []testFnc{ + testWrite{"abcdefg", 0, errWriteHole}, + }, + }, { + maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, + tests: []testFnc{ + testWrite{"\x00\x00abcde", 5, errWriteHole}, + }, + }, { + maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, + tests: []testFnc{ + testWrite{"\x00\x00abc\x00\x00z", 7, ErrWriteTooLong}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{3, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, + tests: []testFnc{ + testWrite{"\x00\x00", 2, nil}, + testRemaining{5, 3}, + testWrite{"abc", 3, nil}, + testRemaining{2, 0}, + testWrite{"\x00\x00", 2, nil}, + testRemaining{0, 0}, + }, + }, { + maker: makeSparse{makeReg{2, "ab"}, sparseHoles{{0, 2}, {5, 2}}, 7}, + tests: []testFnc{ + testWrite{"\x00\x00", 2, nil}, + testWrite{"abc", 2, errMissData}, + testWrite{"\x00\x00", 0, errMissData}, + }, + }, { + maker: makeSparse{makeReg{4, "abc"}, sparseHoles{{0, 2}, {5, 2}}, 7}, + tests: []testFnc{ + testWrite{"\x00\x00", 2, nil}, + testWrite{"abc", 3, nil}, + testWrite{"\x00\x00", 2, errUnrefData}, + }, + }} + + for i, v := range vectors { + var wantStr string + bb := new(bytes.Buffer) + w := testNonEmptyWriter{bb} + var fw fileWriter + switch maker := v.maker.(type) { + case makeReg: + fw = ®FileWriter{w, maker.size} + wantStr = maker.wantStr + case makeSparse: + if !validateSparseEntries(maker.sph, maker.size) { + t.Fatalf("invalid sparse map: %v", maker.sph) + } + spd := invertSparseEntries(maker.sph, maker.size) + fw = ®FileWriter{w, maker.makeReg.size} + fw = &sparseFileWriter{fw, spd, 0} + wantStr = maker.makeReg.wantStr + default: + t.Fatalf("test %d, unknown make operation: %T", i, maker) + } + + for j, tf := range v.tests { + switch tf := tf.(type) { + case testWrite: + got, err := fw.Write([]byte(tf.str)) + if got != tf.wantCnt || err != tf.wantErr { + t.Errorf("test %d.%d, Write(%s):\ngot (%d, %v)\nwant (%d, %v)", i, j, tf.str, got, err, tf.wantCnt, tf.wantErr) + } + case testReadFrom: + f := &testFile{ops: tf.ops} + got, err := fw.ReadFrom(f) + if _, ok := err.(testError); ok { + t.Errorf("test %d.%d, ReadFrom(): %v", i, j, err) + } else if got != tf.wantCnt || err != tf.wantErr { + t.Errorf("test %d.%d, ReadFrom() = (%d, %v), want (%d, %v)", i, j, got, err, tf.wantCnt, tf.wantErr) + } + if len(f.ops) > 0 { + t.Errorf("test %d.%d, expected %d more operations", i, j, len(f.ops)) + } + case testRemaining: + if got := fw.LogicalRemaining(); got != tf.wantLCnt { + t.Errorf("test %d.%d, LogicalRemaining() = %d, want %d", i, j, got, tf.wantLCnt) + } + if got := fw.PhysicalRemaining(); got != tf.wantPCnt { + t.Errorf("test %d.%d, PhysicalRemaining() = %d, want %d", i, j, got, tf.wantPCnt) + } + default: + t.Fatalf("test %d.%d, unknown test operation: %T", i, j, tf) + } + } + + if got := bb.String(); got != wantStr { + t.Fatalf("test %d, String() = %q, want %q", i, got, wantStr) + } + } +} diff --git a/vendor/cloud.google.com/go/README.md b/vendor/cloud.google.com/go/README.md index ac66000bc74b6..f3a0c3c5e8fc3 100644 --- a/vendor/cloud.google.com/go/README.md +++ b/vendor/cloud.google.com/go/README.md @@ -8,7 +8,7 @@ Go packages for [Google Cloud Platform](https://cloud.google.com) services. import "cloud.google.com/go" ``` -To install the packages on your system, +To install the packages on your system, *do not clone the repo*. Instead use ``` $ go get -u cloud.google.com/go/... @@ -19,263 +19,44 @@ make backwards-incompatible changes. **NOTE:** Github repo is a mirror of [https://code.googlesource.com/gocloud](https://code.googlesource.com/gocloud). - * [News](#news) - * [Supported APIs](#supported-apis) - * [Go Versions Supported](#go-versions-supported) - * [Authorization](#authorization) - * [Cloud Datastore](#cloud-datastore-) - * [Cloud Storage](#cloud-storage-) - * [Cloud Pub/Sub](#cloud-pub-sub-) - * [Cloud BigQuery](#cloud-bigquery-) - * [Stackdriver Logging](#stackdriver-logging-) - * [Cloud Spanner](#cloud-spanner-) - - -## News - -_May 18, 2018_ - -*v0.23.0* - -- bigquery: Add DDL stats to query statistics. -- bigtable: - - cbt: Add cells-per-column limit for row lookup. - - cbt: Make it possible to combine read filters. -- dlp: v2beta2 client removed. Use the v2 client instead. -- firestore, spanner: Fix compilation errors due to protobuf changes. - -_May 8, 2018_ - -*v0.22.0* - -- bigtable: - - cbt: Support cells per column limit for row read. - - bttest: Correctly handle empty RowSet. - - Fix ReadModifyWrite operation in emulator. - - Fix API path in GetCluster. - -- bigquery: - - BEHAVIOR CHANGE: Retry on 503 status code. - - Add dataset.DeleteWithContents. - - Add SchemaUpdateOptions for query jobs. - - Add Timeline to QueryStatistics. - - Add more stats to ExplainQueryStage. - - Support Parquet data format. - -- datastore: - - Support omitempty for times. - -- dlp: - - **BREAKING CHANGE:** Remove v1beta1 client. Please migrate to the v2 client, - which is now out of beta. - - Add v2 client. - -- firestore: - - BEHAVIOR CHANGE: Treat set({}, MergeAll) as valid. - -- iam: - - Support JWT signing via SignJwt callopt. - -- profiler: - - BEHAVIOR CHANGE: PollForSerialOutput returns an error when context.Done. - - BEHAVIOR CHANGE: Increase the initial backoff to 1 minute. - - Avoid returning empty serial port output. - -- pubsub: - - BEHAVIOR CHANGE: Don't backoff during next retryable error once stream is healthy. - - BEHAVIOR CHANGE: Don't backoff on EOF. - - pstest: Support Acknowledge and ModifyAckDeadline RPCs. - -- redis: - - Add v1 beta Redis client. - -- spanner: - - Support SessionLabels. - -- speech: - - Add api v1 beta1 client. - -- storage: - - BEHAVIOR CHANGE: Retry reads when retryable error occurs. - - Fix delete of object in requester-pays bucket. - - Support KMS integration. - -_April 9, 2018_ - -*v0.21.0* - -- bigquery: - - Add OpenCensus tracing. - -- firestore: - - **BREAKING CHANGE:** If a document does not exist, return a DocumentSnapshot - whose Exists method returns false. DocumentRef.Get and Transaction.Get - return the non-nil DocumentSnapshot in addition to a NotFound error. - **DocumentRef.GetAll and Transaction.GetAll return a non-nil - DocumentSnapshot instead of nil.** - - Add DocumentIterator.Stop. **Call Stop whenever you are done with a - DocumentIterator.** - - Added Query.Snapshots and DocumentRef.Snapshots, which provide realtime - notification of updates. See https://cloud.google.com/firestore/docs/query-data/listen. - - Canceling an RPC now always returns a grpc.Status with codes.Canceled. - -- spanner: - - Add `CommitTimestamp`, which supports inserting the commit timestamp of a - transaction into a column. - -_March 22, 2018_ - -*v0.20.0* - -- bigquery: Support SchemaUpdateOptions for load jobs. - -- bigtable: - - Add SampleRowKeys. - - cbt: Support union, intersection GCPolicy. - - Retry admin RPCS. - - Add trace spans to retries. - -- datastore: Add OpenCensus tracing. - -- firestore: - - Fix queries involving Null and NaN. - - Allow Timestamp protobuffers for time values. - -- logging: Add a WriteTimeout option. - -- spanner: Support Batch API. - -- storage: Add OpenCensus tracing. - - -_February 26, 2018_ - -*v0.19.0* - -- bigquery: - - Support customer-managed encryption keys. - -- bigtable: - - Improved emulator support. - - Support GetCluster. - -- datastore: - - Add general mutations. - - Support pointer struct fields. - - Support transaction options. - -- firestore: - - Add Transaction.GetAll. - - Support document cursors. - -- logging: - - Support concurrent RPCs to the service. - - Support per-entry resources. - -- profiler: - - Add config options to disable heap and thread profiling. - - Read the project ID from $GOOGLE_CLOUD_PROJECT when it's set. - -- pubsub: - - BEHAVIOR CHANGE: Release flow control after ack/nack (instead of after the - callback returns). - - Add SubscriptionInProject. - - Add OpenCensus instrumentation for streaming pull. - -- storage: - - Support CORS. - - -_January 18, 2018_ - -*v0.18.0* - -- bigquery: - - Marked stable. - - Schema inference of nullable fields supported. - - Added TimePartitioning to QueryConfig. - -- firestore: Data provided to DocumentRef.Set with a Merge option can contain - Delete sentinels. - -- logging: Clients can accept parent resources other than projects. - -- pubsub: - - pubsub/pstest: A lighweight fake for pubsub. Experimental; feedback welcome. - - Support updating more subscription metadata: AckDeadline, - RetainAckedMessages and RetentionDuration. - -- oslogin/apiv1beta: New client for the Cloud OS Login API. - -- rpcreplay: A package for recording and replaying gRPC traffic. - -- spanner: - - Add a ReadWithOptions that supports a row limit, as well as an index. - - Support query plan and execution statistics. - - Added [OpenCensus](http://opencensus.io) support. - -- storage: Clarify checksum validation for gzipped files (it is not validated - when the file is served uncompressed). - - -_December 11, 2017_ - -*v0.17.0* - -- firestore BREAKING CHANGES: - - Remove UpdateMap and UpdateStruct; rename UpdatePaths to Update. - Change - `docref.UpdateMap(ctx, map[string]interface{}{"a.b", 1})` - to - `docref.Update(ctx, []firestore.Update{{Path: "a.b", Value: 1}})` - - Change - `docref.UpdateStruct(ctx, []string{"Field"}, aStruct)` - to - `docref.Update(ctx, []firestore.Update{{Path: "Field", Value: aStruct.Field}})` - - Rename MergePaths to Merge; require args to be FieldPaths - - A value stored as an integer can be read into a floating-point field, and vice versa. -- bigtable/cmd/cbt: - - Support deleting a column. - - Add regex option for row read. -- spanner: Mark stable. -- storage: - - Add Reader.ContentEncoding method. - - Fix handling of SignedURL headers. -- bigquery: - - If Uploader.Put is called with no rows, it returns nil without making a - call. - - Schema inference supports the "nullable" option in struct tags for - non-required fields. - - TimePartitioning supports "Field". - - -[Older news](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/master/old-news.md) - ## Supported APIs -Google API | Status | Package ----------------------------------|--------------|----------------------------------------------------------- -[BigQuery][cloud-bigquery] | stable | [`cloud.google.com/go/bigquery`][cloud-bigquery-ref] -[Bigtable][cloud-bigtable] | stable | [`cloud.google.com/go/bigtable`][cloud-bigtable-ref] -[Container][cloud-container] | alpha | [`cloud.google.com/go/container/apiv1`][cloud-container-ref] -[Data Loss Prevention][cloud-dlp]| alpha | [`cloud.google.com/go/dlp/apiv2beta1`][cloud-dlp-ref] -[Datastore][cloud-datastore] | stable | [`cloud.google.com/go/datastore`][cloud-datastore-ref] -[Debugger][cloud-debugger] | alpha | [`cloud.google.com/go/debugger/apiv2`][cloud-debugger-ref] -[ErrorReporting][cloud-errors] | alpha | [`cloud.google.com/go/errorreporting`][cloud-errors-ref] -[Firestore][cloud-firestore] | beta | [`cloud.google.com/go/firestore`][cloud-firestore-ref] -[Language][cloud-language] | stable | [`cloud.google.com/go/language/apiv1`][cloud-language-ref] -[Logging][cloud-logging] | stable | [`cloud.google.com/go/logging`][cloud-logging-ref] -[Monitoring][cloud-monitoring] | beta | [`cloud.google.com/go/monitoring/apiv3`][cloud-monitoring-ref] -[OS Login][cloud-oslogin] | alpha | [`cloud.google.com/compute/docs/oslogin/rest`][cloud-oslogin-ref] -[Pub/Sub][cloud-pubsub] | beta | [`cloud.google.com/go/pubsub`][cloud-pubsub-ref] -[Spanner][cloud-spanner] | stable | [`cloud.google.com/go/spanner`][cloud-spanner-ref] -[Speech][cloud-speech] | stable | [`cloud.google.com/go/speech/apiv1`][cloud-speech-ref] -[Storage][cloud-storage] | stable | [`cloud.google.com/go/storage`][cloud-storage-ref] -[Translation][cloud-translation] | stable | [`cloud.google.com/go/translate`][cloud-translation-ref] -[Video Intelligence][cloud-video]| beta | [`cloud.google.com/go/videointelligence/apiv1beta1`][cloud-video-ref] -[Vision][cloud-vision] | stable | [`cloud.google.com/go/vision/apiv1`][cloud-vision-ref] - +Google API | Status | Package +------------------------------------------------|--------------|----------------------------------------------------------- +[Asset][cloud-asset] | alpha | [`cloud.google.com/go/asset/v1beta`][cloud-asset-ref] +[BigQuery][cloud-bigquery] | stable | [`cloud.google.com/go/bigquery`][cloud-bigquery-ref] +[Bigtable][cloud-bigtable] | stable | [`cloud.google.com/go/bigtable`][cloud-bigtable-ref] +[Cloudtasks][cloud-tasks] | stable | [`cloud.google.com/go/cloudtasks/apiv2`][cloud-tasks-ref] +[Container][cloud-container] | stable | [`cloud.google.com/go/container/apiv1`][cloud-container-ref] +[ContainerAnalysis][cloud-containeranalysis] | beta | [`cloud.google.com/go/containeranalysis/apiv1beta1`][cloud-containeranalysis-ref] +[Dataproc][cloud-dataproc] | stable | [`cloud.google.com/go/dataproc/apiv1`][cloud-dataproc-ref] +[Datastore][cloud-datastore] | stable | [`cloud.google.com/go/datastore`][cloud-datastore-ref] +[Debugger][cloud-debugger] | alpha | [`cloud.google.com/go/debugger/apiv2`][cloud-debugger-ref] +[Dialogflow][cloud-dialogflow] | alpha | [`cloud.google.com/go/dialogflow/apiv2`][cloud-dialogflow-ref] +[Data Loss Prevention][cloud-dlp] | alpha | [`cloud.google.com/go/dlp/apiv2`][cloud-dlp-ref] +[ErrorReporting][cloud-errors] | alpha | [`cloud.google.com/go/errorreporting`][cloud-errors-ref] +[Firestore][cloud-firestore] | stable | [`cloud.google.com/go/firestore`][cloud-firestore-ref] +[IAM][cloud-iam] | stable | [`cloud.google.com/go/iam`][cloud-iam-ref] +[IoT][cloud-iot] | alpha | [`cloud.google.com/iot/apiv1`][cloud-iot-ref] +[KMS][cloud-kms] | stable | [`cloud.google.com/go/kms`][cloud-kms-ref] +[Natural Language][cloud-natural-language] | stable | [`cloud.google.com/go/language/apiv1`][cloud-natural-language-ref] +[Logging][cloud-logging] | stable | [`cloud.google.com/go/logging`][cloud-logging-ref] +[Monitoring][cloud-monitoring] | alpha | [`cloud.google.com/go/monitoring/apiv3`][cloud-monitoring-ref] +[OS Login][cloud-oslogin] | alpha | [`cloud.google.com/go/oslogin/apiv1`][cloud-oslogin-ref] +[Pub/Sub][cloud-pubsub] | stable | [`cloud.google.com/go/pubsub`][cloud-pubsub-ref] +[Phishing Protection][cloud-phishingprotection] | alpha | [`cloud.google.com/go/phishingprotection/apiv1betad1`][cloud-phishingprotection-ref] +[reCAPTCHA Enterprise][cloud-recaptcha] | alpha | [`cloud.google.com/go/recaptchaenterprise/apiv1betad1`][cloud-recaptcha-ref] +[Memorystore][cloud-memorystore] | alpha | [`cloud.google.com/go/redis/apiv1`][cloud-memorystore-ref] +[Scheduler][cloud-scheduler] | stable | [`cloud.google.com/go/scheduler/apiv1`][cloud-scheduler-ref] +[Spanner][cloud-spanner] | stable | [`cloud.google.com/go/spanner`][cloud-spanner-ref] +[Speech][cloud-speech] | stable | [`cloud.google.com/go/speech/apiv1`][cloud-speech-ref] +[Storage][cloud-storage] | stable | [`cloud.google.com/go/storage`][cloud-storage-ref] +[Talent][cloud-talent] | alpha | [`cloud.google.com/go/talent/apiv4beta1`][cloud-talent-ref] +[Text To Speech][cloud-texttospeech] | alpha | [`cloud.google.com/go/texttospeech/apiv1`][cloud-texttospeech-ref] +[Trace][cloud-trace] | alpha | [`cloud.google.com/go/trace/apiv2`][cloud-trace-ref] +[Translate][cloud-translate] | stable | [`cloud.google.com/go/translate`][cloud-translate-ref] +[Video Intelligence][cloud-video] | alpha | [`cloud.google.com/go/videointelligence/apiv1beta1`][cloud-video-ref] +[Vision][cloud-vision] | stable | [`cloud.google.com/go/vision/apiv1`][cloud-vision-ref] > **Alpha status**: the API is still being actively developed. As a > result, it might change in backward-incompatible ways and is not recommended @@ -288,23 +69,16 @@ Google API | Status | Package > **Stable status**: the API is mature and ready for production use. We will > continue addressing bugs and feature requests. -Documentation and examples are available at -https://godoc.org/cloud.google.com/go - -Visit or join the -[google-api-go-announce group](https://groups.google.com/forum/#!forum/google-api-go-announce) -for updates on these packages. +Documentation and examples are available at [godoc.org/cloud.google.com/go](godoc.org/cloud.google.com/go) ## Go Versions Supported We support the two most recent major versions of Go. If Google App Engine uses -an older version, we support that as well. You can see which versions are -currently supported by looking at the lines following `go:` in -[`.travis.yml`](.travis.yml). +an older version, we support that as well. ## Authorization -By default, each API will use [Google Application Default Credentials][default-creds] +By default, each API will use [Google Application Default Credentials](https://developers.google.com/identity/protocols/application-default-credentials) for authorization credentials used in calling the API endpoints. This will allow your application to run in many environments without requiring explicit configuration. @@ -316,12 +90,12 @@ client, err := storage.NewClient(ctx) To authorize using a [JSON key file](https://cloud.google.com/iam/docs/managing-service-account-keys), pass -[`option.WithServiceAccountFile`](https://godoc.org/google.golang.org/api/option#WithServiceAccountFile) +[`option.WithCredentialsFile`](https://godoc.org/google.golang.org/api/option#WithCredentialsFile) to the `NewClient` function of the desired package. For example: [snip]:# (auth-JSON) ```go -client, err := storage.NewClient(ctx, option.WithServiceAccountFile("path/to/keyfile.json")) +client, err := storage.NewClient(ctx, option.WithCredentialsFile("path/to/keyfile.json")) ``` You can exert more control over authorization by using the @@ -335,249 +109,6 @@ tokenSource := ... client, err := storage.NewClient(ctx, option.WithTokenSource(tokenSource)) ``` -## Cloud Datastore [![GoDoc](https://godoc.org/cloud.google.com/go/datastore?status.svg)](https://godoc.org/cloud.google.com/go/datastore) - -- [About Cloud Datastore][cloud-datastore] -- [Activating the API for your project][cloud-datastore-activation] -- [API documentation][cloud-datastore-docs] -- [Go client documentation](https://godoc.org/cloud.google.com/go/datastore) -- [Complete sample program](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/datastore/tasks) - -### Example Usage - -First create a `datastore.Client` to use throughout your application: - -[snip]:# (datastore-1) -```go -client, err := datastore.NewClient(ctx, "my-project-id") -if err != nil { - log.Fatal(err) -} -``` - -Then use that client to interact with the API: - -[snip]:# (datastore-2) -```go -type Post struct { - Title string - Body string `datastore:",noindex"` - PublishedAt time.Time -} -keys := []*datastore.Key{ - datastore.NameKey("Post", "post1", nil), - datastore.NameKey("Post", "post2", nil), -} -posts := []*Post{ - {Title: "Post 1", Body: "...", PublishedAt: time.Now()}, - {Title: "Post 2", Body: "...", PublishedAt: time.Now()}, -} -if _, err := client.PutMulti(ctx, keys, posts); err != nil { - log.Fatal(err) -} -``` - -## Cloud Storage [![GoDoc](https://godoc.org/cloud.google.com/go/storage?status.svg)](https://godoc.org/cloud.google.com/go/storage) - -- [About Cloud Storage][cloud-storage] -- [API documentation][cloud-storage-docs] -- [Go client documentation](https://godoc.org/cloud.google.com/go/storage) -- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/storage) - -### Example Usage - -First create a `storage.Client` to use throughout your application: - -[snip]:# (storage-1) -```go -client, err := storage.NewClient(ctx) -if err != nil { - log.Fatal(err) -} -``` - -[snip]:# (storage-2) -```go -// Read the object1 from bucket. -rc, err := client.Bucket("bucket").Object("object1").NewReader(ctx) -if err != nil { - log.Fatal(err) -} -defer rc.Close() -body, err := ioutil.ReadAll(rc) -if err != nil { - log.Fatal(err) -} -``` - -## Cloud Pub/Sub [![GoDoc](https://godoc.org/cloud.google.com/go/pubsub?status.svg)](https://godoc.org/cloud.google.com/go/pubsub) - -- [About Cloud Pubsub][cloud-pubsub] -- [API documentation][cloud-pubsub-docs] -- [Go client documentation](https://godoc.org/cloud.google.com/go/pubsub) -- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/pubsub) - -### Example Usage - -First create a `pubsub.Client` to use throughout your application: - -[snip]:# (pubsub-1) -```go -client, err := pubsub.NewClient(ctx, "project-id") -if err != nil { - log.Fatal(err) -} -``` - -Then use the client to publish and subscribe: - -[snip]:# (pubsub-2) -```go -// Publish "hello world" on topic1. -topic := client.Topic("topic1") -res := topic.Publish(ctx, &pubsub.Message{ - Data: []byte("hello world"), -}) -// The publish happens asynchronously. -// Later, you can get the result from res: -... -msgID, err := res.Get(ctx) -if err != nil { - log.Fatal(err) -} - -// Use a callback to receive messages via subscription1. -sub := client.Subscription("subscription1") -err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) { - fmt.Println(m.Data) - m.Ack() // Acknowledge that we've consumed the message. -}) -if err != nil { - log.Println(err) -} -``` - -## Cloud BigQuery [![GoDoc](https://godoc.org/cloud.google.com/go/bigquery?status.svg)](https://godoc.org/cloud.google.com/go/bigquery) - -- [About Cloud BigQuery][cloud-bigquery] -- [API documentation][cloud-bigquery-docs] -- [Go client documentation][cloud-bigquery-ref] -- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/bigquery) - -### Example Usage - -First create a `bigquery.Client` to use throughout your application: -[snip]:# (bq-1) -```go -c, err := bigquery.NewClient(ctx, "my-project-ID") -if err != nil { - // TODO: Handle error. -} -``` - -Then use that client to interact with the API: -[snip]:# (bq-2) -```go -// Construct a query. -q := c.Query(` - SELECT year, SUM(number) - FROM [bigquery-public-data:usa_names.usa_1910_2013] - WHERE name = "William" - GROUP BY year - ORDER BY year -`) -// Execute the query. -it, err := q.Read(ctx) -if err != nil { - // TODO: Handle error. -} -// Iterate through the results. -for { - var values []bigquery.Value - err := it.Next(&values) - if err == iterator.Done { - break - } - if err != nil { - // TODO: Handle error. - } - fmt.Println(values) -} -``` - - -## Stackdriver Logging [![GoDoc](https://godoc.org/cloud.google.com/go/logging?status.svg)](https://godoc.org/cloud.google.com/go/logging) - -- [About Stackdriver Logging][cloud-logging] -- [API documentation][cloud-logging-docs] -- [Go client documentation][cloud-logging-ref] -- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/logging) - -### Example Usage - -First create a `logging.Client` to use throughout your application: -[snip]:# (logging-1) -```go -ctx := context.Background() -client, err := logging.NewClient(ctx, "my-project") -if err != nil { - // TODO: Handle error. -} -``` - -Usually, you'll want to add log entries to a buffer to be periodically flushed -(automatically and asynchronously) to the Stackdriver Logging service. -[snip]:# (logging-2) -```go -logger := client.Logger("my-log") -logger.Log(logging.Entry{Payload: "something happened!"}) -``` - -Close your client before your program exits, to flush any buffered log entries. -[snip]:# (logging-3) -```go -err = client.Close() -if err != nil { - // TODO: Handle error. -} -``` - -## Cloud Spanner [![GoDoc](https://godoc.org/cloud.google.com/go/spanner?status.svg)](https://godoc.org/cloud.google.com/go/spanner) - -- [About Cloud Spanner][cloud-spanner] -- [API documentation][cloud-spanner-docs] -- [Go client documentation](https://godoc.org/cloud.google.com/go/spanner) - -### Example Usage - -First create a `spanner.Client` to use throughout your application: - -[snip]:# (spanner-1) -```go -client, err := spanner.NewClient(ctx, "projects/P/instances/I/databases/D") -if err != nil { - log.Fatal(err) -} -``` - -[snip]:# (spanner-2) -```go -// Simple Reads And Writes -_, err = client.Apply(ctx, []*spanner.Mutation{ - spanner.Insert("Users", - []string{"name", "email"}, - []interface{}{"alice", "a@example.com"})}) -if err != nil { - log.Fatal(err) -} -row, err := client.Single().ReadRow(ctx, "Users", - spanner.Key{"alice"}, []string{"email"}) -if err != nil { - log.Fatal(err) -} -``` - - ## Contributing Contributions are welcome. Please, see the @@ -592,32 +123,23 @@ for more information. [cloud-datastore]: https://cloud.google.com/datastore/ [cloud-datastore-ref]: https://godoc.org/cloud.google.com/go/datastore -[cloud-datastore-docs]: https://cloud.google.com/datastore/docs -[cloud-datastore-activation]: https://cloud.google.com/datastore/docs/activate [cloud-firestore]: https://cloud.google.com/firestore/ [cloud-firestore-ref]: https://godoc.org/cloud.google.com/go/firestore -[cloud-firestore-docs]: https://cloud.google.com/firestore/docs -[cloud-firestore-activation]: https://cloud.google.com/firestore/docs/activate [cloud-pubsub]: https://cloud.google.com/pubsub/ [cloud-pubsub-ref]: https://godoc.org/cloud.google.com/go/pubsub -[cloud-pubsub-docs]: https://cloud.google.com/pubsub/docs [cloud-storage]: https://cloud.google.com/storage/ [cloud-storage-ref]: https://godoc.org/cloud.google.com/go/storage -[cloud-storage-docs]: https://cloud.google.com/storage/docs -[cloud-storage-create-bucket]: https://cloud.google.com/storage/docs/cloud-console#_creatingbuckets [cloud-bigtable]: https://cloud.google.com/bigtable/ [cloud-bigtable-ref]: https://godoc.org/cloud.google.com/go/bigtable [cloud-bigquery]: https://cloud.google.com/bigquery/ -[cloud-bigquery-docs]: https://cloud.google.com/bigquery/docs [cloud-bigquery-ref]: https://godoc.org/cloud.google.com/go/bigquery [cloud-logging]: https://cloud.google.com/logging/ -[cloud-logging-docs]: https://cloud.google.com/logging/docs [cloud-logging-ref]: https://godoc.org/cloud.google.com/go/logging [cloud-monitoring]: https://cloud.google.com/monitoring/ @@ -630,17 +152,16 @@ for more information. [cloud-language-ref]: https://godoc.org/cloud.google.com/go/language/apiv1 [cloud-oslogin]: https://cloud.google.com/compute/docs/oslogin/rest -[cloud-oslogin-ref]: https://cloud.google.com/compute/docs/oslogin/rest +[cloud-oslogin-ref]: https://cloud.google.com/go/oslogin/apiv1 [cloud-speech]: https://cloud.google.com/speech [cloud-speech-ref]: https://godoc.org/cloud.google.com/go/speech/apiv1 [cloud-spanner]: https://cloud.google.com/spanner/ [cloud-spanner-ref]: https://godoc.org/cloud.google.com/go/spanner -[cloud-spanner-docs]: https://cloud.google.com/spanner/docs -[cloud-translation]: https://cloud.google.com/translation -[cloud-translation-ref]: https://godoc.org/cloud.google.com/go/translation +[cloud-translate]: https://cloud.google.com/translate +[cloud-translate-ref]: https://godoc.org/cloud.google.com/go/translate [cloud-video]: https://cloud.google.com/video-intelligence/ [cloud-video-ref]: https://godoc.org/cloud.google.com/go/videointelligence/apiv1beta1 @@ -657,4 +178,50 @@ for more information. [cloud-dlp]: https://cloud.google.com/dlp/ [cloud-dlp-ref]: https://godoc.org/cloud.google.com/go/dlp/apiv2beta1 -[default-creds]: https://developers.google.com/identity/protocols/application-default-credentials +[cloud-dataproc]: https://cloud.google.com/dataproc/ +[cloud-dataproc-ref]: https://godoc.org/cloud.google.com/go/dataproc/apiv1 + +[cloud-iam]: https://cloud.google.com/iam/ +[cloud-iam-ref]: https://godoc.org/cloud.google.com/go/iam + +[cloud-kms]: https://cloud.google.com/kms/ +[cloud-kms-ref]: https://godoc.org/cloud.google.com/go/kms/apiv1 + +[cloud-natural-language]: https://cloud.google.com/natural-language/ +[cloud-natural-language-ref]: https://godoc.org/cloud.google.com/go/language/apiv1 + +[cloud-memorystore]: https://cloud.google.com/memorystore/ +[cloud-memorystore-ref]: https://godoc.org/cloud.google.com/go/redis/apiv1 + +[cloud-texttospeech]: https://cloud.google.com/texttospeech/ +[cloud-texttospeech-ref]: https://godoc.org/cloud.google.com/go/texttospeech/apiv1 + +[cloud-trace]: https://cloud.google.com/trace/ +[cloud-trace-ref]: https://godoc.org/cloud.google.com/go/trace/apiv2 + +[cloud-dialogflow]: https://cloud.google.com/dialogflow-enterprise/ +[cloud-dialogflow-ref]: https://godoc.org/cloud.google.com/go/dialogflow/apiv2 + +[cloud-containeranalysis]: https://cloud.google.com/container-registry/docs/container-analysis +[cloud-containeranalysis-ref]: https://godoc.org/cloud.google.com/go/devtools/containeranalysis/apiv1beta1 + +[cloud-asset]: https://cloud.google.com/security-command-center/docs/how-to-asset-inventory +[cloud-asset-ref]: https://godoc.org/cloud.google.com/go/asset/apiv1 + +[cloud-tasks]: https://cloud.google.com/tasks/ +[cloud-tasks-ref]: https://godoc.org/cloud.google.com/go/cloudtasks/apiv2 + +[cloud-scheduler]: https://cloud.google.com/scheduler +[cloud-scheduler-ref]: https://godoc.org/cloud.google.com/go/scheduler/apiv1 + +[cloud-iot]: https://cloud.google.com/iot-core/ +[cloud-iot-ref]: https://godoc.org/cloud.google.com/go/iot/apiv1 + +[cloud-phishingprotection]: https://cloud.google.com/phishing-protection/ +[cloud-phishingprotection-ref]: https://cloud.google.com/go/phishingprotection/apiv1beta1 + +[cloud-recaptcha]: https://cloud.google.com/recaptcha-enterprise/ +[cloud-recaptcha-ref]: https://cloud.google.com/go/recaptchaenterprise/apiv1beta1 + +[cloud-talent]: https://cloud.google.com/solutions/talent-solution/ +[cloud-talent-ref]: https://godoc.org/cloud.google.com/go/talent/apiv4beta1 diff --git a/vendor/cloud.google.com/go/cloud.go b/vendor/cloud.google.com/go/cloud.go new file mode 100644 index 0000000000000..237d84561ce0a --- /dev/null +++ b/vendor/cloud.google.com/go/cloud.go @@ -0,0 +1,100 @@ +// Copyright 2014 Google LLC +// +// 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. + +/* +Package cloud is the root of the packages used to access Google Cloud +Services. See https://godoc.org/cloud.google.com/go for a full list +of sub-packages. + + +Client Options + +All clients in sub-packages are configurable via client options. These options are +described here: https://godoc.org/google.golang.org/api/option. + + +Authentication and Authorization + +All the clients in sub-packages support authentication via Google Application Default +Credentials (see https://cloud.google.com/docs/authentication/production), or +by providing a JSON key file for a Service Account. See the authentication examples +in this package for details. + + +Timeouts and Cancellation + +By default, all requests in sub-packages will run indefinitely, retrying on transient +errors when correctness allows. To set timeouts or arrange for cancellation, use +contexts. See the examples for details. + +Do not attempt to control the initial connection (dialing) of a service by setting a +timeout on the context passed to NewClient. Dialing is non-blocking, so timeouts +would be ineffective and would only interfere with credential refreshing, which uses +the same context. + + +Connection Pooling + +Connection pooling differs in clients based on their transport. Cloud +clients either rely on HTTP or gRPC transports to communicate +with Google Cloud. + +Cloud clients that use HTTP (bigquery, compute, storage, and translate) rely on the +underlying HTTP transport to cache connections for later re-use. These are cached to +the default http.MaxIdleConns and http.MaxIdleConnsPerHost settings in +http.DefaultTransport. + +For gRPC clients (all others in this repo), connection pooling is configurable. Users +of cloud client libraries may specify option.WithGRPCConnectionPool(n) as a client +option to NewClient calls. This configures the underlying gRPC connections to be +pooled and addressed in a round robin fashion. + + +Using the Libraries with Docker + +Minimal docker images like Alpine lack CA certificates. This causes RPCs to appear to +hang, because gRPC retries indefinitely. See https://github.com/googleapis/google-cloud-go/issues/928 +for more information. + + +Debugging + +To see gRPC logs, set the environment variable GRPC_GO_LOG_SEVERITY_LEVEL. See +https://godoc.org/google.golang.org/grpc/grpclog for more information. + +For HTTP logging, set the GODEBUG environment variable to "http2debug=1" or "http2debug=2". + + +Client Stability + +Clients in this repository are considered alpha or beta unless otherwise +marked as stable in the README.md. Semver is not used to communicate stability +of clients. + +Alpha and beta clients may change or go away without notice. + +Clients marked stable will maintain compatibility with future versions for as +long as we can reasonably sustain. Incompatible changes might be made in some +situations, including: + +- Security bugs may prompt backwards-incompatible changes. + +- Situations in which components are no longer feasible to maintain without +making breaking changes, including removal. + +- Parts of the client surface may be outright unstable and subject to change. +These parts of the surface will be labeled with the note, "It is EXPERIMENTAL +and subject to change or removal without notice." +*/ +package cloud // import "cloud.google.com/go" diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h new file mode 100644 index 0000000000000..156c0b87b00a0 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclinetest.h @@ -0,0 +1,9 @@ +// +build ignore + +// Empty include file to generate z symbols + + + + + +// EOF diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go new file mode 100644 index 0000000000000..dcd7015bb7298 --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/pclntab.go @@ -0,0 +1,472 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +/* + * Line tables + */ + +package gosym + +import ( + "encoding/binary" + "sync" +) + +// A LineTable is a data structure mapping program counters to line numbers. +// +// In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable, +// and the line number corresponded to a numbering of all source lines in the +// program, across all files. That absolute line number would then have to be +// converted separately to a file name and line number within the file. +// +// In Go 1.2, the format of the data changed so that there is a single LineTable +// for the entire program, shared by all Funcs, and there are no absolute line +// numbers, just line numbers within specific files. +// +// For the most part, LineTable's methods should be treated as an internal +// detail of the package; callers should use the methods on Table instead. +type LineTable struct { + Data []byte + PC uint64 + Line int + + // Go 1.2 state + mu sync.Mutex + go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes + binary binary.ByteOrder + quantum uint32 + ptrsize uint32 + functab []byte + nfunctab uint32 + filetab []byte + nfiletab uint32 + fileMap map[string]uint32 +} + +// NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4, +// but we have no idea whether we're using arm or not. This only +// matters in the old (pre-Go 1.2) symbol table format, so it's not worth +// fixing. +const oldQuantum = 1 + +func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) { + // The PC/line table can be thought of as a sequence of + // * + // batches. Each update batch results in a (pc, line) pair, + // where line applies to every PC from pc up to but not + // including the pc of the next pair. + // + // Here we process each update individually, which simplifies + // the code, but makes the corner cases more confusing. + b, pc, line = t.Data, t.PC, t.Line + for pc <= targetPC && line != targetLine && len(b) > 0 { + code := b[0] + b = b[1:] + switch { + case code == 0: + if len(b) < 4 { + b = b[0:0] + break + } + val := binary.BigEndian.Uint32(b) + b = b[4:] + line += int(val) + case code <= 64: + line += int(code) + case code <= 128: + line -= int(code - 64) + default: + pc += oldQuantum * uint64(code-128) + continue + } + pc += oldQuantum + } + return b, pc, line +} + +func (t *LineTable) slice(pc uint64) *LineTable { + data, pc, line := t.parse(pc, -1) + return &LineTable{Data: data, PC: pc, Line: line} +} + +// PCToLine returns the line number for the given program counter. +// Callers should use Table's PCToLine method instead. +func (t *LineTable) PCToLine(pc uint64) int { + if t.isGo12() { + return t.go12PCToLine(pc) + } + _, _, line := t.parse(pc, -1) + return line +} + +// LineToPC returns the program counter for the given line number, +// considering only program counters before maxpc. +// Callers should use Table's LineToPC method instead. +func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 { + if t.isGo12() { + return 0 + } + _, pc, line1 := t.parse(maxpc, line) + if line1 != line { + return 0 + } + // Subtract quantum from PC to account for post-line increment + return pc - oldQuantum +} + +// NewLineTable returns a new PC/line table +// corresponding to the encoded data. +// Text must be the start address of the +// corresponding text segment. +func NewLineTable(data []byte, text uint64) *LineTable { + return &LineTable{Data: data, PC: text, Line: 0} +} + +// Go 1.2 symbol table format. +// See golang.org/s/go12symtab. +// +// A general note about the methods here: rather than try to avoid +// index out of bounds errors, we trust Go to detect them, and then +// we recover from the panics and treat them as indicative of a malformed +// or incomplete table. +// +// The methods called by symtab.go, which begin with "go12" prefixes, +// are expected to have that recovery logic. + +// isGo12 reports whether this is a Go 1.2 (or later) symbol table. +func (t *LineTable) isGo12() bool { + t.go12Init() + return t.go12 == 1 +} + +const go12magic = 0xfffffffb + +// uintptr returns the pointer-sized value encoded at b. +// The pointer size is dictated by the table being read. +func (t *LineTable) uintptr(b []byte) uint64 { + if t.ptrsize == 4 { + return uint64(t.binary.Uint32(b)) + } + return t.binary.Uint64(b) +} + +// go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table. +func (t *LineTable) go12Init() { + t.mu.Lock() + defer t.mu.Unlock() + if t.go12 != 0 { + return + } + + defer func() { + // If we panic parsing, assume it's not a Go 1.2 symbol table. + recover() + }() + + // Check header: 4-byte magic, two zeros, pc quantum, pointer size. + t.go12 = -1 // not Go 1.2 until proven otherwise + if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 || + (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum + (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size + return + } + + switch uint32(go12magic) { + case binary.LittleEndian.Uint32(t.Data): + t.binary = binary.LittleEndian + case binary.BigEndian.Uint32(t.Data): + t.binary = binary.BigEndian + default: + return + } + + t.quantum = uint32(t.Data[6]) + t.ptrsize = uint32(t.Data[7]) + + t.nfunctab = uint32(t.uintptr(t.Data[8:])) + t.functab = t.Data[8+t.ptrsize:] + functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize + fileoff := t.binary.Uint32(t.functab[functabsize:]) + t.functab = t.functab[:functabsize] + t.filetab = t.Data[fileoff:] + t.nfiletab = t.binary.Uint32(t.filetab) + t.filetab = t.filetab[:t.nfiletab*4] + + t.go12 = 1 // so far so good +} + +// go12Funcs returns a slice of Funcs derived from the Go 1.2 pcln table. +func (t *LineTable) go12Funcs() []Func { + // Assume it is malformed and return nil on error. + defer func() { + recover() + }() + + n := len(t.functab) / int(t.ptrsize) / 2 + funcs := make([]Func, n) + for i := range funcs { + f := &funcs[i] + f.Entry = uint64(t.uintptr(t.functab[2*i*int(t.ptrsize):])) + f.End = uint64(t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):])) + info := t.Data[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):] + f.LineTable = t + f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:])) + f.Sym = &Sym{ + Value: f.Entry, + Type: 'T', + Name: t.string(t.binary.Uint32(info[t.ptrsize:])), + GoType: 0, + Func: f, + } + } + return funcs +} + +// findFunc returns the func corresponding to the given program counter. +func (t *LineTable) findFunc(pc uint64) []byte { + if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) { + return nil + } + + // The function table is a list of 2*nfunctab+1 uintptrs, + // alternating program counters and offsets to func structures. + f := t.functab + nf := t.nfunctab + for nf > 0 { + m := nf / 2 + fm := f[2*t.ptrsize*m:] + if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) { + return t.Data[t.uintptr(fm[t.ptrsize:]):] + } else if pc < t.uintptr(fm) { + nf = m + } else { + f = f[(m+1)*2*t.ptrsize:] + nf -= m + 1 + } + } + return nil +} + +// readvarint reads, removes, and returns a varint from *pp. +func (t *LineTable) readvarint(pp *[]byte) uint32 { + var v, shift uint32 + p := *pp + for shift = 0; ; shift += 7 { + b := p[0] + p = p[1:] + v |= (uint32(b) & 0x7F) << shift + if b&0x80 == 0 { + break + } + } + *pp = p + return v +} + +// string returns a Go string found at off. +func (t *LineTable) string(off uint32) string { + for i := off; ; i++ { + if t.Data[i] == 0 { + return string(t.Data[off:i]) + } + } +} + +// step advances to the next pc, value pair in the encoded table. +func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool { + uvdelta := t.readvarint(p) + if uvdelta == 0 && !first { + return false + } + if uvdelta&1 != 0 { + uvdelta = ^(uvdelta >> 1) + } else { + uvdelta >>= 1 + } + vdelta := int32(uvdelta) + pcdelta := t.readvarint(p) * t.quantum + *pc += uint64(pcdelta) + *val += vdelta + return true +} + +// pcvalue reports the value associated with the target pc. +// off is the offset to the beginning of the pc-value table, +// and entry is the start PC for the corresponding function. +func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 { + if off == 0 { + return -1 + } + p := t.Data[off:] + + val := int32(-1) + pc := entry + for t.step(&p, &pc, &val, pc == entry) { + if targetpc < pc { + return val + } + } + return -1 +} + +// findFileLine scans one function in the binary looking for a +// program counter in the given file on the given line. +// It does so by running the pc-value tables mapping program counter +// to file number. Since most functions come from a single file, these +// are usually short and quick to scan. If a file match is found, then the +// code goes to the expense of looking for a simultaneous line number match. +func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 { + if filetab == 0 || linetab == 0 { + return 0 + } + + fp := t.Data[filetab:] + fl := t.Data[linetab:] + fileVal := int32(-1) + filePC := entry + lineVal := int32(-1) + linePC := entry + fileStartPC := filePC + for t.step(&fp, &filePC, &fileVal, filePC == entry) { + if fileVal == filenum && fileStartPC < filePC { + // fileVal is in effect starting at fileStartPC up to + // but not including filePC, and it's the file we want. + // Run the PC table looking for a matching line number + // or until we reach filePC. + lineStartPC := linePC + for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) { + // lineVal is in effect until linePC, and lineStartPC < filePC. + if lineVal == line { + if fileStartPC <= lineStartPC { + return lineStartPC + } + if fileStartPC < linePC { + return fileStartPC + } + } + lineStartPC = linePC + } + } + fileStartPC = filePC + } + return 0 +} + +// go12PCToLine maps program counter to line number for the Go 1.2 pcln table. +func (t *LineTable) go12PCToLine(pc uint64) (line int) { + return t.go12PCToVal(pc, t.ptrsize+5*4) +} + +// go12PCToSPAdj maps program counter to Stack Pointer adjustment for the Go 1.2 pcln table. +func (t *LineTable) go12PCToSPAdj(pc uint64) (spadj int) { + return t.go12PCToVal(pc, t.ptrsize+3*4) +} + +func (t *LineTable) go12PCToVal(pc uint64, fOffset uint32) (val int) { + defer func() { + if recover() != nil { + val = -1 + } + }() + + f := t.findFunc(pc) + if f == nil { + return -1 + } + entry := t.uintptr(f) + linetab := t.binary.Uint32(f[fOffset:]) + return int(t.pcvalue(linetab, entry, pc)) +} + +// go12PCToFile maps program counter to file name for the Go 1.2 pcln table. +func (t *LineTable) go12PCToFile(pc uint64) (file string) { + defer func() { + if recover() != nil { + file = "" + } + }() + + f := t.findFunc(pc) + if f == nil { + return "" + } + entry := t.uintptr(f) + filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + fno := t.pcvalue(filetab, entry, pc) + if fno <= 0 { + return "" + } + return t.string(t.binary.Uint32(t.filetab[4*fno:])) +} + +// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table. +func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) { + defer func() { + if recover() != nil { + pc = 0 + } + }() + + t.initFileMap() + filenum := t.fileMap[file] + if filenum == 0 { + return 0 + } + + // Scan all functions. + // If this turns out to be a bottleneck, we could build a map[int32][]int32 + // mapping file number to a list of functions with code from that file. + for i := uint32(0); i < t.nfunctab; i++ { + f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):] + entry := t.uintptr(f) + filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) + pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line)) + if pc != 0 { + return pc + } + } + return 0 +} + +// initFileMap initializes the map from file name to file number. +func (t *LineTable) initFileMap() { + t.mu.Lock() + defer t.mu.Unlock() + + if t.fileMap != nil { + return + } + m := make(map[string]uint32) + + for i := uint32(1); i < t.nfiletab; i++ { + s := t.string(t.binary.Uint32(t.filetab[4*i:])) + m[s] = i + } + t.fileMap = m +} + +// go12MapFiles adds to m a key for every file in the Go 1.2 LineTable. +// Every key maps to obj. That's not a very interesting map, but it provides +// a way for callers to obtain the list of files in the program. +func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) { + defer func() { + recover() + }() + + t.initFileMap() + for file := range t.fileMap { + m[file] = obj + } +} diff --git a/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go new file mode 100644 index 0000000000000..2c83b841cf8ce --- /dev/null +++ b/vendor/cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/gosym/symtab.go @@ -0,0 +1,731 @@ +// Copyright 2018 Google Inc. All Rights Reserved. +// +// 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. + +// Package gosym implements access to the Go symbol +// and line number tables embedded in Go binaries generated +// by the gc compilers. +package gosym + +// The table format is a variant of the format used in Plan 9's a.out +// format, documented at http://plan9.bell-labs.com/magic/man2html/6/a.out. +// The best reference for the differences between the Plan 9 format +// and the Go format is the runtime source, specifically ../../runtime/symtab.c. + +import ( + "bytes" + "encoding/binary" + "fmt" + "strconv" + "strings" +) + +/* + * Symbols + */ + +// A Sym represents a single symbol table entry. +type Sym struct { + Value uint64 + Type byte + Name string + GoType uint64 + // If this symbol if a function symbol, the corresponding Func + Func *Func +} + +// Static reports whether this symbol is static (not visible outside its file). +func (s *Sym) Static() bool { return s.Type >= 'a' } + +// PackageName returns the package part of the symbol name, +// or the empty string if there is none. +func (s *Sym) PackageName() string { + if i := strings.Index(s.Name, "."); i != -1 { + return s.Name[0:i] + } + return "" +} + +// ReceiverName returns the receiver type name of this symbol, +// or the empty string if there is none. +func (s *Sym) ReceiverName() string { + l := strings.Index(s.Name, ".") + r := strings.LastIndex(s.Name, ".") + if l == -1 || r == -1 || l == r { + return "" + } + return s.Name[l+1 : r] +} + +// BaseName returns the symbol name without the package or receiver name. +func (s *Sym) BaseName() string { + if i := strings.LastIndex(s.Name, "."); i != -1 { + return s.Name[i+1:] + } + return s.Name +} + +// A Func collects information about a single function. +type Func struct { + Entry uint64 + *Sym + End uint64 + Params []*Sym + Locals []*Sym + FrameSize int + LineTable *LineTable + Obj *Obj +} + +// An Obj represents a collection of functions in a symbol table. +// +// The exact method of division of a binary into separate Objs is an internal detail +// of the symbol table format. +// +// In early versions of Go each source file became a different Obj. +// +// In Go 1 and Go 1.1, each package produced one Obj for all Go sources +// and one Obj per C source file. +// +// In Go 1.2, there is a single Obj for the entire program. +type Obj struct { + // Funcs is a list of functions in the Obj. + Funcs []Func + + // In Go 1.1 and earlier, Paths is a list of symbols corresponding + // to the source file names that produced the Obj. + // In Go 1.2, Paths is nil. + // Use the keys of Table.Files to obtain a list of source files. + Paths []Sym // meta +} + +/* + * Symbol tables + */ + +// Table represents a Go symbol table. It stores all of the +// symbols decoded from the program and provides methods to translate +// between symbols, names, and addresses. +type Table struct { + Syms []Sym + Funcs []Func + Files map[string]*Obj // nil for Go 1.2 and later binaries + Objs []Obj // nil for Go 1.2 and later binaries + + go12line *LineTable // Go 1.2 line number table +} + +type sym struct { + value uint64 + gotype uint64 + typ byte + name []byte +} + +var ( + littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00} + bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00} + oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00} +) + +func walksymtab(data []byte, fn func(sym) error) error { + if len(data) == 0 { // missing symtab is okay + return nil + } + var order binary.ByteOrder = binary.BigEndian + newTable := false + switch { + case bytes.HasPrefix(data, oldLittleEndianSymtab): + // Same as Go 1.0, but little endian. + // Format was used during interim development between Go 1.0 and Go 1.1. + // Should not be widespread, but easy to support. + data = data[6:] + order = binary.LittleEndian + case bytes.HasPrefix(data, bigEndianSymtab): + newTable = true + case bytes.HasPrefix(data, littleEndianSymtab): + newTable = true + order = binary.LittleEndian + } + var ptrsz int + if newTable { + if len(data) < 8 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + ptrsz = int(data[7]) + if ptrsz != 4 && ptrsz != 8 { + return &DecodingError{7, "invalid pointer size", ptrsz} + } + data = data[8:] + } + var s sym + p := data + for len(p) >= 4 { + var typ byte + if newTable { + // Symbol type, value, Go type. + typ = p[0] & 0x3F + wideValue := p[0]&0x40 != 0 + goType := p[0]&0x80 != 0 + if typ < 26 { + typ += 'A' + } else { + typ += 'a' - 26 + } + s.typ = typ + p = p[1:] + if wideValue { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width value + if ptrsz == 8 { + s.value = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.value = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } else { + // varint value + s.value = 0 + shift := uint(0) + for len(p) > 0 && p[0]&0x80 != 0 { + s.value |= uint64(p[0]&0x7F) << shift + shift += 7 + p = p[1:] + } + if len(p) == 0 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.value |= uint64(p[0]) << shift + p = p[1:] + } + if goType { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width go type + if ptrsz == 8 { + s.gotype = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.gotype = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } + } else { + // Value, symbol type. + s.value = uint64(order.Uint32(p[0:4])) + if len(p) < 5 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + typ = p[4] + if typ&0x80 == 0 { + return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ} + } + typ &^= 0x80 + s.typ = typ + p = p[5:] + } + + // Name. + var i int + var nnul int + for i = 0; i < len(p); i++ { + if p[i] == 0 { + nnul = 1 + break + } + } + switch typ { + case 'z', 'Z': + p = p[i+nnul:] + for i = 0; i+2 <= len(p); i += 2 { + if p[i] == 0 && p[i+1] == 0 { + nnul = 2 + break + } + } + } + if len(p) < i+nnul { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.name = p[0:i] + i += nnul + p = p[i:] + + if !newTable { + if len(p) < 4 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // Go type. + s.gotype = uint64(order.Uint32(p[:4])) + p = p[4:] + } + fn(s) + } + return nil +} + +// NewTable decodes the Go symbol table in data, +// returning an in-memory representation. +func NewTable(symtab []byte, pcln *LineTable) (*Table, error) { + var n int + err := walksymtab(symtab, func(s sym) error { + n++ + return nil + }) + if err != nil { + return nil, err + } + + var t Table + if pcln.isGo12() { + t.go12line = pcln + } + fname := make(map[uint16]string) + t.Syms = make([]Sym, 0, n) + nf := 0 + nz := 0 + lasttyp := uint8(0) + err = walksymtab(symtab, func(s sym) error { + n := len(t.Syms) + t.Syms = t.Syms[0 : n+1] + ts := &t.Syms[n] + ts.Type = s.typ + ts.Value = uint64(s.value) + ts.GoType = uint64(s.gotype) + switch s.typ { + default: + // rewrite name to use . instead of · (c2 b7) + w := 0 + b := s.name + for i := 0; i < len(b); i++ { + if b[i] == 0xc2 && i+1 < len(b) && b[i+1] == 0xb7 { + i++ + b[i] = '.' + } + b[w] = b[i] + w++ + } + ts.Name = string(s.name[0:w]) + case 'z', 'Z': + if lasttyp != 'z' && lasttyp != 'Z' { + nz++ + } + for i := 0; i < len(s.name); i += 2 { + eltIdx := binary.BigEndian.Uint16(s.name[i : i+2]) + elt, ok := fname[eltIdx] + if !ok { + return &DecodingError{-1, "bad filename code", eltIdx} + } + if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' { + ts.Name += "/" + } + ts.Name += elt + } + } + switch s.typ { + case 'T', 't', 'L', 'l': + nf++ + case 'f': + fname[uint16(s.value)] = ts.Name + } + lasttyp = s.typ + return nil + }) + if err != nil { + return nil, err + } + + t.Funcs = make([]Func, 0, nf) + t.Files = make(map[string]*Obj) + + var obj *Obj + if t.go12line != nil { + // Put all functions into one Obj. + t.Objs = make([]Obj, 1) + obj = &t.Objs[0] + t.go12line.go12MapFiles(t.Files, obj) + } else { + t.Objs = make([]Obj, 0, nz) + } + + // Count text symbols and attach frame sizes, parameters, and + // locals to them. Also, find object file boundaries. + lastf := 0 + for i := 0; i < len(t.Syms); i++ { + sym := &t.Syms[i] + switch sym.Type { + case 'Z', 'z': // path symbol + if t.go12line != nil { + // Go 1.2 binaries have the file information elsewhere. Ignore. + break + } + // Finish the current object + if obj != nil { + obj.Funcs = t.Funcs[lastf:] + } + lastf = len(t.Funcs) + + // Start new object + n := len(t.Objs) + t.Objs = t.Objs[0 : n+1] + obj = &t.Objs[n] + + // Count & copy path symbols + var end int + for end = i + 1; end < len(t.Syms); end++ { + if c := t.Syms[end].Type; c != 'Z' && c != 'z' { + break + } + } + obj.Paths = t.Syms[i:end] + i = end - 1 // loop will i++ + + // Record file names + depth := 0 + for j := range obj.Paths { + s := &obj.Paths[j] + if s.Name == "" { + depth-- + } else { + if depth == 0 { + t.Files[s.Name] = obj + } + depth++ + } + } + + case 'T', 't', 'L', 'l': // text symbol + if n := len(t.Funcs); n > 0 { + t.Funcs[n-1].End = sym.Value + } + if sym.Name == "etext" { + continue + } + + // Count parameter and local (auto) syms + var np, na int + var end int + countloop: + for end = i + 1; end < len(t.Syms); end++ { + switch t.Syms[end].Type { + case 'T', 't', 'L', 'l', 'Z', 'z': + break countloop + case 'p': + np++ + case 'a': + na++ + } + } + + // Fill in the function symbol + n := len(t.Funcs) + t.Funcs = t.Funcs[0 : n+1] + fn := &t.Funcs[n] + sym.Func = fn + fn.Params = make([]*Sym, 0, np) + fn.Locals = make([]*Sym, 0, na) + fn.Sym = sym + fn.Entry = sym.Value + fn.Obj = obj + if t.go12line != nil { + // All functions share the same line table. + // It knows how to narrow down to a specific + // function quickly. + fn.LineTable = t.go12line + } else if pcln != nil { + fn.LineTable = pcln.slice(fn.Entry) + pcln = fn.LineTable + } + for j := i; j < end; j++ { + s := &t.Syms[j] + switch s.Type { + case 'm': + fn.FrameSize = int(s.Value) + case 'p': + n := len(fn.Params) + fn.Params = fn.Params[0 : n+1] + fn.Params[n] = s + case 'a': + n := len(fn.Locals) + fn.Locals = fn.Locals[0 : n+1] + fn.Locals[n] = s + } + } + i = end - 1 // loop will i++ + } + } + + if t.go12line != nil && nf == 0 { + t.Funcs = t.go12line.go12Funcs() + } + if obj != nil { + obj.Funcs = t.Funcs[lastf:] + } + return &t, nil +} + +// PCToFunc returns the function containing the program counter pc, +// or nil if there is no such function. +func (t *Table) PCToFunc(pc uint64) *Func { + funcs := t.Funcs + for len(funcs) > 0 { + m := len(funcs) / 2 + fn := &funcs[m] + switch { + case pc < fn.Entry: + funcs = funcs[0:m] + case fn.Entry <= pc && pc < fn.End: + return fn + default: + funcs = funcs[m+1:] + } + } + return nil +} + +// PCToLine looks up line number information for a program counter. +// If there is no information, it returns fn == nil. +func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) { + if fn = t.PCToFunc(pc); fn == nil { + return + } + if t.go12line != nil { + file = t.go12line.go12PCToFile(pc) + line = t.go12line.go12PCToLine(pc) + } else { + file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc)) + } + return +} + +// PCToSPAdj returns the stack pointer adjustment for a program counter. +func (t *Table) PCToSPAdj(pc uint64) (spadj int) { + if fn := t.PCToFunc(pc); fn == nil { + return 0 + } + if t.go12line != nil { + return t.go12line.go12PCToSPAdj(pc) + } + return 0 +} + +// LineToPC looks up the first program counter on the given line in +// the named file. It returns UnknownPathError or UnknownLineError if +// there is an error looking up this line. +func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) { + obj, ok := t.Files[file] + if !ok { + return 0, nil, UnknownFileError(file) + } + + if t.go12line != nil { + pc := t.go12line.go12LineToPC(file, line) + if pc == 0 { + return 0, nil, &UnknownLineError{file, line} + } + return pc, t.PCToFunc(pc), nil + } + + abs, err := obj.alineFromLine(file, line) + if err != nil { + return + } + for i := range obj.Funcs { + f := &obj.Funcs[i] + pc := f.LineTable.LineToPC(abs, f.End) + if pc != 0 { + return pc, f, nil + } + } + return 0, nil, &UnknownLineError{file, line} +} + +// LookupSym returns the text, data, or bss symbol with the given name, +// or nil if no such symbol is found. +func (t *Table) LookupSym(name string) *Sym { + // TODO(austin) Maybe make a map + for i := range t.Syms { + s := &t.Syms[i] + switch s.Type { + case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b': + if s.Name == name { + return s + } + } + } + return nil +} + +// LookupFunc returns the text, data, or bss symbol with the given name, +// or nil if no such symbol is found. +func (t *Table) LookupFunc(name string) *Func { + for i := range t.Funcs { + f := &t.Funcs[i] + if f.Sym.Name == name { + return f + } + } + return nil +} + +// SymByAddr returns the text, data, or bss symbol starting at the given address. +func (t *Table) SymByAddr(addr uint64) *Sym { + for i := range t.Syms { + s := &t.Syms[i] + switch s.Type { + case 'T', 't', 'L', 'l', 'D', 'd', 'B', 'b': + if s.Value == addr { + return s + } + } + } + return nil +} + +/* + * Object files + */ + +// This is legacy code for Go 1.1 and earlier, which used the +// Plan 9 format for pc-line tables. This code was never quite +// correct. It's probably very close, and it's usually correct, but +// we never quite found all the corner cases. +// +// Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab. + +func (o *Obj) lineFromAline(aline int) (string, int) { + type stackEnt struct { + path string + start int + offset int + prev *stackEnt + } + + noPath := &stackEnt{"", 0, 0, nil} + tos := noPath + +pathloop: + for _, s := range o.Paths { + val := int(s.Value) + switch { + case val > aline: + break pathloop + + case val == 1: + // Start a new stack + tos = &stackEnt{s.Name, val, 0, noPath} + + case s.Name == "": + // Pop + if tos == noPath { + return "", 0 + } + tos.prev.offset += val - tos.start + tos = tos.prev + + default: + // Push + tos = &stackEnt{s.Name, val, 0, tos} + } + } + + if tos == noPath { + return "", 0 + } + return tos.path, aline - tos.start - tos.offset + 1 +} + +func (o *Obj) alineFromLine(path string, line int) (int, error) { + if line < 1 { + return 0, &UnknownLineError{path, line} + } + + for i, s := range o.Paths { + // Find this path + if s.Name != path { + continue + } + + // Find this line at this stack level + depth := 0 + var incstart int + line += int(s.Value) + pathloop: + for _, s := range o.Paths[i:] { + val := int(s.Value) + switch { + case depth == 1 && val >= line: + return line - 1, nil + + case s.Name == "": + depth-- + if depth == 0 { + break pathloop + } else if depth == 1 { + line += val - incstart + } + + default: + if depth == 1 { + incstart = val + } + depth++ + } + } + return 0, &UnknownLineError{path, line} + } + return 0, UnknownFileError(path) +} + +/* + * Errors + */ + +// UnknownFileError represents a failure to find the specific file in +// the symbol table. +type UnknownFileError string + +func (e UnknownFileError) Error() string { return "unknown file: " + string(e) } + +// UnknownLineError represents a failure to map a line to a program +// counter, either because the line is beyond the bounds of the file +// or because there is no code on the given line. +type UnknownLineError struct { + File string + Line int +} + +func (e *UnknownLineError) Error() string { + return "no code at " + e.File + ":" + strconv.Itoa(e.Line) +} + +// DecodingError represents an error during the decoding of +// the symbol table. +type DecodingError struct { + off int + msg string + val interface{} +} + +func (e *DecodingError) Error() string { + msg := e.msg + if e.val != nil { + msg += fmt.Sprintf(" '%v'", e.val) + } + msg += fmt.Sprintf(" at byte %#x", e.off) + return msg +} diff --git a/vendor/cloud.google.com/go/compute/metadata/metadata.go b/vendor/cloud.google.com/go/compute/metadata/metadata.go index e708c031b9586..125b7033c96b9 100644 --- a/vendor/cloud.google.com/go/compute/metadata/metadata.go +++ b/vendor/cloud.google.com/go/compute/metadata/metadata.go @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All Rights Reserved. +// Copyright 2014 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package metadata // import "cloud.google.com/go/compute/metadata" import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -31,9 +32,6 @@ import ( "strings" "sync" "time" - - "golang.org/x/net/context" - "golang.org/x/net/context/ctxhttp" ) const ( @@ -64,7 +62,7 @@ var ( ) var ( - metaClient = &http.Client{ + defaultClient = &Client{hc: &http.Client{ Transport: &http.Transport{ Dial: (&net.Dialer{ Timeout: 2 * time.Second, @@ -72,15 +70,15 @@ var ( }).Dial, ResponseHeaderTimeout: 2 * time.Second, }, - } - subscribeClient = &http.Client{ + }} + subscribeClient = &Client{hc: &http.Client{ Transport: &http.Transport{ Dial: (&net.Dialer{ Timeout: 2 * time.Second, KeepAlive: 30 * time.Second, }).Dial, }, - } + }} ) // NotDefinedError is returned when requested metadata is not defined. @@ -95,74 +93,16 @@ func (suffix NotDefinedError) Error() string { return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix)) } -// Get returns a value from the metadata service. -// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". -// -// If the GCE_METADATA_HOST environment variable is not defined, a default of -// 169.254.169.254 will be used instead. -// -// If the requested metadata is not defined, the returned error will -// be of type NotDefinedError. -func Get(suffix string) (string, error) { - val, _, err := getETag(metaClient, suffix) - return val, err -} - -// getETag returns a value from the metadata service as well as the associated -// ETag using the provided client. This func is otherwise equivalent to Get. -func getETag(client *http.Client, suffix string) (value, etag string, err error) { - // Using a fixed IP makes it very difficult to spoof the metadata service in - // a container, which is an important use-case for local testing of cloud - // deployments. To enable spoofing of the metadata service, the environment - // variable GCE_METADATA_HOST is first inspected to decide where metadata - // requests shall go. - host := os.Getenv(metadataHostEnv) - if host == "" { - // Using 169.254.169.254 instead of "metadata" here because Go - // binaries built with the "netgo" tag and without cgo won't - // know the search suffix for "metadata" is - // ".google.internal", and this IP address is documented as - // being stable anyway. - host = metadataIP - } - url := "http://" + host + "/computeMetadata/v1/" + suffix - req, _ := http.NewRequest("GET", url, nil) - req.Header.Set("Metadata-Flavor", "Google") - req.Header.Set("User-Agent", userAgent) - res, err := client.Do(req) - if err != nil { - return "", "", err - } - defer res.Body.Close() - if res.StatusCode == http.StatusNotFound { - return "", "", NotDefinedError(suffix) - } - if res.StatusCode != 200 { - return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url) - } - all, err := ioutil.ReadAll(res.Body) - if err != nil { - return "", "", err - } - return string(all), res.Header.Get("Etag"), nil -} - -func getTrimmed(suffix string) (s string, err error) { - s, err = Get(suffix) - s = strings.TrimSpace(s) - return -} - -func (c *cachedValue) get() (v string, err error) { +func (c *cachedValue) get(cl *Client) (v string, err error) { defer c.mu.Unlock() c.mu.Lock() if c.v != "" { return c.v, nil } if c.trim { - v, err = getTrimmed(c.k) + v, err = cl.getTrimmed(c.k) } else { - v, err = Get(c.k) + v, err = cl.Get(c.k) } if err == nil { c.v = v @@ -197,11 +137,11 @@ func testOnGCE() bool { resc := make(chan bool, 2) // Try two strategies in parallel. - // See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194 + // See https://github.com/googleapis/google-cloud-go/issues/194 go func() { req, _ := http.NewRequest("GET", "http://"+metadataIP, nil) req.Header.Set("User-Agent", userAgent) - res, err := ctxhttp.Do(ctx, metaClient, req) + res, err := defaultClient.hc.Do(req.WithContext(ctx)) if err != nil { resc <- false return @@ -266,78 +206,183 @@ func systemInfoSuggestsGCE() bool { return name == "Google" || name == "Google Compute Engine" } -// Subscribe subscribes to a value from the metadata service. -// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". -// The suffix may contain query parameters. -// -// Subscribe calls fn with the latest metadata value indicated by the provided -// suffix. If the metadata value is deleted, fn is called with the empty string -// and ok false. Subscribe blocks until fn returns a non-nil error or the value -// is deleted. Subscribe returns the error value returned from the last call to -// fn, which may be nil when ok == false. +// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no +// ResponseHeaderTimeout). func Subscribe(suffix string, fn func(v string, ok bool) error) error { - const failedSubscribeSleep = time.Second * 5 + return subscribeClient.Subscribe(suffix, fn) +} - // First check to see if the metadata value exists at all. - val, lastETag, err := getETag(subscribeClient, suffix) - if err != nil { - return err - } +// Get calls Client.Get on the default client. +func Get(suffix string) (string, error) { return defaultClient.Get(suffix) } - if err := fn(val, true); err != nil { - return err +// ProjectID returns the current instance's project ID string. +func ProjectID() (string, error) { return defaultClient.ProjectID() } + +// NumericProjectID returns the current instance's numeric project ID. +func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() } + +// InternalIP returns the instance's primary internal IP address. +func InternalIP() (string, error) { return defaultClient.InternalIP() } + +// ExternalIP returns the instance's primary external (public) IP address. +func ExternalIP() (string, error) { return defaultClient.ExternalIP() } + +// Hostname returns the instance's hostname. This will be of the form +// ".c..internal". +func Hostname() (string, error) { return defaultClient.Hostname() } + +// InstanceTags returns the list of user-defined instance tags, +// assigned when initially creating a GCE instance. +func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() } + +// InstanceID returns the current VM's numeric instance ID. +func InstanceID() (string, error) { return defaultClient.InstanceID() } + +// InstanceName returns the current VM's instance ID string. +func InstanceName() (string, error) { return defaultClient.InstanceName() } + +// Zone returns the current VM's zone, such as "us-central1-b". +func Zone() (string, error) { return defaultClient.Zone() } + +// InstanceAttributes calls Client.InstanceAttributes on the default client. +func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() } + +// ProjectAttributes calls Client.ProjectAttributes on the default client. +func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() } + +// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client. +func InstanceAttributeValue(attr string) (string, error) { + return defaultClient.InstanceAttributeValue(attr) +} + +// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client. +func ProjectAttributeValue(attr string) (string, error) { + return defaultClient.ProjectAttributeValue(attr) +} + +// Scopes calls Client.Scopes on the default client. +func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) } + +func strsContains(ss []string, s string) bool { + for _, v := range ss { + if v == s { + return true + } } + return false +} - ok := true - if strings.ContainsRune(suffix, '?') { - suffix += "&wait_for_change=true&last_etag=" - } else { - suffix += "?wait_for_change=true&last_etag=" +// A Client provides metadata. +type Client struct { + hc *http.Client +} + +// NewClient returns a Client that can be used to fetch metadata. All HTTP requests +// will use the given http.Client instead of the default client. +func NewClient(c *http.Client) *Client { + return &Client{hc: c} +} + +// getETag returns a value from the metadata service as well as the associated ETag. +// This func is otherwise equivalent to Get. +func (c *Client) getETag(suffix string) (value, etag string, err error) { + // Using a fixed IP makes it very difficult to spoof the metadata service in + // a container, which is an important use-case for local testing of cloud + // deployments. To enable spoofing of the metadata service, the environment + // variable GCE_METADATA_HOST is first inspected to decide where metadata + // requests shall go. + host := os.Getenv(metadataHostEnv) + if host == "" { + // Using 169.254.169.254 instead of "metadata" here because Go + // binaries built with the "netgo" tag and without cgo won't + // know the search suffix for "metadata" is + // ".google.internal", and this IP address is documented as + // being stable anyway. + host = metadataIP } - for { - val, etag, err := getETag(subscribeClient, suffix+url.QueryEscape(lastETag)) - if err != nil { - if _, deleted := err.(NotDefinedError); !deleted { - time.Sleep(failedSubscribeSleep) - continue // Retry on other errors. - } - ok = false - } - lastETag = etag + u := "http://" + host + "/computeMetadata/v1/" + suffix + req, _ := http.NewRequest("GET", u, nil) + req.Header.Set("Metadata-Flavor", "Google") + req.Header.Set("User-Agent", userAgent) + res, err := c.hc.Do(req) + if err != nil { + return "", "", err + } + defer res.Body.Close() + if res.StatusCode == http.StatusNotFound { + return "", "", NotDefinedError(suffix) + } + all, err := ioutil.ReadAll(res.Body) + if err != nil { + return "", "", err + } + if res.StatusCode != 200 { + return "", "", &Error{Code: res.StatusCode, Message: string(all)} + } + return string(all), res.Header.Get("Etag"), nil +} - if err := fn(val, ok); err != nil || !ok { - return err - } +// Get returns a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// +// If the GCE_METADATA_HOST environment variable is not defined, a default of +// 169.254.169.254 will be used instead. +// +// If the requested metadata is not defined, the returned error will +// be of type NotDefinedError. +func (c *Client) Get(suffix string) (string, error) { + val, _, err := c.getETag(suffix) + return val, err +} + +func (c *Client) getTrimmed(suffix string) (s string, err error) { + s, err = c.Get(suffix) + s = strings.TrimSpace(s) + return +} + +func (c *Client) lines(suffix string) ([]string, error) { + j, err := c.Get(suffix) + if err != nil { + return nil, err } + s := strings.Split(strings.TrimSpace(j), "\n") + for i := range s { + s[i] = strings.TrimSpace(s[i]) + } + return s, nil } // ProjectID returns the current instance's project ID string. -func ProjectID() (string, error) { return projID.get() } +func (c *Client) ProjectID() (string, error) { return projID.get(c) } // NumericProjectID returns the current instance's numeric project ID. -func NumericProjectID() (string, error) { return projNum.get() } +func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) } + +// InstanceID returns the current VM's numeric instance ID. +func (c *Client) InstanceID() (string, error) { return instID.get(c) } // InternalIP returns the instance's primary internal IP address. -func InternalIP() (string, error) { - return getTrimmed("instance/network-interfaces/0/ip") +func (c *Client) InternalIP() (string, error) { + return c.getTrimmed("instance/network-interfaces/0/ip") } // ExternalIP returns the instance's primary external (public) IP address. -func ExternalIP() (string, error) { - return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip") +func (c *Client) ExternalIP() (string, error) { + return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip") } // Hostname returns the instance's hostname. This will be of the form // ".c..internal". -func Hostname() (string, error) { - return getTrimmed("instance/hostname") +func (c *Client) Hostname() (string, error) { + return c.getTrimmed("instance/hostname") } // InstanceTags returns the list of user-defined instance tags, // assigned when initially creating a GCE instance. -func InstanceTags() ([]string, error) { +func (c *Client) InstanceTags() ([]string, error) { var s []string - j, err := Get("instance/tags") + j, err := c.Get("instance/tags") if err != nil { return nil, err } @@ -347,14 +392,9 @@ func InstanceTags() ([]string, error) { return s, nil } -// InstanceID returns the current VM's numeric instance ID. -func InstanceID() (string, error) { - return instID.get() -} - // InstanceName returns the current VM's instance ID string. -func InstanceName() (string, error) { - host, err := Hostname() +func (c *Client) InstanceName() (string, error) { + host, err := c.Hostname() if err != nil { return "", err } @@ -362,8 +402,8 @@ func InstanceName() (string, error) { } // Zone returns the current VM's zone, such as "us-central1-b". -func Zone() (string, error) { - zone, err := getTrimmed("instance/zone") +func (c *Client) Zone() (string, error) { + zone, err := c.getTrimmed("instance/zone") // zone is of the form "projects//zones/". if err != nil { return "", err @@ -374,24 +414,12 @@ func Zone() (string, error) { // InstanceAttributes returns the list of user-defined attributes, // assigned when initially creating a GCE VM instance. The value of an // attribute can be obtained with InstanceAttributeValue. -func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") } +func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") } // ProjectAttributes returns the list of user-defined attributes // applying to the project as a whole, not just this VM. The value of // an attribute can be obtained with ProjectAttributeValue. -func ProjectAttributes() ([]string, error) { return lines("project/attributes/") } - -func lines(suffix string) ([]string, error) { - j, err := Get(suffix) - if err != nil { - return nil, err - } - s := strings.Split(strings.TrimSpace(j), "\n") - for i := range s { - s[i] = strings.TrimSpace(s[i]) - } - return s, nil -} +func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") } // InstanceAttributeValue returns the value of the provided VM // instance attribute. @@ -401,8 +429,8 @@ func lines(suffix string) ([]string, error) { // // InstanceAttributeValue may return ("", nil) if the attribute was // defined to be the empty string. -func InstanceAttributeValue(attr string) (string, error) { - return Get("instance/attributes/" + attr) +func (c *Client) InstanceAttributeValue(attr string) (string, error) { + return c.Get("instance/attributes/" + attr) } // ProjectAttributeValue returns the value of the provided @@ -413,25 +441,73 @@ func InstanceAttributeValue(attr string) (string, error) { // // ProjectAttributeValue may return ("", nil) if the attribute was // defined to be the empty string. -func ProjectAttributeValue(attr string) (string, error) { - return Get("project/attributes/" + attr) +func (c *Client) ProjectAttributeValue(attr string) (string, error) { + return c.Get("project/attributes/" + attr) } // Scopes returns the service account scopes for the given account. // The account may be empty or the string "default" to use the instance's // main account. -func Scopes(serviceAccount string) ([]string, error) { +func (c *Client) Scopes(serviceAccount string) ([]string, error) { if serviceAccount == "" { serviceAccount = "default" } - return lines("instance/service-accounts/" + serviceAccount + "/scopes") + return c.lines("instance/service-accounts/" + serviceAccount + "/scopes") } -func strsContains(ss []string, s string) bool { - for _, v := range ss { - if v == s { - return true +// Subscribe subscribes to a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// The suffix may contain query parameters. +// +// Subscribe calls fn with the latest metadata value indicated by the provided +// suffix. If the metadata value is deleted, fn is called with the empty string +// and ok false. Subscribe blocks until fn returns a non-nil error or the value +// is deleted. Subscribe returns the error value returned from the last call to +// fn, which may be nil when ok == false. +func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error { + const failedSubscribeSleep = time.Second * 5 + + // First check to see if the metadata value exists at all. + val, lastETag, err := c.getETag(suffix) + if err != nil { + return err + } + + if err := fn(val, true); err != nil { + return err + } + + ok := true + if strings.ContainsRune(suffix, '?') { + suffix += "&wait_for_change=true&last_etag=" + } else { + suffix += "?wait_for_change=true&last_etag=" + } + for { + val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag)) + if err != nil { + if _, deleted := err.(NotDefinedError); !deleted { + time.Sleep(failedSubscribeSleep) + continue // Retry on other errors. + } + ok = false + } + lastETag = etag + + if err := fn(val, ok); err != nil || !ok { + return err } } - return false +} + +// Error contains an error response from the server. +type Error struct { + // Code is the HTTP response status code. + Code int + // Message is the server response message. + Message string +} + +func (e *Error) Error() string { + return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message) } diff --git a/vendor/cloud.google.com/go/go.mod b/vendor/cloud.google.com/go/go.mod new file mode 100644 index 0000000000000..e3d7d7e62391d --- /dev/null +++ b/vendor/cloud.google.com/go/go.mod @@ -0,0 +1,29 @@ +module cloud.google.com/go + +go 1.9 + +require ( + cloud.google.com/go/datastore v1.0.0 + github.com/golang/mock v1.3.1 + github.com/golang/protobuf v1.3.2 + github.com/google/btree v1.0.0 + github.com/google/go-cmp v0.3.0 + github.com/google/martian v2.1.0+incompatible + github.com/google/pprof v0.0.0-20190515194954-54271f7e092f + github.com/googleapis/gax-go/v2 v2.0.5 + github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 + go.opencensus.io v0.22.0 + golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522 + golang.org/x/lint v0.0.0-20190409202823-959b441ac422 + golang.org/x/net v0.0.0-20190620200207-3b0461eec859 + golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 + golang.org/x/sync v0.0.0-20190423024810-112230192c58 + golang.org/x/text v0.3.2 + golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 + golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0 + google.golang.org/api v0.8.0 + google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 + google.golang.org/grpc v1.21.1 + honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a + rsc.io/binaryregexp v0.2.0 +) diff --git a/vendor/cloud.google.com/go/internal/version/version.go b/vendor/cloud.google.com/go/internal/version/version.go index f5c23a564c604..d291921b18f92 100644 --- a/vendor/cloud.google.com/go/internal/version/version.go +++ b/vendor/cloud.google.com/go/internal/version/version.go @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All Rights Reserved. +// Copyright 2016 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ import ( // Repo is the current version of the client libraries in this // repo. It should be a date in YYYYMMDD format. -const Repo = "20180226" +const Repo = "20190802" // Go returns the Go runtime version. The returned string // has no whitespace. @@ -67,5 +67,5 @@ func goVer(s string) string { } func notSemverRune(r rune) bool { - return strings.IndexRune("0123456789.", r) < 0 + return !strings.ContainsRune("0123456789.", r) } diff --git a/vendor/cloud.google.com/go/logging/README.md b/vendor/cloud.google.com/go/logging/README.md new file mode 100644 index 0000000000000..06050110e4eed --- /dev/null +++ b/vendor/cloud.google.com/go/logging/README.md @@ -0,0 +1,35 @@ +## Stackdriver Logging [![GoDoc](https://godoc.org/cloud.google.com/go/logging?status.svg)](https://godoc.org/cloud.google.com/go/logging) + +- [About Stackdriver Logging](https://cloud.google.com/logging/) +- [API documentation](https://cloud.google.com/logging/docs) +- [Go client documentation](https://godoc.org/cloud.google.com/go/logging) +- [Complete sample programs](https://github.com/GoogleCloudPlatform/golang-samples/tree/master/logging) + +### Example Usage + +First create a `logging.Client` to use throughout your application: +[snip]:# (logging-1) +```go +ctx := context.Background() +client, err := logging.NewClient(ctx, "my-project") +if err != nil { + // TODO: Handle error. +} +``` + +Usually, you'll want to add log entries to a buffer to be periodically flushed +(automatically and asynchronously) to the Stackdriver Logging service. +[snip]:# (logging-2) +```go +logger := client.Logger("my-log") +logger.Log(logging.Entry{Payload: "something happened!"}) +``` + +Close your client before your program exits, to flush any buffered log entries. +[snip]:# (logging-3) +```go +err = client.Close() +if err != nil { + // TODO: Handle error. +} +``` \ No newline at end of file diff --git a/vendor/cloud.google.com/go/logging/apiv2/config_client.go b/vendor/cloud.google.com/go/logging/apiv2/config_client.go index bad7a3a4834e4..747158714fd2b 100644 --- a/vendor/cloud.google.com/go/logging/apiv2/config_client.go +++ b/vendor/cloud.google.com/go/logging/apiv2/config_client.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,17 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -// AUTO-GENERATED CODE. DO NOT EDIT. +// Code generated by gapic-generator. DO NOT EDIT. package logging import ( + "context" + "fmt" "math" + "net/url" "time" - "cloud.google.com/go/internal/version" - gax "github.com/googleapis/gax-go" - "golang.org/x/net/context" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go/v2" "google.golang.org/api/iterator" "google.golang.org/api/option" "google.golang.org/api/transport" @@ -63,8 +65,8 @@ func defaultConfigCallOptions() *ConfigCallOptions { codes.Unavailable, }, gax.Backoff{ Initial: 100 * time.Millisecond, - Max: 1000 * time.Millisecond, - Multiplier: 1.2, + Max: 60000 * time.Millisecond, + Multiplier: 1.3, }) }), }, @@ -73,7 +75,7 @@ func defaultConfigCallOptions() *ConfigCallOptions { ListSinks: retry[[2]string{"default", "idempotent"}], GetSink: retry[[2]string{"default", "idempotent"}], CreateSink: retry[[2]string{"default", "non_idempotent"}], - UpdateSink: retry[[2]string{"default", "non_idempotent"}], + UpdateSink: retry[[2]string{"default", "idempotent"}], DeleteSink: retry[[2]string{"default", "idempotent"}], ListExclusions: retry[[2]string{"default", "idempotent"}], GetExclusion: retry[[2]string{"default", "idempotent"}], @@ -84,6 +86,8 @@ func defaultConfigCallOptions() *ConfigCallOptions { } // ConfigClient is a client for interacting with Stackdriver Logging API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. type ConfigClient struct { // The connection to the service. conn *grpc.ClientConn @@ -100,8 +104,8 @@ type ConfigClient struct { // NewConfigClient creates a new config service v2 client. // -// Service for configuring sinks used to export log entries outside of -// Stackdriver Logging. +// Service for configuring sinks used to export log entries out of +// Logging. func NewConfigClient(ctx context.Context, opts ...option.ClientOption) (*ConfigClient, error) { conn, err := transport.DialGRPC(ctx, append(defaultConfigClientOptions(), opts...)...) if err != nil { @@ -132,16 +136,18 @@ func (c *ConfigClient) Close() error { // the `x-goog-api-client` header passed on each request. Intended for // use by Google-written clients. func (c *ConfigClient) SetGoogleClientInfo(keyval ...string) { - kv := append([]string{"gl-go", version.Go()}, keyval...) - kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + kv := append([]string{"gl-go", versionGo()}, keyval...) + kv = append(kv, "gapic", versionClient, "gax", gax.Version, "grpc", grpc.Version) c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) } // ListSinks lists sinks. func (c *ConfigClient) ListSinks(ctx context.Context, req *loggingpb.ListSinksRequest, opts ...gax.CallOption) *LogSinkIterator { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.ListSinks[0:len(c.CallOptions.ListSinks):len(c.CallOptions.ListSinks)], opts...) it := &LogSinkIterator{} + req = proto.Clone(req).(*loggingpb.ListSinksRequest) it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogSink, string, error) { var resp *loggingpb.ListSinksResponse req.PageToken = pageToken @@ -169,12 +175,15 @@ func (c *ConfigClient) ListSinks(ctx context.Context, req *loggingpb.ListSinksRe return nextPageToken, nil } it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) + it.pageInfo.MaxSize = int(req.PageSize) + it.pageInfo.Token = req.PageToken return it } // GetSink gets a sink. func (c *ConfigClient) GetSink(ctx context.Context, req *loggingpb.GetSinkRequest, opts ...gax.CallOption) (*loggingpb.LogSink, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "sink_name", url.QueryEscape(req.GetSinkName()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.GetSink[0:len(c.CallOptions.GetSink):len(c.CallOptions.GetSink)], opts...) var resp *loggingpb.LogSink err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { @@ -193,7 +202,8 @@ func (c *ConfigClient) GetSink(ctx context.Context, req *loggingpb.GetSinkReques // writer_identity is not permitted to write to the destination. A sink can // export log entries only from the resource owning the sink. func (c *ConfigClient) CreateSink(ctx context.Context, req *loggingpb.CreateSinkRequest, opts ...gax.CallOption) (*loggingpb.LogSink, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.CreateSink[0:len(c.CallOptions.CreateSink):len(c.CallOptions.CreateSink)], opts...) var resp *loggingpb.LogSink err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { @@ -212,7 +222,8 @@ func (c *ConfigClient) CreateSink(ctx context.Context, req *loggingpb.CreateSink // The updated sink might also have a new writer_identity; see the // unique_writer_identity field. func (c *ConfigClient) UpdateSink(ctx context.Context, req *loggingpb.UpdateSinkRequest, opts ...gax.CallOption) (*loggingpb.LogSink, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "sink_name", url.QueryEscape(req.GetSinkName()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.UpdateSink[0:len(c.CallOptions.UpdateSink):len(c.CallOptions.UpdateSink)], opts...) var resp *loggingpb.LogSink err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { @@ -229,7 +240,8 @@ func (c *ConfigClient) UpdateSink(ctx context.Context, req *loggingpb.UpdateSink // DeleteSink deletes a sink. If the sink has a unique writer_identity, then that // service account is also deleted. func (c *ConfigClient) DeleteSink(ctx context.Context, req *loggingpb.DeleteSinkRequest, opts ...gax.CallOption) error { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "sink_name", url.QueryEscape(req.GetSinkName()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.DeleteSink[0:len(c.CallOptions.DeleteSink):len(c.CallOptions.DeleteSink)], opts...) err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error @@ -241,9 +253,11 @@ func (c *ConfigClient) DeleteSink(ctx context.Context, req *loggingpb.DeleteSink // ListExclusions lists all the exclusions in a parent resource. func (c *ConfigClient) ListExclusions(ctx context.Context, req *loggingpb.ListExclusionsRequest, opts ...gax.CallOption) *LogExclusionIterator { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.ListExclusions[0:len(c.CallOptions.ListExclusions):len(c.CallOptions.ListExclusions)], opts...) it := &LogExclusionIterator{} + req = proto.Clone(req).(*loggingpb.ListExclusionsRequest) it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogExclusion, string, error) { var resp *loggingpb.ListExclusionsResponse req.PageToken = pageToken @@ -271,12 +285,15 @@ func (c *ConfigClient) ListExclusions(ctx context.Context, req *loggingpb.ListEx return nextPageToken, nil } it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) + it.pageInfo.MaxSize = int(req.PageSize) + it.pageInfo.Token = req.PageToken return it } // GetExclusion gets the description of an exclusion. func (c *ConfigClient) GetExclusion(ctx context.Context, req *loggingpb.GetExclusionRequest, opts ...gax.CallOption) (*loggingpb.LogExclusion, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", url.QueryEscape(req.GetName()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.GetExclusion[0:len(c.CallOptions.GetExclusion):len(c.CallOptions.GetExclusion)], opts...) var resp *loggingpb.LogExclusion err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { @@ -294,7 +311,8 @@ func (c *ConfigClient) GetExclusion(ctx context.Context, req *loggingpb.GetExclu // Only log entries belonging to that resource can be excluded. // You can have up to 10 exclusions in a resource. func (c *ConfigClient) CreateExclusion(ctx context.Context, req *loggingpb.CreateExclusionRequest, opts ...gax.CallOption) (*loggingpb.LogExclusion, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.CreateExclusion[0:len(c.CallOptions.CreateExclusion):len(c.CallOptions.CreateExclusion)], opts...) var resp *loggingpb.LogExclusion err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { @@ -310,7 +328,8 @@ func (c *ConfigClient) CreateExclusion(ctx context.Context, req *loggingpb.Creat // UpdateExclusion changes one or more properties of an existing exclusion. func (c *ConfigClient) UpdateExclusion(ctx context.Context, req *loggingpb.UpdateExclusionRequest, opts ...gax.CallOption) (*loggingpb.LogExclusion, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", url.QueryEscape(req.GetName()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.UpdateExclusion[0:len(c.CallOptions.UpdateExclusion):len(c.CallOptions.UpdateExclusion)], opts...) var resp *loggingpb.LogExclusion err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { @@ -326,7 +345,8 @@ func (c *ConfigClient) UpdateExclusion(ctx context.Context, req *loggingpb.Updat // DeleteExclusion deletes an exclusion. func (c *ConfigClient) DeleteExclusion(ctx context.Context, req *loggingpb.DeleteExclusionRequest, opts ...gax.CallOption) error { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "name", url.QueryEscape(req.GetName()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.DeleteExclusion[0:len(c.CallOptions.DeleteExclusion):len(c.CallOptions.DeleteExclusion)], opts...) err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error diff --git a/vendor/cloud.google.com/go/logging/apiv2/doc.go b/vendor/cloud.google.com/go/logging/apiv2/doc.go index b8087b020f0a3..cd6d8f7421ec4 100644 --- a/vendor/cloud.google.com/go/logging/apiv2/doc.go +++ b/vendor/cloud.google.com/go/logging/apiv2/doc.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,20 +12,35 @@ // See the License for the specific language governing permissions and // limitations under the License. -// AUTO-GENERATED CODE. DO NOT EDIT. +// Code generated by gapic-generator. DO NOT EDIT. // Package logging is an auto-generated package for the // Stackdriver Logging API. // // NOTE: This package is in alpha. It is not stable, and is likely to change. // -// Writes log entries and manages your Stackdriver Logging configuration. +// Writes log entries and manages your Logging configuration. +// +// Use of Context +// +// The ctx passed to NewClient is used for authentication requests and +// for creating the underlying connection, but is not used for subsequent calls. +// Individual methods on the client use the ctx given to them. +// +// To close the open connection, use the Close() method. +// +// For information about setting deadlines, reusing contexts, and more +// please visit godoc.org/cloud.google.com/go. // // Use the client at cloud.google.com/go/logging in preference to this. package logging // import "cloud.google.com/go/logging/apiv2" import ( - "golang.org/x/net/context" + "context" + "runtime" + "strings" + "unicode" + "google.golang.org/grpc/metadata" ) @@ -50,3 +65,42 @@ func DefaultAuthScopes() []string { "https://www.googleapis.com/auth/logging.write", } } + +// versionGo returns the Go runtime version. The returned string +// has no whitespace, suitable for reporting in header. +func versionGo() string { + const develPrefix = "devel +" + + s := runtime.Version() + if strings.HasPrefix(s, develPrefix) { + s = s[len(develPrefix):] + if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 { + s = s[:p] + } + return s + } + + notSemverRune := func(r rune) bool { + return strings.IndexRune("0123456789.", r) < 0 + } + + if strings.HasPrefix(s, "go1") { + s = s[2:] + var prerelease string + if p := strings.IndexFunc(s, notSemverRune); p >= 0 { + s, prerelease = s[:p], s[p:] + } + if strings.HasSuffix(s, ".") { + s += "0" + } else if strings.Count(s, ".") < 2 { + s += ".0" + } + if prerelease != "" { + s += "-" + prerelease + } + return s + } + return "UNKNOWN" +} + +const versionClient = "20190801" diff --git a/vendor/cloud.google.com/go/logging/apiv2/logging_client.go b/vendor/cloud.google.com/go/logging/apiv2/logging_client.go index 90dae0ab4a1c6..f39bdce45759f 100644 --- a/vendor/cloud.google.com/go/logging/apiv2/logging_client.go +++ b/vendor/cloud.google.com/go/logging/apiv2/logging_client.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,17 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -// AUTO-GENERATED CODE. DO NOT EDIT. +// Code generated by gapic-generator. DO NOT EDIT. package logging import ( + "context" + "fmt" "math" + "net/url" "time" - "cloud.google.com/go/internal/version" - gax "github.com/googleapis/gax-go" - "golang.org/x/net/context" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go/v2" "google.golang.org/api/iterator" "google.golang.org/api/option" "google.golang.org/api/transport" @@ -59,35 +61,24 @@ func defaultCallOptions() *CallOptions { codes.Unavailable, }, gax.Backoff{ Initial: 100 * time.Millisecond, - Max: 1000 * time.Millisecond, - Multiplier: 1.2, - }) - }), - }, - {"list", "idempotent"}: { - gax.WithRetry(func() gax.Retryer { - return gax.OnCodes([]codes.Code{ - codes.DeadlineExceeded, - codes.Internal, - codes.Unavailable, - }, gax.Backoff{ - Initial: 100 * time.Millisecond, - Max: 1000 * time.Millisecond, - Multiplier: 1.2, + Max: 60000 * time.Millisecond, + Multiplier: 1.3, }) }), }, } return &CallOptions{ DeleteLog: retry[[2]string{"default", "idempotent"}], - WriteLogEntries: retry[[2]string{"default", "non_idempotent"}], - ListLogEntries: retry[[2]string{"list", "idempotent"}], + WriteLogEntries: retry[[2]string{"default", "idempotent"}], + ListLogEntries: retry[[2]string{"default", "idempotent"}], ListMonitoredResourceDescriptors: retry[[2]string{"default", "idempotent"}], - ListLogs: retry[[2]string{"default", "idempotent"}], + ListLogs: retry[[2]string{"default", "idempotent"}], } } // Client is a client for interacting with Stackdriver Logging API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. type Client struct { // The connection to the service. conn *grpc.ClientConn @@ -135,8 +126,8 @@ func (c *Client) Close() error { // the `x-goog-api-client` header passed on each request. Intended for // use by Google-written clients. func (c *Client) SetGoogleClientInfo(keyval ...string) { - kv := append([]string{"gl-go", version.Go()}, keyval...) - kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + kv := append([]string{"gl-go", versionGo()}, keyval...) + kv = append(kv, "gapic", versionClient, "gax", gax.Version, "grpc", grpc.Version) c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) } @@ -145,7 +136,8 @@ func (c *Client) SetGoogleClientInfo(keyval ...string) { // Log entries written shortly before the delete operation might not be // deleted. func (c *Client) DeleteLog(ctx context.Context, req *loggingpb.DeleteLogRequest, opts ...gax.CallOption) error { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "log_name", url.QueryEscape(req.GetLogName()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.DeleteLog[0:len(c.CallOptions.DeleteLog):len(c.CallOptions.DeleteLog)], opts...) err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error @@ -155,13 +147,13 @@ func (c *Client) DeleteLog(ctx context.Context, req *loggingpb.DeleteLogRequest, return err } -// WriteLogEntries ## Log entry resources -// -// Writes log entries to Stackdriver Logging. This API method is the -// only way to send log entries to Stackdriver Logging. This method -// is used, directly or indirectly, by the Stackdriver Logging agent -// (fluentd) and all logging libraries configured to use Stackdriver -// Logging. +// WriteLogEntries writes log entries to Logging. This API method is the +// only way to send log entries to Logging. This method +// is used, directly or indirectly, by the Logging agent +// (fluentd) and all logging libraries configured to use Logging. +// A single request may contain log entries for a maximum of 1000 +// different resources (projects, organizations, billing accounts or +// folders) func (c *Client) WriteLogEntries(ctx context.Context, req *loggingpb.WriteLogEntriesRequest, opts ...gax.CallOption) (*loggingpb.WriteLogEntriesResponse, error) { ctx = insertMetadata(ctx, c.xGoogMetadata) opts = append(c.CallOptions.WriteLogEntries[0:len(c.CallOptions.WriteLogEntries):len(c.CallOptions.WriteLogEntries)], opts...) @@ -178,12 +170,13 @@ func (c *Client) WriteLogEntries(ctx context.Context, req *loggingpb.WriteLogEnt } // ListLogEntries lists log entries. Use this method to retrieve log entries from -// Stackdriver Logging. For ways to export log entries, see +// Logging. For ways to export log entries, see // Exporting Logs (at /logging/docs/export). func (c *Client) ListLogEntries(ctx context.Context, req *loggingpb.ListLogEntriesRequest, opts ...gax.CallOption) *LogEntryIterator { ctx = insertMetadata(ctx, c.xGoogMetadata) opts = append(c.CallOptions.ListLogEntries[0:len(c.CallOptions.ListLogEntries):len(c.CallOptions.ListLogEntries)], opts...) it := &LogEntryIterator{} + req = proto.Clone(req).(*loggingpb.ListLogEntriesRequest) it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogEntry, string, error) { var resp *loggingpb.ListLogEntriesResponse req.PageToken = pageToken @@ -211,15 +204,17 @@ func (c *Client) ListLogEntries(ctx context.Context, req *loggingpb.ListLogEntri return nextPageToken, nil } it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) + it.pageInfo.MaxSize = int(req.PageSize) + it.pageInfo.Token = req.PageToken return it } -// ListMonitoredResourceDescriptors lists the descriptors for monitored resource types used by Stackdriver -// Logging. +// ListMonitoredResourceDescriptors lists the descriptors for monitored resource types used by Logging. func (c *Client) ListMonitoredResourceDescriptors(ctx context.Context, req *loggingpb.ListMonitoredResourceDescriptorsRequest, opts ...gax.CallOption) *MonitoredResourceDescriptorIterator { ctx = insertMetadata(ctx, c.xGoogMetadata) opts = append(c.CallOptions.ListMonitoredResourceDescriptors[0:len(c.CallOptions.ListMonitoredResourceDescriptors):len(c.CallOptions.ListMonitoredResourceDescriptors)], opts...) it := &MonitoredResourceDescriptorIterator{} + req = proto.Clone(req).(*loggingpb.ListMonitoredResourceDescriptorsRequest) it.InternalFetch = func(pageSize int, pageToken string) ([]*monitoredrespb.MonitoredResourceDescriptor, string, error) { var resp *loggingpb.ListMonitoredResourceDescriptorsResponse req.PageToken = pageToken @@ -247,15 +242,19 @@ func (c *Client) ListMonitoredResourceDescriptors(ctx context.Context, req *logg return nextPageToken, nil } it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) + it.pageInfo.MaxSize = int(req.PageSize) + it.pageInfo.Token = req.PageToken return it } // ListLogs lists the logs in projects, organizations, folders, or billing accounts. // Only logs that have entries are listed. func (c *Client) ListLogs(ctx context.Context, req *loggingpb.ListLogsRequest, opts ...gax.CallOption) *StringIterator { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.ListLogs[0:len(c.CallOptions.ListLogs):len(c.CallOptions.ListLogs)], opts...) it := &StringIterator{} + req = proto.Clone(req).(*loggingpb.ListLogsRequest) it.InternalFetch = func(pageSize int, pageToken string) ([]string, string, error) { var resp *loggingpb.ListLogsResponse req.PageToken = pageToken @@ -283,6 +282,8 @@ func (c *Client) ListLogs(ctx context.Context, req *loggingpb.ListLogsRequest, o return nextPageToken, nil } it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) + it.pageInfo.MaxSize = int(req.PageSize) + it.pageInfo.Token = req.PageToken return it } diff --git a/vendor/cloud.google.com/go/logging/apiv2/metrics_client.go b/vendor/cloud.google.com/go/logging/apiv2/metrics_client.go index e2f96612b3dab..3b70ab741f51f 100644 --- a/vendor/cloud.google.com/go/logging/apiv2/metrics_client.go +++ b/vendor/cloud.google.com/go/logging/apiv2/metrics_client.go @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,17 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -// AUTO-GENERATED CODE. DO NOT EDIT. +// Code generated by gapic-generator. DO NOT EDIT. package logging import ( + "context" + "fmt" "math" + "net/url" "time" - "cloud.google.com/go/internal/version" - gax "github.com/googleapis/gax-go" - "golang.org/x/net/context" + "github.com/golang/protobuf/proto" + gax "github.com/googleapis/gax-go/v2" "google.golang.org/api/iterator" "google.golang.org/api/option" "google.golang.org/api/transport" @@ -58,8 +60,8 @@ func defaultMetricsCallOptions() *MetricsCallOptions { codes.Unavailable, }, gax.Backoff{ Initial: 100 * time.Millisecond, - Max: 1000 * time.Millisecond, - Multiplier: 1.2, + Max: 60000 * time.Millisecond, + Multiplier: 1.3, }) }), }, @@ -68,12 +70,14 @@ func defaultMetricsCallOptions() *MetricsCallOptions { ListLogMetrics: retry[[2]string{"default", "idempotent"}], GetLogMetric: retry[[2]string{"default", "idempotent"}], CreateLogMetric: retry[[2]string{"default", "non_idempotent"}], - UpdateLogMetric: retry[[2]string{"default", "non_idempotent"}], + UpdateLogMetric: retry[[2]string{"default", "idempotent"}], DeleteLogMetric: retry[[2]string{"default", "idempotent"}], } } // MetricsClient is a client for interacting with Stackdriver Logging API. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. type MetricsClient struct { // The connection to the service. conn *grpc.ClientConn @@ -121,16 +125,18 @@ func (c *MetricsClient) Close() error { // the `x-goog-api-client` header passed on each request. Intended for // use by Google-written clients. func (c *MetricsClient) SetGoogleClientInfo(keyval ...string) { - kv := append([]string{"gl-go", version.Go()}, keyval...) - kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) + kv := append([]string{"gl-go", versionGo()}, keyval...) + kv = append(kv, "gapic", versionClient, "gax", gax.Version, "grpc", grpc.Version) c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) } // ListLogMetrics lists logs-based metrics. func (c *MetricsClient) ListLogMetrics(ctx context.Context, req *loggingpb.ListLogMetricsRequest, opts ...gax.CallOption) *LogMetricIterator { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.ListLogMetrics[0:len(c.CallOptions.ListLogMetrics):len(c.CallOptions.ListLogMetrics)], opts...) it := &LogMetricIterator{} + req = proto.Clone(req).(*loggingpb.ListLogMetricsRequest) it.InternalFetch = func(pageSize int, pageToken string) ([]*loggingpb.LogMetric, string, error) { var resp *loggingpb.ListLogMetricsResponse req.PageToken = pageToken @@ -158,12 +164,15 @@ func (c *MetricsClient) ListLogMetrics(ctx context.Context, req *loggingpb.ListL return nextPageToken, nil } it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) + it.pageInfo.MaxSize = int(req.PageSize) + it.pageInfo.Token = req.PageToken return it } // GetLogMetric gets a logs-based metric. func (c *MetricsClient) GetLogMetric(ctx context.Context, req *loggingpb.GetLogMetricRequest, opts ...gax.CallOption) (*loggingpb.LogMetric, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "metric_name", url.QueryEscape(req.GetMetricName()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.GetLogMetric[0:len(c.CallOptions.GetLogMetric):len(c.CallOptions.GetLogMetric)], opts...) var resp *loggingpb.LogMetric err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { @@ -179,7 +188,8 @@ func (c *MetricsClient) GetLogMetric(ctx context.Context, req *loggingpb.GetLogM // CreateLogMetric creates a logs-based metric. func (c *MetricsClient) CreateLogMetric(ctx context.Context, req *loggingpb.CreateLogMetricRequest, opts ...gax.CallOption) (*loggingpb.LogMetric, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "parent", url.QueryEscape(req.GetParent()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.CreateLogMetric[0:len(c.CallOptions.CreateLogMetric):len(c.CallOptions.CreateLogMetric)], opts...) var resp *loggingpb.LogMetric err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { @@ -195,7 +205,8 @@ func (c *MetricsClient) CreateLogMetric(ctx context.Context, req *loggingpb.Crea // UpdateLogMetric creates or updates a logs-based metric. func (c *MetricsClient) UpdateLogMetric(ctx context.Context, req *loggingpb.UpdateLogMetricRequest, opts ...gax.CallOption) (*loggingpb.LogMetric, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "metric_name", url.QueryEscape(req.GetMetricName()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.UpdateLogMetric[0:len(c.CallOptions.UpdateLogMetric):len(c.CallOptions.UpdateLogMetric)], opts...) var resp *loggingpb.LogMetric err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { @@ -211,7 +222,8 @@ func (c *MetricsClient) UpdateLogMetric(ctx context.Context, req *loggingpb.Upda // DeleteLogMetric deletes a logs-based metric. func (c *MetricsClient) DeleteLogMetric(ctx context.Context, req *loggingpb.DeleteLogMetricRequest, opts ...gax.CallOption) error { - ctx = insertMetadata(ctx, c.xGoogMetadata) + md := metadata.Pairs("x-goog-request-params", fmt.Sprintf("%s=%v", "metric_name", url.QueryEscape(req.GetMetricName()))) + ctx = insertMetadata(ctx, c.xGoogMetadata, md) opts = append(c.CallOptions.DeleteLogMetric[0:len(c.CallOptions.DeleteLogMetric):len(c.CallOptions.DeleteLogMetric)], opts...) err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error diff --git a/vendor/cloud.google.com/go/logging/doc.go b/vendor/cloud.google.com/go/logging/doc.go index d5f425e557cd5..2a2a4ce478404 100644 --- a/vendor/cloud.google.com/go/logging/doc.go +++ b/vendor/cloud.google.com/go/logging/doc.go @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All Rights Reserved. +// Copyright 2016 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,9 +21,6 @@ This client uses Logging API v2. See https://cloud.google.com/logging/docs/api/v2/ for an introduction to the API. -Note: This package is in beta. Some backwards-incompatible changes may occur. - - Creating a Client Use a Client to interact with the Stackdriver Logging API. @@ -65,7 +62,10 @@ For critical errors, you may want to send your log entries immediately. LogSync is slow and will block until the log entry has been sent, so it is not recommended for normal use. - lg.LogSync(ctx, logging.Entry{Payload: "ALERT! Something critical happened!"}) + err = lg.LogSync(ctx, logging.Entry{Payload: "ALERT! Something critical happened!"}) + if err != nil { + // TODO: Handle error. + } Payloads @@ -85,11 +85,11 @@ If you have a []byte of JSON, wrap it in json.RawMessage: lg.Log(logging.Entry{Payload: json.RawMessage(j)}) -The Standard Logger Interface +The Standard Logger You may want use a standard log.Logger in your program. - // stdlg implements log.Logger + // stdlg is an instance of *log.Logger. stdlg := lg.StandardLogger(logging.Info) stdlg.Println("some info") @@ -113,5 +113,22 @@ running from a Google Cloud Platform VM, select "GCE VM Instance". Otherwise, se accounts can be viewed on the command line with the "gcloud logging read" command. +Grouping Logs by Request + +To group all the log entries written during a single HTTP request, create two +Loggers, a "parent" and a "child," with different log IDs. Both should be in the same +project, and have the same MonitoredResouce type and labels. + +- Parent entries must have HTTPRequest.Request populated. (Strictly speaking, only the URL is necessary.) + +- A child entry's timestamp must be within the time interval covered by the parent request (i.e., older +than parent.Timestamp, and newer than parent.Timestamp - parent.HTTPRequest.Latency, assuming the +parent timestamp marks the end of the request. + +- The trace field must be populated in all of the entries and match exactly. + +You should observe the child log entries grouped under the parent on the console. The +parent entry will not inherit the severity of its children; you must update the +parent severity yourself. */ package logging // import "cloud.google.com/go/logging" diff --git a/vendor/cloud.google.com/go/logging/go.mod b/vendor/cloud.google.com/go/logging/go.mod new file mode 100644 index 0000000000000..8739fdf496f10 --- /dev/null +++ b/vendor/cloud.google.com/go/logging/go.mod @@ -0,0 +1,15 @@ +module cloud.google.com/go/logging + +go 1.9 + +require ( + cloud.google.com/go v0.43.0 + github.com/golang/protobuf v1.3.1 + github.com/google/go-cmp v0.3.0 + github.com/googleapis/gax-go/v2 v2.0.5 + go.opencensus.io v0.22.0 + golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 + google.golang.org/api v0.7.0 + google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 + google.golang.org/grpc v1.21.1 +) diff --git a/vendor/cloud.google.com/go/logging/go_mod_tidy_hack.go b/vendor/cloud.google.com/go/logging/go_mod_tidy_hack.go new file mode 100644 index 0000000000000..a932c70c41e0e --- /dev/null +++ b/vendor/cloud.google.com/go/logging/go_mod_tidy_hack.go @@ -0,0 +1,22 @@ +// Copyright 2019 Google LLC +// +// 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. + +// This file, and the cloud.google.com/go import, won't actually become part of +// the resultant binary. +// +build modhack + +package logging + +// Necessary for safely adding multi-module repo. See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository +import _ "cloud.google.com/go" diff --git a/vendor/cloud.google.com/go/logging/internal/common.go b/vendor/cloud.google.com/go/logging/internal/common.go index 38cfbb5fa292e..c5788feb0b2c9 100644 --- a/vendor/cloud.google.com/go/logging/internal/common.go +++ b/vendor/cloud.google.com/go/logging/internal/common.go @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All Rights Reserved. +// Copyright 2016 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -20,15 +20,17 @@ import ( ) const ( + // ProdAddr is the production address. ProdAddr = "logging.googleapis.com:443" - Version = "0.2.0" ) +// LogPath creates a formatted path from a parent and a logID. func LogPath(parent, logID string) string { logID = strings.Replace(logID, "/", "%2F", -1) return fmt.Sprintf("%s/logs/%s", parent, logID) } +// LogIDFromPath parses and returns the ID from a log path. func LogIDFromPath(parent, path string) string { start := len(parent) + len("/logs/") if len(path) < start { diff --git a/vendor/cloud.google.com/go/logging/logging.go b/vendor/cloud.google.com/go/logging/logging.go index b341f61f7f215..c537675f12165 100644 --- a/vendor/cloud.google.com/go/logging/logging.go +++ b/vendor/cloud.google.com/go/logging/logging.go @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All Rights Reserved. +// Copyright 2016 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,16 +25,19 @@ package logging import ( + "bytes" + "context" "encoding/json" "errors" "fmt" "log" - "math" "net/http" + "regexp" "strconv" "strings" "sync" "time" + "unicode/utf8" "cloud.google.com/go/compute/metadata" "cloud.google.com/go/internal/version" @@ -44,7 +47,6 @@ import ( "github.com/golang/protobuf/ptypes" structpb "github.com/golang/protobuf/ptypes/struct" tspb "github.com/golang/protobuf/ptypes/timestamp" - "golang.org/x/net/context" "google.golang.org/api/option" "google.golang.org/api/support/bundler" mrpb "google.golang.org/genproto/googleapis/api/monitoredres" @@ -53,13 +55,13 @@ import ( ) const ( - // Scope for reading from the logging service. + // ReadScope is the scope for reading from the logging service. ReadScope = "https://www.googleapis.com/auth/logging.read" - // Scope for writing to the logging service. + // WriteScope is the scope for writing to the logging service. WriteScope = "https://www.googleapis.com/auth/logging.write" - // Scope for administrative actions on the logging service. + // AdminScope is the scope for administrative actions on the logging service. AdminScope = "https://www.googleapis.com/auth/logging.admin" ) @@ -234,7 +236,7 @@ type Logger struct { // Options commonResource *mrpb.MonitoredResource commonLabels map[string]string - writeTimeout time.Duration + ctxFunc func() (context.Context, func()) } // A LoggerOption is a configuration option for a Logger. @@ -274,12 +276,17 @@ func detectResource() *mrpb.MonitoredResource { if err != nil { return } + name, err := metadata.InstanceName() + if err != nil { + return + } detectedResource.pb = &mrpb.MonitoredResource{ Type: "gce_instance", Labels: map[string]string{ - "project_id": projectID, - "instance_id": id, - "zone": zone, + "project_id": projectID, + "instance_id": id, + "instance_name": name, + "zone": zone, }, } }) @@ -398,6 +405,23 @@ type bufferedByteLimit int func (b bufferedByteLimit) set(l *Logger) { l.bundler.BufferedByteLimit = int(b) } +// ContextFunc is a function that will be called to obtain a context.Context for the +// WriteLogEntries RPC executed in the background for calls to Logger.Log. The +// default is a function that always returns context.Background. The second return +// value of the function is a function to call after the RPC completes. +// +// The function is not used for calls to Logger.LogSync, since the caller can pass +// in the context directly. +// +// This option is EXPERIMENTAL. It may be changed or removed. +func ContextFunc(f func() (ctx context.Context, afterCall func())) LoggerOption { + return contextFunc(f) +} + +type contextFunc func() (ctx context.Context, afterCall func()) + +func (c contextFunc) set(l *Logger) { l.ctxFunc = c } + // Logger returns a Logger that will write entries with the given log ID, such as // "syslog". A log ID must be less than 512 characters long and can only // include the following characters: upper and lower case alphanumeric @@ -412,6 +436,7 @@ func (c *Client) Logger(logID string, opts ...LoggerOption) *Logger { client: c, logName: internal.LogPath(c.parent, logID), commonResource: r, + ctxFunc: func() (context.Context, func()) { return context.Background(), nil }, } l.bundler = bundler.NewBundler(&logpb.LogEntry{}, func(entries interface{}) { l.writeLogEntries(entries.([]*logpb.LogEntry)) @@ -578,6 +603,17 @@ type Entry struct { // if any. If it contains a relative resource name, the name is assumed to // be relative to //tracing.googleapis.com. Trace string + + // ID of the span within the trace associated with the log entry. + // The ID is a 16-character hexadecimal encoding of an 8-byte array. + SpanID string + + // If set, symbolizes that this request was sampled. + TraceSampled bool + + // Optional. Source code location information associated with the log entry, + // if any. + SourceLocation *logpb.LogEntrySourceLocation } // HTTPRequest contains an http.Request as well as additional @@ -631,7 +667,7 @@ func fromHTTPRequest(r *HTTPRequest) *logtypepb.HttpRequest { u.Fragment = "" pb := &logtypepb.HttpRequest{ RequestMethod: r.Request.Method, - RequestUrl: u.String(), + RequestUrl: fixUTF8(u.String()), RequestSize: r.RequestSize, Status: int32(r.Status), ResponseSize: r.ResponseSize, @@ -648,6 +684,27 @@ func fromHTTPRequest(r *HTTPRequest) *logtypepb.HttpRequest { return pb } +// fixUTF8 is a helper that fixes an invalid UTF-8 string by replacing +// invalid UTF-8 runes with the Unicode replacement character (U+FFFD). +// See Issue https://github.com/googleapis/google-cloud-go/issues/1383. +func fixUTF8(s string) string { + if utf8.ValidString(s) { + return s + } + + // Otherwise time to build the sequence. + buf := new(bytes.Buffer) + buf.Grow(len(s)) + for _, r := range s { + if utf8.ValidRune(r) { + buf.WriteRune(r) + } else { + buf.WriteRune('\uFFFD') + } + } + return buf.String() +} + // toProtoStruct converts v, which must marshal into a JSON object, // into a Google Struct proto. func toProtoStruct(v interface{}) (*structpb.Struct, error) { @@ -713,7 +770,7 @@ func jsonValueToStructValue(v interface{}) *structpb.Value { // Prefer Log for most uses. // TODO(jba): come up with a better name (LogNow?) or eliminate. func (l *Logger) LogSync(ctx context.Context, e Entry) error { - ent, err := toLogEntry(e) + ent, err := l.toLogEntry(e) if err != nil { return err } @@ -728,7 +785,7 @@ func (l *Logger) LogSync(ctx context.Context, e Entry) error { // Log buffers the Entry for output to the logging service. It never blocks. func (l *Logger) Log(e Entry) { - ent, err := toLogEntry(e) + ent, err := l.toLogEntry(e) if err != nil { l.client.error(err) return @@ -756,12 +813,16 @@ func (l *Logger) writeLogEntries(entries []*logpb.LogEntry) { Labels: l.commonLabels, Entries: entries, } - ctx, cancel := context.WithTimeout(context.Background(), defaultWriteTimeout) + ctx, afterCall := l.ctxFunc() + ctx, cancel := context.WithTimeout(ctx, defaultWriteTimeout) defer cancel() _, err := l.client.client.WriteLogEntries(ctx, req) if err != nil { l.client.error(err) } + if afterCall != nil { + afterCall() + } } // StandardLogger returns a *log.Logger for the provided severity. @@ -771,14 +832,38 @@ func (l *Logger) writeLogEntries(entries []*logpb.LogEntry) { // (for example by calling SetFlags or SetPrefix). func (l *Logger) StandardLogger(s Severity) *log.Logger { return l.stdLoggers[s] } -func trunc32(i int) int32 { - if i > math.MaxInt32 { - i = math.MaxInt32 +var reCloudTraceContext = regexp.MustCompile(`([a-f\d]+)/([a-f\d]+);o=(\d)`) + +func deconstructXCloudTraceContext(s string) (traceID, spanID string, traceSampled bool) { + // As per the format described at https://cloud.google.com/trace/docs/troubleshooting#force-trace + // "X-Cloud-Trace-Context: TRACE_ID/SPAN_ID;o=TRACE_TRUE" + // for example: + // "X-Cloud-Trace-Context: 105445aa7843bc8bf206b120001000/0;o=1" + // + // We expect: + // * traceID: "105445aa7843bc8bf206b120001000" + // * spanID: "" + // * traceSampled: true + matches := reCloudTraceContext.FindAllStringSubmatch(s, -1) + if len(matches) != 1 { + return + } + + sub := matches[0] + if len(sub) != 4 { + return } - return int32(i) + + traceID, spanID = sub[1], sub[2] + if spanID == "0" { + spanID = "" + } + traceSampled = sub[3] == "1" + + return } -func toLogEntry(e Entry) (*logpb.LogEntry, error) { +func (l *Logger) toLogEntry(e Entry) (*logpb.LogEntry, error) { if e.LogName != "" { return nil, errors.New("logging: Entry.LogName should be not be set when writing") } @@ -790,15 +875,37 @@ func toLogEntry(e Entry) (*logpb.LogEntry, error) { if err != nil { return nil, err } + if e.Trace == "" && e.HTTPRequest != nil && e.HTTPRequest.Request != nil { + traceHeader := e.HTTPRequest.Request.Header.Get("X-Cloud-Trace-Context") + if traceHeader != "" { + // Set to a relative resource name, as described at + // https://cloud.google.com/appengine/docs/flexible/go/writing-application-logs. + traceID, spanID, traceSampled := deconstructXCloudTraceContext(traceHeader) + if traceID != "" { + e.Trace = fmt.Sprintf("%s/traces/%s", l.client.parent, traceID) + } + if e.SpanID == "" { + e.SpanID = spanID + } + + // If we previously hadn't set TraceSampled, let's retrieve it + // from the HTTP request's header, as per: + // https://cloud.google.com/trace/docs/troubleshooting#force-trace + e.TraceSampled = e.TraceSampled || traceSampled + } + } ent := &logpb.LogEntry{ - Timestamp: ts, - Severity: logtypepb.LogSeverity(e.Severity), - InsertId: e.InsertID, - HttpRequest: fromHTTPRequest(e.HTTPRequest), - Operation: e.Operation, - Labels: e.Labels, - Trace: e.Trace, - Resource: e.Resource, + Timestamp: ts, + Severity: logtypepb.LogSeverity(e.Severity), + InsertId: e.InsertID, + HttpRequest: fromHTTPRequest(e.HTTPRequest), + Operation: e.Operation, + Labels: e.Labels, + Trace: e.Trace, + SpanId: e.SpanID, + Resource: e.Resource, + SourceLocation: e.SourceLocation, + TraceSampled: e.TraceSampled, } switch p := e.Payload.(type) { case string: diff --git a/vendor/cloud.google.com/go/tools.go b/vendor/cloud.google.com/go/tools.go new file mode 100644 index 0000000000000..fa01cc44ccc4b --- /dev/null +++ b/vendor/cloud.google.com/go/tools.go @@ -0,0 +1,33 @@ +// +build tools + +// Copyright 2018 Google LLC +// +// 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. + +// This package exists to cause `go mod` and `go get` to believe these tools +// are dependencies, even though they are not runtime dependencies of any +// package (these are tools used by our CI builds). This means they will appear +// in our `go.mod` file, but will not be a part of the build. Also, since the +// build target is something non-existent, these should not be included in any +// binaries. + +package cloud + +import ( + _ "github.com/golang/protobuf/protoc-gen-go" + _ "github.com/jstemmer/go-junit-report" + _ "golang.org/x/exp/cmd/apidiff" + _ "golang.org/x/lint/golint" + _ "golang.org/x/tools/cmd/goimports" + _ "honnef.co/go/tools/cmd/staticcheck" +) diff --git a/vendor/code.cloudfoundry.org/clock/LICENSE b/vendor/code.cloudfoundry.org/clock/LICENSE new file mode 100644 index 0000000000000..f49a4e16e68b1 --- /dev/null +++ b/vendor/code.cloudfoundry.org/clock/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. \ No newline at end of file diff --git a/vendor/code.cloudfoundry.org/clock/NOTICE b/vendor/code.cloudfoundry.org/clock/NOTICE new file mode 100644 index 0000000000000..29c0e5ff07cb7 --- /dev/null +++ b/vendor/code.cloudfoundry.org/clock/NOTICE @@ -0,0 +1,20 @@ +Copyright (c) 2015-Present CloudFoundry.org Foundation, Inc. All Rights Reserved. + +This project contains software that is Copyright (c) 2015 Pivotal Software, Inc. + +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. + +This project may include a number of subcomponents with separate +copyright notices and license terms. Your use of these subcomponents +is subject to the terms and conditions of each subcomponent's license, +as noted in the LICENSE file. diff --git a/vendor/code.cloudfoundry.org/clock/README.md b/vendor/code.cloudfoundry.org/clock/README.md new file mode 100644 index 0000000000000..abaf64149e608 --- /dev/null +++ b/vendor/code.cloudfoundry.org/clock/README.md @@ -0,0 +1,5 @@ +# clock + +**Note**: This repository should be imported as `code.cloudfoundry.org/clock`. + +Provides a `Clock` interface, useful for injecting time dependencies in tests. diff --git a/vendor/code.cloudfoundry.org/clock/clock.go b/vendor/code.cloudfoundry.org/clock/clock.go new file mode 100644 index 0000000000000..6b091d99a4907 --- /dev/null +++ b/vendor/code.cloudfoundry.org/clock/clock.go @@ -0,0 +1,53 @@ +package clock + +import "time" + +type Clock interface { + Now() time.Time + Sleep(d time.Duration) + Since(t time.Time) time.Duration + // After waits for the duration to elapse and then sends the current time + // on the returned channel. + // It is equivalent to clock.NewTimer(d).C. + // The underlying Timer is not recovered by the garbage collector + // until the timer fires. If efficiency is a concern, use clock.NewTimer + // instead and call Timer.Stop if the timer is no longer needed. + After(d time.Duration) <-chan time.Time + + NewTimer(d time.Duration) Timer + NewTicker(d time.Duration) Ticker +} + +type realClock struct{} + +func NewClock() Clock { + return &realClock{} +} + +func (clock *realClock) Now() time.Time { + return time.Now() +} + +func (clock *realClock) Since(t time.Time) time.Duration { + return time.Now().Sub(t) +} + +func (clock *realClock) Sleep(d time.Duration) { + <-clock.NewTimer(d).C() +} + +func (clock *realClock) After(d time.Duration) <-chan time.Time { + return clock.NewTimer(d).C() +} + +func (clock *realClock) NewTimer(d time.Duration) Timer { + return &realTimer{ + t: time.NewTimer(d), + } +} + +func (clock *realClock) NewTicker(d time.Duration) Ticker { + return &realTicker{ + t: time.NewTicker(d), + } +} diff --git a/vendor/code.cloudfoundry.org/clock/package.go b/vendor/code.cloudfoundry.org/clock/package.go new file mode 100644 index 0000000000000..349f67c82a15b --- /dev/null +++ b/vendor/code.cloudfoundry.org/clock/package.go @@ -0,0 +1 @@ +package clock // import "code.cloudfoundry.org/clock" diff --git a/vendor/github.com/pivotal-golang/clock/ticker.go b/vendor/code.cloudfoundry.org/clock/ticker.go similarity index 100% rename from vendor/github.com/pivotal-golang/clock/ticker.go rename to vendor/code.cloudfoundry.org/clock/ticker.go diff --git a/vendor/github.com/pivotal-golang/clock/timer.go b/vendor/code.cloudfoundry.org/clock/timer.go similarity index 100% rename from vendor/github.com/pivotal-golang/clock/timer.go rename to vendor/code.cloudfoundry.org/clock/timer.go diff --git a/vendor/github.com/Azure/go-ansiterm/go.mod b/vendor/github.com/Azure/go-ansiterm/go.mod new file mode 100644 index 0000000000000..965cb8120ec93 --- /dev/null +++ b/vendor/github.com/Azure/go-ansiterm/go.mod @@ -0,0 +1,5 @@ +module github.com/Azure/go-ansiterm + +go 1.16 + +require golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 diff --git a/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go b/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go index a6732797263ff..5599082ae9cb7 100644 --- a/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go +++ b/vendor/github.com/Azure/go-ansiterm/winterm/ansi.go @@ -10,6 +10,7 @@ import ( "syscall" "github.com/Azure/go-ansiterm" + windows "golang.org/x/sys/windows" ) // Windows keyboard constants @@ -162,15 +163,28 @@ func ensureInRange(n int16, min int16, max int16) int16 { func GetStdFile(nFile int) (*os.File, uintptr) { var file *os.File - switch nFile { - case syscall.STD_INPUT_HANDLE: + + // syscall uses negative numbers + // windows package uses very big uint32 + // Keep these switches split so we don't have to convert ints too much. + switch uint32(nFile) { + case windows.STD_INPUT_HANDLE: file = os.Stdin - case syscall.STD_OUTPUT_HANDLE: + case windows.STD_OUTPUT_HANDLE: file = os.Stdout - case syscall.STD_ERROR_HANDLE: + case windows.STD_ERROR_HANDLE: file = os.Stderr default: - panic(fmt.Errorf("Invalid standard handle identifier: %v", nFile)) + switch nFile { + case syscall.STD_INPUT_HANDLE: + file = os.Stdin + case syscall.STD_OUTPUT_HANDLE: + file = os.Stdout + case syscall.STD_ERROR_HANDLE: + file = os.Stderr + default: + panic(fmt.Errorf("Invalid standard handle identifier: %v", nFile)) + } } fd, err := syscall.GetStdHandle(nFile) diff --git a/vendor/github.com/BurntSushi/toml/COPYING b/vendor/github.com/BurntSushi/toml/COPYING deleted file mode 100644 index 01b5743200b84..0000000000000 --- a/vendor/github.com/BurntSushi/toml/COPYING +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 TOML authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/BurntSushi/toml/README.md b/vendor/github.com/BurntSushi/toml/README.md deleted file mode 100644 index 7c1b37ecc7a02..0000000000000 --- a/vendor/github.com/BurntSushi/toml/README.md +++ /dev/null @@ -1,218 +0,0 @@ -## TOML parser and encoder for Go with reflection - -TOML stands for Tom's Obvious, Minimal Language. This Go package provides a -reflection interface similar to Go's standard library `json` and `xml` -packages. This package also supports the `encoding.TextUnmarshaler` and -`encoding.TextMarshaler` interfaces so that you can define custom data -representations. (There is an example of this below.) - -Spec: https://github.com/toml-lang/toml - -Compatible with TOML version -[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md) - -Documentation: https://godoc.org/github.com/BurntSushi/toml - -Installation: - -```bash -go get github.com/BurntSushi/toml -``` - -Try the toml validator: - -```bash -go get github.com/BurntSushi/toml/cmd/tomlv -tomlv some-toml-file.toml -``` - -[![Build Status](https://travis-ci.org/BurntSushi/toml.svg?branch=master)](https://travis-ci.org/BurntSushi/toml) [![GoDoc](https://godoc.org/github.com/BurntSushi/toml?status.svg)](https://godoc.org/github.com/BurntSushi/toml) - -### Testing - -This package passes all tests in -[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder -and the encoder. - -### Examples - -This package works similarly to how the Go standard library handles `XML` -and `JSON`. Namely, data is loaded into Go values via reflection. - -For the simplest example, consider some TOML file as just a list of keys -and values: - -```toml -Age = 25 -Cats = [ "Cauchy", "Plato" ] -Pi = 3.14 -Perfection = [ 6, 28, 496, 8128 ] -DOB = 1987-07-05T05:45:00Z -``` - -Which could be defined in Go as: - -```go -type Config struct { - Age int - Cats []string - Pi float64 - Perfection []int - DOB time.Time // requires `import time` -} -``` - -And then decoded with: - -```go -var conf Config -if _, err := toml.Decode(tomlData, &conf); err != nil { - // handle error -} -``` - -You can also use struct tags if your struct field name doesn't map to a TOML -key value directly: - -```toml -some_key_NAME = "wat" -``` - -```go -type TOML struct { - ObscureKey string `toml:"some_key_NAME"` -} -``` - -### Using the `encoding.TextUnmarshaler` interface - -Here's an example that automatically parses duration strings into -`time.Duration` values: - -```toml -[[song]] -name = "Thunder Road" -duration = "4m49s" - -[[song]] -name = "Stairway to Heaven" -duration = "8m03s" -``` - -Which can be decoded with: - -```go -type song struct { - Name string - Duration duration -} -type songs struct { - Song []song -} -var favorites songs -if _, err := toml.Decode(blob, &favorites); err != nil { - log.Fatal(err) -} - -for _, s := range favorites.Song { - fmt.Printf("%s (%s)\n", s.Name, s.Duration) -} -``` - -And you'll also need a `duration` type that satisfies the -`encoding.TextUnmarshaler` interface: - -```go -type duration struct { - time.Duration -} - -func (d *duration) UnmarshalText(text []byte) error { - var err error - d.Duration, err = time.ParseDuration(string(text)) - return err -} -``` - -### More complex usage - -Here's an example of how to load the example from the official spec page: - -```toml -# This is a TOML document. Boom. - -title = "TOML Example" - -[owner] -name = "Tom Preston-Werner" -organization = "GitHub" -bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." -dob = 1979-05-27T07:32:00Z # First class dates? Why not? - -[database] -server = "192.168.1.1" -ports = [ 8001, 8001, 8002 ] -connection_max = 5000 -enabled = true - -[servers] - - # You can indent as you please. Tabs or spaces. TOML don't care. - [servers.alpha] - ip = "10.0.0.1" - dc = "eqdc10" - - [servers.beta] - ip = "10.0.0.2" - dc = "eqdc10" - -[clients] -data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it - -# Line breaks are OK when inside arrays -hosts = [ - "alpha", - "omega" -] -``` - -And the corresponding Go types are: - -```go -type tomlConfig struct { - Title string - Owner ownerInfo - DB database `toml:"database"` - Servers map[string]server - Clients clients -} - -type ownerInfo struct { - Name string - Org string `toml:"organization"` - Bio string - DOB time.Time -} - -type database struct { - Server string - Ports []int - ConnMax int `toml:"connection_max"` - Enabled bool -} - -type server struct { - IP string - DC string -} - -type clients struct { - Data [][]interface{} - Hosts []string -} -``` - -Note that a case insensitive match will be tried if an exact match can't be -found. - -A working example of the above can be found in `_examples/example.{go,toml}`. diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go deleted file mode 100644 index b0fd51d5b6ea5..0000000000000 --- a/vendor/github.com/BurntSushi/toml/decode.go +++ /dev/null @@ -1,509 +0,0 @@ -package toml - -import ( - "fmt" - "io" - "io/ioutil" - "math" - "reflect" - "strings" - "time" -) - -func e(format string, args ...interface{}) error { - return fmt.Errorf("toml: "+format, args...) -} - -// Unmarshaler is the interface implemented by objects that can unmarshal a -// TOML description of themselves. -type Unmarshaler interface { - UnmarshalTOML(interface{}) error -} - -// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`. -func Unmarshal(p []byte, v interface{}) error { - _, err := Decode(string(p), v) - return err -} - -// Primitive is a TOML value that hasn't been decoded into a Go value. -// When using the various `Decode*` functions, the type `Primitive` may -// be given to any value, and its decoding will be delayed. -// -// A `Primitive` value can be decoded using the `PrimitiveDecode` function. -// -// The underlying representation of a `Primitive` value is subject to change. -// Do not rely on it. -// -// N.B. Primitive values are still parsed, so using them will only avoid -// the overhead of reflection. They can be useful when you don't know the -// exact type of TOML data until run time. -type Primitive struct { - undecoded interface{} - context Key -} - -// DEPRECATED! -// -// Use MetaData.PrimitiveDecode instead. -func PrimitiveDecode(primValue Primitive, v interface{}) error { - md := MetaData{decoded: make(map[string]bool)} - return md.unify(primValue.undecoded, rvalue(v)) -} - -// PrimitiveDecode is just like the other `Decode*` functions, except it -// decodes a TOML value that has already been parsed. Valid primitive values -// can *only* be obtained from values filled by the decoder functions, -// including this method. (i.e., `v` may contain more `Primitive` -// values.) -// -// Meta data for primitive values is included in the meta data returned by -// the `Decode*` functions with one exception: keys returned by the Undecoded -// method will only reflect keys that were decoded. Namely, any keys hidden -// behind a Primitive will be considered undecoded. Executing this method will -// update the undecoded keys in the meta data. (See the example.) -func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error { - md.context = primValue.context - defer func() { md.context = nil }() - return md.unify(primValue.undecoded, rvalue(v)) -} - -// Decode will decode the contents of `data` in TOML format into a pointer -// `v`. -// -// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be -// used interchangeably.) -// -// TOML arrays of tables correspond to either a slice of structs or a slice -// of maps. -// -// TOML datetimes correspond to Go `time.Time` values. -// -// All other TOML types (float, string, int, bool and array) correspond -// to the obvious Go types. -// -// An exception to the above rules is if a type implements the -// encoding.TextUnmarshaler interface. In this case, any primitive TOML value -// (floats, strings, integers, booleans and datetimes) will be converted to -// a byte string and given to the value's UnmarshalText method. See the -// Unmarshaler example for a demonstration with time duration strings. -// -// Key mapping -// -// TOML keys can map to either keys in a Go map or field names in a Go -// struct. The special `toml` struct tag may be used to map TOML keys to -// struct fields that don't match the key name exactly. (See the example.) -// A case insensitive match to struct names will be tried if an exact match -// can't be found. -// -// The mapping between TOML values and Go values is loose. That is, there -// may exist TOML values that cannot be placed into your representation, and -// there may be parts of your representation that do not correspond to -// TOML values. This loose mapping can be made stricter by using the IsDefined -// and/or Undecoded methods on the MetaData returned. -// -// This decoder will not handle cyclic types. If a cyclic type is passed, -// `Decode` will not terminate. -func Decode(data string, v interface{}) (MetaData, error) { - rv := reflect.ValueOf(v) - if rv.Kind() != reflect.Ptr { - return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v)) - } - if rv.IsNil() { - return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v)) - } - p, err := parse(data) - if err != nil { - return MetaData{}, err - } - md := MetaData{ - p.mapping, p.types, p.ordered, - make(map[string]bool, len(p.ordered)), nil, - } - return md, md.unify(p.mapping, indirect(rv)) -} - -// DecodeFile is just like Decode, except it will automatically read the -// contents of the file at `fpath` and decode it for you. -func DecodeFile(fpath string, v interface{}) (MetaData, error) { - bs, err := ioutil.ReadFile(fpath) - if err != nil { - return MetaData{}, err - } - return Decode(string(bs), v) -} - -// DecodeReader is just like Decode, except it will consume all bytes -// from the reader and decode it for you. -func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { - bs, err := ioutil.ReadAll(r) - if err != nil { - return MetaData{}, err - } - return Decode(string(bs), v) -} - -// unify performs a sort of type unification based on the structure of `rv`, -// which is the client representation. -// -// Any type mismatch produces an error. Finding a type that we don't know -// how to handle produces an unsupported type error. -func (md *MetaData) unify(data interface{}, rv reflect.Value) error { - - // Special case. Look for a `Primitive` value. - if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() { - // Save the undecoded data and the key context into the primitive - // value. - context := make(Key, len(md.context)) - copy(context, md.context) - rv.Set(reflect.ValueOf(Primitive{ - undecoded: data, - context: context, - })) - return nil - } - - // Special case. Unmarshaler Interface support. - if rv.CanAddr() { - if v, ok := rv.Addr().Interface().(Unmarshaler); ok { - return v.UnmarshalTOML(data) - } - } - - // Special case. Handle time.Time values specifically. - // TODO: Remove this code when we decide to drop support for Go 1.1. - // This isn't necessary in Go 1.2 because time.Time satisfies the encoding - // interfaces. - if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) { - return md.unifyDatetime(data, rv) - } - - // Special case. Look for a value satisfying the TextUnmarshaler interface. - if v, ok := rv.Interface().(TextUnmarshaler); ok { - return md.unifyText(data, v) - } - // BUG(burntsushi) - // The behavior here is incorrect whenever a Go type satisfies the - // encoding.TextUnmarshaler interface but also corresponds to a TOML - // hash or array. In particular, the unmarshaler should only be applied - // to primitive TOML values. But at this point, it will be applied to - // all kinds of values and produce an incorrect error whenever those values - // are hashes or arrays (including arrays of tables). - - k := rv.Kind() - - // laziness - if k >= reflect.Int && k <= reflect.Uint64 { - return md.unifyInt(data, rv) - } - switch k { - case reflect.Ptr: - elem := reflect.New(rv.Type().Elem()) - err := md.unify(data, reflect.Indirect(elem)) - if err != nil { - return err - } - rv.Set(elem) - return nil - case reflect.Struct: - return md.unifyStruct(data, rv) - case reflect.Map: - return md.unifyMap(data, rv) - case reflect.Array: - return md.unifyArray(data, rv) - case reflect.Slice: - return md.unifySlice(data, rv) - case reflect.String: - return md.unifyString(data, rv) - case reflect.Bool: - return md.unifyBool(data, rv) - case reflect.Interface: - // we only support empty interfaces. - if rv.NumMethod() > 0 { - return e("unsupported type %s", rv.Type()) - } - return md.unifyAnything(data, rv) - case reflect.Float32: - fallthrough - case reflect.Float64: - return md.unifyFloat64(data, rv) - } - return e("unsupported type %s", rv.Kind()) -} - -func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error { - tmap, ok := mapping.(map[string]interface{}) - if !ok { - if mapping == nil { - return nil - } - return e("type mismatch for %s: expected table but found %T", - rv.Type().String(), mapping) - } - - for key, datum := range tmap { - var f *field - fields := cachedTypeFields(rv.Type()) - for i := range fields { - ff := &fields[i] - if ff.name == key { - f = ff - break - } - if f == nil && strings.EqualFold(ff.name, key) { - f = ff - } - } - if f != nil { - subv := rv - for _, i := range f.index { - subv = indirect(subv.Field(i)) - } - if isUnifiable(subv) { - md.decoded[md.context.add(key).String()] = true - md.context = append(md.context, key) - if err := md.unify(datum, subv); err != nil { - return err - } - md.context = md.context[0 : len(md.context)-1] - } else if f.name != "" { - // Bad user! No soup for you! - return e("cannot write unexported field %s.%s", - rv.Type().String(), f.name) - } - } - } - return nil -} - -func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error { - tmap, ok := mapping.(map[string]interface{}) - if !ok { - if tmap == nil { - return nil - } - return badtype("map", mapping) - } - if rv.IsNil() { - rv.Set(reflect.MakeMap(rv.Type())) - } - for k, v := range tmap { - md.decoded[md.context.add(k).String()] = true - md.context = append(md.context, k) - - rvkey := indirect(reflect.New(rv.Type().Key())) - rvval := reflect.Indirect(reflect.New(rv.Type().Elem())) - if err := md.unify(v, rvval); err != nil { - return err - } - md.context = md.context[0 : len(md.context)-1] - - rvkey.SetString(k) - rv.SetMapIndex(rvkey, rvval) - } - return nil -} - -func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error { - datav := reflect.ValueOf(data) - if datav.Kind() != reflect.Slice { - if !datav.IsValid() { - return nil - } - return badtype("slice", data) - } - sliceLen := datav.Len() - if sliceLen != rv.Len() { - return e("expected array length %d; got TOML array of length %d", - rv.Len(), sliceLen) - } - return md.unifySliceArray(datav, rv) -} - -func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error { - datav := reflect.ValueOf(data) - if datav.Kind() != reflect.Slice { - if !datav.IsValid() { - return nil - } - return badtype("slice", data) - } - n := datav.Len() - if rv.IsNil() || rv.Cap() < n { - rv.Set(reflect.MakeSlice(rv.Type(), n, n)) - } - rv.SetLen(n) - return md.unifySliceArray(datav, rv) -} - -func (md *MetaData) unifySliceArray(data, rv reflect.Value) error { - sliceLen := data.Len() - for i := 0; i < sliceLen; i++ { - v := data.Index(i).Interface() - sliceval := indirect(rv.Index(i)) - if err := md.unify(v, sliceval); err != nil { - return err - } - } - return nil -} - -func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error { - if _, ok := data.(time.Time); ok { - rv.Set(reflect.ValueOf(data)) - return nil - } - return badtype("time.Time", data) -} - -func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error { - if s, ok := data.(string); ok { - rv.SetString(s) - return nil - } - return badtype("string", data) -} - -func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error { - if num, ok := data.(float64); ok { - switch rv.Kind() { - case reflect.Float32: - fallthrough - case reflect.Float64: - rv.SetFloat(num) - default: - panic("bug") - } - return nil - } - return badtype("float", data) -} - -func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error { - if num, ok := data.(int64); ok { - if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 { - switch rv.Kind() { - case reflect.Int, reflect.Int64: - // No bounds checking necessary. - case reflect.Int8: - if num < math.MinInt8 || num > math.MaxInt8 { - return e("value %d is out of range for int8", num) - } - case reflect.Int16: - if num < math.MinInt16 || num > math.MaxInt16 { - return e("value %d is out of range for int16", num) - } - case reflect.Int32: - if num < math.MinInt32 || num > math.MaxInt32 { - return e("value %d is out of range for int32", num) - } - } - rv.SetInt(num) - } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 { - unum := uint64(num) - switch rv.Kind() { - case reflect.Uint, reflect.Uint64: - // No bounds checking necessary. - case reflect.Uint8: - if num < 0 || unum > math.MaxUint8 { - return e("value %d is out of range for uint8", num) - } - case reflect.Uint16: - if num < 0 || unum > math.MaxUint16 { - return e("value %d is out of range for uint16", num) - } - case reflect.Uint32: - if num < 0 || unum > math.MaxUint32 { - return e("value %d is out of range for uint32", num) - } - } - rv.SetUint(unum) - } else { - panic("unreachable") - } - return nil - } - return badtype("integer", data) -} - -func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error { - if b, ok := data.(bool); ok { - rv.SetBool(b) - return nil - } - return badtype("boolean", data) -} - -func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error { - rv.Set(reflect.ValueOf(data)) - return nil -} - -func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error { - var s string - switch sdata := data.(type) { - case TextMarshaler: - text, err := sdata.MarshalText() - if err != nil { - return err - } - s = string(text) - case fmt.Stringer: - s = sdata.String() - case string: - s = sdata - case bool: - s = fmt.Sprintf("%v", sdata) - case int64: - s = fmt.Sprintf("%d", sdata) - case float64: - s = fmt.Sprintf("%f", sdata) - default: - return badtype("primitive (string-like)", data) - } - if err := v.UnmarshalText([]byte(s)); err != nil { - return err - } - return nil -} - -// rvalue returns a reflect.Value of `v`. All pointers are resolved. -func rvalue(v interface{}) reflect.Value { - return indirect(reflect.ValueOf(v)) -} - -// indirect returns the value pointed to by a pointer. -// Pointers are followed until the value is not a pointer. -// New values are allocated for each nil pointer. -// -// An exception to this rule is if the value satisfies an interface of -// interest to us (like encoding.TextUnmarshaler). -func indirect(v reflect.Value) reflect.Value { - if v.Kind() != reflect.Ptr { - if v.CanSet() { - pv := v.Addr() - if _, ok := pv.Interface().(TextUnmarshaler); ok { - return pv - } - } - return v - } - if v.IsNil() { - v.Set(reflect.New(v.Type().Elem())) - } - return indirect(reflect.Indirect(v)) -} - -func isUnifiable(rv reflect.Value) bool { - if rv.CanSet() { - return true - } - if _, ok := rv.Interface().(TextUnmarshaler); ok { - return true - } - return false -} - -func badtype(expected string, data interface{}) error { - return e("cannot load TOML value of type %T into a Go %s", data, expected) -} diff --git a/vendor/github.com/BurntSushi/toml/decode_meta.go b/vendor/github.com/BurntSushi/toml/decode_meta.go deleted file mode 100644 index b9914a6798cf9..0000000000000 --- a/vendor/github.com/BurntSushi/toml/decode_meta.go +++ /dev/null @@ -1,121 +0,0 @@ -package toml - -import "strings" - -// MetaData allows access to meta information about TOML data that may not -// be inferrable via reflection. In particular, whether a key has been defined -// and the TOML type of a key. -type MetaData struct { - mapping map[string]interface{} - types map[string]tomlType - keys []Key - decoded map[string]bool - context Key // Used only during decoding. -} - -// IsDefined returns true if the key given exists in the TOML data. The key -// should be specified hierarchially. e.g., -// -// // access the TOML key 'a.b.c' -// IsDefined("a", "b", "c") -// -// IsDefined will return false if an empty key given. Keys are case sensitive. -func (md *MetaData) IsDefined(key ...string) bool { - if len(key) == 0 { - return false - } - - var hash map[string]interface{} - var ok bool - var hashOrVal interface{} = md.mapping - for _, k := range key { - if hash, ok = hashOrVal.(map[string]interface{}); !ok { - return false - } - if hashOrVal, ok = hash[k]; !ok { - return false - } - } - return true -} - -// Type returns a string representation of the type of the key specified. -// -// Type will return the empty string if given an empty key or a key that -// does not exist. Keys are case sensitive. -func (md *MetaData) Type(key ...string) string { - fullkey := strings.Join(key, ".") - if typ, ok := md.types[fullkey]; ok { - return typ.typeString() - } - return "" -} - -// Key is the type of any TOML key, including key groups. Use (MetaData).Keys -// to get values of this type. -type Key []string - -func (k Key) String() string { - return strings.Join(k, ".") -} - -func (k Key) maybeQuotedAll() string { - var ss []string - for i := range k { - ss = append(ss, k.maybeQuoted(i)) - } - return strings.Join(ss, ".") -} - -func (k Key) maybeQuoted(i int) string { - quote := false - for _, c := range k[i] { - if !isBareKeyChar(c) { - quote = true - break - } - } - if quote { - return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\"" - } - return k[i] -} - -func (k Key) add(piece string) Key { - newKey := make(Key, len(k)+1) - copy(newKey, k) - newKey[len(k)] = piece - return newKey -} - -// Keys returns a slice of every key in the TOML data, including key groups. -// Each key is itself a slice, where the first element is the top of the -// hierarchy and the last is the most specific. -// -// The list will have the same order as the keys appeared in the TOML data. -// -// All keys returned are non-empty. -func (md *MetaData) Keys() []Key { - return md.keys -} - -// Undecoded returns all keys that have not been decoded in the order in which -// they appear in the original TOML document. -// -// This includes keys that haven't been decoded because of a Primitive value. -// Once the Primitive value is decoded, the keys will be considered decoded. -// -// Also note that decoding into an empty interface will result in no decoding, -// and so no keys will be considered decoded. -// -// In this sense, the Undecoded keys correspond to keys in the TOML document -// that do not have a concrete type in your representation. -func (md *MetaData) Undecoded() []Key { - undecoded := make([]Key, 0, len(md.keys)) - for _, key := range md.keys { - if !md.decoded[key.String()] { - undecoded = append(undecoded, key) - } - } - return undecoded -} diff --git a/vendor/github.com/BurntSushi/toml/doc.go b/vendor/github.com/BurntSushi/toml/doc.go deleted file mode 100644 index b371f396edcac..0000000000000 --- a/vendor/github.com/BurntSushi/toml/doc.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Package toml provides facilities for decoding and encoding TOML configuration -files via reflection. There is also support for delaying decoding with -the Primitive type, and querying the set of keys in a TOML document with the -MetaData type. - -The specification implemented: https://github.com/toml-lang/toml - -The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify -whether a file is a valid TOML document. It can also be used to print the -type of each key in a TOML document. - -Testing - -There are two important types of tests used for this package. The first is -contained inside '*_test.go' files and uses the standard Go unit testing -framework. These tests are primarily devoted to holistically testing the -decoder and encoder. - -The second type of testing is used to verify the implementation's adherence -to the TOML specification. These tests have been factored into their own -project: https://github.com/BurntSushi/toml-test - -The reason the tests are in a separate project is so that they can be used by -any implementation of TOML. Namely, it is language agnostic. -*/ -package toml diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go deleted file mode 100644 index d905c21a24662..0000000000000 --- a/vendor/github.com/BurntSushi/toml/encode.go +++ /dev/null @@ -1,568 +0,0 @@ -package toml - -import ( - "bufio" - "errors" - "fmt" - "io" - "reflect" - "sort" - "strconv" - "strings" - "time" -) - -type tomlEncodeError struct{ error } - -var ( - errArrayMixedElementTypes = errors.New( - "toml: cannot encode array with mixed element types") - errArrayNilElement = errors.New( - "toml: cannot encode array with nil element") - errNonString = errors.New( - "toml: cannot encode a map with non-string key type") - errAnonNonStruct = errors.New( - "toml: cannot encode an anonymous field that is not a struct") - errArrayNoTable = errors.New( - "toml: TOML array element cannot contain a table") - errNoKey = errors.New( - "toml: top-level values must be Go maps or structs") - errAnything = errors.New("") // used in testing -) - -var quotedReplacer = strings.NewReplacer( - "\t", "\\t", - "\n", "\\n", - "\r", "\\r", - "\"", "\\\"", - "\\", "\\\\", -) - -// Encoder controls the encoding of Go values to a TOML document to some -// io.Writer. -// -// The indentation level can be controlled with the Indent field. -type Encoder struct { - // A single indentation level. By default it is two spaces. - Indent string - - // hasWritten is whether we have written any output to w yet. - hasWritten bool - w *bufio.Writer -} - -// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer -// given. By default, a single indentation level is 2 spaces. -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{ - w: bufio.NewWriter(w), - Indent: " ", - } -} - -// Encode writes a TOML representation of the Go value to the underlying -// io.Writer. If the value given cannot be encoded to a valid TOML document, -// then an error is returned. -// -// The mapping between Go values and TOML values should be precisely the same -// as for the Decode* functions. Similarly, the TextMarshaler interface is -// supported by encoding the resulting bytes as strings. (If you want to write -// arbitrary binary data then you will need to use something like base64 since -// TOML does not have any binary types.) -// -// When encoding TOML hashes (i.e., Go maps or structs), keys without any -// sub-hashes are encoded first. -// -// If a Go map is encoded, then its keys are sorted alphabetically for -// deterministic output. More control over this behavior may be provided if -// there is demand for it. -// -// Encoding Go values without a corresponding TOML representation---like map -// types with non-string keys---will cause an error to be returned. Similarly -// for mixed arrays/slices, arrays/slices with nil elements, embedded -// non-struct types and nested slices containing maps or structs. -// (e.g., [][]map[string]string is not allowed but []map[string]string is OK -// and so is []map[string][]string.) -func (enc *Encoder) Encode(v interface{}) error { - rv := eindirect(reflect.ValueOf(v)) - if err := enc.safeEncode(Key([]string{}), rv); err != nil { - return err - } - return enc.w.Flush() -} - -func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) { - defer func() { - if r := recover(); r != nil { - if terr, ok := r.(tomlEncodeError); ok { - err = terr.error - return - } - panic(r) - } - }() - enc.encode(key, rv) - return nil -} - -func (enc *Encoder) encode(key Key, rv reflect.Value) { - // Special case. Time needs to be in ISO8601 format. - // Special case. If we can marshal the type to text, then we used that. - // Basically, this prevents the encoder for handling these types as - // generic structs (or whatever the underlying type of a TextMarshaler is). - switch rv.Interface().(type) { - case time.Time, TextMarshaler: - enc.keyEqElement(key, rv) - return - } - - k := rv.Kind() - switch k { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, - reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, - reflect.Uint64, - reflect.Float32, reflect.Float64, reflect.String, reflect.Bool: - enc.keyEqElement(key, rv) - case reflect.Array, reflect.Slice: - if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) { - enc.eArrayOfTables(key, rv) - } else { - enc.keyEqElement(key, rv) - } - case reflect.Interface: - if rv.IsNil() { - return - } - enc.encode(key, rv.Elem()) - case reflect.Map: - if rv.IsNil() { - return - } - enc.eTable(key, rv) - case reflect.Ptr: - if rv.IsNil() { - return - } - enc.encode(key, rv.Elem()) - case reflect.Struct: - enc.eTable(key, rv) - default: - panic(e("unsupported type for key '%s': %s", key, k)) - } -} - -// eElement encodes any value that can be an array element (primitives and -// arrays). -func (enc *Encoder) eElement(rv reflect.Value) { - switch v := rv.Interface().(type) { - case time.Time: - // Special case time.Time as a primitive. Has to come before - // TextMarshaler below because time.Time implements - // encoding.TextMarshaler, but we need to always use UTC. - enc.wf(v.UTC().Format("2006-01-02T15:04:05Z")) - return - case TextMarshaler: - // Special case. Use text marshaler if it's available for this value. - if s, err := v.MarshalText(); err != nil { - encPanic(err) - } else { - enc.writeQuoted(string(s)) - } - return - } - switch rv.Kind() { - case reflect.Bool: - enc.wf(strconv.FormatBool(rv.Bool())) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, - reflect.Int64: - enc.wf(strconv.FormatInt(rv.Int(), 10)) - case reflect.Uint, reflect.Uint8, reflect.Uint16, - reflect.Uint32, reflect.Uint64: - enc.wf(strconv.FormatUint(rv.Uint(), 10)) - case reflect.Float32: - enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32))) - case reflect.Float64: - enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64))) - case reflect.Array, reflect.Slice: - enc.eArrayOrSliceElement(rv) - case reflect.Interface: - enc.eElement(rv.Elem()) - case reflect.String: - enc.writeQuoted(rv.String()) - default: - panic(e("unexpected primitive type: %s", rv.Kind())) - } -} - -// By the TOML spec, all floats must have a decimal with at least one -// number on either side. -func floatAddDecimal(fstr string) string { - if !strings.Contains(fstr, ".") { - return fstr + ".0" - } - return fstr -} - -func (enc *Encoder) writeQuoted(s string) { - enc.wf("\"%s\"", quotedReplacer.Replace(s)) -} - -func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) { - length := rv.Len() - enc.wf("[") - for i := 0; i < length; i++ { - elem := rv.Index(i) - enc.eElement(elem) - if i != length-1 { - enc.wf(", ") - } - } - enc.wf("]") -} - -func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) { - if len(key) == 0 { - encPanic(errNoKey) - } - for i := 0; i < rv.Len(); i++ { - trv := rv.Index(i) - if isNil(trv) { - continue - } - panicIfInvalidKey(key) - enc.newline() - enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll()) - enc.newline() - enc.eMapOrStruct(key, trv) - } -} - -func (enc *Encoder) eTable(key Key, rv reflect.Value) { - panicIfInvalidKey(key) - if len(key) == 1 { - // Output an extra newline between top-level tables. - // (The newline isn't written if nothing else has been written though.) - enc.newline() - } - if len(key) > 0 { - enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll()) - enc.newline() - } - enc.eMapOrStruct(key, rv) -} - -func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) { - switch rv := eindirect(rv); rv.Kind() { - case reflect.Map: - enc.eMap(key, rv) - case reflect.Struct: - enc.eStruct(key, rv) - default: - panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String()) - } -} - -func (enc *Encoder) eMap(key Key, rv reflect.Value) { - rt := rv.Type() - if rt.Key().Kind() != reflect.String { - encPanic(errNonString) - } - - // Sort keys so that we have deterministic output. And write keys directly - // underneath this key first, before writing sub-structs or sub-maps. - var mapKeysDirect, mapKeysSub []string - for _, mapKey := range rv.MapKeys() { - k := mapKey.String() - if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) { - mapKeysSub = append(mapKeysSub, k) - } else { - mapKeysDirect = append(mapKeysDirect, k) - } - } - - var writeMapKeys = func(mapKeys []string) { - sort.Strings(mapKeys) - for _, mapKey := range mapKeys { - mrv := rv.MapIndex(reflect.ValueOf(mapKey)) - if isNil(mrv) { - // Don't write anything for nil fields. - continue - } - enc.encode(key.add(mapKey), mrv) - } - } - writeMapKeys(mapKeysDirect) - writeMapKeys(mapKeysSub) -} - -func (enc *Encoder) eStruct(key Key, rv reflect.Value) { - // Write keys for fields directly under this key first, because if we write - // a field that creates a new table, then all keys under it will be in that - // table (not the one we're writing here). - rt := rv.Type() - var fieldsDirect, fieldsSub [][]int - var addFields func(rt reflect.Type, rv reflect.Value, start []int) - addFields = func(rt reflect.Type, rv reflect.Value, start []int) { - for i := 0; i < rt.NumField(); i++ { - f := rt.Field(i) - // skip unexported fields - if f.PkgPath != "" && !f.Anonymous { - continue - } - frv := rv.Field(i) - if f.Anonymous { - t := f.Type - switch t.Kind() { - case reflect.Struct: - // Treat anonymous struct fields with - // tag names as though they are not - // anonymous, like encoding/json does. - if getOptions(f.Tag).name == "" { - addFields(t, frv, f.Index) - continue - } - case reflect.Ptr: - if t.Elem().Kind() == reflect.Struct && - getOptions(f.Tag).name == "" { - if !frv.IsNil() { - addFields(t.Elem(), frv.Elem(), f.Index) - } - continue - } - // Fall through to the normal field encoding logic below - // for non-struct anonymous fields. - } - } - - if typeIsHash(tomlTypeOfGo(frv)) { - fieldsSub = append(fieldsSub, append(start, f.Index...)) - } else { - fieldsDirect = append(fieldsDirect, append(start, f.Index...)) - } - } - } - addFields(rt, rv, nil) - - var writeFields = func(fields [][]int) { - for _, fieldIndex := range fields { - sft := rt.FieldByIndex(fieldIndex) - sf := rv.FieldByIndex(fieldIndex) - if isNil(sf) { - // Don't write anything for nil fields. - continue - } - - opts := getOptions(sft.Tag) - if opts.skip { - continue - } - keyName := sft.Name - if opts.name != "" { - keyName = opts.name - } - if opts.omitempty && isEmpty(sf) { - continue - } - if opts.omitzero && isZero(sf) { - continue - } - - enc.encode(key.add(keyName), sf) - } - } - writeFields(fieldsDirect) - writeFields(fieldsSub) -} - -// tomlTypeName returns the TOML type name of the Go value's type. It is -// used to determine whether the types of array elements are mixed (which is -// forbidden). If the Go value is nil, then it is illegal for it to be an array -// element, and valueIsNil is returned as true. - -// Returns the TOML type of a Go value. The type may be `nil`, which means -// no concrete TOML type could be found. -func tomlTypeOfGo(rv reflect.Value) tomlType { - if isNil(rv) || !rv.IsValid() { - return nil - } - switch rv.Kind() { - case reflect.Bool: - return tomlBool - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, - reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, - reflect.Uint64: - return tomlInteger - case reflect.Float32, reflect.Float64: - return tomlFloat - case reflect.Array, reflect.Slice: - if typeEqual(tomlHash, tomlArrayType(rv)) { - return tomlArrayHash - } - return tomlArray - case reflect.Ptr, reflect.Interface: - return tomlTypeOfGo(rv.Elem()) - case reflect.String: - return tomlString - case reflect.Map: - return tomlHash - case reflect.Struct: - switch rv.Interface().(type) { - case time.Time: - return tomlDatetime - case TextMarshaler: - return tomlString - default: - return tomlHash - } - default: - panic("unexpected reflect.Kind: " + rv.Kind().String()) - } -} - -// tomlArrayType returns the element type of a TOML array. The type returned -// may be nil if it cannot be determined (e.g., a nil slice or a zero length -// slize). This function may also panic if it finds a type that cannot be -// expressed in TOML (such as nil elements, heterogeneous arrays or directly -// nested arrays of tables). -func tomlArrayType(rv reflect.Value) tomlType { - if isNil(rv) || !rv.IsValid() || rv.Len() == 0 { - return nil - } - firstType := tomlTypeOfGo(rv.Index(0)) - if firstType == nil { - encPanic(errArrayNilElement) - } - - rvlen := rv.Len() - for i := 1; i < rvlen; i++ { - elem := rv.Index(i) - switch elemType := tomlTypeOfGo(elem); { - case elemType == nil: - encPanic(errArrayNilElement) - case !typeEqual(firstType, elemType): - encPanic(errArrayMixedElementTypes) - } - } - // If we have a nested array, then we must make sure that the nested - // array contains ONLY primitives. - // This checks arbitrarily nested arrays. - if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) { - nest := tomlArrayType(eindirect(rv.Index(0))) - if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) { - encPanic(errArrayNoTable) - } - } - return firstType -} - -type tagOptions struct { - skip bool // "-" - name string - omitempty bool - omitzero bool -} - -func getOptions(tag reflect.StructTag) tagOptions { - t := tag.Get("toml") - if t == "-" { - return tagOptions{skip: true} - } - var opts tagOptions - parts := strings.Split(t, ",") - opts.name = parts[0] - for _, s := range parts[1:] { - switch s { - case "omitempty": - opts.omitempty = true - case "omitzero": - opts.omitzero = true - } - } - return opts -} - -func isZero(rv reflect.Value) bool { - switch rv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return rv.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return rv.Uint() == 0 - case reflect.Float32, reflect.Float64: - return rv.Float() == 0.0 - } - return false -} - -func isEmpty(rv reflect.Value) bool { - switch rv.Kind() { - case reflect.Array, reflect.Slice, reflect.Map, reflect.String: - return rv.Len() == 0 - case reflect.Bool: - return !rv.Bool() - } - return false -} - -func (enc *Encoder) newline() { - if enc.hasWritten { - enc.wf("\n") - } -} - -func (enc *Encoder) keyEqElement(key Key, val reflect.Value) { - if len(key) == 0 { - encPanic(errNoKey) - } - panicIfInvalidKey(key) - enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1)) - enc.eElement(val) - enc.newline() -} - -func (enc *Encoder) wf(format string, v ...interface{}) { - if _, err := fmt.Fprintf(enc.w, format, v...); err != nil { - encPanic(err) - } - enc.hasWritten = true -} - -func (enc *Encoder) indentStr(key Key) string { - return strings.Repeat(enc.Indent, len(key)-1) -} - -func encPanic(err error) { - panic(tomlEncodeError{err}) -} - -func eindirect(v reflect.Value) reflect.Value { - switch v.Kind() { - case reflect.Ptr, reflect.Interface: - return eindirect(v.Elem()) - default: - return v - } -} - -func isNil(rv reflect.Value) bool { - switch rv.Kind() { - case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: - return rv.IsNil() - default: - return false - } -} - -func panicIfInvalidKey(key Key) { - for _, k := range key { - if len(k) == 0 { - encPanic(e("Key '%s' is not a valid table name. Key names "+ - "cannot be empty.", key.maybeQuotedAll())) - } - } -} - -func isValidKeyName(s string) bool { - return len(s) != 0 -} diff --git a/vendor/github.com/BurntSushi/toml/encoding_types.go b/vendor/github.com/BurntSushi/toml/encoding_types.go deleted file mode 100644 index d36e1dd6002be..0000000000000 --- a/vendor/github.com/BurntSushi/toml/encoding_types.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build go1.2 - -package toml - -// In order to support Go 1.1, we define our own TextMarshaler and -// TextUnmarshaler types. For Go 1.2+, we just alias them with the -// standard library interfaces. - -import ( - "encoding" -) - -// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here -// so that Go 1.1 can be supported. -type TextMarshaler encoding.TextMarshaler - -// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined -// here so that Go 1.1 can be supported. -type TextUnmarshaler encoding.TextUnmarshaler diff --git a/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go deleted file mode 100644 index e8d503d04690d..0000000000000 --- a/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build !go1.2 - -package toml - -// These interfaces were introduced in Go 1.2, so we add them manually when -// compiling for Go 1.1. - -// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here -// so that Go 1.1 can be supported. -type TextMarshaler interface { - MarshalText() (text []byte, err error) -} - -// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined -// here so that Go 1.1 can be supported. -type TextUnmarshaler interface { - UnmarshalText(text []byte) error -} diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go deleted file mode 100644 index e0a742a8870f1..0000000000000 --- a/vendor/github.com/BurntSushi/toml/lex.go +++ /dev/null @@ -1,953 +0,0 @@ -package toml - -import ( - "fmt" - "strings" - "unicode" - "unicode/utf8" -) - -type itemType int - -const ( - itemError itemType = iota - itemNIL // used in the parser to indicate no type - itemEOF - itemText - itemString - itemRawString - itemMultilineString - itemRawMultilineString - itemBool - itemInteger - itemFloat - itemDatetime - itemArray // the start of an array - itemArrayEnd - itemTableStart - itemTableEnd - itemArrayTableStart - itemArrayTableEnd - itemKeyStart - itemCommentStart - itemInlineTableStart - itemInlineTableEnd -) - -const ( - eof = 0 - comma = ',' - tableStart = '[' - tableEnd = ']' - arrayTableStart = '[' - arrayTableEnd = ']' - tableSep = '.' - keySep = '=' - arrayStart = '[' - arrayEnd = ']' - commentStart = '#' - stringStart = '"' - stringEnd = '"' - rawStringStart = '\'' - rawStringEnd = '\'' - inlineTableStart = '{' - inlineTableEnd = '}' -) - -type stateFn func(lx *lexer) stateFn - -type lexer struct { - input string - start int - pos int - line int - state stateFn - items chan item - - // Allow for backing up up to three runes. - // This is necessary because TOML contains 3-rune tokens (""" and '''). - prevWidths [3]int - nprev int // how many of prevWidths are in use - // If we emit an eof, we can still back up, but it is not OK to call - // next again. - atEOF bool - - // A stack of state functions used to maintain context. - // The idea is to reuse parts of the state machine in various places. - // For example, values can appear at the top level or within arbitrarily - // nested arrays. The last state on the stack is used after a value has - // been lexed. Similarly for comments. - stack []stateFn -} - -type item struct { - typ itemType - val string - line int -} - -func (lx *lexer) nextItem() item { - for { - select { - case item := <-lx.items: - return item - default: - lx.state = lx.state(lx) - } - } -} - -func lex(input string) *lexer { - lx := &lexer{ - input: input, - state: lexTop, - line: 1, - items: make(chan item, 10), - stack: make([]stateFn, 0, 10), - } - return lx -} - -func (lx *lexer) push(state stateFn) { - lx.stack = append(lx.stack, state) -} - -func (lx *lexer) pop() stateFn { - if len(lx.stack) == 0 { - return lx.errorf("BUG in lexer: no states to pop") - } - last := lx.stack[len(lx.stack)-1] - lx.stack = lx.stack[0 : len(lx.stack)-1] - return last -} - -func (lx *lexer) current() string { - return lx.input[lx.start:lx.pos] -} - -func (lx *lexer) emit(typ itemType) { - lx.items <- item{typ, lx.current(), lx.line} - lx.start = lx.pos -} - -func (lx *lexer) emitTrim(typ itemType) { - lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line} - lx.start = lx.pos -} - -func (lx *lexer) next() (r rune) { - if lx.atEOF { - panic("next called after EOF") - } - if lx.pos >= len(lx.input) { - lx.atEOF = true - return eof - } - - if lx.input[lx.pos] == '\n' { - lx.line++ - } - lx.prevWidths[2] = lx.prevWidths[1] - lx.prevWidths[1] = lx.prevWidths[0] - if lx.nprev < 3 { - lx.nprev++ - } - r, w := utf8.DecodeRuneInString(lx.input[lx.pos:]) - lx.prevWidths[0] = w - lx.pos += w - return r -} - -// ignore skips over the pending input before this point. -func (lx *lexer) ignore() { - lx.start = lx.pos -} - -// backup steps back one rune. Can be called only twice between calls to next. -func (lx *lexer) backup() { - if lx.atEOF { - lx.atEOF = false - return - } - if lx.nprev < 1 { - panic("backed up too far") - } - w := lx.prevWidths[0] - lx.prevWidths[0] = lx.prevWidths[1] - lx.prevWidths[1] = lx.prevWidths[2] - lx.nprev-- - lx.pos -= w - if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' { - lx.line-- - } -} - -// accept consumes the next rune if it's equal to `valid`. -func (lx *lexer) accept(valid rune) bool { - if lx.next() == valid { - return true - } - lx.backup() - return false -} - -// peek returns but does not consume the next rune in the input. -func (lx *lexer) peek() rune { - r := lx.next() - lx.backup() - return r -} - -// skip ignores all input that matches the given predicate. -func (lx *lexer) skip(pred func(rune) bool) { - for { - r := lx.next() - if pred(r) { - continue - } - lx.backup() - lx.ignore() - return - } -} - -// errorf stops all lexing by emitting an error and returning `nil`. -// Note that any value that is a character is escaped if it's a special -// character (newlines, tabs, etc.). -func (lx *lexer) errorf(format string, values ...interface{}) stateFn { - lx.items <- item{ - itemError, - fmt.Sprintf(format, values...), - lx.line, - } - return nil -} - -// lexTop consumes elements at the top level of TOML data. -func lexTop(lx *lexer) stateFn { - r := lx.next() - if isWhitespace(r) || isNL(r) { - return lexSkip(lx, lexTop) - } - switch r { - case commentStart: - lx.push(lexTop) - return lexCommentStart - case tableStart: - return lexTableStart - case eof: - if lx.pos > lx.start { - return lx.errorf("unexpected EOF") - } - lx.emit(itemEOF) - return nil - } - - // At this point, the only valid item can be a key, so we back up - // and let the key lexer do the rest. - lx.backup() - lx.push(lexTopEnd) - return lexKeyStart -} - -// lexTopEnd is entered whenever a top-level item has been consumed. (A value -// or a table.) It must see only whitespace, and will turn back to lexTop -// upon a newline. If it sees EOF, it will quit the lexer successfully. -func lexTopEnd(lx *lexer) stateFn { - r := lx.next() - switch { - case r == commentStart: - // a comment will read to a newline for us. - lx.push(lexTop) - return lexCommentStart - case isWhitespace(r): - return lexTopEnd - case isNL(r): - lx.ignore() - return lexTop - case r == eof: - lx.emit(itemEOF) - return nil - } - return lx.errorf("expected a top-level item to end with a newline, "+ - "comment, or EOF, but got %q instead", r) -} - -// lexTable lexes the beginning of a table. Namely, it makes sure that -// it starts with a character other than '.' and ']'. -// It assumes that '[' has already been consumed. -// It also handles the case that this is an item in an array of tables. -// e.g., '[[name]]'. -func lexTableStart(lx *lexer) stateFn { - if lx.peek() == arrayTableStart { - lx.next() - lx.emit(itemArrayTableStart) - lx.push(lexArrayTableEnd) - } else { - lx.emit(itemTableStart) - lx.push(lexTableEnd) - } - return lexTableNameStart -} - -func lexTableEnd(lx *lexer) stateFn { - lx.emit(itemTableEnd) - return lexTopEnd -} - -func lexArrayTableEnd(lx *lexer) stateFn { - if r := lx.next(); r != arrayTableEnd { - return lx.errorf("expected end of table array name delimiter %q, "+ - "but got %q instead", arrayTableEnd, r) - } - lx.emit(itemArrayTableEnd) - return lexTopEnd -} - -func lexTableNameStart(lx *lexer) stateFn { - lx.skip(isWhitespace) - switch r := lx.peek(); { - case r == tableEnd || r == eof: - return lx.errorf("unexpected end of table name " + - "(table names cannot be empty)") - case r == tableSep: - return lx.errorf("unexpected table separator " + - "(table names cannot be empty)") - case r == stringStart || r == rawStringStart: - lx.ignore() - lx.push(lexTableNameEnd) - return lexValue // reuse string lexing - default: - return lexBareTableName - } -} - -// lexBareTableName lexes the name of a table. It assumes that at least one -// valid character for the table has already been read. -func lexBareTableName(lx *lexer) stateFn { - r := lx.next() - if isBareKeyChar(r) { - return lexBareTableName - } - lx.backup() - lx.emit(itemText) - return lexTableNameEnd -} - -// lexTableNameEnd reads the end of a piece of a table name, optionally -// consuming whitespace. -func lexTableNameEnd(lx *lexer) stateFn { - lx.skip(isWhitespace) - switch r := lx.next(); { - case isWhitespace(r): - return lexTableNameEnd - case r == tableSep: - lx.ignore() - return lexTableNameStart - case r == tableEnd: - return lx.pop() - default: - return lx.errorf("expected '.' or ']' to end table name, "+ - "but got %q instead", r) - } -} - -// lexKeyStart consumes a key name up until the first non-whitespace character. -// lexKeyStart will ignore whitespace. -func lexKeyStart(lx *lexer) stateFn { - r := lx.peek() - switch { - case r == keySep: - return lx.errorf("unexpected key separator %q", keySep) - case isWhitespace(r) || isNL(r): - lx.next() - return lexSkip(lx, lexKeyStart) - case r == stringStart || r == rawStringStart: - lx.ignore() - lx.emit(itemKeyStart) - lx.push(lexKeyEnd) - return lexValue // reuse string lexing - default: - lx.ignore() - lx.emit(itemKeyStart) - return lexBareKey - } -} - -// lexBareKey consumes the text of a bare key. Assumes that the first character -// (which is not whitespace) has not yet been consumed. -func lexBareKey(lx *lexer) stateFn { - switch r := lx.next(); { - case isBareKeyChar(r): - return lexBareKey - case isWhitespace(r): - lx.backup() - lx.emit(itemText) - return lexKeyEnd - case r == keySep: - lx.backup() - lx.emit(itemText) - return lexKeyEnd - default: - return lx.errorf("bare keys cannot contain %q", r) - } -} - -// lexKeyEnd consumes the end of a key and trims whitespace (up to the key -// separator). -func lexKeyEnd(lx *lexer) stateFn { - switch r := lx.next(); { - case r == keySep: - return lexSkip(lx, lexValue) - case isWhitespace(r): - return lexSkip(lx, lexKeyEnd) - default: - return lx.errorf("expected key separator %q, but got %q instead", - keySep, r) - } -} - -// lexValue starts the consumption of a value anywhere a value is expected. -// lexValue will ignore whitespace. -// After a value is lexed, the last state on the next is popped and returned. -func lexValue(lx *lexer) stateFn { - // We allow whitespace to precede a value, but NOT newlines. - // In array syntax, the array states are responsible for ignoring newlines. - r := lx.next() - switch { - case isWhitespace(r): - return lexSkip(lx, lexValue) - case isDigit(r): - lx.backup() // avoid an extra state and use the same as above - return lexNumberOrDateStart - } - switch r { - case arrayStart: - lx.ignore() - lx.emit(itemArray) - return lexArrayValue - case inlineTableStart: - lx.ignore() - lx.emit(itemInlineTableStart) - return lexInlineTableValue - case stringStart: - if lx.accept(stringStart) { - if lx.accept(stringStart) { - lx.ignore() // Ignore """ - return lexMultilineString - } - lx.backup() - } - lx.ignore() // ignore the '"' - return lexString - case rawStringStart: - if lx.accept(rawStringStart) { - if lx.accept(rawStringStart) { - lx.ignore() // Ignore """ - return lexMultilineRawString - } - lx.backup() - } - lx.ignore() // ignore the "'" - return lexRawString - case '+', '-': - return lexNumberStart - case '.': // special error case, be kind to users - return lx.errorf("floats must start with a digit, not '.'") - } - if unicode.IsLetter(r) { - // Be permissive here; lexBool will give a nice error if the - // user wrote something like - // x = foo - // (i.e. not 'true' or 'false' but is something else word-like.) - lx.backup() - return lexBool - } - return lx.errorf("expected value but found %q instead", r) -} - -// lexArrayValue consumes one value in an array. It assumes that '[' or ',' -// have already been consumed. All whitespace and newlines are ignored. -func lexArrayValue(lx *lexer) stateFn { - r := lx.next() - switch { - case isWhitespace(r) || isNL(r): - return lexSkip(lx, lexArrayValue) - case r == commentStart: - lx.push(lexArrayValue) - return lexCommentStart - case r == comma: - return lx.errorf("unexpected comma") - case r == arrayEnd: - // NOTE(caleb): The spec isn't clear about whether you can have - // a trailing comma or not, so we'll allow it. - return lexArrayEnd - } - - lx.backup() - lx.push(lexArrayValueEnd) - return lexValue -} - -// lexArrayValueEnd consumes everything between the end of an array value and -// the next value (or the end of the array): it ignores whitespace and newlines -// and expects either a ',' or a ']'. -func lexArrayValueEnd(lx *lexer) stateFn { - r := lx.next() - switch { - case isWhitespace(r) || isNL(r): - return lexSkip(lx, lexArrayValueEnd) - case r == commentStart: - lx.push(lexArrayValueEnd) - return lexCommentStart - case r == comma: - lx.ignore() - return lexArrayValue // move on to the next value - case r == arrayEnd: - return lexArrayEnd - } - return lx.errorf( - "expected a comma or array terminator %q, but got %q instead", - arrayEnd, r, - ) -} - -// lexArrayEnd finishes the lexing of an array. -// It assumes that a ']' has just been consumed. -func lexArrayEnd(lx *lexer) stateFn { - lx.ignore() - lx.emit(itemArrayEnd) - return lx.pop() -} - -// lexInlineTableValue consumes one key/value pair in an inline table. -// It assumes that '{' or ',' have already been consumed. Whitespace is ignored. -func lexInlineTableValue(lx *lexer) stateFn { - r := lx.next() - switch { - case isWhitespace(r): - return lexSkip(lx, lexInlineTableValue) - case isNL(r): - return lx.errorf("newlines not allowed within inline tables") - case r == commentStart: - lx.push(lexInlineTableValue) - return lexCommentStart - case r == comma: - return lx.errorf("unexpected comma") - case r == inlineTableEnd: - return lexInlineTableEnd - } - lx.backup() - lx.push(lexInlineTableValueEnd) - return lexKeyStart -} - -// lexInlineTableValueEnd consumes everything between the end of an inline table -// key/value pair and the next pair (or the end of the table): -// it ignores whitespace and expects either a ',' or a '}'. -func lexInlineTableValueEnd(lx *lexer) stateFn { - r := lx.next() - switch { - case isWhitespace(r): - return lexSkip(lx, lexInlineTableValueEnd) - case isNL(r): - return lx.errorf("newlines not allowed within inline tables") - case r == commentStart: - lx.push(lexInlineTableValueEnd) - return lexCommentStart - case r == comma: - lx.ignore() - return lexInlineTableValue - case r == inlineTableEnd: - return lexInlineTableEnd - } - return lx.errorf("expected a comma or an inline table terminator %q, "+ - "but got %q instead", inlineTableEnd, r) -} - -// lexInlineTableEnd finishes the lexing of an inline table. -// It assumes that a '}' has just been consumed. -func lexInlineTableEnd(lx *lexer) stateFn { - lx.ignore() - lx.emit(itemInlineTableEnd) - return lx.pop() -} - -// lexString consumes the inner contents of a string. It assumes that the -// beginning '"' has already been consumed and ignored. -func lexString(lx *lexer) stateFn { - r := lx.next() - switch { - case r == eof: - return lx.errorf("unexpected EOF") - case isNL(r): - return lx.errorf("strings cannot contain newlines") - case r == '\\': - lx.push(lexString) - return lexStringEscape - case r == stringEnd: - lx.backup() - lx.emit(itemString) - lx.next() - lx.ignore() - return lx.pop() - } - return lexString -} - -// lexMultilineString consumes the inner contents of a string. It assumes that -// the beginning '"""' has already been consumed and ignored. -func lexMultilineString(lx *lexer) stateFn { - switch lx.next() { - case eof: - return lx.errorf("unexpected EOF") - case '\\': - return lexMultilineStringEscape - case stringEnd: - if lx.accept(stringEnd) { - if lx.accept(stringEnd) { - lx.backup() - lx.backup() - lx.backup() - lx.emit(itemMultilineString) - lx.next() - lx.next() - lx.next() - lx.ignore() - return lx.pop() - } - lx.backup() - } - } - return lexMultilineString -} - -// lexRawString consumes a raw string. Nothing can be escaped in such a string. -// It assumes that the beginning "'" has already been consumed and ignored. -func lexRawString(lx *lexer) stateFn { - r := lx.next() - switch { - case r == eof: - return lx.errorf("unexpected EOF") - case isNL(r): - return lx.errorf("strings cannot contain newlines") - case r == rawStringEnd: - lx.backup() - lx.emit(itemRawString) - lx.next() - lx.ignore() - return lx.pop() - } - return lexRawString -} - -// lexMultilineRawString consumes a raw string. Nothing can be escaped in such -// a string. It assumes that the beginning "'''" has already been consumed and -// ignored. -func lexMultilineRawString(lx *lexer) stateFn { - switch lx.next() { - case eof: - return lx.errorf("unexpected EOF") - case rawStringEnd: - if lx.accept(rawStringEnd) { - if lx.accept(rawStringEnd) { - lx.backup() - lx.backup() - lx.backup() - lx.emit(itemRawMultilineString) - lx.next() - lx.next() - lx.next() - lx.ignore() - return lx.pop() - } - lx.backup() - } - } - return lexMultilineRawString -} - -// lexMultilineStringEscape consumes an escaped character. It assumes that the -// preceding '\\' has already been consumed. -func lexMultilineStringEscape(lx *lexer) stateFn { - // Handle the special case first: - if isNL(lx.next()) { - return lexMultilineString - } - lx.backup() - lx.push(lexMultilineString) - return lexStringEscape(lx) -} - -func lexStringEscape(lx *lexer) stateFn { - r := lx.next() - switch r { - case 'b': - fallthrough - case 't': - fallthrough - case 'n': - fallthrough - case 'f': - fallthrough - case 'r': - fallthrough - case '"': - fallthrough - case '\\': - return lx.pop() - case 'u': - return lexShortUnicodeEscape - case 'U': - return lexLongUnicodeEscape - } - return lx.errorf("invalid escape character %q; only the following "+ - "escape characters are allowed: "+ - `\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r) -} - -func lexShortUnicodeEscape(lx *lexer) stateFn { - var r rune - for i := 0; i < 4; i++ { - r = lx.next() - if !isHexadecimal(r) { - return lx.errorf(`expected four hexadecimal digits after '\u', `+ - "but got %q instead", lx.current()) - } - } - return lx.pop() -} - -func lexLongUnicodeEscape(lx *lexer) stateFn { - var r rune - for i := 0; i < 8; i++ { - r = lx.next() - if !isHexadecimal(r) { - return lx.errorf(`expected eight hexadecimal digits after '\U', `+ - "but got %q instead", lx.current()) - } - } - return lx.pop() -} - -// lexNumberOrDateStart consumes either an integer, a float, or datetime. -func lexNumberOrDateStart(lx *lexer) stateFn { - r := lx.next() - if isDigit(r) { - return lexNumberOrDate - } - switch r { - case '_': - return lexNumber - case 'e', 'E': - return lexFloat - case '.': - return lx.errorf("floats must start with a digit, not '.'") - } - return lx.errorf("expected a digit but got %q", r) -} - -// lexNumberOrDate consumes either an integer, float or datetime. -func lexNumberOrDate(lx *lexer) stateFn { - r := lx.next() - if isDigit(r) { - return lexNumberOrDate - } - switch r { - case '-': - return lexDatetime - case '_': - return lexNumber - case '.', 'e', 'E': - return lexFloat - } - - lx.backup() - lx.emit(itemInteger) - return lx.pop() -} - -// lexDatetime consumes a Datetime, to a first approximation. -// The parser validates that it matches one of the accepted formats. -func lexDatetime(lx *lexer) stateFn { - r := lx.next() - if isDigit(r) { - return lexDatetime - } - switch r { - case '-', 'T', ':', '.', 'Z', '+': - return lexDatetime - } - - lx.backup() - lx.emit(itemDatetime) - return lx.pop() -} - -// lexNumberStart consumes either an integer or a float. It assumes that a sign -// has already been read, but that *no* digits have been consumed. -// lexNumberStart will move to the appropriate integer or float states. -func lexNumberStart(lx *lexer) stateFn { - // We MUST see a digit. Even floats have to start with a digit. - r := lx.next() - if !isDigit(r) { - if r == '.' { - return lx.errorf("floats must start with a digit, not '.'") - } - return lx.errorf("expected a digit but got %q", r) - } - return lexNumber -} - -// lexNumber consumes an integer or a float after seeing the first digit. -func lexNumber(lx *lexer) stateFn { - r := lx.next() - if isDigit(r) { - return lexNumber - } - switch r { - case '_': - return lexNumber - case '.', 'e', 'E': - return lexFloat - } - - lx.backup() - lx.emit(itemInteger) - return lx.pop() -} - -// lexFloat consumes the elements of a float. It allows any sequence of -// float-like characters, so floats emitted by the lexer are only a first -// approximation and must be validated by the parser. -func lexFloat(lx *lexer) stateFn { - r := lx.next() - if isDigit(r) { - return lexFloat - } - switch r { - case '_', '.', '-', '+', 'e', 'E': - return lexFloat - } - - lx.backup() - lx.emit(itemFloat) - return lx.pop() -} - -// lexBool consumes a bool string: 'true' or 'false. -func lexBool(lx *lexer) stateFn { - var rs []rune - for { - r := lx.next() - if !unicode.IsLetter(r) { - lx.backup() - break - } - rs = append(rs, r) - } - s := string(rs) - switch s { - case "true", "false": - lx.emit(itemBool) - return lx.pop() - } - return lx.errorf("expected value but found %q instead", s) -} - -// lexCommentStart begins the lexing of a comment. It will emit -// itemCommentStart and consume no characters, passing control to lexComment. -func lexCommentStart(lx *lexer) stateFn { - lx.ignore() - lx.emit(itemCommentStart) - return lexComment -} - -// lexComment lexes an entire comment. It assumes that '#' has been consumed. -// It will consume *up to* the first newline character, and pass control -// back to the last state on the stack. -func lexComment(lx *lexer) stateFn { - r := lx.peek() - if isNL(r) || r == eof { - lx.emit(itemText) - return lx.pop() - } - lx.next() - return lexComment -} - -// lexSkip ignores all slurped input and moves on to the next state. -func lexSkip(lx *lexer, nextState stateFn) stateFn { - return func(lx *lexer) stateFn { - lx.ignore() - return nextState - } -} - -// isWhitespace returns true if `r` is a whitespace character according -// to the spec. -func isWhitespace(r rune) bool { - return r == '\t' || r == ' ' -} - -func isNL(r rune) bool { - return r == '\n' || r == '\r' -} - -func isDigit(r rune) bool { - return r >= '0' && r <= '9' -} - -func isHexadecimal(r rune) bool { - return (r >= '0' && r <= '9') || - (r >= 'a' && r <= 'f') || - (r >= 'A' && r <= 'F') -} - -func isBareKeyChar(r rune) bool { - return (r >= 'A' && r <= 'Z') || - (r >= 'a' && r <= 'z') || - (r >= '0' && r <= '9') || - r == '_' || - r == '-' -} - -func (itype itemType) String() string { - switch itype { - case itemError: - return "Error" - case itemNIL: - return "NIL" - case itemEOF: - return "EOF" - case itemText: - return "Text" - case itemString, itemRawString, itemMultilineString, itemRawMultilineString: - return "String" - case itemBool: - return "Bool" - case itemInteger: - return "Integer" - case itemFloat: - return "Float" - case itemDatetime: - return "DateTime" - case itemTableStart: - return "TableStart" - case itemTableEnd: - return "TableEnd" - case itemKeyStart: - return "KeyStart" - case itemArray: - return "Array" - case itemArrayEnd: - return "ArrayEnd" - case itemCommentStart: - return "CommentStart" - } - panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype))) -} - -func (item item) String() string { - return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val) -} diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go deleted file mode 100644 index 50869ef9266e4..0000000000000 --- a/vendor/github.com/BurntSushi/toml/parse.go +++ /dev/null @@ -1,592 +0,0 @@ -package toml - -import ( - "fmt" - "strconv" - "strings" - "time" - "unicode" - "unicode/utf8" -) - -type parser struct { - mapping map[string]interface{} - types map[string]tomlType - lx *lexer - - // A list of keys in the order that they appear in the TOML data. - ordered []Key - - // the full key for the current hash in scope - context Key - - // the base key name for everything except hashes - currentKey string - - // rough approximation of line number - approxLine int - - // A map of 'key.group.names' to whether they were created implicitly. - implicits map[string]bool -} - -type parseError string - -func (pe parseError) Error() string { - return string(pe) -} - -func parse(data string) (p *parser, err error) { - defer func() { - if r := recover(); r != nil { - var ok bool - if err, ok = r.(parseError); ok { - return - } - panic(r) - } - }() - - p = &parser{ - mapping: make(map[string]interface{}), - types: make(map[string]tomlType), - lx: lex(data), - ordered: make([]Key, 0), - implicits: make(map[string]bool), - } - for { - item := p.next() - if item.typ == itemEOF { - break - } - p.topLevel(item) - } - - return p, nil -} - -func (p *parser) panicf(format string, v ...interface{}) { - msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s", - p.approxLine, p.current(), fmt.Sprintf(format, v...)) - panic(parseError(msg)) -} - -func (p *parser) next() item { - it := p.lx.nextItem() - if it.typ == itemError { - p.panicf("%s", it.val) - } - return it -} - -func (p *parser) bug(format string, v ...interface{}) { - panic(fmt.Sprintf("BUG: "+format+"\n\n", v...)) -} - -func (p *parser) expect(typ itemType) item { - it := p.next() - p.assertEqual(typ, it.typ) - return it -} - -func (p *parser) assertEqual(expected, got itemType) { - if expected != got { - p.bug("Expected '%s' but got '%s'.", expected, got) - } -} - -func (p *parser) topLevel(item item) { - switch item.typ { - case itemCommentStart: - p.approxLine = item.line - p.expect(itemText) - case itemTableStart: - kg := p.next() - p.approxLine = kg.line - - var key Key - for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() { - key = append(key, p.keyString(kg)) - } - p.assertEqual(itemTableEnd, kg.typ) - - p.establishContext(key, false) - p.setType("", tomlHash) - p.ordered = append(p.ordered, key) - case itemArrayTableStart: - kg := p.next() - p.approxLine = kg.line - - var key Key - for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() { - key = append(key, p.keyString(kg)) - } - p.assertEqual(itemArrayTableEnd, kg.typ) - - p.establishContext(key, true) - p.setType("", tomlArrayHash) - p.ordered = append(p.ordered, key) - case itemKeyStart: - kname := p.next() - p.approxLine = kname.line - p.currentKey = p.keyString(kname) - - val, typ := p.value(p.next()) - p.setValue(p.currentKey, val) - p.setType(p.currentKey, typ) - p.ordered = append(p.ordered, p.context.add(p.currentKey)) - p.currentKey = "" - default: - p.bug("Unexpected type at top level: %s", item.typ) - } -} - -// Gets a string for a key (or part of a key in a table name). -func (p *parser) keyString(it item) string { - switch it.typ { - case itemText: - return it.val - case itemString, itemMultilineString, - itemRawString, itemRawMultilineString: - s, _ := p.value(it) - return s.(string) - default: - p.bug("Unexpected key type: %s", it.typ) - panic("unreachable") - } -} - -// value translates an expected value from the lexer into a Go value wrapped -// as an empty interface. -func (p *parser) value(it item) (interface{}, tomlType) { - switch it.typ { - case itemString: - return p.replaceEscapes(it.val), p.typeOfPrimitive(it) - case itemMultilineString: - trimmed := stripFirstNewline(stripEscapedWhitespace(it.val)) - return p.replaceEscapes(trimmed), p.typeOfPrimitive(it) - case itemRawString: - return it.val, p.typeOfPrimitive(it) - case itemRawMultilineString: - return stripFirstNewline(it.val), p.typeOfPrimitive(it) - case itemBool: - switch it.val { - case "true": - return true, p.typeOfPrimitive(it) - case "false": - return false, p.typeOfPrimitive(it) - } - p.bug("Expected boolean value, but got '%s'.", it.val) - case itemInteger: - if !numUnderscoresOK(it.val) { - p.panicf("Invalid integer %q: underscores must be surrounded by digits", - it.val) - } - val := strings.Replace(it.val, "_", "", -1) - num, err := strconv.ParseInt(val, 10, 64) - if err != nil { - // Distinguish integer values. Normally, it'd be a bug if the lexer - // provides an invalid integer, but it's possible that the number is - // out of range of valid values (which the lexer cannot determine). - // So mark the former as a bug but the latter as a legitimate user - // error. - if e, ok := err.(*strconv.NumError); ok && - e.Err == strconv.ErrRange { - - p.panicf("Integer '%s' is out of the range of 64-bit "+ - "signed integers.", it.val) - } else { - p.bug("Expected integer value, but got '%s'.", it.val) - } - } - return num, p.typeOfPrimitive(it) - case itemFloat: - parts := strings.FieldsFunc(it.val, func(r rune) bool { - switch r { - case '.', 'e', 'E': - return true - } - return false - }) - for _, part := range parts { - if !numUnderscoresOK(part) { - p.panicf("Invalid float %q: underscores must be "+ - "surrounded by digits", it.val) - } - } - if !numPeriodsOK(it.val) { - // As a special case, numbers like '123.' or '1.e2', - // which are valid as far as Go/strconv are concerned, - // must be rejected because TOML says that a fractional - // part consists of '.' followed by 1+ digits. - p.panicf("Invalid float %q: '.' must be followed "+ - "by one or more digits", it.val) - } - val := strings.Replace(it.val, "_", "", -1) - num, err := strconv.ParseFloat(val, 64) - if err != nil { - if e, ok := err.(*strconv.NumError); ok && - e.Err == strconv.ErrRange { - - p.panicf("Float '%s' is out of the range of 64-bit "+ - "IEEE-754 floating-point numbers.", it.val) - } else { - p.panicf("Invalid float value: %q", it.val) - } - } - return num, p.typeOfPrimitive(it) - case itemDatetime: - var t time.Time - var ok bool - var err error - for _, format := range []string{ - "2006-01-02T15:04:05Z07:00", - "2006-01-02T15:04:05", - "2006-01-02", - } { - t, err = time.ParseInLocation(format, it.val, time.Local) - if err == nil { - ok = true - break - } - } - if !ok { - p.panicf("Invalid TOML Datetime: %q.", it.val) - } - return t, p.typeOfPrimitive(it) - case itemArray: - array := make([]interface{}, 0) - types := make([]tomlType, 0) - - for it = p.next(); it.typ != itemArrayEnd; it = p.next() { - if it.typ == itemCommentStart { - p.expect(itemText) - continue - } - - val, typ := p.value(it) - array = append(array, val) - types = append(types, typ) - } - return array, p.typeOfArray(types) - case itemInlineTableStart: - var ( - hash = make(map[string]interface{}) - outerContext = p.context - outerKey = p.currentKey - ) - - p.context = append(p.context, p.currentKey) - p.currentKey = "" - for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() { - if it.typ != itemKeyStart { - p.bug("Expected key start but instead found %q, around line %d", - it.val, p.approxLine) - } - if it.typ == itemCommentStart { - p.expect(itemText) - continue - } - - // retrieve key - k := p.next() - p.approxLine = k.line - kname := p.keyString(k) - - // retrieve value - p.currentKey = kname - val, typ := p.value(p.next()) - // make sure we keep metadata up to date - p.setType(kname, typ) - p.ordered = append(p.ordered, p.context.add(p.currentKey)) - hash[kname] = val - } - p.context = outerContext - p.currentKey = outerKey - return hash, tomlHash - } - p.bug("Unexpected value type: %s", it.typ) - panic("unreachable") -} - -// numUnderscoresOK checks whether each underscore in s is surrounded by -// characters that are not underscores. -func numUnderscoresOK(s string) bool { - accept := false - for _, r := range s { - if r == '_' { - if !accept { - return false - } - accept = false - continue - } - accept = true - } - return accept -} - -// numPeriodsOK checks whether every period in s is followed by a digit. -func numPeriodsOK(s string) bool { - period := false - for _, r := range s { - if period && !isDigit(r) { - return false - } - period = r == '.' - } - return !period -} - -// establishContext sets the current context of the parser, -// where the context is either a hash or an array of hashes. Which one is -// set depends on the value of the `array` parameter. -// -// Establishing the context also makes sure that the key isn't a duplicate, and -// will create implicit hashes automatically. -func (p *parser) establishContext(key Key, array bool) { - var ok bool - - // Always start at the top level and drill down for our context. - hashContext := p.mapping - keyContext := make(Key, 0) - - // We only need implicit hashes for key[0:-1] - for _, k := range key[0 : len(key)-1] { - _, ok = hashContext[k] - keyContext = append(keyContext, k) - - // No key? Make an implicit hash and move on. - if !ok { - p.addImplicit(keyContext) - hashContext[k] = make(map[string]interface{}) - } - - // If the hash context is actually an array of tables, then set - // the hash context to the last element in that array. - // - // Otherwise, it better be a table, since this MUST be a key group (by - // virtue of it not being the last element in a key). - switch t := hashContext[k].(type) { - case []map[string]interface{}: - hashContext = t[len(t)-1] - case map[string]interface{}: - hashContext = t - default: - p.panicf("Key '%s' was already created as a hash.", keyContext) - } - } - - p.context = keyContext - if array { - // If this is the first element for this array, then allocate a new - // list of tables for it. - k := key[len(key)-1] - if _, ok := hashContext[k]; !ok { - hashContext[k] = make([]map[string]interface{}, 0, 5) - } - - // Add a new table. But make sure the key hasn't already been used - // for something else. - if hash, ok := hashContext[k].([]map[string]interface{}); ok { - hashContext[k] = append(hash, make(map[string]interface{})) - } else { - p.panicf("Key '%s' was already created and cannot be used as "+ - "an array.", keyContext) - } - } else { - p.setValue(key[len(key)-1], make(map[string]interface{})) - } - p.context = append(p.context, key[len(key)-1]) -} - -// setValue sets the given key to the given value in the current context. -// It will make sure that the key hasn't already been defined, account for -// implicit key groups. -func (p *parser) setValue(key string, value interface{}) { - var tmpHash interface{} - var ok bool - - hash := p.mapping - keyContext := make(Key, 0) - for _, k := range p.context { - keyContext = append(keyContext, k) - if tmpHash, ok = hash[k]; !ok { - p.bug("Context for key '%s' has not been established.", keyContext) - } - switch t := tmpHash.(type) { - case []map[string]interface{}: - // The context is a table of hashes. Pick the most recent table - // defined as the current hash. - hash = t[len(t)-1] - case map[string]interface{}: - hash = t - default: - p.bug("Expected hash to have type 'map[string]interface{}', but "+ - "it has '%T' instead.", tmpHash) - } - } - keyContext = append(keyContext, key) - - if _, ok := hash[key]; ok { - // Typically, if the given key has already been set, then we have - // to raise an error since duplicate keys are disallowed. However, - // it's possible that a key was previously defined implicitly. In this - // case, it is allowed to be redefined concretely. (See the - // `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.) - // - // But we have to make sure to stop marking it as an implicit. (So that - // another redefinition provokes an error.) - // - // Note that since it has already been defined (as a hash), we don't - // want to overwrite it. So our business is done. - if p.isImplicit(keyContext) { - p.removeImplicit(keyContext) - return - } - - // Otherwise, we have a concrete key trying to override a previous - // key, which is *always* wrong. - p.panicf("Key '%s' has already been defined.", keyContext) - } - hash[key] = value -} - -// setType sets the type of a particular value at a given key. -// It should be called immediately AFTER setValue. -// -// Note that if `key` is empty, then the type given will be applied to the -// current context (which is either a table or an array of tables). -func (p *parser) setType(key string, typ tomlType) { - keyContext := make(Key, 0, len(p.context)+1) - for _, k := range p.context { - keyContext = append(keyContext, k) - } - if len(key) > 0 { // allow type setting for hashes - keyContext = append(keyContext, key) - } - p.types[keyContext.String()] = typ -} - -// addImplicit sets the given Key as having been created implicitly. -func (p *parser) addImplicit(key Key) { - p.implicits[key.String()] = true -} - -// removeImplicit stops tagging the given key as having been implicitly -// created. -func (p *parser) removeImplicit(key Key) { - p.implicits[key.String()] = false -} - -// isImplicit returns true if the key group pointed to by the key was created -// implicitly. -func (p *parser) isImplicit(key Key) bool { - return p.implicits[key.String()] -} - -// current returns the full key name of the current context. -func (p *parser) current() string { - if len(p.currentKey) == 0 { - return p.context.String() - } - if len(p.context) == 0 { - return p.currentKey - } - return fmt.Sprintf("%s.%s", p.context, p.currentKey) -} - -func stripFirstNewline(s string) string { - if len(s) == 0 || s[0] != '\n' { - return s - } - return s[1:] -} - -func stripEscapedWhitespace(s string) string { - esc := strings.Split(s, "\\\n") - if len(esc) > 1 { - for i := 1; i < len(esc); i++ { - esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace) - } - } - return strings.Join(esc, "") -} - -func (p *parser) replaceEscapes(str string) string { - var replaced []rune - s := []byte(str) - r := 0 - for r < len(s) { - if s[r] != '\\' { - c, size := utf8.DecodeRune(s[r:]) - r += size - replaced = append(replaced, c) - continue - } - r += 1 - if r >= len(s) { - p.bug("Escape sequence at end of string.") - return "" - } - switch s[r] { - default: - p.bug("Expected valid escape code after \\, but got %q.", s[r]) - return "" - case 'b': - replaced = append(replaced, rune(0x0008)) - r += 1 - case 't': - replaced = append(replaced, rune(0x0009)) - r += 1 - case 'n': - replaced = append(replaced, rune(0x000A)) - r += 1 - case 'f': - replaced = append(replaced, rune(0x000C)) - r += 1 - case 'r': - replaced = append(replaced, rune(0x000D)) - r += 1 - case '"': - replaced = append(replaced, rune(0x0022)) - r += 1 - case '\\': - replaced = append(replaced, rune(0x005C)) - r += 1 - case 'u': - // At this point, we know we have a Unicode escape of the form - // `uXXXX` at [r, r+5). (Because the lexer guarantees this - // for us.) - escaped := p.asciiEscapeToUnicode(s[r+1 : r+5]) - replaced = append(replaced, escaped) - r += 5 - case 'U': - // At this point, we know we have a Unicode escape of the form - // `uXXXX` at [r, r+9). (Because the lexer guarantees this - // for us.) - escaped := p.asciiEscapeToUnicode(s[r+1 : r+9]) - replaced = append(replaced, escaped) - r += 9 - } - } - return string(replaced) -} - -func (p *parser) asciiEscapeToUnicode(bs []byte) rune { - s := string(bs) - hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32) - if err != nil { - p.bug("Could not parse '%s' as a hexadecimal number, but the "+ - "lexer claims it's OK: %s", s, err) - } - if !utf8.ValidRune(rune(hex)) { - p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s) - } - return rune(hex) -} - -func isStringType(ty itemType) bool { - return ty == itemString || ty == itemMultilineString || - ty == itemRawString || ty == itemRawMultilineString -} diff --git a/vendor/github.com/BurntSushi/toml/type_check.go b/vendor/github.com/BurntSushi/toml/type_check.go deleted file mode 100644 index c73f8afc1a6db..0000000000000 --- a/vendor/github.com/BurntSushi/toml/type_check.go +++ /dev/null @@ -1,91 +0,0 @@ -package toml - -// tomlType represents any Go type that corresponds to a TOML type. -// While the first draft of the TOML spec has a simplistic type system that -// probably doesn't need this level of sophistication, we seem to be militating -// toward adding real composite types. -type tomlType interface { - typeString() string -} - -// typeEqual accepts any two types and returns true if they are equal. -func typeEqual(t1, t2 tomlType) bool { - if t1 == nil || t2 == nil { - return false - } - return t1.typeString() == t2.typeString() -} - -func typeIsHash(t tomlType) bool { - return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) -} - -type tomlBaseType string - -func (btype tomlBaseType) typeString() string { - return string(btype) -} - -func (btype tomlBaseType) String() string { - return btype.typeString() -} - -var ( - tomlInteger tomlBaseType = "Integer" - tomlFloat tomlBaseType = "Float" - tomlDatetime tomlBaseType = "Datetime" - tomlString tomlBaseType = "String" - tomlBool tomlBaseType = "Bool" - tomlArray tomlBaseType = "Array" - tomlHash tomlBaseType = "Hash" - tomlArrayHash tomlBaseType = "ArrayHash" -) - -// typeOfPrimitive returns a tomlType of any primitive value in TOML. -// Primitive values are: Integer, Float, Datetime, String and Bool. -// -// Passing a lexer item other than the following will cause a BUG message -// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. -func (p *parser) typeOfPrimitive(lexItem item) tomlType { - switch lexItem.typ { - case itemInteger: - return tomlInteger - case itemFloat: - return tomlFloat - case itemDatetime: - return tomlDatetime - case itemString: - return tomlString - case itemMultilineString: - return tomlString - case itemRawString: - return tomlString - case itemRawMultilineString: - return tomlString - case itemBool: - return tomlBool - } - p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) - panic("unreachable") -} - -// typeOfArray returns a tomlType for an array given a list of types of its -// values. -// -// In the current spec, if an array is homogeneous, then its type is always -// "Array". If the array is not homogeneous, an error is generated. -func (p *parser) typeOfArray(types []tomlType) tomlType { - // Empty arrays are cool. - if len(types) == 0 { - return tomlArray - } - - theType := types[0] - for _, t := range types[1:] { - if !typeEqual(theType, t) { - p.panicf("Array contains values of type '%s' and '%s', but "+ - "arrays must be homogeneous.", theType, t) - } - } - return tomlArray -} diff --git a/vendor/github.com/BurntSushi/toml/type_fields.go b/vendor/github.com/BurntSushi/toml/type_fields.go deleted file mode 100644 index 608997c22f68c..0000000000000 --- a/vendor/github.com/BurntSushi/toml/type_fields.go +++ /dev/null @@ -1,242 +0,0 @@ -package toml - -// Struct field handling is adapted from code in encoding/json: -// -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the Go distribution. - -import ( - "reflect" - "sort" - "sync" -) - -// A field represents a single field found in a struct. -type field struct { - name string // the name of the field (`toml` tag included) - tag bool // whether field has a `toml` tag - index []int // represents the depth of an anonymous field - typ reflect.Type // the type of the field -} - -// byName sorts field by name, breaking ties with depth, -// then breaking ties with "name came from toml tag", then -// breaking ties with index sequence. -type byName []field - -func (x byName) Len() int { return len(x) } - -func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } - -func (x byName) Less(i, j int) bool { - if x[i].name != x[j].name { - return x[i].name < x[j].name - } - if len(x[i].index) != len(x[j].index) { - return len(x[i].index) < len(x[j].index) - } - if x[i].tag != x[j].tag { - return x[i].tag - } - return byIndex(x).Less(i, j) -} - -// byIndex sorts field by index sequence. -type byIndex []field - -func (x byIndex) Len() int { return len(x) } - -func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } - -func (x byIndex) Less(i, j int) bool { - for k, xik := range x[i].index { - if k >= len(x[j].index) { - return false - } - if xik != x[j].index[k] { - return xik < x[j].index[k] - } - } - return len(x[i].index) < len(x[j].index) -} - -// typeFields returns a list of fields that TOML should recognize for the given -// type. The algorithm is breadth-first search over the set of structs to -// include - the top struct and then any reachable anonymous structs. -func typeFields(t reflect.Type) []field { - // Anonymous fields to explore at the current level and the next. - current := []field{} - next := []field{{typ: t}} - - // Count of queued names for current level and the next. - count := map[reflect.Type]int{} - nextCount := map[reflect.Type]int{} - - // Types already visited at an earlier level. - visited := map[reflect.Type]bool{} - - // Fields found. - var fields []field - - for len(next) > 0 { - current, next = next, current[:0] - count, nextCount = nextCount, map[reflect.Type]int{} - - for _, f := range current { - if visited[f.typ] { - continue - } - visited[f.typ] = true - - // Scan f.typ for fields to include. - for i := 0; i < f.typ.NumField(); i++ { - sf := f.typ.Field(i) - if sf.PkgPath != "" && !sf.Anonymous { // unexported - continue - } - opts := getOptions(sf.Tag) - if opts.skip { - continue - } - index := make([]int, len(f.index)+1) - copy(index, f.index) - index[len(f.index)] = i - - ft := sf.Type - if ft.Name() == "" && ft.Kind() == reflect.Ptr { - // Follow pointer. - ft = ft.Elem() - } - - // Record found field and index sequence. - if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { - tagged := opts.name != "" - name := opts.name - if name == "" { - name = sf.Name - } - fields = append(fields, field{name, tagged, index, ft}) - if count[f.typ] > 1 { - // If there were multiple instances, add a second, - // so that the annihilation code will see a duplicate. - // It only cares about the distinction between 1 or 2, - // so don't bother generating any more copies. - fields = append(fields, fields[len(fields)-1]) - } - continue - } - - // Record new anonymous struct to explore in next round. - nextCount[ft]++ - if nextCount[ft] == 1 { - f := field{name: ft.Name(), index: index, typ: ft} - next = append(next, f) - } - } - } - } - - sort.Sort(byName(fields)) - - // Delete all fields that are hidden by the Go rules for embedded fields, - // except that fields with TOML tags are promoted. - - // The fields are sorted in primary order of name, secondary order - // of field index length. Loop over names; for each name, delete - // hidden fields by choosing the one dominant field that survives. - out := fields[:0] - for advance, i := 0, 0; i < len(fields); i += advance { - // One iteration per name. - // Find the sequence of fields with the name of this first field. - fi := fields[i] - name := fi.name - for advance = 1; i+advance < len(fields); advance++ { - fj := fields[i+advance] - if fj.name != name { - break - } - } - if advance == 1 { // Only one field with this name - out = append(out, fi) - continue - } - dominant, ok := dominantField(fields[i : i+advance]) - if ok { - out = append(out, dominant) - } - } - - fields = out - sort.Sort(byIndex(fields)) - - return fields -} - -// dominantField looks through the fields, all of which are known to -// have the same name, to find the single field that dominates the -// others using Go's embedding rules, modified by the presence of -// TOML tags. If there are multiple top-level fields, the boolean -// will be false: This condition is an error in Go and we skip all -// the fields. -func dominantField(fields []field) (field, bool) { - // The fields are sorted in increasing index-length order. The winner - // must therefore be one with the shortest index length. Drop all - // longer entries, which is easy: just truncate the slice. - length := len(fields[0].index) - tagged := -1 // Index of first tagged field. - for i, f := range fields { - if len(f.index) > length { - fields = fields[:i] - break - } - if f.tag { - if tagged >= 0 { - // Multiple tagged fields at the same level: conflict. - // Return no field. - return field{}, false - } - tagged = i - } - } - if tagged >= 0 { - return fields[tagged], true - } - // All remaining fields have the same length. If there's more than one, - // we have a conflict (two fields named "X" at the same level) and we - // return no field. - if len(fields) > 1 { - return field{}, false - } - return fields[0], true -} - -var fieldCache struct { - sync.RWMutex - m map[reflect.Type][]field -} - -// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. -func cachedTypeFields(t reflect.Type) []field { - fieldCache.RLock() - f := fieldCache.m[t] - fieldCache.RUnlock() - if f != nil { - return f - } - - // Compute fields without lock. - // Might duplicate effort but won't hold other computations back. - f = typeFields(t) - if f == nil { - f = []field{} - } - - fieldCache.Lock() - if fieldCache.m == nil { - fieldCache.m = map[reflect.Type][]field{} - } - fieldCache.m[t] = f - fieldCache.Unlock() - return f -} diff --git a/vendor/github.com/Graylog2/go-gelf/gelf/message.go b/vendor/github.com/Graylog2/go-gelf/gelf/message.go index 4e5a05ec325e6..fcb182f9122e3 100644 --- a/vendor/github.com/Graylog2/go-gelf/gelf/message.go +++ b/vendor/github.com/Graylog2/go-gelf/gelf/message.go @@ -3,6 +3,7 @@ package gelf import ( "bytes" "encoding/json" + "fmt" "time" ) @@ -84,29 +85,35 @@ func (m *Message) UnmarshalJSON(data []byte) error { m.Extra[k] = v continue } + + ok := true switch k { case "version": - m.Version = v.(string) + m.Version, ok = v.(string) case "host": - m.Host = v.(string) + m.Host, ok = v.(string) case "short_message": - m.Short = v.(string) + m.Short, ok = v.(string) case "full_message": - m.Full = v.(string) + m.Full, ok = v.(string) case "timestamp": - m.TimeUnix = v.(float64) + m.TimeUnix, ok = v.(float64) case "level": - m.Level = int32(v.(float64)) + var level float64 + level, ok = v.(float64) + m.Level = int32(level) case "facility": - m.Facility = v.(string) + m.Facility, ok = v.(string) + } + + if !ok { + return fmt.Errorf("invalid type for field %s", k) } } return nil } -func (m *Message) toBytes() (messageBytes []byte, err error) { - buf := newBuffer() - defer bufPool.Put(buf) +func (m *Message) toBytes(buf *bytes.Buffer) (messageBytes []byte, err error) { if err = m.MarshalJSONBuf(buf); err != nil { return nil, err } @@ -134,7 +141,7 @@ func constructMessage(p []byte, hostname string, facility string, file string, l Host: hostname, Short: string(short), Full: string(full), - TimeUnix: float64(time.Now().Unix()), + TimeUnix: float64(time.Now().UnixNano()) / float64(time.Second), Level: 6, // info Facility: facility, Extra: map[string]interface{}{ diff --git a/vendor/github.com/Graylog2/go-gelf/gelf/tcpwriter.go b/vendor/github.com/Graylog2/go-gelf/gelf/tcpwriter.go index da1390d1d63a7..5a637ff19b8a5 100644 --- a/vendor/github.com/Graylog2/go-gelf/gelf/tcpwriter.go +++ b/vendor/github.com/Graylog2/go-gelf/gelf/tcpwriter.go @@ -43,7 +43,9 @@ func NewTCPWriter(addr string) (*TCPWriter, error) { // filled out appropriately. In general, clients will want to use // Write, rather than WriteMessage. func (w *TCPWriter) WriteMessage(m *Message) (err error) { - messageBytes, err := m.toBytes() + buf := newBuffer() + defer bufPool.Put(buf) + messageBytes, err := m.toBytes(buf) if err != nil { return err } diff --git a/vendor/github.com/Microsoft/go-winio/README.md b/vendor/github.com/Microsoft/go-winio/README.md index 5680010575b7f..683be1dcf9c8a 100644 --- a/vendor/github.com/Microsoft/go-winio/README.md +++ b/vendor/github.com/Microsoft/go-winio/README.md @@ -1,4 +1,4 @@ -# go-winio +# go-winio [![Build Status](https://github.com/microsoft/go-winio/actions/workflows/ci.yml/badge.svg)](https://github.com/microsoft/go-winio/actions/workflows/ci.yml) This repository contains utilities for efficiently performing Win32 IO operations in Go. Currently, this is focused on accessing named pipes and other file handles, and @@ -11,12 +11,27 @@ package. Please see the LICENSE file for licensing information. -This project has adopted the [Microsoft Open Source Code of -Conduct](https://opensource.microsoft.com/codeofconduct/). For more information -see the [Code of Conduct -FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact -[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional -questions or comments. +## Contributing +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) +declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR +appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. + +We also require that contributors sign their commits using git commit -s or git commit --signoff to certify they either authored the work themselves +or otherwise have permission to use it in this project. Please see https://developercertificate.org/ for more info, as well as to make sure that you can +attest to the rules listed. Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off. + + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + + + +## Special Thanks Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe for another named pipe implementation. diff --git a/vendor/github.com/Microsoft/go-winio/archive/tar/LICENSE b/vendor/github.com/Microsoft/go-winio/archive/tar/LICENSE deleted file mode 100644 index 74487567632c8..0000000000000 --- a/vendor/github.com/Microsoft/go-winio/archive/tar/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/Microsoft/go-winio/archive/tar/common.go b/vendor/github.com/Microsoft/go-winio/archive/tar/common.go deleted file mode 100644 index 0378401c0d11f..0000000000000 --- a/vendor/github.com/Microsoft/go-winio/archive/tar/common.go +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package tar implements access to tar archives. -// It aims to cover most of the variations, including those produced -// by GNU and BSD tars. -// -// References: -// http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5 -// http://www.gnu.org/software/tar/manual/html_node/Standard.html -// http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html -package tar - -import ( - "bytes" - "errors" - "fmt" - "os" - "path" - "time" -) - -const ( - blockSize = 512 - - // Types - TypeReg = '0' // regular file - TypeRegA = '\x00' // regular file - TypeLink = '1' // hard link - TypeSymlink = '2' // symbolic link - TypeChar = '3' // character device node - TypeBlock = '4' // block device node - TypeDir = '5' // directory - TypeFifo = '6' // fifo node - TypeCont = '7' // reserved - TypeXHeader = 'x' // extended header - TypeXGlobalHeader = 'g' // global extended header - TypeGNULongName = 'L' // Next file has a long name - TypeGNULongLink = 'K' // Next file symlinks to a file w/ a long name - TypeGNUSparse = 'S' // sparse file -) - -// A Header represents a single header in a tar archive. -// Some fields may not be populated. -type Header struct { - Name string // name of header file entry - Mode int64 // permission and mode bits - Uid int // user id of owner - Gid int // group id of owner - Size int64 // length in bytes - ModTime time.Time // modified time - Typeflag byte // type of header entry - Linkname string // target name of link - Uname string // user name of owner - Gname string // group name of owner - Devmajor int64 // major number of character or block device - Devminor int64 // minor number of character or block device - AccessTime time.Time // access time - ChangeTime time.Time // status change time - CreationTime time.Time // creation time - Xattrs map[string]string - Winheaders map[string]string -} - -// File name constants from the tar spec. -const ( - fileNameSize = 100 // Maximum number of bytes in a standard tar name. - fileNamePrefixSize = 155 // Maximum number of ustar extension bytes. -) - -// FileInfo returns an os.FileInfo for the Header. -func (h *Header) FileInfo() os.FileInfo { - return headerFileInfo{h} -} - -// headerFileInfo implements os.FileInfo. -type headerFileInfo struct { - h *Header -} - -func (fi headerFileInfo) Size() int64 { return fi.h.Size } -func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } -func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } -func (fi headerFileInfo) Sys() interface{} { return fi.h } - -// Name returns the base name of the file. -func (fi headerFileInfo) Name() string { - if fi.IsDir() { - return path.Base(path.Clean(fi.h.Name)) - } - return path.Base(fi.h.Name) -} - -// Mode returns the permission and mode bits for the headerFileInfo. -func (fi headerFileInfo) Mode() (mode os.FileMode) { - // Set file permission bits. - mode = os.FileMode(fi.h.Mode).Perm() - - // Set setuid, setgid and sticky bits. - if fi.h.Mode&c_ISUID != 0 { - // setuid - mode |= os.ModeSetuid - } - if fi.h.Mode&c_ISGID != 0 { - // setgid - mode |= os.ModeSetgid - } - if fi.h.Mode&c_ISVTX != 0 { - // sticky - mode |= os.ModeSticky - } - - // Set file mode bits. - // clear perm, setuid, setgid and sticky bits. - m := os.FileMode(fi.h.Mode) &^ 07777 - if m == c_ISDIR { - // directory - mode |= os.ModeDir - } - if m == c_ISFIFO { - // named pipe (FIFO) - mode |= os.ModeNamedPipe - } - if m == c_ISLNK { - // symbolic link - mode |= os.ModeSymlink - } - if m == c_ISBLK { - // device file - mode |= os.ModeDevice - } - if m == c_ISCHR { - // Unix character device - mode |= os.ModeDevice - mode |= os.ModeCharDevice - } - if m == c_ISSOCK { - // Unix domain socket - mode |= os.ModeSocket - } - - switch fi.h.Typeflag { - case TypeSymlink: - // symbolic link - mode |= os.ModeSymlink - case TypeChar: - // character device node - mode |= os.ModeDevice - mode |= os.ModeCharDevice - case TypeBlock: - // block device node - mode |= os.ModeDevice - case TypeDir: - // directory - mode |= os.ModeDir - case TypeFifo: - // fifo node - mode |= os.ModeNamedPipe - } - - return mode -} - -// sysStat, if non-nil, populates h from system-dependent fields of fi. -var sysStat func(fi os.FileInfo, h *Header) error - -// Mode constants from the tar spec. -const ( - c_ISUID = 04000 // Set uid - c_ISGID = 02000 // Set gid - c_ISVTX = 01000 // Save text (sticky bit) - c_ISDIR = 040000 // Directory - c_ISFIFO = 010000 // FIFO - c_ISREG = 0100000 // Regular file - c_ISLNK = 0120000 // Symbolic link - c_ISBLK = 060000 // Block special file - c_ISCHR = 020000 // Character special file - c_ISSOCK = 0140000 // Socket -) - -// Keywords for the PAX Extended Header -const ( - paxAtime = "atime" - paxCharset = "charset" - paxComment = "comment" - paxCtime = "ctime" // please note that ctime is not a valid pax header. - paxCreationTime = "LIBARCHIVE.creationtime" - paxGid = "gid" - paxGname = "gname" - paxLinkpath = "linkpath" - paxMtime = "mtime" - paxPath = "path" - paxSize = "size" - paxUid = "uid" - paxUname = "uname" - paxXattr = "SCHILY.xattr." - paxWindows = "MSWINDOWS." - paxNone = "" -) - -// FileInfoHeader creates a partially-populated Header from fi. -// If fi describes a symlink, FileInfoHeader records link as the link target. -// If fi describes a directory, a slash is appended to the name. -// Because os.FileInfo's Name method returns only the base name of -// the file it describes, it may be necessary to modify the Name field -// of the returned header to provide the full path name of the file. -func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { - if fi == nil { - return nil, errors.New("tar: FileInfo is nil") - } - fm := fi.Mode() - h := &Header{ - Name: fi.Name(), - ModTime: fi.ModTime(), - Mode: int64(fm.Perm()), // or'd with c_IS* constants later - } - switch { - case fm.IsRegular(): - h.Mode |= c_ISREG - h.Typeflag = TypeReg - h.Size = fi.Size() - case fi.IsDir(): - h.Typeflag = TypeDir - h.Mode |= c_ISDIR - h.Name += "/" - case fm&os.ModeSymlink != 0: - h.Typeflag = TypeSymlink - h.Mode |= c_ISLNK - h.Linkname = link - case fm&os.ModeDevice != 0: - if fm&os.ModeCharDevice != 0 { - h.Mode |= c_ISCHR - h.Typeflag = TypeChar - } else { - h.Mode |= c_ISBLK - h.Typeflag = TypeBlock - } - case fm&os.ModeNamedPipe != 0: - h.Typeflag = TypeFifo - h.Mode |= c_ISFIFO - case fm&os.ModeSocket != 0: - h.Mode |= c_ISSOCK - default: - return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm) - } - if fm&os.ModeSetuid != 0 { - h.Mode |= c_ISUID - } - if fm&os.ModeSetgid != 0 { - h.Mode |= c_ISGID - } - if fm&os.ModeSticky != 0 { - h.Mode |= c_ISVTX - } - // If possible, populate additional fields from OS-specific - // FileInfo fields. - if sys, ok := fi.Sys().(*Header); ok { - // This FileInfo came from a Header (not the OS). Use the - // original Header to populate all remaining fields. - h.Uid = sys.Uid - h.Gid = sys.Gid - h.Uname = sys.Uname - h.Gname = sys.Gname - h.AccessTime = sys.AccessTime - h.ChangeTime = sys.ChangeTime - if sys.Xattrs != nil { - h.Xattrs = make(map[string]string) - for k, v := range sys.Xattrs { - h.Xattrs[k] = v - } - } - if sys.Typeflag == TypeLink { - // hard link - h.Typeflag = TypeLink - h.Size = 0 - h.Linkname = sys.Linkname - } - } - if sysStat != nil { - return h, sysStat(fi, h) - } - return h, nil -} - -var zeroBlock = make([]byte, blockSize) - -// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values. -// We compute and return both. -func checksum(header []byte) (unsigned int64, signed int64) { - for i := 0; i < len(header); i++ { - if i == 148 { - // The chksum field (header[148:156]) is special: it should be treated as space bytes. - unsigned += ' ' * 8 - signed += ' ' * 8 - i += 7 - continue - } - unsigned += int64(header[i]) - signed += int64(int8(header[i])) - } - return -} - -type slicer []byte - -func (sp *slicer) next(n int) (b []byte) { - s := *sp - b, *sp = s[0:n], s[n:] - return -} - -func isASCII(s string) bool { - for _, c := range s { - if c >= 0x80 { - return false - } - } - return true -} - -func toASCII(s string) string { - if isASCII(s) { - return s - } - var buf bytes.Buffer - for _, c := range s { - if c < 0x80 { - buf.WriteByte(byte(c)) - } - } - return buf.String() -} - -// isHeaderOnlyType checks if the given type flag is of the type that has no -// data section even if a size is specified. -func isHeaderOnlyType(flag byte) bool { - switch flag { - case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo: - return true - default: - return false - } -} diff --git a/vendor/github.com/Microsoft/go-winio/archive/tar/reader.go b/vendor/github.com/Microsoft/go-winio/archive/tar/reader.go deleted file mode 100644 index e210c618a1831..0000000000000 --- a/vendor/github.com/Microsoft/go-winio/archive/tar/reader.go +++ /dev/null @@ -1,1002 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tar - -// TODO(dsymonds): -// - pax extensions - -import ( - "bytes" - "errors" - "io" - "io/ioutil" - "math" - "os" - "strconv" - "strings" - "time" -) - -var ( - ErrHeader = errors.New("archive/tar: invalid tar header") -) - -const maxNanoSecondIntSize = 9 - -// A Reader provides sequential access to the contents of a tar archive. -// A tar archive consists of a sequence of files. -// The Next method advances to the next file in the archive (including the first), -// and then it can be treated as an io.Reader to access the file's data. -type Reader struct { - r io.Reader - err error - pad int64 // amount of padding (ignored) after current file entry - curr numBytesReader // reader for current file entry - hdrBuff [blockSize]byte // buffer to use in readHeader -} - -type parser struct { - err error // Last error seen -} - -// A numBytesReader is an io.Reader with a numBytes method, returning the number -// of bytes remaining in the underlying encoded data. -type numBytesReader interface { - io.Reader - numBytes() int64 -} - -// A regFileReader is a numBytesReader for reading file data from a tar archive. -type regFileReader struct { - r io.Reader // underlying reader - nb int64 // number of unread bytes for current file entry -} - -// A sparseFileReader is a numBytesReader for reading sparse file data from a -// tar archive. -type sparseFileReader struct { - rfr numBytesReader // Reads the sparse-encoded file data - sp []sparseEntry // The sparse map for the file - pos int64 // Keeps track of file position - total int64 // Total size of the file -} - -// A sparseEntry holds a single entry in a sparse file's sparse map. -// -// Sparse files are represented using a series of sparseEntrys. -// Despite the name, a sparseEntry represents an actual data fragment that -// references data found in the underlying archive stream. All regions not -// covered by a sparseEntry are logically filled with zeros. -// -// For example, if the underlying raw file contains the 10-byte data: -// var compactData = "abcdefgh" -// -// And the sparse map has the following entries: -// var sp = []sparseEntry{ -// {offset: 2, numBytes: 5} // Data fragment for [2..7] -// {offset: 18, numBytes: 3} // Data fragment for [18..21] -// } -// -// Then the content of the resulting sparse file with a "real" size of 25 is: -// var sparseData = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4 -type sparseEntry struct { - offset int64 // Starting position of the fragment - numBytes int64 // Length of the fragment -} - -// Keywords for GNU sparse files in a PAX extended header -const ( - paxGNUSparseNumBlocks = "GNU.sparse.numblocks" - paxGNUSparseOffset = "GNU.sparse.offset" - paxGNUSparseNumBytes = "GNU.sparse.numbytes" - paxGNUSparseMap = "GNU.sparse.map" - paxGNUSparseName = "GNU.sparse.name" - paxGNUSparseMajor = "GNU.sparse.major" - paxGNUSparseMinor = "GNU.sparse.minor" - paxGNUSparseSize = "GNU.sparse.size" - paxGNUSparseRealSize = "GNU.sparse.realsize" -) - -// Keywords for old GNU sparse headers -const ( - oldGNUSparseMainHeaderOffset = 386 - oldGNUSparseMainHeaderIsExtendedOffset = 482 - oldGNUSparseMainHeaderNumEntries = 4 - oldGNUSparseExtendedHeaderIsExtendedOffset = 504 - oldGNUSparseExtendedHeaderNumEntries = 21 - oldGNUSparseOffsetSize = 12 - oldGNUSparseNumBytesSize = 12 -) - -// NewReader creates a new Reader reading from r. -func NewReader(r io.Reader) *Reader { return &Reader{r: r} } - -// Next advances to the next entry in the tar archive. -// -// io.EOF is returned at the end of the input. -func (tr *Reader) Next() (*Header, error) { - if tr.err != nil { - return nil, tr.err - } - - var hdr *Header - var extHdrs map[string]string - - // Externally, Next iterates through the tar archive as if it is a series of - // files. Internally, the tar format often uses fake "files" to add meta - // data that describes the next file. These meta data "files" should not - // normally be visible to the outside. As such, this loop iterates through - // one or more "header files" until it finds a "normal file". -loop: - for { - tr.err = tr.skipUnread() - if tr.err != nil { - return nil, tr.err - } - - hdr = tr.readHeader() - if tr.err != nil { - return nil, tr.err - } - - // Check for PAX/GNU special headers and files. - switch hdr.Typeflag { - case TypeXHeader: - extHdrs, tr.err = parsePAX(tr) - if tr.err != nil { - return nil, tr.err - } - continue loop // This is a meta header affecting the next header - case TypeGNULongName, TypeGNULongLink: - var realname []byte - realname, tr.err = ioutil.ReadAll(tr) - if tr.err != nil { - return nil, tr.err - } - - // Convert GNU extensions to use PAX headers. - if extHdrs == nil { - extHdrs = make(map[string]string) - } - var p parser - switch hdr.Typeflag { - case TypeGNULongName: - extHdrs[paxPath] = p.parseString(realname) - case TypeGNULongLink: - extHdrs[paxLinkpath] = p.parseString(realname) - } - if p.err != nil { - tr.err = p.err - return nil, tr.err - } - continue loop // This is a meta header affecting the next header - default: - mergePAX(hdr, extHdrs) - - // Check for a PAX format sparse file - sp, err := tr.checkForGNUSparsePAXHeaders(hdr, extHdrs) - if err != nil { - tr.err = err - return nil, err - } - if sp != nil { - // Current file is a PAX format GNU sparse file. - // Set the current file reader to a sparse file reader. - tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size) - if tr.err != nil { - return nil, tr.err - } - } - break loop // This is a file, so stop - } - } - return hdr, nil -} - -// checkForGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. If they are found, then -// this function reads the sparse map and returns it. Unknown sparse formats are ignored, causing the file to -// be treated as a regular file. -func (tr *Reader) checkForGNUSparsePAXHeaders(hdr *Header, headers map[string]string) ([]sparseEntry, error) { - var sparseFormat string - - // Check for sparse format indicators - major, majorOk := headers[paxGNUSparseMajor] - minor, minorOk := headers[paxGNUSparseMinor] - sparseName, sparseNameOk := headers[paxGNUSparseName] - _, sparseMapOk := headers[paxGNUSparseMap] - sparseSize, sparseSizeOk := headers[paxGNUSparseSize] - sparseRealSize, sparseRealSizeOk := headers[paxGNUSparseRealSize] - - // Identify which, if any, sparse format applies from which PAX headers are set - if majorOk && minorOk { - sparseFormat = major + "." + minor - } else if sparseNameOk && sparseMapOk { - sparseFormat = "0.1" - } else if sparseSizeOk { - sparseFormat = "0.0" - } else { - // Not a PAX format GNU sparse file. - return nil, nil - } - - // Check for unknown sparse format - if sparseFormat != "0.0" && sparseFormat != "0.1" && sparseFormat != "1.0" { - return nil, nil - } - - // Update hdr from GNU sparse PAX headers - if sparseNameOk { - hdr.Name = sparseName - } - if sparseSizeOk { - realSize, err := strconv.ParseInt(sparseSize, 10, 0) - if err != nil { - return nil, ErrHeader - } - hdr.Size = realSize - } else if sparseRealSizeOk { - realSize, err := strconv.ParseInt(sparseRealSize, 10, 0) - if err != nil { - return nil, ErrHeader - } - hdr.Size = realSize - } - - // Set up the sparse map, according to the particular sparse format in use - var sp []sparseEntry - var err error - switch sparseFormat { - case "0.0", "0.1": - sp, err = readGNUSparseMap0x1(headers) - case "1.0": - sp, err = readGNUSparseMap1x0(tr.curr) - } - return sp, err -} - -// mergePAX merges well known headers according to PAX standard. -// In general headers with the same name as those found -// in the header struct overwrite those found in the header -// struct with higher precision or longer values. Esp. useful -// for name and linkname fields. -func mergePAX(hdr *Header, headers map[string]string) error { - for k, v := range headers { - switch k { - case paxPath: - hdr.Name = v - case paxLinkpath: - hdr.Linkname = v - case paxGname: - hdr.Gname = v - case paxUname: - hdr.Uname = v - case paxUid: - uid, err := strconv.ParseInt(v, 10, 0) - if err != nil { - return err - } - hdr.Uid = int(uid) - case paxGid: - gid, err := strconv.ParseInt(v, 10, 0) - if err != nil { - return err - } - hdr.Gid = int(gid) - case paxAtime: - t, err := parsePAXTime(v) - if err != nil { - return err - } - hdr.AccessTime = t - case paxMtime: - t, err := parsePAXTime(v) - if err != nil { - return err - } - hdr.ModTime = t - case paxCtime: - t, err := parsePAXTime(v) - if err != nil { - return err - } - hdr.ChangeTime = t - case paxCreationTime: - t, err := parsePAXTime(v) - if err != nil { - return err - } - hdr.CreationTime = t - case paxSize: - size, err := strconv.ParseInt(v, 10, 0) - if err != nil { - return err - } - hdr.Size = int64(size) - default: - if strings.HasPrefix(k, paxXattr) { - if hdr.Xattrs == nil { - hdr.Xattrs = make(map[string]string) - } - hdr.Xattrs[k[len(paxXattr):]] = v - } else if strings.HasPrefix(k, paxWindows) { - if hdr.Winheaders == nil { - hdr.Winheaders = make(map[string]string) - } - hdr.Winheaders[k[len(paxWindows):]] = v - } - } - } - return nil -} - -// parsePAXTime takes a string of the form %d.%d as described in -// the PAX specification. -func parsePAXTime(t string) (time.Time, error) { - buf := []byte(t) - pos := bytes.IndexByte(buf, '.') - var seconds, nanoseconds int64 - var err error - if pos == -1 { - seconds, err = strconv.ParseInt(t, 10, 0) - if err != nil { - return time.Time{}, err - } - } else { - seconds, err = strconv.ParseInt(string(buf[:pos]), 10, 0) - if err != nil { - return time.Time{}, err - } - nano_buf := string(buf[pos+1:]) - // Pad as needed before converting to a decimal. - // For example .030 -> .030000000 -> 30000000 nanoseconds - if len(nano_buf) < maxNanoSecondIntSize { - // Right pad - nano_buf += strings.Repeat("0", maxNanoSecondIntSize-len(nano_buf)) - } else if len(nano_buf) > maxNanoSecondIntSize { - // Right truncate - nano_buf = nano_buf[:maxNanoSecondIntSize] - } - nanoseconds, err = strconv.ParseInt(string(nano_buf), 10, 0) - if err != nil { - return time.Time{}, err - } - } - ts := time.Unix(seconds, nanoseconds) - return ts, nil -} - -// parsePAX parses PAX headers. -// If an extended header (type 'x') is invalid, ErrHeader is returned -func parsePAX(r io.Reader) (map[string]string, error) { - buf, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - sbuf := string(buf) - - // For GNU PAX sparse format 0.0 support. - // This function transforms the sparse format 0.0 headers into sparse format 0.1 headers. - var sparseMap bytes.Buffer - - headers := make(map[string]string) - // Each record is constructed as - // "%d %s=%s\n", length, keyword, value - for len(sbuf) > 0 { - key, value, residual, err := parsePAXRecord(sbuf) - if err != nil { - return nil, ErrHeader - } - sbuf = residual - - keyStr := string(key) - if keyStr == paxGNUSparseOffset || keyStr == paxGNUSparseNumBytes { - // GNU sparse format 0.0 special key. Write to sparseMap instead of using the headers map. - sparseMap.WriteString(value) - sparseMap.Write([]byte{','}) - } else { - // Normal key. Set the value in the headers map. - headers[keyStr] = string(value) - } - } - if sparseMap.Len() != 0 { - // Add sparse info to headers, chopping off the extra comma - sparseMap.Truncate(sparseMap.Len() - 1) - headers[paxGNUSparseMap] = sparseMap.String() - } - return headers, nil -} - -// parsePAXRecord parses the input PAX record string into a key-value pair. -// If parsing is successful, it will slice off the currently read record and -// return the remainder as r. -// -// A PAX record is of the following form: -// "%d %s=%s\n" % (size, key, value) -func parsePAXRecord(s string) (k, v, r string, err error) { - // The size field ends at the first space. - sp := strings.IndexByte(s, ' ') - if sp == -1 { - return "", "", s, ErrHeader - } - - // Parse the first token as a decimal integer. - n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int - if perr != nil || n < 5 || int64(len(s)) < n { - return "", "", s, ErrHeader - } - - // Extract everything between the space and the final newline. - rec, nl, rem := s[sp+1:n-1], s[n-1:n], s[n:] - if nl != "\n" { - return "", "", s, ErrHeader - } - - // The first equals separates the key from the value. - eq := strings.IndexByte(rec, '=') - if eq == -1 { - return "", "", s, ErrHeader - } - return rec[:eq], rec[eq+1:], rem, nil -} - -// parseString parses bytes as a NUL-terminated C-style string. -// If a NUL byte is not found then the whole slice is returned as a string. -func (*parser) parseString(b []byte) string { - n := 0 - for n < len(b) && b[n] != 0 { - n++ - } - return string(b[0:n]) -} - -// parseNumeric parses the input as being encoded in either base-256 or octal. -// This function may return negative numbers. -// If parsing fails or an integer overflow occurs, err will be set. -func (p *parser) parseNumeric(b []byte) int64 { - // Check for base-256 (binary) format first. - // If the first bit is set, then all following bits constitute a two's - // complement encoded number in big-endian byte order. - if len(b) > 0 && b[0]&0x80 != 0 { - // Handling negative numbers relies on the following identity: - // -a-1 == ^a - // - // If the number is negative, we use an inversion mask to invert the - // data bytes and treat the value as an unsigned number. - var inv byte // 0x00 if positive or zero, 0xff if negative - if b[0]&0x40 != 0 { - inv = 0xff - } - - var x uint64 - for i, c := range b { - c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing - if i == 0 { - c &= 0x7f // Ignore signal bit in first byte - } - if (x >> 56) > 0 { - p.err = ErrHeader // Integer overflow - return 0 - } - x = x<<8 | uint64(c) - } - if (x >> 63) > 0 { - p.err = ErrHeader // Integer overflow - return 0 - } - if inv == 0xff { - return ^int64(x) - } - return int64(x) - } - - // Normal case is base-8 (octal) format. - return p.parseOctal(b) -} - -func (p *parser) parseOctal(b []byte) int64 { - // Because unused fields are filled with NULs, we need - // to skip leading NULs. Fields may also be padded with - // spaces or NULs. - // So we remove leading and trailing NULs and spaces to - // be sure. - b = bytes.Trim(b, " \x00") - - if len(b) == 0 { - return 0 - } - x, perr := strconv.ParseUint(p.parseString(b), 8, 64) - if perr != nil { - p.err = ErrHeader - } - return int64(x) -} - -// skipUnread skips any unread bytes in the existing file entry, as well as any -// alignment padding. It returns io.ErrUnexpectedEOF if any io.EOF is -// encountered in the data portion; it is okay to hit io.EOF in the padding. -// -// Note that this function still works properly even when sparse files are being -// used since numBytes returns the bytes remaining in the underlying io.Reader. -func (tr *Reader) skipUnread() error { - dataSkip := tr.numBytes() // Number of data bytes to skip - totalSkip := dataSkip + tr.pad // Total number of bytes to skip - tr.curr, tr.pad = nil, 0 - - // If possible, Seek to the last byte before the end of the data section. - // Do this because Seek is often lazy about reporting errors; this will mask - // the fact that the tar stream may be truncated. We can rely on the - // io.CopyN done shortly afterwards to trigger any IO errors. - var seekSkipped int64 // Number of bytes skipped via Seek - if sr, ok := tr.r.(io.Seeker); ok && dataSkip > 1 { - // Not all io.Seeker can actually Seek. For example, os.Stdin implements - // io.Seeker, but calling Seek always returns an error and performs - // no action. Thus, we try an innocent seek to the current position - // to see if Seek is really supported. - pos1, err := sr.Seek(0, os.SEEK_CUR) - if err == nil { - // Seek seems supported, so perform the real Seek. - pos2, err := sr.Seek(dataSkip-1, os.SEEK_CUR) - if err != nil { - tr.err = err - return tr.err - } - seekSkipped = pos2 - pos1 - } - } - - var copySkipped int64 // Number of bytes skipped via CopyN - copySkipped, tr.err = io.CopyN(ioutil.Discard, tr.r, totalSkip-seekSkipped) - if tr.err == io.EOF && seekSkipped+copySkipped < dataSkip { - tr.err = io.ErrUnexpectedEOF - } - return tr.err -} - -func (tr *Reader) verifyChecksum(header []byte) bool { - if tr.err != nil { - return false - } - - var p parser - given := p.parseOctal(header[148:156]) - unsigned, signed := checksum(header) - return p.err == nil && (given == unsigned || given == signed) -} - -// readHeader reads the next block header and assumes that the underlying reader -// is already aligned to a block boundary. -// -// The err will be set to io.EOF only when one of the following occurs: -// * Exactly 0 bytes are read and EOF is hit. -// * Exactly 1 block of zeros is read and EOF is hit. -// * At least 2 blocks of zeros are read. -func (tr *Reader) readHeader() *Header { - header := tr.hdrBuff[:] - copy(header, zeroBlock) - - if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil { - return nil // io.EOF is okay here - } - - // Two blocks of zero bytes marks the end of the archive. - if bytes.Equal(header, zeroBlock[0:blockSize]) { - if _, tr.err = io.ReadFull(tr.r, header); tr.err != nil { - return nil // io.EOF is okay here - } - if bytes.Equal(header, zeroBlock[0:blockSize]) { - tr.err = io.EOF - } else { - tr.err = ErrHeader // zero block and then non-zero block - } - return nil - } - - if !tr.verifyChecksum(header) { - tr.err = ErrHeader - return nil - } - - // Unpack - var p parser - hdr := new(Header) - s := slicer(header) - - hdr.Name = p.parseString(s.next(100)) - hdr.Mode = p.parseNumeric(s.next(8)) - hdr.Uid = int(p.parseNumeric(s.next(8))) - hdr.Gid = int(p.parseNumeric(s.next(8))) - hdr.Size = p.parseNumeric(s.next(12)) - hdr.ModTime = time.Unix(p.parseNumeric(s.next(12)), 0) - s.next(8) // chksum - hdr.Typeflag = s.next(1)[0] - hdr.Linkname = p.parseString(s.next(100)) - - // The remainder of the header depends on the value of magic. - // The original (v7) version of tar had no explicit magic field, - // so its magic bytes, like the rest of the block, are NULs. - magic := string(s.next(8)) // contains version field as well. - var format string - switch { - case magic[:6] == "ustar\x00": // POSIX tar (1003.1-1988) - if string(header[508:512]) == "tar\x00" { - format = "star" - } else { - format = "posix" - } - case magic == "ustar \x00": // old GNU tar - format = "gnu" - } - - switch format { - case "posix", "gnu", "star": - hdr.Uname = p.parseString(s.next(32)) - hdr.Gname = p.parseString(s.next(32)) - devmajor := s.next(8) - devminor := s.next(8) - if hdr.Typeflag == TypeChar || hdr.Typeflag == TypeBlock { - hdr.Devmajor = p.parseNumeric(devmajor) - hdr.Devminor = p.parseNumeric(devminor) - } - var prefix string - switch format { - case "posix", "gnu": - prefix = p.parseString(s.next(155)) - case "star": - prefix = p.parseString(s.next(131)) - hdr.AccessTime = time.Unix(p.parseNumeric(s.next(12)), 0) - hdr.ChangeTime = time.Unix(p.parseNumeric(s.next(12)), 0) - } - if len(prefix) > 0 { - hdr.Name = prefix + "/" + hdr.Name - } - } - - if p.err != nil { - tr.err = p.err - return nil - } - - nb := hdr.Size - if isHeaderOnlyType(hdr.Typeflag) { - nb = 0 - } - if nb < 0 { - tr.err = ErrHeader - return nil - } - - // Set the current file reader. - tr.pad = -nb & (blockSize - 1) // blockSize is a power of two - tr.curr = ®FileReader{r: tr.r, nb: nb} - - // Check for old GNU sparse format entry. - if hdr.Typeflag == TypeGNUSparse { - // Get the real size of the file. - hdr.Size = p.parseNumeric(header[483:495]) - if p.err != nil { - tr.err = p.err - return nil - } - - // Read the sparse map. - sp := tr.readOldGNUSparseMap(header) - if tr.err != nil { - return nil - } - - // Current file is a GNU sparse file. Update the current file reader. - tr.curr, tr.err = newSparseFileReader(tr.curr, sp, hdr.Size) - if tr.err != nil { - return nil - } - } - - return hdr -} - -// readOldGNUSparseMap reads the sparse map as stored in the old GNU sparse format. -// The sparse map is stored in the tar header if it's small enough. If it's larger than four entries, -// then one or more extension headers are used to store the rest of the sparse map. -func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry { - var p parser - isExtended := header[oldGNUSparseMainHeaderIsExtendedOffset] != 0 - spCap := oldGNUSparseMainHeaderNumEntries - if isExtended { - spCap += oldGNUSparseExtendedHeaderNumEntries - } - sp := make([]sparseEntry, 0, spCap) - s := slicer(header[oldGNUSparseMainHeaderOffset:]) - - // Read the four entries from the main tar header - for i := 0; i < oldGNUSparseMainHeaderNumEntries; i++ { - offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize)) - numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize)) - if p.err != nil { - tr.err = p.err - return nil - } - if offset == 0 && numBytes == 0 { - break - } - sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes}) - } - - for isExtended { - // There are more entries. Read an extension header and parse its entries. - sparseHeader := make([]byte, blockSize) - if _, tr.err = io.ReadFull(tr.r, sparseHeader); tr.err != nil { - return nil - } - isExtended = sparseHeader[oldGNUSparseExtendedHeaderIsExtendedOffset] != 0 - s = slicer(sparseHeader) - for i := 0; i < oldGNUSparseExtendedHeaderNumEntries; i++ { - offset := p.parseNumeric(s.next(oldGNUSparseOffsetSize)) - numBytes := p.parseNumeric(s.next(oldGNUSparseNumBytesSize)) - if p.err != nil { - tr.err = p.err - return nil - } - if offset == 0 && numBytes == 0 { - break - } - sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes}) - } - } - return sp -} - -// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format -// version 1.0. The format of the sparse map consists of a series of -// newline-terminated numeric fields. The first field is the number of entries -// and is always present. Following this are the entries, consisting of two -// fields (offset, numBytes). This function must stop reading at the end -// boundary of the block containing the last newline. -// -// Note that the GNU manual says that numeric values should be encoded in octal -// format. However, the GNU tar utility itself outputs these values in decimal. -// As such, this library treats values as being encoded in decimal. -func readGNUSparseMap1x0(r io.Reader) ([]sparseEntry, error) { - var cntNewline int64 - var buf bytes.Buffer - var blk = make([]byte, blockSize) - - // feedTokens copies data in numBlock chunks from r into buf until there are - // at least cnt newlines in buf. It will not read more blocks than needed. - var feedTokens = func(cnt int64) error { - for cntNewline < cnt { - if _, err := io.ReadFull(r, blk); err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - return err - } - buf.Write(blk) - for _, c := range blk { - if c == '\n' { - cntNewline++ - } - } - } - return nil - } - - // nextToken gets the next token delimited by a newline. This assumes that - // at least one newline exists in the buffer. - var nextToken = func() string { - cntNewline-- - tok, _ := buf.ReadString('\n') - return tok[:len(tok)-1] // Cut off newline - } - - // Parse for the number of entries. - // Use integer overflow resistant math to check this. - if err := feedTokens(1); err != nil { - return nil, err - } - numEntries, err := strconv.ParseInt(nextToken(), 10, 0) // Intentionally parse as native int - if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) { - return nil, ErrHeader - } - - // Parse for all member entries. - // numEntries is trusted after this since a potential attacker must have - // committed resources proportional to what this library used. - if err := feedTokens(2 * numEntries); err != nil { - return nil, err - } - sp := make([]sparseEntry, 0, numEntries) - for i := int64(0); i < numEntries; i++ { - offset, err := strconv.ParseInt(nextToken(), 10, 64) - if err != nil { - return nil, ErrHeader - } - numBytes, err := strconv.ParseInt(nextToken(), 10, 64) - if err != nil { - return nil, ErrHeader - } - sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes}) - } - return sp, nil -} - -// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format -// version 0.1. The sparse map is stored in the PAX headers. -func readGNUSparseMap0x1(extHdrs map[string]string) ([]sparseEntry, error) { - // Get number of entries. - // Use integer overflow resistant math to check this. - numEntriesStr := extHdrs[paxGNUSparseNumBlocks] - numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0) // Intentionally parse as native int - if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) { - return nil, ErrHeader - } - - // There should be two numbers in sparseMap for each entry. - sparseMap := strings.Split(extHdrs[paxGNUSparseMap], ",") - if int64(len(sparseMap)) != 2*numEntries { - return nil, ErrHeader - } - - // Loop through the entries in the sparse map. - // numEntries is trusted now. - sp := make([]sparseEntry, 0, numEntries) - for i := int64(0); i < numEntries; i++ { - offset, err := strconv.ParseInt(sparseMap[2*i], 10, 64) - if err != nil { - return nil, ErrHeader - } - numBytes, err := strconv.ParseInt(sparseMap[2*i+1], 10, 64) - if err != nil { - return nil, ErrHeader - } - sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes}) - } - return sp, nil -} - -// numBytes returns the number of bytes left to read in the current file's entry -// in the tar archive, or 0 if there is no current file. -func (tr *Reader) numBytes() int64 { - if tr.curr == nil { - // No current file, so no bytes - return 0 - } - return tr.curr.numBytes() -} - -// Read reads from the current entry in the tar archive. -// It returns 0, io.EOF when it reaches the end of that entry, -// until Next is called to advance to the next entry. -// -// Calling Read on special types like TypeLink, TypeSymLink, TypeChar, -// TypeBlock, TypeDir, and TypeFifo returns 0, io.EOF regardless of what -// the Header.Size claims. -func (tr *Reader) Read(b []byte) (n int, err error) { - if tr.err != nil { - return 0, tr.err - } - if tr.curr == nil { - return 0, io.EOF - } - - n, err = tr.curr.Read(b) - if err != nil && err != io.EOF { - tr.err = err - } - return -} - -func (rfr *regFileReader) Read(b []byte) (n int, err error) { - if rfr.nb == 0 { - // file consumed - return 0, io.EOF - } - if int64(len(b)) > rfr.nb { - b = b[0:rfr.nb] - } - n, err = rfr.r.Read(b) - rfr.nb -= int64(n) - - if err == io.EOF && rfr.nb > 0 { - err = io.ErrUnexpectedEOF - } - return -} - -// numBytes returns the number of bytes left to read in the file's data in the tar archive. -func (rfr *regFileReader) numBytes() int64 { - return rfr.nb -} - -// newSparseFileReader creates a new sparseFileReader, but validates all of the -// sparse entries before doing so. -func newSparseFileReader(rfr numBytesReader, sp []sparseEntry, total int64) (*sparseFileReader, error) { - if total < 0 { - return nil, ErrHeader // Total size cannot be negative - } - - // Validate all sparse entries. These are the same checks as performed by - // the BSD tar utility. - for i, s := range sp { - switch { - case s.offset < 0 || s.numBytes < 0: - return nil, ErrHeader // Negative values are never okay - case s.offset > math.MaxInt64-s.numBytes: - return nil, ErrHeader // Integer overflow with large length - case s.offset+s.numBytes > total: - return nil, ErrHeader // Region extends beyond the "real" size - case i > 0 && sp[i-1].offset+sp[i-1].numBytes > s.offset: - return nil, ErrHeader // Regions can't overlap and must be in order - } - } - return &sparseFileReader{rfr: rfr, sp: sp, total: total}, nil -} - -// readHole reads a sparse hole ending at endOffset. -func (sfr *sparseFileReader) readHole(b []byte, endOffset int64) int { - n64 := endOffset - sfr.pos - if n64 > int64(len(b)) { - n64 = int64(len(b)) - } - n := int(n64) - for i := 0; i < n; i++ { - b[i] = 0 - } - sfr.pos += n64 - return n -} - -// Read reads the sparse file data in expanded form. -func (sfr *sparseFileReader) Read(b []byte) (n int, err error) { - // Skip past all empty fragments. - for len(sfr.sp) > 0 && sfr.sp[0].numBytes == 0 { - sfr.sp = sfr.sp[1:] - } - - // If there are no more fragments, then it is possible that there - // is one last sparse hole. - if len(sfr.sp) == 0 { - // This behavior matches the BSD tar utility. - // However, GNU tar stops returning data even if sfr.total is unmet. - if sfr.pos < sfr.total { - return sfr.readHole(b, sfr.total), nil - } - return 0, io.EOF - } - - // In front of a data fragment, so read a hole. - if sfr.pos < sfr.sp[0].offset { - return sfr.readHole(b, sfr.sp[0].offset), nil - } - - // In a data fragment, so read from it. - // This math is overflow free since we verify that offset and numBytes can - // be safely added when creating the sparseFileReader. - endPos := sfr.sp[0].offset + sfr.sp[0].numBytes // End offset of fragment - bytesLeft := endPos - sfr.pos // Bytes left in fragment - if int64(len(b)) > bytesLeft { - b = b[:bytesLeft] - } - - n, err = sfr.rfr.Read(b) - sfr.pos += int64(n) - if err == io.EOF { - if sfr.pos < endPos { - err = io.ErrUnexpectedEOF // There was supposed to be more data - } else if sfr.pos < sfr.total { - err = nil // There is still an implicit sparse hole at the end - } - } - - if sfr.pos == endPos { - sfr.sp = sfr.sp[1:] // We are done with this fragment, so pop it - } - return n, err -} - -// numBytes returns the number of bytes left to read in the sparse file's -// sparse-encoded data in the tar archive. -func (sfr *sparseFileReader) numBytes() int64 { - return sfr.rfr.numBytes() -} diff --git a/vendor/github.com/Microsoft/go-winio/archive/tar/stat_atim.go b/vendor/github.com/Microsoft/go-winio/archive/tar/stat_atim.go deleted file mode 100644 index cf9cc79c5915b..0000000000000 --- a/vendor/github.com/Microsoft/go-winio/archive/tar/stat_atim.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux dragonfly openbsd solaris - -package tar - -import ( - "syscall" - "time" -) - -func statAtime(st *syscall.Stat_t) time.Time { - return time.Unix(st.Atim.Unix()) -} - -func statCtime(st *syscall.Stat_t) time.Time { - return time.Unix(st.Ctim.Unix()) -} diff --git a/vendor/github.com/Microsoft/go-winio/archive/tar/stat_atimespec.go b/vendor/github.com/Microsoft/go-winio/archive/tar/stat_atimespec.go deleted file mode 100644 index 6f17dbe30725c..0000000000000 --- a/vendor/github.com/Microsoft/go-winio/archive/tar/stat_atimespec.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd netbsd - -package tar - -import ( - "syscall" - "time" -) - -func statAtime(st *syscall.Stat_t) time.Time { - return time.Unix(st.Atimespec.Unix()) -} - -func statCtime(st *syscall.Stat_t) time.Time { - return time.Unix(st.Ctimespec.Unix()) -} diff --git a/vendor/github.com/Microsoft/go-winio/archive/tar/stat_unix.go b/vendor/github.com/Microsoft/go-winio/archive/tar/stat_unix.go deleted file mode 100644 index cb843db4cfd65..0000000000000 --- a/vendor/github.com/Microsoft/go-winio/archive/tar/stat_unix.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build linux darwin dragonfly freebsd openbsd netbsd solaris - -package tar - -import ( - "os" - "syscall" -) - -func init() { - sysStat = statUnix -} - -func statUnix(fi os.FileInfo, h *Header) error { - sys, ok := fi.Sys().(*syscall.Stat_t) - if !ok { - return nil - } - h.Uid = int(sys.Uid) - h.Gid = int(sys.Gid) - // TODO(bradfitz): populate username & group. os/user - // doesn't cache LookupId lookups, and lacks group - // lookup functions. - h.AccessTime = statAtime(sys) - h.ChangeTime = statCtime(sys) - // TODO(bradfitz): major/minor device numbers? - return nil -} diff --git a/vendor/github.com/Microsoft/go-winio/archive/tar/writer.go b/vendor/github.com/Microsoft/go-winio/archive/tar/writer.go deleted file mode 100644 index 30d7e606d6baf..0000000000000 --- a/vendor/github.com/Microsoft/go-winio/archive/tar/writer.go +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package tar - -// TODO(dsymonds): -// - catch more errors (no first header, etc.) - -import ( - "bytes" - "errors" - "fmt" - "io" - "path" - "sort" - "strconv" - "strings" - "time" -) - -var ( - ErrWriteTooLong = errors.New("archive/tar: write too long") - ErrFieldTooLong = errors.New("archive/tar: header field too long") - ErrWriteAfterClose = errors.New("archive/tar: write after close") - errInvalidHeader = errors.New("archive/tar: header field too long or contains invalid values") -) - -// A Writer provides sequential writing of a tar archive in POSIX.1 format. -// A tar archive consists of a sequence of files. -// Call WriteHeader to begin a new file, and then call Write to supply that file's data, -// writing at most hdr.Size bytes in total. -type Writer struct { - w io.Writer - err error - nb int64 // number of unwritten bytes for current file entry - pad int64 // amount of padding to write after current file entry - closed bool - usedBinary bool // whether the binary numeric field extension was used - preferPax bool // use pax header instead of binary numeric header - hdrBuff [blockSize]byte // buffer to use in writeHeader when writing a regular header - paxHdrBuff [blockSize]byte // buffer to use in writeHeader when writing a pax header -} - -type formatter struct { - err error // Last error seen -} - -// NewWriter creates a new Writer writing to w. -func NewWriter(w io.Writer) *Writer { return &Writer{w: w, preferPax: true} } - -// Flush finishes writing the current file (optional). -func (tw *Writer) Flush() error { - if tw.nb > 0 { - tw.err = fmt.Errorf("archive/tar: missed writing %d bytes", tw.nb) - return tw.err - } - - n := tw.nb + tw.pad - for n > 0 && tw.err == nil { - nr := n - if nr > blockSize { - nr = blockSize - } - var nw int - nw, tw.err = tw.w.Write(zeroBlock[0:nr]) - n -= int64(nw) - } - tw.nb = 0 - tw.pad = 0 - return tw.err -} - -// Write s into b, terminating it with a NUL if there is room. -func (f *formatter) formatString(b []byte, s string) { - if len(s) > len(b) { - f.err = ErrFieldTooLong - return - } - ascii := toASCII(s) - copy(b, ascii) - if len(ascii) < len(b) { - b[len(ascii)] = 0 - } -} - -// Encode x as an octal ASCII string and write it into b with leading zeros. -func (f *formatter) formatOctal(b []byte, x int64) { - s := strconv.FormatInt(x, 8) - // leading zeros, but leave room for a NUL. - for len(s)+1 < len(b) { - s = "0" + s - } - f.formatString(b, s) -} - -// fitsInBase256 reports whether x can be encoded into n bytes using base-256 -// encoding. Unlike octal encoding, base-256 encoding does not require that the -// string ends with a NUL character. Thus, all n bytes are available for output. -// -// If operating in binary mode, this assumes strict GNU binary mode; which means -// that the first byte can only be either 0x80 or 0xff. Thus, the first byte is -// equivalent to the sign bit in two's complement form. -func fitsInBase256(n int, x int64) bool { - var binBits = uint(n-1) * 8 - return n >= 9 || (x >= -1<= 0; i-- { - b[i] = byte(x) - x >>= 8 - } - b[0] |= 0x80 // Highest bit indicates binary format - return - } - - f.formatOctal(b, 0) // Last resort, just write zero - f.err = ErrFieldTooLong -} - -var ( - minTime = time.Unix(0, 0) - // There is room for 11 octal digits (33 bits) of mtime. - maxTime = minTime.Add((1<<33 - 1) * time.Second) -) - -// WriteHeader writes hdr and prepares to accept the file's contents. -// WriteHeader calls Flush if it is not the first header. -// Calling after a Close will return ErrWriteAfterClose. -func (tw *Writer) WriteHeader(hdr *Header) error { - return tw.writeHeader(hdr, true) -} - -// WriteHeader writes hdr and prepares to accept the file's contents. -// WriteHeader calls Flush if it is not the first header. -// Calling after a Close will return ErrWriteAfterClose. -// As this method is called internally by writePax header to allow it to -// suppress writing the pax header. -func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { - if tw.closed { - return ErrWriteAfterClose - } - if tw.err == nil { - tw.Flush() - } - if tw.err != nil { - return tw.err - } - - // a map to hold pax header records, if any are needed - paxHeaders := make(map[string]string) - - // TODO(shanemhansen): we might want to use PAX headers for - // subsecond time resolution, but for now let's just capture - // too long fields or non ascii characters - - var f formatter - var header []byte - - // We need to select which scratch buffer to use carefully, - // since this method is called recursively to write PAX headers. - // If allowPax is true, this is the non-recursive call, and we will use hdrBuff. - // If allowPax is false, we are being called by writePAXHeader, and hdrBuff is - // already being used by the non-recursive call, so we must use paxHdrBuff. - header = tw.hdrBuff[:] - if !allowPax { - header = tw.paxHdrBuff[:] - } - copy(header, zeroBlock) - s := slicer(header) - - // Wrappers around formatter that automatically sets paxHeaders if the - // argument extends beyond the capacity of the input byte slice. - var formatString = func(b []byte, s string, paxKeyword string) { - needsPaxHeader := paxKeyword != paxNone && len(s) > len(b) || !isASCII(s) - if needsPaxHeader { - paxHeaders[paxKeyword] = s - return - } - f.formatString(b, s) - } - var formatNumeric = func(b []byte, x int64, paxKeyword string) { - // Try octal first. - s := strconv.FormatInt(x, 8) - if len(s) < len(b) { - f.formatOctal(b, x) - return - } - - // If it is too long for octal, and PAX is preferred, use a PAX header. - if paxKeyword != paxNone && tw.preferPax { - f.formatOctal(b, 0) - s := strconv.FormatInt(x, 10) - paxHeaders[paxKeyword] = s - return - } - - tw.usedBinary = true - f.formatNumeric(b, x) - } - var formatTime = func(b []byte, t time.Time, paxKeyword string) { - var unixTime int64 - if !t.Before(minTime) && !t.After(maxTime) { - unixTime = t.Unix() - } - formatNumeric(b, unixTime, paxNone) - - // Write a PAX header if the time didn't fit precisely. - if paxKeyword != "" && tw.preferPax && allowPax && (t.Nanosecond() != 0 || !t.Before(minTime) || !t.After(maxTime)) { - paxHeaders[paxKeyword] = formatPAXTime(t) - } - } - - // keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax - pathHeaderBytes := s.next(fileNameSize) - - formatString(pathHeaderBytes, hdr.Name, paxPath) - - f.formatOctal(s.next(8), hdr.Mode) // 100:108 - formatNumeric(s.next(8), int64(hdr.Uid), paxUid) // 108:116 - formatNumeric(s.next(8), int64(hdr.Gid), paxGid) // 116:124 - formatNumeric(s.next(12), hdr.Size, paxSize) // 124:136 - formatTime(s.next(12), hdr.ModTime, paxMtime) // 136:148 - s.next(8) // chksum (148:156) - s.next(1)[0] = hdr.Typeflag // 156:157 - - formatString(s.next(100), hdr.Linkname, paxLinkpath) - - copy(s.next(8), []byte("ustar\x0000")) // 257:265 - formatString(s.next(32), hdr.Uname, paxUname) // 265:297 - formatString(s.next(32), hdr.Gname, paxGname) // 297:329 - formatNumeric(s.next(8), hdr.Devmajor, paxNone) // 329:337 - formatNumeric(s.next(8), hdr.Devminor, paxNone) // 337:345 - - // keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax - prefixHeaderBytes := s.next(155) - formatString(prefixHeaderBytes, "", paxNone) // 345:500 prefix - - // Use the GNU magic instead of POSIX magic if we used any GNU extensions. - if tw.usedBinary { - copy(header[257:265], []byte("ustar \x00")) - } - - _, paxPathUsed := paxHeaders[paxPath] - // try to use a ustar header when only the name is too long - if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed { - prefix, suffix, ok := splitUSTARPath(hdr.Name) - if ok { - // Since we can encode in USTAR format, disable PAX header. - delete(paxHeaders, paxPath) - - // Update the path fields - formatString(pathHeaderBytes, suffix, paxNone) - formatString(prefixHeaderBytes, prefix, paxNone) - } - } - - // The chksum field is terminated by a NUL and a space. - // This is different from the other octal fields. - chksum, _ := checksum(header) - f.formatOctal(header[148:155], chksum) // Never fails - header[155] = ' ' - - // Check if there were any formatting errors. - if f.err != nil { - tw.err = f.err - return tw.err - } - - if allowPax { - if !hdr.AccessTime.IsZero() { - paxHeaders[paxAtime] = formatPAXTime(hdr.AccessTime) - } - if !hdr.ChangeTime.IsZero() { - paxHeaders[paxCtime] = formatPAXTime(hdr.ChangeTime) - } - if !hdr.CreationTime.IsZero() { - paxHeaders[paxCreationTime] = formatPAXTime(hdr.CreationTime) - } - for k, v := range hdr.Xattrs { - paxHeaders[paxXattr+k] = v - } - for k, v := range hdr.Winheaders { - paxHeaders[paxWindows+k] = v - } - } - - if len(paxHeaders) > 0 { - if !allowPax { - return errInvalidHeader - } - if err := tw.writePAXHeader(hdr, paxHeaders); err != nil { - return err - } - } - tw.nb = int64(hdr.Size) - tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize - - _, tw.err = tw.w.Write(header) - return tw.err -} - -func formatPAXTime(t time.Time) string { - sec := t.Unix() - usec := t.Nanosecond() - s := strconv.FormatInt(sec, 10) - if usec != 0 { - s = fmt.Sprintf("%s.%09d", s, usec) - } - return s -} - -// splitUSTARPath splits a path according to USTAR prefix and suffix rules. -// If the path is not splittable, then it will return ("", "", false). -func splitUSTARPath(name string) (prefix, suffix string, ok bool) { - length := len(name) - if length <= fileNameSize || !isASCII(name) { - return "", "", false - } else if length > fileNamePrefixSize+1 { - length = fileNamePrefixSize + 1 - } else if name[length-1] == '/' { - length-- - } - - i := strings.LastIndex(name[:length], "/") - nlen := len(name) - i - 1 // nlen is length of suffix - plen := i // plen is length of prefix - if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize { - return "", "", false - } - return name[:i], name[i+1:], true -} - -// writePaxHeader writes an extended pax header to the -// archive. -func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error { - // Prepare extended header - ext := new(Header) - ext.Typeflag = TypeXHeader - // Setting ModTime is required for reader parsing to - // succeed, and seems harmless enough. - ext.ModTime = hdr.ModTime - // The spec asks that we namespace our pseudo files - // with the current pid. However, this results in differing outputs - // for identical inputs. As such, the constant 0 is now used instead. - // golang.org/issue/12358 - dir, file := path.Split(hdr.Name) - fullName := path.Join(dir, "PaxHeaders.0", file) - - ascii := toASCII(fullName) - if len(ascii) > 100 { - ascii = ascii[:100] - } - ext.Name = ascii - // Construct the body - var buf bytes.Buffer - - // Keys are sorted before writing to body to allow deterministic output. - var keys []string - for k := range paxHeaders { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - fmt.Fprint(&buf, formatPAXRecord(k, paxHeaders[k])) - } - - ext.Size = int64(len(buf.Bytes())) - if err := tw.writeHeader(ext, false); err != nil { - return err - } - if _, err := tw.Write(buf.Bytes()); err != nil { - return err - } - if err := tw.Flush(); err != nil { - return err - } - return nil -} - -// formatPAXRecord formats a single PAX record, prefixing it with the -// appropriate length. -func formatPAXRecord(k, v string) string { - const padding = 3 // Extra padding for ' ', '=', and '\n' - size := len(k) + len(v) + padding - size += len(strconv.Itoa(size)) - record := fmt.Sprintf("%d %s=%s\n", size, k, v) - - // Final adjustment if adding size field increased the record size. - if len(record) != size { - size = len(record) - record = fmt.Sprintf("%d %s=%s\n", size, k, v) - } - return record -} - -// Write writes to the current entry in the tar archive. -// Write returns the error ErrWriteTooLong if more than -// hdr.Size bytes are written after WriteHeader. -func (tw *Writer) Write(b []byte) (n int, err error) { - if tw.closed { - err = ErrWriteAfterClose - return - } - overwrite := false - if int64(len(b)) > tw.nb { - b = b[0:tw.nb] - overwrite = true - } - n, err = tw.w.Write(b) - tw.nb -= int64(n) - if err == nil && overwrite { - err = ErrWriteTooLong - return - } - tw.err = err - return -} - -// Close closes the tar archive, flushing any unwritten -// data to the underlying writer. -func (tw *Writer) Close() error { - if tw.err != nil || tw.closed { - return tw.err - } - tw.Flush() - tw.closed = true - if tw.err != nil { - return tw.err - } - - // trailer: two zero blocks - for i := 0; i < 2; i++ { - _, tw.err = tw.w.Write(zeroBlock) - if tw.err != nil { - break - } - } - return tw.err -} diff --git a/vendor/github.com/Microsoft/go-winio/backuptar/strconv.go b/vendor/github.com/Microsoft/go-winio/backuptar/strconv.go new file mode 100644 index 0000000000000..3416096639918 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/backuptar/strconv.go @@ -0,0 +1,68 @@ +package backuptar + +import ( + "archive/tar" + "fmt" + "strconv" + "strings" + "time" +) + +// Functions copied from https://github.com/golang/go/blob/master/src/archive/tar/strconv.go +// as we need to manage the LIBARCHIVE.creationtime PAXRecord manually. +// Idea taken from containerd which did the same thing. + +// parsePAXTime takes a string of the form %d.%d as described in the PAX +// specification. Note that this implementation allows for negative timestamps, +// which is allowed for by the PAX specification, but not always portable. +func parsePAXTime(s string) (time.Time, error) { + const maxNanoSecondDigits = 9 + + // Split string into seconds and sub-seconds parts. + ss, sn := s, "" + if pos := strings.IndexByte(s, '.'); pos >= 0 { + ss, sn = s[:pos], s[pos+1:] + } + + // Parse the seconds. + secs, err := strconv.ParseInt(ss, 10, 64) + if err != nil { + return time.Time{}, tar.ErrHeader + } + if len(sn) == 0 { + return time.Unix(secs, 0), nil // No sub-second values + } + + // Parse the nanoseconds. + if strings.Trim(sn, "0123456789") != "" { + return time.Time{}, tar.ErrHeader + } + if len(sn) < maxNanoSecondDigits { + sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad + } else { + sn = sn[:maxNanoSecondDigits] // Right truncate + } + nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed + if len(ss) > 0 && ss[0] == '-' { + return time.Unix(secs, -1*nsecs), nil // Negative correction + } + return time.Unix(secs, nsecs), nil +} + +// formatPAXTime converts ts into a time of the form %d.%d as described in the +// PAX specification. This function is capable of negative timestamps. +func formatPAXTime(ts time.Time) (s string) { + secs, nsecs := ts.Unix(), ts.Nanosecond() + if nsecs == 0 { + return strconv.FormatInt(secs, 10) + } + + // If seconds is negative, then perform correction. + sign := "" + if secs < 0 { + sign = "-" // Remember sign + secs = -(secs + 1) // Add a second to secs + nsecs = -(nsecs - 1e9) // Take that second away from nsecs + } + return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0") +} diff --git a/vendor/github.com/Microsoft/go-winio/backuptar/tar.go b/vendor/github.com/Microsoft/go-winio/backuptar/tar.go index d6566dbf0c6cf..689e4da6bdacc 100644 --- a/vendor/github.com/Microsoft/go-winio/backuptar/tar.go +++ b/vendor/github.com/Microsoft/go-winio/backuptar/tar.go @@ -3,8 +3,8 @@ package backuptar import ( + "archive/tar" "encoding/base64" - "errors" "fmt" "io" "io/ioutil" @@ -15,7 +15,7 @@ import ( "time" "github.com/Microsoft/go-winio" - "github.com/Microsoft/go-winio/archive/tar" // until archive/tar supports pax extensions in its interface + "golang.org/x/sys/windows" ) const ( @@ -32,26 +32,23 @@ const ( ) const ( - hdrFileAttributes = "fileattr" - hdrSecurityDescriptor = "sd" - hdrRawSecurityDescriptor = "rawsd" - hdrMountPoint = "mountpoint" - hdrEaPrefix = "xattr." + hdrFileAttributes = "MSWINDOWS.fileattr" + hdrSecurityDescriptor = "MSWINDOWS.sd" + hdrRawSecurityDescriptor = "MSWINDOWS.rawsd" + hdrMountPoint = "MSWINDOWS.mountpoint" + hdrEaPrefix = "MSWINDOWS.xattr." + + hdrCreationTime = "LIBARCHIVE.creationtime" ) -func writeZeroes(w io.Writer, count int64) error { - buf := make([]byte, 8192) - c := len(buf) - for i := int64(0); i < count; i += int64(c) { - if int64(c) > count-i { - c = int(count - i) - } - _, err := w.Write(buf[:c]) - if err != nil { - return err - } +// zeroReader is an io.Reader that always returns 0s. +type zeroReader struct{} + +func (zr zeroReader) Read(b []byte) (int, error) { + for i := range b { + b[i] = 0 } - return nil + return len(b), nil } func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error { @@ -68,16 +65,26 @@ func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error { return fmt.Errorf("unexpected stream %d", bhdr.Id) } + // We can't seek backwards, since we have already written that data to the tar.Writer. + if bhdr.Offset < curOffset { + return fmt.Errorf("cannot seek back from %d to %d", curOffset, bhdr.Offset) + } // archive/tar does not support writing sparse files // so just write zeroes to catch up to the current offset. - err = writeZeroes(t, bhdr.Offset-curOffset) + if _, err := io.CopyN(t, zeroReader{}, bhdr.Offset-curOffset); err != nil { + return fmt.Errorf("seek to offset %d: %s", bhdr.Offset, err) + } if bhdr.Size == 0 { + // A sparse block with size = 0 is used to mark the end of the sparse blocks. break } n, err := io.Copy(t, br) if err != nil { return err } + if n != bhdr.Size { + return fmt.Errorf("copied %d bytes instead of %d at offset %d", n, bhdr.Size, bhdr.Offset) + } curOffset = bhdr.Offset + n } return nil @@ -86,16 +93,17 @@ func copySparse(t *tar.Writer, br *winio.BackupStreamReader) error { // BasicInfoHeader creates a tar header from basic file information. func BasicInfoHeader(name string, size int64, fileInfo *winio.FileBasicInfo) *tar.Header { hdr := &tar.Header{ - Name: filepath.ToSlash(name), - Size: size, - Typeflag: tar.TypeReg, - ModTime: time.Unix(0, fileInfo.LastWriteTime.Nanoseconds()), - ChangeTime: time.Unix(0, fileInfo.ChangeTime.Nanoseconds()), - AccessTime: time.Unix(0, fileInfo.LastAccessTime.Nanoseconds()), - CreationTime: time.Unix(0, fileInfo.CreationTime.Nanoseconds()), - Winheaders: make(map[string]string), + Format: tar.FormatPAX, + Name: filepath.ToSlash(name), + Size: size, + Typeflag: tar.TypeReg, + ModTime: time.Unix(0, fileInfo.LastWriteTime.Nanoseconds()), + ChangeTime: time.Unix(0, fileInfo.ChangeTime.Nanoseconds()), + AccessTime: time.Unix(0, fileInfo.LastAccessTime.Nanoseconds()), + PAXRecords: make(map[string]string), } - hdr.Winheaders[hdrFileAttributes] = fmt.Sprintf("%d", fileInfo.FileAttributes) + hdr.PAXRecords[hdrFileAttributes] = fmt.Sprintf("%d", fileInfo.FileAttributes) + hdr.PAXRecords[hdrCreationTime] = formatPAXTime(time.Unix(0, fileInfo.CreationTime.Nanoseconds())) if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { hdr.Mode |= c_ISDIR @@ -155,7 +163,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size if err != nil { return err } - hdr.Winheaders[hdrRawSecurityDescriptor] = base64.StdEncoding.EncodeToString(sd) + hdr.PAXRecords[hdrRawSecurityDescriptor] = base64.StdEncoding.EncodeToString(sd) case winio.BackupReparseData: hdr.Mode |= c_ISLNK @@ -166,7 +174,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size return err } if rp.IsMountPoint { - hdr.Winheaders[hdrMountPoint] = "1" + hdr.PAXRecords[hdrMountPoint] = "1" } hdr.Linkname = rp.Target @@ -183,7 +191,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size // Use base64 encoding for the binary value. Note that there // is no way to encode the EA's flags, since their use doesn't // make any sense for persisted EAs. - hdr.Winheaders[hdrEaPrefix+ea.Name] = base64.StdEncoding.EncodeToString(ea.Value) + hdr.PAXRecords[hdrEaPrefix+ea.Name] = base64.StdEncoding.EncodeToString(ea.Value) } case winio.BackupAlternateData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData: @@ -217,20 +225,44 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size } } + // The logic for copying file contents is fairly complicated due to the need for handling sparse files, + // and the weird ways they are represented by BackupRead. A normal file will always either have a data stream + // with size and content, or no data stream at all (if empty). However, for a sparse file, the content can also + // be represented using a series of sparse block streams following the data stream. Additionally, the way sparse + // files are handled by BackupRead has changed in the OS recently. The specifics of the representation are described + // in the list at the bottom of this block comment. + // + // Sparse files can be represented in four different ways, based on the specifics of the file. + // - Size = 0: + // Previously: BackupRead yields no data stream and no sparse block streams. + // Recently: BackupRead yields a data stream with size = 0. There are no following sparse block streams. + // - Size > 0, no allocated ranges: + // BackupRead yields a data stream with size = 0. Following is a single sparse block stream with + // size = 0 and offset = . + // - Size > 0, one allocated range: + // BackupRead yields a data stream with size = containing the file contents. There are no + // sparse block streams. This is the case if you take a normal file with contents and simply set the + // sparse flag on it. + // - Size > 0, multiple allocated ranges: + // BackupRead yields a data stream with size = 0. Following are sparse block streams for each allocated + // range of the file containing the range contents. Finally there is a sparse block stream with + // size = 0 and offset = . + if dataHdr != nil { // A data stream was found. Copy the data. - if (dataHdr.Attributes & winio.StreamSparseAttributes) == 0 { + // We assume that we will either have a data stream size > 0 XOR have sparse block streams. + if dataHdr.Size > 0 || (dataHdr.Attributes&winio.StreamSparseAttributes) == 0 { if size != dataHdr.Size { return fmt.Errorf("%s: mismatch between file size %d and header size %d", name, size, dataHdr.Size) } - _, err = io.Copy(t, br) - if err != nil { - return err + if _, err = io.Copy(t, br); err != nil { + return fmt.Errorf("%s: copying contents from data stream: %s", name, err) } - } else { - err = copySparse(t, br) - if err != nil { - return err + } else if size > 0 { + // As of a recent OS change, BackupRead now returns a data stream for empty sparse files. + // These files have no sparse block streams, so skip the copySparse call if file size = 0. + if err = copySparse(t, br); err != nil { + return fmt.Errorf("%s: copying contents from sparse block stream: %s", name, err) } } } @@ -254,6 +286,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size } if (bhdr.Attributes & winio.StreamSparseAttributes) == 0 { hdr = &tar.Header{ + Format: hdr.Format, Name: name + altName, Mode: hdr.Mode, Typeflag: tar.TypeReg, @@ -274,7 +307,7 @@ func WriteTarFileFromBackupStream(t *tar.Writer, r io.Reader, name string, size } else { // Unsupported for now, since the size of the alternate stream is not present // in the backup stream until after the data has been read. - return errors.New("tar of sparse alternate data streams is unsupported") + return fmt.Errorf("%s: tar of sparse alternate data streams is unsupported", name) } case winio.BackupEaData, winio.BackupLink, winio.BackupPropertyData, winio.BackupObjectId, winio.BackupTxfsData: // ignore these streams @@ -293,12 +326,13 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win size = hdr.Size } fileInfo = &winio.FileBasicInfo{ - LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()), - LastWriteTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()), - ChangeTime: syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()), - CreationTime: syscall.NsecToFiletime(hdr.CreationTime.UnixNano()), + LastAccessTime: windows.NsecToFiletime(hdr.AccessTime.UnixNano()), + LastWriteTime: windows.NsecToFiletime(hdr.ModTime.UnixNano()), + ChangeTime: windows.NsecToFiletime(hdr.ChangeTime.UnixNano()), + // Default to ModTime, we'll pull hdrCreationTime below if present + CreationTime: windows.NsecToFiletime(hdr.ModTime.UnixNano()), } - if attrStr, ok := hdr.Winheaders[hdrFileAttributes]; ok { + if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok { attr, err := strconv.ParseUint(attrStr, 10, 32) if err != nil { return "", 0, nil, err @@ -309,6 +343,13 @@ func FileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY } } + if creationTimeStr, ok := hdr.PAXRecords[hdrCreationTime]; ok { + creationTime, err := parsePAXTime(creationTimeStr) + if err != nil { + return "", 0, nil, err + } + fileInfo.CreationTime = windows.NsecToFiletime(creationTime.UnixNano()) + } return } @@ -321,13 +362,13 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) ( var err error // Maintaining old SDDL-based behavior for backward compatibility. All new tar headers written // by this library will have raw binary for the security descriptor. - if sddl, ok := hdr.Winheaders[hdrSecurityDescriptor]; ok { + if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok { sd, err = winio.SddlToSecurityDescriptor(sddl) if err != nil { return nil, err } } - if sdraw, ok := hdr.Winheaders[hdrRawSecurityDescriptor]; ok { + if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok { sd, err = base64.StdEncoding.DecodeString(sdraw) if err != nil { return nil, err @@ -348,7 +389,7 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) ( } } var eas []winio.ExtendedAttribute - for k, v := range hdr.Winheaders { + for k, v := range hdr.PAXRecords { if !strings.HasPrefix(k, hdrEaPrefix) { continue } @@ -380,7 +421,7 @@ func WriteBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) ( } } if hdr.Typeflag == tar.TypeSymlink { - _, isMountPoint := hdr.Winheaders[hdrMountPoint] + _, isMountPoint := hdr.PAXRecords[hdrMountPoint] rp := winio.ReparsePoint{ Target: filepath.FromSlash(hdr.Linkname), IsMountPoint: isMountPoint, diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go index 4334ff1cbeeb1..0385e4108129b 100644 --- a/vendor/github.com/Microsoft/go-winio/file.go +++ b/vendor/github.com/Microsoft/go-winio/file.go @@ -16,6 +16,7 @@ import ( //sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort //sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus //sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes +//sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult type atomicBool int32 @@ -79,6 +80,7 @@ type win32File struct { wg sync.WaitGroup wgLock sync.RWMutex closing atomicBool + socket bool readDeadline deadlineHandler writeDeadline deadlineHandler } @@ -109,7 +111,13 @@ func makeWin32File(h syscall.Handle) (*win32File, error) { } func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { - return makeWin32File(h) + // If we return the result of makeWin32File directly, it can result in an + // interface-wrapped nil, rather than a nil interface value. + f, err := makeWin32File(h) + if err != nil { + return nil, err + } + return f, nil } // closeHandle closes the resources associated with a Win32 handle @@ -190,6 +198,10 @@ func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, er if f.closing.isSet() { err = ErrFileClosed } + } else if err != nil && f.socket { + // err is from Win32. Query the overlapped structure to get the winsock error. + var bytes, flags uint32 + err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags) } case <-timeout: cancelIoEx(f.handle, &c.o) @@ -265,6 +277,10 @@ func (f *win32File) Flush() error { return syscall.FlushFileBuffers(f.handle) } +func (f *win32File) Fd() uintptr { + return uintptr(f.handle) +} + func (d *deadlineHandler) set(deadline time.Time) error { d.setLock.Lock() defer d.setLock.Unlock() diff --git a/vendor/github.com/Microsoft/go-winio/fileinfo.go b/vendor/github.com/Microsoft/go-winio/fileinfo.go index ada2fbab63288..3ab6bff69c5d6 100644 --- a/vendor/github.com/Microsoft/go-winio/fileinfo.go +++ b/vendor/github.com/Microsoft/go-winio/fileinfo.go @@ -5,21 +5,14 @@ package winio import ( "os" "runtime" - "syscall" "unsafe" -) - -//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx -//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle -const ( - fileBasicInfo = 0 - fileIDInfo = 0x12 + "golang.org/x/sys/windows" ) // FileBasicInfo contains file access time and file attributes information. type FileBasicInfo struct { - CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime + CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime FileAttributes uint32 pad uint32 // padding } @@ -27,7 +20,7 @@ type FileBasicInfo struct { // GetFileBasicInfo retrieves times and attributes for a file. func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { bi := &FileBasicInfo{} - if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { + if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) @@ -36,13 +29,32 @@ func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { // SetFileBasicInfo sets times and attributes for a file. func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { - if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { + if err := windows.SetFileInformationByHandle(windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil { return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} } runtime.KeepAlive(f) return nil } +// FileStandardInfo contains extended information for the file. +// FILE_STANDARD_INFO in WinBase.h +// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info +type FileStandardInfo struct { + AllocationSize, EndOfFile int64 + NumberOfLinks uint32 + DeletePending, Directory bool +} + +// GetFileStandardInfo retrieves ended information for the file. +func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) { + si := &FileStandardInfo{} + if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil { + return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} + } + runtime.KeepAlive(f) + return si, nil +} + // FileIDInfo contains the volume serial number and file ID for a file. This pair should be // unique on a system. type FileIDInfo struct { @@ -53,7 +65,7 @@ type FileIDInfo struct { // GetFileID retrieves the unique (volume, file ID) pair for a file. func GetFileID(f *os.File) (*FileIDInfo, error) { fileID := &FileIDInfo{} - if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { + if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) diff --git a/vendor/github.com/Microsoft/go-winio/go.mod b/vendor/github.com/Microsoft/go-winio/go.mod new file mode 100644 index 0000000000000..98a8dea0e7e1d --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/go.mod @@ -0,0 +1,9 @@ +module github.com/Microsoft/go-winio + +go 1.12 + +require ( + github.com/pkg/errors v0.9.1 + github.com/sirupsen/logrus v1.7.0 + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c +) diff --git a/vendor/github.com/Microsoft/go-winio/hvsock.go b/vendor/github.com/Microsoft/go-winio/hvsock.go new file mode 100644 index 0000000000000..b632f8f8bb98b --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/hvsock.go @@ -0,0 +1,307 @@ +// +build windows + +package winio + +import ( + "fmt" + "io" + "net" + "os" + "syscall" + "time" + "unsafe" + + "github.com/Microsoft/go-winio/pkg/guid" +) + +//sys bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind + +const ( + afHvSock = 34 // AF_HYPERV + + socketError = ^uintptr(0) +) + +// An HvsockAddr is an address for a AF_HYPERV socket. +type HvsockAddr struct { + VMID guid.GUID + ServiceID guid.GUID +} + +type rawHvsockAddr struct { + Family uint16 + _ uint16 + VMID guid.GUID + ServiceID guid.GUID +} + +// Network returns the address's network name, "hvsock". +func (addr *HvsockAddr) Network() string { + return "hvsock" +} + +func (addr *HvsockAddr) String() string { + return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID) +} + +// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port. +func VsockServiceID(port uint32) guid.GUID { + g, _ := guid.FromString("00000000-facb-11e6-bd58-64006a7986d3") + g.Data1 = port + return g +} + +func (addr *HvsockAddr) raw() rawHvsockAddr { + return rawHvsockAddr{ + Family: afHvSock, + VMID: addr.VMID, + ServiceID: addr.ServiceID, + } +} + +func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) { + addr.VMID = raw.VMID + addr.ServiceID = raw.ServiceID +} + +// HvsockListener is a socket listener for the AF_HYPERV address family. +type HvsockListener struct { + sock *win32File + addr HvsockAddr +} + +// HvsockConn is a connected socket of the AF_HYPERV address family. +type HvsockConn struct { + sock *win32File + local, remote HvsockAddr +} + +func newHvSocket() (*win32File, error) { + fd, err := syscall.Socket(afHvSock, syscall.SOCK_STREAM, 1) + if err != nil { + return nil, os.NewSyscallError("socket", err) + } + f, err := makeWin32File(fd) + if err != nil { + syscall.Close(fd) + return nil, err + } + f.socket = true + return f, nil +} + +// ListenHvsock listens for connections on the specified hvsock address. +func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) { + l := &HvsockListener{addr: *addr} + sock, err := newHvSocket() + if err != nil { + return nil, l.opErr("listen", err) + } + sa := addr.raw() + err = bind(sock.handle, unsafe.Pointer(&sa), int32(unsafe.Sizeof(sa))) + if err != nil { + return nil, l.opErr("listen", os.NewSyscallError("socket", err)) + } + err = syscall.Listen(sock.handle, 16) + if err != nil { + return nil, l.opErr("listen", os.NewSyscallError("listen", err)) + } + return &HvsockListener{sock: sock, addr: *addr}, nil +} + +func (l *HvsockListener) opErr(op string, err error) error { + return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err} +} + +// Addr returns the listener's network address. +func (l *HvsockListener) Addr() net.Addr { + return &l.addr +} + +// Accept waits for the next connection and returns it. +func (l *HvsockListener) Accept() (_ net.Conn, err error) { + sock, err := newHvSocket() + if err != nil { + return nil, l.opErr("accept", err) + } + defer func() { + if sock != nil { + sock.Close() + } + }() + c, err := l.sock.prepareIo() + if err != nil { + return nil, l.opErr("accept", err) + } + defer l.sock.wg.Done() + + // AcceptEx, per documentation, requires an extra 16 bytes per address. + const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{})) + var addrbuf [addrlen * 2]byte + + var bytes uint32 + err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0, addrlen, addrlen, &bytes, &c.o) + _, err = l.sock.asyncIo(c, nil, bytes, err) + if err != nil { + return nil, l.opErr("accept", os.NewSyscallError("acceptex", err)) + } + conn := &HvsockConn{ + sock: sock, + } + conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0]))) + conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen]))) + sock = nil + return conn, nil +} + +// Close closes the listener, causing any pending Accept calls to fail. +func (l *HvsockListener) Close() error { + return l.sock.Close() +} + +/* Need to finish ConnectEx handling +func DialHvsock(ctx context.Context, addr *HvsockAddr) (*HvsockConn, error) { + sock, err := newHvSocket() + if err != nil { + return nil, err + } + defer func() { + if sock != nil { + sock.Close() + } + }() + c, err := sock.prepareIo() + if err != nil { + return nil, err + } + defer sock.wg.Done() + var bytes uint32 + err = windows.ConnectEx(windows.Handle(sock.handle), sa, nil, 0, &bytes, &c.o) + _, err = sock.asyncIo(ctx, c, nil, bytes, err) + if err != nil { + return nil, err + } + conn := &HvsockConn{ + sock: sock, + remote: *addr, + } + sock = nil + return conn, nil +} +*/ + +func (conn *HvsockConn) opErr(op string, err error) error { + return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err} +} + +func (conn *HvsockConn) Read(b []byte) (int, error) { + c, err := conn.sock.prepareIo() + if err != nil { + return 0, conn.opErr("read", err) + } + defer conn.sock.wg.Done() + buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} + var flags, bytes uint32 + err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil) + n, err := conn.sock.asyncIo(c, &conn.sock.readDeadline, bytes, err) + if err != nil { + if _, ok := err.(syscall.Errno); ok { + err = os.NewSyscallError("wsarecv", err) + } + return 0, conn.opErr("read", err) + } else if n == 0 { + err = io.EOF + } + return n, err +} + +func (conn *HvsockConn) Write(b []byte) (int, error) { + t := 0 + for len(b) != 0 { + n, err := conn.write(b) + if err != nil { + return t + n, err + } + t += n + b = b[n:] + } + return t, nil +} + +func (conn *HvsockConn) write(b []byte) (int, error) { + c, err := conn.sock.prepareIo() + if err != nil { + return 0, conn.opErr("write", err) + } + defer conn.sock.wg.Done() + buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} + var bytes uint32 + err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil) + n, err := conn.sock.asyncIo(c, &conn.sock.writeDeadline, bytes, err) + if err != nil { + if _, ok := err.(syscall.Errno); ok { + err = os.NewSyscallError("wsasend", err) + } + return 0, conn.opErr("write", err) + } + return n, err +} + +// Close closes the socket connection, failing any pending read or write calls. +func (conn *HvsockConn) Close() error { + return conn.sock.Close() +} + +func (conn *HvsockConn) shutdown(how int) error { + err := syscall.Shutdown(conn.sock.handle, syscall.SHUT_RD) + if err != nil { + return os.NewSyscallError("shutdown", err) + } + return nil +} + +// CloseRead shuts down the read end of the socket. +func (conn *HvsockConn) CloseRead() error { + err := conn.shutdown(syscall.SHUT_RD) + if err != nil { + return conn.opErr("close", err) + } + return nil +} + +// CloseWrite shuts down the write end of the socket, notifying the other endpoint that +// no more data will be written. +func (conn *HvsockConn) CloseWrite() error { + err := conn.shutdown(syscall.SHUT_WR) + if err != nil { + return conn.opErr("close", err) + } + return nil +} + +// LocalAddr returns the local address of the connection. +func (conn *HvsockConn) LocalAddr() net.Addr { + return &conn.local +} + +// RemoteAddr returns the remote address of the connection. +func (conn *HvsockConn) RemoteAddr() net.Addr { + return &conn.remote +} + +// SetDeadline implements the net.Conn SetDeadline method. +func (conn *HvsockConn) SetDeadline(t time.Time) error { + conn.SetReadDeadline(t) + conn.SetWriteDeadline(t) + return nil +} + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (conn *HvsockConn) SetReadDeadline(t time.Time) error { + return conn.sock.SetReadDeadline(t) +} + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (conn *HvsockConn) SetWriteDeadline(t time.Time) error { + return conn.sock.SetWriteDeadline(t) +} diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go index d99eedb6489b0..96700a73de25a 100644 --- a/vendor/github.com/Microsoft/go-winio/pipe.go +++ b/vendor/github.com/Microsoft/go-winio/pipe.go @@ -3,10 +3,13 @@ package winio import ( + "context" "errors" + "fmt" "io" "net" "os" + "runtime" "syscall" "time" "unsafe" @@ -18,6 +21,48 @@ import ( //sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo //sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW //sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc +//sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) = ntdll.NtCreateNamedPipeFile +//sys rtlNtStatusToDosError(status ntstatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb +//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) = ntdll.RtlDosPathNameToNtPathName_U +//sys rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) = ntdll.RtlDefaultNpAcl + +type ioStatusBlock struct { + Status, Information uintptr +} + +type objectAttributes struct { + Length uintptr + RootDirectory uintptr + ObjectName *unicodeString + Attributes uintptr + SecurityDescriptor *securityDescriptor + SecurityQoS uintptr +} + +type unicodeString struct { + Length uint16 + MaximumLength uint16 + Buffer uintptr +} + +type securityDescriptor struct { + Revision byte + Sbz1 byte + Control uint16 + Owner uintptr + Group uintptr + Sacl uintptr + Dacl uintptr +} + +type ntstatus int32 + +func (status ntstatus) Err() error { + if status >= 0 { + return nil + } + return rtlNtStatusToDosError(status) +} const ( cERROR_PIPE_BUSY = syscall.Errno(231) @@ -25,21 +70,20 @@ const ( cERROR_PIPE_CONNECTED = syscall.Errno(535) cERROR_SEM_TIMEOUT = syscall.Errno(121) - cPIPE_ACCESS_DUPLEX = 0x3 - cFILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000 - cSECURITY_SQOS_PRESENT = 0x100000 - cSECURITY_ANONYMOUS = 0 + cSECURITY_SQOS_PRESENT = 0x100000 + cSECURITY_ANONYMOUS = 0 - cPIPE_REJECT_REMOTE_CLIENTS = 0x8 + cPIPE_TYPE_MESSAGE = 4 - cPIPE_UNLIMITED_INSTANCES = 255 + cPIPE_READMODE_MESSAGE = 2 - cNMPWAIT_USE_DEFAULT_WAIT = 0 - cNMPWAIT_NOWAIT = 1 + cFILE_OPEN = 1 + cFILE_CREATE = 2 - cPIPE_TYPE_MESSAGE = 4 + cFILE_PIPE_MESSAGE_TYPE = 1 + cFILE_PIPE_REJECT_REMOTE_CLIENTS = 2 - cPIPE_READMODE_MESSAGE = 2 + cSE_DACL_PRESENT = 4 ) var ( @@ -137,33 +181,60 @@ func (s pipeAddress) String() string { return string(s) } +// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout. +func tryDialPipe(ctx context.Context, path *string, access uint32) (syscall.Handle, error) { + for { + + select { + case <-ctx.Done(): + return syscall.Handle(0), ctx.Err() + default: + h, err := createFile(*path, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) + if err == nil { + return h, nil + } + if err != cERROR_PIPE_BUSY { + return h, &os.PathError{Err: err, Op: "open", Path: *path} + } + // Wait 10 msec and try again. This is a rather simplistic + // view, as we always try each 10 milliseconds. + time.Sleep(10 * time.Millisecond) + } + } +} + // DialPipe connects to a named pipe by path, timing out if the connection // takes longer than the specified duration. If timeout is nil, then we use -// a default timeout of 5 seconds. (We do not use WaitNamedPipe.) +// a default timeout of 2 seconds. (We do not use WaitNamedPipe.) func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { var absTimeout time.Time if timeout != nil { absTimeout = time.Now().Add(*timeout) } else { - absTimeout = time.Now().Add(time.Second * 2) + absTimeout = time.Now().Add(2 * time.Second) + } + ctx, _ := context.WithDeadline(context.Background(), absTimeout) + conn, err := DialPipeContext(ctx, path) + if err == context.DeadlineExceeded { + return nil, ErrTimeout } + return conn, err +} + +// DialPipeContext attempts to connect to a named pipe by `path` until `ctx` +// cancellation or timeout. +func DialPipeContext(ctx context.Context, path string) (net.Conn, error) { + return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE) +} + +// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx` +// cancellation or timeout. +func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) { var err error var h syscall.Handle - for { - h, err = createFile(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) - if err != cERROR_PIPE_BUSY { - break - } - if time.Now().After(absTimeout) { - return nil, ErrTimeout - } - - // Wait 10 msec and try again. This is a rather simplistic - // view, as we always try each 10 milliseconds. - time.Sleep(time.Millisecond * 10) - } + h, err = tryDialPipe(ctx, &path, access) if err != nil { - return nil, &os.PathError{Op: "open", Path: path, Err: err} + return nil, err } var flags uint32 @@ -194,43 +265,87 @@ type acceptResponse struct { } type win32PipeListener struct { - firstHandle syscall.Handle - path string - securityDescriptor []byte - config PipeConfig - acceptCh chan (chan acceptResponse) - closeCh chan int - doneCh chan int + firstHandle syscall.Handle + path string + config PipeConfig + acceptCh chan (chan acceptResponse) + closeCh chan int + doneCh chan int } -func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, first bool) (syscall.Handle, error) { - var flags uint32 = cPIPE_ACCESS_DUPLEX | syscall.FILE_FLAG_OVERLAPPED +func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) { + path16, err := syscall.UTF16FromString(path) + if err != nil { + return 0, &os.PathError{Op: "open", Path: path, Err: err} + } + + var oa objectAttributes + oa.Length = unsafe.Sizeof(oa) + + var ntPath unicodeString + if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0).Err(); err != nil { + return 0, &os.PathError{Op: "open", Path: path, Err: err} + } + defer localFree(ntPath.Buffer) + oa.ObjectName = &ntPath + + // The security descriptor is only needed for the first pipe. if first { - flags |= cFILE_FLAG_FIRST_PIPE_INSTANCE + if sd != nil { + len := uint32(len(sd)) + sdb := localAlloc(0, len) + defer localFree(sdb) + copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd) + oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb)) + } else { + // Construct the default named pipe security descriptor. + var dacl uintptr + if err := rtlDefaultNpAcl(&dacl).Err(); err != nil { + return 0, fmt.Errorf("getting default named pipe ACL: %s", err) + } + defer localFree(dacl) + + sdb := &securityDescriptor{ + Revision: 1, + Control: cSE_DACL_PRESENT, + Dacl: dacl, + } + oa.SecurityDescriptor = sdb + } } - var mode uint32 = cPIPE_REJECT_REMOTE_CLIENTS + typ := uint32(cFILE_PIPE_REJECT_REMOTE_CLIENTS) if c.MessageMode { - mode |= cPIPE_TYPE_MESSAGE + typ |= cFILE_PIPE_MESSAGE_TYPE } - sa := &syscall.SecurityAttributes{} - sa.Length = uint32(unsafe.Sizeof(*sa)) - if securityDescriptor != nil { - len := uint32(len(securityDescriptor)) - sa.SecurityDescriptor = localAlloc(0, len) - defer localFree(sa.SecurityDescriptor) - copy((*[0xffff]byte)(unsafe.Pointer(sa.SecurityDescriptor))[:], securityDescriptor) + disposition := uint32(cFILE_OPEN) + access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE) + if first { + disposition = cFILE_CREATE + // By not asking for read or write access, the named pipe file system + // will put this pipe into an initially disconnected state, blocking + // client connections until the next call with first == false. + access = syscall.SYNCHRONIZE } - h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, sa) + + timeout := int64(-50 * 10000) // 50ms + + var ( + h syscall.Handle + iosb ioStatusBlock + ) + err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err() if err != nil { return 0, &os.PathError{Op: "open", Path: path, Err: err} } + + runtime.KeepAlive(ntPath) return h, nil } func (l *win32PipeListener) makeServerPipe() (*win32File, error) { - h, err := makeServerPipeHandle(l.path, l.securityDescriptor, &l.config, false) + h, err := makeServerPipeHandle(l.path, nil, &l.config, false) if err != nil { return nil, err } @@ -314,10 +429,10 @@ type PipeConfig struct { // when the pipe is in message mode. MessageMode bool - // InputBufferSize specifies the size the input buffer, in bytes. + // InputBufferSize specifies the size of the input buffer, in bytes. InputBufferSize int32 - // OutputBufferSize specifies the size the input buffer, in bytes. + // OutputBufferSize specifies the size of the output buffer, in bytes. OutputBufferSize int32 } @@ -341,32 +456,13 @@ func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { if err != nil { return nil, err } - // Create a client handle and connect it. This results in the pipe - // instance always existing, so that clients see ERROR_PIPE_BUSY - // rather than ERROR_FILE_NOT_FOUND. This ties the first instance - // up so that no other instances can be used. This would have been - // cleaner if the Win32 API matched CreateFile with ConnectNamedPipe - // instead of CreateNamedPipe. (Apparently created named pipes are - // considered to be in listening state regardless of whether any - // active calls to ConnectNamedPipe are outstanding.) - h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) - if err != nil { - syscall.Close(h) - return nil, err - } - // Close the client handle. The server side of the instance will - // still be busy, leading to ERROR_PIPE_BUSY instead of - // ERROR_NOT_FOUND, as long as we don't close the server handle, - // or disconnect the client with DisconnectNamedPipe. - syscall.Close(h2) l := &win32PipeListener{ - firstHandle: h, - path: path, - securityDescriptor: sd, - config: *c, - acceptCh: make(chan (chan acceptResponse)), - closeCh: make(chan int), - doneCh: make(chan int), + firstHandle: h, + path: path, + config: *c, + acceptCh: make(chan (chan acceptResponse)), + closeCh: make(chan int), + doneCh: make(chan int), } go l.listenerRoutine() return l, nil diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/etw.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/etw.go new file mode 100644 index 0000000000000..10cd08d84c2b9 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/etw.go @@ -0,0 +1,20 @@ +// Package etw provides support for TraceLogging-based ETW (Event Tracing +// for Windows). TraceLogging is a format of ETW events that are self-describing +// (the event contains information on its own schema). This allows them to be +// decoded without needing a separate manifest with event information. The +// implementation here is based on the information found in +// TraceLoggingProvider.h in the Windows SDK, which implements TraceLogging as a +// set of C macros. +package etw + +//go:generate go run mksyscall_windows.go -output zsyscall_windows.go etw.go + +//sys eventRegister(providerId *windows.GUID, callback uintptr, callbackContext uintptr, providerHandle *providerHandle) (win32err error) = advapi32.EventRegister + +//sys eventUnregister_64(providerHandle providerHandle) (win32err error) = advapi32.EventUnregister +//sys eventWriteTransfer_64(providerHandle providerHandle, descriptor *eventDescriptor, activityID *windows.GUID, relatedActivityID *windows.GUID, dataDescriptorCount uint32, dataDescriptors *eventDataDescriptor) (win32err error) = advapi32.EventWriteTransfer +//sys eventSetInformation_64(providerHandle providerHandle, class eventInfoClass, information uintptr, length uint32) (win32err error) = advapi32.EventSetInformation + +//sys eventUnregister_32(providerHandle_low uint32, providerHandle_high uint32) (win32err error) = advapi32.EventUnregister +//sys eventWriteTransfer_32(providerHandle_low uint32, providerHandle_high uint32, descriptor *eventDescriptor, activityID *windows.GUID, relatedActivityID *windows.GUID, dataDescriptorCount uint32, dataDescriptors *eventDataDescriptor) (win32err error) = advapi32.EventWriteTransfer +//sys eventSetInformation_32(providerHandle_low uint32, providerHandle_high uint32, class eventInfoClass, information uintptr, length uint32) (win32err error) = advapi32.EventSetInformation diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdata.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdata.go new file mode 100644 index 0000000000000..abf16803ee40d --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdata.go @@ -0,0 +1,73 @@ +// +build windows + +package etw + +import ( + "bytes" + "encoding/binary" + "syscall" +) + +// eventData maintains a buffer which builds up the data for an ETW event. It +// needs to be paired with EventMetadata which describes the event. +type eventData struct { + buffer bytes.Buffer +} + +// bytes returns the raw binary data containing the event data. The returned +// value is not copied from the internal buffer, so it can be mutated by the +// eventData object after it is returned. +func (ed *eventData) bytes() []byte { + return ed.buffer.Bytes() +} + +// writeString appends a string, including the null terminator, to the buffer. +func (ed *eventData) writeString(data string) { + ed.buffer.WriteString(data) + ed.buffer.WriteByte(0) +} + +// writeInt8 appends a int8 to the buffer. +func (ed *eventData) writeInt8(value int8) { + ed.buffer.WriteByte(uint8(value)) +} + +// writeInt16 appends a int16 to the buffer. +func (ed *eventData) writeInt16(value int16) { + binary.Write(&ed.buffer, binary.LittleEndian, value) +} + +// writeInt32 appends a int32 to the buffer. +func (ed *eventData) writeInt32(value int32) { + binary.Write(&ed.buffer, binary.LittleEndian, value) +} + +// writeInt64 appends a int64 to the buffer. +func (ed *eventData) writeInt64(value int64) { + binary.Write(&ed.buffer, binary.LittleEndian, value) +} + +// writeUint8 appends a uint8 to the buffer. +func (ed *eventData) writeUint8(value uint8) { + ed.buffer.WriteByte(value) +} + +// writeUint16 appends a uint16 to the buffer. +func (ed *eventData) writeUint16(value uint16) { + binary.Write(&ed.buffer, binary.LittleEndian, value) +} + +// writeUint32 appends a uint32 to the buffer. +func (ed *eventData) writeUint32(value uint32) { + binary.Write(&ed.buffer, binary.LittleEndian, value) +} + +// writeUint64 appends a uint64 to the buffer. +func (ed *eventData) writeUint64(value uint64) { + binary.Write(&ed.buffer, binary.LittleEndian, value) +} + +// writeFiletime appends a FILETIME to the buffer. +func (ed *eventData) writeFiletime(value syscall.Filetime) { + binary.Write(&ed.buffer, binary.LittleEndian, value) +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdatadescriptor.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdatadescriptor.go new file mode 100644 index 0000000000000..8b0ad4816254f --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdatadescriptor.go @@ -0,0 +1,29 @@ +package etw + +import ( + "unsafe" +) + +type eventDataDescriptorType uint8 + +const ( + eventDataDescriptorTypeUserData eventDataDescriptorType = iota + eventDataDescriptorTypeEventMetadata + eventDataDescriptorTypeProviderMetadata +) + +type eventDataDescriptor struct { + ptr ptr64 + size uint32 + dataType eventDataDescriptorType + reserved1 uint8 + reserved2 uint16 +} + +func newEventDataDescriptor(dataType eventDataDescriptorType, buffer []byte) eventDataDescriptor { + return eventDataDescriptor{ + ptr: ptr64{ptr: unsafe.Pointer(&buffer[0])}, + size: uint32(len(buffer)), + dataType: dataType, + } +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdescriptor.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdescriptor.go new file mode 100644 index 0000000000000..cc41f15999c3c --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventdescriptor.go @@ -0,0 +1,84 @@ +package etw + +// Channel represents the ETW logging channel that is used. It can be used by +// event consumers to give an event special treatment. +type Channel uint8 + +const ( + // ChannelTraceLogging is the default channel for TraceLogging events. It is + // not required to be used for TraceLogging, but will prevent decoding + // issues for these events on older operating systems. + ChannelTraceLogging Channel = 11 +) + +// Level represents the ETW logging level. There are several predefined levels +// that are commonly used, but technically anything from 0-255 is allowed. +// Lower levels indicate more important events, and 0 indicates an event that +// will always be collected. +type Level uint8 + +// Predefined ETW log levels from winmeta.xml in the Windows SDK. +const ( + LevelAlways Level = iota + LevelCritical + LevelError + LevelWarning + LevelInfo + LevelVerbose +) + +// Opcode represents the operation that the event indicates is being performed. +type Opcode uint8 + +// Predefined ETW opcodes from winmeta.xml in the Windows SDK. +const ( + // OpcodeInfo indicates an informational event. + OpcodeInfo Opcode = iota + // OpcodeStart indicates the start of an operation. + OpcodeStart + // OpcodeStop indicates the end of an operation. + OpcodeStop + // OpcodeDCStart indicates the start of a provider capture state operation. + OpcodeDCStart + // OpcodeDCStop indicates the end of a provider capture state operation. + OpcodeDCStop +) + +// EventDescriptor represents various metadata for an ETW event. +type eventDescriptor struct { + id uint16 + version uint8 + channel Channel + level Level + opcode Opcode + task uint16 + keyword uint64 +} + +// NewEventDescriptor returns an EventDescriptor initialized for use with +// TraceLogging. +func newEventDescriptor() *eventDescriptor { + // Standard TraceLogging events default to the TraceLogging channel, and + // verbose level. + return &eventDescriptor{ + channel: ChannelTraceLogging, + level: LevelVerbose, + } +} + +// Identity returns the identity of the event. If the identity is not 0, it +// should uniquely identify the other event metadata (contained in +// EventDescriptor, and field metadata). Only the lower 24 bits of this value +// are relevant. +func (ed *eventDescriptor) identity() uint32 { + return (uint32(ed.version) << 16) | uint32(ed.id) +} + +// SetIdentity sets the identity of the event. If the identity is not 0, it +// should uniquely identify the other event metadata (contained in +// EventDescriptor, and field metadata). Only the lower 24 bits of this value +// are relevant. +func (ed *eventDescriptor) setIdentity(identity uint32) { + ed.id = uint16(identity) + ed.version = uint8(identity >> 16) +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/eventmetadata.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventmetadata.go new file mode 100644 index 0000000000000..6fdc126cc90e9 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventmetadata.go @@ -0,0 +1,177 @@ +package etw + +import ( + "bytes" + "encoding/binary" +) + +// inType indicates the type of data contained in the ETW event. +type inType byte + +// Various inType definitions for TraceLogging. These must match the definitions +// found in TraceLoggingProvider.h in the Windows SDK. +const ( + inTypeNull inType = iota + inTypeUnicodeString + inTypeANSIString + inTypeInt8 + inTypeUint8 + inTypeInt16 + inTypeUint16 + inTypeInt32 + inTypeUint32 + inTypeInt64 + inTypeUint64 + inTypeFloat + inTypeDouble + inTypeBool32 + inTypeBinary + inTypeGUID + inTypePointerUnsupported + inTypeFileTime + inTypeSystemTime + inTypeSID + inTypeHexInt32 + inTypeHexInt64 + inTypeCountedString + inTypeCountedANSIString + inTypeStruct + inTypeCountedBinary + inTypeCountedArray inType = 32 + inTypeArray inType = 64 +) + +// outType specifies a hint to the event decoder for how the value should be +// formatted. +type outType byte + +// Various outType definitions for TraceLogging. These must match the +// definitions found in TraceLoggingProvider.h in the Windows SDK. +const ( + // outTypeDefault indicates that the default formatting for the inType will + // be used by the event decoder. + outTypeDefault outType = iota + outTypeNoPrint + outTypeString + outTypeBoolean + outTypeHex + outTypePID + outTypeTID + outTypePort + outTypeIPv4 + outTypeIPv6 + outTypeSocketAddress + outTypeXML + outTypeJSON + outTypeWin32Error + outTypeNTStatus + outTypeHResult + outTypeFileTime + outTypeSigned + outTypeUnsigned + outTypeUTF8 outType = 35 + outTypePKCS7WithTypeInfo outType = 36 + outTypeCodePointer outType = 37 + outTypeDateTimeUTC outType = 38 +) + +// eventMetadata maintains a buffer which builds up the metadata for an ETW +// event. It needs to be paired with EventData which describes the event. +type eventMetadata struct { + buffer bytes.Buffer +} + +// bytes returns the raw binary data containing the event metadata. Before being +// returned, the current size of the buffer is written to the start of the +// buffer. The returned value is not copied from the internal buffer, so it can +// be mutated by the eventMetadata object after it is returned. +func (em *eventMetadata) bytes() []byte { + // Finalize the event metadata buffer by filling in the buffer length at the + // beginning. + binary.LittleEndian.PutUint16(em.buffer.Bytes(), uint16(em.buffer.Len())) + return em.buffer.Bytes() +} + +// writeEventHeader writes the metadata for the start of an event to the buffer. +// This specifies the event name and tags. +func (em *eventMetadata) writeEventHeader(name string, tags uint32) { + binary.Write(&em.buffer, binary.LittleEndian, uint16(0)) // Length placeholder + em.writeTags(tags) + em.buffer.WriteString(name) + em.buffer.WriteByte(0) // Null terminator for name +} + +func (em *eventMetadata) writeFieldInner(name string, inType inType, outType outType, tags uint32, arrSize uint16) { + em.buffer.WriteString(name) + em.buffer.WriteByte(0) // Null terminator for name + + if outType == outTypeDefault && tags == 0 { + em.buffer.WriteByte(byte(inType)) + } else { + em.buffer.WriteByte(byte(inType | 128)) + if tags == 0 { + em.buffer.WriteByte(byte(outType)) + } else { + em.buffer.WriteByte(byte(outType | 128)) + em.writeTags(tags) + } + } + + if arrSize != 0 { + binary.Write(&em.buffer, binary.LittleEndian, arrSize) + } +} + +// writeTags writes out the tags value to the event metadata. Tags is a 28-bit +// value, interpreted as bit flags, which are only relevant to the event +// consumer. The event consumer may choose to attribute special meaning to tags +// (e.g. 0x4 could mean the field contains PII). Tags are written as a series of +// bytes, each containing 7 bits of tag value, with the high bit set if there is +// more tag data in the following byte. This allows for a more compact +// representation when not all of the tag bits are needed. +func (em *eventMetadata) writeTags(tags uint32) { + // Only use the top 28 bits of the tags value. + tags &= 0xfffffff + + for { + // Tags are written with the most significant bits (e.g. 21-27) first. + val := tags >> 21 + + if tags&0x1fffff == 0 { + // If there is no more data to write after this, write this value + // without the high bit set, and return. + em.buffer.WriteByte(byte(val & 0x7f)) + return + } + + em.buffer.WriteByte(byte(val | 0x80)) + + tags <<= 7 + } +} + +// writeField writes the metadata for a simple field to the buffer. +func (em *eventMetadata) writeField(name string, inType inType, outType outType, tags uint32) { + em.writeFieldInner(name, inType, outType, tags, 0) +} + +// writeArray writes the metadata for an array field to the buffer. The number +// of elements in the array must be written as a uint16 in the event data, +// immediately preceeding the event data. +func (em *eventMetadata) writeArray(name string, inType inType, outType outType, tags uint32) { + em.writeFieldInner(name, inType|inTypeArray, outType, tags, 0) +} + +// writeCountedArray writes the metadata for an array field to the buffer. The +// size of a counted array is fixed, and the size is written into the metadata +// directly. +func (em *eventMetadata) writeCountedArray(name string, count uint16, inType inType, outType outType, tags uint32) { + em.writeFieldInner(name, inType|inTypeCountedArray, outType, tags, count) +} + +// writeStruct writes the metadata for a nested struct to the buffer. The struct +// contains the next N fields in the metadata, where N is specified by the +// fieldCount argument. +func (em *eventMetadata) writeStruct(name string, fieldCount uint8, tags uint32) { + em.writeFieldInner(name, inTypeStruct, outType(fieldCount), tags, 0) +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/eventopt.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventopt.go new file mode 100644 index 0000000000000..eaace6886edc9 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/eventopt.go @@ -0,0 +1,75 @@ +// +build windows + +package etw + +import ( + "github.com/Microsoft/go-winio/pkg/guid" +) + +type eventOptions struct { + descriptor *eventDescriptor + activityID guid.GUID + relatedActivityID guid.GUID + tags uint32 +} + +// EventOpt defines the option function type that can be passed to +// Provider.WriteEvent to specify general event options, such as level and +// keyword. +type EventOpt func(options *eventOptions) + +// WithEventOpts returns the variadic arguments as a single slice. +func WithEventOpts(opts ...EventOpt) []EventOpt { + return opts +} + +// WithLevel specifies the level of the event to be written. +func WithLevel(level Level) EventOpt { + return func(options *eventOptions) { + options.descriptor.level = level + } +} + +// WithKeyword specifies the keywords of the event to be written. Multiple uses +// of this option are OR'd together. +func WithKeyword(keyword uint64) EventOpt { + return func(options *eventOptions) { + options.descriptor.keyword |= keyword + } +} + +// WithChannel specifies the channel of the event to be written. +func WithChannel(channel Channel) EventOpt { + return func(options *eventOptions) { + options.descriptor.channel = channel + } +} + +// WithOpcode specifies the opcode of the event to be written. +func WithOpcode(opcode Opcode) EventOpt { + return func(options *eventOptions) { + options.descriptor.opcode = opcode + } +} + +// WithTags specifies the tags of the event to be written. Tags is a 28-bit +// value (top 4 bits are ignored) which are interpreted by the event consumer. +func WithTags(newTags uint32) EventOpt { + return func(options *eventOptions) { + options.tags |= newTags + } +} + +// WithActivityID specifies the activity ID of the event to be written. +func WithActivityID(activityID guid.GUID) EventOpt { + return func(options *eventOptions) { + options.activityID = activityID + } +} + +// WithRelatedActivityID specifies the parent activity ID of the event to be written. +func WithRelatedActivityID(activityID guid.GUID) EventOpt { + return func(options *eventOptions) { + options.relatedActivityID = activityID + } +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/fieldopt.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/fieldopt.go new file mode 100644 index 0000000000000..b5ea80a4607aa --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/fieldopt.go @@ -0,0 +1,516 @@ +// +build windows + +package etw + +import ( + "fmt" + "math" + "reflect" + "syscall" + "time" + "unsafe" +) + +// FieldOpt defines the option function type that can be passed to +// Provider.WriteEvent to add fields to the event. +type FieldOpt func(em *eventMetadata, ed *eventData) + +// WithFields returns the variadic arguments as a single slice. +func WithFields(opts ...FieldOpt) []FieldOpt { + return opts +} + +// BoolField adds a single bool field to the event. +func BoolField(name string, value bool) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeUint8, outTypeBoolean, 0) + bool8 := uint8(0) + if value { + bool8 = uint8(1) + } + ed.writeUint8(bool8) + } +} + +// BoolArray adds an array of bool to the event. +func BoolArray(name string, values []bool) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeUint8, outTypeBoolean, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + bool8 := uint8(0) + if v { + bool8 = uint8(1) + } + ed.writeUint8(bool8) + } + } +} + +// StringField adds a single string field to the event. +func StringField(name string, value string) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeANSIString, outTypeUTF8, 0) + ed.writeString(value) + } +} + +// StringArray adds an array of string to the event. +func StringArray(name string, values []string) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeANSIString, outTypeUTF8, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeString(v) + } + } +} + +// IntField adds a single int field to the event. +func IntField(name string, value int) FieldOpt { + switch unsafe.Sizeof(value) { + case 4: + return Int32Field(name, int32(value)) + case 8: + return Int64Field(name, int64(value)) + default: + panic("Unsupported int size") + } +} + +// IntArray adds an array of int to the event. +func IntArray(name string, values []int) FieldOpt { + inType := inTypeNull + var writeItem func(*eventData, int) + switch unsafe.Sizeof(values[0]) { + case 4: + inType = inTypeInt32 + writeItem = func(ed *eventData, item int) { ed.writeInt32(int32(item)) } + case 8: + inType = inTypeInt64 + writeItem = func(ed *eventData, item int) { ed.writeInt64(int64(item)) } + default: + panic("Unsupported int size") + } + + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inType, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + writeItem(ed, v) + } + } +} + +// Int8Field adds a single int8 field to the event. +func Int8Field(name string, value int8) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeInt8, outTypeDefault, 0) + ed.writeInt8(value) + } +} + +// Int8Array adds an array of int8 to the event. +func Int8Array(name string, values []int8) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeInt8, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeInt8(v) + } + } +} + +// Int16Field adds a single int16 field to the event. +func Int16Field(name string, value int16) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeInt16, outTypeDefault, 0) + ed.writeInt16(value) + } +} + +// Int16Array adds an array of int16 to the event. +func Int16Array(name string, values []int16) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeInt16, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeInt16(v) + } + } +} + +// Int32Field adds a single int32 field to the event. +func Int32Field(name string, value int32) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeInt32, outTypeDefault, 0) + ed.writeInt32(value) + } +} + +// Int32Array adds an array of int32 to the event. +func Int32Array(name string, values []int32) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeInt32, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeInt32(v) + } + } +} + +// Int64Field adds a single int64 field to the event. +func Int64Field(name string, value int64) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeInt64, outTypeDefault, 0) + ed.writeInt64(value) + } +} + +// Int64Array adds an array of int64 to the event. +func Int64Array(name string, values []int64) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeInt64, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeInt64(v) + } + } +} + +// UintField adds a single uint field to the event. +func UintField(name string, value uint) FieldOpt { + switch unsafe.Sizeof(value) { + case 4: + return Uint32Field(name, uint32(value)) + case 8: + return Uint64Field(name, uint64(value)) + default: + panic("Unsupported uint size") + } +} + +// UintArray adds an array of uint to the event. +func UintArray(name string, values []uint) FieldOpt { + inType := inTypeNull + var writeItem func(*eventData, uint) + switch unsafe.Sizeof(values[0]) { + case 4: + inType = inTypeUint32 + writeItem = func(ed *eventData, item uint) { ed.writeUint32(uint32(item)) } + case 8: + inType = inTypeUint64 + writeItem = func(ed *eventData, item uint) { ed.writeUint64(uint64(item)) } + default: + panic("Unsupported uint size") + } + + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inType, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + writeItem(ed, v) + } + } +} + +// Uint8Field adds a single uint8 field to the event. +func Uint8Field(name string, value uint8) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeUint8, outTypeDefault, 0) + ed.writeUint8(value) + } +} + +// Uint8Array adds an array of uint8 to the event. +func Uint8Array(name string, values []uint8) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeUint8, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeUint8(v) + } + } +} + +// Uint16Field adds a single uint16 field to the event. +func Uint16Field(name string, value uint16) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeUint16, outTypeDefault, 0) + ed.writeUint16(value) + } +} + +// Uint16Array adds an array of uint16 to the event. +func Uint16Array(name string, values []uint16) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeUint16, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeUint16(v) + } + } +} + +// Uint32Field adds a single uint32 field to the event. +func Uint32Field(name string, value uint32) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeUint32, outTypeDefault, 0) + ed.writeUint32(value) + } +} + +// Uint32Array adds an array of uint32 to the event. +func Uint32Array(name string, values []uint32) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeUint32, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeUint32(v) + } + } +} + +// Uint64Field adds a single uint64 field to the event. +func Uint64Field(name string, value uint64) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeUint64, outTypeDefault, 0) + ed.writeUint64(value) + } +} + +// Uint64Array adds an array of uint64 to the event. +func Uint64Array(name string, values []uint64) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeUint64, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeUint64(v) + } + } +} + +// UintptrField adds a single uintptr field to the event. +func UintptrField(name string, value uintptr) FieldOpt { + inType := inTypeNull + var writeItem func(*eventData, uintptr) + switch unsafe.Sizeof(value) { + case 4: + inType = inTypeHexInt32 + writeItem = func(ed *eventData, item uintptr) { ed.writeUint32(uint32(item)) } + case 8: + inType = inTypeHexInt64 + writeItem = func(ed *eventData, item uintptr) { ed.writeUint64(uint64(item)) } + default: + panic("Unsupported uintptr size") + } + + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inType, outTypeDefault, 0) + writeItem(ed, value) + } +} + +// UintptrArray adds an array of uintptr to the event. +func UintptrArray(name string, values []uintptr) FieldOpt { + inType := inTypeNull + var writeItem func(*eventData, uintptr) + switch unsafe.Sizeof(values[0]) { + case 4: + inType = inTypeHexInt32 + writeItem = func(ed *eventData, item uintptr) { ed.writeUint32(uint32(item)) } + case 8: + inType = inTypeHexInt64 + writeItem = func(ed *eventData, item uintptr) { ed.writeUint64(uint64(item)) } + default: + panic("Unsupported uintptr size") + } + + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inType, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + writeItem(ed, v) + } + } +} + +// Float32Field adds a single float32 field to the event. +func Float32Field(name string, value float32) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeFloat, outTypeDefault, 0) + ed.writeUint32(math.Float32bits(value)) + } +} + +// Float32Array adds an array of float32 to the event. +func Float32Array(name string, values []float32) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeFloat, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeUint32(math.Float32bits(v)) + } + } +} + +// Float64Field adds a single float64 field to the event. +func Float64Field(name string, value float64) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeDouble, outTypeDefault, 0) + ed.writeUint64(math.Float64bits(value)) + } +} + +// Float64Array adds an array of float64 to the event. +func Float64Array(name string, values []float64) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeArray(name, inTypeDouble, outTypeDefault, 0) + ed.writeUint16(uint16(len(values))) + for _, v := range values { + ed.writeUint64(math.Float64bits(v)) + } + } +} + +// Struct adds a nested struct to the event, the FieldOpts in the opts argument +// are used to specify the fields of the struct. +func Struct(name string, opts ...FieldOpt) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeStruct(name, uint8(len(opts)), 0) + for _, opt := range opts { + opt(em, ed) + } + } +} + +// Time adds a time to the event. +func Time(name string, value time.Time) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeFileTime, outTypeDateTimeUTC, 0) + ed.writeFiletime(syscall.NsecToFiletime(value.UTC().UnixNano())) + } +} + +// Currently, we support logging basic builtin types (int, string, etc), slices +// of basic builtin types, error, types derived from the basic types (e.g. "type +// foo int"), and structs (recursively logging their fields). We do not support +// slices of derived types (e.g. "[]foo"). +// +// For types that we don't support, the value is formatted via fmt.Sprint, and +// we also log a message that the type is unsupported along with the formatted +// type. The intent of this is to make it easier to see which types are not +// supported in traces, so we can evaluate adding support for more types in the +// future. +func SmartField(name string, v interface{}) FieldOpt { + switch v := v.(type) { + case bool: + return BoolField(name, v) + case []bool: + return BoolArray(name, v) + case string: + return StringField(name, v) + case []string: + return StringArray(name, v) + case int: + return IntField(name, v) + case []int: + return IntArray(name, v) + case int8: + return Int8Field(name, v) + case []int8: + return Int8Array(name, v) + case int16: + return Int16Field(name, v) + case []int16: + return Int16Array(name, v) + case int32: + return Int32Field(name, v) + case []int32: + return Int32Array(name, v) + case int64: + return Int64Field(name, v) + case []int64: + return Int64Array(name, v) + case uint: + return UintField(name, v) + case []uint: + return UintArray(name, v) + case uint8: + return Uint8Field(name, v) + case []uint8: + return Uint8Array(name, v) + case uint16: + return Uint16Field(name, v) + case []uint16: + return Uint16Array(name, v) + case uint32: + return Uint32Field(name, v) + case []uint32: + return Uint32Array(name, v) + case uint64: + return Uint64Field(name, v) + case []uint64: + return Uint64Array(name, v) + case uintptr: + return UintptrField(name, v) + case []uintptr: + return UintptrArray(name, v) + case float32: + return Float32Field(name, v) + case []float32: + return Float32Array(name, v) + case float64: + return Float64Field(name, v) + case []float64: + return Float64Array(name, v) + case error: + return StringField(name, v.Error()) + case time.Time: + return Time(name, v) + default: + switch rv := reflect.ValueOf(v); rv.Kind() { + case reflect.Bool: + return SmartField(name, rv.Bool()) + case reflect.Int: + return SmartField(name, int(rv.Int())) + case reflect.Int8: + return SmartField(name, int8(rv.Int())) + case reflect.Int16: + return SmartField(name, int16(rv.Int())) + case reflect.Int32: + return SmartField(name, int32(rv.Int())) + case reflect.Int64: + return SmartField(name, int64(rv.Int())) + case reflect.Uint: + return SmartField(name, uint(rv.Uint())) + case reflect.Uint8: + return SmartField(name, uint8(rv.Uint())) + case reflect.Uint16: + return SmartField(name, uint16(rv.Uint())) + case reflect.Uint32: + return SmartField(name, uint32(rv.Uint())) + case reflect.Uint64: + return SmartField(name, uint64(rv.Uint())) + case reflect.Uintptr: + return SmartField(name, uintptr(rv.Uint())) + case reflect.Float32: + return SmartField(name, float32(rv.Float())) + case reflect.Float64: + return SmartField(name, float64(rv.Float())) + case reflect.String: + return SmartField(name, rv.String()) + case reflect.Struct: + fields := make([]FieldOpt, 0, rv.NumField()) + for i := 0; i < rv.NumField(); i++ { + field := rv.Field(i) + if field.CanInterface() { + fields = append(fields, SmartField(name, field.Interface())) + } + } + return Struct(name, fields...) + } + } + + return StringField(name, fmt.Sprintf("(Unsupported: %T) %v", v, v)) +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider.go new file mode 100644 index 0000000000000..581ef595a5f97 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider.go @@ -0,0 +1,73 @@ +// +build windows +// +build amd64 arm64 386 + +package etw + +import ( + "bytes" + "encoding/binary" + "unsafe" + + "github.com/Microsoft/go-winio/pkg/guid" + "golang.org/x/sys/windows" +) + +// NewProviderWithOptions creates and registers a new ETW provider, allowing +// the provider ID and Group to be manually specified. This is most useful when +// there is an existing provider ID that must be used to conform to existing +// diagnostic infrastructure. +func NewProviderWithOptions(name string, options ...ProviderOpt) (provider *Provider, err error) { + var opts providerOpts + for _, opt := range options { + opt(&opts) + } + + if opts.id == (guid.GUID{}) { + opts.id = providerIDFromName(name) + } + + providerCallbackOnce.Do(func() { + globalProviderCallback = windows.NewCallback(providerCallbackAdapter) + }) + + provider = providers.newProvider() + defer func(provider *Provider) { + if err != nil { + providers.removeProvider(provider) + } + }(provider) + provider.ID = opts.id + provider.callback = opts.callback + + if err := eventRegister((*windows.GUID)(&provider.ID), globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil { + return nil, err + } + + trait := &bytes.Buffer{} + if opts.group != (guid.GUID{}) { + binary.Write(trait, binary.LittleEndian, uint16(0)) // Write empty size for buffer (update later) + binary.Write(trait, binary.LittleEndian, uint8(1)) // EtwProviderTraitTypeGroup + traitArray := opts.group.ToWindowsArray() // Append group guid + trait.Write(traitArray[:]) + binary.LittleEndian.PutUint16(trait.Bytes(), uint16(trait.Len())) // Update size + } + + metadata := &bytes.Buffer{} + binary.Write(metadata, binary.LittleEndian, uint16(0)) // Write empty size for buffer (to update later) + metadata.WriteString(name) + metadata.WriteByte(0) // Null terminator for name + trait.WriteTo(metadata) // Add traits if applicable + binary.LittleEndian.PutUint16(metadata.Bytes(), uint16(metadata.Len())) // Update the size at the beginning of the buffer + provider.metadata = metadata.Bytes() + + if err := eventSetInformation( + provider.handle, + eventInfoClassProviderSetTraits, + uintptr(unsafe.Pointer(&provider.metadata[0])), + uint32(len(provider.metadata))); err != nil { + + return nil, err + } + + return provider, nil +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider_unsupported.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider_unsupported.go new file mode 100644 index 0000000000000..5a05c13425421 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/newprovider_unsupported.go @@ -0,0 +1,9 @@ +// +build windows +// +build arm + +package etw + +// NewProviderWithID returns a nil provider on unsupported platforms. +func NewProviderWithOptions(name string, options ...ProviderOpt) (provider *Provider, err error) { + return nil, nil +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/provider.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/provider.go new file mode 100644 index 0000000000000..a5b90d037ddee --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/provider.go @@ -0,0 +1,273 @@ +// +build windows + +package etw + +import ( + "crypto/sha1" + "encoding/binary" + "strings" + "unicode/utf16" + + "github.com/Microsoft/go-winio/pkg/guid" + "golang.org/x/sys/windows" +) + +// Provider represents an ETW event provider. It is identified by a provider +// name and ID (GUID), which should always have a 1:1 mapping to each other +// (e.g. don't use multiple provider names with the same ID, or vice versa). +type Provider struct { + ID guid.GUID + handle providerHandle + metadata []byte + callback EnableCallback + index uint + enabled bool + level Level + keywordAny uint64 + keywordAll uint64 +} + +// String returns the `provider`.ID as a string +func (provider *Provider) String() string { + if provider == nil { + return "" + } + + return provider.ID.String() +} + +type providerHandle uint64 + +// ProviderState informs the provider EnableCallback what action is being +// performed. +type ProviderState uint32 + +const ( + // ProviderStateDisable indicates the provider is being disabled. + ProviderStateDisable ProviderState = iota + // ProviderStateEnable indicates the provider is being enabled. + ProviderStateEnable + // ProviderStateCaptureState indicates the provider is having its current + // state snap-shotted. + ProviderStateCaptureState +) + +type eventInfoClass uint32 + +const ( + eventInfoClassProviderBinaryTrackInfo eventInfoClass = iota + eventInfoClassProviderSetReserved1 + eventInfoClassProviderSetTraits + eventInfoClassProviderUseDescriptorType +) + +// EnableCallback is the form of the callback function that receives provider +// enable/disable notifications from ETW. +type EnableCallback func(guid.GUID, ProviderState, Level, uint64, uint64, uintptr) + +func providerCallback(sourceID guid.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) { + provider := providers.getProvider(uint(i)) + + switch state { + case ProviderStateDisable: + provider.enabled = false + case ProviderStateEnable: + provider.enabled = true + provider.level = level + provider.keywordAny = matchAnyKeyword + provider.keywordAll = matchAllKeyword + } + + if provider.callback != nil { + provider.callback(sourceID, state, level, matchAnyKeyword, matchAllKeyword, filterData) + } +} + +// providerIDFromName generates a provider ID based on the provider name. It +// uses the same algorithm as used by .NET's EventSource class, which is based +// on RFC 4122. More information on the algorithm can be found here: +// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/ +// +// The algorithm is roughly the RFC 4122 algorithm for a V5 UUID, but differs in +// the following ways: +// - The input name is first upper-cased, UTF16-encoded, and converted to +// big-endian. +// - No variant is set on the result UUID. +// - The result UUID is treated as being in little-endian format, rather than +// big-endian. +func providerIDFromName(name string) guid.GUID { + buffer := sha1.New() + namespace := guid.GUID{0x482C2DB2, 0xC390, 0x47C8, [8]byte{0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}} + namespaceBytes := namespace.ToArray() + buffer.Write(namespaceBytes[:]) + binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name)))) + + sum := buffer.Sum(nil) + sum[7] = (sum[7] & 0xf) | 0x50 + + a := [16]byte{} + copy(a[:], sum) + return guid.FromWindowsArray(a) +} + +type providerOpts struct { + callback EnableCallback + id guid.GUID + group guid.GUID +} + +// ProviderOpt allows the caller to specify provider options to +// NewProviderWithOptions +type ProviderOpt func(*providerOpts) + +// WithCallback is used to provide a callback option to NewProviderWithOptions +func WithCallback(callback EnableCallback) ProviderOpt { + return func(opts *providerOpts) { + opts.callback = callback + } +} + +// WithID is used to provide a provider ID option to NewProviderWithOptions +func WithID(id guid.GUID) ProviderOpt { + return func(opts *providerOpts) { + opts.id = id + } +} + +// WithGroup is used to provide a provider group option to +// NewProviderWithOptions +func WithGroup(group guid.GUID) ProviderOpt { + return func(opts *providerOpts) { + opts.group = group + } +} + +// NewProviderWithID creates and registers a new ETW provider, allowing the +// provider ID to be manually specified. This is most useful when there is an +// existing provider ID that must be used to conform to existing diagnostic +// infrastructure. +func NewProviderWithID(name string, id guid.GUID, callback EnableCallback) (provider *Provider, err error) { + return NewProviderWithOptions(name, WithID(id), WithCallback(callback)) +} + +// NewProvider creates and registers a new ETW provider. The provider ID is +// generated based on the provider name. +func NewProvider(name string, callback EnableCallback) (provider *Provider, err error) { + return NewProviderWithOptions(name, WithCallback(callback)) +} + +// Close unregisters the provider. +func (provider *Provider) Close() error { + if provider == nil { + return nil + } + + providers.removeProvider(provider) + return eventUnregister(provider.handle) +} + +// IsEnabled calls IsEnabledForLevelAndKeywords with LevelAlways and all +// keywords set. +func (provider *Provider) IsEnabled() bool { + return provider.IsEnabledForLevelAndKeywords(LevelAlways, ^uint64(0)) +} + +// IsEnabledForLevel calls IsEnabledForLevelAndKeywords with the specified level +// and all keywords set. +func (provider *Provider) IsEnabledForLevel(level Level) bool { + return provider.IsEnabledForLevelAndKeywords(level, ^uint64(0)) +} + +// IsEnabledForLevelAndKeywords allows event producer code to check if there are +// any event sessions that are interested in an event, based on the event level +// and keywords. Although this check happens automatically in the ETW +// infrastructure, it can be useful to check if an event will actually be +// consumed before doing expensive work to build the event data. +func (provider *Provider) IsEnabledForLevelAndKeywords(level Level, keywords uint64) bool { + if provider == nil { + return false + } + + if !provider.enabled { + return false + } + + // ETW automatically sets the level to 255 if it is specified as 0, so we + // don't need to worry about the level=0 (all events) case. + if level > provider.level { + return false + } + + if keywords != 0 && (keywords&provider.keywordAny == 0 || keywords&provider.keywordAll != provider.keywordAll) { + return false + } + + return true +} + +// WriteEvent writes a single ETW event from the provider. The event is +// constructed based on the EventOpt and FieldOpt values that are passed as +// opts. +func (provider *Provider) WriteEvent(name string, eventOpts []EventOpt, fieldOpts []FieldOpt) error { + if provider == nil { + return nil + } + + options := eventOptions{descriptor: newEventDescriptor()} + em := &eventMetadata{} + ed := &eventData{} + + // We need to evaluate the EventOpts first since they might change tags, and + // we write out the tags before evaluating FieldOpts. + for _, opt := range eventOpts { + opt(&options) + } + + if !provider.IsEnabledForLevelAndKeywords(options.descriptor.level, options.descriptor.keyword) { + return nil + } + + em.writeEventHeader(name, options.tags) + + for _, opt := range fieldOpts { + opt(em, ed) + } + + // Don't pass a data blob if there is no event data. There will always be + // event metadata (e.g. for the name) so we don't need to do this check for + // the metadata. + dataBlobs := [][]byte{} + if len(ed.bytes()) > 0 { + dataBlobs = [][]byte{ed.bytes()} + } + + return provider.writeEventRaw(options.descriptor, options.activityID, options.relatedActivityID, [][]byte{em.bytes()}, dataBlobs) +} + +// writeEventRaw writes a single ETW event from the provider. This function is +// less abstracted than WriteEvent, and presents a fairly direct interface to +// the event writing functionality. It expects a series of event metadata and +// event data blobs to be passed in, which must conform to the TraceLogging +// schema. The functions on EventMetadata and EventData can help with creating +// these blobs. The blobs of each type are effectively concatenated together by +// the ETW infrastructure. +func (provider *Provider) writeEventRaw( + descriptor *eventDescriptor, + activityID guid.GUID, + relatedActivityID guid.GUID, + metadataBlobs [][]byte, + dataBlobs [][]byte) error { + + dataDescriptorCount := uint32(1 + len(metadataBlobs) + len(dataBlobs)) + dataDescriptors := make([]eventDataDescriptor, 0, dataDescriptorCount) + + dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeProviderMetadata, provider.metadata)) + for _, blob := range metadataBlobs { + dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeEventMetadata, blob)) + } + for _, blob := range dataBlobs { + dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeUserData, blob)) + } + + return eventWriteTransfer(provider.handle, descriptor, (*windows.GUID)(&activityID), (*windows.GUID)(&relatedActivityID), dataDescriptorCount, &dataDescriptors[0]) +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/providerglobal.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/providerglobal.go new file mode 100644 index 0000000000000..ce3d305762ab5 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/providerglobal.go @@ -0,0 +1,54 @@ +// +build windows + +package etw + +import ( + "sync" +) + +// Because the provider callback function needs to be able to access the +// provider data when it is invoked by ETW, we need to keep provider data stored +// in a global map based on an index. The index is passed as the callback +// context to ETW. +type providerMap struct { + m map[uint]*Provider + i uint + lock sync.Mutex + once sync.Once +} + +var providers = providerMap{ + m: make(map[uint]*Provider), +} + +func (p *providerMap) newProvider() *Provider { + p.lock.Lock() + defer p.lock.Unlock() + + i := p.i + p.i++ + + provider := &Provider{ + index: i, + } + + p.m[i] = provider + return provider +} + +func (p *providerMap) removeProvider(provider *Provider) { + p.lock.Lock() + defer p.lock.Unlock() + + delete(p.m, provider.index) +} + +func (p *providerMap) getProvider(index uint) *Provider { + p.lock.Lock() + defer p.lock.Unlock() + + return p.m[index] +} + +var providerCallbackOnce sync.Once +var globalProviderCallback uintptr diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_32.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_32.go new file mode 100644 index 0000000000000..d1a76125d7e66 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_32.go @@ -0,0 +1,16 @@ +// +build 386 arm + +package etw + +import ( + "unsafe" +) + +// byteptr64 defines a struct containing a pointer. The struct is guaranteed to +// be 64 bits, regardless of the actual size of a pointer on the platform. This +// is intended for use with certain Windows APIs that expect a pointer as a +// ULONGLONG. +type ptr64 struct { + ptr unsafe.Pointer + _ uint32 +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_64.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_64.go new file mode 100644 index 0000000000000..b86c8f2bd8822 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/ptr64_64.go @@ -0,0 +1,15 @@ +// +build amd64 arm64 + +package etw + +import ( + "unsafe" +) + +// byteptr64 defines a struct containing a pointer. The struct is guaranteed to +// be 64 bits, regardless of the actual size of a pointer on the platform. This +// is intended for use with certain Windows APIs that expect a pointer as a +// ULONGLONG. +type ptr64 struct { + ptr unsafe.Pointer +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_32.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_32.go new file mode 100644 index 0000000000000..6867a1f87843e --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_32.go @@ -0,0 +1,67 @@ +// +build windows +// +build 386 arm + +package etw + +import ( + "github.com/Microsoft/go-winio/pkg/guid" + "golang.org/x/sys/windows" +) + +func low(v providerHandle) uint32 { + return uint32(v & 0xffffffff) +} + +func high(v providerHandle) uint32 { + return low(v >> 32) +} + +func eventUnregister(providerHandle providerHandle) (win32err error) { + return eventUnregister_32(low(providerHandle), high(providerHandle)) +} + +func eventWriteTransfer( + providerHandle providerHandle, + descriptor *eventDescriptor, + activityID *windows.GUID, + relatedActivityID *windows.GUID, + dataDescriptorCount uint32, + dataDescriptors *eventDataDescriptor) (win32err error) { + + return eventWriteTransfer_32( + low(providerHandle), + high(providerHandle), + descriptor, + activityID, + relatedActivityID, + dataDescriptorCount, + dataDescriptors) +} + +func eventSetInformation( + providerHandle providerHandle, + class eventInfoClass, + information uintptr, + length uint32) (win32err error) { + + return eventSetInformation_32( + low(providerHandle), + high(providerHandle), + class, + information, + length) +} + +// providerCallbackAdapter acts as the first-level callback from the C/ETW side +// for provider notifications. Because Go has trouble with callback arguments of +// different size, it has only pointer-sized arguments, which are then cast to +// the appropriate types when calling providerCallback. +// For x86, the matchAny and matchAll keywords need to be assembled from two +// 32-bit integers, because the max size of an argument is uintptr, but those +// two arguments are actually 64-bit integers. +func providerCallbackAdapter(sourceID *guid.GUID, state uint32, level uint32, matchAnyKeyword_low uint32, matchAnyKeyword_high uint32, matchAllKeyword_low uint32, matchAllKeyword_high uint32, filterData uintptr, i uintptr) uintptr { + matchAnyKeyword := uint64(matchAnyKeyword_high)<<32 | uint64(matchAnyKeyword_low) + matchAllKeyword := uint64(matchAllKeyword_high)<<32 | uint64(matchAllKeyword_low) + providerCallback(*sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i) + return 0 +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_64.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_64.go new file mode 100644 index 0000000000000..fe83df2bf0ec0 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/wrapper_64.go @@ -0,0 +1,52 @@ +// +build windows +// +build amd64 arm64 + +package etw + +import ( + "github.com/Microsoft/go-winio/pkg/guid" + "golang.org/x/sys/windows" +) + +func eventUnregister(providerHandle providerHandle) (win32err error) { + return eventUnregister_64(providerHandle) +} + +func eventWriteTransfer( + providerHandle providerHandle, + descriptor *eventDescriptor, + activityID *windows.GUID, + relatedActivityID *windows.GUID, + dataDescriptorCount uint32, + dataDescriptors *eventDataDescriptor) (win32err error) { + + return eventWriteTransfer_64( + providerHandle, + descriptor, + activityID, + relatedActivityID, + dataDescriptorCount, + dataDescriptors) +} + +func eventSetInformation( + providerHandle providerHandle, + class eventInfoClass, + information uintptr, + length uint32) (win32err error) { + + return eventSetInformation_64( + providerHandle, + class, + information, + length) +} + +// providerCallbackAdapter acts as the first-level callback from the C/ETW side +// for provider notifications. Because Go has trouble with callback arguments of +// different size, it has only pointer-sized arguments, which are then cast to +// the appropriate types when calling providerCallback. +func providerCallbackAdapter(sourceID *guid.GUID, state uintptr, level uintptr, matchAnyKeyword uintptr, matchAllKeyword uintptr, filterData uintptr, i uintptr) uintptr { + providerCallback(*sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i) + return 0 +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etw/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/etw/zsyscall_windows.go new file mode 100644 index 0000000000000..719b13d284293 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etw/zsyscall_windows.go @@ -0,0 +1,103 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package etw + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + + procEventRegister = modadvapi32.NewProc("EventRegister") + procEventSetInformation = modadvapi32.NewProc("EventSetInformation") + procEventUnregister = modadvapi32.NewProc("EventUnregister") + procEventWriteTransfer = modadvapi32.NewProc("EventWriteTransfer") +) + +func eventRegister(providerId *windows.GUID, callback uintptr, callbackContext uintptr, providerHandle *providerHandle) (win32err error) { + r0, _, _ := syscall.Syscall6(procEventRegister.Addr(), 4, uintptr(unsafe.Pointer(providerId)), uintptr(callback), uintptr(callbackContext), uintptr(unsafe.Pointer(providerHandle)), 0, 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func eventSetInformation_64(providerHandle providerHandle, class eventInfoClass, information uintptr, length uint32) (win32err error) { + r0, _, _ := syscall.Syscall6(procEventSetInformation.Addr(), 4, uintptr(providerHandle), uintptr(class), uintptr(information), uintptr(length), 0, 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func eventSetInformation_32(providerHandle_low uint32, providerHandle_high uint32, class eventInfoClass, information uintptr, length uint32) (win32err error) { + r0, _, _ := syscall.Syscall6(procEventSetInformation.Addr(), 5, uintptr(providerHandle_low), uintptr(providerHandle_high), uintptr(class), uintptr(information), uintptr(length), 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func eventUnregister_64(providerHandle providerHandle) (win32err error) { + r0, _, _ := syscall.Syscall(procEventUnregister.Addr(), 1, uintptr(providerHandle), 0, 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func eventUnregister_32(providerHandle_low uint32, providerHandle_high uint32) (win32err error) { + r0, _, _ := syscall.Syscall(procEventUnregister.Addr(), 2, uintptr(providerHandle_low), uintptr(providerHandle_high), 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func eventWriteTransfer_64(providerHandle providerHandle, descriptor *eventDescriptor, activityID *windows.GUID, relatedActivityID *windows.GUID, dataDescriptorCount uint32, dataDescriptors *eventDataDescriptor) (win32err error) { + r0, _, _ := syscall.Syscall6(procEventWriteTransfer.Addr(), 6, uintptr(providerHandle), uintptr(unsafe.Pointer(descriptor)), uintptr(unsafe.Pointer(activityID)), uintptr(unsafe.Pointer(relatedActivityID)), uintptr(dataDescriptorCount), uintptr(unsafe.Pointer(dataDescriptors))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func eventWriteTransfer_32(providerHandle_low uint32, providerHandle_high uint32, descriptor *eventDescriptor, activityID *windows.GUID, relatedActivityID *windows.GUID, dataDescriptorCount uint32, dataDescriptors *eventDataDescriptor) (win32err error) { + r0, _, _ := syscall.Syscall9(procEventWriteTransfer.Addr(), 7, uintptr(providerHandle_low), uintptr(providerHandle_high), uintptr(unsafe.Pointer(descriptor)), uintptr(unsafe.Pointer(activityID)), uintptr(unsafe.Pointer(relatedActivityID)), uintptr(dataDescriptorCount), uintptr(unsafe.Pointer(dataDescriptors)), 0, 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/etwlogrus/hook.go b/vendor/github.com/Microsoft/go-winio/pkg/etwlogrus/hook.go new file mode 100644 index 0000000000000..4332af56492dd --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/etwlogrus/hook.go @@ -0,0 +1,107 @@ +// +build windows + +package etwlogrus + +import ( + "sort" + + "github.com/Microsoft/go-winio/pkg/etw" + "github.com/sirupsen/logrus" +) + +// Hook is a Logrus hook which logs received events to ETW. +type Hook struct { + provider *etw.Provider + closeProvider bool +} + +// NewHook registers a new ETW provider and returns a hook to log from it. The +// provider will be closed when the hook is closed. +func NewHook(providerName string) (*Hook, error) { + provider, err := etw.NewProvider(providerName, nil) + if err != nil { + return nil, err + } + + return &Hook{provider, true}, nil +} + +// NewHookFromProvider creates a new hook based on an existing ETW provider. The +// provider will not be closed when the hook is closed. +func NewHookFromProvider(provider *etw.Provider) (*Hook, error) { + return &Hook{provider, false}, nil +} + +// Levels returns the set of levels that this hook wants to receive log entries +// for. +func (h *Hook) Levels() []logrus.Level { + return logrus.AllLevels +} + +var logrusToETWLevelMap = map[logrus.Level]etw.Level{ + logrus.PanicLevel: etw.LevelAlways, + logrus.FatalLevel: etw.LevelCritical, + logrus.ErrorLevel: etw.LevelError, + logrus.WarnLevel: etw.LevelWarning, + logrus.InfoLevel: etw.LevelInfo, + logrus.DebugLevel: etw.LevelVerbose, + logrus.TraceLevel: etw.LevelVerbose, +} + +// Fire receives each Logrus entry as it is logged, and logs it to ETW. +func (h *Hook) Fire(e *logrus.Entry) error { + // Logrus defines more levels than ETW typically uses, but analysis is + // easiest when using a consistent set of levels across ETW providers, so we + // map the Logrus levels to ETW levels. + level := logrusToETWLevelMap[e.Level] + if !h.provider.IsEnabledForLevel(level) { + return nil + } + + // Sort the fields by name so they are consistent in each instance + // of an event. Otherwise, the fields don't line up in WPA. + names := make([]string, 0, len(e.Data)) + hasError := false + for k := range e.Data { + if k == logrus.ErrorKey { + // Always put the error last because it is optional in some events. + hasError = true + } else { + names = append(names, k) + } + } + sort.Strings(names) + + // Reserve extra space for the message and time fields. + fields := make([]etw.FieldOpt, 0, len(e.Data)+2) + fields = append(fields, etw.StringField("Message", e.Message)) + fields = append(fields, etw.Time("Time", e.Time)) + for _, k := range names { + fields = append(fields, etw.SmartField(k, e.Data[k])) + } + if hasError { + fields = append(fields, etw.SmartField(logrus.ErrorKey, e.Data[logrus.ErrorKey])) + } + + // Firing an ETW event is essentially best effort, as the event write can + // fail for reasons completely out of the control of the event writer (such + // as a session listening for the event having no available space in its + // buffers). Therefore, we don't return the error from WriteEvent, as it is + // just noise in many cases. + h.provider.WriteEvent( + "LogrusEntry", + etw.WithEventOpts(etw.WithLevel(level)), + fields) + + return nil +} + +// Close cleans up the hook and closes the ETW provider. If the provder was +// registered by etwlogrus, it will be closed as part of `Close`. If the +// provider was passed in, it will not be closed. +func (h *Hook) Close() error { + if h.closeProvider { + return h.provider.Close() + } + return nil +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go new file mode 100644 index 0000000000000..f497c0e391786 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go @@ -0,0 +1,237 @@ +// +build windows + +// Package guid provides a GUID type. The backing structure for a GUID is +// identical to that used by the golang.org/x/sys/windows GUID type. +// There are two main binary encodings used for a GUID, the big-endian encoding, +// and the Windows (mixed-endian) encoding. See here for details: +// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding +package guid + +import ( + "crypto/rand" + "crypto/sha1" + "encoding" + "encoding/binary" + "fmt" + "strconv" + + "golang.org/x/sys/windows" +) + +// Variant specifies which GUID variant (or "type") of the GUID. It determines +// how the entirety of the rest of the GUID is interpreted. +type Variant uint8 + +// The variants specified by RFC 4122. +const ( + // VariantUnknown specifies a GUID variant which does not conform to one of + // the variant encodings specified in RFC 4122. + VariantUnknown Variant = iota + VariantNCS + VariantRFC4122 + VariantMicrosoft + VariantFuture +) + +// Version specifies how the bits in the GUID were generated. For instance, a +// version 4 GUID is randomly generated, and a version 5 is generated from the +// hash of an input string. +type Version uint8 + +var _ = (encoding.TextMarshaler)(GUID{}) +var _ = (encoding.TextUnmarshaler)(&GUID{}) + +// GUID represents a GUID/UUID. It has the same structure as +// golang.org/x/sys/windows.GUID so that it can be used with functions expecting +// that type. It is defined as its own type so that stringification and +// marshaling can be supported. The representation matches that used by native +// Windows code. +type GUID windows.GUID + +// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122. +func NewV4() (GUID, error) { + var b [16]byte + if _, err := rand.Read(b[:]); err != nil { + return GUID{}, err + } + + g := FromArray(b) + g.setVersion(4) // Version 4 means randomly generated. + g.setVariant(VariantRFC4122) + + return g, nil +} + +// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing) +// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name, +// and the sample code treats it as a series of bytes, so we do the same here. +// +// Some implementations, such as those found on Windows, treat the name as a +// big-endian UTF16 stream of bytes. If that is desired, the string can be +// encoded as such before being passed to this function. +func NewV5(namespace GUID, name []byte) (GUID, error) { + b := sha1.New() + namespaceBytes := namespace.ToArray() + b.Write(namespaceBytes[:]) + b.Write(name) + + a := [16]byte{} + copy(a[:], b.Sum(nil)) + + g := FromArray(a) + g.setVersion(5) // Version 5 means generated from a string. + g.setVariant(VariantRFC4122) + + return g, nil +} + +func fromArray(b [16]byte, order binary.ByteOrder) GUID { + var g GUID + g.Data1 = order.Uint32(b[0:4]) + g.Data2 = order.Uint16(b[4:6]) + g.Data3 = order.Uint16(b[6:8]) + copy(g.Data4[:], b[8:16]) + return g +} + +func (g GUID) toArray(order binary.ByteOrder) [16]byte { + b := [16]byte{} + order.PutUint32(b[0:4], g.Data1) + order.PutUint16(b[4:6], g.Data2) + order.PutUint16(b[6:8], g.Data3) + copy(b[8:16], g.Data4[:]) + return b +} + +// FromArray constructs a GUID from a big-endian encoding array of 16 bytes. +func FromArray(b [16]byte) GUID { + return fromArray(b, binary.BigEndian) +} + +// ToArray returns an array of 16 bytes representing the GUID in big-endian +// encoding. +func (g GUID) ToArray() [16]byte { + return g.toArray(binary.BigEndian) +} + +// FromWindowsArray constructs a GUID from a Windows encoding array of bytes. +func FromWindowsArray(b [16]byte) GUID { + return fromArray(b, binary.LittleEndian) +} + +// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows +// encoding. +func (g GUID) ToWindowsArray() [16]byte { + return g.toArray(binary.LittleEndian) +} + +func (g GUID) String() string { + return fmt.Sprintf( + "%08x-%04x-%04x-%04x-%012x", + g.Data1, + g.Data2, + g.Data3, + g.Data4[:2], + g.Data4[2:]) +} + +// FromString parses a string containing a GUID and returns the GUID. The only +// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` +// format. +func FromString(s string) (GUID, error) { + if len(s) != 36 { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + + var g GUID + + data1, err := strconv.ParseUint(s[0:8], 16, 32) + if err != nil { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + g.Data1 = uint32(data1) + + data2, err := strconv.ParseUint(s[9:13], 16, 16) + if err != nil { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + g.Data2 = uint16(data2) + + data3, err := strconv.ParseUint(s[14:18], 16, 16) + if err != nil { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + g.Data3 = uint16(data3) + + for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} { + v, err := strconv.ParseUint(s[x:x+2], 16, 8) + if err != nil { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + g.Data4[i] = uint8(v) + } + + return g, nil +} + +func (g *GUID) setVariant(v Variant) { + d := g.Data4[0] + switch v { + case VariantNCS: + d = (d & 0x7f) + case VariantRFC4122: + d = (d & 0x3f) | 0x80 + case VariantMicrosoft: + d = (d & 0x1f) | 0xc0 + case VariantFuture: + d = (d & 0x0f) | 0xe0 + case VariantUnknown: + fallthrough + default: + panic(fmt.Sprintf("invalid variant: %d", v)) + } + g.Data4[0] = d +} + +// Variant returns the GUID variant, as defined in RFC 4122. +func (g GUID) Variant() Variant { + b := g.Data4[0] + if b&0x80 == 0 { + return VariantNCS + } else if b&0xc0 == 0x80 { + return VariantRFC4122 + } else if b&0xe0 == 0xc0 { + return VariantMicrosoft + } else if b&0xe0 == 0xe0 { + return VariantFuture + } + return VariantUnknown +} + +func (g *GUID) setVersion(v Version) { + g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12) +} + +// Version returns the GUID version, as defined in RFC 4122. +func (g GUID) Version() Version { + return Version((g.Data3 & 0xF000) >> 12) +} + +// MarshalText returns the textual representation of the GUID. +func (g GUID) MarshalText() ([]byte, error) { + return []byte(g.String()), nil +} + +// UnmarshalText takes the textual representation of a GUID, and unmarhals it +// into this GUID. +func (g *GUID) UnmarshalText(text []byte) error { + g2, err := FromString(string(text)) + if err != nil { + return err + } + *g = g2 + return nil +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go b/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go new file mode 100644 index 0000000000000..fca241590ccac --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go @@ -0,0 +1,161 @@ +// +build windows + +package security + +import ( + "os" + "syscall" + "unsafe" + + "github.com/pkg/errors" +) + +type ( + accessMask uint32 + accessMode uint32 + desiredAccess uint32 + inheritMode uint32 + objectType uint32 + shareMode uint32 + securityInformation uint32 + trusteeForm uint32 + trusteeType uint32 + + explicitAccess struct { + accessPermissions accessMask + accessMode accessMode + inheritance inheritMode + trustee trustee + } + + trustee struct { + multipleTrustee *trustee + multipleTrusteeOperation int32 + trusteeForm trusteeForm + trusteeType trusteeType + name uintptr + } +) + +const ( + accessMaskDesiredPermission accessMask = 1 << 31 // GENERIC_READ + + accessModeGrant accessMode = 1 + + desiredAccessReadControl desiredAccess = 0x20000 + desiredAccessWriteDac desiredAccess = 0x40000 + + gvmga = "GrantVmGroupAccess:" + + inheritModeNoInheritance inheritMode = 0x0 + inheritModeSubContainersAndObjectsInherit inheritMode = 0x3 + + objectTypeFileObject objectType = 0x1 + + securityInformationDACL securityInformation = 0x4 + + shareModeRead shareMode = 0x1 + shareModeWrite shareMode = 0x2 + + sidVmGroup = "S-1-5-83-0" + + trusteeFormIsSid trusteeForm = 0 + + trusteeTypeWellKnownGroup trusteeType = 5 +) + +// GrantVMGroupAccess sets the DACL for a specified file or directory to +// include Grant ACE entries for the VM Group SID. This is a golang re- +// implementation of the same function in vmcompute, just not exported in +// RS5. Which kind of sucks. Sucks a lot :/ +func GrantVmGroupAccess(name string) error { + // Stat (to determine if `name` is a directory). + s, err := os.Stat(name) + if err != nil { + return errors.Wrapf(err, "%s os.Stat %s", gvmga, name) + } + + // Get a handle to the file/directory. Must defer Close on success. + fd, err := createFile(name, s.IsDir()) + if err != nil { + return err // Already wrapped + } + defer syscall.CloseHandle(fd) + + // Get the current DACL and Security Descriptor. Must defer LocalFree on success. + ot := objectTypeFileObject + si := securityInformationDACL + sd := uintptr(0) + origDACL := uintptr(0) + if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil { + return errors.Wrapf(err, "%s GetSecurityInfo %s", gvmga, name) + } + defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) + + // Generate a new DACL which is the current DACL with the required ACEs added. + // Must defer LocalFree on success. + newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), origDACL) + if err != nil { + return err // Already wrapped + } + defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL))) + + // And finally use SetSecurityInfo to apply the updated DACL. + if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil { + return errors.Wrapf(err, "%s SetSecurityInfo %s", gvmga, name) + } + + return nil +} + +// createFile is a helper function to call [Nt]CreateFile to get a handle to +// the file or directory. +func createFile(name string, isDir bool) (syscall.Handle, error) { + namep := syscall.StringToUTF16(name) + da := uint32(desiredAccessReadControl | desiredAccessWriteDac) + sm := uint32(shareModeRead | shareModeWrite) + fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL) + if isDir { + fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS) + } + fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0) + if err != nil { + return 0, errors.Wrapf(err, "%s syscall.CreateFile %s", gvmga, name) + } + return fd, nil +} + +// generateDACLWithAcesAdded generates a new DACL with the two needed ACEs added. +// The caller is responsible for LocalFree of the returned DACL on success. +func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) { + // Generate pointers to the SIDs based on the string SIDs + sid, err := syscall.StringToSid(sidVmGroup) + if err != nil { + return 0, errors.Wrapf(err, "%s syscall.StringToSid %s %s", gvmga, name, sidVmGroup) + } + + inheritance := inheritModeNoInheritance + if isDir { + inheritance = inheritModeSubContainersAndObjectsInherit + } + + eaArray := []explicitAccess{ + explicitAccess{ + accessPermissions: accessMaskDesiredPermission, + accessMode: accessModeGrant, + inheritance: inheritance, + trustee: trustee{ + trusteeForm: trusteeFormIsSid, + trusteeType: trusteeTypeWellKnownGroup, + name: uintptr(unsafe.Pointer(sid)), + }, + }, + } + + modifiedDACL := uintptr(0) + if err := setEntriesInAcl(uintptr(uint32(1)), uintptr(unsafe.Pointer(&eaArray[0])), origDACL, &modifiedDACL); err != nil { + return 0, errors.Wrapf(err, "%s SetEntriesInAcl %s", gvmga, name) + } + + return modifiedDACL, nil +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go new file mode 100644 index 0000000000000..d7096716ce2cf --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/security/syscall_windows.go @@ -0,0 +1,7 @@ +package security + +//go:generate go run mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go + +//sys getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (win32err error) = advapi32.GetSecurityInfo +//sys setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (win32err error) = advapi32.SetSecurityInfo +//sys setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (win32err error) = advapi32.SetEntriesInAclW diff --git a/vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go new file mode 100644 index 0000000000000..4084680e0f0a0 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/security/zsyscall_windows.go @@ -0,0 +1,70 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package security + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + + procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo") + procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW") + procSetSecurityInfo = modadvapi32.NewProc("SetSecurityInfo") +) + +func getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (win32err error) { + r0, _, _ := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(ppsidOwner)), uintptr(unsafe.Pointer(ppsidGroup)), uintptr(unsafe.Pointer(ppDacl)), uintptr(unsafe.Pointer(ppSacl)), uintptr(unsafe.Pointer(ppSecurityDescriptor)), 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (win32err error) { + r0, _, _ := syscall.Syscall6(procSetEntriesInAclW.Addr(), 4, uintptr(count), uintptr(pListOfEEs), uintptr(oldAcl), uintptr(unsafe.Pointer(newAcl)), 0, 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (win32err error) { + r0, _, _ := syscall.Syscall9(procSetSecurityInfo.Addr(), 7, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(psidOwner), uintptr(psidGroup), uintptr(pDacl), uintptr(pSacl), 0, 0) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/go-winio/privilege.go b/vendor/github.com/Microsoft/go-winio/privilege.go index 9c83d36fe533c..c3dd7c2176971 100644 --- a/vendor/github.com/Microsoft/go-winio/privilege.go +++ b/vendor/github.com/Microsoft/go-winio/privilege.go @@ -28,8 +28,9 @@ const ( ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300 - SeBackupPrivilege = "SeBackupPrivilege" - SeRestorePrivilege = "SeRestorePrivilege" + SeBackupPrivilege = "SeBackupPrivilege" + SeRestorePrivilege = "SeRestorePrivilege" + SeSecurityPrivilege = "SeSecurityPrivilege" ) const ( diff --git a/vendor/github.com/Microsoft/go-winio/syscall.go b/vendor/github.com/Microsoft/go-winio/syscall.go index 20d64cf41d0e7..5955c99fdeafe 100644 --- a/vendor/github.com/Microsoft/go-winio/syscall.go +++ b/vendor/github.com/Microsoft/go-winio/syscall.go @@ -1,3 +1,3 @@ package winio -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go +//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go hvsock.go diff --git a/vendor/github.com/Microsoft/go-winio/vhd/vhd.go b/vendor/github.com/Microsoft/go-winio/vhd/vhd.go index 8fa90e917ce6f..a33a36c0ffba4 100644 --- a/vendor/github.com/Microsoft/go-winio/vhd/vhd.go +++ b/vendor/github.com/Microsoft/go-winio/vhd/vhd.go @@ -2,107 +2,322 @@ package vhd -import "syscall" +import ( + "fmt" + "syscall" -//go:generate go run mksyscall_windows.go -output zvhd.go vhd.go + "github.com/Microsoft/go-winio/pkg/guid" + "github.com/pkg/errors" + "golang.org/x/sys/windows" +) -//sys createVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) [failretval != 0] = VirtDisk.CreateVirtualDisk -//sys openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *uintptr, handle *syscall.Handle) (err error) [failretval != 0] = VirtDisk.OpenVirtualDisk -//sys detachVirtualDisk(handle syscall.Handle, flags uint32, providerSpecificFlags uint32) (err error) [failretval != 0] = VirtDisk.DetachVirtualDisk +//go:generate go run mksyscall_windows.go -output zvhd_windows.go vhd.go -type virtualStorageType struct { +//sys createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) = virtdisk.CreateVirtualDisk +//sys openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (win32err error) = virtdisk.OpenVirtualDisk +//sys attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) = virtdisk.AttachVirtualDisk +//sys detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (win32err error) = virtdisk.DetachVirtualDisk +//sys getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) = virtdisk.GetVirtualDiskPhysicalPath + +type ( + CreateVirtualDiskFlag uint32 + VirtualDiskFlag uint32 + AttachVirtualDiskFlag uint32 + DetachVirtualDiskFlag uint32 + VirtualDiskAccessMask uint32 +) + +type VirtualStorageType struct { DeviceID uint32 - VendorID [16]byte -} - -const virtualDiskAccessNONE uint32 = 0 -const virtualDiskAccessATTACHRO uint32 = 65536 -const virtualDiskAccessATTACHRW uint32 = 131072 -const virtualDiskAccessDETACH uint32 = 262144 -const virtualDiskAccessGETINFO uint32 = 524288 -const virtualDiskAccessCREATE uint32 = 1048576 -const virtualDiskAccessMETAOPS uint32 = 2097152 -const virtualDiskAccessREAD uint32 = 851968 -const virtualDiskAccessALL uint32 = 4128768 -const virtualDiskAccessWRITABLE uint32 = 3276800 - -const createVirtualDiskFlagNone uint32 = 0 -const createVirtualDiskFlagFullPhysicalAllocation uint32 = 1 -const createVirtualDiskFlagPreventWritesToSourceDisk uint32 = 2 -const createVirtualDiskFlagDoNotCopyMetadataFromParent uint32 = 4 - -type version2 struct { - UniqueID [16]byte // GUID + VendorID guid.GUID +} + +type CreateVersion2 struct { + UniqueID guid.GUID MaximumSize uint64 BlockSizeInBytes uint32 SectorSizeInBytes uint32 + PhysicalSectorSizeInByte uint32 ParentPath *uint16 // string SourcePath *uint16 // string OpenFlags uint32 - ParentVirtualStorageType virtualStorageType - SourceVirtualStorageType virtualStorageType - ResiliencyGUID [16]byte // GUID + ParentVirtualStorageType VirtualStorageType + SourceVirtualStorageType VirtualStorageType + ResiliencyGUID guid.GUID } -type createVirtualDiskParameters struct { +type CreateVirtualDiskParameters struct { Version uint32 // Must always be set to 2 - Version2 version2 + Version2 CreateVersion2 } -// CreateVhdx will create a simple vhdx file at the given path using default values. -func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error { - var defaultType virtualStorageType +type OpenVersion2 struct { + GetInfoOnly bool + ReadOnly bool + ResiliencyGUID guid.GUID +} + +type OpenVirtualDiskParameters struct { + Version uint32 // Must always be set to 2 + Version2 OpenVersion2 +} + +type AttachVersion2 struct { + RestrictedOffset uint64 + RestrictedLength uint64 +} - parameters := createVirtualDiskParameters{ +type AttachVirtualDiskParameters struct { + Version uint32 // Must always be set to 2 + Version2 AttachVersion2 +} + +const ( + VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 0x3 + + // Access Mask for opening a VHD + VirtualDiskAccessNone VirtualDiskAccessMask = 0x00000000 + VirtualDiskAccessAttachRO VirtualDiskAccessMask = 0x00010000 + VirtualDiskAccessAttachRW VirtualDiskAccessMask = 0x00020000 + VirtualDiskAccessDetach VirtualDiskAccessMask = 0x00040000 + VirtualDiskAccessGetInfo VirtualDiskAccessMask = 0x00080000 + VirtualDiskAccessCreate VirtualDiskAccessMask = 0x00100000 + VirtualDiskAccessMetaOps VirtualDiskAccessMask = 0x00200000 + VirtualDiskAccessRead VirtualDiskAccessMask = 0x000d0000 + VirtualDiskAccessAll VirtualDiskAccessMask = 0x003f0000 + VirtualDiskAccessWritable VirtualDiskAccessMask = 0x00320000 + + // Flags for creating a VHD + CreateVirtualDiskFlagNone CreateVirtualDiskFlag = 0x0 + CreateVirtualDiskFlagFullPhysicalAllocation CreateVirtualDiskFlag = 0x1 + CreateVirtualDiskFlagPreventWritesToSourceDisk CreateVirtualDiskFlag = 0x2 + CreateVirtualDiskFlagDoNotCopyMetadataFromParent CreateVirtualDiskFlag = 0x4 + CreateVirtualDiskFlagCreateBackingStorage CreateVirtualDiskFlag = 0x8 + CreateVirtualDiskFlagUseChangeTrackingSourceLimit CreateVirtualDiskFlag = 0x10 + CreateVirtualDiskFlagPreserveParentChangeTrackingState CreateVirtualDiskFlag = 0x20 + CreateVirtualDiskFlagVhdSetUseOriginalBackingStorage CreateVirtualDiskFlag = 0x40 + CreateVirtualDiskFlagSparseFile CreateVirtualDiskFlag = 0x80 + CreateVirtualDiskFlagPmemCompatible CreateVirtualDiskFlag = 0x100 + CreateVirtualDiskFlagSupportCompressedVolumes CreateVirtualDiskFlag = 0x200 + + // Flags for opening a VHD + OpenVirtualDiskFlagNone VirtualDiskFlag = 0x00000000 + OpenVirtualDiskFlagNoParents VirtualDiskFlag = 0x00000001 + OpenVirtualDiskFlagBlankFile VirtualDiskFlag = 0x00000002 + OpenVirtualDiskFlagBootDrive VirtualDiskFlag = 0x00000004 + OpenVirtualDiskFlagCachedIO VirtualDiskFlag = 0x00000008 + OpenVirtualDiskFlagCustomDiffChain VirtualDiskFlag = 0x00000010 + OpenVirtualDiskFlagParentCachedIO VirtualDiskFlag = 0x00000020 + OpenVirtualDiskFlagVhdsetFileOnly VirtualDiskFlag = 0x00000040 + OpenVirtualDiskFlagIgnoreRelativeParentLocator VirtualDiskFlag = 0x00000080 + OpenVirtualDiskFlagNoWriteHardening VirtualDiskFlag = 0x00000100 + OpenVirtualDiskFlagSupportCompressedVolumes VirtualDiskFlag = 0x00000200 + + // Flags for attaching a VHD + AttachVirtualDiskFlagNone AttachVirtualDiskFlag = 0x00000000 + AttachVirtualDiskFlagReadOnly AttachVirtualDiskFlag = 0x00000001 + AttachVirtualDiskFlagNoDriveLetter AttachVirtualDiskFlag = 0x00000002 + AttachVirtualDiskFlagPermanentLifetime AttachVirtualDiskFlag = 0x00000004 + AttachVirtualDiskFlagNoLocalHost AttachVirtualDiskFlag = 0x00000008 + AttachVirtualDiskFlagNoSecurityDescriptor AttachVirtualDiskFlag = 0x00000010 + AttachVirtualDiskFlagBypassDefaultEncryptionPolicy AttachVirtualDiskFlag = 0x00000020 + AttachVirtualDiskFlagNonPnp AttachVirtualDiskFlag = 0x00000040 + AttachVirtualDiskFlagRestrictedRange AttachVirtualDiskFlag = 0x00000080 + AttachVirtualDiskFlagSinglePartition AttachVirtualDiskFlag = 0x00000100 + AttachVirtualDiskFlagRegisterVolume AttachVirtualDiskFlag = 0x00000200 + + // Flags for detaching a VHD + DetachVirtualDiskFlagNone DetachVirtualDiskFlag = 0x0 +) + +// CreateVhdx is a helper function to create a simple vhdx file at the given path using +// default values. +func CreateVhdx(path string, maxSizeInGb, blockSizeInMb uint32) error { + params := CreateVirtualDiskParameters{ Version: 2, - Version2: version2{ + Version2: CreateVersion2{ MaximumSize: uint64(maxSizeInGb) * 1024 * 1024 * 1024, BlockSizeInBytes: blockSizeInMb * 1024 * 1024, }, } - var handle syscall.Handle + handle, err := CreateVirtualDisk(path, VirtualDiskAccessNone, CreateVirtualDiskFlagNone, ¶ms) + if err != nil { + return err + } + + if err := syscall.CloseHandle(handle); err != nil { + return err + } + return nil +} - if err := createVirtualDisk( - &defaultType, +// DetachVirtualDisk detaches a virtual hard disk by handle. +func DetachVirtualDisk(handle syscall.Handle) (err error) { + if err := detachVirtualDisk(handle, 0, 0); err != nil { + return errors.Wrap(err, "failed to detach virtual disk") + } + return nil +} + +// DetachVhd detaches a vhd found at `path`. +func DetachVhd(path string) error { + handle, err := OpenVirtualDisk( path, - virtualDiskAccessNONE, + VirtualDiskAccessNone, + OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator, + ) + if err != nil { + return err + } + defer syscall.CloseHandle(handle) + return DetachVirtualDisk(handle) +} + +// AttachVirtualDisk attaches a virtual hard disk for use. +func AttachVirtualDisk(handle syscall.Handle, attachVirtualDiskFlag AttachVirtualDiskFlag, parameters *AttachVirtualDiskParameters) (err error) { + // Supports both version 1 and 2 of the attach parameters as version 2 wasn't present in RS5. + if err := attachVirtualDisk( + handle, nil, - createVirtualDiskFlagNone, + uint32(attachVirtualDiskFlag), 0, - ¶meters, + parameters, nil, - &handle); err != nil { - return err + ); err != nil { + return errors.Wrap(err, "failed to attach virtual disk") } + return nil +} - if err := syscall.CloseHandle(handle); err != nil { +// AttachVhd attaches a virtual hard disk at `path` for use. Attaches using version 2 +// of the ATTACH_VIRTUAL_DISK_PARAMETERS. +func AttachVhd(path string) (err error) { + handle, err := OpenVirtualDisk( + path, + VirtualDiskAccessNone, + OpenVirtualDiskFlagCachedIO|OpenVirtualDiskFlagIgnoreRelativeParentLocator, + ) + if err != nil { return err } + defer syscall.CloseHandle(handle) + params := AttachVirtualDiskParameters{Version: 2} + if err := AttachVirtualDisk( + handle, + AttachVirtualDiskFlagNone, + ¶ms, + ); err != nil { + return errors.Wrap(err, "failed to attach virtual disk") + } return nil } -// DetachVhd detaches a VHD attached at the given path. -func DetachVhd(path string) error { +// OpenVirtualDisk obtains a handle to a VHD opened with supplied access mask and flags. +func OpenVirtualDisk(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag) (syscall.Handle, error) { + parameters := OpenVirtualDiskParameters{Version: 2} + handle, err := OpenVirtualDiskWithParameters( + vhdPath, + virtualDiskAccessMask, + openVirtualDiskFlags, + ¶meters, + ) + if err != nil { + return 0, err + } + return handle, nil +} + +// OpenVirtualDiskWithParameters obtains a handle to a VHD opened with supplied access mask, flags and parameters. +func OpenVirtualDiskWithParameters(vhdPath string, virtualDiskAccessMask VirtualDiskAccessMask, openVirtualDiskFlags VirtualDiskFlag, parameters *OpenVirtualDiskParameters) (syscall.Handle, error) { var ( - defaultType virtualStorageType handle syscall.Handle + defaultType VirtualStorageType ) - + if parameters.Version != 2 { + return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version) + } if err := openVirtualDisk( + &defaultType, + vhdPath, + uint32(virtualDiskAccessMask), + uint32(openVirtualDiskFlags), + parameters, + &handle, + ); err != nil { + return 0, errors.Wrap(err, "failed to open virtual disk") + } + return handle, nil +} + +// CreateVirtualDisk creates a virtual harddisk and returns a handle to the disk. +func CreateVirtualDisk(path string, virtualDiskAccessMask VirtualDiskAccessMask, createVirtualDiskFlags CreateVirtualDiskFlag, parameters *CreateVirtualDiskParameters) (syscall.Handle, error) { + var ( + handle syscall.Handle + defaultType VirtualStorageType + ) + if parameters.Version != 2 { + return handle, fmt.Errorf("only version 2 VHDs are supported, found version: %d", parameters.Version) + } + + if err := createVirtualDisk( &defaultType, path, - virtualDiskAccessDETACH, + uint32(virtualDiskAccessMask), + nil, + uint32(createVirtualDiskFlags), 0, + parameters, nil, - &handle); err != nil { - return err + &handle, + ); err != nil { + return handle, errors.Wrap(err, "failed to create virtual disk") } - defer syscall.CloseHandle(handle) + return handle, nil +} - if err := detachVirtualDisk(handle, 0, 0); err != nil { - return err +// GetVirtualDiskPhysicalPath takes a handle to a virtual hard disk and returns the physical +// path of the disk on the machine. This path is in the form \\.\PhysicalDriveX where X is an integer +// that represents the particular enumeration of the physical disk on the caller's system. +func GetVirtualDiskPhysicalPath(handle syscall.Handle) (_ string, err error) { + var ( + diskPathSizeInBytes uint32 = 256 * 2 // max path length 256 wide chars + diskPhysicalPathBuf [256]uint16 + ) + if err := getVirtualDiskPhysicalPath( + handle, + &diskPathSizeInBytes, + &diskPhysicalPathBuf[0], + ); err != nil { + return "", errors.Wrap(err, "failed to get disk physical path") + } + return windows.UTF16ToString(diskPhysicalPathBuf[:]), nil +} + +// CreateDiffVhd is a helper function to create a differencing virtual disk. +func CreateDiffVhd(diffVhdPath, baseVhdPath string, blockSizeInMB uint32) error { + // Setting `ParentPath` is how to signal to create a differencing disk. + createParams := &CreateVirtualDiskParameters{ + Version: 2, + Version2: CreateVersion2{ + ParentPath: windows.StringToUTF16Ptr(baseVhdPath), + BlockSizeInBytes: blockSizeInMB * 1024 * 1024, + OpenFlags: uint32(OpenVirtualDiskFlagCachedIO), + }, + } + + vhdHandle, err := CreateVirtualDisk( + diffVhdPath, + VirtualDiskAccessNone, + CreateVirtualDiskFlagNone, + createParams, + ) + if err != nil { + return fmt.Errorf("failed to create differencing vhd: %s", err) + } + if err := syscall.CloseHandle(vhdHandle); err != nil { + return fmt.Errorf("failed to close differencing vhd handle: %s", err) } return nil } diff --git a/vendor/github.com/Microsoft/go-winio/vhd/zvhd.go b/vendor/github.com/Microsoft/go-winio/vhd/zvhd.go deleted file mode 100644 index 73f52596eda18..0000000000000 --- a/vendor/github.com/Microsoft/go-winio/vhd/zvhd.go +++ /dev/null @@ -1,99 +0,0 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT - -package vhd - -import ( - "syscall" - "unsafe" - - "golang.org/x/sys/windows" -) - -var _ unsafe.Pointer - -// Do the interface allocations only once for common -// Errno values. -const ( - errnoERROR_IO_PENDING = 997 -) - -var ( - errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) -) - -// errnoErr returns common boxed Errno values, to prevent -// allocations at runtime. -func errnoErr(e syscall.Errno) error { - switch e { - case 0: - return nil - case errnoERROR_IO_PENDING: - return errERROR_IO_PENDING - } - // TODO: add more here, after collecting data on the common - // error values see on Windows. (perhaps when running - // all.bat?) - return e -} - -var ( - modVirtDisk = windows.NewLazySystemDLL("VirtDisk.dll") - - procCreateVirtualDisk = modVirtDisk.NewProc("CreateVirtualDisk") - procOpenVirtualDisk = modVirtDisk.NewProc("OpenVirtualDisk") - procDetachVirtualDisk = modVirtDisk.NewProc("DetachVirtualDisk") -) - -func createVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) { - var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(path) - if err != nil { - return - } - return _createVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, securityDescriptor, flags, providerSpecificFlags, parameters, o, handle) -} - -func _createVirtualDisk(virtualStorageType *virtualStorageType, path *uint16, virtualDiskAccessMask uint32, securityDescriptor *uintptr, flags uint32, providerSpecificFlags uint32, parameters *createVirtualDiskParameters, o *syscall.Overlapped, handle *syscall.Handle) (err error) { - r1, _, e1 := syscall.Syscall9(procCreateVirtualDisk.Addr(), 9, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(flags), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(handle))) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *uintptr, handle *syscall.Handle) (err error) { - var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(path) - if err != nil { - return - } - return _openVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, flags, parameters, handle) -} - -func _openVirtualDisk(virtualStorageType *virtualStorageType, path *uint16, virtualDiskAccessMask uint32, flags uint32, parameters *uintptr, handle *syscall.Handle) (err error) { - r1, _, e1 := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(flags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle))) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func detachVirtualDisk(handle syscall.Handle, flags uint32, providerSpecificFlags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procDetachVirtualDisk.Addr(), 3, uintptr(handle), uintptr(flags), uintptr(providerSpecificFlags)) - if r1 != 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} diff --git a/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go b/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go new file mode 100644 index 0000000000000..7fb5f3651b955 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/vhd/zvhd_windows.go @@ -0,0 +1,106 @@ +// Code generated by 'go generate'; DO NOT EDIT. + +package vhd + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvirtdisk = windows.NewLazySystemDLL("virtdisk.dll") + + procAttachVirtualDisk = modvirtdisk.NewProc("AttachVirtualDisk") + procCreateVirtualDisk = modvirtdisk.NewProc("CreateVirtualDisk") + procDetachVirtualDisk = modvirtdisk.NewProc("DetachVirtualDisk") + procGetVirtualDiskPhysicalPath = modvirtdisk.NewProc("GetVirtualDiskPhysicalPath") + procOpenVirtualDisk = modvirtdisk.NewProc("OpenVirtualDisk") +) + +func attachVirtualDisk(handle syscall.Handle, securityDescriptor *uintptr, attachVirtualDiskFlag uint32, providerSpecificFlags uint32, parameters *AttachVirtualDiskParameters, overlapped *syscall.Overlapped) (win32err error) { + r0, _, _ := syscall.Syscall6(procAttachVirtualDisk.Addr(), 6, uintptr(handle), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(attachVirtualDiskFlag), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func createVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) { + var _p0 *uint16 + _p0, win32err = syscall.UTF16PtrFromString(path) + if win32err != nil { + return + } + return _createVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, securityDescriptor, createVirtualDiskFlags, providerSpecificFlags, parameters, overlapped, handle) +} + +func _createVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, securityDescriptor *uintptr, createVirtualDiskFlags uint32, providerSpecificFlags uint32, parameters *CreateVirtualDiskParameters, overlapped *syscall.Overlapped, handle *syscall.Handle) (win32err error) { + r0, _, _ := syscall.Syscall9(procCreateVirtualDisk.Addr(), 9, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(unsafe.Pointer(securityDescriptor)), uintptr(createVirtualDiskFlags), uintptr(providerSpecificFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(handle))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func detachVirtualDisk(handle syscall.Handle, detachVirtualDiskFlags uint32, providerSpecificFlags uint32) (win32err error) { + r0, _, _ := syscall.Syscall(procDetachVirtualDisk.Addr(), 3, uintptr(handle), uintptr(detachVirtualDiskFlags), uintptr(providerSpecificFlags)) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func getVirtualDiskPhysicalPath(handle syscall.Handle, diskPathSizeInBytes *uint32, buffer *uint16) (win32err error) { + r0, _, _ := syscall.Syscall(procGetVirtualDiskPhysicalPath.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(diskPathSizeInBytes)), uintptr(unsafe.Pointer(buffer))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} + +func openVirtualDisk(virtualStorageType *VirtualStorageType, path string, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (win32err error) { + var _p0 *uint16 + _p0, win32err = syscall.UTF16PtrFromString(path) + if win32err != nil { + return + } + return _openVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, openVirtualDiskFlags, parameters, handle) +} + +func _openVirtualDisk(virtualStorageType *VirtualStorageType, path *uint16, virtualDiskAccessMask uint32, openVirtualDiskFlags uint32, parameters *OpenVirtualDiskParameters, handle *syscall.Handle) (win32err error) { + r0, _, _ := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(openVirtualDiskFlags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle))) + if r0 != 0 { + win32err = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go index 3f527639a47f6..176ff75e320cf 100644 --- a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated by 'go generate'; DO NOT EDIT. package winio @@ -19,6 +19,7 @@ const ( var ( errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL ) // errnoErr returns common boxed Errno values, to prevent @@ -26,7 +27,7 @@ var ( func errnoErr(e syscall.Errno) error { switch e { case 0: - return nil + return errERROR_EINVAL case errnoERROR_IO_PENDING: return errERROR_IO_PENDING } @@ -37,484 +38,390 @@ func errnoErr(e syscall.Errno) error { } var ( - modkernel32 = windows.NewLazySystemDLL("kernel32.dll") modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + modntdll = windows.NewLazySystemDLL("ntdll.dll") + modws2_32 = windows.NewLazySystemDLL("ws2_32.dll") - procCancelIoEx = modkernel32.NewProc("CancelIoEx") - procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") - procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") - procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") - procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") - procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") - procCreateFileW = modkernel32.NewProc("CreateFileW") - procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") - procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") - procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") - procLocalAlloc = modkernel32.NewProc("LocalAlloc") - procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") + procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") + procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") - procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") - procLocalFree = modkernel32.NewProc("LocalFree") procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") - procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") - procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle") - procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") - procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") - procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") - procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW") + procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") + procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW") + procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") + procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") + procRevertToSelf = modadvapi32.NewProc("RevertToSelf") procBackupRead = modkernel32.NewProc("BackupRead") procBackupWrite = modkernel32.NewProc("BackupWrite") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") + procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") + procCreateFileW = modkernel32.NewProc("CreateFileW") + procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") + procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") + procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") + procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") + procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") + procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") + procLocalAlloc = modkernel32.NewProc("LocalAlloc") + procLocalFree = modkernel32.NewProc("LocalFree") + procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") + procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile") + procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl") + procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U") + procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") + procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult") + procbind = modws2_32.NewProc("bind") ) -func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { + var _p0 uint32 + if releaseAll { + _p0 = 1 + } + r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize))) + success = r0 != 0 + if true { + err = errnoErr(e1) } return } -func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) { - r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0) - newport = syscall.Handle(r0) - if newport == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0) + if r1 == 0 { + err = errnoErr(e1) } return } -func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0) +func convertSidToStringSid(sid *byte, str **uint16) (err error) { + r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) { - r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0) +func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(str) + if err != nil { + return + } + return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size) +} + +func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0) +func getSecurityDescriptorLength(sd uintptr) (len uint32) { + r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) + len = uint32(r0) + return +} + +func impersonateSelf(level uint32) (err error) { + r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { +func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(name) + _p0, err = syscall.UTF16PtrFromString(accountName) if err != nil { return } - return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa) + return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse) } -func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { - r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { + r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) + if r1 == 0 { + err = errnoErr(e1) } return } -func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { +func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(name) + _p0, err = syscall.UTF16PtrFromString(systemName) if err != nil { return } - return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile) + return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId) } -func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { - r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0) + if r1 == 0 { + err = errnoErr(e1) } return } -func waitNamedPipe(name string, timeout uint32) (err error) { +func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) { var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(name) + _p0, err = syscall.UTF16PtrFromString(systemName) if err != nil { return } - return _waitNamedPipe(_p0, timeout) + return _lookupPrivilegeName(_p0, luid, buffer, size) } -func _waitNamedPipe(name *uint16, timeout uint32) (err error) { - r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) +func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(systemName) + if err != nil { + return } - return + var _p1 *uint16 + _p1, err = syscall.UTF16PtrFromString(name) + if err != nil { + return + } + return _lookupPrivilegeValue(_p0, _p1, luid) } -func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) +func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) { + r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func localAlloc(uFlags uint32, length uint32) (ptr uintptr) { - r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0) - ptr = uintptr(r0) +func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) { + var _p0 uint32 + if openAsSelf { + _p0 = 1 + } + r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } return } -func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { - var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(accountName) - if err != nil { - return +func revertToSelf() (err error) { + r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) + if r1 == 0 { + err = errnoErr(e1) } - return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse) + return } -func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { - r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) +func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { + var _p0 *byte + if len(b) > 0 { + _p0 = &b[0] + } + var _p1 uint32 + if abort { + _p1 = 1 + } + var _p2 uint32 + if processSecurity { + _p2 = 1 + } + r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func convertSidToStringSid(sid *byte, str **uint16) (err error) { - r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0) +func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { + var _p0 *byte + if len(b) > 0 { + _p0 = &b[0] + } + var _p1 uint32 + if abort { + _p1 = 1 + } + var _p2 uint32 + if processSecurity { + _p2 = 1 + } + r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) { - var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(str) - if err != nil { - return +func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0) + if r1 == 0 { + err = errnoErr(e1) } - return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size) + return } -func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) +func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) { + r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(name) + if err != nil { + return } - return + return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile) } -func localFree(mem uintptr) { - syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0) +func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + err = errnoErr(e1) + } return } -func getSecurityDescriptorLength(sd uintptr) (len uint32) { - r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) - len = uint32(r0) +func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0) + newport = syscall.Handle(r0) + if newport == 0 { + err = errnoErr(e1) + } return } -func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(name) + if err != nil { + return } - return + return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa) } -func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) + handle = syscall.Handle(r0) + if handle == syscall.InvalidHandle { + err = errnoErr(e1) } return } -func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { - var _p0 uint32 - if releaseAll { - _p0 = 1 - } else { - _p0 = 0 - } - r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize))) - success = r0 != 0 - if true { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } +func getCurrentThread() (h syscall.Handle) { + r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) + h = syscall.Handle(r0) return } -func impersonateSelf(level uint32) (err error) { - r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0) +func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { + r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func revertToSelf() (err error) { - r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) +func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) { - var _p0 uint32 - if openAsSelf { - _p0 = 1 - } else { - _p0 = 0 - } - r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) +func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func getCurrentThread() (h syscall.Handle) { - r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) - h = syscall.Handle(r0) +func localAlloc(uFlags uint32, length uint32) (ptr uintptr) { + r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0) + ptr = uintptr(r0) return } -func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) { - var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(systemName) - if err != nil { - return - } - var _p1 *uint16 - _p1, err = syscall.UTF16PtrFromString(name) - if err != nil { - return - } - return _lookupPrivilegeValue(_p0, _p1, luid) +func localFree(mem uintptr) { + syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0) + return } -func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) { - r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) +func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) { + r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) { - var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(systemName) - if err != nil { - return - } - return _lookupPrivilegeName(_p0, luid, buffer, size) +func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntstatus) { + r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0) + status = ntstatus(r0) + return } -func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } +func rtlDefaultNpAcl(dacl *uintptr) (status ntstatus) { + r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0) + status = ntstatus(r0) return } -func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { - var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(systemName) - if err != nil { - return - } - return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId) +func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntstatus) { + r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0) + status = ntstatus(r0) + return } -func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func rtlNtStatusToDosError(status ntstatus) (winerr error) { + r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) + if r0 != 0 { + winerr = syscall.Errno(r0) } return } -func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { - var _p0 *byte - if len(b) > 0 { - _p0 = &b[0] - } - var _p1 uint32 - if abort { - _p1 = 1 - } else { - _p1 = 0 - } - var _p2 uint32 - if processSecurity { - _p2 = 1 - } else { - _p2 = 0 +func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) { + var _p0 uint32 + if wait { + _p0 = 1 } - r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) + r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { - var _p0 *byte - if len(b) > 0 { - _p0 = &b[0] - } - var _p1 uint32 - if abort { - _p1 = 1 - } else { - _p1 = 0 - } - var _p2 uint32 - if processSecurity { - _p2 = 1 - } else { - _p2 = 0 - } - r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) { + r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) + if r1 == socketError { + err = errnoErr(e1) } return } diff --git a/vendor/github.com/Microsoft/hcsshim/README.md b/vendor/github.com/Microsoft/hcsshim/README.md index 15b39181a5dff..95c300365623d 100644 --- a/vendor/github.com/Microsoft/hcsshim/README.md +++ b/vendor/github.com/Microsoft/hcsshim/README.md @@ -1,8 +1,8 @@ # hcsshim -[![Build status](https://ci.appveyor.com/api/projects/status/nbcw28mnkqml0loa/branch/master?svg=true)](https://ci.appveyor.com/project/WindowsVirtualization/hcsshim/branch/master) +[![Build status](https://github.com/microsoft/hcsshim/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/microsoft/hcsshim/actions?query=branch%3Amaster) -This package contains the Golang interface for using the Windows [Host Compute Service](https://blogs.technet.microsoft.com/virtualization/2017/01/27/introducing-the-host-compute-service-hcs/) (HCS) to launch and manage [Windows Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/). It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS). +This package contains the Golang interface for using the Windows [Host Compute Service](https://techcommunity.microsoft.com/t5/containers/introducing-the-host-compute-service-hcs/ba-p/382332) (HCS) to launch and manage [Windows Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/). It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS). It is primarily used in the [Moby Project](https://github.com/moby/moby), but it can be freely used by other projects as well. @@ -16,6 +16,11 @@ When you submit a pull request, a CLA-bot will automatically determine whether y a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. +We also ask that contributors [sign their commits](https://git-scm.com/docs/git-commit) using `git commit -s` or `git commit --signoff` to certify they either authored the work themselves or otherwise have permission to use it in this project. + + +## Code of Conduct + This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/doc.go b/vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/doc.go new file mode 100644 index 0000000000000..0684d05963c77 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/doc.go @@ -0,0 +1 @@ +package options diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.pb.go b/vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.pb.go new file mode 100644 index 0000000000000..89aff3723a5db --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.pb.go @@ -0,0 +1,1542 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.proto + +package options + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + _ "github.com/gogo/protobuf/types" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + io "io" + math "math" + reflect "reflect" + strings "strings" + time "time" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type Options_DebugType int32 + +const ( + Options_NPIPE Options_DebugType = 0 + Options_FILE Options_DebugType = 1 + Options_ETW Options_DebugType = 2 +) + +var Options_DebugType_name = map[int32]string{ + 0: "NPIPE", + 1: "FILE", + 2: "ETW", +} + +var Options_DebugType_value = map[string]int32{ + "NPIPE": 0, + "FILE": 1, + "ETW": 2, +} + +func (x Options_DebugType) String() string { + return proto.EnumName(Options_DebugType_name, int32(x)) +} + +func (Options_DebugType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_b643df6839c75082, []int{0, 0} +} + +type Options_SandboxIsolation int32 + +const ( + Options_PROCESS Options_SandboxIsolation = 0 + Options_HYPERVISOR Options_SandboxIsolation = 1 +) + +var Options_SandboxIsolation_name = map[int32]string{ + 0: "PROCESS", + 1: "HYPERVISOR", +} + +var Options_SandboxIsolation_value = map[string]int32{ + "PROCESS": 0, + "HYPERVISOR": 1, +} + +func (x Options_SandboxIsolation) String() string { + return proto.EnumName(Options_SandboxIsolation_name, int32(x)) +} + +func (Options_SandboxIsolation) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_b643df6839c75082, []int{0, 1} +} + +// Options are the set of customizations that can be passed at Create time. +type Options struct { + // Enable debug tracing (sets the logrus log level to debug). This may be deprecated in the future, prefer + // log_level as this will override debug if both of them are set. + Debug bool `protobuf:"varint,1,opt,name=debug,proto3" json:"debug,omitempty"` + // debug tracing output type + DebugType Options_DebugType `protobuf:"varint,2,opt,name=debug_type,json=debugType,proto3,enum=containerd.runhcs.v1.Options_DebugType" json:"debug_type,omitempty"` + // registry key root for storage of the runhcs container state + RegistryRoot string `protobuf:"bytes,3,opt,name=registry_root,json=registryRoot,proto3" json:"registry_root,omitempty"` + // sandbox_image is the image to use for the sandbox that matches the + // sandbox_platform. + SandboxImage string `protobuf:"bytes,4,opt,name=sandbox_image,json=sandboxImage,proto3" json:"sandbox_image,omitempty"` + // sandbox_platform is a CRI setting that specifies the platform + // architecture for all sandbox's in this runtime. Values are + // 'windows/amd64' and 'linux/amd64'. + SandboxPlatform string `protobuf:"bytes,5,opt,name=sandbox_platform,json=sandboxPlatform,proto3" json:"sandbox_platform,omitempty"` + // sandbox_isolation is a CRI setting that specifies the isolation level of + // the sandbox. For Windows runtime PROCESS and HYPERVISOR are valid. For + // LCOW only HYPERVISOR is valid and default if omitted. + SandboxIsolation Options_SandboxIsolation `protobuf:"varint,6,opt,name=sandbox_isolation,json=sandboxIsolation,proto3,enum=containerd.runhcs.v1.Options_SandboxIsolation" json:"sandbox_isolation,omitempty"` + // boot_files_root_path is the path to the directory containing the LCOW + // kernel and root FS files. + BootFilesRootPath string `protobuf:"bytes,7,opt,name=boot_files_root_path,json=bootFilesRootPath,proto3" json:"boot_files_root_path,omitempty"` + // vm_processor_count is the default number of processors to create for the + // hypervisor isolated utility vm. + // + // The platform default if omitted is 2, unless the host only has a single + // core in which case it is 1. + VmProcessorCount int32 `protobuf:"varint,8,opt,name=vm_processor_count,json=vmProcessorCount,proto3" json:"vm_processor_count,omitempty"` + // vm_memory_size_in_mb is the default amount of memory to assign to the + // hypervisor isolated utility vm. + // + // The platform default is 1024MB if omitted. + VmMemorySizeInMb int32 `protobuf:"varint,9,opt,name=vm_memory_size_in_mb,json=vmMemorySizeInMb,proto3" json:"vm_memory_size_in_mb,omitempty"` + // GPUVHDPath is the path to the gpu vhd to add to the uvm + // when a container requests a gpu + GPUVHDPath string `protobuf:"bytes,10,opt,name=GPUVHDPath,proto3" json:"GPUVHDPath,omitempty"` + // scale_cpu_limits_to_sandbox indicates that container CPU limits should + // be adjusted to account for the difference in number of cores between the + // host and UVM. + ScaleCpuLimitsToSandbox bool `protobuf:"varint,11,opt,name=scale_cpu_limits_to_sandbox,json=scaleCpuLimitsToSandbox,proto3" json:"scale_cpu_limits_to_sandbox,omitempty"` + // default_container_scratch_size_in_gb is the default scratch size (sandbox.vhdx) + // to be used for containers. Every container will get a sandbox of `size_in_gb` assigned + // instead of the default of 20GB. + DefaultContainerScratchSizeInGb int32 `protobuf:"varint,12,opt,name=default_container_scratch_size_in_gb,json=defaultContainerScratchSizeInGb,proto3" json:"default_container_scratch_size_in_gb,omitempty"` + // default_vm_scratch_size_in_gb is the default scratch size (sandbox.vhdx) + // to be used for the UVM. This only applies to WCOW as LCOW doesn't mount a scratch + // specifically for the UVM. + DefaultVmScratchSizeInGb int32 `protobuf:"varint,13,opt,name=default_vm_scratch_size_in_gb,json=defaultVmScratchSizeInGb,proto3" json:"default_vm_scratch_size_in_gb,omitempty"` + // share_scratch specifies if we'd like to reuse scratch space between multiple containers. + // This currently only affects LCOW. The sandbox containers scratch space is re-used for all + // subsequent containers launched in the pod. + ShareScratch bool `protobuf:"varint,14,opt,name=share_scratch,json=shareScratch,proto3" json:"share_scratch,omitempty"` + // NCProxyAddr is the address of the network configuration proxy service. If omitted + // the network is setup locally. + NCProxyAddr string `protobuf:"bytes,15,opt,name=NCProxyAddr,proto3" json:"NCProxyAddr,omitempty"` + // log_level specifies the logrus log level for the shim. Supported values are a string representation of the + // logrus log levels: "trace", "debug", "info", "warn", "error", "fatal", "panic". This setting will override + // the `debug` field if both are specified, unless the level specified is also "debug", as these are equivalent. + LogLevel string `protobuf:"bytes,16,opt,name=log_level,json=logLevel,proto3" json:"log_level,omitempty"` + // io_retry_timeout_in_sec is the timeout in seconds for how long to try and reconnect to an upstream IO provider if a connection is lost. + // The typical example is if Containerd has restarted but is expected to come back online. A 0 for this field is interpreted as an infinite + // timeout. + IoRetryTimeoutInSec int32 `protobuf:"varint,17,opt,name=io_retry_timeout_in_sec,json=ioRetryTimeoutInSec,proto3" json:"io_retry_timeout_in_sec,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Options) Reset() { *m = Options{} } +func (*Options) ProtoMessage() {} +func (*Options) Descriptor() ([]byte, []int) { + return fileDescriptor_b643df6839c75082, []int{0} +} +func (m *Options) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Options) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Options.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Options) XXX_Merge(src proto.Message) { + xxx_messageInfo_Options.Merge(m, src) +} +func (m *Options) XXX_Size() int { + return m.Size() +} +func (m *Options) XXX_DiscardUnknown() { + xxx_messageInfo_Options.DiscardUnknown(m) +} + +var xxx_messageInfo_Options proto.InternalMessageInfo + +// ProcessDetails contains additional information about a process. This is the additional +// info returned in the Pids query. +type ProcessDetails struct { + ImageName string `protobuf:"bytes,1,opt,name=image_name,json=imageName,proto3" json:"image_name,omitempty"` + CreatedAt time.Time `protobuf:"bytes,2,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at"` + KernelTime_100Ns uint64 `protobuf:"varint,3,opt,name=kernel_time_100_ns,json=kernelTime100Ns,proto3" json:"kernel_time_100_ns,omitempty"` + MemoryCommitBytes uint64 `protobuf:"varint,4,opt,name=memory_commit_bytes,json=memoryCommitBytes,proto3" json:"memory_commit_bytes,omitempty"` + MemoryWorkingSetPrivateBytes uint64 `protobuf:"varint,5,opt,name=memory_working_set_private_bytes,json=memoryWorkingSetPrivateBytes,proto3" json:"memory_working_set_private_bytes,omitempty"` + MemoryWorkingSetSharedBytes uint64 `protobuf:"varint,6,opt,name=memory_working_set_shared_bytes,json=memoryWorkingSetSharedBytes,proto3" json:"memory_working_set_shared_bytes,omitempty"` + ProcessID uint32 `protobuf:"varint,7,opt,name=process_id,json=processId,proto3" json:"process_id,omitempty"` + UserTime_100Ns uint64 `protobuf:"varint,8,opt,name=user_time_100_ns,json=userTime100Ns,proto3" json:"user_time_100_ns,omitempty"` + ExecID string `protobuf:"bytes,9,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ProcessDetails) Reset() { *m = ProcessDetails{} } +func (*ProcessDetails) ProtoMessage() {} +func (*ProcessDetails) Descriptor() ([]byte, []int) { + return fileDescriptor_b643df6839c75082, []int{1} +} +func (m *ProcessDetails) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProcessDetails) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProcessDetails.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ProcessDetails) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProcessDetails.Merge(m, src) +} +func (m *ProcessDetails) XXX_Size() int { + return m.Size() +} +func (m *ProcessDetails) XXX_DiscardUnknown() { + xxx_messageInfo_ProcessDetails.DiscardUnknown(m) +} + +var xxx_messageInfo_ProcessDetails proto.InternalMessageInfo + +func init() { + proto.RegisterEnum("containerd.runhcs.v1.Options_DebugType", Options_DebugType_name, Options_DebugType_value) + proto.RegisterEnum("containerd.runhcs.v1.Options_SandboxIsolation", Options_SandboxIsolation_name, Options_SandboxIsolation_value) + proto.RegisterType((*Options)(nil), "containerd.runhcs.v1.Options") + proto.RegisterType((*ProcessDetails)(nil), "containerd.runhcs.v1.ProcessDetails") +} + +func init() { + proto.RegisterFile("github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.proto", fileDescriptor_b643df6839c75082) +} + +var fileDescriptor_b643df6839c75082 = []byte{ + // 953 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0x5d, 0x6f, 0xdb, 0x36, + 0x17, 0xb6, 0xda, 0x24, 0xb6, 0x4e, 0xbe, 0x1c, 0x36, 0x40, 0x85, 0xe4, 0xad, 0x6d, 0xa4, 0x2f, + 0xd0, 0x14, 0x6b, 0xa4, 0xa4, 0xdb, 0xdd, 0x06, 0x0c, 0x8d, 0xed, 0xb4, 0x1e, 0xf2, 0x61, 0xc8, + 0x59, 0xba, 0x8f, 0x0b, 0x42, 0x1f, 0x8c, 0x4c, 0x54, 0x12, 0x05, 0x92, 0xf6, 0xe2, 0x5e, 0xed, + 0x27, 0xec, 0x87, 0xec, 0x87, 0xe4, 0x72, 0x97, 0x03, 0x06, 0x64, 0xab, 0x7f, 0xc9, 0x40, 0x8a, + 0x4a, 0xbb, 0x20, 0xd8, 0xcd, 0xae, 0x4c, 0x3e, 0xcf, 0xc3, 0x87, 0xe7, 0x1c, 0x9d, 0x43, 0xc3, + 0x59, 0x42, 0xe5, 0x78, 0x12, 0xba, 0x11, 0xcb, 0xbc, 0x13, 0x1a, 0x71, 0x26, 0xd8, 0xa5, 0xf4, + 0xc6, 0x91, 0x10, 0x63, 0x9a, 0x79, 0x51, 0x16, 0x7b, 0x11, 0xcb, 0x65, 0x40, 0x73, 0xc2, 0xe3, + 0x3d, 0x85, 0xed, 0xf1, 0x49, 0x3e, 0x8e, 0xc4, 0xde, 0xf4, 0xc0, 0x63, 0x85, 0xa4, 0x2c, 0x17, + 0x5e, 0x89, 0xb8, 0x05, 0x67, 0x92, 0xa1, 0xcd, 0x8f, 0x7a, 0xd7, 0x10, 0xd3, 0x83, 0xad, 0xcd, + 0x84, 0x25, 0x4c, 0x0b, 0x3c, 0xb5, 0x2a, 0xb5, 0x5b, 0xed, 0x84, 0xb1, 0x24, 0x25, 0x9e, 0xde, + 0x85, 0x93, 0x4b, 0x4f, 0xd2, 0x8c, 0x08, 0x19, 0x64, 0x45, 0x29, 0xd8, 0xf9, 0xb5, 0x0e, 0xf5, + 0xb3, 0xf2, 0x16, 0xb4, 0x09, 0x8b, 0x31, 0x09, 0x27, 0x89, 0x63, 0x75, 0xac, 0xdd, 0x86, 0x5f, + 0x6e, 0xd0, 0x11, 0x80, 0x5e, 0x60, 0x39, 0x2b, 0x88, 0xf3, 0xa0, 0x63, 0xed, 0xae, 0xbd, 0x7c, + 0xe6, 0xde, 0x17, 0x83, 0x6b, 0x8c, 0xdc, 0x9e, 0xd2, 0x9f, 0xcf, 0x0a, 0xe2, 0xdb, 0x71, 0xb5, + 0x44, 0x4f, 0x61, 0x95, 0x93, 0x84, 0x0a, 0xc9, 0x67, 0x98, 0x33, 0x26, 0x9d, 0x87, 0x1d, 0x6b, + 0xd7, 0xf6, 0x57, 0x2a, 0xd0, 0x67, 0x4c, 0x2a, 0x91, 0x08, 0xf2, 0x38, 0x64, 0x57, 0x98, 0x66, + 0x41, 0x42, 0x9c, 0x85, 0x52, 0x64, 0xc0, 0x81, 0xc2, 0xd0, 0x73, 0x68, 0x56, 0xa2, 0x22, 0x0d, + 0xe4, 0x25, 0xe3, 0x99, 0xb3, 0xa8, 0x75, 0xeb, 0x06, 0x1f, 0x1a, 0x18, 0xfd, 0x08, 0x1b, 0xb7, + 0x7e, 0x82, 0xa5, 0x81, 0x8a, 0xcf, 0x59, 0xd2, 0x39, 0xb8, 0xff, 0x9e, 0xc3, 0xc8, 0xdc, 0x58, + 0x9d, 0xf2, 0xab, 0x3b, 0x6f, 0x11, 0xe4, 0xc1, 0x66, 0xc8, 0x98, 0xc4, 0x97, 0x34, 0x25, 0x42, + 0xe7, 0x84, 0x8b, 0x40, 0x8e, 0x9d, 0xba, 0x8e, 0x65, 0x43, 0x71, 0x47, 0x8a, 0x52, 0x99, 0x0d, + 0x03, 0x39, 0x46, 0x2f, 0x00, 0x4d, 0x33, 0x5c, 0x70, 0x16, 0x11, 0x21, 0x18, 0xc7, 0x11, 0x9b, + 0xe4, 0xd2, 0x69, 0x74, 0xac, 0xdd, 0x45, 0xbf, 0x39, 0xcd, 0x86, 0x15, 0xd1, 0x55, 0x38, 0x72, + 0x61, 0x73, 0x9a, 0xe1, 0x8c, 0x64, 0x8c, 0xcf, 0xb0, 0xa0, 0xef, 0x09, 0xa6, 0x39, 0xce, 0x42, + 0xc7, 0xae, 0xf4, 0x27, 0x9a, 0x1a, 0xd1, 0xf7, 0x64, 0x90, 0x9f, 0x84, 0xa8, 0x05, 0xf0, 0x7a, + 0xf8, 0xed, 0xc5, 0x9b, 0x9e, 0xba, 0xcb, 0x01, 0x1d, 0xc4, 0x27, 0x08, 0xfa, 0x0a, 0xb6, 0x45, + 0x14, 0xa4, 0x04, 0x47, 0xc5, 0x04, 0xa7, 0x34, 0xa3, 0x52, 0x60, 0xc9, 0xb0, 0x49, 0xcb, 0x59, + 0xd6, 0x1f, 0xfd, 0xb1, 0x96, 0x74, 0x8b, 0xc9, 0xb1, 0x16, 0x9c, 0x33, 0x53, 0x07, 0x74, 0x02, + 0xff, 0x8f, 0xc9, 0x65, 0x30, 0x49, 0x25, 0xbe, 0xad, 0x1b, 0x16, 0x11, 0x0f, 0x64, 0x34, 0xbe, + 0x8d, 0x2e, 0x09, 0x9d, 0x15, 0x1d, 0x5d, 0xdb, 0x68, 0xbb, 0x95, 0x74, 0x54, 0x2a, 0xcb, 0x60, + 0x5f, 0x87, 0xe8, 0x6b, 0x78, 0x52, 0xd9, 0x4d, 0xb3, 0xfb, 0x7c, 0x56, 0xb5, 0x8f, 0x63, 0x44, + 0x17, 0xd9, 0x5d, 0x03, 0xd5, 0x29, 0xe3, 0x80, 0x93, 0xea, 0xac, 0xb3, 0xa6, 0xe3, 0x5f, 0xd1, + 0xa0, 0x11, 0xa3, 0x0e, 0x2c, 0x9f, 0x76, 0x87, 0x9c, 0x5d, 0xcd, 0x5e, 0xc5, 0x31, 0x77, 0xd6, + 0x75, 0x4d, 0x3e, 0x85, 0xd0, 0x36, 0xd8, 0x29, 0x4b, 0x70, 0x4a, 0xa6, 0x24, 0x75, 0x9a, 0x9a, + 0x6f, 0xa4, 0x2c, 0x39, 0x56, 0x7b, 0xf4, 0x05, 0x3c, 0xa6, 0x0c, 0x73, 0xa2, 0x5a, 0x56, 0x0d, + 0x0e, 0x9b, 0x48, 0x15, 0x9d, 0x20, 0x91, 0xb3, 0xa1, 0xc3, 0x7b, 0x44, 0x99, 0xaf, 0xd8, 0xf3, + 0x92, 0x1c, 0xe4, 0x23, 0x12, 0xed, 0x3c, 0x07, 0xfb, 0x76, 0x00, 0x90, 0x0d, 0x8b, 0xa7, 0xc3, + 0xc1, 0xb0, 0xdf, 0xac, 0xa1, 0x06, 0x2c, 0x1c, 0x0d, 0x8e, 0xfb, 0x4d, 0x0b, 0xd5, 0xe1, 0x61, + 0xff, 0xfc, 0x6d, 0xf3, 0xc1, 0x8e, 0x07, 0xcd, 0xbb, 0x7d, 0x86, 0x96, 0xa1, 0x3e, 0xf4, 0xcf, + 0xba, 0xfd, 0xd1, 0xa8, 0x59, 0x43, 0x6b, 0x00, 0x6f, 0xbe, 0x1f, 0xf6, 0xfd, 0x8b, 0xc1, 0xe8, + 0xcc, 0x6f, 0x5a, 0x3b, 0x7f, 0x3c, 0x84, 0x35, 0xd3, 0x26, 0x3d, 0x22, 0x03, 0x9a, 0x0a, 0xf4, + 0x04, 0x40, 0x8f, 0x0a, 0xce, 0x83, 0x8c, 0xe8, 0xd1, 0xb5, 0x7d, 0x5b, 0x23, 0xa7, 0x41, 0x46, + 0x50, 0x17, 0x20, 0xe2, 0x24, 0x90, 0x24, 0xc6, 0x81, 0xd4, 0xe3, 0xbb, 0xfc, 0x72, 0xcb, 0x2d, + 0x9f, 0x05, 0xb7, 0x7a, 0x16, 0xdc, 0xf3, 0xea, 0x59, 0x38, 0x6c, 0x5c, 0xdf, 0xb4, 0x6b, 0xbf, + 0xfc, 0xd9, 0xb6, 0x7c, 0xdb, 0x9c, 0x7b, 0x25, 0xd1, 0x67, 0x80, 0xde, 0x11, 0x9e, 0x93, 0x54, + 0x97, 0x01, 0x1f, 0xec, 0xef, 0xe3, 0x5c, 0xe8, 0x01, 0x5e, 0xf0, 0xd7, 0x4b, 0x46, 0x39, 0x1c, + 0xec, 0xef, 0x9f, 0x0a, 0xe4, 0xc2, 0x23, 0xd3, 0xb4, 0x11, 0xcb, 0x32, 0x2a, 0x71, 0x38, 0x93, + 0x44, 0xe8, 0x49, 0x5e, 0xf0, 0x37, 0x4a, 0xaa, 0xab, 0x99, 0x43, 0x45, 0xa0, 0x23, 0xe8, 0x18, + 0xfd, 0x4f, 0x8c, 0xbf, 0xa3, 0x79, 0x82, 0x05, 0x91, 0xb8, 0xe0, 0x74, 0x1a, 0x48, 0x62, 0x0e, + 0x2f, 0xea, 0xc3, 0xff, 0x2b, 0x75, 0x6f, 0x4b, 0xd9, 0x88, 0xc8, 0x61, 0x29, 0x2a, 0x7d, 0x7a, + 0xd0, 0xbe, 0xc7, 0x47, 0xf7, 0x43, 0x6c, 0x6c, 0x96, 0xb4, 0xcd, 0xf6, 0x5d, 0x9b, 0x91, 0xd6, + 0x94, 0x2e, 0x2f, 0x00, 0xcc, 0x80, 0x62, 0x1a, 0xeb, 0x51, 0x5e, 0x3d, 0x5c, 0x9d, 0xdf, 0xb4, + 0x6d, 0x53, 0xf6, 0x41, 0xcf, 0xb7, 0x8d, 0x60, 0x10, 0xa3, 0x67, 0xd0, 0x9c, 0x08, 0xc2, 0xff, + 0x51, 0x96, 0x86, 0xbe, 0x64, 0x55, 0xe1, 0x1f, 0x8b, 0xf2, 0x14, 0xea, 0xe4, 0x8a, 0x44, 0xca, + 0x53, 0xcd, 0xaf, 0x7d, 0x08, 0xf3, 0x9b, 0xf6, 0x52, 0xff, 0x8a, 0x44, 0x83, 0x9e, 0xbf, 0xa4, + 0xa8, 0x41, 0x7c, 0x18, 0x5f, 0x7f, 0x68, 0xd5, 0x7e, 0xff, 0xd0, 0xaa, 0xfd, 0x3c, 0x6f, 0x59, + 0xd7, 0xf3, 0x96, 0xf5, 0xdb, 0xbc, 0x65, 0xfd, 0x35, 0x6f, 0x59, 0x3f, 0x7c, 0xf3, 0xdf, 0xff, + 0x44, 0xbe, 0x34, 0xbf, 0xdf, 0xd5, 0xc2, 0x25, 0xfd, 0xdd, 0x3f, 0xff, 0x3b, 0x00, 0x00, 0xff, + 0xff, 0x6b, 0x83, 0xa6, 0x5f, 0x9b, 0x06, 0x00, 0x00, +} + +func (m *Options) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Options) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Debug { + dAtA[i] = 0x8 + i++ + if m.Debug { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.DebugType != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.DebugType)) + } + if len(m.RegistryRoot) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(len(m.RegistryRoot))) + i += copy(dAtA[i:], m.RegistryRoot) + } + if len(m.SandboxImage) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(len(m.SandboxImage))) + i += copy(dAtA[i:], m.SandboxImage) + } + if len(m.SandboxPlatform) > 0 { + dAtA[i] = 0x2a + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(len(m.SandboxPlatform))) + i += copy(dAtA[i:], m.SandboxPlatform) + } + if m.SandboxIsolation != 0 { + dAtA[i] = 0x30 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.SandboxIsolation)) + } + if len(m.BootFilesRootPath) > 0 { + dAtA[i] = 0x3a + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(len(m.BootFilesRootPath))) + i += copy(dAtA[i:], m.BootFilesRootPath) + } + if m.VmProcessorCount != 0 { + dAtA[i] = 0x40 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.VmProcessorCount)) + } + if m.VmMemorySizeInMb != 0 { + dAtA[i] = 0x48 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.VmMemorySizeInMb)) + } + if len(m.GPUVHDPath) > 0 { + dAtA[i] = 0x52 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(len(m.GPUVHDPath))) + i += copy(dAtA[i:], m.GPUVHDPath) + } + if m.ScaleCpuLimitsToSandbox { + dAtA[i] = 0x58 + i++ + if m.ScaleCpuLimitsToSandbox { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.DefaultContainerScratchSizeInGb != 0 { + dAtA[i] = 0x60 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.DefaultContainerScratchSizeInGb)) + } + if m.DefaultVmScratchSizeInGb != 0 { + dAtA[i] = 0x68 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.DefaultVmScratchSizeInGb)) + } + if m.ShareScratch { + dAtA[i] = 0x70 + i++ + if m.ShareScratch { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if len(m.NCProxyAddr) > 0 { + dAtA[i] = 0x7a + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(len(m.NCProxyAddr))) + i += copy(dAtA[i:], m.NCProxyAddr) + } + if len(m.LogLevel) > 0 { + dAtA[i] = 0x82 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(len(m.LogLevel))) + i += copy(dAtA[i:], m.LogLevel) + } + if m.IoRetryTimeoutInSec != 0 { + dAtA[i] = 0x88 + i++ + dAtA[i] = 0x1 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.IoRetryTimeoutInSec)) + } + if m.XXX_unrecognized != nil { + i += copy(dAtA[i:], m.XXX_unrecognized) + } + return i, nil +} + +func (m *ProcessDetails) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProcessDetails) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.ImageName) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(len(m.ImageName))) + i += copy(dAtA[i:], m.ImageName) + } + dAtA[i] = 0x12 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt))) + n1, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + if m.KernelTime_100Ns != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.KernelTime_100Ns)) + } + if m.MemoryCommitBytes != 0 { + dAtA[i] = 0x20 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.MemoryCommitBytes)) + } + if m.MemoryWorkingSetPrivateBytes != 0 { + dAtA[i] = 0x28 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.MemoryWorkingSetPrivateBytes)) + } + if m.MemoryWorkingSetSharedBytes != 0 { + dAtA[i] = 0x30 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.MemoryWorkingSetSharedBytes)) + } + if m.ProcessID != 0 { + dAtA[i] = 0x38 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.ProcessID)) + } + if m.UserTime_100Ns != 0 { + dAtA[i] = 0x40 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.UserTime_100Ns)) + } + if len(m.ExecID) > 0 { + dAtA[i] = 0x4a + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(len(m.ExecID))) + i += copy(dAtA[i:], m.ExecID) + } + if m.XXX_unrecognized != nil { + i += copy(dAtA[i:], m.XXX_unrecognized) + } + return i, nil +} + +func encodeVarintRunhcs(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Options) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Debug { + n += 2 + } + if m.DebugType != 0 { + n += 1 + sovRunhcs(uint64(m.DebugType)) + } + l = len(m.RegistryRoot) + if l > 0 { + n += 1 + l + sovRunhcs(uint64(l)) + } + l = len(m.SandboxImage) + if l > 0 { + n += 1 + l + sovRunhcs(uint64(l)) + } + l = len(m.SandboxPlatform) + if l > 0 { + n += 1 + l + sovRunhcs(uint64(l)) + } + if m.SandboxIsolation != 0 { + n += 1 + sovRunhcs(uint64(m.SandboxIsolation)) + } + l = len(m.BootFilesRootPath) + if l > 0 { + n += 1 + l + sovRunhcs(uint64(l)) + } + if m.VmProcessorCount != 0 { + n += 1 + sovRunhcs(uint64(m.VmProcessorCount)) + } + if m.VmMemorySizeInMb != 0 { + n += 1 + sovRunhcs(uint64(m.VmMemorySizeInMb)) + } + l = len(m.GPUVHDPath) + if l > 0 { + n += 1 + l + sovRunhcs(uint64(l)) + } + if m.ScaleCpuLimitsToSandbox { + n += 2 + } + if m.DefaultContainerScratchSizeInGb != 0 { + n += 1 + sovRunhcs(uint64(m.DefaultContainerScratchSizeInGb)) + } + if m.DefaultVmScratchSizeInGb != 0 { + n += 1 + sovRunhcs(uint64(m.DefaultVmScratchSizeInGb)) + } + if m.ShareScratch { + n += 2 + } + l = len(m.NCProxyAddr) + if l > 0 { + n += 1 + l + sovRunhcs(uint64(l)) + } + l = len(m.LogLevel) + if l > 0 { + n += 2 + l + sovRunhcs(uint64(l)) + } + if m.IoRetryTimeoutInSec != 0 { + n += 2 + sovRunhcs(uint64(m.IoRetryTimeoutInSec)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *ProcessDetails) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ImageName) + if l > 0 { + n += 1 + l + sovRunhcs(uint64(l)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt) + n += 1 + l + sovRunhcs(uint64(l)) + if m.KernelTime_100Ns != 0 { + n += 1 + sovRunhcs(uint64(m.KernelTime_100Ns)) + } + if m.MemoryCommitBytes != 0 { + n += 1 + sovRunhcs(uint64(m.MemoryCommitBytes)) + } + if m.MemoryWorkingSetPrivateBytes != 0 { + n += 1 + sovRunhcs(uint64(m.MemoryWorkingSetPrivateBytes)) + } + if m.MemoryWorkingSetSharedBytes != 0 { + n += 1 + sovRunhcs(uint64(m.MemoryWorkingSetSharedBytes)) + } + if m.ProcessID != 0 { + n += 1 + sovRunhcs(uint64(m.ProcessID)) + } + if m.UserTime_100Ns != 0 { + n += 1 + sovRunhcs(uint64(m.UserTime_100Ns)) + } + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovRunhcs(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovRunhcs(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozRunhcs(x uint64) (n int) { + return sovRunhcs(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Options) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Options{`, + `Debug:` + fmt.Sprintf("%v", this.Debug) + `,`, + `DebugType:` + fmt.Sprintf("%v", this.DebugType) + `,`, + `RegistryRoot:` + fmt.Sprintf("%v", this.RegistryRoot) + `,`, + `SandboxImage:` + fmt.Sprintf("%v", this.SandboxImage) + `,`, + `SandboxPlatform:` + fmt.Sprintf("%v", this.SandboxPlatform) + `,`, + `SandboxIsolation:` + fmt.Sprintf("%v", this.SandboxIsolation) + `,`, + `BootFilesRootPath:` + fmt.Sprintf("%v", this.BootFilesRootPath) + `,`, + `VmProcessorCount:` + fmt.Sprintf("%v", this.VmProcessorCount) + `,`, + `VmMemorySizeInMb:` + fmt.Sprintf("%v", this.VmMemorySizeInMb) + `,`, + `GPUVHDPath:` + fmt.Sprintf("%v", this.GPUVHDPath) + `,`, + `ScaleCpuLimitsToSandbox:` + fmt.Sprintf("%v", this.ScaleCpuLimitsToSandbox) + `,`, + `DefaultContainerScratchSizeInGb:` + fmt.Sprintf("%v", this.DefaultContainerScratchSizeInGb) + `,`, + `DefaultVmScratchSizeInGb:` + fmt.Sprintf("%v", this.DefaultVmScratchSizeInGb) + `,`, + `ShareScratch:` + fmt.Sprintf("%v", this.ShareScratch) + `,`, + `NCProxyAddr:` + fmt.Sprintf("%v", this.NCProxyAddr) + `,`, + `LogLevel:` + fmt.Sprintf("%v", this.LogLevel) + `,`, + `IoRetryTimeoutInSec:` + fmt.Sprintf("%v", this.IoRetryTimeoutInSec) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *ProcessDetails) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ProcessDetails{`, + `ImageName:` + fmt.Sprintf("%v", this.ImageName) + `,`, + `CreatedAt:` + strings.Replace(strings.Replace(this.CreatedAt.String(), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, + `KernelTime_100Ns:` + fmt.Sprintf("%v", this.KernelTime_100Ns) + `,`, + `MemoryCommitBytes:` + fmt.Sprintf("%v", this.MemoryCommitBytes) + `,`, + `MemoryWorkingSetPrivateBytes:` + fmt.Sprintf("%v", this.MemoryWorkingSetPrivateBytes) + `,`, + `MemoryWorkingSetSharedBytes:` + fmt.Sprintf("%v", this.MemoryWorkingSetSharedBytes) + `,`, + `ProcessID:` + fmt.Sprintf("%v", this.ProcessID) + `,`, + `UserTime_100Ns:` + fmt.Sprintf("%v", this.UserTime_100Ns) + `,`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringRunhcs(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Options) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Options: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Options: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Debug", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Debug = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DebugType", wireType) + } + m.DebugType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DebugType |= Options_DebugType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RegistryRoot", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunhcs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunhcs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RegistryRoot = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SandboxImage", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunhcs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunhcs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SandboxImage = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SandboxPlatform", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunhcs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunhcs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SandboxPlatform = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SandboxIsolation", wireType) + } + m.SandboxIsolation = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SandboxIsolation |= Options_SandboxIsolation(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BootFilesRootPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunhcs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunhcs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BootFilesRootPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VmProcessorCount", wireType) + } + m.VmProcessorCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.VmProcessorCount |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VmMemorySizeInMb", wireType) + } + m.VmMemorySizeInMb = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.VmMemorySizeInMb |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GPUVHDPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunhcs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunhcs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GPUVHDPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ScaleCpuLimitsToSandbox", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ScaleCpuLimitsToSandbox = bool(v != 0) + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DefaultContainerScratchSizeInGb", wireType) + } + m.DefaultContainerScratchSizeInGb = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DefaultContainerScratchSizeInGb |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DefaultVmScratchSizeInGb", wireType) + } + m.DefaultVmScratchSizeInGb = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DefaultVmScratchSizeInGb |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ShareScratch", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ShareScratch = bool(v != 0) + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NCProxyAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunhcs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunhcs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NCProxyAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 16: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LogLevel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunhcs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunhcs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LogLevel = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IoRetryTimeoutInSec", wireType) + } + m.IoRetryTimeoutInSec = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.IoRetryTimeoutInSec |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipRunhcs(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthRunhcs + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRunhcs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProcessDetails) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProcessDetails: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProcessDetails: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ImageName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunhcs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunhcs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ImageName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthRunhcs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRunhcs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field KernelTime_100Ns", wireType) + } + m.KernelTime_100Ns = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.KernelTime_100Ns |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MemoryCommitBytes", wireType) + } + m.MemoryCommitBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MemoryCommitBytes |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MemoryWorkingSetPrivateBytes", wireType) + } + m.MemoryWorkingSetPrivateBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MemoryWorkingSetPrivateBytes |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MemoryWorkingSetSharedBytes", wireType) + } + m.MemoryWorkingSetSharedBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MemoryWorkingSetSharedBytes |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProcessID", wireType) + } + m.ProcessID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProcessID |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UserTime_100Ns", wireType) + } + m.UserTime_100Ns = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UserTime_100Ns |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunhcs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunhcs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRunhcs(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthRunhcs + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRunhcs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipRunhcs(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunhcs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunhcs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunhcs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthRunhcs + } + iNdEx += length + if iNdEx < 0 { + return 0, ErrInvalidLengthRunhcs + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunhcs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipRunhcs(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + if iNdEx < 0 { + return 0, ErrInvalidLengthRunhcs + } + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthRunhcs = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowRunhcs = fmt.Errorf("proto: integer overflow") +) diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.proto b/vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.proto new file mode 100644 index 0000000000000..60c89adbde85f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options/runhcs.proto @@ -0,0 +1,115 @@ +syntax = "proto3"; + +package containerd.runhcs.v1; + +import weak "gogoproto/gogo.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options;options"; + +// Options are the set of customizations that can be passed at Create time. +message Options { + // Enable debug tracing (sets the logrus log level to debug). This may be deprecated in the future, prefer + // log_level as this will override debug if both of them are set. + bool debug = 1; + + enum DebugType { + NPIPE = 0; + FILE = 1; + ETW = 2; + } + + // debug tracing output type + DebugType debug_type = 2; + + // registry key root for storage of the runhcs container state + string registry_root = 3; + + // sandbox_image is the image to use for the sandbox that matches the + // sandbox_platform. + string sandbox_image = 4; + + // sandbox_platform is a CRI setting that specifies the platform + // architecture for all sandbox's in this runtime. Values are + // 'windows/amd64' and 'linux/amd64'. + string sandbox_platform = 5; + + enum SandboxIsolation { + PROCESS = 0; + HYPERVISOR = 1; + } + + // sandbox_isolation is a CRI setting that specifies the isolation level of + // the sandbox. For Windows runtime PROCESS and HYPERVISOR are valid. For + // LCOW only HYPERVISOR is valid and default if omitted. + SandboxIsolation sandbox_isolation = 6; + + // boot_files_root_path is the path to the directory containing the LCOW + // kernel and root FS files. + string boot_files_root_path = 7; + + // vm_processor_count is the default number of processors to create for the + // hypervisor isolated utility vm. + // + // The platform default if omitted is 2, unless the host only has a single + // core in which case it is 1. + int32 vm_processor_count = 8; + + // vm_memory_size_in_mb is the default amount of memory to assign to the + // hypervisor isolated utility vm. + // + // The platform default is 1024MB if omitted. + int32 vm_memory_size_in_mb = 9; + + // GPUVHDPath is the path to the gpu vhd to add to the uvm + // when a container requests a gpu + string GPUVHDPath = 10; + + // scale_cpu_limits_to_sandbox indicates that container CPU limits should + // be adjusted to account for the difference in number of cores between the + // host and UVM. + bool scale_cpu_limits_to_sandbox = 11; + + // default_container_scratch_size_in_gb is the default scratch size (sandbox.vhdx) + // to be used for containers. Every container will get a sandbox of `size_in_gb` assigned + // instead of the default of 20GB. + int32 default_container_scratch_size_in_gb = 12; + + // default_vm_scratch_size_in_gb is the default scratch size (sandbox.vhdx) + // to be used for the UVM. This only applies to WCOW as LCOW doesn't mount a scratch + // specifically for the UVM. + int32 default_vm_scratch_size_in_gb = 13; + + // share_scratch specifies if we'd like to reuse scratch space between multiple containers. + // This currently only affects LCOW. The sandbox containers scratch space is re-used for all + // subsequent containers launched in the pod. + bool share_scratch = 14; + + // NCProxyAddr is the address of the network configuration proxy service. If omitted + // the network is setup locally. + string NCProxyAddr = 15; + + // log_level specifies the logrus log level for the shim. Supported values are a string representation of the + // logrus log levels: "trace", "debug", "info", "warn", "error", "fatal", "panic". This setting will override + // the `debug` field if both are specified, unless the level specified is also "debug", as these are equivalent. + string log_level = 16; + + // io_retry_timeout_in_sec is the timeout in seconds for how long to try and reconnect to an upstream IO provider if a connection is lost. + // The typical example is if Containerd has restarted but is expected to come back online. A 0 for this field is interpreted as an infinite + // timeout. + int32 io_retry_timeout_in_sec = 17; +} + +// ProcessDetails contains additional information about a process. This is the additional +// info returned in the Pids query. +message ProcessDetails { + string image_name = 1; + google.protobuf.Timestamp created_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + uint64 kernel_time_100_ns = 3; + uint64 memory_commit_bytes = 4; + uint64 memory_working_set_private_bytes = 5; + uint64 memory_working_set_shared_bytes = 6; + uint32 process_id = 7; + uint64 user_time_100_ns = 8; + string exec_id = 9; +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/attach.go b/vendor/github.com/Microsoft/hcsshim/computestorage/attach.go new file mode 100644 index 0000000000000..7f1f2823ddce3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/attach.go @@ -0,0 +1,38 @@ +package computestorage + +import ( + "context" + "encoding/json" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// AttachLayerStorageFilter sets up the layer storage filter on a writable +// container layer. +// +// `layerPath` is a path to a directory the writable layer is mounted. If the +// path does not end in a `\` the platform will append it automatically. +// +// `layerData` is the parent read-only layer data. +func AttachLayerStorageFilter(ctx context.Context, layerPath string, layerData LayerData) (err error) { + title := "hcsshim.AttachLayerStorageFilter" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + ) + + bytes, err := json.Marshal(layerData) + if err != nil { + return err + } + + err = hcsAttachLayerStorageFilter(layerPath, string(bytes)) + if err != nil { + return errors.Wrap(err, "failed to attach layer storage filter") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/destroy.go b/vendor/github.com/Microsoft/hcsshim/computestorage/destroy.go new file mode 100644 index 0000000000000..8e28e6c504693 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/destroy.go @@ -0,0 +1,26 @@ +package computestorage + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// DestroyLayer deletes a container layer. +// +// `layerPath` is a path to a directory containing the layer to export. +func DestroyLayer(ctx context.Context, layerPath string) (err error) { + title := "hcsshim.DestroyLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("layerPath", layerPath)) + + err = hcsDestroyLayer(layerPath) + if err != nil { + return errors.Wrap(err, "failed to destroy layer") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/detach.go b/vendor/github.com/Microsoft/hcsshim/computestorage/detach.go new file mode 100644 index 0000000000000..435473257e30c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/detach.go @@ -0,0 +1,26 @@ +package computestorage + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// DetachLayerStorageFilter detaches the layer storage filter on a writable container layer. +// +// `layerPath` is a path to a directory containing the layer to export. +func DetachLayerStorageFilter(ctx context.Context, layerPath string) (err error) { + title := "hcsshim.DetachLayerStorageFilter" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("layerPath", layerPath)) + + err = hcsDetachLayerStorageFilter(layerPath) + if err != nil { + return errors.Wrap(err, "failed to detach layer storage filter") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/export.go b/vendor/github.com/Microsoft/hcsshim/computestorage/export.go new file mode 100644 index 0000000000000..a1b12dd129286 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/export.go @@ -0,0 +1,46 @@ +package computestorage + +import ( + "context" + "encoding/json" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// ExportLayer exports a container layer. +// +// `layerPath` is a path to a directory containing the layer to export. +// +// `exportFolderPath` is a pre-existing folder to export the layer to. +// +// `layerData` is the parent layer data. +// +// `options` are the export options applied to the exported layer. +func ExportLayer(ctx context.Context, layerPath, exportFolderPath string, layerData LayerData, options ExportLayerOptions) (err error) { + title := "hcsshim.ExportLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + trace.StringAttribute("exportFolderPath", exportFolderPath), + ) + + ldbytes, err := json.Marshal(layerData) + if err != nil { + return err + } + + obytes, err := json.Marshal(options) + if err != nil { + return err + } + + err = hcsExportLayer(layerPath, exportFolderPath, string(ldbytes), string(obytes)) + if err != nil { + return errors.Wrap(err, "failed to export layer") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/format.go b/vendor/github.com/Microsoft/hcsshim/computestorage/format.go new file mode 100644 index 0000000000000..83c0fa33f0fce --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/format.go @@ -0,0 +1,26 @@ +package computestorage + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" + "golang.org/x/sys/windows" +) + +// FormatWritableLayerVhd formats a virtual disk for use as a writable container layer. +// +// If the VHD is not mounted it will be temporarily mounted. +func FormatWritableLayerVhd(ctx context.Context, vhdHandle windows.Handle) (err error) { + title := "hcsshim.FormatWritableLayerVhd" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + + err = hcsFormatWritableLayerVhd(vhdHandle) + if err != nil { + return errors.Wrap(err, "failed to format writable layer vhd") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/helpers.go b/vendor/github.com/Microsoft/hcsshim/computestorage/helpers.go new file mode 100644 index 0000000000000..87fee452cd3f8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/helpers.go @@ -0,0 +1,193 @@ +package computestorage + +import ( + "context" + "os" + "path/filepath" + "syscall" + + "github.com/Microsoft/go-winio/pkg/security" + "github.com/Microsoft/go-winio/vhd" + "github.com/pkg/errors" + "golang.org/x/sys/windows" +) + +const defaultVHDXBlockSizeInMB = 1 + +// SetupContainerBaseLayer is a helper to setup a containers scratch. It +// will create and format the vhdx's inside and the size is configurable with the sizeInGB +// parameter. +// +// `layerPath` is the path to the base container layer on disk. +// +// `baseVhdPath` is the path to where the base vhdx for the base layer should be created. +// +// `diffVhdPath` is the path where the differencing disk for the base layer should be created. +// +// `sizeInGB` is the size in gigabytes to make the base vhdx. +func SetupContainerBaseLayer(ctx context.Context, layerPath, baseVhdPath, diffVhdPath string, sizeInGB uint64) (err error) { + var ( + hivesPath = filepath.Join(layerPath, "Hives") + layoutPath = filepath.Join(layerPath, "Layout") + ) + + // We need to remove the hives directory and layout file as `SetupBaseOSLayer` fails if these files + // already exist. `SetupBaseOSLayer` will create these files internally. We also remove the base and + // differencing disks if they exist in case we're asking for a different size. + if _, err := os.Stat(hivesPath); err == nil { + if err := os.RemoveAll(hivesPath); err != nil { + return errors.Wrap(err, "failed to remove prexisting hives directory") + } + } + if _, err := os.Stat(layoutPath); err == nil { + if err := os.RemoveAll(layoutPath); err != nil { + return errors.Wrap(err, "failed to remove prexisting layout file") + } + } + + if _, err := os.Stat(baseVhdPath); err == nil { + if err := os.RemoveAll(baseVhdPath); err != nil { + return errors.Wrap(err, "failed to remove base vhdx path") + } + } + if _, err := os.Stat(diffVhdPath); err == nil { + if err := os.RemoveAll(diffVhdPath); err != nil { + return errors.Wrap(err, "failed to remove differencing vhdx") + } + } + + createParams := &vhd.CreateVirtualDiskParameters{ + Version: 2, + Version2: vhd.CreateVersion2{ + MaximumSize: sizeInGB * 1024 * 1024 * 1024, + BlockSizeInBytes: defaultVHDXBlockSizeInMB * 1024 * 1024, + }, + } + handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams) + if err != nil { + return errors.Wrap(err, "failed to create vhdx") + } + + defer func() { + if err != nil { + _ = syscall.CloseHandle(handle) + os.RemoveAll(baseVhdPath) + os.RemoveAll(diffVhdPath) + } + }() + + if err = FormatWritableLayerVhd(ctx, windows.Handle(handle)); err != nil { + return err + } + // Base vhd handle must be closed before calling SetupBaseLayer in case of Container layer + if err = syscall.CloseHandle(handle); err != nil { + return errors.Wrap(err, "failed to close vhdx handle") + } + + options := OsLayerOptions{ + Type: OsLayerTypeContainer, + } + + // SetupBaseOSLayer expects an empty vhd handle for a container layer and will + // error out otherwise. + if err = SetupBaseOSLayer(ctx, layerPath, 0, options); err != nil { + return err + } + // Create the differencing disk that will be what's copied for the final rw layer + // for a container. + if err = vhd.CreateDiffVhd(diffVhdPath, baseVhdPath, defaultVHDXBlockSizeInMB); err != nil { + return errors.Wrap(err, "failed to create differencing disk") + } + + if err = security.GrantVmGroupAccess(baseVhdPath); err != nil { + return errors.Wrapf(err, "failed to grant vm group access to %s", baseVhdPath) + } + if err = security.GrantVmGroupAccess(diffVhdPath); err != nil { + return errors.Wrapf(err, "failed to grant vm group access to %s", diffVhdPath) + } + return nil +} + +// SetupUtilityVMBaseLayer is a helper to setup a UVMs scratch space. It will create and format +// the vhdx inside and the size is configurable by the sizeInGB parameter. +// +// `uvmPath` is the path to the UtilityVM filesystem. +// +// `baseVhdPath` is the path to where the base vhdx for the UVM should be created. +// +// `diffVhdPath` is the path where the differencing disk for the UVM should be created. +// +// `sizeInGB` specifies the size in gigabytes to make the base vhdx. +func SetupUtilityVMBaseLayer(ctx context.Context, uvmPath, baseVhdPath, diffVhdPath string, sizeInGB uint64) (err error) { + // Remove the base and differencing disks if they exist in case we're asking for a different size. + if _, err := os.Stat(baseVhdPath); err == nil { + if err := os.RemoveAll(baseVhdPath); err != nil { + return errors.Wrap(err, "failed to remove base vhdx") + } + } + if _, err := os.Stat(diffVhdPath); err == nil { + if err := os.RemoveAll(diffVhdPath); err != nil { + return errors.Wrap(err, "failed to remove differencing vhdx") + } + } + + // Just create the vhdx for utilityVM layer, no need to format it. + createParams := &vhd.CreateVirtualDiskParameters{ + Version: 2, + Version2: vhd.CreateVersion2{ + MaximumSize: sizeInGB * 1024 * 1024 * 1024, + BlockSizeInBytes: defaultVHDXBlockSizeInMB * 1024 * 1024, + }, + } + handle, err := vhd.CreateVirtualDisk(baseVhdPath, vhd.VirtualDiskAccessNone, vhd.CreateVirtualDiskFlagNone, createParams) + if err != nil { + return errors.Wrap(err, "failed to create vhdx") + } + + defer func() { + if err != nil { + _ = syscall.CloseHandle(handle) + os.RemoveAll(baseVhdPath) + os.RemoveAll(diffVhdPath) + } + }() + + // If it is a UtilityVM layer then the base vhdx must be attached when calling + // `SetupBaseOSLayer` + attachParams := &vhd.AttachVirtualDiskParameters{ + Version: 2, + } + if err := vhd.AttachVirtualDisk(handle, vhd.AttachVirtualDiskFlagNone, attachParams); err != nil { + return errors.Wrapf(err, "failed to attach virtual disk") + } + + options := OsLayerOptions{ + Type: OsLayerTypeVM, + } + if err := SetupBaseOSLayer(ctx, uvmPath, windows.Handle(handle), options); err != nil { + return err + } + + // Detach and close the handle after setting up the layer as we don't need the handle + // for anything else and we no longer need to be attached either. + if err = vhd.DetachVirtualDisk(handle); err != nil { + return errors.Wrap(err, "failed to detach vhdx") + } + if err = syscall.CloseHandle(handle); err != nil { + return errors.Wrap(err, "failed to close vhdx handle") + } + + // Create the differencing disk that will be what's copied for the final rw layer + // for a container. + if err = vhd.CreateDiffVhd(diffVhdPath, baseVhdPath, defaultVHDXBlockSizeInMB); err != nil { + return errors.Wrap(err, "failed to create differencing disk") + } + + if err := security.GrantVmGroupAccess(baseVhdPath); err != nil { + return errors.Wrapf(err, "failed to grant vm group access to %s", baseVhdPath) + } + if err := security.GrantVmGroupAccess(diffVhdPath); err != nil { + return errors.Wrapf(err, "failed to grant vm group access to %s", diffVhdPath) + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/import.go b/vendor/github.com/Microsoft/hcsshim/computestorage/import.go new file mode 100644 index 0000000000000..0c61dab329185 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/import.go @@ -0,0 +1,41 @@ +package computestorage + +import ( + "context" + "encoding/json" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// ImportLayer imports a container layer. +// +// `layerPath` is a path to a directory to import the layer to. If the directory +// does not exist it will be automatically created. +// +// `sourceFolderpath` is a pre-existing folder that contains the layer to +// import. +// +// `layerData` is the parent layer data. +func ImportLayer(ctx context.Context, layerPath, sourceFolderPath string, layerData LayerData) (err error) { + title := "hcsshim.ImportLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + trace.StringAttribute("sourceFolderPath", sourceFolderPath), + ) + + bytes, err := json.Marshal(layerData) + if err != nil { + return err + } + + err = hcsImportLayer(layerPath, sourceFolderPath, string(bytes)) + if err != nil { + return errors.Wrap(err, "failed to import layer") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/initialize.go b/vendor/github.com/Microsoft/hcsshim/computestorage/initialize.go new file mode 100644 index 0000000000000..53ed8ea6edaaf --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/initialize.go @@ -0,0 +1,38 @@ +package computestorage + +import ( + "context" + "encoding/json" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" +) + +// InitializeWritableLayer initializes a writable layer for a container. +// +// `layerPath` is a path to a directory the layer is mounted. If the +// path does not end in a `\` the platform will append it automatically. +// +// `layerData` is the parent read-only layer data. +func InitializeWritableLayer(ctx context.Context, layerPath string, layerData LayerData) (err error) { + title := "hcsshim.InitializeWritableLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + ) + + bytes, err := json.Marshal(layerData) + if err != nil { + return err + } + + // Options are not used in the platform as of RS5 + err = hcsInitializeWritableLayer(layerPath, string(bytes), "") + if err != nil { + return errors.Wrap(err, "failed to intitialize container layer") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/mount.go b/vendor/github.com/Microsoft/hcsshim/computestorage/mount.go new file mode 100644 index 0000000000000..fcdbbef81437d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/mount.go @@ -0,0 +1,27 @@ +package computestorage + +import ( + "context" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/pkg/errors" + "go.opencensus.io/trace" + "golang.org/x/sys/windows" +) + +// GetLayerVhdMountPath returns the volume path for a virtual disk of a writable container layer. +func GetLayerVhdMountPath(ctx context.Context, vhdHandle windows.Handle) (path string, err error) { + title := "hcsshim.GetLayerVhdMountPath" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + + var mountPath *uint16 + err = hcsGetLayerVhdMountPath(vhdHandle, &mountPath) + if err != nil { + return "", errors.Wrap(err, "failed to get vhd mount path") + } + path = interop.ConvertAndFreeCoTaskMemString(mountPath) + return path, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/setup.go b/vendor/github.com/Microsoft/hcsshim/computestorage/setup.go new file mode 100644 index 0000000000000..06aaf841e8f28 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/setup.go @@ -0,0 +1,74 @@ +package computestorage + +import ( + "context" + "encoding/json" + + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/osversion" + "github.com/pkg/errors" + "go.opencensus.io/trace" + "golang.org/x/sys/windows" +) + +// SetupBaseOSLayer sets up a layer that contains a base OS for a container. +// +// `layerPath` is a path to a directory containing the layer. +// +// `vhdHandle` is an empty file handle of `options.Type == OsLayerTypeContainer` +// or else it is a file handle to the 'SystemTemplateBase.vhdx' if `options.Type +// == OsLayerTypeVm`. +// +// `options` are the options applied while processing the layer. +func SetupBaseOSLayer(ctx context.Context, layerPath string, vhdHandle windows.Handle, options OsLayerOptions) (err error) { + title := "hcsshim.SetupBaseOSLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + ) + + bytes, err := json.Marshal(options) + if err != nil { + return err + } + + err = hcsSetupBaseOSLayer(layerPath, vhdHandle, string(bytes)) + if err != nil { + return errors.Wrap(err, "failed to setup base OS layer") + } + return nil +} + +// SetupBaseOSVolume sets up a volume that contains a base OS for a container. +// +// `layerPath` is a path to a directory containing the layer. +// +// `volumePath` is the path to the volume to be used for setup. +// +// `options` are the options applied while processing the layer. +func SetupBaseOSVolume(ctx context.Context, layerPath, volumePath string, options OsLayerOptions) (err error) { + if osversion.Build() < 19645 { + return errors.New("SetupBaseOSVolume is not present on builds older than 19645") + } + title := "hcsshim.SetupBaseOSVolume" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("layerPath", layerPath), + trace.StringAttribute("volumePath", volumePath), + ) + + bytes, err := json.Marshal(options) + if err != nil { + return err + } + + err = hcsSetupBaseOSVolume(layerPath, volumePath, string(bytes)) + if err != nil { + return errors.Wrap(err, "failed to setup base OS layer") + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/storage.go b/vendor/github.com/Microsoft/hcsshim/computestorage/storage.go new file mode 100644 index 0000000000000..95aff9c184838 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/storage.go @@ -0,0 +1,50 @@ +// Package computestorage is a wrapper around the HCS storage APIs. These are new storage APIs introduced +// separate from the original graphdriver calls intended to give more freedom around creating +// and managing container layers and scratch spaces. +package computestorage + +import ( + hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" +) + +//go:generate go run ../mksyscall_windows.go -output zsyscall_windows.go storage.go + +//sys hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) = computestorage.HcsImportLayer? +//sys hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) = computestorage.HcsExportLayer? +//sys hcsDestroyLayer(layerPath string) (hr error) = computestorage.HcsDestoryLayer? +//sys hcsSetupBaseOSLayer(layerPath string, handle windows.Handle, options string) (hr error) = computestorage.HcsSetupBaseOSLayer? +//sys hcsInitializeWritableLayer(writableLayerPath string, layerData string, options string) (hr error) = computestorage.HcsInitializeWritableLayer? +//sys hcsAttachLayerStorageFilter(layerPath string, layerData string) (hr error) = computestorage.HcsAttachLayerStorageFilter? +//sys hcsDetachLayerStorageFilter(layerPath string) (hr error) = computestorage.HcsDetachLayerStorageFilter? +//sys hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) = computestorage.HcsFormatWritableLayerVhd? +//sys hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) = computestorage.HcsGetLayerVhdMountPath? +//sys hcsSetupBaseOSVolume(layerPath string, volumePath string, options string) (hr error) = computestorage.HcsSetupBaseOSVolume? + +// LayerData is the data used to describe parent layer information. +type LayerData struct { + SchemaVersion hcsschema.Version `json:"SchemaVersion,omitempty"` + Layers []hcsschema.Layer `json:"Layers,omitempty"` +} + +// ExportLayerOptions are the set of options that are used with the `computestorage.HcsExportLayer` syscall. +type ExportLayerOptions struct { + IsWritableLayer bool `json:"IsWritableLayer,omitempty"` +} + +// OsLayerType is the type of layer being operated on. +type OsLayerType string + +const ( + // OsLayerTypeContainer is a container layer. + OsLayerTypeContainer OsLayerType = "Container" + // OsLayerTypeVM is a virtual machine layer. + OsLayerTypeVM OsLayerType = "Vm" +) + +// OsLayerOptions are the set of options that are used with the `SetupBaseOSLayer` and +// `SetupBaseOSVolume` calls. +type OsLayerOptions struct { + Type OsLayerType `json:"Type,omitempty"` + DisableCiCacheOptimization bool `json:"DisableCiCacheOptimization,omitempty"` + SkipUpdateBcdForBoot bool `json:"SkipUpdateBcdForBoot,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go new file mode 100644 index 0000000000000..4f95180674340 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/computestorage/zsyscall_windows.go @@ -0,0 +1,319 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package computestorage + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modcomputestorage = windows.NewLazySystemDLL("computestorage.dll") + + procHcsImportLayer = modcomputestorage.NewProc("HcsImportLayer") + procHcsExportLayer = modcomputestorage.NewProc("HcsExportLayer") + procHcsDestoryLayer = modcomputestorage.NewProc("HcsDestoryLayer") + procHcsSetupBaseOSLayer = modcomputestorage.NewProc("HcsSetupBaseOSLayer") + procHcsInitializeWritableLayer = modcomputestorage.NewProc("HcsInitializeWritableLayer") + procHcsAttachLayerStorageFilter = modcomputestorage.NewProc("HcsAttachLayerStorageFilter") + procHcsDetachLayerStorageFilter = modcomputestorage.NewProc("HcsDetachLayerStorageFilter") + procHcsFormatWritableLayerVhd = modcomputestorage.NewProc("HcsFormatWritableLayerVhd") + procHcsGetLayerVhdMountPath = modcomputestorage.NewProc("HcsGetLayerVhdMountPath") + procHcsSetupBaseOSVolume = modcomputestorage.NewProc("HcsSetupBaseOSVolume") +) + +func hcsImportLayer(layerPath string, sourceFolderPath string, layerData string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(sourceFolderPath) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(layerData) + if hr != nil { + return + } + return _hcsImportLayer(_p0, _p1, _p2) +} + +func _hcsImportLayer(layerPath *uint16, sourceFolderPath *uint16, layerData *uint16) (hr error) { + if hr = procHcsImportLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsImportLayer.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(sourceFolderPath)), uintptr(unsafe.Pointer(layerData))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsExportLayer(layerPath string, exportFolderPath string, layerData string, options string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(exportFolderPath) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(layerData) + if hr != nil { + return + } + var _p3 *uint16 + _p3, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsExportLayer(_p0, _p1, _p2, _p3) +} + +func _hcsExportLayer(layerPath *uint16, exportFolderPath *uint16, layerData *uint16, options *uint16) (hr error) { + if hr = procHcsExportLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsExportLayer.Addr(), 4, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(exportFolderPath)), uintptr(unsafe.Pointer(layerData)), uintptr(unsafe.Pointer(options)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsDestroyLayer(layerPath string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + return _hcsDestroyLayer(_p0) +} + +func _hcsDestroyLayer(layerPath *uint16) (hr error) { + if hr = procHcsDestoryLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsDestoryLayer.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsSetupBaseOSLayer(layerPath string, handle windows.Handle, options string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSetupBaseOSLayer(_p0, handle, _p1) +} + +func _hcsSetupBaseOSLayer(layerPath *uint16, handle windows.Handle, options *uint16) (hr error) { + if hr = procHcsSetupBaseOSLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsSetupBaseOSLayer.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(handle), uintptr(unsafe.Pointer(options))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsInitializeWritableLayer(writableLayerPath string, layerData string, options string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(writableLayerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(layerData) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsInitializeWritableLayer(_p0, _p1, _p2) +} + +func _hcsInitializeWritableLayer(writableLayerPath *uint16, layerData *uint16, options *uint16) (hr error) { + if hr = procHcsInitializeWritableLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsInitializeWritableLayer.Addr(), 3, uintptr(unsafe.Pointer(writableLayerPath)), uintptr(unsafe.Pointer(layerData)), uintptr(unsafe.Pointer(options))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsAttachLayerStorageFilter(layerPath string, layerData string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(layerData) + if hr != nil { + return + } + return _hcsAttachLayerStorageFilter(_p0, _p1) +} + +func _hcsAttachLayerStorageFilter(layerPath *uint16, layerData *uint16) (hr error) { + if hr = procHcsAttachLayerStorageFilter.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsAttachLayerStorageFilter.Addr(), 2, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(layerData)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsDetachLayerStorageFilter(layerPath string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + return _hcsDetachLayerStorageFilter(_p0) +} + +func _hcsDetachLayerStorageFilter(layerPath *uint16) (hr error) { + if hr = procHcsDetachLayerStorageFilter.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsDetachLayerStorageFilter.Addr(), 1, uintptr(unsafe.Pointer(layerPath)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsFormatWritableLayerVhd(handle windows.Handle) (hr error) { + if hr = procHcsFormatWritableLayerVhd.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsFormatWritableLayerVhd.Addr(), 1, uintptr(handle), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetLayerVhdMountPath(vhdHandle windows.Handle, mountPath **uint16) (hr error) { + if hr = procHcsGetLayerVhdMountPath.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetLayerVhdMountPath.Addr(), 2, uintptr(vhdHandle), uintptr(unsafe.Pointer(mountPath)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsSetupBaseOSVolume(layerPath string, volumePath string, options string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(layerPath) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(volumePath) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSetupBaseOSVolume(_p0, _p1, _p2) +} + +func _hcsSetupBaseOSVolume(layerPath *uint16, volumePath *uint16, options *uint16) (hr error) { + if hr = procHcsSetupBaseOSVolume.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsSetupBaseOSVolume.Addr(), 3, uintptr(unsafe.Pointer(layerPath)), uintptr(unsafe.Pointer(volumePath)), uintptr(unsafe.Pointer(options))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/container.go b/vendor/github.com/Microsoft/hcsshim/container.go index e142c315445a1..bfd722898e948 100644 --- a/vendor/github.com/Microsoft/hcsshim/container.go +++ b/vendor/github.com/Microsoft/hcsshim/container.go @@ -1,13 +1,15 @@ package hcsshim import ( + "context" "fmt" "os" + "sync" "time" "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/hcs/schema1" "github.com/Microsoft/hcsshim/internal/mergemaps" - "github.com/Microsoft/hcsshim/internal/schema1" ) // ContainerProperties holds the properties for a container and the processes running in that container @@ -52,7 +54,10 @@ const ( type ResourceModificationRequestResponse = schema1.ResourceModificationRequestResponse type container struct { - system *hcs.System + system *hcs.System + waitOnce sync.Once + waitErr error + waitCh chan struct{} } // createComputeSystemAdditionalJSON is read from the environment at initialisation @@ -71,61 +76,87 @@ func CreateContainer(id string, c *ContainerConfig) (Container, error) { return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", createContainerAdditionalJSON, err) } - system, err := hcs.CreateComputeSystem(id, fullConfig) + system, err := hcs.CreateComputeSystem(context.Background(), id, fullConfig) if err != nil { return nil, err } - return &container{system}, err + return &container{system: system}, err } // OpenContainer opens an existing container by ID. func OpenContainer(id string) (Container, error) { - system, err := hcs.OpenComputeSystem(id) + system, err := hcs.OpenComputeSystem(context.Background(), id) if err != nil { return nil, err } - return &container{system}, err + return &container{system: system}, err } // GetContainers gets a list of the containers on the system that match the query func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) { - return hcs.GetComputeSystems(q) + return hcs.GetComputeSystems(context.Background(), q) } // Start synchronously starts the container. func (container *container) Start() error { - return convertSystemError(container.system.Start(), container) + return convertSystemError(container.system.Start(context.Background()), container) } // Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds. func (container *container) Shutdown() error { - return convertSystemError(container.system.Shutdown(), container) + err := container.system.Shutdown(context.Background()) + if err != nil { + return convertSystemError(err, container) + } + return &ContainerError{Container: container, Err: ErrVmcomputeOperationPending, Operation: "hcsshim::ComputeSystem::Shutdown"} } // Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds. func (container *container) Terminate() error { - return convertSystemError(container.system.Terminate(), container) + err := container.system.Terminate(context.Background()) + if err != nil { + return convertSystemError(err, container) + } + return &ContainerError{Container: container, Err: ErrVmcomputeOperationPending, Operation: "hcsshim::ComputeSystem::Terminate"} } // Waits synchronously waits for the container to shutdown or terminate. func (container *container) Wait() error { - return convertSystemError(container.system.Wait(), container) + err := container.system.Wait() + if err == nil { + err = container.system.ExitError() + } + return convertSystemError(err, container) } // WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It // returns false if timeout occurs. -func (container *container) WaitTimeout(t time.Duration) error { - return convertSystemError(container.system.WaitTimeout(t), container) +func (container *container) WaitTimeout(timeout time.Duration) error { + container.waitOnce.Do(func() { + container.waitCh = make(chan struct{}) + go func() { + container.waitErr = container.Wait() + close(container.waitCh) + }() + }) + t := time.NewTimer(timeout) + defer t.Stop() + select { + case <-t.C: + return &ContainerError{Container: container, Err: ErrTimeout, Operation: "hcsshim::ComputeSystem::Wait"} + case <-container.waitCh: + return container.waitErr + } } // Pause pauses the execution of a container. func (container *container) Pause() error { - return convertSystemError(container.system.Pause(), container) + return convertSystemError(container.system.Pause(context.Background()), container) } // Resume resumes the execution of a container. func (container *container) Resume() error { - return convertSystemError(container.system.Resume(), container) + return convertSystemError(container.system.Resume(context.Background()), container) } // HasPendingUpdates returns true if the container has updates pending to install @@ -135,7 +166,7 @@ func (container *container) HasPendingUpdates() (bool, error) { // Statistics returns statistics for the container. This is a legacy v1 call func (container *container) Statistics() (Statistics, error) { - properties, err := container.system.Properties(schema1.PropertyTypeStatistics) + properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeStatistics) if err != nil { return Statistics{}, convertSystemError(err, container) } @@ -145,7 +176,7 @@ func (container *container) Statistics() (Statistics, error) { // ProcessList returns an array of ProcessListItems for the container. This is a legacy v1 call func (container *container) ProcessList() ([]ProcessListItem, error) { - properties, err := container.system.Properties(schema1.PropertyTypeProcessList) + properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeProcessList) if err != nil { return nil, convertSystemError(err, container) } @@ -155,7 +186,7 @@ func (container *container) ProcessList() ([]ProcessListItem, error) { // This is a legacy v1 call func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) { - properties, err := container.system.Properties(schema1.PropertyTypeMappedVirtualDisk) + properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeMappedVirtualDisk) if err != nil { return nil, convertSystemError(err, container) } @@ -165,20 +196,20 @@ func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskContr // CreateProcess launches a new process within the container. func (container *container) CreateProcess(c *ProcessConfig) (Process, error) { - p, err := container.system.CreateProcess(c) + p, err := container.system.CreateProcess(context.Background(), c) if err != nil { return nil, convertSystemError(err, container) } - return &process{p}, nil + return &process{p: p.(*hcs.Process)}, nil } // OpenProcess gets an interface to an existing process within the container. func (container *container) OpenProcess(pid int) (Process, error) { - p, err := container.system.OpenProcess(pid) + p, err := container.system.OpenProcess(context.Background(), pid) if err != nil { return nil, convertSystemError(err, container) } - return &process{p}, nil + return &process{p: p}, nil } // Close cleans up any state associated with the container but does not terminate or wait for it. @@ -188,5 +219,5 @@ func (container *container) Close() error { // Modify the System func (container *container) Modify(config *ResourceModificationRequestResponse) error { - return convertSystemError(container.system.Modify(config), container) + return convertSystemError(container.system.Modify(context.Background(), config), container) } diff --git a/vendor/github.com/Microsoft/hcsshim/errors.go b/vendor/github.com/Microsoft/hcsshim/errors.go index 63efa23c7a4e8..f367022e712ec 100644 --- a/vendor/github.com/Microsoft/hcsshim/errors.go +++ b/vendor/github.com/Microsoft/hcsshim/errors.go @@ -59,7 +59,7 @@ var ( // ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation ErrVmcomputeOperationInvalidState = hcs.ErrVmcomputeOperationInvalidState - // ErrProcNotFound is an error encountered when the the process cannot be found + // ErrProcNotFound is an error encountered when a procedure look up fails. ErrProcNotFound = hcs.ErrProcNotFound // ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2 @@ -83,7 +83,6 @@ type NetworkNotFoundError = hns.NetworkNotFoundError type ProcessError struct { Process *process Operation string - ExtraInfo string Err error Events []hcs.ErrorEvent } @@ -92,7 +91,6 @@ type ProcessError struct { type ContainerError struct { Container *container Operation string - ExtraInfo string Err error Events []hcs.ErrorEvent } @@ -125,22 +123,9 @@ func (e *ContainerError) Error() string { s += "\n" + ev.String() } - if e.ExtraInfo != "" { - s += " extra info: " + e.ExtraInfo - } - return s } -func makeContainerError(container *container, operation string, extraInfo string, err error) error { - // Don't double wrap errors - if _, ok := err.(*ContainerError); ok { - return err - } - containerError := &ContainerError{Container: container, Operation: operation, ExtraInfo: extraInfo, Err: err} - return containerError -} - func (e *ProcessError) Error() string { if e == nil { return "" @@ -171,19 +156,10 @@ func (e *ProcessError) Error() string { return s } -func makeProcessError(process *process, operation string, extraInfo string, err error) error { - // Don't double wrap errors - if _, ok := err.(*ProcessError); ok { - return err - } - processError := &ProcessError{Process: process, Operation: operation, ExtraInfo: extraInfo, Err: err} - return processError -} - // IsNotExist checks if an error is caused by the Container or Process not existing. // Note: Currently, ErrElementNotFound can mean that a Process has either // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist -// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +// will currently return true when the error is ErrElementNotFound. func IsNotExist(err error) bool { if _, ok := err.(EndpointNotFoundError); ok { return true @@ -216,7 +192,7 @@ func IsTimeout(err error) bool { // a Container or Process being already stopped. // Note: Currently, ErrElementNotFound can mean that a Process has either // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist -// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +// will currently return true when the error is ErrElementNotFound. func IsAlreadyStopped(err error) bool { return hcs.IsAlreadyStopped(getInnerError(err)) } @@ -230,6 +206,18 @@ func IsNotSupported(err error) bool { return hcs.IsNotSupported(getInnerError(err)) } +// IsOperationInvalidState returns true when err is caused by +// `ErrVmcomputeOperationInvalidState`. +func IsOperationInvalidState(err error) bool { + return hcs.IsOperationInvalidState(getInnerError(err)) +} + +// IsAccessIsDenied returns true when err is caused by +// `ErrVmcomputeOperationAccessIsDenied`. +func IsAccessIsDenied(err error) bool { + return hcs.IsAccessIsDenied(getInnerError(err)) +} + func getInnerError(err error) error { switch pe := err.(type) { case nil: @@ -244,7 +232,7 @@ func getInnerError(err error) error { func convertSystemError(err error, c *container) error { if serr, ok := err.(*hcs.SystemError); ok { - return &ContainerError{Container: c, Operation: serr.Op, ExtraInfo: serr.Extra, Err: serr.Err, Events: serr.Events} + return &ContainerError{Container: c, Operation: serr.Op, Err: serr.Err, Events: serr.Events} } return err } diff --git a/vendor/github.com/Microsoft/hcsshim/go.mod b/vendor/github.com/Microsoft/hcsshim/go.mod new file mode 100644 index 0000000000000..ffc9c2f387905 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/go.mod @@ -0,0 +1,31 @@ +module github.com/Microsoft/hcsshim + +go 1.13 + +require ( + github.com/Microsoft/go-winio v0.4.17 + github.com/cenkalti/backoff/v4 v4.1.1 + github.com/containerd/cgroups v1.0.1 + github.com/containerd/console v1.0.2 + github.com/containerd/containerd v1.4.9 + github.com/containerd/continuity v0.1.0 // indirect + github.com/containerd/fifo v1.0.0 // indirect + github.com/containerd/go-runc v1.0.0 + github.com/containerd/ttrpc v1.1.0 + github.com/containerd/typeurl v1.0.2 + github.com/gogo/protobuf v1.3.2 + github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d + github.com/pkg/errors v0.9.1 + github.com/sirupsen/logrus v1.8.1 + github.com/urfave/cli v1.22.2 + go.opencensus.io v0.22.3 + golang.org/x/sync v0.0.0-20201207232520-09787c993a3a + golang.org/x/sys v0.0.0-20210324051608-47abb6519492 + google.golang.org/grpc v1.33.2 + gotest.tools/v3 v3.0.3 // indirect +) + +replace ( + google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 + google.golang.org/grpc => google.golang.org/grpc v1.27.1 +) diff --git a/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go b/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go index f2eedbe3d3aee..9e0059447d9f4 100644 --- a/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go +++ b/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go @@ -6,6 +6,10 @@ import ( // HNSEndpoint represents a network endpoint in HNS type HNSEndpoint = hns.HNSEndpoint + +// HNSEndpointStats represent the stats for an networkendpoint in HNS +type HNSEndpointStats = hns.EndpointStats + // Namespace represents a Compartment. type Namespace = hns.Namespace @@ -38,11 +42,27 @@ func HNSListEndpointRequest() ([]HNSEndpoint, error) { // HotAttachEndpoint makes a HCS Call to attach the endpoint to the container func HotAttachEndpoint(containerID string, endpointID string) error { + endpoint, err := GetHNSEndpointByID(endpointID) + if err != nil { + return err + } + isAttached, err := endpoint.IsAttached(containerID) + if isAttached { + return err + } return modifyNetworkEndpoint(containerID, endpointID, Add) } // HotDetachEndpoint makes a HCS Call to detach the endpoint from the container func HotDetachEndpoint(containerID string, endpointID string) error { + endpoint, err := GetHNSEndpointByID(endpointID) + if err != nil { + return err + } + isAttached, err := endpoint.IsAttached(containerID) + if !isAttached { + return err + } return modifyNetworkEndpoint(containerID, endpointID, Remove) } @@ -91,3 +111,8 @@ func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) { func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { return hns.GetHNSEndpointByName(endpointName) } + +// GetHNSEndpointStats gets the endpoint stats by ID +func GetHNSEndpointStats(endpointName string) (*HNSEndpointStats, error) { + return hns.GetHNSEndpointStats(endpointName) +} diff --git a/vendor/github.com/Microsoft/hcsshim/hnspolicy.go b/vendor/github.com/Microsoft/hcsshim/hnspolicy.go index a3e03ff8fcf89..00ab2636449ea 100644 --- a/vendor/github.com/Microsoft/hcsshim/hnspolicy.go +++ b/vendor/github.com/Microsoft/hcsshim/hnspolicy.go @@ -21,8 +21,11 @@ const ( OutboundNat = hns.OutboundNat ExternalLoadBalancer = hns.ExternalLoadBalancer Route = hns.Route + Proxy = hns.Proxy ) +type ProxyPolicy = hns.ProxyPolicy + type NatPolicy = hns.NatPolicy type QosPolicy = hns.QosPolicy diff --git a/vendor/github.com/Microsoft/hcsshim/interface.go b/vendor/github.com/Microsoft/hcsshim/interface.go index 2724624fd5aa6..300eb59966874 100644 --- a/vendor/github.com/Microsoft/hcsshim/interface.go +++ b/vendor/github.com/Microsoft/hcsshim/interface.go @@ -4,7 +4,7 @@ import ( "io" "time" - "github.com/Microsoft/hcsshim/internal/schema1" + "github.com/Microsoft/hcsshim/internal/hcs/schema1" ) // ProcessConfig is used as both the input of Container.CreateProcess @@ -17,6 +17,11 @@ type MappedPipe = schema1.MappedPipe type HvRuntime = schema1.HvRuntime type MappedVirtualDisk = schema1.MappedVirtualDisk +// AssignedDevice represents a device that has been directly assigned to a container +// +// NOTE: Support added in RS5 +type AssignedDevice = schema1.AssignedDevice + // ContainerConfig is used as both the input of CreateContainer // and to convert the parameters to JSON for passing onto the HCS type ContainerConfig = schema1.ContainerConfig diff --git a/vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go b/vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go new file mode 100644 index 0000000000000..27a62a7238618 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/cow/cow.go @@ -0,0 +1,91 @@ +package cow + +import ( + "context" + "io" + + "github.com/Microsoft/hcsshim/internal/hcs/schema1" + hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" +) + +// Process is the interface for an OS process running in a container or utility VM. +type Process interface { + // Close releases resources associated with the process and closes the + // writer and readers returned by Stdio. Depending on the implementation, + // this may also terminate the process. + Close() error + // CloseStdin causes the process's stdin handle to receive EOF/EPIPE/whatever + // is appropriate to indicate that no more data is available. + CloseStdin(ctx context.Context) error + // CloseStdout closes the stdout connection to the process. It is used to indicate + // that we are done receiving output on the shim side. + CloseStdout(ctx context.Context) error + // CloseStderr closes the stderr connection to the process. It is used to indicate + // that we are done receiving output on the shim side. + CloseStderr(ctx context.Context) error + // Pid returns the process ID. + Pid() int + // Stdio returns the stdio streams for a process. These may be nil if a stream + // was not requested during CreateProcess. + Stdio() (_ io.Writer, _ io.Reader, _ io.Reader) + // ResizeConsole resizes the virtual terminal associated with the process. + ResizeConsole(ctx context.Context, width, height uint16) error + // Kill sends a SIGKILL or equivalent signal to the process and returns whether + // the signal was delivered. It does not wait for the process to terminate. + Kill(ctx context.Context) (bool, error) + // Signal sends a signal to the process and returns whether the signal was + // delivered. The input is OS specific (either + // guestrequest.SignalProcessOptionsWCOW or + // guestrequest.SignalProcessOptionsLCOW). It does not wait for the process + // to terminate. + Signal(ctx context.Context, options interface{}) (bool, error) + // Wait waits for the process to complete, or for a connection to the process to be + // terminated by some error condition (including calling Close). + Wait() error + // ExitCode returns the exit code of the process. Returns an error if the process is + // not running. + ExitCode() (int, error) +} + +// ProcessHost is the interface for creating processes. +type ProcessHost interface { + // CreateProcess creates a process. The configuration is host specific + // (either hcsschema.ProcessParameters or lcow.ProcessParameters). + CreateProcess(ctx context.Context, config interface{}) (Process, error) + // OS returns the host's operating system, "linux" or "windows". + OS() string + // IsOCI specifies whether this is an OCI-compliant process host. If true, + // then the configuration passed to CreateProcess should have an OCI process + // spec (or nil if this is the initial process in an OCI container). + // Otherwise, it should have the HCS-specific process parameters. + IsOCI() bool +} + +// Container is the interface for container objects, either running on the host or +// in a utility VM. +type Container interface { + ProcessHost + // Close releases the resources associated with the container. Depending on + // the implementation, this may also terminate the container. + Close() error + // ID returns the container ID. + ID() string + // Properties returns the requested container properties targeting a V1 schema container. + Properties(ctx context.Context, types ...schema1.PropertyType) (*schema1.ContainerProperties, error) + // PropertiesV2 returns the requested container properties targeting a V2 schema container. + PropertiesV2(ctx context.Context, types ...hcsschema.PropertyType) (*hcsschema.Properties, error) + // Start starts a container. + Start(ctx context.Context) error + // Shutdown sends a shutdown request to the container (but does not wait for + // the shutdown to complete). + Shutdown(ctx context.Context) error + // Terminate sends a terminate request to the container (but does not wait + // for the terminate to complete). + Terminate(ctx context.Context) error + // Wait waits for the container to terminate, or for the connection to the + // container to be terminated by some error condition (including calling + // Close). + Wait() error + // Modify sends a request to modify container resources + Modify(ctx context.Context, config interface{}) error +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go b/vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go deleted file mode 100644 index e9e45c0306dc0..0000000000000 --- a/vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go +++ /dev/null @@ -1,69 +0,0 @@ -package guid - -import ( - "crypto/rand" - "encoding/json" - "fmt" - "io" - "strconv" - "strings" -) - -var _ = (json.Marshaler)(&GUID{}) -var _ = (json.Unmarshaler)(&GUID{}) - -type GUID [16]byte - -func New() GUID { - g := GUID{} - _, err := io.ReadFull(rand.Reader, g[:]) - if err != nil { - panic(err) - } - return g -} - -func (g GUID) String() string { - return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x-%02x", g[3], g[2], g[1], g[0], g[5], g[4], g[7], g[6], g[8:10], g[10:]) -} - -func FromString(s string) GUID { - if len(s) != 36 { - panic(fmt.Sprintf("invalid GUID length: %d", len(s))) - } - if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { - panic("invalid GUID format") - } - indexOrder := [16]int{ - 0, 2, 4, 6, - 9, 11, - 14, 16, - 19, 21, - 24, 26, 28, 30, 32, 34, - } - byteOrder := [16]int{ - 3, 2, 1, 0, - 5, 4, - 7, 6, - 8, 9, - 10, 11, 12, 13, 14, 15, - } - var g GUID - for i, x := range indexOrder { - b, err := strconv.ParseInt(s[x:x+2], 16, 16) - if err != nil { - panic(err) - } - g[byteOrder[i]] = byte(b) - } - return g -} - -func (g GUID) MarshalJSON() ([]byte, error) { - return json.Marshal(g.String()) -} - -func (g *GUID) UnmarshalJSON(data []byte) error { - *g = FromString(strings.Trim(string(data), "\"")) - return nil -} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go index e41c40ec8fe7f..d13772b0301eb 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go @@ -1,25 +1,38 @@ package hcs import ( + "fmt" "sync" "syscall" "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/logfields" + "github.com/Microsoft/hcsshim/internal/vmcompute" + "github.com/sirupsen/logrus" ) var ( nextCallback uintptr - callbackMap = map[uintptr]*notifcationWatcherContext{} + callbackMap = map[uintptr]*notificationWatcherContext{} callbackMapLock = sync.RWMutex{} notificationWatcherCallback = syscall.NewCallback(notificationWatcher) // Notifications for HCS_SYSTEM handles - hcsNotificationSystemExited hcsNotification = 0x00000001 - hcsNotificationSystemCreateCompleted hcsNotification = 0x00000002 - hcsNotificationSystemStartCompleted hcsNotification = 0x00000003 - hcsNotificationSystemPauseCompleted hcsNotification = 0x00000004 - hcsNotificationSystemResumeCompleted hcsNotification = 0x00000005 + hcsNotificationSystemExited hcsNotification = 0x00000001 + hcsNotificationSystemCreateCompleted hcsNotification = 0x00000002 + hcsNotificationSystemStartCompleted hcsNotification = 0x00000003 + hcsNotificationSystemPauseCompleted hcsNotification = 0x00000004 + hcsNotificationSystemResumeCompleted hcsNotification = 0x00000005 + hcsNotificationSystemCrashReport hcsNotification = 0x00000006 + hcsNotificationSystemSiloJobCreated hcsNotification = 0x00000007 + hcsNotificationSystemSaveCompleted hcsNotification = 0x00000008 + hcsNotificationSystemRdpEnhancedModeStateChanged hcsNotification = 0x00000009 + hcsNotificationSystemShutdownFailed hcsNotification = 0x0000000A + hcsNotificationSystemGetPropertiesCompleted hcsNotification = 0x0000000B + hcsNotificationSystemModifyCompleted hcsNotification = 0x0000000C + hcsNotificationSystemCrashInitiated hcsNotification = 0x0000000D + hcsNotificationSystemGuestConnectionClosed hcsNotification = 0x0000000E // Notifications for HCS_PROCESS handles hcsNotificationProcessExited hcsNotification = 0x00010000 @@ -30,35 +43,91 @@ var ( ) type hcsNotification uint32 + +func (hn hcsNotification) String() string { + switch hn { + case hcsNotificationSystemExited: + return "SystemExited" + case hcsNotificationSystemCreateCompleted: + return "SystemCreateCompleted" + case hcsNotificationSystemStartCompleted: + return "SystemStartCompleted" + case hcsNotificationSystemPauseCompleted: + return "SystemPauseCompleted" + case hcsNotificationSystemResumeCompleted: + return "SystemResumeCompleted" + case hcsNotificationSystemCrashReport: + return "SystemCrashReport" + case hcsNotificationSystemSiloJobCreated: + return "SystemSiloJobCreated" + case hcsNotificationSystemSaveCompleted: + return "SystemSaveCompleted" + case hcsNotificationSystemRdpEnhancedModeStateChanged: + return "SystemRdpEnhancedModeStateChanged" + case hcsNotificationSystemShutdownFailed: + return "SystemShutdownFailed" + case hcsNotificationSystemGetPropertiesCompleted: + return "SystemGetPropertiesCompleted" + case hcsNotificationSystemModifyCompleted: + return "SystemModifyCompleted" + case hcsNotificationSystemCrashInitiated: + return "SystemCrashInitiated" + case hcsNotificationSystemGuestConnectionClosed: + return "SystemGuestConnectionClosed" + case hcsNotificationProcessExited: + return "ProcessExited" + case hcsNotificationInvalid: + return "Invalid" + case hcsNotificationServiceDisconnect: + return "ServiceDisconnect" + default: + return fmt.Sprintf("Unknown: %d", hn) + } +} + type notificationChannel chan error -type notifcationWatcherContext struct { +type notificationWatcherContext struct { channels notificationChannels - handle hcsCallback + handle vmcompute.HcsCallback + + systemID string + processID int } type notificationChannels map[hcsNotification]notificationChannel -func newChannels() notificationChannels { +func newSystemChannels() notificationChannels { channels := make(notificationChannels) + for _, notif := range []hcsNotification{ + hcsNotificationServiceDisconnect, + hcsNotificationSystemExited, + hcsNotificationSystemCreateCompleted, + hcsNotificationSystemStartCompleted, + hcsNotificationSystemPauseCompleted, + hcsNotificationSystemResumeCompleted, + hcsNotificationSystemSaveCompleted, + } { + channels[notif] = make(notificationChannel, 1) + } + return channels +} - channels[hcsNotificationSystemExited] = make(notificationChannel, 1) - channels[hcsNotificationSystemCreateCompleted] = make(notificationChannel, 1) - channels[hcsNotificationSystemStartCompleted] = make(notificationChannel, 1) - channels[hcsNotificationSystemPauseCompleted] = make(notificationChannel, 1) - channels[hcsNotificationSystemResumeCompleted] = make(notificationChannel, 1) - channels[hcsNotificationProcessExited] = make(notificationChannel, 1) - channels[hcsNotificationServiceDisconnect] = make(notificationChannel, 1) +func newProcessChannels() notificationChannels { + channels := make(notificationChannels) + for _, notif := range []hcsNotification{ + hcsNotificationServiceDisconnect, + hcsNotificationProcessExited, + } { + channels[notif] = make(notificationChannel, 1) + } return channels } + func closeChannels(channels notificationChannels) { - close(channels[hcsNotificationSystemExited]) - close(channels[hcsNotificationSystemCreateCompleted]) - close(channels[hcsNotificationSystemStartCompleted]) - close(channels[hcsNotificationSystemPauseCompleted]) - close(channels[hcsNotificationSystemResumeCompleted]) - close(channels[hcsNotificationProcessExited]) - close(channels[hcsNotificationServiceDisconnect]) + for _, c := range channels { + close(c) + } } func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr { @@ -75,7 +144,18 @@ func notificationWatcher(notificationType hcsNotification, callbackNumber uintpt return 0 } - context.channels[notificationType] <- result + log := logrus.WithFields(logrus.Fields{ + "notification-type": notificationType.String(), + "system-id": context.systemID, + }) + if context.processID != 0 { + log.Data[logfields.ProcessID] = context.processID + } + log.Debug("HCS notification") + + if channel, ok := context.channels[notificationType]; ok { + channel <- result + } return 0 } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go deleted file mode 100644 index 3669c34aa2cc1..0000000000000 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go +++ /dev/null @@ -1,7 +0,0 @@ -package hcs - -import "C" - -// This import is needed to make the library compile as CGO because HCSSHIM -// only works with CGO due to callbacks from HCS comming back from a C thread -// which is not supported without CGO. See https://github.com/golang/go/issues/10973 diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go index 7471f5cc13f73..644f0ab711f1b 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go @@ -1,13 +1,14 @@ package hcs import ( + "context" "encoding/json" "errors" "fmt" + "net" "syscall" - "github.com/Microsoft/hcsshim/internal/interop" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/log" ) var ( @@ -59,7 +60,7 @@ var ( // ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105) - // ErrProcNotFound is an error encountered when the the process cannot be found + // ErrProcNotFound is an error encountered when a procedure look up fails. ErrProcNotFound = syscall.Errno(0x7f) // ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2 @@ -72,6 +73,9 @@ var ( // ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b) + // ErrVmcomputeUnexpectedExit is an error encountered when the compute system terminates unexpectedly + ErrVmcomputeUnexpectedExit = syscall.Errno(0xC0370106) + // ErrNotSupported is an error encountered when hcs doesn't support the request ErrPlatformNotSupported = errors.New("unsupported platform request") ) @@ -113,13 +117,11 @@ func (ev *ErrorEvent) String() string { return evs } -func processHcsResult(resultp *uint16) []ErrorEvent { - if resultp != nil { - resultj := interop.ConvertAndFreeCoTaskMemString(resultp) - logrus.Debugf("Result: %s", resultj) +func processHcsResult(ctx context.Context, resultJSON string) []ErrorEvent { + if resultJSON != "" { result := &hcsResult{} - if err := json.Unmarshal([]byte(resultj), result); err != nil { - logrus.Warnf("Could not unmarshal HCS result %s: %s", resultj, err) + if err := json.Unmarshal([]byte(resultJSON), result); err != nil { + log.G(ctx).WithError(err).Warning("Could not unmarshal HCS result") return nil } return result.ErrorEvents @@ -133,6 +135,8 @@ type HcsError struct { Events []ErrorEvent } +var _ net.Error = &HcsError{} + func (e *HcsError) Error() string { s := e.Op + ": " + e.Err.Error() for _, ev := range e.Events { @@ -141,6 +145,16 @@ func (e *HcsError) Error() string { return s } +func (e *HcsError) Temporary() bool { + err, ok := e.Err.(net.Error) + return ok && err.Temporary() +} + +func (e *HcsError) Timeout() bool { + err, ok := e.Err.(net.Error) + return ok && err.Timeout() +} + // ProcessError is an error encountered in HCS during an operation on a Process object type ProcessError struct { SystemID string @@ -150,27 +164,37 @@ type ProcessError struct { Events []ErrorEvent } +var _ net.Error = &ProcessError{} + // SystemError is an error encountered in HCS during an operation on a Container object type SystemError struct { ID string Op string Err error - Extra string Events []ErrorEvent } +var _ net.Error = &SystemError{} + func (e *SystemError) Error() string { s := e.Op + " " + e.ID + ": " + e.Err.Error() for _, ev := range e.Events { s += "\n" + ev.String() } - if e.Extra != "" { - s += "\n(extra info: " + e.Extra + ")" - } return s } -func makeSystemError(system *System, op string, extra string, err error, events []ErrorEvent) error { +func (e *SystemError) Temporary() bool { + err, ok := e.Err.(net.Error) + return ok && err.Temporary() +} + +func (e *SystemError) Timeout() bool { + err, ok := e.Err.(net.Error) + return ok && err.Timeout() +} + +func makeSystemError(system *System, op string, err error, events []ErrorEvent) error { // Don't double wrap errors if _, ok := err.(*SystemError); ok { return err @@ -178,7 +202,6 @@ func makeSystemError(system *System, op string, extra string, err error, events return &SystemError{ ID: system.ID(), Op: op, - Extra: extra, Err: err, Events: events, } @@ -192,6 +215,16 @@ func (e *ProcessError) Error() string { return s } +func (e *ProcessError) Temporary() bool { + err, ok := e.Err.(net.Error) + return ok && err.Temporary() +} + +func (e *ProcessError) Timeout() bool { + err, ok := e.Err.(net.Error) + return ok && err.Timeout() +} + func makeProcessError(process *Process, op string, err error, events []ErrorEvent) error { // Don't double wrap errors if _, ok := err.(*ProcessError); ok { @@ -209,12 +242,11 @@ func makeProcessError(process *Process, op string, err error, events []ErrorEven // IsNotExist checks if an error is caused by the Container or Process not existing. // Note: Currently, ErrElementNotFound can mean that a Process has either // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist -// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +// will currently return true when the error is ErrElementNotFound. func IsNotExist(err error) bool { err = getInnerError(err) return err == ErrComputeSystemDoesNotExist || - err == ErrElementNotFound || - err == ErrProcNotFound + err == ErrElementNotFound } // IsAlreadyClosed checks if an error is caused by the Container or Process having been @@ -234,6 +266,9 @@ func IsPending(err error) bool { // IsTimeout returns a boolean indicating whether the error is caused by // a timeout waiting for the operation to complete. func IsTimeout(err error) bool { + if err, ok := err.(net.Error); ok && err.Timeout() { + return true + } err = getInnerError(err) return err == ErrTimeout } @@ -242,12 +277,11 @@ func IsTimeout(err error) bool { // a Container or Process being already stopped. // Note: Currently, ErrElementNotFound can mean that a Process has either // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist -// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +// will currently return true when the error is ErrElementNotFound. func IsAlreadyStopped(err error) bool { err = getInnerError(err) return err == ErrVmcomputeAlreadyStopped || - err == ErrElementNotFound || - err == ErrProcNotFound + err == ErrElementNotFound } // IsNotSupported returns a boolean indicating whether the error is caused by @@ -264,6 +298,20 @@ func IsNotSupported(err error) bool { err == ErrVmcomputeUnknownMessage } +// IsOperationInvalidState returns true when err is caused by +// `ErrVmcomputeOperationInvalidState`. +func IsOperationInvalidState(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeOperationInvalidState +} + +// IsAccessIsDenied returns true when err is caused by +// `ErrVmcomputeOperationAccessIsDenied`. +func IsAccessIsDenied(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeOperationAccessIsDenied +} + func getInnerError(err error) error { switch pe := err.(type) { case nil: diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go deleted file mode 100644 index b8e30eba174ef..0000000000000 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go +++ /dev/null @@ -1,47 +0,0 @@ -// Shim for the Host Compute Service (HCS) to manage Windows Server -// containers and Hyper-V containers. - -package hcs - -import ( - "syscall" -) - -//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go hcs.go - -//sys hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) = vmcompute.HcsEnumerateComputeSystems? -//sys hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem? -//sys hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsOpenComputeSystem? -//sys hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) = vmcompute.HcsCloseComputeSystem? -//sys hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem? -//sys hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem? -//sys hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem? -//sys hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem? -//sys hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem? -//sys hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetComputeSystemProperties? -//sys hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) = vmcompute.HcsModifyComputeSystem? -//sys hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterComputeSystemCallback? -//sys hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterComputeSystemCallback? - -//sys hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsCreateProcess? -//sys hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsOpenProcess? -//sys hcsCloseProcess(process hcsProcess) (hr error) = vmcompute.HcsCloseProcess? -//sys hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) = vmcompute.HcsTerminateProcess? -//sys hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) = vmcompute.HcsGetProcessInfo? -//sys hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) = vmcompute.HcsGetProcessProperties? -//sys hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) = vmcompute.HcsModifyProcess? -//sys hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetServiceProperties? -//sys hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback? -//sys hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback? - -type hcsSystem syscall.Handle -type hcsProcess syscall.Handle -type hcsCallback syscall.Handle - -type hcsProcessInformation struct { - ProcessId uint32 - Reserved uint32 - StdInput syscall.Handle - StdOutput syscall.Handle - StdError syscall.Handle -} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go index 8294d66d7b489..8f203466887ab 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go @@ -1,31 +1,45 @@ package hcs import ( + "context" "encoding/json" - "fmt" "io" "sync" "syscall" "time" - "github.com/Microsoft/hcsshim/internal/interop" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/log" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/internal/vmcompute" + "go.opencensus.io/trace" ) // ContainerError is an error encountered in HCS type Process struct { handleLock sync.RWMutex - handle hcsProcess + handle vmcompute.HcsProcess processID int system *System - cachedPipes *cachedPipes + hasCachedStdio bool + stdioLock sync.Mutex + stdin io.WriteCloser + stdout io.ReadCloser + stderr io.ReadCloser callbackNumber uintptr + + closedWaitOnce sync.Once + waitBlock chan struct{} + exitCode int + waitError error } -type cachedPipes struct { - stdIn syscall.Handle - stdOut syscall.Handle - stdErr syscall.Handle +func newProcess(process vmcompute.HcsProcess, processID int, computeSystem *System) *Process { + return &Process{ + handle: process, + processID: processID, + system: computeSystem, + waitBlock: make(chan struct{}), + } } type processModifyRequest struct { @@ -43,18 +57,14 @@ type closeHandle struct { Handle string } -type ProcessStatus struct { +type processStatus struct { ProcessID uint32 Exited bool ExitCode uint32 LastWaitResult int32 } -const ( - stdIn string = "StdIn" - stdOut string = "StdOut" - stdErr string = "StdErr" -) +const stdIn string = "StdIn" const ( modifyConsoleSize string = "ConsoleSize" @@ -71,70 +81,155 @@ func (process *Process) SystemID() string { return process.system.ID() } -// Kill signals the process to terminate but does not wait for it to finish terminating. -func (process *Process) Kill() error { +func (process *Process) processSignalResult(ctx context.Context, err error) (bool, error) { + switch err { + case nil: + return true, nil + case ErrVmcomputeOperationInvalidState, ErrComputeSystemDoesNotExist, ErrElementNotFound: + select { + case <-process.waitBlock: + // The process exit notification has already arrived. + default: + // The process should be gone, but we have not received the notification. + // After a second, force unblock the process wait to work around a possible + // deadlock in the HCS. + go func() { + time.Sleep(time.Second) + process.closedWaitOnce.Do(func() { + log.G(ctx).WithError(err).Warn("force unblocking process waits") + process.exitCode = -1 + process.waitError = err + close(process.waitBlock) + }) + }() + } + return false, nil + default: + return false, err + } +} + +// Signal signals the process with `options`. +// +// For LCOW `guestrequest.SignalProcessOptionsLCOW`. +// +// For WCOW `guestrequest.SignalProcessOptionsWCOW`. +func (process *Process) Signal(ctx context.Context, options interface{}) (bool, error) { process.handleLock.RLock() defer process.handleLock.RUnlock() - operation := "Kill" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) + + operation := "hcs::Process::Signal" if process.handle == 0 { - return makeProcessError(process, operation, ErrAlreadyClosed, nil) + return false, makeProcessError(process, operation, ErrAlreadyClosed, nil) } - var resultp *uint16 - completed := false - go syscallWatcher(fmt.Sprintf("TerminateProcess %s: %d", process.SystemID(), process.Pid()), &completed) - err := hcsTerminateProcess(process.handle, &resultp) - completed = true - events := processHcsResult(resultp) + optionsb, err := json.Marshal(options) if err != nil { - return makeProcessError(process, operation, err, events) + return false, err } - logrus.Debugf(title+" succeeded processid=%d", process.processID) - return nil + resultJSON, err := vmcompute.HcsSignalProcess(ctx, process.handle, string(optionsb)) + events := processHcsResult(ctx, resultJSON) + delivered, err := process.processSignalResult(ctx, err) + if err != nil { + err = makeProcessError(process, operation, err, events) + } + return delivered, err } -// Wait waits for the process to exit. -func (process *Process) Wait() error { - operation := "Wait" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) +// Kill signals the process to terminate but does not wait for it to finish terminating. +func (process *Process) Kill(ctx context.Context) (bool, error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() - err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, nil) - if err != nil { - return makeProcessError(process, operation, err, nil) + operation := "hcs::Process::Kill" + + if process.handle == 0 { + return false, makeProcessError(process, operation, ErrAlreadyClosed, nil) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) - return nil + resultJSON, err := vmcompute.HcsTerminateProcess(ctx, process.handle) + events := processHcsResult(ctx, resultJSON) + delivered, err := process.processSignalResult(ctx, err) + if err != nil { + err = makeProcessError(process, operation, err, events) + } + return delivered, err } -// WaitTimeout waits for the process to exit or the duration to elapse. It returns -// false if timeout occurs. -func (process *Process) WaitTimeout(timeout time.Duration) error { - operation := "WaitTimeout" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) +// waitBackground waits for the process exit notification. Once received sets +// `process.waitError` (if any) and unblocks all `Wait` calls. +// +// This MUST be called exactly once per `process.handle` but `Wait` is safe to +// call multiple times. +func (process *Process) waitBackground() { + operation := "hcs::Process::waitBackground" + ctx, span := trace.StartSpan(context.Background(), operation) + defer span.End() + span.AddAttributes( + trace.StringAttribute("cid", process.SystemID()), + trace.Int64Attribute("pid", int64(process.processID))) - err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, &timeout) + var ( + err error + exitCode = -1 + propertiesJSON string + resultJSON string + ) + + err = waitForNotification(ctx, process.callbackNumber, hcsNotificationProcessExited, nil) if err != nil { - return makeProcessError(process, operation, err, nil) + err = makeProcessError(process, operation, err, nil) + log.G(ctx).WithError(err).Error("failed wait") + } else { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + // Make sure we didnt race with Close() here + if process.handle != 0 { + propertiesJSON, resultJSON, err = vmcompute.HcsGetProcessProperties(ctx, process.handle) + events := processHcsResult(ctx, resultJSON) + if err != nil { + err = makeProcessError(process, operation, err, events) //nolint:ineffassign + } else { + properties := &processStatus{} + err = json.Unmarshal([]byte(propertiesJSON), properties) + if err != nil { + err = makeProcessError(process, operation, err, nil) //nolint:ineffassign + } else { + if properties.LastWaitResult != 0 { + log.G(ctx).WithField("wait-result", properties.LastWaitResult).Warning("non-zero last wait result") + } else { + exitCode = int(properties.ExitCode) + } + } + } + } } + log.G(ctx).WithField("exitCode", exitCode).Debug("process exited") + + process.closedWaitOnce.Do(func() { + process.exitCode = exitCode + process.waitError = err + close(process.waitBlock) + }) + oc.SetSpanStatus(span, err) +} - logrus.Debugf(title+" succeeded processid=%d", process.processID) - return nil +// Wait waits for the process to exit. If the process has already exited returns +// the pervious error (if any). +func (process *Process) Wait() error { + <-process.waitBlock + return process.waitError } // ResizeConsole resizes the console of the process. -func (process *Process) ResizeConsole(width, height uint16) error { +func (process *Process) ResizeConsole(ctx context.Context, width, height uint16) error { process.handleLock.RLock() defer process.handleLock.RUnlock() - operation := "ResizeConsole" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) + + operation := "hcs::Process::ResizeConsole" if process.handle == 0 { return makeProcessError(process, operation, ErrAlreadyClosed, nil) @@ -153,130 +248,86 @@ func (process *Process) ResizeConsole(width, height uint16) error { return err } - modifyRequestStr := string(modifyRequestb) - - var resultp *uint16 - err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp) - events := processHcsResult(resultp) + resultJSON, err := vmcompute.HcsModifyProcess(ctx, process.handle, string(modifyRequestb)) + events := processHcsResult(ctx, resultJSON) if err != nil { return makeProcessError(process, operation, err, events) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) return nil } -func (process *Process) Properties() (*ProcessStatus, error) { - process.handleLock.RLock() - defer process.handleLock.RUnlock() - operation := "Properties" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) - - if process.handle == 0 { - return nil, makeProcessError(process, operation, ErrAlreadyClosed, nil) - } - - var ( - resultp *uint16 - propertiesp *uint16 - ) - completed := false - go syscallWatcher(fmt.Sprintf("GetProcessProperties %s: %d", process.SystemID(), process.Pid()), &completed) - err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp) - completed = true - events := processHcsResult(resultp) - if err != nil { - return nil, makeProcessError(process, operation, err, events) - } - - if propertiesp == nil { - return nil, ErrUnexpectedValue - } - propertiesRaw := interop.ConvertAndFreeCoTaskMemBytes(propertiesp) - - properties := &ProcessStatus{} - if err := json.Unmarshal(propertiesRaw, properties); err != nil { - return nil, makeProcessError(process, operation, err, nil) - } - - logrus.Debugf(title+" succeeded processid=%d, properties=%s", process.processID, propertiesRaw) - return properties, nil -} - // ExitCode returns the exit code of the process. The process must have // already terminated. func (process *Process) ExitCode() (int, error) { - operation := "ExitCode" - properties, err := process.Properties() - if err != nil { - return 0, makeProcessError(process, operation, err, nil) - } - - if properties.Exited == false { - return 0, makeProcessError(process, operation, ErrInvalidProcessState, nil) - } - - if properties.LastWaitResult != 0 { - return 0, makeProcessError(process, operation, syscall.Errno(properties.LastWaitResult), nil) + select { + case <-process.waitBlock: + if process.waitError != nil { + return -1, process.waitError + } + return process.exitCode, nil + default: + return -1, makeProcessError(process, "hcs::Process::ExitCode", ErrInvalidProcessState, nil) } - - return int(properties.ExitCode), nil } -// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing -// these pipes does not close the underlying pipes; it should be possible to -// call this multiple times to get multiple interfaces. -func (process *Process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) { +// StdioLegacy returns the stdin, stdout, and stderr pipes, respectively. Closing +// these pipes does not close the underlying pipes. Once returned, these pipes +// are the responsibility of the caller to close. +func (process *Process) StdioLegacy() (_ io.WriteCloser, _ io.ReadCloser, _ io.ReadCloser, err error) { + operation := "hcs::Process::StdioLegacy" + ctx, span := trace.StartSpan(context.Background(), operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("cid", process.SystemID()), + trace.Int64Attribute("pid", int64(process.processID))) + process.handleLock.RLock() defer process.handleLock.RUnlock() - operation := "Stdio" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) if process.handle == 0 { return nil, nil, nil, makeProcessError(process, operation, ErrAlreadyClosed, nil) } - var stdIn, stdOut, stdErr syscall.Handle - - if process.cachedPipes == nil { - var ( - processInfo hcsProcessInformation - resultp *uint16 - ) - err := hcsGetProcessInfo(process.handle, &processInfo, &resultp) - events := processHcsResult(resultp) - if err != nil { - return nil, nil, nil, makeProcessError(process, operation, err, events) - } - - stdIn, stdOut, stdErr = processInfo.StdInput, processInfo.StdOutput, processInfo.StdError - } else { - // Use cached pipes - stdIn, stdOut, stdErr = process.cachedPipes.stdIn, process.cachedPipes.stdOut, process.cachedPipes.stdErr + process.stdioLock.Lock() + defer process.stdioLock.Unlock() + if process.hasCachedStdio { + stdin, stdout, stderr := process.stdin, process.stdout, process.stderr + process.stdin, process.stdout, process.stderr = nil, nil, nil + process.hasCachedStdio = false + return stdin, stdout, stderr, nil + } - // Invalidate the cache - process.cachedPipes = nil + processInfo, resultJSON, err := vmcompute.HcsGetProcessInfo(ctx, process.handle) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, nil, nil, makeProcessError(process, operation, err, events) } - pipes, err := makeOpenFiles([]syscall.Handle{stdIn, stdOut, stdErr}) + pipes, err := makeOpenFiles([]syscall.Handle{processInfo.StdInput, processInfo.StdOutput, processInfo.StdError}) if err != nil { return nil, nil, nil, makeProcessError(process, operation, err, nil) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) return pipes[0], pipes[1], pipes[2], nil } +// Stdio returns the stdin, stdout, and stderr pipes, respectively. +// To close them, close the process handle. +func (process *Process) Stdio() (stdin io.Writer, stdout, stderr io.Reader) { + process.stdioLock.Lock() + defer process.stdioLock.Unlock() + return process.stdin, process.stdout, process.stderr +} + // CloseStdin closes the write side of the stdin pipe so that the process is // notified on the read side that there is no more data in stdin. -func (process *Process) CloseStdin() error { +func (process *Process) CloseStdin(ctx context.Context) error { process.handleLock.RLock() defer process.handleLock.RUnlock() - operation := "CloseStdin" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) + + operation := "hcs::Process::CloseStdin" if process.handle == 0 { return makeProcessError(process, operation, ErrAlreadyClosed, nil) @@ -294,100 +345,177 @@ func (process *Process) CloseStdin() error { return err } - modifyRequestStr := string(modifyRequestb) - - var resultp *uint16 - err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp) - events := processHcsResult(resultp) + resultJSON, err := vmcompute.HcsModifyProcess(ctx, process.handle, string(modifyRequestb)) + events := processHcsResult(ctx, resultJSON) if err != nil { return makeProcessError(process, operation, err, events) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) + process.stdioLock.Lock() + if process.stdin != nil { + process.stdin.Close() + process.stdin = nil + } + process.stdioLock.Unlock() + + return nil +} + +func (process *Process) CloseStdout(ctx context.Context) (err error) { + ctx, span := trace.StartSpan(ctx, "hcs::Process::CloseStdout") //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("cid", process.SystemID()), + trace.Int64Attribute("pid", int64(process.processID))) + + process.handleLock.Lock() + defer process.handleLock.Unlock() + + if process.handle == 0 { + return nil + } + + process.stdioLock.Lock() + defer process.stdioLock.Unlock() + if process.stdout != nil { + process.stdout.Close() + process.stdout = nil + } + return nil +} + +func (process *Process) CloseStderr(ctx context.Context) (err error) { + ctx, span := trace.StartSpan(ctx, "hcs::Process::CloseStderr") //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("cid", process.SystemID()), + trace.Int64Attribute("pid", int64(process.processID))) + + process.handleLock.Lock() + defer process.handleLock.Unlock() + + if process.handle == 0 { + return nil + } + + process.stdioLock.Lock() + defer process.stdioLock.Unlock() + if process.stderr != nil { + process.stderr.Close() + process.stderr = nil + + } return nil } // Close cleans up any state associated with the process but does not kill // or wait on it. -func (process *Process) Close() error { +func (process *Process) Close() (err error) { + operation := "hcs::Process::Close" + ctx, span := trace.StartSpan(context.Background(), operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("cid", process.SystemID()), + trace.Int64Attribute("pid", int64(process.processID))) + process.handleLock.Lock() defer process.handleLock.Unlock() - operation := "Close" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) // Don't double free this if process.handle == 0 { return nil } - if err := process.unregisterCallback(); err != nil { + process.stdioLock.Lock() + if process.stdin != nil { + process.stdin.Close() + process.stdin = nil + } + if process.stdout != nil { + process.stdout.Close() + process.stdout = nil + } + if process.stderr != nil { + process.stderr.Close() + process.stderr = nil + } + process.stdioLock.Unlock() + + if err = process.unregisterCallback(ctx); err != nil { return makeProcessError(process, operation, err, nil) } - if err := hcsCloseProcess(process.handle); err != nil { + if err = vmcompute.HcsCloseProcess(ctx, process.handle); err != nil { return makeProcessError(process, operation, err, nil) } process.handle = 0 + process.closedWaitOnce.Do(func() { + process.exitCode = -1 + process.waitError = ErrAlreadyClosed + close(process.waitBlock) + }) - logrus.Debugf(title+" succeeded processid=%d", process.processID) return nil } -func (process *Process) registerCallback() error { - context := ¬ifcationWatcherContext{ - channels: newChannels(), +func (process *Process) registerCallback(ctx context.Context) error { + callbackContext := ¬ificationWatcherContext{ + channels: newProcessChannels(), + systemID: process.SystemID(), + processID: process.processID, } callbackMapLock.Lock() callbackNumber := nextCallback nextCallback++ - callbackMap[callbackNumber] = context + callbackMap[callbackNumber] = callbackContext callbackMapLock.Unlock() - var callbackHandle hcsCallback - err := hcsRegisterProcessCallback(process.handle, notificationWatcherCallback, callbackNumber, &callbackHandle) + callbackHandle, err := vmcompute.HcsRegisterProcessCallback(ctx, process.handle, notificationWatcherCallback, callbackNumber) if err != nil { return err } - context.handle = callbackHandle + callbackContext.handle = callbackHandle process.callbackNumber = callbackNumber return nil } -func (process *Process) unregisterCallback() error { +func (process *Process) unregisterCallback(ctx context.Context) error { callbackNumber := process.callbackNumber callbackMapLock.RLock() - context := callbackMap[callbackNumber] + callbackContext := callbackMap[callbackNumber] callbackMapLock.RUnlock() - if context == nil { + if callbackContext == nil { return nil } - handle := context.handle + handle := callbackContext.handle if handle == 0 { return nil } - // hcsUnregisterProcessCallback has its own syncronization - // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. - err := hcsUnregisterProcessCallback(handle) + // vmcompute.HcsUnregisterProcessCallback has its own synchronization to + // wait for all callbacks to complete. We must NOT hold the callbackMapLock. + err := vmcompute.HcsUnregisterProcessCallback(ctx, handle) if err != nil { return err } - closeChannels(context.channels) + closeChannels(callbackContext.channels) callbackMapLock.Lock() - callbackMap[callbackNumber] = nil + delete(callbackMap, callbackNumber) callbackMapLock.Unlock() - handle = 0 + handle = 0 //nolint:ineffassign return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema1/schema1.go similarity index 88% rename from vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go rename to vendor/github.com/Microsoft/hcsshim/internal/hcs/schema1/schema1.go index 6fa3bbc73d62a..b621c55938836 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema1/schema1.go @@ -3,6 +3,9 @@ package schema1 import ( "encoding/json" "time" + + "github.com/Microsoft/go-winio/pkg/guid" + hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" ) // ProcessConfig is used as both the input of Container.CreateProcess @@ -60,7 +63,7 @@ type MappedVirtualDisk struct { CreateInUtilityVM bool `json:",omitempty"` ReadOnly bool `json:",omitempty"` Cache string `json:",omitempty"` // "" (Unspecified); "Disabled"; "Enabled"; "Private"; "PrivateAllowSharing" - AttachOnly bool `json:",omitempty:` + AttachOnly bool `json:",omitempty"` } // AssignedDevice represents a device that has been directly assigned to a container @@ -115,9 +118,10 @@ type ComputeSystemQuery struct { type PropertyType string const ( - PropertyTypeStatistics PropertyType = "Statistics" - PropertyTypeProcessList = "ProcessList" - PropertyTypeMappedVirtualDisk = "MappedVirtualDisk" + PropertyTypeStatistics PropertyType = "Statistics" // V1 and V2 + PropertyTypeProcessList PropertyType = "ProcessList" // V1 and V2 + PropertyTypeMappedVirtualDisk PropertyType = "MappedVirtualDisk" // Not supported in V2 schema call + PropertyTypeGuestConnection PropertyType = "GuestConnection" // V1 and V2. Nil return from HCS before RS5 ) type PropertyQuery struct { @@ -130,9 +134,10 @@ type ContainerProperties struct { State string Name string SystemType string + RuntimeOSType string `json:"RuntimeOsType,omitempty"` Owner string SiloGUID string `json:"SiloGuid,omitempty"` - RuntimeID string `json:"RuntimeId,omitempty"` + RuntimeID guid.GUID `json:"RuntimeId,omitempty"` IsRuntimeTemplate bool `json:",omitempty"` RuntimeImagePath string `json:",omitempty"` Stopped bool `json:",omitempty"` @@ -142,6 +147,7 @@ type ContainerProperties struct { Statistics Statistics `json:",omitempty"` ProcessList []ProcessListItem `json:",omitempty"` MappedVirtualDiskControllers map[int]MappedVirtualDiskController `json:",omitempty"` + GuestConnectionInfo GuestConnectionInfo `json:",omitempty"` } // MemoryStats holds the memory statistics for a container @@ -206,6 +212,22 @@ type MappedVirtualDiskController struct { MappedVirtualDisks map[int]MappedVirtualDisk `json:",omitempty"` } +// GuestDefinedCapabilities is part of the GuestConnectionInfo returned by a GuestConnection call on a utility VM +type GuestDefinedCapabilities struct { + NamespaceAddRequestSupported bool `json:",omitempty"` + SignalProcessSupported bool `json:",omitempty"` + DumpStacksSupported bool `json:",omitempty"` + DeleteContainerStateSupported bool `json:",omitempty"` + UpdateContainerSupported bool `json:",omitempty"` +} + +// GuestConnectionInfo is the structure of an iterm return by a GuestConnection call on a utility VM +type GuestConnectionInfo struct { + SupportedSchemaVersions []hcsschema.Version `json:",omitempty"` + ProtocolVersion uint32 `json:",omitempty"` + GuestDefinedCapabilities GuestDefinedCapabilities `json:",omitempty"` +} + // Type of Request Support in ModifySystem type RequestType string diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go new file mode 100644 index 0000000000000..bcfeb34d54936 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go @@ -0,0 +1,30 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Attachment struct { + Type_ string `json:"Type,omitempty"` + + Path string `json:"Path,omitempty"` + + IgnoreFlushes bool `json:"IgnoreFlushes,omitempty"` + + CachingMode string `json:"CachingMode,omitempty"` + + NoWriteHardening bool `json:"NoWriteHardening,omitempty"` + + DisableExpansionOptimization bool `json:"DisableExpansionOptimization,omitempty"` + + IgnoreRelativeLocator bool `json:"IgnoreRelativeLocator,omitempty"` + + CaptureIoAttributionContext bool `json:"CaptureIoAttributionContext,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/battery.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/battery.go new file mode 100644 index 0000000000000..ecbbed4c233f3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/battery.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Battery struct { +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cache_query_stats_response.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cache_query_stats_response.go new file mode 100644 index 0000000000000..c1ea3953b58bf --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cache_query_stats_response.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CacheQueryStatsResponse struct { + L3OccupancyBytes int32 `json:"L3OccupancyBytes,omitempty"` + + L3TotalBwBytes int32 `json:"L3TotalBwBytes,omitempty"` + + L3LocalBwBytes int32 `json:"L3LocalBwBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/chipset.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/chipset.go new file mode 100644 index 0000000000000..ca75277a3f25f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/chipset.go @@ -0,0 +1,27 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Chipset struct { + Uefi *Uefi `json:"Uefi,omitempty"` + + IsNumLockDisabled bool `json:"IsNumLockDisabled,omitempty"` + + BaseBoardSerialNumber string `json:"BaseBoardSerialNumber,omitempty"` + + ChassisSerialNumber string `json:"ChassisSerialNumber,omitempty"` + + ChassisAssetTag string `json:"ChassisAssetTag,omitempty"` + + UseUtc bool `json:"UseUtc,omitempty"` + + // LinuxKernelDirect - Added in v2.2 Builds >=181117 + LinuxKernelDirect *LinuxKernelDirect `json:"LinuxKernelDirect,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/close_handle.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/close_handle.go new file mode 100644 index 0000000000000..b4f9c315b05b6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/close_handle.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CloseHandle struct { + Handle string `json:"Handle,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/com_port.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/com_port.go new file mode 100644 index 0000000000000..8bf8cab60e559 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/com_port.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// ComPort specifies the named pipe that will be used for the port, with empty string indicating a disconnected port. +type ComPort struct { + NamedPipe string `json:"NamedPipe,omitempty"` + + OptimizeForDebugger bool `json:"OptimizeForDebugger,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/compute_system.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/compute_system.go new file mode 100644 index 0000000000000..10cea67e04280 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/compute_system.go @@ -0,0 +1,26 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ComputeSystem struct { + Owner string `json:"Owner,omitempty"` + + SchemaVersion *Version `json:"SchemaVersion,omitempty"` + + HostingSystemId string `json:"HostingSystemId,omitempty"` + + HostedSystem interface{} `json:"HostedSystem,omitempty"` + + Container *Container `json:"Container,omitempty"` + + VirtualMachine *VirtualMachine `json:"VirtualMachine,omitempty"` + + ShouldTerminateOnLastHandleClosed bool `json:"ShouldTerminateOnLastHandleClosed,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/configuration.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/configuration.go new file mode 100644 index 0000000000000..1d5dfe68ad696 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/configuration.go @@ -0,0 +1,72 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "net/http" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKey takes an APIKey as authentication for the request + ContextAPIKey = contextKey("apikey") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +type Configuration struct { + BasePath string `json:"basePath,omitempty"` + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + HTTPClient *http.Client +} + +func NewConfiguration() *Configuration { + cfg := &Configuration{ + BasePath: "https://localhost", + DefaultHeader: make(map[string]string), + UserAgent: "Swagger-Codegen/2.1.0/go", + } + return cfg +} + +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/console_size.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/console_size.go new file mode 100644 index 0000000000000..68aa04a573e75 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/console_size.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ConsoleSize struct { + Height int32 `json:"Height,omitempty"` + + Width int32 `json:"Width,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go new file mode 100644 index 0000000000000..4fb23107683c4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go @@ -0,0 +1,34 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Container struct { + GuestOs *GuestOs `json:"GuestOs,omitempty"` + + Storage *Storage `json:"Storage,omitempty"` + + MappedDirectories []MappedDirectory `json:"MappedDirectories,omitempty"` + + MappedPipes []MappedPipe `json:"MappedPipes,omitempty"` + + Memory *Memory `json:"Memory,omitempty"` + + Processor *Processor `json:"Processor,omitempty"` + + Networking *Networking `json:"Networking,omitempty"` + + HvSocket *HvSocket `json:"HvSocket,omitempty"` + + ContainerCredentialGuard *ContainerCredentialGuardState `json:"ContainerCredentialGuard,omitempty"` + + RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"` + + AssignedDevices []Device `json:"AssignedDevices,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_add_instance_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_add_instance_request.go new file mode 100644 index 0000000000000..495c6ebc8f4c2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_add_instance_request.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardAddInstanceRequest struct { + Id string `json:"Id,omitempty"` + CredentialSpec string `json:"CredentialSpec,omitempty"` + Transport string `json:"Transport,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_hv_socket_service_config.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_hv_socket_service_config.go new file mode 100644 index 0000000000000..1ed4c008f253c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_hv_socket_service_config.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardHvSocketServiceConfig struct { + ServiceId string `json:"ServiceId,omitempty"` + ServiceConfig *HvSocketServiceConfig `json:"ServiceConfig,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_instance.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_instance.go new file mode 100644 index 0000000000000..d7ebd0fcca14a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_instance.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardInstance struct { + Id string `json:"Id,omitempty"` + CredentialGuard *ContainerCredentialGuardState `json:"CredentialGuard,omitempty"` + HvSocketConfig *ContainerCredentialGuardHvSocketServiceConfig `json:"HvSocketConfig,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_modify_operation.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_modify_operation.go new file mode 100644 index 0000000000000..71005b090be0d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_modify_operation.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardModifyOperation string + +const ( + AddInstance ContainerCredentialGuardModifyOperation = "AddInstance" + RemoveInstance ContainerCredentialGuardModifyOperation = "RemoveInstance" +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_operation_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_operation_request.go new file mode 100644 index 0000000000000..952cda4965ce8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_operation_request.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardOperationRequest struct { + Operation ContainerCredentialGuardModifyOperation `json:"Operation,omitempty"` + OperationDetails interface{} `json:"OperationDetails,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_remove_instance_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_remove_instance_request.go new file mode 100644 index 0000000000000..32e5a3beed15a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_remove_instance_request.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardRemoveInstanceRequest struct { + Id string `json:"Id,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_state.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_state.go new file mode 100644 index 0000000000000..0f8f644379ced --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_state.go @@ -0,0 +1,25 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardState struct { + + // Authentication cookie for calls to a Container Credential Guard instance. + Cookie string `json:"Cookie,omitempty"` + + // Name of the RPC endpoint of the Container Credential Guard instance. + RpcEndpoint string `json:"RpcEndpoint,omitempty"` + + // Transport used for the configured Container Credential Guard instance. + Transport string `json:"Transport,omitempty"` + + // Credential spec used for the configured Container Credential Guard instance. + CredentialSpec string `json:"CredentialSpec,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_system_info.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_system_info.go new file mode 100644 index 0000000000000..ea306fa21aca9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_credential_guard_system_info.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardSystemInfo struct { + Instances []ContainerCredentialGuardInstance `json:"Instances,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_memory_information.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_memory_information.go new file mode 100644 index 0000000000000..1fd7ca5d56f6d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container_memory_information.go @@ -0,0 +1,25 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// memory usage as viewed from within the container +type ContainerMemoryInformation struct { + TotalPhysicalBytes int32 `json:"TotalPhysicalBytes,omitempty"` + + TotalUsage int32 `json:"TotalUsage,omitempty"` + + CommittedBytes int32 `json:"CommittedBytes,omitempty"` + + SharedCommittedBytes int32 `json:"SharedCommittedBytes,omitempty"` + + CommitLimitBytes int32 `json:"CommitLimitBytes,omitempty"` + + PeakCommitmentBytes int32 `json:"PeakCommitmentBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group.go new file mode 100644 index 0000000000000..90332a5190f16 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// CPU groups allow Hyper-V administrators to better manage and allocate the host's CPU resources across guest virtual machines +type CpuGroup struct { + Id string `json:"Id,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_affinity.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_affinity.go new file mode 100644 index 0000000000000..8794961bf5c41 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_affinity.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CpuGroupAffinity struct { + LogicalProcessorCount int32 `json:"LogicalProcessorCount,omitempty"` + LogicalProcessors []int32 `json:"LogicalProcessors,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go new file mode 100644 index 0000000000000..f1a28cd389896 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CpuGroupConfig struct { + GroupId string `json:"GroupId,omitempty"` + Affinity *CpuGroupAffinity `json:"Affinity,omitempty"` + GroupProperties []CpuGroupProperty `json:"GroupProperties,omitempty"` + // Hypervisor CPU group IDs exposed to clients + HypervisorGroupId int32 `json:"HypervisorGroupId,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_configurations.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_configurations.go new file mode 100644 index 0000000000000..3ace0ccc3b890 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_configurations.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Structure used to return cpu groups for a Service property query +type CpuGroupConfigurations struct { + CpuGroups []CpuGroupConfig `json:"CpuGroups,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_operations.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_operations.go new file mode 100644 index 0000000000000..7d897807016d9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_operations.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CPUGroupOperation string + +const ( + CreateGroup CPUGroupOperation = "CreateGroup" + DeleteGroup CPUGroupOperation = "DeleteGroup" + SetProperty CPUGroupOperation = "SetProperty" +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_property.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_property.go new file mode 100644 index 0000000000000..bbad6a2c450ab --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_property.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CpuGroupProperty struct { + PropertyCode uint32 `json:"PropertyCode,omitempty"` + PropertyValue uint32 `json:"PropertyValue,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/create_group_operation.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/create_group_operation.go new file mode 100644 index 0000000000000..91a8278fe3c50 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/create_group_operation.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Create group operation settings +type CreateGroupOperation struct { + GroupId string `json:"GroupId,omitempty"` + LogicalProcessorCount uint32 `json:"LogicalProcessorCount,omitempty"` + LogicalProcessors []uint32 `json:"LogicalProcessors,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/delete_group_operation.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/delete_group_operation.go new file mode 100644 index 0000000000000..134bd988175c0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/delete_group_operation.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Delete group operation settings +type DeleteGroupOperation struct { + GroupId string `json:"GroupId,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go new file mode 100644 index 0000000000000..107caddadae43 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go @@ -0,0 +1,27 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type DeviceType string + +const ( + ClassGUID DeviceType = "ClassGuid" + DeviceInstance DeviceType = "DeviceInstance" + GPUMirror DeviceType = "GpuMirror" +) + +type Device struct { + // The type of device to assign to the container. + Type DeviceType `json:"Type,omitempty"` + // The interface class guid of the device interfaces to assign to the container. Only used when Type is ClassGuid. + InterfaceClassGuid string `json:"InterfaceClassGuid,omitempty"` + // The location path of the device to assign to the container. Only used when Type is DeviceInstance. + LocationPath string `json:"LocationPath,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/devices.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/devices.go new file mode 100644 index 0000000000000..e985d96d2283c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/devices.go @@ -0,0 +1,46 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Devices struct { + ComPorts map[string]ComPort `json:"ComPorts,omitempty"` + + Scsi map[string]Scsi `json:"Scsi,omitempty"` + + VirtualPMem *VirtualPMemController `json:"VirtualPMem,omitempty"` + + NetworkAdapters map[string]NetworkAdapter `json:"NetworkAdapters,omitempty"` + + VideoMonitor *VideoMonitor `json:"VideoMonitor,omitempty"` + + Keyboard *Keyboard `json:"Keyboard,omitempty"` + + Mouse *Mouse `json:"Mouse,omitempty"` + + HvSocket *HvSocket2 `json:"HvSocket,omitempty"` + + EnhancedModeVideo *EnhancedModeVideo `json:"EnhancedModeVideo,omitempty"` + + GuestCrashReporting *GuestCrashReporting `json:"GuestCrashReporting,omitempty"` + + VirtualSmb *VirtualSmb `json:"VirtualSmb,omitempty"` + + Plan9 *Plan9 `json:"Plan9,omitempty"` + + Battery *Battery `json:"Battery,omitempty"` + + FlexibleIov map[string]FlexibleIoDevice `json:"FlexibleIov,omitempty"` + + SharedMemory *SharedMemoryConfiguration `json:"SharedMemory,omitempty"` + + // TODO: This is pre-release support in schema 2.3. Need to add build number + // docs when a public build with this is out. + VirtualPci map[string]VirtualPciDevice `json:",omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/enhanced_mode_video.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/enhanced_mode_video.go new file mode 100644 index 0000000000000..85450c41e10d9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/enhanced_mode_video.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type EnhancedModeVideo struct { + ConnectionOptions *RdpConnectionOptions `json:"ConnectionOptions,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/flexible_io_device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/flexible_io_device.go new file mode 100644 index 0000000000000..fe86cab655667 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/flexible_io_device.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type FlexibleIoDevice struct { + EmulatorId string `json:"EmulatorId,omitempty"` + + HostingModel string `json:"HostingModel,omitempty"` + + Configuration []string `json:"Configuration,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection.go new file mode 100644 index 0000000000000..7db29495b3e69 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestConnection struct { + + // Use Vsock rather than Hyper-V sockets to communicate with the guest service. + UseVsock bool `json:"UseVsock,omitempty"` + + // Don't disconnect the guest connection when pausing the virtual machine. + UseConnectedSuspend bool `json:"UseConnectedSuspend,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection_info.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection_info.go new file mode 100644 index 0000000000000..8a369bab71c42 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_connection_info.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Information about the guest. +type GuestConnectionInfo struct { + + // Each schema version x.y stands for the range of versions a.b where a==x and b<=y. This list comes from the SupportedSchemaVersions field in GcsCapabilities. + SupportedSchemaVersions []Version `json:"SupportedSchemaVersions,omitempty"` + + ProtocolVersion int32 `json:"ProtocolVersion,omitempty"` + + GuestDefinedCapabilities *interface{} `json:"GuestDefinedCapabilities,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_crash_reporting.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_crash_reporting.go new file mode 100644 index 0000000000000..af828004835c1 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_crash_reporting.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestCrashReporting struct { + WindowsCrashSettings *WindowsCrashReporting `json:"WindowsCrashSettings,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_os.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_os.go new file mode 100644 index 0000000000000..8838519a39c40 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_os.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestOs struct { + HostName string `json:"HostName,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_state.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_state.go new file mode 100644 index 0000000000000..ef1eec88656fd --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/guest_state.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestState struct { + + // The path to an existing file uses for persistent guest state storage. An empty string indicates the system should initialize new transient, in-memory guest state. + GuestStateFilePath string `json:"GuestStateFilePath,omitempty"` + + // The path to an existing file for persistent runtime state storage. An empty string indicates the system should initialize new transient, in-memory runtime state. + RuntimeStateFilePath string `json:"RuntimeStateFilePath,omitempty"` + + // If true, the guest state and runtime state files will be used as templates to populate transient, in-memory state instead of using the files as persistent backing store. + ForceTransientState bool `json:"ForceTransientState,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/host_processor_modify_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/host_processor_modify_request.go new file mode 100644 index 0000000000000..2238ce5306c39 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/host_processor_modify_request.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Structure used to request a service processor modification +type HostProcessorModificationRequest struct { + Operation CPUGroupOperation `json:"Operation,omitempty"` + OperationDetails interface{} `json:"OperationDetails,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hosted_system.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hosted_system.go new file mode 100644 index 0000000000000..ea3084bca7ff8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hosted_system.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HostedSystem struct { + SchemaVersion *Version `json:"SchemaVersion,omitempty"` + + Container *Container `json:"Container,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket.go new file mode 100644 index 0000000000000..23b2ee9e7d450 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HvSocket struct { + Config *HvSocketSystemConfig `json:"Config,omitempty"` + + EnablePowerShellDirect bool `json:"EnablePowerShellDirect,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_2.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_2.go new file mode 100644 index 0000000000000..a017691f02d72 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_2.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// HvSocket configuration for a VM +type HvSocket2 struct { + HvSocketConfig *HvSocketSystemConfig `json:"HvSocketConfig,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_address.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_address.go new file mode 100644 index 0000000000000..84c11b93ee582 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_address.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// This class defines address settings applied to a VM +// by the GCS every time a VM starts or restores. +type HvSocketAddress struct { + LocalAddress string `json:"LocalAddress,omitempty"` + ParentAddress string `json:"ParentAddress,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_service_config.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_service_config.go new file mode 100644 index 0000000000000..ecd9f7fbac245 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_service_config.go @@ -0,0 +1,28 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HvSocketServiceConfig struct { + + // SDDL string that HvSocket will check before allowing a host process to bind to this specific service. If not specified, defaults to the system DefaultBindSecurityDescriptor, defined in HvSocketSystemWpConfig in V1. + BindSecurityDescriptor string `json:"BindSecurityDescriptor,omitempty"` + + // SDDL string that HvSocket will check before allowing a host process to connect to this specific service. If not specified, defaults to the system DefaultConnectSecurityDescriptor, defined in HvSocketSystemWpConfig in V1. + ConnectSecurityDescriptor string `json:"ConnectSecurityDescriptor,omitempty"` + + // If true, HvSocket will process wildcard binds for this service/system combination. Wildcard binds are secured in the registry at SOFTWARE/Microsoft/Windows NT/CurrentVersion/Virtualization/HvSocket/WildcardDescriptors + AllowWildcardBinds bool `json:"AllowWildcardBinds,omitempty"` + + // Disabled controls whether the HvSocket service is accepting connection requests. + // This set to true will make the service refuse all incoming connections as well as cancel + // any connections already established. The service itself will still be active however + // and can be re-enabled at a future time. + Disabled bool `json:"Disabled,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_system_config.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_system_config.go new file mode 100644 index 0000000000000..69f4f9d39b9a5 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/hv_socket_system_config.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// This is the HCS Schema version of the HvSocket configuration. The VMWP version is located in Config.Devices.IC in V1. +type HvSocketSystemConfig struct { + + // SDDL string that HvSocket will check before allowing a host process to bind to an unlisted service for this specific container/VM (not wildcard binds). + DefaultBindSecurityDescriptor string `json:"DefaultBindSecurityDescriptor,omitempty"` + + // SDDL string that HvSocket will check before allowing a host process to connect to an unlisted service in the VM/container. + DefaultConnectSecurityDescriptor string `json:"DefaultConnectSecurityDescriptor,omitempty"` + + ServiceTable map[string]HvSocketServiceConfig `json:"ServiceTable,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/interrupt_moderation_mode.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/interrupt_moderation_mode.go new file mode 100644 index 0000000000000..a614d63bd7255 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/interrupt_moderation_mode.go @@ -0,0 +1,42 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type InterruptModerationName string + +// The valid interrupt moderation modes for I/O virtualization (IOV) offloading. +const ( + DefaultName InterruptModerationName = "Default" + AdaptiveName InterruptModerationName = "Adaptive" + OffName InterruptModerationName = "Off" + LowName InterruptModerationName = "Low" + MediumName InterruptModerationName = "Medium" + HighName InterruptModerationName = "High" +) + +type InterruptModerationValue uint32 + +const ( + DefaultValue InterruptModerationValue = iota + AdaptiveValue + OffValue + LowValue InterruptModerationValue = 100 + MediumValue InterruptModerationValue = 200 + HighValue InterruptModerationValue = 300 +) + +var InterruptModerationValueToName = map[InterruptModerationValue]InterruptModerationName{ + DefaultValue: DefaultName, + AdaptiveValue: AdaptiveName, + OffValue: OffName, + LowValue: LowName, + MediumValue: MediumName, + HighValue: HighName, +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/iov_settings.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/iov_settings.go new file mode 100644 index 0000000000000..2a55cc37cd33b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/iov_settings.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type IovSettings struct { + // The weight assigned to this port for I/O virtualization (IOV) offloading. + // Setting this to 0 disables IOV offloading. + OffloadWeight *uint32 `json:"OffloadWeight,omitempty"` + + // The number of queue pairs requested for this port for I/O virtualization (IOV) offloading. + QueuePairsRequested *uint32 `json:"QueuePairsRequested,omitempty"` + + // The interrupt moderation mode for I/O virtualization (IOV) offloading. + InterruptModeration *InterruptModerationName `json:"InterruptModeration,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/keyboard.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/keyboard.go new file mode 100644 index 0000000000000..3d3fa3b1c7326 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/keyboard.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Keyboard struct { +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/layer.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/layer.go new file mode 100644 index 0000000000000..176c49d4959ce --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/layer.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Layer struct { + Id string `json:"Id,omitempty"` + + Path string `json:"Path,omitempty"` + + PathType string `json:"PathType,omitempty"` + + // Unspecified defaults to Enabled + Cache string `json:"Cache,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/linux_kernel_direct.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/linux_kernel_direct.go new file mode 100644 index 0000000000000..0ab6c280fc8d5 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/linux_kernel_direct.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.2 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type LinuxKernelDirect struct { + KernelFilePath string `json:"KernelFilePath,omitempty"` + + InitRdPath string `json:"InitRdPath,omitempty"` + + KernelCmdLine string `json:"KernelCmdLine,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/logical_processor.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/logical_processor.go new file mode 100644 index 0000000000000..2e3aa5e1750f2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/logical_processor.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type LogicalProcessor struct { + LpIndex uint32 `json:"LpIndex,omitempty"` + NodeNumber uint8 `json:"NodeNumber,omitempty"` + PackageId uint32 `json:"PackageId,omitempty"` + CoreId uint32 `json:"CoreId,omitempty"` + RootVpIndex int32 `json:"RootVpIndex,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_directory.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_directory.go new file mode 100644 index 0000000000000..9b86a40457f20 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_directory.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MappedDirectory struct { + HostPath string `json:"HostPath,omitempty"` + + HostPathType string `json:"HostPathType,omitempty"` + + ContainerPath string `json:"ContainerPath,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_pipe.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_pipe.go new file mode 100644 index 0000000000000..208074e9a2507 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mapped_pipe.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MappedPipe struct { + ContainerPipeName string `json:"ContainerPipeName,omitempty"` + + HostPath string `json:"HostPath,omitempty"` + + HostPathType string `json:"HostPathType,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory.go new file mode 100644 index 0000000000000..30749c6724968 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Memory struct { + SizeInMB uint64 `json:"SizeInMB,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_2.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_2.go new file mode 100644 index 0000000000000..71224c75b9de0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_2.go @@ -0,0 +1,49 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Memory2 struct { + SizeInMB uint64 `json:"SizeInMB,omitempty"` + + AllowOvercommit bool `json:"AllowOvercommit,omitempty"` + + EnableHotHint bool `json:"EnableHotHint,omitempty"` + + EnableColdHint bool `json:"EnableColdHint,omitempty"` + + EnableEpf bool `json:"EnableEpf,omitempty"` + + // EnableDeferredCommit is private in the schema. If regenerated need to add back. + EnableDeferredCommit bool `json:"EnableDeferredCommit,omitempty"` + + // EnableColdDiscardHint if enabled, then the memory cold discard hint feature is exposed + // to the VM, allowing it to trim non-zeroed pages from the working set (if supported by + // the guest operating system). + EnableColdDiscardHint bool `json:"EnableColdDiscardHint,omitempty"` + + // LowMmioGapInMB is the low MMIO region allocated below 4GB. + // + // TODO: This is pre-release support in schema 2.3. Need to add build number + // docs when a public build with this is out. + LowMMIOGapInMB uint64 `json:"LowMmioGapInMB,omitempty"` + + // HighMmioBaseInMB is the high MMIO region allocated above 4GB (base and + // size). + // + // TODO: This is pre-release support in schema 2.3. Need to add build number + // docs when a public build with this is out. + HighMMIOBaseInMB uint64 `json:"HighMmioBaseInMB,omitempty"` + + // HighMmioGapInMB is the high MMIO region. + // + // TODO: This is pre-release support in schema 2.3. Need to add build number + // docs when a public build with this is out. + HighMMIOGapInMB uint64 `json:"HighMmioGapInMB,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_information_for_vm.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_information_for_vm.go new file mode 100644 index 0000000000000..811779b04b24c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_information_for_vm.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MemoryInformationForVm struct { + VirtualNodeCount uint32 `json:"VirtualNodeCount,omitempty"` + + VirtualMachineMemory *VmMemory `json:"VirtualMachineMemory,omitempty"` + + VirtualNodes []VirtualNodeInfo `json:"VirtualNodes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_stats.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_stats.go new file mode 100644 index 0000000000000..906ba597f9f50 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/memory_stats.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Memory runtime statistics +type MemoryStats struct { + MemoryUsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"` + + MemoryUsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"` + + MemoryUsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modification_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modification_request.go new file mode 100644 index 0000000000000..1384ed888218f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modification_request.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ModificationRequest struct { + PropertyType PropertyType `json:"PropertyType,omitempty"` + Settings interface{} `json:"Settings,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modify_setting_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modify_setting_request.go new file mode 100644 index 0000000000000..d29455a3e4345 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/modify_setting_request.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ModifySettingRequest struct { + ResourcePath string `json:"ResourcePath,omitempty"` + + RequestType string `json:"RequestType,omitempty"` + + Settings interface{} `json:"Settings,omitempty"` // NOTE: Swagger generated as *interface{}. Locally updated + + GuestRequest interface{} `json:"GuestRequest,omitempty"` // NOTE: Swagger generated as *interface{}. Locally updated +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mouse.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mouse.go new file mode 100644 index 0000000000000..ccf8b938f3aca --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/mouse.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Mouse struct { +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/network_adapter.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/network_adapter.go new file mode 100644 index 0000000000000..7408abd317dc7 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/network_adapter.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type NetworkAdapter struct { + EndpointId string `json:"EndpointId,omitempty"` + MacAddress string `json:"MacAddress,omitempty"` + // The I/O virtualization (IOV) offloading configuration. + IovSettings *IovSettings `json:"IovSettings,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/networking.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/networking.go new file mode 100644 index 0000000000000..e5ea187a2954d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/networking.go @@ -0,0 +1,23 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Networking struct { + AllowUnqualifiedDnsQuery bool `json:"AllowUnqualifiedDnsQuery,omitempty"` + + DnsSearchList string `json:"DnsSearchList,omitempty"` + + NetworkSharedContainerName string `json:"NetworkSharedContainerName,omitempty"` + + // Guid in windows; string in linux + Namespace string `json:"Namespace,omitempty"` + + NetworkAdapters []string `json:"NetworkAdapters,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_notification.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_notification.go new file mode 100644 index 0000000000000..d96c9501f3318 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_notification.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Notification data that is indicated to components running in the Virtual Machine. +type PauseNotification struct { + Reason string `json:"Reason,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_options.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_options.go new file mode 100644 index 0000000000000..21707a88eb7a7 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/pause_options.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Options for HcsPauseComputeSystem +type PauseOptions struct { + SuspensionLevel string `json:"SuspensionLevel,omitempty"` + + HostedNotification *PauseNotification `json:"HostedNotification,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9.go new file mode 100644 index 0000000000000..29d8c8012ffca --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Plan9 struct { + Shares []Plan9Share `json:"Shares,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9_share.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9_share.go new file mode 100644 index 0000000000000..41f8fdea0296c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/plan9_share.go @@ -0,0 +1,34 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Plan9Share struct { + Name string `json:"Name,omitempty"` + + // The name by which the guest operation system can access this share, via the aname parameter in the Plan9 protocol. + AccessName string `json:"AccessName,omitempty"` + + Path string `json:"Path,omitempty"` + + Port int32 `json:"Port,omitempty"` + + // Flags are marked private. Until they are exported correctly + // + // ReadOnly 0x00000001 + // LinuxMetadata 0x00000004 + // CaseSensitive 0x00000008 + Flags int32 `json:"Flags,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` + + UseShareRootIdentity bool `json:"UseShareRootIdentity,omitempty"` + + AllowedFiles []string `json:"AllowedFiles,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_details.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_details.go new file mode 100644 index 0000000000000..e9a662dd59d41 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_details.go @@ -0,0 +1,33 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "time" +) + +// Information about a process running in a container +type ProcessDetails struct { + ProcessId int32 `json:"ProcessId,omitempty"` + + ImageName string `json:"ImageName,omitempty"` + + CreateTimestamp time.Time `json:"CreateTimestamp,omitempty"` + + UserTime100ns int32 `json:"UserTime100ns,omitempty"` + + KernelTime100ns int32 `json:"KernelTime100ns,omitempty"` + + MemoryCommitBytes int32 `json:"MemoryCommitBytes,omitempty"` + + MemoryWorkingSetPrivateBytes int32 `json:"MemoryWorkingSetPrivateBytes,omitempty"` + + MemoryWorkingSetSharedBytes int32 `json:"MemoryWorkingSetSharedBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_modify_request.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_modify_request.go new file mode 100644 index 0000000000000..e4ed095c7bec5 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_modify_request.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Passed to HcsRpc_ModifyProcess +type ProcessModifyRequest struct { + Operation string `json:"Operation,omitempty"` + + ConsoleSize *ConsoleSize `json:"ConsoleSize,omitempty"` + + CloseHandle *CloseHandle `json:"CloseHandle,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_parameters.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_parameters.go new file mode 100644 index 0000000000000..82b0d0532b28e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_parameters.go @@ -0,0 +1,46 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ProcessParameters struct { + ApplicationName string `json:"ApplicationName,omitempty"` + + CommandLine string `json:"CommandLine,omitempty"` + + // optional alternative to CommandLine, currently only supported by Linux GCS + CommandArgs []string `json:"CommandArgs,omitempty"` + + User string `json:"User,omitempty"` + + WorkingDirectory string `json:"WorkingDirectory,omitempty"` + + Environment map[string]string `json:"Environment,omitempty"` + + // if set, will run as low-privilege process + RestrictedToken bool `json:"RestrictedToken,omitempty"` + + // if set, ignore StdErrPipe + EmulateConsole bool `json:"EmulateConsole,omitempty"` + + CreateStdInPipe bool `json:"CreateStdInPipe,omitempty"` + + CreateStdOutPipe bool `json:"CreateStdOutPipe,omitempty"` + + CreateStdErrPipe bool `json:"CreateStdErrPipe,omitempty"` + + // height then width + ConsoleSize []int32 `json:"ConsoleSize,omitempty"` + + // if set, find an existing session for the user and create the process in it + UseExistingLogin bool `json:"UseExistingLogin,omitempty"` + + // if set, use the legacy console instead of conhost + UseLegacyConsole bool `json:"UseLegacyConsole,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_status.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_status.go new file mode 100644 index 0000000000000..ad9a4fa9ad6d4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/process_status.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Status of a process running in a container +type ProcessStatus struct { + ProcessId int32 `json:"ProcessId,omitempty"` + + Exited bool `json:"Exited,omitempty"` + + ExitCode int32 `json:"ExitCode,omitempty"` + + LastWaitResult int32 `json:"LastWaitResult,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor.go new file mode 100644 index 0000000000000..bb24e88da1a42 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Processor struct { + Count int32 `json:"Count,omitempty"` + + Maximum int32 `json:"Maximum,omitempty"` + + Weight int32 `json:"Weight,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_2.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_2.go new file mode 100644 index 0000000000000..c64f335ec7d66 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_2.go @@ -0,0 +1,23 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.5 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Processor2 struct { + Count int32 `json:"Count,omitempty"` + + Limit int32 `json:"Limit,omitempty"` + + Weight int32 `json:"Weight,omitempty"` + + ExposeVirtualizationExtensions bool `json:"ExposeVirtualizationExtensions,omitempty"` + + // An optional object that configures the CPU Group to which a Virtual Machine is going to bind to. + CpuGroup *CpuGroup `json:"CpuGroup,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_stats.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_stats.go new file mode 100644 index 0000000000000..6157e252256a0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_stats.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// CPU runtime statistics +type ProcessorStats struct { + TotalRuntime100ns uint64 `json:"TotalRuntime100ns,omitempty"` + + RuntimeUser100ns uint64 `json:"RuntimeUser100ns,omitempty"` + + RuntimeKernel100ns uint64 `json:"RuntimeKernel100ns,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_topology.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_topology.go new file mode 100644 index 0000000000000..885156e77fae0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/processor_topology.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ProcessorTopology struct { + LogicalProcessorCount uint32 `json:"LogicalProcessorCount,omitempty"` + LogicalProcessors []LogicalProcessor `json:"LogicalProcessors,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/properties.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/properties.go new file mode 100644 index 0000000000000..17558cba0f286 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/properties.go @@ -0,0 +1,54 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + v1 "github.com/containerd/cgroups/stats/v1" +) + +type Properties struct { + Id string `json:"Id,omitempty"` + + SystemType string `json:"SystemType,omitempty"` + + RuntimeOsType string `json:"RuntimeOsType,omitempty"` + + Name string `json:"Name,omitempty"` + + Owner string `json:"Owner,omitempty"` + + RuntimeId string `json:"RuntimeId,omitempty"` + + RuntimeTemplateId string `json:"RuntimeTemplateId,omitempty"` + + State string `json:"State,omitempty"` + + Stopped bool `json:"Stopped,omitempty"` + + ExitType string `json:"ExitType,omitempty"` + + Memory *MemoryInformationForVm `json:"Memory,omitempty"` + + Statistics *Statistics `json:"Statistics,omitempty"` + + ProcessList []ProcessDetails `json:"ProcessList,omitempty"` + + TerminateOnLastHandleClosed bool `json:"TerminateOnLastHandleClosed,omitempty"` + + HostingSystemId string `json:"HostingSystemId,omitempty"` + + SharedMemoryRegionInfo []SharedMemoryRegionInfo `json:"SharedMemoryRegionInfo,omitempty"` + + GuestConnectionInfo *GuestConnectionInfo `json:"GuestConnectionInfo,omitempty"` + + // Metrics is not part of the API for HCS but this is used for LCOW v2 to + // return the full cgroup metrics from the guest. + Metrics *v1.Metrics `json:"LCOWMetrics,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_query.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_query.go new file mode 100644 index 0000000000000..d6d80df13146f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_query.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// By default the basic properties will be returned. This query provides a way to request specific properties. +type PropertyQuery struct { + PropertyTypes []PropertyType `json:"PropertyTypes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_type.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_type.go new file mode 100644 index 0000000000000..98f2c96edbd62 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/property_type.go @@ -0,0 +1,26 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type PropertyType string + +const ( + PTMemory PropertyType = "Memory" + PTGuestMemory PropertyType = "GuestMemory" + PTStatistics PropertyType = "Statistics" + PTProcessList PropertyType = "ProcessList" + PTTerminateOnLastHandleClosed PropertyType = "TerminateOnLastHandleClosed" + PTSharedMemoryRegion PropertyType = "SharedMemoryRegion" + PTContainerCredentialGuard PropertyType = "ContainerCredentialGuard" // This field is not generated by swagger. This was added manually. + PTGuestConnection PropertyType = "GuestConnection" + PTICHeartbeatStatus PropertyType = "ICHeartbeatStatus" + PTProcessorTopology PropertyType = "ProcessorTopology" + PTCPUGroup PropertyType = "CpuGroup" +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/rdp_connection_options.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/rdp_connection_options.go new file mode 100644 index 0000000000000..8d5f5c1719efa --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/rdp_connection_options.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RdpConnectionOptions struct { + AccessSids []string `json:"AccessSids,omitempty"` + + NamedPipe string `json:"NamedPipe,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_changes.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_changes.go new file mode 100644 index 0000000000000..006906f6e2fee --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_changes.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryChanges struct { + AddValues []RegistryValue `json:"AddValues,omitempty"` + + DeleteKeys []RegistryKey `json:"DeleteKeys,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_key.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_key.go new file mode 100644 index 0000000000000..26fde99c74c10 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_key.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryKey struct { + Hive string `json:"Hive,omitempty"` + + Name string `json:"Name,omitempty"` + + Volatile bool `json:"Volatile,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_value.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_value.go new file mode 100644 index 0000000000000..3f203176c322a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/registry_value.go @@ -0,0 +1,30 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryValue struct { + Key *RegistryKey `json:"Key,omitempty"` + + Name string `json:"Name,omitempty"` + + Type_ string `json:"Type,omitempty"` + + // One and only one value type must be set. + StringValue string `json:"StringValue,omitempty"` + + BinaryValue string `json:"BinaryValue,omitempty"` + + DWordValue int32 `json:"DWordValue,omitempty"` + + QWordValue int32 `json:"QWordValue,omitempty"` + + // Only used if RegistryValueType is CustomType The data is in BinaryValue + CustomType int32 `json:"CustomType,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/restore_state.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/restore_state.go new file mode 100644 index 0000000000000..778ff58735aad --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/restore_state.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RestoreState struct { + + // The path to the save state file to restore the system from. + SaveStateFilePath string `json:"SaveStateFilePath,omitempty"` + + // The ID of the template system to clone this new system off of. An empty string indicates the system should not be cloned from a template. + TemplateSystemId string `json:"TemplateSystemId,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/save_options.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/save_options.go new file mode 100644 index 0000000000000..e55fa1d98a5b8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/save_options.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SaveOptions struct { + + // The type of save operation to be performed. + SaveType string `json:"SaveType,omitempty"` + + // The path to the file that will container the saved state. + SaveStateFilePath string `json:"SaveStateFilePath,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/scsi.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/scsi.go new file mode 100644 index 0000000000000..bf253a470b657 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/scsi.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Scsi struct { + + // Map of attachments, where the key is the integer LUN number on the controller. + Attachments map[string]Attachment `json:"Attachments,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/service_properties.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/service_properties.go new file mode 100644 index 0000000000000..b8142ca6a610d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/service_properties.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import "encoding/json" + +type ServiceProperties struct { + // Changed Properties field to []json.RawMessage from []interface{} to avoid having to + // remarshal sp.Properties[n] and unmarshal into the type(s) we want. + Properties []json.RawMessage `json:"Properties,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_configuration.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_configuration.go new file mode 100644 index 0000000000000..df9baa9219ac2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_configuration.go @@ -0,0 +1,14 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryConfiguration struct { + Regions []SharedMemoryRegion `json:"Regions,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region.go new file mode 100644 index 0000000000000..825b71865d797 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryRegion struct { + SectionName string `json:"SectionName,omitempty"` + + StartOffset int32 `json:"StartOffset,omitempty"` + + Length int32 `json:"Length,omitempty"` + + AllowGuestWrite bool `json:"AllowGuestWrite,omitempty"` + + HiddenFromGuest bool `json:"HiddenFromGuest,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region_info.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region_info.go new file mode 100644 index 0000000000000..f67b08eb57a24 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/shared_memory_region_info.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryRegionInfo struct { + SectionName string `json:"SectionName,omitempty"` + + GuestPhysicalAddress int32 `json:"GuestPhysicalAddress,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/silo_properties.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/silo_properties.go new file mode 100644 index 0000000000000..5eaf6a7f4a2d1 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/silo_properties.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Silo job information +type SiloProperties struct { + Enabled bool `json:"Enabled,omitempty"` + + JobName string `json:"JobName,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/statistics.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/statistics.go new file mode 100644 index 0000000000000..ba7a6b3963bd7 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/statistics.go @@ -0,0 +1,29 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "time" +) + +// Runtime statistics for a container +type Statistics struct { + Timestamp time.Time `json:"Timestamp,omitempty"` + + ContainerStartTime time.Time `json:"ContainerStartTime,omitempty"` + + Uptime100ns uint64 `json:"Uptime100ns,omitempty"` + + Processor *ProcessorStats `json:"Processor,omitempty"` + + Memory *MemoryStats `json:"Memory,omitempty"` + + Storage *StorageStats `json:"Storage,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage.go new file mode 100644 index 0000000000000..2627af91323e3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Storage struct { + + // List of layers that describe the parent hierarchy for a container's storage. These layers combined together, presented as a disposable and/or committable working storage, are used by the container to record all changes done to the parent layers. + Layers []Layer `json:"Layers,omitempty"` + + // Path that points to the scratch space of a container, where parent layers are combined together to present a new disposable and/or committable layer with the changes done during its runtime. + Path string `json:"Path,omitempty"` + + QoS *StorageQoS `json:"QoS,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_qo_s.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_qo_s.go new file mode 100644 index 0000000000000..9c5e6eb53235c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_qo_s.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type StorageQoS struct { + IopsMaximum int32 `json:"IopsMaximum,omitempty"` + + BandwidthMaximum int32 `json:"BandwidthMaximum,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_stats.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_stats.go new file mode 100644 index 0000000000000..4f042ffd9371c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/storage_stats.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Storage runtime statistics +type StorageStats struct { + ReadCountNormalized uint64 `json:"ReadCountNormalized,omitempty"` + + ReadSizeBytes uint64 `json:"ReadSizeBytes,omitempty"` + + WriteCountNormalized uint64 `json:"WriteCountNormalized,omitempty"` + + WriteSizeBytes uint64 `json:"WriteSizeBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/topology.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/topology.go new file mode 100644 index 0000000000000..83486994036dd --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/topology.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Topology struct { + Memory *Memory2 `json:"Memory,omitempty"` + + Processor *Processor2 `json:"Processor,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi.go new file mode 100644 index 0000000000000..0e48ece500c61 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Uefi struct { + EnableDebugger bool `json:"EnableDebugger,omitempty"` + + SecureBootTemplateId string `json:"SecureBootTemplateId,omitempty"` + + BootThis *UefiBootEntry `json:"BootThis,omitempty"` + + Console string `json:"Console,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi_boot_entry.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi_boot_entry.go new file mode 100644 index 0000000000000..3ab409d825e58 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/uefi_boot_entry.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type UefiBootEntry struct { + DeviceType string `json:"DeviceType,omitempty"` + + DevicePath string `json:"DevicePath,omitempty"` + + DiskNumber int32 `json:"DiskNumber,omitempty"` + + OptionalData string `json:"OptionalData,omitempty"` + + VmbFsRootPath string `json:"VmbFsRootPath,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/version.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/version.go new file mode 100644 index 0000000000000..2abfccca31547 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/version.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Version struct { + Major int32 `json:"Major,omitempty"` + + Minor int32 `json:"Minor,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/video_monitor.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/video_monitor.go new file mode 100644 index 0000000000000..ec5d0fb936dfa --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/video_monitor.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VideoMonitor struct { + HorizontalResolution int32 `json:"HorizontalResolution,omitempty"` + + VerticalResolution int32 `json:"VerticalResolution,omitempty"` + + ConnectionOptions *RdpConnectionOptions `json:"ConnectionOptions,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_machine.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_machine.go new file mode 100644 index 0000000000000..2d22b1bcb0843 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_machine.go @@ -0,0 +1,32 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualMachine struct { + + // StopOnReset is private in the schema. If regenerated need to put back. + StopOnReset bool `json:"StopOnReset,omitempty"` + + Chipset *Chipset `json:"Chipset,omitempty"` + + ComputeTopology *Topology `json:"ComputeTopology,omitempty"` + + Devices *Devices `json:"Devices,omitempty"` + + GuestState *GuestState `json:"GuestState,omitempty"` + + RestoreState *RestoreState `json:"RestoreState,omitempty"` + + RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"` + + StorageQoS *StorageQoS `json:"StorageQoS,omitempty"` + + GuestConnection *GuestConnection `json:"GuestConnection,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_node_info.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_node_info.go new file mode 100644 index 0000000000000..91a3c83d4ff1c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_node_info.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualNodeInfo struct { + VirtualNodeIndex int32 `json:"VirtualNodeIndex,omitempty"` + + PhysicalNodeNumber int32 `json:"PhysicalNodeNumber,omitempty"` + + VirtualProcessorCount int32 `json:"VirtualProcessorCount,omitempty"` + + MemoryUsageInPages int32 `json:"MemoryUsageInPages,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_controller.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_controller.go new file mode 100644 index 0000000000000..f5b7f3e38c0f2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_controller.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualPMemController struct { + Devices map[string]VirtualPMemDevice `json:"Devices,omitempty"` + + MaximumCount uint32 `json:"MaximumCount,omitempty"` + + MaximumSizeBytes uint64 `json:"MaximumSizeBytes,omitempty"` + + Backing string `json:"Backing,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_device.go new file mode 100644 index 0000000000000..70cf2d90de034 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_device.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualPMemDevice struct { + HostPath string `json:"HostPath,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` + + ImageFormat string `json:"ImageFormat,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_device.go new file mode 100644 index 0000000000000..f5e05903c5496 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_device.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.3 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// TODO: This is pre-release support in schema 2.3. Need to add build number +// docs when a public build with this is out. +type VirtualPciDevice struct { + Functions []VirtualPciFunction `json:",omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_function.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_function.go new file mode 100644 index 0000000000000..cedb7d18bc287 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_pci_function.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.3 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// TODO: This is pre-release support in schema 2.3. Need to add build number +// docs when a public build with this is out. +type VirtualPciFunction struct { + DeviceInstancePath string `json:",omitempty"` + + VirtualFunction uint16 `json:",omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb.go new file mode 100644 index 0000000000000..362df363e13ad --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmb struct { + Shares []VirtualSmbShare `json:"Shares,omitempty"` + + DirectFileMappingInMB int64 `json:"DirectFileMappingInMB,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share.go new file mode 100644 index 0000000000000..915e9b6386ab3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmbShare struct { + Name string `json:"Name,omitempty"` + + Path string `json:"Path,omitempty"` + + AllowedFiles []string `json:"AllowedFiles,omitempty"` + + Options *VirtualSmbShareOptions `json:"Options,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share_options.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share_options.go new file mode 100644 index 0000000000000..75196bd8c8dbb --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_smb_share_options.go @@ -0,0 +1,62 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmbShareOptions struct { + ReadOnly bool `json:"ReadOnly,omitempty"` + + // convert exclusive access to shared read access + ShareRead bool `json:"ShareRead,omitempty"` + + // all opens will use cached I/O + CacheIo bool `json:"CacheIo,omitempty"` + + // disable oplock support + NoOplocks bool `json:"NoOplocks,omitempty"` + + // Acquire the backup privilege when attempting to open + TakeBackupPrivilege bool `json:"TakeBackupPrivilege,omitempty"` + + // Use the identity of the share root when opening + UseShareRootIdentity bool `json:"UseShareRootIdentity,omitempty"` + + // disable Direct Mapping + NoDirectmap bool `json:"NoDirectmap,omitempty"` + + // disable Byterange locks + NoLocks bool `json:"NoLocks,omitempty"` + + // disable Directory CHange Notifications + NoDirnotify bool `json:"NoDirnotify,omitempty"` + + // share is use for VM shared memory + VmSharedMemory bool `json:"VmSharedMemory,omitempty"` + + // allow access only to the files specified in AllowedFiles + RestrictFileAccess bool `json:"RestrictFileAccess,omitempty"` + + // disable all oplocks except Level II + ForceLevelIIOplocks bool `json:"ForceLevelIIOplocks,omitempty"` + + // Allow the host to reparse this base layer + ReparseBaseLayer bool `json:"ReparseBaseLayer,omitempty"` + + // Enable pseudo-oplocks + PseudoOplocks bool `json:"PseudoOplocks,omitempty"` + + // All opens will use non-cached IO + NonCacheIo bool `json:"NonCacheIo,omitempty"` + + // Enable pseudo directory change notifications + PseudoDirnotify bool `json:"PseudoDirnotify,omitempty"` + + // Block directory enumeration, renames, and deletes. + SingleFileMapping bool `json:"SingleFileMapping,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_memory.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_memory.go new file mode 100644 index 0000000000000..8e1836dd6be42 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_memory.go @@ -0,0 +1,26 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VmMemory struct { + AvailableMemory int32 `json:"AvailableMemory,omitempty"` + + AvailableMemoryBuffer int32 `json:"AvailableMemoryBuffer,omitempty"` + + ReservedMemory uint64 `json:"ReservedMemory,omitempty"` + + AssignedMemory uint64 `json:"AssignedMemory,omitempty"` + + SlpActive bool `json:"SlpActive,omitempty"` + + BalancingEnabled bool `json:"BalancingEnabled,omitempty"` + + DmOperationInProgress bool `json:"DmOperationInProgress,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_processor_limits.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_processor_limits.go new file mode 100644 index 0000000000000..de1b9cf1ae20e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/vm_processor_limits.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.4 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// ProcessorLimits is used when modifying processor scheduling limits of a virtual machine. +type ProcessorLimits struct { + // Maximum amount of host CPU resources that the virtual machine can use. + Limit uint64 `json:"Limit,omitempty"` + // Value describing the relative priority of this virtual machine compared to other virtual machines. + Weight uint64 `json:"Weight,omitempty"` + // Minimum amount of host CPU resources that the virtual machine is guaranteed. + Reservation uint64 `json:"Reservation,omitempty"` + // Provides the target maximum CPU frequency, in MHz, for a virtual machine. + MaximumFrequencyMHz uint32 `json:"MaximumFrequencyMHz,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/windows_crash_reporting.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/windows_crash_reporting.go new file mode 100644 index 0000000000000..8ed7e566d6425 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/windows_crash_reporting.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type WindowsCrashReporting struct { + DumpFileName string `json:"DumpFileName,omitempty"` + + MaxDumpSize int64 `json:"MaxDumpSize,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/service.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/service.go new file mode 100644 index 0000000000000..a634dfc151587 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/service.go @@ -0,0 +1,49 @@ +package hcs + +import ( + "context" + "encoding/json" + + hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" + "github.com/Microsoft/hcsshim/internal/vmcompute" +) + +// GetServiceProperties returns properties of the host compute service. +func GetServiceProperties(ctx context.Context, q hcsschema.PropertyQuery) (*hcsschema.ServiceProperties, error) { + operation := "hcs::GetServiceProperties" + + queryb, err := json.Marshal(q) + if err != nil { + return nil, err + } + propertiesJSON, resultJSON, err := vmcompute.HcsGetServiceProperties(ctx, string(queryb)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, &HcsError{Op: operation, Err: err, Events: events} + } + + if propertiesJSON == "" { + return nil, ErrUnexpectedValue + } + properties := &hcsschema.ServiceProperties{} + if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil { + return nil, err + } + return properties, nil +} + +// ModifyServiceSettings modifies settings of the host compute service. +func ModifyServiceSettings(ctx context.Context, settings hcsschema.ModificationRequest) error { + operation := "hcs::ModifyServiceSettings" + + settingsJSON, err := json.Marshal(settings) + if err != nil { + return err + } + resultJSON, err := vmcompute.HcsModifyServiceSettings(ctx, string(settingsJSON)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return &HcsError{Op: operation, Err: err, Events: events} + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go index 57afd5ec6ba35..75499c967f0f3 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go @@ -1,56 +1,55 @@ package hcs import ( + "context" "encoding/json" - "fmt" - "os" - "strconv" + "errors" + "strings" "sync" "syscall" - "time" - "github.com/Microsoft/hcsshim/internal/interop" - "github.com/Microsoft/hcsshim/internal/schema1" + "github.com/Microsoft/hcsshim/internal/cow" + "github.com/Microsoft/hcsshim/internal/hcs/schema1" + hcsschema "github.com/Microsoft/hcsshim/internal/hcs/schema2" + "github.com/Microsoft/hcsshim/internal/log" + "github.com/Microsoft/hcsshim/internal/oc" "github.com/Microsoft/hcsshim/internal/timeout" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/vmcompute" + "go.opencensus.io/trace" ) -// currentContainerStarts is used to limit the number of concurrent container -// starts. -var currentContainerStarts containerStarts - -type containerStarts struct { - maxParallel int - inProgress int - sync.Mutex -} - -func init() { - mpsS := os.Getenv("HCSSHIM_MAX_PARALLEL_START") - if len(mpsS) > 0 { - mpsI, err := strconv.Atoi(mpsS) - if err != nil || mpsI < 0 { - return - } - currentContainerStarts.maxParallel = mpsI - } -} - type System struct { handleLock sync.RWMutex - handle hcsSystem + handle vmcompute.HcsSystem id string callbackNumber uintptr + + closedWaitOnce sync.Once + waitBlock chan struct{} + waitError error + exitError error + os, typ string +} + +func newSystem(id string) *System { + return &System{ + id: id, + waitBlock: make(chan struct{}), + } } // CreateComputeSystem creates a new compute system with the given configuration but does not start it. -func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (*System, error) { - operation := "CreateComputeSystem" - title := "hcsshim::" + operation +func CreateComputeSystem(ctx context.Context, id string, hcsDocumentInterface interface{}) (_ *System, err error) { + operation := "hcs::CreateComputeSystem" - computeSystem := &System{ - id: id, - } + // hcsCreateComputeSystemContext is an async operation. Start the outer span + // here to measure the full create time. + ctx, span := trace.StartSpan(ctx, operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", id)) + + computeSystem := newSystem(id) hcsDocumentB, err := json.Marshal(hcsDocumentInterface) if err != nil { @@ -58,157 +57,145 @@ func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (*System, } hcsDocument := string(hcsDocumentB) - logrus.Debugf(title+" ID=%s config=%s", id, hcsDocument) var ( - resultp *uint16 - identity syscall.Handle + identity syscall.Handle + resultJSON string + createError error ) - completed := false - go syscallWatcher(fmt.Sprintf("CreateCompleteSystem %s: %s", id, hcsDocument), &completed) - createError := hcsCreateComputeSystem(id, hcsDocument, identity, &computeSystem.handle, &resultp) - completed = true - + computeSystem.handle, resultJSON, createError = vmcompute.HcsCreateComputeSystem(ctx, id, hcsDocument, identity) if createError == nil || IsPending(createError) { - if err := computeSystem.registerCallback(); err != nil { + defer func() { + if err != nil { + computeSystem.Close() + } + }() + if err = computeSystem.registerCallback(ctx); err != nil { // Terminate the compute system if it still exists. We're okay to // ignore a failure here. - computeSystem.Terminate() - return nil, makeSystemError(computeSystem, operation, "", err, nil) + _ = computeSystem.Terminate(ctx) + return nil, makeSystemError(computeSystem, operation, err, nil) } } - events, err := processAsyncHcsResult(createError, resultp, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.SystemCreate) + events, err := processAsyncHcsResult(ctx, createError, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.SystemCreate) if err != nil { if err == ErrTimeout { // Terminate the compute system if it still exists. We're okay to // ignore a failure here. - computeSystem.Terminate() + _ = computeSystem.Terminate(ctx) } - return nil, makeSystemError(computeSystem, operation, hcsDocument, err, events) + return nil, makeSystemError(computeSystem, operation, err, events) + } + go computeSystem.waitBackground() + if err = computeSystem.getCachedProperties(ctx); err != nil { + return nil, err } - - logrus.Debugf(title+" succeeded id=%s handle=%d", id, computeSystem.handle) return computeSystem, nil } // OpenComputeSystem opens an existing compute system by ID. -func OpenComputeSystem(id string) (*System, error) { - operation := "OpenComputeSystem" - title := "hcsshim::" + operation - logrus.Debugf(title+" ID=%s", id) +func OpenComputeSystem(ctx context.Context, id string) (*System, error) { + operation := "hcs::OpenComputeSystem" - computeSystem := &System{ - id: id, - } - - var ( - handle hcsSystem - resultp *uint16 - ) - err := hcsOpenComputeSystem(id, &handle, &resultp) - events := processHcsResult(resultp) + computeSystem := newSystem(id) + handle, resultJSON, err := vmcompute.HcsOpenComputeSystem(ctx, id) + events := processHcsResult(ctx, resultJSON) if err != nil { - return nil, makeSystemError(computeSystem, operation, "", err, events) + return nil, makeSystemError(computeSystem, operation, err, events) } - computeSystem.handle = handle + defer func() { + if err != nil { + computeSystem.Close() + } + }() + if err = computeSystem.registerCallback(ctx); err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) + } + go computeSystem.waitBackground() + if err = computeSystem.getCachedProperties(ctx); err != nil { + return nil, err + } + return computeSystem, nil +} - if err := computeSystem.registerCallback(); err != nil { - return nil, makeSystemError(computeSystem, operation, "", err, nil) +func (computeSystem *System) getCachedProperties(ctx context.Context) error { + props, err := computeSystem.Properties(ctx) + if err != nil { + return err } + computeSystem.typ = strings.ToLower(props.SystemType) + computeSystem.os = strings.ToLower(props.RuntimeOSType) + if computeSystem.os == "" && computeSystem.typ == "container" { + // Pre-RS5 HCS did not return the OS, but it only supported containers + // that ran Windows. + computeSystem.os = "windows" + } + return nil +} - logrus.Debugf(title+" succeeded id=%s handle=%d", id, handle) - return computeSystem, nil +// OS returns the operating system of the compute system, "linux" or "windows". +func (computeSystem *System) OS() string { + return computeSystem.os +} + +// IsOCI returns whether processes in the compute system should be created via +// OCI. +func (computeSystem *System) IsOCI() bool { + return computeSystem.os == "linux" && computeSystem.typ == "container" } // GetComputeSystems gets a list of the compute systems on the system that match the query -func GetComputeSystems(q schema1.ComputeSystemQuery) ([]schema1.ContainerProperties, error) { - operation := "GetComputeSystems" - title := "hcsshim::" + operation +func GetComputeSystems(ctx context.Context, q schema1.ComputeSystemQuery) ([]schema1.ContainerProperties, error) { + operation := "hcs::GetComputeSystems" queryb, err := json.Marshal(q) if err != nil { return nil, err } - query := string(queryb) - logrus.Debugf(title+" query=%s", query) - - var ( - resultp *uint16 - computeSystemsp *uint16 - ) - completed := false - go syscallWatcher(fmt.Sprintf("GetComputeSystems %s:", query), &completed) - err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp) - completed = true - events := processHcsResult(resultp) + computeSystemsJSON, resultJSON, err := vmcompute.HcsEnumerateComputeSystems(ctx, string(queryb)) + events := processHcsResult(ctx, resultJSON) if err != nil { return nil, &HcsError{Op: operation, Err: err, Events: events} } - if computeSystemsp == nil { + if computeSystemsJSON == "" { return nil, ErrUnexpectedValue } - computeSystemsRaw := interop.ConvertAndFreeCoTaskMemBytes(computeSystemsp) computeSystems := []schema1.ContainerProperties{} - if err := json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil { + if err = json.Unmarshal([]byte(computeSystemsJSON), &computeSystems); err != nil { return nil, err } - logrus.Debugf(title + " succeeded") return computeSystems, nil } // Start synchronously starts the computeSystem. -func (computeSystem *System) Start() error { +func (computeSystem *System) Start(ctx context.Context) (err error) { + operation := "hcs::System::Start" + + // hcsStartComputeSystemContext is an async operation. Start the outer span + // here to measure the full start time. + ctx, span := trace.StartSpan(ctx, operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::Start ID=" + computeSystem.ID() - logrus.Debugf(title) if computeSystem.handle == 0 { - return makeSystemError(computeSystem, "Start", "", ErrAlreadyClosed, nil) - } - - // This is a very simple backoff-retry loop to limit the number - // of parallel container starts if environment variable - // HCSSHIM_MAX_PARALLEL_START is set to a positive integer. - // It should generally only be used as a workaround to various - // platform issues that exist between RS1 and RS4 as of Aug 2018 - if currentContainerStarts.maxParallel > 0 { - for { - currentContainerStarts.Lock() - if currentContainerStarts.inProgress < currentContainerStarts.maxParallel { - currentContainerStarts.inProgress++ - currentContainerStarts.Unlock() - break - } - if currentContainerStarts.inProgress == currentContainerStarts.maxParallel { - currentContainerStarts.Unlock() - time.Sleep(100 * time.Millisecond) - } - } - // Make sure we decrement the count when we are done. - defer func() { - currentContainerStarts.Lock() - currentContainerStarts.inProgress-- - currentContainerStarts.Unlock() - }() + return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } - var resultp *uint16 - completed := false - go syscallWatcher(fmt.Sprintf("StartComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsStartComputeSystem(computeSystem.handle, "", &resultp) - completed = true - events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.SystemStart) + resultJSON, err := vmcompute.HcsStartComputeSystem(ctx, computeSystem.handle, "") + events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.SystemStart) if err != nil { - return makeSystemError(computeSystem, "Start", "", err, events) + return makeSystemError(computeSystem, operation, err, events) } - logrus.Debugf(title + " succeeded") return nil } @@ -217,319 +204,389 @@ func (computeSystem *System) ID() string { return computeSystem.id } -// Shutdown requests a compute system shutdown, if IsPending() on the error returned is true, -// it may not actually be shut down until Wait() succeeds. -func (computeSystem *System) Shutdown() error { +// Shutdown requests a compute system shutdown. +func (computeSystem *System) Shutdown(ctx context.Context) error { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::Shutdown" - logrus.Debugf(title) + + operation := "hcs::System::Shutdown" + if computeSystem.handle == 0 { - return makeSystemError(computeSystem, "Shutdown", "", ErrAlreadyClosed, nil) + return nil } - var resultp *uint16 - completed := false - go syscallWatcher(fmt.Sprintf("ShutdownComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsShutdownComputeSystem(computeSystem.handle, "", &resultp) - completed = true - events := processHcsResult(resultp) - if err != nil { - return makeSystemError(computeSystem, "Shutdown", "", err, events) + resultJSON, err := vmcompute.HcsShutdownComputeSystem(ctx, computeSystem.handle, "") + events := processHcsResult(ctx, resultJSON) + switch err { + case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending: + default: + return makeSystemError(computeSystem, operation, err, events) } - - logrus.Debugf(title + " succeeded") return nil } -// Terminate requests a compute system terminate, if IsPending() on the error returned is true, -// it may not actually be shut down until Wait() succeeds. -func (computeSystem *System) Terminate() error { +// Terminate requests a compute system terminate. +func (computeSystem *System) Terminate(ctx context.Context) error { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::Terminate ID=" + computeSystem.ID() - logrus.Debugf(title) + + operation := "hcs::System::Terminate" if computeSystem.handle == 0 { - return makeSystemError(computeSystem, "Terminate", "", ErrAlreadyClosed, nil) + return nil } - var resultp *uint16 - completed := false - go syscallWatcher(fmt.Sprintf("TerminateComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsTerminateComputeSystem(computeSystem.handle, "", &resultp) - completed = true - events := processHcsResult(resultp) - if err != nil { - return makeSystemError(computeSystem, "Terminate", "", err, events) + resultJSON, err := vmcompute.HcsTerminateComputeSystem(ctx, computeSystem.handle, "") + events := processHcsResult(ctx, resultJSON) + switch err { + case nil, ErrVmcomputeAlreadyStopped, ErrComputeSystemDoesNotExist, ErrVmcomputeOperationPending: + default: + return makeSystemError(computeSystem, operation, err, events) } - - logrus.Debugf(title + " succeeded") return nil } -// Wait synchronously waits for the compute system to shutdown or terminate. +// waitBackground waits for the compute system exit notification. Once received +// sets `computeSystem.waitError` (if any) and unblocks all `Wait` calls. +// +// This MUST be called exactly once per `computeSystem.handle` but `Wait` is +// safe to call multiple times. +func (computeSystem *System) waitBackground() { + operation := "hcs::System::waitBackground" + ctx, span := trace.StartSpan(context.Background(), operation) + defer span.End() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + + err := waitForNotification(ctx, computeSystem.callbackNumber, hcsNotificationSystemExited, nil) + switch err { + case nil: + log.G(ctx).Debug("system exited") + case ErrVmcomputeUnexpectedExit: + log.G(ctx).Debug("unexpected system exit") + computeSystem.exitError = makeSystemError(computeSystem, operation, err, nil) + err = nil + default: + err = makeSystemError(computeSystem, operation, err, nil) + } + computeSystem.closedWaitOnce.Do(func() { + computeSystem.waitError = err + close(computeSystem.waitBlock) + }) + oc.SetSpanStatus(span, err) +} + +// Wait synchronously waits for the compute system to shutdown or terminate. If +// the compute system has already exited returns the previous error (if any). func (computeSystem *System) Wait() error { - title := "hcsshim::ComputeSystem::Wait ID=" + computeSystem.ID() - logrus.Debugf(title) + <-computeSystem.waitBlock + return computeSystem.waitError +} - err := waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, nil) - if err != nil { - return makeSystemError(computeSystem, "Wait", "", err, nil) +// ExitError returns an error describing the reason the compute system terminated. +func (computeSystem *System) ExitError() error { + select { + case <-computeSystem.waitBlock: + if computeSystem.waitError != nil { + return computeSystem.waitError + } + return computeSystem.exitError + default: + return errors.New("container not exited") } - - logrus.Debugf(title + " succeeded") - return nil } -// WaitTimeout synchronously waits for the compute system to terminate or the duration to elapse. -// If the timeout expires, IsTimeout(err) == true -func (computeSystem *System) WaitTimeout(timeout time.Duration) error { - title := "hcsshim::ComputeSystem::WaitTimeout ID=" + computeSystem.ID() - logrus.Debugf(title) +// Properties returns the requested container properties targeting a V1 schema container. +func (computeSystem *System) Properties(ctx context.Context, types ...schema1.PropertyType) (*schema1.ContainerProperties, error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + operation := "hcs::System::Properties" - err := waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, &timeout) + queryBytes, err := json.Marshal(schema1.PropertyQuery{PropertyTypes: types}) if err != nil { - return makeSystemError(computeSystem, "WaitTimeout", "", err, nil) + return nil, makeSystemError(computeSystem, operation, err, nil) } - logrus.Debugf(title + " succeeded") - return nil + propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes)) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, makeSystemError(computeSystem, operation, err, events) + } + + if propertiesJSON == "" { + return nil, ErrUnexpectedValue + } + properties := &schema1.ContainerProperties{} + if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) + } + + return properties, nil } -func (computeSystem *System) Properties(types ...schema1.PropertyType) (*schema1.ContainerProperties, error) { +// PropertiesV2 returns the requested container properties targeting a V2 schema container. +func (computeSystem *System) PropertiesV2(ctx context.Context, types ...hcsschema.PropertyType) (*hcsschema.Properties, error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - queryj, err := json.Marshal(schema1.PropertyQuery{types}) + operation := "hcs::System::PropertiesV2" + + queryBytes, err := json.Marshal(hcsschema.PropertyQuery{PropertyTypes: types}) if err != nil { - return nil, makeSystemError(computeSystem, "Properties", "", err, nil) + return nil, makeSystemError(computeSystem, operation, err, nil) } - var resultp, propertiesp *uint16 - completed := false - go syscallWatcher(fmt.Sprintf("GetComputeSystemProperties %s:", computeSystem.ID()), &completed) - err = hcsGetComputeSystemProperties(computeSystem.handle, string(queryj), &propertiesp, &resultp) - completed = true - events := processHcsResult(resultp) + propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes)) + events := processHcsResult(ctx, resultJSON) if err != nil { - return nil, makeSystemError(computeSystem, "Properties", "", err, events) + return nil, makeSystemError(computeSystem, operation, err, events) } - if propertiesp == nil { + if propertiesJSON == "" { return nil, ErrUnexpectedValue } - propertiesRaw := interop.ConvertAndFreeCoTaskMemBytes(propertiesp) - properties := &schema1.ContainerProperties{} - if err := json.Unmarshal(propertiesRaw, properties); err != nil { - return nil, makeSystemError(computeSystem, "Properties", "", err, nil) + properties := &hcsschema.Properties{} + if err := json.Unmarshal([]byte(propertiesJSON), properties); err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) } + return properties, nil } // Pause pauses the execution of the computeSystem. This feature is not enabled in TP5. -func (computeSystem *System) Pause() error { +func (computeSystem *System) Pause(ctx context.Context) (err error) { + operation := "hcs::System::Pause" + + // hcsPauseComputeSystemContext is an async peration. Start the outer span + // here to measure the full pause time. + ctx, span := trace.StartSpan(ctx, operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::Pause ID=" + computeSystem.ID() - logrus.Debugf(title) if computeSystem.handle == 0 { - return makeSystemError(computeSystem, "Pause", "", ErrAlreadyClosed, nil) + return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } - var resultp *uint16 - completed := false - go syscallWatcher(fmt.Sprintf("PauseComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsPauseComputeSystem(computeSystem.handle, "", &resultp) - completed = true - events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.SystemPause) + resultJSON, err := vmcompute.HcsPauseComputeSystem(ctx, computeSystem.handle, "") + events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.SystemPause) if err != nil { - return makeSystemError(computeSystem, "Pause", "", err, events) + return makeSystemError(computeSystem, operation, err, events) } - logrus.Debugf(title + " succeeded") return nil } // Resume resumes the execution of the computeSystem. This feature is not enabled in TP5. -func (computeSystem *System) Resume() error { +func (computeSystem *System) Resume(ctx context.Context) (err error) { + operation := "hcs::System::Resume" + + // hcsResumeComputeSystemContext is an async operation. Start the outer span + // here to measure the full restore time. + ctx, span := trace.StartSpan(ctx, operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::Resume ID=" + computeSystem.ID() - logrus.Debugf(title) if computeSystem.handle == 0 { - return makeSystemError(computeSystem, "Resume", "", ErrAlreadyClosed, nil) + return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } - var resultp *uint16 - completed := false - go syscallWatcher(fmt.Sprintf("ResumeComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsResumeComputeSystem(computeSystem.handle, "", &resultp) - completed = true - events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.SystemResume) + resultJSON, err := vmcompute.HcsResumeComputeSystem(ctx, computeSystem.handle, "") + events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.SystemResume) if err != nil { - return makeSystemError(computeSystem, "Resume", "", err, events) + return makeSystemError(computeSystem, operation, err, events) } - logrus.Debugf(title + " succeeded") return nil } -// CreateProcess launches a new process within the computeSystem. -func (computeSystem *System) CreateProcess(c interface{}) (*Process, error) { +// Save the compute system +func (computeSystem *System) Save(ctx context.Context, options interface{}) (err error) { + operation := "hcs::System::Save" + + // hcsSaveComputeSystemContext is an async peration. Start the outer span + // here to measure the full save time. + ctx, span := trace.StartSpan(ctx, operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + + saveOptions, err := json.Marshal(options) + if err != nil { + return err + } + computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::CreateProcess ID=" + computeSystem.ID() - var ( - processInfo hcsProcessInformation - processHandle hcsProcess - resultp *uint16 - ) if computeSystem.handle == 0 { - return nil, makeSystemError(computeSystem, "CreateProcess", "", ErrAlreadyClosed, nil) + return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) + } + + result, err := vmcompute.HcsSaveComputeSystem(ctx, computeSystem.handle, string(saveOptions)) + events, err := processAsyncHcsResult(ctx, err, result, computeSystem.callbackNumber, hcsNotificationSystemSaveCompleted, &timeout.SystemSave) + if err != nil { + return makeSystemError(computeSystem, operation, err, events) + } + + return nil +} + +func (computeSystem *System) createProcess(ctx context.Context, operation string, c interface{}) (*Process, *vmcompute.HcsProcessInformation, error) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + if computeSystem.handle == 0 { + return nil, nil, makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } configurationb, err := json.Marshal(c) if err != nil { - return nil, makeSystemError(computeSystem, "CreateProcess", "", err, nil) + return nil, nil, makeSystemError(computeSystem, operation, err, nil) } configuration := string(configurationb) - logrus.Debugf(title+" config=%s", configuration) + processInfo, processHandle, resultJSON, err := vmcompute.HcsCreateProcess(ctx, computeSystem.handle, configuration) + events := processHcsResult(ctx, resultJSON) + if err != nil { + return nil, nil, makeSystemError(computeSystem, operation, err, events) + } - completed := false - go syscallWatcher(fmt.Sprintf("CreateProcess %s: %s", computeSystem.ID(), configuration), &completed) - err = hcsCreateProcess(computeSystem.handle, configuration, &processInfo, &processHandle, &resultp) - completed = true - events := processHcsResult(resultp) + log.G(ctx).WithField("pid", processInfo.ProcessId).Debug("created process pid") + return newProcess(processHandle, int(processInfo.ProcessId), computeSystem), &processInfo, nil +} + +// CreateProcess launches a new process within the computeSystem. +func (computeSystem *System) CreateProcess(ctx context.Context, c interface{}) (cow.Process, error) { + operation := "hcs::System::CreateProcess" + process, processInfo, err := computeSystem.createProcess(ctx, operation, c) if err != nil { - return nil, makeSystemError(computeSystem, "CreateProcess", configuration, err, events) + return nil, err } + defer func() { + if err != nil { + process.Close() + } + }() - process := &Process{ - handle: processHandle, - processID: int(processInfo.ProcessId), - system: computeSystem, - cachedPipes: &cachedPipes{ - stdIn: processInfo.StdInput, - stdOut: processInfo.StdOutput, - stdErr: processInfo.StdError, - }, + pipes, err := makeOpenFiles([]syscall.Handle{processInfo.StdInput, processInfo.StdOutput, processInfo.StdError}) + if err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) } + process.stdin = pipes[0] + process.stdout = pipes[1] + process.stderr = pipes[2] + process.hasCachedStdio = true - if err := process.registerCallback(); err != nil { - return nil, makeSystemError(computeSystem, "CreateProcess", "", err, nil) + if err = process.registerCallback(ctx); err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) } + go process.waitBackground() - logrus.Debugf(title+" succeeded processid=%d", process.processID) return process, nil } // OpenProcess gets an interface to an existing process within the computeSystem. -func (computeSystem *System) OpenProcess(pid int) (*Process, error) { +func (computeSystem *System) OpenProcess(ctx context.Context, pid int) (*Process, error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::OpenProcess ID=" + computeSystem.ID() - logrus.Debugf(title+" processid=%d", pid) - var ( - processHandle hcsProcess - resultp *uint16 - ) + + operation := "hcs::System::OpenProcess" if computeSystem.handle == 0 { - return nil, makeSystemError(computeSystem, "OpenProcess", "", ErrAlreadyClosed, nil) + return nil, makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } - completed := false - go syscallWatcher(fmt.Sprintf("OpenProcess %s: %d", computeSystem.ID(), pid), &completed) - err := hcsOpenProcess(computeSystem.handle, uint32(pid), &processHandle, &resultp) - completed = true - events := processHcsResult(resultp) + processHandle, resultJSON, err := vmcompute.HcsOpenProcess(ctx, computeSystem.handle, uint32(pid)) + events := processHcsResult(ctx, resultJSON) if err != nil { - return nil, makeSystemError(computeSystem, "OpenProcess", "", err, events) - } - - process := &Process{ - handle: processHandle, - processID: pid, - system: computeSystem, + return nil, makeSystemError(computeSystem, operation, err, events) } - if err := process.registerCallback(); err != nil { - return nil, makeSystemError(computeSystem, "OpenProcess", "", err, nil) + process := newProcess(processHandle, pid, computeSystem) + if err = process.registerCallback(ctx); err != nil { + return nil, makeSystemError(computeSystem, operation, err, nil) } + go process.waitBackground() - logrus.Debugf(title+" succeeded processid=%s", process.processID) return process, nil } // Close cleans up any state associated with the compute system but does not terminate or wait for it. -func (computeSystem *System) Close() error { +func (computeSystem *System) Close() (err error) { + operation := "hcs::System::Close" + ctx, span := trace.StartSpan(context.Background(), operation) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("cid", computeSystem.id)) + computeSystem.handleLock.Lock() defer computeSystem.handleLock.Unlock() - title := "hcsshim::ComputeSystem::Close ID=" + computeSystem.ID() - logrus.Debugf(title) // Don't double free this if computeSystem.handle == 0 { return nil } - if err := computeSystem.unregisterCallback(); err != nil { - return makeSystemError(computeSystem, "Close", "", err, nil) + if err = computeSystem.unregisterCallback(ctx); err != nil { + return makeSystemError(computeSystem, operation, err, nil) } - completed := false - go syscallWatcher(fmt.Sprintf("CloseComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsCloseComputeSystem(computeSystem.handle) - completed = true + err = vmcompute.HcsCloseComputeSystem(ctx, computeSystem.handle) if err != nil { - return makeSystemError(computeSystem, "Close", "", err, nil) + return makeSystemError(computeSystem, operation, err, nil) } computeSystem.handle = 0 + computeSystem.closedWaitOnce.Do(func() { + computeSystem.waitError = ErrAlreadyClosed + close(computeSystem.waitBlock) + }) - logrus.Debugf(title + " succeeded") return nil } -func (computeSystem *System) registerCallback() error { - context := ¬ifcationWatcherContext{ - channels: newChannels(), +func (computeSystem *System) registerCallback(ctx context.Context) error { + callbackContext := ¬ificationWatcherContext{ + channels: newSystemChannels(), + systemID: computeSystem.id, } callbackMapLock.Lock() callbackNumber := nextCallback nextCallback++ - callbackMap[callbackNumber] = context + callbackMap[callbackNumber] = callbackContext callbackMapLock.Unlock() - var callbackHandle hcsCallback - err := hcsRegisterComputeSystemCallback(computeSystem.handle, notificationWatcherCallback, callbackNumber, &callbackHandle) + callbackHandle, err := vmcompute.HcsRegisterComputeSystemCallback(ctx, computeSystem.handle, notificationWatcherCallback, callbackNumber) if err != nil { return err } - context.handle = callbackHandle + callbackContext.handle = callbackHandle computeSystem.callbackNumber = callbackNumber return nil } -func (computeSystem *System) unregisterCallback() error { +func (computeSystem *System) unregisterCallback(ctx context.Context) error { callbackNumber := computeSystem.callbackNumber callbackMapLock.RLock() - context := callbackMap[callbackNumber] + callbackContext := callbackMap[callbackNumber] callbackMapLock.RUnlock() - if context == nil { + if callbackContext == nil { return nil } - handle := context.handle + handle := callbackContext.handle if handle == 0 { return nil @@ -537,49 +594,44 @@ func (computeSystem *System) unregisterCallback() error { // hcsUnregisterComputeSystemCallback has its own syncronization // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. - err := hcsUnregisterComputeSystemCallback(handle) + err := vmcompute.HcsUnregisterComputeSystemCallback(ctx, handle) if err != nil { return err } - closeChannels(context.channels) + closeChannels(callbackContext.channels) callbackMapLock.Lock() - callbackMap[callbackNumber] = nil + delete(callbackMap, callbackNumber) callbackMapLock.Unlock() - handle = 0 + handle = 0 //nolint:ineffassign return nil } -// Modifies the System by sending a request to HCS -func (computeSystem *System) Modify(config interface{}) error { +// Modify the System by sending a request to HCS +func (computeSystem *System) Modify(ctx context.Context, config interface{}) error { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::Modify ID=" + computeSystem.id + + operation := "hcs::System::Modify" if computeSystem.handle == 0 { - return makeSystemError(computeSystem, "Modify", "", ErrAlreadyClosed, nil) + return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil) } - requestJSON, err := json.Marshal(config) + requestBytes, err := json.Marshal(config) if err != nil { return err } - requestString := string(requestJSON) - logrus.Debugf(title + " " + requestString) - - var resultp *uint16 - completed := false - go syscallWatcher(fmt.Sprintf("ModifyComputeSystem %s: %s", computeSystem.ID(), requestString), &completed) - err = hcsModifyComputeSystem(computeSystem.handle, requestString, &resultp) - completed = true - events := processHcsResult(resultp) + requestJSON := string(requestBytes) + resultJSON, err := vmcompute.HcsModifyComputeSystem(ctx, computeSystem.handle, requestJSON) + events := processHcsResult(ctx, resultJSON) if err != nil { - return makeSystemError(computeSystem, "Modify", requestString, err, events) + return makeSystemError(computeSystem, operation, err, events) } - logrus.Debugf(title + " succeeded ") + return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go index a638677ed5afb..3342e5bb94868 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go @@ -1,10 +1,15 @@ package hcs import ( + "context" "io" "syscall" "github.com/Microsoft/go-winio" + diskutil "github.com/Microsoft/go-winio/vhd" + "github.com/Microsoft/hcsshim/computestorage" + "github.com/pkg/errors" + "golang.org/x/sys/windows" ) // makeOpenFiles calls winio.MakeOpenFile for each handle in a slice but closes all the handles @@ -31,3 +36,27 @@ func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) { } return fs, nil } + +// CreateNTFSVHD creates a VHD formatted with NTFS of size `sizeGB` at the given `vhdPath`. +func CreateNTFSVHD(ctx context.Context, vhdPath string, sizeGB uint32) (err error) { + if err := diskutil.CreateVhdx(vhdPath, sizeGB, 1); err != nil { + return errors.Wrap(err, "failed to create VHD") + } + + vhd, err := diskutil.OpenVirtualDisk(vhdPath, diskutil.VirtualDiskAccessNone, diskutil.OpenVirtualDiskFlagNone) + if err != nil { + return errors.Wrap(err, "failed to open VHD") + } + defer func() { + err2 := windows.CloseHandle(windows.Handle(vhd)) + if err == nil { + err = errors.Wrap(err2, "failed to close VHD") + } + }() + + if err := computestorage.FormatWritableLayerVhd(ctx, windows.Handle(vhd)); err != nil { + return errors.Wrap(err, "failed to format VHD") + } + + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go index 91e212c574829..db4e14fdfb29e 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go @@ -1,28 +1,34 @@ package hcs import ( + "context" "time" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/log" ) -func processAsyncHcsResult(err error, resultp *uint16, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) ([]ErrorEvent, error) { - events := processHcsResult(resultp) +func processAsyncHcsResult(ctx context.Context, err error, resultJSON string, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) ([]ErrorEvent, error) { + events := processHcsResult(ctx, resultJSON) if IsPending(err) { - return nil, waitForNotification(callbackNumber, expectedNotification, timeout) + return nil, waitForNotification(ctx, callbackNumber, expectedNotification, timeout) } return events, err } -func waitForNotification(callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error { +func waitForNotification(ctx context.Context, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error { callbackMapLock.RLock() + if _, ok := callbackMap[callbackNumber]; !ok { + callbackMapLock.RUnlock() + log.G(ctx).WithField("callbackNumber", callbackNumber).Error("failed to waitForNotification: callbackNumber does not exist in callbackMap") + return ErrHandleClose + } channels := callbackMap[callbackNumber].channels callbackMapLock.RUnlock() expectedChannel := channels[expectedNotification] if expectedChannel == nil { - logrus.Errorf("unknown notification type in waitForNotification %x", expectedNotification) + log.G(ctx).WithField("type", expectedNotification).Error("unknown notification type in waitForNotification") return ErrInvalidNotificationType } @@ -59,5 +65,4 @@ func waitForNotification(callbackNumber uintptr, expectedNotification hcsNotific case <-c: return ErrTimeout } - return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go deleted file mode 100644 index 6b94bc9ff8172..0000000000000 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go +++ /dev/null @@ -1,30 +0,0 @@ -package hcs - -import ( - "time" - - "github.com/Microsoft/hcsshim/internal/timeout" - "github.com/sirupsen/logrus" -) - -// syscallWatcher is used as a very simple goroutine around calls into -// the platform. In some cases, we have seen HCS APIs not returning due to -// various bugs, and the goroutine making the syscall ends up not returning, -// prior to its async callback. By spinning up a syscallWatcher, it allows -// us to at least log a warning if a syscall doesn't complete in a reasonable -// amount of time. -// -// Usage is: -// -// completed := false -// go syscallWatcher("some description", &completed) -// -// completed = true -// -func syscallWatcher(description string, syscallCompleted *bool) { - time.Sleep(timeout.SyscallWatcher) - if *syscallCompleted { - return - } - logrus.Warnf("%s: Did not complete within %s. This may indicate a platform issue. If it appears to be making no forward progress, obtain the stacks and see is there is a syscall stuck in the platform API for a significant length of time.", description, timeout.SyscallWatcher) -} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go deleted file mode 100644 index 48d5cd32b99fe..0000000000000 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go +++ /dev/null @@ -1,441 +0,0 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT - -package hcs - -import ( - "syscall" - "unsafe" - - "github.com/Microsoft/hcsshim/internal/interop" - "golang.org/x/sys/windows" -) - -var _ unsafe.Pointer - -// Do the interface allocations only once for common -// Errno values. -const ( - errnoERROR_IO_PENDING = 997 -) - -var ( - errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) -) - -// errnoErr returns common boxed Errno values, to prevent -// allocations at runtime. -func errnoErr(e syscall.Errno) error { - switch e { - case 0: - return nil - case errnoERROR_IO_PENDING: - return errERROR_IO_PENDING - } - // TODO: add more here, after collecting data on the common - // error values see on Windows. (perhaps when running - // all.bat?) - return e -} - -var ( - modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") - - procHcsEnumerateComputeSystems = modvmcompute.NewProc("HcsEnumerateComputeSystems") - procHcsCreateComputeSystem = modvmcompute.NewProc("HcsCreateComputeSystem") - procHcsOpenComputeSystem = modvmcompute.NewProc("HcsOpenComputeSystem") - procHcsCloseComputeSystem = modvmcompute.NewProc("HcsCloseComputeSystem") - procHcsStartComputeSystem = modvmcompute.NewProc("HcsStartComputeSystem") - procHcsShutdownComputeSystem = modvmcompute.NewProc("HcsShutdownComputeSystem") - procHcsTerminateComputeSystem = modvmcompute.NewProc("HcsTerminateComputeSystem") - procHcsPauseComputeSystem = modvmcompute.NewProc("HcsPauseComputeSystem") - procHcsResumeComputeSystem = modvmcompute.NewProc("HcsResumeComputeSystem") - procHcsGetComputeSystemProperties = modvmcompute.NewProc("HcsGetComputeSystemProperties") - procHcsModifyComputeSystem = modvmcompute.NewProc("HcsModifyComputeSystem") - procHcsRegisterComputeSystemCallback = modvmcompute.NewProc("HcsRegisterComputeSystemCallback") - procHcsUnregisterComputeSystemCallback = modvmcompute.NewProc("HcsUnregisterComputeSystemCallback") - procHcsCreateProcess = modvmcompute.NewProc("HcsCreateProcess") - procHcsOpenProcess = modvmcompute.NewProc("HcsOpenProcess") - procHcsCloseProcess = modvmcompute.NewProc("HcsCloseProcess") - procHcsTerminateProcess = modvmcompute.NewProc("HcsTerminateProcess") - procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo") - procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties") - procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess") - procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties") - procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback") - procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback") -) - -func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(query) - if hr != nil { - return - } - return _hcsEnumerateComputeSystems(_p0, computeSystems, result) -} - -func _hcsEnumerateComputeSystems(query *uint16, computeSystems **uint16, result **uint16) (hr error) { - if hr = procHcsEnumerateComputeSystems.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsEnumerateComputeSystems.Addr(), 3, uintptr(unsafe.Pointer(query)), uintptr(unsafe.Pointer(computeSystems)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - var _p1 *uint16 - _p1, hr = syscall.UTF16PtrFromString(configuration) - if hr != nil { - return - } - return _hcsCreateComputeSystem(_p0, _p1, identity, computeSystem, result) -} - -func _hcsCreateComputeSystem(id *uint16, configuration *uint16, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) { - if hr = procHcsCreateComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procHcsCreateComputeSystem.Addr(), 5, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), uintptr(identity), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result)), 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _hcsOpenComputeSystem(_p0, computeSystem, result) -} - -func _hcsOpenComputeSystem(id *uint16, computeSystem *hcsSystem, result **uint16) (hr error) { - if hr = procHcsOpenComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsOpenComputeSystem.Addr(), 3, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) { - if hr = procHcsCloseComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsCloseComputeSystem.Addr(), 1, uintptr(computeSystem), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsStartComputeSystem(computeSystem, _p0, result) -} - -func _hcsStartComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { - if hr = procHcsStartComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsStartComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsShutdownComputeSystem(computeSystem, _p0, result) -} - -func _hcsShutdownComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { - if hr = procHcsShutdownComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsShutdownComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsTerminateComputeSystem(computeSystem, _p0, result) -} - -func _hcsTerminateComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { - if hr = procHcsTerminateComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsTerminateComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsPauseComputeSystem(computeSystem, _p0, result) -} - -func _hcsPauseComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { - if hr = procHcsPauseComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsPauseComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsResumeComputeSystem(computeSystem, _p0, result) -} - -func _hcsResumeComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { - if hr = procHcsResumeComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsResumeComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(propertyQuery) - if hr != nil { - return - } - return _hcsGetComputeSystemProperties(computeSystem, _p0, properties, result) -} - -func _hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { - if hr = procHcsGetComputeSystemProperties.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procHcsGetComputeSystemProperties.Addr(), 4, uintptr(computeSystem), uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result)), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(configuration) - if hr != nil { - return - } - return _hcsModifyComputeSystem(computeSystem, _p0, result) -} - -func _hcsModifyComputeSystem(computeSystem hcsSystem, configuration *uint16, result **uint16) (hr error) { - if hr = procHcsModifyComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsModifyComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) { - if hr = procHcsRegisterComputeSystemCallback.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procHcsRegisterComputeSystemCallback.Addr(), 4, uintptr(computeSystem), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) { - if hr = procHcsUnregisterComputeSystemCallback.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsUnregisterComputeSystemCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(processParameters) - if hr != nil { - return - } - return _hcsCreateProcess(computeSystem, _p0, processInformation, process, result) -} - -func _hcsCreateProcess(computeSystem hcsSystem, processParameters *uint16, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) { - if hr = procHcsCreateProcess.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procHcsCreateProcess.Addr(), 5, uintptr(computeSystem), uintptr(unsafe.Pointer(processParameters)), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) { - if hr = procHcsOpenProcess.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procHcsOpenProcess.Addr(), 4, uintptr(computeSystem), uintptr(pid), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsCloseProcess(process hcsProcess) (hr error) { - if hr = procHcsCloseProcess.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsCloseProcess.Addr(), 1, uintptr(process), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) { - if hr = procHcsTerminateProcess.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 2, uintptr(process), uintptr(unsafe.Pointer(result)), 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) { - if hr = procHcsGetProcessInfo.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsGetProcessInfo.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) { - if hr = procHcsGetProcessProperties.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsGetProcessProperties.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processProperties)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(settings) - if hr != nil { - return - } - return _hcsModifyProcess(process, _p0, result) -} - -func _hcsModifyProcess(process hcsProcess, settings *uint16, result **uint16) (hr error) { - if hr = procHcsModifyProcess.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsModifyProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(propertyQuery) - if hr != nil { - return - } - return _hcsGetServiceProperties(_p0, properties, result) -} - -func _hcsGetServiceProperties(propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { - if hr = procHcsGetServiceProperties.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsGetServiceProperties.Addr(), 3, uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) { - if hr = procHcsRegisterProcessCallback.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procHcsRegisterProcessCallback.Addr(), 4, uintptr(process), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) { - if hr = procHcsUnregisterProcessCallback.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsUnregisterProcessCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go b/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go index c8d362c66cba4..921c2c8556c2f 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go @@ -36,10 +36,6 @@ func New(err error, title, rest string) error { return &HcsError{title, rest, err} } -func Errorf(err error, title, format string, a ...interface{}) error { - return New(err, title, fmt.Sprintf(format, a...)) -} - func Win32FromError(err error) uint32 { if herr, ok := err.(*HcsError); ok { return Win32FromError(herr.Err) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go index ce636458c0065..262714b4d0f4c 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go @@ -3,6 +3,7 @@ package hns import ( "encoding/json" "net" + "strings" "github.com/sirupsen/logrus" ) @@ -16,14 +17,20 @@ type HNSEndpoint struct { Policies []json.RawMessage `json:",omitempty"` MacAddress string `json:",omitempty"` IPAddress net.IP `json:",omitempty"` + IPv6Address net.IP `json:",omitempty"` DNSSuffix string `json:",omitempty"` DNSServerList string `json:",omitempty"` GatewayAddress string `json:",omitempty"` + GatewayAddressV6 string `json:",omitempty"` EnableInternalDNS bool `json:",omitempty"` DisableICC bool `json:",omitempty"` PrefixLength uint8 `json:",omitempty"` + IPv6PrefixLength uint8 `json:",omitempty"` IsRemoteEndpoint bool `json:",omitempty"` + EnableLowMetric bool `json:",omitempty"` Namespace *Namespace `json:",omitempty"` + EncapOverhead uint16 `json:",omitempty"` + SharedContainers []string `json:",omitempty"` } //SystemType represents the type of the system on which actions are done @@ -51,6 +58,18 @@ type EndpointResquestResponse struct { Error string } +// EndpointStats is the object that has stats for a given endpoint +type EndpointStats struct { + BytesReceived uint64 `json:"BytesReceived"` + BytesSent uint64 `json:"BytesSent"` + DroppedPacketsIncoming uint64 `json:"DroppedPacketsIncoming"` + DroppedPacketsOutgoing uint64 `json:"DroppedPacketsOutgoing"` + EndpointID string `json:"EndpointId"` + InstanceID string `json:"InstanceId"` + PacketsReceived uint64 `json:"PacketsReceived"` + PacketsSent uint64 `json:"PacketsSent"` +} + // HNSEndpointRequest makes a HNS call to modify/query a network endpoint func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) { endpoint := &HNSEndpoint{} @@ -73,11 +92,27 @@ func HNSListEndpointRequest() ([]HNSEndpoint, error) { return endpoint, nil } +// hnsEndpointStatsRequest makes a HNS call to query the stats for a given endpoint ID +func hnsEndpointStatsRequest(id string) (*EndpointStats, error) { + var stats EndpointStats + err := hnsCall("GET", "/endpointstats/"+id, "", &stats) + if err != nil { + return nil, err + } + + return &stats, nil +} + // GetHNSEndpointByID get the Endpoint by ID func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) { return HNSEndpointRequest("GET", endpointID, "") } +// GetHNSEndpointStats get the stats for a n Endpoint by ID +func GetHNSEndpointStats(endpointID string) (*EndpointStats, error) { + return hnsEndpointStatsRequest(endpointID) +} + // GetHNSEndpointByName gets the endpoint filtered by Name func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { hnsResponse, err := HNSListEndpointRequest() @@ -92,6 +127,27 @@ func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { return nil, EndpointNotFoundError{EndpointName: endpointName} } +type endpointAttachInfo struct { + SharedContainers json.RawMessage `json:",omitempty"` +} + +func (endpoint *HNSEndpoint) IsAttached(vID string) (bool, error) { + attachInfo := endpointAttachInfo{} + err := hnsCall("GET", "/endpoints/"+endpoint.Id, "", &attachInfo) + + // Return false allows us to just return the err + if err != nil { + return false, err + } + + if strings.Contains(strings.ToLower(string(attachInfo.SharedContainers)), strings.ToLower(vID)) { + return true, nil + } + + return false, nil + +} + // Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) { operation := "Create" @@ -149,6 +205,27 @@ func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error { return err } +// ApplyProxyPolicy applies a set of Proxy Policies on the Endpoint +func (endpoint *HNSEndpoint) ApplyProxyPolicy(policies ...*ProxyPolicy) error { + operation := "ApplyProxyPolicy" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + for _, policy := range policies { + if policy == nil { + continue + } + jsonString, err := json.Marshal(policy) + if err != nil { + return err + } + endpoint.Policies = append(endpoint.Policies, jsonString) + } + + _, err := endpoint.Update() + return err +} + // ContainerAttach attaches an endpoint to container func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error { operation := "ContainerAttach" diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go index 969d1b263bcbe..2df4a57f56c71 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go @@ -9,23 +9,30 @@ import ( "github.com/sirupsen/logrus" ) -func hnsCall(method, path, request string, returnResponse interface{}) error { +func hnsCallRawResponse(method, path, request string) (*hnsResponse, error) { var responseBuffer *uint16 logrus.Debugf("[%s]=>[%s] Request : %s", method, path, request) err := _hnsCall(method, path, request, &responseBuffer) if err != nil { - return hcserror.New(err, "hnsCall ", "") + return nil, hcserror.New(err, "hnsCall ", "") } response := interop.ConvertAndFreeCoTaskMemString(responseBuffer) hnsresponse := &hnsResponse{} if err = json.Unmarshal([]byte(response), &hnsresponse); err != nil { - return err + return nil, err } + return hnsresponse, nil +} +func hnsCall(method, path, request string, returnResponse interface{}) error { + hnsresponse, err := hnsCallRawResponse(method, path, request) + if err != nil { + return fmt.Errorf("failed during hnsCallRawResponse: %v", err) + } if !hnsresponse.Success { - return fmt.Errorf("HNS failed with error : %s", hnsresponse.Error) + return fmt.Errorf("hns failed with error : %s", hnsresponse.Error) } if len(hnsresponse.Output) == 0 { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go index 7e859de9124b3..f12d3ab0411a6 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go @@ -2,9 +2,9 @@ package hns import ( "encoding/json" - "net" - + "errors" "github.com/sirupsen/logrus" + "net" ) // Subnet is assoicated with a network and represents a list @@ -39,12 +39,6 @@ type HNSNetwork struct { AutomaticDNS bool `json:",omitempty"` } -type hnsNetworkResponse struct { - Success bool - Error string - Output HNSNetwork -} - type hnsResponse struct { Success bool Error string @@ -98,6 +92,12 @@ func (network *HNSNetwork) Create() (*HNSNetwork, error) { title := "hcsshim::HNSNetwork::" + operation logrus.Debugf(title+" id=%s", network.Id) + for _, subnet := range network.Subnets { + if (subnet.AddressPrefix != "") && (subnet.GatewayAddress == "") { + return nil, errors.New("network create error, subnet has address prefix but no gateway specified") + } + } + jsonString, err := json.Marshal(network) if err != nil { return nil, err diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go index 2318a4fce2b67..6765aaead5eee 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go @@ -17,6 +17,7 @@ const ( OutboundNat PolicyType = "OutBoundNAT" ExternalLoadBalancer PolicyType = "ELB" Route PolicyType = "ROUTE" + Proxy PolicyType = "PROXY" ) type NatPolicy struct { @@ -55,8 +56,18 @@ type PaPolicy struct { type OutboundNatPolicy struct { Policy - VIP string `json:"VIP,omitempty"` - Exceptions []string `json:"ExceptionList,omitempty"` + VIP string `json:"VIP,omitempty"` + Exceptions []string `json:"ExceptionList,omitempty"` + Destinations []string `json:",omitempty"` +} + +type ProxyPolicy struct { + Type PolicyType `json:"Type"` + IP string `json:",omitempty"` + Port string `json:",omitempty"` + ExceptionList []string `json:",omitempty"` + Destination string `json:",omitempty"` + OutboundNat bool `json:",omitempty"` } type ActionType string diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go index ff7369e6ff9d3..31322a68167c5 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go @@ -20,6 +20,7 @@ type ELBPolicy struct { SourceVIP string `json:"SourceVIP,omitempty"` VIPs []string `json:"VIPs,omitempty"` ILB bool `json:"ILB,omitempty"` + DSR bool `json:"IsDSR,omitempty"` } // LBPolicy is a structure defining schema for LoadBalancing based Policy diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go index 45e2281b07810..d3b04eefe0cd6 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go @@ -27,9 +27,10 @@ type namespaceResourceRequest struct { } type Namespace struct { - ID string - IsDefault bool `json:",omitempty"` - ResourceList []NamespaceResource `json:",omitempty"` + ID string + IsDefault bool `json:",omitempty"` + ResourceList []NamespaceResource `json:",omitempty"` + CompartmentId uint32 `json:",omitempty"` } func issueNamespaceRequest(id *string, method, subpath string, request interface{}) (*Namespace, error) { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go index 863e3429c7804..204633a4887d7 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated mksyscall_windows.exe DO NOT EDIT package hns @@ -6,7 +6,6 @@ import ( "syscall" "unsafe" - "github.com/Microsoft/hcsshim/internal/interop" "golang.org/x/sys/windows" ) @@ -68,7 +67,10 @@ func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16) } r0, _, _ := syscall.Syscall6(procHNSCall.Addr(), 4, uintptr(unsafe.Pointer(method)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(object)), uintptr(unsafe.Pointer(response)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go b/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go index f10c88d08cb0d..922f7c679e063 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go @@ -5,9 +5,9 @@ import ( "unsafe" ) -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go interop.go +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go interop.go -//sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree +//sys coTaskMemFree(buffer unsafe.Pointer) = api_ms_win_core_com_l1_1_0.CoTaskMemFree func ConvertAndFreeCoTaskMemString(buffer *uint16) string { str := syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(buffer))[:]) @@ -15,10 +15,6 @@ func ConvertAndFreeCoTaskMemString(buffer *uint16) string { return str } -func ConvertAndFreeCoTaskMemBytes(buffer *uint16) []byte { - return []byte(ConvertAndFreeCoTaskMemString(buffer)) -} - func Win32FromHresult(hr uintptr) syscall.Errno { if hr&0x1fff0000 == 0x00070000 { return syscall.Errno(hr & 0xffff) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go index 2f5bf8f555bb4..12b0c71c5ae0e 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go @@ -1,4 +1,4 @@ -// Code generated by 'go generate'; DO NOT EDIT. +// Code generated mksyscall_windows.exe DO NOT EDIT package interop @@ -37,9 +37,9 @@ func errnoErr(e syscall.Errno) error { } var ( - modole32 = windows.NewLazySystemDLL("ole32.dll") + modapi_ms_win_core_com_l1_1_0 = windows.NewLazySystemDLL("api-ms-win-core-com-l1-1-0.dll") - procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") + procCoTaskMemFree = modapi_ms_win_core_com_l1_1_0.NewProc("CoTaskMemFree") ) func coTaskMemFree(buffer unsafe.Pointer) { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/log/g.go b/vendor/github.com/Microsoft/hcsshim/internal/log/g.go new file mode 100644 index 0000000000000..ba6b1a4a53a7a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/log/g.go @@ -0,0 +1,23 @@ +package log + +import ( + "context" + + "github.com/sirupsen/logrus" + "go.opencensus.io/trace" +) + +// G returns a `logrus.Entry` with the `TraceID, SpanID` from `ctx` if `ctx` +// contains an OpenCensus `trace.Span`. +func G(ctx context.Context) *logrus.Entry { + span := trace.FromContext(ctx) + if span != nil { + sctx := span.SpanContext() + return logrus.WithFields(logrus.Fields{ + "traceID": sctx.TraceID.String(), + "spanID": sctx.SpanID.String(), + // "parentSpanID": TODO: JTERRY75 - Try to convince OC to export this? + }) + } + return logrus.NewEntry(logrus.StandardLogger()) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go b/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go new file mode 100644 index 0000000000000..cf2c166d9b832 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go @@ -0,0 +1,32 @@ +package logfields + +const ( + // Identifiers + + ContainerID = "cid" + UVMID = "uvm-id" + ProcessID = "pid" + + // Common Misc + + // Timeout represents an operation timeout. + Timeout = "timeout" + JSON = "json" + + // Keys/values + + Field = "field" + OCIAnnotation = "oci-annotation" + Value = "value" + + // Golang type's + + ExpectedType = "expected-type" + Bool = "bool" + Uint32 = "uint32" + Uint64 = "uint64" + + // runhcs + + VMShimOperation = "vmshim-op" +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/oc/exporter.go b/vendor/github.com/Microsoft/hcsshim/internal/oc/exporter.go new file mode 100644 index 0000000000000..f428bdaf720e8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/oc/exporter.go @@ -0,0 +1,43 @@ +package oc + +import ( + "github.com/sirupsen/logrus" + "go.opencensus.io/trace" +) + +var _ = (trace.Exporter)(&LogrusExporter{}) + +// LogrusExporter is an OpenCensus `trace.Exporter` that exports +// `trace.SpanData` to logrus output. +type LogrusExporter struct { +} + +// ExportSpan exports `s` based on the the following rules: +// +// 1. All output will contain `s.Attributes`, `s.TraceID`, `s.SpanID`, +// `s.ParentSpanID` for correlation +// +// 2. Any calls to .Annotate will not be supported. +// +// 3. The span itself will be written at `logrus.InfoLevel` unless +// `s.Status.Code != 0` in which case it will be written at `logrus.ErrorLevel` +// providing `s.Status.Message` as the error value. +func (le *LogrusExporter) ExportSpan(s *trace.SpanData) { + // Combine all span annotations with traceID, spanID, parentSpanID + baseEntry := logrus.WithFields(logrus.Fields(s.Attributes)) + baseEntry.Data["traceID"] = s.TraceID.String() + baseEntry.Data["spanID"] = s.SpanID.String() + baseEntry.Data["parentSpanID"] = s.ParentSpanID.String() + baseEntry.Data["startTime"] = s.StartTime + baseEntry.Data["endTime"] = s.EndTime + baseEntry.Data["duration"] = s.EndTime.Sub(s.StartTime).String() + baseEntry.Data["name"] = s.Name + baseEntry.Time = s.StartTime + + level := logrus.InfoLevel + if s.Status.Code != 0 { + level = logrus.ErrorLevel + baseEntry.Data[logrus.ErrorKey] = s.Status.Message + } + baseEntry.Log(level, "Span") +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/oc/span.go b/vendor/github.com/Microsoft/hcsshim/internal/oc/span.go new file mode 100644 index 0000000000000..fee4765cbc49d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/oc/span.go @@ -0,0 +1,17 @@ +package oc + +import ( + "go.opencensus.io/trace" +) + +// SetSpanStatus sets `span.SetStatus` to the proper status depending on `err`. If +// `err` is `nil` assumes `trace.StatusCodeOk`. +func SetSpanStatus(span *trace.Span, err error) { + status := trace.Status{} + if err != nil { + // TODO: JTERRY75 - Handle errors in a non-generic way + status.Code = trace.StatusCodeUnknown + status.Message = err.Error() + } + span.SetStatus(status) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go b/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go index 0c0b1159f284d..66b8d7e03537f 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go @@ -11,72 +11,11 @@ import ( "unsafe" "github.com/Microsoft/hcsshim/internal/longpath" + "github.com/Microsoft/hcsshim/internal/winapi" winio "github.com/Microsoft/go-winio" ) -//go:generate go run $GOROOT\src\syscall\mksyscall_windows.go -output zsyscall_windows.go safeopen.go - -//sys ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) = ntdll.NtCreateFile -//sys ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) = ntdll.NtSetInformationFile -//sys rtlNtStatusToDosError(status uint32) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb -//sys localAlloc(flags uint32, size int) (ptr uintptr) = kernel32.LocalAlloc -//sys localFree(ptr uintptr) = kernel32.LocalFree - -type ioStatusBlock struct { - Status, Information uintptr -} - -type objectAttributes struct { - Length uintptr - RootDirectory uintptr - ObjectName uintptr - Attributes uintptr - SecurityDescriptor uintptr - SecurityQoS uintptr -} - -type unicodeString struct { - Length uint16 - MaximumLength uint16 - Buffer uintptr -} - -type fileLinkInformation struct { - ReplaceIfExists bool - RootDirectory uintptr - FileNameLength uint32 - FileName [1]uint16 -} - -type fileDispositionInformationEx struct { - Flags uintptr -} - -const ( - _FileLinkInformation = 11 - _FileDispositionInformationEx = 64 - - FILE_READ_ATTRIBUTES = 0x0080 - FILE_WRITE_ATTRIBUTES = 0x0100 - DELETE = 0x10000 - - FILE_OPEN = 1 - FILE_CREATE = 2 - - FILE_DIRECTORY_FILE = 0x00000001 - FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020 - FILE_DELETE_ON_CLOSE = 0x00001000 - FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000 - FILE_OPEN_REPARSE_POINT = 0x00200000 - - FILE_DISPOSITION_DELETE = 0x00000001 - - _OBJ_DONT_REPARSE = 0x1000 - - _STATUS_REPARSE_POINT_ENCOUNTERED = 0xC000050B -) - func OpenRoot(path string) (*os.File, error) { longpath, err := longpath.LongAbs(path) if err != nil { @@ -85,16 +24,24 @@ func OpenRoot(path string) (*os.File, error) { return winio.OpenForBackup(longpath, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, syscall.OPEN_EXISTING) } -func ntRelativePath(path string) ([]uint16, error) { +func cleanGoStringRelativePath(path string) (string, error) { path = filepath.Clean(path) - if strings.Contains(":", path) { + if strings.Contains(path, ":") { // Since alternate data streams must follow the file they // are attached to, finding one here (out of order) is invalid. - return nil, errors.New("path contains invalid character `:`") + return "", errors.New("path contains invalid character `:`") } fspath := filepath.FromSlash(path) if len(fspath) > 0 && fspath[0] == '\\' { - return nil, errors.New("expected relative path") + return "", errors.New("expected relative path") + } + return fspath, nil +} + +func ntRelativePath(path string) ([]uint16, error) { + fspath, err := cleanGoStringRelativePath(path) + if err != nil { + return nil, err } path16 := utf16.Encode(([]rune)(fspath)) @@ -110,11 +57,11 @@ func ntRelativePath(path string) ([]uint16, error) { func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) { var ( h uintptr - iosb ioStatusBlock - oa objectAttributes + iosb winapi.IOStatusBlock + oa winapi.ObjectAttributes ) - path16, err := ntRelativePath(path) + cleanRelativePath, err := cleanGoStringRelativePath(path) if err != nil { return nil, err } @@ -123,20 +70,16 @@ func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFl return nil, errors.New("missing root directory") } - upathBuffer := localAlloc(0, int(unsafe.Sizeof(unicodeString{}))+len(path16)*2) - defer localFree(upathBuffer) - - upath := (*unicodeString)(unsafe.Pointer(upathBuffer)) - upath.Length = uint16(len(path16) * 2) - upath.MaximumLength = upath.Length - upath.Buffer = upathBuffer + unsafe.Sizeof(*upath) - copy((*[32768]uint16)(unsafe.Pointer(upath.Buffer))[:], path16) + pathUnicode, err := winapi.NewUnicodeString(cleanRelativePath) + if err != nil { + return nil, err + } oa.Length = unsafe.Sizeof(oa) - oa.ObjectName = upathBuffer + oa.ObjectName = pathUnicode oa.RootDirectory = uintptr(root.Fd()) - oa.Attributes = _OBJ_DONT_REPARSE - status := ntCreateFile( + oa.Attributes = winapi.OBJ_DONT_REPARSE + status := winapi.NtCreateFile( &h, accessMask|syscall.SYNCHRONIZE, &oa, @@ -145,12 +88,12 @@ func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFl 0, shareFlags, createDisposition, - FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT|flags, + winapi.FILE_OPEN_FOR_BACKUP_INTENT|winapi.FILE_SYNCHRONOUS_IO_NONALERT|flags, nil, 0, ) if status != 0 { - return nil, rtlNtStatusToDosError(status) + return nil, winapi.RtlNtStatusToDosError(status) } fullPath, err := longpath.LongAbs(filepath.Join(root.Name(), path)) @@ -182,7 +125,7 @@ func LinkRelative(oldname string, oldroot *os.File, newname string, newroot *os. oldroot, syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - FILE_OPEN, + winapi.FILE_OPEN, 0, ) if err != nil { @@ -199,8 +142,8 @@ func LinkRelative(oldname string, oldroot *os.File, newname string, newroot *os. newroot, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - FILE_OPEN, - FILE_DIRECTORY_FILE) + winapi.FILE_OPEN, + winapi.FILE_DIRECTORY_FILE) if err != nil { return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: err} } @@ -211,7 +154,7 @@ func LinkRelative(oldname string, oldroot *os.File, newname string, newroot *os. return err } if (fi.FileAttributes & syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 { - return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: rtlNtStatusToDosError(_STATUS_REPARSE_POINT_ENCOUNTERED)} + return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: winapi.RtlNtStatusToDosError(winapi.STATUS_REPARSE_POINT_ENCOUNTERED)} } } else { @@ -227,24 +170,25 @@ func LinkRelative(oldname string, oldroot *os.File, newname string, newroot *os. return err } - size := int(unsafe.Offsetof(fileLinkInformation{}.FileName)) + len(newbase16)*2 - linkinfoBuffer := localAlloc(0, size) - defer localFree(linkinfoBuffer) - linkinfo := (*fileLinkInformation)(unsafe.Pointer(linkinfoBuffer)) + size := int(unsafe.Offsetof(winapi.FileLinkInformation{}.FileName)) + len(newbase16)*2 + linkinfoBuffer := winapi.LocalAlloc(0, size) + defer winapi.LocalFree(linkinfoBuffer) + + linkinfo := (*winapi.FileLinkInformation)(unsafe.Pointer(linkinfoBuffer)) linkinfo.RootDirectory = parent.Fd() linkinfo.FileNameLength = uint32(len(newbase16) * 2) - copy((*[32768]uint16)(unsafe.Pointer(&linkinfo.FileName[0]))[:], newbase16) + copy(winapi.Uint16BufferToSlice(&linkinfo.FileName[0], len(newbase16)), newbase16) - var iosb ioStatusBlock - status := ntSetInformationFile( + var iosb winapi.IOStatusBlock + status := winapi.NtSetInformationFile( oldf.Fd(), &iosb, linkinfoBuffer, uint32(size), - _FileLinkInformation, + winapi.FileLinkInformationClass, ) if status != 0 { - return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(parent.Name(), newbase), Err: rtlNtStatusToDosError(status)} + return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(parent.Name(), newbase), Err: winapi.RtlNtStatusToDosError(status)} } return nil @@ -252,17 +196,17 @@ func LinkRelative(oldname string, oldroot *os.File, newname string, newroot *os. // deleteOnClose marks a file to be deleted when the handle is closed. func deleteOnClose(f *os.File) error { - disposition := fileDispositionInformationEx{Flags: FILE_DISPOSITION_DELETE} - var iosb ioStatusBlock - status := ntSetInformationFile( + disposition := winapi.FileDispositionInformationEx{Flags: winapi.FILE_DISPOSITION_DELETE} + var iosb winapi.IOStatusBlock + status := winapi.NtSetInformationFile( f.Fd(), &iosb, uintptr(unsafe.Pointer(&disposition)), uint32(unsafe.Sizeof(disposition)), - _FileDispositionInformationEx, + winapi.FileDispositionInformationExClass, ) if status != 0 { - return rtlNtStatusToDosError(status) + return winapi.RtlNtStatusToDosError(status) } return nil } @@ -291,16 +235,16 @@ func RemoveRelative(path string, root *os.File) error { f, err := openRelativeInternal( path, root, - FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES|DELETE, + winapi.FILE_READ_ATTRIBUTES|winapi.FILE_WRITE_ATTRIBUTES|winapi.DELETE, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - FILE_OPEN, - FILE_OPEN_REPARSE_POINT) + winapi.FILE_OPEN, + winapi.FILE_OPEN_REPARSE_POINT) if err == nil { defer f.Close() err = deleteOnClose(f) if err == syscall.ERROR_ACCESS_DENIED { // Maybe the file is marked readonly. Clear the bit and retry. - clearReadOnly(f) + _ = clearReadOnly(f) err = deleteOnClose(f) } } @@ -385,8 +329,8 @@ func MkdirRelative(path string, root *os.File) error { root, 0, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - FILE_CREATE, - FILE_DIRECTORY_FILE) + winapi.FILE_CREATE, + winapi.FILE_DIRECTORY_FILE) if err == nil { f.Close() } else { @@ -401,10 +345,10 @@ func LstatRelative(path string, root *os.File) (os.FileInfo, error) { f, err := openRelativeInternal( path, root, - FILE_READ_ATTRIBUTES, + winapi.FILE_READ_ATTRIBUTES, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - FILE_OPEN, - FILE_OPEN_REPARSE_POINT) + winapi.FILE_OPEN, + winapi.FILE_OPEN_REPARSE_POINT) if err != nil { return nil, &os.PathError{Op: "stat", Path: filepath.Join(root.Name(), path), Err: err} } @@ -421,7 +365,7 @@ func EnsureNotReparsePointRelative(path string, root *os.File) error { root, 0, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - FILE_OPEN, + winapi.FILE_OPEN, 0) if err != nil { return err diff --git a/vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go deleted file mode 100644 index 709b9d3475d10..0000000000000 --- a/vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go +++ /dev/null @@ -1,79 +0,0 @@ -// Code generated by 'go generate'; DO NOT EDIT. - -package safefile - -import ( - "syscall" - "unsafe" - - "golang.org/x/sys/windows" -) - -var _ unsafe.Pointer - -// Do the interface allocations only once for common -// Errno values. -const ( - errnoERROR_IO_PENDING = 997 -) - -var ( - errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) -) - -// errnoErr returns common boxed Errno values, to prevent -// allocations at runtime. -func errnoErr(e syscall.Errno) error { - switch e { - case 0: - return nil - case errnoERROR_IO_PENDING: - return errERROR_IO_PENDING - } - // TODO: add more here, after collecting data on the common - // error values see on Windows. (perhaps when running - // all.bat?) - return e -} - -var ( - modntdll = windows.NewLazySystemDLL("ntdll.dll") - modkernel32 = windows.NewLazySystemDLL("kernel32.dll") - - procNtCreateFile = modntdll.NewProc("NtCreateFile") - procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile") - procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") - procLocalAlloc = modkernel32.NewProc("LocalAlloc") - procLocalFree = modkernel32.NewProc("LocalFree") -) - -func ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) { - r0, _, _ := syscall.Syscall12(procNtCreateFile.Addr(), 11, uintptr(unsafe.Pointer(handle)), uintptr(accessMask), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(allocationSize)), uintptr(fileAttributes), uintptr(shareAccess), uintptr(createDisposition), uintptr(createOptions), uintptr(unsafe.Pointer(eaBuffer)), uintptr(eaLength), 0) - status = uint32(r0) - return -} - -func ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) { - r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(information), uintptr(length), uintptr(class), 0) - status = uint32(r0) - return -} - -func rtlNtStatusToDosError(status uint32) (winerr error) { - r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) - if r0 != 0 { - winerr = syscall.Errno(r0) - } - return -} - -func localAlloc(flags uint32, size int) (ptr uintptr) { - r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(size), 0) - ptr = uintptr(r0) - return -} - -func localFree(ptr uintptr) { - syscall.Syscall(procLocalFree.Addr(), 1, uintptr(ptr), 0, 0) - return -} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go b/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go index ff3b6572e659b..eaf39fa513274 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go @@ -29,6 +29,9 @@ var ( // SystemResume is the timeout for resuming a compute system SystemResume time.Duration = defaultTimeout + // SystemSave is the timeout for saving a compute system + SystemSave time.Duration = defaultTimeout + // SyscallWatcher is the timeout before warning of a potential stuck platform syscall. SyscallWatcher time.Duration = defaultTimeout @@ -51,6 +54,7 @@ func init() { SystemStart = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMSTART", SystemStart) SystemPause = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMPAUSE", SystemPause) SystemResume = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMRESUME", SystemResume) + SystemSave = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMSAVE", SystemSave) SyscallWatcher = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSCALLWATCHER", SyscallWatcher) Tar2VHD = durationFromEnvironment("HCSSHIM_TIMEOUT_TAR2VHD", Tar2VHD) ExternalCommandToStart = durationFromEnvironment("HCSSHIM_TIMEOUT_EXTERNALCOMMANDSTART", ExternalCommandToStart) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/vmcompute.go b/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/vmcompute.go new file mode 100644 index 0000000000000..e7f114b67aaa0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/vmcompute.go @@ -0,0 +1,610 @@ +package vmcompute + +import ( + gcontext "context" + "syscall" + "time" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/log" + "github.com/Microsoft/hcsshim/internal/logfields" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/internal/timeout" + "go.opencensus.io/trace" +) + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go vmcompute.go + +//sys hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) = vmcompute.HcsEnumerateComputeSystems? +//sys hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *HcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem? +//sys hcsOpenComputeSystem(id string, computeSystem *HcsSystem, result **uint16) (hr error) = vmcompute.HcsOpenComputeSystem? +//sys hcsCloseComputeSystem(computeSystem HcsSystem) (hr error) = vmcompute.HcsCloseComputeSystem? +//sys hcsStartComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem? +//sys hcsShutdownComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem? +//sys hcsTerminateComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem? +//sys hcsPauseComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem? +//sys hcsResumeComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem? +//sys hcsGetComputeSystemProperties(computeSystem HcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetComputeSystemProperties? +//sys hcsModifyComputeSystem(computeSystem HcsSystem, configuration string, result **uint16) (hr error) = vmcompute.HcsModifyComputeSystem? +//sys hcsModifyServiceSettings(settings string, result **uint16) (hr error) = vmcompute.HcsModifyServiceSettings? +//sys hcsRegisterComputeSystemCallback(computeSystem HcsSystem, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) = vmcompute.HcsRegisterComputeSystemCallback? +//sys hcsUnregisterComputeSystemCallback(callbackHandle HcsCallback) (hr error) = vmcompute.HcsUnregisterComputeSystemCallback? +//sys hcsSaveComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsSaveComputeSystem? + +//sys hcsCreateProcess(computeSystem HcsSystem, processParameters string, processInformation *HcsProcessInformation, process *HcsProcess, result **uint16) (hr error) = vmcompute.HcsCreateProcess? +//sys hcsOpenProcess(computeSystem HcsSystem, pid uint32, process *HcsProcess, result **uint16) (hr error) = vmcompute.HcsOpenProcess? +//sys hcsCloseProcess(process HcsProcess) (hr error) = vmcompute.HcsCloseProcess? +//sys hcsTerminateProcess(process HcsProcess, result **uint16) (hr error) = vmcompute.HcsTerminateProcess? +//sys hcsSignalProcess(process HcsProcess, options string, result **uint16) (hr error) = vmcompute.HcsSignalProcess? +//sys hcsGetProcessInfo(process HcsProcess, processInformation *HcsProcessInformation, result **uint16) (hr error) = vmcompute.HcsGetProcessInfo? +//sys hcsGetProcessProperties(process HcsProcess, processProperties **uint16, result **uint16) (hr error) = vmcompute.HcsGetProcessProperties? +//sys hcsModifyProcess(process HcsProcess, settings string, result **uint16) (hr error) = vmcompute.HcsModifyProcess? +//sys hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetServiceProperties? +//sys hcsRegisterProcessCallback(process HcsProcess, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback? +//sys hcsUnregisterProcessCallback(callbackHandle HcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback? + +// errVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously +const errVmcomputeOperationPending = syscall.Errno(0xC0370103) + +// HcsSystem is the handle associated with a created compute system. +type HcsSystem syscall.Handle + +// HcsProcess is the handle associated with a created process in a compute +// system. +type HcsProcess syscall.Handle + +// HcsCallback is the handle associated with the function to call when events +// occur. +type HcsCallback syscall.Handle + +// HcsProcessInformation is the structure used when creating or getting process +// info. +type HcsProcessInformation struct { + // ProcessId is the pid of the created process. + ProcessId uint32 + reserved uint32 //nolint:structcheck + // StdInput is the handle associated with the stdin of the process. + StdInput syscall.Handle + // StdOutput is the handle associated with the stdout of the process. + StdOutput syscall.Handle + // StdError is the handle associated with the stderr of the process. + StdError syscall.Handle +} + +func execute(ctx gcontext.Context, timeout time.Duration, f func() error) error { + if timeout > 0 { + var cancel gcontext.CancelFunc + ctx, cancel = gcontext.WithTimeout(ctx, timeout) + defer cancel() + } + + done := make(chan error, 1) + go func() { + done <- f() + }() + select { + case <-ctx.Done(): + if ctx.Err() == gcontext.DeadlineExceeded { + log.G(ctx).WithField(logfields.Timeout, timeout). + Warning("Syscall did not complete within operation timeout. This may indicate a platform issue. If it appears to be making no forward progress, obtain the stacks and see if there is a syscall stuck in the platform API for a significant length of time.") + } + return ctx.Err() + case err := <-done: + return err + } +} + +func HcsEnumerateComputeSystems(ctx gcontext.Context, query string) (computeSystems, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsEnumerateComputeSystems") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("query", query)) + + return computeSystems, result, execute(ctx, timeout.SyscallWatcher, func() error { + var ( + computeSystemsp *uint16 + resultp *uint16 + ) + err := hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp) + if computeSystemsp != nil { + computeSystems = interop.ConvertAndFreeCoTaskMemString(computeSystemsp) + } + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsCreateComputeSystem(ctx gcontext.Context, id string, configuration string, identity syscall.Handle) (computeSystem HcsSystem, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsCreateComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes( + trace.StringAttribute("id", id), + trace.StringAttribute("configuration", configuration)) + + return computeSystem, result, execute(ctx, timeout.SystemCreate, func() error { + var resultp *uint16 + err := hcsCreateComputeSystem(id, configuration, identity, &computeSystem, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsOpenComputeSystem(ctx gcontext.Context, id string) (computeSystem HcsSystem, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsOpenComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + + return computeSystem, result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsOpenComputeSystem(id, &computeSystem, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsCloseComputeSystem(ctx gcontext.Context, computeSystem HcsSystem) (hr error) { + ctx, span := trace.StartSpan(ctx, "HcsCloseComputeSystem") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return execute(ctx, timeout.SyscallWatcher, func() error { + return hcsCloseComputeSystem(computeSystem) + }) +} + +func HcsStartComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsStartComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SystemStart, func() error { + var resultp *uint16 + err := hcsStartComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsShutdownComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsShutdownComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsShutdownComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsTerminateComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsTerminateComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsTerminateComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsPauseComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsPauseComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SystemPause, func() error { + var resultp *uint16 + err := hcsPauseComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsResumeComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsResumeComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SystemResume, func() error { + var resultp *uint16 + err := hcsResumeComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsGetComputeSystemProperties(ctx gcontext.Context, computeSystem HcsSystem, propertyQuery string) (properties, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsGetComputeSystemProperties") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("propertyQuery", propertyQuery)) + + return properties, result, execute(ctx, timeout.SyscallWatcher, func() error { + var ( + propertiesp *uint16 + resultp *uint16 + ) + err := hcsGetComputeSystemProperties(computeSystem, propertyQuery, &propertiesp, &resultp) + if propertiesp != nil { + properties = interop.ConvertAndFreeCoTaskMemString(propertiesp) + } + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsModifyComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, configuration string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsModifyComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("configuration", configuration)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsModifyComputeSystem(computeSystem, configuration, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsModifyServiceSettings(ctx gcontext.Context, settings string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsModifyServiceSettings") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("settings", settings)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsModifyServiceSettings(settings, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsRegisterComputeSystemCallback(ctx gcontext.Context, computeSystem HcsSystem, callback uintptr, context uintptr) (callbackHandle HcsCallback, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsRegisterComputeSystemCallback") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return callbackHandle, execute(ctx, timeout.SyscallWatcher, func() error { + return hcsRegisterComputeSystemCallback(computeSystem, callback, context, &callbackHandle) + }) +} + +func HcsUnregisterComputeSystemCallback(ctx gcontext.Context, callbackHandle HcsCallback) (hr error) { + ctx, span := trace.StartSpan(ctx, "HcsUnregisterComputeSystemCallback") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return execute(ctx, timeout.SyscallWatcher, func() error { + return hcsUnregisterComputeSystemCallback(callbackHandle) + }) +} + +func HcsCreateProcess(ctx gcontext.Context, computeSystem HcsSystem, processParameters string) (processInformation HcsProcessInformation, process HcsProcess, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsCreateProcess") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("processParameters", processParameters)) + + return processInformation, process, result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsCreateProcess(computeSystem, processParameters, &processInformation, &process, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsOpenProcess(ctx gcontext.Context, computeSystem HcsSystem, pid uint32) (process HcsProcess, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsOpenProcess") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.Int64Attribute("pid", int64(pid))) + + return process, result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsOpenProcess(computeSystem, pid, &process, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsCloseProcess(ctx gcontext.Context, process HcsProcess) (hr error) { + ctx, span := trace.StartSpan(ctx, "HcsCloseProcess") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return execute(ctx, timeout.SyscallWatcher, func() error { + return hcsCloseProcess(process) + }) +} + +func HcsTerminateProcess(ctx gcontext.Context, process HcsProcess) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsTerminateProcess") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsTerminateProcess(process, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsSignalProcess(ctx gcontext.Context, process HcsProcess, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsSignalProcess") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("options", options)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsSignalProcess(process, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsGetProcessInfo(ctx gcontext.Context, process HcsProcess) (processInformation HcsProcessInformation, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsGetProcessInfo") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + + return processInformation, result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsGetProcessInfo(process, &processInformation, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsGetProcessProperties(ctx gcontext.Context, process HcsProcess) (processProperties, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsGetProcessProperties") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + + return processProperties, result, execute(ctx, timeout.SyscallWatcher, func() error { + var ( + processPropertiesp *uint16 + resultp *uint16 + ) + err := hcsGetProcessProperties(process, &processPropertiesp, &resultp) + if processPropertiesp != nil { + processProperties = interop.ConvertAndFreeCoTaskMemString(processPropertiesp) + } + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsModifyProcess(ctx gcontext.Context, process HcsProcess, settings string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsModifyProcess") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("settings", settings)) + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsModifyProcess(process, settings, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsGetServiceProperties(ctx gcontext.Context, propertyQuery string) (properties, result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsGetServiceProperties") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + oc.SetSpanStatus(span, hr) + }() + span.AddAttributes(trace.StringAttribute("propertyQuery", propertyQuery)) + + return properties, result, execute(ctx, timeout.SyscallWatcher, func() error { + var ( + propertiesp *uint16 + resultp *uint16 + ) + err := hcsGetServiceProperties(propertyQuery, &propertiesp, &resultp) + if propertiesp != nil { + properties = interop.ConvertAndFreeCoTaskMemString(propertiesp) + } + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} + +func HcsRegisterProcessCallback(ctx gcontext.Context, process HcsProcess, callback uintptr, context uintptr) (callbackHandle HcsCallback, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsRegisterProcessCallback") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return callbackHandle, execute(ctx, timeout.SyscallWatcher, func() error { + return hcsRegisterProcessCallback(process, callback, context, &callbackHandle) + }) +} + +func HcsUnregisterProcessCallback(ctx gcontext.Context, callbackHandle HcsCallback) (hr error) { + ctx, span := trace.StartSpan(ctx, "HcsUnregisterProcessCallback") + defer span.End() + defer func() { oc.SetSpanStatus(span, hr) }() + + return execute(ctx, timeout.SyscallWatcher, func() error { + return hcsUnregisterProcessCallback(callbackHandle) + }) +} + +func HcsSaveComputeSystem(ctx gcontext.Context, computeSystem HcsSystem, options string) (result string, hr error) { + ctx, span := trace.StartSpan(ctx, "HcsSaveComputeSystem") + defer span.End() + defer func() { + if result != "" { + span.AddAttributes(trace.StringAttribute("result", result)) + } + if hr != errVmcomputeOperationPending { + oc.SetSpanStatus(span, hr) + } + }() + + return result, execute(ctx, timeout.SyscallWatcher, func() error { + var resultp *uint16 + err := hcsSaveComputeSystem(computeSystem, options, &resultp) + if resultp != nil { + result = interop.ConvertAndFreeCoTaskMemString(resultp) + } + return err + }) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/zsyscall_windows.go new file mode 100644 index 0000000000000..cae55058deb81 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/vmcompute/zsyscall_windows.go @@ -0,0 +1,581 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package vmcompute + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + + procHcsEnumerateComputeSystems = modvmcompute.NewProc("HcsEnumerateComputeSystems") + procHcsCreateComputeSystem = modvmcompute.NewProc("HcsCreateComputeSystem") + procHcsOpenComputeSystem = modvmcompute.NewProc("HcsOpenComputeSystem") + procHcsCloseComputeSystem = modvmcompute.NewProc("HcsCloseComputeSystem") + procHcsStartComputeSystem = modvmcompute.NewProc("HcsStartComputeSystem") + procHcsShutdownComputeSystem = modvmcompute.NewProc("HcsShutdownComputeSystem") + procHcsTerminateComputeSystem = modvmcompute.NewProc("HcsTerminateComputeSystem") + procHcsPauseComputeSystem = modvmcompute.NewProc("HcsPauseComputeSystem") + procHcsResumeComputeSystem = modvmcompute.NewProc("HcsResumeComputeSystem") + procHcsGetComputeSystemProperties = modvmcompute.NewProc("HcsGetComputeSystemProperties") + procHcsModifyComputeSystem = modvmcompute.NewProc("HcsModifyComputeSystem") + procHcsModifyServiceSettings = modvmcompute.NewProc("HcsModifyServiceSettings") + procHcsRegisterComputeSystemCallback = modvmcompute.NewProc("HcsRegisterComputeSystemCallback") + procHcsUnregisterComputeSystemCallback = modvmcompute.NewProc("HcsUnregisterComputeSystemCallback") + procHcsSaveComputeSystem = modvmcompute.NewProc("HcsSaveComputeSystem") + procHcsCreateProcess = modvmcompute.NewProc("HcsCreateProcess") + procHcsOpenProcess = modvmcompute.NewProc("HcsOpenProcess") + procHcsCloseProcess = modvmcompute.NewProc("HcsCloseProcess") + procHcsTerminateProcess = modvmcompute.NewProc("HcsTerminateProcess") + procHcsSignalProcess = modvmcompute.NewProc("HcsSignalProcess") + procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo") + procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties") + procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess") + procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties") + procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback") + procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback") +) + +func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(query) + if hr != nil { + return + } + return _hcsEnumerateComputeSystems(_p0, computeSystems, result) +} + +func _hcsEnumerateComputeSystems(query *uint16, computeSystems **uint16, result **uint16) (hr error) { + if hr = procHcsEnumerateComputeSystems.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsEnumerateComputeSystems.Addr(), 3, uintptr(unsafe.Pointer(query)), uintptr(unsafe.Pointer(computeSystems)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *HcsSystem, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(configuration) + if hr != nil { + return + } + return _hcsCreateComputeSystem(_p0, _p1, identity, computeSystem, result) +} + +func _hcsCreateComputeSystem(id *uint16, configuration *uint16, identity syscall.Handle, computeSystem *HcsSystem, result **uint16) (hr error) { + if hr = procHcsCreateComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsCreateComputeSystem.Addr(), 5, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), uintptr(identity), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsOpenComputeSystem(id string, computeSystem *HcsSystem, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _hcsOpenComputeSystem(_p0, computeSystem, result) +} + +func _hcsOpenComputeSystem(id *uint16, computeSystem *HcsSystem, result **uint16) (hr error) { + if hr = procHcsOpenComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsOpenComputeSystem.Addr(), 3, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCloseComputeSystem(computeSystem HcsSystem) (hr error) { + if hr = procHcsCloseComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsCloseComputeSystem.Addr(), 1, uintptr(computeSystem), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsStartComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsStartComputeSystem(computeSystem, _p0, result) +} + +func _hcsStartComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsStartComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsStartComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsShutdownComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsShutdownComputeSystem(computeSystem, _p0, result) +} + +func _hcsShutdownComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsShutdownComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsShutdownComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsTerminateComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsTerminateComputeSystem(computeSystem, _p0, result) +} + +func _hcsTerminateComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsTerminateComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsPauseComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsPauseComputeSystem(computeSystem, _p0, result) +} + +func _hcsPauseComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsPauseComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsPauseComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsResumeComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsResumeComputeSystem(computeSystem, _p0, result) +} + +func _hcsResumeComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsResumeComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsResumeComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetComputeSystemProperties(computeSystem HcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(propertyQuery) + if hr != nil { + return + } + return _hcsGetComputeSystemProperties(computeSystem, _p0, properties, result) +} + +func _hcsGetComputeSystemProperties(computeSystem HcsSystem, propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { + if hr = procHcsGetComputeSystemProperties.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsGetComputeSystemProperties.Addr(), 4, uintptr(computeSystem), uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsModifyComputeSystem(computeSystem HcsSystem, configuration string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(configuration) + if hr != nil { + return + } + return _hcsModifyComputeSystem(computeSystem, _p0, result) +} + +func _hcsModifyComputeSystem(computeSystem HcsSystem, configuration *uint16, result **uint16) (hr error) { + if hr = procHcsModifyComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsModifyComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsModifyServiceSettings(settings string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(settings) + if hr != nil { + return + } + return _hcsModifyServiceSettings(_p0, result) +} + +func _hcsModifyServiceSettings(settings *uint16, result **uint16) (hr error) { + if hr = procHcsModifyServiceSettings.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsModifyServiceSettings.Addr(), 2, uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsRegisterComputeSystemCallback(computeSystem HcsSystem, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) { + if hr = procHcsRegisterComputeSystemCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsRegisterComputeSystemCallback.Addr(), 4, uintptr(computeSystem), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsUnregisterComputeSystemCallback(callbackHandle HcsCallback) (hr error) { + if hr = procHcsUnregisterComputeSystemCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsUnregisterComputeSystemCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsSaveComputeSystem(computeSystem HcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSaveComputeSystem(computeSystem, _p0, result) +} + +func _hcsSaveComputeSystem(computeSystem HcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsSaveComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsSaveComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCreateProcess(computeSystem HcsSystem, processParameters string, processInformation *HcsProcessInformation, process *HcsProcess, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(processParameters) + if hr != nil { + return + } + return _hcsCreateProcess(computeSystem, _p0, processInformation, process, result) +} + +func _hcsCreateProcess(computeSystem HcsSystem, processParameters *uint16, processInformation *HcsProcessInformation, process *HcsProcess, result **uint16) (hr error) { + if hr = procHcsCreateProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsCreateProcess.Addr(), 5, uintptr(computeSystem), uintptr(unsafe.Pointer(processParameters)), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsOpenProcess(computeSystem HcsSystem, pid uint32, process *HcsProcess, result **uint16) (hr error) { + if hr = procHcsOpenProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsOpenProcess.Addr(), 4, uintptr(computeSystem), uintptr(pid), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsCloseProcess(process HcsProcess) (hr error) { + if hr = procHcsCloseProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsCloseProcess.Addr(), 1, uintptr(process), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsTerminateProcess(process HcsProcess, result **uint16) (hr error) { + if hr = procHcsTerminateProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 2, uintptr(process), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsSignalProcess(process HcsProcess, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSignalProcess(process, _p0, result) +} + +func _hcsSignalProcess(process HcsProcess, options *uint16, result **uint16) (hr error) { + if hr = procHcsSignalProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsSignalProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetProcessInfo(process HcsProcess, processInformation *HcsProcessInformation, result **uint16) (hr error) { + if hr = procHcsGetProcessInfo.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetProcessInfo.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetProcessProperties(process HcsProcess, processProperties **uint16, result **uint16) (hr error) { + if hr = procHcsGetProcessProperties.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetProcessProperties.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processProperties)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsModifyProcess(process HcsProcess, settings string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(settings) + if hr != nil { + return + } + return _hcsModifyProcess(process, _p0, result) +} + +func _hcsModifyProcess(process HcsProcess, settings *uint16, result **uint16) (hr error) { + if hr = procHcsModifyProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsModifyProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(propertyQuery) + if hr != nil { + return + } + return _hcsGetServiceProperties(_p0, properties, result) +} + +func _hcsGetServiceProperties(propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { + if hr = procHcsGetServiceProperties.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetServiceProperties.Addr(), 3, uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsRegisterProcessCallback(process HcsProcess, callback uintptr, context uintptr, callbackHandle *HcsCallback) (hr error) { + if hr = procHcsRegisterProcessCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall6(procHcsRegisterProcessCallback.Addr(), 4, uintptr(process), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsUnregisterProcessCallback(callbackHandle HcsCallback) (hr error) { + if hr = procHcsUnregisterProcessCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsUnregisterProcessCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go index 3a0d4bc58ef36..ff81ac2c15637 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go @@ -1,25 +1,27 @@ package wclayer import ( + "context" + "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // ActivateLayer will find the layer with the given id and mount it's filesystem. // For a read/write layer, the mounted filesystem will appear as a volume on the // host, while a read-only layer is generally expected to be a no-op. // An activated layer must later be deactivated via DeactivateLayer. -func ActivateLayer(path string) error { - title := "hcsshim::ActivateLayer " - logrus.Debugf(title+"path %s", path) +func ActivateLayer(ctx context.Context, path string) (err error) { + title := "hcsshim::ActivateLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) - err := activateLayer(&stdDriverInfo, path) + err = activateLayer(&stdDriverInfo, path) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+" - succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go index 5784241dfa03f..3ec708d1ed35c 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go @@ -1,6 +1,7 @@ package wclayer import ( + "context" "errors" "os" "path/filepath" @@ -8,10 +9,16 @@ import ( "github.com/Microsoft/go-winio" "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" "github.com/Microsoft/hcsshim/internal/safefile" + "github.com/Microsoft/hcsshim/internal/winapi" + "go.opencensus.io/trace" ) type baseLayerWriter struct { + ctx context.Context + s *trace.Span + root *os.File f *os.File bw *winio.BackupFileWriter @@ -31,7 +38,7 @@ type dirInfo struct { func reapplyDirectoryTimes(root *os.File, dis []dirInfo) error { for i := range dis { di := &dis[len(dis)-i-1] // reverse order: process child directories first - f, err := safefile.OpenRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, safefile.FILE_OPEN, safefile.FILE_DIRECTORY_FILE) + f, err := safefile.OpenRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, winapi.FILE_OPEN, winapi.FILE_DIRECTORY_FILE|syscall.FILE_FLAG_OPEN_REPARSE_POINT) if err != nil { return err } @@ -41,6 +48,7 @@ func reapplyDirectoryTimes(root *os.File, dis []dirInfo) error { if err != nil { return err } + } return nil } @@ -86,14 +94,12 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e extraFlags := uint32(0) if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { - extraFlags |= safefile.FILE_DIRECTORY_FILE - if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { - w.dirInfo = append(w.dirInfo, dirInfo{name, *fileInfo}) - } + extraFlags |= winapi.FILE_DIRECTORY_FILE + w.dirInfo = append(w.dirInfo, dirInfo{name, *fileInfo}) } mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY) - f, err = safefile.OpenRelative(name, w.root, mode, syscall.FILE_SHARE_READ, safefile.FILE_CREATE, extraFlags) + f, err = safefile.OpenRelative(name, w.root, mode, syscall.FILE_SHARE_READ, winapi.FILE_CREATE, extraFlags) if err != nil { return hcserror.New(err, "Failed to safefile.OpenRelative", name) } @@ -136,12 +142,15 @@ func (w *baseLayerWriter) Write(b []byte) (int, error) { return n, err } -func (w *baseLayerWriter) Close() error { +func (w *baseLayerWriter) Close() (err error) { + defer w.s.End() + defer func() { oc.SetSpanStatus(w.s, err) }() defer func() { w.root.Close() w.root = nil }() - err := w.closeCurrentFile() + + err = w.closeCurrentFile() if err != nil { return err } @@ -153,7 +162,7 @@ func (w *baseLayerWriter) Close() error { return err } - err = ProcessBaseLayer(w.root.Name()) + err = ProcessBaseLayer(w.ctx, w.root.Name()) if err != nil { return err } @@ -163,7 +172,7 @@ func (w *baseLayerWriter) Close() error { if err != nil { return err } - err = ProcessUtilityVMImage(filepath.Join(w.root.Name(), "UtilityVM")) + err = ProcessUtilityVMImage(w.ctx, filepath.Join(w.root.Name(), "UtilityVM")) if err != nil { return err } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go index d158177308858..ffee31ab1261e 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go @@ -1,23 +1,27 @@ package wclayer import ( + "context" + "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // CreateLayer creates a new, empty, read-only layer on the filesystem based on // the parent layer provided. -func CreateLayer(path, parent string) error { - title := "hcsshim::CreateLayer " - logrus.Debugf(title+"ID %s parent %s", path, parent) +func CreateLayer(ctx context.Context, path, parent string) (err error) { + title := "hcsshim::CreateLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("parent", parent)) - err := createLayer(&stdDriverInfo, path, parent) + err = createLayer(&stdDriverInfo, path, parent) if err != nil { - err = hcserror.Errorf(err, title, "path=%s parent=%s", path, parent) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"- succeeded path=%s parent=%s", path, parent) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go index bf2fece19869b..5a3809ae2292d 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go @@ -1,31 +1,34 @@ package wclayer import ( + "context" + "strings" + "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // CreateScratchLayer creates and populates new read-write layer for use by a container. -// This requires both the id of the direct parent layer, as well as the full list -// of paths to all parent layers up to the base (and including the direct parent -// whose id was provided). -func CreateScratchLayer(path string, parentLayerPaths []string) error { - title := "hcsshim::CreateScratchLayer " - logrus.Debugf(title+"path %s", path) +// This requires the full list of paths to all parent layers up to the base +func CreateScratchLayer(ctx context.Context, path string, parentLayerPaths []string) (err error) { + title := "hcsshim::CreateScratchLayer" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) // Generate layer descriptors - layers, err := layerPathsToDescriptors(parentLayerPaths) + layers, err := layerPathsToDescriptors(ctx, parentLayerPaths) if err != nil { return err } err = createSandboxLayer(&stdDriverInfo, path, 0, layers) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"- succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go index b998f8a193e0f..d5bf2f5bdc5f7 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go @@ -1,22 +1,24 @@ package wclayer import ( + "context" + "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // DeactivateLayer will dismount a layer that was mounted via ActivateLayer. -func DeactivateLayer(path string) error { - title := "hcsshim::DeactivateLayer " - logrus.Debugf(title+"path %s", path) +func DeactivateLayer(ctx context.Context, path string) (err error) { + title := "hcsshim::DeactivateLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) - err := deactivateLayer(&stdDriverInfo, path) + err = deactivateLayer(&stdDriverInfo, path) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+"- failed", "") } - - logrus.Debugf(title+"succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go index dc14cecc47e35..787054e794101 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go @@ -1,23 +1,25 @@ package wclayer import ( + "context" + "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // DestroyLayer will remove the on-disk files representing the layer with the given // path, including that layer's containing folder, if any. -func DestroyLayer(path string) error { - title := "hcsshim::DestroyLayer " - logrus.Debugf(title+"path %s", path) +func DestroyLayer(ctx context.Context, path string) (err error) { + title := "hcsshim::DestroyLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) - err := destroyLayer(&stdDriverInfo, path) + err = destroyLayer(&stdDriverInfo, path) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go index 7832bb452e2fb..22f7605beee69 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go @@ -1,22 +1,140 @@ package wclayer import ( + "context" + "os" + "path/filepath" + "syscall" + "unsafe" + "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "github.com/Microsoft/hcsshim/osversion" + "go.opencensus.io/trace" ) // ExpandScratchSize expands the size of a layer to at least size bytes. -func ExpandScratchSize(path string, size uint64) error { - title := "hcsshim::ExpandScratchSize " - logrus.Debugf(title+"path=%s size=%d", path, size) +func ExpandScratchSize(ctx context.Context, path string, size uint64) (err error) { + title := "hcsshim::ExpandScratchSize" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.Int64Attribute("size", int64(size))) + + err = expandSandboxSize(&stdDriverInfo, path, size) + if err != nil { + return hcserror.New(err, title+" - failed", "") + } + + // Manually expand the volume now in order to work around bugs in 19H1 and + // prerelease versions of Vb. Remove once this is fixed in Windows. + if build := osversion.Build(); build >= osversion.V19H1 && build < 19020 { + err = expandSandboxVolume(ctx, path) + if err != nil { + return err + } + } + return nil +} + +type virtualStorageType struct { + DeviceID uint32 + VendorID [16]byte +} + +type openVersion2 struct { + GetInfoOnly int32 // bool but 4-byte aligned + ReadOnly int32 // bool but 4-byte aligned + ResiliencyGUID [16]byte // GUID +} + +type openVirtualDiskParameters struct { + Version uint32 // Must always be set to 2 + Version2 openVersion2 +} + +func attachVhd(path string) (syscall.Handle, error) { + var ( + defaultType virtualStorageType + handle syscall.Handle + ) + parameters := openVirtualDiskParameters{Version: 2} + err := openVirtualDisk( + &defaultType, + path, + 0, + 0, + ¶meters, + &handle) + if err != nil { + return 0, &os.PathError{Op: "OpenVirtualDisk", Path: path, Err: err} + } + err = attachVirtualDisk(handle, 0, 0, 0, 0, 0) + if err != nil { + syscall.Close(handle) + return 0, &os.PathError{Op: "AttachVirtualDisk", Path: path, Err: err} + } + return handle, nil +} - err := expandSandboxSize(&stdDriverInfo, path, size) +func expandSandboxVolume(ctx context.Context, path string) error { + // Mount the sandbox VHD temporarily. + vhdPath := filepath.Join(path, "sandbox.vhdx") + vhd, err := attachVhd(vhdPath) + if err != nil { + return &os.PathError{Op: "OpenVirtualDisk", Path: vhdPath, Err: err} + } + defer syscall.Close(vhd) + + // Open the volume. + volumePath, err := GetLayerMountPath(ctx, path) if err != nil { - err = hcserror.Errorf(err, title, "path=%s size=%d", path, size) - logrus.Error(err) return err } + if volumePath[len(volumePath)-1] == '\\' { + volumePath = volumePath[:len(volumePath)-1] + } + volume, err := os.OpenFile(volumePath, os.O_RDWR, 0) + if err != nil { + return err + } + defer volume.Close() + + // Get the volume's underlying partition size in NTFS clusters. + var ( + partitionSize int64 + bytes uint32 + ) + const _IOCTL_DISK_GET_LENGTH_INFO = 0x0007405C + err = syscall.DeviceIoControl(syscall.Handle(volume.Fd()), _IOCTL_DISK_GET_LENGTH_INFO, nil, 0, (*byte)(unsafe.Pointer(&partitionSize)), 8, &bytes, nil) + if err != nil { + return &os.PathError{Op: "IOCTL_DISK_GET_LENGTH_INFO", Path: volume.Name(), Err: err} + } + const ( + clusterSize = 4096 + sectorSize = 512 + ) + targetClusters := partitionSize / clusterSize - logrus.Debugf(title+"- succeeded path=%s size=%d", path, size) + // Get the volume's current size in NTFS clusters. + var volumeSize int64 + err = getDiskFreeSpaceEx(volume.Name()+"\\", nil, &volumeSize, nil) + if err != nil { + return &os.PathError{Op: "GetDiskFreeSpaceEx", Path: volume.Name(), Err: err} + } + volumeClusters := volumeSize / clusterSize + + // Only resize the volume if there is space to grow, otherwise this will + // fail with invalid parameter. NTFS reserves one cluster. + if volumeClusters+1 < targetClusters { + targetSectors := targetClusters * (clusterSize / sectorSize) + const _FSCTL_EXTEND_VOLUME = 0x000900F0 + err = syscall.DeviceIoControl(syscall.Handle(volume.Fd()), _FSCTL_EXTEND_VOLUME, (*byte)(unsafe.Pointer(&targetSectors)), 8, nil, 0, &bytes, nil) + if err != nil { + return &os.PathError{Op: "FSCTL_EXTEND_VOLUME", Path: volume.Name(), Err: err} + } + } return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go index c6b3480ce7a88..09f0de1a4464d 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go @@ -1,15 +1,15 @@ package wclayer import ( - "io" + "context" "io/ioutil" "os" - "syscall" + "strings" "github.com/Microsoft/go-winio" "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/Microsoft/hcsshim/internal/interop" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // ExportLayer will create a folder at exportFolderPath and fill that folder with @@ -17,24 +17,26 @@ import ( // format includes any metadata required for later importing the layer (using // ImportLayer), and requires the full list of parent layer paths in order to // perform the export. -func ExportLayer(path string, exportFolderPath string, parentLayerPaths []string) error { - title := "hcsshim::ExportLayer " - logrus.Debugf(title+"path %s folder %s", path, exportFolderPath) +func ExportLayer(ctx context.Context, path string, exportFolderPath string, parentLayerPaths []string) (err error) { + title := "hcsshim::ExportLayer" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("exportFolderPath", exportFolderPath), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) // Generate layer descriptors - layers, err := layerPathsToDescriptors(parentLayerPaths) + layers, err := layerPathsToDescriptors(ctx, parentLayerPaths) if err != nil { return err } err = exportLayer(&stdDriverInfo, path, exportFolderPath, layers) if err != nil { - err = hcserror.Errorf(err, title, "path=%s folder=%s", path, exportFolderPath) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s folder=%s", path, exportFolderPath) return nil } @@ -44,104 +46,49 @@ type LayerReader interface { Close() error } -// FilterLayerReader provides an interface for extracting the contents of an on-disk layer. -type FilterLayerReader struct { - context uintptr -} - -// Next reads the next available file from a layer, ensuring that parent directories are always read -// before child files and directories. -// -// Next returns the file's relative path, size, and basic file metadata. Read() should be used to -// extract a Win32 backup stream with the remainder of the metadata and the data. -func (r *FilterLayerReader) Next() (string, int64, *winio.FileBasicInfo, error) { - var fileNamep *uint16 - fileInfo := &winio.FileBasicInfo{} - var deleted uint32 - var fileSize int64 - err := exportLayerNext(r.context, &fileNamep, fileInfo, &fileSize, &deleted) - if err != nil { - if err == syscall.ERROR_NO_MORE_FILES { - err = io.EOF - } else { - err = hcserror.New(err, "ExportLayerNext", "") - } - return "", 0, nil, err - } - fileName := interop.ConvertAndFreeCoTaskMemString(fileNamep) - if deleted != 0 { - fileInfo = nil - } - if fileName[0] == '\\' { - fileName = fileName[1:] - } - return fileName, fileSize, fileInfo, nil -} - -// Read reads from the current file's Win32 backup stream. -func (r *FilterLayerReader) Read(b []byte) (int, error) { - var bytesRead uint32 - err := exportLayerRead(r.context, b, &bytesRead) - if err != nil { - return 0, hcserror.New(err, "ExportLayerRead", "") - } - if bytesRead == 0 { - return 0, io.EOF - } - return int(bytesRead), nil -} - -// Close frees resources associated with the layer reader. It will return an -// error if there was an error while reading the layer or of the layer was not -// completely read. -func (r *FilterLayerReader) Close() (err error) { - if r.context != 0 { - err = exportLayerEnd(r.context) - if err != nil { - err = hcserror.New(err, "ExportLayerEnd", "") - } - r.context = 0 - } - return -} - // NewLayerReader returns a new layer reader for reading the contents of an on-disk layer. // The caller must have taken the SeBackupPrivilege privilege // to call this and any methods on the resulting LayerReader. -func NewLayerReader(path string, parentLayerPaths []string) (LayerReader, error) { - if procExportLayerBegin.Find() != nil { - // The new layer reader is not available on this Windows build. Fall back to the - // legacy export code path. - exportPath, err := ioutil.TempDir("", "hcs") +func NewLayerReader(ctx context.Context, path string, parentLayerPaths []string) (_ LayerReader, err error) { + ctx, span := trace.StartSpan(ctx, "hcsshim::NewLayerReader") + defer func() { if err != nil { - return nil, err + oc.SetSpanStatus(span, err) + span.End() } - err = ExportLayer(path, exportPath, parentLayerPaths) - if err != nil { - os.RemoveAll(exportPath) - return nil, err - } - return &legacyLayerReaderWrapper{newLegacyLayerReader(exportPath)}, nil - } + }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) - layers, err := layerPathsToDescriptors(parentLayerPaths) + exportPath, err := ioutil.TempDir("", "hcs") if err != nil { return nil, err } - r := &FilterLayerReader{} - err = exportLayerBegin(&stdDriverInfo, path, layers, &r.context) + err = ExportLayer(ctx, path, exportPath, parentLayerPaths) if err != nil { - return nil, hcserror.New(err, "ExportLayerBegin", "") + os.RemoveAll(exportPath) + return nil, err } - return r, err + return &legacyLayerReaderWrapper{ + ctx: ctx, + s: span, + legacyLayerReader: newLegacyLayerReader(exportPath), + }, nil } type legacyLayerReaderWrapper struct { + ctx context.Context + s *trace.Span + *legacyLayerReader } -func (r *legacyLayerReaderWrapper) Close() error { - err := r.legacyLayerReader.Close() +func (r *legacyLayerReaderWrapper) Close() (err error) { + defer r.s.End() + defer func() { oc.SetSpanStatus(r.s, err) }() + + err = r.legacyLayerReader.Close() os.RemoveAll(r.root) return err } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go index 8c37549a0eb4b..4d22d0ecf3df7 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go @@ -1,30 +1,33 @@ package wclayer import ( + "context" "syscall" "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/log" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // GetLayerMountPath will look for a mounted layer with the given path and return // the path at which that layer can be accessed. This path may be a volume path // if the layer is a mounted read-write layer, otherwise it is expected to be the // folder path at which the layer is stored. -func GetLayerMountPath(path string) (string, error) { - title := "hcsshim::GetLayerMountPath " - logrus.Debugf(title+"path %s", path) +func GetLayerMountPath(ctx context.Context, path string) (_ string, err error) { + title := "hcsshim::GetLayerMountPath" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) - var mountPathLength uintptr - mountPathLength = 0 + var mountPathLength uintptr = 0 // Call the procedure itself. - logrus.Debugf("Calling proc (1)") - err := getLayerMountPath(&stdDriverInfo, path, &mountPathLength, nil) + log.G(ctx).Debug("Calling proc (1)") + err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, nil) if err != nil { - err = hcserror.Errorf(err, title, "(first call) path=%s", path) - logrus.Error(err) - return "", err + return "", hcserror.New(err, title+" - failed", "(first call)") } // Allocate a mount path of the returned length. @@ -35,15 +38,13 @@ func GetLayerMountPath(path string) (string, error) { mountPathp[0] = 0 // Call the procedure again - logrus.Debugf("Calling proc (2)") + log.G(ctx).Debug("Calling proc (2)") err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, &mountPathp[0]) if err != nil { - err = hcserror.Errorf(err, title, "(second call) path=%s", path) - logrus.Error(err) - return "", err + return "", hcserror.New(err, title+" - failed", "(second call)") } mountPath := syscall.UTF16ToString(mountPathp[0:]) - logrus.Debugf(title+"succeeded path=%s mountPath=%s", path, mountPath) + span.AddAttributes(trace.StringAttribute("mountPath", mountPath)) return mountPath, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go index 10899c68af77c..bcc8fbd423932 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go @@ -1,26 +1,29 @@ package wclayer import ( + "context" + "github.com/Microsoft/hcsshim/internal/hcserror" "github.com/Microsoft/hcsshim/internal/interop" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // GetSharedBaseImages will enumerate the images stored in the common central // image store and return descriptive info about those images for the purpose // of registering them with the graphdriver, graph, and tagstore. -func GetSharedBaseImages() (imageData string, err error) { - title := "hcsshim::GetSharedBaseImages " +func GetSharedBaseImages(ctx context.Context) (_ string, err error) { + title := "hcsshim::GetSharedBaseImages" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() - logrus.Debugf("Calling proc") var buffer *uint16 err = getBaseImages(&buffer) if err != nil { - err = hcserror.New(err, title, "") - logrus.Error(err) - return + return "", hcserror.New(err, title+" - failed", "") } - imageData = interop.ConvertAndFreeCoTaskMemString(buffer) - logrus.Debugf(title+" - succeeded output=%s", imageData) - return + imageData := interop.ConvertAndFreeCoTaskMemString(buffer) + span.AddAttributes(trace.StringAttribute("imageData", imageData)) + return imageData, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go index d86e6782756a6..3eaca27808e1c 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go @@ -1,24 +1,26 @@ package wclayer import ( - "fmt" + "context" "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // GrantVmAccess adds access to a file for a given VM -func GrantVmAccess(vmid string, filepath string) error { - title := fmt.Sprintf("hcsshim::GrantVmAccess id:%s path:%s ", vmid, filepath) - logrus.Debugf(title) +func GrantVmAccess(ctx context.Context, vmid string, filepath string) (err error) { + title := "hcsshim::GrantVmAccess" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("vm-id", vmid), + trace.StringAttribute("path", filepath)) - err := grantVmAccess(vmid, filepath) + err = grantVmAccess(vmid, filepath) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", filepath) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title + " - succeeded") return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go index c978450f8284f..b3c150d66fe7a 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go @@ -1,39 +1,43 @@ package wclayer import ( - "errors" + "context" "io/ioutil" "os" "path/filepath" + "strings" "github.com/Microsoft/go-winio" "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/oc" "github.com/Microsoft/hcsshim/internal/safefile" - "github.com/sirupsen/logrus" + "go.opencensus.io/trace" ) // ImportLayer will take the contents of the folder at importFolderPath and import // that into a layer with the id layerId. Note that in order to correctly populate // the layer and interperet the transport format, all parent layers must already // be present on the system at the paths provided in parentLayerPaths. -func ImportLayer(path string, importFolderPath string, parentLayerPaths []string) error { - title := "hcsshim::ImportLayer " - logrus.Debugf(title+"path %s folder %s", path, importFolderPath) +func ImportLayer(ctx context.Context, path string, importFolderPath string, parentLayerPaths []string) (err error) { + title := "hcsshim::ImportLayer" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("importFolderPath", importFolderPath), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) // Generate layer descriptors - layers, err := layerPathsToDescriptors(parentLayerPaths) + layers, err := layerPathsToDescriptors(ctx, parentLayerPaths) if err != nil { return err } err = importLayer(&stdDriverInfo, path, importFolderPath, layers) if err != nil { - err = hcserror.Errorf(err, title, "path=%s folder=%s", path, importFolderPath) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s folder=%s", path, importFolderPath) return nil } @@ -52,84 +56,27 @@ type LayerWriter interface { Close() error } -// FilterLayerWriter provides an interface to write the contents of a layer to the file system. -type FilterLayerWriter struct { - context uintptr -} - -// Add adds a file or directory to the layer. The file's parent directory must have already been added. -// -// name contains the file's relative path. fileInfo contains file times and file attributes; the rest -// of the file metadata and the file data must be written as a Win32 backup stream to the Write() method. -// winio.BackupStreamWriter can be used to facilitate this. -func (w *FilterLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error { - if name[0] != '\\' { - name = `\` + name - } - err := importLayerNext(w.context, name, fileInfo) - if err != nil { - return hcserror.New(err, "ImportLayerNext", "") - } - return nil -} - -// AddLink adds a hard link to the layer. The target of the link must have already been added. -func (w *FilterLayerWriter) AddLink(name string, target string) error { - return errors.New("hard links not yet supported") -} - -// Remove removes a file from the layer. The file must have been present in the parent layer. -// -// name contains the file's relative path. -func (w *FilterLayerWriter) Remove(name string) error { - if name[0] != '\\' { - name = `\` + name - } - err := importLayerNext(w.context, name, nil) - if err != nil { - return hcserror.New(err, "ImportLayerNext", "") - } - return nil -} - -// Write writes more backup stream data to the current file. -func (w *FilterLayerWriter) Write(b []byte) (int, error) { - err := importLayerWrite(w.context, b) - if err != nil { - err = hcserror.New(err, "ImportLayerWrite", "") - return 0, err - } - return len(b), err -} - -// Close completes the layer write operation. The error must be checked to ensure that the -// operation was successful. -func (w *FilterLayerWriter) Close() (err error) { - if w.context != 0 { - err = importLayerEnd(w.context) - if err != nil { - err = hcserror.New(err, "ImportLayerEnd", "") - } - w.context = 0 - } - return -} - type legacyLayerWriterWrapper struct { + ctx context.Context + s *trace.Span + *legacyLayerWriter path string parentLayerPaths []string } -func (r *legacyLayerWriterWrapper) Close() error { +func (r *legacyLayerWriterWrapper) Close() (err error) { + defer r.s.End() + defer func() { oc.SetSpanStatus(r.s, err) }() defer os.RemoveAll(r.root.Name()) defer r.legacyLayerWriter.CloseRoots() - err := r.legacyLayerWriter.Close() + + err = r.legacyLayerWriter.Close() if err != nil { return err } - if err = ImportLayer(r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil { + if err = ImportLayer(r.ctx, r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil { return err } for _, name := range r.Tombstones { @@ -146,13 +93,26 @@ func (r *legacyLayerWriterWrapper) Close() error { return err } } + + // The reapplyDirectoryTimes must be called AFTER we are done with Tombstone + // deletion and hard link creation. This is because Tombstone deletion and hard link + // creation updates the directory last write timestamps so that will change the + // timestamps added by the `Add` call. Some container applications depend on the + // correctness of these timestamps and so we should change the timestamps back to + // the original value (i.e the value provided in the Add call) after this + // processing is done. + err = reapplyDirectoryTimes(r.destRoot, r.changedDi) + if err != nil { + return err + } + // Prepare the utility VM for use if one is present in the layer. if r.HasUtilityVM { err := safefile.EnsureNotReparsePointRelative("UtilityVM", r.destRoot) if err != nil { return err } - err = ProcessUtilityVMImage(filepath.Join(r.destRoot.Name(), "UtilityVM")) + err = ProcessUtilityVMImage(r.ctx, filepath.Join(r.destRoot.Name(), "UtilityVM")) if err != nil { return err } @@ -163,7 +123,18 @@ func (r *legacyLayerWriterWrapper) Close() error { // NewLayerWriter returns a new layer writer for creating a layer on disk. // The caller must have taken the SeBackupPrivilege and SeRestorePrivilege privileges // to call this and any methods on the resulting LayerWriter. -func NewLayerWriter(path string, parentLayerPaths []string) (LayerWriter, error) { +func NewLayerWriter(ctx context.Context, path string, parentLayerPaths []string) (_ LayerWriter, err error) { + ctx, span := trace.StartSpan(ctx, "hcsshim::NewLayerWriter") + defer func() { + if err != nil { + oc.SetSpanStatus(span, err) + span.End() + } + }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) + if len(parentLayerPaths) == 0 { // This is a base layer. It gets imported differently. f, err := safefile.OpenRoot(path) @@ -171,36 +142,25 @@ func NewLayerWriter(path string, parentLayerPaths []string) (LayerWriter, error) return nil, err } return &baseLayerWriter{ + ctx: ctx, + s: span, root: f, }, nil } - if procImportLayerBegin.Find() != nil { - // The new layer reader is not available on this Windows build. Fall back to the - // legacy export code path. - importPath, err := ioutil.TempDir("", "hcs") - if err != nil { - return nil, err - } - w, err := newLegacyLayerWriter(importPath, parentLayerPaths, path) - if err != nil { - return nil, err - } - return &legacyLayerWriterWrapper{ - legacyLayerWriter: w, - path: importPath, - parentLayerPaths: parentLayerPaths, - }, nil - } - layers, err := layerPathsToDescriptors(parentLayerPaths) + importPath, err := ioutil.TempDir("", "hcs") if err != nil { return nil, err } - - w := &FilterLayerWriter{} - err = importLayerBegin(&stdDriverInfo, path, layers, &w.context) + w, err := newLegacyLayerWriter(importPath, parentLayerPaths, path) if err != nil { - return nil, hcserror.New(err, "ImportLayerStart", "") + return nil, err } - return w, nil + return &legacyLayerWriterWrapper{ + ctx: ctx, + s: span, + legacyLayerWriter: w, + path: importPath, + parentLayerPaths: parentLayerPaths, + }, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go index 71287ff8a7597..c6999973cb805 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go @@ -1,25 +1,28 @@ package wclayer import ( + "context" + "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // LayerExists will return true if a layer with the given id exists and is known // to the system. -func LayerExists(path string) (bool, error) { - title := "hcsshim::LayerExists " - logrus.Debugf(title+"path %s", path) +func LayerExists(ctx context.Context, path string) (_ bool, err error) { + title := "hcsshim::LayerExists" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) // Call the procedure itself. var exists uint32 - err := layerExists(&stdDriverInfo, path, &exists) + err = layerExists(&stdDriverInfo, path, &exists) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return false, err + return false, hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s exists=%d", path, exists) + span.AddAttributes(trace.BoolAttribute("layer-exists", exists != 0)) return exists != 0, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go index 90df3bedceb65..0ce34a30f86a9 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go @@ -1,13 +1,22 @@ package wclayer import ( + "context" "path/filepath" - "github.com/Microsoft/hcsshim/internal/guid" + "github.com/Microsoft/go-winio/pkg/guid" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // LayerID returns the layer ID of a layer on disk. -func LayerID(path string) (guid.GUID, error) { +func LayerID(ctx context.Context, path string) (_ guid.GUID, err error) { + title := "hcsshim::LayerID" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + _, file := filepath.Split(path) - return NameToGuid(file) + return NameToGuid(ctx, file) } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go index a1b8b9882662f..1ec893c6af7a8 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go @@ -4,9 +4,10 @@ package wclayer // functionality. import ( + "context" "syscall" - "github.com/Microsoft/hcsshim/internal/guid" + "github.com/Microsoft/go-winio/pkg/guid" "github.com/sirupsen/logrus" ) @@ -68,20 +69,20 @@ type WC_LAYER_DESCRIPTOR struct { Pathp *uint16 } -func layerPathsToDescriptors(parentLayerPaths []string) ([]WC_LAYER_DESCRIPTOR, error) { +func layerPathsToDescriptors(ctx context.Context, parentLayerPaths []string) ([]WC_LAYER_DESCRIPTOR, error) { // Array of descriptors that gets constructed. var layers []WC_LAYER_DESCRIPTOR for i := 0; i < len(parentLayerPaths); i++ { - g, err := LayerID(parentLayerPaths[i]) + g, err := LayerID(ctx, parentLayerPaths[i]) if err != nil { - logrus.Debugf("Failed to convert name to guid %s", err) + logrus.WithError(err).Debug("Failed to convert name to guid") return nil, err } p, err := syscall.UTF16PtrFromString(parentLayerPaths[i]) if err != nil { - logrus.Debugf("Failed conversion of parentLayerPath to pointer %s", err) + logrus.WithError(err).Debug("Failed conversion of parentLayerPath to pointer") return nil, err } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go index b8ea5d2632ea0..83ba72cfadac3 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go @@ -15,6 +15,7 @@ import ( "github.com/Microsoft/go-winio" "github.com/Microsoft/hcsshim/internal/longpath" "github.com/Microsoft/hcsshim/internal/safefile" + "github.com/Microsoft/hcsshim/internal/winapi" ) var errorIterationCanceled = errors.New("") @@ -341,7 +342,7 @@ type legacyLayerWriter struct { backupWriter *winio.BackupFileWriter Tombstones []string HasUtilityVM bool - uvmDi []dirInfo + changedDi []dirInfo addedFiles map[string]bool PendingLinks []pendingLink pendingDirs []pendingDir @@ -389,7 +390,7 @@ func (w *legacyLayerWriter) CloseRoots() { w.destRoot = nil } for i := range w.parentRoots { - w.parentRoots[i].Close() + _ = w.parentRoots[i].Close() } w.parentRoots = nil } @@ -472,8 +473,8 @@ func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool srcRoot, syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY, syscall.FILE_SHARE_READ, - safefile.FILE_OPEN, - safefile.FILE_OPEN_REPARSE_POINT) + winapi.FILE_OPEN, + winapi.FILE_OPEN_REPARSE_POINT) if err != nil { return nil, err } @@ -488,14 +489,14 @@ func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool extraFlags := uint32(0) if isDir { - extraFlags |= safefile.FILE_DIRECTORY_FILE + extraFlags |= winapi.FILE_DIRECTORY_FILE } dest, err := safefile.OpenRelative( subPath, destRoot, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, syscall.FILE_SHARE_READ, - safefile.FILE_CREATE, + winapi.FILE_CREATE, extraFlags) if err != nil { return nil, err @@ -555,7 +556,7 @@ func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles if err != nil { return err } - if isDir && !isReparsePoint { + if isDir { di = append(di, dirInfo{path: relPath, fileInfo: *fi}) } } else { @@ -583,6 +584,10 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro return w.initUtilityVM() } + if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { + w.changedDi = append(w.changedDi, dirInfo{path: name, fileInfo: *fileInfo}) + } + name = filepath.Clean(name) if hasPathPrefix(name, utilityVMPath) { if !w.HasUtilityVM { @@ -591,7 +596,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath { return errors.New("invalid UtilityVM layer") } - createDisposition := uint32(safefile.FILE_OPEN) + createDisposition := uint32(winapi.FILE_OPEN) if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { st, err := safefile.LstatRelative(name, w.destRoot) if err != nil && !os.IsNotExist(err) { @@ -612,16 +617,13 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro return err } } - if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { - w.uvmDi = append(w.uvmDi, dirInfo{path: name, fileInfo: *fileInfo}) - } } else { // Overwrite any existing hard link. err := safefile.RemoveRelative(name, w.destRoot) if err != nil && !os.IsNotExist(err) { return err } - createDisposition = safefile.FILE_CREATE + createDisposition = winapi.FILE_CREATE } f, err := safefile.OpenRelative( @@ -630,7 +632,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, syscall.FILE_SHARE_READ, createDisposition, - safefile.FILE_OPEN_REPARSE_POINT, + winapi.FILE_OPEN_REPARSE_POINT, ) if err != nil { return err @@ -638,7 +640,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro defer func() { if f != nil { f.Close() - safefile.RemoveRelative(name, w.destRoot) + _ = safefile.RemoveRelative(name, w.destRoot) } }() @@ -667,14 +669,14 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro w.currentIsDir = true } - f, err := safefile.OpenRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, safefile.FILE_CREATE, 0) + f, err := safefile.OpenRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, winapi.FILE_CREATE, 0) if err != nil { return err } defer func() { if f != nil { f.Close() - safefile.RemoveRelative(fname, w.root) + _ = safefile.RemoveRelative(fname, w.root) } }() @@ -805,11 +807,5 @@ func (w *legacyLayerWriter) Close() error { return err } } - if w.HasUtilityVM { - err := reapplyDirectoryTimes(w.destRoot, w.uvmDi) - if err != nil { - return err - } - } return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go index 741994ba4dca0..bcf39c6b86c24 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go @@ -1,24 +1,29 @@ package wclayer import ( - "github.com/Microsoft/hcsshim/internal/guid" + "context" + + "github.com/Microsoft/go-winio/pkg/guid" "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // NameToGuid converts the given string into a GUID using the algorithm in the // Host Compute Service, ensuring GUIDs generated with the same string are common // across all clients. -func NameToGuid(name string) (id guid.GUID, err error) { - title := "hcsshim::NameToGuid " +func NameToGuid(ctx context.Context, name string) (_ guid.GUID, err error) { + title := "hcsshim::NameToGuid" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("name", name)) + var id guid.GUID err = nameToGuid(name, &id) if err != nil { - err = hcserror.Errorf(err, title, "name=%s", name) - logrus.Error(err) - return + return guid.GUID{}, hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"name:%s guid:%s", name, id.String()) - return + span.AddAttributes(trace.StringAttribute("guid", id.String())) + return id, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go index bd4005dc4f659..55f7730d0c181 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go @@ -1,10 +1,13 @@ package wclayer import ( + "context" + "strings" "sync" "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) var prepareLayerLock sync.Mutex @@ -14,12 +17,17 @@ var prepareLayerLock sync.Mutex // parent layers, and is necessary in order to view or interact with the layer // as an actual filesystem (reading and writing files, creating directories, etc). // Disabling the filter must be done via UnprepareLayer. -func PrepareLayer(path string, parentLayerPaths []string) error { - title := "hcsshim::PrepareLayer " - logrus.Debugf(title+"path %s", path) +func PrepareLayer(ctx context.Context, path string, parentLayerPaths []string) (err error) { + title := "hcsshim::PrepareLayer" + ctx, span := trace.StartSpan(ctx, title) + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes( + trace.StringAttribute("path", path), + trace.StringAttribute("parentLayerPaths", strings.Join(parentLayerPaths, ", "))) // Generate layer descriptors - layers, err := layerPathsToDescriptors(parentLayerPaths) + layers, err := layerPathsToDescriptors(ctx, parentLayerPaths) if err != nil { return err } @@ -30,11 +38,7 @@ func PrepareLayer(path string, parentLayerPaths []string) error { defer prepareLayerLock.Unlock() err = prepareLayer(&stdDriverInfo, path, layers) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go index 884207c3edb77..30bcdff5f5539 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go @@ -1,23 +1,41 @@ package wclayer -import "os" +import ( + "context" + "os" + + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" +) // ProcessBaseLayer post-processes a base layer that has had its files extracted. // The files should have been extracted to \Files. -func ProcessBaseLayer(path string) error { - err := processBaseImage(path) +func ProcessBaseLayer(ctx context.Context, path string) (err error) { + title := "hcsshim::ProcessBaseLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + err = processBaseImage(path) if err != nil { - return &os.PathError{Op: "ProcessBaseLayer", Path: path, Err: err} + return &os.PathError{Op: title, Path: path, Err: err} } return nil } // ProcessUtilityVMImage post-processes a utility VM image that has had its files extracted. // The files should have been extracted to \Files. -func ProcessUtilityVMImage(path string) error { - err := processUtilityImage(path) +func ProcessUtilityVMImage(ctx context.Context, path string) (err error) { + title := "hcsshim::ProcessUtilityVMImage" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) + + err = processUtilityImage(path) if err != nil { - return &os.PathError{Op: "ProcessUtilityVMImage", Path: path, Err: err} + return &os.PathError{Op: title, Path: path, Err: err} } return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go index 5f1b4f4f4ec79..79fb986786cd9 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go @@ -1,23 +1,25 @@ package wclayer import ( + "context" + "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/oc" + "go.opencensus.io/trace" ) // UnprepareLayer disables the filesystem filter for the read-write layer with // the given id. -func UnprepareLayer(path string) error { - title := "hcsshim::UnprepareLayer " - logrus.Debugf(title+"path %s", path) +func UnprepareLayer(ctx context.Context, path string) (err error) { + title := "hcsshim::UnprepareLayer" + ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + span.AddAttributes(trace.StringAttribute("path", path)) - err := unprepareLayer(&stdDriverInfo, path) + err = unprepareLayer(&stdDriverInfo, path) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go index 768a6f2f16116..9b1e06d50c5d7 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go @@ -1,8 +1,11 @@ +// Package wclayer provides bindings to HCS's legacy layer management API and +// provides a higher level interface around these calls for container layer +// management. package wclayer -import "github.com/Microsoft/hcsshim/internal/guid" +import "github.com/Microsoft/go-winio/pkg/guid" -//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go -winio wclayer.go +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go wclayer.go //sys activateLayer(info *driverInfo, id string) (hr error) = vmcompute.ActivateLayer? //sys copyLayer(info *driverInfo, srcId string, dstId string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.CopyLayer? @@ -22,16 +25,11 @@ import "github.com/Microsoft/hcsshim/internal/guid" //sys processBaseImage(path string) (hr error) = vmcompute.ProcessBaseImage? //sys processUtilityImage(path string) (hr error) = vmcompute.ProcessUtilityImage? -//sys importLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) = vmcompute.ImportLayerBegin? -//sys importLayerNext(context uintptr, fileName string, fileInfo *winio.FileBasicInfo) (hr error) = vmcompute.ImportLayerNext? -//sys importLayerWrite(context uintptr, buffer []byte) (hr error) = vmcompute.ImportLayerWrite? -//sys importLayerEnd(context uintptr) (hr error) = vmcompute.ImportLayerEnd? +//sys grantVmAccess(vmid string, filepath string) (hr error) = vmcompute.GrantVmAccess? -//sys exportLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) = vmcompute.ExportLayerBegin? -//sys exportLayerNext(context uintptr, fileName **uint16, fileInfo *winio.FileBasicInfo, fileSize *int64, deleted *uint32) (hr error) = vmcompute.ExportLayerNext? -//sys exportLayerRead(context uintptr, buffer []byte, bytesRead *uint32) (hr error) = vmcompute.ExportLayerRead? -//sys exportLayerEnd(context uintptr) (hr error) = vmcompute.ExportLayerEnd? +//sys openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (err error) [failretval != 0] = virtdisk.OpenVirtualDisk +//sys attachVirtualDisk(handle syscall.Handle, sd uintptr, flags uint32, providerFlags uint32, params uintptr, overlapped uintptr) (err error) [failretval != 0] = virtdisk.AttachVirtualDisk -//sys grantVmAccess(vmid string, filepath string) (hr error) = vmcompute.GrantVmAccess? +//sys getDiskFreeSpaceEx(directoryName string, freeBytesAvailableToCaller *int64, totalNumberOfBytes *int64, totalNumberOfFreeBytes *int64) (err error) = GetDiskFreeSpaceExW type _guid = guid.GUID diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go index cb813aa3d43f5..67f917f07e615 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated mksyscall_windows.exe DO NOT EDIT package wclayer @@ -6,8 +6,6 @@ import ( "syscall" "unsafe" - "github.com/Microsoft/go-winio" - "github.com/Microsoft/hcsshim/internal/interop" "golang.org/x/sys/windows" ) @@ -40,6 +38,8 @@ func errnoErr(e syscall.Errno) error { var ( modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + modvirtdisk = windows.NewLazySystemDLL("virtdisk.dll") + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") procActivateLayer = modvmcompute.NewProc("ActivateLayer") procCopyLayer = modvmcompute.NewProc("CopyLayer") @@ -58,15 +58,10 @@ var ( procUnprepareLayer = modvmcompute.NewProc("UnprepareLayer") procProcessBaseImage = modvmcompute.NewProc("ProcessBaseImage") procProcessUtilityImage = modvmcompute.NewProc("ProcessUtilityImage") - procImportLayerBegin = modvmcompute.NewProc("ImportLayerBegin") - procImportLayerNext = modvmcompute.NewProc("ImportLayerNext") - procImportLayerWrite = modvmcompute.NewProc("ImportLayerWrite") - procImportLayerEnd = modvmcompute.NewProc("ImportLayerEnd") - procExportLayerBegin = modvmcompute.NewProc("ExportLayerBegin") - procExportLayerNext = modvmcompute.NewProc("ExportLayerNext") - procExportLayerRead = modvmcompute.NewProc("ExportLayerRead") - procExportLayerEnd = modvmcompute.NewProc("ExportLayerEnd") procGrantVmAccess = modvmcompute.NewProc("GrantVmAccess") + procOpenVirtualDisk = modvirtdisk.NewProc("OpenVirtualDisk") + procAttachVirtualDisk = modvirtdisk.NewProc("AttachVirtualDisk") + procGetDiskFreeSpaceExW = modkernel32.NewProc("GetDiskFreeSpaceExW") ) func activateLayer(info *driverInfo, id string) (hr error) { @@ -84,7 +79,10 @@ func _activateLayer(info *driverInfo, id *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procActivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -113,7 +111,10 @@ func _copyLayer(info *driverInfo, srcId *uint16, dstId *uint16, descriptors []WC } r0, _, _ := syscall.Syscall6(procCopyLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(srcId)), uintptr(unsafe.Pointer(dstId)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -138,7 +139,10 @@ func _createLayer(info *driverInfo, id *uint16, parent *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procCreateLayer.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(parent))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -162,7 +166,10 @@ func _createSandboxLayer(info *driverInfo, id *uint16, parent uintptr, descripto } r0, _, _ := syscall.Syscall6(procCreateSandboxLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(parent), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -182,7 +189,10 @@ func _expandSandboxSize(info *driverInfo, id *uint16, size uint64) (hr error) { } r0, _, _ := syscall.Syscall(procExpandSandboxSize.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(size)) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -202,7 +212,10 @@ func _deactivateLayer(info *driverInfo, id *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procDeactivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -222,7 +235,10 @@ func _destroyLayer(info *driverInfo, id *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procDestroyLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -251,7 +267,10 @@ func _exportLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_L } r0, _, _ := syscall.Syscall6(procExportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -271,7 +290,10 @@ func _getLayerMountPath(info *driverInfo, id *uint16, length *uintptr, buffer *u } r0, _, _ := syscall.Syscall6(procGetLayerMountPath.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(length)), uintptr(unsafe.Pointer(buffer)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -282,7 +304,10 @@ func getBaseImages(buffer **uint16) (hr error) { } r0, _, _ := syscall.Syscall(procGetBaseImages.Addr(), 1, uintptr(unsafe.Pointer(buffer)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -311,7 +336,10 @@ func _importLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_L } r0, _, _ := syscall.Syscall6(procImportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -331,7 +359,10 @@ func _layerExists(info *driverInfo, id *uint16, exists *uint32) (hr error) { } r0, _, _ := syscall.Syscall(procLayerExists.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(exists))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -351,7 +382,10 @@ func _nameToGuid(name *uint16, guid *_guid) (hr error) { } r0, _, _ := syscall.Syscall(procNameToGuid.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(guid)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -375,7 +409,10 @@ func _prepareLayer(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPT } r0, _, _ := syscall.Syscall6(procPrepareLayer.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -395,7 +432,10 @@ func _unprepareLayer(info *driverInfo, id *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procUnprepareLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -415,7 +455,10 @@ func _processBaseImage(path *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procProcessBaseImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -435,163 +478,92 @@ func _processUtilityImage(path *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procProcessUtilityImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } -func importLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { +func grantVmAccess(vmid string, filepath string) (hr error) { var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) + _p0, hr = syscall.UTF16PtrFromString(vmid) if hr != nil { return } - return _importLayerBegin(info, _p0, descriptors, context) -} - -func _importLayerBegin(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { - var _p1 *WC_LAYER_DESCRIPTOR - if len(descriptors) > 0 { - _p1 = &descriptors[0] - } - if hr = procImportLayerBegin.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procImportLayerBegin.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), uintptr(unsafe.Pointer(context)), 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func importLayerNext(context uintptr, fileName string, fileInfo *winio.FileBasicInfo) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(fileName) + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(filepath) if hr != nil { return } - return _importLayerNext(context, _p0, fileInfo) -} - -func _importLayerNext(context uintptr, fileName *uint16, fileInfo *winio.FileBasicInfo) (hr error) { - if hr = procImportLayerNext.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procImportLayerNext.Addr(), 3, uintptr(context), uintptr(unsafe.Pointer(fileName)), uintptr(unsafe.Pointer(fileInfo))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func importLayerWrite(context uintptr, buffer []byte) (hr error) { - var _p0 *byte - if len(buffer) > 0 { - _p0 = &buffer[0] - } - if hr = procImportLayerWrite.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procImportLayerWrite.Addr(), 3, uintptr(context), uintptr(unsafe.Pointer(_p0)), uintptr(len(buffer))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return + return _grantVmAccess(_p0, _p1) } -func importLayerEnd(context uintptr) (hr error) { - if hr = procImportLayerEnd.Find(); hr != nil { +func _grantVmAccess(vmid *uint16, filepath *uint16) (hr error) { + if hr = procGrantVmAccess.Find(); hr != nil { return } - r0, _, _ := syscall.Syscall(procImportLayerEnd.Addr(), 1, uintptr(context), 0, 0) + r0, _, _ := syscall.Syscall(procGrantVmAccess.Addr(), 2, uintptr(unsafe.Pointer(vmid)), uintptr(unsafe.Pointer(filepath)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } -func exportLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { +func openVirtualDisk(virtualStorageType *virtualStorageType, path string, virtualDiskAccessMask uint32, flags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (err error) { var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { + _p0, err = syscall.UTF16PtrFromString(path) + if err != nil { return } - return _exportLayerBegin(info, _p0, descriptors, context) + return _openVirtualDisk(virtualStorageType, _p0, virtualDiskAccessMask, flags, parameters, handle) } -func _exportLayerBegin(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { - var _p1 *WC_LAYER_DESCRIPTOR - if len(descriptors) > 0 { - _p1 = &descriptors[0] - } - if hr = procExportLayerBegin.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procExportLayerBegin.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), uintptr(unsafe.Pointer(context)), 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func exportLayerNext(context uintptr, fileName **uint16, fileInfo *winio.FileBasicInfo, fileSize *int64, deleted *uint32) (hr error) { - if hr = procExportLayerNext.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procExportLayerNext.Addr(), 5, uintptr(context), uintptr(unsafe.Pointer(fileName)), uintptr(unsafe.Pointer(fileInfo)), uintptr(unsafe.Pointer(fileSize)), uintptr(unsafe.Pointer(deleted)), 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func exportLayerRead(context uintptr, buffer []byte, bytesRead *uint32) (hr error) { - var _p0 *byte - if len(buffer) > 0 { - _p0 = &buffer[0] - } - if hr = procExportLayerRead.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procExportLayerRead.Addr(), 4, uintptr(context), uintptr(unsafe.Pointer(_p0)), uintptr(len(buffer)), uintptr(unsafe.Pointer(bytesRead)), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) +func _openVirtualDisk(virtualStorageType *virtualStorageType, path *uint16, virtualDiskAccessMask uint32, flags uint32, parameters *openVirtualDiskParameters, handle *syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall6(procOpenVirtualDisk.Addr(), 6, uintptr(unsafe.Pointer(virtualStorageType)), uintptr(unsafe.Pointer(path)), uintptr(virtualDiskAccessMask), uintptr(flags), uintptr(unsafe.Pointer(parameters)), uintptr(unsafe.Pointer(handle))) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } } return } -func exportLayerEnd(context uintptr) (hr error) { - if hr = procExportLayerEnd.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procExportLayerEnd.Addr(), 1, uintptr(context), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) +func attachVirtualDisk(handle syscall.Handle, sd uintptr, flags uint32, providerFlags uint32, params uintptr, overlapped uintptr) (err error) { + r1, _, e1 := syscall.Syscall6(procAttachVirtualDisk.Addr(), 6, uintptr(handle), uintptr(sd), uintptr(flags), uintptr(providerFlags), uintptr(params), uintptr(overlapped)) + if r1 != 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } } return } -func grantVmAccess(vmid string, filepath string) (hr error) { +func getDiskFreeSpaceEx(directoryName string, freeBytesAvailableToCaller *int64, totalNumberOfBytes *int64, totalNumberOfFreeBytes *int64) (err error) { var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(vmid) - if hr != nil { - return - } - var _p1 *uint16 - _p1, hr = syscall.UTF16PtrFromString(filepath) - if hr != nil { + _p0, err = syscall.UTF16PtrFromString(directoryName) + if err != nil { return } - return _grantVmAccess(_p0, _p1) + return _getDiskFreeSpaceEx(_p0, freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes) } -func _grantVmAccess(vmid *uint16, filepath *uint16) (hr error) { - if hr = procGrantVmAccess.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procGrantVmAccess.Addr(), 2, uintptr(unsafe.Pointer(vmid)), uintptr(unsafe.Pointer(filepath)), 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) +func _getDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailableToCaller *int64, totalNumberOfBytes *int64, totalNumberOfFreeBytes *int64) (err error) { + r1, _, e1 := syscall.Syscall6(procGetDiskFreeSpaceExW.Addr(), 4, uintptr(unsafe.Pointer(directoryName)), uintptr(unsafe.Pointer(freeBytesAvailableToCaller)), uintptr(unsafe.Pointer(totalNumberOfBytes)), uintptr(unsafe.Pointer(totalNumberOfFreeBytes)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } } return } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/devices.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/devices.go new file mode 100644 index 0000000000000..df28ea24216d1 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/devices.go @@ -0,0 +1,13 @@ +package winapi + +import "github.com/Microsoft/go-winio/pkg/guid" + +//sys CMGetDeviceIDListSize(pulLen *uint32, pszFilter *byte, uFlags uint32) (hr error) = cfgmgr32.CM_Get_Device_ID_List_SizeA +//sys CMGetDeviceIDList(pszFilter *byte, buffer *byte, bufferLen uint32, uFlags uint32) (hr error)= cfgmgr32.CM_Get_Device_ID_ListA +//sys CMLocateDevNode(pdnDevInst *uint32, pDeviceID string, uFlags uint32) (hr error) = cfgmgr32.CM_Locate_DevNodeW +//sys CMGetDevNodeProperty(dnDevInst uint32, propertyKey *DevPropKey, propertyType *uint32, propertyBuffer *uint16, propertyBufferSize *uint32, uFlags uint32) (hr error) = cfgmgr32.CM_Get_DevNode_PropertyW + +type DevPropKey struct { + Fmtid guid.GUID + Pid uint32 +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/errors.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/errors.go new file mode 100644 index 0000000000000..4e80ef68c92c5 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/errors.go @@ -0,0 +1,15 @@ +package winapi + +import "syscall" + +//sys RtlNtStatusToDosError(status uint32) (winerr error) = ntdll.RtlNtStatusToDosError + +const ( + STATUS_REPARSE_POINT_ENCOUNTERED = 0xC000050B + ERROR_NO_MORE_ITEMS = 0x103 + ERROR_MORE_DATA syscall.Errno = 234 +) + +func NTSuccess(status uint32) bool { + return status == 0 +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/filesystem.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/filesystem.go new file mode 100644 index 0000000000000..7ce52afd5e18a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/filesystem.go @@ -0,0 +1,110 @@ +package winapi + +//sys NtCreateFile(handle *uintptr, accessMask uint32, oa *ObjectAttributes, iosb *IOStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) = ntdll.NtCreateFile +//sys NtSetInformationFile(handle uintptr, iosb *IOStatusBlock, information uintptr, length uint32, class uint32) (status uint32) = ntdll.NtSetInformationFile + +//sys NtOpenDirectoryObject(handle *uintptr, accessMask uint32, oa *ObjectAttributes) (status uint32) = ntdll.NtOpenDirectoryObject +//sys NtQueryDirectoryObject(handle uintptr, buffer *byte, length uint32, singleEntry bool, restartScan bool, context *uint32, returnLength *uint32)(status uint32) = ntdll.NtQueryDirectoryObject + +const ( + FileLinkInformationClass = 11 + FileDispositionInformationExClass = 64 + + FILE_READ_ATTRIBUTES = 0x0080 + FILE_WRITE_ATTRIBUTES = 0x0100 + DELETE = 0x10000 + + FILE_OPEN = 1 + FILE_CREATE = 2 + + FILE_LIST_DIRECTORY = 0x00000001 + FILE_DIRECTORY_FILE = 0x00000001 + FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020 + FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000 + FILE_OPEN_REPARSE_POINT = 0x00200000 + + FILE_DISPOSITION_DELETE = 0x00000001 + + OBJ_DONT_REPARSE = 0x1000 + + STATUS_MORE_ENTRIES = 0x105 + STATUS_NO_MORE_ENTRIES = 0x8000001a +) + +// Select entries from FILE_INFO_BY_HANDLE_CLASS. +// +// C declaration: +// typedef enum _FILE_INFO_BY_HANDLE_CLASS { +// FileBasicInfo, +// FileStandardInfo, +// FileNameInfo, +// FileRenameInfo, +// FileDispositionInfo, +// FileAllocationInfo, +// FileEndOfFileInfo, +// FileStreamInfo, +// FileCompressionInfo, +// FileAttributeTagInfo, +// FileIdBothDirectoryInfo, +// FileIdBothDirectoryRestartInfo, +// FileIoPriorityHintInfo, +// FileRemoteProtocolInfo, +// FileFullDirectoryInfo, +// FileFullDirectoryRestartInfo, +// FileStorageInfo, +// FileAlignmentInfo, +// FileIdInfo, +// FileIdExtdDirectoryInfo, +// FileIdExtdDirectoryRestartInfo, +// FileDispositionInfoEx, +// FileRenameInfoEx, +// FileCaseSensitiveInfo, +// FileNormalizedNameInfo, +// MaximumFileInfoByHandleClass +// } FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS; +// +// Documentation: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ne-minwinbase-file_info_by_handle_class +const ( + FileIdInfo = 18 +) + +type FileDispositionInformationEx struct { + Flags uintptr +} + +type IOStatusBlock struct { + Status, Information uintptr +} + +type ObjectAttributes struct { + Length uintptr + RootDirectory uintptr + ObjectName *UnicodeString + Attributes uintptr + SecurityDescriptor uintptr + SecurityQoS uintptr +} + +type ObjectDirectoryInformation struct { + Name UnicodeString + TypeName UnicodeString +} + +type FileLinkInformation struct { + ReplaceIfExists bool + RootDirectory uintptr + FileNameLength uint32 + FileName [1]uint16 +} + +// C declaration: +// typedef struct _FILE_ID_INFO { +// ULONGLONG VolumeSerialNumber; +// FILE_ID_128 FileId; +// } FILE_ID_INFO, *PFILE_ID_INFO; +// +// Documentation: https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_info +type FILE_ID_INFO struct { + VolumeSerialNumber uint64 + FileID [16]byte +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/iocp.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/iocp.go new file mode 100644 index 0000000000000..4e609cbf1cdb5 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/iocp.go @@ -0,0 +1,3 @@ +package winapi + +//sys GetQueuedCompletionStatus(cphandle windows.Handle, qty *uint32, key *uintptr, overlapped **windows.Overlapped, timeout uint32) (err error) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/jobobject.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/jobobject.go new file mode 100644 index 0000000000000..ba12b1ad92e34 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/jobobject.go @@ -0,0 +1,215 @@ +package winapi + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +// Messages that can be received from an assigned io completion port. +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_associate_completion_port +const ( + JOB_OBJECT_MSG_END_OF_JOB_TIME uint32 = 1 + JOB_OBJECT_MSG_END_OF_PROCESS_TIME uint32 = 2 + JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT uint32 = 3 + JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO uint32 = 4 + JOB_OBJECT_MSG_NEW_PROCESS uint32 = 6 + JOB_OBJECT_MSG_EXIT_PROCESS uint32 = 7 + JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS uint32 = 8 + JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT uint32 = 9 + JOB_OBJECT_MSG_JOB_MEMORY_LIMIT uint32 = 10 + JOB_OBJECT_MSG_NOTIFICATION_LIMIT uint32 = 11 +) + +// Access rights for creating or opening job objects. +// +// https://docs.microsoft.com/en-us/windows/win32/procthread/job-object-security-and-access-rights +const JOB_OBJECT_ALL_ACCESS = 0x1F001F + +// IO limit flags +// +// https://docs.microsoft.com/en-us/windows/win32/api/jobapi2/ns-jobapi2-jobobject_io_rate_control_information +const JOB_OBJECT_IO_RATE_CONTROL_ENABLE = 0x1 + +const JOBOBJECT_IO_ATTRIBUTION_CONTROL_ENABLE uint32 = 0x1 + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_cpu_rate_control_information +const ( + JOB_OBJECT_CPU_RATE_CONTROL_ENABLE uint32 = 1 << iota + JOB_OBJECT_CPU_RATE_CONTROL_WEIGHT_BASED + JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP + JOB_OBJECT_CPU_RATE_CONTROL_NOTIFY + JOB_OBJECT_CPU_RATE_CONTROL_MIN_MAX_RATE +) + +// JobObjectInformationClass values. Used for a call to QueryInformationJobObject +// +// https://docs.microsoft.com/en-us/windows/win32/api/jobapi2/nf-jobapi2-queryinformationjobobject +const ( + JobObjectBasicAccountingInformation uint32 = 1 + JobObjectBasicProcessIdList uint32 = 3 + JobObjectBasicAndIoAccountingInformation uint32 = 8 + JobObjectLimitViolationInformation uint32 = 13 + JobObjectMemoryUsageInformation uint32 = 28 + JobObjectNotificationLimitInformation2 uint32 = 33 + JobObjectIoAttribution uint32 = 42 +) + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_basic_limit_information +type JOBOBJECT_BASIC_LIMIT_INFORMATION struct { + PerProcessUserTimeLimit int64 + PerJobUserTimeLimit int64 + LimitFlags uint32 + MinimumWorkingSetSize uintptr + MaximumWorkingSetSize uintptr + ActiveProcessLimit uint32 + Affinity uintptr + PriorityClass uint32 + SchedulingClass uint32 +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_cpu_rate_control_information +type JOBOBJECT_CPU_RATE_CONTROL_INFORMATION struct { + ControlFlags uint32 + Value uint32 +} + +// https://docs.microsoft.com/en-us/windows/win32/api/jobapi2/ns-jobapi2-jobobject_io_rate_control_information +type JOBOBJECT_IO_RATE_CONTROL_INFORMATION struct { + MaxIops int64 + MaxBandwidth int64 + ReservationIops int64 + BaseIOSize uint32 + VolumeName string + ControlFlags uint32 +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_basic_process_id_list +type JOBOBJECT_BASIC_PROCESS_ID_LIST struct { + NumberOfAssignedProcesses uint32 + NumberOfProcessIdsInList uint32 + ProcessIdList [1]uintptr +} + +// AllPids returns all the process Ids in the job object. +func (p *JOBOBJECT_BASIC_PROCESS_ID_LIST) AllPids() []uintptr { + return (*[(1 << 27) - 1]uintptr)(unsafe.Pointer(&p.ProcessIdList[0]))[:p.NumberOfProcessIdsInList] +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_basic_accounting_information +type JOBOBJECT_BASIC_ACCOUNTING_INFORMATION struct { + TotalUserTime int64 + TotalKernelTime int64 + ThisPeriodTotalUserTime int64 + ThisPeriodTotalKernelTime int64 + TotalPageFaultCount uint32 + TotalProcesses uint32 + ActiveProcesses uint32 + TotalTerminateProcesses uint32 +} + +//https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_basic_and_io_accounting_information +type JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION struct { + BasicInfo JOBOBJECT_BASIC_ACCOUNTING_INFORMATION + IoInfo windows.IO_COUNTERS +} + +// typedef struct _JOBOBJECT_MEMORY_USAGE_INFORMATION { +// ULONG64 JobMemory; +// ULONG64 PeakJobMemoryUsed; +// } JOBOBJECT_MEMORY_USAGE_INFORMATION, *PJOBOBJECT_MEMORY_USAGE_INFORMATION; +// +type JOBOBJECT_MEMORY_USAGE_INFORMATION struct { + JobMemory uint64 + PeakJobMemoryUsed uint64 +} + +// typedef struct _JOBOBJECT_IO_ATTRIBUTION_STATS { +// ULONG_PTR IoCount; +// ULONGLONG TotalNonOverlappedQueueTime; +// ULONGLONG TotalNonOverlappedServiceTime; +// ULONGLONG TotalSize; +// } JOBOBJECT_IO_ATTRIBUTION_STATS, *PJOBOBJECT_IO_ATTRIBUTION_STATS; +// +type JOBOBJECT_IO_ATTRIBUTION_STATS struct { + IoCount uintptr + TotalNonOverlappedQueueTime uint64 + TotalNonOverlappedServiceTime uint64 + TotalSize uint64 +} + +// typedef struct _JOBOBJECT_IO_ATTRIBUTION_INFORMATION { +// ULONG ControlFlags; +// JOBOBJECT_IO_ATTRIBUTION_STATS ReadStats; +// JOBOBJECT_IO_ATTRIBUTION_STATS WriteStats; +// } JOBOBJECT_IO_ATTRIBUTION_INFORMATION, *PJOBOBJECT_IO_ATTRIBUTION_INFORMATION; +// +type JOBOBJECT_IO_ATTRIBUTION_INFORMATION struct { + ControlFlags uint32 + ReadStats JOBOBJECT_IO_ATTRIBUTION_STATS + WriteStats JOBOBJECT_IO_ATTRIBUTION_STATS +} + +// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_associate_completion_port +type JOBOBJECT_ASSOCIATE_COMPLETION_PORT struct { + CompletionKey windows.Handle + CompletionPort windows.Handle +} + +// BOOL IsProcessInJob( +// HANDLE ProcessHandle, +// HANDLE JobHandle, +// PBOOL Result +// ); +// +//sys IsProcessInJob(procHandle windows.Handle, jobHandle windows.Handle, result *bool) (err error) = kernel32.IsProcessInJob + +// BOOL QueryInformationJobObject( +// HANDLE hJob, +// JOBOBJECTINFOCLASS JobObjectInformationClass, +// LPVOID lpJobObjectInformation, +// DWORD cbJobObjectInformationLength, +// LPDWORD lpReturnLength +// ); +// +//sys QueryInformationJobObject(jobHandle windows.Handle, infoClass uint32, jobObjectInfo uintptr, jobObjectInformationLength uint32, lpReturnLength *uint32) (err error) = kernel32.QueryInformationJobObject + +// HANDLE OpenJobObjectW( +// DWORD dwDesiredAccess, +// BOOL bInheritHandle, +// LPCWSTR lpName +// ); +// +//sys OpenJobObject(desiredAccess uint32, inheritHandle bool, lpName *uint16) (handle windows.Handle, err error) = kernel32.OpenJobObjectW + +// DWORD SetIoRateControlInformationJobObject( +// HANDLE hJob, +// JOBOBJECT_IO_RATE_CONTROL_INFORMATION *IoRateControlInfo +// ); +// +//sys SetIoRateControlInformationJobObject(jobHandle windows.Handle, ioRateControlInfo *JOBOBJECT_IO_RATE_CONTROL_INFORMATION) (ret uint32, err error) = kernel32.SetIoRateControlInformationJobObject + +// DWORD QueryIoRateControlInformationJobObject( +// HANDLE hJob, +// PCWSTR VolumeName, +// JOBOBJECT_IO_RATE_CONTROL_INFORMATION **InfoBlocks, +// ULONG *InfoBlockCount +// ); +//sys QueryIoRateControlInformationJobObject(jobHandle windows.Handle, volumeName *uint16, ioRateControlInfo **JOBOBJECT_IO_RATE_CONTROL_INFORMATION, infoBlockCount *uint32) (ret uint32, err error) = kernel32.QueryIoRateControlInformationJobObject + +// NTSTATUS +// NtOpenJobObject ( +// _Out_ PHANDLE JobHandle, +// _In_ ACCESS_MASK DesiredAccess, +// _In_ POBJECT_ATTRIBUTES ObjectAttributes +// ); +//sys NtOpenJobObject(jobHandle *windows.Handle, desiredAccess uint32, objAttributes *ObjectAttributes) (status uint32) = ntdll.NtOpenJobObject + +// NTSTATUS +// NTAPI +// NtCreateJobObject ( +// _Out_ PHANDLE JobHandle, +// _In_ ACCESS_MASK DesiredAccess, +// _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes +// ); +//sys NtCreateJobObject(jobHandle *windows.Handle, desiredAccess uint32, objAttributes *ObjectAttributes) (status uint32) = ntdll.NtCreateJobObject diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/logon.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/logon.go new file mode 100644 index 0000000000000..b6e7cfd4601d2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/logon.go @@ -0,0 +1,30 @@ +package winapi + +// BOOL LogonUserA( +// LPCWSTR lpszUsername, +// LPCWSTR lpszDomain, +// LPCWSTR lpszPassword, +// DWORD dwLogonType, +// DWORD dwLogonProvider, +// PHANDLE phToken +// ); +// +//sys LogonUser(username *uint16, domain *uint16, password *uint16, logonType uint32, logonProvider uint32, token *windows.Token) (err error) = advapi32.LogonUserW + +// Logon types +const ( + LOGON32_LOGON_INTERACTIVE uint32 = 2 + LOGON32_LOGON_NETWORK uint32 = 3 + LOGON32_LOGON_BATCH uint32 = 4 + LOGON32_LOGON_SERVICE uint32 = 5 + LOGON32_LOGON_UNLOCK uint32 = 7 + LOGON32_LOGON_NETWORK_CLEARTEXT uint32 = 8 + LOGON32_LOGON_NEW_CREDENTIALS uint32 = 9 +) + +// Logon providers +const ( + LOGON32_PROVIDER_DEFAULT uint32 = 0 + LOGON32_PROVIDER_WINNT40 uint32 = 2 + LOGON32_PROVIDER_WINNT50 uint32 = 3 +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go new file mode 100644 index 0000000000000..83f7040644655 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go @@ -0,0 +1,27 @@ +package winapi + +// VOID RtlMoveMemory( +// _Out_ VOID UNALIGNED *Destination, +// _In_ const VOID UNALIGNED *Source, +// _In_ SIZE_T Length +// ); +//sys RtlMoveMemory(destination *byte, source *byte, length uintptr) (err error) = kernel32.RtlMoveMemory + +//sys LocalAlloc(flags uint32, size int) (ptr uintptr) = kernel32.LocalAlloc +//sys LocalFree(ptr uintptr) = kernel32.LocalFree + +// BOOL QueryWorkingSet( +// HANDLE hProcess, +// PVOID pv, +// DWORD cb +// ); +//sys QueryWorkingSet(handle windows.Handle, pv uintptr, cb uint32) (err error) = psapi.QueryWorkingSet + +type PSAPI_WORKING_SET_INFORMATION struct { + NumberOfEntries uintptr + WorkingSetInfo [1]PSAPI_WORKING_SET_BLOCK +} + +type PSAPI_WORKING_SET_BLOCK struct { + Flags uintptr +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/net.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/net.go new file mode 100644 index 0000000000000..f37910024f72c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/net.go @@ -0,0 +1,3 @@ +package winapi + +//sys SetJobCompartmentId(handle windows.Handle, compartmentId uint32) (win32Err error) = iphlpapi.SetJobCompartmentId diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/path.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/path.go new file mode 100644 index 0000000000000..908920e8722a4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/path.go @@ -0,0 +1,11 @@ +package winapi + +// DWORD SearchPathW( +// LPCWSTR lpPath, +// LPCWSTR lpFileName, +// LPCWSTR lpExtension, +// DWORD nBufferLength, +// LPWSTR lpBuffer, +// LPWSTR *lpFilePart +// ); +//sys SearchPath(lpPath *uint16, lpFileName *uint16, lpExtension *uint16, nBufferLength uint32, lpBuffer *uint16, lpFilePath *uint16) (size uint32, err error) = kernel32.SearchPathW diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go new file mode 100644 index 0000000000000..b87068327ccde --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/process.go @@ -0,0 +1,10 @@ +package winapi + +const PROCESS_ALL_ACCESS uint32 = 2097151 + +// DWORD GetProcessImageFileNameW( +// HANDLE hProcess, +// LPWSTR lpImageFileName, +// DWORD nSize +// ); +//sys GetProcessImageFileName(hProcess windows.Handle, imageFileName *uint16, nSize uint32) (size uint32, err error) = kernel32.GetProcessImageFileNameW diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/processor.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/processor.go new file mode 100644 index 0000000000000..ce79ac2cdb8ad --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/processor.go @@ -0,0 +1,7 @@ +package winapi + +// Get count from all processor groups. +// https://docs.microsoft.com/en-us/windows/win32/procthread/processor-groups +const ALL_PROCESSOR_GROUPS = 0xFFFF + +//sys GetActiveProcessorCount(groupNumber uint16) (amount uint32) = kernel32.GetActiveProcessorCount diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/system.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/system.go new file mode 100644 index 0000000000000..327f57d7c296c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/system.go @@ -0,0 +1,52 @@ +package winapi + +import "golang.org/x/sys/windows" + +const SystemProcessInformation = 5 + +const STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 + +// __kernel_entry NTSTATUS NtQuerySystemInformation( +// SYSTEM_INFORMATION_CLASS SystemInformationClass, +// PVOID SystemInformation, +// ULONG SystemInformationLength, +// PULONG ReturnLength +// ); +//sys NtQuerySystemInformation(systemInfoClass int, systemInformation uintptr, systemInfoLength uint32, returnLength *uint32) (status uint32) = ntdll.NtQuerySystemInformation + +type SYSTEM_PROCESS_INFORMATION struct { + NextEntryOffset uint32 // ULONG + NumberOfThreads uint32 // ULONG + WorkingSetPrivateSize int64 // LARGE_INTEGER + HardFaultCount uint32 // ULONG + NumberOfThreadsHighWatermark uint32 // ULONG + CycleTime uint64 // ULONGLONG + CreateTime int64 // LARGE_INTEGER + UserTime int64 // LARGE_INTEGER + KernelTime int64 // LARGE_INTEGER + ImageName UnicodeString // UNICODE_STRING + BasePriority int32 // KPRIORITY + UniqueProcessID windows.Handle // HANDLE + InheritedFromUniqueProcessID windows.Handle // HANDLE + HandleCount uint32 // ULONG + SessionID uint32 // ULONG + UniqueProcessKey *uint32 // ULONG_PTR + PeakVirtualSize uintptr // SIZE_T + VirtualSize uintptr // SIZE_T + PageFaultCount uint32 // ULONG + PeakWorkingSetSize uintptr // SIZE_T + WorkingSetSize uintptr // SIZE_T + QuotaPeakPagedPoolUsage uintptr // SIZE_T + QuotaPagedPoolUsage uintptr // SIZE_T + QuotaPeakNonPagedPoolUsage uintptr // SIZE_T + QuotaNonPagedPoolUsage uintptr // SIZE_T + PagefileUsage uintptr // SIZE_T + PeakPagefileUsage uintptr // SIZE_T + PrivatePageCount uintptr // SIZE_T + ReadOperationCount int64 // LARGE_INTEGER + WriteOperationCount int64 // LARGE_INTEGER + OtherOperationCount int64 // LARGE_INTEGER + ReadTransferCount int64 // LARGE_INTEGER + WriteTransferCount int64 // LARGE_INTEGER + OtherTransferCount int64 // LARGE_INTEGER +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/thread.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/thread.go new file mode 100644 index 0000000000000..4724713e3e41c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/thread.go @@ -0,0 +1,12 @@ +package winapi + +// HANDLE CreateRemoteThread( +// HANDLE hProcess, +// LPSECURITY_ATTRIBUTES lpThreadAttributes, +// SIZE_T dwStackSize, +// LPTHREAD_START_ROUTINE lpStartAddress, +// LPVOID lpParameter, +// DWORD dwCreationFlags, +// LPDWORD lpThreadId +// ); +//sys CreateRemoteThread(process windows.Handle, sa *windows.SecurityAttributes, stackSize uint32, startAddr uintptr, parameter uintptr, creationFlags uint32, threadID *uint32) (handle windows.Handle, err error) = kernel32.CreateRemoteThread diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go new file mode 100644 index 0000000000000..db59567d08952 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go @@ -0,0 +1,75 @@ +package winapi + +import ( + "errors" + "reflect" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +// Uint16BufferToSlice wraps a uint16 pointer-and-length into a slice +// for easier interop with Go APIs +func Uint16BufferToSlice(buffer *uint16, bufferLength int) (result []uint16) { + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&result)) + hdr.Data = uintptr(unsafe.Pointer(buffer)) + hdr.Cap = bufferLength + hdr.Len = bufferLength + + return +} + +type UnicodeString struct { + Length uint16 + MaximumLength uint16 + Buffer *uint16 +} + +//String converts a UnicodeString to a golang string +func (uni UnicodeString) String() string { + // UnicodeString is not guaranteed to be null terminated, therefore + // use the UnicodeString's Length field + return syscall.UTF16ToString(Uint16BufferToSlice(uni.Buffer, int(uni.Length/2))) +} + +// NewUnicodeString allocates a new UnicodeString and copies `s` into +// the buffer of the new UnicodeString. +func NewUnicodeString(s string) (*UnicodeString, error) { + // Get length of original `s` to use in the UnicodeString since the `buf` + // created later will have an additional trailing null character + length := len(s) + if length > 32767 { + return nil, syscall.ENAMETOOLONG + } + + buf, err := windows.UTF16FromString(s) + if err != nil { + return nil, err + } + uni := &UnicodeString{ + Length: uint16(length * 2), + MaximumLength: uint16(length * 2), + Buffer: &buf[0], + } + return uni, nil +} + +// ConvertStringSetToSlice is a helper function used to convert the contents of +// `buf` into a string slice. `buf` contains a set of null terminated strings +// with an additional null at the end to indicate the end of the set. +func ConvertStringSetToSlice(buf []byte) ([]string, error) { + var results []string + prev := 0 + for i := range buf { + if buf[i] == 0 { + if prev == i { + // found two null characters in a row, return result + return results, nil + } + results = append(results, string(buf[prev:i])) + prev = i + 1 + } + } + return nil, errors.New("string set malformed: missing null terminator at end of buffer") +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go new file mode 100644 index 0000000000000..ec88c0d2128b6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/winapi.go @@ -0,0 +1,5 @@ +// Package winapi contains various low-level bindings to Windows APIs. It can +// be thought of as an extension to golang.org/x/sys/windows. +package winapi + +//go:generate go run ..\..\mksyscall_windows.go -output zsyscall_windows.go system.go net.go path.go thread.go iocp.go jobobject.go logon.go memory.go process.go processor.go devices.go filesystem.go errors.go diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go new file mode 100644 index 0000000000000..2941b0f9809ce --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go @@ -0,0 +1,371 @@ +// Code generated mksyscall_windows.exe DO NOT EDIT + +package winapi + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modntdll = windows.NewLazySystemDLL("ntdll.dll") + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + modpsapi = windows.NewLazySystemDLL("psapi.dll") + modcfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.dll") + + procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation") + procSetJobCompartmentId = modiphlpapi.NewProc("SetJobCompartmentId") + procSearchPathW = modkernel32.NewProc("SearchPathW") + procCreateRemoteThread = modkernel32.NewProc("CreateRemoteThread") + procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") + procIsProcessInJob = modkernel32.NewProc("IsProcessInJob") + procQueryInformationJobObject = modkernel32.NewProc("QueryInformationJobObject") + procOpenJobObjectW = modkernel32.NewProc("OpenJobObjectW") + procSetIoRateControlInformationJobObject = modkernel32.NewProc("SetIoRateControlInformationJobObject") + procQueryIoRateControlInformationJobObject = modkernel32.NewProc("QueryIoRateControlInformationJobObject") + procNtOpenJobObject = modntdll.NewProc("NtOpenJobObject") + procNtCreateJobObject = modntdll.NewProc("NtCreateJobObject") + procLogonUserW = modadvapi32.NewProc("LogonUserW") + procRtlMoveMemory = modkernel32.NewProc("RtlMoveMemory") + procLocalAlloc = modkernel32.NewProc("LocalAlloc") + procLocalFree = modkernel32.NewProc("LocalFree") + procQueryWorkingSet = modpsapi.NewProc("QueryWorkingSet") + procGetProcessImageFileNameW = modkernel32.NewProc("GetProcessImageFileNameW") + procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount") + procCM_Get_Device_ID_List_SizeA = modcfgmgr32.NewProc("CM_Get_Device_ID_List_SizeA") + procCM_Get_Device_ID_ListA = modcfgmgr32.NewProc("CM_Get_Device_ID_ListA") + procCM_Locate_DevNodeW = modcfgmgr32.NewProc("CM_Locate_DevNodeW") + procCM_Get_DevNode_PropertyW = modcfgmgr32.NewProc("CM_Get_DevNode_PropertyW") + procNtCreateFile = modntdll.NewProc("NtCreateFile") + procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile") + procNtOpenDirectoryObject = modntdll.NewProc("NtOpenDirectoryObject") + procNtQueryDirectoryObject = modntdll.NewProc("NtQueryDirectoryObject") + procRtlNtStatusToDosError = modntdll.NewProc("RtlNtStatusToDosError") +) + +func NtQuerySystemInformation(systemInfoClass int, systemInformation uintptr, systemInfoLength uint32, returnLength *uint32) (status uint32) { + r0, _, _ := syscall.Syscall6(procNtQuerySystemInformation.Addr(), 4, uintptr(systemInfoClass), uintptr(systemInformation), uintptr(systemInfoLength), uintptr(unsafe.Pointer(returnLength)), 0, 0) + status = uint32(r0) + return +} + +func SetJobCompartmentId(handle windows.Handle, compartmentId uint32) (win32Err error) { + r0, _, _ := syscall.Syscall(procSetJobCompartmentId.Addr(), 2, uintptr(handle), uintptr(compartmentId), 0) + if r0 != 0 { + win32Err = syscall.Errno(r0) + } + return +} + +func SearchPath(lpPath *uint16, lpFileName *uint16, lpExtension *uint16, nBufferLength uint32, lpBuffer *uint16, lpFilePath *uint16) (size uint32, err error) { + r0, _, e1 := syscall.Syscall6(procSearchPathW.Addr(), 6, uintptr(unsafe.Pointer(lpPath)), uintptr(unsafe.Pointer(lpFileName)), uintptr(unsafe.Pointer(lpExtension)), uintptr(nBufferLength), uintptr(unsafe.Pointer(lpBuffer)), uintptr(unsafe.Pointer(lpFilePath))) + size = uint32(r0) + if size == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func CreateRemoteThread(process windows.Handle, sa *windows.SecurityAttributes, stackSize uint32, startAddr uintptr, parameter uintptr, creationFlags uint32, threadID *uint32) (handle windows.Handle, err error) { + r0, _, e1 := syscall.Syscall9(procCreateRemoteThread.Addr(), 7, uintptr(process), uintptr(unsafe.Pointer(sa)), uintptr(stackSize), uintptr(startAddr), uintptr(parameter), uintptr(creationFlags), uintptr(unsafe.Pointer(threadID)), 0, 0) + handle = windows.Handle(r0) + if handle == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func GetQueuedCompletionStatus(cphandle windows.Handle, qty *uint32, key *uintptr, overlapped **windows.Overlapped, timeout uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func IsProcessInJob(procHandle windows.Handle, jobHandle windows.Handle, result *bool) (err error) { + r1, _, e1 := syscall.Syscall(procIsProcessInJob.Addr(), 3, uintptr(procHandle), uintptr(jobHandle), uintptr(unsafe.Pointer(result))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func QueryInformationJobObject(jobHandle windows.Handle, infoClass uint32, jobObjectInfo uintptr, jobObjectInformationLength uint32, lpReturnLength *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procQueryInformationJobObject.Addr(), 5, uintptr(jobHandle), uintptr(infoClass), uintptr(jobObjectInfo), uintptr(jobObjectInformationLength), uintptr(unsafe.Pointer(lpReturnLength)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func OpenJobObject(desiredAccess uint32, inheritHandle bool, lpName *uint16) (handle windows.Handle, err error) { + var _p0 uint32 + if inheritHandle { + _p0 = 1 + } else { + _p0 = 0 + } + r0, _, e1 := syscall.Syscall(procOpenJobObjectW.Addr(), 3, uintptr(desiredAccess), uintptr(_p0), uintptr(unsafe.Pointer(lpName))) + handle = windows.Handle(r0) + if handle == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func SetIoRateControlInformationJobObject(jobHandle windows.Handle, ioRateControlInfo *JOBOBJECT_IO_RATE_CONTROL_INFORMATION) (ret uint32, err error) { + r0, _, e1 := syscall.Syscall(procSetIoRateControlInformationJobObject.Addr(), 2, uintptr(jobHandle), uintptr(unsafe.Pointer(ioRateControlInfo)), 0) + ret = uint32(r0) + if ret == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func QueryIoRateControlInformationJobObject(jobHandle windows.Handle, volumeName *uint16, ioRateControlInfo **JOBOBJECT_IO_RATE_CONTROL_INFORMATION, infoBlockCount *uint32) (ret uint32, err error) { + r0, _, e1 := syscall.Syscall6(procQueryIoRateControlInformationJobObject.Addr(), 4, uintptr(jobHandle), uintptr(unsafe.Pointer(volumeName)), uintptr(unsafe.Pointer(ioRateControlInfo)), uintptr(unsafe.Pointer(infoBlockCount)), 0, 0) + ret = uint32(r0) + if ret == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func NtOpenJobObject(jobHandle *windows.Handle, desiredAccess uint32, objAttributes *ObjectAttributes) (status uint32) { + r0, _, _ := syscall.Syscall(procNtOpenJobObject.Addr(), 3, uintptr(unsafe.Pointer(jobHandle)), uintptr(desiredAccess), uintptr(unsafe.Pointer(objAttributes))) + status = uint32(r0) + return +} + +func NtCreateJobObject(jobHandle *windows.Handle, desiredAccess uint32, objAttributes *ObjectAttributes) (status uint32) { + r0, _, _ := syscall.Syscall(procNtCreateJobObject.Addr(), 3, uintptr(unsafe.Pointer(jobHandle)), uintptr(desiredAccess), uintptr(unsafe.Pointer(objAttributes))) + status = uint32(r0) + return +} + +func LogonUser(username *uint16, domain *uint16, password *uint16, logonType uint32, logonProvider uint32, token *windows.Token) (err error) { + r1, _, e1 := syscall.Syscall6(procLogonUserW.Addr(), 6, uintptr(unsafe.Pointer(username)), uintptr(unsafe.Pointer(domain)), uintptr(unsafe.Pointer(password)), uintptr(logonType), uintptr(logonProvider), uintptr(unsafe.Pointer(token))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func RtlMoveMemory(destination *byte, source *byte, length uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procRtlMoveMemory.Addr(), 3, uintptr(unsafe.Pointer(destination)), uintptr(unsafe.Pointer(source)), uintptr(length)) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func LocalAlloc(flags uint32, size int) (ptr uintptr) { + r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(size), 0) + ptr = uintptr(r0) + return +} + +func LocalFree(ptr uintptr) { + syscall.Syscall(procLocalFree.Addr(), 1, uintptr(ptr), 0, 0) + return +} + +func QueryWorkingSet(handle windows.Handle, pv uintptr, cb uint32) (err error) { + r1, _, e1 := syscall.Syscall(procQueryWorkingSet.Addr(), 3, uintptr(handle), uintptr(pv), uintptr(cb)) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func GetProcessImageFileName(hProcess windows.Handle, imageFileName *uint16, nSize uint32) (size uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetProcessImageFileNameW.Addr(), 3, uintptr(hProcess), uintptr(unsafe.Pointer(imageFileName)), uintptr(nSize)) + size = uint32(r0) + if size == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func GetActiveProcessorCount(groupNumber uint16) (amount uint32) { + r0, _, _ := syscall.Syscall(procGetActiveProcessorCount.Addr(), 1, uintptr(groupNumber), 0, 0) + amount = uint32(r0) + return +} + +func CMGetDeviceIDListSize(pulLen *uint32, pszFilter *byte, uFlags uint32) (hr error) { + r0, _, _ := syscall.Syscall(procCM_Get_Device_ID_List_SizeA.Addr(), 3, uintptr(unsafe.Pointer(pulLen)), uintptr(unsafe.Pointer(pszFilter)), uintptr(uFlags)) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func CMGetDeviceIDList(pszFilter *byte, buffer *byte, bufferLen uint32, uFlags uint32) (hr error) { + r0, _, _ := syscall.Syscall6(procCM_Get_Device_ID_ListA.Addr(), 4, uintptr(unsafe.Pointer(pszFilter)), uintptr(unsafe.Pointer(buffer)), uintptr(bufferLen), uintptr(uFlags), 0, 0) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func CMLocateDevNode(pdnDevInst *uint32, pDeviceID string, uFlags uint32) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(pDeviceID) + if hr != nil { + return + } + return _CMLocateDevNode(pdnDevInst, _p0, uFlags) +} + +func _CMLocateDevNode(pdnDevInst *uint32, pDeviceID *uint16, uFlags uint32) (hr error) { + r0, _, _ := syscall.Syscall(procCM_Locate_DevNodeW.Addr(), 3, uintptr(unsafe.Pointer(pdnDevInst)), uintptr(unsafe.Pointer(pDeviceID)), uintptr(uFlags)) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func CMGetDevNodeProperty(dnDevInst uint32, propertyKey *DevPropKey, propertyType *uint32, propertyBuffer *uint16, propertyBufferSize *uint32, uFlags uint32) (hr error) { + r0, _, _ := syscall.Syscall6(procCM_Get_DevNode_PropertyW.Addr(), 6, uintptr(dnDevInst), uintptr(unsafe.Pointer(propertyKey)), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(unsafe.Pointer(propertyBufferSize)), uintptr(uFlags)) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func NtCreateFile(handle *uintptr, accessMask uint32, oa *ObjectAttributes, iosb *IOStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) { + r0, _, _ := syscall.Syscall12(procNtCreateFile.Addr(), 11, uintptr(unsafe.Pointer(handle)), uintptr(accessMask), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(allocationSize)), uintptr(fileAttributes), uintptr(shareAccess), uintptr(createDisposition), uintptr(createOptions), uintptr(unsafe.Pointer(eaBuffer)), uintptr(eaLength), 0) + status = uint32(r0) + return +} + +func NtSetInformationFile(handle uintptr, iosb *IOStatusBlock, information uintptr, length uint32, class uint32) (status uint32) { + r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(information), uintptr(length), uintptr(class), 0) + status = uint32(r0) + return +} + +func NtOpenDirectoryObject(handle *uintptr, accessMask uint32, oa *ObjectAttributes) (status uint32) { + r0, _, _ := syscall.Syscall(procNtOpenDirectoryObject.Addr(), 3, uintptr(unsafe.Pointer(handle)), uintptr(accessMask), uintptr(unsafe.Pointer(oa))) + status = uint32(r0) + return +} + +func NtQueryDirectoryObject(handle uintptr, buffer *byte, length uint32, singleEntry bool, restartScan bool, context *uint32, returnLength *uint32) (status uint32) { + var _p0 uint32 + if singleEntry { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if restartScan { + _p1 = 1 + } else { + _p1 = 0 + } + r0, _, _ := syscall.Syscall9(procNtQueryDirectoryObject.Addr(), 7, uintptr(handle), uintptr(unsafe.Pointer(buffer)), uintptr(length), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(context)), uintptr(unsafe.Pointer(returnLength)), 0, 0) + status = uint32(r0) + return +} + +func RtlNtStatusToDosError(status uint32) (winerr error) { + r0, _, _ := syscall.Syscall(procRtlNtStatusToDosError.Addr(), 1, uintptr(status), 0, 0) + if r0 != 0 { + winerr = syscall.Errno(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/layer.go b/vendor/github.com/Microsoft/hcsshim/layer.go index 8cdc247dcdb51..8916163706cfe 100644 --- a/vendor/github.com/Microsoft/hcsshim/layer.go +++ b/vendor/github.com/Microsoft/hcsshim/layer.go @@ -1,11 +1,11 @@ package hcsshim import ( + "context" "crypto/sha1" "path/filepath" - "github.com/Microsoft/hcsshim/internal/guid" - + "github.com/Microsoft/go-winio/pkg/guid" "github.com/Microsoft/hcsshim/internal/wclayer" ) @@ -14,57 +14,59 @@ func layerPath(info *DriverInfo, id string) string { } func ActivateLayer(info DriverInfo, id string) error { - return wclayer.ActivateLayer(layerPath(&info, id)) + return wclayer.ActivateLayer(context.Background(), layerPath(&info, id)) } func CreateLayer(info DriverInfo, id, parent string) error { - return wclayer.CreateLayer(layerPath(&info, id), parent) + return wclayer.CreateLayer(context.Background(), layerPath(&info, id), parent) } + // New clients should use CreateScratchLayer instead. Kept in to preserve API compatibility. func CreateSandboxLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error { - return wclayer.CreateScratchLayer(layerPath(&info, layerId), parentLayerPaths) + return wclayer.CreateScratchLayer(context.Background(), layerPath(&info, layerId), parentLayerPaths) } func CreateScratchLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error { - return wclayer.CreateScratchLayer(layerPath(&info, layerId), parentLayerPaths) + return wclayer.CreateScratchLayer(context.Background(), layerPath(&info, layerId), parentLayerPaths) } func DeactivateLayer(info DriverInfo, id string) error { - return wclayer.DeactivateLayer(layerPath(&info, id)) + return wclayer.DeactivateLayer(context.Background(), layerPath(&info, id)) } func DestroyLayer(info DriverInfo, id string) error { - return wclayer.DestroyLayer(layerPath(&info, id)) + return wclayer.DestroyLayer(context.Background(), layerPath(&info, id)) } + // New clients should use ExpandScratchSize instead. Kept in to preserve API compatibility. func ExpandSandboxSize(info DriverInfo, layerId string, size uint64) error { - return wclayer.ExpandScratchSize(layerPath(&info, layerId), size) + return wclayer.ExpandScratchSize(context.Background(), layerPath(&info, layerId), size) } func ExpandScratchSize(info DriverInfo, layerId string, size uint64) error { - return wclayer.ExpandScratchSize(layerPath(&info, layerId), size) + return wclayer.ExpandScratchSize(context.Background(), layerPath(&info, layerId), size) } func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, parentLayerPaths []string) error { - return wclayer.ExportLayer(layerPath(&info, layerId), exportFolderPath, parentLayerPaths) + return wclayer.ExportLayer(context.Background(), layerPath(&info, layerId), exportFolderPath, parentLayerPaths) } func GetLayerMountPath(info DriverInfo, id string) (string, error) { - return wclayer.GetLayerMountPath(layerPath(&info, id)) + return wclayer.GetLayerMountPath(context.Background(), layerPath(&info, id)) } func GetSharedBaseImages() (imageData string, err error) { - return wclayer.GetSharedBaseImages() + return wclayer.GetSharedBaseImages(context.Background()) } func ImportLayer(info DriverInfo, layerID string, importFolderPath string, parentLayerPaths []string) error { - return wclayer.ImportLayer(layerPath(&info, layerID), importFolderPath, parentLayerPaths) + return wclayer.ImportLayer(context.Background(), layerPath(&info, layerID), importFolderPath, parentLayerPaths) } func LayerExists(info DriverInfo, id string) (bool, error) { - return wclayer.LayerExists(layerPath(&info, id)) + return wclayer.LayerExists(context.Background(), layerPath(&info, id)) } func PrepareLayer(info DriverInfo, layerId string, parentLayerPaths []string) error { - return wclayer.PrepareLayer(layerPath(&info, layerId), parentLayerPaths) + return wclayer.PrepareLayer(context.Background(), layerPath(&info, layerId), parentLayerPaths) } func ProcessBaseLayer(path string) error { - return wclayer.ProcessBaseLayer(path) + return wclayer.ProcessBaseLayer(context.Background(), path) } func ProcessUtilityVMImage(path string) error { - return wclayer.ProcessUtilityVMImage(path) + return wclayer.ProcessUtilityVMImage(context.Background(), path) } func UnprepareLayer(info DriverInfo, layerId string) error { - return wclayer.UnprepareLayer(layerPath(&info, layerId)) + return wclayer.UnprepareLayer(context.Background(), layerPath(&info, layerId)) } type DriverInfo struct { @@ -72,14 +74,11 @@ type DriverInfo struct { HomeDir string } -type FilterLayerReader = wclayer.FilterLayerReader -type FilterLayerWriter = wclayer.FilterLayerWriter - type GUID [16]byte func NameToGuid(name string) (id GUID, err error) { - g, err := wclayer.NameToGuid(name) - return GUID(g), err + g, err := wclayer.NameToGuid(context.Background(), name) + return g.ToWindowsArray(), err } func NewGUID(source string) *GUID { @@ -90,19 +89,19 @@ func NewGUID(source string) *GUID { } func (g *GUID) ToString() string { - return (guid.GUID)(*g).String() + return guid.FromWindowsArray(*g).String() } type LayerReader = wclayer.LayerReader func NewLayerReader(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error) { - return wclayer.NewLayerReader(layerPath(&info, layerID), parentLayerPaths) + return wclayer.NewLayerReader(context.Background(), layerPath(&info, layerID), parentLayerPaths) } type LayerWriter = wclayer.LayerWriter func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) { - return wclayer.NewLayerWriter(layerPath(&info, layerID), parentLayerPaths) + return wclayer.NewLayerWriter(context.Background(), layerPath(&info, layerID), parentLayerPaths) } type WC_LAYER_DESCRIPTOR = wclayer.WC_LAYER_DESCRIPTOR diff --git a/vendor/github.com/Microsoft/hcsshim/osversion/osversion_windows.go b/vendor/github.com/Microsoft/hcsshim/osversion/osversion_windows.go new file mode 100644 index 0000000000000..3ab3bcd89a118 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/osversion/osversion_windows.go @@ -0,0 +1,50 @@ +package osversion + +import ( + "fmt" + "sync" + + "golang.org/x/sys/windows" +) + +// OSVersion is a wrapper for Windows version information +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx +type OSVersion struct { + Version uint32 + MajorVersion uint8 + MinorVersion uint8 + Build uint16 +} + +var ( + osv OSVersion + once sync.Once +) + +// Get gets the operating system version on Windows. +// The calling application must be manifested to get the correct version information. +func Get() OSVersion { + once.Do(func() { + var err error + osv = OSVersion{} + osv.Version, err = windows.GetVersion() + if err != nil { + // GetVersion never fails. + panic(err) + } + osv.MajorVersion = uint8(osv.Version & 0xFF) + osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF) + osv.Build = uint16(osv.Version >> 16) + }) + return osv +} + +// Build gets the build-number on Windows +// The calling application must be manifested to get the correct version information. +func Build() uint16 { + return Get().Build +} + +func (osv OSVersion) ToString() string { + return fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.Build) +} diff --git a/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go b/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go new file mode 100644 index 0000000000000..e9267b9554f29 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go @@ -0,0 +1,38 @@ +package osversion + +const ( + // RS1 (version 1607, codename "Redstone 1") corresponds to Windows Server + // 2016 (ltsc2016) and Windows 10 (Anniversary Update). + RS1 = 14393 + + // RS2 (version 1703, codename "Redstone 2") was a client-only update, and + // corresponds to Windows 10 (Creators Update). + RS2 = 15063 + + // RS3 (version 1709, codename "Redstone 3") corresponds to Windows Server + // 1709 (Semi-Annual Channel (SAC)), and Windows 10 (Fall Creators Update). + RS3 = 16299 + + // RS4 (version 1803, codename "Redstone 4") corresponds to Windows Server + // 1803 (Semi-Annual Channel (SAC)), and Windows 10 (April 2018 Update). + RS4 = 17134 + + // RS5 (version 1809, codename "Redstone 5") corresponds to Windows Server + // 2019 (ltsc2019), and Windows 10 (October 2018 Update). + RS5 = 17763 + + // V19H1 (version 1903) corresponds to Windows Server 1903 (semi-annual + // channel). + V19H1 = 18362 + + // V19H2 (version 1909) corresponds to Windows Server 1909 (semi-annual + // channel). + V19H2 = 18363 + + // V20H1 (version 2004) corresponds to Windows Server 2004 (semi-annual + // channel). + V20H1 = 19041 + + // V20H2 corresponds to Windows Server 20H2 (semi-annual channel). + V20H2 = 19042 +) diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/export.go b/vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/export.go new file mode 100644 index 0000000000000..e3f1be333db36 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/export.go @@ -0,0 +1,88 @@ +// Package ociwclayer provides functions for importing and exporting Windows +// container layers from and to their OCI tar representation. +package ociwclayer + +import ( + "archive/tar" + "context" + "io" + "path/filepath" + + "github.com/Microsoft/go-winio/backuptar" + "github.com/Microsoft/hcsshim" +) + +var driverInfo = hcsshim.DriverInfo{} + +// ExportLayerToTar writes an OCI layer tar stream from the provided on-disk layer. +// The caller must specify the parent layers, if any, ordered from lowest to +// highest layer. +// +// The layer will be mounted for this process, so the caller should ensure that +// it is not currently mounted. +func ExportLayerToTar(ctx context.Context, w io.Writer, path string, parentLayerPaths []string) error { + err := hcsshim.ActivateLayer(driverInfo, path) + if err != nil { + return err + } + defer func() { + _ = hcsshim.DeactivateLayer(driverInfo, path) + }() + + // Prepare and unprepare the layer to ensure that it has been initialized. + err = hcsshim.PrepareLayer(driverInfo, path, parentLayerPaths) + if err != nil { + return err + } + err = hcsshim.UnprepareLayer(driverInfo, path) + if err != nil { + return err + } + + r, err := hcsshim.NewLayerReader(driverInfo, path, parentLayerPaths) + if err != nil { + return err + } + + err = writeTarFromLayer(ctx, r, w) + cerr := r.Close() + if err != nil { + return err + } + return cerr +} + +func writeTarFromLayer(ctx context.Context, r hcsshim.LayerReader, w io.Writer) error { + t := tar.NewWriter(w) + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + name, size, fileInfo, err := r.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + if fileInfo == nil { + // Write a whiteout file. + hdr := &tar.Header{ + Name: filepath.ToSlash(filepath.Join(filepath.Dir(name), whiteoutPrefix+filepath.Base(name))), + } + err := t.WriteHeader(hdr) + if err != nil { + return err + } + } else { + err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo) + if err != nil { + return err + } + } + } + return t.Close() +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/import.go b/vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/import.go new file mode 100644 index 0000000000000..e74a6b5946c47 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/ociwclayer/import.go @@ -0,0 +1,148 @@ +package ociwclayer + +import ( + "archive/tar" + "bufio" + "context" + "io" + "os" + "path" + "path/filepath" + "strings" + + winio "github.com/Microsoft/go-winio" + "github.com/Microsoft/go-winio/backuptar" + "github.com/Microsoft/hcsshim" +) + +const whiteoutPrefix = ".wh." + +var ( + // mutatedFiles is a list of files that are mutated by the import process + // and must be backed up and restored. + mutatedFiles = map[string]string{ + "UtilityVM/Files/EFI/Microsoft/Boot/BCD": "bcd.bak", + "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG": "bcd.log.bak", + "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak", + "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak", + } +) + +// ImportLayerFromTar reads a layer from an OCI layer tar stream and extracts it to the +// specified path. The caller must specify the parent layers, if any, ordered +// from lowest to highest layer. +// +// The caller must ensure that the thread or process has acquired backup and +// restore privileges. +// +// This function returns the total size of the layer's files, in bytes. +func ImportLayerFromTar(ctx context.Context, r io.Reader, path string, parentLayerPaths []string) (int64, error) { + err := os.MkdirAll(path, 0) + if err != nil { + return 0, err + } + w, err := hcsshim.NewLayerWriter(hcsshim.DriverInfo{}, path, parentLayerPaths) + if err != nil { + return 0, err + } + n, err := writeLayerFromTar(ctx, r, w, path) + cerr := w.Close() + if err != nil { + return 0, err + } + if cerr != nil { + return 0, cerr + } + return n, nil +} + +func writeLayerFromTar(ctx context.Context, r io.Reader, w hcsshim.LayerWriter, root string) (int64, error) { + t := tar.NewReader(r) + hdr, err := t.Next() + totalSize := int64(0) + buf := bufio.NewWriter(nil) + for err == nil { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + + base := path.Base(hdr.Name) + if strings.HasPrefix(base, whiteoutPrefix) { + name := path.Join(path.Dir(hdr.Name), base[len(whiteoutPrefix):]) + err = w.Remove(filepath.FromSlash(name)) + if err != nil { + return 0, err + } + hdr, err = t.Next() + } else if hdr.Typeflag == tar.TypeLink { + err = w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname)) + if err != nil { + return 0, err + } + hdr, err = t.Next() + } else { + var ( + name string + size int64 + fileInfo *winio.FileBasicInfo + ) + name, size, fileInfo, err = backuptar.FileInfoFromHeader(hdr) + if err != nil { + return 0, err + } + err = w.Add(filepath.FromSlash(name), fileInfo) + if err != nil { + return 0, err + } + hdr, err = writeBackupStreamFromTarAndSaveMutatedFiles(buf, w, t, hdr, root) + totalSize += size + } + } + if err != io.EOF { + return 0, err + } + return totalSize, nil +} + +// writeBackupStreamFromTarAndSaveMutatedFiles reads data from a tar stream and +// writes it to a backup stream, and also saves any files that will be mutated +// by the import layer process to a backup location. +func writeBackupStreamFromTarAndSaveMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) { + var bcdBackup *os.File + var bcdBackupWriter *winio.BackupFileWriter + if backupPath, ok := mutatedFiles[hdr.Name]; ok { + bcdBackup, err = os.Create(filepath.Join(root, backupPath)) + if err != nil { + return nil, err + } + defer func() { + cerr := bcdBackup.Close() + if err == nil { + err = cerr + } + }() + + bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false) + defer func() { + cerr := bcdBackupWriter.Close() + if err == nil { + err = cerr + } + }() + + buf.Reset(io.MultiWriter(w, bcdBackupWriter)) + } else { + buf.Reset(w) + } + + defer func() { + ferr := buf.Flush() + if err == nil { + err = ferr + } + }() + + return backuptar.WriteBackupStreamFromTarFile(buf, t, hdr) +} diff --git a/vendor/github.com/Microsoft/hcsshim/process.go b/vendor/github.com/Microsoft/hcsshim/process.go index ca8acbb7c2563..3362c683357a9 100644 --- a/vendor/github.com/Microsoft/hcsshim/process.go +++ b/vendor/github.com/Microsoft/hcsshim/process.go @@ -1,7 +1,9 @@ package hcsshim import ( + "context" "io" + "sync" "time" "github.com/Microsoft/hcsshim/internal/hcs" @@ -9,7 +11,10 @@ import ( // ContainerError is an error encountered in HCS type process struct { - p *hcs.Process + p *hcs.Process + waitOnce sync.Once + waitCh chan struct{} + waitErr error } // Pid returns the process ID of the process within the container. @@ -19,7 +24,14 @@ func (process *process) Pid() int { // Kill signals the process to terminate but does not wait for it to finish terminating. func (process *process) Kill() error { - return convertProcessError(process.p.Kill(), process) + found, err := process.p.Kill(context.Background()) + if err != nil { + return convertProcessError(err, process) + } + if !found { + return &ProcessError{Process: process, Err: ErrElementNotFound, Operation: "hcsshim::Process::Kill"} + } + return nil } // Wait waits for the process to exit. @@ -30,7 +42,21 @@ func (process *process) Wait() error { // WaitTimeout waits for the process to exit or the duration to elapse. It returns // false if timeout occurs. func (process *process) WaitTimeout(timeout time.Duration) error { - return convertProcessError(process.p.WaitTimeout(timeout), process) + process.waitOnce.Do(func() { + process.waitCh = make(chan struct{}) + go func() { + process.waitErr = process.Wait() + close(process.waitCh) + }() + }) + t := time.NewTimer(timeout) + defer t.Stop() + select { + case <-t.C: + return &ProcessError{Process: process, Err: ErrTimeout, Operation: "hcsshim::Process::Wait"} + case <-process.waitCh: + return process.waitErr + } } // ExitCode returns the exit code of the process. The process must have @@ -45,14 +71,14 @@ func (process *process) ExitCode() (int, error) { // ResizeConsole resizes the console of the process. func (process *process) ResizeConsole(width, height uint16) error { - return convertProcessError(process.p.ResizeConsole(width, height), process) + return convertProcessError(process.p.ResizeConsole(context.Background(), width, height), process) } // Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing // these pipes does not close the underlying pipes; it should be possible to // call this multiple times to get multiple interfaces. func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) { - stdin, stdout, stderr, err := process.p.Stdio() + stdin, stdout, stderr, err := process.p.StdioLegacy() if err != nil { err = convertProcessError(err, process) } @@ -62,7 +88,7 @@ func (process *process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, e // CloseStdin closes the write side of the stdin pipe so that the process is // notified on the read side that there is no more data in stdin. func (process *process) CloseStdin() error { - return convertProcessError(process.p.CloseStdin(), process) + return convertProcessError(process.p.CloseStdin(context.Background()), process) } // Close cleans up any state associated with the process but does not kill diff --git a/vendor/github.com/Microsoft/hcsshim/version.go b/vendor/github.com/Microsoft/hcsshim/version.go deleted file mode 100644 index 9ebb257b3e4c1..0000000000000 --- a/vendor/github.com/Microsoft/hcsshim/version.go +++ /dev/null @@ -1,6 +0,0 @@ -package hcsshim - -// IsTP4 returns whether the currently running Windows build is at least TP4. -func IsTP4() bool { - return false -} diff --git a/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go index cd471295b83f7..8bed848573831 100644 --- a/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated mksyscall_windows.exe DO NOT EDIT package hcsshim @@ -6,7 +6,6 @@ import ( "syscall" "unsafe" - "github.com/Microsoft/hcsshim/internal/interop" "golang.org/x/sys/windows" ) @@ -46,7 +45,10 @@ var ( func SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) { r0, _, _ := syscall.Syscall(procSetCurrentThreadCompartmentId.Addr(), 1, uintptr(compartmentId), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } diff --git a/vendor/github.com/Microsoft/opengcs/LICENSE b/vendor/github.com/Microsoft/opengcs/LICENSE deleted file mode 100644 index 4b1ad51b2f0ef..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE diff --git a/vendor/github.com/Microsoft/opengcs/README.md b/vendor/github.com/Microsoft/opengcs/README.md deleted file mode 100644 index e931fde8f3e8e..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/README.md +++ /dev/null @@ -1,14 +0,0 @@ - -# Open Guest Compute Service (opengcs) [![Build Status](https://travis-ci.org/Microsoft/opengcs.svg?branch=master)](https://travis-ci.org/Microsoft/opengcs) - -Open Guest Compute Service is a Linux open source project to further the development of a production quality implementation of Linux Hyper-V container on Windows (LCOW). It's designed to run inside a custom Linux OS for supporting Linux container payload. - -# Getting Started - - [How to build GCS binaries](./docs/gcsbuildinstructions.md/) - - [How to build custom Linux OS images](./docs/customosbuildinstructions.md/) - -# Contributing - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/vendor/github.com/Microsoft/opengcs/client/config.go b/vendor/github.com/Microsoft/opengcs/client/config.go deleted file mode 100644 index c2f6b21631422..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/config.go +++ /dev/null @@ -1,274 +0,0 @@ -// +build windows - -package client - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/Microsoft/hcsshim" - "github.com/sirupsen/logrus" -) - -// Mode is the operational mode, both requested, and actual after verification -type Mode uint - -const ( - // Constants for the actual mode after validation - - // ModeActualError means an error has occurred during validation - ModeActualError = iota - // ModeActualVhdx means that we are going to use VHDX boot after validation - ModeActualVhdx - // ModeActualKernelInitrd means that we are going to use kernel+initrd for boot after validation - ModeActualKernelInitrd - - // Constants for the requested mode - - // ModeRequestAuto means auto-select the boot mode for a utility VM - ModeRequestAuto = iota // VHDX will be priority over kernel+initrd - // ModeRequestVhdx means request VHDX boot if possible - ModeRequestVhdx - // ModeRequestKernelInitrd means request Kernel+initrd boot if possible - ModeRequestKernelInitrd - - // defaultUvmTimeoutSeconds is the default time to wait for utility VM operations - defaultUvmTimeoutSeconds = 5 * 60 - - // DefaultVhdxSizeGB is the size of the default sandbox & scratch in GB - DefaultVhdxSizeGB = 20 - - // defaultVhdxBlockSizeMB is the block-size for the sandbox/scratch VHDx's this package can create. - defaultVhdxBlockSizeMB = 1 -) - -// Config is the structure used to configuring a utility VM. There are two ways -// of starting. Either supply a VHD, or a Kernel+Initrd. For the latter, both -// must be supplied, and both must be in the same directory. -// -// VHD is the priority. -type Config struct { - Options // Configuration options - Name string // Name of the utility VM - RequestedMode Mode // What mode is preferred when validating - ActualMode Mode // What mode was obtained during validation - UvmTimeoutSeconds int // How long to wait for the utility VM to respond in seconds - Uvm hcsshim.Container // The actual container - MappedVirtualDisks []hcsshim.MappedVirtualDisk // Data-disks to be attached -} - -// Options is the structure used by a client to define configurable options for a utility VM. -type Options struct { - KirdPath string // Path to where kernel/initrd are found (defaults to %PROGRAMFILES%\Linux Containers) - KernelFile string // Kernel for Utility VM (embedded in a UEFI bootloader) - does NOT include full path, just filename - InitrdFile string // Initrd image for Utility VM - does NOT include full path, just filename - Vhdx string // VHD for booting the utility VM - is a full path - TimeoutSeconds int // Requested time for the utility VM to respond in seconds (may be over-ridden by environment) - BootParameters string // Additional boot parameters for initrd booting (not VHDx) -} - -// ParseOptions parses a set of K-V pairs into options used by opengcs. Note -// for consistency with the LCOW graphdriver in docker, we keep the same -// convention of an `lcow.` prefix. -func ParseOptions(options []string) (Options, error) { - rOpts := Options{TimeoutSeconds: 0} - for _, v := range options { - opt := strings.SplitN(v, "=", 2) - if len(opt) == 2 { - switch strings.ToLower(opt[0]) { - case "lcow.kirdpath": - rOpts.KirdPath = opt[1] - case "lcow.kernel": - rOpts.KernelFile = opt[1] - case "lcow.initrd": - rOpts.InitrdFile = opt[1] - case "lcow.vhdx": - rOpts.Vhdx = opt[1] - case "lcow.bootparameters": - rOpts.BootParameters = opt[1] - case "lcow.timeout": - var err error - if rOpts.TimeoutSeconds, err = strconv.Atoi(opt[1]); err != nil { - return rOpts, fmt.Errorf("lcow.timeout option could not be interpreted as an integer") - } - if rOpts.TimeoutSeconds < 0 { - return rOpts, fmt.Errorf("lcow.timeout option cannot be negative") - } - } - } - } - - // Set default values if not supplied - if rOpts.KirdPath == "" { - rOpts.KirdPath = filepath.Join(os.Getenv("ProgramFiles"), "Linux Containers") - } - if rOpts.Vhdx == "" { - rOpts.Vhdx = filepath.Join(rOpts.KirdPath, `uvm.vhdx`) - } - if rOpts.KernelFile == "" { - rOpts.KernelFile = `kernel` - } - if rOpts.InitrdFile == "" { - rOpts.InitrdFile = `initrd.img` - } - - return rOpts, nil -} - -// GenerateDefault generates a default config from a set of options -// If baseDir is not supplied, defaults to $env:ProgramFiles\Linux Containers -func (config *Config) GenerateDefault(options []string) error { - // Parse the options that the user supplied. - var err error - config.Options, err = ParseOptions(options) - if err != nil { - return err - } - - // Get the timeout from the environment - envTimeoutSeconds := 0 - envTimeout := os.Getenv("OPENGCS_UVM_TIMEOUT_SECONDS") - if len(envTimeout) > 0 { - var err error - if envTimeoutSeconds, err = strconv.Atoi(envTimeout); err != nil { - return fmt.Errorf("OPENGCS_UVM_TIMEOUT_SECONDS could not be interpreted as an integer") - } - if envTimeoutSeconds < 0 { - return fmt.Errorf("OPENGCS_UVM_TIMEOUT_SECONDS cannot be negative") - } - } - - // Priority to the requested timeout from the options. - if config.TimeoutSeconds != 0 { - config.UvmTimeoutSeconds = config.TimeoutSeconds - return nil - } - - // Next priority, the environment - if envTimeoutSeconds != 0 { - config.UvmTimeoutSeconds = envTimeoutSeconds - return nil - } - - // Last priority is the default timeout - config.UvmTimeoutSeconds = defaultUvmTimeoutSeconds - - // Set the default requested mode - config.RequestedMode = ModeRequestAuto - - return nil -} - -// Validate validates a Config structure for starting a utility VM. -func (config *Config) Validate() error { - config.ActualMode = ModeActualError - - if config.RequestedMode == ModeRequestVhdx && config.Vhdx == "" { - return fmt.Errorf("VHDx mode must supply a VHDx") - } - if config.RequestedMode == ModeRequestKernelInitrd && (config.KernelFile == "" || config.InitrdFile == "") { - return fmt.Errorf("kernel+initrd mode must supply both kernel and initrd") - } - - // Validate that if VHDX requested or auto, it exists. - if config.RequestedMode == ModeRequestAuto || config.RequestedMode == ModeRequestVhdx { - if _, err := os.Stat(config.Vhdx); os.IsNotExist(err) { - if config.RequestedMode == ModeRequestVhdx { - return fmt.Errorf("VHDx '%s' not found", config.Vhdx) - } - } else { - config.ActualMode = ModeActualVhdx - - // Can't specify boot parameters with VHDx - if config.BootParameters != "" { - return fmt.Errorf("Boot parameters cannot be specified in VHDx mode") - } - return nil - } - } - - // So must be kernel+initrd, or auto where we fallback as the VHDX doesn't exist - if config.InitrdFile == "" || config.KernelFile == "" { - if config.RequestedMode == ModeRequestKernelInitrd { - return fmt.Errorf("initrd and kernel options must be supplied") - } - return fmt.Errorf("opengcs: configuration is invalid") - } - - if _, err := os.Stat(filepath.Join(config.KirdPath, config.KernelFile)); os.IsNotExist(err) { - return fmt.Errorf("kernel '%s' not found", filepath.Join(config.KirdPath, config.KernelFile)) - } - if _, err := os.Stat(filepath.Join(config.KirdPath, config.InitrdFile)); os.IsNotExist(err) { - return fmt.Errorf("initrd '%s' not found", filepath.Join(config.KirdPath, config.InitrdFile)) - } - - config.ActualMode = ModeActualKernelInitrd - - // Ensure all the MappedVirtualDisks exist on the host - for _, mvd := range config.MappedVirtualDisks { - if _, err := os.Stat(mvd.HostPath); err != nil { - return fmt.Errorf("mapped virtual disk '%s' not found", mvd.HostPath) - } - if mvd.ContainerPath == "" { - return fmt.Errorf("mapped virtual disk '%s' requested without a container path", mvd.HostPath) - } - } - - return nil -} - -// StartUtilityVM creates and starts a utility VM from a configuration. -func (config *Config) StartUtilityVM() error { - logrus.Debugf("opengcs: StartUtilityVM: %+v", config) - - if err := config.Validate(); err != nil { - return err - } - - configuration := &hcsshim.ContainerConfig{ - HvPartition: true, - Name: config.Name, - SystemType: "container", - ContainerType: "linux", - TerminateOnLastHandleClosed: true, - MappedVirtualDisks: config.MappedVirtualDisks, - } - - if config.ActualMode == ModeActualVhdx { - configuration.HvRuntime = &hcsshim.HvRuntime{ - ImagePath: config.Vhdx, - BootSource: "Vhd", - WritableBootSource: false, - } - } else { - configuration.HvRuntime = &hcsshim.HvRuntime{ - ImagePath: config.KirdPath, - LinuxInitrdFile: config.InitrdFile, - LinuxKernelFile: config.KernelFile, - LinuxBootParameters: config.BootParameters, - } - } - - configurationS, _ := json.Marshal(configuration) - logrus.Debugf("opengcs: StartUtilityVM: calling HCS with '%s'", string(configurationS)) - uvm, err := hcsshim.CreateContainer(config.Name, configuration) - if err != nil { - return err - } - logrus.Debugf("opengcs: StartUtilityVM: uvm created, starting...") - err = uvm.Start() - if err != nil { - logrus.Debugf("opengcs: StartUtilityVM: uvm failed to start: %s", err) - // Make sure we don't leave it laying around as it's been created in HCS - uvm.Terminate() - return err - } - - config.Uvm = uvm - logrus.Debugf("opengcs StartUtilityVM: uvm %s is running", config.Name) - return nil -} diff --git a/vendor/github.com/Microsoft/opengcs/client/createext4vhdx.go b/vendor/github.com/Microsoft/opengcs/client/createext4vhdx.go deleted file mode 100644 index 8fb7520b41a4a..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/createext4vhdx.go +++ /dev/null @@ -1,167 +0,0 @@ -// +build windows - -package client - -import ( - "bytes" - "fmt" - "os" - "strings" - "time" - - winio "github.com/Microsoft/go-winio/vhd" - // "github.com/Microsoft/hcsshim" - "github.com/sirupsen/logrus" -) - -// dismount is a simple utility function wrapping a conditional HotRemove. It would -// have been easier if you could cancel a deferred function, but this works just -// as well. -func (config *Config) dismount(file string) error { - logrus.Debugf("opengcs: CreateExt4Vhdx: hot-remove of %s", file) - err := config.HotRemoveVhd(file) - if err != nil { - logrus.Warnf("failed to hot-remove: %s", err) - } - return err -} - -// CreateExt4Vhdx does what it says on the tin. It is the responsibility of the caller to synchronise -// simultaneous attempts to create the cache file. -func (config *Config) CreateExt4Vhdx(destFile string, sizeGB uint32, cacheFile string) error { - // Smallest we can accept is the default sandbox size as we can't size down, only expand. - if sizeGB < DefaultVhdxSizeGB { - sizeGB = DefaultVhdxSizeGB - } - - logrus.Debugf("opengcs: CreateExt4Vhdx: %s size:%dGB cache:%s", destFile, sizeGB, cacheFile) - - // Retrieve from cache if the default size and already on disk - if cacheFile != "" && sizeGB == DefaultVhdxSizeGB { - if _, err := os.Stat(cacheFile); err == nil { - if err := CopyFile(cacheFile, destFile, false); err != nil { - return fmt.Errorf("failed to copy cached file '%s' to '%s': %s", cacheFile, destFile, err) - } - logrus.Debugf("opengcs: CreateExt4Vhdx: %s fulfilled from cache", destFile) - return nil - } - } - - // Must have a utility VM to operate on - if config.Uvm == nil { - return fmt.Errorf("no utility VM") - } - - // Create the VHDX - if err := winio.CreateVhdx(destFile, sizeGB, defaultVhdxBlockSizeMB); err != nil { - return fmt.Errorf("failed to create VHDx %s: %s", destFile, err) - } - - defer config.DebugGCS() - - // Attach it to the utility VM, but don't mount it (as there's no filesystem on it) - if err := config.HotAddVhd(destFile, "", false, false); err != nil { - return fmt.Errorf("opengcs: CreateExt4Vhdx: failed to hot-add %s to utility VM: %s", cacheFile, err) - } - - // Get the list of mapped virtual disks to find the controller and LUN IDs - logrus.Debugf("opengcs: CreateExt4Vhdx: %s querying mapped virtual disks", destFile) - mvdControllers, err := config.Uvm.MappedVirtualDisks() - if err != nil { - return fmt.Errorf("failed to get mapped virtual disks: %s", err) - } - - // Find our mapped disk from the list of all currently added. - controller := -1 - lun := -1 - for controllerNumber, controllerElement := range mvdControllers { - for diskNumber, diskElement := range controllerElement.MappedVirtualDisks { - if diskElement.HostPath == destFile { - controller = controllerNumber - lun = diskNumber - break - } - } - } - if controller == -1 || lun == -1 { - config.dismount(destFile) - return fmt.Errorf("failed to find %s in mapped virtual disks after hot-adding", destFile) - } - logrus.Debugf("opengcs: CreateExt4Vhdx: %s at C=%d L=%d", destFile, controller, lun) - - // Validate /sys/bus/scsi/devices/C:0:0:L exists as a directory - testdCommand := fmt.Sprintf(`test -d /sys/bus/scsi/devices/%d:0:0:%d`, controller, lun) - testdProc, err := config.RunProcess(testdCommand, nil, nil, nil) - if err != nil { - config.dismount(destFile) - return fmt.Errorf("failed to `%s` following hot-add %s to utility VM: %s", testdCommand, destFile, err) - } - defer testdProc.Close() - testdProc.WaitTimeout(time.Second * time.Duration(config.UvmTimeoutSeconds)) - testdExitCode, err := testdProc.ExitCode() - if err != nil { - config.dismount(destFile) - return fmt.Errorf("failed to get exit code from `%s` following hot-add %s to utility VM: %s", testdCommand, destFile, err) - } - if testdExitCode != 0 { - config.dismount(destFile) - return fmt.Errorf("`%s` return non-zero exit code (%d) following hot-add %s to utility VM", testdCommand, testdExitCode, destFile) - } - - // Get the device from under the block subdirectory by doing a simple ls. This will come back as (eg) `sda` - lsCommand := fmt.Sprintf(`ls /sys/bus/scsi/devices/%d:0:0:%d/block`, controller, lun) - var lsOutput bytes.Buffer - lsProc, err := config.RunProcess(lsCommand, nil, &lsOutput, nil) - if err != nil { - config.dismount(destFile) - return fmt.Errorf("failed to `%s` following hot-add %s to utility VM: %s", lsCommand, destFile, err) - } - defer lsProc.Close() - lsProc.WaitTimeout(time.Second * time.Duration(config.UvmTimeoutSeconds)) - lsExitCode, err := lsProc.ExitCode() - if err != nil { - config.dismount(destFile) - return fmt.Errorf("failed to get exit code from `%s` following hot-add %s to utility VM: %s", lsCommand, destFile, err) - } - if lsExitCode != 0 { - config.dismount(destFile) - return fmt.Errorf("`%s` return non-zero exit code (%d) following hot-add %s to utility VM", lsCommand, lsExitCode, destFile) - } - device := fmt.Sprintf(`/dev/%s`, strings.TrimSpace(lsOutput.String())) - logrus.Debugf("opengcs: CreateExt4Vhdx: %s: device at %s", destFile, device) - - // Format it ext4 - mkfsCommand := fmt.Sprintf(`mkfs.ext4 -q -E lazy_itable_init=1 -O ^has_journal,sparse_super2,uninit_bg,^resize_inode %s`, device) - var mkfsStderr bytes.Buffer - mkfsProc, err := config.RunProcess(mkfsCommand, nil, nil, &mkfsStderr) - if err != nil { - config.dismount(destFile) - return fmt.Errorf("failed to RunProcess %q following hot-add %s to utility VM: %s", destFile, mkfsCommand, err) - } - defer mkfsProc.Close() - mkfsProc.WaitTimeout(time.Second * time.Duration(config.UvmTimeoutSeconds)) - mkfsExitCode, err := mkfsProc.ExitCode() - if err != nil { - config.dismount(destFile) - return fmt.Errorf("failed to get exit code from `%s` following hot-add %s to utility VM: %s", mkfsCommand, destFile, err) - } - if mkfsExitCode != 0 { - config.dismount(destFile) - return fmt.Errorf("`%s` return non-zero exit code (%d) following hot-add %s to utility VM: %s", mkfsCommand, mkfsExitCode, destFile, strings.TrimSpace(mkfsStderr.String())) - } - - // Dismount before we copy it - if err := config.dismount(destFile); err != nil { - return fmt.Errorf("failed to hot-remove: %s", err) - } - - // Populate the cache. - if cacheFile != "" && (sizeGB == DefaultVhdxSizeGB) { - if err := CopyFile(destFile, cacheFile, true); err != nil { - return fmt.Errorf("failed to seed cache '%s' from '%s': %s", destFile, cacheFile, err) - } - } - - logrus.Debugf("opengcs: CreateExt4Vhdx: %s created (non-cache)", destFile) - return nil -} diff --git a/vendor/github.com/Microsoft/opengcs/client/hotaddvhd.go b/vendor/github.com/Microsoft/opengcs/client/hotaddvhd.go deleted file mode 100644 index ef1e51fd65090..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/hotaddvhd.go +++ /dev/null @@ -1,42 +0,0 @@ -// +build windows - -package client - -import ( - "fmt" - - "github.com/Microsoft/hcsshim" - "github.com/sirupsen/logrus" -) - -// HotAddVhd hot-adds a VHD to a utility VM. This is used in the global one-utility-VM- -// service-VM per host scenario. In order to do a graphdriver `Diff`, we hot-add the -// sandbox to /mnt/ so that we can run `exportSandbox` inside the utility VM to -// get a tar-stream of the sandboxes contents back to the daemon. -func (config *Config) HotAddVhd(hostPath string, containerPath string, readOnly bool, mount bool) error { - logrus.Debugf("opengcs: HotAddVhd: %s: %s", hostPath, containerPath) - - if config.Uvm == nil { - return fmt.Errorf("cannot hot-add VHD as no utility VM is in configuration") - } - - defer config.DebugGCS() - - modification := &hcsshim.ResourceModificationRequestResponse{ - Resource: "MappedVirtualDisk", - Data: hcsshim.MappedVirtualDisk{ - HostPath: hostPath, - ContainerPath: containerPath, - CreateInUtilityVM: true, - ReadOnly: readOnly, - AttachOnly: !mount, - }, - Request: "Add", - } - - if err := config.Uvm.Modify(modification); err != nil { - return fmt.Errorf("failed to modify utility VM configuration for hot-add: %s", err) - } - logrus.Debugf("opengcs: HotAddVhd: %s added successfully", hostPath) - return nil -} diff --git a/vendor/github.com/Microsoft/opengcs/client/hotremovevhd.go b/vendor/github.com/Microsoft/opengcs/client/hotremovevhd.go deleted file mode 100644 index be631891731b4..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/hotremovevhd.go +++ /dev/null @@ -1,36 +0,0 @@ -// +build windows - -package client - -import ( - "fmt" - - "github.com/Microsoft/hcsshim" - "github.com/sirupsen/logrus" -) - -// HotRemoveVhd hot-removes a VHD from a utility VM. This is used in the global one-utility-VM- -// service-VM per host scenario. -func (config *Config) HotRemoveVhd(hostPath string) error { - logrus.Debugf("opengcs: HotRemoveVhd: %s", hostPath) - - if config.Uvm == nil { - return fmt.Errorf("cannot hot-add VHD as no utility VM is in configuration") - } - - defer config.DebugGCS() - - modification := &hcsshim.ResourceModificationRequestResponse{ - Resource: "MappedVirtualDisk", - Data: hcsshim.MappedVirtualDisk{ - HostPath: hostPath, - CreateInUtilityVM: true, - }, - Request: "Remove", - } - if err := config.Uvm.Modify(modification); err != nil { - return fmt.Errorf("failed modifying utility VM for hot-remove %s: %s", hostPath, err) - } - logrus.Debugf("opengcs: HotRemoveVhd: %s removed successfully", hostPath) - return nil -} diff --git a/vendor/github.com/Microsoft/opengcs/client/init.go b/vendor/github.com/Microsoft/opengcs/client/init.go deleted file mode 100644 index 246ac185e9f89..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/init.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build windows - -package client - -import ( - "os" - "strconv" -) - -var ( - logDataFromUVM int64 -) - -func init() { - bytes := os.Getenv("OPENGCS_LOG_DATA_FROM_UVM") - if len(bytes) == 0 { - return - } - u, err := strconv.ParseUint(bytes, 10, 32) - if err != nil { - return - } - logDataFromUVM = int64(u) -} diff --git a/vendor/github.com/Microsoft/opengcs/client/layervhddetails.go b/vendor/github.com/Microsoft/opengcs/client/layervhddetails.go deleted file mode 100644 index 010780069ae0b..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/layervhddetails.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build windows - -package client - -import ( - "fmt" - "os" - "path/filepath" -) - -// LayerVhdDetails is a utility for getting a file name, size and indication of -// sandbox for a VHD(x) in a folder. A read-only layer will be layer.vhd. A -// read-write layer will be sandbox.vhdx. -func LayerVhdDetails(folder string) (string, int64, bool, error) { - var fileInfo os.FileInfo - isSandbox := false - filename := filepath.Join(folder, "layer.vhd") - var err error - - if fileInfo, err = os.Stat(filename); err != nil { - filename = filepath.Join(folder, "sandbox.vhdx") - if fileInfo, err = os.Stat(filename); err != nil { - if os.IsNotExist(err) { - return "", 0, isSandbox, fmt.Errorf("could not find layer or sandbox in %s", folder) - } - return "", 0, isSandbox, fmt.Errorf("error locating layer or sandbox in %s: %s", folder, err) - } - isSandbox = true - } - return filename, fileInfo.Size(), isSandbox, nil -} diff --git a/vendor/github.com/Microsoft/opengcs/client/process.go b/vendor/github.com/Microsoft/opengcs/client/process.go deleted file mode 100644 index a8ffdf98e61d1..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/process.go +++ /dev/null @@ -1,164 +0,0 @@ -// +build windows - -package client - -import ( - "bytes" - "fmt" - "io" - "os" - "strings" - "time" - - "github.com/Microsoft/hcsshim" - "github.com/sirupsen/logrus" -) - -// Process is the structure pertaining to a process running in a utility VM. -type process struct { - Process hcsshim.Process - Stdin io.WriteCloser - Stdout io.ReadCloser - Stderr io.ReadCloser -} - -// createUtilsProcess is a convenient wrapper for hcsshim.createUtilsProcess to use when -// communicating with a utility VM. -func (config *Config) createUtilsProcess(commandLine string) (process, error) { - logrus.Debugf("opengcs: createUtilsProcess") - - if config.Uvm == nil { - return process{}, fmt.Errorf("cannot create utils process as no utility VM is in configuration") - } - - var ( - err error - proc process - ) - - env := make(map[string]string) - env["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:" - processConfig := &hcsshim.ProcessConfig{ - EmulateConsole: false, - CreateStdInPipe: true, - CreateStdOutPipe: true, - CreateStdErrPipe: true, - CreateInUtilityVm: true, - WorkingDirectory: "/bin", - Environment: env, - CommandLine: commandLine, - } - proc.Process, err = config.Uvm.CreateProcess(processConfig) - if err != nil { - return process{}, fmt.Errorf("failed to create process (%+v) in utility VM: %s", config, err) - } - - if proc.Stdin, proc.Stdout, proc.Stderr, err = proc.Process.Stdio(); err != nil { - proc.Process.Kill() // Should this have a timeout? - proc.Process.Close() - return process{}, fmt.Errorf("failed to get stdio pipes for process %+v: %s", config, err) - } - - logrus.Debugf("opengcs: createUtilsProcess success: pid %d", proc.Process.Pid()) - return proc, nil -} - -// RunProcess runs the given command line program in the utilityVM. It takes in -// an input to the reader to feed into stdin and returns stdout to output. -// IMPORTANT: It is the responsibility of the caller to call Close() on the returned process. -func (config *Config) RunProcess(commandLine string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (hcsshim.Process, error) { - logrus.Debugf("opengcs: RunProcess: %s", commandLine) - process, err := config.createUtilsProcess(commandLine) - if err != nil { - return nil, err - } - - // Send the data into the process's stdin - if stdin != nil { - if _, err = copyWithTimeout(process.Stdin, - stdin, - 0, - config.UvmTimeoutSeconds, - fmt.Sprintf("send to stdin of %s", commandLine)); err != nil { - return nil, err - } - - // Don't need stdin now we've sent everything. This signals GCS that we are finished sending data. - if err := process.Process.CloseStdin(); err != nil && !hcsshim.IsNotExist(err) && !hcsshim.IsAlreadyClosed(err) { - // This error will occur if the compute system is currently shutting down - if perr, ok := err.(*hcsshim.ProcessError); ok && perr.Err != hcsshim.ErrVmcomputeOperationInvalidState { - return nil, err - } - } - } - - if stdout != nil { - // Copy the data over to the writer. - if _, err := copyWithTimeout(stdout, - process.Stdout, - 0, - config.UvmTimeoutSeconds, - fmt.Sprintf("RunProcess: copy back from %s", commandLine)); err != nil { - return nil, err - } - } - - if stderr != nil { - // Copy the data over to the writer. - if _, err := copyWithTimeout(stderr, - process.Stderr, - 0, - config.UvmTimeoutSeconds, - fmt.Sprintf("RunProcess: copy back from %s", commandLine)); err != nil { - return nil, err - } - } - - logrus.Debugf("opengcs: runProcess success: %s", commandLine) - return process.Process, nil -} - -func debugCommand(s string) string { - return fmt.Sprintf(`echo -e 'DEBUG COMMAND: %s\\n--------------\\n';%s;echo -e '\\n\\n';`, s, s) -} - -// DebugGCS extracts logs from the GCS. It's a useful hack for debugging, -// but not necessarily optimal, but all that is available to us in RS3. -func (config *Config) DebugGCS() { - if logrus.GetLevel() < logrus.DebugLevel || len(os.Getenv("OPENGCS_DEBUG_ENABLE")) == 0 { - return - } - - var out bytes.Buffer - cmd := os.Getenv("OPENGCS_DEBUG_COMMAND") - if cmd == "" { - cmd = `sh -c "` - cmd += debugCommand("kill -10 `pidof gcs`") // SIGUSR1 for stackdump - cmd += debugCommand("ls -l /tmp") - cmd += debugCommand("cat /tmp/gcs.log") - cmd += debugCommand("cat /tmp/gcs/gcs-stacks*") - cmd += debugCommand("cat /tmp/gcs/paniclog*") - cmd += debugCommand("ls -l /tmp/gcs") - cmd += debugCommand("ls -l /tmp/gcs/*") - cmd += debugCommand("cat /tmp/gcs/*/config.json") - cmd += debugCommand("ls -lR /var/run/gcsrunc") - cmd += debugCommand("cat /tmp/gcs/global-runc.log") - cmd += debugCommand("cat /tmp/gcs/*/runc.log") - cmd += debugCommand("ps -ef") - cmd += `"` - } - proc, err := config.RunProcess(cmd, nil, &out, nil) - defer func() { - if proc != nil { - proc.Kill() - proc.Close() - } - }() - if err != nil { - logrus.Debugln("benign failure getting gcs logs: ", err) - } - if proc != nil { - proc.WaitTimeout(time.Second * 30) - } - logrus.Debugf("GCS Debugging:\n%s\n\nEnd GCS Debugging", strings.TrimSpace(out.String())) -} diff --git a/vendor/github.com/Microsoft/opengcs/client/tartovhd.go b/vendor/github.com/Microsoft/opengcs/client/tartovhd.go deleted file mode 100644 index 29ee48957aa84..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/tartovhd.go +++ /dev/null @@ -1,46 +0,0 @@ -// +build windows - -package client - -import ( - "fmt" - "io" - - "github.com/sirupsen/logrus" -) - -// TarToVhd streams a tarstream contained in an io.Reader to a fixed vhd file -func (config *Config) TarToVhd(targetVHDFile string, reader io.Reader) (int64, error) { - logrus.Debugf("opengcs: TarToVhd: %s", targetVHDFile) - - if config.Uvm == nil { - return 0, fmt.Errorf("cannot Tar2Vhd as no utility VM is in configuration") - } - - defer config.DebugGCS() - - process, err := config.createUtilsProcess("tar2vhd") - if err != nil { - return 0, fmt.Errorf("failed to start tar2vhd for %s: %s", targetVHDFile, err) - } - defer process.Process.Close() - - // Send the tarstream into the `tar2vhd`s stdin - if _, err = copyWithTimeout(process.Stdin, reader, 0, config.UvmTimeoutSeconds, fmt.Sprintf("stdin of tar2vhd for generating %s", targetVHDFile)); err != nil { - return 0, fmt.Errorf("failed sending to tar2vhd for %s: %s", targetVHDFile, err) - } - - // Don't need stdin now we've sent everything. This signals GCS that we are finished sending data. - if err := process.Process.CloseStdin(); err != nil { - return 0, fmt.Errorf("failed closing stdin handle for %s: %s", targetVHDFile, err) - } - - // Write stdout contents of `tar2vhd` to the VHD file - payloadSize, err := writeFileFromReader(targetVHDFile, process.Stdout, config.UvmTimeoutSeconds, fmt.Sprintf("stdout of tar2vhd to %s", targetVHDFile)) - if err != nil { - return 0, fmt.Errorf("failed to write %s during tar2vhd: %s", targetVHDFile, err) - } - - logrus.Debugf("opengcs: TarToVhd: %s created, %d bytes", targetVHDFile, payloadSize) - return payloadSize, err -} diff --git a/vendor/github.com/Microsoft/opengcs/client/unsupported.go b/vendor/github.com/Microsoft/opengcs/client/unsupported.go deleted file mode 100644 index 63b7067dce043..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/unsupported.go +++ /dev/null @@ -1,3 +0,0 @@ -// +build !windows - -package client diff --git a/vendor/github.com/Microsoft/opengcs/client/utilities.go b/vendor/github.com/Microsoft/opengcs/client/utilities.go deleted file mode 100644 index 8441d960f409d..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/utilities.go +++ /dev/null @@ -1,122 +0,0 @@ -// +build windows - -package client - -import ( - "bytes" - "encoding/hex" - "fmt" - "io" - "os" - "syscall" - "time" - "unsafe" - - "github.com/sirupsen/logrus" -) - -var ( - modkernel32 = syscall.NewLazyDLL("kernel32.dll") - procCopyFileW = modkernel32.NewProc("CopyFileW") -) - -// writeFileFromReader writes an output file from an io.Reader -func writeFileFromReader(path string, reader io.Reader, timeoutSeconds int, context string) (int64, error) { - outFile, err := os.Create(path) - if err != nil { - return 0, fmt.Errorf("opengcs: writeFileFromReader: failed to create %s: %s", path, err) - } - defer outFile.Close() - return copyWithTimeout(outFile, reader, 0, timeoutSeconds, context) -} - -// copyWithTimeout is a wrapper for io.Copy using a timeout duration -func copyWithTimeout(dst io.Writer, src io.Reader, size int64, timeoutSeconds int, context string) (int64, error) { - logrus.Debugf("opengcs: copywithtimeout: size %d: timeout %d: (%s)", size, timeoutSeconds, context) - - type resultType struct { - err error - bytes int64 - } - - done := make(chan resultType, 1) - go func() { - result := resultType{} - if logrus.GetLevel() < logrus.DebugLevel || logDataFromUVM == 0 { - result.bytes, result.err = io.Copy(dst, src) - } else { - // In advanced debug mode where we log (hexdump format) what is copied - // up to the number of bytes defined by environment variable - // OPENGCS_LOG_DATA_FROM_UVM - var buf bytes.Buffer - tee := io.TeeReader(src, &buf) - result.bytes, result.err = io.Copy(dst, tee) - if result.err == nil { - size := result.bytes - if size > logDataFromUVM { - size = logDataFromUVM - } - if size > 0 { - bytes := make([]byte, size) - if _, err := buf.Read(bytes); err == nil { - logrus.Debugf(fmt.Sprintf("opengcs: copyWithTimeout\n%s", hex.Dump(bytes))) - } - } - } - } - done <- result - }() - - var result resultType - timedout := time.After(time.Duration(timeoutSeconds) * time.Second) - - select { - case <-timedout: - return 0, fmt.Errorf("opengcs: copyWithTimeout: timed out (%s)", context) - case result = <-done: - if result.err != nil && result.err != io.EOF { - // See https://github.com/golang/go/blob/f3f29d1dea525f48995c1693c609f5e67c046893/src/os/exec/exec_windows.go for a clue as to why we are doing this :) - if se, ok := result.err.(syscall.Errno); ok { - const ( - errNoData = syscall.Errno(232) - errBrokenPipe = syscall.Errno(109) - ) - if se == errNoData || se == errBrokenPipe { - logrus.Debugf("opengcs: copyWithTimeout: hit NoData or BrokenPipe: %d: %s", se, context) - return result.bytes, nil - } - } - return 0, fmt.Errorf("opengcs: copyWithTimeout: error reading: '%s' after %d bytes (%s)", result.err, result.bytes, context) - } - } - logrus.Debugf("opengcs: copyWithTimeout: success - copied %d bytes (%s)", result.bytes, context) - return result.bytes, nil -} - -// CopyFile is a utility for copying a file - used for the sandbox cache. -// Uses CopyFileW win32 API for performance -func CopyFile(srcFile, destFile string, overwrite bool) error { - var bFailIfExists uint32 = 1 - if overwrite { - bFailIfExists = 0 - } - - lpExistingFileName, err := syscall.UTF16PtrFromString(srcFile) - if err != nil { - return err - } - lpNewFileName, err := syscall.UTF16PtrFromString(destFile) - if err != nil { - return err - } - r1, _, err := syscall.Syscall( - procCopyFileW.Addr(), - 3, - uintptr(unsafe.Pointer(lpExistingFileName)), - uintptr(unsafe.Pointer(lpNewFileName)), - uintptr(bFailIfExists)) - if r1 == 0 { - return fmt.Errorf("failed CopyFileW Win32 call from '%s' to '%s': %s", srcFile, destFile, err) - } - return nil -} diff --git a/vendor/github.com/Microsoft/opengcs/client/vhdtotar.go b/vendor/github.com/Microsoft/opengcs/client/vhdtotar.go deleted file mode 100644 index 72e9a24ff978a..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/client/vhdtotar.go +++ /dev/null @@ -1,69 +0,0 @@ -// +build windows - -package client - -import ( - "fmt" - "io" - "os" - - "github.com/sirupsen/logrus" -) - -// VhdToTar does what is says - it exports a VHD in a specified -// folder (either a read-only layer.vhd, or a read-write sandbox.vhd) to a -// ReadCloser containing a tar-stream of the layers contents. -func (config *Config) VhdToTar(vhdFile string, uvmMountPath string, isSandbox bool, vhdSize int64) (io.ReadCloser, error) { - logrus.Debugf("opengcs: VhdToTar: %s isSandbox: %t", vhdFile, isSandbox) - - if config.Uvm == nil { - return nil, fmt.Errorf("cannot VhdToTar as no utility VM is in configuration") - } - - defer config.DebugGCS() - - vhdHandle, err := os.Open(vhdFile) - if err != nil { - return nil, fmt.Errorf("opengcs: VhdToTar: failed to open %s: %s", vhdFile, err) - } - defer vhdHandle.Close() - logrus.Debugf("opengcs: VhdToTar: exporting %s, size %d, isSandbox %t", vhdHandle.Name(), vhdSize, isSandbox) - - // Different binary depending on whether a RO layer or a RW sandbox - command := "vhd2tar" - if isSandbox { - command = fmt.Sprintf("exportSandbox -path %s", uvmMountPath) - } - - // Start the binary in the utility VM - process, err := config.createUtilsProcess(command) - if err != nil { - return nil, fmt.Errorf("opengcs: VhdToTar: %s: failed to create utils process %s: %s", vhdHandle.Name(), command, err) - } - - if !isSandbox { - // Send the VHD contents to the utility VM processes stdin handle if not a sandbox - logrus.Debugf("opengcs: VhdToTar: copying the layer VHD into the utility VM") - if _, err = copyWithTimeout(process.Stdin, vhdHandle, vhdSize, config.UvmTimeoutSeconds, fmt.Sprintf("vhdtotarstream: sending %s to %s", vhdHandle.Name(), command)); err != nil { - process.Process.Close() - return nil, fmt.Errorf("opengcs: VhdToTar: %s: failed to copyWithTimeout on the stdin pipe (to utility VM): %s", vhdHandle.Name(), err) - } - } - - // Start a goroutine which copies the stdout (ie the tar stream) - reader, writer := io.Pipe() - go func() { - defer writer.Close() - defer process.Process.Close() - logrus.Debugf("opengcs: VhdToTar: copying tar stream back from the utility VM") - bytes, err := copyWithTimeout(writer, process.Stdout, vhdSize, config.UvmTimeoutSeconds, fmt.Sprintf("vhdtotarstream: copy tarstream from %s", command)) - if err != nil { - logrus.Errorf("opengcs: VhdToTar: %s: copyWithTimeout on the stdout pipe (from utility VM) failed: %s", vhdHandle.Name(), err) - } - logrus.Debugf("opengcs: VhdToTar: copied %d bytes of the tarstream of %s from the utility VM", bytes, vhdHandle.Name()) - }() - - // Return the read-side of the pipe connected to the goroutine which is reading from the stdout of the process in the utility VM - return reader, nil - -} diff --git a/vendor/github.com/Microsoft/opengcs/service/gcsutils/remotefs/defs.go b/vendor/github.com/Microsoft/opengcs/service/gcsutils/remotefs/defs.go deleted file mode 100644 index f1f2c04a45d23..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/service/gcsutils/remotefs/defs.go +++ /dev/null @@ -1,109 +0,0 @@ -package remotefs - -import ( - "errors" - "os" - "time" -) - -// RemotefsCmd is the name of the remotefs meta command -const RemotefsCmd = "remotefs" - -// Name of the commands when called from the cli context (remotefs ...) -const ( - StatCmd = "stat" - LstatCmd = "lstat" - ReadlinkCmd = "readlink" - MkdirCmd = "mkdir" - MkdirAllCmd = "mkdirall" - RemoveCmd = "remove" - RemoveAllCmd = "removeall" - LinkCmd = "link" - SymlinkCmd = "symlink" - LchmodCmd = "lchmod" - LchownCmd = "lchown" - MknodCmd = "mknod" - MkfifoCmd = "mkfifo" - OpenFileCmd = "openfile" - ReadFileCmd = "readfile" - WriteFileCmd = "writefile" - ReadDirCmd = "readdir" - ResolvePathCmd = "resolvepath" - ExtractArchiveCmd = "extractarchive" - ArchivePathCmd = "archivepath" -) - -// ErrInvalid is returned if the parameters are invalid -var ErrInvalid = errors.New("invalid arguments") - -// ErrUnknown is returned for an unknown remotefs command -var ErrUnknown = errors.New("unkown command") - -// ExportedError is the serialized version of the a Go error. -// It also provides a trivial implementation of the error interface. -type ExportedError struct { - ErrString string - ErrNum int `json:",omitempty"` -} - -// Error returns an error string -func (ee *ExportedError) Error() string { - return ee.ErrString -} - -// FileInfo is the stat struct returned by the remotefs system. It -// fulfills the os.FileInfo interface. -type FileInfo struct { - NameVar string - SizeVar int64 - ModeVar os.FileMode - ModTimeVar int64 // Serialization of time.Time breaks in travis, so use an int - IsDirVar bool -} - -var _ os.FileInfo = &FileInfo{} - -// Name returns the filename from a FileInfo structure -func (f *FileInfo) Name() string { return f.NameVar } - -// Size returns the size from a FileInfo structure -func (f *FileInfo) Size() int64 { return f.SizeVar } - -// Mode returns the mode from a FileInfo structure -func (f *FileInfo) Mode() os.FileMode { return f.ModeVar } - -// ModTime returns the modification time from a FileInfo structure -func (f *FileInfo) ModTime() time.Time { return time.Unix(0, f.ModTimeVar) } - -// IsDir returns the is-directory indicator from a FileInfo structure -func (f *FileInfo) IsDir() bool { return f.IsDirVar } - -// Sys provides an interface to a FileInfo structure -func (f *FileInfo) Sys() interface{} { return nil } - -// FileHeader is a header for remote *os.File operations for remotefs.OpenFile -type FileHeader struct { - Cmd uint32 - Size uint64 -} - -const ( - // Read request command. - Read uint32 = iota - // Write request command. - Write - // Seek request command. - Seek - // Close request command. - Close - // CmdOK is a response meaning request succeeded. - CmdOK - // CmdFailed is a response meaning request failed. - CmdFailed -) - -// SeekHeader is header for the Seek operation for remotefs.OpenFile -type SeekHeader struct { - Offset int64 - Whence int32 -} diff --git a/vendor/github.com/Microsoft/opengcs/service/gcsutils/remotefs/remotefs.go b/vendor/github.com/Microsoft/opengcs/service/gcsutils/remotefs/remotefs.go deleted file mode 100644 index ebc9e01df2fa6..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/service/gcsutils/remotefs/remotefs.go +++ /dev/null @@ -1,578 +0,0 @@ -// +build !windows - -package remotefs - -import ( - "bytes" - "encoding/binary" - "encoding/json" - "io" - "os" - "path/filepath" - "strconv" - - "github.com/docker/docker/pkg/archive" - "github.com/docker/docker/pkg/symlink" - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" -) - -// Func is the function definition for a generic remote fs function -// The input to the function is any serialized structs / data from in and the string slice -// from args. The output of the function will be serialized and written to out. -type Func func(stdin io.Reader, stdout io.Writer, args []string) error - -// Commands provide a string -> remotefs function mapping. -// This is useful for commandline programs that will receive a string -// as the function to execute. -var Commands = map[string]Func{ - StatCmd: Stat, - LstatCmd: Lstat, - ReadlinkCmd: Readlink, - MkdirCmd: Mkdir, - MkdirAllCmd: MkdirAll, - RemoveCmd: Remove, - RemoveAllCmd: RemoveAll, - LinkCmd: Link, - SymlinkCmd: Symlink, - LchmodCmd: Lchmod, - LchownCmd: Lchown, - MknodCmd: Mknod, - MkfifoCmd: Mkfifo, - OpenFileCmd: OpenFile, - ReadFileCmd: ReadFile, - WriteFileCmd: WriteFile, - ReadDirCmd: ReadDir, - ResolvePathCmd: ResolvePath, - ExtractArchiveCmd: ExtractArchive, - ArchivePathCmd: ArchivePath, -} - -// Stat functions like os.Stat. -// Args: -// - args[0] is the path -// Out: -// - out = FileInfo object -func Stat(in io.Reader, out io.Writer, args []string) error { - return stat(in, out, args, os.Stat) -} - -// Lstat functions like os.Lstat. -// Args: -// - args[0] is the path -// Out: -// - out = FileInfo object -func Lstat(in io.Reader, out io.Writer, args []string) error { - return stat(in, out, args, os.Lstat) -} - -func stat(in io.Reader, out io.Writer, args []string, statfunc func(string) (os.FileInfo, error)) error { - if len(args) < 1 { - return ErrInvalid - } - - fi, err := statfunc(args[0]) - if err != nil { - return err - } - - info := FileInfo{ - NameVar: fi.Name(), - SizeVar: fi.Size(), - ModeVar: fi.Mode(), - ModTimeVar: fi.ModTime().UnixNano(), - IsDirVar: fi.IsDir(), - } - - buf, err := json.Marshal(info) - if err != nil { - return err - } - - if _, err := out.Write(buf); err != nil { - return err - } - return nil -} - -// Readlink works like os.Readlink -// In: -// - args[0] is path -// Out: -// - Write link result to out -func Readlink(in io.Reader, out io.Writer, args []string) error { - if len(args) < 1 { - return ErrInvalid - } - - l, err := os.Readlink(args[0]) - if err != nil { - return err - } - - if _, err := out.Write([]byte(l)); err != nil { - return err - } - return nil -} - -// Mkdir works like os.Mkdir -// Args: -// - args[0] is the path -// - args[1] is the permissions in octal (like 0755) -func Mkdir(in io.Reader, out io.Writer, args []string) error { - return mkdir(in, out, args, os.Mkdir) -} - -// MkdirAll works like os.MkdirAll. -// Args: -// - args[0] is the path -// - args[1] is the permissions in octal (like 0755) -func MkdirAll(in io.Reader, out io.Writer, args []string) error { - return mkdir(in, out, args, os.MkdirAll) -} - -func mkdir(in io.Reader, out io.Writer, args []string, mkdirFunc func(string, os.FileMode) error) error { - if len(args) < 2 { - return ErrInvalid - } - - perm, err := strconv.ParseUint(args[1], 8, 32) - if err != nil { - return err - } - return mkdirFunc(args[0], os.FileMode(perm)) -} - -// Remove works like os.Remove -// Args: -// - args[0] is the path -func Remove(in io.Reader, out io.Writer, args []string) error { - return remove(in, out, args, os.Remove) -} - -// RemoveAll works like os.RemoveAll -// Args: -// - args[0] is the path -func RemoveAll(in io.Reader, out io.Writer, args []string) error { - return remove(in, out, args, os.RemoveAll) -} - -func remove(in io.Reader, out io.Writer, args []string, removefunc func(string) error) error { - if len(args) < 1 { - return ErrInvalid - } - return removefunc(args[0]) -} - -// Link works like os.Link -// Args: -// - args[0] = old path name (link source) -// - args[1] = new path name (link dest) -func Link(in io.Reader, out io.Writer, args []string) error { - return link(in, out, args, os.Link) -} - -// Symlink works like os.Symlink -// Args: -// - args[0] = old path name (link source) -// - args[1] = new path name (link dest) -func Symlink(in io.Reader, out io.Writer, args []string) error { - return link(in, out, args, os.Symlink) -} - -func link(in io.Reader, out io.Writer, args []string, linkfunc func(string, string) error) error { - if len(args) < 2 { - return ErrInvalid - } - return linkfunc(args[0], args[1]) -} - -// Lchmod changes permission of the given file without following symlinks -// Args: -// - args[0] = path -// - args[1] = permission mode in octal (like 0755) -func Lchmod(in io.Reader, out io.Writer, args []string) error { - if len(args) < 2 { - return ErrInvalid - } - - perm, err := strconv.ParseUint(args[1], 8, 32) - if err != nil { - return err - } - - path := args[0] - if !filepath.IsAbs(path) { - path, err = filepath.Abs(path) - if err != nil { - return err - } - } - return unix.Fchmodat(0, path, uint32(perm), unix.AT_SYMLINK_NOFOLLOW) -} - -// Lchown works like os.Lchown -// Args: -// - args[0] = path -// - args[1] = uid in base 10 -// - args[2] = gid in base 10 -func Lchown(in io.Reader, out io.Writer, args []string) error { - if len(args) < 3 { - return ErrInvalid - } - - uid, err := strconv.ParseInt(args[1], 10, 64) - if err != nil { - return err - } - - gid, err := strconv.ParseInt(args[2], 10, 64) - if err != nil { - return err - } - return os.Lchown(args[0], int(uid), int(gid)) -} - -// Mknod works like syscall.Mknod -// Args: -// - args[0] = path -// - args[1] = permission mode in octal (like 0755) -// - args[2] = major device number in base 10 -// - args[3] = minor device number in base 10 -func Mknod(in io.Reader, out io.Writer, args []string) error { - if len(args) < 4 { - return ErrInvalid - } - - perm, err := strconv.ParseUint(args[1], 8, 32) - if err != nil { - return err - } - - major, err := strconv.ParseInt(args[2], 10, 32) - if err != nil { - return err - } - - minor, err := strconv.ParseInt(args[3], 10, 32) - if err != nil { - return err - } - - dev := unix.Mkdev(uint32(major), uint32(minor)) - return unix.Mknod(args[0], uint32(perm), int(dev)) -} - -// Mkfifo creates a FIFO special file with the given path name and permissions -// Args: -// - args[0] = path -// - args[1] = permission mode in octal (like 0755) -func Mkfifo(in io.Reader, out io.Writer, args []string) error { - if len(args) < 2 { - return ErrInvalid - } - - perm, err := strconv.ParseUint(args[1], 8, 32) - if err != nil { - return err - } - return unix.Mkfifo(args[0], uint32(perm)) -} - -// OpenFile works like os.OpenFile. To manage the file pointer state, -// this function acts as a single file "file server" with Read/Write/Close -// being serialized control codes from in. -// Args: -// - args[0] = path -// - args[1] = flag in base 10 -// - args[2] = permission mode in octal (like 0755) -func OpenFile(in io.Reader, out io.Writer, args []string) (err error) { - logrus.Debugf("OpenFile: %v", args) - - defer func() { - if err != nil { - logrus.Errorf("OpenFile: return is non-nil, so writing cmdFailed back: %v", err) - // error code will be serialized by the caller, so don't write it here - WriteFileHeader(out, &FileHeader{Cmd: CmdFailed}, nil) - } - }() - - if len(args) < 3 { - logrus.Errorf("OpenFile: Not enough parameters") - return ErrInvalid - } - - flag, err := strconv.ParseInt(args[1], 10, 32) - if err != nil { - logrus.Errorf("OpenFile: Invalid flag: %v", err) - return err - } - - perm, err := strconv.ParseUint(args[2], 8, 32) - if err != nil { - logrus.Errorf("OpenFile: Invalid permission: %v", err) - return err - } - - f, err := os.OpenFile(args[0], int(flag), os.FileMode(perm)) - if err != nil { - logrus.Errorf("OpenFile: Failed to open: %v", err) - return err - } - - // Signal the client that OpenFile succeeded - logrus.Debugf("OpenFile: Sending OK header") - if err := WriteFileHeader(out, &FileHeader{Cmd: CmdOK}, nil); err != nil { - return err - } - - for { - logrus.Debugf("OpenFile: reading header") - hdr, err := ReadFileHeader(in) - if err != nil { - logrus.Errorf("OpenFile: Failed to ReadFileHeader: %v", err) - return err - } - logrus.Debugf("OpenFile: Header: %+v", hdr) - - var buf []byte - switch hdr.Cmd { - case Read: - logrus.Debugf("OpenFile: Read command") - buf = make([]byte, hdr.Size, hdr.Size) - n, err := f.Read(buf) - logrus.Debugf("OpenFile: Issued a read for %d, got %d bytes and error %v", hdr.Size, n, err) - if err != nil { - logrus.Errorf("OpenFile: Read failed: %v", err) - return err - } - buf = buf[:n] - case Write: - logrus.Debugf("OpenFile: Write command") - if _, err := io.CopyN(f, in, int64(hdr.Size)); err != nil { - logrus.Errorf("OpenFile: Write CopyN() failed: %v", err) - return err - } - case Seek: - logrus.Debugf("OpenFile: Seek command") - seekHdr := &SeekHeader{} - if err := binary.Read(in, binary.BigEndian, seekHdr); err != nil { - logrus.Errorf("OpenFile: Seek Read() failed: %v", err) - return err - } - res, err := f.Seek(seekHdr.Offset, int(seekHdr.Whence)) - if err != nil { - logrus.Errorf("OpenFile: Seek Seek() failed: %v", err) - return err - } - buffer := &bytes.Buffer{} - if err := binary.Write(buffer, binary.BigEndian, res); err != nil { - logrus.Errorf("OpenFile: Seek Write() failed: %v", err) - return err - } - buf = buffer.Bytes() - case Close: - logrus.Debugf("OpenFile: Close command") - if err := f.Close(); err != nil { - return err - } - default: - logrus.Errorf("OpenFile: unknown command") - return ErrUnknown - } - - logrus.Debugf("OpenFile: Writing back OK header of size %d", len(buf)) - retHdr := &FileHeader{ - Cmd: CmdOK, - Size: uint64(len(buf)), - } - if err := WriteFileHeader(out, retHdr, buf); err != nil { - logrus.Errorf("OpenFile: WriteFileHeader() failed: %v", err) - return err - } - - if hdr.Cmd == Close { - break - } - } - logrus.Debugf("OpenFile: Done, no error") - return nil -} - -// ReadFile works like ioutil.ReadFile but instead writes the file to a writer -// Args: -// - args[0] = path -// Out: -// - Write file contents to out -func ReadFile(in io.Reader, out io.Writer, args []string) error { - if len(args) < 1 { - return ErrInvalid - } - - f, err := os.Open(args[0]) - if err != nil { - return err - } - defer f.Close() - - if _, err := io.Copy(out, f); err != nil { - return nil - } - return nil -} - -// WriteFile works like ioutil.WriteFile but instead reads the file from a reader -// Args: -// - args[0] = path -// - args[1] = permission mode in octal (like 0755) -// - input data stream from in -func WriteFile(in io.Reader, out io.Writer, args []string) error { - if len(args) < 2 { - return ErrInvalid - } - - perm, err := strconv.ParseUint(args[1], 8, 32) - if err != nil { - return err - } - - f, err := os.OpenFile(args[0], os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(perm)) - if err != nil { - return err - } - defer f.Close() - - if _, err := io.Copy(f, in); err != nil { - return err - } - return nil -} - -// ReadDir works like *os.File.Readdir but instead writes the result to a writer -// Args: -// - args[0] = path -// - args[1] = number of directory entries to return. If <= 0, return all entries in directory -func ReadDir(in io.Reader, out io.Writer, args []string) error { - if len(args) < 2 { - return ErrInvalid - } - - n, err := strconv.ParseInt(args[1], 10, 32) - if err != nil { - return err - } - - f, err := os.Open(args[0]) - if err != nil { - return err - } - defer f.Close() - - infos, err := f.Readdir(int(n)) - if err != nil { - return err - } - - fileInfos := make([]FileInfo, len(infos)) - for i := range infos { - fileInfos[i] = FileInfo{ - NameVar: infos[i].Name(), - SizeVar: infos[i].Size(), - ModeVar: infos[i].Mode(), - ModTimeVar: infos[i].ModTime().UnixNano(), - IsDirVar: infos[i].IsDir(), - } - } - - buf, err := json.Marshal(fileInfos) - if err != nil { - return err - } - - if _, err := out.Write(buf); err != nil { - return err - } - return nil -} - -// ResolvePath works like docker's symlink.FollowSymlinkInScope. -// It takens in a `path` and a `root` and evaluates symlinks in `path` -// as if they were scoped in `root`. `path` must be a child path of `root`. -// In other words, `path` must have `root` as a prefix. -// Example: -// path=/foo/bar -> /baz -// root=/foo, -// Expected result = /foo/baz -// -// Args: -// - args[0] is `path` -// - args[1] is `root` -// Out: -// - Write resolved path to stdout -func ResolvePath(in io.Reader, out io.Writer, args []string) error { - if len(args) < 2 { - return ErrInvalid - } - res, err := symlink.FollowSymlinkInScope(args[0], args[1]) - if err != nil { - return err - } - if _, err = out.Write([]byte(res)); err != nil { - return err - } - return nil -} - -// ExtractArchive extracts the archive read from in. -// Args: -// - in = size of json | json of archive.TarOptions | input tar stream -// - args[0] = extract directory name -func ExtractArchive(in io.Reader, out io.Writer, args []string) error { - logrus.Debugln("ExtractArchive:", args) - if len(args) < 1 { - logrus.Errorln("ExtractArchive: invalid args") - return ErrInvalid - } - - opts, err := ReadTarOptions(in) - if err != nil { - logrus.Errorf("ExtractArchive: Failed to read tar options: %v", err) - return err - } - - logrus.Debugf("ExtractArchive: Tar options: %+v", opts) - if err := archive.Untar(in, args[0], opts); err != nil { - logrus.Errorf("ExtractArchive: Failed to Untar: %v", err) - return err - } - logrus.Debugf("ExtractArchive: Success") - return nil -} - -// ArchivePath archives the given directory and writes it to out. -// Args: -// - in = size of json | json of archive.TarOptions -// - args[0] = source directory name -// Out: -// - out = tar file of the archive -func ArchivePath(in io.Reader, out io.Writer, args []string) error { - if len(args) < 1 { - return ErrInvalid - } - - opts, err := ReadTarOptions(in) - if err != nil { - return err - } - - r, err := archive.TarWithOptions(args[0], opts) - if err != nil { - return err - } - - if _, err := io.Copy(out, r); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/Microsoft/opengcs/service/gcsutils/remotefs/utils.go b/vendor/github.com/Microsoft/opengcs/service/gcsutils/remotefs/utils.go deleted file mode 100644 index 727fd50e5ad3d..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/service/gcsutils/remotefs/utils.go +++ /dev/null @@ -1,170 +0,0 @@ -package remotefs - -import ( - "bytes" - "encoding/binary" - "encoding/json" - "io" - "io/ioutil" - "os" - "syscall" - - "github.com/docker/docker/pkg/archive" -) - -// ReadError is an utility function that reads a serialized error from the given reader -// and deserializes it. -func ReadError(in io.Reader) (*ExportedError, error) { - b, err := ioutil.ReadAll(in) - if err != nil { - return nil, err - } - - // No error - if len(b) == 0 { - return nil, nil - } - - var exportedErr ExportedError - if err := json.Unmarshal(b, &exportedErr); err != nil { - return nil, err - } - - return &exportedErr, nil -} - -// ExportedToError will convert a ExportedError to an error. It will try to match -// the error to any existing known error like os.ErrNotExist. Otherwise, it will just -// return an implementation of the error interface. -func ExportedToError(ee *ExportedError) error { - if ee.Error() == os.ErrNotExist.Error() { - return os.ErrNotExist - } else if ee.Error() == os.ErrExist.Error() { - return os.ErrExist - } else if ee.Error() == os.ErrPermission.Error() { - return os.ErrPermission - } else if ee.Error() == io.EOF.Error() { - return io.EOF - } - return ee -} - -// WriteError is an utility function that serializes the error -// and writes it to the output writer. -func WriteError(err error, out io.Writer) error { - if err == nil { - return nil - } - err = fixOSError(err) - - var errno int - switch typedError := err.(type) { - case *os.PathError: - if se, ok := typedError.Err.(syscall.Errno); ok { - errno = int(se) - } - case *os.LinkError: - if se, ok := typedError.Err.(syscall.Errno); ok { - errno = int(se) - } - case *os.SyscallError: - if se, ok := typedError.Err.(syscall.Errno); ok { - errno = int(se) - } - } - - exportedError := &ExportedError{ - ErrString: err.Error(), - ErrNum: errno, - } - - b, err1 := json.Marshal(exportedError) - if err1 != nil { - return err1 - } - - _, err1 = out.Write(b) - if err1 != nil { - return err1 - } - return nil -} - -// fixOSError converts possible platform dependent error into the portable errors in the -// Go os package if possible. -func fixOSError(err error) error { - // The os.IsExist, os.IsNotExist, and os.IsPermissions functions are platform - // dependent, so sending the raw error might break those functions on a different OS. - // Go defines portable errors for these. - if os.IsExist(err) { - return os.ErrExist - } else if os.IsNotExist(err) { - return os.ErrNotExist - } else if os.IsPermission(err) { - return os.ErrPermission - } - return err -} - -// ReadTarOptions reads from the specified reader and deserializes an archive.TarOptions struct. -func ReadTarOptions(r io.Reader) (*archive.TarOptions, error) { - var size uint64 - if err := binary.Read(r, binary.BigEndian, &size); err != nil { - return nil, err - } - - rawJSON := make([]byte, size) - if _, err := io.ReadFull(r, rawJSON); err != nil { - return nil, err - } - - var opts archive.TarOptions - if err := json.Unmarshal(rawJSON, &opts); err != nil { - return nil, err - } - return &opts, nil -} - -// WriteTarOptions serializes a archive.TarOptions struct and writes it to the writer. -func WriteTarOptions(w io.Writer, opts *archive.TarOptions) error { - optsBuf, err := json.Marshal(opts) - if err != nil { - return err - } - - optsSize := uint64(len(optsBuf)) - optsSizeBuf := &bytes.Buffer{} - if err := binary.Write(optsSizeBuf, binary.BigEndian, optsSize); err != nil { - return err - } - - if _, err := optsSizeBuf.WriteTo(w); err != nil { - return err - } - - if _, err := w.Write(optsBuf); err != nil { - return err - } - - return nil -} - -// ReadFileHeader reads from r and returns a deserialized FileHeader -func ReadFileHeader(r io.Reader) (*FileHeader, error) { - hdr := &FileHeader{} - if err := binary.Read(r, binary.BigEndian, hdr); err != nil { - return nil, err - } - return hdr, nil -} - -// WriteFileHeader serializes a FileHeader and writes it to w, along with any extra data -func WriteFileHeader(w io.Writer, hdr *FileHeader, extraData []byte) error { - if err := binary.Write(w, binary.BigEndian, hdr); err != nil { - return err - } - if _, err := w.Write(extraData); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/Microsoft/opengcs/vsockexec/vsockexec.c b/vendor/github.com/Microsoft/opengcs/vsockexec/vsockexec.c deleted file mode 100644 index 7a26d4c0da250..0000000000000 --- a/vendor/github.com/Microsoft/opengcs/vsockexec/vsockexec.c +++ /dev/null @@ -1,129 +0,0 @@ -// vsockexec opens vsock connections for the specified stdio descriptors and -// then execs the specified process. - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef USE_TCP -static const int tcpmode = 1; -#else -static const int tcpmode; -#endif - -static int openvsock(unsigned int cid, unsigned int port) -{ - int s = socket(AF_VSOCK, SOCK_STREAM, 0); - if (s < 0) { - perror("socket: AF_VSOCK"); - return -1; - } - - struct sockaddr_vm addr = {0}; - addr.svm_family = AF_VSOCK; - addr.svm_port = port; - addr.svm_cid = cid; - if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - fprintf(stderr, "connect: port %u: %s", port, strerror(errno)); - return -1; - } - - return s; -} - -static int opentcp(unsigned short port) -{ - int s = socket(AF_INET, SOCK_STREAM, 0); - if (s < 0) { - perror("socket: AF_INET"); - return -1; - } - - struct sockaddr_in addr = {0}; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - fprintf(stderr, "connect: port %u: %s\n", port, strerror(errno)); - return -1; - } - - return s; -} - -_Noreturn static void usage(const char *argv0) -{ - fprintf(stderr, "%s [-i port] [-o port] [-e port] -- program [args...]\n", argv0); - exit(1); -} - -int main(int argc, char **argv) -{ - unsigned int ports[3] = {0}; - int sockets[3] = {-1, -1, -1}; - int c; - while ((c = getopt(argc, argv, "+i:o:e:")) != -1) { - switch (c) { - case 'i': - ports[0] = strtoul(optarg, NULL, 10); - break; - - case 'o': - ports[1] = strtoul(optarg, NULL, 10); - break; - - case 'e': - ports[2] = strtoul(optarg, NULL, 10); - break; - - default: - usage(argv[0]); - } - } - - if (optind == argc) { - fprintf(stderr, "%s: missing program argument\n", argv[0]); - usage(argv[0]); - } - - for (int i = 0; i < 3; i++) { - if (ports[i] != 0) { - int j; - for (j = 0; j < i; j++) { - if (ports[i] == ports[j]) { - int s = dup(sockets[j]); - if (s < 0) { - perror("dup"); - return 1; - } - sockets[i] = s; - break; - } - } - - if (j == i) { - int s = tcpmode ? opentcp(ports[i]) : openvsock(VMADDR_CID_HOST, ports[i]); - if (s < 0) { - return 1; - } - sockets[i] = s; - } - } - } - - for (int i = 0; i < 3; i++) { - if (sockets[i] >= 0) { - dup2(sockets[i], i); - close(sockets[i]); - } - } - - execvp(argv[optind], argv + optind); - fprintf(stderr, "execvp: %s: %s\n", argv[optind], strerror(errno)); - return 1; -} diff --git a/vendor/github.com/Nvveen/Gotty/LICENSE b/vendor/github.com/Nvveen/Gotty/LICENSE deleted file mode 100644 index 0b71c97360eb9..0000000000000 --- a/vendor/github.com/Nvveen/Gotty/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ -Copyright (c) 2012, Neal van Veen (nealvanveen@gmail.com) -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those -of the authors and should not be interpreted as representing official policies, -either expressed or implied, of the FreeBSD Project. diff --git a/vendor/github.com/Nvveen/Gotty/README b/vendor/github.com/Nvveen/Gotty/README deleted file mode 100644 index a6b0d9a8fe25e..0000000000000 --- a/vendor/github.com/Nvveen/Gotty/README +++ /dev/null @@ -1,5 +0,0 @@ -Gotty is a library written in Go that determines and reads termcap database -files to produce an interface for interacting with the capabilities of a -terminal. -See the godoc documentation or the source code for more information about -function usage. diff --git a/vendor/github.com/Nvveen/Gotty/attributes.go b/vendor/github.com/Nvveen/Gotty/attributes.go deleted file mode 100644 index a4c005fae5836..0000000000000 --- a/vendor/github.com/Nvveen/Gotty/attributes.go +++ /dev/null @@ -1,514 +0,0 @@ -// Copyright 2012 Neal van Veen. All rights reserved. -// Usage of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package gotty - -// Boolean capabilities -var BoolAttr = [...]string{ - "auto_left_margin", "bw", - "auto_right_margin", "am", - "no_esc_ctlc", "xsb", - "ceol_standout_glitch", "xhp", - "eat_newline_glitch", "xenl", - "erase_overstrike", "eo", - "generic_type", "gn", - "hard_copy", "hc", - "has_meta_key", "km", - "has_status_line", "hs", - "insert_null_glitch", "in", - "memory_above", "da", - "memory_below", "db", - "move_insert_mode", "mir", - "move_standout_mode", "msgr", - "over_strike", "os", - "status_line_esc_ok", "eslok", - "dest_tabs_magic_smso", "xt", - "tilde_glitch", "hz", - "transparent_underline", "ul", - "xon_xoff", "nxon", - "needs_xon_xoff", "nxon", - "prtr_silent", "mc5i", - "hard_cursor", "chts", - "non_rev_rmcup", "nrrmc", - "no_pad_char", "npc", - "non_dest_scroll_region", "ndscr", - "can_change", "ccc", - "back_color_erase", "bce", - "hue_lightness_saturation", "hls", - "col_addr_glitch", "xhpa", - "cr_cancels_micro_mode", "crxm", - "has_print_wheel", "daisy", - "row_addr_glitch", "xvpa", - "semi_auto_right_margin", "sam", - "cpi_changes_res", "cpix", - "lpi_changes_res", "lpix", - "backspaces_with_bs", "", - "crt_no_scrolling", "", - "no_correctly_working_cr", "", - "gnu_has_meta_key", "", - "linefeed_is_newline", "", - "has_hardware_tabs", "", - "return_does_clr_eol", "", -} - -// Numerical capabilities -var NumAttr = [...]string{ - "columns", "cols", - "init_tabs", "it", - "lines", "lines", - "lines_of_memory", "lm", - "magic_cookie_glitch", "xmc", - "padding_baud_rate", "pb", - "virtual_terminal", "vt", - "width_status_line", "wsl", - "num_labels", "nlab", - "label_height", "lh", - "label_width", "lw", - "max_attributes", "ma", - "maximum_windows", "wnum", - "max_colors", "colors", - "max_pairs", "pairs", - "no_color_video", "ncv", - "buffer_capacity", "bufsz", - "dot_vert_spacing", "spinv", - "dot_horz_spacing", "spinh", - "max_micro_address", "maddr", - "max_micro_jump", "mjump", - "micro_col_size", "mcs", - "micro_line_size", "mls", - "number_of_pins", "npins", - "output_res_char", "orc", - "output_res_line", "orl", - "output_res_horz_inch", "orhi", - "output_res_vert_inch", "orvi", - "print_rate", "cps", - "wide_char_size", "widcs", - "buttons", "btns", - "bit_image_entwining", "bitwin", - "bit_image_type", "bitype", - "magic_cookie_glitch_ul", "", - "carriage_return_delay", "", - "new_line_delay", "", - "backspace_delay", "", - "horizontal_tab_delay", "", - "number_of_function_keys", "", -} - -// String capabilities -var StrAttr = [...]string{ - "back_tab", "cbt", - "bell", "bel", - "carriage_return", "cr", - "change_scroll_region", "csr", - "clear_all_tabs", "tbc", - "clear_screen", "clear", - "clr_eol", "el", - "clr_eos", "ed", - "column_address", "hpa", - "command_character", "cmdch", - "cursor_address", "cup", - "cursor_down", "cud1", - "cursor_home", "home", - "cursor_invisible", "civis", - "cursor_left", "cub1", - "cursor_mem_address", "mrcup", - "cursor_normal", "cnorm", - "cursor_right", "cuf1", - "cursor_to_ll", "ll", - "cursor_up", "cuu1", - "cursor_visible", "cvvis", - "delete_character", "dch1", - "delete_line", "dl1", - "dis_status_line", "dsl", - "down_half_line", "hd", - "enter_alt_charset_mode", "smacs", - "enter_blink_mode", "blink", - "enter_bold_mode", "bold", - "enter_ca_mode", "smcup", - "enter_delete_mode", "smdc", - "enter_dim_mode", "dim", - "enter_insert_mode", "smir", - "enter_secure_mode", "invis", - "enter_protected_mode", "prot", - "enter_reverse_mode", "rev", - "enter_standout_mode", "smso", - "enter_underline_mode", "smul", - "erase_chars", "ech", - "exit_alt_charset_mode", "rmacs", - "exit_attribute_mode", "sgr0", - "exit_ca_mode", "rmcup", - "exit_delete_mode", "rmdc", - "exit_insert_mode", "rmir", - "exit_standout_mode", "rmso", - "exit_underline_mode", "rmul", - "flash_screen", "flash", - "form_feed", "ff", - "from_status_line", "fsl", - "init_1string", "is1", - "init_2string", "is2", - "init_3string", "is3", - "init_file", "if", - "insert_character", "ich1", - "insert_line", "il1", - "insert_padding", "ip", - "key_backspace", "kbs", - "key_catab", "ktbc", - "key_clear", "kclr", - "key_ctab", "kctab", - "key_dc", "kdch1", - "key_dl", "kdl1", - "key_down", "kcud1", - "key_eic", "krmir", - "key_eol", "kel", - "key_eos", "ked", - "key_f0", "kf0", - "key_f1", "kf1", - "key_f10", "kf10", - "key_f2", "kf2", - "key_f3", "kf3", - "key_f4", "kf4", - "key_f5", "kf5", - "key_f6", "kf6", - "key_f7", "kf7", - "key_f8", "kf8", - "key_f9", "kf9", - "key_home", "khome", - "key_ic", "kich1", - "key_il", "kil1", - "key_left", "kcub1", - "key_ll", "kll", - "key_npage", "knp", - "key_ppage", "kpp", - "key_right", "kcuf1", - "key_sf", "kind", - "key_sr", "kri", - "key_stab", "khts", - "key_up", "kcuu1", - "keypad_local", "rmkx", - "keypad_xmit", "smkx", - "lab_f0", "lf0", - "lab_f1", "lf1", - "lab_f10", "lf10", - "lab_f2", "lf2", - "lab_f3", "lf3", - "lab_f4", "lf4", - "lab_f5", "lf5", - "lab_f6", "lf6", - "lab_f7", "lf7", - "lab_f8", "lf8", - "lab_f9", "lf9", - "meta_off", "rmm", - "meta_on", "smm", - "newline", "_glitch", - "pad_char", "npc", - "parm_dch", "dch", - "parm_delete_line", "dl", - "parm_down_cursor", "cud", - "parm_ich", "ich", - "parm_index", "indn", - "parm_insert_line", "il", - "parm_left_cursor", "cub", - "parm_right_cursor", "cuf", - "parm_rindex", "rin", - "parm_up_cursor", "cuu", - "pkey_key", "pfkey", - "pkey_local", "pfloc", - "pkey_xmit", "pfx", - "print_screen", "mc0", - "prtr_off", "mc4", - "prtr_on", "mc5", - "repeat_char", "rep", - "reset_1string", "rs1", - "reset_2string", "rs2", - "reset_3string", "rs3", - "reset_file", "rf", - "restore_cursor", "rc", - "row_address", "mvpa", - "save_cursor", "row_address", - "scroll_forward", "ind", - "scroll_reverse", "ri", - "set_attributes", "sgr", - "set_tab", "hts", - "set_window", "wind", - "tab", "s_magic_smso", - "to_status_line", "tsl", - "underline_char", "uc", - "up_half_line", "hu", - "init_prog", "iprog", - "key_a1", "ka1", - "key_a3", "ka3", - "key_b2", "kb2", - "key_c1", "kc1", - "key_c3", "kc3", - "prtr_non", "mc5p", - "char_padding", "rmp", - "acs_chars", "acsc", - "plab_norm", "pln", - "key_btab", "kcbt", - "enter_xon_mode", "smxon", - "exit_xon_mode", "rmxon", - "enter_am_mode", "smam", - "exit_am_mode", "rmam", - "xon_character", "xonc", - "xoff_character", "xoffc", - "ena_acs", "enacs", - "label_on", "smln", - "label_off", "rmln", - "key_beg", "kbeg", - "key_cancel", "kcan", - "key_close", "kclo", - "key_command", "kcmd", - "key_copy", "kcpy", - "key_create", "kcrt", - "key_end", "kend", - "key_enter", "kent", - "key_exit", "kext", - "key_find", "kfnd", - "key_help", "khlp", - "key_mark", "kmrk", - "key_message", "kmsg", - "key_move", "kmov", - "key_next", "knxt", - "key_open", "kopn", - "key_options", "kopt", - "key_previous", "kprv", - "key_print", "kprt", - "key_redo", "krdo", - "key_reference", "kref", - "key_refresh", "krfr", - "key_replace", "krpl", - "key_restart", "krst", - "key_resume", "kres", - "key_save", "ksav", - "key_suspend", "kspd", - "key_undo", "kund", - "key_sbeg", "kBEG", - "key_scancel", "kCAN", - "key_scommand", "kCMD", - "key_scopy", "kCPY", - "key_screate", "kCRT", - "key_sdc", "kDC", - "key_sdl", "kDL", - "key_select", "kslt", - "key_send", "kEND", - "key_seol", "kEOL", - "key_sexit", "kEXT", - "key_sfind", "kFND", - "key_shelp", "kHLP", - "key_shome", "kHOM", - "key_sic", "kIC", - "key_sleft", "kLFT", - "key_smessage", "kMSG", - "key_smove", "kMOV", - "key_snext", "kNXT", - "key_soptions", "kOPT", - "key_sprevious", "kPRV", - "key_sprint", "kPRT", - "key_sredo", "kRDO", - "key_sreplace", "kRPL", - "key_sright", "kRIT", - "key_srsume", "kRES", - "key_ssave", "kSAV", - "key_ssuspend", "kSPD", - "key_sundo", "kUND", - "req_for_input", "rfi", - "key_f11", "kf11", - "key_f12", "kf12", - "key_f13", "kf13", - "key_f14", "kf14", - "key_f15", "kf15", - "key_f16", "kf16", - "key_f17", "kf17", - "key_f18", "kf18", - "key_f19", "kf19", - "key_f20", "kf20", - "key_f21", "kf21", - "key_f22", "kf22", - "key_f23", "kf23", - "key_f24", "kf24", - "key_f25", "kf25", - "key_f26", "kf26", - "key_f27", "kf27", - "key_f28", "kf28", - "key_f29", "kf29", - "key_f30", "kf30", - "key_f31", "kf31", - "key_f32", "kf32", - "key_f33", "kf33", - "key_f34", "kf34", - "key_f35", "kf35", - "key_f36", "kf36", - "key_f37", "kf37", - "key_f38", "kf38", - "key_f39", "kf39", - "key_f40", "kf40", - "key_f41", "kf41", - "key_f42", "kf42", - "key_f43", "kf43", - "key_f44", "kf44", - "key_f45", "kf45", - "key_f46", "kf46", - "key_f47", "kf47", - "key_f48", "kf48", - "key_f49", "kf49", - "key_f50", "kf50", - "key_f51", "kf51", - "key_f52", "kf52", - "key_f53", "kf53", - "key_f54", "kf54", - "key_f55", "kf55", - "key_f56", "kf56", - "key_f57", "kf57", - "key_f58", "kf58", - "key_f59", "kf59", - "key_f60", "kf60", - "key_f61", "kf61", - "key_f62", "kf62", - "key_f63", "kf63", - "clr_bol", "el1", - "clear_margins", "mgc", - "set_left_margin", "smgl", - "set_right_margin", "smgr", - "label_format", "fln", - "set_clock", "sclk", - "display_clock", "dclk", - "remove_clock", "rmclk", - "create_window", "cwin", - "goto_window", "wingo", - "hangup", "hup", - "dial_phone", "dial", - "quick_dial", "qdial", - "tone", "tone", - "pulse", "pulse", - "flash_hook", "hook", - "fixed_pause", "pause", - "wait_tone", "wait", - "user0", "u0", - "user1", "u1", - "user2", "u2", - "user3", "u3", - "user4", "u4", - "user5", "u5", - "user6", "u6", - "user7", "u7", - "user8", "u8", - "user9", "u9", - "orig_pair", "op", - "orig_colors", "oc", - "initialize_color", "initc", - "initialize_pair", "initp", - "set_color_pair", "scp", - "set_foreground", "setf", - "set_background", "setb", - "change_char_pitch", "cpi", - "change_line_pitch", "lpi", - "change_res_horz", "chr", - "change_res_vert", "cvr", - "define_char", "defc", - "enter_doublewide_mode", "swidm", - "enter_draft_quality", "sdrfq", - "enter_italics_mode", "sitm", - "enter_leftward_mode", "slm", - "enter_micro_mode", "smicm", - "enter_near_letter_quality", "snlq", - "enter_normal_quality", "snrmq", - "enter_shadow_mode", "sshm", - "enter_subscript_mode", "ssubm", - "enter_superscript_mode", "ssupm", - "enter_upward_mode", "sum", - "exit_doublewide_mode", "rwidm", - "exit_italics_mode", "ritm", - "exit_leftward_mode", "rlm", - "exit_micro_mode", "rmicm", - "exit_shadow_mode", "rshm", - "exit_subscript_mode", "rsubm", - "exit_superscript_mode", "rsupm", - "exit_upward_mode", "rum", - "micro_column_address", "mhpa", - "micro_down", "mcud1", - "micro_left", "mcub1", - "micro_right", "mcuf1", - "micro_row_address", "mvpa", - "micro_up", "mcuu1", - "order_of_pins", "porder", - "parm_down_micro", "mcud", - "parm_left_micro", "mcub", - "parm_right_micro", "mcuf", - "parm_up_micro", "mcuu", - "select_char_set", "scs", - "set_bottom_margin", "smgb", - "set_bottom_margin_parm", "smgbp", - "set_left_margin_parm", "smglp", - "set_right_margin_parm", "smgrp", - "set_top_margin", "smgt", - "set_top_margin_parm", "smgtp", - "start_bit_image", "sbim", - "start_char_set_def", "scsd", - "stop_bit_image", "rbim", - "stop_char_set_def", "rcsd", - "subscript_characters", "subcs", - "superscript_characters", "supcs", - "these_cause_cr", "docr", - "zero_motion", "zerom", - "char_set_names", "csnm", - "key_mouse", "kmous", - "mouse_info", "minfo", - "req_mouse_pos", "reqmp", - "get_mouse", "getm", - "set_a_foreground", "setaf", - "set_a_background", "setab", - "pkey_plab", "pfxl", - "device_type", "devt", - "code_set_init", "csin", - "set0_des_seq", "s0ds", - "set1_des_seq", "s1ds", - "set2_des_seq", "s2ds", - "set3_des_seq", "s3ds", - "set_lr_margin", "smglr", - "set_tb_margin", "smgtb", - "bit_image_repeat", "birep", - "bit_image_newline", "binel", - "bit_image_carriage_return", "bicr", - "color_names", "colornm", - "define_bit_image_region", "defbi", - "end_bit_image_region", "endbi", - "set_color_band", "setcolor", - "set_page_length", "slines", - "display_pc_char", "dispc", - "enter_pc_charset_mode", "smpch", - "exit_pc_charset_mode", "rmpch", - "enter_scancode_mode", "smsc", - "exit_scancode_mode", "rmsc", - "pc_term_options", "pctrm", - "scancode_escape", "scesc", - "alt_scancode_esc", "scesa", - "enter_horizontal_hl_mode", "ehhlm", - "enter_left_hl_mode", "elhlm", - "enter_low_hl_mode", "elohlm", - "enter_right_hl_mode", "erhlm", - "enter_top_hl_mode", "ethlm", - "enter_vertical_hl_mode", "evhlm", - "set_a_attributes", "sgr1", - "set_pglen_inch", "slength", - "termcap_init2", "", - "termcap_reset", "", - "linefeed_if_not_lf", "", - "backspace_if_not_bs", "", - "other_non_function_keys", "", - "arrow_key_map", "", - "acs_ulcorner", "", - "acs_llcorner", "", - "acs_urcorner", "", - "acs_lrcorner", "", - "acs_ltee", "", - "acs_rtee", "", - "acs_btee", "", - "acs_ttee", "", - "acs_hline", "", - "acs_vline", "", - "acs_plus", "", - "memory_lock", "", - "memory_unlock", "", - "box_chars_1", "", -} diff --git a/vendor/github.com/Nvveen/Gotty/gotty.go b/vendor/github.com/Nvveen/Gotty/gotty.go deleted file mode 100644 index c329778a1d0fe..0000000000000 --- a/vendor/github.com/Nvveen/Gotty/gotty.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2012 Neal van Veen. All rights reserved. -// Usage of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Gotty is a Go-package for reading and parsing the terminfo database -package gotty - -// TODO add more concurrency to name lookup, look for more opportunities. - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "os" - "path" - "reflect" - "strings" - "sync" -) - -// Open a terminfo file by the name given and construct a TermInfo object. -// If something went wrong reading the terminfo database file, an error is -// returned. -func OpenTermInfo(termName string) (*TermInfo, error) { - if len(termName) == 0 { - return nil, errors.New("No termname given") - } - // Find the environment variables - if termloc := os.Getenv("TERMINFO"); len(termloc) > 0 { - return readTermInfo(path.Join(termloc, string(termName[0]), termName)) - } else { - // Search like ncurses - locations := []string{} - if h := os.Getenv("HOME"); len(h) > 0 { - locations = append(locations, path.Join(h, ".terminfo")) - } - locations = append(locations, - "/etc/terminfo/", - "/lib/terminfo/", - "/usr/share/terminfo/") - for _, str := range locations { - term, err := readTermInfo(path.Join(str, string(termName[0]), termName)) - if err == nil { - return term, nil - } - } - return nil, errors.New("No terminfo file(-location) found") - } -} - -// Open a terminfo file from the environment variable containing the current -// terminal name and construct a TermInfo object. If something went wrong -// reading the terminfo database file, an error is returned. -func OpenTermInfoEnv() (*TermInfo, error) { - termenv := os.Getenv("TERM") - return OpenTermInfo(termenv) -} - -// Return an attribute by the name attr provided. If none can be found, -// an error is returned. -func (term *TermInfo) GetAttribute(attr string) (stacker, error) { - // Channel to store the main value in. - var value stacker - // Add a blocking WaitGroup - var block sync.WaitGroup - // Keep track of variable being written. - written := false - // Function to put into goroutine. - f := func(ats interface{}) { - var ok bool - var v stacker - // Switch on type of map to use and assign value to it. - switch reflect.TypeOf(ats).Elem().Kind() { - case reflect.Bool: - v, ok = ats.(map[string]bool)[attr] - case reflect.Int16: - v, ok = ats.(map[string]int16)[attr] - case reflect.String: - v, ok = ats.(map[string]string)[attr] - } - // If ok, a value is found, so we can write. - if ok { - value = v - written = true - } - // Goroutine is done - block.Done() - } - block.Add(3) - // Go for all 3 attribute lists. - go f(term.boolAttributes) - go f(term.numAttributes) - go f(term.strAttributes) - // Wait until every goroutine is done. - block.Wait() - // If a value has been written, return it. - if written { - return value, nil - } - // Otherwise, error. - return nil, fmt.Errorf("Erorr finding attribute") -} - -// Return an attribute by the name attr provided. If none can be found, -// an error is returned. A name is first converted to its termcap value. -func (term *TermInfo) GetAttributeName(name string) (stacker, error) { - tc := GetTermcapName(name) - return term.GetAttribute(tc) -} - -// A utility function that finds and returns the termcap equivalent of a -// variable name. -func GetTermcapName(name string) string { - // Termcap name - var tc string - // Blocking group - var wait sync.WaitGroup - // Function to put into a goroutine - f := func(attrs []string) { - // Find the string corresponding to the name - for i, s := range attrs { - if s == name { - tc = attrs[i+1] - } - } - // Goroutine is finished - wait.Done() - } - wait.Add(3) - // Go for all 3 attribute lists - go f(BoolAttr[:]) - go f(NumAttr[:]) - go f(StrAttr[:]) - // Wait until every goroutine is done - wait.Wait() - // Return the termcap name - return tc -} - -// This function takes a path to a terminfo file and reads it in binary -// form to construct the actual TermInfo file. -func readTermInfo(path string) (*TermInfo, error) { - // Open the terminfo file - file, err := os.Open(path) - defer file.Close() - if err != nil { - return nil, err - } - - // magic, nameSize, boolSize, nrSNum, nrOffsetsStr, strSize - // Header is composed of the magic 0432 octal number, size of the name - // section, size of the boolean section, the amount of number values, - // the number of offsets of strings, and the size of the string section. - var header [6]int16 - // Byte array is used to read in byte values - var byteArray []byte - // Short array is used to read in short values - var shArray []int16 - // TermInfo object to store values - var term TermInfo - - // Read in the header - err = binary.Read(file, binary.LittleEndian, &header) - if err != nil { - return nil, err - } - // If magic number isn't there or isn't correct, we have the wrong filetype - if header[0] != 0432 { - return nil, errors.New(fmt.Sprintf("Wrong filetype")) - } - - // Read in the names - byteArray = make([]byte, header[1]) - err = binary.Read(file, binary.LittleEndian, &byteArray) - if err != nil { - return nil, err - } - term.Names = strings.Split(string(byteArray), "|") - - // Read in the booleans - byteArray = make([]byte, header[2]) - err = binary.Read(file, binary.LittleEndian, &byteArray) - if err != nil { - return nil, err - } - term.boolAttributes = make(map[string]bool) - for i, b := range byteArray { - if b == 1 { - term.boolAttributes[BoolAttr[i*2+1]] = true - } - } - // If the number of bytes read is not even, a byte for alignment is added - // We know the header is an even number of bytes so only need to check the - // total of the names and booleans. - if (header[1]+header[2])%2 != 0 { - err = binary.Read(file, binary.LittleEndian, make([]byte, 1)) - if err != nil { - return nil, err - } - } - - // Read in shorts - shArray = make([]int16, header[3]) - err = binary.Read(file, binary.LittleEndian, &shArray) - if err != nil { - return nil, err - } - term.numAttributes = make(map[string]int16) - for i, n := range shArray { - if n != 0377 && n > -1 { - term.numAttributes[NumAttr[i*2+1]] = n - } - } - - // Read the offsets into the short array - shArray = make([]int16, header[4]) - err = binary.Read(file, binary.LittleEndian, &shArray) - if err != nil { - return nil, err - } - // Read the actual strings in the byte array - byteArray = make([]byte, header[5]) - err = binary.Read(file, binary.LittleEndian, &byteArray) - if err != nil { - return nil, err - } - term.strAttributes = make(map[string]string) - // We get an offset, and then iterate until the string is null-terminated - for i, offset := range shArray { - if offset > -1 { - if int(offset) >= len(byteArray) { - return nil, errors.New("array out of bounds reading string section") - } - r := bytes.IndexByte(byteArray[offset:], 0) - if r == -1 { - return nil, errors.New("missing nul byte reading string section") - } - r += int(offset) - term.strAttributes[StrAttr[i*2+1]] = string(byteArray[offset:r]) - } - } - return &term, nil -} diff --git a/vendor/github.com/Nvveen/Gotty/parser.go b/vendor/github.com/Nvveen/Gotty/parser.go deleted file mode 100644 index a9d5d23c5425d..0000000000000 --- a/vendor/github.com/Nvveen/Gotty/parser.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2012 Neal van Veen. All rights reserved. -// Usage of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package gotty - -import ( - "bytes" - "errors" - "fmt" - "regexp" - "strconv" - "strings" -) - -var exp = [...]string{ - "%%", - "%c", - "%s", - "%p(\\d)", - "%P([A-z])", - "%g([A-z])", - "%'(.)'", - "%{([0-9]+)}", - "%l", - "%\\+|%-|%\\*|%/|%m", - "%&|%\\||%\\^", - "%=|%>|%<", - "%A|%O", - "%!|%~", - "%i", - "%(:[\\ #\\-\\+]{0,4})?(\\d+\\.\\d+|\\d+)?[doxXs]", - "%\\?(.*?);", -} - -var regex *regexp.Regexp -var staticVar map[byte]stacker - -// Parses the attribute that is received with name attr and parameters params. -func (term *TermInfo) Parse(attr string, params ...interface{}) (string, error) { - // Get the attribute name first. - iface, err := term.GetAttribute(attr) - str, ok := iface.(string) - if err != nil { - return "", err - } - if !ok { - return str, errors.New("Only string capabilities can be parsed.") - } - // Construct the hidden parser struct so we can use a recursive stack based - // parser. - ps := &parser{} - // Dynamic variables only exist in this context. - ps.dynamicVar = make(map[byte]stacker, 26) - ps.parameters = make([]stacker, len(params)) - // Convert the parameters to insert them into the parser struct. - for i, x := range params { - ps.parameters[i] = x - } - // Recursively walk and return. - result, err := ps.walk(str) - return result, err -} - -// Parses the attribute that is received with name attr and parameters params. -// Only works on full name of a capability that is given, which it uses to -// search for the termcap name. -func (term *TermInfo) ParseName(attr string, params ...interface{}) (string, error) { - tc := GetTermcapName(attr) - return term.Parse(tc, params) -} - -// Identify each token in a stack based manner and do the actual parsing. -func (ps *parser) walk(attr string) (string, error) { - // We use a buffer to get the modified string. - var buf bytes.Buffer - // Next, find and identify all tokens by their indices and strings. - tokens := regex.FindAllStringSubmatch(attr, -1) - if len(tokens) == 0 { - return attr, nil - } - indices := regex.FindAllStringIndex(attr, -1) - q := 0 // q counts the matches of one token - // Iterate through the string per character. - for i := 0; i < len(attr); i++ { - // If the current position is an identified token, execute the following - // steps. - if q < len(indices) && i >= indices[q][0] && i < indices[q][1] { - // Switch on token. - switch { - case tokens[q][0][:2] == "%%": - // Literal percentage character. - buf.WriteByte('%') - case tokens[q][0][:2] == "%c": - // Pop a character. - c, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - buf.WriteByte(c.(byte)) - case tokens[q][0][:2] == "%s": - // Pop a string. - str, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - if _, ok := str.(string); !ok { - return buf.String(), errors.New("Stack head is not a string") - } - buf.WriteString(str.(string)) - case tokens[q][0][:2] == "%p": - // Push a parameter on the stack. - index, err := strconv.ParseInt(tokens[q][1], 10, 8) - index-- - if err != nil { - return buf.String(), err - } - if int(index) >= len(ps.parameters) { - return buf.String(), errors.New("Parameters index out of bound") - } - ps.st.push(ps.parameters[index]) - case tokens[q][0][:2] == "%P": - // Pop a variable from the stack as a dynamic or static variable. - val, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - index := tokens[q][2] - if len(index) > 1 { - errorStr := fmt.Sprintf("%s is not a valid dynamic variables index", - index) - return buf.String(), errors.New(errorStr) - } - // Specify either dynamic or static. - if index[0] >= 'a' && index[0] <= 'z' { - ps.dynamicVar[index[0]] = val - } else if index[0] >= 'A' && index[0] <= 'Z' { - staticVar[index[0]] = val - } - case tokens[q][0][:2] == "%g": - // Push a variable from the stack as a dynamic or static variable. - index := tokens[q][3] - if len(index) > 1 { - errorStr := fmt.Sprintf("%s is not a valid static variables index", - index) - return buf.String(), errors.New(errorStr) - } - var val stacker - if index[0] >= 'a' && index[0] <= 'z' { - val = ps.dynamicVar[index[0]] - } else if index[0] >= 'A' && index[0] <= 'Z' { - val = staticVar[index[0]] - } - ps.st.push(val) - case tokens[q][0][:2] == "%'": - // Push a character constant. - con := tokens[q][4] - if len(con) > 1 { - errorStr := fmt.Sprintf("%s is not a valid character constant", con) - return buf.String(), errors.New(errorStr) - } - ps.st.push(con[0]) - case tokens[q][0][:2] == "%{": - // Push an integer constant. - con, err := strconv.ParseInt(tokens[q][5], 10, 32) - if err != nil { - return buf.String(), err - } - ps.st.push(con) - case tokens[q][0][:2] == "%l": - // Push the length of the string that is popped from the stack. - popStr, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - if _, ok := popStr.(string); !ok { - errStr := fmt.Sprintf("Stack head is not a string") - return buf.String(), errors.New(errStr) - } - ps.st.push(len(popStr.(string))) - case tokens[q][0][:2] == "%?": - // If-then-else construct. First, the whole string is identified and - // then inside this substring, we can specify which parts to switch on. - ifReg, _ := regexp.Compile("%\\?(.*)%t(.*)%e(.*);|%\\?(.*)%t(.*);") - ifTokens := ifReg.FindStringSubmatch(tokens[q][0]) - var ( - ifStr string - err error - ) - // Parse the if-part to determine if-else. - if len(ifTokens[1]) > 0 { - ifStr, err = ps.walk(ifTokens[1]) - } else { // else - ifStr, err = ps.walk(ifTokens[4]) - } - // Return any errors - if err != nil { - return buf.String(), err - } else if len(ifStr) > 0 { - // Self-defined limitation, not sure if this is correct, but didn't - // seem like it. - return buf.String(), errors.New("If-clause cannot print statements") - } - var thenStr string - // Pop the first value that is set by parsing the if-clause. - choose, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - // Switch to if or else. - if choose.(int) == 0 && len(ifTokens[1]) > 0 { - thenStr, err = ps.walk(ifTokens[3]) - } else if choose.(int) != 0 { - if len(ifTokens[1]) > 0 { - thenStr, err = ps.walk(ifTokens[2]) - } else { - thenStr, err = ps.walk(ifTokens[5]) - } - } - if err != nil { - return buf.String(), err - } - buf.WriteString(thenStr) - case tokens[q][0][len(tokens[q][0])-1] == 'd': // Fallthrough for printing - fallthrough - case tokens[q][0][len(tokens[q][0])-1] == 'o': // digits. - fallthrough - case tokens[q][0][len(tokens[q][0])-1] == 'x': - fallthrough - case tokens[q][0][len(tokens[q][0])-1] == 'X': - fallthrough - case tokens[q][0][len(tokens[q][0])-1] == 's': - token := tokens[q][0] - // Remove the : that comes before a flag. - if token[1] == ':' { - token = token[:1] + token[2:] - } - digit, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - // The rest is determined like the normal formatted prints. - digitStr := fmt.Sprintf(token, digit.(int)) - buf.WriteString(digitStr) - case tokens[q][0][:2] == "%i": - // Increment the parameters by one. - if len(ps.parameters) < 2 { - return buf.String(), errors.New("Not enough parameters to increment.") - } - val1, val2 := ps.parameters[0].(int), ps.parameters[1].(int) - val1++ - val2++ - ps.parameters[0], ps.parameters[1] = val1, val2 - default: - // The rest of the tokens is a special case, where two values are - // popped and then operated on by the token that comes after them. - op1, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - op2, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - var result stacker - switch tokens[q][0][:2] { - case "%+": - // Addition - result = op2.(int) + op1.(int) - case "%-": - // Subtraction - result = op2.(int) - op1.(int) - case "%*": - // Multiplication - result = op2.(int) * op1.(int) - case "%/": - // Division - result = op2.(int) / op1.(int) - case "%m": - // Modulo - result = op2.(int) % op1.(int) - case "%&": - // Bitwise AND - result = op2.(int) & op1.(int) - case "%|": - // Bitwise OR - result = op2.(int) | op1.(int) - case "%^": - // Bitwise XOR - result = op2.(int) ^ op1.(int) - case "%=": - // Equals - result = op2 == op1 - case "%>": - // Greater-than - result = op2.(int) > op1.(int) - case "%<": - // Lesser-than - result = op2.(int) < op1.(int) - case "%A": - // Logical AND - result = op2.(bool) && op1.(bool) - case "%O": - // Logical OR - result = op2.(bool) || op1.(bool) - case "%!": - // Logical complement - result = !op1.(bool) - case "%~": - // Bitwise complement - result = ^(op1.(int)) - } - ps.st.push(result) - } - - i = indices[q][1] - 1 - q++ - } else { - // We are not "inside" a token, so just skip until the end or the next - // token, and add all characters to the buffer. - j := i - if q != len(indices) { - for !(j >= indices[q][0] && j < indices[q][1]) { - j++ - } - } else { - j = len(attr) - } - buf.WriteString(string(attr[i:j])) - i = j - } - } - // Return the buffer as a string. - return buf.String(), nil -} - -// Push a stacker-value onto the stack. -func (st *stack) push(s stacker) { - *st = append(*st, s) -} - -// Pop a stacker-value from the stack. -func (st *stack) pop() (stacker, error) { - if len(*st) == 0 { - return nil, errors.New("Stack is empty.") - } - newStack := make(stack, len(*st)-1) - val := (*st)[len(*st)-1] - copy(newStack, (*st)[:len(*st)-1]) - *st = newStack - return val, nil -} - -// Initialize regexes and the static vars (that don't get changed between -// calls. -func init() { - // Initialize the main regex. - expStr := strings.Join(exp[:], "|") - regex, _ = regexp.Compile(expStr) - // Initialize the static variables. - staticVar = make(map[byte]stacker, 26) -} diff --git a/vendor/github.com/Nvveen/Gotty/types.go b/vendor/github.com/Nvveen/Gotty/types.go deleted file mode 100644 index 9bcc65e9b889d..0000000000000 --- a/vendor/github.com/Nvveen/Gotty/types.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2012 Neal van Veen. All rights reserved. -// Usage of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package gotty - -type TermInfo struct { - boolAttributes map[string]bool - numAttributes map[string]int16 - strAttributes map[string]string - // The various names of the TermInfo file. - Names []string -} - -type stacker interface { -} -type stack []stacker - -type parser struct { - st stack - parameters []stacker - dynamicVar map[byte]stacker -} diff --git a/vendor/github.com/RackSec/srslog/README.md b/vendor/github.com/RackSec/srslog/README.md index 1ae1fd4ef8930..dcacc34881a91 100644 --- a/vendor/github.com/RackSec/srslog/README.md +++ b/vendor/github.com/RackSec/srslog/README.md @@ -90,6 +90,22 @@ w.Debug("this is debug") w.Write([]byte("these are some bytes")) ``` +If you need further control over connection attempts, you can use the DialWithCustomDialer +function. To continue with the DialWithTLSConfig example: + +``` +netDialer := &net.Dialer{Timeout: time.Second*5} // easy timeouts +realNetwork := "tcp" // real network, other vars your dail func can close over +dial := func(network, addr string) (net.Conn, error) { + // cannot use "network" here as it'll simply be "custom" which will fail + return tls.DialWithDialer(netDialer, realNetwork, addr, &config) +} + +w, err := DialWithCustomDialer("custom", "192.168.0.52:514", syslog.LOG_ERR, "testtag", dial) +``` + +Your custom dial func can set timeouts, proxy connections, and do whatever else it needs before returning a net.Conn. + # Generating TLS Certificates We've provided a script that you can use to generate a self-signed keypair: diff --git a/vendor/github.com/RackSec/srslog/dialer.go b/vendor/github.com/RackSec/srslog/dialer.go index 47a7b2beafce5..fc7e53860f40c 100644 --- a/vendor/github.com/RackSec/srslog/dialer.go +++ b/vendor/github.com/RackSec/srslog/dialer.go @@ -37,6 +37,7 @@ func (w *Writer) getDialer() dialerFunctionWrapper { dialers := map[string]dialerFunctionWrapper{ "": dialerFunctionWrapper{"unixDialer", w.unixDialer}, "tcp+tls": dialerFunctionWrapper{"tlsDialer", w.tlsDialer}, + "custom": dialerFunctionWrapper{"customDialer", w.customDialer}, } dialer, ok := dialers[w.network] if !ok { @@ -85,3 +86,19 @@ func (w *Writer) basicDialer() (serverConn, string, error) { } return sc, hostname, err } + +// customDialer uses the custom dialer when the Writer was created +// giving developers total control over how connections are made and returned. +// Note it does not check if cdialer is nil, as it should only be referenced from getDialer. +func (w *Writer) customDialer() (serverConn, string, error) { + c, err := w.customDial(w.network, w.raddr) + var sc serverConn + hostname := w.hostname + if err == nil { + sc = &netConn{conn: c} + if hostname == "" { + hostname = c.LocalAddr().String() + } + } + return sc, hostname, err +} diff --git a/vendor/github.com/RackSec/srslog/formatter.go b/vendor/github.com/RackSec/srslog/formatter.go index 7852ad37e4c42..e306fd671305c 100644 --- a/vendor/github.com/RackSec/srslog/formatter.go +++ b/vendor/github.com/RackSec/srslog/formatter.go @@ -6,6 +6,8 @@ import ( "time" ) +const appNameMaxLength = 48 // limit to 48 chars as per RFC5424 + // Formatter is a type of function that takes the consituent parts of a // syslog message and returns a formatted string. A different Formatter is // defined for each different syslog protocol we support. @@ -37,12 +39,20 @@ func RFC3164Formatter(p Priority, hostname, tag, content string) string { return msg } +// if string's length is greater than max, then use the last part +func truncateStartStr(s string, max int) string { + if (len(s) > max) { + return s[len(s) - max:] + } + return s +} + // RFC5424Formatter provides an RFC 5424 compliant message. func RFC5424Formatter(p Priority, hostname, tag, content string) string { timestamp := time.Now().Format(time.RFC3339) pid := os.Getpid() - appName := os.Args[0] - msg := fmt.Sprintf("<%d>%d %s %s %s %d %s %s", + appName := truncateStartStr(os.Args[0], appNameMaxLength) + msg := fmt.Sprintf("<%d>%d %s %s %s %d %s - %s", p, 1, timestamp, hostname, appName, pid, tag, content) return msg } diff --git a/vendor/github.com/RackSec/srslog/srslog.go b/vendor/github.com/RackSec/srslog/srslog.go index b404dff7c5c7e..b47ad72df4d0e 100644 --- a/vendor/github.com/RackSec/srslog/srslog.go +++ b/vendor/github.com/RackSec/srslog/srslog.go @@ -3,8 +3,10 @@ package srslog import ( "crypto/tls" "crypto/x509" + "errors" "io/ioutil" "log" + "net" "os" ) @@ -15,6 +17,10 @@ type serverConn interface { close() error } +// DialFunc is the function signature to be used for a custom dialer callback +// with DialWithCustomDialer +type DialFunc func(string, string) (net.Conn, error) + // New establishes a new connection to the system log daemon. Each // write to the returned Writer sends a log message with the given // priority and prefix. @@ -31,6 +37,22 @@ func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) return DialWithTLSConfig(network, raddr, priority, tag, nil) } +// ErrNilDialFunc is returned from DialWithCustomDialer when a nil DialFunc is passed, +// avoiding a nil pointer deference panic. +var ErrNilDialFunc = errors.New("srslog: nil DialFunc passed to DialWithCustomDialer") + +// DialWithCustomDialer establishes a connection by calling customDial. +// Each write to the returned Writer sends a log message with the given facility, severity and tag. +// Network must be "custom" in order for this package to use customDial. +// While network and raddr will be passed to customDial, it is allowed for customDial to ignore them. +// If customDial is nil, this function returns ErrNilDialFunc. +func DialWithCustomDialer(network, raddr string, priority Priority, tag string, customDial DialFunc) (*Writer, error) { + if customDial == nil { + return nil, ErrNilDialFunc + } + return dialAllParameters(network, raddr, priority, tag, nil, customDial) +} + // DialWithTLSCertPath establishes a secure connection to a log daemon by connecting to // address raddr on the specified network. It uses certPath to load TLS certificates and configure // the secure connection. @@ -59,6 +81,11 @@ func DialWithTLSCert(network, raddr string, priority Priority, tag string, serve // DialWithTLSConfig establishes a secure connection to a log daemon by connecting to // address raddr on the specified network. It uses tlsConfig to configure the secure connection. func DialWithTLSConfig(network, raddr string, priority Priority, tag string, tlsConfig *tls.Config) (*Writer, error) { + return dialAllParameters(network, raddr, priority, tag, tlsConfig, nil) +} + +// implementation of the various functions above +func dialAllParameters(network, raddr string, priority Priority, tag string, tlsConfig *tls.Config, customDial DialFunc) (*Writer, error) { if err := validatePriority(priority); err != nil { return nil, err } @@ -69,12 +96,13 @@ func DialWithTLSConfig(network, raddr string, priority Priority, tag string, tls hostname, _ := os.Hostname() w := &Writer{ - priority: priority, - tag: tag, - hostname: hostname, - network: network, - raddr: raddr, - tlsConfig: tlsConfig, + priority: priority, + tag: tag, + hostname: hostname, + network: network, + raddr: raddr, + tlsConfig: tlsConfig, + customDial: customDial, } _, err := w.connect() diff --git a/vendor/github.com/RackSec/srslog/writer.go b/vendor/github.com/RackSec/srslog/writer.go index ce3d86763544c..86bccba157504 100644 --- a/vendor/github.com/RackSec/srslog/writer.go +++ b/vendor/github.com/RackSec/srslog/writer.go @@ -17,6 +17,9 @@ type Writer struct { framer Framer formatter Formatter + //non-nil if custom dialer set, used in getDialer + customDial DialFunc + mu sync.RWMutex // guards conn conn serverConn } @@ -71,15 +74,20 @@ func (w *Writer) SetFramer(f Framer) { w.framer = f } +// SetHostname changes the hostname for syslog messages if needed. +func (w *Writer) SetHostname(hostname string) { + w.hostname = hostname +} + // Write sends a log message to the syslog daemon using the default priority // passed into `srslog.New` or the `srslog.Dial*` functions. func (w *Writer) Write(b []byte) (int, error) { return w.writeAndRetry(w.priority, string(b)) } -// WriteWithPriority sends a log message with a custom priority +// WriteWithPriority sends a log message with a custom priority. func (w *Writer) WriteWithPriority(p Priority, b []byte) (int, error) { - return w.writeAndRetry(p, string(b)) + return w.writeAndRetryWithPriority(p, string(b)) } // Close closes a connection to the syslog daemon. @@ -149,12 +157,20 @@ func (w *Writer) Debug(m string) (err error) { return err } -func (w *Writer) writeAndRetry(p Priority, s string) (int, error) { - pr := (w.priority & facilityMask) | (p & severityMask) +// writeAndRetry takes a severity and the string to write. Any facility passed to +// it as part of the severity Priority will be ignored. +func (w *Writer) writeAndRetry(severity Priority, s string) (int, error) { + pr := (w.priority & facilityMask) | (severity & severityMask) + + return w.writeAndRetryWithPriority(pr, s) +} +// writeAndRetryWithPriority differs from writeAndRetry in that it allows setting +// of both the facility and the severity. +func (w *Writer) writeAndRetryWithPriority(p Priority, s string) (int, error) { conn := w.getConn() if conn != nil { - if n, err := w.write(conn, pr, s); err == nil { + if n, err := w.write(conn, p, s); err == nil { return n, err } } @@ -163,7 +179,7 @@ func (w *Writer) writeAndRetry(p Priority, s string) (int, error) { if conn, err = w.connect(); err != nil { return 0, err } - return w.write(conn, pr, s) + return w.write(conn, p, s) } // write generates and writes a syslog formatted string. It formats the diff --git a/vendor/github.com/aws/aws-sdk-go/NOTICE.txt b/vendor/github.com/aws/aws-sdk-go/NOTICE.txt index 5f14d1162ed45..899129ecc465b 100644 --- a/vendor/github.com/aws/aws-sdk-go/NOTICE.txt +++ b/vendor/github.com/aws/aws-sdk-go/NOTICE.txt @@ -1,3 +1,3 @@ AWS SDK for Go -Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. Copyright 2014-2015 Stripe, Inc. diff --git a/vendor/github.com/aws/aws-sdk-go/README.md b/vendor/github.com/aws/aws-sdk-go/README.md index c32774491c7c3..27e1aee45a9cc 100644 --- a/vendor/github.com/aws/aws-sdk-go/README.md +++ b/vendor/github.com/aws/aws-sdk-go/README.md @@ -1,35 +1,61 @@ -[![API Reference](http://img.shields.io/badge/api-reference-blue.svg)](http://docs.aws.amazon.com/sdk-for-go/api) [![Join the chat at https://gitter.im/aws/aws-sdk-go](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aws/aws-sdk-go?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://img.shields.io/travis/aws/aws-sdk-go.svg)](https://travis-ci.org/aws/aws-sdk-go) [![Apache V2 License](http://img.shields.io/badge/license-Apache%20V2-blue.svg)](https://github.com/aws/aws-sdk-go/blob/master/LICENSE.txt) +[![API Reference](https://img.shields.io/badge/api-reference-blue.svg)](https://docs.aws.amazon.com/sdk-for-go/api) [![Join the chat at https://gitter.im/aws/aws-sdk-go](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aws/aws-sdk-go?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://img.shields.io/travis/aws/aws-sdk-go.svg)](https://travis-ci.org/aws/aws-sdk-go) [![Apache V2 License](https://img.shields.io/badge/license-Apache%20V2-blue.svg)](https://github.com/aws/aws-sdk-go/blob/master/LICENSE.txt) # AWS SDK for Go aws-sdk-go is the official AWS SDK for the Go programming language. -Checkout our [release notes](https://github.com/aws/aws-sdk-go/releases) for information about the latest bug fixes, updates, and features added to the SDK. +Checkout our [release notes](https://github.com/aws/aws-sdk-go/releases) for +information about the latest bug fixes, updates, and features added to the SDK. -We [announced](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-2-0-developer-preview/) the Developer Preview for the [v2 AWS SDK for Go](). The v2 SDK is available at https://github.com/aws/aws-sdk-go-v2, and `go get github.com/aws/aws-sdk-go-v2` via `go get`. Check out the v2 SDK's [changes and updates](https://github.com/aws/aws-sdk-go-v2/blob/master/CHANGELOG.md), and let us know what you think. We want your feedback. +We [announced](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-2-0-developer-preview/) the Developer Preview for the [v2 AWS SDK for Go](https://github.com/aws/aws-sdk-go-v2). The v2 SDK source is available at https://github.com/aws/aws-sdk-go-v2, and add it to your project with `go get github.com/aws/aws-sdk-go-v2`. Check out the v2 SDK's [changes and updates](https://github.com/aws/aws-sdk-go-v2/blob/master/CHANGELOG.md), and let us know what you think. We want your feedback. + +We have a pilot redesign of the [AWS SDK for Go API reference documentation](https://docs.aws.amazon.com/sdk-for-go/v1/api/gosdk-apiref.html). Let us know what you think. ## Installing -If you are using Go 1.5 with the `GO15VENDOREXPERIMENT=1` vendoring flag, or 1.6 and higher you can use the following command to retrieve the SDK. The SDK's non-testing dependencies will be included and are vendored in the `vendor` folder. +Use `go get` to retrieve the SDK to add it to your `GOPATH` workspace, or +project's Go module dependencies. + + go get github.com/aws/aws-sdk-go + +To update the SDK use `go get -u` to retrieve the latest version of the SDK. + + go get -u github.com/aws/aws-sdk-go + +### Dependencies + +The SDK includes a `vendor` folder containing the runtime dependencies of the +SDK. The metadata of the SDK's dependencies can be found in the Go module file +`go.mod` or Dep file `Gopkg.toml`. - go get -u github.com/aws/aws-sdk-go +### Go Modules -Otherwise if your Go environment does not have vendoring support enabled, or you do not want to include the vendored SDK's dependencies you can use the following command to retrieve the SDK and its non-testing dependencies using `go get`. +If you are using Go modules, your `go get` will default to the latest tagged +release version of the SDK. To get a specific release version of the SDK use +`@` in your `go get` command. - go get -u github.com/aws/aws-sdk-go/aws/... - go get -u github.com/aws/aws-sdk-go/service/... + go get github.com/aws/aws-sdk-go@v1.15.77 -If you're looking to retrieve just the SDK without any dependencies use the following command. +To get the latest SDK repository change use `@latest`. - go get -d github.com/aws/aws-sdk-go/ + go get github.com/aws/aws-sdk-go@latest -These two processes will still include the `vendor` folder and it should be deleted if its not going to be used by your environment. +### Go 1.5 + +If you are using Go 1.5 without vendoring enabled, (`GO15VENDOREXPERIMENT=1`), +you will need to use `...` when retrieving the SDK to get its dependencies. + + go get github.com/aws/aws-sdk-go/... + +This will still include the `vendor` folder. The `vendor` folder can be deleted +if not used by your environment. rm -rf $GOPATH/src/github.com/aws/aws-sdk-go/vendor ## Getting Help -Please use these community resources for getting help. We use the GitHub issues for tracking bugs and feature requests. +Please use these community resources for getting help. We use the GitHub issues +for tracking bugs and feature requests. * Ask a question on [StackOverflow](http://stackoverflow.com/) and tag it with the [`aws-sdk-go`](http://stackoverflow.com/questions/tagged/aws-sdk-go) tag. * Come join the AWS SDK for Go community chat on [gitter](https://gitter.im/aws/aws-sdk-go). @@ -38,19 +64,43 @@ Please use these community resources for getting help. We use the GitHub issues ## Opening Issues -If you encounter a bug with the AWS SDK for Go we would like to hear about it. Search the [existing issues](https://github.com/aws/aws-sdk-go/issues) and see if others are also experiencing the issue before opening a new issue. Please include the version of AWS SDK for Go, Go language, and OS you’re using. Please also include repro case when appropriate. +If you encounter a bug with the AWS SDK for Go we would like to hear about it. +Search the [existing issues](https://github.com/aws/aws-sdk-go/issues) and see +if others are also experiencing the issue before opening a new issue. Please +include the version of AWS SDK for Go, Go language, and OS you’re using. Please +also include reproduction case when appropriate. -The GitHub issues are intended for bug reports and feature requests. For help and questions with using AWS SDK for GO please make use of the resources listed in the [Getting Help](https://github.com/aws/aws-sdk-go#getting-help) section. Keeping the list of open issues lean will help us respond in a timely manner. +The GitHub issues are intended for bug reports and feature requests. For help +and questions with using AWS SDK for GO please make use of the resources listed +in the [Getting Help](https://github.com/aws/aws-sdk-go#getting-help) section. +Keeping the list of open issues lean will help us respond in a timely manner. ## Reference Documentation -[`Getting Started Guide`](https://aws.amazon.com/sdk-for-go/) - This document is a general introduction how to configure and make requests with the SDK. If this is your first time using the SDK, this documentation and the API documentation will help you get started. This document focuses on the syntax and behavior of the SDK. The [Service Developer Guide](https://aws.amazon.com/documentation/) will help you get started using specific AWS services. - -[`SDK API Reference Documentation`](https://docs.aws.amazon.com/sdk-for-go/api/) - Use this document to look up all API operation input and output parameters for AWS services supported by the SDK. The API reference also includes documentation of the SDK, and examples how to using the SDK, service client API operations, and API operation require parameters. - -[`Service Developer Guide`](https://aws.amazon.com/documentation/) - Use this documentation to learn how to interface with an AWS service. These are great guides both, if you're getting started with a service, or looking for more information on a service. You should not need this document for coding, though in some cases, services may supply helpful samples that you might want to look out for. - -[`SDK Examples`](https://github.com/aws/aws-sdk-go/tree/master/example) - Included in the SDK's repo are a several hand crafted examples using the SDK features and AWS services. +[`Getting Started Guide`](https://aws.amazon.com/sdk-for-go/) - This document +is a general introduction on how to configure and make requests with the SDK. +If this is your first time using the SDK, this documentation and the API +documentation will help you get started. This document focuses on the syntax +and behavior of the SDK. The [Service Developer +Guide](https://aws.amazon.com/documentation/) will help you get started using +specific AWS services. + +[`SDK API Reference +Documentation`](https://docs.aws.amazon.com/sdk-for-go/api/) - Use this +document to look up all API operation input and output parameters for AWS +services supported by the SDK. The API reference also includes documentation of +the SDK, and examples how to using the SDK, service client API operations, and +API operation require parameters. + +[`Service Developer Guide`](https://aws.amazon.com/documentation/) - Use this +documentation to learn how to interface with AWS services. These guides are +great for getting started with a service, or when looking for more +information about a service. While this document is not required for coding, +services may supply helpful samples to look out for. + +[`SDK Examples`](https://github.com/aws/aws-sdk-go/tree/master/example) - +Included in the SDK's repo are several hand crafted examples using the SDK +features and AWS services. ## Overview of SDK's Packages @@ -94,8 +144,7 @@ package under the service folder at the root of the SDK. The SDK includes the Go types and utilities you can use to make requests to AWS service APIs. Within the service folder at the root of the SDK you'll find -a package for each AWS service the SDK supports. All service clients follows -a common pattern of creation and usage. +a package for each AWS service the SDK supports. All service clients follow common pattern of creation and usage. When creating a client for an AWS service you'll first need to have a Session value constructed. The Session provides shared configuration that can be shared @@ -334,7 +383,7 @@ take a callback function that will be called for each page of the API's response ``` Waiter helper methods provide the functionality to wait for an AWS resource -state. These methods abstract the logic needed to to check the state of an +state. These methods abstract the logic needed to check the state of an AWS resource, and wait until that resource is in a desired state. The waiter will block until the resource is in the state that is desired, an error occurs, or the waiter times out. If a resource times out the error code returned will @@ -420,7 +469,9 @@ response. } // Ensure the context is canceled to prevent leaking. // See context package for more information, https://golang.org/pkg/context/ - defer cancelFn() + if cancelFn != nil { + defer cancelFn() + } // Uploads the object to S3. The Context will interrupt the request if the // timeout expires. diff --git a/vendor/github.com/aws/aws-sdk-go/aws/awserr/error.go b/vendor/github.com/aws/aws-sdk-go/aws/awserr/error.go index 56fdfc2bfc76c..99849c0e19c00 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/awserr/error.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/awserr/error.go @@ -138,8 +138,27 @@ type RequestFailure interface { RequestID() string } -// NewRequestFailure returns a new request error wrapper for the given Error -// provided. +// NewRequestFailure returns a wrapped error with additional information for +// request status code, and service requestID. +// +// Should be used to wrap all request which involve service requests. Even if +// the request failed without a service response, but had an HTTP status code +// that may be meaningful. func NewRequestFailure(err Error, statusCode int, reqID string) RequestFailure { return newRequestError(err, statusCode, reqID) } + +// UnmarshalError provides the interface for the SDK failing to unmarshal data. +type UnmarshalError interface { + awsError + Bytes() []byte +} + +// NewUnmarshalError returns an initialized UnmarshalError error wrapper adding +// the bytes that fail to unmarshal to the error. +func NewUnmarshalError(err error, msg string, bytes []byte) UnmarshalError { + return &unmarshalError{ + awsError: New("UnmarshalError", msg, err), + bytes: bytes, + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/awserr/types.go b/vendor/github.com/aws/aws-sdk-go/aws/awserr/types.go index 0202a008f5d7d..9cf7eaf4007f5 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/awserr/types.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/awserr/types.go @@ -1,6 +1,9 @@ package awserr -import "fmt" +import ( + "encoding/hex" + "fmt" +) // SprintError returns a string of the formatted error code. // @@ -119,6 +122,7 @@ type requestError struct { awsError statusCode int requestID string + bytes []byte } // newRequestError returns a wrapped error with additional information for @@ -170,6 +174,29 @@ func (r requestError) OrigErrs() []error { return []error{r.OrigErr()} } +type unmarshalError struct { + awsError + bytes []byte +} + +// Error returns the string representation of the error. +// Satisfies the error interface. +func (e unmarshalError) Error() string { + extra := hex.Dump(e.bytes) + return SprintError(e.Code(), e.Message(), extra, e.OrigErr()) +} + +// String returns the string representation of the error. +// Alias for Error to satisfy the stringer interface. +func (e unmarshalError) String() string { + return e.Error() +} + +// Bytes returns the bytes that failed to unmarshal. +func (e unmarshalError) Bytes() []byte { + return e.bytes +} + // An error list that satisfies the golang interface type errorList []error @@ -181,7 +208,7 @@ func (e errorList) Error() string { // How do we want to handle the array size being zero if size := len(e); size > 0 { for i := 0; i < size; i++ { - msg += fmt.Sprintf("%s", e[i].Error()) + msg += e[i].Error() // We check the next index to see if it is within the slice. // If it is, then we append a newline. We do this, because unit tests // could be broken with the additional '\n' diff --git a/vendor/github.com/aws/aws-sdk-go/aws/awsutil/equal.go b/vendor/github.com/aws/aws-sdk-go/aws/awsutil/equal.go index 59fa4a558a9ac..142a7a01c527d 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/awsutil/equal.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/awsutil/equal.go @@ -15,7 +15,7 @@ func DeepEqual(a, b interface{}) bool { rb := reflect.Indirect(reflect.ValueOf(b)) if raValid, rbValid := ra.IsValid(), rb.IsValid(); !raValid && !rbValid { - // If the elements are both nil, and of the same type the are equal + // If the elements are both nil, and of the same type they are equal // If they are of different types they are not equal return reflect.TypeOf(a) == reflect.TypeOf(b) } else if raValid != rbValid { diff --git a/vendor/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go b/vendor/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go index 11c52c3896874..a4eb6a7f43aae 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/awsutil/path_value.go @@ -70,7 +70,7 @@ func rValuesAtPath(v interface{}, path string, createPath, caseSensitive, nilTer value = value.FieldByNameFunc(func(name string) bool { if c == name { return true - } else if !caseSensitive && strings.ToLower(name) == strings.ToLower(c) { + } else if !caseSensitive && strings.EqualFold(name, c) { return true } return false @@ -185,13 +185,12 @@ func ValuesAtPath(i interface{}, path string) ([]interface{}, error) { // SetValueAtPath sets a value at the case insensitive lexical path inside // of a structure. func SetValueAtPath(i interface{}, path string, v interface{}) { - if rvals := rValuesAtPath(i, path, true, false, v == nil); rvals != nil { - for _, rval := range rvals { - if rval.Kind() == reflect.Ptr && rval.IsNil() { - continue - } - setValue(rval, v) + rvals := rValuesAtPath(i, path, true, false, v == nil) + for _, rval := range rvals { + if rval.Kind() == reflect.Ptr && rval.IsNil() { + continue } + setValue(rval, v) } } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/awsutil/string_value.go b/vendor/github.com/aws/aws-sdk-go/aws/awsutil/string_value.go index b6432f1a1188d..645df2450fc5b 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/awsutil/string_value.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/awsutil/string_value.go @@ -23,28 +23,27 @@ func stringValue(v reflect.Value, indent int, buf *bytes.Buffer) { case reflect.Struct: buf.WriteString("{\n") - names := []string{} for i := 0; i < v.Type().NumField(); i++ { - name := v.Type().Field(i).Name - f := v.Field(i) - if name[0:1] == strings.ToLower(name[0:1]) { + ft := v.Type().Field(i) + fv := v.Field(i) + + if ft.Name[0:1] == strings.ToLower(ft.Name[0:1]) { continue // ignore unexported fields } - if (f.Kind() == reflect.Ptr || f.Kind() == reflect.Slice) && f.IsNil() { + if (fv.Kind() == reflect.Ptr || fv.Kind() == reflect.Slice) && fv.IsNil() { continue // ignore unset fields } - names = append(names, name) - } - for i, n := range names { - val := v.FieldByName(n) buf.WriteString(strings.Repeat(" ", indent+2)) - buf.WriteString(n + ": ") - stringValue(val, indent+2, buf) + buf.WriteString(ft.Name + ": ") - if i < len(names)-1 { - buf.WriteString(",\n") + if tag := ft.Tag.Get("sensitive"); tag == "true" { + buf.WriteString("") + } else { + stringValue(fv, indent+2, buf) } + + buf.WriteString(",\n") } buf.WriteString("\n" + strings.Repeat(" ", indent) + "}") diff --git a/vendor/github.com/aws/aws-sdk-go/aws/client/client.go b/vendor/github.com/aws/aws-sdk-go/aws/client/client.go index 788fe6e279b27..03334d69207b8 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/client/client.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/client/client.go @@ -12,9 +12,16 @@ import ( type Config struct { Config *aws.Config Handlers request.Handlers + PartitionID string Endpoint string SigningRegion string SigningName string + + // States that the signing name did not come from a modeled source but + // was derived based on other data. Used by service client constructors + // to determine if the signin name can be overridden based on metadata the + // service has. + SigningNameDerived bool } // ConfigProvider provides a generic way for a service client to receive @@ -58,7 +65,7 @@ func New(cfg aws.Config, info metadata.ClientInfo, handlers request.Handlers, op default: maxRetries := aws.IntValue(cfg.MaxRetries) if cfg.MaxRetries == nil || maxRetries == aws.UseServiceDefaultRetries { - maxRetries = 3 + maxRetries = DefaultRetryerMaxNumRetries } svc.Retryer = DefaultRetryer{NumMaxRetries: maxRetries} } @@ -85,6 +92,6 @@ func (c *Client) AddDebugHandlers() { return } - c.Handlers.Send.PushFrontNamed(request.NamedHandler{Name: "awssdk.client.LogRequest", Fn: logRequest}) - c.Handlers.Send.PushBackNamed(request.NamedHandler{Name: "awssdk.client.LogResponse", Fn: logResponse}) + c.Handlers.Send.PushFrontNamed(LogHTTPRequestHandler) + c.Handlers.Send.PushBackNamed(LogHTTPResponseHandler) } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go b/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go index 63d2df67c63ea..9f6af19dd459e 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go @@ -1,93 +1,151 @@ package client import ( - "math/rand" + "math" "strconv" - "sync" "time" "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/internal/sdkrand" ) // DefaultRetryer implements basic retry logic using exponential backoff for -// most services. If you want to implement custom retry logic, implement the -// request.Retryer interface or create a structure type that composes this -// struct and override the specific methods. For example, to override only -// the MaxRetries method: +// most services. If you want to implement custom retry logic, you can implement the +// request.Retryer interface. // -// type retryer struct { -// client.DefaultRetryer -// } -// -// // This implementation always has 100 max retries -// func (d retryer) MaxRetries() int { return 100 } type DefaultRetryer struct { + // Num max Retries is the number of max retries that will be performed. + // By default, this is zero. NumMaxRetries int + + // MinRetryDelay is the minimum retry delay after which retry will be performed. + // If not set, the value is 0ns. + MinRetryDelay time.Duration + + // MinThrottleRetryDelay is the minimum retry delay when throttled. + // If not set, the value is 0ns. + MinThrottleDelay time.Duration + + // MaxRetryDelay is the maximum retry delay before which retry must be performed. + // If not set, the value is 0ns. + MaxRetryDelay time.Duration + + // MaxThrottleDelay is the maximum retry delay when throttled. + // If not set, the value is 0ns. + MaxThrottleDelay time.Duration } +const ( + // DefaultRetryerMaxNumRetries sets maximum number of retries + DefaultRetryerMaxNumRetries = 3 + + // DefaultRetryerMinRetryDelay sets minimum retry delay + DefaultRetryerMinRetryDelay = 30 * time.Millisecond + + // DefaultRetryerMinThrottleDelay sets minimum delay when throttled + DefaultRetryerMinThrottleDelay = 500 * time.Millisecond + + // DefaultRetryerMaxRetryDelay sets maximum retry delay + DefaultRetryerMaxRetryDelay = 300 * time.Second + + // DefaultRetryerMaxThrottleDelay sets maximum delay when throttled + DefaultRetryerMaxThrottleDelay = 300 * time.Second +) + // MaxRetries returns the number of maximum returns the service will use to make // an individual API request. func (d DefaultRetryer) MaxRetries() int { return d.NumMaxRetries } -var seededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())}) +// setRetryerDefaults sets the default values of the retryer if not set +func (d *DefaultRetryer) setRetryerDefaults() { + if d.MinRetryDelay == 0 { + d.MinRetryDelay = DefaultRetryerMinRetryDelay + } + if d.MaxRetryDelay == 0 { + d.MaxRetryDelay = DefaultRetryerMaxRetryDelay + } + if d.MinThrottleDelay == 0 { + d.MinThrottleDelay = DefaultRetryerMinThrottleDelay + } + if d.MaxThrottleDelay == 0 { + d.MaxThrottleDelay = DefaultRetryerMaxThrottleDelay + } +} // RetryRules returns the delay duration before retrying this request again func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration { - // Set the upper limit of delay in retrying at ~five minutes - minTime := 30 - throttle := d.shouldThrottle(r) - if throttle { - if delay, ok := getRetryDelay(r); ok { - return delay - } - minTime = 500 + // if number of max retries is zero, no retries will be performed. + if d.NumMaxRetries == 0 { + return 0 + } + + // Sets default value for retryer members + d.setRetryerDefaults() + + // minDelay is the minimum retryer delay + minDelay := d.MinRetryDelay + + var initialDelay time.Duration + + isThrottle := r.IsErrorThrottle() + if isThrottle { + if delay, ok := getRetryAfterDelay(r); ok { + initialDelay = delay + } + minDelay = d.MinThrottleDelay } retryCount := r.RetryCount - if throttle && retryCount > 8 { - retryCount = 8 - } else if retryCount > 13 { - retryCount = 13 + + // maxDelay the maximum retryer delay + maxDelay := d.MaxRetryDelay + + if isThrottle { + maxDelay = d.MaxThrottleDelay } - delay := (1 << uint(retryCount)) * (seededRand.Intn(minTime) + minTime) - return time.Duration(delay) * time.Millisecond + var delay time.Duration + + // Logic to cap the retry count based on the minDelay provided + actualRetryCount := int(math.Log2(float64(minDelay))) + 1 + if actualRetryCount < 63-retryCount { + delay = time.Duration(1< maxDelay { + delay = getJitterDelay(maxDelay / 2) + } + } else { + delay = getJitterDelay(maxDelay / 2) + } + return delay + initialDelay +} + +// getJitterDelay returns a jittered delay for retry +func getJitterDelay(duration time.Duration) time.Duration { + return time.Duration(sdkrand.SeededRand.Int63n(int64(duration)) + int64(duration)) } // ShouldRetry returns true if the request should be retried. func (d DefaultRetryer) ShouldRetry(r *request.Request) bool { + + // ShouldRetry returns false if number of max retries is 0. + if d.NumMaxRetries == 0 { + return false + } + // If one of the other handlers already set the retry state // we don't want to override it based on the service's state if r.Retryable != nil { return *r.Retryable } - - if r.HTTPResponse.StatusCode >= 500 { - return true - } - return r.IsErrorRetryable() || d.shouldThrottle(r) -} - -// ShouldThrottle returns true if the request should be throttled. -func (d DefaultRetryer) shouldThrottle(r *request.Request) bool { - switch r.HTTPResponse.StatusCode { - case 429: - case 502: - case 503: - case 504: - default: - return r.IsErrorThrottle() - } - - return true + return r.IsErrorRetryable() || r.IsErrorThrottle() } // This will look in the Retry-After header, RFC 7231, for how long // it will wait before attempting another request -func getRetryDelay(r *request.Request) (time.Duration, bool) { +func getRetryAfterDelay(r *request.Request) (time.Duration, bool) { if !canUseRetryAfterHeader(r) { return 0, false } @@ -117,22 +175,3 @@ func canUseRetryAfterHeader(r *request.Request) bool { return true } - -// lockedSource is a thread-safe implementation of rand.Source -type lockedSource struct { - lk sync.Mutex - src rand.Source -} - -func (r *lockedSource) Int63() (n int64) { - r.lk.Lock() - n = r.src.Int63() - r.lk.Unlock() - return -} - -func (r *lockedSource) Seed(seed int64) { - r.lk.Lock() - r.src.Seed(seed) - r.lk.Unlock() -} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/client/logger.go b/vendor/github.com/aws/aws-sdk-go/aws/client/logger.go index 1f39c91f2e990..8958c32d4e9fb 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/client/logger.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/client/logger.go @@ -44,22 +44,61 @@ func (reader *teeReaderCloser) Close() error { return reader.Source.Close() } +// LogHTTPRequestHandler is a SDK request handler to log the HTTP request sent +// to a service. Will include the HTTP request body if the LogLevel of the +// request matches LogDebugWithHTTPBody. +var LogHTTPRequestHandler = request.NamedHandler{ + Name: "awssdk.client.LogRequest", + Fn: logRequest, +} + func logRequest(r *request.Request) { logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) - dumpedBody, err := httputil.DumpRequestOut(r.HTTPRequest, logBody) + bodySeekable := aws.IsReaderSeekable(r.Body) + + b, err := httputil.DumpRequestOut(r.HTTPRequest, logBody) if err != nil { - r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, r.ClientInfo.ServiceName, r.Operation.Name, err)) + r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, + r.ClientInfo.ServiceName, r.Operation.Name, err)) return } if logBody { - // Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's - // Body as a NoOpCloser and will not be reset after read by the HTTP - // client reader. - r.ResetBody() + if !bodySeekable { + r.SetReaderBody(aws.ReadSeekCloser(r.HTTPRequest.Body)) + } + // Reset the request body because dumpRequest will re-wrap the + // r.HTTPRequest's Body as a NoOpCloser and will not be reset after + // read by the HTTP client reader. + if err := r.Error; err != nil { + r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, + r.ClientInfo.ServiceName, r.Operation.Name, err)) + return + } + } + + r.Config.Logger.Log(fmt.Sprintf(logReqMsg, + r.ClientInfo.ServiceName, r.Operation.Name, string(b))) +} + +// LogHTTPRequestHeaderHandler is a SDK request handler to log the HTTP request sent +// to a service. Will only log the HTTP request's headers. The request payload +// will not be read. +var LogHTTPRequestHeaderHandler = request.NamedHandler{ + Name: "awssdk.client.LogRequestHeader", + Fn: logRequestHeader, +} + +func logRequestHeader(r *request.Request) { + b, err := httputil.DumpRequestOut(r.HTTPRequest, false) + if err != nil { + r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, + r.ClientInfo.ServiceName, r.Operation.Name, err)) + return } - r.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.ClientInfo.ServiceName, r.Operation.Name, string(dumpedBody))) + r.Config.Logger.Log(fmt.Sprintf(logReqMsg, + r.ClientInfo.ServiceName, r.Operation.Name, string(b))) } const logRespMsg = `DEBUG: Response %s/%s Details: @@ -72,27 +111,50 @@ const logRespErrMsg = `DEBUG ERROR: Response %s/%s: %s -----------------------------------------------------` +// LogHTTPResponseHandler is a SDK request handler to log the HTTP response +// received from a service. Will include the HTTP response body if the LogLevel +// of the request matches LogDebugWithHTTPBody. +var LogHTTPResponseHandler = request.NamedHandler{ + Name: "awssdk.client.LogResponse", + Fn: logResponse, +} + func logResponse(r *request.Request) { lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)} - r.HTTPResponse.Body = &teeReaderCloser{ - Reader: io.TeeReader(r.HTTPResponse.Body, lw), - Source: r.HTTPResponse.Body, + + if r.HTTPResponse == nil { + lw.Logger.Log(fmt.Sprintf(logRespErrMsg, + r.ClientInfo.ServiceName, r.Operation.Name, "request's HTTPResponse is nil")) + return } - handlerFn := func(req *request.Request) { - body, err := httputil.DumpResponse(req.HTTPResponse, false) - if err != nil { - lw.Logger.Log(fmt.Sprintf(logRespErrMsg, req.ClientInfo.ServiceName, req.Operation.Name, err)) - return + logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) + if logBody { + r.HTTPResponse.Body = &teeReaderCloser{ + Reader: io.TeeReader(r.HTTPResponse.Body, lw), + Source: r.HTTPResponse.Body, } + } - b, err := ioutil.ReadAll(lw.buf) + handlerFn := func(req *request.Request) { + b, err := httputil.DumpResponse(req.HTTPResponse, false) if err != nil { - lw.Logger.Log(fmt.Sprintf(logRespErrMsg, req.ClientInfo.ServiceName, req.Operation.Name, err)) + lw.Logger.Log(fmt.Sprintf(logRespErrMsg, + req.ClientInfo.ServiceName, req.Operation.Name, err)) return } - lw.Logger.Log(fmt.Sprintf(logRespMsg, req.ClientInfo.ServiceName, req.Operation.Name, string(body))) - if req.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) { + + lw.Logger.Log(fmt.Sprintf(logRespMsg, + req.ClientInfo.ServiceName, req.Operation.Name, string(b))) + + if logBody { + b, err := ioutil.ReadAll(lw.buf) + if err != nil { + lw.Logger.Log(fmt.Sprintf(logRespErrMsg, + req.ClientInfo.ServiceName, req.Operation.Name, err)) + return + } + lw.Logger.Log(string(b)) } } @@ -106,3 +168,27 @@ func logResponse(r *request.Request) { Name: handlerName, Fn: handlerFn, }) } + +// LogHTTPResponseHeaderHandler is a SDK request handler to log the HTTP +// response received from a service. Will only log the HTTP response's headers. +// The response payload will not be read. +var LogHTTPResponseHeaderHandler = request.NamedHandler{ + Name: "awssdk.client.LogResponseHeader", + Fn: logResponseHeader, +} + +func logResponseHeader(r *request.Request) { + if r.Config.Logger == nil { + return + } + + b, err := httputil.DumpResponse(r.HTTPResponse, false) + if err != nil { + r.Config.Logger.Log(fmt.Sprintf(logRespErrMsg, + r.ClientInfo.ServiceName, r.Operation.Name, err)) + return + } + + r.Config.Logger.Log(fmt.Sprintf(logRespMsg, + r.ClientInfo.ServiceName, r.Operation.Name, string(b))) +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/client/metadata/client_info.go b/vendor/github.com/aws/aws-sdk-go/aws/client/metadata/client_info.go index 4778056ddfdae..0c48f72e08e44 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/client/metadata/client_info.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/client/metadata/client_info.go @@ -3,7 +3,9 @@ package metadata // ClientInfo wraps immutable data from the client.Client structure. type ClientInfo struct { ServiceName string + ServiceID string APIVersion string + PartitionID string Endpoint string SigningName string SigningRegion string diff --git a/vendor/github.com/aws/aws-sdk-go/aws/client/no_op_retryer.go b/vendor/github.com/aws/aws-sdk-go/aws/client/no_op_retryer.go new file mode 100644 index 0000000000000..881d575f01018 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/client/no_op_retryer.go @@ -0,0 +1,28 @@ +package client + +import ( + "time" + + "github.com/aws/aws-sdk-go/aws/request" +) + +// NoOpRetryer provides a retryer that performs no retries. +// It should be used when we do not want retries to be performed. +type NoOpRetryer struct{} + +// MaxRetries returns the number of maximum returns the service will use to make +// an individual API; For NoOpRetryer the MaxRetries will always be zero. +func (d NoOpRetryer) MaxRetries() int { + return 0 +} + +// ShouldRetry will always return false for NoOpRetryer, as it should never retry. +func (d NoOpRetryer) ShouldRetry(_ *request.Request) bool { + return false +} + +// RetryRules returns the delay duration before retrying this request again; +// since NoOpRetryer does not retry, RetryRules always returns 0. +func (d NoOpRetryer) RetryRules(_ *request.Request) time.Duration { + return 0 +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/config.go b/vendor/github.com/aws/aws-sdk-go/aws/config.go index 4fd0d072474d8..2def23fa1d1c6 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/config.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/config.go @@ -18,9 +18,9 @@ const UseServiceDefaultRetries = -1 type RequestRetryer interface{} // A Config provides service configuration for service clients. By default, -// all clients will use the defaults.DefaultConfig tructure. +// all clients will use the defaults.DefaultConfig structure. // -// // Create Session with MaxRetry configuration to be shared by multiple +// // Create Session with MaxRetries configuration to be shared by multiple // // service clients. // sess := session.Must(session.NewSession(&aws.Config{ // MaxRetries: aws.Int(3), @@ -45,8 +45,8 @@ type Config struct { // that overrides the default generated endpoint for a client. Set this // to `""` to use the default generated endpoint. // - // @note You must still provide a `Region` value when specifying an - // endpoint for a client. + // Note: You must still provide a `Region` value when specifying an + // endpoint for a client. Endpoint *string // The resolver to use for looking up endpoints for AWS service clients @@ -65,8 +65,8 @@ type Config struct { // noted. A full list of regions is found in the "Regions and Endpoints" // document. // - // @see http://docs.aws.amazon.com/general/latest/gr/rande.html - // AWS Regions and Endpoints + // See http://docs.aws.amazon.com/general/latest/gr/rande.html for AWS + // Regions and Endpoints. Region *string // Set this to `true` to disable SSL when sending requests. Defaults @@ -120,9 +120,10 @@ type Config struct { // will use virtual hosted bucket addressing when possible // (`http://BUCKET.s3.amazonaws.com/KEY`). // - // @note This configuration option is specific to the Amazon S3 service. - // @see http://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html - // Amazon S3: Virtual Hosting of Buckets + // Note: This configuration option is specific to the Amazon S3 service. + // + // See http://docs.aws.amazon.com/AmazonS3/latest/dev/VirtualHosting.html + // for Amazon S3: Virtual Hosting of Buckets S3ForcePathStyle *bool // Set this to `true` to disable the SDK adding the `Expect: 100-Continue` @@ -151,6 +152,26 @@ type Config struct { // with accelerate. S3UseAccelerate *bool + // S3DisableContentMD5Validation config option is temporarily disabled, + // For S3 GetObject API calls, #1837. + // + // Set this to `true` to disable the S3 service client from automatically + // adding the ContentMD5 to S3 Object Put and Upload API calls. This option + // will also disable the SDK from performing object ContentMD5 validation + // on GetObject API calls. + S3DisableContentMD5Validation *bool + + // Set this to `true` to have the S3 service client to use the region specified + // in the ARN, when an ARN is provided as an argument to a bucket parameter. + S3UseARNRegion *bool + + // Set this to `true` to enable the SDK to unmarshal API response header maps to + // normalized lower case map keys. + // + // For example S3's X-Amz-Meta prefixed header will be unmarshaled to lower case + // Metadata member's map keys. The value of the header in the map is unaffected. + LowerCaseHeaderMaps *bool + // Set this to `true` to disable the EC2Metadata client from overriding the // default http.Client's Timeout. This is helpful if you do not want the // EC2Metadata client to create a new http.Client. This options is only @@ -214,12 +235,40 @@ type Config struct { // Key: aws.String("//foo//bar//moo"), // }) DisableRestProtocolURICleaning *bool + + // EnableEndpointDiscovery will allow for endpoint discovery on operations that + // have the definition in its model. By default, endpoint discovery is off. + // + // Example: + // sess := session.Must(session.NewSession(&aws.Config{ + // EnableEndpointDiscovery: aws.Bool(true), + // })) + // + // svc := s3.New(sess) + // out, err := svc.GetObject(&s3.GetObjectInput { + // Bucket: aws.String("bucketname"), + // Key: aws.String("/foo/bar/moo"), + // }) + EnableEndpointDiscovery *bool + + // DisableEndpointHostPrefix will disable the SDK's behavior of prefixing + // request endpoint hosts with modeled information. + // + // Disabling this feature is useful when you want to use local endpoints + // for testing that do not support the modeled host prefix pattern. + DisableEndpointHostPrefix *bool + + // STSRegionalEndpoint will enable regional or legacy endpoint resolving + STSRegionalEndpoint endpoints.STSRegionalEndpoint + + // S3UsEast1RegionalEndpoint will enable regional or legacy endpoint resolving + S3UsEast1RegionalEndpoint endpoints.S3UsEast1RegionalEndpoint } // NewConfig returns a new Config pointer that can be chained with builder // methods to set multiple configuration values inline without using pointers. // -// // Create Session with MaxRetry configuration to be shared by multiple +// // Create Session with MaxRetries configuration to be shared by multiple // // service clients. // sess := session.Must(session.NewSession(aws.NewConfig(). // WithMaxRetries(3), @@ -336,6 +385,22 @@ func (c *Config) WithS3Disable100Continue(disable bool) *Config { func (c *Config) WithS3UseAccelerate(enable bool) *Config { c.S3UseAccelerate = &enable return c + +} + +// WithS3DisableContentMD5Validation sets a config +// S3DisableContentMD5Validation value returning a Config pointer for chaining. +func (c *Config) WithS3DisableContentMD5Validation(enable bool) *Config { + c.S3DisableContentMD5Validation = &enable + return c + +} + +// WithS3UseARNRegion sets a config S3UseARNRegion value and +// returning a Config pointer for chaining +func (c *Config) WithS3UseARNRegion(enable bool) *Config { + c.S3UseARNRegion = &enable + return c } // WithUseDualStack sets a config UseDualStack value returning a Config @@ -359,6 +424,19 @@ func (c *Config) WithSleepDelay(fn func(time.Duration)) *Config { return c } +// WithEndpointDiscovery will set whether or not to use endpoint discovery. +func (c *Config) WithEndpointDiscovery(t bool) *Config { + c.EnableEndpointDiscovery = &t + return c +} + +// WithDisableEndpointHostPrefix will set whether or not to use modeled host prefix +// when making requests. +func (c *Config) WithDisableEndpointHostPrefix(t bool) *Config { + c.DisableEndpointHostPrefix = &t + return c +} + // MergeIn merges the passed in configs into the existing config object. func (c *Config) MergeIn(cfgs ...*Config) { for _, other := range cfgs { @@ -366,6 +444,20 @@ func (c *Config) MergeIn(cfgs ...*Config) { } } +// WithSTSRegionalEndpoint will set whether or not to use regional endpoint flag +// when resolving the endpoint for a service +func (c *Config) WithSTSRegionalEndpoint(sre endpoints.STSRegionalEndpoint) *Config { + c.STSRegionalEndpoint = sre + return c +} + +// WithS3UsEast1RegionalEndpoint will set whether or not to use regional endpoint flag +// when resolving the endpoint for a service +func (c *Config) WithS3UsEast1RegionalEndpoint(sre endpoints.S3UsEast1RegionalEndpoint) *Config { + c.S3UsEast1RegionalEndpoint = sre + return c +} + func mergeInConfig(dst *Config, other *Config) { if other == nil { return @@ -435,6 +527,14 @@ func mergeInConfig(dst *Config, other *Config) { dst.S3UseAccelerate = other.S3UseAccelerate } + if other.S3DisableContentMD5Validation != nil { + dst.S3DisableContentMD5Validation = other.S3DisableContentMD5Validation + } + + if other.S3UseARNRegion != nil { + dst.S3UseARNRegion = other.S3UseARNRegion + } + if other.UseDualStack != nil { dst.UseDualStack = other.UseDualStack } @@ -454,6 +554,22 @@ func mergeInConfig(dst *Config, other *Config) { if other.EnforceShouldRetryCheck != nil { dst.EnforceShouldRetryCheck = other.EnforceShouldRetryCheck } + + if other.EnableEndpointDiscovery != nil { + dst.EnableEndpointDiscovery = other.EnableEndpointDiscovery + } + + if other.DisableEndpointHostPrefix != nil { + dst.DisableEndpointHostPrefix = other.DisableEndpointHostPrefix + } + + if other.STSRegionalEndpoint != endpoints.UnsetSTSEndpoint { + dst.STSRegionalEndpoint = other.STSRegionalEndpoint + } + + if other.S3UsEast1RegionalEndpoint != endpoints.UnsetS3UsEast1Endpoint { + dst.S3UsEast1RegionalEndpoint = other.S3UsEast1RegionalEndpoint + } } // Copy will return a shallow copy of the Config object. If any additional diff --git a/vendor/github.com/aws/aws-sdk-go/aws/context.go b/vendor/github.com/aws/aws-sdk-go/aws/context.go deleted file mode 100644 index 79f426853b5d8..0000000000000 --- a/vendor/github.com/aws/aws-sdk-go/aws/context.go +++ /dev/null @@ -1,71 +0,0 @@ -package aws - -import ( - "time" -) - -// Context is an copy of the Go v1.7 stdlib's context.Context interface. -// It is represented as a SDK interface to enable you to use the "WithContext" -// API methods with Go v1.6 and a Context type such as golang.org/x/net/context. -// -// See https://golang.org/pkg/context on how to use contexts. -type Context interface { - // Deadline returns the time when work done on behalf of this context - // should be canceled. Deadline returns ok==false when no deadline is - // set. Successive calls to Deadline return the same results. - Deadline() (deadline time.Time, ok bool) - - // Done returns a channel that's closed when work done on behalf of this - // context should be canceled. Done may return nil if this context can - // never be canceled. Successive calls to Done return the same value. - Done() <-chan struct{} - - // Err returns a non-nil error value after Done is closed. Err returns - // Canceled if the context was canceled or DeadlineExceeded if the - // context's deadline passed. No other values for Err are defined. - // After Done is closed, successive calls to Err return the same value. - Err() error - - // Value returns the value associated with this context for key, or nil - // if no value is associated with key. Successive calls to Value with - // the same key returns the same result. - // - // Use context values only for request-scoped data that transits - // processes and API boundaries, not for passing optional parameters to - // functions. - Value(key interface{}) interface{} -} - -// BackgroundContext returns a context that will never be canceled, has no -// values, and no deadline. This context is used by the SDK to provide -// backwards compatibility with non-context API operations and functionality. -// -// Go 1.6 and before: -// This context function is equivalent to context.Background in the Go stdlib. -// -// Go 1.7 and later: -// The context returned will be the value returned by context.Background() -// -// See https://golang.org/pkg/context for more information on Contexts. -func BackgroundContext() Context { - return backgroundCtx -} - -// SleepWithContext will wait for the timer duration to expire, or the context -// is canceled. Which ever happens first. If the context is canceled the Context's -// error will be returned. -// -// Expects Context to always return a non-nil error if the Done channel is closed. -func SleepWithContext(ctx Context, dur time.Duration) error { - t := time.NewTimer(dur) - defer t.Stop() - - select { - case <-t.C: - break - case <-ctx.Done(): - return ctx.Err() - } - - return nil -} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/context_1_5.go b/vendor/github.com/aws/aws-sdk-go/aws/context_1_5.go new file mode 100644 index 0000000000000..2866f9a7fb9d7 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/context_1_5.go @@ -0,0 +1,37 @@ +// +build !go1.9 + +package aws + +import "time" + +// Context is an copy of the Go v1.7 stdlib's context.Context interface. +// It is represented as a SDK interface to enable you to use the "WithContext" +// API methods with Go v1.6 and a Context type such as golang.org/x/net/context. +// +// See https://golang.org/pkg/context on how to use contexts. +type Context interface { + // Deadline returns the time when work done on behalf of this context + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. + Deadline() (deadline time.Time, ok bool) + + // Done returns a channel that's closed when work done on behalf of this + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. + Done() <-chan struct{} + + // Err returns a non-nil error value after Done is closed. Err returns + // Canceled if the context was canceled or DeadlineExceeded if the + // context's deadline passed. No other values for Err are defined. + // After Done is closed, successive calls to Err return the same value. + Err() error + + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + Value(key interface{}) interface{} +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/context_1_6.go b/vendor/github.com/aws/aws-sdk-go/aws/context_1_6.go deleted file mode 100644 index 8fdda53033809..0000000000000 --- a/vendor/github.com/aws/aws-sdk-go/aws/context_1_6.go +++ /dev/null @@ -1,41 +0,0 @@ -// +build !go1.7 - -package aws - -import "time" - -// An emptyCtx is a copy of the Go 1.7 context.emptyCtx type. This is copied to -// provide a 1.6 and 1.5 safe version of context that is compatible with Go -// 1.7's Context. -// -// An emptyCtx is never canceled, has no values, and has no deadline. It is not -// struct{}, since vars of this type must have distinct addresses. -type emptyCtx int - -func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { - return -} - -func (*emptyCtx) Done() <-chan struct{} { - return nil -} - -func (*emptyCtx) Err() error { - return nil -} - -func (*emptyCtx) Value(key interface{}) interface{} { - return nil -} - -func (e *emptyCtx) String() string { - switch e { - case backgroundCtx: - return "aws.BackgroundContext" - } - return "unknown empty Context" -} - -var ( - backgroundCtx = new(emptyCtx) -) diff --git a/vendor/github.com/aws/aws-sdk-go/aws/context_1_7.go b/vendor/github.com/aws/aws-sdk-go/aws/context_1_7.go deleted file mode 100644 index 064f75c925cc5..0000000000000 --- a/vendor/github.com/aws/aws-sdk-go/aws/context_1_7.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build go1.7 - -package aws - -import "context" - -var ( - backgroundCtx = context.Background() -) diff --git a/vendor/github.com/aws/aws-sdk-go/aws/context_1_9.go b/vendor/github.com/aws/aws-sdk-go/aws/context_1_9.go new file mode 100644 index 0000000000000..3718b26e1019b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/context_1_9.go @@ -0,0 +1,11 @@ +// +build go1.9 + +package aws + +import "context" + +// Context is an alias of the Go stdlib's context.Context interface. +// It can be used within the SDK's API operation "WithContext" methods. +// +// See https://golang.org/pkg/context on how to use contexts. +type Context = context.Context diff --git a/vendor/github.com/aws/aws-sdk-go/aws/context_background_1_5.go b/vendor/github.com/aws/aws-sdk-go/aws/context_background_1_5.go new file mode 100644 index 0000000000000..66c5945db15ea --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/context_background_1_5.go @@ -0,0 +1,56 @@ +// +build !go1.7 + +package aws + +import "time" + +// An emptyCtx is a copy of the Go 1.7 context.emptyCtx type. This is copied to +// provide a 1.6 and 1.5 safe version of context that is compatible with Go +// 1.7's Context. +// +// An emptyCtx is never canceled, has no values, and has no deadline. It is not +// struct{}, since vars of this type must have distinct addresses. +type emptyCtx int + +func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (*emptyCtx) Done() <-chan struct{} { + return nil +} + +func (*emptyCtx) Err() error { + return nil +} + +func (*emptyCtx) Value(key interface{}) interface{} { + return nil +} + +func (e *emptyCtx) String() string { + switch e { + case backgroundCtx: + return "aws.BackgroundContext" + } + return "unknown empty Context" +} + +var ( + backgroundCtx = new(emptyCtx) +) + +// BackgroundContext returns a context that will never be canceled, has no +// values, and no deadline. This context is used by the SDK to provide +// backwards compatibility with non-context API operations and functionality. +// +// Go 1.6 and before: +// This context function is equivalent to context.Background in the Go stdlib. +// +// Go 1.7 and later: +// The context returned will be the value returned by context.Background() +// +// See https://golang.org/pkg/context for more information on Contexts. +func BackgroundContext() Context { + return backgroundCtx +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/context_background_1_7.go b/vendor/github.com/aws/aws-sdk-go/aws/context_background_1_7.go new file mode 100644 index 0000000000000..9c29f29af17a0 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/context_background_1_7.go @@ -0,0 +1,20 @@ +// +build go1.7 + +package aws + +import "context" + +// BackgroundContext returns a context that will never be canceled, has no +// values, and no deadline. This context is used by the SDK to provide +// backwards compatibility with non-context API operations and functionality. +// +// Go 1.6 and before: +// This context function is equivalent to context.Background in the Go stdlib. +// +// Go 1.7 and later: +// The context returned will be the value returned by context.Background() +// +// See https://golang.org/pkg/context for more information on Contexts. +func BackgroundContext() Context { + return context.Background() +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/context_sleep.go b/vendor/github.com/aws/aws-sdk-go/aws/context_sleep.go new file mode 100644 index 0000000000000..304fd156120c1 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/context_sleep.go @@ -0,0 +1,24 @@ +package aws + +import ( + "time" +) + +// SleepWithContext will wait for the timer duration to expire, or the context +// is canceled. Which ever happens first. If the context is canceled the Context's +// error will be returned. +// +// Expects Context to always return a non-nil error if the Done channel is closed. +func SleepWithContext(ctx Context, dur time.Duration) error { + t := time.NewTimer(dur) + defer t.Stop() + + select { + case <-t.C: + break + case <-ctx.Done(): + return ctx.Err() + } + + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/convert_types.go b/vendor/github.com/aws/aws-sdk-go/aws/convert_types.go index ff5d58e068318..4e076c1837a74 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/convert_types.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/convert_types.go @@ -179,6 +179,242 @@ func IntValueMap(src map[string]*int) map[string]int { return dst } +// Uint returns a pointer to the uint value passed in. +func Uint(v uint) *uint { + return &v +} + +// UintValue returns the value of the uint pointer passed in or +// 0 if the pointer is nil. +func UintValue(v *uint) uint { + if v != nil { + return *v + } + return 0 +} + +// UintSlice converts a slice of uint values uinto a slice of +// uint pointers +func UintSlice(src []uint) []*uint { + dst := make([]*uint, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// UintValueSlice converts a slice of uint pointers uinto a slice of +// uint values +func UintValueSlice(src []*uint) []uint { + dst := make([]uint, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// UintMap converts a string map of uint values uinto a string +// map of uint pointers +func UintMap(src map[string]uint) map[string]*uint { + dst := make(map[string]*uint) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// UintValueMap converts a string map of uint pointers uinto a string +// map of uint values +func UintValueMap(src map[string]*uint) map[string]uint { + dst := make(map[string]uint) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Int8 returns a pointer to the int8 value passed in. +func Int8(v int8) *int8 { + return &v +} + +// Int8Value returns the value of the int8 pointer passed in or +// 0 if the pointer is nil. +func Int8Value(v *int8) int8 { + if v != nil { + return *v + } + return 0 +} + +// Int8Slice converts a slice of int8 values into a slice of +// int8 pointers +func Int8Slice(src []int8) []*int8 { + dst := make([]*int8, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int8ValueSlice converts a slice of int8 pointers into a slice of +// int8 values +func Int8ValueSlice(src []*int8) []int8 { + dst := make([]int8, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Int8Map converts a string map of int8 values into a string +// map of int8 pointers +func Int8Map(src map[string]int8) map[string]*int8 { + dst := make(map[string]*int8) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Int8ValueMap converts a string map of int8 pointers into a string +// map of int8 values +func Int8ValueMap(src map[string]*int8) map[string]int8 { + dst := make(map[string]int8) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Int16 returns a pointer to the int16 value passed in. +func Int16(v int16) *int16 { + return &v +} + +// Int16Value returns the value of the int16 pointer passed in or +// 0 if the pointer is nil. +func Int16Value(v *int16) int16 { + if v != nil { + return *v + } + return 0 +} + +// Int16Slice converts a slice of int16 values into a slice of +// int16 pointers +func Int16Slice(src []int16) []*int16 { + dst := make([]*int16, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int16ValueSlice converts a slice of int16 pointers into a slice of +// int16 values +func Int16ValueSlice(src []*int16) []int16 { + dst := make([]int16, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Int16Map converts a string map of int16 values into a string +// map of int16 pointers +func Int16Map(src map[string]int16) map[string]*int16 { + dst := make(map[string]*int16) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Int16ValueMap converts a string map of int16 pointers into a string +// map of int16 values +func Int16ValueMap(src map[string]*int16) map[string]int16 { + dst := make(map[string]int16) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Int32 returns a pointer to the int32 value passed in. +func Int32(v int32) *int32 { + return &v +} + +// Int32Value returns the value of the int32 pointer passed in or +// 0 if the pointer is nil. +func Int32Value(v *int32) int32 { + if v != nil { + return *v + } + return 0 +} + +// Int32Slice converts a slice of int32 values into a slice of +// int32 pointers +func Int32Slice(src []int32) []*int32 { + dst := make([]*int32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Int32ValueSlice converts a slice of int32 pointers into a slice of +// int32 values +func Int32ValueSlice(src []*int32) []int32 { + dst := make([]int32, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Int32Map converts a string map of int32 values into a string +// map of int32 pointers +func Int32Map(src map[string]int32) map[string]*int32 { + dst := make(map[string]*int32) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Int32ValueMap converts a string map of int32 pointers into a string +// map of int32 values +func Int32ValueMap(src map[string]*int32) map[string]int32 { + dst := make(map[string]int32) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + // Int64 returns a pointer to the int64 value passed in. func Int64(v int64) *int64 { return &v @@ -238,6 +474,301 @@ func Int64ValueMap(src map[string]*int64) map[string]int64 { return dst } +// Uint8 returns a pointer to the uint8 value passed in. +func Uint8(v uint8) *uint8 { + return &v +} + +// Uint8Value returns the value of the uint8 pointer passed in or +// 0 if the pointer is nil. +func Uint8Value(v *uint8) uint8 { + if v != nil { + return *v + } + return 0 +} + +// Uint8Slice converts a slice of uint8 values into a slice of +// uint8 pointers +func Uint8Slice(src []uint8) []*uint8 { + dst := make([]*uint8, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint8ValueSlice converts a slice of uint8 pointers into a slice of +// uint8 values +func Uint8ValueSlice(src []*uint8) []uint8 { + dst := make([]uint8, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Uint8Map converts a string map of uint8 values into a string +// map of uint8 pointers +func Uint8Map(src map[string]uint8) map[string]*uint8 { + dst := make(map[string]*uint8) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Uint8ValueMap converts a string map of uint8 pointers into a string +// map of uint8 values +func Uint8ValueMap(src map[string]*uint8) map[string]uint8 { + dst := make(map[string]uint8) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Uint16 returns a pointer to the uint16 value passed in. +func Uint16(v uint16) *uint16 { + return &v +} + +// Uint16Value returns the value of the uint16 pointer passed in or +// 0 if the pointer is nil. +func Uint16Value(v *uint16) uint16 { + if v != nil { + return *v + } + return 0 +} + +// Uint16Slice converts a slice of uint16 values into a slice of +// uint16 pointers +func Uint16Slice(src []uint16) []*uint16 { + dst := make([]*uint16, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint16ValueSlice converts a slice of uint16 pointers into a slice of +// uint16 values +func Uint16ValueSlice(src []*uint16) []uint16 { + dst := make([]uint16, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Uint16Map converts a string map of uint16 values into a string +// map of uint16 pointers +func Uint16Map(src map[string]uint16) map[string]*uint16 { + dst := make(map[string]*uint16) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Uint16ValueMap converts a string map of uint16 pointers into a string +// map of uint16 values +func Uint16ValueMap(src map[string]*uint16) map[string]uint16 { + dst := make(map[string]uint16) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Uint32 returns a pointer to the uint32 value passed in. +func Uint32(v uint32) *uint32 { + return &v +} + +// Uint32Value returns the value of the uint32 pointer passed in or +// 0 if the pointer is nil. +func Uint32Value(v *uint32) uint32 { + if v != nil { + return *v + } + return 0 +} + +// Uint32Slice converts a slice of uint32 values into a slice of +// uint32 pointers +func Uint32Slice(src []uint32) []*uint32 { + dst := make([]*uint32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint32ValueSlice converts a slice of uint32 pointers into a slice of +// uint32 values +func Uint32ValueSlice(src []*uint32) []uint32 { + dst := make([]uint32, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Uint32Map converts a string map of uint32 values into a string +// map of uint32 pointers +func Uint32Map(src map[string]uint32) map[string]*uint32 { + dst := make(map[string]*uint32) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Uint32ValueMap converts a string map of uint32 pointers into a string +// map of uint32 values +func Uint32ValueMap(src map[string]*uint32) map[string]uint32 { + dst := make(map[string]uint32) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Uint64 returns a pointer to the uint64 value passed in. +func Uint64(v uint64) *uint64 { + return &v +} + +// Uint64Value returns the value of the uint64 pointer passed in or +// 0 if the pointer is nil. +func Uint64Value(v *uint64) uint64 { + if v != nil { + return *v + } + return 0 +} + +// Uint64Slice converts a slice of uint64 values into a slice of +// uint64 pointers +func Uint64Slice(src []uint64) []*uint64 { + dst := make([]*uint64, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Uint64ValueSlice converts a slice of uint64 pointers into a slice of +// uint64 values +func Uint64ValueSlice(src []*uint64) []uint64 { + dst := make([]uint64, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Uint64Map converts a string map of uint64 values into a string +// map of uint64 pointers +func Uint64Map(src map[string]uint64) map[string]*uint64 { + dst := make(map[string]*uint64) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Uint64ValueMap converts a string map of uint64 pointers into a string +// map of uint64 values +func Uint64ValueMap(src map[string]*uint64) map[string]uint64 { + dst := make(map[string]uint64) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + +// Float32 returns a pointer to the float32 value passed in. +func Float32(v float32) *float32 { + return &v +} + +// Float32Value returns the value of the float32 pointer passed in or +// 0 if the pointer is nil. +func Float32Value(v *float32) float32 { + if v != nil { + return *v + } + return 0 +} + +// Float32Slice converts a slice of float32 values into a slice of +// float32 pointers +func Float32Slice(src []float32) []*float32 { + dst := make([]*float32, len(src)) + for i := 0; i < len(src); i++ { + dst[i] = &(src[i]) + } + return dst +} + +// Float32ValueSlice converts a slice of float32 pointers into a slice of +// float32 values +func Float32ValueSlice(src []*float32) []float32 { + dst := make([]float32, len(src)) + for i := 0; i < len(src); i++ { + if src[i] != nil { + dst[i] = *(src[i]) + } + } + return dst +} + +// Float32Map converts a string map of float32 values into a string +// map of float32 pointers +func Float32Map(src map[string]float32) map[string]*float32 { + dst := make(map[string]*float32) + for k, val := range src { + v := val + dst[k] = &v + } + return dst +} + +// Float32ValueMap converts a string map of float32 pointers into a string +// map of float32 values +func Float32ValueMap(src map[string]*float32) map[string]float32 { + dst := make(map[string]float32) + for k, val := range src { + if val != nil { + dst[k] = *val + } + } + return dst +} + // Float64 returns a pointer to the float64 value passed in. func Float64(v float64) *float64 { return &v diff --git a/vendor/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go b/vendor/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go index 495e3ef62ca9c..aa902d70837f8 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go @@ -3,12 +3,10 @@ package corehandlers import ( "bytes" "fmt" - "io" "io/ioutil" "net/http" "net/url" "regexp" - "runtime" "strconv" "time" @@ -36,18 +34,13 @@ var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLen if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" { length, _ = strconv.ParseInt(slength, 10, 64) } else { - switch body := r.Body.(type) { - case nil: - length = 0 - case lener: - length = int64(body.Len()) - case io.Seeker: - r.BodyStart, _ = body.Seek(0, 1) - end, _ := body.Seek(0, 2) - body.Seek(r.BodyStart, 0) // make sure to seek back to original location - length = end - r.BodyStart - default: - panic("Cannot get length of body, must provide `ContentLength`") + if r.Body != nil { + var err error + length, err = aws.SeekerLen(r.Body) + if err != nil { + r.Error = awserr.New(request.ErrCodeSerialization, "failed to get request body's length", err) + return + } } } @@ -60,13 +53,6 @@ var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLen } }} -// SDKVersionUserAgentHandler is a request handler for adding the SDK Version to the user agent. -var SDKVersionUserAgentHandler = request.NamedHandler{ - Name: "core.SDKVersionUserAgentHandler", - Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion, - runtime.Version(), runtime.GOOS, runtime.GOARCH), -} - var reStatusCode = regexp.MustCompile(`^(\d{3})`) // ValidateReqSigHandler is a request handler to ensure that the request's @@ -86,9 +72,9 @@ var ValidateReqSigHandler = request.NamedHandler{ signedTime = r.LastSignedAt } - // 10 minutes to allow for some clock skew/delays in transmission. + // 5 minutes to allow for some clock skew/delays in transmission. // Would be improved with aws/aws-sdk-go#423 - if signedTime.Add(10 * time.Minute).After(time.Now()) { + if signedTime.Add(5 * time.Minute).After(time.Now()) { return } @@ -173,9 +159,9 @@ func handleSendError(r *request.Request, err error) { Body: ioutil.NopCloser(bytes.NewReader([]byte{})), } } - // Catch all other request errors. - r.Error = awserr.New("RequestError", "send request failed", err) - r.Retryable = aws.Bool(true) // network errors are retryable + // Catch all request errors, and let the default retrier determine + // if the error is retryable. + r.Error = awserr.New(request.ErrCodeRequestError, "send request failed", err) // Override the error with a context canceled error, if that was canceled. ctx := r.Context() @@ -198,37 +184,39 @@ var ValidateResponseHandler = request.NamedHandler{Name: "core.ValidateResponseH // AfterRetryHandler performs final checks to determine if the request should // be retried and how long to delay. -var AfterRetryHandler = request.NamedHandler{Name: "core.AfterRetryHandler", Fn: func(r *request.Request) { - // If one of the other handlers already set the retry state - // we don't want to override it based on the service's state - if r.Retryable == nil || aws.BoolValue(r.Config.EnforceShouldRetryCheck) { - r.Retryable = aws.Bool(r.ShouldRetry(r)) - } +var AfterRetryHandler = request.NamedHandler{ + Name: "core.AfterRetryHandler", + Fn: func(r *request.Request) { + // If one of the other handlers already set the retry state + // we don't want to override it based on the service's state + if r.Retryable == nil || aws.BoolValue(r.Config.EnforceShouldRetryCheck) { + r.Retryable = aws.Bool(r.ShouldRetry(r)) + } - if r.WillRetry() { - r.RetryDelay = r.RetryRules(r) + if r.WillRetry() { + r.RetryDelay = r.RetryRules(r) + + if sleepFn := r.Config.SleepDelay; sleepFn != nil { + // Support SleepDelay for backwards compatibility and testing + sleepFn(r.RetryDelay) + } else if err := aws.SleepWithContext(r.Context(), r.RetryDelay); err != nil { + r.Error = awserr.New(request.CanceledErrorCode, + "request context canceled", err) + r.Retryable = aws.Bool(false) + return + } - if sleepFn := r.Config.SleepDelay; sleepFn != nil { - // Support SleepDelay for backwards compatibility and testing - sleepFn(r.RetryDelay) - } else if err := aws.SleepWithContext(r.Context(), r.RetryDelay); err != nil { - r.Error = awserr.New(request.CanceledErrorCode, - "request context canceled", err) - r.Retryable = aws.Bool(false) - return - } + // when the expired token exception occurs the credentials + // need to be expired locally so that the next request to + // get credentials will trigger a credentials refresh. + if r.IsErrorExpired() { + r.Config.Credentials.Expire() + } - // when the expired token exception occurs the credentials - // need to be expired locally so that the next request to - // get credentials will trigger a credentials refresh. - if r.IsErrorExpired() { - r.Config.Credentials.Expire() + r.RetryCount++ + r.Error = nil } - - r.RetryCount++ - r.Error = nil - } -}} + }} // ValidateEndpointHandler is a request handler to validate a request had the // appropriate Region and Endpoint set. Will set r.Error if the endpoint or diff --git a/vendor/github.com/aws/aws-sdk-go/aws/corehandlers/user_agent.go b/vendor/github.com/aws/aws-sdk-go/aws/corehandlers/user_agent.go new file mode 100644 index 0000000000000..ab69c7a6f3889 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/corehandlers/user_agent.go @@ -0,0 +1,37 @@ +package corehandlers + +import ( + "os" + "runtime" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" +) + +// SDKVersionUserAgentHandler is a request handler for adding the SDK Version +// to the user agent. +var SDKVersionUserAgentHandler = request.NamedHandler{ + Name: "core.SDKVersionUserAgentHandler", + Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion, + runtime.Version(), runtime.GOOS, runtime.GOARCH), +} + +const execEnvVar = `AWS_EXECUTION_ENV` +const execEnvUAKey = `exec-env` + +// AddHostExecEnvUserAgentHander is a request handler appending the SDK's +// execution environment to the user agent. +// +// If the environment variable AWS_EXECUTION_ENV is set, its value will be +// appended to the user agent string. +var AddHostExecEnvUserAgentHander = request.NamedHandler{ + Name: "core.AddHostExecEnvUserAgentHander", + Fn: func(r *request.Request) { + v := os.Getenv(execEnvVar) + if len(v) == 0 { + return + } + + request.AddToUserAgent(r, execEnvUAKey+"/"+v) + }, +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go b/vendor/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go index f298d6596268b..3ad1e798df80e 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/credentials/chain_provider.go @@ -9,9 +9,7 @@ var ( // providers in the ChainProvider. // // This has been deprecated. For verbose error messaging set - // aws.Config.CredentialsChainVerboseErrors to true - // - // @readonly + // aws.Config.CredentialsChainVerboseErrors to true. ErrNoValidProvidersFoundInChain = awserr.New("NoCredentialProviders", `no valid providers in chain. Deprecated. For verbose messaging see aws.Config.CredentialsChainVerboseErrors`, diff --git a/vendor/github.com/aws/aws-sdk-go/aws/credentials/credentials.go b/vendor/github.com/aws/aws-sdk-go/aws/credentials/credentials.go index 42416fc2f0fcc..4af592158144c 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/credentials/credentials.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/credentials/credentials.go @@ -49,8 +49,11 @@ package credentials import ( + "fmt" "sync" "time" + + "github.com/aws/aws-sdk-go/aws/awserr" ) // AnonymousCredentials is an empty Credential object that can be used as @@ -64,8 +67,6 @@ import ( // Credentials: credentials.AnonymousCredentials, // }))) // // Access public S3 buckets. -// -// @readonly var AnonymousCredentials = NewStaticCredentials("", "", "") // A Value is the AWS credentials value for individual credential fields. @@ -83,6 +84,12 @@ type Value struct { ProviderName string } +// HasKeys returns if the credentials Value has both AccessKeyID and +// SecretAccessKey value set. +func (v Value) HasKeys() bool { + return len(v.AccessKeyID) != 0 && len(v.SecretAccessKey) != 0 +} + // A Provider is the interface for any component which will provide credentials // Value. A provider is required to manage its own Expired state, and what to // be expired means. @@ -99,6 +106,14 @@ type Provider interface { IsExpired() bool } +// An Expirer is an interface that Providers can implement to expose the expiration +// time, if known. If the Provider cannot accurately provide this info, +// it should not implement this interface. +type Expirer interface { + // The time at which the credentials are no longer valid + ExpiresAt() time.Time +} + // An ErrorProvider is a stub credentials provider that always returns an error // this is used by the SDK when construction a known provider is not possible // due to an error. @@ -158,13 +173,19 @@ func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) { // IsExpired returns if the credentials are expired. func (e *Expiry) IsExpired() bool { - if e.CurrentTime == nil { - e.CurrentTime = time.Now + curTime := e.CurrentTime + if curTime == nil { + curTime = time.Now } - return e.expiration.Before(e.CurrentTime()) + return e.expiration.Before(curTime()) +} + +// ExpiresAt returns the expiration time of the credential +func (e *Expiry) ExpiresAt() time.Time { + return e.expiration } -// A Credentials provides synchronous safe retrieval of AWS credentials Value. +// A Credentials provides concurrency safe retrieval of AWS credentials Value. // Credentials will cache the credentials value until they expire. Once the value // expires the next Get will attempt to retrieve valid credentials. // @@ -178,7 +199,8 @@ func (e *Expiry) IsExpired() bool { type Credentials struct { creds Value forceRefresh bool - m sync.Mutex + + m sync.RWMutex provider Provider } @@ -201,6 +223,17 @@ func NewCredentials(provider Provider) *Credentials { // If Credentials.Expire() was called the credentials Value will be force // expired, and the next call to Get() will cause them to be refreshed. func (c *Credentials) Get() (Value, error) { + // Check the cached credentials first with just the read lock. + c.m.RLock() + if !c.isExpired() { + creds := c.creds + c.m.RUnlock() + return creds, nil + } + c.m.RUnlock() + + // Credentials are expired need to retrieve the credentials taking the full + // lock. c.m.Lock() defer c.m.Unlock() @@ -234,8 +267,8 @@ func (c *Credentials) Expire() { // If the Credentials were forced to be expired with Expire() this will // reflect that override. func (c *Credentials) IsExpired() bool { - c.m.Lock() - defer c.m.Unlock() + c.m.RLock() + defer c.m.RUnlock() return c.isExpired() } @@ -244,3 +277,23 @@ func (c *Credentials) IsExpired() bool { func (c *Credentials) isExpired() bool { return c.forceRefresh || c.provider.IsExpired() } + +// ExpiresAt provides access to the functionality of the Expirer interface of +// the underlying Provider, if it supports that interface. Otherwise, it returns +// an error. +func (c *Credentials) ExpiresAt() (time.Time, error) { + c.m.RLock() + defer c.m.RUnlock() + + expirer, ok := c.provider.(Expirer) + if !ok { + return time.Time{}, awserr.New("ProviderNotExpirer", + fmt.Sprintf("provider %s does not support ExpiresAt()", c.creds.ProviderName), + nil) + } + if c.forceRefresh { + // set expiration time to the distant past + return time.Time{}, nil + } + return expirer.ExpiresAt(), nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go b/vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go index c39749524ecac..43d4ed386ab85 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go @@ -4,7 +4,6 @@ import ( "bufio" "encoding/json" "fmt" - "path" "strings" "time" @@ -12,6 +11,8 @@ import ( "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/ec2metadata" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/internal/sdkuri" ) // ProviderName provides a name of EC2Role provider @@ -125,7 +126,7 @@ type ec2RoleCredRespBody struct { Message string } -const iamSecurityCredsPath = "/iam/security-credentials" +const iamSecurityCredsPath = "iam/security-credentials/" // requestCredList requests a list of credentials from the EC2 service. // If there are no credentials, or there is an error making or receiving the request @@ -142,7 +143,8 @@ func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) { } if err := s.Err(); err != nil { - return nil, awserr.New("SerializationError", "failed to read EC2 instance role from metadata service", err) + return nil, awserr.New(request.ErrCodeSerialization, + "failed to read EC2 instance role from metadata service", err) } return credsList, nil @@ -153,7 +155,7 @@ func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) { // If the credentials cannot be found, or there is an error reading the response // and error will be returned. func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) { - resp, err := client.GetMetadata(path.Join(iamSecurityCredsPath, credsName)) + resp, err := client.GetMetadata(sdkuri.PathJoin(iamSecurityCredsPath, credsName)) if err != nil { return ec2RoleCredRespBody{}, awserr.New("EC2RoleRequestError", @@ -164,7 +166,7 @@ func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCred respCreds := ec2RoleCredRespBody{} if err := json.NewDecoder(strings.NewReader(resp)).Decode(&respCreds); err != nil { return ec2RoleCredRespBody{}, - awserr.New("SerializationError", + awserr.New(request.ErrCodeSerialization, fmt.Sprintf("failed to decode %s EC2 instance role credentials", credsName), err) } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/credentials/endpointcreds/provider.go b/vendor/github.com/aws/aws-sdk-go/aws/credentials/endpointcreds/provider.go index a4cec5c553a48..1a7af53a4da17 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/credentials/endpointcreds/provider.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/credentials/endpointcreds/provider.go @@ -39,6 +39,7 @@ import ( "github.com/aws/aws-sdk-go/aws/client/metadata" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" ) // ProviderName is the name of the credentials provider. @@ -65,6 +66,10 @@ type Provider struct { // // If ExpiryWindow is 0 or less it will be ignored. ExpiryWindow time.Duration + + // Optional authorization token value if set will be used as the value of + // the Authorization header of the endpoint credential request. + AuthorizationToken string } // NewProviderClient returns a credentials Provider for retrieving AWS credentials @@ -93,8 +98,8 @@ func NewProviderClient(cfg aws.Config, handlers request.Handlers, endpoint strin return p } -// NewCredentialsClient returns a Credentials wrapper for retrieving credentials -// from an arbitrary endpoint concurrently. The client will request the +// NewCredentialsClient returns a pointer to a new Credentials object +// wrapping the endpoint credentials Provider. func NewCredentialsClient(cfg aws.Config, handlers request.Handlers, endpoint string, options ...func(*Provider)) *credentials.Credentials { return credentials.NewCredentials(NewProviderClient(cfg, handlers, endpoint, options...)) } @@ -152,6 +157,9 @@ func (p *Provider) getCredentials() (*getCredentialsOutput, error) { out := &getCredentialsOutput{} req := p.Client.NewRequest(op, nil, out) req.HTTPRequest.Header.Set("Accept", "application/json") + if authToken := p.AuthorizationToken; len(authToken) != 0 { + req.HTTPRequest.Header.Set("Authorization", authToken) + } return out, req.Send() } @@ -167,7 +175,7 @@ func unmarshalHandler(r *request.Request) { out := r.Data.(*getCredentialsOutput) if err := json.NewDecoder(r.HTTPResponse.Body).Decode(&out); err != nil { - r.Error = awserr.New("SerializationError", + r.Error = awserr.New(request.ErrCodeSerialization, "failed to decode endpoint credentials", err, ) @@ -178,11 +186,15 @@ func unmarshalError(r *request.Request) { defer r.HTTPResponse.Body.Close() var errOut errorOutput - if err := json.NewDecoder(r.HTTPResponse.Body).Decode(&errOut); err != nil { - r.Error = awserr.New("SerializationError", - "failed to decode endpoint credentials", - err, + err := jsonutil.UnmarshalJSONError(&errOut, r.HTTPResponse.Body) + if err != nil { + r.Error = awserr.NewRequestFailure( + awserr.New(request.ErrCodeSerialization, + "failed to decode error message", err), + r.HTTPResponse.StatusCode, + r.RequestID, ) + return } // Response body format is not consistent between metadata endpoints. diff --git a/vendor/github.com/aws/aws-sdk-go/aws/credentials/env_provider.go b/vendor/github.com/aws/aws-sdk-go/aws/credentials/env_provider.go index c14231a16f2b4..54c5cf7333ffc 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/credentials/env_provider.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/credentials/env_provider.go @@ -12,14 +12,10 @@ const EnvProviderName = "EnvProvider" var ( // ErrAccessKeyIDNotFound is returned when the AWS Access Key ID can't be // found in the process's environment. - // - // @readonly ErrAccessKeyIDNotFound = awserr.New("EnvAccessKeyNotFound", "AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY not found in environment", nil) // ErrSecretAccessKeyNotFound is returned when the AWS Secret Access Key // can't be found in the process's environment. - // - // @readonly ErrSecretAccessKeyNotFound = awserr.New("EnvSecretNotFound", "AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY not found in environment", nil) ) diff --git a/vendor/github.com/aws/aws-sdk-go/aws/credentials/processcreds/provider.go b/vendor/github.com/aws/aws-sdk-go/aws/credentials/processcreds/provider.go new file mode 100644 index 0000000000000..e62483600299c --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/credentials/processcreds/provider.go @@ -0,0 +1,426 @@ +/* +Package processcreds is a credential Provider to retrieve `credential_process` +credentials. + +WARNING: The following describes a method of sourcing credentials from an external +process. This can potentially be dangerous, so proceed with caution. Other +credential providers should be preferred if at all possible. If using this +option, you should make sure that the config file is as locked down as possible +using security best practices for your operating system. + +You can use credentials from a `credential_process` in a variety of ways. + +One way is to setup your shared config file, located in the default +location, with the `credential_process` key and the command you want to be +called. You also need to set the AWS_SDK_LOAD_CONFIG environment variable +(e.g., `export AWS_SDK_LOAD_CONFIG=1`) to use the shared config file. + + [default] + credential_process = /command/to/call + +Creating a new session will use the credential process to retrieve credentials. +NOTE: If there are credentials in the profile you are using, the credential +process will not be used. + + // Initialize a session to load credentials. + sess, _ := session.NewSession(&aws.Config{ + Region: aws.String("us-east-1")}, + ) + + // Create S3 service client to use the credentials. + svc := s3.New(sess) + +Another way to use the `credential_process` method is by using +`credentials.NewCredentials()` and providing a command to be executed to +retrieve credentials: + + // Create credentials using the ProcessProvider. + creds := processcreds.NewCredentials("/path/to/command") + + // Create service client value configured for credentials. + svc := s3.New(sess, &aws.Config{Credentials: creds}) + +You can set a non-default timeout for the `credential_process` with another +constructor, `credentials.NewCredentialsTimeout()`, providing the timeout. To +set a one minute timeout: + + // Create credentials using the ProcessProvider. + creds := processcreds.NewCredentialsTimeout( + "/path/to/command", + time.Duration(500) * time.Millisecond) + +If you need more control, you can set any configurable options in the +credentials using one or more option functions. For example, you can set a two +minute timeout, a credential duration of 60 minutes, and a maximum stdout +buffer size of 2k. + + creds := processcreds.NewCredentials( + "/path/to/command", + func(opt *ProcessProvider) { + opt.Timeout = time.Duration(2) * time.Minute + opt.Duration = time.Duration(60) * time.Minute + opt.MaxBufSize = 2048 + }) + +You can also use your own `exec.Cmd`: + + // Create an exec.Cmd + myCommand := exec.Command("/path/to/command") + + // Create credentials using your exec.Cmd and custom timeout + creds := processcreds.NewCredentialsCommand( + myCommand, + func(opt *processcreds.ProcessProvider) { + opt.Timeout = time.Duration(1) * time.Second + }) +*/ +package processcreds + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "runtime" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/internal/sdkio" +) + +const ( + // ProviderName is the name this credentials provider will label any + // returned credentials Value with. + ProviderName = `ProcessProvider` + + // ErrCodeProcessProviderParse error parsing process output + ErrCodeProcessProviderParse = "ProcessProviderParseError" + + // ErrCodeProcessProviderVersion version error in output + ErrCodeProcessProviderVersion = "ProcessProviderVersionError" + + // ErrCodeProcessProviderRequired required attribute missing in output + ErrCodeProcessProviderRequired = "ProcessProviderRequiredError" + + // ErrCodeProcessProviderExecution execution of command failed + ErrCodeProcessProviderExecution = "ProcessProviderExecutionError" + + // errMsgProcessProviderTimeout process took longer than allowed + errMsgProcessProviderTimeout = "credential process timed out" + + // errMsgProcessProviderProcess process error + errMsgProcessProviderProcess = "error in credential_process" + + // errMsgProcessProviderParse problem parsing output + errMsgProcessProviderParse = "parse failed of credential_process output" + + // errMsgProcessProviderVersion version error in output + errMsgProcessProviderVersion = "wrong version in process output (not 1)" + + // errMsgProcessProviderMissKey missing access key id in output + errMsgProcessProviderMissKey = "missing AccessKeyId in process output" + + // errMsgProcessProviderMissSecret missing secret acess key in output + errMsgProcessProviderMissSecret = "missing SecretAccessKey in process output" + + // errMsgProcessProviderPrepareCmd prepare of command failed + errMsgProcessProviderPrepareCmd = "failed to prepare command" + + // errMsgProcessProviderEmptyCmd command must not be empty + errMsgProcessProviderEmptyCmd = "command must not be empty" + + // errMsgProcessProviderPipe failed to initialize pipe + errMsgProcessProviderPipe = "failed to initialize pipe" + + // DefaultDuration is the default amount of time in minutes that the + // credentials will be valid for. + DefaultDuration = time.Duration(15) * time.Minute + + // DefaultBufSize limits buffer size from growing to an enormous + // amount due to a faulty process. + DefaultBufSize = int(8 * sdkio.KibiByte) + + // DefaultTimeout default limit on time a process can run. + DefaultTimeout = time.Duration(1) * time.Minute +) + +// ProcessProvider satisfies the credentials.Provider interface, and is a +// client to retrieve credentials from a process. +type ProcessProvider struct { + staticCreds bool + credentials.Expiry + originalCommand []string + + // Expiry duration of the credentials. Defaults to 15 minutes if not set. + Duration time.Duration + + // ExpiryWindow will allow the credentials to trigger refreshing prior to + // the credentials actually expiring. This is beneficial so race conditions + // with expiring credentials do not cause request to fail unexpectedly + // due to ExpiredTokenException exceptions. + // + // So a ExpiryWindow of 10s would cause calls to IsExpired() to return true + // 10 seconds before the credentials are actually expired. + // + // If ExpiryWindow is 0 or less it will be ignored. + ExpiryWindow time.Duration + + // A string representing an os command that should return a JSON with + // credential information. + command *exec.Cmd + + // MaxBufSize limits memory usage from growing to an enormous + // amount due to a faulty process. + MaxBufSize int + + // Timeout limits the time a process can run. + Timeout time.Duration +} + +// NewCredentials returns a pointer to a new Credentials object wrapping the +// ProcessProvider. The credentials will expire every 15 minutes by default. +func NewCredentials(command string, options ...func(*ProcessProvider)) *credentials.Credentials { + p := &ProcessProvider{ + command: exec.Command(command), + Duration: DefaultDuration, + Timeout: DefaultTimeout, + MaxBufSize: DefaultBufSize, + } + + for _, option := range options { + option(p) + } + + return credentials.NewCredentials(p) +} + +// NewCredentialsTimeout returns a pointer to a new Credentials object with +// the specified command and timeout, and default duration and max buffer size. +func NewCredentialsTimeout(command string, timeout time.Duration) *credentials.Credentials { + p := NewCredentials(command, func(opt *ProcessProvider) { + opt.Timeout = timeout + }) + + return p +} + +// NewCredentialsCommand returns a pointer to a new Credentials object with +// the specified command, and default timeout, duration and max buffer size. +func NewCredentialsCommand(command *exec.Cmd, options ...func(*ProcessProvider)) *credentials.Credentials { + p := &ProcessProvider{ + command: command, + Duration: DefaultDuration, + Timeout: DefaultTimeout, + MaxBufSize: DefaultBufSize, + } + + for _, option := range options { + option(p) + } + + return credentials.NewCredentials(p) +} + +type credentialProcessResponse struct { + Version int + AccessKeyID string `json:"AccessKeyId"` + SecretAccessKey string + SessionToken string + Expiration *time.Time +} + +// Retrieve executes the 'credential_process' and returns the credentials. +func (p *ProcessProvider) Retrieve() (credentials.Value, error) { + out, err := p.executeCredentialProcess() + if err != nil { + return credentials.Value{ProviderName: ProviderName}, err + } + + // Serialize and validate response + resp := &credentialProcessResponse{} + if err = json.Unmarshal(out, resp); err != nil { + return credentials.Value{ProviderName: ProviderName}, awserr.New( + ErrCodeProcessProviderParse, + fmt.Sprintf("%s: %s", errMsgProcessProviderParse, string(out)), + err) + } + + if resp.Version != 1 { + return credentials.Value{ProviderName: ProviderName}, awserr.New( + ErrCodeProcessProviderVersion, + errMsgProcessProviderVersion, + nil) + } + + if len(resp.AccessKeyID) == 0 { + return credentials.Value{ProviderName: ProviderName}, awserr.New( + ErrCodeProcessProviderRequired, + errMsgProcessProviderMissKey, + nil) + } + + if len(resp.SecretAccessKey) == 0 { + return credentials.Value{ProviderName: ProviderName}, awserr.New( + ErrCodeProcessProviderRequired, + errMsgProcessProviderMissSecret, + nil) + } + + // Handle expiration + p.staticCreds = resp.Expiration == nil + if resp.Expiration != nil { + p.SetExpiration(*resp.Expiration, p.ExpiryWindow) + } + + return credentials.Value{ + ProviderName: ProviderName, + AccessKeyID: resp.AccessKeyID, + SecretAccessKey: resp.SecretAccessKey, + SessionToken: resp.SessionToken, + }, nil +} + +// IsExpired returns true if the credentials retrieved are expired, or not yet +// retrieved. +func (p *ProcessProvider) IsExpired() bool { + if p.staticCreds { + return false + } + return p.Expiry.IsExpired() +} + +// prepareCommand prepares the command to be executed. +func (p *ProcessProvider) prepareCommand() error { + + var cmdArgs []string + if runtime.GOOS == "windows" { + cmdArgs = []string{"cmd.exe", "/C"} + } else { + cmdArgs = []string{"sh", "-c"} + } + + if len(p.originalCommand) == 0 { + p.originalCommand = make([]string, len(p.command.Args)) + copy(p.originalCommand, p.command.Args) + + // check for empty command because it succeeds + if len(strings.TrimSpace(p.originalCommand[0])) < 1 { + return awserr.New( + ErrCodeProcessProviderExecution, + fmt.Sprintf( + "%s: %s", + errMsgProcessProviderPrepareCmd, + errMsgProcessProviderEmptyCmd), + nil) + } + } + + cmdArgs = append(cmdArgs, p.originalCommand...) + p.command = exec.Command(cmdArgs[0], cmdArgs[1:]...) + p.command.Env = os.Environ() + + return nil +} + +// executeCredentialProcess starts the credential process on the OS and +// returns the results or an error. +func (p *ProcessProvider) executeCredentialProcess() ([]byte, error) { + + if err := p.prepareCommand(); err != nil { + return nil, err + } + + // Setup the pipes + outReadPipe, outWritePipe, err := os.Pipe() + if err != nil { + return nil, awserr.New( + ErrCodeProcessProviderExecution, + errMsgProcessProviderPipe, + err) + } + + p.command.Stderr = os.Stderr // display stderr on console for MFA + p.command.Stdout = outWritePipe // get creds json on process's stdout + p.command.Stdin = os.Stdin // enable stdin for MFA + + output := bytes.NewBuffer(make([]byte, 0, p.MaxBufSize)) + + stdoutCh := make(chan error, 1) + go readInput( + io.LimitReader(outReadPipe, int64(p.MaxBufSize)), + output, + stdoutCh) + + execCh := make(chan error, 1) + go executeCommand(*p.command, execCh) + + finished := false + var errors []error + for !finished { + select { + case readError := <-stdoutCh: + errors = appendError(errors, readError) + finished = true + case execError := <-execCh: + err := outWritePipe.Close() + errors = appendError(errors, err) + errors = appendError(errors, execError) + if errors != nil { + return output.Bytes(), awserr.NewBatchError( + ErrCodeProcessProviderExecution, + errMsgProcessProviderProcess, + errors) + } + case <-time.After(p.Timeout): + finished = true + return output.Bytes(), awserr.NewBatchError( + ErrCodeProcessProviderExecution, + errMsgProcessProviderTimeout, + errors) // errors can be nil + } + } + + out := output.Bytes() + + if runtime.GOOS == "windows" { + // windows adds slashes to quotes + out = []byte(strings.Replace(string(out), `\"`, `"`, -1)) + } + + return out, nil +} + +// appendError conveniently checks for nil before appending slice +func appendError(errors []error, err error) []error { + if err != nil { + return append(errors, err) + } + return errors +} + +func executeCommand(cmd exec.Cmd, exec chan error) { + // Start the command + err := cmd.Start() + if err == nil { + err = cmd.Wait() + } + + exec <- err +} + +func readInput(r io.Reader, w io.Writer, read chan error) { + tee := io.TeeReader(r, w) + + _, err := ioutil.ReadAll(tee) + + if err == io.EOF { + err = nil + } + + read <- err // will only arrive here when write end of pipe is closed +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go b/vendor/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go index 51e21e0f38f65..e1551495812ab 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/credentials/shared_credentials_provider.go @@ -4,9 +4,8 @@ import ( "fmt" "os" - "github.com/go-ini/ini" - "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/internal/ini" "github.com/aws/aws-sdk-go/internal/shareddefaults" ) @@ -77,36 +76,37 @@ func (p *SharedCredentialsProvider) IsExpired() bool { // The credentials retrieved from the profile will be returned or error. Error will be // returned if it fails to read from the file, or the data is invalid. func loadProfile(filename, profile string) (Value, error) { - config, err := ini.Load(filename) + config, err := ini.OpenFile(filename) if err != nil { return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsLoad", "failed to load shared credentials file", err) } - iniProfile, err := config.GetSection(profile) - if err != nil { - return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsLoad", "failed to get profile", err) + + iniProfile, ok := config.GetSection(profile) + if !ok { + return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsLoad", "failed to get profile", nil) } - id, err := iniProfile.GetKey("aws_access_key_id") - if err != nil { + id := iniProfile.String("aws_access_key_id") + if len(id) == 0 { return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsAccessKey", fmt.Sprintf("shared credentials %s in %s did not contain aws_access_key_id", profile, filename), - err) + nil) } - secret, err := iniProfile.GetKey("aws_secret_access_key") - if err != nil { + secret := iniProfile.String("aws_secret_access_key") + if len(secret) == 0 { return Value{ProviderName: SharedCredsProviderName}, awserr.New("SharedCredsSecret", fmt.Sprintf("shared credentials %s in %s did not contain aws_secret_access_key", profile, filename), nil) } // Default to empty string if not found - token := iniProfile.Key("aws_session_token") + token := iniProfile.String("aws_session_token") return Value{ - AccessKeyID: id.String(), - SecretAccessKey: secret.String(), - SessionToken: token.String(), + AccessKeyID: id, + SecretAccessKey: secret, + SessionToken: token, ProviderName: SharedCredsProviderName, }, nil } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go b/vendor/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go index 4f5dab3fcc477..531139e3971a2 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go @@ -9,8 +9,6 @@ const StaticProviderName = "StaticProvider" var ( // ErrStaticCredentialsEmpty is emitted when static credentials are empty. - // - // @readonly ErrStaticCredentialsEmpty = awserr.New("EmptyStaticCreds", "static credentials are empty", nil) ) diff --git a/vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go b/vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go index 4108e433e64a1..9f37f44bcfa32 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/assume_role_provider.go @@ -80,16 +80,18 @@ package stscreds import ( "fmt" + "os" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/internal/sdkrand" "github.com/aws/aws-sdk-go/service/sts" ) -// StdinTokenProvider will prompt on stdout and read from stdin for a string value. +// StdinTokenProvider will prompt on stderr and read from stdin for a string value. // An error is returned if reading from stdin fails. // // Use this function go read MFA tokens from stdin. The function makes no attempt @@ -102,7 +104,7 @@ import ( // Will wait forever until something is provided on the stdin. func StdinTokenProvider() (string, error) { var v string - fmt.Printf("Assume Role MFA token code: ") + fmt.Fprintf(os.Stderr, "Assume Role MFA token code: ") _, err := fmt.Scanln(&v) return v, err @@ -142,6 +144,13 @@ type AssumeRoleProvider struct { // Session name, if you wish to reuse the credentials elsewhere. RoleSessionName string + // Optional, you can pass tag key-value pairs to your session. These tags are called session tags. + Tags []*sts.Tag + + // A list of keys for session tags that you want to set as transitive. + // If you set a tag key as transitive, the corresponding key and value passes to subsequent sessions in a role chain. + TransitiveTagKeys []*string + // Expiry duration of the STS credentials. Defaults to 15 minutes if not set. Duration time.Duration @@ -193,6 +202,18 @@ type AssumeRoleProvider struct { // // If ExpiryWindow is 0 or less it will be ignored. ExpiryWindow time.Duration + + // MaxJitterFrac reduces the effective Duration of each credential requested + // by a random percentage between 0 and MaxJitterFraction. MaxJitterFrac must + // have a value between 0 and 1. Any other value may lead to expected behavior. + // With a MaxJitterFrac value of 0, default) will no jitter will be used. + // + // For example, with a Duration of 30m and a MaxJitterFrac of 0.1, the + // AssumeRole call will be made with an arbitrary Duration between 27m and + // 30m. + // + // MaxJitterFrac should not be negative. + MaxJitterFrac float64 } // NewCredentials returns a pointer to a new Credentials object wrapping the @@ -244,7 +265,6 @@ func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(* // Retrieve generates a new set of temporary credentials using STS. func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) { - // Apply defaults where parameters are not set. if p.RoleSessionName == "" { // Try to work out a role name that will hopefully end up unique. @@ -254,11 +274,14 @@ func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) { // Expire as often as AWS permits. p.Duration = DefaultDuration } + jitter := time.Duration(sdkrand.SeededRand.Float64() * p.MaxJitterFrac * float64(p.Duration)) input := &sts.AssumeRoleInput{ - DurationSeconds: aws.Int64(int64(p.Duration / time.Second)), - RoleArn: aws.String(p.RoleARN), - RoleSessionName: aws.String(p.RoleSessionName), - ExternalId: p.ExternalID, + DurationSeconds: aws.Int64(int64((p.Duration - jitter) / time.Second)), + RoleArn: aws.String(p.RoleARN), + RoleSessionName: aws.String(p.RoleSessionName), + ExternalId: p.ExternalID, + Tags: p.Tags, + TransitiveTagKeys: p.TransitiveTagKeys, } if p.Policy != nil { input.Policy = p.Policy diff --git a/vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/web_identity_provider.go b/vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/web_identity_provider.go new file mode 100644 index 0000000000000..b20b63394847c --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/credentials/stscreds/web_identity_provider.go @@ -0,0 +1,100 @@ +package stscreds + +import ( + "fmt" + "io/ioutil" + "strconv" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/service/sts" + "github.com/aws/aws-sdk-go/service/sts/stsiface" +) + +const ( + // ErrCodeWebIdentity will be used as an error code when constructing + // a new error to be returned during session creation or retrieval. + ErrCodeWebIdentity = "WebIdentityErr" + + // WebIdentityProviderName is the web identity provider name + WebIdentityProviderName = "WebIdentityCredentials" +) + +// now is used to return a time.Time object representing +// the current time. This can be used to easily test and +// compare test values. +var now = time.Now + +// WebIdentityRoleProvider is used to retrieve credentials using +// an OIDC token. +type WebIdentityRoleProvider struct { + credentials.Expiry + + client stsiface.STSAPI + ExpiryWindow time.Duration + + tokenFilePath string + roleARN string + roleSessionName string +} + +// NewWebIdentityCredentials will return a new set of credentials with a given +// configuration, role arn, and token file path. +func NewWebIdentityCredentials(c client.ConfigProvider, roleARN, roleSessionName, path string) *credentials.Credentials { + svc := sts.New(c) + p := NewWebIdentityRoleProvider(svc, roleARN, roleSessionName, path) + return credentials.NewCredentials(p) +} + +// NewWebIdentityRoleProvider will return a new WebIdentityRoleProvider with the +// provided stsiface.STSAPI +func NewWebIdentityRoleProvider(svc stsiface.STSAPI, roleARN, roleSessionName, path string) *WebIdentityRoleProvider { + return &WebIdentityRoleProvider{ + client: svc, + tokenFilePath: path, + roleARN: roleARN, + roleSessionName: roleSessionName, + } +} + +// Retrieve will attempt to assume a role from a token which is located at +// 'WebIdentityTokenFilePath' specified destination and if that is empty an +// error will be returned. +func (p *WebIdentityRoleProvider) Retrieve() (credentials.Value, error) { + b, err := ioutil.ReadFile(p.tokenFilePath) + if err != nil { + errMsg := fmt.Sprintf("unable to read file at %s", p.tokenFilePath) + return credentials.Value{}, awserr.New(ErrCodeWebIdentity, errMsg, err) + } + + sessionName := p.roleSessionName + if len(sessionName) == 0 { + // session name is used to uniquely identify a session. This simply + // uses unix time in nanoseconds to uniquely identify sessions. + sessionName = strconv.FormatInt(now().UnixNano(), 10) + } + req, resp := p.client.AssumeRoleWithWebIdentityRequest(&sts.AssumeRoleWithWebIdentityInput{ + RoleArn: &p.roleARN, + RoleSessionName: &sessionName, + WebIdentityToken: aws.String(string(b)), + }) + // InvalidIdentityToken error is a temporary error that can occur + // when assuming an Role with a JWT web identity token. + req.RetryErrorCodes = append(req.RetryErrorCodes, sts.ErrCodeInvalidIdentityTokenException) + if err := req.Send(); err != nil { + return credentials.Value{}, awserr.New(ErrCodeWebIdentity, "failed to retrieve credentials", err) + } + + p.SetExpiration(aws.TimeValue(resp.Credentials.Expiration), p.ExpiryWindow) + + value := credentials.Value{ + AccessKeyID: aws.StringValue(resp.Credentials.AccessKeyId), + SecretAccessKey: aws.StringValue(resp.Credentials.SecretAccessKey), + SessionToken: aws.StringValue(resp.Credentials.SessionToken), + ProviderName: WebIdentityProviderName, + } + return value, nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/csm/doc.go b/vendor/github.com/aws/aws-sdk-go/aws/csm/doc.go new file mode 100644 index 0000000000000..25a66d1dda22d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/csm/doc.go @@ -0,0 +1,69 @@ +// Package csm provides the Client Side Monitoring (CSM) client which enables +// sending metrics via UDP connection to the CSM agent. This package provides +// control options, and configuration for the CSM client. The client can be +// controlled manually, or automatically via the SDK's Session configuration. +// +// Enabling CSM client via SDK's Session configuration +// +// The CSM client can be enabled automatically via SDK's Session configuration. +// The SDK's session configuration enables the CSM client if the AWS_CSM_PORT +// environment variable is set to a non-empty value. +// +// The configuration options for the CSM client via the SDK's session +// configuration are: +// +// * AWS_CSM_PORT= +// The port number the CSM agent will receive metrics on. +// +// * AWS_CSM_HOST= +// The hostname, or IP address the CSM agent will receive metrics on. +// Without port number. +// +// Manually enabling the CSM client +// +// The CSM client can be started, paused, and resumed manually. The Start +// function will enable the CSM client to publish metrics to the CSM agent. It +// is safe to call Start concurrently, but if Start is called additional times +// with different ClientID or address it will panic. +// +// r, err := csm.Start("clientID", ":31000") +// if err != nil { +// panic(fmt.Errorf("failed starting CSM: %v", err)) +// } +// +// When controlling the CSM client manually, you must also inject its request +// handlers into the SDK's Session configuration for the SDK's API clients to +// publish metrics. +// +// sess, err := session.NewSession(&aws.Config{}) +// if err != nil { +// panic(fmt.Errorf("failed loading session: %v", err)) +// } +// +// // Add CSM client's metric publishing request handlers to the SDK's +// // Session Configuration. +// r.InjectHandlers(&sess.Handlers) +// +// Controlling CSM client +// +// Once the CSM client has been enabled the Get function will return a Reporter +// value that you can use to pause and resume the metrics published to the CSM +// agent. If Get function is called before the reporter is enabled with the +// Start function or via SDK's Session configuration nil will be returned. +// +// The Pause method can be called to stop the CSM client publishing metrics to +// the CSM agent. The Continue method will resume metric publishing. +// +// // Get the CSM client Reporter. +// r := csm.Get() +// +// // Will pause monitoring +// r.Pause() +// resp, err = client.GetObject(&s3.GetObjectInput{ +// Bucket: aws.String("bucket"), +// Key: aws.String("key"), +// }) +// +// // Resume monitoring +// r.Continue() +package csm diff --git a/vendor/github.com/aws/aws-sdk-go/aws/csm/enable.go b/vendor/github.com/aws/aws-sdk-go/aws/csm/enable.go new file mode 100644 index 0000000000000..4b19e2800e3c8 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/csm/enable.go @@ -0,0 +1,89 @@ +package csm + +import ( + "fmt" + "strings" + "sync" +) + +var ( + lock sync.Mutex +) + +const ( + // DefaultPort is used when no port is specified. + DefaultPort = "31000" + + // DefaultHost is the host that will be used when none is specified. + DefaultHost = "127.0.0.1" +) + +// AddressWithDefaults returns a CSM address built from the host and port +// values. If the host or port is not set, default values will be used +// instead. If host is "localhost" it will be replaced with "127.0.0.1". +func AddressWithDefaults(host, port string) string { + if len(host) == 0 || strings.EqualFold(host, "localhost") { + host = DefaultHost + } + + if len(port) == 0 { + port = DefaultPort + } + + // Only IP6 host can contain a colon + if strings.Contains(host, ":") { + return "[" + host + "]:" + port + } + + return host + ":" + port +} + +// Start will start a long running go routine to capture +// client side metrics. Calling start multiple time will only +// start the metric listener once and will panic if a different +// client ID or port is passed in. +// +// r, err := csm.Start("clientID", "127.0.0.1:31000") +// if err != nil { +// panic(fmt.Errorf("expected no error, but received %v", err)) +// } +// sess := session.NewSession() +// r.InjectHandlers(sess.Handlers) +// +// svc := s3.New(sess) +// out, err := svc.GetObject(&s3.GetObjectInput{ +// Bucket: aws.String("bucket"), +// Key: aws.String("key"), +// }) +func Start(clientID string, url string) (*Reporter, error) { + lock.Lock() + defer lock.Unlock() + + if sender == nil { + sender = newReporter(clientID, url) + } else { + if sender.clientID != clientID { + panic(fmt.Errorf("inconsistent client IDs. %q was expected, but received %q", sender.clientID, clientID)) + } + + if sender.url != url { + panic(fmt.Errorf("inconsistent URLs. %q was expected, but received %q", sender.url, url)) + } + } + + if err := connect(url); err != nil { + sender = nil + return nil, err + } + + return sender, nil +} + +// Get will return a reporter if one exists, if one does not exist, nil will +// be returned. +func Get() *Reporter { + lock.Lock() + defer lock.Unlock() + + return sender +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/csm/metric.go b/vendor/github.com/aws/aws-sdk-go/aws/csm/metric.go new file mode 100644 index 0000000000000..5bacc791a1e9b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/csm/metric.go @@ -0,0 +1,109 @@ +package csm + +import ( + "strconv" + "time" + + "github.com/aws/aws-sdk-go/aws" +) + +type metricTime time.Time + +func (t metricTime) MarshalJSON() ([]byte, error) { + ns := time.Duration(time.Time(t).UnixNano()) + return []byte(strconv.FormatInt(int64(ns/time.Millisecond), 10)), nil +} + +type metric struct { + ClientID *string `json:"ClientId,omitempty"` + API *string `json:"Api,omitempty"` + Service *string `json:"Service,omitempty"` + Timestamp *metricTime `json:"Timestamp,omitempty"` + Type *string `json:"Type,omitempty"` + Version *int `json:"Version,omitempty"` + + AttemptCount *int `json:"AttemptCount,omitempty"` + Latency *int `json:"Latency,omitempty"` + + Fqdn *string `json:"Fqdn,omitempty"` + UserAgent *string `json:"UserAgent,omitempty"` + AttemptLatency *int `json:"AttemptLatency,omitempty"` + + SessionToken *string `json:"SessionToken,omitempty"` + Region *string `json:"Region,omitempty"` + AccessKey *string `json:"AccessKey,omitempty"` + HTTPStatusCode *int `json:"HttpStatusCode,omitempty"` + XAmzID2 *string `json:"XAmzId2,omitempty"` + XAmzRequestID *string `json:"XAmznRequestId,omitempty"` + + AWSException *string `json:"AwsException,omitempty"` + AWSExceptionMessage *string `json:"AwsExceptionMessage,omitempty"` + SDKException *string `json:"SdkException,omitempty"` + SDKExceptionMessage *string `json:"SdkExceptionMessage,omitempty"` + + FinalHTTPStatusCode *int `json:"FinalHttpStatusCode,omitempty"` + FinalAWSException *string `json:"FinalAwsException,omitempty"` + FinalAWSExceptionMessage *string `json:"FinalAwsExceptionMessage,omitempty"` + FinalSDKException *string `json:"FinalSdkException,omitempty"` + FinalSDKExceptionMessage *string `json:"FinalSdkExceptionMessage,omitempty"` + + DestinationIP *string `json:"DestinationIp,omitempty"` + ConnectionReused *int `json:"ConnectionReused,omitempty"` + + AcquireConnectionLatency *int `json:"AcquireConnectionLatency,omitempty"` + ConnectLatency *int `json:"ConnectLatency,omitempty"` + RequestLatency *int `json:"RequestLatency,omitempty"` + DNSLatency *int `json:"DnsLatency,omitempty"` + TCPLatency *int `json:"TcpLatency,omitempty"` + SSLLatency *int `json:"SslLatency,omitempty"` + + MaxRetriesExceeded *int `json:"MaxRetriesExceeded,omitempty"` +} + +func (m *metric) TruncateFields() { + m.ClientID = truncateString(m.ClientID, 255) + m.UserAgent = truncateString(m.UserAgent, 256) + + m.AWSException = truncateString(m.AWSException, 128) + m.AWSExceptionMessage = truncateString(m.AWSExceptionMessage, 512) + + m.SDKException = truncateString(m.SDKException, 128) + m.SDKExceptionMessage = truncateString(m.SDKExceptionMessage, 512) + + m.FinalAWSException = truncateString(m.FinalAWSException, 128) + m.FinalAWSExceptionMessage = truncateString(m.FinalAWSExceptionMessage, 512) + + m.FinalSDKException = truncateString(m.FinalSDKException, 128) + m.FinalSDKExceptionMessage = truncateString(m.FinalSDKExceptionMessage, 512) +} + +func truncateString(v *string, l int) *string { + if v != nil && len(*v) > l { + nv := (*v)[:l] + return &nv + } + + return v +} + +func (m *metric) SetException(e metricException) { + switch te := e.(type) { + case awsException: + m.AWSException = aws.String(te.exception) + m.AWSExceptionMessage = aws.String(te.message) + case sdkException: + m.SDKException = aws.String(te.exception) + m.SDKExceptionMessage = aws.String(te.message) + } +} + +func (m *metric) SetFinalException(e metricException) { + switch te := e.(type) { + case awsException: + m.FinalAWSException = aws.String(te.exception) + m.FinalAWSExceptionMessage = aws.String(te.message) + case sdkException: + m.FinalSDKException = aws.String(te.exception) + m.FinalSDKExceptionMessage = aws.String(te.message) + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/csm/metric_chan.go b/vendor/github.com/aws/aws-sdk-go/aws/csm/metric_chan.go new file mode 100644 index 0000000000000..82a3e345e936a --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/csm/metric_chan.go @@ -0,0 +1,55 @@ +package csm + +import ( + "sync/atomic" +) + +const ( + runningEnum = iota + pausedEnum +) + +var ( + // MetricsChannelSize of metrics to hold in the channel + MetricsChannelSize = 100 +) + +type metricChan struct { + ch chan metric + paused *int64 +} + +func newMetricChan(size int) metricChan { + return metricChan{ + ch: make(chan metric, size), + paused: new(int64), + } +} + +func (ch *metricChan) Pause() { + atomic.StoreInt64(ch.paused, pausedEnum) +} + +func (ch *metricChan) Continue() { + atomic.StoreInt64(ch.paused, runningEnum) +} + +func (ch *metricChan) IsPaused() bool { + v := atomic.LoadInt64(ch.paused) + return v == pausedEnum +} + +// Push will push metrics to the metric channel if the channel +// is not paused +func (ch *metricChan) Push(m metric) bool { + if ch.IsPaused() { + return false + } + + select { + case ch.ch <- m: + return true + default: + return false + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/csm/metric_exception.go b/vendor/github.com/aws/aws-sdk-go/aws/csm/metric_exception.go new file mode 100644 index 0000000000000..54a99280ce9c1 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/csm/metric_exception.go @@ -0,0 +1,26 @@ +package csm + +type metricException interface { + Exception() string + Message() string +} + +type requestException struct { + exception string + message string +} + +func (e requestException) Exception() string { + return e.exception +} +func (e requestException) Message() string { + return e.message +} + +type awsException struct { + requestException +} + +type sdkException struct { + requestException +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/csm/reporter.go b/vendor/github.com/aws/aws-sdk-go/aws/csm/reporter.go new file mode 100644 index 0000000000000..835bcd49cbaf1 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/csm/reporter.go @@ -0,0 +1,264 @@ +package csm + +import ( + "encoding/json" + "net" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/request" +) + +// Reporter will gather metrics of API requests made and +// send those metrics to the CSM endpoint. +type Reporter struct { + clientID string + url string + conn net.Conn + metricsCh metricChan + done chan struct{} +} + +var ( + sender *Reporter +) + +func connect(url string) error { + const network = "udp" + if err := sender.connect(network, url); err != nil { + return err + } + + if sender.done == nil { + sender.done = make(chan struct{}) + go sender.start() + } + + return nil +} + +func newReporter(clientID, url string) *Reporter { + return &Reporter{ + clientID: clientID, + url: url, + metricsCh: newMetricChan(MetricsChannelSize), + } +} + +func (rep *Reporter) sendAPICallAttemptMetric(r *request.Request) { + if rep == nil { + return + } + + now := time.Now() + creds, _ := r.Config.Credentials.Get() + + m := metric{ + ClientID: aws.String(rep.clientID), + API: aws.String(r.Operation.Name), + Service: aws.String(r.ClientInfo.ServiceID), + Timestamp: (*metricTime)(&now), + UserAgent: aws.String(r.HTTPRequest.Header.Get("User-Agent")), + Region: r.Config.Region, + Type: aws.String("ApiCallAttempt"), + Version: aws.Int(1), + + XAmzRequestID: aws.String(r.RequestID), + + AttemptLatency: aws.Int(int(now.Sub(r.AttemptTime).Nanoseconds() / int64(time.Millisecond))), + AccessKey: aws.String(creds.AccessKeyID), + } + + if r.HTTPResponse != nil { + m.HTTPStatusCode = aws.Int(r.HTTPResponse.StatusCode) + } + + if r.Error != nil { + if awserr, ok := r.Error.(awserr.Error); ok { + m.SetException(getMetricException(awserr)) + } + } + + m.TruncateFields() + rep.metricsCh.Push(m) +} + +func getMetricException(err awserr.Error) metricException { + msg := err.Error() + code := err.Code() + + switch code { + case request.ErrCodeRequestError, + request.ErrCodeSerialization, + request.CanceledErrorCode: + return sdkException{ + requestException{exception: code, message: msg}, + } + default: + return awsException{ + requestException{exception: code, message: msg}, + } + } +} + +func (rep *Reporter) sendAPICallMetric(r *request.Request) { + if rep == nil { + return + } + + now := time.Now() + m := metric{ + ClientID: aws.String(rep.clientID), + API: aws.String(r.Operation.Name), + Service: aws.String(r.ClientInfo.ServiceID), + Timestamp: (*metricTime)(&now), + UserAgent: aws.String(r.HTTPRequest.Header.Get("User-Agent")), + Type: aws.String("ApiCall"), + AttemptCount: aws.Int(r.RetryCount + 1), + Region: r.Config.Region, + Latency: aws.Int(int(time.Since(r.Time) / time.Millisecond)), + XAmzRequestID: aws.String(r.RequestID), + MaxRetriesExceeded: aws.Int(boolIntValue(r.RetryCount >= r.MaxRetries())), + } + + if r.HTTPResponse != nil { + m.FinalHTTPStatusCode = aws.Int(r.HTTPResponse.StatusCode) + } + + if r.Error != nil { + if awserr, ok := r.Error.(awserr.Error); ok { + m.SetFinalException(getMetricException(awserr)) + } + } + + m.TruncateFields() + + // TODO: Probably want to figure something out for logging dropped + // metrics + rep.metricsCh.Push(m) +} + +func (rep *Reporter) connect(network, url string) error { + if rep.conn != nil { + rep.conn.Close() + } + + conn, err := net.Dial(network, url) + if err != nil { + return awserr.New("UDPError", "Could not connect", err) + } + + rep.conn = conn + + return nil +} + +func (rep *Reporter) close() { + if rep.done != nil { + close(rep.done) + } + + rep.metricsCh.Pause() +} + +func (rep *Reporter) start() { + defer func() { + rep.metricsCh.Pause() + }() + + for { + select { + case <-rep.done: + rep.done = nil + return + case m := <-rep.metricsCh.ch: + // TODO: What to do with this error? Probably should just log + b, err := json.Marshal(m) + if err != nil { + continue + } + + rep.conn.Write(b) + } + } +} + +// Pause will pause the metric channel preventing any new metrics from being +// added. It is safe to call concurrently with other calls to Pause, but if +// called concurently with Continue can lead to unexpected state. +func (rep *Reporter) Pause() { + lock.Lock() + defer lock.Unlock() + + if rep == nil { + return + } + + rep.close() +} + +// Continue will reopen the metric channel and allow for monitoring to be +// resumed. It is safe to call concurrently with other calls to Continue, but +// if called concurently with Pause can lead to unexpected state. +func (rep *Reporter) Continue() { + lock.Lock() + defer lock.Unlock() + if rep == nil { + return + } + + if !rep.metricsCh.IsPaused() { + return + } + + rep.metricsCh.Continue() +} + +// Client side metric handler names +const ( + APICallMetricHandlerName = "awscsm.SendAPICallMetric" + APICallAttemptMetricHandlerName = "awscsm.SendAPICallAttemptMetric" +) + +// InjectHandlers will will enable client side metrics and inject the proper +// handlers to handle how metrics are sent. +// +// InjectHandlers is NOT safe to call concurrently. Calling InjectHandlers +// multiple times may lead to unexpected behavior, (e.g. duplicate metrics). +// +// // Start must be called in order to inject the correct handlers +// r, err := csm.Start("clientID", "127.0.0.1:8094") +// if err != nil { +// panic(fmt.Errorf("expected no error, but received %v", err)) +// } +// +// sess := session.NewSession() +// r.InjectHandlers(&sess.Handlers) +// +// // create a new service client with our client side metric session +// svc := s3.New(sess) +func (rep *Reporter) InjectHandlers(handlers *request.Handlers) { + if rep == nil { + return + } + + handlers.Complete.PushFrontNamed(request.NamedHandler{ + Name: APICallMetricHandlerName, + Fn: rep.sendAPICallMetric, + }) + + handlers.CompleteAttempt.PushFrontNamed(request.NamedHandler{ + Name: APICallAttemptMetricHandlerName, + Fn: rep.sendAPICallAttemptMetric, + }) +} + +// boolIntValue return 1 for true and 0 for false. +func boolIntValue(b bool) int { + if b { + return 1 + } + + return 0 +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/defaults/defaults.go b/vendor/github.com/aws/aws-sdk-go/aws/defaults/defaults.go index 2cb08182fb035..23bb639e01867 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/defaults/defaults.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/defaults/defaults.go @@ -24,6 +24,7 @@ import ( "github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/internal/shareddefaults" ) // A Defaults provides a collection of default values for SDK clients. @@ -73,6 +74,7 @@ func Handlers() request.Handlers { handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler) handlers.Validate.AfterEachFn = request.HandlerListStopOnError handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler) + handlers.Build.PushBackNamed(corehandlers.AddHostExecEnvUserAgentHander) handlers.Build.AfterEachFn = request.HandlerListStopOnError handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler) handlers.Send.PushBackNamed(corehandlers.ValidateReqSigHandler) @@ -91,17 +93,28 @@ func Handlers() request.Handlers { func CredChain(cfg *aws.Config, handlers request.Handlers) *credentials.Credentials { return credentials.NewCredentials(&credentials.ChainProvider{ VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors), - Providers: []credentials.Provider{ - &credentials.EnvProvider{}, - &credentials.SharedCredentialsProvider{Filename: "", Profile: ""}, - RemoteCredProvider(*cfg, handlers), - }, + Providers: CredProviders(cfg, handlers), }) } +// CredProviders returns the slice of providers used in +// the default credential chain. +// +// For applications that need to use some other provider (for example use +// different environment variables for legacy reasons) but still fall back +// on the default chain of providers. This allows that default chaint to be +// automatically updated +func CredProviders(cfg *aws.Config, handlers request.Handlers) []credentials.Provider { + return []credentials.Provider{ + &credentials.EnvProvider{}, + &credentials.SharedCredentialsProvider{Filename: "", Profile: ""}, + RemoteCredProvider(*cfg, handlers), + } +} + const ( - httpProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI" - ecsCredsProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" + httpProviderAuthorizationEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN" + httpProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI" ) // RemoteCredProvider returns a credentials provider for the default remote @@ -111,8 +124,8 @@ func RemoteCredProvider(cfg aws.Config, handlers request.Handlers) credentials.P return localHTTPCredProvider(cfg, handlers, u) } - if uri := os.Getenv(ecsCredsProviderEnvVar); len(uri) > 0 { - u := fmt.Sprintf("http://169.254.170.2%s", uri) + if uri := os.Getenv(shareddefaults.ECSCredsProviderEnvVar); len(uri) > 0 { + u := fmt.Sprintf("%s%s", shareddefaults.ECSContainerCredentialsURI, uri) return httpCredProvider(cfg, handlers, u) } @@ -175,6 +188,7 @@ func httpCredProvider(cfg aws.Config, handlers request.Handlers, u string) crede return endpointcreds.NewProviderClient(cfg, handlers, u, func(p *endpointcreds.Provider) { p.ExpiryWindow = 5 * time.Minute + p.AuthorizationToken = os.Getenv(httpProviderAuthorizationEnvVar) }, ) } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go b/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go index 984407a580f47..12897eef6265c 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go @@ -4,28 +4,63 @@ import ( "encoding/json" "fmt" "net/http" - "path" + "strconv" "strings" "time" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/internal/sdkuri" ) +// getToken uses the duration to return a token for EC2 metadata service, +// or an error if the request failed. +func (c *EC2Metadata) getToken(duration time.Duration) (tokenOutput, error) { + op := &request.Operation{ + Name: "GetToken", + HTTPMethod: "PUT", + HTTPPath: "/api/token", + } + + var output tokenOutput + req := c.NewRequest(op, nil, &output) + + // remove the fetch token handler from the request handlers to avoid infinite recursion + req.Handlers.Sign.RemoveByName(fetchTokenHandlerName) + + // Swap the unmarshalMetadataHandler with unmarshalTokenHandler on this request. + req.Handlers.Unmarshal.Swap(unmarshalMetadataHandlerName, unmarshalTokenHandler) + + ttl := strconv.FormatInt(int64(duration/time.Second), 10) + req.HTTPRequest.Header.Set(ttlHeader, ttl) + + err := req.Send() + + // Errors with bad request status should be returned. + if err != nil { + err = awserr.NewRequestFailure( + awserr.New(req.HTTPResponse.Status, http.StatusText(req.HTTPResponse.StatusCode), err), + req.HTTPResponse.StatusCode, req.RequestID) + } + + return output, err +} + // GetMetadata uses the path provided to request information from the EC2 -// instance metdata service. The content will be returned as a string, or +// instance metadata service. The content will be returned as a string, or // error if the request failed. func (c *EC2Metadata) GetMetadata(p string) (string, error) { op := &request.Operation{ Name: "GetMetadata", HTTPMethod: "GET", - HTTPPath: path.Join("/", "meta-data", p), + HTTPPath: sdkuri.PathJoin("/meta-data", p), } - output := &metadataOutput{} + req := c.NewRequest(op, nil, output) - return output.Content, req.Send() + err := req.Send() + return output.Content, err } // GetUserData returns the userdata that was configured for the service. If @@ -35,18 +70,14 @@ func (c *EC2Metadata) GetUserData() (string, error) { op := &request.Operation{ Name: "GetUserData", HTTPMethod: "GET", - HTTPPath: path.Join("/", "user-data"), + HTTPPath: "/user-data", } output := &metadataOutput{} req := c.NewRequest(op, nil, output) - req.Handlers.UnmarshalError.PushBack(func(r *request.Request) { - if r.HTTPResponse.StatusCode == http.StatusNotFound { - r.Error = awserr.New("NotFoundError", "user-data not found", r.Error) - } - }) - return output.Content, req.Send() + err := req.Send() + return output.Content, err } // GetDynamicData uses the path provided to request information from the EC2 @@ -56,13 +87,14 @@ func (c *EC2Metadata) GetDynamicData(p string) (string, error) { op := &request.Operation{ Name: "GetDynamicData", HTTPMethod: "GET", - HTTPPath: path.Join("/", "dynamic", p), + HTTPPath: sdkuri.PathJoin("/dynamic", p), } output := &metadataOutput{} req := c.NewRequest(op, nil, output) - return output.Content, req.Send() + err := req.Send() + return output.Content, err } // GetInstanceIdentityDocument retrieves an identity document describing an @@ -79,7 +111,7 @@ func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument doc := EC2InstanceIdentityDocument{} if err := json.NewDecoder(strings.NewReader(resp)).Decode(&doc); err != nil { return EC2InstanceIdentityDocument{}, - awserr.New("SerializationError", + awserr.New(request.ErrCodeSerialization, "failed to decode EC2 instance identity document", err) } @@ -98,7 +130,7 @@ func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) { info := EC2IAMInfo{} if err := json.NewDecoder(strings.NewReader(resp)).Decode(&info); err != nil { return EC2IAMInfo{}, - awserr.New("SerializationError", + awserr.New(request.ErrCodeSerialization, "failed to decode EC2 IAM info", err) } @@ -113,13 +145,17 @@ func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) { // Region returns the region the instance is running in. func (c *EC2Metadata) Region() (string, error) { - resp, err := c.GetMetadata("placement/availability-zone") + ec2InstanceIdentityDocument, err := c.GetInstanceIdentityDocument() if err != nil { return "", err } - - // returns region without the suffix. Eg: us-west-2a becomes us-west-2 - return resp[:len(resp)-1], nil + // extract region from the ec2InstanceIdentityDocument + region := ec2InstanceIdentityDocument.Region + if len(region) == 0 { + return "", awserr.New("EC2MetadataError", "invalid region received for ec2metadata instance", nil) + } + // returns region + return region, nil } // Available returns if the application has access to the EC2 Metadata service. @@ -145,18 +181,19 @@ type EC2IAMInfo struct { // An EC2InstanceIdentityDocument provides the shape for unmarshaling // an instance identity document type EC2InstanceIdentityDocument struct { - DevpayProductCodes []string `json:"devpayProductCodes"` - AvailabilityZone string `json:"availabilityZone"` - PrivateIP string `json:"privateIp"` - Version string `json:"version"` - Region string `json:"region"` - InstanceID string `json:"instanceId"` - BillingProducts []string `json:"billingProducts"` - InstanceType string `json:"instanceType"` - AccountID string `json:"accountId"` - PendingTime time.Time `json:"pendingTime"` - ImageID string `json:"imageId"` - KernelID string `json:"kernelId"` - RamdiskID string `json:"ramdiskId"` - Architecture string `json:"architecture"` + DevpayProductCodes []string `json:"devpayProductCodes"` + MarketplaceProductCodes []string `json:"marketplaceProductCodes"` + AvailabilityZone string `json:"availabilityZone"` + PrivateIP string `json:"privateIp"` + Version string `json:"version"` + Region string `json:"region"` + InstanceID string `json:"instanceId"` + BillingProducts []string `json:"billingProducts"` + InstanceType string `json:"instanceType"` + AccountID string `json:"accountId"` + PendingTime time.Time `json:"pendingTime"` + ImageID string `json:"imageId"` + KernelID string `json:"kernelId"` + RamdiskID string `json:"ramdiskId"` + Architecture string `json:"architecture"` } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/service.go b/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/service.go index 5b4379dbd8ed0..b8b2940d74460 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/service.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/service.go @@ -1,5 +1,10 @@ // Package ec2metadata provides the client for making API calls to the // EC2 Metadata service. +// +// This package's client can be disabled completely by setting the environment +// variable "AWS_EC2_METADATA_DISABLED=true". This environment variable set to +// true instructs the SDK to disable the EC2 Metadata client. The client cannot +// be used while the environment variable is set to true, (case insensitive). package ec2metadata import ( @@ -7,17 +12,38 @@ import ( "errors" "io" "net/http" + "os" + "strconv" + "strings" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/client/metadata" + "github.com/aws/aws-sdk-go/aws/corehandlers" "github.com/aws/aws-sdk-go/aws/request" ) -// ServiceName is the name of the service. -const ServiceName = "ec2metadata" +const ( + // ServiceName is the name of the service. + ServiceName = "ec2metadata" + disableServiceEnvVar = "AWS_EC2_METADATA_DISABLED" + + // Headers for Token and TTL + ttlHeader = "x-aws-ec2-metadata-token-ttl-seconds" + tokenHeader = "x-aws-ec2-metadata-token" + + // Named Handler constants + fetchTokenHandlerName = "FetchTokenHandler" + unmarshalMetadataHandlerName = "unmarshalMetadataHandler" + unmarshalTokenHandlerName = "unmarshalTokenHandler" + enableTokenProviderHandlerName = "enableTokenProviderHandler" + + // TTL constants + defaultTTL = 21600 * time.Second + ttlExpirationWindow = 30 * time.Second +) // A EC2Metadata is an EC2 Metadata service Client. type EC2Metadata struct { @@ -54,8 +80,10 @@ func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio // use a shorter timeout than default because the metadata // service is local if it is running, and to fail faster // if not running on an ec2 instance. - Timeout: 5 * time.Second, + Timeout: 1 * time.Second, } + // max number of retries on the client operation + cfg.MaxRetries = aws.Int(2) } svc := &EC2Metadata{ @@ -63,6 +91,7 @@ func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio cfg, metadata.ClientInfo{ ServiceName: ServiceName, + ServiceID: ServiceName, Endpoint: endpoint, APIVersion: "latest", }, @@ -70,16 +99,47 @@ func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio ), } - svc.Handlers.Unmarshal.PushBack(unmarshalHandler) + // token provider instance + tp := newTokenProvider(svc, defaultTTL) + + // NamedHandler for fetching token + svc.Handlers.Sign.PushBackNamed(request.NamedHandler{ + Name: fetchTokenHandlerName, + Fn: tp.fetchTokenHandler, + }) + // NamedHandler for enabling token provider + svc.Handlers.Complete.PushBackNamed(request.NamedHandler{ + Name: enableTokenProviderHandlerName, + Fn: tp.enableTokenProviderHandler, + }) + + svc.Handlers.Unmarshal.PushBackNamed(unmarshalHandler) svc.Handlers.UnmarshalError.PushBack(unmarshalError) svc.Handlers.Validate.Clear() svc.Handlers.Validate.PushBack(validateEndpointHandler) + // Disable the EC2 Metadata service if the environment variable is set. + // This short-circuits the service's functionality to always fail to send + // requests. + if strings.ToLower(os.Getenv(disableServiceEnvVar)) == "true" { + svc.Handlers.Send.SwapNamed(request.NamedHandler{ + Name: corehandlers.SendHandler.Name, + Fn: func(r *request.Request) { + r.HTTPResponse = &http.Response{ + Header: http.Header{}, + } + r.Error = awserr.New( + request.CanceledErrorCode, + "EC2 IMDS access disabled via "+disableServiceEnvVar+" env var", + nil) + }, + }) + } + // Add additional options to the service config for _, option := range opts { option(svc.Client) } - return svc } @@ -91,30 +151,74 @@ type metadataOutput struct { Content string } -func unmarshalHandler(r *request.Request) { - defer r.HTTPResponse.Body.Close() - b := &bytes.Buffer{} - if _, err := io.Copy(b, r.HTTPResponse.Body); err != nil { - r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata respose", err) - return - } +type tokenOutput struct { + Token string + TTL time.Duration +} - if data, ok := r.Data.(*metadataOutput); ok { - data.Content = b.String() - } +// unmarshal token handler is used to parse the response of a getToken operation +var unmarshalTokenHandler = request.NamedHandler{ + Name: unmarshalTokenHandlerName, + Fn: func(r *request.Request) { + defer r.HTTPResponse.Body.Close() + var b bytes.Buffer + if _, err := io.Copy(&b, r.HTTPResponse.Body); err != nil { + r.Error = awserr.NewRequestFailure(awserr.New(request.ErrCodeSerialization, + "unable to unmarshal EC2 metadata response", err), r.HTTPResponse.StatusCode, r.RequestID) + return + } + + v := r.HTTPResponse.Header.Get(ttlHeader) + data, ok := r.Data.(*tokenOutput) + if !ok { + return + } + + data.Token = b.String() + // TTL is in seconds + i, err := strconv.ParseInt(v, 10, 64) + if err != nil { + r.Error = awserr.NewRequestFailure(awserr.New(request.ParamFormatErrCode, + "unable to parse EC2 token TTL response", err), r.HTTPResponse.StatusCode, r.RequestID) + return + } + t := time.Duration(i) * time.Second + data.TTL = t + }, +} + +var unmarshalHandler = request.NamedHandler{ + Name: unmarshalMetadataHandlerName, + Fn: func(r *request.Request) { + defer r.HTTPResponse.Body.Close() + var b bytes.Buffer + if _, err := io.Copy(&b, r.HTTPResponse.Body); err != nil { + r.Error = awserr.NewRequestFailure(awserr.New(request.ErrCodeSerialization, + "unable to unmarshal EC2 metadata response", err), r.HTTPResponse.StatusCode, r.RequestID) + return + } + + if data, ok := r.Data.(*metadataOutput); ok { + data.Content = b.String() + } + }, } func unmarshalError(r *request.Request) { defer r.HTTPResponse.Body.Close() - b := &bytes.Buffer{} - if _, err := io.Copy(b, r.HTTPResponse.Body); err != nil { - r.Error = awserr.New("SerializationError", "unable to unmarshal EC2 metadata error respose", err) + var b bytes.Buffer + + if _, err := io.Copy(&b, r.HTTPResponse.Body); err != nil { + r.Error = awserr.NewRequestFailure( + awserr.New(request.ErrCodeSerialization, "unable to unmarshal EC2 metadata error response", err), + r.HTTPResponse.StatusCode, r.RequestID) return } // Response body format is not consistent between metadata endpoints. // Grab the error message as a string and include that as the source error - r.Error = awserr.New("EC2MetadataError", "failed to make EC2Metadata request", errors.New(b.String())) + r.Error = awserr.NewRequestFailure(awserr.New("EC2MetadataError", "failed to make EC2Metadata request", errors.New(b.String())), + r.HTTPResponse.StatusCode, r.RequestID) } func validateEndpointHandler(r *request.Request) { diff --git a/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/token_provider.go b/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/token_provider.go new file mode 100644 index 0000000000000..663372a9154ec --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/token_provider.go @@ -0,0 +1,92 @@ +package ec2metadata + +import ( + "net/http" + "sync/atomic" + "time" + + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/request" +) + +// A tokenProvider struct provides access to EC2Metadata client +// and atomic instance of a token, along with configuredTTL for it. +// tokenProvider also provides an atomic flag to disable the +// fetch token operation. +// The disabled member will use 0 as false, and 1 as true. +type tokenProvider struct { + client *EC2Metadata + token atomic.Value + configuredTTL time.Duration + disabled uint32 +} + +// A ec2Token struct helps use of token in EC2 Metadata service ops +type ec2Token struct { + token string + credentials.Expiry +} + +// newTokenProvider provides a pointer to a tokenProvider instance +func newTokenProvider(c *EC2Metadata, duration time.Duration) *tokenProvider { + return &tokenProvider{client: c, configuredTTL: duration} +} + +// fetchTokenHandler fetches token for EC2Metadata service client by default. +func (t *tokenProvider) fetchTokenHandler(r *request.Request) { + + // short-circuits to insecure data flow if tokenProvider is disabled. + if v := atomic.LoadUint32(&t.disabled); v == 1 { + return + } + + if ec2Token, ok := t.token.Load().(ec2Token); ok && !ec2Token.IsExpired() { + r.HTTPRequest.Header.Set(tokenHeader, ec2Token.token) + return + } + + output, err := t.client.getToken(t.configuredTTL) + + if err != nil { + + // change the disabled flag on token provider to true, + // when error is request timeout error. + if requestFailureError, ok := err.(awserr.RequestFailure); ok { + switch requestFailureError.StatusCode() { + case http.StatusForbidden, http.StatusNotFound, http.StatusMethodNotAllowed: + atomic.StoreUint32(&t.disabled, 1) + case http.StatusBadRequest: + r.Error = requestFailureError + } + + // Check if request timed out while waiting for response + if e, ok := requestFailureError.OrigErr().(awserr.Error); ok { + if e.Code() == request.ErrCodeRequestError { + atomic.StoreUint32(&t.disabled, 1) + } + } + } + return + } + + newToken := ec2Token{ + token: output.Token, + } + newToken.SetExpiration(time.Now().Add(output.TTL), ttlExpirationWindow) + t.token.Store(newToken) + + // Inject token header to the request. + if ec2Token, ok := t.token.Load().(ec2Token); ok { + r.HTTPRequest.Header.Set(tokenHeader, ec2Token.token) + } +} + +// enableTokenProviderHandler enables the token provider +func (t *tokenProvider) enableTokenProviderHandler(r *request.Request) { + // If the error code status is 401, we enable the token provider + if e, ok := r.Error.(awserr.RequestFailure); ok && e != nil && + e.StatusCode() == http.StatusUnauthorized { + atomic.StoreUint32(&t.disabled, 0) + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/decode.go b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/decode.go index 74f72de07356e..343a2106f81a4 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/decode.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/decode.go @@ -83,7 +83,10 @@ func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resol p := &ps[i] custAddEC2Metadata(p) custAddS3DualStack(p) + custRegionalS3(p) custRmIotDataService(p) + custFixAppAutoscalingChina(p) + custFixAppAutoscalingUsGov(p) } return ps, nil @@ -94,7 +97,39 @@ func custAddS3DualStack(p *partition) { return } - s, ok := p.Services["s3"] + custAddDualstack(p, "s3") + custAddDualstack(p, "s3-control") +} + +func custRegionalS3(p *partition) { + if p.ID != "aws" { + return + } + + service, ok := p.Services["s3"] + if !ok { + return + } + + // If global endpoint already exists no customization needed. + if _, ok := service.Endpoints["aws-global"]; ok { + return + } + + service.PartitionEndpoint = "aws-global" + service.Endpoints["us-east-1"] = endpoint{} + service.Endpoints["aws-global"] = endpoint{ + Hostname: "s3.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + } + + p.Services["s3"] = service +} + +func custAddDualstack(p *partition, svcName string) { + s, ok := p.Services[svcName] if !ok { return } @@ -102,7 +137,7 @@ func custAddS3DualStack(p *partition) { s.Defaults.HasDualStack = boxedTrue s.Defaults.DualStackHostname = "{service}.dualstack.{region}.{dnsSuffix}" - p.Services["s3"] = s + p.Services[svcName] = s } func custAddEC2Metadata(p *partition) { @@ -122,6 +157,54 @@ func custRmIotDataService(p *partition) { delete(p.Services, "data.iot") } +func custFixAppAutoscalingChina(p *partition) { + if p.ID != "aws-cn" { + return + } + + const serviceName = "application-autoscaling" + s, ok := p.Services[serviceName] + if !ok { + return + } + + const expectHostname = `autoscaling.{region}.amazonaws.com` + if e, a := s.Defaults.Hostname, expectHostname; e != a { + fmt.Printf("custFixAppAutoscalingChina: ignoring customization, expected %s, got %s\n", e, a) + return + } + + s.Defaults.Hostname = expectHostname + ".cn" + p.Services[serviceName] = s +} + +func custFixAppAutoscalingUsGov(p *partition) { + if p.ID != "aws-us-gov" { + return + } + + const serviceName = "application-autoscaling" + s, ok := p.Services[serviceName] + if !ok { + return + } + + if a := s.Defaults.CredentialScope.Service; a != "" { + fmt.Printf("custFixAppAutoscalingUsGov: ignoring customization, expected empty credential scope service, got %s\n", a) + return + } + + if a := s.Defaults.Hostname; a != "" { + fmt.Printf("custFixAppAutoscalingUsGov: ignoring customization, expected empty hostname, got %s\n", a) + return + } + + s.Defaults.CredentialScope.Service = "application-autoscaling" + s.Defaults.Hostname = "autoscaling.{region}.amazonaws.com" + + p.Services[serviceName] = s +} + type decodeModelError struct { awsError } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go index 5470a8c080f79..9088b3e32ad14 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/defaults.go @@ -11,10 +11,13 @@ const ( AwsPartitionID = "aws" // AWS Standard partition. AwsCnPartitionID = "aws-cn" // AWS China partition. AwsUsGovPartitionID = "aws-us-gov" // AWS GovCloud (US) partition. + AwsIsoPartitionID = "aws-iso" // AWS ISO (US) partition. + AwsIsoBPartitionID = "aws-iso-b" // AWS ISOB (US) partition. ) // AWS Standard partition's regions. const ( + ApEast1RegionID = "ap-east-1" // Asia Pacific (Hong Kong). ApNortheast1RegionID = "ap-northeast-1" // Asia Pacific (Tokyo). ApNortheast2RegionID = "ap-northeast-2" // Asia Pacific (Seoul). ApSouth1RegionID = "ap-south-1" // Asia Pacific (Mumbai). @@ -22,9 +25,11 @@ const ( ApSoutheast2RegionID = "ap-southeast-2" // Asia Pacific (Sydney). CaCentral1RegionID = "ca-central-1" // Canada (Central). EuCentral1RegionID = "eu-central-1" // EU (Frankfurt). + EuNorth1RegionID = "eu-north-1" // EU (Stockholm). EuWest1RegionID = "eu-west-1" // EU (Ireland). EuWest2RegionID = "eu-west-2" // EU (London). EuWest3RegionID = "eu-west-3" // EU (Paris). + MeSouth1RegionID = "me-south-1" // Middle East (Bahrain). SaEast1RegionID = "sa-east-1" // South America (Sao Paulo). UsEast1RegionID = "us-east-1" // US East (N. Virginia). UsEast2RegionID = "us-east-2" // US East (Ohio). @@ -40,119 +45,22 @@ const ( // AWS GovCloud (US) partition's regions. const ( + UsGovEast1RegionID = "us-gov-east-1" // AWS GovCloud (US-East). UsGovWest1RegionID = "us-gov-west-1" // AWS GovCloud (US). ) -// Service identifiers +// AWS ISO (US) partition's regions. const ( - AcmServiceID = "acm" // Acm. - ApiPricingServiceID = "api.pricing" // ApiPricing. - ApigatewayServiceID = "apigateway" // Apigateway. - ApplicationAutoscalingServiceID = "application-autoscaling" // ApplicationAutoscaling. - Appstream2ServiceID = "appstream2" // Appstream2. - AthenaServiceID = "athena" // Athena. - AutoscalingServiceID = "autoscaling" // Autoscaling. - BatchServiceID = "batch" // Batch. - BudgetsServiceID = "budgets" // Budgets. - ClouddirectoryServiceID = "clouddirectory" // Clouddirectory. - CloudformationServiceID = "cloudformation" // Cloudformation. - CloudfrontServiceID = "cloudfront" // Cloudfront. - CloudhsmServiceID = "cloudhsm" // Cloudhsm. - Cloudhsmv2ServiceID = "cloudhsmv2" // Cloudhsmv2. - CloudsearchServiceID = "cloudsearch" // Cloudsearch. - CloudtrailServiceID = "cloudtrail" // Cloudtrail. - CodebuildServiceID = "codebuild" // Codebuild. - CodecommitServiceID = "codecommit" // Codecommit. - CodedeployServiceID = "codedeploy" // Codedeploy. - CodepipelineServiceID = "codepipeline" // Codepipeline. - CodestarServiceID = "codestar" // Codestar. - CognitoIdentityServiceID = "cognito-identity" // CognitoIdentity. - CognitoIdpServiceID = "cognito-idp" // CognitoIdp. - CognitoSyncServiceID = "cognito-sync" // CognitoSync. - ConfigServiceID = "config" // Config. - CurServiceID = "cur" // Cur. - DatapipelineServiceID = "datapipeline" // Datapipeline. - DaxServiceID = "dax" // Dax. - DevicefarmServiceID = "devicefarm" // Devicefarm. - DirectconnectServiceID = "directconnect" // Directconnect. - DiscoveryServiceID = "discovery" // Discovery. - DmsServiceID = "dms" // Dms. - DsServiceID = "ds" // Ds. - DynamodbServiceID = "dynamodb" // Dynamodb. - Ec2ServiceID = "ec2" // Ec2. - Ec2metadataServiceID = "ec2metadata" // Ec2metadata. - EcrServiceID = "ecr" // Ecr. - EcsServiceID = "ecs" // Ecs. - ElasticacheServiceID = "elasticache" // Elasticache. - ElasticbeanstalkServiceID = "elasticbeanstalk" // Elasticbeanstalk. - ElasticfilesystemServiceID = "elasticfilesystem" // Elasticfilesystem. - ElasticloadbalancingServiceID = "elasticloadbalancing" // Elasticloadbalancing. - ElasticmapreduceServiceID = "elasticmapreduce" // Elasticmapreduce. - ElastictranscoderServiceID = "elastictranscoder" // Elastictranscoder. - EmailServiceID = "email" // Email. - EntitlementMarketplaceServiceID = "entitlement.marketplace" // EntitlementMarketplace. - EsServiceID = "es" // Es. - EventsServiceID = "events" // Events. - FirehoseServiceID = "firehose" // Firehose. - GameliftServiceID = "gamelift" // Gamelift. - GlacierServiceID = "glacier" // Glacier. - GlueServiceID = "glue" // Glue. - GreengrassServiceID = "greengrass" // Greengrass. - HealthServiceID = "health" // Health. - IamServiceID = "iam" // Iam. - ImportexportServiceID = "importexport" // Importexport. - InspectorServiceID = "inspector" // Inspector. - IotServiceID = "iot" // Iot. - KinesisServiceID = "kinesis" // Kinesis. - KinesisanalyticsServiceID = "kinesisanalytics" // Kinesisanalytics. - KmsServiceID = "kms" // Kms. - LambdaServiceID = "lambda" // Lambda. - LightsailServiceID = "lightsail" // Lightsail. - LogsServiceID = "logs" // Logs. - MachinelearningServiceID = "machinelearning" // Machinelearning. - MarketplacecommerceanalyticsServiceID = "marketplacecommerceanalytics" // Marketplacecommerceanalytics. - MeteringMarketplaceServiceID = "metering.marketplace" // MeteringMarketplace. - MghServiceID = "mgh" // Mgh. - MobileanalyticsServiceID = "mobileanalytics" // Mobileanalytics. - ModelsLexServiceID = "models.lex" // ModelsLex. - MonitoringServiceID = "monitoring" // Monitoring. - MturkRequesterServiceID = "mturk-requester" // MturkRequester. - OpsworksServiceID = "opsworks" // Opsworks. - OpsworksCmServiceID = "opsworks-cm" // OpsworksCm. - OrganizationsServiceID = "organizations" // Organizations. - PinpointServiceID = "pinpoint" // Pinpoint. - PollyServiceID = "polly" // Polly. - RdsServiceID = "rds" // Rds. - RedshiftServiceID = "redshift" // Redshift. - RekognitionServiceID = "rekognition" // Rekognition. - Route53ServiceID = "route53" // Route53. - Route53domainsServiceID = "route53domains" // Route53domains. - RuntimeLexServiceID = "runtime.lex" // RuntimeLex. - S3ServiceID = "s3" // S3. - SdbServiceID = "sdb" // Sdb. - ServicecatalogServiceID = "servicecatalog" // Servicecatalog. - ShieldServiceID = "shield" // Shield. - SmsServiceID = "sms" // Sms. - SnowballServiceID = "snowball" // Snowball. - SnsServiceID = "sns" // Sns. - SqsServiceID = "sqs" // Sqs. - SsmServiceID = "ssm" // Ssm. - StatesServiceID = "states" // States. - StoragegatewayServiceID = "storagegateway" // Storagegateway. - StreamsDynamodbServiceID = "streams.dynamodb" // StreamsDynamodb. - StsServiceID = "sts" // Sts. - SupportServiceID = "support" // Support. - SwfServiceID = "swf" // Swf. - TaggingServiceID = "tagging" // Tagging. - WafServiceID = "waf" // Waf. - WafRegionalServiceID = "waf-regional" // WafRegional. - WorkdocsServiceID = "workdocs" // Workdocs. - WorkspacesServiceID = "workspaces" // Workspaces. - XrayServiceID = "xray" // Xray. + UsIsoEast1RegionID = "us-iso-east-1" // US ISO East. +) + +// AWS ISOB (US) partition's regions. +const ( + UsIsobEast1RegionID = "us-isob-east-1" // US ISOB East (Ohio). ) // DefaultResolver returns an Endpoint resolver that will be able -// to resolve endpoints for: AWS Standard, AWS China, and AWS GovCloud (US). +// to resolve endpoints for: AWS Standard, AWS China, AWS GovCloud (US), AWS ISO (US), and AWS ISOB (US). // // Use DefaultPartitions() to get the list of the default partitions. func DefaultResolver() Resolver { @@ -160,7 +68,7 @@ func DefaultResolver() Resolver { } // DefaultPartitions returns a list of the partitions the SDK is bundled -// with. The available partitions are: AWS Standard, AWS China, and AWS GovCloud (US). +// with. The available partitions are: AWS Standard, AWS China, AWS GovCloud (US), AWS ISO (US), and AWS ISOB (US). // // partitions := endpoints.DefaultPartitions // for _, p := range partitions { @@ -174,6 +82,8 @@ var defaultPartitions = partitions{ awsPartition, awscnPartition, awsusgovPartition, + awsisoPartition, + awsisobPartition, } // AwsPartition returns the Resolver for AWS Standard. @@ -187,7 +97,7 @@ var awsPartition = partition{ DNSSuffix: "amazonaws.com", RegionRegex: regionRegex{ Regexp: func() *regexp.Regexp { - reg, _ := regexp.Compile("^(us|eu|ap|sa|ca)\\-\\w+\\-\\d+$") + reg, _ := regexp.Compile("^(us|eu|ap|sa|ca|me)\\-\\w+\\-\\d+$") return reg }(), }, @@ -197,6 +107,9 @@ var awsPartition = partition{ SignatureVersions: []string{"v4"}, }, Regions: regions{ + "ap-east-1": region{ + Description: "Asia Pacific (Hong Kong)", + }, "ap-northeast-1": region{ Description: "Asia Pacific (Tokyo)", }, @@ -218,6 +131,9 @@ var awsPartition = partition{ "eu-central-1": region{ Description: "EU (Frankfurt)", }, + "eu-north-1": region{ + Description: "EU (Stockholm)", + }, "eu-west-1": region{ Description: "EU (Ireland)", }, @@ -227,6 +143,9 @@ var awsPartition = partition{ "eu-west-3": region{ Description: "EU (Paris)", }, + "me-south-1": region{ + Description: "Middle East (Bahrain)", + }, "sa-east-1": region{ Description: "South America (Sao Paulo)", }, @@ -244,9 +163,16 @@ var awsPartition = partition{ }, }, Services: services{ - "acm": service{ + "a4b": service{ + + Endpoints: endpoints{ + "us-east-1": endpoint{}, + }, + }, + "access-analyzer": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -254,9 +180,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -264,46 +192,65 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "api.pricing": service{ - Defaults: endpoint{ - CredentialScope: credentialScope{ - Service: "pricing", - }, - }, - Endpoints: endpoints{ - "ap-south-1": endpoint{}, - "us-east-1": endpoint{}, - }, - }, - "apigateway": service{ + "acm": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "ca-central-1-fips": endpoint{ + Hostname: "acm-fips.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "acm-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "acm-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "acm-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "acm-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, }, }, - "application-autoscaling": service{ + "acm-pca": service{ Defaults: endpoint{ - Hostname: "autoscaling.{region}.amazonaws.com", - Protocols: []string{"http", "https"}, - CredentialScope: credentialScope{ - Service: "application-autoscaling", - }, + Protocols: []string{"https"}, }, Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -311,31 +258,162 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "appstream2": service{ - Defaults: endpoint{ - Protocols: []string{"https"}, - CredentialScope: credentialScope{ - Service: "appstream", + "fips-ca-central-1": endpoint{ + Hostname: "acm-pca-fips.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "fips-us-east-1": endpoint{ + Hostname: "acm-pca-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "fips-us-east-2": endpoint{ + Hostname: "acm-pca-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "fips-us-west-1": endpoint{ + Hostname: "acm-pca-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "fips-us-west-2": endpoint{ + Hostname: "acm-pca-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, }, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, }, + }, + "api.ecr": service{ + Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, - "us-west-2": endpoint{}, + "ap-east-1": endpoint{ + Hostname: "api.ecr.ap-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-east-1", + }, + }, + "ap-northeast-1": endpoint{ + Hostname: "api.ecr.ap-northeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-1", + }, + }, + "ap-northeast-2": endpoint{ + Hostname: "api.ecr.ap-northeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-2", + }, + }, + "ap-south-1": endpoint{ + Hostname: "api.ecr.ap-south-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-south-1", + }, + }, + "ap-southeast-1": endpoint{ + Hostname: "api.ecr.ap-southeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-1", + }, + }, + "ap-southeast-2": endpoint{ + Hostname: "api.ecr.ap-southeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-2", + }, + }, + "ca-central-1": endpoint{ + Hostname: "api.ecr.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "eu-central-1": endpoint{ + Hostname: "api.ecr.eu-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-central-1", + }, + }, + "eu-north-1": endpoint{ + Hostname: "api.ecr.eu-north-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-north-1", + }, + }, + "eu-west-1": endpoint{ + Hostname: "api.ecr.eu-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-1", + }, + }, + "eu-west-2": endpoint{ + Hostname: "api.ecr.eu-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-2", + }, + }, + "eu-west-3": endpoint{ + Hostname: "api.ecr.eu-west-3.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-3", + }, + }, + "me-south-1": endpoint{ + Hostname: "api.ecr.me-south-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "me-south-1", + }, + }, + "sa-east-1": endpoint{ + Hostname: "api.ecr.sa-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "sa-east-1", + }, + }, + "us-east-1": endpoint{ + Hostname: "api.ecr.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{ + Hostname: "api.ecr.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{ + Hostname: "api.ecr.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-2": endpoint{ + Hostname: "api.ecr.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, }, }, - "athena": service{ + "api.mediatailor": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, @@ -344,15 +422,24 @@ var awsPartition = partition{ "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "us-east-1": endpoint{}, - "us-east-2": endpoint{}, "us-west-2": endpoint{}, }, }, - "autoscaling": service{ + "api.pricing": service{ Defaults: endpoint{ - Protocols: []string{"http", "https"}, + CredentialScope: credentialScope{ + Service: "pricing", + }, + }, + Endpoints: endpoints{ + "ap-south-1": endpoint{}, + "us-east-1": endpoint{}, }, + }, + "api.sagemaker": service{ + Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -360,56 +447,91 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "api-fips.sagemaker.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "api-fips.sagemaker.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "api-fips.sagemaker.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "api-fips.sagemaker.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, }, }, - "batch": service{ + "apigateway": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, + "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "budgets": service{ - PartitionEndpoint: "aws-global", - IsRegionalized: boxedFalse, - - Endpoints: endpoints{ - "aws-global": endpoint{ - Hostname: "budgets.amazonaws.com", - CredentialScope: credentialScope{ - Region: "us-east-1", - }, - }, + "application-autoscaling": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, }, - }, - "clouddirectory": service{ - Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, + "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "cloudformation": service{ + "appmesh": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, @@ -422,76 +544,81 @@ var awsPartition = partition{ "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "cloudfront": service{ - PartitionEndpoint: "aws-global", - IsRegionalized: boxedFalse, - - Endpoints: endpoints{ - "aws-global": endpoint{ - Hostname: "cloudfront.amazonaws.com", - Protocols: []string{"http", "https"}, - CredentialScope: credentialScope{ - Region: "us-east-1", - }, + "appstream2": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + CredentialScope: credentialScope{ + Service: "appstream", }, }, - }, - "cloudhsm": service{ - Endpoints: endpoints{ "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "fips": endpoint{ + Hostname: "appstream2-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + "us-east-1": endpoint{}, + "us-west-2": endpoint{}, }, }, - "cloudhsmv2": service{ + "appsync": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, - "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "cloudsearch": service{ + "athena": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, + "us-east-2": endpoint{}, "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "cloudtrail": service{ - + "autoscaling": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -499,9 +626,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -509,11 +638,14 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "codebuild": service{ - + "autoscaling-plans": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, Endpoints: endpoints{ "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, @@ -526,9 +658,10 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "codecommit": service{ + "backup": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -536,8 +669,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -545,9 +681,10 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "codedeploy": service{ + "batch": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -555,9 +692,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -565,7 +704,50 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "codepipeline": service{ + "budgets": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "budgets.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + }, + }, + "ce": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "ce.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + }, + }, + "chime": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, + Defaults: endpoint{ + SSLCommonName: "service.chime.aws.amazon.com", + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "service.chime.aws.amazon.com", + Protocols: []string{"https"}, + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + }, + }, + "cloud9": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, @@ -575,19 +757,17 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, - "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "codestar": service{ + "clouddirectory": service{ Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, @@ -596,61 +776,69 @@ var awsPartition = partition{ "eu-west-2": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, - "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "cognito-identity": service{ + "cloudformation": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, + "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "cognito-idp": service{ + "cloudfront": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-2": endpoint{}, + "aws-global": endpoint{ + Hostname: "cloudfront.amazonaws.com", + Protocols: []string{"http", "https"}, + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, }, }, - "cognito-sync": service{ + "cloudhsm": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, + "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "config": service{ - + "cloudhsmv2": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "cloudhsm", + }, + }, Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -658,9 +846,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -668,27 +858,14 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "cur": service{ - - Endpoints: endpoints{ - "us-east-1": endpoint{}, - }, - }, - "datapipeline": service{ + "cloudsearch": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "dax": service{ - - Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-south-1": endpoint{}, + "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, @@ -696,15 +873,10 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "devicefarm": service{ - - Endpoints: endpoints{ - "us-west-2": endpoint{}, - }, - }, - "directconnect": service{ + "cloudtrail": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -712,9 +884,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -722,15 +896,10 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "discovery": service{ - - Endpoints: endpoints{ - "us-west-2": endpoint{}, - }, - }, - "dms": service{ + "codebuild": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -738,19 +907,46 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "codebuild-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "codebuild-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "codebuild-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "codebuild-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, }, }, - "ds": service{ + "codecommit": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -758,20 +954,28 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "fips": endpoint{ + Hostname: "codecommit-fips.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, }, }, - "dynamodb": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, - }, + "codedeploy": service{ + Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -779,27 +983,44 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, - "local": endpoint{ - Hostname: "localhost:8000", - Protocols: []string{"http"}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "codedeploy-fips.us-east-1.amazonaws.com", CredentialScope: credentialScope{ Region: "us-east-1", }, }, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{}, "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "codedeploy-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "codedeploy-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "codedeploy-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, }, }, - "ec2": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, - }, + "codepipeline": service{ + Endpoints: endpoints{ "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, @@ -808,6 +1029,7 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, @@ -818,38 +1040,24 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "ec2metadata": service{ - PartitionEndpoint: "aws-global", - IsRegionalized: boxedFalse, - - Endpoints: endpoints{ - "aws-global": endpoint{ - Hostname: "169.254.169.254/latest", - Protocols: []string{"http"}, - }, - }, - }, - "ecr": service{ + "codestar": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "ecs": service{ + "cognito-identity": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, @@ -861,15 +1069,12 @@ var awsPartition = partition{ "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, - "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "elasticache": service{ + "cognito-idp": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, @@ -881,15 +1086,12 @@ var awsPartition = partition{ "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, - "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "elasticbeanstalk": service{ + "cognito-sync": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, @@ -897,112 +1099,46 @@ var awsPartition = partition{ "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "elasticfilesystem": service{ - - Endpoints: endpoints{ - "ap-southeast-2": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, "us-west-2": endpoint{}, }, }, - "elasticloadbalancing": service{ + "comprehend": service{ Defaults: endpoint{ Protocols: []string{"https"}, }, Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, - "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "elasticmapreduce": service{ - Defaults: endpoint{ - SSLCommonName: "{region}.{service}.{dnsSuffix}", - Protocols: []string{"http", "https"}, - }, - Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, - "eu-central-1": endpoint{ - SSLCommonName: "{service}.{region}.{dnsSuffix}", - }, - "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{ - SSLCommonName: "{service}.{region}.{dnsSuffix}", - }, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "elastictranscoder": service{ + "comprehendmedical": service{ Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, "us-east-1": endpoint{}, - "us-west-1": endpoint{}, + "us-east-2": endpoint{}, "us-west-2": endpoint{}, }, }, - "email": service{ - - Endpoints: endpoints{ - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "entitlement.marketplace": service{ - Defaults: endpoint{ - CredentialScope: credentialScope{ - Service: "aws-marketplace", - }, - }, - Endpoints: endpoints{ - "us-east-1": endpoint{}, - }, - }, - "es": service{ + "config": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -1010,9 +1146,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -1020,179 +1158,117 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "events": service{ + "connect": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "firehose": service{ + "cur": service{ Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-southeast-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "us-east-1": endpoint{}, }, }, - "gamelift": service{ + "data.mediastore": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "glacier": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, - }, + "dataexchange": service{ + Endpoints: endpoints{ "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "glue": service{ + "datapipeline": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, "eu-west-1": endpoint{}, "us-east-1": endpoint{}, - "us-east-2": endpoint{}, "us-west-2": endpoint{}, }, }, - "greengrass": service{ - IsRegionalized: boxedTrue, - Defaults: endpoint{ - Protocols: []string{"https"}, - }, + "datasync": service{ + Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, - "us-east-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "health": service{ - - Endpoints: endpoints{ - "us-east-1": endpoint{}, - }, - }, - "iam": service{ - PartitionEndpoint: "aws-global", - IsRegionalized: boxedFalse, - - Endpoints: endpoints{ - "aws-global": endpoint{ - Hostname: "iam.amazonaws.com", + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "fips-us-east-1": endpoint{ + Hostname: "datasync-fips.us-east-1.amazonaws.com", CredentialScope: credentialScope{ Region: "us-east-1", }, }, - }, - }, - "importexport": service{ - PartitionEndpoint: "aws-global", - IsRegionalized: boxedFalse, - - Endpoints: endpoints{ - "aws-global": endpoint{ - Hostname: "importexport.amazonaws.com", - SignatureVersions: []string{"v2", "v4"}, + "fips-us-east-2": endpoint{ + Hostname: "datasync-fips.us-east-2.amazonaws.com", CredentialScope: credentialScope{ - Region: "us-east-1", - Service: "IngestionService", + Region: "us-east-2", }, }, - }, - }, - "inspector": service{ - - Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "iot": service{ - Defaults: endpoint{ - CredentialScope: credentialScope{ - Service: "execute-api", + "fips-us-west-1": endpoint{ + Hostname: "datasync-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, }, - }, - Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-southeast-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-2": endpoint{}, + "fips-us-west-2": endpoint{ + Hostname: "datasync-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, }, }, - "kinesis": service{ + "dax": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, @@ -1204,17 +1280,16 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "kinesisanalytics": service{ + "devicefarm": service{ Endpoints: endpoints{ - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "kms": service{ + "directconnect": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -1222,9 +1297,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -1232,9 +1309,17 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "lambda": service{ + "discovery": service{ + + Endpoints: endpoints{ + "eu-central-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "dms": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -1242,9 +1327,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -1252,31 +1339,101 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "lightsail": service{ - - Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "logs": service{ + "docdb": service{ Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{}, + "ap-northeast-1": endpoint{ + Hostname: "rds.ap-northeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-1", + }, + }, + "ap-northeast-2": endpoint{ + Hostname: "rds.ap-northeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-2", + }, + }, + "ap-south-1": endpoint{ + Hostname: "rds.ap-south-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-south-1", + }, + }, + "ap-southeast-1": endpoint{ + Hostname: "rds.ap-southeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-1", + }, + }, + "ap-southeast-2": endpoint{ + Hostname: "rds.ap-southeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-2", + }, + }, + "ca-central-1": endpoint{ + Hostname: "rds.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "eu-central-1": endpoint{ + Hostname: "rds.eu-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-central-1", + }, + }, + "eu-west-1": endpoint{ + Hostname: "rds.eu-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-1", + }, + }, + "eu-west-2": endpoint{ + Hostname: "rds.eu-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-2", + }, + }, + "eu-west-3": endpoint{ + Hostname: "rds.eu-west-3.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-3", + }, + }, + "us-east-1": endpoint{ + Hostname: "rds.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{ + Hostname: "rds.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-2": endpoint{ + Hostname: "rds.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "ds": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, @@ -1287,69 +1444,74 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "machinelearning": service{ - - Endpoints: endpoints{ - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, - }, - }, - "marketplacecommerceanalytics": service{ - - Endpoints: endpoints{ - "us-east-1": endpoint{}, - }, - }, - "metering.marketplace": service{ + "dynamodb": service{ Defaults: endpoint{ - CredentialScope: credentialScope{ - Service: "aws-marketplace", - }, + Protocols: []string{"http", "https"}, }, Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "mgh": service{ - - Endpoints: endpoints{ + "ca-central-1-fips": endpoint{ + Hostname: "dynamodb-fips.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "local": endpoint{ + Hostname: "localhost:8000", + Protocols: []string{"http"}, + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "dynamodb-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "dynamodb-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "dynamodb-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, "us-west-2": endpoint{}, - }, - }, - "mobileanalytics": service{ - - Endpoints: endpoints{ - "us-east-1": endpoint{}, - }, - }, - "models.lex": service{ - Defaults: endpoint{ - CredentialScope: credentialScope{ - Service: "lex", + "us-west-2-fips": endpoint{ + Hostname: "dynamodb-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, }, }, - Endpoints: endpoints{ - "us-east-1": endpoint{}, - }, }, - "monitoring": service{ + "ec2": service{ Defaults: endpoint{ Protocols: []string{"http", "https"}, }, Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -1357,9 +1519,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -1367,28 +1531,33 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "mturk-requester": service{ - IsRegionalized: boxedFalse, + "ec2metadata": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, Endpoints: endpoints{ - "sandbox": endpoint{ - Hostname: "mturk-requester-sandbox.us-east-1.amazonaws.com", + "aws-global": endpoint{ + Hostname: "169.254.169.254/latest", + Protocols: []string{"http"}, }, - "us-east-1": endpoint{}, }, }, - "opsworks": service{ + "ecs": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -1396,40 +1565,39 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "opsworks-cm": service{ - - Endpoints: endpoints{ - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "organizations": service{ - PartitionEndpoint: "aws-global", - IsRegionalized: boxedFalse, + "elasticache": service{ Endpoints: endpoints{ - "aws-global": endpoint{ - Hostname: "organizations.us-east-1.amazonaws.com", + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "fips": endpoint{ + Hostname: "elasticache-fips.us-west-1.amazonaws.com", CredentialScope: credentialScope{ - Region: "us-east-1", + Region: "us-west-1", }, }, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, }, }, - "pinpoint": service{ - Defaults: endpoint{ - CredentialScope: credentialScope{ - Service: "mobiletargeting", - }, - }, - Endpoints: endpoints{ - "us-east-1": endpoint{}, - }, - }, - "polly": service{ + "elasticbeanstalk": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -1437,9 +1605,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -1447,9 +1617,10 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "rds": service{ + "elasticfilesystem": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -1457,21 +1628,24 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, - "us-east-1": endpoint{ - SSLCommonName: "{service}.{dnsSuffix}", - }, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, }, }, - "redshift": service{ - + "elasticloadbalancing": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -1479,9 +1653,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -1489,125 +1665,126 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "rekognition": service{ - + "elasticmapreduce": service{ + Defaults: endpoint{ + SSLCommonName: "{region}.{service}.{dnsSuffix}", + Protocols: []string{"https"}, + }, Endpoints: endpoints{ - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{ + SSLCommonName: "{service}.{region}.{dnsSuffix}", + }, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{ + SSLCommonName: "{service}.{region}.{dnsSuffix}", + }, "us-east-2": endpoint{}, + "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "route53": service{ - PartitionEndpoint: "aws-global", - IsRegionalized: boxedFalse, + "elastictranscoder": service{ Endpoints: endpoints{ - "aws-global": endpoint{ - Hostname: "route53.amazonaws.com", - CredentialScope: credentialScope{ - Region: "us-east-1", - }, - }, + "ap-northeast-1": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, }, }, - "route53domains": service{ + "email": service{ Endpoints: endpoints{ - "us-east-1": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-west-2": endpoint{}, }, }, - "runtime.lex": service{ + "entitlement.marketplace": service{ Defaults: endpoint{ CredentialScope: credentialScope{ - Service: "lex", + Service: "aws-marketplace", }, }, Endpoints: endpoints{ - "eu-west-1": endpoint{}, "us-east-1": endpoint{}, }, }, - "s3": service{ - PartitionEndpoint: "us-east-1", - IsRegionalized: boxedTrue, - Defaults: endpoint{ - Protocols: []string{"http", "https"}, - SignatureVersions: []string{"s3v4"}, + "es": service{ - HasDualStack: boxedTrue, - DualStackHostname: "{service}.dualstack.{region}.{dnsSuffix}", - }, Endpoints: endpoints{ - "ap-northeast-1": endpoint{ - Hostname: "s3.ap-northeast-1.amazonaws.com", - SignatureVersions: []string{"s3", "s3v4"}, - }, + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{ - Hostname: "s3.ap-southeast-1.amazonaws.com", - SignatureVersions: []string{"s3", "s3v4"}, - }, - "ap-southeast-2": endpoint{ - Hostname: "s3.ap-southeast-2.amazonaws.com", - SignatureVersions: []string{"s3", "s3v4"}, - }, - "ca-central-1": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{ - Hostname: "s3.eu-west-1.amazonaws.com", - SignatureVersions: []string{"s3", "s3v4"}, - }, - "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "s3-external-1": endpoint{ - Hostname: "s3-external-1.amazonaws.com", - SignatureVersions: []string{"s3", "s3v4"}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "fips": endpoint{ + Hostname: "es-fips.us-west-1.amazonaws.com", CredentialScope: credentialScope{ - Region: "us-east-1", + Region: "us-west-1", }, }, - "sa-east-1": endpoint{ - Hostname: "s3.sa-east-1.amazonaws.com", - SignatureVersions: []string{"s3", "s3v4"}, - }, - "us-east-1": endpoint{ - Hostname: "s3.amazonaws.com", - SignatureVersions: []string{"s3", "s3v4"}, - }, - "us-east-2": endpoint{}, - "us-west-1": endpoint{ - Hostname: "s3.us-west-1.amazonaws.com", - SignatureVersions: []string{"s3", "s3v4"}, - }, - "us-west-2": endpoint{ - Hostname: "s3.us-west-2.amazonaws.com", - SignatureVersions: []string{"s3", "s3v4"}, - }, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, }, }, - "sdb": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, - SignatureVersions: []string{"v2"}, - }, + "events": service{ + Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, - "us-east-1": endpoint{ - Hostname: "sdb.amazonaws.com", - }, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, }, }, - "servicecatalog": service{ + "firehose": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -1615,9 +1792,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -1625,77 +1804,70 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "shield": service{ - IsRegionalized: boxedFalse, + "fms": service{ Defaults: endpoint{ - SSLCommonName: "Shield.us-east-1.amazonaws.com", - Protocols: []string{"https"}, - }, - Endpoints: endpoints{ - "us-east-1": endpoint{}, + Protocols: []string{"https"}, }, - }, - "sms": service{ - Endpoints: endpoints{ "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "snowball": service{ + "forecast": service{ Endpoints: endpoints{ "ap-northeast-1": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "eu-central-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-southeast-1": endpoint{}, "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, - "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "sns": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, + "forecastquery": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, }, + }, + "fsx": service{ + Endpoints: endpoints{ "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "sqs": service{ - Defaults: endpoint{ - SSLCommonName: "{region}.queue.{dnsSuffix}", - Protocols: []string{"http", "https"}, - }, + "gamelift": service{ + Endpoints: endpoints{ "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, @@ -1706,19 +1878,19 @@ var awsPartition = partition{ "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, "sa-east-1": endpoint{}, - "us-east-1": endpoint{ - SSLCommonName: "queue.{dnsSuffix}", - }, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, }, }, - "ssm": service{ - + "glacier": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -1726,9 +1898,11 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, @@ -1736,47 +1910,64 @@ var awsPartition = partition{ "us-west-2": endpoint{}, }, }, - "states": service{ + "glue": service{ Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, + "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "storagegateway": service{ - + "greengrass": service{ + IsRegionalized: boxedTrue, + Defaults: endpoint{ + Protocols: []string{"https"}, + }, Endpoints: endpoints{ "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, "us-east-2": endpoint{}, - "us-west-1": endpoint{}, "us-west-2": endpoint{}, }, }, - "streams.dynamodb": service{ + "groundstation": service{ + + Endpoints: endpoints{ + "eu-north-1": endpoint{}, + "me-south-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "guardduty": service{ + IsRegionalized: boxedTrue, Defaults: endpoint{ - Protocols: []string{"http", "https"}, - CredentialScope: credentialScope{ - Service: "dynamodb", - }, + Protocols: []string{"https"}, }, Endpoints: endpoints{ + "ap-east-1": endpoint{}, "ap-northeast-1": endpoint{}, "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, @@ -1784,133 +1975,3796 @@ var awsPartition = partition{ "ap-southeast-2": endpoint{}, "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, "eu-west-3": endpoint{}, - "local": endpoint{ - Hostname: "localhost:8000", - Protocols: []string{"http"}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "guardduty-fips.us-east-1.amazonaws.com", CredentialScope: credentialScope{ Region: "us-east-1", }, }, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{}, "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "guardduty-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "guardduty-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "guardduty-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, }, }, - "sts": service{ + "health": service{ + + Endpoints: endpoints{ + "us-east-1": endpoint{}, + }, + }, + "iam": service{ PartitionEndpoint: "aws-global", - Defaults: endpoint{ - Hostname: "sts.amazonaws.com", - CredentialScope: credentialScope{ - Region: "us-east-1", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "iam.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, }, }, + }, + "importexport": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, + Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{ - Hostname: "sts.ap-northeast-2.amazonaws.com", + "aws-global": endpoint{ + Hostname: "importexport.amazonaws.com", + SignatureVersions: []string{"v2", "v4"}, CredentialScope: credentialScope{ - Region: "ap-northeast-2", + Region: "us-east-1", + Service: "IngestionService", }, }, + }, + }, + "inspector": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{}, "ap-southeast-2": endpoint{}, - "aws-global": endpoint{}, - "ca-central-1": endpoint{}, "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, "eu-west-1": endpoint{}, "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, "us-east-1": endpoint{}, - "us-east-1-fips": endpoint{ - Hostname: "sts-fips.us-east-1.amazonaws.com", + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "iot": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "execute-api", + }, + }, + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "iotanalytics": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "iotevents": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "ioteventsdata": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{ + Hostname: "data.iotevents.ap-northeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-1", + }, + }, + "ap-northeast-2": endpoint{ + Hostname: "data.iotevents.ap-northeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-2", + }, + }, + "ap-southeast-1": endpoint{ + Hostname: "data.iotevents.ap-southeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-1", + }, + }, + "ap-southeast-2": endpoint{ + Hostname: "data.iotevents.ap-southeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-2", + }, + }, + "eu-central-1": endpoint{ + Hostname: "data.iotevents.eu-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-central-1", + }, + }, + "eu-west-1": endpoint{ + Hostname: "data.iotevents.eu-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-1", + }, + }, + "eu-west-2": endpoint{ + Hostname: "data.iotevents.eu-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-2", + }, + }, + "us-east-1": endpoint{ + Hostname: "data.iotevents.us-east-1.amazonaws.com", CredentialScope: credentialScope{ Region: "us-east-1", }, }, - "us-east-2": endpoint{}, - "us-east-2-fips": endpoint{ - Hostname: "sts-fips.us-east-2.amazonaws.com", + "us-east-2": endpoint{ + Hostname: "data.iotevents.us-east-2.amazonaws.com", CredentialScope: credentialScope{ Region: "us-east-2", }, }, - "us-west-1": endpoint{}, - "us-west-1-fips": endpoint{ - Hostname: "sts-fips.us-west-1.amazonaws.com", + "us-west-2": endpoint{ + Hostname: "data.iotevents.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "iotsecuredtunneling": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "iotthingsgraph": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "iotthingsgraph", + }, + }, + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "kafka": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "kinesis": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "kinesisanalytics": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "kinesisvideo": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "kms": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "lakeformation": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "lambda": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "license-manager": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "lightsail": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "logs": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "machinelearning": service{ + + Endpoints: endpoints{ + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + }, + }, + "marketplacecommerceanalytics": service{ + + Endpoints: endpoints{ + "us-east-1": endpoint{}, + }, + }, + "mediaconnect": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "mediaconvert": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "medialive": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "mediapackage": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "mediastore": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "metering.marketplace": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "aws-marketplace", + }, + }, + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "mgh": service{ + + Endpoints: endpoints{ + "eu-central-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "mobileanalytics": service{ + + Endpoints: endpoints{ + "us-east-1": endpoint{}, + }, + }, + "models.lex": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "lex", + }, + }, + Endpoints: endpoints{ + "ap-southeast-2": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "monitoring": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "mq": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "fips-us-east-1": endpoint{ + Hostname: "mq-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "fips-us-east-2": endpoint{ + Hostname: "mq-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "fips-us-west-1": endpoint{ + Hostname: "mq-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "fips-us-west-2": endpoint{ + Hostname: "mq-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "mturk-requester": service{ + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "sandbox": endpoint{ + Hostname: "mturk-requester-sandbox.us-east-1.amazonaws.com", + }, + "us-east-1": endpoint{}, + }, + }, + "neptune": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{ + Hostname: "rds.ap-northeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-1", + }, + }, + "ap-northeast-2": endpoint{ + Hostname: "rds.ap-northeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-northeast-2", + }, + }, + "ap-south-1": endpoint{ + Hostname: "rds.ap-south-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-south-1", + }, + }, + "ap-southeast-1": endpoint{ + Hostname: "rds.ap-southeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-1", + }, + }, + "ap-southeast-2": endpoint{ + Hostname: "rds.ap-southeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-2", + }, + }, + "ca-central-1": endpoint{ + Hostname: "rds.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "eu-central-1": endpoint{ + Hostname: "rds.eu-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-central-1", + }, + }, + "eu-north-1": endpoint{ + Hostname: "rds.eu-north-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-north-1", + }, + }, + "eu-west-1": endpoint{ + Hostname: "rds.eu-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-1", + }, + }, + "eu-west-2": endpoint{ + Hostname: "rds.eu-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-2", + }, + }, + "me-south-1": endpoint{ + Hostname: "rds.me-south-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "me-south-1", + }, + }, + "us-east-1": endpoint{ + Hostname: "rds.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{ + Hostname: "rds.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-2": endpoint{ + Hostname: "rds.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "oidc": service{ + + Endpoints: endpoints{ + "ap-southeast-1": endpoint{ + Hostname: "oidc.ap-southeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-1", + }, + }, + "ap-southeast-2": endpoint{ + Hostname: "oidc.ap-southeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-2", + }, + }, + "ca-central-1": endpoint{ + Hostname: "oidc.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "eu-central-1": endpoint{ + Hostname: "oidc.eu-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-central-1", + }, + }, + "eu-west-1": endpoint{ + Hostname: "oidc.eu-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-1", + }, + }, + "eu-west-2": endpoint{ + Hostname: "oidc.eu-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-2", + }, + }, + "us-east-1": endpoint{ + Hostname: "oidc.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{ + Hostname: "oidc.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-2": endpoint{ + Hostname: "oidc.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "opsworks": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "opsworks-cm": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "organizations": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "organizations.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + }, + }, + "outposts": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "pinpoint": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "mobiletargeting", + }, + }, + Endpoints: endpoints{ + "ap-south-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "fips-us-east-1": endpoint{ + Hostname: "pinpoint-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "fips-us-west-2": endpoint{ + Hostname: "pinpoint-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + "us-east-1": endpoint{ + Hostname: "pinpoint.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-west-2": endpoint{ + Hostname: "pinpoint.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "polly": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "portal.sso": service{ + + Endpoints: endpoints{ + "ap-southeast-1": endpoint{ + Hostname: "portal.sso.ap-southeast-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-1", + }, + }, + "ap-southeast-2": endpoint{ + Hostname: "portal.sso.ap-southeast-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ap-southeast-2", + }, + }, + "ca-central-1": endpoint{ + Hostname: "portal.sso.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "eu-central-1": endpoint{ + Hostname: "portal.sso.eu-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-central-1", + }, + }, + "eu-west-1": endpoint{ + Hostname: "portal.sso.eu-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-1", + }, + }, + "eu-west-2": endpoint{ + Hostname: "portal.sso.eu-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "eu-west-2", + }, + }, + "us-east-1": endpoint{ + Hostname: "portal.sso.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{ + Hostname: "portal.sso.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-2": endpoint{ + Hostname: "portal.sso.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "projects.iot1click": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "qldb": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "ram": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "rds": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{ + SSLCommonName: "{service}.{dnsSuffix}", + }, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "redshift": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "rekognition": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "resource-groups": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "fips-us-east-1": endpoint{ + Hostname: "resource-groups-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "fips-us-east-2": endpoint{ + Hostname: "resource-groups-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "fips-us-west-1": endpoint{ + Hostname: "resource-groups-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "fips-us-west-2": endpoint{ + Hostname: "resource-groups-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "robomaker": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "route53": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "route53.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + }, + }, + "route53domains": service{ + + Endpoints: endpoints{ + "us-east-1": endpoint{}, + }, + }, + "route53resolver": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "runtime.lex": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "lex", + }, + }, + Endpoints: endpoints{ + "ap-southeast-2": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "runtime.sagemaker": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "runtime-fips.sagemaker.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "runtime-fips.sagemaker.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "runtime-fips.sagemaker.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "runtime-fips.sagemaker.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "s3": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedTrue, + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + SignatureVersions: []string{"s3v4"}, + + HasDualStack: boxedTrue, + DualStackHostname: "{service}.dualstack.{region}.{dnsSuffix}", + }, + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{ + Hostname: "s3.ap-northeast-1.amazonaws.com", + SignatureVersions: []string{"s3", "s3v4"}, + }, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{ + Hostname: "s3.ap-southeast-1.amazonaws.com", + SignatureVersions: []string{"s3", "s3v4"}, + }, + "ap-southeast-2": endpoint{ + Hostname: "s3.ap-southeast-2.amazonaws.com", + SignatureVersions: []string{"s3", "s3v4"}, + }, + "aws-global": endpoint{ + Hostname: "s3.amazonaws.com", + SignatureVersions: []string{"s3", "s3v4"}, + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{ + Hostname: "s3.eu-west-1.amazonaws.com", + SignatureVersions: []string{"s3", "s3v4"}, + }, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "s3-external-1": endpoint{ + Hostname: "s3-external-1.amazonaws.com", + SignatureVersions: []string{"s3", "s3v4"}, + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "sa-east-1": endpoint{ + Hostname: "s3.sa-east-1.amazonaws.com", + SignatureVersions: []string{"s3", "s3v4"}, + }, + "us-east-1": endpoint{ + Hostname: "s3.us-east-1.amazonaws.com", + SignatureVersions: []string{"s3", "s3v4"}, + }, + "us-east-2": endpoint{}, + "us-west-1": endpoint{ + Hostname: "s3.us-west-1.amazonaws.com", + SignatureVersions: []string{"s3", "s3v4"}, + }, + "us-west-2": endpoint{ + Hostname: "s3.us-west-2.amazonaws.com", + SignatureVersions: []string{"s3", "s3v4"}, + }, + }, + }, + "s3-control": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + SignatureVersions: []string{"s3v4"}, + + HasDualStack: boxedTrue, + DualStackHostname: "{service}.dualstack.{region}.{dnsSuffix}", + }, + Endpoints: endpoints{ + "ap-northeast-1": endpoint{ + Hostname: "s3-control.ap-northeast-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "ap-northeast-1", + }, + }, + "ap-northeast-2": endpoint{ + Hostname: "s3-control.ap-northeast-2.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "ap-northeast-2", + }, + }, + "ap-south-1": endpoint{ + Hostname: "s3-control.ap-south-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "ap-south-1", + }, + }, + "ap-southeast-1": endpoint{ + Hostname: "s3-control.ap-southeast-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "ap-southeast-1", + }, + }, + "ap-southeast-2": endpoint{ + Hostname: "s3-control.ap-southeast-2.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "ap-southeast-2", + }, + }, + "ca-central-1": endpoint{ + Hostname: "s3-control.ca-central-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "eu-central-1": endpoint{ + Hostname: "s3-control.eu-central-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "eu-central-1", + }, + }, + "eu-north-1": endpoint{ + Hostname: "s3-control.eu-north-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "eu-north-1", + }, + }, + "eu-west-1": endpoint{ + Hostname: "s3-control.eu-west-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "eu-west-1", + }, + }, + "eu-west-2": endpoint{ + Hostname: "s3-control.eu-west-2.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "eu-west-2", + }, + }, + "eu-west-3": endpoint{ + Hostname: "s3-control.eu-west-3.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "eu-west-3", + }, + }, + "sa-east-1": endpoint{ + Hostname: "s3-control.sa-east-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "sa-east-1", + }, + }, + "us-east-1": endpoint{ + Hostname: "s3-control.us-east-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-1-fips": endpoint{ + Hostname: "s3-control-fips.us-east-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{ + Hostname: "s3-control.us-east-2.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-east-2-fips": endpoint{ + Hostname: "s3-control-fips.us-east-2.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{ + Hostname: "s3-control.us-west-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-1-fips": endpoint{ + Hostname: "s3-control-fips.us-west-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-2": endpoint{ + Hostname: "s3-control.us-west-2.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + "us-west-2-fips": endpoint{ + Hostname: "s3-control-fips.us-west-2.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "savingsplans": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "savingsplans.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + }, + }, + "schemas": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "sdb": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + SignatureVersions: []string{"v2"}, + }, + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-west-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{ + Hostname: "sdb.amazonaws.com", + }, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "secretsmanager": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "secretsmanager-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "secretsmanager-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "secretsmanager-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "secretsmanager-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "securityhub": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "serverlessrepo": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "ap-east-1": endpoint{ + Protocols: []string{"https"}, + }, + "ap-northeast-1": endpoint{ + Protocols: []string{"https"}, + }, + "ap-northeast-2": endpoint{ + Protocols: []string{"https"}, + }, + "ap-south-1": endpoint{ + Protocols: []string{"https"}, + }, + "ap-southeast-1": endpoint{ + Protocols: []string{"https"}, + }, + "ap-southeast-2": endpoint{ + Protocols: []string{"https"}, + }, + "ca-central-1": endpoint{ + Protocols: []string{"https"}, + }, + "eu-central-1": endpoint{ + Protocols: []string{"https"}, + }, + "eu-north-1": endpoint{ + Protocols: []string{"https"}, + }, + "eu-west-1": endpoint{ + Protocols: []string{"https"}, + }, + "eu-west-2": endpoint{ + Protocols: []string{"https"}, + }, + "eu-west-3": endpoint{ + Protocols: []string{"https"}, + }, + "me-south-1": endpoint{ + Protocols: []string{"https"}, + }, + "sa-east-1": endpoint{ + Protocols: []string{"https"}, + }, + "us-east-1": endpoint{ + Protocols: []string{"https"}, + }, + "us-east-2": endpoint{ + Protocols: []string{"https"}, + }, + "us-west-1": endpoint{ + Protocols: []string{"https"}, + }, + "us-west-2": endpoint{ + Protocols: []string{"https"}, + }, + }, + }, + "servicecatalog": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "servicecatalog-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "servicecatalog-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "servicecatalog-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "servicecatalog-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "servicediscovery": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "session.qldb": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "shield": service{ + IsRegionalized: boxedFalse, + Defaults: endpoint{ + SSLCommonName: "shield.us-east-1.amazonaws.com", + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "us-east-1": endpoint{}, + }, + }, + "sms": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "snowball": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "sns": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "sqs": service{ + Defaults: endpoint{ + SSLCommonName: "{region}.queue.{dnsSuffix}", + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "fips-us-east-1": endpoint{ + Hostname: "sqs-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "fips-us-east-2": endpoint{ + Hostname: "sqs-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "fips-us-west-1": endpoint{ + Hostname: "sqs-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "fips-us-west-2": endpoint{ + Hostname: "sqs-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{ + SSLCommonName: "queue.{dnsSuffix}", + }, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "ssm": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "states": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "storagegateway": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "streams.dynamodb": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + CredentialScope: credentialScope{ + Service: "dynamodb", + }, + }, + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "ca-central-1-fips": endpoint{ + Hostname: "dynamodb-fips.ca-central-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "ca-central-1", + }, + }, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "local": endpoint{ + Hostname: "localhost:8000", + Protocols: []string{"http"}, + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "dynamodb-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "dynamodb-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "dynamodb-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "dynamodb-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "sts": service{ + PartitionEndpoint: "aws-global", + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "aws-global": endpoint{ + Hostname: "sts.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "sts-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "sts-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{}, + "us-west-1-fips": endpoint{ + Hostname: "sts-fips.us-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-1", + }, + }, + "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "sts-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "support": service{ + PartitionEndpoint: "aws-global", + + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "support.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + }, + }, + "swf": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "tagging": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "transcribe": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "transcribestreaming": service{ + + Endpoints: endpoints{ + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "transfer": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "translate": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "us-east-1": endpoint{}, + "us-east-1-fips": endpoint{ + Hostname: "translate-fips.us-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + "us-east-2": endpoint{}, + "us-east-2-fips": endpoint{ + Hostname: "translate-fips.us-east-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-2", + }, + }, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + "us-west-2-fips": endpoint{ + Hostname: "translate-fips.us-west-2.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-west-2", + }, + }, + }, + }, + "waf": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "waf.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-east-1", + }, + }, + }, + }, + "waf-regional": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "workdocs": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "workmail": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "eu-west-1": endpoint{}, + "us-east-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "workspaces": service{ + + Endpoints: endpoints{ + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + "xray": service{ + + Endpoints: endpoints{ + "ap-east-1": endpoint{}, + "ap-northeast-1": endpoint{}, + "ap-northeast-2": endpoint{}, + "ap-south-1": endpoint{}, + "ap-southeast-1": endpoint{}, + "ap-southeast-2": endpoint{}, + "ca-central-1": endpoint{}, + "eu-central-1": endpoint{}, + "eu-north-1": endpoint{}, + "eu-west-1": endpoint{}, + "eu-west-2": endpoint{}, + "eu-west-3": endpoint{}, + "me-south-1": endpoint{}, + "sa-east-1": endpoint{}, + "us-east-1": endpoint{}, + "us-east-2": endpoint{}, + "us-west-1": endpoint{}, + "us-west-2": endpoint{}, + }, + }, + }, +} + +// AwsCnPartition returns the Resolver for AWS China. +func AwsCnPartition() Partition { + return awscnPartition.Partition() +} + +var awscnPartition = partition{ + ID: "aws-cn", + Name: "AWS China", + DNSSuffix: "amazonaws.com.cn", + RegionRegex: regionRegex{ + Regexp: func() *regexp.Regexp { + reg, _ := regexp.Compile("^cn\\-\\w+\\-\\d+$") + return reg + }(), + }, + Defaults: endpoint{ + Hostname: "{service}.{region}.{dnsSuffix}", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + Regions: regions{ + "cn-north-1": region{ + Description: "China (Beijing)", + }, + "cn-northwest-1": region{ + Description: "China (Ningxia)", + }, + }, + Services: services{ + "api.ecr": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{ + Hostname: "api.ecr.cn-north-1.amazonaws.com.cn", + CredentialScope: credentialScope{ + Region: "cn-north-1", + }, + }, + "cn-northwest-1": endpoint{ + Hostname: "api.ecr.cn-northwest-1.amazonaws.com.cn", + CredentialScope: credentialScope{ + Region: "cn-northwest-1", + }, + }, + }, + }, + "apigateway": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "application-autoscaling": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "appsync": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + }, + }, + "athena": service{ + + Endpoints: endpoints{ + "cn-northwest-1": endpoint{}, + }, + }, + "autoscaling": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "batch": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "cloudformation": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "cloudfront": service{ + PartitionEndpoint: "aws-cn-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-cn-global": endpoint{ + Hostname: "cloudfront.cn-northwest-1.amazonaws.com.cn", + Protocols: []string{"http", "https"}, + CredentialScope: credentialScope{ + Region: "cn-northwest-1", + }, + }, + }, + }, + "cloudtrail": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "codebuild": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "codedeploy": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "cognito-identity": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + }, + }, + "config": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "dax": service{ + + Endpoints: endpoints{ + "cn-northwest-1": endpoint{}, + }, + }, + "directconnect": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "dms": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "ds": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "dynamodb": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "ec2": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "ec2metadata": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "169.254.169.254/latest", + Protocols: []string{"http"}, + }, + }, + }, + "ecs": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "elasticache": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "elasticbeanstalk": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "elasticfilesystem": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "elasticloadbalancing": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "elasticmapreduce": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "es": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "events": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "firehose": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "gamelift": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + }, + }, + "glacier": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "glue": service{ + + Endpoints: endpoints{ + "cn-northwest-1": endpoint{}, + }, + }, + "greengrass": service{ + IsRegionalized: boxedTrue, + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + }, + }, + "health": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "iam": service{ + PartitionEndpoint: "aws-cn-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-cn-global": endpoint{ + Hostname: "iam.cn-north-1.amazonaws.com.cn", + CredentialScope: credentialScope{ + Region: "cn-north-1", + }, + }, + }, + }, + "iot": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "execute-api", + }, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "kinesis": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "kms": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "lambda": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "license-manager": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "logs": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "mediaconvert": service{ + + Endpoints: endpoints{ + "cn-northwest-1": endpoint{ + Hostname: "subscribe.mediaconvert.cn-northwest-1.amazonaws.com.cn", + CredentialScope: credentialScope{ + Region: "cn-northwest-1", + }, + }, + }, + }, + "monitoring": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "neptune": service{ + + Endpoints: endpoints{ + "cn-northwest-1": endpoint{ + Hostname: "rds.cn-northwest-1.amazonaws.com.cn", + CredentialScope: credentialScope{ + Region: "cn-northwest-1", + }, + }, + }, + }, + "polly": service{ + + Endpoints: endpoints{ + "cn-northwest-1": endpoint{}, + }, + }, + "rds": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "redshift": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "s3": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + SignatureVersions: []string{"s3v4"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "s3-control": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + SignatureVersions: []string{"s3v4"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{ + Hostname: "s3-control.cn-north-1.amazonaws.com.cn", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "cn-north-1", + }, + }, + "cn-northwest-1": endpoint{ + Hostname: "s3-control.cn-northwest-1.amazonaws.com.cn", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "cn-northwest-1", + }, + }, + }, + }, + "secretsmanager": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "serverlessrepo": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{ + Protocols: []string{"https"}, + }, + "cn-northwest-1": endpoint{ + Protocols: []string{"https"}, + }, + }, + }, + "sms": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "snowball": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + }, + }, + "sns": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "sqs": service{ + Defaults: endpoint{ + SSLCommonName: "{region}.queue.{dnsSuffix}", + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "ssm": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "states": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "storagegateway": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + }, + }, + "streams.dynamodb": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + CredentialScope: credentialScope{ + Service: "dynamodb", + }, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "sts": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "support": service{ + PartitionEndpoint: "aws-cn-global", + + Endpoints: endpoints{ + "aws-cn-global": endpoint{ + Hostname: "support.cn-north-1.amazonaws.com.cn", + CredentialScope: credentialScope{ + Region: "cn-north-1", + }, + }, + }, + }, + "swf": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "tagging": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + "transcribe": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "cn-north-1": endpoint{ + Hostname: "cn.transcribe.cn-north-1.amazonaws.com.cn", + CredentialScope: credentialScope{ + Region: "cn-north-1", + }, + }, + "cn-northwest-1": endpoint{ + Hostname: "cn.transcribe.cn-northwest-1.amazonaws.com.cn", + CredentialScope: credentialScope{ + Region: "cn-northwest-1", + }, + }, + }, + }, + "workspaces": service{ + + Endpoints: endpoints{ + "cn-northwest-1": endpoint{}, + }, + }, + "xray": service{ + + Endpoints: endpoints{ + "cn-north-1": endpoint{}, + "cn-northwest-1": endpoint{}, + }, + }, + }, +} + +// AwsUsGovPartition returns the Resolver for AWS GovCloud (US). +func AwsUsGovPartition() Partition { + return awsusgovPartition.Partition() +} + +var awsusgovPartition = partition{ + ID: "aws-us-gov", + Name: "AWS GovCloud (US)", + DNSSuffix: "amazonaws.com", + RegionRegex: regionRegex{ + Regexp: func() *regexp.Regexp { + reg, _ := regexp.Compile("^us\\-gov\\-\\w+\\-\\d+$") + return reg + }(), + }, + Defaults: endpoint{ + Hostname: "{service}.{region}.{dnsSuffix}", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + Regions: regions{ + "us-gov-east-1": region{ + Description: "AWS GovCloud (US-East)", + }, + "us-gov-west-1": region{ + Description: "AWS GovCloud (US)", + }, + }, + Services: services{ + "access-analyzer": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "acm": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "acm-pca": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "api.ecr": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{ + Hostname: "api.ecr.us-gov-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-east-1", + }, + }, + "us-gov-west-1": endpoint{ + Hostname: "api.ecr.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + }, + }, + "api.sagemaker": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "apigateway": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "application-autoscaling": service{ + Defaults: endpoint{ + Hostname: "autoscaling.{region}.amazonaws.com", + Protocols: []string{"http", "https"}, + CredentialScope: credentialScope{ + Service: "application-autoscaling", + }, + }, + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "appstream2": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + CredentialScope: credentialScope{ + Service: "appstream", + }, + }, + Endpoints: endpoints{ + "fips": endpoint{ + Hostname: "appstream2-fips.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + "us-gov-west-1": endpoint{}, + }, + }, + "athena": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "autoscaling": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{ + Protocols: []string{"http", "https"}, + }, + }, + }, + "autoscaling-plans": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "batch": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "clouddirectory": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "cloudformation": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "cloudhsm": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "cloudhsmv2": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "cloudhsm", + }, + }, + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "cloudtrail": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "codebuild": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "codecommit": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "codedeploy": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-east-1-fips": endpoint{ + Hostname: "codedeploy-fips.us-gov-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-east-1", + }, + }, + "us-gov-west-1": endpoint{}, + "us-gov-west-1-fips": endpoint{ + Hostname: "codedeploy-fips.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + }, + }, + "comprehend": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "comprehendmedical": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "config": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "datasync": service{ + + Endpoints: endpoints{ + "fips-us-gov-west-1": endpoint{ + Hostname: "datasync-fips.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "directconnect": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "dms": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "ds": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "dynamodb": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-east-1-fips": endpoint{ + Hostname: "dynamodb.us-gov-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-east-1", + }, + }, + "us-gov-west-1": endpoint{}, + "us-gov-west-1-fips": endpoint{ + Hostname: "dynamodb.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + }, + }, + "ec2": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "ec2metadata": service{ + PartitionEndpoint: "aws-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-global": endpoint{ + Hostname: "169.254.169.254/latest", + Protocols: []string{"http"}, + }, + }, + }, + "ecs": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "elasticache": service{ + + Endpoints: endpoints{ + "fips": endpoint{ + Hostname: "elasticache-fips.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "elasticbeanstalk": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "elasticfilesystem": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "elasticloadbalancing": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{ + Protocols: []string{"http", "https"}, + }, + }, + }, + "elasticmapreduce": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{ + Protocols: []string{"https"}, + }, + }, + }, + "es": service{ + + Endpoints: endpoints{ + "fips": endpoint{ + Hostname: "es-fips.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "events": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "firehose": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "glacier": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{ + Protocols: []string{"http", "https"}, + }, + }, + }, + "glue": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "greengrass": service{ + IsRegionalized: boxedTrue, + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "guardduty": service{ + IsRegionalized: boxedTrue, + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "health": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "iam": service{ + PartitionEndpoint: "aws-us-gov-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-us-gov-global": endpoint{ + Hostname: "iam.us-gov.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + }, + }, + "inspector": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "iot": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "execute-api", + }, + }, + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "kinesis": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "kms": service{ + + Endpoints: endpoints{ + "ProdFips": endpoint{ + Hostname: "kms-fips.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "lambda": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "license-manager": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "logs": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "mediaconvert": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "metering.marketplace": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "aws-marketplace", + }, + }, + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "monitoring": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "neptune": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{ + Hostname: "rds.us-gov-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-east-1", + }, + }, + "us-gov-west-1": endpoint{ + Hostname: "rds.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + }, + }, + "organizations": service{ + PartitionEndpoint: "aws-us-gov-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-us-gov-global": endpoint{ + Hostname: "organizations.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + }, + }, + "polly": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "ram": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "rds": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "redshift": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "rekognition": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "resource-groups": service{ + + Endpoints: endpoints{ + "fips-us-gov-east-1": endpoint{ + Hostname: "resource-groups.us-gov-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-east-1", + }, + }, + "fips-us-gov-west-1": endpoint{ + Hostname: "resource-groups.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "route53": service{ + PartitionEndpoint: "aws-us-gov-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-us-gov-global": endpoint{ + Hostname: "route53.us-gov.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + }, + }, + "route53resolver": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "runtime.sagemaker": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "s3": service{ + Defaults: endpoint{ + SignatureVersions: []string{"s3", "s3v4"}, + }, + Endpoints: endpoints{ + "fips-us-gov-west-1": endpoint{ + Hostname: "s3-fips-us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + "us-gov-east-1": endpoint{ + Hostname: "s3.us-gov-east-1.amazonaws.com", + Protocols: []string{"http", "https"}, + }, + "us-gov-west-1": endpoint{ + Hostname: "s3.us-gov-west-1.amazonaws.com", + Protocols: []string{"http", "https"}, + }, + }, + }, + "s3-control": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + SignatureVersions: []string{"s3v4"}, + }, + Endpoints: endpoints{ + "us-gov-east-1": endpoint{ + Hostname: "s3-control.us-gov-east-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-gov-east-1", + }, + }, + "us-gov-east-1-fips": endpoint{ + Hostname: "s3-control-fips.us-gov-east-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-gov-east-1", + }, + }, + "us-gov-west-1": endpoint{ + Hostname: "s3-control.us-gov-west-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + "us-gov-west-1-fips": endpoint{ + Hostname: "s3-control-fips.us-gov-west-1.amazonaws.com", + SignatureVersions: []string{"s3v4"}, + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + }, + }, + "secretsmanager": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-east-1-fips": endpoint{ + Hostname: "secretsmanager-fips.us-gov-east-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-east-1", + }, + }, + "us-gov-west-1": endpoint{}, + "us-gov-west-1-fips": endpoint{ + Hostname: "secretsmanager-fips.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + }, + }, + "serverlessrepo": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "us-gov-east-1": endpoint{ + Protocols: []string{"https"}, + }, + "us-gov-west-1": endpoint{ + Protocols: []string{"https"}, + }, + }, + }, + "servicecatalog": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + "us-gov-west-1-fips": endpoint{ + Hostname: "servicecatalog-fips.us-gov-west-1.amazonaws.com", + CredentialScope: credentialScope{ + Region: "us-gov-west-1", + }, + }, + }, + }, + "sms": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "snowball": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "sns": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{ + Protocols: []string{"http", "https"}, + }, + }, + }, + "sqs": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{ + SSLCommonName: "{region}.queue.{dnsSuffix}", + Protocols: []string{"http", "https"}, + }, + }, + }, + "ssm": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "states": service{ + + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "storagegateway": service{ + + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + }, + }, + "streams.dynamodb": service{ + Defaults: endpoint{ + CredentialScope: credentialScope{ + Service: "dynamodb", + }, + }, + Endpoints: endpoints{ + "us-gov-east-1": endpoint{}, + "us-gov-east-1-fips": endpoint{ + Hostname: "dynamodb.us-gov-east-1.amazonaws.com", CredentialScope: credentialScope{ - Region: "us-west-1", + Region: "us-gov-east-1", }, }, - "us-west-2": endpoint{}, - "us-west-2-fips": endpoint{ - Hostname: "sts-fips.us-west-2.amazonaws.com", + "us-gov-west-1": endpoint{}, + "us-gov-west-1-fips": endpoint{ + Hostname: "dynamodb.us-gov-west-1.amazonaws.com", CredentialScope: credentialScope{ - Region: "us-west-2", + Region: "us-gov-west-1", }, }, }, }, - "support": service{ + "sts": service{ Endpoints: endpoints{ - "us-east-1": endpoint{}, + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, }, }, "swf": service{ Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "eu-west-3": endpoint{}, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, }, }, "tagging": service{ Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, }, }, - "waf": service{ - PartitionEndpoint: "aws-global", - IsRegionalized: boxedFalse, - + "transcribe": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, Endpoints: endpoints{ - "aws-global": endpoint{ - Hostname: "waf.amazonaws.com", + "us-gov-east-1": endpoint{}, + "us-gov-west-1": endpoint{}, + }, + }, + "translate": service{ + Defaults: endpoint{ + Protocols: []string{"https"}, + }, + Endpoints: endpoints{ + "us-gov-west-1": endpoint{}, + "us-gov-west-1-fips": endpoint{ + Hostname: "translate-fips.us-gov-west-1.amazonaws.com", CredentialScope: credentialScope{ - Region: "us-east-1", + Region: "us-gov-west-1", }, }, }, @@ -1918,71 +5772,30 @@ var awsPartition = partition{ "waf-regional": service{ Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "workdocs": service{ - - Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-southeast-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "eu-west-1": endpoint{}, - "us-east-1": endpoint{}, - "us-west-2": endpoint{}, + "us-gov-west-1": endpoint{}, }, }, "workspaces": service{ Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-southeast-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "us-east-1": endpoint{}, - "us-west-2": endpoint{}, - }, - }, - "xray": service{ - - Endpoints: endpoints{ - "ap-northeast-1": endpoint{}, - "ap-northeast-2": endpoint{}, - "ap-south-1": endpoint{}, - "ap-southeast-1": endpoint{}, - "ap-southeast-2": endpoint{}, - "ca-central-1": endpoint{}, - "eu-central-1": endpoint{}, - "eu-west-1": endpoint{}, - "eu-west-2": endpoint{}, - "sa-east-1": endpoint{}, - "us-east-1": endpoint{}, - "us-east-2": endpoint{}, - "us-west-1": endpoint{}, - "us-west-2": endpoint{}, + "us-gov-west-1": endpoint{}, }, }, }, } -// AwsCnPartition returns the Resolver for AWS China. -func AwsCnPartition() Partition { - return awscnPartition.Partition() +// AwsIsoPartition returns the Resolver for AWS ISO (US). +func AwsIsoPartition() Partition { + return awsisoPartition.Partition() } -var awscnPartition = partition{ - ID: "aws-cn", - Name: "AWS China", - DNSSuffix: "amazonaws.com.cn", +var awsisoPartition = partition{ + ID: "aws-iso", + Name: "AWS ISO (US)", + DNSSuffix: "c2s.ic.gov", RegionRegex: regionRegex{ Regexp: func() *regexp.Regexp { - reg, _ := regexp.Compile("^cn\\-\\w+\\-\\d+$") + reg, _ := regexp.Compile("^us\\-iso\\-\\w+\\-\\d+$") return reg }(), }, @@ -1992,99 +5805,110 @@ var awscnPartition = partition{ SignatureVersions: []string{"v4"}, }, Regions: regions{ - "cn-north-1": region{ - Description: "China (Beijing)", - }, - "cn-northwest-1": region{ - Description: "China (Ningxia)", + "us-iso-east-1": region{ + Description: "US ISO East", }, }, Services: services{ + "api.ecr": service{ + + Endpoints: endpoints{ + "us-iso-east-1": endpoint{ + Hostname: "api.ecr.us-iso-east-1.c2s.ic.gov", + CredentialScope: credentialScope{ + Region: "us-iso-east-1", + }, + }, + }, + }, + "api.sagemaker": service{ + + Endpoints: endpoints{ + "us-iso-east-1": endpoint{}, + }, + }, "apigateway": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "application-autoscaling": service{ Defaults: endpoint{ - Hostname: "autoscaling.{region}.amazonaws.com", Protocols: []string{"http", "https"}, - CredentialScope: credentialScope{ - Service: "application-autoscaling", - }, }, Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "autoscaling": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, - }, + Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{ + Protocols: []string{"http", "https"}, + }, }, }, "cloudformation": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "cloudtrail": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "codedeploy": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, - "cognito-identity": service{ + "config": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, - "config": service{ + "datapipeline": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "directconnect": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, - "dynamodb": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, + "dms": service{ + + Endpoints: endpoints{ + "us-iso-east-1": endpoint{}, }, + }, + "ds": service{ + Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, - "ec2": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, + "dynamodb": service{ + + Endpoints: endpoints{ + "us-iso-east-1": endpoint{ + Protocols: []string{"http", "https"}, + }, }, + }, + "ec2": service{ + Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "ec2metadata": service{ @@ -2098,184 +5922,171 @@ var awscnPartition = partition{ }, }, }, - "ecr": service{ - - Endpoints: endpoints{ - "cn-north-1": endpoint{}, - }, - }, "ecs": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "elasticache": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, - }, - }, - "elasticbeanstalk": service{ - - Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "elasticloadbalancing": service{ - Defaults: endpoint{ - Protocols: []string{"https"}, - }, + Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{ + Protocols: []string{"http", "https"}, + }, }, }, "elasticmapreduce": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, - }, - Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, - }, - }, - "es": service{ Endpoints: endpoints{ - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{ + Protocols: []string{"https"}, + }, }, }, "events": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "glacier": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, + + Endpoints: endpoints{ + "us-iso-east-1": endpoint{ + Protocols: []string{"http", "https"}, + }, }, + }, + "health": service{ + Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "iam": service{ - PartitionEndpoint: "aws-cn-global", + PartitionEndpoint: "aws-iso-global", IsRegionalized: boxedFalse, Endpoints: endpoints{ - "aws-cn-global": endpoint{ - Hostname: "iam.cn-north-1.amazonaws.com.cn", + "aws-iso-global": endpoint{ + Hostname: "iam.us-iso-east-1.c2s.ic.gov", CredentialScope: credentialScope{ - Region: "cn-north-1", + Region: "us-iso-east-1", }, }, }, }, - "iot": service{ - Defaults: endpoint{ - CredentialScope: credentialScope{ - Service: "execute-api", - }, - }, + "kinesis": service{ + Endpoints: endpoints{ - "cn-north-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, - "kinesis": service{ + "kms": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "ProdFips": endpoint{ + Hostname: "kms-fips.us-iso-east-1.c2s.ic.gov", + CredentialScope: credentialScope{ + Region: "us-iso-east-1", + }, + }, + "us-iso-east-1": endpoint{}, }, }, "lambda": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "logs": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "monitoring": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, - }, + Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "rds": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "redshift": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, + }, + }, + "route53": service{ + PartitionEndpoint: "aws-iso-global", + IsRegionalized: boxedFalse, + + Endpoints: endpoints{ + "aws-iso-global": endpoint{ + Hostname: "route53.c2s.ic.gov", + CredentialScope: credentialScope{ + Region: "us-iso-east-1", + }, + }, + }, + }, + "runtime.sagemaker": service{ + + Endpoints: endpoints{ + "us-iso-east-1": endpoint{}, }, }, "s3": service{ Defaults: endpoint{ - Protocols: []string{"http", "https"}, SignatureVersions: []string{"s3v4"}, }, Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{ + Protocols: []string{"http", "https"}, + SignatureVersions: []string{"s3v4"}, + }, }, }, "snowball": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "sns": service{ - Defaults: endpoint{ - Protocols: []string{"http", "https"}, - }, + Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{ + Protocols: []string{"http", "https"}, + }, }, }, "sqs": service{ - Defaults: endpoint{ - SSLCommonName: "{region}.queue.{dnsSuffix}", - Protocols: []string{"http", "https"}, - }, - Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, - }, - }, - "ssm": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{ + Protocols: []string{"http", "https"}, + }, }, }, - "storagegateway": service{ + "states": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, "streams.dynamodb": service{ @@ -2286,45 +6097,56 @@ var awscnPartition = partition{ }, }, Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{ + Protocols: []string{"http", "https"}, + }, }, }, "sts": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, + }, + }, + "support": service{ + PartitionEndpoint: "aws-iso-global", + + Endpoints: endpoints{ + "aws-iso-global": endpoint{ + Hostname: "support.us-iso-east-1.c2s.ic.gov", + CredentialScope: credentialScope{ + Region: "us-iso-east-1", + }, + }, }, }, "swf": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, - "cn-northwest-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, - "tagging": service{ + "workspaces": service{ Endpoints: endpoints{ - "cn-north-1": endpoint{}, + "us-iso-east-1": endpoint{}, }, }, }, } -// AwsUsGovPartition returns the Resolver for AWS GovCloud (US). -func AwsUsGovPartition() Partition { - return awsusgovPartition.Partition() +// AwsIsoBPartition returns the Resolver for AWS ISOB (US). +func AwsIsoBPartition() Partition { + return awsisobPartition.Partition() } -var awsusgovPartition = partition{ - ID: "aws-us-gov", - Name: "AWS GovCloud (US)", - DNSSuffix: "amazonaws.com", +var awsisobPartition = partition{ + ID: "aws-iso-b", + Name: "AWS ISOB (US)", + DNSSuffix: "sc2s.sgov.gov", RegionRegex: regionRegex{ Regexp: func() *regexp.Regexp { - reg, _ := regexp.Compile("^us\\-gov\\-\\w+\\-\\d+$") + reg, _ := regexp.Compile("^us\\-isob\\-\\w+\\-\\d+$") return reg }(), }, @@ -2334,89 +6156,71 @@ var awsusgovPartition = partition{ SignatureVersions: []string{"v4"}, }, Regions: regions{ - "us-gov-west-1": region{ - Description: "AWS GovCloud (US)", + "us-isob-east-1": region{ + Description: "US ISOB East (Ohio)", }, }, Services: services{ - "acm": service{ - - Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "application-autoscaling": service{ + Defaults: endpoint{ + Protocols: []string{"http", "https"}, }, - }, - "apigateway": service{ - Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "autoscaling": service{ - - Endpoints: endpoints{ - "us-gov-west-1": endpoint{ - Protocols: []string{"http", "https"}, - }, + Defaults: endpoint{ + Protocols: []string{"http", "https"}, }, - }, - "cloudformation": service{ - Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, - "cloudhsm": service{ + "cloudformation": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "cloudtrail": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, - }, - }, - "codedeploy": service{ - - Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "config": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "directconnect": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "dms": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "dynamodb": service{ - + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, - "us-gov-west-1-fips": endpoint{ - Hostname: "dynamodb.us-gov-west-1.amazonaws.com", - CredentialScope: credentialScope{ - Region: "us-gov-west-1", - }, - }, + "us-isob-east-1": endpoint{}, }, }, "ec2": service{ - + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "ec2metadata": service{ @@ -2433,54 +6237,50 @@ var awsusgovPartition = partition{ "elasticache": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, - }, - }, - "elasticbeanstalk": service{ - - Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "elasticloadbalancing": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{ - Protocols: []string{"http", "https"}, + "us-isob-east-1": endpoint{ + Protocols: []string{"https"}, }, }, }, "elasticmapreduce": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{ - Protocols: []string{"http", "https"}, - }, + "us-isob-east-1": endpoint{}, }, }, "events": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "glacier": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{ - Protocols: []string{"http", "https"}, - }, + "us-isob-east-1": endpoint{}, + }, + }, + "health": service{ + + Endpoints: endpoints{ + "us-isob-east-1": endpoint{}, }, }, "iam": service{ - PartitionEndpoint: "aws-us-gov-global", + PartitionEndpoint: "aws-iso-b-global", IsRegionalized: boxedFalse, Endpoints: endpoints{ - "aws-us-gov-global": endpoint{ - Hostname: "iam.us-gov.amazonaws.com", + "aws-iso-b-global": endpoint{ + Hostname: "iam.us-isob-east-1.sc2s.sgov.gov", CredentialScope: credentialScope{ - Region: "us-gov-west-1", + Region: "us-isob-east-1", }, }, }, @@ -2488,129 +6288,116 @@ var awsusgovPartition = partition{ "kinesis": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "kms": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, - }, - }, - "lambda": service{ - - Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "ProdFips": endpoint{ + Hostname: "kms-fips.us-isob-east-1.sc2s.sgov.gov", + CredentialScope: credentialScope{ + Region: "us-isob-east-1", + }, + }, + "us-isob-east-1": endpoint{}, }, }, "logs": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "monitoring": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "rds": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "redshift": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, - }, - }, - "rekognition": service{ - - Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "s3": service{ Defaults: endpoint{ - SignatureVersions: []string{"s3", "s3v4"}, - }, - Endpoints: endpoints{ - "fips-us-gov-west-1": endpoint{ - Hostname: "s3-fips-us-gov-west-1.amazonaws.com", - CredentialScope: credentialScope{ - Region: "us-gov-west-1", - }, - }, - "us-gov-west-1": endpoint{ - Hostname: "s3.us-gov-west-1.amazonaws.com", - Protocols: []string{"http", "https"}, - }, + Protocols: []string{"http", "https"}, + SignatureVersions: []string{"s3v4"}, }, - }, - "sms": service{ - Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "snowball": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "sns": service{ - + Defaults: endpoint{ + Protocols: []string{"http", "https"}, + }, Endpoints: endpoints{ - "us-gov-west-1": endpoint{ - Protocols: []string{"http", "https"}, - }, + "us-isob-east-1": endpoint{}, }, }, "sqs": service{ - + Defaults: endpoint{ + SSLCommonName: "{region}.queue.{dnsSuffix}", + Protocols: []string{"http", "https"}, + }, Endpoints: endpoints{ - "us-gov-west-1": endpoint{ - SSLCommonName: "{region}.queue.{dnsSuffix}", - Protocols: []string{"http", "https"}, - }, + "us-isob-east-1": endpoint{}, }, }, - "ssm": service{ + "states": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, "streams.dynamodb": service{ Defaults: endpoint{ + Protocols: []string{"http", "https"}, CredentialScope: credentialScope{ Service: "dynamodb", }, }, Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, - "us-gov-west-1-fips": endpoint{ - Hostname: "dynamodb.us-gov-west-1.amazonaws.com", - CredentialScope: credentialScope{ - Region: "us-gov-west-1", - }, - }, + "us-isob-east-1": endpoint{}, }, }, "sts": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, + }, + }, + "support": service{ + PartitionEndpoint: "aws-iso-b-global", + + Endpoints: endpoints{ + "aws-iso-b-global": endpoint{ + Hostname: "support.us-isob-east-1.sc2s.sgov.gov", + CredentialScope: credentialScope{ + Region: "us-isob-east-1", + }, + }, }, }, "swf": service{ Endpoints: endpoints{ - "us-gov-west-1": endpoint{}, + "us-isob-east-1": endpoint{}, }, }, }, diff --git a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/dep_service_ids.go b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/dep_service_ids.go new file mode 100644 index 0000000000000..ca8fc828e1598 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/dep_service_ids.go @@ -0,0 +1,141 @@ +package endpoints + +// Service identifiers +// +// Deprecated: Use client package's EndpointsID value instead of these +// ServiceIDs. These IDs are not maintained, and are out of date. +const ( + A4bServiceID = "a4b" // A4b. + AcmServiceID = "acm" // Acm. + AcmPcaServiceID = "acm-pca" // AcmPca. + ApiMediatailorServiceID = "api.mediatailor" // ApiMediatailor. + ApiPricingServiceID = "api.pricing" // ApiPricing. + ApiSagemakerServiceID = "api.sagemaker" // ApiSagemaker. + ApigatewayServiceID = "apigateway" // Apigateway. + ApplicationAutoscalingServiceID = "application-autoscaling" // ApplicationAutoscaling. + Appstream2ServiceID = "appstream2" // Appstream2. + AppsyncServiceID = "appsync" // Appsync. + AthenaServiceID = "athena" // Athena. + AutoscalingServiceID = "autoscaling" // Autoscaling. + AutoscalingPlansServiceID = "autoscaling-plans" // AutoscalingPlans. + BatchServiceID = "batch" // Batch. + BudgetsServiceID = "budgets" // Budgets. + CeServiceID = "ce" // Ce. + ChimeServiceID = "chime" // Chime. + Cloud9ServiceID = "cloud9" // Cloud9. + ClouddirectoryServiceID = "clouddirectory" // Clouddirectory. + CloudformationServiceID = "cloudformation" // Cloudformation. + CloudfrontServiceID = "cloudfront" // Cloudfront. + CloudhsmServiceID = "cloudhsm" // Cloudhsm. + Cloudhsmv2ServiceID = "cloudhsmv2" // Cloudhsmv2. + CloudsearchServiceID = "cloudsearch" // Cloudsearch. + CloudtrailServiceID = "cloudtrail" // Cloudtrail. + CodebuildServiceID = "codebuild" // Codebuild. + CodecommitServiceID = "codecommit" // Codecommit. + CodedeployServiceID = "codedeploy" // Codedeploy. + CodepipelineServiceID = "codepipeline" // Codepipeline. + CodestarServiceID = "codestar" // Codestar. + CognitoIdentityServiceID = "cognito-identity" // CognitoIdentity. + CognitoIdpServiceID = "cognito-idp" // CognitoIdp. + CognitoSyncServiceID = "cognito-sync" // CognitoSync. + ComprehendServiceID = "comprehend" // Comprehend. + ConfigServiceID = "config" // Config. + CurServiceID = "cur" // Cur. + DatapipelineServiceID = "datapipeline" // Datapipeline. + DaxServiceID = "dax" // Dax. + DevicefarmServiceID = "devicefarm" // Devicefarm. + DirectconnectServiceID = "directconnect" // Directconnect. + DiscoveryServiceID = "discovery" // Discovery. + DmsServiceID = "dms" // Dms. + DsServiceID = "ds" // Ds. + DynamodbServiceID = "dynamodb" // Dynamodb. + Ec2ServiceID = "ec2" // Ec2. + Ec2metadataServiceID = "ec2metadata" // Ec2metadata. + EcrServiceID = "ecr" // Ecr. + EcsServiceID = "ecs" // Ecs. + ElasticacheServiceID = "elasticache" // Elasticache. + ElasticbeanstalkServiceID = "elasticbeanstalk" // Elasticbeanstalk. + ElasticfilesystemServiceID = "elasticfilesystem" // Elasticfilesystem. + ElasticloadbalancingServiceID = "elasticloadbalancing" // Elasticloadbalancing. + ElasticmapreduceServiceID = "elasticmapreduce" // Elasticmapreduce. + ElastictranscoderServiceID = "elastictranscoder" // Elastictranscoder. + EmailServiceID = "email" // Email. + EntitlementMarketplaceServiceID = "entitlement.marketplace" // EntitlementMarketplace. + EsServiceID = "es" // Es. + EventsServiceID = "events" // Events. + FirehoseServiceID = "firehose" // Firehose. + FmsServiceID = "fms" // Fms. + GameliftServiceID = "gamelift" // Gamelift. + GlacierServiceID = "glacier" // Glacier. + GlueServiceID = "glue" // Glue. + GreengrassServiceID = "greengrass" // Greengrass. + GuarddutyServiceID = "guardduty" // Guardduty. + HealthServiceID = "health" // Health. + IamServiceID = "iam" // Iam. + ImportexportServiceID = "importexport" // Importexport. + InspectorServiceID = "inspector" // Inspector. + IotServiceID = "iot" // Iot. + IotanalyticsServiceID = "iotanalytics" // Iotanalytics. + KinesisServiceID = "kinesis" // Kinesis. + KinesisanalyticsServiceID = "kinesisanalytics" // Kinesisanalytics. + KinesisvideoServiceID = "kinesisvideo" // Kinesisvideo. + KmsServiceID = "kms" // Kms. + LambdaServiceID = "lambda" // Lambda. + LightsailServiceID = "lightsail" // Lightsail. + LogsServiceID = "logs" // Logs. + MachinelearningServiceID = "machinelearning" // Machinelearning. + MarketplacecommerceanalyticsServiceID = "marketplacecommerceanalytics" // Marketplacecommerceanalytics. + MediaconvertServiceID = "mediaconvert" // Mediaconvert. + MedialiveServiceID = "medialive" // Medialive. + MediapackageServiceID = "mediapackage" // Mediapackage. + MediastoreServiceID = "mediastore" // Mediastore. + MeteringMarketplaceServiceID = "metering.marketplace" // MeteringMarketplace. + MghServiceID = "mgh" // Mgh. + MobileanalyticsServiceID = "mobileanalytics" // Mobileanalytics. + ModelsLexServiceID = "models.lex" // ModelsLex. + MonitoringServiceID = "monitoring" // Monitoring. + MturkRequesterServiceID = "mturk-requester" // MturkRequester. + NeptuneServiceID = "neptune" // Neptune. + OpsworksServiceID = "opsworks" // Opsworks. + OpsworksCmServiceID = "opsworks-cm" // OpsworksCm. + OrganizationsServiceID = "organizations" // Organizations. + PinpointServiceID = "pinpoint" // Pinpoint. + PollyServiceID = "polly" // Polly. + RdsServiceID = "rds" // Rds. + RedshiftServiceID = "redshift" // Redshift. + RekognitionServiceID = "rekognition" // Rekognition. + ResourceGroupsServiceID = "resource-groups" // ResourceGroups. + Route53ServiceID = "route53" // Route53. + Route53domainsServiceID = "route53domains" // Route53domains. + RuntimeLexServiceID = "runtime.lex" // RuntimeLex. + RuntimeSagemakerServiceID = "runtime.sagemaker" // RuntimeSagemaker. + S3ServiceID = "s3" // S3. + S3ControlServiceID = "s3-control" // S3Control. + SagemakerServiceID = "api.sagemaker" // Sagemaker. + SdbServiceID = "sdb" // Sdb. + SecretsmanagerServiceID = "secretsmanager" // Secretsmanager. + ServerlessrepoServiceID = "serverlessrepo" // Serverlessrepo. + ServicecatalogServiceID = "servicecatalog" // Servicecatalog. + ServicediscoveryServiceID = "servicediscovery" // Servicediscovery. + ShieldServiceID = "shield" // Shield. + SmsServiceID = "sms" // Sms. + SnowballServiceID = "snowball" // Snowball. + SnsServiceID = "sns" // Sns. + SqsServiceID = "sqs" // Sqs. + SsmServiceID = "ssm" // Ssm. + StatesServiceID = "states" // States. + StoragegatewayServiceID = "storagegateway" // Storagegateway. + StreamsDynamodbServiceID = "streams.dynamodb" // StreamsDynamodb. + StsServiceID = "sts" // Sts. + SupportServiceID = "support" // Support. + SwfServiceID = "swf" // Swf. + TaggingServiceID = "tagging" // Tagging. + TransferServiceID = "transfer" // Transfer. + TranslateServiceID = "translate" // Translate. + WafServiceID = "waf" // Waf. + WafRegionalServiceID = "waf-regional" // WafRegional. + WorkdocsServiceID = "workdocs" // Workdocs. + WorkmailServiceID = "workmail" // Workmail. + WorkspacesServiceID = "workspaces" // Workspaces. + XrayServiceID = "xray" // Xray. +) diff --git a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/endpoints.go b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/endpoints.go index 9c3eedb48d5df..ca956e5f12aff 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/endpoints.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/endpoints.go @@ -3,6 +3,7 @@ package endpoints import ( "fmt" "regexp" + "strings" "github.com/aws/aws-sdk-go/aws/awserr" ) @@ -35,7 +36,7 @@ type Options struct { // // If resolving an endpoint on the partition list the provided region will // be used to determine which partition's domain name pattern to the service - // endpoint ID with. If both the service and region are unkonwn and resolving + // endpoint ID with. If both the service and region are unknown and resolving // the endpoint on partition list an UnknownEndpointError error will be returned. // // If resolving and endpoint on a partition specific resolver that partition's @@ -46,6 +47,108 @@ type Options struct { // // This option is ignored if StrictMatching is enabled. ResolveUnknownService bool + + // STS Regional Endpoint flag helps with resolving the STS endpoint + STSRegionalEndpoint STSRegionalEndpoint + + // S3 Regional Endpoint flag helps with resolving the S3 endpoint + S3UsEast1RegionalEndpoint S3UsEast1RegionalEndpoint +} + +// STSRegionalEndpoint is an enum for the states of the STS Regional Endpoint +// options. +type STSRegionalEndpoint int + +func (e STSRegionalEndpoint) String() string { + switch e { + case LegacySTSEndpoint: + return "legacy" + case RegionalSTSEndpoint: + return "regional" + case UnsetSTSEndpoint: + return "" + default: + return "unknown" + } +} + +const ( + + // UnsetSTSEndpoint represents that STS Regional Endpoint flag is not specified. + UnsetSTSEndpoint STSRegionalEndpoint = iota + + // LegacySTSEndpoint represents when STS Regional Endpoint flag is specified + // to use legacy endpoints. + LegacySTSEndpoint + + // RegionalSTSEndpoint represents when STS Regional Endpoint flag is specified + // to use regional endpoints. + RegionalSTSEndpoint +) + +// GetSTSRegionalEndpoint function returns the STSRegionalEndpointFlag based +// on the input string provided in env config or shared config by the user. +// +// `legacy`, `regional` are the only case-insensitive valid strings for +// resolving the STS regional Endpoint flag. +func GetSTSRegionalEndpoint(s string) (STSRegionalEndpoint, error) { + switch { + case strings.EqualFold(s, "legacy"): + return LegacySTSEndpoint, nil + case strings.EqualFold(s, "regional"): + return RegionalSTSEndpoint, nil + default: + return UnsetSTSEndpoint, fmt.Errorf("unable to resolve the value of STSRegionalEndpoint for %v", s) + } +} + +// S3UsEast1RegionalEndpoint is an enum for the states of the S3 us-east-1 +// Regional Endpoint options. +type S3UsEast1RegionalEndpoint int + +func (e S3UsEast1RegionalEndpoint) String() string { + switch e { + case LegacyS3UsEast1Endpoint: + return "legacy" + case RegionalS3UsEast1Endpoint: + return "regional" + case UnsetS3UsEast1Endpoint: + return "" + default: + return "unknown" + } +} + +const ( + + // UnsetS3UsEast1Endpoint represents that S3 Regional Endpoint flag is not + // specified. + UnsetS3UsEast1Endpoint S3UsEast1RegionalEndpoint = iota + + // LegacyS3UsEast1Endpoint represents when S3 Regional Endpoint flag is + // specified to use legacy endpoints. + LegacyS3UsEast1Endpoint + + // RegionalS3UsEast1Endpoint represents when S3 Regional Endpoint flag is + // specified to use regional endpoints. + RegionalS3UsEast1Endpoint +) + +// GetS3UsEast1RegionalEndpoint function returns the S3UsEast1RegionalEndpointFlag based +// on the input string provided in env config or shared config by the user. +// +// `legacy`, `regional` are the only case-insensitive valid strings for +// resolving the S3 regional Endpoint flag. +func GetS3UsEast1RegionalEndpoint(s string) (S3UsEast1RegionalEndpoint, error) { + switch { + case strings.EqualFold(s, "legacy"): + return LegacyS3UsEast1Endpoint, nil + case strings.EqualFold(s, "regional"): + return RegionalS3UsEast1Endpoint, nil + default: + return UnsetS3UsEast1Endpoint, + fmt.Errorf("unable to resolve the value of S3UsEast1RegionalEndpoint for %v", s) + } } // Set combines all of the option functions together. @@ -79,6 +182,12 @@ func ResolveUnknownServiceOption(o *Options) { o.ResolveUnknownService = true } +// STSRegionalEndpointOption enables the STS endpoint resolver behavior to resolve +// STS endpoint to their regional endpoint, instead of the global endpoint. +func STSRegionalEndpointOption(o *Options) { + o.STSRegionalEndpoint = RegionalSTSEndpoint +} + // A Resolver provides the interface for functionality to resolve endpoints. // The build in Partition and DefaultResolver return value satisfy this interface. type Resolver interface { @@ -170,10 +279,13 @@ func PartitionForRegion(ps []Partition, regionID string) (Partition, bool) { // A Partition provides the ability to enumerate the partition's regions // and services. type Partition struct { - id string - p *partition + id, dnsSuffix string + p *partition } +// DNSSuffix returns the base domain name of the partition. +func (p Partition) DNSSuffix() string { return p.dnsSuffix } + // ID returns the identifier of the partition. func (p Partition) ID() string { return p.id } @@ -191,7 +303,7 @@ func (p Partition) ID() string { return p.id } // require the provided service and region to be known by the partition. // If the endpoint cannot be strictly resolved an error will be returned. This // mode is useful to ensure the endpoint resolved is valid. Without -// StrictMatching enabled the endpoint returned my look valid but may not work. +// StrictMatching enabled the endpoint returned may look valid but may not work. // StrictMatching requires the SDK to be updated if you want to take advantage // of new regions and services expansions. // @@ -205,11 +317,12 @@ func (p Partition) EndpointFor(service, region string, opts ...func(*Options)) ( // Regions returns a map of Regions indexed by their ID. This is useful for // enumerating over the regions in a partition. func (p Partition) Regions() map[string]Region { - rs := map[string]Region{} - for id := range p.p.Regions { + rs := make(map[string]Region, len(p.p.Regions)) + for id, r := range p.p.Regions { rs[id] = Region{ - id: id, - p: p.p, + id: id, + desc: r.Description, + p: p.p, } } @@ -219,7 +332,7 @@ func (p Partition) Regions() map[string]Region { // Services returns a map of Service indexed by their ID. This is useful for // enumerating over the services in a partition. func (p Partition) Services() map[string]Service { - ss := map[string]Service{} + ss := make(map[string]Service, len(p.p.Services)) for id := range p.p.Services { ss[id] = Service{ id: id, @@ -240,6 +353,10 @@ type Region struct { // ID returns the region's identifier. func (r Region) ID() string { return r.id } +// Description returns the region's description. The region description +// is free text, it can be empty, and it may change between SDK releases. +func (r Region) Description() string { return r.desc } + // ResolveEndpoint resolves an endpoint from the context of the region given // a service. See Partition.EndpointFor for usage and errors that can be returned. func (r Region) ResolveEndpoint(service string, opts ...func(*Options)) (ResolvedEndpoint, error) { @@ -284,10 +401,11 @@ func (s Service) ResolveEndpoint(region string, opts ...func(*Options)) (Resolve func (s Service) Regions() map[string]Region { rs := map[string]Region{} for id := range s.p.Services[s.id].Endpoints { - if _, ok := s.p.Regions[id]; ok { + if r, ok := s.p.Regions[id]; ok { rs[id] = Region{ - id: id, - p: s.p, + id: id, + desc: r.Description, + p: s.p, } } } @@ -301,7 +419,7 @@ func (s Service) Regions() map[string]Region { // A region is the AWS region the service exists in. Whereas a Endpoint is // an URL that can be resolved to a instance of a service. func (s Service) Endpoints() map[string]Endpoint { - es := map[string]Endpoint{} + es := make(map[string]Endpoint, len(s.p.Services[s.id].Endpoints)) for id := range s.p.Services[s.id].Endpoints { es[id] = Endpoint{ id: id, @@ -341,12 +459,19 @@ type ResolvedEndpoint struct { // The endpoint URL URL string + // The endpoint partition + PartitionID string + // The region that should be used for signing requests. SigningRegion string // The service name that should be used for signing requests. SigningName string + // States that the signing name for this endpoint was derived from metadata + // passed in, but was not explicitly modeled. + SigningNameDerived bool + // The signing method that should be used for signing requests. SigningMethod string } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/legacy_regions.go b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/legacy_regions.go new file mode 100644 index 0000000000000..df75e899adbe8 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/legacy_regions.go @@ -0,0 +1,24 @@ +package endpoints + +var legacyGlobalRegions = map[string]map[string]struct{}{ + "sts": { + "ap-northeast-1": {}, + "ap-south-1": {}, + "ap-southeast-1": {}, + "ap-southeast-2": {}, + "ca-central-1": {}, + "eu-central-1": {}, + "eu-north-1": {}, + "eu-west-1": {}, + "eu-west-2": {}, + "eu-west-3": {}, + "sa-east-1": {}, + "us-east-1": {}, + "us-east-2": {}, + "us-west-1": {}, + "us-west-2": {}, + }, + "s3": { + "us-east-1": {}, + }, +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model.go b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model.go index 13d968a249e38..eb2ac83c99275 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model.go @@ -54,8 +54,9 @@ type partition struct { func (p partition) Partition() Partition { return Partition{ - id: p.ID, - p: &p, + dnsSuffix: p.DNSSuffix, + id: p.ID, + p: &p, } } @@ -74,24 +75,56 @@ func (p partition) canResolveEndpoint(service, region string, strictMatch bool) return p.RegionRegex.MatchString(region) } +func allowLegacyEmptyRegion(service string) bool { + legacy := map[string]struct{}{ + "budgets": {}, + "ce": {}, + "chime": {}, + "cloudfront": {}, + "ec2metadata": {}, + "iam": {}, + "importexport": {}, + "organizations": {}, + "route53": {}, + "sts": {}, + "support": {}, + "waf": {}, + } + + _, allowed := legacy[service] + return allowed +} + func (p partition) EndpointFor(service, region string, opts ...func(*Options)) (resolved ResolvedEndpoint, err error) { var opt Options opt.Set(opts...) s, hasService := p.Services[service] - if !(hasService || opt.ResolveUnknownService) { + if len(service) == 0 || !(hasService || opt.ResolveUnknownService) { // Only return error if the resolver will not fallback to creating // endpoint based on service endpoint ID passed in. return resolved, NewUnknownServiceError(p.ID, service, serviceList(p.Services)) } + if len(region) == 0 && allowLegacyEmptyRegion(service) && len(s.PartitionEndpoint) != 0 { + region = s.PartitionEndpoint + } + + if (service == "sts" && opt.STSRegionalEndpoint != RegionalSTSEndpoint) || + (service == "s3" && opt.S3UsEast1RegionalEndpoint != RegionalS3UsEast1Endpoint) { + if _, ok := legacyGlobalRegions[service][region]; ok { + region = "aws-global" + } + } + e, hasEndpoint := s.endpointForRegion(region) - if !hasEndpoint && opt.StrictMatching { + if len(region) == 0 || (!hasEndpoint && opt.StrictMatching) { return resolved, NewUnknownEndpointError(p.ID, service, region, endpointList(s.Endpoints)) } defs := []endpoint{p.Defaults, s.Defaults} - return e.resolve(service, region, p.DNSSuffix, defs, opt), nil + + return e.resolve(service, p.ID, region, p.DNSSuffix, defs, opt), nil } func serviceList(ss services) []string { @@ -200,7 +233,7 @@ func getByPriority(s []string, p []string, def string) string { return s[0] } -func (e endpoint) resolve(service, region, dnsSuffix string, defs []endpoint, opts Options) ResolvedEndpoint { +func (e endpoint) resolve(service, partitionID, region, dnsSuffix string, defs []endpoint, opts Options) ResolvedEndpoint { var merged endpoint for _, def := range defs { merged.mergeIn(def) @@ -208,11 +241,23 @@ func (e endpoint) resolve(service, region, dnsSuffix string, defs []endpoint, op merged.mergeIn(e) e = merged - hostname := e.Hostname + signingRegion := e.CredentialScope.Region + if len(signingRegion) == 0 { + signingRegion = region + } + + signingName := e.CredentialScope.Service + var signingNameDerived bool + if len(signingName) == 0 { + signingName = service + signingNameDerived = true + } + hostname := e.Hostname // Offset the hostname for dualstack if enabled if opts.UseDualStack && e.HasDualStack == boxedTrue { hostname = e.DualStackHostname + region = signingRegion } u := strings.Replace(hostname, "{service}", service, 1) @@ -222,20 +267,13 @@ func (e endpoint) resolve(service, region, dnsSuffix string, defs []endpoint, op scheme := getEndpointScheme(e.Protocols, opts.DisableSSL) u = fmt.Sprintf("%s://%s", scheme, u) - signingRegion := e.CredentialScope.Region - if len(signingRegion) == 0 { - signingRegion = region - } - signingName := e.CredentialScope.Service - if len(signingName) == 0 { - signingName = service - } - return ResolvedEndpoint{ - URL: u, - SigningRegion: signingRegion, - SigningName: signingName, - SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner), + URL: u, + PartitionID: partitionID, + SigningRegion: signingRegion, + SigningName: signingName, + SigningNameDerived: signingNameDerived, + SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner), } } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model_codegen.go b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model_codegen.go index 05e92df22af22..0fdfcc56e05d1 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model_codegen.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/endpoints/v3model_codegen.go @@ -16,6 +16,10 @@ import ( type CodeGenOptions struct { // Options for how the model will be decoded. DecodeModelOptions DecodeModelOptions + + // Disables code generation of the service endpoint prefix IDs defined in + // the model. + DisableGenerateServiceIDs bool } // Set combines all of the option functions together @@ -39,8 +43,16 @@ func CodeGenModel(modelFile io.Reader, outFile io.Writer, optFns ...func(*CodeGe return err } + v := struct { + Resolver + CodeGenOptions + }{ + Resolver: resolver, + CodeGenOptions: opts, + } + tmpl := template.Must(template.New("tmpl").Funcs(funcMap).Parse(v3Tmpl)) - if err := tmpl.ExecuteTemplate(outFile, "defaults", resolver); err != nil { + if err := tmpl.ExecuteTemplate(outFile, "defaults", v); err != nil { return fmt.Errorf("failed to execute template, %v", err) } @@ -166,15 +178,17 @@ import ( "regexp" ) - {{ template "partition consts" . }} + {{ template "partition consts" $.Resolver }} - {{ range $_, $partition := . }} + {{ range $_, $partition := $.Resolver }} {{ template "partition region consts" $partition }} {{ end }} - {{ template "service consts" . }} + {{ if not $.DisableGenerateServiceIDs -}} + {{ template "service consts" $.Resolver }} + {{- end }} - {{ template "endpoint resolvers" . }} + {{ template "endpoint resolvers" $.Resolver }} {{- end }} {{ define "partition consts" }} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/errors.go b/vendor/github.com/aws/aws-sdk-go/aws/errors.go index 57663616868fc..fa06f7a8f8b80 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/errors.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/errors.go @@ -5,13 +5,9 @@ import "github.com/aws/aws-sdk-go/aws/awserr" var ( // ErrMissingRegion is an error that is returned if region configuration is // not found. - // - // @readonly ErrMissingRegion = awserr.New("MissingRegion", "could not find region configuration", nil) // ErrMissingEndpoint is an error that is returned if an endpoint cannot be // resolved for a service. - // - // @readonly ErrMissingEndpoint = awserr.New("MissingEndpoint", "'Endpoint' configuration is required for this service", nil) ) diff --git a/vendor/github.com/aws/aws-sdk-go/aws/logger.go b/vendor/github.com/aws/aws-sdk-go/aws/logger.go index 3babb5abdb69e..6ed15b2ecc26d 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/logger.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/logger.go @@ -71,6 +71,12 @@ const ( // LogDebugWithRequestErrors states the SDK should log when service requests fail // to build, send, validate, or unmarshal. LogDebugWithRequestErrors + + // LogDebugWithEventStreamBody states the SDK should log EventStream + // request and response bodys. This should be used to log the EventStream + // wire unmarshaled message content of requests and responses made while + // using the SDK Will also enable LogDebug. + LogDebugWithEventStreamBody ) // A Logger is a minimalistic interface for the SDK to log messages to. Should diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error.go b/vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error.go index 271da432ce177..d9b37f4d32ad0 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error.go @@ -1,18 +1,17 @@ -// +build !appengine,!plan9 - package request import ( - "net" - "os" - "syscall" + "strings" ) func isErrConnectionReset(err error) bool { - if opErr, ok := err.(*net.OpError); ok { - if sysErr, ok := opErr.Err.(*os.SyscallError); ok { - return sysErr.Err == syscall.ECONNRESET - } + if strings.Contains(err.Error(), "read: connection reset") { + return false + } + + if strings.Contains(err.Error(), "connection reset") || + strings.Contains(err.Error(), "broken pipe") { + return true } return false diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error_other.go b/vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error_other.go deleted file mode 100644 index daf9eca437347..0000000000000 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/connection_reset_error_other.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build appengine plan9 - -package request - -import ( - "strings" -) - -func isErrConnectionReset(err error) bool { - return strings.Contains(err.Error(), "connection reset") -} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/handlers.go b/vendor/github.com/aws/aws-sdk-go/aws/request/handlers.go index 802ac88ad5cd6..e819ab6c0e875 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/handlers.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/handlers.go @@ -10,49 +10,106 @@ import ( type Handlers struct { Validate HandlerList Build HandlerList + BuildStream HandlerList Sign HandlerList Send HandlerList ValidateResponse HandlerList Unmarshal HandlerList + UnmarshalStream HandlerList UnmarshalMeta HandlerList UnmarshalError HandlerList Retry HandlerList AfterRetry HandlerList + CompleteAttempt HandlerList Complete HandlerList } -// Copy returns of this handler's lists. +// Copy returns a copy of this handler's lists. func (h *Handlers) Copy() Handlers { return Handlers{ Validate: h.Validate.copy(), Build: h.Build.copy(), + BuildStream: h.BuildStream.copy(), Sign: h.Sign.copy(), Send: h.Send.copy(), ValidateResponse: h.ValidateResponse.copy(), Unmarshal: h.Unmarshal.copy(), + UnmarshalStream: h.UnmarshalStream.copy(), UnmarshalError: h.UnmarshalError.copy(), UnmarshalMeta: h.UnmarshalMeta.copy(), Retry: h.Retry.copy(), AfterRetry: h.AfterRetry.copy(), + CompleteAttempt: h.CompleteAttempt.copy(), Complete: h.Complete.copy(), } } -// Clear removes callback functions for all handlers +// Clear removes callback functions for all handlers. func (h *Handlers) Clear() { h.Validate.Clear() h.Build.Clear() + h.BuildStream.Clear() h.Send.Clear() h.Sign.Clear() h.Unmarshal.Clear() + h.UnmarshalStream.Clear() h.UnmarshalMeta.Clear() h.UnmarshalError.Clear() h.ValidateResponse.Clear() h.Retry.Clear() h.AfterRetry.Clear() + h.CompleteAttempt.Clear() h.Complete.Clear() } +// IsEmpty returns if there are no handlers in any of the handlerlists. +func (h *Handlers) IsEmpty() bool { + if h.Validate.Len() != 0 { + return false + } + if h.Build.Len() != 0 { + return false + } + if h.BuildStream.Len() != 0 { + return false + } + if h.Send.Len() != 0 { + return false + } + if h.Sign.Len() != 0 { + return false + } + if h.Unmarshal.Len() != 0 { + return false + } + if h.UnmarshalStream.Len() != 0 { + return false + } + if h.UnmarshalMeta.Len() != 0 { + return false + } + if h.UnmarshalError.Len() != 0 { + return false + } + if h.ValidateResponse.Len() != 0 { + return false + } + if h.Retry.Len() != 0 { + return false + } + if h.AfterRetry.Len() != 0 { + return false + } + if h.CompleteAttempt.Len() != 0 { + return false + } + if h.Complete.Len() != 0 { + return false + } + + return true +} + // A HandlerListRunItem represents an entry in the HandlerList which // is being run. type HandlerListRunItem struct { @@ -172,6 +229,21 @@ func (l *HandlerList) SwapNamed(n NamedHandler) (swapped bool) { return swapped } +// Swap will swap out all handlers matching the name passed in. The matched +// handlers will be swapped in. True is returned if the handlers were swapped. +func (l *HandlerList) Swap(name string, replace NamedHandler) bool { + var swapped bool + + for i := 0; i < len(l.list); i++ { + if l.list[i].Name == name { + l.list[i] = replace + swapped = true + } + } + + return swapped +} + // SetBackNamed will replace the named handler if it exists in the handler list. // If the handler does not exist the handler will be added to the end of the list. func (l *HandlerList) SetBackNamed(n NamedHandler) { @@ -254,3 +326,18 @@ func MakeAddToUserAgentFreeFormHandler(s string) func(*Request) { AddToUserAgent(r, s) } } + +// WithSetRequestHeaders updates the operation request's HTTP header to contain +// the header key value pairs provided. If the header key already exists in the +// request's HTTP header set, the existing value(s) will be replaced. +func WithSetRequestHeaders(h map[string]string) Option { + return withRequestHeader(h).SetRequestHeaders +} + +type withRequestHeader map[string]string + +func (h withRequestHeader) SetRequestHeaders(r *Request) { + for k, v := range h { + r.HTTPRequest.Header[k] = []string{v} + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/offset_reader.go b/vendor/github.com/aws/aws-sdk-go/aws/request/offset_reader.go index 02f07f4a462f7..9370fa50c3827 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/offset_reader.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/offset_reader.go @@ -3,6 +3,8 @@ package request import ( "io" "sync" + + "github.com/aws/aws-sdk-go/internal/sdkio" ) // offsetReader is a thread-safe io.ReadCloser to prevent racing @@ -13,12 +15,15 @@ type offsetReader struct { closed bool } -func newOffsetReader(buf io.ReadSeeker, offset int64) *offsetReader { +func newOffsetReader(buf io.ReadSeeker, offset int64) (*offsetReader, error) { reader := &offsetReader{} - buf.Seek(offset, 0) + _, err := buf.Seek(offset, sdkio.SeekStart) + if err != nil { + return nil, err + } reader.buf = buf - return reader + return reader, nil } // Close will close the instance of the offset reader's access to @@ -52,7 +57,9 @@ func (o *offsetReader) Seek(offset int64, whence int) (int64, error) { // CloseAndCopy will return a new offsetReader with a copy of the old buffer // and close the old buffer. -func (o *offsetReader) CloseAndCopy(offset int64) *offsetReader { - o.Close() +func (o *offsetReader) CloseAndCopy(offset int64) (*offsetReader, error) { + if err := o.Close(); err != nil { + return nil, err + } return newOffsetReader(o.buf, offset) } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/request.go b/vendor/github.com/aws/aws-sdk-go/aws/request/request.go index 5c7db4982c969..5050db4e3d1e6 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/request.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/request.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "io" - "net" "net/http" "net/url" "reflect" @@ -14,6 +13,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/client/metadata" + "github.com/aws/aws-sdk-go/internal/sdkio" ) const ( @@ -36,6 +36,10 @@ const ( // API request that was canceled. Requests given a aws.Context may // return this error when canceled. CanceledErrorCode = "RequestCanceled" + + // ErrCodeRequestError is an error preventing the SDK from continuing to + // process the request. + ErrCodeRequestError = "RequestError" ) // A Request is the service request to be made. @@ -45,11 +49,13 @@ type Request struct { Handlers Handlers Retryer + AttemptTime time.Time Time time.Time Operation *Operation HTTPRequest *http.Request HTTPResponse *http.Response Body io.ReadSeeker + streamingBody io.ReadCloser BodyStart int64 // offset from beginning of Body that the request body starts Params interface{} Error error @@ -63,6 +69,15 @@ type Request struct { LastSignedAt time.Time DisableFollowRedirects bool + // Additional API error codes that should be retried. IsErrorRetryable + // will consider these codes in addition to its built in cases. + RetryErrorCodes []string + + // Additional API error codes that should be retried with throttle backoff + // delay. IsErrorThrottle will consider these codes in addition to its + // built in cases. + ThrottleErrorCodes []string + // A value greater than 0 instructs the request to be signed as Presigned URL // You should not set this field directly. Instead use Request's // Presign or PresignRequest methods. @@ -89,8 +104,12 @@ type Operation struct { BeforePresignFn func(r *Request) error } -// New returns a new Request pointer for the service API -// operation and parameters. +// New returns a new Request pointer for the service API operation and +// parameters. +// +// A Retryer should be provided to direct how the request is retried. If +// Retryer is nil, a default no retry value will be used. You can use +// NoOpRetryer in the Client package to disable retry behavior directly. // // Params is any value of input parameters to be the request payload. // Data is pointer value to an object which the request's response @@ -98,6 +117,10 @@ type Operation struct { func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers, retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request { + if retryer == nil { + retryer = noOpRetryer{} + } + method := operation.HTTPMethod if method == "" { method = "POST" @@ -112,8 +135,6 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers, err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err) } - SanitizeHostForHeader(httpReq) - r := &Request{ Config: cfg, ClientInfo: clientInfo, @@ -224,9 +245,16 @@ func (r *Request) SetContext(ctx aws.Context) { // WillRetry returns if the request's can be retried. func (r *Request) WillRetry() bool { + if !aws.IsReaderSeekable(r.Body) && r.HTTPRequest.Body != NoBody { + return false + } return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries() } +func fmtAttemptCount(retryCount, maxRetries int) string { + return fmt.Sprintf("attempt %v/%v", retryCount, maxRetries) +} + // ParamsFilled returns if the request's parameters have been populated // and the parameters are valid. False is returned if no parameters are // provided or invalid. @@ -255,11 +283,32 @@ func (r *Request) SetStringBody(s string) { // SetReaderBody will set the request's body reader. func (r *Request) SetReaderBody(reader io.ReadSeeker) { r.Body = reader + + if aws.IsReaderSeekable(reader) { + var err error + // Get the Bodies current offset so retries will start from the same + // initial position. + r.BodyStart, err = reader.Seek(0, sdkio.SeekCurrent) + if err != nil { + r.Error = awserr.New(ErrCodeSerialization, + "failed to determine start of request body", err) + return + } + } r.ResetBody() } +// SetStreamingBody set the reader to be used for the request that will stream +// bytes to the server. Request's Body must not be set to any reader. +func (r *Request) SetStreamingBody(reader io.ReadCloser) { + r.streamingBody = reader + r.SetReaderBody(aws.ReadSeekCloser(reader)) +} + // Presign returns the request's signed URL. Error will be returned -// if the signing fails. +// if the signing fails. The expire parameter is only used for presigned Amazon +// S3 API requests. All other AWS services will use a fixed expiration +// time of 15 minutes. // // It is invalid to create a presigned URL with a expire duration 0 or less. An // error is returned if expire duration is 0 or less. @@ -276,7 +325,9 @@ func (r *Request) Presign(expire time.Duration) (string, error) { } // PresignRequest behaves just like presign, with the addition of returning a -// set of headers that were signed. +// set of headers that were signed. The expire parameter is only used for +// presigned Amazon S3 API requests. All other AWS services will use a fixed +// expiration time of 15 minutes. // // It is invalid to create a presigned URL with a expire duration 0 or less. An // error is returned if expire duration is 0 or less. @@ -292,6 +343,11 @@ func (r *Request) PresignRequest(expire time.Duration) (string, http.Header, err return getPresignedURL(r, expire) } +// IsPresigned returns true if the request represents a presigned API url. +func (r *Request) IsPresigned() bool { + return r.ExpireTime != 0 +} + func getPresignedURL(r *Request, expire time.Duration) (string, http.Header, error) { if expire <= 0 { return "", nil, awserr.New( @@ -316,23 +372,22 @@ func getPresignedURL(r *Request, expire time.Duration) (string, http.Header, err return r.HTTPRequest.URL.String(), r.SignedHeaderVals, nil } -func debugLogReqError(r *Request, stage string, retrying bool, err error) { +const ( + notRetrying = "not retrying" +) + +func debugLogReqError(r *Request, stage, retryStr string, err error) { if !r.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) { return } - retryStr := "not retrying" - if retrying { - retryStr = "will retry" - } - r.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v", stage, r.ClientInfo.ServiceName, r.Operation.Name, retryStr, err)) } // Build will build the request's object so it can be signed and sent // to the service. Build will also validate all the request's parameters. -// Anny additional build Handlers set on this request will be run +// Any additional build Handlers set on this request will be run // in the order they were set. // // The request will only be built once. Multiple calls to build will have @@ -344,12 +399,12 @@ func (r *Request) Build() error { if !r.built { r.Handlers.Validate.Run(r) if r.Error != nil { - debugLogReqError(r, "Validate Request", false, r.Error) + debugLogReqError(r, "Validate Request", notRetrying, r.Error) return r.Error } r.Handlers.Build.Run(r) if r.Error != nil { - debugLogReqError(r, "Build Request", false, r.Error) + debugLogReqError(r, "Build Request", notRetrying, r.Error) return r.Error } r.built = true @@ -358,27 +413,37 @@ func (r *Request) Build() error { return r.Error } -// Sign will sign the request returning error if errors are encountered. +// Sign will sign the request, returning error if errors are encountered. // -// Send will build the request prior to signing. All Sign Handlers will +// Sign will build the request prior to signing. All Sign Handlers will // be executed in the order they were set. func (r *Request) Sign() error { r.Build() if r.Error != nil { - debugLogReqError(r, "Build Request", false, r.Error) + debugLogReqError(r, "Build Request", notRetrying, r.Error) return r.Error } + SanitizeHostForHeader(r.HTTPRequest) + r.Handlers.Sign.Run(r) return r.Error } -func (r *Request) getNextRequestBody() (io.ReadCloser, error) { +func (r *Request) getNextRequestBody() (body io.ReadCloser, err error) { + if r.streamingBody != nil { + return r.streamingBody, nil + } + if r.safeBody != nil { r.safeBody.Close() } - r.safeBody = newOffsetReader(r.Body, r.BodyStart) + r.safeBody, err = newOffsetReader(r.Body, r.BodyStart) + if err != nil { + return nil, awserr.New(ErrCodeSerialization, + "failed to get next request body reader", err) + } // Go 1.8 tightened and clarified the rules code needs to use when building // requests with the http package. Go 1.8 removed the automatic detection @@ -393,12 +458,12 @@ func (r *Request) getNextRequestBody() (io.ReadCloser, error) { // of the SDK if they used that field. // // Related golang/go#18257 - l, err := computeBodyLength(r.Body) + l, err := aws.SeekerLen(r.Body) if err != nil { - return nil, awserr.New(ErrCodeSerialization, "failed to compute request body size", err) + return nil, awserr.New(ErrCodeSerialization, + "failed to compute request body size", err) } - var body io.ReadCloser if l == 0 { body = NoBody } else if l > 0 { @@ -411,7 +476,8 @@ func (r *Request) getNextRequestBody() (io.ReadCloser, error) { // Transfer-Encoding: chunked bodies for these methods. // // This would only happen if a aws.ReaderSeekerCloser was used with - // a io.Reader that was not also an io.Seeker. + // a io.Reader that was not also an io.Seeker, or did not implement + // Len() method. switch r.Operation.HTTPMethod { case "GET", "HEAD", "DELETE": body = NoBody @@ -423,49 +489,13 @@ func (r *Request) getNextRequestBody() (io.ReadCloser, error) { return body, nil } -// Attempts to compute the length of the body of the reader using the -// io.Seeker interface. If the value is not seekable because of being -// a ReaderSeekerCloser without an unerlying Seeker -1 will be returned. -// If no error occurs the length of the body will be returned. -func computeBodyLength(r io.ReadSeeker) (int64, error) { - seekable := true - // Determine if the seeker is actually seekable. ReaderSeekerCloser - // hides the fact that a io.Readers might not actually be seekable. - switch v := r.(type) { - case aws.ReaderSeekerCloser: - seekable = v.IsSeeker() - case *aws.ReaderSeekerCloser: - seekable = v.IsSeeker() - } - if !seekable { - return -1, nil - } - - curOffset, err := r.Seek(0, 1) - if err != nil { - return 0, err - } - - endOffset, err := r.Seek(0, 2) - if err != nil { - return 0, err - } - - _, err = r.Seek(curOffset, 0) - if err != nil { - return 0, err - } - - return endOffset - curOffset, nil -} - // GetBody will return an io.ReadSeeker of the Request's underlying // input body with a concurrency safe wrapper. func (r *Request) GetBody() io.ReadSeeker { return r.safeBody } -// Send will send the request returning error if errors are encountered. +// Send will send the request, returning error if errors are encountered. // // Send will sign the request prior to sending. All Send Handlers will // be executed in the order they were set. @@ -485,79 +515,90 @@ func (r *Request) Send() error { r.Handlers.Complete.Run(r) }() + if err := r.Error; err != nil { + return err + } + for { - if aws.BoolValue(r.Retryable) { - if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) { - r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d", - r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount)) - } - - // The previous http.Request will have a reference to the r.Body - // and the HTTP Client's Transport may still be reading from - // the request's body even though the Client's Do returned. - r.HTTPRequest = copyHTTPRequest(r.HTTPRequest, nil) - r.ResetBody() - - // Closing response body to ensure that no response body is leaked - // between retry attempts. - if r.HTTPResponse != nil && r.HTTPResponse.Body != nil { - r.HTTPResponse.Body.Close() - } - } + r.Error = nil + r.AttemptTime = time.Now() - r.Sign() - if r.Error != nil { - return r.Error + if err := r.Sign(); err != nil { + debugLogReqError(r, "Sign Request", notRetrying, err) + return err } - r.Retryable = nil - - r.Handlers.Send.Run(r) - if r.Error != nil { - if !shouldRetryCancel(r) { - return r.Error - } - - err := r.Error - r.Handlers.Retry.Run(r) - r.Handlers.AfterRetry.Run(r) - if r.Error != nil { - debugLogReqError(r, "Send Request", false, err) - return r.Error - } - debugLogReqError(r, "Send Request", true, err) - continue + if err := r.sendRequest(); err == nil { + return nil } - r.Handlers.UnmarshalMeta.Run(r) - r.Handlers.ValidateResponse.Run(r) - if r.Error != nil { - r.Handlers.UnmarshalError.Run(r) - err := r.Error - - r.Handlers.Retry.Run(r) - r.Handlers.AfterRetry.Run(r) - if r.Error != nil { - debugLogReqError(r, "Validate Response", false, err) - return r.Error - } - debugLogReqError(r, "Validate Response", true, err) - continue + r.Handlers.Retry.Run(r) + r.Handlers.AfterRetry.Run(r) + + if r.Error != nil || !aws.BoolValue(r.Retryable) { + return r.Error } - r.Handlers.Unmarshal.Run(r) - if r.Error != nil { - err := r.Error - r.Handlers.Retry.Run(r) - r.Handlers.AfterRetry.Run(r) - if r.Error != nil { - debugLogReqError(r, "Unmarshal Response", false, err) - return r.Error - } - debugLogReqError(r, "Unmarshal Response", true, err) - continue + if err := r.prepareRetry(); err != nil { + r.Error = err + return err } + } +} + +func (r *Request) prepareRetry() error { + if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) { + r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d", + r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount)) + } + + // The previous http.Request will have a reference to the r.Body + // and the HTTP Client's Transport may still be reading from + // the request's body even though the Client's Do returned. + r.HTTPRequest = copyHTTPRequest(r.HTTPRequest, nil) + r.ResetBody() + if err := r.Error; err != nil { + return awserr.New(ErrCodeSerialization, + "failed to prepare body for retry", err) - break + } + + // Closing response body to ensure that no response body is leaked + // between retry attempts. + if r.HTTPResponse != nil && r.HTTPResponse.Body != nil { + r.HTTPResponse.Body.Close() + } + + return nil +} + +func (r *Request) sendRequest() (sendErr error) { + defer r.Handlers.CompleteAttempt.Run(r) + + r.Retryable = nil + r.Handlers.Send.Run(r) + if r.Error != nil { + debugLogReqError(r, "Send Request", + fmtAttemptCount(r.RetryCount, r.MaxRetries()), + r.Error) + return r.Error + } + + r.Handlers.UnmarshalMeta.Run(r) + r.Handlers.ValidateResponse.Run(r) + if r.Error != nil { + r.Handlers.UnmarshalError.Run(r) + debugLogReqError(r, "Validate Response", + fmtAttemptCount(r.RetryCount, r.MaxRetries()), + r.Error) + return r.Error + } + + r.Handlers.Unmarshal.Run(r) + if r.Error != nil { + debugLogReqError(r, "Unmarshal Response", + fmtAttemptCount(r.RetryCount, r.MaxRetries()), + r.Error) + return r.Error } return nil @@ -583,32 +624,6 @@ func AddToUserAgent(r *Request, s string) { r.HTTPRequest.Header.Set("User-Agent", s) } -func shouldRetryCancel(r *Request) bool { - awsErr, ok := r.Error.(awserr.Error) - timeoutErr := false - errStr := r.Error.Error() - if ok { - if awsErr.Code() == CanceledErrorCode { - return false - } - err := awsErr.OrigErr() - netErr, netOK := err.(net.Error) - timeoutErr = netOK && netErr.Temporary() - if urlErr, ok := err.(*url.Error); !timeoutErr && ok { - errStr = urlErr.Err.Error() - } - } - - // There can be two types of canceled errors here. - // The first being a net.Error and the other being an error. - // If the request was timed out, we want to continue the retry - // process. Otherwise, return the canceled error. - return timeoutErr || - (errStr != "net/http: request canceled" && - errStr != "net/http: request canceled while waiting for connection") - -} - // SanitizeHostForHeader removes default port from host and updates request.Host func SanitizeHostForHeader(r *http.Request) { host := getHost(r) diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/request_1_7.go b/vendor/github.com/aws/aws-sdk-go/aws/request/request_1_7.go index 869b97a1a0fa7..e36e468b7c61d 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/request_1_7.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/request_1_7.go @@ -21,7 +21,7 @@ func (noBody) WriteTo(io.Writer) (int64, error) { return 0, nil } var NoBody = noBody{} // ResetBody rewinds the request body back to its starting position, and -// set's the HTTP Request body reference. When the body is read prior +// sets the HTTP Request body reference. When the body is read prior // to being sent in the HTTP request it will need to be rewound. // // ResetBody will automatically be called by the SDK's build handler, but if diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/request_1_8.go b/vendor/github.com/aws/aws-sdk-go/aws/request/request_1_8.go index c32fc69bc56fe..de1292f45a23d 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/request_1_8.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/request_1_8.go @@ -4,6 +4,8 @@ package request import ( "net/http" + + "github.com/aws/aws-sdk-go/aws/awserr" ) // NoBody is a http.NoBody reader instructing Go HTTP client to not include @@ -11,7 +13,7 @@ import ( var NoBody = http.NoBody // ResetBody rewinds the request body back to its starting position, and -// set's the HTTP Request body reference. When the body is read prior +// sets the HTTP Request body reference. When the body is read prior // to being sent in the HTTP request it will need to be rewound. // // ResetBody will automatically be called by the SDK's build handler, but if @@ -24,7 +26,8 @@ var NoBody = http.NoBody func (r *Request) ResetBody() { body, err := r.getNextRequestBody() if err != nil { - r.Error = err + r.Error = awserr.New(ErrCodeSerialization, + "failed to reset request body", err) return } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/request_pagination.go b/vendor/github.com/aws/aws-sdk-go/aws/request/request_pagination.go index 59de6736b6119..64784e16f3dec 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/request_pagination.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/request_pagination.go @@ -17,11 +17,13 @@ import ( // does the pagination between API operations, and Paginator defines the // configuration that will be used per page request. // -// cont := true -// for p.Next() && cont { +// for p.Next() { // data := p.Page().(*s3.ListObjectsOutput) // // process the page's data +// // ... +// // break out of loop to stop fetching additional pages // } +// // return p.Err() // // See service client API operation Pages methods for examples how the SDK will @@ -35,8 +37,12 @@ type Pagination struct { // NewRequest should always be built from the same API operations. It is // undefined if different API operations are returned on subsequent calls. NewRequest func() (*Request, error) + // EndPageOnSameToken, when enabled, will allow the paginator to stop on + // token that are the same as its previous tokens. + EndPageOnSameToken bool started bool + prevTokens []interface{} nextTokens []interface{} err error @@ -49,7 +55,15 @@ type Pagination struct { // // Will always return true if Next has not been called yet. func (p *Pagination) HasNextPage() bool { - return !(p.started && len(p.nextTokens) == 0) + if !p.started { + return true + } + + hasNextPage := len(p.nextTokens) != 0 + if p.EndPageOnSameToken { + return hasNextPage && !awsutil.DeepEqual(p.nextTokens, p.prevTokens) + } + return hasNextPage } // Err returns the error Pagination encountered when retrieving the next page. @@ -96,6 +110,7 @@ func (p *Pagination) Next() bool { return false } + p.prevTokens = p.nextTokens p.nextTokens = req.nextPageTokens() p.curPage = req.Data @@ -133,7 +148,7 @@ func (r *Request) nextPageTokens() []interface{} { return nil } case bool: - if v == false { + if !v { return nil } } @@ -142,13 +157,28 @@ func (r *Request) nextPageTokens() []interface{} { tokens := []interface{}{} tokenAdded := false for _, outToken := range r.Operation.OutputTokens { - v, _ := awsutil.ValuesAtPath(r.Data, outToken) - if len(v) > 0 { - tokens = append(tokens, v[0]) - tokenAdded = true - } else { + vs, _ := awsutil.ValuesAtPath(r.Data, outToken) + if len(vs) == 0 { tokens = append(tokens, nil) + continue } + v := vs[0] + + switch tv := v.(type) { + case *string: + if len(aws.StringValue(tv)) == 0 { + tokens = append(tokens, nil) + continue + } + case string: + if len(tv) == 0 { + tokens = append(tokens, nil) + continue + } + } + + tokenAdded = true + tokens = append(tokens, v) } if !tokenAdded { return nil diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go b/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go index f35fef213ed96..752ae47f8459a 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go @@ -1,32 +1,81 @@ package request import ( + "net" + "net/url" + "strings" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" ) -// Retryer is an interface to control retry logic for a given service. -// The default implementation used by most services is the client.DefaultRetryer -// structure, which contains basic retry logic using exponential backoff. +// Retryer provides the interface drive the SDK's request retry behavior. The +// Retryer implementation is responsible for implementing exponential backoff, +// and determine if a request API error should be retried. +// +// client.DefaultRetryer is the SDK's default implementation of the Retryer. It +// uses the which uses the Request.IsErrorRetryable and Request.IsErrorThrottle +// methods to determine if the request is retried. type Retryer interface { + // RetryRules return the retry delay that should be used by the SDK before + // making another request attempt for the failed request. RetryRules(*Request) time.Duration + + // ShouldRetry returns if the failed request is retryable. + // + // Implementations may consider request attempt count when determining if a + // request is retryable, but the SDK will use MaxRetries to limit the + // number of attempts a request are made. ShouldRetry(*Request) bool + + // MaxRetries is the number of times a request may be retried before + // failing. MaxRetries() int } -// WithRetryer sets a config Retryer value to the given Config returning it -// for chaining. +// WithRetryer sets a Retryer value to the given Config returning the Config +// value for chaining. The value must not be nil. func WithRetryer(cfg *aws.Config, retryer Retryer) *aws.Config { + if retryer == nil { + if cfg.Logger != nil { + cfg.Logger.Log("ERROR: Request.WithRetryer called with nil retryer. Replacing with retry disabled Retryer.") + } + retryer = noOpRetryer{} + } cfg.Retryer = retryer return cfg + +} + +// noOpRetryer is a internal no op retryer used when a request is created +// without a retryer. +// +// Provides a retryer that performs no retries. +// It should be used when we do not want retries to be performed. +type noOpRetryer struct{} + +// MaxRetries returns the number of maximum returns the service will use to make +// an individual API; For NoOpRetryer the MaxRetries will always be zero. +func (d noOpRetryer) MaxRetries() int { + return 0 +} + +// ShouldRetry will always return false for NoOpRetryer, as it should never retry. +func (d noOpRetryer) ShouldRetry(_ *Request) bool { + return false +} + +// RetryRules returns the delay duration before retrying this request again; +// since NoOpRetryer does not retry, RetryRules always returns 0. +func (d noOpRetryer) RetryRules(_ *Request) time.Duration { + return 0 } // retryableCodes is a collection of service response codes which are retry-able // without any further action. var retryableCodes = map[string]struct{}{ - "RequestError": {}, + ErrCodeRequestError: {}, "RequestTimeout": {}, ErrCodeResponseTimeout: {}, "RequestTimeoutException": {}, // Glacier's flavor of RequestTimeout @@ -34,12 +83,16 @@ var retryableCodes = map[string]struct{}{ var throttleCodes = map[string]struct{}{ "ProvisionedThroughputExceededException": {}, + "ThrottledException": {}, // SNS, XRay, ResourceGroupsTagging API "Throttling": {}, "ThrottlingException": {}, "RequestLimitExceeded": {}, "RequestThrottled": {}, + "RequestThrottledException": {}, "TooManyRequestsException": {}, // Lambda functions "PriorRequestNotComplete": {}, // Route53 + "TransactionInProgressException": {}, + "EC2ThrottledException": {}, // EC2 } // credsExpiredCodes is a collection of error codes which signify the credentials @@ -74,10 +127,6 @@ var validParentCodes = map[string]struct{}{ ErrCodeRead: {}, } -type temporaryError interface { - Temporary() bool -} - func isNestedErrorRetryable(parentErr awserr.Error) bool { if parentErr == nil { return false @@ -96,8 +145,8 @@ func isNestedErrorRetryable(parentErr awserr.Error) bool { return isCodeRetryable(aerr.Code()) } - if t, ok := err.(temporaryError); ok { - return t.Temporary() + if t, ok := err.(temporary); ok { + return t.Temporary() || isErrConnectionReset(err) } return isErrConnectionReset(err) @@ -106,32 +155,90 @@ func isNestedErrorRetryable(parentErr awserr.Error) bool { // IsErrorRetryable returns whether the error is retryable, based on its Code. // Returns false if error is nil. func IsErrorRetryable(err error) bool { - if err != nil { - if aerr, ok := err.(awserr.Error); ok { - return isCodeRetryable(aerr.Code()) || isNestedErrorRetryable(aerr) + if err == nil { + return false + } + return shouldRetryError(err) +} + +type temporary interface { + Temporary() bool +} + +func shouldRetryError(origErr error) bool { + switch err := origErr.(type) { + case awserr.Error: + if err.Code() == CanceledErrorCode { + return false } + if isNestedErrorRetryable(err) { + return true + } + + origErr := err.OrigErr() + var shouldRetry bool + if origErr != nil { + shouldRetry = shouldRetryError(origErr) + if err.Code() == ErrCodeRequestError && !shouldRetry { + return false + } + } + if isCodeRetryable(err.Code()) { + return true + } + return shouldRetry + + case *url.Error: + if strings.Contains(err.Error(), "connection refused") { + // Refused connections should be retried as the service may not yet + // be running on the port. Go TCP dial considers refused + // connections as not temporary. + return true + } + // *url.Error only implements Temporary after golang 1.6 but since + // url.Error only wraps the error: + return shouldRetryError(err.Err) + + case temporary: + if netErr, ok := err.(*net.OpError); ok && netErr.Op == "dial" { + return true + } + // If the error is temporary, we want to allow continuation of the + // retry process + return err.Temporary() || isErrConnectionReset(origErr) + + case nil: + // `awserr.Error.OrigErr()` can be nil, meaning there was an error but + // because we don't know the cause, it is marked as retryable. See + // TestRequest4xxUnretryable for an example. + return true + + default: + switch err.Error() { + case "net/http: request canceled", + "net/http: request canceled while waiting for connection": + // known 1.5 error case when an http request is cancelled + return false + } + // here we don't know the error; so we allow a retry. + return true } - return false } // IsErrorThrottle returns whether the error is to be throttled based on its code. // Returns false if error is nil. func IsErrorThrottle(err error) bool { - if err != nil { - if aerr, ok := err.(awserr.Error); ok { - return isCodeThrottle(aerr.Code()) - } + if aerr, ok := err.(awserr.Error); ok && aerr != nil { + return isCodeThrottle(aerr.Code()) } return false } -// IsErrorExpiredCreds returns whether the error code is a credential expiry error. -// Returns false if error is nil. +// IsErrorExpiredCreds returns whether the error code is a credential expiry +// error. Returns false if error is nil. func IsErrorExpiredCreds(err error) bool { - if err != nil { - if aerr, ok := err.(awserr.Error); ok { - return isCodeExpiredCreds(aerr.Code()) - } + if aerr, ok := err.(awserr.Error); ok && aerr != nil { + return isCodeExpiredCreds(aerr.Code()) } return false } @@ -141,17 +248,58 @@ func IsErrorExpiredCreds(err error) bool { // // Alias for the utility function IsErrorRetryable func (r *Request) IsErrorRetryable() bool { + if isErrCode(r.Error, r.RetryErrorCodes) { + return true + } + + // HTTP response status code 501 should not be retried. + // 501 represents Not Implemented which means the request method is not + // supported by the server and cannot be handled. + if r.HTTPResponse != nil { + // HTTP response status code 500 represents internal server error and + // should be retried without any throttle. + if r.HTTPResponse.StatusCode == 500 { + return true + } + } return IsErrorRetryable(r.Error) } -// IsErrorThrottle returns whether the error is to be throttled based on its code. -// Returns false if the request has no Error set +// IsErrorThrottle returns whether the error is to be throttled based on its +// code. Returns false if the request has no Error set. // // Alias for the utility function IsErrorThrottle func (r *Request) IsErrorThrottle() bool { + if isErrCode(r.Error, r.ThrottleErrorCodes) { + return true + } + + if r.HTTPResponse != nil { + switch r.HTTPResponse.StatusCode { + case + 429, // error caused due to too many requests + 502, // Bad Gateway error should be throttled + 503, // caused when service is unavailable + 504: // error occurred due to gateway timeout + return true + } + } + return IsErrorThrottle(r.Error) } +func isErrCode(err error, codes []string) bool { + if aerr, ok := err.(awserr.Error); ok && aerr != nil { + for _, code := range codes { + if code == aerr.Code() { + return true + } + } + } + + return false +} + // IsErrorExpired returns whether the error code is a credential expiry error. // Returns false if the request has no Error set. // diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/validation.go b/vendor/github.com/aws/aws-sdk-go/aws/request/validation.go index 4012462282197..8630683f3174b 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/validation.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/validation.go @@ -17,6 +17,12 @@ const ( ParamMinValueErrCode = "ParamMinValueError" // ParamMinLenErrCode is the error code for fields without enough elements. ParamMinLenErrCode = "ParamMinLenError" + // ParamMaxLenErrCode is the error code for value being too long. + ParamMaxLenErrCode = "ParamMaxLenError" + + // ParamFormatErrCode is the error code for a field with invalid + // format or characters. + ParamFormatErrCode = "ParamFormatInvalidError" ) // Validator provides a way for types to perform validation logic on their @@ -232,3 +238,49 @@ func NewErrParamMinLen(field string, min int) *ErrParamMinLen { func (e *ErrParamMinLen) MinLen() int { return e.min } + +// An ErrParamMaxLen represents a maximum length parameter error. +type ErrParamMaxLen struct { + errInvalidParam + max int +} + +// NewErrParamMaxLen creates a new maximum length parameter error. +func NewErrParamMaxLen(field string, max int, value string) *ErrParamMaxLen { + return &ErrParamMaxLen{ + errInvalidParam: errInvalidParam{ + code: ParamMaxLenErrCode, + field: field, + msg: fmt.Sprintf("maximum size of %v, %v", max, value), + }, + max: max, + } +} + +// MaxLen returns the field's required minimum length. +func (e *ErrParamMaxLen) MaxLen() int { + return e.max +} + +// An ErrParamFormat represents a invalid format parameter error. +type ErrParamFormat struct { + errInvalidParam + format string +} + +// NewErrParamFormat creates a new invalid format parameter error. +func NewErrParamFormat(field string, format, value string) *ErrParamFormat { + return &ErrParamFormat{ + errInvalidParam: errInvalidParam{ + code: ParamFormatErrCode, + field: field, + msg: fmt.Sprintf("format %v, %v", format, value), + }, + format: format, + } +} + +// Format returns the field's required format. +func (e *ErrParamFormat) Format() string { + return e.format +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport.go b/vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport.go new file mode 100644 index 0000000000000..ea9ebb6f6a25d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport.go @@ -0,0 +1,26 @@ +// +build go1.7 + +package session + +import ( + "net" + "net/http" + "time" +) + +// Transport that should be used when a custom CA bundle is specified with the +// SDK. +func getCABundleTransport() *http.Transport { + return &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport_1_5.go b/vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport_1_5.go new file mode 100644 index 0000000000000..fec39dfc1264e --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport_1_5.go @@ -0,0 +1,22 @@ +// +build !go1.6,go1.5 + +package session + +import ( + "net" + "net/http" + "time" +) + +// Transport that should be used when a custom CA bundle is specified with the +// SDK. +func getCABundleTransport() *http.Transport { + return &http.Transport{ + Proxy: http.ProxyFromEnvironment, + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + TLSHandshakeTimeout: 10 * time.Second, + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport_1_6.go b/vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport_1_6.go new file mode 100644 index 0000000000000..1c5a5391e658b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/session/cabundle_transport_1_6.go @@ -0,0 +1,23 @@ +// +build !go1.7,go1.6 + +package session + +import ( + "net" + "net/http" + "time" +) + +// Transport that should be used when a custom CA bundle is specified with the +// SDK. +func getCABundleTransport() *http.Transport { + return &http.Transport{ + Proxy: http.ProxyFromEnvironment, + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/session/credentials.go b/vendor/github.com/aws/aws-sdk-go/aws/session/credentials.go new file mode 100644 index 0000000000000..cc64e24f1d563 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/session/credentials.go @@ -0,0 +1,259 @@ +package session + +import ( + "fmt" + "os" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/processcreds" + "github.com/aws/aws-sdk-go/aws/credentials/stscreds" + "github.com/aws/aws-sdk-go/aws/defaults" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/internal/shareddefaults" +) + +func resolveCredentials(cfg *aws.Config, + envCfg envConfig, sharedCfg sharedConfig, + handlers request.Handlers, + sessOpts Options, +) (*credentials.Credentials, error) { + + switch { + case len(sessOpts.Profile) != 0: + // User explicitly provided an Profile in the session's configuration + // so load that profile from shared config first. + // Github(aws/aws-sdk-go#2727) + return resolveCredsFromProfile(cfg, envCfg, sharedCfg, handlers, sessOpts) + + case envCfg.Creds.HasKeys(): + // Environment credentials + return credentials.NewStaticCredentialsFromCreds(envCfg.Creds), nil + + case len(envCfg.WebIdentityTokenFilePath) != 0: + // Web identity token from environment, RoleARN required to also be + // set. + return assumeWebIdentity(cfg, handlers, + envCfg.WebIdentityTokenFilePath, + envCfg.RoleARN, + envCfg.RoleSessionName, + ) + + default: + // Fallback to the "default" credential resolution chain. + return resolveCredsFromProfile(cfg, envCfg, sharedCfg, handlers, sessOpts) + } +} + +// WebIdentityEmptyRoleARNErr will occur if 'AWS_WEB_IDENTITY_TOKEN_FILE' was set but +// 'AWS_ROLE_ARN' was not set. +var WebIdentityEmptyRoleARNErr = awserr.New(stscreds.ErrCodeWebIdentity, "role ARN is not set", nil) + +// WebIdentityEmptyTokenFilePathErr will occur if 'AWS_ROLE_ARN' was set but +// 'AWS_WEB_IDENTITY_TOKEN_FILE' was not set. +var WebIdentityEmptyTokenFilePathErr = awserr.New(stscreds.ErrCodeWebIdentity, "token file path is not set", nil) + +func assumeWebIdentity(cfg *aws.Config, handlers request.Handlers, + filepath string, + roleARN, sessionName string, +) (*credentials.Credentials, error) { + + if len(filepath) == 0 { + return nil, WebIdentityEmptyTokenFilePathErr + } + + if len(roleARN) == 0 { + return nil, WebIdentityEmptyRoleARNErr + } + + creds := stscreds.NewWebIdentityCredentials( + &Session{ + Config: cfg, + Handlers: handlers.Copy(), + }, + roleARN, + sessionName, + filepath, + ) + + return creds, nil +} + +func resolveCredsFromProfile(cfg *aws.Config, + envCfg envConfig, sharedCfg sharedConfig, + handlers request.Handlers, + sessOpts Options, +) (creds *credentials.Credentials, err error) { + + switch { + case sharedCfg.SourceProfile != nil: + // Assume IAM role with credentials source from a different profile. + creds, err = resolveCredsFromProfile(cfg, envCfg, + *sharedCfg.SourceProfile, handlers, sessOpts, + ) + + case sharedCfg.Creds.HasKeys(): + // Static Credentials from Shared Config/Credentials file. + creds = credentials.NewStaticCredentialsFromCreds( + sharedCfg.Creds, + ) + + case len(sharedCfg.CredentialProcess) != 0: + // Get credentials from CredentialProcess + creds = processcreds.NewCredentials(sharedCfg.CredentialProcess) + + case len(sharedCfg.CredentialSource) != 0: + creds, err = resolveCredsFromSource(cfg, envCfg, + sharedCfg, handlers, sessOpts, + ) + + case len(sharedCfg.WebIdentityTokenFile) != 0: + // Credentials from Assume Web Identity token require an IAM Role, and + // that roll will be assumed. May be wrapped with another assume role + // via SourceProfile. + return assumeWebIdentity(cfg, handlers, + sharedCfg.WebIdentityTokenFile, + sharedCfg.RoleARN, + sharedCfg.RoleSessionName, + ) + + default: + // Fallback to default credentials provider, include mock errors for + // the credential chain so user can identify why credentials failed to + // be retrieved. + creds = credentials.NewCredentials(&credentials.ChainProvider{ + VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors), + Providers: []credentials.Provider{ + &credProviderError{ + Err: awserr.New("EnvAccessKeyNotFound", + "failed to find credentials in the environment.", nil), + }, + &credProviderError{ + Err: awserr.New("SharedCredsLoad", + fmt.Sprintf("failed to load profile, %s.", envCfg.Profile), nil), + }, + defaults.RemoteCredProvider(*cfg, handlers), + }, + }) + } + if err != nil { + return nil, err + } + + if len(sharedCfg.RoleARN) > 0 { + cfgCp := *cfg + cfgCp.Credentials = creds + return credsFromAssumeRole(cfgCp, handlers, sharedCfg, sessOpts) + } + + return creds, nil +} + +// valid credential source values +const ( + credSourceEc2Metadata = "Ec2InstanceMetadata" + credSourceEnvironment = "Environment" + credSourceECSContainer = "EcsContainer" +) + +func resolveCredsFromSource(cfg *aws.Config, + envCfg envConfig, sharedCfg sharedConfig, + handlers request.Handlers, + sessOpts Options, +) (creds *credentials.Credentials, err error) { + + switch sharedCfg.CredentialSource { + case credSourceEc2Metadata: + p := defaults.RemoteCredProvider(*cfg, handlers) + creds = credentials.NewCredentials(p) + + case credSourceEnvironment: + creds = credentials.NewStaticCredentialsFromCreds(envCfg.Creds) + + case credSourceECSContainer: + if len(os.Getenv(shareddefaults.ECSCredsProviderEnvVar)) == 0 { + return nil, ErrSharedConfigECSContainerEnvVarEmpty + } + + p := defaults.RemoteCredProvider(*cfg, handlers) + creds = credentials.NewCredentials(p) + + default: + return nil, ErrSharedConfigInvalidCredSource + } + + return creds, nil +} + +func credsFromAssumeRole(cfg aws.Config, + handlers request.Handlers, + sharedCfg sharedConfig, + sessOpts Options, +) (*credentials.Credentials, error) { + + if len(sharedCfg.MFASerial) != 0 && sessOpts.AssumeRoleTokenProvider == nil { + // AssumeRole Token provider is required if doing Assume Role + // with MFA. + return nil, AssumeRoleTokenProviderNotSetError{} + } + + return stscreds.NewCredentials( + &Session{ + Config: &cfg, + Handlers: handlers.Copy(), + }, + sharedCfg.RoleARN, + func(opt *stscreds.AssumeRoleProvider) { + opt.RoleSessionName = sharedCfg.RoleSessionName + opt.Duration = sessOpts.AssumeRoleDuration + + // Assume role with external ID + if len(sharedCfg.ExternalID) > 0 { + opt.ExternalID = aws.String(sharedCfg.ExternalID) + } + + // Assume role with MFA + if len(sharedCfg.MFASerial) > 0 { + opt.SerialNumber = aws.String(sharedCfg.MFASerial) + opt.TokenProvider = sessOpts.AssumeRoleTokenProvider + } + }, + ), nil +} + +// AssumeRoleTokenProviderNotSetError is an error returned when creating a +// session when the MFAToken option is not set when shared config is configured +// load assume a role with an MFA token. +type AssumeRoleTokenProviderNotSetError struct{} + +// Code is the short id of the error. +func (e AssumeRoleTokenProviderNotSetError) Code() string { + return "AssumeRoleTokenProviderNotSetError" +} + +// Message is the description of the error +func (e AssumeRoleTokenProviderNotSetError) Message() string { + return fmt.Sprintf("assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.") +} + +// OrigErr is the underlying error that caused the failure. +func (e AssumeRoleTokenProviderNotSetError) OrigErr() error { + return nil +} + +// Error satisfies the error interface. +func (e AssumeRoleTokenProviderNotSetError) Error() string { + return awserr.SprintError(e.Code(), e.Message(), "", nil) +} + +type credProviderError struct { + Err error +} + +func (c credProviderError) Retrieve() (credentials.Value, error) { + return credentials.Value{}, c.Err +} +func (c credProviderError) IsExpired() bool { + return true +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/session/doc.go b/vendor/github.com/aws/aws-sdk-go/aws/session/doc.go index ea7b886f81f56..7ec66e7e589ae 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/session/doc.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/session/doc.go @@ -1,97 +1,93 @@ /* -Package session provides configuration for the SDK's service clients. - -Sessions can be shared across all service clients that share the same base -configuration. The Session is built from the SDK's default configuration and -request handlers. - -Sessions should be cached when possible, because creating a new Session will -load all configuration values from the environment, and config files each time -the Session is created. Sharing the Session value across all of your service -clients will ensure the configuration is loaded the fewest number of times possible. - -Concurrency +Package session provides configuration for the SDK's service clients. Sessions +can be shared across service clients that share the same base configuration. Sessions are safe to use concurrently as long as the Session is not being -modified. The SDK will not modify the Session once the Session has been created. -Creating service clients concurrently from a shared Session is safe. - -Sessions from Shared Config - -Sessions can be created using the method above that will only load the -additional config if the AWS_SDK_LOAD_CONFIG environment variable is set. -Alternatively you can explicitly create a Session with shared config enabled. -To do this you can use NewSessionWithOptions to configure how the Session will -be created. Using the NewSessionWithOptions with SharedConfigState set to -SharedConfigEnable will create the session as if the AWS_SDK_LOAD_CONFIG -environment variable was set. +modified. Sessions should be cached when possible, because creating a new +Session will load all configuration values from the environment, and config +files each time the Session is created. Sharing the Session value across all of +your service clients will ensure the configuration is loaded the fewest number +of times possible. -Creating Sessions - -When creating Sessions optional aws.Config values can be passed in that will -override the default, or loaded config values the Session is being created -with. This allows you to provide additional, or case based, configuration -as needed. +Sessions options from Shared Config By default NewSession will only load credentials from the shared credentials file (~/.aws/credentials). If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value the Session will be created from the configuration values from the shared config (~/.aws/config) and shared credentials -(~/.aws/credentials) files. See the section Sessions from Shared Config for -more information. +(~/.aws/credentials) files. Using the NewSessionWithOptions with +SharedConfigState set to SharedConfigEnable will create the session as if the +AWS_SDK_LOAD_CONFIG environment variable was set. -Create a Session with the default config and request handlers. With credentials -region, and profile loaded from the environment and shared config automatically. -Requires the AWS_PROFILE to be set, or "default" is used. +Credential and config loading order - // Create Session - sess := session.Must(session.NewSession()) +The Session will attempt to load configuration and credentials from the +environment, configuration files, and other credential sources. The order +configuration is loaded in is: - // Create a Session with a custom region - sess := session.Must(session.NewSession(&aws.Config{ - Region: aws.String("us-east-1"), - })) + * Environment Variables + * Shared Credentials file + * Shared Configuration file (if SharedConfig is enabled) + * EC2 Instance Metadata (credentials only) - // Create a S3 client instance from a session - sess := session.Must(session.NewSession()) +The Environment variables for credentials will have precedence over shared +config even if SharedConfig is enabled. To override this behavior, and use +shared config credentials instead specify the session.Options.Profile, (e.g. +when using credential_source=Environment to assume a role). + + sess, err := session.NewSessionWithOptions(session.Options{ + Profile: "myProfile", + }) - svc := s3.New(sess) +Creating Sessions -Create Session With Option Overrides +Creating a Session without additional options will load credentials region, and +profile loaded from the environment and shared config automatically. See, +"Environment Variables" section for information on environment variables used +by Session. -In addition to NewSession, Sessions can be created using NewSessionWithOptions. -This func allows you to control and override how the Session will be created -through code instead of being driven by environment variables only. + // Create Session + sess, err := session.NewSession() -Use NewSessionWithOptions when you want to provide the config profile, or -override the shared config state (AWS_SDK_LOAD_CONFIG). + +When creating Sessions optional aws.Config values can be passed in that will +override the default, or loaded, config values the Session is being created +with. This allows you to provide additional, or case based, configuration +as needed. + + // Create a Session with a custom region + sess, err := session.NewSession(&aws.Config{ + Region: aws.String("us-west-2"), + }) + +Use NewSessionWithOptions to provide additional configuration driving how the +Session's configuration will be loaded. Such as, specifying shared config +profile, or override the shared config state, (AWS_SDK_LOAD_CONFIG). // Equivalent to session.NewSession() - sess := session.Must(session.NewSessionWithOptions(session.Options{ + sess, err := session.NewSessionWithOptions(session.Options{ // Options - })) + }) - // Specify profile to load for the session's config - sess := session.Must(session.NewSessionWithOptions(session.Options{ - Profile: "profile_name", - })) + sess, err := session.NewSessionWithOptions(session.Options{ + // Specify profile to load for the session's config + Profile: "profile_name", - // Specify profile for config and region for requests - sess := session.Must(session.NewSessionWithOptions(session.Options{ - Config: aws.Config{Region: aws.String("us-east-1")}, - Profile: "profile_name", - })) + // Provide SDK Config options, such as Region. + Config: aws.Config{ + Region: aws.String("us-west-2"), + }, - // Force enable Shared Config support - sess := session.Must(session.NewSessionWithOptions(session.Options{ + // Force enable Shared Config support SharedConfigState: session.SharedConfigEnable, - })) + }) Adding Handlers -You can add handlers to a session for processing HTTP requests. All service -clients that use the session inherit the handlers. For example, the following -handler logs every request and its payload made by a service client: +You can add handlers to a session to decorate API operation, (e.g. adding HTTP +headers). All clients that use the Session receive a copy of the Session's +handlers. For example, the following request handler added to the Session logs +every requests made. // Create a session, and add additional handlers for all service // clients created with the Session to inherit. Adds logging handler. @@ -99,22 +95,15 @@ handler logs every request and its payload made by a service client: sess.Handlers.Send.PushFront(func(r *request.Request) { // Log every request made and its payload - logger.Println("Request: %s/%s, Payload: %s", + logger.Printf("Request: %s/%s, Params: %s", r.ClientInfo.ServiceName, r.Operation, r.Params) }) -Deprecated "New" function - -The New session function has been deprecated because it does not provide good -way to return errors that occur when loading the configuration files and values. -Because of this, NewSession was created so errors can be retrieved when -creating a session fails. - Shared Config Fields -By default the SDK will only load the shared credentials file's (~/.aws/credentials) -credentials values, and all other config is provided by the environment variables, -SDK defaults, and user provided aws.Config values. +By default the SDK will only load the shared credentials file's +(~/.aws/credentials) credentials values, and all other config is provided by +the environment variables, SDK defaults, and user provided aws.Config values. If the AWS_SDK_LOAD_CONFIG environment variable is set, or SharedConfigEnable option is used to create the Session the full shared config values will be @@ -125,24 +114,31 @@ files have the same format. If both config files are present the configuration from both files will be read. The Session will be created from configuration values from the shared -credentials file (~/.aws/credentials) over those in the shared config file (~/.aws/config). +credentials file (~/.aws/credentials) over those in the shared config file +(~/.aws/config). -Credentials are the values the SDK should use for authenticating requests with -AWS Services. They arfrom a configuration file will need to include both -aws_access_key_id and aws_secret_access_key must be provided together in the -same file to be considered valid. The values will be ignored if not a complete -group. aws_session_token is an optional field that can be provided if both of -the other two fields are also provided. +Credentials are the values the SDK uses to authenticating requests with AWS +Services. When specified in a file, both aws_access_key_id and +aws_secret_access_key must be provided together in the same file to be +considered valid. They will be ignored if both are not present. +aws_session_token is an optional field that can be provided in addition to the +other two fields. aws_access_key_id = AKID aws_secret_access_key = SECRET aws_session_token = TOKEN -Assume Role values allow you to configure the SDK to assume an IAM role using -a set of credentials provided in a config file via the source_profile field. -Both "role_arn" and "source_profile" are required. The SDK supports assuming -a role with MFA token if the session option AssumeRoleTokenProvider -is set. + ; region only supported if SharedConfigEnabled. + region = us-east-1 + +Assume Role configuration + +The role_arn field allows you to configure the SDK to assume an IAM role using +a set of credentials from another source. Such as when paired with static +credentials, "profile_source", "credential_process", or "credential_source" +fields. If "role_arn" is provided, a source of credentials must also be +specified, such as "source_profile", "credential_source", or +"credential_process". role_arn = arn:aws:iam:::role/ source_profile = profile_with_creds @@ -150,40 +146,16 @@ is set. mfa_serial = role_session_name = session_name -Region is the region the SDK should use for looking up AWS service endpoints -and signing requests. - - region = us-east-1 - -Assume Role with MFA token -To create a session with support for assuming an IAM role with MFA set the -session option AssumeRoleTokenProvider to a function that will prompt for the -MFA token code when the SDK assumes the role and refreshes the role's credentials. -This allows you to configure the SDK via the shared config to assumea role -with MFA tokens. - -In order for the SDK to assume a role with MFA the SharedConfigState -session option must be set to SharedConfigEnable, or AWS_SDK_LOAD_CONFIG -environment variable set. - -The shared configuration instructs the SDK to assume an IAM role with MFA -when the mfa_serial configuration field is set in the shared config -(~/.aws/config) or shared credentials (~/.aws/credentials) file. - -If mfa_serial is set in the configuration, the SDK will assume the role, and -the AssumeRoleTokenProvider session option is not set an an error will -be returned when creating the session. +The SDK supports assuming a role with MFA token. If "mfa_serial" is set, you +must also set the Session Option.AssumeRoleTokenProvider. The Session will fail +to load if the AssumeRoleTokenProvider is not specified. sess := session.Must(session.NewSessionWithOptions(session.Options{ AssumeRoleTokenProvider: stscreds.StdinTokenProvider, })) - // Create service client value configured for credentials - // from assumed role. - svc := s3.New(sess) - -To setup assume role outside of a session see the stscrds.AssumeRoleProvider +To setup Assume Role outside of a session see the stscreds.AssumeRoleProvider documentation. Environment Variables diff --git a/vendor/github.com/aws/aws-sdk-go/aws/session/env_config.go b/vendor/github.com/aws/aws-sdk-go/aws/session/env_config.go index f1adcf4819829..c1e0e9c9543da 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/session/env_config.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/session/env_config.go @@ -1,10 +1,15 @@ package session import ( + "fmt" "os" "strconv" + "strings" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/defaults" + "github.com/aws/aws-sdk-go/aws/endpoints" ) // EnvProviderName provides a name of the provider when config is loaded from environment. @@ -78,7 +83,7 @@ type envConfig struct { // AWS_CONFIG_FILE=$HOME/my_shared_config SharedConfigFile string - // Sets the path to a custom Credentials Authroity (CA) Bundle PEM file + // Sets the path to a custom Credentials Authority (CA) Bundle PEM file // that the SDK will use instead of the system's root CA bundle. // Only use this if you want to configure the SDK to use a custom set // of CAs. @@ -95,9 +100,69 @@ type envConfig struct { // // AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle CustomCABundle string + + csmEnabled string + CSMEnabled *bool + CSMPort string + CSMHost string + CSMClientID string + + // Enables endpoint discovery via environment variables. + // + // AWS_ENABLE_ENDPOINT_DISCOVERY=true + EnableEndpointDiscovery *bool + enableEndpointDiscovery string + + // Specifies the WebIdentity token the SDK should use to assume a role + // with. + // + // AWS_WEB_IDENTITY_TOKEN_FILE=file_path + WebIdentityTokenFilePath string + + // Specifies the IAM role arn to use when assuming an role. + // + // AWS_ROLE_ARN=role_arn + RoleARN string + + // Specifies the IAM role session name to use when assuming a role. + // + // AWS_ROLE_SESSION_NAME=session_name + RoleSessionName string + + // Specifies the STS Regional Endpoint flag for the SDK to resolve the endpoint + // for a service. + // + // AWS_STS_REGIONAL_ENDPOINTS=regional + // This can take value as `regional` or `legacy` + STSRegionalEndpoint endpoints.STSRegionalEndpoint + + // Specifies the S3 Regional Endpoint flag for the SDK to resolve the + // endpoint for a service. + // + // AWS_S3_US_EAST_1_REGIONAL_ENDPOINT=regional + // This can take value as `regional` or `legacy` + S3UsEast1RegionalEndpoint endpoints.S3UsEast1RegionalEndpoint + + // Specifies if the S3 service should allow ARNs to direct the region + // the client's requests are sent to. + // + // AWS_S3_USE_ARN_REGION=true + S3UseARNRegion bool } var ( + csmEnabledEnvKey = []string{ + "AWS_CSM_ENABLED", + } + csmHostEnvKey = []string{ + "AWS_CSM_HOST", + } + csmPortEnvKey = []string{ + "AWS_CSM_PORT", + } + csmClientIDEnvKey = []string{ + "AWS_CSM_CLIENT_ID", + } credAccessEnvKey = []string{ "AWS_ACCESS_KEY_ID", "AWS_ACCESS_KEY", @@ -110,6 +175,10 @@ var ( "AWS_SESSION_TOKEN", } + enableEndpointDiscoveryEnvKey = []string{ + "AWS_ENABLE_ENDPOINT_DISCOVERY", + } + regionEnvKeys = []string{ "AWS_REGION", "AWS_DEFAULT_REGION", // Only read if AWS_SDK_LOAD_CONFIG is also set @@ -124,6 +193,24 @@ var ( sharedConfigFileEnvKey = []string{ "AWS_CONFIG_FILE", } + webIdentityTokenFilePathEnvKey = []string{ + "AWS_WEB_IDENTITY_TOKEN_FILE", + } + roleARNEnvKey = []string{ + "AWS_ROLE_ARN", + } + roleSessionNameEnvKey = []string{ + "AWS_ROLE_SESSION_NAME", + } + stsRegionalEndpointKey = []string{ + "AWS_STS_REGIONAL_ENDPOINTS", + } + s3UsEast1RegionalEndpoint = []string{ + "AWS_S3_US_EAST_1_REGIONAL_ENDPOINT", + } + s3UseARNRegionEnvKey = []string{ + "AWS_S3_USE_ARN_REGION", + } ) // loadEnvConfig retrieves the SDK's environment configuration. @@ -132,7 +219,7 @@ var ( // If the environment variable `AWS_SDK_LOAD_CONFIG` is set to a truthy value // the shared SDK config will be loaded in addition to the SDK's specific // configuration values. -func loadEnvConfig() envConfig { +func loadEnvConfig() (envConfig, error) { enableSharedConfig, _ := strconv.ParseBool(os.Getenv("AWS_SDK_LOAD_CONFIG")) return envConfigLoad(enableSharedConfig) } @@ -143,24 +230,42 @@ func loadEnvConfig() envConfig { // Loads the shared configuration in addition to the SDK's specific configuration. // This will load the same values as `loadEnvConfig` if the `AWS_SDK_LOAD_CONFIG` // environment variable is set. -func loadSharedEnvConfig() envConfig { +func loadSharedEnvConfig() (envConfig, error) { return envConfigLoad(true) } -func envConfigLoad(enableSharedConfig bool) envConfig { +func envConfigLoad(enableSharedConfig bool) (envConfig, error) { cfg := envConfig{} cfg.EnableSharedConfig = enableSharedConfig - setFromEnvVal(&cfg.Creds.AccessKeyID, credAccessEnvKey) - setFromEnvVal(&cfg.Creds.SecretAccessKey, credSecretEnvKey) - setFromEnvVal(&cfg.Creds.SessionToken, credSessionEnvKey) + // Static environment credentials + var creds credentials.Value + setFromEnvVal(&creds.AccessKeyID, credAccessEnvKey) + setFromEnvVal(&creds.SecretAccessKey, credSecretEnvKey) + setFromEnvVal(&creds.SessionToken, credSessionEnvKey) + if creds.HasKeys() { + // Require logical grouping of credentials + creds.ProviderName = EnvProviderName + cfg.Creds = creds + } + + // Role Metadata + setFromEnvVal(&cfg.RoleARN, roleARNEnvKey) + setFromEnvVal(&cfg.RoleSessionName, roleSessionNameEnvKey) - // Require logical grouping of credentials - if len(cfg.Creds.AccessKeyID) == 0 || len(cfg.Creds.SecretAccessKey) == 0 { - cfg.Creds = credentials.Value{} - } else { - cfg.Creds.ProviderName = EnvProviderName + // Web identity environment variables + setFromEnvVal(&cfg.WebIdentityTokenFilePath, webIdentityTokenFilePathEnvKey) + + // CSM environment variables + setFromEnvVal(&cfg.csmEnabled, csmEnabledEnvKey) + setFromEnvVal(&cfg.CSMHost, csmHostEnvKey) + setFromEnvVal(&cfg.CSMPort, csmPortEnvKey) + setFromEnvVal(&cfg.CSMClientID, csmClientIDEnvKey) + + if len(cfg.csmEnabled) != 0 { + v, _ := strconv.ParseBool(cfg.csmEnabled) + cfg.CSMEnabled = &v } regionKeys := regionEnvKeys @@ -173,17 +278,66 @@ func envConfigLoad(enableSharedConfig bool) envConfig { setFromEnvVal(&cfg.Region, regionKeys) setFromEnvVal(&cfg.Profile, profileKeys) + // endpoint discovery is in reference to it being enabled. + setFromEnvVal(&cfg.enableEndpointDiscovery, enableEndpointDiscoveryEnvKey) + if len(cfg.enableEndpointDiscovery) > 0 { + cfg.EnableEndpointDiscovery = aws.Bool(cfg.enableEndpointDiscovery != "false") + } + setFromEnvVal(&cfg.SharedCredentialsFile, sharedCredsFileEnvKey) setFromEnvVal(&cfg.SharedConfigFile, sharedConfigFileEnvKey) + if len(cfg.SharedCredentialsFile) == 0 { + cfg.SharedCredentialsFile = defaults.SharedCredentialsFilename() + } + if len(cfg.SharedConfigFile) == 0 { + cfg.SharedConfigFile = defaults.SharedConfigFilename() + } + cfg.CustomCABundle = os.Getenv("AWS_CA_BUNDLE") - return cfg + var err error + // STS Regional Endpoint variable + for _, k := range stsRegionalEndpointKey { + if v := os.Getenv(k); len(v) != 0 { + cfg.STSRegionalEndpoint, err = endpoints.GetSTSRegionalEndpoint(v) + if err != nil { + return cfg, fmt.Errorf("failed to load, %v from env config, %v", k, err) + } + } + } + + // S3 Regional Endpoint variable + for _, k := range s3UsEast1RegionalEndpoint { + if v := os.Getenv(k); len(v) != 0 { + cfg.S3UsEast1RegionalEndpoint, err = endpoints.GetS3UsEast1RegionalEndpoint(v) + if err != nil { + return cfg, fmt.Errorf("failed to load, %v from env config, %v", k, err) + } + } + } + + var s3UseARNRegion string + setFromEnvVal(&s3UseARNRegion, s3UseARNRegionEnvKey) + if len(s3UseARNRegion) != 0 { + switch { + case strings.EqualFold(s3UseARNRegion, "false"): + cfg.S3UseARNRegion = false + case strings.EqualFold(s3UseARNRegion, "true"): + cfg.S3UseARNRegion = true + default: + return envConfig{}, fmt.Errorf( + "invalid value for environment variable, %s=%s, need true or false", + s3UseARNRegionEnvKey[0], s3UseARNRegion) + } + } + + return cfg, nil } func setFromEnvVal(dst *string, keys []string) { for _, k := range keys { - if v := os.Getenv(k); len(v) > 0 { + if v := os.Getenv(k); len(v) != 0 { *dst = v break } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/session/session.go b/vendor/github.com/aws/aws-sdk-go/aws/session/session.go index 9f75d5ac58893..0ff4996051012 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/session/session.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/session/session.go @@ -8,25 +8,43 @@ import ( "io/ioutil" "net/http" "os" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/corehandlers" "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/credentials/stscreds" + "github.com/aws/aws-sdk-go/aws/csm" "github.com/aws/aws-sdk-go/aws/defaults" "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/aws/request" ) +const ( + // ErrCodeSharedConfig represents an error that occurs in the shared + // configuration logic + ErrCodeSharedConfig = "SharedConfigErr" +) + +// ErrSharedConfigSourceCollision will be returned if a section contains both +// source_profile and credential_source +var ErrSharedConfigSourceCollision = awserr.New(ErrCodeSharedConfig, "only source profile or credential source can be specified, not both", nil) + +// ErrSharedConfigECSContainerEnvVarEmpty will be returned if the environment +// variables are empty and Environment was set as the credential source +var ErrSharedConfigECSContainerEnvVarEmpty = awserr.New(ErrCodeSharedConfig, "EcsContainer was specified as the credential_source, but 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' was not set", nil) + +// ErrSharedConfigInvalidCredSource will be returned if an invalid credential source was provided +var ErrSharedConfigInvalidCredSource = awserr.New(ErrCodeSharedConfig, "credential source values must be EcsContainer, Ec2InstanceMetadata, or Environment", nil) + // A Session provides a central location to create service clients from and // store configurations and request handlers for those services. // // Sessions are safe to create service clients concurrently, but it is not safe // to mutate the Session concurrently. // -// The Session satisfies the service client's client.ClientConfigProvider. +// The Session satisfies the service client's client.ConfigProvider. type Session struct { Config *aws.Config Handlers request.Handlers @@ -55,10 +73,15 @@ type Session struct { // func is called instead of waiting to receive an error until a request is made. func New(cfgs ...*aws.Config) *Session { // load initial config from environment - envCfg := loadEnvConfig() + envCfg, envErr := loadEnvConfig() if envCfg.EnableSharedConfig { - s, err := newSession(Options{}, envCfg, cfgs...) + var cfg aws.Config + cfg.MergeIn(cfgs...) + s, err := NewSessionWithOptions(Options{ + Config: cfg, + SharedConfigState: SharedConfigEnable, + }) if err != nil { // Old session.New expected all errors to be discovered when // a request is made, and would report the errors then. This @@ -70,16 +93,31 @@ func New(cfgs ...*aws.Config) *Session { // Session creation failed, need to report the error and prevent // any requests from succeeding. s = &Session{Config: defaults.Config()} - s.Config.MergeIn(cfgs...) - s.Config.Logger.Log("ERROR:", msg, "Error:", err) - s.Handlers.Validate.PushBack(func(r *request.Request) { - r.Error = err - }) + s.logDeprecatedNewSessionError(msg, err, cfgs) } + return s } - return deprecatedNewSession(cfgs...) + s := deprecatedNewSession(cfgs...) + if envErr != nil { + msg := "failed to load env config" + s.logDeprecatedNewSessionError(msg, envErr, cfgs) + } + + if csmCfg, err := loadCSMConfig(envCfg, []string{}); err != nil { + if l := s.Config.Logger; l != nil { + l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err)) + } + } else if csmCfg.Enabled { + err := enableCSM(&s.Handlers, csmCfg, s.Config.Logger) + if err != nil { + msg := "failed to enable CSM" + s.logDeprecatedNewSessionError(msg, err, cfgs) + } + } + + return s } // NewSession returns a new Session created from SDK defaults, config files, @@ -95,7 +133,7 @@ func New(cfgs ...*aws.Config) *Session { // to be built with retrieving credentials with AssumeRole set in the config. // // See the NewSessionWithOptions func for information on how to override or -// control through code how the Session will be created. Such as specifying the +// control through code how the Session will be created, such as specifying the // config profile, and controlling if shared config is enabled or not. func NewSession(cfgs ...*aws.Config) (*Session, error) { opts := Options{} @@ -179,6 +217,12 @@ type Options struct { // the config enables assume role wit MFA via the mfa_serial field. AssumeRoleTokenProvider func() (string, error) + // When the SDK's shared config is configured to assume a role this option + // may be provided to set the expiry duration of the STS credentials. + // Defaults to 15 minutes if not set as documented in the + // stscreds.AssumeRoleProvider. + AssumeRoleDuration time.Duration + // Reader for a custom Credentials Authority (CA) bundle in PEM format that // the SDK will use instead of the default system's root CA bundle. Use this // only if you want to replace the CA bundle the SDK uses for TLS requests. @@ -193,6 +237,12 @@ type Options struct { // to also enable this feature. CustomCABundle session option field has priority // over the AWS_CA_BUNDLE environment variable, and will be used if both are set. CustomCABundle io.Reader + + // The handlers that the session and all API clients will be created with. + // This must be a complete set of handlers. Use the defaults.Handlers() + // function to initialize this value before changing the handlers to be + // used by the SDK. + Handlers request.Handlers } // NewSessionWithOptions returns a new Session created from SDK defaults, config files, @@ -226,13 +276,20 @@ type Options struct { // })) func NewSessionWithOptions(opts Options) (*Session, error) { var envCfg envConfig + var err error if opts.SharedConfigState == SharedConfigEnable { - envCfg = loadSharedEnvConfig() + envCfg, err = loadSharedEnvConfig() + if err != nil { + return nil, fmt.Errorf("failed to load shared config, %v", err) + } } else { - envCfg = loadEnvConfig() + envCfg, err = loadEnvConfig() + if err != nil { + return nil, fmt.Errorf("failed to load environment config, %v", err) + } } - if len(opts.Profile) > 0 { + if len(opts.Profile) != 0 { envCfg.Profile = opts.Profile } @@ -243,13 +300,6 @@ func NewSessionWithOptions(opts Options) (*Session, error) { envCfg.EnableSharedConfig = true } - if len(envCfg.SharedCredentialsFile) == 0 { - envCfg.SharedCredentialsFile = defaults.SharedCredentialsFilename() - } - if len(envCfg.SharedConfigFile) == 0 { - envCfg.SharedConfigFile = defaults.SharedConfigFilename() - } - // Only use AWS_CA_BUNDLE if session option is not provided. if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil { f, err := os.Open(envCfg.CustomCABundle) @@ -302,18 +352,36 @@ func deprecatedNewSession(cfgs ...*aws.Config) *Session { } initHandlers(s) - return s } +func enableCSM(handlers *request.Handlers, cfg csmConfig, logger aws.Logger) error { + if logger != nil { + logger.Log("Enabling CSM") + } + + r, err := csm.Start(cfg.ClientID, csm.AddressWithDefaults(cfg.Host, cfg.Port)) + if err != nil { + return err + } + r.InjectHandlers(handlers) + + return nil +} + func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) { cfg := defaults.Config() - handlers := defaults.Handlers() + + handlers := opts.Handlers + if handlers.IsEmpty() { + handlers = defaults.Handlers() + } // Get a merged version of the user provided config to determine if // credentials were. userCfg := &aws.Config{} userCfg.MergeIn(cfgs...) + cfg.MergeIn(userCfg) // Ordered config files will be loaded in with later files overwriting // previous config file values. @@ -330,9 +398,17 @@ func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, } // Load additional config from file(s) - sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles) + sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles, envCfg.EnableSharedConfig) if err != nil { - return nil, err + if len(envCfg.Profile) == 0 && !envCfg.EnableSharedConfig && (envCfg.Creds.HasKeys() || userCfg.Credentials != nil) { + // Special case where the user has not explicitly specified an AWS_PROFILE, + // or session.Options.profile, shared config is not enabled, and the + // environment has credentials, allow the shared config file to fail to + // load since the user has already provided credentials, and nothing else + // is required to be read file. Github(aws/aws-sdk-go#2455) + } else if _, ok := err.(SharedConfigProfileNotExistsError); !ok { + return nil, err + } } if err := mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers, opts); err != nil { @@ -346,6 +422,17 @@ func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, initHandlers(s) + if csmCfg, err := loadCSMConfig(envCfg, cfgFiles); err != nil { + if l := s.Config.Logger; l != nil { + l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err)) + } + } else if csmCfg.Enabled { + err = enableCSM(&s.Handlers, csmCfg, s.Config.Logger) + if err != nil { + return nil, err + } + } + // Setup HTTP client with custom cert bundle if enabled if opts.CustomCABundle != nil { if err := loadCustomCABundle(s, opts.CustomCABundle); err != nil { @@ -356,6 +443,46 @@ func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, return s, nil } +type csmConfig struct { + Enabled bool + Host string + Port string + ClientID string +} + +var csmProfileName = "aws_csm" + +func loadCSMConfig(envCfg envConfig, cfgFiles []string) (csmConfig, error) { + if envCfg.CSMEnabled != nil { + if *envCfg.CSMEnabled { + return csmConfig{ + Enabled: true, + ClientID: envCfg.CSMClientID, + Host: envCfg.CSMHost, + Port: envCfg.CSMPort, + }, nil + } + return csmConfig{}, nil + } + + sharedCfg, err := loadSharedConfig(csmProfileName, cfgFiles, false) + if err != nil { + if _, ok := err.(SharedConfigProfileNotExistsError); !ok { + return csmConfig{}, err + } + } + if sharedCfg.CSMEnabled != nil && *sharedCfg.CSMEnabled == true { + return csmConfig{ + Enabled: true, + ClientID: sharedCfg.CSMClientID, + Host: sharedCfg.CSMHost, + Port: sharedCfg.CSMPort, + }, nil + } + + return csmConfig{}, nil +} + func loadCustomCABundle(s *Session, bundle io.Reader) error { var t *http.Transport switch v := s.Config.HTTPClient.Transport.(type) { @@ -368,7 +495,10 @@ func loadCustomCABundle(s *Session, bundle io.Reader) error { } } if t == nil { - t = &http.Transport{} + // Nil transport implies `http.DefaultTransport` should be used. Since + // the SDK cannot modify, nor copy the `DefaultTransport` specifying + // the values the next closest behavior. + t = getCABundleTransport() } p, err := loadCertPool(bundle) @@ -401,9 +531,11 @@ func loadCertPool(r io.Reader) (*x509.CertPool, error) { return p, nil } -func mergeConfigSrcs(cfg, userCfg *aws.Config, envCfg envConfig, sharedCfg sharedConfig, handlers request.Handlers, sessOpts Options) error { - // Merge in user provided configuration - cfg.MergeIn(userCfg) +func mergeConfigSrcs(cfg, userCfg *aws.Config, + envCfg envConfig, sharedCfg sharedConfig, + handlers request.Handlers, + sessOpts Options, +) error { // Region if not already set by user if len(aws.StringValue(cfg.Region)) == 0 { @@ -414,101 +546,67 @@ func mergeConfigSrcs(cfg, userCfg *aws.Config, envCfg envConfig, sharedCfg share } } - // Configure credentials if not already set - if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil { - if len(envCfg.Creds.AccessKeyID) > 0 { - cfg.Credentials = credentials.NewStaticCredentialsFromCreds( - envCfg.Creds, - ) - } else if envCfg.EnableSharedConfig && len(sharedCfg.AssumeRole.RoleARN) > 0 && sharedCfg.AssumeRoleSource != nil { - cfgCp := *cfg - cfgCp.Credentials = credentials.NewStaticCredentialsFromCreds( - sharedCfg.AssumeRoleSource.Creds, - ) - if len(sharedCfg.AssumeRole.MFASerial) > 0 && sessOpts.AssumeRoleTokenProvider == nil { - // AssumeRole Token provider is required if doing Assume Role - // with MFA. - return AssumeRoleTokenProviderNotSetError{} - } - cfg.Credentials = stscreds.NewCredentials( - &Session{ - Config: &cfgCp, - Handlers: handlers.Copy(), - }, - sharedCfg.AssumeRole.RoleARN, - func(opt *stscreds.AssumeRoleProvider) { - opt.RoleSessionName = sharedCfg.AssumeRole.RoleSessionName - - // Assume role with external ID - if len(sharedCfg.AssumeRole.ExternalID) > 0 { - opt.ExternalID = aws.String(sharedCfg.AssumeRole.ExternalID) - } - - // Assume role with MFA - if len(sharedCfg.AssumeRole.MFASerial) > 0 { - opt.SerialNumber = aws.String(sharedCfg.AssumeRole.MFASerial) - opt.TokenProvider = sessOpts.AssumeRoleTokenProvider - } - }, - ) - } else if len(sharedCfg.Creds.AccessKeyID) > 0 { - cfg.Credentials = credentials.NewStaticCredentialsFromCreds( - sharedCfg.Creds, - ) - } else { - // Fallback to default credentials provider, include mock errors - // for the credential chain so user can identify why credentials - // failed to be retrieved. - cfg.Credentials = credentials.NewCredentials(&credentials.ChainProvider{ - VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors), - Providers: []credentials.Provider{ - &credProviderError{Err: awserr.New("EnvAccessKeyNotFound", "failed to find credentials in the environment.", nil)}, - &credProviderError{Err: awserr.New("SharedCredsLoad", fmt.Sprintf("failed to load profile, %s.", envCfg.Profile), nil)}, - defaults.RemoteCredProvider(*cfg, handlers), - }, - }) + if cfg.EnableEndpointDiscovery == nil { + if envCfg.EnableEndpointDiscovery != nil { + cfg.WithEndpointDiscovery(*envCfg.EnableEndpointDiscovery) + } else if envCfg.EnableSharedConfig && sharedCfg.EnableEndpointDiscovery != nil { + cfg.WithEndpointDiscovery(*sharedCfg.EnableEndpointDiscovery) } } - return nil -} - -// AssumeRoleTokenProviderNotSetError is an error returned when creating a session when the -// MFAToken option is not set when shared config is configured load assume a -// role with an MFA token. -type AssumeRoleTokenProviderNotSetError struct{} - -// Code is the short id of the error. -func (e AssumeRoleTokenProviderNotSetError) Code() string { - return "AssumeRoleTokenProviderNotSetError" -} + // Regional Endpoint flag for STS endpoint resolving + mergeSTSRegionalEndpointConfig(cfg, []endpoints.STSRegionalEndpoint{ + userCfg.STSRegionalEndpoint, + envCfg.STSRegionalEndpoint, + sharedCfg.STSRegionalEndpoint, + endpoints.LegacySTSEndpoint, + }) + + // Regional Endpoint flag for S3 endpoint resolving + mergeS3UsEast1RegionalEndpointConfig(cfg, []endpoints.S3UsEast1RegionalEndpoint{ + userCfg.S3UsEast1RegionalEndpoint, + envCfg.S3UsEast1RegionalEndpoint, + sharedCfg.S3UsEast1RegionalEndpoint, + endpoints.LegacyS3UsEast1Endpoint, + }) + + // Configure credentials if not already set by the user when creating the + // Session. + if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil { + creds, err := resolveCredentials(cfg, envCfg, sharedCfg, handlers, sessOpts) + if err != nil { + return err + } + cfg.Credentials = creds + } -// Message is the description of the error -func (e AssumeRoleTokenProviderNotSetError) Message() string { - return fmt.Sprintf("assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.") -} + cfg.S3UseARNRegion = userCfg.S3UseARNRegion + if cfg.S3UseARNRegion == nil { + cfg.S3UseARNRegion = &envCfg.S3UseARNRegion + } + if cfg.S3UseARNRegion == nil { + cfg.S3UseARNRegion = &sharedCfg.S3UseARNRegion + } -// OrigErr is the underlying error that caused the failure. -func (e AssumeRoleTokenProviderNotSetError) OrigErr() error { return nil } -// Error satisfies the error interface. -func (e AssumeRoleTokenProviderNotSetError) Error() string { - return awserr.SprintError(e.Code(), e.Message(), "", nil) -} - -type credProviderError struct { - Err error +func mergeSTSRegionalEndpointConfig(cfg *aws.Config, values []endpoints.STSRegionalEndpoint) { + for _, v := range values { + if v != endpoints.UnsetSTSEndpoint { + cfg.STSRegionalEndpoint = v + break + } + } } -var emptyCreds = credentials.Value{} - -func (c credProviderError) Retrieve() (credentials.Value, error) { - return credentials.Value{}, c.Err -} -func (c credProviderError) IsExpired() bool { - return true +func mergeS3UsEast1RegionalEndpointConfig(cfg *aws.Config, values []endpoints.S3UsEast1RegionalEndpoint) { + for _, v := range values { + if v != endpoints.UnsetS3UsEast1Endpoint { + cfg.S3UsEast1RegionalEndpoint = v + break + } + } } func initHandlers(s *Session) { @@ -519,7 +617,7 @@ func initHandlers(s *Session) { } } -// Copy creates and returns a copy of the current Session, coping the config +// Copy creates and returns a copy of the current Session, copying the config // and handlers. If any additional configs are provided they will be merged // on top of the Session's copied config. // @@ -539,46 +637,67 @@ func (s *Session) Copy(cfgs ...*aws.Config) *Session { // ClientConfig satisfies the client.ConfigProvider interface and is used to // configure the service client instances. Passing the Session to the service // client's constructor (New) will use this method to configure the client. -func (s *Session) ClientConfig(serviceName string, cfgs ...*aws.Config) client.Config { - // Backwards compatibility, the error will be eaten if user calls ClientConfig - // directly. All SDK services will use ClientconfigWithError. - cfg, _ := s.clientConfigWithErr(serviceName, cfgs...) - - return cfg -} - -func (s *Session) clientConfigWithErr(serviceName string, cfgs ...*aws.Config) (client.Config, error) { +func (s *Session) ClientConfig(service string, cfgs ...*aws.Config) client.Config { s = s.Copy(cfgs...) - var resolved endpoints.ResolvedEndpoint - var err error - region := aws.StringValue(s.Config.Region) + resolved, err := s.resolveEndpoint(service, region, s.Config) + if err != nil { + s.Handlers.Validate.PushBack(func(r *request.Request) { + if len(r.ClientInfo.Endpoint) != 0 { + // Error occurred while resolving endpoint, but the request + // being invoked has had an endpoint specified after the client + // was created. + return + } + r.Error = err + }) + } - if endpoint := aws.StringValue(s.Config.Endpoint); len(endpoint) != 0 { - resolved.URL = endpoints.AddScheme(endpoint, aws.BoolValue(s.Config.DisableSSL)) - resolved.SigningRegion = region - } else { - resolved, err = s.Config.EndpointResolver.EndpointFor( - serviceName, region, - func(opt *endpoints.Options) { - opt.DisableSSL = aws.BoolValue(s.Config.DisableSSL) - opt.UseDualStack = aws.BoolValue(s.Config.UseDualStack) + return client.Config{ + Config: s.Config, + Handlers: s.Handlers, + PartitionID: resolved.PartitionID, + Endpoint: resolved.URL, + SigningRegion: resolved.SigningRegion, + SigningNameDerived: resolved.SigningNameDerived, + SigningName: resolved.SigningName, + } +} + +func (s *Session) resolveEndpoint(service, region string, cfg *aws.Config) (endpoints.ResolvedEndpoint, error) { - // Support the condition where the service is modeled but its - // endpoint metadata is not available. - opt.ResolveUnknownService = true - }, - ) + if ep := aws.StringValue(cfg.Endpoint); len(ep) != 0 { + return endpoints.ResolvedEndpoint{ + URL: endpoints.AddScheme(ep, aws.BoolValue(cfg.DisableSSL)), + SigningRegion: region, + }, nil } - return client.Config{ - Config: s.Config, - Handlers: s.Handlers, - Endpoint: resolved.URL, - SigningRegion: resolved.SigningRegion, - SigningName: resolved.SigningName, - }, err + resolved, err := cfg.EndpointResolver.EndpointFor(service, region, + func(opt *endpoints.Options) { + opt.DisableSSL = aws.BoolValue(cfg.DisableSSL) + opt.UseDualStack = aws.BoolValue(cfg.UseDualStack) + // Support for STSRegionalEndpoint where the STSRegionalEndpoint is + // provided in envConfig or sharedConfig with envConfig getting + // precedence. + opt.STSRegionalEndpoint = cfg.STSRegionalEndpoint + + // Support for S3UsEast1RegionalEndpoint where the S3UsEast1RegionalEndpoint is + // provided in envConfig or sharedConfig with envConfig getting + // precedence. + opt.S3UsEast1RegionalEndpoint = cfg.S3UsEast1RegionalEndpoint + + // Support the condition where the service is modeled but its + // endpoint metadata is not available. + opt.ResolveUnknownService = true + }, + ) + if err != nil { + return endpoints.ResolvedEndpoint{}, err + } + + return resolved, nil } // ClientConfigNoResolveEndpoint is the same as ClientConfig with the exception @@ -588,19 +707,28 @@ func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Conf s = s.Copy(cfgs...) var resolved endpoints.ResolvedEndpoint - - region := aws.StringValue(s.Config.Region) - if ep := aws.StringValue(s.Config.Endpoint); len(ep) > 0 { resolved.URL = endpoints.AddScheme(ep, aws.BoolValue(s.Config.DisableSSL)) - resolved.SigningRegion = region + resolved.SigningRegion = aws.StringValue(s.Config.Region) } return client.Config{ - Config: s.Config, - Handlers: s.Handlers, - Endpoint: resolved.URL, - SigningRegion: resolved.SigningRegion, - SigningName: resolved.SigningName, + Config: s.Config, + Handlers: s.Handlers, + Endpoint: resolved.URL, + SigningRegion: resolved.SigningRegion, + SigningNameDerived: resolved.SigningNameDerived, + SigningName: resolved.SigningName, } } + +// logDeprecatedNewSessionError function enables error handling for session +func (s *Session) logDeprecatedNewSessionError(msg string, err error, cfgs []*aws.Config) { + // Session creation failed, need to report the error and prevent + // any requests from succeeding. + s.Config.MergeIn(cfgs...) + s.Config.Logger.Log("ERROR:", msg, "Error:", err) + s.Handlers.Validate.PushBack(func(r *request.Request) { + r.Error = err + }) +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/session/shared_config.go b/vendor/github.com/aws/aws-sdk-go/aws/session/shared_config.go index 09c8e5bc7abef..a8ed880760073 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/session/shared_config.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/session/shared_config.go @@ -2,11 +2,11 @@ package session import ( "fmt" - "io/ioutil" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/go-ini/ini" + "github.com/aws/aws-sdk-go/aws/endpoints" + "github.com/aws/aws-sdk-go/internal/ini" ) const ( @@ -16,68 +16,125 @@ const ( sessionTokenKey = `aws_session_token` // optional // Assume Role Credentials group - roleArnKey = `role_arn` // group required - sourceProfileKey = `source_profile` // group required - externalIDKey = `external_id` // optional - mfaSerialKey = `mfa_serial` // optional - roleSessionNameKey = `role_session_name` // optional + roleArnKey = `role_arn` // group required + sourceProfileKey = `source_profile` // group required (or credential_source) + credentialSourceKey = `credential_source` // group required (or source_profile) + externalIDKey = `external_id` // optional + mfaSerialKey = `mfa_serial` // optional + roleSessionNameKey = `role_session_name` // optional + + // CSM options + csmEnabledKey = `csm_enabled` + csmHostKey = `csm_host` + csmPortKey = `csm_port` + csmClientIDKey = `csm_client_id` // Additional Config fields regionKey = `region` + // endpoint discovery group + enableEndpointDiscoveryKey = `endpoint_discovery_enabled` // optional + + // External Credential Process + credentialProcessKey = `credential_process` // optional + + // Web Identity Token File + webIdentityTokenFileKey = `web_identity_token_file` // optional + + // Additional config fields for regional or legacy endpoints + stsRegionalEndpointSharedKey = `sts_regional_endpoints` + + // Additional config fields for regional or legacy endpoints + s3UsEast1RegionalSharedKey = `s3_us_east_1_regional_endpoint` + // DefaultSharedConfigProfile is the default profile to be used when // loading configuration from the config files if another profile name // is not provided. DefaultSharedConfigProfile = `default` -) -type assumeRoleConfig struct { - RoleARN string - SourceProfile string - ExternalID string - MFASerial string - RoleSessionName string -} + // S3 ARN Region Usage + s3UseARNRegionKey = "s3_use_arn_region" +) // sharedConfig represents the configuration fields of the SDK config files. type sharedConfig struct { - // Credentials values from the config file. Both aws_access_key_id - // and aws_secret_access_key must be provided together in the same file - // to be considered valid. The values will be ignored if not a complete group. - // aws_session_token is an optional field that can be provided if both of the - // other two fields are also provided. + // Credentials values from the config file. Both aws_access_key_id and + // aws_secret_access_key must be provided together in the same file to be + // considered valid. The values will be ignored if not a complete group. + // aws_session_token is an optional field that can be provided if both of + // the other two fields are also provided. // // aws_access_key_id // aws_secret_access_key // aws_session_token Creds credentials.Value - AssumeRole assumeRoleConfig - AssumeRoleSource *sharedConfig + CredentialSource string + CredentialProcess string + WebIdentityTokenFile string - // Region is the region the SDK should use for looking up AWS service endpoints - // and signing requests. + RoleARN string + RoleSessionName string + ExternalID string + MFASerial string + + SourceProfileName string + SourceProfile *sharedConfig + + // Region is the region the SDK should use for looking up AWS service + // endpoints and signing requests. // // region Region string + + // EnableEndpointDiscovery can be enabled in the shared config by setting + // endpoint_discovery_enabled to true + // + // endpoint_discovery_enabled = true + EnableEndpointDiscovery *bool + + // CSM Options + CSMEnabled *bool + CSMHost string + CSMPort string + CSMClientID string + + // Specifies the Regional Endpoint flag for the SDK to resolve the endpoint for a service + // + // sts_regional_endpoints = regional + // This can take value as `LegacySTSEndpoint` or `RegionalSTSEndpoint` + STSRegionalEndpoint endpoints.STSRegionalEndpoint + + // Specifies the Regional Endpoint flag for the SDK to resolve the endpoint for a service + // + // s3_us_east_1_regional_endpoint = regional + // This can take value as `LegacyS3UsEast1Endpoint` or `RegionalS3UsEast1Endpoint` + S3UsEast1RegionalEndpoint endpoints.S3UsEast1RegionalEndpoint + + // Specifies if the S3 service should allow ARNs to direct the region + // the client's requests are sent to. + // + // s3_use_arn_region=true + S3UseARNRegion bool } type sharedConfigFile struct { Filename string - IniData *ini.File + IniData ini.Sections } -// loadSharedConfig retrieves the configuration from the list of files -// using the profile provided. The order the files are listed will determine +// loadSharedConfig retrieves the configuration from the list of files using +// the profile provided. The order the files are listed will determine // precedence. Values in subsequent files will overwrite values defined in // earlier files. // // For example, given two files A and B. Both define credentials. If the order -// of the files are A then B, B's credential values will be used instead of A's. +// of the files are A then B, B's credential values will be used instead of +// A's. // // See sharedConfig.setFromFile for information how the config files // will be loaded. -func loadSharedConfig(profile string, filenames []string) (sharedConfig, error) { +func loadSharedConfig(profile string, filenames []string, exOpts bool) (sharedConfig, error) { if len(profile) == 0 { profile = DefaultSharedConfigProfile } @@ -88,16 +145,11 @@ func loadSharedConfig(profile string, filenames []string) (sharedConfig, error) } cfg := sharedConfig{} - if err = cfg.setFromIniFiles(profile, files); err != nil { + profiles := map[string]struct{}{} + if err = cfg.setFromIniFiles(profiles, profile, files, exOpts); err != nil { return sharedConfig{}, err } - if len(cfg.AssumeRole.SourceProfile) > 0 { - if err := cfg.setAssumeRoleSource(profile, files); err != nil { - return sharedConfig{}, err - } - } - return cfg, nil } @@ -105,114 +157,278 @@ func loadSharedConfigIniFiles(filenames []string) ([]sharedConfigFile, error) { files := make([]sharedConfigFile, 0, len(filenames)) for _, filename := range filenames { - b, err := ioutil.ReadFile(filename) - if err != nil { + sections, err := ini.OpenFile(filename) + if aerr, ok := err.(awserr.Error); ok && aerr.Code() == ini.ErrCodeUnableToReadFile { // Skip files which can't be opened and read for whatever reason continue - } - - f, err := ini.Load(b) - if err != nil { + } else if err != nil { return nil, SharedConfigLoadError{Filename: filename, Err: err} } files = append(files, sharedConfigFile{ - Filename: filename, IniData: f, + Filename: filename, IniData: sections, }) } return files, nil } -func (cfg *sharedConfig) setAssumeRoleSource(origProfile string, files []sharedConfigFile) error { - var assumeRoleSrc sharedConfig +func (cfg *sharedConfig) setFromIniFiles(profiles map[string]struct{}, profile string, files []sharedConfigFile, exOpts bool) error { + // Trim files from the list that don't exist. + var skippedFiles int + var profileNotFoundErr error + for _, f := range files { + if err := cfg.setFromIniFile(profile, f, exOpts); err != nil { + if _, ok := err.(SharedConfigProfileNotExistsError); ok { + // Ignore profiles not defined in individual files. + profileNotFoundErr = err + skippedFiles++ + continue + } + return err + } + } + if skippedFiles == len(files) { + // If all files were skipped because the profile is not found, return + // the original profile not found error. + return profileNotFoundErr + } - // Multiple level assume role chains are not support - if cfg.AssumeRole.SourceProfile == origProfile { - assumeRoleSrc = *cfg - assumeRoleSrc.AssumeRole = assumeRoleConfig{} + if _, ok := profiles[profile]; ok { + // if this is the second instance of the profile the Assume Role + // options must be cleared because they are only valid for the + // first reference of a profile. The self linked instance of the + // profile only have credential provider options. + cfg.clearAssumeRoleOptions() } else { - err := assumeRoleSrc.setFromIniFiles(cfg.AssumeRole.SourceProfile, files) - if err != nil { + // First time a profile has been seen, It must either be a assume role + // or credentials. Assert if the credential type requires a role ARN, + // the ARN is also set. + if err := cfg.validateCredentialsRequireARN(profile); err != nil { return err } } + profiles[profile] = struct{}{} - if len(assumeRoleSrc.Creds.AccessKeyID) == 0 { - return SharedConfigAssumeRoleError{RoleARN: cfg.AssumeRole.RoleARN} + if err := cfg.validateCredentialType(); err != nil { + return err } - cfg.AssumeRoleSource = &assumeRoleSrc - - return nil -} + // Link source profiles for assume roles + if len(cfg.SourceProfileName) != 0 { + // Linked profile via source_profile ignore credential provider + // options, the source profile must provide the credentials. + cfg.clearCredentialOptions() -func (cfg *sharedConfig) setFromIniFiles(profile string, files []sharedConfigFile) error { - // Trim files from the list that don't exist. - for _, f := range files { - if err := cfg.setFromIniFile(profile, f); err != nil { + srcCfg := &sharedConfig{} + err := srcCfg.setFromIniFiles(profiles, cfg.SourceProfileName, files, exOpts) + if err != nil { + // SourceProfile that doesn't exist is an error in configuration. if _, ok := err.(SharedConfigProfileNotExistsError); ok { - // Ignore proviles missings - continue + err = SharedConfigAssumeRoleError{ + RoleARN: cfg.RoleARN, + SourceProfile: cfg.SourceProfileName, + } } return err } + + if !srcCfg.hasCredentials() { + return SharedConfigAssumeRoleError{ + RoleARN: cfg.RoleARN, + SourceProfile: cfg.SourceProfileName, + } + } + + cfg.SourceProfile = srcCfg } return nil } -// setFromFile loads the configuration from the file using -// the profile provided. A sharedConfig pointer type value is used so that -// multiple config file loadings can be chained. +// setFromFile loads the configuration from the file using the profile +// provided. A sharedConfig pointer type value is used so that multiple config +// file loadings can be chained. // // Only loads complete logically grouped values, and will not set fields in cfg -// for incomplete grouped values in the config. Such as credentials. For example -// if a config file only includes aws_access_key_id but no aws_secret_access_key -// the aws_access_key_id will be ignored. -func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile) error { - section, err := file.IniData.GetSection(profile) - if err != nil { +// for incomplete grouped values in the config. Such as credentials. For +// example if a config file only includes aws_access_key_id but no +// aws_secret_access_key the aws_access_key_id will be ignored. +func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile, exOpts bool) error { + section, ok := file.IniData.GetSection(profile) + if !ok { // Fallback to to alternate profile name: profile - section, err = file.IniData.GetSection(fmt.Sprintf("profile %s", profile)) - if err != nil { - return SharedConfigProfileNotExistsError{Profile: profile, Err: err} + section, ok = file.IniData.GetSection(fmt.Sprintf("profile %s", profile)) + if !ok { + return SharedConfigProfileNotExistsError{Profile: profile, Err: nil} } } - // Shared Credentials - akid := section.Key(accessKeyIDKey).String() - secret := section.Key(secretAccessKey).String() - if len(akid) > 0 && len(secret) > 0 { - cfg.Creds = credentials.Value{ - AccessKeyID: akid, - SecretAccessKey: secret, - SessionToken: section.Key(sessionTokenKey).String(), - ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", file.Filename), + if exOpts { + // Assume Role Parameters + updateString(&cfg.RoleARN, section, roleArnKey) + updateString(&cfg.ExternalID, section, externalIDKey) + updateString(&cfg.MFASerial, section, mfaSerialKey) + updateString(&cfg.RoleSessionName, section, roleSessionNameKey) + updateString(&cfg.SourceProfileName, section, sourceProfileKey) + updateString(&cfg.CredentialSource, section, credentialSourceKey) + updateString(&cfg.Region, section, regionKey) + + if v := section.String(stsRegionalEndpointSharedKey); len(v) != 0 { + sre, err := endpoints.GetSTSRegionalEndpoint(v) + if err != nil { + return fmt.Errorf("failed to load %s from shared config, %s, %v", + stsRegionalEndpointSharedKey, file.Filename, err) + } + cfg.STSRegionalEndpoint = sre } + + if v := section.String(s3UsEast1RegionalSharedKey); len(v) != 0 { + sre, err := endpoints.GetS3UsEast1RegionalEndpoint(v) + if err != nil { + return fmt.Errorf("failed to load %s from shared config, %s, %v", + s3UsEast1RegionalSharedKey, file.Filename, err) + } + cfg.S3UsEast1RegionalEndpoint = sre + } + } + + updateString(&cfg.CredentialProcess, section, credentialProcessKey) + updateString(&cfg.WebIdentityTokenFile, section, webIdentityTokenFileKey) + + // Shared Credentials + creds := credentials.Value{ + AccessKeyID: section.String(accessKeyIDKey), + SecretAccessKey: section.String(secretAccessKey), + SessionToken: section.String(sessionTokenKey), + ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", file.Filename), + } + if creds.HasKeys() { + cfg.Creds = creds + } + + // Endpoint discovery + updateBoolPtr(&cfg.EnableEndpointDiscovery, section, enableEndpointDiscoveryKey) + + // CSM options + updateBoolPtr(&cfg.CSMEnabled, section, csmEnabledKey) + updateString(&cfg.CSMHost, section, csmHostKey) + updateString(&cfg.CSMPort, section, csmPortKey) + updateString(&cfg.CSMClientID, section, csmClientIDKey) + + updateBool(&cfg.S3UseARNRegion, section, s3UseARNRegionKey) + + return nil +} + +func (cfg *sharedConfig) validateCredentialsRequireARN(profile string) error { + var credSource string + + switch { + case len(cfg.SourceProfileName) != 0: + credSource = sourceProfileKey + case len(cfg.CredentialSource) != 0: + credSource = credentialSourceKey + case len(cfg.WebIdentityTokenFile) != 0: + credSource = webIdentityTokenFileKey } - // Assume Role - roleArn := section.Key(roleArnKey).String() - srcProfile := section.Key(sourceProfileKey).String() - if len(roleArn) > 0 && len(srcProfile) > 0 { - cfg.AssumeRole = assumeRoleConfig{ - RoleARN: roleArn, - SourceProfile: srcProfile, - ExternalID: section.Key(externalIDKey).String(), - MFASerial: section.Key(mfaSerialKey).String(), - RoleSessionName: section.Key(roleSessionNameKey).String(), + if len(credSource) != 0 && len(cfg.RoleARN) == 0 { + return CredentialRequiresARNError{ + Type: credSource, + Profile: profile, } } - // Region - if v := section.Key(regionKey).String(); len(v) > 0 { - cfg.Region = v + return nil +} + +func (cfg *sharedConfig) validateCredentialType() error { + // Only one or no credential type can be defined. + if !oneOrNone( + len(cfg.SourceProfileName) != 0, + len(cfg.CredentialSource) != 0, + len(cfg.CredentialProcess) != 0, + len(cfg.WebIdentityTokenFile) != 0, + ) { + return ErrSharedConfigSourceCollision } return nil } +func (cfg *sharedConfig) hasCredentials() bool { + switch { + case len(cfg.SourceProfileName) != 0: + case len(cfg.CredentialSource) != 0: + case len(cfg.CredentialProcess) != 0: + case len(cfg.WebIdentityTokenFile) != 0: + case cfg.Creds.HasKeys(): + default: + return false + } + + return true +} + +func (cfg *sharedConfig) clearCredentialOptions() { + cfg.CredentialSource = "" + cfg.CredentialProcess = "" + cfg.WebIdentityTokenFile = "" + cfg.Creds = credentials.Value{} +} + +func (cfg *sharedConfig) clearAssumeRoleOptions() { + cfg.RoleARN = "" + cfg.ExternalID = "" + cfg.MFASerial = "" + cfg.RoleSessionName = "" + cfg.SourceProfileName = "" +} + +func oneOrNone(bs ...bool) bool { + var count int + + for _, b := range bs { + if b { + count++ + if count > 1 { + return false + } + } + } + + return true +} + +// updateString will only update the dst with the value in the section key, key +// is present in the section. +func updateString(dst *string, section ini.Section, key string) { + if !section.Has(key) { + return + } + *dst = section.String(key) +} + +// updateBool will only update the dst with the value in the section key, key +// is present in the section. +func updateBool(dst *bool, section ini.Section, key string) { + if !section.Has(key) { + return + } + *dst = section.Bool(key) +} + +// updateBoolPtr will only update the dst with the value in the section key, +// key is present in the section. +func updateBoolPtr(dst **bool, section ini.Section, key string) { + if !section.Has(key) { + return + } + *dst = new(bool) + **dst = section.Bool(key) +} + // SharedConfigLoadError is an error for the shared config file failed to load. type SharedConfigLoadError struct { Filename string @@ -270,7 +486,8 @@ func (e SharedConfigProfileNotExistsError) Error() string { // profile contains assume role information, but that information is invalid // or not complete. type SharedConfigAssumeRoleError struct { - RoleARN string + RoleARN string + SourceProfile string } // Code is the short id of the error. @@ -280,8 +497,10 @@ func (e SharedConfigAssumeRoleError) Code() string { // Message is the description of the error func (e SharedConfigAssumeRoleError) Message() string { - return fmt.Sprintf("failed to load assume role for %s, source profile has no shared credentials", - e.RoleARN) + return fmt.Sprintf( + "failed to load assume role for %s, source profile %s has no shared credentials", + e.RoleARN, e.SourceProfile, + ) } // OrigErr is the underlying error that caused the failure. @@ -293,3 +512,36 @@ func (e SharedConfigAssumeRoleError) OrigErr() error { func (e SharedConfigAssumeRoleError) Error() string { return awserr.SprintError(e.Code(), e.Message(), "", nil) } + +// CredentialRequiresARNError provides the error for shared config credentials +// that are incorrectly configured in the shared config or credentials file. +type CredentialRequiresARNError struct { + // type of credentials that were configured. + Type string + + // Profile name the credentials were in. + Profile string +} + +// Code is the short id of the error. +func (e CredentialRequiresARNError) Code() string { + return "CredentialRequiresARNError" +} + +// Message is the description of the error +func (e CredentialRequiresARNError) Message() string { + return fmt.Sprintf( + "credential type %s requires role_arn, profile %s", + e.Type, e.Profile, + ) +} + +// OrigErr is the underlying error that caused the failure. +func (e CredentialRequiresARNError) OrigErr() error { + return nil +} + +// Error satisfies the error interface. +func (e CredentialRequiresARNError) Error() string { + return awserr.SprintError(e.Code(), e.Message(), "", nil) +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/header_rules.go b/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/header_rules.go index 244c86da0543a..07ea799fbd37e 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/header_rules.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/header_rules.go @@ -1,8 +1,7 @@ package v4 import ( - "net/http" - "strings" + "github.com/aws/aws-sdk-go/internal/strings" ) // validator houses a set of rule needed for validation of a @@ -61,7 +60,7 @@ type patterns []string // been found func (p patterns) IsValid(value string) bool { for _, pattern := range p { - if strings.HasPrefix(http.CanonicalHeaderKey(value), pattern) { + if strings.HasPrefixFold(value, pattern) { return true } } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/stream.go b/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/stream.go new file mode 100644 index 0000000000000..02cbd97e23412 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/stream.go @@ -0,0 +1,63 @@ +package v4 + +import ( + "encoding/hex" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws/credentials" +) + +type credentialValueProvider interface { + Get() (credentials.Value, error) +} + +// StreamSigner implements signing of event stream encoded payloads +type StreamSigner struct { + region string + service string + + credentials credentialValueProvider + + prevSig []byte +} + +// NewStreamSigner creates a SigV4 signer used to sign Event Stream encoded messages +func NewStreamSigner(region, service string, seedSignature []byte, credentials *credentials.Credentials) *StreamSigner { + return &StreamSigner{ + region: region, + service: service, + credentials: credentials, + prevSig: seedSignature, + } +} + +// GetSignature takes an event stream encoded headers and payload and returns a signature +func (s *StreamSigner) GetSignature(headers, payload []byte, date time.Time) ([]byte, error) { + credValue, err := s.credentials.Get() + if err != nil { + return nil, err + } + + sigKey := deriveSigningKey(s.region, s.service, credValue.SecretAccessKey, date) + + keyPath := buildSigningScope(s.region, s.service, date) + + stringToSign := buildEventStreamStringToSign(headers, payload, s.prevSig, keyPath, date) + + signature := hmacSHA256(sigKey, []byte(stringToSign)) + s.prevSig = signature + + return signature, nil +} + +func buildEventStreamStringToSign(headers, payload, prevSig []byte, scope string, date time.Time) string { + return strings.Join([]string{ + "AWS4-HMAC-SHA256-PAYLOAD", + formatTime(date), + scope, + hex.EncodeToString(prevSig), + hex.EncodeToString(hashSHA256(headers)), + hex.EncodeToString(hashSHA256(payload)), + }, "\n") +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/v4.go b/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/v4.go index ccc88b4ac1c4a..b97334c7f28ef 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/v4.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/signer/v4/v4.go @@ -71,13 +71,19 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/internal/sdkio" "github.com/aws/aws-sdk-go/private/protocol/rest" ) const ( + authorizationHeader = "Authorization" + authHeaderSignatureElem = "Signature=" + signatureQueryKey = "X-Amz-Signature" + authHeaderPrefix = "AWS4-HMAC-SHA256" timeFormat = "20060102T150405Z" shortTimeFormat = "20060102" + awsV4Request = "aws4_request" // emptyStringSHA256 is a SHA256 of an empty string emptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` @@ -86,9 +92,9 @@ const ( var ignoredHeaders = rules{ blacklist{ mapRule{ - "Authorization": struct{}{}, - "User-Agent": struct{}{}, - "X-Amzn-Trace-Id": struct{}{}, + authorizationHeader: struct{}{}, + "User-Agent": struct{}{}, + "X-Amzn-Trace-Id": struct{}{}, }, }, } @@ -97,25 +103,25 @@ var ignoredHeaders = rules{ var requiredSignedHeaders = rules{ whitelist{ mapRule{ - "Cache-Control": struct{}{}, - "Content-Disposition": struct{}{}, - "Content-Encoding": struct{}{}, - "Content-Language": struct{}{}, - "Content-Md5": struct{}{}, - "Content-Type": struct{}{}, - "Expires": struct{}{}, - "If-Match": struct{}{}, - "If-Modified-Since": struct{}{}, - "If-None-Match": struct{}{}, - "If-Unmodified-Since": struct{}{}, - "Range": struct{}{}, - "X-Amz-Acl": struct{}{}, - "X-Amz-Copy-Source": struct{}{}, - "X-Amz-Copy-Source-If-Match": struct{}{}, - "X-Amz-Copy-Source-If-Modified-Since": struct{}{}, - "X-Amz-Copy-Source-If-None-Match": struct{}{}, - "X-Amz-Copy-Source-If-Unmodified-Since": struct{}{}, - "X-Amz-Copy-Source-Range": struct{}{}, + "Cache-Control": struct{}{}, + "Content-Disposition": struct{}{}, + "Content-Encoding": struct{}{}, + "Content-Language": struct{}{}, + "Content-Md5": struct{}{}, + "Content-Type": struct{}{}, + "Expires": struct{}{}, + "If-Match": struct{}{}, + "If-Modified-Since": struct{}{}, + "If-None-Match": struct{}{}, + "If-Unmodified-Since": struct{}{}, + "Range": struct{}{}, + "X-Amz-Acl": struct{}{}, + "X-Amz-Copy-Source": struct{}{}, + "X-Amz-Copy-Source-If-Match": struct{}{}, + "X-Amz-Copy-Source-If-Modified-Since": struct{}{}, + "X-Amz-Copy-Source-If-None-Match": struct{}{}, + "X-Amz-Copy-Source-If-Unmodified-Since": struct{}{}, + "X-Amz-Copy-Source-Range": struct{}{}, "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{}, "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": struct{}{}, "X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": struct{}{}, @@ -133,7 +139,9 @@ var requiredSignedHeaders = rules{ "X-Amz-Server-Side-Encryption-Customer-Key": struct{}{}, "X-Amz-Server-Side-Encryption-Customer-Key-Md5": struct{}{}, "X-Amz-Storage-Class": struct{}{}, + "X-Amz-Tagging": struct{}{}, "X-Amz-Website-Redirect-Location": struct{}{}, + "X-Amz-Content-Sha256": struct{}{}, }, }, patterns{"X-Amz-Meta-"}, @@ -179,7 +187,7 @@ type Signer struct { // http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html DisableURIPathEscaping bool - // Disales the automatical setting of the HTTP request's Body field with the + // Disables the automatical setting of the HTTP request's Body field with the // io.ReadSeeker passed in to the signer. This is useful if you're using a // custom wrapper around the body for the io.ReadSeeker and want to preserve // the Body value on the Request.Body. @@ -226,11 +234,9 @@ type signingCtx struct { DisableURIPathEscaping bool - credValues credentials.Value - isPresign bool - formattedTime string - formattedShortTime string - unsignedPayload bool + credValues credentials.Value + isPresign bool + unsignedPayload bool bodyDigest string signedHeaders string @@ -341,7 +347,9 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi ctx.sanitizeHostForHeader() ctx.assignAmzQueryValues() - ctx.build(v4.DisableHeaderHoisting) + if err := ctx.build(v4.DisableHeaderHoisting); err != nil { + return nil, err + } // If the request is not presigned the body should be attached to it. This // prevents the confusion of wanting to send a signed request without @@ -417,7 +425,7 @@ var SignRequestHandler = request.NamedHandler{ // If the credentials of the request's config are set to // credentials.AnonymousCredentials the request will not be signed. func SignSDKRequest(req *request.Request) { - signSDKRequestWithCurrTime(req, time.Now) + SignSDKRequestWithCurrentTime(req, time.Now) } // BuildNamedHandler will build a generic handler for signing. @@ -425,12 +433,15 @@ func BuildNamedHandler(name string, opts ...func(*Signer)) request.NamedHandler return request.NamedHandler{ Name: name, Fn: func(req *request.Request) { - signSDKRequestWithCurrTime(req, time.Now, opts...) + SignSDKRequestWithCurrentTime(req, time.Now, opts...) }, } } -func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer)) { +// SignSDKRequestWithCurrentTime will sign the SDK's request using the time +// function passed in. Behaves the same as SignSDKRequest with the exception +// the request is signed with the value returned by the current time function. +func SignSDKRequestWithCurrentTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer)) { // If the request does not need to be signed ignore the signing of the // request if the AnonymousCredentials object is used. if req.Config.Credentials == credentials.AnonymousCredentials { @@ -466,13 +477,9 @@ func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time opt(v4) } - signingTime := req.Time - if !req.LastSignedAt.IsZero() { - signingTime = req.LastSignedAt - } - + curTime := curTimeFn() signedHeaders, err := v4.signWithBody(req.HTTPRequest, req.GetBody(), - name, region, req.ExpireTime, req.ExpireTime > 0, signingTime, + name, region, req.ExpireTime, req.ExpireTime > 0, curTime, ) if err != nil { req.Error = err @@ -481,7 +488,7 @@ func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time } req.SignedHeaderVals = signedHeaders - req.LastSignedAt = curTimeFn() + req.LastSignedAt = curTime } const logSignInfoMsg = `DEBUG: Request Signature: @@ -503,11 +510,13 @@ func (v4 *Signer) logSigningInfo(ctx *signingCtx) { v4.Logger.Log(msg) } -func (ctx *signingCtx) build(disableHeaderHoisting bool) { +func (ctx *signingCtx) build(disableHeaderHoisting bool) error { ctx.buildTime() // no depends ctx.buildCredentialString() // no depends - ctx.buildBodyDigest() + if err := ctx.buildBodyDigest(); err != nil { + return err + } unsignedHeaders := ctx.Request.Header if ctx.isPresign { @@ -526,37 +535,56 @@ func (ctx *signingCtx) build(disableHeaderHoisting bool) { ctx.buildSignature() // depends on string to sign if ctx.isPresign { - ctx.Request.URL.RawQuery += "&X-Amz-Signature=" + ctx.signature + ctx.Request.URL.RawQuery += "&" + signatureQueryKey + "=" + ctx.signature } else { parts := []string{ authHeaderPrefix + " Credential=" + ctx.credValues.AccessKeyID + "/" + ctx.credentialString, "SignedHeaders=" + ctx.signedHeaders, - "Signature=" + ctx.signature, + authHeaderSignatureElem + ctx.signature, } - ctx.Request.Header.Set("Authorization", strings.Join(parts, ", ")) + ctx.Request.Header.Set(authorizationHeader, strings.Join(parts, ", ")) } + + return nil } -func (ctx *signingCtx) buildTime() { - ctx.formattedTime = ctx.Time.UTC().Format(timeFormat) - ctx.formattedShortTime = ctx.Time.UTC().Format(shortTimeFormat) +// GetSignedRequestSignature attempts to extract the signature of the request. +// Returning an error if the request is unsigned, or unable to extract the +// signature. +func GetSignedRequestSignature(r *http.Request) ([]byte, error) { + + if auth := r.Header.Get(authorizationHeader); len(auth) != 0 { + ps := strings.Split(auth, ", ") + for _, p := range ps { + if idx := strings.Index(p, authHeaderSignatureElem); idx >= 0 { + sig := p[len(authHeaderSignatureElem):] + if len(sig) == 0 { + return nil, fmt.Errorf("invalid request signature authorization header") + } + return hex.DecodeString(sig) + } + } + } + + if sig := r.URL.Query().Get("X-Amz-Signature"); len(sig) != 0 { + return hex.DecodeString(sig) + } + + return nil, fmt.Errorf("request not signed") +} +func (ctx *signingCtx) buildTime() { if ctx.isPresign { duration := int64(ctx.ExpireTime / time.Second) - ctx.Query.Set("X-Amz-Date", ctx.formattedTime) + ctx.Query.Set("X-Amz-Date", formatTime(ctx.Time)) ctx.Query.Set("X-Amz-Expires", strconv.FormatInt(duration, 10)) } else { - ctx.Request.Header.Set("X-Amz-Date", ctx.formattedTime) + ctx.Request.Header.Set("X-Amz-Date", formatTime(ctx.Time)) } } func (ctx *signingCtx) buildCredentialString() { - ctx.credentialString = strings.Join([]string{ - ctx.formattedShortTime, - ctx.Region, - ctx.ServiceName, - "aws4_request", - }, "/") + ctx.credentialString = buildSigningScope(ctx.Region, ctx.ServiceName, ctx.Time) if ctx.isPresign { ctx.Query.Set("X-Amz-Credential", ctx.credValues.AccessKeyID+"/"+ctx.credentialString) @@ -580,8 +608,7 @@ func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) { var headers []string headers = append(headers, "host") for k, v := range header { - canonicalKey := http.CanonicalHeaderKey(k) - if !r.IsValid(canonicalKey) { + if !r.IsValid(k) { continue // ignored header } if ctx.SignedHeaderVals == nil { @@ -645,37 +672,50 @@ func (ctx *signingCtx) buildCanonicalString() { func (ctx *signingCtx) buildStringToSign() { ctx.stringToSign = strings.Join([]string{ authHeaderPrefix, - ctx.formattedTime, + formatTime(ctx.Time), ctx.credentialString, - hex.EncodeToString(makeSha256([]byte(ctx.canonicalString))), + hex.EncodeToString(hashSHA256([]byte(ctx.canonicalString))), }, "\n") } func (ctx *signingCtx) buildSignature() { - secret := ctx.credValues.SecretAccessKey - date := makeHmac([]byte("AWS4"+secret), []byte(ctx.formattedShortTime)) - region := makeHmac(date, []byte(ctx.Region)) - service := makeHmac(region, []byte(ctx.ServiceName)) - credentials := makeHmac(service, []byte("aws4_request")) - signature := makeHmac(credentials, []byte(ctx.stringToSign)) + creds := deriveSigningKey(ctx.Region, ctx.ServiceName, ctx.credValues.SecretAccessKey, ctx.Time) + signature := hmacSHA256(creds, []byte(ctx.stringToSign)) ctx.signature = hex.EncodeToString(signature) } -func (ctx *signingCtx) buildBodyDigest() { +func (ctx *signingCtx) buildBodyDigest() error { hash := ctx.Request.Header.Get("X-Amz-Content-Sha256") if hash == "" { - if ctx.unsignedPayload || (ctx.isPresign && ctx.ServiceName == "s3") { + includeSHA256Header := ctx.unsignedPayload || + ctx.ServiceName == "s3" || + ctx.ServiceName == "glacier" + + s3Presign := ctx.isPresign && ctx.ServiceName == "s3" + + if ctx.unsignedPayload || s3Presign { hash = "UNSIGNED-PAYLOAD" + includeSHA256Header = !s3Presign } else if ctx.Body == nil { hash = emptyStringSHA256 } else { - hash = hex.EncodeToString(makeSha256Reader(ctx.Body)) + if !aws.IsReaderSeekable(ctx.Body) { + return fmt.Errorf("cannot use unseekable request body %T, for signed request with body", ctx.Body) + } + hashBytes, err := makeSha256Reader(ctx.Body) + if err != nil { + return err + } + hash = hex.EncodeToString(hashBytes) } - if ctx.unsignedPayload || ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" { + + if includeSHA256Header { ctx.Request.Header.Set("X-Amz-Content-Sha256", hash) } } ctx.bodyDigest = hash + + return nil } // isRequestSigned returns if the request is currently signed or presigned @@ -701,31 +741,45 @@ func (ctx *signingCtx) removePresign() { ctx.Query.Del("X-Amz-SignedHeaders") } -func makeHmac(key []byte, data []byte) []byte { +func hmacSHA256(key []byte, data []byte) []byte { hash := hmac.New(sha256.New, key) hash.Write(data) return hash.Sum(nil) } -func makeSha256(data []byte) []byte { +func hashSHA256(data []byte) []byte { hash := sha256.New() hash.Write(data) return hash.Sum(nil) } -func makeSha256Reader(reader io.ReadSeeker) []byte { +func makeSha256Reader(reader io.ReadSeeker) (hashBytes []byte, err error) { hash := sha256.New() - start, _ := reader.Seek(0, 1) - defer reader.Seek(start, 0) + start, err := reader.Seek(0, sdkio.SeekCurrent) + if err != nil { + return nil, err + } + defer func() { + // ensure error is return if unable to seek back to start of payload. + _, err = reader.Seek(start, sdkio.SeekStart) + }() - io.Copy(hash, reader) - return hash.Sum(nil) + // Use CopyN to avoid allocating the 32KB buffer in io.Copy for bodies + // smaller than 32KB. Fall back to io.Copy if we fail to determine the size. + size, err := aws.SeekerLen(reader) + if err != nil { + io.Copy(hash, reader) + } else { + io.CopyN(hash, reader, size) + } + + return hash.Sum(nil), nil } const doubleSpace = " " // stripExcessSpaces will rewrite the passed in slice's string values to not -// contain muliple side-by-side spaces. +// contain multiple side-by-side spaces. func stripExcessSpaces(vals []string) { var j, k, l, m, spaces int for i, str := range vals { @@ -765,3 +819,28 @@ func stripExcessSpaces(vals []string) { vals[i] = string(buf[:m]) } } + +func buildSigningScope(region, service string, dt time.Time) string { + return strings.Join([]string{ + formatShortTime(dt), + region, + service, + awsV4Request, + }, "/") +} + +func deriveSigningKey(region, service, secretKey string, dt time.Time) []byte { + kDate := hmacSHA256([]byte("AWS4"+secretKey), []byte(formatShortTime(dt))) + kRegion := hmacSHA256(kDate, []byte(region)) + kService := hmacSHA256(kRegion, []byte(service)) + signingKey := hmacSHA256(kService, []byte(awsV4Request)) + return signingKey +} + +func formatShortTime(dt time.Time) string { + return dt.UTC().Format(shortTimeFormat) +} + +func formatTime(dt time.Time) string { + return dt.UTC().Format(timeFormat) +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/types.go b/vendor/github.com/aws/aws-sdk-go/aws/types.go index 0e2d864e10a8e..d542ef01bc89d 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/types.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/types.go @@ -2,16 +2,24 @@ package aws import ( "io" + "strings" "sync" + + "github.com/aws/aws-sdk-go/internal/sdkio" ) -// ReadSeekCloser wraps a io.Reader returning a ReaderSeekerCloser. Should -// only be used with an io.Reader that is also an io.Seeker. Doing so may -// cause request signature errors, or request body's not sent for GET, HEAD -// and DELETE HTTP methods. +// ReadSeekCloser wraps a io.Reader returning a ReaderSeekerCloser. Allows the +// SDK to accept an io.Reader that is not also an io.Seeker for unsigned +// streaming payload API operations. +// +// A ReadSeekCloser wrapping an nonseekable io.Reader used in an API +// operation's input will prevent that operation being retried in the case of +// network errors, and cause operation requests to fail if the operation +// requires payload signing. // -// Deprecated: Should only be used with io.ReadSeeker. If using for -// S3 PutObject to stream content use s3manager.Uploader instead. +// Note: If using With S3 PutObject to stream an object upload The SDK's S3 +// Upload manager (s3manager.Uploader) provides support for streaming with the +// ability to retry network errors. func ReadSeekCloser(r io.Reader) ReaderSeekerCloser { return ReaderSeekerCloser{r} } @@ -22,10 +30,27 @@ type ReaderSeekerCloser struct { r io.Reader } +// IsReaderSeekable returns if the underlying reader type can be seeked. A +// io.Reader might not actually be seekable if it is the ReaderSeekerCloser +// type. +func IsReaderSeekable(r io.Reader) bool { + switch v := r.(type) { + case ReaderSeekerCloser: + return v.IsSeeker() + case *ReaderSeekerCloser: + return v.IsSeeker() + case io.ReadSeeker: + return true + default: + return false + } +} + // Read reads from the reader up to size of p. The number of bytes read, and // error if it occurred will be returned. // -// If the reader is not an io.Reader zero bytes read, and nil error will be returned. +// If the reader is not an io.Reader zero bytes read, and nil error will be +// returned. // // Performs the same functionality as io.Reader Read func (r ReaderSeekerCloser) Read(p []byte) (int, error) { @@ -56,6 +81,71 @@ func (r ReaderSeekerCloser) IsSeeker() bool { return ok } +// HasLen returns the length of the underlying reader if the value implements +// the Len() int method. +func (r ReaderSeekerCloser) HasLen() (int, bool) { + type lenner interface { + Len() int + } + + if lr, ok := r.r.(lenner); ok { + return lr.Len(), true + } + + return 0, false +} + +// GetLen returns the length of the bytes remaining in the underlying reader. +// Checks first for Len(), then io.Seeker to determine the size of the +// underlying reader. +// +// Will return -1 if the length cannot be determined. +func (r ReaderSeekerCloser) GetLen() (int64, error) { + if l, ok := r.HasLen(); ok { + return int64(l), nil + } + + if s, ok := r.r.(io.Seeker); ok { + return seekerLen(s) + } + + return -1, nil +} + +// SeekerLen attempts to get the number of bytes remaining at the seeker's +// current position. Returns the number of bytes remaining or error. +func SeekerLen(s io.Seeker) (int64, error) { + // Determine if the seeker is actually seekable. ReaderSeekerCloser + // hides the fact that a io.Readers might not actually be seekable. + switch v := s.(type) { + case ReaderSeekerCloser: + return v.GetLen() + case *ReaderSeekerCloser: + return v.GetLen() + } + + return seekerLen(s) +} + +func seekerLen(s io.Seeker) (int64, error) { + curOffset, err := s.Seek(0, sdkio.SeekCurrent) + if err != nil { + return 0, err + } + + endOffset, err := s.Seek(0, sdkio.SeekEnd) + if err != nil { + return 0, err + } + + _, err = s.Seek(curOffset, sdkio.SeekStart) + if err != nil { + return 0, err + } + + return endOffset - curOffset, nil +} + // Close closes the ReaderSeekerCloser. // // If the ReaderSeekerCloser is not an io.Closer nothing will be done. @@ -116,3 +206,36 @@ func (b *WriteAtBuffer) Bytes() []byte { defer b.m.Unlock() return b.buf } + +// MultiCloser is a utility to close multiple io.Closers within a single +// statement. +type MultiCloser []io.Closer + +// Close closes all of the io.Closers making up the MultiClosers. Any +// errors that occur while closing will be returned in the order they +// occur. +func (m MultiCloser) Close() error { + var errs errors + for _, c := range m { + err := c.Close() + if err != nil { + errs = append(errs, err) + } + } + if len(errs) != 0 { + return errs + } + + return nil +} + +type errors []error + +func (es errors) Error() string { + var parts []string + for _, e := range es { + parts = append(parts, e.Error()) + } + + return strings.Join(parts, "\n") +} diff --git a/vendor/github.com/aws/aws-sdk-go/aws/version.go b/vendor/github.com/aws/aws-sdk-go/aws/version.go index 7bd32f3d66be5..34d4d1ee62ca5 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.12.66" +const SDKVersion = "1.28.11" diff --git a/vendor/github.com/aws/aws-sdk-go/go.mod b/vendor/github.com/aws/aws-sdk-go/go.mod new file mode 100644 index 0000000000000..329c3ea439116 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/go.mod @@ -0,0 +1,3 @@ +module github.com/aws/aws-sdk-go + +require github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/ast.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/ast.go new file mode 100644 index 0000000000000..e83a99886bccd --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/ast.go @@ -0,0 +1,120 @@ +package ini + +// ASTKind represents different states in the parse table +// and the type of AST that is being constructed +type ASTKind int + +// ASTKind* is used in the parse table to transition between +// the different states +const ( + ASTKindNone = ASTKind(iota) + ASTKindStart + ASTKindExpr + ASTKindEqualExpr + ASTKindStatement + ASTKindSkipStatement + ASTKindExprStatement + ASTKindSectionStatement + ASTKindNestedSectionStatement + ASTKindCompletedNestedSectionStatement + ASTKindCommentStatement + ASTKindCompletedSectionStatement +) + +func (k ASTKind) String() string { + switch k { + case ASTKindNone: + return "none" + case ASTKindStart: + return "start" + case ASTKindExpr: + return "expr" + case ASTKindStatement: + return "stmt" + case ASTKindSectionStatement: + return "section_stmt" + case ASTKindExprStatement: + return "expr_stmt" + case ASTKindCommentStatement: + return "comment" + case ASTKindNestedSectionStatement: + return "nested_section_stmt" + case ASTKindCompletedSectionStatement: + return "completed_stmt" + case ASTKindSkipStatement: + return "skip" + default: + return "" + } +} + +// AST interface allows us to determine what kind of node we +// are on and casting may not need to be necessary. +// +// The root is always the first node in Children +type AST struct { + Kind ASTKind + Root Token + RootToken bool + Children []AST +} + +func newAST(kind ASTKind, root AST, children ...AST) AST { + return AST{ + Kind: kind, + Children: append([]AST{root}, children...), + } +} + +func newASTWithRootToken(kind ASTKind, root Token, children ...AST) AST { + return AST{ + Kind: kind, + Root: root, + RootToken: true, + Children: children, + } +} + +// AppendChild will append to the list of children an AST has. +func (a *AST) AppendChild(child AST) { + a.Children = append(a.Children, child) +} + +// GetRoot will return the root AST which can be the first entry +// in the children list or a token. +func (a *AST) GetRoot() AST { + if a.RootToken { + return *a + } + + if len(a.Children) == 0 { + return AST{} + } + + return a.Children[0] +} + +// GetChildren will return the current AST's list of children +func (a *AST) GetChildren() []AST { + if len(a.Children) == 0 { + return []AST{} + } + + if a.RootToken { + return a.Children + } + + return a.Children[1:] +} + +// SetChildren will set and override all children of the AST. +func (a *AST) SetChildren(children []AST) { + if a.RootToken { + a.Children = children + } else { + a.Children = append(a.Children[:1], children...) + } +} + +// Start is used to indicate the starting state of the parse table. +var Start = newAST(ASTKindStart, AST{}) diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/comma_token.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/comma_token.go new file mode 100644 index 0000000000000..0895d53cbe656 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/comma_token.go @@ -0,0 +1,11 @@ +package ini + +var commaRunes = []rune(",") + +func isComma(b rune) bool { + return b == ',' +} + +func newCommaToken() Token { + return newToken(TokenComma, commaRunes, NoneType) +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/comment_token.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/comment_token.go new file mode 100644 index 0000000000000..0b76999ba1f37 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/comment_token.go @@ -0,0 +1,35 @@ +package ini + +// isComment will return whether or not the next byte(s) is a +// comment. +func isComment(b []rune) bool { + if len(b) == 0 { + return false + } + + switch b[0] { + case ';': + return true + case '#': + return true + } + + return false +} + +// newCommentToken will create a comment token and +// return how many bytes were read. +func newCommentToken(b []rune) (Token, int, error) { + i := 0 + for ; i < len(b); i++ { + if b[i] == '\n' { + break + } + + if len(b)-i > 2 && b[i] == '\r' && b[i+1] == '\n' { + break + } + } + + return newToken(TokenComment, b[:i], NoneType), i, nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/doc.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/doc.go new file mode 100644 index 0000000000000..25ce0fe134deb --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/doc.go @@ -0,0 +1,29 @@ +// Package ini is an LL(1) parser for configuration files. +// +// Example: +// sections, err := ini.OpenFile("/path/to/file") +// if err != nil { +// panic(err) +// } +// +// profile := "foo" +// section, ok := sections.GetSection(profile) +// if !ok { +// fmt.Printf("section %q could not be found", profile) +// } +// +// Below is the BNF that describes this parser +// Grammar: +// stmt -> value stmt' +// stmt' -> epsilon | op stmt +// value -> number | string | boolean | quoted_string +// +// section -> [ section' +// section' -> value section_close +// section_close -> ] +// +// SkipState will skip (NL WS)+ +// +// comment -> # comment' | ; comment' +// comment' -> epsilon | value +package ini diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/empty_token.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/empty_token.go new file mode 100644 index 0000000000000..04345a54c20d5 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/empty_token.go @@ -0,0 +1,4 @@ +package ini + +// emptyToken is used to satisfy the Token interface +var emptyToken = newToken(TokenNone, []rune{}, NoneType) diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/expression.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/expression.go new file mode 100644 index 0000000000000..91ba2a59dd5e7 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/expression.go @@ -0,0 +1,24 @@ +package ini + +// newExpression will return an expression AST. +// Expr represents an expression +// +// grammar: +// expr -> string | number +func newExpression(tok Token) AST { + return newASTWithRootToken(ASTKindExpr, tok) +} + +func newEqualExpr(left AST, tok Token) AST { + return newASTWithRootToken(ASTKindEqualExpr, tok, left) +} + +// EqualExprKey will return a LHS value in the equal expr +func EqualExprKey(ast AST) string { + children := ast.GetChildren() + if len(children) == 0 || ast.Kind != ASTKindEqualExpr { + return "" + } + + return string(children[0].Root.Raw()) +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/fuzz.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/fuzz.go new file mode 100644 index 0000000000000..8d462f77e24f7 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/fuzz.go @@ -0,0 +1,17 @@ +// +build gofuzz + +package ini + +import ( + "bytes" +) + +func Fuzz(data []byte) int { + b := bytes.NewReader(data) + + if _, err := Parse(b); err != nil { + return 0 + } + + return 1 +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/ini.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/ini.go new file mode 100644 index 0000000000000..3b0ca7afe3b25 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/ini.go @@ -0,0 +1,51 @@ +package ini + +import ( + "io" + "os" + + "github.com/aws/aws-sdk-go/aws/awserr" +) + +// OpenFile takes a path to a given file, and will open and parse +// that file. +func OpenFile(path string) (Sections, error) { + f, err := os.Open(path) + if err != nil { + return Sections{}, awserr.New(ErrCodeUnableToReadFile, "unable to open file", err) + } + defer f.Close() + + return Parse(f) +} + +// Parse will parse the given file using the shared config +// visitor. +func Parse(f io.Reader) (Sections, error) { + tree, err := ParseAST(f) + if err != nil { + return Sections{}, err + } + + v := NewDefaultVisitor() + if err = Walk(tree, v); err != nil { + return Sections{}, err + } + + return v.Sections, nil +} + +// ParseBytes will parse the given bytes and return the parsed sections. +func ParseBytes(b []byte) (Sections, error) { + tree, err := ParseASTBytes(b) + if err != nil { + return Sections{}, err + } + + v := NewDefaultVisitor() + if err = Walk(tree, v); err != nil { + return Sections{}, err + } + + return v.Sections, nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/ini_lexer.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/ini_lexer.go new file mode 100644 index 0000000000000..582c024ad1583 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/ini_lexer.go @@ -0,0 +1,165 @@ +package ini + +import ( + "bytes" + "io" + "io/ioutil" + + "github.com/aws/aws-sdk-go/aws/awserr" +) + +const ( + // ErrCodeUnableToReadFile is used when a file is failed to be + // opened or read from. + ErrCodeUnableToReadFile = "FailedRead" +) + +// TokenType represents the various different tokens types +type TokenType int + +func (t TokenType) String() string { + switch t { + case TokenNone: + return "none" + case TokenLit: + return "literal" + case TokenSep: + return "sep" + case TokenOp: + return "op" + case TokenWS: + return "ws" + case TokenNL: + return "newline" + case TokenComment: + return "comment" + case TokenComma: + return "comma" + default: + return "" + } +} + +// TokenType enums +const ( + TokenNone = TokenType(iota) + TokenLit + TokenSep + TokenComma + TokenOp + TokenWS + TokenNL + TokenComment +) + +type iniLexer struct{} + +// Tokenize will return a list of tokens during lexical analysis of the +// io.Reader. +func (l *iniLexer) Tokenize(r io.Reader) ([]Token, error) { + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, awserr.New(ErrCodeUnableToReadFile, "unable to read file", err) + } + + return l.tokenize(b) +} + +func (l *iniLexer) tokenize(b []byte) ([]Token, error) { + runes := bytes.Runes(b) + var err error + n := 0 + tokenAmount := countTokens(runes) + tokens := make([]Token, tokenAmount) + count := 0 + + for len(runes) > 0 && count < tokenAmount { + switch { + case isWhitespace(runes[0]): + tokens[count], n, err = newWSToken(runes) + case isComma(runes[0]): + tokens[count], n = newCommaToken(), 1 + case isComment(runes): + tokens[count], n, err = newCommentToken(runes) + case isNewline(runes): + tokens[count], n, err = newNewlineToken(runes) + case isSep(runes): + tokens[count], n, err = newSepToken(runes) + case isOp(runes): + tokens[count], n, err = newOpToken(runes) + default: + tokens[count], n, err = newLitToken(runes) + } + + if err != nil { + return nil, err + } + + count++ + + runes = runes[n:] + } + + return tokens[:count], nil +} + +func countTokens(runes []rune) int { + count, n := 0, 0 + var err error + + for len(runes) > 0 { + switch { + case isWhitespace(runes[0]): + _, n, err = newWSToken(runes) + case isComma(runes[0]): + _, n = newCommaToken(), 1 + case isComment(runes): + _, n, err = newCommentToken(runes) + case isNewline(runes): + _, n, err = newNewlineToken(runes) + case isSep(runes): + _, n, err = newSepToken(runes) + case isOp(runes): + _, n, err = newOpToken(runes) + default: + _, n, err = newLitToken(runes) + } + + if err != nil { + return 0 + } + + count++ + runes = runes[n:] + } + + return count + 1 +} + +// Token indicates a metadata about a given value. +type Token struct { + t TokenType + ValueType ValueType + base int + raw []rune +} + +var emptyValue = Value{} + +func newToken(t TokenType, raw []rune, v ValueType) Token { + return Token{ + t: t, + raw: raw, + ValueType: v, + } +} + +// Raw return the raw runes that were consumed +func (tok Token) Raw() []rune { + return tok.raw +} + +// Type returns the token type +func (tok Token) Type() TokenType { + return tok.t +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/ini_parser.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/ini_parser.go new file mode 100644 index 0000000000000..cf9fad81e704d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/ini_parser.go @@ -0,0 +1,356 @@ +package ini + +import ( + "fmt" + "io" +) + +// State enums for the parse table +const ( + InvalidState = iota + // stmt -> value stmt' + StatementState + // stmt' -> MarkComplete | op stmt + StatementPrimeState + // value -> number | string | boolean | quoted_string + ValueState + // section -> [ section' + OpenScopeState + // section' -> value section_close + SectionState + // section_close -> ] + CloseScopeState + // SkipState will skip (NL WS)+ + SkipState + // SkipTokenState will skip any token and push the previous + // state onto the stack. + SkipTokenState + // comment -> # comment' | ; comment' + // comment' -> MarkComplete | value + CommentState + // MarkComplete state will complete statements and move that + // to the completed AST list + MarkCompleteState + // TerminalState signifies that the tokens have been fully parsed + TerminalState +) + +// parseTable is a state machine to dictate the grammar above. +var parseTable = map[ASTKind]map[TokenType]int{ + ASTKindStart: map[TokenType]int{ + TokenLit: StatementState, + TokenSep: OpenScopeState, + TokenWS: SkipTokenState, + TokenNL: SkipTokenState, + TokenComment: CommentState, + TokenNone: TerminalState, + }, + ASTKindCommentStatement: map[TokenType]int{ + TokenLit: StatementState, + TokenSep: OpenScopeState, + TokenWS: SkipTokenState, + TokenNL: SkipTokenState, + TokenComment: CommentState, + TokenNone: MarkCompleteState, + }, + ASTKindExpr: map[TokenType]int{ + TokenOp: StatementPrimeState, + TokenLit: ValueState, + TokenSep: OpenScopeState, + TokenWS: ValueState, + TokenNL: SkipState, + TokenComment: CommentState, + TokenNone: MarkCompleteState, + }, + ASTKindEqualExpr: map[TokenType]int{ + TokenLit: ValueState, + TokenWS: SkipTokenState, + TokenNL: SkipState, + }, + ASTKindStatement: map[TokenType]int{ + TokenLit: SectionState, + TokenSep: CloseScopeState, + TokenWS: SkipTokenState, + TokenNL: SkipTokenState, + TokenComment: CommentState, + TokenNone: MarkCompleteState, + }, + ASTKindExprStatement: map[TokenType]int{ + TokenLit: ValueState, + TokenSep: OpenScopeState, + TokenOp: ValueState, + TokenWS: ValueState, + TokenNL: MarkCompleteState, + TokenComment: CommentState, + TokenNone: TerminalState, + TokenComma: SkipState, + }, + ASTKindSectionStatement: map[TokenType]int{ + TokenLit: SectionState, + TokenOp: SectionState, + TokenSep: CloseScopeState, + TokenWS: SectionState, + TokenNL: SkipTokenState, + }, + ASTKindCompletedSectionStatement: map[TokenType]int{ + TokenWS: SkipTokenState, + TokenNL: SkipTokenState, + TokenLit: StatementState, + TokenSep: OpenScopeState, + TokenComment: CommentState, + TokenNone: MarkCompleteState, + }, + ASTKindSkipStatement: map[TokenType]int{ + TokenLit: StatementState, + TokenSep: OpenScopeState, + TokenWS: SkipTokenState, + TokenNL: SkipTokenState, + TokenComment: CommentState, + TokenNone: TerminalState, + }, +} + +// ParseAST will parse input from an io.Reader using +// an LL(1) parser. +func ParseAST(r io.Reader) ([]AST, error) { + lexer := iniLexer{} + tokens, err := lexer.Tokenize(r) + if err != nil { + return []AST{}, err + } + + return parse(tokens) +} + +// ParseASTBytes will parse input from a byte slice using +// an LL(1) parser. +func ParseASTBytes(b []byte) ([]AST, error) { + lexer := iniLexer{} + tokens, err := lexer.tokenize(b) + if err != nil { + return []AST{}, err + } + + return parse(tokens) +} + +func parse(tokens []Token) ([]AST, error) { + start := Start + stack := newParseStack(3, len(tokens)) + + stack.Push(start) + s := newSkipper() + +loop: + for stack.Len() > 0 { + k := stack.Pop() + + var tok Token + if len(tokens) == 0 { + // this occurs when all the tokens have been processed + // but reduction of what's left on the stack needs to + // occur. + tok = emptyToken + } else { + tok = tokens[0] + } + + step := parseTable[k.Kind][tok.Type()] + if s.ShouldSkip(tok) { + // being in a skip state with no tokens will break out of + // the parse loop since there is nothing left to process. + if len(tokens) == 0 { + break loop + } + // if should skip is true, we skip the tokens until should skip is set to false. + step = SkipTokenState + } + + switch step { + case TerminalState: + // Finished parsing. Push what should be the last + // statement to the stack. If there is anything left + // on the stack, an error in parsing has occurred. + if k.Kind != ASTKindStart { + stack.MarkComplete(k) + } + break loop + case SkipTokenState: + // When skipping a token, the previous state was popped off the stack. + // To maintain the correct state, the previous state will be pushed + // onto the stack. + stack.Push(k) + case StatementState: + if k.Kind != ASTKindStart { + stack.MarkComplete(k) + } + expr := newExpression(tok) + stack.Push(expr) + case StatementPrimeState: + if tok.Type() != TokenOp { + stack.MarkComplete(k) + continue + } + + if k.Kind != ASTKindExpr { + return nil, NewParseError( + fmt.Sprintf("invalid expression: expected Expr type, but found %T type", k), + ) + } + + k = trimSpaces(k) + expr := newEqualExpr(k, tok) + stack.Push(expr) + case ValueState: + // ValueState requires the previous state to either be an equal expression + // or an expression statement. + // + // This grammar occurs when the RHS is a number, word, or quoted string. + // equal_expr -> lit op equal_expr' + // equal_expr' -> number | string | quoted_string + // quoted_string -> " quoted_string' + // quoted_string' -> string quoted_string_end + // quoted_string_end -> " + // + // otherwise + // expr_stmt -> equal_expr (expr_stmt')* + // expr_stmt' -> ws S | op S | MarkComplete + // S -> equal_expr' expr_stmt' + switch k.Kind { + case ASTKindEqualExpr: + // assigning a value to some key + k.AppendChild(newExpression(tok)) + stack.Push(newExprStatement(k)) + case ASTKindExpr: + k.Root.raw = append(k.Root.raw, tok.Raw()...) + stack.Push(k) + case ASTKindExprStatement: + root := k.GetRoot() + children := root.GetChildren() + if len(children) == 0 { + return nil, NewParseError( + fmt.Sprintf("invalid expression: AST contains no children %s", k.Kind), + ) + } + + rhs := children[len(children)-1] + + if rhs.Root.ValueType != QuotedStringType { + rhs.Root.ValueType = StringType + rhs.Root.raw = append(rhs.Root.raw, tok.Raw()...) + + } + + children[len(children)-1] = rhs + k.SetChildren(children) + + stack.Push(k) + } + case OpenScopeState: + if !runeCompare(tok.Raw(), openBrace) { + return nil, NewParseError("expected '['") + } + // If OpenScopeState is not at the start, we must mark the previous ast as complete + // + // for example: if previous ast was a skip statement; + // we should mark it as complete before we create a new statement + if k.Kind != ASTKindStart { + stack.MarkComplete(k) + } + + stmt := newStatement() + stack.Push(stmt) + case CloseScopeState: + if !runeCompare(tok.Raw(), closeBrace) { + return nil, NewParseError("expected ']'") + } + + k = trimSpaces(k) + stack.Push(newCompletedSectionStatement(k)) + case SectionState: + var stmt AST + + switch k.Kind { + case ASTKindStatement: + // If there are multiple literals inside of a scope declaration, + // then the current token's raw value will be appended to the Name. + // + // This handles cases like [ profile default ] + // + // k will represent a SectionStatement with the children representing + // the label of the section + stmt = newSectionStatement(tok) + case ASTKindSectionStatement: + k.Root.raw = append(k.Root.raw, tok.Raw()...) + stmt = k + default: + return nil, NewParseError( + fmt.Sprintf("invalid statement: expected statement: %v", k.Kind), + ) + } + + stack.Push(stmt) + case MarkCompleteState: + if k.Kind != ASTKindStart { + stack.MarkComplete(k) + } + + if stack.Len() == 0 { + stack.Push(start) + } + case SkipState: + stack.Push(newSkipStatement(k)) + s.Skip() + case CommentState: + if k.Kind == ASTKindStart { + stack.Push(k) + } else { + stack.MarkComplete(k) + } + + stmt := newCommentStatement(tok) + stack.Push(stmt) + default: + return nil, NewParseError( + fmt.Sprintf("invalid state with ASTKind %v and TokenType %v", + k, tok.Type())) + } + + if len(tokens) > 0 { + tokens = tokens[1:] + } + } + + // this occurs when a statement has not been completed + if stack.top > 1 { + return nil, NewParseError(fmt.Sprintf("incomplete ini expression")) + } + + // returns a sublist which excludes the start symbol + return stack.List(), nil +} + +// trimSpaces will trim spaces on the left and right hand side of +// the literal. +func trimSpaces(k AST) AST { + // trim left hand side of spaces + for i := 0; i < len(k.Root.raw); i++ { + if !isWhitespace(k.Root.raw[i]) { + break + } + + k.Root.raw = k.Root.raw[1:] + i-- + } + + // trim right hand side of spaces + for i := len(k.Root.raw) - 1; i >= 0; i-- { + if !isWhitespace(k.Root.raw[i]) { + break + } + + k.Root.raw = k.Root.raw[:len(k.Root.raw)-1] + } + + return k +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/literal_tokens.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/literal_tokens.go new file mode 100644 index 0000000000000..24df543d38cc2 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/literal_tokens.go @@ -0,0 +1,324 @@ +package ini + +import ( + "fmt" + "strconv" + "strings" +) + +var ( + runesTrue = []rune("true") + runesFalse = []rune("false") +) + +var literalValues = [][]rune{ + runesTrue, + runesFalse, +} + +func isBoolValue(b []rune) bool { + for _, lv := range literalValues { + if isLitValue(lv, b) { + return true + } + } + return false +} + +func isLitValue(want, have []rune) bool { + if len(have) < len(want) { + return false + } + + for i := 0; i < len(want); i++ { + if want[i] != have[i] { + return false + } + } + + return true +} + +// isNumberValue will return whether not the leading characters in +// a byte slice is a number. A number is delimited by whitespace or +// the newline token. +// +// A number is defined to be in a binary, octal, decimal (int | float), hex format, +// or in scientific notation. +func isNumberValue(b []rune) bool { + negativeIndex := 0 + helper := numberHelper{} + needDigit := false + + for i := 0; i < len(b); i++ { + negativeIndex++ + + switch b[i] { + case '-': + if helper.IsNegative() || negativeIndex != 1 { + return false + } + helper.Determine(b[i]) + needDigit = true + continue + case 'e', 'E': + if err := helper.Determine(b[i]); err != nil { + return false + } + negativeIndex = 0 + needDigit = true + continue + case 'b': + if helper.numberFormat == hex { + break + } + fallthrough + case 'o', 'x': + needDigit = true + if i == 0 { + return false + } + + fallthrough + case '.': + if err := helper.Determine(b[i]); err != nil { + return false + } + needDigit = true + continue + } + + if i > 0 && (isNewline(b[i:]) || isWhitespace(b[i])) { + return !needDigit + } + + if !helper.CorrectByte(b[i]) { + return false + } + needDigit = false + } + + return !needDigit +} + +func isValid(b []rune) (bool, int, error) { + if len(b) == 0 { + // TODO: should probably return an error + return false, 0, nil + } + + return isValidRune(b[0]), 1, nil +} + +func isValidRune(r rune) bool { + return r != ':' && r != '=' && r != '[' && r != ']' && r != ' ' && r != '\n' +} + +// ValueType is an enum that will signify what type +// the Value is +type ValueType int + +func (v ValueType) String() string { + switch v { + case NoneType: + return "NONE" + case DecimalType: + return "FLOAT" + case IntegerType: + return "INT" + case StringType: + return "STRING" + case BoolType: + return "BOOL" + } + + return "" +} + +// ValueType enums +const ( + NoneType = ValueType(iota) + DecimalType + IntegerType + StringType + QuotedStringType + BoolType +) + +// Value is a union container +type Value struct { + Type ValueType + raw []rune + + integer int64 + decimal float64 + boolean bool + str string +} + +func newValue(t ValueType, base int, raw []rune) (Value, error) { + v := Value{ + Type: t, + raw: raw, + } + var err error + + switch t { + case DecimalType: + v.decimal, err = strconv.ParseFloat(string(raw), 64) + case IntegerType: + if base != 10 { + raw = raw[2:] + } + + v.integer, err = strconv.ParseInt(string(raw), base, 64) + case StringType: + v.str = string(raw) + case QuotedStringType: + v.str = string(raw[1 : len(raw)-1]) + case BoolType: + v.boolean = runeCompare(v.raw, runesTrue) + } + + // issue 2253 + // + // if the value trying to be parsed is too large, then we will use + // the 'StringType' and raw value instead. + if nerr, ok := err.(*strconv.NumError); ok && nerr.Err == strconv.ErrRange { + v.Type = StringType + v.str = string(raw) + err = nil + } + + return v, err +} + +// Append will append values and change the type to a string +// type. +func (v *Value) Append(tok Token) { + r := tok.Raw() + if v.Type != QuotedStringType { + v.Type = StringType + r = tok.raw[1 : len(tok.raw)-1] + } + if tok.Type() != TokenLit { + v.raw = append(v.raw, tok.Raw()...) + } else { + v.raw = append(v.raw, r...) + } +} + +func (v Value) String() string { + switch v.Type { + case DecimalType: + return fmt.Sprintf("decimal: %f", v.decimal) + case IntegerType: + return fmt.Sprintf("integer: %d", v.integer) + case StringType: + return fmt.Sprintf("string: %s", string(v.raw)) + case QuotedStringType: + return fmt.Sprintf("quoted string: %s", string(v.raw)) + case BoolType: + return fmt.Sprintf("bool: %t", v.boolean) + default: + return "union not set" + } +} + +func newLitToken(b []rune) (Token, int, error) { + n := 0 + var err error + + token := Token{} + if b[0] == '"' { + n, err = getStringValue(b) + if err != nil { + return token, n, err + } + + token = newToken(TokenLit, b[:n], QuotedStringType) + } else if isNumberValue(b) { + var base int + base, n, err = getNumericalValue(b) + if err != nil { + return token, 0, err + } + + value := b[:n] + vType := IntegerType + if contains(value, '.') || hasExponent(value) { + vType = DecimalType + } + token = newToken(TokenLit, value, vType) + token.base = base + } else if isBoolValue(b) { + n, err = getBoolValue(b) + + token = newToken(TokenLit, b[:n], BoolType) + } else { + n, err = getValue(b) + token = newToken(TokenLit, b[:n], StringType) + } + + return token, n, err +} + +// IntValue returns an integer value +func (v Value) IntValue() int64 { + return v.integer +} + +// FloatValue returns a float value +func (v Value) FloatValue() float64 { + return v.decimal +} + +// BoolValue returns a bool value +func (v Value) BoolValue() bool { + return v.boolean +} + +func isTrimmable(r rune) bool { + switch r { + case '\n', ' ': + return true + } + return false +} + +// StringValue returns the string value +func (v Value) StringValue() string { + switch v.Type { + case StringType: + return strings.TrimFunc(string(v.raw), isTrimmable) + case QuotedStringType: + // preserve all characters in the quotes + return string(removeEscapedCharacters(v.raw[1 : len(v.raw)-1])) + default: + return strings.TrimFunc(string(v.raw), isTrimmable) + } +} + +func contains(runes []rune, c rune) bool { + for i := 0; i < len(runes); i++ { + if runes[i] == c { + return true + } + } + + return false +} + +func runeCompare(v1 []rune, v2 []rune) bool { + if len(v1) != len(v2) { + return false + } + + for i := 0; i < len(v1); i++ { + if v1[i] != v2[i] { + return false + } + } + + return true +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/newline_token.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/newline_token.go new file mode 100644 index 0000000000000..e52ac399f17d4 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/newline_token.go @@ -0,0 +1,30 @@ +package ini + +func isNewline(b []rune) bool { + if len(b) == 0 { + return false + } + + if b[0] == '\n' { + return true + } + + if len(b) < 2 { + return false + } + + return b[0] == '\r' && b[1] == '\n' +} + +func newNewlineToken(b []rune) (Token, int, error) { + i := 1 + if b[0] == '\r' && isNewline(b[1:]) { + i++ + } + + if !isNewline([]rune(b[:i])) { + return emptyToken, 0, NewParseError("invalid new line token") + } + + return newToken(TokenNL, b[:i], NoneType), i, nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/number_helper.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/number_helper.go new file mode 100644 index 0000000000000..a45c0bc56622a --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/number_helper.go @@ -0,0 +1,152 @@ +package ini + +import ( + "bytes" + "fmt" + "strconv" +) + +const ( + none = numberFormat(iota) + binary + octal + decimal + hex + exponent +) + +type numberFormat int + +// numberHelper is used to dictate what format a number is in +// and what to do for negative values. Since -1e-4 is a valid +// number, we cannot just simply check for duplicate negatives. +type numberHelper struct { + numberFormat numberFormat + + negative bool + negativeExponent bool +} + +func (b numberHelper) Exists() bool { + return b.numberFormat != none +} + +func (b numberHelper) IsNegative() bool { + return b.negative || b.negativeExponent +} + +func (b *numberHelper) Determine(c rune) error { + if b.Exists() { + return NewParseError(fmt.Sprintf("multiple number formats: 0%v", string(c))) + } + + switch c { + case 'b': + b.numberFormat = binary + case 'o': + b.numberFormat = octal + case 'x': + b.numberFormat = hex + case 'e', 'E': + b.numberFormat = exponent + case '-': + if b.numberFormat != exponent { + b.negative = true + } else { + b.negativeExponent = true + } + case '.': + b.numberFormat = decimal + default: + return NewParseError(fmt.Sprintf("invalid number character: %v", string(c))) + } + + return nil +} + +func (b numberHelper) CorrectByte(c rune) bool { + switch { + case b.numberFormat == binary: + if !isBinaryByte(c) { + return false + } + case b.numberFormat == octal: + if !isOctalByte(c) { + return false + } + case b.numberFormat == hex: + if !isHexByte(c) { + return false + } + case b.numberFormat == decimal: + if !isDigit(c) { + return false + } + case b.numberFormat == exponent: + if !isDigit(c) { + return false + } + case b.negativeExponent: + if !isDigit(c) { + return false + } + case b.negative: + if !isDigit(c) { + return false + } + default: + if !isDigit(c) { + return false + } + } + + return true +} + +func (b numberHelper) Base() int { + switch b.numberFormat { + case binary: + return 2 + case octal: + return 8 + case hex: + return 16 + default: + return 10 + } +} + +func (b numberHelper) String() string { + buf := bytes.Buffer{} + i := 0 + + switch b.numberFormat { + case binary: + i++ + buf.WriteString(strconv.Itoa(i) + ": binary format\n") + case octal: + i++ + buf.WriteString(strconv.Itoa(i) + ": octal format\n") + case hex: + i++ + buf.WriteString(strconv.Itoa(i) + ": hex format\n") + case exponent: + i++ + buf.WriteString(strconv.Itoa(i) + ": exponent format\n") + default: + i++ + buf.WriteString(strconv.Itoa(i) + ": integer format\n") + } + + if b.negative { + i++ + buf.WriteString(strconv.Itoa(i) + ": negative format\n") + } + + if b.negativeExponent { + i++ + buf.WriteString(strconv.Itoa(i) + ": negative exponent format\n") + } + + return buf.String() +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/op_tokens.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/op_tokens.go new file mode 100644 index 0000000000000..8a84c7cbe0809 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/op_tokens.go @@ -0,0 +1,39 @@ +package ini + +import ( + "fmt" +) + +var ( + equalOp = []rune("=") + equalColonOp = []rune(":") +) + +func isOp(b []rune) bool { + if len(b) == 0 { + return false + } + + switch b[0] { + case '=': + return true + case ':': + return true + default: + return false + } +} + +func newOpToken(b []rune) (Token, int, error) { + tok := Token{} + + switch b[0] { + case '=': + tok = newToken(TokenOp, equalOp, NoneType) + case ':': + tok = newToken(TokenOp, equalColonOp, NoneType) + default: + return tok, 0, NewParseError(fmt.Sprintf("unexpected op type, %v", b[0])) + } + return tok, 1, nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/parse_error.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/parse_error.go new file mode 100644 index 0000000000000..45728701931ce --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/parse_error.go @@ -0,0 +1,43 @@ +package ini + +import "fmt" + +const ( + // ErrCodeParseError is returned when a parsing error + // has occurred. + ErrCodeParseError = "INIParseError" +) + +// ParseError is an error which is returned during any part of +// the parsing process. +type ParseError struct { + msg string +} + +// NewParseError will return a new ParseError where message +// is the description of the error. +func NewParseError(message string) *ParseError { + return &ParseError{ + msg: message, + } +} + +// Code will return the ErrCodeParseError +func (err *ParseError) Code() string { + return ErrCodeParseError +} + +// Message returns the error's message +func (err *ParseError) Message() string { + return err.msg +} + +// OrigError return nothing since there will never be any +// original error. +func (err *ParseError) OrigError() error { + return nil +} + +func (err *ParseError) Error() string { + return fmt.Sprintf("%s: %s", err.Code(), err.Message()) +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/parse_stack.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/parse_stack.go new file mode 100644 index 0000000000000..7f01cf7c70367 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/parse_stack.go @@ -0,0 +1,60 @@ +package ini + +import ( + "bytes" + "fmt" +) + +// ParseStack is a stack that contains a container, the stack portion, +// and the list which is the list of ASTs that have been successfully +// parsed. +type ParseStack struct { + top int + container []AST + list []AST + index int +} + +func newParseStack(sizeContainer, sizeList int) ParseStack { + return ParseStack{ + container: make([]AST, sizeContainer), + list: make([]AST, sizeList), + } +} + +// Pop will return and truncate the last container element. +func (s *ParseStack) Pop() AST { + s.top-- + return s.container[s.top] +} + +// Push will add the new AST to the container +func (s *ParseStack) Push(ast AST) { + s.container[s.top] = ast + s.top++ +} + +// MarkComplete will append the AST to the list of completed statements +func (s *ParseStack) MarkComplete(ast AST) { + s.list[s.index] = ast + s.index++ +} + +// List will return the completed statements +func (s ParseStack) List() []AST { + return s.list[:s.index] +} + +// Len will return the length of the container +func (s *ParseStack) Len() int { + return s.top +} + +func (s ParseStack) String() string { + buf := bytes.Buffer{} + for i, node := range s.list { + buf.WriteString(fmt.Sprintf("%d: %v\n", i+1, node)) + } + + return buf.String() +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/sep_tokens.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/sep_tokens.go new file mode 100644 index 0000000000000..f82095ba2594e --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/sep_tokens.go @@ -0,0 +1,41 @@ +package ini + +import ( + "fmt" +) + +var ( + emptyRunes = []rune{} +) + +func isSep(b []rune) bool { + if len(b) == 0 { + return false + } + + switch b[0] { + case '[', ']': + return true + default: + return false + } +} + +var ( + openBrace = []rune("[") + closeBrace = []rune("]") +) + +func newSepToken(b []rune) (Token, int, error) { + tok := Token{} + + switch b[0] { + case '[': + tok = newToken(TokenSep, openBrace, NoneType) + case ']': + tok = newToken(TokenSep, closeBrace, NoneType) + default: + return tok, 0, NewParseError(fmt.Sprintf("unexpected sep type, %v", b[0])) + } + return tok, 1, nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/skipper.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/skipper.go new file mode 100644 index 0000000000000..da7a4049cfae5 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/skipper.go @@ -0,0 +1,45 @@ +package ini + +// skipper is used to skip certain blocks of an ini file. +// Currently skipper is used to skip nested blocks of ini +// files. See example below +// +// [ foo ] +// nested = ; this section will be skipped +// a=b +// c=d +// bar=baz ; this will be included +type skipper struct { + shouldSkip bool + TokenSet bool + prevTok Token +} + +func newSkipper() skipper { + return skipper{ + prevTok: emptyToken, + } +} + +func (s *skipper) ShouldSkip(tok Token) bool { + // should skip state will be modified only if previous token was new line (NL); + // and the current token is not WhiteSpace (WS). + if s.shouldSkip && + s.prevTok.Type() == TokenNL && + tok.Type() != TokenWS { + s.Continue() + return false + } + s.prevTok = tok + return s.shouldSkip +} + +func (s *skipper) Skip() { + s.shouldSkip = true +} + +func (s *skipper) Continue() { + s.shouldSkip = false + // empty token is assigned as we return to default state, when should skip is false + s.prevTok = emptyToken +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/statement.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/statement.go new file mode 100644 index 0000000000000..18f3fe893170c --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/statement.go @@ -0,0 +1,35 @@ +package ini + +// Statement is an empty AST mostly used for transitioning states. +func newStatement() AST { + return newAST(ASTKindStatement, AST{}) +} + +// SectionStatement represents a section AST +func newSectionStatement(tok Token) AST { + return newASTWithRootToken(ASTKindSectionStatement, tok) +} + +// ExprStatement represents a completed expression AST +func newExprStatement(ast AST) AST { + return newAST(ASTKindExprStatement, ast) +} + +// CommentStatement represents a comment in the ini definition. +// +// grammar: +// comment -> #comment' | ;comment' +// comment' -> epsilon | value +func newCommentStatement(tok Token) AST { + return newAST(ASTKindCommentStatement, newExpression(tok)) +} + +// CompletedSectionStatement represents a completed section +func newCompletedSectionStatement(ast AST) AST { + return newAST(ASTKindCompletedSectionStatement, ast) +} + +// SkipStatement is used to skip whole statements +func newSkipStatement(ast AST) AST { + return newAST(ASTKindSkipStatement, ast) +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/value_util.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/value_util.go new file mode 100644 index 0000000000000..305999d29be08 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/value_util.go @@ -0,0 +1,284 @@ +package ini + +import ( + "fmt" +) + +// getStringValue will return a quoted string and the amount +// of bytes read +// +// an error will be returned if the string is not properly formatted +func getStringValue(b []rune) (int, error) { + if b[0] != '"' { + return 0, NewParseError("strings must start with '\"'") + } + + endQuote := false + i := 1 + + for ; i < len(b) && !endQuote; i++ { + if escaped := isEscaped(b[:i], b[i]); b[i] == '"' && !escaped { + endQuote = true + break + } else if escaped { + /*c, err := getEscapedByte(b[i]) + if err != nil { + return 0, err + } + + b[i-1] = c + b = append(b[:i], b[i+1:]...) + i--*/ + + continue + } + } + + if !endQuote { + return 0, NewParseError("missing '\"' in string value") + } + + return i + 1, nil +} + +// getBoolValue will return a boolean and the amount +// of bytes read +// +// an error will be returned if the boolean is not of a correct +// value +func getBoolValue(b []rune) (int, error) { + if len(b) < 4 { + return 0, NewParseError("invalid boolean value") + } + + n := 0 + for _, lv := range literalValues { + if len(lv) > len(b) { + continue + } + + if isLitValue(lv, b) { + n = len(lv) + } + } + + if n == 0 { + return 0, NewParseError("invalid boolean value") + } + + return n, nil +} + +// getNumericalValue will return a numerical string, the amount +// of bytes read, and the base of the number +// +// an error will be returned if the number is not of a correct +// value +func getNumericalValue(b []rune) (int, int, error) { + if !isDigit(b[0]) { + return 0, 0, NewParseError("invalid digit value") + } + + i := 0 + helper := numberHelper{} + +loop: + for negativeIndex := 0; i < len(b); i++ { + negativeIndex++ + + if !isDigit(b[i]) { + switch b[i] { + case '-': + if helper.IsNegative() || negativeIndex != 1 { + return 0, 0, NewParseError("parse error '-'") + } + + n := getNegativeNumber(b[i:]) + i += (n - 1) + helper.Determine(b[i]) + continue + case '.': + if err := helper.Determine(b[i]); err != nil { + return 0, 0, err + } + case 'e', 'E': + if err := helper.Determine(b[i]); err != nil { + return 0, 0, err + } + + negativeIndex = 0 + case 'b': + if helper.numberFormat == hex { + break + } + fallthrough + case 'o', 'x': + if i == 0 && b[i] != '0' { + return 0, 0, NewParseError("incorrect base format, expected leading '0'") + } + + if i != 1 { + return 0, 0, NewParseError(fmt.Sprintf("incorrect base format found %s at %d index", string(b[i]), i)) + } + + if err := helper.Determine(b[i]); err != nil { + return 0, 0, err + } + default: + if isWhitespace(b[i]) { + break loop + } + + if isNewline(b[i:]) { + break loop + } + + if !(helper.numberFormat == hex && isHexByte(b[i])) { + if i+2 < len(b) && !isNewline(b[i:i+2]) { + return 0, 0, NewParseError("invalid numerical character") + } else if !isNewline([]rune{b[i]}) { + return 0, 0, NewParseError("invalid numerical character") + } + + break loop + } + } + } + } + + return helper.Base(), i, nil +} + +// isDigit will return whether or not something is an integer +func isDigit(b rune) bool { + return b >= '0' && b <= '9' +} + +func hasExponent(v []rune) bool { + return contains(v, 'e') || contains(v, 'E') +} + +func isBinaryByte(b rune) bool { + switch b { + case '0', '1': + return true + default: + return false + } +} + +func isOctalByte(b rune) bool { + switch b { + case '0', '1', '2', '3', '4', '5', '6', '7': + return true + default: + return false + } +} + +func isHexByte(b rune) bool { + if isDigit(b) { + return true + } + return (b >= 'A' && b <= 'F') || + (b >= 'a' && b <= 'f') +} + +func getValue(b []rune) (int, error) { + i := 0 + + for i < len(b) { + if isNewline(b[i:]) { + break + } + + if isOp(b[i:]) { + break + } + + valid, n, err := isValid(b[i:]) + if err != nil { + return 0, err + } + + if !valid { + break + } + + i += n + } + + return i, nil +} + +// getNegativeNumber will return a negative number from a +// byte slice. This will iterate through all characters until +// a non-digit has been found. +func getNegativeNumber(b []rune) int { + if b[0] != '-' { + return 0 + } + + i := 1 + for ; i < len(b); i++ { + if !isDigit(b[i]) { + return i + } + } + + return i +} + +// isEscaped will return whether or not the character is an escaped +// character. +func isEscaped(value []rune, b rune) bool { + if len(value) == 0 { + return false + } + + switch b { + case '\'': // single quote + case '"': // quote + case 'n': // newline + case 't': // tab + case '\\': // backslash + default: + return false + } + + return value[len(value)-1] == '\\' +} + +func getEscapedByte(b rune) (rune, error) { + switch b { + case '\'': // single quote + return '\'', nil + case '"': // quote + return '"', nil + case 'n': // newline + return '\n', nil + case 't': // table + return '\t', nil + case '\\': // backslash + return '\\', nil + default: + return b, NewParseError(fmt.Sprintf("invalid escaped character %c", b)) + } +} + +func removeEscapedCharacters(b []rune) []rune { + for i := 0; i < len(b); i++ { + if isEscaped(b[:i], b[i]) { + c, err := getEscapedByte(b[i]) + if err != nil { + return b + } + + b[i-1] = c + b = append(b[:i], b[i+1:]...) + i-- + } + } + + return b +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/visitor.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/visitor.go new file mode 100644 index 0000000000000..94841c32443c8 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/visitor.go @@ -0,0 +1,166 @@ +package ini + +import ( + "fmt" + "sort" +) + +// Visitor is an interface used by walkers that will +// traverse an array of ASTs. +type Visitor interface { + VisitExpr(AST) error + VisitStatement(AST) error +} + +// DefaultVisitor is used to visit statements and expressions +// and ensure that they are both of the correct format. +// In addition, upon visiting this will build sections and populate +// the Sections field which can be used to retrieve profile +// configuration. +type DefaultVisitor struct { + scope string + Sections Sections +} + +// NewDefaultVisitor return a DefaultVisitor +func NewDefaultVisitor() *DefaultVisitor { + return &DefaultVisitor{ + Sections: Sections{ + container: map[string]Section{}, + }, + } +} + +// VisitExpr visits expressions... +func (v *DefaultVisitor) VisitExpr(expr AST) error { + t := v.Sections.container[v.scope] + if t.values == nil { + t.values = values{} + } + + switch expr.Kind { + case ASTKindExprStatement: + opExpr := expr.GetRoot() + switch opExpr.Kind { + case ASTKindEqualExpr: + children := opExpr.GetChildren() + if len(children) <= 1 { + return NewParseError("unexpected token type") + } + + rhs := children[1] + + if rhs.Root.Type() != TokenLit { + return NewParseError("unexpected token type") + } + + key := EqualExprKey(opExpr) + v, err := newValue(rhs.Root.ValueType, rhs.Root.base, rhs.Root.Raw()) + if err != nil { + return err + } + + t.values[key] = v + default: + return NewParseError(fmt.Sprintf("unsupported expression %v", expr)) + } + default: + return NewParseError(fmt.Sprintf("unsupported expression %v", expr)) + } + + v.Sections.container[v.scope] = t + return nil +} + +// VisitStatement visits statements... +func (v *DefaultVisitor) VisitStatement(stmt AST) error { + switch stmt.Kind { + case ASTKindCompletedSectionStatement: + child := stmt.GetRoot() + if child.Kind != ASTKindSectionStatement { + return NewParseError(fmt.Sprintf("unsupported child statement: %T", child)) + } + + name := string(child.Root.Raw()) + v.Sections.container[name] = Section{} + v.scope = name + default: + return NewParseError(fmt.Sprintf("unsupported statement: %s", stmt.Kind)) + } + + return nil +} + +// Sections is a map of Section structures that represent +// a configuration. +type Sections struct { + container map[string]Section +} + +// GetSection will return section p. If section p does not exist, +// false will be returned in the second parameter. +func (t Sections) GetSection(p string) (Section, bool) { + v, ok := t.container[p] + return v, ok +} + +// values represents a map of union values. +type values map[string]Value + +// List will return a list of all sections that were successfully +// parsed. +func (t Sections) List() []string { + keys := make([]string, len(t.container)) + i := 0 + for k := range t.container { + keys[i] = k + i++ + } + + sort.Strings(keys) + return keys +} + +// Section contains a name and values. This represent +// a sectioned entry in a configuration file. +type Section struct { + Name string + values values +} + +// Has will return whether or not an entry exists in a given section +func (t Section) Has(k string) bool { + _, ok := t.values[k] + return ok +} + +// ValueType will returned what type the union is set to. If +// k was not found, the NoneType will be returned. +func (t Section) ValueType(k string) (ValueType, bool) { + v, ok := t.values[k] + return v.Type, ok +} + +// Bool returns a bool value at k +func (t Section) Bool(k string) bool { + return t.values[k].BoolValue() +} + +// Int returns an integer value at k +func (t Section) Int(k string) int64 { + return t.values[k].IntValue() +} + +// Float64 returns a float value at k +func (t Section) Float64(k string) float64 { + return t.values[k].FloatValue() +} + +// String returns the string value at k +func (t Section) String(k string) string { + _, ok := t.values[k] + if !ok { + return "" + } + return t.values[k].StringValue() +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/walker.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/walker.go new file mode 100644 index 0000000000000..99915f7f777ce --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/walker.go @@ -0,0 +1,25 @@ +package ini + +// Walk will traverse the AST using the v, the Visitor. +func Walk(tree []AST, v Visitor) error { + for _, node := range tree { + switch node.Kind { + case ASTKindExpr, + ASTKindExprStatement: + + if err := v.VisitExpr(node); err != nil { + return err + } + case ASTKindStatement, + ASTKindCompletedSectionStatement, + ASTKindNestedSectionStatement, + ASTKindCompletedNestedSectionStatement: + + if err := v.VisitStatement(node); err != nil { + return err + } + } + } + + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/ini/ws_token.go b/vendor/github.com/aws/aws-sdk-go/internal/ini/ws_token.go new file mode 100644 index 0000000000000..7ffb4ae06ff0c --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/ini/ws_token.go @@ -0,0 +1,24 @@ +package ini + +import ( + "unicode" +) + +// isWhitespace will return whether or not the character is +// a whitespace character. +// +// Whitespace is defined as a space or tab. +func isWhitespace(c rune) bool { + return unicode.IsSpace(c) && c != '\n' && c != '\r' +} + +func newWSToken(b []rune) (Token, int, error) { + i := 0 + for ; i < len(b); i++ { + if !isWhitespace(b[i]) { + break + } + } + + return newToken(TokenWS, b[:i], NoneType), i, nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/sdkio/byte.go b/vendor/github.com/aws/aws-sdk-go/internal/sdkio/byte.go new file mode 100644 index 0000000000000..6c443988bbc9a --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/sdkio/byte.go @@ -0,0 +1,12 @@ +package sdkio + +const ( + // Byte is 8 bits + Byte int64 = 1 + // KibiByte (KiB) is 1024 Bytes + KibiByte = Byte * 1024 + // MebiByte (MiB) is 1024 KiB + MebiByte = KibiByte * 1024 + // GibiByte (GiB) is 1024 MiB + GibiByte = MebiByte * 1024 +) diff --git a/vendor/github.com/aws/aws-sdk-go/internal/sdkio/io_go1.6.go b/vendor/github.com/aws/aws-sdk-go/internal/sdkio/io_go1.6.go new file mode 100644 index 0000000000000..5aa9137e0f936 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/sdkio/io_go1.6.go @@ -0,0 +1,10 @@ +// +build !go1.7 + +package sdkio + +// Copy of Go 1.7 io package's Seeker constants. +const ( + SeekStart = 0 // seek relative to the origin of the file + SeekCurrent = 1 // seek relative to the current offset + SeekEnd = 2 // seek relative to the end +) diff --git a/vendor/github.com/aws/aws-sdk-go/internal/sdkio/io_go1.7.go b/vendor/github.com/aws/aws-sdk-go/internal/sdkio/io_go1.7.go new file mode 100644 index 0000000000000..e5f005613b796 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/sdkio/io_go1.7.go @@ -0,0 +1,12 @@ +// +build go1.7 + +package sdkio + +import "io" + +// Alias for Go 1.7 io package Seeker constants +const ( + SeekStart = io.SeekStart // seek relative to the origin of the file + SeekCurrent = io.SeekCurrent // seek relative to the current offset + SeekEnd = io.SeekEnd // seek relative to the end +) diff --git a/vendor/github.com/aws/aws-sdk-go/internal/sdkmath/floor.go b/vendor/github.com/aws/aws-sdk-go/internal/sdkmath/floor.go new file mode 100644 index 0000000000000..44898eed0fdd7 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/sdkmath/floor.go @@ -0,0 +1,15 @@ +// +build go1.10 + +package sdkmath + +import "math" + +// Round returns the nearest integer, rounding half away from zero. +// +// Special cases are: +// Round(±0) = ±0 +// Round(±Inf) = ±Inf +// Round(NaN) = NaN +func Round(x float64) float64 { + return math.Round(x) +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/sdkmath/floor_go1.9.go b/vendor/github.com/aws/aws-sdk-go/internal/sdkmath/floor_go1.9.go new file mode 100644 index 0000000000000..810ec7f08b004 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/sdkmath/floor_go1.9.go @@ -0,0 +1,56 @@ +// +build !go1.10 + +package sdkmath + +import "math" + +// Copied from the Go standard library's (Go 1.12) math/floor.go for use in +// Go version prior to Go 1.10. +const ( + uvone = 0x3FF0000000000000 + mask = 0x7FF + shift = 64 - 11 - 1 + bias = 1023 + signMask = 1 << 63 + fracMask = 1<= 0.5 { + // return t + Copysign(1, x) + // } + // return t + // } + bits := math.Float64bits(x) + e := uint(bits>>shift) & mask + if e < bias { + // Round abs(x) < 1 including denormals. + bits &= signMask // +-0 + if e == bias-1 { + bits |= uvone // +-1 + } + } else if e < bias+shift { + // Round any abs(x) >= 1 containing a fractional component [0,1). + // + // Numbers with larger exponents are returned unchanged since they + // must be either an integer, infinity, or NaN. + const half = 1 << (shift - 1) + e -= bias + bits += half >> e + bits &^= fracMask >> e + } + return math.Float64frombits(bits) +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/sdkrand/locked_source.go b/vendor/github.com/aws/aws-sdk-go/internal/sdkrand/locked_source.go new file mode 100644 index 0000000000000..0c9802d877066 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/sdkrand/locked_source.go @@ -0,0 +1,29 @@ +package sdkrand + +import ( + "math/rand" + "sync" + "time" +) + +// lockedSource is a thread-safe implementation of rand.Source +type lockedSource struct { + lk sync.Mutex + src rand.Source +} + +func (r *lockedSource) Int63() (n int64) { + r.lk.Lock() + n = r.src.Int63() + r.lk.Unlock() + return +} + +func (r *lockedSource) Seed(seed int64) { + r.lk.Lock() + r.src.Seed(seed) + r.lk.Unlock() +} + +// SeededRand is a new RNG using a thread safe implementation of rand.Source +var SeededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())}) diff --git a/vendor/github.com/aws/aws-sdk-go/internal/sdkrand/read.go b/vendor/github.com/aws/aws-sdk-go/internal/sdkrand/read.go new file mode 100644 index 0000000000000..f4651da2da58b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/sdkrand/read.go @@ -0,0 +1,11 @@ +// +build go1.6 + +package sdkrand + +import "math/rand" + +// Read provides the stub for math.Rand.Read method support for go version's +// 1.6 and greater. +func Read(r *rand.Rand, p []byte) (int, error) { + return r.Read(p) +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/sdkrand/read_1_5.go b/vendor/github.com/aws/aws-sdk-go/internal/sdkrand/read_1_5.go new file mode 100644 index 0000000000000..b1d93a33d48a3 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/sdkrand/read_1_5.go @@ -0,0 +1,24 @@ +// +build !go1.6 + +package sdkrand + +import "math/rand" + +// Read backfills Go 1.6's math.Rand.Reader for Go 1.5 +func Read(r *rand.Rand, p []byte) (n int, err error) { + // Copy of Go standard libraries math package's read function not added to + // standard library until Go 1.6. + var pos int8 + var val int64 + for n = 0; n < len(p); n++ { + if pos == 0 { + val = r.Int63() + pos = 7 + } + p[n] = byte(val) + val >>= 8 + pos-- + } + + return n, err +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/sdkuri/path.go b/vendor/github.com/aws/aws-sdk-go/internal/sdkuri/path.go new file mode 100644 index 0000000000000..38ea61afeaa63 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/sdkuri/path.go @@ -0,0 +1,23 @@ +package sdkuri + +import ( + "path" + "strings" +) + +// PathJoin will join the elements of the path delimited by the "/" +// character. Similar to path.Join with the exception the trailing "/" +// character is preserved if present. +func PathJoin(elems ...string) string { + if len(elems) == 0 { + return "" + } + + hasTrailing := strings.HasSuffix(elems[len(elems)-1], "/") + str := path.Join(elems...) + if hasTrailing && str != "/" { + str += "/" + } + + return str +} diff --git a/vendor/github.com/aws/aws-sdk-go/internal/shareddefaults/ecs_container.go b/vendor/github.com/aws/aws-sdk-go/internal/shareddefaults/ecs_container.go new file mode 100644 index 0000000000000..7da8a49ce522a --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/shareddefaults/ecs_container.go @@ -0,0 +1,12 @@ +package shareddefaults + +const ( + // ECSCredsProviderEnvVar is an environmental variable key used to + // determine which path needs to be hit. + ECSCredsProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" +) + +// ECSContainerCredentialsURI is the endpoint to retrieve container +// credentials. This can be overridden to test to ensure the credential process +// is behaving correctly. +var ECSContainerCredentialsURI = "http://169.254.170.2" diff --git a/vendor/github.com/aws/aws-sdk-go/internal/strings/strings.go b/vendor/github.com/aws/aws-sdk-go/internal/strings/strings.go new file mode 100644 index 0000000000000..d008ae27cb319 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/internal/strings/strings.go @@ -0,0 +1,11 @@ +package strings + +import ( + "strings" +) + +// HasPrefixFold tests whether the string s begins with prefix, interpreted as UTF-8 strings, +// under Unicode case-folding. +func HasPrefixFold(s, prefix string) bool { + return len(s) >= len(prefix) && strings.EqualFold(s[0:len(prefix)], prefix) +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/host.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/host.go new file mode 100644 index 0000000000000..d7d42db0a6a5e --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/host.go @@ -0,0 +1,68 @@ +package protocol + +import ( + "strings" + + "github.com/aws/aws-sdk-go/aws/request" +) + +// ValidateEndpointHostHandler is a request handler that will validate the +// request endpoint's hosts is a valid RFC 3986 host. +var ValidateEndpointHostHandler = request.NamedHandler{ + Name: "awssdk.protocol.ValidateEndpointHostHandler", + Fn: func(r *request.Request) { + err := ValidateEndpointHost(r.Operation.Name, r.HTTPRequest.URL.Host) + if err != nil { + r.Error = err + } + }, +} + +// ValidateEndpointHost validates that the host string passed in is a valid RFC +// 3986 host. Returns error if the host is not valid. +func ValidateEndpointHost(opName, host string) error { + paramErrs := request.ErrInvalidParams{Context: opName} + labels := strings.Split(host, ".") + + for i, label := range labels { + if i == len(labels)-1 && len(label) == 0 { + // Allow trailing dot for FQDN hosts. + continue + } + + if !ValidHostLabel(label) { + paramErrs.Add(request.NewErrParamFormat( + "endpoint host label", "[a-zA-Z0-9-]{1,63}", label)) + } + } + + if len(host) > 255 { + paramErrs.Add(request.NewErrParamMaxLen( + "endpoint host", 255, host, + )) + } + + if paramErrs.Len() > 0 { + return paramErrs + } + return nil +} + +// ValidHostLabel returns if the label is a valid RFC 3986 host label. +func ValidHostLabel(label string) bool { + if l := len(label); l == 0 || l > 63 { + return false + } + for _, r := range label { + switch { + case r >= '0' && r <= '9': + case r >= 'A' && r <= 'Z': + case r >= 'a' && r <= 'z': + case r == '-': + default: + return false + } + } + + return true +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/host_prefix.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/host_prefix.go new file mode 100644 index 0000000000000..915b0fcafd73f --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/host_prefix.go @@ -0,0 +1,54 @@ +package protocol + +import ( + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" +) + +// HostPrefixHandlerName is the handler name for the host prefix request +// handler. +const HostPrefixHandlerName = "awssdk.endpoint.HostPrefixHandler" + +// NewHostPrefixHandler constructs a build handler +func NewHostPrefixHandler(prefix string, labelsFn func() map[string]string) request.NamedHandler { + builder := HostPrefixBuilder{ + Prefix: prefix, + LabelsFn: labelsFn, + } + + return request.NamedHandler{ + Name: HostPrefixHandlerName, + Fn: builder.Build, + } +} + +// HostPrefixBuilder provides the request handler to expand and prepend +// the host prefix into the operation's request endpoint host. +type HostPrefixBuilder struct { + Prefix string + LabelsFn func() map[string]string +} + +// Build updates the passed in Request with the HostPrefix template expanded. +func (h HostPrefixBuilder) Build(r *request.Request) { + if aws.BoolValue(r.Config.DisableEndpointHostPrefix) { + return + } + + var labels map[string]string + if h.LabelsFn != nil { + labels = h.LabelsFn() + } + + prefix := h.Prefix + for name, value := range labels { + prefix = strings.Replace(prefix, "{"+name+"}", value, -1) + } + + r.HTTPRequest.URL.Host = prefix + r.HTTPRequest.URL.Host + if len(r.HTTPRequest.Host) > 0 { + r.HTTPRequest.Host = prefix + r.HTTPRequest.Host + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go index ec765ba257ed5..864fb6704b479 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go @@ -216,7 +216,17 @@ func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) erro default: switch converted := value.Interface().(type) { case time.Time: - buf.Write(strconv.AppendInt(scratch[:0], converted.UTC().Unix(), 10)) + format := tag.Get("timestampFormat") + if len(format) == 0 { + format = protocol.UnixTimeFormatName + } + + ts := protocol.FormatTime(format, converted) + if format != protocol.UnixTimeFormatName { + ts = `"` + ts + `"` + } + + buf.WriteString(ts) case []byte: if !value.IsNil() { buf.WriteByte('"') diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go index 037e1e7be78df..5e9499699bace 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go @@ -1,39 +1,76 @@ package jsonutil import ( + "bytes" "encoding/base64" "encoding/json" "fmt" "io" - "io/ioutil" "reflect" + "strings" "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/private/protocol" ) +// UnmarshalJSONError unmarshal's the reader's JSON document into the passed in +// type. The value to unmarshal the json document into must be a pointer to the +// type. +func UnmarshalJSONError(v interface{}, stream io.Reader) error { + var errBuf bytes.Buffer + body := io.TeeReader(stream, &errBuf) + + err := json.NewDecoder(body).Decode(v) + if err != nil { + msg := "failed decoding error message" + if err == io.EOF { + msg = "error message missing" + err = nil + } + return awserr.NewUnmarshalError(err, msg, errBuf.Bytes()) + } + + return nil +} + // UnmarshalJSON reads a stream and unmarshals the results in object v. func UnmarshalJSON(v interface{}, stream io.Reader) error { var out interface{} - b, err := ioutil.ReadAll(stream) - if err != nil { + err := json.NewDecoder(stream).Decode(&out) + if err == io.EOF { + return nil + } else if err != nil { return err } - if len(b) == 0 { - return nil - } + return unmarshaler{}.unmarshalAny(reflect.ValueOf(v), out, "") +} + +// UnmarshalJSONCaseInsensitive reads a stream and unmarshals the result into the +// object v. Ignores casing for structure members. +func UnmarshalJSONCaseInsensitive(v interface{}, stream io.Reader) error { + var out interface{} - if err := json.Unmarshal(b, &out); err != nil { + err := json.NewDecoder(stream).Decode(&out) + if err == io.EOF { + return nil + } else if err != nil { return err } - return unmarshalAny(reflect.ValueOf(v), out, "") + return unmarshaler{ + caseInsensitive: true, + }.unmarshalAny(reflect.ValueOf(v), out, "") +} + +type unmarshaler struct { + caseInsensitive bool } -func unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) error { +func (u unmarshaler) unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) error { vtype := value.Type() if vtype.Kind() == reflect.Ptr { vtype = vtype.Elem() // check kind of actual element type @@ -65,17 +102,17 @@ func unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) if field, ok := vtype.FieldByName("_"); ok { tag = field.Tag } - return unmarshalStruct(value, data, tag) + return u.unmarshalStruct(value, data, tag) case "list": - return unmarshalList(value, data, tag) + return u.unmarshalList(value, data, tag) case "map": - return unmarshalMap(value, data, tag) + return u.unmarshalMap(value, data, tag) default: - return unmarshalScalar(value, data, tag) + return u.unmarshalScalar(value, data, tag) } } -func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTag) error { +func (u unmarshaler) unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTag) error { if data == nil { return nil } @@ -99,7 +136,7 @@ func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTa // unwrap any payloads if payload := tag.Get("payload"); payload != "" { field, _ := t.FieldByName(payload) - return unmarshalAny(value.FieldByName(payload), data, field.Tag) + return u.unmarshalAny(value.FieldByName(payload), data, field.Tag) } for i := 0; i < t.NumField(); i++ { @@ -113,9 +150,19 @@ func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTa if locName := field.Tag.Get("locationName"); locName != "" { name = locName } + if u.caseInsensitive { + if _, ok := mapData[name]; !ok { + // Fallback to uncased name search if the exact name didn't match. + for kn, v := range mapData { + if strings.EqualFold(kn, name) { + mapData[name] = v + } + } + } + } member := value.FieldByIndex(field.Index) - err := unmarshalAny(member, mapData[name], field.Tag) + err := u.unmarshalAny(member, mapData[name], field.Tag) if err != nil { return err } @@ -123,7 +170,7 @@ func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTa return nil } -func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) error { +func (u unmarshaler) unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) error { if data == nil { return nil } @@ -138,7 +185,7 @@ func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) } for i, c := range listData { - err := unmarshalAny(value.Index(i), c, "") + err := u.unmarshalAny(value.Index(i), c, "") if err != nil { return err } @@ -147,7 +194,7 @@ func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) return nil } -func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) error { +func (u unmarshaler) unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) error { if data == nil { return nil } @@ -164,17 +211,14 @@ func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) kvalue := reflect.ValueOf(k) vvalue := reflect.New(value.Type().Elem()).Elem() - unmarshalAny(vvalue, v, "") + u.unmarshalAny(vvalue, v, "") value.SetMapIndex(kvalue, vvalue) } return nil } -func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error { - errf := func() error { - return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) - } +func (u unmarshaler) unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error { switch d := data.(type) { case nil: @@ -189,6 +233,17 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa return err } value.Set(reflect.ValueOf(b)) + case *time.Time: + format := tag.Get("timestampFormat") + if len(format) == 0 { + format = protocol.ISO8601TimeFormatName + } + + t, err := protocol.ParseTime(format, d) + if err != nil { + return err + } + value.Set(reflect.ValueOf(&t)) case aws.JSONValue: // No need to use escaping as the value is a non-quoted string. v, err := protocol.DecodeJSONValue(d, protocol.NoEscape) @@ -197,7 +252,7 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa } value.Set(reflect.ValueOf(v)) default: - return errf() + return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) } case float64: switch value.Interface().(type) { @@ -207,17 +262,18 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa case *float64: value.Set(reflect.ValueOf(&d)) case *time.Time: + // Time unmarshaled from a float64 can only be epoch seconds t := time.Unix(int64(d), 0).UTC() value.Set(reflect.ValueOf(&t)) default: - return errf() + return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) } case bool: switch value.Interface().(type) { case *bool: value.Set(reflect.ValueOf(&d)) default: - return errf() + return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) } default: return fmt.Errorf("unsupported JSON value (%v)", data) diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go index 56af4dc4426c5..89cfda75f668e 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go @@ -6,10 +6,6 @@ package jsonrpc //go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/json.json unmarshal_test.go import ( - "encoding/json" - "io/ioutil" - "strings" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" @@ -18,17 +14,26 @@ import ( var emptyJSON = []byte("{}") -// BuildHandler is a named request handler for building jsonrpc protocol requests -var BuildHandler = request.NamedHandler{Name: "awssdk.jsonrpc.Build", Fn: Build} - -// UnmarshalHandler is a named request handler for unmarshaling jsonrpc protocol requests -var UnmarshalHandler = request.NamedHandler{Name: "awssdk.jsonrpc.Unmarshal", Fn: Unmarshal} +// BuildHandler is a named request handler for building jsonrpc protocol +// requests +var BuildHandler = request.NamedHandler{ + Name: "awssdk.jsonrpc.Build", + Fn: Build, +} -// UnmarshalMetaHandler is a named request handler for unmarshaling jsonrpc protocol request metadata -var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.jsonrpc.UnmarshalMeta", Fn: UnmarshalMeta} +// UnmarshalHandler is a named request handler for unmarshaling jsonrpc +// protocol requests +var UnmarshalHandler = request.NamedHandler{ + Name: "awssdk.jsonrpc.Unmarshal", + Fn: Unmarshal, +} -// UnmarshalErrorHandler is a named request handler for unmarshaling jsonrpc protocol request errors -var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.jsonrpc.UnmarshalError", Fn: UnmarshalError} +// UnmarshalMetaHandler is a named request handler for unmarshaling jsonrpc +// protocol request metadata +var UnmarshalMetaHandler = request.NamedHandler{ + Name: "awssdk.jsonrpc.UnmarshalMeta", + Fn: UnmarshalMeta, +} // Build builds a JSON payload for a JSON RPC request. func Build(req *request.Request) { @@ -37,7 +42,7 @@ func Build(req *request.Request) { if req.ParamsFilled() { buf, err = jsonutil.BuildJSON(req.Params) if err != nil { - req.Error = awserr.New("SerializationError", "failed encoding JSON RPC request", err) + req.Error = awserr.New(request.ErrCodeSerialization, "failed encoding JSON RPC request", err) return } } else { @@ -52,9 +57,12 @@ func Build(req *request.Request) { target := req.ClientInfo.TargetPrefix + "." + req.Operation.Name req.HTTPRequest.Header.Add("X-Amz-Target", target) } - if req.ClientInfo.JSONVersion != "" { + + // Only set the content type if one is not already specified and an + // JSONVersion is specified. + if ct, v := req.HTTPRequest.Header.Get("Content-Type"), req.ClientInfo.JSONVersion; len(ct) == 0 && len(v) != 0 { jsonVersion := req.ClientInfo.JSONVersion - req.HTTPRequest.Header.Add("Content-Type", "application/x-amz-json-"+jsonVersion) + req.HTTPRequest.Header.Set("Content-Type", "application/x-amz-json-"+jsonVersion) } } @@ -64,7 +72,11 @@ func Unmarshal(req *request.Request) { if req.DataFilled() { err := jsonutil.UnmarshalJSON(req.Data, req.HTTPResponse.Body) if err != nil { - req.Error = awserr.New("SerializationError", "failed decoding JSON RPC response", err) + req.Error = awserr.NewRequestFailure( + awserr.New(request.ErrCodeSerialization, "failed decoding JSON RPC response", err), + req.HTTPResponse.StatusCode, + req.RequestID, + ) } } return @@ -74,38 +86,3 @@ func Unmarshal(req *request.Request) { func UnmarshalMeta(req *request.Request) { rest.UnmarshalMeta(req) } - -// UnmarshalError unmarshals an error response for a JSON RPC service. -func UnmarshalError(req *request.Request) { - defer req.HTTPResponse.Body.Close() - bodyBytes, err := ioutil.ReadAll(req.HTTPResponse.Body) - if err != nil { - req.Error = awserr.New("SerializationError", "failed reading JSON RPC error response", err) - return - } - if len(bodyBytes) == 0 { - req.Error = awserr.NewRequestFailure( - awserr.New("SerializationError", req.HTTPResponse.Status, nil), - req.HTTPResponse.StatusCode, - "", - ) - return - } - var jsonErr jsonErrorResponse - if err := json.Unmarshal(bodyBytes, &jsonErr); err != nil { - req.Error = awserr.New("SerializationError", "failed decoding JSON RPC error response", err) - return - } - - codes := strings.SplitN(jsonErr.Code, "#", 2) - req.Error = awserr.NewRequestFailure( - awserr.New(codes[len(codes)-1], jsonErr.Message, nil), - req.HTTPResponse.StatusCode, - req.RequestID, - ) -} - -type jsonErrorResponse struct { - Code string `json:"__type"` - Message string `json:"message"` -} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/unmarshal_error.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/unmarshal_error.go new file mode 100644 index 0000000000000..c0c52e2db0f37 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/unmarshal_error.go @@ -0,0 +1,107 @@ +package jsonrpc + +import ( + "bytes" + "io" + "io/ioutil" + "net/http" + "strings" + + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/private/protocol" + "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" +) + +// UnmarshalTypedError provides unmarshaling errors API response errors +// for both typed and untyped errors. +type UnmarshalTypedError struct { + exceptions map[string]func(protocol.ResponseMetadata) error +} + +// NewUnmarshalTypedError returns an UnmarshalTypedError initialized for the +// set of exception names to the error unmarshalers +func NewUnmarshalTypedError(exceptions map[string]func(protocol.ResponseMetadata) error) *UnmarshalTypedError { + return &UnmarshalTypedError{ + exceptions: exceptions, + } +} + +// UnmarshalError attempts to unmarshal the HTTP response error as a known +// error type. If unable to unmarshal the error type, the generic SDK error +// type will be used. +func (u *UnmarshalTypedError) UnmarshalError( + resp *http.Response, + respMeta protocol.ResponseMetadata, +) (error, error) { + + var buf bytes.Buffer + var jsonErr jsonErrorResponse + teeReader := io.TeeReader(resp.Body, &buf) + err := jsonutil.UnmarshalJSONError(&jsonErr, teeReader) + if err != nil { + return nil, err + } + body := ioutil.NopCloser(&buf) + + // Code may be separated by hash(#), with the last element being the code + // used by the SDK. + codeParts := strings.SplitN(jsonErr.Code, "#", 2) + code := codeParts[len(codeParts)-1] + msg := jsonErr.Message + + if fn, ok := u.exceptions[code]; ok { + // If exception code is know, use associated constructor to get a value + // for the exception that the JSON body can be unmarshaled into. + v := fn(respMeta) + err := jsonutil.UnmarshalJSONCaseInsensitive(v, body) + if err != nil { + return nil, err + } + + return v, nil + } + + // fallback to unmodeled generic exceptions + return awserr.NewRequestFailure( + awserr.New(code, msg, nil), + respMeta.StatusCode, + respMeta.RequestID, + ), nil +} + +// UnmarshalErrorHandler is a named request handler for unmarshaling jsonrpc +// protocol request errors +var UnmarshalErrorHandler = request.NamedHandler{ + Name: "awssdk.jsonrpc.UnmarshalError", + Fn: UnmarshalError, +} + +// UnmarshalError unmarshals an error response for a JSON RPC service. +func UnmarshalError(req *request.Request) { + defer req.HTTPResponse.Body.Close() + + var jsonErr jsonErrorResponse + err := jsonutil.UnmarshalJSONError(&jsonErr, req.HTTPResponse.Body) + if err != nil { + req.Error = awserr.NewRequestFailure( + awserr.New(request.ErrCodeSerialization, + "failed to unmarshal error message", err), + req.HTTPResponse.StatusCode, + req.RequestID, + ) + return + } + + codes := strings.SplitN(jsonErr.Code, "#", 2) + req.Error = awserr.NewRequestFailure( + awserr.New(codes[len(codes)-1], jsonErr.Message, nil), + req.HTTPResponse.StatusCode, + req.RequestID, + ) +} + +type jsonErrorResponse struct { + Code string `json:"__type"` + Message string `json:"message"` +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/payload.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/payload.go new file mode 100644 index 0000000000000..0ea0647a57dff --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/payload.go @@ -0,0 +1,81 @@ +package protocol + +import ( + "io" + "io/ioutil" + "net/http" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/client/metadata" + "github.com/aws/aws-sdk-go/aws/request" +) + +// PayloadUnmarshaler provides the interface for unmarshaling a payload's +// reader into a SDK shape. +type PayloadUnmarshaler interface { + UnmarshalPayload(io.Reader, interface{}) error +} + +// HandlerPayloadUnmarshal implements the PayloadUnmarshaler from a +// HandlerList. This provides the support for unmarshaling a payload reader to +// a shape without needing a SDK request first. +type HandlerPayloadUnmarshal struct { + Unmarshalers request.HandlerList +} + +// UnmarshalPayload unmarshals the io.Reader payload into the SDK shape using +// the Unmarshalers HandlerList provided. Returns an error if unable +// unmarshaling fails. +func (h HandlerPayloadUnmarshal) UnmarshalPayload(r io.Reader, v interface{}) error { + req := &request.Request{ + HTTPRequest: &http.Request{}, + HTTPResponse: &http.Response{ + StatusCode: 200, + Header: http.Header{}, + Body: ioutil.NopCloser(r), + }, + Data: v, + } + + h.Unmarshalers.Run(req) + + return req.Error +} + +// PayloadMarshaler provides the interface for marshaling a SDK shape into and +// io.Writer. +type PayloadMarshaler interface { + MarshalPayload(io.Writer, interface{}) error +} + +// HandlerPayloadMarshal implements the PayloadMarshaler from a HandlerList. +// This provides support for marshaling a SDK shape into an io.Writer without +// needing a SDK request first. +type HandlerPayloadMarshal struct { + Marshalers request.HandlerList +} + +// MarshalPayload marshals the SDK shape into the io.Writer using the +// Marshalers HandlerList provided. Returns an error if unable if marshal +// fails. +func (h HandlerPayloadMarshal) MarshalPayload(w io.Writer, v interface{}) error { + req := request.New( + aws.Config{}, + metadata.ClientInfo{}, + request.Handlers{}, + nil, + &request.Operation{HTTPMethod: "PUT"}, + v, + nil, + ) + + h.Marshalers.Run(req) + + if req.Error != nil { + return req.Error + } + + io.Copy(w, req.GetBody()) + + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/protocol.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/protocol.go new file mode 100644 index 0000000000000..9d521dcb950ae --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/protocol.go @@ -0,0 +1,49 @@ +package protocol + +import ( + "fmt" + "strings" + + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/request" +) + +// RequireHTTPMinProtocol request handler is used to enforce that +// the target endpoint supports the given major and minor HTTP protocol version. +type RequireHTTPMinProtocol struct { + Major, Minor int +} + +// Handler will mark the request.Request with an error if the +// target endpoint did not connect with the required HTTP protocol +// major and minor version. +func (p RequireHTTPMinProtocol) Handler(r *request.Request) { + if r.Error != nil || r.HTTPResponse == nil { + return + } + + if !strings.HasPrefix(r.HTTPResponse.Proto, "HTTP") { + r.Error = newMinHTTPProtoError(p.Major, p.Minor, r) + } + + if r.HTTPResponse.ProtoMajor < p.Major || r.HTTPResponse.ProtoMinor < p.Minor { + r.Error = newMinHTTPProtoError(p.Major, p.Minor, r) + } +} + +// ErrCodeMinimumHTTPProtocolError error code is returned when the target endpoint +// did not match the required HTTP major and minor protocol version. +const ErrCodeMinimumHTTPProtocolError = "MinimumHTTPProtocolError" + +func newMinHTTPProtoError(major, minor int, r *request.Request) error { + return awserr.NewRequestFailure( + awserr.New("MinimumHTTPProtocolError", + fmt.Sprintf( + "operation requires minimum HTTP protocol of HTTP/%d.%d, but was %s", + major, minor, r.HTTPResponse.Proto, + ), + nil, + ), + r.HTTPResponse.StatusCode, r.RequestID, + ) +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/query/build.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/query/build.go index 18169f0f8ce49..0cb99eb579682 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/query/build.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/query/build.go @@ -21,11 +21,11 @@ func Build(r *request.Request) { "Version": {r.ClientInfo.APIVersion}, } if err := queryutil.Parse(body, r.Params, false); err != nil { - r.Error = awserr.New("SerializationError", "failed encoding Query request", err) + r.Error = awserr.New(request.ErrCodeSerialization, "failed encoding Query request", err) return } - if r.ExpireTime == 0 { + if !r.IsPresigned() { r.HTTPRequest.Method = "POST" r.HTTPRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8") r.SetBufferBody([]byte(body.Encode())) diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/query/queryutil/queryutil.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/query/queryutil/queryutil.go index 5ce9cba329153..75866d0121842 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/query/queryutil/queryutil.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/query/queryutil/queryutil.go @@ -233,7 +233,12 @@ func (q *queryParser) parseScalar(v url.Values, r reflect.Value, name string, ta v.Set(name, strconv.FormatFloat(float64(value), 'f', -1, 32)) case time.Time: const ISO8601UTC = "2006-01-02T15:04:05Z" - v.Set(name, value.UTC().Format(ISO8601UTC)) + format := tag.Get("timestampFormat") + if len(format) == 0 { + format = protocol.ISO8601TimeFormatName + } + + v.Set(name, protocol.FormatTime(format, value)) default: return fmt.Errorf("unsupported value for param %s: %v (%s)", name, r.Interface(), r.Type().Name()) } diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal.go index e0f4d5a541941..f69c1efc93ad2 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal.go @@ -23,7 +23,11 @@ func Unmarshal(r *request.Request) { decoder := xml.NewDecoder(r.HTTPResponse.Body) err := xmlutil.UnmarshalXML(r.Data, decoder, r.Operation.Name+"Result") if err != nil { - r.Error = awserr.New("SerializationError", "failed decoding Query response", err) + r.Error = awserr.NewRequestFailure( + awserr.New(request.ErrCodeSerialization, "failed decoding Query response", err), + r.HTTPResponse.StatusCode, + r.RequestID, + ) return } } diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal_error.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal_error.go index f2142961717e0..831b0110c54bd 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal_error.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/query/unmarshal_error.go @@ -2,65 +2,68 @@ package query import ( "encoding/xml" - "io/ioutil" + "fmt" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil" ) +// UnmarshalErrorHandler is a name request handler to unmarshal request errors +var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.query.UnmarshalError", Fn: UnmarshalError} + type xmlErrorResponse struct { - XMLName xml.Name `xml:"ErrorResponse"` - Code string `xml:"Error>Code"` - Message string `xml:"Error>Message"` - RequestID string `xml:"RequestId"` + Code string `xml:"Error>Code"` + Message string `xml:"Error>Message"` + RequestID string `xml:"RequestId"` } -type xmlServiceUnavailableResponse struct { - XMLName xml.Name `xml:"ServiceUnavailableException"` +type xmlResponseError struct { + xmlErrorResponse } -// UnmarshalErrorHandler is a name request handler to unmarshal request errors -var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.query.UnmarshalError", Fn: UnmarshalError} +func (e *xmlResponseError) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + const svcUnavailableTagName = "ServiceUnavailableException" + const errorResponseTagName = "ErrorResponse" + + switch start.Name.Local { + case svcUnavailableTagName: + e.Code = svcUnavailableTagName + e.Message = "service is unavailable" + return d.Skip() + + case errorResponseTagName: + return d.DecodeElement(&e.xmlErrorResponse, &start) + + default: + return fmt.Errorf("unknown error response tag, %v", start) + } +} // UnmarshalError unmarshals an error response for an AWS Query service. func UnmarshalError(r *request.Request) { defer r.HTTPResponse.Body.Close() - bodyBytes, err := ioutil.ReadAll(r.HTTPResponse.Body) + var respErr xmlResponseError + err := xmlutil.UnmarshalXMLError(&respErr, r.HTTPResponse.Body) if err != nil { - r.Error = awserr.New("SerializationError", "failed to read from query HTTP response body", err) - return - } - - // First check for specific error - resp := xmlErrorResponse{} - decodeErr := xml.Unmarshal(bodyBytes, &resp) - if decodeErr == nil { - reqID := resp.RequestID - if reqID == "" { - reqID = r.RequestID - } r.Error = awserr.NewRequestFailure( - awserr.New(resp.Code, resp.Message, nil), + awserr.New(request.ErrCodeSerialization, + "failed to unmarshal error message", err), r.HTTPResponse.StatusCode, - reqID, + r.RequestID, ) return } - // Check for unhandled error - servUnavailResp := xmlServiceUnavailableResponse{} - unavailErr := xml.Unmarshal(bodyBytes, &servUnavailResp) - if unavailErr == nil { - r.Error = awserr.NewRequestFailure( - awserr.New("ServiceUnavailableException", "service is unavailable", nil), - r.HTTPResponse.StatusCode, - r.RequestID, - ) - return + reqID := respErr.RequestID + if len(reqID) == 0 { + reqID = r.RequestID } - // Failed to retrieve any error message from the response body - r.Error = awserr.New("SerializationError", - "failed to decode query XML error response", decodeErr) + r.Error = awserr.NewRequestFailure( + awserr.New(respErr.Code, respErr.Message, nil), + r.HTTPResponse.StatusCode, + reqID, + ) } diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/build.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/build.go index c405288d74233..1301b149d35e8 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/build.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/build.go @@ -20,14 +20,13 @@ import ( "github.com/aws/aws-sdk-go/private/protocol" ) -// RFC822 returns an RFC822 formatted timestamp for AWS protocols -const RFC822 = "Mon, 2 Jan 2006 15:04:05 GMT" - // Whether the byte value can be sent without escaping in AWS URLs var noEscape [256]bool var errValueNotSet = fmt.Errorf("value not set") +var byteSliceType = reflect.TypeOf([]byte{}) + func init() { for i := 0; i < len(noEscape); i++ { // AWS expects every character except these to be escaped @@ -97,6 +96,14 @@ func buildLocationElements(r *request.Request, v reflect.Value, buildGETQuery bo continue } + // Support the ability to customize values to be marshaled as a + // blob even though they were modeled as a string. Required for S3 + // API operations like SSECustomerKey is modeled as stirng but + // required to be base64 encoded in request. + if field.Tag.Get("marshal-as") == "blob" { + m = m.Convert(byteSliceType) + } + var err error switch field.Tag.Get("location") { case "headers": // header maps @@ -140,7 +147,7 @@ func buildBody(r *request.Request, v reflect.Value) { case string: r.SetStringBody(reader) default: - r.Error = awserr.New("SerializationError", + r.Error = awserr.New(request.ErrCodeSerialization, "failed to encode REST request", fmt.Errorf("unknown payload type %s", payload.Type())) } @@ -155,9 +162,12 @@ func buildHeader(header *http.Header, v reflect.Value, name string, tag reflect. if err == errValueNotSet { return nil } else if err != nil { - return awserr.New("SerializationError", "failed to encode REST request", err) + return awserr.New(request.ErrCodeSerialization, "failed to encode REST request", err) } + name = strings.TrimSpace(name) + str = strings.TrimSpace(str) + header.Add(name, str) return nil @@ -170,11 +180,13 @@ func buildHeaderMap(header *http.Header, v reflect.Value, tag reflect.StructTag) if err == errValueNotSet { continue } else if err != nil { - return awserr.New("SerializationError", "failed to encode REST request", err) + return awserr.New(request.ErrCodeSerialization, "failed to encode REST request", err) } + keyStr := strings.TrimSpace(key.String()) + str = strings.TrimSpace(str) - header.Add(prefix+key.String(), str) + header.Add(prefix+keyStr, str) } return nil } @@ -184,7 +196,7 @@ func buildURI(u *url.URL, v reflect.Value, name string, tag reflect.StructTag) e if err == errValueNotSet { return nil } else if err != nil { - return awserr.New("SerializationError", "failed to encode REST request", err) + return awserr.New(request.ErrCodeSerialization, "failed to encode REST request", err) } u.Path = strings.Replace(u.Path, "{"+name+"}", value, -1) @@ -217,7 +229,7 @@ func buildQueryString(query url.Values, v reflect.Value, name string, tag reflec if err == errValueNotSet { return nil } else if err != nil { - return awserr.New("SerializationError", "failed to encode REST request", err) + return awserr.New(request.ErrCodeSerialization, "failed to encode REST request", err) } query.Set(name, str) } @@ -270,7 +282,14 @@ func convertType(v reflect.Value, tag reflect.StructTag) (str string, err error) case float64: str = strconv.FormatFloat(value, 'f', -1, 64) case time.Time: - str = value.UTC().Format(RFC822) + format := tag.Get("timestampFormat") + if len(format) == 0 { + format = protocol.RFC822TimeFormatName + if tag.Get("location") == "querystring" { + format = protocol.ISO8601TimeFormatName + } + } + str = protocol.FormatTime(format, value) case aws.JSONValue: if len(value) == 0 { return "", errValueNotSet diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/unmarshal.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/unmarshal.go index 823f045eed798..92f8b4d9a48a6 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/unmarshal.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/rest/unmarshal.go @@ -15,6 +15,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/request" + awsStrings "github.com/aws/aws-sdk-go/internal/strings" "github.com/aws/aws-sdk-go/private/protocol" ) @@ -28,7 +29,9 @@ var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.rest.UnmarshalMeta func Unmarshal(r *request.Request) { if r.DataFilled() { v := reflect.Indirect(reflect.ValueOf(r.Data)) - unmarshalBody(r, v) + if err := unmarshalBody(r, v); err != nil { + r.Error = err + } } } @@ -40,12 +43,21 @@ func UnmarshalMeta(r *request.Request) { r.RequestID = r.HTTPResponse.Header.Get("X-Amz-Request-Id") } if r.DataFilled() { - v := reflect.Indirect(reflect.ValueOf(r.Data)) - unmarshalLocationElements(r, v) + if err := UnmarshalResponse(r.HTTPResponse, r.Data, aws.BoolValue(r.Config.LowerCaseHeaderMaps)); err != nil { + r.Error = err + } } } -func unmarshalBody(r *request.Request, v reflect.Value) { +// UnmarshalResponse attempts to unmarshal the REST response headers to +// the data type passed in. The type must be a pointer. An error is returned +// with any error unmarshaling the response into the target datatype. +func UnmarshalResponse(resp *http.Response, data interface{}, lowerCaseHeaderMaps bool) error { + v := reflect.Indirect(reflect.ValueOf(data)) + return unmarshalLocationElements(resp, v, lowerCaseHeaderMaps) +} + +func unmarshalBody(r *request.Request, v reflect.Value) error { if field, ok := v.Type().FieldByName("_"); ok { if payloadName := field.Tag.Get("payload"); payloadName != "" { pfield, _ := v.Type().FieldByName(payloadName) @@ -57,35 +69,38 @@ func unmarshalBody(r *request.Request, v reflect.Value) { defer r.HTTPResponse.Body.Close() b, err := ioutil.ReadAll(r.HTTPResponse.Body) if err != nil { - r.Error = awserr.New("SerializationError", "failed to decode REST response", err) - } else { - payload.Set(reflect.ValueOf(b)) + return awserr.New(request.ErrCodeSerialization, "failed to decode REST response", err) } + + payload.Set(reflect.ValueOf(b)) + case *string: defer r.HTTPResponse.Body.Close() b, err := ioutil.ReadAll(r.HTTPResponse.Body) if err != nil { - r.Error = awserr.New("SerializationError", "failed to decode REST response", err) - } else { - str := string(b) - payload.Set(reflect.ValueOf(&str)) + return awserr.New(request.ErrCodeSerialization, "failed to decode REST response", err) } + + str := string(b) + payload.Set(reflect.ValueOf(&str)) + default: switch payload.Type().String() { case "io.ReadCloser": payload.Set(reflect.ValueOf(r.HTTPResponse.Body)) + case "io.ReadSeeker": b, err := ioutil.ReadAll(r.HTTPResponse.Body) if err != nil { - r.Error = awserr.New("SerializationError", + return awserr.New(request.ErrCodeSerialization, "failed to read response body", err) - return } payload.Set(reflect.ValueOf(ioutil.NopCloser(bytes.NewReader(b)))) + default: io.Copy(ioutil.Discard, r.HTTPResponse.Body) - defer r.HTTPResponse.Body.Close() - r.Error = awserr.New("SerializationError", + r.HTTPResponse.Body.Close() + return awserr.New(request.ErrCodeSerialization, "failed to decode REST response", fmt.Errorf("unknown payload type %s", payload.Type())) } @@ -94,9 +109,11 @@ func unmarshalBody(r *request.Request, v reflect.Value) { } } } + + return nil } -func unmarshalLocationElements(r *request.Request, v reflect.Value) { +func unmarshalLocationElements(resp *http.Response, v reflect.Value, lowerCaseHeaderMaps bool) error { for i := 0; i < v.NumField(); i++ { m, field := v.Field(i), v.Type().Field(i) if n := field.Name; n[0:1] == strings.ToLower(n[0:1]) { @@ -111,26 +128,25 @@ func unmarshalLocationElements(r *request.Request, v reflect.Value) { switch field.Tag.Get("location") { case "statusCode": - unmarshalStatusCode(m, r.HTTPResponse.StatusCode) + unmarshalStatusCode(m, resp.StatusCode) + case "header": - err := unmarshalHeader(m, r.HTTPResponse.Header.Get(name), field.Tag) + err := unmarshalHeader(m, resp.Header.Get(name), field.Tag) if err != nil { - r.Error = awserr.New("SerializationError", "failed to decode REST response", err) - break + return awserr.New(request.ErrCodeSerialization, "failed to decode REST response", err) } + case "headers": prefix := field.Tag.Get("locationName") - err := unmarshalHeaderMap(m, r.HTTPResponse.Header, prefix) + err := unmarshalHeaderMap(m, resp.Header, prefix, lowerCaseHeaderMaps) if err != nil { - r.Error = awserr.New("SerializationError", "failed to decode REST response", err) - break + awserr.New(request.ErrCodeSerialization, "failed to decode REST response", err) } } } - if r.Error != nil { - return - } } + + return nil } func unmarshalStatusCode(v reflect.Value, statusCode int) { @@ -145,29 +161,45 @@ func unmarshalStatusCode(v reflect.Value, statusCode int) { } } -func unmarshalHeaderMap(r reflect.Value, headers http.Header, prefix string) error { +func unmarshalHeaderMap(r reflect.Value, headers http.Header, prefix string, normalize bool) error { + if len(headers) == 0 { + return nil + } switch r.Interface().(type) { case map[string]*string: // we only support string map value types out := map[string]*string{} for k, v := range headers { - k = http.CanonicalHeaderKey(k) - if strings.HasPrefix(strings.ToLower(k), strings.ToLower(prefix)) { + if awsStrings.HasPrefixFold(k, prefix) { + if normalize == true { + k = strings.ToLower(k) + } else { + k = http.CanonicalHeaderKey(k) + } out[k[len(prefix):]] = &v[0] } } - r.Set(reflect.ValueOf(out)) + if len(out) != 0 { + r.Set(reflect.ValueOf(out)) + } + } return nil } func unmarshalHeader(v reflect.Value, header string, tag reflect.StructTag) error { - isJSONValue := tag.Get("type") == "jsonvalue" - if isJSONValue { + switch tag.Get("type") { + case "jsonvalue": if len(header) == 0 { return nil } - } else if !v.IsValid() || (header == "" && v.Elem().Kind() != reflect.String) { - return nil + case "blob": + if len(header) == 0 { + return nil + } + default: + if !v.IsValid() || (header == "" && v.Elem().Kind() != reflect.String) { + return nil + } } switch v.Interface().(type) { @@ -178,7 +210,7 @@ func unmarshalHeader(v reflect.Value, header string, tag reflect.StructTag) erro if err != nil { return err } - v.Set(reflect.ValueOf(&b)) + v.Set(reflect.ValueOf(b)) case *bool: b, err := strconv.ParseBool(header) if err != nil { @@ -198,7 +230,11 @@ func unmarshalHeader(v reflect.Value, header string, tag reflect.StructTag) erro } v.Set(reflect.ValueOf(&f)) case *time.Time: - t, err := time.Parse(RFC822, header) + format := tag.Get("timestampFormat") + if len(format) == 0 { + format = protocol.RFC822TimeFormatName + } + t, err := protocol.ParseTime(format, header) if err != nil { return err } diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/timestamp.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/timestamp.go new file mode 100644 index 0000000000000..05d4ff5192583 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/timestamp.go @@ -0,0 +1,84 @@ +package protocol + +import ( + "math" + "strconv" + "time" + + "github.com/aws/aws-sdk-go/internal/sdkmath" +) + +// Names of time formats supported by the SDK +const ( + RFC822TimeFormatName = "rfc822" + ISO8601TimeFormatName = "iso8601" + UnixTimeFormatName = "unixTimestamp" +) + +// Time formats supported by the SDK +// Output time is intended to not contain decimals +const ( + // RFC 7231#section-7.1.1.1 timetamp format. e.g Tue, 29 Apr 2014 18:30:38 GMT + RFC822TimeFormat = "Mon, 2 Jan 2006 15:04:05 GMT" + + // This format is used for output time without seconds precision + RFC822OutputTimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" + + // RFC3339 a subset of the ISO8601 timestamp format. e.g 2014-04-29T18:30:38Z + ISO8601TimeFormat = "2006-01-02T15:04:05.999999999Z" + + // This format is used for output time without seconds precision + ISO8601OutputTimeFormat = "2006-01-02T15:04:05Z" +) + +// IsKnownTimestampFormat returns if the timestamp format name +// is know to the SDK's protocols. +func IsKnownTimestampFormat(name string) bool { + switch name { + case RFC822TimeFormatName: + fallthrough + case ISO8601TimeFormatName: + fallthrough + case UnixTimeFormatName: + return true + default: + return false + } +} + +// FormatTime returns a string value of the time. +func FormatTime(name string, t time.Time) string { + t = t.UTC() + + switch name { + case RFC822TimeFormatName: + return t.Format(RFC822OutputTimeFormat) + case ISO8601TimeFormatName: + return t.Format(ISO8601OutputTimeFormat) + case UnixTimeFormatName: + return strconv.FormatInt(t.Unix(), 10) + default: + panic("unknown timestamp format name, " + name) + } +} + +// ParseTime attempts to parse the time given the format. Returns +// the time if it was able to be parsed, and fails otherwise. +func ParseTime(formatName, value string) (time.Time, error) { + switch formatName { + case RFC822TimeFormatName: + return time.Parse(RFC822TimeFormat, value) + case ISO8601TimeFormatName: + return time.Parse(ISO8601TimeFormat, value) + case UnixTimeFormatName: + v, err := strconv.ParseFloat(value, 64) + _, dec := math.Modf(v) + dec = sdkmath.Round(dec*1e3) / 1e3 //Rounds 0.1229999 to 0.123 + if err != nil { + return time.Time{}, err + } + return time.Unix(int64(v), int64(dec*(1e9))), nil + default: + panic("unknown timestamp format name, " + formatName) + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/unmarshal.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/unmarshal.go index da1a68111db49..f614ef898be87 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/unmarshal.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/unmarshal.go @@ -19,3 +19,9 @@ func UnmarshalDiscardBody(r *request.Request) { io.Copy(ioutil.Discard, r.HTTPResponse.Body) r.HTTPResponse.Body.Close() } + +// ResponseMetadata provides the SDK response metadata attributes. +type ResponseMetadata struct { + StatusCode int + RequestID string +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/unmarshal_error.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/unmarshal_error.go new file mode 100644 index 0000000000000..cc857f136c53c --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/unmarshal_error.go @@ -0,0 +1,65 @@ +package protocol + +import ( + "net/http" + + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/request" +) + +// UnmarshalErrorHandler provides unmarshaling errors API response errors for +// both typed and untyped errors. +type UnmarshalErrorHandler struct { + unmarshaler ErrorUnmarshaler +} + +// ErrorUnmarshaler is an abstract interface for concrete implementations to +// unmarshal protocol specific response errors. +type ErrorUnmarshaler interface { + UnmarshalError(*http.Response, ResponseMetadata) (error, error) +} + +// NewUnmarshalErrorHandler returns an UnmarshalErrorHandler +// initialized for the set of exception names to the error unmarshalers +func NewUnmarshalErrorHandler(unmarshaler ErrorUnmarshaler) *UnmarshalErrorHandler { + return &UnmarshalErrorHandler{ + unmarshaler: unmarshaler, + } +} + +// UnmarshalErrorHandlerName is the name of the named handler. +const UnmarshalErrorHandlerName = "awssdk.protocol.UnmarshalError" + +// NamedHandler returns a NamedHandler for the unmarshaler using the set of +// errors the unmarshaler was initialized for. +func (u *UnmarshalErrorHandler) NamedHandler() request.NamedHandler { + return request.NamedHandler{ + Name: UnmarshalErrorHandlerName, + Fn: u.UnmarshalError, + } +} + +// UnmarshalError will attempt to unmarshal the API response's error message +// into either a generic SDK error type, or a typed error corresponding to the +// errors exception name. +func (u *UnmarshalErrorHandler) UnmarshalError(r *request.Request) { + defer r.HTTPResponse.Body.Close() + + respMeta := ResponseMetadata{ + StatusCode: r.HTTPResponse.StatusCode, + RequestID: r.RequestID, + } + + v, err := u.unmarshaler.UnmarshalError(r.HTTPResponse, respMeta) + if err != nil { + r.Error = awserr.NewRequestFailure( + awserr.New(request.ErrCodeSerialization, + "failed to unmarshal response error", err), + respMeta.StatusCode, + respMeta.RequestID, + ) + return + } + + r.Error = v +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/build.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/build.go index 7091b456d180a..cf981fe951324 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/build.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/build.go @@ -13,9 +13,13 @@ import ( "github.com/aws/aws-sdk-go/private/protocol" ) -// BuildXML will serialize params into an xml.Encoder. -// Error will be returned if the serialization of any of the params or nested values fails. +// BuildXML will serialize params into an xml.Encoder. Error will be returned +// if the serialization of any of the params or nested values fails. func BuildXML(params interface{}, e *xml.Encoder) error { + return buildXML(params, e, false) +} + +func buildXML(params interface{}, e *xml.Encoder, sorted bool) error { b := xmlBuilder{encoder: e, namespaces: map[string]string{}} root := NewXMLElement(xml.Name{}) if err := b.buildValue(reflect.ValueOf(params), root, ""); err != nil { @@ -23,7 +27,7 @@ func BuildXML(params interface{}, e *xml.Encoder) error { } for _, c := range root.Children { for _, v := range c { - return StructToXML(e, v, false) + return StructToXML(e, v, sorted) } } return nil @@ -83,15 +87,13 @@ func (b *xmlBuilder) buildValue(value reflect.Value, current *XMLNode, tag refle } } -// buildStruct adds a struct and its fields to the current XMLNode. All fields any any nested +// buildStruct adds a struct and its fields to the current XMLNode. All fields and any nested // types are converted to XMLNodes also. func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag reflect.StructTag) error { if !value.IsValid() { return nil } - fieldAdded := false - // unwrap payloads if payload := tag.Get("payload"); payload != "" { field, _ := value.Type().FieldByName(payload) @@ -119,6 +121,8 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl child.Attr = append(child.Attr, ns) } + var payloadFields, nonPayloadFields int + t := value.Type() for i := 0; i < value.NumField(); i++ { member := elemOf(value.Field(i)) @@ -133,8 +137,10 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl mTag := field.Tag if mTag.Get("location") != "" { // skip non-body members + nonPayloadFields++ continue } + payloadFields++ if protocol.CanSetIdempotencyToken(value.Field(i), field) { token := protocol.GetIdempotencyToken() @@ -149,11 +155,11 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl if err := b.buildValue(member, child, mTag); err != nil { return err } - - fieldAdded = true } - if fieldAdded { // only append this child if we have one ore more valid members + // Only case where the child shape is not added is if the shape only contains + // non-payload fields, e.g headers/query. + if !(payloadFields == 0 && nonPayloadFields > 0) { current.AddChild(child) } @@ -278,8 +284,12 @@ func (b *xmlBuilder) buildScalar(value reflect.Value, current *XMLNode, tag refl case float32: str = strconv.FormatFloat(float64(converted), 'f', -1, 32) case time.Time: - const ISO8601UTC = "2006-01-02T15:04:05Z" - str = converted.UTC().Format(ISO8601UTC) + format := tag.Get("timestampFormat") + if len(format) == 0 { + format = protocol.ISO8601TimeFormatName + } + + str = protocol.FormatTime(format, converted) default: return fmt.Errorf("unsupported value for param %s: %v (%s)", tag.Get("locationName"), value.Interface(), value.Type().Name()) diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/sort.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/sort.go new file mode 100644 index 0000000000000..c1a511851f6ee --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/sort.go @@ -0,0 +1,32 @@ +package xmlutil + +import ( + "encoding/xml" + "strings" +) + +type xmlAttrSlice []xml.Attr + +func (x xmlAttrSlice) Len() int { + return len(x) +} + +func (x xmlAttrSlice) Less(i, j int) bool { + spaceI, spaceJ := x[i].Name.Space, x[j].Name.Space + localI, localJ := x[i].Name.Local, x[j].Name.Local + valueI, valueJ := x[i].Value, x[j].Value + + spaceCmp := strings.Compare(spaceI, spaceJ) + localCmp := strings.Compare(localI, localJ) + valueCmp := strings.Compare(valueI, valueJ) + + if spaceCmp == -1 || (spaceCmp == 0 && (localCmp == -1 || (localCmp == 0 && valueCmp == -1))) { + return true + } + + return false +} + +func (x xmlAttrSlice) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/unmarshal.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/unmarshal.go index 87584628a2b22..7108d38009371 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/unmarshal.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/unmarshal.go @@ -1,6 +1,7 @@ package xmlutil import ( + "bytes" "encoding/base64" "encoding/xml" "fmt" @@ -9,8 +10,28 @@ import ( "strconv" "strings" "time" + + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/private/protocol" ) +// UnmarshalXMLError unmarshals the XML error from the stream into the value +// type specified. The value must be a pointer. If the message fails to +// unmarshal, the message content will be included in the returned error as a +// awserr.UnmarshalError. +func UnmarshalXMLError(v interface{}, stream io.Reader) error { + var errBuf bytes.Buffer + body := io.TeeReader(stream, &errBuf) + + err := xml.NewDecoder(body).Decode(v) + if err != nil && err != io.EOF { + return awserr.NewUnmarshalError(err, + "failed to unmarshal error message", errBuf.Bytes()) + } + + return nil +} + // UnmarshalXML deserializes an xml.Decoder into the container v. V // needs to match the shape of the XML expected to be decoded. // If the shape doesn't match unmarshaling will fail. @@ -52,9 +73,15 @@ func parse(r reflect.Value, node *XMLNode, tag reflect.StructTag) error { if t == "" { switch rtype.Kind() { case reflect.Struct: - t = "structure" + // also it can't be a time object + if _, ok := r.Interface().(*time.Time); !ok { + t = "structure" + } case reflect.Slice: - t = "list" + // also it can't be a byte slice + if _, ok := r.Interface().([]byte); !ok { + t = "list" + } case reflect.Map: t = "map" } @@ -247,8 +274,12 @@ func parseScalar(r reflect.Value, node *XMLNode, tag reflect.StructTag) error { } r.Set(reflect.ValueOf(&v)) case *time.Time: - const ISO8601UTC = "2006-01-02T15:04:05Z" - t, err := time.Parse(ISO8601UTC, node.Text) + format := tag.Get("timestampFormat") + if len(format) == 0 { + format = protocol.ISO8601TimeFormatName + } + + t, err := protocol.ParseTime(format, node.Text) if err != nil { return err } diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/xml_to_struct.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/xml_to_struct.go index 3e970b629daef..42f71648eee31 100644 --- a/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/xml_to_struct.go +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil/xml_to_struct.go @@ -29,6 +29,7 @@ func NewXMLElement(name xml.Name) *XMLNode { // AddChild adds child to the XMLNode. func (n *XMLNode) AddChild(child *XMLNode) { + child.parent = n if _, ok := n.Children[child.Name.Local]; !ok { n.Children[child.Name.Local] = []*XMLNode{} } @@ -118,7 +119,18 @@ func (n *XMLNode) findElem(name string) (string, bool) { // StructToXML writes an XMLNode to a xml.Encoder as tokens. func StructToXML(e *xml.Encoder, node *XMLNode, sorted bool) error { - e.EncodeToken(xml.StartElement{Name: node.Name, Attr: node.Attr}) + // Sort Attributes + attrs := node.Attr + if sorted { + sortedAttrs := make([]xml.Attr, len(attrs)) + for _, k := range node.Attr { + sortedAttrs = append(sortedAttrs, k) + } + sort.Sort(xmlAttrSlice(sortedAttrs)) + attrs = sortedAttrs + } + + e.EncodeToken(xml.StartElement{Name: node.Name, Attr: attrs}) if node.Text != "" { e.EncodeToken(xml.CharData([]byte(node.Text))) diff --git a/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/api.go b/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/api.go index 94f06d10bd486..594ec846d09f6 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/api.go @@ -16,8 +16,8 @@ const opAssociateKmsKey = "AssociateKmsKey" // AssociateKmsKeyRequest generates a "aws/request.Request" representing the // client's request for the AssociateKmsKey operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -51,8 +51,7 @@ func (c *CloudWatchLogs) AssociateKmsKeyRequest(input *AssociateKmsKeyInput) (re output = &AssociateKmsKeyOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -68,6 +67,10 @@ func (c *CloudWatchLogs) AssociateKmsKeyRequest(input *AssociateKmsKeyInput) (re // within Amazon CloudWatch Logs. This enables Amazon CloudWatch Logs to decrypt // this data whenever it is requested. // +// Important: CloudWatch Logs supports only symmetric CMKs. Do not use an associate +// an asymmetric CMK with your log group. For more information, see Using Symmetric +// and Asymmetric Keys (https://docs.aws.amazon.com/kms/latest/developerguide/symmetric-asymmetric.html). +// // Note that it can take up to 5 minutes for this operation to take effect. // // If you attempt to associate a CMK with a log group but the CMK does not exist @@ -80,17 +83,17 @@ func (c *CloudWatchLogs) AssociateKmsKeyRequest(input *AssociateKmsKeyInput) (re // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation AssociateKmsKey for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/AssociateKmsKey @@ -119,8 +122,8 @@ const opCancelExportTask = "CancelExportTask" // CancelExportTaskRequest generates a "aws/request.Request" representing the // client's request for the CancelExportTask operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -154,8 +157,7 @@ func (c *CloudWatchLogs) CancelExportTaskRequest(input *CancelExportTaskInput) ( output = &CancelExportTaskOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -172,17 +174,17 @@ func (c *CloudWatchLogs) CancelExportTaskRequest(input *CancelExportTaskInput) ( // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation CancelExportTask for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeInvalidOperationException "InvalidOperationException" +// * InvalidOperationException // The operation is not valid on the specified resource. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CancelExportTask @@ -211,8 +213,8 @@ const opCreateExportTask = "CreateExportTask" // CreateExportTaskRequest generates a "aws/request.Request" representing the // client's request for the CreateExportTask operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -264,6 +266,9 @@ func (c *CloudWatchLogs) CreateExportTaskRequest(input *CreateExportTaskInput) ( // same S3 bucket. To separate out log data for each export task, you can specify // a prefix to be used as the Amazon S3 key prefix for all exported objects. // +// Exporting to S3 buckets that are encrypted with AES-256 is supported. Exporting +// to S3 buckets encrypted with SSE-KMS is not supported. +// // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. @@ -271,23 +276,23 @@ func (c *CloudWatchLogs) CreateExportTaskRequest(input *CreateExportTaskInput) ( // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation CreateExportTask for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeLimitExceededException "LimitExceededException" +// * LimitExceededException // You have reached the maximum number of resources that can be created. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeResourceAlreadyExistsException "ResourceAlreadyExistsException" +// * ResourceAlreadyExistsException // The specified resource already exists. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CreateExportTask @@ -316,8 +321,8 @@ const opCreateLogGroup = "CreateLogGroup" // CreateLogGroupRequest generates a "aws/request.Request" representing the // client's request for the CreateLogGroup operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -351,8 +356,7 @@ func (c *CloudWatchLogs) CreateLogGroupRequest(input *CreateLogGroupInput) (req output = &CreateLogGroupOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -360,7 +364,7 @@ func (c *CloudWatchLogs) CreateLogGroupRequest(input *CreateLogGroupInput) (req // // Creates a log group with the specified name. // -// You can create up to 5000 log groups per account. +// You can create up to 20,000 log groups per account. // // You must use the following guidelines when naming a log group: // @@ -369,7 +373,8 @@ func (c *CloudWatchLogs) CreateLogGroupRequest(input *CreateLogGroupInput) (req // * Log group names can be between 1 and 512 characters long. // // * Log group names consist of the following characters: a-z, A-Z, 0-9, -// '_' (underscore), '-' (hyphen), '/' (forward slash), and '.' (period). +// '_' (underscore), '-' (hyphen), '/' (forward slash), '.' (period), and +// '#' (number sign) // // If you associate a AWS Key Management Service (AWS KMS) customer master key // (CMK) with the log group, ingested data is encrypted using the CMK. This @@ -381,6 +386,10 @@ func (c *CloudWatchLogs) CreateLogGroupRequest(input *CreateLogGroupInput) (req // exist or the CMK is disabled, you will receive an InvalidParameterException // error. // +// Important: CloudWatch Logs supports only symmetric CMKs. Do not associate +// an asymmetric CMK with your log group. For more information, see Using Symmetric +// and Asymmetric Keys (https://docs.aws.amazon.com/kms/latest/developerguide/symmetric-asymmetric.html). +// // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. @@ -388,20 +397,20 @@ func (c *CloudWatchLogs) CreateLogGroupRequest(input *CreateLogGroupInput) (req // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation CreateLogGroup for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceAlreadyExistsException "ResourceAlreadyExistsException" +// * ResourceAlreadyExistsException // The specified resource already exists. // -// * ErrCodeLimitExceededException "LimitExceededException" +// * LimitExceededException // You have reached the maximum number of resources that can be created. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CreateLogGroup @@ -430,8 +439,8 @@ const opCreateLogStream = "CreateLogStream" // CreateLogStreamRequest generates a "aws/request.Request" representing the // client's request for the CreateLogStream operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -465,8 +474,7 @@ func (c *CloudWatchLogs) CreateLogStreamRequest(input *CreateLogStreamInput) (re output = &CreateLogStreamOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -475,7 +483,8 @@ func (c *CloudWatchLogs) CreateLogStreamRequest(input *CreateLogStreamInput) (re // Creates a log stream for the specified log group. // // There is no limit on the number of log streams that you can create for a -// log group. +// log group. There is a limit of 50 TPS on CreateLogStream operations, after +// which transactions are throttled. // // You must use the following guidelines when naming a log stream: // @@ -492,17 +501,17 @@ func (c *CloudWatchLogs) CreateLogStreamRequest(input *CreateLogStreamInput) (re // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation CreateLogStream for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceAlreadyExistsException "ResourceAlreadyExistsException" +// * ResourceAlreadyExistsException // The specified resource already exists. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CreateLogStream @@ -531,8 +540,8 @@ const opDeleteDestination = "DeleteDestination" // DeleteDestinationRequest generates a "aws/request.Request" representing the // client's request for the DeleteDestination operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -566,8 +575,7 @@ func (c *CloudWatchLogs) DeleteDestinationRequest(input *DeleteDestinationInput) output = &DeleteDestinationOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -584,17 +592,17 @@ func (c *CloudWatchLogs) DeleteDestinationRequest(input *DeleteDestinationInput) // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DeleteDestination for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteDestination @@ -623,8 +631,8 @@ const opDeleteLogGroup = "DeleteLogGroup" // DeleteLogGroupRequest generates a "aws/request.Request" representing the // client's request for the DeleteLogGroup operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -658,8 +666,7 @@ func (c *CloudWatchLogs) DeleteLogGroupRequest(input *DeleteLogGroupInput) (req output = &DeleteLogGroupOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -675,17 +682,17 @@ func (c *CloudWatchLogs) DeleteLogGroupRequest(input *DeleteLogGroupInput) (req // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DeleteLogGroup for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteLogGroup @@ -714,8 +721,8 @@ const opDeleteLogStream = "DeleteLogStream" // DeleteLogStreamRequest generates a "aws/request.Request" representing the // client's request for the DeleteLogStream operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -749,8 +756,7 @@ func (c *CloudWatchLogs) DeleteLogStreamRequest(input *DeleteLogStreamInput) (re output = &DeleteLogStreamOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -766,17 +772,17 @@ func (c *CloudWatchLogs) DeleteLogStreamRequest(input *DeleteLogStreamInput) (re // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DeleteLogStream for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteLogStream @@ -805,8 +811,8 @@ const opDeleteMetricFilter = "DeleteMetricFilter" // DeleteMetricFilterRequest generates a "aws/request.Request" representing the // client's request for the DeleteMetricFilter operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -840,8 +846,7 @@ func (c *CloudWatchLogs) DeleteMetricFilterRequest(input *DeleteMetricFilterInpu output = &DeleteMetricFilterOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -856,17 +861,17 @@ func (c *CloudWatchLogs) DeleteMetricFilterRequest(input *DeleteMetricFilterInpu // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DeleteMetricFilter for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteMetricFilter @@ -895,8 +900,8 @@ const opDeleteResourcePolicy = "DeleteResourcePolicy" // DeleteResourcePolicyRequest generates a "aws/request.Request" representing the // client's request for the DeleteResourcePolicy operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -930,8 +935,7 @@ func (c *CloudWatchLogs) DeleteResourcePolicyRequest(input *DeleteResourcePolicy output = &DeleteResourcePolicyOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -947,14 +951,14 @@ func (c *CloudWatchLogs) DeleteResourcePolicyRequest(input *DeleteResourcePolicy // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DeleteResourcePolicy for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteResourcePolicy @@ -983,8 +987,8 @@ const opDeleteRetentionPolicy = "DeleteRetentionPolicy" // DeleteRetentionPolicyRequest generates a "aws/request.Request" representing the // client's request for the DeleteRetentionPolicy operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -1018,8 +1022,7 @@ func (c *CloudWatchLogs) DeleteRetentionPolicyRequest(input *DeleteRetentionPoli output = &DeleteRetentionPolicyOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -1037,17 +1040,17 @@ func (c *CloudWatchLogs) DeleteRetentionPolicyRequest(input *DeleteRetentionPoli // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DeleteRetentionPolicy for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteRetentionPolicy @@ -1076,8 +1079,8 @@ const opDeleteSubscriptionFilter = "DeleteSubscriptionFilter" // DeleteSubscriptionFilterRequest generates a "aws/request.Request" representing the // client's request for the DeleteSubscriptionFilter operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -1111,8 +1114,7 @@ func (c *CloudWatchLogs) DeleteSubscriptionFilterRequest(input *DeleteSubscripti output = &DeleteSubscriptionFilterOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -1127,17 +1129,17 @@ func (c *CloudWatchLogs) DeleteSubscriptionFilterRequest(input *DeleteSubscripti // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DeleteSubscriptionFilter for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteSubscriptionFilter @@ -1166,8 +1168,8 @@ const opDescribeDestinations = "DescribeDestinations" // DescribeDestinationsRequest generates a "aws/request.Request" representing the // client's request for the DescribeDestinations operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -1222,11 +1224,11 @@ func (c *CloudWatchLogs) DescribeDestinationsRequest(input *DescribeDestinations // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DescribeDestinations for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeDestinations @@ -1262,7 +1264,7 @@ func (c *CloudWatchLogs) DescribeDestinationsWithContext(ctx aws.Context, input // // Example iterating over at most 3 pages of a DescribeDestinations operation. // pageNum := 0 // err := client.DescribeDestinationsPages(params, -// func(page *DescribeDestinationsOutput, lastPage bool) bool { +// func(page *cloudwatchlogs.DescribeDestinationsOutput, lastPage bool) bool { // pageNum++ // fmt.Println(page) // return pageNum <= 3 @@ -1281,6 +1283,7 @@ func (c *CloudWatchLogs) DescribeDestinationsPages(input *DescribeDestinationsIn // for more information on using Contexts. func (c *CloudWatchLogs) DescribeDestinationsPagesWithContext(ctx aws.Context, input *DescribeDestinationsInput, fn func(*DescribeDestinationsOutput, bool) bool, opts ...request.Option) error { p := request.Pagination{ + EndPageOnSameToken: true, NewRequest: func() (*request.Request, error) { var inCpy *DescribeDestinationsInput if input != nil { @@ -1294,10 +1297,12 @@ func (c *CloudWatchLogs) DescribeDestinationsPagesWithContext(ctx aws.Context, i }, } - cont := true - for p.Next() && cont { - cont = fn(p.Page().(*DescribeDestinationsOutput), !p.HasNextPage()) + for p.Next() { + if !fn(p.Page().(*DescribeDestinationsOutput), !p.HasNextPage()) { + break + } } + return p.Err() } @@ -1305,8 +1310,8 @@ const opDescribeExportTasks = "DescribeExportTasks" // DescribeExportTasksRequest generates a "aws/request.Request" representing the // client's request for the DescribeExportTasks operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -1355,11 +1360,11 @@ func (c *CloudWatchLogs) DescribeExportTasksRequest(input *DescribeExportTasksIn // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DescribeExportTasks for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeExportTasks @@ -1388,8 +1393,8 @@ const opDescribeLogGroups = "DescribeLogGroups" // DescribeLogGroupsRequest generates a "aws/request.Request" representing the // client's request for the DescribeLogGroups operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -1444,11 +1449,11 @@ func (c *CloudWatchLogs) DescribeLogGroupsRequest(input *DescribeLogGroupsInput) // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DescribeLogGroups for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeLogGroups @@ -1484,7 +1489,7 @@ func (c *CloudWatchLogs) DescribeLogGroupsWithContext(ctx aws.Context, input *De // // Example iterating over at most 3 pages of a DescribeLogGroups operation. // pageNum := 0 // err := client.DescribeLogGroupsPages(params, -// func(page *DescribeLogGroupsOutput, lastPage bool) bool { +// func(page *cloudwatchlogs.DescribeLogGroupsOutput, lastPage bool) bool { // pageNum++ // fmt.Println(page) // return pageNum <= 3 @@ -1503,6 +1508,7 @@ func (c *CloudWatchLogs) DescribeLogGroupsPages(input *DescribeLogGroupsInput, f // for more information on using Contexts. func (c *CloudWatchLogs) DescribeLogGroupsPagesWithContext(ctx aws.Context, input *DescribeLogGroupsInput, fn func(*DescribeLogGroupsOutput, bool) bool, opts ...request.Option) error { p := request.Pagination{ + EndPageOnSameToken: true, NewRequest: func() (*request.Request, error) { var inCpy *DescribeLogGroupsInput if input != nil { @@ -1516,10 +1522,12 @@ func (c *CloudWatchLogs) DescribeLogGroupsPagesWithContext(ctx aws.Context, inpu }, } - cont := true - for p.Next() && cont { - cont = fn(p.Page().(*DescribeLogGroupsOutput), !p.HasNextPage()) + for p.Next() { + if !fn(p.Page().(*DescribeLogGroupsOutput), !p.HasNextPage()) { + break + } } + return p.Err() } @@ -1527,8 +1535,8 @@ const opDescribeLogStreams = "DescribeLogStreams" // DescribeLogStreamsRequest generates a "aws/request.Request" representing the // client's request for the DescribeLogStreams operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -1587,14 +1595,14 @@ func (c *CloudWatchLogs) DescribeLogStreamsRequest(input *DescribeLogStreamsInpu // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DescribeLogStreams for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeLogStreams @@ -1630,7 +1638,7 @@ func (c *CloudWatchLogs) DescribeLogStreamsWithContext(ctx aws.Context, input *D // // Example iterating over at most 3 pages of a DescribeLogStreams operation. // pageNum := 0 // err := client.DescribeLogStreamsPages(params, -// func(page *DescribeLogStreamsOutput, lastPage bool) bool { +// func(page *cloudwatchlogs.DescribeLogStreamsOutput, lastPage bool) bool { // pageNum++ // fmt.Println(page) // return pageNum <= 3 @@ -1649,6 +1657,7 @@ func (c *CloudWatchLogs) DescribeLogStreamsPages(input *DescribeLogStreamsInput, // for more information on using Contexts. func (c *CloudWatchLogs) DescribeLogStreamsPagesWithContext(ctx aws.Context, input *DescribeLogStreamsInput, fn func(*DescribeLogStreamsOutput, bool) bool, opts ...request.Option) error { p := request.Pagination{ + EndPageOnSameToken: true, NewRequest: func() (*request.Request, error) { var inCpy *DescribeLogStreamsInput if input != nil { @@ -1662,10 +1671,12 @@ func (c *CloudWatchLogs) DescribeLogStreamsPagesWithContext(ctx aws.Context, inp }, } - cont := true - for p.Next() && cont { - cont = fn(p.Page().(*DescribeLogStreamsOutput), !p.HasNextPage()) + for p.Next() { + if !fn(p.Page().(*DescribeLogStreamsOutput), !p.HasNextPage()) { + break + } } + return p.Err() } @@ -1673,8 +1684,8 @@ const opDescribeMetricFilters = "DescribeMetricFilters" // DescribeMetricFiltersRequest generates a "aws/request.Request" representing the // client's request for the DescribeMetricFilters operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -1730,14 +1741,14 @@ func (c *CloudWatchLogs) DescribeMetricFiltersRequest(input *DescribeMetricFilte // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DescribeMetricFilters for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeMetricFilters @@ -1773,7 +1784,7 @@ func (c *CloudWatchLogs) DescribeMetricFiltersWithContext(ctx aws.Context, input // // Example iterating over at most 3 pages of a DescribeMetricFilters operation. // pageNum := 0 // err := client.DescribeMetricFiltersPages(params, -// func(page *DescribeMetricFiltersOutput, lastPage bool) bool { +// func(page *cloudwatchlogs.DescribeMetricFiltersOutput, lastPage bool) bool { // pageNum++ // fmt.Println(page) // return pageNum <= 3 @@ -1792,6 +1803,7 @@ func (c *CloudWatchLogs) DescribeMetricFiltersPages(input *DescribeMetricFilters // for more information on using Contexts. func (c *CloudWatchLogs) DescribeMetricFiltersPagesWithContext(ctx aws.Context, input *DescribeMetricFiltersInput, fn func(*DescribeMetricFiltersOutput, bool) bool, opts ...request.Option) error { p := request.Pagination{ + EndPageOnSameToken: true, NewRequest: func() (*request.Request, error) { var inCpy *DescribeMetricFiltersInput if input != nil { @@ -1805,19 +1817,109 @@ func (c *CloudWatchLogs) DescribeMetricFiltersPagesWithContext(ctx aws.Context, }, } - cont := true - for p.Next() && cont { - cont = fn(p.Page().(*DescribeMetricFiltersOutput), !p.HasNextPage()) + for p.Next() { + if !fn(p.Page().(*DescribeMetricFiltersOutput), !p.HasNextPage()) { + break + } } + return p.Err() } +const opDescribeQueries = "DescribeQueries" + +// DescribeQueriesRequest generates a "aws/request.Request" representing the +// client's request for the DescribeQueries operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See DescribeQueries for more information on using the DescribeQueries +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the DescribeQueriesRequest method. +// req, resp := client.DescribeQueriesRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeQueries +func (c *CloudWatchLogs) DescribeQueriesRequest(input *DescribeQueriesInput) (req *request.Request, output *DescribeQueriesOutput) { + op := &request.Operation{ + Name: opDescribeQueries, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &DescribeQueriesInput{} + } + + output = &DescribeQueriesOutput{} + req = c.newRequest(op, input, output) + return +} + +// DescribeQueries API operation for Amazon CloudWatch Logs. +// +// Returns a list of CloudWatch Logs Insights queries that are scheduled, executing, +// or have been executed recently in this account. You can request all queries, +// or limit it to queries of a specific log group or queries with a certain +// status. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for Amazon CloudWatch Logs's +// API operation DescribeQueries for usage and error information. +// +// Returned Error Types: +// * InvalidParameterException +// A parameter is specified incorrectly. +// +// * ResourceNotFoundException +// The specified resource does not exist. +// +// * ServiceUnavailableException +// The service cannot complete the request. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeQueries +func (c *CloudWatchLogs) DescribeQueries(input *DescribeQueriesInput) (*DescribeQueriesOutput, error) { + req, out := c.DescribeQueriesRequest(input) + return out, req.Send() +} + +// DescribeQueriesWithContext is the same as DescribeQueries with the addition of +// the ability to pass a context and additional request options. +// +// See DescribeQueries for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *CloudWatchLogs) DescribeQueriesWithContext(ctx aws.Context, input *DescribeQueriesInput, opts ...request.Option) (*DescribeQueriesOutput, error) { + req, out := c.DescribeQueriesRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + const opDescribeResourcePolicies = "DescribeResourcePolicies" // DescribeResourcePoliciesRequest generates a "aws/request.Request" representing the // client's request for the DescribeResourcePolicies operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -1865,11 +1967,11 @@ func (c *CloudWatchLogs) DescribeResourcePoliciesRequest(input *DescribeResource // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DescribeResourcePolicies for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeResourcePolicies @@ -1898,8 +2000,8 @@ const opDescribeSubscriptionFilters = "DescribeSubscriptionFilters" // DescribeSubscriptionFiltersRequest generates a "aws/request.Request" representing the // client's request for the DescribeSubscriptionFilters operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -1955,14 +2057,14 @@ func (c *CloudWatchLogs) DescribeSubscriptionFiltersRequest(input *DescribeSubsc // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DescribeSubscriptionFilters for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeSubscriptionFilters @@ -1998,7 +2100,7 @@ func (c *CloudWatchLogs) DescribeSubscriptionFiltersWithContext(ctx aws.Context, // // Example iterating over at most 3 pages of a DescribeSubscriptionFilters operation. // pageNum := 0 // err := client.DescribeSubscriptionFiltersPages(params, -// func(page *DescribeSubscriptionFiltersOutput, lastPage bool) bool { +// func(page *cloudwatchlogs.DescribeSubscriptionFiltersOutput, lastPage bool) bool { // pageNum++ // fmt.Println(page) // return pageNum <= 3 @@ -2017,6 +2119,7 @@ func (c *CloudWatchLogs) DescribeSubscriptionFiltersPages(input *DescribeSubscri // for more information on using Contexts. func (c *CloudWatchLogs) DescribeSubscriptionFiltersPagesWithContext(ctx aws.Context, input *DescribeSubscriptionFiltersInput, fn func(*DescribeSubscriptionFiltersOutput, bool) bool, opts ...request.Option) error { p := request.Pagination{ + EndPageOnSameToken: true, NewRequest: func() (*request.Request, error) { var inCpy *DescribeSubscriptionFiltersInput if input != nil { @@ -2030,10 +2133,12 @@ func (c *CloudWatchLogs) DescribeSubscriptionFiltersPagesWithContext(ctx aws.Con }, } - cont := true - for p.Next() && cont { - cont = fn(p.Page().(*DescribeSubscriptionFiltersOutput), !p.HasNextPage()) + for p.Next() { + if !fn(p.Page().(*DescribeSubscriptionFiltersOutput), !p.HasNextPage()) { + break + } } + return p.Err() } @@ -2041,8 +2146,8 @@ const opDisassociateKmsKey = "DisassociateKmsKey" // DisassociateKmsKeyRequest generates a "aws/request.Request" representing the // client's request for the DisassociateKmsKey operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -2076,8 +2181,7 @@ func (c *CloudWatchLogs) DisassociateKmsKeyRequest(input *DisassociateKmsKeyInpu output = &DisassociateKmsKeyOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } @@ -2100,17 +2204,17 @@ func (c *CloudWatchLogs) DisassociateKmsKeyRequest(input *DisassociateKmsKeyInpu // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation DisassociateKmsKey for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DisassociateKmsKey @@ -2139,8 +2243,8 @@ const opFilterLogEvents = "FilterLogEvents" // FilterLogEventsRequest generates a "aws/request.Request" representing the // client's request for the FilterLogEvents operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -2202,14 +2306,14 @@ func (c *CloudWatchLogs) FilterLogEventsRequest(input *FilterLogEventsInput) (re // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation FilterLogEvents for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/FilterLogEvents @@ -2245,7 +2349,7 @@ func (c *CloudWatchLogs) FilterLogEventsWithContext(ctx aws.Context, input *Filt // // Example iterating over at most 3 pages of a FilterLogEvents operation. // pageNum := 0 // err := client.FilterLogEventsPages(params, -// func(page *FilterLogEventsOutput, lastPage bool) bool { +// func(page *cloudwatchlogs.FilterLogEventsOutput, lastPage bool) bool { // pageNum++ // fmt.Println(page) // return pageNum <= 3 @@ -2264,6 +2368,7 @@ func (c *CloudWatchLogs) FilterLogEventsPages(input *FilterLogEventsInput, fn fu // for more information on using Contexts. func (c *CloudWatchLogs) FilterLogEventsPagesWithContext(ctx aws.Context, input *FilterLogEventsInput, fn func(*FilterLogEventsOutput, bool) bool, opts ...request.Option) error { p := request.Pagination{ + EndPageOnSameToken: true, NewRequest: func() (*request.Request, error) { var inCpy *FilterLogEventsInput if input != nil { @@ -2277,10 +2382,12 @@ func (c *CloudWatchLogs) FilterLogEventsPagesWithContext(ctx aws.Context, input }, } - cont := true - for p.Next() && cont { - cont = fn(p.Page().(*FilterLogEventsOutput), !p.HasNextPage()) + for p.Next() { + if !fn(p.Page().(*FilterLogEventsOutput), !p.HasNextPage()) { + break + } } + return p.Err() } @@ -2288,8 +2395,8 @@ const opGetLogEvents = "GetLogEvents" // GetLogEventsRequest generates a "aws/request.Request" representing the // client's request for the GetLogEvents operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -2348,14 +2455,14 @@ func (c *CloudWatchLogs) GetLogEventsRequest(input *GetLogEventsInput) (req *req // See the AWS API reference guide for Amazon CloudWatch Logs's // API operation GetLogEvents for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // // See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/GetLogEvents @@ -2391,7 +2498,7 @@ func (c *CloudWatchLogs) GetLogEventsWithContext(ctx aws.Context, input *GetLogE // // Example iterating over at most 3 pages of a GetLogEvents operation. // pageNum := 0 // err := client.GetLogEventsPages(params, -// func(page *GetLogEventsOutput, lastPage bool) bool { +// func(page *cloudwatchlogs.GetLogEventsOutput, lastPage bool) bool { // pageNum++ // fmt.Println(page) // return pageNum <= 3 @@ -2410,6 +2517,7 @@ func (c *CloudWatchLogs) GetLogEventsPages(input *GetLogEventsInput, fn func(*Ge // for more information on using Contexts. func (c *CloudWatchLogs) GetLogEventsPagesWithContext(ctx aws.Context, input *GetLogEventsInput, fn func(*GetLogEventsOutput, bool) bool, opts ...request.Option) error { p := request.Pagination{ + EndPageOnSameToken: true, NewRequest: func() (*request.Request, error) { var inCpy *GetLogEventsInput if input != nil { @@ -2423,1148 +2531,2468 @@ func (c *CloudWatchLogs) GetLogEventsPagesWithContext(ctx aws.Context, input *Ge }, } - cont := true - for p.Next() && cont { - cont = fn(p.Page().(*GetLogEventsOutput), !p.HasNextPage()) + for p.Next() { + if !fn(p.Page().(*GetLogEventsOutput), !p.HasNextPage()) { + break + } } + return p.Err() } -const opListTagsLogGroup = "ListTagsLogGroup" +const opGetLogGroupFields = "GetLogGroupFields" -// ListTagsLogGroupRequest generates a "aws/request.Request" representing the -// client's request for the ListTagsLogGroup operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// GetLogGroupFieldsRequest generates a "aws/request.Request" representing the +// client's request for the GetLogGroupFields operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See ListTagsLogGroup for more information on using the ListTagsLogGroup +// See GetLogGroupFields for more information on using the GetLogGroupFields // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the ListTagsLogGroupRequest method. -// req, resp := client.ListTagsLogGroupRequest(params) +// // Example sending a request using the GetLogGroupFieldsRequest method. +// req, resp := client.GetLogGroupFieldsRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/ListTagsLogGroup -func (c *CloudWatchLogs) ListTagsLogGroupRequest(input *ListTagsLogGroupInput) (req *request.Request, output *ListTagsLogGroupOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/GetLogGroupFields +func (c *CloudWatchLogs) GetLogGroupFieldsRequest(input *GetLogGroupFieldsInput) (req *request.Request, output *GetLogGroupFieldsOutput) { op := &request.Operation{ - Name: opListTagsLogGroup, + Name: opGetLogGroupFields, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &ListTagsLogGroupInput{} + input = &GetLogGroupFieldsInput{} } - output = &ListTagsLogGroupOutput{} + output = &GetLogGroupFieldsOutput{} req = c.newRequest(op, input, output) return } -// ListTagsLogGroup API operation for Amazon CloudWatch Logs. +// GetLogGroupFields API operation for Amazon CloudWatch Logs. // -// Lists the tags for the specified log group. +// Returns a list of the fields that are included in log events in the specified +// log group, along with the percentage of log events that contain each field. +// The search is limited to a time period that you specify. +// +// In the results, fields that start with @ are fields generated by CloudWatch +// Logs. For example, @timestamp is the timestamp of each log event. +// +// The response results are sorted by the frequency percentage, starting with +// the highest percentage. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation ListTagsLogGroup for usage and error information. +// API operation GetLogGroupFields for usage and error information. +// +// Returned Error Types: +// * InvalidParameterException +// A parameter is specified incorrectly. +// +// * LimitExceededException +// You have reached the maximum number of resources that can be created. // -// Returned Error Codes: -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/ListTagsLogGroup -func (c *CloudWatchLogs) ListTagsLogGroup(input *ListTagsLogGroupInput) (*ListTagsLogGroupOutput, error) { - req, out := c.ListTagsLogGroupRequest(input) +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/GetLogGroupFields +func (c *CloudWatchLogs) GetLogGroupFields(input *GetLogGroupFieldsInput) (*GetLogGroupFieldsOutput, error) { + req, out := c.GetLogGroupFieldsRequest(input) return out, req.Send() } -// ListTagsLogGroupWithContext is the same as ListTagsLogGroup with the addition of +// GetLogGroupFieldsWithContext is the same as GetLogGroupFields with the addition of // the ability to pass a context and additional request options. // -// See ListTagsLogGroup for details on how to use this API operation. +// See GetLogGroupFields for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) ListTagsLogGroupWithContext(ctx aws.Context, input *ListTagsLogGroupInput, opts ...request.Option) (*ListTagsLogGroupOutput, error) { - req, out := c.ListTagsLogGroupRequest(input) +func (c *CloudWatchLogs) GetLogGroupFieldsWithContext(ctx aws.Context, input *GetLogGroupFieldsInput, opts ...request.Option) (*GetLogGroupFieldsOutput, error) { + req, out := c.GetLogGroupFieldsRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -const opPutDestination = "PutDestination" +const opGetLogRecord = "GetLogRecord" -// PutDestinationRequest generates a "aws/request.Request" representing the -// client's request for the PutDestination operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// GetLogRecordRequest generates a "aws/request.Request" representing the +// client's request for the GetLogRecord operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See PutDestination for more information on using the PutDestination +// See GetLogRecord for more information on using the GetLogRecord // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the PutDestinationRequest method. -// req, resp := client.PutDestinationRequest(params) +// // Example sending a request using the GetLogRecordRequest method. +// req, resp := client.GetLogRecordRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestination -func (c *CloudWatchLogs) PutDestinationRequest(input *PutDestinationInput) (req *request.Request, output *PutDestinationOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/GetLogRecord +func (c *CloudWatchLogs) GetLogRecordRequest(input *GetLogRecordInput) (req *request.Request, output *GetLogRecordOutput) { op := &request.Operation{ - Name: opPutDestination, + Name: opGetLogRecord, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &PutDestinationInput{} + input = &GetLogRecordInput{} } - output = &PutDestinationOutput{} + output = &GetLogRecordOutput{} req = c.newRequest(op, input, output) return } -// PutDestination API operation for Amazon CloudWatch Logs. +// GetLogRecord API operation for Amazon CloudWatch Logs. // -// Creates or updates a destination. A destination encapsulates a physical resource -// (such as an Amazon Kinesis stream) and enables you to subscribe to a real-time -// stream of log events for a different account, ingested using PutLogEvents. -// Currently, the only supported physical resource is a Kinesis stream belonging -// to the same account as the destination. +// Retrieves all the fields and values of a single log event. All fields are +// retrieved, even if the original query that produced the logRecordPointer +// retrieved only a subset of fields. Fields are returned as field name/field +// value pairs. // -// Through an access policy, a destination controls what is written to its Kinesis -// stream. By default, PutDestination does not set any access policy with the -// destination, which means a cross-account user cannot call PutSubscriptionFilter -// against this destination. To enable this, the destination owner must call -// PutDestinationPolicy after PutDestination. +// Additionally, the entire unparsed log event is returned within @message. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation PutDestination for usage and error information. +// API operation GetLogRecord for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeOperationAbortedException "OperationAbortedException" -// Multiple requests to update the same resource were in conflict. +// * LimitExceededException +// You have reached the maximum number of resources that can be created. +// +// * ResourceNotFoundException +// The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestination -func (c *CloudWatchLogs) PutDestination(input *PutDestinationInput) (*PutDestinationOutput, error) { - req, out := c.PutDestinationRequest(input) +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/GetLogRecord +func (c *CloudWatchLogs) GetLogRecord(input *GetLogRecordInput) (*GetLogRecordOutput, error) { + req, out := c.GetLogRecordRequest(input) return out, req.Send() } -// PutDestinationWithContext is the same as PutDestination with the addition of +// GetLogRecordWithContext is the same as GetLogRecord with the addition of // the ability to pass a context and additional request options. // -// See PutDestination for details on how to use this API operation. +// See GetLogRecord for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) PutDestinationWithContext(ctx aws.Context, input *PutDestinationInput, opts ...request.Option) (*PutDestinationOutput, error) { - req, out := c.PutDestinationRequest(input) +func (c *CloudWatchLogs) GetLogRecordWithContext(ctx aws.Context, input *GetLogRecordInput, opts ...request.Option) (*GetLogRecordOutput, error) { + req, out := c.GetLogRecordRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -const opPutDestinationPolicy = "PutDestinationPolicy" +const opGetQueryResults = "GetQueryResults" -// PutDestinationPolicyRequest generates a "aws/request.Request" representing the -// client's request for the PutDestinationPolicy operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// GetQueryResultsRequest generates a "aws/request.Request" representing the +// client's request for the GetQueryResults operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See PutDestinationPolicy for more information on using the PutDestinationPolicy +// See GetQueryResults for more information on using the GetQueryResults // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the PutDestinationPolicyRequest method. -// req, resp := client.PutDestinationPolicyRequest(params) +// // Example sending a request using the GetQueryResultsRequest method. +// req, resp := client.GetQueryResultsRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestinationPolicy -func (c *CloudWatchLogs) PutDestinationPolicyRequest(input *PutDestinationPolicyInput) (req *request.Request, output *PutDestinationPolicyOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/GetQueryResults +func (c *CloudWatchLogs) GetQueryResultsRequest(input *GetQueryResultsInput) (req *request.Request, output *GetQueryResultsOutput) { op := &request.Operation{ - Name: opPutDestinationPolicy, + Name: opGetQueryResults, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &PutDestinationPolicyInput{} + input = &GetQueryResultsInput{} } - output = &PutDestinationPolicyOutput{} + output = &GetQueryResultsOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) return } -// PutDestinationPolicy API operation for Amazon CloudWatch Logs. +// GetQueryResults API operation for Amazon CloudWatch Logs. // -// Creates or updates an access policy associated with an existing destination. -// An access policy is an IAM policy document (http://docs.aws.amazon.com/IAM/latest/UserGuide/policies_overview.html) -// that is used to authorize claims to register a subscription filter against -// a given destination. +// Returns the results from the specified query. +// +// Only the fields requested in the query are returned, along with a @ptr field +// which is the identifier for the log record. You can use the value of @ptr +// in a operation to get the full log record. +// +// GetQueryResults does not start a query execution. To run a query, use . +// +// If the value of the Status field in the output is Running, this operation +// returns only partial results. If you see a value of Scheduled or Running +// for the status, you can retry the operation later to see the final results. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation PutDestinationPolicy for usage and error information. +// API operation GetQueryResults for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeOperationAbortedException "OperationAbortedException" -// Multiple requests to update the same resource were in conflict. +// * ResourceNotFoundException +// The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestinationPolicy -func (c *CloudWatchLogs) PutDestinationPolicy(input *PutDestinationPolicyInput) (*PutDestinationPolicyOutput, error) { - req, out := c.PutDestinationPolicyRequest(input) +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/GetQueryResults +func (c *CloudWatchLogs) GetQueryResults(input *GetQueryResultsInput) (*GetQueryResultsOutput, error) { + req, out := c.GetQueryResultsRequest(input) return out, req.Send() } -// PutDestinationPolicyWithContext is the same as PutDestinationPolicy with the addition of +// GetQueryResultsWithContext is the same as GetQueryResults with the addition of // the ability to pass a context and additional request options. // -// See PutDestinationPolicy for details on how to use this API operation. +// See GetQueryResults for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) PutDestinationPolicyWithContext(ctx aws.Context, input *PutDestinationPolicyInput, opts ...request.Option) (*PutDestinationPolicyOutput, error) { - req, out := c.PutDestinationPolicyRequest(input) +func (c *CloudWatchLogs) GetQueryResultsWithContext(ctx aws.Context, input *GetQueryResultsInput, opts ...request.Option) (*GetQueryResultsOutput, error) { + req, out := c.GetQueryResultsRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -const opPutLogEvents = "PutLogEvents" +const opListTagsLogGroup = "ListTagsLogGroup" -// PutLogEventsRequest generates a "aws/request.Request" representing the -// client's request for the PutLogEvents operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// ListTagsLogGroupRequest generates a "aws/request.Request" representing the +// client's request for the ListTagsLogGroup operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See PutLogEvents for more information on using the PutLogEvents +// See ListTagsLogGroup for more information on using the ListTagsLogGroup // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the PutLogEventsRequest method. -// req, resp := client.PutLogEventsRequest(params) +// // Example sending a request using the ListTagsLogGroupRequest method. +// req, resp := client.ListTagsLogGroupRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutLogEvents -func (c *CloudWatchLogs) PutLogEventsRequest(input *PutLogEventsInput) (req *request.Request, output *PutLogEventsOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/ListTagsLogGroup +func (c *CloudWatchLogs) ListTagsLogGroupRequest(input *ListTagsLogGroupInput) (req *request.Request, output *ListTagsLogGroupOutput) { op := &request.Operation{ - Name: opPutLogEvents, + Name: opListTagsLogGroup, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &PutLogEventsInput{} + input = &ListTagsLogGroupInput{} } - output = &PutLogEventsOutput{} + output = &ListTagsLogGroupOutput{} req = c.newRequest(op, input, output) return } -// PutLogEvents API operation for Amazon CloudWatch Logs. -// -// Uploads a batch of log events to the specified log stream. -// -// You must include the sequence token obtained from the response of the previous -// call. An upload in a newly created log stream does not require a sequence -// token. You can also get the sequence token using DescribeLogStreams. If you -// call PutLogEvents twice within a narrow time period using the same value -// for sequenceToken, both calls may be successful, or one may be rejected. -// -// The batch of events must satisfy the following constraints: -// -// * The maximum batch size is 1,048,576 bytes, and this size is calculated -// as the sum of all event messages in UTF-8, plus 26 bytes for each log -// event. -// -// * None of the log events in the batch can be more than 2 hours in the -// future. -// -// * None of the log events in the batch can be older than 14 days or the -// retention period of the log group. -// -// * The log events in the batch must be in chronological ordered by their -// time stamp (the time the event occurred, expressed as the number of milliseconds -// after Jan 1, 1970 00:00:00 UTC). -// -// * The maximum number of log events in a batch is 10,000. +// ListTagsLogGroup API operation for Amazon CloudWatch Logs. // -// * A batch of log events in a single request cannot span more than 24 hours. -// Otherwise, the operation fails. +// Lists the tags for the specified log group. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation PutLogEvents for usage and error information. -// -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" -// A parameter is specified incorrectly. -// -// * ErrCodeInvalidSequenceTokenException "InvalidSequenceTokenException" -// The sequence token is not valid. -// -// * ErrCodeDataAlreadyAcceptedException "DataAlreadyAcceptedException" -// The event was already logged. +// API operation ListTagsLogGroup for usage and error information. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// Returned Error Types: +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutLogEvents -func (c *CloudWatchLogs) PutLogEvents(input *PutLogEventsInput) (*PutLogEventsOutput, error) { - req, out := c.PutLogEventsRequest(input) +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/ListTagsLogGroup +func (c *CloudWatchLogs) ListTagsLogGroup(input *ListTagsLogGroupInput) (*ListTagsLogGroupOutput, error) { + req, out := c.ListTagsLogGroupRequest(input) return out, req.Send() } -// PutLogEventsWithContext is the same as PutLogEvents with the addition of +// ListTagsLogGroupWithContext is the same as ListTagsLogGroup with the addition of // the ability to pass a context and additional request options. // -// See PutLogEvents for details on how to use this API operation. +// See ListTagsLogGroup for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) PutLogEventsWithContext(ctx aws.Context, input *PutLogEventsInput, opts ...request.Option) (*PutLogEventsOutput, error) { - req, out := c.PutLogEventsRequest(input) +func (c *CloudWatchLogs) ListTagsLogGroupWithContext(ctx aws.Context, input *ListTagsLogGroupInput, opts ...request.Option) (*ListTagsLogGroupOutput, error) { + req, out := c.ListTagsLogGroupRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -const opPutMetricFilter = "PutMetricFilter" +const opPutDestination = "PutDestination" -// PutMetricFilterRequest generates a "aws/request.Request" representing the -// client's request for the PutMetricFilter operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// PutDestinationRequest generates a "aws/request.Request" representing the +// client's request for the PutDestination operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See PutMetricFilter for more information on using the PutMetricFilter +// See PutDestination for more information on using the PutDestination // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the PutMetricFilterRequest method. -// req, resp := client.PutMetricFilterRequest(params) +// // Example sending a request using the PutDestinationRequest method. +// req, resp := client.PutDestinationRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutMetricFilter -func (c *CloudWatchLogs) PutMetricFilterRequest(input *PutMetricFilterInput) (req *request.Request, output *PutMetricFilterOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestination +func (c *CloudWatchLogs) PutDestinationRequest(input *PutDestinationInput) (req *request.Request, output *PutDestinationOutput) { op := &request.Operation{ - Name: opPutMetricFilter, + Name: opPutDestination, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &PutMetricFilterInput{} + input = &PutDestinationInput{} } - output = &PutMetricFilterOutput{} + output = &PutDestinationOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) return } -// PutMetricFilter API operation for Amazon CloudWatch Logs. +// PutDestination API operation for Amazon CloudWatch Logs. // -// Creates or updates a metric filter and associates it with the specified log -// group. Metric filters allow you to configure rules to extract metric data -// from log events ingested through PutLogEvents. +// Creates or updates a destination. This operation is used only to create destinations +// for cross-account subscriptions. // -// The maximum number of metric filters that can be associated with a log group -// is 100. +// A destination encapsulates a physical resource (such as an Amazon Kinesis +// stream) and enables you to subscribe to a real-time stream of log events +// for a different account, ingested using PutLogEvents. +// +// Through an access policy, a destination controls what is written to it. By +// default, PutDestination does not set any access policy with the destination, +// which means a cross-account user cannot call PutSubscriptionFilter against +// this destination. To enable this, the destination owner must call PutDestinationPolicy +// after PutDestination. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation PutMetricFilter for usage and error information. +// API operation PutDestination for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" -// The specified resource does not exist. -// -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeLimitExceededException "LimitExceededException" -// You have reached the maximum number of resources that can be created. -// -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutMetricFilter -func (c *CloudWatchLogs) PutMetricFilter(input *PutMetricFilterInput) (*PutMetricFilterOutput, error) { - req, out := c.PutMetricFilterRequest(input) +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestination +func (c *CloudWatchLogs) PutDestination(input *PutDestinationInput) (*PutDestinationOutput, error) { + req, out := c.PutDestinationRequest(input) return out, req.Send() } -// PutMetricFilterWithContext is the same as PutMetricFilter with the addition of +// PutDestinationWithContext is the same as PutDestination with the addition of // the ability to pass a context and additional request options. // -// See PutMetricFilter for details on how to use this API operation. +// See PutDestination for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) PutMetricFilterWithContext(ctx aws.Context, input *PutMetricFilterInput, opts ...request.Option) (*PutMetricFilterOutput, error) { - req, out := c.PutMetricFilterRequest(input) +func (c *CloudWatchLogs) PutDestinationWithContext(ctx aws.Context, input *PutDestinationInput, opts ...request.Option) (*PutDestinationOutput, error) { + req, out := c.PutDestinationRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -const opPutResourcePolicy = "PutResourcePolicy" +const opPutDestinationPolicy = "PutDestinationPolicy" -// PutResourcePolicyRequest generates a "aws/request.Request" representing the -// client's request for the PutResourcePolicy operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// PutDestinationPolicyRequest generates a "aws/request.Request" representing the +// client's request for the PutDestinationPolicy operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See PutResourcePolicy for more information on using the PutResourcePolicy +// See PutDestinationPolicy for more information on using the PutDestinationPolicy // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the PutResourcePolicyRequest method. -// req, resp := client.PutResourcePolicyRequest(params) +// // Example sending a request using the PutDestinationPolicyRequest method. +// req, resp := client.PutDestinationPolicyRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutResourcePolicy -func (c *CloudWatchLogs) PutResourcePolicyRequest(input *PutResourcePolicyInput) (req *request.Request, output *PutResourcePolicyOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestinationPolicy +func (c *CloudWatchLogs) PutDestinationPolicyRequest(input *PutDestinationPolicyInput) (req *request.Request, output *PutDestinationPolicyOutput) { op := &request.Operation{ - Name: opPutResourcePolicy, + Name: opPutDestinationPolicy, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &PutResourcePolicyInput{} + input = &PutDestinationPolicyInput{} } - output = &PutResourcePolicyOutput{} + output = &PutDestinationPolicyOutput{} req = c.newRequest(op, input, output) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } -// PutResourcePolicy API operation for Amazon CloudWatch Logs. +// PutDestinationPolicy API operation for Amazon CloudWatch Logs. // -// Creates or updates a resource policy allowing other AWS services to put log -// events to this account, such as Amazon Route 53. An account can have up to -// 50 resource policies per region. +// Creates or updates an access policy associated with an existing destination. +// An access policy is an IAM policy document (https://docs.aws.amazon.com/IAM/latest/UserGuide/policies_overview.html) +// that is used to authorize claims to register a subscription filter against +// a given destination. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation PutResourcePolicy for usage and error information. +// API operation PutDestinationPolicy for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeLimitExceededException "LimitExceededException" -// You have reached the maximum number of resources that can be created. +// * OperationAbortedException +// Multiple requests to update the same resource were in conflict. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutResourcePolicy -func (c *CloudWatchLogs) PutResourcePolicy(input *PutResourcePolicyInput) (*PutResourcePolicyOutput, error) { - req, out := c.PutResourcePolicyRequest(input) +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestinationPolicy +func (c *CloudWatchLogs) PutDestinationPolicy(input *PutDestinationPolicyInput) (*PutDestinationPolicyOutput, error) { + req, out := c.PutDestinationPolicyRequest(input) return out, req.Send() } -// PutResourcePolicyWithContext is the same as PutResourcePolicy with the addition of +// PutDestinationPolicyWithContext is the same as PutDestinationPolicy with the addition of // the ability to pass a context and additional request options. // -// See PutResourcePolicy for details on how to use this API operation. +// See PutDestinationPolicy for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) PutResourcePolicyWithContext(ctx aws.Context, input *PutResourcePolicyInput, opts ...request.Option) (*PutResourcePolicyOutput, error) { - req, out := c.PutResourcePolicyRequest(input) +func (c *CloudWatchLogs) PutDestinationPolicyWithContext(ctx aws.Context, input *PutDestinationPolicyInput, opts ...request.Option) (*PutDestinationPolicyOutput, error) { + req, out := c.PutDestinationPolicyRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -const opPutRetentionPolicy = "PutRetentionPolicy" +const opPutLogEvents = "PutLogEvents" -// PutRetentionPolicyRequest generates a "aws/request.Request" representing the -// client's request for the PutRetentionPolicy operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// PutLogEventsRequest generates a "aws/request.Request" representing the +// client's request for the PutLogEvents operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See PutRetentionPolicy for more information on using the PutRetentionPolicy +// See PutLogEvents for more information on using the PutLogEvents // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the PutRetentionPolicyRequest method. -// req, resp := client.PutRetentionPolicyRequest(params) +// // Example sending a request using the PutLogEventsRequest method. +// req, resp := client.PutLogEventsRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutRetentionPolicy -func (c *CloudWatchLogs) PutRetentionPolicyRequest(input *PutRetentionPolicyInput) (req *request.Request, output *PutRetentionPolicyOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutLogEvents +func (c *CloudWatchLogs) PutLogEventsRequest(input *PutLogEventsInput) (req *request.Request, output *PutLogEventsOutput) { op := &request.Operation{ - Name: opPutRetentionPolicy, + Name: opPutLogEvents, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &PutRetentionPolicyInput{} + input = &PutLogEventsInput{} } - output = &PutRetentionPolicyOutput{} + output = &PutLogEventsOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) return } -// PutRetentionPolicy API operation for Amazon CloudWatch Logs. +// PutLogEvents API operation for Amazon CloudWatch Logs. // -// Sets the retention of the specified log group. A retention policy allows -// you to configure the number of days for which to retain log events in the -// specified log group. +// Uploads a batch of log events to the specified log stream. +// +// You must include the sequence token obtained from the response of the previous +// call. An upload in a newly created log stream does not require a sequence +// token. You can also get the sequence token in the expectedSequenceToken field +// from InvalidSequenceTokenException. If you call PutLogEvents twice within +// a narrow time period using the same value for sequenceToken, both calls may +// be successful, or one may be rejected. +// +// The batch of events must satisfy the following constraints: +// +// * The maximum batch size is 1,048,576 bytes, and this size is calculated +// as the sum of all event messages in UTF-8, plus 26 bytes for each log +// event. +// +// * None of the log events in the batch can be more than 2 hours in the +// future. +// +// * None of the log events in the batch can be older than 14 days or older +// than the retention period of the log group. +// +// * The log events in the batch must be in chronological ordered by their +// timestamp. The timestamp is the time the event occurred, expressed as +// the number of milliseconds after Jan 1, 1970 00:00:00 UTC. (In AWS Tools +// for PowerShell and the AWS SDK for .NET, the timestamp is specified in +// .NET format: yyyy-mm-ddThh:mm:ss. For example, 2017-09-15T13:45:30.) +// +// * A batch of log events in a single request cannot span more than 24 hours. +// Otherwise, the operation fails. +// +// * The maximum number of log events in a batch is 10,000. +// +// * There is a quota of 5 requests per second per log stream. Additional +// requests are throttled. This quota can't be changed. +// +// If a call to PutLogEvents returns "UnrecognizedClientException" the most +// likely cause is an invalid AWS access key ID or secret key. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation PutRetentionPolicy for usage and error information. +// API operation PutLogEvents for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" -// The specified resource does not exist. +// * InvalidSequenceTokenException +// The sequence token is not valid. You can get the correct sequence token in +// the expectedSequenceToken field in the InvalidSequenceTokenException message. // -// * ErrCodeOperationAbortedException "OperationAbortedException" -// Multiple requests to update the same resource were in conflict. +// * DataAlreadyAcceptedException +// The event was already logged. +// +// * ResourceNotFoundException +// The specified resource does not exist. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutRetentionPolicy -func (c *CloudWatchLogs) PutRetentionPolicy(input *PutRetentionPolicyInput) (*PutRetentionPolicyOutput, error) { - req, out := c.PutRetentionPolicyRequest(input) +// * UnrecognizedClientException +// The most likely cause is an invalid AWS access key ID or secret key. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutLogEvents +func (c *CloudWatchLogs) PutLogEvents(input *PutLogEventsInput) (*PutLogEventsOutput, error) { + req, out := c.PutLogEventsRequest(input) return out, req.Send() } -// PutRetentionPolicyWithContext is the same as PutRetentionPolicy with the addition of +// PutLogEventsWithContext is the same as PutLogEvents with the addition of // the ability to pass a context and additional request options. // -// See PutRetentionPolicy for details on how to use this API operation. +// See PutLogEvents for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) PutRetentionPolicyWithContext(ctx aws.Context, input *PutRetentionPolicyInput, opts ...request.Option) (*PutRetentionPolicyOutput, error) { - req, out := c.PutRetentionPolicyRequest(input) +func (c *CloudWatchLogs) PutLogEventsWithContext(ctx aws.Context, input *PutLogEventsInput, opts ...request.Option) (*PutLogEventsOutput, error) { + req, out := c.PutLogEventsRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -const opPutSubscriptionFilter = "PutSubscriptionFilter" +const opPutMetricFilter = "PutMetricFilter" -// PutSubscriptionFilterRequest generates a "aws/request.Request" representing the -// client's request for the PutSubscriptionFilter operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// PutMetricFilterRequest generates a "aws/request.Request" representing the +// client's request for the PutMetricFilter operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See PutSubscriptionFilter for more information on using the PutSubscriptionFilter +// See PutMetricFilter for more information on using the PutMetricFilter // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the PutSubscriptionFilterRequest method. -// req, resp := client.PutSubscriptionFilterRequest(params) +// // Example sending a request using the PutMetricFilterRequest method. +// req, resp := client.PutMetricFilterRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutSubscriptionFilter -func (c *CloudWatchLogs) PutSubscriptionFilterRequest(input *PutSubscriptionFilterInput) (req *request.Request, output *PutSubscriptionFilterOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutMetricFilter +func (c *CloudWatchLogs) PutMetricFilterRequest(input *PutMetricFilterInput) (req *request.Request, output *PutMetricFilterOutput) { op := &request.Operation{ - Name: opPutSubscriptionFilter, + Name: opPutMetricFilter, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &PutSubscriptionFilterInput{} + input = &PutMetricFilterInput{} } - output = &PutSubscriptionFilterOutput{} + output = &PutMetricFilterOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } -// PutSubscriptionFilter API operation for Amazon CloudWatch Logs. -// -// Creates or updates a subscription filter and associates it with the specified -// log group. Subscription filters allow you to subscribe to a real-time stream -// of log events ingested through PutLogEvents and have them delivered to a -// specific destination. Currently, the supported destinations are: -// -// * An Amazon Kinesis stream belonging to the same account as the subscription -// filter, for same-account delivery. -// -// * A logical destination that belongs to a different account, for cross-account -// delivery. -// -// * An Amazon Kinesis Firehose delivery stream that belongs to the same -// account as the subscription filter, for same-account delivery. +// PutMetricFilter API operation for Amazon CloudWatch Logs. // -// * An AWS Lambda function that belongs to the same account as the subscription -// filter, for same-account delivery. +// Creates or updates a metric filter and associates it with the specified log +// group. Metric filters allow you to configure rules to extract metric data +// from log events ingested through PutLogEvents. // -// There can only be one subscription filter associated with a log group. If -// you are updating an existing filter, you must specify the correct name in -// filterName. Otherwise, the call fails because you cannot associate a second -// filter with a log group. +// The maximum number of metric filters that can be associated with a log group +// is 100. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation PutSubscriptionFilter for usage and error information. +// API operation PutMetricFilter for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// * ErrCodeOperationAbortedException "OperationAbortedException" +// * OperationAbortedException // Multiple requests to update the same resource were in conflict. // -// * ErrCodeLimitExceededException "LimitExceededException" +// * LimitExceededException // You have reached the maximum number of resources that can be created. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ServiceUnavailableException // The service cannot complete the request. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutSubscriptionFilter -func (c *CloudWatchLogs) PutSubscriptionFilter(input *PutSubscriptionFilterInput) (*PutSubscriptionFilterOutput, error) { - req, out := c.PutSubscriptionFilterRequest(input) +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutMetricFilter +func (c *CloudWatchLogs) PutMetricFilter(input *PutMetricFilterInput) (*PutMetricFilterOutput, error) { + req, out := c.PutMetricFilterRequest(input) return out, req.Send() } -// PutSubscriptionFilterWithContext is the same as PutSubscriptionFilter with the addition of +// PutMetricFilterWithContext is the same as PutMetricFilter with the addition of // the ability to pass a context and additional request options. // -// See PutSubscriptionFilter for details on how to use this API operation. +// See PutMetricFilter for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) PutSubscriptionFilterWithContext(ctx aws.Context, input *PutSubscriptionFilterInput, opts ...request.Option) (*PutSubscriptionFilterOutput, error) { - req, out := c.PutSubscriptionFilterRequest(input) +func (c *CloudWatchLogs) PutMetricFilterWithContext(ctx aws.Context, input *PutMetricFilterInput, opts ...request.Option) (*PutMetricFilterOutput, error) { + req, out := c.PutMetricFilterRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -const opTagLogGroup = "TagLogGroup" +const opPutResourcePolicy = "PutResourcePolicy" -// TagLogGroupRequest generates a "aws/request.Request" representing the -// client's request for the TagLogGroup operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// PutResourcePolicyRequest generates a "aws/request.Request" representing the +// client's request for the PutResourcePolicy operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See TagLogGroup for more information on using the TagLogGroup +// See PutResourcePolicy for more information on using the PutResourcePolicy // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the TagLogGroupRequest method. -// req, resp := client.TagLogGroupRequest(params) +// // Example sending a request using the PutResourcePolicyRequest method. +// req, resp := client.PutResourcePolicyRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TagLogGroup -func (c *CloudWatchLogs) TagLogGroupRequest(input *TagLogGroupInput) (req *request.Request, output *TagLogGroupOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutResourcePolicy +func (c *CloudWatchLogs) PutResourcePolicyRequest(input *PutResourcePolicyInput) (req *request.Request, output *PutResourcePolicyOutput) { op := &request.Operation{ - Name: opTagLogGroup, + Name: opPutResourcePolicy, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &TagLogGroupInput{} + input = &PutResourcePolicyInput{} } - output = &TagLogGroupOutput{} + output = &PutResourcePolicyOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) return } -// TagLogGroup API operation for Amazon CloudWatch Logs. -// -// Adds or updates the specified tags for the specified log group. -// -// To list the tags for a log group, use ListTagsLogGroup. To remove tags, use -// UntagLogGroup. +// PutResourcePolicy API operation for Amazon CloudWatch Logs. // -// For more information about tags, see Tag Log Groups in Amazon CloudWatch -// Logs (http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/log-group-tagging.html) -// in the Amazon CloudWatch Logs User Guide. +// Creates or updates a resource policy allowing other AWS services to put log +// events to this account, such as Amazon Route 53. An account can have up to +// 10 resource policies per region. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation TagLogGroup for usage and error information. -// -// Returned Error Codes: -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" -// The specified resource does not exist. +// API operation PutResourcePolicy for usage and error information. // -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TagLogGroup -func (c *CloudWatchLogs) TagLogGroup(input *TagLogGroupInput) (*TagLogGroupOutput, error) { - req, out := c.TagLogGroupRequest(input) +// * LimitExceededException +// You have reached the maximum number of resources that can be created. +// +// * ServiceUnavailableException +// The service cannot complete the request. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutResourcePolicy +func (c *CloudWatchLogs) PutResourcePolicy(input *PutResourcePolicyInput) (*PutResourcePolicyOutput, error) { + req, out := c.PutResourcePolicyRequest(input) return out, req.Send() } -// TagLogGroupWithContext is the same as TagLogGroup with the addition of +// PutResourcePolicyWithContext is the same as PutResourcePolicy with the addition of // the ability to pass a context and additional request options. // -// See TagLogGroup for details on how to use this API operation. +// See PutResourcePolicy for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) TagLogGroupWithContext(ctx aws.Context, input *TagLogGroupInput, opts ...request.Option) (*TagLogGroupOutput, error) { - req, out := c.TagLogGroupRequest(input) +func (c *CloudWatchLogs) PutResourcePolicyWithContext(ctx aws.Context, input *PutResourcePolicyInput, opts ...request.Option) (*PutResourcePolicyOutput, error) { + req, out := c.PutResourcePolicyRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -const opTestMetricFilter = "TestMetricFilter" +const opPutRetentionPolicy = "PutRetentionPolicy" -// TestMetricFilterRequest generates a "aws/request.Request" representing the -// client's request for the TestMetricFilter operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// PutRetentionPolicyRequest generates a "aws/request.Request" representing the +// client's request for the PutRetentionPolicy operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See TestMetricFilter for more information on using the TestMetricFilter +// See PutRetentionPolicy for more information on using the PutRetentionPolicy // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the TestMetricFilterRequest method. -// req, resp := client.TestMetricFilterRequest(params) +// // Example sending a request using the PutRetentionPolicyRequest method. +// req, resp := client.PutRetentionPolicyRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TestMetricFilter -func (c *CloudWatchLogs) TestMetricFilterRequest(input *TestMetricFilterInput) (req *request.Request, output *TestMetricFilterOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutRetentionPolicy +func (c *CloudWatchLogs) PutRetentionPolicyRequest(input *PutRetentionPolicyInput) (req *request.Request, output *PutRetentionPolicyOutput) { op := &request.Operation{ - Name: opTestMetricFilter, + Name: opPutRetentionPolicy, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &TestMetricFilterInput{} + input = &PutRetentionPolicyInput{} } - output = &TestMetricFilterOutput{} + output = &PutRetentionPolicyOutput{} req = c.newRequest(op, input, output) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } -// TestMetricFilter API operation for Amazon CloudWatch Logs. +// PutRetentionPolicy API operation for Amazon CloudWatch Logs. // -// Tests the filter pattern of a metric filter against a sample of log event -// messages. You can use this operation to validate the correctness of a metric -// filter pattern. +// Sets the retention of the specified log group. A retention policy allows +// you to configure the number of days for which to retain log events in the +// specified log group. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation TestMetricFilter for usage and error information. +// API operation PutRetentionPolicy for usage and error information. // -// Returned Error Codes: -// * ErrCodeInvalidParameterException "InvalidParameterException" +// Returned Error Types: +// * InvalidParameterException // A parameter is specified incorrectly. // -// * ErrCodeServiceUnavailableException "ServiceUnavailableException" +// * ResourceNotFoundException +// The specified resource does not exist. +// +// * OperationAbortedException +// Multiple requests to update the same resource were in conflict. +// +// * ServiceUnavailableException // The service cannot complete the request. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TestMetricFilter -func (c *CloudWatchLogs) TestMetricFilter(input *TestMetricFilterInput) (*TestMetricFilterOutput, error) { - req, out := c.TestMetricFilterRequest(input) +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutRetentionPolicy +func (c *CloudWatchLogs) PutRetentionPolicy(input *PutRetentionPolicyInput) (*PutRetentionPolicyOutput, error) { + req, out := c.PutRetentionPolicyRequest(input) return out, req.Send() } -// TestMetricFilterWithContext is the same as TestMetricFilter with the addition of +// PutRetentionPolicyWithContext is the same as PutRetentionPolicy with the addition of // the ability to pass a context and additional request options. // -// See TestMetricFilter for details on how to use this API operation. +// See PutRetentionPolicy for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) TestMetricFilterWithContext(ctx aws.Context, input *TestMetricFilterInput, opts ...request.Option) (*TestMetricFilterOutput, error) { - req, out := c.TestMetricFilterRequest(input) +func (c *CloudWatchLogs) PutRetentionPolicyWithContext(ctx aws.Context, input *PutRetentionPolicyInput, opts ...request.Option) (*PutRetentionPolicyOutput, error) { + req, out := c.PutRetentionPolicyRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -const opUntagLogGroup = "UntagLogGroup" +const opPutSubscriptionFilter = "PutSubscriptionFilter" -// UntagLogGroupRequest generates a "aws/request.Request" representing the -// client's request for the UntagLogGroup operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// PutSubscriptionFilterRequest generates a "aws/request.Request" representing the +// client's request for the PutSubscriptionFilter operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. // -// See UntagLogGroup for more information on using the UntagLogGroup +// See PutSubscriptionFilter for more information on using the PutSubscriptionFilter // API call, and error handling. // // This method is useful when you want to inject custom logic or configuration // into the SDK's request lifecycle. Such as custom headers, or retry logic. // // -// // Example sending a request using the UntagLogGroupRequest method. -// req, resp := client.UntagLogGroupRequest(params) +// // Example sending a request using the PutSubscriptionFilterRequest method. +// req, resp := client.PutSubscriptionFilterRequest(params) // // err := req.Send() // if err == nil { // resp is now filled // fmt.Println(resp) // } // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/UntagLogGroup -func (c *CloudWatchLogs) UntagLogGroupRequest(input *UntagLogGroupInput) (req *request.Request, output *UntagLogGroupOutput) { +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutSubscriptionFilter +func (c *CloudWatchLogs) PutSubscriptionFilterRequest(input *PutSubscriptionFilterInput) (req *request.Request, output *PutSubscriptionFilterOutput) { op := &request.Operation{ - Name: opUntagLogGroup, + Name: opPutSubscriptionFilter, HTTPMethod: "POST", HTTPPath: "/", } if input == nil { - input = &UntagLogGroupInput{} + input = &PutSubscriptionFilterInput{} } - output = &UntagLogGroupOutput{} + output = &PutSubscriptionFilterOutput{} req = c.newRequest(op, input, output) - req.Handlers.Unmarshal.Remove(jsonrpc.UnmarshalHandler) - req.Handlers.Unmarshal.PushBackNamed(protocol.UnmarshalDiscardBodyHandler) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) return } -// UntagLogGroup API operation for Amazon CloudWatch Logs. +// PutSubscriptionFilter API operation for Amazon CloudWatch Logs. // -// Removes the specified tags from the specified log group. +// Creates or updates a subscription filter and associates it with the specified +// log group. Subscription filters allow you to subscribe to a real-time stream +// of log events ingested through PutLogEvents and have them delivered to a +// specific destination. Currently, the supported destinations are: // -// To list the tags for a log group, use ListTagsLogGroup. To add tags, use -// UntagLogGroup. +// * An Amazon Kinesis stream belonging to the same account as the subscription +// filter, for same-account delivery. +// +// * A logical destination that belongs to a different account, for cross-account +// delivery. +// +// * An Amazon Kinesis Firehose delivery stream that belongs to the same +// account as the subscription filter, for same-account delivery. +// +// * An AWS Lambda function that belongs to the same account as the subscription +// filter, for same-account delivery. +// +// There can only be one subscription filter associated with a log group. If +// you are updating an existing filter, you must specify the correct name in +// filterName. Otherwise, the call fails because you cannot associate a second +// filter with a log group. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // // See the AWS API reference guide for Amazon CloudWatch Logs's -// API operation UntagLogGroup for usage and error information. +// API operation PutSubscriptionFilter for usage and error information. +// +// Returned Error Types: +// * InvalidParameterException +// A parameter is specified incorrectly. // -// Returned Error Codes: -// * ErrCodeResourceNotFoundException "ResourceNotFoundException" +// * ResourceNotFoundException // The specified resource does not exist. // -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/UntagLogGroup -func (c *CloudWatchLogs) UntagLogGroup(input *UntagLogGroupInput) (*UntagLogGroupOutput, error) { - req, out := c.UntagLogGroupRequest(input) +// * OperationAbortedException +// Multiple requests to update the same resource were in conflict. +// +// * LimitExceededException +// You have reached the maximum number of resources that can be created. +// +// * ServiceUnavailableException +// The service cannot complete the request. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutSubscriptionFilter +func (c *CloudWatchLogs) PutSubscriptionFilter(input *PutSubscriptionFilterInput) (*PutSubscriptionFilterOutput, error) { + req, out := c.PutSubscriptionFilterRequest(input) return out, req.Send() } -// UntagLogGroupWithContext is the same as UntagLogGroup with the addition of +// PutSubscriptionFilterWithContext is the same as PutSubscriptionFilter with the addition of // the ability to pass a context and additional request options. // -// See UntagLogGroup for details on how to use this API operation. +// See PutSubscriptionFilter for details on how to use this API operation. // // The context must be non-nil and will be used for request cancellation. If // the context is nil a panic will occur. In the future the SDK may create // sub-contexts for http.Requests. See https://golang.org/pkg/context/ // for more information on using Contexts. -func (c *CloudWatchLogs) UntagLogGroupWithContext(ctx aws.Context, input *UntagLogGroupInput, opts ...request.Option) (*UntagLogGroupOutput, error) { - req, out := c.UntagLogGroupRequest(input) +func (c *CloudWatchLogs) PutSubscriptionFilterWithContext(ctx aws.Context, input *PutSubscriptionFilterInput, opts ...request.Option) (*PutSubscriptionFilterOutput, error) { + req, out := c.PutSubscriptionFilterRequest(input) req.SetContext(ctx) req.ApplyOptions(opts...) return out, req.Send() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/AssociateKmsKeyRequest -type AssociateKmsKeyInput struct { - _ struct{} `type:"structure"` +const opStartQuery = "StartQuery" - // The Amazon Resource Name (ARN) of the CMK to use when encrypting log data. - // For more information, see Amazon Resource Names - AWS Key Management Service - // (AWS KMS) (http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#arn-syntax-kms). - // - // KmsKeyId is a required field - KmsKeyId *string `locationName:"kmsKeyId" type:"string" required:"true"` +// StartQueryRequest generates a "aws/request.Request" representing the +// client's request for the StartQuery operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See StartQuery for more information on using the StartQuery +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the StartQueryRequest method. +// req, resp := client.StartQueryRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/StartQuery +func (c *CloudWatchLogs) StartQueryRequest(input *StartQueryInput) (req *request.Request, output *StartQueryOutput) { + op := &request.Operation{ + Name: opStartQuery, + HTTPMethod: "POST", + HTTPPath: "/", + } - // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + if input == nil { + input = &StartQueryInput{} + } + + output = &StartQueryOutput{} + req = c.newRequest(op, input, output) + return } -// String returns the string representation -func (s AssociateKmsKeyInput) String() string { - return awsutil.Prettify(s) +// StartQuery API operation for Amazon CloudWatch Logs. +// +// Schedules a query of a log group using CloudWatch Logs Insights. You specify +// the log group and time range to query, and the query string to use. +// +// For more information, see CloudWatch Logs Insights Query Syntax (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html). +// +// Queries time out after 15 minutes of execution. If your queries are timing +// out, reduce the time range being searched, or partition your query into a +// number of queries. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for Amazon CloudWatch Logs's +// API operation StartQuery for usage and error information. +// +// Returned Error Types: +// * MalformedQueryException +// The query string is not valid. Details about this error are displayed in +// a QueryCompileError object. For more information, see . +// +// For more information about valid query syntax, see CloudWatch Logs Insights +// Query Syntax (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html). +// +// * InvalidParameterException +// A parameter is specified incorrectly. +// +// * LimitExceededException +// You have reached the maximum number of resources that can be created. +// +// * ResourceNotFoundException +// The specified resource does not exist. +// +// * ServiceUnavailableException +// The service cannot complete the request. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/StartQuery +func (c *CloudWatchLogs) StartQuery(input *StartQueryInput) (*StartQueryOutput, error) { + req, out := c.StartQueryRequest(input) + return out, req.Send() } -// GoString returns the string representation -func (s AssociateKmsKeyInput) GoString() string { - return s.String() +// StartQueryWithContext is the same as StartQuery with the addition of +// the ability to pass a context and additional request options. +// +// See StartQuery for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *CloudWatchLogs) StartQueryWithContext(ctx aws.Context, input *StartQueryInput, opts ...request.Option) (*StartQueryOutput, error) { + req, out := c.StartQueryRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *AssociateKmsKeyInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "AssociateKmsKeyInput"} - if s.KmsKeyId == nil { - invalidParams.Add(request.NewErrParamRequired("KmsKeyId")) - } - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) +const opStopQuery = "StopQuery" + +// StopQueryRequest generates a "aws/request.Request" representing the +// client's request for the StopQuery operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See StopQuery for more information on using the StopQuery +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the StopQueryRequest method. +// req, resp := client.StopQueryRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/StopQuery +func (c *CloudWatchLogs) StopQueryRequest(input *StopQueryInput) (req *request.Request, output *StopQueryOutput) { + op := &request.Operation{ + Name: opStopQuery, + HTTPMethod: "POST", + HTTPPath: "/", } - if invalidParams.Len() > 0 { - return invalidParams + if input == nil { + input = &StopQueryInput{} } - return nil -} -// SetKmsKeyId sets the KmsKeyId field's value. -func (s *AssociateKmsKeyInput) SetKmsKeyId(v string) *AssociateKmsKeyInput { - s.KmsKeyId = &v - return s + output = &StopQueryOutput{} + req = c.newRequest(op, input, output) + return } -// SetLogGroupName sets the LogGroupName field's value. -func (s *AssociateKmsKeyInput) SetLogGroupName(v string) *AssociateKmsKeyInput { - s.LogGroupName = &v - return s +// StopQuery API operation for Amazon CloudWatch Logs. +// +// Stops a CloudWatch Logs Insights query that is in progress. If the query +// has already ended, the operation returns an error indicating that the specified +// query is not running. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for Amazon CloudWatch Logs's +// API operation StopQuery for usage and error information. +// +// Returned Error Types: +// * InvalidParameterException +// A parameter is specified incorrectly. +// +// * ResourceNotFoundException +// The specified resource does not exist. +// +// * ServiceUnavailableException +// The service cannot complete the request. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/StopQuery +func (c *CloudWatchLogs) StopQuery(input *StopQueryInput) (*StopQueryOutput, error) { + req, out := c.StopQueryRequest(input) + return out, req.Send() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/AssociateKmsKeyOutput -type AssociateKmsKeyOutput struct { - _ struct{} `type:"structure"` -} +// StopQueryWithContext is the same as StopQuery with the addition of +// the ability to pass a context and additional request options. +// +// See StopQuery for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *CloudWatchLogs) StopQueryWithContext(ctx aws.Context, input *StopQueryInput, opts ...request.Option) (*StopQueryOutput, error) { + req, out := c.StopQueryRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +const opTagLogGroup = "TagLogGroup" + +// TagLogGroupRequest generates a "aws/request.Request" representing the +// client's request for the TagLogGroup operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See TagLogGroup for more information on using the TagLogGroup +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the TagLogGroupRequest method. +// req, resp := client.TagLogGroupRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TagLogGroup +func (c *CloudWatchLogs) TagLogGroupRequest(input *TagLogGroupInput) (req *request.Request, output *TagLogGroupOutput) { + op := &request.Operation{ + Name: opTagLogGroup, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &TagLogGroupInput{} + } + + output = &TagLogGroupOutput{} + req = c.newRequest(op, input, output) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) + return +} + +// TagLogGroup API operation for Amazon CloudWatch Logs. +// +// Adds or updates the specified tags for the specified log group. +// +// To list the tags for a log group, use ListTagsLogGroup. To remove tags, use +// UntagLogGroup. +// +// For more information about tags, see Tag Log Groups in Amazon CloudWatch +// Logs (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/log-group-tagging.html) +// in the Amazon CloudWatch Logs User Guide. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for Amazon CloudWatch Logs's +// API operation TagLogGroup for usage and error information. +// +// Returned Error Types: +// * ResourceNotFoundException +// The specified resource does not exist. +// +// * InvalidParameterException +// A parameter is specified incorrectly. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TagLogGroup +func (c *CloudWatchLogs) TagLogGroup(input *TagLogGroupInput) (*TagLogGroupOutput, error) { + req, out := c.TagLogGroupRequest(input) + return out, req.Send() +} + +// TagLogGroupWithContext is the same as TagLogGroup with the addition of +// the ability to pass a context and additional request options. +// +// See TagLogGroup for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *CloudWatchLogs) TagLogGroupWithContext(ctx aws.Context, input *TagLogGroupInput, opts ...request.Option) (*TagLogGroupOutput, error) { + req, out := c.TagLogGroupRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +const opTestMetricFilter = "TestMetricFilter" + +// TestMetricFilterRequest generates a "aws/request.Request" representing the +// client's request for the TestMetricFilter operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See TestMetricFilter for more information on using the TestMetricFilter +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the TestMetricFilterRequest method. +// req, resp := client.TestMetricFilterRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TestMetricFilter +func (c *CloudWatchLogs) TestMetricFilterRequest(input *TestMetricFilterInput) (req *request.Request, output *TestMetricFilterOutput) { + op := &request.Operation{ + Name: opTestMetricFilter, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &TestMetricFilterInput{} + } + + output = &TestMetricFilterOutput{} + req = c.newRequest(op, input, output) + return +} + +// TestMetricFilter API operation for Amazon CloudWatch Logs. +// +// Tests the filter pattern of a metric filter against a sample of log event +// messages. You can use this operation to validate the correctness of a metric +// filter pattern. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for Amazon CloudWatch Logs's +// API operation TestMetricFilter for usage and error information. +// +// Returned Error Types: +// * InvalidParameterException +// A parameter is specified incorrectly. +// +// * ServiceUnavailableException +// The service cannot complete the request. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TestMetricFilter +func (c *CloudWatchLogs) TestMetricFilter(input *TestMetricFilterInput) (*TestMetricFilterOutput, error) { + req, out := c.TestMetricFilterRequest(input) + return out, req.Send() +} + +// TestMetricFilterWithContext is the same as TestMetricFilter with the addition of +// the ability to pass a context and additional request options. +// +// See TestMetricFilter for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *CloudWatchLogs) TestMetricFilterWithContext(ctx aws.Context, input *TestMetricFilterInput, opts ...request.Option) (*TestMetricFilterOutput, error) { + req, out := c.TestMetricFilterRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +const opUntagLogGroup = "UntagLogGroup" + +// UntagLogGroupRequest generates a "aws/request.Request" representing the +// client's request for the UntagLogGroup operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See UntagLogGroup for more information on using the UntagLogGroup +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the UntagLogGroupRequest method. +// req, resp := client.UntagLogGroupRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/UntagLogGroup +func (c *CloudWatchLogs) UntagLogGroupRequest(input *UntagLogGroupInput) (req *request.Request, output *UntagLogGroupOutput) { + op := &request.Operation{ + Name: opUntagLogGroup, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &UntagLogGroupInput{} + } + + output = &UntagLogGroupOutput{} + req = c.newRequest(op, input, output) + req.Handlers.Unmarshal.Swap(jsonrpc.UnmarshalHandler.Name, protocol.UnmarshalDiscardBodyHandler) + return +} + +// UntagLogGroup API operation for Amazon CloudWatch Logs. +// +// Removes the specified tags from the specified log group. +// +// To list the tags for a log group, use ListTagsLogGroup. To add tags, use +// UntagLogGroup. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for Amazon CloudWatch Logs's +// API operation UntagLogGroup for usage and error information. +// +// Returned Error Types: +// * ResourceNotFoundException +// The specified resource does not exist. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/UntagLogGroup +func (c *CloudWatchLogs) UntagLogGroup(input *UntagLogGroupInput) (*UntagLogGroupOutput, error) { + req, out := c.UntagLogGroupRequest(input) + return out, req.Send() +} + +// UntagLogGroupWithContext is the same as UntagLogGroup with the addition of +// the ability to pass a context and additional request options. +// +// See UntagLogGroup for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *CloudWatchLogs) UntagLogGroupWithContext(ctx aws.Context, input *UntagLogGroupInput, opts ...request.Option) (*UntagLogGroupOutput, error) { + req, out := c.UntagLogGroupRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +type AssociateKmsKeyInput struct { + _ struct{} `type:"structure"` + + // The Amazon Resource Name (ARN) of the CMK to use when encrypting log data. + // This must be a symmetric CMK. For more information, see Amazon Resource Names + // - AWS Key Management Service (AWS KMS) (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#arn-syntax-kms) + // and Using Symmetric and Asymmetric Keys (https://docs.aws.amazon.com/kms/latest/developerguide/symmetric-asymmetric.html). + // + // KmsKeyId is a required field + KmsKeyId *string `locationName:"kmsKeyId" type:"string" required:"true"` + + // The name of the log group. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s AssociateKmsKeyInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s AssociateKmsKeyInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *AssociateKmsKeyInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "AssociateKmsKeyInput"} + if s.KmsKeyId == nil { + invalidParams.Add(request.NewErrParamRequired("KmsKeyId")) + } + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetKmsKeyId sets the KmsKeyId field's value. +func (s *AssociateKmsKeyInput) SetKmsKeyId(v string) *AssociateKmsKeyInput { + s.KmsKeyId = &v + return s +} + +// SetLogGroupName sets the LogGroupName field's value. +func (s *AssociateKmsKeyInput) SetLogGroupName(v string) *AssociateKmsKeyInput { + s.LogGroupName = &v + return s +} + +type AssociateKmsKeyOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s AssociateKmsKeyOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s AssociateKmsKeyOutput) GoString() string { + return s.String() +} + +type CancelExportTaskInput struct { + _ struct{} `type:"structure"` + + // The ID of the export task. + // + // TaskId is a required field + TaskId *string `locationName:"taskId" min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s CancelExportTaskInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CancelExportTaskInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *CancelExportTaskInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CancelExportTaskInput"} + if s.TaskId == nil { + invalidParams.Add(request.NewErrParamRequired("TaskId")) + } + if s.TaskId != nil && len(*s.TaskId) < 1 { + invalidParams.Add(request.NewErrParamMinLen("TaskId", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetTaskId sets the TaskId field's value. +func (s *CancelExportTaskInput) SetTaskId(v string) *CancelExportTaskInput { + s.TaskId = &v + return s +} + +type CancelExportTaskOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s CancelExportTaskOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CancelExportTaskOutput) GoString() string { + return s.String() +} + +type CreateExportTaskInput struct { + _ struct{} `type:"structure"` + + // The name of S3 bucket for the exported log data. The bucket must be in the + // same AWS region. + // + // Destination is a required field + Destination *string `locationName:"destination" min:"1" type:"string" required:"true"` + + // The prefix used as the start of the key for every object exported. If you + // don't specify a value, the default is exportedlogs. + DestinationPrefix *string `locationName:"destinationPrefix" type:"string"` + + // The start time of the range for the request, expressed as the number of milliseconds + // after Jan 1, 1970 00:00:00 UTC. Events with a timestamp earlier than this + // time are not exported. + // + // From is a required field + From *int64 `locationName:"from" type:"long" required:"true"` + + // The name of the log group. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + + // Export only log streams that match the provided prefix. If you don't specify + // a value, no prefix filter is applied. + LogStreamNamePrefix *string `locationName:"logStreamNamePrefix" min:"1" type:"string"` + + // The name of the export task. + TaskName *string `locationName:"taskName" min:"1" type:"string"` + + // The end time of the range for the request, expressed as the number of milliseconds + // after Jan 1, 1970 00:00:00 UTC. Events with a timestamp later than this time + // are not exported. + // + // To is a required field + To *int64 `locationName:"to" type:"long" required:"true"` +} + +// String returns the string representation +func (s CreateExportTaskInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CreateExportTaskInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *CreateExportTaskInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CreateExportTaskInput"} + if s.Destination == nil { + invalidParams.Add(request.NewErrParamRequired("Destination")) + } + if s.Destination != nil && len(*s.Destination) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Destination", 1)) + } + if s.From == nil { + invalidParams.Add(request.NewErrParamRequired("From")) + } + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + if s.LogStreamNamePrefix != nil && len(*s.LogStreamNamePrefix) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogStreamNamePrefix", 1)) + } + if s.TaskName != nil && len(*s.TaskName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("TaskName", 1)) + } + if s.To == nil { + invalidParams.Add(request.NewErrParamRequired("To")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetDestination sets the Destination field's value. +func (s *CreateExportTaskInput) SetDestination(v string) *CreateExportTaskInput { + s.Destination = &v + return s +} + +// SetDestinationPrefix sets the DestinationPrefix field's value. +func (s *CreateExportTaskInput) SetDestinationPrefix(v string) *CreateExportTaskInput { + s.DestinationPrefix = &v + return s +} + +// SetFrom sets the From field's value. +func (s *CreateExportTaskInput) SetFrom(v int64) *CreateExportTaskInput { + s.From = &v + return s +} + +// SetLogGroupName sets the LogGroupName field's value. +func (s *CreateExportTaskInput) SetLogGroupName(v string) *CreateExportTaskInput { + s.LogGroupName = &v + return s +} + +// SetLogStreamNamePrefix sets the LogStreamNamePrefix field's value. +func (s *CreateExportTaskInput) SetLogStreamNamePrefix(v string) *CreateExportTaskInput { + s.LogStreamNamePrefix = &v + return s +} + +// SetTaskName sets the TaskName field's value. +func (s *CreateExportTaskInput) SetTaskName(v string) *CreateExportTaskInput { + s.TaskName = &v + return s +} + +// SetTo sets the To field's value. +func (s *CreateExportTaskInput) SetTo(v int64) *CreateExportTaskInput { + s.To = &v + return s +} + +type CreateExportTaskOutput struct { + _ struct{} `type:"structure"` + + // The ID of the export task. + TaskId *string `locationName:"taskId" min:"1" type:"string"` +} + +// String returns the string representation +func (s CreateExportTaskOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CreateExportTaskOutput) GoString() string { + return s.String() +} + +// SetTaskId sets the TaskId field's value. +func (s *CreateExportTaskOutput) SetTaskId(v string) *CreateExportTaskOutput { + s.TaskId = &v + return s +} + +type CreateLogGroupInput struct { + _ struct{} `type:"structure"` + + // The Amazon Resource Name (ARN) of the CMK to use when encrypting log data. + // For more information, see Amazon Resource Names - AWS Key Management Service + // (AWS KMS) (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#arn-syntax-kms). + KmsKeyId *string `locationName:"kmsKeyId" type:"string"` + + // The name of the log group. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + + // The key-value pairs to use for the tags. + Tags map[string]*string `locationName:"tags" min:"1" type:"map"` +} + +// String returns the string representation +func (s CreateLogGroupInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CreateLogGroupInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *CreateLogGroupInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CreateLogGroupInput"} + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + if s.Tags != nil && len(s.Tags) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Tags", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetKmsKeyId sets the KmsKeyId field's value. +func (s *CreateLogGroupInput) SetKmsKeyId(v string) *CreateLogGroupInput { + s.KmsKeyId = &v + return s +} + +// SetLogGroupName sets the LogGroupName field's value. +func (s *CreateLogGroupInput) SetLogGroupName(v string) *CreateLogGroupInput { + s.LogGroupName = &v + return s +} + +// SetTags sets the Tags field's value. +func (s *CreateLogGroupInput) SetTags(v map[string]*string) *CreateLogGroupInput { + s.Tags = v + return s +} + +type CreateLogGroupOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s CreateLogGroupOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CreateLogGroupOutput) GoString() string { + return s.String() +} + +type CreateLogStreamInput struct { + _ struct{} `type:"structure"` + + // The name of the log group. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + + // The name of the log stream. + // + // LogStreamName is a required field + LogStreamName *string `locationName:"logStreamName" min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s CreateLogStreamInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CreateLogStreamInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *CreateLogStreamInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CreateLogStreamInput"} + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + if s.LogStreamName == nil { + invalidParams.Add(request.NewErrParamRequired("LogStreamName")) + } + if s.LogStreamName != nil && len(*s.LogStreamName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogStreamName", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetLogGroupName sets the LogGroupName field's value. +func (s *CreateLogStreamInput) SetLogGroupName(v string) *CreateLogStreamInput { + s.LogGroupName = &v + return s +} + +// SetLogStreamName sets the LogStreamName field's value. +func (s *CreateLogStreamInput) SetLogStreamName(v string) *CreateLogStreamInput { + s.LogStreamName = &v + return s +} + +type CreateLogStreamOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s CreateLogStreamOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CreateLogStreamOutput) GoString() string { + return s.String() +} + +// The event was already logged. +type DataAlreadyAcceptedException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata + + ExpectedSequenceToken *string `locationName:"expectedSequenceToken" min:"1" type:"string"` + + Message_ *string `locationName:"message" type:"string"` +} + +// String returns the string representation +func (s DataAlreadyAcceptedException) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DataAlreadyAcceptedException) GoString() string { + return s.String() +} + +func newErrorDataAlreadyAcceptedException(v protocol.ResponseMetadata) error { + return &DataAlreadyAcceptedException{ + respMetadata: v, + } +} + +// Code returns the exception type name. +func (s DataAlreadyAcceptedException) Code() string { + return "DataAlreadyAcceptedException" +} + +// Message returns the exception's message. +func (s DataAlreadyAcceptedException) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" +} + +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s DataAlreadyAcceptedException) OrigErr() error { + return nil +} + +func (s DataAlreadyAcceptedException) Error() string { + return fmt.Sprintf("%s: %s\n%s", s.Code(), s.Message(), s.String()) +} + +// Status code returns the HTTP status code for the request's response error. +func (s DataAlreadyAcceptedException) StatusCode() int { + return s.respMetadata.StatusCode +} + +// RequestID returns the service's response RequestID for request. +func (s DataAlreadyAcceptedException) RequestID() string { + return s.respMetadata.RequestID +} + +type DeleteDestinationInput struct { + _ struct{} `type:"structure"` + + // The name of the destination. + // + // DestinationName is a required field + DestinationName *string `locationName:"destinationName" min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s DeleteDestinationInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteDestinationInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteDestinationInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteDestinationInput"} + if s.DestinationName == nil { + invalidParams.Add(request.NewErrParamRequired("DestinationName")) + } + if s.DestinationName != nil && len(*s.DestinationName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("DestinationName", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetDestinationName sets the DestinationName field's value. +func (s *DeleteDestinationInput) SetDestinationName(v string) *DeleteDestinationInput { + s.DestinationName = &v + return s +} + +type DeleteDestinationOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s DeleteDestinationOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteDestinationOutput) GoString() string { + return s.String() +} + +type DeleteLogGroupInput struct { + _ struct{} `type:"structure"` + + // The name of the log group. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s DeleteLogGroupInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteLogGroupInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteLogGroupInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteLogGroupInput"} + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetLogGroupName sets the LogGroupName field's value. +func (s *DeleteLogGroupInput) SetLogGroupName(v string) *DeleteLogGroupInput { + s.LogGroupName = &v + return s +} + +type DeleteLogGroupOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s DeleteLogGroupOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteLogGroupOutput) GoString() string { + return s.String() +} + +type DeleteLogStreamInput struct { + _ struct{} `type:"structure"` + + // The name of the log group. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + + // The name of the log stream. + // + // LogStreamName is a required field + LogStreamName *string `locationName:"logStreamName" min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s DeleteLogStreamInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteLogStreamInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteLogStreamInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteLogStreamInput"} + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + if s.LogStreamName == nil { + invalidParams.Add(request.NewErrParamRequired("LogStreamName")) + } + if s.LogStreamName != nil && len(*s.LogStreamName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogStreamName", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetLogGroupName sets the LogGroupName field's value. +func (s *DeleteLogStreamInput) SetLogGroupName(v string) *DeleteLogStreamInput { + s.LogGroupName = &v + return s +} + +// SetLogStreamName sets the LogStreamName field's value. +func (s *DeleteLogStreamInput) SetLogStreamName(v string) *DeleteLogStreamInput { + s.LogStreamName = &v + return s +} + +type DeleteLogStreamOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s DeleteLogStreamOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteLogStreamOutput) GoString() string { + return s.String() +} + +type DeleteMetricFilterInput struct { + _ struct{} `type:"structure"` + + // The name of the metric filter. + // + // FilterName is a required field + FilterName *string `locationName:"filterName" min:"1" type:"string" required:"true"` + + // The name of the log group. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s DeleteMetricFilterInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteMetricFilterInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteMetricFilterInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteMetricFilterInput"} + if s.FilterName == nil { + invalidParams.Add(request.NewErrParamRequired("FilterName")) + } + if s.FilterName != nil && len(*s.FilterName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("FilterName", 1)) + } + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetFilterName sets the FilterName field's value. +func (s *DeleteMetricFilterInput) SetFilterName(v string) *DeleteMetricFilterInput { + s.FilterName = &v + return s +} + +// SetLogGroupName sets the LogGroupName field's value. +func (s *DeleteMetricFilterInput) SetLogGroupName(v string) *DeleteMetricFilterInput { + s.LogGroupName = &v + return s +} + +type DeleteMetricFilterOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s DeleteMetricFilterOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteMetricFilterOutput) GoString() string { + return s.String() +} + +type DeleteResourcePolicyInput struct { + _ struct{} `type:"structure"` + + // The name of the policy to be revoked. This parameter is required. + PolicyName *string `locationName:"policyName" type:"string"` +} + +// String returns the string representation +func (s DeleteResourcePolicyInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteResourcePolicyInput) GoString() string { + return s.String() +} + +// SetPolicyName sets the PolicyName field's value. +func (s *DeleteResourcePolicyInput) SetPolicyName(v string) *DeleteResourcePolicyInput { + s.PolicyName = &v + return s +} + +type DeleteResourcePolicyOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s DeleteResourcePolicyOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteResourcePolicyOutput) GoString() string { + return s.String() +} + +type DeleteRetentionPolicyInput struct { + _ struct{} `type:"structure"` + + // The name of the log group. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s DeleteRetentionPolicyInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteRetentionPolicyInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteRetentionPolicyInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteRetentionPolicyInput"} + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetLogGroupName sets the LogGroupName field's value. +func (s *DeleteRetentionPolicyInput) SetLogGroupName(v string) *DeleteRetentionPolicyInput { + s.LogGroupName = &v + return s +} + +type DeleteRetentionPolicyOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s DeleteRetentionPolicyOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteRetentionPolicyOutput) GoString() string { + return s.String() +} + +type DeleteSubscriptionFilterInput struct { + _ struct{} `type:"structure"` + + // The name of the subscription filter. + // + // FilterName is a required field + FilterName *string `locationName:"filterName" min:"1" type:"string" required:"true"` + + // The name of the log group. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s DeleteSubscriptionFilterInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteSubscriptionFilterInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteSubscriptionFilterInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteSubscriptionFilterInput"} + if s.FilterName == nil { + invalidParams.Add(request.NewErrParamRequired("FilterName")) + } + if s.FilterName != nil && len(*s.FilterName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("FilterName", 1)) + } + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetFilterName sets the FilterName field's value. +func (s *DeleteSubscriptionFilterInput) SetFilterName(v string) *DeleteSubscriptionFilterInput { + s.FilterName = &v + return s +} + +// SetLogGroupName sets the LogGroupName field's value. +func (s *DeleteSubscriptionFilterInput) SetLogGroupName(v string) *DeleteSubscriptionFilterInput { + s.LogGroupName = &v + return s +} + +type DeleteSubscriptionFilterOutput struct { + _ struct{} `type:"structure"` +} // String returns the string representation -func (s AssociateKmsKeyOutput) String() string { +func (s DeleteSubscriptionFilterOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s AssociateKmsKeyOutput) GoString() string { +func (s DeleteSubscriptionFilterOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CancelExportTaskRequest -type CancelExportTaskInput struct { +type DescribeDestinationsInput struct { _ struct{} `type:"structure"` - // The ID of the export task. - // - // TaskId is a required field - TaskId *string `locationName:"taskId" min:"1" type:"string" required:"true"` + // The prefix to match. If you don't specify a value, no prefix filter is applied. + DestinationNamePrefix *string `min:"1" type:"string"` + + // The maximum number of items returned. If you don't specify a value, the default + // is up to 50 items. + Limit *int64 `locationName:"limit" min:"1" type:"integer"` + + // The token for the next set of items to return. (You received this token from + // a previous call.) + NextToken *string `locationName:"nextToken" min:"1" type:"string"` } // String returns the string representation -func (s CancelExportTaskInput) String() string { +func (s DescribeDestinationsInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s CancelExportTaskInput) GoString() string { +func (s DescribeDestinationsInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *CancelExportTaskInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "CancelExportTaskInput"} - if s.TaskId == nil { - invalidParams.Add(request.NewErrParamRequired("TaskId")) +func (s *DescribeDestinationsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DescribeDestinationsInput"} + if s.DestinationNamePrefix != nil && len(*s.DestinationNamePrefix) < 1 { + invalidParams.Add(request.NewErrParamMinLen("DestinationNamePrefix", 1)) } - if s.TaskId != nil && len(*s.TaskId) < 1 { - invalidParams.Add(request.NewErrParamMinLen("TaskId", 1)) + if s.Limit != nil && *s.Limit < 1 { + invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) + } + if s.NextToken != nil && len(*s.NextToken) < 1 { + invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) } if invalidParams.Len() > 0 { @@ -3573,104 +5001,199 @@ func (s *CancelExportTaskInput) Validate() error { return nil } -// SetTaskId sets the TaskId field's value. -func (s *CancelExportTaskInput) SetTaskId(v string) *CancelExportTaskInput { - s.TaskId = &v +// SetDestinationNamePrefix sets the DestinationNamePrefix field's value. +func (s *DescribeDestinationsInput) SetDestinationNamePrefix(v string) *DescribeDestinationsInput { + s.DestinationNamePrefix = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CancelExportTaskOutput -type CancelExportTaskOutput struct { +// SetLimit sets the Limit field's value. +func (s *DescribeDestinationsInput) SetLimit(v int64) *DescribeDestinationsInput { + s.Limit = &v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *DescribeDestinationsInput) SetNextToken(v string) *DescribeDestinationsInput { + s.NextToken = &v + return s +} + +type DescribeDestinationsOutput struct { _ struct{} `type:"structure"` + + // The destinations. + Destinations []*Destination `locationName:"destinations" type:"list"` + + // The token for the next set of items to return. The token expires after 24 + // hours. + NextToken *string `locationName:"nextToken" min:"1" type:"string"` } // String returns the string representation -func (s CancelExportTaskOutput) String() string { +func (s DescribeDestinationsOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s CancelExportTaskOutput) GoString() string { +func (s DescribeDestinationsOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CreateExportTaskRequest -type CreateExportTaskInput struct { +// SetDestinations sets the Destinations field's value. +func (s *DescribeDestinationsOutput) SetDestinations(v []*Destination) *DescribeDestinationsOutput { + s.Destinations = v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *DescribeDestinationsOutput) SetNextToken(v string) *DescribeDestinationsOutput { + s.NextToken = &v + return s +} + +type DescribeExportTasksInput struct { _ struct{} `type:"structure"` - // The name of S3 bucket for the exported log data. The bucket must be in the - // same AWS region. - // - // Destination is a required field - Destination *string `locationName:"destination" min:"1" type:"string" required:"true"` + // The maximum number of items returned. If you don't specify a value, the default + // is up to 50 items. + Limit *int64 `locationName:"limit" min:"1" type:"integer"` - // The prefix used as the start of the key for every object exported. If you - // don't specify a value, the default is exportedlogs. - DestinationPrefix *string `locationName:"destinationPrefix" type:"string"` + // The token for the next set of items to return. (You received this token from + // a previous call.) + NextToken *string `locationName:"nextToken" min:"1" type:"string"` - // The start time of the range for the request, expressed as the number of milliseconds - // after Jan 1, 1970 00:00:00 UTC. Events with a time stamp earlier than this - // time are not exported. - // - // From is a required field - From *int64 `locationName:"from" type:"long" required:"true"` + // The status code of the export task. Specifying a status code filters the + // results to zero or more export tasks. + StatusCode *string `locationName:"statusCode" type:"string" enum:"ExportTaskStatusCode"` - // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + // The ID of the export task. Specifying a task ID filters the results to zero + // or one export tasks. + TaskId *string `locationName:"taskId" min:"1" type:"string"` +} - // Export only log streams that match the provided prefix. If you don't specify - // a value, no prefix filter is applied. - LogStreamNamePrefix *string `locationName:"logStreamNamePrefix" min:"1" type:"string"` +// String returns the string representation +func (s DescribeExportTasksInput) String() string { + return awsutil.Prettify(s) +} - // The name of the export task. - TaskName *string `locationName:"taskName" min:"1" type:"string"` +// GoString returns the string representation +func (s DescribeExportTasksInput) GoString() string { + return s.String() +} - // The end time of the range for the request, expressed as the number of milliseconds - // after Jan 1, 1970 00:00:00 UTC. Events with a time stamp later than this - // time are not exported. - // - // To is a required field - To *int64 `locationName:"to" type:"long" required:"true"` +// Validate inspects the fields of the type to determine if they are valid. +func (s *DescribeExportTasksInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DescribeExportTasksInput"} + if s.Limit != nil && *s.Limit < 1 { + invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) + } + if s.NextToken != nil && len(*s.NextToken) < 1 { + invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) + } + if s.TaskId != nil && len(*s.TaskId) < 1 { + invalidParams.Add(request.NewErrParamMinLen("TaskId", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetLimit sets the Limit field's value. +func (s *DescribeExportTasksInput) SetLimit(v int64) *DescribeExportTasksInput { + s.Limit = &v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *DescribeExportTasksInput) SetNextToken(v string) *DescribeExportTasksInput { + s.NextToken = &v + return s +} + +// SetStatusCode sets the StatusCode field's value. +func (s *DescribeExportTasksInput) SetStatusCode(v string) *DescribeExportTasksInput { + s.StatusCode = &v + return s +} + +// SetTaskId sets the TaskId field's value. +func (s *DescribeExportTasksInput) SetTaskId(v string) *DescribeExportTasksInput { + s.TaskId = &v + return s +} + +type DescribeExportTasksOutput struct { + _ struct{} `type:"structure"` + + // The export tasks. + ExportTasks []*ExportTask `locationName:"exportTasks" type:"list"` + + // The token for the next set of items to return. The token expires after 24 + // hours. + NextToken *string `locationName:"nextToken" min:"1" type:"string"` +} + +// String returns the string representation +func (s DescribeExportTasksOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DescribeExportTasksOutput) GoString() string { + return s.String() +} + +// SetExportTasks sets the ExportTasks field's value. +func (s *DescribeExportTasksOutput) SetExportTasks(v []*ExportTask) *DescribeExportTasksOutput { + s.ExportTasks = v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *DescribeExportTasksOutput) SetNextToken(v string) *DescribeExportTasksOutput { + s.NextToken = &v + return s +} + +type DescribeLogGroupsInput struct { + _ struct{} `type:"structure"` + + // The maximum number of items returned. If you don't specify a value, the default + // is up to 50 items. + Limit *int64 `locationName:"limit" min:"1" type:"integer"` + + // The prefix to match. + LogGroupNamePrefix *string `locationName:"logGroupNamePrefix" min:"1" type:"string"` + + // The token for the next set of items to return. (You received this token from + // a previous call.) + NextToken *string `locationName:"nextToken" min:"1" type:"string"` } // String returns the string representation -func (s CreateExportTaskInput) String() string { +func (s DescribeLogGroupsInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s CreateExportTaskInput) GoString() string { +func (s DescribeLogGroupsInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *CreateExportTaskInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "CreateExportTaskInput"} - if s.Destination == nil { - invalidParams.Add(request.NewErrParamRequired("Destination")) - } - if s.Destination != nil && len(*s.Destination) < 1 { - invalidParams.Add(request.NewErrParamMinLen("Destination", 1)) - } - if s.From == nil { - invalidParams.Add(request.NewErrParamRequired("From")) - } - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) - } - if s.LogStreamNamePrefix != nil && len(*s.LogStreamNamePrefix) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogStreamNamePrefix", 1)) +func (s *DescribeLogGroupsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DescribeLogGroupsInput"} + if s.Limit != nil && *s.Limit < 1 { + invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) } - if s.TaskName != nil && len(*s.TaskName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("TaskName", 1)) + if s.LogGroupNamePrefix != nil && len(*s.LogGroupNamePrefix) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupNamePrefix", 1)) } - if s.To == nil { - invalidParams.Add(request.NewErrParamRequired("To")) + if s.NextToken != nil && len(*s.NextToken) < 1 { + invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) } if invalidParams.Len() > 0 { @@ -3679,111 +5202,125 @@ func (s *CreateExportTaskInput) Validate() error { return nil } -// SetDestination sets the Destination field's value. -func (s *CreateExportTaskInput) SetDestination(v string) *CreateExportTaskInput { - s.Destination = &v - return s -} - -// SetDestinationPrefix sets the DestinationPrefix field's value. -func (s *CreateExportTaskInput) SetDestinationPrefix(v string) *CreateExportTaskInput { - s.DestinationPrefix = &v - return s -} - -// SetFrom sets the From field's value. -func (s *CreateExportTaskInput) SetFrom(v int64) *CreateExportTaskInput { - s.From = &v - return s -} - -// SetLogGroupName sets the LogGroupName field's value. -func (s *CreateExportTaskInput) SetLogGroupName(v string) *CreateExportTaskInput { - s.LogGroupName = &v - return s -} - -// SetLogStreamNamePrefix sets the LogStreamNamePrefix field's value. -func (s *CreateExportTaskInput) SetLogStreamNamePrefix(v string) *CreateExportTaskInput { - s.LogStreamNamePrefix = &v +// SetLimit sets the Limit field's value. +func (s *DescribeLogGroupsInput) SetLimit(v int64) *DescribeLogGroupsInput { + s.Limit = &v return s } -// SetTaskName sets the TaskName field's value. -func (s *CreateExportTaskInput) SetTaskName(v string) *CreateExportTaskInput { - s.TaskName = &v +// SetLogGroupNamePrefix sets the LogGroupNamePrefix field's value. +func (s *DescribeLogGroupsInput) SetLogGroupNamePrefix(v string) *DescribeLogGroupsInput { + s.LogGroupNamePrefix = &v return s } -// SetTo sets the To field's value. -func (s *CreateExportTaskInput) SetTo(v int64) *CreateExportTaskInput { - s.To = &v +// SetNextToken sets the NextToken field's value. +func (s *DescribeLogGroupsInput) SetNextToken(v string) *DescribeLogGroupsInput { + s.NextToken = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CreateExportTaskResponse -type CreateExportTaskOutput struct { +type DescribeLogGroupsOutput struct { _ struct{} `type:"structure"` - // The ID of the export task. - TaskId *string `locationName:"taskId" min:"1" type:"string"` + // The log groups. + LogGroups []*LogGroup `locationName:"logGroups" type:"list"` + + // The token for the next set of items to return. The token expires after 24 + // hours. + NextToken *string `locationName:"nextToken" min:"1" type:"string"` } // String returns the string representation -func (s CreateExportTaskOutput) String() string { +func (s DescribeLogGroupsOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s CreateExportTaskOutput) GoString() string { +func (s DescribeLogGroupsOutput) GoString() string { return s.String() } -// SetTaskId sets the TaskId field's value. -func (s *CreateExportTaskOutput) SetTaskId(v string) *CreateExportTaskOutput { - s.TaskId = &v +// SetLogGroups sets the LogGroups field's value. +func (s *DescribeLogGroupsOutput) SetLogGroups(v []*LogGroup) *DescribeLogGroupsOutput { + s.LogGroups = v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CreateLogGroupRequest -type CreateLogGroupInput struct { +// SetNextToken sets the NextToken field's value. +func (s *DescribeLogGroupsOutput) SetNextToken(v string) *DescribeLogGroupsOutput { + s.NextToken = &v + return s +} + +type DescribeLogStreamsInput struct { _ struct{} `type:"structure"` - // The Amazon Resource Name (ARN) of the CMK to use when encrypting log data. - // For more information, see Amazon Resource Names - AWS Key Management Service - // (AWS KMS) (http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#arn-syntax-kms). - KmsKeyId *string `locationName:"kmsKeyId" type:"string"` + // If the value is true, results are returned in descending order. If the value + // is to false, results are returned in ascending order. The default value is + // false. + Descending *bool `locationName:"descending" type:"boolean"` + + // The maximum number of items returned. If you don't specify a value, the default + // is up to 50 items. + Limit *int64 `locationName:"limit" min:"1" type:"integer"` // The name of the log group. // // LogGroupName is a required field LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` - // The key-value pairs to use for the tags. - Tags map[string]*string `locationName:"tags" min:"1" type:"map"` + // The prefix to match. + // + // If orderBy is LastEventTime,you cannot specify this parameter. + LogStreamNamePrefix *string `locationName:"logStreamNamePrefix" min:"1" type:"string"` + + // The token for the next set of items to return. (You received this token from + // a previous call.) + NextToken *string `locationName:"nextToken" min:"1" type:"string"` + + // If the value is LogStreamName, the results are ordered by log stream name. + // If the value is LastEventTime, the results are ordered by the event time. + // The default value is LogStreamName. + // + // If you order the results by event time, you cannot specify the logStreamNamePrefix + // parameter. + // + // lastEventTimestamp represents the time of the most recent log event in the + // log stream in CloudWatch Logs. This number is expressed as the number of + // milliseconds after Jan 1, 1970 00:00:00 UTC. lastEventTimeStamp updates on + // an eventual consistency basis. It typically updates in less than an hour + // from ingestion, but may take longer in some rare situations. + OrderBy *string `locationName:"orderBy" type:"string" enum:"OrderBy"` } // String returns the string representation -func (s CreateLogGroupInput) String() string { +func (s DescribeLogStreamsInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s CreateLogGroupInput) GoString() string { +func (s DescribeLogStreamsInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *CreateLogGroupInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "CreateLogGroupInput"} +func (s *DescribeLogStreamsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DescribeLogStreamsInput"} + if s.Limit != nil && *s.Limit < 1 { + invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) + } if s.LogGroupName == nil { invalidParams.Add(request.NewErrParamRequired("LogGroupName")) } if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) } - if s.Tags != nil && len(s.Tags) < 1 { - invalidParams.Add(request.NewErrParamMinLen("Tags", 1)) + if s.LogStreamNamePrefix != nil && len(*s.LogStreamNamePrefix) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogStreamNamePrefix", 1)) + } + if s.NextToken != nil && len(*s.NextToken) < 1 { + invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) } if invalidParams.Len() > 0 { @@ -3792,78 +5329,127 @@ func (s *CreateLogGroupInput) Validate() error { return nil } -// SetKmsKeyId sets the KmsKeyId field's value. -func (s *CreateLogGroupInput) SetKmsKeyId(v string) *CreateLogGroupInput { - s.KmsKeyId = &v +// SetDescending sets the Descending field's value. +func (s *DescribeLogStreamsInput) SetDescending(v bool) *DescribeLogStreamsInput { + s.Descending = &v + return s +} + +// SetLimit sets the Limit field's value. +func (s *DescribeLogStreamsInput) SetLimit(v int64) *DescribeLogStreamsInput { + s.Limit = &v return s } // SetLogGroupName sets the LogGroupName field's value. -func (s *CreateLogGroupInput) SetLogGroupName(v string) *CreateLogGroupInput { +func (s *DescribeLogStreamsInput) SetLogGroupName(v string) *DescribeLogStreamsInput { s.LogGroupName = &v return s } -// SetTags sets the Tags field's value. -func (s *CreateLogGroupInput) SetTags(v map[string]*string) *CreateLogGroupInput { - s.Tags = v +// SetLogStreamNamePrefix sets the LogStreamNamePrefix field's value. +func (s *DescribeLogStreamsInput) SetLogStreamNamePrefix(v string) *DescribeLogStreamsInput { + s.LogStreamNamePrefix = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CreateLogGroupOutput -type CreateLogGroupOutput struct { +// SetNextToken sets the NextToken field's value. +func (s *DescribeLogStreamsInput) SetNextToken(v string) *DescribeLogStreamsInput { + s.NextToken = &v + return s +} + +// SetOrderBy sets the OrderBy field's value. +func (s *DescribeLogStreamsInput) SetOrderBy(v string) *DescribeLogStreamsInput { + s.OrderBy = &v + return s +} + +type DescribeLogStreamsOutput struct { _ struct{} `type:"structure"` + + // The log streams. + LogStreams []*LogStream `locationName:"logStreams" type:"list"` + + // The token for the next set of items to return. The token expires after 24 + // hours. + NextToken *string `locationName:"nextToken" min:"1" type:"string"` } // String returns the string representation -func (s CreateLogGroupOutput) String() string { +func (s DescribeLogStreamsOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s CreateLogGroupOutput) GoString() string { +func (s DescribeLogStreamsOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CreateLogStreamRequest -type CreateLogStreamInput struct { +// SetLogStreams sets the LogStreams field's value. +func (s *DescribeLogStreamsOutput) SetLogStreams(v []*LogStream) *DescribeLogStreamsOutput { + s.LogStreams = v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *DescribeLogStreamsOutput) SetNextToken(v string) *DescribeLogStreamsOutput { + s.NextToken = &v + return s +} + +type DescribeMetricFiltersInput struct { _ struct{} `type:"structure"` + // The prefix to match. + FilterNamePrefix *string `locationName:"filterNamePrefix" min:"1" type:"string"` + + // The maximum number of items returned. If you don't specify a value, the default + // is up to 50 items. + Limit *int64 `locationName:"limit" min:"1" type:"integer"` + // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` - // The name of the log stream. - // - // LogStreamName is a required field - LogStreamName *string `locationName:"logStreamName" min:"1" type:"string" required:"true"` + // Filters results to include only those with the specified metric name. If + // you include this parameter in your request, you must also include the metricNamespace + // parameter. + MetricName *string `locationName:"metricName" type:"string"` + + // Filters results to include only those in the specified namespace. If you + // include this parameter in your request, you must also include the metricName + // parameter. + MetricNamespace *string `locationName:"metricNamespace" type:"string"` + + // The token for the next set of items to return. (You received this token from + // a previous call.) + NextToken *string `locationName:"nextToken" min:"1" type:"string"` } // String returns the string representation -func (s CreateLogStreamInput) String() string { +func (s DescribeMetricFiltersInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s CreateLogStreamInput) GoString() string { +func (s DescribeMetricFiltersInput) GoString() string { return s.String() -} - -// Validate inspects the fields of the type to determine if they are valid. -func (s *CreateLogStreamInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "CreateLogStreamInput"} - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *DescribeMetricFiltersInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DescribeMetricFiltersInput"} + if s.FilterNamePrefix != nil && len(*s.FilterNamePrefix) < 1 { + invalidParams.Add(request.NewErrParamMinLen("FilterNamePrefix", 1)) + } + if s.Limit != nil && *s.Limit < 1 { + invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) } if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) } - if s.LogStreamName == nil { - invalidParams.Add(request.NewErrParamRequired("LogStreamName")) - } - if s.LogStreamName != nil && len(*s.LogStreamName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogStreamName", 1)) + if s.NextToken != nil && len(*s.NextToken) < 1 { + invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) } if invalidParams.Len() > 0 { @@ -3872,119 +5458,115 @@ func (s *CreateLogStreamInput) Validate() error { return nil } -// SetLogGroupName sets the LogGroupName field's value. -func (s *CreateLogStreamInput) SetLogGroupName(v string) *CreateLogStreamInput { - s.LogGroupName = &v +// SetFilterNamePrefix sets the FilterNamePrefix field's value. +func (s *DescribeMetricFiltersInput) SetFilterNamePrefix(v string) *DescribeMetricFiltersInput { + s.FilterNamePrefix = &v return s } -// SetLogStreamName sets the LogStreamName field's value. -func (s *CreateLogStreamInput) SetLogStreamName(v string) *CreateLogStreamInput { - s.LogStreamName = &v +// SetLimit sets the Limit field's value. +func (s *DescribeMetricFiltersInput) SetLimit(v int64) *DescribeMetricFiltersInput { + s.Limit = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/CreateLogStreamOutput -type CreateLogStreamOutput struct { - _ struct{} `type:"structure"` +// SetLogGroupName sets the LogGroupName field's value. +func (s *DescribeMetricFiltersInput) SetLogGroupName(v string) *DescribeMetricFiltersInput { + s.LogGroupName = &v + return s } -// String returns the string representation -func (s CreateLogStreamOutput) String() string { - return awsutil.Prettify(s) +// SetMetricName sets the MetricName field's value. +func (s *DescribeMetricFiltersInput) SetMetricName(v string) *DescribeMetricFiltersInput { + s.MetricName = &v + return s } -// GoString returns the string representation -func (s CreateLogStreamOutput) GoString() string { - return s.String() +// SetMetricNamespace sets the MetricNamespace field's value. +func (s *DescribeMetricFiltersInput) SetMetricNamespace(v string) *DescribeMetricFiltersInput { + s.MetricNamespace = &v + return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteDestinationRequest -type DeleteDestinationInput struct { +// SetNextToken sets the NextToken field's value. +func (s *DescribeMetricFiltersInput) SetNextToken(v string) *DescribeMetricFiltersInput { + s.NextToken = &v + return s +} + +type DescribeMetricFiltersOutput struct { _ struct{} `type:"structure"` - // The name of the destination. - // - // DestinationName is a required field - DestinationName *string `locationName:"destinationName" min:"1" type:"string" required:"true"` + // The metric filters. + MetricFilters []*MetricFilter `locationName:"metricFilters" type:"list"` + + // The token for the next set of items to return. The token expires after 24 + // hours. + NextToken *string `locationName:"nextToken" min:"1" type:"string"` } // String returns the string representation -func (s DeleteDestinationInput) String() string { +func (s DescribeMetricFiltersOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteDestinationInput) GoString() string { +func (s DescribeMetricFiltersOutput) GoString() string { return s.String() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *DeleteDestinationInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DeleteDestinationInput"} - if s.DestinationName == nil { - invalidParams.Add(request.NewErrParamRequired("DestinationName")) - } - if s.DestinationName != nil && len(*s.DestinationName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("DestinationName", 1)) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil +// SetMetricFilters sets the MetricFilters field's value. +func (s *DescribeMetricFiltersOutput) SetMetricFilters(v []*MetricFilter) *DescribeMetricFiltersOutput { + s.MetricFilters = v + return s } -// SetDestinationName sets the DestinationName field's value. -func (s *DeleteDestinationInput) SetDestinationName(v string) *DeleteDestinationInput { - s.DestinationName = &v +// SetNextToken sets the NextToken field's value. +func (s *DescribeMetricFiltersOutput) SetNextToken(v string) *DescribeMetricFiltersOutput { + s.NextToken = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteDestinationOutput -type DeleteDestinationOutput struct { +type DescribeQueriesInput struct { _ struct{} `type:"structure"` -} -// String returns the string representation -func (s DeleteDestinationOutput) String() string { - return awsutil.Prettify(s) -} + // Limits the returned queries to only those for the specified log group. + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` -// GoString returns the string representation -func (s DeleteDestinationOutput) GoString() string { - return s.String() -} + // Limits the number of returned queries to the specified number. + MaxResults *int64 `locationName:"maxResults" min:"1" type:"integer"` -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteLogGroupRequest -type DeleteLogGroupInput struct { - _ struct{} `type:"structure"` + // The token for the next set of items to return. The token expires after 24 + // hours. + NextToken *string `locationName:"nextToken" min:"1" type:"string"` - // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + // Limits the returned queries to only those that have the specified status. + // Valid values are Cancelled, Complete, Failed, Running, and Scheduled. + Status *string `locationName:"status" type:"string" enum:"QueryStatus"` } // String returns the string representation -func (s DeleteLogGroupInput) String() string { +func (s DescribeQueriesInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteLogGroupInput) GoString() string { +func (s DescribeQueriesInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *DeleteLogGroupInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DeleteLogGroupInput"} - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } +func (s *DescribeQueriesInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DescribeQueriesInput"} if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) } + if s.MaxResults != nil && *s.MaxResults < 1 { + invalidParams.Add(request.NewErrParamMinValue("MaxResults", 1)) + } + if s.NextToken != nil && len(*s.NextToken) < 1 { + invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) + } if invalidParams.Len() > 0 { return invalidParams @@ -3993,65 +5575,92 @@ func (s *DeleteLogGroupInput) Validate() error { } // SetLogGroupName sets the LogGroupName field's value. -func (s *DeleteLogGroupInput) SetLogGroupName(v string) *DeleteLogGroupInput { +func (s *DescribeQueriesInput) SetLogGroupName(v string) *DescribeQueriesInput { s.LogGroupName = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteLogGroupOutput -type DeleteLogGroupOutput struct { +// SetMaxResults sets the MaxResults field's value. +func (s *DescribeQueriesInput) SetMaxResults(v int64) *DescribeQueriesInput { + s.MaxResults = &v + return s +} + +// SetNextToken sets the NextToken field's value. +func (s *DescribeQueriesInput) SetNextToken(v string) *DescribeQueriesInput { + s.NextToken = &v + return s +} + +// SetStatus sets the Status field's value. +func (s *DescribeQueriesInput) SetStatus(v string) *DescribeQueriesInput { + s.Status = &v + return s +} + +type DescribeQueriesOutput struct { _ struct{} `type:"structure"` + + // The token for the next set of items to return. The token expires after 24 + // hours. + NextToken *string `locationName:"nextToken" min:"1" type:"string"` + + // The list of queries that match the request. + Queries []*QueryInfo `locationName:"queries" type:"list"` } // String returns the string representation -func (s DeleteLogGroupOutput) String() string { +func (s DescribeQueriesOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteLogGroupOutput) GoString() string { +func (s DescribeQueriesOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteLogStreamRequest -type DeleteLogStreamInput struct { +// SetNextToken sets the NextToken field's value. +func (s *DescribeQueriesOutput) SetNextToken(v string) *DescribeQueriesOutput { + s.NextToken = &v + return s +} + +// SetQueries sets the Queries field's value. +func (s *DescribeQueriesOutput) SetQueries(v []*QueryInfo) *DescribeQueriesOutput { + s.Queries = v + return s +} + +type DescribeResourcePoliciesInput struct { _ struct{} `type:"structure"` - // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + // The maximum number of resource policies to be displayed with one call of + // this API. + Limit *int64 `locationName:"limit" min:"1" type:"integer"` - // The name of the log stream. - // - // LogStreamName is a required field - LogStreamName *string `locationName:"logStreamName" min:"1" type:"string" required:"true"` + // The token for the next set of items to return. The token expires after 24 + // hours. + NextToken *string `locationName:"nextToken" min:"1" type:"string"` } // String returns the string representation -func (s DeleteLogStreamInput) String() string { +func (s DescribeResourcePoliciesInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteLogStreamInput) GoString() string { +func (s DescribeResourcePoliciesInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *DeleteLogStreamInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DeleteLogStreamInput"} - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) - } - if s.LogStreamName == nil { - invalidParams.Add(request.NewErrParamRequired("LogStreamName")) +func (s *DescribeResourcePoliciesInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DescribeResourcePoliciesInput"} + if s.Limit != nil && *s.Limit < 1 { + invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) } - if s.LogStreamName != nil && len(*s.LogStreamName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogStreamName", 1)) + if s.NextToken != nil && len(*s.NextToken) < 1 { + invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) } if invalidParams.Len() > 0 { @@ -4060,66 +5669,89 @@ func (s *DeleteLogStreamInput) Validate() error { return nil } -// SetLogGroupName sets the LogGroupName field's value. -func (s *DeleteLogStreamInput) SetLogGroupName(v string) *DeleteLogStreamInput { - s.LogGroupName = &v +// SetLimit sets the Limit field's value. +func (s *DescribeResourcePoliciesInput) SetLimit(v int64) *DescribeResourcePoliciesInput { + s.Limit = &v return s } -// SetLogStreamName sets the LogStreamName field's value. -func (s *DeleteLogStreamInput) SetLogStreamName(v string) *DeleteLogStreamInput { - s.LogStreamName = &v +// SetNextToken sets the NextToken field's value. +func (s *DescribeResourcePoliciesInput) SetNextToken(v string) *DescribeResourcePoliciesInput { + s.NextToken = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteLogStreamOutput -type DeleteLogStreamOutput struct { +type DescribeResourcePoliciesOutput struct { _ struct{} `type:"structure"` + + // The token for the next set of items to return. The token expires after 24 + // hours. + NextToken *string `locationName:"nextToken" min:"1" type:"string"` + + // The resource policies that exist in this account. + ResourcePolicies []*ResourcePolicy `locationName:"resourcePolicies" type:"list"` } // String returns the string representation -func (s DeleteLogStreamOutput) String() string { +func (s DescribeResourcePoliciesOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteLogStreamOutput) GoString() string { +func (s DescribeResourcePoliciesOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteMetricFilterRequest -type DeleteMetricFilterInput struct { +// SetNextToken sets the NextToken field's value. +func (s *DescribeResourcePoliciesOutput) SetNextToken(v string) *DescribeResourcePoliciesOutput { + s.NextToken = &v + return s +} + +// SetResourcePolicies sets the ResourcePolicies field's value. +func (s *DescribeResourcePoliciesOutput) SetResourcePolicies(v []*ResourcePolicy) *DescribeResourcePoliciesOutput { + s.ResourcePolicies = v + return s +} + +type DescribeSubscriptionFiltersInput struct { _ struct{} `type:"structure"` - // The name of the metric filter. - // - // FilterName is a required field - FilterName *string `locationName:"filterName" min:"1" type:"string" required:"true"` + // The prefix to match. If you don't specify a value, no prefix filter is applied. + FilterNamePrefix *string `locationName:"filterNamePrefix" min:"1" type:"string"` + + // The maximum number of items returned. If you don't specify a value, the default + // is up to 50 items. + Limit *int64 `locationName:"limit" min:"1" type:"integer"` // The name of the log group. // // LogGroupName is a required field LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + + // The token for the next set of items to return. (You received this token from + // a previous call.) + NextToken *string `locationName:"nextToken" min:"1" type:"string"` } // String returns the string representation -func (s DeleteMetricFilterInput) String() string { +func (s DescribeSubscriptionFiltersInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteMetricFilterInput) GoString() string { +func (s DescribeSubscriptionFiltersInput) GoString() string { return s.String() } - -// Validate inspects the fields of the type to determine if they are valid. -func (s *DeleteMetricFilterInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DeleteMetricFilterInput"} - if s.FilterName == nil { - invalidParams.Add(request.NewErrParamRequired("FilterName")) + +// Validate inspects the fields of the type to determine if they are valid. +func (s *DescribeSubscriptionFiltersInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DescribeSubscriptionFiltersInput"} + if s.FilterNamePrefix != nil && len(*s.FilterNamePrefix) < 1 { + invalidParams.Add(request.NewErrParamMinLen("FilterNamePrefix", 1)) } - if s.FilterName != nil && len(*s.FilterName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("FilterName", 1)) + if s.Limit != nil && *s.Limit < 1 { + invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) } if s.LogGroupName == nil { invalidParams.Add(request.NewErrParamRequired("LogGroupName")) @@ -4127,6 +5759,9 @@ func (s *DeleteMetricFilterInput) Validate() error { if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) } + if s.NextToken != nil && len(*s.NextToken) < 1 { + invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) + } if invalidParams.Len() > 0 { return invalidParams @@ -4134,74 +5769,136 @@ func (s *DeleteMetricFilterInput) Validate() error { return nil } -// SetFilterName sets the FilterName field's value. -func (s *DeleteMetricFilterInput) SetFilterName(v string) *DeleteMetricFilterInput { - s.FilterName = &v +// SetFilterNamePrefix sets the FilterNamePrefix field's value. +func (s *DescribeSubscriptionFiltersInput) SetFilterNamePrefix(v string) *DescribeSubscriptionFiltersInput { + s.FilterNamePrefix = &v + return s +} + +// SetLimit sets the Limit field's value. +func (s *DescribeSubscriptionFiltersInput) SetLimit(v int64) *DescribeSubscriptionFiltersInput { + s.Limit = &v return s } // SetLogGroupName sets the LogGroupName field's value. -func (s *DeleteMetricFilterInput) SetLogGroupName(v string) *DeleteMetricFilterInput { +func (s *DescribeSubscriptionFiltersInput) SetLogGroupName(v string) *DescribeSubscriptionFiltersInput { s.LogGroupName = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteMetricFilterOutput -type DeleteMetricFilterOutput struct { +// SetNextToken sets the NextToken field's value. +func (s *DescribeSubscriptionFiltersInput) SetNextToken(v string) *DescribeSubscriptionFiltersInput { + s.NextToken = &v + return s +} + +type DescribeSubscriptionFiltersOutput struct { _ struct{} `type:"structure"` + + // The token for the next set of items to return. The token expires after 24 + // hours. + NextToken *string `locationName:"nextToken" min:"1" type:"string"` + + // The subscription filters. + SubscriptionFilters []*SubscriptionFilter `locationName:"subscriptionFilters" type:"list"` } // String returns the string representation -func (s DeleteMetricFilterOutput) String() string { +func (s DescribeSubscriptionFiltersOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteMetricFilterOutput) GoString() string { +func (s DescribeSubscriptionFiltersOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteResourcePolicyRequest -type DeleteResourcePolicyInput struct { +// SetNextToken sets the NextToken field's value. +func (s *DescribeSubscriptionFiltersOutput) SetNextToken(v string) *DescribeSubscriptionFiltersOutput { + s.NextToken = &v + return s +} + +// SetSubscriptionFilters sets the SubscriptionFilters field's value. +func (s *DescribeSubscriptionFiltersOutput) SetSubscriptionFilters(v []*SubscriptionFilter) *DescribeSubscriptionFiltersOutput { + s.SubscriptionFilters = v + return s +} + +// Represents a cross-account destination that receives subscription log events. +type Destination struct { _ struct{} `type:"structure"` - // The name of the policy to be revoked. This parameter is required. - PolicyName *string `locationName:"policyName" type:"string"` + // An IAM policy document that governs which AWS accounts can create subscription + // filters against this destination. + AccessPolicy *string `locationName:"accessPolicy" min:"1" type:"string"` + + // The ARN of this destination. + Arn *string `locationName:"arn" type:"string"` + + // The creation time of the destination, expressed as the number of milliseconds + // after Jan 1, 1970 00:00:00 UTC. + CreationTime *int64 `locationName:"creationTime" type:"long"` + + // The name of the destination. + DestinationName *string `locationName:"destinationName" min:"1" type:"string"` + + // A role for impersonation, used when delivering log events to the target. + RoleArn *string `locationName:"roleArn" min:"1" type:"string"` + + // The Amazon Resource Name (ARN) of the physical target to where the log events + // are delivered (for example, a Kinesis stream). + TargetArn *string `locationName:"targetArn" min:"1" type:"string"` } // String returns the string representation -func (s DeleteResourcePolicyInput) String() string { +func (s Destination) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteResourcePolicyInput) GoString() string { +func (s Destination) GoString() string { return s.String() } -// SetPolicyName sets the PolicyName field's value. -func (s *DeleteResourcePolicyInput) SetPolicyName(v string) *DeleteResourcePolicyInput { - s.PolicyName = &v +// SetAccessPolicy sets the AccessPolicy field's value. +func (s *Destination) SetAccessPolicy(v string) *Destination { + s.AccessPolicy = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteResourcePolicyOutput -type DeleteResourcePolicyOutput struct { - _ struct{} `type:"structure"` +// SetArn sets the Arn field's value. +func (s *Destination) SetArn(v string) *Destination { + s.Arn = &v + return s } -// String returns the string representation -func (s DeleteResourcePolicyOutput) String() string { - return awsutil.Prettify(s) +// SetCreationTime sets the CreationTime field's value. +func (s *Destination) SetCreationTime(v int64) *Destination { + s.CreationTime = &v + return s } -// GoString returns the string representation -func (s DeleteResourcePolicyOutput) GoString() string { - return s.String() +// SetDestinationName sets the DestinationName field's value. +func (s *Destination) SetDestinationName(v string) *Destination { + s.DestinationName = &v + return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteRetentionPolicyRequest -type DeleteRetentionPolicyInput struct { +// SetRoleArn sets the RoleArn field's value. +func (s *Destination) SetRoleArn(v string) *Destination { + s.RoleArn = &v + return s +} + +// SetTargetArn sets the TargetArn field's value. +func (s *Destination) SetTargetArn(v string) *Destination { + s.TargetArn = &v + return s +} + +type DisassociateKmsKeyInput struct { _ struct{} `type:"structure"` // The name of the log group. @@ -4211,18 +5908,18 @@ type DeleteRetentionPolicyInput struct { } // String returns the string representation -func (s DeleteRetentionPolicyInput) String() string { +func (s DisassociateKmsKeyInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteRetentionPolicyInput) GoString() string { +func (s DisassociateKmsKeyInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *DeleteRetentionPolicyInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DeleteRetentionPolicyInput"} +func (s *DisassociateKmsKeyInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DisassociateKmsKeyInput"} if s.LogGroupName == nil { invalidParams.Add(request.NewErrParamRequired("LogGroupName")) } @@ -4237,240 +5934,281 @@ func (s *DeleteRetentionPolicyInput) Validate() error { } // SetLogGroupName sets the LogGroupName field's value. -func (s *DeleteRetentionPolicyInput) SetLogGroupName(v string) *DeleteRetentionPolicyInput { +func (s *DisassociateKmsKeyInput) SetLogGroupName(v string) *DisassociateKmsKeyInput { s.LogGroupName = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteRetentionPolicyOutput -type DeleteRetentionPolicyOutput struct { +type DisassociateKmsKeyOutput struct { _ struct{} `type:"structure"` } // String returns the string representation -func (s DeleteRetentionPolicyOutput) String() string { +func (s DisassociateKmsKeyOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteRetentionPolicyOutput) GoString() string { +func (s DisassociateKmsKeyOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteSubscriptionFilterRequest -type DeleteSubscriptionFilterInput struct { +// Represents an export task. +type ExportTask struct { _ struct{} `type:"structure"` - // The name of the subscription filter. - // - // FilterName is a required field - FilterName *string `locationName:"filterName" min:"1" type:"string" required:"true"` + // The name of Amazon S3 bucket to which the log data was exported. + Destination *string `locationName:"destination" min:"1" type:"string"` - // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + // The prefix that was used as the start of Amazon S3 key for every object exported. + DestinationPrefix *string `locationName:"destinationPrefix" type:"string"` + + // Execution info about the export task. + ExecutionInfo *ExportTaskExecutionInfo `locationName:"executionInfo" type:"structure"` + + // The start time, expressed as the number of milliseconds after Jan 1, 1970 + // 00:00:00 UTC. Events with a timestamp before this time are not exported. + From *int64 `locationName:"from" type:"long"` + + // The name of the log group from which logs data was exported. + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` + + // The status of the export task. + Status *ExportTaskStatus `locationName:"status" type:"structure"` + + // The ID of the export task. + TaskId *string `locationName:"taskId" min:"1" type:"string"` + + // The name of the export task. + TaskName *string `locationName:"taskName" min:"1" type:"string"` + + // The end time, expressed as the number of milliseconds after Jan 1, 1970 00:00:00 + // UTC. Events with a timestamp later than this time are not exported. + To *int64 `locationName:"to" type:"long"` } // String returns the string representation -func (s DeleteSubscriptionFilterInput) String() string { +func (s ExportTask) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DeleteSubscriptionFilterInput) GoString() string { +func (s ExportTask) GoString() string { return s.String() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *DeleteSubscriptionFilterInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DeleteSubscriptionFilterInput"} - if s.FilterName == nil { - invalidParams.Add(request.NewErrParamRequired("FilterName")) - } - if s.FilterName != nil && len(*s.FilterName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("FilterName", 1)) - } - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) - } +// SetDestination sets the Destination field's value. +func (s *ExportTask) SetDestination(v string) *ExportTask { + s.Destination = &v + return s +} - if invalidParams.Len() > 0 { - return invalidParams - } - return nil +// SetDestinationPrefix sets the DestinationPrefix field's value. +func (s *ExportTask) SetDestinationPrefix(v string) *ExportTask { + s.DestinationPrefix = &v + return s } -// SetFilterName sets the FilterName field's value. -func (s *DeleteSubscriptionFilterInput) SetFilterName(v string) *DeleteSubscriptionFilterInput { - s.FilterName = &v +// SetExecutionInfo sets the ExecutionInfo field's value. +func (s *ExportTask) SetExecutionInfo(v *ExportTaskExecutionInfo) *ExportTask { + s.ExecutionInfo = v + return s +} + +// SetFrom sets the From field's value. +func (s *ExportTask) SetFrom(v int64) *ExportTask { + s.From = &v return s } // SetLogGroupName sets the LogGroupName field's value. -func (s *DeleteSubscriptionFilterInput) SetLogGroupName(v string) *DeleteSubscriptionFilterInput { +func (s *ExportTask) SetLogGroupName(v string) *ExportTask { s.LogGroupName = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DeleteSubscriptionFilterOutput -type DeleteSubscriptionFilterOutput struct { - _ struct{} `type:"structure"` +// SetStatus sets the Status field's value. +func (s *ExportTask) SetStatus(v *ExportTaskStatus) *ExportTask { + s.Status = v + return s } -// String returns the string representation -func (s DeleteSubscriptionFilterOutput) String() string { - return awsutil.Prettify(s) +// SetTaskId sets the TaskId field's value. +func (s *ExportTask) SetTaskId(v string) *ExportTask { + s.TaskId = &v + return s } -// GoString returns the string representation -func (s DeleteSubscriptionFilterOutput) GoString() string { - return s.String() +// SetTaskName sets the TaskName field's value. +func (s *ExportTask) SetTaskName(v string) *ExportTask { + s.TaskName = &v + return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeDestinationsRequest -type DescribeDestinationsInput struct { - _ struct{} `type:"structure"` +// SetTo sets the To field's value. +func (s *ExportTask) SetTo(v int64) *ExportTask { + s.To = &v + return s +} - // The prefix to match. If you don't specify a value, no prefix filter is applied. - DestinationNamePrefix *string `min:"1" type:"string"` +// Represents the status of an export task. +type ExportTaskExecutionInfo struct { + _ struct{} `type:"structure"` - // The maximum number of items returned. If you don't specify a value, the default - // is up to 50 items. - Limit *int64 `locationName:"limit" min:"1" type:"integer"` + // The completion time of the export task, expressed as the number of milliseconds + // after Jan 1, 1970 00:00:00 UTC. + CompletionTime *int64 `locationName:"completionTime" type:"long"` - // The token for the next set of items to return. (You received this token from - // a previous call.) - NextToken *string `locationName:"nextToken" min:"1" type:"string"` + // The creation time of the export task, expressed as the number of milliseconds + // after Jan 1, 1970 00:00:00 UTC. + CreationTime *int64 `locationName:"creationTime" type:"long"` } // String returns the string representation -func (s DescribeDestinationsInput) String() string { +func (s ExportTaskExecutionInfo) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeDestinationsInput) GoString() string { +func (s ExportTaskExecutionInfo) GoString() string { return s.String() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *DescribeDestinationsInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DescribeDestinationsInput"} - if s.DestinationNamePrefix != nil && len(*s.DestinationNamePrefix) < 1 { - invalidParams.Add(request.NewErrParamMinLen("DestinationNamePrefix", 1)) - } - if s.Limit != nil && *s.Limit < 1 { - invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) - } - if s.NextToken != nil && len(*s.NextToken) < 1 { - invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil -} - -// SetDestinationNamePrefix sets the DestinationNamePrefix field's value. -func (s *DescribeDestinationsInput) SetDestinationNamePrefix(v string) *DescribeDestinationsInput { - s.DestinationNamePrefix = &v - return s -} - -// SetLimit sets the Limit field's value. -func (s *DescribeDestinationsInput) SetLimit(v int64) *DescribeDestinationsInput { - s.Limit = &v +// SetCompletionTime sets the CompletionTime field's value. +func (s *ExportTaskExecutionInfo) SetCompletionTime(v int64) *ExportTaskExecutionInfo { + s.CompletionTime = &v return s } -// SetNextToken sets the NextToken field's value. -func (s *DescribeDestinationsInput) SetNextToken(v string) *DescribeDestinationsInput { - s.NextToken = &v +// SetCreationTime sets the CreationTime field's value. +func (s *ExportTaskExecutionInfo) SetCreationTime(v int64) *ExportTaskExecutionInfo { + s.CreationTime = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeDestinationsResponse -type DescribeDestinationsOutput struct { +// Represents the status of an export task. +type ExportTaskStatus struct { _ struct{} `type:"structure"` - // The destinations. - Destinations []*Destination `locationName:"destinations" type:"list"` + // The status code of the export task. + Code *string `locationName:"code" type:"string" enum:"ExportTaskStatusCode"` - // The token for the next set of items to return. The token expires after 24 - // hours. - NextToken *string `locationName:"nextToken" min:"1" type:"string"` + // The status message related to the status code. + Message *string `locationName:"message" type:"string"` } // String returns the string representation -func (s DescribeDestinationsOutput) String() string { +func (s ExportTaskStatus) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeDestinationsOutput) GoString() string { +func (s ExportTaskStatus) GoString() string { return s.String() } -// SetDestinations sets the Destinations field's value. -func (s *DescribeDestinationsOutput) SetDestinations(v []*Destination) *DescribeDestinationsOutput { - s.Destinations = v +// SetCode sets the Code field's value. +func (s *ExportTaskStatus) SetCode(v string) *ExportTaskStatus { + s.Code = &v return s } -// SetNextToken sets the NextToken field's value. -func (s *DescribeDestinationsOutput) SetNextToken(v string) *DescribeDestinationsOutput { - s.NextToken = &v +// SetMessage sets the Message field's value. +func (s *ExportTaskStatus) SetMessage(v string) *ExportTaskStatus { + s.Message = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeExportTasksRequest -type DescribeExportTasksInput struct { +type FilterLogEventsInput struct { _ struct{} `type:"structure"` - // The maximum number of items returned. If you don't specify a value, the default - // is up to 50 items. + // The end of the time range, expressed as the number of milliseconds after + // Jan 1, 1970 00:00:00 UTC. Events with a timestamp later than this time are + // not returned. + EndTime *int64 `locationName:"endTime" type:"long"` + + // The filter pattern to use. For more information, see Filter and Pattern Syntax + // (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html). + // + // If not provided, all the events are matched. + FilterPattern *string `locationName:"filterPattern" type:"string"` + + // If the value is true, the operation makes a best effort to provide responses + // that contain events from multiple log streams within the log group, interleaved + // in a single response. If the value is false, all the matched log events in + // the first log stream are searched first, then those in the next log stream, + // and so on. The default is false. + // + // IMPORTANT: Starting on June 17, 2019, this parameter will be ignored and + // the value will be assumed to be true. The response from this operation will + // always interleave events from multiple log streams within a log group. + // + // Deprecated: Starting on June 17, 2019, this parameter will be ignored and the value will be assumed to be true. The response from this operation will always interleave events from multiple log streams within a log group. + Interleaved *bool `locationName:"interleaved" deprecated:"true" type:"boolean"` + + // The maximum number of events to return. The default is 10,000 events. Limit *int64 `locationName:"limit" min:"1" type:"integer"` - // The token for the next set of items to return. (You received this token from - // a previous call.) - NextToken *string `locationName:"nextToken" min:"1" type:"string"` + // The name of the log group to search. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` - // The status code of the export task. Specifying a status code filters the - // results to zero or more export tasks. - StatusCode *string `locationName:"statusCode" type:"string" enum:"ExportTaskStatusCode"` + // Filters the results to include only events from log streams that have names + // starting with this prefix. + // + // If you specify a value for both logStreamNamePrefix and logStreamNames, but + // the value for logStreamNamePrefix does not match any log stream names specified + // in logStreamNames, the action returns an InvalidParameterException error. + LogStreamNamePrefix *string `locationName:"logStreamNamePrefix" min:"1" type:"string"` - // The ID of the export task. Specifying a task ID filters the results to zero - // or one export tasks. - TaskId *string `locationName:"taskId" min:"1" type:"string"` + // Filters the results to only logs from the log streams in this list. + // + // If you specify a value for both logStreamNamePrefix and logStreamNames, the + // action returns an InvalidParameterException error. + LogStreamNames []*string `locationName:"logStreamNames" min:"1" type:"list"` + + // The token for the next set of events to return. (You received this token + // from a previous call.) + NextToken *string `locationName:"nextToken" min:"1" type:"string"` + + // The start of the time range, expressed as the number of milliseconds after + // Jan 1, 1970 00:00:00 UTC. Events with a timestamp before this time are not + // returned. + StartTime *int64 `locationName:"startTime" type:"long"` } // String returns the string representation -func (s DescribeExportTasksInput) String() string { +func (s FilterLogEventsInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeExportTasksInput) GoString() string { +func (s FilterLogEventsInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *DescribeExportTasksInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DescribeExportTasksInput"} +func (s *FilterLogEventsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "FilterLogEventsInput"} if s.Limit != nil && *s.Limit < 1 { invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) } + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + if s.LogStreamNamePrefix != nil && len(*s.LogStreamNamePrefix) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogStreamNamePrefix", 1)) + } + if s.LogStreamNames != nil && len(s.LogStreamNames) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogStreamNames", 1)) + } if s.NextToken != nil && len(*s.NextToken) < 1 { invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) } - if s.TaskId != nil && len(*s.TaskId) < 1 { - invalidParams.Add(request.NewErrParamMinLen("TaskId", 1)) - } if invalidParams.Len() > 0 { return invalidParams @@ -4478,172 +6216,176 @@ func (s *DescribeExportTasksInput) Validate() error { return nil } -// SetLimit sets the Limit field's value. -func (s *DescribeExportTasksInput) SetLimit(v int64) *DescribeExportTasksInput { - s.Limit = &v +// SetEndTime sets the EndTime field's value. +func (s *FilterLogEventsInput) SetEndTime(v int64) *FilterLogEventsInput { + s.EndTime = &v return s } -// SetNextToken sets the NextToken field's value. -func (s *DescribeExportTasksInput) SetNextToken(v string) *DescribeExportTasksInput { - s.NextToken = &v +// SetFilterPattern sets the FilterPattern field's value. +func (s *FilterLogEventsInput) SetFilterPattern(v string) *FilterLogEventsInput { + s.FilterPattern = &v return s } -// SetStatusCode sets the StatusCode field's value. -func (s *DescribeExportTasksInput) SetStatusCode(v string) *DescribeExportTasksInput { - s.StatusCode = &v +// SetInterleaved sets the Interleaved field's value. +func (s *FilterLogEventsInput) SetInterleaved(v bool) *FilterLogEventsInput { + s.Interleaved = &v return s } -// SetTaskId sets the TaskId field's value. -func (s *DescribeExportTasksInput) SetTaskId(v string) *DescribeExportTasksInput { - s.TaskId = &v +// SetLimit sets the Limit field's value. +func (s *FilterLogEventsInput) SetLimit(v int64) *FilterLogEventsInput { + s.Limit = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeExportTasksResponse -type DescribeExportTasksOutput struct { - _ struct{} `type:"structure"` - - // The export tasks. - ExportTasks []*ExportTask `locationName:"exportTasks" type:"list"` - - // The token for the next set of items to return. The token expires after 24 - // hours. - NextToken *string `locationName:"nextToken" min:"1" type:"string"` -} - -// String returns the string representation -func (s DescribeExportTasksOutput) String() string { - return awsutil.Prettify(s) +// SetLogGroupName sets the LogGroupName field's value. +func (s *FilterLogEventsInput) SetLogGroupName(v string) *FilterLogEventsInput { + s.LogGroupName = &v + return s } -// GoString returns the string representation -func (s DescribeExportTasksOutput) GoString() string { - return s.String() +// SetLogStreamNamePrefix sets the LogStreamNamePrefix field's value. +func (s *FilterLogEventsInput) SetLogStreamNamePrefix(v string) *FilterLogEventsInput { + s.LogStreamNamePrefix = &v + return s } -// SetExportTasks sets the ExportTasks field's value. -func (s *DescribeExportTasksOutput) SetExportTasks(v []*ExportTask) *DescribeExportTasksOutput { - s.ExportTasks = v +// SetLogStreamNames sets the LogStreamNames field's value. +func (s *FilterLogEventsInput) SetLogStreamNames(v []*string) *FilterLogEventsInput { + s.LogStreamNames = v return s } // SetNextToken sets the NextToken field's value. -func (s *DescribeExportTasksOutput) SetNextToken(v string) *DescribeExportTasksOutput { +func (s *FilterLogEventsInput) SetNextToken(v string) *FilterLogEventsInput { s.NextToken = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeLogGroupsRequest -type DescribeLogGroupsInput struct { - _ struct{} `type:"structure"` +// SetStartTime sets the StartTime field's value. +func (s *FilterLogEventsInput) SetStartTime(v int64) *FilterLogEventsInput { + s.StartTime = &v + return s +} - // The maximum number of items returned. If you don't specify a value, the default - // is up to 50 items. - Limit *int64 `locationName:"limit" min:"1" type:"integer"` +type FilterLogEventsOutput struct { + _ struct{} `type:"structure"` - // The prefix to match. - LogGroupNamePrefix *string `locationName:"logGroupNamePrefix" min:"1" type:"string"` + // The matched events. + Events []*FilteredLogEvent `locationName:"events" type:"list"` - // The token for the next set of items to return. (You received this token from - // a previous call.) + // The token to use when requesting the next set of items. The token expires + // after 24 hours. NextToken *string `locationName:"nextToken" min:"1" type:"string"` -} - -// String returns the string representation -func (s DescribeLogGroupsInput) String() string { - return awsutil.Prettify(s) -} -// GoString returns the string representation -func (s DescribeLogGroupsInput) GoString() string { - return s.String() + // Indicates which log streams have been searched and whether each has been + // searched completely. + SearchedLogStreams []*SearchedLogStream `locationName:"searchedLogStreams" type:"list"` } -// Validate inspects the fields of the type to determine if they are valid. -func (s *DescribeLogGroupsInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DescribeLogGroupsInput"} - if s.Limit != nil && *s.Limit < 1 { - invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) - } - if s.LogGroupNamePrefix != nil && len(*s.LogGroupNamePrefix) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupNamePrefix", 1)) - } - if s.NextToken != nil && len(*s.NextToken) < 1 { - invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil +// String returns the string representation +func (s FilterLogEventsOutput) String() string { + return awsutil.Prettify(s) } -// SetLimit sets the Limit field's value. -func (s *DescribeLogGroupsInput) SetLimit(v int64) *DescribeLogGroupsInput { - s.Limit = &v - return s +// GoString returns the string representation +func (s FilterLogEventsOutput) GoString() string { + return s.String() } -// SetLogGroupNamePrefix sets the LogGroupNamePrefix field's value. -func (s *DescribeLogGroupsInput) SetLogGroupNamePrefix(v string) *DescribeLogGroupsInput { - s.LogGroupNamePrefix = &v +// SetEvents sets the Events field's value. +func (s *FilterLogEventsOutput) SetEvents(v []*FilteredLogEvent) *FilterLogEventsOutput { + s.Events = v return s } // SetNextToken sets the NextToken field's value. -func (s *DescribeLogGroupsInput) SetNextToken(v string) *DescribeLogGroupsInput { +func (s *FilterLogEventsOutput) SetNextToken(v string) *FilterLogEventsOutput { s.NextToken = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeLogGroupsResponse -type DescribeLogGroupsOutput struct { +// SetSearchedLogStreams sets the SearchedLogStreams field's value. +func (s *FilterLogEventsOutput) SetSearchedLogStreams(v []*SearchedLogStream) *FilterLogEventsOutput { + s.SearchedLogStreams = v + return s +} + +// Represents a matched event. +type FilteredLogEvent struct { _ struct{} `type:"structure"` - // The log groups. - LogGroups []*LogGroup `locationName:"logGroups" type:"list"` + // The ID of the event. + EventId *string `locationName:"eventId" type:"string"` - // The token for the next set of items to return. The token expires after 24 - // hours. - NextToken *string `locationName:"nextToken" min:"1" type:"string"` + // The time the event was ingested, expressed as the number of milliseconds + // after Jan 1, 1970 00:00:00 UTC. + IngestionTime *int64 `locationName:"ingestionTime" type:"long"` + + // The name of the log stream to which this event belongs. + LogStreamName *string `locationName:"logStreamName" min:"1" type:"string"` + + // The data contained in the log event. + Message *string `locationName:"message" min:"1" type:"string"` + + // The time the event occurred, expressed as the number of milliseconds after + // Jan 1, 1970 00:00:00 UTC. + Timestamp *int64 `locationName:"timestamp" type:"long"` } // String returns the string representation -func (s DescribeLogGroupsOutput) String() string { +func (s FilteredLogEvent) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeLogGroupsOutput) GoString() string { +func (s FilteredLogEvent) GoString() string { return s.String() } -// SetLogGroups sets the LogGroups field's value. -func (s *DescribeLogGroupsOutput) SetLogGroups(v []*LogGroup) *DescribeLogGroupsOutput { - s.LogGroups = v +// SetEventId sets the EventId field's value. +func (s *FilteredLogEvent) SetEventId(v string) *FilteredLogEvent { + s.EventId = &v return s } -// SetNextToken sets the NextToken field's value. -func (s *DescribeLogGroupsOutput) SetNextToken(v string) *DescribeLogGroupsOutput { - s.NextToken = &v +// SetIngestionTime sets the IngestionTime field's value. +func (s *FilteredLogEvent) SetIngestionTime(v int64) *FilteredLogEvent { + s.IngestionTime = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeLogStreamsRequest -type DescribeLogStreamsInput struct { +// SetLogStreamName sets the LogStreamName field's value. +func (s *FilteredLogEvent) SetLogStreamName(v string) *FilteredLogEvent { + s.LogStreamName = &v + return s +} + +// SetMessage sets the Message field's value. +func (s *FilteredLogEvent) SetMessage(v string) *FilteredLogEvent { + s.Message = &v + return s +} + +// SetTimestamp sets the Timestamp field's value. +func (s *FilteredLogEvent) SetTimestamp(v int64) *FilteredLogEvent { + s.Timestamp = &v + return s +} + +type GetLogEventsInput struct { _ struct{} `type:"structure"` - // If the value is true, results are returned in descending order. If the value - // is to false, results are returned in ascending order. The default value is - // false. - Descending *bool `locationName:"descending" type:"boolean"` + // The end of the time range, expressed as the number of milliseconds after + // Jan 1, 1970 00:00:00 UTC. Events with a timestamp equal to or later than + // this time are not included. + EndTime *int64 `locationName:"endTime" type:"long"` - // The maximum number of items returned. If you don't specify a value, the default - // is up to 50 items. + // The maximum number of log events returned. If you don't specify a value, + // the maximum is as many log events as can fit in a response size of 1 MB, + // up to 10,000 log events. Limit *int64 `locationName:"limit" min:"1" type:"integer"` // The name of the log group. @@ -4651,43 +6393,44 @@ type DescribeLogStreamsInput struct { // LogGroupName is a required field LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` - // The prefix to match. + // The name of the log stream. // - // iIf orderBy is LastEventTime,you cannot specify this parameter. - LogStreamNamePrefix *string `locationName:"logStreamNamePrefix" min:"1" type:"string"` + // LogStreamName is a required field + LogStreamName *string `locationName:"logStreamName" min:"1" type:"string" required:"true"` // The token for the next set of items to return. (You received this token from // a previous call.) + // + // Using this token works only when you specify true for startFromHead. NextToken *string `locationName:"nextToken" min:"1" type:"string"` - // If the value is LogStreamName, the results are ordered by log stream name. - // If the value is LastEventTime, the results are ordered by the event time. - // The default value is LogStreamName. - // - // If you order the results by event time, you cannot specify the logStreamNamePrefix - // parameter. + // If the value is true, the earliest log events are returned first. If the + // value is false, the latest log events are returned first. The default value + // is false. // - // lastEventTimestamp represents the time of the most recent log event in the - // log stream in CloudWatch Logs. This number is expressed as the number of - // milliseconds after Jan 1, 1970 00:00:00 UTC. lastEventTimeStamp updates on - // an eventual consistency basis. It typically updates in less than an hour - // from ingestion, but may take longer in some rare situations. - OrderBy *string `locationName:"orderBy" type:"string" enum:"OrderBy"` + // If you are using nextToken in this operation, you must specify true for startFromHead. + StartFromHead *bool `locationName:"startFromHead" type:"boolean"` + + // The start of the time range, expressed as the number of milliseconds after + // Jan 1, 1970 00:00:00 UTC. Events with a timestamp equal to this time or later + // than this time are included. Events with a timestamp earlier than this time + // are not included. + StartTime *int64 `locationName:"startTime" type:"long"` } // String returns the string representation -func (s DescribeLogStreamsInput) String() string { +func (s GetLogEventsInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeLogStreamsInput) GoString() string { +func (s GetLogEventsInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *DescribeLogStreamsInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DescribeLogStreamsInput"} +func (s *GetLogEventsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetLogEventsInput"} if s.Limit != nil && *s.Limit < 1 { invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) } @@ -4697,8 +6440,11 @@ func (s *DescribeLogStreamsInput) Validate() error { if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) } - if s.LogStreamNamePrefix != nil && len(*s.LogStreamNamePrefix) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogStreamNamePrefix", 1)) + if s.LogStreamName == nil { + invalidParams.Add(request.NewErrParamRequired("LogStreamName")) + } + if s.LogStreamName != nil && len(*s.LogStreamName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogStreamName", 1)) } if s.NextToken != nil && len(*s.NextToken) < 1 { invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) @@ -4710,126 +6456,200 @@ func (s *DescribeLogStreamsInput) Validate() error { return nil } -// SetDescending sets the Descending field's value. -func (s *DescribeLogStreamsInput) SetDescending(v bool) *DescribeLogStreamsInput { - s.Descending = &v +// SetEndTime sets the EndTime field's value. +func (s *GetLogEventsInput) SetEndTime(v int64) *GetLogEventsInput { + s.EndTime = &v return s } // SetLimit sets the Limit field's value. -func (s *DescribeLogStreamsInput) SetLimit(v int64) *DescribeLogStreamsInput { +func (s *GetLogEventsInput) SetLimit(v int64) *GetLogEventsInput { s.Limit = &v return s } // SetLogGroupName sets the LogGroupName field's value. -func (s *DescribeLogStreamsInput) SetLogGroupName(v string) *DescribeLogStreamsInput { +func (s *GetLogEventsInput) SetLogGroupName(v string) *GetLogEventsInput { s.LogGroupName = &v return s } -// SetLogStreamNamePrefix sets the LogStreamNamePrefix field's value. -func (s *DescribeLogStreamsInput) SetLogStreamNamePrefix(v string) *DescribeLogStreamsInput { - s.LogStreamNamePrefix = &v +// SetLogStreamName sets the LogStreamName field's value. +func (s *GetLogEventsInput) SetLogStreamName(v string) *GetLogEventsInput { + s.LogStreamName = &v return s } // SetNextToken sets the NextToken field's value. -func (s *DescribeLogStreamsInput) SetNextToken(v string) *DescribeLogStreamsInput { +func (s *GetLogEventsInput) SetNextToken(v string) *GetLogEventsInput { s.NextToken = &v return s } -// SetOrderBy sets the OrderBy field's value. -func (s *DescribeLogStreamsInput) SetOrderBy(v string) *DescribeLogStreamsInput { - s.OrderBy = &v +// SetStartFromHead sets the StartFromHead field's value. +func (s *GetLogEventsInput) SetStartFromHead(v bool) *GetLogEventsInput { + s.StartFromHead = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeLogStreamsResponse -type DescribeLogStreamsOutput struct { +// SetStartTime sets the StartTime field's value. +func (s *GetLogEventsInput) SetStartTime(v int64) *GetLogEventsInput { + s.StartTime = &v + return s +} + +type GetLogEventsOutput struct { _ struct{} `type:"structure"` - // The log streams. - LogStreams []*LogStream `locationName:"logStreams" type:"list"` + // The events. + Events []*OutputLogEvent `locationName:"events" type:"list"` - // The token for the next set of items to return. The token expires after 24 - // hours. - NextToken *string `locationName:"nextToken" min:"1" type:"string"` + // The token for the next set of items in the backward direction. The token + // expires after 24 hours. This token will never be null. If you have reached + // the end of the stream, it will return the same token you passed in. + NextBackwardToken *string `locationName:"nextBackwardToken" min:"1" type:"string"` + + // The token for the next set of items in the forward direction. The token expires + // after 24 hours. If you have reached the end of the stream, it will return + // the same token you passed in. + NextForwardToken *string `locationName:"nextForwardToken" min:"1" type:"string"` } // String returns the string representation -func (s DescribeLogStreamsOutput) String() string { +func (s GetLogEventsOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeLogStreamsOutput) GoString() string { +func (s GetLogEventsOutput) GoString() string { return s.String() } -// SetLogStreams sets the LogStreams field's value. -func (s *DescribeLogStreamsOutput) SetLogStreams(v []*LogStream) *DescribeLogStreamsOutput { - s.LogStreams = v +// SetEvents sets the Events field's value. +func (s *GetLogEventsOutput) SetEvents(v []*OutputLogEvent) *GetLogEventsOutput { + s.Events = v return s } -// SetNextToken sets the NextToken field's value. -func (s *DescribeLogStreamsOutput) SetNextToken(v string) *DescribeLogStreamsOutput { - s.NextToken = &v +// SetNextBackwardToken sets the NextBackwardToken field's value. +func (s *GetLogEventsOutput) SetNextBackwardToken(v string) *GetLogEventsOutput { + s.NextBackwardToken = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeMetricFiltersRequest -type DescribeMetricFiltersInput struct { +// SetNextForwardToken sets the NextForwardToken field's value. +func (s *GetLogEventsOutput) SetNextForwardToken(v string) *GetLogEventsOutput { + s.NextForwardToken = &v + return s +} + +type GetLogGroupFieldsInput struct { _ struct{} `type:"structure"` - // The prefix to match. - FilterNamePrefix *string `locationName:"filterNamePrefix" min:"1" type:"string"` + // The name of the log group to search. + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` - // The maximum number of items returned. If you don't specify a value, the default - // is up to 50 items. - Limit *int64 `locationName:"limit" min:"1" type:"integer"` + // The time to set as the center of the query. If you specify time, the 8 minutes + // before and 8 minutes after this time are searched. If you omit time, the + // past 15 minutes are queried. + // + // The time value is specified as epoch time, the number of seconds since January + // 1, 1970, 00:00:00 UTC. + Time *int64 `locationName:"time" type:"long"` +} - // The name of the log group. - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` +// String returns the string representation +func (s GetLogGroupFieldsInput) String() string { + return awsutil.Prettify(s) +} - // The name of the CloudWatch metric to which the monitored log information - // should be published. For example, you may publish to a metric called ErrorCount. - MetricName *string `locationName:"metricName" type:"string"` +// GoString returns the string representation +func (s GetLogGroupFieldsInput) GoString() string { + return s.String() +} - // The namespace of the CloudWatch metric. - MetricNamespace *string `locationName:"metricNamespace" type:"string"` +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetLogGroupFieldsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetLogGroupFieldsInput"} + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetLogGroupName sets the LogGroupName field's value. +func (s *GetLogGroupFieldsInput) SetLogGroupName(v string) *GetLogGroupFieldsInput { + s.LogGroupName = &v + return s +} + +// SetTime sets the Time field's value. +func (s *GetLogGroupFieldsInput) SetTime(v int64) *GetLogGroupFieldsInput { + s.Time = &v + return s +} + +type GetLogGroupFieldsOutput struct { + _ struct{} `type:"structure"` + + // The array of fields found in the query. Each object in the array contains + // the name of the field, along with the percentage of time it appeared in the + // log events that were queried. + LogGroupFields []*LogGroupField `locationName:"logGroupFields" type:"list"` +} + +// String returns the string representation +func (s GetLogGroupFieldsOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetLogGroupFieldsOutput) GoString() string { + return s.String() +} + +// SetLogGroupFields sets the LogGroupFields field's value. +func (s *GetLogGroupFieldsOutput) SetLogGroupFields(v []*LogGroupField) *GetLogGroupFieldsOutput { + s.LogGroupFields = v + return s +} - // The token for the next set of items to return. (You received this token from - // a previous call.) - NextToken *string `locationName:"nextToken" min:"1" type:"string"` +type GetLogRecordInput struct { + _ struct{} `type:"structure"` + + // The pointer corresponding to the log event record you want to retrieve. You + // get this from the response of a GetQueryResults operation. In that response, + // the value of the @ptr field for a log event is the value to use as logRecordPointer + // to retrieve that complete log event record. + // + // LogRecordPointer is a required field + LogRecordPointer *string `locationName:"logRecordPointer" type:"string" required:"true"` } // String returns the string representation -func (s DescribeMetricFiltersInput) String() string { +func (s GetLogRecordInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeMetricFiltersInput) GoString() string { +func (s GetLogRecordInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *DescribeMetricFiltersInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DescribeMetricFiltersInput"} - if s.FilterNamePrefix != nil && len(*s.FilterNamePrefix) < 1 { - invalidParams.Add(request.NewErrParamMinLen("FilterNamePrefix", 1)) - } - if s.Limit != nil && *s.Limit < 1 { - invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) - } - if s.NextToken != nil && len(*s.NextToken) < 1 { - invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) +func (s *GetLogRecordInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetLogRecordInput"} + if s.LogRecordPointer == nil { + invalidParams.Add(request.NewErrParamRequired("LogRecordPointer")) } if invalidParams.Len() > 0 { @@ -4838,107 +6658,164 @@ func (s *DescribeMetricFiltersInput) Validate() error { return nil } -// SetFilterNamePrefix sets the FilterNamePrefix field's value. -func (s *DescribeMetricFiltersInput) SetFilterNamePrefix(v string) *DescribeMetricFiltersInput { - s.FilterNamePrefix = &v +// SetLogRecordPointer sets the LogRecordPointer field's value. +func (s *GetLogRecordInput) SetLogRecordPointer(v string) *GetLogRecordInput { + s.LogRecordPointer = &v return s } -// SetLimit sets the Limit field's value. -func (s *DescribeMetricFiltersInput) SetLimit(v int64) *DescribeMetricFiltersInput { - s.Limit = &v - return s +type GetLogRecordOutput struct { + _ struct{} `type:"structure"` + + // The requested log event, as a JSON string. + LogRecord map[string]*string `locationName:"logRecord" type:"map"` } -// SetLogGroupName sets the LogGroupName field's value. -func (s *DescribeMetricFiltersInput) SetLogGroupName(v string) *DescribeMetricFiltersInput { - s.LogGroupName = &v - return s +// String returns the string representation +func (s GetLogRecordOutput) String() string { + return awsutil.Prettify(s) } -// SetMetricName sets the MetricName field's value. -func (s *DescribeMetricFiltersInput) SetMetricName(v string) *DescribeMetricFiltersInput { - s.MetricName = &v - return s +// GoString returns the string representation +func (s GetLogRecordOutput) GoString() string { + return s.String() } -// SetMetricNamespace sets the MetricNamespace field's value. -func (s *DescribeMetricFiltersInput) SetMetricNamespace(v string) *DescribeMetricFiltersInput { - s.MetricNamespace = &v +// SetLogRecord sets the LogRecord field's value. +func (s *GetLogRecordOutput) SetLogRecord(v map[string]*string) *GetLogRecordOutput { + s.LogRecord = v return s } -// SetNextToken sets the NextToken field's value. -func (s *DescribeMetricFiltersInput) SetNextToken(v string) *DescribeMetricFiltersInput { - s.NextToken = &v +type GetQueryResultsInput struct { + _ struct{} `type:"structure"` + + // The ID number of the query. + // + // QueryId is a required field + QueryId *string `locationName:"queryId" type:"string" required:"true"` +} + +// String returns the string representation +func (s GetQueryResultsInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetQueryResultsInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetQueryResultsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetQueryResultsInput"} + if s.QueryId == nil { + invalidParams.Add(request.NewErrParamRequired("QueryId")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetQueryId sets the QueryId field's value. +func (s *GetQueryResultsInput) SetQueryId(v string) *GetQueryResultsInput { + s.QueryId = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeMetricFiltersResponse -type DescribeMetricFiltersOutput struct { +type GetQueryResultsOutput struct { _ struct{} `type:"structure"` - // The metric filters. - MetricFilters []*MetricFilter `locationName:"metricFilters" type:"list"` - - // The token for the next set of items to return. The token expires after 24 - // hours. - NextToken *string `locationName:"nextToken" min:"1" type:"string"` + // The log events that matched the query criteria during the most recent time + // it ran. + // + // The results value is an array of arrays. Each log event is one object in + // the top-level array. Each of these log event objects is an array of field/value + // pairs. + Results [][]*ResultField `locationName:"results" type:"list"` + + // Includes the number of log events scanned by the query, the number of log + // events that matched the query criteria, and the total number of bytes in + // the log events that were scanned. + Statistics *QueryStatistics `locationName:"statistics" type:"structure"` + + // The status of the most recent running of the query. Possible values are Cancelled, + // Complete, Failed, Running, Scheduled, Timeout, and Unknown. + // + // Queries time out after 15 minutes of execution. To avoid having your queries + // time out, reduce the time range being searched, or partition your query into + // a number of queries. + Status *string `locationName:"status" type:"string" enum:"QueryStatus"` } // String returns the string representation -func (s DescribeMetricFiltersOutput) String() string { +func (s GetQueryResultsOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeMetricFiltersOutput) GoString() string { +func (s GetQueryResultsOutput) GoString() string { return s.String() } -// SetMetricFilters sets the MetricFilters field's value. -func (s *DescribeMetricFiltersOutput) SetMetricFilters(v []*MetricFilter) *DescribeMetricFiltersOutput { - s.MetricFilters = v +// SetResults sets the Results field's value. +func (s *GetQueryResultsOutput) SetResults(v [][]*ResultField) *GetQueryResultsOutput { + s.Results = v return s } -// SetNextToken sets the NextToken field's value. -func (s *DescribeMetricFiltersOutput) SetNextToken(v string) *DescribeMetricFiltersOutput { - s.NextToken = &v +// SetStatistics sets the Statistics field's value. +func (s *GetQueryResultsOutput) SetStatistics(v *QueryStatistics) *GetQueryResultsOutput { + s.Statistics = v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeResourcePoliciesRequest -type DescribeResourcePoliciesInput struct { +// SetStatus sets the Status field's value. +func (s *GetQueryResultsOutput) SetStatus(v string) *GetQueryResultsOutput { + s.Status = &v + return s +} + +// Represents a log event, which is a record of activity that was recorded by +// the application or resource being monitored. +type InputLogEvent struct { _ struct{} `type:"structure"` - // The maximum number of resource policies to be displayed with one call of - // this API. - Limit *int64 `locationName:"limit" min:"1" type:"integer"` + // The raw event message. + // + // Message is a required field + Message *string `locationName:"message" min:"1" type:"string" required:"true"` - // The token for the next set of items to return. The token expires after 24 - // hours. - NextToken *string `locationName:"nextToken" min:"1" type:"string"` + // The time the event occurred, expressed as the number of milliseconds after + // Jan 1, 1970 00:00:00 UTC. + // + // Timestamp is a required field + Timestamp *int64 `locationName:"timestamp" type:"long" required:"true"` } // String returns the string representation -func (s DescribeResourcePoliciesInput) String() string { +func (s InputLogEvent) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeResourcePoliciesInput) GoString() string { +func (s InputLogEvent) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *DescribeResourcePoliciesInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DescribeResourcePoliciesInput"} - if s.Limit != nil && *s.Limit < 1 { - invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) +func (s *InputLogEvent) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "InputLogEvent"} + if s.Message == nil { + invalidParams.Add(request.NewErrParamRequired("Message")) } - if s.NextToken != nil && len(*s.NextToken) < 1 { - invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) + if s.Message != nil && len(*s.Message) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Message", 1)) + } + if s.Timestamp == nil { + invalidParams.Add(request.NewErrParamRequired("Timestamp")) } if invalidParams.Len() > 0 { @@ -4947,241 +6824,246 @@ func (s *DescribeResourcePoliciesInput) Validate() error { return nil } -// SetLimit sets the Limit field's value. -func (s *DescribeResourcePoliciesInput) SetLimit(v int64) *DescribeResourcePoliciesInput { - s.Limit = &v +// SetMessage sets the Message field's value. +func (s *InputLogEvent) SetMessage(v string) *InputLogEvent { + s.Message = &v return s } -// SetNextToken sets the NextToken field's value. -func (s *DescribeResourcePoliciesInput) SetNextToken(v string) *DescribeResourcePoliciesInput { - s.NextToken = &v +// SetTimestamp sets the Timestamp field's value. +func (s *InputLogEvent) SetTimestamp(v int64) *InputLogEvent { + s.Timestamp = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeResourcePoliciesResponse -type DescribeResourcePoliciesOutput struct { - _ struct{} `type:"structure"` - - // The token for the next set of items to return. The token expires after 24 - // hours. - NextToken *string `locationName:"nextToken" min:"1" type:"string"` +// The operation is not valid on the specified resource. +type InvalidOperationException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata - // The resource policies that exist in this account. - ResourcePolicies []*ResourcePolicy `locationName:"resourcePolicies" type:"list"` + Message_ *string `locationName:"message" type:"string"` } // String returns the string representation -func (s DescribeResourcePoliciesOutput) String() string { +func (s InvalidOperationException) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeResourcePoliciesOutput) GoString() string { +func (s InvalidOperationException) GoString() string { return s.String() } -// SetNextToken sets the NextToken field's value. -func (s *DescribeResourcePoliciesOutput) SetNextToken(v string) *DescribeResourcePoliciesOutput { - s.NextToken = &v - return s +func newErrorInvalidOperationException(v protocol.ResponseMetadata) error { + return &InvalidOperationException{ + respMetadata: v, + } } -// SetResourcePolicies sets the ResourcePolicies field's value. -func (s *DescribeResourcePoliciesOutput) SetResourcePolicies(v []*ResourcePolicy) *DescribeResourcePoliciesOutput { - s.ResourcePolicies = v - return s +// Code returns the exception type name. +func (s InvalidOperationException) Code() string { + return "InvalidOperationException" } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeSubscriptionFiltersRequest -type DescribeSubscriptionFiltersInput struct { - _ struct{} `type:"structure"` +// Message returns the exception's message. +func (s InvalidOperationException) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" +} - // The prefix to match. If you don't specify a value, no prefix filter is applied. - FilterNamePrefix *string `locationName:"filterNamePrefix" min:"1" type:"string"` +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s InvalidOperationException) OrigErr() error { + return nil +} - // The maximum number of items returned. If you don't specify a value, the default - // is up to 50 items. - Limit *int64 `locationName:"limit" min:"1" type:"integer"` +func (s InvalidOperationException) Error() string { + return fmt.Sprintf("%s: %s", s.Code(), s.Message()) +} - // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` +// Status code returns the HTTP status code for the request's response error. +func (s InvalidOperationException) StatusCode() int { + return s.respMetadata.StatusCode +} - // The token for the next set of items to return. (You received this token from - // a previous call.) - NextToken *string `locationName:"nextToken" min:"1" type:"string"` +// RequestID returns the service's response RequestID for request. +func (s InvalidOperationException) RequestID() string { + return s.respMetadata.RequestID +} + +// A parameter is specified incorrectly. +type InvalidParameterException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata + + Message_ *string `locationName:"message" type:"string"` } // String returns the string representation -func (s DescribeSubscriptionFiltersInput) String() string { +func (s InvalidParameterException) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeSubscriptionFiltersInput) GoString() string { +func (s InvalidParameterException) GoString() string { return s.String() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *DescribeSubscriptionFiltersInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DescribeSubscriptionFiltersInput"} - if s.FilterNamePrefix != nil && len(*s.FilterNamePrefix) < 1 { - invalidParams.Add(request.NewErrParamMinLen("FilterNamePrefix", 1)) - } - if s.Limit != nil && *s.Limit < 1 { - invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) - } - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) - } - if s.NextToken != nil && len(*s.NextToken) < 1 { - invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) +func newErrorInvalidParameterException(v protocol.ResponseMetadata) error { + return &InvalidParameterException{ + respMetadata: v, } +} - if invalidParams.Len() > 0 { - return invalidParams +// Code returns the exception type name. +func (s InvalidParameterException) Code() string { + return "InvalidParameterException" +} + +// Message returns the exception's message. +func (s InvalidParameterException) Message() string { + if s.Message_ != nil { + return *s.Message_ } - return nil + return "" } -// SetFilterNamePrefix sets the FilterNamePrefix field's value. -func (s *DescribeSubscriptionFiltersInput) SetFilterNamePrefix(v string) *DescribeSubscriptionFiltersInput { - s.FilterNamePrefix = &v - return s +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s InvalidParameterException) OrigErr() error { + return nil } -// SetLimit sets the Limit field's value. -func (s *DescribeSubscriptionFiltersInput) SetLimit(v int64) *DescribeSubscriptionFiltersInput { - s.Limit = &v - return s +func (s InvalidParameterException) Error() string { + return fmt.Sprintf("%s: %s", s.Code(), s.Message()) } -// SetLogGroupName sets the LogGroupName field's value. -func (s *DescribeSubscriptionFiltersInput) SetLogGroupName(v string) *DescribeSubscriptionFiltersInput { - s.LogGroupName = &v - return s +// Status code returns the HTTP status code for the request's response error. +func (s InvalidParameterException) StatusCode() int { + return s.respMetadata.StatusCode } -// SetNextToken sets the NextToken field's value. -func (s *DescribeSubscriptionFiltersInput) SetNextToken(v string) *DescribeSubscriptionFiltersInput { - s.NextToken = &v - return s +// RequestID returns the service's response RequestID for request. +func (s InvalidParameterException) RequestID() string { + return s.respMetadata.RequestID } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DescribeSubscriptionFiltersResponse -type DescribeSubscriptionFiltersOutput struct { - _ struct{} `type:"structure"` +// The sequence token is not valid. You can get the correct sequence token in +// the expectedSequenceToken field in the InvalidSequenceTokenException message. +type InvalidSequenceTokenException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata - // The token for the next set of items to return. The token expires after 24 - // hours. - NextToken *string `locationName:"nextToken" min:"1" type:"string"` + ExpectedSequenceToken *string `locationName:"expectedSequenceToken" min:"1" type:"string"` - // The subscription filters. - SubscriptionFilters []*SubscriptionFilter `locationName:"subscriptionFilters" type:"list"` + Message_ *string `locationName:"message" type:"string"` } // String returns the string representation -func (s DescribeSubscriptionFiltersOutput) String() string { +func (s InvalidSequenceTokenException) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DescribeSubscriptionFiltersOutput) GoString() string { +func (s InvalidSequenceTokenException) GoString() string { return s.String() } -// SetNextToken sets the NextToken field's value. -func (s *DescribeSubscriptionFiltersOutput) SetNextToken(v string) *DescribeSubscriptionFiltersOutput { - s.NextToken = &v - return s +func newErrorInvalidSequenceTokenException(v protocol.ResponseMetadata) error { + return &InvalidSequenceTokenException{ + respMetadata: v, + } } -// SetSubscriptionFilters sets the SubscriptionFilters field's value. -func (s *DescribeSubscriptionFiltersOutput) SetSubscriptionFilters(v []*SubscriptionFilter) *DescribeSubscriptionFiltersOutput { - s.SubscriptionFilters = v - return s +// Code returns the exception type name. +func (s InvalidSequenceTokenException) Code() string { + return "InvalidSequenceTokenException" } -// Represents a cross-account destination that receives subscription log events. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/Destination -type Destination struct { - _ struct{} `type:"structure"` +// Message returns the exception's message. +func (s InvalidSequenceTokenException) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" +} - // An IAM policy document that governs which AWS accounts can create subscription - // filters against this destination. - AccessPolicy *string `locationName:"accessPolicy" min:"1" type:"string"` +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s InvalidSequenceTokenException) OrigErr() error { + return nil +} - // The ARN of this destination. - Arn *string `locationName:"arn" type:"string"` +func (s InvalidSequenceTokenException) Error() string { + return fmt.Sprintf("%s: %s\n%s", s.Code(), s.Message(), s.String()) +} - // The creation time of the destination, expressed as the number of milliseconds - // after Jan 1, 1970 00:00:00 UTC. - CreationTime *int64 `locationName:"creationTime" type:"long"` +// Status code returns the HTTP status code for the request's response error. +func (s InvalidSequenceTokenException) StatusCode() int { + return s.respMetadata.StatusCode +} - // The name of the destination. - DestinationName *string `locationName:"destinationName" min:"1" type:"string"` +// RequestID returns the service's response RequestID for request. +func (s InvalidSequenceTokenException) RequestID() string { + return s.respMetadata.RequestID +} - // A role for impersonation, used when delivering log events to the target. - RoleArn *string `locationName:"roleArn" min:"1" type:"string"` +// You have reached the maximum number of resources that can be created. +type LimitExceededException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata - // The Amazon Resource Name (ARN) of the physical target to where the log events - // are delivered (for example, a Kinesis stream). - TargetArn *string `locationName:"targetArn" min:"1" type:"string"` + Message_ *string `locationName:"message" type:"string"` } // String returns the string representation -func (s Destination) String() string { +func (s LimitExceededException) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s Destination) GoString() string { +func (s LimitExceededException) GoString() string { return s.String() } -// SetAccessPolicy sets the AccessPolicy field's value. -func (s *Destination) SetAccessPolicy(v string) *Destination { - s.AccessPolicy = &v - return s +func newErrorLimitExceededException(v protocol.ResponseMetadata) error { + return &LimitExceededException{ + respMetadata: v, + } } -// SetArn sets the Arn field's value. -func (s *Destination) SetArn(v string) *Destination { - s.Arn = &v - return s +// Code returns the exception type name. +func (s LimitExceededException) Code() string { + return "LimitExceededException" } -// SetCreationTime sets the CreationTime field's value. -func (s *Destination) SetCreationTime(v int64) *Destination { - s.CreationTime = &v - return s +// Message returns the exception's message. +func (s LimitExceededException) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" } -// SetDestinationName sets the DestinationName field's value. -func (s *Destination) SetDestinationName(v string) *Destination { - s.DestinationName = &v - return s +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s LimitExceededException) OrigErr() error { + return nil } -// SetRoleArn sets the RoleArn field's value. -func (s *Destination) SetRoleArn(v string) *Destination { - s.RoleArn = &v - return s +func (s LimitExceededException) Error() string { + return fmt.Sprintf("%s: %s", s.Code(), s.Message()) } -// SetTargetArn sets the TargetArn field's value. -func (s *Destination) SetTargetArn(v string) *Destination { - s.TargetArn = &v - return s +// Status code returns the HTTP status code for the request's response error. +func (s LimitExceededException) StatusCode() int { + return s.respMetadata.StatusCode } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DisassociateKmsKeyRequest -type DisassociateKmsKeyInput struct { +// RequestID returns the service's response RequestID for request. +func (s LimitExceededException) RequestID() string { + return s.respMetadata.RequestID +} + +type ListTagsLogGroupInput struct { _ struct{} `type:"structure"` // The name of the log group. @@ -5191,18 +7073,18 @@ type DisassociateKmsKeyInput struct { } // String returns the string representation -func (s DisassociateKmsKeyInput) String() string { +func (s ListTagsLogGroupInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DisassociateKmsKeyInput) GoString() string { +func (s ListTagsLogGroupInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *DisassociateKmsKeyInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DisassociateKmsKeyInput"} +func (s *ListTagsLogGroupInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ListTagsLogGroupInput"} if s.LogGroupName == nil { invalidParams.Add(request.NewErrParamRequired("LogGroupName")) } @@ -5217,494 +7099,468 @@ func (s *DisassociateKmsKeyInput) Validate() error { } // SetLogGroupName sets the LogGroupName field's value. -func (s *DisassociateKmsKeyInput) SetLogGroupName(v string) *DisassociateKmsKeyInput { +func (s *ListTagsLogGroupInput) SetLogGroupName(v string) *ListTagsLogGroupInput { s.LogGroupName = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/DisassociateKmsKeyOutput -type DisassociateKmsKeyOutput struct { +type ListTagsLogGroupOutput struct { _ struct{} `type:"structure"` + + // The tags for the log group. + Tags map[string]*string `locationName:"tags" min:"1" type:"map"` } // String returns the string representation -func (s DisassociateKmsKeyOutput) String() string { +func (s ListTagsLogGroupOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s DisassociateKmsKeyOutput) GoString() string { +func (s ListTagsLogGroupOutput) GoString() string { return s.String() } -// Represents an export task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/ExportTask -type ExportTask struct { - _ struct{} `type:"structure"` +// SetTags sets the Tags field's value. +func (s *ListTagsLogGroupOutput) SetTags(v map[string]*string) *ListTagsLogGroupOutput { + s.Tags = v + return s +} - // The name of Amazon S3 bucket to which the log data was exported. - Destination *string `locationName:"destination" min:"1" type:"string"` +// Represents a log group. +type LogGroup struct { + _ struct{} `type:"structure"` - // The prefix that was used as the start of Amazon S3 key for every object exported. - DestinationPrefix *string `locationName:"destinationPrefix" type:"string"` + // The Amazon Resource Name (ARN) of the log group. + Arn *string `locationName:"arn" type:"string"` - // Execution info about the export task. - ExecutionInfo *ExportTaskExecutionInfo `locationName:"executionInfo" type:"structure"` + // The creation time of the log group, expressed as the number of milliseconds + // after Jan 1, 1970 00:00:00 UTC. + CreationTime *int64 `locationName:"creationTime" type:"long"` - // The start time, expressed as the number of milliseconds after Jan 1, 1970 - // 00:00:00 UTC. Events with a time stamp before this time are not exported. - From *int64 `locationName:"from" type:"long"` + // The Amazon Resource Name (ARN) of the CMK to use when encrypting log data. + KmsKeyId *string `locationName:"kmsKeyId" type:"string"` - // The name of the log group from which logs data was exported. + // The name of the log group. LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` - // The status of the export task. - Status *ExportTaskStatus `locationName:"status" type:"structure"` - - // The ID of the export task. - TaskId *string `locationName:"taskId" min:"1" type:"string"` + // The number of metric filters. + MetricFilterCount *int64 `locationName:"metricFilterCount" type:"integer"` - // The name of the export task. - TaskName *string `locationName:"taskName" min:"1" type:"string"` + // The number of days to retain the log events in the specified log group. Possible + // values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, + // 1827, and 3653. + RetentionInDays *int64 `locationName:"retentionInDays" type:"integer"` - // The end time, expressed as the number of milliseconds after Jan 1, 1970 00:00:00 - // UTC. Events with a time stamp later than this time are not exported. - To *int64 `locationName:"to" type:"long"` + // The number of bytes stored. + StoredBytes *int64 `locationName:"storedBytes" type:"long"` } // String returns the string representation -func (s ExportTask) String() string { +func (s LogGroup) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s ExportTask) GoString() string { +func (s LogGroup) GoString() string { return s.String() } -// SetDestination sets the Destination field's value. -func (s *ExportTask) SetDestination(v string) *ExportTask { - s.Destination = &v +// SetArn sets the Arn field's value. +func (s *LogGroup) SetArn(v string) *LogGroup { + s.Arn = &v return s } -// SetDestinationPrefix sets the DestinationPrefix field's value. -func (s *ExportTask) SetDestinationPrefix(v string) *ExportTask { - s.DestinationPrefix = &v +// SetCreationTime sets the CreationTime field's value. +func (s *LogGroup) SetCreationTime(v int64) *LogGroup { + s.CreationTime = &v return s } -// SetExecutionInfo sets the ExecutionInfo field's value. -func (s *ExportTask) SetExecutionInfo(v *ExportTaskExecutionInfo) *ExportTask { - s.ExecutionInfo = v +// SetKmsKeyId sets the KmsKeyId field's value. +func (s *LogGroup) SetKmsKeyId(v string) *LogGroup { + s.KmsKeyId = &v return s } -// SetFrom sets the From field's value. -func (s *ExportTask) SetFrom(v int64) *ExportTask { - s.From = &v +// SetLogGroupName sets the LogGroupName field's value. +func (s *LogGroup) SetLogGroupName(v string) *LogGroup { + s.LogGroupName = &v return s } -// SetLogGroupName sets the LogGroupName field's value. -func (s *ExportTask) SetLogGroupName(v string) *ExportTask { - s.LogGroupName = &v +// SetMetricFilterCount sets the MetricFilterCount field's value. +func (s *LogGroup) SetMetricFilterCount(v int64) *LogGroup { + s.MetricFilterCount = &v return s } -// SetStatus sets the Status field's value. -func (s *ExportTask) SetStatus(v *ExportTaskStatus) *ExportTask { - s.Status = v +// SetRetentionInDays sets the RetentionInDays field's value. +func (s *LogGroup) SetRetentionInDays(v int64) *LogGroup { + s.RetentionInDays = &v return s } -// SetTaskId sets the TaskId field's value. -func (s *ExportTask) SetTaskId(v string) *ExportTask { - s.TaskId = &v +// SetStoredBytes sets the StoredBytes field's value. +func (s *LogGroup) SetStoredBytes(v int64) *LogGroup { + s.StoredBytes = &v return s } -// SetTaskName sets the TaskName field's value. -func (s *ExportTask) SetTaskName(v string) *ExportTask { - s.TaskName = &v +// The fields contained in log events found by a GetLogGroupFields operation, +// along with the percentage of queried log events in which each field appears. +type LogGroupField struct { + _ struct{} `type:"structure"` + + // The name of a log field. + Name *string `locationName:"name" type:"string"` + + // The percentage of log events queried that contained the field. + Percent *int64 `locationName:"percent" type:"integer"` +} + +// String returns the string representation +func (s LogGroupField) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s LogGroupField) GoString() string { + return s.String() +} + +// SetName sets the Name field's value. +func (s *LogGroupField) SetName(v string) *LogGroupField { + s.Name = &v return s } -// SetTo sets the To field's value. -func (s *ExportTask) SetTo(v int64) *ExportTask { - s.To = &v +// SetPercent sets the Percent field's value. +func (s *LogGroupField) SetPercent(v int64) *LogGroupField { + s.Percent = &v return s } -// Represents the status of an export task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/ExportTaskExecutionInfo -type ExportTaskExecutionInfo struct { +// Represents a log stream, which is a sequence of log events from a single +// emitter of logs. +type LogStream struct { _ struct{} `type:"structure"` - // The completion time of the export task, expressed as the number of milliseconds - // after Jan 1, 1970 00:00:00 UTC. - CompletionTime *int64 `locationName:"completionTime" type:"long"` + // The Amazon Resource Name (ARN) of the log stream. + Arn *string `locationName:"arn" type:"string"` - // The creation time of the export task, expressed as the number of milliseconds + // The creation time of the stream, expressed as the number of milliseconds // after Jan 1, 1970 00:00:00 UTC. CreationTime *int64 `locationName:"creationTime" type:"long"` + + // The time of the first event, expressed as the number of milliseconds after + // Jan 1, 1970 00:00:00 UTC. + FirstEventTimestamp *int64 `locationName:"firstEventTimestamp" type:"long"` + + // The time of the most recent log event in the log stream in CloudWatch Logs. + // This number is expressed as the number of milliseconds after Jan 1, 1970 + // 00:00:00 UTC. The lastEventTime value updates on an eventual consistency + // basis. It typically updates in less than an hour from ingestion, but may + // take longer in some rare situations. + LastEventTimestamp *int64 `locationName:"lastEventTimestamp" type:"long"` + + // The ingestion time, expressed as the number of milliseconds after Jan 1, + // 1970 00:00:00 UTC. + LastIngestionTime *int64 `locationName:"lastIngestionTime" type:"long"` + + // The name of the log stream. + LogStreamName *string `locationName:"logStreamName" min:"1" type:"string"` + + // The number of bytes stored. + // + // IMPORTANT:On June 17, 2019, this parameter was deprecated for log streams, + // and is always reported as zero. This change applies only to log streams. + // The storedBytes parameter for log groups is not affected. + // + // Deprecated: Starting on June 17, 2019, this parameter will be deprecated for log streams, and will be reported as zero. This change applies only to log streams. The storedBytes parameter for log groups is not affected. + StoredBytes *int64 `locationName:"storedBytes" deprecated:"true" type:"long"` + + // The sequence token. + UploadSequenceToken *string `locationName:"uploadSequenceToken" min:"1" type:"string"` } // String returns the string representation -func (s ExportTaskExecutionInfo) String() string { +func (s LogStream) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s ExportTaskExecutionInfo) GoString() string { +func (s LogStream) GoString() string { return s.String() } -// SetCompletionTime sets the CompletionTime field's value. -func (s *ExportTaskExecutionInfo) SetCompletionTime(v int64) *ExportTaskExecutionInfo { - s.CompletionTime = &v +// SetArn sets the Arn field's value. +func (s *LogStream) SetArn(v string) *LogStream { + s.Arn = &v return s } // SetCreationTime sets the CreationTime field's value. -func (s *ExportTaskExecutionInfo) SetCreationTime(v int64) *ExportTaskExecutionInfo { +func (s *LogStream) SetCreationTime(v int64) *LogStream { s.CreationTime = &v return s } -// Represents the status of an export task. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/ExportTaskStatus -type ExportTaskStatus struct { - _ struct{} `type:"structure"` - - // The status code of the export task. - Code *string `locationName:"code" type:"string" enum:"ExportTaskStatusCode"` +// SetFirstEventTimestamp sets the FirstEventTimestamp field's value. +func (s *LogStream) SetFirstEventTimestamp(v int64) *LogStream { + s.FirstEventTimestamp = &v + return s +} - // The status message related to the status code. - Message *string `locationName:"message" type:"string"` +// SetLastEventTimestamp sets the LastEventTimestamp field's value. +func (s *LogStream) SetLastEventTimestamp(v int64) *LogStream { + s.LastEventTimestamp = &v + return s } -// String returns the string representation -func (s ExportTaskStatus) String() string { - return awsutil.Prettify(s) +// SetLastIngestionTime sets the LastIngestionTime field's value. +func (s *LogStream) SetLastIngestionTime(v int64) *LogStream { + s.LastIngestionTime = &v + return s } -// GoString returns the string representation -func (s ExportTaskStatus) GoString() string { - return s.String() +// SetLogStreamName sets the LogStreamName field's value. +func (s *LogStream) SetLogStreamName(v string) *LogStream { + s.LogStreamName = &v + return s } -// SetCode sets the Code field's value. -func (s *ExportTaskStatus) SetCode(v string) *ExportTaskStatus { - s.Code = &v +// SetStoredBytes sets the StoredBytes field's value. +func (s *LogStream) SetStoredBytes(v int64) *LogStream { + s.StoredBytes = &v return s } -// SetMessage sets the Message field's value. -func (s *ExportTaskStatus) SetMessage(v string) *ExportTaskStatus { - s.Message = &v +// SetUploadSequenceToken sets the UploadSequenceToken field's value. +func (s *LogStream) SetUploadSequenceToken(v string) *LogStream { + s.UploadSequenceToken = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/FilterLogEventsRequest -type FilterLogEventsInput struct { - _ struct{} `type:"structure"` - - // The end of the time range, expressed as the number of milliseconds after - // Jan 1, 1970 00:00:00 UTC. Events with a time stamp later than this time are - // not returned. - EndTime *int64 `locationName:"endTime" type:"long"` - - // The filter pattern to use. If not provided, all the events are matched. - FilterPattern *string `locationName:"filterPattern" type:"string"` - - // If the value is true, the operation makes a best effort to provide responses - // that contain events from multiple log streams within the log group, interleaved - // in a single response. If the value is false, all the matched log events in - // the first log stream are searched first, then those in the next log stream, - // and so on. The default is false. - Interleaved *bool `locationName:"interleaved" type:"boolean"` - - // The maximum number of events to return. The default is 10,000 events. - Limit *int64 `locationName:"limit" min:"1" type:"integer"` - - // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` - - // Optional list of log stream names. - LogStreamNames []*string `locationName:"logStreamNames" min:"1" type:"list"` +// The query string is not valid. Details about this error are displayed in +// a QueryCompileError object. For more information, see . +// +// For more information about valid query syntax, see CloudWatch Logs Insights +// Query Syntax (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html). +type MalformedQueryException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata - // The token for the next set of events to return. (You received this token - // from a previous call.) - NextToken *string `locationName:"nextToken" min:"1" type:"string"` + Message_ *string `locationName:"message" type:"string"` - // The start of the time range, expressed as the number of milliseconds after - // Jan 1, 1970 00:00:00 UTC. Events with a time stamp before this time are not - // returned. - StartTime *int64 `locationName:"startTime" type:"long"` + // Reserved. + QueryCompileError *QueryCompileError `locationName:"queryCompileError" type:"structure"` } // String returns the string representation -func (s FilterLogEventsInput) String() string { +func (s MalformedQueryException) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s FilterLogEventsInput) GoString() string { +func (s MalformedQueryException) GoString() string { return s.String() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *FilterLogEventsInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "FilterLogEventsInput"} - if s.Limit != nil && *s.Limit < 1 { - invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) - } - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) - } - if s.LogStreamNames != nil && len(s.LogStreamNames) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogStreamNames", 1)) - } - if s.NextToken != nil && len(*s.NextToken) < 1 { - invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) - } - - if invalidParams.Len() > 0 { - return invalidParams +func newErrorMalformedQueryException(v protocol.ResponseMetadata) error { + return &MalformedQueryException{ + respMetadata: v, } - return nil } -// SetEndTime sets the EndTime field's value. -func (s *FilterLogEventsInput) SetEndTime(v int64) *FilterLogEventsInput { - s.EndTime = &v - return s +// Code returns the exception type name. +func (s MalformedQueryException) Code() string { + return "MalformedQueryException" } -// SetFilterPattern sets the FilterPattern field's value. -func (s *FilterLogEventsInput) SetFilterPattern(v string) *FilterLogEventsInput { - s.FilterPattern = &v - return s +// Message returns the exception's message. +func (s MalformedQueryException) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" } -// SetInterleaved sets the Interleaved field's value. -func (s *FilterLogEventsInput) SetInterleaved(v bool) *FilterLogEventsInput { - s.Interleaved = &v - return s +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s MalformedQueryException) OrigErr() error { + return nil } -// SetLimit sets the Limit field's value. -func (s *FilterLogEventsInput) SetLimit(v int64) *FilterLogEventsInput { - s.Limit = &v - return s +func (s MalformedQueryException) Error() string { + return fmt.Sprintf("%s: %s\n%s", s.Code(), s.Message(), s.String()) } -// SetLogGroupName sets the LogGroupName field's value. -func (s *FilterLogEventsInput) SetLogGroupName(v string) *FilterLogEventsInput { - s.LogGroupName = &v - return s +// Status code returns the HTTP status code for the request's response error. +func (s MalformedQueryException) StatusCode() int { + return s.respMetadata.StatusCode } -// SetLogStreamNames sets the LogStreamNames field's value. -func (s *FilterLogEventsInput) SetLogStreamNames(v []*string) *FilterLogEventsInput { - s.LogStreamNames = v - return s +// RequestID returns the service's response RequestID for request. +func (s MalformedQueryException) RequestID() string { + return s.respMetadata.RequestID } -// SetNextToken sets the NextToken field's value. -func (s *FilterLogEventsInput) SetNextToken(v string) *FilterLogEventsInput { - s.NextToken = &v - return s -} +// Metric filters express how CloudWatch Logs would extract metric observations +// from ingested log events and transform them into metric data in a CloudWatch +// metric. +type MetricFilter struct { + _ struct{} `type:"structure"` -// SetStartTime sets the StartTime field's value. -func (s *FilterLogEventsInput) SetStartTime(v int64) *FilterLogEventsInput { - s.StartTime = &v - return s -} + // The creation time of the metric filter, expressed as the number of milliseconds + // after Jan 1, 1970 00:00:00 UTC. + CreationTime *int64 `locationName:"creationTime" type:"long"` -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/FilterLogEventsResponse -type FilterLogEventsOutput struct { - _ struct{} `type:"structure"` + // The name of the metric filter. + FilterName *string `locationName:"filterName" min:"1" type:"string"` - // The matched events. - Events []*FilteredLogEvent `locationName:"events" type:"list"` + // A symbolic description of how CloudWatch Logs should interpret the data in + // each log event. For example, a log event may contain timestamps, IP addresses, + // strings, and so on. You use the filter pattern to specify what to look for + // in the log event message. + FilterPattern *string `locationName:"filterPattern" type:"string"` - // The token to use when requesting the next set of items. The token expires - // after 24 hours. - NextToken *string `locationName:"nextToken" min:"1" type:"string"` + // The name of the log group. + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` - // Indicates which log streams have been searched and whether each has been - // searched completely. - SearchedLogStreams []*SearchedLogStream `locationName:"searchedLogStreams" type:"list"` + // The metric transformations. + MetricTransformations []*MetricTransformation `locationName:"metricTransformations" min:"1" type:"list"` } // String returns the string representation -func (s FilterLogEventsOutput) String() string { +func (s MetricFilter) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s FilterLogEventsOutput) GoString() string { +func (s MetricFilter) GoString() string { return s.String() } -// SetEvents sets the Events field's value. -func (s *FilterLogEventsOutput) SetEvents(v []*FilteredLogEvent) *FilterLogEventsOutput { - s.Events = v +// SetCreationTime sets the CreationTime field's value. +func (s *MetricFilter) SetCreationTime(v int64) *MetricFilter { + s.CreationTime = &v return s } -// SetNextToken sets the NextToken field's value. -func (s *FilterLogEventsOutput) SetNextToken(v string) *FilterLogEventsOutput { - s.NextToken = &v +// SetFilterName sets the FilterName field's value. +func (s *MetricFilter) SetFilterName(v string) *MetricFilter { + s.FilterName = &v return s } -// SetSearchedLogStreams sets the SearchedLogStreams field's value. -func (s *FilterLogEventsOutput) SetSearchedLogStreams(v []*SearchedLogStream) *FilterLogEventsOutput { - s.SearchedLogStreams = v +// SetFilterPattern sets the FilterPattern field's value. +func (s *MetricFilter) SetFilterPattern(v string) *MetricFilter { + s.FilterPattern = &v return s } -// Represents a matched event. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/FilteredLogEvent -type FilteredLogEvent struct { - _ struct{} `type:"structure"` +// SetLogGroupName sets the LogGroupName field's value. +func (s *MetricFilter) SetLogGroupName(v string) *MetricFilter { + s.LogGroupName = &v + return s +} - // The ID of the event. - EventId *string `locationName:"eventId" type:"string"` +// SetMetricTransformations sets the MetricTransformations field's value. +func (s *MetricFilter) SetMetricTransformations(v []*MetricTransformation) *MetricFilter { + s.MetricTransformations = v + return s +} - // The time the event was ingested, expressed as the number of milliseconds - // after Jan 1, 1970 00:00:00 UTC. - IngestionTime *int64 `locationName:"ingestionTime" type:"long"` +// Represents a matched event. +type MetricFilterMatchRecord struct { + _ struct{} `type:"structure"` - // The name of the log stream this event belongs to. - LogStreamName *string `locationName:"logStreamName" min:"1" type:"string"` + // The raw event data. + EventMessage *string `locationName:"eventMessage" min:"1" type:"string"` - // The data contained in the log event. - Message *string `locationName:"message" min:"1" type:"string"` + // The event number. + EventNumber *int64 `locationName:"eventNumber" type:"long"` - // The time the event occurred, expressed as the number of milliseconds after - // Jan 1, 1970 00:00:00 UTC. - Timestamp *int64 `locationName:"timestamp" type:"long"` + // The values extracted from the event data by the filter. + ExtractedValues map[string]*string `locationName:"extractedValues" type:"map"` } // String returns the string representation -func (s FilteredLogEvent) String() string { +func (s MetricFilterMatchRecord) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s FilteredLogEvent) GoString() string { +func (s MetricFilterMatchRecord) GoString() string { return s.String() } -// SetEventId sets the EventId field's value. -func (s *FilteredLogEvent) SetEventId(v string) *FilteredLogEvent { - s.EventId = &v - return s -} - -// SetIngestionTime sets the IngestionTime field's value. -func (s *FilteredLogEvent) SetIngestionTime(v int64) *FilteredLogEvent { - s.IngestionTime = &v - return s -} - -// SetLogStreamName sets the LogStreamName field's value. -func (s *FilteredLogEvent) SetLogStreamName(v string) *FilteredLogEvent { - s.LogStreamName = &v +// SetEventMessage sets the EventMessage field's value. +func (s *MetricFilterMatchRecord) SetEventMessage(v string) *MetricFilterMatchRecord { + s.EventMessage = &v return s } -// SetMessage sets the Message field's value. -func (s *FilteredLogEvent) SetMessage(v string) *FilteredLogEvent { - s.Message = &v +// SetEventNumber sets the EventNumber field's value. +func (s *MetricFilterMatchRecord) SetEventNumber(v int64) *MetricFilterMatchRecord { + s.EventNumber = &v return s } -// SetTimestamp sets the Timestamp field's value. -func (s *FilteredLogEvent) SetTimestamp(v int64) *FilteredLogEvent { - s.Timestamp = &v +// SetExtractedValues sets the ExtractedValues field's value. +func (s *MetricFilterMatchRecord) SetExtractedValues(v map[string]*string) *MetricFilterMatchRecord { + s.ExtractedValues = v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/GetLogEventsRequest -type GetLogEventsInput struct { +// Indicates how to transform ingested log events to metric data in a CloudWatch +// metric. +type MetricTransformation struct { _ struct{} `type:"structure"` - // The end of the time range, expressed as the number of milliseconds after - // Jan 1, 1970 00:00:00 UTC. Events with a time stamp later than this time are - // not included. - EndTime *int64 `locationName:"endTime" type:"long"` - - // The maximum number of log events returned. If you don't specify a value, - // the maximum is as many log events as can fit in a response size of 1 MB, - // up to 10,000 log events. - Limit *int64 `locationName:"limit" min:"1" type:"integer"` + // (Optional) The value to emit when a filter pattern does not match a log event. + // This value can be null. + DefaultValue *float64 `locationName:"defaultValue" type:"double"` - // The name of the log group. + // The name of the CloudWatch metric. // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + // MetricName is a required field + MetricName *string `locationName:"metricName" type:"string" required:"true"` - // The name of the log stream. + // The namespace of the CloudWatch metric. // - // LogStreamName is a required field - LogStreamName *string `locationName:"logStreamName" min:"1" type:"string" required:"true"` - - // The token for the next set of items to return. (You received this token from - // a previous call.) - NextToken *string `locationName:"nextToken" min:"1" type:"string"` - - // If the value is true, the earliest log events are returned first. If the - // value is false, the latest log events are returned first. The default value - // is false. - StartFromHead *bool `locationName:"startFromHead" type:"boolean"` + // MetricNamespace is a required field + MetricNamespace *string `locationName:"metricNamespace" type:"string" required:"true"` - // The start of the time range, expressed as the number of milliseconds after - // Jan 1, 1970 00:00:00 UTC. Events with a time stamp earlier than this time - // are not included. - StartTime *int64 `locationName:"startTime" type:"long"` + // The value to publish to the CloudWatch metric when a filter pattern matches + // a log event. + // + // MetricValue is a required field + MetricValue *string `locationName:"metricValue" type:"string" required:"true"` } // String returns the string representation -func (s GetLogEventsInput) String() string { +func (s MetricTransformation) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s GetLogEventsInput) GoString() string { +func (s MetricTransformation) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *GetLogEventsInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "GetLogEventsInput"} - if s.Limit != nil && *s.Limit < 1 { - invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) - } - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) - } - if s.LogStreamName == nil { - invalidParams.Add(request.NewErrParamRequired("LogStreamName")) +func (s *MetricTransformation) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "MetricTransformation"} + if s.MetricName == nil { + invalidParams.Add(request.NewErrParamRequired("MetricName")) } - if s.LogStreamName != nil && len(*s.LogStreamName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogStreamName", 1)) + if s.MetricNamespace == nil { + invalidParams.Add(request.NewErrParamRequired("MetricNamespace")) } - if s.NextToken != nil && len(*s.NextToken) < 1 { - invalidParams.Add(request.NewErrParamMinLen("NextToken", 1)) + if s.MetricValue == nil { + invalidParams.Add(request.NewErrParamRequired("MetricValue")) } if invalidParams.Len() > 0 { @@ -5713,131 +7569,180 @@ func (s *GetLogEventsInput) Validate() error { return nil } -// SetEndTime sets the EndTime field's value. -func (s *GetLogEventsInput) SetEndTime(v int64) *GetLogEventsInput { - s.EndTime = &v +// SetDefaultValue sets the DefaultValue field's value. +func (s *MetricTransformation) SetDefaultValue(v float64) *MetricTransformation { + s.DefaultValue = &v return s } -// SetLimit sets the Limit field's value. -func (s *GetLogEventsInput) SetLimit(v int64) *GetLogEventsInput { - s.Limit = &v +// SetMetricName sets the MetricName field's value. +func (s *MetricTransformation) SetMetricName(v string) *MetricTransformation { + s.MetricName = &v return s } -// SetLogGroupName sets the LogGroupName field's value. -func (s *GetLogEventsInput) SetLogGroupName(v string) *GetLogEventsInput { - s.LogGroupName = &v +// SetMetricNamespace sets the MetricNamespace field's value. +func (s *MetricTransformation) SetMetricNamespace(v string) *MetricTransformation { + s.MetricNamespace = &v return s } -// SetLogStreamName sets the LogStreamName field's value. -func (s *GetLogEventsInput) SetLogStreamName(v string) *GetLogEventsInput { - s.LogStreamName = &v +// SetMetricValue sets the MetricValue field's value. +func (s *MetricTransformation) SetMetricValue(v string) *MetricTransformation { + s.MetricValue = &v return s } -// SetNextToken sets the NextToken field's value. -func (s *GetLogEventsInput) SetNextToken(v string) *GetLogEventsInput { - s.NextToken = &v - return s +// Multiple requests to update the same resource were in conflict. +type OperationAbortedException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata + + Message_ *string `locationName:"message" type:"string"` } -// SetStartFromHead sets the StartFromHead field's value. -func (s *GetLogEventsInput) SetStartFromHead(v bool) *GetLogEventsInput { - s.StartFromHead = &v - return s +// String returns the string representation +func (s OperationAbortedException) String() string { + return awsutil.Prettify(s) } -// SetStartTime sets the StartTime field's value. -func (s *GetLogEventsInput) SetStartTime(v int64) *GetLogEventsInput { - s.StartTime = &v - return s +// GoString returns the string representation +func (s OperationAbortedException) GoString() string { + return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/GetLogEventsResponse -type GetLogEventsOutput struct { +func newErrorOperationAbortedException(v protocol.ResponseMetadata) error { + return &OperationAbortedException{ + respMetadata: v, + } +} + +// Code returns the exception type name. +func (s OperationAbortedException) Code() string { + return "OperationAbortedException" +} + +// Message returns the exception's message. +func (s OperationAbortedException) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" +} + +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s OperationAbortedException) OrigErr() error { + return nil +} + +func (s OperationAbortedException) Error() string { + return fmt.Sprintf("%s: %s", s.Code(), s.Message()) +} + +// Status code returns the HTTP status code for the request's response error. +func (s OperationAbortedException) StatusCode() int { + return s.respMetadata.StatusCode +} + +// RequestID returns the service's response RequestID for request. +func (s OperationAbortedException) RequestID() string { + return s.respMetadata.RequestID +} + +// Represents a log event. +type OutputLogEvent struct { _ struct{} `type:"structure"` - // The events. - Events []*OutputLogEvent `locationName:"events" type:"list"` + // The time the event was ingested, expressed as the number of milliseconds + // after Jan 1, 1970 00:00:00 UTC. + IngestionTime *int64 `locationName:"ingestionTime" type:"long"` - // The token for the next set of items in the backward direction. The token - // expires after 24 hours. - NextBackwardToken *string `locationName:"nextBackwardToken" min:"1" type:"string"` + // The data contained in the log event. + Message *string `locationName:"message" min:"1" type:"string"` - // The token for the next set of items in the forward direction. The token expires - // after 24 hours. - NextForwardToken *string `locationName:"nextForwardToken" min:"1" type:"string"` + // The time the event occurred, expressed as the number of milliseconds after + // Jan 1, 1970 00:00:00 UTC. + Timestamp *int64 `locationName:"timestamp" type:"long"` } // String returns the string representation -func (s GetLogEventsOutput) String() string { +func (s OutputLogEvent) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s GetLogEventsOutput) GoString() string { +func (s OutputLogEvent) GoString() string { return s.String() } -// SetEvents sets the Events field's value. -func (s *GetLogEventsOutput) SetEvents(v []*OutputLogEvent) *GetLogEventsOutput { - s.Events = v +// SetIngestionTime sets the IngestionTime field's value. +func (s *OutputLogEvent) SetIngestionTime(v int64) *OutputLogEvent { + s.IngestionTime = &v return s } -// SetNextBackwardToken sets the NextBackwardToken field's value. -func (s *GetLogEventsOutput) SetNextBackwardToken(v string) *GetLogEventsOutput { - s.NextBackwardToken = &v +// SetMessage sets the Message field's value. +func (s *OutputLogEvent) SetMessage(v string) *OutputLogEvent { + s.Message = &v return s } -// SetNextForwardToken sets the NextForwardToken field's value. -func (s *GetLogEventsOutput) SetNextForwardToken(v string) *GetLogEventsOutput { - s.NextForwardToken = &v +// SetTimestamp sets the Timestamp field's value. +func (s *OutputLogEvent) SetTimestamp(v int64) *OutputLogEvent { + s.Timestamp = &v return s } -// Represents a log event, which is a record of activity that was recorded by -// the application or resource being monitored. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/InputLogEvent -type InputLogEvent struct { +type PutDestinationInput struct { _ struct{} `type:"structure"` - // The raw event message. + // A name for the destination. // - // Message is a required field - Message *string `locationName:"message" min:"1" type:"string" required:"true"` + // DestinationName is a required field + DestinationName *string `locationName:"destinationName" min:"1" type:"string" required:"true"` - // The time the event occurred, expressed as the number of milliseconds fter - // Jan 1, 1970 00:00:00 UTC. + // The ARN of an IAM role that grants CloudWatch Logs permissions to call the + // Amazon Kinesis PutRecord operation on the destination stream. // - // Timestamp is a required field - Timestamp *int64 `locationName:"timestamp" type:"long" required:"true"` + // RoleArn is a required field + RoleArn *string `locationName:"roleArn" min:"1" type:"string" required:"true"` + + // The ARN of an Amazon Kinesis stream to which to deliver matching log events. + // + // TargetArn is a required field + TargetArn *string `locationName:"targetArn" min:"1" type:"string" required:"true"` } // String returns the string representation -func (s InputLogEvent) String() string { +func (s PutDestinationInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s InputLogEvent) GoString() string { +func (s PutDestinationInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *InputLogEvent) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "InputLogEvent"} - if s.Message == nil { - invalidParams.Add(request.NewErrParamRequired("Message")) +func (s *PutDestinationInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "PutDestinationInput"} + if s.DestinationName == nil { + invalidParams.Add(request.NewErrParamRequired("DestinationName")) } - if s.Message != nil && len(*s.Message) < 1 { - invalidParams.Add(request.NewErrParamMinLen("Message", 1)) + if s.DestinationName != nil && len(*s.DestinationName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("DestinationName", 1)) } - if s.Timestamp == nil { - invalidParams.Add(request.NewErrParamRequired("Timestamp")) + if s.RoleArn == nil { + invalidParams.Add(request.NewErrParamRequired("RoleArn")) + } + if s.RoleArn != nil && len(*s.RoleArn) < 1 { + invalidParams.Add(request.NewErrParamMinLen("RoleArn", 1)) + } + if s.TargetArn == nil { + invalidParams.Add(request.NewErrParamRequired("TargetArn")) + } + if s.TargetArn != nil && len(*s.TargetArn) < 1 { + invalidParams.Add(request.NewErrParamMinLen("TargetArn", 1)) } if invalidParams.Len() > 0 { @@ -5846,46 +7751,86 @@ func (s *InputLogEvent) Validate() error { return nil } -// SetMessage sets the Message field's value. -func (s *InputLogEvent) SetMessage(v string) *InputLogEvent { - s.Message = &v +// SetDestinationName sets the DestinationName field's value. +func (s *PutDestinationInput) SetDestinationName(v string) *PutDestinationInput { + s.DestinationName = &v return s } -// SetTimestamp sets the Timestamp field's value. -func (s *InputLogEvent) SetTimestamp(v int64) *InputLogEvent { - s.Timestamp = &v +// SetRoleArn sets the RoleArn field's value. +func (s *PutDestinationInput) SetRoleArn(v string) *PutDestinationInput { + s.RoleArn = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/ListTagsLogGroupRequest -type ListTagsLogGroupInput struct { +// SetTargetArn sets the TargetArn field's value. +func (s *PutDestinationInput) SetTargetArn(v string) *PutDestinationInput { + s.TargetArn = &v + return s +} + +type PutDestinationOutput struct { _ struct{} `type:"structure"` - // The name of the log group. + // The destination. + Destination *Destination `locationName:"destination" type:"structure"` +} + +// String returns the string representation +func (s PutDestinationOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s PutDestinationOutput) GoString() string { + return s.String() +} + +// SetDestination sets the Destination field's value. +func (s *PutDestinationOutput) SetDestination(v *Destination) *PutDestinationOutput { + s.Destination = v + return s +} + +type PutDestinationPolicyInput struct { + _ struct{} `type:"structure"` + + // An IAM policy document that authorizes cross-account users to deliver their + // log events to the associated destination. // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + // AccessPolicy is a required field + AccessPolicy *string `locationName:"accessPolicy" min:"1" type:"string" required:"true"` + + // A name for an existing destination. + // + // DestinationName is a required field + DestinationName *string `locationName:"destinationName" min:"1" type:"string" required:"true"` } // String returns the string representation -func (s ListTagsLogGroupInput) String() string { +func (s PutDestinationPolicyInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s ListTagsLogGroupInput) GoString() string { +func (s PutDestinationPolicyInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *ListTagsLogGroupInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "ListTagsLogGroupInput"} - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) +func (s *PutDestinationPolicyInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "PutDestinationPolicyInput"} + if s.AccessPolicy == nil { + invalidParams.Add(request.NewErrParamRequired("AccessPolicy")) } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + if s.AccessPolicy != nil && len(*s.AccessPolicy) < 1 { + invalidParams.Add(request.NewErrParamMinLen("AccessPolicy", 1)) + } + if s.DestinationName == nil { + invalidParams.Add(request.NewErrParamRequired("DestinationName")) + } + if s.DestinationName != nil && len(*s.DestinationName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("DestinationName", 1)) } if invalidParams.Len() > 0 { @@ -5894,372 +7839,393 @@ func (s *ListTagsLogGroupInput) Validate() error { return nil } -// SetLogGroupName sets the LogGroupName field's value. -func (s *ListTagsLogGroupInput) SetLogGroupName(v string) *ListTagsLogGroupInput { - s.LogGroupName = &v +// SetAccessPolicy sets the AccessPolicy field's value. +func (s *PutDestinationPolicyInput) SetAccessPolicy(v string) *PutDestinationPolicyInput { + s.AccessPolicy = &v + return s +} + +// SetDestinationName sets the DestinationName field's value. +func (s *PutDestinationPolicyInput) SetDestinationName(v string) *PutDestinationPolicyInput { + s.DestinationName = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/ListTagsLogGroupResponse -type ListTagsLogGroupOutput struct { +type PutDestinationPolicyOutput struct { _ struct{} `type:"structure"` - - // The tags for the log group. - Tags map[string]*string `locationName:"tags" min:"1" type:"map"` } // String returns the string representation -func (s ListTagsLogGroupOutput) String() string { +func (s PutDestinationPolicyOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s ListTagsLogGroupOutput) GoString() string { +func (s PutDestinationPolicyOutput) GoString() string { return s.String() } -// SetTags sets the Tags field's value. -func (s *ListTagsLogGroupOutput) SetTags(v map[string]*string) *ListTagsLogGroupOutput { - s.Tags = v - return s -} - -// Represents a log group. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/LogGroup -type LogGroup struct { +type PutLogEventsInput struct { _ struct{} `type:"structure"` - // The Amazon Resource Name (ARN) of the log group. - Arn *string `locationName:"arn" type:"string"` - - // The creation time of the log group, expressed as the number of milliseconds - // after Jan 1, 1970 00:00:00 UTC. - CreationTime *int64 `locationName:"creationTime" type:"long"` - - // The Amazon Resource Name (ARN) of the CMK to use when encrypting log data. - KmsKeyId *string `locationName:"kmsKeyId" type:"string"` + // The log events. + // + // LogEvents is a required field + LogEvents []*InputLogEvent `locationName:"logEvents" min:"1" type:"list" required:"true"` // The name of the log group. - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` - - // The number of metric filters. - MetricFilterCount *int64 `locationName:"metricFilterCount" type:"integer"` + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` - // The number of days to retain the log events in the specified log group. Possible - // values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, - // 1827, and 3653. - RetentionInDays *int64 `locationName:"retentionInDays" type:"integer"` + // The name of the log stream. + // + // LogStreamName is a required field + LogStreamName *string `locationName:"logStreamName" min:"1" type:"string" required:"true"` - // The number of bytes stored. - StoredBytes *int64 `locationName:"storedBytes" type:"long"` + // The sequence token obtained from the response of the previous PutLogEvents + // call. An upload in a newly created log stream does not require a sequence + // token. You can also get the sequence token using DescribeLogStreams. If you + // call PutLogEvents twice within a narrow time period using the same value + // for sequenceToken, both calls may be successful, or one may be rejected. + SequenceToken *string `locationName:"sequenceToken" min:"1" type:"string"` } // String returns the string representation -func (s LogGroup) String() string { +func (s PutLogEventsInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s LogGroup) GoString() string { +func (s PutLogEventsInput) GoString() string { return s.String() } -// SetArn sets the Arn field's value. -func (s *LogGroup) SetArn(v string) *LogGroup { - s.Arn = &v - return s -} +// Validate inspects the fields of the type to determine if they are valid. +func (s *PutLogEventsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "PutLogEventsInput"} + if s.LogEvents == nil { + invalidParams.Add(request.NewErrParamRequired("LogEvents")) + } + if s.LogEvents != nil && len(s.LogEvents) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogEvents", 1)) + } + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + if s.LogStreamName == nil { + invalidParams.Add(request.NewErrParamRequired("LogStreamName")) + } + if s.LogStreamName != nil && len(*s.LogStreamName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogStreamName", 1)) + } + if s.SequenceToken != nil && len(*s.SequenceToken) < 1 { + invalidParams.Add(request.NewErrParamMinLen("SequenceToken", 1)) + } + if s.LogEvents != nil { + for i, v := range s.LogEvents { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "LogEvents", i), err.(request.ErrInvalidParams)) + } + } + } -// SetCreationTime sets the CreationTime field's value. -func (s *LogGroup) SetCreationTime(v int64) *LogGroup { - s.CreationTime = &v - return s + if invalidParams.Len() > 0 { + return invalidParams + } + return nil } -// SetKmsKeyId sets the KmsKeyId field's value. -func (s *LogGroup) SetKmsKeyId(v string) *LogGroup { - s.KmsKeyId = &v +// SetLogEvents sets the LogEvents field's value. +func (s *PutLogEventsInput) SetLogEvents(v []*InputLogEvent) *PutLogEventsInput { + s.LogEvents = v return s } // SetLogGroupName sets the LogGroupName field's value. -func (s *LogGroup) SetLogGroupName(v string) *LogGroup { +func (s *PutLogEventsInput) SetLogGroupName(v string) *PutLogEventsInput { s.LogGroupName = &v return s } -// SetMetricFilterCount sets the MetricFilterCount field's value. -func (s *LogGroup) SetMetricFilterCount(v int64) *LogGroup { - s.MetricFilterCount = &v - return s -} - -// SetRetentionInDays sets the RetentionInDays field's value. -func (s *LogGroup) SetRetentionInDays(v int64) *LogGroup { - s.RetentionInDays = &v +// SetLogStreamName sets the LogStreamName field's value. +func (s *PutLogEventsInput) SetLogStreamName(v string) *PutLogEventsInput { + s.LogStreamName = &v return s } -// SetStoredBytes sets the StoredBytes field's value. -func (s *LogGroup) SetStoredBytes(v int64) *LogGroup { - s.StoredBytes = &v +// SetSequenceToken sets the SequenceToken field's value. +func (s *PutLogEventsInput) SetSequenceToken(v string) *PutLogEventsInput { + s.SequenceToken = &v return s } -// Represents a log stream, which is a sequence of log events from a single -// emitter of logs. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/LogStream -type LogStream struct { +type PutLogEventsOutput struct { _ struct{} `type:"structure"` - // The Amazon Resource Name (ARN) of the log stream. - Arn *string `locationName:"arn" type:"string"` - - // The creation time of the stream, expressed as the number of milliseconds - // after Jan 1, 1970 00:00:00 UTC. - CreationTime *int64 `locationName:"creationTime" type:"long"` - - // The time of the first event, expressed as the number of milliseconds after - // Jan 1, 1970 00:00:00 UTC. - FirstEventTimestamp *int64 `locationName:"firstEventTimestamp" type:"long"` - - // the time of the most recent log event in the log stream in CloudWatch Logs. - // This number is expressed as the number of milliseconds after Jan 1, 1970 - // 00:00:00 UTC. lastEventTime updates on an eventual consistency basis. It - // typically updates in less than an hour from ingestion, but may take longer - // in some rare situations. - LastEventTimestamp *int64 `locationName:"lastEventTimestamp" type:"long"` - - // The ingestion time, expressed as the number of milliseconds after Jan 1, - // 1970 00:00:00 UTC. - LastIngestionTime *int64 `locationName:"lastIngestionTime" type:"long"` - - // The name of the log stream. - LogStreamName *string `locationName:"logStreamName" min:"1" type:"string"` - - // The number of bytes stored. - StoredBytes *int64 `locationName:"storedBytes" type:"long"` + // The next sequence token. + NextSequenceToken *string `locationName:"nextSequenceToken" min:"1" type:"string"` - // The sequence token. - UploadSequenceToken *string `locationName:"uploadSequenceToken" min:"1" type:"string"` + // The rejected events. + RejectedLogEventsInfo *RejectedLogEventsInfo `locationName:"rejectedLogEventsInfo" type:"structure"` } // String returns the string representation -func (s LogStream) String() string { +func (s PutLogEventsOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s LogStream) GoString() string { +func (s PutLogEventsOutput) GoString() string { return s.String() } -// SetArn sets the Arn field's value. -func (s *LogStream) SetArn(v string) *LogStream { - s.Arn = &v - return s -} - -// SetCreationTime sets the CreationTime field's value. -func (s *LogStream) SetCreationTime(v int64) *LogStream { - s.CreationTime = &v - return s -} - -// SetFirstEventTimestamp sets the FirstEventTimestamp field's value. -func (s *LogStream) SetFirstEventTimestamp(v int64) *LogStream { - s.FirstEventTimestamp = &v - return s -} - -// SetLastEventTimestamp sets the LastEventTimestamp field's value. -func (s *LogStream) SetLastEventTimestamp(v int64) *LogStream { - s.LastEventTimestamp = &v - return s -} - -// SetLastIngestionTime sets the LastIngestionTime field's value. -func (s *LogStream) SetLastIngestionTime(v int64) *LogStream { - s.LastIngestionTime = &v - return s -} - -// SetLogStreamName sets the LogStreamName field's value. -func (s *LogStream) SetLogStreamName(v string) *LogStream { - s.LogStreamName = &v - return s -} - -// SetStoredBytes sets the StoredBytes field's value. -func (s *LogStream) SetStoredBytes(v int64) *LogStream { - s.StoredBytes = &v +// SetNextSequenceToken sets the NextSequenceToken field's value. +func (s *PutLogEventsOutput) SetNextSequenceToken(v string) *PutLogEventsOutput { + s.NextSequenceToken = &v return s } -// SetUploadSequenceToken sets the UploadSequenceToken field's value. -func (s *LogStream) SetUploadSequenceToken(v string) *LogStream { - s.UploadSequenceToken = &v +// SetRejectedLogEventsInfo sets the RejectedLogEventsInfo field's value. +func (s *PutLogEventsOutput) SetRejectedLogEventsInfo(v *RejectedLogEventsInfo) *PutLogEventsOutput { + s.RejectedLogEventsInfo = v return s } -// Metric filters express how CloudWatch Logs would extract metric observations -// from ingested log events and transform them into metric data in a CloudWatch -// metric. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/MetricFilter -type MetricFilter struct { +type PutMetricFilterInput struct { _ struct{} `type:"structure"` - // The creation time of the metric filter, expressed as the number of milliseconds - // after Jan 1, 1970 00:00:00 UTC. - CreationTime *int64 `locationName:"creationTime" type:"long"` - - // The name of the metric filter. - FilterName *string `locationName:"filterName" min:"1" type:"string"` + // A name for the metric filter. + // + // FilterName is a required field + FilterName *string `locationName:"filterName" min:"1" type:"string" required:"true"` - // A symbolic description of how CloudWatch Logs should interpret the data in - // each log event. For example, a log event may contain time stamps, IP addresses, - // strings, and so on. You use the filter pattern to specify what to look for - // in the log event message. - FilterPattern *string `locationName:"filterPattern" type:"string"` + // A filter pattern for extracting metric data out of ingested log events. + // + // FilterPattern is a required field + FilterPattern *string `locationName:"filterPattern" type:"string" required:"true"` // The name of the log group. - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` + // + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` - // The metric transformations. - MetricTransformations []*MetricTransformation `locationName:"metricTransformations" min:"1" type:"list"` + // A collection of information that defines how metric data gets emitted. + // + // MetricTransformations is a required field + MetricTransformations []*MetricTransformation `locationName:"metricTransformations" min:"1" type:"list" required:"true"` } // String returns the string representation -func (s MetricFilter) String() string { +func (s PutMetricFilterInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s MetricFilter) GoString() string { +func (s PutMetricFilterInput) GoString() string { return s.String() } -// SetCreationTime sets the CreationTime field's value. -func (s *MetricFilter) SetCreationTime(v int64) *MetricFilter { - s.CreationTime = &v - return s +// Validate inspects the fields of the type to determine if they are valid. +func (s *PutMetricFilterInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "PutMetricFilterInput"} + if s.FilterName == nil { + invalidParams.Add(request.NewErrParamRequired("FilterName")) + } + if s.FilterName != nil && len(*s.FilterName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("FilterName", 1)) + } + if s.FilterPattern == nil { + invalidParams.Add(request.NewErrParamRequired("FilterPattern")) + } + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + if s.MetricTransformations == nil { + invalidParams.Add(request.NewErrParamRequired("MetricTransformations")) + } + if s.MetricTransformations != nil && len(s.MetricTransformations) < 1 { + invalidParams.Add(request.NewErrParamMinLen("MetricTransformations", 1)) + } + if s.MetricTransformations != nil { + for i, v := range s.MetricTransformations { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "MetricTransformations", i), err.(request.ErrInvalidParams)) + } + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil } // SetFilterName sets the FilterName field's value. -func (s *MetricFilter) SetFilterName(v string) *MetricFilter { +func (s *PutMetricFilterInput) SetFilterName(v string) *PutMetricFilterInput { s.FilterName = &v return s } // SetFilterPattern sets the FilterPattern field's value. -func (s *MetricFilter) SetFilterPattern(v string) *MetricFilter { +func (s *PutMetricFilterInput) SetFilterPattern(v string) *PutMetricFilterInput { s.FilterPattern = &v return s } // SetLogGroupName sets the LogGroupName field's value. -func (s *MetricFilter) SetLogGroupName(v string) *MetricFilter { +func (s *PutMetricFilterInput) SetLogGroupName(v string) *PutMetricFilterInput { s.LogGroupName = &v return s } // SetMetricTransformations sets the MetricTransformations field's value. -func (s *MetricFilter) SetMetricTransformations(v []*MetricTransformation) *MetricFilter { +func (s *PutMetricFilterInput) SetMetricTransformations(v []*MetricTransformation) *PutMetricFilterInput { s.MetricTransformations = v return s } -// Represents a matched event. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/MetricFilterMatchRecord -type MetricFilterMatchRecord struct { +type PutMetricFilterOutput struct { _ struct{} `type:"structure"` +} - // The raw event data. - EventMessage *string `locationName:"eventMessage" min:"1" type:"string"` +// String returns the string representation +func (s PutMetricFilterOutput) String() string { + return awsutil.Prettify(s) +} - // The event number. - EventNumber *int64 `locationName:"eventNumber" type:"long"` +// GoString returns the string representation +func (s PutMetricFilterOutput) GoString() string { + return s.String() +} - // The values extracted from the event data by the filter. - ExtractedValues map[string]*string `locationName:"extractedValues" type:"map"` +type PutResourcePolicyInput struct { + _ struct{} `type:"structure"` + + // Details of the new policy, including the identity of the principal that is + // enabled to put logs to this account. This is formatted as a JSON string. + // This parameter is required. + // + // The following example creates a resource policy enabling the Route 53 service + // to put DNS query logs in to the specified log group. Replace "logArn" with + // the ARN of your CloudWatch Logs resource, such as a log group or log stream. + // + // { "Version": "2012-10-17", "Statement": [ { "Sid": "Route53LogsToCloudWatchLogs", + // "Effect": "Allow", "Principal": { "Service": [ "route53.amazonaws.com" ] + // }, "Action":"logs:PutLogEvents", "Resource": "logArn" } ] } + PolicyDocument *string `locationName:"policyDocument" min:"1" type:"string"` + + // Name of the new policy. This parameter is required. + PolicyName *string `locationName:"policyName" type:"string"` } // String returns the string representation -func (s MetricFilterMatchRecord) String() string { +func (s PutResourcePolicyInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s MetricFilterMatchRecord) GoString() string { +func (s PutResourcePolicyInput) GoString() string { return s.String() } -// SetEventMessage sets the EventMessage field's value. -func (s *MetricFilterMatchRecord) SetEventMessage(v string) *MetricFilterMatchRecord { - s.EventMessage = &v - return s +// Validate inspects the fields of the type to determine if they are valid. +func (s *PutResourcePolicyInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "PutResourcePolicyInput"} + if s.PolicyDocument != nil && len(*s.PolicyDocument) < 1 { + invalidParams.Add(request.NewErrParamMinLen("PolicyDocument", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil } -// SetEventNumber sets the EventNumber field's value. -func (s *MetricFilterMatchRecord) SetEventNumber(v int64) *MetricFilterMatchRecord { - s.EventNumber = &v +// SetPolicyDocument sets the PolicyDocument field's value. +func (s *PutResourcePolicyInput) SetPolicyDocument(v string) *PutResourcePolicyInput { + s.PolicyDocument = &v return s } -// SetExtractedValues sets the ExtractedValues field's value. -func (s *MetricFilterMatchRecord) SetExtractedValues(v map[string]*string) *MetricFilterMatchRecord { - s.ExtractedValues = v +// SetPolicyName sets the PolicyName field's value. +func (s *PutResourcePolicyInput) SetPolicyName(v string) *PutResourcePolicyInput { + s.PolicyName = &v return s } -// Indicates how to transform ingested log events in to metric data in a CloudWatch -// metric. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/MetricTransformation -type MetricTransformation struct { +type PutResourcePolicyOutput struct { _ struct{} `type:"structure"` - // (Optional) The value to emit when a filter pattern does not match a log event. - // This value can be null. - DefaultValue *float64 `locationName:"defaultValue" type:"double"` + // The new policy. + ResourcePolicy *ResourcePolicy `locationName:"resourcePolicy" type:"structure"` +} - // The name of the CloudWatch metric. - // - // MetricName is a required field - MetricName *string `locationName:"metricName" type:"string" required:"true"` +// String returns the string representation +func (s PutResourcePolicyOutput) String() string { + return awsutil.Prettify(s) +} - // The namespace of the CloudWatch metric. +// GoString returns the string representation +func (s PutResourcePolicyOutput) GoString() string { + return s.String() +} + +// SetResourcePolicy sets the ResourcePolicy field's value. +func (s *PutResourcePolicyOutput) SetResourcePolicy(v *ResourcePolicy) *PutResourcePolicyOutput { + s.ResourcePolicy = v + return s +} + +type PutRetentionPolicyInput struct { + _ struct{} `type:"structure"` + + // The name of the log group. // - // MetricNamespace is a required field - MetricNamespace *string `locationName:"metricNamespace" type:"string" required:"true"` + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` - // The value to publish to the CloudWatch metric when a filter pattern matches - // a log event. + // The number of days to retain the log events in the specified log group. Possible + // values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, + // 1827, and 3653. // - // MetricValue is a required field - MetricValue *string `locationName:"metricValue" type:"string" required:"true"` + // RetentionInDays is a required field + RetentionInDays *int64 `locationName:"retentionInDays" type:"integer" required:"true"` } // String returns the string representation -func (s MetricTransformation) String() string { +func (s PutRetentionPolicyInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s MetricTransformation) GoString() string { +func (s PutRetentionPolicyInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *MetricTransformation) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "MetricTransformation"} - if s.MetricName == nil { - invalidParams.Add(request.NewErrParamRequired("MetricName")) +func (s *PutRetentionPolicyInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "PutRetentionPolicyInput"} + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) } - if s.MetricNamespace == nil { - invalidParams.Add(request.NewErrParamRequired("MetricNamespace")) + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) } - if s.MetricValue == nil { - invalidParams.Add(request.NewErrParamRequired("MetricValue")) + if s.RetentionInDays == nil { + invalidParams.Add(request.NewErrParamRequired("RetentionInDays")) } if invalidParams.Len() > 0 { @@ -6268,126 +8234,120 @@ func (s *MetricTransformation) Validate() error { return nil } -// SetDefaultValue sets the DefaultValue field's value. -func (s *MetricTransformation) SetDefaultValue(v float64) *MetricTransformation { - s.DefaultValue = &v - return s -} - -// SetMetricName sets the MetricName field's value. -func (s *MetricTransformation) SetMetricName(v string) *MetricTransformation { - s.MetricName = &v - return s -} - -// SetMetricNamespace sets the MetricNamespace field's value. -func (s *MetricTransformation) SetMetricNamespace(v string) *MetricTransformation { - s.MetricNamespace = &v +// SetLogGroupName sets the LogGroupName field's value. +func (s *PutRetentionPolicyInput) SetLogGroupName(v string) *PutRetentionPolicyInput { + s.LogGroupName = &v return s } -// SetMetricValue sets the MetricValue field's value. -func (s *MetricTransformation) SetMetricValue(v string) *MetricTransformation { - s.MetricValue = &v +// SetRetentionInDays sets the RetentionInDays field's value. +func (s *PutRetentionPolicyInput) SetRetentionInDays(v int64) *PutRetentionPolicyInput { + s.RetentionInDays = &v return s } -// Represents a log event. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/OutputLogEvent -type OutputLogEvent struct { +type PutRetentionPolicyOutput struct { _ struct{} `type:"structure"` - - // The time the event was ingested, expressed as the number of milliseconds - // after Jan 1, 1970 00:00:00 UTC. - IngestionTime *int64 `locationName:"ingestionTime" type:"long"` - - // The data contained in the log event. - Message *string `locationName:"message" min:"1" type:"string"` - - // The time the event occurred, expressed as the number of milliseconds after - // Jan 1, 1970 00:00:00 UTC. - Timestamp *int64 `locationName:"timestamp" type:"long"` } // String returns the string representation -func (s OutputLogEvent) String() string { +func (s PutRetentionPolicyOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s OutputLogEvent) GoString() string { +func (s PutRetentionPolicyOutput) GoString() string { return s.String() } -// SetIngestionTime sets the IngestionTime field's value. -func (s *OutputLogEvent) SetIngestionTime(v int64) *OutputLogEvent { - s.IngestionTime = &v - return s -} - -// SetMessage sets the Message field's value. -func (s *OutputLogEvent) SetMessage(v string) *OutputLogEvent { - s.Message = &v - return s -} +type PutSubscriptionFilterInput struct { + _ struct{} `type:"structure"` -// SetTimestamp sets the Timestamp field's value. -func (s *OutputLogEvent) SetTimestamp(v int64) *OutputLogEvent { - s.Timestamp = &v - return s -} + // The ARN of the destination to deliver matching log events to. Currently, + // the supported destinations are: + // + // * An Amazon Kinesis stream belonging to the same account as the subscription + // filter, for same-account delivery. + // + // * A logical destination (specified using an ARN) belonging to a different + // account, for cross-account delivery. + // + // * An Amazon Kinesis Firehose delivery stream belonging to the same account + // as the subscription filter, for same-account delivery. + // + // * An AWS Lambda function belonging to the same account as the subscription + // filter, for same-account delivery. + // + // DestinationArn is a required field + DestinationArn *string `locationName:"destinationArn" min:"1" type:"string" required:"true"` -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestinationRequest -type PutDestinationInput struct { - _ struct{} `type:"structure"` + // The method used to distribute log data to the destination. By default log + // data is grouped by log stream, but the grouping can be set to random for + // a more even distribution. This property is only applicable when the destination + // is an Amazon Kinesis stream. + Distribution *string `locationName:"distribution" type:"string" enum:"Distribution"` - // A name for the destination. + // A name for the subscription filter. If you are updating an existing filter, + // you must specify the correct name in filterName. Otherwise, the call fails + // because you cannot associate a second filter with a log group. To find the + // name of the filter currently associated with a log group, use DescribeSubscriptionFilters. // - // DestinationName is a required field - DestinationName *string `locationName:"destinationName" min:"1" type:"string" required:"true"` + // FilterName is a required field + FilterName *string `locationName:"filterName" min:"1" type:"string" required:"true"` - // The ARN of an IAM role that grants CloudWatch Logs permissions to call the - // Amazon Kinesis PutRecord operation on the destination stream. + // A filter pattern for subscribing to a filtered stream of log events. // - // RoleArn is a required field - RoleArn *string `locationName:"roleArn" min:"1" type:"string" required:"true"` + // FilterPattern is a required field + FilterPattern *string `locationName:"filterPattern" type:"string" required:"true"` - // The ARN of an Amazon Kinesis stream to which to deliver matching log events. + // The name of the log group. // - // TargetArn is a required field - TargetArn *string `locationName:"targetArn" min:"1" type:"string" required:"true"` + // LogGroupName is a required field + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + + // The ARN of an IAM role that grants CloudWatch Logs permissions to deliver + // ingested log events to the destination stream. You don't need to provide + // the ARN when you are working with a logical destination for cross-account + // delivery. + RoleArn *string `locationName:"roleArn" min:"1" type:"string"` } // String returns the string representation -func (s PutDestinationInput) String() string { +func (s PutSubscriptionFilterInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutDestinationInput) GoString() string { +func (s PutSubscriptionFilterInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *PutDestinationInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "PutDestinationInput"} - if s.DestinationName == nil { - invalidParams.Add(request.NewErrParamRequired("DestinationName")) +func (s *PutSubscriptionFilterInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "PutSubscriptionFilterInput"} + if s.DestinationArn == nil { + invalidParams.Add(request.NewErrParamRequired("DestinationArn")) } - if s.DestinationName != nil && len(*s.DestinationName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("DestinationName", 1)) + if s.DestinationArn != nil && len(*s.DestinationArn) < 1 { + invalidParams.Add(request.NewErrParamMinLen("DestinationArn", 1)) } - if s.RoleArn == nil { - invalidParams.Add(request.NewErrParamRequired("RoleArn")) + if s.FilterName == nil { + invalidParams.Add(request.NewErrParamRequired("FilterName")) } - if s.RoleArn != nil && len(*s.RoleArn) < 1 { - invalidParams.Add(request.NewErrParamMinLen("RoleArn", 1)) + if s.FilterName != nil && len(*s.FilterName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("FilterName", 1)) } - if s.TargetArn == nil { - invalidParams.Add(request.NewErrParamRequired("TargetArn")) + if s.FilterPattern == nil { + invalidParams.Add(request.NewErrParamRequired("FilterPattern")) } - if s.TargetArn != nil && len(*s.TargetArn) < 1 { - invalidParams.Add(request.NewErrParamMinLen("TargetArn", 1)) + if s.LogGroupName == nil { + invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + } + if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { + invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) + } + if s.RoleArn != nil && len(*s.RoleArn) < 1 { + invalidParams.Add(request.NewErrParamMinLen("RoleArn", 1)) } if invalidParams.Len() > 0 { @@ -6396,614 +8356,617 @@ func (s *PutDestinationInput) Validate() error { return nil } -// SetDestinationName sets the DestinationName field's value. -func (s *PutDestinationInput) SetDestinationName(v string) *PutDestinationInput { - s.DestinationName = &v +// SetDestinationArn sets the DestinationArn field's value. +func (s *PutSubscriptionFilterInput) SetDestinationArn(v string) *PutSubscriptionFilterInput { + s.DestinationArn = &v return s } -// SetRoleArn sets the RoleArn field's value. -func (s *PutDestinationInput) SetRoleArn(v string) *PutDestinationInput { - s.RoleArn = &v +// SetDistribution sets the Distribution field's value. +func (s *PutSubscriptionFilterInput) SetDistribution(v string) *PutSubscriptionFilterInput { + s.Distribution = &v return s } -// SetTargetArn sets the TargetArn field's value. -func (s *PutDestinationInput) SetTargetArn(v string) *PutDestinationInput { - s.TargetArn = &v +// SetFilterName sets the FilterName field's value. +func (s *PutSubscriptionFilterInput) SetFilterName(v string) *PutSubscriptionFilterInput { + s.FilterName = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestinationResponse -type PutDestinationOutput struct { - _ struct{} `type:"structure"` +// SetFilterPattern sets the FilterPattern field's value. +func (s *PutSubscriptionFilterInput) SetFilterPattern(v string) *PutSubscriptionFilterInput { + s.FilterPattern = &v + return s +} - // The destination. - Destination *Destination `locationName:"destination" type:"structure"` +// SetLogGroupName sets the LogGroupName field's value. +func (s *PutSubscriptionFilterInput) SetLogGroupName(v string) *PutSubscriptionFilterInput { + s.LogGroupName = &v + return s +} + +// SetRoleArn sets the RoleArn field's value. +func (s *PutSubscriptionFilterInput) SetRoleArn(v string) *PutSubscriptionFilterInput { + s.RoleArn = &v + return s +} + +type PutSubscriptionFilterOutput struct { + _ struct{} `type:"structure"` } // String returns the string representation -func (s PutDestinationOutput) String() string { +func (s PutSubscriptionFilterOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutDestinationOutput) GoString() string { +func (s PutSubscriptionFilterOutput) GoString() string { return s.String() } -// SetDestination sets the Destination field's value. -func (s *PutDestinationOutput) SetDestination(v *Destination) *PutDestinationOutput { - s.Destination = v - return s -} - -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestinationPolicyRequest -type PutDestinationPolicyInput struct { +// Reserved. +type QueryCompileError struct { _ struct{} `type:"structure"` - // An IAM policy document that authorizes cross-account users to deliver their - // log events to the associated destination. - // - // AccessPolicy is a required field - AccessPolicy *string `locationName:"accessPolicy" min:"1" type:"string" required:"true"` + // Reserved. + Location *QueryCompileErrorLocation `locationName:"location" type:"structure"` - // A name for an existing destination. - // - // DestinationName is a required field - DestinationName *string `locationName:"destinationName" min:"1" type:"string" required:"true"` + // Reserved. + Message *string `locationName:"message" type:"string"` } // String returns the string representation -func (s PutDestinationPolicyInput) String() string { +func (s QueryCompileError) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutDestinationPolicyInput) GoString() string { +func (s QueryCompileError) GoString() string { return s.String() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *PutDestinationPolicyInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "PutDestinationPolicyInput"} - if s.AccessPolicy == nil { - invalidParams.Add(request.NewErrParamRequired("AccessPolicy")) - } - if s.AccessPolicy != nil && len(*s.AccessPolicy) < 1 { - invalidParams.Add(request.NewErrParamMinLen("AccessPolicy", 1)) - } - if s.DestinationName == nil { - invalidParams.Add(request.NewErrParamRequired("DestinationName")) - } - if s.DestinationName != nil && len(*s.DestinationName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("DestinationName", 1)) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil -} - -// SetAccessPolicy sets the AccessPolicy field's value. -func (s *PutDestinationPolicyInput) SetAccessPolicy(v string) *PutDestinationPolicyInput { - s.AccessPolicy = &v +// SetLocation sets the Location field's value. +func (s *QueryCompileError) SetLocation(v *QueryCompileErrorLocation) *QueryCompileError { + s.Location = v return s } -// SetDestinationName sets the DestinationName field's value. -func (s *PutDestinationPolicyInput) SetDestinationName(v string) *PutDestinationPolicyInput { - s.DestinationName = &v +// SetMessage sets the Message field's value. +func (s *QueryCompileError) SetMessage(v string) *QueryCompileError { + s.Message = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutDestinationPolicyOutput -type PutDestinationPolicyOutput struct { +// Reserved. +type QueryCompileErrorLocation struct { _ struct{} `type:"structure"` + + // Reserved. + EndCharOffset *int64 `locationName:"endCharOffset" type:"integer"` + + // Reserved. + StartCharOffset *int64 `locationName:"startCharOffset" type:"integer"` } // String returns the string representation -func (s PutDestinationPolicyOutput) String() string { +func (s QueryCompileErrorLocation) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutDestinationPolicyOutput) GoString() string { +func (s QueryCompileErrorLocation) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutLogEventsRequest -type PutLogEventsInput struct { +// SetEndCharOffset sets the EndCharOffset field's value. +func (s *QueryCompileErrorLocation) SetEndCharOffset(v int64) *QueryCompileErrorLocation { + s.EndCharOffset = &v + return s +} + +// SetStartCharOffset sets the StartCharOffset field's value. +func (s *QueryCompileErrorLocation) SetStartCharOffset(v int64) *QueryCompileErrorLocation { + s.StartCharOffset = &v + return s +} + +// Information about one CloudWatch Logs Insights query that matches the request +// in a DescribeQueries operation. +type QueryInfo struct { _ struct{} `type:"structure"` - // The log events. - // - // LogEvents is a required field - LogEvents []*InputLogEvent `locationName:"logEvents" min:"1" type:"list" required:"true"` + // The date and time that this query was created. + CreateTime *int64 `locationName:"createTime" type:"long"` - // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + // The name of the log group scanned by this query. + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` - // The name of the log stream. - // - // LogStreamName is a required field - LogStreamName *string `locationName:"logStreamName" min:"1" type:"string" required:"true"` + // The unique ID number of this query. + QueryId *string `locationName:"queryId" type:"string"` - // The sequence token obtained from the response of the previous PutLogEvents - // call. An upload in a newly created log stream does not require a sequence - // token. You can also get the sequence token using DescribeLogStreams. If you - // call PutLogEvents twice within a narrow time period using the same value - // for sequenceToken, both calls may be successful, or one may be rejected. - SequenceToken *string `locationName:"sequenceToken" min:"1" type:"string"` + // The query string used in this query. + QueryString *string `locationName:"queryString" type:"string"` + + // The status of this query. Possible values are Cancelled, Complete, Failed, + // Running, Scheduled, and Unknown. + Status *string `locationName:"status" type:"string" enum:"QueryStatus"` } // String returns the string representation -func (s PutLogEventsInput) String() string { +func (s QueryInfo) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutLogEventsInput) GoString() string { +func (s QueryInfo) GoString() string { return s.String() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *PutLogEventsInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "PutLogEventsInput"} - if s.LogEvents == nil { - invalidParams.Add(request.NewErrParamRequired("LogEvents")) - } - if s.LogEvents != nil && len(s.LogEvents) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogEvents", 1)) - } - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) - } - if s.LogStreamName == nil { - invalidParams.Add(request.NewErrParamRequired("LogStreamName")) - } - if s.LogStreamName != nil && len(*s.LogStreamName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogStreamName", 1)) - } - if s.SequenceToken != nil && len(*s.SequenceToken) < 1 { - invalidParams.Add(request.NewErrParamMinLen("SequenceToken", 1)) - } - if s.LogEvents != nil { - for i, v := range s.LogEvents { - if v == nil { - continue - } - if err := v.Validate(); err != nil { - invalidParams.AddNested(fmt.Sprintf("%s[%v]", "LogEvents", i), err.(request.ErrInvalidParams)) - } - } - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil +// SetCreateTime sets the CreateTime field's value. +func (s *QueryInfo) SetCreateTime(v int64) *QueryInfo { + s.CreateTime = &v + return s } -// SetLogEvents sets the LogEvents field's value. -func (s *PutLogEventsInput) SetLogEvents(v []*InputLogEvent) *PutLogEventsInput { - s.LogEvents = v +// SetLogGroupName sets the LogGroupName field's value. +func (s *QueryInfo) SetLogGroupName(v string) *QueryInfo { + s.LogGroupName = &v return s } -// SetLogGroupName sets the LogGroupName field's value. -func (s *PutLogEventsInput) SetLogGroupName(v string) *PutLogEventsInput { - s.LogGroupName = &v +// SetQueryId sets the QueryId field's value. +func (s *QueryInfo) SetQueryId(v string) *QueryInfo { + s.QueryId = &v return s } -// SetLogStreamName sets the LogStreamName field's value. -func (s *PutLogEventsInput) SetLogStreamName(v string) *PutLogEventsInput { - s.LogStreamName = &v +// SetQueryString sets the QueryString field's value. +func (s *QueryInfo) SetQueryString(v string) *QueryInfo { + s.QueryString = &v return s } -// SetSequenceToken sets the SequenceToken field's value. -func (s *PutLogEventsInput) SetSequenceToken(v string) *PutLogEventsInput { - s.SequenceToken = &v +// SetStatus sets the Status field's value. +func (s *QueryInfo) SetStatus(v string) *QueryInfo { + s.Status = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutLogEventsResponse -type PutLogEventsOutput struct { +// Contains the number of log events scanned by the query, the number of log +// events that matched the query criteria, and the total number of bytes in +// the log events that were scanned. +type QueryStatistics struct { _ struct{} `type:"structure"` - // The next sequence token. - NextSequenceToken *string `locationName:"nextSequenceToken" min:"1" type:"string"` + // The total number of bytes in the log events scanned during the query. + BytesScanned *float64 `locationName:"bytesScanned" type:"double"` - // The rejected events. - RejectedLogEventsInfo *RejectedLogEventsInfo `locationName:"rejectedLogEventsInfo" type:"structure"` + // The number of log events that matched the query string. + RecordsMatched *float64 `locationName:"recordsMatched" type:"double"` + + // The total number of log events scanned during the query. + RecordsScanned *float64 `locationName:"recordsScanned" type:"double"` } // String returns the string representation -func (s PutLogEventsOutput) String() string { +func (s QueryStatistics) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutLogEventsOutput) GoString() string { +func (s QueryStatistics) GoString() string { return s.String() } -// SetNextSequenceToken sets the NextSequenceToken field's value. -func (s *PutLogEventsOutput) SetNextSequenceToken(v string) *PutLogEventsOutput { - s.NextSequenceToken = &v +// SetBytesScanned sets the BytesScanned field's value. +func (s *QueryStatistics) SetBytesScanned(v float64) *QueryStatistics { + s.BytesScanned = &v return s } -// SetRejectedLogEventsInfo sets the RejectedLogEventsInfo field's value. -func (s *PutLogEventsOutput) SetRejectedLogEventsInfo(v *RejectedLogEventsInfo) *PutLogEventsOutput { - s.RejectedLogEventsInfo = v +// SetRecordsMatched sets the RecordsMatched field's value. +func (s *QueryStatistics) SetRecordsMatched(v float64) *QueryStatistics { + s.RecordsMatched = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutMetricFilterRequest -type PutMetricFilterInput struct { +// SetRecordsScanned sets the RecordsScanned field's value. +func (s *QueryStatistics) SetRecordsScanned(v float64) *QueryStatistics { + s.RecordsScanned = &v + return s +} + +// Represents the rejected events. +type RejectedLogEventsInfo struct { _ struct{} `type:"structure"` - // A name for the metric filter. - // - // FilterName is a required field - FilterName *string `locationName:"filterName" min:"1" type:"string" required:"true"` + // The expired log events. + ExpiredLogEventEndIndex *int64 `locationName:"expiredLogEventEndIndex" type:"integer"` - // A filter pattern for extracting metric data out of ingested log events. - // - // FilterPattern is a required field - FilterPattern *string `locationName:"filterPattern" type:"string" required:"true"` + // The log events that are too new. + TooNewLogEventStartIndex *int64 `locationName:"tooNewLogEventStartIndex" type:"integer"` - // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + // The log events that are too old. + TooOldLogEventEndIndex *int64 `locationName:"tooOldLogEventEndIndex" type:"integer"` +} - // A collection of information that defines how metric data gets emitted. - // - // MetricTransformations is a required field - MetricTransformations []*MetricTransformation `locationName:"metricTransformations" min:"1" type:"list" required:"true"` +// String returns the string representation +func (s RejectedLogEventsInfo) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s RejectedLogEventsInfo) GoString() string { + return s.String() +} + +// SetExpiredLogEventEndIndex sets the ExpiredLogEventEndIndex field's value. +func (s *RejectedLogEventsInfo) SetExpiredLogEventEndIndex(v int64) *RejectedLogEventsInfo { + s.ExpiredLogEventEndIndex = &v + return s +} + +// SetTooNewLogEventStartIndex sets the TooNewLogEventStartIndex field's value. +func (s *RejectedLogEventsInfo) SetTooNewLogEventStartIndex(v int64) *RejectedLogEventsInfo { + s.TooNewLogEventStartIndex = &v + return s +} + +// SetTooOldLogEventEndIndex sets the TooOldLogEventEndIndex field's value. +func (s *RejectedLogEventsInfo) SetTooOldLogEventEndIndex(v int64) *RejectedLogEventsInfo { + s.TooOldLogEventEndIndex = &v + return s +} + +// The specified resource already exists. +type ResourceAlreadyExistsException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata + + Message_ *string `locationName:"message" type:"string"` } // String returns the string representation -func (s PutMetricFilterInput) String() string { +func (s ResourceAlreadyExistsException) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutMetricFilterInput) GoString() string { +func (s ResourceAlreadyExistsException) GoString() string { return s.String() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *PutMetricFilterInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "PutMetricFilterInput"} - if s.FilterName == nil { - invalidParams.Add(request.NewErrParamRequired("FilterName")) - } - if s.FilterName != nil && len(*s.FilterName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("FilterName", 1)) - } - if s.FilterPattern == nil { - invalidParams.Add(request.NewErrParamRequired("FilterPattern")) - } - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) - } - if s.MetricTransformations == nil { - invalidParams.Add(request.NewErrParamRequired("MetricTransformations")) - } - if s.MetricTransformations != nil && len(s.MetricTransformations) < 1 { - invalidParams.Add(request.NewErrParamMinLen("MetricTransformations", 1)) - } - if s.MetricTransformations != nil { - for i, v := range s.MetricTransformations { - if v == nil { - continue - } - if err := v.Validate(); err != nil { - invalidParams.AddNested(fmt.Sprintf("%s[%v]", "MetricTransformations", i), err.(request.ErrInvalidParams)) - } - } +func newErrorResourceAlreadyExistsException(v protocol.ResponseMetadata) error { + return &ResourceAlreadyExistsException{ + respMetadata: v, } +} - if invalidParams.Len() > 0 { - return invalidParams +// Code returns the exception type name. +func (s ResourceAlreadyExistsException) Code() string { + return "ResourceAlreadyExistsException" +} + +// Message returns the exception's message. +func (s ResourceAlreadyExistsException) Message() string { + if s.Message_ != nil { + return *s.Message_ } - return nil + return "" } -// SetFilterName sets the FilterName field's value. -func (s *PutMetricFilterInput) SetFilterName(v string) *PutMetricFilterInput { - s.FilterName = &v - return s +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s ResourceAlreadyExistsException) OrigErr() error { + return nil } -// SetFilterPattern sets the FilterPattern field's value. -func (s *PutMetricFilterInput) SetFilterPattern(v string) *PutMetricFilterInput { - s.FilterPattern = &v - return s +func (s ResourceAlreadyExistsException) Error() string { + return fmt.Sprintf("%s: %s", s.Code(), s.Message()) } -// SetLogGroupName sets the LogGroupName field's value. -func (s *PutMetricFilterInput) SetLogGroupName(v string) *PutMetricFilterInput { - s.LogGroupName = &v - return s +// Status code returns the HTTP status code for the request's response error. +func (s ResourceAlreadyExistsException) StatusCode() int { + return s.respMetadata.StatusCode } -// SetMetricTransformations sets the MetricTransformations field's value. -func (s *PutMetricFilterInput) SetMetricTransformations(v []*MetricTransformation) *PutMetricFilterInput { - s.MetricTransformations = v - return s +// RequestID returns the service's response RequestID for request. +func (s ResourceAlreadyExistsException) RequestID() string { + return s.respMetadata.RequestID } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutMetricFilterOutput -type PutMetricFilterOutput struct { - _ struct{} `type:"structure"` +// The specified resource does not exist. +type ResourceNotFoundException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata + + Message_ *string `locationName:"message" type:"string"` } // String returns the string representation -func (s PutMetricFilterOutput) String() string { +func (s ResourceNotFoundException) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutMetricFilterOutput) GoString() string { +func (s ResourceNotFoundException) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutResourcePolicyRequest -type PutResourcePolicyInput struct { +func newErrorResourceNotFoundException(v protocol.ResponseMetadata) error { + return &ResourceNotFoundException{ + respMetadata: v, + } +} + +// Code returns the exception type name. +func (s ResourceNotFoundException) Code() string { + return "ResourceNotFoundException" +} + +// Message returns the exception's message. +func (s ResourceNotFoundException) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" +} + +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s ResourceNotFoundException) OrigErr() error { + return nil +} + +func (s ResourceNotFoundException) Error() string { + return fmt.Sprintf("%s: %s", s.Code(), s.Message()) +} + +// Status code returns the HTTP status code for the request's response error. +func (s ResourceNotFoundException) StatusCode() int { + return s.respMetadata.StatusCode +} + +// RequestID returns the service's response RequestID for request. +func (s ResourceNotFoundException) RequestID() string { + return s.respMetadata.RequestID +} + +// A policy enabling one or more entities to put logs to a log group in this +// account. +type ResourcePolicy struct { _ struct{} `type:"structure"` - // Details of the new policy, including the identity of the principal that is - // enabled to put logs to this account. This is formatted as a JSON string. - // - // The following example creates a resource policy enabling the Route 53 service - // to put DNS query logs in to the specified log group. Replace "logArn" with - // the ARN of your CloudWatch Logs resource, such as a log group or log stream. - // - // { "Version": "2012-10-17" "Statement": [ { "Sid": "Route53LogsToCloudWatchLogs", - // "Effect": "Allow", "Principal": { "Service": [ "route53.amazonaws.com" ] - // }, "Action":"logs:PutLogEvents", "Resource": logArn } ] } + // Timestamp showing when this policy was last updated, expressed as the number + // of milliseconds after Jan 1, 1970 00:00:00 UTC. + LastUpdatedTime *int64 `locationName:"lastUpdatedTime" type:"long"` + + // The details of the policy. PolicyDocument *string `locationName:"policyDocument" min:"1" type:"string"` - // Name of the new policy. This parameter is required. + // The name of the resource policy. PolicyName *string `locationName:"policyName" type:"string"` } // String returns the string representation -func (s PutResourcePolicyInput) String() string { +func (s ResourcePolicy) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutResourcePolicyInput) GoString() string { +func (s ResourcePolicy) GoString() string { return s.String() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *PutResourcePolicyInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "PutResourcePolicyInput"} - if s.PolicyDocument != nil && len(*s.PolicyDocument) < 1 { - invalidParams.Add(request.NewErrParamMinLen("PolicyDocument", 1)) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil +// SetLastUpdatedTime sets the LastUpdatedTime field's value. +func (s *ResourcePolicy) SetLastUpdatedTime(v int64) *ResourcePolicy { + s.LastUpdatedTime = &v + return s } // SetPolicyDocument sets the PolicyDocument field's value. -func (s *PutResourcePolicyInput) SetPolicyDocument(v string) *PutResourcePolicyInput { +func (s *ResourcePolicy) SetPolicyDocument(v string) *ResourcePolicy { s.PolicyDocument = &v return s } // SetPolicyName sets the PolicyName field's value. -func (s *PutResourcePolicyInput) SetPolicyName(v string) *PutResourcePolicyInput { +func (s *ResourcePolicy) SetPolicyName(v string) *ResourcePolicy { s.PolicyName = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutResourcePolicyResponse -type PutResourcePolicyOutput struct { +// Contains one field from one log event returned by a CloudWatch Logs Insights +// query, along with the value of that field. +type ResultField struct { _ struct{} `type:"structure"` - // The new policy. - ResourcePolicy *ResourcePolicy `locationName:"resourcePolicy" type:"structure"` + // The log event field. + Field *string `locationName:"field" type:"string"` + + // The value of this field. + Value *string `locationName:"value" type:"string"` } // String returns the string representation -func (s PutResourcePolicyOutput) String() string { +func (s ResultField) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutResourcePolicyOutput) GoString() string { +func (s ResultField) GoString() string { return s.String() } -// SetResourcePolicy sets the ResourcePolicy field's value. -func (s *PutResourcePolicyOutput) SetResourcePolicy(v *ResourcePolicy) *PutResourcePolicyOutput { - s.ResourcePolicy = v +// SetField sets the Field field's value. +func (s *ResultField) SetField(v string) *ResultField { + s.Field = &v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutRetentionPolicyRequest -type PutRetentionPolicyInput struct { +// SetValue sets the Value field's value. +func (s *ResultField) SetValue(v string) *ResultField { + s.Value = &v + return s +} + +// Represents the search status of a log stream. +type SearchedLogStream struct { _ struct{} `type:"structure"` - // The name of the log group. - // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + // The name of the log stream. + LogStreamName *string `locationName:"logStreamName" min:"1" type:"string"` - // The number of days to retain the log events in the specified log group. Possible - // values are: 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, - // 1827, and 3653. - // - // RetentionInDays is a required field - RetentionInDays *int64 `locationName:"retentionInDays" type:"integer" required:"true"` + // Indicates whether all the events in this log stream were searched. + SearchedCompletely *bool `locationName:"searchedCompletely" type:"boolean"` +} + +// String returns the string representation +func (s SearchedLogStream) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s SearchedLogStream) GoString() string { + return s.String() +} + +// SetLogStreamName sets the LogStreamName field's value. +func (s *SearchedLogStream) SetLogStreamName(v string) *SearchedLogStream { + s.LogStreamName = &v + return s +} + +// SetSearchedCompletely sets the SearchedCompletely field's value. +func (s *SearchedLogStream) SetSearchedCompletely(v bool) *SearchedLogStream { + s.SearchedCompletely = &v + return s +} + +// The service cannot complete the request. +type ServiceUnavailableException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata + + Message_ *string `locationName:"message" type:"string"` } // String returns the string representation -func (s PutRetentionPolicyInput) String() string { +func (s ServiceUnavailableException) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutRetentionPolicyInput) GoString() string { +func (s ServiceUnavailableException) GoString() string { return s.String() } -// Validate inspects the fields of the type to determine if they are valid. -func (s *PutRetentionPolicyInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "PutRetentionPolicyInput"} - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) - } - if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) - } - if s.RetentionInDays == nil { - invalidParams.Add(request.NewErrParamRequired("RetentionInDays")) +func newErrorServiceUnavailableException(v protocol.ResponseMetadata) error { + return &ServiceUnavailableException{ + respMetadata: v, } +} - if invalidParams.Len() > 0 { - return invalidParams - } - return nil +// Code returns the exception type name. +func (s ServiceUnavailableException) Code() string { + return "ServiceUnavailableException" } -// SetLogGroupName sets the LogGroupName field's value. -func (s *PutRetentionPolicyInput) SetLogGroupName(v string) *PutRetentionPolicyInput { - s.LogGroupName = &v - return s +// Message returns the exception's message. +func (s ServiceUnavailableException) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" } -// SetRetentionInDays sets the RetentionInDays field's value. -func (s *PutRetentionPolicyInput) SetRetentionInDays(v int64) *PutRetentionPolicyInput { - s.RetentionInDays = &v - return s +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s ServiceUnavailableException) OrigErr() error { + return nil } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutRetentionPolicyOutput -type PutRetentionPolicyOutput struct { - _ struct{} `type:"structure"` +func (s ServiceUnavailableException) Error() string { + return fmt.Sprintf("%s: %s", s.Code(), s.Message()) } -// String returns the string representation -func (s PutRetentionPolicyOutput) String() string { - return awsutil.Prettify(s) +// Status code returns the HTTP status code for the request's response error. +func (s ServiceUnavailableException) StatusCode() int { + return s.respMetadata.StatusCode } -// GoString returns the string representation -func (s PutRetentionPolicyOutput) GoString() string { - return s.String() +// RequestID returns the service's response RequestID for request. +func (s ServiceUnavailableException) RequestID() string { + return s.respMetadata.RequestID } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutSubscriptionFilterRequest -type PutSubscriptionFilterInput struct { +type StartQueryInput struct { _ struct{} `type:"structure"` - // The ARN of the destination to deliver matching log events to. Currently, - // the supported destinations are: - // - // * An Amazon Kinesis stream belonging to the same account as the subscription - // filter, for same-account delivery. - // - // * A logical destination (specified using an ARN) belonging to a different - // account, for cross-account delivery. - // - // * An Amazon Kinesis Firehose delivery stream belonging to the same account - // as the subscription filter, for same-account delivery. - // - // * An AWS Lambda function belonging to the same account as the subscription - // filter, for same-account delivery. + // The end of the time range to query. The range is inclusive, so the specified + // end time is included in the query. Specified as epoch time, the number of + // seconds since January 1, 1970, 00:00:00 UTC. // - // DestinationArn is a required field - DestinationArn *string `locationName:"destinationArn" min:"1" type:"string" required:"true"` + // EndTime is a required field + EndTime *int64 `locationName:"endTime" type:"long" required:"true"` - // The method used to distribute log data to the destination. By default log - // data is grouped by log stream, but the grouping can be set to random for - // a more even distribution. This property is only applicable when the destination - // is an Amazon Kinesis stream. - Distribution *string `locationName:"distribution" type:"string" enum:"Distribution"` + // The maximum number of log events to return in the query. If the query string + // uses the fields command, only the specified fields and their values are returned. + // The default is 1000. + Limit *int64 `locationName:"limit" min:"1" type:"integer"` - // A name for the subscription filter. If you are updating an existing filter, - // you must specify the correct name in filterName. Otherwise, the call fails - // because you cannot associate a second filter with a log group. To find the - // name of the filter currently associated with a log group, use DescribeSubscriptionFilters. + // The log group on which to perform the query. // - // FilterName is a required field - FilterName *string `locationName:"filterName" min:"1" type:"string" required:"true"` + // A StartQuery operation must include a logGroupNames or a logGroupName parameter, + // but not both. + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` - // A filter pattern for subscribing to a filtered stream of log events. + // The list of log groups to be queried. You can include up to 20 log groups. // - // FilterPattern is a required field - FilterPattern *string `locationName:"filterPattern" type:"string" required:"true"` + // A StartQuery operation must include a logGroupNames or a logGroupName parameter, + // but not both. + LogGroupNames []*string `locationName:"logGroupNames" type:"list"` - // The name of the log group. + // The query string to use. For more information, see CloudWatch Logs Insights + // Query Syntax (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html). // - // LogGroupName is a required field - LogGroupName *string `locationName:"logGroupName" min:"1" type:"string" required:"true"` + // QueryString is a required field + QueryString *string `locationName:"queryString" type:"string" required:"true"` - // The ARN of an IAM role that grants CloudWatch Logs permissions to deliver - // ingested log events to the destination stream. You don't need to provide - // the ARN when you are working with a logical destination for cross-account - // delivery. - RoleArn *string `locationName:"roleArn" min:"1" type:"string"` + // The beginning of the time range to query. The range is inclusive, so the + // specified start time is included in the query. Specified as epoch time, the + // number of seconds since January 1, 1970, 00:00:00 UTC. + // + // StartTime is a required field + StartTime *int64 `locationName:"startTime" type:"long" required:"true"` } // String returns the string representation -func (s PutSubscriptionFilterInput) String() string { +func (s StartQueryInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s PutSubscriptionFilterInput) GoString() string { +func (s StartQueryInput) GoString() string { return s.String() } // Validate inspects the fields of the type to determine if they are valid. -func (s *PutSubscriptionFilterInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "PutSubscriptionFilterInput"} - if s.DestinationArn == nil { - invalidParams.Add(request.NewErrParamRequired("DestinationArn")) - } - if s.DestinationArn != nil && len(*s.DestinationArn) < 1 { - invalidParams.Add(request.NewErrParamMinLen("DestinationArn", 1)) - } - if s.FilterName == nil { - invalidParams.Add(request.NewErrParamRequired("FilterName")) - } - if s.FilterName != nil && len(*s.FilterName) < 1 { - invalidParams.Add(request.NewErrParamMinLen("FilterName", 1)) - } - if s.FilterPattern == nil { - invalidParams.Add(request.NewErrParamRequired("FilterPattern")) +func (s *StartQueryInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "StartQueryInput"} + if s.EndTime == nil { + invalidParams.Add(request.NewErrParamRequired("EndTime")) } - if s.LogGroupName == nil { - invalidParams.Add(request.NewErrParamRequired("LogGroupName")) + if s.Limit != nil && *s.Limit < 1 { + invalidParams.Add(request.NewErrParamMinValue("Limit", 1)) } if s.LogGroupName != nil && len(*s.LogGroupName) < 1 { invalidParams.Add(request.NewErrParamMinLen("LogGroupName", 1)) } - if s.RoleArn != nil && len(*s.RoleArn) < 1 { - invalidParams.Add(request.NewErrParamMinLen("RoleArn", 1)) + if s.QueryString == nil { + invalidParams.Add(request.NewErrParamRequired("QueryString")) + } + if s.StartTime == nil { + invalidParams.Add(request.NewErrParamRequired("StartTime")) } if invalidParams.Len() > 0 { @@ -7012,181 +8975,128 @@ func (s *PutSubscriptionFilterInput) Validate() error { return nil } -// SetDestinationArn sets the DestinationArn field's value. -func (s *PutSubscriptionFilterInput) SetDestinationArn(v string) *PutSubscriptionFilterInput { - s.DestinationArn = &v - return s -} - -// SetDistribution sets the Distribution field's value. -func (s *PutSubscriptionFilterInput) SetDistribution(v string) *PutSubscriptionFilterInput { - s.Distribution = &v - return s -} - -// SetFilterName sets the FilterName field's value. -func (s *PutSubscriptionFilterInput) SetFilterName(v string) *PutSubscriptionFilterInput { - s.FilterName = &v +// SetEndTime sets the EndTime field's value. +func (s *StartQueryInput) SetEndTime(v int64) *StartQueryInput { + s.EndTime = &v return s } -// SetFilterPattern sets the FilterPattern field's value. -func (s *PutSubscriptionFilterInput) SetFilterPattern(v string) *PutSubscriptionFilterInput { - s.FilterPattern = &v +// SetLimit sets the Limit field's value. +func (s *StartQueryInput) SetLimit(v int64) *StartQueryInput { + s.Limit = &v return s } // SetLogGroupName sets the LogGroupName field's value. -func (s *PutSubscriptionFilterInput) SetLogGroupName(v string) *PutSubscriptionFilterInput { +func (s *StartQueryInput) SetLogGroupName(v string) *StartQueryInput { s.LogGroupName = &v return s } -// SetRoleArn sets the RoleArn field's value. -func (s *PutSubscriptionFilterInput) SetRoleArn(v string) *PutSubscriptionFilterInput { - s.RoleArn = &v +// SetLogGroupNames sets the LogGroupNames field's value. +func (s *StartQueryInput) SetLogGroupNames(v []*string) *StartQueryInput { + s.LogGroupNames = v return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/PutSubscriptionFilterOutput -type PutSubscriptionFilterOutput struct { - _ struct{} `type:"structure"` -} - -// String returns the string representation -func (s PutSubscriptionFilterOutput) String() string { - return awsutil.Prettify(s) +// SetQueryString sets the QueryString field's value. +func (s *StartQueryInput) SetQueryString(v string) *StartQueryInput { + s.QueryString = &v + return s } -// GoString returns the string representation -func (s PutSubscriptionFilterOutput) GoString() string { - return s.String() +// SetStartTime sets the StartTime field's value. +func (s *StartQueryInput) SetStartTime(v int64) *StartQueryInput { + s.StartTime = &v + return s } -// Represents the rejected events. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/RejectedLogEventsInfo -type RejectedLogEventsInfo struct { +type StartQueryOutput struct { _ struct{} `type:"structure"` - // The expired log events. - ExpiredLogEventEndIndex *int64 `locationName:"expiredLogEventEndIndex" type:"integer"` - - // The log events that are too new. - TooNewLogEventStartIndex *int64 `locationName:"tooNewLogEventStartIndex" type:"integer"` - - // The log events that are too old. - TooOldLogEventEndIndex *int64 `locationName:"tooOldLogEventEndIndex" type:"integer"` + // The unique ID of the query. + QueryId *string `locationName:"queryId" type:"string"` } // String returns the string representation -func (s RejectedLogEventsInfo) String() string { +func (s StartQueryOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s RejectedLogEventsInfo) GoString() string { +func (s StartQueryOutput) GoString() string { return s.String() } -// SetExpiredLogEventEndIndex sets the ExpiredLogEventEndIndex field's value. -func (s *RejectedLogEventsInfo) SetExpiredLogEventEndIndex(v int64) *RejectedLogEventsInfo { - s.ExpiredLogEventEndIndex = &v - return s -} - -// SetTooNewLogEventStartIndex sets the TooNewLogEventStartIndex field's value. -func (s *RejectedLogEventsInfo) SetTooNewLogEventStartIndex(v int64) *RejectedLogEventsInfo { - s.TooNewLogEventStartIndex = &v - return s -} - -// SetTooOldLogEventEndIndex sets the TooOldLogEventEndIndex field's value. -func (s *RejectedLogEventsInfo) SetTooOldLogEventEndIndex(v int64) *RejectedLogEventsInfo { - s.TooOldLogEventEndIndex = &v +// SetQueryId sets the QueryId field's value. +func (s *StartQueryOutput) SetQueryId(v string) *StartQueryOutput { + s.QueryId = &v return s } -// A policy enabling one or more entities to put logs to a log group in this -// account. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/ResourcePolicy -type ResourcePolicy struct { +type StopQueryInput struct { _ struct{} `type:"structure"` - // Time stamp showing when this policy was last updated, expressed as the number - // of milliseconds after Jan 1, 1970 00:00:00 UTC. - LastUpdatedTime *int64 `locationName:"lastUpdatedTime" type:"long"` - - // The details of the policy. - PolicyDocument *string `locationName:"policyDocument" min:"1" type:"string"` - - // The name of the resource policy. - PolicyName *string `locationName:"policyName" type:"string"` + // The ID number of the query to stop. If necessary, you can use DescribeQueries + // to find this ID number. + // + // QueryId is a required field + QueryId *string `locationName:"queryId" type:"string" required:"true"` } // String returns the string representation -func (s ResourcePolicy) String() string { +func (s StopQueryInput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s ResourcePolicy) GoString() string { +func (s StopQueryInput) GoString() string { return s.String() } -// SetLastUpdatedTime sets the LastUpdatedTime field's value. -func (s *ResourcePolicy) SetLastUpdatedTime(v int64) *ResourcePolicy { - s.LastUpdatedTime = &v - return s -} +// Validate inspects the fields of the type to determine if they are valid. +func (s *StopQueryInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "StopQueryInput"} + if s.QueryId == nil { + invalidParams.Add(request.NewErrParamRequired("QueryId")) + } -// SetPolicyDocument sets the PolicyDocument field's value. -func (s *ResourcePolicy) SetPolicyDocument(v string) *ResourcePolicy { - s.PolicyDocument = &v - return s + if invalidParams.Len() > 0 { + return invalidParams + } + return nil } -// SetPolicyName sets the PolicyName field's value. -func (s *ResourcePolicy) SetPolicyName(v string) *ResourcePolicy { - s.PolicyName = &v +// SetQueryId sets the QueryId field's value. +func (s *StopQueryInput) SetQueryId(v string) *StopQueryInput { + s.QueryId = &v return s } -// Represents the search status of a log stream. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/SearchedLogStream -type SearchedLogStream struct { +type StopQueryOutput struct { _ struct{} `type:"structure"` - // The name of the log stream. - LogStreamName *string `locationName:"logStreamName" min:"1" type:"string"` - - // Indicates whether all the events in this log stream were searched. - SearchedCompletely *bool `locationName:"searchedCompletely" type:"boolean"` + // This is true if the query was stopped by the StopQuery operation. + Success *bool `locationName:"success" type:"boolean"` } // String returns the string representation -func (s SearchedLogStream) String() string { +func (s StopQueryOutput) String() string { return awsutil.Prettify(s) } // GoString returns the string representation -func (s SearchedLogStream) GoString() string { +func (s StopQueryOutput) GoString() string { return s.String() } -// SetLogStreamName sets the LogStreamName field's value. -func (s *SearchedLogStream) SetLogStreamName(v string) *SearchedLogStream { - s.LogStreamName = &v - return s -} - -// SetSearchedCompletely sets the SearchedCompletely field's value. -func (s *SearchedLogStream) SetSearchedCompletely(v bool) *SearchedLogStream { - s.SearchedCompletely = &v +// SetSuccess sets the Success field's value. +func (s *StopQueryOutput) SetSuccess(v bool) *StopQueryOutput { + s.Success = &v return s } // Represents a subscription filter. -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/SubscriptionFilter type SubscriptionFilter struct { _ struct{} `type:"structure"` @@ -7205,7 +9115,7 @@ type SubscriptionFilter struct { FilterName *string `locationName:"filterName" min:"1" type:"string"` // A symbolic description of how CloudWatch Logs should interpret the data in - // each log event. For example, a log event may contain time stamps, IP addresses, + // each log event. For example, a log event may contain timestamps, IP addresses, // strings, and so on. You use the filter pattern to specify what to look for // in the log event message. FilterPattern *string `locationName:"filterPattern" type:"string"` @@ -7268,7 +9178,6 @@ func (s *SubscriptionFilter) SetRoleArn(v string) *SubscriptionFilter { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TagLogGroupRequest type TagLogGroupInput struct { _ struct{} `type:"structure"` @@ -7327,7 +9236,6 @@ func (s *TagLogGroupInput) SetTags(v map[string]*string) *TagLogGroupInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TagLogGroupOutput type TagLogGroupOutput struct { _ struct{} `type:"structure"` } @@ -7342,12 +9250,11 @@ func (s TagLogGroupOutput) GoString() string { return s.String() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TestMetricFilterRequest type TestMetricFilterInput struct { _ struct{} `type:"structure"` // A symbolic description of how CloudWatch Logs should interpret the data in - // each log event. For example, a log event may contain time stamps, IP addresses, + // each log event. For example, a log event may contain timestamps, IP addresses, // strings, and so on. You use the filter pattern to specify what to look for // in the log event message. // @@ -7401,7 +9308,6 @@ func (s *TestMetricFilterInput) SetLogEventMessages(v []*string) *TestMetricFilt return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/TestMetricFilterResponse type TestMetricFilterOutput struct { _ struct{} `type:"structure"` @@ -7425,7 +9331,62 @@ func (s *TestMetricFilterOutput) SetMatches(v []*MetricFilterMatchRecord) *TestM return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/UntagLogGroupRequest +// The most likely cause is an invalid AWS access key ID or secret key. +type UnrecognizedClientException struct { + _ struct{} `type:"structure"` + respMetadata protocol.ResponseMetadata + + Message_ *string `locationName:"message" type:"string"` +} + +// String returns the string representation +func (s UnrecognizedClientException) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s UnrecognizedClientException) GoString() string { + return s.String() +} + +func newErrorUnrecognizedClientException(v protocol.ResponseMetadata) error { + return &UnrecognizedClientException{ + respMetadata: v, + } +} + +// Code returns the exception type name. +func (s UnrecognizedClientException) Code() string { + return "UnrecognizedClientException" +} + +// Message returns the exception's message. +func (s UnrecognizedClientException) Message() string { + if s.Message_ != nil { + return *s.Message_ + } + return "" +} + +// OrigErr always returns nil, satisfies awserr.Error interface. +func (s UnrecognizedClientException) OrigErr() error { + return nil +} + +func (s UnrecognizedClientException) Error() string { + return fmt.Sprintf("%s: %s", s.Code(), s.Message()) +} + +// Status code returns the HTTP status code for the request's response error. +func (s UnrecognizedClientException) StatusCode() int { + return s.respMetadata.StatusCode +} + +// RequestID returns the service's response RequestID for request. +func (s UnrecognizedClientException) RequestID() string { + return s.respMetadata.RequestID +} + type UntagLogGroupInput struct { _ struct{} `type:"structure"` @@ -7484,7 +9445,6 @@ func (s *UntagLogGroupInput) SetTags(v []*string) *UntagLogGroupInput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/logs-2014-03-28/UntagLogGroupOutput type UntagLogGroupOutput struct { _ struct{} `type:"structure"` } @@ -7536,3 +9496,20 @@ const ( // OrderByLastEventTime is a OrderBy enum value OrderByLastEventTime = "LastEventTime" ) + +const ( + // QueryStatusScheduled is a QueryStatus enum value + QueryStatusScheduled = "Scheduled" + + // QueryStatusRunning is a QueryStatus enum value + QueryStatusRunning = "Running" + + // QueryStatusComplete is a QueryStatus enum value + QueryStatusComplete = "Complete" + + // QueryStatusFailed is a QueryStatus enum value + QueryStatusFailed = "Failed" + + // QueryStatusCancelled is a QueryStatus enum value + QueryStatusCancelled = "Cancelled" +) diff --git a/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/errors.go b/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/errors.go index 772141f53a70d..c6e23336d00ac 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/errors.go +++ b/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/errors.go @@ -2,6 +2,10 @@ package cloudwatchlogs +import ( + "github.com/aws/aws-sdk-go/private/protocol" +) + const ( // ErrCodeDataAlreadyAcceptedException for service response error code @@ -25,7 +29,8 @@ const ( // ErrCodeInvalidSequenceTokenException for service response error code // "InvalidSequenceTokenException". // - // The sequence token is not valid. + // The sequence token is not valid. You can get the correct sequence token in + // the expectedSequenceToken field in the InvalidSequenceTokenException message. ErrCodeInvalidSequenceTokenException = "InvalidSequenceTokenException" // ErrCodeLimitExceededException for service response error code @@ -34,6 +39,16 @@ const ( // You have reached the maximum number of resources that can be created. ErrCodeLimitExceededException = "LimitExceededException" + // ErrCodeMalformedQueryException for service response error code + // "MalformedQueryException". + // + // The query string is not valid. Details about this error are displayed in + // a QueryCompileError object. For more information, see . + // + // For more information about valid query syntax, see CloudWatch Logs Insights + // Query Syntax (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html). + ErrCodeMalformedQueryException = "MalformedQueryException" + // ErrCodeOperationAbortedException for service response error code // "OperationAbortedException". // @@ -57,4 +72,24 @@ const ( // // The service cannot complete the request. ErrCodeServiceUnavailableException = "ServiceUnavailableException" + + // ErrCodeUnrecognizedClientException for service response error code + // "UnrecognizedClientException". + // + // The most likely cause is an invalid AWS access key ID or secret key. + ErrCodeUnrecognizedClientException = "UnrecognizedClientException" ) + +var exceptionFromCode = map[string]func(protocol.ResponseMetadata) error{ + "DataAlreadyAcceptedException": newErrorDataAlreadyAcceptedException, + "InvalidOperationException": newErrorInvalidOperationException, + "InvalidParameterException": newErrorInvalidParameterException, + "InvalidSequenceTokenException": newErrorInvalidSequenceTokenException, + "LimitExceededException": newErrorLimitExceededException, + "MalformedQueryException": newErrorMalformedQueryException, + "OperationAbortedException": newErrorOperationAbortedException, + "ResourceAlreadyExistsException": newErrorResourceAlreadyExistsException, + "ResourceNotFoundException": newErrorResourceNotFoundException, + "ServiceUnavailableException": newErrorServiceUnavailableException, + "UnrecognizedClientException": newErrorUnrecognizedClientException, +} diff --git a/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/service.go b/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/service.go index 8e6094d58a5f1..41520eda94688 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/service.go +++ b/vendor/github.com/aws/aws-sdk-go/service/cloudwatchlogs/service.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws/client/metadata" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/signer/v4" + "github.com/aws/aws-sdk-go/private/protocol" "github.com/aws/aws-sdk-go/private/protocol/jsonrpc" ) @@ -29,8 +30,9 @@ var initRequest func(*request.Request) // Service information constants const ( - ServiceName = "logs" // Service endpoint prefix API calls made to. - EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata. + ServiceName = "logs" // Name of service. + EndpointsID = ServiceName // ID to lookup a service endpoint with. + ServiceID = "CloudWatch Logs" // ServiceID is a unique identifier of a specific service. ) // New creates a new instance of the CloudWatchLogs client with a session. @@ -38,6 +40,8 @@ const ( // aws.Config parameter to add your extra config. // // Example: +// mySession := session.Must(session.NewSession()) +// // // Create a CloudWatchLogs client from just a session. // svc := cloudwatchlogs.New(mySession) // @@ -45,18 +49,20 @@ const ( // svc := cloudwatchlogs.New(mySession, aws.NewConfig().WithRegion("us-west-2")) func New(p client.ConfigProvider, cfgs ...*aws.Config) *CloudWatchLogs { c := p.ClientConfig(EndpointsID, cfgs...) - return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName) + return newClient(*c.Config, c.Handlers, c.PartitionID, c.Endpoint, c.SigningRegion, c.SigningName) } // newClient creates, initializes and returns a new service client instance. -func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *CloudWatchLogs { +func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, signingRegion, signingName string) *CloudWatchLogs { svc := &CloudWatchLogs{ Client: client.New( cfg, metadata.ClientInfo{ ServiceName: ServiceName, + ServiceID: ServiceID, SigningName: signingName, SigningRegion: signingRegion, + PartitionID: partitionID, Endpoint: endpoint, APIVersion: "2014-03-28", JSONVersion: "1.1", @@ -71,7 +77,9 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler) svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler) svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler) - svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler) + svc.Handlers.UnmarshalError.PushBackNamed( + protocol.NewUnmarshalErrorHandler(jsonrpc.NewUnmarshalTypedError(exceptionFromCode)).NamedHandler(), + ) // Run custom client initialization if present if initClient != nil { diff --git a/vendor/github.com/aws/aws-sdk-go/service/sts/api.go b/vendor/github.com/aws/aws-sdk-go/service/sts/api.go index 23f0a06db8abc..7f60d4aa1851e 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/sts/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/sts/api.go @@ -3,10 +3,12 @@ package sts import ( + "fmt" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awsutil" + "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/request" ) @@ -14,8 +16,8 @@ const opAssumeRole = "AssumeRole" // AssumeRoleRequest generates a "aws/request.Request" representing the // client's request for the AssumeRole operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -54,95 +56,123 @@ func (c *STS) AssumeRoleRequest(input *AssumeRoleInput) (req *request.Request, o // AssumeRole API operation for AWS Security Token Service. // -// Returns a set of temporary security credentials (consisting of an access -// key ID, a secret access key, and a security token) that you can use to access -// AWS resources that you might not normally have access to. Typically, you -// use AssumeRole for cross-account access or federation. For a comparison of -// AssumeRole with the other APIs that produce temporary credentials, see Requesting -// Temporary Security Credentials (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) -// and Comparing the AWS STS APIs (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) +// Returns a set of temporary security credentials that you can use to access +// AWS resources that you might not normally have access to. These temporary +// credentials consist of an access key ID, a secret access key, and a security +// token. Typically, you use AssumeRole within your account or for cross-account +// access. For a comparison of AssumeRole with other API operations that produce +// temporary credentials, see Requesting Temporary Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) +// and Comparing the AWS STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) // in the IAM User Guide. // -// Important: You cannot call AssumeRole by using AWS root account credentials; -// access is denied. You must use credentials for an IAM user or an IAM role -// to call AssumeRole. +// You cannot use AWS account root user credentials to call AssumeRole. You +// must use credentials for an IAM user or an IAM role to call AssumeRole. // // For cross-account access, imagine that you own multiple accounts and need // to access resources in each account. You could create long-term credentials // in each account to access those resources. However, managing all those credentials // and remembering which one can access which account can be time consuming. -// Instead, you can create one set of long-term credentials in one account and -// then use temporary security credentials to access all the other accounts +// Instead, you can create one set of long-term credentials in one account. +// Then use temporary security credentials to access all the other accounts // by assuming roles in those accounts. For more information about roles, see -// IAM Roles (Delegation and Federation) (http://docs.aws.amazon.com/IAM/latest/UserGuide/roles-toplevel.html) +// IAM Roles (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html) // in the IAM User Guide. // -// For federation, you can, for example, grant single sign-on access to the -// AWS Management Console. If you already have an identity and authentication -// system in your corporate network, you don't have to recreate user identities -// in AWS in order to grant those user identities access to AWS. Instead, after -// a user has been authenticated, you call AssumeRole (and specify the role -// with the appropriate permissions) to get temporary security credentials for -// that user. With those temporary security credentials, you construct a sign-in -// URL that users can use to access the console. For more information, see Common -// Scenarios for Temporary Credentials (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html#sts-introduction) +// Session Duration +// +// By default, the temporary security credentials created by AssumeRole last +// for one hour. However, you can use the optional DurationSeconds parameter +// to specify the duration of your session. You can provide a value from 900 +// seconds (15 minutes) up to the maximum session duration setting for the role. +// This setting can have a value from 1 hour to 12 hours. To learn how to view +// the maximum value for your role, see View the Maximum Session Duration Setting +// for a Role (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session) +// in the IAM User Guide. The maximum session duration limit applies when you +// use the AssumeRole* API operations or the assume-role* CLI commands. However +// the limit does not apply when you use those operations to create a console +// URL. For more information, see Using IAM Roles (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html) // in the IAM User Guide. // -// The temporary security credentials are valid for the duration that you specified -// when calling AssumeRole, which can be from 900 seconds (15 minutes) to a -// maximum of 3600 seconds (1 hour). The default is 1 hour. +// Permissions // // The temporary security credentials created by AssumeRole can be used to make -// API calls to any AWS service with the following exception: you cannot call -// the STS service's GetFederationToken or GetSessionToken APIs. -// -// Optionally, you can pass an IAM access policy to this operation. If you choose -// not to pass a policy, the temporary security credentials that are returned -// by the operation have the permissions that are defined in the access policy -// of the role that is being assumed. If you pass a policy to this operation, -// the temporary security credentials that are returned by the operation have -// the permissions that are allowed by both the access policy of the role that -// is being assumed, and the policy that you pass. This gives you a way to further -// restrict the permissions for the resulting temporary security credentials. -// You cannot use the passed policy to grant permissions that are in excess -// of those allowed by the access policy of the role that is being assumed. -// For more information, see Permissions for AssumeRole, AssumeRoleWithSAML, -// and AssumeRoleWithWebIdentity (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_assumerole.html) +// API calls to any AWS service with the following exception: You cannot call +// the AWS STS GetFederationToken or GetSessionToken API operations. +// +// (Optional) You can pass inline or managed session policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) +// to this operation. You can pass a single JSON policy document to use as an +// inline session policy. You can also specify up to 10 managed policies to +// use as managed session policies. The plain text that you use for both inline +// and managed session policies can't exceed 2,048 characters. Passing policies +// to this operation returns new temporary credentials. The resulting session's +// permissions are the intersection of the role's identity-based policy and +// the session policies. You can use the role's temporary credentials in subsequent +// AWS API calls to access resources in the account that owns the role. You +// cannot use session policies to grant more permissions than those allowed +// by the identity-based policy of the role that is being assumed. For more +// information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) +// in the IAM User Guide. +// +// To assume a role from a different account, your AWS account must be trusted +// by the role. The trust relationship is defined in the role's trust policy +// when the role is created. That trust policy states which accounts are allowed +// to delegate that access to users in the account. +// +// A user who wants to access a role in a different account must also have permissions +// that are delegated from the user account administrator. The administrator +// must attach a policy that allows the user to call AssumeRole for the ARN +// of the role in the other account. If the user is in the same account as the +// role, then you can do either of the following: +// +// * Attach a policy to the user (identical to the previous user in a different +// account). +// +// * Add the user as a principal directly in the role's trust policy. +// +// In this case, the trust policy acts as an IAM resource-based policy. Users +// in the same account as the role do not need explicit permission to assume +// the role. For more information about trust policies and resource-based policies, +// see IAM Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html) +// in the IAM User Guide. +// +// Tags +// +// (Optional) You can pass tag key-value pairs to your session. These tags are +// called session tags. For more information about session tags, see Passing +// Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) // in the IAM User Guide. // -// To assume a role, your AWS account must be trusted by the role. The trust -// relationship is defined in the role's trust policy when the role is created. -// That trust policy states which accounts are allowed to delegate access to -// this account's role. +// An administrator must grant you the permissions necessary to pass session +// tags. The administrator can also create granular permissions to allow you +// to pass only specific session tags. For more information, see Tutorial: Using +// Tags for Attribute-Based Access Control (https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_attribute-based-access-control.html) +// in the IAM User Guide. // -// The user who wants to access the role must also have permissions delegated -// from the role's administrator. If the user is in a different account than -// the role, then the user's administrator must attach a policy that allows -// the user to call AssumeRole on the ARN of the role in the other account. -// If the user is in the same account as the role, then you can either attach -// a policy to the user (identical to the previous different account user), -// or you can add the user as a principal directly in the role's trust policy +// You can set the session tags as transitive. Transitive tags persist during +// role chaining. For more information, see Chaining Roles with Session Tags +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining) +// in the IAM User Guide. // // Using MFA with AssumeRole // -// You can optionally include multi-factor authentication (MFA) information -// when you call AssumeRole. This is useful for cross-account scenarios in which -// you want to make sure that the user who is assuming the role has been authenticated -// using an AWS MFA device. In that scenario, the trust policy of the role being -// assumed includes a condition that tests for MFA authentication; if the caller -// does not include valid MFA information, the request to assume the role is -// denied. The condition in a trust policy that tests for MFA authentication -// might look like the following example. +// (Optional) You can include multi-factor authentication (MFA) information +// when you call AssumeRole. This is useful for cross-account scenarios to ensure +// that the user that assumes the role has been authenticated with an AWS MFA +// device. In that scenario, the trust policy of the role being assumed includes +// a condition that tests for MFA authentication. If the caller does not include +// valid MFA information, the request to assume the role is denied. The condition +// in a trust policy that tests for MFA authentication might look like the following +// example. // // "Condition": {"Bool": {"aws:MultiFactorAuthPresent": true}} // -// For more information, see Configuring MFA-Protected API Access (http://docs.aws.amazon.com/IAM/latest/UserGuide/MFAProtectedAPI.html) +// For more information, see Configuring MFA-Protected API Access (https://docs.aws.amazon.com/IAM/latest/UserGuide/MFAProtectedAPI.html) // in the IAM User Guide guide. // // To use MFA with AssumeRole, you pass values for the SerialNumber and TokenCode // parameters. The SerialNumber value identifies the user's hardware or virtual // MFA device. The TokenCode is the time-based one-time password (TOTP) that -// the MFA devices produces. +// the MFA device produces. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -157,15 +187,24 @@ func (c *STS) AssumeRoleRequest(input *AssumeRoleInput) (req *request.Request, o // message describes the specific error. // // * ErrCodePackedPolicyTooLargeException "PackedPolicyTooLarge" -// The request was rejected because the policy document was too large. The error -// message describes how big the policy document is, in packed form, as a percentage -// of what the API allows. +// The request was rejected because the total packed size of the session policies +// and session tags combined was too large. An AWS conversion compresses the +// session policy document, session policy ARNs, and session tags into a packed +// binary format that has a separate limit. The error message indicates by percentage +// how close the policies and tags are to the upper size limit. For more information, +// see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// in the IAM User Guide. +// +// You could receive this error even though you meet other defined session policy +// and session tag limits. For more information, see IAM and STS Entity Character +// Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// in the IAM User Guide. // // * ErrCodeRegionDisabledException "RegionDisabledException" // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating -// and Deactivating AWS STS in an AWS Region (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. // // See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRole @@ -194,8 +233,8 @@ const opAssumeRoleWithSAML = "AssumeRoleWithSAML" // AssumeRoleWithSAMLRequest generates a "aws/request.Request" representing the // client's request for the AssumeRoleWithSAML operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -229,6 +268,7 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re output = &AssumeRoleWithSAMLOutput{} req = c.newRequest(op, input, output) + req.Config.Credentials = credentials.AnonymousCredentials return } @@ -238,46 +278,52 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // via a SAML authentication response. This operation provides a mechanism for // tying an enterprise identity store or directory to role-based AWS access // without user-specific credentials or configuration. For a comparison of AssumeRoleWithSAML -// with the other APIs that produce temporary credentials, see Requesting Temporary -// Security Credentials (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) -// and Comparing the AWS STS APIs (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) +// with the other API operations that produce temporary credentials, see Requesting +// Temporary Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) +// and Comparing the AWS STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) // in the IAM User Guide. // // The temporary security credentials returned by this operation consist of // an access key ID, a secret access key, and a security token. Applications // can use these temporary security credentials to sign calls to AWS services. // -// The temporary security credentials are valid for the duration that you specified -// when calling AssumeRole, or until the time specified in the SAML authentication -// response's SessionNotOnOrAfter value, whichever is shorter. The duration -// can be from 900 seconds (15 minutes) to a maximum of 3600 seconds (1 hour). -// The default is 1 hour. +// Session Duration +// +// By default, the temporary security credentials created by AssumeRoleWithSAML +// last for one hour. However, you can use the optional DurationSeconds parameter +// to specify the duration of your session. Your role session lasts for the +// duration that you specify, or until the time specified in the SAML authentication +// response's SessionNotOnOrAfter value, whichever is shorter. You can provide +// a DurationSeconds value from 900 seconds (15 minutes) up to the maximum session +// duration setting for the role. This setting can have a value from 1 hour +// to 12 hours. To learn how to view the maximum value for your role, see View +// the Maximum Session Duration Setting for a Role (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session) +// in the IAM User Guide. The maximum session duration limit applies when you +// use the AssumeRole* API operations or the assume-role* CLI commands. However +// the limit does not apply when you use those operations to create a console +// URL. For more information, see Using IAM Roles (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html) +// in the IAM User Guide. +// +// Permissions // // The temporary security credentials created by AssumeRoleWithSAML can be used // to make API calls to any AWS service with the following exception: you cannot -// call the STS service's GetFederationToken or GetSessionToken APIs. -// -// Optionally, you can pass an IAM access policy to this operation. If you choose -// not to pass a policy, the temporary security credentials that are returned -// by the operation have the permissions that are defined in the access policy -// of the role that is being assumed. If you pass a policy to this operation, -// the temporary security credentials that are returned by the operation have -// the permissions that are allowed by the intersection of both the access policy -// of the role that is being assumed, and the policy that you pass. This means -// that both policies must grant the permission for the action to be allowed. -// This gives you a way to further restrict the permissions for the resulting -// temporary security credentials. You cannot use the passed policy to grant -// permissions that are in excess of those allowed by the access policy of the -// role that is being assumed. For more information, see Permissions for AssumeRole, -// AssumeRoleWithSAML, and AssumeRoleWithWebIdentity (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_assumerole.html) +// call the STS GetFederationToken or GetSessionToken API operations. +// +// (Optional) You can pass inline or managed session policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) +// to this operation. You can pass a single JSON policy document to use as an +// inline session policy. You can also specify up to 10 managed policies to +// use as managed session policies. The plain text that you use for both inline +// and managed session policies can't exceed 2,048 characters. Passing policies +// to this operation returns new temporary credentials. The resulting session's +// permissions are the intersection of the role's identity-based policy and +// the session policies. You can use the role's temporary credentials in subsequent +// AWS API calls to access resources in the account that owns the role. You +// cannot use session policies to grant more permissions than those allowed +// by the identity-based policy of the role that is being assumed. For more +// information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. // -// Before your application can call AssumeRoleWithSAML, you must configure your -// SAML identity provider (IdP) to issue the claims required by AWS. Additionally, -// you must use AWS Identity and Access Management (IAM) to create a SAML provider -// entity in your AWS account that represents your identity provider, and create -// an IAM role that specifies this SAML provider in its trust policy. -// // Calling AssumeRoleWithSAML does not require the use of AWS security credentials. // The identity of the caller is validated by using keys in the metadata document // that is uploaded for the SAML provider entity for your identity provider. @@ -285,21 +331,63 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // Calling AssumeRoleWithSAML can result in an entry in your AWS CloudTrail // logs. The entry includes the value in the NameID element of the SAML assertion. // We recommend that you use a NameIDType that is not associated with any personally -// identifiable information (PII). For example, you could instead use the Persistent -// Identifier (urn:oasis:names:tc:SAML:2.0:nameid-format:persistent). +// identifiable information (PII). For example, you could instead use the persistent +// identifier (urn:oasis:names:tc:SAML:2.0:nameid-format:persistent). +// +// Tags +// +// (Optional) You can configure your IdP to pass attributes into your SAML assertion +// as session tags. Each session tag consists of a key name and an associated +// value. For more information about session tags, see Passing Session Tags +// in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// in the IAM User Guide. +// +// You can pass up to 50 session tags. The plain text session tag keys can’t +// exceed 128 characters and the values can’t exceed 256 characters. For these +// and additional limits, see IAM and STS Character Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html#reference_iam-limits-entity-length) +// in the IAM User Guide. +// +// An AWS conversion compresses the passed session policies and session tags +// into a packed binary format that has a separate limit. Your request can fail +// for this limit even if your plain text meets the other requirements. The +// PackedPolicySize response element indicates by percentage how close the policies +// and tags for your request are to the upper size limit. +// +// You can pass a session tag with the same key as a tag that is attached to +// the role. When you do, session tags override the role's tags with the same +// key. +// +// An administrator must grant you the permissions necessary to pass session +// tags. The administrator can also create granular permissions to allow you +// to pass only specific session tags. For more information, see Tutorial: Using +// Tags for Attribute-Based Access Control (https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_attribute-based-access-control.html) +// in the IAM User Guide. +// +// You can set the session tags as transitive. Transitive tags persist during +// role chaining. For more information, see Chaining Roles with Session Tags +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining) +// in the IAM User Guide. +// +// SAML Configuration +// +// Before your application can call AssumeRoleWithSAML, you must configure your +// SAML identity provider (IdP) to issue the claims required by AWS. Additionally, +// you must use AWS Identity and Access Management (IAM) to create a SAML provider +// entity in your AWS account that represents your identity provider. You must +// also create an IAM role that specifies this SAML provider in its trust policy. // // For more information, see the following resources: // -// * About SAML 2.0-based Federation (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_saml.html) +// * About SAML 2.0-based Federation (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_saml.html) // in the IAM User Guide. // -// * Creating SAML Identity Providers (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml.html) +// * Creating SAML Identity Providers (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml.html) // in the IAM User Guide. // -// * Configuring a Relying Party and Claims (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml_relying-party.html) +// * Configuring a Relying Party and Claims (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_saml_relying-party.html) // in the IAM User Guide. // -// * Creating a Role for SAML 2.0 Federation (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_saml.html) +// * Creating a Role for SAML 2.0 Federation (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_saml.html) // in the IAM User Guide. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions @@ -315,9 +403,18 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // message describes the specific error. // // * ErrCodePackedPolicyTooLargeException "PackedPolicyTooLarge" -// The request was rejected because the policy document was too large. The error -// message describes how big the policy document is, in packed form, as a percentage -// of what the API allows. +// The request was rejected because the total packed size of the session policies +// and session tags combined was too large. An AWS conversion compresses the +// session policy document, session policy ARNs, and session tags into a packed +// binary format that has a separate limit. The error message indicates by percentage +// how close the policies and tags are to the upper size limit. For more information, +// see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// in the IAM User Guide. +// +// You could receive this error even though you meet other defined session policy +// and session tag limits. For more information, see IAM and STS Entity Character +// Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// in the IAM User Guide. // // * ErrCodeIDPRejectedClaimException "IDPRejectedClaim" // The identity provider (IdP) reported that authentication failed. This might @@ -338,7 +435,7 @@ func (c *STS) AssumeRoleWithSAMLRequest(input *AssumeRoleWithSAMLInput) (req *re // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating -// and Deactivating AWS STS in an AWS Region (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. // // See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithSAML @@ -367,8 +464,8 @@ const opAssumeRoleWithWebIdentity = "AssumeRoleWithWebIdentity" // AssumeRoleWithWebIdentityRequest generates a "aws/request.Request" representing the // client's request for the AssumeRoleWithWebIdentity operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -402,64 +499,114 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI output = &AssumeRoleWithWebIdentityOutput{} req = c.newRequest(op, input, output) + req.Config.Credentials = credentials.AnonymousCredentials return } // AssumeRoleWithWebIdentity API operation for AWS Security Token Service. // // Returns a set of temporary security credentials for users who have been authenticated -// in a mobile or web application with a web identity provider, such as Amazon -// Cognito, Login with Amazon, Facebook, Google, or any OpenID Connect-compatible -// identity provider. +// in a mobile or web application with a web identity provider. Example providers +// include Amazon Cognito, Login with Amazon, Facebook, Google, or any OpenID +// Connect-compatible identity provider. // // For mobile applications, we recommend that you use Amazon Cognito. You can -// use Amazon Cognito with the AWS SDK for iOS (http://aws.amazon.com/sdkforios/) -// and the AWS SDK for Android (http://aws.amazon.com/sdkforandroid/) to uniquely -// identify a user and supply the user with a consistent identity throughout -// the lifetime of an application. -// -// To learn more about Amazon Cognito, see Amazon Cognito Overview (http://docs.aws.amazon.com/mobile/sdkforandroid/developerguide/cognito-auth.html#d0e840) -// in the AWS SDK for Android Developer Guide guide and Amazon Cognito Overview -// (http://docs.aws.amazon.com/mobile/sdkforios/developerguide/cognito-auth.html#d0e664) +// use Amazon Cognito with the AWS SDK for iOS Developer Guide (http://aws.amazon.com/sdkforios/) +// and the AWS SDK for Android Developer Guide (http://aws.amazon.com/sdkforandroid/) +// to uniquely identify a user. You can also supply the user with a consistent +// identity throughout the lifetime of an application. +// +// To learn more about Amazon Cognito, see Amazon Cognito Overview (https://docs.aws.amazon.com/mobile/sdkforandroid/developerguide/cognito-auth.html#d0e840) +// in AWS SDK for Android Developer Guide and Amazon Cognito Overview (https://docs.aws.amazon.com/mobile/sdkforios/developerguide/cognito-auth.html#d0e664) // in the AWS SDK for iOS Developer Guide. // // Calling AssumeRoleWithWebIdentity does not require the use of AWS security // credentials. Therefore, you can distribute an application (for example, on // mobile devices) that requests temporary security credentials without including -// long-term AWS credentials in the application, and without deploying server-based -// proxy services that use long-term AWS credentials. Instead, the identity -// of the caller is validated by using a token from the web identity provider. -// For a comparison of AssumeRoleWithWebIdentity with the other APIs that produce -// temporary credentials, see Requesting Temporary Security Credentials (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) -// and Comparing the AWS STS APIs (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) +// long-term AWS credentials in the application. You also don't need to deploy +// server-based proxy services that use long-term AWS credentials. Instead, +// the identity of the caller is validated by using a token from the web identity +// provider. For a comparison of AssumeRoleWithWebIdentity with the other API +// operations that produce temporary credentials, see Requesting Temporary Security +// Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) +// and Comparing the AWS STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) // in the IAM User Guide. // // The temporary security credentials returned by this API consist of an access // key ID, a secret access key, and a security token. Applications can use these -// temporary security credentials to sign calls to AWS service APIs. +// temporary security credentials to sign calls to AWS service API operations. +// +// Session Duration +// +// By default, the temporary security credentials created by AssumeRoleWithWebIdentity +// last for one hour. However, you can use the optional DurationSeconds parameter +// to specify the duration of your session. You can provide a value from 900 +// seconds (15 minutes) up to the maximum session duration setting for the role. +// This setting can have a value from 1 hour to 12 hours. To learn how to view +// the maximum value for your role, see View the Maximum Session Duration Setting +// for a Role (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session) +// in the IAM User Guide. The maximum session duration limit applies when you +// use the AssumeRole* API operations or the assume-role* CLI commands. However +// the limit does not apply when you use those operations to create a console +// URL. For more information, see Using IAM Roles (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html) +// in the IAM User Guide. // -// The credentials are valid for the duration that you specified when calling -// AssumeRoleWithWebIdentity, which can be from 900 seconds (15 minutes) to -// a maximum of 3600 seconds (1 hour). The default is 1 hour. +// Permissions // // The temporary security credentials created by AssumeRoleWithWebIdentity can // be used to make API calls to any AWS service with the following exception: -// you cannot call the STS service's GetFederationToken or GetSessionToken APIs. -// -// Optionally, you can pass an IAM access policy to this operation. If you choose -// not to pass a policy, the temporary security credentials that are returned -// by the operation have the permissions that are defined in the access policy -// of the role that is being assumed. If you pass a policy to this operation, -// the temporary security credentials that are returned by the operation have -// the permissions that are allowed by both the access policy of the role that -// is being assumed, and the policy that you pass. This gives you a way to further -// restrict the permissions for the resulting temporary security credentials. -// You cannot use the passed policy to grant permissions that are in excess -// of those allowed by the access policy of the role that is being assumed. -// For more information, see Permissions for AssumeRole, AssumeRoleWithSAML, -// and AssumeRoleWithWebIdentity (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_assumerole.html) +// you cannot call the STS GetFederationToken or GetSessionToken API operations. +// +// (Optional) You can pass inline or managed session policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) +// to this operation. You can pass a single JSON policy document to use as an +// inline session policy. You can also specify up to 10 managed policies to +// use as managed session policies. The plain text that you use for both inline +// and managed session policies can't exceed 2,048 characters. Passing policies +// to this operation returns new temporary credentials. The resulting session's +// permissions are the intersection of the role's identity-based policy and +// the session policies. You can use the role's temporary credentials in subsequent +// AWS API calls to access resources in the account that owns the role. You +// cannot use session policies to grant more permissions than those allowed +// by the identity-based policy of the role that is being assumed. For more +// information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) +// in the IAM User Guide. +// +// Tags +// +// (Optional) You can configure your IdP to pass attributes into your web identity +// token as session tags. Each session tag consists of a key name and an associated +// value. For more information about session tags, see Passing Session Tags +// in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) // in the IAM User Guide. // +// You can pass up to 50 session tags. The plain text session tag keys can’t +// exceed 128 characters and the values can’t exceed 256 characters. For these +// and additional limits, see IAM and STS Character Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html#reference_iam-limits-entity-length) +// in the IAM User Guide. +// +// An AWS conversion compresses the passed session policies and session tags +// into a packed binary format that has a separate limit. Your request can fail +// for this limit even if your plain text meets the other requirements. The +// PackedPolicySize response element indicates by percentage how close the policies +// and tags for your request are to the upper size limit. +// +// You can pass a session tag with the same key as a tag that is attached to +// the role. When you do, the session tag overrides the role tag with the same +// key. +// +// An administrator must grant you the permissions necessary to pass session +// tags. The administrator can also create granular permissions to allow you +// to pass only specific session tags. For more information, see Tutorial: Using +// Tags for Attribute-Based Access Control (https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_attribute-based-access-control.html) +// in the IAM User Guide. +// +// You can set the session tags as transitive. Transitive tags persist during +// role chaining. For more information, see Chaining Roles with Session Tags +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining) +// in the IAM User Guide. +// +// Identities +// // Before your application can call AssumeRoleWithWebIdentity, you must have // an identity token from a supported identity provider and create a role that // the application can assume. The role that your application assumes must trust @@ -476,23 +623,21 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // For more information about how to use web identity federation and the AssumeRoleWithWebIdentity // API, see the following resources: // -// * Using Web Identity Federation APIs for Mobile Apps (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_oidc_manual.html) -// and Federation Through a Web-based Identity Provider (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#api_assumerolewithwebidentity). -// +// * Using Web Identity Federation API Operations for Mobile Apps (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_oidc_manual.html) +// and Federation Through a Web-based Identity Provider (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#api_assumerolewithwebidentity). // -// * Web Identity Federation Playground (https://web-identity-federation-playground.s3.amazonaws.com/index.html). -// This interactive website lets you walk through the process of authenticating -// via Login with Amazon, Facebook, or Google, getting temporary security -// credentials, and then using those credentials to make a request to AWS. +// * Web Identity Federation Playground (https://web-identity-federation-playground.s3.amazonaws.com/index.html). +// Walk through the process of authenticating through Login with Amazon, +// Facebook, or Google, getting temporary security credentials, and then +// using those credentials to make a request to AWS. // +// * AWS SDK for iOS Developer Guide (http://aws.amazon.com/sdkforios/) and +// AWS SDK for Android Developer Guide (http://aws.amazon.com/sdkforandroid/). +// These toolkits contain sample apps that show how to invoke the identity +// providers. The toolkits then show how to use the information from these +// providers to get and use temporary security credentials. // -// * AWS SDK for iOS (http://aws.amazon.com/sdkforios/) and AWS SDK for Android -// (http://aws.amazon.com/sdkforandroid/). These toolkits contain sample -// apps that show how to invoke the identity providers, and then how to use -// the information from these providers to get and use temporary security -// credentials. -// -// * Web Identity Federation with Mobile Applications (http://aws.amazon.com/articles/4617974389850313). +// * Web Identity Federation with Mobile Applications (http://aws.amazon.com/articles/web-identity-federation-with-mobile-applications). // This article discusses web identity federation and shows an example of // how to use web identity federation to get access to content in Amazon // S3. @@ -510,9 +655,18 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // message describes the specific error. // // * ErrCodePackedPolicyTooLargeException "PackedPolicyTooLarge" -// The request was rejected because the policy document was too large. The error -// message describes how big the policy document is, in packed form, as a percentage -// of what the API allows. +// The request was rejected because the total packed size of the session policies +// and session tags combined was too large. An AWS conversion compresses the +// session policy document, session policy ARNs, and session tags into a packed +// binary format that has a separate limit. The error message indicates by percentage +// how close the policies and tags are to the upper size limit. For more information, +// see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// in the IAM User Guide. +// +// You could receive this error even though you meet other defined session policy +// and session tag limits. For more information, see IAM and STS Entity Character +// Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// in the IAM User Guide. // // * ErrCodeIDPRejectedClaimException "IDPRejectedClaim" // The identity provider (IdP) reported that authentication failed. This might @@ -522,11 +676,11 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // can also mean that the claim has expired or has been explicitly revoked. // // * ErrCodeIDPCommunicationErrorException "IDPCommunicationError" -// The request could not be fulfilled because the non-AWS identity provider -// (IDP) that was asked to verify the incoming identity token could not be reached. -// This is often a transient error caused by network conditions. Retry the request +// The request could not be fulfilled because the identity provider (IDP) that +// was asked to verify the incoming identity token could not be reached. This +// is often a transient error caused by network conditions. Retry the request // a limited number of times so that you don't exceed the request rate. If the -// error persists, the non-AWS identity provider might be down or not responding. +// error persists, the identity provider might be down or not responding. // // * ErrCodeInvalidIdentityTokenException "InvalidIdentityToken" // The web identity token that was passed could not be validated by AWS. Get @@ -540,7 +694,7 @@ func (c *STS) AssumeRoleWithWebIdentityRequest(input *AssumeRoleWithWebIdentityI // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating -// and Deactivating AWS STS in an AWS Region (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. // // See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithWebIdentity @@ -569,8 +723,8 @@ const opDecodeAuthorizationMessage = "DecodeAuthorizationMessage" // DecodeAuthorizationMessageRequest generates a "aws/request.Request" representing the // client's request for the DecodeAuthorizationMessage operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -612,17 +766,17 @@ func (c *STS) DecodeAuthorizationMessageRequest(input *DecodeAuthorizationMessag // Decodes additional information about the authorization status of a request // from an encoded message returned in response to an AWS request. // -// For example, if a user is not authorized to perform an action that he or -// she has requested, the request returns a Client.UnauthorizedOperation response -// (an HTTP 403 response). Some AWS actions additionally return an encoded message -// that can provide details about this authorization failure. +// For example, if a user is not authorized to perform an operation that he +// or she has requested, the request returns a Client.UnauthorizedOperation +// response (an HTTP 403 response). Some AWS operations additionally return +// an encoded message that can provide details about this authorization failure. // -// Only certain AWS actions return an encoded authorization message. The documentation -// for an individual action indicates whether that action returns an encoded -// message in addition to returning an HTTP code. +// Only certain AWS operations return an encoded authorization message. The +// documentation for an individual operation indicates whether that operation +// returns an encoded message in addition to returning an HTTP code. // // The message is encoded because the details of the authorization status can -// constitute privileged information that the user who requested the action +// constitute privileged information that the user who requested the operation // should not see. To decode an authorization status message, a user must be // granted permissions via an IAM policy to request the DecodeAuthorizationMessage // (sts:DecodeAuthorizationMessage) action. @@ -631,7 +785,7 @@ func (c *STS) DecodeAuthorizationMessageRequest(input *DecodeAuthorizationMessag // // * Whether the request was denied due to an explicit deny or due to the // absence of an explicit allow. For more information, see Determining Whether -// a Request is Allowed or Denied (http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html#policy-eval-denyallow) +// a Request is Allowed or Denied (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html#policy-eval-denyallow) // in the IAM User Guide. // // * The principal who made the request. @@ -677,12 +831,109 @@ func (c *STS) DecodeAuthorizationMessageWithContext(ctx aws.Context, input *Deco return out, req.Send() } +const opGetAccessKeyInfo = "GetAccessKeyInfo" + +// GetAccessKeyInfoRequest generates a "aws/request.Request" representing the +// client's request for the GetAccessKeyInfo operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See GetAccessKeyInfo for more information on using the GetAccessKeyInfo +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the GetAccessKeyInfoRequest method. +// req, resp := client.GetAccessKeyInfoRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetAccessKeyInfo +func (c *STS) GetAccessKeyInfoRequest(input *GetAccessKeyInfoInput) (req *request.Request, output *GetAccessKeyInfoOutput) { + op := &request.Operation{ + Name: opGetAccessKeyInfo, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &GetAccessKeyInfoInput{} + } + + output = &GetAccessKeyInfoOutput{} + req = c.newRequest(op, input, output) + return +} + +// GetAccessKeyInfo API operation for AWS Security Token Service. +// +// Returns the account identifier for the specified access key ID. +// +// Access keys consist of two parts: an access key ID (for example, AKIAIOSFODNN7EXAMPLE) +// and a secret access key (for example, wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY). +// For more information about access keys, see Managing Access Keys for IAM +// Users (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) +// in the IAM User Guide. +// +// When you pass an access key ID to this operation, it returns the ID of the +// AWS account to which the keys belong. Access key IDs beginning with AKIA +// are long-term credentials for an IAM user or the AWS account root user. Access +// key IDs beginning with ASIA are temporary credentials that are created using +// STS operations. If the account in the response belongs to you, you can sign +// in as the root user and review your root user access keys. Then, you can +// pull a credentials report (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html) +// to learn which IAM user owns the keys. To learn who requested the temporary +// credentials for an ASIA access key, view the STS events in your CloudTrail +// logs (https://docs.aws.amazon.com/IAM/latest/UserGuide/cloudtrail-integration.html) +// in the IAM User Guide. +// +// This operation does not indicate the state of the access key. The key might +// be active, inactive, or deleted. Active keys might not have permissions to +// perform an operation. Providing a deleted access key might return an error +// that the key doesn't exist. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for AWS Security Token Service's +// API operation GetAccessKeyInfo for usage and error information. +// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetAccessKeyInfo +func (c *STS) GetAccessKeyInfo(input *GetAccessKeyInfoInput) (*GetAccessKeyInfoOutput, error) { + req, out := c.GetAccessKeyInfoRequest(input) + return out, req.Send() +} + +// GetAccessKeyInfoWithContext is the same as GetAccessKeyInfo with the addition of +// the ability to pass a context and additional request options. +// +// See GetAccessKeyInfo for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *STS) GetAccessKeyInfoWithContext(ctx aws.Context, input *GetAccessKeyInfoInput, opts ...request.Option) (*GetAccessKeyInfoOutput, error) { + req, out := c.GetAccessKeyInfoRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + const opGetCallerIdentity = "GetCallerIdentity" // GetCallerIdentityRequest generates a "aws/request.Request" representing the // client's request for the GetCallerIdentity operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -721,8 +972,16 @@ func (c *STS) GetCallerIdentityRequest(input *GetCallerIdentityInput) (req *requ // GetCallerIdentity API operation for AWS Security Token Service. // -// Returns details about the IAM identity whose credentials are used to call -// the API. +// Returns details about the IAM user or role whose credentials are used to +// call the operation. +// +// No permissions are required to perform this operation. If an administrator +// adds a policy to your IAM user or role that explicitly denies access to the +// sts:GetCallerIdentity action, you can still perform this operation. Permissions +// are not required because the same information is returned when an IAM user +// or role is denied access. To view an example response, see I Am Not Authorized +// to Perform: iam:DeleteVirtualMFADevice (https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_general.html#troubleshoot_general_access-denied-delete-mfa) +// in the IAM User Guide. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -756,8 +1015,8 @@ const opGetFederationToken = "GetFederationToken" // GetFederationTokenRequest generates a "aws/request.Request" representing the // client's request for the GetFederationToken operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -799,81 +1058,92 @@ func (c *STS) GetFederationTokenRequest(input *GetFederationTokenInput) (req *re // Returns a set of temporary security credentials (consisting of an access // key ID, a secret access key, and a security token) for a federated user. // A typical use is in a proxy application that gets temporary security credentials -// on behalf of distributed applications inside a corporate network. Because -// you must call the GetFederationToken action using the long-term security -// credentials of an IAM user, this call is appropriate in contexts where those +// on behalf of distributed applications inside a corporate network. You must +// call the GetFederationToken operation using the long-term security credentials +// of an IAM user. As a result, this call is appropriate in contexts where those // credentials can be safely stored, usually in a server-based application. -// For a comparison of GetFederationToken with the other APIs that produce temporary -// credentials, see Requesting Temporary Security Credentials (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) -// and Comparing the AWS STS APIs (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) +// For a comparison of GetFederationToken with the other API operations that +// produce temporary credentials, see Requesting Temporary Security Credentials +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) +// and Comparing the AWS STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) // in the IAM User Guide. // -// If you are creating a mobile-based or browser-based app that can authenticate +// You can create a mobile-based or browser-based app that can authenticate // users using a web identity provider like Login with Amazon, Facebook, Google, -// or an OpenID Connect-compatible identity provider, we recommend that you -// use Amazon Cognito (http://aws.amazon.com/cognito/) or AssumeRoleWithWebIdentity. +// or an OpenID Connect-compatible identity provider. In this case, we recommend +// that you use Amazon Cognito (http://aws.amazon.com/cognito/) or AssumeRoleWithWebIdentity. // For more information, see Federation Through a Web-based Identity Provider -// (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#api_assumerolewithwebidentity). -// -// The GetFederationToken action must be called by using the long-term AWS security -// credentials of an IAM user. You can also call GetFederationToken using the -// security credentials of an AWS root account, but we do not recommended it. -// Instead, we recommend that you create an IAM user for the purpose of the -// proxy application and then attach a policy to the IAM user that limits federated -// users to only the actions and resources that they need access to. For more -// information, see IAM Best Practices (http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) +// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#api_assumerolewithwebidentity) // in the IAM User Guide. // -// The temporary security credentials that are obtained by using the long-term -// credentials of an IAM user are valid for the specified duration, from 900 -// seconds (15 minutes) up to a maximium of 129600 seconds (36 hours). The default -// is 43200 seconds (12 hours). Temporary credentials that are obtained by using -// AWS root account credentials have a maximum duration of 3600 seconds (1 hour). -// -// The temporary security credentials created by GetFederationToken can be used -// to make API calls to any AWS service with the following exceptions: +// You can also call GetFederationToken using the security credentials of an +// AWS account root user, but we do not recommend it. Instead, we recommend +// that you create an IAM user for the purpose of the proxy application. Then +// attach a policy to the IAM user that limits federated users to only the actions +// and resources that they need to access. For more information, see IAM Best +// Practices (https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) +// in the IAM User Guide. // -// * You cannot use these credentials to call any IAM APIs. +// Session duration // -// * You cannot call any STS APIs except GetCallerIdentity. +// The temporary credentials are valid for the specified duration, from 900 +// seconds (15 minutes) up to a maximum of 129,600 seconds (36 hours). The default +// session duration is 43,200 seconds (12 hours). Temporary credentials that +// are obtained by using AWS account root user credentials have a maximum duration +// of 3,600 seconds (1 hour). // // Permissions // -// The permissions for the temporary security credentials returned by GetFederationToken -// are determined by a combination of the following: -// -// * The policy or policies that are attached to the IAM user whose credentials -// are used to call GetFederationToken. -// -// * The policy that is passed as a parameter in the call. -// -// The passed policy is attached to the temporary security credentials that -// result from the GetFederationToken API call--that is, to the federated user. -// When the federated user makes an AWS request, AWS evaluates the policy attached -// to the federated user in combination with the policy or policies attached -// to the IAM user whose credentials were used to call GetFederationToken. AWS -// allows the federated user's request only when both the federated user and -// the IAM user are explicitly allowed to perform the requested action. The -// passed policy cannot grant more permissions than those that are defined in -// the IAM user policy. -// -// A typical use case is that the permissions of the IAM user whose credentials -// are used to call GetFederationToken are designed to allow access to all the -// actions and resources that any federated user will need. Then, for individual -// users, you pass a policy to the operation that scopes down the permissions -// to a level that's appropriate to that individual user, using a policy that -// allows only a subset of permissions that are granted to the IAM user. -// -// If you do not pass a policy, the resulting temporary security credentials -// have no effective permissions. The only exception is when the temporary security -// credentials are used to access a resource that has a resource-based policy -// that specifically allows the federated user to access the resource. -// -// For more information about how permissions work, see Permissions for GetFederationToken -// (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_getfederationtoken.html). -// For information about using GetFederationToken to create temporary security -// credentials, see GetFederationToken—Federation Through a Custom Identity -// Broker (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#api_getfederationtoken). +// You can use the temporary credentials created by GetFederationToken in any +// AWS service except the following: +// +// * You cannot call any IAM operations using the AWS CLI or the AWS API. +// +// * You cannot call any STS operations except GetCallerIdentity. +// +// You must pass an inline or managed session policy (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) +// to this operation. You can pass a single JSON policy document to use as an +// inline session policy. You can also specify up to 10 managed policies to +// use as managed session policies. The plain text that you use for both inline +// and managed session policies can't exceed 2,048 characters. +// +// Though the session policy parameters are optional, if you do not pass a policy, +// then the resulting federated user session has no permissions. When you pass +// session policies, the session permissions are the intersection of the IAM +// user policies and the session policies that you pass. This gives you a way +// to further restrict the permissions for a federated user. You cannot use +// session policies to grant more permissions than those that are defined in +// the permissions policy of the IAM user. For more information, see Session +// Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) +// in the IAM User Guide. For information about using GetFederationToken to +// create temporary security credentials, see GetFederationToken—Federation +// Through a Custom Identity Broker (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#api_getfederationtoken). +// +// You can use the credentials to access a resource that has a resource-based +// policy. If that policy specifically references the federated user session +// in the Principal element of the policy, the session has the permissions allowed +// by the policy. These permissions are granted in addition to the permissions +// granted by the session policies. +// +// Tags +// +// (Optional) You can pass tag key-value pairs to your session. These are called +// session tags. For more information about session tags, see Passing Session +// Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// in the IAM User Guide. +// +// An administrator must grant you the permissions necessary to pass session +// tags. The administrator can also create granular permissions to allow you +// to pass only specific session tags. For more information, see Tutorial: Using +// Tags for Attribute-Based Access Control (https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_attribute-based-access-control.html) +// in the IAM User Guide. +// +// Tag key–value pairs are not case sensitive, but case is preserved. This +// means that you cannot have separate Department and department tag keys. Assume +// that the user that you are federating has the Department=Marketing tag and +// you pass the department=engineering session tag. Department and department +// are not saved as separate tags, and the session tag passed in the request +// takes precedence over the user tag. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about @@ -888,15 +1158,24 @@ func (c *STS) GetFederationTokenRequest(input *GetFederationTokenInput) (req *re // message describes the specific error. // // * ErrCodePackedPolicyTooLargeException "PackedPolicyTooLarge" -// The request was rejected because the policy document was too large. The error -// message describes how big the policy document is, in packed form, as a percentage -// of what the API allows. +// The request was rejected because the total packed size of the session policies +// and session tags combined was too large. An AWS conversion compresses the +// session policy document, session policy ARNs, and session tags into a packed +// binary format that has a separate limit. The error message indicates by percentage +// how close the policies and tags are to the upper size limit. For more information, +// see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// in the IAM User Guide. +// +// You could receive this error even though you meet other defined session policy +// and session tag limits. For more information, see IAM and STS Entity Character +// Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// in the IAM User Guide. // // * ErrCodeRegionDisabledException "RegionDisabledException" // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating -// and Deactivating AWS STS in an AWS Region (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. // // See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetFederationToken @@ -925,8 +1204,8 @@ const opGetSessionToken = "GetSessionToken" // GetSessionTokenRequest generates a "aws/request.Request" representing the // client's request for the GetSessionToken operation. The "output" return -// value will be populated with the request's response once the request complets -// successfuly. +// value will be populated with the request's response once the request completes +// successfully. // // Use "Send" method on the returned Request to send the API call to the service. // the "output" return value is not valid until after Send returns without error. @@ -968,48 +1247,51 @@ func (c *STS) GetSessionTokenRequest(input *GetSessionTokenInput) (req *request. // Returns a set of temporary credentials for an AWS account or IAM user. The // credentials consist of an access key ID, a secret access key, and a security // token. Typically, you use GetSessionToken if you want to use MFA to protect -// programmatic calls to specific AWS APIs like Amazon EC2 StopInstances. MFA-enabled -// IAM users would need to call GetSessionToken and submit an MFA code that -// is associated with their MFA device. Using the temporary security credentials -// that are returned from the call, IAM users can then make programmatic calls -// to APIs that require MFA authentication. If you do not supply a correct MFA -// code, then the API returns an access denied error. For a comparison of GetSessionToken -// with the other APIs that produce temporary credentials, see Requesting Temporary -// Security Credentials (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) -// and Comparing the AWS STS APIs (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) +// programmatic calls to specific AWS API operations like Amazon EC2 StopInstances. +// MFA-enabled IAM users would need to call GetSessionToken and submit an MFA +// code that is associated with their MFA device. Using the temporary security +// credentials that are returned from the call, IAM users can then make programmatic +// calls to API operations that require MFA authentication. If you do not supply +// a correct MFA code, then the API returns an access denied error. For a comparison +// of GetSessionToken with the other API operations that produce temporary credentials, +// see Requesting Temporary Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html) +// and Comparing the AWS STS API operations (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#stsapi_comparison) // in the IAM User Guide. // -// The GetSessionToken action must be called by using the long-term AWS security -// credentials of the AWS account or an IAM user. Credentials that are created -// by IAM users are valid for the duration that you specify, from 900 seconds -// (15 minutes) up to a maximum of 129600 seconds (36 hours), with a default -// of 43200 seconds (12 hours); credentials that are created by using account -// credentials can range from 900 seconds (15 minutes) up to a maximum of 3600 -// seconds (1 hour), with a default of 1 hour. +// Session Duration +// +// The GetSessionToken operation must be called by using the long-term AWS security +// credentials of the AWS account root user or an IAM user. Credentials that +// are created by IAM users are valid for the duration that you specify. This +// duration can range from 900 seconds (15 minutes) up to a maximum of 129,600 +// seconds (36 hours), with a default of 43,200 seconds (12 hours). Credentials +// based on account credentials can range from 900 seconds (15 minutes) up to +// 3,600 seconds (1 hour), with a default of 1 hour. +// +// Permissions // // The temporary security credentials created by GetSessionToken can be used // to make API calls to any AWS service with the following exceptions: // -// * You cannot call any IAM APIs unless MFA authentication information is -// included in the request. +// * You cannot call any IAM API operations unless MFA authentication information +// is included in the request. // -// * You cannot call any STS API exceptAssumeRole or GetCallerIdentity. +// * You cannot call any STS API except AssumeRole or GetCallerIdentity. // -// We recommend that you do not call GetSessionToken with root account credentials. -// Instead, follow our best practices (http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#create-iam-users) +// We recommend that you do not call GetSessionToken with AWS account root user +// credentials. Instead, follow our best practices (https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#create-iam-users) // by creating one or more IAM users, giving them the necessary permissions, // and using IAM users for everyday interaction with AWS. // -// The permissions associated with the temporary security credentials returned -// by GetSessionToken are based on the permissions associated with account or -// IAM user whose credentials are used to call the action. If GetSessionToken -// is called using root account credentials, the temporary credentials have -// root account permissions. Similarly, if GetSessionToken is called using the -// credentials of an IAM user, the temporary credentials have the same permissions -// as the IAM user. +// The credentials that are returned by GetSessionToken are based on permissions +// associated with the user whose credentials were used to call the operation. +// If GetSessionToken is called using AWS account root user credentials, the +// temporary credentials have root user permissions. Similarly, if GetSessionToken +// is called using the credentials of an IAM user, the temporary credentials +// have the same permissions as the IAM user. // // For more information about using GetSessionToken to create temporary credentials, -// go to Temporary Credentials for Users in Untrusted Environments (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#api_getsessiontoken) +// go to Temporary Credentials for Users in Untrusted Environments (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html#api_getsessiontoken) // in the IAM User Guide. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions @@ -1024,7 +1306,7 @@ func (c *STS) GetSessionTokenRequest(input *GetSessionTokenInput) (req *request. // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating -// and Deactivating AWS STS in an AWS Region (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. // // See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetSessionToken @@ -1049,64 +1331,97 @@ func (c *STS) GetSessionTokenWithContext(ctx aws.Context, input *GetSessionToken return out, req.Send() } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleRequest type AssumeRoleInput struct { _ struct{} `type:"structure"` // The duration, in seconds, of the role session. The value can range from 900 - // seconds (15 minutes) to 3600 seconds (1 hour). By default, the value is set - // to 3600 seconds. - // - // This is separate from the duration of a console session that you might request - // using the returned credentials. The request to the federation endpoint for - // a console sign-in token takes a SessionDuration parameter that specifies - // the maximum length of the console session, separately from the DurationSeconds - // parameter on this API. For more information, see Creating a URL that Enables - // Federated Users to Access the AWS Management Console (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) + // seconds (15 minutes) up to the maximum session duration setting for the role. + // This setting can have a value from 1 hour to 12 hours. If you specify a value + // higher than this setting, the operation fails. For example, if you specify + // a session duration of 12 hours, but your administrator set the maximum session + // duration to 6 hours, your operation fails. To learn how to view the maximum + // value for your role, see View the Maximum Session Duration Setting for a + // Role (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session) + // in the IAM User Guide. + // + // By default, the value is set to 3600 seconds. + // + // The DurationSeconds parameter is separate from the duration of a console + // session that you might request using the returned credentials. The request + // to the federation endpoint for a console sign-in token takes a SessionDuration + // parameter that specifies the maximum length of the console session. For more + // information, see Creating a URL that Enables Federated Users to Access the + // AWS Management Console (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) // in the IAM User Guide. DurationSeconds *int64 `min:"900" type:"integer"` - // A unique identifier that is used by third parties when assuming roles in - // their customers' accounts. For each role that the third party can assume, - // they should instruct their customers to ensure the role's trust policy checks - // for the external ID that the third party generated. Each time the third party - // assumes the role, they should pass the customer's external ID. The external - // ID is useful in order to help third parties bind a role to the customer who - // created it. For more information about the external ID, see How to Use an - // External ID When Granting Access to Your AWS Resources to a Third Party (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html) + // A unique identifier that might be required when you assume a role in another + // account. If the administrator of the account to which the role belongs provided + // you with an external ID, then provide that value in the ExternalId parameter. + // This value can be any string, such as a passphrase or account number. A cross-account + // role is usually set up to trust everyone in an account. Therefore, the administrator + // of the trusting account might send an external ID to the administrator of + // the trusted account. That way, only someone with the ID can assume the role, + // rather than everyone in the account. For more information about the external + // ID, see How to Use an External ID When Granting Access to Your AWS Resources + // to a Third Party (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html) // in the IAM User Guide. // - // The regex used to validated this parameter is a string of characters consisting + // The regex used to validate this parameter is a string of characters consisting // of upper- and lower-case alphanumeric characters with no spaces. You can // also include underscores or any of the following characters: =,.@:/- ExternalId *string `min:"2" type:"string"` - // An IAM policy in JSON format. - // - // This parameter is optional. If you pass a policy, the temporary security - // credentials that are returned by the operation have the permissions that - // are allowed by both (the intersection of) the access policy of the role that - // is being assumed, and the policy that you pass. This gives you a way to further - // restrict the permissions for the resulting temporary security credentials. - // You cannot use the passed policy to grant permissions that are in excess - // of those allowed by the access policy of the role that is being assumed. - // For more information, see Permissions for AssumeRole, AssumeRoleWithSAML, - // and AssumeRoleWithWebIdentity (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_assumerole.html) + // An IAM policy in JSON format that you want to use as an inline session policy. + // + // This parameter is optional. Passing policies to this operation returns new + // temporary credentials. The resulting session's permissions are the intersection + // of the role's identity-based policy and the session policies. You can use + // the role's temporary credentials in subsequent AWS API calls to access resources + // in the account that owns the role. You cannot use session policies to grant + // more permissions than those allowed by the identity-based policy of the role + // that is being assumed. For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. // - // The format for this parameter, as described by its regex pattern, is a string - // of characters up to 2048 characters in length. The characters can be any - // ASCII character from the space character to the end of the valid character - // list (\u0020-\u00FF). It can also include the tab (\u0009), linefeed (\u000A), + // The plain text that you use for both inline and managed session policies + // can't exceed 2,048 characters. The JSON policy characters can be any ASCII + // character from the space character to the end of the valid character list + // (\u0020 through \u00FF). It can also include the tab (\u0009), linefeed (\u000A), // and carriage return (\u000D) characters. // - // The policy plain text must be 2048 bytes or shorter. However, an internal - // conversion compresses it into a packed binary format with a separate limit. - // The PackedPolicySize response element indicates by percentage how close to - // the upper size limit the policy is, with 100% equaling the maximum allowed - // size. + // An AWS conversion compresses the passed session policies and session tags + // into a packed binary format that has a separate limit. Your request can fail + // for this limit even if your plain text meets the other requirements. The + // PackedPolicySize response element indicates by percentage how close the policies + // and tags for your request are to the upper size limit. Policy *string `min:"1" type:"string"` + // The Amazon Resource Names (ARNs) of the IAM managed policies that you want + // to use as managed session policies. The policies must exist in the same account + // as the role. + // + // This parameter is optional. You can provide up to 10 managed policy ARNs. + // However, the plain text that you use for both inline and managed session + // policies can't exceed 2,048 characters. For more information about ARNs, + // see Amazon Resource Names (ARNs) and AWS Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the AWS General Reference. + // + // An AWS conversion compresses the passed session policies and session tags + // into a packed binary format that has a separate limit. Your request can fail + // for this limit even if your plain text meets the other requirements. The + // PackedPolicySize response element indicates by percentage how close the policies + // and tags for your request are to the upper size limit. + // + // Passing policies to this operation returns new temporary credentials. The + // resulting session's permissions are the intersection of the role's identity-based + // policy and the session policies. You can use the role's temporary credentials + // in subsequent AWS API calls to access resources in the account that owns + // the role. You cannot use session policies to grant more permissions than + // those allowed by the identity-based policy of the role that is being assumed. + // For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // in the IAM User Guide. + PolicyArns []*PolicyDescriptorType `type:"list"` + // The Amazon Resource Name (ARN) of the role to assume. // // RoleArn is a required field @@ -1119,8 +1434,8 @@ type AssumeRoleInput struct { // scenarios, the role session name is visible to, and can be logged by the // account that owns the role. The role session name is also used in the ARN // of the assumed role principal. This means that subsequent cross-account API - // requests using the temporary security credentials will expose the role session - // name to the external account in their CloudTrail logs. + // requests that use the temporary security credentials will expose the role + // session name to the external account in their AWS CloudTrail logs. // // The regex used to validate this parameter is a string of characters consisting // of upper- and lower-case alphanumeric characters with no spaces. You can @@ -1140,6 +1455,41 @@ type AssumeRoleInput struct { // also include underscores or any of the following characters: =,.@- SerialNumber *string `min:"9" type:"string"` + // A list of session tags that you want to pass. Each session tag consists of + // a key name and an associated value. For more information about session tags, + // see Tagging AWS STS Sessions (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) + // in the IAM User Guide. + // + // This parameter is optional. You can pass up to 50 session tags. The plain + // text session tag keys can’t exceed 128 characters, and the values can’t + // exceed 256 characters. For these and additional limits, see IAM and STS Character + // Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html#reference_iam-limits-entity-length) + // in the IAM User Guide. + // + // An AWS conversion compresses the passed session policies and session tags + // into a packed binary format that has a separate limit. Your request can fail + // for this limit even if your plain text meets the other requirements. The + // PackedPolicySize response element indicates by percentage how close the policies + // and tags for your request are to the upper size limit. + // + // You can pass a session tag with the same key as a tag that is already attached + // to the role. When you do, session tags override a role tag with the same + // key. + // + // Tag key–value pairs are not case sensitive, but case is preserved. This + // means that you cannot have separate Department and department tag keys. Assume + // that the role has the Department=Marketing tag and you pass the department=engineering + // session tag. Department and department are not saved as separate tags, and + // the session tag passed in the request takes precedence over the role tag. + // + // Additionally, if you used temporary credentials to perform this operation, + // the new session inherits any transitive session tags from the calling session. + // If you pass a session tag with the same key as an inherited tag, the operation + // fails. To view the inherited tags for a session, see the AWS CloudTrail logs. + // For more information, see Viewing Session Tags in CloudTrail (https://docs.aws.amazon.com/IAM/latest/UserGuide/session-tags.html#id_session-tags_ctlogs) + // in the IAM User Guide. + Tags []*Tag `type:"list"` + // The value provided by the MFA device, if the trust policy of the role being // assumed requires MFA (that is, if the policy includes a condition that tests // for MFA). If the role being assumed requires MFA and if the TokenCode value @@ -1148,6 +1498,19 @@ type AssumeRoleInput struct { // The format for this parameter, as described by its regex pattern, is a sequence // of six numeric digits. TokenCode *string `min:"6" type:"string"` + + // A list of keys for session tags that you want to set as transitive. If you + // set a tag key as transitive, the corresponding key and value passes to subsequent + // sessions in a role chain. For more information, see Chaining Roles with Session + // Tags (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining) + // in the IAM User Guide. + // + // This parameter is optional. When you set session tags as transitive, the + // session policy and session tags packed binary limit is not affected. + // + // If you choose not to specify a transitive tag key, then no tags are passed + // from this session to any subsequent sessions. + TransitiveTagKeys []*string `type:"list"` } // String returns the string representation @@ -1190,6 +1553,26 @@ func (s *AssumeRoleInput) Validate() error { if s.TokenCode != nil && len(*s.TokenCode) < 6 { invalidParams.Add(request.NewErrParamMinLen("TokenCode", 6)) } + if s.PolicyArns != nil { + for i, v := range s.PolicyArns { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "PolicyArns", i), err.(request.ErrInvalidParams)) + } + } + } + if s.Tags != nil { + for i, v := range s.Tags { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "Tags", i), err.(request.ErrInvalidParams)) + } + } + } if invalidParams.Len() > 0 { return invalidParams @@ -1215,6 +1598,12 @@ func (s *AssumeRoleInput) SetPolicy(v string) *AssumeRoleInput { return s } +// SetPolicyArns sets the PolicyArns field's value. +func (s *AssumeRoleInput) SetPolicyArns(v []*PolicyDescriptorType) *AssumeRoleInput { + s.PolicyArns = v + return s +} + // SetRoleArn sets the RoleArn field's value. func (s *AssumeRoleInput) SetRoleArn(v string) *AssumeRoleInput { s.RoleArn = &v @@ -1233,15 +1622,26 @@ func (s *AssumeRoleInput) SetSerialNumber(v string) *AssumeRoleInput { return s } +// SetTags sets the Tags field's value. +func (s *AssumeRoleInput) SetTags(v []*Tag) *AssumeRoleInput { + s.Tags = v + return s +} + // SetTokenCode sets the TokenCode field's value. func (s *AssumeRoleInput) SetTokenCode(v string) *AssumeRoleInput { s.TokenCode = &v return s } +// SetTransitiveTagKeys sets the TransitiveTagKeys field's value. +func (s *AssumeRoleInput) SetTransitiveTagKeys(v []*string) *AssumeRoleInput { + s.TransitiveTagKeys = v + return s +} + // Contains the response to a successful AssumeRole request, including temporary // AWS credentials that can be used to make AWS requests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleResponse type AssumeRoleOutput struct { _ struct{} `type:"structure"` @@ -1255,15 +1655,14 @@ type AssumeRoleOutput struct { // The temporary security credentials, which include an access key ID, a secret // access key, and a security (or session) token. // - // Note: The size of the security token that STS APIs return is not fixed. We - // strongly recommend that you make no assumptions about the maximum size. As - // of this writing, the typical size is less than 4096 bytes, but that can vary. - // Also, future updates to AWS might require larger sizes. + // The size of the security token that STS API operations return is not fixed. + // We strongly recommend that you make no assumptions about the maximum size. Credentials *Credentials `type:"structure"` - // A percentage value that indicates the size of the policy in packed form. - // The service rejects any policy with a packed size greater than 100 percent, - // which means the policy exceeded the allowed space. + // A percentage value that indicates the packed size of the session policies + // and session tags combined passed in the request. The request fails if the + // packed size is greater than 100 percent, which means the policies and tags + // exceeded the allowed space. PackedPolicySize *int64 `type:"integer"` } @@ -1295,51 +1694,83 @@ func (s *AssumeRoleOutput) SetPackedPolicySize(v int64) *AssumeRoleOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithSAMLRequest type AssumeRoleWithSAMLInput struct { _ struct{} `type:"structure"` - // The duration, in seconds, of the role session. The value can range from 900 - // seconds (15 minutes) to 3600 seconds (1 hour). By default, the value is set - // to 3600 seconds. An expiration can also be specified in the SAML authentication - // response's SessionNotOnOrAfter value. The actual expiration time is whichever - // value is shorter. - // - // This is separate from the duration of a console session that you might request - // using the returned credentials. The request to the federation endpoint for - // a console sign-in token takes a SessionDuration parameter that specifies - // the maximum length of the console session, separately from the DurationSeconds - // parameter on this API. For more information, see Enabling SAML 2.0 Federated - // Users to Access the AWS Management Console (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-saml.html) + // The duration, in seconds, of the role session. Your role session lasts for + // the duration that you specify for the DurationSeconds parameter, or until + // the time specified in the SAML authentication response's SessionNotOnOrAfter + // value, whichever is shorter. You can provide a DurationSeconds value from + // 900 seconds (15 minutes) up to the maximum session duration setting for the + // role. This setting can have a value from 1 hour to 12 hours. If you specify + // a value higher than this setting, the operation fails. For example, if you + // specify a session duration of 12 hours, but your administrator set the maximum + // session duration to 6 hours, your operation fails. To learn how to view the + // maximum value for your role, see View the Maximum Session Duration Setting + // for a Role (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session) + // in the IAM User Guide. + // + // By default, the value is set to 3600 seconds. + // + // The DurationSeconds parameter is separate from the duration of a console + // session that you might request using the returned credentials. The request + // to the federation endpoint for a console sign-in token takes a SessionDuration + // parameter that specifies the maximum length of the console session. For more + // information, see Creating a URL that Enables Federated Users to Access the + // AWS Management Console (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) // in the IAM User Guide. DurationSeconds *int64 `min:"900" type:"integer"` - // An IAM policy in JSON format. - // - // The policy parameter is optional. If you pass a policy, the temporary security - // credentials that are returned by the operation have the permissions that - // are allowed by both the access policy of the role that is being assumed, - // and the policy that you pass. This gives you a way to further restrict the - // permissions for the resulting temporary security credentials. You cannot - // use the passed policy to grant permissions that are in excess of those allowed - // by the access policy of the role that is being assumed. For more information, - // Permissions for AssumeRole, AssumeRoleWithSAML, and AssumeRoleWithWebIdentity - // (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_assumerole.html) + // An IAM policy in JSON format that you want to use as an inline session policy. + // + // This parameter is optional. Passing policies to this operation returns new + // temporary credentials. The resulting session's permissions are the intersection + // of the role's identity-based policy and the session policies. You can use + // the role's temporary credentials in subsequent AWS API calls to access resources + // in the account that owns the role. You cannot use session policies to grant + // more permissions than those allowed by the identity-based policy of the role + // that is being assumed. For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. // - // The format for this parameter, as described by its regex pattern, is a string - // of characters up to 2048 characters in length. The characters can be any - // ASCII character from the space character to the end of the valid character - // list (\u0020-\u00FF). It can also include the tab (\u0009), linefeed (\u000A), + // The plain text that you use for both inline and managed session policies + // can't exceed 2,048 characters. The JSON policy characters can be any ASCII + // character from the space character to the end of the valid character list + // (\u0020 through \u00FF). It can also include the tab (\u0009), linefeed (\u000A), // and carriage return (\u000D) characters. // - // The policy plain text must be 2048 bytes or shorter. However, an internal - // conversion compresses it into a packed binary format with a separate limit. - // The PackedPolicySize response element indicates by percentage how close to - // the upper size limit the policy is, with 100% equaling the maximum allowed - // size. + // An AWS conversion compresses the passed session policies and session tags + // into a packed binary format that has a separate limit. Your request can fail + // for this limit even if your plain text meets the other requirements. The + // PackedPolicySize response element indicates by percentage how close the policies + // and tags for your request are to the upper size limit. Policy *string `min:"1" type:"string"` + // The Amazon Resource Names (ARNs) of the IAM managed policies that you want + // to use as managed session policies. The policies must exist in the same account + // as the role. + // + // This parameter is optional. You can provide up to 10 managed policy ARNs. + // However, the plain text that you use for both inline and managed session + // policies can't exceed 2,048 characters. For more information about ARNs, + // see Amazon Resource Names (ARNs) and AWS Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the AWS General Reference. + // + // An AWS conversion compresses the passed session policies and session tags + // into a packed binary format that has a separate limit. Your request can fail + // for this limit even if your plain text meets the other requirements. The + // PackedPolicySize response element indicates by percentage how close the policies + // and tags for your request are to the upper size limit. + // + // Passing policies to this operation returns new temporary credentials. The + // resulting session's permissions are the intersection of the role's identity-based + // policy and the session policies. You can use the role's temporary credentials + // in subsequent AWS API calls to access resources in the account that owns + // the role. You cannot use session policies to grant more permissions than + // those allowed by the identity-based policy of the role that is being assumed. + // For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // in the IAM User Guide. + PolicyArns []*PolicyDescriptorType `type:"list"` + // The Amazon Resource Name (ARN) of the SAML provider in IAM that describes // the IdP. // @@ -1353,8 +1784,8 @@ type AssumeRoleWithSAMLInput struct { // The base-64 encoded SAML authentication response provided by the IdP. // - // For more information, see Configuring a Relying Party and Adding Claims (http://docs.aws.amazon.com/IAM/latest/UserGuide/create-role-saml-IdP-tasks.html) - // in the Using IAM guide. + // For more information, see Configuring a Relying Party and Adding Claims (https://docs.aws.amazon.com/IAM/latest/UserGuide/create-role-saml-IdP-tasks.html) + // in the IAM User Guide. // // SAMLAssertion is a required field SAMLAssertion *string `min:"4" type:"string" required:"true"` @@ -1397,6 +1828,16 @@ func (s *AssumeRoleWithSAMLInput) Validate() error { if s.SAMLAssertion != nil && len(*s.SAMLAssertion) < 4 { invalidParams.Add(request.NewErrParamMinLen("SAMLAssertion", 4)) } + if s.PolicyArns != nil { + for i, v := range s.PolicyArns { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "PolicyArns", i), err.(request.ErrInvalidParams)) + } + } + } if invalidParams.Len() > 0 { return invalidParams @@ -1416,6 +1857,12 @@ func (s *AssumeRoleWithSAMLInput) SetPolicy(v string) *AssumeRoleWithSAMLInput { return s } +// SetPolicyArns sets the PolicyArns field's value. +func (s *AssumeRoleWithSAMLInput) SetPolicyArns(v []*PolicyDescriptorType) *AssumeRoleWithSAMLInput { + s.PolicyArns = v + return s +} + // SetPrincipalArn sets the PrincipalArn field's value. func (s *AssumeRoleWithSAMLInput) SetPrincipalArn(v string) *AssumeRoleWithSAMLInput { s.PrincipalArn = &v @@ -1436,7 +1883,6 @@ func (s *AssumeRoleWithSAMLInput) SetSAMLAssertion(v string) *AssumeRoleWithSAML // Contains the response to a successful AssumeRoleWithSAML request, including // temporary AWS credentials that can be used to make AWS requests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithSAMLResponse type AssumeRoleWithSAMLOutput struct { _ struct{} `type:"structure"` @@ -1451,10 +1897,8 @@ type AssumeRoleWithSAMLOutput struct { // The temporary security credentials, which include an access key ID, a secret // access key, and a security (or session) token. // - // Note: The size of the security token that STS APIs return is not fixed. We - // strongly recommend that you make no assumptions about the maximum size. As - // of this writing, the typical size is less than 4096 bytes, but that can vary. - // Also, future updates to AWS might require larger sizes. + // The size of the security token that STS API operations return is not fixed. + // We strongly recommend that you make no assumptions about the maximum size. Credentials *Credentials `type:"structure"` // The value of the Issuer element of the SAML assertion. @@ -1471,9 +1915,10 @@ type AssumeRoleWithSAMLOutput struct { // ) ) NameQualifier *string `type:"string"` - // A percentage value that indicates the size of the policy in packed form. - // The service rejects any policy with a packed size greater than 100 percent, - // which means the policy exceeded the allowed space. + // A percentage value that indicates the packed size of the session policies + // and session tags combined passed in the request. The request fails if the + // packed size is greater than 100 percent, which means the policies and tags + // exceeded the allowed space. PackedPolicySize *int64 `type:"integer"` // The value of the NameID element in the Subject element of the SAML assertion. @@ -1548,48 +1993,80 @@ func (s *AssumeRoleWithSAMLOutput) SetSubjectType(v string) *AssumeRoleWithSAMLO return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithWebIdentityRequest type AssumeRoleWithWebIdentityInput struct { _ struct{} `type:"structure"` // The duration, in seconds, of the role session. The value can range from 900 - // seconds (15 minutes) to 3600 seconds (1 hour). By default, the value is set - // to 3600 seconds. - // - // This is separate from the duration of a console session that you might request - // using the returned credentials. The request to the federation endpoint for - // a console sign-in token takes a SessionDuration parameter that specifies - // the maximum length of the console session, separately from the DurationSeconds - // parameter on this API. For more information, see Creating a URL that Enables - // Federated Users to Access the AWS Management Console (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) + // seconds (15 minutes) up to the maximum session duration setting for the role. + // This setting can have a value from 1 hour to 12 hours. If you specify a value + // higher than this setting, the operation fails. For example, if you specify + // a session duration of 12 hours, but your administrator set the maximum session + // duration to 6 hours, your operation fails. To learn how to view the maximum + // value for your role, see View the Maximum Session Duration Setting for a + // Role (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html#id_roles_use_view-role-max-session) + // in the IAM User Guide. + // + // By default, the value is set to 3600 seconds. + // + // The DurationSeconds parameter is separate from the duration of a console + // session that you might request using the returned credentials. The request + // to the federation endpoint for a console sign-in token takes a SessionDuration + // parameter that specifies the maximum length of the console session. For more + // information, see Creating a URL that Enables Federated Users to Access the + // AWS Management Console (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_enable-console-custom-url.html) // in the IAM User Guide. DurationSeconds *int64 `min:"900" type:"integer"` - // An IAM policy in JSON format. + // An IAM policy in JSON format that you want to use as an inline session policy. // - // The policy parameter is optional. If you pass a policy, the temporary security - // credentials that are returned by the operation have the permissions that - // are allowed by both the access policy of the role that is being assumed, - // and the policy that you pass. This gives you a way to further restrict the - // permissions for the resulting temporary security credentials. You cannot - // use the passed policy to grant permissions that are in excess of those allowed - // by the access policy of the role that is being assumed. For more information, - // see Permissions for AssumeRoleWithWebIdentity (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_assumerole.html) + // This parameter is optional. Passing policies to this operation returns new + // temporary credentials. The resulting session's permissions are the intersection + // of the role's identity-based policy and the session policies. You can use + // the role's temporary credentials in subsequent AWS API calls to access resources + // in the account that owns the role. You cannot use session policies to grant + // more permissions than those allowed by the identity-based policy of the role + // that is being assumed. For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) // in the IAM User Guide. // - // The format for this parameter, as described by its regex pattern, is a string - // of characters up to 2048 characters in length. The characters can be any - // ASCII character from the space character to the end of the valid character - // list (\u0020-\u00FF). It can also include the tab (\u0009), linefeed (\u000A), + // The plain text that you use for both inline and managed session policies + // can't exceed 2,048 characters. The JSON policy characters can be any ASCII + // character from the space character to the end of the valid character list + // (\u0020 through \u00FF). It can also include the tab (\u0009), linefeed (\u000A), // and carriage return (\u000D) characters. // - // The policy plain text must be 2048 bytes or shorter. However, an internal - // conversion compresses it into a packed binary format with a separate limit. - // The PackedPolicySize response element indicates by percentage how close to - // the upper size limit the policy is, with 100% equaling the maximum allowed - // size. + // An AWS conversion compresses the passed session policies and session tags + // into a packed binary format that has a separate limit. Your request can fail + // for this limit even if your plain text meets the other requirements. The + // PackedPolicySize response element indicates by percentage how close the policies + // and tags for your request are to the upper size limit. Policy *string `min:"1" type:"string"` + // The Amazon Resource Names (ARNs) of the IAM managed policies that you want + // to use as managed session policies. The policies must exist in the same account + // as the role. + // + // This parameter is optional. You can provide up to 10 managed policy ARNs. + // However, the plain text that you use for both inline and managed session + // policies can't exceed 2,048 characters. For more information about ARNs, + // see Amazon Resource Names (ARNs) and AWS Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the AWS General Reference. + // + // An AWS conversion compresses the passed session policies and session tags + // into a packed binary format that has a separate limit. Your request can fail + // for this limit even if your plain text meets the other requirements. The + // PackedPolicySize response element indicates by percentage how close the policies + // and tags for your request are to the upper size limit. + // + // Passing policies to this operation returns new temporary credentials. The + // resulting session's permissions are the intersection of the role's identity-based + // policy and the session policies. You can use the role's temporary credentials + // in subsequent AWS API calls to access resources in the account that owns + // the role. You cannot use session policies to grant more permissions than + // those allowed by the identity-based policy of the role that is being assumed. + // For more information, see Session Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // in the IAM User Guide. + PolicyArns []*PolicyDescriptorType `type:"list"` + // The fully qualified host component of the domain name of the identity provider. // // Specify this value only for OAuth 2.0 access tokens. Currently www.amazon.com @@ -1666,6 +2143,16 @@ func (s *AssumeRoleWithWebIdentityInput) Validate() error { if s.WebIdentityToken != nil && len(*s.WebIdentityToken) < 4 { invalidParams.Add(request.NewErrParamMinLen("WebIdentityToken", 4)) } + if s.PolicyArns != nil { + for i, v := range s.PolicyArns { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "PolicyArns", i), err.(request.ErrInvalidParams)) + } + } + } if invalidParams.Len() > 0 { return invalidParams @@ -1685,6 +2172,12 @@ func (s *AssumeRoleWithWebIdentityInput) SetPolicy(v string) *AssumeRoleWithWebI return s } +// SetPolicyArns sets the PolicyArns field's value. +func (s *AssumeRoleWithWebIdentityInput) SetPolicyArns(v []*PolicyDescriptorType) *AssumeRoleWithWebIdentityInput { + s.PolicyArns = v + return s +} + // SetProviderId sets the ProviderId field's value. func (s *AssumeRoleWithWebIdentityInput) SetProviderId(v string) *AssumeRoleWithWebIdentityInput { s.ProviderId = &v @@ -1711,7 +2204,6 @@ func (s *AssumeRoleWithWebIdentityInput) SetWebIdentityToken(v string) *AssumeRo // Contains the response to a successful AssumeRoleWithWebIdentity request, // including temporary AWS credentials that can be used to make AWS requests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumeRoleWithWebIdentityResponse type AssumeRoleWithWebIdentityOutput struct { _ struct{} `type:"structure"` @@ -1730,19 +2222,18 @@ type AssumeRoleWithWebIdentityOutput struct { // The temporary security credentials, which include an access key ID, a secret // access key, and a security token. // - // Note: The size of the security token that STS APIs return is not fixed. We - // strongly recommend that you make no assumptions about the maximum size. As - // of this writing, the typical size is less than 4096 bytes, but that can vary. - // Also, future updates to AWS might require larger sizes. + // The size of the security token that STS API operations return is not fixed. + // We strongly recommend that you make no assumptions about the maximum size. Credentials *Credentials `type:"structure"` - // A percentage value that indicates the size of the policy in packed form. - // The service rejects any policy with a packed size greater than 100 percent, - // which means the policy exceeded the allowed space. + // A percentage value that indicates the packed size of the session policies + // and session tags combined passed in the request. The request fails if the + // packed size is greater than 100 percent, which means the policies and tags + // exceeded the allowed space. PackedPolicySize *int64 `type:"integer"` // The issuing authority of the web identity token presented. For OpenID Connect - // ID Tokens this contains the value of the iss field. For OAuth 2.0 access + // ID tokens, this contains the value of the iss field. For OAuth 2.0 access // tokens, this contains the value of the ProviderId parameter that was passed // in the AssumeRoleWithWebIdentity request. Provider *string `type:"string"` @@ -1804,14 +2295,13 @@ func (s *AssumeRoleWithWebIdentityOutput) SetSubjectFromWebIdentityToken(v strin // The identifiers for the temporary security credentials that the operation // returns. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/AssumedRoleUser type AssumedRoleUser struct { _ struct{} `type:"structure"` // The ARN of the temporary security credentials that are returned from the // AssumeRole action. For more information about ARNs and how to use them in - // policies, see IAM Identifiers (http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html) - // in Using IAM. + // policies, see IAM Identifiers (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html) + // in the IAM User Guide. // // Arn is a required field Arn *string `min:"20" type:"string" required:"true"` @@ -1847,7 +2337,6 @@ func (s *AssumedRoleUser) SetAssumedRoleId(v string) *AssumedRoleUser { } // AWS credentials for API authentication. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/Credentials type Credentials struct { _ struct{} `type:"structure"` @@ -1859,7 +2348,7 @@ type Credentials struct { // The date on which the current credentials expire. // // Expiration is a required field - Expiration *time.Time `type:"timestamp" timestampFormat:"iso8601" required:"true"` + Expiration *time.Time `type:"timestamp" required:"true"` // The secret access key that can be used to sign requests. // @@ -1906,7 +2395,6 @@ func (s *Credentials) SetSessionToken(v string) *Credentials { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/DecodeAuthorizationMessageRequest type DecodeAuthorizationMessageInput struct { _ struct{} `type:"structure"` @@ -1951,7 +2439,6 @@ func (s *DecodeAuthorizationMessageInput) SetEncodedMessage(v string) *DecodeAut // A document that contains additional information about the authorization status // of a request from an encoded message that is returned in response to an AWS // request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/DecodeAuthorizationMessageResponse type DecodeAuthorizationMessageOutput struct { _ struct{} `type:"structure"` @@ -1976,14 +2463,13 @@ func (s *DecodeAuthorizationMessageOutput) SetDecodedMessage(v string) *DecodeAu } // Identifiers for the federated user that is associated with the credentials. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/FederatedUser type FederatedUser struct { _ struct{} `type:"structure"` // The ARN that specifies the federated user that is associated with the credentials. // For more information about ARNs and how to use them in policies, see IAM - // Identifiers (http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html) - // in Using IAM. + // Identifiers (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html) + // in the IAM User Guide. // // Arn is a required field Arn *string `min:"20" type:"string" required:"true"` @@ -2017,7 +2503,73 @@ func (s *FederatedUser) SetFederatedUserId(v string) *FederatedUser { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetCallerIdentityRequest +type GetAccessKeyInfoInput struct { + _ struct{} `type:"structure"` + + // The identifier of an access key. + // + // This parameter allows (through its regex pattern) a string of characters + // that can consist of any upper- or lowercase letter or digit. + // + // AccessKeyId is a required field + AccessKeyId *string `min:"16" type:"string" required:"true"` +} + +// String returns the string representation +func (s GetAccessKeyInfoInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetAccessKeyInfoInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetAccessKeyInfoInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetAccessKeyInfoInput"} + if s.AccessKeyId == nil { + invalidParams.Add(request.NewErrParamRequired("AccessKeyId")) + } + if s.AccessKeyId != nil && len(*s.AccessKeyId) < 16 { + invalidParams.Add(request.NewErrParamMinLen("AccessKeyId", 16)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetAccessKeyId sets the AccessKeyId field's value. +func (s *GetAccessKeyInfoInput) SetAccessKeyId(v string) *GetAccessKeyInfoInput { + s.AccessKeyId = &v + return s +} + +type GetAccessKeyInfoOutput struct { + _ struct{} `type:"structure"` + + // The number used to identify the AWS account. + Account *string `type:"string"` +} + +// String returns the string representation +func (s GetAccessKeyInfoOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetAccessKeyInfoOutput) GoString() string { + return s.String() +} + +// SetAccount sets the Account field's value. +func (s *GetAccessKeyInfoOutput) SetAccount(v string) *GetAccessKeyInfoOutput { + s.Account = &v + return s +} + type GetCallerIdentityInput struct { _ struct{} `type:"structure"` } @@ -2034,7 +2586,6 @@ func (s GetCallerIdentityInput) GoString() string { // Contains the response to a successful GetCallerIdentity request, including // information about the entity making the request. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetCallerIdentityResponse type GetCallerIdentityOutput struct { _ struct{} `type:"structure"` @@ -2046,8 +2597,8 @@ type GetCallerIdentityOutput struct { Arn *string `min:"20" type:"string"` // The unique identifier of the calling entity. The exact value depends on the - // type of entity making the call. The values returned are those listed in the - // aws:userid column in the Principal table (http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html#principaltable) + // type of entity that is making the call. The values returned are those listed + // in the aws:userid column in the Principal table (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html#principaltable) // found on the Policy Variables reference page in the IAM User Guide. UserId *string `type:"string"` } @@ -2080,17 +2631,15 @@ func (s *GetCallerIdentityOutput) SetUserId(v string) *GetCallerIdentityOutput { return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetFederationTokenRequest type GetFederationTokenInput struct { _ struct{} `type:"structure"` // The duration, in seconds, that the session should last. Acceptable durations - // for federation sessions range from 900 seconds (15 minutes) to 129600 seconds - // (36 hours), with 43200 seconds (12 hours) as the default. Sessions obtained - // using AWS account (root) credentials are restricted to a maximum of 3600 + // for federation sessions range from 900 seconds (15 minutes) to 129,600 seconds + // (36 hours), with 43,200 seconds (12 hours) as the default. Sessions obtained + // using AWS account root user credentials are restricted to a maximum of 3,600 // seconds (one hour). If the specified duration is longer than one hour, the - // session obtained by using AWS account (root) credentials defaults to one - // hour. + // session obtained by using root user credentials defaults to one hour. DurationSeconds *int64 `min:"900" type:"integer"` // The name of the federated user. The name is used as an identifier for the @@ -2105,36 +2654,107 @@ type GetFederationTokenInput struct { // Name is a required field Name *string `min:"2" type:"string" required:"true"` - // An IAM policy in JSON format that is passed with the GetFederationToken call - // and evaluated along with the policy or policies that are attached to the - // IAM user whose credentials are used to call GetFederationToken. The passed - // policy is used to scope down the permissions that are available to the IAM - // user, by allowing only a subset of the permissions that are granted to the - // IAM user. The passed policy cannot grant more permissions than those granted - // to the IAM user. The final permissions for the federated user are the most - // restrictive set based on the intersection of the passed policy and the IAM - // user policy. - // - // If you do not pass a policy, the resulting temporary security credentials - // have no effective permissions. The only exception is when the temporary security - // credentials are used to access a resource that has a resource-based policy - // that specifically allows the federated user to access the resource. - // - // The format for this parameter, as described by its regex pattern, is a string - // of characters up to 2048 characters in length. The characters can be any - // ASCII character from the space character to the end of the valid character - // list (\u0020-\u00FF). It can also include the tab (\u0009), linefeed (\u000A), - // and carriage return (\u000D) characters. + // An IAM policy in JSON format that you want to use as an inline session policy. + // + // You must pass an inline or managed session policy (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // to this operation. You can pass a single JSON policy document to use as an + // inline session policy. You can also specify up to 10 managed policies to + // use as managed session policies. + // + // This parameter is optional. However, if you do not pass any session policies, + // then the resulting federated user session has no permissions. // - // The policy plain text must be 2048 bytes or shorter. However, an internal - // conversion compresses it into a packed binary format with a separate limit. - // The PackedPolicySize response element indicates by percentage how close to - // the upper size limit the policy is, with 100% equaling the maximum allowed - // size. + // When you pass session policies, the session permissions are the intersection + // of the IAM user policies and the session policies that you pass. This gives + // you a way to further restrict the permissions for a federated user. You cannot + // use session policies to grant more permissions than those that are defined + // in the permissions policy of the IAM user. For more information, see Session + // Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // in the IAM User Guide. + // + // The resulting credentials can be used to access a resource that has a resource-based + // policy. If that policy specifically references the federated user session + // in the Principal element of the policy, the session has the permissions allowed + // by the policy. These permissions are granted in addition to the permissions + // that are granted by the session policies. // - // For more information about how permissions work, see Permissions for GetFederationToken - // (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_control-access_getfederationtoken.html). + // The plain text that you use for both inline and managed session policies + // can't exceed 2,048 characters. The JSON policy characters can be any ASCII + // character from the space character to the end of the valid character list + // (\u0020 through \u00FF). It can also include the tab (\u0009), linefeed (\u000A), + // and carriage return (\u000D) characters. + // + // An AWS conversion compresses the passed session policies and session tags + // into a packed binary format that has a separate limit. Your request can fail + // for this limit even if your plain text meets the other requirements. The + // PackedPolicySize response element indicates by percentage how close the policies + // and tags for your request are to the upper size limit. Policy *string `min:"1" type:"string"` + + // The Amazon Resource Names (ARNs) of the IAM managed policies that you want + // to use as a managed session policy. The policies must exist in the same account + // as the IAM user that is requesting federated access. + // + // You must pass an inline or managed session policy (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // to this operation. You can pass a single JSON policy document to use as an + // inline session policy. You can also specify up to 10 managed policies to + // use as managed session policies. The plain text that you use for both inline + // and managed session policies can't exceed 2,048 characters. You can provide + // up to 10 managed policy ARNs. For more information about ARNs, see Amazon + // Resource Names (ARNs) and AWS Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the AWS General Reference. + // + // This parameter is optional. However, if you do not pass any session policies, + // then the resulting federated user session has no permissions. + // + // When you pass session policies, the session permissions are the intersection + // of the IAM user policies and the session policies that you pass. This gives + // you a way to further restrict the permissions for a federated user. You cannot + // use session policies to grant more permissions than those that are defined + // in the permissions policy of the IAM user. For more information, see Session + // Policies (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html#policies_session) + // in the IAM User Guide. + // + // The resulting credentials can be used to access a resource that has a resource-based + // policy. If that policy specifically references the federated user session + // in the Principal element of the policy, the session has the permissions allowed + // by the policy. These permissions are granted in addition to the permissions + // that are granted by the session policies. + // + // An AWS conversion compresses the passed session policies and session tags + // into a packed binary format that has a separate limit. Your request can fail + // for this limit even if your plain text meets the other requirements. The + // PackedPolicySize response element indicates by percentage how close the policies + // and tags for your request are to the upper size limit. + PolicyArns []*PolicyDescriptorType `type:"list"` + + // A list of session tags. Each session tag consists of a key name and an associated + // value. For more information about session tags, see Passing Session Tags + // in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) + // in the IAM User Guide. + // + // This parameter is optional. You can pass up to 50 session tags. The plain + // text session tag keys can’t exceed 128 characters and the values can’t + // exceed 256 characters. For these and additional limits, see IAM and STS Character + // Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html#reference_iam-limits-entity-length) + // in the IAM User Guide. + // + // An AWS conversion compresses the passed session policies and session tags + // into a packed binary format that has a separate limit. Your request can fail + // for this limit even if your plain text meets the other requirements. The + // PackedPolicySize response element indicates by percentage how close the policies + // and tags for your request are to the upper size limit. + // + // You can pass a session tag with the same key as a tag that is already attached + // to the user you are federating. When you do, session tags override a user + // tag with the same key. + // + // Tag key–value pairs are not case sensitive, but case is preserved. This + // means that you cannot have separate Department and department tag keys. Assume + // that the role has the Department=Marketing tag and you pass the department=engineering + // session tag. Department and department are not saved as separate tags, and + // the session tag passed in the request takes precedence over the role tag. + Tags []*Tag `type:"list"` } // String returns the string representation @@ -2162,6 +2782,26 @@ func (s *GetFederationTokenInput) Validate() error { if s.Policy != nil && len(*s.Policy) < 1 { invalidParams.Add(request.NewErrParamMinLen("Policy", 1)) } + if s.PolicyArns != nil { + for i, v := range s.PolicyArns { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "PolicyArns", i), err.(request.ErrInvalidParams)) + } + } + } + if s.Tags != nil { + for i, v := range s.Tags { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "Tags", i), err.(request.ErrInvalidParams)) + } + } + } if invalidParams.Len() > 0 { return invalidParams @@ -2187,19 +2827,28 @@ func (s *GetFederationTokenInput) SetPolicy(v string) *GetFederationTokenInput { return s } +// SetPolicyArns sets the PolicyArns field's value. +func (s *GetFederationTokenInput) SetPolicyArns(v []*PolicyDescriptorType) *GetFederationTokenInput { + s.PolicyArns = v + return s +} + +// SetTags sets the Tags field's value. +func (s *GetFederationTokenInput) SetTags(v []*Tag) *GetFederationTokenInput { + s.Tags = v + return s +} + // Contains the response to a successful GetFederationToken request, including // temporary AWS credentials that can be used to make AWS requests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetFederationTokenResponse type GetFederationTokenOutput struct { _ struct{} `type:"structure"` // The temporary security credentials, which include an access key ID, a secret // access key, and a security (or session) token. // - // Note: The size of the security token that STS APIs return is not fixed. We - // strongly recommend that you make no assumptions about the maximum size. As - // of this writing, the typical size is less than 4096 bytes, but that can vary. - // Also, future updates to AWS might require larger sizes. + // The size of the security token that STS API operations return is not fixed. + // We strongly recommend that you make no assumptions about the maximum size. Credentials *Credentials `type:"structure"` // Identifiers for the federated user associated with the credentials (such @@ -2208,9 +2857,10 @@ type GetFederationTokenOutput struct { // an Amazon S3 bucket policy. FederatedUser *FederatedUser `type:"structure"` - // A percentage value indicating the size of the policy in packed form. The - // service rejects policies for which the packed size is greater than 100 percent - // of the allowed value. + // A percentage value that indicates the packed size of the session policies + // and session tags combined passed in the request. The request fails if the + // packed size is greater than 100 percent, which means the policies and tags + // exceeded the allowed space. PackedPolicySize *int64 `type:"integer"` } @@ -2242,16 +2892,15 @@ func (s *GetFederationTokenOutput) SetPackedPolicySize(v int64) *GetFederationTo return s } -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetSessionTokenRequest type GetSessionTokenInput struct { _ struct{} `type:"structure"` // The duration, in seconds, that the credentials should remain valid. Acceptable - // durations for IAM user sessions range from 900 seconds (15 minutes) to 129600 - // seconds (36 hours), with 43200 seconds (12 hours) as the default. Sessions - // for AWS account owners are restricted to a maximum of 3600 seconds (one hour). - // If the duration is longer than one hour, the session for AWS account owners - // defaults to one hour. + // durations for IAM user sessions range from 900 seconds (15 minutes) to 129,600 + // seconds (36 hours), with 43,200 seconds (12 hours) as the default. Sessions + // for AWS account owners are restricted to a maximum of 3,600 seconds (one + // hour). If the duration is longer than one hour, the session for AWS account + // owners defaults to one hour. DurationSeconds *int64 `min:"900" type:"integer"` // The identification number of the MFA device that is associated with the IAM @@ -2262,16 +2911,16 @@ type GetSessionTokenInput struct { // You can find the device for an IAM user by going to the AWS Management Console // and viewing the user's security credentials. // - // The regex used to validated this parameter is a string of characters consisting + // The regex used to validate this parameter is a string of characters consisting // of upper- and lower-case alphanumeric characters with no spaces. You can // also include underscores or any of the following characters: =,.@:/- SerialNumber *string `min:"9" type:"string"` // The value provided by the MFA device, if MFA is required. If any policy requires // the IAM user to submit an MFA code, specify this value. If MFA authentication - // is required, and the user does not provide a code when requesting a set of - // temporary security credentials, the user will receive an "access denied" - // response when requesting resources that require MFA authentication. + // is required, the user must provide a code when requesting a set of temporary + // security credentials. A user who fails to provide the code receives an "access + // denied" response when requesting resources that require MFA authentication. // // The format for this parameter, as described by its regex pattern, is a sequence // of six numeric digits. @@ -2327,17 +2976,14 @@ func (s *GetSessionTokenInput) SetTokenCode(v string) *GetSessionTokenInput { // Contains the response to a successful GetSessionToken request, including // temporary AWS credentials that can be used to make AWS requests. -// See also, https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15/GetSessionTokenResponse type GetSessionTokenOutput struct { _ struct{} `type:"structure"` // The temporary security credentials, which include an access key ID, a secret // access key, and a security (or session) token. // - // Note: The size of the security token that STS APIs return is not fixed. We - // strongly recommend that you make no assumptions about the maximum size. As - // of this writing, the typical size is less than 4096 bytes, but that can vary. - // Also, future updates to AWS might require larger sizes. + // The size of the security token that STS API operations return is not fixed. + // We strongly recommend that you make no assumptions about the maximum size. Credentials *Credentials `type:"structure"` } @@ -2356,3 +3002,114 @@ func (s *GetSessionTokenOutput) SetCredentials(v *Credentials) *GetSessionTokenO s.Credentials = v return s } + +// A reference to the IAM managed policy that is passed as a session policy +// for a role session or a federated user session. +type PolicyDescriptorType struct { + _ struct{} `type:"structure"` + + // The Amazon Resource Name (ARN) of the IAM managed policy to use as a session + // policy for the role. For more information about ARNs, see Amazon Resource + // Names (ARNs) and AWS Service Namespaces (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the AWS General Reference. + Arn *string `locationName:"arn" min:"20" type:"string"` +} + +// String returns the string representation +func (s PolicyDescriptorType) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s PolicyDescriptorType) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *PolicyDescriptorType) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "PolicyDescriptorType"} + if s.Arn != nil && len(*s.Arn) < 20 { + invalidParams.Add(request.NewErrParamMinLen("Arn", 20)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetArn sets the Arn field's value. +func (s *PolicyDescriptorType) SetArn(v string) *PolicyDescriptorType { + s.Arn = &v + return s +} + +// You can pass custom key-value pair attributes when you assume a role or federate +// a user. These are called session tags. You can then use the session tags +// to control access to resources. For more information, see Tagging AWS STS +// Sessions (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) +// in the IAM User Guide. +type Tag struct { + _ struct{} `type:"structure"` + + // The key for a session tag. + // + // You can pass up to 50 session tags. The plain text session tag keys can’t + // exceed 128 characters. For these and additional limits, see IAM and STS Character + // Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html#reference_iam-limits-entity-length) + // in the IAM User Guide. + // + // Key is a required field + Key *string `min:"1" type:"string" required:"true"` + + // The value for a session tag. + // + // You can pass up to 50 session tags. The plain text session tag values can’t + // exceed 256 characters. For these and additional limits, see IAM and STS Character + // Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-limits.html#reference_iam-limits-entity-length) + // in the IAM User Guide. + // + // Value is a required field + Value *string `type:"string" required:"true"` +} + +// String returns the string representation +func (s Tag) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s Tag) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *Tag) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "Tag"} + if s.Key == nil { + invalidParams.Add(request.NewErrParamRequired("Key")) + } + if s.Key != nil && len(*s.Key) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Key", 1)) + } + if s.Value == nil { + invalidParams.Add(request.NewErrParamRequired("Value")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetKey sets the Key field's value. +func (s *Tag) SetKey(v string) *Tag { + s.Key = &v + return s +} + +// SetValue sets the Value field's value. +func (s *Tag) SetValue(v string) *Tag { + s.Value = &v + return s +} diff --git a/vendor/github.com/aws/aws-sdk-go/service/sts/customizations.go b/vendor/github.com/aws/aws-sdk-go/service/sts/customizations.go index 4010cc7fa147d..d5307fcaa0f3a 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/sts/customizations.go +++ b/vendor/github.com/aws/aws-sdk-go/service/sts/customizations.go @@ -3,10 +3,9 @@ package sts import "github.com/aws/aws-sdk-go/aws/request" func init() { - initRequest = func(r *request.Request) { - switch r.Operation.Name { - case opAssumeRoleWithSAML, opAssumeRoleWithWebIdentity: - r.Handlers.Sign.Clear() // these operations are unsigned - } - } + initRequest = customizeRequest +} + +func customizeRequest(r *request.Request) { + r.RetryErrorCodes = append(r.RetryErrorCodes, ErrCodeIDPCommunicationErrorException) } diff --git a/vendor/github.com/aws/aws-sdk-go/service/sts/doc.go b/vendor/github.com/aws/aws-sdk-go/service/sts/doc.go index ef681ab0c63c6..fcb720dcac6dd 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/sts/doc.go +++ b/vendor/github.com/aws/aws-sdk-go/service/sts/doc.go @@ -7,22 +7,14 @@ // request temporary, limited-privilege credentials for AWS Identity and Access // Management (IAM) users or for users that you authenticate (federated users). // This guide provides descriptions of the STS API. For more detailed information -// about using this service, go to Temporary Security Credentials (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html). -// -// As an alternative to using the API, you can use one of the AWS SDKs, which -// consist of libraries and sample code for various programming languages and -// platforms (Java, Ruby, .NET, iOS, Android, etc.). The SDKs provide a convenient -// way to create programmatic access to STS. For example, the SDKs take care -// of cryptographically signing requests, managing errors, and retrying requests -// automatically. For information about the AWS SDKs, including how to download -// and install them, see the Tools for Amazon Web Services page (http://aws.amazon.com/tools/). +// about using this service, go to Temporary Security Credentials (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html). // // For information about setting up signatures and authorization through the -// API, go to Signing AWS API Requests (http://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html) +// API, go to Signing AWS API Requests (https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html) // in the AWS General Reference. For general information about the Query API, -// go to Making Query Requests (http://docs.aws.amazon.com/IAM/latest/UserGuide/IAM_UsingQueryAPI.html) +// go to Making Query Requests (https://docs.aws.amazon.com/IAM/latest/UserGuide/IAM_UsingQueryAPI.html) // in Using IAM. For information about using security tokens with other AWS -// products, go to AWS Services That Work with IAM (http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html) +// products, go to AWS Services That Work with IAM (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html) // in the IAM User Guide. // // If you're new to AWS and need additional technical information about a specific @@ -31,14 +23,38 @@ // // Endpoints // -// The AWS Security Token Service (STS) has a default endpoint of https://sts.amazonaws.com -// that maps to the US East (N. Virginia) region. Additional regions are available -// and are activated by default. For more information, see Activating and Deactivating -// AWS STS in an AWS Region (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// By default, AWS Security Token Service (STS) is available as a global service, +// and all AWS STS requests go to a single endpoint at https://sts.amazonaws.com. +// Global requests map to the US East (N. Virginia) region. AWS recommends using +// Regional AWS STS endpoints instead of the global endpoint to reduce latency, +// build in redundancy, and increase session token validity. For more information, +// see Managing AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) +// in the IAM User Guide. +// +// Most AWS Regions are enabled for operations in all AWS services by default. +// Those Regions are automatically activated for use with AWS STS. Some Regions, +// such as Asia Pacific (Hong Kong), must be manually enabled. To learn more +// about enabling and disabling AWS Regions, see Managing AWS Regions (https://docs.aws.amazon.com/general/latest/gr/rande-manage.html) +// in the AWS General Reference. When you enable these AWS Regions, they are +// automatically activated for use with AWS STS. You cannot activate the STS +// endpoint for a Region that is disabled. Tokens that are valid in all AWS +// Regions are longer than tokens that are valid in Regions that are enabled +// by default. Changing this setting might affect existing systems where you +// temporarily store tokens. For more information, see Managing Global Endpoint +// Session Tokens (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html#sts-regions-manage-tokens) // in the IAM User Guide. // -// For information about STS endpoints, see Regions and Endpoints (http://docs.aws.amazon.com/general/latest/gr/rande.html#sts_region) -// in the AWS General Reference. +// After you activate a Region for use with AWS STS, you can direct AWS STS +// API calls to that Region. AWS STS recommends that you provide both the Region +// and endpoint when you make calls to a Regional endpoint. You can provide +// the Region alone for manually enabled Regions, such as Asia Pacific (Hong +// Kong). In this case, the calls are directed to the STS Regional endpoint. +// However, if you provide the Region alone for Regions enabled by default, +// the calls are directed to the global endpoint of https://sts.amazonaws.com. +// +// To view the list of AWS STS endpoints and whether they are active by default, +// see Writing Code to Use AWS STS Regions (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html#id_credentials_temp_enable-regions_writing_code) +// in the IAM User Guide. // // Recording API requests // @@ -46,8 +62,28 @@ // your AWS account and delivers log files to an Amazon S3 bucket. By using // information collected by CloudTrail, you can determine what requests were // successfully made to STS, who made the request, when it was made, and so -// on. To learn more about CloudTrail, including how to turn it on and find -// your log files, see the AWS CloudTrail User Guide (http://docs.aws.amazon.com/awscloudtrail/latest/userguide/what_is_cloud_trail_top_level.html). +// on. +// +// If you activate AWS STS endpoints in Regions other than the default global +// endpoint, then you must also turn on CloudTrail logging in those Regions. +// This is necessary to record any AWS STS API calls that are made in those +// Regions. For more information, see Turning On CloudTrail in Additional Regions +// (https://docs.aws.amazon.com/awscloudtrail/latest/userguide/aggregating_logs_regions_turn_on_ct.html) +// in the AWS CloudTrail User Guide. +// +// AWS Security Token Service (STS) is a global service with a single endpoint +// at https://sts.amazonaws.com. Calls to this endpoint are logged as calls +// to a global service. However, because this endpoint is physically located +// in the US East (N. Virginia) Region, your logs list us-east-1 as the event +// Region. CloudTrail does not write these logs to the US East (Ohio) Region +// unless you choose to include global service logs in that Region. CloudTrail +// writes calls to all Regional endpoints to their respective Regions. For example, +// calls to sts.us-east-2.amazonaws.com are published to the US East (Ohio) +// Region and calls to sts.eu-central-1.amazonaws.com are published to the EU +// (Frankfurt) Region. +// +// To learn more about CloudTrail, including how to turn it on and find your +// log files, see the AWS CloudTrail User Guide (https://docs.aws.amazon.com/awscloudtrail/latest/userguide/what_is_cloud_trail_top_level.html). // // See https://docs.aws.amazon.com/goto/WebAPI/sts-2011-06-15 for more information on this service. // diff --git a/vendor/github.com/aws/aws-sdk-go/service/sts/errors.go b/vendor/github.com/aws/aws-sdk-go/service/sts/errors.go index e24884ef371a9..a233f542ef293 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/sts/errors.go +++ b/vendor/github.com/aws/aws-sdk-go/service/sts/errors.go @@ -14,11 +14,11 @@ const ( // ErrCodeIDPCommunicationErrorException for service response error code // "IDPCommunicationError". // - // The request could not be fulfilled because the non-AWS identity provider - // (IDP) that was asked to verify the incoming identity token could not be reached. - // This is often a transient error caused by network conditions. Retry the request + // The request could not be fulfilled because the identity provider (IDP) that + // was asked to verify the incoming identity token could not be reached. This + // is often a transient error caused by network conditions. Retry the request // a limited number of times so that you don't exceed the request rate. If the - // error persists, the non-AWS identity provider might be down or not responding. + // error persists, the identity provider might be down or not responding. ErrCodeIDPCommunicationErrorException = "IDPCommunicationError" // ErrCodeIDPRejectedClaimException for service response error code @@ -56,9 +56,18 @@ const ( // ErrCodePackedPolicyTooLargeException for service response error code // "PackedPolicyTooLarge". // - // The request was rejected because the policy document was too large. The error - // message describes how big the policy document is, in packed form, as a percentage - // of what the API allows. + // The request was rejected because the total packed size of the session policies + // and session tags combined was too large. An AWS conversion compresses the + // session policy document, session policy ARNs, and session tags into a packed + // binary format that has a separate limit. The error message indicates by percentage + // how close the policies and tags are to the upper size limit. For more information, + // see Passing Session Tags in STS (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html) + // in the IAM User Guide. + // + // You could receive this error even though you meet other defined session policy + // and session tag limits. For more information, see IAM and STS Entity Character + // Limits (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) + // in the IAM User Guide. ErrCodePackedPolicyTooLargeException = "PackedPolicyTooLarge" // ErrCodeRegionDisabledException for service response error code @@ -67,7 +76,7 @@ const ( // STS is not activated in the requested region for the account that is being // asked to generate credentials. The account administrator must use the IAM // console to activate STS in that region. For more information, see Activating - // and Deactivating AWS STS in an AWS Region (http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) + // and Deactivating AWS STS in an AWS Region (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html) // in the IAM User Guide. ErrCodeRegionDisabledException = "RegionDisabledException" ) diff --git a/vendor/github.com/aws/aws-sdk-go/service/sts/service.go b/vendor/github.com/aws/aws-sdk-go/service/sts/service.go index 1ee5839e0462b..d34a6855331bc 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/sts/service.go +++ b/vendor/github.com/aws/aws-sdk-go/service/sts/service.go @@ -29,8 +29,9 @@ var initRequest func(*request.Request) // Service information constants const ( - ServiceName = "sts" // Service endpoint prefix API calls made to. - EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata. + ServiceName = "sts" // Name of service. + EndpointsID = ServiceName // ID to lookup a service endpoint with. + ServiceID = "STS" // ServiceID is a unique identifier of a specific service. ) // New creates a new instance of the STS client with a session. @@ -38,6 +39,8 @@ const ( // aws.Config parameter to add your extra config. // // Example: +// mySession := session.Must(session.NewSession()) +// // // Create a STS client from just a session. // svc := sts.New(mySession) // @@ -45,18 +48,20 @@ const ( // svc := sts.New(mySession, aws.NewConfig().WithRegion("us-west-2")) func New(p client.ConfigProvider, cfgs ...*aws.Config) *STS { c := p.ClientConfig(EndpointsID, cfgs...) - return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName) + return newClient(*c.Config, c.Handlers, c.PartitionID, c.Endpoint, c.SigningRegion, c.SigningName) } // newClient creates, initializes and returns a new service client instance. -func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *STS { +func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, signingRegion, signingName string) *STS { svc := &STS{ Client: client.New( cfg, metadata.ClientInfo{ ServiceName: ServiceName, + ServiceID: ServiceID, SigningName: signingName, SigningRegion: signingRegion, + PartitionID: partitionID, Endpoint: endpoint, APIVersion: "2011-06-15", }, diff --git a/vendor/github.com/aws/aws-sdk-go/service/sts/stsiface/interface.go b/vendor/github.com/aws/aws-sdk-go/service/sts/stsiface/interface.go new file mode 100644 index 0000000000000..e2e1d6efe55f0 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/sts/stsiface/interface.go @@ -0,0 +1,96 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +// Package stsiface provides an interface to enable mocking the AWS Security Token Service service client +// for testing your code. +// +// It is important to note that this interface will have breaking changes +// when the service model is updated and adds new API operations, paginators, +// and waiters. +package stsiface + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/sts" +) + +// STSAPI provides an interface to enable mocking the +// sts.STS service client's API operation, +// paginators, and waiters. This make unit testing your code that calls out +// to the SDK's service client's calls easier. +// +// The best way to use this interface is so the SDK's service client's calls +// can be stubbed out for unit testing your code with the SDK without needing +// to inject custom request handlers into the SDK's request pipeline. +// +// // myFunc uses an SDK service client to make a request to +// // AWS Security Token Service. +// func myFunc(svc stsiface.STSAPI) bool { +// // Make svc.AssumeRole request +// } +// +// func main() { +// sess := session.New() +// svc := sts.New(sess) +// +// myFunc(svc) +// } +// +// In your _test.go file: +// +// // Define a mock struct to be used in your unit tests of myFunc. +// type mockSTSClient struct { +// stsiface.STSAPI +// } +// func (m *mockSTSClient) AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) { +// // mock response/functionality +// } +// +// func TestMyFunc(t *testing.T) { +// // Setup Test +// mockSvc := &mockSTSClient{} +// +// myfunc(mockSvc) +// +// // Verify myFunc's functionality +// } +// +// It is important to note that this interface will have breaking changes +// when the service model is updated and adds new API operations, paginators, +// and waiters. Its suggested to use the pattern above for testing, or using +// tooling to generate mocks to satisfy the interfaces. +type STSAPI interface { + AssumeRole(*sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error) + AssumeRoleWithContext(aws.Context, *sts.AssumeRoleInput, ...request.Option) (*sts.AssumeRoleOutput, error) + AssumeRoleRequest(*sts.AssumeRoleInput) (*request.Request, *sts.AssumeRoleOutput) + + AssumeRoleWithSAML(*sts.AssumeRoleWithSAMLInput) (*sts.AssumeRoleWithSAMLOutput, error) + AssumeRoleWithSAMLWithContext(aws.Context, *sts.AssumeRoleWithSAMLInput, ...request.Option) (*sts.AssumeRoleWithSAMLOutput, error) + AssumeRoleWithSAMLRequest(*sts.AssumeRoleWithSAMLInput) (*request.Request, *sts.AssumeRoleWithSAMLOutput) + + AssumeRoleWithWebIdentity(*sts.AssumeRoleWithWebIdentityInput) (*sts.AssumeRoleWithWebIdentityOutput, error) + AssumeRoleWithWebIdentityWithContext(aws.Context, *sts.AssumeRoleWithWebIdentityInput, ...request.Option) (*sts.AssumeRoleWithWebIdentityOutput, error) + AssumeRoleWithWebIdentityRequest(*sts.AssumeRoleWithWebIdentityInput) (*request.Request, *sts.AssumeRoleWithWebIdentityOutput) + + DecodeAuthorizationMessage(*sts.DecodeAuthorizationMessageInput) (*sts.DecodeAuthorizationMessageOutput, error) + DecodeAuthorizationMessageWithContext(aws.Context, *sts.DecodeAuthorizationMessageInput, ...request.Option) (*sts.DecodeAuthorizationMessageOutput, error) + DecodeAuthorizationMessageRequest(*sts.DecodeAuthorizationMessageInput) (*request.Request, *sts.DecodeAuthorizationMessageOutput) + + GetAccessKeyInfo(*sts.GetAccessKeyInfoInput) (*sts.GetAccessKeyInfoOutput, error) + GetAccessKeyInfoWithContext(aws.Context, *sts.GetAccessKeyInfoInput, ...request.Option) (*sts.GetAccessKeyInfoOutput, error) + GetAccessKeyInfoRequest(*sts.GetAccessKeyInfoInput) (*request.Request, *sts.GetAccessKeyInfoOutput) + + GetCallerIdentity(*sts.GetCallerIdentityInput) (*sts.GetCallerIdentityOutput, error) + GetCallerIdentityWithContext(aws.Context, *sts.GetCallerIdentityInput, ...request.Option) (*sts.GetCallerIdentityOutput, error) + GetCallerIdentityRequest(*sts.GetCallerIdentityInput) (*request.Request, *sts.GetCallerIdentityOutput) + + GetFederationToken(*sts.GetFederationTokenInput) (*sts.GetFederationTokenOutput, error) + GetFederationTokenWithContext(aws.Context, *sts.GetFederationTokenInput, ...request.Option) (*sts.GetFederationTokenOutput, error) + GetFederationTokenRequest(*sts.GetFederationTokenInput) (*request.Request, *sts.GetFederationTokenOutput) + + GetSessionToken(*sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) + GetSessionTokenWithContext(aws.Context, *sts.GetSessionTokenInput, ...request.Option) (*sts.GetSessionTokenOutput, error) + GetSessionTokenRequest(*sts.GetSessionTokenInput) (*request.Request, *sts.GetSessionTokenOutput) +} + +var _ STSAPI = (*sts.STS)(nil) diff --git a/vendor/github.com/beorn7/perks/go.mod b/vendor/github.com/beorn7/perks/go.mod new file mode 100644 index 0000000000000..c4022f2127a94 --- /dev/null +++ b/vendor/github.com/beorn7/perks/go.mod @@ -0,0 +1,3 @@ +module github.com/beorn7/perks + +go 1.11 diff --git a/vendor/github.com/cespare/xxhash/v2/LICENSE.txt b/vendor/github.com/cespare/xxhash/v2/LICENSE.txt new file mode 100644 index 0000000000000..24b53065f40b5 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2016 Caleb Spare + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/cespare/xxhash/v2/README.md b/vendor/github.com/cespare/xxhash/v2/README.md new file mode 100644 index 0000000000000..2fd8693c21b20 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/README.md @@ -0,0 +1,67 @@ +# xxhash + +[![GoDoc](https://godoc.org/github.com/cespare/xxhash?status.svg)](https://godoc.org/github.com/cespare/xxhash) +[![Build Status](https://travis-ci.org/cespare/xxhash.svg?branch=master)](https://travis-ci.org/cespare/xxhash) + +xxhash is a Go implementation of the 64-bit +[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a +high-quality hashing algorithm that is much faster than anything in the Go +standard library. + +This package provides a straightforward API: + +``` +func Sum64(b []byte) uint64 +func Sum64String(s string) uint64 +type Digest struct{ ... } + func New() *Digest +``` + +The `Digest` type implements hash.Hash64. Its key methods are: + +``` +func (*Digest) Write([]byte) (int, error) +func (*Digest) WriteString(string) (int, error) +func (*Digest) Sum64() uint64 +``` + +This implementation provides a fast pure-Go implementation and an even faster +assembly implementation for amd64. + +## Compatibility + +This package is in a module and the latest code is in version 2 of the module. +You need a version of Go with at least "minimal module compatibility" to use +github.com/cespare/xxhash/v2: + +* 1.9.7+ for Go 1.9 +* 1.10.3+ for Go 1.10 +* Go 1.11 or later + +I recommend using the latest release of Go. + +## Benchmarks + +Here are some quick benchmarks comparing the pure-Go and assembly +implementations of Sum64. + +| input size | purego | asm | +| --- | --- | --- | +| 5 B | 979.66 MB/s | 1291.17 MB/s | +| 100 B | 7475.26 MB/s | 7973.40 MB/s | +| 4 KB | 17573.46 MB/s | 17602.65 MB/s | +| 10 MB | 17131.46 MB/s | 17142.16 MB/s | + +These numbers were generated on Ubuntu 18.04 with an Intel i7-8700K CPU using +the following commands under Go 1.11.2: + +``` +$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes' +$ go test -benchtime 10s -bench '/xxhash,direct,bytes' +``` + +## Projects using this package + +- [InfluxDB](https://github.com/influxdata/influxdb) +- [Prometheus](https://github.com/prometheus/prometheus) +- [FreeCache](https://github.com/coocood/freecache) diff --git a/vendor/github.com/cespare/xxhash/v2/go.mod b/vendor/github.com/cespare/xxhash/v2/go.mod new file mode 100644 index 0000000000000..49f67608bf6bb --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/go.mod @@ -0,0 +1,3 @@ +module github.com/cespare/xxhash/v2 + +go 1.11 diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash.go b/vendor/github.com/cespare/xxhash/v2/xxhash.go new file mode 100644 index 0000000000000..db0b35fbe39f0 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash.go @@ -0,0 +1,236 @@ +// Package xxhash implements the 64-bit variant of xxHash (XXH64) as described +// at http://cyan4973.github.io/xxHash/. +package xxhash + +import ( + "encoding/binary" + "errors" + "math/bits" +) + +const ( + prime1 uint64 = 11400714785074694791 + prime2 uint64 = 14029467366897019727 + prime3 uint64 = 1609587929392839161 + prime4 uint64 = 9650029242287828579 + prime5 uint64 = 2870177450012600261 +) + +// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where +// possible in the Go code is worth a small (but measurable) performance boost +// by avoiding some MOVQs. Vars are needed for the asm and also are useful for +// convenience in the Go code in a few places where we need to intentionally +// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the +// result overflows a uint64). +var ( + prime1v = prime1 + prime2v = prime2 + prime3v = prime3 + prime4v = prime4 + prime5v = prime5 +) + +// Digest implements hash.Hash64. +type Digest struct { + v1 uint64 + v2 uint64 + v3 uint64 + v4 uint64 + total uint64 + mem [32]byte + n int // how much of mem is used +} + +// New creates a new Digest that computes the 64-bit xxHash algorithm. +func New() *Digest { + var d Digest + d.Reset() + return &d +} + +// Reset clears the Digest's state so that it can be reused. +func (d *Digest) Reset() { + d.v1 = prime1v + prime2 + d.v2 = prime2 + d.v3 = 0 + d.v4 = -prime1v + d.total = 0 + d.n = 0 +} + +// Size always returns 8 bytes. +func (d *Digest) Size() int { return 8 } + +// BlockSize always returns 32 bytes. +func (d *Digest) BlockSize() int { return 32 } + +// Write adds more data to d. It always returns len(b), nil. +func (d *Digest) Write(b []byte) (n int, err error) { + n = len(b) + d.total += uint64(n) + + if d.n+n < 32 { + // This new data doesn't even fill the current block. + copy(d.mem[d.n:], b) + d.n += n + return + } + + if d.n > 0 { + // Finish off the partial block. + copy(d.mem[d.n:], b) + d.v1 = round(d.v1, u64(d.mem[0:8])) + d.v2 = round(d.v2, u64(d.mem[8:16])) + d.v3 = round(d.v3, u64(d.mem[16:24])) + d.v4 = round(d.v4, u64(d.mem[24:32])) + b = b[32-d.n:] + d.n = 0 + } + + if len(b) >= 32 { + // One or more full blocks left. + nw := writeBlocks(d, b) + b = b[nw:] + } + + // Store any remaining partial block. + copy(d.mem[:], b) + d.n = len(b) + + return +} + +// Sum appends the current hash to b and returns the resulting slice. +func (d *Digest) Sum(b []byte) []byte { + s := d.Sum64() + return append( + b, + byte(s>>56), + byte(s>>48), + byte(s>>40), + byte(s>>32), + byte(s>>24), + byte(s>>16), + byte(s>>8), + byte(s), + ) +} + +// Sum64 returns the current hash. +func (d *Digest) Sum64() uint64 { + var h uint64 + + if d.total >= 32 { + v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = d.v3 + prime5 + } + + h += d.total + + i, end := 0, d.n + for ; i+8 <= end; i += 8 { + k1 := round(0, u64(d.mem[i:i+8])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if i+4 <= end { + h ^= uint64(u32(d.mem[i:i+4])) * prime1 + h = rol23(h)*prime2 + prime3 + i += 4 + } + for i < end { + h ^= uint64(d.mem[i]) * prime5 + h = rol11(h) * prime1 + i++ + } + + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + + return h +} + +const ( + magic = "xxh\x06" + marshaledSize = len(magic) + 8*5 + 32 +) + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (d *Digest) MarshalBinary() ([]byte, error) { + b := make([]byte, 0, marshaledSize) + b = append(b, magic...) + b = appendUint64(b, d.v1) + b = appendUint64(b, d.v2) + b = appendUint64(b, d.v3) + b = appendUint64(b, d.v4) + b = appendUint64(b, d.total) + b = append(b, d.mem[:d.n]...) + b = b[:len(b)+len(d.mem)-d.n] + return b, nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +func (d *Digest) UnmarshalBinary(b []byte) error { + if len(b) < len(magic) || string(b[:len(magic)]) != magic { + return errors.New("xxhash: invalid hash state identifier") + } + if len(b) != marshaledSize { + return errors.New("xxhash: invalid hash state size") + } + b = b[len(magic):] + b, d.v1 = consumeUint64(b) + b, d.v2 = consumeUint64(b) + b, d.v3 = consumeUint64(b) + b, d.v4 = consumeUint64(b) + b, d.total = consumeUint64(b) + copy(d.mem[:], b) + b = b[len(d.mem):] + d.n = int(d.total % uint64(len(d.mem))) + return nil +} + +func appendUint64(b []byte, x uint64) []byte { + var a [8]byte + binary.LittleEndian.PutUint64(a[:], x) + return append(b, a[:]...) +} + +func consumeUint64(b []byte) ([]byte, uint64) { + x := u64(b) + return b[8:], x +} + +func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } +func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } + +func round(acc, input uint64) uint64 { + acc += input * prime2 + acc = rol31(acc) + acc *= prime1 + return acc +} + +func mergeRound(acc, val uint64) uint64 { + val = round(0, val) + acc ^= val + acc = acc*prime1 + prime4 + return acc +} + +func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) } +func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) } +func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) } +func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) } +func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) } +func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) } +func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) } +func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) } diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go new file mode 100644 index 0000000000000..ad14b807f4d96 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.go @@ -0,0 +1,13 @@ +// +build !appengine +// +build gc +// +build !purego + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +// +//go:noescape +func Sum64(b []byte) uint64 + +//go:noescape +func writeBlocks(d *Digest, b []byte) int diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s new file mode 100644 index 0000000000000..d580e32aed4af --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s @@ -0,0 +1,215 @@ +// +build !appengine +// +build gc +// +build !purego + +#include "textflag.h" + +// Register allocation: +// AX h +// CX pointer to advance through b +// DX n +// BX loop end +// R8 v1, k1 +// R9 v2 +// R10 v3 +// R11 v4 +// R12 tmp +// R13 prime1v +// R14 prime2v +// R15 prime4v + +// round reads from and advances the buffer pointer in CX. +// It assumes that R13 has prime1v and R14 has prime2v. +#define round(r) \ + MOVQ (CX), R12 \ + ADDQ $8, CX \ + IMULQ R14, R12 \ + ADDQ R12, r \ + ROLQ $31, r \ + IMULQ R13, r + +// mergeRound applies a merge round on the two registers acc and val. +// It assumes that R13 has prime1v, R14 has prime2v, and R15 has prime4v. +#define mergeRound(acc, val) \ + IMULQ R14, val \ + ROLQ $31, val \ + IMULQ R13, val \ + XORQ val, acc \ + IMULQ R13, acc \ + ADDQ R15, acc + +// func Sum64(b []byte) uint64 +TEXT ·Sum64(SB), NOSPLIT, $0-32 + // Load fixed primes. + MOVQ ·prime1v(SB), R13 + MOVQ ·prime2v(SB), R14 + MOVQ ·prime4v(SB), R15 + + // Load slice. + MOVQ b_base+0(FP), CX + MOVQ b_len+8(FP), DX + LEAQ (CX)(DX*1), BX + + // The first loop limit will be len(b)-32. + SUBQ $32, BX + + // Check whether we have at least one block. + CMPQ DX, $32 + JLT noBlocks + + // Set up initial state (v1, v2, v3, v4). + MOVQ R13, R8 + ADDQ R14, R8 + MOVQ R14, R9 + XORQ R10, R10 + XORQ R11, R11 + SUBQ R13, R11 + + // Loop until CX > BX. +blockLoop: + round(R8) + round(R9) + round(R10) + round(R11) + + CMPQ CX, BX + JLE blockLoop + + MOVQ R8, AX + ROLQ $1, AX + MOVQ R9, R12 + ROLQ $7, R12 + ADDQ R12, AX + MOVQ R10, R12 + ROLQ $12, R12 + ADDQ R12, AX + MOVQ R11, R12 + ROLQ $18, R12 + ADDQ R12, AX + + mergeRound(AX, R8) + mergeRound(AX, R9) + mergeRound(AX, R10) + mergeRound(AX, R11) + + JMP afterBlocks + +noBlocks: + MOVQ ·prime5v(SB), AX + +afterBlocks: + ADDQ DX, AX + + // Right now BX has len(b)-32, and we want to loop until CX > len(b)-8. + ADDQ $24, BX + + CMPQ CX, BX + JG fourByte + +wordLoop: + // Calculate k1. + MOVQ (CX), R8 + ADDQ $8, CX + IMULQ R14, R8 + ROLQ $31, R8 + IMULQ R13, R8 + + XORQ R8, AX + ROLQ $27, AX + IMULQ R13, AX + ADDQ R15, AX + + CMPQ CX, BX + JLE wordLoop + +fourByte: + ADDQ $4, BX + CMPQ CX, BX + JG singles + + MOVL (CX), R8 + ADDQ $4, CX + IMULQ R13, R8 + XORQ R8, AX + + ROLQ $23, AX + IMULQ R14, AX + ADDQ ·prime3v(SB), AX + +singles: + ADDQ $4, BX + CMPQ CX, BX + JGE finalize + +singlesLoop: + MOVBQZX (CX), R12 + ADDQ $1, CX + IMULQ ·prime5v(SB), R12 + XORQ R12, AX + + ROLQ $11, AX + IMULQ R13, AX + + CMPQ CX, BX + JL singlesLoop + +finalize: + MOVQ AX, R12 + SHRQ $33, R12 + XORQ R12, AX + IMULQ R14, AX + MOVQ AX, R12 + SHRQ $29, R12 + XORQ R12, AX + IMULQ ·prime3v(SB), AX + MOVQ AX, R12 + SHRQ $32, R12 + XORQ R12, AX + + MOVQ AX, ret+24(FP) + RET + +// writeBlocks uses the same registers as above except that it uses AX to store +// the d pointer. + +// func writeBlocks(d *Digest, b []byte) int +TEXT ·writeBlocks(SB), NOSPLIT, $0-40 + // Load fixed primes needed for round. + MOVQ ·prime1v(SB), R13 + MOVQ ·prime2v(SB), R14 + + // Load slice. + MOVQ b_base+8(FP), CX + MOVQ b_len+16(FP), DX + LEAQ (CX)(DX*1), BX + SUBQ $32, BX + + // Load vN from d. + MOVQ d+0(FP), AX + MOVQ 0(AX), R8 // v1 + MOVQ 8(AX), R9 // v2 + MOVQ 16(AX), R10 // v3 + MOVQ 24(AX), R11 // v4 + + // We don't need to check the loop condition here; this function is + // always called with at least one block of data to process. +blockLoop: + round(R8) + round(R9) + round(R10) + round(R11) + + CMPQ CX, BX + JLE blockLoop + + // Copy vN back to d. + MOVQ R8, 0(AX) + MOVQ R9, 8(AX) + MOVQ R10, 16(AX) + MOVQ R11, 24(AX) + + // The number of bytes written is CX minus the old base pointer. + SUBQ b_base+8(FP), CX + MOVQ CX, ret+32(FP) + + RET diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_other.go b/vendor/github.com/cespare/xxhash/v2/xxhash_other.go new file mode 100644 index 0000000000000..4a5a821603e5b --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_other.go @@ -0,0 +1,76 @@ +// +build !amd64 appengine !gc purego + +package xxhash + +// Sum64 computes the 64-bit xxHash digest of b. +func Sum64(b []byte) uint64 { + // A simpler version would be + // d := New() + // d.Write(b) + // return d.Sum64() + // but this is faster, particularly for small inputs. + + n := len(b) + var h uint64 + + if n >= 32 { + v1 := prime1v + prime2 + v2 := prime2 + v3 := uint64(0) + v4 := -prime1v + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + h = mergeRound(h, v1) + h = mergeRound(h, v2) + h = mergeRound(h, v3) + h = mergeRound(h, v4) + } else { + h = prime5 + } + + h += uint64(n) + + i, end := 0, len(b) + for ; i+8 <= end; i += 8 { + k1 := round(0, u64(b[i:i+8:len(b)])) + h ^= k1 + h = rol27(h)*prime1 + prime4 + } + if i+4 <= end { + h ^= uint64(u32(b[i:i+4:len(b)])) * prime1 + h = rol23(h)*prime2 + prime3 + i += 4 + } + for ; i < end; i++ { + h ^= uint64(b[i]) * prime5 + h = rol11(h) * prime1 + } + + h ^= h >> 33 + h *= prime2 + h ^= h >> 29 + h *= prime3 + h ^= h >> 32 + + return h +} + +func writeBlocks(d *Digest, b []byte) int { + v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 + n := len(b) + for len(b) >= 32 { + v1 = round(v1, u64(b[0:8:len(b)])) + v2 = round(v2, u64(b[8:16:len(b)])) + v3 = round(v3, u64(b[16:24:len(b)])) + v4 = round(v4, u64(b[24:32:len(b)])) + b = b[32:len(b):len(b)] + } + d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4 + return n - len(b) +} diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go new file mode 100644 index 0000000000000..fc9bea7a31f2b --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go @@ -0,0 +1,15 @@ +// +build appengine + +// This file contains the safe implementations of otherwise unsafe-using code. + +package xxhash + +// Sum64String computes the 64-bit xxHash digest of s. +func Sum64String(s string) uint64 { + return Sum64([]byte(s)) +} + +// WriteString adds more data to d. It always returns len(s), nil. +func (d *Digest) WriteString(s string) (n int, err error) { + return d.Write([]byte(s)) +} diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go new file mode 100644 index 0000000000000..53bf76efbc247 --- /dev/null +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go @@ -0,0 +1,46 @@ +// +build !appengine + +// This file encapsulates usage of unsafe. +// xxhash_safe.go contains the safe implementations. + +package xxhash + +import ( + "reflect" + "unsafe" +) + +// Notes: +// +// See https://groups.google.com/d/msg/golang-nuts/dcjzJy-bSpw/tcZYBzQqAQAJ +// for some discussion about these unsafe conversions. +// +// In the future it's possible that compiler optimizations will make these +// unsafe operations unnecessary: https://golang.org/issue/2205. +// +// Both of these wrapper functions still incur function call overhead since they +// will not be inlined. We could write Go/asm copies of Sum64 and Digest.Write +// for strings to squeeze out a bit more speed. Mid-stack inlining should +// eventually fix this. + +// Sum64String computes the 64-bit xxHash digest of s. +// It may be faster than Sum64([]byte(s)) by avoiding a copy. +func Sum64String(s string) uint64 { + var b []byte + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data + bh.Len = len(s) + bh.Cap = len(s) + return Sum64(b) +} + +// WriteString adds more data to d. It always returns len(s), nil. +// It may be faster than Write([]byte(s)) by avoiding a copy. +func (d *Digest) WriteString(s string) (n int, err error) { + var b []byte + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data + bh.Len = len(s) + bh.Cap = len(s) + return d.Write(b) +} diff --git a/vendor/github.com/cilium/ebpf/LICENSE b/vendor/github.com/cilium/ebpf/LICENSE new file mode 100644 index 0000000000000..c637ae99c26d2 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2017 Nathan Sweet +Copyright (c) 2018, 2019 Cloudflare +Copyright (c) 2019 Authors of Cilium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/cilium/ebpf/README.md b/vendor/github.com/cilium/ebpf/README.md new file mode 100644 index 0000000000000..76c3c303bb1be --- /dev/null +++ b/vendor/github.com/cilium/ebpf/README.md @@ -0,0 +1,62 @@ +# eBPF + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf) + +eBPF is a pure Go library that provides utilities for loading, compiling, and +debugging eBPF programs. It has minimal external dependencies and is intended to +be used in long running processes. + +* [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic + assembler +* [link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF + to various hooks +* [perf](https://pkg.go.dev/github.com/cilium/ebpf/perf) allows reading from a + `PERF_EVENT_ARRAY` +* [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows + compiling and embedding eBPF programs in Go code + +The library is maintained by [Cloudflare](https://www.cloudflare.com) and +[Cilium](https://www.cilium.io). Feel free to +[join](https://cilium.herokuapp.com/) the +[#libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack. + +## Current status + +The package is production ready, but **the API is explicitly unstable right +now**. Expect to update your code if you want to follow along. + +## Getting Started + +A small collection of Go and eBPF programs that serve as examples for building +your own tools can be found under [examples/](examples/). + +Contributions are highly encouraged, as they highlight certain use cases of +eBPF and the library, and help shape the future of the project. + +## Requirements + +* A version of Go that is [supported by + upstream](https://golang.org/doc/devel/release.html#policy) +* Linux 4.9, 4.19 or 5.4 (versions in-between should work, but are not tested) + +## Useful resources + +* [eBPF.io](https://ebpf.io) (recommended) +* [Cilium eBPF documentation](https://docs.cilium.io/en/latest/bpf/#bpf-guide) + (recommended) +* [Linux documentation on + BPF](https://www.kernel.org/doc/html/latest/networking/filter.html) +* [eBPF features by Linux + version](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md) + +## Regenerating Testdata + +Run `make` in the root of this repository to rebuild testdata in all +subpackages. This requires Docker, as it relies on a standardized build +environment to keep the build output stable. + +The toolchain image build files are kept in [testdata/docker/](testdata/docker/). + +## License + +MIT diff --git a/vendor/github.com/cilium/ebpf/asm/alu.go b/vendor/github.com/cilium/ebpf/asm/alu.go new file mode 100644 index 0000000000000..70ccc4d1518dc --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/alu.go @@ -0,0 +1,149 @@ +package asm + +//go:generate stringer -output alu_string.go -type=Source,Endianness,ALUOp + +// Source of ALU / ALU64 / Branch operations +// +// msb lsb +// +----+-+---+ +// |op |S|cls| +// +----+-+---+ +type Source uint8 + +const sourceMask OpCode = 0x08 + +// Source bitmask +const ( + // InvalidSource is returned by getters when invoked + // on non ALU / branch OpCodes. + InvalidSource Source = 0xff + // ImmSource src is from constant + ImmSource Source = 0x00 + // RegSource src is from register + RegSource Source = 0x08 +) + +// The Endianness of a byte swap instruction. +type Endianness uint8 + +const endianMask = sourceMask + +// Endian flags +const ( + InvalidEndian Endianness = 0xff + // Convert to little endian + LE Endianness = 0x00 + // Convert to big endian + BE Endianness = 0x08 +) + +// ALUOp are ALU / ALU64 operations +// +// msb lsb +// +----+-+---+ +// |OP |s|cls| +// +----+-+---+ +type ALUOp uint8 + +const aluMask OpCode = 0xf0 + +const ( + // InvalidALUOp is returned by getters when invoked + // on non ALU OpCodes + InvalidALUOp ALUOp = 0xff + // Add - addition + Add ALUOp = 0x00 + // Sub - subtraction + Sub ALUOp = 0x10 + // Mul - multiplication + Mul ALUOp = 0x20 + // Div - division + Div ALUOp = 0x30 + // Or - bitwise or + Or ALUOp = 0x40 + // And - bitwise and + And ALUOp = 0x50 + // LSh - bitwise shift left + LSh ALUOp = 0x60 + // RSh - bitwise shift right + RSh ALUOp = 0x70 + // Neg - sign/unsign signing bit + Neg ALUOp = 0x80 + // Mod - modulo + Mod ALUOp = 0x90 + // Xor - bitwise xor + Xor ALUOp = 0xa0 + // Mov - move value from one place to another + Mov ALUOp = 0xb0 + // ArSh - arithmatic shift + ArSh ALUOp = 0xc0 + // Swap - endian conversions + Swap ALUOp = 0xd0 +) + +// HostTo converts from host to another endianness. +func HostTo(endian Endianness, dst Register, size Size) Instruction { + var imm int64 + switch size { + case Half: + imm = 16 + case Word: + imm = 32 + case DWord: + imm = 64 + default: + return Instruction{OpCode: InvalidOpCode} + } + + return Instruction{ + OpCode: OpCode(ALUClass).SetALUOp(Swap).SetSource(Source(endian)), + Dst: dst, + Constant: imm, + } +} + +// Op returns the OpCode for an ALU operation with a given source. +func (op ALUOp) Op(source Source) OpCode { + return OpCode(ALU64Class).SetALUOp(op).SetSource(source) +} + +// Reg emits `dst (op) src`. +func (op ALUOp) Reg(dst, src Register) Instruction { + return Instruction{ + OpCode: op.Op(RegSource), + Dst: dst, + Src: src, + } +} + +// Imm emits `dst (op) value`. +func (op ALUOp) Imm(dst Register, value int32) Instruction { + return Instruction{ + OpCode: op.Op(ImmSource), + Dst: dst, + Constant: int64(value), + } +} + +// Op32 returns the OpCode for a 32-bit ALU operation with a given source. +func (op ALUOp) Op32(source Source) OpCode { + return OpCode(ALUClass).SetALUOp(op).SetSource(source) +} + +// Reg32 emits `dst (op) src`, zeroing the upper 32 bit of dst. +func (op ALUOp) Reg32(dst, src Register) Instruction { + return Instruction{ + OpCode: op.Op32(RegSource), + Dst: dst, + Src: src, + } +} + +// Imm32 emits `dst (op) value`, zeroing the upper 32 bit of dst. +func (op ALUOp) Imm32(dst Register, value int32) Instruction { + return Instruction{ + OpCode: op.Op32(ImmSource), + Dst: dst, + Constant: int64(value), + } +} diff --git a/vendor/github.com/cilium/ebpf/asm/alu_string.go b/vendor/github.com/cilium/ebpf/asm/alu_string.go new file mode 100644 index 0000000000000..72d3fe6292ea6 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/alu_string.go @@ -0,0 +1,107 @@ +// Code generated by "stringer -output alu_string.go -type=Source,Endianness,ALUOp"; DO NOT EDIT. + +package asm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidSource-255] + _ = x[ImmSource-0] + _ = x[RegSource-8] +} + +const ( + _Source_name_0 = "ImmSource" + _Source_name_1 = "RegSource" + _Source_name_2 = "InvalidSource" +) + +func (i Source) String() string { + switch { + case i == 0: + return _Source_name_0 + case i == 8: + return _Source_name_1 + case i == 255: + return _Source_name_2 + default: + return "Source(" + strconv.FormatInt(int64(i), 10) + ")" + } +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidEndian-255] + _ = x[LE-0] + _ = x[BE-8] +} + +const ( + _Endianness_name_0 = "LE" + _Endianness_name_1 = "BE" + _Endianness_name_2 = "InvalidEndian" +) + +func (i Endianness) String() string { + switch { + case i == 0: + return _Endianness_name_0 + case i == 8: + return _Endianness_name_1 + case i == 255: + return _Endianness_name_2 + default: + return "Endianness(" + strconv.FormatInt(int64(i), 10) + ")" + } +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidALUOp-255] + _ = x[Add-0] + _ = x[Sub-16] + _ = x[Mul-32] + _ = x[Div-48] + _ = x[Or-64] + _ = x[And-80] + _ = x[LSh-96] + _ = x[RSh-112] + _ = x[Neg-128] + _ = x[Mod-144] + _ = x[Xor-160] + _ = x[Mov-176] + _ = x[ArSh-192] + _ = x[Swap-208] +} + +const _ALUOp_name = "AddSubMulDivOrAndLShRShNegModXorMovArShSwapInvalidALUOp" + +var _ALUOp_map = map[ALUOp]string{ + 0: _ALUOp_name[0:3], + 16: _ALUOp_name[3:6], + 32: _ALUOp_name[6:9], + 48: _ALUOp_name[9:12], + 64: _ALUOp_name[12:14], + 80: _ALUOp_name[14:17], + 96: _ALUOp_name[17:20], + 112: _ALUOp_name[20:23], + 128: _ALUOp_name[23:26], + 144: _ALUOp_name[26:29], + 160: _ALUOp_name[29:32], + 176: _ALUOp_name[32:35], + 192: _ALUOp_name[35:39], + 208: _ALUOp_name[39:43], + 255: _ALUOp_name[43:55], +} + +func (i ALUOp) String() string { + if str, ok := _ALUOp_map[i]; ok { + return str + } + return "ALUOp(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/vendor/github.com/cilium/ebpf/asm/doc.go b/vendor/github.com/cilium/ebpf/asm/doc.go new file mode 100644 index 0000000000000..7031bdc2768a9 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/doc.go @@ -0,0 +1,2 @@ +// Package asm is an assembler for eBPF bytecode. +package asm diff --git a/vendor/github.com/cilium/ebpf/asm/func.go b/vendor/github.com/cilium/ebpf/asm/func.go new file mode 100644 index 0000000000000..aee2c7ac81062 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/func.go @@ -0,0 +1,195 @@ +package asm + +//go:generate stringer -output func_string.go -type=BuiltinFunc + +// BuiltinFunc is a built-in eBPF function. +type BuiltinFunc int32 + +// eBPF built-in functions +// +// You can regenerate this list using the following gawk script: +// +// /FN\(.+\),/ { +// match($1, /\((.+)\)/, r) +// split(r[1], p, "_") +// printf "Fn" +// for (i in p) { +// printf "%s%s", toupper(substr(p[i], 1, 1)), substr(p[i], 2) +// } +// print "" +// } +// +// The script expects include/uapi/linux/bpf.h as it's input. +const ( + FnUnspec BuiltinFunc = iota + FnMapLookupElem + FnMapUpdateElem + FnMapDeleteElem + FnProbeRead + FnKtimeGetNs + FnTracePrintk + FnGetPrandomU32 + FnGetSmpProcessorId + FnSkbStoreBytes + FnL3CsumReplace + FnL4CsumReplace + FnTailCall + FnCloneRedirect + FnGetCurrentPidTgid + FnGetCurrentUidGid + FnGetCurrentComm + FnGetCgroupClassid + FnSkbVlanPush + FnSkbVlanPop + FnSkbGetTunnelKey + FnSkbSetTunnelKey + FnPerfEventRead + FnRedirect + FnGetRouteRealm + FnPerfEventOutput + FnSkbLoadBytes + FnGetStackid + FnCsumDiff + FnSkbGetTunnelOpt + FnSkbSetTunnelOpt + FnSkbChangeProto + FnSkbChangeType + FnSkbUnderCgroup + FnGetHashRecalc + FnGetCurrentTask + FnProbeWriteUser + FnCurrentTaskUnderCgroup + FnSkbChangeTail + FnSkbPullData + FnCsumUpdate + FnSetHashInvalid + FnGetNumaNodeId + FnSkbChangeHead + FnXdpAdjustHead + FnProbeReadStr + FnGetSocketCookie + FnGetSocketUid + FnSetHash + FnSetsockopt + FnSkbAdjustRoom + FnRedirectMap + FnSkRedirectMap + FnSockMapUpdate + FnXdpAdjustMeta + FnPerfEventReadValue + FnPerfProgReadValue + FnGetsockopt + FnOverrideReturn + FnSockOpsCbFlagsSet + FnMsgRedirectMap + FnMsgApplyBytes + FnMsgCorkBytes + FnMsgPullData + FnBind + FnXdpAdjustTail + FnSkbGetXfrmState + FnGetStack + FnSkbLoadBytesRelative + FnFibLookup + FnSockHashUpdate + FnMsgRedirectHash + FnSkRedirectHash + FnLwtPushEncap + FnLwtSeg6StoreBytes + FnLwtSeg6AdjustSrh + FnLwtSeg6Action + FnRcRepeat + FnRcKeydown + FnSkbCgroupId + FnGetCurrentCgroupId + FnGetLocalStorage + FnSkSelectReuseport + FnSkbAncestorCgroupId + FnSkLookupTcp + FnSkLookupUdp + FnSkRelease + FnMapPushElem + FnMapPopElem + FnMapPeekElem + FnMsgPushData + FnMsgPopData + FnRcPointerRel + FnSpinLock + FnSpinUnlock + FnSkFullsock + FnTcpSock + FnSkbEcnSetCe + FnGetListenerSock + FnSkcLookupTcp + FnTcpCheckSyncookie + FnSysctlGetName + FnSysctlGetCurrentValue + FnSysctlGetNewValue + FnSysctlSetNewValue + FnStrtol + FnStrtoul + FnSkStorageGet + FnSkStorageDelete + FnSendSignal + FnTcpGenSyncookie + FnSkbOutput + FnProbeReadUser + FnProbeReadKernel + FnProbeReadUserStr + FnProbeReadKernelStr + FnTcpSendAck + FnSendSignalThread + FnJiffies64 + FnReadBranchRecords + FnGetNsCurrentPidTgid + FnXdpOutput + FnGetNetnsCookie + FnGetCurrentAncestorCgroupId + FnSkAssign + FnKtimeGetBootNs + FnSeqPrintf + FnSeqWrite + FnSkCgroupId + FnSkAncestorCgroupId + FnRingbufOutput + FnRingbufReserve + FnRingbufSubmit + FnRingbufDiscard + FnRingbufQuery + FnCsumLevel + FnSkcToTcp6Sock + FnSkcToTcpSock + FnSkcToTcpTimewaitSock + FnSkcToTcpRequestSock + FnSkcToUdp6Sock + FnGetTaskStack + FnLoadHdrOpt + FnStoreHdrOpt + FnReserveHdrOpt + FnInodeStorageGet + FnInodeStorageDelete + FnDPath + FnCopyFromUser + FnSnprintfBtf + FnSeqPrintfBtf + FnSkbCgroupClassid + FnRedirectNeigh + FnPerCpuPtr + FnThisCpuPtr + FnRedirectPeer + FnTaskStorageGet + FnTaskStorageDelete + FnGetCurrentTaskBtf + FnBprmOptsSet + FnKtimeGetCoarseNs + FnImaInodeHash + FnSockFromFile +) + +// Call emits a function call. +func (fn BuiltinFunc) Call() Instruction { + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(Call), + Constant: int64(fn), + } +} diff --git a/vendor/github.com/cilium/ebpf/asm/func_string.go b/vendor/github.com/cilium/ebpf/asm/func_string.go new file mode 100644 index 0000000000000..a712c5da8af99 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/func_string.go @@ -0,0 +1,185 @@ +// Code generated by "stringer -output func_string.go -type=BuiltinFunc"; DO NOT EDIT. + +package asm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[FnUnspec-0] + _ = x[FnMapLookupElem-1] + _ = x[FnMapUpdateElem-2] + _ = x[FnMapDeleteElem-3] + _ = x[FnProbeRead-4] + _ = x[FnKtimeGetNs-5] + _ = x[FnTracePrintk-6] + _ = x[FnGetPrandomU32-7] + _ = x[FnGetSmpProcessorId-8] + _ = x[FnSkbStoreBytes-9] + _ = x[FnL3CsumReplace-10] + _ = x[FnL4CsumReplace-11] + _ = x[FnTailCall-12] + _ = x[FnCloneRedirect-13] + _ = x[FnGetCurrentPidTgid-14] + _ = x[FnGetCurrentUidGid-15] + _ = x[FnGetCurrentComm-16] + _ = x[FnGetCgroupClassid-17] + _ = x[FnSkbVlanPush-18] + _ = x[FnSkbVlanPop-19] + _ = x[FnSkbGetTunnelKey-20] + _ = x[FnSkbSetTunnelKey-21] + _ = x[FnPerfEventRead-22] + _ = x[FnRedirect-23] + _ = x[FnGetRouteRealm-24] + _ = x[FnPerfEventOutput-25] + _ = x[FnSkbLoadBytes-26] + _ = x[FnGetStackid-27] + _ = x[FnCsumDiff-28] + _ = x[FnSkbGetTunnelOpt-29] + _ = x[FnSkbSetTunnelOpt-30] + _ = x[FnSkbChangeProto-31] + _ = x[FnSkbChangeType-32] + _ = x[FnSkbUnderCgroup-33] + _ = x[FnGetHashRecalc-34] + _ = x[FnGetCurrentTask-35] + _ = x[FnProbeWriteUser-36] + _ = x[FnCurrentTaskUnderCgroup-37] + _ = x[FnSkbChangeTail-38] + _ = x[FnSkbPullData-39] + _ = x[FnCsumUpdate-40] + _ = x[FnSetHashInvalid-41] + _ = x[FnGetNumaNodeId-42] + _ = x[FnSkbChangeHead-43] + _ = x[FnXdpAdjustHead-44] + _ = x[FnProbeReadStr-45] + _ = x[FnGetSocketCookie-46] + _ = x[FnGetSocketUid-47] + _ = x[FnSetHash-48] + _ = x[FnSetsockopt-49] + _ = x[FnSkbAdjustRoom-50] + _ = x[FnRedirectMap-51] + _ = x[FnSkRedirectMap-52] + _ = x[FnSockMapUpdate-53] + _ = x[FnXdpAdjustMeta-54] + _ = x[FnPerfEventReadValue-55] + _ = x[FnPerfProgReadValue-56] + _ = x[FnGetsockopt-57] + _ = x[FnOverrideReturn-58] + _ = x[FnSockOpsCbFlagsSet-59] + _ = x[FnMsgRedirectMap-60] + _ = x[FnMsgApplyBytes-61] + _ = x[FnMsgCorkBytes-62] + _ = x[FnMsgPullData-63] + _ = x[FnBind-64] + _ = x[FnXdpAdjustTail-65] + _ = x[FnSkbGetXfrmState-66] + _ = x[FnGetStack-67] + _ = x[FnSkbLoadBytesRelative-68] + _ = x[FnFibLookup-69] + _ = x[FnSockHashUpdate-70] + _ = x[FnMsgRedirectHash-71] + _ = x[FnSkRedirectHash-72] + _ = x[FnLwtPushEncap-73] + _ = x[FnLwtSeg6StoreBytes-74] + _ = x[FnLwtSeg6AdjustSrh-75] + _ = x[FnLwtSeg6Action-76] + _ = x[FnRcRepeat-77] + _ = x[FnRcKeydown-78] + _ = x[FnSkbCgroupId-79] + _ = x[FnGetCurrentCgroupId-80] + _ = x[FnGetLocalStorage-81] + _ = x[FnSkSelectReuseport-82] + _ = x[FnSkbAncestorCgroupId-83] + _ = x[FnSkLookupTcp-84] + _ = x[FnSkLookupUdp-85] + _ = x[FnSkRelease-86] + _ = x[FnMapPushElem-87] + _ = x[FnMapPopElem-88] + _ = x[FnMapPeekElem-89] + _ = x[FnMsgPushData-90] + _ = x[FnMsgPopData-91] + _ = x[FnRcPointerRel-92] + _ = x[FnSpinLock-93] + _ = x[FnSpinUnlock-94] + _ = x[FnSkFullsock-95] + _ = x[FnTcpSock-96] + _ = x[FnSkbEcnSetCe-97] + _ = x[FnGetListenerSock-98] + _ = x[FnSkcLookupTcp-99] + _ = x[FnTcpCheckSyncookie-100] + _ = x[FnSysctlGetName-101] + _ = x[FnSysctlGetCurrentValue-102] + _ = x[FnSysctlGetNewValue-103] + _ = x[FnSysctlSetNewValue-104] + _ = x[FnStrtol-105] + _ = x[FnStrtoul-106] + _ = x[FnSkStorageGet-107] + _ = x[FnSkStorageDelete-108] + _ = x[FnSendSignal-109] + _ = x[FnTcpGenSyncookie-110] + _ = x[FnSkbOutput-111] + _ = x[FnProbeReadUser-112] + _ = x[FnProbeReadKernel-113] + _ = x[FnProbeReadUserStr-114] + _ = x[FnProbeReadKernelStr-115] + _ = x[FnTcpSendAck-116] + _ = x[FnSendSignalThread-117] + _ = x[FnJiffies64-118] + _ = x[FnReadBranchRecords-119] + _ = x[FnGetNsCurrentPidTgid-120] + _ = x[FnXdpOutput-121] + _ = x[FnGetNetnsCookie-122] + _ = x[FnGetCurrentAncestorCgroupId-123] + _ = x[FnSkAssign-124] + _ = x[FnKtimeGetBootNs-125] + _ = x[FnSeqPrintf-126] + _ = x[FnSeqWrite-127] + _ = x[FnSkCgroupId-128] + _ = x[FnSkAncestorCgroupId-129] + _ = x[FnRingbufOutput-130] + _ = x[FnRingbufReserve-131] + _ = x[FnRingbufSubmit-132] + _ = x[FnRingbufDiscard-133] + _ = x[FnRingbufQuery-134] + _ = x[FnCsumLevel-135] + _ = x[FnSkcToTcp6Sock-136] + _ = x[FnSkcToTcpSock-137] + _ = x[FnSkcToTcpTimewaitSock-138] + _ = x[FnSkcToTcpRequestSock-139] + _ = x[FnSkcToUdp6Sock-140] + _ = x[FnGetTaskStack-141] + _ = x[FnLoadHdrOpt-142] + _ = x[FnStoreHdrOpt-143] + _ = x[FnReserveHdrOpt-144] + _ = x[FnInodeStorageGet-145] + _ = x[FnInodeStorageDelete-146] + _ = x[FnDPath-147] + _ = x[FnCopyFromUser-148] + _ = x[FnSnprintfBtf-149] + _ = x[FnSeqPrintfBtf-150] + _ = x[FnSkbCgroupClassid-151] + _ = x[FnRedirectNeigh-152] + _ = x[FnPerCpuPtr-153] + _ = x[FnThisCpuPtr-154] + _ = x[FnRedirectPeer-155] + _ = x[FnTaskStorageGet-156] + _ = x[FnTaskStorageDelete-157] + _ = x[FnGetCurrentTaskBtf-158] + _ = x[FnBprmOptsSet-159] + _ = x[FnKtimeGetCoarseNs-160] + _ = x[FnImaInodeHash-161] + _ = x[FnSockFromFile-162] +} + +const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFile" + +var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424} + +func (i BuiltinFunc) String() string { + if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) { + return "BuiltinFunc(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _BuiltinFunc_name[_BuiltinFunc_index[i]:_BuiltinFunc_index[i+1]] +} diff --git a/vendor/github.com/cilium/ebpf/asm/instruction.go b/vendor/github.com/cilium/ebpf/asm/instruction.go new file mode 100644 index 0000000000000..e7ac0109e2dcc --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/instruction.go @@ -0,0 +1,506 @@ +package asm + +import ( + "crypto/sha1" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "io" + "math" + "strings" + + "github.com/cilium/ebpf/internal/unix" +) + +// InstructionSize is the size of a BPF instruction in bytes +const InstructionSize = 8 + +// RawInstructionOffset is an offset in units of raw BPF instructions. +type RawInstructionOffset uint64 + +// Bytes returns the offset of an instruction in bytes. +func (rio RawInstructionOffset) Bytes() uint64 { + return uint64(rio) * InstructionSize +} + +// Instruction is a single eBPF instruction. +type Instruction struct { + OpCode OpCode + Dst Register + Src Register + Offset int16 + Constant int64 + Reference string + Symbol string +} + +// Sym creates a symbol. +func (ins Instruction) Sym(name string) Instruction { + ins.Symbol = name + return ins +} + +// Unmarshal decodes a BPF instruction. +func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, error) { + var bi bpfInstruction + err := binary.Read(r, bo, &bi) + if err != nil { + return 0, err + } + + ins.OpCode = bi.OpCode + ins.Offset = bi.Offset + ins.Constant = int64(bi.Constant) + ins.Dst, ins.Src, err = bi.Registers.Unmarshal(bo) + if err != nil { + return 0, fmt.Errorf("can't unmarshal registers: %s", err) + } + + if !bi.OpCode.IsDWordLoad() { + return InstructionSize, nil + } + + var bi2 bpfInstruction + if err := binary.Read(r, bo, &bi2); err != nil { + // No Wrap, to avoid io.EOF clash + return 0, errors.New("64bit immediate is missing second half") + } + if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 { + return 0, errors.New("64bit immediate has non-zero fields") + } + ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant))) + + return 2 * InstructionSize, nil +} + +// Marshal encodes a BPF instruction. +func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) { + if ins.OpCode == InvalidOpCode { + return 0, errors.New("invalid opcode") + } + + isDWordLoad := ins.OpCode.IsDWordLoad() + + cons := int32(ins.Constant) + if isDWordLoad { + // Encode least significant 32bit first for 64bit operations. + cons = int32(uint32(ins.Constant)) + } + + regs, err := newBPFRegisters(ins.Dst, ins.Src, bo) + if err != nil { + return 0, fmt.Errorf("can't marshal registers: %s", err) + } + + bpfi := bpfInstruction{ + ins.OpCode, + regs, + ins.Offset, + cons, + } + + if err := binary.Write(w, bo, &bpfi); err != nil { + return 0, err + } + + if !isDWordLoad { + return InstructionSize, nil + } + + bpfi = bpfInstruction{ + Constant: int32(ins.Constant >> 32), + } + + if err := binary.Write(w, bo, &bpfi); err != nil { + return 0, err + } + + return 2 * InstructionSize, nil +} + +// RewriteMapPtr changes an instruction to use a new map fd. +// +// Returns an error if the instruction doesn't load a map. +func (ins *Instruction) RewriteMapPtr(fd int) error { + if !ins.OpCode.IsDWordLoad() { + return fmt.Errorf("%s is not a 64 bit load", ins.OpCode) + } + + if ins.Src != PseudoMapFD && ins.Src != PseudoMapValue { + return errors.New("not a load from a map") + } + + // Preserve the offset value for direct map loads. + offset := uint64(ins.Constant) & (math.MaxUint32 << 32) + rawFd := uint64(uint32(fd)) + ins.Constant = int64(offset | rawFd) + return nil +} + +// MapPtr returns the map fd for this instruction. +// +// The result is undefined if the instruction is not a load from a map, +// see IsLoadFromMap. +func (ins *Instruction) MapPtr() int { + return int(int32(uint64(ins.Constant) & math.MaxUint32)) +} + +// RewriteMapOffset changes the offset of a direct load from a map. +// +// Returns an error if the instruction is not a direct load. +func (ins *Instruction) RewriteMapOffset(offset uint32) error { + if !ins.OpCode.IsDWordLoad() { + return fmt.Errorf("%s is not a 64 bit load", ins.OpCode) + } + + if ins.Src != PseudoMapValue { + return errors.New("not a direct load from a map") + } + + fd := uint64(ins.Constant) & math.MaxUint32 + ins.Constant = int64(uint64(offset)<<32 | fd) + return nil +} + +func (ins *Instruction) mapOffset() uint32 { + return uint32(uint64(ins.Constant) >> 32) +} + +// IsLoadFromMap returns true if the instruction loads from a map. +// +// This covers both loading the map pointer and direct map value loads. +func (ins *Instruction) IsLoadFromMap() bool { + return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue) +} + +// IsFunctionCall returns true if the instruction calls another BPF function. +// +// This is not the same thing as a BPF helper call. +func (ins *Instruction) IsFunctionCall() bool { + return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall +} + +// IsConstantLoad returns true if the instruction loads a constant of the +// given size. +func (ins *Instruction) IsConstantLoad(size Size) bool { + return ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0 +} + +// Format implements fmt.Formatter. +func (ins Instruction) Format(f fmt.State, c rune) { + if c != 'v' { + fmt.Fprintf(f, "{UNRECOGNIZED: %c}", c) + return + } + + op := ins.OpCode + + if op == InvalidOpCode { + fmt.Fprint(f, "INVALID") + return + } + + // Omit trailing space for Exit + if op.JumpOp() == Exit { + fmt.Fprint(f, op) + return + } + + if ins.IsLoadFromMap() { + fd := ins.MapPtr() + switch ins.Src { + case PseudoMapFD: + fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd) + + case PseudoMapValue: + fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset()) + } + + goto ref + } + + fmt.Fprintf(f, "%v ", op) + switch cls := op.Class(); cls { + case LdClass, LdXClass, StClass, StXClass: + switch op.Mode() { + case ImmMode: + fmt.Fprintf(f, "dst: %s imm: %d", ins.Dst, ins.Constant) + case AbsMode: + fmt.Fprintf(f, "imm: %d", ins.Constant) + case IndMode: + fmt.Fprintf(f, "dst: %s src: %s imm: %d", ins.Dst, ins.Src, ins.Constant) + case MemMode: + fmt.Fprintf(f, "dst: %s src: %s off: %d imm: %d", ins.Dst, ins.Src, ins.Offset, ins.Constant) + case XAddMode: + fmt.Fprintf(f, "dst: %s src: %s", ins.Dst, ins.Src) + } + + case ALU64Class, ALUClass: + fmt.Fprintf(f, "dst: %s ", ins.Dst) + if op.ALUOp() == Swap || op.Source() == ImmSource { + fmt.Fprintf(f, "imm: %d", ins.Constant) + } else { + fmt.Fprintf(f, "src: %s", ins.Src) + } + + case JumpClass: + switch jop := op.JumpOp(); jop { + case Call: + if ins.Src == PseudoCall { + // bpf-to-bpf call + fmt.Fprint(f, ins.Constant) + } else { + fmt.Fprint(f, BuiltinFunc(ins.Constant)) + } + + default: + fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset) + if op.Source() == ImmSource { + fmt.Fprintf(f, "imm: %d", ins.Constant) + } else { + fmt.Fprintf(f, "src: %s", ins.Src) + } + } + } + +ref: + if ins.Reference != "" { + fmt.Fprintf(f, " <%s>", ins.Reference) + } +} + +// Instructions is an eBPF program. +type Instructions []Instruction + +func (insns Instructions) String() string { + return fmt.Sprint(insns) +} + +// RewriteMapPtr rewrites all loads of a specific map pointer to a new fd. +// +// Returns an error if the symbol isn't used, see IsUnreferencedSymbol. +func (insns Instructions) RewriteMapPtr(symbol string, fd int) error { + if symbol == "" { + return errors.New("empty symbol") + } + + found := false + for i := range insns { + ins := &insns[i] + if ins.Reference != symbol { + continue + } + + if err := ins.RewriteMapPtr(fd); err != nil { + return err + } + + found = true + } + + if !found { + return &unreferencedSymbolError{symbol} + } + + return nil +} + +// SymbolOffsets returns the set of symbols and their offset in +// the instructions. +func (insns Instructions) SymbolOffsets() (map[string]int, error) { + offsets := make(map[string]int) + + for i, ins := range insns { + if ins.Symbol == "" { + continue + } + + if _, ok := offsets[ins.Symbol]; ok { + return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol) + } + + offsets[ins.Symbol] = i + } + + return offsets, nil +} + +// ReferenceOffsets returns the set of references and their offset in +// the instructions. +func (insns Instructions) ReferenceOffsets() map[string][]int { + offsets := make(map[string][]int) + + for i, ins := range insns { + if ins.Reference == "" { + continue + } + + offsets[ins.Reference] = append(offsets[ins.Reference], i) + } + + return offsets +} + +// Format implements fmt.Formatter. +// +// You can control indentation of symbols by +// specifying a width. Setting a precision controls the indentation of +// instructions. +// The default character is a tab, which can be overridden by specifying +// the ' ' space flag. +func (insns Instructions) Format(f fmt.State, c rune) { + if c != 's' && c != 'v' { + fmt.Fprintf(f, "{UNKNOWN FORMAT '%c'}", c) + return + } + + // Precision is better in this case, because it allows + // specifying 0 padding easily. + padding, ok := f.Precision() + if !ok { + padding = 1 + } + + indent := strings.Repeat("\t", padding) + if f.Flag(' ') { + indent = strings.Repeat(" ", padding) + } + + symPadding, ok := f.Width() + if !ok { + symPadding = padding - 1 + } + if symPadding < 0 { + symPadding = 0 + } + + symIndent := strings.Repeat("\t", symPadding) + if f.Flag(' ') { + symIndent = strings.Repeat(" ", symPadding) + } + + // Guess how many digits we need at most, by assuming that all instructions + // are double wide. + highestOffset := len(insns) * 2 + offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset)))) + + iter := insns.Iterate() + for iter.Next() { + if iter.Ins.Symbol != "" { + fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol) + } + fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins) + } +} + +// Marshal encodes a BPF program into the kernel format. +func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error { + for i, ins := range insns { + _, err := ins.Marshal(w, bo) + if err != nil { + return fmt.Errorf("instruction %d: %w", i, err) + } + } + return nil +} + +// Tag calculates the kernel tag for a series of instructions. +// +// It mirrors bpf_prog_calc_tag in the kernel and so can be compared +// to ProgramInfo.Tag to figure out whether a loaded program matches +// certain instructions. +func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) { + h := sha1.New() + for i, ins := range insns { + if ins.IsLoadFromMap() { + ins.Constant = 0 + } + _, err := ins.Marshal(h, bo) + if err != nil { + return "", fmt.Errorf("instruction %d: %w", i, err) + } + } + return hex.EncodeToString(h.Sum(nil)[:unix.BPF_TAG_SIZE]), nil +} + +// Iterate allows iterating a BPF program while keeping track of +// various offsets. +// +// Modifying the instruction slice will lead to undefined behaviour. +func (insns Instructions) Iterate() *InstructionIterator { + return &InstructionIterator{insns: insns} +} + +// InstructionIterator iterates over a BPF program. +type InstructionIterator struct { + insns Instructions + // The instruction in question. + Ins *Instruction + // The index of the instruction in the original instruction slice. + Index int + // The offset of the instruction in raw BPF instructions. This accounts + // for double-wide instructions. + Offset RawInstructionOffset +} + +// Next returns true as long as there are any instructions remaining. +func (iter *InstructionIterator) Next() bool { + if len(iter.insns) == 0 { + return false + } + + if iter.Ins != nil { + iter.Index++ + iter.Offset += RawInstructionOffset(iter.Ins.OpCode.rawInstructions()) + } + iter.Ins = &iter.insns[0] + iter.insns = iter.insns[1:] + return true +} + +type bpfInstruction struct { + OpCode OpCode + Registers bpfRegisters + Offset int16 + Constant int32 +} + +type bpfRegisters uint8 + +func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) { + switch bo { + case binary.LittleEndian: + return bpfRegisters((src << 4) | (dst & 0xF)), nil + case binary.BigEndian: + return bpfRegisters((dst << 4) | (src & 0xF)), nil + default: + return 0, fmt.Errorf("unrecognized ByteOrder %T", bo) + } +} + +func (r bpfRegisters) Unmarshal(bo binary.ByteOrder) (dst, src Register, err error) { + switch bo { + case binary.LittleEndian: + return Register(r & 0xF), Register(r >> 4), nil + case binary.BigEndian: + return Register(r >> 4), Register(r & 0xf), nil + default: + return 0, 0, fmt.Errorf("unrecognized ByteOrder %T", bo) + } +} + +type unreferencedSymbolError struct { + symbol string +} + +func (use *unreferencedSymbolError) Error() string { + return fmt.Sprintf("unreferenced symbol %s", use.symbol) +} + +// IsUnreferencedSymbol returns true if err was caused by +// an unreferenced symbol. +func IsUnreferencedSymbol(err error) bool { + _, ok := err.(*unreferencedSymbolError) + return ok +} diff --git a/vendor/github.com/cilium/ebpf/asm/jump.go b/vendor/github.com/cilium/ebpf/asm/jump.go new file mode 100644 index 0000000000000..7757179de6491 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/jump.go @@ -0,0 +1,109 @@ +package asm + +//go:generate stringer -output jump_string.go -type=JumpOp + +// JumpOp affect control flow. +// +// msb lsb +// +----+-+---+ +// |OP |s|cls| +// +----+-+---+ +type JumpOp uint8 + +const jumpMask OpCode = aluMask + +const ( + // InvalidJumpOp is returned by getters when invoked + // on non branch OpCodes + InvalidJumpOp JumpOp = 0xff + // Ja jumps by offset unconditionally + Ja JumpOp = 0x00 + // JEq jumps by offset if r == imm + JEq JumpOp = 0x10 + // JGT jumps by offset if r > imm + JGT JumpOp = 0x20 + // JGE jumps by offset if r >= imm + JGE JumpOp = 0x30 + // JSet jumps by offset if r & imm + JSet JumpOp = 0x40 + // JNE jumps by offset if r != imm + JNE JumpOp = 0x50 + // JSGT jumps by offset if signed r > signed imm + JSGT JumpOp = 0x60 + // JSGE jumps by offset if signed r >= signed imm + JSGE JumpOp = 0x70 + // Call builtin or user defined function from imm + Call JumpOp = 0x80 + // Exit ends execution, with value in r0 + Exit JumpOp = 0x90 + // JLT jumps by offset if r < imm + JLT JumpOp = 0xa0 + // JLE jumps by offset if r <= imm + JLE JumpOp = 0xb0 + // JSLT jumps by offset if signed r < signed imm + JSLT JumpOp = 0xc0 + // JSLE jumps by offset if signed r <= signed imm + JSLE JumpOp = 0xd0 +) + +// Return emits an exit instruction. +// +// Requires a return value in R0. +func Return() Instruction { + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(Exit), + } +} + +// Op returns the OpCode for a given jump source. +func (op JumpOp) Op(source Source) OpCode { + return OpCode(JumpClass).SetJumpOp(op).SetSource(source) +} + +// Imm compares dst to value, and adjusts PC by offset if the condition is fulfilled. +func (op JumpOp) Imm(dst Register, value int32, label string) Instruction { + if op == Exit || op == Call || op == Ja { + return Instruction{OpCode: InvalidOpCode} + } + + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(op).SetSource(ImmSource), + Dst: dst, + Offset: -1, + Constant: int64(value), + Reference: label, + } +} + +// Reg compares dst to src, and adjusts PC by offset if the condition is fulfilled. +func (op JumpOp) Reg(dst, src Register, label string) Instruction { + if op == Exit || op == Call || op == Ja { + return Instruction{OpCode: InvalidOpCode} + } + + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(op).SetSource(RegSource), + Dst: dst, + Src: src, + Offset: -1, + Reference: label, + } +} + +// Label adjusts PC to the address of the label. +func (op JumpOp) Label(label string) Instruction { + if op == Call { + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(Call), + Src: PseudoCall, + Constant: -1, + Reference: label, + } + } + + return Instruction{ + OpCode: OpCode(JumpClass).SetJumpOp(op), + Offset: -1, + Reference: label, + } +} diff --git a/vendor/github.com/cilium/ebpf/asm/jump_string.go b/vendor/github.com/cilium/ebpf/asm/jump_string.go new file mode 100644 index 0000000000000..85a4aaffa5700 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/jump_string.go @@ -0,0 +1,53 @@ +// Code generated by "stringer -output jump_string.go -type=JumpOp"; DO NOT EDIT. + +package asm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidJumpOp-255] + _ = x[Ja-0] + _ = x[JEq-16] + _ = x[JGT-32] + _ = x[JGE-48] + _ = x[JSet-64] + _ = x[JNE-80] + _ = x[JSGT-96] + _ = x[JSGE-112] + _ = x[Call-128] + _ = x[Exit-144] + _ = x[JLT-160] + _ = x[JLE-176] + _ = x[JSLT-192] + _ = x[JSLE-208] +} + +const _JumpOp_name = "JaJEqJGTJGEJSetJNEJSGTJSGECallExitJLTJLEJSLTJSLEInvalidJumpOp" + +var _JumpOp_map = map[JumpOp]string{ + 0: _JumpOp_name[0:2], + 16: _JumpOp_name[2:5], + 32: _JumpOp_name[5:8], + 48: _JumpOp_name[8:11], + 64: _JumpOp_name[11:15], + 80: _JumpOp_name[15:18], + 96: _JumpOp_name[18:22], + 112: _JumpOp_name[22:26], + 128: _JumpOp_name[26:30], + 144: _JumpOp_name[30:34], + 160: _JumpOp_name[34:37], + 176: _JumpOp_name[37:40], + 192: _JumpOp_name[40:44], + 208: _JumpOp_name[44:48], + 255: _JumpOp_name[48:61], +} + +func (i JumpOp) String() string { + if str, ok := _JumpOp_map[i]; ok { + return str + } + return "JumpOp(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/vendor/github.com/cilium/ebpf/asm/load_store.go b/vendor/github.com/cilium/ebpf/asm/load_store.go new file mode 100644 index 0000000000000..85ed286b02b06 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/load_store.go @@ -0,0 +1,204 @@ +package asm + +//go:generate stringer -output load_store_string.go -type=Mode,Size + +// Mode for load and store operations +// +// msb lsb +// +---+--+---+ +// |MDE|sz|cls| +// +---+--+---+ +type Mode uint8 + +const modeMask OpCode = 0xe0 + +const ( + // InvalidMode is returned by getters when invoked + // on non load / store OpCodes + InvalidMode Mode = 0xff + // ImmMode - immediate value + ImmMode Mode = 0x00 + // AbsMode - immediate value + offset + AbsMode Mode = 0x20 + // IndMode - indirect (imm+src) + IndMode Mode = 0x40 + // MemMode - load from memory + MemMode Mode = 0x60 + // XAddMode - add atomically across processors. + XAddMode Mode = 0xc0 +) + +// Size of load and store operations +// +// msb lsb +// +---+--+---+ +// |mde|SZ|cls| +// +---+--+---+ +type Size uint8 + +const sizeMask OpCode = 0x18 + +const ( + // InvalidSize is returned by getters when invoked + // on non load / store OpCodes + InvalidSize Size = 0xff + // DWord - double word; 64 bits + DWord Size = 0x18 + // Word - word; 32 bits + Word Size = 0x00 + // Half - half-word; 16 bits + Half Size = 0x08 + // Byte - byte; 8 bits + Byte Size = 0x10 +) + +// Sizeof returns the size in bytes. +func (s Size) Sizeof() int { + switch s { + case DWord: + return 8 + case Word: + return 4 + case Half: + return 2 + case Byte: + return 1 + default: + return -1 + } +} + +// LoadMemOp returns the OpCode to load a value of given size from memory. +func LoadMemOp(size Size) OpCode { + return OpCode(LdXClass).SetMode(MemMode).SetSize(size) +} + +// LoadMem emits `dst = *(size *)(src + offset)`. +func LoadMem(dst, src Register, offset int16, size Size) Instruction { + return Instruction{ + OpCode: LoadMemOp(size), + Dst: dst, + Src: src, + Offset: offset, + } +} + +// LoadImmOp returns the OpCode to load an immediate of given size. +// +// As of kernel 4.20, only DWord size is accepted. +func LoadImmOp(size Size) OpCode { + return OpCode(LdClass).SetMode(ImmMode).SetSize(size) +} + +// LoadImm emits `dst = (size)value`. +// +// As of kernel 4.20, only DWord size is accepted. +func LoadImm(dst Register, value int64, size Size) Instruction { + return Instruction{ + OpCode: LoadImmOp(size), + Dst: dst, + Constant: value, + } +} + +// LoadMapPtr stores a pointer to a map in dst. +func LoadMapPtr(dst Register, fd int) Instruction { + if fd < 0 { + return Instruction{OpCode: InvalidOpCode} + } + + return Instruction{ + OpCode: LoadImmOp(DWord), + Dst: dst, + Src: PseudoMapFD, + Constant: int64(uint32(fd)), + } +} + +// LoadMapValue stores a pointer to the value at a certain offset of a map. +func LoadMapValue(dst Register, fd int, offset uint32) Instruction { + if fd < 0 { + return Instruction{OpCode: InvalidOpCode} + } + + fdAndOffset := (uint64(offset) << 32) | uint64(uint32(fd)) + return Instruction{ + OpCode: LoadImmOp(DWord), + Dst: dst, + Src: PseudoMapValue, + Constant: int64(fdAndOffset), + } +} + +// LoadIndOp returns the OpCode for loading a value of given size from an sk_buff. +func LoadIndOp(size Size) OpCode { + return OpCode(LdClass).SetMode(IndMode).SetSize(size) +} + +// LoadInd emits `dst = ntoh(*(size *)(((sk_buff *)R6)->data + src + offset))`. +func LoadInd(dst, src Register, offset int32, size Size) Instruction { + return Instruction{ + OpCode: LoadIndOp(size), + Dst: dst, + Src: src, + Constant: int64(offset), + } +} + +// LoadAbsOp returns the OpCode for loading a value of given size from an sk_buff. +func LoadAbsOp(size Size) OpCode { + return OpCode(LdClass).SetMode(AbsMode).SetSize(size) +} + +// LoadAbs emits `r0 = ntoh(*(size *)(((sk_buff *)R6)->data + offset))`. +func LoadAbs(offset int32, size Size) Instruction { + return Instruction{ + OpCode: LoadAbsOp(size), + Dst: R0, + Constant: int64(offset), + } +} + +// StoreMemOp returns the OpCode for storing a register of given size in memory. +func StoreMemOp(size Size) OpCode { + return OpCode(StXClass).SetMode(MemMode).SetSize(size) +} + +// StoreMem emits `*(size *)(dst + offset) = src` +func StoreMem(dst Register, offset int16, src Register, size Size) Instruction { + return Instruction{ + OpCode: StoreMemOp(size), + Dst: dst, + Src: src, + Offset: offset, + } +} + +// StoreImmOp returns the OpCode for storing an immediate of given size in memory. +func StoreImmOp(size Size) OpCode { + return OpCode(StClass).SetMode(MemMode).SetSize(size) +} + +// StoreImm emits `*(size *)(dst + offset) = value`. +func StoreImm(dst Register, offset int16, value int64, size Size) Instruction { + return Instruction{ + OpCode: StoreImmOp(size), + Dst: dst, + Offset: offset, + Constant: value, + } +} + +// StoreXAddOp returns the OpCode to atomically add a register to a value in memory. +func StoreXAddOp(size Size) OpCode { + return OpCode(StXClass).SetMode(XAddMode).SetSize(size) +} + +// StoreXAdd atomically adds src to *dst. +func StoreXAdd(dst, src Register, size Size) Instruction { + return Instruction{ + OpCode: StoreXAddOp(size), + Dst: dst, + Src: src, + } +} diff --git a/vendor/github.com/cilium/ebpf/asm/load_store_string.go b/vendor/github.com/cilium/ebpf/asm/load_store_string.go new file mode 100644 index 0000000000000..76d29a0756c6e --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/load_store_string.go @@ -0,0 +1,80 @@ +// Code generated by "stringer -output load_store_string.go -type=Mode,Size"; DO NOT EDIT. + +package asm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidMode-255] + _ = x[ImmMode-0] + _ = x[AbsMode-32] + _ = x[IndMode-64] + _ = x[MemMode-96] + _ = x[XAddMode-192] +} + +const ( + _Mode_name_0 = "ImmMode" + _Mode_name_1 = "AbsMode" + _Mode_name_2 = "IndMode" + _Mode_name_3 = "MemMode" + _Mode_name_4 = "XAddMode" + _Mode_name_5 = "InvalidMode" +) + +func (i Mode) String() string { + switch { + case i == 0: + return _Mode_name_0 + case i == 32: + return _Mode_name_1 + case i == 64: + return _Mode_name_2 + case i == 96: + return _Mode_name_3 + case i == 192: + return _Mode_name_4 + case i == 255: + return _Mode_name_5 + default: + return "Mode(" + strconv.FormatInt(int64(i), 10) + ")" + } +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[InvalidSize-255] + _ = x[DWord-24] + _ = x[Word-0] + _ = x[Half-8] + _ = x[Byte-16] +} + +const ( + _Size_name_0 = "Word" + _Size_name_1 = "Half" + _Size_name_2 = "Byte" + _Size_name_3 = "DWord" + _Size_name_4 = "InvalidSize" +) + +func (i Size) String() string { + switch { + case i == 0: + return _Size_name_0 + case i == 8: + return _Size_name_1 + case i == 16: + return _Size_name_2 + case i == 24: + return _Size_name_3 + case i == 255: + return _Size_name_4 + default: + return "Size(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/vendor/github.com/cilium/ebpf/asm/opcode.go b/vendor/github.com/cilium/ebpf/asm/opcode.go new file mode 100644 index 0000000000000..6edc3cf5917d0 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/opcode.go @@ -0,0 +1,237 @@ +package asm + +import ( + "fmt" + "strings" +) + +//go:generate stringer -output opcode_string.go -type=Class + +type encoding int + +const ( + unknownEncoding encoding = iota + loadOrStore + jumpOrALU +) + +// Class of operations +// +// msb lsb +// +---+--+---+ +// | ?? |CLS| +// +---+--+---+ +type Class uint8 + +const classMask OpCode = 0x07 + +const ( + // LdClass load memory + LdClass Class = 0x00 + // LdXClass load memory from constant + LdXClass Class = 0x01 + // StClass load register from memory + StClass Class = 0x02 + // StXClass load register from constant + StXClass Class = 0x03 + // ALUClass arithmetic operators + ALUClass Class = 0x04 + // JumpClass jump operators + JumpClass Class = 0x05 + // ALU64Class arithmetic in 64 bit mode + ALU64Class Class = 0x07 +) + +func (cls Class) encoding() encoding { + switch cls { + case LdClass, LdXClass, StClass, StXClass: + return loadOrStore + case ALU64Class, ALUClass, JumpClass: + return jumpOrALU + default: + return unknownEncoding + } +} + +// OpCode is a packed eBPF opcode. +// +// Its encoding is defined by a Class value: +// +// msb lsb +// +----+-+---+ +// | ???? |CLS| +// +----+-+---+ +type OpCode uint8 + +// InvalidOpCode is returned by setters on OpCode +const InvalidOpCode OpCode = 0xff + +// rawInstructions returns the number of BPF instructions required +// to encode this opcode. +func (op OpCode) rawInstructions() int { + if op.IsDWordLoad() { + return 2 + } + return 1 +} + +func (op OpCode) IsDWordLoad() bool { + return op == LoadImmOp(DWord) +} + +// Class returns the class of operation. +func (op OpCode) Class() Class { + return Class(op & classMask) +} + +// Mode returns the mode for load and store operations. +func (op OpCode) Mode() Mode { + if op.Class().encoding() != loadOrStore { + return InvalidMode + } + return Mode(op & modeMask) +} + +// Size returns the size for load and store operations. +func (op OpCode) Size() Size { + if op.Class().encoding() != loadOrStore { + return InvalidSize + } + return Size(op & sizeMask) +} + +// Source returns the source for branch and ALU operations. +func (op OpCode) Source() Source { + if op.Class().encoding() != jumpOrALU || op.ALUOp() == Swap { + return InvalidSource + } + return Source(op & sourceMask) +} + +// ALUOp returns the ALUOp. +func (op OpCode) ALUOp() ALUOp { + if op.Class().encoding() != jumpOrALU { + return InvalidALUOp + } + return ALUOp(op & aluMask) +} + +// Endianness returns the Endianness for a byte swap instruction. +func (op OpCode) Endianness() Endianness { + if op.ALUOp() != Swap { + return InvalidEndian + } + return Endianness(op & endianMask) +} + +// JumpOp returns the JumpOp. +func (op OpCode) JumpOp() JumpOp { + if op.Class().encoding() != jumpOrALU { + return InvalidJumpOp + } + return JumpOp(op & jumpMask) +} + +// SetMode sets the mode on load and store operations. +// +// Returns InvalidOpCode if op is of the wrong class. +func (op OpCode) SetMode(mode Mode) OpCode { + if op.Class().encoding() != loadOrStore || !valid(OpCode(mode), modeMask) { + return InvalidOpCode + } + return (op & ^modeMask) | OpCode(mode) +} + +// SetSize sets the size on load and store operations. +// +// Returns InvalidOpCode if op is of the wrong class. +func (op OpCode) SetSize(size Size) OpCode { + if op.Class().encoding() != loadOrStore || !valid(OpCode(size), sizeMask) { + return InvalidOpCode + } + return (op & ^sizeMask) | OpCode(size) +} + +// SetSource sets the source on jump and ALU operations. +// +// Returns InvalidOpCode if op is of the wrong class. +func (op OpCode) SetSource(source Source) OpCode { + if op.Class().encoding() != jumpOrALU || !valid(OpCode(source), sourceMask) { + return InvalidOpCode + } + return (op & ^sourceMask) | OpCode(source) +} + +// SetALUOp sets the ALUOp on ALU operations. +// +// Returns InvalidOpCode if op is of the wrong class. +func (op OpCode) SetALUOp(alu ALUOp) OpCode { + class := op.Class() + if (class != ALUClass && class != ALU64Class) || !valid(OpCode(alu), aluMask) { + return InvalidOpCode + } + return (op & ^aluMask) | OpCode(alu) +} + +// SetJumpOp sets the JumpOp on jump operations. +// +// Returns InvalidOpCode if op is of the wrong class. +func (op OpCode) SetJumpOp(jump JumpOp) OpCode { + if op.Class() != JumpClass || !valid(OpCode(jump), jumpMask) { + return InvalidOpCode + } + return (op & ^jumpMask) | OpCode(jump) +} + +func (op OpCode) String() string { + var f strings.Builder + + switch class := op.Class(); class { + case LdClass, LdXClass, StClass, StXClass: + f.WriteString(strings.TrimSuffix(class.String(), "Class")) + + mode := op.Mode() + f.WriteString(strings.TrimSuffix(mode.String(), "Mode")) + + switch op.Size() { + case DWord: + f.WriteString("DW") + case Word: + f.WriteString("W") + case Half: + f.WriteString("H") + case Byte: + f.WriteString("B") + } + + case ALU64Class, ALUClass: + f.WriteString(op.ALUOp().String()) + + if op.ALUOp() == Swap { + // Width for Endian is controlled by Constant + f.WriteString(op.Endianness().String()) + } else { + if class == ALUClass { + f.WriteString("32") + } + + f.WriteString(strings.TrimSuffix(op.Source().String(), "Source")) + } + + case JumpClass: + f.WriteString(op.JumpOp().String()) + if jop := op.JumpOp(); jop != Exit && jop != Call { + f.WriteString(strings.TrimSuffix(op.Source().String(), "Source")) + } + + default: + fmt.Fprintf(&f, "OpCode(%#x)", uint8(op)) + } + + return f.String() +} + +// valid returns true if all bits in value are covered by mask. +func valid(value, mask OpCode) bool { + return value & ^mask == 0 +} diff --git a/vendor/github.com/cilium/ebpf/asm/opcode_string.go b/vendor/github.com/cilium/ebpf/asm/opcode_string.go new file mode 100644 index 0000000000000..079ce1db0b83a --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/opcode_string.go @@ -0,0 +1,38 @@ +// Code generated by "stringer -output opcode_string.go -type=Class"; DO NOT EDIT. + +package asm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[LdClass-0] + _ = x[LdXClass-1] + _ = x[StClass-2] + _ = x[StXClass-3] + _ = x[ALUClass-4] + _ = x[JumpClass-5] + _ = x[ALU64Class-7] +} + +const ( + _Class_name_0 = "LdClassLdXClassStClassStXClassALUClassJumpClass" + _Class_name_1 = "ALU64Class" +) + +var ( + _Class_index_0 = [...]uint8{0, 7, 15, 22, 30, 38, 47} +) + +func (i Class) String() string { + switch { + case 0 <= i && i <= 5: + return _Class_name_0[_Class_index_0[i]:_Class_index_0[i+1]] + case i == 7: + return _Class_name_1 + default: + return "Class(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/vendor/github.com/cilium/ebpf/asm/register.go b/vendor/github.com/cilium/ebpf/asm/register.go new file mode 100644 index 0000000000000..76cb44bffc716 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/asm/register.go @@ -0,0 +1,49 @@ +package asm + +import ( + "fmt" +) + +// Register is the source or destination of most operations. +type Register uint8 + +// R0 contains return values. +const R0 Register = 0 + +// Registers for function arguments. +const ( + R1 Register = R0 + 1 + iota + R2 + R3 + R4 + R5 +) + +// Callee saved registers preserved by function calls. +const ( + R6 Register = R5 + 1 + iota + R7 + R8 + R9 +) + +// Read-only frame pointer to access stack. +const ( + R10 Register = R9 + 1 + RFP = R10 +) + +// Pseudo registers used by 64bit loads and jumps +const ( + PseudoMapFD = R1 // BPF_PSEUDO_MAP_FD + PseudoMapValue = R2 // BPF_PSEUDO_MAP_VALUE + PseudoCall = R1 // BPF_PSEUDO_CALL +) + +func (r Register) String() string { + v := uint8(r) + if v == 10 { + return "rfp" + } + return fmt.Sprintf("r%d", v) +} diff --git a/vendor/github.com/cilium/ebpf/collection.go b/vendor/github.com/cilium/ebpf/collection.go new file mode 100644 index 0000000000000..17cc69492ea95 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/collection.go @@ -0,0 +1,616 @@ +package ebpf + +import ( + "errors" + "fmt" + "io" + "math" + "reflect" + "strings" + + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" +) + +// CollectionOptions control loading a collection into the kernel. +// +// Maps and Programs are passed to NewMapWithOptions and NewProgramsWithOptions. +type CollectionOptions struct { + Maps MapOptions + Programs ProgramOptions +} + +// CollectionSpec describes a collection. +type CollectionSpec struct { + Maps map[string]*MapSpec + Programs map[string]*ProgramSpec +} + +// Copy returns a recursive copy of the spec. +func (cs *CollectionSpec) Copy() *CollectionSpec { + if cs == nil { + return nil + } + + cpy := CollectionSpec{ + Maps: make(map[string]*MapSpec, len(cs.Maps)), + Programs: make(map[string]*ProgramSpec, len(cs.Programs)), + } + + for name, spec := range cs.Maps { + cpy.Maps[name] = spec.Copy() + } + + for name, spec := range cs.Programs { + cpy.Programs[name] = spec.Copy() + } + + return &cpy +} + +// RewriteMaps replaces all references to specific maps. +// +// Use this function to use pre-existing maps instead of creating new ones +// when calling NewCollection. Any named maps are removed from CollectionSpec.Maps. +// +// Returns an error if a named map isn't used in at least one program. +func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error { + for symbol, m := range maps { + // have we seen a program that uses this symbol / map + seen := false + fd := m.FD() + for progName, progSpec := range cs.Programs { + err := progSpec.Instructions.RewriteMapPtr(symbol, fd) + + switch { + case err == nil: + seen = true + + case asm.IsUnreferencedSymbol(err): + // Not all programs need to use the map + + default: + return fmt.Errorf("program %s: %w", progName, err) + } + } + + if !seen { + return fmt.Errorf("map %s not referenced by any programs", symbol) + } + + // Prevent NewCollection from creating rewritten maps + delete(cs.Maps, symbol) + } + + return nil +} + +// RewriteConstants replaces the value of multiple constants. +// +// The constant must be defined like so in the C program: +// +// volatile const type foobar; +// volatile const type foobar = default; +// +// Replacement values must be of the same length as the C sizeof(type). +// If necessary, they are marshalled according to the same rules as +// map values. +// +// From Linux 5.5 the verifier will use constants to eliminate dead code. +// +// Returns an error if a constant doesn't exist. +func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error { + rodata := cs.Maps[".rodata"] + if rodata == nil { + return errors.New("missing .rodata section") + } + + if rodata.BTF == nil { + return errors.New(".rodata section has no BTF") + } + + if n := len(rodata.Contents); n != 1 { + return fmt.Errorf("expected one key in .rodata, found %d", n) + } + + kv := rodata.Contents[0] + value, ok := kv.Value.([]byte) + if !ok { + return fmt.Errorf("first value in .rodata is %T not []byte", kv.Value) + } + + buf := make([]byte, len(value)) + copy(buf, value) + + err := patchValue(buf, btf.MapValue(rodata.BTF), consts) + if err != nil { + return err + } + + rodata.Contents[0] = MapKV{kv.Key, buf} + return nil +} + +// Assign the contents of a CollectionSpec to a struct. +// +// This function is a short-cut to manually checking the presence +// of maps and programs in a collection spec. Consider using bpf2go if this +// sounds useful. +// +// The argument to must be a pointer to a struct. A field of the +// struct is updated with values from Programs or Maps if it +// has an `ebpf` tag and its type is *ProgramSpec or *MapSpec. +// The tag gives the name of the program or map as found in +// the CollectionSpec. +// +// struct { +// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` +// Bar *ebpf.MapSpec `ebpf:"bar_map"` +// Ignored int +// } +// +// Returns an error if any of the fields can't be found, or +// if the same map or program is assigned multiple times. +func (cs *CollectionSpec) Assign(to interface{}) error { + valueOf := func(typ reflect.Type, name string) (reflect.Value, error) { + switch typ { + case reflect.TypeOf((*ProgramSpec)(nil)): + p := cs.Programs[name] + if p == nil { + return reflect.Value{}, fmt.Errorf("missing program %q", name) + } + return reflect.ValueOf(p), nil + case reflect.TypeOf((*MapSpec)(nil)): + m := cs.Maps[name] + if m == nil { + return reflect.Value{}, fmt.Errorf("missing map %q", name) + } + return reflect.ValueOf(m), nil + default: + return reflect.Value{}, fmt.Errorf("unsupported type %s", typ) + } + } + + return assignValues(to, valueOf) +} + +// LoadAndAssign maps and programs into the kernel and assign them to a struct. +// +// This function is a short-cut to manually checking the presence +// of maps and programs in a collection spec. Consider using bpf2go if this +// sounds useful. +// +// The argument to must be a pointer to a struct. A field of the +// struct is updated with values from Programs or Maps if it +// has an `ebpf` tag and its type is *Program or *Map. +// The tag gives the name of the program or map as found in +// the CollectionSpec. +// +// struct { +// Foo *ebpf.Program `ebpf:"xdp_foo"` +// Bar *ebpf.Map `ebpf:"bar_map"` +// Ignored int +// } +// +// opts may be nil. +// +// Returns an error if any of the fields can't be found, or +// if the same map or program is assigned multiple times. +func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error { + if opts == nil { + opts = &CollectionOptions{} + } + + loadMap, loadProgram, done, cleanup := lazyLoadCollection(cs, opts) + defer cleanup() + + valueOf := func(typ reflect.Type, name string) (reflect.Value, error) { + switch typ { + case reflect.TypeOf((*Program)(nil)): + p, err := loadProgram(name) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(p), nil + case reflect.TypeOf((*Map)(nil)): + m, err := loadMap(name) + if err != nil { + return reflect.Value{}, err + } + return reflect.ValueOf(m), nil + default: + return reflect.Value{}, fmt.Errorf("unsupported type %s", typ) + } + } + + if err := assignValues(to, valueOf); err != nil { + return err + } + + done() + return nil +} + +// Collection is a collection of Programs and Maps associated +// with their symbols +type Collection struct { + Programs map[string]*Program + Maps map[string]*Map +} + +// NewCollection creates a Collection from a specification. +func NewCollection(spec *CollectionSpec) (*Collection, error) { + return NewCollectionWithOptions(spec, CollectionOptions{}) +} + +// NewCollectionWithOptions creates a Collection from a specification. +func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) { + loadMap, loadProgram, done, cleanup := lazyLoadCollection(spec, &opts) + defer cleanup() + + for mapName := range spec.Maps { + _, err := loadMap(mapName) + if err != nil { + return nil, err + } + } + + for progName := range spec.Programs { + _, err := loadProgram(progName) + if err != nil { + return nil, err + } + } + + maps, progs := done() + return &Collection{ + progs, + maps, + }, nil +} + +type handleCache struct { + btfHandles map[*btf.Spec]*btf.Handle + btfSpecs map[io.ReaderAt]*btf.Spec +} + +func newHandleCache() *handleCache { + return &handleCache{ + btfHandles: make(map[*btf.Spec]*btf.Handle), + btfSpecs: make(map[io.ReaderAt]*btf.Spec), + } +} + +func (hc handleCache) btfHandle(spec *btf.Spec) (*btf.Handle, error) { + if hc.btfHandles[spec] != nil { + return hc.btfHandles[spec], nil + } + + handle, err := btf.NewHandle(spec) + if err != nil { + return nil, err + } + + hc.btfHandles[spec] = handle + return handle, nil +} + +func (hc handleCache) btfSpec(rd io.ReaderAt) (*btf.Spec, error) { + if hc.btfSpecs[rd] != nil { + return hc.btfSpecs[rd], nil + } + + spec, err := btf.LoadSpecFromReader(rd) + if err != nil { + return nil, err + } + + hc.btfSpecs[rd] = spec + return spec, nil +} + +func (hc handleCache) close() { + for _, handle := range hc.btfHandles { + handle.Close() + } + hc.btfHandles = nil + hc.btfSpecs = nil +} + +func lazyLoadCollection(coll *CollectionSpec, opts *CollectionOptions) ( + loadMap func(string) (*Map, error), + loadProgram func(string) (*Program, error), + done func() (map[string]*Map, map[string]*Program), + cleanup func(), +) { + var ( + maps = make(map[string]*Map) + progs = make(map[string]*Program) + handles = newHandleCache() + skipMapsAndProgs = false + ) + + cleanup = func() { + handles.close() + + if skipMapsAndProgs { + return + } + + for _, m := range maps { + m.Close() + } + + for _, p := range progs { + p.Close() + } + } + + done = func() (map[string]*Map, map[string]*Program) { + skipMapsAndProgs = true + return maps, progs + } + + loadMap = func(mapName string) (*Map, error) { + if m := maps[mapName]; m != nil { + return m, nil + } + + mapSpec := coll.Maps[mapName] + if mapSpec == nil { + return nil, fmt.Errorf("missing map %s", mapName) + } + + m, err := newMapWithOptions(mapSpec, opts.Maps, handles) + if err != nil { + return nil, fmt.Errorf("map %s: %w", mapName, err) + } + + maps[mapName] = m + return m, nil + } + + loadProgram = func(progName string) (*Program, error) { + if prog := progs[progName]; prog != nil { + return prog, nil + } + + progSpec := coll.Programs[progName] + if progSpec == nil { + return nil, fmt.Errorf("unknown program %s", progName) + } + + progSpec = progSpec.Copy() + + // Rewrite any reference to a valid map. + for i := range progSpec.Instructions { + ins := &progSpec.Instructions[i] + + if !ins.IsLoadFromMap() || ins.Reference == "" { + continue + } + + if uint32(ins.Constant) != math.MaxUint32 { + // Don't overwrite maps already rewritten, users can + // rewrite programs in the spec themselves + continue + } + + m, err := loadMap(ins.Reference) + if err != nil { + return nil, fmt.Errorf("program %s: %w", progName, err) + } + + fd := m.FD() + if fd < 0 { + return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd) + } + if err := ins.RewriteMapPtr(m.FD()); err != nil { + return nil, fmt.Errorf("progam %s: map %s: %w", progName, ins.Reference, err) + } + } + + prog, err := newProgramWithOptions(progSpec, opts.Programs, handles) + if err != nil { + return nil, fmt.Errorf("program %s: %w", progName, err) + } + + progs[progName] = prog + return prog, nil + } + + return +} + +// LoadCollection parses an object file and converts it to a collection. +func LoadCollection(file string) (*Collection, error) { + spec, err := LoadCollectionSpec(file) + if err != nil { + return nil, err + } + return NewCollection(spec) +} + +// Close frees all maps and programs associated with the collection. +// +// The collection mustn't be used afterwards. +func (coll *Collection) Close() { + for _, prog := range coll.Programs { + prog.Close() + } + for _, m := range coll.Maps { + m.Close() + } +} + +// DetachMap removes the named map from the Collection. +// +// This means that a later call to Close() will not affect this map. +// +// Returns nil if no map of that name exists. +func (coll *Collection) DetachMap(name string) *Map { + m := coll.Maps[name] + delete(coll.Maps, name) + return m +} + +// DetachProgram removes the named program from the Collection. +// +// This means that a later call to Close() will not affect this program. +// +// Returns nil if no program of that name exists. +func (coll *Collection) DetachProgram(name string) *Program { + p := coll.Programs[name] + delete(coll.Programs, name) + return p +} + +// Assign the contents of a collection to a struct. +// +// Deprecated: use CollectionSpec.Assign instead. It provides the same +// functionality but creates only the maps and programs requested. +func (coll *Collection) Assign(to interface{}) error { + assignedMaps := make(map[string]struct{}) + assignedPrograms := make(map[string]struct{}) + valueOf := func(typ reflect.Type, name string) (reflect.Value, error) { + switch typ { + case reflect.TypeOf((*Program)(nil)): + p := coll.Programs[name] + if p == nil { + return reflect.Value{}, fmt.Errorf("missing program %q", name) + } + assignedPrograms[name] = struct{}{} + return reflect.ValueOf(p), nil + case reflect.TypeOf((*Map)(nil)): + m := coll.Maps[name] + if m == nil { + return reflect.Value{}, fmt.Errorf("missing map %q", name) + } + assignedMaps[name] = struct{}{} + return reflect.ValueOf(m), nil + default: + return reflect.Value{}, fmt.Errorf("unsupported type %s", typ) + } + } + + if err := assignValues(to, valueOf); err != nil { + return err + } + + for name := range assignedPrograms { + coll.DetachProgram(name) + } + + for name := range assignedMaps { + coll.DetachMap(name) + } + + return nil +} + +func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Value, error)) error { + type structField struct { + reflect.StructField + value reflect.Value + } + + var ( + fields []structField + visitedTypes = make(map[reflect.Type]bool) + flattenStruct func(reflect.Value) error + ) + + flattenStruct = func(structVal reflect.Value) error { + structType := structVal.Type() + if structType.Kind() != reflect.Struct { + return fmt.Errorf("%s is not a struct", structType) + } + + if visitedTypes[structType] { + return fmt.Errorf("recursion on type %s", structType) + } + + for i := 0; i < structType.NumField(); i++ { + field := structField{structType.Field(i), structVal.Field(i)} + + name := field.Tag.Get("ebpf") + if name != "" { + fields = append(fields, field) + continue + } + + var err error + switch field.Type.Kind() { + case reflect.Ptr: + if field.Type.Elem().Kind() != reflect.Struct { + continue + } + + if field.value.IsNil() { + return fmt.Errorf("nil pointer to %s", structType) + } + + err = flattenStruct(field.value.Elem()) + + case reflect.Struct: + err = flattenStruct(field.value) + + default: + continue + } + + if err != nil { + return fmt.Errorf("field %s: %w", field.Name, err) + } + } + + return nil + } + + toValue := reflect.ValueOf(to) + if toValue.Type().Kind() != reflect.Ptr { + return fmt.Errorf("%T is not a pointer to struct", to) + } + + if toValue.IsNil() { + return fmt.Errorf("nil pointer to %T", to) + } + + if err := flattenStruct(toValue.Elem()); err != nil { + return err + } + + type elem struct { + // Either *Map or *Program + typ reflect.Type + name string + } + + assignedTo := make(map[elem]string) + for _, field := range fields { + name := field.Tag.Get("ebpf") + if strings.Contains(name, ",") { + return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name) + } + + e := elem{field.Type, name} + if assignedField := assignedTo[e]; assignedField != "" { + return fmt.Errorf("field %s: %q was already assigned to %s", field.Name, name, assignedField) + } + + value, err := valueOf(field.Type, name) + if err != nil { + return fmt.Errorf("field %s: %w", field.Name, err) + } + + if !field.value.CanSet() { + return fmt.Errorf("field %s: can't set value", field.Name) + } + + field.value.Set(value) + assignedTo[e] = field.Name + } + + return nil +} diff --git a/vendor/github.com/cilium/ebpf/doc.go b/vendor/github.com/cilium/ebpf/doc.go new file mode 100644 index 0000000000000..f7f34da8f44c2 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/doc.go @@ -0,0 +1,16 @@ +// Package ebpf is a toolkit for working with eBPF programs. +// +// eBPF programs are small snippets of code which are executed directly +// in a VM in the Linux kernel, which makes them very fast and flexible. +// Many Linux subsystems now accept eBPF programs. This makes it possible +// to implement highly application specific logic inside the kernel, +// without having to modify the actual kernel itself. +// +// This package is designed for long-running processes which +// want to use eBPF to implement part of their application logic. It has no +// run-time dependencies outside of the library and the Linux kernel itself. +// eBPF code should be compiled ahead of time using clang, and shipped with +// your application as any other resource. +// +// Use the link subpackage to attach a loaded program to a hook in the kernel. +package ebpf diff --git a/vendor/github.com/cilium/ebpf/elf_reader.go b/vendor/github.com/cilium/ebpf/elf_reader.go new file mode 100644 index 0000000000000..c2afbc36a5a9e --- /dev/null +++ b/vendor/github.com/cilium/ebpf/elf_reader.go @@ -0,0 +1,953 @@ +package ebpf + +import ( + "bufio" + "bytes" + "debug/elf" + "encoding/binary" + "errors" + "fmt" + "io" + "math" + "os" + "strings" + + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" + "github.com/cilium/ebpf/internal/unix" +) + +// elfCode is a convenience to reduce the amount of arguments that have to +// be passed around explicitly. You should treat it's contents as immutable. +type elfCode struct { + *internal.SafeELFFile + sections map[elf.SectionIndex]*elfSection + license string + version uint32 + btf *btf.Spec +} + +// LoadCollectionSpec parses an ELF file into a CollectionSpec. +func LoadCollectionSpec(file string) (*CollectionSpec, error) { + f, err := os.Open(file) + if err != nil { + return nil, err + } + defer f.Close() + + spec, err := LoadCollectionSpecFromReader(f) + if err != nil { + return nil, fmt.Errorf("file %s: %w", file, err) + } + return spec, nil +} + +// LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec. +func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { + f, err := internal.NewSafeELFFile(rd) + if err != nil { + return nil, err + } + defer f.Close() + + var ( + licenseSection *elf.Section + versionSection *elf.Section + sections = make(map[elf.SectionIndex]*elfSection) + relSections = make(map[elf.SectionIndex]*elf.Section) + ) + + // This is the target of relocations generated by inline assembly. + sections[elf.SHN_UNDEF] = newElfSection(new(elf.Section), undefSection) + + // Collect all the sections we're interested in. This includes relocations + // which we parse later. + for i, sec := range f.Sections { + idx := elf.SectionIndex(i) + + switch { + case strings.HasPrefix(sec.Name, "license"): + licenseSection = sec + case strings.HasPrefix(sec.Name, "version"): + versionSection = sec + case strings.HasPrefix(sec.Name, "maps"): + sections[idx] = newElfSection(sec, mapSection) + case sec.Name == ".maps": + sections[idx] = newElfSection(sec, btfMapSection) + case sec.Name == ".bss" || sec.Name == ".data" || strings.HasPrefix(sec.Name, ".rodata"): + sections[idx] = newElfSection(sec, dataSection) + case sec.Type == elf.SHT_REL: + // Store relocations under the section index of the target + relSections[elf.SectionIndex(sec.Info)] = sec + case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0: + sections[idx] = newElfSection(sec, programSection) + } + } + + license, err := loadLicense(licenseSection) + if err != nil { + return nil, fmt.Errorf("load license: %w", err) + } + + version, err := loadVersion(versionSection, f.ByteOrder) + if err != nil { + return nil, fmt.Errorf("load version: %w", err) + } + + btfSpec, err := btf.LoadSpecFromReader(rd) + if err != nil && !errors.Is(err, btf.ErrNotFound) { + return nil, fmt.Errorf("load BTF: %w", err) + } + + // Assign symbols to all the sections we're interested in. + symbols, err := f.Symbols() + if err != nil { + return nil, fmt.Errorf("load symbols: %v", err) + } + + for _, symbol := range symbols { + idx := symbol.Section + symType := elf.ST_TYPE(symbol.Info) + + section := sections[idx] + if section == nil { + continue + } + + // Older versions of LLVM don't tag symbols correctly, so keep + // all NOTYPE ones. + keep := symType == elf.STT_NOTYPE + switch section.kind { + case mapSection, btfMapSection, dataSection: + keep = keep || symType == elf.STT_OBJECT + case programSection: + keep = keep || symType == elf.STT_FUNC + } + if !keep || symbol.Name == "" { + continue + } + + section.symbols[symbol.Value] = symbol + } + + ec := &elfCode{ + SafeELFFile: f, + sections: sections, + license: license, + version: version, + btf: btfSpec, + } + + // Go through relocation sections, and parse the ones for sections we're + // interested in. Make sure that relocations point at valid sections. + for idx, relSection := range relSections { + section := sections[idx] + if section == nil { + continue + } + + rels, err := ec.loadRelocations(relSection, symbols) + if err != nil { + return nil, fmt.Errorf("relocation for section %q: %w", section.Name, err) + } + + for _, rel := range rels { + target := sections[rel.Section] + if target == nil { + return nil, fmt.Errorf("section %q: reference to %q in section %s: %w", section.Name, rel.Name, rel.Section, ErrNotSupported) + } + + if target.Flags&elf.SHF_STRINGS > 0 { + return nil, fmt.Errorf("section %q: string is not stack allocated: %w", section.Name, ErrNotSupported) + } + + target.references++ + } + + section.relocations = rels + } + + // Collect all the various ways to define maps. + maps := make(map[string]*MapSpec) + if err := ec.loadMaps(maps); err != nil { + return nil, fmt.Errorf("load maps: %w", err) + } + + if err := ec.loadBTFMaps(maps); err != nil { + return nil, fmt.Errorf("load BTF maps: %w", err) + } + + if err := ec.loadDataSections(maps); err != nil { + return nil, fmt.Errorf("load data sections: %w", err) + } + + // Finally, collect programs and link them. + progs, err := ec.loadPrograms() + if err != nil { + return nil, fmt.Errorf("load programs: %w", err) + } + + return &CollectionSpec{maps, progs}, nil +} + +func loadLicense(sec *elf.Section) (string, error) { + if sec == nil { + return "", nil + } + + data, err := sec.Data() + if err != nil { + return "", fmt.Errorf("section %s: %v", sec.Name, err) + } + return string(bytes.TrimRight(data, "\000")), nil +} + +func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) { + if sec == nil { + return 0, nil + } + + var version uint32 + if err := binary.Read(sec.Open(), bo, &version); err != nil { + return 0, fmt.Errorf("section %s: %v", sec.Name, err) + } + return version, nil +} + +type elfSectionKind int + +const ( + undefSection elfSectionKind = iota + mapSection + btfMapSection + programSection + dataSection +) + +type elfSection struct { + *elf.Section + kind elfSectionKind + // Offset from the start of the section to a symbol + symbols map[uint64]elf.Symbol + // Offset from the start of the section to a relocation, which points at + // a symbol in another section. + relocations map[uint64]elf.Symbol + // The number of relocations pointing at this section. + references int +} + +func newElfSection(section *elf.Section, kind elfSectionKind) *elfSection { + return &elfSection{ + section, + kind, + make(map[uint64]elf.Symbol), + make(map[uint64]elf.Symbol), + 0, + } +} + +func (ec *elfCode) loadPrograms() (map[string]*ProgramSpec, error) { + var ( + progs []*ProgramSpec + libs []*ProgramSpec + ) + + for _, sec := range ec.sections { + if sec.kind != programSection { + continue + } + + if len(sec.symbols) == 0 { + return nil, fmt.Errorf("section %v: missing symbols", sec.Name) + } + + funcSym, ok := sec.symbols[0] + if !ok { + return nil, fmt.Errorf("section %v: no label at start", sec.Name) + } + + insns, length, err := ec.loadInstructions(sec) + if err != nil { + return nil, fmt.Errorf("program %s: %w", funcSym.Name, err) + } + + progType, attachType, progFlags, attachTo := getProgType(sec.Name) + + spec := &ProgramSpec{ + Name: funcSym.Name, + Type: progType, + Flags: progFlags, + AttachType: attachType, + AttachTo: attachTo, + License: ec.license, + KernelVersion: ec.version, + Instructions: insns, + ByteOrder: ec.ByteOrder, + } + + if ec.btf != nil { + spec.BTF, err = ec.btf.Program(sec.Name, length) + if err != nil && !errors.Is(err, btf.ErrNoExtendedInfo) { + return nil, fmt.Errorf("program %s: %w", funcSym.Name, err) + } + } + + if spec.Type == UnspecifiedProgram { + // There is no single name we can use for "library" sections, + // since they may contain multiple functions. We'll decode the + // labels they contain later on, and then link sections that way. + libs = append(libs, spec) + } else { + progs = append(progs, spec) + } + } + + res := make(map[string]*ProgramSpec, len(progs)) + for _, prog := range progs { + err := link(prog, libs) + if err != nil { + return nil, fmt.Errorf("program %s: %w", prog.Name, err) + } + res[prog.Name] = prog + } + + return res, nil +} + +func (ec *elfCode) loadInstructions(section *elfSection) (asm.Instructions, uint64, error) { + var ( + r = bufio.NewReader(section.Open()) + insns asm.Instructions + offset uint64 + ) + for { + var ins asm.Instruction + n, err := ins.Unmarshal(r, ec.ByteOrder) + if err == io.EOF { + return insns, offset, nil + } + if err != nil { + return nil, 0, fmt.Errorf("offset %d: %w", offset, err) + } + + ins.Symbol = section.symbols[offset].Name + + if rel, ok := section.relocations[offset]; ok { + if err = ec.relocateInstruction(&ins, rel); err != nil { + return nil, 0, fmt.Errorf("offset %d: relocate instruction: %w", offset, err) + } + } + + insns = append(insns, ins) + offset += n + } +} + +func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) error { + var ( + typ = elf.ST_TYPE(rel.Info) + bind = elf.ST_BIND(rel.Info) + name = rel.Name + ) + + target := ec.sections[rel.Section] + + switch target.kind { + case mapSection, btfMapSection: + if bind != elf.STB_GLOBAL { + return fmt.Errorf("possible erroneous static qualifier on map definition: found reference to %q", name) + } + + if typ != elf.STT_OBJECT && typ != elf.STT_NOTYPE { + // STT_NOTYPE is generated on clang < 8 which doesn't tag + // relocations appropriately. + return fmt.Errorf("map load: incorrect relocation type %v", typ) + } + + ins.Src = asm.PseudoMapFD + + // Mark the instruction as needing an update when creating the + // collection. + if err := ins.RewriteMapPtr(-1); err != nil { + return err + } + + case dataSection: + var offset uint32 + switch typ { + case elf.STT_SECTION: + if bind != elf.STB_LOCAL { + return fmt.Errorf("direct load: %s: unsupported relocation %s", name, bind) + } + + // This is really a reference to a static symbol, which clang doesn't + // emit a symbol table entry for. Instead it encodes the offset in + // the instruction itself. + offset = uint32(uint64(ins.Constant)) + + case elf.STT_OBJECT: + if bind != elf.STB_GLOBAL { + return fmt.Errorf("direct load: %s: unsupported relocation %s", name, bind) + } + + offset = uint32(rel.Value) + + default: + return fmt.Errorf("incorrect relocation type %v for direct map load", typ) + } + + // We rely on using the name of the data section as the reference. It + // would be nicer to keep the real name in case of an STT_OBJECT, but + // it's not clear how to encode that into Instruction. + name = target.Name + + // The kernel expects the offset in the second basic BPF instruction. + ins.Constant = int64(uint64(offset) << 32) + ins.Src = asm.PseudoMapValue + + // Mark the instruction as needing an update when creating the + // collection. + if err := ins.RewriteMapPtr(-1); err != nil { + return err + } + + case programSection: + if ins.OpCode.JumpOp() != asm.Call { + return fmt.Errorf("not a call instruction: %s", ins) + } + + if ins.Src != asm.PseudoCall { + return fmt.Errorf("call: %s: incorrect source register", name) + } + + switch typ { + case elf.STT_NOTYPE, elf.STT_FUNC: + if bind != elf.STB_GLOBAL { + return fmt.Errorf("call: %s: unsupported binding: %s", name, bind) + } + + case elf.STT_SECTION: + if bind != elf.STB_LOCAL { + return fmt.Errorf("call: %s: unsupported binding: %s", name, bind) + } + + // The function we want to call is in the indicated section, + // at the offset encoded in the instruction itself. Reverse + // the calculation to find the real function we're looking for. + // A value of -1 references the first instruction in the section. + offset := int64(int32(ins.Constant)+1) * asm.InstructionSize + if offset < 0 { + return fmt.Errorf("call: %s: invalid offset %d", name, offset) + } + + sym, ok := target.symbols[uint64(offset)] + if !ok { + return fmt.Errorf("call: %s: no symbol at offset %d", name, offset) + } + + ins.Constant = -1 + name = sym.Name + + default: + return fmt.Errorf("call: %s: invalid symbol type %s", name, typ) + } + + case undefSection: + if bind != elf.STB_GLOBAL { + return fmt.Errorf("asm relocation: %s: unsupported binding: %s", name, bind) + } + + if typ != elf.STT_NOTYPE { + return fmt.Errorf("asm relocation: %s: unsupported type %s", name, typ) + } + + // There is nothing to do here but set ins.Reference. + + default: + return fmt.Errorf("relocation to %q: %w", target.Name, ErrNotSupported) + } + + ins.Reference = name + return nil +} + +func (ec *elfCode) loadMaps(maps map[string]*MapSpec) error { + for _, sec := range ec.sections { + if sec.kind != mapSection { + continue + } + + nSym := len(sec.symbols) + if nSym == 0 { + return fmt.Errorf("section %v: no symbols", sec.Name) + } + + if sec.Size%uint64(nSym) != 0 { + return fmt.Errorf("section %v: map descriptors are not of equal size", sec.Name) + } + + var ( + r = bufio.NewReader(sec.Open()) + size = sec.Size / uint64(nSym) + ) + for i, offset := 0, uint64(0); i < nSym; i, offset = i+1, offset+size { + mapSym, ok := sec.symbols[offset] + if !ok { + return fmt.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset) + } + + mapName := mapSym.Name + if maps[mapName] != nil { + return fmt.Errorf("section %v: map %v already exists", sec.Name, mapSym) + } + + lr := io.LimitReader(r, int64(size)) + + spec := MapSpec{ + Name: SanitizeName(mapName, -1), + } + switch { + case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil: + return fmt.Errorf("map %s: missing type", mapName) + case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil: + return fmt.Errorf("map %s: missing key size", mapName) + case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil: + return fmt.Errorf("map %s: missing value size", mapName) + case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil: + return fmt.Errorf("map %s: missing max entries", mapName) + case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil: + return fmt.Errorf("map %s: missing flags", mapName) + } + + if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil { + return fmt.Errorf("map %s: unknown and non-zero fields in definition", mapName) + } + + if err := spec.clampPerfEventArraySize(); err != nil { + return fmt.Errorf("map %s: %w", mapName, err) + } + + maps[mapName] = &spec + } + } + + return nil +} + +func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error { + for _, sec := range ec.sections { + if sec.kind != btfMapSection { + continue + } + + if ec.btf == nil { + return fmt.Errorf("missing BTF") + } + + _, err := io.Copy(internal.DiscardZeroes{}, bufio.NewReader(sec.Open())) + if err != nil { + return fmt.Errorf("section %v: initializing BTF map definitions: %w", sec.Name, internal.ErrNotSupported) + } + + var ds btf.Datasec + if err := ec.btf.FindType(sec.Name, &ds); err != nil { + return fmt.Errorf("cannot find section '%s' in BTF: %w", sec.Name, err) + } + + for _, vs := range ds.Vars { + v, ok := vs.Type.(*btf.Var) + if !ok { + return fmt.Errorf("section %v: unexpected type %s", sec.Name, vs.Type) + } + name := string(v.Name) + + if maps[name] != nil { + return fmt.Errorf("section %v: map %s already exists", sec.Name, name) + } + + mapStruct, ok := v.Type.(*btf.Struct) + if !ok { + return fmt.Errorf("expected struct, got %s", v.Type) + } + + mapSpec, err := mapSpecFromBTF(name, mapStruct, false, ec.btf) + if err != nil { + return fmt.Errorf("map %v: %w", name, err) + } + + if err := mapSpec.clampPerfEventArraySize(); err != nil { + return fmt.Errorf("map %v: %w", name, err) + } + + maps[name] = mapSpec + } + } + + return nil +} + +// mapSpecFromBTF produces a MapSpec based on a btf.Struct def representing +// a BTF map definition. The name and spec arguments will be copied to the +// resulting MapSpec, and inner must be true on any resursive invocations. +func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (*MapSpec, error) { + + var ( + key, value btf.Type + keySize, valueSize uint32 + mapType, flags, maxEntries uint32 + pinType PinType + innerMapSpec *MapSpec + err error + ) + + for i, member := range def.Members { + switch member.Name { + case "type": + mapType, err = uintFromBTF(member.Type) + if err != nil { + return nil, fmt.Errorf("can't get type: %w", err) + } + + case "map_flags": + flags, err = uintFromBTF(member.Type) + if err != nil { + return nil, fmt.Errorf("can't get BTF map flags: %w", err) + } + + case "max_entries": + maxEntries, err = uintFromBTF(member.Type) + if err != nil { + return nil, fmt.Errorf("can't get BTF map max entries: %w", err) + } + + case "key": + if keySize != 0 { + return nil, errors.New("both key and key_size given") + } + + pk, ok := member.Type.(*btf.Pointer) + if !ok { + return nil, fmt.Errorf("key type is not a pointer: %T", member.Type) + } + + key = pk.Target + + size, err := btf.Sizeof(pk.Target) + if err != nil { + return nil, fmt.Errorf("can't get size of BTF key: %w", err) + } + + keySize = uint32(size) + + case "value": + if valueSize != 0 { + return nil, errors.New("both value and value_size given") + } + + vk, ok := member.Type.(*btf.Pointer) + if !ok { + return nil, fmt.Errorf("value type is not a pointer: %T", member.Type) + } + + value = vk.Target + + size, err := btf.Sizeof(vk.Target) + if err != nil { + return nil, fmt.Errorf("can't get size of BTF value: %w", err) + } + + valueSize = uint32(size) + + case "key_size": + // Key needs to be nil and keySize needs to be 0 for key_size to be + // considered a valid member. + if key != nil || keySize != 0 { + return nil, errors.New("both key and key_size given") + } + + keySize, err = uintFromBTF(member.Type) + if err != nil { + return nil, fmt.Errorf("can't get BTF key size: %w", err) + } + + case "value_size": + // Value needs to be nil and valueSize needs to be 0 for value_size to be + // considered a valid member. + if value != nil || valueSize != 0 { + return nil, errors.New("both value and value_size given") + } + + valueSize, err = uintFromBTF(member.Type) + if err != nil { + return nil, fmt.Errorf("can't get BTF value size: %w", err) + } + + case "pinning": + if inner { + return nil, errors.New("inner maps can't be pinned") + } + + pinning, err := uintFromBTF(member.Type) + if err != nil { + return nil, fmt.Errorf("can't get pinning: %w", err) + } + + pinType = PinType(pinning) + + case "values": + // The 'values' field in BTF map definitions is used for declaring map + // value types that are references to other BPF objects, like other maps + // or programs. It is always expected to be an array of pointers. + if i != len(def.Members)-1 { + return nil, errors.New("'values' must be the last member in a BTF map definition") + } + + if valueSize != 0 && valueSize != 4 { + return nil, errors.New("value_size must be 0 or 4") + } + valueSize = 4 + + valueType, err := resolveBTFArrayMacro(member.Type) + if err != nil { + return nil, fmt.Errorf("can't resolve type of member 'values': %w", err) + } + + switch t := valueType.(type) { + case *btf.Struct: + // The values member pointing to an array of structs means we're expecting + // a map-in-map declaration. + if MapType(mapType) != ArrayOfMaps && MapType(mapType) != HashOfMaps { + return nil, errors.New("outer map needs to be an array or a hash of maps") + } + if inner { + return nil, fmt.Errorf("nested inner maps are not supported") + } + + // This inner map spec is used as a map template, but it needs to be + // created as a traditional map before it can be used to do so. + // libbpf names the inner map template '.inner', but we + // opted for _inner to simplify validation logic. (dots only supported + // on kernels 5.2 and up) + // Pass the BTF spec from the parent object, since both parent and + // child must be created from the same BTF blob (on kernels that support BTF). + innerMapSpec, err = mapSpecFromBTF(name+"_inner", t, true, spec) + if err != nil { + return nil, fmt.Errorf("can't parse BTF map definition of inner map: %w", err) + } + + default: + return nil, fmt.Errorf("unsupported value type %q in 'values' field", t) + } + + default: + return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name) + } + } + + bm := btf.NewMap(spec, key, value) + + return &MapSpec{ + Name: SanitizeName(name, -1), + Type: MapType(mapType), + KeySize: keySize, + ValueSize: valueSize, + MaxEntries: maxEntries, + Flags: flags, + BTF: &bm, + Pinning: pinType, + InnerMap: innerMapSpec, + }, nil +} + +// uintFromBTF resolves the __uint macro, which is a pointer to a sized +// array, e.g. for int (*foo)[10], this function will return 10. +func uintFromBTF(typ btf.Type) (uint32, error) { + ptr, ok := typ.(*btf.Pointer) + if !ok { + return 0, fmt.Errorf("not a pointer: %v", typ) + } + + arr, ok := ptr.Target.(*btf.Array) + if !ok { + return 0, fmt.Errorf("not a pointer to array: %v", typ) + } + + return arr.Nelems, nil +} + +// resolveBTFArrayMacro resolves the __array macro, which declares an array +// of pointers to a given type. This function returns the target Type of +// the pointers in the array. +func resolveBTFArrayMacro(typ btf.Type) (btf.Type, error) { + arr, ok := typ.(*btf.Array) + if !ok { + return nil, fmt.Errorf("not an array: %v", typ) + } + + ptr, ok := arr.Type.(*btf.Pointer) + if !ok { + return nil, fmt.Errorf("not an array of pointers: %v", typ) + } + + return ptr.Target, nil +} + +func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error { + for _, sec := range ec.sections { + if sec.kind != dataSection { + continue + } + + if sec.references == 0 { + // Prune data sections which are not referenced by any + // instructions. + continue + } + + if ec.btf == nil { + return errors.New("data sections require BTF, make sure all consts are marked as static") + } + + btfMap, err := ec.btf.Datasec(sec.Name) + if err != nil { + return err + } + + data, err := sec.Data() + if err != nil { + return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err) + } + + if uint64(len(data)) > math.MaxUint32 { + return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name) + } + + mapSpec := &MapSpec{ + Name: SanitizeName(sec.Name, -1), + Type: Array, + KeySize: 4, + ValueSize: uint32(len(data)), + MaxEntries: 1, + Contents: []MapKV{{uint32(0), data}}, + BTF: btfMap, + } + + switch sec.Name { + case ".rodata": + mapSpec.Flags = unix.BPF_F_RDONLY_PROG + mapSpec.Freeze = true + case ".bss": + // The kernel already zero-initializes the map + mapSpec.Contents = nil + } + + maps[sec.Name] = mapSpec + } + return nil +} + +func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) { + types := map[string]struct { + progType ProgramType + attachType AttachType + progFlags uint32 + }{ + // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c + "socket": {SocketFilter, AttachNone, 0}, + "seccomp": {SocketFilter, AttachNone, 0}, + "kprobe/": {Kprobe, AttachNone, 0}, + "uprobe/": {Kprobe, AttachNone, 0}, + "kretprobe/": {Kprobe, AttachNone, 0}, + "uretprobe/": {Kprobe, AttachNone, 0}, + "tracepoint/": {TracePoint, AttachNone, 0}, + "raw_tracepoint/": {RawTracepoint, AttachNone, 0}, + "raw_tp/": {RawTracepoint, AttachNone, 0}, + "tp_btf/": {Tracing, AttachTraceRawTp, 0}, + "xdp": {XDP, AttachNone, 0}, + "perf_event": {PerfEvent, AttachNone, 0}, + "lwt_in": {LWTIn, AttachNone, 0}, + "lwt_out": {LWTOut, AttachNone, 0}, + "lwt_xmit": {LWTXmit, AttachNone, 0}, + "lwt_seg6local": {LWTSeg6Local, AttachNone, 0}, + "sockops": {SockOps, AttachCGroupSockOps, 0}, + "sk_skb/stream_parser": {SkSKB, AttachSkSKBStreamParser, 0}, + "sk_skb/stream_verdict": {SkSKB, AttachSkSKBStreamParser, 0}, + "sk_msg": {SkMsg, AttachSkSKBStreamVerdict, 0}, + "lirc_mode2": {LircMode2, AttachLircMode2, 0}, + "flow_dissector": {FlowDissector, AttachFlowDissector, 0}, + "iter/": {Tracing, AttachTraceIter, 0}, + "fentry/": {Tracing, AttachTraceFEntry, 0}, + "fmod_ret/": {Tracing, AttachModifyReturn, 0}, + "fexit/": {Tracing, AttachTraceFExit, 0}, + "fentry.s/": {Tracing, AttachTraceFEntry, unix.BPF_F_SLEEPABLE}, + "fmod_ret.s/": {Tracing, AttachModifyReturn, unix.BPF_F_SLEEPABLE}, + "fexit.s/": {Tracing, AttachTraceFExit, unix.BPF_F_SLEEPABLE}, + "sk_lookup/": {SkLookup, AttachSkLookup, 0}, + "lsm/": {LSM, AttachLSMMac, 0}, + "lsm.s/": {LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE}, + + "cgroup_skb/ingress": {CGroupSKB, AttachCGroupInetIngress, 0}, + "cgroup_skb/egress": {CGroupSKB, AttachCGroupInetEgress, 0}, + "cgroup/dev": {CGroupDevice, AttachCGroupDevice, 0}, + "cgroup/skb": {CGroupSKB, AttachNone, 0}, + "cgroup/sock": {CGroupSock, AttachCGroupInetSockCreate, 0}, + "cgroup/post_bind4": {CGroupSock, AttachCGroupInet4PostBind, 0}, + "cgroup/post_bind6": {CGroupSock, AttachCGroupInet6PostBind, 0}, + "cgroup/bind4": {CGroupSockAddr, AttachCGroupInet4Bind, 0}, + "cgroup/bind6": {CGroupSockAddr, AttachCGroupInet6Bind, 0}, + "cgroup/connect4": {CGroupSockAddr, AttachCGroupInet4Connect, 0}, + "cgroup/connect6": {CGroupSockAddr, AttachCGroupInet6Connect, 0}, + "cgroup/sendmsg4": {CGroupSockAddr, AttachCGroupUDP4Sendmsg, 0}, + "cgroup/sendmsg6": {CGroupSockAddr, AttachCGroupUDP6Sendmsg, 0}, + "cgroup/recvmsg4": {CGroupSockAddr, AttachCGroupUDP4Recvmsg, 0}, + "cgroup/recvmsg6": {CGroupSockAddr, AttachCGroupUDP6Recvmsg, 0}, + "cgroup/sysctl": {CGroupSysctl, AttachCGroupSysctl, 0}, + "cgroup/getsockopt": {CGroupSockopt, AttachCGroupGetsockopt, 0}, + "cgroup/setsockopt": {CGroupSockopt, AttachCGroupSetsockopt, 0}, + "classifier": {SchedCLS, AttachNone, 0}, + "action": {SchedACT, AttachNone, 0}, + } + + for prefix, t := range types { + if !strings.HasPrefix(sectionName, prefix) { + continue + } + + if !strings.HasSuffix(prefix, "/") { + return t.progType, t.attachType, t.progFlags, "" + } + + return t.progType, t.attachType, t.progFlags, sectionName[len(prefix):] + } + + return UnspecifiedProgram, AttachNone, 0, "" +} + +func (ec *elfCode) loadRelocations(sec *elf.Section, symbols []elf.Symbol) (map[uint64]elf.Symbol, error) { + rels := make(map[uint64]elf.Symbol) + + if sec.Entsize < 16 { + return nil, fmt.Errorf("section %s: relocations are less than 16 bytes", sec.Name) + } + + r := bufio.NewReader(sec.Open()) + for off := uint64(0); off < sec.Size; off += sec.Entsize { + ent := io.LimitReader(r, int64(sec.Entsize)) + + var rel elf.Rel64 + if binary.Read(ent, ec.ByteOrder, &rel) != nil { + return nil, fmt.Errorf("can't parse relocation at offset %v", off) + } + + symNo := int(elf.R_SYM64(rel.Info) - 1) + if symNo >= len(symbols) { + return nil, fmt.Errorf("offset %d: symbol %d doesn't exist", off, symNo) + } + + symbol := symbols[symNo] + rels[rel.Off] = symbol + } + + return rels, nil +} diff --git a/vendor/github.com/cilium/ebpf/elf_reader_fuzz.go b/vendor/github.com/cilium/ebpf/elf_reader_fuzz.go new file mode 100644 index 0000000000000..d46d135f2fcd1 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/elf_reader_fuzz.go @@ -0,0 +1,21 @@ +// +build gofuzz + +// Use with https://github.com/dvyukov/go-fuzz + +package ebpf + +import "bytes" + +func FuzzLoadCollectionSpec(data []byte) int { + spec, err := LoadCollectionSpecFromReader(bytes.NewReader(data)) + if err != nil { + if spec != nil { + panic("spec is not nil") + } + return 0 + } + if spec == nil { + panic("spec is nil") + } + return 1 +} diff --git a/vendor/github.com/cilium/ebpf/examples/README.md b/vendor/github.com/cilium/ebpf/examples/README.md new file mode 100644 index 0000000000000..1765e3ce2e8d9 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/examples/README.md @@ -0,0 +1,13 @@ +# eBPF Examples + +- [kprobe](kprobe/) - Attach a program to the entry or exit of an arbitrary kernel symbol (function). +- [uretprobe](uretprobe/) - Like a kprobe, but for symbols in userspace binaries (e.g. `bash`). +- [tracepoint](tracepoint/) - Attach a program to predetermined kernel tracepoints. +- Add your use case(s) here! + +## How to run + +```bash +cd ebpf/examples/ +go run -exec sudo [./kprobe, ./uretprobe, ./tracepoint, ...] +``` diff --git a/vendor/github.com/cilium/ebpf/examples/go.mod b/vendor/github.com/cilium/ebpf/examples/go.mod new file mode 100644 index 0000000000000..6722dfd3c4ff5 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/examples/go.mod @@ -0,0 +1,8 @@ +module github.com/cilium/ebpf/examples + +go 1.15 + +require ( + github.com/cilium/ebpf v0.6.1-0.20210610105443-1e7f01c7124c + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c +) diff --git a/vendor/github.com/cilium/ebpf/examples/headers/bpf_helper_defs.h b/vendor/github.com/cilium/ebpf/examples/headers/bpf_helper_defs.h new file mode 100644 index 0000000000000..5ac02ece119eb --- /dev/null +++ b/vendor/github.com/cilium/ebpf/examples/headers/bpf_helper_defs.h @@ -0,0 +1,3265 @@ +/* This is auto-generated file. See bpf_helpers_doc.py for details. */ + +/* Forward declarations of BPF structs */ +struct bpf_fib_lookup; +struct bpf_sk_lookup; +struct bpf_perf_event_data; +struct bpf_perf_event_value; +struct bpf_pidns_info; +struct bpf_sock; +struct bpf_sock_addr; +struct bpf_sock_ops; +struct bpf_sock_tuple; +struct bpf_spin_lock; +struct bpf_sysctl; +struct bpf_tcp_sock; +struct bpf_tunnel_key; +struct bpf_xfrm_state; +struct pt_regs; +struct sk_reuseport_md; +struct sockaddr; +struct tcphdr; +struct seq_file; +struct tcp6_sock; +struct tcp_sock; +struct tcp_timewait_sock; +struct tcp_request_sock; +struct udp6_sock; +struct task_struct; +struct __sk_buff; +struct sk_msg_md; +struct xdp_md; + +/* + * bpf_map_lookup_elem + * + * Perform a lookup in *map* for an entry associated to *key*. + * + * Returns + * Map value associated to *key*, or **NULL** if no entry was + * found. + */ +static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; + +/* + * bpf_map_update_elem + * + * Add or update the value of the entry associated to *key* in + * *map* with *value*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * Flag value **BPF_NOEXIST** cannot be used for maps of types + * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all + * elements always exist), the helper would return an error. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2; + +/* + * bpf_map_delete_elem + * + * Delete entry with *key* from *map*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_delete_elem)(void *map, const void *key) = (void *) 3; + +/* + * bpf_probe_read + * + * For tracing programs, safely attempt to read *size* bytes from + * kernel space address *unsafe_ptr* and store the data in *dst*. + * + * Generally, use **bpf_probe_read_user**\ () or + * **bpf_probe_read_kernel**\ () instead. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_read)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 4; + +/* + * bpf_ktime_get_ns + * + * Return the time elapsed since system boot, in nanoseconds. + * Does not include time the system was suspended. + * See: **clock_gettime**\ (**CLOCK_MONOTONIC**) + * + * Returns + * Current *ktime*. + */ +static __u64 (*bpf_ktime_get_ns)(void) = (void *) 5; + +/* + * bpf_trace_printk + * + * This helper is a "printk()-like" facility for debugging. It + * prints a message defined by format *fmt* (of size *fmt_size*) + * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if + * available. It can take up to three additional **u64** + * arguments (as an eBPF helpers, the total number of arguments is + * limited to five). + * + * Each time the helper is called, it appends a line to the trace. + * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is + * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this. + * The format of the trace is customizable, and the exact output + * one will get depends on the options set in + * *\/sys/kernel/debug/tracing/trace_options* (see also the + * *README* file under the same directory). However, it usually + * defaults to something like: + * + * :: + * + * telnet-470 [001] .N.. 419421.045894: 0x00000001: + * + * In the above: + * + * * ``telnet`` is the name of the current task. + * * ``470`` is the PID of the current task. + * * ``001`` is the CPU number on which the task is + * running. + * * In ``.N..``, each character refers to a set of + * options (whether irqs are enabled, scheduling + * options, whether hard/softirqs are running, level of + * preempt_disabled respectively). **N** means that + * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED** + * are set. + * * ``419421.045894`` is a timestamp. + * * ``0x00000001`` is a fake value used by BPF for the + * instruction pointer register. + * * ```` is the message formatted with + * *fmt*. + * + * The conversion specifiers supported by *fmt* are similar, but + * more limited than for printk(). They are **%d**, **%i**, + * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**, + * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size + * of field, padding with zeroes, etc.) is available, and the + * helper will return **-EINVAL** (but print nothing) if it + * encounters an unknown specifier. + * + * Also, note that **bpf_trace_printk**\ () is slow, and should + * only be used for debugging purposes. For this reason, a notice + * bloc (spanning several lines) is printed to kernel logs and + * states that the helper should not be used "for production use" + * the first time this helper is used (or more precisely, when + * **trace_printk**\ () buffers are allocated). For passing values + * to user space, perf events should be preferred. + * + * Returns + * The number of bytes written to the buffer, or a negative error + * in case of failure. + */ +static long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) 6; + +/* + * bpf_get_prandom_u32 + * + * Get a pseudo-random number. + * + * From a security point of view, this helper uses its own + * pseudo-random internal state, and cannot be used to infer the + * seed of other random functions in the kernel. However, it is + * essential to note that the generator used by the helper is not + * cryptographically secure. + * + * Returns + * A random 32-bit unsigned value. + */ +static __u32 (*bpf_get_prandom_u32)(void) = (void *) 7; + +/* + * bpf_get_smp_processor_id + * + * Get the SMP (symmetric multiprocessing) processor id. Note that + * all programs run with preemption disabled, which means that the + * SMP processor id is stable during all the execution of the + * program. + * + * Returns + * The SMP id of the processor running the program. + */ +static __u32 (*bpf_get_smp_processor_id)(void) = (void *) 8; + +/* + * bpf_skb_store_bytes + * + * Store *len* bytes from address *from* into the packet + * associated to *skb*, at *offset*. *flags* are a combination of + * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the + * checksum for the packet after storing the bytes) and + * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ + * **->swhash** and *skb*\ **->l4hash** to 0). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len, __u64 flags) = (void *) 9; + +/* + * bpf_l3_csum_replace + * + * Recompute the layer 3 (e.g. IP) checksum for the packet + * associated to *skb*. Computation is incremental, so the helper + * must know the former value of the header field that was + * modified (*from*), the new value of this field (*to*), and the + * number of bytes (2 or 4) for this field, stored in *size*. + * Alternatively, it is possible to store the difference between + * the previous and the new values of the header field in *to*, by + * setting *from* and *size* to 0. For both methods, *offset* + * indicates the location of the IP checksum within the packet. + * + * This helper works in combination with **bpf_csum_diff**\ (), + * which does not update the checksum in-place, but offers more + * flexibility and can handle sizes larger than 2 or 4 for the + * checksum to update. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_l3_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 size) = (void *) 10; + +/* + * bpf_l4_csum_replace + * + * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the + * packet associated to *skb*. Computation is incremental, so the + * helper must know the former value of the header field that was + * modified (*from*), the new value of this field (*to*), and the + * number of bytes (2 or 4) for this field, stored on the lowest + * four bits of *flags*. Alternatively, it is possible to store + * the difference between the previous and the new values of the + * header field in *to*, by setting *from* and the four lowest + * bits of *flags* to 0. For both methods, *offset* indicates the + * location of the IP checksum within the packet. In addition to + * the size of the field, *flags* can be added (bitwise OR) actual + * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left + * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and + * for updates resulting in a null checksum the value is set to + * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates + * the checksum is to be computed against a pseudo-header. + * + * This helper works in combination with **bpf_csum_diff**\ (), + * which does not update the checksum in-place, but offers more + * flexibility and can handle sizes larger than 2 or 4 for the + * checksum to update. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_l4_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 flags) = (void *) 11; + +/* + * bpf_tail_call + * + * This special helper is used to trigger a "tail call", or in + * other words, to jump into another eBPF program. The same stack + * frame is used (but values on stack and in registers for the + * caller are not accessible to the callee). This mechanism allows + * for program chaining, either for raising the maximum number of + * available eBPF instructions, or to execute given programs in + * conditional blocks. For security reasons, there is an upper + * limit to the number of successive tail calls that can be + * performed. + * + * Upon call of this helper, the program attempts to jump into a + * program referenced at index *index* in *prog_array_map*, a + * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes + * *ctx*, a pointer to the context. + * + * If the call succeeds, the kernel immediately runs the first + * instruction of the new program. This is not a function call, + * and it never returns to the previous program. If the call + * fails, then the helper has no effect, and the caller continues + * to run its subsequent instructions. A call can fail if the + * destination program for the jump does not exist (i.e. *index* + * is superior to the number of entries in *prog_array_map*), or + * if the maximum number of tail calls has been reached for this + * chain of programs. This limit is defined in the kernel by the + * macro **MAX_TAIL_CALL_CNT** (not accessible to user space), + * which is currently set to 32. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_tail_call)(void *ctx, void *prog_array_map, __u32 index) = (void *) 12; + +/* + * bpf_clone_redirect + * + * Clone and redirect the packet associated to *skb* to another + * net device of index *ifindex*. Both ingress and egress + * interfaces can be used for redirection. The **BPF_F_INGRESS** + * value in *flags* is used to make the distinction (ingress path + * is selected if the flag is present, egress path otherwise). + * This is the only flag supported for now. + * + * In comparison with **bpf_redirect**\ () helper, + * **bpf_clone_redirect**\ () has the associated cost of + * duplicating the packet buffer, but this can be executed out of + * the eBPF program. Conversely, **bpf_redirect**\ () is more + * efficient, but it is handled through an action code where the + * redirection happens only after the eBPF program has returned. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_clone_redirect)(struct __sk_buff *skb, __u32 ifindex, __u64 flags) = (void *) 13; + +/* + * bpf_get_current_pid_tgid + * + * + * Returns + * A 64-bit integer containing the current tgid and pid, and + * created as such: + * *current_task*\ **->tgid << 32 \|** + * *current_task*\ **->pid**. + */ +static __u64 (*bpf_get_current_pid_tgid)(void) = (void *) 14; + +/* + * bpf_get_current_uid_gid + * + * + * Returns + * A 64-bit integer containing the current GID and UID, and + * created as such: *current_gid* **<< 32 \|** *current_uid*. + */ +static __u64 (*bpf_get_current_uid_gid)(void) = (void *) 15; + +/* + * bpf_get_current_comm + * + * Copy the **comm** attribute of the current task into *buf* of + * *size_of_buf*. The **comm** attribute contains the name of + * the executable (excluding the path) for the current task. The + * *size_of_buf* must be strictly positive. On success, the + * helper makes sure that the *buf* is NUL-terminated. On failure, + * it is filled with zeroes. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_get_current_comm)(void *buf, __u32 size_of_buf) = (void *) 16; + +/* + * bpf_get_cgroup_classid + * + * Retrieve the classid for the current task, i.e. for the net_cls + * cgroup to which *skb* belongs. + * + * This helper can be used on TC egress path, but not on ingress. + * + * The net_cls cgroup provides an interface to tag network packets + * based on a user-provided identifier for all traffic coming from + * the tasks belonging to the related cgroup. See also the related + * kernel documentation, available from the Linux sources in file + * *Documentation/admin-guide/cgroup-v1/net_cls.rst*. + * + * The Linux kernel has two versions for cgroups: there are + * cgroups v1 and cgroups v2. Both are available to users, who can + * use a mixture of them, but note that the net_cls cgroup is for + * cgroup v1 only. This makes it incompatible with BPF programs + * run on cgroups, which is a cgroup-v2-only feature (a socket can + * only hold data for one version of cgroups at a time). + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to + * "**y**" or to "**m**". + * + * Returns + * The classid, or 0 for the default unconfigured classid. + */ +static __u32 (*bpf_get_cgroup_classid)(struct __sk_buff *skb) = (void *) 17; + +/* + * bpf_skb_vlan_push + * + * Push a *vlan_tci* (VLAN tag control information) of protocol + * *vlan_proto* to the packet associated to *skb*, then update + * the checksum. Note that if *vlan_proto* is different from + * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to + * be **ETH_P_8021Q**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_vlan_push)(struct __sk_buff *skb, __be16 vlan_proto, __u16 vlan_tci) = (void *) 18; + +/* + * bpf_skb_vlan_pop + * + * Pop a VLAN header from the packet associated to *skb*. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_vlan_pop)(struct __sk_buff *skb) = (void *) 19; + +/* + * bpf_skb_get_tunnel_key + * + * Get tunnel metadata. This helper takes a pointer *key* to an + * empty **struct bpf_tunnel_key** of **size**, that will be + * filled with tunnel metadata for the packet associated to *skb*. + * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which + * indicates that the tunnel is based on IPv6 protocol instead of + * IPv4. + * + * The **struct bpf_tunnel_key** is an object that generalizes the + * principal parameters used by various tunneling protocols into a + * single struct. This way, it can be used to easily make a + * decision based on the contents of the encapsulation header, + * "summarized" in this struct. In particular, it holds the IP + * address of the remote end (IPv4 or IPv6, depending on the case) + * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also, + * this struct exposes the *key*\ **->tunnel_id**, which is + * generally mapped to a VNI (Virtual Network Identifier), making + * it programmable together with the **bpf_skb_set_tunnel_key**\ + * () helper. + * + * Let's imagine that the following code is part of a program + * attached to the TC ingress interface, on one end of a GRE + * tunnel, and is supposed to filter out all messages coming from + * remote ends with IPv4 address other than 10.0.0.1: + * + * :: + * + * int ret; + * struct bpf_tunnel_key key = {}; + * + * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0); + * if (ret < 0) + * return TC_ACT_SHOT; // drop packet + * + * if (key.remote_ipv4 != 0x0a000001) + * return TC_ACT_SHOT; // drop packet + * + * return TC_ACT_OK; // accept packet + * + * This interface can also be used with all encapsulation devices + * that can operate in "collect metadata" mode: instead of having + * one network device per specific configuration, the "collect + * metadata" mode only requires a single device where the + * configuration can be extracted from this helper. + * + * This can be used together with various tunnels such as VXLan, + * Geneve, GRE or IP in IP (IPIP). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_get_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 20; + +/* + * bpf_skb_set_tunnel_key + * + * Populate tunnel metadata for packet associated to *skb.* The + * tunnel metadata is set to the contents of *key*, of *size*. The + * *flags* can be set to a combination of the following values: + * + * **BPF_F_TUNINFO_IPV6** + * Indicate that the tunnel is based on IPv6 protocol + * instead of IPv4. + * **BPF_F_ZERO_CSUM_TX** + * For IPv4 packets, add a flag to tunnel metadata + * indicating that checksum computation should be skipped + * and checksum set to zeroes. + * **BPF_F_DONT_FRAGMENT** + * Add a flag to tunnel metadata indicating that the + * packet should not be fragmented. + * **BPF_F_SEQ_NUMBER** + * Add a flag to tunnel metadata indicating that a + * sequence number should be added to tunnel header before + * sending the packet. This flag was added for GRE + * encapsulation, but might be used with other protocols + * as well in the future. + * + * Here is a typical usage on the transmit path: + * + * :: + * + * struct bpf_tunnel_key key; + * populate key ... + * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0); + * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0); + * + * See also the description of the **bpf_skb_get_tunnel_key**\ () + * helper for additional information. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_set_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 21; + +/* + * bpf_perf_event_read + * + * Read the value of a perf event counter. This helper relies on a + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of + * the perf event counter is selected when *map* is updated with + * perf event file descriptors. The *map* is an array whose size + * is the number of available CPUs, and each cell contains a value + * relative to one CPU. The value to retrieve is indicated by + * *flags*, that contains the index of the CPU to look up, masked + * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to + * **BPF_F_CURRENT_CPU** to indicate that the value for the + * current CPU should be retrieved. + * + * Note that before Linux 4.13, only hardware perf event can be + * retrieved. + * + * Also, be aware that the newer helper + * **bpf_perf_event_read_value**\ () is recommended over + * **bpf_perf_event_read**\ () in general. The latter has some ABI + * quirks where error and counter value are used as a return code + * (which is wrong to do since ranges may overlap). This issue is + * fixed with **bpf_perf_event_read_value**\ (), which at the same + * time provides more features over the **bpf_perf_event_read**\ + * () interface. Please refer to the description of + * **bpf_perf_event_read_value**\ () for details. + * + * Returns + * The value of the perf event counter read from the map, or a + * negative error code in case of failure. + */ +static __u64 (*bpf_perf_event_read)(void *map, __u64 flags) = (void *) 22; + +/* + * bpf_redirect + * + * Redirect the packet to another net device of index *ifindex*. + * This helper is somewhat similar to **bpf_clone_redirect**\ + * (), except that the packet is not cloned, which provides + * increased performance. + * + * Except for XDP, both ingress and egress interfaces can be used + * for redirection. The **BPF_F_INGRESS** value in *flags* is used + * to make the distinction (ingress path is selected if the flag + * is present, egress path otherwise). Currently, XDP only + * supports redirection to the egress interface, and accepts no + * flag at all. + * + * The same effect can also be attained with the more generic + * **bpf_redirect_map**\ (), which uses a BPF map to store the + * redirect target instead of providing it directly to the helper. + * + * Returns + * For XDP, the helper returns **XDP_REDIRECT** on success or + * **XDP_ABORTED** on error. For other program types, the values + * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on + * error. + */ +static long (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void *) 23; + +/* + * bpf_get_route_realm + * + * Retrieve the realm or the route, that is to say the + * **tclassid** field of the destination for the *skb*. The + * indentifier retrieved is a user-provided tag, similar to the + * one used with the net_cls cgroup (see description for + * **bpf_get_cgroup_classid**\ () helper), but here this tag is + * held by a route (a destination entry), not by a task. + * + * Retrieving this identifier works with the clsact TC egress hook + * (see also **tc-bpf(8)**), or alternatively on conventional + * classful egress qdiscs, but not on TC ingress path. In case of + * clsact TC egress hook, this has the advantage that, internally, + * the destination entry has not been dropped yet in the transmit + * path. Therefore, the destination entry does not need to be + * artificially held via **netif_keep_dst**\ () for a classful + * qdisc until the *skb* is freed. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_IP_ROUTE_CLASSID** configuration option. + * + * Returns + * The realm of the route for the packet associated to *skb*, or 0 + * if none was found. + */ +static __u32 (*bpf_get_route_realm)(struct __sk_buff *skb) = (void *) 24; + +/* + * bpf_perf_event_output + * + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * The context of the program *ctx* needs also be passed to the + * helper. + * + * On user space, a program willing to read the values needs to + * call **perf_event_open**\ () on the perf event (either for + * one or for all CPUs) and to store the file descriptor into the + * *map*. This must be done before the eBPF program can send data + * into it. An example is available in file + * *samples/bpf/trace_output_user.c* in the Linux kernel source + * tree (the eBPF program counterpart is in + * *samples/bpf/trace_output_kern.c*). + * + * **bpf_perf_event_output**\ () achieves better performance + * than **bpf_trace_printk**\ () for sharing data with user + * space, and is much better suitable for streaming data from eBPF + * programs. + * + * Note that this helper is not restricted to tracing use cases + * and can be used with programs attached to TC or XDP as well, + * where it allows for passing data to user space listeners. Data + * can be: + * + * * Only custom structs, + * * Only the packet payload, or + * * A combination of both. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_perf_event_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 25; + +/* + * bpf_skb_load_bytes + * + * This helper was provided as an easy way to load data from a + * packet. It can be used to load *len* bytes from *offset* from + * the packet associated to *skb*, into the buffer pointed by + * *to*. + * + * Since Linux 4.7, usage of this helper has mostly been replaced + * by "direct packet access", enabling packet data to be + * manipulated with *skb*\ **->data** and *skb*\ **->data_end** + * pointing respectively to the first byte of packet data and to + * the byte after the last byte of packet data. However, it + * remains useful if one wishes to read large quantities of data + * at once from a packet into the eBPF stack. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_load_bytes)(const void *skb, __u32 offset, void *to, __u32 len) = (void *) 26; + +/* + * bpf_get_stackid + * + * Walk a user or a kernel stack and return its id. To achieve + * this, the helper needs *ctx*, which is a pointer to the context + * on which the tracing program is executed, and a pointer to a + * *map* of type **BPF_MAP_TYPE_STACK_TRACE**. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * a combination of the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_FAST_STACK_CMP** + * Compare stacks by hash only. + * **BPF_F_REUSE_STACKID** + * If two different stacks hash into the same *stackid*, + * discard the old one. + * + * The stack id retrieved is a 32 bit long integer handle which + * can be further combined with other data (including other stack + * ids) and used as a key into maps. This can be useful for + * generating a variety of graphs (such as flame graphs or off-cpu + * graphs). + * + * For walking a stack, this helper is an improvement over + * **bpf_probe_read**\ (), which can be used with unrolled loops + * but is not efficient and consumes a lot of eBPF instructions. + * Instead, **bpf_get_stackid**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack= + * + * Returns + * The positive or null stack id on success, or a negative error + * in case of failure. + */ +static long (*bpf_get_stackid)(void *ctx, void *map, __u64 flags) = (void *) 27; + +/* + * bpf_csum_diff + * + * Compute a checksum difference, from the raw buffer pointed by + * *from*, of length *from_size* (that must be a multiple of 4), + * towards the raw buffer pointed by *to*, of size *to_size* + * (same remark). An optional *seed* can be added to the value + * (this can be cascaded, the seed may come from a previous call + * to the helper). + * + * This is flexible enough to be used in several ways: + * + * * With *from_size* == 0, *to_size* > 0 and *seed* set to + * checksum, it can be used when pushing new data. + * * With *from_size* > 0, *to_size* == 0 and *seed* set to + * checksum, it can be used when removing data from a packet. + * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it + * can be used to compute a diff. Note that *from_size* and + * *to_size* do not need to be equal. + * + * This helper can be used in combination with + * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to + * which one can feed in the difference computed with + * **bpf_csum_diff**\ (). + * + * Returns + * The checksum result, or a negative error code in case of + * failure. + */ +static __s64 (*bpf_csum_diff)(__be32 *from, __u32 from_size, __be32 *to, __u32 to_size, __wsum seed) = (void *) 28; + +/* + * bpf_skb_get_tunnel_opt + * + * Retrieve tunnel options metadata for the packet associated to + * *skb*, and store the raw tunnel option data to the buffer *opt* + * of *size*. + * + * This helper can be used with encapsulation devices that can + * operate in "collect metadata" mode (please refer to the related + * note in the description of **bpf_skb_get_tunnel_key**\ () for + * more details). A particular example where this can be used is + * in combination with the Geneve encapsulation protocol, where it + * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper) + * and retrieving arbitrary TLVs (Type-Length-Value headers) from + * the eBPF program. This allows for full customization of these + * headers. + * + * Returns + * The size of the option data retrieved. + */ +static long (*bpf_skb_get_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 29; + +/* + * bpf_skb_set_tunnel_opt + * + * Set tunnel options metadata for the packet associated to *skb* + * to the option data contained in the raw buffer *opt* of *size*. + * + * See also the description of the **bpf_skb_get_tunnel_opt**\ () + * helper for additional information. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_set_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 30; + +/* + * bpf_skb_change_proto + * + * Change the protocol of the *skb* to *proto*. Currently + * supported are transition from IPv4 to IPv6, and from IPv6 to + * IPv4. The helper takes care of the groundwork for the + * transition, including resizing the socket buffer. The eBPF + * program is expected to fill the new headers, if any, via + * **skb_store_bytes**\ () and to recompute the checksums with + * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ + * (). The main case for this helper is to perform NAT64 + * operations out of an eBPF program. + * + * Internally, the GSO type is marked as dodgy so that headers are + * checked and segments are recalculated by the GSO/GRO engine. + * The size for GSO target is adapted as well. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_proto)(struct __sk_buff *skb, __be16 proto, __u64 flags) = (void *) 31; + +/* + * bpf_skb_change_type + * + * Change the packet type for the packet associated to *skb*. This + * comes down to setting *skb*\ **->pkt_type** to *type*, except + * the eBPF program does not have a write access to *skb*\ + * **->pkt_type** beside this helper. Using a helper here allows + * for graceful handling of errors. + * + * The major use case is to change incoming *skb*s to + * **PACKET_HOST** in a programmatic way instead of having to + * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for + * example. + * + * Note that *type* only allows certain values. At this time, they + * are: + * + * **PACKET_HOST** + * Packet is for us. + * **PACKET_BROADCAST** + * Send packet to all. + * **PACKET_MULTICAST** + * Send packet to group. + * **PACKET_OTHERHOST** + * Send packet to someone else. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_type)(struct __sk_buff *skb, __u32 type) = (void *) 32; + +/* + * bpf_skb_under_cgroup + * + * Check whether *skb* is a descendant of the cgroup2 held by + * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. + * + * Returns + * The return value depends on the result of the test, and can be: + * + * * 0, if the *skb* failed the cgroup2 descendant test. + * * 1, if the *skb* succeeded the cgroup2 descendant test. + * * A negative error code, if an error occurred. + */ +static long (*bpf_skb_under_cgroup)(struct __sk_buff *skb, void *map, __u32 index) = (void *) 33; + +/* + * bpf_get_hash_recalc + * + * Retrieve the hash of the packet, *skb*\ **->hash**. If it is + * not set, in particular if the hash was cleared due to mangling, + * recompute this hash. Later accesses to the hash can be done + * directly with *skb*\ **->hash**. + * + * Calling **bpf_set_hash_invalid**\ (), changing a packet + * prototype with **bpf_skb_change_proto**\ (), or calling + * **bpf_skb_store_bytes**\ () with the + * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear + * the hash and to trigger a new computation for the next call to + * **bpf_get_hash_recalc**\ (). + * + * Returns + * The 32-bit hash. + */ +static __u32 (*bpf_get_hash_recalc)(struct __sk_buff *skb) = (void *) 34; + +/* + * bpf_get_current_task + * + * + * Returns + * A pointer to the current task struct. + */ +static __u64 (*bpf_get_current_task)(void) = (void *) 35; + +/* + * bpf_probe_write_user + * + * Attempt in a safe way to write *len* bytes from the buffer + * *src* to *dst* in memory. It only works for threads that are in + * user context, and *dst* must be a valid user space address. + * + * This helper should not be used to implement any kind of + * security mechanism because of TOC-TOU attacks, but rather to + * debug, divert, and manipulate execution of semi-cooperative + * processes. + * + * Keep in mind that this feature is meant for experiments, and it + * has a risk of crashing the system and running programs. + * Therefore, when an eBPF program using this helper is attached, + * a warning including PID and process name is printed to kernel + * logs. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_write_user)(void *dst, const void *src, __u32 len) = (void *) 36; + +/* + * bpf_current_task_under_cgroup + * + * Check whether the probe is being run is the context of a given + * subset of the cgroup2 hierarchy. The cgroup2 to test is held by + * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*. + * + * Returns + * The return value depends on the result of the test, and can be: + * + * * 0, if the *skb* task belongs to the cgroup2. + * * 1, if the *skb* task does not belong to the cgroup2. + * * A negative error code, if an error occurred. + */ +static long (*bpf_current_task_under_cgroup)(void *map, __u32 index) = (void *) 37; + +/* + * bpf_skb_change_tail + * + * Resize (trim or grow) the packet associated to *skb* to the + * new *len*. The *flags* are reserved for future usage, and must + * be left at zero. + * + * The basic idea is that the helper performs the needed work to + * change the size of the packet, then the eBPF program rewrites + * the rest via helpers like **bpf_skb_store_bytes**\ (), + * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ () + * and others. This helper is a slow path utility intended for + * replies with control messages. And because it is targeted for + * slow path, the helper itself can afford to be slow: it + * implicitly linearizes, unclones and drops offloads from the + * *skb*. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_tail)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 38; + +/* + * bpf_skb_pull_data + * + * Pull in non-linear data in case the *skb* is non-linear and not + * all of *len* are part of the linear section. Make *len* bytes + * from *skb* readable and writable. If a zero value is passed for + * *len*, then the whole length of the *skb* is pulled. + * + * This helper is only needed for reading and writing with direct + * packet access. + * + * For direct packet access, testing that offsets to access + * are within packet boundaries (test on *skb*\ **->data_end**) is + * susceptible to fail if offsets are invalid, or if the requested + * data is in non-linear parts of the *skb*. On failure the + * program can just bail out, or in the case of a non-linear + * buffer, use a helper to make the data available. The + * **bpf_skb_load_bytes**\ () helper is a first solution to access + * the data. Another one consists in using **bpf_skb_pull_data** + * to pull in once the non-linear parts, then retesting and + * eventually access the data. + * + * At the same time, this also makes sure the *skb* is uncloned, + * which is a necessary condition for direct write. As this needs + * to be an invariant for the write part only, the verifier + * detects writes and adds a prologue that is calling + * **bpf_skb_pull_data()** to effectively unclone the *skb* from + * the very beginning in case it is indeed cloned. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_pull_data)(struct __sk_buff *skb, __u32 len) = (void *) 39; + +/* + * bpf_csum_update + * + * Add the checksum *csum* into *skb*\ **->csum** in case the + * driver has supplied a checksum for the entire packet into that + * field. Return an error otherwise. This helper is intended to be + * used in combination with **bpf_csum_diff**\ (), in particular + * when the checksum needs to be updated after data has been + * written into the packet through direct packet access. + * + * Returns + * The checksum on success, or a negative error code in case of + * failure. + */ +static __s64 (*bpf_csum_update)(struct __sk_buff *skb, __wsum csum) = (void *) 40; + +/* + * bpf_set_hash_invalid + * + * Invalidate the current *skb*\ **->hash**. It can be used after + * mangling on headers through direct packet access, in order to + * indicate that the hash is outdated and to trigger a + * recalculation the next time the kernel tries to access this + * hash or when the **bpf_get_hash_recalc**\ () helper is called. + * + */ +static void (*bpf_set_hash_invalid)(struct __sk_buff *skb) = (void *) 41; + +/* + * bpf_get_numa_node_id + * + * Return the id of the current NUMA node. The primary use case + * for this helper is the selection of sockets for the local NUMA + * node, when the program is attached to sockets using the + * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**), + * but the helper is also available to other eBPF program types, + * similarly to **bpf_get_smp_processor_id**\ (). + * + * Returns + * The id of current NUMA node. + */ +static long (*bpf_get_numa_node_id)(void) = (void *) 42; + +/* + * bpf_skb_change_head + * + * Grows headroom of packet associated to *skb* and adjusts the + * offset of the MAC header accordingly, adding *len* bytes of + * space. It automatically extends and reallocates memory as + * required. + * + * This helper can be used on a layer 3 *skb* to push a MAC header + * for redirection into a layer 2 device. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_change_head)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 43; + +/* + * bpf_xdp_adjust_head + * + * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that + * it is possible to use a negative value for *delta*. This helper + * can be used to prepare the packet for pushing or popping + * headers. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_adjust_head)(struct xdp_md *xdp_md, int delta) = (void *) 44; + +/* + * bpf_probe_read_str + * + * Copy a NUL terminated string from an unsafe kernel address + * *unsafe_ptr* to *dst*. See **bpf_probe_read_kernel_str**\ () for + * more details. + * + * Generally, use **bpf_probe_read_user_str**\ () or + * **bpf_probe_read_kernel_str**\ () instead. + * + * Returns + * On success, the strictly positive length of the string, + * including the trailing NUL character. On error, a negative + * value. + */ +static long (*bpf_probe_read_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 45; + +/* + * bpf_get_socket_cookie + * + * If the **struct sk_buff** pointed by *skb* has a known socket, + * retrieve the cookie (generated by the kernel) of this socket. + * If no cookie has been set yet, generate a new cookie. Once + * generated, the socket cookie remains stable for the life of the + * socket. This helper can be useful for monitoring per socket + * networking traffic statistics as it provides a global socket + * identifier that can be assumed unique. + * + * Returns + * A 8-byte long non-decreasing number on success, or 0 if the + * socket field is missing inside *skb*. + */ +static __u64 (*bpf_get_socket_cookie)(void *ctx) = (void *) 46; + +/* + * bpf_get_socket_uid + * + * + * Returns + * The owner UID of the socket associated to *skb*. If the socket + * is **NULL**, or if it is not a full socket (i.e. if it is a + * time-wait or a request socket instead), **overflowuid** value + * is returned (note that **overflowuid** might also be the actual + * UID value for the socket). + */ +static __u32 (*bpf_get_socket_uid)(struct __sk_buff *skb) = (void *) 47; + +/* + * bpf_set_hash + * + * Set the full hash for *skb* (set the field *skb*\ **->hash**) + * to value *hash*. + * + * Returns + * 0 + */ +static long (*bpf_set_hash)(struct __sk_buff *skb, __u32 hash) = (void *) 48; + +/* + * bpf_setsockopt + * + * Emulate a call to **setsockopt()** on the socket associated to + * *bpf_socket*, which must be a full socket. The *level* at + * which the option resides and the name *optname* of the option + * must be specified, see **setsockopt(2)** for more information. + * The option value of length *optlen* is pointed by *optval*. + * + * *bpf_socket* should be one of the following: + * + * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. + * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** + * and **BPF_CGROUP_INET6_CONNECT**. + * + * This helper actually implements a subset of **setsockopt()**. + * It supports the following *level*\ s: + * + * * **SOL_SOCKET**, which supports the following *optname*\ s: + * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**, + * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**, + * **SO_BINDTODEVICE**, **SO_KEEPALIVE**. + * * **IPPROTO_TCP**, which supports the following *optname*\ s: + * **TCP_CONGESTION**, **TCP_BPF_IW**, + * **TCP_BPF_SNDCWND_CLAMP**, **TCP_SAVE_SYN**, + * **TCP_KEEPIDLE**, **TCP_KEEPINTVL**, **TCP_KEEPCNT**, + * **TCP_SYNCNT**, **TCP_USER_TIMEOUT**. + * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. + * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_setsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 49; + +/* + * bpf_skb_adjust_room + * + * Grow or shrink the room for data in the packet associated to + * *skb* by *len_diff*, and according to the selected *mode*. + * + * By default, the helper will reset any offloaded checksum + * indicator of the skb to CHECKSUM_NONE. This can be avoided + * by the following flag: + * + * * **BPF_F_ADJ_ROOM_NO_CSUM_RESET**: Do not reset offloaded + * checksum data of the skb to CHECKSUM_NONE. + * + * There are two supported modes at this time: + * + * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer + * (room space is added or removed below the layer 2 header). + * + * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer + * (room space is added or removed below the layer 3 header). + * + * The following flags are supported at this time: + * + * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size. + * Adjusting mss in this way is not allowed for datagrams. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**, + * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**: + * Any new space is reserved to hold a tunnel header. + * Configure skb offsets and other fields accordingly. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**, + * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**: + * Use with ENCAP_L3 flags to further specify the tunnel type. + * + * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*): + * Use with ENCAP_L3/L4 flags to further specify the tunnel + * type; *len* is the length of the inner MAC header. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_adjust_room)(struct __sk_buff *skb, __s32 len_diff, __u32 mode, __u64 flags) = (void *) 50; + +/* + * bpf_redirect_map + * + * Redirect the packet to the endpoint referenced by *map* at + * index *key*. Depending on its type, this *map* can contain + * references to net devices (for forwarding packets through other + * ports), or to CPUs (for redirecting XDP frames to another CPU; + * but this is only implemented for native XDP (with driver + * support) as of this writing). + * + * The lower two bits of *flags* are used as the return code if + * the map lookup fails. This is so that the return value can be + * one of the XDP program return codes up to **XDP_TX**, as chosen + * by the caller. Any higher bits in the *flags* argument must be + * unset. + * + * See also **bpf_redirect**\ (), which only supports redirecting + * to an ifindex, but doesn't require a map to do so. + * + * Returns + * **XDP_REDIRECT** on success, or the value of the two lower bits + * of the *flags* argument on error. + */ +static long (*bpf_redirect_map)(void *map, __u32 key, __u64 flags) = (void *) 51; + +/* + * bpf_sk_redirect_map + * + * Redirect the packet to the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_sk_redirect_map)(struct __sk_buff *skb, void *map, __u32 key, __u64 flags) = (void *) 52; + +/* + * bpf_sock_map_update + * + * Add an entry to, or update a *map* referencing sockets. The + * *skops* is used as a new value for the entry associated to + * *key*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * If the *map* has eBPF programs (parser and verdict), those will + * be inherited by the socket being added. If the socket is + * already attached to eBPF programs, this results in an error. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sock_map_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 53; + +/* + * bpf_xdp_adjust_meta + * + * Adjust the address pointed by *xdp_md*\ **->data_meta** by + * *delta* (which can be positive or negative). Note that this + * operation modifies the address stored in *xdp_md*\ **->data**, + * so the latter must be loaded only after the helper has been + * called. + * + * The use of *xdp_md*\ **->data_meta** is optional and programs + * are not required to use it. The rationale is that when the + * packet is processed with XDP (e.g. as DoS filter), it is + * possible to push further meta data along with it before passing + * to the stack, and to give the guarantee that an ingress eBPF + * program attached as a TC classifier on the same device can pick + * this up for further post-processing. Since TC works with socket + * buffers, it remains possible to set from XDP the **mark** or + * **priority** pointers, or other pointers for the socket buffer. + * Having this scratch space generic and programmable allows for + * more flexibility as the user is free to store whatever meta + * data they need. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_adjust_meta)(struct xdp_md *xdp_md, int delta) = (void *) 54; + +/* + * bpf_perf_event_read_value + * + * Read the value of a perf event counter, and store it into *buf* + * of size *buf_size*. This helper relies on a *map* of type + * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event + * counter is selected when *map* is updated with perf event file + * descriptors. The *map* is an array whose size is the number of + * available CPUs, and each cell contains a value relative to one + * CPU. The value to retrieve is indicated by *flags*, that + * contains the index of the CPU to look up, masked with + * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to + * **BPF_F_CURRENT_CPU** to indicate that the value for the + * current CPU should be retrieved. + * + * This helper behaves in a way close to + * **bpf_perf_event_read**\ () helper, save that instead of + * just returning the value observed, it fills the *buf* + * structure. This allows for additional data to be retrieved: in + * particular, the enabled and running times (in *buf*\ + * **->enabled** and *buf*\ **->running**, respectively) are + * copied. In general, **bpf_perf_event_read_value**\ () is + * recommended over **bpf_perf_event_read**\ (), which has some + * ABI issues and provides fewer functionalities. + * + * These values are interesting, because hardware PMU (Performance + * Monitoring Unit) counters are limited resources. When there are + * more PMU based perf events opened than available counters, + * kernel will multiplex these events so each event gets certain + * percentage (but not all) of the PMU time. In case that + * multiplexing happens, the number of samples or counter value + * will not reflect the case compared to when no multiplexing + * occurs. This makes comparison between different runs difficult. + * Typically, the counter value should be normalized before + * comparing to other experiments. The usual normalization is done + * as follows. + * + * :: + * + * normalized_counter = counter * t_enabled / t_running + * + * Where t_enabled is the time enabled for event and t_running is + * the time running for event since last normalization. The + * enabled and running times are accumulated since the perf event + * open. To achieve scaling factor between two invocations of an + * eBPF program, users can use CPU id as the key (which is + * typical for perf array usage model) to remember the previous + * value and do the calculation inside the eBPF program. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_perf_event_read_value)(void *map, __u64 flags, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 55; + +/* + * bpf_perf_prog_read_value + * + * For en eBPF program attached to a perf event, retrieve the + * value of the event counter associated to *ctx* and store it in + * the structure pointed by *buf* and of size *buf_size*. Enabled + * and running times are also stored in the structure (see + * description of helper **bpf_perf_event_read_value**\ () for + * more details). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_perf_prog_read_value)(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 56; + +/* + * bpf_getsockopt + * + * Emulate a call to **getsockopt()** on the socket associated to + * *bpf_socket*, which must be a full socket. The *level* at + * which the option resides and the name *optname* of the option + * must be specified, see **getsockopt(2)** for more information. + * The retrieved value is stored in the structure pointed by + * *opval* and of length *optlen*. + * + * *bpf_socket* should be one of the following: + * + * * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**. + * * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT** + * and **BPF_CGROUP_INET6_CONNECT**. + * + * This helper actually implements a subset of **getsockopt()**. + * It supports the following *level*\ s: + * + * * **IPPROTO_TCP**, which supports *optname* + * **TCP_CONGESTION**. + * * **IPPROTO_IP**, which supports *optname* **IP_TOS**. + * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_getsockopt)(void *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 57; + +/* + * bpf_override_return + * + * Used for error injection, this helper uses kprobes to override + * the return value of the probed function, and to set it to *rc*. + * The first argument is the context *regs* on which the kprobe + * works. + * + * This helper works by setting the PC (program counter) + * to an override function which is run in place of the original + * probed function. This means the probed function is not run at + * all. The replacement function just returns with the required + * value. + * + * This helper has security implications, and thus is subject to + * restrictions. It is only available if the kernel was compiled + * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration + * option, and in this case it only works on functions tagged with + * **ALLOW_ERROR_INJECTION** in the kernel code. + * + * Also, the helper is only available for the architectures having + * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, + * x86 architecture is the only one to support this feature. + * + * Returns + * 0 + */ +static long (*bpf_override_return)(struct pt_regs *regs, __u64 rc) = (void *) 58; + +/* + * bpf_sock_ops_cb_flags_set + * + * Attempt to set the value of the **bpf_sock_ops_cb_flags** field + * for the full TCP socket associated to *bpf_sock_ops* to + * *argval*. + * + * The primary use of this field is to determine if there should + * be calls to eBPF programs of type + * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP + * code. A program of the same type can change its value, per + * connection and as necessary, when the connection is + * established. This field is directly accessible for reading, but + * this helper must be used for updates in order to return an + * error if an eBPF program tries to set a callback that is not + * supported in the current kernel. + * + * *argval* is a flag array which can combine these flags: + * + * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out) + * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission) + * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change) + * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT) + * + * Therefore, this function can be used to clear a callback flag by + * setting the appropriate bit to zero. e.g. to disable the RTO + * callback: + * + * **bpf_sock_ops_cb_flags_set(bpf_sock,** + * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)** + * + * Here are some examples of where one could call such eBPF + * program: + * + * * When RTO fires. + * * When a packet is retransmitted. + * * When the connection terminates. + * * When a packet is sent. + * * When a packet is received. + * + * Returns + * Code **-EINVAL** if the socket is not a full TCP socket; + * otherwise, a positive number containing the bits that could not + * be set is returned (which comes down to 0 if all bits were set + * as required). + */ +static long (*bpf_sock_ops_cb_flags_set)(struct bpf_sock_ops *bpf_sock, int argval) = (void *) 59; + +/* + * bpf_msg_redirect_map + * + * This helper is used in programs implementing policies at the + * socket level. If the message *msg* is allowed to pass (i.e. if + * the verdict eBPF program returns **SK_PASS**), redirect it to + * the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_msg_redirect_map)(struct sk_msg_md *msg, void *map, __u32 key, __u64 flags) = (void *) 60; + +/* + * bpf_msg_apply_bytes + * + * For socket policies, apply the verdict of the eBPF program to + * the next *bytes* (number of bytes) of message *msg*. + * + * For example, this helper can be used in the following cases: + * + * * A single **sendmsg**\ () or **sendfile**\ () system call + * contains multiple logical messages that the eBPF program is + * supposed to read and for which it should apply a verdict. + * * An eBPF program only cares to read the first *bytes* of a + * *msg*. If the message has a large payload, then setting up + * and calling the eBPF program repeatedly for all bytes, even + * though the verdict is already known, would create unnecessary + * overhead. + * + * When called from within an eBPF program, the helper sets a + * counter internal to the BPF infrastructure, that is used to + * apply the last verdict to the next *bytes*. If *bytes* is + * smaller than the current data being processed from a + * **sendmsg**\ () or **sendfile**\ () system call, the first + * *bytes* will be sent and the eBPF program will be re-run with + * the pointer for start of data pointing to byte number *bytes* + * **+ 1**. If *bytes* is larger than the current data being + * processed, then the eBPF verdict will be applied to multiple + * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are + * consumed. + * + * Note that if a socket closes with the internal counter holding + * a non-zero value, this is not a problem because data is not + * being buffered for *bytes* and is sent as it is received. + * + * Returns + * 0 + */ +static long (*bpf_msg_apply_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 61; + +/* + * bpf_msg_cork_bytes + * + * For socket policies, prevent the execution of the verdict eBPF + * program for message *msg* until *bytes* (byte number) have been + * accumulated. + * + * This can be used when one needs a specific number of bytes + * before a verdict can be assigned, even if the data spans + * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme + * case would be a user calling **sendmsg**\ () repeatedly with + * 1-byte long message segments. Obviously, this is bad for + * performance, but it is still valid. If the eBPF program needs + * *bytes* bytes to validate a header, this helper can be used to + * prevent the eBPF program to be called again until *bytes* have + * been accumulated. + * + * Returns + * 0 + */ +static long (*bpf_msg_cork_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 62; + +/* + * bpf_msg_pull_data + * + * For socket policies, pull in non-linear data from user space + * for *msg* and set pointers *msg*\ **->data** and *msg*\ + * **->data_end** to *start* and *end* bytes offsets into *msg*, + * respectively. + * + * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a + * *msg* it can only parse data that the (**data**, **data_end**) + * pointers have already consumed. For **sendmsg**\ () hooks this + * is likely the first scatterlist element. But for calls relying + * on the **sendpage** handler (e.g. **sendfile**\ ()) this will + * be the range (**0**, **0**) because the data is shared with + * user space and by default the objective is to avoid allowing + * user space to modify data while (or after) eBPF verdict is + * being decided. This helper can be used to pull in data and to + * set the start and end pointer to given values. Data will be + * copied if necessary (i.e. if data was not linear and if start + * and end pointers do not point to the same chunk). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_msg_pull_data)(struct sk_msg_md *msg, __u32 start, __u32 end, __u64 flags) = (void *) 63; + +/* + * bpf_bind + * + * Bind the socket associated to *ctx* to the address pointed by + * *addr*, of length *addr_len*. This allows for making outgoing + * connection from the desired IP address, which can be useful for + * example when all processes inside a cgroup should use one + * single IP address on a host that has multiple IP configured. + * + * This helper works for IPv4 and IPv6, TCP and UDP sockets. The + * domain (*addr*\ **->sa_family**) must be **AF_INET** (or + * **AF_INET6**). It's advised to pass zero port (**sin_port** + * or **sin6_port**) which triggers IP_BIND_ADDRESS_NO_PORT-like + * behavior and lets the kernel efficiently pick up an unused + * port as long as 4-tuple is unique. Passing non-zero port might + * lead to degraded performance. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_bind)(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) = (void *) 64; + +/* + * bpf_xdp_adjust_tail + * + * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is + * possible to both shrink and grow the packet tail. + * Shrink done via *delta* being a negative integer. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_adjust_tail)(struct xdp_md *xdp_md, int delta) = (void *) 65; + +/* + * bpf_skb_get_xfrm_state + * + * Retrieve the XFRM state (IP transform framework, see also + * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*. + * + * The retrieved value is stored in the **struct bpf_xfrm_state** + * pointed by *xfrm_state* and of length *size*. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_XFRM** configuration option. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_get_xfrm_state)(struct __sk_buff *skb, __u32 index, struct bpf_xfrm_state *xfrm_state, __u32 size, __u64 flags) = (void *) 66; + +/* + * bpf_get_stack + * + * Return a user or a kernel stack in bpf program provided buffer. + * To achieve this, the helper needs *ctx*, which is a pointer + * to the context on which the tracing program is executed. + * To store the stacktrace, the bpf program provides *buf* with + * a nonnegative *size*. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_USER_BUILD_ID** + * Collect buildid+offset instead of ips for user stack, + * only valid if **BPF_F_USER_STACK** is also specified. + * + * **bpf_get_stack**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject + * to sufficient large buffer size. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack= + * + * Returns + * A non-negative value equal to or less than *size* on success, + * or a negative error in case of failure. + */ +static long (*bpf_get_stack)(void *ctx, void *buf, __u32 size, __u64 flags) = (void *) 67; + +/* + * bpf_skb_load_bytes_relative + * + * This helper is similar to **bpf_skb_load_bytes**\ () in that + * it provides an easy way to load *len* bytes from *offset* + * from the packet associated to *skb*, into the buffer pointed + * by *to*. The difference to **bpf_skb_load_bytes**\ () is that + * a fifth argument *start_header* exists in order to select a + * base offset to start from. *start_header* can be one of: + * + * **BPF_HDR_START_MAC** + * Base offset to load data from is *skb*'s mac header. + * **BPF_HDR_START_NET** + * Base offset to load data from is *skb*'s network header. + * + * In general, "direct packet access" is the preferred method to + * access packet data, however, this helper is in particular useful + * in socket filters where *skb*\ **->data** does not always point + * to the start of the mac header and where "direct packet access" + * is not available. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_load_bytes_relative)(const void *skb, __u32 offset, void *to, __u32 len, __u32 start_header) = (void *) 68; + +/* + * bpf_fib_lookup + * + * Do FIB lookup in kernel tables using parameters in *params*. + * If lookup is successful and result shows packet is to be + * forwarded, the neighbor tables are searched for the nexthop. + * If successful (ie., FIB lookup shows forwarding and nexthop + * is resolved), the nexthop address is returned in ipv4_dst + * or ipv6_dst based on family, smac is set to mac address of + * egress device, dmac is set to nexthop mac address, rt_metric + * is set to metric from route (IPv4/IPv6 only), and ifindex + * is set to the device index of the nexthop from the FIB lookup. + * + * *plen* argument is the size of the passed in struct. + * *flags* argument can be a combination of one or more of the + * following values: + * + * **BPF_FIB_LOOKUP_DIRECT** + * Do a direct table lookup vs full lookup using FIB + * rules. + * **BPF_FIB_LOOKUP_OUTPUT** + * Perform lookup from an egress perspective (default is + * ingress). + * + * *ctx* is either **struct xdp_md** for XDP programs or + * **struct sk_buff** tc cls_act programs. + * + * Returns + * * < 0 if any input argument is invalid + * * 0 on success (packet is forwarded, nexthop neighbor exists) + * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the + * packet is not forwarded or needs assist from full stack + */ +static long (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, int plen, __u32 flags) = (void *) 69; + +/* + * bpf_sock_hash_update + * + * Add an entry to, or update a sockhash *map* referencing sockets. + * The *skops* is used as a new value for the entry associated to + * *key*. *flags* is one of: + * + * **BPF_NOEXIST** + * The entry for *key* must not exist in the map. + * **BPF_EXIST** + * The entry for *key* must already exist in the map. + * **BPF_ANY** + * No condition on the existence of the entry for *key*. + * + * If the *map* has eBPF programs (parser and verdict), those will + * be inherited by the socket being added. If the socket is + * already attached to eBPF programs, this results in an error. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sock_hash_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 70; + +/* + * bpf_msg_redirect_hash + * + * This helper is used in programs implementing policies at the + * socket level. If the message *msg* is allowed to pass (i.e. if + * the verdict eBPF program returns **SK_PASS**), redirect it to + * the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress path otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_msg_redirect_hash)(struct sk_msg_md *msg, void *map, void *key, __u64 flags) = (void *) 71; + +/* + * bpf_sk_redirect_hash + * + * This helper is used in programs implementing policies at the + * skb socket level. If the sk_buff *skb* is allowed to pass (i.e. + * if the verdeict eBPF program returns **SK_PASS**), redirect it + * to the socket referenced by *map* (of type + * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and + * egress interfaces can be used for redirection. The + * **BPF_F_INGRESS** value in *flags* is used to make the + * distinction (ingress path is selected if the flag is present, + * egress otherwise). This is the only flag supported for now. + * + * Returns + * **SK_PASS** on success, or **SK_DROP** on error. + */ +static long (*bpf_sk_redirect_hash)(struct __sk_buff *skb, void *map, void *key, __u64 flags) = (void *) 72; + +/* + * bpf_lwt_push_encap + * + * Encapsulate the packet associated to *skb* within a Layer 3 + * protocol header. This header is provided in the buffer at + * address *hdr*, with *len* its size in bytes. *type* indicates + * the protocol of the header and can be one of: + * + * **BPF_LWT_ENCAP_SEG6** + * IPv6 encapsulation with Segment Routing Header + * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH, + * the IPv6 header is computed by the kernel. + * **BPF_LWT_ENCAP_SEG6_INLINE** + * Only works if *skb* contains an IPv6 packet. Insert a + * Segment Routing Header (**struct ipv6_sr_hdr**) inside + * the IPv6 header. + * **BPF_LWT_ENCAP_IP** + * IP encapsulation (GRE/GUE/IPIP/etc). The outer header + * must be IPv4 or IPv6, followed by zero or more + * additional headers, up to **LWT_BPF_MAX_HEADROOM** + * total bytes in all prepended headers. Please note that + * if **skb_is_gso**\ (*skb*) is true, no more than two + * headers can be prepended, and the inner header, if + * present, should be either GRE or UDP/GUE. + * + * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs + * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can + * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and + * **BPF_PROG_TYPE_LWT_XMIT**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_push_encap)(struct __sk_buff *skb, __u32 type, void *hdr, __u32 len) = (void *) 73; + +/* + * bpf_lwt_seg6_store_bytes + * + * Store *len* bytes from address *from* into the packet + * associated to *skb*, at *offset*. Only the flags, tag and TLVs + * inside the outermost IPv6 Segment Routing Header can be + * modified through this helper. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_seg6_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len) = (void *) 74; + +/* + * bpf_lwt_seg6_adjust_srh + * + * Adjust the size allocated to TLVs in the outermost IPv6 + * Segment Routing Header contained in the packet associated to + * *skb*, at position *offset* by *delta* bytes. Only offsets + * after the segments are accepted. *delta* can be as well + * positive (growing) as negative (shrinking). + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_seg6_adjust_srh)(struct __sk_buff *skb, __u32 offset, __s32 delta) = (void *) 75; + +/* + * bpf_lwt_seg6_action + * + * Apply an IPv6 Segment Routing action of type *action* to the + * packet associated to *skb*. Each action takes a parameter + * contained at address *param*, and of length *param_len* bytes. + * *action* can be one of: + * + * **SEG6_LOCAL_ACTION_END_X** + * End.X action: Endpoint with Layer-3 cross-connect. + * Type of *param*: **struct in6_addr**. + * **SEG6_LOCAL_ACTION_END_T** + * End.T action: Endpoint with specific IPv6 table lookup. + * Type of *param*: **int**. + * **SEG6_LOCAL_ACTION_END_B6** + * End.B6 action: Endpoint bound to an SRv6 policy. + * Type of *param*: **struct ipv6_sr_hdr**. + * **SEG6_LOCAL_ACTION_END_B6_ENCAP** + * End.B6.Encap action: Endpoint bound to an SRv6 + * encapsulation policy. + * Type of *param*: **struct ipv6_sr_hdr**. + * + * A call to this helper is susceptible to change the underlying + * packet buffer. Therefore, at load time, all checks on pointers + * previously done by the verifier are invalidated and must be + * performed again, if the helper is used in combination with + * direct packet access. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_lwt_seg6_action)(struct __sk_buff *skb, __u32 action, void *param, __u32 param_len) = (void *) 76; + +/* + * bpf_rc_repeat + * + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded repeat key message. This delays + * the generation of a key up event for previously generated + * key down event. + * + * Some IR protocols like NEC have a special IR message for + * repeating last button, for when a button is held down. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * + * Returns + * 0 + */ +static long (*bpf_rc_repeat)(void *ctx) = (void *) 77; + +/* + * bpf_rc_keydown + * + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded key press with *scancode*, + * *toggle* value in the given *protocol*. The scancode will be + * translated to a keycode using the rc keymap, and reported as + * an input key down event. After a period a key up event is + * generated. This period can be extended by calling either + * **bpf_rc_keydown**\ () again with the same values, or calling + * **bpf_rc_repeat**\ (). + * + * Some protocols include a toggle bit, in case the button was + * released and pressed again between consecutive scancodes. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * The *protocol* is the decoded protocol number (see + * **enum rc_proto** for some predefined values). + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * + * Returns + * 0 + */ +static long (*bpf_rc_keydown)(void *ctx, __u32 protocol, __u64 scancode, __u32 toggle) = (void *) 78; + +/* + * bpf_skb_cgroup_id + * + * Return the cgroup v2 id of the socket associated with the *skb*. + * This is roughly similar to the **bpf_get_cgroup_classid**\ () + * helper for cgroup v1 by providing a tag resp. identifier that + * can be matched on or used for map lookups e.g. to implement + * policy. The cgroup v2 id of a given path in the hierarchy is + * exposed in user space through the f_handle API in order to get + * to the same 64-bit id. + * + * This helper can be used on TC egress path, but not on ingress, + * and is available only if the kernel was compiled with the + * **CONFIG_SOCK_CGROUP_DATA** configuration option. + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_skb_cgroup_id)(struct __sk_buff *skb) = (void *) 79; + +/* + * bpf_get_current_cgroup_id + * + * + * Returns + * A 64-bit integer containing the current cgroup id based + * on the cgroup within which the current task is running. + */ +static __u64 (*bpf_get_current_cgroup_id)(void) = (void *) 80; + +/* + * bpf_get_local_storage + * + * Get the pointer to the local storage area. + * The type and the size of the local storage is defined + * by the *map* argument. + * The *flags* meaning is specific for each map type, + * and has to be 0 for cgroup local storage. + * + * Depending on the BPF program type, a local storage area + * can be shared between multiple instances of the BPF program, + * running simultaneously. + * + * A user should care about the synchronization by himself. + * For example, by using the **BPF_STX_XADD** instruction to alter + * the shared data. + * + * Returns + * A pointer to the local storage area. + */ +static void *(*bpf_get_local_storage)(void *map, __u64 flags) = (void *) 81; + +/* + * bpf_sk_select_reuseport + * + * Select a **SO_REUSEPORT** socket from a + * **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*. + * It checks the selected socket is matching the incoming + * request in the socket buffer. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sk_select_reuseport)(struct sk_reuseport_md *reuse, void *map, void *key, __u64 flags) = (void *) 82; + +/* + * bpf_skb_ancestor_cgroup_id + * + * Return id of cgroup v2 that is ancestor of cgroup associated + * with the *skb* at the *ancestor_level*. The root cgroup is at + * *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with *skb*, then return value will be same as that + * of **bpf_skb_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with *skb*. + * + * The format of returned id and helper limitations are same as in + * **bpf_skb_cgroup_id**\ (). + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_skb_ancestor_cgroup_id)(struct __sk_buff *skb, int ancestor_level) = (void *) 83; + +/* + * bpf_sk_lookup_tcp + * + * Look for TCP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * The *ctx* should point to the context of the program, such as + * the skb or socket (depending on the hook in use). This is used + * to determine the base network namespace for the lookup. + * + * *tuple_size* must be one of: + * + * **sizeof**\ (*tuple*\ **->ipv4**) + * Look for an IPv4 socket. + * **sizeof**\ (*tuple*\ **->ipv6**) + * Look for an IPv6 socket. + * + * If the *netns* is a negative signed 32-bit integer, then the + * socket lookup table in the netns associated with the *ctx* + * will be used. For the TC hooks, this is the netns of the device + * in the skb. For socket hooks, this is the netns of the socket. + * If *netns* is any other signed 32-bit value greater than or + * equal to zero then it specifies the ID of the netns relative to + * the netns associated with the *ctx*. *netns* values beyond the + * range of 32-bit integers are reserved for future use. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * + * Returns + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + */ +static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 84; + +/* + * bpf_sk_lookup_udp + * + * Look for UDP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * The *ctx* should point to the context of the program, such as + * the skb or socket (depending on the hook in use). This is used + * to determine the base network namespace for the lookup. + * + * *tuple_size* must be one of: + * + * **sizeof**\ (*tuple*\ **->ipv4**) + * Look for an IPv4 socket. + * **sizeof**\ (*tuple*\ **->ipv6**) + * Look for an IPv6 socket. + * + * If the *netns* is a negative signed 32-bit integer, then the + * socket lookup table in the netns associated with the *ctx* + * will be used. For the TC hooks, this is the netns of the device + * in the skb. For socket hooks, this is the netns of the socket. + * If *netns* is any other signed 32-bit value greater than or + * equal to zero then it specifies the ID of the netns relative to + * the netns associated with the *ctx*. *netns* values beyond the + * range of 32-bit integers are reserved for future use. + * + * All values for *flags* are reserved for future usage, and must + * be left at zero. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * + * Returns + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + */ +static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 85; + +/* + * bpf_sk_release + * + * Release the reference held by *sock*. *sock* must be a + * non-**NULL** pointer that was returned from + * **bpf_sk_lookup_xxx**\ (). + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_sk_release)(struct bpf_sock *sock) = (void *) 86; + +/* + * bpf_map_push_elem + * + * Push an element *value* in *map*. *flags* is one of: + * + * **BPF_EXIST** + * If the queue/stack is full, the oldest element is + * removed to make room for this. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_push_elem)(void *map, const void *value, __u64 flags) = (void *) 87; + +/* + * bpf_map_pop_elem + * + * Pop an element from *map*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_pop_elem)(void *map, void *value) = (void *) 88; + +/* + * bpf_map_peek_elem + * + * Get an element from *map* without removing it. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_map_peek_elem)(void *map, void *value) = (void *) 89; + +/* + * bpf_msg_push_data + * + * For socket policies, insert *len* bytes into *msg* at offset + * *start*. + * + * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a + * *msg* it may want to insert metadata or options into the *msg*. + * This can later be read and used by any of the lower layer BPF + * hooks. + * + * This helper may fail if under memory pressure (a malloc + * fails) in these cases BPF programs will get an appropriate + * error and BPF programs will need to handle them. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_msg_push_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 90; + +/* + * bpf_msg_pop_data + * + * Will remove *len* bytes from a *msg* starting at byte *start*. + * This may result in **ENOMEM** errors under certain situations if + * an allocation and copy are required due to a full ring buffer. + * However, the helper will try to avoid doing the allocation + * if possible. Other errors can occur if input parameters are + * invalid either due to *start* byte not being valid part of *msg* + * payload and/or *pop* value being to large. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_msg_pop_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 91; + +/* + * bpf_rc_pointer_rel + * + * This helper is used in programs implementing IR decoding, to + * report a successfully decoded pointer movement. + * + * The *ctx* should point to the lirc sample as passed into + * the program. + * + * This helper is only available is the kernel was compiled with + * the **CONFIG_BPF_LIRC_MODE2** configuration option set to + * "**y**". + * + * Returns + * 0 + */ +static long (*bpf_rc_pointer_rel)(void *ctx, __s32 rel_x, __s32 rel_y) = (void *) 92; + +/* + * bpf_spin_lock + * + * Acquire a spinlock represented by the pointer *lock*, which is + * stored as part of a value of a map. Taking the lock allows to + * safely update the rest of the fields in that value. The + * spinlock can (and must) later be released with a call to + * **bpf_spin_unlock**\ (\ *lock*\ ). + * + * Spinlocks in BPF programs come with a number of restrictions + * and constraints: + * + * * **bpf_spin_lock** objects are only allowed inside maps of + * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this + * list could be extended in the future). + * * BTF description of the map is mandatory. + * * The BPF program can take ONE lock at a time, since taking two + * or more could cause dead locks. + * * Only one **struct bpf_spin_lock** is allowed per map element. + * * When the lock is taken, calls (either BPF to BPF or helpers) + * are not allowed. + * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not + * allowed inside a spinlock-ed region. + * * The BPF program MUST call **bpf_spin_unlock**\ () to release + * the lock, on all execution paths, before it returns. + * * The BPF program can access **struct bpf_spin_lock** only via + * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ () + * helpers. Loading or storing data into the **struct + * bpf_spin_lock** *lock*\ **;** field of a map is not allowed. + * * To use the **bpf_spin_lock**\ () helper, the BTF description + * of the map value must be a struct and have **struct + * bpf_spin_lock** *anyname*\ **;** field at the top level. + * Nested lock inside another struct is not allowed. + * * The **struct bpf_spin_lock** *lock* field in a map value must + * be aligned on a multiple of 4 bytes in that value. + * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy + * the **bpf_spin_lock** field to user space. + * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from + * a BPF program, do not update the **bpf_spin_lock** field. + * * **bpf_spin_lock** cannot be on the stack or inside a + * networking packet (it can only be inside of a map values). + * * **bpf_spin_lock** is available to root only. + * * Tracing programs and socket filter programs cannot use + * **bpf_spin_lock**\ () due to insufficient preemption checks + * (but this may change in the future). + * * **bpf_spin_lock** is not allowed in inner maps of map-in-map. + * + * Returns + * 0 + */ +static long (*bpf_spin_lock)(struct bpf_spin_lock *lock) = (void *) 93; + +/* + * bpf_spin_unlock + * + * Release the *lock* previously locked by a call to + * **bpf_spin_lock**\ (\ *lock*\ ). + * + * Returns + * 0 + */ +static long (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = (void *) 94; + +/* + * bpf_sk_fullsock + * + * This helper gets a **struct bpf_sock** pointer such + * that all the fields in this **bpf_sock** can be accessed. + * + * Returns + * A **struct bpf_sock** pointer on success, or **NULL** in + * case of failure. + */ +static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = (void *) 95; + +/* + * bpf_tcp_sock + * + * This helper gets a **struct bpf_tcp_sock** pointer from a + * **struct bpf_sock** pointer. + * + * Returns + * A **struct bpf_tcp_sock** pointer on success, or **NULL** in + * case of failure. + */ +static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = (void *) 96; + +/* + * bpf_skb_ecn_set_ce + * + * Set ECN (Explicit Congestion Notification) field of IP header + * to **CE** (Congestion Encountered) if current value is **ECT** + * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6 + * and IPv4. + * + * Returns + * 1 if the **CE** flag is set (either by the current helper call + * or because it was already present), 0 if it is not set. + */ +static long (*bpf_skb_ecn_set_ce)(struct __sk_buff *skb) = (void *) 97; + +/* + * bpf_get_listener_sock + * + * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state. + * **bpf_sk_release**\ () is unnecessary and not allowed. + * + * Returns + * A **struct bpf_sock** pointer on success, or **NULL** in + * case of failure. + */ +static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = (void *) 98; + +/* + * bpf_skc_lookup_tcp + * + * Look for TCP socket matching *tuple*, optionally in a child + * network namespace *netns*. The return value must be checked, + * and if non-**NULL**, released via **bpf_sk_release**\ (). + * + * This function is identical to **bpf_sk_lookup_tcp**\ (), except + * that it also returns timewait or request sockets. Use + * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the + * full structure. + * + * This helper is available only if the kernel was compiled with + * **CONFIG_NET** configuration option. + * + * Returns + * Pointer to **struct bpf_sock**, or **NULL** in case of failure. + * For sockets with reuseport option, the **struct bpf_sock** + * result is from *reuse*\ **->socks**\ [] using the hash of the + * tuple. + */ +static struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 99; + +/* + * bpf_tcp_check_syncookie + * + * Check whether *iph* and *th* contain a valid SYN cookie ACK for + * the listening socket in *sk*. + * + * *iph* points to the start of the IPv4 or IPv6 header, while + * *iph_len* contains **sizeof**\ (**struct iphdr**) or + * **sizeof**\ (**struct ip6hdr**). + * + * *th* points to the start of the TCP header, while *th_len* + * contains **sizeof**\ (**struct tcphdr**). + * + * Returns + * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative + * error otherwise. + */ +static long (*bpf_tcp_check_syncookie)(struct bpf_sock *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 100; + +/* + * bpf_sysctl_get_name + * + * Get name of sysctl in /proc/sys/ and copy it into provided by + * program buffer *buf* of size *buf_len*. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * + * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is + * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name + * only (e.g. "tcp_mem"). + * + * Returns + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + */ +static long (*bpf_sysctl_get_name)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len, __u64 flags) = (void *) 101; + +/* + * bpf_sysctl_get_current_value + * + * Get current value of sysctl as it is presented in /proc/sys + * (incl. newline, etc), and copy it as a string into provided + * by program buffer *buf* of size *buf_len*. + * + * The whole value is copied, no matter what file position user + * space issued e.g. sys_read at. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * + * Returns + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + * + * **-EINVAL** if current value was unavailable, e.g. because + * sysctl is uninitialized and read returns -EIO for it. + */ +static long (*bpf_sysctl_get_current_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 102; + +/* + * bpf_sysctl_get_new_value + * + * Get new value being written by user space to sysctl (before + * the actual write happens) and copy it as a string into + * provided by program buffer *buf* of size *buf_len*. + * + * User space may write new value at file position > 0. + * + * The buffer is always NUL terminated, unless it's zero-sized. + * + * Returns + * Number of character copied (not including the trailing NUL). + * + * **-E2BIG** if the buffer wasn't big enough (*buf* will contain + * truncated name in this case). + * + * **-EINVAL** if sysctl is being read. + */ +static long (*bpf_sysctl_get_new_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 103; + +/* + * bpf_sysctl_set_new_value + * + * Override new value being written by user space to sysctl with + * value provided by program in buffer *buf* of size *buf_len*. + * + * *buf* should contain a string in same form as provided by user + * space on sysctl write. + * + * User space may write new value at file position > 0. To override + * the whole sysctl value file position should be set to zero. + * + * Returns + * 0 on success. + * + * **-E2BIG** if the *buf_len* is too big. + * + * **-EINVAL** if sysctl is being read. + */ +static long (*bpf_sysctl_set_new_value)(struct bpf_sysctl *ctx, const char *buf, unsigned long buf_len) = (void *) 104; + +/* + * bpf_strtol + * + * Convert the initial part of the string from buffer *buf* of + * size *buf_len* to a long integer according to the given base + * and save the result in *res*. + * + * The string may begin with an arbitrary amount of white space + * (as determined by **isspace**\ (3)) followed by a single + * optional '**-**' sign. + * + * Five least significant bits of *flags* encode base, other bits + * are currently unused. + * + * Base must be either 8, 10, 16 or 0 to detect it automatically + * similar to user space **strtol**\ (3). + * + * Returns + * Number of characters consumed on success. Must be positive but + * no more than *buf_len*. + * + * **-EINVAL** if no valid digits were found or unsupported base + * was provided. + * + * **-ERANGE** if resulting value was out of range. + */ +static long (*bpf_strtol)(const char *buf, unsigned long buf_len, __u64 flags, long *res) = (void *) 105; + +/* + * bpf_strtoul + * + * Convert the initial part of the string from buffer *buf* of + * size *buf_len* to an unsigned long integer according to the + * given base and save the result in *res*. + * + * The string may begin with an arbitrary amount of white space + * (as determined by **isspace**\ (3)). + * + * Five least significant bits of *flags* encode base, other bits + * are currently unused. + * + * Base must be either 8, 10, 16 or 0 to detect it automatically + * similar to user space **strtoul**\ (3). + * + * Returns + * Number of characters consumed on success. Must be positive but + * no more than *buf_len*. + * + * **-EINVAL** if no valid digits were found or unsupported base + * was provided. + * + * **-ERANGE** if resulting value was out of range. + */ +static long (*bpf_strtoul)(const char *buf, unsigned long buf_len, __u64 flags, unsigned long *res) = (void *) 106; + +/* + * bpf_sk_storage_get + * + * Get a bpf-local-storage from a *sk*. + * + * Logically, it could be thought of getting the value from + * a *map* with *sk* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this + * helper enforces the key must be a full socket and the map must + * be a **BPF_MAP_TYPE_SK_STORAGE** also. + * + * Underneath, the value is stored locally at *sk* instead of + * the *map*. The *map* is used as the bpf-local-storage + * "type". The bpf-local-storage "type" (i.e. the *map*) is + * searched against all bpf-local-storages residing at *sk*. + * + * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf-local-storage will be + * created if one does not exist. *value* can be used + * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf-local-storage. If *value* is + * **NULL**, the new bpf-local-storage will be zero initialized. + * + * Returns + * A bpf-local-storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf-local-storage. + */ +static void *(*bpf_sk_storage_get)(void *map, struct bpf_sock *sk, void *value, __u64 flags) = (void *) 107; + +/* + * bpf_sk_storage_delete + * + * Delete a bpf-local-storage from a *sk*. + * + * Returns + * 0 on success. + * + * **-ENOENT** if the bpf-local-storage cannot be found. + */ +static long (*bpf_sk_storage_delete)(void *map, struct bpf_sock *sk) = (void *) 108; + +/* + * bpf_send_signal + * + * Send signal *sig* to the process of the current task. + * The signal may be delivered to any of this process's threads. + * + * Returns + * 0 on success or successfully queued. + * + * **-EBUSY** if work queue under nmi is full. + * + * **-EINVAL** if *sig* is invalid. + * + * **-EPERM** if no permission to send the *sig*. + * + * **-EAGAIN** if bpf program can try again. + */ +static long (*bpf_send_signal)(__u32 sig) = (void *) 109; + +/* + * bpf_tcp_gen_syncookie + * + * Try to issue a SYN cookie for the packet with corresponding + * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*. + * + * *iph* points to the start of the IPv4 or IPv6 header, while + * *iph_len* contains **sizeof**\ (**struct iphdr**) or + * **sizeof**\ (**struct ip6hdr**). + * + * *th* points to the start of the TCP header, while *th_len* + * contains the length of the TCP header. + * + * Returns + * On success, lower 32 bits hold the generated SYN cookie in + * followed by 16 bits which hold the MSS value for that cookie, + * and the top 16 bits are unused. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** SYN cookie cannot be issued due to error + * + * **-ENOENT** SYN cookie should not be issued (no SYN flood) + * + * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies + * + * **-EPROTONOSUPPORT** IP packet version is not 4 or 6 + */ +static __s64 (*bpf_tcp_gen_syncookie)(struct bpf_sock *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 110; + +/* + * bpf_skb_output + * + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * *ctx* is a pointer to in-kernel struct sk_buff. + * + * This helper is similar to **bpf_perf_event_output**\ () but + * restricted to raw_tracepoint bpf programs. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_skb_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 111; + +/* + * bpf_probe_read_user + * + * Safely attempt to read *size* bytes from user space address + * *unsafe_ptr* and store the data in *dst*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_read_user)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 112; + +/* + * bpf_probe_read_kernel + * + * Safely attempt to read *size* bytes from kernel space address + * *unsafe_ptr* and store the data in *dst*. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_probe_read_kernel)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 113; + +/* + * bpf_probe_read_user_str + * + * Copy a NUL terminated string from an unsafe user address + * *unsafe_ptr* to *dst*. The *size* should include the + * terminating NUL byte. In case the string length is smaller than + * *size*, the target is not padded with further NUL bytes. If the + * string length is larger than *size*, just *size*-1 bytes are + * copied and the last byte is set to NUL. + * + * On success, the length of the copied string is returned. This + * makes this helper useful in tracing programs for reading + * strings, and more importantly to get its length at runtime. See + * the following snippet: + * + * :: + * + * SEC("kprobe/sys_open") + * void bpf_sys_open(struct pt_regs *ctx) + * { + * char buf[PATHLEN]; // PATHLEN is defined to 256 + * int res = bpf_probe_read_user_str(buf, sizeof(buf), + * ctx->di); + * + * // Consume buf, for example push it to + * // userspace via bpf_perf_event_output(); we + * // can use res (the string length) as event + * // size, after checking its boundaries. + * } + * + * In comparison, using **bpf_probe_read_user**\ () helper here + * instead to read the string would require to estimate the length + * at compile time, and would often result in copying more memory + * than necessary. + * + * Another useful use case is when parsing individual process + * arguments or individual environment variables navigating + * *current*\ **->mm->arg_start** and *current*\ + * **->mm->env_start**: using this helper and the return value, + * one can quickly iterate at the right offset of the memory area. + * + * Returns + * On success, the strictly positive length of the string, + * including the trailing NUL character. On error, a negative + * value. + */ +static long (*bpf_probe_read_user_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 114; + +/* + * bpf_probe_read_kernel_str + * + * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr* + * to *dst*. Same semantics as with **bpf_probe_read_user_str**\ () apply. + * + * Returns + * On success, the strictly positive length of the string, including + * the trailing NUL character. On error, a negative value. + */ +static long (*bpf_probe_read_kernel_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 115; + +/* + * bpf_tcp_send_ack + * + * Send out a tcp-ack. *tp* is the in-kernel struct **tcp_sock**. + * *rcv_nxt* is the ack_seq to be sent out. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_tcp_send_ack)(void *tp, __u32 rcv_nxt) = (void *) 116; + +/* + * bpf_send_signal_thread + * + * Send signal *sig* to the thread corresponding to the current task. + * + * Returns + * 0 on success or successfully queued. + * + * **-EBUSY** if work queue under nmi is full. + * + * **-EINVAL** if *sig* is invalid. + * + * **-EPERM** if no permission to send the *sig*. + * + * **-EAGAIN** if bpf program can try again. + */ +static long (*bpf_send_signal_thread)(__u32 sig) = (void *) 117; + +/* + * bpf_jiffies64 + * + * Obtain the 64bit jiffies + * + * Returns + * The 64 bit jiffies + */ +static __u64 (*bpf_jiffies64)(void) = (void *) 118; + +/* + * bpf_read_branch_records + * + * For an eBPF program attached to a perf event, retrieve the + * branch records (**struct perf_branch_entry**) associated to *ctx* + * and store it in the buffer pointed by *buf* up to size + * *size* bytes. + * + * Returns + * On success, number of bytes written to *buf*. On error, a + * negative value. + * + * The *flags* can be set to **BPF_F_GET_BRANCH_RECORDS_SIZE** to + * instead return the number of bytes required to store all the + * branch entries. If this flag is set, *buf* may be NULL. + * + * **-EINVAL** if arguments invalid or **size** not a multiple + * of **sizeof**\ (**struct perf_branch_entry**\ ). + * + * **-ENOENT** if architecture does not support branch records. + */ +static long (*bpf_read_branch_records)(struct bpf_perf_event_data *ctx, void *buf, __u32 size, __u64 flags) = (void *) 119; + +/* + * bpf_get_ns_current_pid_tgid + * + * Returns 0 on success, values for *pid* and *tgid* as seen from the current + * *namespace* will be returned in *nsdata*. + * + * Returns + * 0 on success, or one of the following in case of failure: + * + * **-EINVAL** if dev and inum supplied don't match dev_t and inode number + * with nsfs of current task, or if dev conversion to dev_t lost high bits. + * + * **-ENOENT** if pidns does not exists for the current task. + */ +static long (*bpf_get_ns_current_pid_tgid)(__u64 dev, __u64 ino, struct bpf_pidns_info *nsdata, __u32 size) = (void *) 120; + +/* + * bpf_xdp_output + * + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * *ctx* is a pointer to in-kernel struct xdp_buff. + * + * This helper is similar to **bpf_perf_eventoutput**\ () but + * restricted to raw_tracepoint bpf programs. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_xdp_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 121; + +/* + * bpf_get_netns_cookie + * + * Retrieve the cookie (generated by the kernel) of the network + * namespace the input *ctx* is associated with. The network + * namespace cookie remains stable for its lifetime and provides + * a global identifier that can be assumed unique. If *ctx* is + * NULL, then the helper returns the cookie for the initial + * network namespace. The cookie itself is very similar to that + * of **bpf_get_socket_cookie**\ () helper, but for network + * namespaces instead of sockets. + * + * Returns + * A 8-byte long opaque number. + */ +static __u64 (*bpf_get_netns_cookie)(void *ctx) = (void *) 122; + +/* + * bpf_get_current_ancestor_cgroup_id + * + * Return id of cgroup v2 that is ancestor of the cgroup associated + * with the current task at the *ancestor_level*. The root cgroup + * is at *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with the current task, then return value will be the + * same as that of **bpf_get_current_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with the current task. + * + * The format of returned id and helper limitations are same as in + * **bpf_get_current_cgroup_id**\ (). + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_get_current_ancestor_cgroup_id)(int ancestor_level) = (void *) 123; + +/* + * bpf_sk_assign + * + * Helper is overloaded depending on BPF program type. This + * description applies to **BPF_PROG_TYPE_SCHED_CLS** and + * **BPF_PROG_TYPE_SCHED_ACT** programs. + * + * Assign the *sk* to the *skb*. When combined with appropriate + * routing configuration to receive the packet towards the socket, + * will cause *skb* to be delivered to the specified socket. + * Subsequent redirection of *skb* via **bpf_redirect**\ (), + * **bpf_clone_redirect**\ () or other methods outside of BPF may + * interfere with successful delivery to the socket. + * + * This operation is only valid from TC ingress path. + * + * The *flags* argument must be zero. + * + * Returns + * 0 on success, or a negative error in case of failure: + * + * **-EINVAL** if specified *flags* are not supported. + * + * **-ENOENT** if the socket is unavailable for assignment. + * + * **-ENETUNREACH** if the socket is unreachable (wrong netns). + * + * **-EOPNOTSUPP** if the operation is not supported, for example + * a call from outside of TC ingress. + * + * **-ESOCKTNOSUPPORT** if the socket type is not supported + * (reuseport). + */ +static long (*bpf_sk_assign)(void *ctx, struct bpf_sock *sk, __u64 flags) = (void *) 124; + +/* + * bpf_ktime_get_boot_ns + * + * Return the time elapsed since system boot, in nanoseconds. + * Does include the time the system was suspended. + * See: **clock_gettime**\ (**CLOCK_BOOTTIME**) + * + * Returns + * Current *ktime*. + */ +static __u64 (*bpf_ktime_get_boot_ns)(void) = (void *) 125; + +/* + * bpf_seq_printf + * + * **bpf_seq_printf**\ () uses seq_file **seq_printf**\ () to print + * out the format string. + * The *m* represents the seq_file. The *fmt* and *fmt_size* are for + * the format string itself. The *data* and *data_len* are format string + * arguments. The *data* are a **u64** array and corresponding format string + * values are stored in the array. For strings and pointers where pointees + * are accessed, only the pointer values are stored in the *data* array. + * The *data_len* is the size of *data* in bytes. + * + * Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory. + * Reading kernel memory may fail due to either invalid address or + * valid address but requiring a major memory fault. If reading kernel memory + * fails, the string for **%s** will be an empty string, and the ip + * address for **%p{i,I}{4,6}** will be 0. Not returning error to + * bpf program is consistent with what **bpf_trace_printk**\ () does for now. + * + * Returns + * 0 on success, or a negative error in case of failure: + * + * **-EBUSY** if per-CPU memory copy buffer is busy, can try again + * by returning 1 from bpf program. + * + * **-EINVAL** if arguments are invalid, or if *fmt* is invalid/unsupported. + * + * **-E2BIG** if *fmt* contains too many format specifiers. + * + * **-EOVERFLOW** if an overflow happened: The same object will be tried again. + */ +static long (*bpf_seq_printf)(struct seq_file *m, const char *fmt, __u32 fmt_size, const void *data, __u32 data_len) = (void *) 126; + +/* + * bpf_seq_write + * + * **bpf_seq_write**\ () uses seq_file **seq_write**\ () to write the data. + * The *m* represents the seq_file. The *data* and *len* represent the + * data to write in bytes. + * + * Returns + * 0 on success, or a negative error in case of failure: + * + * **-EOVERFLOW** if an overflow happened: The same object will be tried again. + */ +static long (*bpf_seq_write)(struct seq_file *m, const void *data, __u32 len) = (void *) 127; + +/* + * bpf_sk_cgroup_id + * + * Return the cgroup v2 id of the socket *sk*. + * + * *sk* must be a non-**NULL** pointer to a full socket, e.g. one + * returned from **bpf_sk_lookup_xxx**\ (), + * **bpf_sk_fullsock**\ (), etc. The format of returned id is + * same as in **bpf_skb_cgroup_id**\ (). + * + * This helper is available only if the kernel was compiled with + * the **CONFIG_SOCK_CGROUP_DATA** configuration option. + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_sk_cgroup_id)(struct bpf_sock *sk) = (void *) 128; + +/* + * bpf_sk_ancestor_cgroup_id + * + * Return id of cgroup v2 that is ancestor of cgroup associated + * with the *sk* at the *ancestor_level*. The root cgroup is at + * *ancestor_level* zero and each step down the hierarchy + * increments the level. If *ancestor_level* == level of cgroup + * associated with *sk*, then return value will be same as that + * of **bpf_sk_cgroup_id**\ (). + * + * The helper is useful to implement policies based on cgroups + * that are upper in hierarchy than immediate cgroup associated + * with *sk*. + * + * The format of returned id and helper limitations are same as in + * **bpf_sk_cgroup_id**\ (). + * + * Returns + * The id is returned or 0 in case the id could not be retrieved. + */ +static __u64 (*bpf_sk_ancestor_cgroup_id)(struct bpf_sock *sk, int ancestor_level) = (void *) 129; + +/* + * bpf_ringbuf_output + * + * Copy *size* bytes from *data* into a ring buffer *ringbuf*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * + * Returns + * 0 on success, or a negative error in case of failure. + */ +static long (*bpf_ringbuf_output)(void *ringbuf, void *data, __u64 size, __u64 flags) = (void *) 130; + +/* + * bpf_ringbuf_reserve + * + * Reserve *size* bytes of payload in a ring buffer *ringbuf*. + * + * Returns + * Valid pointer with *size* bytes of memory available; NULL, + * otherwise. + */ +static void *(*bpf_ringbuf_reserve)(void *ringbuf, __u64 size, __u64 flags) = (void *) 131; + +/* + * bpf_ringbuf_submit + * + * Submit reserved ring buffer sample, pointed to by *data*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * + * Returns + * Nothing. Always succeeds. + */ +static void (*bpf_ringbuf_submit)(void *data, __u64 flags) = (void *) 132; + +/* + * bpf_ringbuf_discard + * + * Discard reserved ring buffer sample, pointed to by *data*. + * If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification + * of new data availability is sent. + * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification + * of new data availability is sent unconditionally. + * + * Returns + * Nothing. Always succeeds. + */ +static void (*bpf_ringbuf_discard)(void *data, __u64 flags) = (void *) 133; + +/* + * bpf_ringbuf_query + * + * Query various characteristics of provided ring buffer. What + * exactly is queries is determined by *flags*: + * + * * **BPF_RB_AVAIL_DATA**: Amount of data not yet consumed. + * * **BPF_RB_RING_SIZE**: The size of ring buffer. + * * **BPF_RB_CONS_POS**: Consumer position (can wrap around). + * * **BPF_RB_PROD_POS**: Producer(s) position (can wrap around). + * + * Data returned is just a momentary snapshot of actual values + * and could be inaccurate, so this facility should be used to + * power heuristics and for reporting, not to make 100% correct + * calculation. + * + * Returns + * Requested value, or 0, if *flags* are not recognized. + */ +static __u64 (*bpf_ringbuf_query)(void *ringbuf, __u64 flags) = (void *) 134; + +/* + * bpf_csum_level + * + * Change the skbs checksum level by one layer up or down, or + * reset it entirely to none in order to have the stack perform + * checksum validation. The level is applicable to the following + * protocols: TCP, UDP, GRE, SCTP, FCOE. For example, a decap of + * | ETH | IP | UDP | GUE | IP | TCP | into | ETH | IP | TCP | + * through **bpf_skb_adjust_room**\ () helper with passing in + * **BPF_F_ADJ_ROOM_NO_CSUM_RESET** flag would require one call + * to **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_DEC** since + * the UDP header is removed. Similarly, an encap of the latter + * into the former could be accompanied by a helper call to + * **bpf_csum_level**\ () with **BPF_CSUM_LEVEL_INC** if the + * skb is still intended to be processed in higher layers of the + * stack instead of just egressing at tc. + * + * There are three supported level settings at this time: + * + * * **BPF_CSUM_LEVEL_INC**: Increases skb->csum_level for skbs + * with CHECKSUM_UNNECESSARY. + * * **BPF_CSUM_LEVEL_DEC**: Decreases skb->csum_level for skbs + * with CHECKSUM_UNNECESSARY. + * * **BPF_CSUM_LEVEL_RESET**: Resets skb->csum_level to 0 and + * sets CHECKSUM_NONE to force checksum validation by the stack. + * * **BPF_CSUM_LEVEL_QUERY**: No-op, returns the current + * skb->csum_level. + * + * Returns + * 0 on success, or a negative error in case of failure. In the + * case of **BPF_CSUM_LEVEL_QUERY**, the current skb->csum_level + * is returned or the error code -EACCES in case the skb is not + * subject to CHECKSUM_UNNECESSARY. + */ +static long (*bpf_csum_level)(struct __sk_buff *skb, __u64 level) = (void *) 135; + +/* + * bpf_skc_to_tcp6_sock + * + * Dynamically cast a *sk* pointer to a *tcp6_sock* pointer. + * + * Returns + * *sk* if casting is valid, or NULL otherwise. + */ +static struct tcp6_sock *(*bpf_skc_to_tcp6_sock)(void *sk) = (void *) 136; + +/* + * bpf_skc_to_tcp_sock + * + * Dynamically cast a *sk* pointer to a *tcp_sock* pointer. + * + * Returns + * *sk* if casting is valid, or NULL otherwise. + */ +static struct tcp_sock *(*bpf_skc_to_tcp_sock)(void *sk) = (void *) 137; + +/* + * bpf_skc_to_tcp_timewait_sock + * + * Dynamically cast a *sk* pointer to a *tcp_timewait_sock* pointer. + * + * Returns + * *sk* if casting is valid, or NULL otherwise. + */ +static struct tcp_timewait_sock *(*bpf_skc_to_tcp_timewait_sock)(void *sk) = (void *) 138; + +/* + * bpf_skc_to_tcp_request_sock + * + * Dynamically cast a *sk* pointer to a *tcp_request_sock* pointer. + * + * Returns + * *sk* if casting is valid, or NULL otherwise. + */ +static struct tcp_request_sock *(*bpf_skc_to_tcp_request_sock)(void *sk) = (void *) 139; + +/* + * bpf_skc_to_udp6_sock + * + * Dynamically cast a *sk* pointer to a *udp6_sock* pointer. + * + * Returns + * *sk* if casting is valid, or NULL otherwise. + */ +static struct udp6_sock *(*bpf_skc_to_udp6_sock)(void *sk) = (void *) 140; + +/* + * bpf_get_task_stack + * + * Return a user or a kernel stack in bpf program provided buffer. + * To achieve this, the helper needs *task*, which is a valid + * pointer to struct task_struct. To store the stacktrace, the + * bpf program provides *buf* with a nonnegative *size*. + * + * The last argument, *flags*, holds the number of stack frames to + * skip (from 0 to 255), masked with + * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set + * the following flags: + * + * **BPF_F_USER_STACK** + * Collect a user space stack instead of a kernel stack. + * **BPF_F_USER_BUILD_ID** + * Collect buildid+offset instead of ips for user stack, + * only valid if **BPF_F_USER_STACK** is also specified. + * + * **bpf_get_task_stack**\ () can collect up to + * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject + * to sufficient large buffer size. Note that + * this limit can be controlled with the **sysctl** program, and + * that it should be manually increased in order to profile long + * user stacks (such as stacks for Java programs). To do so, use: + * + * :: + * + * # sysctl kernel.perf_event_max_stack= + * + * Returns + * A non-negative value equal to or less than *size* on success, + * or a negative error in case of failure. + */ +static long (*bpf_get_task_stack)(struct task_struct *task, void *buf, __u32 size, __u64 flags) = (void *) 141; + + diff --git a/vendor/github.com/cilium/ebpf/examples/headers/bpf_helpers.h b/vendor/github.com/cilium/ebpf/examples/headers/bpf_helpers.h new file mode 100644 index 0000000000000..bc14db706b88a --- /dev/null +++ b/vendor/github.com/cilium/ebpf/examples/headers/bpf_helpers.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ +#ifndef __BPF_HELPERS__ +#define __BPF_HELPERS__ + +/* + * Note that bpf programs need to include either + * vmlinux.h (auto-generated from BTF) or linux/types.h + * in advance since bpf_helper_defs.h uses such types + * as __u64. + */ +#include "bpf_helper_defs.h" + +#define __uint(name, val) int (*name)[val] +#define __type(name, val) typeof(val) *name +#define __array(name, val) typeof(val) *name[] + +/* Helper macro to print out debug messages */ +#define bpf_printk(fmt, ...) \ +({ \ + char ____fmt[] = fmt; \ + bpf_trace_printk(____fmt, sizeof(____fmt), \ + ##__VA_ARGS__); \ +}) + +/* + * Helper macro to place programs, maps, license in + * different sections in elf_bpf file. Section names + * are interpreted by elf_bpf loader + */ +#define SEC(NAME) __attribute__((section(NAME), used)) + +#ifndef __always_inline +#define __always_inline __attribute__((always_inline)) +#endif +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +/* + * Helper macro to manipulate data structures + */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER) +#endif +#ifndef container_of +#define container_of(ptr, type, member) \ + ({ \ + void *__mptr = (void *)(ptr); \ + ((type *)(__mptr - offsetof(type, member))); \ + }) +#endif + +/* + * Helper structure used by eBPF C program + * to describe BPF map attributes to libbpf loader + */ +struct bpf_map_def { + unsigned int type; + unsigned int key_size; + unsigned int value_size; + unsigned int max_entries; + unsigned int map_flags; +}; + +enum libbpf_pin_type { + LIBBPF_PIN_NONE, + /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */ + LIBBPF_PIN_BY_NAME, +}; + +enum libbpf_tristate { + TRI_NO = 0, + TRI_YES = 1, + TRI_MODULE = 2, +}; + +#define __kconfig __attribute__((section(".kconfig"))) +#define __ksym __attribute__((section(".ksyms"))) + +#endif diff --git a/vendor/github.com/cilium/ebpf/examples/headers/common.h b/vendor/github.com/cilium/ebpf/examples/headers/common.h new file mode 100644 index 0000000000000..1a3d58a13c98b --- /dev/null +++ b/vendor/github.com/cilium/ebpf/examples/headers/common.h @@ -0,0 +1,107 @@ +// This is a compact version of `vmlinux.h` to be used in the examples using C code. + +#ifndef __VMLINUX_H__ +#define __VMLINUX_H__ + +typedef unsigned char __u8; +typedef short int __s16; +typedef short unsigned int __u16; +typedef int __s32; +typedef unsigned int __u32; +typedef long long int __s64; +typedef long long unsigned int __u64; +typedef __u8 u8; +typedef __s16 s16; +typedef __u16 u16; +typedef __s32 s32; +typedef __u32 u32; +typedef __s64 s64; +typedef __u64 u64; +typedef __u16 __le16; +typedef __u16 __be16; +typedef __u32 __be32; +typedef __u64 __be64; +typedef __u32 __wsum; + +enum bpf_map_type { + BPF_MAP_TYPE_UNSPEC = 0, + BPF_MAP_TYPE_HASH = 1, + BPF_MAP_TYPE_ARRAY = 2, + BPF_MAP_TYPE_PROG_ARRAY = 3, + BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4, + BPF_MAP_TYPE_PERCPU_HASH = 5, + BPF_MAP_TYPE_PERCPU_ARRAY = 6, + BPF_MAP_TYPE_STACK_TRACE = 7, + BPF_MAP_TYPE_CGROUP_ARRAY = 8, + BPF_MAP_TYPE_LRU_HASH = 9, + BPF_MAP_TYPE_LRU_PERCPU_HASH = 10, + BPF_MAP_TYPE_LPM_TRIE = 11, + BPF_MAP_TYPE_ARRAY_OF_MAPS = 12, + BPF_MAP_TYPE_HASH_OF_MAPS = 13, + BPF_MAP_TYPE_DEVMAP = 14, + BPF_MAP_TYPE_SOCKMAP = 15, + BPF_MAP_TYPE_CPUMAP = 16, + BPF_MAP_TYPE_XSKMAP = 17, + BPF_MAP_TYPE_SOCKHASH = 18, + BPF_MAP_TYPE_CGROUP_STORAGE = 19, + BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 20, + BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21, + BPF_MAP_TYPE_QUEUE = 22, + BPF_MAP_TYPE_STACK = 23, + BPF_MAP_TYPE_SK_STORAGE = 24, + BPF_MAP_TYPE_DEVMAP_HASH = 25, + BPF_MAP_TYPE_STRUCT_OPS = 26, + BPF_MAP_TYPE_RINGBUF = 27, + BPF_MAP_TYPE_INODE_STORAGE = 28, +}; + +enum { + BPF_ANY = 0, + BPF_NOEXIST = 1, + BPF_EXIST = 2, + BPF_F_LOCK = 4, +}; + +/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and + * BPF_FUNC_perf_event_read_value flags. + */ +#define BPF_F_INDEX_MASK 0xffffffffULL +#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK + +#define PT_REGS_RC(x) ((x)->rax) +struct pt_regs { +/* + * C ABI says these regs are callee-preserved. They aren't saved on kernel entry + * unless syscall needs a complete, fully filled "struct pt_regs". + */ + unsigned long r15; + unsigned long r14; + unsigned long r13; + unsigned long r12; + unsigned long rbp; + unsigned long rbx; +/* These regs are callee-clobbered. Always saved on kernel entry. */ + unsigned long r11; + unsigned long r10; + unsigned long r9; + unsigned long r8; + unsigned long rax; + unsigned long rcx; + unsigned long rdx; + unsigned long rsi; + unsigned long rdi; +/* + * On syscall entry, this is syscall#. On CPU exception, this is error code. + * On hw interrupt, it's IRQ number: + */ + unsigned long orig_rax; +/* Return frame for iretq */ + unsigned long rip; + unsigned long cs; + unsigned long eflags; + unsigned long rsp; + unsigned long ss; +/* top of stack page */ +}; + +#endif /* __VMLINUX_H__ */ diff --git a/vendor/github.com/cilium/ebpf/examples/kprobe/bpf/kprobe_example.c b/vendor/github.com/cilium/ebpf/examples/kprobe/bpf/kprobe_example.c new file mode 100644 index 0000000000000..04f7a33dcc6b1 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/examples/kprobe/bpf/kprobe_example.c @@ -0,0 +1,26 @@ +#include "common.h" +#include "bpf_helpers.h" + +char __license[] SEC("license") = "Dual MIT/GPL"; + +struct bpf_map_def SEC("maps") kprobe_map = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(u32), + .value_size = sizeof(u64), + .max_entries = 1, +}; + +SEC("kprobe/sys_execve") +int kprobe_execve() { + u32 key = 0; + u64 initval = 1, *valp; + + valp = bpf_map_lookup_elem(&kprobe_map, &key); + if (!valp) { + bpf_map_update_elem(&kprobe_map, &key, &initval, BPF_ANY); + return 0; + } + __sync_fetch_and_add(valp, 1); + + return 0; +} diff --git a/vendor/github.com/cilium/ebpf/examples/uretprobe/bpf/uretprobe_example.c b/vendor/github.com/cilium/ebpf/examples/uretprobe/bpf/uretprobe_example.c new file mode 100644 index 0000000000000..94766ea4139e8 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/examples/uretprobe/bpf/uretprobe_example.c @@ -0,0 +1,25 @@ +#include "common.h" +#include "bpf_helpers.h" + +char __license[] SEC("license") = "Dual MIT/GPL"; + +struct event_t { + u32 pid; + char str[80]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); +} events SEC(".maps"); + +SEC("uretprobe/bash_readline") +int uretprobe_bash_readline(struct pt_regs *ctx) { + struct event_t event; + + event.pid = bpf_get_current_pid_tgid(); + bpf_probe_read(&event.str, sizeof(event.str), (void *)PT_REGS_RC(ctx)); + + bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); + + return 0; +} diff --git a/vendor/github.com/cilium/ebpf/go.mod b/vendor/github.com/cilium/ebpf/go.mod new file mode 100644 index 0000000000000..df8139621c3e5 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/go.mod @@ -0,0 +1,9 @@ +module github.com/cilium/ebpf + +go 1.15 + +require ( + github.com/frankban/quicktest v1.11.3 + github.com/google/go-cmp v0.5.4 + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c +) diff --git a/vendor/github.com/cilium/ebpf/info.go b/vendor/github.com/cilium/ebpf/info.go new file mode 100644 index 0000000000000..b95131ef57262 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/info.go @@ -0,0 +1,239 @@ +package ebpf + +import ( + "bufio" + "encoding/hex" + "errors" + "fmt" + "io" + "os" + "strings" + "syscall" + "time" + + "github.com/cilium/ebpf/internal" +) + +// MapInfo describes a map. +type MapInfo struct { + Type MapType + id MapID + KeySize uint32 + ValueSize uint32 + MaxEntries uint32 + Flags uint32 + // Name as supplied by user space at load time. + Name string +} + +func newMapInfoFromFd(fd *internal.FD) (*MapInfo, error) { + info, err := bpfGetMapInfoByFD(fd) + if errors.Is(err, syscall.EINVAL) { + return newMapInfoFromProc(fd) + } + if err != nil { + return nil, err + } + + return &MapInfo{ + MapType(info.map_type), + MapID(info.id), + info.key_size, + info.value_size, + info.max_entries, + info.map_flags, + // name is available from 4.15. + internal.CString(info.name[:]), + }, nil +} + +func newMapInfoFromProc(fd *internal.FD) (*MapInfo, error) { + var mi MapInfo + err := scanFdInfo(fd, map[string]interface{}{ + "map_type": &mi.Type, + "key_size": &mi.KeySize, + "value_size": &mi.ValueSize, + "max_entries": &mi.MaxEntries, + "map_flags": &mi.Flags, + }) + if err != nil { + return nil, err + } + return &mi, nil +} + +// ID returns the map ID. +// +// Available from 4.13. +// +// The bool return value indicates whether this optional field is available. +func (mi *MapInfo) ID() (MapID, bool) { + return mi.id, mi.id > 0 +} + +// programStats holds statistics of a program. +type programStats struct { + // Total accumulated runtime of the program ins ns. + runtime time.Duration + // Total number of times the program was called. + runCount uint64 +} + +// ProgramInfo describes a program. +type ProgramInfo struct { + Type ProgramType + id ProgramID + // Truncated hash of the BPF bytecode. + Tag string + // Name as supplied by user space at load time. + Name string + + stats *programStats +} + +func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) { + info, err := bpfGetProgInfoByFD(fd) + if errors.Is(err, syscall.EINVAL) { + return newProgramInfoFromProc(fd) + } + if err != nil { + return nil, err + } + + return &ProgramInfo{ + Type: ProgramType(info.prog_type), + id: ProgramID(info.id), + // tag is available if the kernel supports BPF_PROG_GET_INFO_BY_FD. + Tag: hex.EncodeToString(info.tag[:]), + // name is available from 4.15. + Name: internal.CString(info.name[:]), + stats: &programStats{ + runtime: time.Duration(info.run_time_ns), + runCount: info.run_cnt, + }, + }, nil +} + +func newProgramInfoFromProc(fd *internal.FD) (*ProgramInfo, error) { + var info ProgramInfo + err := scanFdInfo(fd, map[string]interface{}{ + "prog_type": &info.Type, + "prog_tag": &info.Tag, + }) + if errors.Is(err, errMissingFields) { + return nil, &internal.UnsupportedFeatureError{ + Name: "reading program info from /proc/self/fdinfo", + MinimumVersion: internal.Version{4, 10, 0}, + } + } + if err != nil { + return nil, err + } + + return &info, nil +} + +// ID returns the program ID. +// +// Available from 4.13. +// +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) ID() (ProgramID, bool) { + return pi.id, pi.id > 0 +} + +// RunCount returns the total number of times the program was called. +// +// Can return 0 if the collection of statistics is not enabled. See EnableStats(). +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) RunCount() (uint64, bool) { + if pi.stats != nil { + return pi.stats.runCount, true + } + return 0, false +} + +// Runtime returns the total accumulated runtime of the program. +// +// Can return 0 if the collection of statistics is not enabled. See EnableStats(). +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) Runtime() (time.Duration, bool) { + if pi.stats != nil { + return pi.stats.runtime, true + } + return time.Duration(0), false +} + +func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error { + raw, err := fd.Value() + if err != nil { + return err + } + + fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", raw)) + if err != nil { + return err + } + defer fh.Close() + + if err := scanFdInfoReader(fh, fields); err != nil { + return fmt.Errorf("%s: %w", fh.Name(), err) + } + return nil +} + +var errMissingFields = errors.New("missing fields") + +func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { + var ( + scanner = bufio.NewScanner(r) + scanned int + ) + + for scanner.Scan() { + parts := strings.SplitN(scanner.Text(), "\t", 2) + if len(parts) != 2 { + continue + } + + name := strings.TrimSuffix(parts[0], ":") + field, ok := fields[string(name)] + if !ok { + continue + } + + if n, err := fmt.Sscanln(parts[1], field); err != nil || n != 1 { + return fmt.Errorf("can't parse field %s: %v", name, err) + } + + scanned++ + } + + if err := scanner.Err(); err != nil { + return err + } + + if scanned != len(fields) { + return errMissingFields + } + + return nil +} + +// EnableStats starts the measuring of the runtime +// and run counts of eBPF programs. +// +// Collecting statistics can have an impact on the performance. +// +// Requires at least 5.8. +func EnableStats(which uint32) (io.Closer, error) { + attr := internal.BPFEnableStatsAttr{ + StatsType: which, + } + + fd, err := internal.BPFEnableStats(&attr) + if err != nil { + return nil, err + } + return fd, nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/btf.go b/vendor/github.com/cilium/ebpf/internal/btf/btf.go new file mode 100644 index 0000000000000..5da9e11921a3b --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/btf.go @@ -0,0 +1,799 @@ +package btf + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "math" + "os" + "reflect" + "sync" + "unsafe" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/unix" +) + +const btfMagic = 0xeB9F + +// Errors returned by BTF functions. +var ( + ErrNotSupported = internal.ErrNotSupported + ErrNotFound = errors.New("not found") + ErrNoExtendedInfo = errors.New("no extended info") +) + +// Spec represents decoded BTF. +type Spec struct { + rawTypes []rawType + strings stringTable + types []Type + namedTypes map[string][]namedType + funcInfos map[string]extInfo + lineInfos map[string]extInfo + coreRelos map[string]coreRelos + byteOrder binary.ByteOrder +} + +type btfHeader struct { + Magic uint16 + Version uint8 + Flags uint8 + HdrLen uint32 + + TypeOff uint32 + TypeLen uint32 + StringOff uint32 + StringLen uint32 +} + +// LoadSpecFromReader reads BTF sections from an ELF. +// +// Returns ErrNotFound if the reader contains no BTF. +func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { + file, err := internal.NewSafeELFFile(rd) + if err != nil { + return nil, err + } + defer file.Close() + + btfSection, btfExtSection, sectionSizes, err := findBtfSections(file) + if err != nil { + return nil, err + } + + if btfSection == nil { + return nil, fmt.Errorf("btf: %w", ErrNotFound) + } + + symbols, err := file.Symbols() + if err != nil { + return nil, fmt.Errorf("can't read symbols: %v", err) + } + + variableOffsets := make(map[variable]uint32) + for _, symbol := range symbols { + if idx := symbol.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE { + // Ignore things like SHN_ABS + continue + } + + if int(symbol.Section) >= len(file.Sections) { + return nil, fmt.Errorf("symbol %s: invalid section %d", symbol.Name, symbol.Section) + } + + secName := file.Sections[symbol.Section].Name + if _, ok := sectionSizes[secName]; !ok { + continue + } + + if symbol.Value > math.MaxUint32 { + return nil, fmt.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name) + } + + variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value) + } + + spec, err := loadNakedSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets) + if err != nil { + return nil, err + } + + if btfExtSection == nil { + return spec, nil + } + + spec.funcInfos, spec.lineInfos, spec.coreRelos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings) + if err != nil { + return nil, fmt.Errorf("can't read ext info: %w", err) + } + + return spec, nil +} + +func findBtfSections(file *internal.SafeELFFile) (*elf.Section, *elf.Section, map[string]uint32, error) { + var ( + btfSection *elf.Section + btfExtSection *elf.Section + sectionSizes = make(map[string]uint32) + ) + + for _, sec := range file.Sections { + switch sec.Name { + case ".BTF": + btfSection = sec + case ".BTF.ext": + btfExtSection = sec + default: + if sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS { + break + } + + if sec.Size > math.MaxUint32 { + return nil, nil, nil, fmt.Errorf("section %s exceeds maximum size", sec.Name) + } + + sectionSizes[sec.Name] = uint32(sec.Size) + } + } + return btfSection, btfExtSection, sectionSizes, nil +} + +func loadSpecFromVmlinux(rd io.ReaderAt) (*Spec, error) { + file, err := internal.NewSafeELFFile(rd) + if err != nil { + return nil, err + } + defer file.Close() + + btfSection, _, _, err := findBtfSections(file) + if err != nil { + return nil, fmt.Errorf(".BTF ELF section: %s", err) + } + if btfSection == nil { + return nil, fmt.Errorf("unable to find .BTF ELF section") + } + return loadNakedSpec(btfSection.Open(), file.ByteOrder, nil, nil) +} + +func loadNakedSpec(btf io.ReadSeeker, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) { + rawTypes, rawStrings, err := parseBTF(btf, bo) + if err != nil { + return nil, err + } + + err = fixupDatasec(rawTypes, rawStrings, sectionSizes, variableOffsets) + if err != nil { + return nil, err + } + + types, typesByName, err := inflateRawTypes(rawTypes, rawStrings) + if err != nil { + return nil, err + } + + return &Spec{ + rawTypes: rawTypes, + namedTypes: typesByName, + types: types, + strings: rawStrings, + byteOrder: bo, + }, nil +} + +var kernelBTF struct { + sync.Mutex + *Spec +} + +// LoadKernelSpec returns the current kernel's BTF information. +// +// Requires a >= 5.5 kernel with CONFIG_DEBUG_INFO_BTF enabled. Returns +// ErrNotSupported if BTF is not enabled. +func LoadKernelSpec() (*Spec, error) { + kernelBTF.Lock() + defer kernelBTF.Unlock() + + if kernelBTF.Spec != nil { + return kernelBTF.Spec, nil + } + + var err error + kernelBTF.Spec, err = loadKernelSpec() + return kernelBTF.Spec, err +} + +func loadKernelSpec() (*Spec, error) { + release, err := unix.KernelRelease() + if err != nil { + return nil, fmt.Errorf("can't read kernel release number: %w", err) + } + + fh, err := os.Open("/sys/kernel/btf/vmlinux") + if err == nil { + defer fh.Close() + + return loadNakedSpec(fh, internal.NativeEndian, nil, nil) + } + + // use same list of locations as libbpf + // https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122 + locations := []string{ + "/boot/vmlinux-%s", + "/lib/modules/%s/vmlinux-%[1]s", + "/lib/modules/%s/build/vmlinux", + "/usr/lib/modules/%s/kernel/vmlinux", + "/usr/lib/debug/boot/vmlinux-%s", + "/usr/lib/debug/boot/vmlinux-%s.debug", + "/usr/lib/debug/lib/modules/%s/vmlinux", + } + + for _, loc := range locations { + path := fmt.Sprintf(loc, release) + + fh, err := os.Open(path) + if err != nil { + continue + } + defer fh.Close() + + return loadSpecFromVmlinux(fh) + } + + return nil, fmt.Errorf("no BTF for kernel version %s: %w", release, internal.ErrNotSupported) +} + +func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) { + rawBTF, err := ioutil.ReadAll(btf) + if err != nil { + return nil, nil, fmt.Errorf("can't read BTF: %v", err) + } + + rd := bytes.NewReader(rawBTF) + + var header btfHeader + if err := binary.Read(rd, bo, &header); err != nil { + return nil, nil, fmt.Errorf("can't read header: %v", err) + } + + if header.Magic != btfMagic { + return nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic) + } + + if header.Version != 1 { + return nil, nil, fmt.Errorf("unexpected version %v", header.Version) + } + + if header.Flags != 0 { + return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags) + } + + remainder := int64(header.HdrLen) - int64(binary.Size(&header)) + if remainder < 0 { + return nil, nil, errors.New("header is too short") + } + + if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil { + return nil, nil, fmt.Errorf("header padding: %v", err) + } + + if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil { + return nil, nil, fmt.Errorf("can't seek to start of string section: %v", err) + } + + rawStrings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen))) + if err != nil { + return nil, nil, fmt.Errorf("can't read type names: %w", err) + } + + if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil { + return nil, nil, fmt.Errorf("can't seek to start of type section: %v", err) + } + + rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo) + if err != nil { + return nil, nil, fmt.Errorf("can't read types: %w", err) + } + + return rawTypes, rawStrings, nil +} + +type variable struct { + section string + name string +} + +func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) error { + for i, rawType := range rawTypes { + if rawType.Kind() != kindDatasec { + continue + } + + name, err := rawStrings.Lookup(rawType.NameOff) + if err != nil { + return err + } + + if name == ".kconfig" || name == ".ksyms" { + return fmt.Errorf("reference to %s: %w", name, ErrNotSupported) + } + + if rawTypes[i].SizeType != 0 { + continue + } + + size, ok := sectionSizes[name] + if !ok { + return fmt.Errorf("data section %s: missing size", name) + } + + rawTypes[i].SizeType = size + + secinfos := rawType.data.([]btfVarSecinfo) + for j, secInfo := range secinfos { + id := int(secInfo.Type - 1) + if id >= len(rawTypes) { + return fmt.Errorf("data section %s: invalid type id %d for variable %d", name, id, j) + } + + varName, err := rawStrings.Lookup(rawTypes[id].NameOff) + if err != nil { + return fmt.Errorf("data section %s: can't get name for type %d: %w", name, id, err) + } + + offset, ok := variableOffsets[variable{name, varName}] + if !ok { + return fmt.Errorf("data section %s: missing offset for variable %s", name, varName) + } + + secinfos[j].Offset = offset + } + } + + return nil +} + +type marshalOpts struct { + ByteOrder binary.ByteOrder + StripFuncLinkage bool +} + +func (s *Spec) marshal(opts marshalOpts) ([]byte, error) { + var ( + buf bytes.Buffer + header = new(btfHeader) + headerLen = binary.Size(header) + ) + + // Reserve space for the header. We have to write it last since + // we don't know the size of the type section yet. + _, _ = buf.Write(make([]byte, headerLen)) + + // Write type section, just after the header. + for _, raw := range s.rawTypes { + switch { + case opts.StripFuncLinkage && raw.Kind() == kindFunc: + raw.SetLinkage(StaticFunc) + } + + if err := raw.Marshal(&buf, opts.ByteOrder); err != nil { + return nil, fmt.Errorf("can't marshal BTF: %w", err) + } + } + + typeLen := uint32(buf.Len() - headerLen) + + // Write string section after type section. + _, _ = buf.Write(s.strings) + + // Fill out the header, and write it out. + header = &btfHeader{ + Magic: btfMagic, + Version: 1, + Flags: 0, + HdrLen: uint32(headerLen), + TypeOff: 0, + TypeLen: typeLen, + StringOff: typeLen, + StringLen: uint32(len(s.strings)), + } + + raw := buf.Bytes() + err := binary.Write(sliceWriter(raw[:headerLen]), opts.ByteOrder, header) + if err != nil { + return nil, fmt.Errorf("can't write header: %v", err) + } + + return raw, nil +} + +type sliceWriter []byte + +func (sw sliceWriter) Write(p []byte) (int, error) { + if len(p) != len(sw) { + return 0, errors.New("size doesn't match") + } + + return copy(sw, p), nil +} + +// Program finds the BTF for a specific section. +// +// Length is the number of bytes in the raw BPF instruction stream. +// +// Returns an error which may wrap ErrNoExtendedInfo if the Spec doesn't +// contain extended BTF info. +func (s *Spec) Program(name string, length uint64) (*Program, error) { + if length == 0 { + return nil, errors.New("length musn't be zero") + } + + if s.funcInfos == nil && s.lineInfos == nil && s.coreRelos == nil { + return nil, fmt.Errorf("BTF for section %s: %w", name, ErrNoExtendedInfo) + } + + funcInfos, funcOK := s.funcInfos[name] + lineInfos, lineOK := s.lineInfos[name] + relos, coreOK := s.coreRelos[name] + + if !funcOK && !lineOK && !coreOK { + return nil, fmt.Errorf("no extended BTF info for section %s", name) + } + + return &Program{s, length, funcInfos, lineInfos, relos}, nil +} + +// Datasec returns the BTF required to create maps which represent data sections. +func (s *Spec) Datasec(name string) (*Map, error) { + var datasec Datasec + if err := s.FindType(name, &datasec); err != nil { + return nil, fmt.Errorf("data section %s: can't get BTF: %w", name, err) + } + + m := NewMap(s, &Void{}, &datasec) + return &m, nil +} + +// FindType searches for a type with a specific name. +// +// hint determines the type of the returned Type. +// +// Returns an error wrapping ErrNotFound if no matching +// type exists in spec. +func (s *Spec) FindType(name string, typ Type) error { + var ( + wanted = reflect.TypeOf(typ) + candidate Type + ) + + for _, typ := range s.namedTypes[essentialName(name)] { + if reflect.TypeOf(typ) != wanted { + continue + } + + // Match against the full name, not just the essential one. + if typ.name() != name { + continue + } + + if candidate != nil { + return fmt.Errorf("type %s: multiple candidates for %T", name, typ) + } + + candidate = typ + } + + if candidate == nil { + return fmt.Errorf("type %s: %w", name, ErrNotFound) + } + + cpy, _ := copyType(candidate, nil) + value := reflect.Indirect(reflect.ValueOf(cpy)) + reflect.Indirect(reflect.ValueOf(typ)).Set(value) + return nil +} + +// Handle is a reference to BTF loaded into the kernel. +type Handle struct { + fd *internal.FD +} + +// NewHandle loads BTF into the kernel. +// +// Returns ErrNotSupported if BTF is not supported. +func NewHandle(spec *Spec) (*Handle, error) { + if err := haveBTF(); err != nil { + return nil, err + } + + if spec.byteOrder != internal.NativeEndian { + return nil, fmt.Errorf("can't load %s BTF on %s", spec.byteOrder, internal.NativeEndian) + } + + btf, err := spec.marshal(marshalOpts{ + ByteOrder: internal.NativeEndian, + StripFuncLinkage: haveFuncLinkage() != nil, + }) + if err != nil { + return nil, fmt.Errorf("can't marshal BTF: %w", err) + } + + if uint64(len(btf)) > math.MaxUint32 { + return nil, errors.New("BTF exceeds the maximum size") + } + + attr := &bpfLoadBTFAttr{ + btf: internal.NewSlicePointer(btf), + btfSize: uint32(len(btf)), + } + + fd, err := bpfLoadBTF(attr) + if err != nil { + logBuf := make([]byte, 64*1024) + attr.logBuf = internal.NewSlicePointer(logBuf) + attr.btfLogSize = uint32(len(logBuf)) + attr.btfLogLevel = 1 + _, logErr := bpfLoadBTF(attr) + return nil, internal.ErrorWithLog(err, logBuf, logErr) + } + + return &Handle{fd}, nil +} + +// Close destroys the handle. +// +// Subsequent calls to FD will return an invalid value. +func (h *Handle) Close() error { + return h.fd.Close() +} + +// FD returns the file descriptor for the handle. +func (h *Handle) FD() int { + value, err := h.fd.Value() + if err != nil { + return -1 + } + + return int(value) +} + +// Map is the BTF for a map. +type Map struct { + spec *Spec + key, value Type +} + +// NewMap returns a new Map containing the given values. +// The key and value arguments are initialized to Void if nil values are given. +func NewMap(spec *Spec, key Type, value Type) Map { + if key == nil { + key = &Void{} + } + if value == nil { + value = &Void{} + } + + return Map{ + spec: spec, + key: key, + value: value, + } +} + +// MapSpec should be a method on Map, but is a free function +// to hide it from users of the ebpf package. +func MapSpec(m *Map) *Spec { + return m.spec +} + +// MapKey should be a method on Map, but is a free function +// to hide it from users of the ebpf package. +func MapKey(m *Map) Type { + return m.key +} + +// MapValue should be a method on Map, but is a free function +// to hide it from users of the ebpf package. +func MapValue(m *Map) Type { + return m.value +} + +// Program is the BTF information for a stream of instructions. +type Program struct { + spec *Spec + length uint64 + funcInfos, lineInfos extInfo + coreRelos coreRelos +} + +// ProgramSpec returns the Spec needed for loading function and line infos into the kernel. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramSpec(s *Program) *Spec { + return s.spec +} + +// ProgramAppend the information from other to the Program. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramAppend(s, other *Program) error { + funcInfos, err := s.funcInfos.append(other.funcInfos, s.length) + if err != nil { + return fmt.Errorf("func infos: %w", err) + } + + lineInfos, err := s.lineInfos.append(other.lineInfos, s.length) + if err != nil { + return fmt.Errorf("line infos: %w", err) + } + + s.funcInfos = funcInfos + s.lineInfos = lineInfos + s.coreRelos = s.coreRelos.append(other.coreRelos, s.length) + s.length += other.length + return nil +} + +// ProgramFuncInfos returns the binary form of BTF function infos. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramFuncInfos(s *Program) (recordSize uint32, bytes []byte, err error) { + bytes, err = s.funcInfos.MarshalBinary() + if err != nil { + return 0, nil, err + } + + return s.funcInfos.recordSize, bytes, nil +} + +// ProgramLineInfos returns the binary form of BTF line infos. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramLineInfos(s *Program) (recordSize uint32, bytes []byte, err error) { + bytes, err = s.lineInfos.MarshalBinary() + if err != nil { + return 0, nil, err + } + + return s.lineInfos.recordSize, bytes, nil +} + +// ProgramFixups returns the changes required to adjust the program to the target. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramFixups(s *Program, target *Spec) (COREFixups, error) { + if len(s.coreRelos) == 0 { + return nil, nil + } + + if target == nil { + var err error + target, err = LoadKernelSpec() + if err != nil { + return nil, err + } + } + + return coreRelocate(s.spec, target, s.coreRelos) +} + +type bpfLoadBTFAttr struct { + btf internal.Pointer + logBuf internal.Pointer + btfSize uint32 + btfLogSize uint32 + btfLogLevel uint32 +} + +func bpfLoadBTF(attr *bpfLoadBTFAttr) (*internal.FD, error) { + fd, err := internal.BPF(internal.BPF_BTF_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err != nil { + return nil, err + } + + return internal.NewFD(uint32(fd)), nil +} + +func marshalBTF(types interface{}, strings []byte, bo binary.ByteOrder) []byte { + const minHeaderLength = 24 + + typesLen := uint32(binary.Size(types)) + header := btfHeader{ + Magic: btfMagic, + Version: 1, + HdrLen: minHeaderLength, + TypeOff: 0, + TypeLen: typesLen, + StringOff: typesLen, + StringLen: uint32(len(strings)), + } + + buf := new(bytes.Buffer) + _ = binary.Write(buf, bo, &header) + _ = binary.Write(buf, bo, types) + buf.Write(strings) + + return buf.Bytes() +} + +var haveBTF = internal.FeatureTest("BTF", "5.1", func() error { + var ( + types struct { + Integer btfType + Var btfType + btfVar struct{ Linkage uint32 } + } + strings = []byte{0, 'a', 0} + ) + + // We use a BTF_KIND_VAR here, to make sure that + // the kernel understands BTF at least as well as we + // do. BTF_KIND_VAR was introduced ~5.1. + types.Integer.SetKind(kindPointer) + types.Var.NameOff = 1 + types.Var.SetKind(kindVar) + types.Var.SizeType = 1 + + btf := marshalBTF(&types, strings, internal.NativeEndian) + + fd, err := bpfLoadBTF(&bpfLoadBTFAttr{ + btf: internal.NewSlicePointer(btf), + btfSize: uint32(len(btf)), + }) + if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { + // Treat both EINVAL and EPERM as not supported: loading the program + // might still succeed without BTF. + return internal.ErrNotSupported + } + if err != nil { + return err + } + + fd.Close() + return nil +}) + +var haveFuncLinkage = internal.FeatureTest("BTF func linkage", "5.6", func() error { + if err := haveBTF(); err != nil { + return err + } + + var ( + types struct { + FuncProto btfType + Func btfType + } + strings = []byte{0, 'a', 0} + ) + + types.FuncProto.SetKind(kindFuncProto) + types.Func.SetKind(kindFunc) + types.Func.SizeType = 1 // aka FuncProto + types.Func.NameOff = 1 + types.Func.SetLinkage(GlobalFunc) + + btf := marshalBTF(&types, strings, internal.NativeEndian) + + fd, err := bpfLoadBTF(&bpfLoadBTFAttr{ + btf: internal.NewSlicePointer(btf), + btfSize: uint32(len(btf)), + }) + if errors.Is(err, unix.EINVAL) { + return internal.ErrNotSupported + } + if err != nil { + return err + } + + fd.Close() + return nil +}) diff --git a/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go b/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go new file mode 100644 index 0000000000000..a5ef945120138 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go @@ -0,0 +1,282 @@ +package btf + +import ( + "encoding/binary" + "fmt" + "io" +) + +//go:generate stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage + +// btfKind describes a Type. +type btfKind uint8 + +// Equivalents of the BTF_KIND_* constants. +const ( + kindUnknown btfKind = iota + kindInt + kindPointer + kindArray + kindStruct + kindUnion + kindEnum + kindForward + kindTypedef + kindVolatile + kindConst + kindRestrict + // Added ~4.20 + kindFunc + kindFuncProto + // Added ~5.1 + kindVar + kindDatasec +) + +// FuncLinkage describes BTF function linkage metadata. +type FuncLinkage int + +// Equivalent of enum btf_func_linkage. +const ( + StaticFunc FuncLinkage = iota // static + GlobalFunc // global + ExternFunc // extern +) + +// VarLinkage describes BTF variable linkage metadata. +type VarLinkage int + +const ( + StaticVar VarLinkage = iota // static + GlobalVar // global + ExternVar // extern +) + +const ( + btfTypeKindShift = 24 + btfTypeKindLen = 4 + btfTypeVlenShift = 0 + btfTypeVlenMask = 16 + btfTypeKindFlagShift = 31 + btfTypeKindFlagMask = 1 +) + +// btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst. +type btfType struct { + NameOff uint32 + /* "info" bits arrangement + * bits 0-15: vlen (e.g. # of struct's members), linkage + * bits 16-23: unused + * bits 24-27: kind (e.g. int, ptr, array...etc) + * bits 28-30: unused + * bit 31: kind_flag, currently used by + * struct, union and fwd + */ + Info uint32 + /* "size" is used by INT, ENUM, STRUCT and UNION. + * "size" tells the size of the type it is describing. + * + * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, + * FUNC and FUNC_PROTO. + * "type" is a type_id referring to another type. + */ + SizeType uint32 +} + +func (k btfKind) String() string { + switch k { + case kindUnknown: + return "Unknown" + case kindInt: + return "Integer" + case kindPointer: + return "Pointer" + case kindArray: + return "Array" + case kindStruct: + return "Struct" + case kindUnion: + return "Union" + case kindEnum: + return "Enumeration" + case kindForward: + return "Forward" + case kindTypedef: + return "Typedef" + case kindVolatile: + return "Volatile" + case kindConst: + return "Const" + case kindRestrict: + return "Restrict" + case kindFunc: + return "Function" + case kindFuncProto: + return "Function Proto" + case kindVar: + return "Variable" + case kindDatasec: + return "Section" + default: + return fmt.Sprintf("Unknown (%d)", k) + } +} + +func mask(len uint32) uint32 { + return (1 << len) - 1 +} + +func (bt *btfType) info(len, shift uint32) uint32 { + return (bt.Info >> shift) & mask(len) +} + +func (bt *btfType) setInfo(value, len, shift uint32) { + bt.Info &^= mask(len) << shift + bt.Info |= (value & mask(len)) << shift +} + +func (bt *btfType) Kind() btfKind { + return btfKind(bt.info(btfTypeKindLen, btfTypeKindShift)) +} + +func (bt *btfType) SetKind(kind btfKind) { + bt.setInfo(uint32(kind), btfTypeKindLen, btfTypeKindShift) +} + +func (bt *btfType) Vlen() int { + return int(bt.info(btfTypeVlenMask, btfTypeVlenShift)) +} + +func (bt *btfType) SetVlen(vlen int) { + bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift) +} + +func (bt *btfType) KindFlag() bool { + return bt.info(btfTypeKindFlagMask, btfTypeKindFlagShift) == 1 +} + +func (bt *btfType) Linkage() FuncLinkage { + return FuncLinkage(bt.info(btfTypeVlenMask, btfTypeVlenShift)) +} + +func (bt *btfType) SetLinkage(linkage FuncLinkage) { + bt.setInfo(uint32(linkage), btfTypeVlenMask, btfTypeVlenShift) +} + +func (bt *btfType) Type() TypeID { + // TODO: Panic here if wrong kind? + return TypeID(bt.SizeType) +} + +func (bt *btfType) Size() uint32 { + // TODO: Panic here if wrong kind? + return bt.SizeType +} + +type rawType struct { + btfType + data interface{} +} + +func (rt *rawType) Marshal(w io.Writer, bo binary.ByteOrder) error { + if err := binary.Write(w, bo, &rt.btfType); err != nil { + return err + } + + if rt.data == nil { + return nil + } + + return binary.Write(w, bo, rt.data) +} + +type btfArray struct { + Type TypeID + IndexType TypeID + Nelems uint32 +} + +type btfMember struct { + NameOff uint32 + Type TypeID + Offset uint32 +} + +type btfVarSecinfo struct { + Type TypeID + Offset uint32 + Size uint32 +} + +type btfVariable struct { + Linkage uint32 +} + +type btfEnum struct { + NameOff uint32 + Val int32 +} + +type btfParam struct { + NameOff uint32 + Type TypeID +} + +func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { + var ( + header btfType + types []rawType + ) + + for id := TypeID(1); ; id++ { + if err := binary.Read(r, bo, &header); err == io.EOF { + return types, nil + } else if err != nil { + return nil, fmt.Errorf("can't read type info for id %v: %v", id, err) + } + + var data interface{} + switch header.Kind() { + case kindInt: + data = new(uint32) + case kindPointer: + case kindArray: + data = new(btfArray) + case kindStruct: + fallthrough + case kindUnion: + data = make([]btfMember, header.Vlen()) + case kindEnum: + data = make([]btfEnum, header.Vlen()) + case kindForward: + case kindTypedef: + case kindVolatile: + case kindConst: + case kindRestrict: + case kindFunc: + case kindFuncProto: + data = make([]btfParam, header.Vlen()) + case kindVar: + data = new(btfVariable) + case kindDatasec: + data = make([]btfVarSecinfo, header.Vlen()) + default: + return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind()) + } + + if data == nil { + types = append(types, rawType{header, nil}) + continue + } + + if err := binary.Read(r, bo, data); err != nil { + return nil, fmt.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err) + } + + types = append(types, rawType{header, data}) + } +} + +func intEncoding(raw uint32) (IntEncoding, uint32, byte) { + return IntEncoding((raw & 0x0f000000) >> 24), (raw & 0x00ff0000) >> 16, byte(raw & 0x000000ff) +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/btf_types_string.go b/vendor/github.com/cilium/ebpf/internal/btf/btf_types_string.go new file mode 100644 index 0000000000000..0e0c17d68ba92 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/btf_types_string.go @@ -0,0 +1,44 @@ +// Code generated by "stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage"; DO NOT EDIT. + +package btf + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[StaticFunc-0] + _ = x[GlobalFunc-1] + _ = x[ExternFunc-2] +} + +const _FuncLinkage_name = "staticglobalextern" + +var _FuncLinkage_index = [...]uint8{0, 6, 12, 18} + +func (i FuncLinkage) String() string { + if i < 0 || i >= FuncLinkage(len(_FuncLinkage_index)-1) { + return "FuncLinkage(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _FuncLinkage_name[_FuncLinkage_index[i]:_FuncLinkage_index[i+1]] +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[StaticVar-0] + _ = x[GlobalVar-1] + _ = x[ExternVar-2] +} + +const _VarLinkage_name = "staticglobalextern" + +var _VarLinkage_index = [...]uint8{0, 6, 12, 18} + +func (i VarLinkage) String() string { + if i < 0 || i >= VarLinkage(len(_VarLinkage_index)-1) { + return "VarLinkage(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _VarLinkage_name[_VarLinkage_index[i]:_VarLinkage_index[i+1]] +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/core.go b/vendor/github.com/cilium/ebpf/internal/btf/core.go new file mode 100644 index 0000000000000..7c888f602d0df --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/core.go @@ -0,0 +1,887 @@ +package btf + +import ( + "errors" + "fmt" + "math" + "reflect" + "sort" + "strconv" + "strings" + + "github.com/cilium/ebpf/asm" +) + +// Code in this file is derived from libbpf, which is available under a BSD +// 2-Clause license. + +// COREFixup is the result of computing a CO-RE relocation for a target. +type COREFixup struct { + Kind COREKind + Local uint32 + Target uint32 + Poison bool +} + +func (f COREFixup) equal(other COREFixup) bool { + return f.Local == other.Local && f.Target == other.Target +} + +func (f COREFixup) String() string { + if f.Poison { + return fmt.Sprintf("%s=poison", f.Kind) + } + return fmt.Sprintf("%s=%d->%d", f.Kind, f.Local, f.Target) +} + +func (f COREFixup) apply(ins *asm.Instruction) error { + if f.Poison { + return errors.New("can't poison individual instruction") + } + + switch class := ins.OpCode.Class(); class { + case asm.LdXClass, asm.StClass, asm.StXClass: + if want := int16(f.Local); want != ins.Offset { + return fmt.Errorf("invalid offset %d, expected %d", ins.Offset, want) + } + + if f.Target > math.MaxInt16 { + return fmt.Errorf("offset %d exceeds MaxInt16", f.Target) + } + + ins.Offset = int16(f.Target) + + case asm.LdClass: + if !ins.IsConstantLoad(asm.DWord) { + return fmt.Errorf("not a dword-sized immediate load") + } + + if want := int64(f.Local); want != ins.Constant { + return fmt.Errorf("invalid immediate %d, expected %d", ins.Constant, want) + } + + ins.Constant = int64(f.Target) + + case asm.ALUClass: + if ins.OpCode.ALUOp() == asm.Swap { + return fmt.Errorf("relocation against swap") + } + + fallthrough + + case asm.ALU64Class: + if src := ins.OpCode.Source(); src != asm.ImmSource { + return fmt.Errorf("invalid source %s", src) + } + + if want := int64(f.Local); want != ins.Constant { + return fmt.Errorf("invalid immediate %d, expected %d", ins.Constant, want) + } + + if f.Target > math.MaxInt32 { + return fmt.Errorf("immediate %d exceeds MaxInt32", f.Target) + } + + ins.Constant = int64(f.Target) + + default: + return fmt.Errorf("invalid class %s", class) + } + + return nil +} + +func (f COREFixup) isNonExistant() bool { + return f.Kind.checksForExistence() && f.Target == 0 +} + +type COREFixups map[uint64]COREFixup + +// Apply a set of CO-RE relocations to a BPF program. +func (fs COREFixups) Apply(insns asm.Instructions) (asm.Instructions, error) { + if len(fs) == 0 { + cpy := make(asm.Instructions, len(insns)) + copy(cpy, insns) + return insns, nil + } + + cpy := make(asm.Instructions, 0, len(insns)) + iter := insns.Iterate() + for iter.Next() { + fixup, ok := fs[iter.Offset.Bytes()] + if !ok { + cpy = append(cpy, *iter.Ins) + continue + } + + ins := *iter.Ins + if fixup.Poison { + const badRelo = asm.BuiltinFunc(0xbad2310) + + cpy = append(cpy, badRelo.Call()) + if ins.OpCode.IsDWordLoad() { + // 64 bit constant loads occupy two raw bpf instructions, so + // we need to add another instruction as padding. + cpy = append(cpy, badRelo.Call()) + } + + continue + } + + if err := fixup.apply(&ins); err != nil { + return nil, fmt.Errorf("instruction %d, offset %d: %s: %w", iter.Index, iter.Offset.Bytes(), fixup.Kind, err) + } + + cpy = append(cpy, ins) + } + + return cpy, nil +} + +// COREKind is the type of CO-RE relocation +type COREKind uint32 + +const ( + reloFieldByteOffset COREKind = iota /* field byte offset */ + reloFieldByteSize /* field size in bytes */ + reloFieldExists /* field existence in target kernel */ + reloFieldSigned /* field signedness (0 - unsigned, 1 - signed) */ + reloFieldLShiftU64 /* bitfield-specific left bitshift */ + reloFieldRShiftU64 /* bitfield-specific right bitshift */ + reloTypeIDLocal /* type ID in local BPF object */ + reloTypeIDTarget /* type ID in target kernel */ + reloTypeExists /* type existence in target kernel */ + reloTypeSize /* type size in bytes */ + reloEnumvalExists /* enum value existence in target kernel */ + reloEnumvalValue /* enum value integer value */ +) + +func (k COREKind) String() string { + switch k { + case reloFieldByteOffset: + return "byte_off" + case reloFieldByteSize: + return "byte_sz" + case reloFieldExists: + return "field_exists" + case reloFieldSigned: + return "signed" + case reloFieldLShiftU64: + return "lshift_u64" + case reloFieldRShiftU64: + return "rshift_u64" + case reloTypeIDLocal: + return "local_type_id" + case reloTypeIDTarget: + return "target_type_id" + case reloTypeExists: + return "type_exists" + case reloTypeSize: + return "type_size" + case reloEnumvalExists: + return "enumval_exists" + case reloEnumvalValue: + return "enumval_value" + default: + return "unknown" + } +} + +func (k COREKind) checksForExistence() bool { + return k == reloEnumvalExists || k == reloTypeExists || k == reloFieldExists +} + +func coreRelocate(local, target *Spec, relos coreRelos) (COREFixups, error) { + if local.byteOrder != target.byteOrder { + return nil, fmt.Errorf("can't relocate %s against %s", local.byteOrder, target.byteOrder) + } + + var ids []TypeID + relosByID := make(map[TypeID]coreRelos) + result := make(COREFixups, len(relos)) + for _, relo := range relos { + if relo.kind == reloTypeIDLocal { + // Filtering out reloTypeIDLocal here makes our lives a lot easier + // down the line, since it doesn't have a target at all. + if len(relo.accessor) > 1 || relo.accessor[0] != 0 { + return nil, fmt.Errorf("%s: unexpected accessor %v", relo.kind, relo.accessor) + } + + result[uint64(relo.insnOff)] = COREFixup{ + relo.kind, + uint32(relo.typeID), + uint32(relo.typeID), + false, + } + continue + } + + relos, ok := relosByID[relo.typeID] + if !ok { + ids = append(ids, relo.typeID) + } + relosByID[relo.typeID] = append(relos, relo) + } + + // Ensure we work on relocations in a deterministic order. + sort.Slice(ids, func(i, j int) bool { + return ids[i] < ids[j] + }) + + for _, id := range ids { + if int(id) >= len(local.types) { + return nil, fmt.Errorf("invalid type id %d", id) + } + + localType := local.types[id] + named, ok := localType.(namedType) + if !ok || named.name() == "" { + return nil, fmt.Errorf("relocate unnamed or anonymous type %s: %w", localType, ErrNotSupported) + } + + relos := relosByID[id] + targets := target.namedTypes[named.essentialName()] + fixups, err := coreCalculateFixups(localType, targets, relos) + if err != nil { + return nil, fmt.Errorf("relocate %s: %w", localType, err) + } + + for i, relo := range relos { + result[uint64(relo.insnOff)] = fixups[i] + } + } + + return result, nil +} + +var errAmbiguousRelocation = errors.New("ambiguous relocation") +var errImpossibleRelocation = errors.New("impossible relocation") + +// coreCalculateFixups calculates the fixups for the given relocations using +// the "best" target. +// +// The best target is determined by scoring: the less poisoning we have to do +// the better the target is. +func coreCalculateFixups(local Type, targets []namedType, relos coreRelos) ([]COREFixup, error) { + localID := local.ID() + local, err := copyType(local, skipQualifierAndTypedef) + if err != nil { + return nil, err + } + + bestScore := len(relos) + var bestFixups []COREFixup + for i := range targets { + targetID := targets[i].ID() + target, err := copyType(targets[i], skipQualifierAndTypedef) + if err != nil { + return nil, err + } + + score := 0 // lower is better + fixups := make([]COREFixup, 0, len(relos)) + for _, relo := range relos { + fixup, err := coreCalculateFixup(local, localID, target, targetID, relo) + if err != nil { + return nil, fmt.Errorf("target %s: %w", target, err) + } + if fixup.Poison || fixup.isNonExistant() { + score++ + } + fixups = append(fixups, fixup) + } + + if score > bestScore { + // We have a better target already, ignore this one. + continue + } + + if score < bestScore { + // This is the best target yet, use it. + bestScore = score + bestFixups = fixups + continue + } + + // Some other target has the same score as the current one. Make sure + // the fixups agree with each other. + for i, fixup := range bestFixups { + if !fixup.equal(fixups[i]) { + return nil, fmt.Errorf("%s: multiple types match: %w", fixup.Kind, errAmbiguousRelocation) + } + } + } + + if bestFixups == nil { + // Nothing at all matched, probably because there are no suitable + // targets at all. Poison everything! + bestFixups = make([]COREFixup, len(relos)) + for i, relo := range relos { + bestFixups[i] = COREFixup{Kind: relo.kind, Poison: true} + } + } + + return bestFixups, nil +} + +// coreCalculateFixup calculates the fixup for a single local type, target type +// and relocation. +func coreCalculateFixup(local Type, localID TypeID, target Type, targetID TypeID, relo coreRelo) (COREFixup, error) { + fixup := func(local, target uint32) (COREFixup, error) { + return COREFixup{relo.kind, local, target, false}, nil + } + poison := func() (COREFixup, error) { + if relo.kind.checksForExistence() { + return fixup(1, 0) + } + return COREFixup{relo.kind, 0, 0, true}, nil + } + zero := COREFixup{} + + switch relo.kind { + case reloTypeIDTarget, reloTypeSize, reloTypeExists: + if len(relo.accessor) > 1 || relo.accessor[0] != 0 { + return zero, fmt.Errorf("%s: unexpected accessor %v", relo.kind, relo.accessor) + } + + err := coreAreTypesCompatible(local, target) + if errors.Is(err, errImpossibleRelocation) { + return poison() + } + if err != nil { + return zero, fmt.Errorf("relocation %s: %w", relo.kind, err) + } + + switch relo.kind { + case reloTypeExists: + return fixup(1, 1) + + case reloTypeIDTarget: + return fixup(uint32(localID), uint32(targetID)) + + case reloTypeSize: + localSize, err := Sizeof(local) + if err != nil { + return zero, err + } + + targetSize, err := Sizeof(target) + if err != nil { + return zero, err + } + + return fixup(uint32(localSize), uint32(targetSize)) + } + + case reloEnumvalValue, reloEnumvalExists: + localValue, targetValue, err := coreFindEnumValue(local, relo.accessor, target) + if errors.Is(err, errImpossibleRelocation) { + return poison() + } + if err != nil { + return zero, fmt.Errorf("relocation %s: %w", relo.kind, err) + } + + switch relo.kind { + case reloEnumvalExists: + return fixup(1, 1) + + case reloEnumvalValue: + return fixup(uint32(localValue.Value), uint32(targetValue.Value)) + } + + case reloFieldByteOffset, reloFieldByteSize, reloFieldExists: + if _, ok := target.(*Fwd); ok { + // We can't relocate fields using a forward declaration, so + // skip it. If a non-forward declaration is present in the BTF + // we'll find it in one of the other iterations. + return poison() + } + + localField, targetField, err := coreFindField(local, relo.accessor, target) + if errors.Is(err, errImpossibleRelocation) { + return poison() + } + if err != nil { + return zero, fmt.Errorf("target %s: %w", target, err) + } + + switch relo.kind { + case reloFieldExists: + return fixup(1, 1) + + case reloFieldByteOffset: + return fixup(localField.offset/8, targetField.offset/8) + + case reloFieldByteSize: + localSize, err := Sizeof(localField.Type) + if err != nil { + return zero, err + } + + targetSize, err := Sizeof(targetField.Type) + if err != nil { + return zero, err + } + + return fixup(uint32(localSize), uint32(targetSize)) + + } + } + + return zero, fmt.Errorf("relocation %s: %w", relo.kind, ErrNotSupported) +} + +/* coreAccessor contains a path through a struct. It contains at least one index. + * + * The interpretation depends on the kind of the relocation. The following is + * taken from struct bpf_core_relo in libbpf_internal.h: + * + * - for field-based relocations, string encodes an accessed field using + * a sequence of field and array indices, separated by colon (:). It's + * conceptually very close to LLVM's getelementptr ([0]) instruction's + * arguments for identifying offset to a field. + * - for type-based relocations, strings is expected to be just "0"; + * - for enum value-based relocations, string contains an index of enum + * value within its enum type; + * + * Example to provide a better feel. + * + * struct sample { + * int a; + * struct { + * int b[10]; + * }; + * }; + * + * struct sample s = ...; + * int x = &s->a; // encoded as "0:0" (a is field #0) + * int y = &s->b[5]; // encoded as "0:1:0:5" (anon struct is field #1, + * // b is field #0 inside anon struct, accessing elem #5) + * int z = &s[10]->b; // encoded as "10:1" (ptr is used as an array) + */ +type coreAccessor []int + +func parseCoreAccessor(accessor string) (coreAccessor, error) { + if accessor == "" { + return nil, fmt.Errorf("empty accessor") + } + + var result coreAccessor + parts := strings.Split(accessor, ":") + for _, part := range parts { + // 31 bits to avoid overflowing int on 32 bit platforms. + index, err := strconv.ParseUint(part, 10, 31) + if err != nil { + return nil, fmt.Errorf("accessor index %q: %s", part, err) + } + + result = append(result, int(index)) + } + + return result, nil +} + +func (ca coreAccessor) String() string { + strs := make([]string, 0, len(ca)) + for _, i := range ca { + strs = append(strs, strconv.Itoa(i)) + } + return strings.Join(strs, ":") +} + +func (ca coreAccessor) enumValue(t Type) (*EnumValue, error) { + e, ok := t.(*Enum) + if !ok { + return nil, fmt.Errorf("not an enum: %s", t) + } + + if len(ca) > 1 { + return nil, fmt.Errorf("invalid accessor %s for enum", ca) + } + + i := ca[0] + if i >= len(e.Values) { + return nil, fmt.Errorf("invalid index %d for %s", i, e) + } + + return &e.Values[i], nil +} + +type coreField struct { + Type Type + offset uint32 +} + +func adjustOffset(base uint32, t Type, n int) (uint32, error) { + size, err := Sizeof(t) + if err != nil { + return 0, err + } + + return base + (uint32(n) * uint32(size) * 8), nil +} + +// coreFindField descends into the local type using the accessor and tries to +// find an equivalent field in target at each step. +// +// Returns the field and the offset of the field from the start of +// target in bits. +func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreField, _ error) { + // The first index is used to offset a pointer of the base type like + // when accessing an array. + localOffset, err := adjustOffset(0, local, localAcc[0]) + if err != nil { + return coreField{}, coreField{}, err + } + + targetOffset, err := adjustOffset(0, target, localAcc[0]) + if err != nil { + return coreField{}, coreField{}, err + } + + if err := coreAreMembersCompatible(local, target); err != nil { + return coreField{}, coreField{}, fmt.Errorf("fields: %w", err) + } + + var localMaybeFlex, targetMaybeFlex bool + for _, acc := range localAcc[1:] { + switch localType := local.(type) { + case composite: + // For composite types acc is used to find the field in the local type, + // and then we try to find a field in target with the same name. + localMembers := localType.members() + if acc >= len(localMembers) { + return coreField{}, coreField{}, fmt.Errorf("invalid accessor %d for %s", acc, local) + } + + localMember := localMembers[acc] + if localMember.Name == "" { + _, ok := localMember.Type.(composite) + if !ok { + return coreField{}, coreField{}, fmt.Errorf("unnamed field with type %s: %s", localMember.Type, ErrNotSupported) + } + + // This is an anonymous struct or union, ignore it. + local = localMember.Type + localOffset += localMember.Offset + localMaybeFlex = false + continue + } + + targetType, ok := target.(composite) + if !ok { + return coreField{}, coreField{}, fmt.Errorf("target not composite: %w", errImpossibleRelocation) + } + + targetMember, last, err := coreFindMember(targetType, localMember.Name) + if err != nil { + return coreField{}, coreField{}, err + } + + if targetMember.BitfieldSize > 0 { + return coreField{}, coreField{}, fmt.Errorf("field %q is a bitfield: %w", targetMember.Name, ErrNotSupported) + } + + local = localMember.Type + localMaybeFlex = acc == len(localMembers)-1 + localOffset += localMember.Offset + target = targetMember.Type + targetMaybeFlex = last + targetOffset += targetMember.Offset + + case *Array: + // For arrays, acc is the index in the target. + targetType, ok := target.(*Array) + if !ok { + return coreField{}, coreField{}, fmt.Errorf("target not array: %w", errImpossibleRelocation) + } + + if localType.Nelems == 0 && !localMaybeFlex { + return coreField{}, coreField{}, fmt.Errorf("local type has invalid flexible array") + } + if targetType.Nelems == 0 && !targetMaybeFlex { + return coreField{}, coreField{}, fmt.Errorf("target type has invalid flexible array") + } + + if localType.Nelems > 0 && acc >= int(localType.Nelems) { + return coreField{}, coreField{}, fmt.Errorf("invalid access of %s at index %d", localType, acc) + } + if targetType.Nelems > 0 && acc >= int(targetType.Nelems) { + return coreField{}, coreField{}, fmt.Errorf("out of bounds access of target: %w", errImpossibleRelocation) + } + + local = localType.Type + localMaybeFlex = false + localOffset, err = adjustOffset(localOffset, local, acc) + if err != nil { + return coreField{}, coreField{}, err + } + + target = targetType.Type + targetMaybeFlex = false + targetOffset, err = adjustOffset(targetOffset, target, acc) + if err != nil { + return coreField{}, coreField{}, err + } + + default: + return coreField{}, coreField{}, fmt.Errorf("relocate field of %T: %w", localType, ErrNotSupported) + } + + if err := coreAreMembersCompatible(local, target); err != nil { + return coreField{}, coreField{}, err + } + } + + return coreField{local, localOffset}, coreField{target, targetOffset}, nil +} + +// coreFindMember finds a member in a composite type while handling anonymous +// structs and unions. +func coreFindMember(typ composite, name Name) (Member, bool, error) { + if name == "" { + return Member{}, false, errors.New("can't search for anonymous member") + } + + type offsetTarget struct { + composite + offset uint32 + } + + targets := []offsetTarget{{typ, 0}} + visited := make(map[composite]bool) + + for i := 0; i < len(targets); i++ { + target := targets[i] + + // Only visit targets once to prevent infinite recursion. + if visited[target] { + continue + } + if len(visited) >= maxTypeDepth { + // This check is different than libbpf, which restricts the entire + // path to BPF_CORE_SPEC_MAX_LEN items. + return Member{}, false, fmt.Errorf("type is nested too deep") + } + visited[target] = true + + members := target.members() + for j, member := range members { + if member.Name == name { + // NB: This is safe because member is a copy. + member.Offset += target.offset + return member, j == len(members)-1, nil + } + + // The names don't match, but this member could be an anonymous struct + // or union. + if member.Name != "" { + continue + } + + comp, ok := member.Type.(composite) + if !ok { + return Member{}, false, fmt.Errorf("anonymous non-composite type %T not allowed", member.Type) + } + + targets = append(targets, offsetTarget{comp, target.offset + member.Offset}) + } + } + + return Member{}, false, fmt.Errorf("no matching member: %w", errImpossibleRelocation) +} + +// coreFindEnumValue follows localAcc to find the equivalent enum value in target. +func coreFindEnumValue(local Type, localAcc coreAccessor, target Type) (localValue, targetValue *EnumValue, _ error) { + localValue, err := localAcc.enumValue(local) + if err != nil { + return nil, nil, err + } + + targetEnum, ok := target.(*Enum) + if !ok { + return nil, nil, errImpossibleRelocation + } + + localName := localValue.Name.essentialName() + for i, targetValue := range targetEnum.Values { + if targetValue.Name.essentialName() != localName { + continue + } + + return localValue, &targetEnum.Values[i], nil + } + + return nil, nil, errImpossibleRelocation +} + +/* The comment below is from bpf_core_types_are_compat in libbpf.c: + * + * Check local and target types for compatibility. This check is used for + * type-based CO-RE relocations and follow slightly different rules than + * field-based relocations. This function assumes that root types were already + * checked for name match. Beyond that initial root-level name check, names + * are completely ignored. Compatibility rules are as follows: + * - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs are considered compatible, but + * kind should match for local and target types (i.e., STRUCT is not + * compatible with UNION); + * - for ENUMs, the size is ignored; + * - for INT, size and signedness are ignored; + * - for ARRAY, dimensionality is ignored, element types are checked for + * compatibility recursively; + * - CONST/VOLATILE/RESTRICT modifiers are ignored; + * - TYPEDEFs/PTRs are compatible if types they pointing to are compatible; + * - FUNC_PROTOs are compatible if they have compatible signature: same + * number of input args and compatible return and argument types. + * These rules are not set in stone and probably will be adjusted as we get + * more experience with using BPF CO-RE relocations. + * + * Returns errImpossibleRelocation if types are not compatible. + */ +func coreAreTypesCompatible(localType Type, targetType Type) error { + var ( + localTs, targetTs typeDeque + l, t = &localType, &targetType + depth = 0 + ) + + for ; l != nil && t != nil; l, t = localTs.shift(), targetTs.shift() { + if depth >= maxTypeDepth { + return errors.New("types are nested too deep") + } + + localType = *l + targetType = *t + + if reflect.TypeOf(localType) != reflect.TypeOf(targetType) { + return fmt.Errorf("type mismatch: %w", errImpossibleRelocation) + } + + switch lv := (localType).(type) { + case *Void, *Struct, *Union, *Enum, *Fwd: + // Nothing to do here + + case *Int: + tv := targetType.(*Int) + if lv.isBitfield() || tv.isBitfield() { + return fmt.Errorf("bitfield: %w", errImpossibleRelocation) + } + + case *Pointer, *Array: + depth++ + localType.walk(&localTs) + targetType.walk(&targetTs) + + case *FuncProto: + tv := targetType.(*FuncProto) + if len(lv.Params) != len(tv.Params) { + return fmt.Errorf("function param mismatch: %w", errImpossibleRelocation) + } + + depth++ + localType.walk(&localTs) + targetType.walk(&targetTs) + + default: + return fmt.Errorf("unsupported type %T", localType) + } + } + + if l != nil { + return fmt.Errorf("dangling local type %T", *l) + } + + if t != nil { + return fmt.Errorf("dangling target type %T", *t) + } + + return nil +} + +/* coreAreMembersCompatible checks two types for field-based relocation compatibility. + * + * The comment below is from bpf_core_fields_are_compat in libbpf.c: + * + * Check two types for compatibility for the purpose of field access + * relocation. const/volatile/restrict and typedefs are skipped to ensure we + * are relocating semantically compatible entities: + * - any two STRUCTs/UNIONs are compatible and can be mixed; + * - any two FWDs are compatible, if their names match (modulo flavor suffix); + * - any two PTRs are always compatible; + * - for ENUMs, names should be the same (ignoring flavor suffix) or at + * least one of enums should be anonymous; + * - for ENUMs, check sizes, names are ignored; + * - for INT, size and signedness are ignored; + * - for ARRAY, dimensionality is ignored, element types are checked for + * compatibility recursively; + * [ NB: coreAreMembersCompatible doesn't recurse, this check is done + * by coreFindField. ] + * - everything else shouldn't be ever a target of relocation. + * These rules are not set in stone and probably will be adjusted as we get + * more experience with using BPF CO-RE relocations. + * + * Returns errImpossibleRelocation if the members are not compatible. + */ +func coreAreMembersCompatible(localType Type, targetType Type) error { + doNamesMatch := func(a, b string) error { + if a == "" || b == "" { + // allow anonymous and named type to match + return nil + } + + if essentialName(a) == essentialName(b) { + return nil + } + + return fmt.Errorf("names don't match: %w", errImpossibleRelocation) + } + + _, lok := localType.(composite) + _, tok := targetType.(composite) + if lok && tok { + return nil + } + + if reflect.TypeOf(localType) != reflect.TypeOf(targetType) { + return fmt.Errorf("type mismatch: %w", errImpossibleRelocation) + } + + switch lv := localType.(type) { + case *Array, *Pointer: + return nil + + case *Enum: + tv := targetType.(*Enum) + return doNamesMatch(lv.name(), tv.name()) + + case *Fwd: + tv := targetType.(*Fwd) + return doNamesMatch(lv.name(), tv.name()) + + case *Int: + tv := targetType.(*Int) + if lv.isBitfield() || tv.isBitfield() { + return fmt.Errorf("bitfield: %w", errImpossibleRelocation) + } + return nil + + default: + return fmt.Errorf("type %s: %w", localType, ErrNotSupported) + } +} + +func skipQualifierAndTypedef(typ Type) (Type, error) { + result := typ + for depth := 0; depth <= maxTypeDepth; depth++ { + switch v := (result).(type) { + case qualifier: + result = v.qualify() + case *Typedef: + result = v.Type + default: + return result, nil + } + } + return nil, errors.New("exceeded type depth") +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/doc.go b/vendor/github.com/cilium/ebpf/internal/btf/doc.go new file mode 100644 index 0000000000000..ad2576cb23c48 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/doc.go @@ -0,0 +1,8 @@ +// Package btf handles data encoded according to the BPF Type Format. +// +// The canonical documentation lives in the Linux kernel repository and is +// available at https://www.kernel.org/doc/html/latest/bpf/btf.html +// +// The API is very much unstable. You should only use this via the main +// ebpf library. +package btf diff --git a/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go b/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go new file mode 100644 index 0000000000000..beba1bce6980b --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go @@ -0,0 +1,303 @@ +package btf + +import ( + "bufio" + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" +) + +type btfExtHeader struct { + Magic uint16 + Version uint8 + Flags uint8 + HdrLen uint32 + + FuncInfoOff uint32 + FuncInfoLen uint32 + LineInfoOff uint32 + LineInfoLen uint32 +} + +type btfExtCoreHeader struct { + CoreReloOff uint32 + CoreReloLen uint32 +} + +func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, relos map[string]coreRelos, err error) { + var header btfExtHeader + var coreHeader btfExtCoreHeader + if err := binary.Read(r, bo, &header); err != nil { + return nil, nil, nil, fmt.Errorf("can't read header: %v", err) + } + + if header.Magic != btfMagic { + return nil, nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic) + } + + if header.Version != 1 { + return nil, nil, nil, fmt.Errorf("unexpected version %v", header.Version) + } + + if header.Flags != 0 { + return nil, nil, nil, fmt.Errorf("unsupported flags %v", header.Flags) + } + + remainder := int64(header.HdrLen) - int64(binary.Size(&header)) + if remainder < 0 { + return nil, nil, nil, errors.New("header is too short") + } + + coreHdrSize := int64(binary.Size(&coreHeader)) + if remainder >= coreHdrSize { + if err := binary.Read(r, bo, &coreHeader); err != nil { + return nil, nil, nil, fmt.Errorf("can't read CO-RE relocation header: %v", err) + } + remainder -= coreHdrSize + } + + // Of course, the .BTF.ext header has different semantics than the + // .BTF ext header. We need to ignore non-null values. + _, err = io.CopyN(ioutil.Discard, r, remainder) + if err != nil { + return nil, nil, nil, fmt.Errorf("header padding: %v", err) + } + + if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil { + return nil, nil, nil, fmt.Errorf("can't seek to function info section: %v", err) + } + + buf := bufio.NewReader(io.LimitReader(r, int64(header.FuncInfoLen))) + funcInfo, err = parseExtInfo(buf, bo, strings) + if err != nil { + return nil, nil, nil, fmt.Errorf("function info: %w", err) + } + + if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil { + return nil, nil, nil, fmt.Errorf("can't seek to line info section: %v", err) + } + + buf = bufio.NewReader(io.LimitReader(r, int64(header.LineInfoLen))) + lineInfo, err = parseExtInfo(buf, bo, strings) + if err != nil { + return nil, nil, nil, fmt.Errorf("line info: %w", err) + } + + if coreHeader.CoreReloOff > 0 && coreHeader.CoreReloLen > 0 { + if _, err := r.Seek(int64(header.HdrLen+coreHeader.CoreReloOff), io.SeekStart); err != nil { + return nil, nil, nil, fmt.Errorf("can't seek to CO-RE relocation section: %v", err) + } + + relos, err = parseExtInfoRelos(io.LimitReader(r, int64(coreHeader.CoreReloLen)), bo, strings) + if err != nil { + return nil, nil, nil, fmt.Errorf("CO-RE relocation info: %w", err) + } + } + + return funcInfo, lineInfo, relos, nil +} + +type btfExtInfoSec struct { + SecNameOff uint32 + NumInfo uint32 +} + +type extInfoRecord struct { + InsnOff uint64 + Opaque []byte +} + +type extInfo struct { + recordSize uint32 + records []extInfoRecord +} + +func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) { + if other.recordSize != ei.recordSize { + return extInfo{}, fmt.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize) + } + + records := make([]extInfoRecord, 0, len(ei.records)+len(other.records)) + records = append(records, ei.records...) + for _, info := range other.records { + records = append(records, extInfoRecord{ + InsnOff: info.InsnOff + offset, + Opaque: info.Opaque, + }) + } + return extInfo{ei.recordSize, records}, nil +} + +func (ei extInfo) MarshalBinary() ([]byte, error) { + if len(ei.records) == 0 { + return nil, nil + } + + buf := bytes.NewBuffer(make([]byte, 0, int(ei.recordSize)*len(ei.records))) + for _, info := range ei.records { + // The kernel expects offsets in number of raw bpf instructions, + // while the ELF tracks it in bytes. + insnOff := uint32(info.InsnOff / asm.InstructionSize) + if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil { + return nil, fmt.Errorf("can't write instruction offset: %v", err) + } + + buf.Write(info.Opaque) + } + + return buf.Bytes(), nil +} + +func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) { + const maxRecordSize = 256 + + var recordSize uint32 + if err := binary.Read(r, bo, &recordSize); err != nil { + return nil, fmt.Errorf("can't read record size: %v", err) + } + + if recordSize < 4 { + // Need at least insnOff + return nil, errors.New("record size too short") + } + if recordSize > maxRecordSize { + return nil, fmt.Errorf("record size %v exceeds %v", recordSize, maxRecordSize) + } + + result := make(map[string]extInfo) + for { + secName, infoHeader, err := parseExtInfoHeader(r, bo, strings) + if errors.Is(err, io.EOF) { + return result, nil + } + + var records []extInfoRecord + for i := uint32(0); i < infoHeader.NumInfo; i++ { + var byteOff uint32 + if err := binary.Read(r, bo, &byteOff); err != nil { + return nil, fmt.Errorf("section %v: can't read extended info offset: %v", secName, err) + } + + buf := make([]byte, int(recordSize-4)) + if _, err := io.ReadFull(r, buf); err != nil { + return nil, fmt.Errorf("section %v: can't read record: %v", secName, err) + } + + if byteOff%asm.InstructionSize != 0 { + return nil, fmt.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff) + } + + records = append(records, extInfoRecord{uint64(byteOff), buf}) + } + + result[secName] = extInfo{ + recordSize, + records, + } + } +} + +// bpfCoreRelo matches `struct bpf_core_relo` from the kernel +type bpfCoreRelo struct { + InsnOff uint32 + TypeID TypeID + AccessStrOff uint32 + Kind COREKind +} + +type coreRelo struct { + insnOff uint32 + typeID TypeID + accessor coreAccessor + kind COREKind +} + +type coreRelos []coreRelo + +// append two slices of extInfoRelo to each other. The InsnOff of b are adjusted +// by offset. +func (r coreRelos) append(other coreRelos, offset uint64) coreRelos { + result := make([]coreRelo, 0, len(r)+len(other)) + result = append(result, r...) + for _, relo := range other { + relo.insnOff += uint32(offset) + result = append(result, relo) + } + return result +} + +var extInfoReloSize = binary.Size(bpfCoreRelo{}) + +func parseExtInfoRelos(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]coreRelos, error) { + var recordSize uint32 + if err := binary.Read(r, bo, &recordSize); err != nil { + return nil, fmt.Errorf("read record size: %v", err) + } + + if recordSize != uint32(extInfoReloSize) { + return nil, fmt.Errorf("expected record size %d, got %d", extInfoReloSize, recordSize) + } + + result := make(map[string]coreRelos) + for { + secName, infoHeader, err := parseExtInfoHeader(r, bo, strings) + if errors.Is(err, io.EOF) { + return result, nil + } + + var relos coreRelos + for i := uint32(0); i < infoHeader.NumInfo; i++ { + var relo bpfCoreRelo + if err := binary.Read(r, bo, &relo); err != nil { + return nil, fmt.Errorf("section %v: read record: %v", secName, err) + } + + if relo.InsnOff%asm.InstructionSize != 0 { + return nil, fmt.Errorf("section %v: offset %v is not aligned with instruction size", secName, relo.InsnOff) + } + + accessorStr, err := strings.Lookup(relo.AccessStrOff) + if err != nil { + return nil, err + } + + accessor, err := parseCoreAccessor(accessorStr) + if err != nil { + return nil, fmt.Errorf("accessor %q: %s", accessorStr, err) + } + + relos = append(relos, coreRelo{ + relo.InsnOff, + relo.TypeID, + accessor, + relo.Kind, + }) + } + + result[secName] = relos + } +} + +func parseExtInfoHeader(r io.Reader, bo binary.ByteOrder, strings stringTable) (string, *btfExtInfoSec, error) { + var infoHeader btfExtInfoSec + if err := binary.Read(r, bo, &infoHeader); err != nil { + return "", nil, fmt.Errorf("read ext info header: %w", err) + } + + secName, err := strings.Lookup(infoHeader.SecNameOff) + if err != nil { + return "", nil, fmt.Errorf("get section name: %w", err) + } + + if infoHeader.NumInfo == 0 { + return "", nil, fmt.Errorf("section %s has zero records", secName) + } + + return secName, &infoHeader, nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/fuzz.go b/vendor/github.com/cilium/ebpf/internal/btf/fuzz.go new file mode 100644 index 0000000000000..37e043fd378c7 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/fuzz.go @@ -0,0 +1,49 @@ +// +build gofuzz + +// Use with https://github.com/dvyukov/go-fuzz + +package btf + +import ( + "bytes" + "encoding/binary" + + "github.com/cilium/ebpf/internal" +) + +func FuzzSpec(data []byte) int { + if len(data) < binary.Size(btfHeader{}) { + return -1 + } + + spec, err := loadNakedSpec(bytes.NewReader(data), internal.NativeEndian, nil, nil) + if err != nil { + if spec != nil { + panic("spec is not nil") + } + return 0 + } + if spec == nil { + panic("spec is nil") + } + return 1 +} + +func FuzzExtInfo(data []byte) int { + if len(data) < binary.Size(btfExtHeader{}) { + return -1 + } + + table := stringTable("\x00foo\x00barfoo\x00") + info, err := parseExtInfo(bytes.NewReader(data), internal.NativeEndian, table) + if err != nil { + if info != nil { + panic("info is not nil") + } + return 0 + } + if info == nil { + panic("info is nil") + } + return 1 +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/strings.go b/vendor/github.com/cilium/ebpf/internal/btf/strings.go new file mode 100644 index 0000000000000..8782643a043ef --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/strings.go @@ -0,0 +1,60 @@ +package btf + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" +) + +type stringTable []byte + +func readStringTable(r io.Reader) (stringTable, error) { + contents, err := ioutil.ReadAll(r) + if err != nil { + return nil, fmt.Errorf("can't read string table: %v", err) + } + + if len(contents) < 1 { + return nil, errors.New("string table is empty") + } + + if contents[0] != '\x00' { + return nil, errors.New("first item in string table is non-empty") + } + + if contents[len(contents)-1] != '\x00' { + return nil, errors.New("string table isn't null terminated") + } + + return stringTable(contents), nil +} + +func (st stringTable) Lookup(offset uint32) (string, error) { + if int64(offset) > int64(^uint(0)>>1) { + return "", fmt.Errorf("offset %d overflows int", offset) + } + + pos := int(offset) + if pos >= len(st) { + return "", fmt.Errorf("offset %d is out of bounds", offset) + } + + if pos > 0 && st[pos-1] != '\x00' { + return "", fmt.Errorf("offset %d isn't start of a string", offset) + } + + str := st[pos:] + end := bytes.IndexByte(str, '\x00') + if end == -1 { + return "", fmt.Errorf("offset %d isn't null terminated", offset) + } + + return string(str[:end]), nil +} + +func (st stringTable) LookupName(offset uint32) (Name, error) { + str, err := st.Lookup(offset) + return Name(str), err +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/types.go b/vendor/github.com/cilium/ebpf/internal/btf/types.go new file mode 100644 index 0000000000000..62aa31bcd7819 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/types.go @@ -0,0 +1,893 @@ +package btf + +import ( + "fmt" + "math" + "strings" +) + +const maxTypeDepth = 32 + +// TypeID identifies a type in a BTF section. +type TypeID uint32 + +// ID implements part of the Type interface. +func (tid TypeID) ID() TypeID { + return tid +} + +// Type represents a type described by BTF. +type Type interface { + ID() TypeID + + String() string + + // Make a copy of the type, without copying Type members. + copy() Type + + // Enumerate all nested Types. Repeated calls must visit nested + // types in the same order. + walk(*typeDeque) +} + +// namedType is a type with a name. +// +// Most named types simply embed Name. +type namedType interface { + Type + name() string + essentialName() string +} + +// Name identifies a type. +// +// Anonymous types have an empty name. +type Name string + +func (n Name) name() string { + return string(n) +} + +func (n Name) essentialName() string { + return essentialName(string(n)) +} + +// Void is the unit type of BTF. +type Void struct{} + +func (v *Void) ID() TypeID { return 0 } +func (v *Void) String() string { return "void#0" } +func (v *Void) size() uint32 { return 0 } +func (v *Void) copy() Type { return (*Void)(nil) } +func (v *Void) walk(*typeDeque) {} + +type IntEncoding byte + +const ( + Signed IntEncoding = 1 << iota + Char + Bool +) + +// Int is an integer of a given length. +type Int struct { + TypeID + Name + + // The size of the integer in bytes. + Size uint32 + Encoding IntEncoding + // Offset is the starting bit offset. Currently always 0. + // See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int + Offset uint32 + Bits byte +} + +var _ namedType = (*Int)(nil) + +func (i *Int) String() string { + var s strings.Builder + + switch { + case i.Encoding&Char != 0: + s.WriteString("char") + case i.Encoding&Bool != 0: + s.WriteString("bool") + default: + if i.Encoding&Signed == 0 { + s.WriteRune('u') + } + s.WriteString("int") + fmt.Fprintf(&s, "%d", i.Size*8) + } + + fmt.Fprintf(&s, "#%d", i.TypeID) + + if i.Bits > 0 { + fmt.Fprintf(&s, "[bits=%d]", i.Bits) + } + + return s.String() +} + +func (i *Int) size() uint32 { return i.Size } +func (i *Int) walk(*typeDeque) {} +func (i *Int) copy() Type { + cpy := *i + return &cpy +} + +func (i *Int) isBitfield() bool { + return i.Offset > 0 +} + +// Pointer is a pointer to another type. +type Pointer struct { + TypeID + Target Type +} + +func (p *Pointer) String() string { + return fmt.Sprintf("pointer#%d[target=#%d]", p.TypeID, p.Target.ID()) +} + +func (p *Pointer) size() uint32 { return 8 } +func (p *Pointer) walk(tdq *typeDeque) { tdq.push(&p.Target) } +func (p *Pointer) copy() Type { + cpy := *p + return &cpy +} + +// Array is an array with a fixed number of elements. +type Array struct { + TypeID + Type Type + Nelems uint32 +} + +func (arr *Array) String() string { + return fmt.Sprintf("array#%d[type=#%d n=%d]", arr.TypeID, arr.Type.ID(), arr.Nelems) +} + +func (arr *Array) walk(tdq *typeDeque) { tdq.push(&arr.Type) } +func (arr *Array) copy() Type { + cpy := *arr + return &cpy +} + +// Struct is a compound type of consecutive members. +type Struct struct { + TypeID + Name + // The size of the struct including padding, in bytes + Size uint32 + Members []Member +} + +func (s *Struct) String() string { + return fmt.Sprintf("struct#%d[%q]", s.TypeID, s.Name) +} + +func (s *Struct) size() uint32 { return s.Size } + +func (s *Struct) walk(tdq *typeDeque) { + for i := range s.Members { + tdq.push(&s.Members[i].Type) + } +} + +func (s *Struct) copy() Type { + cpy := *s + cpy.Members = copyMembers(s.Members) + return &cpy +} + +func (s *Struct) members() []Member { + return s.Members +} + +// Union is a compound type where members occupy the same memory. +type Union struct { + TypeID + Name + // The size of the union including padding, in bytes. + Size uint32 + Members []Member +} + +func (u *Union) String() string { + return fmt.Sprintf("union#%d[%q]", u.TypeID, u.Name) +} + +func (u *Union) size() uint32 { return u.Size } + +func (u *Union) walk(tdq *typeDeque) { + for i := range u.Members { + tdq.push(&u.Members[i].Type) + } +} + +func (u *Union) copy() Type { + cpy := *u + cpy.Members = copyMembers(u.Members) + return &cpy +} + +func (u *Union) members() []Member { + return u.Members +} + +func copyMembers(orig []Member) []Member { + cpy := make([]Member, len(orig)) + copy(cpy, orig) + return cpy +} + +type composite interface { + members() []Member +} + +var ( + _ composite = (*Struct)(nil) + _ composite = (*Union)(nil) +) + +// Member is part of a Struct or Union. +// +// It is not a valid Type. +type Member struct { + Name + Type Type + // Offset is the bit offset of this member + Offset uint32 + BitfieldSize uint32 +} + +// Enum lists possible values. +type Enum struct { + TypeID + Name + Values []EnumValue +} + +func (e *Enum) String() string { + return fmt.Sprintf("enum#%d[%q]", e.TypeID, e.Name) +} + +// EnumValue is part of an Enum +// +// Is is not a valid Type +type EnumValue struct { + Name + Value int32 +} + +func (e *Enum) size() uint32 { return 4 } +func (e *Enum) walk(*typeDeque) {} +func (e *Enum) copy() Type { + cpy := *e + cpy.Values = make([]EnumValue, len(e.Values)) + copy(cpy.Values, e.Values) + return &cpy +} + +// FwdKind is the type of forward declaration. +type FwdKind int + +// Valid types of forward declaration. +const ( + FwdStruct FwdKind = iota + FwdUnion +) + +func (fk FwdKind) String() string { + switch fk { + case FwdStruct: + return "struct" + case FwdUnion: + return "union" + default: + return fmt.Sprintf("%T(%d)", fk, int(fk)) + } +} + +// Fwd is a forward declaration of a Type. +type Fwd struct { + TypeID + Name + Kind FwdKind +} + +func (f *Fwd) String() string { + return fmt.Sprintf("fwd#%d[%s %q]", f.TypeID, f.Kind, f.Name) +} + +func (f *Fwd) walk(*typeDeque) {} +func (f *Fwd) copy() Type { + cpy := *f + return &cpy +} + +// Typedef is an alias of a Type. +type Typedef struct { + TypeID + Name + Type Type +} + +func (td *Typedef) String() string { + return fmt.Sprintf("typedef#%d[%q #%d]", td.TypeID, td.Name, td.Type.ID()) +} + +func (td *Typedef) walk(tdq *typeDeque) { tdq.push(&td.Type) } +func (td *Typedef) copy() Type { + cpy := *td + return &cpy +} + +// Volatile is a qualifier. +type Volatile struct { + TypeID + Type Type +} + +func (v *Volatile) String() string { + return fmt.Sprintf("volatile#%d[#%d]", v.TypeID, v.Type.ID()) +} + +func (v *Volatile) qualify() Type { return v.Type } +func (v *Volatile) walk(tdq *typeDeque) { tdq.push(&v.Type) } +func (v *Volatile) copy() Type { + cpy := *v + return &cpy +} + +// Const is a qualifier. +type Const struct { + TypeID + Type Type +} + +func (c *Const) String() string { + return fmt.Sprintf("const#%d[#%d]", c.TypeID, c.Type.ID()) +} + +func (c *Const) qualify() Type { return c.Type } +func (c *Const) walk(tdq *typeDeque) { tdq.push(&c.Type) } +func (c *Const) copy() Type { + cpy := *c + return &cpy +} + +// Restrict is a qualifier. +type Restrict struct { + TypeID + Type Type +} + +func (r *Restrict) String() string { + return fmt.Sprintf("restrict#%d[#%d]", r.TypeID, r.Type.ID()) +} + +func (r *Restrict) qualify() Type { return r.Type } +func (r *Restrict) walk(tdq *typeDeque) { tdq.push(&r.Type) } +func (r *Restrict) copy() Type { + cpy := *r + return &cpy +} + +// Func is a function definition. +type Func struct { + TypeID + Name + Type Type + Linkage FuncLinkage +} + +func (f *Func) String() string { + return fmt.Sprintf("func#%d[%s %q proto=#%d]", f.TypeID, f.Linkage, f.Name, f.Type.ID()) +} + +func (f *Func) walk(tdq *typeDeque) { tdq.push(&f.Type) } +func (f *Func) copy() Type { + cpy := *f + return &cpy +} + +// FuncProto is a function declaration. +type FuncProto struct { + TypeID + Return Type + Params []FuncParam +} + +func (fp *FuncProto) String() string { + var s strings.Builder + fmt.Fprintf(&s, "proto#%d[", fp.TypeID) + for _, param := range fp.Params { + fmt.Fprintf(&s, "%q=#%d, ", param.Name, param.Type.ID()) + } + fmt.Fprintf(&s, "return=#%d]", fp.Return.ID()) + return s.String() +} + +func (fp *FuncProto) walk(tdq *typeDeque) { + tdq.push(&fp.Return) + for i := range fp.Params { + tdq.push(&fp.Params[i].Type) + } +} + +func (fp *FuncProto) copy() Type { + cpy := *fp + cpy.Params = make([]FuncParam, len(fp.Params)) + copy(cpy.Params, fp.Params) + return &cpy +} + +type FuncParam struct { + Name + Type Type +} + +// Var is a global variable. +type Var struct { + TypeID + Name + Type Type + Linkage VarLinkage +} + +func (v *Var) String() string { + return fmt.Sprintf("var#%d[%s %q]", v.TypeID, v.Linkage, v.Name) +} + +func (v *Var) walk(tdq *typeDeque) { tdq.push(&v.Type) } +func (v *Var) copy() Type { + cpy := *v + return &cpy +} + +// Datasec is a global program section containing data. +type Datasec struct { + TypeID + Name + Size uint32 + Vars []VarSecinfo +} + +func (ds *Datasec) String() string { + return fmt.Sprintf("section#%d[%q]", ds.TypeID, ds.Name) +} + +func (ds *Datasec) size() uint32 { return ds.Size } + +func (ds *Datasec) walk(tdq *typeDeque) { + for i := range ds.Vars { + tdq.push(&ds.Vars[i].Type) + } +} + +func (ds *Datasec) copy() Type { + cpy := *ds + cpy.Vars = make([]VarSecinfo, len(ds.Vars)) + copy(cpy.Vars, ds.Vars) + return &cpy +} + +// VarSecinfo describes variable in a Datasec +// +// It is not a valid Type. +type VarSecinfo struct { + Type Type + Offset uint32 + Size uint32 +} + +type sizer interface { + size() uint32 +} + +var ( + _ sizer = (*Int)(nil) + _ sizer = (*Pointer)(nil) + _ sizer = (*Struct)(nil) + _ sizer = (*Union)(nil) + _ sizer = (*Enum)(nil) + _ sizer = (*Datasec)(nil) +) + +type qualifier interface { + qualify() Type +} + +var ( + _ qualifier = (*Const)(nil) + _ qualifier = (*Restrict)(nil) + _ qualifier = (*Volatile)(nil) +) + +// Sizeof returns the size of a type in bytes. +// +// Returns an error if the size can't be computed. +func Sizeof(typ Type) (int, error) { + var ( + n = int64(1) + elem int64 + ) + + for i := 0; i < maxTypeDepth; i++ { + switch v := typ.(type) { + case *Array: + if n > 0 && int64(v.Nelems) > math.MaxInt64/n { + return 0, fmt.Errorf("type %s: overflow", typ) + } + + // Arrays may be of zero length, which allows + // n to be zero as well. + n *= int64(v.Nelems) + typ = v.Type + continue + + case sizer: + elem = int64(v.size()) + + case *Typedef: + typ = v.Type + continue + + case qualifier: + typ = v.qualify() + continue + + default: + return 0, fmt.Errorf("unsized type %T", typ) + } + + if n > 0 && elem > math.MaxInt64/n { + return 0, fmt.Errorf("type %s: overflow", typ) + } + + size := n * elem + if int64(int(size)) != size { + return 0, fmt.Errorf("type %s: overflow", typ) + } + + return int(size), nil + } + + return 0, fmt.Errorf("type %s: exceeded type depth", typ) +} + +// copy a Type recursively. +// +// typ may form a cycle. +// +// Returns any errors from transform verbatim. +func copyType(typ Type, transform func(Type) (Type, error)) (Type, error) { + var ( + copies = make(map[Type]Type) + work typeDeque + ) + + for t := &typ; t != nil; t = work.pop() { + // *t is the identity of the type. + if cpy := copies[*t]; cpy != nil { + *t = cpy + continue + } + + var cpy Type + if transform != nil { + tf, err := transform(*t) + if err != nil { + return nil, fmt.Errorf("copy %s: %w", typ, err) + } + cpy = tf.copy() + } else { + cpy = (*t).copy() + } + + copies[*t] = cpy + *t = cpy + + // Mark any nested types for copying. + cpy.walk(&work) + } + + return typ, nil +} + +// typeDeque keeps track of pointers to types which still +// need to be visited. +type typeDeque struct { + types []*Type + read, write uint64 + mask uint64 +} + +// push adds a type to the stack. +func (dq *typeDeque) push(t *Type) { + if dq.write-dq.read < uint64(len(dq.types)) { + dq.types[dq.write&dq.mask] = t + dq.write++ + return + } + + new := len(dq.types) * 2 + if new == 0 { + new = 8 + } + + types := make([]*Type, new) + pivot := dq.read & dq.mask + n := copy(types, dq.types[pivot:]) + n += copy(types[n:], dq.types[:pivot]) + types[n] = t + + dq.types = types + dq.mask = uint64(new) - 1 + dq.read, dq.write = 0, uint64(n+1) +} + +// shift returns the first element or null. +func (dq *typeDeque) shift() *Type { + if dq.read == dq.write { + return nil + } + + index := dq.read & dq.mask + t := dq.types[index] + dq.types[index] = nil + dq.read++ + return t +} + +// pop returns the last element or null. +func (dq *typeDeque) pop() *Type { + if dq.read == dq.write { + return nil + } + + dq.write-- + index := dq.write & dq.mask + t := dq.types[index] + dq.types[index] = nil + return t +} + +// all returns all elements. +// +// The deque is empty after calling this method. +func (dq *typeDeque) all() []*Type { + length := dq.write - dq.read + types := make([]*Type, 0, length) + for t := dq.shift(); t != nil; t = dq.shift() { + types = append(types, t) + } + return types +} + +// inflateRawTypes takes a list of raw btf types linked via type IDs, and turns +// it into a graph of Types connected via pointers. +// +// Returns a map of named types (so, where NameOff is non-zero) and a slice of types +// indexed by TypeID. Since BTF ignores compilation units, multiple types may share +// the same name. A Type may form a cyclic graph by pointing at itself. +func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, namedTypes map[string][]namedType, err error) { + type fixupDef struct { + id TypeID + expectedKind btfKind + typ *Type + } + + var fixups []fixupDef + fixup := func(id TypeID, expectedKind btfKind, typ *Type) { + fixups = append(fixups, fixupDef{id, expectedKind, typ}) + } + + convertMembers := func(raw []btfMember, kindFlag bool) ([]Member, error) { + // NB: The fixup below relies on pre-allocating this array to + // work, since otherwise append might re-allocate members. + members := make([]Member, 0, len(raw)) + for i, btfMember := range raw { + name, err := rawStrings.LookupName(btfMember.NameOff) + if err != nil { + return nil, fmt.Errorf("can't get name for member %d: %w", i, err) + } + m := Member{ + Name: name, + Offset: btfMember.Offset, + } + if kindFlag { + m.BitfieldSize = btfMember.Offset >> 24 + m.Offset &= 0xffffff + } + members = append(members, m) + } + for i := range members { + fixup(raw[i].Type, kindUnknown, &members[i].Type) + } + return members, nil + } + + types = make([]Type, 0, len(rawTypes)) + types = append(types, (*Void)(nil)) + namedTypes = make(map[string][]namedType) + + for i, raw := range rawTypes { + var ( + // Void is defined to always be type ID 0, and is thus + // omitted from BTF. + id = TypeID(i + 1) + typ Type + ) + + name, err := rawStrings.LookupName(raw.NameOff) + if err != nil { + return nil, nil, fmt.Errorf("get name for type id %d: %w", id, err) + } + + switch raw.Kind() { + case kindInt: + encoding, offset, bits := intEncoding(*raw.data.(*uint32)) + typ = &Int{id, name, raw.Size(), encoding, offset, bits} + + case kindPointer: + ptr := &Pointer{id, nil} + fixup(raw.Type(), kindUnknown, &ptr.Target) + typ = ptr + + case kindArray: + btfArr := raw.data.(*btfArray) + + // IndexType is unused according to btf.rst. + // Don't make it available right now. + arr := &Array{id, nil, btfArr.Nelems} + fixup(btfArr.Type, kindUnknown, &arr.Type) + typ = arr + + case kindStruct: + members, err := convertMembers(raw.data.([]btfMember), raw.KindFlag()) + if err != nil { + return nil, nil, fmt.Errorf("struct %s (id %d): %w", name, id, err) + } + typ = &Struct{id, name, raw.Size(), members} + + case kindUnion: + members, err := convertMembers(raw.data.([]btfMember), raw.KindFlag()) + if err != nil { + return nil, nil, fmt.Errorf("union %s (id %d): %w", name, id, err) + } + typ = &Union{id, name, raw.Size(), members} + + case kindEnum: + rawvals := raw.data.([]btfEnum) + vals := make([]EnumValue, 0, len(rawvals)) + for i, btfVal := range rawvals { + name, err := rawStrings.LookupName(btfVal.NameOff) + if err != nil { + return nil, nil, fmt.Errorf("get name for enum value %d: %s", i, err) + } + vals = append(vals, EnumValue{ + Name: name, + Value: btfVal.Val, + }) + } + typ = &Enum{id, name, vals} + + case kindForward: + if raw.KindFlag() { + typ = &Fwd{id, name, FwdUnion} + } else { + typ = &Fwd{id, name, FwdStruct} + } + + case kindTypedef: + typedef := &Typedef{id, name, nil} + fixup(raw.Type(), kindUnknown, &typedef.Type) + typ = typedef + + case kindVolatile: + volatile := &Volatile{id, nil} + fixup(raw.Type(), kindUnknown, &volatile.Type) + typ = volatile + + case kindConst: + cnst := &Const{id, nil} + fixup(raw.Type(), kindUnknown, &cnst.Type) + typ = cnst + + case kindRestrict: + restrict := &Restrict{id, nil} + fixup(raw.Type(), kindUnknown, &restrict.Type) + typ = restrict + + case kindFunc: + fn := &Func{id, name, nil, raw.Linkage()} + fixup(raw.Type(), kindFuncProto, &fn.Type) + typ = fn + + case kindFuncProto: + rawparams := raw.data.([]btfParam) + params := make([]FuncParam, 0, len(rawparams)) + for i, param := range rawparams { + name, err := rawStrings.LookupName(param.NameOff) + if err != nil { + return nil, nil, fmt.Errorf("get name for func proto parameter %d: %s", i, err) + } + params = append(params, FuncParam{ + Name: name, + }) + } + for i := range params { + fixup(rawparams[i].Type, kindUnknown, ¶ms[i].Type) + } + + fp := &FuncProto{id, nil, params} + fixup(raw.Type(), kindUnknown, &fp.Return) + typ = fp + + case kindVar: + variable := raw.data.(*btfVariable) + v := &Var{id, name, nil, VarLinkage(variable.Linkage)} + fixup(raw.Type(), kindUnknown, &v.Type) + typ = v + + case kindDatasec: + btfVars := raw.data.([]btfVarSecinfo) + vars := make([]VarSecinfo, 0, len(btfVars)) + for _, btfVar := range btfVars { + vars = append(vars, VarSecinfo{ + Offset: btfVar.Offset, + Size: btfVar.Size, + }) + } + for i := range vars { + fixup(btfVars[i].Type, kindVar, &vars[i].Type) + } + typ = &Datasec{id, name, raw.SizeType, vars} + + default: + return nil, nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind()) + } + + types = append(types, typ) + + if named, ok := typ.(namedType); ok { + if name := essentialName(named.name()); name != "" { + namedTypes[name] = append(namedTypes[name], named) + } + } + } + + for _, fixup := range fixups { + i := int(fixup.id) + if i >= len(types) { + return nil, nil, fmt.Errorf("reference to invalid type id: %d", fixup.id) + } + + // Default void (id 0) to unknown + rawKind := kindUnknown + if i > 0 { + rawKind = rawTypes[i-1].Kind() + } + + if expected := fixup.expectedKind; expected != kindUnknown && rawKind != expected { + return nil, nil, fmt.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind) + } + + *fixup.typ = types[i] + } + + return types, namedTypes, nil +} + +// essentialName returns name without a ___ suffix. +func essentialName(name string) string { + lastIdx := strings.LastIndex(name, "___") + if lastIdx > 0 { + return name[:lastIdx] + } + return name +} diff --git a/vendor/github.com/cilium/ebpf/internal/cpu.go b/vendor/github.com/cilium/ebpf/internal/cpu.go new file mode 100644 index 0000000000000..d3424ba4345d1 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/cpu.go @@ -0,0 +1,62 @@ +package internal + +import ( + "fmt" + "io/ioutil" + "strings" + "sync" +) + +var sysCPU struct { + once sync.Once + err error + num int +} + +// PossibleCPUs returns the max number of CPUs a system may possibly have +// Logical CPU numbers must be of the form 0-n +func PossibleCPUs() (int, error) { + sysCPU.once.Do(func() { + sysCPU.num, sysCPU.err = parseCPUsFromFile("/sys/devices/system/cpu/possible") + }) + + return sysCPU.num, sysCPU.err +} + +func parseCPUsFromFile(path string) (int, error) { + spec, err := ioutil.ReadFile(path) + if err != nil { + return 0, err + } + + n, err := parseCPUs(string(spec)) + if err != nil { + return 0, fmt.Errorf("can't parse %s: %v", path, err) + } + + return n, nil +} + +// parseCPUs parses the number of cpus from a string produced +// by bitmap_list_string() in the Linux kernel. +// Multiple ranges are rejected, since they can't be unified +// into a single number. +// This is the format of /sys/devices/system/cpu/possible, it +// is not suitable for /sys/devices/system/cpu/online, etc. +func parseCPUs(spec string) (int, error) { + if strings.Trim(spec, "\n") == "0" { + return 1, nil + } + + var low, high int + n, err := fmt.Sscanf(spec, "%d-%d\n", &low, &high) + if n != 2 || err != nil { + return 0, fmt.Errorf("invalid format: %s", spec) + } + if low != 0 { + return 0, fmt.Errorf("CPU spec doesn't start at zero: %s", spec) + } + + // cpus is 0 indexed + return high + 1, nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/elf.go b/vendor/github.com/cilium/ebpf/internal/elf.go new file mode 100644 index 0000000000000..54a4313130a42 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/elf.go @@ -0,0 +1,68 @@ +package internal + +import ( + "debug/elf" + "fmt" + "io" +) + +type SafeELFFile struct { + *elf.File +} + +// NewSafeELFFile reads an ELF safely. +// +// Any panic during parsing is turned into an error. This is necessary since +// there are a bunch of unfixed bugs in debug/elf. +// +// https://github.com/golang/go/issues?q=is%3Aissue+is%3Aopen+debug%2Felf+in%3Atitle +func NewSafeELFFile(r io.ReaderAt) (safe *SafeELFFile, err error) { + defer func() { + r := recover() + if r == nil { + return + } + + safe = nil + err = fmt.Errorf("reading ELF file panicked: %s", r) + }() + + file, err := elf.NewFile(r) + if err != nil { + return nil, err + } + + return &SafeELFFile{file}, nil +} + +// Symbols is the safe version of elf.File.Symbols. +func (se *SafeELFFile) Symbols() (syms []elf.Symbol, err error) { + defer func() { + r := recover() + if r == nil { + return + } + + syms = nil + err = fmt.Errorf("reading ELF symbols panicked: %s", r) + }() + + syms, err = se.File.Symbols() + return +} + +// DynamicSymbols is the safe version of elf.File.DynamicSymbols. +func (se *SafeELFFile) DynamicSymbols() (syms []elf.Symbol, err error) { + defer func() { + r := recover() + if r == nil { + return + } + + syms = nil + err = fmt.Errorf("reading ELF dynamic symbols panicked: %s", r) + }() + + syms, err = se.File.DynamicSymbols() + return +} diff --git a/vendor/github.com/cilium/ebpf/internal/endian.go b/vendor/github.com/cilium/ebpf/internal/endian.go new file mode 100644 index 0000000000000..6ae99fcd5f3b4 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/endian.go @@ -0,0 +1,29 @@ +package internal + +import ( + "encoding/binary" + "unsafe" +) + +// NativeEndian is set to either binary.BigEndian or binary.LittleEndian, +// depending on the host's endianness. +var NativeEndian binary.ByteOrder + +// Clang is set to either "el" or "eb" depending on the host's endianness. +var ClangEndian string + +func init() { + if isBigEndian() { + NativeEndian = binary.BigEndian + ClangEndian = "eb" + } else { + NativeEndian = binary.LittleEndian + ClangEndian = "el" + } +} + +func isBigEndian() (ret bool) { + i := int(0x1) + bs := (*[int(unsafe.Sizeof(i))]byte)(unsafe.Pointer(&i)) + return bs[0] == 0 +} diff --git a/vendor/github.com/cilium/ebpf/internal/errors.go b/vendor/github.com/cilium/ebpf/internal/errors.go new file mode 100644 index 0000000000000..877bd72ee2661 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/errors.go @@ -0,0 +1,51 @@ +package internal + +import ( + "bytes" + "errors" + "fmt" + "strings" + + "github.com/cilium/ebpf/internal/unix" +) + +// ErrorWithLog returns an error that includes logs from the +// kernel verifier. +// +// logErr should be the error returned by the syscall that generated +// the log. It is used to check for truncation of the output. +func ErrorWithLog(err error, log []byte, logErr error) error { + logStr := strings.Trim(CString(log), "\t\r\n ") + if errors.Is(logErr, unix.ENOSPC) { + logStr += " (truncated...)" + } + + return &VerifierError{err, logStr} +} + +// VerifierError includes information from the eBPF verifier. +type VerifierError struct { + cause error + log string +} + +func (le *VerifierError) Unwrap() error { + return le.cause +} + +func (le *VerifierError) Error() string { + if le.log == "" { + return le.cause.Error() + } + + return fmt.Sprintf("%s: %s", le.cause, le.log) +} + +// CString turns a NUL / zero terminated byte buffer into a string. +func CString(in []byte) string { + inLen := bytes.IndexByte(in, 0) + if inLen == -1 { + return "" + } + return string(in[:inLen]) +} diff --git a/vendor/github.com/cilium/ebpf/internal/fd.go b/vendor/github.com/cilium/ebpf/internal/fd.go new file mode 100644 index 0000000000000..af04955bd531b --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/fd.go @@ -0,0 +1,69 @@ +package internal + +import ( + "errors" + "fmt" + "os" + "runtime" + "strconv" + + "github.com/cilium/ebpf/internal/unix" +) + +var ErrClosedFd = errors.New("use of closed file descriptor") + +type FD struct { + raw int64 +} + +func NewFD(value uint32) *FD { + fd := &FD{int64(value)} + runtime.SetFinalizer(fd, (*FD).Close) + return fd +} + +func (fd *FD) String() string { + return strconv.FormatInt(fd.raw, 10) +} + +func (fd *FD) Value() (uint32, error) { + if fd.raw < 0 { + return 0, ErrClosedFd + } + + return uint32(fd.raw), nil +} + +func (fd *FD) Close() error { + if fd.raw < 0 { + return nil + } + + value := int(fd.raw) + fd.raw = -1 + + fd.Forget() + return unix.Close(value) +} + +func (fd *FD) Forget() { + runtime.SetFinalizer(fd, nil) +} + +func (fd *FD) Dup() (*FD, error) { + if fd.raw < 0 { + return nil, ErrClosedFd + } + + dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0) + if err != nil { + return nil, fmt.Errorf("can't dup fd: %v", err) + } + + return NewFD(uint32(dup)), nil +} + +func (fd *FD) File(name string) *os.File { + fd.Forget() + return os.NewFile(uintptr(fd.raw), name) +} diff --git a/vendor/github.com/cilium/ebpf/internal/feature.go b/vendor/github.com/cilium/ebpf/internal/feature.go new file mode 100644 index 0000000000000..c94a2e1ee018c --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/feature.go @@ -0,0 +1,100 @@ +package internal + +import ( + "errors" + "fmt" + "sync" +) + +// ErrNotSupported indicates that a feature is not supported by the current kernel. +var ErrNotSupported = errors.New("not supported") + +// UnsupportedFeatureError is returned by FeatureTest() functions. +type UnsupportedFeatureError struct { + // The minimum Linux mainline version required for this feature. + // Used for the error string, and for sanity checking during testing. + MinimumVersion Version + + // The name of the feature that isn't supported. + Name string +} + +func (ufe *UnsupportedFeatureError) Error() string { + if ufe.MinimumVersion.Unspecified() { + return fmt.Sprintf("%s not supported", ufe.Name) + } + return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion) +} + +// Is indicates that UnsupportedFeatureError is ErrNotSupported. +func (ufe *UnsupportedFeatureError) Is(target error) bool { + return target == ErrNotSupported +} + +type featureTest struct { + sync.RWMutex + successful bool + result error +} + +// FeatureTestFn is used to determine whether the kernel supports +// a certain feature. +// +// The return values have the following semantics: +// +// err == ErrNotSupported: the feature is not available +// err == nil: the feature is available +// err != nil: the test couldn't be executed +type FeatureTestFn func() error + +// FeatureTest wraps a function so that it is run at most once. +// +// name should identify the tested feature, while version must be in the +// form Major.Minor[.Patch]. +// +// Returns an error wrapping ErrNotSupported if the feature is not supported. +func FeatureTest(name, version string, fn FeatureTestFn) func() error { + v, err := NewVersion(version) + if err != nil { + return func() error { return err } + } + + ft := new(featureTest) + return func() error { + ft.RLock() + if ft.successful { + defer ft.RUnlock() + return ft.result + } + ft.RUnlock() + ft.Lock() + defer ft.Unlock() + // check one more time on the off + // chance that two go routines + // were able to call into the write + // lock + if ft.successful { + return ft.result + } + err := fn() + switch { + case errors.Is(err, ErrNotSupported): + ft.result = &UnsupportedFeatureError{ + MinimumVersion: v, + Name: name, + } + fallthrough + + case err == nil: + ft.successful = true + + default: + // We couldn't execute the feature test to a point + // where it could make a determination. + // Don't cache the result, just return it. + return fmt.Errorf("detect support for %s: %w", name, err) + } + + return ft.result + } +} diff --git a/vendor/github.com/cilium/ebpf/internal/io.go b/vendor/github.com/cilium/ebpf/internal/io.go new file mode 100644 index 0000000000000..fa7402782d7a2 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/io.go @@ -0,0 +1,16 @@ +package internal + +import "errors" + +// DiscardZeroes makes sure that all written bytes are zero +// before discarding them. +type DiscardZeroes struct{} + +func (DiscardZeroes) Write(p []byte) (int, error) { + for _, b := range p { + if b != 0 { + return 0, errors.New("encountered non-zero byte") + } + } + return len(p), nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/pinning.go b/vendor/github.com/cilium/ebpf/internal/pinning.go new file mode 100644 index 0000000000000..5329b432d72e6 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/pinning.go @@ -0,0 +1,44 @@ +package internal + +import ( + "errors" + "fmt" + "os" + + "github.com/cilium/ebpf/internal/unix" +) + +func Pin(currentPath, newPath string, fd *FD) error { + if newPath == "" { + return errors.New("given pinning path cannot be empty") + } + if currentPath == newPath { + return nil + } + if currentPath == "" { + return BPFObjPin(newPath, fd) + } + var err error + // Renameat2 is used instead of os.Rename to disallow the new path replacing + // an existing path. + if err = unix.Renameat2(unix.AT_FDCWD, currentPath, unix.AT_FDCWD, newPath, unix.RENAME_NOREPLACE); err == nil { + // Object is now moved to the new pinning path. + return nil + } + if !os.IsNotExist(err) { + return fmt.Errorf("unable to move pinned object to new path %v: %w", newPath, err) + } + // Internal state not in sync with the file system so let's fix it. + return BPFObjPin(newPath, fd) +} + +func Unpin(pinnedPath string) error { + if pinnedPath == "" { + return nil + } + err := os.Remove(pinnedPath) + if err == nil || os.IsNotExist(err) { + return nil + } + return err +} diff --git a/vendor/github.com/cilium/ebpf/internal/ptr.go b/vendor/github.com/cilium/ebpf/internal/ptr.go new file mode 100644 index 0000000000000..f295de72cfebe --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/ptr.go @@ -0,0 +1,31 @@ +package internal + +import ( + "unsafe" + + "github.com/cilium/ebpf/internal/unix" +) + +// NewPointer creates a 64-bit pointer from an unsafe Pointer. +func NewPointer(ptr unsafe.Pointer) Pointer { + return Pointer{ptr: ptr} +} + +// NewSlicePointer creates a 64-bit pointer from a byte slice. +func NewSlicePointer(buf []byte) Pointer { + if len(buf) == 0 { + return Pointer{} + } + + return Pointer{ptr: unsafe.Pointer(&buf[0])} +} + +// NewStringPointer creates a 64-bit pointer from a string. +func NewStringPointer(str string) Pointer { + p, err := unix.BytePtrFromString(str) + if err != nil { + return Pointer{} + } + + return Pointer{ptr: unsafe.Pointer(p)} +} diff --git a/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go b/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go new file mode 100644 index 0000000000000..a56fbcc8e01b2 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go @@ -0,0 +1,14 @@ +// +build armbe mips mips64p32 + +package internal + +import ( + "unsafe" +) + +// Pointer wraps an unsafe.Pointer to be 64bit to +// conform to the syscall specification. +type Pointer struct { + pad uint32 + ptr unsafe.Pointer +} diff --git a/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go b/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go new file mode 100644 index 0000000000000..be2ecfca7313f --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go @@ -0,0 +1,14 @@ +// +build 386 amd64p32 arm mipsle mips64p32le + +package internal + +import ( + "unsafe" +) + +// Pointer wraps an unsafe.Pointer to be 64bit to +// conform to the syscall specification. +type Pointer struct { + ptr unsafe.Pointer + pad uint32 +} diff --git a/vendor/github.com/cilium/ebpf/internal/ptr_64.go b/vendor/github.com/cilium/ebpf/internal/ptr_64.go new file mode 100644 index 0000000000000..69452dceb9a03 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/ptr_64.go @@ -0,0 +1,14 @@ +// +build !386,!amd64p32,!arm,!mipsle,!mips64p32le +// +build !armbe,!mips,!mips64p32 + +package internal + +import ( + "unsafe" +) + +// Pointer wraps an unsafe.Pointer to be 64bit to +// conform to the syscall specification. +type Pointer struct { + ptr unsafe.Pointer +} diff --git a/vendor/github.com/cilium/ebpf/internal/syscall.go b/vendor/github.com/cilium/ebpf/internal/syscall.go new file mode 100644 index 0000000000000..b766e643e07af --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/syscall.go @@ -0,0 +1,245 @@ +package internal + +import ( + "fmt" + "path/filepath" + "runtime" + "syscall" + "unsafe" + + "github.com/cilium/ebpf/internal/unix" +) + +//go:generate stringer -output syscall_string.go -type=BPFCmd + +// BPFCmd identifies a subcommand of the bpf syscall. +type BPFCmd int + +// Well known BPF commands. +const ( + BPF_MAP_CREATE BPFCmd = iota + BPF_MAP_LOOKUP_ELEM + BPF_MAP_UPDATE_ELEM + BPF_MAP_DELETE_ELEM + BPF_MAP_GET_NEXT_KEY + BPF_PROG_LOAD + BPF_OBJ_PIN + BPF_OBJ_GET + BPF_PROG_ATTACH + BPF_PROG_DETACH + BPF_PROG_TEST_RUN + BPF_PROG_GET_NEXT_ID + BPF_MAP_GET_NEXT_ID + BPF_PROG_GET_FD_BY_ID + BPF_MAP_GET_FD_BY_ID + BPF_OBJ_GET_INFO_BY_FD + BPF_PROG_QUERY + BPF_RAW_TRACEPOINT_OPEN + BPF_BTF_LOAD + BPF_BTF_GET_FD_BY_ID + BPF_TASK_FD_QUERY + BPF_MAP_LOOKUP_AND_DELETE_ELEM + BPF_MAP_FREEZE + BPF_BTF_GET_NEXT_ID + BPF_MAP_LOOKUP_BATCH + BPF_MAP_LOOKUP_AND_DELETE_BATCH + BPF_MAP_UPDATE_BATCH + BPF_MAP_DELETE_BATCH + BPF_LINK_CREATE + BPF_LINK_UPDATE + BPF_LINK_GET_FD_BY_ID + BPF_LINK_GET_NEXT_ID + BPF_ENABLE_STATS + BPF_ITER_CREATE +) + +// BPF wraps SYS_BPF. +// +// Any pointers contained in attr must use the Pointer type from this package. +func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) { + r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size) + runtime.KeepAlive(attr) + + var err error + if errNo != 0 { + err = wrappedErrno{errNo} + } + + return r1, err +} + +type BPFProgAttachAttr struct { + TargetFd uint32 + AttachBpfFd uint32 + AttachType uint32 + AttachFlags uint32 + ReplaceBpfFd uint32 +} + +func BPFProgAttach(attr *BPFProgAttachAttr) error { + _, err := BPF(BPF_PROG_ATTACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + return err +} + +type BPFProgDetachAttr struct { + TargetFd uint32 + AttachBpfFd uint32 + AttachType uint32 +} + +func BPFProgDetach(attr *BPFProgDetachAttr) error { + _, err := BPF(BPF_PROG_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + return err +} + +type BPFEnableStatsAttr struct { + StatsType uint32 +} + +func BPFEnableStats(attr *BPFEnableStatsAttr) (*FD, error) { + ptr, err := BPF(BPF_ENABLE_STATS, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err != nil { + return nil, fmt.Errorf("enable stats: %w", err) + } + return NewFD(uint32(ptr)), nil + +} + +type bpfObjAttr struct { + fileName Pointer + fd uint32 + fileFlags uint32 +} + +const bpfFSType = 0xcafe4a11 + +// BPFObjPin wraps BPF_OBJ_PIN. +func BPFObjPin(fileName string, fd *FD) error { + dirName := filepath.Dir(fileName) + var statfs unix.Statfs_t + if err := unix.Statfs(dirName, &statfs); err != nil { + return err + } + if uint64(statfs.Type) != bpfFSType { + return fmt.Errorf("%s is not on a bpf filesystem", fileName) + } + + value, err := fd.Value() + if err != nil { + return err + } + + attr := bpfObjAttr{ + fileName: NewStringPointer(fileName), + fd: value, + } + _, err = BPF(BPF_OBJ_PIN, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + if err != nil { + return fmt.Errorf("pin object %s: %w", fileName, err) + } + return nil +} + +// BPFObjGet wraps BPF_OBJ_GET. +func BPFObjGet(fileName string, flags uint32) (*FD, error) { + attr := bpfObjAttr{ + fileName: NewStringPointer(fileName), + fileFlags: flags, + } + ptr, err := BPF(BPF_OBJ_GET, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + if err != nil { + return nil, fmt.Errorf("get object %s: %w", fileName, err) + } + return NewFD(uint32(ptr)), nil +} + +type bpfObjGetInfoByFDAttr struct { + fd uint32 + infoLen uint32 + info Pointer +} + +// BPFObjGetInfoByFD wraps BPF_OBJ_GET_INFO_BY_FD. +// +// Available from 4.13. +func BPFObjGetInfoByFD(fd *FD, info unsafe.Pointer, size uintptr) error { + value, err := fd.Value() + if err != nil { + return err + } + + attr := bpfObjGetInfoByFDAttr{ + fd: value, + infoLen: uint32(size), + info: NewPointer(info), + } + _, err = BPF(BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + if err != nil { + return fmt.Errorf("fd %v: %w", fd, err) + } + return nil +} + +// BPFObjName is a null-terminated string made up of +// 'A-Za-z0-9_' characters. +type BPFObjName [unix.BPF_OBJ_NAME_LEN]byte + +// NewBPFObjName truncates the result if it is too long. +func NewBPFObjName(name string) BPFObjName { + var result BPFObjName + copy(result[:unix.BPF_OBJ_NAME_LEN-1], name) + return result +} + +type BPFMapCreateAttr struct { + MapType uint32 + KeySize uint32 + ValueSize uint32 + MaxEntries uint32 + Flags uint32 + InnerMapFd uint32 // since 4.12 56f668dfe00d + NumaNode uint32 // since 4.14 96eabe7a40aa + MapName BPFObjName // since 4.15 ad5b177bd73f + MapIfIndex uint32 + BTFFd uint32 + BTFKeyTypeID uint32 + BTFValueTypeID uint32 +} + +func BPFMapCreate(attr *BPFMapCreateAttr) (*FD, error) { + fd, err := BPF(BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err != nil { + return nil, err + } + + return NewFD(uint32(fd)), nil +} + +// wrappedErrno wraps syscall.Errno to prevent direct comparisons with +// syscall.E* or unix.E* constants. +// +// You should never export an error of this type. +type wrappedErrno struct { + syscall.Errno +} + +func (we wrappedErrno) Unwrap() error { + return we.Errno +} + +type syscallError struct { + error + errno syscall.Errno +} + +func SyscallError(err error, errno syscall.Errno) error { + return &syscallError{err, errno} +} + +func (se *syscallError) Is(target error) bool { + return target == se.error +} + +func (se *syscallError) Unwrap() error { + return se.errno +} diff --git a/vendor/github.com/cilium/ebpf/internal/syscall_string.go b/vendor/github.com/cilium/ebpf/internal/syscall_string.go new file mode 100644 index 0000000000000..85df047797314 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/syscall_string.go @@ -0,0 +1,56 @@ +// Code generated by "stringer -output syscall_string.go -type=BPFCmd"; DO NOT EDIT. + +package internal + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[BPF_MAP_CREATE-0] + _ = x[BPF_MAP_LOOKUP_ELEM-1] + _ = x[BPF_MAP_UPDATE_ELEM-2] + _ = x[BPF_MAP_DELETE_ELEM-3] + _ = x[BPF_MAP_GET_NEXT_KEY-4] + _ = x[BPF_PROG_LOAD-5] + _ = x[BPF_OBJ_PIN-6] + _ = x[BPF_OBJ_GET-7] + _ = x[BPF_PROG_ATTACH-8] + _ = x[BPF_PROG_DETACH-9] + _ = x[BPF_PROG_TEST_RUN-10] + _ = x[BPF_PROG_GET_NEXT_ID-11] + _ = x[BPF_MAP_GET_NEXT_ID-12] + _ = x[BPF_PROG_GET_FD_BY_ID-13] + _ = x[BPF_MAP_GET_FD_BY_ID-14] + _ = x[BPF_OBJ_GET_INFO_BY_FD-15] + _ = x[BPF_PROG_QUERY-16] + _ = x[BPF_RAW_TRACEPOINT_OPEN-17] + _ = x[BPF_BTF_LOAD-18] + _ = x[BPF_BTF_GET_FD_BY_ID-19] + _ = x[BPF_TASK_FD_QUERY-20] + _ = x[BPF_MAP_LOOKUP_AND_DELETE_ELEM-21] + _ = x[BPF_MAP_FREEZE-22] + _ = x[BPF_BTF_GET_NEXT_ID-23] + _ = x[BPF_MAP_LOOKUP_BATCH-24] + _ = x[BPF_MAP_LOOKUP_AND_DELETE_BATCH-25] + _ = x[BPF_MAP_UPDATE_BATCH-26] + _ = x[BPF_MAP_DELETE_BATCH-27] + _ = x[BPF_LINK_CREATE-28] + _ = x[BPF_LINK_UPDATE-29] + _ = x[BPF_LINK_GET_FD_BY_ID-30] + _ = x[BPF_LINK_GET_NEXT_ID-31] + _ = x[BPF_ENABLE_STATS-32] + _ = x[BPF_ITER_CREATE-33] +} + +const _BPFCmd_name = "BPF_MAP_CREATEBPF_MAP_LOOKUP_ELEMBPF_MAP_UPDATE_ELEMBPF_MAP_DELETE_ELEMBPF_MAP_GET_NEXT_KEYBPF_PROG_LOADBPF_OBJ_PINBPF_OBJ_GETBPF_PROG_ATTACHBPF_PROG_DETACHBPF_PROG_TEST_RUNBPF_PROG_GET_NEXT_IDBPF_MAP_GET_NEXT_IDBPF_PROG_GET_FD_BY_IDBPF_MAP_GET_FD_BY_IDBPF_OBJ_GET_INFO_BY_FDBPF_PROG_QUERYBPF_RAW_TRACEPOINT_OPENBPF_BTF_LOADBPF_BTF_GET_FD_BY_IDBPF_TASK_FD_QUERYBPF_MAP_LOOKUP_AND_DELETE_ELEMBPF_MAP_FREEZEBPF_BTF_GET_NEXT_IDBPF_MAP_LOOKUP_BATCHBPF_MAP_LOOKUP_AND_DELETE_BATCHBPF_MAP_UPDATE_BATCHBPF_MAP_DELETE_BATCHBPF_LINK_CREATEBPF_LINK_UPDATEBPF_LINK_GET_FD_BY_IDBPF_LINK_GET_NEXT_IDBPF_ENABLE_STATSBPF_ITER_CREATE" + +var _BPFCmd_index = [...]uint16{0, 14, 33, 52, 71, 91, 104, 115, 126, 141, 156, 173, 193, 212, 233, 253, 275, 289, 312, 324, 344, 361, 391, 405, 424, 444, 475, 495, 515, 530, 545, 566, 586, 602, 617} + +func (i BPFCmd) String() string { + if i < 0 || i >= BPFCmd(len(_BPFCmd_index)-1) { + return "BPFCmd(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _BPFCmd_name[_BPFCmd_index[i]:_BPFCmd_index[i+1]] +} diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go new file mode 100644 index 0000000000000..0a18eaf0cf84d --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go @@ -0,0 +1,204 @@ +// +build linux + +package unix + +import ( + "bytes" + "syscall" + + linux "golang.org/x/sys/unix" +) + +const ( + ENOENT = linux.ENOENT + EEXIST = linux.EEXIST + EAGAIN = linux.EAGAIN + ENOSPC = linux.ENOSPC + EINVAL = linux.EINVAL + EPOLLIN = linux.EPOLLIN + EINTR = linux.EINTR + EPERM = linux.EPERM + ESRCH = linux.ESRCH + ENODEV = linux.ENODEV + // ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP + ENOTSUPP = syscall.Errno(0x20c) + + EBADF = linux.EBADF + BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC + BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE + BPF_F_RDONLY = linux.BPF_F_RDONLY + BPF_F_WRONLY = linux.BPF_F_WRONLY + BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG + BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG + BPF_F_SLEEPABLE = linux.BPF_F_SLEEPABLE + BPF_F_MMAPABLE = linux.BPF_F_MMAPABLE + BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP + BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN + BPF_TAG_SIZE = linux.BPF_TAG_SIZE + SYS_BPF = linux.SYS_BPF + F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC + EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD + EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC + O_CLOEXEC = linux.O_CLOEXEC + O_NONBLOCK = linux.O_NONBLOCK + PROT_READ = linux.PROT_READ + PROT_WRITE = linux.PROT_WRITE + MAP_SHARED = linux.MAP_SHARED + PERF_ATTR_SIZE_VER1 = linux.PERF_ATTR_SIZE_VER1 + PERF_TYPE_SOFTWARE = linux.PERF_TYPE_SOFTWARE + PERF_TYPE_TRACEPOINT = linux.PERF_TYPE_TRACEPOINT + PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT + PERF_EVENT_IOC_DISABLE = linux.PERF_EVENT_IOC_DISABLE + PERF_EVENT_IOC_ENABLE = linux.PERF_EVENT_IOC_ENABLE + PERF_EVENT_IOC_SET_BPF = linux.PERF_EVENT_IOC_SET_BPF + PerfBitWatermark = linux.PerfBitWatermark + PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW + PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC + RLIM_INFINITY = linux.RLIM_INFINITY + RLIMIT_MEMLOCK = linux.RLIMIT_MEMLOCK + BPF_STATS_RUN_TIME = linux.BPF_STATS_RUN_TIME + PERF_RECORD_LOST = linux.PERF_RECORD_LOST + PERF_RECORD_SAMPLE = linux.PERF_RECORD_SAMPLE + AT_FDCWD = linux.AT_FDCWD + RENAME_NOREPLACE = linux.RENAME_NOREPLACE +) + +// Statfs_t is a wrapper +type Statfs_t = linux.Statfs_t + +// Rlimit is a wrapper +type Rlimit = linux.Rlimit + +// Setrlimit is a wrapper +func Setrlimit(resource int, rlim *Rlimit) (err error) { + return linux.Setrlimit(resource, rlim) +} + +// Syscall is a wrapper +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + return linux.Syscall(trap, a1, a2, a3) +} + +// FcntlInt is a wrapper +func FcntlInt(fd uintptr, cmd, arg int) (int, error) { + return linux.FcntlInt(fd, cmd, arg) +} + +// IoctlSetInt is a wrapper +func IoctlSetInt(fd int, req uint, value int) error { + return linux.IoctlSetInt(fd, req, value) +} + +// Statfs is a wrapper +func Statfs(path string, buf *Statfs_t) (err error) { + return linux.Statfs(path, buf) +} + +// Close is a wrapper +func Close(fd int) (err error) { + return linux.Close(fd) +} + +// EpollEvent is a wrapper +type EpollEvent = linux.EpollEvent + +// EpollWait is a wrapper +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + return linux.EpollWait(epfd, events, msec) +} + +// EpollCtl is a wrapper +func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { + return linux.EpollCtl(epfd, op, fd, event) +} + +// Eventfd is a wrapper +func Eventfd(initval uint, flags int) (fd int, err error) { + return linux.Eventfd(initval, flags) +} + +// Write is a wrapper +func Write(fd int, p []byte) (n int, err error) { + return linux.Write(fd, p) +} + +// EpollCreate1 is a wrapper +func EpollCreate1(flag int) (fd int, err error) { + return linux.EpollCreate1(flag) +} + +// PerfEventMmapPage is a wrapper +type PerfEventMmapPage linux.PerfEventMmapPage + +// SetNonblock is a wrapper +func SetNonblock(fd int, nonblocking bool) (err error) { + return linux.SetNonblock(fd, nonblocking) +} + +// Mmap is a wrapper +func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { + return linux.Mmap(fd, offset, length, prot, flags) +} + +// Munmap is a wrapper +func Munmap(b []byte) (err error) { + return linux.Munmap(b) +} + +// PerfEventAttr is a wrapper +type PerfEventAttr = linux.PerfEventAttr + +// PerfEventOpen is a wrapper +func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { + return linux.PerfEventOpen(attr, pid, cpu, groupFd, flags) +} + +// Utsname is a wrapper +type Utsname = linux.Utsname + +// Uname is a wrapper +func Uname(buf *Utsname) (err error) { + return linux.Uname(buf) +} + +// Getpid is a wrapper +func Getpid() int { + return linux.Getpid() +} + +// Gettid is a wrapper +func Gettid() int { + return linux.Gettid() +} + +// Tgkill is a wrapper +func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { + return linux.Tgkill(tgid, tid, sig) +} + +// BytePtrFromString is a wrapper +func BytePtrFromString(s string) (*byte, error) { + return linux.BytePtrFromString(s) +} + +// ByteSliceToString is a wrapper +func ByteSliceToString(s []byte) string { + return linux.ByteSliceToString(s) +} + +// Renameat2 is a wrapper +func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error { + return linux.Renameat2(olddirfd, oldpath, newdirfd, newpath, flags) +} + +func KernelRelease() (string, error) { + var uname Utsname + err := Uname(&uname) + if err != nil { + return "", err + } + + end := bytes.IndexByte(uname.Release[:], 0) + release := string(uname.Release[:end]) + return release, nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go new file mode 100644 index 0000000000000..1b06defc0a095 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go @@ -0,0 +1,263 @@ +// +build !linux + +package unix + +import ( + "fmt" + "runtime" + "syscall" +) + +var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH) + +const ( + ENOENT = syscall.ENOENT + EEXIST = syscall.EEXIST + EAGAIN = syscall.EAGAIN + ENOSPC = syscall.ENOSPC + EINVAL = syscall.EINVAL + EINTR = syscall.EINTR + EPERM = syscall.EPERM + ESRCH = syscall.ESRCH + ENODEV = syscall.ENODEV + EBADF = syscall.Errno(0) + // ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP + ENOTSUPP = syscall.Errno(0x20c) + + BPF_F_NO_PREALLOC = 0 + BPF_F_NUMA_NODE = 0 + BPF_F_RDONLY = 0 + BPF_F_WRONLY = 0 + BPF_F_RDONLY_PROG = 0 + BPF_F_WRONLY_PROG = 0 + BPF_F_SLEEPABLE = 0 + BPF_F_MMAPABLE = 0 + BPF_F_INNER_MAP = 0 + BPF_OBJ_NAME_LEN = 0x10 + BPF_TAG_SIZE = 0x8 + SYS_BPF = 321 + F_DUPFD_CLOEXEC = 0x406 + EPOLLIN = 0x1 + EPOLL_CTL_ADD = 0x1 + EPOLL_CLOEXEC = 0x80000 + O_CLOEXEC = 0x80000 + O_NONBLOCK = 0x800 + PROT_READ = 0x1 + PROT_WRITE = 0x2 + MAP_SHARED = 0x1 + PERF_ATTR_SIZE_VER1 = 0 + PERF_TYPE_SOFTWARE = 0x1 + PERF_TYPE_TRACEPOINT = 0 + PERF_COUNT_SW_BPF_OUTPUT = 0xa + PERF_EVENT_IOC_DISABLE = 0 + PERF_EVENT_IOC_ENABLE = 0 + PERF_EVENT_IOC_SET_BPF = 0 + PerfBitWatermark = 0x4000 + PERF_SAMPLE_RAW = 0x400 + PERF_FLAG_FD_CLOEXEC = 0x8 + RLIM_INFINITY = 0x7fffffffffffffff + RLIMIT_MEMLOCK = 8 + BPF_STATS_RUN_TIME = 0 + PERF_RECORD_LOST = 2 + PERF_RECORD_SAMPLE = 9 + AT_FDCWD = -0x2 + RENAME_NOREPLACE = 0x1 +) + +// Statfs_t is a wrapper +type Statfs_t struct { + Type int64 + Bsize int64 + Blocks uint64 + Bfree uint64 + Bavail uint64 + Files uint64 + Ffree uint64 + Fsid [2]int32 + Namelen int64 + Frsize int64 + Flags int64 + Spare [4]int64 +} + +// Rlimit is a wrapper +type Rlimit struct { + Cur uint64 + Max uint64 +} + +// Setrlimit is a wrapper +func Setrlimit(resource int, rlim *Rlimit) (err error) { + return errNonLinux +} + +// Syscall is a wrapper +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { + return 0, 0, syscall.Errno(1) +} + +// FcntlInt is a wrapper +func FcntlInt(fd uintptr, cmd, arg int) (int, error) { + return -1, errNonLinux +} + +// IoctlSetInt is a wrapper +func IoctlSetInt(fd int, req uint, value int) error { + return errNonLinux +} + +// Statfs is a wrapper +func Statfs(path string, buf *Statfs_t) error { + return errNonLinux +} + +// Close is a wrapper +func Close(fd int) (err error) { + return errNonLinux +} + +// EpollEvent is a wrapper +type EpollEvent struct { + Events uint32 + Fd int32 + Pad int32 +} + +// EpollWait is a wrapper +func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { + return 0, errNonLinux +} + +// EpollCtl is a wrapper +func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { + return errNonLinux +} + +// Eventfd is a wrapper +func Eventfd(initval uint, flags int) (fd int, err error) { + return 0, errNonLinux +} + +// Write is a wrapper +func Write(fd int, p []byte) (n int, err error) { + return 0, errNonLinux +} + +// EpollCreate1 is a wrapper +func EpollCreate1(flag int) (fd int, err error) { + return 0, errNonLinux +} + +// PerfEventMmapPage is a wrapper +type PerfEventMmapPage struct { + Version uint32 + Compat_version uint32 + Lock uint32 + Index uint32 + Offset int64 + Time_enabled uint64 + Time_running uint64 + Capabilities uint64 + Pmc_width uint16 + Time_shift uint16 + Time_mult uint32 + Time_offset uint64 + Time_zero uint64 + Size uint32 + + Data_head uint64 + Data_tail uint64 + Data_offset uint64 + Data_size uint64 + Aux_head uint64 + Aux_tail uint64 + Aux_offset uint64 + Aux_size uint64 +} + +// SetNonblock is a wrapper +func SetNonblock(fd int, nonblocking bool) (err error) { + return errNonLinux +} + +// Mmap is a wrapper +func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { + return []byte{}, errNonLinux +} + +// Munmap is a wrapper +func Munmap(b []byte) (err error) { + return errNonLinux +} + +// PerfEventAttr is a wrapper +type PerfEventAttr struct { + Type uint32 + Size uint32 + Config uint64 + Sample uint64 + Sample_type uint64 + Read_format uint64 + Bits uint64 + Wakeup uint32 + Bp_type uint32 + Ext1 uint64 + Ext2 uint64 + Branch_sample_type uint64 + Sample_regs_user uint64 + Sample_stack_user uint32 + Clockid int32 + Sample_regs_intr uint64 + Aux_watermark uint32 + Sample_max_stack uint16 +} + +// PerfEventOpen is a wrapper +func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { + return 0, errNonLinux +} + +// Utsname is a wrapper +type Utsname struct { + Release [65]byte + Version [65]byte +} + +// Uname is a wrapper +func Uname(buf *Utsname) (err error) { + return errNonLinux +} + +// Getpid is a wrapper +func Getpid() int { + return -1 +} + +// Gettid is a wrapper +func Gettid() int { + return -1 +} + +// Tgkill is a wrapper +func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { + return errNonLinux +} + +// BytePtrFromString is a wrapper +func BytePtrFromString(s string) (*byte, error) { + return nil, errNonLinux +} + +// ByteSliceToString is a wrapper +func ByteSliceToString(s []byte) string { + return "" +} + +// Renameat2 is a wrapper +func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error { + return errNonLinux +} + +func KernelRelease() (string, error) { + return "", errNonLinux +} diff --git a/vendor/github.com/cilium/ebpf/internal/version.go b/vendor/github.com/cilium/ebpf/internal/version.go new file mode 100644 index 0000000000000..1a678bfe65749 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/version.go @@ -0,0 +1,163 @@ +package internal + +import ( + "fmt" + "io/ioutil" + "regexp" + "sync" + + "github.com/cilium/ebpf/internal/unix" +) + +const ( + // Version constant used in ELF binaries indicating that the loader needs to + // substitute the eBPF program's version with the value of the kernel's + // KERNEL_VERSION compile-time macro. Used for compatibility with BCC, gobpf + // and RedSift. + MagicKernelVersion = 0xFFFFFFFE +) + +var ( + // Match between one and three decimals separated by dots, with the last + // segment (patch level) being optional on some kernels. + // The x.y.z string must appear at the start of a string or right after + // whitespace to prevent sequences like 'x.y.z-a.b.c' from matching 'a.b.c'. + rgxKernelVersion = regexp.MustCompile(`(?:\A|\s)\d{1,3}\.\d{1,3}(?:\.\d{1,3})?`) + + kernelVersion = struct { + once sync.Once + version Version + err error + }{} +) + +// A Version in the form Major.Minor.Patch. +type Version [3]uint16 + +// NewVersion creates a version from a string like "Major.Minor.Patch". +// +// Patch is optional. +func NewVersion(ver string) (Version, error) { + var major, minor, patch uint16 + n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch) + if n < 2 { + return Version{}, fmt.Errorf("invalid version: %s", ver) + } + return Version{major, minor, patch}, nil +} + +func (v Version) String() string { + if v[2] == 0 { + return fmt.Sprintf("v%d.%d", v[0], v[1]) + } + return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2]) +} + +// Less returns true if the version is less than another version. +func (v Version) Less(other Version) bool { + for i, a := range v { + if a == other[i] { + continue + } + return a < other[i] + } + return false +} + +// Unspecified returns true if the version is all zero. +func (v Version) Unspecified() bool { + return v[0] == 0 && v[1] == 0 && v[2] == 0 +} + +// Kernel implements the kernel's KERNEL_VERSION macro from linux/version.h. +// It represents the kernel version and patch level as a single value. +func (v Version) Kernel() uint32 { + + // Kernels 4.4 and 4.9 have their SUBLEVEL clamped to 255 to avoid + // overflowing into PATCHLEVEL. + // See kernel commit 9b82f13e7ef3 ("kbuild: clamp SUBLEVEL to 255"). + s := v[2] + if s > 255 { + s = 255 + } + + // Truncate members to uint8 to prevent them from spilling over into + // each other when overflowing 8 bits. + return uint32(uint8(v[0]))<<16 | uint32(uint8(v[1]))<<8 | uint32(uint8(s)) +} + +// KernelVersion returns the version of the currently running kernel. +func KernelVersion() (Version, error) { + kernelVersion.once.Do(func() { + kernelVersion.version, kernelVersion.err = detectKernelVersion() + }) + + if kernelVersion.err != nil { + return Version{}, kernelVersion.err + } + return kernelVersion.version, nil +} + +// detectKernelVersion returns the version of the running kernel. It scans the +// following sources in order: /proc/version_signature, uname -v, uname -r. +// In each of those locations, the last-appearing x.y(.z) value is selected +// for parsing. The first location that yields a usable version number is +// returned. +func detectKernelVersion() (Version, error) { + + // Try reading /proc/version_signature for Ubuntu compatibility. + // Example format: Ubuntu 4.15.0-91.92-generic 4.15.18 + // This method exists in the kernel itself, see d18acd15c + // ("perf tools: Fix kernel version error in ubuntu"). + if pvs, err := ioutil.ReadFile("/proc/version_signature"); err == nil { + // If /proc/version_signature exists, failing to parse it is an error. + // It only exists on Ubuntu, where the real patch level is not obtainable + // through any other method. + v, err := findKernelVersion(string(pvs)) + if err != nil { + return Version{}, err + } + return v, nil + } + + var uname unix.Utsname + if err := unix.Uname(&uname); err != nil { + return Version{}, fmt.Errorf("calling uname: %w", err) + } + + // Debian puts the version including the patch level in uname.Version. + // It is not an error if there's no version number in uname.Version, + // as most distributions don't use it. Parsing can continue on uname.Release. + // Example format: #1 SMP Debian 4.19.37-5+deb10u2 (2019-08-08) + if v, err := findKernelVersion(unix.ByteSliceToString(uname.Version[:])); err == nil { + return v, nil + } + + // Most other distributions have the full kernel version including patch + // level in uname.Release. + // Example format: 4.19.0-5-amd64, 5.5.10-arch1-1 + v, err := findKernelVersion(unix.ByteSliceToString(uname.Release[:])) + if err != nil { + return Version{}, err + } + + return v, nil +} + +// findKernelVersion matches s against rgxKernelVersion and parses the result +// into a Version. If s contains multiple matches, the last entry is selected. +func findKernelVersion(s string) (Version, error) { + m := rgxKernelVersion.FindAllString(s, -1) + if m == nil { + return Version{}, fmt.Errorf("no kernel version in string: %s", s) + } + // Pick the last match of the string in case there are multiple. + s = m[len(m)-1] + + v, err := NewVersion(s) + if err != nil { + return Version{}, fmt.Errorf("parsing version string %s: %w", s, err) + } + + return v, nil +} diff --git a/vendor/github.com/cilium/ebpf/link/cgroup.go b/vendor/github.com/cilium/ebpf/link/cgroup.go new file mode 100644 index 0000000000000..5540bb068cd20 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/cgroup.go @@ -0,0 +1,171 @@ +package link + +import ( + "errors" + "fmt" + "os" + + "github.com/cilium/ebpf" +) + +type cgroupAttachFlags uint32 + +// cgroup attach flags +const ( + flagAllowOverride cgroupAttachFlags = 1 << iota + flagAllowMulti + flagReplace +) + +type CgroupOptions struct { + // Path to a cgroupv2 folder. + Path string + // One of the AttachCgroup* constants + Attach ebpf.AttachType + // Program must be of type CGroup*, and the attach type must match Attach. + Program *ebpf.Program +} + +// AttachCgroup links a BPF program to a cgroup. +func AttachCgroup(opts CgroupOptions) (Link, error) { + cgroup, err := os.Open(opts.Path) + if err != nil { + return nil, fmt.Errorf("can't open cgroup: %s", err) + } + + clone, err := opts.Program.Clone() + if err != nil { + cgroup.Close() + return nil, err + } + + var cg Link + cg, err = newLinkCgroup(cgroup, opts.Attach, clone) + if errors.Is(err, ErrNotSupported) { + cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowMulti) + } + if errors.Is(err, ErrNotSupported) { + cg, err = newProgAttachCgroup(cgroup, opts.Attach, clone, flagAllowOverride) + } + if err != nil { + cgroup.Close() + clone.Close() + return nil, err + } + + return cg, nil +} + +// LoadPinnedCgroup loads a pinned cgroup from a bpffs. +func LoadPinnedCgroup(fileName string, opts *ebpf.LoadPinOptions) (Link, error) { + link, err := LoadPinnedRawLink(fileName, CgroupType, opts) + if err != nil { + return nil, err + } + + return &linkCgroup{*link}, nil +} + +type progAttachCgroup struct { + cgroup *os.File + current *ebpf.Program + attachType ebpf.AttachType + flags cgroupAttachFlags +} + +var _ Link = (*progAttachCgroup)(nil) + +func (cg *progAttachCgroup) isLink() {} + +func newProgAttachCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program, flags cgroupAttachFlags) (*progAttachCgroup, error) { + if flags&flagAllowMulti > 0 { + if err := haveProgAttachReplace(); err != nil { + return nil, fmt.Errorf("can't support multiple programs: %w", err) + } + } + + err := RawAttachProgram(RawAttachProgramOptions{ + Target: int(cgroup.Fd()), + Program: prog, + Flags: uint32(flags), + Attach: attach, + }) + if err != nil { + return nil, fmt.Errorf("cgroup: %w", err) + } + + return &progAttachCgroup{cgroup, prog, attach, flags}, nil +} + +func (cg *progAttachCgroup) Close() error { + defer cg.cgroup.Close() + defer cg.current.Close() + + err := RawDetachProgram(RawDetachProgramOptions{ + Target: int(cg.cgroup.Fd()), + Program: cg.current, + Attach: cg.attachType, + }) + if err != nil { + return fmt.Errorf("close cgroup: %s", err) + } + return nil +} + +func (cg *progAttachCgroup) Update(prog *ebpf.Program) error { + new, err := prog.Clone() + if err != nil { + return err + } + + args := RawAttachProgramOptions{ + Target: int(cg.cgroup.Fd()), + Program: prog, + Attach: cg.attachType, + Flags: uint32(cg.flags), + } + + if cg.flags&flagAllowMulti > 0 { + // Atomically replacing multiple programs requires at least + // 5.5 (commit 7dd68b3279f17921 "bpf: Support replacing cgroup-bpf + // program in MULTI mode") + args.Flags |= uint32(flagReplace) + args.Replace = cg.current + } + + if err := RawAttachProgram(args); err != nil { + new.Close() + return fmt.Errorf("can't update cgroup: %s", err) + } + + cg.current.Close() + cg.current = new + return nil +} + +func (cg *progAttachCgroup) Pin(string) error { + return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported) +} + +func (cg *progAttachCgroup) Unpin() error { + return fmt.Errorf("can't pin cgroup: %w", ErrNotSupported) +} + +type linkCgroup struct { + RawLink +} + +var _ Link = (*linkCgroup)(nil) + +func newLinkCgroup(cgroup *os.File, attach ebpf.AttachType, prog *ebpf.Program) (*linkCgroup, error) { + link, err := AttachRawLink(RawLinkOptions{ + Target: int(cgroup.Fd()), + Program: prog, + Attach: attach, + }) + if err != nil { + return nil, err + } + + return &linkCgroup{*link}, err +} diff --git a/vendor/github.com/cilium/ebpf/link/doc.go b/vendor/github.com/cilium/ebpf/link/doc.go new file mode 100644 index 0000000000000..2bde35ed7a265 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/doc.go @@ -0,0 +1,2 @@ +// Package link allows attaching eBPF programs to various kernel hooks. +package link diff --git a/vendor/github.com/cilium/ebpf/link/iter.go b/vendor/github.com/cilium/ebpf/link/iter.go new file mode 100644 index 0000000000000..654d34ef8482e --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/iter.go @@ -0,0 +1,100 @@ +package link + +import ( + "fmt" + "io" + "unsafe" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal" +) + +type IterOptions struct { + // Program must be of type Tracing with attach type + // AttachTraceIter. The kind of iterator to attach to is + // determined at load time via the AttachTo field. + // + // AttachTo requires the kernel to include BTF of itself, + // and it to be compiled with a recent pahole (>= 1.16). + Program *ebpf.Program + + // Map specifies the target map for bpf_map_elem and sockmap iterators. + // It may be nil. + Map *ebpf.Map +} + +// AttachIter attaches a BPF seq_file iterator. +func AttachIter(opts IterOptions) (*Iter, error) { + if err := haveBPFLink(); err != nil { + return nil, err + } + + progFd := opts.Program.FD() + if progFd < 0 { + return nil, fmt.Errorf("invalid program: %s", internal.ErrClosedFd) + } + + var info bpfIterLinkInfoMap + if opts.Map != nil { + mapFd := opts.Map.FD() + if mapFd < 0 { + return nil, fmt.Errorf("invalid map: %w", internal.ErrClosedFd) + } + info.map_fd = uint32(mapFd) + } + + attr := bpfLinkCreateIterAttr{ + prog_fd: uint32(progFd), + attach_type: ebpf.AttachTraceIter, + iter_info: internal.NewPointer(unsafe.Pointer(&info)), + iter_info_len: uint32(unsafe.Sizeof(info)), + } + + fd, err := bpfLinkCreateIter(&attr) + if err != nil { + return nil, fmt.Errorf("can't link iterator: %w", err) + } + + return &Iter{RawLink{fd, ""}}, err +} + +// LoadPinnedIter loads a pinned iterator from a bpffs. +func LoadPinnedIter(fileName string, opts *ebpf.LoadPinOptions) (*Iter, error) { + link, err := LoadPinnedRawLink(fileName, IterType, opts) + if err != nil { + return nil, err + } + + return &Iter{*link}, err +} + +// Iter represents an attached bpf_iter. +type Iter struct { + RawLink +} + +// Open creates a new instance of the iterator. +// +// Reading from the returned reader triggers the BPF program. +func (it *Iter) Open() (io.ReadCloser, error) { + linkFd, err := it.fd.Value() + if err != nil { + return nil, err + } + + attr := &bpfIterCreateAttr{ + linkFd: linkFd, + } + + fd, err := bpfIterCreate(attr) + if err != nil { + return nil, fmt.Errorf("can't create iterator: %w", err) + } + + return fd.File("bpf_iter"), nil +} + +// union bpf_iter_link_info.map +type bpfIterLinkInfoMap struct { + map_fd uint32 +} diff --git a/vendor/github.com/cilium/ebpf/link/kprobe.go b/vendor/github.com/cilium/ebpf/link/kprobe.go new file mode 100644 index 0000000000000..ea71d6d608a82 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/kprobe.go @@ -0,0 +1,438 @@ +package link + +import ( + "bytes" + "crypto/rand" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "sync" + "unsafe" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/unix" +) + +var ( + kprobeEventsPath = filepath.Join(tracefsPath, "kprobe_events") + + kprobeRetprobeBit = struct { + once sync.Once + value uint64 + err error + }{} +) + +type probeType uint8 + +const ( + kprobeType probeType = iota + uprobeType +) + +func (pt probeType) String() string { + if pt == kprobeType { + return "kprobe" + } + return "uprobe" +} + +func (pt probeType) EventsPath() string { + if pt == kprobeType { + return kprobeEventsPath + } + return uprobeEventsPath +} + +func (pt probeType) PerfEventType(ret bool) perfEventType { + if pt == kprobeType { + if ret { + return kretprobeEvent + } + return kprobeEvent + } + if ret { + return uretprobeEvent + } + return uprobeEvent +} + +func (pt probeType) RetprobeBit() (uint64, error) { + if pt == kprobeType { + return kretprobeBit() + } + return uretprobeBit() +} + +// Kprobe attaches the given eBPF program to a perf event that fires when the +// given kernel symbol starts executing. See /proc/kallsyms for available +// symbols. For example, printk(): +// +// Kprobe("printk", prog) +// +// The resulting Link must be Closed during program shutdown to avoid leaking +// system resources. +func Kprobe(symbol string, prog *ebpf.Program) (Link, error) { + k, err := kprobe(symbol, prog, false) + if err != nil { + return nil, err + } + + err = k.attach(prog) + if err != nil { + k.Close() + return nil, err + } + + return k, nil +} + +// Kretprobe attaches the given eBPF program to a perf event that fires right +// before the given kernel symbol exits, with the function stack left intact. +// See /proc/kallsyms for available symbols. For example, printk(): +// +// Kretprobe("printk", prog) +// +// The resulting Link must be Closed during program shutdown to avoid leaking +// system resources. +func Kretprobe(symbol string, prog *ebpf.Program) (Link, error) { + k, err := kprobe(symbol, prog, true) + if err != nil { + return nil, err + } + + err = k.attach(prog) + if err != nil { + k.Close() + return nil, err + } + + return k, nil +} + +// kprobe opens a perf event on the given symbol and attaches prog to it. +// If ret is true, create a kretprobe. +func kprobe(symbol string, prog *ebpf.Program, ret bool) (*perfEvent, error) { + if symbol == "" { + return nil, fmt.Errorf("symbol name cannot be empty: %w", errInvalidInput) + } + if prog == nil { + return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) + } + if !rgxTraceEvent.MatchString(symbol) { + return nil, fmt.Errorf("symbol '%s' must be alphanumeric or underscore: %w", symbol, errInvalidInput) + } + if prog.Type() != ebpf.Kprobe { + return nil, fmt.Errorf("eBPF program type %s is not a Kprobe: %w", prog.Type(), errInvalidInput) + } + + // Use kprobe PMU if the kernel has it available. + tp, err := pmuKprobe(platformPrefix(symbol), ret) + if errors.Is(err, os.ErrNotExist) { + tp, err = pmuKprobe(symbol, ret) + } + if err == nil { + return tp, nil + } + if err != nil && !errors.Is(err, ErrNotSupported) { + return nil, fmt.Errorf("creating perf_kprobe PMU: %w", err) + } + + // Use tracefs if kprobe PMU is missing. + tp, err = tracefsKprobe(platformPrefix(symbol), ret) + if errors.Is(err, os.ErrNotExist) { + tp, err = tracefsKprobe(symbol, ret) + } + if err != nil { + return nil, fmt.Errorf("creating trace event '%s' in tracefs: %w", symbol, err) + } + + return tp, nil +} + +// pmuKprobe opens a perf event based on the kprobe PMU. +// Returns os.ErrNotExist if the given symbol does not exist in the kernel. +func pmuKprobe(symbol string, ret bool) (*perfEvent, error) { + return pmuProbe(kprobeType, symbol, "", 0, ret) +} + +// pmuProbe opens a perf event based on a Performance Monitoring Unit. +// +// Requires at least a 4.17 kernel. +// e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU" +// 33ea4b24277b "perf/core: Implement the 'perf_uprobe' PMU" +// +// Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU +func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*perfEvent, error) { + // Getting the PMU type will fail if the kernel doesn't support + // the perf_[k,u]probe PMU. + et, err := getPMUEventType(typ) + if err != nil { + return nil, err + } + + var config uint64 + if ret { + bit, err := typ.RetprobeBit() + if err != nil { + return nil, err + } + config |= 1 << bit + } + + var ( + attr unix.PerfEventAttr + sp unsafe.Pointer + ) + switch typ { + case kprobeType: + // Create a pointer to a NUL-terminated string for the kernel. + sp, err := unsafeStringPtr(symbol) + if err != nil { + return nil, err + } + + attr = unix.PerfEventAttr{ + Type: uint32(et), // PMU event type read from sysfs + Ext1: uint64(uintptr(sp)), // Kernel symbol to trace + Config: config, // Retprobe flag + } + case uprobeType: + sp, err := unsafeStringPtr(path) + if err != nil { + return nil, err + } + + attr = unix.PerfEventAttr{ + // The minimum size required for PMU uprobes is PERF_ATTR_SIZE_VER1, + // since it added the config2 (Ext2) field. The Size field controls the + // size of the internal buffer the kernel allocates for reading the + // perf_event_attr argument from userspace. + Size: unix.PERF_ATTR_SIZE_VER1, + Type: uint32(et), // PMU event type read from sysfs + Ext1: uint64(uintptr(sp)), // Uprobe path + Ext2: offset, // Uprobe offset + Config: config, // Retprobe flag + } + } + + fd, err := unix.PerfEventOpen(&attr, perfAllThreads, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) + + // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL + // when trying to create a kretprobe for a missing symbol. Make sure ENOENT + // is returned to the caller. + if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { + return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, os.ErrNotExist) + } + if err != nil { + return nil, fmt.Errorf("opening perf event: %w", err) + } + + // Ensure the string pointer is not collected before PerfEventOpen returns. + runtime.KeepAlive(sp) + + // Kernel has perf_[k,u]probe PMU available, initialize perf event. + return &perfEvent{ + fd: internal.NewFD(uint32(fd)), + pmuID: et, + name: symbol, + typ: typ.PerfEventType(ret), + }, nil +} + +// tracefsKprobe creates a Kprobe tracefs entry. +func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) { + return tracefsProbe(kprobeType, symbol, "", 0, ret) +} + +// tracefsProbe creates a trace event by writing an entry to /[k,u]probe_events. +// A new trace event group name is generated on every call to support creating +// multiple trace events for the same kernel or userspace symbol. +// Path and offset are only set in the case of uprobe(s) and are used to set +// the executable/library path on the filesystem and the offset where the probe is inserted. +// A perf event is then opened on the newly-created trace event and returned to the caller. +func tracefsProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*perfEvent, error) { + // Generate a random string for each trace event we attempt to create. + // This value is used as the 'group' token in tracefs to allow creating + // multiple kprobe trace events with the same name. + group, err := randomGroup("ebpf") + if err != nil { + return nil, fmt.Errorf("randomizing group name: %w", err) + } + + // Before attempting to create a trace event through tracefs, + // check if an event with the same group and name already exists. + // Kernels 4.x and earlier don't return os.ErrExist on writing a duplicate + // entry, so we need to rely on reads for detecting uniqueness. + _, err = getTraceEventID(group, symbol) + if err == nil { + return nil, fmt.Errorf("trace event already exists: %s/%s", group, symbol) + } + if err != nil && !errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("checking trace event %s/%s: %w", group, symbol, err) + } + + // Create the [k,u]probe trace event using tracefs. + if err := createTraceFSProbeEvent(typ, group, symbol, path, offset, ret); err != nil { + return nil, fmt.Errorf("creating probe entry on tracefs: %w", err) + } + + // Get the newly-created trace event's id. + tid, err := getTraceEventID(group, symbol) + if err != nil { + return nil, fmt.Errorf("getting trace event id: %w", err) + } + + // Kprobes are ephemeral tracepoints and share the same perf event type. + fd, err := openTracepointPerfEvent(tid) + if err != nil { + return nil, err + } + + return &perfEvent{ + fd: fd, + group: group, + name: symbol, + tracefsID: tid, + typ: typ.PerfEventType(ret), + }, nil +} + +// createTraceFSProbeEvent creates a new ephemeral trace event by writing to +// /[k,u]probe_events. Returns os.ErrNotExist if symbol is not a valid +// kernel symbol, or if it is not traceable with kprobes. Returns os.ErrExist +// if a probe with the same group and symbol already exists. +func createTraceFSProbeEvent(typ probeType, group, symbol, path string, offset uint64, ret bool) error { + // Open the kprobe_events file in tracefs. + f, err := os.OpenFile(typ.EventsPath(), os.O_APPEND|os.O_WRONLY, 0666) + if err != nil { + return fmt.Errorf("error opening '%s': %w", typ.EventsPath(), err) + } + defer f.Close() + + var pe string + switch typ { + case kprobeType: + // The kprobe_events syntax is as follows (see Documentation/trace/kprobetrace.txt): + // p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe + // r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe + // -:[GRP/]EVENT : Clear a probe + // + // Some examples: + // r:ebpf_1234/r_my_kretprobe nf_conntrack_destroy + // p:ebpf_5678/p_my_kprobe __x64_sys_execve + // + // Leaving the kretprobe's MAXACTIVE set to 0 (or absent) will make the + // kernel default to NR_CPUS. This is desired in most eBPF cases since + // subsampling or rate limiting logic can be more accurately implemented in + // the eBPF program itself. + // See Documentation/kprobes.txt for more details. + pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(ret), group, symbol, symbol) + case uprobeType: + // The uprobe_events syntax is as follows: + // p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a probe + // r[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a return probe + // -:[GRP/]EVENT : Clear a probe + // + // Some examples: + // r:ebpf_1234/readline /bin/bash:0x12345 + // p:ebpf_5678/main_mySymbol /bin/mybin:0x12345 + // + // See Documentation/trace/uprobetracer.txt for more details. + pathOffset := uprobePathOffset(path, offset) + pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(ret), group, symbol, pathOffset) + } + _, err = f.WriteString(pe) + // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL + // when trying to create a kretprobe for a missing symbol. Make sure ENOENT + // is returned to the caller. + if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { + return fmt.Errorf("symbol %s not found: %w", symbol, os.ErrNotExist) + } + if err != nil { + return fmt.Errorf("writing '%s' to '%s': %w", pe, typ.EventsPath(), err) + } + + return nil +} + +// closeTraceFSProbeEvent removes the [k,u]probe with the given type, group and symbol +// from /[k,u]probe_events. +func closeTraceFSProbeEvent(typ probeType, group, symbol string) error { + f, err := os.OpenFile(typ.EventsPath(), os.O_APPEND|os.O_WRONLY, 0666) + if err != nil { + return fmt.Errorf("error opening %s: %w", typ.EventsPath(), err) + } + defer f.Close() + + // See [k,u]probe_events syntax above. The probe type does not need to be specified + // for removals. + pe := fmt.Sprintf("-:%s/%s", group, symbol) + if _, err = f.WriteString(pe); err != nil { + return fmt.Errorf("writing '%s' to '%s': %w", pe, typ.EventsPath(), err) + } + + return nil +} + +// randomGroup generates a pseudorandom string for use as a tracefs group name. +// Returns an error when the output string would exceed 63 characters (kernel +// limitation), when rand.Read() fails or when prefix contains characters not +// allowed by rgxTraceEvent. +func randomGroup(prefix string) (string, error) { + if !rgxTraceEvent.MatchString(prefix) { + return "", fmt.Errorf("prefix '%s' must be alphanumeric or underscore: %w", prefix, errInvalidInput) + } + + b := make([]byte, 8) + if _, err := rand.Read(b); err != nil { + return "", fmt.Errorf("reading random bytes: %w", err) + } + + group := fmt.Sprintf("%s_%x", prefix, b) + if len(group) > 63 { + return "", fmt.Errorf("group name '%s' cannot be longer than 63 characters: %w", group, errInvalidInput) + } + + return group, nil +} + +func probePrefix(ret bool) string { + if ret { + return "r" + } + return "p" +} + +// determineRetprobeBit reads a Performance Monitoring Unit's retprobe bit +// from /sys/bus/event_source/devices//format/retprobe. +func determineRetprobeBit(typ probeType) (uint64, error) { + p := filepath.Join("/sys/bus/event_source/devices/", typ.String(), "/format/retprobe") + + data, err := ioutil.ReadFile(p) + if err != nil { + return 0, err + } + + var rp uint64 + n, err := fmt.Sscanf(string(bytes.TrimSpace(data)), "config:%d", &rp) + if err != nil { + return 0, fmt.Errorf("parse retprobe bit: %w", err) + } + if n != 1 { + return 0, fmt.Errorf("parse retprobe bit: expected 1 item, got %d", n) + } + + return rp, nil +} + +func kretprobeBit() (uint64, error) { + kprobeRetprobeBit.once.Do(func() { + kprobeRetprobeBit.value, kprobeRetprobeBit.err = determineRetprobeBit(kprobeType) + }) + return kprobeRetprobeBit.value, kprobeRetprobeBit.err +} diff --git a/vendor/github.com/cilium/ebpf/link/link.go b/vendor/github.com/cilium/ebpf/link/link.go new file mode 100644 index 0000000000000..16cfff415dd40 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/link.go @@ -0,0 +1,229 @@ +package link + +import ( + "fmt" + "unsafe" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal" +) + +var ErrNotSupported = internal.ErrNotSupported + +// Link represents a Program attached to a BPF hook. +type Link interface { + // Replace the current program with a new program. + // + // Passing a nil program is an error. May return an error wrapping ErrNotSupported. + Update(*ebpf.Program) error + + // Persist a link by pinning it into a bpffs. + // + // May return an error wrapping ErrNotSupported. + Pin(string) error + + // Undo a previous call to Pin. + // + // May return an error wrapping ErrNotSupported. + Unpin() error + + // Close frees resources. + // + // The link will be broken unless it has been pinned. A link + // may continue past the lifetime of the process if Close is + // not called. + Close() error + + // Prevent external users from implementing this interface. + isLink() +} + +// ID uniquely identifies a BPF link. +type ID uint32 + +// RawLinkOptions control the creation of a raw link. +type RawLinkOptions struct { + // File descriptor to attach to. This differs for each attach type. + Target int + // Program to attach. + Program *ebpf.Program + // Attach must match the attach type of Program. + Attach ebpf.AttachType +} + +// RawLinkInfo contains metadata on a link. +type RawLinkInfo struct { + Type Type + ID ID + Program ebpf.ProgramID +} + +// RawLink is the low-level API to bpf_link. +// +// You should consider using the higher level interfaces in this +// package instead. +type RawLink struct { + fd *internal.FD + pinnedPath string +} + +// AttachRawLink creates a raw link. +func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { + if err := haveBPFLink(); err != nil { + return nil, err + } + + if opts.Target < 0 { + return nil, fmt.Errorf("invalid target: %s", internal.ErrClosedFd) + } + + progFd := opts.Program.FD() + if progFd < 0 { + return nil, fmt.Errorf("invalid program: %s", internal.ErrClosedFd) + } + + attr := bpfLinkCreateAttr{ + targetFd: uint32(opts.Target), + progFd: uint32(progFd), + attachType: opts.Attach, + } + fd, err := bpfLinkCreate(&attr) + if err != nil { + return nil, fmt.Errorf("can't create link: %s", err) + } + + return &RawLink{fd, ""}, nil +} + +// LoadPinnedRawLink loads a persisted link from a bpffs. +// +// Returns an error if the pinned link type doesn't match linkType. Pass +// UnspecifiedType to disable this behaviour. +func LoadPinnedRawLink(fileName string, linkType Type, opts *ebpf.LoadPinOptions) (*RawLink, error) { + fd, err := internal.BPFObjGet(fileName, opts.Marshal()) + if err != nil { + return nil, fmt.Errorf("load pinned link: %w", err) + } + + link := &RawLink{fd, fileName} + if linkType == UnspecifiedType { + return link, nil + } + + info, err := link.Info() + if err != nil { + link.Close() + return nil, fmt.Errorf("get pinned link info: %s", err) + } + + if info.Type != linkType { + link.Close() + return nil, fmt.Errorf("link type %v doesn't match %v", info.Type, linkType) + } + + return link, nil +} + +func (l *RawLink) isLink() {} + +// FD returns the raw file descriptor. +func (l *RawLink) FD() int { + fd, err := l.fd.Value() + if err != nil { + return -1 + } + return int(fd) +} + +// Close breaks the link. +// +// Use Pin if you want to make the link persistent. +func (l *RawLink) Close() error { + return l.fd.Close() +} + +// Pin persists a link past the lifetime of the process. +// +// Calling Close on a pinned Link will not break the link +// until the pin is removed. +func (l *RawLink) Pin(fileName string) error { + if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil { + return err + } + l.pinnedPath = fileName + return nil +} + +// Unpin implements the Link interface. +func (l *RawLink) Unpin() error { + if err := internal.Unpin(l.pinnedPath); err != nil { + return err + } + l.pinnedPath = "" + return nil +} + +// Update implements the Link interface. +func (l *RawLink) Update(new *ebpf.Program) error { + return l.UpdateArgs(RawLinkUpdateOptions{ + New: new, + }) +} + +// RawLinkUpdateOptions control the behaviour of RawLink.UpdateArgs. +type RawLinkUpdateOptions struct { + New *ebpf.Program + Old *ebpf.Program + Flags uint32 +} + +// UpdateArgs updates a link based on args. +func (l *RawLink) UpdateArgs(opts RawLinkUpdateOptions) error { + newFd := opts.New.FD() + if newFd < 0 { + return fmt.Errorf("invalid program: %s", internal.ErrClosedFd) + } + + var oldFd int + if opts.Old != nil { + oldFd = opts.Old.FD() + if oldFd < 0 { + return fmt.Errorf("invalid replacement program: %s", internal.ErrClosedFd) + } + } + + linkFd, err := l.fd.Value() + if err != nil { + return fmt.Errorf("can't update link: %s", err) + } + + attr := bpfLinkUpdateAttr{ + linkFd: linkFd, + newProgFd: uint32(newFd), + oldProgFd: uint32(oldFd), + flags: opts.Flags, + } + return bpfLinkUpdate(&attr) +} + +// struct bpf_link_info +type bpfLinkInfo struct { + typ uint32 + id uint32 + prog_id uint32 +} + +// Info returns metadata about the link. +func (l *RawLink) Info() (*RawLinkInfo, error) { + var info bpfLinkInfo + err := internal.BPFObjGetInfoByFD(l.fd, unsafe.Pointer(&info), unsafe.Sizeof(info)) + if err != nil { + return nil, fmt.Errorf("link info: %s", err) + } + + return &RawLinkInfo{ + Type(info.typ), + ID(info.id), + ebpf.ProgramID(info.prog_id), + }, nil +} diff --git a/vendor/github.com/cilium/ebpf/link/netns.go b/vendor/github.com/cilium/ebpf/link/netns.go new file mode 100644 index 0000000000000..37e5b84c4ddfd --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/netns.go @@ -0,0 +1,60 @@ +package link + +import ( + "fmt" + + "github.com/cilium/ebpf" +) + +// NetNsInfo contains metadata about a network namespace link. +type NetNsInfo struct { + RawLinkInfo +} + +// NetNsLink is a program attached to a network namespace. +type NetNsLink struct { + *RawLink +} + +// AttachNetNs attaches a program to a network namespace. +func AttachNetNs(ns int, prog *ebpf.Program) (*NetNsLink, error) { + var attach ebpf.AttachType + switch t := prog.Type(); t { + case ebpf.FlowDissector: + attach = ebpf.AttachFlowDissector + case ebpf.SkLookup: + attach = ebpf.AttachSkLookup + default: + return nil, fmt.Errorf("can't attach %v to network namespace", t) + } + + link, err := AttachRawLink(RawLinkOptions{ + Target: ns, + Program: prog, + Attach: attach, + }) + if err != nil { + return nil, err + } + + return &NetNsLink{link}, nil +} + +// LoadPinnedNetNs loads a network namespace link from bpffs. +func LoadPinnedNetNs(fileName string, opts *ebpf.LoadPinOptions) (*NetNsLink, error) { + link, err := LoadPinnedRawLink(fileName, NetNsType, opts) + if err != nil { + return nil, err + } + + return &NetNsLink{link}, nil +} + +// Info returns information about the link. +func (nns *NetNsLink) Info() (*NetNsInfo, error) { + info, err := nns.RawLink.Info() + if err != nil { + return nil, err + } + return &NetNsInfo{*info}, nil +} diff --git a/vendor/github.com/cilium/ebpf/link/perf_event.go b/vendor/github.com/cilium/ebpf/link/perf_event.go new file mode 100644 index 0000000000000..5267a47ec9bbd --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/perf_event.go @@ -0,0 +1,273 @@ +package link + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "runtime" + "strconv" + "strings" + "unsafe" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/unix" +) + +// Getting the terminology right is usually the hardest part. For posterity and +// for staying sane during implementation: +// +// - trace event: Representation of a kernel runtime hook. Filesystem entries +// under /events. Can be tracepoints (static), kprobes or uprobes. +// Can be instantiated into perf events (see below). +// - tracepoint: A predetermined hook point in the kernel. Exposed as trace +// events in (sub)directories under /events. Cannot be closed or +// removed, they are static. +// - k(ret)probe: Ephemeral trace events based on entry or exit points of +// exported kernel symbols. kprobe-based (tracefs) trace events can be +// created system-wide by writing to the /kprobe_events file, or +// they can be scoped to the current process by creating PMU perf events. +// - u(ret)probe: Ephemeral trace events based on user provides ELF binaries +// and offsets. uprobe-based (tracefs) trace events can be +// created system-wide by writing to the /uprobe_events file, or +// they can be scoped to the current process by creating PMU perf events. +// - perf event: An object instantiated based on an existing trace event or +// kernel symbol. Referred to by fd in userspace. +// Exactly one eBPF program can be attached to a perf event. Multiple perf +// events can be created from a single trace event. Closing a perf event +// stops any further invocations of the attached eBPF program. + +var ( + tracefsPath = "/sys/kernel/debug/tracing" + + // Trace event groups, names and kernel symbols must adhere to this set + // of characters. Non-empty, first character must not be a number, all + // characters must be alphanumeric or underscore. + rgxTraceEvent = regexp.MustCompile("^[a-zA-Z_][0-9a-zA-Z_]*$") + + errInvalidInput = errors.New("invalid input") +) + +const ( + perfAllThreads = -1 +) + +type perfEventType uint8 + +const ( + tracepointEvent perfEventType = iota + kprobeEvent + kretprobeEvent + uprobeEvent + uretprobeEvent +) + +// A perfEvent represents a perf event kernel object. Exactly one eBPF program +// can be attached to it. It is created based on a tracefs trace event or a +// Performance Monitoring Unit (PMU). +type perfEvent struct { + + // Group and name of the tracepoint/kprobe/uprobe. + group string + name string + + // PMU event ID read from sysfs. Valid IDs are non-zero. + pmuID uint64 + // ID of the trace event read from tracefs. Valid IDs are non-zero. + tracefsID uint64 + + // The event type determines the types of programs that can be attached. + typ perfEventType + + fd *internal.FD +} + +func (pe *perfEvent) isLink() {} + +func (pe *perfEvent) Pin(string) error { + return fmt.Errorf("pin perf event: %w", ErrNotSupported) +} + +func (pe *perfEvent) Unpin() error { + return fmt.Errorf("unpin perf event: %w", ErrNotSupported) +} + +// Since 4.15 (e87c6bc3852b "bpf: permit multiple bpf attachments for a single perf event"), +// calling PERF_EVENT_IOC_SET_BPF appends the given program to a prog_array +// owned by the perf event, which means multiple programs can be attached +// simultaneously. +// +// Before 4.15, calling PERF_EVENT_IOC_SET_BPF more than once on a perf event +// returns EEXIST. +// +// Detaching a program from a perf event is currently not possible, so a +// program replacement mechanism cannot be implemented for perf events. +func (pe *perfEvent) Update(prog *ebpf.Program) error { + return fmt.Errorf("can't replace eBPF program in perf event: %w", ErrNotSupported) +} + +func (pe *perfEvent) Close() error { + if pe.fd == nil { + return nil + } + + pfd, err := pe.fd.Value() + if err != nil { + return fmt.Errorf("getting perf event fd: %w", err) + } + + err = unix.IoctlSetInt(int(pfd), unix.PERF_EVENT_IOC_DISABLE, 0) + if err != nil { + return fmt.Errorf("disabling perf event: %w", err) + } + + err = pe.fd.Close() + if err != nil { + return fmt.Errorf("closing perf event fd: %w", err) + } + + switch pe.typ { + case kprobeEvent, kretprobeEvent: + // Clean up kprobe tracefs entry. + if pe.tracefsID != 0 { + return closeTraceFSProbeEvent(kprobeType, pe.group, pe.name) + } + case uprobeEvent, uretprobeEvent: + // Clean up uprobe tracefs entry. + if pe.tracefsID != 0 { + return closeTraceFSProbeEvent(uprobeType, pe.group, pe.name) + } + case tracepointEvent: + // Tracepoint trace events don't hold any extra resources. + return nil + } + + return nil +} + +// attach the given eBPF prog to the perf event stored in pe. +// pe must contain a valid perf event fd. +// prog's type must match the program type stored in pe. +func (pe *perfEvent) attach(prog *ebpf.Program) error { + if prog == nil { + return errors.New("cannot attach a nil program") + } + if pe.fd == nil { + return errors.New("cannot attach to nil perf event") + } + if prog.FD() < 0 { + return fmt.Errorf("invalid program: %w", internal.ErrClosedFd) + } + switch pe.typ { + case kprobeEvent, kretprobeEvent, uprobeEvent, uretprobeEvent: + if t := prog.Type(); t != ebpf.Kprobe { + return fmt.Errorf("invalid program type (expected %s): %s", ebpf.Kprobe, t) + } + case tracepointEvent: + if t := prog.Type(); t != ebpf.TracePoint { + return fmt.Errorf("invalid program type (expected %s): %s", ebpf.TracePoint, t) + } + default: + return fmt.Errorf("unknown perf event type: %d", pe.typ) + } + + // The ioctl below will fail when the fd is invalid. + kfd, _ := pe.fd.Value() + + // Assign the eBPF program to the perf event. + err := unix.IoctlSetInt(int(kfd), unix.PERF_EVENT_IOC_SET_BPF, prog.FD()) + if err != nil { + return fmt.Errorf("setting perf event bpf program: %w", err) + } + + // PERF_EVENT_IOC_ENABLE and _DISABLE ignore their given values. + if err := unix.IoctlSetInt(int(kfd), unix.PERF_EVENT_IOC_ENABLE, 0); err != nil { + return fmt.Errorf("enable perf event: %s", err) + } + + // Close the perf event when its reference is lost to avoid leaking system resources. + runtime.SetFinalizer(pe, (*perfEvent).Close) + return nil +} + +// unsafeStringPtr returns an unsafe.Pointer to a NUL-terminated copy of str. +func unsafeStringPtr(str string) (unsafe.Pointer, error) { + p, err := unix.BytePtrFromString(str) + if err != nil { + return nil, err + } + return unsafe.Pointer(p), nil +} + +// getTraceEventID reads a trace event's ID from tracefs given its group and name. +// group and name must be alphanumeric or underscore, as required by the kernel. +func getTraceEventID(group, name string) (uint64, error) { + tid, err := uint64FromFile(tracefsPath, "events", group, name, "id") + if errors.Is(err, os.ErrNotExist) { + return 0, fmt.Errorf("trace event %s/%s: %w", group, name, os.ErrNotExist) + } + if err != nil { + return 0, fmt.Errorf("reading trace event ID of %s/%s: %w", group, name, err) + } + + return tid, nil +} + +// getPMUEventType reads a Performance Monitoring Unit's type (numeric identifier) +// from /sys/bus/event_source/devices//type. +// +// Returns ErrNotSupported if the pmu type is not supported. +func getPMUEventType(typ probeType) (uint64, error) { + et, err := uint64FromFile("/sys/bus/event_source/devices", typ.String(), "type") + if errors.Is(err, os.ErrNotExist) { + return 0, fmt.Errorf("pmu type %s: %w", typ, ErrNotSupported) + } + if err != nil { + return 0, fmt.Errorf("reading pmu type %s: %w", typ, err) + } + + return et, nil +} + +// openTracepointPerfEvent opens a tracepoint-type perf event. System-wide +// [k,u]probes created by writing to /[k,u]probe_events are tracepoints +// behind the scenes, and can be attached to using these perf events. +func openTracepointPerfEvent(tid uint64) (*internal.FD, error) { + attr := unix.PerfEventAttr{ + Type: unix.PERF_TYPE_TRACEPOINT, + Config: tid, + Sample_type: unix.PERF_SAMPLE_RAW, + Sample: 1, + Wakeup: 1, + } + + fd, err := unix.PerfEventOpen(&attr, perfAllThreads, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) + if err != nil { + return nil, fmt.Errorf("opening tracepoint perf event: %w", err) + } + + return internal.NewFD(uint32(fd)), nil +} + +// uint64FromFile reads a uint64 from a file. All elements of path are sanitized +// and joined onto base. Returns error if base no longer prefixes the path after +// joining all components. +func uint64FromFile(base string, path ...string) (uint64, error) { + l := filepath.Join(path...) + p := filepath.Join(base, l) + if !strings.HasPrefix(p, base) { + return 0, fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput) + } + + data, err := ioutil.ReadFile(p) + if err != nil { + return 0, fmt.Errorf("reading file %s: %w", p, err) + } + + et := bytes.TrimSpace(data) + return strconv.ParseUint(string(et), 10, 64) +} diff --git a/vendor/github.com/cilium/ebpf/link/platform.go b/vendor/github.com/cilium/ebpf/link/platform.go new file mode 100644 index 0000000000000..eb6f7b7a37690 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/platform.go @@ -0,0 +1,25 @@ +package link + +import ( + "fmt" + "runtime" +) + +func platformPrefix(symbol string) string { + + prefix := runtime.GOARCH + + // per https://github.com/golang/go/blob/master/src/go/build/syslist.go + switch prefix { + case "386": + prefix = "ia32" + case "amd64", "amd64p32": + prefix = "x64" + case "arm64", "arm64be": + prefix = "arm64" + default: + return symbol + } + + return fmt.Sprintf("__%s_%s", prefix, symbol) +} diff --git a/vendor/github.com/cilium/ebpf/link/program.go b/vendor/github.com/cilium/ebpf/link/program.go new file mode 100644 index 0000000000000..b90c4574676e8 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/program.go @@ -0,0 +1,76 @@ +package link + +import ( + "fmt" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal" +) + +type RawAttachProgramOptions struct { + // File descriptor to attach to. This differs for each attach type. + Target int + // Program to attach. + Program *ebpf.Program + // Program to replace (cgroups). + Replace *ebpf.Program + // Attach must match the attach type of Program (and Replace). + Attach ebpf.AttachType + // Flags control the attach behaviour. This differs for each attach type. + Flags uint32 +} + +// RawAttachProgram is a low level wrapper around BPF_PROG_ATTACH. +// +// You should use one of the higher level abstractions available in this +// package if possible. +func RawAttachProgram(opts RawAttachProgramOptions) error { + if err := haveProgAttach(); err != nil { + return err + } + + var replaceFd uint32 + if opts.Replace != nil { + replaceFd = uint32(opts.Replace.FD()) + } + + attr := internal.BPFProgAttachAttr{ + TargetFd: uint32(opts.Target), + AttachBpfFd: uint32(opts.Program.FD()), + ReplaceBpfFd: replaceFd, + AttachType: uint32(opts.Attach), + AttachFlags: uint32(opts.Flags), + } + + if err := internal.BPFProgAttach(&attr); err != nil { + return fmt.Errorf("can't attach program: %w", err) + } + return nil +} + +type RawDetachProgramOptions struct { + Target int + Program *ebpf.Program + Attach ebpf.AttachType +} + +// RawDetachProgram is a low level wrapper around BPF_PROG_DETACH. +// +// You should use one of the higher level abstractions available in this +// package if possible. +func RawDetachProgram(opts RawDetachProgramOptions) error { + if err := haveProgAttach(); err != nil { + return err + } + + attr := internal.BPFProgDetachAttr{ + TargetFd: uint32(opts.Target), + AttachBpfFd: uint32(opts.Program.FD()), + AttachType: uint32(opts.Attach), + } + if err := internal.BPFProgDetach(&attr); err != nil { + return fmt.Errorf("can't detach program: %w", err) + } + + return nil +} diff --git a/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go b/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go new file mode 100644 index 0000000000000..f4beb1e07863f --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/raw_tracepoint.go @@ -0,0 +1,61 @@ +package link + +import ( + "fmt" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal" +) + +type RawTracepointOptions struct { + // Tracepoint name. + Name string + // Program must be of type RawTracepoint* + Program *ebpf.Program +} + +// AttachRawTracepoint links a BPF program to a raw_tracepoint. +// +// Requires at least Linux 4.17. +func AttachRawTracepoint(opts RawTracepointOptions) (Link, error) { + if t := opts.Program.Type(); t != ebpf.RawTracepoint && t != ebpf.RawTracepointWritable { + return nil, fmt.Errorf("invalid program type %s, expected RawTracepoint(Writable)", t) + } + if opts.Program.FD() < 0 { + return nil, fmt.Errorf("invalid program: %w", internal.ErrClosedFd) + } + + fd, err := bpfRawTracepointOpen(&bpfRawTracepointOpenAttr{ + name: internal.NewStringPointer(opts.Name), + fd: uint32(opts.Program.FD()), + }) + if err != nil { + return nil, err + } + + return &progAttachRawTracepoint{fd: fd}, nil +} + +type progAttachRawTracepoint struct { + fd *internal.FD +} + +var _ Link = (*progAttachRawTracepoint)(nil) + +func (rt *progAttachRawTracepoint) isLink() {} + +func (rt *progAttachRawTracepoint) Close() error { + return rt.fd.Close() +} + +func (rt *progAttachRawTracepoint) Update(_ *ebpf.Program) error { + return fmt.Errorf("can't update raw_tracepoint: %w", ErrNotSupported) +} + +func (rt *progAttachRawTracepoint) Pin(_ string) error { + return fmt.Errorf("can't pin raw_tracepoint: %w", ErrNotSupported) +} + +func (rt *progAttachRawTracepoint) Unpin() error { + return fmt.Errorf("unpin raw_tracepoint: %w", ErrNotSupported) +} diff --git a/vendor/github.com/cilium/ebpf/link/syscalls.go b/vendor/github.com/cilium/ebpf/link/syscalls.go new file mode 100644 index 0000000000000..30e8a88050723 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/syscalls.go @@ -0,0 +1,190 @@ +package link + +import ( + "errors" + "unsafe" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/unix" +) + +// Type is the kind of link. +type Type uint32 + +// Valid link types. +// +// Equivalent to enum bpf_link_type. +const ( + UnspecifiedType Type = iota + RawTracepointType + TracingType + CgroupType + IterType + NetNsType + XDPType +) + +var haveProgAttach = internal.FeatureTest("BPF_PROG_ATTACH", "4.10", func() error { + prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ + Type: ebpf.CGroupSKB, + AttachType: ebpf.AttachCGroupInetIngress, + License: "MIT", + Instructions: asm.Instructions{ + asm.Mov.Imm(asm.R0, 0), + asm.Return(), + }, + }) + if err != nil { + return internal.ErrNotSupported + } + + // BPF_PROG_ATTACH was introduced at the same time as CGgroupSKB, + // so being able to load the program is enough to infer that we + // have the syscall. + prog.Close() + return nil +}) + +var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replacement", "5.5", func() error { + if err := haveProgAttach(); err != nil { + return err + } + + prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ + Type: ebpf.CGroupSKB, + AttachType: ebpf.AttachCGroupInetIngress, + License: "MIT", + Instructions: asm.Instructions{ + asm.Mov.Imm(asm.R0, 0), + asm.Return(), + }, + }) + if err != nil { + return internal.ErrNotSupported + } + defer prog.Close() + + // We know that we have BPF_PROG_ATTACH since we can load CGroupSKB programs. + // If passing BPF_F_REPLACE gives us EINVAL we know that the feature isn't + // present. + attr := internal.BPFProgAttachAttr{ + // We rely on this being checked after attachFlags. + TargetFd: ^uint32(0), + AttachBpfFd: uint32(prog.FD()), + AttachType: uint32(ebpf.AttachCGroupInetIngress), + AttachFlags: uint32(flagReplace), + } + + err = internal.BPFProgAttach(&attr) + if errors.Is(err, unix.EINVAL) { + return internal.ErrNotSupported + } + if errors.Is(err, unix.EBADF) { + return nil + } + return err +}) + +type bpfLinkCreateAttr struct { + progFd uint32 + targetFd uint32 + attachType ebpf.AttachType + flags uint32 +} + +func bpfLinkCreate(attr *bpfLinkCreateAttr) (*internal.FD, error) { + ptr, err := internal.BPF(internal.BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err != nil { + return nil, err + } + return internal.NewFD(uint32(ptr)), nil +} + +type bpfLinkCreateIterAttr struct { + prog_fd uint32 + target_fd uint32 + attach_type ebpf.AttachType + flags uint32 + iter_info internal.Pointer + iter_info_len uint32 +} + +func bpfLinkCreateIter(attr *bpfLinkCreateIterAttr) (*internal.FD, error) { + ptr, err := internal.BPF(internal.BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err != nil { + return nil, err + } + return internal.NewFD(uint32(ptr)), nil +} + +type bpfLinkUpdateAttr struct { + linkFd uint32 + newProgFd uint32 + flags uint32 + oldProgFd uint32 +} + +func bpfLinkUpdate(attr *bpfLinkUpdateAttr) error { + _, err := internal.BPF(internal.BPF_LINK_UPDATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + return err +} + +var haveBPFLink = internal.FeatureTest("bpf_link", "5.7", func() error { + prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ + Type: ebpf.CGroupSKB, + AttachType: ebpf.AttachCGroupInetIngress, + License: "MIT", + Instructions: asm.Instructions{ + asm.Mov.Imm(asm.R0, 0), + asm.Return(), + }, + }) + if err != nil { + return internal.ErrNotSupported + } + defer prog.Close() + + attr := bpfLinkCreateAttr{ + // This is a hopefully invalid file descriptor, which triggers EBADF. + targetFd: ^uint32(0), + progFd: uint32(prog.FD()), + attachType: ebpf.AttachCGroupInetIngress, + } + _, err = bpfLinkCreate(&attr) + if errors.Is(err, unix.EINVAL) { + return internal.ErrNotSupported + } + if errors.Is(err, unix.EBADF) { + return nil + } + return err +}) + +type bpfIterCreateAttr struct { + linkFd uint32 + flags uint32 +} + +func bpfIterCreate(attr *bpfIterCreateAttr) (*internal.FD, error) { + ptr, err := internal.BPF(internal.BPF_ITER_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err == nil { + return internal.NewFD(uint32(ptr)), nil + } + return nil, err +} + +type bpfRawTracepointOpenAttr struct { + name internal.Pointer + fd uint32 + _ uint32 +} + +func bpfRawTracepointOpen(attr *bpfRawTracepointOpenAttr) (*internal.FD, error) { + ptr, err := internal.BPF(internal.BPF_RAW_TRACEPOINT_OPEN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err == nil { + return internal.NewFD(uint32(ptr)), nil + } + return nil, err +} diff --git a/vendor/github.com/cilium/ebpf/link/tracepoint.go b/vendor/github.com/cilium/ebpf/link/tracepoint.go new file mode 100644 index 0000000000000..b8ae04bf0a058 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/tracepoint.go @@ -0,0 +1,56 @@ +package link + +import ( + "fmt" + + "github.com/cilium/ebpf" +) + +// Tracepoint attaches the given eBPF program to the tracepoint with the given +// group and name. See /sys/kernel/debug/tracing/events to find available +// tracepoints. The top-level directory is the group, the event's subdirectory +// is the name. Example: +// +// Tracepoint("syscalls", "sys_enter_fork", prog) +// +// Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is +// only possible as of kernel 4.14 (commit cf5f5ce). +func Tracepoint(group, name string, prog *ebpf.Program) (Link, error) { + if group == "" || name == "" { + return nil, fmt.Errorf("group and name cannot be empty: %w", errInvalidInput) + } + if prog == nil { + return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) + } + if !rgxTraceEvent.MatchString(group) || !rgxTraceEvent.MatchString(name) { + return nil, fmt.Errorf("group and name '%s/%s' must be alphanumeric or underscore: %w", group, name, errInvalidInput) + } + if prog.Type() != ebpf.TracePoint { + return nil, fmt.Errorf("eBPF program type %s is not a Tracepoint: %w", prog.Type(), errInvalidInput) + } + + tid, err := getTraceEventID(group, name) + if err != nil { + return nil, err + } + + fd, err := openTracepointPerfEvent(tid) + if err != nil { + return nil, err + } + + pe := &perfEvent{ + fd: fd, + tracefsID: tid, + group: group, + name: name, + typ: tracepointEvent, + } + + if err := pe.attach(prog); err != nil { + pe.Close() + return nil, err + } + + return pe, nil +} diff --git a/vendor/github.com/cilium/ebpf/link/uprobe.go b/vendor/github.com/cilium/ebpf/link/uprobe.go new file mode 100644 index 0000000000000..2bc395ee3cd2e --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/uprobe.go @@ -0,0 +1,237 @@ +package link + +import ( + "debug/elf" + "errors" + "fmt" + "os" + "path/filepath" + "regexp" + "sync" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal" +) + +var ( + uprobeEventsPath = filepath.Join(tracefsPath, "uprobe_events") + + // rgxUprobeSymbol is used to strip invalid characters from the uprobe symbol + // as they are not allowed to be used as the EVENT token in tracefs. + rgxUprobeSymbol = regexp.MustCompile("[^a-zA-Z0-9]+") + + uprobeRetprobeBit = struct { + once sync.Once + value uint64 + err error + }{} +) + +// Executable defines an executable program on the filesystem. +type Executable struct { + // Path of the executable on the filesystem. + path string + // Parsed ELF symbols and dynamic symbols. + symbols map[string]elf.Symbol +} + +// UprobeOptions defines additional parameters that will be used +// when loading Uprobes. +type UprobeOptions struct { + // Symbol offset. Must be provided in case of external symbols (shared libs). + // If set, overrides the offset eventually parsed from the executable. + Offset uint64 +} + +// To open a new Executable, use: +// +// OpenExecutable("/bin/bash") +// +// The returned value can then be used to open Uprobe(s). +func OpenExecutable(path string) (*Executable, error) { + if path == "" { + return nil, fmt.Errorf("path cannot be empty") + } + + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("open file '%s': %w", path, err) + } + defer f.Close() + + se, err := internal.NewSafeELFFile(f) + if err != nil { + return nil, fmt.Errorf("parse ELF file: %w", err) + } + + var ex = Executable{ + path: path, + symbols: make(map[string]elf.Symbol), + } + if err := ex.addSymbols(se.Symbols); err != nil { + return nil, err + } + + if err := ex.addSymbols(se.DynamicSymbols); err != nil { + return nil, err + } + + return &ex, nil +} + +func (ex *Executable) addSymbols(f func() ([]elf.Symbol, error)) error { + // elf.Symbols and elf.DynamicSymbols return ErrNoSymbols if the section is not found. + syms, err := f() + if err != nil && !errors.Is(err, elf.ErrNoSymbols) { + return err + } + for _, s := range syms { + if elf.ST_TYPE(s.Info) != elf.STT_FUNC { + // Symbol not associated with a function or other executable code. + continue + } + ex.symbols[s.Name] = s + } + return nil +} + +func (ex *Executable) symbol(symbol string) (*elf.Symbol, error) { + if s, ok := ex.symbols[symbol]; ok { + return &s, nil + } + return nil, fmt.Errorf("symbol %s not found", symbol) +} + +// Uprobe attaches the given eBPF program to a perf event that fires when the +// given symbol starts executing in the given Executable. +// For example, /bin/bash::main(): +// +// ex, _ = OpenExecutable("/bin/bash") +// ex.Uprobe("main", prog, nil) +// +// When using symbols which belongs to shared libraries, +// an offset must be provided via options: +// +// ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123}) +// +// The resulting Link must be Closed during program shutdown to avoid leaking +// system resources. Functions provided by shared libraries can currently not +// be traced and will result in an ErrNotSupported. +func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) { + u, err := ex.uprobe(symbol, prog, opts, false) + if err != nil { + return nil, err + } + + err = u.attach(prog) + if err != nil { + u.Close() + return nil, err + } + + return u, nil +} + +// Uretprobe attaches the given eBPF program to a perf event that fires right +// before the given symbol exits. For example, /bin/bash::main(): +// +// ex, _ = OpenExecutable("/bin/bash") +// ex.Uretprobe("main", prog, nil) +// +// When using symbols which belongs to shared libraries, +// an offset must be provided via options: +// +// ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123}) +// +// The resulting Link must be Closed during program shutdown to avoid leaking +// system resources. Functions provided by shared libraries can currently not +// be traced and will result in an ErrNotSupported. +func (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) { + u, err := ex.uprobe(symbol, prog, opts, true) + if err != nil { + return nil, err + } + + err = u.attach(prog) + if err != nil { + u.Close() + return nil, err + } + + return u, nil +} + +// uprobe opens a perf event for the given binary/symbol and attaches prog to it. +// If ret is true, create a uretprobe. +func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions, ret bool) (*perfEvent, error) { + if prog == nil { + return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) + } + if prog.Type() != ebpf.Kprobe { + return nil, fmt.Errorf("eBPF program type %s is not Kprobe: %w", prog.Type(), errInvalidInput) + } + + var offset uint64 + if opts != nil && opts.Offset != 0 { + offset = opts.Offset + } else { + sym, err := ex.symbol(symbol) + if err != nil { + return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, err) + } + + // Symbols with location 0 from section undef are shared library calls and + // are relocated before the binary is executed. Dynamic linking is not + // implemented by the library, so mark this as unsupported for now. + if sym.Section == elf.SHN_UNDEF && sym.Value == 0 { + return nil, fmt.Errorf("cannot resolve %s library call '%s', "+ + "consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported) + } + + offset = sym.Value + } + + // Use uprobe PMU if the kernel has it available. + tp, err := pmuUprobe(symbol, ex.path, offset, ret) + if err == nil { + return tp, nil + } + if err != nil && !errors.Is(err, ErrNotSupported) { + return nil, fmt.Errorf("creating perf_uprobe PMU: %w", err) + } + + // Use tracefs if uprobe PMU is missing. + tp, err = tracefsUprobe(uprobeSanitizedSymbol(symbol), ex.path, offset, ret) + if err != nil { + return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err) + } + + return tp, nil +} + +// pmuUprobe opens a perf event based on the uprobe PMU. +func pmuUprobe(symbol, path string, offset uint64, ret bool) (*perfEvent, error) { + return pmuProbe(uprobeType, symbol, path, offset, ret) +} + +// tracefsUprobe creates a Uprobe tracefs entry. +func tracefsUprobe(symbol, path string, offset uint64, ret bool) (*perfEvent, error) { + return tracefsProbe(uprobeType, symbol, path, offset, ret) +} + +// uprobeSanitizedSymbol replaces every invalid characted for the tracefs api with an underscore. +func uprobeSanitizedSymbol(symbol string) string { + return rgxUprobeSymbol.ReplaceAllString(symbol, "_") +} + +// uprobePathOffset creates the PATH:OFFSET token for the tracefs api. +func uprobePathOffset(path string, offset uint64) string { + return fmt.Sprintf("%s:%#x", path, offset) +} + +func uretprobeBit() (uint64, error) { + uprobeRetprobeBit.once.Do(func() { + uprobeRetprobeBit.value, uprobeRetprobeBit.err = determineRetprobeBit(uprobeType) + }) + return uprobeRetprobeBit.value, uprobeRetprobeBit.err +} diff --git a/vendor/github.com/cilium/ebpf/linker.go b/vendor/github.com/cilium/ebpf/linker.go new file mode 100644 index 0000000000000..6c2efef9e42c8 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/linker.go @@ -0,0 +1,140 @@ +package ebpf + +import ( + "fmt" + + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal/btf" +) + +// link resolves bpf-to-bpf calls. +// +// Each library may contain multiple functions / labels, and is only linked +// if prog references one of these functions. +// +// Libraries also linked. +func link(prog *ProgramSpec, libs []*ProgramSpec) error { + var ( + linked = make(map[*ProgramSpec]bool) + pending = []asm.Instructions{prog.Instructions} + insns asm.Instructions + ) + for len(pending) > 0 { + insns, pending = pending[0], pending[1:] + for _, lib := range libs { + if linked[lib] { + continue + } + + needed, err := needSection(insns, lib.Instructions) + if err != nil { + return fmt.Errorf("linking %s: %w", lib.Name, err) + } + + if !needed { + continue + } + + linked[lib] = true + prog.Instructions = append(prog.Instructions, lib.Instructions...) + pending = append(pending, lib.Instructions) + + if prog.BTF != nil && lib.BTF != nil { + if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil { + return fmt.Errorf("linking BTF of %s: %w", lib.Name, err) + } + } + } + } + + return nil +} + +func needSection(insns, section asm.Instructions) (bool, error) { + // A map of symbols to the libraries which contain them. + symbols, err := section.SymbolOffsets() + if err != nil { + return false, err + } + + for _, ins := range insns { + if ins.Reference == "" { + continue + } + + if ins.OpCode.JumpOp() != asm.Call || ins.Src != asm.PseudoCall { + continue + } + + if ins.Constant != -1 { + // This is already a valid call, no need to link again. + continue + } + + if _, ok := symbols[ins.Reference]; !ok { + // Symbol isn't available in this section + continue + } + + // At this point we know that at least one function in the + // library is called from insns, so we have to link it. + return true, nil + } + + // None of the functions in the section are called. + return false, nil +} + +func fixupJumpsAndCalls(insns asm.Instructions) error { + symbolOffsets := make(map[string]asm.RawInstructionOffset) + iter := insns.Iterate() + for iter.Next() { + ins := iter.Ins + + if ins.Symbol == "" { + continue + } + + if _, ok := symbolOffsets[ins.Symbol]; ok { + return fmt.Errorf("duplicate symbol %s", ins.Symbol) + } + + symbolOffsets[ins.Symbol] = iter.Offset + } + + iter = insns.Iterate() + for iter.Next() { + i := iter.Index + offset := iter.Offset + ins := iter.Ins + + if ins.Reference == "" { + continue + } + + switch { + case ins.IsFunctionCall() && ins.Constant == -1: + // Rewrite bpf to bpf call + callOffset, ok := symbolOffsets[ins.Reference] + if !ok { + return fmt.Errorf("call at %d: reference to missing symbol %q", i, ins.Reference) + } + + ins.Constant = int64(callOffset - offset - 1) + + case ins.OpCode.Class() == asm.JumpClass && ins.Offset == -1: + // Rewrite jump to label + jumpOffset, ok := symbolOffsets[ins.Reference] + if !ok { + return fmt.Errorf("jump at %d: reference to missing symbol %q", i, ins.Reference) + } + + ins.Offset = int16(jumpOffset - offset - 1) + + case ins.IsLoadFromMap() && ins.MapPtr() == -1: + return fmt.Errorf("map %s: %w", ins.Reference, errUnsatisfiedReference) + } + } + + return nil +} diff --git a/vendor/github.com/cilium/ebpf/map.go b/vendor/github.com/cilium/ebpf/map.go new file mode 100644 index 0000000000000..f257d88c03e3c --- /dev/null +++ b/vendor/github.com/cilium/ebpf/map.go @@ -0,0 +1,1232 @@ +package ebpf + +import ( + "errors" + "fmt" + "io" + "path/filepath" + "reflect" + "strings" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" + "github.com/cilium/ebpf/internal/unix" +) + +// Errors returned by Map and MapIterator methods. +var ( + ErrKeyNotExist = errors.New("key does not exist") + ErrKeyExist = errors.New("key already exists") + ErrIterationAborted = errors.New("iteration aborted") + ErrMapIncompatible = errors.New("map's spec is incompatible with pinned map") +) + +// MapOptions control loading a map into the kernel. +type MapOptions struct { + // The base path to pin maps in if requested via PinByName. + // Existing maps will be re-used if they are compatible, otherwise an + // error is returned. + PinPath string + LoadPinOptions LoadPinOptions +} + +// MapID represents the unique ID of an eBPF map +type MapID uint32 + +// MapSpec defines a Map. +type MapSpec struct { + // Name is passed to the kernel as a debug aid. Must only contain + // alpha numeric and '_' characters. + Name string + Type MapType + KeySize uint32 + ValueSize uint32 + MaxEntries uint32 + + // Flags is passed to the kernel and specifies additional map + // creation attributes. + Flags uint32 + + // Automatically pin and load a map from MapOptions.PinPath. + // Generates an error if an existing pinned map is incompatible with the MapSpec. + Pinning PinType + + // Specify numa node during map creation + // (effective only if unix.BPF_F_NUMA_NODE flag is set, + // which can be imported from golang.org/x/sys/unix) + NumaNode uint32 + + // The initial contents of the map. May be nil. + Contents []MapKV + + // Whether to freeze a map after setting its initial contents. + Freeze bool + + // InnerMap is used as a template for ArrayOfMaps and HashOfMaps + InnerMap *MapSpec + + // The BTF associated with this map. + BTF *btf.Map +} + +func (ms *MapSpec) String() string { + return fmt.Sprintf("%s(keySize=%d, valueSize=%d, maxEntries=%d, flags=%d)", ms.Type, ms.KeySize, ms.ValueSize, ms.MaxEntries, ms.Flags) +} + +// Copy returns a copy of the spec. +// +// MapSpec.Contents is a shallow copy. +func (ms *MapSpec) Copy() *MapSpec { + if ms == nil { + return nil + } + + cpy := *ms + cpy.Contents = make([]MapKV, len(ms.Contents)) + copy(cpy.Contents, ms.Contents) + cpy.InnerMap = ms.InnerMap.Copy() + return &cpy +} + +func (ms *MapSpec) clampPerfEventArraySize() error { + if ms.Type != PerfEventArray { + return nil + } + + n, err := internal.PossibleCPUs() + if err != nil { + return fmt.Errorf("perf event array: %w", err) + } + + if n := uint32(n); ms.MaxEntries > n { + ms.MaxEntries = n + } + + return nil +} + +// MapKV is used to initialize the contents of a Map. +type MapKV struct { + Key interface{} + Value interface{} +} + +func (ms *MapSpec) checkCompatibility(m *Map) error { + switch { + case m.typ != ms.Type: + return fmt.Errorf("expected type %v, got %v: %w", ms.Type, m.typ, ErrMapIncompatible) + + case m.keySize != ms.KeySize: + return fmt.Errorf("expected key size %v, got %v: %w", ms.KeySize, m.keySize, ErrMapIncompatible) + + case m.valueSize != ms.ValueSize: + return fmt.Errorf("expected value size %v, got %v: %w", ms.ValueSize, m.valueSize, ErrMapIncompatible) + + case m.maxEntries != ms.MaxEntries: + return fmt.Errorf("expected max entries %v, got %v: %w", ms.MaxEntries, m.maxEntries, ErrMapIncompatible) + + case m.flags != ms.Flags: + return fmt.Errorf("expected flags %v, got %v: %w", ms.Flags, m.flags, ErrMapIncompatible) + } + return nil +} + +// Map represents a Map file descriptor. +// +// It is not safe to close a map which is used by other goroutines. +// +// Methods which take interface{} arguments by default encode +// them using binary.Read/Write in the machine's native endianness. +// +// Implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler +// if you require custom encoding. +type Map struct { + name string + fd *internal.FD + typ MapType + keySize uint32 + valueSize uint32 + maxEntries uint32 + flags uint32 + pinnedPath string + // Per CPU maps return values larger than the size in the spec + fullValueSize int +} + +// NewMapFromFD creates a map from a raw fd. +// +// You should not use fd after calling this function. +func NewMapFromFD(fd int) (*Map, error) { + if fd < 0 { + return nil, errors.New("invalid fd") + } + + return newMapFromFD(internal.NewFD(uint32(fd))) +} + +func newMapFromFD(fd *internal.FD) (*Map, error) { + info, err := newMapInfoFromFd(fd) + if err != nil { + fd.Close() + return nil, fmt.Errorf("get map info: %s", err) + } + + return newMap(fd, info.Name, info.Type, info.KeySize, info.ValueSize, info.MaxEntries, info.Flags) +} + +// NewMap creates a new Map. +// +// It's equivalent to calling NewMapWithOptions with default options. +func NewMap(spec *MapSpec) (*Map, error) { + return NewMapWithOptions(spec, MapOptions{}) +} + +// NewMapWithOptions creates a new Map. +// +// Creating a map for the first time will perform feature detection +// by creating small, temporary maps. +// +// The caller is responsible for ensuring the process' rlimit is set +// sufficiently high for locking memory during map creation. This can be done +// by calling unix.Setrlimit with unix.RLIMIT_MEMLOCK prior to calling NewMapWithOptions. +// +// May return an error wrapping ErrMapIncompatible. +func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) { + handles := newHandleCache() + defer handles.close() + + return newMapWithOptions(spec, opts, handles) +} + +func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ *Map, err error) { + closeOnError := func(c io.Closer) { + if err != nil { + c.Close() + } + } + + switch spec.Pinning { + case PinByName: + if spec.Name == "" || opts.PinPath == "" { + return nil, fmt.Errorf("pin by name: missing Name or PinPath") + } + + path := filepath.Join(opts.PinPath, spec.Name) + m, err := LoadPinnedMap(path, &opts.LoadPinOptions) + if errors.Is(err, unix.ENOENT) { + break + } + if err != nil { + return nil, fmt.Errorf("load pinned map: %w", err) + } + defer closeOnError(m) + + if err := spec.checkCompatibility(m); err != nil { + return nil, fmt.Errorf("use pinned map %s: %w", spec.Name, err) + } + + return m, nil + + case PinNone: + // Nothing to do here + + default: + return nil, fmt.Errorf("pin type %d: %w", int(spec.Pinning), ErrNotSupported) + } + + var innerFd *internal.FD + if spec.Type == ArrayOfMaps || spec.Type == HashOfMaps { + if spec.InnerMap == nil { + return nil, fmt.Errorf("%s requires InnerMap", spec.Type) + } + + if spec.InnerMap.Pinning != PinNone { + return nil, errors.New("inner maps cannot be pinned") + } + + template, err := createMap(spec.InnerMap, nil, opts, handles) + if err != nil { + return nil, err + } + defer template.Close() + + innerFd = template.fd + } + + m, err := createMap(spec, innerFd, opts, handles) + if err != nil { + return nil, err + } + defer closeOnError(m) + + if spec.Pinning == PinByName { + path := filepath.Join(opts.PinPath, spec.Name) + if err := m.Pin(path); err != nil { + return nil, fmt.Errorf("pin map: %s", err) + } + } + + return m, nil +} + +func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) { + closeOnError := func(closer io.Closer) { + if err != nil { + closer.Close() + } + } + + spec = spec.Copy() + + switch spec.Type { + case ArrayOfMaps: + fallthrough + case HashOfMaps: + if err := haveNestedMaps(); err != nil { + return nil, err + } + + if spec.ValueSize != 0 && spec.ValueSize != 4 { + return nil, errors.New("ValueSize must be zero or four for map of map") + } + spec.ValueSize = 4 + + case PerfEventArray: + if spec.KeySize != 0 && spec.KeySize != 4 { + return nil, errors.New("KeySize must be zero or four for perf event array") + } + spec.KeySize = 4 + + if spec.ValueSize != 0 && spec.ValueSize != 4 { + return nil, errors.New("ValueSize must be zero or four for perf event array") + } + spec.ValueSize = 4 + + if spec.MaxEntries == 0 { + n, err := internal.PossibleCPUs() + if err != nil { + return nil, fmt.Errorf("perf event array: %w", err) + } + spec.MaxEntries = uint32(n) + } + } + + if spec.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze { + if err := haveMapMutabilityModifiers(); err != nil { + return nil, fmt.Errorf("map create: %w", err) + } + } + if spec.Flags&unix.BPF_F_MMAPABLE > 0 { + if err := haveMmapableMaps(); err != nil { + return nil, fmt.Errorf("map create: %w", err) + } + } + if spec.Flags&unix.BPF_F_INNER_MAP > 0 { + if err := haveInnerMaps(); err != nil { + return nil, fmt.Errorf("map create: %w", err) + } + } + + attr := internal.BPFMapCreateAttr{ + MapType: uint32(spec.Type), + KeySize: spec.KeySize, + ValueSize: spec.ValueSize, + MaxEntries: spec.MaxEntries, + Flags: spec.Flags, + NumaNode: spec.NumaNode, + } + + if inner != nil { + var err error + attr.InnerMapFd, err = inner.Value() + if err != nil { + return nil, fmt.Errorf("map create: %w", err) + } + } + + if haveObjName() == nil { + attr.MapName = internal.NewBPFObjName(spec.Name) + } + + var btfDisabled bool + if spec.BTF != nil { + handle, err := handles.btfHandle(btf.MapSpec(spec.BTF)) + btfDisabled = errors.Is(err, btf.ErrNotSupported) + if err != nil && !btfDisabled { + return nil, fmt.Errorf("load BTF: %w", err) + } + + if handle != nil { + attr.BTFFd = uint32(handle.FD()) + attr.BTFKeyTypeID = uint32(btf.MapKey(spec.BTF).ID()) + attr.BTFValueTypeID = uint32(btf.MapValue(spec.BTF).ID()) + } + } + + fd, err := internal.BPFMapCreate(&attr) + if err != nil { + if errors.Is(err, unix.EPERM) { + return nil, fmt.Errorf("map create: RLIMIT_MEMLOCK may be too low: %w", err) + } + if btfDisabled { + return nil, fmt.Errorf("map create without BTF: %w", err) + } + return nil, fmt.Errorf("map create: %w", err) + } + defer closeOnError(fd) + + m, err := newMap(fd, spec.Name, spec.Type, spec.KeySize, spec.ValueSize, spec.MaxEntries, spec.Flags) + if err != nil { + return nil, fmt.Errorf("map create: %w", err) + } + + if err := m.populate(spec.Contents); err != nil { + return nil, fmt.Errorf("map create: can't set initial contents: %w", err) + } + + if spec.Freeze { + if err := m.Freeze(); err != nil { + return nil, fmt.Errorf("can't freeze map: %w", err) + } + } + + return m, nil +} + +func newMap(fd *internal.FD, name string, typ MapType, keySize, valueSize, maxEntries, flags uint32) (*Map, error) { + m := &Map{ + name, + fd, + typ, + keySize, + valueSize, + maxEntries, + flags, + "", + int(valueSize), + } + + if !typ.hasPerCPUValue() { + return m, nil + } + + possibleCPUs, err := internal.PossibleCPUs() + if err != nil { + return nil, err + } + + m.fullValueSize = align(int(valueSize), 8) * possibleCPUs + return m, nil +} + +func (m *Map) String() string { + if m.name != "" { + return fmt.Sprintf("%s(%s)#%v", m.typ, m.name, m.fd) + } + return fmt.Sprintf("%s#%v", m.typ, m.fd) +} + +// Type returns the underlying type of the map. +func (m *Map) Type() MapType { + return m.typ +} + +// KeySize returns the size of the map key in bytes. +func (m *Map) KeySize() uint32 { + return m.keySize +} + +// ValueSize returns the size of the map value in bytes. +func (m *Map) ValueSize() uint32 { + return m.valueSize +} + +// MaxEntries returns the maximum number of elements the map can hold. +func (m *Map) MaxEntries() uint32 { + return m.maxEntries +} + +// Flags returns the flags of the map. +func (m *Map) Flags() uint32 { + return m.flags +} + +// Info returns metadata about the map. +func (m *Map) Info() (*MapInfo, error) { + return newMapInfoFromFd(m.fd) +} + +// Lookup retrieves a value from a Map. +// +// Calls Close() on valueOut if it is of type **Map or **Program, +// and *valueOut is not nil. +// +// Returns an error if the key doesn't exist, see ErrKeyNotExist. +func (m *Map) Lookup(key, valueOut interface{}) error { + valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize) + if err := m.lookup(key, valuePtr); err != nil { + return err + } + + return m.unmarshalValue(valueOut, valueBytes) +} + +// LookupAndDelete retrieves and deletes a value from a Map. +// +// Returns ErrKeyNotExist if the key doesn't exist. +func (m *Map) LookupAndDelete(key, valueOut interface{}) error { + valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize) + + keyPtr, err := m.marshalKey(key) + if err != nil { + return fmt.Errorf("can't marshal key: %w", err) + } + + if err := bpfMapLookupAndDelete(m.fd, keyPtr, valuePtr); err != nil { + return fmt.Errorf("lookup and delete failed: %w", err) + } + + return m.unmarshalValue(valueOut, valueBytes) +} + +// LookupBytes gets a value from Map. +// +// Returns a nil value if a key doesn't exist. +func (m *Map) LookupBytes(key interface{}) ([]byte, error) { + valueBytes := make([]byte, m.fullValueSize) + valuePtr := internal.NewSlicePointer(valueBytes) + + err := m.lookup(key, valuePtr) + if errors.Is(err, ErrKeyNotExist) { + return nil, nil + } + + return valueBytes, err +} + +func (m *Map) lookup(key interface{}, valueOut internal.Pointer) error { + keyPtr, err := m.marshalKey(key) + if err != nil { + return fmt.Errorf("can't marshal key: %w", err) + } + + if err = bpfMapLookupElem(m.fd, keyPtr, valueOut); err != nil { + return fmt.Errorf("lookup failed: %w", err) + } + return nil +} + +// MapUpdateFlags controls the behaviour of the Map.Update call. +// +// The exact semantics depend on the specific MapType. +type MapUpdateFlags uint64 + +const ( + // UpdateAny creates a new element or update an existing one. + UpdateAny MapUpdateFlags = iota + // UpdateNoExist creates a new element. + UpdateNoExist MapUpdateFlags = 1 << (iota - 1) + // UpdateExist updates an existing element. + UpdateExist +) + +// Put replaces or creates a value in map. +// +// It is equivalent to calling Update with UpdateAny. +func (m *Map) Put(key, value interface{}) error { + return m.Update(key, value, UpdateAny) +} + +// Update changes the value of a key. +func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error { + keyPtr, err := m.marshalKey(key) + if err != nil { + return fmt.Errorf("can't marshal key: %w", err) + } + + valuePtr, err := m.marshalValue(value) + if err != nil { + return fmt.Errorf("can't marshal value: %w", err) + } + + if err = bpfMapUpdateElem(m.fd, keyPtr, valuePtr, uint64(flags)); err != nil { + return fmt.Errorf("update failed: %w", err) + } + + return nil +} + +// Delete removes a value. +// +// Returns ErrKeyNotExist if the key does not exist. +func (m *Map) Delete(key interface{}) error { + keyPtr, err := m.marshalKey(key) + if err != nil { + return fmt.Errorf("can't marshal key: %w", err) + } + + if err = bpfMapDeleteElem(m.fd, keyPtr); err != nil { + return fmt.Errorf("delete failed: %w", err) + } + return nil +} + +// NextKey finds the key following an initial key. +// +// See NextKeyBytes for details. +// +// Returns ErrKeyNotExist if there is no next key. +func (m *Map) NextKey(key, nextKeyOut interface{}) error { + nextKeyPtr, nextKeyBytes := makeBuffer(nextKeyOut, int(m.keySize)) + + if err := m.nextKey(key, nextKeyPtr); err != nil { + return err + } + + if err := m.unmarshalKey(nextKeyOut, nextKeyBytes); err != nil { + return fmt.Errorf("can't unmarshal next key: %w", err) + } + return nil +} + +// NextKeyBytes returns the key following an initial key as a byte slice. +// +// Passing nil will return the first key. +// +// Use Iterate if you want to traverse all entries in the map. +// +// Returns nil if there are no more keys. +func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) { + nextKey := make([]byte, m.keySize) + nextKeyPtr := internal.NewSlicePointer(nextKey) + + err := m.nextKey(key, nextKeyPtr) + if errors.Is(err, ErrKeyNotExist) { + return nil, nil + } + + return nextKey, err +} + +func (m *Map) nextKey(key interface{}, nextKeyOut internal.Pointer) error { + var ( + keyPtr internal.Pointer + err error + ) + + if key != nil { + keyPtr, err = m.marshalKey(key) + if err != nil { + return fmt.Errorf("can't marshal key: %w", err) + } + } + + if err = bpfMapGetNextKey(m.fd, keyPtr, nextKeyOut); err != nil { + return fmt.Errorf("next key failed: %w", err) + } + return nil +} + +// BatchLookup looks up many elements in a map at once. +// +// "keysOut" and "valuesOut" must be of type slice, a pointer +// to a slice or buffer will not work. +// "prevKey" is the key to start the batch lookup from, it will +// *not* be included in the results. Use nil to start at the first key. +// +// ErrKeyNotExist is returned when the batch lookup has reached +// the end of all possible results, even when partial results +// are returned. It should be used to evaluate when lookup is "done". +func (m *Map) BatchLookup(prevKey, nextKeyOut, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { + return m.batchLookup(internal.BPF_MAP_LOOKUP_BATCH, prevKey, nextKeyOut, keysOut, valuesOut, opts) +} + +// BatchLookupAndDelete looks up many elements in a map at once, +// +// It then deletes all those elements. +// "keysOut" and "valuesOut" must be of type slice, a pointer +// to a slice or buffer will not work. +// "prevKey" is the key to start the batch lookup from, it will +// *not* be included in the results. Use nil to start at the first key. +// +// ErrKeyNotExist is returned when the batch lookup has reached +// the end of all possible results, even when partial results +// are returned. It should be used to evaluate when lookup is "done". +func (m *Map) BatchLookupAndDelete(prevKey, nextKeyOut, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { + return m.batchLookup(internal.BPF_MAP_LOOKUP_AND_DELETE_BATCH, prevKey, nextKeyOut, keysOut, valuesOut, opts) +} + +func (m *Map) batchLookup(cmd internal.BPFCmd, startKey, nextKeyOut, keysOut, valuesOut interface{}, opts *BatchOptions) (int, error) { + if err := haveBatchAPI(); err != nil { + return 0, err + } + if m.typ.hasPerCPUValue() { + return 0, ErrNotSupported + } + keysValue := reflect.ValueOf(keysOut) + if keysValue.Kind() != reflect.Slice { + return 0, fmt.Errorf("keys must be a slice") + } + valuesValue := reflect.ValueOf(valuesOut) + if valuesValue.Kind() != reflect.Slice { + return 0, fmt.Errorf("valuesOut must be a slice") + } + count := keysValue.Len() + if count != valuesValue.Len() { + return 0, fmt.Errorf("keysOut and valuesOut must be the same length") + } + keyBuf := make([]byte, count*int(m.keySize)) + keyPtr := internal.NewSlicePointer(keyBuf) + valueBuf := make([]byte, count*int(m.fullValueSize)) + valuePtr := internal.NewSlicePointer(valueBuf) + + var ( + startPtr internal.Pointer + err error + retErr error + ) + if startKey != nil { + startPtr, err = marshalPtr(startKey, int(m.keySize)) + if err != nil { + return 0, err + } + } + nextPtr, nextBuf := makeBuffer(nextKeyOut, int(m.keySize)) + + ct, err := bpfMapBatch(cmd, m.fd, startPtr, nextPtr, keyPtr, valuePtr, uint32(count), opts) + if err != nil { + if !errors.Is(err, ErrKeyNotExist) { + return 0, err + } + retErr = ErrKeyNotExist + } + + err = m.unmarshalKey(nextKeyOut, nextBuf) + if err != nil { + return 0, err + } + err = unmarshalBytes(keysOut, keyBuf) + if err != nil { + return 0, err + } + err = unmarshalBytes(valuesOut, valueBuf) + if err != nil { + retErr = err + } + return int(ct), retErr +} + +// BatchUpdate updates the map with multiple keys and values +// simultaneously. +// "keys" and "values" must be of type slice, a pointer +// to a slice or buffer will not work. +func (m *Map) BatchUpdate(keys, values interface{}, opts *BatchOptions) (int, error) { + if err := haveBatchAPI(); err != nil { + return 0, err + } + if m.typ.hasPerCPUValue() { + return 0, ErrNotSupported + } + keysValue := reflect.ValueOf(keys) + if keysValue.Kind() != reflect.Slice { + return 0, fmt.Errorf("keys must be a slice") + } + valuesValue := reflect.ValueOf(values) + if valuesValue.Kind() != reflect.Slice { + return 0, fmt.Errorf("values must be a slice") + } + var ( + count = keysValue.Len() + valuePtr internal.Pointer + err error + ) + if count != valuesValue.Len() { + return 0, fmt.Errorf("keys and values must be the same length") + } + keyPtr, err := marshalPtr(keys, count*int(m.keySize)) + if err != nil { + return 0, err + } + valuePtr, err = marshalPtr(values, count*int(m.valueSize)) + if err != nil { + return 0, err + } + var nilPtr internal.Pointer + ct, err := bpfMapBatch(internal.BPF_MAP_UPDATE_BATCH, m.fd, nilPtr, nilPtr, keyPtr, valuePtr, uint32(count), opts) + return int(ct), err +} + +// BatchDelete batch deletes entries in the map by keys. +// "keys" must be of type slice, a pointer to a slice or buffer will not work. +func (m *Map) BatchDelete(keys interface{}, opts *BatchOptions) (int, error) { + if err := haveBatchAPI(); err != nil { + return 0, err + } + if m.typ.hasPerCPUValue() { + return 0, ErrNotSupported + } + keysValue := reflect.ValueOf(keys) + if keysValue.Kind() != reflect.Slice { + return 0, fmt.Errorf("keys must be a slice") + } + count := keysValue.Len() + keyPtr, err := marshalPtr(keys, count*int(m.keySize)) + if err != nil { + return 0, fmt.Errorf("cannot marshal keys: %v", err) + } + var nilPtr internal.Pointer + ct, err := bpfMapBatch(internal.BPF_MAP_DELETE_BATCH, m.fd, nilPtr, nilPtr, keyPtr, nilPtr, uint32(count), opts) + return int(ct), err +} + +// Iterate traverses a map. +// +// It's safe to create multiple iterators at the same time. +// +// It's not possible to guarantee that all keys in a map will be +// returned if there are concurrent modifications to the map. +func (m *Map) Iterate() *MapIterator { + return newMapIterator(m) +} + +// Close removes a Map +func (m *Map) Close() error { + if m == nil { + // This makes it easier to clean up when iterating maps + // of maps / programs. + return nil + } + + return m.fd.Close() +} + +// FD gets the file descriptor of the Map. +// +// Calling this function is invalid after Close has been called. +func (m *Map) FD() int { + fd, err := m.fd.Value() + if err != nil { + // Best effort: -1 is the number most likely to be an + // invalid file descriptor. + return -1 + } + + return int(fd) +} + +// Clone creates a duplicate of the Map. +// +// Closing the duplicate does not affect the original, and vice versa. +// Changes made to the map are reflected by both instances however. +// If the original map was pinned, the cloned map will not be pinned by default. +// +// Cloning a nil Map returns nil. +func (m *Map) Clone() (*Map, error) { + if m == nil { + return nil, nil + } + + dup, err := m.fd.Dup() + if err != nil { + return nil, fmt.Errorf("can't clone map: %w", err) + } + + return &Map{ + m.name, + dup, + m.typ, + m.keySize, + m.valueSize, + m.maxEntries, + m.flags, + "", + m.fullValueSize, + }, nil +} + +// Pin persists the map on the BPF virtual file system past the lifetime of +// the process that created it . +// +// Calling Pin on a previously pinned map will overwrite the path, except when +// the new path already exists. Re-pinning across filesystems is not supported. +// You can Clone a map to pin it to a different path. +// +// This requires bpffs to be mounted above fileName. See https://docs.cilium.io/en/k8s-doc/admin/#admin-mount-bpffs +func (m *Map) Pin(fileName string) error { + if err := internal.Pin(m.pinnedPath, fileName, m.fd); err != nil { + return err + } + m.pinnedPath = fileName + return nil +} + +// Unpin removes the persisted state for the map from the BPF virtual filesystem. +// +// Failed calls to Unpin will not alter the state returned by IsPinned. +// +// Unpinning an unpinned Map returns nil. +func (m *Map) Unpin() error { + if err := internal.Unpin(m.pinnedPath); err != nil { + return err + } + m.pinnedPath = "" + return nil +} + +// IsPinned returns true if the map has a non-empty pinned path. +func (m *Map) IsPinned() bool { + return m.pinnedPath != "" +} + +// Freeze prevents a map to be modified from user space. +// +// It makes no changes to kernel-side restrictions. +func (m *Map) Freeze() error { + if err := haveMapMutabilityModifiers(); err != nil { + return fmt.Errorf("can't freeze map: %w", err) + } + + if err := bpfMapFreeze(m.fd); err != nil { + return fmt.Errorf("can't freeze map: %w", err) + } + return nil +} + +func (m *Map) populate(contents []MapKV) error { + for _, kv := range contents { + if err := m.Put(kv.Key, kv.Value); err != nil { + return fmt.Errorf("key %v: %w", kv.Key, err) + } + } + return nil +} + +func (m *Map) marshalKey(data interface{}) (internal.Pointer, error) { + if data == nil { + if m.keySize == 0 { + // Queues have a key length of zero, so passing nil here is valid. + return internal.NewPointer(nil), nil + } + return internal.Pointer{}, errors.New("can't use nil as key of map") + } + + return marshalPtr(data, int(m.keySize)) +} + +func (m *Map) unmarshalKey(data interface{}, buf []byte) error { + if buf == nil { + // This is from a makeBuffer call, nothing do do here. + return nil + } + + return unmarshalBytes(data, buf) +} + +func (m *Map) marshalValue(data interface{}) (internal.Pointer, error) { + if m.typ.hasPerCPUValue() { + return marshalPerCPUValue(data, int(m.valueSize)) + } + + var ( + buf []byte + err error + ) + + switch value := data.(type) { + case *Map: + if !m.typ.canStoreMap() { + return internal.Pointer{}, fmt.Errorf("can't store map in %s", m.typ) + } + buf, err = marshalMap(value, int(m.valueSize)) + + case *Program: + if !m.typ.canStoreProgram() { + return internal.Pointer{}, fmt.Errorf("can't store program in %s", m.typ) + } + buf, err = marshalProgram(value, int(m.valueSize)) + + default: + return marshalPtr(data, int(m.valueSize)) + } + + if err != nil { + return internal.Pointer{}, err + } + + return internal.NewSlicePointer(buf), nil +} + +func (m *Map) unmarshalValue(value interface{}, buf []byte) error { + if buf == nil { + // This is from a makeBuffer call, nothing do do here. + return nil + } + + if m.typ.hasPerCPUValue() { + return unmarshalPerCPUValue(value, int(m.valueSize), buf) + } + + switch value := value.(type) { + case **Map: + if !m.typ.canStoreMap() { + return fmt.Errorf("can't read a map from %s", m.typ) + } + + other, err := unmarshalMap(buf) + if err != nil { + return err + } + + // The caller might close the map externally, so ignore errors. + _ = (*value).Close() + + *value = other + return nil + + case *Map: + if !m.typ.canStoreMap() { + return fmt.Errorf("can't read a map from %s", m.typ) + } + return errors.New("require pointer to *Map") + + case **Program: + if !m.typ.canStoreProgram() { + return fmt.Errorf("can't read a program from %s", m.typ) + } + + other, err := unmarshalProgram(buf) + if err != nil { + return err + } + + // The caller might close the program externally, so ignore errors. + _ = (*value).Close() + + *value = other + return nil + + case *Program: + if !m.typ.canStoreProgram() { + return fmt.Errorf("can't read a program from %s", m.typ) + } + return errors.New("require pointer to *Program") + } + + return unmarshalBytes(value, buf) +} + +// LoadPinnedMap loads a Map from a BPF file. +func LoadPinnedMap(fileName string, opts *LoadPinOptions) (*Map, error) { + fd, err := internal.BPFObjGet(fileName, opts.Marshal()) + if err != nil { + return nil, err + } + + m, err := newMapFromFD(fd) + if err == nil { + m.pinnedPath = fileName + } + + return m, err +} + +// unmarshalMap creates a map from a map ID encoded in host endianness. +func unmarshalMap(buf []byte) (*Map, error) { + if len(buf) != 4 { + return nil, errors.New("map id requires 4 byte value") + } + + id := internal.NativeEndian.Uint32(buf) + return NewMapFromID(MapID(id)) +} + +// marshalMap marshals the fd of a map into a buffer in host endianness. +func marshalMap(m *Map, length int) ([]byte, error) { + if length != 4 { + return nil, fmt.Errorf("can't marshal map to %d bytes", length) + } + + fd, err := m.fd.Value() + if err != nil { + return nil, err + } + + buf := make([]byte, 4) + internal.NativeEndian.PutUint32(buf, fd) + return buf, nil +} + +func patchValue(value []byte, typ btf.Type, replacements map[string]interface{}) error { + replaced := make(map[string]bool) + replace := func(name string, offset, size int, replacement interface{}) error { + if offset+size > len(value) { + return fmt.Errorf("%s: offset %d(+%d) is out of bounds", name, offset, size) + } + + buf, err := marshalBytes(replacement, size) + if err != nil { + return fmt.Errorf("marshal %s: %w", name, err) + } + + copy(value[offset:offset+size], buf) + replaced[name] = true + return nil + } + + switch parent := typ.(type) { + case *btf.Datasec: + for _, secinfo := range parent.Vars { + name := string(secinfo.Type.(*btf.Var).Name) + replacement, ok := replacements[name] + if !ok { + continue + } + + err := replace(name, int(secinfo.Offset), int(secinfo.Size), replacement) + if err != nil { + return err + } + } + + default: + return fmt.Errorf("patching %T is not supported", typ) + } + + if len(replaced) == len(replacements) { + return nil + } + + var missing []string + for name := range replacements { + if !replaced[name] { + missing = append(missing, name) + } + } + + if len(missing) == 1 { + return fmt.Errorf("unknown field: %s", missing[0]) + } + + return fmt.Errorf("unknown fields: %s", strings.Join(missing, ",")) +} + +// MapIterator iterates a Map. +// +// See Map.Iterate. +type MapIterator struct { + target *Map + prevKey interface{} + prevBytes []byte + count, maxEntries uint32 + done bool + err error +} + +func newMapIterator(target *Map) *MapIterator { + return &MapIterator{ + target: target, + maxEntries: target.maxEntries, + prevBytes: make([]byte, target.keySize), + } +} + +// Next decodes the next key and value. +// +// Iterating a hash map from which keys are being deleted is not +// safe. You may see the same key multiple times. Iteration may +// also abort with an error, see IsIterationAborted. +// +// Returns false if there are no more entries. You must check +// the result of Err afterwards. +// +// See Map.Get for further caveats around valueOut. +func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool { + if mi.err != nil || mi.done { + return false + } + + // For array-like maps NextKeyBytes returns nil only on after maxEntries + // iterations. + for mi.count <= mi.maxEntries { + var nextBytes []byte + nextBytes, mi.err = mi.target.NextKeyBytes(mi.prevKey) + if mi.err != nil { + return false + } + + if nextBytes == nil { + mi.done = true + return false + } + + // The user can get access to nextBytes since unmarshalBytes + // does not copy when unmarshaling into a []byte. + // Make a copy to prevent accidental corruption of + // iterator state. + copy(mi.prevBytes, nextBytes) + mi.prevKey = mi.prevBytes + + mi.count++ + mi.err = mi.target.Lookup(nextBytes, valueOut) + if errors.Is(mi.err, ErrKeyNotExist) { + // Even though the key should be valid, we couldn't look up + // its value. If we're iterating a hash map this is probably + // because a concurrent delete removed the value before we + // could get it. This means that the next call to NextKeyBytes + // is very likely to restart iteration. + // If we're iterating one of the fd maps like + // ProgramArray it means that a given slot doesn't have + // a valid fd associated. It's OK to continue to the next slot. + continue + } + if mi.err != nil { + return false + } + + mi.err = mi.target.unmarshalKey(keyOut, nextBytes) + return mi.err == nil + } + + mi.err = fmt.Errorf("%w", ErrIterationAborted) + return false +} + +// Err returns any encountered error. +// +// The method must be called after Next returns nil. +// +// Returns ErrIterationAborted if it wasn't possible to do a full iteration. +func (mi *MapIterator) Err() error { + return mi.err +} + +// MapGetNextID returns the ID of the next eBPF map. +// +// Returns ErrNotExist, if there is no next eBPF map. +func MapGetNextID(startID MapID) (MapID, error) { + id, err := objGetNextID(internal.BPF_MAP_GET_NEXT_ID, uint32(startID)) + return MapID(id), err +} + +// NewMapFromID returns the map for a given id. +// +// Returns ErrNotExist, if there is no eBPF map with the given id. +func NewMapFromID(id MapID) (*Map, error) { + fd, err := bpfObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id)) + if err != nil { + return nil, err + } + + return newMapFromFD(fd) +} + +// ID returns the systemwide unique ID of the map. +// +// Deprecated: use MapInfo.ID() instead. +func (m *Map) ID() (MapID, error) { + info, err := bpfGetMapInfoByFD(m.fd) + if err != nil { + return MapID(0), err + } + return MapID(info.id), nil +} diff --git a/vendor/github.com/cilium/ebpf/marshalers.go b/vendor/github.com/cilium/ebpf/marshalers.go new file mode 100644 index 0000000000000..f2610eff9c781 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/marshalers.go @@ -0,0 +1,218 @@ +package ebpf + +import ( + "bytes" + "encoding" + "encoding/binary" + "errors" + "fmt" + "reflect" + "runtime" + "unsafe" + + "github.com/cilium/ebpf/internal" +) + +// marshalPtr converts an arbitrary value into a pointer suitable +// to be passed to the kernel. +// +// As an optimization, it returns the original value if it is an +// unsafe.Pointer. +func marshalPtr(data interface{}, length int) (internal.Pointer, error) { + if ptr, ok := data.(unsafe.Pointer); ok { + return internal.NewPointer(ptr), nil + } + + buf, err := marshalBytes(data, length) + if err != nil { + return internal.Pointer{}, err + } + + return internal.NewSlicePointer(buf), nil +} + +// marshalBytes converts an arbitrary value into a byte buffer. +// +// Prefer using Map.marshalKey and Map.marshalValue if possible, since +// those have special cases that allow more types to be encoded. +// +// Returns an error if the given value isn't representable in exactly +// length bytes. +func marshalBytes(data interface{}, length int) (buf []byte, err error) { + switch value := data.(type) { + case encoding.BinaryMarshaler: + buf, err = value.MarshalBinary() + case string: + buf = []byte(value) + case []byte: + buf = value + case unsafe.Pointer: + err = errors.New("can't marshal from unsafe.Pointer") + case Map, *Map, Program, *Program: + err = fmt.Errorf("can't marshal %T", value) + default: + var wr bytes.Buffer + err = binary.Write(&wr, internal.NativeEndian, value) + if err != nil { + err = fmt.Errorf("encoding %T: %v", value, err) + } + buf = wr.Bytes() + } + if err != nil { + return nil, err + } + + if len(buf) != length { + return nil, fmt.Errorf("%T doesn't marshal to %d bytes", data, length) + } + return buf, nil +} + +func makeBuffer(dst interface{}, length int) (internal.Pointer, []byte) { + if ptr, ok := dst.(unsafe.Pointer); ok { + return internal.NewPointer(ptr), nil + } + + buf := make([]byte, length) + return internal.NewSlicePointer(buf), buf +} + +// unmarshalBytes converts a byte buffer into an arbitrary value. +// +// Prefer using Map.unmarshalKey and Map.unmarshalValue if possible, since +// those have special cases that allow more types to be encoded. +func unmarshalBytes(data interface{}, buf []byte) error { + switch value := data.(type) { + case unsafe.Pointer: + // This could be solved in Go 1.17 by unsafe.Slice instead. (https://github.com/golang/go/issues/19367) + // We could opt for removing unsafe.Pointer support in the lib as well. + sh := &reflect.SliceHeader{ //nolint:govet + Data: uintptr(value), + Len: len(buf), + Cap: len(buf), + } + + dst := *(*[]byte)(unsafe.Pointer(sh)) + copy(dst, buf) + runtime.KeepAlive(value) + return nil + case Map, *Map, Program, *Program: + return fmt.Errorf("can't unmarshal into %T", value) + case encoding.BinaryUnmarshaler: + return value.UnmarshalBinary(buf) + case *string: + *value = string(buf) + return nil + case *[]byte: + *value = buf + return nil + case string: + return errors.New("require pointer to string") + case []byte: + return errors.New("require pointer to []byte") + default: + rd := bytes.NewReader(buf) + if err := binary.Read(rd, internal.NativeEndian, value); err != nil { + return fmt.Errorf("decoding %T: %v", value, err) + } + return nil + } +} + +// marshalPerCPUValue encodes a slice containing one value per +// possible CPU into a buffer of bytes. +// +// Values are initialized to zero if the slice has less elements than CPUs. +// +// slice must have a type like []elementType. +func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, error) { + sliceType := reflect.TypeOf(slice) + if sliceType.Kind() != reflect.Slice { + return internal.Pointer{}, errors.New("per-CPU value requires slice") + } + + possibleCPUs, err := internal.PossibleCPUs() + if err != nil { + return internal.Pointer{}, err + } + + sliceValue := reflect.ValueOf(slice) + sliceLen := sliceValue.Len() + if sliceLen > possibleCPUs { + return internal.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs") + } + + alignedElemLength := align(elemLength, 8) + buf := make([]byte, alignedElemLength*possibleCPUs) + + for i := 0; i < sliceLen; i++ { + elem := sliceValue.Index(i).Interface() + elemBytes, err := marshalBytes(elem, elemLength) + if err != nil { + return internal.Pointer{}, err + } + + offset := i * alignedElemLength + copy(buf[offset:offset+elemLength], elemBytes) + } + + return internal.NewSlicePointer(buf), nil +} + +// unmarshalPerCPUValue decodes a buffer into a slice containing one value per +// possible CPU. +// +// valueOut must have a type like *[]elementType +func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error { + slicePtrType := reflect.TypeOf(slicePtr) + if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice { + return fmt.Errorf("per-cpu value requires pointer to slice") + } + + possibleCPUs, err := internal.PossibleCPUs() + if err != nil { + return err + } + + sliceType := slicePtrType.Elem() + slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs) + + sliceElemType := sliceType.Elem() + sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr + if sliceElemIsPointer { + sliceElemType = sliceElemType.Elem() + } + + step := len(buf) / possibleCPUs + if step < elemLength { + return fmt.Errorf("per-cpu element length is larger than available data") + } + for i := 0; i < possibleCPUs; i++ { + var elem interface{} + if sliceElemIsPointer { + newElem := reflect.New(sliceElemType) + slice.Index(i).Set(newElem) + elem = newElem.Interface() + } else { + elem = slice.Index(i).Addr().Interface() + } + + // Make a copy, since unmarshal can hold on to itemBytes + elemBytes := make([]byte, elemLength) + copy(elemBytes, buf[:elemLength]) + + err := unmarshalBytes(elem, elemBytes) + if err != nil { + return fmt.Errorf("cpu %d: %w", i, err) + } + + buf = buf[step:] + } + + reflect.ValueOf(slicePtr).Elem().Set(slice) + return nil +} + +func align(n, alignment int) int { + return (int(n) + alignment - 1) / alignment * alignment +} diff --git a/vendor/github.com/cilium/ebpf/prog.go b/vendor/github.com/cilium/ebpf/prog.go new file mode 100644 index 0000000000000..13bdb6ddad1e2 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/prog.go @@ -0,0 +1,728 @@ +package ebpf + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "math" + "path/filepath" + "strings" + "time" + + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" + "github.com/cilium/ebpf/internal/unix" +) + +// ErrNotSupported is returned whenever the kernel doesn't support a feature. +var ErrNotSupported = internal.ErrNotSupported + +var errUnsatisfiedReference = errors.New("unsatisfied reference") + +// ProgramID represents the unique ID of an eBPF program. +type ProgramID uint32 + +const ( + // Number of bytes to pad the output buffer for BPF_PROG_TEST_RUN. + // This is currently the maximum of spare space allocated for SKB + // and XDP programs, and equal to XDP_PACKET_HEADROOM + NET_IP_ALIGN. + outputPad = 256 + 2 +) + +// DefaultVerifierLogSize is the default number of bytes allocated for the +// verifier log. +const DefaultVerifierLogSize = 64 * 1024 + +// ProgramOptions control loading a program into the kernel. +type ProgramOptions struct { + // Controls the detail emitted by the kernel verifier. Set to non-zero + // to enable logging. + LogLevel uint32 + // Controls the output buffer size for the verifier. Defaults to + // DefaultVerifierLogSize. + LogSize int + // An ELF containing the target BTF for this program. It is used both to + // find the correct function to trace and to apply CO-RE relocations. + // This is useful in environments where the kernel BTF is not available + // (containers) or where it is in a non-standard location. Defaults to + // use the kernel BTF from a well-known location. + TargetBTF io.ReaderAt +} + +// ProgramSpec defines a Program. +type ProgramSpec struct { + // Name is passed to the kernel as a debug aid. Must only contain + // alpha numeric and '_' characters. + Name string + // Type determines at which hook in the kernel a program will run. + Type ProgramType + AttachType AttachType + // Name of a kernel data structure to attach to. It's interpretation + // depends on Type and AttachType. + AttachTo string + Instructions asm.Instructions + // Flags is passed to the kernel and specifies additional program + // load attributes. + Flags uint32 + // License of the program. Some helpers are only available if + // the license is deemed compatible with the GPL. + // + // See https://www.kernel.org/doc/html/latest/process/license-rules.html#id1 + License string + + // Version used by Kprobe programs. + // + // Deprecated on kernels 5.0 and later. Leave empty to let the library + // detect this value automatically. + KernelVersion uint32 + + // The BTF associated with this program. Changing Instructions + // will most likely invalidate the contained data, and may + // result in errors when attempting to load it into the kernel. + BTF *btf.Program + + // The byte order this program was compiled for, may be nil. + ByteOrder binary.ByteOrder +} + +// Copy returns a copy of the spec. +func (ps *ProgramSpec) Copy() *ProgramSpec { + if ps == nil { + return nil + } + + cpy := *ps + cpy.Instructions = make(asm.Instructions, len(ps.Instructions)) + copy(cpy.Instructions, ps.Instructions) + return &cpy +} + +// Tag calculates the kernel tag for a series of instructions. +// +// Use asm.Instructions.Tag if you need to calculate for non-native endianness. +func (ps *ProgramSpec) Tag() (string, error) { + return ps.Instructions.Tag(internal.NativeEndian) +} + +// Program represents BPF program loaded into the kernel. +// +// It is not safe to close a Program which is used by other goroutines. +type Program struct { + // Contains the output of the kernel verifier if enabled, + // otherwise it is empty. + VerifierLog string + + fd *internal.FD + name string + pinnedPath string + typ ProgramType +} + +// NewProgram creates a new Program. +// +// Loading a program for the first time will perform +// feature detection by loading small, temporary programs. +func NewProgram(spec *ProgramSpec) (*Program, error) { + return NewProgramWithOptions(spec, ProgramOptions{}) +} + +// NewProgramWithOptions creates a new Program. +// +// Loading a program for the first time will perform +// feature detection by loading small, temporary programs. +func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) { + handles := newHandleCache() + defer handles.close() + + prog, err := newProgramWithOptions(spec, opts, handles) + if errors.Is(err, errUnsatisfiedReference) { + return nil, fmt.Errorf("cannot load program without loading its whole collection: %w", err) + } + return prog, err +} + +func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *handleCache) (*Program, error) { + if len(spec.Instructions) == 0 { + return nil, errors.New("Instructions cannot be empty") + } + + if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian { + return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian) + } + + // Kernels before 5.0 (6c4fc209fcf9 "bpf: remove useless version check for prog load") + // require the version field to be set to the value of the KERNEL_VERSION + // macro for kprobe-type programs. + // Overwrite Kprobe program version if set to zero or the magic version constant. + kv := spec.KernelVersion + if spec.Type == Kprobe && (kv == 0 || kv == internal.MagicKernelVersion) { + v, err := internal.KernelVersion() + if err != nil { + return nil, fmt.Errorf("detecting kernel version: %w", err) + } + kv = v.Kernel() + } + + attr := &bpfProgLoadAttr{ + progType: spec.Type, + progFlags: spec.Flags, + expectedAttachType: spec.AttachType, + license: internal.NewStringPointer(spec.License), + kernelVersion: kv, + } + + if haveObjName() == nil { + attr.progName = internal.NewBPFObjName(spec.Name) + } + + var err error + var targetBTF *btf.Spec + if opts.TargetBTF != nil { + targetBTF, err = handles.btfSpec(opts.TargetBTF) + if err != nil { + return nil, fmt.Errorf("load target BTF: %w", err) + } + } + + var btfDisabled bool + var core btf.COREFixups + if spec.BTF != nil { + core, err = btf.ProgramFixups(spec.BTF, targetBTF) + if err != nil { + return nil, fmt.Errorf("CO-RE relocations: %w", err) + } + + handle, err := handles.btfHandle(btf.ProgramSpec(spec.BTF)) + btfDisabled = errors.Is(err, btf.ErrNotSupported) + if err != nil && !btfDisabled { + return nil, fmt.Errorf("load BTF: %w", err) + } + + if handle != nil { + attr.progBTFFd = uint32(handle.FD()) + + recSize, bytes, err := btf.ProgramLineInfos(spec.BTF) + if err != nil { + return nil, fmt.Errorf("get BTF line infos: %w", err) + } + attr.lineInfoRecSize = recSize + attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) + attr.lineInfo = internal.NewSlicePointer(bytes) + + recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF) + if err != nil { + return nil, fmt.Errorf("get BTF function infos: %w", err) + } + attr.funcInfoRecSize = recSize + attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) + attr.funcInfo = internal.NewSlicePointer(bytes) + } + } + + insns, err := core.Apply(spec.Instructions) + if err != nil { + return nil, fmt.Errorf("CO-RE fixup: %w", err) + } + + if err := fixupJumpsAndCalls(insns); err != nil { + return nil, err + } + + buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize)) + err = insns.Marshal(buf, internal.NativeEndian) + if err != nil { + return nil, err + } + + bytecode := buf.Bytes() + attr.instructions = internal.NewSlicePointer(bytecode) + attr.insCount = uint32(len(bytecode) / asm.InstructionSize) + + if spec.AttachTo != "" { + target, err := resolveBTFType(targetBTF, spec.AttachTo, spec.Type, spec.AttachType) + if err != nil { + return nil, err + } + if target != nil { + attr.attachBTFID = target.ID() + } + } + + logSize := DefaultVerifierLogSize + if opts.LogSize > 0 { + logSize = opts.LogSize + } + + var logBuf []byte + if opts.LogLevel > 0 { + logBuf = make([]byte, logSize) + attr.logLevel = opts.LogLevel + attr.logSize = uint32(len(logBuf)) + attr.logBuf = internal.NewSlicePointer(logBuf) + } + + fd, err := bpfProgLoad(attr) + if err == nil { + return &Program{internal.CString(logBuf), fd, spec.Name, "", spec.Type}, nil + } + + logErr := err + if opts.LogLevel == 0 && opts.LogSize >= 0 { + // Re-run with the verifier enabled to get better error messages. + logBuf = make([]byte, logSize) + attr.logLevel = 1 + attr.logSize = uint32(len(logBuf)) + attr.logBuf = internal.NewSlicePointer(logBuf) + + _, logErr = bpfProgLoad(attr) + } + + if errors.Is(logErr, unix.EPERM) && logBuf[0] == 0 { + // EPERM due to RLIMIT_MEMLOCK happens before the verifier, so we can + // check that the log is empty to reduce false positives. + return nil, fmt.Errorf("load program: RLIMIT_MEMLOCK may be too low: %w", logErr) + } + + err = internal.ErrorWithLog(err, logBuf, logErr) + if btfDisabled { + return nil, fmt.Errorf("load program without BTF: %w", err) + } + return nil, fmt.Errorf("load program: %w", err) +} + +// NewProgramFromFD creates a program from a raw fd. +// +// You should not use fd after calling this function. +// +// Requires at least Linux 4.10. +func NewProgramFromFD(fd int) (*Program, error) { + if fd < 0 { + return nil, errors.New("invalid fd") + } + + return newProgramFromFD(internal.NewFD(uint32(fd))) +} + +// NewProgramFromID returns the program for a given id. +// +// Returns ErrNotExist, if there is no eBPF program with the given id. +func NewProgramFromID(id ProgramID) (*Program, error) { + fd, err := bpfObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id)) + if err != nil { + return nil, fmt.Errorf("get program by id: %w", err) + } + + return newProgramFromFD(fd) +} + +func newProgramFromFD(fd *internal.FD) (*Program, error) { + info, err := newProgramInfoFromFd(fd) + if err != nil { + fd.Close() + return nil, fmt.Errorf("discover program type: %w", err) + } + + return &Program{"", fd, "", "", info.Type}, nil +} + +func (p *Program) String() string { + if p.name != "" { + return fmt.Sprintf("%s(%s)#%v", p.typ, p.name, p.fd) + } + return fmt.Sprintf("%s(%v)", p.typ, p.fd) +} + +// Type returns the underlying type of the program. +func (p *Program) Type() ProgramType { + return p.typ +} + +// Info returns metadata about the program. +// +// Requires at least 4.10. +func (p *Program) Info() (*ProgramInfo, error) { + return newProgramInfoFromFd(p.fd) +} + +// FD gets the file descriptor of the Program. +// +// It is invalid to call this function after Close has been called. +func (p *Program) FD() int { + fd, err := p.fd.Value() + if err != nil { + // Best effort: -1 is the number most likely to be an + // invalid file descriptor. + return -1 + } + + return int(fd) +} + +// Clone creates a duplicate of the Program. +// +// Closing the duplicate does not affect the original, and vice versa. +// +// Cloning a nil Program returns nil. +func (p *Program) Clone() (*Program, error) { + if p == nil { + return nil, nil + } + + dup, err := p.fd.Dup() + if err != nil { + return nil, fmt.Errorf("can't clone program: %w", err) + } + + return &Program{p.VerifierLog, dup, p.name, "", p.typ}, nil +} + +// Pin persists the Program on the BPF virtual file system past the lifetime of +// the process that created it +// +// Calling Pin on a previously pinned program will overwrite the path, except when +// the new path already exists. Re-pinning across filesystems is not supported. +// +// This requires bpffs to be mounted above fileName. See https://docs.cilium.io/en/k8s-doc/admin/#admin-mount-bpffs +func (p *Program) Pin(fileName string) error { + if err := internal.Pin(p.pinnedPath, fileName, p.fd); err != nil { + return err + } + p.pinnedPath = fileName + return nil +} + +// Unpin removes the persisted state for the Program from the BPF virtual filesystem. +// +// Failed calls to Unpin will not alter the state returned by IsPinned. +// +// Unpinning an unpinned Program returns nil. +func (p *Program) Unpin() error { + if err := internal.Unpin(p.pinnedPath); err != nil { + return err + } + p.pinnedPath = "" + return nil +} + +// IsPinned returns true if the Program has a non-empty pinned path. +func (p *Program) IsPinned() bool { + return p.pinnedPath != "" +} + +// Close unloads the program from the kernel. +func (p *Program) Close() error { + if p == nil { + return nil + } + + return p.fd.Close() +} + +// Test runs the Program in the kernel with the given input and returns the +// value returned by the eBPF program. outLen may be zero. +// +// Note: the kernel expects at least 14 bytes input for an ethernet header for +// XDP and SKB programs. +// +// This function requires at least Linux 4.12. +func (p *Program) Test(in []byte) (uint32, []byte, error) { + ret, out, _, err := p.testRun(in, 1, nil) + if err != nil { + return ret, nil, fmt.Errorf("can't test program: %w", err) + } + return ret, out, nil +} + +// Benchmark runs the Program with the given input for a number of times +// and returns the time taken per iteration. +// +// Returns the result of the last execution of the program and the time per +// run or an error. reset is called whenever the benchmark syscall is +// interrupted, and should be set to testing.B.ResetTimer or similar. +// +// Note: profiling a call to this function will skew it's results, see +// https://github.com/cilium/ebpf/issues/24 +// +// This function requires at least Linux 4.12. +func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) { + ret, _, total, err := p.testRun(in, repeat, reset) + if err != nil { + return ret, total, fmt.Errorf("can't benchmark program: %w", err) + } + return ret, total, nil +} + +var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() error { + prog, err := NewProgram(&ProgramSpec{ + Type: SocketFilter, + Instructions: asm.Instructions{ + asm.LoadImm(asm.R0, 0, asm.DWord), + asm.Return(), + }, + License: "MIT", + }) + if err != nil { + // This may be because we lack sufficient permissions, etc. + return err + } + defer prog.Close() + + // Programs require at least 14 bytes input + in := make([]byte, 14) + attr := bpfProgTestRunAttr{ + fd: uint32(prog.FD()), + dataSizeIn: uint32(len(in)), + dataIn: internal.NewSlicePointer(in), + } + + err = bpfProgTestRun(&attr) + if errors.Is(err, unix.EINVAL) { + // Check for EINVAL specifically, rather than err != nil since we + // otherwise misdetect due to insufficient permissions. + return internal.ErrNotSupported + } + if errors.Is(err, unix.EINTR) { + // We know that PROG_TEST_RUN is supported if we get EINTR. + return nil + } + return err +}) + +func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, time.Duration, error) { + if uint(repeat) > math.MaxUint32 { + return 0, nil, 0, fmt.Errorf("repeat is too high") + } + + if len(in) == 0 { + return 0, nil, 0, fmt.Errorf("missing input") + } + + if uint(len(in)) > math.MaxUint32 { + return 0, nil, 0, fmt.Errorf("input is too long") + } + + if err := haveProgTestRun(); err != nil { + return 0, nil, 0, err + } + + // Older kernels ignore the dataSizeOut argument when copying to user space. + // Combined with things like bpf_xdp_adjust_head() we don't really know what the final + // size will be. Hence we allocate an output buffer which we hope will always be large + // enough, and panic if the kernel wrote past the end of the allocation. + // See https://patchwork.ozlabs.org/cover/1006822/ + out := make([]byte, len(in)+outputPad) + + fd, err := p.fd.Value() + if err != nil { + return 0, nil, 0, err + } + + attr := bpfProgTestRunAttr{ + fd: fd, + dataSizeIn: uint32(len(in)), + dataSizeOut: uint32(len(out)), + dataIn: internal.NewSlicePointer(in), + dataOut: internal.NewSlicePointer(out), + repeat: uint32(repeat), + } + + for { + err = bpfProgTestRun(&attr) + if err == nil { + break + } + + if errors.Is(err, unix.EINTR) { + if reset != nil { + reset() + } + continue + } + + return 0, nil, 0, fmt.Errorf("can't run test: %w", err) + } + + if int(attr.dataSizeOut) > cap(out) { + // Houston, we have a problem. The program created more data than we allocated, + // and the kernel wrote past the end of our buffer. + panic("kernel wrote past end of output buffer") + } + out = out[:int(attr.dataSizeOut)] + + total := time.Duration(attr.duration) * time.Nanosecond + return attr.retval, out, total, nil +} + +func unmarshalProgram(buf []byte) (*Program, error) { + if len(buf) != 4 { + return nil, errors.New("program id requires 4 byte value") + } + + // Looking up an entry in a nested map or prog array returns an id, + // not an fd. + id := internal.NativeEndian.Uint32(buf) + return NewProgramFromID(ProgramID(id)) +} + +func marshalProgram(p *Program, length int) ([]byte, error) { + if length != 4 { + return nil, fmt.Errorf("can't marshal program to %d bytes", length) + } + + value, err := p.fd.Value() + if err != nil { + return nil, err + } + + buf := make([]byte, 4) + internal.NativeEndian.PutUint32(buf, value) + return buf, nil +} + +// Attach a Program. +// +// Deprecated: use link.RawAttachProgram instead. +func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error { + if fd < 0 { + return errors.New("invalid fd") + } + + pfd, err := p.fd.Value() + if err != nil { + return err + } + + attr := internal.BPFProgAttachAttr{ + TargetFd: uint32(fd), + AttachBpfFd: pfd, + AttachType: uint32(typ), + AttachFlags: uint32(flags), + } + + return internal.BPFProgAttach(&attr) +} + +// Detach a Program. +// +// Deprecated: use link.RawDetachProgram instead. +func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error { + if fd < 0 { + return errors.New("invalid fd") + } + + if flags != 0 { + return errors.New("flags must be zero") + } + + pfd, err := p.fd.Value() + if err != nil { + return err + } + + attr := internal.BPFProgDetachAttr{ + TargetFd: uint32(fd), + AttachBpfFd: pfd, + AttachType: uint32(typ), + } + + return internal.BPFProgDetach(&attr) +} + +// LoadPinnedProgram loads a Program from a BPF file. +// +// Requires at least Linux 4.11. +func LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error) { + fd, err := internal.BPFObjGet(fileName, opts.Marshal()) + if err != nil { + return nil, err + } + + info, err := newProgramInfoFromFd(fd) + if err != nil { + _ = fd.Close() + return nil, fmt.Errorf("info for %s: %w", fileName, err) + } + + return &Program{"", fd, filepath.Base(fileName), fileName, info.Type}, nil +} + +// SanitizeName replaces all invalid characters in name with replacement. +// Passing a negative value for replacement will delete characters instead +// of replacing them. Use this to automatically generate valid names for maps +// and programs at runtime. +// +// The set of allowed characters depends on the running kernel version. +// Dots are only allowed as of kernel 5.2. +func SanitizeName(name string, replacement rune) string { + return strings.Map(func(char rune) rune { + if invalidBPFObjNameChar(char) { + return replacement + } + return char + }, name) +} + +// ProgramGetNextID returns the ID of the next eBPF program. +// +// Returns ErrNotExist, if there is no next eBPF program. +func ProgramGetNextID(startID ProgramID) (ProgramID, error) { + id, err := objGetNextID(internal.BPF_PROG_GET_NEXT_ID, uint32(startID)) + return ProgramID(id), err +} + +// ID returns the systemwide unique ID of the program. +// +// Deprecated: use ProgramInfo.ID() instead. +func (p *Program) ID() (ProgramID, error) { + info, err := bpfGetProgInfoByFD(p.fd) + if err != nil { + return ProgramID(0), err + } + return ProgramID(info.id), nil +} + +func resolveBTFType(kernel *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.Type, error) { + type match struct { + p ProgramType + a AttachType + } + + var target btf.Type + var typeName, featureName string + switch (match{progType, attachType}) { + case match{LSM, AttachLSMMac}: + target = new(btf.Func) + typeName = "bpf_lsm_" + name + featureName = name + " LSM hook" + + case match{Tracing, AttachTraceIter}: + target = new(btf.Func) + typeName = "bpf_iter_" + name + featureName = name + " iterator" + + default: + return nil, nil + } + + if kernel == nil { + var err error + kernel, err = btf.LoadKernelSpec() + if err != nil { + return nil, fmt.Errorf("load kernel spec: %w", err) + } + } + + err := kernel.FindType(typeName, target) + if errors.Is(err, btf.ErrNotFound) { + return nil, &internal.UnsupportedFeatureError{ + Name: featureName, + } + } + if err != nil { + return nil, fmt.Errorf("resolve BTF for %s: %w", featureName, err) + } + return target, nil +} diff --git a/vendor/github.com/cilium/ebpf/syscalls.go b/vendor/github.com/cilium/ebpf/syscalls.go new file mode 100644 index 0000000000000..f5a38549bb7cf --- /dev/null +++ b/vendor/github.com/cilium/ebpf/syscalls.go @@ -0,0 +1,480 @@ +package ebpf + +import ( + "errors" + "fmt" + "os" + "unsafe" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" + "github.com/cilium/ebpf/internal/unix" +) + +// ErrNotExist is returned when loading a non-existing map or program. +// +// Deprecated: use os.ErrNotExist instead. +var ErrNotExist = os.ErrNotExist + +// invalidBPFObjNameChar returns true if char may not appear in +// a BPF object name. +func invalidBPFObjNameChar(char rune) bool { + dotAllowed := objNameAllowsDot() == nil + + switch { + case char >= 'A' && char <= 'Z': + return false + case char >= 'a' && char <= 'z': + return false + case char >= '0' && char <= '9': + return false + case dotAllowed && char == '.': + return false + case char == '_': + return false + default: + return true + } +} + +type bpfMapOpAttr struct { + mapFd uint32 + padding uint32 + key internal.Pointer + value internal.Pointer + flags uint64 +} + +type bpfBatchMapOpAttr struct { + inBatch internal.Pointer + outBatch internal.Pointer + keys internal.Pointer + values internal.Pointer + count uint32 + mapFd uint32 + elemFlags uint64 + flags uint64 +} + +type bpfMapInfo struct { + map_type uint32 // since 4.12 1e2709769086 + id uint32 + key_size uint32 + value_size uint32 + max_entries uint32 + map_flags uint32 + name internal.BPFObjName // since 4.15 ad5b177bd73f + ifindex uint32 // since 4.16 52775b33bb50 + btf_vmlinux_value_type_id uint32 // since 5.6 85d33df357b6 + netns_dev uint64 // since 4.16 52775b33bb50 + netns_ino uint64 + btf_id uint32 // since 4.18 78958fca7ead + btf_key_type_id uint32 // since 4.18 9b2cf328b2ec + btf_value_type_id uint32 +} + +type bpfProgLoadAttr struct { + progType ProgramType + insCount uint32 + instructions internal.Pointer + license internal.Pointer + logLevel uint32 + logSize uint32 + logBuf internal.Pointer + kernelVersion uint32 // since 4.1 2541517c32be + progFlags uint32 // since 4.11 e07b98d9bffe + progName internal.BPFObjName // since 4.15 067cae47771c + progIfIndex uint32 // since 4.15 1f6f4cb7ba21 + expectedAttachType AttachType // since 4.17 5e43f899b03a + progBTFFd uint32 + funcInfoRecSize uint32 + funcInfo internal.Pointer + funcInfoCnt uint32 + lineInfoRecSize uint32 + lineInfo internal.Pointer + lineInfoCnt uint32 + attachBTFID btf.TypeID + attachProgFd uint32 +} + +type bpfProgInfo struct { + prog_type uint32 + id uint32 + tag [unix.BPF_TAG_SIZE]byte + jited_prog_len uint32 + xlated_prog_len uint32 + jited_prog_insns internal.Pointer + xlated_prog_insns internal.Pointer + load_time uint64 // since 4.15 cb4d2b3f03d8 + created_by_uid uint32 + nr_map_ids uint32 + map_ids internal.Pointer + name internal.BPFObjName // since 4.15 067cae47771c + ifindex uint32 + gpl_compatible uint32 + netns_dev uint64 + netns_ino uint64 + nr_jited_ksyms uint32 + nr_jited_func_lens uint32 + jited_ksyms internal.Pointer + jited_func_lens internal.Pointer + btf_id uint32 + func_info_rec_size uint32 + func_info internal.Pointer + nr_func_info uint32 + nr_line_info uint32 + line_info internal.Pointer + jited_line_info internal.Pointer + nr_jited_line_info uint32 + line_info_rec_size uint32 + jited_line_info_rec_size uint32 + nr_prog_tags uint32 + prog_tags internal.Pointer + run_time_ns uint64 + run_cnt uint64 +} + +type bpfProgTestRunAttr struct { + fd uint32 + retval uint32 + dataSizeIn uint32 + dataSizeOut uint32 + dataIn internal.Pointer + dataOut internal.Pointer + repeat uint32 + duration uint32 +} + +type bpfGetFDByIDAttr struct { + id uint32 + next uint32 +} + +type bpfMapFreezeAttr struct { + mapFd uint32 +} + +type bpfObjGetNextIDAttr struct { + startID uint32 + nextID uint32 + openFlags uint32 +} + +func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) { + for { + fd, err := internal.BPF(internal.BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + // As of ~4.20 the verifier can be interrupted by a signal, + // and returns EAGAIN in that case. + if errors.Is(err, unix.EAGAIN) { + continue + } + + if err != nil { + return nil, err + } + + return internal.NewFD(uint32(fd)), nil + } +} + +func bpfProgTestRun(attr *bpfProgTestRunAttr) error { + _, err := internal.BPF(internal.BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + return err +} + +var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() error { + _, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{ + MapType: uint32(ArrayOfMaps), + KeySize: 4, + ValueSize: 4, + MaxEntries: 1, + // Invalid file descriptor. + InnerMapFd: ^uint32(0), + }) + if errors.Is(err, unix.EINVAL) { + return internal.ErrNotSupported + } + if errors.Is(err, unix.EBADF) { + return nil + } + return err +}) + +var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() error { + // This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since + // BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check. + m, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{ + MapType: uint32(Array), + KeySize: 4, + ValueSize: 4, + MaxEntries: 1, + Flags: unix.BPF_F_RDONLY_PROG, + }) + if err != nil { + return internal.ErrNotSupported + } + _ = m.Close() + return nil +}) + +var haveMmapableMaps = internal.FeatureTest("mmapable maps", "5.5", func() error { + // This checks BPF_F_MMAPABLE, which appeared in 5.5 for array maps. + m, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{ + MapType: uint32(Array), + KeySize: 4, + ValueSize: 4, + MaxEntries: 1, + Flags: unix.BPF_F_MMAPABLE, + }) + if err != nil { + return internal.ErrNotSupported + } + _ = m.Close() + return nil +}) + +var haveInnerMaps = internal.FeatureTest("inner maps", "5.10", func() error { + // This checks BPF_F_INNER_MAP, which appeared in 5.10. + m, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{ + MapType: uint32(Array), + KeySize: 4, + ValueSize: 4, + MaxEntries: 1, + Flags: unix.BPF_F_INNER_MAP, + }) + if err != nil { + return internal.ErrNotSupported + } + _ = m.Close() + return nil +}) + +func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + value: valueOut, + } + _, err = internal.BPF(internal.BPF_MAP_LOOKUP_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return wrapMapError(err) +} + +func bpfMapLookupAndDelete(m *internal.FD, key, valueOut internal.Pointer) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + value: valueOut, + } + _, err = internal.BPF(internal.BPF_MAP_LOOKUP_AND_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return wrapMapError(err) +} + +func bpfMapUpdateElem(m *internal.FD, key, valueOut internal.Pointer, flags uint64) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + value: valueOut, + flags: flags, + } + _, err = internal.BPF(internal.BPF_MAP_UPDATE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return wrapMapError(err) +} + +func bpfMapDeleteElem(m *internal.FD, key internal.Pointer) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + } + _, err = internal.BPF(internal.BPF_MAP_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return wrapMapError(err) +} + +func bpfMapGetNextKey(m *internal.FD, key, nextKeyOut internal.Pointer) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + value: nextKeyOut, + } + _, err = internal.BPF(internal.BPF_MAP_GET_NEXT_KEY, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return wrapMapError(err) +} + +func objGetNextID(cmd internal.BPFCmd, start uint32) (uint32, error) { + attr := bpfObjGetNextIDAttr{ + startID: start, + } + _, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return attr.nextID, err +} + +func bpfMapBatch(cmd internal.BPFCmd, m *internal.FD, inBatch, outBatch, keys, values internal.Pointer, count uint32, opts *BatchOptions) (uint32, error) { + fd, err := m.Value() + if err != nil { + return 0, err + } + + attr := bpfBatchMapOpAttr{ + inBatch: inBatch, + outBatch: outBatch, + keys: keys, + values: values, + count: count, + mapFd: fd, + } + if opts != nil { + attr.elemFlags = opts.ElemFlags + attr.flags = opts.Flags + } + _, err = internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + // always return count even on an error, as things like update might partially be fulfilled. + return attr.count, wrapMapError(err) +} + +func wrapMapError(err error) error { + if err == nil { + return nil + } + + if errors.Is(err, unix.ENOENT) { + return internal.SyscallError(ErrKeyNotExist, unix.ENOENT) + } + + if errors.Is(err, unix.EEXIST) { + return internal.SyscallError(ErrKeyExist, unix.EEXIST) + } + + if errors.Is(err, unix.ENOTSUPP) { + return internal.SyscallError(ErrNotSupported, unix.ENOTSUPP) + } + + return err +} + +func bpfMapFreeze(m *internal.FD) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapFreezeAttr{ + mapFd: fd, + } + _, err = internal.BPF(internal.BPF_MAP_FREEZE, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return err +} + +func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) { + var info bpfProgInfo + if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil { + return nil, fmt.Errorf("can't get program info: %w", err) + } + return &info, nil +} + +func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) { + var info bpfMapInfo + err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)) + if err != nil { + return nil, fmt.Errorf("can't get map info: %w", err) + } + return &info, nil +} + +var haveObjName = internal.FeatureTest("object names", "4.15", func() error { + attr := internal.BPFMapCreateAttr{ + MapType: uint32(Array), + KeySize: 4, + ValueSize: 4, + MaxEntries: 1, + MapName: internal.NewBPFObjName("feature_test"), + } + + fd, err := internal.BPFMapCreate(&attr) + if err != nil { + return internal.ErrNotSupported + } + + _ = fd.Close() + return nil +}) + +var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() error { + if err := haveObjName(); err != nil { + return err + } + + attr := internal.BPFMapCreateAttr{ + MapType: uint32(Array), + KeySize: 4, + ValueSize: 4, + MaxEntries: 1, + MapName: internal.NewBPFObjName(".test"), + } + + fd, err := internal.BPFMapCreate(&attr) + if err != nil { + return internal.ErrNotSupported + } + + _ = fd.Close() + return nil +}) + +var haveBatchAPI = internal.FeatureTest("map batch api", "5.6", func() error { + var maxEntries uint32 = 2 + attr := internal.BPFMapCreateAttr{ + MapType: uint32(Hash), + KeySize: 4, + ValueSize: 4, + MaxEntries: maxEntries, + } + + fd, err := internal.BPFMapCreate(&attr) + if err != nil { + return internal.ErrNotSupported + } + defer fd.Close() + keys := []uint32{1, 2} + values := []uint32{3, 4} + kp, _ := marshalPtr(keys, 8) + vp, _ := marshalPtr(values, 8) + nilPtr := internal.NewPointer(nil) + _, err = bpfMapBatch(internal.BPF_MAP_UPDATE_BATCH, fd, nilPtr, nilPtr, kp, vp, maxEntries, nil) + if err != nil { + return internal.ErrNotSupported + } + return nil +}) + +func bpfObjGetFDByID(cmd internal.BPFCmd, id uint32) (*internal.FD, error) { + attr := bpfGetFDByIDAttr{ + id: id, + } + ptr, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return internal.NewFD(uint32(ptr)), err +} diff --git a/vendor/github.com/cilium/ebpf/types.go b/vendor/github.com/cilium/ebpf/types.go new file mode 100644 index 0000000000000..441a82fe4c1c6 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/types.go @@ -0,0 +1,248 @@ +package ebpf + +import ( + "github.com/cilium/ebpf/internal/unix" +) + +//go:generate stringer -output types_string.go -type=MapType,ProgramType,AttachType,PinType + +// MapType indicates the type map structure +// that will be initialized in the kernel. +type MapType uint32 + +// All the various map types that can be created +const ( + UnspecifiedMap MapType = iota + // Hash is a hash map + Hash + // Array is an array map + Array + // ProgramArray - A program array map is a special kind of array map whose map + // values contain only file descriptors referring to other eBPF + // programs. Thus, both the key_size and value_size must be + // exactly four bytes. This map is used in conjunction with the + // TailCall helper. + ProgramArray + // PerfEventArray - A perf event array is used in conjunction with PerfEventRead + // and PerfEventOutput calls, to read the raw bpf_perf_data from the registers. + PerfEventArray + // PerCPUHash - This data structure is useful for people who have high performance + // network needs and can reconcile adds at the end of some cycle, so that + // hashes can be lock free without the use of XAdd, which can be costly. + PerCPUHash + // PerCPUArray - This data structure is useful for people who have high performance + // network needs and can reconcile adds at the end of some cycle, so that + // hashes can be lock free without the use of XAdd, which can be costly. + // Each CPU gets a copy of this hash, the contents of all of which can be reconciled + // later. + PerCPUArray + // StackTrace - This holds whole user and kernel stack traces, it can be retrieved with + // GetStackID + StackTrace + // CGroupArray - This is a very niche structure used to help SKBInCGroup determine + // if an skb is from a socket belonging to a specific cgroup + CGroupArray + // LRUHash - This allows you to create a small hash structure that will purge the + // least recently used items rather than thow an error when you run out of memory + LRUHash + // LRUCPUHash - This is NOT like PerCPUHash, this structure is shared among the CPUs, + // it has more to do with including the CPU id with the LRU calculation so that if a + // particular CPU is using a value over-and-over again, then it will be saved, but if + // a value is being retrieved a lot but sparsely across CPUs it is not as important, basically + // giving weight to CPU locality over overall usage. + LRUCPUHash + // LPMTrie - This is an implementation of Longest-Prefix-Match Trie structure. It is useful, + // for storing things like IP addresses which can be bit masked allowing for keys of differing + // values to refer to the same reference based on their masks. See wikipedia for more details. + LPMTrie + // ArrayOfMaps - Each item in the array is another map. The inner map mustn't be a map of maps + // itself. + ArrayOfMaps + // HashOfMaps - Each item in the hash map is another map. The inner map mustn't be a map of maps + // itself. + HashOfMaps + // DevMap - Specialized map to store references to network devices. + DevMap + // SockMap - Specialized map to store references to sockets. + SockMap + // CPUMap - Specialized map to store references to CPUs. + CPUMap + // XSKMap - Specialized map for XDP programs to store references to open sockets. + XSKMap + // SockHash - Specialized hash to store references to sockets. + SockHash + // CGroupStorage - Special map for CGroups. + CGroupStorage + // ReusePortSockArray - Specialized map to store references to sockets that can be reused. + ReusePortSockArray + // PerCPUCGroupStorage - Special per CPU map for CGroups. + PerCPUCGroupStorage + // Queue - FIFO storage for BPF programs. + Queue + // Stack - LIFO storage for BPF programs. + Stack + // SkStorage - Specialized map for local storage at SK for BPF programs. + SkStorage + // DevMapHash - Hash-based indexing scheme for references to network devices. + DevMapHash + StructOpts + RingBuf + InodeStorage + TaskStorage +) + +// hasPerCPUValue returns true if the Map stores a value per CPU. +func (mt MapType) hasPerCPUValue() bool { + return mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash +} + +// canStoreMap returns true if the map type accepts a map fd +// for update and returns a map id for lookup. +func (mt MapType) canStoreMap() bool { + return mt == ArrayOfMaps || mt == HashOfMaps +} + +// canStoreProgram returns true if the map type accepts a program fd +// for update and returns a program id for lookup. +func (mt MapType) canStoreProgram() bool { + return mt == ProgramArray +} + +// ProgramType of the eBPF program +type ProgramType uint32 + +// eBPF program types +const ( + UnspecifiedProgram ProgramType = iota + SocketFilter + Kprobe + SchedCLS + SchedACT + TracePoint + XDP + PerfEvent + CGroupSKB + CGroupSock + LWTIn + LWTOut + LWTXmit + SockOps + SkSKB + CGroupDevice + SkMsg + RawTracepoint + CGroupSockAddr + LWTSeg6Local + LircMode2 + SkReuseport + FlowDissector + CGroupSysctl + RawTracepointWritable + CGroupSockopt + Tracing + StructOps + Extension + LSM + SkLookup +) + +// AttachType of the eBPF program, needed to differentiate allowed context accesses in +// some newer program types like CGroupSockAddr. Should be set to AttachNone if not required. +// Will cause invalid argument (EINVAL) at program load time if set incorrectly. +type AttachType uint32 + +// AttachNone is an alias for AttachCGroupInetIngress for readability reasons. +const AttachNone AttachType = 0 + +const ( + AttachCGroupInetIngress AttachType = iota + AttachCGroupInetEgress + AttachCGroupInetSockCreate + AttachCGroupSockOps + AttachSkSKBStreamParser + AttachSkSKBStreamVerdict + AttachCGroupDevice + AttachSkMsgVerdict + AttachCGroupInet4Bind + AttachCGroupInet6Bind + AttachCGroupInet4Connect + AttachCGroupInet6Connect + AttachCGroupInet4PostBind + AttachCGroupInet6PostBind + AttachCGroupUDP4Sendmsg + AttachCGroupUDP6Sendmsg + AttachLircMode2 + AttachFlowDissector + AttachCGroupSysctl + AttachCGroupUDP4Recvmsg + AttachCGroupUDP6Recvmsg + AttachCGroupGetsockopt + AttachCGroupSetsockopt + AttachTraceRawTp + AttachTraceFEntry + AttachTraceFExit + AttachModifyReturn + AttachLSMMac + AttachTraceIter + AttachCgroupInet4GetPeername + AttachCgroupInet6GetPeername + AttachCgroupInet4GetSockname + AttachCgroupInet6GetSockname + AttachXDPDevMap + AttachCgroupInetSockRelease + AttachXDPCPUMap + AttachSkLookup + AttachXDP +) + +// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command +type AttachFlags uint32 + +// PinType determines whether a map is pinned into a BPFFS. +type PinType int + +// Valid pin types. +// +// Mirrors enum libbpf_pin_type. +const ( + PinNone PinType = iota + // Pin an object by using its name as the filename. + PinByName +) + +// LoadPinOptions control how a pinned object is loaded. +type LoadPinOptions struct { + // Request a read-only or write-only object. The default is a read-write + // object. Only one of the flags may be set. + ReadOnly bool + WriteOnly bool + + // Raw flags for the syscall. Other fields of this struct take precedence. + Flags uint32 +} + +// Marshal returns a value suitable for BPF_OBJ_GET syscall file_flags parameter. +func (lpo *LoadPinOptions) Marshal() uint32 { + if lpo == nil { + return 0 + } + + flags := lpo.Flags + if lpo.ReadOnly { + flags |= unix.BPF_F_RDONLY + } + if lpo.WriteOnly { + flags |= unix.BPF_F_WRONLY + } + return flags +} + +// BatchOptions batch map operations options +// +// Mirrors libbpf struct bpf_map_batch_opts +// Currently BPF_F_FLAG is the only supported +// flag (for ElemFlags). +type BatchOptions struct { + ElemFlags uint64 + Flags uint64 +} diff --git a/vendor/github.com/cilium/ebpf/types_string.go b/vendor/github.com/cilium/ebpf/types_string.go new file mode 100644 index 0000000000000..c25f7656479bf --- /dev/null +++ b/vendor/github.com/cilium/ebpf/types_string.go @@ -0,0 +1,172 @@ +// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,AttachType,PinType"; DO NOT EDIT. + +package ebpf + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[UnspecifiedMap-0] + _ = x[Hash-1] + _ = x[Array-2] + _ = x[ProgramArray-3] + _ = x[PerfEventArray-4] + _ = x[PerCPUHash-5] + _ = x[PerCPUArray-6] + _ = x[StackTrace-7] + _ = x[CGroupArray-8] + _ = x[LRUHash-9] + _ = x[LRUCPUHash-10] + _ = x[LPMTrie-11] + _ = x[ArrayOfMaps-12] + _ = x[HashOfMaps-13] + _ = x[DevMap-14] + _ = x[SockMap-15] + _ = x[CPUMap-16] + _ = x[XSKMap-17] + _ = x[SockHash-18] + _ = x[CGroupStorage-19] + _ = x[ReusePortSockArray-20] + _ = x[PerCPUCGroupStorage-21] + _ = x[Queue-22] + _ = x[Stack-23] + _ = x[SkStorage-24] + _ = x[DevMapHash-25] + _ = x[StructOpts-26] + _ = x[RingBuf-27] + _ = x[InodeStorage-28] + _ = x[TaskStorage-29] +} + +const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOptsRingBufInodeStorageTaskStorage" + +var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 258, 265, 277, 288} + +func (i MapType) String() string { + if i >= MapType(len(_MapType_index)-1) { + return "MapType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _MapType_name[_MapType_index[i]:_MapType_index[i+1]] +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[UnspecifiedProgram-0] + _ = x[SocketFilter-1] + _ = x[Kprobe-2] + _ = x[SchedCLS-3] + _ = x[SchedACT-4] + _ = x[TracePoint-5] + _ = x[XDP-6] + _ = x[PerfEvent-7] + _ = x[CGroupSKB-8] + _ = x[CGroupSock-9] + _ = x[LWTIn-10] + _ = x[LWTOut-11] + _ = x[LWTXmit-12] + _ = x[SockOps-13] + _ = x[SkSKB-14] + _ = x[CGroupDevice-15] + _ = x[SkMsg-16] + _ = x[RawTracepoint-17] + _ = x[CGroupSockAddr-18] + _ = x[LWTSeg6Local-19] + _ = x[LircMode2-20] + _ = x[SkReuseport-21] + _ = x[FlowDissector-22] + _ = x[CGroupSysctl-23] + _ = x[RawTracepointWritable-24] + _ = x[CGroupSockopt-25] + _ = x[Tracing-26] + _ = x[StructOps-27] + _ = x[Extension-28] + _ = x[LSM-29] + _ = x[SkLookup-30] +} + +const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookup" + +var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265, 274, 283, 286, 294} + +func (i ProgramType) String() string { + if i >= ProgramType(len(_ProgramType_index)-1) { + return "ProgramType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]] +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[AttachNone-0] + _ = x[AttachCGroupInetIngress-0] + _ = x[AttachCGroupInetEgress-1] + _ = x[AttachCGroupInetSockCreate-2] + _ = x[AttachCGroupSockOps-3] + _ = x[AttachSkSKBStreamParser-4] + _ = x[AttachSkSKBStreamVerdict-5] + _ = x[AttachCGroupDevice-6] + _ = x[AttachSkMsgVerdict-7] + _ = x[AttachCGroupInet4Bind-8] + _ = x[AttachCGroupInet6Bind-9] + _ = x[AttachCGroupInet4Connect-10] + _ = x[AttachCGroupInet6Connect-11] + _ = x[AttachCGroupInet4PostBind-12] + _ = x[AttachCGroupInet6PostBind-13] + _ = x[AttachCGroupUDP4Sendmsg-14] + _ = x[AttachCGroupUDP6Sendmsg-15] + _ = x[AttachLircMode2-16] + _ = x[AttachFlowDissector-17] + _ = x[AttachCGroupSysctl-18] + _ = x[AttachCGroupUDP4Recvmsg-19] + _ = x[AttachCGroupUDP6Recvmsg-20] + _ = x[AttachCGroupGetsockopt-21] + _ = x[AttachCGroupSetsockopt-22] + _ = x[AttachTraceRawTp-23] + _ = x[AttachTraceFEntry-24] + _ = x[AttachTraceFExit-25] + _ = x[AttachModifyReturn-26] + _ = x[AttachLSMMac-27] + _ = x[AttachTraceIter-28] + _ = x[AttachCgroupInet4GetPeername-29] + _ = x[AttachCgroupInet6GetPeername-30] + _ = x[AttachCgroupInet4GetSockname-31] + _ = x[AttachCgroupInet6GetSockname-32] + _ = x[AttachXDPDevMap-33] + _ = x[AttachCgroupInetSockRelease-34] + _ = x[AttachXDPCPUMap-35] + _ = x[AttachSkLookup-36] + _ = x[AttachXDP-37] +} + +const _AttachType_name = "AttachNoneAttachCGroupInetEgressAttachCGroupInetSockCreateAttachCGroupSockOpsAttachSkSKBStreamParserAttachSkSKBStreamVerdictAttachCGroupDeviceAttachSkMsgVerdictAttachCGroupInet4BindAttachCGroupInet6BindAttachCGroupInet4ConnectAttachCGroupInet6ConnectAttachCGroupInet4PostBindAttachCGroupInet6PostBindAttachCGroupUDP4SendmsgAttachCGroupUDP6SendmsgAttachLircMode2AttachFlowDissectorAttachCGroupSysctlAttachCGroupUDP4RecvmsgAttachCGroupUDP6RecvmsgAttachCGroupGetsockoptAttachCGroupSetsockoptAttachTraceRawTpAttachTraceFEntryAttachTraceFExitAttachModifyReturnAttachLSMMacAttachTraceIterAttachCgroupInet4GetPeernameAttachCgroupInet6GetPeernameAttachCgroupInet4GetSocknameAttachCgroupInet6GetSocknameAttachXDPDevMapAttachCgroupInetSockReleaseAttachXDPCPUMapAttachSkLookupAttachXDP" + +var _AttachType_index = [...]uint16{0, 10, 32, 58, 77, 100, 124, 142, 160, 181, 202, 226, 250, 275, 300, 323, 346, 361, 380, 398, 421, 444, 466, 488, 504, 521, 537, 555, 567, 582, 610, 638, 666, 694, 709, 736, 751, 765, 774} + +func (i AttachType) String() string { + if i >= AttachType(len(_AttachType_index)-1) { + return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]] +} +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[PinNone-0] + _ = x[PinByName-1] +} + +const _PinType_name = "PinNonePinByName" + +var _PinType_index = [...]uint8{0, 7, 16} + +func (i PinType) String() string { + if i < 0 || i >= PinType(len(_PinType_index)-1) { + return "PinType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _PinType_name[_PinType_index[i]:_PinType_index[i+1]] +} diff --git a/vendor/github.com/containerd/cgroups/README.md b/vendor/github.com/containerd/cgroups/README.md index 69e932a9f7559..d4b09f3d61046 100644 --- a/vendor/github.com/containerd/cgroups/README.md +++ b/vendor/github.com/containerd/cgroups/README.md @@ -1,8 +1,9 @@ # cgroups -[![Build Status](https://travis-ci.org/containerd/cgroups.svg?branch=master)](https://travis-ci.org/containerd/cgroups) - +[![Build Status](https://github.com/containerd/cgroups/workflows/CI/badge.svg)](https://github.com/containerd/cgroups/actions?query=workflow%3ACI) [![codecov](https://codecov.io/gh/containerd/cgroups/branch/master/graph/badge.svg)](https://codecov.io/gh/containerd/cgroups) +[![GoDoc](https://godoc.org/github.com/containerd/cgroups?status.svg)](https://godoc.org/github.com/containerd/cgroups) +[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/cgroups)](https://goreportcard.com/report/github.com/containerd/cgroups) Go package for creating, managing, inspecting, and destroying cgroups. The resources format for settings on the cgroup uses the OCI runtime-spec found @@ -64,7 +65,7 @@ To update the resources applied in the cgroup ```go shares = uint64(200) if err := control.Update(&specs.LinuxResources{ - CPU: &specs.CPU{ + CPU: &specs.LinuxCPU{ Shares: &shares, }, }); err != nil { @@ -110,3 +111,39 @@ err := control.MoveTo(destination) ```go subCgroup, err := control.New("child", resources) ``` + +### Registering for memory events + +This allows you to get notified by an eventfd for v1 memory cgroups events. + +```go +event := cgroups.MemoryThresholdEvent(50 * 1024 * 1024, false) +efd, err := control.RegisterMemoryEvent(event) +``` + +```go +event := cgroups.MemoryPressureEvent(cgroups.MediumPressure, cgroups.DefaultMode) +efd, err := control.RegisterMemoryEvent(event) +``` + +```go +efd, err := control.OOMEventFD() +// or by using RegisterMemoryEvent +event := cgroups.OOMEvent() +efd, err := control.RegisterMemoryEvent(event) +``` + +### Attention + +All static path should not include `/sys/fs/cgroup/` prefix, it should start with your own cgroups name + +## Project details + +Cgroups is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/cgroups/blkio.go b/vendor/github.com/containerd/cgroups/blkio.go index fc1e689cbdcee..a837e19fbffa2 100644 --- a/vendor/github.com/containerd/cgroups/blkio.go +++ b/vendor/github.com/containerd/cgroups/blkio.go @@ -20,23 +20,38 @@ import ( "bufio" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strconv" "strings" + v1 "github.com/containerd/cgroups/stats/v1" specs "github.com/opencontainers/runtime-spec/specs-go" ) -func NewBlkio(root string) *blkioController { - return &blkioController{ - root: filepath.Join(root, string(Blkio)), +// NewBlkio returns a Blkio controller given the root folder of cgroups. +// It may optionally accept other configuration options, such as ProcRoot(path) +func NewBlkio(root string, options ...func(controller *blkioController)) *blkioController { + ctrl := &blkioController{ + root: filepath.Join(root, string(Blkio)), + procRoot: "/proc", + } + for _, opt := range options { + opt(ctrl) + } + return ctrl +} + +// ProcRoot overrides the default location of the "/proc" filesystem +func ProcRoot(path string) func(controller *blkioController) { + return func(c *blkioController) { + c.procRoot = path } } type blkioController struct { - root string + root string + procRoot string } func (b *blkioController) Name() Name { @@ -56,8 +71,8 @@ func (b *blkioController) Create(path string, resources *specs.LinuxResources) e } for _, t := range createBlkioSettings(resources.BlockIO) { if t.value != nil { - if err := ioutil.WriteFile( - filepath.Join(b.Path(path), fmt.Sprintf("blkio.%s", t.name)), + if err := retryingWriteFile( + filepath.Join(b.Path(path), "blkio."+t.name), t.format(t.value), defaultFilePerm, ); err != nil { @@ -72,56 +87,50 @@ func (b *blkioController) Update(path string, resources *specs.LinuxResources) e return b.Create(path, resources) } -func (b *blkioController) Stat(path string, stats *Metrics) error { - stats.Blkio = &BlkIOStat{} - settings := []blkioStatSettings{ - { - name: "throttle.io_serviced", - entry: &stats.Blkio.IoServicedRecursive, - }, - { - name: "throttle.io_service_bytes", - entry: &stats.Blkio.IoServiceBytesRecursive, - }, - } +func (b *blkioController) Stat(path string, stats *v1.Metrics) error { + stats.Blkio = &v1.BlkIOStat{} + + var settings []blkioStatSettings + // Try to read CFQ stats available on all CFQ enabled kernels first - if _, err := os.Lstat(filepath.Join(b.Path(path), fmt.Sprintf("blkio.io_serviced_recursive"))); err == nil { - settings = append(settings, - blkioStatSettings{ + if _, err := os.Lstat(filepath.Join(b.Path(path), "blkio.io_serviced_recursive")); err == nil { + settings = []blkioStatSettings{ + { name: "sectors_recursive", entry: &stats.Blkio.SectorsRecursive, }, - blkioStatSettings{ + { name: "io_service_bytes_recursive", entry: &stats.Blkio.IoServiceBytesRecursive, }, - blkioStatSettings{ + { name: "io_serviced_recursive", entry: &stats.Blkio.IoServicedRecursive, }, - blkioStatSettings{ + { name: "io_queued_recursive", entry: &stats.Blkio.IoQueuedRecursive, }, - blkioStatSettings{ + { name: "io_service_time_recursive", entry: &stats.Blkio.IoServiceTimeRecursive, }, - blkioStatSettings{ + { name: "io_wait_time_recursive", entry: &stats.Blkio.IoWaitTimeRecursive, }, - blkioStatSettings{ + { name: "io_merged_recursive", entry: &stats.Blkio.IoMergedRecursive, }, - blkioStatSettings{ + { name: "time_recursive", entry: &stats.Blkio.IoTimeRecursive, }, - ) + } } - f, err := os.Open("/proc/diskstats") + + f, err := os.Open(filepath.Join(b.procRoot, "diskstats")) if err != nil { return err } @@ -132,6 +141,29 @@ func (b *blkioController) Stat(path string, stats *Metrics) error { return err } + var size int + for _, t := range settings { + if err := b.readEntry(devices, path, t.name, t.entry); err != nil { + return err + } + size += len(*t.entry) + } + if size > 0 { + return nil + } + + // Even the kernel is compiled with the CFQ scheduler, the cgroup may not use + // block devices with the CFQ scheduler. If so, we should fallback to throttle.* files. + settings = []blkioStatSettings{ + { + name: "throttle.io_serviced", + entry: &stats.Blkio.IoServicedRecursive, + }, + { + name: "throttle.io_service_bytes", + entry: &stats.Blkio.IoServiceBytesRecursive, + }, + } for _, t := range settings { if err := b.readEntry(devices, path, t.name, t.entry); err != nil { return err @@ -140,17 +172,14 @@ func (b *blkioController) Stat(path string, stats *Metrics) error { return nil } -func (b *blkioController) readEntry(devices map[deviceKey]string, path, name string, entry *[]*BlkIOEntry) error { - f, err := os.Open(filepath.Join(b.Path(path), fmt.Sprintf("blkio.%s", name))) +func (b *blkioController) readEntry(devices map[deviceKey]string, path, name string, entry *[]*v1.BlkIOEntry) error { + f, err := os.Open(filepath.Join(b.Path(path), "blkio."+name)) if err != nil { return err } defer f.Close() sc := bufio.NewScanner(f) for sc.Scan() { - if err := sc.Err(); err != nil { - return err - } // format: dev type amount fields := strings.FieldsFunc(sc.Text(), splitBlkIOStatLine) if len(fields) < 3 { @@ -158,7 +187,7 @@ func (b *blkioController) readEntry(devices map[deviceKey]string, path, name str // skip total line continue } else { - return fmt.Errorf("Invalid line found while parsing %s: %s", path, sc.Text()) + return fmt.Errorf("invalid line found while parsing %s: %s", path, sc.Text()) } } major, err := strconv.ParseUint(fields[0], 10, 64) @@ -179,7 +208,7 @@ func (b *blkioController) readEntry(devices map[deviceKey]string, path, name str if err != nil { return err } - *entry = append(*entry, &BlkIOEntry{ + *entry = append(*entry, &v1.BlkIOEntry{ Device: devices[deviceKey{major, minor}], Major: major, Minor: minor, @@ -187,35 +216,46 @@ func (b *blkioController) readEntry(devices map[deviceKey]string, path, name str Value: v, }) } - return nil + return sc.Err() } func createBlkioSettings(blkio *specs.LinuxBlockIO) []blkioSettings { - settings := []blkioSettings{ - { - name: "weight", - value: blkio.Weight, - format: uintf, - }, - { - name: "leaf_weight", - value: blkio.LeafWeight, - format: uintf, - }, - } - for _, wd := range blkio.WeightDevice { + settings := []blkioSettings{} + + if blkio.Weight != nil { settings = append(settings, blkioSettings{ - name: "weight_device", - value: wd, - format: weightdev, - }, + name: "weight", + value: blkio.Weight, + format: uintf, + }) + } + if blkio.LeafWeight != nil { + settings = append(settings, blkioSettings{ - name: "leaf_weight_device", - value: wd, - format: weightleafdev, + name: "leaf_weight", + value: blkio.LeafWeight, + format: uintf, }) } + for _, wd := range blkio.WeightDevice { + if wd.Weight != nil { + settings = append(settings, + blkioSettings{ + name: "weight_device", + value: wd, + format: weightdev, + }) + } + if wd.LeafWeight != nil { + settings = append(settings, + blkioSettings{ + name: "leaf_weight_device", + value: wd, + format: weightleafdev, + }) + } + } for _, t := range []struct { name string list []specs.LinuxThrottleDevice @@ -256,7 +296,7 @@ type blkioSettings struct { type blkioStatSettings struct { name string - entry *[]*BlkIOEntry + entry *[]*v1.BlkIOEntry } func uintf(v interface{}) []byte { @@ -265,12 +305,12 @@ func uintf(v interface{}) []byte { func weightdev(v interface{}) []byte { wd := v.(specs.LinuxWeightDevice) - return []byte(fmt.Sprintf("%d:%d %d", wd.Major, wd.Minor, wd.Weight)) + return []byte(fmt.Sprintf("%d:%d %d", wd.Major, wd.Minor, *wd.Weight)) } func weightleafdev(v interface{}) []byte { wd := v.(specs.LinuxWeightDevice) - return []byte(fmt.Sprintf("%d:%d %d", wd.Major, wd.Minor, wd.LeafWeight)) + return []byte(fmt.Sprintf("%d:%d %d", wd.Major, wd.Minor, *wd.LeafWeight)) } func throttleddev(v interface{}) []byte { @@ -316,11 +356,3 @@ func getDevices(r io.Reader) (map[deviceKey]string, error) { } return devices, s.Err() } - -func major(devNumber uint64) uint64 { - return (devNumber >> 8) & 0xfff -} - -func minor(devNumber uint64) uint64 { - return (devNumber & 0xff) | ((devNumber >> 12) & 0xfff00) -} diff --git a/vendor/github.com/containerd/cgroups/cgroup.go b/vendor/github.com/containerd/cgroups/cgroup.go index 7959feb490376..e0e014b282f4f 100644 --- a/vendor/github.com/containerd/cgroups/cgroup.go +++ b/vendor/github.com/containerd/cgroups/cgroup.go @@ -18,59 +18,101 @@ package cgroups import ( "fmt" - "io/ioutil" "os" "path/filepath" "strconv" "strings" "sync" + v1 "github.com/containerd/cgroups/stats/v1" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) // New returns a new control via the cgroup cgroups interface -func New(hierarchy Hierarchy, path Path, resources *specs.LinuxResources) (Cgroup, error) { +func New(hierarchy Hierarchy, path Path, resources *specs.LinuxResources, opts ...InitOpts) (Cgroup, error) { + config := newInitConfig() + for _, o := range opts { + if err := o(config); err != nil { + return nil, err + } + } subsystems, err := hierarchy() if err != nil { return nil, err } + var active []Subsystem for _, s := range subsystems { + // check if subsystem exists if err := initializeSubsystem(s, path, resources); err != nil { + if err == ErrControllerNotActive { + if config.InitCheck != nil { + if skerr := config.InitCheck(s, path, err); skerr != nil { + if skerr != ErrIgnoreSubsystem { + return nil, skerr + } + } + } + continue + } return nil, err } + active = append(active, s) } return &cgroup{ path: path, - subsystems: subsystems, + subsystems: active, }, nil } // Load will load an existing cgroup and allow it to be controlled -func Load(hierarchy Hierarchy, path Path) (Cgroup, error) { +// All static path should not include `/sys/fs/cgroup/` prefix, it should start with your own cgroups name +func Load(hierarchy Hierarchy, path Path, opts ...InitOpts) (Cgroup, error) { + config := newInitConfig() + for _, o := range opts { + if err := o(config); err != nil { + return nil, err + } + } + var activeSubsystems []Subsystem subsystems, err := hierarchy() if err != nil { return nil, err } - // check the the subsystems still exist + // check that the subsystems still exist, and keep only those that actually exist for _, s := range pathers(subsystems) { p, err := path(s.Name()) if err != nil { if os.IsNotExist(errors.Cause(err)) { return nil, ErrCgroupDeleted } + if err == ErrControllerNotActive { + if config.InitCheck != nil { + if skerr := config.InitCheck(s, path, err); skerr != nil { + if skerr != ErrIgnoreSubsystem { + return nil, skerr + } + } + } + continue + } return nil, err } if _, err := os.Lstat(s.Path(p)); err != nil { if os.IsNotExist(err) { - return nil, ErrCgroupDeleted + continue } return nil, err } + activeSubsystems = append(activeSubsystems, s) + } + // if we do not have any active systems then the cgroup is deleted + if len(activeSubsystems) == 0 { + return nil, ErrCgroupDeleted } return &cgroup{ path: path, - subsystems: subsystems, + subsystems: activeSubsystems, }, nil } @@ -126,7 +168,7 @@ func (c *cgroup) add(process Process) error { if err != nil { return err } - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(s.Path(p), cgroupProcs), []byte(strconv.Itoa(process.Pid)), defaultFilePerm, @@ -156,7 +198,7 @@ func (c *cgroup) addTask(process Process) error { if err != nil { return err } - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(s.Path(p), cgroupTasks), []byte(strconv.Itoa(process.Pid)), defaultFilePerm, @@ -174,7 +216,7 @@ func (c *cgroup) Delete() error { if c.err != nil { return c.err } - var errors []string + var errs []string for _, s := range c.subsystems { if d, ok := s.(deleter); ok { sp, err := c.path(s.Name()) @@ -182,7 +224,7 @@ func (c *cgroup) Delete() error { return err } if err := d.Delete(sp); err != nil { - errors = append(errors, string(s.Name())) + errs = append(errs, string(s.Name())) } continue } @@ -193,19 +235,19 @@ func (c *cgroup) Delete() error { } path := p.Path(sp) if err := remove(path); err != nil { - errors = append(errors, path) + errs = append(errs, path) } } } - if len(errors) > 0 { - return fmt.Errorf("cgroups: unable to remove paths %s", strings.Join(errors, ", ")) + if len(errs) > 0 { + return fmt.Errorf("cgroups: unable to remove paths %s", strings.Join(errs, ", ")) } c.err = ErrCgroupDeleted return nil } // Stat returns the current metrics for the cgroup -func (c *cgroup) Stat(handlers ...ErrorHandler) (*Metrics, error) { +func (c *cgroup) Stat(handlers ...ErrorHandler) (*v1.Metrics, error) { c.mu.Lock() defer c.mu.Unlock() if c.err != nil { @@ -215,10 +257,10 @@ func (c *cgroup) Stat(handlers ...ErrorHandler) (*Metrics, error) { handlers = append(handlers, errPassthrough) } var ( - stats = &Metrics{ - CPU: &CPUStat{ - Throttling: &Throttle{}, - Usage: &CPUUsage{}, + stats = &v1.Metrics{ + CPU: &v1.CPUStat{ + Throttling: &v1.Throttle{}, + Usage: &v1.CPUUsage{}, }, } wg = &sync.WaitGroup{} @@ -319,6 +361,49 @@ func (c *cgroup) processes(subsystem Name, recursive bool) ([]Process, error) { return processes, err } +// Tasks returns the tasks running inside the cgroup along +// with the subsystem used, pid, and path +func (c *cgroup) Tasks(subsystem Name, recursive bool) ([]Task, error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return nil, c.err + } + return c.tasks(subsystem, recursive) +} + +func (c *cgroup) tasks(subsystem Name, recursive bool) ([]Task, error) { + s := c.getSubsystem(subsystem) + sp, err := c.path(subsystem) + if err != nil { + return nil, err + } + path := s.(pather).Path(sp) + var tasks []Task + err = filepath.Walk(path, func(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !recursive && info.IsDir() { + if p == path { + return nil + } + return filepath.SkipDir + } + dir, name := filepath.Split(p) + if name != cgroupTasks { + return nil + } + procs, err := readTasksPids(dir, subsystem) + if err != nil { + return err + } + tasks = append(tasks, procs...) + return nil + }) + return tasks, err +} + // Freeze freezes the entire cgroup and all the processes inside it func (c *cgroup) Freeze() error { c.mu.Lock() @@ -372,7 +457,26 @@ func (c *cgroup) OOMEventFD() (uintptr, error) { if err != nil { return 0, err } - return s.(*memoryController).OOMEventFD(sp) + return s.(*memoryController).memoryEvent(sp, OOMEvent()) +} + +// RegisterMemoryEvent allows the ability to register for all v1 memory cgroups +// notifications. +func (c *cgroup) RegisterMemoryEvent(event MemoryEvent) (uintptr, error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.err != nil { + return 0, c.err + } + s := c.getSubsystem(Memory) + if s == nil { + return 0, ErrMemoryNotSupported + } + sp, err := c.path(Memory) + if err != nil { + return 0, err + } + return s.(*memoryController).memoryEvent(sp, event) } // State returns the state of the cgroup and its processes @@ -413,6 +517,9 @@ func (c *cgroup) MoveTo(destination Cgroup) error { } for _, p := range processes { if err := destination.Add(p); err != nil { + if strings.Contains(err.Error(), "no such process") { + continue + } return err } } diff --git a/vendor/github.com/containerd/cgroups/control.go b/vendor/github.com/containerd/cgroups/control.go index 63e2df93dd9d2..a4cb9b8324658 100644 --- a/vendor/github.com/containerd/cgroups/control.go +++ b/vendor/github.com/containerd/cgroups/control.go @@ -19,6 +19,7 @@ package cgroups import ( "os" + v1 "github.com/containerd/cgroups/stats/v1" specs "github.com/opencontainers/runtime-spec/specs-go" ) @@ -44,6 +45,15 @@ type Process struct { Path string } +type Task struct { + // Subsystem is the name of the subsystem that the task is in + Subsystem Name + // Pid is the process id of the task + Pid int + // Path is the full path of the subsystem and location that the task is in + Path string +} + // Cgroup handles interactions with the individual groups to perform // actions on them as them main interface to this cgroup package type Cgroup interface { @@ -59,17 +69,22 @@ type Cgroup interface { // subsystems are moved one at a time MoveTo(Cgroup) error // Stat returns the stats for all subsystems in the cgroup - Stat(...ErrorHandler) (*Metrics, error) + Stat(...ErrorHandler) (*v1.Metrics, error) // Update updates all the subsystems with the provided resource changes Update(resources *specs.LinuxResources) error // Processes returns all the processes in a select subsystem for the cgroup Processes(Name, bool) ([]Process, error) + // Tasks returns all the tasks in a select subsystem for the cgroup + Tasks(Name, bool) ([]Task, error) // Freeze freezes or pauses all processes inside the cgroup Freeze() error // Thaw thaw or resumes all processes inside the cgroup Thaw() error // OOMEventFD returns the memory subsystem's event fd for OOM events OOMEventFD() (uintptr, error) + // RegisterMemoryEvent returns the memory subsystems event fd for whatever memory event was + // registered for. Can alternatively register for the oom event with this method. + RegisterMemoryEvent(MemoryEvent) (uintptr, error) // State returns the cgroups current state State() State // Subsystems returns all the subsystems in the cgroup diff --git a/vendor/github.com/containerd/cgroups/cpu.go b/vendor/github.com/containerd/cgroups/cpu.go index 431cd3e51e468..27024f17b826c 100644 --- a/vendor/github.com/containerd/cgroups/cpu.go +++ b/vendor/github.com/containerd/cgroups/cpu.go @@ -18,12 +18,11 @@ package cgroups import ( "bufio" - "fmt" - "io/ioutil" "os" "path/filepath" "strconv" + v1 "github.com/containerd/cgroups/stats/v1" specs "github.com/opencontainers/runtime-spec/specs-go" ) @@ -83,8 +82,8 @@ func (c *cpuController) Create(path string, resources *specs.LinuxResources) err value = []byte(strconv.FormatInt(*t.ivalue, 10)) } if value != nil { - if err := ioutil.WriteFile( - filepath.Join(c.Path(path), fmt.Sprintf("cpu.%s", t.name)), + if err := retryingWriteFile( + filepath.Join(c.Path(path), "cpu."+t.name), value, defaultFilePerm, ); err != nil { @@ -100,7 +99,7 @@ func (c *cpuController) Update(path string, resources *specs.LinuxResources) err return c.Create(path, resources) } -func (c *cpuController) Stat(path string, stats *Metrics) error { +func (c *cpuController) Stat(path string, stats *v1.Metrics) error { f, err := os.Open(filepath.Join(c.Path(path), "cpu.stat")) if err != nil { return err @@ -109,9 +108,6 @@ func (c *cpuController) Stat(path string, stats *Metrics) error { // get or create the cpu field because cpuacct can also set values on this struct sc := bufio.NewScanner(f) for sc.Scan() { - if err := sc.Err(); err != nil { - return err - } key, v, err := parseKV(sc.Text()) if err != nil { return err @@ -125,5 +121,5 @@ func (c *cpuController) Stat(path string, stats *Metrics) error { stats.CPU.Throttling.ThrottledTime = v } } - return nil + return sc.Err() } diff --git a/vendor/github.com/containerd/cgroups/cpuacct.go b/vendor/github.com/containerd/cgroups/cpuacct.go index 42a490a87e7c9..e5fc864bd7546 100644 --- a/vendor/github.com/containerd/cgroups/cpuacct.go +++ b/vendor/github.com/containerd/cgroups/cpuacct.go @@ -22,6 +22,8 @@ import ( "path/filepath" "strconv" "strings" + + v1 "github.com/containerd/cgroups/stats/v1" ) const nanosecondsInSecond = 1000000000 @@ -46,7 +48,7 @@ func (c *cpuacctController) Path(path string) string { return filepath.Join(c.root, path) } -func (c *cpuacctController) Stat(path string, stats *Metrics) error { +func (c *cpuacctController) Stat(path string, stats *v1.Metrics) error { user, kernel, err := c.getUsage(path) if err != nil { return err diff --git a/vendor/github.com/containerd/cgroups/cpuset.go b/vendor/github.com/containerd/cgroups/cpuset.go index f182aa68c100e..3cae173bdd1b4 100644 --- a/vendor/github.com/containerd/cgroups/cpuset.go +++ b/vendor/github.com/containerd/cgroups/cpuset.go @@ -26,7 +26,7 @@ import ( specs "github.com/opencontainers/runtime-spec/specs-go" ) -func NewCputset(root string) *cpusetController { +func NewCpuset(root string) *cpusetController { return &cpusetController{ root: filepath.Join(root, string(Cpuset)), } @@ -57,21 +57,21 @@ func (c *cpusetController) Create(path string, resources *specs.LinuxResources) if resources.CPU != nil { for _, t := range []struct { name string - value *string + value string }{ { name: "cpus", - value: &resources.CPU.Cpus, + value: resources.CPU.Cpus, }, { name: "mems", - value: &resources.CPU.Mems, + value: resources.CPU.Mems, }, } { - if t.value != nil { - if err := ioutil.WriteFile( - filepath.Join(c.Path(path), fmt.Sprintf("cpuset.%s", t.name)), - []byte(*t.value), + if t.value != "" { + if err := retryingWriteFile( + filepath.Join(c.Path(path), "cpuset."+t.name), + []byte(t.value), defaultFilePerm, ); err != nil { return err @@ -134,7 +134,7 @@ func (c *cpusetController) copyIfNeeded(current, parent string) error { return err } if isEmpty(currentCpus) { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(current, "cpuset.cpus"), parentCpus, defaultFilePerm, @@ -143,7 +143,7 @@ func (c *cpusetController) copyIfNeeded(current, parent string) error { } } if isEmpty(currentMems) { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(current, "cpuset.mems"), parentMems, defaultFilePerm, diff --git a/vendor/github.com/containerd/cgroups/devices.go b/vendor/github.com/containerd/cgroups/devices.go index f9a118b22710c..7792566d5ee40 100644 --- a/vendor/github.com/containerd/cgroups/devices.go +++ b/vendor/github.com/containerd/cgroups/devices.go @@ -18,7 +18,6 @@ package cgroups import ( "fmt" - "io/ioutil" "os" "path/filepath" @@ -58,7 +57,10 @@ func (d *devicesController) Create(path string, resources *specs.LinuxResources) if device.Allow { file = allowDeviceFile } - if err := ioutil.WriteFile( + if device.Type == "" { + device.Type = "a" + } + if err := retryingWriteFile( filepath.Join(d.Path(path), file), []byte(deviceString(device)), defaultFilePerm, diff --git a/vendor/github.com/containerd/cgroups/freezer.go b/vendor/github.com/containerd/cgroups/freezer.go index 5e668408a274a..59a7e71283682 100644 --- a/vendor/github.com/containerd/cgroups/freezer.go +++ b/vendor/github.com/containerd/cgroups/freezer.go @@ -50,7 +50,7 @@ func (f *freezerController) Thaw(path string) error { } func (f *freezerController) changeState(path string, state State) error { - return ioutil.WriteFile( + return retryingWriteFile( filepath.Join(f.root, path, "freezer.state"), []byte(strings.ToUpper(string(state))), defaultFilePerm, diff --git a/vendor/github.com/containerd/cgroups/go.mod b/vendor/github.com/containerd/cgroups/go.mod new file mode 100644 index 0000000000000..eed71ffb485e4 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/go.mod @@ -0,0 +1,18 @@ +module github.com/containerd/cgroups + +go 1.13 + +require ( + github.com/cilium/ebpf v0.4.0 + github.com/coreos/go-systemd/v22 v22.1.0 + github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/docker/go-units v0.4.0 + github.com/godbus/dbus/v5 v5.0.3 + github.com/gogo/protobuf v1.3.2 + github.com/opencontainers/runtime-spec v1.0.2 + github.com/pkg/errors v0.9.1 + github.com/sirupsen/logrus v1.7.0 + github.com/stretchr/testify v1.6.1 + github.com/urfave/cli v1.22.2 + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c +) diff --git a/vendor/github.com/containerd/cgroups/hierarchy.go b/vendor/github.com/containerd/cgroups/hierarchy.go index 9221bf3f15c28..ca3f1b9380b75 100644 --- a/vendor/github.com/containerd/cgroups/hierarchy.go +++ b/vendor/github.com/containerd/cgroups/hierarchy.go @@ -16,5 +16,5 @@ package cgroups -// Hierarchy enableds both unified and split hierarchy for cgroups +// Hierarchy enables both unified and split hierarchy for cgroups type Hierarchy func() ([]Subsystem, error) diff --git a/vendor/github.com/containerd/cgroups/hugetlb.go b/vendor/github.com/containerd/cgroups/hugetlb.go index 3718706d7b458..c0eb03b24da3a 100644 --- a/vendor/github.com/containerd/cgroups/hugetlb.go +++ b/vendor/github.com/containerd/cgroups/hugetlb.go @@ -17,12 +17,12 @@ package cgroups import ( - "io/ioutil" "os" "path/filepath" "strconv" "strings" + v1 "github.com/containerd/cgroups/stats/v1" specs "github.com/opencontainers/runtime-spec/specs-go" ) @@ -56,7 +56,7 @@ func (h *hugetlbController) Create(path string, resources *specs.LinuxResources) return err } for _, limit := range resources.HugepageLimits { - if err := ioutil.WriteFile( + if err := retryingWriteFile( filepath.Join(h.Path(path), strings.Join([]string{"hugetlb", limit.Pagesize, "limit_in_bytes"}, ".")), []byte(strconv.FormatUint(limit.Limit, 10)), defaultFilePerm, @@ -67,7 +67,7 @@ func (h *hugetlbController) Create(path string, resources *specs.LinuxResources) return nil } -func (h *hugetlbController) Stat(path string, stats *Metrics) error { +func (h *hugetlbController) Stat(path string, stats *v1.Metrics) error { for _, size := range h.sizes { s, err := h.readSizeStat(path, size) if err != nil { @@ -78,8 +78,8 @@ func (h *hugetlbController) Stat(path string, stats *Metrics) error { return nil } -func (h *hugetlbController) readSizeStat(path, size string) (*HugetlbStat, error) { - s := HugetlbStat{ +func (h *hugetlbController) readSizeStat(path, size string) (*v1.HugetlbStat, error) { + s := v1.HugetlbStat{ Pagesize: size, } for _, t := range []struct { diff --git a/vendor/github.com/containerd/cgroups/memory.go b/vendor/github.com/containerd/cgroups/memory.go index ce15ca2b9a336..e271866ef94fc 100644 --- a/vendor/github.com/containerd/cgroups/memory.go +++ b/vendor/github.com/containerd/cgroups/memory.go @@ -20,26 +20,175 @@ import ( "bufio" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strconv" "strings" - "syscall" + v1 "github.com/containerd/cgroups/stats/v1" + specs "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/sys/unix" +) - specs "github.com/opencontainers/runtime-spec/specs-go" +// MemoryEvent is an interface that V1 memory Cgroup notifications implement. Arg returns the +// file name whose fd should be written to "cgroups.event_control". EventFile returns the name of +// the file that supports the notification api e.g. "memory.usage_in_bytes". +type MemoryEvent interface { + Arg() string + EventFile() string +} + +type memoryThresholdEvent struct { + threshold uint64 + swap bool +} + +// MemoryThresholdEvent returns a new memory threshold event to be used with RegisterMemoryEvent. +// If swap is true, the event will be registered using memory.memsw.usage_in_bytes +func MemoryThresholdEvent(threshold uint64, swap bool) MemoryEvent { + return &memoryThresholdEvent{ + threshold, + swap, + } +} + +func (m *memoryThresholdEvent) Arg() string { + return strconv.FormatUint(m.threshold, 10) +} + +func (m *memoryThresholdEvent) EventFile() string { + if m.swap { + return "memory.memsw.usage_in_bytes" + } + return "memory.usage_in_bytes" +} + +type oomEvent struct{} + +// OOMEvent returns a new oom event to be used with RegisterMemoryEvent. +func OOMEvent() MemoryEvent { + return &oomEvent{} +} + +func (oom *oomEvent) Arg() string { + return "" +} + +func (oom *oomEvent) EventFile() string { + return "memory.oom_control" +} + +type memoryPressureEvent struct { + pressureLevel MemoryPressureLevel + hierarchy EventNotificationMode +} + +// MemoryPressureEvent returns a new memory pressure event to be used with RegisterMemoryEvent. +func MemoryPressureEvent(pressureLevel MemoryPressureLevel, hierarchy EventNotificationMode) MemoryEvent { + return &memoryPressureEvent{ + pressureLevel, + hierarchy, + } +} + +func (m *memoryPressureEvent) Arg() string { + return string(m.pressureLevel) + "," + string(m.hierarchy) +} + +func (m *memoryPressureEvent) EventFile() string { + return "memory.pressure_level" +} + +// MemoryPressureLevel corresponds to the memory pressure levels defined +// for memory cgroups. +type MemoryPressureLevel string + +// The three memory pressure levels are as follows. +// - The "low" level means that the system is reclaiming memory for new +// allocations. Monitoring this reclaiming activity might be useful for +// maintaining cache level. Upon notification, the program (typically +// "Activity Manager") might analyze vmstat and act in advance (i.e. +// prematurely shutdown unimportant services). +// - The "medium" level means that the system is experiencing medium memory +// pressure, the system might be making swap, paging out active file caches, +// etc. Upon this event applications may decide to further analyze +// vmstat/zoneinfo/memcg or internal memory usage statistics and free any +// resources that can be easily reconstructed or re-read from a disk. +// - The "critical" level means that the system is actively thrashing, it is +// about to out of memory (OOM) or even the in-kernel OOM killer is on its +// way to trigger. Applications should do whatever they can to help the +// system. It might be too late to consult with vmstat or any other +// statistics, so it is advisable to take an immediate action. +// "https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt" Section 11 +const ( + LowPressure MemoryPressureLevel = "low" + MediumPressure MemoryPressureLevel = "medium" + CriticalPressure MemoryPressureLevel = "critical" +) + +// EventNotificationMode corresponds to the notification modes +// for the memory cgroups pressure level notifications. +type EventNotificationMode string + +// There are three optional modes that specify different propagation behavior: +// - "default": this is the default behavior specified above. This mode is the +// same as omitting the optional mode parameter, preserved by backwards +// compatibility. +// - "hierarchy": events always propagate up to the root, similar to the default +// behavior, except that propagation continues regardless of whether there are +// event listeners at each level, with the "hierarchy" mode. In the above +// example, groups A, B, and C will receive notification of memory pressure. +// - "local": events are pass-through, i.e. they only receive notifications when +// memory pressure is experienced in the memcg for which the notification is +// registered. In the above example, group C will receive notification if +// registered for "local" notification and the group experiences memory +// pressure. However, group B will never receive notification, regardless if +// there is an event listener for group C or not, if group B is registered for +// local notification. +// "https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt" Section 11 +const ( + DefaultMode EventNotificationMode = "default" + LocalMode EventNotificationMode = "local" + HierarchyMode EventNotificationMode = "hierarchy" ) -func NewMemory(root string) *memoryController { - return &memoryController{ - root: filepath.Join(root, string(Memory)), +// NewMemory returns a Memory controller given the root folder of cgroups. +// It may optionally accept other configuration options, such as IgnoreModules(...) +func NewMemory(root string, options ...func(*memoryController)) *memoryController { + mc := &memoryController{ + root: filepath.Join(root, string(Memory)), + ignored: map[string]struct{}{}, + } + for _, opt := range options { + opt(mc) + } + return mc +} + +// IgnoreModules configure the memory controller to not read memory metrics for some +// module names (e.g. passing "memsw" would avoid all the memory.memsw.* entries) +func IgnoreModules(names ...string) func(*memoryController) { + return func(mc *memoryController) { + for _, name := range names { + mc.ignored[name] = struct{}{} + } + } +} + +// OptionalSwap allows the memory controller to not fail if cgroups is not accounting +// Swap memory (there are no memory.memsw.* entries) +func OptionalSwap() func(*memoryController) { + return func(mc *memoryController) { + _, err := os.Stat(filepath.Join(mc.root, "memory.memsw.usage_in_bytes")) + if os.IsNotExist(err) { + mc.ignored["memsw"] = struct{}{} + } } } type memoryController struct { - root string + root string + ignored map[string]struct{} } func (m *memoryController) Name() Name { @@ -57,21 +206,6 @@ func (m *memoryController) Create(path string, resources *specs.LinuxResources) if resources.Memory == nil { return nil } - if resources.Memory.Kernel != nil { - // Check if kernel memory is enabled - // We have to limit the kernel memory here as it won't be accounted at all - // until a limit is set on the cgroup and limit cannot be set once the - // cgroup has children, or if there are already tasks in the cgroup. - for _, i := range []int64{1, -1} { - if err := ioutil.WriteFile( - filepath.Join(m.Path(path), "memory.kmem.limit_in_bytes"), - []byte(strconv.FormatInt(i, 10)), - defaultFilePerm, - ); err != nil { - return checkEBUSY(err) - } - } - } return m.set(path, getMemorySettings(resources)) } @@ -97,24 +231,34 @@ func (m *memoryController) Update(path string, resources *specs.LinuxResources) return m.set(path, settings) } -func (m *memoryController) Stat(path string, stats *Metrics) error { - f, err := os.Open(filepath.Join(m.Path(path), "memory.stat")) +func (m *memoryController) Stat(path string, stats *v1.Metrics) error { + fMemStat, err := os.Open(filepath.Join(m.Path(path), "memory.stat")) if err != nil { return err } - defer f.Close() - stats.Memory = &MemoryStat{ - Usage: &MemoryEntry{}, - Swap: &MemoryEntry{}, - Kernel: &MemoryEntry{}, - KernelTCP: &MemoryEntry{}, + defer fMemStat.Close() + stats.Memory = &v1.MemoryStat{ + Usage: &v1.MemoryEntry{}, + Swap: &v1.MemoryEntry{}, + Kernel: &v1.MemoryEntry{}, + KernelTCP: &v1.MemoryEntry{}, + } + if err := m.parseStats(fMemStat, stats.Memory); err != nil { + return err + } + + fMemOomControl, err := os.Open(filepath.Join(m.Path(path), "memory.oom_control")) + if err != nil { + return err } - if err := m.parseStats(f, stats.Memory); err != nil { + defer fMemOomControl.Close() + stats.MemoryOomControl = &v1.MemoryOomControl{} + if err := m.parseOomControlStats(fMemOomControl, stats.MemoryOomControl); err != nil { return err } for _, t := range []struct { module string - entry *MemoryEntry + entry *v1.MemoryEntry }{ { module: "", @@ -133,6 +277,9 @@ func (m *memoryController) Stat(path string, stats *Metrics) error { entry: stats.Memory.KernelTCP, }, } { + if _, ok := m.ignored[t.module]; ok { + continue + } for _, tt := range []struct { name string value *uint64 @@ -169,44 +316,13 @@ func (m *memoryController) Stat(path string, stats *Metrics) error { return nil } -func (m *memoryController) OOMEventFD(path string) (uintptr, error) { - root := m.Path(path) - f, err := os.Open(filepath.Join(root, "memory.oom_control")) - if err != nil { - return 0, err - } - defer f.Close() - fd, _, serr := unix.RawSyscall(unix.SYS_EVENTFD2, 0, unix.EFD_CLOEXEC, 0) - if serr != 0 { - return 0, serr - } - if err := writeEventFD(root, f.Fd(), fd); err != nil { - unix.Close(int(fd)) - return 0, err - } - return fd, nil -} - -func writeEventFD(root string, cfd, efd uintptr) error { - f, err := os.OpenFile(filepath.Join(root, "cgroup.event_control"), os.O_WRONLY, 0) - if err != nil { - return err - } - _, err = f.WriteString(fmt.Sprintf("%d %d", efd, cfd)) - f.Close() - return err -} - -func (m *memoryController) parseStats(r io.Reader, stat *MemoryStat) error { +func (m *memoryController) parseStats(r io.Reader, stat *v1.MemoryStat) error { var ( raw = make(map[string]uint64) sc = bufio.NewScanner(r) line int ) for sc.Scan() { - if err := sc.Err(); err != nil { - return err - } key, v, err := parseKV(sc.Text()) if err != nil { return fmt.Errorf("%d: %v", line, err) @@ -214,6 +330,9 @@ func (m *memoryController) parseStats(r io.Reader, stat *MemoryStat) error { raw[key] = v line++ } + if err := sc.Err(); err != nil { + return err + } stat.Cache = raw["cache"] stat.RSS = raw["rss"] stat.RSSHuge = raw["rss_huge"] @@ -249,11 +368,34 @@ func (m *memoryController) parseStats(r io.Reader, stat *MemoryStat) error { return nil } +func (m *memoryController) parseOomControlStats(r io.Reader, stat *v1.MemoryOomControl) error { + var ( + raw = make(map[string]uint64) + sc = bufio.NewScanner(r) + line int + ) + for sc.Scan() { + key, v, err := parseKV(sc.Text()) + if err != nil { + return fmt.Errorf("%d: %v", line, err) + } + raw[key] = v + line++ + } + if err := sc.Err(); err != nil { + return err + } + stat.OomKillDisable = raw["oom_kill_disable"] + stat.UnderOom = raw["under_oom"] + stat.OomKill = raw["oom_kill"] + return nil +} + func (m *memoryController) set(path string, settings []memorySettings) error { for _, t := range settings { if t.value != nil { - if err := ioutil.WriteFile( - filepath.Join(m.Path(path), fmt.Sprintf("memory.%s", t.name)), + if err := retryingWriteFile( + filepath.Join(m.Path(path), "memory."+t.name), []byte(strconv.FormatInt(*t.value, 10)), defaultFilePerm, ); err != nil { @@ -281,6 +423,10 @@ func getMemorySettings(resources *specs.LinuxResources) []memorySettings { name: "limit_in_bytes", value: mem.Limit, }, + { + name: "soft_limit_in_bytes", + value: mem.Reservation, + }, { name: "memsw.limit_in_bytes", value: mem.Swap, @@ -304,18 +450,6 @@ func getMemorySettings(resources *specs.LinuxResources) []memorySettings { } } -func checkEBUSY(err error) error { - if pathErr, ok := err.(*os.PathError); ok { - if errNo, ok := pathErr.Err.(syscall.Errno); ok { - if errNo == unix.EBUSY { - return fmt.Errorf( - "failed to set memory.kmem.limit_in_bytes, because either tasks have already joined this cgroup or it has children") - } - } - } - return err -} - func getOomControlValue(mem *specs.LinuxMemory) *int64 { if mem.DisableOOMKiller != nil && *mem.DisableOOMKiller { i := int64(1) @@ -323,3 +457,24 @@ func getOomControlValue(mem *specs.LinuxMemory) *int64 { } return nil } + +func (m *memoryController) memoryEvent(path string, event MemoryEvent) (uintptr, error) { + root := m.Path(path) + efd, err := unix.Eventfd(0, unix.EFD_CLOEXEC) + if err != nil { + return 0, err + } + evtFile, err := os.Open(filepath.Join(root, event.EventFile())) + if err != nil { + unix.Close(efd) + return 0, err + } + defer evtFile.Close() + data := fmt.Sprintf("%d %d %s", efd, evtFile.Fd(), event.Arg()) + evctlPath := filepath.Join(root, "cgroup.event_control") + if err := retryingWriteFile(evctlPath, []byte(data), 0700); err != nil { + unix.Close(efd) + return 0, err + } + return uintptr(efd), nil +} diff --git a/vendor/github.com/containerd/cgroups/metrics.pb.go b/vendor/github.com/containerd/cgroups/metrics.pb.go deleted file mode 100644 index 6043a8f7db02b..0000000000000 --- a/vendor/github.com/containerd/cgroups/metrics.pb.go +++ /dev/null @@ -1,4288 +0,0 @@ -// Code generated by protoc-gen-gogo. -// source: github.com/containerd/cgroups/metrics.proto -// DO NOT EDIT! - -/* - Package cgroups is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/cgroups/metrics.proto - - It has these top-level messages: - Metrics - HugetlbStat - PidsStat - CPUStat - CPUUsage - Throttle - MemoryStat - MemoryEntry - BlkIOStat - BlkIOEntry - RdmaStat - RdmaEntry -*/ -package cgroups - -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" - -import strings "strings" -import reflect "reflect" - -import io "io" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package - -type Metrics struct { - Hugetlb []*HugetlbStat `protobuf:"bytes,1,rep,name=hugetlb" json:"hugetlb,omitempty"` - Pids *PidsStat `protobuf:"bytes,2,opt,name=pids" json:"pids,omitempty"` - CPU *CPUStat `protobuf:"bytes,3,opt,name=cpu" json:"cpu,omitempty"` - Memory *MemoryStat `protobuf:"bytes,4,opt,name=memory" json:"memory,omitempty"` - Blkio *BlkIOStat `protobuf:"bytes,5,opt,name=blkio" json:"blkio,omitempty"` - Rdma *RdmaStat `protobuf:"bytes,6,opt,name=rdma" json:"rdma,omitempty"` -} - -func (m *Metrics) Reset() { *m = Metrics{} } -func (*Metrics) ProtoMessage() {} -func (*Metrics) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{0} } - -type HugetlbStat struct { - Usage uint64 `protobuf:"varint,1,opt,name=usage,proto3" json:"usage,omitempty"` - Max uint64 `protobuf:"varint,2,opt,name=max,proto3" json:"max,omitempty"` - Failcnt uint64 `protobuf:"varint,3,opt,name=failcnt,proto3" json:"failcnt,omitempty"` - Pagesize string `protobuf:"bytes,4,opt,name=pagesize,proto3" json:"pagesize,omitempty"` -} - -func (m *HugetlbStat) Reset() { *m = HugetlbStat{} } -func (*HugetlbStat) ProtoMessage() {} -func (*HugetlbStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{1} } - -type PidsStat struct { - Current uint64 `protobuf:"varint,1,opt,name=current,proto3" json:"current,omitempty"` - Limit uint64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` -} - -func (m *PidsStat) Reset() { *m = PidsStat{} } -func (*PidsStat) ProtoMessage() {} -func (*PidsStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{2} } - -type CPUStat struct { - Usage *CPUUsage `protobuf:"bytes,1,opt,name=usage" json:"usage,omitempty"` - Throttling *Throttle `protobuf:"bytes,2,opt,name=throttling" json:"throttling,omitempty"` -} - -func (m *CPUStat) Reset() { *m = CPUStat{} } -func (*CPUStat) ProtoMessage() {} -func (*CPUStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{3} } - -type CPUUsage struct { - // values in nanoseconds - Total uint64 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"` - Kernel uint64 `protobuf:"varint,2,opt,name=kernel,proto3" json:"kernel,omitempty"` - User uint64 `protobuf:"varint,3,opt,name=user,proto3" json:"user,omitempty"` - PerCPU []uint64 `protobuf:"varint,4,rep,packed,name=per_cpu,json=perCpu" json:"per_cpu,omitempty"` -} - -func (m *CPUUsage) Reset() { *m = CPUUsage{} } -func (*CPUUsage) ProtoMessage() {} -func (*CPUUsage) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{4} } - -type Throttle struct { - Periods uint64 `protobuf:"varint,1,opt,name=periods,proto3" json:"periods,omitempty"` - ThrottledPeriods uint64 `protobuf:"varint,2,opt,name=throttled_periods,json=throttledPeriods,proto3" json:"throttled_periods,omitempty"` - ThrottledTime uint64 `protobuf:"varint,3,opt,name=throttled_time,json=throttledTime,proto3" json:"throttled_time,omitempty"` -} - -func (m *Throttle) Reset() { *m = Throttle{} } -func (*Throttle) ProtoMessage() {} -func (*Throttle) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{5} } - -type MemoryStat struct { - Cache uint64 `protobuf:"varint,1,opt,name=cache,proto3" json:"cache,omitempty"` - RSS uint64 `protobuf:"varint,2,opt,name=rss,proto3" json:"rss,omitempty"` - RSSHuge uint64 `protobuf:"varint,3,opt,name=rss_huge,json=rssHuge,proto3" json:"rss_huge,omitempty"` - MappedFile uint64 `protobuf:"varint,4,opt,name=mapped_file,json=mappedFile,proto3" json:"mapped_file,omitempty"` - Dirty uint64 `protobuf:"varint,5,opt,name=dirty,proto3" json:"dirty,omitempty"` - Writeback uint64 `protobuf:"varint,6,opt,name=writeback,proto3" json:"writeback,omitempty"` - PgPgIn uint64 `protobuf:"varint,7,opt,name=pg_pg_in,json=pgPgIn,proto3" json:"pg_pg_in,omitempty"` - PgPgOut uint64 `protobuf:"varint,8,opt,name=pg_pg_out,json=pgPgOut,proto3" json:"pg_pg_out,omitempty"` - PgFault uint64 `protobuf:"varint,9,opt,name=pg_fault,json=pgFault,proto3" json:"pg_fault,omitempty"` - PgMajFault uint64 `protobuf:"varint,10,opt,name=pg_maj_fault,json=pgMajFault,proto3" json:"pg_maj_fault,omitempty"` - InactiveAnon uint64 `protobuf:"varint,11,opt,name=inactive_anon,json=inactiveAnon,proto3" json:"inactive_anon,omitempty"` - ActiveAnon uint64 `protobuf:"varint,12,opt,name=active_anon,json=activeAnon,proto3" json:"active_anon,omitempty"` - InactiveFile uint64 `protobuf:"varint,13,opt,name=inactive_file,json=inactiveFile,proto3" json:"inactive_file,omitempty"` - ActiveFile uint64 `protobuf:"varint,14,opt,name=active_file,json=activeFile,proto3" json:"active_file,omitempty"` - Unevictable uint64 `protobuf:"varint,15,opt,name=unevictable,proto3" json:"unevictable,omitempty"` - HierarchicalMemoryLimit uint64 `protobuf:"varint,16,opt,name=hierarchical_memory_limit,json=hierarchicalMemoryLimit,proto3" json:"hierarchical_memory_limit,omitempty"` - HierarchicalSwapLimit uint64 `protobuf:"varint,17,opt,name=hierarchical_swap_limit,json=hierarchicalSwapLimit,proto3" json:"hierarchical_swap_limit,omitempty"` - TotalCache uint64 `protobuf:"varint,18,opt,name=total_cache,json=totalCache,proto3" json:"total_cache,omitempty"` - TotalRSS uint64 `protobuf:"varint,19,opt,name=total_rss,json=totalRss,proto3" json:"total_rss,omitempty"` - TotalRSSHuge uint64 `protobuf:"varint,20,opt,name=total_rss_huge,json=totalRssHuge,proto3" json:"total_rss_huge,omitempty"` - TotalMappedFile uint64 `protobuf:"varint,21,opt,name=total_mapped_file,json=totalMappedFile,proto3" json:"total_mapped_file,omitempty"` - TotalDirty uint64 `protobuf:"varint,22,opt,name=total_dirty,json=totalDirty,proto3" json:"total_dirty,omitempty"` - TotalWriteback uint64 `protobuf:"varint,23,opt,name=total_writeback,json=totalWriteback,proto3" json:"total_writeback,omitempty"` - TotalPgPgIn uint64 `protobuf:"varint,24,opt,name=total_pg_pg_in,json=totalPgPgIn,proto3" json:"total_pg_pg_in,omitempty"` - TotalPgPgOut uint64 `protobuf:"varint,25,opt,name=total_pg_pg_out,json=totalPgPgOut,proto3" json:"total_pg_pg_out,omitempty"` - TotalPgFault uint64 `protobuf:"varint,26,opt,name=total_pg_fault,json=totalPgFault,proto3" json:"total_pg_fault,omitempty"` - TotalPgMajFault uint64 `protobuf:"varint,27,opt,name=total_pg_maj_fault,json=totalPgMajFault,proto3" json:"total_pg_maj_fault,omitempty"` - TotalInactiveAnon uint64 `protobuf:"varint,28,opt,name=total_inactive_anon,json=totalInactiveAnon,proto3" json:"total_inactive_anon,omitempty"` - TotalActiveAnon uint64 `protobuf:"varint,29,opt,name=total_active_anon,json=totalActiveAnon,proto3" json:"total_active_anon,omitempty"` - TotalInactiveFile uint64 `protobuf:"varint,30,opt,name=total_inactive_file,json=totalInactiveFile,proto3" json:"total_inactive_file,omitempty"` - TotalActiveFile uint64 `protobuf:"varint,31,opt,name=total_active_file,json=totalActiveFile,proto3" json:"total_active_file,omitempty"` - TotalUnevictable uint64 `protobuf:"varint,32,opt,name=total_unevictable,json=totalUnevictable,proto3" json:"total_unevictable,omitempty"` - Usage *MemoryEntry `protobuf:"bytes,33,opt,name=usage" json:"usage,omitempty"` - Swap *MemoryEntry `protobuf:"bytes,34,opt,name=swap" json:"swap,omitempty"` - Kernel *MemoryEntry `protobuf:"bytes,35,opt,name=kernel" json:"kernel,omitempty"` - KernelTCP *MemoryEntry `protobuf:"bytes,36,opt,name=kernel_tcp,json=kernelTcp" json:"kernel_tcp,omitempty"` -} - -func (m *MemoryStat) Reset() { *m = MemoryStat{} } -func (*MemoryStat) ProtoMessage() {} -func (*MemoryStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{6} } - -type MemoryEntry struct { - Limit uint64 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` - Usage uint64 `protobuf:"varint,2,opt,name=usage,proto3" json:"usage,omitempty"` - Max uint64 `protobuf:"varint,3,opt,name=max,proto3" json:"max,omitempty"` - Failcnt uint64 `protobuf:"varint,4,opt,name=failcnt,proto3" json:"failcnt,omitempty"` -} - -func (m *MemoryEntry) Reset() { *m = MemoryEntry{} } -func (*MemoryEntry) ProtoMessage() {} -func (*MemoryEntry) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{7} } - -type BlkIOStat struct { - IoServiceBytesRecursive []*BlkIOEntry `protobuf:"bytes,1,rep,name=io_service_bytes_recursive,json=ioServiceBytesRecursive" json:"io_service_bytes_recursive,omitempty"` - IoServicedRecursive []*BlkIOEntry `protobuf:"bytes,2,rep,name=io_serviced_recursive,json=ioServicedRecursive" json:"io_serviced_recursive,omitempty"` - IoQueuedRecursive []*BlkIOEntry `protobuf:"bytes,3,rep,name=io_queued_recursive,json=ioQueuedRecursive" json:"io_queued_recursive,omitempty"` - IoServiceTimeRecursive []*BlkIOEntry `protobuf:"bytes,4,rep,name=io_service_time_recursive,json=ioServiceTimeRecursive" json:"io_service_time_recursive,omitempty"` - IoWaitTimeRecursive []*BlkIOEntry `protobuf:"bytes,5,rep,name=io_wait_time_recursive,json=ioWaitTimeRecursive" json:"io_wait_time_recursive,omitempty"` - IoMergedRecursive []*BlkIOEntry `protobuf:"bytes,6,rep,name=io_merged_recursive,json=ioMergedRecursive" json:"io_merged_recursive,omitempty"` - IoTimeRecursive []*BlkIOEntry `protobuf:"bytes,7,rep,name=io_time_recursive,json=ioTimeRecursive" json:"io_time_recursive,omitempty"` - SectorsRecursive []*BlkIOEntry `protobuf:"bytes,8,rep,name=sectors_recursive,json=sectorsRecursive" json:"sectors_recursive,omitempty"` -} - -func (m *BlkIOStat) Reset() { *m = BlkIOStat{} } -func (*BlkIOStat) ProtoMessage() {} -func (*BlkIOStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{8} } - -type BlkIOEntry struct { - Op string `protobuf:"bytes,1,opt,name=op,proto3" json:"op,omitempty"` - Device string `protobuf:"bytes,2,opt,name=device,proto3" json:"device,omitempty"` - Major uint64 `protobuf:"varint,3,opt,name=major,proto3" json:"major,omitempty"` - Minor uint64 `protobuf:"varint,4,opt,name=minor,proto3" json:"minor,omitempty"` - Value uint64 `protobuf:"varint,5,opt,name=value,proto3" json:"value,omitempty"` -} - -func (m *BlkIOEntry) Reset() { *m = BlkIOEntry{} } -func (*BlkIOEntry) ProtoMessage() {} -func (*BlkIOEntry) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{9} } - -type RdmaStat struct { - Current []*RdmaEntry `protobuf:"bytes,1,rep,name=current" json:"current,omitempty"` - Limit []*RdmaEntry `protobuf:"bytes,2,rep,name=limit" json:"limit,omitempty"` -} - -func (m *RdmaStat) Reset() { *m = RdmaStat{} } -func (*RdmaStat) ProtoMessage() {} -func (*RdmaStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{10} } - -type RdmaEntry struct { - Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` - HcaHandles uint32 `protobuf:"varint,2,opt,name=hca_handles,json=hcaHandles,proto3" json:"hca_handles,omitempty"` - HcaObjects uint32 `protobuf:"varint,3,opt,name=hca_objects,json=hcaObjects,proto3" json:"hca_objects,omitempty"` -} - -func (m *RdmaEntry) Reset() { *m = RdmaEntry{} } -func (*RdmaEntry) ProtoMessage() {} -func (*RdmaEntry) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{11} } - -func init() { - proto.RegisterType((*Metrics)(nil), "io.containerd.cgroups.v1.Metrics") - proto.RegisterType((*HugetlbStat)(nil), "io.containerd.cgroups.v1.HugetlbStat") - proto.RegisterType((*PidsStat)(nil), "io.containerd.cgroups.v1.PidsStat") - proto.RegisterType((*CPUStat)(nil), "io.containerd.cgroups.v1.CPUStat") - proto.RegisterType((*CPUUsage)(nil), "io.containerd.cgroups.v1.CPUUsage") - proto.RegisterType((*Throttle)(nil), "io.containerd.cgroups.v1.Throttle") - proto.RegisterType((*MemoryStat)(nil), "io.containerd.cgroups.v1.MemoryStat") - proto.RegisterType((*MemoryEntry)(nil), "io.containerd.cgroups.v1.MemoryEntry") - proto.RegisterType((*BlkIOStat)(nil), "io.containerd.cgroups.v1.BlkIOStat") - proto.RegisterType((*BlkIOEntry)(nil), "io.containerd.cgroups.v1.BlkIOEntry") - proto.RegisterType((*RdmaStat)(nil), "io.containerd.cgroups.v1.RdmaStat") - proto.RegisterType((*RdmaEntry)(nil), "io.containerd.cgroups.v1.RdmaEntry") -} -func (m *Metrics) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Metrics) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Hugetlb) > 0 { - for _, msg := range m.Hugetlb { - dAtA[i] = 0xa - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if m.Pids != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Pids.Size())) - n1, err := m.Pids.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 - } - if m.CPU != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.CPU.Size())) - n2, err := m.CPU.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 - } - if m.Memory != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Memory.Size())) - n3, err := m.Memory.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n3 - } - if m.Blkio != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Blkio.Size())) - n4, err := m.Blkio.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n4 - } - if m.Rdma != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Rdma.Size())) - n5, err := m.Rdma.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n5 - } - return i, nil -} - -func (m *HugetlbStat) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *HugetlbStat) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Usage != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Usage)) - } - if m.Max != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Max)) - } - if m.Failcnt != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Failcnt)) - } - if len(m.Pagesize) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(len(m.Pagesize))) - i += copy(dAtA[i:], m.Pagesize) - } - return i, nil -} - -func (m *PidsStat) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *PidsStat) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Current != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Current)) - } - if m.Limit != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Limit)) - } - return i, nil -} - -func (m *CPUStat) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *CPUStat) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Usage != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Usage.Size())) - n5, err := m.Usage.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n5 - } - if m.Throttling != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Throttling.Size())) - n6, err := m.Throttling.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n6 - } - return i, nil -} - -func (m *CPUUsage) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *CPUUsage) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Total != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Total)) - } - if m.Kernel != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Kernel)) - } - if m.User != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.User)) - } - if len(m.PerCPU) > 0 { - dAtA8 := make([]byte, len(m.PerCPU)*10) - var j7 int - for _, num := range m.PerCPU { - for num >= 1<<7 { - dAtA8[j7] = uint8(uint64(num)&0x7f | 0x80) - num >>= 7 - j7++ - } - dAtA8[j7] = uint8(num) - j7++ - } - dAtA[i] = 0x22 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(j7)) - i += copy(dAtA[i:], dAtA8[:j7]) - } - return i, nil -} - -func (m *Throttle) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Throttle) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Periods != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Periods)) - } - if m.ThrottledPeriods != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.ThrottledPeriods)) - } - if m.ThrottledTime != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.ThrottledTime)) - } - return i, nil -} - -func (m *MemoryStat) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *MemoryStat) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Cache != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Cache)) - } - if m.RSS != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.RSS)) - } - if m.RSSHuge != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.RSSHuge)) - } - if m.MappedFile != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.MappedFile)) - } - if m.Dirty != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Dirty)) - } - if m.Writeback != 0 { - dAtA[i] = 0x30 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Writeback)) - } - if m.PgPgIn != 0 { - dAtA[i] = 0x38 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.PgPgIn)) - } - if m.PgPgOut != 0 { - dAtA[i] = 0x40 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.PgPgOut)) - } - if m.PgFault != 0 { - dAtA[i] = 0x48 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.PgFault)) - } - if m.PgMajFault != 0 { - dAtA[i] = 0x50 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.PgMajFault)) - } - if m.InactiveAnon != 0 { - dAtA[i] = 0x58 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.InactiveAnon)) - } - if m.ActiveAnon != 0 { - dAtA[i] = 0x60 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.ActiveAnon)) - } - if m.InactiveFile != 0 { - dAtA[i] = 0x68 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.InactiveFile)) - } - if m.ActiveFile != 0 { - dAtA[i] = 0x70 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.ActiveFile)) - } - if m.Unevictable != 0 { - dAtA[i] = 0x78 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Unevictable)) - } - if m.HierarchicalMemoryLimit != 0 { - dAtA[i] = 0x80 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.HierarchicalMemoryLimit)) - } - if m.HierarchicalSwapLimit != 0 { - dAtA[i] = 0x88 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.HierarchicalSwapLimit)) - } - if m.TotalCache != 0 { - dAtA[i] = 0x90 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalCache)) - } - if m.TotalRSS != 0 { - dAtA[i] = 0x98 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalRSS)) - } - if m.TotalRSSHuge != 0 { - dAtA[i] = 0xa0 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalRSSHuge)) - } - if m.TotalMappedFile != 0 { - dAtA[i] = 0xa8 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalMappedFile)) - } - if m.TotalDirty != 0 { - dAtA[i] = 0xb0 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalDirty)) - } - if m.TotalWriteback != 0 { - dAtA[i] = 0xb8 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalWriteback)) - } - if m.TotalPgPgIn != 0 { - dAtA[i] = 0xc0 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgPgIn)) - } - if m.TotalPgPgOut != 0 { - dAtA[i] = 0xc8 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgPgOut)) - } - if m.TotalPgFault != 0 { - dAtA[i] = 0xd0 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgFault)) - } - if m.TotalPgMajFault != 0 { - dAtA[i] = 0xd8 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgMajFault)) - } - if m.TotalInactiveAnon != 0 { - dAtA[i] = 0xe0 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalInactiveAnon)) - } - if m.TotalActiveAnon != 0 { - dAtA[i] = 0xe8 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalActiveAnon)) - } - if m.TotalInactiveFile != 0 { - dAtA[i] = 0xf0 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalInactiveFile)) - } - if m.TotalActiveFile != 0 { - dAtA[i] = 0xf8 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalActiveFile)) - } - if m.TotalUnevictable != 0 { - dAtA[i] = 0x80 - i++ - dAtA[i] = 0x2 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.TotalUnevictable)) - } - if m.Usage != nil { - dAtA[i] = 0x8a - i++ - dAtA[i] = 0x2 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Usage.Size())) - n9, err := m.Usage.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n9 - } - if m.Swap != nil { - dAtA[i] = 0x92 - i++ - dAtA[i] = 0x2 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Swap.Size())) - n10, err := m.Swap.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n10 - } - if m.Kernel != nil { - dAtA[i] = 0x9a - i++ - dAtA[i] = 0x2 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Kernel.Size())) - n11, err := m.Kernel.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n11 - } - if m.KernelTCP != nil { - dAtA[i] = 0xa2 - i++ - dAtA[i] = 0x2 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.KernelTCP.Size())) - n12, err := m.KernelTCP.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n12 - } - return i, nil -} - -func (m *MemoryEntry) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *MemoryEntry) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - - if m.Limit != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Limit)) - } - if m.Usage != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Usage)) - } - if m.Max != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Max)) - } - if m.Failcnt != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Failcnt)) - } - return i, nil -} - -func (m *BlkIOStat) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *BlkIOStat) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.IoServiceBytesRecursive) > 0 { - for _, msg := range m.IoServiceBytesRecursive { - dAtA[i] = 0xa - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.IoServicedRecursive) > 0 { - for _, msg := range m.IoServicedRecursive { - dAtA[i] = 0x12 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.IoQueuedRecursive) > 0 { - for _, msg := range m.IoQueuedRecursive { - dAtA[i] = 0x1a - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.IoServiceTimeRecursive) > 0 { - for _, msg := range m.IoServiceTimeRecursive { - dAtA[i] = 0x22 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.IoWaitTimeRecursive) > 0 { - for _, msg := range m.IoWaitTimeRecursive { - dAtA[i] = 0x2a - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.IoMergedRecursive) > 0 { - for _, msg := range m.IoMergedRecursive { - dAtA[i] = 0x32 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.IoTimeRecursive) > 0 { - for _, msg := range m.IoTimeRecursive { - dAtA[i] = 0x3a - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.SectorsRecursive) > 0 { - for _, msg := range m.SectorsRecursive { - dAtA[i] = 0x42 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - return i, nil -} - -func (m *BlkIOEntry) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *BlkIOEntry) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Op) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintMetrics(dAtA, i, uint64(len(m.Op))) - i += copy(dAtA[i:], m.Op) - } - if len(m.Device) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(len(m.Device))) - i += copy(dAtA[i:], m.Device) - } - if m.Major != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Major)) - } - if m.Minor != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Minor)) - } - if m.Value != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Value)) - } - return i, nil -} - -func (m *RdmaStat) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *RdmaStat) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Current) > 0 { - for _, msg := range m.Current { - dAtA[i] = 0xa - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - if len(m.Limit) > 0 { - for _, msg := range m.Limit { - dAtA[i] = 0x12 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } - return i, nil -} - -func (m *RdmaEntry) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *RdmaEntry) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Device) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintMetrics(dAtA, i, uint64(len(m.Device))) - i += copy(dAtA[i:], m.Device) - } - if m.HcaHandles != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.HcaHandles)) - } - if m.HcaObjects != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.HcaObjects)) - } - return i, nil -} - -func encodeFixed64Metrics(dAtA []byte, offset int, v uint64) int { - dAtA[offset] = uint8(v) - dAtA[offset+1] = uint8(v >> 8) - dAtA[offset+2] = uint8(v >> 16) - dAtA[offset+3] = uint8(v >> 24) - dAtA[offset+4] = uint8(v >> 32) - dAtA[offset+5] = uint8(v >> 40) - dAtA[offset+6] = uint8(v >> 48) - dAtA[offset+7] = uint8(v >> 56) - return offset + 8 -} -func encodeFixed32Metrics(dAtA []byte, offset int, v uint32) int { - dAtA[offset] = uint8(v) - dAtA[offset+1] = uint8(v >> 8) - dAtA[offset+2] = uint8(v >> 16) - dAtA[offset+3] = uint8(v >> 24) - return offset + 4 -} -func encodeVarintMetrics(dAtA []byte, offset int, v uint64) int { - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return offset + 1 -} -func (m *Metrics) Size() (n int) { - var l int - _ = l - if len(m.Hugetlb) > 0 { - for _, e := range m.Hugetlb { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - if m.Pids != nil { - l = m.Pids.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - if m.CPU != nil { - l = m.CPU.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - if m.Memory != nil { - l = m.Memory.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - if m.Blkio != nil { - l = m.Blkio.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - if m.Rdma != nil { - l = m.Rdma.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - return n -} - -func (m *HugetlbStat) Size() (n int) { - var l int - _ = l - if m.Usage != 0 { - n += 1 + sovMetrics(uint64(m.Usage)) - } - if m.Max != 0 { - n += 1 + sovMetrics(uint64(m.Max)) - } - if m.Failcnt != 0 { - n += 1 + sovMetrics(uint64(m.Failcnt)) - } - l = len(m.Pagesize) - if l > 0 { - n += 1 + l + sovMetrics(uint64(l)) - } - return n -} - -func (m *PidsStat) Size() (n int) { - var l int - _ = l - if m.Current != 0 { - n += 1 + sovMetrics(uint64(m.Current)) - } - if m.Limit != 0 { - n += 1 + sovMetrics(uint64(m.Limit)) - } - return n -} - -func (m *CPUStat) Size() (n int) { - var l int - _ = l - if m.Usage != nil { - l = m.Usage.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - if m.Throttling != nil { - l = m.Throttling.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - return n -} - -func (m *CPUUsage) Size() (n int) { - var l int - _ = l - if m.Total != 0 { - n += 1 + sovMetrics(uint64(m.Total)) - } - if m.Kernel != 0 { - n += 1 + sovMetrics(uint64(m.Kernel)) - } - if m.User != 0 { - n += 1 + sovMetrics(uint64(m.User)) - } - if len(m.PerCPU) > 0 { - l = 0 - for _, e := range m.PerCPU { - l += sovMetrics(uint64(e)) - } - n += 1 + sovMetrics(uint64(l)) + l - } - return n -} - -func (m *Throttle) Size() (n int) { - var l int - _ = l - if m.Periods != 0 { - n += 1 + sovMetrics(uint64(m.Periods)) - } - if m.ThrottledPeriods != 0 { - n += 1 + sovMetrics(uint64(m.ThrottledPeriods)) - } - if m.ThrottledTime != 0 { - n += 1 + sovMetrics(uint64(m.ThrottledTime)) - } - return n -} - -func (m *MemoryStat) Size() (n int) { - var l int - _ = l - if m.Cache != 0 { - n += 1 + sovMetrics(uint64(m.Cache)) - } - if m.RSS != 0 { - n += 1 + sovMetrics(uint64(m.RSS)) - } - if m.RSSHuge != 0 { - n += 1 + sovMetrics(uint64(m.RSSHuge)) - } - if m.MappedFile != 0 { - n += 1 + sovMetrics(uint64(m.MappedFile)) - } - if m.Dirty != 0 { - n += 1 + sovMetrics(uint64(m.Dirty)) - } - if m.Writeback != 0 { - n += 1 + sovMetrics(uint64(m.Writeback)) - } - if m.PgPgIn != 0 { - n += 1 + sovMetrics(uint64(m.PgPgIn)) - } - if m.PgPgOut != 0 { - n += 1 + sovMetrics(uint64(m.PgPgOut)) - } - if m.PgFault != 0 { - n += 1 + sovMetrics(uint64(m.PgFault)) - } - if m.PgMajFault != 0 { - n += 1 + sovMetrics(uint64(m.PgMajFault)) - } - if m.InactiveAnon != 0 { - n += 1 + sovMetrics(uint64(m.InactiveAnon)) - } - if m.ActiveAnon != 0 { - n += 1 + sovMetrics(uint64(m.ActiveAnon)) - } - if m.InactiveFile != 0 { - n += 1 + sovMetrics(uint64(m.InactiveFile)) - } - if m.ActiveFile != 0 { - n += 1 + sovMetrics(uint64(m.ActiveFile)) - } - if m.Unevictable != 0 { - n += 1 + sovMetrics(uint64(m.Unevictable)) - } - if m.HierarchicalMemoryLimit != 0 { - n += 2 + sovMetrics(uint64(m.HierarchicalMemoryLimit)) - } - if m.HierarchicalSwapLimit != 0 { - n += 2 + sovMetrics(uint64(m.HierarchicalSwapLimit)) - } - if m.TotalCache != 0 { - n += 2 + sovMetrics(uint64(m.TotalCache)) - } - if m.TotalRSS != 0 { - n += 2 + sovMetrics(uint64(m.TotalRSS)) - } - if m.TotalRSSHuge != 0 { - n += 2 + sovMetrics(uint64(m.TotalRSSHuge)) - } - if m.TotalMappedFile != 0 { - n += 2 + sovMetrics(uint64(m.TotalMappedFile)) - } - if m.TotalDirty != 0 { - n += 2 + sovMetrics(uint64(m.TotalDirty)) - } - if m.TotalWriteback != 0 { - n += 2 + sovMetrics(uint64(m.TotalWriteback)) - } - if m.TotalPgPgIn != 0 { - n += 2 + sovMetrics(uint64(m.TotalPgPgIn)) - } - if m.TotalPgPgOut != 0 { - n += 2 + sovMetrics(uint64(m.TotalPgPgOut)) - } - if m.TotalPgFault != 0 { - n += 2 + sovMetrics(uint64(m.TotalPgFault)) - } - if m.TotalPgMajFault != 0 { - n += 2 + sovMetrics(uint64(m.TotalPgMajFault)) - } - if m.TotalInactiveAnon != 0 { - n += 2 + sovMetrics(uint64(m.TotalInactiveAnon)) - } - if m.TotalActiveAnon != 0 { - n += 2 + sovMetrics(uint64(m.TotalActiveAnon)) - } - if m.TotalInactiveFile != 0 { - n += 2 + sovMetrics(uint64(m.TotalInactiveFile)) - } - if m.TotalActiveFile != 0 { - n += 2 + sovMetrics(uint64(m.TotalActiveFile)) - } - if m.TotalUnevictable != 0 { - n += 2 + sovMetrics(uint64(m.TotalUnevictable)) - } - if m.Usage != nil { - l = m.Usage.Size() - n += 2 + l + sovMetrics(uint64(l)) - } - if m.Swap != nil { - l = m.Swap.Size() - n += 2 + l + sovMetrics(uint64(l)) - } - if m.Kernel != nil { - l = m.Kernel.Size() - n += 2 + l + sovMetrics(uint64(l)) - } - if m.KernelTCP != nil { - l = m.KernelTCP.Size() - n += 2 + l + sovMetrics(uint64(l)) - } - return n -} - -func (m *MemoryEntry) Size() (n int) { - var l int - _ = l - if m.Limit != 0 { - n += 1 + sovMetrics(uint64(m.Limit)) - } - if m.Usage != 0 { - n += 1 + sovMetrics(uint64(m.Usage)) - } - if m.Max != 0 { - n += 1 + sovMetrics(uint64(m.Max)) - } - if m.Failcnt != 0 { - n += 1 + sovMetrics(uint64(m.Failcnt)) - } - return n -} - -func (m *BlkIOStat) Size() (n int) { - var l int - _ = l - if len(m.IoServiceBytesRecursive) > 0 { - for _, e := range m.IoServiceBytesRecursive { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - if len(m.IoServicedRecursive) > 0 { - for _, e := range m.IoServicedRecursive { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - if len(m.IoQueuedRecursive) > 0 { - for _, e := range m.IoQueuedRecursive { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - if len(m.IoServiceTimeRecursive) > 0 { - for _, e := range m.IoServiceTimeRecursive { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - if len(m.IoWaitTimeRecursive) > 0 { - for _, e := range m.IoWaitTimeRecursive { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - if len(m.IoMergedRecursive) > 0 { - for _, e := range m.IoMergedRecursive { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - if len(m.IoTimeRecursive) > 0 { - for _, e := range m.IoTimeRecursive { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - if len(m.SectorsRecursive) > 0 { - for _, e := range m.SectorsRecursive { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - return n -} - -func (m *BlkIOEntry) Size() (n int) { - var l int - _ = l - l = len(m.Op) - if l > 0 { - n += 1 + l + sovMetrics(uint64(l)) - } - l = len(m.Device) - if l > 0 { - n += 1 + l + sovMetrics(uint64(l)) - } - if m.Major != 0 { - n += 1 + sovMetrics(uint64(m.Major)) - } - if m.Minor != 0 { - n += 1 + sovMetrics(uint64(m.Minor)) - } - if m.Value != 0 { - n += 1 + sovMetrics(uint64(m.Value)) - } - return n -} - -func (m *RdmaStat) Size() (n int) { - var l int - _ = l - if len(m.Current) > 0 { - for _, e := range m.Current { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - if len(m.Limit) > 0 { - for _, e := range m.Limit { - l = e.Size() - n += 1 + l + sovMetrics(uint64(l)) - } - } - return n -} - -func (m *RdmaEntry) Size() (n int) { - var l int - _ = l - l = len(m.Device) - if l > 0 { - n += 1 + l + sovMetrics(uint64(l)) - } - if m.HcaHandles != 0 { - n += 1 + sovMetrics(uint64(m.HcaHandles)) - } - if m.HcaObjects != 0 { - n += 1 + sovMetrics(uint64(m.HcaObjects)) - } - return n -} - -func sovMetrics(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n -} -func sozMetrics(x uint64) (n int) { - return sovMetrics(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (this *Metrics) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&Metrics{`, - `Hugetlb:` + strings.Replace(fmt.Sprintf("%v", this.Hugetlb), "HugetlbStat", "HugetlbStat", 1) + `,`, - `Pids:` + strings.Replace(fmt.Sprintf("%v", this.Pids), "PidsStat", "PidsStat", 1) + `,`, - `CPU:` + strings.Replace(fmt.Sprintf("%v", this.CPU), "CPUStat", "CPUStat", 1) + `,`, - `Memory:` + strings.Replace(fmt.Sprintf("%v", this.Memory), "MemoryStat", "MemoryStat", 1) + `,`, - `Blkio:` + strings.Replace(fmt.Sprintf("%v", this.Blkio), "BlkIOStat", "BlkIOStat", 1) + `,`, - `Rdma:` + strings.Replace(fmt.Sprintf("%v", this.Rdma), "RdmaStat", "RdmaStat", 1) + `,`, - `}`, - }, "") - return s -} -func (this *HugetlbStat) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&HugetlbStat{`, - `Usage:` + fmt.Sprintf("%v", this.Usage) + `,`, - `Max:` + fmt.Sprintf("%v", this.Max) + `,`, - `Failcnt:` + fmt.Sprintf("%v", this.Failcnt) + `,`, - `Pagesize:` + fmt.Sprintf("%v", this.Pagesize) + `,`, - `}`, - }, "") - return s -} -func (this *PidsStat) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&PidsStat{`, - `Current:` + fmt.Sprintf("%v", this.Current) + `,`, - `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, - `}`, - }, "") - return s -} -func (this *CPUStat) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&CPUStat{`, - `Usage:` + strings.Replace(fmt.Sprintf("%v", this.Usage), "CPUUsage", "CPUUsage", 1) + `,`, - `Throttling:` + strings.Replace(fmt.Sprintf("%v", this.Throttling), "Throttle", "Throttle", 1) + `,`, - `}`, - }, "") - return s -} -func (this *CPUUsage) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&CPUUsage{`, - `Total:` + fmt.Sprintf("%v", this.Total) + `,`, - `Kernel:` + fmt.Sprintf("%v", this.Kernel) + `,`, - `User:` + fmt.Sprintf("%v", this.User) + `,`, - `PerCPU:` + fmt.Sprintf("%v", this.PerCPU) + `,`, - `}`, - }, "") - return s -} -func (this *Throttle) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&Throttle{`, - `Periods:` + fmt.Sprintf("%v", this.Periods) + `,`, - `ThrottledPeriods:` + fmt.Sprintf("%v", this.ThrottledPeriods) + `,`, - `ThrottledTime:` + fmt.Sprintf("%v", this.ThrottledTime) + `,`, - `}`, - }, "") - return s -} -func (this *MemoryStat) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&MemoryStat{`, - `Cache:` + fmt.Sprintf("%v", this.Cache) + `,`, - `RSS:` + fmt.Sprintf("%v", this.RSS) + `,`, - `RSSHuge:` + fmt.Sprintf("%v", this.RSSHuge) + `,`, - `MappedFile:` + fmt.Sprintf("%v", this.MappedFile) + `,`, - `Dirty:` + fmt.Sprintf("%v", this.Dirty) + `,`, - `Writeback:` + fmt.Sprintf("%v", this.Writeback) + `,`, - `PgPgIn:` + fmt.Sprintf("%v", this.PgPgIn) + `,`, - `PgPgOut:` + fmt.Sprintf("%v", this.PgPgOut) + `,`, - `PgFault:` + fmt.Sprintf("%v", this.PgFault) + `,`, - `PgMajFault:` + fmt.Sprintf("%v", this.PgMajFault) + `,`, - `InactiveAnon:` + fmt.Sprintf("%v", this.InactiveAnon) + `,`, - `ActiveAnon:` + fmt.Sprintf("%v", this.ActiveAnon) + `,`, - `InactiveFile:` + fmt.Sprintf("%v", this.InactiveFile) + `,`, - `ActiveFile:` + fmt.Sprintf("%v", this.ActiveFile) + `,`, - `Unevictable:` + fmt.Sprintf("%v", this.Unevictable) + `,`, - `HierarchicalMemoryLimit:` + fmt.Sprintf("%v", this.HierarchicalMemoryLimit) + `,`, - `HierarchicalSwapLimit:` + fmt.Sprintf("%v", this.HierarchicalSwapLimit) + `,`, - `TotalCache:` + fmt.Sprintf("%v", this.TotalCache) + `,`, - `TotalRSS:` + fmt.Sprintf("%v", this.TotalRSS) + `,`, - `TotalRSSHuge:` + fmt.Sprintf("%v", this.TotalRSSHuge) + `,`, - `TotalMappedFile:` + fmt.Sprintf("%v", this.TotalMappedFile) + `,`, - `TotalDirty:` + fmt.Sprintf("%v", this.TotalDirty) + `,`, - `TotalWriteback:` + fmt.Sprintf("%v", this.TotalWriteback) + `,`, - `TotalPgPgIn:` + fmt.Sprintf("%v", this.TotalPgPgIn) + `,`, - `TotalPgPgOut:` + fmt.Sprintf("%v", this.TotalPgPgOut) + `,`, - `TotalPgFault:` + fmt.Sprintf("%v", this.TotalPgFault) + `,`, - `TotalPgMajFault:` + fmt.Sprintf("%v", this.TotalPgMajFault) + `,`, - `TotalInactiveAnon:` + fmt.Sprintf("%v", this.TotalInactiveAnon) + `,`, - `TotalActiveAnon:` + fmt.Sprintf("%v", this.TotalActiveAnon) + `,`, - `TotalInactiveFile:` + fmt.Sprintf("%v", this.TotalInactiveFile) + `,`, - `TotalActiveFile:` + fmt.Sprintf("%v", this.TotalActiveFile) + `,`, - `TotalUnevictable:` + fmt.Sprintf("%v", this.TotalUnevictable) + `,`, - `Usage:` + strings.Replace(fmt.Sprintf("%v", this.Usage), "MemoryEntry", "MemoryEntry", 1) + `,`, - `Swap:` + strings.Replace(fmt.Sprintf("%v", this.Swap), "MemoryEntry", "MemoryEntry", 1) + `,`, - `Kernel:` + strings.Replace(fmt.Sprintf("%v", this.Kernel), "MemoryEntry", "MemoryEntry", 1) + `,`, - `KernelTCP:` + strings.Replace(fmt.Sprintf("%v", this.KernelTCP), "MemoryEntry", "MemoryEntry", 1) + `,`, - `}`, - }, "") - return s -} -func (this *MemoryEntry) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&MemoryEntry{`, - `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, - `Usage:` + fmt.Sprintf("%v", this.Usage) + `,`, - `Max:` + fmt.Sprintf("%v", this.Max) + `,`, - `Failcnt:` + fmt.Sprintf("%v", this.Failcnt) + `,`, - `}`, - }, "") - return s -} -func (this *BlkIOStat) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&BlkIOStat{`, - `IoServiceBytesRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoServiceBytesRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, - `IoServicedRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoServicedRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, - `IoQueuedRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoQueuedRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, - `IoServiceTimeRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoServiceTimeRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, - `IoWaitTimeRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoWaitTimeRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, - `IoMergedRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoMergedRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, - `IoTimeRecursive:` + strings.Replace(fmt.Sprintf("%v", this.IoTimeRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, - `SectorsRecursive:` + strings.Replace(fmt.Sprintf("%v", this.SectorsRecursive), "BlkIOEntry", "BlkIOEntry", 1) + `,`, - `}`, - }, "") - return s -} -func (this *BlkIOEntry) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&BlkIOEntry{`, - `Op:` + fmt.Sprintf("%v", this.Op) + `,`, - `Device:` + fmt.Sprintf("%v", this.Device) + `,`, - `Major:` + fmt.Sprintf("%v", this.Major) + `,`, - `Minor:` + fmt.Sprintf("%v", this.Minor) + `,`, - `Value:` + fmt.Sprintf("%v", this.Value) + `,`, - `}`, - }, "") - return s -} -func (this *RdmaStat) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&RdmaStat{`, - `Current:` + strings.Replace(fmt.Sprintf("%v", this.Current), "RdmaEntry", "RdmaEntry", 1) + `,`, - `Limit:` + strings.Replace(fmt.Sprintf("%v", this.Limit), "RdmaEntry", "RdmaEntry", 1) + `,`, - `}`, - }, "") - return s -} -func (this *RdmaEntry) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&RdmaEntry{`, - `Device:` + fmt.Sprintf("%v", this.Device) + `,`, - `HcaHandles:` + fmt.Sprintf("%v", this.HcaHandles) + `,`, - `HcaObjects:` + fmt.Sprintf("%v", this.HcaObjects) + `,`, - `}`, - }, "") - return s -} -func valueToStringMetrics(v interface{}) string { - rv := reflect.ValueOf(v) - if rv.IsNil() { - return "nil" - } - pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("*%v", pv) -} -func (m *Metrics) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Metrics: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Metrics: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Hugetlb", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Hugetlb = append(m.Hugetlb, &HugetlbStat{}) - if err := m.Hugetlb[len(m.Hugetlb)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Pids", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Pids == nil { - m.Pids = &PidsStat{} - } - if err := m.Pids.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CPU", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.CPU == nil { - m.CPU = &CPUStat{} - } - if err := m.CPU.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Memory == nil { - m.Memory = &MemoryStat{} - } - if err := m.Memory.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Blkio", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Blkio == nil { - m.Blkio = &BlkIOStat{} - } - if err := m.Blkio.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Rdma", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Rdma == nil { - m.Rdma = &RdmaStat{} - } - if err := m.Rdma.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *HugetlbStat) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: HugetlbStat: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: HugetlbStat: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) - } - m.Usage = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Usage |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType) - } - m.Max = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Max |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Failcnt", wireType) - } - m.Failcnt = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Failcnt |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Pagesize", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Pagesize = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *PidsStat) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: PidsStat: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PidsStat: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) - } - m.Current = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Current |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) - } - m.Limit = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Limit |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CPUStat) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CPUStat: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CPUStat: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Usage == nil { - m.Usage = &CPUUsage{} - } - if err := m.Usage.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Throttling", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Throttling == nil { - m.Throttling = &Throttle{} - } - if err := m.Throttling.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CPUUsage) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CPUUsage: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CPUUsage: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) - } - m.Total = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Total |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Kernel", wireType) - } - m.Kernel = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Kernel |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) - } - m.User = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.User |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType == 0 { - var v uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - m.PerCPU = append(m.PerCPU, v) - } else if wireType == 2 { - var packedLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - packedLen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if packedLen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + packedLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - for iNdEx < postIndex { - var v uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - m.PerCPU = append(m.PerCPU, v) - } - } else { - return fmt.Errorf("proto: wrong wireType = %d for field PerCPU", wireType) - } - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *Throttle) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Throttle: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Throttle: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Periods", wireType) - } - m.Periods = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Periods |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ThrottledPeriods", wireType) - } - m.ThrottledPeriods = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ThrottledPeriods |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ThrottledTime", wireType) - } - m.ThrottledTime = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ThrottledTime |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *MemoryStat) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: MemoryStat: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MemoryStat: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Cache", wireType) - } - m.Cache = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Cache |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field RSS", wireType) - } - m.RSS = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.RSS |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field RSSHuge", wireType) - } - m.RSSHuge = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.RSSHuge |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MappedFile", wireType) - } - m.MappedFile = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.MappedFile |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Dirty", wireType) - } - m.Dirty = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Dirty |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Writeback", wireType) - } - m.Writeback = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Writeback |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 7: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field PgPgIn", wireType) - } - m.PgPgIn = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.PgPgIn |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 8: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field PgPgOut", wireType) - } - m.PgPgOut = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.PgPgOut |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 9: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field PgFault", wireType) - } - m.PgFault = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.PgFault |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 10: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field PgMajFault", wireType) - } - m.PgMajFault = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.PgMajFault |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 11: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field InactiveAnon", wireType) - } - m.InactiveAnon = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.InactiveAnon |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 12: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ActiveAnon", wireType) - } - m.ActiveAnon = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ActiveAnon |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 13: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field InactiveFile", wireType) - } - m.InactiveFile = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.InactiveFile |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 14: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ActiveFile", wireType) - } - m.ActiveFile = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ActiveFile |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 15: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Unevictable", wireType) - } - m.Unevictable = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Unevictable |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 16: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field HierarchicalMemoryLimit", wireType) - } - m.HierarchicalMemoryLimit = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.HierarchicalMemoryLimit |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 17: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field HierarchicalSwapLimit", wireType) - } - m.HierarchicalSwapLimit = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.HierarchicalSwapLimit |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 18: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalCache", wireType) - } - m.TotalCache = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalCache |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 19: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalRSS", wireType) - } - m.TotalRSS = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalRSS |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 20: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalRSSHuge", wireType) - } - m.TotalRSSHuge = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalRSSHuge |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 21: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalMappedFile", wireType) - } - m.TotalMappedFile = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalMappedFile |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 22: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalDirty", wireType) - } - m.TotalDirty = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalDirty |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 23: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalWriteback", wireType) - } - m.TotalWriteback = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalWriteback |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 24: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalPgPgIn", wireType) - } - m.TotalPgPgIn = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalPgPgIn |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 25: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalPgPgOut", wireType) - } - m.TotalPgPgOut = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalPgPgOut |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 26: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalPgFault", wireType) - } - m.TotalPgFault = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalPgFault |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 27: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalPgMajFault", wireType) - } - m.TotalPgMajFault = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalPgMajFault |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 28: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalInactiveAnon", wireType) - } - m.TotalInactiveAnon = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalInactiveAnon |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 29: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalActiveAnon", wireType) - } - m.TotalActiveAnon = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalActiveAnon |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 30: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalInactiveFile", wireType) - } - m.TotalInactiveFile = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalInactiveFile |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 31: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalActiveFile", wireType) - } - m.TotalActiveFile = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalActiveFile |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 32: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TotalUnevictable", wireType) - } - m.TotalUnevictable = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TotalUnevictable |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 33: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Usage == nil { - m.Usage = &MemoryEntry{} - } - if err := m.Usage.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 34: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Swap", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Swap == nil { - m.Swap = &MemoryEntry{} - } - if err := m.Swap.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 35: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Kernel", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Kernel == nil { - m.Kernel = &MemoryEntry{} - } - if err := m.Kernel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 36: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field KernelTCP", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.KernelTCP == nil { - m.KernelTCP = &MemoryEntry{} - } - if err := m.KernelTCP.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *MemoryEntry) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: MemoryEntry: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: MemoryEntry: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) - } - m.Limit = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Limit |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) - } - m.Usage = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Usage |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType) - } - m.Max = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Max |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Failcnt", wireType) - } - m.Failcnt = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Failcnt |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *BlkIOStat) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: BlkIOStat: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: BlkIOStat: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field IoServiceBytesRecursive", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.IoServiceBytesRecursive = append(m.IoServiceBytesRecursive, &BlkIOEntry{}) - if err := m.IoServiceBytesRecursive[len(m.IoServiceBytesRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field IoServicedRecursive", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.IoServicedRecursive = append(m.IoServicedRecursive, &BlkIOEntry{}) - if err := m.IoServicedRecursive[len(m.IoServicedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field IoQueuedRecursive", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.IoQueuedRecursive = append(m.IoQueuedRecursive, &BlkIOEntry{}) - if err := m.IoQueuedRecursive[len(m.IoQueuedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field IoServiceTimeRecursive", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.IoServiceTimeRecursive = append(m.IoServiceTimeRecursive, &BlkIOEntry{}) - if err := m.IoServiceTimeRecursive[len(m.IoServiceTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field IoWaitTimeRecursive", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.IoWaitTimeRecursive = append(m.IoWaitTimeRecursive, &BlkIOEntry{}) - if err := m.IoWaitTimeRecursive[len(m.IoWaitTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field IoMergedRecursive", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.IoMergedRecursive = append(m.IoMergedRecursive, &BlkIOEntry{}) - if err := m.IoMergedRecursive[len(m.IoMergedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 7: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field IoTimeRecursive", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.IoTimeRecursive = append(m.IoTimeRecursive, &BlkIOEntry{}) - if err := m.IoTimeRecursive[len(m.IoTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 8: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SectorsRecursive", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.SectorsRecursive = append(m.SectorsRecursive, &BlkIOEntry{}) - if err := m.SectorsRecursive[len(m.SectorsRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *BlkIOEntry) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: BlkIOEntry: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: BlkIOEntry: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Op", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Op = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Device = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Major", wireType) - } - m.Major = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Major |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Minor", wireType) - } - m.Minor = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Minor |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) - } - m.Value = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Value |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *RdmaStat) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RdmaStat: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RdmaStat: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Current = append(m.Current, &RdmaEntry{}) - if err := m.Current[len(m.Current)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Limit = append(m.Limit, &RdmaEntry{}) - if err := m.Limit[len(m.Limit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *RdmaEntry) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RdmaEntry: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RdmaEntry: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthMetrics - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Device = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field HcaHandles", wireType) - } - m.HcaHandles = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.HcaHandles |= (uint32(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field HcaObjects", wireType) - } - m.HcaObjects = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowMetrics - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.HcaObjects |= (uint32(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipMetrics(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthMetrics - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} - -func skipMetrics(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowMetrics - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowMetrics - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - return iNdEx, nil - case 1: - iNdEx += 8 - return iNdEx, nil - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowMetrics - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - iNdEx += length - if length < 0 { - return 0, ErrInvalidLengthMetrics - } - return iNdEx, nil - case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowMetrics - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipMetrics(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil - case 4: - return iNdEx, nil - case 5: - iNdEx += 4 - return iNdEx, nil - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - } - panic("unreachable") -} - -var ( - ErrInvalidLengthMetrics = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowMetrics = fmt.Errorf("proto: integer overflow") -) - -func init() { proto.RegisterFile("github.com/containerd/cgroups/metrics.proto", fileDescriptorMetrics) } - -var fileDescriptorMetrics = []byte{ - // 1325 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x57, 0x4d, 0x6f, 0x1b, 0xb7, - 0x16, 0x8d, 0xac, 0xb1, 0x3e, 0xae, 0x6c, 0xc7, 0xa6, 0x13, 0x67, 0xec, 0x97, 0x27, 0x29, 0xb2, - 0xfd, 0x9e, 0x5b, 0x03, 0x32, 0x9a, 0x02, 0x41, 0x93, 0xa6, 0x28, 0x22, 0xb7, 0x41, 0x83, 0xd6, - 0x88, 0x32, 0xb2, 0x91, 0x76, 0x35, 0x18, 0x8d, 0x98, 0x31, 0xe3, 0xd1, 0x70, 0xc2, 0xe1, 0xc8, - 0x71, 0x57, 0xdd, 0xf5, 0x37, 0xf5, 0x1f, 0x64, 0xd9, 0x4d, 0x81, 0x76, 0x63, 0x34, 0xfa, 0x25, - 0x05, 0x2f, 0xe7, 0x4b, 0x49, 0xdc, 0x40, 0xbb, 0xb9, 0xbc, 0xe7, 0x1c, 0x5e, 0x5e, 0x1e, 0x8a, - 0x14, 0xec, 0x7b, 0x4c, 0x9e, 0xc6, 0xc3, 0xae, 0xcb, 0xc7, 0x07, 0x2e, 0x0f, 0xa4, 0xc3, 0x02, - 0x2a, 0x46, 0x07, 0xae, 0x27, 0x78, 0x1c, 0x46, 0x07, 0x63, 0x2a, 0x05, 0x73, 0xa3, 0x6e, 0x28, - 0xb8, 0xe4, 0xc4, 0x64, 0xbc, 0x9b, 0x83, 0xba, 0x09, 0xa8, 0x3b, 0xf9, 0x6c, 0xeb, 0x86, 0xc7, - 0x3d, 0x8e, 0xa0, 0x03, 0xf5, 0xa5, 0xf1, 0x9d, 0xdf, 0x16, 0xa0, 0x7a, 0xa4, 0x15, 0xc8, 0xd7, - 0x50, 0x3d, 0x8d, 0x3d, 0x2a, 0xfd, 0xa1, 0x59, 0x6a, 0x97, 0xf7, 0x1a, 0x77, 0x77, 0xbb, 0x57, - 0xa9, 0x75, 0xbf, 0xd3, 0xc0, 0x81, 0x74, 0xa4, 0x95, 0xb2, 0xc8, 0x3d, 0x30, 0x42, 0x36, 0x8a, - 0xcc, 0x85, 0x76, 0x69, 0xaf, 0x71, 0xb7, 0x73, 0x35, 0xbb, 0xcf, 0x46, 0x11, 0x52, 0x11, 0x4f, - 0x1e, 0x42, 0xd9, 0x0d, 0x63, 0xb3, 0x8c, 0xb4, 0x3b, 0x57, 0xd3, 0x0e, 0xfb, 0x27, 0x8a, 0xd5, - 0xab, 0x4e, 0x2f, 0x5b, 0xe5, 0xc3, 0xfe, 0x89, 0xa5, 0x68, 0xe4, 0x21, 0x54, 0xc6, 0x74, 0xcc, - 0xc5, 0x85, 0x69, 0xa0, 0xc0, 0xce, 0xd5, 0x02, 0x47, 0x88, 0xc3, 0x99, 0x13, 0x0e, 0xb9, 0x0f, - 0x8b, 0x43, 0xff, 0x8c, 0x71, 0x73, 0x11, 0xc9, 0xdb, 0x57, 0x93, 0x7b, 0xfe, 0xd9, 0x93, 0xa7, - 0xc8, 0xd5, 0x8c, 0xce, 0x19, 0x34, 0x0a, 0x6d, 0x20, 0x37, 0x60, 0x31, 0x8e, 0x1c, 0x8f, 0x9a, - 0xa5, 0x76, 0x69, 0xcf, 0xb0, 0x74, 0x40, 0x56, 0xa1, 0x3c, 0x76, 0x5e, 0x63, 0x4b, 0x0c, 0x4b, - 0x7d, 0x12, 0x13, 0xaa, 0x2f, 0x1c, 0xe6, 0xbb, 0x81, 0xc4, 0x15, 0x1b, 0x56, 0x1a, 0x92, 0x2d, - 0xa8, 0x85, 0x8e, 0x47, 0x23, 0xf6, 0x33, 0xc5, 0xb5, 0xd4, 0xad, 0x2c, 0xee, 0x3c, 0x80, 0x5a, - 0xda, 0x35, 0xa5, 0xe0, 0xc6, 0x42, 0xd0, 0x40, 0x26, 0x73, 0xa5, 0xa1, 0xaa, 0xc1, 0x67, 0x63, - 0x26, 0x93, 0xf9, 0x74, 0xd0, 0xf9, 0xb5, 0x04, 0xd5, 0xa4, 0x77, 0xe4, 0x8b, 0x62, 0x95, 0xff, - 0xba, 0x49, 0x87, 0xfd, 0x93, 0x13, 0x85, 0x4c, 0x57, 0xd2, 0x03, 0x90, 0xa7, 0x82, 0x4b, 0xe9, - 0xb3, 0xc0, 0xfb, 0xf8, 0x1e, 0x1f, 0x6b, 0x2c, 0xb5, 0x0a, 0xac, 0xce, 0x2b, 0xa8, 0xa5, 0xb2, - 0xaa, 0x56, 0xc9, 0xa5, 0xe3, 0xa7, 0xfd, 0xc2, 0x80, 0x6c, 0x40, 0xe5, 0x8c, 0x8a, 0x80, 0xfa, - 0xc9, 0x12, 0x92, 0x88, 0x10, 0x30, 0xe2, 0x88, 0x8a, 0xa4, 0x65, 0xf8, 0x4d, 0xb6, 0xa1, 0x1a, - 0x52, 0x61, 0x2b, 0xef, 0x18, 0xed, 0xf2, 0x9e, 0xd1, 0x83, 0xe9, 0x65, 0xab, 0xd2, 0xa7, 0x42, - 0x79, 0xa3, 0x12, 0x52, 0x71, 0x18, 0xc6, 0x9d, 0xd7, 0x50, 0x4b, 0x4b, 0x51, 0x8d, 0x0b, 0xa9, - 0x60, 0x7c, 0x14, 0xa5, 0x8d, 0x4b, 0x42, 0xb2, 0x0f, 0x6b, 0x49, 0x99, 0x74, 0x64, 0xa7, 0x18, - 0x5d, 0xc1, 0x6a, 0x96, 0xe8, 0x27, 0xe0, 0x5d, 0x58, 0xc9, 0xc1, 0x92, 0x8d, 0x69, 0x52, 0xd5, - 0x72, 0x36, 0x7a, 0xcc, 0xc6, 0xb4, 0xf3, 0x57, 0x03, 0x20, 0x77, 0x9c, 0x5a, 0xaf, 0xeb, 0xb8, - 0xa7, 0x99, 0x3f, 0x30, 0x20, 0x9b, 0x50, 0x16, 0x51, 0x32, 0x95, 0x36, 0xb6, 0x35, 0x18, 0x58, - 0x6a, 0x8c, 0xfc, 0x0f, 0x6a, 0x22, 0x8a, 0x6c, 0x75, 0xba, 0xf4, 0x04, 0xbd, 0xc6, 0xf4, 0xb2, - 0x55, 0xb5, 0x06, 0x03, 0x65, 0x3b, 0xab, 0x2a, 0xa2, 0x48, 0x7d, 0x90, 0x16, 0x34, 0xc6, 0x4e, - 0x18, 0xd2, 0x91, 0xfd, 0x82, 0xf9, 0xda, 0x39, 0x86, 0x05, 0x7a, 0xe8, 0x31, 0xf3, 0xb1, 0xd3, - 0x23, 0x26, 0xe4, 0x05, 0x7a, 0xdc, 0xb0, 0x74, 0x40, 0x6e, 0x43, 0xfd, 0x5c, 0x30, 0x49, 0x87, - 0x8e, 0x7b, 0x66, 0x56, 0x30, 0x93, 0x0f, 0x10, 0x13, 0x6a, 0xa1, 0x67, 0x87, 0x9e, 0xcd, 0x02, - 0xb3, 0xaa, 0x77, 0x22, 0xf4, 0xfa, 0xde, 0x93, 0x80, 0x6c, 0x41, 0x5d, 0x67, 0x78, 0x2c, 0xcd, - 0x5a, 0xd2, 0x46, 0xaf, 0xef, 0x3d, 0x8d, 0x25, 0xd9, 0x44, 0xd6, 0x0b, 0x27, 0xf6, 0xa5, 0x59, - 0x4f, 0x53, 0x8f, 0x55, 0x48, 0xda, 0xb0, 0x14, 0x7a, 0xf6, 0xd8, 0x79, 0x99, 0xa4, 0x41, 0x97, - 0x19, 0x7a, 0x47, 0xce, 0x4b, 0x8d, 0xd8, 0x86, 0x65, 0x16, 0x38, 0xae, 0x64, 0x13, 0x6a, 0x3b, - 0x01, 0x0f, 0xcc, 0x06, 0x42, 0x96, 0xd2, 0xc1, 0x47, 0x01, 0x0f, 0xd4, 0x62, 0x8b, 0x90, 0x25, - 0xad, 0x52, 0x00, 0x14, 0x55, 0xb0, 0x1f, 0xcb, 0xb3, 0x2a, 0xd8, 0x91, 0x5c, 0x05, 0x21, 0x2b, - 0x45, 0x15, 0x04, 0xb4, 0xa1, 0x11, 0x07, 0x74, 0xc2, 0x5c, 0xe9, 0x0c, 0x7d, 0x6a, 0x5e, 0x47, - 0x40, 0x71, 0x88, 0x3c, 0x80, 0xcd, 0x53, 0x46, 0x85, 0x23, 0xdc, 0x53, 0xe6, 0x3a, 0xbe, 0xad, - 0x7f, 0x4f, 0x6c, 0x7d, 0xfc, 0x56, 0x11, 0x7f, 0xab, 0x08, 0xd0, 0x4e, 0xf8, 0x41, 0xa5, 0xc9, - 0x3d, 0x98, 0x49, 0xd9, 0xd1, 0xb9, 0x13, 0x26, 0xcc, 0x35, 0x64, 0xde, 0x2c, 0xa6, 0x07, 0xe7, - 0x4e, 0xa8, 0x79, 0x2d, 0x68, 0xe0, 0x29, 0xb1, 0xb5, 0x91, 0x88, 0x2e, 0x1b, 0x87, 0x0e, 0xd1, - 0x4d, 0x9f, 0x40, 0x5d, 0x03, 0x94, 0xa7, 0xd6, 0xd1, 0x33, 0x4b, 0xd3, 0xcb, 0x56, 0xed, 0x58, - 0x0d, 0x2a, 0x63, 0xd5, 0x30, 0x6d, 0x45, 0x11, 0xb9, 0x07, 0x2b, 0x19, 0x54, 0x7b, 0xec, 0x06, - 0xe2, 0x57, 0xa7, 0x97, 0xad, 0xa5, 0x14, 0x8f, 0x46, 0x5b, 0x4a, 0x39, 0xe8, 0xb6, 0x4f, 0x61, - 0x4d, 0xf3, 0x8a, 0x9e, 0xbb, 0x89, 0x95, 0x5c, 0xc7, 0xc4, 0x51, 0x6e, 0xbc, 0xac, 0x5e, 0x6d, - 0xbf, 0x8d, 0x42, 0xbd, 0xdf, 0xa0, 0x07, 0xff, 0x0f, 0x9a, 0x63, 0xe7, 0x4e, 0xbc, 0x85, 0x20, - 0x5d, 0xdb, 0xf3, 0xcc, 0x8e, 0xdb, 0x69, 0xb5, 0x99, 0x29, 0x4d, 0xbd, 0x25, 0x38, 0xda, 0xd7, - 0xce, 0xdc, 0x4d, 0xd5, 0x72, 0x7f, 0x6e, 0xea, 0xcd, 0xcf, 0x50, 0xca, 0xa4, 0x3b, 0x05, 0x2d, - 0xed, 0xc5, 0xad, 0x19, 0x94, 0x76, 0xe3, 0x3e, 0x90, 0x0c, 0x95, 0xbb, 0xf6, 0x3f, 0x85, 0x85, - 0xf6, 0x73, 0xeb, 0x76, 0x61, 0x5d, 0x83, 0x67, 0x0d, 0x7c, 0x1b, 0xd1, 0xba, 0x5f, 0x4f, 0x8a, - 0x2e, 0xce, 0x9a, 0x58, 0x44, 0xff, 0xb7, 0xa0, 0xfd, 0x28, 0xc7, 0xbe, 0xaf, 0x8d, 0x2d, 0x6f, - 0x7e, 0x40, 0x1b, 0x9b, 0xfe, 0xae, 0x36, 0xa2, 0x5b, 0xef, 0x69, 0x23, 0x76, 0x3f, 0xc5, 0x16, - 0xcd, 0xde, 0x4e, 0x7e, 0xf6, 0x54, 0xe2, 0xa4, 0xe0, 0xf8, 0x2f, 0xd3, 0xab, 0xe3, 0x0e, 0xfe, - 0xf6, 0xef, 0x7e, 0xec, 0x9e, 0xfd, 0x36, 0x90, 0xe2, 0x22, 0xbd, 0x3d, 0xee, 0x83, 0xa1, 0x5c, - 0x6e, 0x76, 0xe6, 0xe1, 0x22, 0x85, 0x7c, 0x95, 0x5d, 0x09, 0xdb, 0xf3, 0x90, 0xd3, 0x9b, 0x63, - 0x00, 0xa0, 0xbf, 0x6c, 0xe9, 0x86, 0xe6, 0xce, 0x1c, 0x12, 0xbd, 0xe5, 0xe9, 0x65, 0xab, 0xfe, - 0x3d, 0x92, 0x8f, 0x0f, 0xfb, 0x56, 0x5d, 0xeb, 0x1c, 0xbb, 0x61, 0x87, 0x42, 0xa3, 0x00, 0xcc, - 0xef, 0xdd, 0x52, 0xe1, 0xde, 0xcd, 0x5f, 0x04, 0x0b, 0x1f, 0x78, 0x11, 0x94, 0x3f, 0xf8, 0x22, - 0x30, 0x66, 0x5e, 0x04, 0x9d, 0x3f, 0x16, 0xa1, 0x9e, 0xbd, 0x3b, 0x88, 0x03, 0x5b, 0x8c, 0xdb, - 0x11, 0x15, 0x13, 0xe6, 0x52, 0x7b, 0x78, 0x21, 0x69, 0x64, 0x0b, 0xea, 0xc6, 0x22, 0x62, 0x13, - 0x9a, 0xbc, 0xd9, 0x76, 0x3e, 0xf2, 0x80, 0xd1, 0xbd, 0xb9, 0xc5, 0xf8, 0x40, 0xcb, 0xf4, 0x94, - 0x8a, 0x95, 0x8a, 0x90, 0x1f, 0xe1, 0x66, 0x3e, 0xc5, 0xa8, 0xa0, 0xbe, 0x30, 0x87, 0xfa, 0x7a, - 0xa6, 0x3e, 0xca, 0x95, 0x8f, 0x61, 0x9d, 0x71, 0xfb, 0x55, 0x4c, 0xe3, 0x19, 0xdd, 0xf2, 0x1c, - 0xba, 0x6b, 0x8c, 0x3f, 0x43, 0x7e, 0xae, 0x6a, 0xc3, 0x66, 0xa1, 0x25, 0xea, 0x2e, 0x2e, 0x68, - 0x1b, 0x73, 0x68, 0x6f, 0x64, 0x35, 0xab, 0xbb, 0x3b, 0x9f, 0xe0, 0x27, 0xd8, 0x60, 0xdc, 0x3e, - 0x77, 0x98, 0x7c, 0x57, 0x7d, 0x71, 0xbe, 0x8e, 0x3c, 0x77, 0x98, 0x9c, 0x95, 0xd6, 0x1d, 0x19, - 0x53, 0xe1, 0xcd, 0x74, 0xa4, 0x32, 0x5f, 0x47, 0x8e, 0x90, 0x9f, 0xab, 0xf6, 0x61, 0x8d, 0xf1, - 0x77, 0x6b, 0xad, 0xce, 0xa1, 0x79, 0x9d, 0xf1, 0xd9, 0x3a, 0x9f, 0xc1, 0x5a, 0x44, 0x5d, 0xc9, - 0x45, 0xd1, 0x6d, 0xb5, 0x39, 0x14, 0x57, 0x13, 0x7a, 0x26, 0xd9, 0x99, 0x00, 0xe4, 0x79, 0xb2, - 0x02, 0x0b, 0x3c, 0xc4, 0xa3, 0x53, 0xb7, 0x16, 0x78, 0xa8, 0xde, 0x80, 0x23, 0xf5, 0xb3, 0xa3, - 0x0f, 0x4e, 0xdd, 0x4a, 0x22, 0x75, 0x9e, 0xc6, 0xce, 0x4b, 0x9e, 0x3e, 0x02, 0x75, 0x80, 0xa3, - 0x2c, 0xe0, 0x22, 0x39, 0x3b, 0x3a, 0x50, 0xa3, 0x13, 0xc7, 0x8f, 0x69, 0xfa, 0xe6, 0xc1, 0xa0, - 0x67, 0xbe, 0x79, 0xdb, 0xbc, 0xf6, 0xe7, 0xdb, 0xe6, 0xb5, 0x5f, 0xa6, 0xcd, 0xd2, 0x9b, 0x69, - 0xb3, 0xf4, 0xfb, 0xb4, 0x59, 0xfa, 0x7b, 0xda, 0x2c, 0x0d, 0x2b, 0xf8, 0x7f, 0xe8, 0xf3, 0x7f, - 0x02, 0x00, 0x00, 0xff, 0xff, 0xb2, 0x21, 0x0b, 0xcd, 0x6e, 0x0d, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/cgroups/metrics.proto b/vendor/github.com/containerd/cgroups/metrics.proto deleted file mode 100644 index 642623fcee45d..0000000000000 --- a/vendor/github.com/containerd/cgroups/metrics.proto +++ /dev/null @@ -1,123 +0,0 @@ -syntax = "proto3"; - -package io.containerd.cgroups.v1; - -import "gogoproto/gogo.proto"; - -message Metrics { - repeated HugetlbStat hugetlb = 1; - PidsStat pids = 2; - CPUStat cpu = 3 [(gogoproto.customname) = "CPU"]; - MemoryStat memory = 4; - BlkIOStat blkio = 5; - RdmaStat rdma = 6; -} - -message HugetlbStat { - uint64 usage = 1; - uint64 max = 2; - uint64 failcnt = 3; - string pagesize = 4; -} - -message PidsStat { - uint64 current = 1; - uint64 limit = 2; -} - -message CPUStat { - CPUUsage usage = 1; - Throttle throttling = 2; -} - -message CPUUsage { - // values in nanoseconds - uint64 total = 1; - uint64 kernel = 2; - uint64 user = 3; - repeated uint64 per_cpu = 4 [(gogoproto.customname) = "PerCPU"]; - -} - -message Throttle { - uint64 periods = 1; - uint64 throttled_periods = 2; - uint64 throttled_time = 3; -} - -message MemoryStat { - uint64 cache = 1; - uint64 rss = 2 [(gogoproto.customname) = "RSS"]; - uint64 rss_huge = 3 [(gogoproto.customname) = "RSSHuge"]; - uint64 mapped_file = 4; - uint64 dirty = 5; - uint64 writeback = 6; - uint64 pg_pg_in = 7; - uint64 pg_pg_out = 8; - uint64 pg_fault = 9; - uint64 pg_maj_fault = 10; - uint64 inactive_anon = 11; - uint64 active_anon = 12; - uint64 inactive_file = 13; - uint64 active_file = 14; - uint64 unevictable = 15; - uint64 hierarchical_memory_limit = 16; - uint64 hierarchical_swap_limit = 17; - uint64 total_cache = 18; - uint64 total_rss = 19 [(gogoproto.customname) = "TotalRSS"]; - uint64 total_rss_huge = 20 [(gogoproto.customname) = "TotalRSSHuge"]; - uint64 total_mapped_file = 21; - uint64 total_dirty = 22; - uint64 total_writeback = 23; - uint64 total_pg_pg_in = 24; - uint64 total_pg_pg_out = 25; - uint64 total_pg_fault = 26; - uint64 total_pg_maj_fault = 27; - uint64 total_inactive_anon = 28; - uint64 total_active_anon = 29; - uint64 total_inactive_file = 30; - uint64 total_active_file = 31; - uint64 total_unevictable = 32; - MemoryEntry usage = 33; - MemoryEntry swap = 34; - MemoryEntry kernel = 35; - MemoryEntry kernel_tcp = 36 [(gogoproto.customname) = "KernelTCP"]; - -} - -message MemoryEntry { - uint64 limit = 1; - uint64 usage = 2; - uint64 max = 3; - uint64 failcnt = 4; -} - -message BlkIOStat { - repeated BlkIOEntry io_service_bytes_recursive = 1; - repeated BlkIOEntry io_serviced_recursive = 2; - repeated BlkIOEntry io_queued_recursive = 3; - repeated BlkIOEntry io_service_time_recursive = 4; - repeated BlkIOEntry io_wait_time_recursive = 5; - repeated BlkIOEntry io_merged_recursive = 6; - repeated BlkIOEntry io_time_recursive = 7; - repeated BlkIOEntry sectors_recursive = 8; -} - -message BlkIOEntry { - string op = 1; - string device = 2; - uint64 major = 3; - uint64 minor = 4; - uint64 value = 5; -} - -message RdmaStat { - repeated RdmaEntry current = 1; - repeated RdmaEntry limit = 2; -} - -message RdmaEntry { - string device = 1; - uint32 hca_handles = 2; - uint32 hca_objects = 3; -} diff --git a/vendor/github.com/containerd/cgroups/net_cls.go b/vendor/github.com/containerd/cgroups/net_cls.go index 8f1a2651f84e2..839b06de080b1 100644 --- a/vendor/github.com/containerd/cgroups/net_cls.go +++ b/vendor/github.com/containerd/cgroups/net_cls.go @@ -17,7 +17,6 @@ package cgroups import ( - "io/ioutil" "os" "path/filepath" "strconv" @@ -48,7 +47,7 @@ func (n *netclsController) Create(path string, resources *specs.LinuxResources) return err } if resources.Network != nil && resources.Network.ClassID != nil && *resources.Network.ClassID > 0 { - return ioutil.WriteFile( + return retryingWriteFile( filepath.Join(n.Path(path), "net_cls.classid"), []byte(strconv.FormatUint(uint64(*resources.Network.ClassID), 10)), defaultFilePerm, @@ -56,3 +55,7 @@ func (n *netclsController) Create(path string, resources *specs.LinuxResources) } return nil } + +func (n *netclsController) Update(path string, resources *specs.LinuxResources) error { + return n.Create(path, resources) +} diff --git a/vendor/github.com/containerd/cgroups/net_prio.go b/vendor/github.com/containerd/cgroups/net_prio.go index c77169215c0f4..6362fd084f716 100644 --- a/vendor/github.com/containerd/cgroups/net_prio.go +++ b/vendor/github.com/containerd/cgroups/net_prio.go @@ -18,7 +18,6 @@ package cgroups import ( "fmt" - "io/ioutil" "os" "path/filepath" @@ -49,8 +48,8 @@ func (n *netprioController) Create(path string, resources *specs.LinuxResources) } if resources.Network != nil { for _, prio := range resources.Network.Priorities { - if err := ioutil.WriteFile( - filepath.Join(n.Path(path), "net_prio_ifpriomap"), + if err := retryingWriteFile( + filepath.Join(n.Path(path), "net_prio.ifpriomap"), formatPrio(prio.Name, prio.Priority), defaultFilePerm, ); err != nil { diff --git a/vendor/github.com/containerd/cgroups/opts.go b/vendor/github.com/containerd/cgroups/opts.go new file mode 100644 index 0000000000000..a1449e298d0ef --- /dev/null +++ b/vendor/github.com/containerd/cgroups/opts.go @@ -0,0 +1,61 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package cgroups + +import ( + "github.com/pkg/errors" +) + +var ( + // ErrIgnoreSubsystem allows the specific subsystem to be skipped + ErrIgnoreSubsystem = errors.New("skip subsystem") + // ErrDevicesRequired is returned when the devices subsystem is required but + // does not exist or is not active + ErrDevicesRequired = errors.New("devices subsystem is required") +) + +// InitOpts allows configuration for the creation or loading of a cgroup +type InitOpts func(*InitConfig) error + +// InitConfig provides configuration options for the creation +// or loading of a cgroup and its subsystems +type InitConfig struct { + // InitCheck can be used to check initialization errors from the subsystem + InitCheck InitCheck +} + +func newInitConfig() *InitConfig { + return &InitConfig{ + InitCheck: RequireDevices, + } +} + +// InitCheck allows subsystems errors to be checked when initialized or loaded +type InitCheck func(Subsystem, Path, error) error + +// AllowAny allows any subsystem errors to be skipped +func AllowAny(_ Subsystem, _ Path, _ error) error { + return ErrIgnoreSubsystem +} + +// RequireDevices requires the device subsystem but no others +func RequireDevices(s Subsystem, _ Path, _ error) error { + if s.Name() == Devices { + return ErrDevicesRequired + } + return ErrIgnoreSubsystem +} diff --git a/vendor/github.com/containerd/cgroups/paths.go b/vendor/github.com/containerd/cgroups/paths.go index 455ce857f9835..27197ecad795c 100644 --- a/vendor/github.com/containerd/cgroups/paths.go +++ b/vendor/github.com/containerd/cgroups/paths.go @@ -25,7 +25,7 @@ import ( type Path func(subsystem Name) (string, error) -func RootPath(subsysem Name) (string, error) { +func RootPath(subsystem Name) (string, error) { return "/", nil } @@ -57,10 +57,13 @@ func PidPath(pid int) Path { return existingPath(paths, "") } +// ErrControllerNotActive is returned when a controller is not supported or enabled +var ErrControllerNotActive = errors.New("controller is not supported") + func existingPath(paths map[string]string, suffix string) Path { // localize the paths based on the root mount dest for nested cgroups for n, p := range paths { - dest, err := getCgroupDestination(string(n)) + dest, err := getCgroupDestination(n) if err != nil { return errorPath(err) } @@ -76,8 +79,8 @@ func existingPath(paths map[string]string, suffix string) Path { return func(name Name) (string, error) { root, ok := paths[string(name)] if !ok { - if root, ok = paths[fmt.Sprintf("name=%s", name)]; !ok { - return "", fmt.Errorf("unable to find %q in controller set", name) + if root, ok = paths["name="+string(name)]; !ok { + return "", ErrControllerNotActive } } if suffix != "" { diff --git a/vendor/github.com/containerd/cgroups/pids.go b/vendor/github.com/containerd/cgroups/pids.go index a1cfcb88d4069..ce78e44c18905 100644 --- a/vendor/github.com/containerd/cgroups/pids.go +++ b/vendor/github.com/containerd/cgroups/pids.go @@ -23,6 +23,7 @@ import ( "strconv" "strings" + v1 "github.com/containerd/cgroups/stats/v1" specs "github.com/opencontainers/runtime-spec/specs-go" ) @@ -49,7 +50,7 @@ func (p *pidsController) Create(path string, resources *specs.LinuxResources) er return err } if resources.Pids != nil && resources.Pids.Limit > 0 { - return ioutil.WriteFile( + return retryingWriteFile( filepath.Join(p.Path(path), "pids.max"), []byte(strconv.FormatInt(resources.Pids.Limit, 10)), defaultFilePerm, @@ -62,7 +63,7 @@ func (p *pidsController) Update(path string, resources *specs.LinuxResources) er return p.Create(path, resources) } -func (p *pidsController) Stat(path string, stats *Metrics) error { +func (p *pidsController) Stat(path string, stats *v1.Metrics) error { current, err := readUint(filepath.Join(p.Path(path), "pids.current")) if err != nil { return err @@ -77,7 +78,7 @@ func (p *pidsController) Stat(path string, stats *Metrics) error { return err } } - stats.Pids = &PidsStat{ + stats.Pids = &v1.PidsStat{ Current: current, Limit: max, } diff --git a/vendor/github.com/containerd/cgroups/rdma.go b/vendor/github.com/containerd/cgroups/rdma.go index 4f423d33a0c39..b6f0d416c7f48 100644 --- a/vendor/github.com/containerd/cgroups/rdma.go +++ b/vendor/github.com/containerd/cgroups/rdma.go @@ -24,6 +24,7 @@ import ( "strconv" "strings" + v1 "github.com/containerd/cgroups/stats/v1" specs "github.com/opencontainers/runtime-spec/specs-go" ) @@ -66,7 +67,7 @@ func (p *rdmaController) Create(path string, resources *specs.LinuxResources) er for device, limit := range resources.Rdma { if device != "" && (limit.HcaHandles != nil || limit.HcaObjects != nil) { - return ioutil.WriteFile( + return retryingWriteFile( filepath.Join(p.Path(path), "rdma.max"), []byte(createCmdString(device, &limit)), defaultFilePerm, @@ -80,7 +81,7 @@ func (p *rdmaController) Update(path string, resources *specs.LinuxResources) er return p.Create(path, resources) } -func parseRdmaKV(raw string, entry *RdmaEntry) { +func parseRdmaKV(raw string, entry *v1.RdmaEntry) { var value uint64 var err error @@ -103,13 +104,13 @@ func parseRdmaKV(raw string, entry *RdmaEntry) { } } -func toRdmaEntry(strEntries []string) []*RdmaEntry { - var rdmaEntries []*RdmaEntry +func toRdmaEntry(strEntries []string) []*v1.RdmaEntry { + var rdmaEntries []*v1.RdmaEntry for i := range strEntries { parts := strings.Fields(strEntries[i]) switch len(parts) { case 3: - entry := new(RdmaEntry) + entry := new(v1.RdmaEntry) entry.Device = parts[0] parseRdmaKV(parts[1], entry) parseRdmaKV(parts[2], entry) @@ -122,7 +123,7 @@ func toRdmaEntry(strEntries []string) []*RdmaEntry { return rdmaEntries } -func (p *rdmaController) Stat(path string, stats *Metrics) error { +func (p *rdmaController) Stat(path string, stats *v1.Metrics) error { currentData, err := ioutil.ReadFile(filepath.Join(p.Path(path), "rdma.current")) if err != nil { @@ -145,7 +146,7 @@ func (p *rdmaController) Stat(path string, stats *Metrics) error { currentEntries := toRdmaEntry(currentPerDevices) maxEntries := toRdmaEntry(maxPerDevices) - stats.Rdma = &RdmaStat{ + stats.Rdma = &v1.RdmaStat{ Current: currentEntries, Limit: maxEntries, } diff --git a/vendor/github.com/containerd/cgroups/stats/v1/doc.go b/vendor/github.com/containerd/cgroups/stats/v1/doc.go new file mode 100644 index 0000000000000..23f3cdd4b376f --- /dev/null +++ b/vendor/github.com/containerd/cgroups/stats/v1/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v1 diff --git a/vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.go b/vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.go new file mode 100644 index 0000000000000..6d2d41770b99c --- /dev/null +++ b/vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.go @@ -0,0 +1,6125 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/cgroups/stats/v1/metrics.proto + +package v1 + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Metrics struct { + Hugetlb []*HugetlbStat `protobuf:"bytes,1,rep,name=hugetlb,proto3" json:"hugetlb,omitempty"` + Pids *PidsStat `protobuf:"bytes,2,opt,name=pids,proto3" json:"pids,omitempty"` + CPU *CPUStat `protobuf:"bytes,3,opt,name=cpu,proto3" json:"cpu,omitempty"` + Memory *MemoryStat `protobuf:"bytes,4,opt,name=memory,proto3" json:"memory,omitempty"` + Blkio *BlkIOStat `protobuf:"bytes,5,opt,name=blkio,proto3" json:"blkio,omitempty"` + Rdma *RdmaStat `protobuf:"bytes,6,opt,name=rdma,proto3" json:"rdma,omitempty"` + Network []*NetworkStat `protobuf:"bytes,7,rep,name=network,proto3" json:"network,omitempty"` + CgroupStats *CgroupStats `protobuf:"bytes,8,opt,name=cgroup_stats,json=cgroupStats,proto3" json:"cgroup_stats,omitempty"` + MemoryOomControl *MemoryOomControl `protobuf:"bytes,9,opt,name=memory_oom_control,json=memoryOomControl,proto3" json:"memory_oom_control,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Metrics) Reset() { *m = Metrics{} } +func (*Metrics) ProtoMessage() {} +func (*Metrics) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{0} +} +func (m *Metrics) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Metrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Metrics.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Metrics) XXX_Merge(src proto.Message) { + xxx_messageInfo_Metrics.Merge(m, src) +} +func (m *Metrics) XXX_Size() int { + return m.Size() +} +func (m *Metrics) XXX_DiscardUnknown() { + xxx_messageInfo_Metrics.DiscardUnknown(m) +} + +var xxx_messageInfo_Metrics proto.InternalMessageInfo + +type HugetlbStat struct { + Usage uint64 `protobuf:"varint,1,opt,name=usage,proto3" json:"usage,omitempty"` + Max uint64 `protobuf:"varint,2,opt,name=max,proto3" json:"max,omitempty"` + Failcnt uint64 `protobuf:"varint,3,opt,name=failcnt,proto3" json:"failcnt,omitempty"` + Pagesize string `protobuf:"bytes,4,opt,name=pagesize,proto3" json:"pagesize,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HugetlbStat) Reset() { *m = HugetlbStat{} } +func (*HugetlbStat) ProtoMessage() {} +func (*HugetlbStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{1} +} +func (m *HugetlbStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HugetlbStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HugetlbStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HugetlbStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_HugetlbStat.Merge(m, src) +} +func (m *HugetlbStat) XXX_Size() int { + return m.Size() +} +func (m *HugetlbStat) XXX_DiscardUnknown() { + xxx_messageInfo_HugetlbStat.DiscardUnknown(m) +} + +var xxx_messageInfo_HugetlbStat proto.InternalMessageInfo + +type PidsStat struct { + Current uint64 `protobuf:"varint,1,opt,name=current,proto3" json:"current,omitempty"` + Limit uint64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PidsStat) Reset() { *m = PidsStat{} } +func (*PidsStat) ProtoMessage() {} +func (*PidsStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{2} +} +func (m *PidsStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PidsStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PidsStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PidsStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_PidsStat.Merge(m, src) +} +func (m *PidsStat) XXX_Size() int { + return m.Size() +} +func (m *PidsStat) XXX_DiscardUnknown() { + xxx_messageInfo_PidsStat.DiscardUnknown(m) +} + +var xxx_messageInfo_PidsStat proto.InternalMessageInfo + +type CPUStat struct { + Usage *CPUUsage `protobuf:"bytes,1,opt,name=usage,proto3" json:"usage,omitempty"` + Throttling *Throttle `protobuf:"bytes,2,opt,name=throttling,proto3" json:"throttling,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CPUStat) Reset() { *m = CPUStat{} } +func (*CPUStat) ProtoMessage() {} +func (*CPUStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{3} +} +func (m *CPUStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CPUStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CPUStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CPUStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_CPUStat.Merge(m, src) +} +func (m *CPUStat) XXX_Size() int { + return m.Size() +} +func (m *CPUStat) XXX_DiscardUnknown() { + xxx_messageInfo_CPUStat.DiscardUnknown(m) +} + +var xxx_messageInfo_CPUStat proto.InternalMessageInfo + +type CPUUsage struct { + // values in nanoseconds + Total uint64 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"` + Kernel uint64 `protobuf:"varint,2,opt,name=kernel,proto3" json:"kernel,omitempty"` + User uint64 `protobuf:"varint,3,opt,name=user,proto3" json:"user,omitempty"` + PerCPU []uint64 `protobuf:"varint,4,rep,packed,name=per_cpu,json=perCpu,proto3" json:"per_cpu,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CPUUsage) Reset() { *m = CPUUsage{} } +func (*CPUUsage) ProtoMessage() {} +func (*CPUUsage) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{4} +} +func (m *CPUUsage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CPUUsage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CPUUsage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CPUUsage) XXX_Merge(src proto.Message) { + xxx_messageInfo_CPUUsage.Merge(m, src) +} +func (m *CPUUsage) XXX_Size() int { + return m.Size() +} +func (m *CPUUsage) XXX_DiscardUnknown() { + xxx_messageInfo_CPUUsage.DiscardUnknown(m) +} + +var xxx_messageInfo_CPUUsage proto.InternalMessageInfo + +type Throttle struct { + Periods uint64 `protobuf:"varint,1,opt,name=periods,proto3" json:"periods,omitempty"` + ThrottledPeriods uint64 `protobuf:"varint,2,opt,name=throttled_periods,json=throttledPeriods,proto3" json:"throttled_periods,omitempty"` + ThrottledTime uint64 `protobuf:"varint,3,opt,name=throttled_time,json=throttledTime,proto3" json:"throttled_time,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Throttle) Reset() { *m = Throttle{} } +func (*Throttle) ProtoMessage() {} +func (*Throttle) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{5} +} +func (m *Throttle) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Throttle) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Throttle.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Throttle) XXX_Merge(src proto.Message) { + xxx_messageInfo_Throttle.Merge(m, src) +} +func (m *Throttle) XXX_Size() int { + return m.Size() +} +func (m *Throttle) XXX_DiscardUnknown() { + xxx_messageInfo_Throttle.DiscardUnknown(m) +} + +var xxx_messageInfo_Throttle proto.InternalMessageInfo + +type MemoryStat struct { + Cache uint64 `protobuf:"varint,1,opt,name=cache,proto3" json:"cache,omitempty"` + RSS uint64 `protobuf:"varint,2,opt,name=rss,proto3" json:"rss,omitempty"` + RSSHuge uint64 `protobuf:"varint,3,opt,name=rss_huge,json=rssHuge,proto3" json:"rss_huge,omitempty"` + MappedFile uint64 `protobuf:"varint,4,opt,name=mapped_file,json=mappedFile,proto3" json:"mapped_file,omitempty"` + Dirty uint64 `protobuf:"varint,5,opt,name=dirty,proto3" json:"dirty,omitempty"` + Writeback uint64 `protobuf:"varint,6,opt,name=writeback,proto3" json:"writeback,omitempty"` + PgPgIn uint64 `protobuf:"varint,7,opt,name=pg_pg_in,json=pgPgIn,proto3" json:"pg_pg_in,omitempty"` + PgPgOut uint64 `protobuf:"varint,8,opt,name=pg_pg_out,json=pgPgOut,proto3" json:"pg_pg_out,omitempty"` + PgFault uint64 `protobuf:"varint,9,opt,name=pg_fault,json=pgFault,proto3" json:"pg_fault,omitempty"` + PgMajFault uint64 `protobuf:"varint,10,opt,name=pg_maj_fault,json=pgMajFault,proto3" json:"pg_maj_fault,omitempty"` + InactiveAnon uint64 `protobuf:"varint,11,opt,name=inactive_anon,json=inactiveAnon,proto3" json:"inactive_anon,omitempty"` + ActiveAnon uint64 `protobuf:"varint,12,opt,name=active_anon,json=activeAnon,proto3" json:"active_anon,omitempty"` + InactiveFile uint64 `protobuf:"varint,13,opt,name=inactive_file,json=inactiveFile,proto3" json:"inactive_file,omitempty"` + ActiveFile uint64 `protobuf:"varint,14,opt,name=active_file,json=activeFile,proto3" json:"active_file,omitempty"` + Unevictable uint64 `protobuf:"varint,15,opt,name=unevictable,proto3" json:"unevictable,omitempty"` + HierarchicalMemoryLimit uint64 `protobuf:"varint,16,opt,name=hierarchical_memory_limit,json=hierarchicalMemoryLimit,proto3" json:"hierarchical_memory_limit,omitempty"` + HierarchicalSwapLimit uint64 `protobuf:"varint,17,opt,name=hierarchical_swap_limit,json=hierarchicalSwapLimit,proto3" json:"hierarchical_swap_limit,omitempty"` + TotalCache uint64 `protobuf:"varint,18,opt,name=total_cache,json=totalCache,proto3" json:"total_cache,omitempty"` + TotalRSS uint64 `protobuf:"varint,19,opt,name=total_rss,json=totalRss,proto3" json:"total_rss,omitempty"` + TotalRSSHuge uint64 `protobuf:"varint,20,opt,name=total_rss_huge,json=totalRssHuge,proto3" json:"total_rss_huge,omitempty"` + TotalMappedFile uint64 `protobuf:"varint,21,opt,name=total_mapped_file,json=totalMappedFile,proto3" json:"total_mapped_file,omitempty"` + TotalDirty uint64 `protobuf:"varint,22,opt,name=total_dirty,json=totalDirty,proto3" json:"total_dirty,omitempty"` + TotalWriteback uint64 `protobuf:"varint,23,opt,name=total_writeback,json=totalWriteback,proto3" json:"total_writeback,omitempty"` + TotalPgPgIn uint64 `protobuf:"varint,24,opt,name=total_pg_pg_in,json=totalPgPgIn,proto3" json:"total_pg_pg_in,omitempty"` + TotalPgPgOut uint64 `protobuf:"varint,25,opt,name=total_pg_pg_out,json=totalPgPgOut,proto3" json:"total_pg_pg_out,omitempty"` + TotalPgFault uint64 `protobuf:"varint,26,opt,name=total_pg_fault,json=totalPgFault,proto3" json:"total_pg_fault,omitempty"` + TotalPgMajFault uint64 `protobuf:"varint,27,opt,name=total_pg_maj_fault,json=totalPgMajFault,proto3" json:"total_pg_maj_fault,omitempty"` + TotalInactiveAnon uint64 `protobuf:"varint,28,opt,name=total_inactive_anon,json=totalInactiveAnon,proto3" json:"total_inactive_anon,omitempty"` + TotalActiveAnon uint64 `protobuf:"varint,29,opt,name=total_active_anon,json=totalActiveAnon,proto3" json:"total_active_anon,omitempty"` + TotalInactiveFile uint64 `protobuf:"varint,30,opt,name=total_inactive_file,json=totalInactiveFile,proto3" json:"total_inactive_file,omitempty"` + TotalActiveFile uint64 `protobuf:"varint,31,opt,name=total_active_file,json=totalActiveFile,proto3" json:"total_active_file,omitempty"` + TotalUnevictable uint64 `protobuf:"varint,32,opt,name=total_unevictable,json=totalUnevictable,proto3" json:"total_unevictable,omitempty"` + Usage *MemoryEntry `protobuf:"bytes,33,opt,name=usage,proto3" json:"usage,omitempty"` + Swap *MemoryEntry `protobuf:"bytes,34,opt,name=swap,proto3" json:"swap,omitempty"` + Kernel *MemoryEntry `protobuf:"bytes,35,opt,name=kernel,proto3" json:"kernel,omitempty"` + KernelTCP *MemoryEntry `protobuf:"bytes,36,opt,name=kernel_tcp,json=kernelTcp,proto3" json:"kernel_tcp,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MemoryStat) Reset() { *m = MemoryStat{} } +func (*MemoryStat) ProtoMessage() {} +func (*MemoryStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{6} +} +func (m *MemoryStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MemoryStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MemoryStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MemoryStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_MemoryStat.Merge(m, src) +} +func (m *MemoryStat) XXX_Size() int { + return m.Size() +} +func (m *MemoryStat) XXX_DiscardUnknown() { + xxx_messageInfo_MemoryStat.DiscardUnknown(m) +} + +var xxx_messageInfo_MemoryStat proto.InternalMessageInfo + +type MemoryEntry struct { + Limit uint64 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` + Usage uint64 `protobuf:"varint,2,opt,name=usage,proto3" json:"usage,omitempty"` + Max uint64 `protobuf:"varint,3,opt,name=max,proto3" json:"max,omitempty"` + Failcnt uint64 `protobuf:"varint,4,opt,name=failcnt,proto3" json:"failcnt,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MemoryEntry) Reset() { *m = MemoryEntry{} } +func (*MemoryEntry) ProtoMessage() {} +func (*MemoryEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{7} +} +func (m *MemoryEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MemoryEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MemoryEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MemoryEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_MemoryEntry.Merge(m, src) +} +func (m *MemoryEntry) XXX_Size() int { + return m.Size() +} +func (m *MemoryEntry) XXX_DiscardUnknown() { + xxx_messageInfo_MemoryEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_MemoryEntry proto.InternalMessageInfo + +type MemoryOomControl struct { + OomKillDisable uint64 `protobuf:"varint,1,opt,name=oom_kill_disable,json=oomKillDisable,proto3" json:"oom_kill_disable,omitempty"` + UnderOom uint64 `protobuf:"varint,2,opt,name=under_oom,json=underOom,proto3" json:"under_oom,omitempty"` + OomKill uint64 `protobuf:"varint,3,opt,name=oom_kill,json=oomKill,proto3" json:"oom_kill,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MemoryOomControl) Reset() { *m = MemoryOomControl{} } +func (*MemoryOomControl) ProtoMessage() {} +func (*MemoryOomControl) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{8} +} +func (m *MemoryOomControl) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MemoryOomControl) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MemoryOomControl.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MemoryOomControl) XXX_Merge(src proto.Message) { + xxx_messageInfo_MemoryOomControl.Merge(m, src) +} +func (m *MemoryOomControl) XXX_Size() int { + return m.Size() +} +func (m *MemoryOomControl) XXX_DiscardUnknown() { + xxx_messageInfo_MemoryOomControl.DiscardUnknown(m) +} + +var xxx_messageInfo_MemoryOomControl proto.InternalMessageInfo + +type BlkIOStat struct { + IoServiceBytesRecursive []*BlkIOEntry `protobuf:"bytes,1,rep,name=io_service_bytes_recursive,json=ioServiceBytesRecursive,proto3" json:"io_service_bytes_recursive,omitempty"` + IoServicedRecursive []*BlkIOEntry `protobuf:"bytes,2,rep,name=io_serviced_recursive,json=ioServicedRecursive,proto3" json:"io_serviced_recursive,omitempty"` + IoQueuedRecursive []*BlkIOEntry `protobuf:"bytes,3,rep,name=io_queued_recursive,json=ioQueuedRecursive,proto3" json:"io_queued_recursive,omitempty"` + IoServiceTimeRecursive []*BlkIOEntry `protobuf:"bytes,4,rep,name=io_service_time_recursive,json=ioServiceTimeRecursive,proto3" json:"io_service_time_recursive,omitempty"` + IoWaitTimeRecursive []*BlkIOEntry `protobuf:"bytes,5,rep,name=io_wait_time_recursive,json=ioWaitTimeRecursive,proto3" json:"io_wait_time_recursive,omitempty"` + IoMergedRecursive []*BlkIOEntry `protobuf:"bytes,6,rep,name=io_merged_recursive,json=ioMergedRecursive,proto3" json:"io_merged_recursive,omitempty"` + IoTimeRecursive []*BlkIOEntry `protobuf:"bytes,7,rep,name=io_time_recursive,json=ioTimeRecursive,proto3" json:"io_time_recursive,omitempty"` + SectorsRecursive []*BlkIOEntry `protobuf:"bytes,8,rep,name=sectors_recursive,json=sectorsRecursive,proto3" json:"sectors_recursive,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BlkIOStat) Reset() { *m = BlkIOStat{} } +func (*BlkIOStat) ProtoMessage() {} +func (*BlkIOStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{9} +} +func (m *BlkIOStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BlkIOStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BlkIOStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BlkIOStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_BlkIOStat.Merge(m, src) +} +func (m *BlkIOStat) XXX_Size() int { + return m.Size() +} +func (m *BlkIOStat) XXX_DiscardUnknown() { + xxx_messageInfo_BlkIOStat.DiscardUnknown(m) +} + +var xxx_messageInfo_BlkIOStat proto.InternalMessageInfo + +type BlkIOEntry struct { + Op string `protobuf:"bytes,1,opt,name=op,proto3" json:"op,omitempty"` + Device string `protobuf:"bytes,2,opt,name=device,proto3" json:"device,omitempty"` + Major uint64 `protobuf:"varint,3,opt,name=major,proto3" json:"major,omitempty"` + Minor uint64 `protobuf:"varint,4,opt,name=minor,proto3" json:"minor,omitempty"` + Value uint64 `protobuf:"varint,5,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BlkIOEntry) Reset() { *m = BlkIOEntry{} } +func (*BlkIOEntry) ProtoMessage() {} +func (*BlkIOEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{10} +} +func (m *BlkIOEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BlkIOEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BlkIOEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BlkIOEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_BlkIOEntry.Merge(m, src) +} +func (m *BlkIOEntry) XXX_Size() int { + return m.Size() +} +func (m *BlkIOEntry) XXX_DiscardUnknown() { + xxx_messageInfo_BlkIOEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_BlkIOEntry proto.InternalMessageInfo + +type RdmaStat struct { + Current []*RdmaEntry `protobuf:"bytes,1,rep,name=current,proto3" json:"current,omitempty"` + Limit []*RdmaEntry `protobuf:"bytes,2,rep,name=limit,proto3" json:"limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RdmaStat) Reset() { *m = RdmaStat{} } +func (*RdmaStat) ProtoMessage() {} +func (*RdmaStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{11} +} +func (m *RdmaStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RdmaStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RdmaStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RdmaStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_RdmaStat.Merge(m, src) +} +func (m *RdmaStat) XXX_Size() int { + return m.Size() +} +func (m *RdmaStat) XXX_DiscardUnknown() { + xxx_messageInfo_RdmaStat.DiscardUnknown(m) +} + +var xxx_messageInfo_RdmaStat proto.InternalMessageInfo + +type RdmaEntry struct { + Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` + HcaHandles uint32 `protobuf:"varint,2,opt,name=hca_handles,json=hcaHandles,proto3" json:"hca_handles,omitempty"` + HcaObjects uint32 `protobuf:"varint,3,opt,name=hca_objects,json=hcaObjects,proto3" json:"hca_objects,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RdmaEntry) Reset() { *m = RdmaEntry{} } +func (*RdmaEntry) ProtoMessage() {} +func (*RdmaEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{12} +} +func (m *RdmaEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RdmaEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RdmaEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RdmaEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_RdmaEntry.Merge(m, src) +} +func (m *RdmaEntry) XXX_Size() int { + return m.Size() +} +func (m *RdmaEntry) XXX_DiscardUnknown() { + xxx_messageInfo_RdmaEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_RdmaEntry proto.InternalMessageInfo + +type NetworkStat struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + RxBytes uint64 `protobuf:"varint,2,opt,name=rx_bytes,json=rxBytes,proto3" json:"rx_bytes,omitempty"` + RxPackets uint64 `protobuf:"varint,3,opt,name=rx_packets,json=rxPackets,proto3" json:"rx_packets,omitempty"` + RxErrors uint64 `protobuf:"varint,4,opt,name=rx_errors,json=rxErrors,proto3" json:"rx_errors,omitempty"` + RxDropped uint64 `protobuf:"varint,5,opt,name=rx_dropped,json=rxDropped,proto3" json:"rx_dropped,omitempty"` + TxBytes uint64 `protobuf:"varint,6,opt,name=tx_bytes,json=txBytes,proto3" json:"tx_bytes,omitempty"` + TxPackets uint64 `protobuf:"varint,7,opt,name=tx_packets,json=txPackets,proto3" json:"tx_packets,omitempty"` + TxErrors uint64 `protobuf:"varint,8,opt,name=tx_errors,json=txErrors,proto3" json:"tx_errors,omitempty"` + TxDropped uint64 `protobuf:"varint,9,opt,name=tx_dropped,json=txDropped,proto3" json:"tx_dropped,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NetworkStat) Reset() { *m = NetworkStat{} } +func (*NetworkStat) ProtoMessage() {} +func (*NetworkStat) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{13} +} +func (m *NetworkStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NetworkStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NetworkStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NetworkStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_NetworkStat.Merge(m, src) +} +func (m *NetworkStat) XXX_Size() int { + return m.Size() +} +func (m *NetworkStat) XXX_DiscardUnknown() { + xxx_messageInfo_NetworkStat.DiscardUnknown(m) +} + +var xxx_messageInfo_NetworkStat proto.InternalMessageInfo + +// CgroupStats exports per-cgroup statistics. +type CgroupStats struct { + // number of tasks sleeping + NrSleeping uint64 `protobuf:"varint,1,opt,name=nr_sleeping,json=nrSleeping,proto3" json:"nr_sleeping,omitempty"` + // number of tasks running + NrRunning uint64 `protobuf:"varint,2,opt,name=nr_running,json=nrRunning,proto3" json:"nr_running,omitempty"` + // number of tasks in stopped state + NrStopped uint64 `protobuf:"varint,3,opt,name=nr_stopped,json=nrStopped,proto3" json:"nr_stopped,omitempty"` + // number of tasks in uninterruptible state + NrUninterruptible uint64 `protobuf:"varint,4,opt,name=nr_uninterruptible,json=nrUninterruptible,proto3" json:"nr_uninterruptible,omitempty"` + // number of tasks waiting on IO + NrIoWait uint64 `protobuf:"varint,5,opt,name=nr_io_wait,json=nrIoWait,proto3" json:"nr_io_wait,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CgroupStats) Reset() { *m = CgroupStats{} } +func (*CgroupStats) ProtoMessage() {} +func (*CgroupStats) Descriptor() ([]byte, []int) { + return fileDescriptor_a17b2d87c332bfaa, []int{14} +} +func (m *CgroupStats) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CgroupStats) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CgroupStats.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CgroupStats) XXX_Merge(src proto.Message) { + xxx_messageInfo_CgroupStats.Merge(m, src) +} +func (m *CgroupStats) XXX_Size() int { + return m.Size() +} +func (m *CgroupStats) XXX_DiscardUnknown() { + xxx_messageInfo_CgroupStats.DiscardUnknown(m) +} + +var xxx_messageInfo_CgroupStats proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Metrics)(nil), "io.containerd.cgroups.v1.Metrics") + proto.RegisterType((*HugetlbStat)(nil), "io.containerd.cgroups.v1.HugetlbStat") + proto.RegisterType((*PidsStat)(nil), "io.containerd.cgroups.v1.PidsStat") + proto.RegisterType((*CPUStat)(nil), "io.containerd.cgroups.v1.CPUStat") + proto.RegisterType((*CPUUsage)(nil), "io.containerd.cgroups.v1.CPUUsage") + proto.RegisterType((*Throttle)(nil), "io.containerd.cgroups.v1.Throttle") + proto.RegisterType((*MemoryStat)(nil), "io.containerd.cgroups.v1.MemoryStat") + proto.RegisterType((*MemoryEntry)(nil), "io.containerd.cgroups.v1.MemoryEntry") + proto.RegisterType((*MemoryOomControl)(nil), "io.containerd.cgroups.v1.MemoryOomControl") + proto.RegisterType((*BlkIOStat)(nil), "io.containerd.cgroups.v1.BlkIOStat") + proto.RegisterType((*BlkIOEntry)(nil), "io.containerd.cgroups.v1.BlkIOEntry") + proto.RegisterType((*RdmaStat)(nil), "io.containerd.cgroups.v1.RdmaStat") + proto.RegisterType((*RdmaEntry)(nil), "io.containerd.cgroups.v1.RdmaEntry") + proto.RegisterType((*NetworkStat)(nil), "io.containerd.cgroups.v1.NetworkStat") + proto.RegisterType((*CgroupStats)(nil), "io.containerd.cgroups.v1.CgroupStats") +} + +func init() { + proto.RegisterFile("github.com/containerd/cgroups/stats/v1/metrics.proto", fileDescriptor_a17b2d87c332bfaa) +} + +var fileDescriptor_a17b2d87c332bfaa = []byte{ + // 1749 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x58, 0xcd, 0x72, 0xe3, 0xc6, + 0x11, 0x36, 0x45, 0x48, 0x24, 0x9a, 0x92, 0x56, 0x9a, 0xfd, 0x83, 0xe4, 0xb5, 0x28, 0x53, 0xbb, + 0x89, 0xe2, 0xad, 0x48, 0x65, 0x27, 0xb5, 0x95, 0x75, 0xec, 0x4a, 0x59, 0x5a, 0xbb, 0x76, 0xcb, + 0x51, 0x44, 0x83, 0x52, 0xd9, 0x39, 0xa1, 0x40, 0x70, 0x16, 0x9c, 0x15, 0x80, 0x81, 0x07, 0x03, + 0x89, 0xca, 0x29, 0x87, 0x54, 0xe5, 0x94, 0x07, 0xca, 0x1b, 0xf8, 0x98, 0x4b, 0x52, 0xc9, 0x45, + 0x15, 0xf3, 0x49, 0x52, 0x33, 0x3d, 0xf8, 0xa1, 0xbc, 0x5a, 0x85, 0x37, 0x76, 0xcf, 0xd7, 0x5f, + 0xf7, 0x34, 0xbe, 0x19, 0x34, 0x08, 0xbf, 0x0e, 0x99, 0x1c, 0xe7, 0xc3, 0xbd, 0x80, 0xc7, 0xfb, + 0x01, 0x4f, 0xa4, 0xcf, 0x12, 0x2a, 0x46, 0xfb, 0x41, 0x28, 0x78, 0x9e, 0x66, 0xfb, 0x99, 0xf4, + 0x65, 0xb6, 0x7f, 0xfe, 0xf1, 0x7e, 0x4c, 0xa5, 0x60, 0x41, 0xb6, 0x97, 0x0a, 0x2e, 0x39, 0x71, + 0x18, 0xdf, 0xab, 0xd0, 0x7b, 0x06, 0xbd, 0x77, 0xfe, 0xf1, 0xe6, 0xbd, 0x90, 0x87, 0x5c, 0x83, + 0xf6, 0xd5, 0x2f, 0xc4, 0xf7, 0xfe, 0x65, 0x41, 0xeb, 0x08, 0x19, 0xc8, 0xef, 0xa0, 0x35, 0xce, + 0x43, 0x2a, 0xa3, 0xa1, 0xd3, 0xd8, 0x6e, 0xee, 0x76, 0x3e, 0x79, 0xb2, 0x77, 0x13, 0xdb, 0xde, + 0x4b, 0x04, 0x0e, 0xa4, 0x2f, 0xdd, 0x22, 0x8a, 0x3c, 0x03, 0x2b, 0x65, 0xa3, 0xcc, 0x59, 0xd8, + 0x6e, 0xec, 0x76, 0x3e, 0xe9, 0xdd, 0x1c, 0xdd, 0x67, 0xa3, 0x4c, 0x87, 0x6a, 0x3c, 0xf9, 0x0c, + 0x9a, 0x41, 0x9a, 0x3b, 0x4d, 0x1d, 0xf6, 0xe1, 0xcd, 0x61, 0x87, 0xfd, 0x53, 0x15, 0x75, 0xd0, + 0x9a, 0x5e, 0x75, 0x9b, 0x87, 0xfd, 0x53, 0x57, 0x85, 0x91, 0xcf, 0x60, 0x29, 0xa6, 0x31, 0x17, + 0x97, 0x8e, 0xa5, 0x09, 0x1e, 0xdf, 0x4c, 0x70, 0xa4, 0x71, 0x3a, 0xb3, 0x89, 0x21, 0xcf, 0x61, + 0x71, 0x18, 0x9d, 0x31, 0xee, 0x2c, 0xea, 0xe0, 0x9d, 0x9b, 0x83, 0x0f, 0xa2, 0xb3, 0x57, 0xc7, + 0x3a, 0x16, 0x23, 0xd4, 0x76, 0xc5, 0x28, 0xf6, 0x9d, 0xa5, 0xdb, 0xb6, 0xeb, 0x8e, 0x62, 0x1f, + 0xb7, 0xab, 0xf0, 0xaa, 0xcf, 0x09, 0x95, 0x17, 0x5c, 0x9c, 0x39, 0xad, 0xdb, 0xfa, 0xfc, 0x07, + 0x04, 0x62, 0x9f, 0x4d, 0x14, 0x79, 0x09, 0xcb, 0x08, 0xf1, 0xb4, 0x0a, 0x9c, 0xb6, 0x2e, 0xe0, + 0x1d, 0x2c, 0x87, 0xfa, 0xa7, 0x22, 0xc9, 0xdc, 0x4e, 0x50, 0x19, 0xe4, 0x3b, 0x20, 0xd8, 0x07, + 0x8f, 0xf3, 0xd8, 0x53, 0xc1, 0x82, 0x47, 0x8e, 0xad, 0xf9, 0x3e, 0xba, 0xad, 0x8f, 0xc7, 0x3c, + 0x3e, 0xc4, 0x08, 0x77, 0x2d, 0xbe, 0xe6, 0xe9, 0x9d, 0x41, 0xa7, 0xa6, 0x11, 0x72, 0x0f, 0x16, + 0xf3, 0xcc, 0x0f, 0xa9, 0xd3, 0xd8, 0x6e, 0xec, 0x5a, 0x2e, 0x1a, 0x64, 0x0d, 0x9a, 0xb1, 0x3f, + 0xd1, 0x7a, 0xb1, 0x5c, 0xf5, 0x93, 0x38, 0xd0, 0x7a, 0xed, 0xb3, 0x28, 0x48, 0xa4, 0x96, 0x83, + 0xe5, 0x16, 0x26, 0xd9, 0x84, 0x76, 0xea, 0x87, 0x34, 0x63, 0x7f, 0xa2, 0xfa, 0x41, 0xdb, 0x6e, + 0x69, 0xf7, 0x3e, 0x85, 0x76, 0x21, 0x29, 0xc5, 0x10, 0xe4, 0x42, 0xd0, 0x44, 0x9a, 0x5c, 0x85, + 0xa9, 0x6a, 0x88, 0x58, 0xcc, 0xa4, 0xc9, 0x87, 0x46, 0xef, 0xaf, 0x0d, 0x68, 0x19, 0x61, 0x91, + 0xdf, 0xd4, 0xab, 0x7c, 0xe7, 0x23, 0x3d, 0xec, 0x9f, 0x9e, 0x2a, 0x64, 0xb1, 0x93, 0x03, 0x00, + 0x39, 0x16, 0x5c, 0xca, 0x88, 0x25, 0xe1, 0xed, 0x07, 0xe0, 0x04, 0xb1, 0xd4, 0xad, 0x45, 0xf5, + 0xbe, 0x87, 0x76, 0x41, 0xab, 0x6a, 0x95, 0x5c, 0xfa, 0x51, 0xd1, 0x2f, 0x6d, 0x90, 0x07, 0xb0, + 0x74, 0x46, 0x45, 0x42, 0x23, 0xb3, 0x05, 0x63, 0x11, 0x02, 0x56, 0x9e, 0x51, 0x61, 0x5a, 0xa6, + 0x7f, 0x93, 0x1d, 0x68, 0xa5, 0x54, 0x78, 0xea, 0x60, 0x59, 0xdb, 0xcd, 0x5d, 0xeb, 0x00, 0xa6, + 0x57, 0xdd, 0xa5, 0x3e, 0x15, 0xea, 0xe0, 0x2c, 0xa5, 0x54, 0x1c, 0xa6, 0x79, 0x6f, 0x02, 0xed, + 0xa2, 0x14, 0xd5, 0xb8, 0x94, 0x0a, 0xc6, 0x47, 0x59, 0xd1, 0x38, 0x63, 0x92, 0xa7, 0xb0, 0x6e, + 0xca, 0xa4, 0x23, 0xaf, 0xc0, 0x60, 0x05, 0x6b, 0xe5, 0x42, 0xdf, 0x80, 0x9f, 0xc0, 0x6a, 0x05, + 0x96, 0x2c, 0xa6, 0xa6, 0xaa, 0x95, 0xd2, 0x7b, 0xc2, 0x62, 0xda, 0xfb, 0x4f, 0x07, 0xa0, 0x3a, + 0x8e, 0x6a, 0xbf, 0x81, 0x1f, 0x8c, 0x4b, 0x7d, 0x68, 0x83, 0x6c, 0x40, 0x53, 0x64, 0x26, 0x15, + 0x9e, 0x7a, 0x77, 0x30, 0x70, 0x95, 0x8f, 0xfc, 0x0c, 0xda, 0x22, 0xcb, 0x3c, 0x75, 0xf5, 0x60, + 0x82, 0x83, 0xce, 0xf4, 0xaa, 0xdb, 0x72, 0x07, 0x03, 0x25, 0x3b, 0xb7, 0x25, 0xb2, 0x4c, 0xfd, + 0x20, 0x5d, 0xe8, 0xc4, 0x7e, 0x9a, 0xd2, 0x91, 0xf7, 0x9a, 0x45, 0xa8, 0x1c, 0xcb, 0x05, 0x74, + 0x7d, 0xc5, 0x22, 0xdd, 0xe9, 0x11, 0x13, 0xf2, 0x52, 0x5f, 0x00, 0x96, 0x8b, 0x06, 0x79, 0x04, + 0xf6, 0x85, 0x60, 0x92, 0x0e, 0xfd, 0xe0, 0x4c, 0x1f, 0x70, 0xcb, 0xad, 0x1c, 0xc4, 0x81, 0x76, + 0x1a, 0x7a, 0x69, 0xe8, 0xb1, 0xc4, 0x69, 0xe1, 0x93, 0x48, 0xc3, 0x7e, 0xf8, 0x2a, 0x21, 0x9b, + 0x60, 0xe3, 0x0a, 0xcf, 0xa5, 0x3e, 0x97, 0xaa, 0x8d, 0x61, 0x3f, 0x3c, 0xce, 0x25, 0xd9, 0xd0, + 0x51, 0xaf, 0xfd, 0x3c, 0x92, 0xfa, 0x88, 0xe9, 0xa5, 0xaf, 0x94, 0x49, 0xb6, 0x61, 0x39, 0x0d, + 0xbd, 0xd8, 0x7f, 0x63, 0x96, 0x01, 0xcb, 0x4c, 0xc3, 0x23, 0xff, 0x0d, 0x22, 0x76, 0x60, 0x85, + 0x25, 0x7e, 0x20, 0xd9, 0x39, 0xf5, 0xfc, 0x84, 0x27, 0x4e, 0x47, 0x43, 0x96, 0x0b, 0xe7, 0x17, + 0x09, 0x4f, 0xd4, 0x66, 0xeb, 0x90, 0x65, 0x64, 0xa9, 0x01, 0xea, 0x2c, 0xba, 0x1f, 0x2b, 0xb3, + 0x2c, 0xba, 0x23, 0x15, 0x8b, 0x86, 0xac, 0xd6, 0x59, 0x34, 0x60, 0x1b, 0x3a, 0x79, 0x42, 0xcf, + 0x59, 0x20, 0xfd, 0x61, 0x44, 0x9d, 0x3b, 0x1a, 0x50, 0x77, 0x91, 0x4f, 0x61, 0x63, 0xcc, 0xa8, + 0xf0, 0x45, 0x30, 0x66, 0x81, 0x1f, 0x79, 0xe6, 0x92, 0xc1, 0xe3, 0xb7, 0xa6, 0xf1, 0x0f, 0xeb, + 0x00, 0x54, 0xc2, 0xef, 0xd5, 0x32, 0x79, 0x06, 0x33, 0x4b, 0x5e, 0x76, 0xe1, 0xa7, 0x26, 0x72, + 0x5d, 0x47, 0xde, 0xaf, 0x2f, 0x0f, 0x2e, 0xfc, 0x14, 0xe3, 0xba, 0xd0, 0xd1, 0xa7, 0xc4, 0x43, + 0x21, 0x11, 0x2c, 0x5b, 0xbb, 0x0e, 0xb5, 0x9a, 0x7e, 0x01, 0x36, 0x02, 0x94, 0xa6, 0xee, 0x6a, + 0xcd, 0x2c, 0x4f, 0xaf, 0xba, 0xed, 0x13, 0xe5, 0x54, 0xc2, 0x6a, 0xeb, 0x65, 0x37, 0xcb, 0xc8, + 0x33, 0x58, 0x2d, 0xa1, 0xa8, 0xb1, 0x7b, 0x1a, 0xbf, 0x36, 0xbd, 0xea, 0x2e, 0x17, 0x78, 0x2d, + 0xb4, 0xe5, 0x22, 0x46, 0xab, 0xed, 0x23, 0x58, 0xc7, 0xb8, 0xba, 0xe6, 0xee, 0xeb, 0x4a, 0xee, + 0xe8, 0x85, 0xa3, 0x4a, 0x78, 0x65, 0xbd, 0x28, 0xbf, 0x07, 0xb5, 0x7a, 0x5f, 0x68, 0x0d, 0xfe, + 0x1c, 0x30, 0xc6, 0xab, 0x94, 0xf8, 0x50, 0x83, 0xb0, 0xb6, 0x6f, 0x4b, 0x39, 0xee, 0x14, 0xd5, + 0x96, 0xa2, 0x74, 0xf0, 0x91, 0x68, 0x6f, 0x1f, 0x95, 0xf9, 0xa4, 0x60, 0xab, 0xf4, 0xb9, 0x81, + 0x0f, 0xbf, 0x44, 0x29, 0x91, 0x3e, 0xae, 0x71, 0xa1, 0x16, 0x37, 0x67, 0x50, 0xa8, 0xc6, 0xa7, + 0x40, 0x4a, 0x54, 0xa5, 0xda, 0xf7, 0x6b, 0x1b, 0xed, 0x57, 0xd2, 0xdd, 0x83, 0xbb, 0x08, 0x9e, + 0x15, 0xf0, 0x23, 0x8d, 0xc6, 0x7e, 0xbd, 0xaa, 0xab, 0xb8, 0x6c, 0x62, 0x1d, 0xfd, 0x41, 0x8d, + 0xfb, 0x8b, 0x0a, 0xfb, 0x53, 0x6e, 0xdd, 0xf2, 0xad, 0xb7, 0x70, 0xeb, 0xa6, 0x5f, 0xe7, 0xd6, + 0xe8, 0xee, 0x4f, 0xb8, 0x35, 0xf6, 0x69, 0x81, 0xad, 0x8b, 0x7d, 0xdb, 0x5c, 0x7b, 0x6a, 0xe1, + 0xb4, 0xa6, 0xf8, 0xdf, 0x16, 0xaf, 0x8e, 0x0f, 0x6f, 0x7b, 0x19, 0xa3, 0xd6, 0xbf, 0x4c, 0xa4, + 0xb8, 0x2c, 0xde, 0x1e, 0xcf, 0xc1, 0x52, 0x2a, 0x77, 0x7a, 0xf3, 0xc4, 0xea, 0x10, 0xf2, 0x79, + 0xf9, 0x4a, 0xd8, 0x99, 0x27, 0xb8, 0x78, 0x73, 0x0c, 0x00, 0xf0, 0x97, 0x27, 0x83, 0xd4, 0x79, + 0x3c, 0x07, 0xc5, 0xc1, 0xca, 0xf4, 0xaa, 0x6b, 0x7f, 0xad, 0x83, 0x4f, 0x0e, 0xfb, 0xae, 0x8d, + 0x3c, 0x27, 0x41, 0xda, 0xa3, 0xd0, 0xa9, 0x01, 0xab, 0xf7, 0x6e, 0xa3, 0xf6, 0xde, 0xad, 0x26, + 0x82, 0x85, 0xb7, 0x4c, 0x04, 0xcd, 0xb7, 0x4e, 0x04, 0xd6, 0xcc, 0x44, 0xd0, 0x93, 0xb0, 0x76, + 0x7d, 0x10, 0x21, 0xbb, 0xb0, 0xa6, 0x26, 0x99, 0x33, 0x16, 0xa9, 0x73, 0x95, 0xe9, 0x47, 0x86, + 0x69, 0x57, 0x39, 0x8f, 0xbf, 0x66, 0x51, 0xf4, 0x02, 0xbd, 0xe4, 0x7d, 0xb0, 0xf3, 0x64, 0x44, + 0x85, 0x9a, 0x7c, 0x4c, 0x0d, 0x6d, 0xed, 0x38, 0xe6, 0xb1, 0xba, 0xaa, 0x0b, 0x9a, 0x62, 0x0e, + 0x31, 0xe1, 0xbd, 0x7f, 0x2e, 0x82, 0x5d, 0x8e, 0x82, 0xc4, 0x87, 0x4d, 0xc6, 0xbd, 0x8c, 0x8a, + 0x73, 0x16, 0x50, 0x6f, 0x78, 0x29, 0x69, 0xe6, 0x09, 0x1a, 0xe4, 0x22, 0x63, 0xe7, 0xd4, 0x8c, + 0xd1, 0x8f, 0x6f, 0x99, 0x29, 0xf1, 0x89, 0x3c, 0x64, 0x7c, 0x80, 0x34, 0x07, 0x8a, 0xc5, 0x2d, + 0x48, 0xc8, 0x77, 0x70, 0xbf, 0x4a, 0x31, 0xaa, 0xb1, 0x2f, 0xcc, 0xc1, 0x7e, 0xb7, 0x64, 0x1f, + 0x55, 0xcc, 0x27, 0x70, 0x97, 0x71, 0xef, 0xfb, 0x9c, 0xe6, 0x33, 0xbc, 0xcd, 0x39, 0x78, 0xd7, + 0x19, 0xff, 0x46, 0xc7, 0x57, 0xac, 0x1e, 0x6c, 0xd4, 0x5a, 0xa2, 0x26, 0x80, 0x1a, 0xb7, 0x35, + 0x07, 0xf7, 0x83, 0xb2, 0x66, 0x35, 0x31, 0x54, 0x09, 0xfe, 0x08, 0x0f, 0x18, 0xf7, 0x2e, 0x7c, + 0x26, 0xaf, 0xb3, 0x2f, 0xce, 0xd7, 0x91, 0x6f, 0x7d, 0x26, 0x67, 0xa9, 0xb1, 0x23, 0x31, 0x15, + 0xe1, 0x4c, 0x47, 0x96, 0xe6, 0xeb, 0xc8, 0x91, 0x8e, 0xaf, 0x58, 0xfb, 0xb0, 0xce, 0xf8, 0xf5, + 0x5a, 0x5b, 0x73, 0x70, 0xde, 0x61, 0x7c, 0xb6, 0xce, 0x6f, 0x60, 0x3d, 0xa3, 0x81, 0xe4, 0xa2, + 0xae, 0xb6, 0xf6, 0x1c, 0x8c, 0x6b, 0x26, 0xbc, 0xa4, 0xec, 0x9d, 0x03, 0x54, 0xeb, 0x64, 0x15, + 0x16, 0x78, 0xaa, 0x4f, 0x8e, 0xed, 0x2e, 0xf0, 0x54, 0x4d, 0x9e, 0x23, 0x75, 0xd9, 0xe1, 0x71, + 0xb5, 0x5d, 0x63, 0xa9, 0x53, 0x1c, 0xfb, 0x6f, 0x78, 0x31, 0x7a, 0xa2, 0xa1, 0xbd, 0x2c, 0xe1, + 0xc2, 0x9c, 0x58, 0x34, 0x94, 0xf7, 0xdc, 0x8f, 0x72, 0x5a, 0x4c, 0x5a, 0xda, 0xe8, 0xfd, 0xa5, + 0x01, 0xed, 0xe2, 0x03, 0x89, 0x7c, 0x5e, 0x1f, 0xde, 0x9b, 0xef, 0xfe, 0x1e, 0x53, 0x41, 0xb8, + 0x99, 0x72, 0xc2, 0x7f, 0x5e, 0x4d, 0xf8, 0xff, 0x77, 0xb0, 0xf9, 0x0c, 0xa0, 0x60, 0x97, 0xbe, + 0xda, 0x6e, 0x1b, 0x33, 0xbb, 0xed, 0x42, 0x67, 0x1c, 0xf8, 0xde, 0xd8, 0x4f, 0x46, 0x11, 0xc5, + 0xb9, 0x74, 0xc5, 0x85, 0x71, 0xe0, 0xbf, 0x44, 0x4f, 0x01, 0xe0, 0xc3, 0x37, 0x34, 0x90, 0x99, + 0x6e, 0x0a, 0x02, 0x8e, 0xd1, 0xd3, 0xfb, 0xdb, 0x02, 0x74, 0x6a, 0xdf, 0x74, 0x6a, 0x72, 0x4f, + 0xfc, 0xb8, 0xc8, 0xa3, 0x7f, 0xab, 0xcb, 0x47, 0x4c, 0xf0, 0x2e, 0x31, 0x17, 0x53, 0x4b, 0x4c, + 0xf4, 0xa5, 0x40, 0x3e, 0x00, 0x10, 0x13, 0x2f, 0xf5, 0x83, 0x33, 0x6a, 0xe8, 0x2d, 0xd7, 0x16, + 0x93, 0x3e, 0x3a, 0xd4, 0x9d, 0x26, 0x26, 0x1e, 0x15, 0x82, 0x8b, 0xcc, 0xf4, 0xbe, 0x2d, 0x26, + 0x5f, 0x6a, 0xdb, 0xc4, 0x8e, 0x04, 0x57, 0x13, 0x88, 0x79, 0x06, 0xb6, 0x98, 0xbc, 0x40, 0x87, + 0xca, 0x2a, 0x8b, 0xac, 0x38, 0xf0, 0xb6, 0x64, 0x95, 0x55, 0x56, 0x59, 0x71, 0xe0, 0xb5, 0x65, + 0x3d, 0xab, 0x2c, 0xb3, 0xe2, 0xcc, 0xdb, 0x96, 0xb5, 0xac, 0xb2, 0xca, 0x6a, 0x17, 0xb1, 0x26, + 0x6b, 0xef, 0xef, 0x0d, 0xe8, 0xd4, 0xbe, 0x4e, 0x55, 0x03, 0x13, 0xe1, 0x65, 0x11, 0xa5, 0xa9, + 0xfa, 0x90, 0xc2, 0xab, 0x1b, 0x12, 0x31, 0x30, 0x1e, 0xc5, 0x97, 0x08, 0x4f, 0xe4, 0x49, 0x52, + 0x7c, 0x68, 0x59, 0xae, 0x9d, 0x08, 0x17, 0x1d, 0x66, 0x39, 0x93, 0x98, 0xae, 0x59, 0x2c, 0x0f, + 0xd0, 0x41, 0x7e, 0x09, 0x24, 0x11, 0x5e, 0x9e, 0xb0, 0x44, 0x52, 0x21, 0xf2, 0x54, 0xb2, 0x61, + 0xf9, 0x51, 0xb0, 0x9e, 0x88, 0xd3, 0xd9, 0x05, 0xf2, 0x48, 0xb3, 0x99, 0xcb, 0xc6, 0xb4, 0xac, + 0x9d, 0x88, 0x57, 0xfa, 0xe6, 0x38, 0x70, 0x7e, 0xf8, 0x71, 0xeb, 0xbd, 0x7f, 0xff, 0xb8, 0xf5, + 0xde, 0x9f, 0xa7, 0x5b, 0x8d, 0x1f, 0xa6, 0x5b, 0x8d, 0x7f, 0x4c, 0xb7, 0x1a, 0xff, 0x9d, 0x6e, + 0x35, 0x86, 0x4b, 0xfa, 0xcf, 0x95, 0x5f, 0xfd, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xc4, 0x4e, 0x24, + 0x22, 0xc4, 0x11, 0x00, 0x00, +} + +func (m *Metrics) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Metrics) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Metrics) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.MemoryOomControl != nil { + { + size, err := m.MemoryOomControl.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + if m.CgroupStats != nil { + { + size, err := m.CgroupStats.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if len(m.Network) > 0 { + for iNdEx := len(m.Network) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Network[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if m.Rdma != nil { + { + size, err := m.Rdma.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.Blkio != nil { + { + size, err := m.Blkio.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.Memory != nil { + { + size, err := m.Memory.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.CPU != nil { + { + size, err := m.CPU.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Pids != nil { + { + size, err := m.Pids.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Hugetlb) > 0 { + for iNdEx := len(m.Hugetlb) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Hugetlb[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *HugetlbStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HugetlbStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HugetlbStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Pagesize) > 0 { + i -= len(m.Pagesize) + copy(dAtA[i:], m.Pagesize) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Pagesize))) + i-- + dAtA[i] = 0x22 + } + if m.Failcnt != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Failcnt)) + i-- + dAtA[i] = 0x18 + } + if m.Max != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Max)) + i-- + dAtA[i] = 0x10 + } + if m.Usage != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Usage)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *PidsStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PidsStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PidsStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Limit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x10 + } + if m.Current != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Current)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *CPUStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CPUStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CPUStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Throttling != nil { + { + size, err := m.Throttling.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Usage != nil { + { + size, err := m.Usage.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CPUUsage) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CPUUsage) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CPUUsage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.PerCPU) > 0 { + dAtA11 := make([]byte, len(m.PerCPU)*10) + var j10 int + for _, num := range m.PerCPU { + for num >= 1<<7 { + dAtA11[j10] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j10++ + } + dAtA11[j10] = uint8(num) + j10++ + } + i -= j10 + copy(dAtA[i:], dAtA11[:j10]) + i = encodeVarintMetrics(dAtA, i, uint64(j10)) + i-- + dAtA[i] = 0x22 + } + if m.User != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.User)) + i-- + dAtA[i] = 0x18 + } + if m.Kernel != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Kernel)) + i-- + dAtA[i] = 0x10 + } + if m.Total != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Total)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Throttle) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Throttle) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Throttle) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.ThrottledTime != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ThrottledTime)) + i-- + dAtA[i] = 0x18 + } + if m.ThrottledPeriods != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ThrottledPeriods)) + i-- + dAtA[i] = 0x10 + } + if m.Periods != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Periods)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MemoryStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MemoryStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MemoryStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.KernelTCP != nil { + { + size, err := m.KernelTCP.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0xa2 + } + if m.Kernel != nil { + { + size, err := m.Kernel.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x9a + } + if m.Swap != nil { + { + size, err := m.Swap.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x92 + } + if m.Usage != nil { + { + size, err := m.Usage.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x8a + } + if m.TotalUnevictable != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalUnevictable)) + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x80 + } + if m.TotalActiveFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalActiveFile)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xf8 + } + if m.TotalInactiveFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalInactiveFile)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xf0 + } + if m.TotalActiveAnon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalActiveAnon)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe8 + } + if m.TotalInactiveAnon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalInactiveAnon)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe0 + } + if m.TotalPgMajFault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgMajFault)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xd8 + } + if m.TotalPgFault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgFault)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xd0 + } + if m.TotalPgPgOut != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgPgOut)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc8 + } + if m.TotalPgPgIn != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalPgPgIn)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc0 + } + if m.TotalWriteback != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalWriteback)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb8 + } + if m.TotalDirty != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalDirty)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb0 + } + if m.TotalMappedFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalMappedFile)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa8 + } + if m.TotalRSSHuge != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalRSSHuge)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa0 + } + if m.TotalRSS != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalRSS)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } + if m.TotalCache != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TotalCache)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x90 + } + if m.HierarchicalSwapLimit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.HierarchicalSwapLimit)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x88 + } + if m.HierarchicalMemoryLimit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.HierarchicalMemoryLimit)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } + if m.Unevictable != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Unevictable)) + i-- + dAtA[i] = 0x78 + } + if m.ActiveFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ActiveFile)) + i-- + dAtA[i] = 0x70 + } + if m.InactiveFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.InactiveFile)) + i-- + dAtA[i] = 0x68 + } + if m.ActiveAnon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ActiveAnon)) + i-- + dAtA[i] = 0x60 + } + if m.InactiveAnon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.InactiveAnon)) + i-- + dAtA[i] = 0x58 + } + if m.PgMajFault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.PgMajFault)) + i-- + dAtA[i] = 0x50 + } + if m.PgFault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.PgFault)) + i-- + dAtA[i] = 0x48 + } + if m.PgPgOut != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.PgPgOut)) + i-- + dAtA[i] = 0x40 + } + if m.PgPgIn != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.PgPgIn)) + i-- + dAtA[i] = 0x38 + } + if m.Writeback != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Writeback)) + i-- + dAtA[i] = 0x30 + } + if m.Dirty != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Dirty)) + i-- + dAtA[i] = 0x28 + } + if m.MappedFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.MappedFile)) + i-- + dAtA[i] = 0x20 + } + if m.RSSHuge != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RSSHuge)) + i-- + dAtA[i] = 0x18 + } + if m.RSS != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RSS)) + i-- + dAtA[i] = 0x10 + } + if m.Cache != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Cache)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MemoryEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MemoryEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MemoryEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Failcnt != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Failcnt)) + i-- + dAtA[i] = 0x20 + } + if m.Max != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Max)) + i-- + dAtA[i] = 0x18 + } + if m.Usage != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Usage)) + i-- + dAtA[i] = 0x10 + } + if m.Limit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MemoryOomControl) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MemoryOomControl) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MemoryOomControl) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.OomKill != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.OomKill)) + i-- + dAtA[i] = 0x18 + } + if m.UnderOom != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.UnderOom)) + i-- + dAtA[i] = 0x10 + } + if m.OomKillDisable != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.OomKillDisable)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *BlkIOStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BlkIOStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BlkIOStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.SectorsRecursive) > 0 { + for iNdEx := len(m.SectorsRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.SectorsRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } + if len(m.IoTimeRecursive) > 0 { + for iNdEx := len(m.IoTimeRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoTimeRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if len(m.IoMergedRecursive) > 0 { + for iNdEx := len(m.IoMergedRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoMergedRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.IoWaitTimeRecursive) > 0 { + for iNdEx := len(m.IoWaitTimeRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoWaitTimeRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.IoServiceTimeRecursive) > 0 { + for iNdEx := len(m.IoServiceTimeRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoServiceTimeRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.IoQueuedRecursive) > 0 { + for iNdEx := len(m.IoQueuedRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoQueuedRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.IoServicedRecursive) > 0 { + for iNdEx := len(m.IoServicedRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoServicedRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.IoServiceBytesRecursive) > 0 { + for iNdEx := len(m.IoServiceBytesRecursive) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.IoServiceBytesRecursive[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *BlkIOEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *BlkIOEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BlkIOEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Value != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Value)) + i-- + dAtA[i] = 0x28 + } + if m.Minor != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Minor)) + i-- + dAtA[i] = 0x20 + } + if m.Major != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Major)) + i-- + dAtA[i] = 0x18 + } + if len(m.Device) > 0 { + i -= len(m.Device) + copy(dAtA[i:], m.Device) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Device))) + i-- + dAtA[i] = 0x12 + } + if len(m.Op) > 0 { + i -= len(m.Op) + copy(dAtA[i:], m.Op) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Op))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RdmaStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RdmaStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RdmaStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Limit) > 0 { + for iNdEx := len(m.Limit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Limit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Current) > 0 { + for iNdEx := len(m.Current) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Current[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *RdmaEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RdmaEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RdmaEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.HcaObjects != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.HcaObjects)) + i-- + dAtA[i] = 0x18 + } + if m.HcaHandles != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.HcaHandles)) + i-- + dAtA[i] = 0x10 + } + if len(m.Device) > 0 { + i -= len(m.Device) + copy(dAtA[i:], m.Device) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Device))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *NetworkStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NetworkStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NetworkStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.TxDropped != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TxDropped)) + i-- + dAtA[i] = 0x48 + } + if m.TxErrors != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TxErrors)) + i-- + dAtA[i] = 0x40 + } + if m.TxPackets != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TxPackets)) + i-- + dAtA[i] = 0x38 + } + if m.TxBytes != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.TxBytes)) + i-- + dAtA[i] = 0x30 + } + if m.RxDropped != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RxDropped)) + i-- + dAtA[i] = 0x28 + } + if m.RxErrors != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RxErrors)) + i-- + dAtA[i] = 0x20 + } + if m.RxPackets != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RxPackets)) + i-- + dAtA[i] = 0x18 + } + if m.RxBytes != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.RxBytes)) + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CgroupStats) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CgroupStats) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CgroupStats) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.NrIoWait != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrIoWait)) + i-- + dAtA[i] = 0x28 + } + if m.NrUninterruptible != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrUninterruptible)) + i-- + dAtA[i] = 0x20 + } + if m.NrStopped != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrStopped)) + i-- + dAtA[i] = 0x18 + } + if m.NrRunning != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrRunning)) + i-- + dAtA[i] = 0x10 + } + if m.NrSleeping != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrSleeping)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintMetrics(dAtA []byte, offset int, v uint64) int { + offset -= sovMetrics(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Metrics) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Hugetlb) > 0 { + for _, e := range m.Hugetlb { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.Pids != nil { + l = m.Pids.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.CPU != nil { + l = m.CPU.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Memory != nil { + l = m.Memory.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Blkio != nil { + l = m.Blkio.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Rdma != nil { + l = m.Rdma.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if len(m.Network) > 0 { + for _, e := range m.Network { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.CgroupStats != nil { + l = m.CgroupStats.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.MemoryOomControl != nil { + l = m.MemoryOomControl.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *HugetlbStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Usage != 0 { + n += 1 + sovMetrics(uint64(m.Usage)) + } + if m.Max != 0 { + n += 1 + sovMetrics(uint64(m.Max)) + } + if m.Failcnt != 0 { + n += 1 + sovMetrics(uint64(m.Failcnt)) + } + l = len(m.Pagesize) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *PidsStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Current != 0 { + n += 1 + sovMetrics(uint64(m.Current)) + } + if m.Limit != 0 { + n += 1 + sovMetrics(uint64(m.Limit)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CPUStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Usage != nil { + l = m.Usage.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Throttling != nil { + l = m.Throttling.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CPUUsage) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Total != 0 { + n += 1 + sovMetrics(uint64(m.Total)) + } + if m.Kernel != 0 { + n += 1 + sovMetrics(uint64(m.Kernel)) + } + if m.User != 0 { + n += 1 + sovMetrics(uint64(m.User)) + } + if len(m.PerCPU) > 0 { + l = 0 + for _, e := range m.PerCPU { + l += sovMetrics(uint64(e)) + } + n += 1 + sovMetrics(uint64(l)) + l + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Throttle) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Periods != 0 { + n += 1 + sovMetrics(uint64(m.Periods)) + } + if m.ThrottledPeriods != 0 { + n += 1 + sovMetrics(uint64(m.ThrottledPeriods)) + } + if m.ThrottledTime != 0 { + n += 1 + sovMetrics(uint64(m.ThrottledTime)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *MemoryStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Cache != 0 { + n += 1 + sovMetrics(uint64(m.Cache)) + } + if m.RSS != 0 { + n += 1 + sovMetrics(uint64(m.RSS)) + } + if m.RSSHuge != 0 { + n += 1 + sovMetrics(uint64(m.RSSHuge)) + } + if m.MappedFile != 0 { + n += 1 + sovMetrics(uint64(m.MappedFile)) + } + if m.Dirty != 0 { + n += 1 + sovMetrics(uint64(m.Dirty)) + } + if m.Writeback != 0 { + n += 1 + sovMetrics(uint64(m.Writeback)) + } + if m.PgPgIn != 0 { + n += 1 + sovMetrics(uint64(m.PgPgIn)) + } + if m.PgPgOut != 0 { + n += 1 + sovMetrics(uint64(m.PgPgOut)) + } + if m.PgFault != 0 { + n += 1 + sovMetrics(uint64(m.PgFault)) + } + if m.PgMajFault != 0 { + n += 1 + sovMetrics(uint64(m.PgMajFault)) + } + if m.InactiveAnon != 0 { + n += 1 + sovMetrics(uint64(m.InactiveAnon)) + } + if m.ActiveAnon != 0 { + n += 1 + sovMetrics(uint64(m.ActiveAnon)) + } + if m.InactiveFile != 0 { + n += 1 + sovMetrics(uint64(m.InactiveFile)) + } + if m.ActiveFile != 0 { + n += 1 + sovMetrics(uint64(m.ActiveFile)) + } + if m.Unevictable != 0 { + n += 1 + sovMetrics(uint64(m.Unevictable)) + } + if m.HierarchicalMemoryLimit != 0 { + n += 2 + sovMetrics(uint64(m.HierarchicalMemoryLimit)) + } + if m.HierarchicalSwapLimit != 0 { + n += 2 + sovMetrics(uint64(m.HierarchicalSwapLimit)) + } + if m.TotalCache != 0 { + n += 2 + sovMetrics(uint64(m.TotalCache)) + } + if m.TotalRSS != 0 { + n += 2 + sovMetrics(uint64(m.TotalRSS)) + } + if m.TotalRSSHuge != 0 { + n += 2 + sovMetrics(uint64(m.TotalRSSHuge)) + } + if m.TotalMappedFile != 0 { + n += 2 + sovMetrics(uint64(m.TotalMappedFile)) + } + if m.TotalDirty != 0 { + n += 2 + sovMetrics(uint64(m.TotalDirty)) + } + if m.TotalWriteback != 0 { + n += 2 + sovMetrics(uint64(m.TotalWriteback)) + } + if m.TotalPgPgIn != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgPgIn)) + } + if m.TotalPgPgOut != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgPgOut)) + } + if m.TotalPgFault != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgFault)) + } + if m.TotalPgMajFault != 0 { + n += 2 + sovMetrics(uint64(m.TotalPgMajFault)) + } + if m.TotalInactiveAnon != 0 { + n += 2 + sovMetrics(uint64(m.TotalInactiveAnon)) + } + if m.TotalActiveAnon != 0 { + n += 2 + sovMetrics(uint64(m.TotalActiveAnon)) + } + if m.TotalInactiveFile != 0 { + n += 2 + sovMetrics(uint64(m.TotalInactiveFile)) + } + if m.TotalActiveFile != 0 { + n += 2 + sovMetrics(uint64(m.TotalActiveFile)) + } + if m.TotalUnevictable != 0 { + n += 2 + sovMetrics(uint64(m.TotalUnevictable)) + } + if m.Usage != nil { + l = m.Usage.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.Swap != nil { + l = m.Swap.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.Kernel != nil { + l = m.Kernel.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.KernelTCP != nil { + l = m.KernelTCP.Size() + n += 2 + l + sovMetrics(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *MemoryEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Limit != 0 { + n += 1 + sovMetrics(uint64(m.Limit)) + } + if m.Usage != 0 { + n += 1 + sovMetrics(uint64(m.Usage)) + } + if m.Max != 0 { + n += 1 + sovMetrics(uint64(m.Max)) + } + if m.Failcnt != 0 { + n += 1 + sovMetrics(uint64(m.Failcnt)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *MemoryOomControl) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OomKillDisable != 0 { + n += 1 + sovMetrics(uint64(m.OomKillDisable)) + } + if m.UnderOom != 0 { + n += 1 + sovMetrics(uint64(m.UnderOom)) + } + if m.OomKill != 0 { + n += 1 + sovMetrics(uint64(m.OomKill)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *BlkIOStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.IoServiceBytesRecursive) > 0 { + for _, e := range m.IoServiceBytesRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoServicedRecursive) > 0 { + for _, e := range m.IoServicedRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoQueuedRecursive) > 0 { + for _, e := range m.IoQueuedRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoServiceTimeRecursive) > 0 { + for _, e := range m.IoServiceTimeRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoWaitTimeRecursive) > 0 { + for _, e := range m.IoWaitTimeRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoMergedRecursive) > 0 { + for _, e := range m.IoMergedRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.IoTimeRecursive) > 0 { + for _, e := range m.IoTimeRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.SectorsRecursive) > 0 { + for _, e := range m.SectorsRecursive { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *BlkIOEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Op) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + l = len(m.Device) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Major != 0 { + n += 1 + sovMetrics(uint64(m.Major)) + } + if m.Minor != 0 { + n += 1 + sovMetrics(uint64(m.Minor)) + } + if m.Value != 0 { + n += 1 + sovMetrics(uint64(m.Value)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RdmaStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Current) > 0 { + for _, e := range m.Current { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.Limit) > 0 { + for _, e := range m.Limit { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RdmaEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Device) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.HcaHandles != 0 { + n += 1 + sovMetrics(uint64(m.HcaHandles)) + } + if m.HcaObjects != 0 { + n += 1 + sovMetrics(uint64(m.HcaObjects)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *NetworkStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.RxBytes != 0 { + n += 1 + sovMetrics(uint64(m.RxBytes)) + } + if m.RxPackets != 0 { + n += 1 + sovMetrics(uint64(m.RxPackets)) + } + if m.RxErrors != 0 { + n += 1 + sovMetrics(uint64(m.RxErrors)) + } + if m.RxDropped != 0 { + n += 1 + sovMetrics(uint64(m.RxDropped)) + } + if m.TxBytes != 0 { + n += 1 + sovMetrics(uint64(m.TxBytes)) + } + if m.TxPackets != 0 { + n += 1 + sovMetrics(uint64(m.TxPackets)) + } + if m.TxErrors != 0 { + n += 1 + sovMetrics(uint64(m.TxErrors)) + } + if m.TxDropped != 0 { + n += 1 + sovMetrics(uint64(m.TxDropped)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CgroupStats) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NrSleeping != 0 { + n += 1 + sovMetrics(uint64(m.NrSleeping)) + } + if m.NrRunning != 0 { + n += 1 + sovMetrics(uint64(m.NrRunning)) + } + if m.NrStopped != 0 { + n += 1 + sovMetrics(uint64(m.NrStopped)) + } + if m.NrUninterruptible != 0 { + n += 1 + sovMetrics(uint64(m.NrUninterruptible)) + } + if m.NrIoWait != 0 { + n += 1 + sovMetrics(uint64(m.NrIoWait)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovMetrics(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMetrics(x uint64) (n int) { + return sovMetrics(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Metrics) String() string { + if this == nil { + return "nil" + } + repeatedStringForHugetlb := "[]*HugetlbStat{" + for _, f := range this.Hugetlb { + repeatedStringForHugetlb += strings.Replace(f.String(), "HugetlbStat", "HugetlbStat", 1) + "," + } + repeatedStringForHugetlb += "}" + repeatedStringForNetwork := "[]*NetworkStat{" + for _, f := range this.Network { + repeatedStringForNetwork += strings.Replace(f.String(), "NetworkStat", "NetworkStat", 1) + "," + } + repeatedStringForNetwork += "}" + s := strings.Join([]string{`&Metrics{`, + `Hugetlb:` + repeatedStringForHugetlb + `,`, + `Pids:` + strings.Replace(this.Pids.String(), "PidsStat", "PidsStat", 1) + `,`, + `CPU:` + strings.Replace(this.CPU.String(), "CPUStat", "CPUStat", 1) + `,`, + `Memory:` + strings.Replace(this.Memory.String(), "MemoryStat", "MemoryStat", 1) + `,`, + `Blkio:` + strings.Replace(this.Blkio.String(), "BlkIOStat", "BlkIOStat", 1) + `,`, + `Rdma:` + strings.Replace(this.Rdma.String(), "RdmaStat", "RdmaStat", 1) + `,`, + `Network:` + repeatedStringForNetwork + `,`, + `CgroupStats:` + strings.Replace(this.CgroupStats.String(), "CgroupStats", "CgroupStats", 1) + `,`, + `MemoryOomControl:` + strings.Replace(this.MemoryOomControl.String(), "MemoryOomControl", "MemoryOomControl", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *HugetlbStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&HugetlbStat{`, + `Usage:` + fmt.Sprintf("%v", this.Usage) + `,`, + `Max:` + fmt.Sprintf("%v", this.Max) + `,`, + `Failcnt:` + fmt.Sprintf("%v", this.Failcnt) + `,`, + `Pagesize:` + fmt.Sprintf("%v", this.Pagesize) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *PidsStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PidsStat{`, + `Current:` + fmt.Sprintf("%v", this.Current) + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *CPUStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CPUStat{`, + `Usage:` + strings.Replace(this.Usage.String(), "CPUUsage", "CPUUsage", 1) + `,`, + `Throttling:` + strings.Replace(this.Throttling.String(), "Throttle", "Throttle", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *CPUUsage) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CPUUsage{`, + `Total:` + fmt.Sprintf("%v", this.Total) + `,`, + `Kernel:` + fmt.Sprintf("%v", this.Kernel) + `,`, + `User:` + fmt.Sprintf("%v", this.User) + `,`, + `PerCPU:` + fmt.Sprintf("%v", this.PerCPU) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *Throttle) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Throttle{`, + `Periods:` + fmt.Sprintf("%v", this.Periods) + `,`, + `ThrottledPeriods:` + fmt.Sprintf("%v", this.ThrottledPeriods) + `,`, + `ThrottledTime:` + fmt.Sprintf("%v", this.ThrottledTime) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *MemoryStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MemoryStat{`, + `Cache:` + fmt.Sprintf("%v", this.Cache) + `,`, + `RSS:` + fmt.Sprintf("%v", this.RSS) + `,`, + `RSSHuge:` + fmt.Sprintf("%v", this.RSSHuge) + `,`, + `MappedFile:` + fmt.Sprintf("%v", this.MappedFile) + `,`, + `Dirty:` + fmt.Sprintf("%v", this.Dirty) + `,`, + `Writeback:` + fmt.Sprintf("%v", this.Writeback) + `,`, + `PgPgIn:` + fmt.Sprintf("%v", this.PgPgIn) + `,`, + `PgPgOut:` + fmt.Sprintf("%v", this.PgPgOut) + `,`, + `PgFault:` + fmt.Sprintf("%v", this.PgFault) + `,`, + `PgMajFault:` + fmt.Sprintf("%v", this.PgMajFault) + `,`, + `InactiveAnon:` + fmt.Sprintf("%v", this.InactiveAnon) + `,`, + `ActiveAnon:` + fmt.Sprintf("%v", this.ActiveAnon) + `,`, + `InactiveFile:` + fmt.Sprintf("%v", this.InactiveFile) + `,`, + `ActiveFile:` + fmt.Sprintf("%v", this.ActiveFile) + `,`, + `Unevictable:` + fmt.Sprintf("%v", this.Unevictable) + `,`, + `HierarchicalMemoryLimit:` + fmt.Sprintf("%v", this.HierarchicalMemoryLimit) + `,`, + `HierarchicalSwapLimit:` + fmt.Sprintf("%v", this.HierarchicalSwapLimit) + `,`, + `TotalCache:` + fmt.Sprintf("%v", this.TotalCache) + `,`, + `TotalRSS:` + fmt.Sprintf("%v", this.TotalRSS) + `,`, + `TotalRSSHuge:` + fmt.Sprintf("%v", this.TotalRSSHuge) + `,`, + `TotalMappedFile:` + fmt.Sprintf("%v", this.TotalMappedFile) + `,`, + `TotalDirty:` + fmt.Sprintf("%v", this.TotalDirty) + `,`, + `TotalWriteback:` + fmt.Sprintf("%v", this.TotalWriteback) + `,`, + `TotalPgPgIn:` + fmt.Sprintf("%v", this.TotalPgPgIn) + `,`, + `TotalPgPgOut:` + fmt.Sprintf("%v", this.TotalPgPgOut) + `,`, + `TotalPgFault:` + fmt.Sprintf("%v", this.TotalPgFault) + `,`, + `TotalPgMajFault:` + fmt.Sprintf("%v", this.TotalPgMajFault) + `,`, + `TotalInactiveAnon:` + fmt.Sprintf("%v", this.TotalInactiveAnon) + `,`, + `TotalActiveAnon:` + fmt.Sprintf("%v", this.TotalActiveAnon) + `,`, + `TotalInactiveFile:` + fmt.Sprintf("%v", this.TotalInactiveFile) + `,`, + `TotalActiveFile:` + fmt.Sprintf("%v", this.TotalActiveFile) + `,`, + `TotalUnevictable:` + fmt.Sprintf("%v", this.TotalUnevictable) + `,`, + `Usage:` + strings.Replace(this.Usage.String(), "MemoryEntry", "MemoryEntry", 1) + `,`, + `Swap:` + strings.Replace(this.Swap.String(), "MemoryEntry", "MemoryEntry", 1) + `,`, + `Kernel:` + strings.Replace(this.Kernel.String(), "MemoryEntry", "MemoryEntry", 1) + `,`, + `KernelTCP:` + strings.Replace(this.KernelTCP.String(), "MemoryEntry", "MemoryEntry", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *MemoryEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MemoryEntry{`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, + `Usage:` + fmt.Sprintf("%v", this.Usage) + `,`, + `Max:` + fmt.Sprintf("%v", this.Max) + `,`, + `Failcnt:` + fmt.Sprintf("%v", this.Failcnt) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *MemoryOomControl) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MemoryOomControl{`, + `OomKillDisable:` + fmt.Sprintf("%v", this.OomKillDisable) + `,`, + `UnderOom:` + fmt.Sprintf("%v", this.UnderOom) + `,`, + `OomKill:` + fmt.Sprintf("%v", this.OomKill) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *BlkIOStat) String() string { + if this == nil { + return "nil" + } + repeatedStringForIoServiceBytesRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoServiceBytesRecursive { + repeatedStringForIoServiceBytesRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoServiceBytesRecursive += "}" + repeatedStringForIoServicedRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoServicedRecursive { + repeatedStringForIoServicedRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoServicedRecursive += "}" + repeatedStringForIoQueuedRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoQueuedRecursive { + repeatedStringForIoQueuedRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoQueuedRecursive += "}" + repeatedStringForIoServiceTimeRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoServiceTimeRecursive { + repeatedStringForIoServiceTimeRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoServiceTimeRecursive += "}" + repeatedStringForIoWaitTimeRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoWaitTimeRecursive { + repeatedStringForIoWaitTimeRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoWaitTimeRecursive += "}" + repeatedStringForIoMergedRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoMergedRecursive { + repeatedStringForIoMergedRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoMergedRecursive += "}" + repeatedStringForIoTimeRecursive := "[]*BlkIOEntry{" + for _, f := range this.IoTimeRecursive { + repeatedStringForIoTimeRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForIoTimeRecursive += "}" + repeatedStringForSectorsRecursive := "[]*BlkIOEntry{" + for _, f := range this.SectorsRecursive { + repeatedStringForSectorsRecursive += strings.Replace(f.String(), "BlkIOEntry", "BlkIOEntry", 1) + "," + } + repeatedStringForSectorsRecursive += "}" + s := strings.Join([]string{`&BlkIOStat{`, + `IoServiceBytesRecursive:` + repeatedStringForIoServiceBytesRecursive + `,`, + `IoServicedRecursive:` + repeatedStringForIoServicedRecursive + `,`, + `IoQueuedRecursive:` + repeatedStringForIoQueuedRecursive + `,`, + `IoServiceTimeRecursive:` + repeatedStringForIoServiceTimeRecursive + `,`, + `IoWaitTimeRecursive:` + repeatedStringForIoWaitTimeRecursive + `,`, + `IoMergedRecursive:` + repeatedStringForIoMergedRecursive + `,`, + `IoTimeRecursive:` + repeatedStringForIoTimeRecursive + `,`, + `SectorsRecursive:` + repeatedStringForSectorsRecursive + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *BlkIOEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&BlkIOEntry{`, + `Op:` + fmt.Sprintf("%v", this.Op) + `,`, + `Device:` + fmt.Sprintf("%v", this.Device) + `,`, + `Major:` + fmt.Sprintf("%v", this.Major) + `,`, + `Minor:` + fmt.Sprintf("%v", this.Minor) + `,`, + `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *RdmaStat) String() string { + if this == nil { + return "nil" + } + repeatedStringForCurrent := "[]*RdmaEntry{" + for _, f := range this.Current { + repeatedStringForCurrent += strings.Replace(f.String(), "RdmaEntry", "RdmaEntry", 1) + "," + } + repeatedStringForCurrent += "}" + repeatedStringForLimit := "[]*RdmaEntry{" + for _, f := range this.Limit { + repeatedStringForLimit += strings.Replace(f.String(), "RdmaEntry", "RdmaEntry", 1) + "," + } + repeatedStringForLimit += "}" + s := strings.Join([]string{`&RdmaStat{`, + `Current:` + repeatedStringForCurrent + `,`, + `Limit:` + repeatedStringForLimit + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *RdmaEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RdmaEntry{`, + `Device:` + fmt.Sprintf("%v", this.Device) + `,`, + `HcaHandles:` + fmt.Sprintf("%v", this.HcaHandles) + `,`, + `HcaObjects:` + fmt.Sprintf("%v", this.HcaObjects) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *NetworkStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&NetworkStat{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `RxBytes:` + fmt.Sprintf("%v", this.RxBytes) + `,`, + `RxPackets:` + fmt.Sprintf("%v", this.RxPackets) + `,`, + `RxErrors:` + fmt.Sprintf("%v", this.RxErrors) + `,`, + `RxDropped:` + fmt.Sprintf("%v", this.RxDropped) + `,`, + `TxBytes:` + fmt.Sprintf("%v", this.TxBytes) + `,`, + `TxPackets:` + fmt.Sprintf("%v", this.TxPackets) + `,`, + `TxErrors:` + fmt.Sprintf("%v", this.TxErrors) + `,`, + `TxDropped:` + fmt.Sprintf("%v", this.TxDropped) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *CgroupStats) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CgroupStats{`, + `NrSleeping:` + fmt.Sprintf("%v", this.NrSleeping) + `,`, + `NrRunning:` + fmt.Sprintf("%v", this.NrRunning) + `,`, + `NrStopped:` + fmt.Sprintf("%v", this.NrStopped) + `,`, + `NrUninterruptible:` + fmt.Sprintf("%v", this.NrUninterruptible) + `,`, + `NrIoWait:` + fmt.Sprintf("%v", this.NrIoWait) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringMetrics(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Metrics) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Metrics: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Metrics: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hugetlb", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hugetlb = append(m.Hugetlb, &HugetlbStat{}) + if err := m.Hugetlb[len(m.Hugetlb)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pids", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pids == nil { + m.Pids = &PidsStat{} + } + if err := m.Pids.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CPU", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CPU == nil { + m.CPU = &CPUStat{} + } + if err := m.CPU.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Memory == nil { + m.Memory = &MemoryStat{} + } + if err := m.Memory.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Blkio", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Blkio == nil { + m.Blkio = &BlkIOStat{} + } + if err := m.Blkio.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rdma", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Rdma == nil { + m.Rdma = &RdmaStat{} + } + if err := m.Rdma.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Network", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Network = append(m.Network, &NetworkStat{}) + if err := m.Network[len(m.Network)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CgroupStats", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CgroupStats == nil { + m.CgroupStats = &CgroupStats{} + } + if err := m.CgroupStats.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MemoryOomControl", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.MemoryOomControl == nil { + m.MemoryOomControl = &MemoryOomControl{} + } + if err := m.MemoryOomControl.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HugetlbStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HugetlbStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HugetlbStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + m.Usage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Usage |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType) + } + m.Max = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Max |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Failcnt", wireType) + } + m.Failcnt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Failcnt |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagesize", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pagesize = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PidsStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PidsStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PidsStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) + } + m.Current = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Current |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CPUStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CPUStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CPUStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Usage == nil { + m.Usage = &CPUUsage{} + } + if err := m.Usage.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Throttling", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Throttling == nil { + m.Throttling = &Throttle{} + } + if err := m.Throttling.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CPUUsage) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CPUUsage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CPUUsage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) + } + m.Total = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Total |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Kernel", wireType) + } + m.Kernel = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Kernel |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) + } + m.User = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.User |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType == 0 { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PerCPU = append(m.PerCPU, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.PerCPU) == 0 { + m.PerCPU = make([]uint64, 0, elementCount) + } + for iNdEx < postIndex { + var v uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PerCPU = append(m.PerCPU, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field PerCPU", wireType) + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Throttle) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Throttle: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Throttle: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Periods", wireType) + } + m.Periods = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Periods |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ThrottledPeriods", wireType) + } + m.ThrottledPeriods = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ThrottledPeriods |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ThrottledTime", wireType) + } + m.ThrottledTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ThrottledTime |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MemoryStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MemoryStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MemoryStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cache", wireType) + } + m.Cache = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Cache |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RSS", wireType) + } + m.RSS = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RSS |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RSSHuge", wireType) + } + m.RSSHuge = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RSSHuge |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MappedFile", wireType) + } + m.MappedFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MappedFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Dirty", wireType) + } + m.Dirty = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Dirty |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Writeback", wireType) + } + m.Writeback = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Writeback |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgPgIn", wireType) + } + m.PgPgIn = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgPgIn |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgPgOut", wireType) + } + m.PgPgOut = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgPgOut |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgFault", wireType) + } + m.PgFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgFault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PgMajFault", wireType) + } + m.PgMajFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PgMajFault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InactiveAnon", wireType) + } + m.InactiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InactiveAnon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveAnon", wireType) + } + m.ActiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ActiveAnon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InactiveFile", wireType) + } + m.InactiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InactiveFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveFile", wireType) + } + m.ActiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ActiveFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Unevictable", wireType) + } + m.Unevictable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Unevictable |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HierarchicalMemoryLimit", wireType) + } + m.HierarchicalMemoryLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HierarchicalMemoryLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HierarchicalSwapLimit", wireType) + } + m.HierarchicalSwapLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HierarchicalSwapLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 18: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalCache", wireType) + } + m.TotalCache = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalCache |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalRSS", wireType) + } + m.TotalRSS = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalRSS |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 20: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalRSSHuge", wireType) + } + m.TotalRSSHuge = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalRSSHuge |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 21: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalMappedFile", wireType) + } + m.TotalMappedFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalMappedFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 22: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalDirty", wireType) + } + m.TotalDirty = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalDirty |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 23: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalWriteback", wireType) + } + m.TotalWriteback = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalWriteback |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 24: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgPgIn", wireType) + } + m.TotalPgPgIn = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgPgIn |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 25: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgPgOut", wireType) + } + m.TotalPgPgOut = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgPgOut |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 26: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgFault", wireType) + } + m.TotalPgFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgFault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 27: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalPgMajFault", wireType) + } + m.TotalPgMajFault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalPgMajFault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 28: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalInactiveAnon", wireType) + } + m.TotalInactiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalInactiveAnon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 29: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalActiveAnon", wireType) + } + m.TotalActiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalActiveAnon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 30: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalInactiveFile", wireType) + } + m.TotalInactiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalInactiveFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 31: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalActiveFile", wireType) + } + m.TotalActiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalActiveFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 32: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalUnevictable", wireType) + } + m.TotalUnevictable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TotalUnevictable |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 33: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Usage == nil { + m.Usage = &MemoryEntry{} + } + if err := m.Usage.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 34: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Swap", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Swap == nil { + m.Swap = &MemoryEntry{} + } + if err := m.Swap.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 35: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kernel", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Kernel == nil { + m.Kernel = &MemoryEntry{} + } + if err := m.Kernel.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 36: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KernelTCP", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.KernelTCP == nil { + m.KernelTCP = &MemoryEntry{} + } + if err := m.KernelTCP.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MemoryEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MemoryEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MemoryEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + m.Usage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Usage |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType) + } + m.Max = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Max |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Failcnt", wireType) + } + m.Failcnt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Failcnt |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MemoryOomControl) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MemoryOomControl: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MemoryOomControl: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OomKillDisable", wireType) + } + m.OomKillDisable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OomKillDisable |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UnderOom", wireType) + } + m.UnderOom = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UnderOom |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OomKill", wireType) + } + m.OomKill = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OomKill |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BlkIOStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BlkIOStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BlkIOStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoServiceBytesRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoServiceBytesRecursive = append(m.IoServiceBytesRecursive, &BlkIOEntry{}) + if err := m.IoServiceBytesRecursive[len(m.IoServiceBytesRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoServicedRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoServicedRecursive = append(m.IoServicedRecursive, &BlkIOEntry{}) + if err := m.IoServicedRecursive[len(m.IoServicedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoQueuedRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoQueuedRecursive = append(m.IoQueuedRecursive, &BlkIOEntry{}) + if err := m.IoQueuedRecursive[len(m.IoQueuedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoServiceTimeRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoServiceTimeRecursive = append(m.IoServiceTimeRecursive, &BlkIOEntry{}) + if err := m.IoServiceTimeRecursive[len(m.IoServiceTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoWaitTimeRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoWaitTimeRecursive = append(m.IoWaitTimeRecursive, &BlkIOEntry{}) + if err := m.IoWaitTimeRecursive[len(m.IoWaitTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoMergedRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoMergedRecursive = append(m.IoMergedRecursive, &BlkIOEntry{}) + if err := m.IoMergedRecursive[len(m.IoMergedRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IoTimeRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IoTimeRecursive = append(m.IoTimeRecursive, &BlkIOEntry{}) + if err := m.IoTimeRecursive[len(m.IoTimeRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SectorsRecursive", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SectorsRecursive = append(m.SectorsRecursive, &BlkIOEntry{}) + if err := m.SectorsRecursive[len(m.SectorsRecursive)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *BlkIOEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: BlkIOEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: BlkIOEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Op", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Op = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Device = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Major", wireType) + } + m.Major = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Major |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Minor", wireType) + } + m.Minor = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Minor |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + m.Value = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Value |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RdmaStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RdmaStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RdmaStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Current = append(m.Current, &RdmaEntry{}) + if err := m.Current[len(m.Current)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Limit = append(m.Limit, &RdmaEntry{}) + if err := m.Limit[len(m.Limit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RdmaEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RdmaEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RdmaEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Device = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HcaHandles", wireType) + } + m.HcaHandles = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HcaHandles |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HcaObjects", wireType) + } + m.HcaObjects = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HcaObjects |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NetworkStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NetworkStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NetworkStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxBytes", wireType) + } + m.RxBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxBytes |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxPackets", wireType) + } + m.RxPackets = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxPackets |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxErrors", wireType) + } + m.RxErrors = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxErrors |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RxDropped", wireType) + } + m.RxDropped = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RxDropped |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxBytes", wireType) + } + m.TxBytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxBytes |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxPackets", wireType) + } + m.TxPackets = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxPackets |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxErrors", wireType) + } + m.TxErrors = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxErrors |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TxDropped", wireType) + } + m.TxDropped = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TxDropped |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CgroupStats) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CgroupStats: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CgroupStats: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrSleeping", wireType) + } + m.NrSleeping = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrSleeping |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrRunning", wireType) + } + m.NrRunning = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrRunning |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrStopped", wireType) + } + m.NrStopped = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrStopped |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrUninterruptible", wireType) + } + m.NrUninterruptible = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrUninterruptible |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrIoWait", wireType) + } + m.NrIoWait = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrIoWait |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMetrics(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMetrics + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMetrics + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMetrics + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMetrics = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMetrics = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMetrics = fmt.Errorf("proto: unexpected end of group") +) diff --git a/vendor/github.com/containerd/cgroups/stats/v1/metrics.proto b/vendor/github.com/containerd/cgroups/stats/v1/metrics.proto new file mode 100644 index 0000000000000..b3f6cc37d8c98 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/stats/v1/metrics.proto @@ -0,0 +1,158 @@ +syntax = "proto3"; + +package io.containerd.cgroups.v1; + +import "gogoproto/gogo.proto"; + +message Metrics { + repeated HugetlbStat hugetlb = 1; + PidsStat pids = 2; + CPUStat cpu = 3 [(gogoproto.customname) = "CPU"]; + MemoryStat memory = 4; + BlkIOStat blkio = 5; + RdmaStat rdma = 6; + repeated NetworkStat network = 7; + CgroupStats cgroup_stats = 8; + MemoryOomControl memory_oom_control = 9; +} + +message HugetlbStat { + uint64 usage = 1; + uint64 max = 2; + uint64 failcnt = 3; + string pagesize = 4; +} + +message PidsStat { + uint64 current = 1; + uint64 limit = 2; +} + +message CPUStat { + CPUUsage usage = 1; + Throttle throttling = 2; +} + +message CPUUsage { + // values in nanoseconds + uint64 total = 1; + uint64 kernel = 2; + uint64 user = 3; + repeated uint64 per_cpu = 4 [(gogoproto.customname) = "PerCPU"]; + +} + +message Throttle { + uint64 periods = 1; + uint64 throttled_periods = 2; + uint64 throttled_time = 3; +} + +message MemoryStat { + uint64 cache = 1; + uint64 rss = 2 [(gogoproto.customname) = "RSS"]; + uint64 rss_huge = 3 [(gogoproto.customname) = "RSSHuge"]; + uint64 mapped_file = 4; + uint64 dirty = 5; + uint64 writeback = 6; + uint64 pg_pg_in = 7; + uint64 pg_pg_out = 8; + uint64 pg_fault = 9; + uint64 pg_maj_fault = 10; + uint64 inactive_anon = 11; + uint64 active_anon = 12; + uint64 inactive_file = 13; + uint64 active_file = 14; + uint64 unevictable = 15; + uint64 hierarchical_memory_limit = 16; + uint64 hierarchical_swap_limit = 17; + uint64 total_cache = 18; + uint64 total_rss = 19 [(gogoproto.customname) = "TotalRSS"]; + uint64 total_rss_huge = 20 [(gogoproto.customname) = "TotalRSSHuge"]; + uint64 total_mapped_file = 21; + uint64 total_dirty = 22; + uint64 total_writeback = 23; + uint64 total_pg_pg_in = 24; + uint64 total_pg_pg_out = 25; + uint64 total_pg_fault = 26; + uint64 total_pg_maj_fault = 27; + uint64 total_inactive_anon = 28; + uint64 total_active_anon = 29; + uint64 total_inactive_file = 30; + uint64 total_active_file = 31; + uint64 total_unevictable = 32; + MemoryEntry usage = 33; + MemoryEntry swap = 34; + MemoryEntry kernel = 35; + MemoryEntry kernel_tcp = 36 [(gogoproto.customname) = "KernelTCP"]; + +} + +message MemoryEntry { + uint64 limit = 1; + uint64 usage = 2; + uint64 max = 3; + uint64 failcnt = 4; +} + +message MemoryOomControl { + uint64 oom_kill_disable = 1; + uint64 under_oom = 2; + uint64 oom_kill = 3; +} + +message BlkIOStat { + repeated BlkIOEntry io_service_bytes_recursive = 1; + repeated BlkIOEntry io_serviced_recursive = 2; + repeated BlkIOEntry io_queued_recursive = 3; + repeated BlkIOEntry io_service_time_recursive = 4; + repeated BlkIOEntry io_wait_time_recursive = 5; + repeated BlkIOEntry io_merged_recursive = 6; + repeated BlkIOEntry io_time_recursive = 7; + repeated BlkIOEntry sectors_recursive = 8; +} + +message BlkIOEntry { + string op = 1; + string device = 2; + uint64 major = 3; + uint64 minor = 4; + uint64 value = 5; +} + +message RdmaStat { + repeated RdmaEntry current = 1; + repeated RdmaEntry limit = 2; +} + +message RdmaEntry { + string device = 1; + uint32 hca_handles = 2; + uint32 hca_objects = 3; +} + +message NetworkStat { + string name = 1; + uint64 rx_bytes = 2; + uint64 rx_packets = 3; + uint64 rx_errors = 4; + uint64 rx_dropped = 5; + uint64 tx_bytes = 6; + uint64 tx_packets = 7; + uint64 tx_errors = 8; + uint64 tx_dropped = 9; +} + +// CgroupStats exports per-cgroup statistics. +message CgroupStats { + // number of tasks sleeping + uint64 nr_sleeping = 1; + // number of tasks running + uint64 nr_running = 2; + // number of tasks in stopped state + uint64 nr_stopped = 3; + // number of tasks in uninterruptible state + uint64 nr_uninterruptible = 4; + // number of tasks waiting on IO + uint64 nr_io_wait = 5; +} diff --git a/vendor/github.com/containerd/cgroups/subsystem.go b/vendor/github.com/containerd/cgroups/subsystem.go index 933a6c38d6b20..b2f41854d2c51 100644 --- a/vendor/github.com/containerd/cgroups/subsystem.go +++ b/vendor/github.com/containerd/cgroups/subsystem.go @@ -18,7 +18,9 @@ package cgroups import ( "fmt" + "os" + v1 "github.com/containerd/cgroups/stats/v1" specs "github.com/opencontainers/runtime-spec/specs-go" ) @@ -42,10 +44,9 @@ const ( ) // Subsystems returns a complete list of the default cgroups -// avaliable on most linux systems +// available on most linux systems func Subsystems() []Name { n := []Name{ - Hugetlb, Freezer, Pids, NetCLS, @@ -58,9 +59,12 @@ func Subsystems() []Name { Blkio, Rdma, } - if !isUserNS { + if !RunningInUserNS() { n = append(n, Devices) } + if _, err := os.Stat("/sys/kernel/mm/hugepages"); err == nil { + n = append(n, Hugetlb) + } return n } @@ -85,7 +89,7 @@ type deleter interface { type stater interface { Subsystem - Stat(path string, stats *Metrics) error + Stat(path string, stats *v1.Metrics) error } type updater interface { diff --git a/vendor/github.com/containerd/cgroups/systemd.go b/vendor/github.com/containerd/cgroups/systemd.go index 8153d744ce8b1..c17f34a62607f 100644 --- a/vendor/github.com/containerd/cgroups/systemd.go +++ b/vendor/github.com/containerd/cgroups/systemd.go @@ -17,13 +17,12 @@ package cgroups import ( - "fmt" "path/filepath" "strings" "sync" - systemdDbus "github.com/coreos/go-systemd/dbus" - "github.com/godbus/dbus" + systemdDbus "github.com/coreos/go-systemd/v22/dbus" + "github.com/godbus/dbus/v5" specs "github.com/opencontainers/runtime-spec/specs-go" ) @@ -32,6 +31,11 @@ const ( defaultSlice = "system.slice" ) +var ( + canDelegate bool + once sync.Once +) + func Systemd() ([]Subsystem, error) { root, err := v1MountPoint() if err != nil { @@ -54,7 +58,7 @@ func Slice(slice, name string) Path { slice = defaultSlice } return func(subsystem Name) (string, error) { - return filepath.Join(slice, unitName(name)), nil + return filepath.Join(slice, name), nil } } @@ -73,22 +77,46 @@ func (s *SystemdController) Name() Name { return SystemdDbus } -func (s *SystemdController) Create(path string, resources *specs.LinuxResources) error { +func (s *SystemdController) Create(path string, _ *specs.LinuxResources) error { conn, err := systemdDbus.New() if err != nil { return err } defer conn.Close() slice, name := splitName(path) + // We need to see if systemd can handle the delegate property + // Systemd will return an error if it cannot handle delegate regardless + // of its bool setting. + checkDelegate := func() { + canDelegate = true + dlSlice := newProperty("Delegate", true) + if _, err := conn.StartTransientUnit(slice, "testdelegate", []systemdDbus.Property{dlSlice}, nil); err != nil { + if dbusError, ok := err.(dbus.Error); ok { + // Starting with systemd v237, Delegate is not even a property of slices anymore, + // so the D-Bus call fails with "InvalidArgs" error. + if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") || strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.InvalidArgs") { + canDelegate = false + } + } + } + + conn.StopUnit(slice, "testDelegate", nil) + } + once.Do(checkDelegate) properties := []systemdDbus.Property{ - systemdDbus.PropDescription(fmt.Sprintf("cgroup %s", name)), + systemdDbus.PropDescription("cgroup " + name), systemdDbus.PropWants(slice), newProperty("DefaultDependencies", false), - newProperty("Delegate", true), newProperty("MemoryAccounting", true), newProperty("CPUAccounting", true), newProperty("BlockIOAccounting", true), } + + // If we can delegate, we add the property back in + if canDelegate { + properties = append(properties, newProperty("Delegate", true)) + } + ch := make(chan string) _, err = conn.StartTransientUnit(name, "replace", properties, ch) if err != nil { @@ -121,10 +149,6 @@ func newProperty(name string, units interface{}) systemdDbus.Property { } } -func unitName(name string) string { - return fmt.Sprintf("%s.slice", name) -} - func splitName(path string) (slice string, unit string) { slice, unit = filepath.Split(path) return strings.TrimSuffix(slice, "/"), unit diff --git a/vendor/github.com/containerd/cgroups/utils.go b/vendor/github.com/containerd/cgroups/utils.go index 345be4e463c0e..ed894b3ec46db 100644 --- a/vendor/github.com/containerd/cgroups/utils.go +++ b/vendor/github.com/containerd/cgroups/utils.go @@ -18,6 +18,7 @@ package cgroups import ( "bufio" + "errors" "fmt" "io" "io/ioutil" @@ -25,41 +26,93 @@ import ( "path/filepath" "strconv" "strings" + "sync" + "syscall" "time" units "github.com/docker/go-units" specs "github.com/opencontainers/runtime-spec/specs-go" + "golang.org/x/sys/unix" ) -var isUserNS = runningInUserNS() +var ( + nsOnce sync.Once + inUserNS bool + checkMode sync.Once + cgMode CGMode +) + +const unifiedMountpoint = "/sys/fs/cgroup" + +// CGMode is the cgroups mode of the host system +type CGMode int + +const ( + // Unavailable cgroup mountpoint + Unavailable CGMode = iota + // Legacy cgroups v1 + Legacy + // Hybrid with cgroups v1 and v2 controllers mounted + Hybrid + // Unified with only cgroups v2 mounted + Unified +) + +// Mode returns the cgroups mode running on the host +func Mode() CGMode { + checkMode.Do(func() { + var st unix.Statfs_t + if err := unix.Statfs(unifiedMountpoint, &st); err != nil { + cgMode = Unavailable + return + } + switch st.Type { + case unix.CGROUP2_SUPER_MAGIC: + cgMode = Unified + default: + cgMode = Legacy + if err := unix.Statfs(filepath.Join(unifiedMountpoint, "unified"), &st); err != nil { + return + } + if st.Type == unix.CGROUP2_SUPER_MAGIC { + cgMode = Hybrid + } + } + }) + return cgMode +} -// runningInUserNS detects whether we are currently running in a user namespace. +// RunningInUserNS detects whether we are currently running in a user namespace. // Copied from github.com/lxc/lxd/shared/util.go -func runningInUserNS() bool { - file, err := os.Open("/proc/self/uid_map") - if err != nil { - // This kernel-provided file only exists if user namespaces are supported - return false - } - defer file.Close() +func RunningInUserNS() bool { + nsOnce.Do(func() { + file, err := os.Open("/proc/self/uid_map") + if err != nil { + // This kernel-provided file only exists if user namespaces are supported + return + } + defer file.Close() - buf := bufio.NewReader(file) - l, _, err := buf.ReadLine() - if err != nil { - return false - } + buf := bufio.NewReader(file) + l, _, err := buf.ReadLine() + if err != nil { + return + } - line := string(l) - var a, b, c int64 - fmt.Sscanf(line, "%d %d %d", &a, &b, &c) - /* - * We assume we are in the initial user namespace if we have a full - * range - 4294967295 uids starting at uid 0. - */ - if a == 0 && b == 0 && c == 4294967295 { - return false - } - return true + line := string(l) + var a, b, c int64 + fmt.Sscanf(line, "%d %d %d", &a, &b, &c) + + /* + * We assume we are in the initial user namespace if we have a full + * range - 4294967295 uids starting at uid 0. + */ + if a == 0 && b == 0 && c == 4294967295 { + return + } + inUserNS = true + }) + return inUserNS } // defaults returns all known groups @@ -75,7 +128,7 @@ func defaults(root string) ([]Subsystem, error) { NewNetCls(root), NewNetPrio(root), NewPerfEvent(root), - NewCputset(root), + NewCpuset(root), NewCpu(root), NewCpuacct(root), NewMemory(root), @@ -84,7 +137,7 @@ func defaults(root string) ([]Subsystem, error) { } // only add the devices cgroup if we are not in a user namespace // because modifications are not allowed - if !isUserNS { + if !RunningInUserNS() { s = append(s, NewDevices(root)) } // add the hugetlb cgroup if error wasn't due to missing hugetlb @@ -111,7 +164,7 @@ func remove(path string) error { return fmt.Errorf("cgroups: unable to remove path %q", path) } -// readPids will read all the pids in a cgroup by the provided path +// readPids will read all the pids of processes in a cgroup by the provided path func readPids(path string, subsystem Name) ([]Process, error) { f, err := os.Open(filepath.Join(path, cgroupProcs)) if err != nil { @@ -135,13 +188,47 @@ func readPids(path string, subsystem Name) ([]Process, error) { }) } } + if err := s.Err(); err != nil { + // failed to read all pids? + return nil, err + } + return out, nil +} + +// readTasksPids will read all the pids of tasks in a cgroup by the provided path +func readTasksPids(path string, subsystem Name) ([]Task, error) { + f, err := os.Open(filepath.Join(path, cgroupTasks)) + if err != nil { + return nil, err + } + defer f.Close() + var ( + out []Task + s = bufio.NewScanner(f) + ) + for s.Scan() { + if t := s.Text(); t != "" { + pid, err := strconv.Atoi(t) + if err != nil { + return nil, err + } + out = append(out, Task{ + Pid: pid, + Subsystem: subsystem, + Path: path, + }) + } + } + if err := s.Err(); err != nil { + return nil, err + } return out, nil } func hugePageSizes() ([]string, error) { var ( pageSizes []string - sizeList = []string{"B", "kB", "MB", "GB", "TB", "PB"} + sizeList = []string{"B", "KB", "MB", "GB", "TB", "PB"} ) files, err := ioutil.ReadDir("/sys/kernel/mm/hugepages") if err != nil { @@ -213,9 +300,6 @@ func parseCgroupFromReader(r io.Reader) (map[string]string, error) { s = bufio.NewScanner(r) ) for s.Scan() { - if err := s.Err(); err != nil { - return nil, err - } var ( text = s.Text() parts = strings.SplitN(text, ":", 3) @@ -229,6 +313,9 @@ func parseCgroupFromReader(r io.Reader) (map[string]string, error) { } } } + if err := s.Err(); err != nil { + return nil, err + } return cgroups, nil } @@ -240,16 +327,23 @@ func getCgroupDestination(subsystem string) (string, error) { defer f.Close() s := bufio.NewScanner(f) for s.Scan() { - if err := s.Err(); err != nil { - return "", err + fields := strings.Split(s.Text(), " ") + if len(fields) < 10 { + // broken mountinfo? + continue + } + if fields[len(fields)-3] != "cgroup" { + continue } - fields := strings.Fields(s.Text()) for _, opt := range strings.Split(fields[len(fields)-1], ",") { if opt == subsystem { return fields[3], nil } } } + if err := s.Err(); err != nil { + return "", err + } return "", ErrNoCgroupMountDestination } @@ -293,5 +387,18 @@ func cleanPath(path string) string { if !filepath.IsAbs(path) { path, _ = filepath.Rel(string(os.PathSeparator), filepath.Clean(string(os.PathSeparator)+path)) } - return filepath.Clean(path) + return path +} + +func retryingWriteFile(path string, data []byte, mode os.FileMode) error { + // Retry writes on EINTR; see: + // https://github.com/golang/go/issues/38033 + for { + err := ioutil.WriteFile(path, data, mode) + if err == nil { + return nil + } else if !errors.Is(err, syscall.EINTR) { + return err + } + } } diff --git a/vendor/github.com/containerd/cgroups/v1.go b/vendor/github.com/containerd/cgroups/v1.go index a076d4692efd3..2ec215c06f424 100644 --- a/vendor/github.com/containerd/cgroups/v1.go +++ b/vendor/github.com/containerd/cgroups/v1.go @@ -54,28 +54,20 @@ func v1MountPoint() (string, error) { defer f.Close() scanner := bufio.NewScanner(f) for scanner.Scan() { - if err := scanner.Err(); err != nil { - return "", err - } var ( - text = scanner.Text() - fields = strings.Split(text, " ") - // safe as mountinfo encodes mountpoints with spaces as \040. - index = strings.Index(text, " - ") - postSeparatorFields = strings.Fields(text[index+3:]) - numPostFields = len(postSeparatorFields) + text = scanner.Text() + fields = strings.Split(text, " ") + numFields = len(fields) ) - // this is an error as we can't detect if the mount is for "cgroup" - if numPostFields == 0 { - return "", fmt.Errorf("Found no fields post '-' in %q", text) + if numFields < 10 { + return "", fmt.Errorf("mountinfo: bad entry %q", text) } - if postSeparatorFields[0] == "cgroup" { - // check that the mount is properly formated. - if numPostFields < 3 { - return "", fmt.Errorf("Error found less than 3 fields post '-' in %q", text) - } + if fields[numFields-3] == "cgroup" { return filepath.Dir(fields[4]), nil } } + if err := scanner.Err(); err != nil { + return "", err + } return "", ErrMountPointNotExist } diff --git a/vendor/github.com/containerd/cgroups/v2/cpu.go b/vendor/github.com/containerd/cgroups/v2/cpu.go new file mode 100644 index 0000000000000..65282ff082d21 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/cpu.go @@ -0,0 +1,83 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import ( + "math" + "strconv" + "strings" +) + +type CPUMax string + +func NewCPUMax(quota *int64, period *uint64) CPUMax { + max := "max" + if quota != nil { + max = strconv.FormatInt(*quota, 10) + } + return CPUMax(strings.Join([]string{max, strconv.FormatUint(*period, 10)}, " ")) +} + +type CPU struct { + Weight *uint64 + Max CPUMax + Cpus string + Mems string +} + +func (c CPUMax) extractQuotaAndPeriod() (int64, uint64) { + var ( + quota int64 + period uint64 + ) + values := strings.Split(string(c), " ") + if values[0] == "max" { + quota = math.MaxInt64 + } else { + quota, _ = strconv.ParseInt(values[0], 10, 64) + } + period, _ = strconv.ParseUint(values[1], 10, 64) + return quota, period +} + +func (r *CPU) Values() (o []Value) { + if r.Weight != nil { + o = append(o, Value{ + filename: "cpu.weight", + value: *r.Weight, + }) + } + if r.Max != "" { + o = append(o, Value{ + filename: "cpu.max", + value: r.Max, + }) + } + if r.Cpus != "" { + o = append(o, Value{ + filename: "cpuset.cpus", + value: r.Cpus, + }) + } + if r.Mems != "" { + o = append(o, Value{ + filename: "cpuset.mems", + value: r.Mems, + }) + } + return o +} diff --git a/vendor/github.com/containerd/cgroups/v2/devicefilter.go b/vendor/github.com/containerd/cgroups/v2/devicefilter.go new file mode 100644 index 0000000000000..4b8c32be92860 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/devicefilter.go @@ -0,0 +1,199 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +// Devicefilter containes eBPF device filter program +// +// The implementation is based on https://github.com/containers/crun/blob/0.10.2/src/libcrun/ebpf.c +// +// Although ebpf.c is originally licensed under LGPL-3.0-or-later, the author (Giuseppe Scrivano) +// agreed to relicense the file in Apache License 2.0: https://github.com/opencontainers/runc/issues/2144#issuecomment-543116397 +// +// This particular Go implementation based on runc version +// https://github.com/opencontainers/runc/blob/master/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go +package v2 + +import ( + "fmt" + "math" + + "github.com/cilium/ebpf/asm" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +const ( + // license string format is same as kernel MODULE_LICENSE macro + license = "Apache" +) + +// DeviceFilter returns eBPF device filter program and its license string +func DeviceFilter(devices []specs.LinuxDeviceCgroup) (asm.Instructions, string, error) { + p := &program{} + p.init() + for i := len(devices) - 1; i >= 0; i-- { + if err := p.appendDevice(devices[i]); err != nil { + return nil, "", err + } + } + insts, err := p.finalize() + return insts, license, err +} + +type program struct { + insts asm.Instructions + hasWildCard bool + blockID int +} + +func (p *program) init() { + // struct bpf_cgroup_dev_ctx: https://elixir.bootlin.com/linux/v5.3.6/source/include/uapi/linux/bpf.h#L3423 + /* + u32 access_type + u32 major + u32 minor + */ + // R2 <- type (lower 16 bit of u32 access_type at R1[0]) + p.insts = append(p.insts, + asm.LoadMem(asm.R2, asm.R1, 0, asm.Half)) + + // R3 <- access (upper 16 bit of u32 access_type at R1[0]) + p.insts = append(p.insts, + asm.LoadMem(asm.R3, asm.R1, 0, asm.Word), + // RSh: bitwise shift right + asm.RSh.Imm32(asm.R3, 16)) + + // R4 <- major (u32 major at R1[4]) + p.insts = append(p.insts, + asm.LoadMem(asm.R4, asm.R1, 4, asm.Word)) + + // R5 <- minor (u32 minor at R1[8]) + p.insts = append(p.insts, + asm.LoadMem(asm.R5, asm.R1, 8, asm.Word)) +} + +// appendDevice needs to be called from the last element of OCI linux.resources.devices to the head element. +func (p *program) appendDevice(dev specs.LinuxDeviceCgroup) error { + if p.blockID < 0 { + return errors.New("the program is finalized") + } + if p.hasWildCard { + // All entries after wildcard entry are ignored + return nil + } + + bpfType := int32(-1) + hasType := true + switch dev.Type { + case string('c'): + bpfType = int32(unix.BPF_DEVCG_DEV_CHAR) + case string('b'): + bpfType = int32(unix.BPF_DEVCG_DEV_BLOCK) + case string('a'): + hasType = false + default: + // if not specified in OCI json, typ is set to DeviceTypeAll + return errors.Errorf("invalid DeviceType %q", dev.Type) + } + if *dev.Major > math.MaxUint32 { + return errors.Errorf("invalid major %d", *dev.Major) + } + if *dev.Minor > math.MaxUint32 { + return errors.Errorf("invalid minor %d", *dev.Major) + } + hasMajor := *dev.Major >= 0 // if not specified in OCI json, major is set to -1 + hasMinor := *dev.Minor >= 0 + bpfAccess := int32(0) + for _, r := range dev.Access { + switch r { + case 'r': + bpfAccess |= unix.BPF_DEVCG_ACC_READ + case 'w': + bpfAccess |= unix.BPF_DEVCG_ACC_WRITE + case 'm': + bpfAccess |= unix.BPF_DEVCG_ACC_MKNOD + default: + return errors.Errorf("unknown device access %v", r) + } + } + // If the access is rwm, skip the check. + hasAccess := bpfAccess != (unix.BPF_DEVCG_ACC_READ | unix.BPF_DEVCG_ACC_WRITE | unix.BPF_DEVCG_ACC_MKNOD) + + blockSym := fmt.Sprintf("block-%d", p.blockID) + nextBlockSym := fmt.Sprintf("block-%d", p.blockID+1) + prevBlockLastIdx := len(p.insts) - 1 + if hasType { + p.insts = append(p.insts, + // if (R2 != bpfType) goto next + asm.JNE.Imm(asm.R2, bpfType, nextBlockSym), + ) + } + if hasAccess { + p.insts = append(p.insts, + // if (R3 & bpfAccess == 0 /* use R1 as a temp var */) goto next + asm.Mov.Reg32(asm.R1, asm.R3), + asm.And.Imm32(asm.R1, bpfAccess), + asm.JEq.Imm(asm.R1, 0, nextBlockSym), + ) + } + if hasMajor { + p.insts = append(p.insts, + // if (R4 != major) goto next + asm.JNE.Imm(asm.R4, int32(*dev.Major), nextBlockSym), + ) + } + if hasMinor { + p.insts = append(p.insts, + // if (R5 != minor) goto next + asm.JNE.Imm(asm.R5, int32(*dev.Minor), nextBlockSym), + ) + } + if !hasType && !hasAccess && !hasMajor && !hasMinor { + p.hasWildCard = true + } + p.insts = append(p.insts, acceptBlock(dev.Allow)...) + // set blockSym to the first instruction we added in this iteration + p.insts[prevBlockLastIdx+1] = p.insts[prevBlockLastIdx+1].Sym(blockSym) + p.blockID++ + return nil +} + +func (p *program) finalize() (asm.Instructions, error) { + if p.hasWildCard { + // acceptBlock with asm.Return() is already inserted + return p.insts, nil + } + blockSym := fmt.Sprintf("block-%d", p.blockID) + p.insts = append(p.insts, + // R0 <- 0 + asm.Mov.Imm32(asm.R0, 0).Sym(blockSym), + asm.Return(), + ) + p.blockID = -1 + return p.insts, nil +} + +func acceptBlock(accept bool) asm.Instructions { + v := int32(0) + if accept { + v = 1 + } + return []asm.Instruction{ + // R0 <- v + asm.Mov.Imm32(asm.R0, v), + asm.Return(), + } +} diff --git a/vendor/github.com/containerd/cgroups/v2/ebpf.go b/vendor/github.com/containerd/cgroups/v2/ebpf.go new file mode 100644 index 0000000000000..bd384818c527a --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/ebpf.go @@ -0,0 +1,95 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import ( + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/link" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// LoadAttachCgroupDeviceFilter installs eBPF device filter program to /sys/fs/cgroup/ directory. +// +// Requires the system to be running in cgroup2 unified-mode with kernel >= 4.15 . +// +// https://github.com/torvalds/linux/commit/ebc614f687369f9df99828572b1d85a7c2de3d92 +func LoadAttachCgroupDeviceFilter(insts asm.Instructions, license string, dirFD int) (func() error, error) { + nilCloser := func() error { + return nil + } + spec := &ebpf.ProgramSpec{ + Type: ebpf.CGroupDevice, + Instructions: insts, + License: license, + } + prog, err := ebpf.NewProgram(spec) + if err != nil { + return nilCloser, err + } + err = link.RawAttachProgram(link.RawAttachProgramOptions{ + Target: dirFD, + Program: prog, + Attach: ebpf.AttachCGroupDevice, + Flags: unix.BPF_F_ALLOW_MULTI, + }) + if err != nil { + return nilCloser, errors.Wrap(err, "failed to call BPF_PROG_ATTACH (BPF_CGROUP_DEVICE, BPF_F_ALLOW_MULTI)") + } + closer := func() error { + err = link.RawDetachProgram(link.RawDetachProgramOptions{ + Target: dirFD, + Program: prog, + Attach: ebpf.AttachCGroupDevice, + }) + if err != nil { + return errors.Wrap(err, "failed to call BPF_PROG_DETACH (BPF_CGROUP_DEVICE)") + } + return nil + } + return closer, nil +} + +func isRWM(cgroupPermissions string) bool { + r := false + w := false + m := false + for _, rn := range cgroupPermissions { + switch rn { + case 'r': + r = true + case 'w': + w = true + case 'm': + m = true + } + } + return r && w && m +} + +// the logic is from runc +// https://github.com/opencontainers/runc/blob/master/libcontainer/cgroups/fs/devices_v2.go#L44 +func canSkipEBPFError(devices []specs.LinuxDeviceCgroup) bool { + for _, dev := range devices { + if dev.Allow || !isRWM(dev.Access) { + return false + } + } + return true +} diff --git a/vendor/github.com/containerd/cgroups/v2/errors.go b/vendor/github.com/containerd/cgroups/v2/errors.go new file mode 100644 index 0000000000000..dfab548e35ef8 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/errors.go @@ -0,0 +1,46 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import ( + "errors" + "os" +) + +var ( + ErrInvalidPid = errors.New("cgroups: pid must be greater than 0") + ErrMountPointNotExist = errors.New("cgroups: cgroup mountpoint does not exist") + ErrInvalidFormat = errors.New("cgroups: parsing file with invalid format failed") + ErrFreezerNotSupported = errors.New("cgroups: freezer cgroup (v2) not supported on this system") + ErrMemoryNotSupported = errors.New("cgroups: memory cgroup (v2) not supported on this system") + ErrPidsNotSupported = errors.New("cgroups: pids cgroup (v2) not supported on this system") + ErrCPUNotSupported = errors.New("cgroups: cpu cgroup (v2) not supported on this system") + ErrCgroupDeleted = errors.New("cgroups: cgroup deleted") + ErrNoCgroupMountDestination = errors.New("cgroups: cannot find cgroup mount destination") + ErrInvalidGroupPath = errors.New("cgroups: invalid group path") +) + +// ErrorHandler is a function that handles and acts on errors +type ErrorHandler func(err error) error + +// IgnoreNotExist ignores any errors that are for not existing files +func IgnoreNotExist(err error) error { + if os.IsNotExist(err) { + return nil + } + return err +} diff --git a/vendor/github.com/containerd/cgroups/v2/hugetlb.go b/vendor/github.com/containerd/cgroups/v2/hugetlb.go new file mode 100644 index 0000000000000..16b35bd780be7 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/hugetlb.go @@ -0,0 +1,37 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import "strings" + +type HugeTlb []HugeTlbEntry + +type HugeTlbEntry struct { + HugePageSize string + Limit uint64 +} + +func (r *HugeTlb) Values() (o []Value) { + for _, e := range *r { + o = append(o, Value{ + filename: strings.Join([]string{"hugetlb", e.HugePageSize, "max"}, "."), + value: e.Limit, + }) + } + + return o +} diff --git a/vendor/github.com/containerd/cgroups/v2/io.go b/vendor/github.com/containerd/cgroups/v2/io.go new file mode 100644 index 0000000000000..70078d576ec38 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/io.go @@ -0,0 +1,64 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import "fmt" + +type IOType string + +const ( + ReadBPS IOType = "rbps" + WriteBPS IOType = "wbps" + ReadIOPS IOType = "riops" + WriteIOPS IOType = "wiops" +) + +type BFQ struct { + Weight uint16 +} + +type Entry struct { + Type IOType + Major int64 + Minor int64 + Rate uint64 +} + +func (e Entry) String() string { + return fmt.Sprintf("%d:%d %s=%d", e.Major, e.Minor, e.Type, e.Rate) +} + +type IO struct { + BFQ BFQ + Max []Entry +} + +func (i *IO) Values() (o []Value) { + if i.BFQ.Weight != 0 { + o = append(o, Value{ + filename: "io.bfq.weight", + value: i.BFQ.Weight, + }) + } + for _, e := range i.Max { + o = append(o, Value{ + filename: "io.max", + value: e.String(), + }) + } + return o +} diff --git a/vendor/github.com/containerd/cgroups/v2/manager.go b/vendor/github.com/containerd/cgroups/v2/manager.go new file mode 100644 index 0000000000000..3bb546cb66e18 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/manager.go @@ -0,0 +1,782 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import ( + "bufio" + "io/ioutil" + "math" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + "time" + + "github.com/containerd/cgroups/v2/stats" + systemdDbus "github.com/coreos/go-systemd/v22/dbus" + "github.com/godbus/dbus/v5" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +) + +const ( + subtreeControl = "cgroup.subtree_control" + controllersFile = "cgroup.controllers" + defaultCgroup2Path = "/sys/fs/cgroup" + defaultSlice = "system.slice" +) + +var ( + canDelegate bool +) + +type Event struct { + Low uint64 + High uint64 + Max uint64 + OOM uint64 + OOMKill uint64 +} + +// Resources for a cgroups v2 unified hierarchy +type Resources struct { + CPU *CPU + Memory *Memory + Pids *Pids + IO *IO + RDMA *RDMA + HugeTlb *HugeTlb + // When len(Devices) is zero, devices are not controlled + Devices []specs.LinuxDeviceCgroup +} + +// Values returns the raw filenames and values that +// can be written to the unified hierarchy +func (r *Resources) Values() (o []Value) { + if r.CPU != nil { + o = append(o, r.CPU.Values()...) + } + if r.Memory != nil { + o = append(o, r.Memory.Values()...) + } + if r.Pids != nil { + o = append(o, r.Pids.Values()...) + } + if r.IO != nil { + o = append(o, r.IO.Values()...) + } + if r.RDMA != nil { + o = append(o, r.RDMA.Values()...) + } + if r.HugeTlb != nil { + o = append(o, r.HugeTlb.Values()...) + } + return o +} + +// EnabledControllers returns the list of all not nil resource controllers +func (r *Resources) EnabledControllers() (c []string) { + if r.CPU != nil { + c = append(c, "cpu") + c = append(c, "cpuset") + } + if r.Memory != nil { + c = append(c, "memory") + } + if r.Pids != nil { + c = append(c, "pids") + } + if r.IO != nil { + c = append(c, "io") + } + if r.RDMA != nil { + c = append(c, "rdma") + } + if r.HugeTlb != nil { + c = append(c, "hugetlb") + } + return +} + +// Value of a cgroup setting +type Value struct { + filename string + value interface{} +} + +// write the value to the full, absolute path, of a unified hierarchy +func (c *Value) write(path string, perm os.FileMode) error { + var data []byte + switch t := c.value.(type) { + case uint64: + data = []byte(strconv.FormatUint(t, 10)) + case uint16: + data = []byte(strconv.FormatUint(uint64(t), 10)) + case int64: + data = []byte(strconv.FormatInt(t, 10)) + case []byte: + data = t + case string: + data = []byte(t) + case CPUMax: + data = []byte(t) + default: + return ErrInvalidFormat + } + + // Retry writes on EINTR; see: + // https://github.com/golang/go/issues/38033 + for { + err := ioutil.WriteFile( + filepath.Join(path, c.filename), + data, + perm, + ) + if err == nil { + return nil + } else if !errors.Is(err, syscall.EINTR) { + return err + } + } +} + +func writeValues(path string, values []Value) error { + for _, o := range values { + if err := o.write(path, defaultFilePerm); err != nil { + return err + } + } + return nil +} + +func NewManager(mountpoint string, group string, resources *Resources) (*Manager, error) { + if resources == nil { + return nil, errors.New("resources reference is nil") + } + if err := VerifyGroupPath(group); err != nil { + return nil, err + } + path := filepath.Join(mountpoint, group) + if err := os.MkdirAll(path, defaultDirPerm); err != nil { + return nil, err + } + m := Manager{ + unifiedMountpoint: mountpoint, + path: path, + } + if err := m.ToggleControllers(resources.EnabledControllers(), Enable); err != nil { + // clean up cgroup dir on failure + os.Remove(path) + return nil, err + } + if err := setResources(path, resources); err != nil { + os.Remove(path) + return nil, err + } + return &m, nil +} + +func LoadManager(mountpoint string, group string) (*Manager, error) { + if err := VerifyGroupPath(group); err != nil { + return nil, err + } + path := filepath.Join(mountpoint, group) + return &Manager{ + unifiedMountpoint: mountpoint, + path: path, + }, nil +} + +type Manager struct { + unifiedMountpoint string + path string +} + +func setResources(path string, resources *Resources) error { + if resources != nil { + if err := writeValues(path, resources.Values()); err != nil { + return err + } + if err := setDevices(path, resources.Devices); err != nil { + return err + } + } + return nil +} + +func (c *Manager) RootControllers() ([]string, error) { + b, err := ioutil.ReadFile(filepath.Join(c.unifiedMountpoint, controllersFile)) + if err != nil { + return nil, err + } + return strings.Fields(string(b)), nil +} + +func (c *Manager) Controllers() ([]string, error) { + b, err := ioutil.ReadFile(filepath.Join(c.path, controllersFile)) + if err != nil { + return nil, err + } + return strings.Fields(string(b)), nil +} + +type ControllerToggle int + +const ( + Enable ControllerToggle = iota + 1 + Disable +) + +func toggleFunc(controllers []string, prefix string) []string { + out := make([]string, len(controllers)) + for i, c := range controllers { + out[i] = prefix + c + } + return out +} + +func (c *Manager) ToggleControllers(controllers []string, t ControllerToggle) error { + // when c.path is like /foo/bar/baz, the following files need to be written: + // * /sys/fs/cgroup/cgroup.subtree_control + // * /sys/fs/cgroup/foo/cgroup.subtree_control + // * /sys/fs/cgroup/foo/bar/cgroup.subtree_control + // Note that /sys/fs/cgroup/foo/bar/baz/cgroup.subtree_control does not need to be written. + split := strings.Split(c.path, "/") + var lastErr error + for i := range split { + f := strings.Join(split[:i], "/") + if !strings.HasPrefix(f, c.unifiedMountpoint) || f == c.path { + continue + } + filePath := filepath.Join(f, subtreeControl) + if err := c.writeSubtreeControl(filePath, controllers, t); err != nil { + // When running as rootless, the user may face EPERM on parent groups, but it is neglible when the + // controller is already written. + // So we only return the last error. + lastErr = errors.Wrapf(err, "failed to write subtree controllers %+v to %q", controllers, filePath) + } + } + return lastErr +} + +func (c *Manager) writeSubtreeControl(filePath string, controllers []string, t ControllerToggle) error { + f, err := os.OpenFile(filePath, os.O_WRONLY, 0) + if err != nil { + return err + } + defer f.Close() + switch t { + case Enable: + controllers = toggleFunc(controllers, "+") + case Disable: + controllers = toggleFunc(controllers, "-") + } + _, err = f.WriteString(strings.Join(controllers, " ")) + return err +} + +func (c *Manager) NewChild(name string, resources *Resources) (*Manager, error) { + if strings.HasPrefix(name, "/") { + return nil, errors.New("name must be relative") + } + path := filepath.Join(c.path, name) + if err := os.MkdirAll(path, defaultDirPerm); err != nil { + return nil, err + } + if err := setResources(path, resources); err != nil { + // clean up cgroup dir on failure + os.Remove(path) + return nil, err + } + return &Manager{ + unifiedMountpoint: c.unifiedMountpoint, + path: path, + }, nil +} + +func (c *Manager) AddProc(pid uint64) error { + v := Value{ + filename: cgroupProcs, + value: pid, + } + return writeValues(c.path, []Value{v}) +} + +func (c *Manager) Delete() error { + return remove(c.path) +} + +func (c *Manager) Procs(recursive bool) ([]uint64, error) { + var processes []uint64 + err := filepath.Walk(c.path, func(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !recursive && info.IsDir() { + if p == c.path { + return nil + } + return filepath.SkipDir + } + _, name := filepath.Split(p) + if name != cgroupProcs { + return nil + } + procs, err := parseCgroupProcsFile(p) + if err != nil { + return err + } + processes = append(processes, procs...) + return nil + }) + return processes, err +} + +var singleValueFiles = []string{ + "pids.current", + "pids.max", +} + +func (c *Manager) Stat() (*stats.Metrics, error) { + controllers, err := c.Controllers() + if err != nil { + return nil, err + } + out := make(map[string]interface{}) + for _, controller := range controllers { + switch controller { + case "cpu", "memory": + if err := readKVStatsFile(c.path, controller+".stat", out); err != nil { + if os.IsNotExist(err) { + continue + } + return nil, err + } + } + } + for _, name := range singleValueFiles { + if err := readSingleFile(c.path, name, out); err != nil { + if os.IsNotExist(err) { + continue + } + return nil, err + } + } + memoryEvents := make(map[string]interface{}) + if err := readKVStatsFile(c.path, "memory.events", memoryEvents); err != nil { + if !os.IsNotExist(err) { + return nil, err + } + } + var metrics stats.Metrics + + metrics.Pids = &stats.PidsStat{ + Current: getPidValue("pids.current", out), + Limit: getPidValue("pids.max", out), + } + metrics.CPU = &stats.CPUStat{ + UsageUsec: getUint64Value("usage_usec", out), + UserUsec: getUint64Value("user_usec", out), + SystemUsec: getUint64Value("system_usec", out), + NrPeriods: getUint64Value("nr_periods", out), + NrThrottled: getUint64Value("nr_throttled", out), + ThrottledUsec: getUint64Value("throttled_usec", out), + } + metrics.Memory = &stats.MemoryStat{ + Anon: getUint64Value("anon", out), + File: getUint64Value("file", out), + KernelStack: getUint64Value("kernel_stack", out), + Slab: getUint64Value("slab", out), + Sock: getUint64Value("sock", out), + Shmem: getUint64Value("shmem", out), + FileMapped: getUint64Value("file_mapped", out), + FileDirty: getUint64Value("file_dirty", out), + FileWriteback: getUint64Value("file_writeback", out), + AnonThp: getUint64Value("anon_thp", out), + InactiveAnon: getUint64Value("inactive_anon", out), + ActiveAnon: getUint64Value("active_anon", out), + InactiveFile: getUint64Value("inactive_file", out), + ActiveFile: getUint64Value("active_file", out), + Unevictable: getUint64Value("unevictable", out), + SlabReclaimable: getUint64Value("slab_reclaimable", out), + SlabUnreclaimable: getUint64Value("slab_unreclaimable", out), + Pgfault: getUint64Value("pgfault", out), + Pgmajfault: getUint64Value("pgmajfault", out), + WorkingsetRefault: getUint64Value("workingset_refault", out), + WorkingsetActivate: getUint64Value("workingset_activate", out), + WorkingsetNodereclaim: getUint64Value("workingset_nodereclaim", out), + Pgrefill: getUint64Value("pgrefill", out), + Pgscan: getUint64Value("pgscan", out), + Pgsteal: getUint64Value("pgsteal", out), + Pgactivate: getUint64Value("pgactivate", out), + Pgdeactivate: getUint64Value("pgdeactivate", out), + Pglazyfree: getUint64Value("pglazyfree", out), + Pglazyfreed: getUint64Value("pglazyfreed", out), + ThpFaultAlloc: getUint64Value("thp_fault_alloc", out), + ThpCollapseAlloc: getUint64Value("thp_collapse_alloc", out), + Usage: getStatFileContentUint64(filepath.Join(c.path, "memory.current")), + UsageLimit: getStatFileContentUint64(filepath.Join(c.path, "memory.max")), + SwapUsage: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.current")), + SwapLimit: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.max")), + } + if len(memoryEvents) > 0 { + metrics.MemoryEvents = &stats.MemoryEvents{ + Low: getUint64Value("low", memoryEvents), + High: getUint64Value("high", memoryEvents), + Max: getUint64Value("max", memoryEvents), + Oom: getUint64Value("oom", memoryEvents), + OomKill: getUint64Value("oom_kill", memoryEvents), + } + } + metrics.Io = &stats.IOStat{Usage: readIoStats(c.path)} + metrics.Rdma = &stats.RdmaStat{ + Current: rdmaStats(filepath.Join(c.path, "rdma.current")), + Limit: rdmaStats(filepath.Join(c.path, "rdma.max")), + } + metrics.Hugetlb = readHugeTlbStats(c.path) + + return &metrics, nil +} + +func getUint64Value(key string, out map[string]interface{}) uint64 { + v, ok := out[key] + if !ok { + return 0 + } + switch t := v.(type) { + case uint64: + return t + } + return 0 +} + +func getPidValue(key string, out map[string]interface{}) uint64 { + v, ok := out[key] + if !ok { + return 0 + } + switch t := v.(type) { + case uint64: + return t + case string: + if t == "max" { + return math.MaxUint64 + } + } + return 0 +} + +func readSingleFile(path string, file string, out map[string]interface{}) error { + f, err := os.Open(filepath.Join(path, file)) + if err != nil { + return err + } + defer f.Close() + data, err := ioutil.ReadAll(f) + if err != nil { + return err + } + s := strings.TrimSpace(string(data)) + v, err := parseUint(s, 10, 64) + if err != nil { + // if we cannot parse as a uint, parse as a string + out[file] = s + return nil + } + out[file] = v + return nil +} + +func readKVStatsFile(path string, file string, out map[string]interface{}) error { + f, err := os.Open(filepath.Join(path, file)) + if err != nil { + return err + } + defer f.Close() + + s := bufio.NewScanner(f) + for s.Scan() { + name, value, err := parseKV(s.Text()) + if err != nil { + return errors.Wrapf(err, "error while parsing %s (line=%q)", filepath.Join(path, file), s.Text()) + } + out[name] = value + } + return s.Err() +} + +func (c *Manager) Freeze() error { + return c.freeze(c.path, Frozen) +} + +func (c *Manager) Thaw() error { + return c.freeze(c.path, Thawed) +} + +func (c *Manager) freeze(path string, state State) error { + values := state.Values() + for { + if err := writeValues(path, values); err != nil { + return err + } + current, err := fetchState(path) + if err != nil { + return err + } + if current == state { + return nil + } + time.Sleep(1 * time.Millisecond) + } +} + +// MemoryEventFD returns inotify file descriptor and 'memory.events' inotify watch descriptor +func (c *Manager) MemoryEventFD() (int, uint32, error) { + fpath := filepath.Join(c.path, "memory.events") + fd, err := syscall.InotifyInit() + if err != nil { + return 0, 0, errors.Errorf("Failed to create inotify fd") + } + wd, err := syscall.InotifyAddWatch(fd, fpath, unix.IN_MODIFY) + if wd < 0 { + syscall.Close(fd) + return 0, 0, errors.Errorf("Failed to add inotify watch for %q", fpath) + } + + return fd, uint32(wd), nil +} + +func (c *Manager) EventChan() (<-chan Event, <-chan error) { + ec := make(chan Event) + errCh := make(chan error) + go c.waitForEvents(ec, errCh) + + return ec, nil +} + +func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) { + fd, wd, err := c.MemoryEventFD() + + defer syscall.InotifyRmWatch(fd, wd) + defer syscall.Close(fd) + + if err != nil { + errCh <- err + return + } + + for { + buffer := make([]byte, syscall.SizeofInotifyEvent*10) + bytesRead, err := syscall.Read(fd, buffer) + if err != nil { + errCh <- err + return + } + if bytesRead >= syscall.SizeofInotifyEvent { + out := make(map[string]interface{}) + if err := readKVStatsFile(c.path, "memory.events", out); err == nil { + e := Event{} + if v, ok := out["high"]; ok { + e.High, ok = v.(uint64) + if !ok { + errCh <- errors.Errorf("cannot convert high to uint64: %+v", v) + return + } + } + if v, ok := out["low"]; ok { + e.Low, ok = v.(uint64) + if !ok { + errCh <- errors.Errorf("cannot convert low to uint64: %+v", v) + return + } + } + if v, ok := out["max"]; ok { + e.Max, ok = v.(uint64) + if !ok { + errCh <- errors.Errorf("cannot convert max to uint64: %+v", v) + return + } + } + if v, ok := out["oom"]; ok { + e.OOM, ok = v.(uint64) + if !ok { + errCh <- errors.Errorf("cannot convert oom to uint64: %+v", v) + return + } + } + if v, ok := out["oom_kill"]; ok { + e.OOMKill, ok = v.(uint64) + if !ok { + errCh <- errors.Errorf("cannot convert oom_kill to uint64: %+v", v) + return + } + } + ec <- e + } else { + errCh <- err + return + } + } + } +} + +func setDevices(path string, devices []specs.LinuxDeviceCgroup) error { + if len(devices) == 0 { + return nil + } + insts, license, err := DeviceFilter(devices) + if err != nil { + return err + } + dirFD, err := unix.Open(path, unix.O_DIRECTORY|unix.O_RDONLY, 0600) + if err != nil { + return errors.Errorf("cannot get dir FD for %s", path) + } + defer unix.Close(dirFD) + if _, err := LoadAttachCgroupDeviceFilter(insts, license, dirFD); err != nil { + if !canSkipEBPFError(devices) { + return err + } + } + return nil +} + +func NewSystemd(slice, group string, pid int, resources *Resources) (*Manager, error) { + if slice == "" { + slice = defaultSlice + } + path := filepath.Join(defaultCgroup2Path, slice, group) + conn, err := systemdDbus.New() + if err != nil { + return &Manager{}, err + } + defer conn.Close() + + properties := []systemdDbus.Property{ + systemdDbus.PropDescription("cgroup " + group), + newSystemdProperty("DefaultDependencies", false), + newSystemdProperty("MemoryAccounting", true), + newSystemdProperty("CPUAccounting", true), + newSystemdProperty("IOAccounting", true), + } + + // if we create a slice, the parent is defined via a Wants= + if strings.HasSuffix(group, ".slice") { + properties = append(properties, systemdDbus.PropWants(defaultSlice)) + } else { + // otherwise, we use Slice= + properties = append(properties, systemdDbus.PropSlice(defaultSlice)) + } + + // only add pid if its valid, -1 is used w/ general slice creation. + if pid != -1 { + properties = append(properties, newSystemdProperty("PIDs", []uint32{uint32(pid)})) + } + + if resources.Memory != nil && *resources.Memory.Max != 0 { + properties = append(properties, + newSystemdProperty("MemoryMax", uint64(*resources.Memory.Max))) + } + + if resources.CPU != nil && *resources.CPU.Weight != 0 { + properties = append(properties, + newSystemdProperty("CPUWeight", *resources.CPU.Weight)) + } + + if resources.CPU != nil && resources.CPU.Max != "" { + quota, period := resources.CPU.Max.extractQuotaAndPeriod() + // cpu.cfs_quota_us and cpu.cfs_period_us are controlled by systemd. + // corresponds to USEC_INFINITY in systemd + // if USEC_INFINITY is provided, CPUQuota is left unbound by systemd + // always setting a property value ensures we can apply a quota and remove it later + cpuQuotaPerSecUSec := uint64(math.MaxUint64) + if quota > 0 { + // systemd converts CPUQuotaPerSecUSec (microseconds per CPU second) to CPUQuota + // (integer percentage of CPU) internally. This means that if a fractional percent of + // CPU is indicated by Resources.CpuQuota, we need to round up to the nearest + // 10ms (1% of a second) such that child cgroups can set the cpu.cfs_quota_us they expect. + cpuQuotaPerSecUSec = uint64(quota*1000000) / period + if cpuQuotaPerSecUSec%10000 != 0 { + cpuQuotaPerSecUSec = ((cpuQuotaPerSecUSec / 10000) + 1) * 10000 + } + } + properties = append(properties, + newSystemdProperty("CPUQuotaPerSecUSec", cpuQuotaPerSecUSec)) + } + + // If we can delegate, we add the property back in + if canDelegate { + properties = append(properties, newSystemdProperty("Delegate", true)) + } + + if resources.Pids != nil && resources.Pids.Max > 0 { + properties = append(properties, + newSystemdProperty("TasksAccounting", true), + newSystemdProperty("TasksMax", uint64(resources.Pids.Max))) + } + + statusChan := make(chan string, 1) + if _, err := conn.StartTransientUnit(group, "replace", properties, statusChan); err == nil { + select { + case <-statusChan: + case <-time.After(time.Second): + logrus.Warnf("Timed out while waiting for StartTransientUnit(%s) completion signal from dbus. Continuing...", group) + } + } else if !isUnitExists(err) { + return &Manager{}, err + } + + return &Manager{ + path: path, + }, nil +} + +func LoadSystemd(slice, group string) (*Manager, error) { + if slice == "" { + slice = defaultSlice + } + group = filepath.Join(defaultCgroup2Path, slice, group) + return &Manager{ + path: group, + }, nil +} + +func (c *Manager) DeleteSystemd() error { + conn, err := systemdDbus.New() + if err != nil { + return err + } + defer conn.Close() + group := systemdUnitFromPath(c.path) + ch := make(chan string) + _, err = conn.StopUnit(group, "replace", ch) + if err != nil { + return err + } + <-ch + return nil +} + +func newSystemdProperty(name string, units interface{}) systemdDbus.Property { + return systemdDbus.Property{ + Name: name, + Value: dbus.MakeVariant(units), + } +} diff --git a/vendor/github.com/containerd/cgroups/v2/memory.go b/vendor/github.com/containerd/cgroups/v2/memory.go new file mode 100644 index 0000000000000..72f94b738b8f3 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/memory.go @@ -0,0 +1,52 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +type Memory struct { + Swap *int64 + Max *int64 + Low *int64 + High *int64 +} + +func (r *Memory) Values() (o []Value) { + if r.Swap != nil { + o = append(o, Value{ + filename: "memory.swap.max", + value: *r.Swap, + }) + } + if r.Max != nil { + o = append(o, Value{ + filename: "memory.max", + value: *r.Max, + }) + } + if r.Low != nil { + o = append(o, Value{ + filename: "memory.low", + value: *r.Low, + }) + } + if r.High != nil { + o = append(o, Value{ + filename: "memory.high", + value: *r.High, + }) + } + return o +} diff --git a/vendor/github.com/containerd/cgroups/v2/paths.go b/vendor/github.com/containerd/cgroups/v2/paths.go new file mode 100644 index 0000000000000..c4778c14244be --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/paths.go @@ -0,0 +1,60 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import ( + "fmt" + "path/filepath" + "strings" +) + +// NestedGroupPath will nest the cgroups based on the calling processes cgroup +// placing its child processes inside its own path +func NestedGroupPath(suffix string) (string, error) { + path, err := parseCgroupFile("/proc/self/cgroup") + if err != nil { + return "", err + } + return filepath.Join(path, suffix), nil +} + +// PidGroupPath will return the correct cgroup paths for an existing process running inside a cgroup +// This is commonly used for the Load function to restore an existing container +func PidGroupPath(pid int) (string, error) { + p := fmt.Sprintf("/proc/%d/cgroup", pid) + return parseCgroupFile(p) +} + +// VerifyGroupPath verifies the format of group path string g. +// The format is same as the third field in /proc/PID/cgroup. +// e.g. "/user.slice/user-1001.slice/session-1.scope" +// +// g must be a "clean" absolute path starts with "/", and must not contain "/sys/fs/cgroup" prefix. +// +// VerifyGroupPath doesn't verify whether g actually exists on the system. +func VerifyGroupPath(g string) error { + if !strings.HasPrefix(g, "/") { + return ErrInvalidGroupPath + } + if filepath.Clean(g) != g { + return ErrInvalidGroupPath + } + if strings.HasPrefix(g, "/sys/fs/cgroup") { + return ErrInvalidGroupPath + } + return nil +} diff --git a/vendor/github.com/containerd/cgroups/v2/pids.go b/vendor/github.com/containerd/cgroups/v2/pids.go new file mode 100644 index 0000000000000..0b5aa0c3bf782 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/pids.go @@ -0,0 +1,37 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import "strconv" + +type Pids struct { + Max int64 +} + +func (r *Pids) Values() (o []Value) { + if r.Max != 0 { + limit := "max" + if r.Max > 0 { + limit = strconv.FormatInt(r.Max, 10) + } + o = append(o, Value{ + filename: "pids.max", + value: limit, + }) + } + return o +} diff --git a/vendor/github.com/containerd/cgroups/v2/rdma.go b/vendor/github.com/containerd/cgroups/v2/rdma.go new file mode 100644 index 0000000000000..44caa4f57a3ca --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/rdma.go @@ -0,0 +1,46 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import ( + "fmt" +) + +type RDMA struct { + Limit []RDMAEntry +} + +type RDMAEntry struct { + Device string + HcaHandles uint32 + HcaObjects uint32 +} + +func (r RDMAEntry) String() string { + return fmt.Sprintf("%s hca_handle=%d hca_object=%d", r.Device, r.HcaHandles, r.HcaObjects) +} + +func (r *RDMA) Values() (o []Value) { + for _, e := range r.Limit { + o = append(o, Value{ + filename: "rdma.max", + value: e.String(), + }) + } + + return o +} diff --git a/vendor/github.com/containerd/cgroups/v2/state.go b/vendor/github.com/containerd/cgroups/v2/state.go new file mode 100644 index 0000000000000..09b75b6c3dd32 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/state.go @@ -0,0 +1,65 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import ( + "io/ioutil" + "path/filepath" + "strings" +) + +// State is a type that represents the state of the current cgroup +type State string + +const ( + Unknown State = "" + Thawed State = "thawed" + Frozen State = "frozen" + Deleted State = "deleted" + + cgroupFreeze = "cgroup.freeze" +) + +func (s State) Values() []Value { + v := Value{ + filename: cgroupFreeze, + } + switch s { + case Frozen: + v.value = "1" + case Thawed: + v.value = "0" + } + return []Value{ + v, + } +} + +func fetchState(path string) (State, error) { + current, err := ioutil.ReadFile(filepath.Join(path, cgroupFreeze)) + if err != nil { + return Unknown, err + } + switch strings.TrimSpace(string(current)) { + case "1": + return Frozen, nil + case "0": + return Thawed, nil + default: + return Unknown, nil + } +} diff --git a/vendor/github.com/containerd/cgroups/v2/stats/doc.go b/vendor/github.com/containerd/cgroups/v2/stats/doc.go new file mode 100644 index 0000000000000..e51e12f800400 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/stats/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package stats diff --git a/vendor/github.com/containerd/cgroups/v2/stats/metrics.pb.go b/vendor/github.com/containerd/cgroups/v2/stats/metrics.pb.go new file mode 100644 index 0000000000000..0bd493998f71f --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/stats/metrics.pb.go @@ -0,0 +1,3992 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/cgroups/v2/stats/metrics.proto + +package stats + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Metrics struct { + Pids *PidsStat `protobuf:"bytes,1,opt,name=pids,proto3" json:"pids,omitempty"` + CPU *CPUStat `protobuf:"bytes,2,opt,name=cpu,proto3" json:"cpu,omitempty"` + Memory *MemoryStat `protobuf:"bytes,4,opt,name=memory,proto3" json:"memory,omitempty"` + Rdma *RdmaStat `protobuf:"bytes,5,opt,name=rdma,proto3" json:"rdma,omitempty"` + Io *IOStat `protobuf:"bytes,6,opt,name=io,proto3" json:"io,omitempty"` + Hugetlb []*HugeTlbStat `protobuf:"bytes,7,rep,name=hugetlb,proto3" json:"hugetlb,omitempty"` + MemoryEvents *MemoryEvents `protobuf:"bytes,8,opt,name=memory_events,json=memoryEvents,proto3" json:"memory_events,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Metrics) Reset() { *m = Metrics{} } +func (*Metrics) ProtoMessage() {} +func (*Metrics) Descriptor() ([]byte, []int) { + return fileDescriptor_2fc6005842049e6b, []int{0} +} +func (m *Metrics) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Metrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Metrics.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Metrics) XXX_Merge(src proto.Message) { + xxx_messageInfo_Metrics.Merge(m, src) +} +func (m *Metrics) XXX_Size() int { + return m.Size() +} +func (m *Metrics) XXX_DiscardUnknown() { + xxx_messageInfo_Metrics.DiscardUnknown(m) +} + +var xxx_messageInfo_Metrics proto.InternalMessageInfo + +type PidsStat struct { + Current uint64 `protobuf:"varint,1,opt,name=current,proto3" json:"current,omitempty"` + Limit uint64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PidsStat) Reset() { *m = PidsStat{} } +func (*PidsStat) ProtoMessage() {} +func (*PidsStat) Descriptor() ([]byte, []int) { + return fileDescriptor_2fc6005842049e6b, []int{1} +} +func (m *PidsStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PidsStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PidsStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PidsStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_PidsStat.Merge(m, src) +} +func (m *PidsStat) XXX_Size() int { + return m.Size() +} +func (m *PidsStat) XXX_DiscardUnknown() { + xxx_messageInfo_PidsStat.DiscardUnknown(m) +} + +var xxx_messageInfo_PidsStat proto.InternalMessageInfo + +type CPUStat struct { + UsageUsec uint64 `protobuf:"varint,1,opt,name=usage_usec,json=usageUsec,proto3" json:"usage_usec,omitempty"` + UserUsec uint64 `protobuf:"varint,2,opt,name=user_usec,json=userUsec,proto3" json:"user_usec,omitempty"` + SystemUsec uint64 `protobuf:"varint,3,opt,name=system_usec,json=systemUsec,proto3" json:"system_usec,omitempty"` + NrPeriods uint64 `protobuf:"varint,4,opt,name=nr_periods,json=nrPeriods,proto3" json:"nr_periods,omitempty"` + NrThrottled uint64 `protobuf:"varint,5,opt,name=nr_throttled,json=nrThrottled,proto3" json:"nr_throttled,omitempty"` + ThrottledUsec uint64 `protobuf:"varint,6,opt,name=throttled_usec,json=throttledUsec,proto3" json:"throttled_usec,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CPUStat) Reset() { *m = CPUStat{} } +func (*CPUStat) ProtoMessage() {} +func (*CPUStat) Descriptor() ([]byte, []int) { + return fileDescriptor_2fc6005842049e6b, []int{2} +} +func (m *CPUStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CPUStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CPUStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CPUStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_CPUStat.Merge(m, src) +} +func (m *CPUStat) XXX_Size() int { + return m.Size() +} +func (m *CPUStat) XXX_DiscardUnknown() { + xxx_messageInfo_CPUStat.DiscardUnknown(m) +} + +var xxx_messageInfo_CPUStat proto.InternalMessageInfo + +type MemoryStat struct { + Anon uint64 `protobuf:"varint,1,opt,name=anon,proto3" json:"anon,omitempty"` + File uint64 `protobuf:"varint,2,opt,name=file,proto3" json:"file,omitempty"` + KernelStack uint64 `protobuf:"varint,3,opt,name=kernel_stack,json=kernelStack,proto3" json:"kernel_stack,omitempty"` + Slab uint64 `protobuf:"varint,4,opt,name=slab,proto3" json:"slab,omitempty"` + Sock uint64 `protobuf:"varint,5,opt,name=sock,proto3" json:"sock,omitempty"` + Shmem uint64 `protobuf:"varint,6,opt,name=shmem,proto3" json:"shmem,omitempty"` + FileMapped uint64 `protobuf:"varint,7,opt,name=file_mapped,json=fileMapped,proto3" json:"file_mapped,omitempty"` + FileDirty uint64 `protobuf:"varint,8,opt,name=file_dirty,json=fileDirty,proto3" json:"file_dirty,omitempty"` + FileWriteback uint64 `protobuf:"varint,9,opt,name=file_writeback,json=fileWriteback,proto3" json:"file_writeback,omitempty"` + AnonThp uint64 `protobuf:"varint,10,opt,name=anon_thp,json=anonThp,proto3" json:"anon_thp,omitempty"` + InactiveAnon uint64 `protobuf:"varint,11,opt,name=inactive_anon,json=inactiveAnon,proto3" json:"inactive_anon,omitempty"` + ActiveAnon uint64 `protobuf:"varint,12,opt,name=active_anon,json=activeAnon,proto3" json:"active_anon,omitempty"` + InactiveFile uint64 `protobuf:"varint,13,opt,name=inactive_file,json=inactiveFile,proto3" json:"inactive_file,omitempty"` + ActiveFile uint64 `protobuf:"varint,14,opt,name=active_file,json=activeFile,proto3" json:"active_file,omitempty"` + Unevictable uint64 `protobuf:"varint,15,opt,name=unevictable,proto3" json:"unevictable,omitempty"` + SlabReclaimable uint64 `protobuf:"varint,16,opt,name=slab_reclaimable,json=slabReclaimable,proto3" json:"slab_reclaimable,omitempty"` + SlabUnreclaimable uint64 `protobuf:"varint,17,opt,name=slab_unreclaimable,json=slabUnreclaimable,proto3" json:"slab_unreclaimable,omitempty"` + Pgfault uint64 `protobuf:"varint,18,opt,name=pgfault,proto3" json:"pgfault,omitempty"` + Pgmajfault uint64 `protobuf:"varint,19,opt,name=pgmajfault,proto3" json:"pgmajfault,omitempty"` + WorkingsetRefault uint64 `protobuf:"varint,20,opt,name=workingset_refault,json=workingsetRefault,proto3" json:"workingset_refault,omitempty"` + WorkingsetActivate uint64 `protobuf:"varint,21,opt,name=workingset_activate,json=workingsetActivate,proto3" json:"workingset_activate,omitempty"` + WorkingsetNodereclaim uint64 `protobuf:"varint,22,opt,name=workingset_nodereclaim,json=workingsetNodereclaim,proto3" json:"workingset_nodereclaim,omitempty"` + Pgrefill uint64 `protobuf:"varint,23,opt,name=pgrefill,proto3" json:"pgrefill,omitempty"` + Pgscan uint64 `protobuf:"varint,24,opt,name=pgscan,proto3" json:"pgscan,omitempty"` + Pgsteal uint64 `protobuf:"varint,25,opt,name=pgsteal,proto3" json:"pgsteal,omitempty"` + Pgactivate uint64 `protobuf:"varint,26,opt,name=pgactivate,proto3" json:"pgactivate,omitempty"` + Pgdeactivate uint64 `protobuf:"varint,27,opt,name=pgdeactivate,proto3" json:"pgdeactivate,omitempty"` + Pglazyfree uint64 `protobuf:"varint,28,opt,name=pglazyfree,proto3" json:"pglazyfree,omitempty"` + Pglazyfreed uint64 `protobuf:"varint,29,opt,name=pglazyfreed,proto3" json:"pglazyfreed,omitempty"` + ThpFaultAlloc uint64 `protobuf:"varint,30,opt,name=thp_fault_alloc,json=thpFaultAlloc,proto3" json:"thp_fault_alloc,omitempty"` + ThpCollapseAlloc uint64 `protobuf:"varint,31,opt,name=thp_collapse_alloc,json=thpCollapseAlloc,proto3" json:"thp_collapse_alloc,omitempty"` + Usage uint64 `protobuf:"varint,32,opt,name=usage,proto3" json:"usage,omitempty"` + UsageLimit uint64 `protobuf:"varint,33,opt,name=usage_limit,json=usageLimit,proto3" json:"usage_limit,omitempty"` + SwapUsage uint64 `protobuf:"varint,34,opt,name=swap_usage,json=swapUsage,proto3" json:"swap_usage,omitempty"` + SwapLimit uint64 `protobuf:"varint,35,opt,name=swap_limit,json=swapLimit,proto3" json:"swap_limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MemoryStat) Reset() { *m = MemoryStat{} } +func (*MemoryStat) ProtoMessage() {} +func (*MemoryStat) Descriptor() ([]byte, []int) { + return fileDescriptor_2fc6005842049e6b, []int{3} +} +func (m *MemoryStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MemoryStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MemoryStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MemoryStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_MemoryStat.Merge(m, src) +} +func (m *MemoryStat) XXX_Size() int { + return m.Size() +} +func (m *MemoryStat) XXX_DiscardUnknown() { + xxx_messageInfo_MemoryStat.DiscardUnknown(m) +} + +var xxx_messageInfo_MemoryStat proto.InternalMessageInfo + +type MemoryEvents struct { + Low uint64 `protobuf:"varint,1,opt,name=low,proto3" json:"low,omitempty"` + High uint64 `protobuf:"varint,2,opt,name=high,proto3" json:"high,omitempty"` + Max uint64 `protobuf:"varint,3,opt,name=max,proto3" json:"max,omitempty"` + Oom uint64 `protobuf:"varint,4,opt,name=oom,proto3" json:"oom,omitempty"` + OomKill uint64 `protobuf:"varint,5,opt,name=oom_kill,json=oomKill,proto3" json:"oom_kill,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MemoryEvents) Reset() { *m = MemoryEvents{} } +func (*MemoryEvents) ProtoMessage() {} +func (*MemoryEvents) Descriptor() ([]byte, []int) { + return fileDescriptor_2fc6005842049e6b, []int{4} +} +func (m *MemoryEvents) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MemoryEvents) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MemoryEvents.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MemoryEvents) XXX_Merge(src proto.Message) { + xxx_messageInfo_MemoryEvents.Merge(m, src) +} +func (m *MemoryEvents) XXX_Size() int { + return m.Size() +} +func (m *MemoryEvents) XXX_DiscardUnknown() { + xxx_messageInfo_MemoryEvents.DiscardUnknown(m) +} + +var xxx_messageInfo_MemoryEvents proto.InternalMessageInfo + +type RdmaStat struct { + Current []*RdmaEntry `protobuf:"bytes,1,rep,name=current,proto3" json:"current,omitempty"` + Limit []*RdmaEntry `protobuf:"bytes,2,rep,name=limit,proto3" json:"limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RdmaStat) Reset() { *m = RdmaStat{} } +func (*RdmaStat) ProtoMessage() {} +func (*RdmaStat) Descriptor() ([]byte, []int) { + return fileDescriptor_2fc6005842049e6b, []int{5} +} +func (m *RdmaStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RdmaStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RdmaStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RdmaStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_RdmaStat.Merge(m, src) +} +func (m *RdmaStat) XXX_Size() int { + return m.Size() +} +func (m *RdmaStat) XXX_DiscardUnknown() { + xxx_messageInfo_RdmaStat.DiscardUnknown(m) +} + +var xxx_messageInfo_RdmaStat proto.InternalMessageInfo + +type RdmaEntry struct { + Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` + HcaHandles uint32 `protobuf:"varint,2,opt,name=hca_handles,json=hcaHandles,proto3" json:"hca_handles,omitempty"` + HcaObjects uint32 `protobuf:"varint,3,opt,name=hca_objects,json=hcaObjects,proto3" json:"hca_objects,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RdmaEntry) Reset() { *m = RdmaEntry{} } +func (*RdmaEntry) ProtoMessage() {} +func (*RdmaEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_2fc6005842049e6b, []int{6} +} +func (m *RdmaEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RdmaEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RdmaEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RdmaEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_RdmaEntry.Merge(m, src) +} +func (m *RdmaEntry) XXX_Size() int { + return m.Size() +} +func (m *RdmaEntry) XXX_DiscardUnknown() { + xxx_messageInfo_RdmaEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_RdmaEntry proto.InternalMessageInfo + +type IOStat struct { + Usage []*IOEntry `protobuf:"bytes,1,rep,name=usage,proto3" json:"usage,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *IOStat) Reset() { *m = IOStat{} } +func (*IOStat) ProtoMessage() {} +func (*IOStat) Descriptor() ([]byte, []int) { + return fileDescriptor_2fc6005842049e6b, []int{7} +} +func (m *IOStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IOStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IOStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *IOStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_IOStat.Merge(m, src) +} +func (m *IOStat) XXX_Size() int { + return m.Size() +} +func (m *IOStat) XXX_DiscardUnknown() { + xxx_messageInfo_IOStat.DiscardUnknown(m) +} + +var xxx_messageInfo_IOStat proto.InternalMessageInfo + +type IOEntry struct { + Major uint64 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"` + Minor uint64 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"` + Rbytes uint64 `protobuf:"varint,3,opt,name=rbytes,proto3" json:"rbytes,omitempty"` + Wbytes uint64 `protobuf:"varint,4,opt,name=wbytes,proto3" json:"wbytes,omitempty"` + Rios uint64 `protobuf:"varint,5,opt,name=rios,proto3" json:"rios,omitempty"` + Wios uint64 `protobuf:"varint,6,opt,name=wios,proto3" json:"wios,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *IOEntry) Reset() { *m = IOEntry{} } +func (*IOEntry) ProtoMessage() {} +func (*IOEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_2fc6005842049e6b, []int{8} +} +func (m *IOEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IOEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IOEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *IOEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_IOEntry.Merge(m, src) +} +func (m *IOEntry) XXX_Size() int { + return m.Size() +} +func (m *IOEntry) XXX_DiscardUnknown() { + xxx_messageInfo_IOEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_IOEntry proto.InternalMessageInfo + +type HugeTlbStat struct { + Current uint64 `protobuf:"varint,1,opt,name=current,proto3" json:"current,omitempty"` + Max uint64 `protobuf:"varint,2,opt,name=max,proto3" json:"max,omitempty"` + Pagesize string `protobuf:"bytes,3,opt,name=pagesize,proto3" json:"pagesize,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HugeTlbStat) Reset() { *m = HugeTlbStat{} } +func (*HugeTlbStat) ProtoMessage() {} +func (*HugeTlbStat) Descriptor() ([]byte, []int) { + return fileDescriptor_2fc6005842049e6b, []int{9} +} +func (m *HugeTlbStat) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HugeTlbStat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HugeTlbStat.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HugeTlbStat) XXX_Merge(src proto.Message) { + xxx_messageInfo_HugeTlbStat.Merge(m, src) +} +func (m *HugeTlbStat) XXX_Size() int { + return m.Size() +} +func (m *HugeTlbStat) XXX_DiscardUnknown() { + xxx_messageInfo_HugeTlbStat.DiscardUnknown(m) +} + +var xxx_messageInfo_HugeTlbStat proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Metrics)(nil), "io.containerd.cgroups.v2.Metrics") + proto.RegisterType((*PidsStat)(nil), "io.containerd.cgroups.v2.PidsStat") + proto.RegisterType((*CPUStat)(nil), "io.containerd.cgroups.v2.CPUStat") + proto.RegisterType((*MemoryStat)(nil), "io.containerd.cgroups.v2.MemoryStat") + proto.RegisterType((*MemoryEvents)(nil), "io.containerd.cgroups.v2.MemoryEvents") + proto.RegisterType((*RdmaStat)(nil), "io.containerd.cgroups.v2.RdmaStat") + proto.RegisterType((*RdmaEntry)(nil), "io.containerd.cgroups.v2.RdmaEntry") + proto.RegisterType((*IOStat)(nil), "io.containerd.cgroups.v2.IOStat") + proto.RegisterType((*IOEntry)(nil), "io.containerd.cgroups.v2.IOEntry") + proto.RegisterType((*HugeTlbStat)(nil), "io.containerd.cgroups.v2.HugeTlbStat") +} + +func init() { + proto.RegisterFile("github.com/containerd/cgroups/v2/stats/metrics.proto", fileDescriptor_2fc6005842049e6b) +} + +var fileDescriptor_2fc6005842049e6b = []byte{ + // 1198 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x56, 0x4d, 0x73, 0xd4, 0x46, + 0x13, 0x66, 0xed, 0xc5, 0xeb, 0xed, 0xb5, 0xc1, 0x0c, 0x86, 0x57, 0xc0, 0xcb, 0xda, 0x5e, 0x02, + 0x45, 0xaa, 0x92, 0xdd, 0x94, 0xf3, 0x55, 0x49, 0x91, 0x4a, 0x19, 0x02, 0x45, 0x8a, 0x10, 0x5c, + 0x02, 0x57, 0x8e, 0xaa, 0x59, 0x69, 0x2c, 0x0d, 0x96, 0x34, 0xaa, 0x99, 0x91, 0x1d, 0x73, 0xca, + 0x21, 0xd7, 0x54, 0x7e, 0x4d, 0xfe, 0x03, 0xb7, 0xe4, 0x98, 0x53, 0x2a, 0xf8, 0x97, 0xa4, 0xba, + 0x67, 0x64, 0x29, 0x07, 0x43, 0x6e, 0xd3, 0x4f, 0x3f, 0xdd, 0xea, 0x8f, 0x99, 0x6e, 0xc1, 0x27, + 0xa9, 0xb4, 0x59, 0x3d, 0x9f, 0xc6, 0xaa, 0x98, 0xc5, 0xaa, 0xb4, 0x5c, 0x96, 0x42, 0x27, 0xb3, + 0x38, 0xd5, 0xaa, 0xae, 0xcc, 0xec, 0x70, 0x7b, 0x66, 0x2c, 0xb7, 0x66, 0x56, 0x08, 0xab, 0x65, + 0x6c, 0xa6, 0x95, 0x56, 0x56, 0xb1, 0x40, 0xaa, 0x69, 0xcb, 0x9e, 0x7a, 0xf6, 0xf4, 0x70, 0xfb, + 0xfa, 0x7a, 0xaa, 0x52, 0x45, 0xa4, 0x19, 0x9e, 0x1c, 0x7f, 0xf2, 0xdb, 0x22, 0x0c, 0x9e, 0x3a, + 0x0f, 0xec, 0x33, 0xe8, 0x57, 0x32, 0x31, 0x41, 0x6f, 0xb3, 0x77, 0x77, 0xb4, 0x3d, 0x99, 0x9e, + 0xe5, 0x6a, 0xba, 0x2b, 0x13, 0xf3, 0xdc, 0x72, 0x1b, 0x12, 0x9f, 0xdd, 0x83, 0xc5, 0xb8, 0xaa, + 0x83, 0x05, 0x32, 0xdb, 0x3a, 0xdb, 0xec, 0xc1, 0xee, 0x1e, 0x5a, 0xdd, 0x1f, 0x9c, 0xfc, 0xb5, + 0xb1, 0xf8, 0x60, 0x77, 0x2f, 0x44, 0x33, 0x76, 0x0f, 0x96, 0x0a, 0x51, 0x28, 0x7d, 0x1c, 0xf4, + 0xc9, 0xc1, 0x7b, 0x67, 0x3b, 0x78, 0x4a, 0x3c, 0xfa, 0xb2, 0xb7, 0xc1, 0x98, 0x75, 0x52, 0xf0, + 0xe0, 0xfc, 0xbb, 0x62, 0x0e, 0x93, 0x82, 0xbb, 0x98, 0x91, 0xcf, 0x3e, 0x82, 0x05, 0xa9, 0x82, + 0x25, 0xb2, 0xda, 0x3c, 0xdb, 0xea, 0xdb, 0x67, 0x64, 0xb3, 0x20, 0x15, 0xfb, 0x1a, 0x06, 0x59, + 0x9d, 0x0a, 0x9b, 0xcf, 0x83, 0xc1, 0xe6, 0xe2, 0xdd, 0xd1, 0xf6, 0xed, 0xb3, 0xcd, 0x1e, 0xd7, + 0xa9, 0x78, 0x91, 0xcf, 0xc9, 0xb6, 0xb1, 0x62, 0x4f, 0x60, 0xd5, 0x05, 0x1d, 0x89, 0x43, 0x51, + 0x5a, 0x13, 0x2c, 0xd3, 0xd7, 0xef, 0xbc, 0x2b, 0xdf, 0x87, 0xc4, 0x0e, 0x57, 0x8a, 0x8e, 0x34, + 0xf9, 0x12, 0x96, 0x9b, 0x2e, 0xb0, 0x00, 0x06, 0x71, 0xad, 0xb5, 0x28, 0x2d, 0xb5, 0xae, 0x1f, + 0x36, 0x22, 0x5b, 0x87, 0xf3, 0xb9, 0x2c, 0xa4, 0xa5, 0xde, 0xf4, 0x43, 0x27, 0x4c, 0x7e, 0xef, + 0xc1, 0xc0, 0xf7, 0x82, 0xdd, 0x04, 0xa8, 0x0d, 0x4f, 0x45, 0x54, 0x1b, 0x11, 0x7b, 0xf3, 0x21, + 0x21, 0x7b, 0x46, 0xc4, 0xec, 0x06, 0x0c, 0x6b, 0x23, 0xb4, 0xd3, 0x3a, 0x27, 0xcb, 0x08, 0x90, + 0x72, 0x03, 0x46, 0xe6, 0xd8, 0x58, 0x51, 0x38, 0xf5, 0x22, 0xa9, 0xc1, 0x41, 0x44, 0xb8, 0x09, + 0x50, 0xea, 0xa8, 0x12, 0x5a, 0xaa, 0xc4, 0x50, 0x7b, 0xfb, 0xe1, 0xb0, 0xd4, 0xbb, 0x0e, 0x60, + 0x5b, 0xb0, 0x52, 0xea, 0xc8, 0x66, 0x5a, 0x59, 0x9b, 0x8b, 0x84, 0x7a, 0xd8, 0x0f, 0x47, 0xa5, + 0x7e, 0xd1, 0x40, 0xec, 0x36, 0x5c, 0x38, 0xd5, 0xbb, 0xaf, 0x2c, 0x11, 0x69, 0xf5, 0x14, 0xc5, + 0x0f, 0x4d, 0x7e, 0x1d, 0x02, 0xb4, 0x97, 0x83, 0x31, 0xe8, 0xf3, 0x52, 0x95, 0x3e, 0x1d, 0x3a, + 0x23, 0xb6, 0x2f, 0x73, 0xe1, 0x93, 0xa0, 0x33, 0x06, 0x70, 0x20, 0x74, 0x29, 0xf2, 0xc8, 0x58, + 0x1e, 0x1f, 0xf8, 0x0c, 0x46, 0x0e, 0x7b, 0x8e, 0x10, 0x9a, 0x99, 0x9c, 0xcf, 0x7d, 0xf0, 0x74, + 0x26, 0x4c, 0xc5, 0x07, 0x3e, 0x5e, 0x3a, 0x63, 0xa5, 0x4d, 0x56, 0x88, 0xc2, 0xc7, 0xe7, 0x04, + 0xac, 0x10, 0x7e, 0x28, 0x2a, 0x78, 0x55, 0x89, 0x24, 0x18, 0xb8, 0x0a, 0x21, 0xf4, 0x94, 0x10, + 0xac, 0x10, 0x11, 0x12, 0xa9, 0xed, 0x31, 0x5d, 0x88, 0x7e, 0x38, 0x44, 0xe4, 0x1b, 0x04, 0x30, + 0x7d, 0x52, 0x1f, 0x69, 0x69, 0xc5, 0x1c, 0x43, 0x1c, 0xba, 0xf4, 0x11, 0xfd, 0xa1, 0x01, 0xd9, + 0x35, 0x58, 0xc6, 0x1c, 0x23, 0x9b, 0x55, 0x01, 0xb8, 0x1b, 0x80, 0xf2, 0x8b, 0xac, 0x62, 0xb7, + 0x60, 0x55, 0x96, 0x3c, 0xb6, 0xf2, 0x50, 0x44, 0x54, 0x93, 0x11, 0xe9, 0x57, 0x1a, 0x70, 0x07, + 0x6b, 0xb3, 0x01, 0xa3, 0x2e, 0x65, 0xc5, 0x85, 0xd9, 0x21, 0x74, 0xbd, 0x50, 0x15, 0x57, 0xff, + 0xed, 0xe5, 0x11, 0x56, 0xb3, 0xf5, 0x42, 0x94, 0x0b, 0x5d, 0x2f, 0x44, 0xd8, 0x84, 0x51, 0x5d, + 0x8a, 0x43, 0x19, 0x5b, 0x3e, 0xcf, 0x45, 0x70, 0xd1, 0x55, 0xbb, 0x03, 0xb1, 0xf7, 0x61, 0x0d, + 0x2b, 0x1c, 0x69, 0x11, 0xe7, 0x5c, 0x16, 0x44, 0x5b, 0x23, 0xda, 0x45, 0xc4, 0xc3, 0x16, 0x66, + 0x1f, 0x02, 0x23, 0x6a, 0x5d, 0x76, 0xc9, 0x97, 0x88, 0x7c, 0x09, 0x35, 0x7b, 0x5d, 0x05, 0xbe, + 0x91, 0x2a, 0xdd, 0xe7, 0x75, 0x6e, 0x03, 0xe6, 0x2a, 0xe4, 0x45, 0x36, 0x06, 0xa8, 0xd2, 0x82, + 0xbf, 0x74, 0xca, 0xcb, 0x2e, 0xea, 0x16, 0xc1, 0x0f, 0x1d, 0x29, 0x7d, 0x20, 0xcb, 0xd4, 0x08, + 0x1b, 0x69, 0xe1, 0x78, 0xeb, 0xee, 0x43, 0xad, 0x26, 0x74, 0x0a, 0x36, 0x83, 0xcb, 0x1d, 0x3a, + 0x65, 0xcf, 0xad, 0x08, 0xae, 0x10, 0xbf, 0xe3, 0x69, 0xc7, 0x6b, 0xd8, 0xa7, 0x70, 0xb5, 0x63, + 0x50, 0xaa, 0x44, 0xf8, 0xb8, 0x83, 0xab, 0x64, 0x73, 0xa5, 0xd5, 0x7e, 0xdf, 0x2a, 0xd9, 0x75, + 0x58, 0xae, 0x52, 0x2d, 0xf6, 0x65, 0x9e, 0x07, 0xff, 0x73, 0x0f, 0xb3, 0x91, 0xd9, 0x55, 0x58, + 0xaa, 0x52, 0x13, 0xf3, 0x32, 0x08, 0x48, 0xe3, 0x25, 0x57, 0x04, 0x63, 0x05, 0xcf, 0x83, 0x6b, + 0x4d, 0x11, 0x48, 0x74, 0x45, 0x38, 0x0d, 0xf6, 0x7a, 0x53, 0x84, 0x06, 0x61, 0x13, 0x58, 0xa9, + 0xd2, 0x44, 0x9c, 0x32, 0x6e, 0xb8, 0xfe, 0x77, 0x31, 0xe7, 0x23, 0xe7, 0xaf, 0x8e, 0xf7, 0xb5, + 0x10, 0xc1, 0xff, 0x1b, 0x1f, 0x0d, 0x82, 0xed, 0x6f, 0xa5, 0x24, 0xb8, 0xe9, 0xda, 0xdf, 0x81, + 0xd8, 0x1d, 0xb8, 0x68, 0xb3, 0x2a, 0xa2, 0x42, 0x46, 0x3c, 0xcf, 0x55, 0x1c, 0x8c, 0x9b, 0xe7, + 0x5e, 0x3d, 0x42, 0x74, 0x07, 0x41, 0xf6, 0x01, 0x30, 0xe4, 0xc5, 0x2a, 0xcf, 0x79, 0x65, 0x84, + 0xa7, 0x6e, 0x10, 0x75, 0xcd, 0x66, 0xd5, 0x03, 0xaf, 0x70, 0xec, 0x75, 0x38, 0x4f, 0x03, 0x2d, + 0xd8, 0x74, 0x4f, 0x93, 0x04, 0xbc, 0xad, 0x6e, 0xf0, 0xb9, 0x01, 0xb9, 0xe5, 0xc2, 0x25, 0xe8, + 0x3b, 0x44, 0xf0, 0x69, 0x9a, 0x23, 0x5e, 0x45, 0xce, 0x76, 0xe2, 0x9e, 0x26, 0x22, 0x7b, 0x64, + 0xdf, 0xa8, 0x9d, 0xf9, 0xad, 0x56, 0x4d, 0xd6, 0x13, 0x03, 0x2b, 0xdd, 0xe9, 0xcd, 0xd6, 0x60, + 0x31, 0x57, 0x47, 0x7e, 0x22, 0xe1, 0x11, 0xa7, 0x48, 0x26, 0xd3, 0xac, 0x19, 0x48, 0x78, 0x46, + 0x56, 0xc1, 0x7f, 0xf4, 0x73, 0x08, 0x8f, 0x88, 0x28, 0x55, 0xf8, 0xf1, 0x83, 0x47, 0x7c, 0xec, + 0x4a, 0x15, 0xd1, 0x01, 0x36, 0xde, 0x4d, 0xa0, 0x81, 0x52, 0xc5, 0x13, 0x99, 0xe7, 0x93, 0x9f, + 0x7b, 0xb0, 0xdc, 0xec, 0x39, 0xf6, 0x55, 0x77, 0x2b, 0xe0, 0xbe, 0xba, 0xf5, 0xf6, 0xe5, 0xf8, + 0xb0, 0xb4, 0xfa, 0xb8, 0x5d, 0x1d, 0x5f, 0xb4, 0xab, 0xe3, 0x3f, 0x1b, 0xfb, 0xfd, 0x22, 0x60, + 0x78, 0x8a, 0xe1, 0x5d, 0x4c, 0xf0, 0x81, 0x0b, 0xca, 0x7d, 0x18, 0x7a, 0x09, 0xeb, 0x9f, 0xc5, + 0x3c, 0xca, 0x78, 0x99, 0xe4, 0xc2, 0x50, 0x15, 0x56, 0x43, 0xc8, 0x62, 0xfe, 0xd8, 0x21, 0x0d, + 0x41, 0xcd, 0x5f, 0x8a, 0xd8, 0x1a, 0xaa, 0x89, 0x23, 0x3c, 0x73, 0xc8, 0x64, 0x07, 0x96, 0xdc, + 0x7a, 0x66, 0x9f, 0x37, 0x1d, 0x76, 0x89, 0x6e, 0xbd, 0x6d, 0x9f, 0xfb, 0x48, 0x89, 0x3f, 0xf9, + 0xa5, 0x07, 0x03, 0x0f, 0xe1, 0x35, 0x29, 0xf8, 0x4b, 0xa5, 0x7d, 0x8f, 0x9c, 0x40, 0xa8, 0x2c, + 0x95, 0x6e, 0x36, 0x28, 0x09, 0x98, 0x94, 0x9e, 0x1f, 0x5b, 0x61, 0x7c, 0xab, 0xbc, 0x84, 0xf8, + 0x91, 0xc3, 0x5d, 0xc3, 0xbc, 0x84, 0xbd, 0xd6, 0x52, 0x99, 0x66, 0x63, 0xe0, 0x19, 0xb1, 0x23, + 0xc4, 0xdc, 0xc2, 0xa0, 0xf3, 0x64, 0x0f, 0x46, 0x9d, 0x5f, 0x87, 0xb7, 0x2c, 0x76, 0x7f, 0x51, + 0x16, 0xda, 0x8b, 0x82, 0xf3, 0x80, 0xa7, 0xc2, 0xc8, 0x57, 0x82, 0x82, 0x1a, 0x86, 0xa7, 0xf2, + 0xfd, 0xe0, 0xf5, 0x9b, 0xf1, 0xb9, 0x3f, 0xdf, 0x8c, 0xcf, 0xfd, 0x74, 0x32, 0xee, 0xbd, 0x3e, + 0x19, 0xf7, 0xfe, 0x38, 0x19, 0xf7, 0xfe, 0x3e, 0x19, 0xf7, 0xe6, 0x4b, 0xf4, 0x17, 0xf8, 0xf1, + 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x4f, 0x2b, 0x30, 0xd6, 0x6d, 0x0a, 0x00, 0x00, +} + +func (m *Metrics) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Metrics) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Metrics) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.MemoryEvents != nil { + { + size, err := m.MemoryEvents.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if len(m.Hugetlb) > 0 { + for iNdEx := len(m.Hugetlb) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Hugetlb[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if m.Io != nil { + { + size, err := m.Io.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.Rdma != nil { + { + size, err := m.Rdma.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.Memory != nil { + { + size, err := m.Memory.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.CPU != nil { + { + size, err := m.CPU.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Pids != nil { + { + size, err := m.Pids.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PidsStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PidsStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PidsStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Limit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Limit)) + i-- + dAtA[i] = 0x10 + } + if m.Current != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Current)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *CPUStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CPUStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CPUStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.ThrottledUsec != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ThrottledUsec)) + i-- + dAtA[i] = 0x30 + } + if m.NrThrottled != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrThrottled)) + i-- + dAtA[i] = 0x28 + } + if m.NrPeriods != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.NrPeriods)) + i-- + dAtA[i] = 0x20 + } + if m.SystemUsec != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.SystemUsec)) + i-- + dAtA[i] = 0x18 + } + if m.UserUsec != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.UserUsec)) + i-- + dAtA[i] = 0x10 + } + if m.UsageUsec != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.UsageUsec)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MemoryStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MemoryStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MemoryStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.SwapLimit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.SwapLimit)) + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x98 + } + if m.SwapUsage != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.SwapUsage)) + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x90 + } + if m.UsageLimit != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.UsageLimit)) + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x88 + } + if m.Usage != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Usage)) + i-- + dAtA[i] = 0x2 + i-- + dAtA[i] = 0x80 + } + if m.ThpCollapseAlloc != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ThpCollapseAlloc)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xf8 + } + if m.ThpFaultAlloc != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ThpFaultAlloc)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xf0 + } + if m.Pglazyfreed != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Pglazyfreed)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe8 + } + if m.Pglazyfree != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Pglazyfree)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe0 + } + if m.Pgdeactivate != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Pgdeactivate)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xd8 + } + if m.Pgactivate != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Pgactivate)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xd0 + } + if m.Pgsteal != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Pgsteal)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc8 + } + if m.Pgscan != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Pgscan)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc0 + } + if m.Pgrefill != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Pgrefill)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb8 + } + if m.WorkingsetNodereclaim != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.WorkingsetNodereclaim)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb0 + } + if m.WorkingsetActivate != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.WorkingsetActivate)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa8 + } + if m.WorkingsetRefault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.WorkingsetRefault)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa0 + } + if m.Pgmajfault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Pgmajfault)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } + if m.Pgfault != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Pgfault)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x90 + } + if m.SlabUnreclaimable != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.SlabUnreclaimable)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x88 + } + if m.SlabReclaimable != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.SlabReclaimable)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } + if m.Unevictable != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Unevictable)) + i-- + dAtA[i] = 0x78 + } + if m.ActiveFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ActiveFile)) + i-- + dAtA[i] = 0x70 + } + if m.InactiveFile != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.InactiveFile)) + i-- + dAtA[i] = 0x68 + } + if m.ActiveAnon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.ActiveAnon)) + i-- + dAtA[i] = 0x60 + } + if m.InactiveAnon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.InactiveAnon)) + i-- + dAtA[i] = 0x58 + } + if m.AnonThp != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.AnonThp)) + i-- + dAtA[i] = 0x50 + } + if m.FileWriteback != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.FileWriteback)) + i-- + dAtA[i] = 0x48 + } + if m.FileDirty != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.FileDirty)) + i-- + dAtA[i] = 0x40 + } + if m.FileMapped != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.FileMapped)) + i-- + dAtA[i] = 0x38 + } + if m.Shmem != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Shmem)) + i-- + dAtA[i] = 0x30 + } + if m.Sock != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Sock)) + i-- + dAtA[i] = 0x28 + } + if m.Slab != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Slab)) + i-- + dAtA[i] = 0x20 + } + if m.KernelStack != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.KernelStack)) + i-- + dAtA[i] = 0x18 + } + if m.File != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.File)) + i-- + dAtA[i] = 0x10 + } + if m.Anon != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Anon)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MemoryEvents) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MemoryEvents) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MemoryEvents) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.OomKill != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.OomKill)) + i-- + dAtA[i] = 0x28 + } + if m.Oom != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Oom)) + i-- + dAtA[i] = 0x20 + } + if m.Max != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Max)) + i-- + dAtA[i] = 0x18 + } + if m.High != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.High)) + i-- + dAtA[i] = 0x10 + } + if m.Low != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Low)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *RdmaStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RdmaStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RdmaStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Limit) > 0 { + for iNdEx := len(m.Limit) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Limit[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Current) > 0 { + for iNdEx := len(m.Current) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Current[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *RdmaEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RdmaEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RdmaEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.HcaObjects != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.HcaObjects)) + i-- + dAtA[i] = 0x18 + } + if m.HcaHandles != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.HcaHandles)) + i-- + dAtA[i] = 0x10 + } + if len(m.Device) > 0 { + i -= len(m.Device) + copy(dAtA[i:], m.Device) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Device))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *IOStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IOStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IOStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Usage) > 0 { + for iNdEx := len(m.Usage) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Usage[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *IOEntry) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *IOEntry) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IOEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Wios != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Wios)) + i-- + dAtA[i] = 0x30 + } + if m.Rios != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Rios)) + i-- + dAtA[i] = 0x28 + } + if m.Wbytes != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Wbytes)) + i-- + dAtA[i] = 0x20 + } + if m.Rbytes != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Rbytes)) + i-- + dAtA[i] = 0x18 + } + if m.Minor != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Minor)) + i-- + dAtA[i] = 0x10 + } + if m.Major != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Major)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *HugeTlbStat) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HugeTlbStat) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HugeTlbStat) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Pagesize) > 0 { + i -= len(m.Pagesize) + copy(dAtA[i:], m.Pagesize) + i = encodeVarintMetrics(dAtA, i, uint64(len(m.Pagesize))) + i-- + dAtA[i] = 0x1a + } + if m.Max != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Max)) + i-- + dAtA[i] = 0x10 + } + if m.Current != 0 { + i = encodeVarintMetrics(dAtA, i, uint64(m.Current)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintMetrics(dAtA []byte, offset int, v uint64) int { + offset -= sovMetrics(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Metrics) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pids != nil { + l = m.Pids.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.CPU != nil { + l = m.CPU.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Memory != nil { + l = m.Memory.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Rdma != nil { + l = m.Rdma.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.Io != nil { + l = m.Io.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if len(m.Hugetlb) > 0 { + for _, e := range m.Hugetlb { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.MemoryEvents != nil { + l = m.MemoryEvents.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *PidsStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Current != 0 { + n += 1 + sovMetrics(uint64(m.Current)) + } + if m.Limit != 0 { + n += 1 + sovMetrics(uint64(m.Limit)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CPUStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.UsageUsec != 0 { + n += 1 + sovMetrics(uint64(m.UsageUsec)) + } + if m.UserUsec != 0 { + n += 1 + sovMetrics(uint64(m.UserUsec)) + } + if m.SystemUsec != 0 { + n += 1 + sovMetrics(uint64(m.SystemUsec)) + } + if m.NrPeriods != 0 { + n += 1 + sovMetrics(uint64(m.NrPeriods)) + } + if m.NrThrottled != 0 { + n += 1 + sovMetrics(uint64(m.NrThrottled)) + } + if m.ThrottledUsec != 0 { + n += 1 + sovMetrics(uint64(m.ThrottledUsec)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *MemoryStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Anon != 0 { + n += 1 + sovMetrics(uint64(m.Anon)) + } + if m.File != 0 { + n += 1 + sovMetrics(uint64(m.File)) + } + if m.KernelStack != 0 { + n += 1 + sovMetrics(uint64(m.KernelStack)) + } + if m.Slab != 0 { + n += 1 + sovMetrics(uint64(m.Slab)) + } + if m.Sock != 0 { + n += 1 + sovMetrics(uint64(m.Sock)) + } + if m.Shmem != 0 { + n += 1 + sovMetrics(uint64(m.Shmem)) + } + if m.FileMapped != 0 { + n += 1 + sovMetrics(uint64(m.FileMapped)) + } + if m.FileDirty != 0 { + n += 1 + sovMetrics(uint64(m.FileDirty)) + } + if m.FileWriteback != 0 { + n += 1 + sovMetrics(uint64(m.FileWriteback)) + } + if m.AnonThp != 0 { + n += 1 + sovMetrics(uint64(m.AnonThp)) + } + if m.InactiveAnon != 0 { + n += 1 + sovMetrics(uint64(m.InactiveAnon)) + } + if m.ActiveAnon != 0 { + n += 1 + sovMetrics(uint64(m.ActiveAnon)) + } + if m.InactiveFile != 0 { + n += 1 + sovMetrics(uint64(m.InactiveFile)) + } + if m.ActiveFile != 0 { + n += 1 + sovMetrics(uint64(m.ActiveFile)) + } + if m.Unevictable != 0 { + n += 1 + sovMetrics(uint64(m.Unevictable)) + } + if m.SlabReclaimable != 0 { + n += 2 + sovMetrics(uint64(m.SlabReclaimable)) + } + if m.SlabUnreclaimable != 0 { + n += 2 + sovMetrics(uint64(m.SlabUnreclaimable)) + } + if m.Pgfault != 0 { + n += 2 + sovMetrics(uint64(m.Pgfault)) + } + if m.Pgmajfault != 0 { + n += 2 + sovMetrics(uint64(m.Pgmajfault)) + } + if m.WorkingsetRefault != 0 { + n += 2 + sovMetrics(uint64(m.WorkingsetRefault)) + } + if m.WorkingsetActivate != 0 { + n += 2 + sovMetrics(uint64(m.WorkingsetActivate)) + } + if m.WorkingsetNodereclaim != 0 { + n += 2 + sovMetrics(uint64(m.WorkingsetNodereclaim)) + } + if m.Pgrefill != 0 { + n += 2 + sovMetrics(uint64(m.Pgrefill)) + } + if m.Pgscan != 0 { + n += 2 + sovMetrics(uint64(m.Pgscan)) + } + if m.Pgsteal != 0 { + n += 2 + sovMetrics(uint64(m.Pgsteal)) + } + if m.Pgactivate != 0 { + n += 2 + sovMetrics(uint64(m.Pgactivate)) + } + if m.Pgdeactivate != 0 { + n += 2 + sovMetrics(uint64(m.Pgdeactivate)) + } + if m.Pglazyfree != 0 { + n += 2 + sovMetrics(uint64(m.Pglazyfree)) + } + if m.Pglazyfreed != 0 { + n += 2 + sovMetrics(uint64(m.Pglazyfreed)) + } + if m.ThpFaultAlloc != 0 { + n += 2 + sovMetrics(uint64(m.ThpFaultAlloc)) + } + if m.ThpCollapseAlloc != 0 { + n += 2 + sovMetrics(uint64(m.ThpCollapseAlloc)) + } + if m.Usage != 0 { + n += 2 + sovMetrics(uint64(m.Usage)) + } + if m.UsageLimit != 0 { + n += 2 + sovMetrics(uint64(m.UsageLimit)) + } + if m.SwapUsage != 0 { + n += 2 + sovMetrics(uint64(m.SwapUsage)) + } + if m.SwapLimit != 0 { + n += 2 + sovMetrics(uint64(m.SwapLimit)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *MemoryEvents) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Low != 0 { + n += 1 + sovMetrics(uint64(m.Low)) + } + if m.High != 0 { + n += 1 + sovMetrics(uint64(m.High)) + } + if m.Max != 0 { + n += 1 + sovMetrics(uint64(m.Max)) + } + if m.Oom != 0 { + n += 1 + sovMetrics(uint64(m.Oom)) + } + if m.OomKill != 0 { + n += 1 + sovMetrics(uint64(m.OomKill)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RdmaStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Current) > 0 { + for _, e := range m.Current { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if len(m.Limit) > 0 { + for _, e := range m.Limit { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *RdmaEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Device) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.HcaHandles != 0 { + n += 1 + sovMetrics(uint64(m.HcaHandles)) + } + if m.HcaObjects != 0 { + n += 1 + sovMetrics(uint64(m.HcaObjects)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *IOStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Usage) > 0 { + for _, e := range m.Usage { + l = e.Size() + n += 1 + l + sovMetrics(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *IOEntry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Major != 0 { + n += 1 + sovMetrics(uint64(m.Major)) + } + if m.Minor != 0 { + n += 1 + sovMetrics(uint64(m.Minor)) + } + if m.Rbytes != 0 { + n += 1 + sovMetrics(uint64(m.Rbytes)) + } + if m.Wbytes != 0 { + n += 1 + sovMetrics(uint64(m.Wbytes)) + } + if m.Rios != 0 { + n += 1 + sovMetrics(uint64(m.Rios)) + } + if m.Wios != 0 { + n += 1 + sovMetrics(uint64(m.Wios)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *HugeTlbStat) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Current != 0 { + n += 1 + sovMetrics(uint64(m.Current)) + } + if m.Max != 0 { + n += 1 + sovMetrics(uint64(m.Max)) + } + l = len(m.Pagesize) + if l > 0 { + n += 1 + l + sovMetrics(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovMetrics(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMetrics(x uint64) (n int) { + return sovMetrics(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Metrics) String() string { + if this == nil { + return "nil" + } + repeatedStringForHugetlb := "[]*HugeTlbStat{" + for _, f := range this.Hugetlb { + repeatedStringForHugetlb += strings.Replace(f.String(), "HugeTlbStat", "HugeTlbStat", 1) + "," + } + repeatedStringForHugetlb += "}" + s := strings.Join([]string{`&Metrics{`, + `Pids:` + strings.Replace(this.Pids.String(), "PidsStat", "PidsStat", 1) + `,`, + `CPU:` + strings.Replace(this.CPU.String(), "CPUStat", "CPUStat", 1) + `,`, + `Memory:` + strings.Replace(this.Memory.String(), "MemoryStat", "MemoryStat", 1) + `,`, + `Rdma:` + strings.Replace(this.Rdma.String(), "RdmaStat", "RdmaStat", 1) + `,`, + `Io:` + strings.Replace(this.Io.String(), "IOStat", "IOStat", 1) + `,`, + `Hugetlb:` + repeatedStringForHugetlb + `,`, + `MemoryEvents:` + strings.Replace(this.MemoryEvents.String(), "MemoryEvents", "MemoryEvents", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *PidsStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PidsStat{`, + `Current:` + fmt.Sprintf("%v", this.Current) + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *CPUStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CPUStat{`, + `UsageUsec:` + fmt.Sprintf("%v", this.UsageUsec) + `,`, + `UserUsec:` + fmt.Sprintf("%v", this.UserUsec) + `,`, + `SystemUsec:` + fmt.Sprintf("%v", this.SystemUsec) + `,`, + `NrPeriods:` + fmt.Sprintf("%v", this.NrPeriods) + `,`, + `NrThrottled:` + fmt.Sprintf("%v", this.NrThrottled) + `,`, + `ThrottledUsec:` + fmt.Sprintf("%v", this.ThrottledUsec) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *MemoryStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MemoryStat{`, + `Anon:` + fmt.Sprintf("%v", this.Anon) + `,`, + `File:` + fmt.Sprintf("%v", this.File) + `,`, + `KernelStack:` + fmt.Sprintf("%v", this.KernelStack) + `,`, + `Slab:` + fmt.Sprintf("%v", this.Slab) + `,`, + `Sock:` + fmt.Sprintf("%v", this.Sock) + `,`, + `Shmem:` + fmt.Sprintf("%v", this.Shmem) + `,`, + `FileMapped:` + fmt.Sprintf("%v", this.FileMapped) + `,`, + `FileDirty:` + fmt.Sprintf("%v", this.FileDirty) + `,`, + `FileWriteback:` + fmt.Sprintf("%v", this.FileWriteback) + `,`, + `AnonThp:` + fmt.Sprintf("%v", this.AnonThp) + `,`, + `InactiveAnon:` + fmt.Sprintf("%v", this.InactiveAnon) + `,`, + `ActiveAnon:` + fmt.Sprintf("%v", this.ActiveAnon) + `,`, + `InactiveFile:` + fmt.Sprintf("%v", this.InactiveFile) + `,`, + `ActiveFile:` + fmt.Sprintf("%v", this.ActiveFile) + `,`, + `Unevictable:` + fmt.Sprintf("%v", this.Unevictable) + `,`, + `SlabReclaimable:` + fmt.Sprintf("%v", this.SlabReclaimable) + `,`, + `SlabUnreclaimable:` + fmt.Sprintf("%v", this.SlabUnreclaimable) + `,`, + `Pgfault:` + fmt.Sprintf("%v", this.Pgfault) + `,`, + `Pgmajfault:` + fmt.Sprintf("%v", this.Pgmajfault) + `,`, + `WorkingsetRefault:` + fmt.Sprintf("%v", this.WorkingsetRefault) + `,`, + `WorkingsetActivate:` + fmt.Sprintf("%v", this.WorkingsetActivate) + `,`, + `WorkingsetNodereclaim:` + fmt.Sprintf("%v", this.WorkingsetNodereclaim) + `,`, + `Pgrefill:` + fmt.Sprintf("%v", this.Pgrefill) + `,`, + `Pgscan:` + fmt.Sprintf("%v", this.Pgscan) + `,`, + `Pgsteal:` + fmt.Sprintf("%v", this.Pgsteal) + `,`, + `Pgactivate:` + fmt.Sprintf("%v", this.Pgactivate) + `,`, + `Pgdeactivate:` + fmt.Sprintf("%v", this.Pgdeactivate) + `,`, + `Pglazyfree:` + fmt.Sprintf("%v", this.Pglazyfree) + `,`, + `Pglazyfreed:` + fmt.Sprintf("%v", this.Pglazyfreed) + `,`, + `ThpFaultAlloc:` + fmt.Sprintf("%v", this.ThpFaultAlloc) + `,`, + `ThpCollapseAlloc:` + fmt.Sprintf("%v", this.ThpCollapseAlloc) + `,`, + `Usage:` + fmt.Sprintf("%v", this.Usage) + `,`, + `UsageLimit:` + fmt.Sprintf("%v", this.UsageLimit) + `,`, + `SwapUsage:` + fmt.Sprintf("%v", this.SwapUsage) + `,`, + `SwapLimit:` + fmt.Sprintf("%v", this.SwapLimit) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *MemoryEvents) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&MemoryEvents{`, + `Low:` + fmt.Sprintf("%v", this.Low) + `,`, + `High:` + fmt.Sprintf("%v", this.High) + `,`, + `Max:` + fmt.Sprintf("%v", this.Max) + `,`, + `Oom:` + fmt.Sprintf("%v", this.Oom) + `,`, + `OomKill:` + fmt.Sprintf("%v", this.OomKill) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *RdmaStat) String() string { + if this == nil { + return "nil" + } + repeatedStringForCurrent := "[]*RdmaEntry{" + for _, f := range this.Current { + repeatedStringForCurrent += strings.Replace(f.String(), "RdmaEntry", "RdmaEntry", 1) + "," + } + repeatedStringForCurrent += "}" + repeatedStringForLimit := "[]*RdmaEntry{" + for _, f := range this.Limit { + repeatedStringForLimit += strings.Replace(f.String(), "RdmaEntry", "RdmaEntry", 1) + "," + } + repeatedStringForLimit += "}" + s := strings.Join([]string{`&RdmaStat{`, + `Current:` + repeatedStringForCurrent + `,`, + `Limit:` + repeatedStringForLimit + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *RdmaEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RdmaEntry{`, + `Device:` + fmt.Sprintf("%v", this.Device) + `,`, + `HcaHandles:` + fmt.Sprintf("%v", this.HcaHandles) + `,`, + `HcaObjects:` + fmt.Sprintf("%v", this.HcaObjects) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *IOStat) String() string { + if this == nil { + return "nil" + } + repeatedStringForUsage := "[]*IOEntry{" + for _, f := range this.Usage { + repeatedStringForUsage += strings.Replace(f.String(), "IOEntry", "IOEntry", 1) + "," + } + repeatedStringForUsage += "}" + s := strings.Join([]string{`&IOStat{`, + `Usage:` + repeatedStringForUsage + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *IOEntry) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&IOEntry{`, + `Major:` + fmt.Sprintf("%v", this.Major) + `,`, + `Minor:` + fmt.Sprintf("%v", this.Minor) + `,`, + `Rbytes:` + fmt.Sprintf("%v", this.Rbytes) + `,`, + `Wbytes:` + fmt.Sprintf("%v", this.Wbytes) + `,`, + `Rios:` + fmt.Sprintf("%v", this.Rios) + `,`, + `Wios:` + fmt.Sprintf("%v", this.Wios) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *HugeTlbStat) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&HugeTlbStat{`, + `Current:` + fmt.Sprintf("%v", this.Current) + `,`, + `Max:` + fmt.Sprintf("%v", this.Max) + `,`, + `Pagesize:` + fmt.Sprintf("%v", this.Pagesize) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringMetrics(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Metrics) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Metrics: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Metrics: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pids", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Pids == nil { + m.Pids = &PidsStat{} + } + if err := m.Pids.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CPU", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CPU == nil { + m.CPU = &CPUStat{} + } + if err := m.CPU.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Memory == nil { + m.Memory = &MemoryStat{} + } + if err := m.Memory.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rdma", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Rdma == nil { + m.Rdma = &RdmaStat{} + } + if err := m.Rdma.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Io", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Io == nil { + m.Io = &IOStat{} + } + if err := m.Io.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hugetlb", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Hugetlb = append(m.Hugetlb, &HugeTlbStat{}) + if err := m.Hugetlb[len(m.Hugetlb)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MemoryEvents", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.MemoryEvents == nil { + m.MemoryEvents = &MemoryEvents{} + } + if err := m.MemoryEvents.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PidsStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PidsStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PidsStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) + } + m.Current = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Current |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CPUStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CPUStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CPUStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UsageUsec", wireType) + } + m.UsageUsec = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UsageUsec |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UserUsec", wireType) + } + m.UserUsec = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UserUsec |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SystemUsec", wireType) + } + m.SystemUsec = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SystemUsec |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrPeriods", wireType) + } + m.NrPeriods = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrPeriods |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NrThrottled", wireType) + } + m.NrThrottled = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NrThrottled |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ThrottledUsec", wireType) + } + m.ThrottledUsec = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ThrottledUsec |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MemoryStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MemoryStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MemoryStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Anon", wireType) + } + m.Anon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Anon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field File", wireType) + } + m.File = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.File |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field KernelStack", wireType) + } + m.KernelStack = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.KernelStack |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Slab", wireType) + } + m.Slab = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Slab |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sock", wireType) + } + m.Sock = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sock |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Shmem", wireType) + } + m.Shmem = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Shmem |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FileMapped", wireType) + } + m.FileMapped = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.FileMapped |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FileDirty", wireType) + } + m.FileDirty = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.FileDirty |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FileWriteback", wireType) + } + m.FileWriteback = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.FileWriteback |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AnonThp", wireType) + } + m.AnonThp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AnonThp |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InactiveAnon", wireType) + } + m.InactiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InactiveAnon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveAnon", wireType) + } + m.ActiveAnon = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ActiveAnon |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InactiveFile", wireType) + } + m.InactiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InactiveFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 14: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ActiveFile", wireType) + } + m.ActiveFile = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ActiveFile |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 15: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Unevictable", wireType) + } + m.Unevictable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Unevictable |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SlabReclaimable", wireType) + } + m.SlabReclaimable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SlabReclaimable |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SlabUnreclaimable", wireType) + } + m.SlabUnreclaimable = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SlabUnreclaimable |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 18: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pgfault", wireType) + } + m.Pgfault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pgfault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pgmajfault", wireType) + } + m.Pgmajfault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pgmajfault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 20: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkingsetRefault", wireType) + } + m.WorkingsetRefault = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.WorkingsetRefault |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 21: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkingsetActivate", wireType) + } + m.WorkingsetActivate = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.WorkingsetActivate |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 22: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkingsetNodereclaim", wireType) + } + m.WorkingsetNodereclaim = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.WorkingsetNodereclaim |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 23: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pgrefill", wireType) + } + m.Pgrefill = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pgrefill |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 24: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pgscan", wireType) + } + m.Pgscan = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pgscan |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 25: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pgsteal", wireType) + } + m.Pgsteal = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pgsteal |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 26: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pgactivate", wireType) + } + m.Pgactivate = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pgactivate |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 27: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pgdeactivate", wireType) + } + m.Pgdeactivate = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pgdeactivate |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 28: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pglazyfree", wireType) + } + m.Pglazyfree = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pglazyfree |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 29: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Pglazyfreed", wireType) + } + m.Pglazyfreed = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Pglazyfreed |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 30: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ThpFaultAlloc", wireType) + } + m.ThpFaultAlloc = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ThpFaultAlloc |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 31: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ThpCollapseAlloc", wireType) + } + m.ThpCollapseAlloc = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ThpCollapseAlloc |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 32: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + m.Usage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Usage |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 33: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UsageLimit", wireType) + } + m.UsageLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UsageLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 34: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SwapUsage", wireType) + } + m.SwapUsage = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SwapUsage |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 35: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SwapLimit", wireType) + } + m.SwapLimit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SwapLimit |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MemoryEvents) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MemoryEvents: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MemoryEvents: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Low", wireType) + } + m.Low = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Low |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field High", wireType) + } + m.High = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.High |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType) + } + m.Max = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Max |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Oom", wireType) + } + m.Oom = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Oom |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OomKill", wireType) + } + m.OomKill = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OomKill |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RdmaStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RdmaStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RdmaStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Current = append(m.Current, &RdmaEntry{}) + if err := m.Current[len(m.Current)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Limit = append(m.Limit, &RdmaEntry{}) + if err := m.Limit[len(m.Limit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RdmaEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RdmaEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RdmaEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Device = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HcaHandles", wireType) + } + m.HcaHandles = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HcaHandles |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HcaObjects", wireType) + } + m.HcaObjects = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.HcaObjects |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IOStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IOStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IOStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Usage", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Usage = append(m.Usage, &IOEntry{}) + if err := m.Usage[len(m.Usage)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *IOEntry) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: IOEntry: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: IOEntry: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Major", wireType) + } + m.Major = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Major |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Minor", wireType) + } + m.Minor = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Minor |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Rbytes", wireType) + } + m.Rbytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Rbytes |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Wbytes", wireType) + } + m.Wbytes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Wbytes |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Rios", wireType) + } + m.Rios = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Rios |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Wios", wireType) + } + m.Wios = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Wios |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *HugeTlbStat) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HugeTlbStat: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HugeTlbStat: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) + } + m.Current = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Current |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Max", wireType) + } + m.Max = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Max |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Pagesize", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetrics + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMetrics + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Pagesize = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMetrics(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthMetrics + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMetrics(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMetrics + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMetrics + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMetrics + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMetrics + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMetrics = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMetrics = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMetrics = fmt.Errorf("proto: unexpected end of group") +) diff --git a/vendor/github.com/containerd/cgroups/v2/stats/metrics.proto b/vendor/github.com/containerd/cgroups/v2/stats/metrics.proto new file mode 100644 index 0000000000000..8ac472e464593 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/stats/metrics.proto @@ -0,0 +1,105 @@ +syntax = "proto3"; + +package io.containerd.cgroups.v2; + + import "gogoproto/gogo.proto"; + +message Metrics { + PidsStat pids = 1; + CPUStat cpu = 2 [(gogoproto.customname) = "CPU"]; + MemoryStat memory = 4; + RdmaStat rdma = 5; + IOStat io = 6; + repeated HugeTlbStat hugetlb = 7; + MemoryEvents memory_events = 8; +} + +message PidsStat { + uint64 current = 1; + uint64 limit = 2; +} + +message CPUStat { + uint64 usage_usec = 1; + uint64 user_usec = 2; + uint64 system_usec = 3; + uint64 nr_periods = 4; + uint64 nr_throttled = 5; + uint64 throttled_usec = 6; +} + +message MemoryStat { + uint64 anon = 1; + uint64 file = 2; + uint64 kernel_stack = 3; + uint64 slab = 4; + uint64 sock = 5; + uint64 shmem = 6; + uint64 file_mapped = 7; + uint64 file_dirty = 8; + uint64 file_writeback = 9; + uint64 anon_thp = 10; + uint64 inactive_anon = 11; + uint64 active_anon = 12; + uint64 inactive_file = 13; + uint64 active_file = 14; + uint64 unevictable = 15; + uint64 slab_reclaimable = 16; + uint64 slab_unreclaimable = 17; + uint64 pgfault = 18; + uint64 pgmajfault = 19; + uint64 workingset_refault = 20; + uint64 workingset_activate = 21; + uint64 workingset_nodereclaim = 22; + uint64 pgrefill = 23; + uint64 pgscan = 24; + uint64 pgsteal = 25; + uint64 pgactivate = 26; + uint64 pgdeactivate = 27; + uint64 pglazyfree = 28; + uint64 pglazyfreed = 29; + uint64 thp_fault_alloc = 30; + uint64 thp_collapse_alloc = 31; + uint64 usage = 32; + uint64 usage_limit = 33; + uint64 swap_usage = 34; + uint64 swap_limit = 35; +} + +message MemoryEvents { + uint64 low = 1; + uint64 high = 2; + uint64 max = 3; + uint64 oom = 4; + uint64 oom_kill = 5; +} + +message RdmaStat { + repeated RdmaEntry current = 1; + repeated RdmaEntry limit = 2; +} + +message RdmaEntry { + string device = 1; + uint32 hca_handles = 2; + uint32 hca_objects = 3; +} + +message IOStat { + repeated IOEntry usage = 1; +} + +message IOEntry { + uint64 major = 1; + uint64 minor = 2; + uint64 rbytes = 3; + uint64 wbytes = 4; + uint64 rios = 5; + uint64 wios = 6; +} + +message HugeTlbStat { + uint64 current = 1; + uint64 max = 2; + string pagesize = 3; +} diff --git a/vendor/github.com/containerd/cgroups/v2/utils.go b/vendor/github.com/containerd/cgroups/v2/utils.go new file mode 100644 index 0000000000000..8b8d654d6c9a1 --- /dev/null +++ b/vendor/github.com/containerd/cgroups/v2/utils.go @@ -0,0 +1,436 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "math" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/containerd/cgroups/v2/stats" + "github.com/godbus/dbus/v5" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + cgroupProcs = "cgroup.procs" + defaultDirPerm = 0755 +) + +// defaultFilePerm is a var so that the test framework can change the filemode +// of all files created when the tests are running. The difference between the +// tests and real world use is that files like "cgroup.procs" will exist when writing +// to a read cgroup filesystem and do not exist prior when running in the tests. +// this is set to a non 0 value in the test code +var defaultFilePerm = os.FileMode(0) + +// remove will remove a cgroup path handling EAGAIN and EBUSY errors and +// retrying the remove after a exp timeout +func remove(path string) error { + var err error + delay := 10 * time.Millisecond + for i := 0; i < 5; i++ { + if i != 0 { + time.Sleep(delay) + delay *= 2 + } + if err = os.RemoveAll(path); err == nil { + return nil + } + } + return errors.Wrapf(err, "cgroups: unable to remove path %q", path) +} + +// parseCgroupProcsFile parses /sys/fs/cgroup/$GROUPPATH/cgroup.procs +func parseCgroupProcsFile(path string) ([]uint64, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + var ( + out []uint64 + s = bufio.NewScanner(f) + ) + for s.Scan() { + if t := s.Text(); t != "" { + pid, err := strconv.ParseUint(t, 10, 0) + if err != nil { + return nil, err + } + out = append(out, pid) + } + } + if err := s.Err(); err != nil { + return nil, err + } + return out, nil +} + +func parseKV(raw string) (string, interface{}, error) { + parts := strings.Fields(raw) + switch len(parts) { + case 2: + v, err := parseUint(parts[1], 10, 64) + if err != nil { + // if we cannot parse as a uint, parse as a string + return parts[0], parts[1], nil + } + return parts[0], v, nil + default: + return "", 0, ErrInvalidFormat + } +} + +func parseUint(s string, base, bitSize int) (uint64, error) { + v, err := strconv.ParseUint(s, base, bitSize) + if err != nil { + intValue, intErr := strconv.ParseInt(s, base, bitSize) + // 1. Handle negative values greater than MinInt64 (and) + // 2. Handle negative values lesser than MinInt64 + if intErr == nil && intValue < 0 { + return 0, nil + } else if intErr != nil && + intErr.(*strconv.NumError).Err == strconv.ErrRange && + intValue < 0 { + return 0, nil + } + return 0, err + } + return v, nil +} + +// parseCgroupFile parses /proc/PID/cgroup file and return string +func parseCgroupFile(path string) (string, error) { + f, err := os.Open(path) + if err != nil { + return "", err + } + defer f.Close() + return parseCgroupFromReader(f) +} + +func parseCgroupFromReader(r io.Reader) (string, error) { + var ( + s = bufio.NewScanner(r) + ) + for s.Scan() { + var ( + text = s.Text() + parts = strings.SplitN(text, ":", 3) + ) + if len(parts) < 3 { + return "", fmt.Errorf("invalid cgroup entry: %q", text) + } + // text is like "0::/user.slice/user-1001.slice/session-1.scope" + if parts[0] == "0" && parts[1] == "" { + return parts[2], nil + } + } + if err := s.Err(); err != nil { + return "", err + } + return "", fmt.Errorf("cgroup path not found") +} + +// ToResources converts the oci LinuxResources struct into a +// v2 Resources type for use with this package. +// +// converting cgroups configuration from v1 to v2 +// ref: https://github.com/containers/crun/blob/master/crun.1.md#cgroup-v2 +func ToResources(spec *specs.LinuxResources) *Resources { + var resources Resources + if cpu := spec.CPU; cpu != nil { + resources.CPU = &CPU{ + Cpus: cpu.Cpus, + Mems: cpu.Mems, + } + if shares := cpu.Shares; shares != nil { + convertedWeight := 1 + ((*shares-2)*9999)/262142 + resources.CPU.Weight = &convertedWeight + } + if period := cpu.Period; period != nil { + resources.CPU.Max = NewCPUMax(cpu.Quota, period) + } + } + if mem := spec.Memory; mem != nil { + resources.Memory = &Memory{} + if swap := mem.Swap; swap != nil { + resources.Memory.Swap = swap + } + if l := mem.Limit; l != nil { + resources.Memory.Max = l + } + if l := mem.Reservation; l != nil { + resources.Memory.Low = l + } + } + if hugetlbs := spec.HugepageLimits; hugetlbs != nil { + hugeTlbUsage := HugeTlb{} + for _, hugetlb := range hugetlbs { + hugeTlbUsage = append(hugeTlbUsage, HugeTlbEntry{ + HugePageSize: hugetlb.Pagesize, + Limit: hugetlb.Limit, + }) + } + resources.HugeTlb = &hugeTlbUsage + } + if pids := spec.Pids; pids != nil { + resources.Pids = &Pids{ + Max: pids.Limit, + } + } + if i := spec.BlockIO; i != nil { + resources.IO = &IO{} + if i.Weight != nil { + resources.IO.BFQ.Weight = 1 + (*i.Weight-10)*9999/990 + } + for t, devices := range map[IOType][]specs.LinuxThrottleDevice{ + ReadBPS: i.ThrottleReadBpsDevice, + WriteBPS: i.ThrottleWriteBpsDevice, + ReadIOPS: i.ThrottleReadIOPSDevice, + WriteIOPS: i.ThrottleWriteIOPSDevice, + } { + for _, d := range devices { + resources.IO.Max = append(resources.IO.Max, Entry{ + Type: t, + Major: d.Major, + Minor: d.Minor, + Rate: d.Rate, + }) + } + } + } + if i := spec.Rdma; i != nil { + resources.RDMA = &RDMA{} + for device, value := range spec.Rdma { + if device != "" && (value.HcaHandles != nil || value.HcaObjects != nil) { + resources.RDMA.Limit = append(resources.RDMA.Limit, RDMAEntry{ + Device: device, + HcaHandles: *value.HcaHandles, + HcaObjects: *value.HcaObjects, + }) + } + } + } + + return &resources +} + +// Gets uint64 parsed content of single value cgroup stat file +func getStatFileContentUint64(filePath string) uint64 { + contents, err := ioutil.ReadFile(filePath) + if err != nil { + return 0 + } + trimmed := strings.TrimSpace(string(contents)) + if trimmed == "max" { + return math.MaxUint64 + } + + res, err := parseUint(trimmed, 10, 64) + if err != nil { + logrus.Errorf("unable to parse %q as a uint from Cgroup file %q", string(contents), filePath) + return res + } + + return res +} + +func readIoStats(path string) []*stats.IOEntry { + // more details on the io.stat file format: https://www.kernel.org/doc/Documentation/cgroup-v2.txt + var usage []*stats.IOEntry + fpath := filepath.Join(path, "io.stat") + currentData, err := ioutil.ReadFile(fpath) + if err != nil { + return usage + } + entries := strings.Split(string(currentData), "\n") + + for _, entry := range entries { + parts := strings.Split(entry, " ") + if len(parts) < 2 { + continue + } + majmin := strings.Split(parts[0], ":") + if len(majmin) != 2 { + continue + } + major, err := strconv.ParseUint(majmin[0], 10, 0) + if err != nil { + return usage + } + minor, err := strconv.ParseUint(majmin[1], 10, 0) + if err != nil { + return usage + } + parts = parts[1:] + ioEntry := stats.IOEntry{ + Major: major, + Minor: minor, + } + for _, s := range parts { + keyPairValue := strings.Split(s, "=") + if len(keyPairValue) != 2 { + continue + } + v, err := strconv.ParseUint(keyPairValue[1], 10, 0) + if err != nil { + continue + } + switch keyPairValue[0] { + case "rbytes": + ioEntry.Rbytes = v + case "wbytes": + ioEntry.Wbytes = v + case "rios": + ioEntry.Rios = v + case "wios": + ioEntry.Wios = v + } + } + usage = append(usage, &ioEntry) + } + return usage +} + +func rdmaStats(filepath string) []*stats.RdmaEntry { + currentData, err := ioutil.ReadFile(filepath) + if err != nil { + return []*stats.RdmaEntry{} + } + return toRdmaEntry(strings.Split(string(currentData), "\n")) +} + +func parseRdmaKV(raw string, entry *stats.RdmaEntry) { + var value uint64 + var err error + + parts := strings.Split(raw, "=") + switch len(parts) { + case 2: + if parts[1] == "max" { + value = math.MaxUint32 + } else { + value, err = parseUint(parts[1], 10, 32) + if err != nil { + return + } + } + if parts[0] == "hca_handle" { + entry.HcaHandles = uint32(value) + } else if parts[0] == "hca_object" { + entry.HcaObjects = uint32(value) + } + } +} + +func toRdmaEntry(strEntries []string) []*stats.RdmaEntry { + var rdmaEntries []*stats.RdmaEntry + for i := range strEntries { + parts := strings.Fields(strEntries[i]) + switch len(parts) { + case 3: + entry := new(stats.RdmaEntry) + entry.Device = parts[0] + parseRdmaKV(parts[1], entry) + parseRdmaKV(parts[2], entry) + + rdmaEntries = append(rdmaEntries, entry) + default: + continue + } + } + return rdmaEntries +} + +// isUnitExists returns true if the error is that a systemd unit already exists. +func isUnitExists(err error) bool { + if err != nil { + if dbusError, ok := err.(dbus.Error); ok { + return strings.Contains(dbusError.Name, "org.freedesktop.systemd1.UnitExists") + } + } + return false +} + +func systemdUnitFromPath(path string) string { + _, unit := filepath.Split(path) + return unit +} + +func readHugeTlbStats(path string) []*stats.HugeTlbStat { + var usage = []*stats.HugeTlbStat{} + var keyUsage = make(map[string]*stats.HugeTlbStat) + f, err := os.Open(path) + if err != nil { + return usage + } + files, err := f.Readdir(-1) + f.Close() + if err != nil { + return usage + } + + for _, file := range files { + if strings.Contains(file.Name(), "hugetlb") && + (strings.HasSuffix(file.Name(), "max") || strings.HasSuffix(file.Name(), "current")) { + var hugeTlb *stats.HugeTlbStat + var ok bool + fileName := strings.Split(file.Name(), ".") + pageSize := fileName[1] + if hugeTlb, ok = keyUsage[pageSize]; !ok { + hugeTlb = &stats.HugeTlbStat{} + } + hugeTlb.Pagesize = pageSize + out, err := ioutil.ReadFile(filepath.Join(path, file.Name())) + if err != nil { + continue + } + var value uint64 + stringVal := strings.TrimSpace(string(out)) + if stringVal == "max" { + value = math.MaxUint64 + } else { + value, err = strconv.ParseUint(stringVal, 10, 64) + } + if err != nil { + continue + } + switch fileName[2] { + case "max": + hugeTlb.Max = value + case "current": + hugeTlb.Current = value + } + keyUsage[pageSize] = hugeTlb + } + } + for _, entry := range keyUsage { + usage = append(usage, entry) + } + return usage +} diff --git a/vendor/github.com/containerd/console/LICENSE b/vendor/github.com/containerd/console/LICENSE index 261eeb9e9f8b2..584149b6ee28c 100644 --- a/vendor/github.com/containerd/console/LICENSE +++ b/vendor/github.com/containerd/console/LICENSE @@ -1,6 +1,7 @@ + Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -175,24 +176,13 @@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] + Copyright The containerd Authors 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 + https://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, diff --git a/vendor/github.com/containerd/console/README.md b/vendor/github.com/containerd/console/README.md index 4c56d9d134a9b..580b461a73dbb 100644 --- a/vendor/github.com/containerd/console/README.md +++ b/vendor/github.com/containerd/console/README.md @@ -1,6 +1,8 @@ # console -[![Build Status](https://travis-ci.org/containerd/console.svg?branch=master)](https://travis-ci.org/containerd/console) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/containerd/console)](https://pkg.go.dev/github.com/containerd/console) +[![Build Status](https://github.com/containerd/console/workflows/CI/badge.svg)](https://github.com/containerd/console/actions?query=workflow%3ACI) +[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/console)](https://goreportcard.com/report/github.com/containerd/console) Golang package for dealing with consoles. Light on deps and a simple API. @@ -15,3 +17,13 @@ if err := current.SetRaw(); err != nil { ws, err := current.Size() current.Resize(ws) ``` + +## Project details + +console is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/console/console.go b/vendor/github.com/containerd/console/console.go index c187a9b412e42..f989d28a41cd4 100644 --- a/vendor/github.com/containerd/console/console.go +++ b/vendor/github.com/containerd/console/console.go @@ -24,10 +24,17 @@ import ( var ErrNotAConsole = errors.New("provided file is not a console") +type File interface { + io.ReadWriteCloser + + // Fd returns its file descriptor + Fd() uintptr + // Name returns its file name + Name() string +} + type Console interface { - io.Reader - io.Writer - io.Closer + File // Resize resizes the console to the provided window size Resize(WinSize) error @@ -42,10 +49,6 @@ type Console interface { Reset() error // Size returns the window size of the console Size() (WinSize, error) - // Fd returns the console's file descriptor - Fd() uintptr - // Name returns the console's file name - Name() string } // WinSize specifies the window size of the console @@ -58,19 +61,25 @@ type WinSize struct { y uint16 } -// Current returns the current processes console -func Current() Console { - c, err := ConsoleFromFile(os.Stdin) - if err != nil { - // stdin should always be a console for the design - // of this function - panic(err) +// Current returns the current process' console +func Current() (c Console) { + var err error + // Usually all three streams (stdin, stdout, and stderr) + // are open to the same console, but some might be redirected, + // so try all three. + for _, s := range []*os.File{os.Stderr, os.Stdout, os.Stdin} { + if c, err = ConsoleFromFile(s); err == nil { + return c + } } - return c + // One of the std streams should always be a console + // for the design of this function. + panic(err) } // ConsoleFromFile returns a console using the provided file -func ConsoleFromFile(f *os.File) (Console, error) { +// nolint:golint +func ConsoleFromFile(f File) (Console, error) { if err := checkConsole(f); err != nil { return nil, err } diff --git a/vendor/github.com/containerd/console/console_linux.go b/vendor/github.com/containerd/console/console_linux.go index 42274e100e687..c1c839ee3ae02 100644 --- a/vendor/github.com/containerd/console/console_linux.go +++ b/vendor/github.com/containerd/console/console_linux.go @@ -58,6 +58,7 @@ type Epoller struct { efd int mu sync.Mutex fdMapping map[int]*EpollConsole + closeOnce sync.Once } // NewEpoller returns an instance of epoller with a valid epoll fd. @@ -151,7 +152,11 @@ func (e *Epoller) getConsole(sysfd int) *EpollConsole { // Close closes the epoll fd func (e *Epoller) Close() error { - return unix.Close(e.efd) + closeErr := os.ErrClosed // default to "file already closed" + e.closeOnce.Do(func() { + closeErr = unix.Close(e.efd) + }) + return closeErr } // EpollConsole acts like a console but registers its file descriptor with an diff --git a/vendor/github.com/containerd/console/console_unix.go b/vendor/github.com/containerd/console/console_unix.go index a4a8d1267b9f5..a08117695e320 100644 --- a/vendor/github.com/containerd/console/console_unix.go +++ b/vendor/github.com/containerd/console/console_unix.go @@ -1,4 +1,4 @@ -// +build darwin freebsd linux openbsd solaris +// +build darwin freebsd linux netbsd openbsd solaris /* Copyright The containerd Authors. @@ -19,8 +19,6 @@ package console import ( - "os" - "golang.org/x/sys/unix" ) @@ -28,7 +26,7 @@ import ( // The master is returned as the first console and a string // with the path to the pty slave is returned as the second func NewPty() (Console, string, error) { - f, err := os.OpenFile("/dev/ptmx", unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0) + f, err := openpt() if err != nil { return nil, "", err } @@ -47,7 +45,7 @@ func NewPty() (Console, string, error) { } type master struct { - f *os.File + f File original *unix.Termios } @@ -122,7 +120,7 @@ func (m *master) Name() string { } // checkConsole checks if the provided file is a console -func checkConsole(f *os.File) error { +func checkConsole(f File) error { var termios unix.Termios if tcget(f.Fd(), &termios) != nil { return ErrNotAConsole @@ -130,7 +128,7 @@ func checkConsole(f *os.File) error { return nil } -func newMaster(f *os.File) (Console, error) { +func newMaster(f File) (Console, error) { m := &master{ f: f, } diff --git a/vendor/github.com/containerd/console/console_windows.go b/vendor/github.com/containerd/console/console_windows.go index 62dbe1c033594..129a92883ddb6 100644 --- a/vendor/github.com/containerd/console/console_windows.go +++ b/vendor/github.com/containerd/console/console_windows.go @@ -198,7 +198,7 @@ func makeInputRaw(fd windows.Handle, mode uint32) error { return nil } -func checkConsole(f *os.File) error { +func checkConsole(f File) error { var mode uint32 if err := windows.GetConsoleMode(windows.Handle(f.Fd()), &mode); err != nil { return err @@ -206,7 +206,7 @@ func checkConsole(f *os.File) error { return nil } -func newMaster(f *os.File) (Console, error) { +func newMaster(f File) (Console, error) { if f != os.Stdin && f != os.Stdout && f != os.Stderr { return nil, errors.New("creating a console from a file is not supported on windows") } diff --git a/vendor/github.com/containerd/console/go.mod b/vendor/github.com/containerd/console/go.mod new file mode 100644 index 0000000000000..7fca0a9a3ac19 --- /dev/null +++ b/vendor/github.com/containerd/console/go.mod @@ -0,0 +1,8 @@ +module github.com/containerd/console + +go 1.13 + +require ( + github.com/pkg/errors v0.9.1 + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c +) diff --git a/vendor/github.com/containerd/console/pty_freebsd_cgo.go b/vendor/github.com/containerd/console/pty_freebsd_cgo.go new file mode 100644 index 0000000000000..cbd3cd7ea43d8 --- /dev/null +++ b/vendor/github.com/containerd/console/pty_freebsd_cgo.go @@ -0,0 +1,45 @@ +// +build freebsd,cgo + +/* + Copyright The containerd Authors. + + 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. +*/ + +package console + +import ( + "fmt" + "os" +) + +/* +#include +#include +#include +*/ +import "C" + +// openpt allocates a new pseudo-terminal and establishes a connection with its +// control device. +func openpt() (*os.File, error) { + fd, err := C.posix_openpt(C.O_RDWR) + if err != nil { + return nil, fmt.Errorf("posix_openpt: %w", err) + } + if _, err := C.grantpt(fd); err != nil { + C.close(fd) + return nil, fmt.Errorf("grantpt: %w", err) + } + return os.NewFile(uintptr(fd), ""), nil +} diff --git a/vendor/github.com/containerd/console/pty_freebsd_nocgo.go b/vendor/github.com/containerd/console/pty_freebsd_nocgo.go new file mode 100644 index 0000000000000..b5e43181d4f36 --- /dev/null +++ b/vendor/github.com/containerd/console/pty_freebsd_nocgo.go @@ -0,0 +1,36 @@ +// +build freebsd,!cgo + +/* + Copyright The containerd Authors. + + 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. +*/ + +package console + +import ( + "os" +) + +// +// Implementing the functions below requires cgo support. Non-cgo stubs +// versions are defined below to enable cross-compilation of source code +// that depends on these functions, but the resultant cross-compiled +// binaries cannot actually be used. If the stub function(s) below are +// actually invoked they will display an error message and cause the +// calling process to exit. +// + +func openpt() (*os.File, error) { + panic("openpt() support requires cgo.") +} diff --git a/vendor/github.com/containerd/console/pty_unix.go b/vendor/github.com/containerd/console/pty_unix.go new file mode 100644 index 0000000000000..d5a6bd8ca2e80 --- /dev/null +++ b/vendor/github.com/containerd/console/pty_unix.go @@ -0,0 +1,30 @@ +// +build darwin linux netbsd openbsd solaris + +/* + Copyright The containerd Authors. + + 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. +*/ + +package console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// openpt allocates a new pseudo-terminal by opening the /dev/ptmx device +func openpt() (*os.File, error) { + return os.OpenFile("/dev/ptmx", unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0) +} diff --git a/vendor/github.com/containerd/console/tc_darwin.go b/vendor/github.com/containerd/console/tc_darwin.go index b0128abb0c62b..787154580f6c2 100644 --- a/vendor/github.com/containerd/console/tc_darwin.go +++ b/vendor/github.com/containerd/console/tc_darwin.go @@ -19,7 +19,6 @@ package console import ( "fmt" "os" - "unsafe" "golang.org/x/sys/unix" ) @@ -29,18 +28,10 @@ const ( cmdTcSet = unix.TIOCSETA ) -func ioctl(fd, flag, data uintptr) error { - if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, flag, data); err != 0 { - return err - } - return nil -} - // unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. // unlockpt should be called before opening the slave side of a pty. func unlockpt(f *os.File) error { - var u int32 - return ioctl(f.Fd(), unix.TIOCPTYUNLK, uintptr(unsafe.Pointer(&u))) + return unix.IoctlSetPointerInt(int(f.Fd()), unix.TIOCPTYUNLK, 0) } // ptsname retrieves the name of the first available pts for the given master. diff --git a/vendor/github.com/containerd/console/tc_freebsd.go b/vendor/github.com/containerd/console/tc_freebsd.go deleted file mode 100644 index 04583a6156989..0000000000000 --- a/vendor/github.com/containerd/console/tc_freebsd.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package console - -import ( - "fmt" - "os" - - "golang.org/x/sys/unix" -) - -const ( - cmdTcGet = unix.TIOCGETA - cmdTcSet = unix.TIOCSETA -) - -// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. -// unlockpt should be called before opening the slave side of a pty. -// This does not exist on FreeBSD, it does not allocate controlling terminals on open -func unlockpt(f *os.File) error { - return nil -} - -// ptsname retrieves the name of the first available pts for the given master. -func ptsname(f *os.File) (string, error) { - n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN) - if err != nil { - return "", err - } - return fmt.Sprintf("/dev/pts/%d", n), nil -} diff --git a/vendor/github.com/containerd/console/tc_freebsd_cgo.go b/vendor/github.com/containerd/console/tc_freebsd_cgo.go new file mode 100644 index 0000000000000..0f3d27273094c --- /dev/null +++ b/vendor/github.com/containerd/console/tc_freebsd_cgo.go @@ -0,0 +1,57 @@ +// +build freebsd,cgo + +/* + Copyright The containerd Authors. + + 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. +*/ + +package console + +import ( + "fmt" + "os" + + "golang.org/x/sys/unix" +) + +/* +#include +#include +*/ +import "C" + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + fd := C.int(f.Fd()) + if _, err := C.unlockpt(fd); err != nil { + C.close(fd) + return fmt.Errorf("unlockpt: %w", err) + } + return nil +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN) + if err != nil { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", n), nil +} diff --git a/vendor/github.com/containerd/console/tc_freebsd_nocgo.go b/vendor/github.com/containerd/console/tc_freebsd_nocgo.go new file mode 100644 index 0000000000000..087fc158a1696 --- /dev/null +++ b/vendor/github.com/containerd/console/tc_freebsd_nocgo.go @@ -0,0 +1,55 @@ +// +build freebsd,!cgo + +/* + Copyright The containerd Authors. + + 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. +*/ + +package console + +import ( + "fmt" + "os" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +// +// Implementing the functions below requires cgo support. Non-cgo stubs +// versions are defined below to enable cross-compilation of source code +// that depends on these functions, but the resultant cross-compiled +// binaries cannot actually be used. If the stub function(s) below are +// actually invoked they will display an error message and cause the +// calling process to exit. +// + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + panic("unlockpt() support requires cgo.") +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN) + if err != nil { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", n), nil +} diff --git a/vendor/github.com/containerd/console/tc_linux.go b/vendor/github.com/containerd/console/tc_linux.go index 1bdd68e6d55b6..7d552ea4ba137 100644 --- a/vendor/github.com/containerd/console/tc_linux.go +++ b/vendor/github.com/containerd/console/tc_linux.go @@ -33,6 +33,7 @@ const ( // unlockpt should be called before opening the slave side of a pty. func unlockpt(f *os.File) error { var u int32 + // XXX do not use unix.IoctlSetPointerInt here, see commit dbd69c59b81. if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))); err != 0 { return err } @@ -42,6 +43,7 @@ func unlockpt(f *os.File) error { // ptsname retrieves the name of the first available pts for the given master. func ptsname(f *os.File) (string, error) { var u uint32 + // XXX do not use unix.IoctlGetInt here, see commit dbd69c59b81. if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCGPTN, uintptr(unsafe.Pointer(&u))); err != 0 { return "", err } diff --git a/vendor/github.com/containerd/console/tc_netbsd.go b/vendor/github.com/containerd/console/tc_netbsd.go new file mode 100644 index 0000000000000..71227aefdffdb --- /dev/null +++ b/vendor/github.com/containerd/console/tc_netbsd.go @@ -0,0 +1,45 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package console + +import ( + "bytes" + "os" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +// This does not exist on NetBSD, it does not allocate controlling terminals on open +func unlockpt(f *os.File) error { + return nil +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + ptm, err := unix.IoctlGetPtmget(int(f.Fd()), unix.TIOCPTSNAME) + if err != nil { + return "", err + } + return string(ptm.Sn[:bytes.IndexByte(ptm.Sn[:], 0)]), nil +} diff --git a/vendor/github.com/containerd/console/tc_unix.go b/vendor/github.com/containerd/console/tc_unix.go index 7ae773c53eaaa..5cd4c550ce884 100644 --- a/vendor/github.com/containerd/console/tc_unix.go +++ b/vendor/github.com/containerd/console/tc_unix.go @@ -1,4 +1,4 @@ -// +build darwin freebsd linux openbsd solaris +// +build darwin freebsd linux netbsd openbsd solaris /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/containerd/README.md b/vendor/github.com/containerd/containerd/README.md index c4d9dffdbdc58..1ecec1ff04b16 100644 --- a/vendor/github.com/containerd/containerd/README.md +++ b/vendor/github.com/containerd/containerd/README.md @@ -1,18 +1,32 @@ -![banner](https://github.com/containerd/containerd.io/blob/master/static/img/containerd-dark.png?raw=true) +![containerd banner](https://raw.githubusercontent.com/cncf/artwork/master/projects/containerd/horizontal/color/containerd-horizontal-color.png) -[![GoDoc](https://godoc.org/github.com/containerd/containerd?status.svg)](https://godoc.org/github.com/containerd/containerd) -[![Build Status](https://travis-ci.org/containerd/containerd.svg?branch=master)](https://travis-ci.org/containerd/containerd) -[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/containerd/containerd?branch=master&svg=true)](https://ci.appveyor.com/project/mlaventure/containerd-3g73f?branch=master) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/containerd/containerd)](https://pkg.go.dev/github.com/containerd/containerd) +[![Build Status](https://github.com/containerd/containerd/workflows/CI/badge.svg)](https://github.com/containerd/containerd/actions?query=workflow%3ACI) +[![Nightlies](https://github.com/containerd/containerd/workflows/Nightly/badge.svg)](https://github.com/containerd/containerd/actions?query=workflow%3ANightly) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd?ref=badge_shield) [![Go Report Card](https://goreportcard.com/badge/github.com/containerd/containerd)](https://goreportcard.com/report/github.com/containerd/containerd) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1271/badge)](https://bestpractices.coreinfrastructure.org/projects/1271) containerd is an industry-standard container runtime with an emphasis on simplicity, robustness and portability. It is available as a daemon for Linux and Windows, which can manage the complete container lifecycle of its host system: image transfer and storage, container execution and supervision, low-level storage and network attachments, etc. +containerd is a member of CNCF with ['graduated'](https://landscape.cncf.io/selected=containerd) status. + containerd is designed to be embedded into a larger system, rather than being used directly by developers or end-users. ![architecture](design/architecture.png) +## Now Recruiting + +We are a large inclusive OSS project that is welcoming help of any kind shape or form: +* Documentation help is needed to make the product easier to consume and extend. +* We need OSS community outreach / organizing help to get the word out; manage +and create messaging and educational content; and to help with social media, community forums/groups, and google groups. +* We are actively inviting new [security advisors](https://github.com/containerd/project/blob/master/GOVERNANCE.md#security-advisors) to join the team. +* New sub-projects are being created, core and non-core that could use additional development help. +* Each of the [containerd projects](https://github.com/containerd) has a list of issues currently being worked on or that need help resolving. + - If the issue has not already been assigned to someone, or has not made recent progress and you are interested, please inquire. + - If you are interested in starting with a smaller / beginner level issue, look for issues with an `exp/beginner` tag, for example [containerd/containerd beginner issues.](https://github.com/containerd/containerd/issues?q=is%3Aissue+is%3Aopen+label%3Aexp%2Fbeginner) + ## Getting Started See our documentation on [containerd.io](https://containerd.io): @@ -24,12 +38,19 @@ See how to build containerd from source at [BUILDING](BUILDING.md). If you are interested in trying out containerd see our example at [Getting Started](docs/getting-started.md). +## Nightly builds + +There are nightly builds available for download [here](https://github.com/containerd/containerd/actions?query=workflow%3ANightly). +Binaries are generated from `master` branch every night for `Linux` and `Windows`. + +Please be aware: nightly builds might have critical bugs, it's not recommended for use in production and no support provided. ## Runtime Requirements Runtime requirements for containerd are very minimal. Most interactions with the Linux and Windows container feature sets are handled via [runc](https://github.com/opencontainers/runc) and/or -OS-specific libraries (e.g. [hcsshim](https://github.com/Microsoft/hcsshim) for Microsoft). The current required version of `runc` is always listed in [RUNC.md](/RUNC.md). +OS-specific libraries (e.g. [hcsshim](https://github.com/Microsoft/hcsshim) for Microsoft). +The current required version of `runc` is described in [RUNC.md](docs/RUNC.md). There are specific features used by containerd core code and snapshotters that will require a minimum kernel @@ -119,7 +140,7 @@ redis, err := client.NewContainer(context, "redis-master", containerd.WithNewSpe ### Root Filesystems -containerd allows you to use overlay or snapshot filesystems with your containers. It comes with builtin support for overlayfs and btrfs. +containerd allows you to use overlay or snapshot filesystems with your containers. It comes with built in support for overlayfs and btrfs. ```go // pull an image and unpack it into the configured snapshotter @@ -147,10 +168,10 @@ Taking a container object and turning it into a runnable process on a system is ```go // create a new task -task, err := redis.NewTask(context, cio.Stdio) +task, err := redis.NewTask(context, cio.NewCreator(cio.WithStdio)) defer task.Delete(context) -// the task is now running and has a pid that can be use to setup networking +// the task is now running and has a pid that can be used to setup networking // or other runtime settings outside of containerd pid := task.Pid() @@ -163,7 +184,7 @@ status, err := task.Wait(context) ### Checkpoint and Restore -If you have [criu](https://criu.org/Main_Page) installed on your machine you can checkpoint and restore containers and their tasks. This allow you to clone and/or live migrate containers to other machines. +If you have [criu](https://criu.org/Main_Page) installed on your machine you can checkpoint and restore containers and their tasks. This allows you to clone and/or live migrate containers to other machines. ```go // checkpoint the task then push it to a registry @@ -172,14 +193,12 @@ checkpoint, err := task.Checkpoint(context) err := client.Push(context, "myregistry/checkpoints/redis:master", checkpoint) // on a new machine pull the checkpoint and restore the redis container -image, err := client.Pull(context, "myregistry/checkpoints/redis:master") - -checkpoint := image.Target() +checkpoint, err := client.Pull(context, "myregistry/checkpoints/redis:master") -redis, err = client.NewContainer(context, "redis-master", containerd.WithCheckpoint(checkpoint, "redis-rootfs")) +redis, err = client.NewContainer(context, "redis-master", containerd.WithNewSnapshot("redis-rootfs", checkpoint)) defer container.Delete(context) -task, err = redis.NewTask(context, cio.Stdio, containerd.WithTaskCheckpoint(checkpoint)) +task, err = redis.NewTask(context, cio.NewCreator(cio.WithStdio), containerd.WithTaskCheckpoint(checkpoint)) defer task.Delete(context) err := task.Start(context) @@ -205,26 +224,83 @@ effect. address = "/var/run/mysnapshotter.sock" ``` -See [PLUGINS.md](PLUGINS.md) for how to create plugins +See [PLUGINS.md](/docs/PLUGINS.md) for how to create plugins ### Releases and API Stability Please see [RELEASES.md](RELEASES.md) for details on versioning and stability of containerd components. -### Development reports. +Downloadable 64-bit Intel/AMD binaries of all official releases are available on +our [releases page](https://github.com/containerd/containerd/releases). + +For other architectures and distribution support, you will find that many +Linux distributions package their own containerd and provide it across several +architectures, such as [Canonical's Ubuntu packaging](https://launchpad.net/ubuntu/bionic/+package/containerd). + +#### Enabling command auto-completion + +Starting with containerd 1.4, the urfave client feature for auto-creation of bash and zsh +autocompletion data is enabled. To use the autocomplete feature in a bash shell for example, source +the autocomplete/ctr file in your `.bashrc`, or manually like: + +``` +$ source ./contrib/autocomplete/ctr +``` + +#### Distribution of `ctr` autocomplete for bash and zsh + +For bash, copy the `contrib/autocomplete/ctr` script into +`/etc/bash_completion.d/` and rename it to `ctr`. The `zsh_autocomplete` +file is also available and can be used similarly for zsh users. + +Provide documentation to users to `source` this file into their shell if +you don't place the autocomplete file in a location where it is automatically +loaded for the user's shell environment. + +### CRI -Weekly summary on the progress and what is being worked on. -https://github.com/containerd/containerd/tree/master/reports +`cri` is a [containerd](https://containerd.io/) plugin implementation of the Kubernetes [container runtime interface (CRI)](https://github.com/kubernetes/cri-api/blob/master/pkg/apis/runtime/v1alpha2/api.proto). With it, you are able to use containerd as the container runtime for a Kubernetes cluster. + +![cri](./docs/cri/cri.png) + +#### CRI Status + +`cri` is a native plugin of containerd. Since containerd 1.1, the cri plugin is built into the release binaries and enabled by default. + +> **Note:** As of containerd 1.5, the `cri` plugin is merged into the containerd/containerd repo. For example, the source code previously stored under [`containerd/cri/pkg`](https://github.com/containerd/cri/tree/release/1.4/pkg) +was moved to [`containerd/containerd/pkg/cri` package](https://github.com/containerd/containerd/tree/master/pkg/cri). + +The `cri` plugin has reached GA status, representing that it is: +* Feature complete +* Works with Kubernetes 1.10 and above +* Passes all [CRI validation tests](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/cri-validation.md). +* Passes all [node e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/e2e-node-tests.md). +* Passes all [e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-testing/e2e-tests.md). + +See results on the containerd k8s [test dashboard](https://k8s-testgrid.appspot.com/sig-node-containerd) + +#### Validating Your `cri` Setup +A Kubernetes incubator project, [cri-tools](https://github.com/kubernetes-sigs/cri-tools), includes programs for exercising CRI implementations. More importantly, cri-tools includes the program `critest` which is used for running [CRI Validation Testing](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/cri-validation.md). + +#### CRI Guides +* [Installing with Ansible and Kubeadm](contrib/ansible/README.md) +* [For Non-Ansible Users, Preforming a Custom Installation Using the Release Tarball and Kubeadm](docs/cri/installation.md) +* [CRI Plugin Testing Guide](./docs/cri/testing.md) +* [Debugging Pods, Containers, and Images with `crictl`](./docs/cri/crictl.md) +* [Configuring `cri` Plugins](./docs/cri/config.md) +* [Configuring containerd](https://github.com/containerd/containerd/blob/master/docs/man/containerd-config.8.md) ### Communication For async communication and long running discussions please use issues and pull requests on the github repo. This will be the best place to discuss design and implementation. -For sync communication we have a community slack with a #containerd channel that everyone is welcome to join and chat about development. +For sync communication catch us in the `#containerd` and `#containerd-dev` slack channels on Cloud Native Computing Foundation's (CNCF) slack - `cloud-native.slack.com`. Everyone is welcome to join and chat. [Get Invite to CNCF slack.](https://slack.cncf.io) -**Slack:** https://join.slack.com/t/dockercommunity/shared_invite/enQtNDM4NjAwNDMyOTUwLWZlMDZmYWRjZjk4Zjc5ZGQ5NWZkOWI1Yjk2NGE3ZWVlYjYxM2VhYjczOWIyZDFhZTE3NTUwZWQzMjhmNGYyZTg +### Security audit + +A third party security audit was performed by Cure53 in 4Q2018; the [full report](docs/SECURITY_AUDIT.pdf) is available in our docs/ directory. ### Reporting security issues @@ -232,7 +308,25 @@ __If you are reporting a security issue, please reach out discreetly at security ## Licenses -The containerd codebase is released under the [Apache 2.0 license](LICENSE.code). +The containerd codebase is released under the [Apache 2.0 license](LICENSE). The README.md file, and files in the "docs" folder are licensed under the Creative Commons Attribution 4.0 International License. You may obtain a copy of the license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/. + +## Project details + +**containerd** is the primary open source project within the broader containerd GitHub repository. +However, all projects within the repo have common maintainership, governance, and contributing +guidelines which are stored in a `project` repository commonly for all containerd projects. + +Please find all these core project documents, including the: + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. + +## Adoption + +Interested to see who is using containerd? Are you using containerd in a project? +Please add yourself via pull request to our [ADOPTERS.md](./ADOPTERS.md) file. diff --git a/vendor/github.com/containerd/containerd/api/events/container.pb.go b/vendor/github.com/containerd/containerd/api/events/container.pb.go index c89d97f3ee9f7..fe002e0736c26 100644 --- a/vendor/github.com/containerd/containerd/api/events/container.pb.go +++ b/vendor/github.com/containerd/containerd/api/events/container.pb.go @@ -1,60 +1,20 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/events/container.proto -/* - Package events is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/events/container.proto - github.com/containerd/containerd/api/events/content.proto - github.com/containerd/containerd/api/events/image.proto - github.com/containerd/containerd/api/events/namespace.proto - github.com/containerd/containerd/api/events/snapshot.proto - github.com/containerd/containerd/api/events/task.proto - - It has these top-level messages: - ContainerCreate - ContainerUpdate - ContainerDelete - ContentDelete - ImageCreate - ImageUpdate - ImageDelete - NamespaceCreate - NamespaceUpdate - NamespaceDelete - SnapshotPrepare - SnapshotCommit - SnapshotRemove - TaskCreate - TaskStart - TaskDelete - TaskIO - TaskExit - TaskOOM - TaskExecAdded - TaskExecStarted - TaskPaused - TaskResumed - TaskCheckpointed -*/ package events -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/gogo/protobuf/types" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" - -import typeurl "github.com/containerd/typeurl" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + fmt "fmt" + github_com_containerd_typeurl "github.com/containerd/typeurl" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + types "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -65,55 +25,212 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type ContainerCreate struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Image string `protobuf:"bytes,2,opt,name=image,proto3" json:"image,omitempty"` - Runtime *ContainerCreate_Runtime `protobuf:"bytes,3,opt,name=runtime" json:"runtime,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Image string `protobuf:"bytes,2,opt,name=image,proto3" json:"image,omitempty"` + Runtime *ContainerCreate_Runtime `protobuf:"bytes,3,opt,name=runtime,proto3" json:"runtime,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ContainerCreate) Reset() { *m = ContainerCreate{} } -func (*ContainerCreate) ProtoMessage() {} -func (*ContainerCreate) Descriptor() ([]byte, []int) { return fileDescriptorContainer, []int{0} } +func (m *ContainerCreate) Reset() { *m = ContainerCreate{} } +func (*ContainerCreate) ProtoMessage() {} +func (*ContainerCreate) Descriptor() ([]byte, []int) { + return fileDescriptor_0d1f05b8626f83ea, []int{0} +} +func (m *ContainerCreate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerCreate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerCreate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerCreate) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerCreate.Merge(m, src) +} +func (m *ContainerCreate) XXX_Size() int { + return m.Size() +} +func (m *ContainerCreate) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerCreate.DiscardUnknown(m) +} + +var xxx_messageInfo_ContainerCreate proto.InternalMessageInfo type ContainerCreate_Runtime struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Options *google_protobuf.Any `protobuf:"bytes,2,opt,name=options" json:"options,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Options *types.Any `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ContainerCreate_Runtime) Reset() { *m = ContainerCreate_Runtime{} } func (*ContainerCreate_Runtime) ProtoMessage() {} func (*ContainerCreate_Runtime) Descriptor() ([]byte, []int) { - return fileDescriptorContainer, []int{0, 0} + return fileDescriptor_0d1f05b8626f83ea, []int{0, 0} +} +func (m *ContainerCreate_Runtime) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerCreate_Runtime) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerCreate_Runtime.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerCreate_Runtime) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerCreate_Runtime.Merge(m, src) +} +func (m *ContainerCreate_Runtime) XXX_Size() int { + return m.Size() } +func (m *ContainerCreate_Runtime) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerCreate_Runtime.DiscardUnknown(m) +} + +var xxx_messageInfo_ContainerCreate_Runtime proto.InternalMessageInfo type ContainerUpdate struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Image string `protobuf:"bytes,2,opt,name=image,proto3" json:"image,omitempty"` - Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - SnapshotKey string `protobuf:"bytes,4,opt,name=snapshot_key,json=snapshotKey,proto3" json:"snapshot_key,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Image string `protobuf:"bytes,2,opt,name=image,proto3" json:"image,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + SnapshotKey string `protobuf:"bytes,4,opt,name=snapshot_key,json=snapshotKey,proto3" json:"snapshot_key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContainerUpdate) Reset() { *m = ContainerUpdate{} } +func (*ContainerUpdate) ProtoMessage() {} +func (*ContainerUpdate) Descriptor() ([]byte, []int) { + return fileDescriptor_0d1f05b8626f83ea, []int{1} +} +func (m *ContainerUpdate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerUpdate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerUpdate) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerUpdate.Merge(m, src) +} +func (m *ContainerUpdate) XXX_Size() int { + return m.Size() +} +func (m *ContainerUpdate) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerUpdate.DiscardUnknown(m) } -func (m *ContainerUpdate) Reset() { *m = ContainerUpdate{} } -func (*ContainerUpdate) ProtoMessage() {} -func (*ContainerUpdate) Descriptor() ([]byte, []int) { return fileDescriptorContainer, []int{1} } +var xxx_messageInfo_ContainerUpdate proto.InternalMessageInfo type ContainerDelete struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContainerDelete) Reset() { *m = ContainerDelete{} } +func (*ContainerDelete) ProtoMessage() {} +func (*ContainerDelete) Descriptor() ([]byte, []int) { + return fileDescriptor_0d1f05b8626f83ea, []int{2} +} +func (m *ContainerDelete) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerDelete) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerDelete.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerDelete) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerDelete.Merge(m, src) +} +func (m *ContainerDelete) XXX_Size() int { + return m.Size() +} +func (m *ContainerDelete) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerDelete.DiscardUnknown(m) } -func (m *ContainerDelete) Reset() { *m = ContainerDelete{} } -func (*ContainerDelete) ProtoMessage() {} -func (*ContainerDelete) Descriptor() ([]byte, []int) { return fileDescriptorContainer, []int{2} } +var xxx_messageInfo_ContainerDelete proto.InternalMessageInfo func init() { proto.RegisterType((*ContainerCreate)(nil), "containerd.events.ContainerCreate") proto.RegisterType((*ContainerCreate_Runtime)(nil), "containerd.events.ContainerCreate.Runtime") proto.RegisterType((*ContainerUpdate)(nil), "containerd.events.ContainerUpdate") + proto.RegisterMapType((map[string]string)(nil), "containerd.events.ContainerUpdate.LabelsEntry") proto.RegisterType((*ContainerDelete)(nil), "containerd.events.ContainerDelete") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/container.proto", fileDescriptor_0d1f05b8626f83ea) +} + +var fileDescriptor_0d1f05b8626f83ea = []byte{ + // 413 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xc1, 0x0a, 0xd3, 0x30, + 0x18, 0xc7, 0x97, 0x76, 0x6e, 0x98, 0x0a, 0x6a, 0x18, 0x52, 0x7b, 0xa8, 0x73, 0xa7, 0xe9, 0x21, + 0x85, 0x7a, 0x51, 0x77, 0xd1, 0x6d, 0x0a, 0xa2, 0x82, 0x14, 0x84, 0xe1, 0x45, 0xd2, 0x35, 0xeb, + 0x82, 0x6d, 0x52, 0xda, 0x74, 0xd0, 0x9b, 0x8f, 0xe2, 0xe3, 0xec, 0xe8, 0xc1, 0x83, 0x27, 0x71, + 0x05, 0xdf, 0xc0, 0x07, 0x90, 0x26, 0xeb, 0x56, 0x14, 0x95, 0x9d, 0xfa, 0xcf, 0xd7, 0xff, 0x3f, + 0xdf, 0xf7, 0xfb, 0x08, 0x9c, 0xc5, 0x4c, 0x6e, 0xcb, 0x10, 0xaf, 0x45, 0xea, 0xad, 0x05, 0x97, + 0x84, 0x71, 0x9a, 0x47, 0x5d, 0x49, 0x32, 0xe6, 0xd1, 0x1d, 0xe5, 0xb2, 0x38, 0x57, 0x71, 0x96, + 0x0b, 0x29, 0xd0, 0xcd, 0xb3, 0x0d, 0x6b, 0x8b, 0x73, 0x3b, 0x16, 0x22, 0x4e, 0xa8, 0xa7, 0x0c, + 0x61, 0xb9, 0xf1, 0x08, 0xaf, 0xb4, 0xdb, 0x19, 0xc5, 0x22, 0x16, 0x4a, 0x7a, 0x8d, 0x3a, 0x56, + 0x9f, 0xfc, 0x77, 0x80, 0xd3, 0x55, 0x59, 0x52, 0xc6, 0x8c, 0x7b, 0x1b, 0x46, 0x93, 0x28, 0x23, + 0x72, 0xab, 0x6f, 0x98, 0x7c, 0x01, 0xf0, 0xfa, 0xa2, 0xb5, 0x2f, 0x72, 0x4a, 0x24, 0x45, 0xb7, + 0xa0, 0xc1, 0x22, 0x1b, 0x8c, 0xc1, 0xf4, 0xea, 0x7c, 0x50, 0x7f, 0xbb, 0x63, 0xbc, 0x58, 0x06, + 0x06, 0x8b, 0xd0, 0x08, 0x5e, 0x61, 0x29, 0x89, 0xa9, 0x6d, 0x34, 0xbf, 0x02, 0x7d, 0x40, 0x4b, + 0x38, 0xcc, 0x4b, 0x2e, 0x59, 0x4a, 0x6d, 0x73, 0x0c, 0xa6, 0x96, 0x7f, 0x1f, 0xff, 0x41, 0x86, + 0x7f, 0x6b, 0x81, 0x03, 0x9d, 0x08, 0xda, 0xa8, 0xf3, 0x1a, 0x0e, 0x8f, 0x35, 0x84, 0x60, 0x9f, + 0x93, 0x94, 0xea, 0x01, 0x02, 0xa5, 0x11, 0x86, 0x43, 0x91, 0x49, 0x26, 0x78, 0xa1, 0x9a, 0x5b, + 0xfe, 0x08, 0xeb, 0x5d, 0xe1, 0x16, 0x10, 0x3f, 0xe5, 0x55, 0xd0, 0x9a, 0x26, 0x3f, 0xba, 0x58, + 0x6f, 0xb3, 0xe8, 0x72, 0xac, 0xe7, 0x70, 0x90, 0x90, 0x90, 0x26, 0x85, 0x6d, 0x8e, 0xcd, 0xa9, + 0xe5, 0xe3, 0x7f, 0x51, 0xe9, 0x0e, 0xf8, 0x95, 0x0a, 0x3c, 0xe3, 0x32, 0xaf, 0x82, 0x63, 0x1a, + 0xdd, 0x85, 0xd7, 0x0a, 0x4e, 0xb2, 0x62, 0x2b, 0xe4, 0xfb, 0x0f, 0xb4, 0xb2, 0xfb, 0xaa, 0x89, + 0xd5, 0xd6, 0x5e, 0xd2, 0xca, 0x79, 0x04, 0xad, 0x4e, 0x12, 0xdd, 0x80, 0x66, 0x63, 0xd4, 0xf8, + 0x8d, 0x6c, 0x26, 0xdc, 0x91, 0xa4, 0x3c, 0x4d, 0xa8, 0x0e, 0x8f, 0x8d, 0x87, 0x60, 0x72, 0xaf, + 0x83, 0xb9, 0xa4, 0x09, 0xfd, 0x3b, 0xe6, 0xfc, 0xcd, 0xfe, 0xe0, 0xf6, 0xbe, 0x1e, 0xdc, 0xde, + 0xc7, 0xda, 0x05, 0xfb, 0xda, 0x05, 0x9f, 0x6b, 0x17, 0x7c, 0xaf, 0x5d, 0xf0, 0xe9, 0xa7, 0x0b, + 0xde, 0xf9, 0x17, 0x3c, 0xe5, 0x99, 0xfe, 0xac, 0xc0, 0xca, 0x08, 0x07, 0x6a, 0xff, 0x0f, 0x7e, + 0x05, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x09, 0xe0, 0xd6, 0x0b, 0x03, 0x00, 0x00, +} + // Field returns the value for the given fieldpath as a string, if defined. // If the value is not defined, the second value will be false. func (m *ContainerCreate) Field(fieldpath []string) (string, bool) { @@ -153,7 +270,7 @@ func (m *ContainerCreate_Runtime) Field(fieldpath []string) (string, bool) { case "name": return string(m.Name), len(m.Name) > 0 case "options": - decoded, err := typeurl.UnmarshalAny(m.Options) + decoded, err := github_com_containerd_typeurl.UnmarshalAny(m.Options) if err != nil { return "", false } @@ -209,7 +326,7 @@ func (m *ContainerDelete) Field(fieldpath []string) (string, bool) { func (m *ContainerCreate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -217,39 +334,52 @@ func (m *ContainerCreate) Marshal() (dAtA []byte, err error) { } func (m *ContainerCreate) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerCreate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintContainer(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Runtime != nil { + { + size, err := m.Runtime.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainer(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } if len(m.Image) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Image) + copy(dAtA[i:], m.Image) i = encodeVarintContainer(dAtA, i, uint64(len(m.Image))) - i += copy(dAtA[i:], m.Image) + i-- + dAtA[i] = 0x12 } - if m.Runtime != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintContainer(dAtA, i, uint64(m.Runtime.Size())) - n1, err := m.Runtime.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintContainer(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ContainerCreate_Runtime) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -257,33 +387,45 @@ func (m *ContainerCreate_Runtime) Marshal() (dAtA []byte, err error) { } func (m *ContainerCreate_Runtime) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerCreate_Runtime) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintContainer(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Options != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintContainer(dAtA, i, uint64(m.Options.Size())) - n2, err := m.Options.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Options.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainer(dAtA, i, uint64(size)) } - i += n2 + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintContainer(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ContainerUpdate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -291,52 +433,66 @@ func (m *ContainerUpdate) Marshal() (dAtA []byte, err error) { } func (m *ContainerUpdate) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintContainer(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Image) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintContainer(dAtA, i, uint64(len(m.Image))) - i += copy(dAtA[i:], m.Image) + if len(m.SnapshotKey) > 0 { + i -= len(m.SnapshotKey) + copy(dAtA[i:], m.SnapshotKey) + i = encodeVarintContainer(dAtA, i, uint64(len(m.SnapshotKey))) + i-- + dAtA[i] = 0x22 } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x1a - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovContainer(uint64(len(k))) + 1 + len(v) + sovContainer(uint64(len(v))) - i = encodeVarintContainer(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintContainer(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) i = encodeVarintContainer(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintContainer(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintContainer(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a } } - if len(m.SnapshotKey) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintContainer(dAtA, i, uint64(len(m.SnapshotKey))) - i += copy(dAtA[i:], m.SnapshotKey) + if len(m.Image) > 0 { + i -= len(m.Image) + copy(dAtA[i:], m.Image) + i = encodeVarintContainer(dAtA, i, uint64(len(m.Image))) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintContainer(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *ContainerDelete) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -344,29 +500,44 @@ func (m *ContainerDelete) Marshal() (dAtA []byte, err error) { } func (m *ContainerDelete) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerDelete) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ID) + copy(dAtA[i:], m.ID) i = encodeVarintContainer(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintContainer(dAtA []byte, offset int, v uint64) int { + offset -= sovContainer(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *ContainerCreate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -381,10 +552,16 @@ func (m *ContainerCreate) Size() (n int) { l = m.Runtime.Size() n += 1 + l + sovContainer(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ContainerCreate_Runtime) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -395,10 +572,16 @@ func (m *ContainerCreate_Runtime) Size() (n int) { l = m.Options.Size() n += 1 + l + sovContainer(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ContainerUpdate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -421,28 +604,30 @@ func (m *ContainerUpdate) Size() (n int) { if l > 0 { n += 1 + l + sovContainer(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ContainerDelete) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) if l > 0 { n += 1 + l + sovContainer(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovContainer(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozContainer(x uint64) (n int) { return sovContainer(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -455,6 +640,7 @@ func (this *ContainerCreate) String() string { `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Image:` + fmt.Sprintf("%v", this.Image) + `,`, `Runtime:` + strings.Replace(fmt.Sprintf("%v", this.Runtime), "ContainerCreate_Runtime", "ContainerCreate_Runtime", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -465,7 +651,8 @@ func (this *ContainerCreate_Runtime) String() string { } s := strings.Join([]string{`&ContainerCreate_Runtime{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, - `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "google_protobuf.Any", 1) + `,`, + `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "types.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -478,7 +665,7 @@ func (this *ContainerUpdate) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -489,6 +676,7 @@ func (this *ContainerUpdate) String() string { `Image:` + fmt.Sprintf("%v", this.Image) + `,`, `Labels:` + mapStringForLabels + `,`, `SnapshotKey:` + fmt.Sprintf("%v", this.SnapshotKey) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -499,6 +687,7 @@ func (this *ContainerDelete) String() string { } s := strings.Join([]string{`&ContainerDelete{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -526,7 +715,7 @@ func (m *ContainerCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -554,7 +743,7 @@ func (m *ContainerCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -564,6 +753,9 @@ func (m *ContainerCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainer + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -583,7 +775,7 @@ func (m *ContainerCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -593,6 +785,9 @@ func (m *ContainerCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainer + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -612,7 +807,7 @@ func (m *ContainerCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -621,6 +816,9 @@ func (m *ContainerCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainer + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -637,12 +835,13 @@ func (m *ContainerCreate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainer } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -667,7 +866,7 @@ func (m *ContainerCreate_Runtime) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -695,7 +894,7 @@ func (m *ContainerCreate_Runtime) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -705,6 +904,9 @@ func (m *ContainerCreate_Runtime) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainer + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -724,7 +926,7 @@ func (m *ContainerCreate_Runtime) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -733,11 +935,14 @@ func (m *ContainerCreate_Runtime) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainer + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Options == nil { - m.Options = &google_protobuf.Any{} + m.Options = &types.Any{} } if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -749,12 +954,13 @@ func (m *ContainerCreate_Runtime) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainer } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -779,7 +985,7 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -807,7 +1013,7 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -817,6 +1023,9 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainer + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -836,7 +1045,7 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -846,6 +1055,9 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainer + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -865,7 +1077,7 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -874,6 +1086,9 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainer + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -894,7 +1109,7 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -911,7 +1126,7 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -921,6 +1136,9 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthContainer + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -937,7 +1155,7 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -947,6 +1165,9 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthContainer + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -958,7 +1179,7 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainer } if (iNdEx + skippy) > postIndex { @@ -983,7 +1204,7 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -993,6 +1214,9 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainer + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1004,12 +1228,13 @@ func (m *ContainerUpdate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainer } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1034,7 +1259,7 @@ func (m *ContainerDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1062,7 +1287,7 @@ func (m *ContainerDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1072,6 +1297,9 @@ func (m *ContainerDelete) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainer } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainer + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1083,12 +1311,13 @@ func (m *ContainerDelete) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainer } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1101,6 +1330,7 @@ func (m *ContainerDelete) Unmarshal(dAtA []byte) error { func skipContainer(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1132,10 +1362,8 @@ func skipContainer(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1152,87 +1380,34 @@ func skipContainer(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthContainer } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowContainer - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipContainer(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupContainer + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthContainer + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthContainer = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowContainer = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthContainer = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowContainer = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupContainer = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/events/container.proto", fileDescriptorContainer) -} - -var fileDescriptorContainer = []byte{ - // 413 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xc1, 0x0a, 0xd3, 0x30, - 0x18, 0xc7, 0x97, 0x76, 0x6e, 0x98, 0x0a, 0x6a, 0x18, 0x52, 0x7b, 0xa8, 0x73, 0xa7, 0xe9, 0x21, - 0x85, 0x7a, 0x51, 0x77, 0xd1, 0x6d, 0x0a, 0xa2, 0x82, 0x14, 0x84, 0xe1, 0x45, 0xd2, 0x35, 0xeb, - 0x82, 0x6d, 0x52, 0xda, 0x74, 0xd0, 0x9b, 0x8f, 0xe2, 0xe3, 0xec, 0xe8, 0xc1, 0x83, 0x27, 0x71, - 0x05, 0xdf, 0xc0, 0x07, 0x90, 0x26, 0xeb, 0x56, 0x14, 0x95, 0x9d, 0xfa, 0xcf, 0xd7, 0xff, 0x3f, - 0xdf, 0xf7, 0xfb, 0x08, 0x9c, 0xc5, 0x4c, 0x6e, 0xcb, 0x10, 0xaf, 0x45, 0xea, 0xad, 0x05, 0x97, - 0x84, 0x71, 0x9a, 0x47, 0x5d, 0x49, 0x32, 0xe6, 0xd1, 0x1d, 0xe5, 0xb2, 0x38, 0x57, 0x71, 0x96, - 0x0b, 0x29, 0xd0, 0xcd, 0xb3, 0x0d, 0x6b, 0x8b, 0x73, 0x3b, 0x16, 0x22, 0x4e, 0xa8, 0xa7, 0x0c, - 0x61, 0xb9, 0xf1, 0x08, 0xaf, 0xb4, 0xdb, 0x19, 0xc5, 0x22, 0x16, 0x4a, 0x7a, 0x8d, 0x3a, 0x56, - 0x9f, 0xfc, 0x77, 0x80, 0xd3, 0x55, 0x59, 0x52, 0xc6, 0x8c, 0x7b, 0x1b, 0x46, 0x93, 0x28, 0x23, - 0x72, 0xab, 0x6f, 0x98, 0x7c, 0x01, 0xf0, 0xfa, 0xa2, 0xb5, 0x2f, 0x72, 0x4a, 0x24, 0x45, 0xb7, - 0xa0, 0xc1, 0x22, 0x1b, 0x8c, 0xc1, 0xf4, 0xea, 0x7c, 0x50, 0x7f, 0xbb, 0x63, 0xbc, 0x58, 0x06, - 0x06, 0x8b, 0xd0, 0x08, 0x5e, 0x61, 0x29, 0x89, 0xa9, 0x6d, 0x34, 0xbf, 0x02, 0x7d, 0x40, 0x4b, - 0x38, 0xcc, 0x4b, 0x2e, 0x59, 0x4a, 0x6d, 0x73, 0x0c, 0xa6, 0x96, 0x7f, 0x1f, 0xff, 0x41, 0x86, - 0x7f, 0x6b, 0x81, 0x03, 0x9d, 0x08, 0xda, 0xa8, 0xf3, 0x1a, 0x0e, 0x8f, 0x35, 0x84, 0x60, 0x9f, - 0x93, 0x94, 0xea, 0x01, 0x02, 0xa5, 0x11, 0x86, 0x43, 0x91, 0x49, 0x26, 0x78, 0xa1, 0x9a, 0x5b, - 0xfe, 0x08, 0xeb, 0x5d, 0xe1, 0x16, 0x10, 0x3f, 0xe5, 0x55, 0xd0, 0x9a, 0x26, 0x3f, 0xba, 0x58, - 0x6f, 0xb3, 0xe8, 0x72, 0xac, 0xe7, 0x70, 0x90, 0x90, 0x90, 0x26, 0x85, 0x6d, 0x8e, 0xcd, 0xa9, - 0xe5, 0xe3, 0x7f, 0x51, 0xe9, 0x0e, 0xf8, 0x95, 0x0a, 0x3c, 0xe3, 0x32, 0xaf, 0x82, 0x63, 0x1a, - 0xdd, 0x85, 0xd7, 0x0a, 0x4e, 0xb2, 0x62, 0x2b, 0xe4, 0xfb, 0x0f, 0xb4, 0xb2, 0xfb, 0xaa, 0x89, - 0xd5, 0xd6, 0x5e, 0xd2, 0xca, 0x79, 0x04, 0xad, 0x4e, 0x12, 0xdd, 0x80, 0x66, 0x63, 0xd4, 0xf8, - 0x8d, 0x6c, 0x26, 0xdc, 0x91, 0xa4, 0x3c, 0x4d, 0xa8, 0x0e, 0x8f, 0x8d, 0x87, 0x60, 0x72, 0xaf, - 0x83, 0xb9, 0xa4, 0x09, 0xfd, 0x3b, 0xe6, 0xfc, 0xcd, 0xfe, 0xe0, 0xf6, 0xbe, 0x1e, 0xdc, 0xde, - 0xc7, 0xda, 0x05, 0xfb, 0xda, 0x05, 0x9f, 0x6b, 0x17, 0x7c, 0xaf, 0x5d, 0xf0, 0xe9, 0xa7, 0x0b, - 0xde, 0xf9, 0x17, 0x3c, 0xe5, 0x99, 0xfe, 0xac, 0xc0, 0xca, 0x08, 0x07, 0x6a, 0xff, 0x0f, 0x7e, - 0x05, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x09, 0xe0, 0xd6, 0x0b, 0x03, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/events/container.proto b/vendor/github.com/containerd/containerd/api/events/container.proto index 13aa5848c067b..dfeca308ea314 100644 --- a/vendor/github.com/containerd/containerd/api/events/container.proto +++ b/vendor/github.com/containerd/containerd/api/events/container.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.events; diff --git a/vendor/github.com/containerd/containerd/api/events/content.pb.go b/vendor/github.com/containerd/containerd/api/events/content.pb.go index 87648d1938cfa..0a7ec9325d351 100644 --- a/vendor/github.com/containerd/containerd/api/events/content.pb.go +++ b/vendor/github.com/containerd/containerd/api/events/content.pb.go @@ -3,37 +3,94 @@ package events -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" - -import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type ContentDelete struct { - Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContentDelete) Reset() { *m = ContentDelete{} } +func (*ContentDelete) ProtoMessage() {} +func (*ContentDelete) Descriptor() ([]byte, []int) { + return fileDescriptor_dfb34b8b808e2ecd, []int{0} +} +func (m *ContentDelete) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContentDelete) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContentDelete.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContentDelete) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContentDelete.Merge(m, src) +} +func (m *ContentDelete) XXX_Size() int { + return m.Size() +} +func (m *ContentDelete) XXX_DiscardUnknown() { + xxx_messageInfo_ContentDelete.DiscardUnknown(m) } -func (m *ContentDelete) Reset() { *m = ContentDelete{} } -func (*ContentDelete) ProtoMessage() {} -func (*ContentDelete) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{0} } +var xxx_messageInfo_ContentDelete proto.InternalMessageInfo func init() { proto.RegisterType((*ContentDelete)(nil), "containerd.events.ContentDelete") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/content.proto", fileDescriptor_dfb34b8b808e2ecd) +} + +var fileDescriptor_dfb34b8b808e2ecd = []byte{ + // 228 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0x83, 0x45, 0x53, + 0xf3, 0x4a, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x04, 0x11, 0x8a, 0xf4, 0x20, 0x0a, 0xa4, + 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xb2, 0xfa, 0x20, 0x16, 0x44, 0xa1, 0x94, 0x03, 0x41, 0x3b, + 0xc0, 0xea, 0x92, 0x4a, 0xd3, 0xf4, 0x0b, 0x72, 0x4a, 0xd3, 0x33, 0xf3, 0xf4, 0xd3, 0x32, 0x53, + 0x73, 0x52, 0x0a, 0x12, 0x4b, 0x32, 0x20, 0x26, 0x28, 0x45, 0x73, 0xf1, 0x3a, 0x43, 0xec, 0x76, + 0x49, 0xcd, 0x49, 0x2d, 0x49, 0x15, 0xf2, 0xe2, 0x62, 0x4b, 0xc9, 0x4c, 0x4f, 0x2d, 0x2e, 0x91, + 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x74, 0x32, 0x3a, 0x71, 0x4f, 0x9e, 0xe1, 0xd6, 0x3d, 0x79, 0x2d, + 0x24, 0xab, 0xf2, 0x0b, 0x52, 0xf3, 0xe0, 0x76, 0x14, 0xeb, 0xa7, 0xe7, 0xeb, 0x42, 0xb4, 0xe8, + 0xb9, 0x80, 0xa9, 0x20, 0xa8, 0x09, 0x4e, 0x01, 0x27, 0x1e, 0xca, 0x31, 0xdc, 0x78, 0x28, 0xc7, + 0xd0, 0xf0, 0x48, 0x8e, 0xf1, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, + 0x63, 0x5c, 0xf0, 0x45, 0x8e, 0x31, 0xca, 0x88, 0x84, 0x00, 0xb2, 0x86, 0x50, 0x11, 0x0c, 0x11, + 0x8c, 0x49, 0x6c, 0x60, 0x97, 0x1b, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x4b, 0x78, 0x99, 0xee, + 0x61, 0x01, 0x00, 0x00, +} + // Field returns the value for the given fieldpath as a string, if defined. // If the value is not defined, the second value will be false. func (m *ContentDelete) Field(fieldpath []string) (string, bool) { @@ -50,7 +107,7 @@ func (m *ContentDelete) Field(fieldpath []string) (string, bool) { func (m *ContentDelete) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -58,47 +115,58 @@ func (m *ContentDelete) Marshal() (dAtA []byte, err error) { } func (m *ContentDelete) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContentDelete) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Digest) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Digest) + copy(dAtA[i:], m.Digest) i = encodeVarintContent(dAtA, i, uint64(len(m.Digest))) - i += copy(dAtA[i:], m.Digest) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintContent(dAtA []byte, offset int, v uint64) int { + offset -= sovContent(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *ContentDelete) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Digest) if l > 0 { n += 1 + l + sovContent(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovContent(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozContent(x uint64) (n int) { return sovContent(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -109,6 +177,7 @@ func (this *ContentDelete) String() string { } s := strings.Join([]string{`&ContentDelete{`, `Digest:` + fmt.Sprintf("%v", this.Digest) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -136,7 +205,7 @@ func (m *ContentDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -164,7 +233,7 @@ func (m *ContentDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -174,6 +243,9 @@ func (m *ContentDelete) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -185,12 +257,13 @@ func (m *ContentDelete) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -203,6 +276,7 @@ func (m *ContentDelete) Unmarshal(dAtA []byte) error { func skipContent(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -234,10 +308,8 @@ func skipContent(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -254,76 +326,34 @@ func skipContent(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthContent } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowContent - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipContent(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupContent + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthContent + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthContent = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowContent = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthContent = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowContent = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupContent = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/events/content.proto", fileDescriptorContent) -} - -var fileDescriptorContent = []byte{ - // 228 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, - 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0x83, 0x45, 0x53, - 0xf3, 0x4a, 0xf4, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0x04, 0x11, 0x8a, 0xf4, 0x20, 0x0a, 0xa4, - 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xb2, 0xfa, 0x20, 0x16, 0x44, 0xa1, 0x94, 0x03, 0x41, 0x3b, - 0xc0, 0xea, 0x92, 0x4a, 0xd3, 0xf4, 0x0b, 0x72, 0x4a, 0xd3, 0x33, 0xf3, 0xf4, 0xd3, 0x32, 0x53, - 0x73, 0x52, 0x0a, 0x12, 0x4b, 0x32, 0x20, 0x26, 0x28, 0x45, 0x73, 0xf1, 0x3a, 0x43, 0xec, 0x76, - 0x49, 0xcd, 0x49, 0x2d, 0x49, 0x15, 0xf2, 0xe2, 0x62, 0x4b, 0xc9, 0x4c, 0x4f, 0x2d, 0x2e, 0x91, - 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x74, 0x32, 0x3a, 0x71, 0x4f, 0x9e, 0xe1, 0xd6, 0x3d, 0x79, 0x2d, - 0x24, 0xab, 0xf2, 0x0b, 0x52, 0xf3, 0xe0, 0x76, 0x14, 0xeb, 0xa7, 0xe7, 0xeb, 0x42, 0xb4, 0xe8, - 0xb9, 0x80, 0xa9, 0x20, 0xa8, 0x09, 0x4e, 0x01, 0x27, 0x1e, 0xca, 0x31, 0xdc, 0x78, 0x28, 0xc7, - 0xd0, 0xf0, 0x48, 0x8e, 0xf1, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, - 0x63, 0x5c, 0xf0, 0x45, 0x8e, 0x31, 0xca, 0x88, 0x84, 0x00, 0xb2, 0x86, 0x50, 0x11, 0x0c, 0x11, - 0x8c, 0x49, 0x6c, 0x60, 0x97, 0x1b, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x4b, 0x78, 0x99, 0xee, - 0x61, 0x01, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/events/content.proto b/vendor/github.com/containerd/containerd/api/events/content.proto index aba50716f22d9..b8f84bc89b736 100644 --- a/vendor/github.com/containerd/containerd/api/events/content.proto +++ b/vendor/github.com/containerd/containerd/api/events/content.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.events; diff --git a/vendor/github.com/containerd/containerd/api/events/image.pb.go b/vendor/github.com/containerd/containerd/api/events/image.pb.go index 8197005b6e1a3..747026945449d 100644 --- a/vendor/github.com/containerd/containerd/api/events/image.pb.go +++ b/vendor/github.com/containerd/containerd/api/events/image.pb.go @@ -3,55 +3,182 @@ package events -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type ImageCreate struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ImageCreate) Reset() { *m = ImageCreate{} } -func (*ImageCreate) ProtoMessage() {} -func (*ImageCreate) Descriptor() ([]byte, []int) { return fileDescriptorImage, []int{0} } +func (m *ImageCreate) Reset() { *m = ImageCreate{} } +func (*ImageCreate) ProtoMessage() {} +func (*ImageCreate) Descriptor() ([]byte, []int) { + return fileDescriptor_7085610f7b33e042, []int{0} +} +func (m *ImageCreate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ImageCreate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ImageCreate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ImageCreate) XXX_Merge(src proto.Message) { + xxx_messageInfo_ImageCreate.Merge(m, src) +} +func (m *ImageCreate) XXX_Size() int { + return m.Size() +} +func (m *ImageCreate) XXX_DiscardUnknown() { + xxx_messageInfo_ImageCreate.DiscardUnknown(m) +} + +var xxx_messageInfo_ImageCreate proto.InternalMessageInfo type ImageUpdate struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ImageUpdate) Reset() { *m = ImageUpdate{} } +func (*ImageUpdate) ProtoMessage() {} +func (*ImageUpdate) Descriptor() ([]byte, []int) { + return fileDescriptor_7085610f7b33e042, []int{1} +} +func (m *ImageUpdate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ImageUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ImageUpdate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ImageUpdate) XXX_Merge(src proto.Message) { + xxx_messageInfo_ImageUpdate.Merge(m, src) +} +func (m *ImageUpdate) XXX_Size() int { + return m.Size() +} +func (m *ImageUpdate) XXX_DiscardUnknown() { + xxx_messageInfo_ImageUpdate.DiscardUnknown(m) } -func (m *ImageUpdate) Reset() { *m = ImageUpdate{} } -func (*ImageUpdate) ProtoMessage() {} -func (*ImageUpdate) Descriptor() ([]byte, []int) { return fileDescriptorImage, []int{1} } +var xxx_messageInfo_ImageUpdate proto.InternalMessageInfo type ImageDelete struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ImageDelete) Reset() { *m = ImageDelete{} } -func (*ImageDelete) ProtoMessage() {} -func (*ImageDelete) Descriptor() ([]byte, []int) { return fileDescriptorImage, []int{2} } +func (m *ImageDelete) Reset() { *m = ImageDelete{} } +func (*ImageDelete) ProtoMessage() {} +func (*ImageDelete) Descriptor() ([]byte, []int) { + return fileDescriptor_7085610f7b33e042, []int{2} +} +func (m *ImageDelete) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ImageDelete) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ImageDelete.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ImageDelete) XXX_Merge(src proto.Message) { + xxx_messageInfo_ImageDelete.Merge(m, src) +} +func (m *ImageDelete) XXX_Size() int { + return m.Size() +} +func (m *ImageDelete) XXX_DiscardUnknown() { + xxx_messageInfo_ImageDelete.DiscardUnknown(m) +} + +var xxx_messageInfo_ImageDelete proto.InternalMessageInfo func init() { proto.RegisterType((*ImageCreate)(nil), "containerd.services.images.v1.ImageCreate") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.images.v1.ImageCreate.LabelsEntry") proto.RegisterType((*ImageUpdate)(nil), "containerd.services.images.v1.ImageUpdate") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.images.v1.ImageUpdate.LabelsEntry") proto.RegisterType((*ImageDelete)(nil), "containerd.services.images.v1.ImageDelete") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/image.proto", fileDescriptor_7085610f7b33e042) +} + +var fileDescriptor_7085610f7b33e042 = []byte{ + // 292 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4f, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0xeb, 0x67, 0xe6, + 0x26, 0xa6, 0xa7, 0xea, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0xc9, 0x22, 0x94, 0xe8, 0x15, 0xa7, + 0x16, 0x95, 0x65, 0x26, 0xa7, 0x16, 0xeb, 0x81, 0x15, 0x14, 0xeb, 0x95, 0x19, 0x4a, 0x39, 0x10, + 0x34, 0x17, 0x6c, 0x4c, 0x52, 0x69, 0x9a, 0x7e, 0x41, 0x4e, 0x69, 0x7a, 0x66, 0x9e, 0x7e, 0x5a, + 0x66, 0x6a, 0x4e, 0x4a, 0x41, 0x62, 0x49, 0x06, 0xc4, 0x02, 0xa5, 0x35, 0x8c, 0x5c, 0xdc, 0x9e, + 0x20, 0xf3, 0x9c, 0x8b, 0x52, 0x13, 0x4b, 0x52, 0x85, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, + 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x21, 0x3f, 0x2e, 0xb6, 0x9c, 0xc4, 0xa4, + 0xd4, 0x9c, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x33, 0x3d, 0xbc, 0xae, 0xd2, 0x43, + 0x32, 0x4f, 0xcf, 0x07, 0xac, 0xd1, 0x35, 0xaf, 0xa4, 0xa8, 0x32, 0x08, 0x6a, 0x8a, 0x94, 0x25, + 0x17, 0x37, 0x92, 0xb0, 0x90, 0x00, 0x17, 0x73, 0x76, 0x6a, 0x25, 0xd4, 0x46, 0x10, 0x53, 0x48, + 0x84, 0x8b, 0xb5, 0x2c, 0x31, 0xa7, 0x34, 0x55, 0x82, 0x09, 0x2c, 0x06, 0xe1, 0x58, 0x31, 0x59, + 0x30, 0x22, 0x9c, 0x1b, 0x5a, 0x90, 0x42, 0x55, 0xe7, 0x42, 0xcc, 0xa3, 0xb6, 0x73, 0x15, 0xa1, + 0xae, 0x75, 0x49, 0xcd, 0x49, 0xc5, 0xee, 0x5a, 0xa7, 0x80, 0x13, 0x0f, 0xe5, 0x18, 0x6e, 0x3c, + 0x94, 0x63, 0x68, 0x78, 0x24, 0xc7, 0x78, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, + 0x1e, 0xc9, 0x31, 0x2e, 0xf8, 0x22, 0xc7, 0x18, 0x65, 0x44, 0x42, 0xc2, 0xb1, 0x86, 0x50, 0x11, + 0x0c, 0x49, 0x6c, 0xe0, 0xb8, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x41, 0x80, 0x92, 0x17, + 0x77, 0x02, 0x00, 0x00, +} + // Field returns the value for the given fieldpath as a string, if defined. // If the value is not defined, the second value will be false. func (m *ImageCreate) Field(fieldpath []string) (string, bool) { @@ -112,7 +239,7 @@ func (m *ImageDelete) Field(fieldpath []string) (string, bool) { func (m *ImageCreate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -120,40 +247,52 @@ func (m *ImageCreate) Marshal() (dAtA []byte, err error) { } func (m *ImageCreate) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ImageCreate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintImage(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x12 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovImage(uint64(len(k))) + 1 + len(v) + sovImage(uint64(len(v))) - i = encodeVarintImage(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintImage(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintImage(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintImage(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintImage(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - return i, nil + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintImage(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *ImageUpdate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -161,40 +300,52 @@ func (m *ImageUpdate) Marshal() (dAtA []byte, err error) { } func (m *ImageUpdate) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ImageUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintImage(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x12 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovImage(uint64(len(k))) + 1 + len(v) + sovImage(uint64(len(v))) - i = encodeVarintImage(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintImage(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintImage(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintImage(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintImage(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - return i, nil + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintImage(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *ImageDelete) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -202,29 +353,44 @@ func (m *ImageDelete) Marshal() (dAtA []byte, err error) { } func (m *ImageDelete) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ImageDelete) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Name) + copy(dAtA[i:], m.Name) i = encodeVarintImage(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintImage(dAtA []byte, offset int, v uint64) int { + offset -= sovImage(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *ImageCreate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -239,10 +405,16 @@ func (m *ImageCreate) Size() (n int) { n += mapEntrySize + 1 + sovImage(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ImageUpdate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -257,28 +429,30 @@ func (m *ImageUpdate) Size() (n int) { n += mapEntrySize + 1 + sovImage(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ImageDelete) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + sovImage(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovImage(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozImage(x uint64) (n int) { return sovImage(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -291,7 +465,7 @@ func (this *ImageCreate) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -300,6 +474,7 @@ func (this *ImageCreate) String() string { s := strings.Join([]string{`&ImageCreate{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -312,7 +487,7 @@ func (this *ImageUpdate) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -321,6 +496,7 @@ func (this *ImageUpdate) String() string { s := strings.Join([]string{`&ImageUpdate{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -331,6 +507,7 @@ func (this *ImageDelete) String() string { } s := strings.Join([]string{`&ImageDelete{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -358,7 +535,7 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -386,7 +563,7 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -396,6 +573,9 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImage } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthImage + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -415,7 +595,7 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -424,6 +604,9 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImage } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImage + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -444,7 +627,7 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -461,7 +644,7 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -471,6 +654,9 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImage } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthImage + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -487,7 +673,7 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -497,6 +683,9 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImage } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthImage + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -508,7 +697,7 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImage } if (iNdEx + skippy) > postIndex { @@ -525,12 +714,13 @@ func (m *ImageCreate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImage } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -555,7 +745,7 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -583,7 +773,7 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -593,6 +783,9 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImage } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthImage + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -612,7 +805,7 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -621,6 +814,9 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImage } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImage + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -641,7 +837,7 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -658,7 +854,7 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -668,6 +864,9 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImage } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthImage + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -684,7 +883,7 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -694,6 +893,9 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImage } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthImage + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -705,7 +907,7 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImage } if (iNdEx + skippy) > postIndex { @@ -722,12 +924,13 @@ func (m *ImageUpdate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImage } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -752,7 +955,7 @@ func (m *ImageDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -780,7 +983,7 @@ func (m *ImageDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -790,6 +993,9 @@ func (m *ImageDelete) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImage } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthImage + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -801,12 +1007,13 @@ func (m *ImageDelete) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImage } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -819,6 +1026,7 @@ func (m *ImageDelete) Unmarshal(dAtA []byte) error { func skipImage(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -850,10 +1058,8 @@ func skipImage(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -870,80 +1076,34 @@ func skipImage(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthImage } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowImage - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipImage(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupImage + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthImage + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthImage = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowImage = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthImage = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowImage = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupImage = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/events/image.proto", fileDescriptorImage) -} - -var fileDescriptorImage = []byte{ - // 292 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4f, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, - 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0xeb, 0x67, 0xe6, - 0x26, 0xa6, 0xa7, 0xea, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0xc9, 0x22, 0x94, 0xe8, 0x15, 0xa7, - 0x16, 0x95, 0x65, 0x26, 0xa7, 0x16, 0xeb, 0x81, 0x15, 0x14, 0xeb, 0x95, 0x19, 0x4a, 0x39, 0x10, - 0x34, 0x17, 0x6c, 0x4c, 0x52, 0x69, 0x9a, 0x7e, 0x41, 0x4e, 0x69, 0x7a, 0x66, 0x9e, 0x7e, 0x5a, - 0x66, 0x6a, 0x4e, 0x4a, 0x41, 0x62, 0x49, 0x06, 0xc4, 0x02, 0xa5, 0x35, 0x8c, 0x5c, 0xdc, 0x9e, - 0x20, 0xf3, 0x9c, 0x8b, 0x52, 0x13, 0x4b, 0x52, 0x85, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, - 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x21, 0x3f, 0x2e, 0xb6, 0x9c, 0xc4, 0xa4, - 0xd4, 0x9c, 0x62, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x33, 0x3d, 0xbc, 0xae, 0xd2, 0x43, - 0x32, 0x4f, 0xcf, 0x07, 0xac, 0xd1, 0x35, 0xaf, 0xa4, 0xa8, 0x32, 0x08, 0x6a, 0x8a, 0x94, 0x25, - 0x17, 0x37, 0x92, 0xb0, 0x90, 0x00, 0x17, 0x73, 0x76, 0x6a, 0x25, 0xd4, 0x46, 0x10, 0x53, 0x48, - 0x84, 0x8b, 0xb5, 0x2c, 0x31, 0xa7, 0x34, 0x55, 0x82, 0x09, 0x2c, 0x06, 0xe1, 0x58, 0x31, 0x59, - 0x30, 0x22, 0x9c, 0x1b, 0x5a, 0x90, 0x42, 0x55, 0xe7, 0x42, 0xcc, 0xa3, 0xb6, 0x73, 0x15, 0xa1, - 0xae, 0x75, 0x49, 0xcd, 0x49, 0xc5, 0xee, 0x5a, 0xa7, 0x80, 0x13, 0x0f, 0xe5, 0x18, 0x6e, 0x3c, - 0x94, 0x63, 0x68, 0x78, 0x24, 0xc7, 0x78, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, - 0x1e, 0xc9, 0x31, 0x2e, 0xf8, 0x22, 0xc7, 0x18, 0x65, 0x44, 0x42, 0xc2, 0xb1, 0x86, 0x50, 0x11, - 0x0c, 0x49, 0x6c, 0xe0, 0xb8, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x41, 0x80, 0x92, 0x17, - 0x77, 0x02, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/events/image.proto b/vendor/github.com/containerd/containerd/api/events/image.proto index 470c3a2fa44a1..fe455b54cab22 100644 --- a/vendor/github.com/containerd/containerd/api/events/image.proto +++ b/vendor/github.com/containerd/containerd/api/events/image.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.images.v1; diff --git a/vendor/github.com/containerd/containerd/api/events/namespace.pb.go b/vendor/github.com/containerd/containerd/api/events/namespace.pb.go index 1c81f9fc44cf9..d406a987e98d1 100644 --- a/vendor/github.com/containerd/containerd/api/events/namespace.pb.go +++ b/vendor/github.com/containerd/containerd/api/events/namespace.pb.go @@ -3,56 +3,182 @@ package events -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type NamespaceCreate struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *NamespaceCreate) Reset() { *m = NamespaceCreate{} } -func (*NamespaceCreate) ProtoMessage() {} -func (*NamespaceCreate) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{0} } +func (m *NamespaceCreate) Reset() { *m = NamespaceCreate{} } +func (*NamespaceCreate) ProtoMessage() {} +func (*NamespaceCreate) Descriptor() ([]byte, []int) { + return fileDescriptor_6cd45d1d5adffe29, []int{0} +} +func (m *NamespaceCreate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NamespaceCreate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NamespaceCreate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NamespaceCreate) XXX_Merge(src proto.Message) { + xxx_messageInfo_NamespaceCreate.Merge(m, src) +} +func (m *NamespaceCreate) XXX_Size() int { + return m.Size() +} +func (m *NamespaceCreate) XXX_DiscardUnknown() { + xxx_messageInfo_NamespaceCreate.DiscardUnknown(m) +} + +var xxx_messageInfo_NamespaceCreate proto.InternalMessageInfo type NamespaceUpdate struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NamespaceUpdate) Reset() { *m = NamespaceUpdate{} } +func (*NamespaceUpdate) ProtoMessage() {} +func (*NamespaceUpdate) Descriptor() ([]byte, []int) { + return fileDescriptor_6cd45d1d5adffe29, []int{1} +} +func (m *NamespaceUpdate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NamespaceUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NamespaceUpdate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NamespaceUpdate) XXX_Merge(src proto.Message) { + xxx_messageInfo_NamespaceUpdate.Merge(m, src) +} +func (m *NamespaceUpdate) XXX_Size() int { + return m.Size() +} +func (m *NamespaceUpdate) XXX_DiscardUnknown() { + xxx_messageInfo_NamespaceUpdate.DiscardUnknown(m) } -func (m *NamespaceUpdate) Reset() { *m = NamespaceUpdate{} } -func (*NamespaceUpdate) ProtoMessage() {} -func (*NamespaceUpdate) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{1} } +var xxx_messageInfo_NamespaceUpdate proto.InternalMessageInfo type NamespaceDelete struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *NamespaceDelete) Reset() { *m = NamespaceDelete{} } -func (*NamespaceDelete) ProtoMessage() {} -func (*NamespaceDelete) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{2} } +func (m *NamespaceDelete) Reset() { *m = NamespaceDelete{} } +func (*NamespaceDelete) ProtoMessage() {} +func (*NamespaceDelete) Descriptor() ([]byte, []int) { + return fileDescriptor_6cd45d1d5adffe29, []int{2} +} +func (m *NamespaceDelete) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NamespaceDelete) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NamespaceDelete.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NamespaceDelete) XXX_Merge(src proto.Message) { + xxx_messageInfo_NamespaceDelete.Merge(m, src) +} +func (m *NamespaceDelete) XXX_Size() int { + return m.Size() +} +func (m *NamespaceDelete) XXX_DiscardUnknown() { + xxx_messageInfo_NamespaceDelete.DiscardUnknown(m) +} + +var xxx_messageInfo_NamespaceDelete proto.InternalMessageInfo func init() { proto.RegisterType((*NamespaceCreate)(nil), "containerd.events.NamespaceCreate") + proto.RegisterMapType((map[string]string)(nil), "containerd.events.NamespaceCreate.LabelsEntry") proto.RegisterType((*NamespaceUpdate)(nil), "containerd.events.NamespaceUpdate") + proto.RegisterMapType((map[string]string)(nil), "containerd.events.NamespaceUpdate.LabelsEntry") proto.RegisterType((*NamespaceDelete)(nil), "containerd.events.NamespaceDelete") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/namespace.proto", fileDescriptor_6cd45d1d5adffe29) +} + +var fileDescriptor_6cd45d1d5adffe29 = []byte{ + // 296 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0xeb, 0xe7, 0x25, + 0xe6, 0xa6, 0x16, 0x17, 0x24, 0x26, 0xa7, 0xea, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x22, + 0x94, 0xe9, 0x41, 0x94, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x65, 0xf5, 0x41, 0x2c, 0x88, + 0x42, 0x29, 0x07, 0x82, 0xb6, 0x80, 0xd5, 0x25, 0x95, 0xa6, 0xe9, 0x17, 0xe4, 0x94, 0xa6, 0x67, + 0xe6, 0xe9, 0xa7, 0x65, 0xa6, 0xe6, 0xa4, 0x14, 0x24, 0x96, 0x64, 0x40, 0x4c, 0x50, 0x5a, 0xc1, + 0xc8, 0xc5, 0xef, 0x07, 0xb3, 0xde, 0xb9, 0x28, 0x35, 0xb1, 0x24, 0x55, 0x48, 0x88, 0x8b, 0x05, + 0xe4, 0x22, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x30, 0x5b, 0xc8, 0x8d, 0x8b, 0x2d, 0x27, + 0x31, 0x29, 0x35, 0xa7, 0x58, 0x82, 0x49, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x4f, 0x0f, 0xc3, 0x8d, + 0x7a, 0x68, 0xe6, 0xe8, 0xf9, 0x80, 0x35, 0xb8, 0xe6, 0x95, 0x14, 0x55, 0x06, 0x41, 0x75, 0x4b, + 0x59, 0x72, 0x71, 0x23, 0x09, 0x0b, 0x09, 0x70, 0x31, 0x67, 0xa7, 0x56, 0x42, 0x6d, 0x02, 0x31, + 0x85, 0x44, 0xb8, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, 0x25, 0x98, 0xc0, 0x62, 0x10, 0x8e, 0x15, + 0x93, 0x05, 0x23, 0xaa, 0x53, 0x43, 0x0b, 0x52, 0xa8, 0xe2, 0x54, 0x88, 0x39, 0xd4, 0x76, 0xaa, + 0x2a, 0x92, 0x4b, 0x5d, 0x52, 0x73, 0x52, 0xb1, 0xbb, 0xd4, 0x29, 0xe0, 0xc4, 0x43, 0x39, 0x86, + 0x1b, 0x0f, 0xe5, 0x18, 0x1a, 0x1e, 0xc9, 0x31, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, + 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x0b, 0xbe, 0xc8, 0x31, 0x46, 0x19, 0x91, 0x90, 0x84, 0xac, 0x21, + 0x54, 0x04, 0x43, 0x04, 0x63, 0x12, 0x1b, 0x38, 0x66, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x50, 0x87, 0x59, 0x83, 0x02, 0x00, 0x00, +} + // Field returns the value for the given fieldpath as a string, if defined. // If the value is not defined, the second value will be false. func (m *NamespaceCreate) Field(fieldpath []string) (string, bool) { @@ -113,7 +239,7 @@ func (m *NamespaceDelete) Field(fieldpath []string) (string, bool) { func (m *NamespaceCreate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -121,40 +247,52 @@ func (m *NamespaceCreate) Marshal() (dAtA []byte, err error) { } func (m *NamespaceCreate) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NamespaceCreate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x12 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovNamespace(uint64(len(k))) + 1 + len(v) + sovNamespace(uint64(len(v))) - i = encodeVarintNamespace(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintNamespace(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintNamespace(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintNamespace(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintNamespace(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - return i, nil + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *NamespaceUpdate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -162,40 +300,52 @@ func (m *NamespaceUpdate) Marshal() (dAtA []byte, err error) { } func (m *NamespaceUpdate) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NamespaceUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x12 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovNamespace(uint64(len(k))) + 1 + len(v) + sovNamespace(uint64(len(v))) - i = encodeVarintNamespace(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintNamespace(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintNamespace(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintNamespace(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintNamespace(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - return i, nil + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *NamespaceDelete) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -203,29 +353,44 @@ func (m *NamespaceDelete) Marshal() (dAtA []byte, err error) { } func (m *NamespaceDelete) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NamespaceDelete) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Name) + copy(dAtA[i:], m.Name) i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintNamespace(dAtA []byte, offset int, v uint64) int { + offset -= sovNamespace(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *NamespaceCreate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -240,10 +405,16 @@ func (m *NamespaceCreate) Size() (n int) { n += mapEntrySize + 1 + sovNamespace(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *NamespaceUpdate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -258,28 +429,30 @@ func (m *NamespaceUpdate) Size() (n int) { n += mapEntrySize + 1 + sovNamespace(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *NamespaceDelete) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + sovNamespace(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovNamespace(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozNamespace(x uint64) (n int) { return sovNamespace(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -292,7 +465,7 @@ func (this *NamespaceCreate) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -301,6 +474,7 @@ func (this *NamespaceCreate) String() string { s := strings.Join([]string{`&NamespaceCreate{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -313,7 +487,7 @@ func (this *NamespaceUpdate) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -322,6 +496,7 @@ func (this *NamespaceUpdate) String() string { s := strings.Join([]string{`&NamespaceUpdate{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -332,6 +507,7 @@ func (this *NamespaceDelete) String() string { } s := strings.Join([]string{`&NamespaceDelete{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -359,7 +535,7 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -387,7 +563,7 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -397,6 +573,9 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -416,7 +595,7 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -425,6 +604,9 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -445,7 +627,7 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -462,7 +644,7 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -472,6 +654,9 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthNamespace + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -488,7 +673,7 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -498,6 +683,9 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthNamespace + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -509,7 +697,7 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > postIndex { @@ -526,12 +714,13 @@ func (m *NamespaceCreate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -556,7 +745,7 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -584,7 +773,7 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -594,6 +783,9 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -613,7 +805,7 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -622,6 +814,9 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -642,7 +837,7 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -659,7 +854,7 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -669,6 +864,9 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthNamespace + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -685,7 +883,7 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -695,6 +893,9 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthNamespace + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -706,7 +907,7 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > postIndex { @@ -723,12 +924,13 @@ func (m *NamespaceUpdate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -753,7 +955,7 @@ func (m *NamespaceDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -781,7 +983,7 @@ func (m *NamespaceDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -791,6 +993,9 @@ func (m *NamespaceDelete) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -802,12 +1007,13 @@ func (m *NamespaceDelete) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -820,6 +1026,7 @@ func (m *NamespaceDelete) Unmarshal(dAtA []byte) error { func skipNamespace(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -851,10 +1058,8 @@ func skipNamespace(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -871,80 +1076,34 @@ func skipNamespace(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthNamespace } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowNamespace - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipNamespace(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupNamespace + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthNamespace + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthNamespace = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowNamespace = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthNamespace = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowNamespace = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupNamespace = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/events/namespace.proto", fileDescriptorNamespace) -} - -var fileDescriptorNamespace = []byte{ - // 296 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, - 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0xeb, 0xe7, 0x25, - 0xe6, 0xa6, 0x16, 0x17, 0x24, 0x26, 0xa7, 0xea, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x22, - 0x94, 0xe9, 0x41, 0x94, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x65, 0xf5, 0x41, 0x2c, 0x88, - 0x42, 0x29, 0x07, 0x82, 0xb6, 0x80, 0xd5, 0x25, 0x95, 0xa6, 0xe9, 0x17, 0xe4, 0x94, 0xa6, 0x67, - 0xe6, 0xe9, 0xa7, 0x65, 0xa6, 0xe6, 0xa4, 0x14, 0x24, 0x96, 0x64, 0x40, 0x4c, 0x50, 0x5a, 0xc1, - 0xc8, 0xc5, 0xef, 0x07, 0xb3, 0xde, 0xb9, 0x28, 0x35, 0xb1, 0x24, 0x55, 0x48, 0x88, 0x8b, 0x05, - 0xe4, 0x22, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x30, 0x5b, 0xc8, 0x8d, 0x8b, 0x2d, 0x27, - 0x31, 0x29, 0x35, 0xa7, 0x58, 0x82, 0x49, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x4f, 0x0f, 0xc3, 0x8d, - 0x7a, 0x68, 0xe6, 0xe8, 0xf9, 0x80, 0x35, 0xb8, 0xe6, 0x95, 0x14, 0x55, 0x06, 0x41, 0x75, 0x4b, - 0x59, 0x72, 0x71, 0x23, 0x09, 0x0b, 0x09, 0x70, 0x31, 0x67, 0xa7, 0x56, 0x42, 0x6d, 0x02, 0x31, - 0x85, 0x44, 0xb8, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, 0x25, 0x98, 0xc0, 0x62, 0x10, 0x8e, 0x15, - 0x93, 0x05, 0x23, 0xaa, 0x53, 0x43, 0x0b, 0x52, 0xa8, 0xe2, 0x54, 0x88, 0x39, 0xd4, 0x76, 0xaa, - 0x2a, 0x92, 0x4b, 0x5d, 0x52, 0x73, 0x52, 0xb1, 0xbb, 0xd4, 0x29, 0xe0, 0xc4, 0x43, 0x39, 0x86, - 0x1b, 0x0f, 0xe5, 0x18, 0x1a, 0x1e, 0xc9, 0x31, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, - 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x0b, 0xbe, 0xc8, 0x31, 0x46, 0x19, 0x91, 0x90, 0x84, 0xac, 0x21, - 0x54, 0x04, 0x43, 0x04, 0x63, 0x12, 0x1b, 0x38, 0x66, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x50, 0x87, 0x59, 0x83, 0x02, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/events/namespace.proto b/vendor/github.com/containerd/containerd/api/events/namespace.proto index 45deae79a482c..53a8ee6306afd 100644 --- a/vendor/github.com/containerd/containerd/api/events/namespace.proto +++ b/vendor/github.com/containerd/containerd/api/events/namespace.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.events; diff --git a/vendor/github.com/containerd/containerd/api/events/snapshot.pb.go b/vendor/github.com/containerd/containerd/api/events/snapshot.pb.go index e1f8f5c5886bc..bec25c3a7c991 100644 --- a/vendor/github.com/containerd/containerd/api/events/snapshot.pb.go +++ b/vendor/github.com/containerd/containerd/api/events/snapshot.pb.go @@ -3,47 +3,145 @@ package events -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type SnapshotPrepare struct { - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Parent string `protobuf:"bytes,2,opt,name=parent,proto3" json:"parent,omitempty"` + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Parent string `protobuf:"bytes,2,opt,name=parent,proto3" json:"parent,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SnapshotPrepare) Reset() { *m = SnapshotPrepare{} } +func (*SnapshotPrepare) ProtoMessage() {} +func (*SnapshotPrepare) Descriptor() ([]byte, []int) { + return fileDescriptor_bd6c184d3d9aa5f2, []int{0} +} +func (m *SnapshotPrepare) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SnapshotPrepare) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SnapshotPrepare.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SnapshotPrepare) XXX_Merge(src proto.Message) { + xxx_messageInfo_SnapshotPrepare.Merge(m, src) +} +func (m *SnapshotPrepare) XXX_Size() int { + return m.Size() +} +func (m *SnapshotPrepare) XXX_DiscardUnknown() { + xxx_messageInfo_SnapshotPrepare.DiscardUnknown(m) } -func (m *SnapshotPrepare) Reset() { *m = SnapshotPrepare{} } -func (*SnapshotPrepare) ProtoMessage() {} -func (*SnapshotPrepare) Descriptor() ([]byte, []int) { return fileDescriptorSnapshot, []int{0} } +var xxx_messageInfo_SnapshotPrepare proto.InternalMessageInfo type SnapshotCommit struct { - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SnapshotCommit) Reset() { *m = SnapshotCommit{} } +func (*SnapshotCommit) ProtoMessage() {} +func (*SnapshotCommit) Descriptor() ([]byte, []int) { + return fileDescriptor_bd6c184d3d9aa5f2, []int{1} +} +func (m *SnapshotCommit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SnapshotCommit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SnapshotCommit.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SnapshotCommit) XXX_Merge(src proto.Message) { + xxx_messageInfo_SnapshotCommit.Merge(m, src) +} +func (m *SnapshotCommit) XXX_Size() int { + return m.Size() +} +func (m *SnapshotCommit) XXX_DiscardUnknown() { + xxx_messageInfo_SnapshotCommit.DiscardUnknown(m) } -func (m *SnapshotCommit) Reset() { *m = SnapshotCommit{} } -func (*SnapshotCommit) ProtoMessage() {} -func (*SnapshotCommit) Descriptor() ([]byte, []int) { return fileDescriptorSnapshot, []int{1} } +var xxx_messageInfo_SnapshotCommit proto.InternalMessageInfo type SnapshotRemove struct { - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SnapshotRemove) Reset() { *m = SnapshotRemove{} } +func (*SnapshotRemove) ProtoMessage() {} +func (*SnapshotRemove) Descriptor() ([]byte, []int) { + return fileDescriptor_bd6c184d3d9aa5f2, []int{2} +} +func (m *SnapshotRemove) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SnapshotRemove) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SnapshotRemove.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SnapshotRemove) XXX_Merge(src proto.Message) { + xxx_messageInfo_SnapshotRemove.Merge(m, src) +} +func (m *SnapshotRemove) XXX_Size() int { + return m.Size() +} +func (m *SnapshotRemove) XXX_DiscardUnknown() { + xxx_messageInfo_SnapshotRemove.DiscardUnknown(m) } -func (m *SnapshotRemove) Reset() { *m = SnapshotRemove{} } -func (*SnapshotRemove) ProtoMessage() {} -func (*SnapshotRemove) Descriptor() ([]byte, []int) { return fileDescriptorSnapshot, []int{2} } +var xxx_messageInfo_SnapshotRemove proto.InternalMessageInfo func init() { proto.RegisterType((*SnapshotPrepare)(nil), "containerd.events.SnapshotPrepare") @@ -51,6 +149,29 @@ func init() { proto.RegisterType((*SnapshotRemove)(nil), "containerd.events.SnapshotRemove") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/snapshot.proto", fileDescriptor_bd6c184d3d9aa5f2) +} + +var fileDescriptor_bd6c184d3d9aa5f2 = []byte{ + // 235 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4a, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0xeb, 0x17, 0xe7, + 0x25, 0x16, 0x14, 0x67, 0xe4, 0x97, 0xe8, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x22, 0x54, + 0xe9, 0x41, 0x54, 0x48, 0x39, 0x10, 0x34, 0x0e, 0xac, 0x35, 0xa9, 0x34, 0x4d, 0xbf, 0x20, 0xa7, + 0x34, 0x3d, 0x33, 0x4f, 0x3f, 0x2d, 0x33, 0x35, 0x27, 0xa5, 0x20, 0xb1, 0x24, 0x03, 0x62, 0xa8, + 0x92, 0x35, 0x17, 0x7f, 0x30, 0xd4, 0x9a, 0x80, 0xa2, 0xd4, 0x82, 0xc4, 0xa2, 0x54, 0x21, 0x01, + 0x2e, 0xe6, 0xec, 0xd4, 0x4a, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x10, 0x53, 0x48, 0x8c, + 0x8b, 0x0d, 0x24, 0x93, 0x57, 0x22, 0xc1, 0x04, 0x16, 0x84, 0xf2, 0x94, 0xcc, 0xb8, 0xf8, 0x60, + 0x9a, 0x9d, 0xf3, 0x73, 0x73, 0x33, 0x4b, 0xb0, 0xe8, 0x15, 0xe2, 0x62, 0xc9, 0x4b, 0xcc, 0x4d, + 0x85, 0xea, 0x04, 0xb3, 0x95, 0x94, 0x10, 0xfa, 0x82, 0x52, 0x73, 0xf3, 0xcb, 0xb0, 0xd8, 0xe9, + 0x14, 0x70, 0xe2, 0xa1, 0x1c, 0xc3, 0x8d, 0x87, 0x72, 0x0c, 0x0d, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, + 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x05, 0x5f, 0xe4, 0x18, 0xa3, + 0x8c, 0x48, 0x08, 0x47, 0x6b, 0x08, 0x15, 0xc1, 0x90, 0xc4, 0x06, 0xf6, 0xb3, 0x31, 0x20, 0x00, + 0x00, 0xff, 0xff, 0x69, 0x66, 0xa9, 0x2a, 0x86, 0x01, 0x00, 0x00, +} + // Field returns the value for the given fieldpath as a string, if defined. // If the value is not defined, the second value will be false. func (m *SnapshotPrepare) Field(fieldpath []string) (string, bool) { @@ -99,7 +220,7 @@ func (m *SnapshotRemove) Field(fieldpath []string) (string, bool) { func (m *SnapshotPrepare) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -107,29 +228,40 @@ func (m *SnapshotPrepare) Marshal() (dAtA []byte, err error) { } func (m *SnapshotPrepare) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SnapshotPrepare) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Key) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Parent) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Parent) + copy(dAtA[i:], m.Parent) i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Parent))) - i += copy(dAtA[i:], m.Parent) + i-- + dAtA[i] = 0x12 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *SnapshotCommit) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -137,29 +269,40 @@ func (m *SnapshotCommit) Marshal() (dAtA []byte, err error) { } func (m *SnapshotCommit) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SnapshotCommit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Key) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Name) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Name) + copy(dAtA[i:], m.Name) i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + i-- + dAtA[i] = 0x12 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *SnapshotRemove) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -167,29 +310,44 @@ func (m *SnapshotRemove) Marshal() (dAtA []byte, err error) { } func (m *SnapshotRemove) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SnapshotRemove) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Key) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Key) + copy(dAtA[i:], m.Key) i = encodeVarintSnapshot(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintSnapshot(dAtA []byte, offset int, v uint64) int { + offset -= sovSnapshot(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *SnapshotPrepare) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Key) @@ -200,10 +358,16 @@ func (m *SnapshotPrepare) Size() (n int) { if l > 0 { n += 1 + l + sovSnapshot(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *SnapshotCommit) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Key) @@ -214,28 +378,30 @@ func (m *SnapshotCommit) Size() (n int) { if l > 0 { n += 1 + l + sovSnapshot(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *SnapshotRemove) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Key) if l > 0 { n += 1 + l + sovSnapshot(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovSnapshot(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozSnapshot(x uint64) (n int) { return sovSnapshot(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -247,6 +413,7 @@ func (this *SnapshotPrepare) String() string { s := strings.Join([]string{`&SnapshotPrepare{`, `Key:` + fmt.Sprintf("%v", this.Key) + `,`, `Parent:` + fmt.Sprintf("%v", this.Parent) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -258,6 +425,7 @@ func (this *SnapshotCommit) String() string { s := strings.Join([]string{`&SnapshotCommit{`, `Key:` + fmt.Sprintf("%v", this.Key) + `,`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -268,6 +436,7 @@ func (this *SnapshotRemove) String() string { } s := strings.Join([]string{`&SnapshotRemove{`, `Key:` + fmt.Sprintf("%v", this.Key) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -295,7 +464,7 @@ func (m *SnapshotPrepare) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -323,7 +492,7 @@ func (m *SnapshotPrepare) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -333,6 +502,9 @@ func (m *SnapshotPrepare) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -352,7 +524,7 @@ func (m *SnapshotPrepare) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -362,6 +534,9 @@ func (m *SnapshotPrepare) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -373,12 +548,13 @@ func (m *SnapshotPrepare) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -403,7 +579,7 @@ func (m *SnapshotCommit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -431,7 +607,7 @@ func (m *SnapshotCommit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -441,6 +617,9 @@ func (m *SnapshotCommit) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -460,7 +639,7 @@ func (m *SnapshotCommit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -470,6 +649,9 @@ func (m *SnapshotCommit) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -481,12 +663,13 @@ func (m *SnapshotCommit) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -511,7 +694,7 @@ func (m *SnapshotRemove) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -539,7 +722,7 @@ func (m *SnapshotRemove) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -549,6 +732,9 @@ func (m *SnapshotRemove) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -560,12 +746,13 @@ func (m *SnapshotRemove) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -578,6 +765,7 @@ func (m *SnapshotRemove) Unmarshal(dAtA []byte) error { func skipSnapshot(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -609,10 +797,8 @@ func skipSnapshot(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -629,76 +815,34 @@ func skipSnapshot(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthSnapshot } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowSnapshot - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipSnapshot(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSnapshot + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthSnapshot + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthSnapshot = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowSnapshot = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthSnapshot = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSnapshot = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSnapshot = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/events/snapshot.proto", fileDescriptorSnapshot) -} - -var fileDescriptorSnapshot = []byte{ - // 235 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4a, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, - 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0xa7, 0x96, 0xa5, 0xe6, 0x95, 0x14, 0xeb, 0x17, 0xe7, - 0x25, 0x16, 0x14, 0x67, 0xe4, 0x97, 0xe8, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x22, 0x54, - 0xe9, 0x41, 0x54, 0x48, 0x39, 0x10, 0x34, 0x0e, 0xac, 0x35, 0xa9, 0x34, 0x4d, 0xbf, 0x20, 0xa7, - 0x34, 0x3d, 0x33, 0x4f, 0x3f, 0x2d, 0x33, 0x35, 0x27, 0xa5, 0x20, 0xb1, 0x24, 0x03, 0x62, 0xa8, - 0x92, 0x35, 0x17, 0x7f, 0x30, 0xd4, 0x9a, 0x80, 0xa2, 0xd4, 0x82, 0xc4, 0xa2, 0x54, 0x21, 0x01, - 0x2e, 0xe6, 0xec, 0xd4, 0x4a, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x10, 0x53, 0x48, 0x8c, - 0x8b, 0x0d, 0x24, 0x93, 0x57, 0x22, 0xc1, 0x04, 0x16, 0x84, 0xf2, 0x94, 0xcc, 0xb8, 0xf8, 0x60, - 0x9a, 0x9d, 0xf3, 0x73, 0x73, 0x33, 0x4b, 0xb0, 0xe8, 0x15, 0xe2, 0x62, 0xc9, 0x4b, 0xcc, 0x4d, - 0x85, 0xea, 0x04, 0xb3, 0x95, 0x94, 0x10, 0xfa, 0x82, 0x52, 0x73, 0xf3, 0xcb, 0xb0, 0xd8, 0xe9, - 0x14, 0x70, 0xe2, 0xa1, 0x1c, 0xc3, 0x8d, 0x87, 0x72, 0x0c, 0x0d, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, - 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x05, 0x5f, 0xe4, 0x18, 0xa3, - 0x8c, 0x48, 0x08, 0x47, 0x6b, 0x08, 0x15, 0xc1, 0x90, 0xc4, 0x06, 0xf6, 0xb3, 0x31, 0x20, 0x00, - 0x00, 0xff, 0xff, 0x69, 0x66, 0xa9, 0x2a, 0x86, 0x01, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/events/snapshot.proto b/vendor/github.com/containerd/containerd/api/events/snapshot.proto index 425eeec8e0e0d..eb1f06725c702 100644 --- a/vendor/github.com/containerd/containerd/api/events/snapshot.proto +++ b/vendor/github.com/containerd/containerd/api/events/snapshot.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.events; diff --git a/vendor/github.com/containerd/containerd/api/events/task.pb.go b/vendor/github.com/containerd/containerd/api/events/task.pb.go index b0a6c2c242ddb..f8f3a3f3d3070 100644 --- a/vendor/github.com/containerd/containerd/api/events/task.pb.go +++ b/vendor/github.com/containerd/containerd/api/events/task.pb.go @@ -3,24 +3,19 @@ package events -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import _ "github.com/gogo/protobuf/types" -import containerd_types "github.com/containerd/containerd/api/types" - -// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" - -import time "time" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + types "github.com/containerd/containerd/api/types" + proto "github.com/gogo/protobuf/proto" + _ "github.com/gogo/protobuf/types" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -28,113 +23,463 @@ var _ = fmt.Errorf var _ = math.Inf var _ = time.Kitchen +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type TaskCreate struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` - Rootfs []*containerd_types.Mount `protobuf:"bytes,3,rep,name=rootfs" json:"rootfs,omitempty"` - IO *TaskIO `protobuf:"bytes,4,opt,name=io" json:"io,omitempty"` - Checkpoint string `protobuf:"bytes,5,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` - Pid uint32 `protobuf:"varint,6,opt,name=pid,proto3" json:"pid,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` + Rootfs []*types.Mount `protobuf:"bytes,3,rep,name=rootfs,proto3" json:"rootfs,omitempty"` + IO *TaskIO `protobuf:"bytes,4,opt,name=io,proto3" json:"io,omitempty"` + Checkpoint string `protobuf:"bytes,5,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + Pid uint32 `protobuf:"varint,6,opt,name=pid,proto3" json:"pid,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskCreate) Reset() { *m = TaskCreate{} } +func (*TaskCreate) ProtoMessage() {} +func (*TaskCreate) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{0} +} +func (m *TaskCreate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskCreate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskCreate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskCreate) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskCreate.Merge(m, src) +} +func (m *TaskCreate) XXX_Size() int { + return m.Size() +} +func (m *TaskCreate) XXX_DiscardUnknown() { + xxx_messageInfo_TaskCreate.DiscardUnknown(m) } -func (m *TaskCreate) Reset() { *m = TaskCreate{} } -func (*TaskCreate) ProtoMessage() {} -func (*TaskCreate) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{0} } +var xxx_messageInfo_TaskCreate proto.InternalMessageInfo type TaskStart struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskStart) Reset() { *m = TaskStart{} } +func (*TaskStart) ProtoMessage() {} +func (*TaskStart) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{1} +} +func (m *TaskStart) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskStart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskStart.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskStart) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskStart.Merge(m, src) +} +func (m *TaskStart) XXX_Size() int { + return m.Size() +} +func (m *TaskStart) XXX_DiscardUnknown() { + xxx_messageInfo_TaskStart.DiscardUnknown(m) } -func (m *TaskStart) Reset() { *m = TaskStart{} } -func (*TaskStart) ProtoMessage() {} -func (*TaskStart) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{1} } +var xxx_messageInfo_TaskStart proto.InternalMessageInfo type TaskDelete struct { ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` ExitStatus uint32 `protobuf:"varint,3,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` - ExitedAt time.Time `protobuf:"bytes,4,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` + ExitedAt time.Time `protobuf:"bytes,4,opt,name=exited_at,json=exitedAt,proto3,stdtime" json:"exited_at"` + // id is the specific exec. By default if omitted will be `""` thus matches + // the init exec of the task matching `container_id`. + ID string `protobuf:"bytes,5,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskDelete) Reset() { *m = TaskDelete{} } +func (*TaskDelete) ProtoMessage() {} +func (*TaskDelete) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{2} +} +func (m *TaskDelete) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskDelete) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskDelete.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskDelete) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskDelete.Merge(m, src) +} +func (m *TaskDelete) XXX_Size() int { + return m.Size() +} +func (m *TaskDelete) XXX_DiscardUnknown() { + xxx_messageInfo_TaskDelete.DiscardUnknown(m) } -func (m *TaskDelete) Reset() { *m = TaskDelete{} } -func (*TaskDelete) ProtoMessage() {} -func (*TaskDelete) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{2} } +var xxx_messageInfo_TaskDelete proto.InternalMessageInfo type TaskIO struct { - Stdin string `protobuf:"bytes,1,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,2,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,3,opt,name=stderr,proto3" json:"stderr,omitempty"` - Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` + Stdin string `protobuf:"bytes,1,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,2,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,3,opt,name=stderr,proto3" json:"stderr,omitempty"` + Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskIO) Reset() { *m = TaskIO{} } +func (*TaskIO) ProtoMessage() {} +func (*TaskIO) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{3} +} +func (m *TaskIO) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskIO) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskIO.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskIO) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskIO.Merge(m, src) +} +func (m *TaskIO) XXX_Size() int { + return m.Size() +} +func (m *TaskIO) XXX_DiscardUnknown() { + xxx_messageInfo_TaskIO.DiscardUnknown(m) } -func (m *TaskIO) Reset() { *m = TaskIO{} } -func (*TaskIO) ProtoMessage() {} -func (*TaskIO) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{3} } +var xxx_messageInfo_TaskIO proto.InternalMessageInfo type TaskExit struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` - ExitStatus uint32 `protobuf:"varint,4,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` - ExitedAt time.Time `protobuf:"bytes,5,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + ExitStatus uint32 `protobuf:"varint,4,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,5,opt,name=exited_at,json=exitedAt,proto3,stdtime" json:"exited_at"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskExit) Reset() { *m = TaskExit{} } +func (*TaskExit) ProtoMessage() {} +func (*TaskExit) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{4} +} +func (m *TaskExit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskExit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskExit.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskExit) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskExit.Merge(m, src) +} +func (m *TaskExit) XXX_Size() int { + return m.Size() +} +func (m *TaskExit) XXX_DiscardUnknown() { + xxx_messageInfo_TaskExit.DiscardUnknown(m) } -func (m *TaskExit) Reset() { *m = TaskExit{} } -func (*TaskExit) ProtoMessage() {} -func (*TaskExit) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{4} } +var xxx_messageInfo_TaskExit proto.InternalMessageInfo type TaskOOM struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskOOM) Reset() { *m = TaskOOM{} } +func (*TaskOOM) ProtoMessage() {} +func (*TaskOOM) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{5} +} +func (m *TaskOOM) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskOOM) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskOOM.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskOOM) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskOOM.Merge(m, src) +} +func (m *TaskOOM) XXX_Size() int { + return m.Size() +} +func (m *TaskOOM) XXX_DiscardUnknown() { + xxx_messageInfo_TaskOOM.DiscardUnknown(m) } -func (m *TaskOOM) Reset() { *m = TaskOOM{} } -func (*TaskOOM) ProtoMessage() {} -func (*TaskOOM) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{5} } +var xxx_messageInfo_TaskOOM proto.InternalMessageInfo type TaskExecAdded struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskExecAdded) Reset() { *m = TaskExecAdded{} } +func (*TaskExecAdded) ProtoMessage() {} +func (*TaskExecAdded) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{6} +} +func (m *TaskExecAdded) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskExecAdded) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskExecAdded.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskExecAdded) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskExecAdded.Merge(m, src) +} +func (m *TaskExecAdded) XXX_Size() int { + return m.Size() +} +func (m *TaskExecAdded) XXX_DiscardUnknown() { + xxx_messageInfo_TaskExecAdded.DiscardUnknown(m) } -func (m *TaskExecAdded) Reset() { *m = TaskExecAdded{} } -func (*TaskExecAdded) ProtoMessage() {} -func (*TaskExecAdded) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{6} } +var xxx_messageInfo_TaskExecAdded proto.InternalMessageInfo type TaskExecStarted struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` - Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskExecStarted) Reset() { *m = TaskExecStarted{} } +func (*TaskExecStarted) ProtoMessage() {} +func (*TaskExecStarted) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{7} +} +func (m *TaskExecStarted) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskExecStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskExecStarted.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskExecStarted) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskExecStarted.Merge(m, src) +} +func (m *TaskExecStarted) XXX_Size() int { + return m.Size() +} +func (m *TaskExecStarted) XXX_DiscardUnknown() { + xxx_messageInfo_TaskExecStarted.DiscardUnknown(m) } -func (m *TaskExecStarted) Reset() { *m = TaskExecStarted{} } -func (*TaskExecStarted) ProtoMessage() {} -func (*TaskExecStarted) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{7} } +var xxx_messageInfo_TaskExecStarted proto.InternalMessageInfo type TaskPaused struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskPaused) Reset() { *m = TaskPaused{} } +func (*TaskPaused) ProtoMessage() {} +func (*TaskPaused) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{8} +} +func (m *TaskPaused) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskPaused) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskPaused.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskPaused) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskPaused.Merge(m, src) +} +func (m *TaskPaused) XXX_Size() int { + return m.Size() +} +func (m *TaskPaused) XXX_DiscardUnknown() { + xxx_messageInfo_TaskPaused.DiscardUnknown(m) } -func (m *TaskPaused) Reset() { *m = TaskPaused{} } -func (*TaskPaused) ProtoMessage() {} -func (*TaskPaused) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{8} } +var xxx_messageInfo_TaskPaused proto.InternalMessageInfo type TaskResumed struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskResumed) Reset() { *m = TaskResumed{} } +func (*TaskResumed) ProtoMessage() {} +func (*TaskResumed) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{9} +} +func (m *TaskResumed) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskResumed) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskResumed.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskResumed) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskResumed.Merge(m, src) +} +func (m *TaskResumed) XXX_Size() int { + return m.Size() +} +func (m *TaskResumed) XXX_DiscardUnknown() { + xxx_messageInfo_TaskResumed.DiscardUnknown(m) } -func (m *TaskResumed) Reset() { *m = TaskResumed{} } -func (*TaskResumed) ProtoMessage() {} -func (*TaskResumed) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{9} } +var xxx_messageInfo_TaskResumed proto.InternalMessageInfo type TaskCheckpointed struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - Checkpoint string `protobuf:"bytes,2,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + Checkpoint string `protobuf:"bytes,2,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TaskCheckpointed) Reset() { *m = TaskCheckpointed{} } +func (*TaskCheckpointed) ProtoMessage() {} +func (*TaskCheckpointed) Descriptor() ([]byte, []int) { + return fileDescriptor_8db0813f7adfb63c, []int{10} +} +func (m *TaskCheckpointed) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskCheckpointed) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskCheckpointed.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskCheckpointed) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskCheckpointed.Merge(m, src) +} +func (m *TaskCheckpointed) XXX_Size() int { + return m.Size() +} +func (m *TaskCheckpointed) XXX_DiscardUnknown() { + xxx_messageInfo_TaskCheckpointed.DiscardUnknown(m) } -func (m *TaskCheckpointed) Reset() { *m = TaskCheckpointed{} } -func (*TaskCheckpointed) ProtoMessage() {} -func (*TaskCheckpointed) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{10} } +var xxx_messageInfo_TaskCheckpointed proto.InternalMessageInfo func init() { proto.RegisterType((*TaskCreate)(nil), "containerd.events.TaskCreate") @@ -150,6 +495,55 @@ func init() { proto.RegisterType((*TaskCheckpointed)(nil), "containerd.events.TaskCheckpointed") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/events/task.proto", fileDescriptor_8db0813f7adfb63c) +} + +var fileDescriptor_8db0813f7adfb63c = []byte{ + // 644 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0xc7, 0x63, 0xa7, 0x75, 0xd3, 0x09, 0x55, 0x8b, 0x55, 0x95, 0x90, 0x83, 0x1d, 0x99, 0x4b, + 0x4e, 0xb6, 0x08, 0x12, 0x17, 0x84, 0xd4, 0xa4, 0xe1, 0x90, 0x43, 0x95, 0xe2, 0xf6, 0x50, 0x71, + 0x89, 0x36, 0xd9, 0x4d, 0xb2, 0x34, 0xf1, 0x5a, 0xf6, 0x18, 0x15, 0x89, 0x03, 0x8f, 0xc0, 0x23, + 0xf0, 0x38, 0x3d, 0x20, 0xc4, 0x91, 0x53, 0xa0, 0x7e, 0x00, 0x4e, 0x3c, 0x00, 0x5a, 0xaf, 0x93, + 0xb6, 0x54, 0x7c, 0x59, 0xe2, 0x94, 0x9d, 0xd9, 0xd9, 0xff, 0xec, 0xfc, 0x76, 0x3c, 0x81, 0xc7, + 0x13, 0x8e, 0xd3, 0x64, 0xe8, 0x8e, 0xc4, 0xdc, 0x1b, 0x89, 0x00, 0x09, 0x0f, 0x58, 0x44, 0xaf, + 0x2f, 0x49, 0xc8, 0x3d, 0xf6, 0x8a, 0x05, 0x18, 0x7b, 0x48, 0xe2, 0x33, 0x37, 0x8c, 0x04, 0x0a, + 0xf3, 0xee, 0x55, 0x84, 0xab, 0x76, 0xeb, 0xbb, 0x13, 0x31, 0x11, 0xd9, 0xae, 0x27, 0x57, 0x2a, + 0xb0, 0x6e, 0x4f, 0x84, 0x98, 0xcc, 0x98, 0x97, 0x59, 0xc3, 0x64, 0xec, 0x21, 0x9f, 0xb3, 0x18, + 0xc9, 0x3c, 0xcc, 0x03, 0xfe, 0xee, 0x06, 0xf8, 0x3a, 0x64, 0xb1, 0x37, 0x17, 0x49, 0x80, 0xf9, + 0xb9, 0xfd, 0x3f, 0x9e, 0x5b, 0xa5, 0x0c, 0x67, 0xc9, 0x84, 0x07, 0xde, 0x98, 0xb3, 0x19, 0x0d, + 0x09, 0x4e, 0x95, 0x82, 0xf3, 0x4d, 0x03, 0x38, 0x21, 0xf1, 0xd9, 0x41, 0xc4, 0x08, 0x32, 0xb3, + 0x05, 0x77, 0x56, 0x87, 0x07, 0x9c, 0xd6, 0xb4, 0x86, 0xd6, 0xdc, 0xec, 0x6c, 0xa7, 0x0b, 0xbb, + 0x7a, 0xb0, 0xf4, 0xf7, 0xba, 0x7e, 0x75, 0x15, 0xd4, 0xa3, 0xe6, 0x1e, 0x18, 0xc3, 0x24, 0xa0, + 0x33, 0x56, 0xd3, 0x65, 0xb4, 0x9f, 0x5b, 0xa6, 0x07, 0x46, 0x24, 0x04, 0x8e, 0xe3, 0x5a, 0xb9, + 0x51, 0x6e, 0x56, 0x5b, 0xf7, 0xdc, 0x6b, 0xbc, 0xb2, 0x5a, 0xdc, 0x43, 0x59, 0x8b, 0x9f, 0x87, + 0x99, 0x0f, 0x41, 0xe7, 0xa2, 0xb6, 0xd6, 0xd0, 0x9a, 0xd5, 0xd6, 0x7d, 0xf7, 0x16, 0x5c, 0x57, + 0xde, 0xb3, 0xd7, 0xef, 0x18, 0xe9, 0xc2, 0xd6, 0x7b, 0x7d, 0x5f, 0xe7, 0xc2, 0xb4, 0x00, 0x46, + 0x53, 0x36, 0x3a, 0x0b, 0x05, 0x0f, 0xb0, 0xb6, 0x9e, 0xe5, 0xbf, 0xe6, 0x31, 0x77, 0xa0, 0x1c, + 0x72, 0x5a, 0x33, 0x1a, 0x5a, 0x73, 0xcb, 0x97, 0x4b, 0xe7, 0x39, 0x6c, 0x4a, 0x9d, 0x63, 0x24, + 0x11, 0x16, 0x2a, 0x37, 0x97, 0xd4, 0xaf, 0x24, 0x3f, 0xe6, 0x0c, 0xbb, 0x6c, 0xc6, 0x0a, 0x32, + 0xbc, 0x25, 0x6a, 0xda, 0x50, 0x65, 0xe7, 0x1c, 0x07, 0x31, 0x12, 0x4c, 0x24, 0x42, 0xb9, 0x03, + 0xd2, 0x75, 0x9c, 0x79, 0xcc, 0x36, 0x6c, 0x4a, 0x8b, 0xd1, 0x01, 0xc1, 0x1c, 0x5a, 0xdd, 0x55, + 0x8d, 0xe6, 0x2e, 0x5f, 0xdd, 0x3d, 0x59, 0x36, 0x5a, 0xa7, 0x72, 0xb1, 0xb0, 0x4b, 0xef, 0xbe, + 0xd8, 0x9a, 0x5f, 0x51, 0xc7, 0xda, 0x68, 0xee, 0x81, 0xce, 0xa9, 0xa2, 0x96, 0x53, 0xed, 0xfa, + 0x3a, 0xa7, 0xce, 0x4b, 0x30, 0x14, 0x6b, 0x73, 0x17, 0xd6, 0x63, 0xa4, 0x3c, 0x50, 0x45, 0xf8, + 0xca, 0x90, 0x2f, 0x1e, 0x23, 0x15, 0x09, 0x2e, 0x5f, 0x5c, 0x59, 0xb9, 0x9f, 0x45, 0x51, 0x76, + 0x5d, 0xe5, 0x67, 0x51, 0x64, 0xd6, 0xa1, 0x82, 0x2c, 0x9a, 0xf3, 0x80, 0xcc, 0xb2, 0x9b, 0x56, + 0xfc, 0x95, 0xed, 0x7c, 0xd0, 0xa0, 0x22, 0x93, 0x3d, 0x3b, 0xe7, 0x58, 0xb0, 0xfd, 0xf4, 0x9c, + 0xdc, 0x8d, 0x22, 0x96, 0x48, 0xcb, 0xbf, 0x44, 0xba, 0xf6, 0x7b, 0xa4, 0xeb, 0x45, 0x90, 0x3a, + 0x4f, 0x61, 0x43, 0x56, 0xd3, 0xef, 0x1f, 0x16, 0x29, 0xc6, 0x99, 0xc2, 0x96, 0x82, 0xc1, 0x46, + 0x6d, 0x4a, 0x19, 0x2d, 0x44, 0xe4, 0x01, 0x6c, 0xb0, 0x73, 0x36, 0x1a, 0xac, 0xb0, 0x40, 0xba, + 0xb0, 0x0d, 0xa9, 0xd9, 0xeb, 0xfa, 0x86, 0xdc, 0xea, 0x51, 0xe7, 0x0d, 0x6c, 0x2f, 0x33, 0x65, + 0xdf, 0xc2, 0x7f, 0xcc, 0x75, 0xfb, 0x29, 0x9c, 0x7d, 0xf5, 0xc5, 0x1c, 0x91, 0x24, 0x2e, 0x96, + 0xd8, 0x69, 0x43, 0x55, 0x2a, 0xf8, 0x2c, 0x4e, 0xe6, 0x05, 0x25, 0xc6, 0xb0, 0x93, 0x8d, 0xbe, + 0xd5, 0xb8, 0x28, 0xc8, 0xe0, 0xe6, 0x10, 0xd2, 0x7f, 0x1e, 0x42, 0x9d, 0xa3, 0x8b, 0x4b, 0xab, + 0xf4, 0xf9, 0xd2, 0x2a, 0xbd, 0x4d, 0x2d, 0xed, 0x22, 0xb5, 0xb4, 0x4f, 0xa9, 0xa5, 0x7d, 0x4d, + 0x2d, 0xed, 0xfd, 0x77, 0x4b, 0x7b, 0xd1, 0xfa, 0x87, 0x7f, 0x9f, 0x27, 0xea, 0xe7, 0xb4, 0x74, + 0x5a, 0x1e, 0x1a, 0x59, 0x47, 0x3e, 0xfa, 0x11, 0x00, 0x00, 0xff, 0xff, 0xc5, 0x58, 0x0f, 0xec, + 0xbe, 0x06, 0x00, 0x00, +} + // Field returns the value for the given fieldpath as a string, if defined. // If the value is not defined, the second value will be false. func (m *TaskCreate) Field(fieldpath []string) (string, bool) { @@ -210,6 +604,8 @@ func (m *TaskDelete) Field(fieldpath []string) (string, bool) { // unhandled: exited_at case "container_id": return string(m.ContainerID), len(m.ContainerID) > 0 + case "id": + return string(m.ID), len(m.ID) > 0 } return "", false } @@ -346,7 +742,7 @@ func (m *TaskCheckpointed) Field(fieldpath []string) (string, bool) { func (m *TaskCreate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -354,62 +750,78 @@ func (m *TaskCreate) Marshal() (dAtA []byte, err error) { } func (m *TaskCreate) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskCreate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Bundle) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.Bundle))) - i += copy(dAtA[i:], m.Bundle) + if m.Pid != 0 { + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x30 } - if len(m.Rootfs) > 0 { - for _, msg := range m.Rootfs { - dAtA[i] = 0x1a - i++ - i = encodeVarintTask(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if len(m.Checkpoint) > 0 { + i -= len(m.Checkpoint) + copy(dAtA[i:], m.Checkpoint) + i = encodeVarintTask(dAtA, i, uint64(len(m.Checkpoint))) + i-- + dAtA[i] = 0x2a + } + if m.IO != nil { + { + size, err := m.IO.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintTask(dAtA, i, uint64(size)) } - } - if m.IO != nil { + i-- dAtA[i] = 0x22 - i++ - i = encodeVarintTask(dAtA, i, uint64(m.IO.Size())) - n1, err := m.IO.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + if len(m.Rootfs) > 0 { + for iNdEx := len(m.Rootfs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Rootfs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTask(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } - i += n1 } - if len(m.Checkpoint) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.Checkpoint))) - i += copy(dAtA[i:], m.Checkpoint) + if len(m.Bundle) > 0 { + i -= len(m.Bundle) + copy(dAtA[i:], m.Bundle) + i = encodeVarintTask(dAtA, i, uint64(len(m.Bundle))) + i-- + dAtA[i] = 0x12 } - if m.Pid != 0 { - dAtA[i] = 0x30 - i++ - i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *TaskStart) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -417,28 +829,38 @@ func (m *TaskStart) Marshal() (dAtA []byte, err error) { } func (m *TaskStart) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskStart) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Pid != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 } - return i, nil + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *TaskDelete) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -446,41 +868,58 @@ func (m *TaskDelete) Marshal() (dAtA []byte, err error) { } func (m *TaskDelete) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskDelete) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if m.Pid != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0x2a } + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExitedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintTask(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x22 if m.ExitStatus != 0 { - dAtA[i] = 0x18 - i++ i = encodeVarintTask(dAtA, i, uint64(m.ExitStatus)) + i-- + dAtA[i] = 0x18 } - dAtA[i] = 0x22 - i++ - i = encodeVarintTask(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) - n2, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) - if err != nil { - return 0, err + if m.Pid != 0 { + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 } - i += n2 - return i, nil + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *TaskIO) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -488,45 +927,57 @@ func (m *TaskIO) Marshal() (dAtA []byte, err error) { } func (m *TaskIO) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskIO) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Stdin) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.Stdin))) - i += copy(dAtA[i:], m.Stdin) - } - if len(m.Stdout) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.Stdout))) - i += copy(dAtA[i:], m.Stdout) - } - if len(m.Stderr) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.Stderr))) - i += copy(dAtA[i:], m.Stderr) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Terminal { - dAtA[i] = 0x20 - i++ + i-- if m.Terminal { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x20 + } + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = encodeVarintTask(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x1a + } + if len(m.Stdout) > 0 { + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) + i = encodeVarintTask(dAtA, i, uint64(len(m.Stdout))) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.Stdin) > 0 { + i -= len(m.Stdin) + copy(dAtA[i:], m.Stdin) + i = encodeVarintTask(dAtA, i, uint64(len(m.Stdin))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *TaskExit) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -534,47 +985,58 @@ func (m *TaskExit) Marshal() (dAtA []byte, err error) { } func (m *TaskExit) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskExit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.ID) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + n3, err3 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExitedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt):]) + if err3 != nil { + return 0, err3 + } + i -= n3 + i = encodeVarintTask(dAtA, i, uint64(n3)) + i-- + dAtA[i] = 0x2a + if m.ExitStatus != 0 { + i = encodeVarintTask(dAtA, i, uint64(m.ExitStatus)) + i-- + dAtA[i] = 0x20 } if m.Pid != 0 { - dAtA[i] = 0x18 - i++ i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x18 } - if m.ExitStatus != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintTask(dAtA, i, uint64(m.ExitStatus)) + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0x12 } - dAtA[i] = 0x2a - i++ - i = encodeVarintTask(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) - n3, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) - if err != nil { - return 0, err + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - i += n3 - return i, nil + return len(dAtA) - i, nil } func (m *TaskOOM) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -582,23 +1044,33 @@ func (m *TaskOOM) Marshal() (dAtA []byte, err error) { } func (m *TaskOOM) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskOOM) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *TaskExecAdded) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -606,29 +1078,40 @@ func (m *TaskExecAdded) Marshal() (dAtA []byte, err error) { } func (m *TaskExecAdded) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskExecAdded) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ExecID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) i = encodeVarintTask(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *TaskExecStarted) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -636,34 +1119,45 @@ func (m *TaskExecStarted) Marshal() (dAtA []byte, err error) { } func (m *TaskExecStarted) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskExecStarted) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Pid != 0 { + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x18 } if len(m.ExecID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) i = encodeVarintTask(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) + i-- + dAtA[i] = 0x12 } - if m.Pid != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *TaskPaused) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -671,23 +1165,33 @@ func (m *TaskPaused) Marshal() (dAtA []byte, err error) { } func (m *TaskPaused) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskPaused) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *TaskResumed) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -695,23 +1199,33 @@ func (m *TaskResumed) Marshal() (dAtA []byte, err error) { } func (m *TaskResumed) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskResumed) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *TaskCheckpointed) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -719,35 +1233,51 @@ func (m *TaskCheckpointed) Marshal() (dAtA []byte, err error) { } func (m *TaskCheckpointed) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskCheckpointed) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Checkpoint) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Checkpoint) + copy(dAtA[i:], m.Checkpoint) i = encodeVarintTask(dAtA, i, uint64(len(m.Checkpoint))) - i += copy(dAtA[i:], m.Checkpoint) + i-- + dAtA[i] = 0x12 + } + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintTask(dAtA []byte, offset int, v uint64) int { + offset -= sovTask(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *TaskCreate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -775,10 +1305,16 @@ func (m *TaskCreate) Size() (n int) { if m.Pid != 0 { n += 1 + sovTask(uint64(m.Pid)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *TaskStart) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -788,10 +1324,16 @@ func (m *TaskStart) Size() (n int) { if m.Pid != 0 { n += 1 + sovTask(uint64(m.Pid)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *TaskDelete) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -804,12 +1346,22 @@ func (m *TaskDelete) Size() (n int) { if m.ExitStatus != 0 { n += 1 + sovTask(uint64(m.ExitStatus)) } - l = types.SizeOfStdTime(m.ExitedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt) n += 1 + l + sovTask(uint64(l)) + l = len(m.ID) + if l > 0 { + n += 1 + l + sovTask(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *TaskIO) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Stdin) @@ -827,10 +1379,16 @@ func (m *TaskIO) Size() (n int) { if m.Terminal { n += 2 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *TaskExit) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -847,22 +1405,34 @@ func (m *TaskExit) Size() (n int) { if m.ExitStatus != 0 { n += 1 + sovTask(uint64(m.ExitStatus)) } - l = types.SizeOfStdTime(m.ExitedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt) n += 1 + l + sovTask(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *TaskOOM) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) if l > 0 { n += 1 + l + sovTask(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *TaskExecAdded) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -873,10 +1443,16 @@ func (m *TaskExecAdded) Size() (n int) { if l > 0 { n += 1 + l + sovTask(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *TaskExecStarted) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -890,30 +1466,48 @@ func (m *TaskExecStarted) Size() (n int) { if m.Pid != 0 { n += 1 + sovTask(uint64(m.Pid)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *TaskPaused) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) if l > 0 { n += 1 + l + sovTask(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *TaskResumed) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) if l > 0 { n += 1 + l + sovTask(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *TaskCheckpointed) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -924,18 +1518,14 @@ func (m *TaskCheckpointed) Size() (n int) { if l > 0 { n += 1 + l + sovTask(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovTask(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozTask(x uint64) (n int) { return sovTask(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -944,13 +1534,19 @@ func (this *TaskCreate) String() string { if this == nil { return "nil" } + repeatedStringForRootfs := "[]*Mount{" + for _, f := range this.Rootfs { + repeatedStringForRootfs += strings.Replace(fmt.Sprintf("%v", f), "Mount", "types.Mount", 1) + "," + } + repeatedStringForRootfs += "}" s := strings.Join([]string{`&TaskCreate{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `Bundle:` + fmt.Sprintf("%v", this.Bundle) + `,`, - `Rootfs:` + strings.Replace(fmt.Sprintf("%v", this.Rootfs), "Mount", "containerd_types.Mount", 1) + `,`, - `IO:` + strings.Replace(fmt.Sprintf("%v", this.IO), "TaskIO", "TaskIO", 1) + `,`, + `Rootfs:` + repeatedStringForRootfs + `,`, + `IO:` + strings.Replace(this.IO.String(), "TaskIO", "TaskIO", 1) + `,`, `Checkpoint:` + fmt.Sprintf("%v", this.Checkpoint) + `,`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -962,6 +1558,7 @@ func (this *TaskStart) String() string { s := strings.Join([]string{`&TaskStart{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -974,7 +1571,9 @@ func (this *TaskDelete) String() string { `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, - `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ExitedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -988,6 +1587,7 @@ func (this *TaskIO) String() string { `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1001,7 +1601,8 @@ func (this *TaskExit) String() string { `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, - `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ExitedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1012,6 +1613,7 @@ func (this *TaskOOM) String() string { } s := strings.Join([]string{`&TaskOOM{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1023,6 +1625,7 @@ func (this *TaskExecAdded) String() string { s := strings.Join([]string{`&TaskExecAdded{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1035,6 +1638,7 @@ func (this *TaskExecStarted) String() string { `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1045,6 +1649,7 @@ func (this *TaskPaused) String() string { } s := strings.Join([]string{`&TaskPaused{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1055,6 +1660,7 @@ func (this *TaskResumed) String() string { } s := strings.Join([]string{`&TaskResumed{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1066,6 +1672,7 @@ func (this *TaskCheckpointed) String() string { s := strings.Join([]string{`&TaskCheckpointed{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `Checkpoint:` + fmt.Sprintf("%v", this.Checkpoint) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1093,7 +1700,7 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1121,7 +1728,7 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1131,6 +1738,9 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1150,7 +1760,7 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1160,6 +1770,9 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1179,7 +1792,7 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1188,10 +1801,13 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Rootfs = append(m.Rootfs, &containerd_types.Mount{}) + m.Rootfs = append(m.Rootfs, &types.Mount{}) if err := m.Rootfs[len(m.Rootfs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -1210,7 +1826,7 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1219,6 +1835,9 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1243,7 +1862,7 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1253,6 +1872,9 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1272,7 +1894,7 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -1283,12 +1905,13 @@ func (m *TaskCreate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1313,7 +1936,7 @@ func (m *TaskStart) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1341,7 +1964,7 @@ func (m *TaskStart) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1351,6 +1974,9 @@ func (m *TaskStart) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1370,7 +1996,7 @@ func (m *TaskStart) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -1381,12 +2007,13 @@ func (m *TaskStart) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1411,7 +2038,7 @@ func (m *TaskDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1439,7 +2066,7 @@ func (m *TaskDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1449,6 +2076,9 @@ func (m *TaskDelete) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1468,7 +2098,7 @@ func (m *TaskDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -1487,7 +2117,7 @@ func (m *TaskDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ExitStatus |= (uint32(b) & 0x7F) << shift + m.ExitStatus |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -1506,7 +2136,7 @@ func (m *TaskDelete) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1515,25 +2145,61 @@ func (m *TaskDelete) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTask + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTask + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTask(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1558,7 +2224,7 @@ func (m *TaskIO) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1586,7 +2252,7 @@ func (m *TaskIO) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1596,6 +2262,9 @@ func (m *TaskIO) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1615,7 +2284,7 @@ func (m *TaskIO) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1625,6 +2294,9 @@ func (m *TaskIO) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1644,7 +2316,7 @@ func (m *TaskIO) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1654,6 +2326,9 @@ func (m *TaskIO) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1673,7 +2348,7 @@ func (m *TaskIO) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1685,12 +2360,13 @@ func (m *TaskIO) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1715,7 +2391,7 @@ func (m *TaskExit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1743,7 +2419,7 @@ func (m *TaskExit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1753,6 +2429,9 @@ func (m *TaskExit) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1772,7 +2451,7 @@ func (m *TaskExit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1782,6 +2461,9 @@ func (m *TaskExit) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1801,7 +2483,7 @@ func (m *TaskExit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -1820,7 +2502,7 @@ func (m *TaskExit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ExitStatus |= (uint32(b) & 0x7F) << shift + m.ExitStatus |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -1839,7 +2521,7 @@ func (m *TaskExit) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1848,10 +2530,13 @@ func (m *TaskExit) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1861,12 +2546,13 @@ func (m *TaskExit) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1891,7 +2577,7 @@ func (m *TaskOOM) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1919,7 +2605,7 @@ func (m *TaskOOM) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1929,6 +2615,9 @@ func (m *TaskOOM) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1940,12 +2629,13 @@ func (m *TaskOOM) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1970,7 +2660,7 @@ func (m *TaskExecAdded) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1998,7 +2688,7 @@ func (m *TaskExecAdded) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2008,6 +2698,9 @@ func (m *TaskExecAdded) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2027,7 +2720,7 @@ func (m *TaskExecAdded) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2037,6 +2730,9 @@ func (m *TaskExecAdded) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2048,12 +2744,13 @@ func (m *TaskExecAdded) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2078,7 +2775,7 @@ func (m *TaskExecStarted) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2106,7 +2803,7 @@ func (m *TaskExecStarted) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2116,6 +2813,9 @@ func (m *TaskExecStarted) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2135,7 +2835,7 @@ func (m *TaskExecStarted) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2145,6 +2845,9 @@ func (m *TaskExecStarted) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2164,7 +2867,7 @@ func (m *TaskExecStarted) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -2175,12 +2878,13 @@ func (m *TaskExecStarted) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2205,7 +2909,7 @@ func (m *TaskPaused) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2233,7 +2937,7 @@ func (m *TaskPaused) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2243,6 +2947,9 @@ func (m *TaskPaused) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2254,12 +2961,13 @@ func (m *TaskPaused) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2284,7 +2992,7 @@ func (m *TaskResumed) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2312,7 +3020,7 @@ func (m *TaskResumed) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2322,6 +3030,9 @@ func (m *TaskResumed) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2333,12 +3044,13 @@ func (m *TaskResumed) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2363,7 +3075,7 @@ func (m *TaskCheckpointed) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2391,7 +3103,7 @@ func (m *TaskCheckpointed) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2401,6 +3113,9 @@ func (m *TaskCheckpointed) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2420,7 +3135,7 @@ func (m *TaskCheckpointed) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2430,6 +3145,9 @@ func (m *TaskCheckpointed) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2441,12 +3159,13 @@ func (m *TaskCheckpointed) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2459,6 +3178,7 @@ func (m *TaskCheckpointed) Unmarshal(dAtA []byte) error { func skipTask(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -2490,10 +3210,8 @@ func skipTask(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -2510,101 +3228,34 @@ func skipTask(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthTask } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTask - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipTask(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTask + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthTask + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthTask = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowTask = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthTask = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTask = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTask = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/events/task.proto", fileDescriptorTask) -} - -var fileDescriptorTask = []byte{ - // 637 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x95, 0xcd, 0x6e, 0xd3, 0x40, - 0x10, 0xc7, 0x63, 0xa7, 0x75, 0x93, 0x09, 0x55, 0x8b, 0x55, 0x41, 0xc8, 0xc1, 0x8e, 0xcc, 0x25, - 0x27, 0x5b, 0x04, 0x89, 0x0b, 0x42, 0x6a, 0xd2, 0x70, 0xc8, 0xa1, 0x4a, 0x71, 0x7b, 0xa8, 0xb8, - 0x44, 0x4e, 0x76, 0x93, 0x2c, 0x8d, 0xbd, 0x96, 0x3d, 0x46, 0x45, 0xe2, 0xc0, 0x23, 0xf0, 0x08, - 0x3c, 0x05, 0xcf, 0xd0, 0x03, 0x07, 0x8e, 0x9c, 0x02, 0xf5, 0x03, 0x70, 0xe2, 0x01, 0xd0, 0x7a, - 0x1d, 0xb7, 0x50, 0xf1, 0x65, 0x89, 0x53, 0x76, 0x66, 0x67, 0xff, 0x33, 0xf3, 0xdb, 0xc9, 0x1a, - 0x1e, 0xcd, 0x19, 0x2e, 0x92, 0x89, 0x3d, 0xe5, 0xbe, 0x33, 0xe5, 0x01, 0x7a, 0x2c, 0xa0, 0x11, - 0xb9, 0xbe, 0xf4, 0x42, 0xe6, 0xd0, 0x97, 0x34, 0xc0, 0xd8, 0x41, 0x2f, 0x3e, 0xb3, 0xc3, 0x88, - 0x23, 0xd7, 0x6f, 0x5f, 0x45, 0xd8, 0x72, 0xb7, 0xb5, 0x37, 0xe7, 0x73, 0x9e, 0xed, 0x3a, 0x62, - 0x25, 0x03, 0x5b, 0xe6, 0x9c, 0xf3, 0xf9, 0x92, 0x3a, 0x99, 0x35, 0x49, 0x66, 0x0e, 0x32, 0x9f, - 0xc6, 0xe8, 0xf9, 0x61, 0x1e, 0xf0, 0x77, 0x15, 0xe0, 0xab, 0x90, 0xc6, 0x8e, 0xcf, 0x93, 0x00, - 0xf3, 0x73, 0xfb, 0x7f, 0x3c, 0x57, 0xa4, 0x0c, 0x97, 0xc9, 0x9c, 0x05, 0xce, 0x8c, 0xd1, 0x25, - 0x09, 0x3d, 0x5c, 0x48, 0x05, 0xeb, 0xab, 0x02, 0x70, 0xe2, 0xc5, 0x67, 0x07, 0x11, 0xf5, 0x90, - 0xea, 0x5d, 0xb8, 0x55, 0x1c, 0x1e, 0x33, 0xd2, 0x54, 0xda, 0x4a, 0xa7, 0xde, 0xdf, 0x49, 0x57, - 0x66, 0xe3, 0x60, 0xed, 0x1f, 0x0e, 0xdc, 0x46, 0x11, 0x34, 0x24, 0xfa, 0x1d, 0xd0, 0x26, 0x49, - 0x40, 0x96, 0xb4, 0xa9, 0x8a, 0x68, 0x37, 0xb7, 0x74, 0x07, 0xb4, 0x88, 0x73, 0x9c, 0xc5, 0xcd, - 0x6a, 0xbb, 0xda, 0x69, 0x74, 0xef, 0xda, 0xd7, 0x78, 0x65, 0xbd, 0xd8, 0x87, 0xa2, 0x17, 0x37, - 0x0f, 0xd3, 0x1f, 0x80, 0xca, 0x78, 0x73, 0xa3, 0xad, 0x74, 0x1a, 0xdd, 0x7b, 0xf6, 0x0d, 0xb8, - 0xb6, 0xa8, 0x73, 0x38, 0xea, 0x6b, 0xe9, 0xca, 0x54, 0x87, 0x23, 0x57, 0x65, 0x5c, 0x37, 0x00, - 0xa6, 0x0b, 0x3a, 0x3d, 0x0b, 0x39, 0x0b, 0xb0, 0xb9, 0x99, 0xe5, 0xbf, 0xe6, 0xd1, 0x77, 0xa1, - 0x1a, 0x32, 0xd2, 0xd4, 0xda, 0x4a, 0x67, 0xdb, 0x15, 0x4b, 0xeb, 0x19, 0xd4, 0x85, 0xce, 0x31, - 0x7a, 0x11, 0x96, 0x6a, 0x37, 0x97, 0x54, 0xaf, 0x24, 0xdf, 0xe7, 0x0c, 0x07, 0x74, 0x49, 0x4b, - 0x32, 0xbc, 0x21, 0xaa, 0x9b, 0xd0, 0xa0, 0xe7, 0x0c, 0xc7, 0x31, 0x7a, 0x98, 0x08, 0x84, 0x62, - 0x07, 0x84, 0xeb, 0x38, 0xf3, 0xe8, 0x3d, 0xa8, 0x0b, 0x8b, 0x92, 0xb1, 0x87, 0x39, 0xb4, 0x96, - 0x2d, 0x07, 0xcd, 0x5e, 0xdf, 0xba, 0x7d, 0xb2, 0x1e, 0xb4, 0x7e, 0xed, 0x62, 0x65, 0x56, 0xde, - 0x7e, 0x36, 0x15, 0xb7, 0x26, 0x8f, 0xf5, 0xd0, 0x7a, 0x01, 0x9a, 0x64, 0xaa, 0xef, 0xc1, 0x66, - 0x8c, 0x84, 0x05, 0xb2, 0x58, 0x57, 0x1a, 0xe2, 0x66, 0x63, 0x24, 0x3c, 0xc1, 0xf5, 0xcd, 0x4a, - 0x2b, 0xf7, 0xd3, 0x28, 0xca, 0xca, 0x92, 0x7e, 0x1a, 0x45, 0x7a, 0x0b, 0x6a, 0x48, 0x23, 0x9f, - 0x05, 0xde, 0x32, 0xab, 0xa8, 0xe6, 0x16, 0xb6, 0xf5, 0x41, 0x81, 0x9a, 0x48, 0xf6, 0xf4, 0x9c, - 0x61, 0xc9, 0x31, 0x53, 0x73, 0x42, 0xf5, 0x7c, 0x04, 0x06, 0xae, 0xca, 0x0a, 0x74, 0xd5, 0x5f, - 0xa2, 0xdb, 0xf8, 0x3d, 0xba, 0xcd, 0x52, 0xe8, 0x9e, 0xc0, 0x96, 0xe8, 0x66, 0x34, 0x3a, 0x2c, - 0xd3, 0x8c, 0xb5, 0x80, 0x6d, 0x09, 0x83, 0x4e, 0x7b, 0x84, 0x50, 0x52, 0x8a, 0xc8, 0x7d, 0xd8, - 0xa2, 0xe7, 0x74, 0x3a, 0x2e, 0xb0, 0x40, 0xba, 0x32, 0x35, 0xa1, 0x39, 0x1c, 0xb8, 0x9a, 0xd8, - 0x1a, 0x12, 0xeb, 0x35, 0xec, 0xac, 0x33, 0x65, 0x33, 0xff, 0x1f, 0x73, 0xdd, 0xbc, 0x0a, 0x6b, - 0x5f, 0xfe, 0x33, 0x8e, 0xbc, 0x24, 0x2e, 0x97, 0xd8, 0xea, 0x41, 0x43, 0x28, 0xb8, 0x34, 0x4e, - 0xfc, 0x92, 0x12, 0x33, 0xd8, 0xcd, 0x9e, 0xb8, 0xe2, 0x59, 0x28, 0xc9, 0xe0, 0xc7, 0xc7, 0x46, - 0xfd, 0xf9, 0xb1, 0xe9, 0x1f, 0x5d, 0x5c, 0x1a, 0x95, 0x4f, 0x97, 0x46, 0xe5, 0x4d, 0x6a, 0x28, - 0x17, 0xa9, 0xa1, 0x7c, 0x4c, 0x0d, 0xe5, 0x4b, 0x6a, 0x28, 0xef, 0xbe, 0x19, 0xca, 0xf3, 0xee, - 0x3f, 0x7c, 0x65, 0x1e, 0xcb, 0x9f, 0xd3, 0xca, 0x69, 0x75, 0xa2, 0x65, 0x13, 0xf9, 0xf0, 0x7b, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x07, 0x69, 0x62, 0x9d, 0xa6, 0x06, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/events/task.proto b/vendor/github.com/containerd/containerd/api/events/task.proto index d69921365ddd6..3cbbbf00a03a7 100644 --- a/vendor/github.com/containerd/containerd/api/events/task.proto +++ b/vendor/github.com/containerd/containerd/api/events/task.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.events; @@ -29,6 +45,9 @@ message TaskDelete { uint32 pid = 2; uint32 exit_status = 3; google.protobuf.Timestamp exited_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; + // id is the specific exec. By default if omitted will be `""` thus matches + // the init exec of the task matching `container_id`. + string id = 5; } message TaskIO { diff --git a/vendor/github.com/containerd/containerd/api/services/containers/v1/containers.pb.go b/vendor/github.com/containerd/containerd/api/services/containers/v1/containers.pb.go index 4d9322164e384..af56c7de2badb 100644 --- a/vendor/github.com/containerd/containerd/api/services/containers/v1/containers.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/containers/v1/containers.pb.go @@ -1,49 +1,25 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/containers/v1/containers.proto -/* - Package containers is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/containers/v1/containers.proto - - It has these top-level messages: - Container - GetContainerRequest - GetContainerResponse - ListContainersRequest - ListContainersResponse - CreateContainerRequest - CreateContainerResponse - UpdateContainerRequest - UpdateContainerResponse - DeleteContainerRequest - ListContainerMessage -*/ package containers -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import google_protobuf1 "github.com/gogo/protobuf/types" -import google_protobuf2 "github.com/gogo/protobuf/types" -import google_protobuf3 "github.com/gogo/protobuf/types" -import _ "github.com/gogo/protobuf/types" - -import time "time" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + context "context" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -55,7 +31,7 @@ var _ = time.Kitchen // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Container struct { // ID is the user-specified identifier. @@ -68,16 +44,16 @@ type Container struct { // // Note that to add a new value to this field, read the existing set and // include the entire result in the update call. - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Image contains the reference of the image used to build the // specification and snapshots for running this container. // // If this field is updated, the spec and rootfs needed to updated, as well. Image string `protobuf:"bytes,3,opt,name=image,proto3" json:"image,omitempty"` // Runtime specifies which runtime to use for executing this container. - Runtime *Container_Runtime `protobuf:"bytes,4,opt,name=runtime" json:"runtime,omitempty"` + Runtime *Container_Runtime `protobuf:"bytes,4,opt,name=runtime,proto3" json:"runtime,omitempty"` // Spec to be used when creating the container. This is runtime specific. - Spec *google_protobuf1.Any `protobuf:"bytes,5,opt,name=spec" json:"spec,omitempty"` + Spec *types.Any `protobuf:"bytes,5,opt,name=spec,proto3" json:"spec,omitempty"` // Snapshotter specifies the snapshotter name used for rootfs Snapshotter string `protobuf:"bytes,6,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` // SnapshotKey specifies the snapshot key to use for the container's root @@ -92,9 +68,9 @@ type Container struct { // This field may be updated. SnapshotKey string `protobuf:"bytes,7,opt,name=snapshot_key,json=snapshotKey,proto3" json:"snapshot_key,omitempty"` // CreatedAt is the time the container was first created. - CreatedAt time.Time `protobuf:"bytes,8,opt,name=created_at,json=createdAt,stdtime" json:"created_at"` + CreatedAt time.Time `protobuf:"bytes,8,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at"` // UpdatedAt is the last time the container was mutated. - UpdatedAt time.Time `protobuf:"bytes,9,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` + UpdatedAt time.Time `protobuf:"bytes,9,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at"` // Extensions allow clients to provide zero or more blobs that are directly // associated with the container. One may provide protobuf, json, or other // encoding formats. The primary use of this is to further decorate the @@ -104,39 +80,163 @@ type Container struct { // that should be unique against other extensions. When updating extension // data, one should only update the specified extension using field paths // to select a specific map key. - Extensions map[string]google_protobuf1.Any `protobuf:"bytes,10,rep,name=extensions" json:"extensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` + Extensions map[string]types.Any `protobuf:"bytes,10,rep,name=extensions,proto3" json:"extensions" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Container) Reset() { *m = Container{} } +func (*Container) ProtoMessage() {} +func (*Container) Descriptor() ([]byte, []int) { + return fileDescriptor_311afb8e15951042, []int{0} +} +func (m *Container) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Container) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Container.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Container) XXX_Merge(src proto.Message) { + xxx_messageInfo_Container.Merge(m, src) +} +func (m *Container) XXX_Size() int { + return m.Size() +} +func (m *Container) XXX_DiscardUnknown() { + xxx_messageInfo_Container.DiscardUnknown(m) } -func (m *Container) Reset() { *m = Container{} } -func (*Container) ProtoMessage() {} -func (*Container) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{0} } +var xxx_messageInfo_Container proto.InternalMessageInfo type Container_Runtime struct { // Name is the name of the runtime. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Options specify additional runtime initialization options. - Options *google_protobuf1.Any `protobuf:"bytes,2,opt,name=options" json:"options,omitempty"` + Options *types.Any `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Container_Runtime) Reset() { *m = Container_Runtime{} } +func (*Container_Runtime) ProtoMessage() {} +func (*Container_Runtime) Descriptor() ([]byte, []int) { + return fileDescriptor_311afb8e15951042, []int{0, 1} +} +func (m *Container_Runtime) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Container_Runtime) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Container_Runtime.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Container_Runtime) XXX_Merge(src proto.Message) { + xxx_messageInfo_Container_Runtime.Merge(m, src) +} +func (m *Container_Runtime) XXX_Size() int { + return m.Size() +} +func (m *Container_Runtime) XXX_DiscardUnknown() { + xxx_messageInfo_Container_Runtime.DiscardUnknown(m) } -func (m *Container_Runtime) Reset() { *m = Container_Runtime{} } -func (*Container_Runtime) ProtoMessage() {} -func (*Container_Runtime) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{0, 1} } +var xxx_messageInfo_Container_Runtime proto.InternalMessageInfo type GetContainerRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetContainerRequest) Reset() { *m = GetContainerRequest{} } +func (*GetContainerRequest) ProtoMessage() {} +func (*GetContainerRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_311afb8e15951042, []int{1} +} +func (m *GetContainerRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetContainerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetContainerRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetContainerRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetContainerRequest.Merge(m, src) +} +func (m *GetContainerRequest) XXX_Size() int { + return m.Size() +} +func (m *GetContainerRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetContainerRequest.DiscardUnknown(m) } -func (m *GetContainerRequest) Reset() { *m = GetContainerRequest{} } -func (*GetContainerRequest) ProtoMessage() {} -func (*GetContainerRequest) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{1} } +var xxx_messageInfo_GetContainerRequest proto.InternalMessageInfo type GetContainerResponse struct { - Container Container `protobuf:"bytes,1,opt,name=container" json:"container"` + Container Container `protobuf:"bytes,1,opt,name=container,proto3" json:"container"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetContainerResponse) Reset() { *m = GetContainerResponse{} } +func (*GetContainerResponse) ProtoMessage() {} +func (*GetContainerResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_311afb8e15951042, []int{2} +} +func (m *GetContainerResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetContainerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetContainerResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetContainerResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetContainerResponse.Merge(m, src) +} +func (m *GetContainerResponse) XXX_Size() int { + return m.Size() +} +func (m *GetContainerResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetContainerResponse.DiscardUnknown(m) } -func (m *GetContainerResponse) Reset() { *m = GetContainerResponse{} } -func (*GetContainerResponse) ProtoMessage() {} -func (*GetContainerResponse) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{2} } +var xxx_messageInfo_GetContainerResponse proto.InternalMessageInfo type ListContainersRequest struct { // Filters contains one or more filters using the syntax defined in the @@ -149,38 +249,160 @@ type ListContainersRequest struct { // filters[0] or filters[1] or ... or filters[n-1] or filters[n] // // If filters is zero-length or nil, all items will be returned. - Filters []string `protobuf:"bytes,1,rep,name=filters" json:"filters,omitempty"` + Filters []string `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListContainersRequest) Reset() { *m = ListContainersRequest{} } +func (*ListContainersRequest) ProtoMessage() {} +func (*ListContainersRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_311afb8e15951042, []int{3} +} +func (m *ListContainersRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListContainersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListContainersRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListContainersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListContainersRequest.Merge(m, src) +} +func (m *ListContainersRequest) XXX_Size() int { + return m.Size() +} +func (m *ListContainersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListContainersRequest.DiscardUnknown(m) } -func (m *ListContainersRequest) Reset() { *m = ListContainersRequest{} } -func (*ListContainersRequest) ProtoMessage() {} -func (*ListContainersRequest) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{3} } +var xxx_messageInfo_ListContainersRequest proto.InternalMessageInfo type ListContainersResponse struct { - Containers []Container `protobuf:"bytes,1,rep,name=containers" json:"containers"` + Containers []Container `protobuf:"bytes,1,rep,name=containers,proto3" json:"containers"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListContainersResponse) Reset() { *m = ListContainersResponse{} } +func (*ListContainersResponse) ProtoMessage() {} +func (*ListContainersResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_311afb8e15951042, []int{4} +} +func (m *ListContainersResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListContainersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListContainersResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListContainersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListContainersResponse.Merge(m, src) +} +func (m *ListContainersResponse) XXX_Size() int { + return m.Size() +} +func (m *ListContainersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListContainersResponse.DiscardUnknown(m) } -func (m *ListContainersResponse) Reset() { *m = ListContainersResponse{} } -func (*ListContainersResponse) ProtoMessage() {} -func (*ListContainersResponse) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{4} } +var xxx_messageInfo_ListContainersResponse proto.InternalMessageInfo type CreateContainerRequest struct { - Container Container `protobuf:"bytes,1,opt,name=container" json:"container"` + Container Container `protobuf:"bytes,1,opt,name=container,proto3" json:"container"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateContainerRequest) Reset() { *m = CreateContainerRequest{} } +func (*CreateContainerRequest) ProtoMessage() {} +func (*CreateContainerRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_311afb8e15951042, []int{5} +} +func (m *CreateContainerRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateContainerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateContainerRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateContainerRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateContainerRequest.Merge(m, src) +} +func (m *CreateContainerRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateContainerRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateContainerRequest.DiscardUnknown(m) } -func (m *CreateContainerRequest) Reset() { *m = CreateContainerRequest{} } -func (*CreateContainerRequest) ProtoMessage() {} -func (*CreateContainerRequest) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{5} } +var xxx_messageInfo_CreateContainerRequest proto.InternalMessageInfo type CreateContainerResponse struct { - Container Container `protobuf:"bytes,1,opt,name=container" json:"container"` + Container Container `protobuf:"bytes,1,opt,name=container,proto3" json:"container"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CreateContainerResponse) Reset() { *m = CreateContainerResponse{} } func (*CreateContainerResponse) ProtoMessage() {} func (*CreateContainerResponse) Descriptor() ([]byte, []int) { - return fileDescriptorContainers, []int{6} + return fileDescriptor_311afb8e15951042, []int{6} +} +func (m *CreateContainerResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateContainerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateContainerResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateContainerResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateContainerResponse.Merge(m, src) +} +func (m *CreateContainerResponse) XXX_Size() int { + return m.Size() } +func (m *CreateContainerResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateContainerResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_CreateContainerResponse proto.InternalMessageInfo // UpdateContainerRequest updates the metadata on one or more container. // @@ -191,44 +413,168 @@ type UpdateContainerRequest struct { // Container provides the target values, as declared by the mask, for the update. // // The ID field must be set. - Container Container `protobuf:"bytes,1,opt,name=container" json:"container"` + Container Container `protobuf:"bytes,1,opt,name=container,proto3" json:"container"` // UpdateMask specifies which fields to perform the update on. If empty, // the operation applies to all fields. - UpdateMask *google_protobuf3.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask" json:"update_mask,omitempty"` + UpdateMask *types.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateContainerRequest) Reset() { *m = UpdateContainerRequest{} } +func (*UpdateContainerRequest) ProtoMessage() {} +func (*UpdateContainerRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_311afb8e15951042, []int{7} +} +func (m *UpdateContainerRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateContainerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateContainerRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateContainerRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateContainerRequest.Merge(m, src) +} +func (m *UpdateContainerRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateContainerRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateContainerRequest.DiscardUnknown(m) } -func (m *UpdateContainerRequest) Reset() { *m = UpdateContainerRequest{} } -func (*UpdateContainerRequest) ProtoMessage() {} -func (*UpdateContainerRequest) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{7} } +var xxx_messageInfo_UpdateContainerRequest proto.InternalMessageInfo type UpdateContainerResponse struct { - Container Container `protobuf:"bytes,1,opt,name=container" json:"container"` + Container Container `protobuf:"bytes,1,opt,name=container,proto3" json:"container"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *UpdateContainerResponse) Reset() { *m = UpdateContainerResponse{} } func (*UpdateContainerResponse) ProtoMessage() {} func (*UpdateContainerResponse) Descriptor() ([]byte, []int) { - return fileDescriptorContainers, []int{8} + return fileDescriptor_311afb8e15951042, []int{8} +} +func (m *UpdateContainerResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateContainerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateContainerResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateContainerResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateContainerResponse.Merge(m, src) +} +func (m *UpdateContainerResponse) XXX_Size() int { + return m.Size() } +func (m *UpdateContainerResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateContainerResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateContainerResponse proto.InternalMessageInfo type DeleteContainerRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteContainerRequest) Reset() { *m = DeleteContainerRequest{} } +func (*DeleteContainerRequest) ProtoMessage() {} +func (*DeleteContainerRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_311afb8e15951042, []int{9} +} +func (m *DeleteContainerRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteContainerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteContainerRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteContainerRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteContainerRequest.Merge(m, src) +} +func (m *DeleteContainerRequest) XXX_Size() int { + return m.Size() +} +func (m *DeleteContainerRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteContainerRequest.DiscardUnknown(m) } -func (m *DeleteContainerRequest) Reset() { *m = DeleteContainerRequest{} } -func (*DeleteContainerRequest) ProtoMessage() {} -func (*DeleteContainerRequest) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{9} } +var xxx_messageInfo_DeleteContainerRequest proto.InternalMessageInfo type ListContainerMessage struct { - Container *Container `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"` + Container *Container `protobuf:"bytes,1,opt,name=container,proto3" json:"container,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListContainerMessage) Reset() { *m = ListContainerMessage{} } +func (*ListContainerMessage) ProtoMessage() {} +func (*ListContainerMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_311afb8e15951042, []int{10} +} +func (m *ListContainerMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListContainerMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListContainerMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListContainerMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListContainerMessage.Merge(m, src) +} +func (m *ListContainerMessage) XXX_Size() int { + return m.Size() +} +func (m *ListContainerMessage) XXX_DiscardUnknown() { + xxx_messageInfo_ListContainerMessage.DiscardUnknown(m) } -func (m *ListContainerMessage) Reset() { *m = ListContainerMessage{} } -func (*ListContainerMessage) ProtoMessage() {} -func (*ListContainerMessage) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{10} } +var xxx_messageInfo_ListContainerMessage proto.InternalMessageInfo func init() { proto.RegisterType((*Container)(nil), "containerd.services.containers.v1.Container") + proto.RegisterMapType((map[string]types.Any)(nil), "containerd.services.containers.v1.Container.ExtensionsEntry") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.containers.v1.Container.LabelsEntry") proto.RegisterType((*Container_Runtime)(nil), "containerd.services.containers.v1.Container.Runtime") proto.RegisterType((*GetContainerRequest)(nil), "containerd.services.containers.v1.GetContainerRequest") proto.RegisterType((*GetContainerResponse)(nil), "containerd.services.containers.v1.GetContainerResponse") @@ -242,6 +588,66 @@ func init() { proto.RegisterType((*ListContainerMessage)(nil), "containerd.services.containers.v1.ListContainerMessage") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/containers/v1/containers.proto", fileDescriptor_311afb8e15951042) +} + +var fileDescriptor_311afb8e15951042 = []byte{ + // 820 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcb, 0x6e, 0x13, 0x49, + 0x14, 0x75, 0xdb, 0x4e, 0x3b, 0xbe, 0x1e, 0x69, 0x46, 0x35, 0x1e, 0x4f, 0x4f, 0x8f, 0x64, 0x3b, + 0x5e, 0x59, 0xa3, 0xa1, 0x9d, 0x18, 0x44, 0x5e, 0x6c, 0xe2, 0xbc, 0x04, 0x24, 0x28, 0xea, 0x80, + 0x84, 0x60, 0x11, 0xda, 0x76, 0xc5, 0x69, 0xdc, 0x2f, 0xba, 0xca, 0x16, 0x16, 0x8b, 0xc0, 0x1f, + 0xb0, 0xe3, 0x13, 0xf8, 0x95, 0x2c, 0x59, 0xb2, 0x0a, 0xc4, 0xe2, 0x43, 0x50, 0x57, 0x57, 0xbb, + 0x3b, 0x7e, 0x80, 0x9d, 0x90, 0x5d, 0x5d, 0xd7, 0x3d, 0xf7, 0x9e, 0x3a, 0xb7, 0x4e, 0xb9, 0x61, + 0xaf, 0xa5, 0xd3, 0x93, 0x4e, 0x5d, 0x69, 0xd8, 0x66, 0xa5, 0x61, 0x5b, 0x54, 0xd3, 0x2d, 0xec, + 0x36, 0xa3, 0x4b, 0xcd, 0xd1, 0x2b, 0x04, 0xbb, 0x5d, 0xbd, 0x81, 0x49, 0xf8, 0x3b, 0xa9, 0x74, + 0x97, 0x22, 0x91, 0xe2, 0xb8, 0x36, 0xb5, 0xd1, 0x42, 0x88, 0x53, 0x02, 0x8c, 0x12, 0xc9, 0xea, + 0x2e, 0xc9, 0xd9, 0x96, 0xdd, 0xb2, 0x59, 0x76, 0xc5, 0x5b, 0xf9, 0x40, 0xf9, 0x9f, 0x96, 0x6d, + 0xb7, 0x0c, 0x5c, 0x61, 0x51, 0xbd, 0x73, 0x5c, 0xd1, 0xac, 0x1e, 0xdf, 0xfa, 0x77, 0x78, 0x0b, + 0x9b, 0x0e, 0x0d, 0x36, 0x8b, 0xc3, 0x9b, 0xc7, 0x3a, 0x36, 0x9a, 0x47, 0xa6, 0x46, 0xda, 0x3c, + 0xa3, 0x30, 0x9c, 0x41, 0x75, 0x13, 0x13, 0xaa, 0x99, 0x8e, 0x9f, 0x50, 0xfa, 0x20, 0x42, 0x7a, + 0x33, 0xa0, 0x88, 0x72, 0x10, 0xd7, 0x9b, 0x92, 0x50, 0x14, 0xca, 0xe9, 0x9a, 0xd8, 0x3f, 0x2f, + 0xc4, 0xef, 0x6f, 0xa9, 0x71, 0xbd, 0x89, 0x0e, 0x40, 0x34, 0xb4, 0x3a, 0x36, 0x88, 0x14, 0x2f, + 0x26, 0xca, 0x99, 0xea, 0x8a, 0xf2, 0xd3, 0xa3, 0x2a, 0x83, 0xaa, 0xca, 0x1e, 0x83, 0x6e, 0x5b, + 0xd4, 0xed, 0xa9, 0xbc, 0x0e, 0xca, 0xc2, 0x9c, 0x6e, 0x6a, 0x2d, 0x2c, 0x25, 0xbc, 0x66, 0xaa, + 0x1f, 0xa0, 0x47, 0x90, 0x72, 0x3b, 0x96, 0xc7, 0x51, 0x4a, 0x16, 0x85, 0x72, 0xa6, 0x7a, 0x67, + 0xa6, 0x46, 0xaa, 0x8f, 0x55, 0x83, 0x22, 0xa8, 0x0c, 0x49, 0xe2, 0xe0, 0x86, 0x34, 0xc7, 0x8a, + 0x65, 0x15, 0x5f, 0x0d, 0x25, 0x50, 0x43, 0xd9, 0xb0, 0x7a, 0x2a, 0xcb, 0x40, 0x45, 0xc8, 0x10, + 0x4b, 0x73, 0xc8, 0x89, 0x4d, 0x29, 0x76, 0x25, 0x91, 0xb1, 0x8a, 0xfe, 0x84, 0x16, 0xe0, 0xb7, + 0x20, 0x3c, 0x6a, 0xe3, 0x9e, 0x94, 0xba, 0x9c, 0xf2, 0x10, 0xf7, 0xd0, 0x26, 0x40, 0xc3, 0xc5, + 0x1a, 0xc5, 0xcd, 0x23, 0x8d, 0x4a, 0xf3, 0xac, 0xa9, 0x3c, 0xd2, 0xf4, 0x71, 0x30, 0x82, 0xda, + 0xfc, 0xd9, 0x79, 0x21, 0xf6, 0xfe, 0x4b, 0x41, 0x50, 0xd3, 0x1c, 0xb7, 0x41, 0xbd, 0x22, 0x1d, + 0xa7, 0x19, 0x14, 0x49, 0xcf, 0x52, 0x84, 0xe3, 0x36, 0x28, 0xaa, 0x03, 0xe0, 0xd7, 0x14, 0x5b, + 0x44, 0xb7, 0x2d, 0x22, 0x01, 0x1b, 0xda, 0xbd, 0x99, 0xb4, 0xdc, 0x1e, 0xc0, 0xd9, 0xe0, 0x6a, + 0x49, 0xaf, 0x8d, 0x1a, 0xa9, 0x2a, 0xaf, 0x42, 0x26, 0x32, 0x59, 0xf4, 0x07, 0x24, 0x3c, 0x59, + 0xd8, 0xe5, 0x51, 0xbd, 0xa5, 0x37, 0xe3, 0xae, 0x66, 0x74, 0xb0, 0x14, 0xf7, 0x67, 0xcc, 0x82, + 0xb5, 0xf8, 0x8a, 0x20, 0xef, 0x43, 0x8a, 0xcf, 0x0a, 0x21, 0x48, 0x5a, 0x9a, 0x89, 0x39, 0x8e, + 0xad, 0x91, 0x02, 0x29, 0xdb, 0xa1, 0x8c, 0x7a, 0xfc, 0x07, 0x93, 0x0b, 0x92, 0xe4, 0x43, 0xf8, + 0x7d, 0x88, 0xee, 0x18, 0x36, 0xff, 0x45, 0xd9, 0x4c, 0x2a, 0x19, 0x72, 0x2c, 0xdd, 0x82, 0x3f, + 0x77, 0x31, 0x1d, 0x08, 0xa2, 0xe2, 0x57, 0x1d, 0x4c, 0xe8, 0x24, 0x8b, 0x94, 0x4e, 0x20, 0x7b, + 0x39, 0x9d, 0x38, 0xb6, 0x45, 0x30, 0x3a, 0x80, 0xf4, 0x40, 0x62, 0x06, 0xcb, 0x54, 0xff, 0x9f, + 0x65, 0x10, 0x5c, 0xf8, 0xb0, 0x48, 0x69, 0x09, 0xfe, 0xda, 0xd3, 0x49, 0xd8, 0x8a, 0x04, 0xd4, + 0x24, 0x48, 0x1d, 0xeb, 0x06, 0xc5, 0x2e, 0x91, 0x84, 0x62, 0xa2, 0x9c, 0x56, 0x83, 0xb0, 0x64, + 0x40, 0x6e, 0x18, 0xc2, 0xe9, 0xa9, 0x00, 0x61, 0x63, 0x06, 0xbb, 0x1a, 0xbf, 0x48, 0x95, 0xd2, + 0x4b, 0xc8, 0x6d, 0xb2, 0xeb, 0x3c, 0x22, 0xde, 0xaf, 0x17, 0xa3, 0x0d, 0x7f, 0x8f, 0xf4, 0xba, + 0x31, 0xe5, 0x3f, 0x0a, 0x90, 0x7b, 0xc2, 0x3c, 0x76, 0xf3, 0x27, 0x43, 0xeb, 0x90, 0xf1, 0xfd, + 0xcc, 0xde, 0x73, 0x7e, 0x6b, 0x47, 0x1f, 0x82, 0x1d, 0xef, 0xc9, 0xdf, 0xd7, 0x48, 0x5b, 0xe5, + 0xcf, 0x86, 0xb7, 0xf6, 0x64, 0x19, 0x21, 0x7a, 0x63, 0xb2, 0x2c, 0x42, 0x6e, 0x0b, 0x1b, 0x78, + 0x8c, 0x2a, 0x93, 0xcc, 0x52, 0x87, 0xec, 0xa5, 0xfb, 0xb8, 0x8f, 0x09, 0xf1, 0xde, 0xff, 0x07, + 0xd7, 0xe4, 0x16, 0x61, 0x55, 0xfd, 0x36, 0x07, 0x10, 0x5e, 0x78, 0xd4, 0x85, 0xc4, 0x2e, 0xa6, + 0xe8, 0xee, 0x14, 0xe5, 0xc6, 0xd8, 0x5e, 0x5e, 0x9e, 0x19, 0xc7, 0xe5, 0x7e, 0x03, 0x49, 0xef, + 0xa8, 0x68, 0x9a, 0xbf, 0xcc, 0xb1, 0xb6, 0x96, 0x57, 0xaf, 0x80, 0xe4, 0xcd, 0xdf, 0x09, 0x00, + 0xde, 0xd6, 0x21, 0x75, 0xb1, 0x66, 0x5e, 0x83, 0xc3, 0xf2, 0xac, 0x48, 0x3e, 0xd1, 0x45, 0x01, + 0x9d, 0x82, 0xe8, 0x3b, 0x14, 0x4d, 0x73, 0x90, 0xf1, 0x0f, 0x87, 0xbc, 0x76, 0x15, 0x28, 0x17, + 0xe1, 0x14, 0x44, 0xdf, 0x0b, 0x53, 0x11, 0x18, 0xef, 0xef, 0xa9, 0x08, 0x4c, 0x72, 0xdc, 0x73, + 0x10, 0x7d, 0x7f, 0x4c, 0x45, 0x60, 0xbc, 0x95, 0xe4, 0xdc, 0x88, 0xf3, 0xb7, 0xbd, 0x2f, 0xc1, + 0xda, 0x8b, 0xb3, 0x8b, 0x7c, 0xec, 0xf3, 0x45, 0x3e, 0xf6, 0xb6, 0x9f, 0x17, 0xce, 0xfa, 0x79, + 0xe1, 0x53, 0x3f, 0x2f, 0x7c, 0xed, 0xe7, 0x85, 0x67, 0x3b, 0xd7, 0xf8, 0xb8, 0x5d, 0x0f, 0xa3, + 0xa7, 0xb1, 0xba, 0xc8, 0x7a, 0xde, 0xfe, 0x1e, 0x00, 0x00, 0xff, 0xff, 0xd0, 0xae, 0xca, 0xcb, + 0x2f, 0x0b, 0x00, 0x00, +} + // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn @@ -250,15 +656,16 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Containers service - +// ContainersClient is the client API for Containers service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ContainersClient interface { Get(ctx context.Context, in *GetContainerRequest, opts ...grpc.CallOption) (*GetContainerResponse, error) List(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (*ListContainersResponse, error) ListStream(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (Containers_ListStreamClient, error) Create(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error) Update(ctx context.Context, in *UpdateContainerRequest, opts ...grpc.CallOption) (*UpdateContainerResponse, error) - Delete(ctx context.Context, in *DeleteContainerRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error) + Delete(ctx context.Context, in *DeleteContainerRequest, opts ...grpc.CallOption) (*types.Empty, error) } type containersClient struct { @@ -271,7 +678,7 @@ func NewContainersClient(cc *grpc.ClientConn) ContainersClient { func (c *containersClient) Get(ctx context.Context, in *GetContainerRequest, opts ...grpc.CallOption) (*GetContainerResponse, error) { out := new(GetContainerResponse) - err := grpc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Get", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Get", in, out, opts...) if err != nil { return nil, err } @@ -280,7 +687,7 @@ func (c *containersClient) Get(ctx context.Context, in *GetContainerRequest, opt func (c *containersClient) List(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (*ListContainersResponse, error) { out := new(ListContainersResponse) - err := grpc.Invoke(ctx, "/containerd.services.containers.v1.Containers/List", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.containers.v1.Containers/List", in, out, opts...) if err != nil { return nil, err } @@ -288,7 +695,7 @@ func (c *containersClient) List(ctx context.Context, in *ListContainersRequest, } func (c *containersClient) ListStream(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (Containers_ListStreamClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Containers_serviceDesc.Streams[0], c.cc, "/containerd.services.containers.v1.Containers/ListStream", opts...) + stream, err := c.cc.NewStream(ctx, &_Containers_serviceDesc.Streams[0], "/containerd.services.containers.v1.Containers/ListStream", opts...) if err != nil { return nil, err } @@ -321,7 +728,7 @@ func (x *containersListStreamClient) Recv() (*ListContainerMessage, error) { func (c *containersClient) Create(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error) { out := new(CreateContainerResponse) - err := grpc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Create", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Create", in, out, opts...) if err != nil { return nil, err } @@ -330,31 +737,53 @@ func (c *containersClient) Create(ctx context.Context, in *CreateContainerReques func (c *containersClient) Update(ctx context.Context, in *UpdateContainerRequest, opts ...grpc.CallOption) (*UpdateContainerResponse, error) { out := new(UpdateContainerResponse) - err := grpc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Update", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Update", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *containersClient) Delete(ctx context.Context, in *DeleteContainerRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error) { - out := new(google_protobuf2.Empty) - err := grpc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Delete", in, out, c.cc, opts...) +func (c *containersClient) Delete(ctx context.Context, in *DeleteContainerRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Delete", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Containers service - +// ContainersServer is the server API for Containers service. type ContainersServer interface { Get(context.Context, *GetContainerRequest) (*GetContainerResponse, error) List(context.Context, *ListContainersRequest) (*ListContainersResponse, error) ListStream(*ListContainersRequest, Containers_ListStreamServer) error Create(context.Context, *CreateContainerRequest) (*CreateContainerResponse, error) Update(context.Context, *UpdateContainerRequest) (*UpdateContainerResponse, error) - Delete(context.Context, *DeleteContainerRequest) (*google_protobuf2.Empty, error) + Delete(context.Context, *DeleteContainerRequest) (*types.Empty, error) +} + +// UnimplementedContainersServer can be embedded to have forward compatible implementations. +type UnimplementedContainersServer struct { +} + +func (*UnimplementedContainersServer) Get(ctx context.Context, req *GetContainerRequest) (*GetContainerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (*UnimplementedContainersServer) List(ctx context.Context, req *ListContainersRequest) (*ListContainersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (*UnimplementedContainersServer) ListStream(req *ListContainersRequest, srv Containers_ListStreamServer) error { + return status.Errorf(codes.Unimplemented, "method ListStream not implemented") +} +func (*UnimplementedContainersServer) Create(ctx context.Context, req *CreateContainerRequest) (*CreateContainerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") +} +func (*UnimplementedContainersServer) Update(ctx context.Context, req *UpdateContainerRequest) (*UpdateContainerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (*UnimplementedContainersServer) Delete(ctx context.Context, req *DeleteContainerRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") } func RegisterContainersServer(s *grpc.Server, srv ContainersServer) { @@ -510,7 +939,7 @@ var _Containers_serviceDesc = grpc.ServiceDesc{ func (m *Container) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -518,120 +947,137 @@ func (m *Container) Marshal() (dAtA []byte, err error) { } func (m *Container) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Container) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintContainers(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { + if len(m.Extensions) > 0 { + for k := range m.Extensions { + v := m.Extensions[k] + baseI := i + { + size, err := (&v).MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovContainers(uint64(len(k))) + 1 + len(v) + sovContainers(uint64(len(v))) - i = encodeVarintContainers(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintContainers(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintContainers(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i-- + dAtA[i] = 0xa + i = encodeVarintContainers(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x52 } } - if len(m.Image) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintContainers(dAtA, i, uint64(len(m.Image))) - i += copy(dAtA[i:], m.Image) + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt):]) + if err2 != nil { + return 0, err2 } - if m.Runtime != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintContainers(dAtA, i, uint64(m.Runtime.Size())) - n1, err := m.Runtime.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 + i -= n2 + i = encodeVarintContainers(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x4a + n3, err3 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):]) + if err3 != nil { + return 0, err3 } - if m.Spec != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintContainers(dAtA, i, uint64(m.Spec.Size())) - n2, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 + i -= n3 + i = encodeVarintContainers(dAtA, i, uint64(n3)) + i-- + dAtA[i] = 0x42 + if len(m.SnapshotKey) > 0 { + i -= len(m.SnapshotKey) + copy(dAtA[i:], m.SnapshotKey) + i = encodeVarintContainers(dAtA, i, uint64(len(m.SnapshotKey))) + i-- + dAtA[i] = 0x3a } if len(m.Snapshotter) > 0 { - dAtA[i] = 0x32 - i++ + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) i = encodeVarintContainers(dAtA, i, uint64(len(m.Snapshotter))) - i += copy(dAtA[i:], m.Snapshotter) + i-- + dAtA[i] = 0x32 } - if len(m.SnapshotKey) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintContainers(dAtA, i, uint64(len(m.SnapshotKey))) - i += copy(dAtA[i:], m.SnapshotKey) + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a } - dAtA[i] = 0x42 - i++ - i = encodeVarintContainers(dAtA, i, uint64(types.SizeOfStdTime(m.CreatedAt))) - n3, err := types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) - if err != nil { - return 0, err + if m.Runtime != nil { + { + size, err := m.Runtime.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 } - i += n3 - dAtA[i] = 0x4a - i++ - i = encodeVarintContainers(dAtA, i, uint64(types.SizeOfStdTime(m.UpdatedAt))) - n4, err := types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i:]) - if err != nil { - return 0, err + if len(m.Image) > 0 { + i -= len(m.Image) + copy(dAtA[i:], m.Image) + i = encodeVarintContainers(dAtA, i, uint64(len(m.Image))) + i-- + dAtA[i] = 0x1a } - i += n4 - if len(m.Extensions) > 0 { - for k, _ := range m.Extensions { - dAtA[i] = 0x52 - i++ - v := m.Extensions[k] - msgSize := 0 - if (&v) != nil { - msgSize = (&v).Size() - msgSize += 1 + sovContainers(uint64(msgSize)) - } - mapSize := 1 + len(k) + sovContainers(uint64(len(k))) + msgSize - i = encodeVarintContainers(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintContainers(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintContainers(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintContainers(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintContainers(dAtA, i, uint64((&v).Size())) - n5, err := (&v).MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n5 } } - return i, nil + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintContainers(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *Container_Runtime) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -639,33 +1085,45 @@ func (m *Container_Runtime) Marshal() (dAtA []byte, err error) { } func (m *Container_Runtime) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Container_Runtime) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintContainers(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Options != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintContainers(dAtA, i, uint64(m.Options.Size())) - n6, err := m.Options.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Options.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) } - i += n6 + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintContainers(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *GetContainerRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -673,23 +1131,33 @@ func (m *GetContainerRequest) Marshal() (dAtA []byte, err error) { } func (m *GetContainerRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetContainerRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ID) + copy(dAtA[i:], m.ID) i = encodeVarintContainers(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *GetContainerResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -697,58 +1165,72 @@ func (m *GetContainerResponse) Marshal() (dAtA []byte, err error) { } func (m *GetContainerResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetContainerResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) - n7, err := m.Container.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n7 - return i, nil + { + size, err := m.Container.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *ListContainersRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *ListContainersRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *ListContainersRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListContainersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Filters) > 0 { - for _, s := range m.Filters { + for iNdEx := len(m.Filters) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Filters[iNdEx]) + copy(dAtA[i:], m.Filters[iNdEx]) + i = encodeVarintContainers(dAtA, i, uint64(len(m.Filters[iNdEx]))) + i-- dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - return i, nil + return len(dAtA) - i, nil } func (m *ListContainersResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -756,29 +1238,40 @@ func (m *ListContainersResponse) Marshal() (dAtA []byte, err error) { } func (m *ListContainersResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListContainersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Containers) > 0 { - for _, msg := range m.Containers { - dAtA[i] = 0xa - i++ - i = encodeVarintContainers(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Containers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Containers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *CreateContainerRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -786,25 +1279,36 @@ func (m *CreateContainerRequest) Marshal() (dAtA []byte, err error) { } func (m *CreateContainerRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateContainerRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) - n8, err := m.Container.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Container.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) } - i += n8 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *CreateContainerResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -812,25 +1316,36 @@ func (m *CreateContainerResponse) Marshal() (dAtA []byte, err error) { } func (m *CreateContainerResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateContainerResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) - n9, err := m.Container.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Container.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) } - i += n9 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *UpdateContainerRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -838,35 +1353,48 @@ func (m *UpdateContainerRequest) Marshal() (dAtA []byte, err error) { } func (m *UpdateContainerRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateContainerRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) - n10, err := m.Container.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n10 if m.UpdateMask != nil { + { + size, err := m.UpdateMask.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintContainers(dAtA, i, uint64(m.UpdateMask.Size())) - n11, err := m.UpdateMask.MarshalTo(dAtA[i:]) + } + { + size, err := m.Container.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n11 + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *UpdateContainerResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -874,25 +1402,36 @@ func (m *UpdateContainerResponse) Marshal() (dAtA []byte, err error) { } func (m *UpdateContainerResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateContainerResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) - n12, err := m.Container.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Container.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) } - i += n12 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *DeleteContainerRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -900,23 +1439,33 @@ func (m *DeleteContainerRequest) Marshal() (dAtA []byte, err error) { } func (m *DeleteContainerRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteContainerRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ID) + copy(dAtA[i:], m.ID) i = encodeVarintContainers(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListContainerMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -924,33 +1473,49 @@ func (m *ListContainerMessage) Marshal() (dAtA []byte, err error) { } func (m *ListContainerMessage) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListContainerMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Container != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size())) - n13, err := m.Container.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Container.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContainers(dAtA, i, uint64(size)) } - i += n13 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintContainers(dAtA []byte, offset int, v uint64) int { + offset -= sovContainers(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Container) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -985,9 +1550,9 @@ func (m *Container) Size() (n int) { if l > 0 { n += 1 + l + sovContainers(uint64(l)) } - l = types.SizeOfStdTime(m.CreatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt) n += 1 + l + sovContainers(uint64(l)) - l = types.SizeOfStdTime(m.UpdatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt) n += 1 + l + sovContainers(uint64(l)) if len(m.Extensions) > 0 { for k, v := range m.Extensions { @@ -998,10 +1563,16 @@ func (m *Container) Size() (n int) { n += mapEntrySize + 1 + sovContainers(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *Container_Runtime) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -1012,28 +1583,46 @@ func (m *Container_Runtime) Size() (n int) { l = m.Options.Size() n += 1 + l + sovContainers(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *GetContainerRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) if l > 0 { n += 1 + l + sovContainers(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *GetContainerResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Container.Size() n += 1 + l + sovContainers(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListContainersRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Filters) > 0 { @@ -1042,10 +1631,16 @@ func (m *ListContainersRequest) Size() (n int) { n += 1 + l + sovContainers(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListContainersResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Containers) > 0 { @@ -1054,26 +1649,44 @@ func (m *ListContainersResponse) Size() (n int) { n += 1 + l + sovContainers(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateContainerRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Container.Size() n += 1 + l + sovContainers(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateContainerResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Container.Size() n += 1 + l + sovContainers(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateContainerRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Container.Size() @@ -1082,46 +1695,60 @@ func (m *UpdateContainerRequest) Size() (n int) { l = m.UpdateMask.Size() n += 1 + l + sovContainers(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateContainerResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Container.Size() n += 1 + l + sovContainers(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DeleteContainerRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) if l > 0 { n += 1 + l + sovContainers(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListContainerMessage) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Container != nil { l = m.Container.Size() n += 1 + l + sovContainers(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovContainers(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozContainers(x uint64) (n int) { return sovContainers(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -1134,7 +1761,7 @@ func (this *Container) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -1144,8 +1771,8 @@ func (this *Container) String() string { for k, _ := range this.Extensions { keysForExtensions = append(keysForExtensions, k) } - sortkeys.Strings(keysForExtensions) - mapStringForExtensions := "map[string]google_protobuf1.Any{" + github_com_gogo_protobuf_sortkeys.Strings(keysForExtensions) + mapStringForExtensions := "map[string]types.Any{" for _, k := range keysForExtensions { mapStringForExtensions += fmt.Sprintf("%v: %v,", k, this.Extensions[k]) } @@ -1155,12 +1782,13 @@ func (this *Container) String() string { `Labels:` + mapStringForLabels + `,`, `Image:` + fmt.Sprintf("%v", this.Image) + `,`, `Runtime:` + strings.Replace(fmt.Sprintf("%v", this.Runtime), "Container_Runtime", "Container_Runtime", 1) + `,`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "Any", "google_protobuf1.Any", 1) + `,`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "Any", "types.Any", 1) + `,`, `Snapshotter:` + fmt.Sprintf("%v", this.Snapshotter) + `,`, `SnapshotKey:` + fmt.Sprintf("%v", this.SnapshotKey) + `,`, - `CreatedAt:` + strings.Replace(strings.Replace(this.CreatedAt.String(), "Timestamp", "google_protobuf4.Timestamp", 1), `&`, ``, 1) + `,`, - `UpdatedAt:` + strings.Replace(strings.Replace(this.UpdatedAt.String(), "Timestamp", "google_protobuf4.Timestamp", 1), `&`, ``, 1) + `,`, + `CreatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.CreatedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, + `UpdatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.UpdatedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, `Extensions:` + mapStringForExtensions + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1171,7 +1799,8 @@ func (this *Container_Runtime) String() string { } s := strings.Join([]string{`&Container_Runtime{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, - `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "google_protobuf1.Any", 1) + `,`, + `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "types.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1182,6 +1811,7 @@ func (this *GetContainerRequest) String() string { } s := strings.Join([]string{`&GetContainerRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1192,6 +1822,7 @@ func (this *GetContainerResponse) String() string { } s := strings.Join([]string{`&GetContainerResponse{`, `Container:` + strings.Replace(strings.Replace(this.Container.String(), "Container", "Container", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1202,6 +1833,7 @@ func (this *ListContainersRequest) String() string { } s := strings.Join([]string{`&ListContainersRequest{`, `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1210,8 +1842,14 @@ func (this *ListContainersResponse) String() string { if this == nil { return "nil" } + repeatedStringForContainers := "[]Container{" + for _, f := range this.Containers { + repeatedStringForContainers += strings.Replace(strings.Replace(f.String(), "Container", "Container", 1), `&`, ``, 1) + "," + } + repeatedStringForContainers += "}" s := strings.Join([]string{`&ListContainersResponse{`, - `Containers:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Containers), "Container", "Container", 1), `&`, ``, 1) + `,`, + `Containers:` + repeatedStringForContainers + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1222,6 +1860,7 @@ func (this *CreateContainerRequest) String() string { } s := strings.Join([]string{`&CreateContainerRequest{`, `Container:` + strings.Replace(strings.Replace(this.Container.String(), "Container", "Container", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1232,6 +1871,7 @@ func (this *CreateContainerResponse) String() string { } s := strings.Join([]string{`&CreateContainerResponse{`, `Container:` + strings.Replace(strings.Replace(this.Container.String(), "Container", "Container", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1242,7 +1882,8 @@ func (this *UpdateContainerRequest) String() string { } s := strings.Join([]string{`&UpdateContainerRequest{`, `Container:` + strings.Replace(strings.Replace(this.Container.String(), "Container", "Container", 1), `&`, ``, 1) + `,`, - `UpdateMask:` + strings.Replace(fmt.Sprintf("%v", this.UpdateMask), "FieldMask", "google_protobuf3.FieldMask", 1) + `,`, + `UpdateMask:` + strings.Replace(fmt.Sprintf("%v", this.UpdateMask), "FieldMask", "types.FieldMask", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1253,6 +1894,7 @@ func (this *UpdateContainerResponse) String() string { } s := strings.Join([]string{`&UpdateContainerResponse{`, `Container:` + strings.Replace(strings.Replace(this.Container.String(), "Container", "Container", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1263,6 +1905,7 @@ func (this *DeleteContainerRequest) String() string { } s := strings.Join([]string{`&DeleteContainerRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1272,7 +1915,8 @@ func (this *ListContainerMessage) String() string { return "nil" } s := strings.Join([]string{`&ListContainerMessage{`, - `Container:` + strings.Replace(fmt.Sprintf("%v", this.Container), "Container", "Container", 1) + `,`, + `Container:` + strings.Replace(this.Container.String(), "Container", "Container", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1300,7 +1944,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1328,7 +1972,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1338,6 +1982,9 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1357,7 +2004,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1366,6 +2013,9 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1386,7 +2036,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1403,7 +2053,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1413,6 +2063,9 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthContainers + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -1429,7 +2082,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1439,6 +2092,9 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthContainers + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -1450,7 +2106,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > postIndex { @@ -1475,7 +2131,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1485,6 +2141,9 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1504,7 +2163,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1513,6 +2172,9 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1537,7 +2199,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1546,11 +2208,14 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Spec == nil { - m.Spec = &google_protobuf1.Any{} + m.Spec = &types.Any{} } if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -1570,7 +2235,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1580,6 +2245,9 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1599,7 +2267,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1609,6 +2277,9 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1628,7 +2299,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1637,10 +2308,13 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1658,7 +2332,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1667,10 +2341,13 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1688,7 +2365,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1697,14 +2374,17 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Extensions == nil { - m.Extensions = make(map[string]google_protobuf1.Any) + m.Extensions = make(map[string]types.Any) } var mapkey string - mapvalue := &google_protobuf1.Any{} + mapvalue := &types.Any{} for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 @@ -1717,7 +2397,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1734,7 +2414,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1744,6 +2424,9 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthContainers + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -1760,7 +2443,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - mapmsglen |= (int(b) & 0x7F) << shift + mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1769,13 +2452,13 @@ func (m *Container) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postmsgIndex := iNdEx + mapmsglen - if mapmsglen < 0 { + if postmsgIndex < 0 { return ErrInvalidLengthContainers } if postmsgIndex > l { return io.ErrUnexpectedEOF } - mapvalue = &google_protobuf1.Any{} + mapvalue = &types.Any{} if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { return err } @@ -1786,7 +2469,7 @@ func (m *Container) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > postIndex { @@ -1803,12 +2486,13 @@ func (m *Container) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1833,7 +2517,7 @@ func (m *Container_Runtime) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1861,7 +2545,7 @@ func (m *Container_Runtime) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1871,6 +2555,9 @@ func (m *Container_Runtime) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1890,7 +2577,7 @@ func (m *Container_Runtime) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1899,11 +2586,14 @@ func (m *Container_Runtime) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Options == nil { - m.Options = &google_protobuf1.Any{} + m.Options = &types.Any{} } if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -1915,12 +2605,13 @@ func (m *Container_Runtime) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1945,7 +2636,7 @@ func (m *GetContainerRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1973,7 +2664,7 @@ func (m *GetContainerRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1983,6 +2674,9 @@ func (m *GetContainerRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1994,12 +2688,13 @@ func (m *GetContainerRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2024,7 +2719,7 @@ func (m *GetContainerResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2052,7 +2747,7 @@ func (m *GetContainerResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2061,6 +2756,9 @@ func (m *GetContainerResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2074,12 +2772,13 @@ func (m *GetContainerResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2104,7 +2803,7 @@ func (m *ListContainersRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2132,7 +2831,7 @@ func (m *ListContainersRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2142,6 +2841,9 @@ func (m *ListContainersRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2153,12 +2855,13 @@ func (m *ListContainersRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2183,7 +2886,7 @@ func (m *ListContainersResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2211,7 +2914,7 @@ func (m *ListContainersResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2220,6 +2923,9 @@ func (m *ListContainersResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2234,12 +2940,13 @@ func (m *ListContainersResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2264,7 +2971,7 @@ func (m *CreateContainerRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2292,7 +2999,7 @@ func (m *CreateContainerRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2301,6 +3008,9 @@ func (m *CreateContainerRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2314,12 +3024,13 @@ func (m *CreateContainerRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2344,7 +3055,7 @@ func (m *CreateContainerResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2372,7 +3083,7 @@ func (m *CreateContainerResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2381,6 +3092,9 @@ func (m *CreateContainerResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2394,12 +3108,13 @@ func (m *CreateContainerResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2424,7 +3139,7 @@ func (m *UpdateContainerRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2452,7 +3167,7 @@ func (m *UpdateContainerRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2461,6 +3176,9 @@ func (m *UpdateContainerRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2482,7 +3200,7 @@ func (m *UpdateContainerRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2491,11 +3209,14 @@ func (m *UpdateContainerRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } if m.UpdateMask == nil { - m.UpdateMask = &google_protobuf3.FieldMask{} + m.UpdateMask = &types.FieldMask{} } if err := m.UpdateMask.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -2507,12 +3228,13 @@ func (m *UpdateContainerRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2537,7 +3259,7 @@ func (m *UpdateContainerResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2565,7 +3287,7 @@ func (m *UpdateContainerResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2574,6 +3296,9 @@ func (m *UpdateContainerResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2587,12 +3312,13 @@ func (m *UpdateContainerResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2617,7 +3343,7 @@ func (m *DeleteContainerRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2645,7 +3371,7 @@ func (m *DeleteContainerRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2655,6 +3381,9 @@ func (m *DeleteContainerRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2666,12 +3395,13 @@ func (m *DeleteContainerRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2696,7 +3426,7 @@ func (m *ListContainerMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2724,7 +3454,7 @@ func (m *ListContainerMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2733,6 +3463,9 @@ func (m *ListContainerMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContainers } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContainers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2749,12 +3482,13 @@ func (m *ListContainerMessage) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContainers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2767,6 +3501,7 @@ func (m *ListContainerMessage) Unmarshal(dAtA []byte) error { func skipContainers(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -2798,10 +3533,8 @@ func skipContainers(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -2818,113 +3551,34 @@ func skipContainers(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthContainers } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowContainers - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipContainers(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupContainers + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthContainers + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthContainers = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowContainers = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthContainers = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowContainers = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupContainers = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/containers/v1/containers.proto", fileDescriptorContainers) -} - -var fileDescriptorContainers = []byte{ - // 820 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcb, 0x6e, 0x13, 0x49, - 0x14, 0x75, 0xdb, 0x4e, 0x3b, 0xbe, 0x1e, 0x69, 0x46, 0x35, 0x1e, 0x4f, 0x4f, 0x8f, 0x64, 0x3b, - 0x5e, 0x59, 0xa3, 0xa1, 0x9d, 0x18, 0x44, 0x5e, 0x6c, 0xe2, 0xbc, 0x04, 0x24, 0x28, 0xea, 0x80, - 0x84, 0x60, 0x11, 0xda, 0x76, 0xc5, 0x69, 0xdc, 0x2f, 0xba, 0xca, 0x16, 0x16, 0x8b, 0xc0, 0x1f, - 0xb0, 0xe3, 0x13, 0xf8, 0x95, 0x2c, 0x59, 0xb2, 0x0a, 0xc4, 0xe2, 0x43, 0x50, 0x57, 0x57, 0xbb, - 0x3b, 0x7e, 0x80, 0x9d, 0x90, 0x5d, 0x5d, 0xd7, 0x3d, 0xf7, 0x9e, 0x3a, 0xb7, 0x4e, 0xb9, 0x61, - 0xaf, 0xa5, 0xd3, 0x93, 0x4e, 0x5d, 0x69, 0xd8, 0x66, 0xa5, 0x61, 0x5b, 0x54, 0xd3, 0x2d, 0xec, - 0x36, 0xa3, 0x4b, 0xcd, 0xd1, 0x2b, 0x04, 0xbb, 0x5d, 0xbd, 0x81, 0x49, 0xf8, 0x3b, 0xa9, 0x74, - 0x97, 0x22, 0x91, 0xe2, 0xb8, 0x36, 0xb5, 0xd1, 0x42, 0x88, 0x53, 0x02, 0x8c, 0x12, 0xc9, 0xea, - 0x2e, 0xc9, 0xd9, 0x96, 0xdd, 0xb2, 0x59, 0x76, 0xc5, 0x5b, 0xf9, 0x40, 0xf9, 0x9f, 0x96, 0x6d, - 0xb7, 0x0c, 0x5c, 0x61, 0x51, 0xbd, 0x73, 0x5c, 0xd1, 0xac, 0x1e, 0xdf, 0xfa, 0x77, 0x78, 0x0b, - 0x9b, 0x0e, 0x0d, 0x36, 0x8b, 0xc3, 0x9b, 0xc7, 0x3a, 0x36, 0x9a, 0x47, 0xa6, 0x46, 0xda, 0x3c, - 0xa3, 0x30, 0x9c, 0x41, 0x75, 0x13, 0x13, 0xaa, 0x99, 0x8e, 0x9f, 0x50, 0xfa, 0x20, 0x42, 0x7a, - 0x33, 0xa0, 0x88, 0x72, 0x10, 0xd7, 0x9b, 0x92, 0x50, 0x14, 0xca, 0xe9, 0x9a, 0xd8, 0x3f, 0x2f, - 0xc4, 0xef, 0x6f, 0xa9, 0x71, 0xbd, 0x89, 0x0e, 0x40, 0x34, 0xb4, 0x3a, 0x36, 0x88, 0x14, 0x2f, - 0x26, 0xca, 0x99, 0xea, 0x8a, 0xf2, 0xd3, 0xa3, 0x2a, 0x83, 0xaa, 0xca, 0x1e, 0x83, 0x6e, 0x5b, - 0xd4, 0xed, 0xa9, 0xbc, 0x0e, 0xca, 0xc2, 0x9c, 0x6e, 0x6a, 0x2d, 0x2c, 0x25, 0xbc, 0x66, 0xaa, - 0x1f, 0xa0, 0x47, 0x90, 0x72, 0x3b, 0x96, 0xc7, 0x51, 0x4a, 0x16, 0x85, 0x72, 0xa6, 0x7a, 0x67, - 0xa6, 0x46, 0xaa, 0x8f, 0x55, 0x83, 0x22, 0xa8, 0x0c, 0x49, 0xe2, 0xe0, 0x86, 0x34, 0xc7, 0x8a, - 0x65, 0x15, 0x5f, 0x0d, 0x25, 0x50, 0x43, 0xd9, 0xb0, 0x7a, 0x2a, 0xcb, 0x40, 0x45, 0xc8, 0x10, - 0x4b, 0x73, 0xc8, 0x89, 0x4d, 0x29, 0x76, 0x25, 0x91, 0xb1, 0x8a, 0xfe, 0x84, 0x16, 0xe0, 0xb7, - 0x20, 0x3c, 0x6a, 0xe3, 0x9e, 0x94, 0xba, 0x9c, 0xf2, 0x10, 0xf7, 0xd0, 0x26, 0x40, 0xc3, 0xc5, - 0x1a, 0xc5, 0xcd, 0x23, 0x8d, 0x4a, 0xf3, 0xac, 0xa9, 0x3c, 0xd2, 0xf4, 0x71, 0x30, 0x82, 0xda, - 0xfc, 0xd9, 0x79, 0x21, 0xf6, 0xfe, 0x4b, 0x41, 0x50, 0xd3, 0x1c, 0xb7, 0x41, 0xbd, 0x22, 0x1d, - 0xa7, 0x19, 0x14, 0x49, 0xcf, 0x52, 0x84, 0xe3, 0x36, 0x28, 0xaa, 0x03, 0xe0, 0xd7, 0x14, 0x5b, - 0x44, 0xb7, 0x2d, 0x22, 0x01, 0x1b, 0xda, 0xbd, 0x99, 0xb4, 0xdc, 0x1e, 0xc0, 0xd9, 0xe0, 0x6a, - 0x49, 0xaf, 0x8d, 0x1a, 0xa9, 0x2a, 0xaf, 0x42, 0x26, 0x32, 0x59, 0xf4, 0x07, 0x24, 0x3c, 0x59, - 0xd8, 0xe5, 0x51, 0xbd, 0xa5, 0x37, 0xe3, 0xae, 0x66, 0x74, 0xb0, 0x14, 0xf7, 0x67, 0xcc, 0x82, - 0xb5, 0xf8, 0x8a, 0x20, 0xef, 0x43, 0x8a, 0xcf, 0x0a, 0x21, 0x48, 0x5a, 0x9a, 0x89, 0x39, 0x8e, - 0xad, 0x91, 0x02, 0x29, 0xdb, 0xa1, 0x8c, 0x7a, 0xfc, 0x07, 0x93, 0x0b, 0x92, 0xe4, 0x43, 0xf8, - 0x7d, 0x88, 0xee, 0x18, 0x36, 0xff, 0x45, 0xd9, 0x4c, 0x2a, 0x19, 0x72, 0x2c, 0xdd, 0x82, 0x3f, - 0x77, 0x31, 0x1d, 0x08, 0xa2, 0xe2, 0x57, 0x1d, 0x4c, 0xe8, 0x24, 0x8b, 0x94, 0x4e, 0x20, 0x7b, - 0x39, 0x9d, 0x38, 0xb6, 0x45, 0x30, 0x3a, 0x80, 0xf4, 0x40, 0x62, 0x06, 0xcb, 0x54, 0xff, 0x9f, - 0x65, 0x10, 0x5c, 0xf8, 0xb0, 0x48, 0x69, 0x09, 0xfe, 0xda, 0xd3, 0x49, 0xd8, 0x8a, 0x04, 0xd4, - 0x24, 0x48, 0x1d, 0xeb, 0x06, 0xc5, 0x2e, 0x91, 0x84, 0x62, 0xa2, 0x9c, 0x56, 0x83, 0xb0, 0x64, - 0x40, 0x6e, 0x18, 0xc2, 0xe9, 0xa9, 0x00, 0x61, 0x63, 0x06, 0xbb, 0x1a, 0xbf, 0x48, 0x95, 0xd2, - 0x4b, 0xc8, 0x6d, 0xb2, 0xeb, 0x3c, 0x22, 0xde, 0xaf, 0x17, 0xa3, 0x0d, 0x7f, 0x8f, 0xf4, 0xba, - 0x31, 0xe5, 0x3f, 0x0a, 0x90, 0x7b, 0xc2, 0x3c, 0x76, 0xf3, 0x27, 0x43, 0xeb, 0x90, 0xf1, 0xfd, - 0xcc, 0xde, 0x73, 0x7e, 0x6b, 0x47, 0x1f, 0x82, 0x1d, 0xef, 0xc9, 0xdf, 0xd7, 0x48, 0x5b, 0xe5, - 0xcf, 0x86, 0xb7, 0xf6, 0x64, 0x19, 0x21, 0x7a, 0x63, 0xb2, 0x2c, 0x42, 0x6e, 0x0b, 0x1b, 0x78, - 0x8c, 0x2a, 0x93, 0xcc, 0x52, 0x87, 0xec, 0xa5, 0xfb, 0xb8, 0x8f, 0x09, 0xf1, 0xde, 0xff, 0x07, - 0xd7, 0xe4, 0x16, 0x61, 0x55, 0xfd, 0x36, 0x07, 0x10, 0x5e, 0x78, 0xd4, 0x85, 0xc4, 0x2e, 0xa6, - 0xe8, 0xee, 0x14, 0xe5, 0xc6, 0xd8, 0x5e, 0x5e, 0x9e, 0x19, 0xc7, 0xe5, 0x7e, 0x03, 0x49, 0xef, - 0xa8, 0x68, 0x9a, 0xbf, 0xcc, 0xb1, 0xb6, 0x96, 0x57, 0xaf, 0x80, 0xe4, 0xcd, 0xdf, 0x09, 0x00, - 0xde, 0xd6, 0x21, 0x75, 0xb1, 0x66, 0x5e, 0x83, 0xc3, 0xf2, 0xac, 0x48, 0x3e, 0xd1, 0x45, 0x01, - 0x9d, 0x82, 0xe8, 0x3b, 0x14, 0x4d, 0x73, 0x90, 0xf1, 0x0f, 0x87, 0xbc, 0x76, 0x15, 0x28, 0x17, - 0xe1, 0x14, 0x44, 0xdf, 0x0b, 0x53, 0x11, 0x18, 0xef, 0xef, 0xa9, 0x08, 0x4c, 0x72, 0xdc, 0x73, - 0x10, 0x7d, 0x7f, 0x4c, 0x45, 0x60, 0xbc, 0x95, 0xe4, 0xdc, 0x88, 0xf3, 0xb7, 0xbd, 0x2f, 0xc1, - 0xda, 0x8b, 0xb3, 0x8b, 0x7c, 0xec, 0xf3, 0x45, 0x3e, 0xf6, 0xb6, 0x9f, 0x17, 0xce, 0xfa, 0x79, - 0xe1, 0x53, 0x3f, 0x2f, 0x7c, 0xed, 0xe7, 0x85, 0x67, 0x3b, 0xd7, 0xf8, 0xb8, 0x5d, 0x0f, 0xa3, - 0xa7, 0xb1, 0xba, 0xc8, 0x7a, 0xde, 0xfe, 0x1e, 0x00, 0x00, 0xff, 0xff, 0xd0, 0xae, 0xca, 0xcb, - 0x2f, 0x0b, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/containers/v1/containers.proto b/vendor/github.com/containerd/containerd/api/services/containers/v1/containers.proto index d491f46cfa1cf..36ab177de7781 100644 --- a/vendor/github.com/containerd/containerd/api/services/containers/v1/containers.proto +++ b/vendor/github.com/containerd/containerd/api/services/containers/v1/containers.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.containers.v1; diff --git a/vendor/github.com/containerd/containerd/api/services/content/v1/content.pb.go b/vendor/github.com/containerd/containerd/api/services/content/v1/content.pb.go index ec08c3b233d27..97c7d4a92b360 100644 --- a/vendor/github.com/containerd/containerd/api/services/content/v1/content.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/content/v1/content.pb.go @@ -1,56 +1,26 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/content/v1/content.proto -/* - Package content is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/content/v1/content.proto - - It has these top-level messages: - Info - InfoRequest - InfoResponse - UpdateRequest - UpdateResponse - ListContentRequest - ListContentResponse - DeleteContentRequest - ReadContentRequest - ReadContentResponse - Status - StatusRequest - StatusResponse - ListStatusesRequest - ListStatusesResponse - WriteContentRequest - WriteContentResponse - AbortRequest -*/ package content -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import google_protobuf1 "github.com/gogo/protobuf/types" -import _ "github.com/gogo/protobuf/types" -import google_protobuf3 "github.com/gogo/protobuf/types" - -import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" -import time "time" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + context "context" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types "github.com/gogo/protobuf/types" + github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -62,7 +32,7 @@ var _ = time.Kitchen // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // WriteAction defines the behavior of a WriteRequest. type WriteAction int32 @@ -93,6 +63,7 @@ var WriteAction_name = map[int32]string{ 1: "WRITE", 2: "COMMIT", } + var WriteAction_value = map[string]int32{ "STAT": 0, "WRITE": 1, @@ -102,7 +73,10 @@ var WriteAction_value = map[string]int32{ func (x WriteAction) String() string { return proto.EnumName(WriteAction_name, int32(x)) } -func (WriteAction) EnumDescriptor() ([]byte, []int) { return fileDescriptorContent, []int{0} } + +func (WriteAction) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{0} +} type Info struct { // Digest is the hash identity of the blob. @@ -110,57 +84,212 @@ type Info struct { // Size is the total number of bytes in the blob. Size_ int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` // CreatedAt provides the time at which the blob was committed. - CreatedAt time.Time `protobuf:"bytes,3,opt,name=created_at,json=createdAt,stdtime" json:"created_at"` + CreatedAt time.Time `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at"` // UpdatedAt provides the time the info was last updated. - UpdatedAt time.Time `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` + UpdatedAt time.Time `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at"` // Labels are arbitrary data on snapshots. // // The combined size of a key/value pair cannot exceed 4096 bytes. - Labels map[string]string `protobuf:"bytes,5,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,5,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Info) Reset() { *m = Info{} } +func (*Info) ProtoMessage() {} +func (*Info) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{0} +} +func (m *Info) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Info) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Info.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Info) XXX_Merge(src proto.Message) { + xxx_messageInfo_Info.Merge(m, src) +} +func (m *Info) XXX_Size() int { + return m.Size() +} +func (m *Info) XXX_DiscardUnknown() { + xxx_messageInfo_Info.DiscardUnknown(m) } -func (m *Info) Reset() { *m = Info{} } -func (*Info) ProtoMessage() {} -func (*Info) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{0} } +var xxx_messageInfo_Info proto.InternalMessageInfo type InfoRequest struct { - Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InfoRequest) Reset() { *m = InfoRequest{} } +func (*InfoRequest) ProtoMessage() {} +func (*InfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{1} +} +func (m *InfoRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *InfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_InfoRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *InfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_InfoRequest.Merge(m, src) +} +func (m *InfoRequest) XXX_Size() int { + return m.Size() +} +func (m *InfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_InfoRequest.DiscardUnknown(m) } -func (m *InfoRequest) Reset() { *m = InfoRequest{} } -func (*InfoRequest) ProtoMessage() {} -func (*InfoRequest) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{1} } +var xxx_messageInfo_InfoRequest proto.InternalMessageInfo type InfoResponse struct { - Info Info `protobuf:"bytes,1,opt,name=info" json:"info"` + Info Info `protobuf:"bytes,1,opt,name=info,proto3" json:"info"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *InfoResponse) Reset() { *m = InfoResponse{} } +func (*InfoResponse) ProtoMessage() {} +func (*InfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{2} +} +func (m *InfoResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *InfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_InfoResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *InfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_InfoResponse.Merge(m, src) +} +func (m *InfoResponse) XXX_Size() int { + return m.Size() +} +func (m *InfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_InfoResponse.DiscardUnknown(m) } -func (m *InfoResponse) Reset() { *m = InfoResponse{} } -func (*InfoResponse) ProtoMessage() {} -func (*InfoResponse) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{2} } +var xxx_messageInfo_InfoResponse proto.InternalMessageInfo type UpdateRequest struct { - Info Info `protobuf:"bytes,1,opt,name=info" json:"info"` + Info Info `protobuf:"bytes,1,opt,name=info,proto3" json:"info"` // UpdateMask specifies which fields to perform the update on. If empty, // the operation applies to all fields. // // In info, Digest, Size, and CreatedAt are immutable, // other field may be updated using this mask. // If no mask is provided, all mutable field are updated. - UpdateMask *google_protobuf1.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask" json:"update_mask,omitempty"` + UpdateMask *types.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateRequest) Reset() { *m = UpdateRequest{} } +func (*UpdateRequest) ProtoMessage() {} +func (*UpdateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{3} +} +func (m *UpdateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateRequest.Merge(m, src) +} +func (m *UpdateRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateRequest.DiscardUnknown(m) } -func (m *UpdateRequest) Reset() { *m = UpdateRequest{} } -func (*UpdateRequest) ProtoMessage() {} -func (*UpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{3} } +var xxx_messageInfo_UpdateRequest proto.InternalMessageInfo type UpdateResponse struct { - Info Info `protobuf:"bytes,1,opt,name=info" json:"info"` + Info Info `protobuf:"bytes,1,opt,name=info,proto3" json:"info"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateResponse) Reset() { *m = UpdateResponse{} } +func (*UpdateResponse) ProtoMessage() {} +func (*UpdateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{4} +} +func (m *UpdateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateResponse.Merge(m, src) +} +func (m *UpdateResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateResponse.DiscardUnknown(m) } -func (m *UpdateResponse) Reset() { *m = UpdateResponse{} } -func (*UpdateResponse) ProtoMessage() {} -func (*UpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{4} } +var xxx_messageInfo_UpdateResponse proto.InternalMessageInfo type ListContentRequest struct { // Filters contains one or more filters using the syntax defined in the @@ -173,29 +302,122 @@ type ListContentRequest struct { // filters[0] or filters[1] or ... or filters[n-1] or filters[n] // // If filters is zero-length or nil, all items will be returned. - Filters []string `protobuf:"bytes,1,rep,name=filters" json:"filters,omitempty"` + Filters []string `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListContentRequest) Reset() { *m = ListContentRequest{} } +func (*ListContentRequest) ProtoMessage() {} +func (*ListContentRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{5} +} +func (m *ListContentRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListContentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListContentRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListContentRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListContentRequest.Merge(m, src) +} +func (m *ListContentRequest) XXX_Size() int { + return m.Size() +} +func (m *ListContentRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListContentRequest.DiscardUnknown(m) } -func (m *ListContentRequest) Reset() { *m = ListContentRequest{} } -func (*ListContentRequest) ProtoMessage() {} -func (*ListContentRequest) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{5} } +var xxx_messageInfo_ListContentRequest proto.InternalMessageInfo type ListContentResponse struct { - Info []Info `protobuf:"bytes,1,rep,name=info" json:"info"` + Info []Info `protobuf:"bytes,1,rep,name=info,proto3" json:"info"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListContentResponse) Reset() { *m = ListContentResponse{} } +func (*ListContentResponse) ProtoMessage() {} +func (*ListContentResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{6} +} +func (m *ListContentResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListContentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListContentResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListContentResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListContentResponse.Merge(m, src) +} +func (m *ListContentResponse) XXX_Size() int { + return m.Size() +} +func (m *ListContentResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListContentResponse.DiscardUnknown(m) } -func (m *ListContentResponse) Reset() { *m = ListContentResponse{} } -func (*ListContentResponse) ProtoMessage() {} -func (*ListContentResponse) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{6} } +var xxx_messageInfo_ListContentResponse proto.InternalMessageInfo type DeleteContentRequest struct { // Digest specifies which content to delete. - Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteContentRequest) Reset() { *m = DeleteContentRequest{} } +func (*DeleteContentRequest) ProtoMessage() {} +func (*DeleteContentRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{7} +} +func (m *DeleteContentRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteContentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteContentRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteContentRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteContentRequest.Merge(m, src) +} +func (m *DeleteContentRequest) XXX_Size() int { + return m.Size() +} +func (m *DeleteContentRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteContentRequest.DiscardUnknown(m) } -func (m *DeleteContentRequest) Reset() { *m = DeleteContentRequest{} } -func (*DeleteContentRequest) ProtoMessage() {} -func (*DeleteContentRequest) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{7} } +var xxx_messageInfo_DeleteContentRequest proto.InternalMessageInfo // ReadContentRequest defines the fields that make up a request to read a portion of // data from a stored object. @@ -208,67 +430,284 @@ type ReadContentRequest struct { Offset int64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` // size is the total size of the read. If zero, the entire blob will be // returned by the service. - Size_ int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` + Size_ int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReadContentRequest) Reset() { *m = ReadContentRequest{} } +func (*ReadContentRequest) ProtoMessage() {} +func (*ReadContentRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{8} +} +func (m *ReadContentRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReadContentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReadContentRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ReadContentRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadContentRequest.Merge(m, src) +} +func (m *ReadContentRequest) XXX_Size() int { + return m.Size() +} +func (m *ReadContentRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ReadContentRequest.DiscardUnknown(m) } -func (m *ReadContentRequest) Reset() { *m = ReadContentRequest{} } -func (*ReadContentRequest) ProtoMessage() {} -func (*ReadContentRequest) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{8} } +var xxx_messageInfo_ReadContentRequest proto.InternalMessageInfo // ReadContentResponse carries byte data for a read request. type ReadContentResponse struct { - Offset int64 `protobuf:"varint,1,opt,name=offset,proto3" json:"offset,omitempty"` - Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Offset int64 `protobuf:"varint,1,opt,name=offset,proto3" json:"offset,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReadContentResponse) Reset() { *m = ReadContentResponse{} } +func (*ReadContentResponse) ProtoMessage() {} +func (*ReadContentResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{9} +} +func (m *ReadContentResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReadContentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReadContentResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ReadContentResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadContentResponse.Merge(m, src) +} +func (m *ReadContentResponse) XXX_Size() int { + return m.Size() +} +func (m *ReadContentResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ReadContentResponse.DiscardUnknown(m) } -func (m *ReadContentResponse) Reset() { *m = ReadContentResponse{} } -func (*ReadContentResponse) ProtoMessage() {} -func (*ReadContentResponse) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{9} } +var xxx_messageInfo_ReadContentResponse proto.InternalMessageInfo type Status struct { - StartedAt time.Time `protobuf:"bytes,1,opt,name=started_at,json=startedAt,stdtime" json:"started_at"` - UpdatedAt time.Time `protobuf:"bytes,2,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` - Ref string `protobuf:"bytes,3,opt,name=ref,proto3" json:"ref,omitempty"` - Offset int64 `protobuf:"varint,4,opt,name=offset,proto3" json:"offset,omitempty"` - Total int64 `protobuf:"varint,5,opt,name=total,proto3" json:"total,omitempty"` - Expected github_com_opencontainers_go_digest.Digest `protobuf:"bytes,6,opt,name=expected,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"expected"` + StartedAt time.Time `protobuf:"bytes,1,opt,name=started_at,json=startedAt,proto3,stdtime" json:"started_at"` + UpdatedAt time.Time `protobuf:"bytes,2,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at"` + Ref string `protobuf:"bytes,3,opt,name=ref,proto3" json:"ref,omitempty"` + Offset int64 `protobuf:"varint,4,opt,name=offset,proto3" json:"offset,omitempty"` + Total int64 `protobuf:"varint,5,opt,name=total,proto3" json:"total,omitempty"` + Expected github_com_opencontainers_go_digest.Digest `protobuf:"bytes,6,opt,name=expected,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"expected"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Status) Reset() { *m = Status{} } +func (*Status) ProtoMessage() {} +func (*Status) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{10} +} +func (m *Status) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Status) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Status.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Status) XXX_Merge(src proto.Message) { + xxx_messageInfo_Status.Merge(m, src) +} +func (m *Status) XXX_Size() int { + return m.Size() +} +func (m *Status) XXX_DiscardUnknown() { + xxx_messageInfo_Status.DiscardUnknown(m) } -func (m *Status) Reset() { *m = Status{} } -func (*Status) ProtoMessage() {} -func (*Status) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{10} } +var xxx_messageInfo_Status proto.InternalMessageInfo type StatusRequest struct { - Ref string `protobuf:"bytes,1,opt,name=ref,proto3" json:"ref,omitempty"` + Ref string `protobuf:"bytes,1,opt,name=ref,proto3" json:"ref,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StatusRequest) Reset() { *m = StatusRequest{} } +func (*StatusRequest) ProtoMessage() {} +func (*StatusRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{11} +} +func (m *StatusRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StatusRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StatusRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_StatusRequest.Merge(m, src) +} +func (m *StatusRequest) XXX_Size() int { + return m.Size() +} +func (m *StatusRequest) XXX_DiscardUnknown() { + xxx_messageInfo_StatusRequest.DiscardUnknown(m) } -func (m *StatusRequest) Reset() { *m = StatusRequest{} } -func (*StatusRequest) ProtoMessage() {} -func (*StatusRequest) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{11} } +var xxx_messageInfo_StatusRequest proto.InternalMessageInfo type StatusResponse struct { - Status *Status `protobuf:"bytes,1,opt,name=status" json:"status,omitempty"` + Status *Status `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StatusResponse) Reset() { *m = StatusResponse{} } +func (*StatusResponse) ProtoMessage() {} +func (*StatusResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{12} +} +func (m *StatusResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StatusResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StatusResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_StatusResponse.Merge(m, src) +} +func (m *StatusResponse) XXX_Size() int { + return m.Size() +} +func (m *StatusResponse) XXX_DiscardUnknown() { + xxx_messageInfo_StatusResponse.DiscardUnknown(m) } -func (m *StatusResponse) Reset() { *m = StatusResponse{} } -func (*StatusResponse) ProtoMessage() {} -func (*StatusResponse) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{12} } +var xxx_messageInfo_StatusResponse proto.InternalMessageInfo type ListStatusesRequest struct { - Filters []string `protobuf:"bytes,1,rep,name=filters" json:"filters,omitempty"` + Filters []string `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListStatusesRequest) Reset() { *m = ListStatusesRequest{} } +func (*ListStatusesRequest) ProtoMessage() {} +func (*ListStatusesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{13} +} +func (m *ListStatusesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListStatusesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListStatusesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListStatusesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListStatusesRequest.Merge(m, src) +} +func (m *ListStatusesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListStatusesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListStatusesRequest.DiscardUnknown(m) } -func (m *ListStatusesRequest) Reset() { *m = ListStatusesRequest{} } -func (*ListStatusesRequest) ProtoMessage() {} -func (*ListStatusesRequest) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{13} } +var xxx_messageInfo_ListStatusesRequest proto.InternalMessageInfo type ListStatusesResponse struct { - Statuses []Status `protobuf:"bytes,1,rep,name=statuses" json:"statuses"` + Statuses []Status `protobuf:"bytes,1,rep,name=statuses,proto3" json:"statuses"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListStatusesResponse) Reset() { *m = ListStatusesResponse{} } +func (*ListStatusesResponse) ProtoMessage() {} +func (*ListStatusesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{14} +} +func (m *ListStatusesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListStatusesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListStatusesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListStatusesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListStatusesResponse.Merge(m, src) +} +func (m *ListStatusesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListStatusesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListStatusesResponse.DiscardUnknown(m) } -func (m *ListStatusesResponse) Reset() { *m = ListStatusesResponse{} } -func (*ListStatusesResponse) ProtoMessage() {} -func (*ListStatusesResponse) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{14} } +var xxx_messageInfo_ListStatusesResponse proto.InternalMessageInfo // WriteContentRequest writes data to the request ref at offset. type WriteContentRequest struct { @@ -324,12 +763,43 @@ type WriteContentRequest struct { // Labels are arbitrary data on snapshots. // // The combined size of a key/value pair cannot exceed 4096 bytes. - Labels map[string]string `protobuf:"bytes,7,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,7,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WriteContentRequest) Reset() { *m = WriteContentRequest{} } +func (*WriteContentRequest) ProtoMessage() {} +func (*WriteContentRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{15} +} +func (m *WriteContentRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WriteContentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WriteContentRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WriteContentRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WriteContentRequest.Merge(m, src) +} +func (m *WriteContentRequest) XXX_Size() int { + return m.Size() +} +func (m *WriteContentRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WriteContentRequest.DiscardUnknown(m) } -func (m *WriteContentRequest) Reset() { *m = WriteContentRequest{} } -func (*WriteContentRequest) ProtoMessage() {} -func (*WriteContentRequest) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{15} } +var xxx_messageInfo_WriteContentRequest proto.InternalMessageInfo // WriteContentResponse is returned on the culmination of a write call. type WriteContentResponse struct { @@ -340,12 +810,12 @@ type WriteContentResponse struct { // // This must be set for stat and commit write actions. All other write // actions may omit this. - StartedAt time.Time `protobuf:"bytes,2,opt,name=started_at,json=startedAt,stdtime" json:"started_at"` + StartedAt time.Time `protobuf:"bytes,2,opt,name=started_at,json=startedAt,proto3,stdtime" json:"started_at"` // UpdatedAt provides the last time of a successful write. // // This must be set for stat and commit write actions. All other write // actions may omit this. - UpdatedAt time.Time `protobuf:"bytes,3,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` + UpdatedAt time.Time `protobuf:"bytes,3,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at"` // Offset is the current committed size for the write. Offset int64 `protobuf:"varint,4,opt,name=offset,proto3" json:"offset,omitempty"` // Total provides the current, expected total size of the write. @@ -358,23 +828,87 @@ type WriteContentResponse struct { // Digest, if present, includes the digest up to the currently committed // bytes. If action is commit, this field will be set. It is implementation // defined if this is set for other actions. - Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,6,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,6,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WriteContentResponse) Reset() { *m = WriteContentResponse{} } +func (*WriteContentResponse) ProtoMessage() {} +func (*WriteContentResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{16} +} +func (m *WriteContentResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WriteContentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WriteContentResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WriteContentResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WriteContentResponse.Merge(m, src) +} +func (m *WriteContentResponse) XXX_Size() int { + return m.Size() +} +func (m *WriteContentResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WriteContentResponse.DiscardUnknown(m) } -func (m *WriteContentResponse) Reset() { *m = WriteContentResponse{} } -func (*WriteContentResponse) ProtoMessage() {} -func (*WriteContentResponse) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{16} } +var xxx_messageInfo_WriteContentResponse proto.InternalMessageInfo type AbortRequest struct { - Ref string `protobuf:"bytes,1,opt,name=ref,proto3" json:"ref,omitempty"` + Ref string `protobuf:"bytes,1,opt,name=ref,proto3" json:"ref,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AbortRequest) Reset() { *m = AbortRequest{} } +func (*AbortRequest) ProtoMessage() {} +func (*AbortRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_468430ba3e400391, []int{17} +} +func (m *AbortRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AbortRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AbortRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AbortRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AbortRequest.Merge(m, src) +} +func (m *AbortRequest) XXX_Size() int { + return m.Size() +} +func (m *AbortRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AbortRequest.DiscardUnknown(m) } -func (m *AbortRequest) Reset() { *m = AbortRequest{} } -func (*AbortRequest) ProtoMessage() {} -func (*AbortRequest) Descriptor() ([]byte, []int) { return fileDescriptorContent, []int{17} } +var xxx_messageInfo_AbortRequest proto.InternalMessageInfo func init() { + proto.RegisterEnum("containerd.services.content.v1.WriteAction", WriteAction_name, WriteAction_value) proto.RegisterType((*Info)(nil), "containerd.services.content.v1.Info") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.content.v1.Info.LabelsEntry") proto.RegisterType((*InfoRequest)(nil), "containerd.services.content.v1.InfoRequest") proto.RegisterType((*InfoResponse)(nil), "containerd.services.content.v1.InfoResponse") proto.RegisterType((*UpdateRequest)(nil), "containerd.services.content.v1.UpdateRequest") @@ -390,9 +924,85 @@ func init() { proto.RegisterType((*ListStatusesRequest)(nil), "containerd.services.content.v1.ListStatusesRequest") proto.RegisterType((*ListStatusesResponse)(nil), "containerd.services.content.v1.ListStatusesResponse") proto.RegisterType((*WriteContentRequest)(nil), "containerd.services.content.v1.WriteContentRequest") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.content.v1.WriteContentRequest.LabelsEntry") proto.RegisterType((*WriteContentResponse)(nil), "containerd.services.content.v1.WriteContentResponse") proto.RegisterType((*AbortRequest)(nil), "containerd.services.content.v1.AbortRequest") - proto.RegisterEnum("containerd.services.content.v1.WriteAction", WriteAction_name, WriteAction_value) +} + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/content/v1/content.proto", fileDescriptor_468430ba3e400391) +} + +var fileDescriptor_468430ba3e400391 = []byte{ + // 1081 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcd, 0x6f, 0x1b, 0x45, + 0x14, 0xf7, 0x78, 0xed, 0x4d, 0xf2, 0x9c, 0x16, 0x33, 0x31, 0x95, 0xb5, 0x08, 0x67, 0xbb, 0x42, + 0xc8, 0x6a, 0xc9, 0x3a, 0x75, 0x7a, 0x00, 0x2a, 0x01, 0x8e, 0x9b, 0xaa, 0x41, 0x4d, 0x41, 0x5b, + 0x97, 0x40, 0x2f, 0x65, 0x6d, 0x8f, 0xcd, 0x2a, 0xb6, 0xd7, 0xdd, 0x19, 0x5b, 0x84, 0x13, 0x17, + 0x24, 0x14, 0xf5, 0x80, 0xb8, 0xe7, 0x02, 0xfc, 0x15, 0x1c, 0x38, 0xe7, 0xc8, 0x11, 0x71, 0x68, + 0x69, 0xfe, 0x07, 0xee, 0x68, 0x66, 0x67, 0xed, 0xf5, 0x47, 0x58, 0xdb, 0x31, 0x27, 0xbf, 0x99, + 0x7d, 0xbf, 0xf7, 0xfd, 0x31, 0x86, 0x7b, 0x4d, 0x87, 0x7d, 0xdd, 0xab, 0x9a, 0x35, 0xb7, 0x5d, + 0xa8, 0xb9, 0x1d, 0x66, 0x3b, 0x1d, 0xe2, 0xd5, 0xc3, 0xa4, 0xdd, 0x75, 0x0a, 0x94, 0x78, 0x7d, + 0xa7, 0x46, 0xa8, 0xb8, 0x27, 0x1d, 0x56, 0xe8, 0xdf, 0x0a, 0x48, 0xb3, 0xeb, 0xb9, 0xcc, 0xc5, + 0xb9, 0x21, 0xc2, 0x0c, 0xb8, 0xcd, 0x80, 0xa5, 0x7f, 0x4b, 0xcb, 0x34, 0xdd, 0xa6, 0x2b, 0x58, + 0x0b, 0x9c, 0xf2, 0x51, 0x9a, 0xde, 0x74, 0xdd, 0x66, 0x8b, 0x14, 0xc4, 0xa9, 0xda, 0x6b, 0x14, + 0x1a, 0x0e, 0x69, 0xd5, 0x9f, 0xb6, 0x6d, 0x7a, 0x24, 0x39, 0x36, 0xc7, 0x39, 0x98, 0xd3, 0x26, + 0x94, 0xd9, 0xed, 0xae, 0x64, 0x78, 0x73, 0x9c, 0x81, 0xb4, 0xbb, 0xec, 0xd8, 0xff, 0x68, 0xfc, + 0x13, 0x87, 0xc4, 0x7e, 0xa7, 0xe1, 0xe2, 0x4f, 0x40, 0xad, 0x3b, 0x4d, 0x42, 0x59, 0x16, 0xe9, + 0x28, 0xbf, 0xb6, 0x5b, 0x3c, 0x7b, 0xb1, 0x19, 0xfb, 0xeb, 0xc5, 0xe6, 0x8d, 0x90, 0xfb, 0x6e, + 0x97, 0x74, 0x06, 0x5e, 0xd0, 0x42, 0xd3, 0xdd, 0xf2, 0x21, 0xe6, 0x5d, 0xf1, 0x63, 0x49, 0x09, + 0x18, 0x43, 0x82, 0x3a, 0xdf, 0x92, 0x6c, 0x5c, 0x47, 0x79, 0xc5, 0x12, 0x34, 0x2e, 0x03, 0xd4, + 0x3c, 0x62, 0x33, 0x52, 0x7f, 0x6a, 0xb3, 0xac, 0xa2, 0xa3, 0x7c, 0xaa, 0xa8, 0x99, 0xbe, 0x69, + 0x66, 0x60, 0x9a, 0x59, 0x09, 0x6c, 0xdf, 0x5d, 0xe5, 0xfa, 0x7f, 0x7c, 0xb9, 0x89, 0xac, 0x35, + 0x89, 0x2b, 0x31, 0x2e, 0xa4, 0xd7, 0xad, 0x07, 0x42, 0x12, 0xf3, 0x08, 0x91, 0xb8, 0x12, 0xc3, + 0xf7, 0x41, 0x6d, 0xd9, 0x55, 0xd2, 0xa2, 0xd9, 0xa4, 0xae, 0xe4, 0x53, 0xc5, 0x6d, 0xf3, 0xbf, + 0x33, 0x63, 0xf2, 0xf8, 0x98, 0x0f, 0x04, 0x64, 0xaf, 0xc3, 0xbc, 0x63, 0x4b, 0xe2, 0xb5, 0xf7, + 0x21, 0x15, 0xba, 0xc6, 0x69, 0x50, 0x8e, 0xc8, 0xb1, 0x1f, 0x3f, 0x8b, 0x93, 0x38, 0x03, 0xc9, + 0xbe, 0xdd, 0xea, 0xf9, 0x91, 0x58, 0xb3, 0xfc, 0xc3, 0x07, 0xf1, 0xf7, 0x90, 0xf1, 0x25, 0xa4, + 0xb8, 0x58, 0x8b, 0x3c, 0xeb, 0xf1, 0x88, 0x2d, 0x31, 0xfa, 0xc6, 0x43, 0x58, 0xf7, 0x45, 0xd3, + 0xae, 0xdb, 0xa1, 0x04, 0x7f, 0x08, 0x09, 0xa7, 0xd3, 0x70, 0x85, 0xe4, 0x54, 0xf1, 0xed, 0x59, + 0xbc, 0xdd, 0x4d, 0x70, 0xfd, 0x96, 0xc0, 0x19, 0xcf, 0x11, 0x5c, 0x79, 0x2c, 0xa2, 0x17, 0x58, + 0x7b, 0x49, 0x89, 0xf8, 0x0e, 0xa4, 0xfc, 0x74, 0x88, 0x3a, 0x16, 0xc1, 0x99, 0x96, 0xc7, 0x7b, + 0xbc, 0xd4, 0x0f, 0x6c, 0x7a, 0x64, 0xc9, 0xac, 0x73, 0xda, 0xf8, 0x0c, 0xae, 0x06, 0xd6, 0x2c, + 0xc9, 0x41, 0x13, 0xf0, 0x03, 0x87, 0xb2, 0xb2, 0xcf, 0x12, 0x38, 0x99, 0x85, 0x95, 0x86, 0xd3, + 0x62, 0xc4, 0xa3, 0x59, 0xa4, 0x2b, 0xf9, 0x35, 0x2b, 0x38, 0x1a, 0x8f, 0x61, 0x63, 0x84, 0x7f, + 0xc2, 0x0c, 0x65, 0x21, 0x33, 0xaa, 0x90, 0xb9, 0x4b, 0x5a, 0x84, 0x91, 0x31, 0x43, 0x96, 0x59, + 0x1b, 0xcf, 0x11, 0x60, 0x8b, 0xd8, 0xf5, 0xff, 0x4f, 0x05, 0xbe, 0x06, 0xaa, 0xdb, 0x68, 0x50, + 0xc2, 0x64, 0xfb, 0xcb, 0xd3, 0x60, 0x28, 0x28, 0xc3, 0xa1, 0x60, 0x94, 0x60, 0x63, 0xc4, 0x1a, + 0x19, 0xc9, 0xa1, 0x08, 0x34, 0x2e, 0xa2, 0x6e, 0x33, 0x5b, 0x08, 0x5e, 0xb7, 0x04, 0x6d, 0xfc, + 0x1c, 0x07, 0xf5, 0x11, 0xb3, 0x59, 0x8f, 0xf2, 0xe9, 0x40, 0x99, 0xed, 0xc9, 0xe9, 0x80, 0xe6, + 0x99, 0x0e, 0x12, 0x37, 0x31, 0x62, 0xe2, 0x8b, 0x8d, 0x98, 0x34, 0x28, 0x1e, 0x69, 0x08, 0x57, + 0xd7, 0x2c, 0x4e, 0x86, 0x5c, 0x4a, 0x8c, 0xb8, 0x94, 0x81, 0x24, 0x73, 0x99, 0xdd, 0xca, 0x26, + 0xc5, 0xb5, 0x7f, 0xc0, 0x0f, 0x61, 0x95, 0x7c, 0xd3, 0x25, 0x35, 0x46, 0xea, 0x59, 0x75, 0xe1, + 0x8c, 0x0c, 0x64, 0x18, 0xd7, 0xe1, 0x8a, 0x1f, 0xa3, 0x20, 0xe1, 0xd2, 0x40, 0x34, 0x30, 0x90, + 0xb7, 0x55, 0xc0, 0x32, 0xa8, 0x67, 0x95, 0x8a, 0x1b, 0x19, 0xca, 0x77, 0xa2, 0x2a, 0x5a, 0xe2, + 0x25, 0xca, 0x28, 0xf8, 0x6d, 0xe2, 0xdf, 0x12, 0x1a, 0xdd, 0x57, 0x5f, 0x41, 0x66, 0x14, 0x20, + 0x0d, 0xb9, 0x0f, 0xab, 0x54, 0xde, 0xc9, 0xe6, 0x9a, 0xd1, 0x14, 0xd9, 0x5e, 0x03, 0xb4, 0xf1, + 0x93, 0x02, 0x1b, 0x87, 0x9e, 0x33, 0xd1, 0x62, 0x65, 0x50, 0xed, 0x1a, 0x73, 0xdc, 0x8e, 0x70, + 0xf5, 0x6a, 0xf1, 0x66, 0x94, 0x7c, 0x21, 0xa4, 0x24, 0x20, 0x96, 0x84, 0x06, 0x31, 0x8d, 0x0f, + 0x93, 0x3e, 0x48, 0xae, 0x72, 0x51, 0x72, 0x13, 0x97, 0x4f, 0x6e, 0xa8, 0xb4, 0x92, 0x53, 0xbb, + 0x45, 0x1d, 0x76, 0x0b, 0x3e, 0x1c, 0xec, 0xbe, 0x15, 0x11, 0xc8, 0x8f, 0x66, 0x72, 0x74, 0x34, + 0x5a, 0xcb, 0x5e, 0x85, 0x2f, 0xe3, 0x90, 0x19, 0x55, 0x23, 0xf3, 0xbe, 0x94, 0xac, 0x8c, 0x0e, + 0x85, 0xf8, 0x32, 0x86, 0x82, 0xb2, 0xd8, 0x50, 0x98, 0x6f, 0x04, 0x0c, 0x47, 0xb2, 0x7a, 0xe9, + 0xa9, 0xaf, 0xc3, 0x7a, 0xa9, 0xea, 0x7a, 0xec, 0xc2, 0xee, 0xbf, 0xf1, 0x3d, 0x82, 0x54, 0x28, + 0x7a, 0xf8, 0x2d, 0x48, 0x3c, 0xaa, 0x94, 0x2a, 0xe9, 0x98, 0xb6, 0x71, 0x72, 0xaa, 0xbf, 0x16, + 0xfa, 0xc4, 0x3b, 0x0b, 0x6f, 0x42, 0xf2, 0xd0, 0xda, 0xaf, 0xec, 0xa5, 0x91, 0x96, 0x39, 0x39, + 0xd5, 0xd3, 0xa1, 0xef, 0x82, 0xc4, 0xd7, 0x41, 0x2d, 0x7f, 0x7a, 0x70, 0xb0, 0x5f, 0x49, 0xc7, + 0xb5, 0x37, 0x4e, 0x4e, 0xf5, 0xd7, 0x43, 0x1c, 0x65, 0xb7, 0xdd, 0x76, 0x98, 0xb6, 0xf1, 0xc3, + 0x2f, 0xb9, 0xd8, 0x6f, 0xbf, 0xe6, 0xc2, 0x7a, 0x8b, 0xbf, 0xaf, 0xc0, 0x8a, 0x2c, 0x03, 0x6c, + 0xcb, 0x97, 0xe9, 0xcd, 0x59, 0x36, 0xa9, 0x74, 0x4d, 0x7b, 0x77, 0x36, 0x66, 0x59, 0x61, 0x4d, + 0x50, 0xfd, 0xb7, 0x04, 0xde, 0x8a, 0xc2, 0x8d, 0xbc, 0x80, 0x34, 0x73, 0x56, 0x76, 0xa9, 0xe8, + 0x19, 0x24, 0xf8, 0x68, 0xc3, 0xc5, 0x28, 0xdc, 0xe4, 0x43, 0x44, 0xdb, 0x99, 0x0b, 0xe3, 0x2b, + 0xdc, 0x46, 0xf8, 0x73, 0x50, 0xfd, 0xe7, 0x04, 0xbe, 0x1d, 0x25, 0x60, 0xda, 0xb3, 0x43, 0xbb, + 0x36, 0x51, 0xdf, 0x7b, 0xfc, 0x7f, 0x03, 0x77, 0x85, 0xef, 0xec, 0x68, 0x57, 0x26, 0xdf, 0x19, + 0xd1, 0xae, 0x4c, 0x79, 0x0d, 0x6c, 0x23, 0x9e, 0x26, 0xb9, 0xe2, 0xb7, 0x66, 0xdc, 0x41, 0xb3, + 0xa6, 0x69, 0x6c, 0xe5, 0x1d, 0xc3, 0x7a, 0x78, 0x03, 0xe1, 0x99, 0x42, 0x3f, 0xb6, 0xe0, 0xb4, + 0xdb, 0xf3, 0x81, 0xa4, 0xea, 0x3e, 0x24, 0xfd, 0xd6, 0xd9, 0x59, 0x60, 0x24, 0x47, 0xeb, 0x9c, + 0x36, 0x60, 0xf3, 0x68, 0x1b, 0xe1, 0x03, 0x48, 0x8a, 0xd9, 0x80, 0x23, 0x3b, 0x27, 0x3c, 0x42, + 0x2e, 0xaa, 0x8e, 0xdd, 0x27, 0x67, 0xaf, 0x72, 0xb1, 0x3f, 0x5f, 0xe5, 0x62, 0xdf, 0x9d, 0xe7, + 0xd0, 0xd9, 0x79, 0x0e, 0xfd, 0x71, 0x9e, 0x43, 0x7f, 0x9f, 0xe7, 0xd0, 0x93, 0x8f, 0x17, 0xfd, + 0x1f, 0x7d, 0x47, 0x92, 0x5f, 0xc4, 0xaa, 0xaa, 0xd0, 0xb6, 0xf3, 0x6f, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xc0, 0xc2, 0x35, 0xb1, 0x94, 0x0f, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -403,8 +1013,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Content service - +// ContentClient is the client API for Content service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ContentClient interface { // Info returns information about a committed object. // @@ -425,7 +1036,7 @@ type ContentClient interface { // set. List(ctx context.Context, in *ListContentRequest, opts ...grpc.CallOption) (Content_ListClient, error) // Delete will delete the referenced object. - Delete(ctx context.Context, in *DeleteContentRequest, opts ...grpc.CallOption) (*google_protobuf3.Empty, error) + Delete(ctx context.Context, in *DeleteContentRequest, opts ...grpc.CallOption) (*types.Empty, error) // Read allows one to read an object based on the offset into the content. // // The requested data may be returned in one or more messages. @@ -458,7 +1069,7 @@ type ContentClient interface { Write(ctx context.Context, opts ...grpc.CallOption) (Content_WriteClient, error) // Abort cancels the ongoing write named in the request. Any resources // associated with the write will be collected. - Abort(ctx context.Context, in *AbortRequest, opts ...grpc.CallOption) (*google_protobuf3.Empty, error) + Abort(ctx context.Context, in *AbortRequest, opts ...grpc.CallOption) (*types.Empty, error) } type contentClient struct { @@ -471,7 +1082,7 @@ func NewContentClient(cc *grpc.ClientConn) ContentClient { func (c *contentClient) Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, error) { out := new(InfoResponse) - err := grpc.Invoke(ctx, "/containerd.services.content.v1.Content/Info", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.content.v1.Content/Info", in, out, opts...) if err != nil { return nil, err } @@ -480,7 +1091,7 @@ func (c *contentClient) Info(ctx context.Context, in *InfoRequest, opts ...grpc. func (c *contentClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*UpdateResponse, error) { out := new(UpdateResponse) - err := grpc.Invoke(ctx, "/containerd.services.content.v1.Content/Update", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.content.v1.Content/Update", in, out, opts...) if err != nil { return nil, err } @@ -488,7 +1099,7 @@ func (c *contentClient) Update(ctx context.Context, in *UpdateRequest, opts ...g } func (c *contentClient) List(ctx context.Context, in *ListContentRequest, opts ...grpc.CallOption) (Content_ListClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Content_serviceDesc.Streams[0], c.cc, "/containerd.services.content.v1.Content/List", opts...) + stream, err := c.cc.NewStream(ctx, &_Content_serviceDesc.Streams[0], "/containerd.services.content.v1.Content/List", opts...) if err != nil { return nil, err } @@ -519,9 +1130,9 @@ func (x *contentListClient) Recv() (*ListContentResponse, error) { return m, nil } -func (c *contentClient) Delete(ctx context.Context, in *DeleteContentRequest, opts ...grpc.CallOption) (*google_protobuf3.Empty, error) { - out := new(google_protobuf3.Empty) - err := grpc.Invoke(ctx, "/containerd.services.content.v1.Content/Delete", in, out, c.cc, opts...) +func (c *contentClient) Delete(ctx context.Context, in *DeleteContentRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.content.v1.Content/Delete", in, out, opts...) if err != nil { return nil, err } @@ -529,7 +1140,7 @@ func (c *contentClient) Delete(ctx context.Context, in *DeleteContentRequest, op } func (c *contentClient) Read(ctx context.Context, in *ReadContentRequest, opts ...grpc.CallOption) (Content_ReadClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Content_serviceDesc.Streams[1], c.cc, "/containerd.services.content.v1.Content/Read", opts...) + stream, err := c.cc.NewStream(ctx, &_Content_serviceDesc.Streams[1], "/containerd.services.content.v1.Content/Read", opts...) if err != nil { return nil, err } @@ -562,7 +1173,7 @@ func (x *contentReadClient) Recv() (*ReadContentResponse, error) { func (c *contentClient) Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error) { out := new(StatusResponse) - err := grpc.Invoke(ctx, "/containerd.services.content.v1.Content/Status", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.content.v1.Content/Status", in, out, opts...) if err != nil { return nil, err } @@ -571,7 +1182,7 @@ func (c *contentClient) Status(ctx context.Context, in *StatusRequest, opts ...g func (c *contentClient) ListStatuses(ctx context.Context, in *ListStatusesRequest, opts ...grpc.CallOption) (*ListStatusesResponse, error) { out := new(ListStatusesResponse) - err := grpc.Invoke(ctx, "/containerd.services.content.v1.Content/ListStatuses", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.content.v1.Content/ListStatuses", in, out, opts...) if err != nil { return nil, err } @@ -579,7 +1190,7 @@ func (c *contentClient) ListStatuses(ctx context.Context, in *ListStatusesReques } func (c *contentClient) Write(ctx context.Context, opts ...grpc.CallOption) (Content_WriteClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Content_serviceDesc.Streams[2], c.cc, "/containerd.services.content.v1.Content/Write", opts...) + stream, err := c.cc.NewStream(ctx, &_Content_serviceDesc.Streams[2], "/containerd.services.content.v1.Content/Write", opts...) if err != nil { return nil, err } @@ -609,17 +1220,16 @@ func (x *contentWriteClient) Recv() (*WriteContentResponse, error) { return m, nil } -func (c *contentClient) Abort(ctx context.Context, in *AbortRequest, opts ...grpc.CallOption) (*google_protobuf3.Empty, error) { - out := new(google_protobuf3.Empty) - err := grpc.Invoke(ctx, "/containerd.services.content.v1.Content/Abort", in, out, c.cc, opts...) +func (c *contentClient) Abort(ctx context.Context, in *AbortRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.content.v1.Content/Abort", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Content service - +// ContentServer is the server API for Content service. type ContentServer interface { // Info returns information about a committed object. // @@ -640,7 +1250,7 @@ type ContentServer interface { // set. List(*ListContentRequest, Content_ListServer) error // Delete will delete the referenced object. - Delete(context.Context, *DeleteContentRequest) (*google_protobuf3.Empty, error) + Delete(context.Context, *DeleteContentRequest) (*types.Empty, error) // Read allows one to read an object based on the offset into the content. // // The requested data may be returned in one or more messages. @@ -673,7 +1283,39 @@ type ContentServer interface { Write(Content_WriteServer) error // Abort cancels the ongoing write named in the request. Any resources // associated with the write will be collected. - Abort(context.Context, *AbortRequest) (*google_protobuf3.Empty, error) + Abort(context.Context, *AbortRequest) (*types.Empty, error) +} + +// UnimplementedContentServer can be embedded to have forward compatible implementations. +type UnimplementedContentServer struct { +} + +func (*UnimplementedContentServer) Info(ctx context.Context, req *InfoRequest) (*InfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Info not implemented") +} +func (*UnimplementedContentServer) Update(ctx context.Context, req *UpdateRequest) (*UpdateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (*UnimplementedContentServer) List(req *ListContentRequest, srv Content_ListServer) error { + return status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (*UnimplementedContentServer) Delete(ctx context.Context, req *DeleteContentRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") +} +func (*UnimplementedContentServer) Read(req *ReadContentRequest, srv Content_ReadServer) error { + return status.Errorf(codes.Unimplemented, "method Read not implemented") +} +func (*UnimplementedContentServer) Status(ctx context.Context, req *StatusRequest) (*StatusResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Status not implemented") +} +func (*UnimplementedContentServer) ListStatuses(ctx context.Context, req *ListStatusesRequest) (*ListStatusesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListStatuses not implemented") +} +func (*UnimplementedContentServer) Write(srv Content_WriteServer) error { + return status.Errorf(codes.Unimplemented, "method Write not implemented") +} +func (*UnimplementedContentServer) Abort(ctx context.Context, req *AbortRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Abort not implemented") } func RegisterContentServer(s *grpc.Server, srv ContentServer) { @@ -909,7 +1551,7 @@ var _Content_serviceDesc = grpc.ServiceDesc{ func (m *Info) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -917,61 +1559,73 @@ func (m *Info) Marshal() (dAtA []byte, err error) { } func (m *Info) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Info) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Digest) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(len(m.Digest))) - i += copy(dAtA[i:], m.Digest) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if m.Size_ != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Size_)) - } - dAtA[i] = 0x1a - i++ - i = encodeVarintContent(dAtA, i, uint64(types.SizeOfStdTime(m.CreatedAt))) - n1, err := types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 - dAtA[i] = 0x22 - i++ - i = encodeVarintContent(dAtA, i, uint64(types.SizeOfStdTime(m.UpdatedAt))) - n2, err := types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x2a - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovContent(uint64(len(k))) + 1 + len(v) + sovContent(uint64(len(v))) - i = encodeVarintContent(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) i = encodeVarintContent(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintContent(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintContent(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a } } - return i, nil + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintContent(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x22 + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintContent(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x1a + if m.Size_ != 0 { + i = encodeVarintContent(dAtA, i, uint64(m.Size_)) + i-- + dAtA[i] = 0x10 + } + if len(m.Digest) > 0 { + i -= len(m.Digest) + copy(dAtA[i:], m.Digest) + i = encodeVarintContent(dAtA, i, uint64(len(m.Digest))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *InfoRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -979,23 +1633,33 @@ func (m *InfoRequest) Marshal() (dAtA []byte, err error) { } func (m *InfoRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *InfoRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Digest) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Digest) + copy(dAtA[i:], m.Digest) i = encodeVarintContent(dAtA, i, uint64(len(m.Digest))) - i += copy(dAtA[i:], m.Digest) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *InfoResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1003,25 +1667,36 @@ func (m *InfoResponse) Marshal() (dAtA []byte, err error) { } func (m *InfoResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *InfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Info.Size())) - n3, err := m.Info.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Info.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContent(dAtA, i, uint64(size)) } - i += n3 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *UpdateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1029,35 +1704,48 @@ func (m *UpdateRequest) Marshal() (dAtA []byte, err error) { } func (m *UpdateRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Info.Size())) - n4, err := m.Info.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n4 if m.UpdateMask != nil { + { + size, err := m.UpdateMask.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContent(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintContent(dAtA, i, uint64(m.UpdateMask.Size())) - n5, err := m.UpdateMask.MarshalTo(dAtA[i:]) + } + { + size, err := m.Info.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n5 + i -= size + i = encodeVarintContent(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *UpdateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1065,25 +1753,36 @@ func (m *UpdateResponse) Marshal() (dAtA []byte, err error) { } func (m *UpdateResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Info.Size())) - n6, err := m.Info.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Info.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContent(dAtA, i, uint64(size)) } - i += n6 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *ListContentRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1091,32 +1790,35 @@ func (m *ListContentRequest) Marshal() (dAtA []byte, err error) { } func (m *ListContentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListContentRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Filters) > 0 { - for _, s := range m.Filters { + for iNdEx := len(m.Filters) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Filters[iNdEx]) + copy(dAtA[i:], m.Filters[iNdEx]) + i = encodeVarintContent(dAtA, i, uint64(len(m.Filters[iNdEx]))) + i-- dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - return i, nil + return len(dAtA) - i, nil } func (m *ListContentResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1124,29 +1826,40 @@ func (m *ListContentResponse) Marshal() (dAtA []byte, err error) { } func (m *ListContentResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListContentResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Info) > 0 { - for _, msg := range m.Info { - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Info) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Info[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContent(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *DeleteContentRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1154,23 +1867,33 @@ func (m *DeleteContentRequest) Marshal() (dAtA []byte, err error) { } func (m *DeleteContentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteContentRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Digest) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Digest) + copy(dAtA[i:], m.Digest) i = encodeVarintContent(dAtA, i, uint64(len(m.Digest))) - i += copy(dAtA[i:], m.Digest) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ReadContentRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1178,33 +1901,43 @@ func (m *ReadContentRequest) Marshal() (dAtA []byte, err error) { } func (m *ReadContentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReadContentRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Digest) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(len(m.Digest))) - i += copy(dAtA[i:], m.Digest) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Size_ != 0 { + i = encodeVarintContent(dAtA, i, uint64(m.Size_)) + i-- + dAtA[i] = 0x18 } if m.Offset != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintContent(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x10 } - if m.Size_ != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Size_)) + if len(m.Digest) > 0 { + i -= len(m.Digest) + copy(dAtA[i:], m.Digest) + i = encodeVarintContent(dAtA, i, uint64(len(m.Digest))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ReadContentResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1212,28 +1945,38 @@ func (m *ReadContentResponse) Marshal() (dAtA []byte, err error) { } func (m *ReadContentResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReadContentResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Offset != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Offset)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Data) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Data) + copy(dAtA[i:], m.Data) i = encodeVarintContent(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) + i-- + dAtA[i] = 0x12 } - return i, nil + if m.Offset != 0 { + i = encodeVarintContent(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func (m *Status) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1241,55 +1984,66 @@ func (m *Status) Marshal() (dAtA []byte, err error) { } func (m *Status) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Status) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(types.SizeOfStdTime(m.StartedAt))) - n7, err := types.StdTimeMarshalTo(m.StartedAt, dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n7 - dAtA[i] = 0x12 - i++ - i = encodeVarintContent(dAtA, i, uint64(types.SizeOfStdTime(m.UpdatedAt))) - n8, err := types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i:]) - if err != nil { - return 0, err + if len(m.Expected) > 0 { + i -= len(m.Expected) + copy(dAtA[i:], m.Expected) + i = encodeVarintContent(dAtA, i, uint64(len(m.Expected))) + i-- + dAtA[i] = 0x32 } - i += n8 - if len(m.Ref) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintContent(dAtA, i, uint64(len(m.Ref))) - i += copy(dAtA[i:], m.Ref) + if m.Total != 0 { + i = encodeVarintContent(dAtA, i, uint64(m.Total)) + i-- + dAtA[i] = 0x28 } if m.Offset != 0 { - dAtA[i] = 0x20 - i++ i = encodeVarintContent(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x20 } - if m.Total != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Total)) + if len(m.Ref) > 0 { + i -= len(m.Ref) + copy(dAtA[i:], m.Ref) + i = encodeVarintContent(dAtA, i, uint64(len(m.Ref))) + i-- + dAtA[i] = 0x1a } - if len(m.Expected) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintContent(dAtA, i, uint64(len(m.Expected))) - i += copy(dAtA[i:], m.Expected) + n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt):]) + if err7 != nil { + return 0, err7 + } + i -= n7 + i = encodeVarintContent(dAtA, i, uint64(n7)) + i-- + dAtA[i] = 0x12 + n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.StartedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.StartedAt):]) + if err8 != nil { + return 0, err8 } - return i, nil + i -= n8 + i = encodeVarintContent(dAtA, i, uint64(n8)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *StatusRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1297,23 +2051,33 @@ func (m *StatusRequest) Marshal() (dAtA []byte, err error) { } func (m *StatusRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StatusRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Ref) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Ref) + copy(dAtA[i:], m.Ref) i = encodeVarintContent(dAtA, i, uint64(len(m.Ref))) - i += copy(dAtA[i:], m.Ref) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *StatusResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1321,27 +2085,38 @@ func (m *StatusResponse) Marshal() (dAtA []byte, err error) { } func (m *StatusResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Status != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Status.Size())) - n9, err := m.Status.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContent(dAtA, i, uint64(size)) } - i += n9 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListStatusesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1349,32 +2124,35 @@ func (m *ListStatusesRequest) Marshal() (dAtA []byte, err error) { } func (m *ListStatusesRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListStatusesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Filters) > 0 { - for _, s := range m.Filters { + for iNdEx := len(m.Filters) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Filters[iNdEx]) + copy(dAtA[i:], m.Filters[iNdEx]) + i = encodeVarintContent(dAtA, i, uint64(len(m.Filters[iNdEx]))) + i-- dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - return i, nil + return len(dAtA) - i, nil } func (m *ListStatusesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1382,29 +2160,40 @@ func (m *ListStatusesResponse) Marshal() (dAtA []byte, err error) { } func (m *ListStatusesResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListStatusesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Statuses) > 0 { - for _, msg := range m.Statuses { - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Statuses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Statuses[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintContent(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *WriteContentRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1412,67 +2201,81 @@ func (m *WriteContentRequest) Marshal() (dAtA []byte, err error) { } func (m *WriteContentRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WriteContentRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Action != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Action)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Ref) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintContent(dAtA, i, uint64(len(m.Ref))) - i += copy(dAtA[i:], m.Ref) + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintContent(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintContent(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintContent(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x3a + } } - if m.Total != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Total)) + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintContent(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x32 + } + if m.Offset != 0 { + i = encodeVarintContent(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x28 } if len(m.Expected) > 0 { - dAtA[i] = 0x22 - i++ + i -= len(m.Expected) + copy(dAtA[i:], m.Expected) i = encodeVarintContent(dAtA, i, uint64(len(m.Expected))) - i += copy(dAtA[i:], m.Expected) + i-- + dAtA[i] = 0x22 } - if m.Offset != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Offset)) + if m.Total != 0 { + i = encodeVarintContent(dAtA, i, uint64(m.Total)) + i-- + dAtA[i] = 0x18 } - if len(m.Data) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintContent(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) + if len(m.Ref) > 0 { + i -= len(m.Ref) + copy(dAtA[i:], m.Ref) + i = encodeVarintContent(dAtA, i, uint64(len(m.Ref))) + i-- + dAtA[i] = 0x12 } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x3a - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovContent(uint64(len(k))) + 1 + len(v) + sovContent(uint64(len(v))) - i = encodeVarintContent(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintContent(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintContent(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) - } + if m.Action != 0 { + i = encodeVarintContent(dAtA, i, uint64(m.Action)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *WriteContentResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1480,54 +2283,64 @@ func (m *WriteContentResponse) Marshal() (dAtA []byte, err error) { } func (m *WriteContentResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WriteContentResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Action != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Action)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - dAtA[i] = 0x12 - i++ - i = encodeVarintContent(dAtA, i, uint64(types.SizeOfStdTime(m.StartedAt))) - n10, err := types.StdTimeMarshalTo(m.StartedAt, dAtA[i:]) - if err != nil { - return 0, err + if len(m.Digest) > 0 { + i -= len(m.Digest) + copy(dAtA[i:], m.Digest) + i = encodeVarintContent(dAtA, i, uint64(len(m.Digest))) + i-- + dAtA[i] = 0x32 } - i += n10 - dAtA[i] = 0x1a - i++ - i = encodeVarintContent(dAtA, i, uint64(types.SizeOfStdTime(m.UpdatedAt))) - n11, err := types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i:]) - if err != nil { - return 0, err + if m.Total != 0 { + i = encodeVarintContent(dAtA, i, uint64(m.Total)) + i-- + dAtA[i] = 0x28 } - i += n11 if m.Offset != 0 { - dAtA[i] = 0x20 - i++ i = encodeVarintContent(dAtA, i, uint64(m.Offset)) + i-- + dAtA[i] = 0x20 } - if m.Total != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintContent(dAtA, i, uint64(m.Total)) + n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt):]) + if err10 != nil { + return 0, err10 } - if len(m.Digest) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintContent(dAtA, i, uint64(len(m.Digest))) - i += copy(dAtA[i:], m.Digest) + i -= n10 + i = encodeVarintContent(dAtA, i, uint64(n10)) + i-- + dAtA[i] = 0x1a + n11, err11 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.StartedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.StartedAt):]) + if err11 != nil { + return 0, err11 + } + i -= n11 + i = encodeVarintContent(dAtA, i, uint64(n11)) + i-- + dAtA[i] = 0x12 + if m.Action != 0 { + i = encodeVarintContent(dAtA, i, uint64(m.Action)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *AbortRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1535,29 +2348,44 @@ func (m *AbortRequest) Marshal() (dAtA []byte, err error) { } func (m *AbortRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AbortRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Ref) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Ref) + copy(dAtA[i:], m.Ref) i = encodeVarintContent(dAtA, i, uint64(len(m.Ref))) - i += copy(dAtA[i:], m.Ref) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintContent(dAtA []byte, offset int, v uint64) int { + offset -= sovContent(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Info) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Digest) @@ -1567,9 +2395,9 @@ func (m *Info) Size() (n int) { if m.Size_ != 0 { n += 1 + sovContent(uint64(m.Size_)) } - l = types.SizeOfStdTime(m.CreatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt) n += 1 + l + sovContent(uint64(l)) - l = types.SizeOfStdTime(m.UpdatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt) n += 1 + l + sovContent(uint64(l)) if len(m.Labels) > 0 { for k, v := range m.Labels { @@ -1579,28 +2407,46 @@ func (m *Info) Size() (n int) { n += mapEntrySize + 1 + sovContent(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *InfoRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Digest) if l > 0 { n += 1 + l + sovContent(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *InfoResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Info.Size() n += 1 + l + sovContent(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Info.Size() @@ -1609,18 +2455,30 @@ func (m *UpdateRequest) Size() (n int) { l = m.UpdateMask.Size() n += 1 + l + sovContent(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Info.Size() n += 1 + l + sovContent(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListContentRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Filters) > 0 { @@ -1629,10 +2487,16 @@ func (m *ListContentRequest) Size() (n int) { n += 1 + l + sovContent(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListContentResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Info) > 0 { @@ -1641,20 +2505,32 @@ func (m *ListContentResponse) Size() (n int) { n += 1 + l + sovContent(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DeleteContentRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Digest) if l > 0 { n += 1 + l + sovContent(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ReadContentRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Digest) @@ -1667,10 +2543,16 @@ func (m *ReadContentRequest) Size() (n int) { if m.Size_ != 0 { n += 1 + sovContent(uint64(m.Size_)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ReadContentResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Offset != 0 { @@ -1680,15 +2562,21 @@ func (m *ReadContentResponse) Size() (n int) { if l > 0 { n += 1 + l + sovContent(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *Status) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l - l = types.SizeOfStdTime(m.StartedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.StartedAt) n += 1 + l + sovContent(uint64(l)) - l = types.SizeOfStdTime(m.UpdatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt) n += 1 + l + sovContent(uint64(l)) l = len(m.Ref) if l > 0 { @@ -1704,30 +2592,48 @@ func (m *Status) Size() (n int) { if l > 0 { n += 1 + l + sovContent(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StatusRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Ref) if l > 0 { n += 1 + l + sovContent(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StatusResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Status != nil { l = m.Status.Size() n += 1 + l + sovContent(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListStatusesRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Filters) > 0 { @@ -1736,10 +2642,16 @@ func (m *ListStatusesRequest) Size() (n int) { n += 1 + l + sovContent(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListStatusesResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Statuses) > 0 { @@ -1748,10 +2660,16 @@ func (m *ListStatusesResponse) Size() (n int) { n += 1 + l + sovContent(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *WriteContentRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Action != 0 { @@ -1783,18 +2701,24 @@ func (m *WriteContentRequest) Size() (n int) { n += mapEntrySize + 1 + sovContent(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *WriteContentResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Action != 0 { n += 1 + sovContent(uint64(m.Action)) } - l = types.SizeOfStdTime(m.StartedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.StartedAt) n += 1 + l + sovContent(uint64(l)) - l = types.SizeOfStdTime(m.UpdatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt) n += 1 + l + sovContent(uint64(l)) if m.Offset != 0 { n += 1 + sovContent(uint64(m.Offset)) @@ -1806,28 +2730,30 @@ func (m *WriteContentResponse) Size() (n int) { if l > 0 { n += 1 + l + sovContent(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *AbortRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Ref) if l > 0 { n += 1 + l + sovContent(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovContent(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozContent(x uint64) (n int) { return sovContent(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -1840,7 +2766,7 @@ func (this *Info) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -1849,9 +2775,10 @@ func (this *Info) String() string { s := strings.Join([]string{`&Info{`, `Digest:` + fmt.Sprintf("%v", this.Digest) + `,`, `Size_:` + fmt.Sprintf("%v", this.Size_) + `,`, - `CreatedAt:` + strings.Replace(strings.Replace(this.CreatedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, - `UpdatedAt:` + strings.Replace(strings.Replace(this.UpdatedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `CreatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.CreatedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, + `UpdatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.UpdatedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1862,6 +2789,7 @@ func (this *InfoRequest) String() string { } s := strings.Join([]string{`&InfoRequest{`, `Digest:` + fmt.Sprintf("%v", this.Digest) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1872,6 +2800,7 @@ func (this *InfoResponse) String() string { } s := strings.Join([]string{`&InfoResponse{`, `Info:` + strings.Replace(strings.Replace(this.Info.String(), "Info", "Info", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1882,7 +2811,8 @@ func (this *UpdateRequest) String() string { } s := strings.Join([]string{`&UpdateRequest{`, `Info:` + strings.Replace(strings.Replace(this.Info.String(), "Info", "Info", 1), `&`, ``, 1) + `,`, - `UpdateMask:` + strings.Replace(fmt.Sprintf("%v", this.UpdateMask), "FieldMask", "google_protobuf1.FieldMask", 1) + `,`, + `UpdateMask:` + strings.Replace(fmt.Sprintf("%v", this.UpdateMask), "FieldMask", "types.FieldMask", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1893,6 +2823,7 @@ func (this *UpdateResponse) String() string { } s := strings.Join([]string{`&UpdateResponse{`, `Info:` + strings.Replace(strings.Replace(this.Info.String(), "Info", "Info", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1903,6 +2834,7 @@ func (this *ListContentRequest) String() string { } s := strings.Join([]string{`&ListContentRequest{`, `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1911,8 +2843,14 @@ func (this *ListContentResponse) String() string { if this == nil { return "nil" } + repeatedStringForInfo := "[]Info{" + for _, f := range this.Info { + repeatedStringForInfo += strings.Replace(strings.Replace(f.String(), "Info", "Info", 1), `&`, ``, 1) + "," + } + repeatedStringForInfo += "}" s := strings.Join([]string{`&ListContentResponse{`, - `Info:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Info), "Info", "Info", 1), `&`, ``, 1) + `,`, + `Info:` + repeatedStringForInfo + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1923,6 +2861,7 @@ func (this *DeleteContentRequest) String() string { } s := strings.Join([]string{`&DeleteContentRequest{`, `Digest:` + fmt.Sprintf("%v", this.Digest) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1935,6 +2874,7 @@ func (this *ReadContentRequest) String() string { `Digest:` + fmt.Sprintf("%v", this.Digest) + `,`, `Offset:` + fmt.Sprintf("%v", this.Offset) + `,`, `Size_:` + fmt.Sprintf("%v", this.Size_) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1946,6 +2886,7 @@ func (this *ReadContentResponse) String() string { s := strings.Join([]string{`&ReadContentResponse{`, `Offset:` + fmt.Sprintf("%v", this.Offset) + `,`, `Data:` + fmt.Sprintf("%v", this.Data) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1955,12 +2896,13 @@ func (this *Status) String() string { return "nil" } s := strings.Join([]string{`&Status{`, - `StartedAt:` + strings.Replace(strings.Replace(this.StartedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, - `UpdatedAt:` + strings.Replace(strings.Replace(this.UpdatedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `StartedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.StartedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, + `UpdatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.UpdatedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, `Ref:` + fmt.Sprintf("%v", this.Ref) + `,`, `Offset:` + fmt.Sprintf("%v", this.Offset) + `,`, `Total:` + fmt.Sprintf("%v", this.Total) + `,`, `Expected:` + fmt.Sprintf("%v", this.Expected) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1971,6 +2913,7 @@ func (this *StatusRequest) String() string { } s := strings.Join([]string{`&StatusRequest{`, `Ref:` + fmt.Sprintf("%v", this.Ref) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1980,7 +2923,8 @@ func (this *StatusResponse) String() string { return "nil" } s := strings.Join([]string{`&StatusResponse{`, - `Status:` + strings.Replace(fmt.Sprintf("%v", this.Status), "Status", "Status", 1) + `,`, + `Status:` + strings.Replace(this.Status.String(), "Status", "Status", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1991,6 +2935,7 @@ func (this *ListStatusesRequest) String() string { } s := strings.Join([]string{`&ListStatusesRequest{`, `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1999,8 +2944,14 @@ func (this *ListStatusesResponse) String() string { if this == nil { return "nil" } + repeatedStringForStatuses := "[]Status{" + for _, f := range this.Statuses { + repeatedStringForStatuses += strings.Replace(strings.Replace(f.String(), "Status", "Status", 1), `&`, ``, 1) + "," + } + repeatedStringForStatuses += "}" s := strings.Join([]string{`&ListStatusesResponse{`, - `Statuses:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Statuses), "Status", "Status", 1), `&`, ``, 1) + `,`, + `Statuses:` + repeatedStringForStatuses + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2013,7 +2964,7 @@ func (this *WriteContentRequest) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -2027,6 +2978,7 @@ func (this *WriteContentRequest) String() string { `Offset:` + fmt.Sprintf("%v", this.Offset) + `,`, `Data:` + fmt.Sprintf("%v", this.Data) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2037,11 +2989,12 @@ func (this *WriteContentResponse) String() string { } s := strings.Join([]string{`&WriteContentResponse{`, `Action:` + fmt.Sprintf("%v", this.Action) + `,`, - `StartedAt:` + strings.Replace(strings.Replace(this.StartedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, - `UpdatedAt:` + strings.Replace(strings.Replace(this.UpdatedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `StartedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.StartedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, + `UpdatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.UpdatedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, `Offset:` + fmt.Sprintf("%v", this.Offset) + `,`, `Total:` + fmt.Sprintf("%v", this.Total) + `,`, `Digest:` + fmt.Sprintf("%v", this.Digest) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2052,6 +3005,7 @@ func (this *AbortRequest) String() string { } s := strings.Join([]string{`&AbortRequest{`, `Ref:` + fmt.Sprintf("%v", this.Ref) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2079,7 +3033,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2107,7 +3061,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2117,6 +3071,9 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2136,7 +3093,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Size_ |= (int64(b) & 0x7F) << shift + m.Size_ |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -2155,7 +3112,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2164,10 +3121,13 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -2185,7 +3145,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2194,10 +3154,13 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -2215,7 +3178,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2224,6 +3187,9 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2244,7 +3210,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2261,7 +3227,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2271,6 +3237,9 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthContent + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -2287,7 +3256,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2297,6 +3266,9 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthContent + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -2308,7 +3280,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > postIndex { @@ -2325,12 +3297,13 @@ func (m *Info) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2355,7 +3328,7 @@ func (m *InfoRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2383,7 +3356,7 @@ func (m *InfoRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2393,6 +3366,9 @@ func (m *InfoRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2404,12 +3380,13 @@ func (m *InfoRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2434,7 +3411,7 @@ func (m *InfoResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2462,7 +3439,7 @@ func (m *InfoResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2471,6 +3448,9 @@ func (m *InfoResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2484,12 +3464,13 @@ func (m *InfoResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2514,7 +3495,7 @@ func (m *UpdateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2542,7 +3523,7 @@ func (m *UpdateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2551,6 +3532,9 @@ func (m *UpdateRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2572,7 +3556,7 @@ func (m *UpdateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2581,11 +3565,14 @@ func (m *UpdateRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } if m.UpdateMask == nil { - m.UpdateMask = &google_protobuf1.FieldMask{} + m.UpdateMask = &types.FieldMask{} } if err := m.UpdateMask.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -2597,12 +3584,13 @@ func (m *UpdateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2627,7 +3615,7 @@ func (m *UpdateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2655,7 +3643,7 @@ func (m *UpdateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2664,6 +3652,9 @@ func (m *UpdateResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2677,12 +3668,13 @@ func (m *UpdateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2707,7 +3699,7 @@ func (m *ListContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2735,7 +3727,7 @@ func (m *ListContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2745,6 +3737,9 @@ func (m *ListContentRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2756,12 +3751,13 @@ func (m *ListContentRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2786,7 +3782,7 @@ func (m *ListContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2814,7 +3810,7 @@ func (m *ListContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2823,6 +3819,9 @@ func (m *ListContentResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2837,12 +3836,13 @@ func (m *ListContentResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2867,7 +3867,7 @@ func (m *DeleteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2895,7 +3895,7 @@ func (m *DeleteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2905,6 +3905,9 @@ func (m *DeleteContentRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2916,12 +3919,13 @@ func (m *DeleteContentRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2946,7 +3950,7 @@ func (m *ReadContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2974,7 +3978,7 @@ func (m *ReadContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2984,6 +3988,9 @@ func (m *ReadContentRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3003,7 +4010,7 @@ func (m *ReadContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Offset |= (int64(b) & 0x7F) << shift + m.Offset |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -3022,7 +4029,7 @@ func (m *ReadContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Size_ |= (int64(b) & 0x7F) << shift + m.Size_ |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -3033,12 +4040,13 @@ func (m *ReadContentRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3063,7 +4071,7 @@ func (m *ReadContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3091,7 +4099,7 @@ func (m *ReadContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Offset |= (int64(b) & 0x7F) << shift + m.Offset |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -3110,7 +4118,7 @@ func (m *ReadContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3119,6 +4127,9 @@ func (m *ReadContentResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3133,12 +4144,13 @@ func (m *ReadContentResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3163,7 +4175,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3191,7 +4203,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3200,10 +4212,13 @@ func (m *Status) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.StartedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.StartedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -3221,7 +4236,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3230,10 +4245,13 @@ func (m *Status) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -3251,7 +4269,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3261,6 +4279,9 @@ func (m *Status) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3280,7 +4301,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Offset |= (int64(b) & 0x7F) << shift + m.Offset |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -3299,7 +4320,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Total |= (int64(b) & 0x7F) << shift + m.Total |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -3318,7 +4339,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3328,6 +4349,9 @@ func (m *Status) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3339,12 +4363,13 @@ func (m *Status) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3369,7 +4394,7 @@ func (m *StatusRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3397,7 +4422,7 @@ func (m *StatusRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3407,6 +4432,9 @@ func (m *StatusRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3418,12 +4446,13 @@ func (m *StatusRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3448,7 +4477,7 @@ func (m *StatusResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3476,7 +4505,7 @@ func (m *StatusResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3485,6 +4514,9 @@ func (m *StatusResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3501,12 +4533,13 @@ func (m *StatusResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3531,7 +4564,7 @@ func (m *ListStatusesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3559,7 +4592,7 @@ func (m *ListStatusesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3569,6 +4602,9 @@ func (m *ListStatusesRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3580,12 +4616,13 @@ func (m *ListStatusesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3610,7 +4647,7 @@ func (m *ListStatusesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3638,7 +4675,7 @@ func (m *ListStatusesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3647,6 +4684,9 @@ func (m *ListStatusesResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3661,12 +4701,13 @@ func (m *ListStatusesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3691,7 +4732,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3719,7 +4760,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Action |= (WriteAction(b) & 0x7F) << shift + m.Action |= WriteAction(b&0x7F) << shift if b < 0x80 { break } @@ -3738,7 +4779,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3748,6 +4789,9 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3767,7 +4811,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Total |= (int64(b) & 0x7F) << shift + m.Total |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -3786,7 +4830,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3796,6 +4840,9 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3815,7 +4862,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Offset |= (int64(b) & 0x7F) << shift + m.Offset |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -3834,7 +4881,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3843,6 +4890,9 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3865,7 +4915,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3874,6 +4924,9 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3894,7 +4947,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3911,7 +4964,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3921,6 +4974,9 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthContent + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -3937,7 +4993,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3947,6 +5003,9 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthContent + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -3958,7 +5017,7 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > postIndex { @@ -3975,12 +5034,13 @@ func (m *WriteContentRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4005,7 +5065,7 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4033,7 +5093,7 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Action |= (WriteAction(b) & 0x7F) << shift + m.Action |= WriteAction(b&0x7F) << shift if b < 0x80 { break } @@ -4052,7 +5112,7 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4061,10 +5121,13 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.StartedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.StartedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -4082,7 +5145,7 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4091,10 +5154,13 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -4112,7 +5178,7 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Offset |= (int64(b) & 0x7F) << shift + m.Offset |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -4131,7 +5197,7 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Total |= (int64(b) & 0x7F) << shift + m.Total |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -4150,7 +5216,7 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4160,6 +5226,9 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4171,12 +5240,13 @@ func (m *WriteContentResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4201,7 +5271,7 @@ func (m *AbortRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4229,7 +5299,7 @@ func (m *AbortRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4239,6 +5309,9 @@ func (m *AbortRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthContent } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthContent + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4250,12 +5323,13 @@ func (m *AbortRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthContent } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4268,6 +5342,7 @@ func (m *AbortRequest) Unmarshal(dAtA []byte) error { func skipContent(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -4299,10 +5374,8 @@ func skipContent(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -4319,129 +5392,34 @@ func skipContent(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthContent } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowContent - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipContent(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupContent + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthContent + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthContent = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowContent = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthContent = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowContent = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupContent = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/content/v1/content.proto", fileDescriptorContent) -} - -var fileDescriptorContent = []byte{ - // 1081 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0xcd, 0x6f, 0x1b, 0x45, - 0x14, 0xf7, 0x78, 0xed, 0x4d, 0xf2, 0x9c, 0x16, 0x33, 0x31, 0x95, 0xb5, 0x08, 0x67, 0xbb, 0x42, - 0xc8, 0x6a, 0xc9, 0x3a, 0x75, 0x7a, 0x00, 0x2a, 0x01, 0x8e, 0x9b, 0xaa, 0x41, 0x4d, 0x41, 0x5b, - 0x97, 0x40, 0x2f, 0x65, 0x6d, 0x8f, 0xcd, 0x2a, 0xb6, 0xd7, 0xdd, 0x19, 0x5b, 0x84, 0x13, 0x17, - 0x24, 0x14, 0xf5, 0x80, 0xb8, 0xe7, 0x02, 0xfc, 0x15, 0x1c, 0x38, 0xe7, 0xc8, 0x11, 0x71, 0x68, - 0x69, 0xfe, 0x07, 0xee, 0x68, 0x66, 0x67, 0xed, 0xf5, 0x47, 0x58, 0xdb, 0x31, 0x27, 0xbf, 0x99, - 0x7d, 0xbf, 0xf7, 0xfd, 0x31, 0x86, 0x7b, 0x4d, 0x87, 0x7d, 0xdd, 0xab, 0x9a, 0x35, 0xb7, 0x5d, - 0xa8, 0xb9, 0x1d, 0x66, 0x3b, 0x1d, 0xe2, 0xd5, 0xc3, 0xa4, 0xdd, 0x75, 0x0a, 0x94, 0x78, 0x7d, - 0xa7, 0x46, 0xa8, 0xb8, 0x27, 0x1d, 0x56, 0xe8, 0xdf, 0x0a, 0x48, 0xb3, 0xeb, 0xb9, 0xcc, 0xc5, - 0xb9, 0x21, 0xc2, 0x0c, 0xb8, 0xcd, 0x80, 0xa5, 0x7f, 0x4b, 0xcb, 0x34, 0xdd, 0xa6, 0x2b, 0x58, - 0x0b, 0x9c, 0xf2, 0x51, 0x9a, 0xde, 0x74, 0xdd, 0x66, 0x8b, 0x14, 0xc4, 0xa9, 0xda, 0x6b, 0x14, - 0x1a, 0x0e, 0x69, 0xd5, 0x9f, 0xb6, 0x6d, 0x7a, 0x24, 0x39, 0x36, 0xc7, 0x39, 0x98, 0xd3, 0x26, - 0x94, 0xd9, 0xed, 0xae, 0x64, 0x78, 0x73, 0x9c, 0x81, 0xb4, 0xbb, 0xec, 0xd8, 0xff, 0x68, 0xfc, - 0x13, 0x87, 0xc4, 0x7e, 0xa7, 0xe1, 0xe2, 0x4f, 0x40, 0xad, 0x3b, 0x4d, 0x42, 0x59, 0x16, 0xe9, - 0x28, 0xbf, 0xb6, 0x5b, 0x3c, 0x7b, 0xb1, 0x19, 0xfb, 0xeb, 0xc5, 0xe6, 0x8d, 0x90, 0xfb, 0x6e, - 0x97, 0x74, 0x06, 0x5e, 0xd0, 0x42, 0xd3, 0xdd, 0xf2, 0x21, 0xe6, 0x5d, 0xf1, 0x63, 0x49, 0x09, - 0x18, 0x43, 0x82, 0x3a, 0xdf, 0x92, 0x6c, 0x5c, 0x47, 0x79, 0xc5, 0x12, 0x34, 0x2e, 0x03, 0xd4, - 0x3c, 0x62, 0x33, 0x52, 0x7f, 0x6a, 0xb3, 0xac, 0xa2, 0xa3, 0x7c, 0xaa, 0xa8, 0x99, 0xbe, 0x69, - 0x66, 0x60, 0x9a, 0x59, 0x09, 0x6c, 0xdf, 0x5d, 0xe5, 0xfa, 0x7f, 0x7c, 0xb9, 0x89, 0xac, 0x35, - 0x89, 0x2b, 0x31, 0x2e, 0xa4, 0xd7, 0xad, 0x07, 0x42, 0x12, 0xf3, 0x08, 0x91, 0xb8, 0x12, 0xc3, - 0xf7, 0x41, 0x6d, 0xd9, 0x55, 0xd2, 0xa2, 0xd9, 0xa4, 0xae, 0xe4, 0x53, 0xc5, 0x6d, 0xf3, 0xbf, - 0x33, 0x63, 0xf2, 0xf8, 0x98, 0x0f, 0x04, 0x64, 0xaf, 0xc3, 0xbc, 0x63, 0x4b, 0xe2, 0xb5, 0xf7, - 0x21, 0x15, 0xba, 0xc6, 0x69, 0x50, 0x8e, 0xc8, 0xb1, 0x1f, 0x3f, 0x8b, 0x93, 0x38, 0x03, 0xc9, - 0xbe, 0xdd, 0xea, 0xf9, 0x91, 0x58, 0xb3, 0xfc, 0xc3, 0x07, 0xf1, 0xf7, 0x90, 0xf1, 0x25, 0xa4, - 0xb8, 0x58, 0x8b, 0x3c, 0xeb, 0xf1, 0x88, 0x2d, 0x31, 0xfa, 0xc6, 0x43, 0x58, 0xf7, 0x45, 0xd3, - 0xae, 0xdb, 0xa1, 0x04, 0x7f, 0x08, 0x09, 0xa7, 0xd3, 0x70, 0x85, 0xe4, 0x54, 0xf1, 0xed, 0x59, - 0xbc, 0xdd, 0x4d, 0x70, 0xfd, 0x96, 0xc0, 0x19, 0xcf, 0x11, 0x5c, 0x79, 0x2c, 0xa2, 0x17, 0x58, - 0x7b, 0x49, 0x89, 0xf8, 0x0e, 0xa4, 0xfc, 0x74, 0x88, 0x3a, 0x16, 0xc1, 0x99, 0x96, 0xc7, 0x7b, - 0xbc, 0xd4, 0x0f, 0x6c, 0x7a, 0x64, 0xc9, 0xac, 0x73, 0xda, 0xf8, 0x0c, 0xae, 0x06, 0xd6, 0x2c, - 0xc9, 0x41, 0x13, 0xf0, 0x03, 0x87, 0xb2, 0xb2, 0xcf, 0x12, 0x38, 0x99, 0x85, 0x95, 0x86, 0xd3, - 0x62, 0xc4, 0xa3, 0x59, 0xa4, 0x2b, 0xf9, 0x35, 0x2b, 0x38, 0x1a, 0x8f, 0x61, 0x63, 0x84, 0x7f, - 0xc2, 0x0c, 0x65, 0x21, 0x33, 0xaa, 0x90, 0xb9, 0x4b, 0x5a, 0x84, 0x91, 0x31, 0x43, 0x96, 0x59, - 0x1b, 0xcf, 0x11, 0x60, 0x8b, 0xd8, 0xf5, 0xff, 0x4f, 0x05, 0xbe, 0x06, 0xaa, 0xdb, 0x68, 0x50, - 0xc2, 0x64, 0xfb, 0xcb, 0xd3, 0x60, 0x28, 0x28, 0xc3, 0xa1, 0x60, 0x94, 0x60, 0x63, 0xc4, 0x1a, - 0x19, 0xc9, 0xa1, 0x08, 0x34, 0x2e, 0xa2, 0x6e, 0x33, 0x5b, 0x08, 0x5e, 0xb7, 0x04, 0x6d, 0xfc, - 0x1c, 0x07, 0xf5, 0x11, 0xb3, 0x59, 0x8f, 0xf2, 0xe9, 0x40, 0x99, 0xed, 0xc9, 0xe9, 0x80, 0xe6, - 0x99, 0x0e, 0x12, 0x37, 0x31, 0x62, 0xe2, 0x8b, 0x8d, 0x98, 0x34, 0x28, 0x1e, 0x69, 0x08, 0x57, - 0xd7, 0x2c, 0x4e, 0x86, 0x5c, 0x4a, 0x8c, 0xb8, 0x94, 0x81, 0x24, 0x73, 0x99, 0xdd, 0xca, 0x26, - 0xc5, 0xb5, 0x7f, 0xc0, 0x0f, 0x61, 0x95, 0x7c, 0xd3, 0x25, 0x35, 0x46, 0xea, 0x59, 0x75, 0xe1, - 0x8c, 0x0c, 0x64, 0x18, 0xd7, 0xe1, 0x8a, 0x1f, 0xa3, 0x20, 0xe1, 0xd2, 0x40, 0x34, 0x30, 0x90, - 0xb7, 0x55, 0xc0, 0x32, 0xa8, 0x67, 0x95, 0x8a, 0x1b, 0x19, 0xca, 0x77, 0xa2, 0x2a, 0x5a, 0xe2, - 0x25, 0xca, 0x28, 0xf8, 0x6d, 0xe2, 0xdf, 0x12, 0x1a, 0xdd, 0x57, 0x5f, 0x41, 0x66, 0x14, 0x20, - 0x0d, 0xb9, 0x0f, 0xab, 0x54, 0xde, 0xc9, 0xe6, 0x9a, 0xd1, 0x14, 0xd9, 0x5e, 0x03, 0xb4, 0xf1, - 0x93, 0x02, 0x1b, 0x87, 0x9e, 0x33, 0xd1, 0x62, 0x65, 0x50, 0xed, 0x1a, 0x73, 0xdc, 0x8e, 0x70, - 0xf5, 0x6a, 0xf1, 0x66, 0x94, 0x7c, 0x21, 0xa4, 0x24, 0x20, 0x96, 0x84, 0x06, 0x31, 0x8d, 0x0f, - 0x93, 0x3e, 0x48, 0xae, 0x72, 0x51, 0x72, 0x13, 0x97, 0x4f, 0x6e, 0xa8, 0xb4, 0x92, 0x53, 0xbb, - 0x45, 0x1d, 0x76, 0x0b, 0x3e, 0x1c, 0xec, 0xbe, 0x15, 0x11, 0xc8, 0x8f, 0x66, 0x72, 0x74, 0x34, - 0x5a, 0xcb, 0x5e, 0x85, 0x2f, 0xe3, 0x90, 0x19, 0x55, 0x23, 0xf3, 0xbe, 0x94, 0xac, 0x8c, 0x0e, - 0x85, 0xf8, 0x32, 0x86, 0x82, 0xb2, 0xd8, 0x50, 0x98, 0x6f, 0x04, 0x0c, 0x47, 0xb2, 0x7a, 0xe9, - 0xa9, 0xaf, 0xc3, 0x7a, 0xa9, 0xea, 0x7a, 0xec, 0xc2, 0xee, 0xbf, 0xf1, 0x3d, 0x82, 0x54, 0x28, - 0x7a, 0xf8, 0x2d, 0x48, 0x3c, 0xaa, 0x94, 0x2a, 0xe9, 0x98, 0xb6, 0x71, 0x72, 0xaa, 0xbf, 0x16, - 0xfa, 0xc4, 0x3b, 0x0b, 0x6f, 0x42, 0xf2, 0xd0, 0xda, 0xaf, 0xec, 0xa5, 0x91, 0x96, 0x39, 0x39, - 0xd5, 0xd3, 0xa1, 0xef, 0x82, 0xc4, 0xd7, 0x41, 0x2d, 0x7f, 0x7a, 0x70, 0xb0, 0x5f, 0x49, 0xc7, - 0xb5, 0x37, 0x4e, 0x4e, 0xf5, 0xd7, 0x43, 0x1c, 0x65, 0xb7, 0xdd, 0x76, 0x98, 0xb6, 0xf1, 0xc3, - 0x2f, 0xb9, 0xd8, 0x6f, 0xbf, 0xe6, 0xc2, 0x7a, 0x8b, 0xbf, 0xaf, 0xc0, 0x8a, 0x2c, 0x03, 0x6c, - 0xcb, 0x97, 0xe9, 0xcd, 0x59, 0x36, 0xa9, 0x74, 0x4d, 0x7b, 0x77, 0x36, 0x66, 0x59, 0x61, 0x4d, - 0x50, 0xfd, 0xb7, 0x04, 0xde, 0x8a, 0xc2, 0x8d, 0xbc, 0x80, 0x34, 0x73, 0x56, 0x76, 0xa9, 0xe8, - 0x19, 0x24, 0xf8, 0x68, 0xc3, 0xc5, 0x28, 0xdc, 0xe4, 0x43, 0x44, 0xdb, 0x99, 0x0b, 0xe3, 0x2b, - 0xdc, 0x46, 0xf8, 0x73, 0x50, 0xfd, 0xe7, 0x04, 0xbe, 0x1d, 0x25, 0x60, 0xda, 0xb3, 0x43, 0xbb, - 0x36, 0x51, 0xdf, 0x7b, 0xfc, 0x7f, 0x03, 0x77, 0x85, 0xef, 0xec, 0x68, 0x57, 0x26, 0xdf, 0x19, - 0xd1, 0xae, 0x4c, 0x79, 0x0d, 0x6c, 0x23, 0x9e, 0x26, 0xb9, 0xe2, 0xb7, 0x66, 0xdc, 0x41, 0xb3, - 0xa6, 0x69, 0x6c, 0xe5, 0x1d, 0xc3, 0x7a, 0x78, 0x03, 0xe1, 0x99, 0x42, 0x3f, 0xb6, 0xe0, 0xb4, - 0xdb, 0xf3, 0x81, 0xa4, 0xea, 0x3e, 0x24, 0xfd, 0xd6, 0xd9, 0x59, 0x60, 0x24, 0x47, 0xeb, 0x9c, - 0x36, 0x60, 0xf3, 0x68, 0x1b, 0xe1, 0x03, 0x48, 0x8a, 0xd9, 0x80, 0x23, 0x3b, 0x27, 0x3c, 0x42, - 0x2e, 0xaa, 0x8e, 0xdd, 0x27, 0x67, 0xaf, 0x72, 0xb1, 0x3f, 0x5f, 0xe5, 0x62, 0xdf, 0x9d, 0xe7, - 0xd0, 0xd9, 0x79, 0x0e, 0xfd, 0x71, 0x9e, 0x43, 0x7f, 0x9f, 0xe7, 0xd0, 0x93, 0x8f, 0x17, 0xfd, - 0x1f, 0x7d, 0x47, 0x92, 0x5f, 0xc4, 0xaa, 0xaa, 0xd0, 0xb6, 0xf3, 0x6f, 0x00, 0x00, 0x00, 0xff, - 0xff, 0xc0, 0xc2, 0x35, 0xb1, 0x94, 0x0f, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/content/v1/content.proto b/vendor/github.com/containerd/containerd/api/services/content/v1/content.proto index 086b3e39b1fe7..b33ea5b2e8c4f 100644 --- a/vendor/github.com/containerd/containerd/api/services/content/v1/content.proto +++ b/vendor/github.com/containerd/containerd/api/services/content/v1/content.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.content.v1; diff --git a/vendor/github.com/containerd/containerd/api/services/diff/v1/diff.pb.go b/vendor/github.com/containerd/containerd/api/services/diff/v1/diff.pb.go index 6eba311ffb485..b1450ceb82f64 100644 --- a/vendor/github.com/containerd/containerd/api/services/diff/v1/diff.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/diff/v1/diff.pb.go @@ -1,36 +1,24 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/diff/v1/diff.proto -/* - Package diff is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/diff/v1/diff.proto - - It has these top-level messages: - ApplyRequest - ApplyResponse - DiffRequest - DiffResponse -*/ package diff -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import containerd_types "github.com/containerd/containerd/api/types" -import containerd_types1 "github.com/containerd/containerd/api/types" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + context "context" + fmt "fmt" + types "github.com/containerd/containerd/api/types" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + types1 "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -41,36 +29,99 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type ApplyRequest struct { // Diff is the descriptor of the diff to be extracted - Diff *containerd_types1.Descriptor `protobuf:"bytes,1,opt,name=diff" json:"diff,omitempty"` - Mounts []*containerd_types.Mount `protobuf:"bytes,2,rep,name=mounts" json:"mounts,omitempty"` + Diff *types.Descriptor `protobuf:"bytes,1,opt,name=diff,proto3" json:"diff,omitempty"` + Mounts []*types.Mount `protobuf:"bytes,2,rep,name=mounts,proto3" json:"mounts,omitempty"` + Payloads map[string]*types1.Any `protobuf:"bytes,3,rep,name=payloads,proto3" json:"payloads,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ApplyRequest) Reset() { *m = ApplyRequest{} } +func (*ApplyRequest) ProtoMessage() {} +func (*ApplyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_3b36a99e6faaa935, []int{0} +} +func (m *ApplyRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ApplyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ApplyRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ApplyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ApplyRequest.Merge(m, src) +} +func (m *ApplyRequest) XXX_Size() int { + return m.Size() +} +func (m *ApplyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ApplyRequest.DiscardUnknown(m) } -func (m *ApplyRequest) Reset() { *m = ApplyRequest{} } -func (*ApplyRequest) ProtoMessage() {} -func (*ApplyRequest) Descriptor() ([]byte, []int) { return fileDescriptorDiff, []int{0} } +var xxx_messageInfo_ApplyRequest proto.InternalMessageInfo type ApplyResponse struct { // Applied is the descriptor for the object which was applied. // If the input was a compressed blob then the result will be // the descriptor for the uncompressed blob. - Applied *containerd_types1.Descriptor `protobuf:"bytes,1,opt,name=applied" json:"applied,omitempty"` + Applied *types.Descriptor `protobuf:"bytes,1,opt,name=applied,proto3" json:"applied,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ApplyResponse) Reset() { *m = ApplyResponse{} } -func (*ApplyResponse) ProtoMessage() {} -func (*ApplyResponse) Descriptor() ([]byte, []int) { return fileDescriptorDiff, []int{1} } +func (m *ApplyResponse) Reset() { *m = ApplyResponse{} } +func (*ApplyResponse) ProtoMessage() {} +func (*ApplyResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3b36a99e6faaa935, []int{1} +} +func (m *ApplyResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ApplyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ApplyResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ApplyResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ApplyResponse.Merge(m, src) +} +func (m *ApplyResponse) XXX_Size() int { + return m.Size() +} +func (m *ApplyResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ApplyResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ApplyResponse proto.InternalMessageInfo type DiffRequest struct { // Left are the mounts which represent the older copy // in which is the base of the computed changes. - Left []*containerd_types.Mount `protobuf:"bytes,1,rep,name=left" json:"left,omitempty"` + Left []*types.Mount `protobuf:"bytes,1,rep,name=left,proto3" json:"left,omitempty"` // Right are the mounts which represents the newer copy // in which changes from the left were made into. - Right []*containerd_types.Mount `protobuf:"bytes,2,rep,name=right" json:"right,omitempty"` + Right []*types.Mount `protobuf:"bytes,2,rep,name=right,proto3" json:"right,omitempty"` // MediaType is the media type descriptor for the created diff // object MediaType string `protobuf:"bytes,3,opt,name=media_type,json=mediaType,proto3" json:"media_type,omitempty"` @@ -79,29 +130,134 @@ type DiffRequest struct { Ref string `protobuf:"bytes,4,opt,name=ref,proto3" json:"ref,omitempty"` // Labels are the labels to apply to the generated content // on content store commit. - Labels map[string]string `protobuf:"bytes,5,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,5,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *DiffRequest) Reset() { *m = DiffRequest{} } -func (*DiffRequest) ProtoMessage() {} -func (*DiffRequest) Descriptor() ([]byte, []int) { return fileDescriptorDiff, []int{2} } +func (m *DiffRequest) Reset() { *m = DiffRequest{} } +func (*DiffRequest) ProtoMessage() {} +func (*DiffRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_3b36a99e6faaa935, []int{2} +} +func (m *DiffRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DiffRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DiffRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DiffRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DiffRequest.Merge(m, src) +} +func (m *DiffRequest) XXX_Size() int { + return m.Size() +} +func (m *DiffRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DiffRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DiffRequest proto.InternalMessageInfo type DiffResponse struct { // Diff is the descriptor of the diff which can be applied - Diff *containerd_types1.Descriptor `protobuf:"bytes,3,opt,name=diff" json:"diff,omitempty"` + Diff *types.Descriptor `protobuf:"bytes,3,opt,name=diff,proto3" json:"diff,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *DiffResponse) Reset() { *m = DiffResponse{} } -func (*DiffResponse) ProtoMessage() {} -func (*DiffResponse) Descriptor() ([]byte, []int) { return fileDescriptorDiff, []int{3} } +func (m *DiffResponse) Reset() { *m = DiffResponse{} } +func (*DiffResponse) ProtoMessage() {} +func (*DiffResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_3b36a99e6faaa935, []int{3} +} +func (m *DiffResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DiffResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DiffResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DiffResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DiffResponse.Merge(m, src) +} +func (m *DiffResponse) XXX_Size() int { + return m.Size() +} +func (m *DiffResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DiffResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DiffResponse proto.InternalMessageInfo func init() { proto.RegisterType((*ApplyRequest)(nil), "containerd.services.diff.v1.ApplyRequest") + proto.RegisterMapType((map[string]*types1.Any)(nil), "containerd.services.diff.v1.ApplyRequest.PayloadsEntry") proto.RegisterType((*ApplyResponse)(nil), "containerd.services.diff.v1.ApplyResponse") proto.RegisterType((*DiffRequest)(nil), "containerd.services.diff.v1.DiffRequest") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.diff.v1.DiffRequest.LabelsEntry") proto.RegisterType((*DiffResponse)(nil), "containerd.services.diff.v1.DiffResponse") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/diff/v1/diff.proto", fileDescriptor_3b36a99e6faaa935) +} + +var fileDescriptor_3b36a99e6faaa935 = []byte{ + // 526 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0x41, 0x6f, 0xd3, 0x4c, + 0x10, 0x8d, 0xed, 0x24, 0xdf, 0x97, 0x49, 0x2b, 0xa1, 0x55, 0x24, 0x8c, 0x01, 0xab, 0xca, 0x29, + 0x2d, 0x62, 0x4d, 0x03, 0x2a, 0xd0, 0x5e, 0x5a, 0x54, 0xc4, 0xa5, 0x48, 0x60, 0x7a, 0x40, 0x20, + 0x81, 0x9c, 0x78, 0xed, 0xae, 0x70, 0xbc, 0x8b, 0x77, 0x1d, 0xc9, 0x37, 0xfe, 0x06, 0x67, 0x7e, + 0x0a, 0x97, 0x1e, 0x39, 0x72, 0xa4, 0xf9, 0x25, 0xc8, 0xeb, 0x75, 0x31, 0x02, 0x05, 0xc3, 0xc9, + 0x9b, 0x9d, 0xf7, 0xde, 0xce, 0xbc, 0x37, 0x0a, 0x1c, 0xc6, 0x54, 0x9e, 0xe5, 0x33, 0x3c, 0x67, + 0x0b, 0x6f, 0xce, 0x52, 0x19, 0xd0, 0x94, 0x64, 0x61, 0xf3, 0x18, 0x70, 0xea, 0x09, 0x92, 0x2d, + 0xe9, 0x9c, 0x08, 0x2f, 0xa4, 0x51, 0xe4, 0x2d, 0x77, 0xd5, 0x17, 0xf3, 0x8c, 0x49, 0x86, 0xae, + 0xff, 0xc0, 0xe2, 0x1a, 0x87, 0x55, 0x7d, 0xb9, 0xeb, 0x8c, 0x62, 0x16, 0x33, 0x85, 0xf3, 0xca, + 0x53, 0x45, 0x71, 0xae, 0xc5, 0x8c, 0xc5, 0x09, 0xf1, 0xd4, 0xaf, 0x59, 0x1e, 0x79, 0x41, 0x5a, + 0xe8, 0xd2, 0x5e, 0xab, 0x7e, 0x64, 0xc1, 0x89, 0xf0, 0x16, 0x2c, 0x4f, 0xa5, 0xe6, 0x1d, 0xfc, + 0x05, 0x2f, 0x24, 0x62, 0x9e, 0x51, 0x2e, 0x59, 0x56, 0x91, 0xc7, 0x1f, 0x4d, 0xd8, 0x38, 0xe2, + 0x3c, 0x29, 0x7c, 0xf2, 0x3e, 0x27, 0x42, 0xa2, 0x3b, 0xd0, 0x2d, 0x27, 0xb0, 0x8d, 0x2d, 0x63, + 0x32, 0x9c, 0xde, 0xc0, 0x8d, 0x11, 0x95, 0x04, 0x3e, 0xbe, 0x94, 0xf0, 0x15, 0x12, 0x79, 0xd0, + 0x57, 0xed, 0x08, 0xdb, 0xdc, 0xb2, 0x26, 0xc3, 0xe9, 0xd5, 0x5f, 0x39, 0x4f, 0xcb, 0xba, 0xaf, + 0x61, 0xe8, 0x05, 0xfc, 0xcf, 0x83, 0x22, 0x61, 0x41, 0x28, 0x6c, 0x4b, 0x51, 0xee, 0xe3, 0x35, + 0x4e, 0xe2, 0x66, 0x7f, 0xf8, 0x99, 0x66, 0x3e, 0x4e, 0x65, 0x56, 0xf8, 0x97, 0x42, 0xce, 0x73, + 0xd8, 0xfc, 0xa9, 0x84, 0xae, 0x80, 0xf5, 0x8e, 0x14, 0x6a, 0x8e, 0x81, 0x5f, 0x1e, 0xd1, 0x0e, + 0xf4, 0x96, 0x41, 0x92, 0x13, 0xdb, 0x54, 0xb3, 0x8d, 0x70, 0x95, 0x05, 0xae, 0xb3, 0xc0, 0x47, + 0x69, 0xe1, 0x57, 0x90, 0x7d, 0xf3, 0x81, 0x31, 0x7e, 0x02, 0x9b, 0xfa, 0x69, 0xc1, 0x59, 0x2a, + 0x08, 0xda, 0x83, 0xff, 0x02, 0xce, 0x13, 0x4a, 0xc2, 0x56, 0xf6, 0xd4, 0xe0, 0xf1, 0x27, 0x13, + 0x86, 0xc7, 0x34, 0x8a, 0x6a, 0x8f, 0x6f, 0x41, 0x37, 0x21, 0x91, 0xb4, 0x8d, 0xf5, 0x7e, 0x29, + 0x10, 0xba, 0x0d, 0xbd, 0x8c, 0xc6, 0x67, 0xf2, 0x4f, 0xee, 0x56, 0x28, 0x74, 0x13, 0x60, 0x41, + 0x42, 0x1a, 0xbc, 0x2d, 0x6b, 0xb6, 0xa5, 0xa6, 0x1f, 0xa8, 0x9b, 0xd3, 0x82, 0x93, 0xd2, 0x95, + 0x8c, 0x44, 0x76, 0xb7, 0x72, 0x25, 0x23, 0x11, 0x3a, 0x81, 0x7e, 0x12, 0xcc, 0x48, 0x22, 0xec, + 0x9e, 0x7a, 0xe0, 0xde, 0xda, 0x2c, 0x1a, 0x63, 0xe0, 0x13, 0x45, 0xab, 0x82, 0xd0, 0x1a, 0xce, + 0x43, 0x18, 0x36, 0xae, 0x7f, 0x13, 0xc2, 0xa8, 0x19, 0xc2, 0xa0, 0x69, 0xf7, 0x21, 0x6c, 0x54, + 0xea, 0xda, 0xed, 0x7a, 0x13, 0xad, 0xb6, 0x9b, 0x38, 0xfd, 0x6c, 0x40, 0xb7, 0x94, 0x40, 0x6f, + 0xa0, 0xa7, 0x92, 0x43, 0xdb, 0xad, 0x17, 0xcb, 0xd9, 0x69, 0x03, 0xd5, 0xad, 0xbd, 0xd6, 0xef, + 0x4c, 0xda, 0x7a, 0xe5, 0x6c, 0xb7, 0x40, 0x56, 0xe2, 0x8f, 0x4e, 0xcf, 0x2f, 0xdc, 0xce, 0xd7, + 0x0b, 0xb7, 0xf3, 0x61, 0xe5, 0x1a, 0xe7, 0x2b, 0xd7, 0xf8, 0xb2, 0x72, 0x8d, 0x6f, 0x2b, 0xd7, + 0x78, 0xb5, 0xff, 0x4f, 0xff, 0x58, 0x07, 0xe5, 0xf7, 0x65, 0x67, 0xd6, 0x57, 0x7b, 0x7e, 0xf7, + 0x7b, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf7, 0x85, 0x25, 0xb8, 0xf8, 0x04, 0x00, 0x00, +} + // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn @@ -110,8 +266,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Diff service - +// DiffClient is the client API for Diff service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type DiffClient interface { // Apply applies the content associated with the provided digests onto // the provided mounts. Archive content will be extracted and @@ -132,7 +289,7 @@ func NewDiffClient(cc *grpc.ClientConn) DiffClient { func (c *diffClient) Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.CallOption) (*ApplyResponse, error) { out := new(ApplyResponse) - err := grpc.Invoke(ctx, "/containerd.services.diff.v1.Diff/Apply", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.diff.v1.Diff/Apply", in, out, opts...) if err != nil { return nil, err } @@ -141,15 +298,14 @@ func (c *diffClient) Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.C func (c *diffClient) Diff(ctx context.Context, in *DiffRequest, opts ...grpc.CallOption) (*DiffResponse, error) { out := new(DiffResponse) - err := grpc.Invoke(ctx, "/containerd.services.diff.v1.Diff/Diff", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.diff.v1.Diff/Diff", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Diff service - +// DiffServer is the server API for Diff service. type DiffServer interface { // Apply applies the content associated with the provided digests onto // the provided mounts. Archive content will be extracted and @@ -160,6 +316,17 @@ type DiffServer interface { Diff(context.Context, *DiffRequest) (*DiffResponse, error) } +// UnimplementedDiffServer can be embedded to have forward compatible implementations. +type UnimplementedDiffServer struct { +} + +func (*UnimplementedDiffServer) Apply(ctx context.Context, req *ApplyRequest) (*ApplyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Apply not implemented") +} +func (*UnimplementedDiffServer) Diff(ctx context.Context, req *DiffRequest) (*DiffResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Diff not implemented") +} + func RegisterDiffServer(s *grpc.Server, srv DiffServer) { s.RegisterService(&_Diff_serviceDesc, srv) } @@ -220,7 +387,7 @@ var _Diff_serviceDesc = grpc.ServiceDesc{ func (m *ApplyRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -228,39 +395,78 @@ func (m *ApplyRequest) Marshal() (dAtA []byte, err error) { } func (m *ApplyRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ApplyRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Diff != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintDiff(dAtA, i, uint64(m.Diff.Size())) - n1, err := m.Diff.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Payloads) > 0 { + for k := range m.Payloads { + v := m.Payloads[k] + baseI := i + if v != nil { + { + size, err := v.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDiff(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintDiff(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintDiff(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a } - i += n1 } if len(m.Mounts) > 0 { - for _, msg := range m.Mounts { + for iNdEx := len(m.Mounts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Mounts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDiff(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintDiff(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + } + } + if m.Diff != nil { + { + size, err := m.Diff.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintDiff(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ApplyResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -268,27 +474,38 @@ func (m *ApplyResponse) Marshal() (dAtA []byte, err error) { } func (m *ApplyResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ApplyResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Applied != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintDiff(dAtA, i, uint64(m.Applied.Size())) - n2, err := m.Applied.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Applied.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDiff(dAtA, i, uint64(size)) } - i += n2 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *DiffRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -296,70 +513,87 @@ func (m *DiffRequest) Marshal() (dAtA []byte, err error) { } func (m *DiffRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DiffRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Left) > 0 { - for _, msg := range m.Left { - dAtA[i] = 0xa - i++ - i = encodeVarintDiff(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Right) > 0 { - for _, msg := range m.Right { + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintDiff(dAtA, i, uint64(len(v))) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintDiff(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintDiff(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintDiff(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a } } + if len(m.Ref) > 0 { + i -= len(m.Ref) + copy(dAtA[i:], m.Ref) + i = encodeVarintDiff(dAtA, i, uint64(len(m.Ref))) + i-- + dAtA[i] = 0x22 + } if len(m.MediaType) > 0 { - dAtA[i] = 0x1a - i++ + i -= len(m.MediaType) + copy(dAtA[i:], m.MediaType) i = encodeVarintDiff(dAtA, i, uint64(len(m.MediaType))) - i += copy(dAtA[i:], m.MediaType) + i-- + dAtA[i] = 0x1a } - if len(m.Ref) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintDiff(dAtA, i, uint64(len(m.Ref))) - i += copy(dAtA[i:], m.Ref) + if len(m.Right) > 0 { + for iNdEx := len(m.Right) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Right[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDiff(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x2a - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovDiff(uint64(len(k))) + 1 + len(v) + sovDiff(uint64(len(v))) - i = encodeVarintDiff(dAtA, i, uint64(mapSize)) + if len(m.Left) > 0 { + for iNdEx := len(m.Left) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Left[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDiff(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0xa - i++ - i = encodeVarintDiff(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintDiff(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - return i, nil + return len(dAtA) - i, nil } func (m *DiffResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -367,33 +601,49 @@ func (m *DiffResponse) Marshal() (dAtA []byte, err error) { } func (m *DiffResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DiffResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Diff != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintDiff(dAtA, i, uint64(m.Diff.Size())) - n3, err := m.Diff.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Diff.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDiff(dAtA, i, uint64(size)) } - i += n3 + i-- + dAtA[i] = 0x1a } - return i, nil + return len(dAtA) - i, nil } func encodeVarintDiff(dAtA []byte, offset int, v uint64) int { + offset -= sovDiff(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *ApplyRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Diff != nil { @@ -406,20 +656,45 @@ func (m *ApplyRequest) Size() (n int) { n += 1 + l + sovDiff(uint64(l)) } } + if len(m.Payloads) > 0 { + for k, v := range m.Payloads { + _ = k + _ = v + l = 0 + if v != nil { + l = v.Size() + l += 1 + sovDiff(uint64(l)) + } + mapEntrySize := 1 + len(k) + sovDiff(uint64(len(k))) + l + n += mapEntrySize + 1 + sovDiff(uint64(mapEntrySize)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ApplyResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Applied != nil { l = m.Applied.Size() n += 1 + l + sovDiff(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DiffRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Left) > 0 { @@ -450,28 +725,30 @@ func (m *DiffRequest) Size() (n int) { n += mapEntrySize + 1 + sovDiff(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DiffResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Diff != nil { l = m.Diff.Size() n += 1 + l + sovDiff(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovDiff(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozDiff(x uint64) (n int) { return sovDiff(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -480,9 +757,26 @@ func (this *ApplyRequest) String() string { if this == nil { return "nil" } + repeatedStringForMounts := "[]*Mount{" + for _, f := range this.Mounts { + repeatedStringForMounts += strings.Replace(fmt.Sprintf("%v", f), "Mount", "types.Mount", 1) + "," + } + repeatedStringForMounts += "}" + keysForPayloads := make([]string, 0, len(this.Payloads)) + for k, _ := range this.Payloads { + keysForPayloads = append(keysForPayloads, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForPayloads) + mapStringForPayloads := "map[string]*types1.Any{" + for _, k := range keysForPayloads { + mapStringForPayloads += fmt.Sprintf("%v: %v,", k, this.Payloads[k]) + } + mapStringForPayloads += "}" s := strings.Join([]string{`&ApplyRequest{`, - `Diff:` + strings.Replace(fmt.Sprintf("%v", this.Diff), "Descriptor", "containerd_types1.Descriptor", 1) + `,`, - `Mounts:` + strings.Replace(fmt.Sprintf("%v", this.Mounts), "Mount", "containerd_types.Mount", 1) + `,`, + `Diff:` + strings.Replace(fmt.Sprintf("%v", this.Diff), "Descriptor", "types.Descriptor", 1) + `,`, + `Mounts:` + repeatedStringForMounts + `,`, + `Payloads:` + mapStringForPayloads + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -492,7 +786,8 @@ func (this *ApplyResponse) String() string { return "nil" } s := strings.Join([]string{`&ApplyResponse{`, - `Applied:` + strings.Replace(fmt.Sprintf("%v", this.Applied), "Descriptor", "containerd_types1.Descriptor", 1) + `,`, + `Applied:` + strings.Replace(fmt.Sprintf("%v", this.Applied), "Descriptor", "types.Descriptor", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -501,22 +796,33 @@ func (this *DiffRequest) String() string { if this == nil { return "nil" } + repeatedStringForLeft := "[]*Mount{" + for _, f := range this.Left { + repeatedStringForLeft += strings.Replace(fmt.Sprintf("%v", f), "Mount", "types.Mount", 1) + "," + } + repeatedStringForLeft += "}" + repeatedStringForRight := "[]*Mount{" + for _, f := range this.Right { + repeatedStringForRight += strings.Replace(fmt.Sprintf("%v", f), "Mount", "types.Mount", 1) + "," + } + repeatedStringForRight += "}" keysForLabels := make([]string, 0, len(this.Labels)) for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) } mapStringForLabels += "}" s := strings.Join([]string{`&DiffRequest{`, - `Left:` + strings.Replace(fmt.Sprintf("%v", this.Left), "Mount", "containerd_types.Mount", 1) + `,`, - `Right:` + strings.Replace(fmt.Sprintf("%v", this.Right), "Mount", "containerd_types.Mount", 1) + `,`, + `Left:` + repeatedStringForLeft + `,`, + `Right:` + repeatedStringForRight + `,`, `MediaType:` + fmt.Sprintf("%v", this.MediaType) + `,`, `Ref:` + fmt.Sprintf("%v", this.Ref) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -526,7 +832,8 @@ func (this *DiffResponse) String() string { return "nil" } s := strings.Join([]string{`&DiffResponse{`, - `Diff:` + strings.Replace(fmt.Sprintf("%v", this.Diff), "Descriptor", "containerd_types1.Descriptor", 1) + `,`, + `Diff:` + strings.Replace(fmt.Sprintf("%v", this.Diff), "Descriptor", "types.Descriptor", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -554,7 +861,7 @@ func (m *ApplyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -582,7 +889,7 @@ func (m *ApplyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -591,11 +898,14 @@ func (m *ApplyRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDiff + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Diff == nil { - m.Diff = &containerd_types1.Descriptor{} + m.Diff = &types.Descriptor{} } if err := m.Diff.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -615,7 +925,7 @@ func (m *ApplyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -624,26 +934,159 @@ func (m *ApplyRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDiff + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Mounts = append(m.Mounts, &containerd_types.Mount{}) + m.Mounts = append(m.Mounts, &types.Mount{}) if err := m.Mounts[len(m.Mounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Payloads", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDiff + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDiff + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Payloads == nil { + m.Payloads = make(map[string]*types1.Any) + } + var mapkey string + var mapvalue *types1.Any + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthDiff + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthDiff + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDiff + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthDiff + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthDiff + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &types1.Any{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipDiff(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDiff + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Payloads[mapkey] = mapvalue + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipDiff(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDiff } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -668,7 +1111,7 @@ func (m *ApplyResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -696,7 +1139,7 @@ func (m *ApplyResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -705,11 +1148,14 @@ func (m *ApplyResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDiff + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Applied == nil { - m.Applied = &containerd_types1.Descriptor{} + m.Applied = &types.Descriptor{} } if err := m.Applied.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -721,12 +1167,13 @@ func (m *ApplyResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDiff } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -751,7 +1198,7 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -779,7 +1226,7 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -788,10 +1235,13 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDiff + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Left = append(m.Left, &containerd_types.Mount{}) + m.Left = append(m.Left, &types.Mount{}) if err := m.Left[len(m.Left)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -810,7 +1260,7 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -819,10 +1269,13 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDiff + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Right = append(m.Right, &containerd_types.Mount{}) + m.Right = append(m.Right, &types.Mount{}) if err := m.Right[len(m.Right)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -841,7 +1294,7 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -851,6 +1304,9 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDiff + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -870,7 +1326,7 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -880,6 +1336,9 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDiff + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -899,7 +1358,7 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -908,6 +1367,9 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDiff + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -928,7 +1390,7 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -945,7 +1407,7 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -955,6 +1417,9 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthDiff + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -971,7 +1436,7 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -981,6 +1446,9 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthDiff + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -992,7 +1460,7 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDiff } if (iNdEx + skippy) > postIndex { @@ -1009,12 +1477,13 @@ func (m *DiffRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDiff } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1039,7 +1508,7 @@ func (m *DiffResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1067,7 +1536,7 @@ func (m *DiffResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1076,11 +1545,14 @@ func (m *DiffResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDiff } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDiff + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Diff == nil { - m.Diff = &containerd_types1.Descriptor{} + m.Diff = &types.Descriptor{} } if err := m.Diff.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -1092,12 +1564,13 @@ func (m *DiffResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDiff } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1110,6 +1583,7 @@ func (m *DiffResponse) Unmarshal(dAtA []byte) error { func skipDiff(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1141,10 +1615,8 @@ func skipDiff(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1161,90 +1633,34 @@ func skipDiff(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthDiff } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowDiff - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipDiff(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupDiff + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthDiff + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthDiff = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowDiff = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthDiff = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDiff = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupDiff = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/diff/v1/diff.proto", fileDescriptorDiff) -} - -var fileDescriptorDiff = []byte{ - // 457 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0x4f, 0x6f, 0xd3, 0x30, - 0x14, 0xaf, 0xfb, 0x0f, 0xf5, 0x75, 0x48, 0xc8, 0x9a, 0x44, 0x14, 0x20, 0xaa, 0x7a, 0xea, 0x40, - 0x38, 0xac, 0xa0, 0x09, 0xb6, 0xcb, 0x40, 0x43, 0x5c, 0xc6, 0x25, 0xda, 0x01, 0x81, 0x04, 0x4a, - 0x9b, 0x97, 0xce, 0x22, 0x8d, 0xbd, 0xd8, 0xad, 0x94, 0x1b, 0xdf, 0x85, 0x8f, 0xc2, 0x65, 0x47, - 0x8e, 0x1c, 0x69, 0x3f, 0x09, 0xb2, 0x93, 0x40, 0x24, 0xa4, 0x12, 0x76, 0xca, 0xcb, 0xf3, 0xef, - 0x9f, 0xfd, 0x6c, 0x38, 0x5d, 0x70, 0x7d, 0xb9, 0x9a, 0xb1, 0xb9, 0x58, 0xfa, 0x73, 0x91, 0xea, - 0x90, 0xa7, 0x98, 0x45, 0xf5, 0x32, 0x94, 0xdc, 0x57, 0x98, 0xad, 0xf9, 0x1c, 0x95, 0x1f, 0xf1, - 0x38, 0xf6, 0xd7, 0x87, 0xf6, 0xcb, 0x64, 0x26, 0xb4, 0xa0, 0xf7, 0xfe, 0x60, 0x59, 0x85, 0x63, - 0x76, 0x7d, 0x7d, 0xe8, 0xee, 0x2f, 0xc4, 0x42, 0x58, 0x9c, 0x6f, 0xaa, 0x82, 0xe2, 0x1e, 0x35, - 0x32, 0xd5, 0xb9, 0x44, 0xe5, 0x2f, 0xc5, 0x2a, 0xd5, 0x25, 0xef, 0xe4, 0x3f, 0x78, 0x11, 0xaa, - 0x79, 0xc6, 0xa5, 0x16, 0x59, 0x41, 0x1e, 0x5f, 0xc1, 0xde, 0x4b, 0x29, 0x93, 0x3c, 0xc0, 0xab, - 0x15, 0x2a, 0x4d, 0x9f, 0x40, 0xd7, 0xa4, 0x74, 0xc8, 0x88, 0x4c, 0x86, 0xd3, 0xfb, 0xac, 0xb6, - 0x0d, 0xab, 0xc0, 0xce, 0x7e, 0x2b, 0x04, 0x16, 0x49, 0x7d, 0xe8, 0xdb, 0x34, 0xca, 0x69, 0x8f, - 0x3a, 0x93, 0xe1, 0xf4, 0xee, 0xdf, 0x9c, 0xb7, 0x66, 0x3d, 0x28, 0x61, 0xe3, 0x37, 0x70, 0xbb, - 0xb4, 0x54, 0x52, 0xa4, 0x0a, 0xe9, 0x11, 0xdc, 0x0a, 0xa5, 0x4c, 0x38, 0x46, 0x8d, 0x6c, 0x2b, - 0xf0, 0xf8, 0x6b, 0x1b, 0x86, 0x67, 0x3c, 0x8e, 0xab, 0xec, 0x8f, 0xa0, 0x9b, 0x60, 0xac, 0x1d, - 0xb2, 0x3b, 0x87, 0x05, 0xd1, 0xc7, 0xd0, 0xcb, 0xf8, 0xe2, 0x52, 0xff, 0x2b, 0x75, 0x81, 0xa2, - 0x0f, 0x00, 0x96, 0x18, 0xf1, 0xf0, 0x93, 0x59, 0x73, 0x3a, 0x23, 0x32, 0x19, 0x04, 0x03, 0xdb, - 0xb9, 0xc8, 0x25, 0xd2, 0x3b, 0xd0, 0xc9, 0x30, 0x76, 0xba, 0xb6, 0x6f, 0x4a, 0x7a, 0x0e, 0xfd, - 0x24, 0x9c, 0x61, 0xa2, 0x9c, 0x9e, 0x35, 0x78, 0xc6, 0x76, 0xdc, 0x08, 0x56, 0xdb, 0x06, 0x3b, - 0xb7, 0xb4, 0xd7, 0xa9, 0xce, 0xf2, 0xa0, 0xd4, 0x70, 0x5f, 0xc0, 0xb0, 0xd6, 0x36, 0x76, 0x9f, - 0x31, 0xb7, 0xa7, 0x35, 0x08, 0x4c, 0x49, 0xf7, 0xa1, 0xb7, 0x0e, 0x93, 0x15, 0x3a, 0x6d, 0xdb, - 0x2b, 0x7e, 0x8e, 0xdb, 0xcf, 0xc9, 0xf8, 0x14, 0xf6, 0x0a, 0xf5, 0xf2, 0xb4, 0xab, 0x09, 0x77, - 0x9a, 0x4e, 0x78, 0xfa, 0x8d, 0x40, 0xd7, 0x48, 0xd0, 0x8f, 0xd0, 0xb3, 0x93, 0xa3, 0x07, 0x3b, - 0x37, 0x53, 0xbf, 0x50, 0xee, 0xc3, 0x26, 0xd0, 0x32, 0xda, 0x87, 0xd2, 0x67, 0xd2, 0xf4, 0xac, - 0xdc, 0x83, 0x06, 0xc8, 0x42, 0xfc, 0xd5, 0xc5, 0xf5, 0xc6, 0x6b, 0xfd, 0xd8, 0x78, 0xad, 0x2f, - 0x5b, 0x8f, 0x5c, 0x6f, 0x3d, 0xf2, 0x7d, 0xeb, 0x91, 0x9f, 0x5b, 0x8f, 0xbc, 0x3f, 0xbe, 0xd1, - 0x6b, 0x3f, 0x31, 0xdf, 0x77, 0xad, 0x59, 0xdf, 0x3e, 0xa4, 0xa7, 0xbf, 0x02, 0x00, 0x00, 0xff, - 0xff, 0x61, 0xd1, 0x6e, 0x9e, 0x34, 0x04, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/diff/v1/diff.proto b/vendor/github.com/containerd/containerd/api/services/diff/v1/diff.proto index 66d7ecb19f6b3..cc24e3f2caa02 100644 --- a/vendor/github.com/containerd/containerd/api/services/diff/v1/diff.proto +++ b/vendor/github.com/containerd/containerd/api/services/diff/v1/diff.proto @@ -1,8 +1,25 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.diff.v1; import weak "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; import "github.com/containerd/containerd/api/types/mount.proto"; import "github.com/containerd/containerd/api/types/descriptor.proto"; @@ -25,6 +42,8 @@ message ApplyRequest { containerd.types.Descriptor diff = 1; repeated containerd.types.Mount mounts = 2; + + map payloads = 3; } message ApplyResponse { diff --git a/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go b/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go index d6a7b38a87419..4373f3bf2fc6d 100644 --- a/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go @@ -1,43 +1,25 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/events/v1/events.proto -/* - Package events is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/events/v1/events.proto - - It has these top-level messages: - PublishRequest - ForwardRequest - SubscribeRequest - Envelope -*/ package events -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import containerd_plugin "github.com/containerd/containerd/protobuf/plugin" -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import google_protobuf1 "github.com/gogo/protobuf/types" -import google_protobuf2 "github.com/gogo/protobuf/types" -import _ "github.com/gogo/protobuf/types" - -import time "time" - -import typeurl "github.com/containerd/typeurl" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + context "context" + fmt "fmt" + github_com_containerd_typeurl "github.com/containerd/typeurl" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -49,43 +31,167 @@ var _ = time.Kitchen // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type PublishRequest struct { - Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"` - Event *google_protobuf1.Any `protobuf:"bytes,2,opt,name=event" json:"event,omitempty"` + Topic string `protobuf:"bytes,1,opt,name=topic,proto3" json:"topic,omitempty"` + Event *types.Any `protobuf:"bytes,2,opt,name=event,proto3" json:"event,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PublishRequest) Reset() { *m = PublishRequest{} } +func (*PublishRequest) ProtoMessage() {} +func (*PublishRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_43fcd20dc1642376, []int{0} +} +func (m *PublishRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PublishRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PublishRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PublishRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PublishRequest.Merge(m, src) +} +func (m *PublishRequest) XXX_Size() int { + return m.Size() +} +func (m *PublishRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PublishRequest.DiscardUnknown(m) } -func (m *PublishRequest) Reset() { *m = PublishRequest{} } -func (*PublishRequest) ProtoMessage() {} -func (*PublishRequest) Descriptor() ([]byte, []int) { return fileDescriptorEvents, []int{0} } +var xxx_messageInfo_PublishRequest proto.InternalMessageInfo type ForwardRequest struct { - Envelope *Envelope `protobuf:"bytes,1,opt,name=envelope" json:"envelope,omitempty"` + Envelope *Envelope `protobuf:"bytes,1,opt,name=envelope,proto3" json:"envelope,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ForwardRequest) Reset() { *m = ForwardRequest{} } -func (*ForwardRequest) ProtoMessage() {} -func (*ForwardRequest) Descriptor() ([]byte, []int) { return fileDescriptorEvents, []int{1} } +func (m *ForwardRequest) Reset() { *m = ForwardRequest{} } +func (*ForwardRequest) ProtoMessage() {} +func (*ForwardRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_43fcd20dc1642376, []int{1} +} +func (m *ForwardRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ForwardRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ForwardRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ForwardRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ForwardRequest.Merge(m, src) +} +func (m *ForwardRequest) XXX_Size() int { + return m.Size() +} +func (m *ForwardRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ForwardRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ForwardRequest proto.InternalMessageInfo type SubscribeRequest struct { - Filters []string `protobuf:"bytes,1,rep,name=filters" json:"filters,omitempty"` + Filters []string `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} } -func (*SubscribeRequest) ProtoMessage() {} -func (*SubscribeRequest) Descriptor() ([]byte, []int) { return fileDescriptorEvents, []int{2} } +func (m *SubscribeRequest) Reset() { *m = SubscribeRequest{} } +func (*SubscribeRequest) ProtoMessage() {} +func (*SubscribeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_43fcd20dc1642376, []int{2} +} +func (m *SubscribeRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubscribeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubscribeRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SubscribeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubscribeRequest.Merge(m, src) +} +func (m *SubscribeRequest) XXX_Size() int { + return m.Size() +} +func (m *SubscribeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SubscribeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SubscribeRequest proto.InternalMessageInfo type Envelope struct { - Timestamp time.Time `protobuf:"bytes,1,opt,name=timestamp,stdtime" json:"timestamp"` - Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` - Topic string `protobuf:"bytes,3,opt,name=topic,proto3" json:"topic,omitempty"` - Event *google_protobuf1.Any `protobuf:"bytes,4,opt,name=event" json:"event,omitempty"` + Timestamp time.Time `protobuf:"bytes,1,opt,name=timestamp,proto3,stdtime" json:"timestamp"` + Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` + Topic string `protobuf:"bytes,3,opt,name=topic,proto3" json:"topic,omitempty"` + Event *types.Any `protobuf:"bytes,4,opt,name=event,proto3" json:"event,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *Envelope) Reset() { *m = Envelope{} } -func (*Envelope) ProtoMessage() {} -func (*Envelope) Descriptor() ([]byte, []int) { return fileDescriptorEvents, []int{3} } +func (m *Envelope) Reset() { *m = Envelope{} } +func (*Envelope) ProtoMessage() {} +func (*Envelope) Descriptor() ([]byte, []int) { + return fileDescriptor_43fcd20dc1642376, []int{3} +} +func (m *Envelope) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Envelope) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Envelope.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Envelope) XXX_Merge(src proto.Message) { + xxx_messageInfo_Envelope.Merge(m, src) +} +func (m *Envelope) XXX_Size() int { + return m.Size() +} +func (m *Envelope) XXX_DiscardUnknown() { + xxx_messageInfo_Envelope.DiscardUnknown(m) +} + +var xxx_messageInfo_Envelope proto.InternalMessageInfo func init() { proto.RegisterType((*PublishRequest)(nil), "containerd.services.events.v1.PublishRequest") @@ -94,6 +200,44 @@ func init() { proto.RegisterType((*Envelope)(nil), "containerd.services.events.v1.Envelope") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/events/v1/events.proto", fileDescriptor_43fcd20dc1642376) +} + +var fileDescriptor_43fcd20dc1642376 = []byte{ + // 466 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0xcd, 0x8e, 0xd3, 0x30, + 0x14, 0x85, 0xeb, 0xf9, 0x6d, 0x3c, 0xd2, 0x08, 0x45, 0x15, 0x2a, 0x01, 0xd2, 0xaa, 0x1b, 0x2a, + 0x04, 0x0e, 0x53, 0x76, 0x20, 0x21, 0x28, 0x94, 0xf5, 0x28, 0x80, 0x54, 0xb1, 0x4b, 0xd2, 0xdb, + 0xd4, 0x52, 0x62, 0x9b, 0xd8, 0x09, 0x9a, 0xdd, 0x3c, 0x02, 0x1b, 0xde, 0x84, 0x0d, 0x6f, 0xd0, + 0x25, 0x4b, 0x56, 0xc0, 0xf4, 0x49, 0x50, 0x13, 0xbb, 0x61, 0x3a, 0x40, 0x10, 0xbb, 0x6b, 0xdf, + 0xe3, 0xcf, 0xb9, 0xe7, 0x38, 0xf8, 0x45, 0x4c, 0xd5, 0x22, 0x0f, 0x49, 0xc4, 0x53, 0x2f, 0xe2, + 0x4c, 0x05, 0x94, 0x41, 0x36, 0xfb, 0xb5, 0x0c, 0x04, 0xf5, 0x24, 0x64, 0x05, 0x8d, 0x40, 0x7a, + 0x50, 0x00, 0x53, 0xd2, 0x2b, 0x4e, 0x74, 0x45, 0x44, 0xc6, 0x15, 0xb7, 0x6f, 0xd7, 0x7a, 0x62, + 0xb4, 0x44, 0x2b, 0x8a, 0x13, 0xe7, 0x69, 0xe3, 0x25, 0x25, 0x26, 0xcc, 0xe7, 0x9e, 0x48, 0xf2, + 0x98, 0x32, 0x6f, 0x4e, 0x21, 0x99, 0x89, 0x40, 0x2d, 0xaa, 0x0b, 0x9c, 0x4e, 0xcc, 0x63, 0x5e, + 0x96, 0xde, 0xba, 0xd2, 0xbb, 0x37, 0x62, 0xce, 0xe3, 0x04, 0xea, 0xd3, 0x01, 0x3b, 0xd3, 0xad, + 0x9b, 0xdb, 0x2d, 0x48, 0x85, 0x32, 0xcd, 0xde, 0x76, 0x53, 0xd1, 0x14, 0xa4, 0x0a, 0x52, 0x51, + 0x09, 0x06, 0x3e, 0x3e, 0x3e, 0xcd, 0xc3, 0x84, 0xca, 0x85, 0x0f, 0xef, 0x72, 0x90, 0xca, 0xee, + 0xe0, 0x7d, 0xc5, 0x05, 0x8d, 0xba, 0xa8, 0x8f, 0x86, 0x96, 0x5f, 0x2d, 0xec, 0xbb, 0x78, 0xbf, + 0x9c, 0xb2, 0xbb, 0xd3, 0x47, 0xc3, 0xa3, 0x51, 0x87, 0x54, 0x60, 0x62, 0xc0, 0xe4, 0x19, 0x3b, + 0xf3, 0x2b, 0xc9, 0xe0, 0x0d, 0x3e, 0x7e, 0xc9, 0xb3, 0xf7, 0x41, 0x36, 0x33, 0xcc, 0xe7, 0xb8, + 0x0d, 0xac, 0x80, 0x84, 0x0b, 0x28, 0xb1, 0x47, 0xa3, 0x3b, 0xe4, 0xaf, 0x46, 0x92, 0x89, 0x96, + 0xfb, 0x9b, 0x83, 0x83, 0x7b, 0xf8, 0xda, 0xab, 0x3c, 0x94, 0x51, 0x46, 0x43, 0x30, 0xe0, 0x2e, + 0x3e, 0x9c, 0xd3, 0x44, 0x41, 0x26, 0xbb, 0xa8, 0xbf, 0x3b, 0xb4, 0x7c, 0xb3, 0x1c, 0x7c, 0x42, + 0xb8, 0x6d, 0x20, 0xf6, 0x18, 0x5b, 0x9b, 0xc1, 0xf5, 0x07, 0x38, 0x57, 0x26, 0x78, 0x6d, 0x14, + 0xe3, 0xf6, 0xf2, 0x5b, 0xaf, 0xf5, 0xe1, 0x7b, 0x0f, 0xf9, 0xf5, 0x31, 0xfb, 0x16, 0xb6, 0x58, + 0x90, 0x82, 0x14, 0x41, 0x04, 0xa5, 0x0b, 0x96, 0x5f, 0x6f, 0xd4, 0xae, 0xed, 0xfe, 0xd6, 0xb5, + 0xbd, 0x46, 0xd7, 0x1e, 0xed, 0x9d, 0x7f, 0xee, 0xa1, 0xd1, 0xc7, 0x1d, 0x7c, 0x30, 0x29, 0x5d, + 0xb0, 0x4f, 0xf1, 0xa1, 0x8e, 0xc6, 0xbe, 0xdf, 0xe0, 0xd6, 0xe5, 0x08, 0x9d, 0xeb, 0x57, 0xee, + 0x99, 0xac, 0xdf, 0xc4, 0x9a, 0xa8, 0x83, 0x69, 0x24, 0x5e, 0x0e, 0xf0, 0x8f, 0xc4, 0x18, 0x5b, + 0x9b, 0x4c, 0x6c, 0xaf, 0x81, 0xb9, 0x9d, 0x9e, 0xf3, 0xaf, 0x8f, 0xe0, 0x01, 0x1a, 0x4f, 0x97, + 0x17, 0x6e, 0xeb, 0xeb, 0x85, 0xdb, 0x3a, 0x5f, 0xb9, 0x68, 0xb9, 0x72, 0xd1, 0x97, 0x95, 0x8b, + 0x7e, 0xac, 0x5c, 0xf4, 0xf6, 0xc9, 0x7f, 0xfe, 0xd7, 0x8f, 0xab, 0x6a, 0xda, 0x9a, 0xa2, 0xf0, + 0xa0, 0x1c, 0xeb, 0xe1, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe6, 0xbf, 0x19, 0xa6, 0x24, 0x04, + 0x00, 0x00, +} + // Field returns the value for the given fieldpath as a string, if defined. // If the value is not defined, the second value will be false. func (m *Envelope) Field(fieldpath []string) (string, bool) { @@ -108,7 +252,7 @@ func (m *Envelope) Field(fieldpath []string) (string, bool) { case "topic": return string(m.Topic), len(m.Topic) > 0 case "event": - decoded, err := typeurl.UnmarshalAny(m.Event) + decoded, err := github_com_containerd_typeurl.UnmarshalAny(m.Event) if err != nil { return "", false } @@ -130,20 +274,21 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Events service - +// EventsClient is the client API for Events service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type EventsClient interface { // Publish an event to a topic. // // The event will be packed into a timestamp envelope with the namespace // introspected from the context. The envelope will then be dispatched. - Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error) + Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*types.Empty, error) // Forward sends an event that has already been packaged into an envelope // with a timestamp and namespace. // // This is useful if earlier timestamping is required or when forwarding on // behalf of another component, namespace or publisher. - Forward(ctx context.Context, in *ForwardRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error) + Forward(ctx context.Context, in *ForwardRequest, opts ...grpc.CallOption) (*types.Empty, error) // Subscribe to a stream of events, possibly returning only that match any // of the provided filters. // @@ -162,18 +307,18 @@ func NewEventsClient(cc *grpc.ClientConn) EventsClient { return &eventsClient{cc} } -func (c *eventsClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error) { - out := new(google_protobuf2.Empty) - err := grpc.Invoke(ctx, "/containerd.services.events.v1.Events/Publish", in, out, c.cc, opts...) +func (c *eventsClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.events.v1.Events/Publish", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *eventsClient) Forward(ctx context.Context, in *ForwardRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error) { - out := new(google_protobuf2.Empty) - err := grpc.Invoke(ctx, "/containerd.services.events.v1.Events/Forward", in, out, c.cc, opts...) +func (c *eventsClient) Forward(ctx context.Context, in *ForwardRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.events.v1.Events/Forward", in, out, opts...) if err != nil { return nil, err } @@ -181,7 +326,7 @@ func (c *eventsClient) Forward(ctx context.Context, in *ForwardRequest, opts ... } func (c *eventsClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (Events_SubscribeClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Events_serviceDesc.Streams[0], c.cc, "/containerd.services.events.v1.Events/Subscribe", opts...) + stream, err := c.cc.NewStream(ctx, &_Events_serviceDesc.Streams[0], "/containerd.services.events.v1.Events/Subscribe", opts...) if err != nil { return nil, err } @@ -212,20 +357,19 @@ func (x *eventsSubscribeClient) Recv() (*Envelope, error) { return m, nil } -// Server API for Events service - +// EventsServer is the server API for Events service. type EventsServer interface { // Publish an event to a topic. // // The event will be packed into a timestamp envelope with the namespace // introspected from the context. The envelope will then be dispatched. - Publish(context.Context, *PublishRequest) (*google_protobuf2.Empty, error) + Publish(context.Context, *PublishRequest) (*types.Empty, error) // Forward sends an event that has already been packaged into an envelope // with a timestamp and namespace. // // This is useful if earlier timestamping is required or when forwarding on // behalf of another component, namespace or publisher. - Forward(context.Context, *ForwardRequest) (*google_protobuf2.Empty, error) + Forward(context.Context, *ForwardRequest) (*types.Empty, error) // Subscribe to a stream of events, possibly returning only that match any // of the provided filters. // @@ -236,6 +380,20 @@ type EventsServer interface { Subscribe(*SubscribeRequest, Events_SubscribeServer) error } +// UnimplementedEventsServer can be embedded to have forward compatible implementations. +type UnimplementedEventsServer struct { +} + +func (*UnimplementedEventsServer) Publish(ctx context.Context, req *PublishRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented") +} +func (*UnimplementedEventsServer) Forward(ctx context.Context, req *ForwardRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Forward not implemented") +} +func (*UnimplementedEventsServer) Subscribe(req *SubscribeRequest, srv Events_SubscribeServer) error { + return status.Errorf(codes.Unimplemented, "method Subscribe not implemented") +} + func RegisterEventsServer(s *grpc.Server, srv EventsServer) { s.RegisterService(&_Events_serviceDesc, srv) } @@ -323,7 +481,7 @@ var _Events_serviceDesc = grpc.ServiceDesc{ func (m *PublishRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -331,33 +489,45 @@ func (m *PublishRequest) Marshal() (dAtA []byte, err error) { } func (m *PublishRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PublishRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Topic) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintEvents(dAtA, i, uint64(len(m.Topic))) - i += copy(dAtA[i:], m.Topic) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Event != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintEvents(dAtA, i, uint64(m.Event.Size())) - n1, err := m.Event.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Event.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) } - i += n1 + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.Topic) > 0 { + i -= len(m.Topic) + copy(dAtA[i:], m.Topic) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Topic))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *ForwardRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -365,27 +535,38 @@ func (m *ForwardRequest) Marshal() (dAtA []byte, err error) { } func (m *ForwardRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ForwardRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Envelope != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintEvents(dAtA, i, uint64(m.Envelope.Size())) - n2, err := m.Envelope.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Envelope.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) } - i += n2 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *SubscribeRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -393,32 +574,35 @@ func (m *SubscribeRequest) Marshal() (dAtA []byte, err error) { } func (m *SubscribeRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Filters) > 0 { - for _, s := range m.Filters { + for iNdEx := len(m.Filters) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Filters[iNdEx]) + copy(dAtA[i:], m.Filters[iNdEx]) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Filters[iNdEx]))) + i-- dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - return i, nil + return len(dAtA) - i, nil } func (m *Envelope) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -426,53 +610,71 @@ func (m *Envelope) Marshal() (dAtA []byte, err error) { } func (m *Envelope) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Envelope) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintEvents(dAtA, i, uint64(types.SizeOfStdTime(m.Timestamp))) - n3, err := types.StdTimeMarshalTo(m.Timestamp, dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n3 - if len(m.Namespace) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintEvents(dAtA, i, uint64(len(m.Namespace))) - i += copy(dAtA[i:], m.Namespace) + if m.Event != nil { + { + size, err := m.Event.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 } if len(m.Topic) > 0 { - dAtA[i] = 0x1a - i++ + i -= len(m.Topic) + copy(dAtA[i:], m.Topic) i = encodeVarintEvents(dAtA, i, uint64(len(m.Topic))) - i += copy(dAtA[i:], m.Topic) + i-- + dAtA[i] = 0x1a } - if m.Event != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintEvents(dAtA, i, uint64(m.Event.Size())) - n4, err := m.Event.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n4 + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0x12 + } + n4, err4 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err4 != nil { + return 0, err4 } - return i, nil + i -= n4 + i = encodeVarintEvents(dAtA, i, uint64(n4)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { + offset -= sovEvents(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *PublishRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Topic) @@ -483,20 +685,32 @@ func (m *PublishRequest) Size() (n int) { l = m.Event.Size() n += 1 + l + sovEvents(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ForwardRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Envelope != nil { l = m.Envelope.Size() n += 1 + l + sovEvents(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *SubscribeRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Filters) > 0 { @@ -505,13 +719,19 @@ func (m *SubscribeRequest) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *Envelope) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l - l = types.SizeOfStdTime(m.Timestamp) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp) n += 1 + l + sovEvents(uint64(l)) l = len(m.Namespace) if l > 0 { @@ -525,18 +745,14 @@ func (m *Envelope) Size() (n int) { l = m.Event.Size() n += 1 + l + sovEvents(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovEvents(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozEvents(x uint64) (n int) { return sovEvents(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -547,7 +763,8 @@ func (this *PublishRequest) String() string { } s := strings.Join([]string{`&PublishRequest{`, `Topic:` + fmt.Sprintf("%v", this.Topic) + `,`, - `Event:` + strings.Replace(fmt.Sprintf("%v", this.Event), "Any", "google_protobuf1.Any", 1) + `,`, + `Event:` + strings.Replace(fmt.Sprintf("%v", this.Event), "Any", "types.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -557,7 +774,8 @@ func (this *ForwardRequest) String() string { return "nil" } s := strings.Join([]string{`&ForwardRequest{`, - `Envelope:` + strings.Replace(fmt.Sprintf("%v", this.Envelope), "Envelope", "Envelope", 1) + `,`, + `Envelope:` + strings.Replace(this.Envelope.String(), "Envelope", "Envelope", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -568,6 +786,7 @@ func (this *SubscribeRequest) String() string { } s := strings.Join([]string{`&SubscribeRequest{`, `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -577,10 +796,11 @@ func (this *Envelope) String() string { return "nil" } s := strings.Join([]string{`&Envelope{`, - `Timestamp:` + strings.Replace(strings.Replace(this.Timestamp.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `Timestamp:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Timestamp), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, `Namespace:` + fmt.Sprintf("%v", this.Namespace) + `,`, `Topic:` + fmt.Sprintf("%v", this.Topic) + `,`, - `Event:` + strings.Replace(fmt.Sprintf("%v", this.Event), "Any", "google_protobuf1.Any", 1) + `,`, + `Event:` + strings.Replace(fmt.Sprintf("%v", this.Event), "Any", "types.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -608,7 +828,7 @@ func (m *PublishRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -636,7 +856,7 @@ func (m *PublishRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -646,6 +866,9 @@ func (m *PublishRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthEvents } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -665,7 +888,7 @@ func (m *PublishRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -674,11 +897,14 @@ func (m *PublishRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthEvents } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Event == nil { - m.Event = &google_protobuf1.Any{} + m.Event = &types.Any{} } if err := m.Event.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -690,12 +916,13 @@ func (m *PublishRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthEvents } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -720,7 +947,7 @@ func (m *ForwardRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -748,7 +975,7 @@ func (m *ForwardRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -757,6 +984,9 @@ func (m *ForwardRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthEvents } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -773,12 +1003,13 @@ func (m *ForwardRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthEvents } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -803,7 +1034,7 @@ func (m *SubscribeRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -831,7 +1062,7 @@ func (m *SubscribeRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -841,6 +1072,9 @@ func (m *SubscribeRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthEvents } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -852,12 +1086,13 @@ func (m *SubscribeRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthEvents } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -882,7 +1117,7 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -910,7 +1145,7 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -919,10 +1154,13 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { return ErrInvalidLengthEvents } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -940,7 +1178,7 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -950,6 +1188,9 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { return ErrInvalidLengthEvents } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -969,7 +1210,7 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -979,6 +1220,9 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { return ErrInvalidLengthEvents } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -998,7 +1242,7 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1007,11 +1251,14 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { return ErrInvalidLengthEvents } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Event == nil { - m.Event = &google_protobuf1.Any{} + m.Event = &types.Any{} } if err := m.Event.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -1023,12 +1270,13 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthEvents } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1041,6 +1289,7 @@ func (m *Envelope) Unmarshal(dAtA []byte) error { func skipEvents(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1072,10 +1321,8 @@ func skipEvents(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1092,91 +1339,34 @@ func skipEvents(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthEvents } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowEvents - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipEvents(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvents + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvents + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthEvents = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowEvents = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthEvents = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvents = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvents = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/events/v1/events.proto", fileDescriptorEvents) -} - -var fileDescriptorEvents = []byte{ - // 466 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0xcd, 0x8e, 0xd3, 0x30, - 0x14, 0x85, 0xeb, 0xf9, 0x6d, 0x3c, 0xd2, 0x08, 0x45, 0x15, 0x2a, 0x01, 0xd2, 0xaa, 0x1b, 0x2a, - 0x04, 0x0e, 0x53, 0x76, 0x20, 0x21, 0x28, 0x94, 0xf5, 0x28, 0x80, 0x54, 0xb1, 0x4b, 0xd2, 0xdb, - 0xd4, 0x52, 0x62, 0x9b, 0xd8, 0x09, 0x9a, 0xdd, 0x3c, 0x02, 0x1b, 0xde, 0x84, 0x0d, 0x6f, 0xd0, - 0x25, 0x4b, 0x56, 0xc0, 0xf4, 0x49, 0x50, 0x13, 0xbb, 0x61, 0x3a, 0x40, 0x10, 0xbb, 0x6b, 0xdf, - 0xe3, 0xcf, 0xb9, 0xe7, 0x38, 0xf8, 0x45, 0x4c, 0xd5, 0x22, 0x0f, 0x49, 0xc4, 0x53, 0x2f, 0xe2, - 0x4c, 0x05, 0x94, 0x41, 0x36, 0xfb, 0xb5, 0x0c, 0x04, 0xf5, 0x24, 0x64, 0x05, 0x8d, 0x40, 0x7a, - 0x50, 0x00, 0x53, 0xd2, 0x2b, 0x4e, 0x74, 0x45, 0x44, 0xc6, 0x15, 0xb7, 0x6f, 0xd7, 0x7a, 0x62, - 0xb4, 0x44, 0x2b, 0x8a, 0x13, 0xe7, 0x69, 0xe3, 0x25, 0x25, 0x26, 0xcc, 0xe7, 0x9e, 0x48, 0xf2, - 0x98, 0x32, 0x6f, 0x4e, 0x21, 0x99, 0x89, 0x40, 0x2d, 0xaa, 0x0b, 0x9c, 0x4e, 0xcc, 0x63, 0x5e, - 0x96, 0xde, 0xba, 0xd2, 0xbb, 0x37, 0x62, 0xce, 0xe3, 0x04, 0xea, 0xd3, 0x01, 0x3b, 0xd3, 0xad, - 0x9b, 0xdb, 0x2d, 0x48, 0x85, 0x32, 0xcd, 0xde, 0x76, 0x53, 0xd1, 0x14, 0xa4, 0x0a, 0x52, 0x51, - 0x09, 0x06, 0x3e, 0x3e, 0x3e, 0xcd, 0xc3, 0x84, 0xca, 0x85, 0x0f, 0xef, 0x72, 0x90, 0xca, 0xee, - 0xe0, 0x7d, 0xc5, 0x05, 0x8d, 0xba, 0xa8, 0x8f, 0x86, 0x96, 0x5f, 0x2d, 0xec, 0xbb, 0x78, 0xbf, - 0x9c, 0xb2, 0xbb, 0xd3, 0x47, 0xc3, 0xa3, 0x51, 0x87, 0x54, 0x60, 0x62, 0xc0, 0xe4, 0x19, 0x3b, - 0xf3, 0x2b, 0xc9, 0xe0, 0x0d, 0x3e, 0x7e, 0xc9, 0xb3, 0xf7, 0x41, 0x36, 0x33, 0xcc, 0xe7, 0xb8, - 0x0d, 0xac, 0x80, 0x84, 0x0b, 0x28, 0xb1, 0x47, 0xa3, 0x3b, 0xe4, 0xaf, 0x46, 0x92, 0x89, 0x96, - 0xfb, 0x9b, 0x83, 0x83, 0x7b, 0xf8, 0xda, 0xab, 0x3c, 0x94, 0x51, 0x46, 0x43, 0x30, 0xe0, 0x2e, - 0x3e, 0x9c, 0xd3, 0x44, 0x41, 0x26, 0xbb, 0xa8, 0xbf, 0x3b, 0xb4, 0x7c, 0xb3, 0x1c, 0x7c, 0x42, - 0xb8, 0x6d, 0x20, 0xf6, 0x18, 0x5b, 0x9b, 0xc1, 0xf5, 0x07, 0x38, 0x57, 0x26, 0x78, 0x6d, 0x14, - 0xe3, 0xf6, 0xf2, 0x5b, 0xaf, 0xf5, 0xe1, 0x7b, 0x0f, 0xf9, 0xf5, 0x31, 0xfb, 0x16, 0xb6, 0x58, - 0x90, 0x82, 0x14, 0x41, 0x04, 0xa5, 0x0b, 0x96, 0x5f, 0x6f, 0xd4, 0xae, 0xed, 0xfe, 0xd6, 0xb5, - 0xbd, 0x46, 0xd7, 0x1e, 0xed, 0x9d, 0x7f, 0xee, 0xa1, 0xd1, 0xc7, 0x1d, 0x7c, 0x30, 0x29, 0x5d, - 0xb0, 0x4f, 0xf1, 0xa1, 0x8e, 0xc6, 0xbe, 0xdf, 0xe0, 0xd6, 0xe5, 0x08, 0x9d, 0xeb, 0x57, 0xee, - 0x99, 0xac, 0xdf, 0xc4, 0x9a, 0xa8, 0x83, 0x69, 0x24, 0x5e, 0x0e, 0xf0, 0x8f, 0xc4, 0x18, 0x5b, - 0x9b, 0x4c, 0x6c, 0xaf, 0x81, 0xb9, 0x9d, 0x9e, 0xf3, 0xaf, 0x8f, 0xe0, 0x01, 0x1a, 0x4f, 0x97, - 0x17, 0x6e, 0xeb, 0xeb, 0x85, 0xdb, 0x3a, 0x5f, 0xb9, 0x68, 0xb9, 0x72, 0xd1, 0x97, 0x95, 0x8b, - 0x7e, 0xac, 0x5c, 0xf4, 0xf6, 0xc9, 0x7f, 0xfe, 0xd7, 0x8f, 0xab, 0x6a, 0xda, 0x9a, 0xa2, 0xf0, - 0xa0, 0x1c, 0xeb, 0xe1, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe6, 0xbf, 0x19, 0xa6, 0x24, 0x04, - 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto b/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto index 1959c8e39551b..9a2444e54ca10 100644 --- a/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto +++ b/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.events.v1; diff --git a/vendor/github.com/containerd/containerd/api/services/images/v1/images.pb.go b/vendor/github.com/containerd/containerd/api/services/images/v1/images.pb.go index 08090748e654c..de08cc08358a5 100644 --- a/vendor/github.com/containerd/containerd/api/services/images/v1/images.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/images/v1/images.pb.go @@ -1,48 +1,26 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/images/v1/images.proto -/* - Package images is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/images/v1/images.proto - - It has these top-level messages: - Image - GetImageRequest - GetImageResponse - CreateImageRequest - CreateImageResponse - UpdateImageRequest - UpdateImageResponse - ListImagesRequest - ListImagesResponse - DeleteImageRequest -*/ package images -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import google_protobuf1 "github.com/gogo/protobuf/types" -import google_protobuf2 "github.com/gogo/protobuf/types" -import _ "github.com/gogo/protobuf/types" -import containerd_types "github.com/containerd/containerd/api/types" - -import time "time" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + context "context" + fmt "fmt" + types "github.com/containerd/containerd/api/types" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types1 "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -54,7 +32,7 @@ var _ = time.Kitchen // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Image struct { // Name provides a unique name for the image. @@ -66,72 +44,289 @@ type Image struct { // // Labels may be updated using the field mask. // The combined size of a key/value pair cannot exceed 4096 bytes. - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Target describes the content entry point of the image. - Target containerd_types.Descriptor `protobuf:"bytes,3,opt,name=target" json:"target"` + Target types.Descriptor `protobuf:"bytes,3,opt,name=target,proto3" json:"target"` // CreatedAt is the time the image was first created. - CreatedAt time.Time `protobuf:"bytes,7,opt,name=created_at,json=createdAt,stdtime" json:"created_at"` + CreatedAt time.Time `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at"` // UpdatedAt is the last time the image was mutated. - UpdatedAt time.Time `protobuf:"bytes,8,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` + UpdatedAt time.Time `protobuf:"bytes,8,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Image) Reset() { *m = Image{} } +func (*Image) ProtoMessage() {} +func (*Image) Descriptor() ([]byte, []int) { + return fileDescriptor_8666fa071128ae5f, []int{0} +} +func (m *Image) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Image) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Image.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Image) XXX_Merge(src proto.Message) { + xxx_messageInfo_Image.Merge(m, src) +} +func (m *Image) XXX_Size() int { + return m.Size() +} +func (m *Image) XXX_DiscardUnknown() { + xxx_messageInfo_Image.DiscardUnknown(m) } -func (m *Image) Reset() { *m = Image{} } -func (*Image) ProtoMessage() {} -func (*Image) Descriptor() ([]byte, []int) { return fileDescriptorImages, []int{0} } +var xxx_messageInfo_Image proto.InternalMessageInfo type GetImageRequest struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetImageRequest) Reset() { *m = GetImageRequest{} } +func (*GetImageRequest) ProtoMessage() {} +func (*GetImageRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8666fa071128ae5f, []int{1} +} +func (m *GetImageRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetImageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetImageRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetImageRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetImageRequest.Merge(m, src) +} +func (m *GetImageRequest) XXX_Size() int { + return m.Size() +} +func (m *GetImageRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetImageRequest.DiscardUnknown(m) } -func (m *GetImageRequest) Reset() { *m = GetImageRequest{} } -func (*GetImageRequest) ProtoMessage() {} -func (*GetImageRequest) Descriptor() ([]byte, []int) { return fileDescriptorImages, []int{1} } +var xxx_messageInfo_GetImageRequest proto.InternalMessageInfo type GetImageResponse struct { - Image *Image `protobuf:"bytes,1,opt,name=image" json:"image,omitempty"` + Image *Image `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetImageResponse) Reset() { *m = GetImageResponse{} } +func (*GetImageResponse) ProtoMessage() {} +func (*GetImageResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_8666fa071128ae5f, []int{2} +} +func (m *GetImageResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetImageResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetImageResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetImageResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetImageResponse.Merge(m, src) +} +func (m *GetImageResponse) XXX_Size() int { + return m.Size() +} +func (m *GetImageResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetImageResponse.DiscardUnknown(m) } -func (m *GetImageResponse) Reset() { *m = GetImageResponse{} } -func (*GetImageResponse) ProtoMessage() {} -func (*GetImageResponse) Descriptor() ([]byte, []int) { return fileDescriptorImages, []int{2} } +var xxx_messageInfo_GetImageResponse proto.InternalMessageInfo type CreateImageRequest struct { - Image Image `protobuf:"bytes,1,opt,name=image" json:"image"` + Image Image `protobuf:"bytes,1,opt,name=image,proto3" json:"image"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateImageRequest) Reset() { *m = CreateImageRequest{} } +func (*CreateImageRequest) ProtoMessage() {} +func (*CreateImageRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8666fa071128ae5f, []int{3} +} +func (m *CreateImageRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateImageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateImageRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateImageRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateImageRequest.Merge(m, src) +} +func (m *CreateImageRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateImageRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateImageRequest.DiscardUnknown(m) } -func (m *CreateImageRequest) Reset() { *m = CreateImageRequest{} } -func (*CreateImageRequest) ProtoMessage() {} -func (*CreateImageRequest) Descriptor() ([]byte, []int) { return fileDescriptorImages, []int{3} } +var xxx_messageInfo_CreateImageRequest proto.InternalMessageInfo type CreateImageResponse struct { - Image Image `protobuf:"bytes,1,opt,name=image" json:"image"` + Image Image `protobuf:"bytes,1,opt,name=image,proto3" json:"image"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateImageResponse) Reset() { *m = CreateImageResponse{} } +func (*CreateImageResponse) ProtoMessage() {} +func (*CreateImageResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_8666fa071128ae5f, []int{4} +} +func (m *CreateImageResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateImageResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateImageResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateImageResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateImageResponse.Merge(m, src) +} +func (m *CreateImageResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateImageResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateImageResponse.DiscardUnknown(m) } -func (m *CreateImageResponse) Reset() { *m = CreateImageResponse{} } -func (*CreateImageResponse) ProtoMessage() {} -func (*CreateImageResponse) Descriptor() ([]byte, []int) { return fileDescriptorImages, []int{4} } +var xxx_messageInfo_CreateImageResponse proto.InternalMessageInfo type UpdateImageRequest struct { // Image provides a full or partial image for update. // // The name field must be set or an error will be returned. - Image Image `protobuf:"bytes,1,opt,name=image" json:"image"` + Image Image `protobuf:"bytes,1,opt,name=image,proto3" json:"image"` // UpdateMask specifies which fields to perform the update on. If empty, // the operation applies to all fields. - UpdateMask *google_protobuf2.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask" json:"update_mask,omitempty"` + UpdateMask *types1.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateImageRequest) Reset() { *m = UpdateImageRequest{} } +func (*UpdateImageRequest) ProtoMessage() {} +func (*UpdateImageRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8666fa071128ae5f, []int{5} +} +func (m *UpdateImageRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateImageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateImageRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateImageRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateImageRequest.Merge(m, src) +} +func (m *UpdateImageRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateImageRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateImageRequest.DiscardUnknown(m) } -func (m *UpdateImageRequest) Reset() { *m = UpdateImageRequest{} } -func (*UpdateImageRequest) ProtoMessage() {} -func (*UpdateImageRequest) Descriptor() ([]byte, []int) { return fileDescriptorImages, []int{5} } +var xxx_messageInfo_UpdateImageRequest proto.InternalMessageInfo type UpdateImageResponse struct { - Image Image `protobuf:"bytes,1,opt,name=image" json:"image"` + Image Image `protobuf:"bytes,1,opt,name=image,proto3" json:"image"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateImageResponse) Reset() { *m = UpdateImageResponse{} } +func (*UpdateImageResponse) ProtoMessage() {} +func (*UpdateImageResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_8666fa071128ae5f, []int{6} +} +func (m *UpdateImageResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateImageResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateImageResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateImageResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateImageResponse.Merge(m, src) +} +func (m *UpdateImageResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateImageResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateImageResponse.DiscardUnknown(m) } -func (m *UpdateImageResponse) Reset() { *m = UpdateImageResponse{} } -func (*UpdateImageResponse) ProtoMessage() {} -func (*UpdateImageResponse) Descriptor() ([]byte, []int) { return fileDescriptorImages, []int{6} } +var xxx_messageInfo_UpdateImageResponse proto.InternalMessageInfo type ListImagesRequest struct { // Filters contains one or more filters using the syntax defined in the @@ -144,20 +339,82 @@ type ListImagesRequest struct { // filters[0] or filters[1] or ... or filters[n-1] or filters[n] // // If filters is zero-length or nil, all items will be returned. - Filters []string `protobuf:"bytes,1,rep,name=filters" json:"filters,omitempty"` + Filters []string `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListImagesRequest) Reset() { *m = ListImagesRequest{} } +func (*ListImagesRequest) ProtoMessage() {} +func (*ListImagesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8666fa071128ae5f, []int{7} +} +func (m *ListImagesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListImagesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListImagesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListImagesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListImagesRequest.Merge(m, src) +} +func (m *ListImagesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListImagesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListImagesRequest.DiscardUnknown(m) } -func (m *ListImagesRequest) Reset() { *m = ListImagesRequest{} } -func (*ListImagesRequest) ProtoMessage() {} -func (*ListImagesRequest) Descriptor() ([]byte, []int) { return fileDescriptorImages, []int{7} } +var xxx_messageInfo_ListImagesRequest proto.InternalMessageInfo type ListImagesResponse struct { - Images []Image `protobuf:"bytes,1,rep,name=images" json:"images"` + Images []Image `protobuf:"bytes,1,rep,name=images,proto3" json:"images"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListImagesResponse) Reset() { *m = ListImagesResponse{} } +func (*ListImagesResponse) ProtoMessage() {} +func (*ListImagesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_8666fa071128ae5f, []int{8} +} +func (m *ListImagesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListImagesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListImagesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListImagesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListImagesResponse.Merge(m, src) +} +func (m *ListImagesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListImagesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListImagesResponse.DiscardUnknown(m) } -func (m *ListImagesResponse) Reset() { *m = ListImagesResponse{} } -func (*ListImagesResponse) ProtoMessage() {} -func (*ListImagesResponse) Descriptor() ([]byte, []int) { return fileDescriptorImages, []int{8} } +var xxx_messageInfo_ListImagesResponse proto.InternalMessageInfo type DeleteImageRequest struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` @@ -165,15 +422,47 @@ type DeleteImageRequest struct { // synchronously before returning to the caller // // Default is false - Sync bool `protobuf:"varint,2,opt,name=sync,proto3" json:"sync,omitempty"` + Sync bool `protobuf:"varint,2,opt,name=sync,proto3" json:"sync,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteImageRequest) Reset() { *m = DeleteImageRequest{} } +func (*DeleteImageRequest) ProtoMessage() {} +func (*DeleteImageRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8666fa071128ae5f, []int{9} +} +func (m *DeleteImageRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteImageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteImageRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteImageRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteImageRequest.Merge(m, src) +} +func (m *DeleteImageRequest) XXX_Size() int { + return m.Size() +} +func (m *DeleteImageRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteImageRequest.DiscardUnknown(m) } -func (m *DeleteImageRequest) Reset() { *m = DeleteImageRequest{} } -func (*DeleteImageRequest) ProtoMessage() {} -func (*DeleteImageRequest) Descriptor() ([]byte, []int) { return fileDescriptorImages, []int{9} } +var xxx_messageInfo_DeleteImageRequest proto.InternalMessageInfo func init() { proto.RegisterType((*Image)(nil), "containerd.services.images.v1.Image") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.images.v1.Image.LabelsEntry") proto.RegisterType((*GetImageRequest)(nil), "containerd.services.images.v1.GetImageRequest") proto.RegisterType((*GetImageResponse)(nil), "containerd.services.images.v1.GetImageResponse") proto.RegisterType((*CreateImageRequest)(nil), "containerd.services.images.v1.CreateImageRequest") @@ -185,6 +474,56 @@ func init() { proto.RegisterType((*DeleteImageRequest)(nil), "containerd.services.images.v1.DeleteImageRequest") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/images/v1/images.proto", fileDescriptor_8666fa071128ae5f) +} + +var fileDescriptor_8666fa071128ae5f = []byte{ + // 659 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0x8e, 0x93, 0xd4, 0x6d, 0x27, 0x07, 0xca, 0x52, 0x21, 0xcb, 0x40, 0x1a, 0x45, 0x20, 0xe5, + 0xc2, 0x9a, 0x86, 0x0b, 0xb4, 0x08, 0xd1, 0xb4, 0xa5, 0x20, 0x15, 0x0e, 0xe6, 0xaf, 0xe2, 0x52, + 0x6d, 0x92, 0x89, 0xb1, 0x62, 0xc7, 0xc6, 0xbb, 0x89, 0x94, 0x1b, 0x8f, 0x80, 0x04, 0x0f, 0xd5, + 0x23, 0x47, 0x4e, 0x40, 0x73, 0xe0, 0x39, 0x90, 0x77, 0x37, 0x34, 0x4d, 0x22, 0x92, 0x94, 0xde, + 0x66, 0xed, 0xef, 0x9b, 0x9f, 0x6f, 0x66, 0x76, 0x61, 0xcf, 0xf3, 0xc5, 0x87, 0x6e, 0x9d, 0x36, + 0xa2, 0xd0, 0x69, 0x44, 0x1d, 0xc1, 0xfc, 0x0e, 0x26, 0xcd, 0x51, 0x93, 0xc5, 0xbe, 0xc3, 0x31, + 0xe9, 0xf9, 0x0d, 0xe4, 0x8e, 0x1f, 0x32, 0x0f, 0xb9, 0xd3, 0xdb, 0xd4, 0x16, 0x8d, 0x93, 0x48, + 0x44, 0xe4, 0xd6, 0x19, 0x9e, 0x0e, 0xb1, 0x54, 0x23, 0x7a, 0x9b, 0xf6, 0xba, 0x17, 0x79, 0x91, + 0x44, 0x3a, 0xa9, 0xa5, 0x48, 0xf6, 0x0d, 0x2f, 0x8a, 0xbc, 0x00, 0x1d, 0x79, 0xaa, 0x77, 0x5b, + 0x0e, 0x86, 0xb1, 0xe8, 0xeb, 0x9f, 0xa5, 0xf1, 0x9f, 0x2d, 0x1f, 0x83, 0xe6, 0x71, 0xc8, 0x78, + 0x5b, 0x23, 0x36, 0xc6, 0x11, 0xc2, 0x0f, 0x91, 0x0b, 0x16, 0xc6, 0x1a, 0xb0, 0x3d, 0x57, 0x69, + 0xa2, 0x1f, 0x23, 0x77, 0x9a, 0xc8, 0x1b, 0x89, 0x1f, 0x8b, 0x28, 0x51, 0xe4, 0xf2, 0xef, 0x2c, + 0x2c, 0x3d, 0x4f, 0x0b, 0x20, 0x04, 0xf2, 0x1d, 0x16, 0xa2, 0x65, 0x94, 0x8c, 0xca, 0xaa, 0x2b, + 0x6d, 0xf2, 0x0c, 0xcc, 0x80, 0xd5, 0x31, 0xe0, 0x56, 0xb6, 0x94, 0xab, 0x14, 0xaa, 0xf7, 0xe8, + 0x3f, 0x05, 0xa0, 0xd2, 0x13, 0x3d, 0x94, 0x94, 0xfd, 0x8e, 0x48, 0xfa, 0xae, 0xe6, 0x93, 0x2d, + 0x30, 0x05, 0x4b, 0x3c, 0x14, 0x56, 0xae, 0x64, 0x54, 0x0a, 0xd5, 0x9b, 0xa3, 0x9e, 0x64, 0x6e, + 0x74, 0xef, 0x6f, 0x6e, 0xb5, 0xfc, 0xc9, 0x8f, 0x8d, 0x8c, 0xab, 0x19, 0x64, 0x17, 0xa0, 0x91, + 0x20, 0x13, 0xd8, 0x3c, 0x66, 0xc2, 0x5a, 0x96, 0x7c, 0x9b, 0x2a, 0x59, 0xe8, 0x50, 0x16, 0xfa, + 0x7a, 0x28, 0x4b, 0x6d, 0x25, 0x65, 0x7f, 0xfe, 0xb9, 0x61, 0xb8, 0xab, 0x9a, 0xb7, 0x23, 0x9d, + 0x74, 0xe3, 0xe6, 0xd0, 0xc9, 0xca, 0x22, 0x4e, 0x34, 0x6f, 0x47, 0xd8, 0x0f, 0xa1, 0x30, 0x52, + 0x1c, 0x59, 0x83, 0x5c, 0x1b, 0xfb, 0x5a, 0xb1, 0xd4, 0x24, 0xeb, 0xb0, 0xd4, 0x63, 0x41, 0x17, + 0xad, 0xac, 0xfc, 0xa6, 0x0e, 0x5b, 0xd9, 0x07, 0x46, 0xf9, 0x0e, 0x5c, 0x39, 0x40, 0x21, 0x05, + 0x72, 0xf1, 0x63, 0x17, 0xb9, 0x98, 0xa6, 0x78, 0xf9, 0x25, 0xac, 0x9d, 0xc1, 0x78, 0x1c, 0x75, + 0x38, 0x92, 0x2d, 0x58, 0x92, 0x12, 0x4b, 0x60, 0xa1, 0x7a, 0x7b, 0x9e, 0x26, 0xb8, 0x8a, 0x52, + 0x7e, 0x0b, 0x64, 0x57, 0x6a, 0x70, 0x2e, 0xf2, 0x93, 0x0b, 0x78, 0xd4, 0x4d, 0xd1, 0x7e, 0xdf, + 0xc1, 0xb5, 0x73, 0x7e, 0x75, 0xaa, 0xff, 0xef, 0xf8, 0x8b, 0x01, 0xe4, 0x8d, 0x14, 0xfc, 0x72, + 0x33, 0x26, 0xdb, 0x50, 0x50, 0x8d, 0x94, 0xcb, 0x25, 0x1b, 0x34, 0x6d, 0x02, 0x9e, 0xa6, 0xfb, + 0xf7, 0x82, 0xf1, 0xb6, 0xab, 0xe7, 0x25, 0xb5, 0xd3, 0x72, 0xcf, 0x25, 0x75, 0x69, 0xe5, 0xde, + 0x85, 0xab, 0x87, 0x3e, 0x57, 0x0d, 0xe7, 0xc3, 0x62, 0x2d, 0x58, 0x6e, 0xf9, 0x81, 0xc0, 0x84, + 0x5b, 0x46, 0x29, 0x57, 0x59, 0x75, 0x87, 0xc7, 0xf2, 0x11, 0x90, 0x51, 0xb8, 0x4e, 0xa3, 0x06, + 0xa6, 0x0a, 0x22, 0xe1, 0x8b, 0xe5, 0xa1, 0x99, 0xe5, 0x47, 0x40, 0xf6, 0x30, 0xc0, 0x31, 0xd9, + 0xa7, 0x5d, 0x0a, 0x04, 0xf2, 0xbc, 0xdf, 0x69, 0x48, 0x05, 0x57, 0x5c, 0x69, 0x57, 0xbf, 0xe6, + 0xc1, 0x54, 0x49, 0x91, 0x16, 0xe4, 0x0e, 0x50, 0x10, 0x3a, 0x23, 0x87, 0xb1, 0x65, 0xb0, 0x9d, + 0xb9, 0xf1, 0xba, 0xe8, 0x36, 0xe4, 0x53, 0x29, 0xc8, 0xac, 0x3b, 0x69, 0x42, 0x5e, 0x7b, 0x73, + 0x01, 0x86, 0x0e, 0x16, 0x81, 0xa9, 0xc6, 0x9d, 0xcc, 0x22, 0x4f, 0x6e, 0x9b, 0x5d, 0x5d, 0x84, + 0x72, 0x16, 0x50, 0x0d, 0xdc, 0xcc, 0x80, 0x93, 0xcb, 0x32, 0x33, 0xe0, 0xb4, 0x51, 0x7e, 0x05, + 0xa6, 0xea, 0xff, 0xcc, 0x80, 0x93, 0x63, 0x62, 0x5f, 0x9f, 0x58, 0xa3, 0xfd, 0xf4, 0x8d, 0xab, + 0x1d, 0x9d, 0x9c, 0x16, 0x33, 0xdf, 0x4f, 0x8b, 0x99, 0x4f, 0x83, 0xa2, 0x71, 0x32, 0x28, 0x1a, + 0xdf, 0x06, 0x45, 0xe3, 0xd7, 0xa0, 0x68, 0xbc, 0x7f, 0x7c, 0xc1, 0xf7, 0x78, 0x5b, 0x59, 0x47, + 0x99, 0xba, 0x29, 0x63, 0xdd, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0x24, 0x4e, 0xca, 0x64, 0xda, + 0x07, 0x00, 0x00, +} + // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn @@ -193,8 +532,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Images service - +// ImagesClient is the client API for Images service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ImagesClient interface { // Get returns an image by name. Get(ctx context.Context, in *GetImageRequest, opts ...grpc.CallOption) (*GetImageResponse, error) @@ -208,7 +548,7 @@ type ImagesClient interface { // image. Update(ctx context.Context, in *UpdateImageRequest, opts ...grpc.CallOption) (*UpdateImageResponse, error) // Delete deletes the image by name. - Delete(ctx context.Context, in *DeleteImageRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + Delete(ctx context.Context, in *DeleteImageRequest, opts ...grpc.CallOption) (*types1.Empty, error) } type imagesClient struct { @@ -221,7 +561,7 @@ func NewImagesClient(cc *grpc.ClientConn) ImagesClient { func (c *imagesClient) Get(ctx context.Context, in *GetImageRequest, opts ...grpc.CallOption) (*GetImageResponse, error) { out := new(GetImageResponse) - err := grpc.Invoke(ctx, "/containerd.services.images.v1.Images/Get", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.images.v1.Images/Get", in, out, opts...) if err != nil { return nil, err } @@ -230,7 +570,7 @@ func (c *imagesClient) Get(ctx context.Context, in *GetImageRequest, opts ...grp func (c *imagesClient) List(ctx context.Context, in *ListImagesRequest, opts ...grpc.CallOption) (*ListImagesResponse, error) { out := new(ListImagesResponse) - err := grpc.Invoke(ctx, "/containerd.services.images.v1.Images/List", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.images.v1.Images/List", in, out, opts...) if err != nil { return nil, err } @@ -239,7 +579,7 @@ func (c *imagesClient) List(ctx context.Context, in *ListImagesRequest, opts ... func (c *imagesClient) Create(ctx context.Context, in *CreateImageRequest, opts ...grpc.CallOption) (*CreateImageResponse, error) { out := new(CreateImageResponse) - err := grpc.Invoke(ctx, "/containerd.services.images.v1.Images/Create", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.images.v1.Images/Create", in, out, opts...) if err != nil { return nil, err } @@ -248,24 +588,23 @@ func (c *imagesClient) Create(ctx context.Context, in *CreateImageRequest, opts func (c *imagesClient) Update(ctx context.Context, in *UpdateImageRequest, opts ...grpc.CallOption) (*UpdateImageResponse, error) { out := new(UpdateImageResponse) - err := grpc.Invoke(ctx, "/containerd.services.images.v1.Images/Update", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.images.v1.Images/Update", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *imagesClient) Delete(ctx context.Context, in *DeleteImageRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { - out := new(google_protobuf1.Empty) - err := grpc.Invoke(ctx, "/containerd.services.images.v1.Images/Delete", in, out, c.cc, opts...) +func (c *imagesClient) Delete(ctx context.Context, in *DeleteImageRequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.images.v1.Images/Delete", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Images service - +// ImagesServer is the server API for Images service. type ImagesServer interface { // Get returns an image by name. Get(context.Context, *GetImageRequest) (*GetImageResponse, error) @@ -279,7 +618,27 @@ type ImagesServer interface { // image. Update(context.Context, *UpdateImageRequest) (*UpdateImageResponse, error) // Delete deletes the image by name. - Delete(context.Context, *DeleteImageRequest) (*google_protobuf1.Empty, error) + Delete(context.Context, *DeleteImageRequest) (*types1.Empty, error) +} + +// UnimplementedImagesServer can be embedded to have forward compatible implementations. +type UnimplementedImagesServer struct { +} + +func (*UnimplementedImagesServer) Get(ctx context.Context, req *GetImageRequest) (*GetImageResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (*UnimplementedImagesServer) List(ctx context.Context, req *ListImagesRequest) (*ListImagesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (*UnimplementedImagesServer) Create(ctx context.Context, req *CreateImageRequest) (*CreateImageResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") +} +func (*UnimplementedImagesServer) Update(ctx context.Context, req *UpdateImageRequest) (*UpdateImageResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (*UnimplementedImagesServer) Delete(ctx context.Context, req *DeleteImageRequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") } func RegisterImagesServer(s *grpc.Server, srv ImagesServer) { @@ -408,7 +767,7 @@ var _Images_serviceDesc = grpc.ServiceDesc{ func (m *Image) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -416,64 +775,78 @@ func (m *Image) Marshal() (dAtA []byte, err error) { } func (m *Image) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Image) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintImages(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintImages(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x42 + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintImages(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x3a + { + size, err := m.Target.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintImages(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x1a if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x12 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovImages(uint64(len(k))) + 1 + len(v) + sovImages(uint64(len(v))) - i = encodeVarintImages(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintImages(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintImages(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintImages(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintImages(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - dAtA[i] = 0x1a - i++ - i = encodeVarintImages(dAtA, i, uint64(m.Target.Size())) - n1, err := m.Target.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 - dAtA[i] = 0x3a - i++ - i = encodeVarintImages(dAtA, i, uint64(types.SizeOfStdTime(m.CreatedAt))) - n2, err := types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 - dAtA[i] = 0x42 - i++ - i = encodeVarintImages(dAtA, i, uint64(types.SizeOfStdTime(m.UpdatedAt))) - n3, err := types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i:]) - if err != nil { - return 0, err + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintImages(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa } - i += n3 - return i, nil + return len(dAtA) - i, nil } func (m *GetImageRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -481,23 +854,33 @@ func (m *GetImageRequest) Marshal() (dAtA []byte, err error) { } func (m *GetImageRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetImageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Name) + copy(dAtA[i:], m.Name) i = encodeVarintImages(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *GetImageResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -505,27 +888,38 @@ func (m *GetImageResponse) Marshal() (dAtA []byte, err error) { } func (m *GetImageResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetImageResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Image != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintImages(dAtA, i, uint64(m.Image.Size())) - n4, err := m.Image.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Image.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintImages(dAtA, i, uint64(size)) } - i += n4 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *CreateImageRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -533,25 +927,36 @@ func (m *CreateImageRequest) Marshal() (dAtA []byte, err error) { } func (m *CreateImageRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateImageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintImages(dAtA, i, uint64(m.Image.Size())) - n5, err := m.Image.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Image.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintImages(dAtA, i, uint64(size)) } - i += n5 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *CreateImageResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -559,25 +964,36 @@ func (m *CreateImageResponse) Marshal() (dAtA []byte, err error) { } func (m *CreateImageResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateImageResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintImages(dAtA, i, uint64(m.Image.Size())) - n6, err := m.Image.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n6 - return i, nil + { + size, err := m.Image.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintImages(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *UpdateImageRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -585,35 +1001,48 @@ func (m *UpdateImageRequest) Marshal() (dAtA []byte, err error) { } func (m *UpdateImageRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateImageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintImages(dAtA, i, uint64(m.Image.Size())) - n7, err := m.Image.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n7 if m.UpdateMask != nil { + { + size, err := m.UpdateMask.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintImages(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintImages(dAtA, i, uint64(m.UpdateMask.Size())) - n8, err := m.UpdateMask.MarshalTo(dAtA[i:]) + } + { + size, err := m.Image.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n8 + i -= size + i = encodeVarintImages(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *UpdateImageResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -621,25 +1050,36 @@ func (m *UpdateImageResponse) Marshal() (dAtA []byte, err error) { } func (m *UpdateImageResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateImageResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintImages(dAtA, i, uint64(m.Image.Size())) - n9, err := m.Image.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Image.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintImages(dAtA, i, uint64(size)) } - i += n9 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *ListImagesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -647,32 +1087,35 @@ func (m *ListImagesRequest) Marshal() (dAtA []byte, err error) { } func (m *ListImagesRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListImagesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Filters) > 0 { - for _, s := range m.Filters { + for iNdEx := len(m.Filters) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Filters[iNdEx]) + copy(dAtA[i:], m.Filters[iNdEx]) + i = encodeVarintImages(dAtA, i, uint64(len(m.Filters[iNdEx]))) + i-- dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - return i, nil + return len(dAtA) - i, nil } func (m *ListImagesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -680,29 +1123,40 @@ func (m *ListImagesResponse) Marshal() (dAtA []byte, err error) { } func (m *ListImagesResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListImagesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Images) > 0 { - for _, msg := range m.Images { - dAtA[i] = 0xa - i++ - i = encodeVarintImages(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Images) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Images[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintImages(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *DeleteImageRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -710,39 +1164,54 @@ func (m *DeleteImageRequest) Marshal() (dAtA []byte, err error) { } func (m *DeleteImageRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteImageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintImages(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Sync { - dAtA[i] = 0x10 - i++ + i-- if m.Sync { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintImages(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintImages(dAtA []byte, offset int, v uint64) int { + offset -= sovImages(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Image) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -759,50 +1228,80 @@ func (m *Image) Size() (n int) { } l = m.Target.Size() n += 1 + l + sovImages(uint64(l)) - l = types.SizeOfStdTime(m.CreatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt) n += 1 + l + sovImages(uint64(l)) - l = types.SizeOfStdTime(m.UpdatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt) n += 1 + l + sovImages(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *GetImageRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + sovImages(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *GetImageResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Image != nil { l = m.Image.Size() n += 1 + l + sovImages(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateImageRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Image.Size() n += 1 + l + sovImages(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateImageResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Image.Size() n += 1 + l + sovImages(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateImageRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Image.Size() @@ -811,18 +1310,30 @@ func (m *UpdateImageRequest) Size() (n int) { l = m.UpdateMask.Size() n += 1 + l + sovImages(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateImageResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Image.Size() n += 1 + l + sovImages(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListImagesRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Filters) > 0 { @@ -831,10 +1342,16 @@ func (m *ListImagesRequest) Size() (n int) { n += 1 + l + sovImages(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListImagesResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Images) > 0 { @@ -843,10 +1360,16 @@ func (m *ListImagesResponse) Size() (n int) { n += 1 + l + sovImages(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DeleteImageRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -856,18 +1379,14 @@ func (m *DeleteImageRequest) Size() (n int) { if m.Sync { n += 2 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovImages(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozImages(x uint64) (n int) { return sovImages(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -880,7 +1399,7 @@ func (this *Image) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -889,9 +1408,10 @@ func (this *Image) String() string { s := strings.Join([]string{`&Image{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Labels:` + mapStringForLabels + `,`, - `Target:` + strings.Replace(strings.Replace(this.Target.String(), "Descriptor", "containerd_types.Descriptor", 1), `&`, ``, 1) + `,`, - `CreatedAt:` + strings.Replace(strings.Replace(this.CreatedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, - `UpdatedAt:` + strings.Replace(strings.Replace(this.UpdatedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `Target:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Target), "Descriptor", "types.Descriptor", 1), `&`, ``, 1) + `,`, + `CreatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.CreatedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, + `UpdatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.UpdatedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -902,6 +1422,7 @@ func (this *GetImageRequest) String() string { } s := strings.Join([]string{`&GetImageRequest{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -911,7 +1432,8 @@ func (this *GetImageResponse) String() string { return "nil" } s := strings.Join([]string{`&GetImageResponse{`, - `Image:` + strings.Replace(fmt.Sprintf("%v", this.Image), "Image", "Image", 1) + `,`, + `Image:` + strings.Replace(this.Image.String(), "Image", "Image", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -922,6 +1444,7 @@ func (this *CreateImageRequest) String() string { } s := strings.Join([]string{`&CreateImageRequest{`, `Image:` + strings.Replace(strings.Replace(this.Image.String(), "Image", "Image", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -932,6 +1455,7 @@ func (this *CreateImageResponse) String() string { } s := strings.Join([]string{`&CreateImageResponse{`, `Image:` + strings.Replace(strings.Replace(this.Image.String(), "Image", "Image", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -942,7 +1466,8 @@ func (this *UpdateImageRequest) String() string { } s := strings.Join([]string{`&UpdateImageRequest{`, `Image:` + strings.Replace(strings.Replace(this.Image.String(), "Image", "Image", 1), `&`, ``, 1) + `,`, - `UpdateMask:` + strings.Replace(fmt.Sprintf("%v", this.UpdateMask), "FieldMask", "google_protobuf2.FieldMask", 1) + `,`, + `UpdateMask:` + strings.Replace(fmt.Sprintf("%v", this.UpdateMask), "FieldMask", "types1.FieldMask", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -953,6 +1478,7 @@ func (this *UpdateImageResponse) String() string { } s := strings.Join([]string{`&UpdateImageResponse{`, `Image:` + strings.Replace(strings.Replace(this.Image.String(), "Image", "Image", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -963,6 +1489,7 @@ func (this *ListImagesRequest) String() string { } s := strings.Join([]string{`&ListImagesRequest{`, `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -971,8 +1498,14 @@ func (this *ListImagesResponse) String() string { if this == nil { return "nil" } + repeatedStringForImages := "[]Image{" + for _, f := range this.Images { + repeatedStringForImages += strings.Replace(strings.Replace(f.String(), "Image", "Image", 1), `&`, ``, 1) + "," + } + repeatedStringForImages += "}" s := strings.Join([]string{`&ListImagesResponse{`, - `Images:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Images), "Image", "Image", 1), `&`, ``, 1) + `,`, + `Images:` + repeatedStringForImages + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -984,6 +1517,7 @@ func (this *DeleteImageRequest) String() string { s := strings.Join([]string{`&DeleteImageRequest{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Sync:` + fmt.Sprintf("%v", this.Sync) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1011,7 +1545,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1039,7 +1573,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1049,6 +1583,9 @@ func (m *Image) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1068,7 +1605,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1077,6 +1614,9 @@ func (m *Image) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1097,7 +1637,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1114,7 +1654,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1124,6 +1664,9 @@ func (m *Image) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthImages + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -1140,7 +1683,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1150,6 +1693,9 @@ func (m *Image) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthImages + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -1161,7 +1707,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > postIndex { @@ -1186,7 +1732,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1195,6 +1741,9 @@ func (m *Image) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1216,7 +1765,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1225,10 +1774,13 @@ func (m *Image) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1246,7 +1798,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1255,10 +1807,13 @@ func (m *Image) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1268,12 +1823,13 @@ func (m *Image) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1298,7 +1854,7 @@ func (m *GetImageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1326,7 +1882,7 @@ func (m *GetImageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1336,6 +1892,9 @@ func (m *GetImageRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1347,12 +1906,13 @@ func (m *GetImageRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1377,7 +1937,7 @@ func (m *GetImageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1405,7 +1965,7 @@ func (m *GetImageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1414,6 +1974,9 @@ func (m *GetImageResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1430,12 +1993,13 @@ func (m *GetImageResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1460,7 +2024,7 @@ func (m *CreateImageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1488,7 +2052,7 @@ func (m *CreateImageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1497,6 +2061,9 @@ func (m *CreateImageRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1510,12 +2077,13 @@ func (m *CreateImageRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1540,7 +2108,7 @@ func (m *CreateImageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1568,7 +2136,7 @@ func (m *CreateImageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1577,6 +2145,9 @@ func (m *CreateImageResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1590,12 +2161,13 @@ func (m *CreateImageResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1620,7 +2192,7 @@ func (m *UpdateImageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1648,7 +2220,7 @@ func (m *UpdateImageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1657,6 +2229,9 @@ func (m *UpdateImageRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1678,7 +2253,7 @@ func (m *UpdateImageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1687,11 +2262,14 @@ func (m *UpdateImageRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } if m.UpdateMask == nil { - m.UpdateMask = &google_protobuf2.FieldMask{} + m.UpdateMask = &types1.FieldMask{} } if err := m.UpdateMask.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -1703,12 +2281,13 @@ func (m *UpdateImageRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1733,7 +2312,7 @@ func (m *UpdateImageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1761,7 +2340,7 @@ func (m *UpdateImageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1770,6 +2349,9 @@ func (m *UpdateImageResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1783,12 +2365,13 @@ func (m *UpdateImageResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1813,7 +2396,7 @@ func (m *ListImagesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1841,7 +2424,7 @@ func (m *ListImagesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1851,6 +2434,9 @@ func (m *ListImagesRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1862,12 +2448,13 @@ func (m *ListImagesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1892,7 +2479,7 @@ func (m *ListImagesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1920,7 +2507,7 @@ func (m *ListImagesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1929,6 +2516,9 @@ func (m *ListImagesResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1943,12 +2533,13 @@ func (m *ListImagesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1973,7 +2564,7 @@ func (m *DeleteImageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2001,7 +2592,7 @@ func (m *DeleteImageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2011,6 +2602,9 @@ func (m *DeleteImageRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthImages } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthImages + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2030,7 +2624,7 @@ func (m *DeleteImageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2042,12 +2636,13 @@ func (m *DeleteImageRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthImages } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2060,6 +2655,7 @@ func (m *DeleteImageRequest) Unmarshal(dAtA []byte) error { func skipImages(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -2091,10 +2687,8 @@ func skipImages(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -2111,103 +2705,34 @@ func skipImages(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthImages } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowImages - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipImages(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupImages + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthImages + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthImages = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowImages = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthImages = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowImages = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupImages = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/images/v1/images.proto", fileDescriptorImages) -} - -var fileDescriptorImages = []byte{ - // 659 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xcd, 0x6e, 0xd3, 0x40, - 0x10, 0x8e, 0x93, 0xd4, 0x6d, 0x27, 0x07, 0xca, 0x52, 0x21, 0xcb, 0x40, 0x1a, 0x45, 0x20, 0xe5, - 0xc2, 0x9a, 0x86, 0x0b, 0xb4, 0x08, 0xd1, 0xb4, 0xa5, 0x20, 0x15, 0x0e, 0xe6, 0xaf, 0xe2, 0x52, - 0x6d, 0x92, 0x89, 0xb1, 0x62, 0xc7, 0xc6, 0xbb, 0x89, 0x94, 0x1b, 0x8f, 0x80, 0x04, 0x0f, 0xd5, - 0x23, 0x47, 0x4e, 0x40, 0x73, 0xe0, 0x39, 0x90, 0x77, 0x37, 0x34, 0x4d, 0x22, 0x92, 0x94, 0xde, - 0x66, 0xed, 0xef, 0x9b, 0x9f, 0x6f, 0x66, 0x76, 0x61, 0xcf, 0xf3, 0xc5, 0x87, 0x6e, 0x9d, 0x36, - 0xa2, 0xd0, 0x69, 0x44, 0x1d, 0xc1, 0xfc, 0x0e, 0x26, 0xcd, 0x51, 0x93, 0xc5, 0xbe, 0xc3, 0x31, - 0xe9, 0xf9, 0x0d, 0xe4, 0x8e, 0x1f, 0x32, 0x0f, 0xb9, 0xd3, 0xdb, 0xd4, 0x16, 0x8d, 0x93, 0x48, - 0x44, 0xe4, 0xd6, 0x19, 0x9e, 0x0e, 0xb1, 0x54, 0x23, 0x7a, 0x9b, 0xf6, 0xba, 0x17, 0x79, 0x91, - 0x44, 0x3a, 0xa9, 0xa5, 0x48, 0xf6, 0x0d, 0x2f, 0x8a, 0xbc, 0x00, 0x1d, 0x79, 0xaa, 0x77, 0x5b, - 0x0e, 0x86, 0xb1, 0xe8, 0xeb, 0x9f, 0xa5, 0xf1, 0x9f, 0x2d, 0x1f, 0x83, 0xe6, 0x71, 0xc8, 0x78, - 0x5b, 0x23, 0x36, 0xc6, 0x11, 0xc2, 0x0f, 0x91, 0x0b, 0x16, 0xc6, 0x1a, 0xb0, 0x3d, 0x57, 0x69, - 0xa2, 0x1f, 0x23, 0x77, 0x9a, 0xc8, 0x1b, 0x89, 0x1f, 0x8b, 0x28, 0x51, 0xe4, 0xf2, 0xef, 0x2c, - 0x2c, 0x3d, 0x4f, 0x0b, 0x20, 0x04, 0xf2, 0x1d, 0x16, 0xa2, 0x65, 0x94, 0x8c, 0xca, 0xaa, 0x2b, - 0x6d, 0xf2, 0x0c, 0xcc, 0x80, 0xd5, 0x31, 0xe0, 0x56, 0xb6, 0x94, 0xab, 0x14, 0xaa, 0xf7, 0xe8, - 0x3f, 0x05, 0xa0, 0xd2, 0x13, 0x3d, 0x94, 0x94, 0xfd, 0x8e, 0x48, 0xfa, 0xae, 0xe6, 0x93, 0x2d, - 0x30, 0x05, 0x4b, 0x3c, 0x14, 0x56, 0xae, 0x64, 0x54, 0x0a, 0xd5, 0x9b, 0xa3, 0x9e, 0x64, 0x6e, - 0x74, 0xef, 0x6f, 0x6e, 0xb5, 0xfc, 0xc9, 0x8f, 0x8d, 0x8c, 0xab, 0x19, 0x64, 0x17, 0xa0, 0x91, - 0x20, 0x13, 0xd8, 0x3c, 0x66, 0xc2, 0x5a, 0x96, 0x7c, 0x9b, 0x2a, 0x59, 0xe8, 0x50, 0x16, 0xfa, - 0x7a, 0x28, 0x4b, 0x6d, 0x25, 0x65, 0x7f, 0xfe, 0xb9, 0x61, 0xb8, 0xab, 0x9a, 0xb7, 0x23, 0x9d, - 0x74, 0xe3, 0xe6, 0xd0, 0xc9, 0xca, 0x22, 0x4e, 0x34, 0x6f, 0x47, 0xd8, 0x0f, 0xa1, 0x30, 0x52, - 0x1c, 0x59, 0x83, 0x5c, 0x1b, 0xfb, 0x5a, 0xb1, 0xd4, 0x24, 0xeb, 0xb0, 0xd4, 0x63, 0x41, 0x17, - 0xad, 0xac, 0xfc, 0xa6, 0x0e, 0x5b, 0xd9, 0x07, 0x46, 0xf9, 0x0e, 0x5c, 0x39, 0x40, 0x21, 0x05, - 0x72, 0xf1, 0x63, 0x17, 0xb9, 0x98, 0xa6, 0x78, 0xf9, 0x25, 0xac, 0x9d, 0xc1, 0x78, 0x1c, 0x75, - 0x38, 0x92, 0x2d, 0x58, 0x92, 0x12, 0x4b, 0x60, 0xa1, 0x7a, 0x7b, 0x9e, 0x26, 0xb8, 0x8a, 0x52, - 0x7e, 0x0b, 0x64, 0x57, 0x6a, 0x70, 0x2e, 0xf2, 0x93, 0x0b, 0x78, 0xd4, 0x4d, 0xd1, 0x7e, 0xdf, - 0xc1, 0xb5, 0x73, 0x7e, 0x75, 0xaa, 0xff, 0xef, 0xf8, 0x8b, 0x01, 0xe4, 0x8d, 0x14, 0xfc, 0x72, - 0x33, 0x26, 0xdb, 0x50, 0x50, 0x8d, 0x94, 0xcb, 0x25, 0x1b, 0x34, 0x6d, 0x02, 0x9e, 0xa6, 0xfb, - 0xf7, 0x82, 0xf1, 0xb6, 0xab, 0xe7, 0x25, 0xb5, 0xd3, 0x72, 0xcf, 0x25, 0x75, 0x69, 0xe5, 0xde, - 0x85, 0xab, 0x87, 0x3e, 0x57, 0x0d, 0xe7, 0xc3, 0x62, 0x2d, 0x58, 0x6e, 0xf9, 0x81, 0xc0, 0x84, - 0x5b, 0x46, 0x29, 0x57, 0x59, 0x75, 0x87, 0xc7, 0xf2, 0x11, 0x90, 0x51, 0xb8, 0x4e, 0xa3, 0x06, - 0xa6, 0x0a, 0x22, 0xe1, 0x8b, 0xe5, 0xa1, 0x99, 0xe5, 0x47, 0x40, 0xf6, 0x30, 0xc0, 0x31, 0xd9, - 0xa7, 0x5d, 0x0a, 0x04, 0xf2, 0xbc, 0xdf, 0x69, 0x48, 0x05, 0x57, 0x5c, 0x69, 0x57, 0xbf, 0xe6, - 0xc1, 0x54, 0x49, 0x91, 0x16, 0xe4, 0x0e, 0x50, 0x10, 0x3a, 0x23, 0x87, 0xb1, 0x65, 0xb0, 0x9d, - 0xb9, 0xf1, 0xba, 0xe8, 0x36, 0xe4, 0x53, 0x29, 0xc8, 0xac, 0x3b, 0x69, 0x42, 0x5e, 0x7b, 0x73, - 0x01, 0x86, 0x0e, 0x16, 0x81, 0xa9, 0xc6, 0x9d, 0xcc, 0x22, 0x4f, 0x6e, 0x9b, 0x5d, 0x5d, 0x84, - 0x72, 0x16, 0x50, 0x0d, 0xdc, 0xcc, 0x80, 0x93, 0xcb, 0x32, 0x33, 0xe0, 0xb4, 0x51, 0x7e, 0x05, - 0xa6, 0xea, 0xff, 0xcc, 0x80, 0x93, 0x63, 0x62, 0x5f, 0x9f, 0x58, 0xa3, 0xfd, 0xf4, 0x8d, 0xab, - 0x1d, 0x9d, 0x9c, 0x16, 0x33, 0xdf, 0x4f, 0x8b, 0x99, 0x4f, 0x83, 0xa2, 0x71, 0x32, 0x28, 0x1a, - 0xdf, 0x06, 0x45, 0xe3, 0xd7, 0xa0, 0x68, 0xbc, 0x7f, 0x7c, 0xc1, 0xf7, 0x78, 0x5b, 0x59, 0x47, - 0x99, 0xba, 0x29, 0x63, 0xdd, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0x24, 0x4e, 0xca, 0x64, 0xda, - 0x07, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/images/v1/images.proto b/vendor/github.com/containerd/containerd/api/services/images/v1/images.proto index 152ade2a08753..338f4fb08d78e 100644 --- a/vendor/github.com/containerd/containerd/api/services/images/v1/images.proto +++ b/vendor/github.com/containerd/containerd/api/services/images/v1/images.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.images.v1; diff --git a/vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.pb.go b/vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.pb.go index 02bac622da3b3..d23c8b61a8e22 100644 --- a/vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.pb.go @@ -1,35 +1,25 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/introspection/v1/introspection.proto -/* - Package introspection is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/introspection/v1/introspection.proto - - It has these top-level messages: - Plugin - PluginsRequest - PluginsResponse -*/ package introspection -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import containerd_types "github.com/containerd/containerd/api/types" -import google_rpc "github.com/gogo/googleapis/google/rpc" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + context "context" + fmt "fmt" + types "github.com/containerd/containerd/api/types" + rpc "github.com/gogo/googleapis/google/rpc" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + types1 "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -40,7 +30,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Plugin struct { // Type defines the type of plugin. @@ -51,7 +41,7 @@ type Plugin struct { // ID identifies the plugin uniquely in the system. ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` // Requires lists the plugin types required by this plugin. - Requires []string `protobuf:"bytes,3,rep,name=requires" json:"requires,omitempty"` + Requires []string `protobuf:"bytes,3,rep,name=requires,proto3" json:"requires,omitempty"` // Platforms enumerates the platforms this plugin will support. // // If values are provided here, the plugin will only be operable under the @@ -61,30 +51,61 @@ type Plugin struct { // // If the plugin prefers certain platforms over others, they should be // listed from most to least preferred. - Platforms []containerd_types.Platform `protobuf:"bytes,4,rep,name=platforms" json:"platforms"` + Platforms []types.Platform `protobuf:"bytes,4,rep,name=platforms,proto3" json:"platforms"` // Exports allows plugins to provide values about state or configuration to // interested parties. // // One example is exposing the configured path of a snapshotter plugin. - Exports map[string]string `protobuf:"bytes,5,rep,name=exports" json:"exports,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Exports map[string]string `protobuf:"bytes,5,rep,name=exports,proto3" json:"exports,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Capabilities allows plugins to communicate feature switches to allow // clients to detect features that may not be on be default or may be // different from version to version. // // Use this sparingly. - Capabilities []string `protobuf:"bytes,6,rep,name=capabilities" json:"capabilities,omitempty"` + Capabilities []string `protobuf:"bytes,6,rep,name=capabilities,proto3" json:"capabilities,omitempty"` // InitErr will be set if the plugin fails initialization. // // This means the plugin may have been registered but a non-terminal error // was encountered during initialization. // // Plugins that have this value set cannot be used. - InitErr *google_rpc.Status `protobuf:"bytes,7,opt,name=init_err,json=initErr" json:"init_err,omitempty"` + InitErr *rpc.Status `protobuf:"bytes,7,opt,name=init_err,json=initErr,proto3" json:"init_err,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *Plugin) Reset() { *m = Plugin{} } -func (*Plugin) ProtoMessage() {} -func (*Plugin) Descriptor() ([]byte, []int) { return fileDescriptorIntrospection, []int{0} } +func (m *Plugin) Reset() { *m = Plugin{} } +func (*Plugin) ProtoMessage() {} +func (*Plugin) Descriptor() ([]byte, []int) { + return fileDescriptor_1a14fda866f10715, []int{0} +} +func (m *Plugin) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Plugin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Plugin.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Plugin) XXX_Merge(src proto.Message) { + xxx_messageInfo_Plugin.Merge(m, src) +} +func (m *Plugin) XXX_Size() int { + return m.Size() +} +func (m *Plugin) XXX_DiscardUnknown() { + xxx_messageInfo_Plugin.DiscardUnknown(m) +} + +var xxx_messageInfo_Plugin proto.InternalMessageInfo type PluginsRequest struct { // Filters contains one or more filters using the syntax defined in the @@ -97,25 +118,171 @@ type PluginsRequest struct { // filters[0] or filters[1] or ... or filters[n-1] or filters[n] // // If filters is zero-length or nil, all items will be returned. - Filters []string `protobuf:"bytes,1,rep,name=filters" json:"filters,omitempty"` + Filters []string `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PluginsRequest) Reset() { *m = PluginsRequest{} } +func (*PluginsRequest) ProtoMessage() {} +func (*PluginsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1a14fda866f10715, []int{1} +} +func (m *PluginsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PluginsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PluginsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PluginsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PluginsRequest.Merge(m, src) +} +func (m *PluginsRequest) XXX_Size() int { + return m.Size() +} +func (m *PluginsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PluginsRequest.DiscardUnknown(m) } -func (m *PluginsRequest) Reset() { *m = PluginsRequest{} } -func (*PluginsRequest) ProtoMessage() {} -func (*PluginsRequest) Descriptor() ([]byte, []int) { return fileDescriptorIntrospection, []int{1} } +var xxx_messageInfo_PluginsRequest proto.InternalMessageInfo type PluginsResponse struct { - Plugins []Plugin `protobuf:"bytes,1,rep,name=plugins" json:"plugins"` + Plugins []Plugin `protobuf:"bytes,1,rep,name=plugins,proto3" json:"plugins"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *PluginsResponse) Reset() { *m = PluginsResponse{} } -func (*PluginsResponse) ProtoMessage() {} -func (*PluginsResponse) Descriptor() ([]byte, []int) { return fileDescriptorIntrospection, []int{2} } +func (m *PluginsResponse) Reset() { *m = PluginsResponse{} } +func (*PluginsResponse) ProtoMessage() {} +func (*PluginsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1a14fda866f10715, []int{2} +} +func (m *PluginsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PluginsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PluginsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PluginsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PluginsResponse.Merge(m, src) +} +func (m *PluginsResponse) XXX_Size() int { + return m.Size() +} +func (m *PluginsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PluginsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PluginsResponse proto.InternalMessageInfo + +type ServerResponse struct { + UUID string `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ServerResponse) Reset() { *m = ServerResponse{} } +func (*ServerResponse) ProtoMessage() {} +func (*ServerResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1a14fda866f10715, []int{3} +} +func (m *ServerResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ServerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ServerResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ServerResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServerResponse.Merge(m, src) +} +func (m *ServerResponse) XXX_Size() int { + return m.Size() +} +func (m *ServerResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ServerResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ServerResponse proto.InternalMessageInfo func init() { proto.RegisterType((*Plugin)(nil), "containerd.services.introspection.v1.Plugin") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.introspection.v1.Plugin.ExportsEntry") proto.RegisterType((*PluginsRequest)(nil), "containerd.services.introspection.v1.PluginsRequest") proto.RegisterType((*PluginsResponse)(nil), "containerd.services.introspection.v1.PluginsResponse") + proto.RegisterType((*ServerResponse)(nil), "containerd.services.introspection.v1.ServerResponse") +} + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/introspection/v1/introspection.proto", fileDescriptor_1a14fda866f10715) +} + +var fileDescriptor_1a14fda866f10715 = []byte{ + // 549 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xc1, 0x6e, 0xd3, 0x40, + 0x10, 0xad, 0x9d, 0x34, 0x6e, 0x37, 0xa5, 0xa0, 0x55, 0x55, 0x2c, 0x83, 0x9c, 0x28, 0xe2, 0x10, + 0x21, 0x58, 0xab, 0x01, 0x24, 0x5a, 0x24, 0x0e, 0x51, 0x73, 0x88, 0xd4, 0x43, 0xe5, 0xa8, 0x08, + 0x71, 0xa9, 0x1c, 0x67, 0x63, 0x56, 0x38, 0xde, 0xed, 0xee, 0xda, 0x22, 0x37, 0x3e, 0x2f, 0x47, + 0x8e, 0x9c, 0x02, 0xf5, 0x37, 0xf0, 0x01, 0xc8, 0xbb, 0x76, 0x9a, 0xdc, 0x12, 0x71, 0x9b, 0x79, + 0x33, 0x6f, 0xe6, 0xcd, 0xf3, 0xca, 0xc0, 0x8f, 0x88, 0xfc, 0x9a, 0x8e, 0x51, 0x48, 0x67, 0x5e, + 0x48, 0x13, 0x19, 0x90, 0x04, 0xf3, 0xc9, 0x7a, 0x18, 0x30, 0xe2, 0x09, 0xcc, 0x33, 0x12, 0x62, + 0xe1, 0x91, 0x44, 0x72, 0x2a, 0x18, 0x0e, 0x25, 0xa1, 0x89, 0x97, 0x9d, 0x6d, 0x02, 0x88, 0x71, + 0x2a, 0x29, 0x7c, 0xf1, 0xc0, 0x46, 0x15, 0x13, 0x6d, 0x36, 0x66, 0x67, 0xce, 0xf9, 0x56, 0x9b, + 0xe5, 0x9c, 0x61, 0xe1, 0xb1, 0x38, 0x90, 0x53, 0xca, 0x67, 0x7a, 0x81, 0xf3, 0x34, 0xa2, 0x34, + 0x8a, 0xb1, 0xc7, 0x59, 0xe8, 0x09, 0x19, 0xc8, 0x54, 0x94, 0x85, 0x67, 0x65, 0x41, 0x65, 0xe3, + 0x74, 0xea, 0xe1, 0x19, 0x93, 0xf3, 0xb2, 0x78, 0x12, 0xd1, 0x88, 0xaa, 0xd0, 0x2b, 0x22, 0x8d, + 0x76, 0xfe, 0x9a, 0xa0, 0x71, 0x1d, 0xa7, 0x11, 0x49, 0x20, 0x04, 0xf5, 0x62, 0x9d, 0x6d, 0xb4, + 0x8d, 0xee, 0xa1, 0xaf, 0x62, 0x78, 0x0a, 0x4c, 0x32, 0xb1, 0xcd, 0x02, 0xe9, 0x37, 0xf2, 0x65, + 0xcb, 0x1c, 0x5e, 0xfa, 0x26, 0x99, 0x40, 0x07, 0x1c, 0x70, 0x7c, 0x97, 0x12, 0x8e, 0x85, 0x5d, + 0x6b, 0xd7, 0xba, 0x87, 0xfe, 0x2a, 0x87, 0x1f, 0xc1, 0x61, 0x25, 0x58, 0xd8, 0xf5, 0x76, 0xad, + 0xdb, 0xec, 0x39, 0x68, 0xcd, 0x13, 0x75, 0x13, 0xba, 0x2e, 0x5b, 0xfa, 0xf5, 0xc5, 0xb2, 0xb5, + 0xe7, 0x3f, 0x50, 0xe0, 0x08, 0x58, 0xf8, 0x3b, 0xa3, 0x5c, 0x0a, 0x7b, 0x5f, 0xb1, 0xcf, 0xd1, + 0x36, 0x8e, 0x22, 0x7d, 0x06, 0x1a, 0x68, 0xee, 0x20, 0x91, 0x7c, 0xee, 0x57, 0x93, 0x60, 0x07, + 0x1c, 0x85, 0x01, 0x0b, 0xc6, 0x24, 0x26, 0x92, 0x60, 0x61, 0x37, 0x94, 0xe8, 0x0d, 0x0c, 0xbe, + 0x06, 0x07, 0x24, 0x21, 0xf2, 0x16, 0x73, 0x6e, 0x5b, 0x6d, 0xa3, 0xdb, 0xec, 0x41, 0xa4, 0x1d, + 0x45, 0x9c, 0x85, 0x68, 0xa4, 0xac, 0xf6, 0xad, 0xa2, 0x67, 0xc0, 0xb9, 0x73, 0x01, 0x8e, 0xd6, + 0x77, 0xc1, 0x27, 0xa0, 0xf6, 0x0d, 0xcf, 0x4b, 0xfb, 0x8a, 0x10, 0x9e, 0x80, 0xfd, 0x2c, 0x88, + 0x53, 0xac, 0x0d, 0xf4, 0x75, 0x72, 0x61, 0xbe, 0x37, 0x3a, 0x2f, 0xc1, 0xb1, 0x96, 0x2b, 0x7c, + 0x7c, 0x97, 0x62, 0x21, 0xa1, 0x0d, 0xac, 0x29, 0x89, 0x25, 0xe6, 0xc2, 0x36, 0x94, 0xb6, 0x2a, + 0xed, 0xdc, 0x82, 0xc7, 0xab, 0x5e, 0xc1, 0x68, 0x22, 0x30, 0xbc, 0x02, 0x16, 0xd3, 0x90, 0x6a, + 0x6e, 0xf6, 0x5e, 0xed, 0x62, 0x51, 0x69, 0x79, 0x35, 0xa2, 0x83, 0xc0, 0xf1, 0x08, 0xf3, 0x0c, + 0xf3, 0xd5, 0xfc, 0xe7, 0xa0, 0x9e, 0xa6, 0x64, 0xa2, 0x6f, 0xe9, 0x1f, 0xe4, 0xcb, 0x56, 0xfd, + 0xe6, 0x66, 0x78, 0xe9, 0x2b, 0xb4, 0xf7, 0xdb, 0x00, 0x8f, 0x86, 0xeb, 0xa3, 0x61, 0x06, 0xac, + 0x52, 0x22, 0x7c, 0xbb, 0x8b, 0x92, 0xea, 0x7a, 0xe7, 0xdd, 0x8e, 0xac, 0x52, 0xe7, 0x27, 0xd0, + 0xd0, 0xca, 0xe1, 0x69, 0xf5, 0xa5, 0xaa, 0xb7, 0x8f, 0x06, 0xc5, 0xdb, 0x77, 0xb6, 0x94, 0xb3, + 0x79, 0x7f, 0x7f, 0xba, 0xb8, 0x77, 0xf7, 0x7e, 0xdd, 0xbb, 0x7b, 0x3f, 0x72, 0xd7, 0x58, 0xe4, + 0xae, 0xf1, 0x33, 0x77, 0x8d, 0x3f, 0xb9, 0x6b, 0x7c, 0xb9, 0xfa, 0xbf, 0x1f, 0xc6, 0x87, 0x0d, + 0xe0, 0x73, 0x6d, 0xdc, 0x50, 0x7a, 0xdf, 0xfc, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x0c, 0xb3, 0x50, + 0xdc, 0x89, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -126,14 +293,17 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Introspection service - +// IntrospectionClient is the client API for Introspection service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type IntrospectionClient interface { // Plugins returns a list of plugins in containerd. // // Clients can use this to detect features and capabilities when using // containerd. Plugins(ctx context.Context, in *PluginsRequest, opts ...grpc.CallOption) (*PluginsResponse, error) + // Server returns information about the containerd server + Server(ctx context.Context, in *types1.Empty, opts ...grpc.CallOption) (*ServerResponse, error) } type introspectionClient struct { @@ -146,21 +316,42 @@ func NewIntrospectionClient(cc *grpc.ClientConn) IntrospectionClient { func (c *introspectionClient) Plugins(ctx context.Context, in *PluginsRequest, opts ...grpc.CallOption) (*PluginsResponse, error) { out := new(PluginsResponse) - err := grpc.Invoke(ctx, "/containerd.services.introspection.v1.Introspection/Plugins", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.introspection.v1.Introspection/Plugins", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Introspection service +func (c *introspectionClient) Server(ctx context.Context, in *types1.Empty, opts ...grpc.CallOption) (*ServerResponse, error) { + out := new(ServerResponse) + err := c.cc.Invoke(ctx, "/containerd.services.introspection.v1.Introspection/Server", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} +// IntrospectionServer is the server API for Introspection service. type IntrospectionServer interface { // Plugins returns a list of plugins in containerd. // // Clients can use this to detect features and capabilities when using // containerd. Plugins(context.Context, *PluginsRequest) (*PluginsResponse, error) + // Server returns information about the containerd server + Server(context.Context, *types1.Empty) (*ServerResponse, error) +} + +// UnimplementedIntrospectionServer can be embedded to have forward compatible implementations. +type UnimplementedIntrospectionServer struct { +} + +func (*UnimplementedIntrospectionServer) Plugins(ctx context.Context, req *PluginsRequest) (*PluginsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Plugins not implemented") +} +func (*UnimplementedIntrospectionServer) Server(ctx context.Context, req *types1.Empty) (*ServerResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Server not implemented") } func RegisterIntrospectionServer(s *grpc.Server, srv IntrospectionServer) { @@ -185,6 +376,24 @@ func _Introspection_Plugins_Handler(srv interface{}, ctx context.Context, dec fu return interceptor(ctx, in, info, handler) } +func _Introspection_Server_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(types1.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(IntrospectionServer).Server(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/containerd.services.introspection.v1.Introspection/Server", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(IntrospectionServer).Server(ctx, req.(*types1.Empty)) + } + return interceptor(ctx, in, info, handler) +} + var _Introspection_serviceDesc = grpc.ServiceDesc{ ServiceName: "containerd.services.introspection.v1.Introspection", HandlerType: (*IntrospectionServer)(nil), @@ -193,6 +402,10 @@ var _Introspection_serviceDesc = grpc.ServiceDesc{ MethodName: "Plugins", Handler: _Introspection_Plugins_Handler, }, + { + MethodName: "Server", + Handler: _Introspection_Server_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "github.com/containerd/containerd/api/services/introspection/v1/introspection.proto", @@ -201,7 +414,7 @@ var _Introspection_serviceDesc = grpc.ServiceDesc{ func (m *Plugin) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -209,98 +422,103 @@ func (m *Plugin) Marshal() (dAtA []byte, err error) { } func (m *Plugin) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Plugin) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Type) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintIntrospection(dAtA, i, uint64(len(m.Type))) - i += copy(dAtA[i:], m.Type) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.ID) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintIntrospection(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - } - if len(m.Requires) > 0 { - for _, s := range m.Requires { - dAtA[i] = 0x1a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.Platforms) > 0 { - for _, msg := range m.Platforms { - dAtA[i] = 0x22 - i++ - i = encodeVarintIntrospection(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.InitErr != nil { + { + size, err := m.InitErr.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintIntrospection(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + if len(m.Capabilities) > 0 { + for iNdEx := len(m.Capabilities) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Capabilities[iNdEx]) + copy(dAtA[i:], m.Capabilities[iNdEx]) + i = encodeVarintIntrospection(dAtA, i, uint64(len(m.Capabilities[iNdEx]))) + i-- + dAtA[i] = 0x32 } } if len(m.Exports) > 0 { - for k, _ := range m.Exports { - dAtA[i] = 0x2a - i++ + for k := range m.Exports { v := m.Exports[k] - mapSize := 1 + len(k) + sovIntrospection(uint64(len(k))) + 1 + len(v) + sovIntrospection(uint64(len(v))) - i = encodeVarintIntrospection(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintIntrospection(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) i = encodeVarintIntrospection(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintIntrospection(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintIntrospection(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a } } - if len(m.Capabilities) > 0 { - for _, s := range m.Capabilities { - dAtA[i] = 0x32 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + if len(m.Platforms) > 0 { + for iNdEx := len(m.Platforms) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Platforms[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintIntrospection(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 } } - if m.InitErr != nil { - dAtA[i] = 0x3a - i++ - i = encodeVarintIntrospection(dAtA, i, uint64(m.InitErr.Size())) - n1, err := m.InitErr.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Requires) > 0 { + for iNdEx := len(m.Requires) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Requires[iNdEx]) + copy(dAtA[i:], m.Requires[iNdEx]) + i = encodeVarintIntrospection(dAtA, i, uint64(len(m.Requires[iNdEx]))) + i-- + dAtA[i] = 0x1a } - i += n1 } - return i, nil + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintIntrospection(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0x12 + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = encodeVarintIntrospection(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *PluginsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -308,32 +526,35 @@ func (m *PluginsRequest) Marshal() (dAtA []byte, err error) { } func (m *PluginsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PluginsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Filters) > 0 { - for _, s := range m.Filters { + for iNdEx := len(m.Filters) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Filters[iNdEx]) + copy(dAtA[i:], m.Filters[iNdEx]) + i = encodeVarintIntrospection(dAtA, i, uint64(len(m.Filters[iNdEx]))) + i-- dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - return i, nil + return len(dAtA) - i, nil } func (m *PluginsResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -341,35 +562,85 @@ func (m *PluginsResponse) Marshal() (dAtA []byte, err error) { } func (m *PluginsResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PluginsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Plugins) > 0 { - for _, msg := range m.Plugins { - dAtA[i] = 0xa - i++ - i = encodeVarintIntrospection(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Plugins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Plugins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintIntrospection(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil +} + +func (m *ServerResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ServerResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ServerResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.UUID) > 0 { + i -= len(m.UUID) + copy(dAtA[i:], m.UUID) + i = encodeVarintIntrospection(dAtA, i, uint64(len(m.UUID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func encodeVarintIntrospection(dAtA []byte, offset int, v uint64) int { + offset -= sovIntrospection(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Plugin) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Type) @@ -410,10 +681,16 @@ func (m *Plugin) Size() (n int) { l = m.InitErr.Size() n += 1 + l + sovIntrospection(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *PluginsRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Filters) > 0 { @@ -422,10 +699,16 @@ func (m *PluginsRequest) Size() (n int) { n += 1 + l + sovIntrospection(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *PluginsResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Plugins) > 0 { @@ -434,19 +717,31 @@ func (m *PluginsResponse) Size() (n int) { n += 1 + l + sovIntrospection(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } -func sovIntrospection(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } +func (m *ServerResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UUID) + if l > 0 { + n += 1 + l + sovIntrospection(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) } return n } + +func sovIntrospection(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} func sozIntrospection(x uint64) (n int) { return sovIntrospection(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } @@ -454,11 +749,16 @@ func (this *Plugin) String() string { if this == nil { return "nil" } + repeatedStringForPlatforms := "[]Platform{" + for _, f := range this.Platforms { + repeatedStringForPlatforms += fmt.Sprintf("%v", f) + "," + } + repeatedStringForPlatforms += "}" keysForExports := make([]string, 0, len(this.Exports)) for k, _ := range this.Exports { keysForExports = append(keysForExports, k) } - sortkeys.Strings(keysForExports) + github_com_gogo_protobuf_sortkeys.Strings(keysForExports) mapStringForExports := "map[string]string{" for _, k := range keysForExports { mapStringForExports += fmt.Sprintf("%v: %v,", k, this.Exports[k]) @@ -468,10 +768,11 @@ func (this *Plugin) String() string { `Type:` + fmt.Sprintf("%v", this.Type) + `,`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Requires:` + fmt.Sprintf("%v", this.Requires) + `,`, - `Platforms:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Platforms), "Platform", "containerd_types.Platform", 1), `&`, ``, 1) + `,`, + `Platforms:` + repeatedStringForPlatforms + `,`, `Exports:` + mapStringForExports + `,`, `Capabilities:` + fmt.Sprintf("%v", this.Capabilities) + `,`, - `InitErr:` + strings.Replace(fmt.Sprintf("%v", this.InitErr), "Status", "google_rpc.Status", 1) + `,`, + `InitErr:` + strings.Replace(fmt.Sprintf("%v", this.InitErr), "Status", "rpc.Status", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -482,6 +783,7 @@ func (this *PluginsRequest) String() string { } s := strings.Join([]string{`&PluginsRequest{`, `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -490,8 +792,25 @@ func (this *PluginsResponse) String() string { if this == nil { return "nil" } + repeatedStringForPlugins := "[]Plugin{" + for _, f := range this.Plugins { + repeatedStringForPlugins += strings.Replace(strings.Replace(f.String(), "Plugin", "Plugin", 1), `&`, ``, 1) + "," + } + repeatedStringForPlugins += "}" s := strings.Join([]string{`&PluginsResponse{`, - `Plugins:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Plugins), "Plugin", "Plugin", 1), `&`, ``, 1) + `,`, + `Plugins:` + repeatedStringForPlugins + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *ServerResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ServerResponse{`, + `UUID:` + fmt.Sprintf("%v", this.UUID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -519,7 +838,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -547,7 +866,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -557,6 +876,9 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthIntrospection + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -576,7 +898,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -586,6 +908,9 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthIntrospection + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -605,7 +930,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -615,6 +940,9 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthIntrospection + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -634,7 +962,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -643,10 +971,13 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIntrospection + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Platforms = append(m.Platforms, containerd_types.Platform{}) + m.Platforms = append(m.Platforms, types.Platform{}) if err := m.Platforms[len(m.Platforms)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -665,7 +996,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -674,6 +1005,9 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIntrospection + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -694,7 +1028,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -711,7 +1045,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -721,6 +1055,9 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthIntrospection + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -737,7 +1074,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -747,6 +1084,9 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthIntrospection + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -758,7 +1098,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthIntrospection } if (iNdEx + skippy) > postIndex { @@ -783,7 +1123,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -793,6 +1133,9 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthIntrospection + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -812,7 +1155,7 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -821,11 +1164,14 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIntrospection + } if postIndex > l { return io.ErrUnexpectedEOF } if m.InitErr == nil { - m.InitErr = &google_rpc.Status{} + m.InitErr = &rpc.Status{} } if err := m.InitErr.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -837,12 +1183,13 @@ func (m *Plugin) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthIntrospection } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -867,7 +1214,7 @@ func (m *PluginsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -895,7 +1242,7 @@ func (m *PluginsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -905,6 +1252,9 @@ func (m *PluginsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthIntrospection + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -916,12 +1266,13 @@ func (m *PluginsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthIntrospection } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -946,7 +1297,7 @@ func (m *PluginsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -974,7 +1325,7 @@ func (m *PluginsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -983,6 +1334,9 @@ func (m *PluginsResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthIntrospection } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthIntrospection + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -997,12 +1351,96 @@ func (m *PluginsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthIntrospection } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ServerResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIntrospection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ServerResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ServerResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UUID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowIntrospection + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthIntrospection + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthIntrospection + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UUID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipIntrospection(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthIntrospection + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1015,6 +1453,7 @@ func (m *PluginsResponse) Unmarshal(dAtA []byte) error { func skipIntrospection(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1046,10 +1485,8 @@ func skipIntrospection(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1066,92 +1503,34 @@ func skipIntrospection(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthIntrospection } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowIntrospection - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipIntrospection(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupIntrospection + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthIntrospection + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthIntrospection = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowIntrospection = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthIntrospection = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowIntrospection = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupIntrospection = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/introspection/v1/introspection.proto", fileDescriptorIntrospection) -} - -var fileDescriptorIntrospection = []byte{ - // 487 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0x4d, 0x6f, 0xd3, 0x40, - 0x10, 0xcd, 0x3a, 0x69, 0xdc, 0x4c, 0xca, 0x87, 0x56, 0x15, 0x58, 0x3e, 0xb8, 0x51, 0xc4, 0x21, - 0x42, 0xb0, 0x56, 0x03, 0x48, 0xb4, 0x48, 0x1c, 0x22, 0x72, 0xa8, 0xd4, 0x43, 0xe5, 0x5e, 0x10, - 0x97, 0xca, 0x71, 0x36, 0x66, 0x85, 0xeb, 0xdd, 0xee, 0xae, 0x2d, 0x72, 0xe3, 0xc6, 0x5f, 0xcb, - 0x91, 0x23, 0xa7, 0x8a, 0xfa, 0x37, 0xf0, 0x03, 0x90, 0xbd, 0x76, 0x9b, 0xdc, 0x12, 0x71, 0x9b, - 0x79, 0x7e, 0x6f, 0xe6, 0xcd, 0x93, 0x17, 0x82, 0x98, 0xe9, 0xaf, 0xd9, 0x8c, 0x44, 0xfc, 0xda, - 0x8f, 0x78, 0xaa, 0x43, 0x96, 0x52, 0x39, 0x5f, 0x2f, 0x43, 0xc1, 0x7c, 0x45, 0x65, 0xce, 0x22, - 0xaa, 0x7c, 0x96, 0x6a, 0xc9, 0x95, 0xa0, 0x91, 0x66, 0x3c, 0xf5, 0xf3, 0xe3, 0x4d, 0x80, 0x08, - 0xc9, 0x35, 0xc7, 0x2f, 0x1e, 0xd4, 0xa4, 0x51, 0x92, 0x4d, 0x62, 0x7e, 0xec, 0x9e, 0x6c, 0xb5, - 0x59, 0x2f, 0x05, 0x55, 0xbe, 0x48, 0x42, 0xbd, 0xe0, 0xf2, 0xda, 0x2c, 0x70, 0x9f, 0xc7, 0x9c, - 0xc7, 0x09, 0xf5, 0xa5, 0x88, 0x7c, 0xa5, 0x43, 0x9d, 0xa9, 0xfa, 0xc3, 0x61, 0xcc, 0x63, 0x5e, - 0x95, 0x7e, 0x59, 0x19, 0x74, 0xf8, 0xd7, 0x82, 0xee, 0x45, 0x92, 0xc5, 0x2c, 0xc5, 0x18, 0x3a, - 0xe5, 0x44, 0x07, 0x0d, 0xd0, 0xa8, 0x17, 0x54, 0x35, 0x7e, 0x06, 0x16, 0x9b, 0x3b, 0x56, 0x89, - 0x4c, 0xba, 0xc5, 0xed, 0x91, 0x75, 0xf6, 0x29, 0xb0, 0xd8, 0x1c, 0xbb, 0xb0, 0x2f, 0xe9, 0x4d, - 0xc6, 0x24, 0x55, 0x4e, 0x7b, 0xd0, 0x1e, 0xf5, 0x82, 0xfb, 0x1e, 0x7f, 0x84, 0x5e, 0xe3, 0x49, - 0x39, 0x9d, 0x41, 0x7b, 0xd4, 0x1f, 0xbb, 0x64, 0xed, 0xec, 0xca, 0x36, 0xb9, 0xa8, 0x29, 0x93, - 0xce, 0xea, 0xf6, 0xa8, 0x15, 0x3c, 0x48, 0xf0, 0x25, 0xd8, 0xf4, 0xbb, 0xe0, 0x52, 0x2b, 0x67, - 0xaf, 0x52, 0x9f, 0x90, 0x6d, 0x42, 0x23, 0xe6, 0x0c, 0x32, 0x35, 0xda, 0x69, 0xaa, 0xe5, 0x32, - 0x68, 0x26, 0xe1, 0x21, 0x1c, 0x44, 0xa1, 0x08, 0x67, 0x2c, 0x61, 0x9a, 0x51, 0xe5, 0x74, 0x2b, - 0xd3, 0x1b, 0x18, 0x7e, 0x0d, 0xfb, 0x2c, 0x65, 0xfa, 0x8a, 0x4a, 0xe9, 0xd8, 0x03, 0x34, 0xea, - 0x8f, 0x31, 0x31, 0x69, 0x12, 0x29, 0x22, 0x72, 0x59, 0xa5, 0x19, 0xd8, 0x25, 0x67, 0x2a, 0xa5, - 0x7b, 0x0a, 0x07, 0xeb, 0xbb, 0xf0, 0x53, 0x68, 0x7f, 0xa3, 0xcb, 0x3a, 0xbe, 0xb2, 0xc4, 0x87, - 0xb0, 0x97, 0x87, 0x49, 0x46, 0x4d, 0x80, 0x81, 0x69, 0x4e, 0xad, 0xf7, 0x68, 0xf8, 0x12, 0x1e, - 0x1b, 0xbb, 0x2a, 0xa0, 0x37, 0x19, 0x55, 0x1a, 0x3b, 0x60, 0x2f, 0x58, 0xa2, 0xa9, 0x54, 0x0e, - 0xaa, 0xbc, 0x35, 0xed, 0xf0, 0x0a, 0x9e, 0xdc, 0x73, 0x95, 0xe0, 0xa9, 0xa2, 0xf8, 0x1c, 0x6c, - 0x61, 0xa0, 0x8a, 0xdc, 0x1f, 0xbf, 0xda, 0x25, 0xa2, 0x3a, 0xf2, 0x66, 0xc4, 0xf8, 0x27, 0x82, - 0x47, 0x67, 0xeb, 0x54, 0x9c, 0x83, 0x5d, 0xaf, 0xc4, 0x6f, 0x77, 0x99, 0xdc, 0x5c, 0xe3, 0xbe, - 0xdb, 0x51, 0x65, 0xee, 0x9a, 0x2c, 0x56, 0x77, 0x5e, 0xeb, 0xf7, 0x9d, 0xd7, 0xfa, 0x51, 0x78, - 0x68, 0x55, 0x78, 0xe8, 0x57, 0xe1, 0xa1, 0x3f, 0x85, 0x87, 0xbe, 0x9c, 0xff, 0xdf, 0x5b, 0xfc, - 0xb0, 0x01, 0x7c, 0xb6, 0x66, 0xdd, 0xea, 0xf7, 0x7f, 0xf3, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xe6, - 0x72, 0xde, 0x35, 0xe4, 0x03, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.proto b/vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.proto index 95e804b9b7e41..65a8bc21b6cd1 100644 --- a/vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.proto +++ b/vendor/github.com/containerd/containerd/api/services/introspection/v1/introspection.proto @@ -1,9 +1,26 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.introspection.v1; import "github.com/containerd/containerd/api/types/platform.proto"; import "google/rpc/status.proto"; +import "google/protobuf/empty.proto"; import weak "gogoproto/gogo.proto"; option go_package = "github.com/containerd/containerd/api/services/introspection/v1;introspection"; @@ -14,6 +31,8 @@ service Introspection { // Clients can use this to detect features and capabilities when using // containerd. rpc Plugins(PluginsRequest) returns (PluginsResponse); + // Server returns information about the containerd server + rpc Server(google.protobuf.Empty) returns (ServerResponse); } message Plugin { @@ -79,3 +98,7 @@ message PluginsRequest { message PluginsResponse { repeated Plugin plugins = 1 [(gogoproto.nullable) = false]; } + +message ServerResponse { + string uuid = 1 [(gogoproto.customname) = "UUID"]; +} diff --git a/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.pb.go b/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.pb.go index 1222c1aec0f8a..5e7cab71f1312 100644 --- a/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.pb.go @@ -1,42 +1,25 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/leases/v1/leases.proto -/* - Package leases is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/leases/v1/leases.proto - - It has these top-level messages: - Lease - CreateRequest - CreateResponse - DeleteRequest - ListRequest - ListResponse -*/ package leases -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import google_protobuf1 "github.com/gogo/protobuf/types" -import _ "github.com/gogo/protobuf/types" - -import time "time" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + context "context" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -48,37 +31,130 @@ var _ = time.Kitchen // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // Lease is an object which retains resources while it exists. type Lease struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - CreatedAt time.Time `protobuf:"bytes,2,opt,name=created_at,json=createdAt,stdtime" json:"created_at"` - Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + CreatedAt time.Time `protobuf:"bytes,2,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Lease) Reset() { *m = Lease{} } +func (*Lease) ProtoMessage() {} +func (*Lease) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{0} +} +func (m *Lease) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Lease) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Lease.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Lease) XXX_Merge(src proto.Message) { + xxx_messageInfo_Lease.Merge(m, src) +} +func (m *Lease) XXX_Size() int { + return m.Size() +} +func (m *Lease) XXX_DiscardUnknown() { + xxx_messageInfo_Lease.DiscardUnknown(m) } -func (m *Lease) Reset() { *m = Lease{} } -func (*Lease) ProtoMessage() {} -func (*Lease) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{0} } +var xxx_messageInfo_Lease proto.InternalMessageInfo type CreateRequest struct { // ID is used to identity the lease, when the id is not set the service // generates a random identifier for the lease. - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateRequest) Reset() { *m = CreateRequest{} } +func (*CreateRequest) ProtoMessage() {} +func (*CreateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{1} +} +func (m *CreateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateRequest.Merge(m, src) +} +func (m *CreateRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateRequest.DiscardUnknown(m) } -func (m *CreateRequest) Reset() { *m = CreateRequest{} } -func (*CreateRequest) ProtoMessage() {} -func (*CreateRequest) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{1} } +var xxx_messageInfo_CreateRequest proto.InternalMessageInfo type CreateResponse struct { - Lease *Lease `protobuf:"bytes,1,opt,name=lease" json:"lease,omitempty"` + Lease *Lease `protobuf:"bytes,1,opt,name=lease,proto3" json:"lease,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateResponse) Reset() { *m = CreateResponse{} } +func (*CreateResponse) ProtoMessage() {} +func (*CreateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{2} +} +func (m *CreateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateResponse.Merge(m, src) +} +func (m *CreateResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateResponse.DiscardUnknown(m) } -func (m *CreateResponse) Reset() { *m = CreateResponse{} } -func (*CreateResponse) ProtoMessage() {} -func (*CreateResponse) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{2} } +var xxx_messageInfo_CreateResponse proto.InternalMessageInfo type DeleteRequest struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` @@ -86,36 +162,386 @@ type DeleteRequest struct { // synchronously before returning to the caller // // Default is false - Sync bool `protobuf:"varint,2,opt,name=sync,proto3" json:"sync,omitempty"` + Sync bool `protobuf:"varint,2,opt,name=sync,proto3" json:"sync,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteRequest) Reset() { *m = DeleteRequest{} } +func (*DeleteRequest) ProtoMessage() {} +func (*DeleteRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{3} +} +func (m *DeleteRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteRequest.Merge(m, src) +} +func (m *DeleteRequest) XXX_Size() int { + return m.Size() +} +func (m *DeleteRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteRequest.DiscardUnknown(m) } -func (m *DeleteRequest) Reset() { *m = DeleteRequest{} } -func (*DeleteRequest) ProtoMessage() {} -func (*DeleteRequest) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{3} } +var xxx_messageInfo_DeleteRequest proto.InternalMessageInfo type ListRequest struct { - Filters []string `protobuf:"bytes,1,rep,name=filters" json:"filters,omitempty"` + Filters []string `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListRequest) Reset() { *m = ListRequest{} } +func (*ListRequest) ProtoMessage() {} +func (*ListRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{4} +} +func (m *ListRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListRequest.Merge(m, src) +} +func (m *ListRequest) XXX_Size() int { + return m.Size() +} +func (m *ListRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListRequest.DiscardUnknown(m) } -func (m *ListRequest) Reset() { *m = ListRequest{} } -func (*ListRequest) ProtoMessage() {} -func (*ListRequest) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{4} } +var xxx_messageInfo_ListRequest proto.InternalMessageInfo type ListResponse struct { - Leases []*Lease `protobuf:"bytes,1,rep,name=leases" json:"leases,omitempty"` + Leases []*Lease `protobuf:"bytes,1,rep,name=leases,proto3" json:"leases,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListResponse) Reset() { *m = ListResponse{} } +func (*ListResponse) ProtoMessage() {} +func (*ListResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{5} +} +func (m *ListResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListResponse.Merge(m, src) +} +func (m *ListResponse) XXX_Size() int { + return m.Size() +} +func (m *ListResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListResponse proto.InternalMessageInfo + +type Resource struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // For snapshotter resource, there are many snapshotter types here, like + // overlayfs, devmapper etc. The type will be formatted with type, + // like "snapshotter/overlayfs". + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Resource) Reset() { *m = Resource{} } +func (*Resource) ProtoMessage() {} +func (*Resource) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{6} +} +func (m *Resource) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Resource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Resource.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Resource) XXX_Merge(src proto.Message) { + xxx_messageInfo_Resource.Merge(m, src) +} +func (m *Resource) XXX_Size() int { + return m.Size() +} +func (m *Resource) XXX_DiscardUnknown() { + xxx_messageInfo_Resource.DiscardUnknown(m) +} + +var xxx_messageInfo_Resource proto.InternalMessageInfo + +type AddResourceRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Resource Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AddResourceRequest) Reset() { *m = AddResourceRequest{} } +func (*AddResourceRequest) ProtoMessage() {} +func (*AddResourceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{7} +} +func (m *AddResourceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AddResourceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AddResourceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AddResourceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddResourceRequest.Merge(m, src) +} +func (m *AddResourceRequest) XXX_Size() int { + return m.Size() +} +func (m *AddResourceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AddResourceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AddResourceRequest proto.InternalMessageInfo + +type DeleteResourceRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Resource Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteResourceRequest) Reset() { *m = DeleteResourceRequest{} } +func (*DeleteResourceRequest) ProtoMessage() {} +func (*DeleteResourceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{8} +} +func (m *DeleteResourceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteResourceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteResourceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteResourceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteResourceRequest.Merge(m, src) +} +func (m *DeleteResourceRequest) XXX_Size() int { + return m.Size() +} +func (m *DeleteResourceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteResourceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DeleteResourceRequest proto.InternalMessageInfo + +type ListResourcesRequest struct { + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListResourcesRequest) Reset() { *m = ListResourcesRequest{} } +func (*ListResourcesRequest) ProtoMessage() {} +func (*ListResourcesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{9} +} +func (m *ListResourcesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListResourcesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListResourcesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListResourcesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListResourcesRequest.Merge(m, src) +} +func (m *ListResourcesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListResourcesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListResourcesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListResourcesRequest proto.InternalMessageInfo + +type ListResourcesResponse struct { + Resources []Resource `protobuf:"bytes,1,rep,name=resources,proto3" json:"resources"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListResourcesResponse) Reset() { *m = ListResourcesResponse{} } +func (*ListResourcesResponse) ProtoMessage() {} +func (*ListResourcesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_fefd70dfe8d93cbf, []int{10} +} +func (m *ListResourcesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListResourcesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListResourcesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListResourcesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListResourcesResponse.Merge(m, src) +} +func (m *ListResourcesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListResourcesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListResourcesResponse.DiscardUnknown(m) } -func (m *ListResponse) Reset() { *m = ListResponse{} } -func (*ListResponse) ProtoMessage() {} -func (*ListResponse) Descriptor() ([]byte, []int) { return fileDescriptorLeases, []int{5} } +var xxx_messageInfo_ListResourcesResponse proto.InternalMessageInfo func init() { proto.RegisterType((*Lease)(nil), "containerd.services.leases.v1.Lease") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.leases.v1.Lease.LabelsEntry") proto.RegisterType((*CreateRequest)(nil), "containerd.services.leases.v1.CreateRequest") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.leases.v1.CreateRequest.LabelsEntry") proto.RegisterType((*CreateResponse)(nil), "containerd.services.leases.v1.CreateResponse") proto.RegisterType((*DeleteRequest)(nil), "containerd.services.leases.v1.DeleteRequest") proto.RegisterType((*ListRequest)(nil), "containerd.services.leases.v1.ListRequest") proto.RegisterType((*ListResponse)(nil), "containerd.services.leases.v1.ListResponse") + proto.RegisterType((*Resource)(nil), "containerd.services.leases.v1.Resource") + proto.RegisterType((*AddResourceRequest)(nil), "containerd.services.leases.v1.AddResourceRequest") + proto.RegisterType((*DeleteResourceRequest)(nil), "containerd.services.leases.v1.DeleteResourceRequest") + proto.RegisterType((*ListResourcesRequest)(nil), "containerd.services.leases.v1.ListResourcesRequest") + proto.RegisterType((*ListResourcesResponse)(nil), "containerd.services.leases.v1.ListResourcesResponse") +} + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/leases/v1/leases.proto", fileDescriptor_fefd70dfe8d93cbf) +} + +var fileDescriptor_fefd70dfe8d93cbf = []byte{ + // 644 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0xce, 0x26, 0xa9, 0x49, 0x26, 0xb4, 0x42, 0xab, 0xb6, 0x8a, 0x8c, 0x48, 0x22, 0x0b, 0xa9, + 0x11, 0x3f, 0x36, 0x4d, 0x2b, 0x54, 0x5a, 0x84, 0xd4, 0xb4, 0x95, 0xa8, 0x88, 0x10, 0xb2, 0x38, + 0x54, 0x1c, 0xa8, 0x1c, 0x7b, 0x1b, 0x2c, 0x9c, 0xd8, 0x78, 0x37, 0x41, 0xe9, 0x89, 0x47, 0xe0, + 0x61, 0x78, 0x88, 0x1e, 0x39, 0x21, 0x4e, 0x85, 0xe6, 0xc6, 0x5b, 0x20, 0xef, 0x0f, 0x6d, 0x5a, + 0xb5, 0x76, 0x11, 0xe2, 0x36, 0x1b, 0x7f, 0xdf, 0xcc, 0x37, 0x33, 0xdf, 0x6e, 0x60, 0xbb, 0xe7, + 0xb3, 0x77, 0xc3, 0xae, 0xe9, 0x86, 0x7d, 0xcb, 0x0d, 0x07, 0xcc, 0xf1, 0x07, 0x24, 0xf6, 0xce, + 0x86, 0x4e, 0xe4, 0x5b, 0x94, 0xc4, 0x23, 0xdf, 0x25, 0xd4, 0x0a, 0x88, 0x43, 0x09, 0xb5, 0x46, + 0xcb, 0x32, 0x32, 0xa3, 0x38, 0x64, 0x21, 0xbe, 0x73, 0x8a, 0x37, 0x15, 0xd6, 0x94, 0x88, 0xd1, + 0xb2, 0x3e, 0xdf, 0x0b, 0x7b, 0x21, 0x47, 0x5a, 0x49, 0x24, 0x48, 0xfa, 0xed, 0x5e, 0x18, 0xf6, + 0x02, 0x62, 0xf1, 0x53, 0x77, 0x78, 0x60, 0x91, 0x7e, 0xc4, 0xc6, 0xf2, 0x63, 0xfd, 0xfc, 0x47, + 0xe6, 0xf7, 0x09, 0x65, 0x4e, 0x3f, 0x12, 0x00, 0xe3, 0x17, 0x82, 0x99, 0x4e, 0x52, 0x01, 0x2f, + 0x42, 0xde, 0xf7, 0xaa, 0xa8, 0x81, 0x9a, 0xe5, 0xb6, 0x36, 0x39, 0xae, 0xe7, 0x77, 0xb7, 0xed, + 0xbc, 0xef, 0xe1, 0x2d, 0x00, 0x37, 0x26, 0x0e, 0x23, 0xde, 0xbe, 0xc3, 0xaa, 0xf9, 0x06, 0x6a, + 0x56, 0x5a, 0xba, 0x29, 0xf2, 0x9a, 0x2a, 0xaf, 0xf9, 0x5a, 0xe5, 0x6d, 0x97, 0x8e, 0x8e, 0xeb, + 0xb9, 0xcf, 0x3f, 0xea, 0xc8, 0x2e, 0x4b, 0xde, 0x26, 0xc3, 0xcf, 0x41, 0x0b, 0x9c, 0x2e, 0x09, + 0x68, 0xb5, 0xd0, 0x28, 0x34, 0x2b, 0xad, 0x47, 0xe6, 0x95, 0xad, 0x9a, 0x5c, 0x92, 0xd9, 0xe1, + 0x94, 0x9d, 0x01, 0x8b, 0xc7, 0xb6, 0xe4, 0xeb, 0x4f, 0xa0, 0x72, 0xe6, 0x67, 0x7c, 0x0b, 0x0a, + 0xef, 0xc9, 0x58, 0xc8, 0xb6, 0x93, 0x10, 0xcf, 0xc3, 0xcc, 0xc8, 0x09, 0x86, 0x84, 0x4b, 0x2d, + 0xdb, 0xe2, 0xb0, 0x9e, 0x5f, 0x43, 0xc6, 0x17, 0x04, 0xb3, 0x5b, 0x5c, 0x92, 0x4d, 0x3e, 0x0c, + 0x09, 0x65, 0x97, 0xf6, 0xfc, 0xea, 0x9c, 0xdc, 0xb5, 0x14, 0xb9, 0x53, 0x59, 0xff, 0xb5, 0xec, + 0x0e, 0xcc, 0xa9, 0xfc, 0x34, 0x0a, 0x07, 0x94, 0xe0, 0x75, 0x98, 0xe1, 0xb5, 0x39, 0xbf, 0xd2, + 0xba, 0x9b, 0x65, 0x98, 0xb6, 0xa0, 0x18, 0x1b, 0x30, 0xbb, 0x4d, 0x02, 0x92, 0x3e, 0x03, 0x0c, + 0x45, 0x3a, 0x1e, 0xb8, 0x5c, 0x4f, 0xc9, 0xe6, 0xb1, 0xb1, 0x04, 0x95, 0x8e, 0x4f, 0x99, 0xa2, + 0x56, 0xe1, 0xc6, 0x81, 0x1f, 0x30, 0x12, 0xd3, 0x2a, 0x6a, 0x14, 0x9a, 0x65, 0x5b, 0x1d, 0x8d, + 0x0e, 0xdc, 0x14, 0x40, 0xa9, 0xf8, 0x29, 0x68, 0x42, 0x0f, 0x07, 0x66, 0x95, 0x2c, 0x39, 0xc6, + 0x63, 0x28, 0xd9, 0x84, 0x86, 0xc3, 0xd8, 0x25, 0x57, 0xc9, 0x65, 0xe3, 0x48, 0x8d, 0x8f, 0xc7, + 0xc6, 0x47, 0xc0, 0x9b, 0x9e, 0xa7, 0xa8, 0x69, 0x0d, 0xef, 0x42, 0x29, 0x96, 0x50, 0x69, 0xf3, + 0xa5, 0x14, 0x95, 0x2a, 0x73, 0xbb, 0x98, 0x78, 0xde, 0xfe, 0x43, 0x37, 0x0e, 0x61, 0x41, 0x0d, + 0xf9, 0xbf, 0xd7, 0x36, 0x61, 0x5e, 0x8e, 0x9e, 0x9f, 0x69, 0x4a, 0x69, 0xc3, 0x83, 0x85, 0x73, + 0x78, 0xb9, 0xb3, 0x17, 0x50, 0x56, 0x49, 0xd5, 0xda, 0xae, 0x29, 0xea, 0x94, 0xdf, 0xfa, 0x56, + 0x04, 0x8d, 0x2f, 0x95, 0x62, 0x02, 0x9a, 0xf0, 0x33, 0x7e, 0x70, 0x9d, 0x6b, 0xa5, 0x3f, 0xcc, + 0x88, 0x96, 0xf2, 0x5f, 0x82, 0x26, 0x76, 0x90, 0x5a, 0x66, 0xea, 0x3e, 0xe8, 0x8b, 0x17, 0xde, + 0xb6, 0x9d, 0xe4, 0x41, 0xc5, 0xfb, 0x50, 0x4c, 0xe6, 0x84, 0xef, 0xa5, 0x59, 0xf7, 0xf4, 0x82, + 0xe8, 0xf7, 0x33, 0x61, 0xa5, 0xe0, 0x3d, 0xa8, 0x9c, 0x71, 0x2b, 0x5e, 0x4e, 0xe1, 0x5e, 0x74, + 0xf6, 0xa5, 0xd2, 0xdf, 0xc2, 0xdc, 0xb4, 0x1d, 0xf1, 0x6a, 0xc6, 0x91, 0x64, 0xcb, 0x7f, 0x08, + 0xb3, 0x53, 0x16, 0xc2, 0x2b, 0xd9, 0xfa, 0x9e, 0x32, 0xa8, 0xbe, 0x7a, 0x3d, 0x92, 0x98, 0x5a, + 0x7b, 0xef, 0xe8, 0xa4, 0x96, 0xfb, 0x7e, 0x52, 0xcb, 0x7d, 0x9a, 0xd4, 0xd0, 0xd1, 0xa4, 0x86, + 0xbe, 0x4e, 0x6a, 0xe8, 0xe7, 0xa4, 0x86, 0xde, 0x3c, 0xfb, 0xcb, 0xff, 0xe4, 0x0d, 0x11, 0xed, + 0xe5, 0xba, 0x1a, 0xef, 0x73, 0xe5, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0d, 0xfe, 0x39, 0x67, + 0xde, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -126,8 +552,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Leases service - +// LeasesClient is the client API for Leases service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type LeasesClient interface { // Create creates a new lease for managing changes to metadata. A lease // can be used to protect objects from being removed. @@ -135,10 +562,16 @@ type LeasesClient interface { // Delete deletes the lease and makes any unreferenced objects created // during the lease eligible for garbage collection if not referenced // or retained by other resources during the lease. - Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*types.Empty, error) // List lists all active leases, returning the full list of // leases and optionally including the referenced resources. List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) + // AddResource references the resource by the provided lease. + AddResource(ctx context.Context, in *AddResourceRequest, opts ...grpc.CallOption) (*types.Empty, error) + // DeleteResource dereferences the resource by the provided lease. + DeleteResource(ctx context.Context, in *DeleteResourceRequest, opts ...grpc.CallOption) (*types.Empty, error) + // ListResources lists all the resources referenced by the lease. + ListResources(ctx context.Context, in *ListResourcesRequest, opts ...grpc.CallOption) (*ListResourcesResponse, error) } type leasesClient struct { @@ -151,16 +584,16 @@ func NewLeasesClient(cc *grpc.ClientConn) LeasesClient { func (c *leasesClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) { out := new(CreateResponse) - err := grpc.Invoke(ctx, "/containerd.services.leases.v1.Leases/Create", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.leases.v1.Leases/Create", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *leasesClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { - out := new(google_protobuf1.Empty) - err := grpc.Invoke(ctx, "/containerd.services.leases.v1.Leases/Delete", in, out, c.cc, opts...) +func (c *leasesClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.leases.v1.Leases/Delete", in, out, opts...) if err != nil { return nil, err } @@ -169,15 +602,41 @@ func (c *leasesClient) Delete(ctx context.Context, in *DeleteRequest, opts ...gr func (c *leasesClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) { out := new(ListResponse) - err := grpc.Invoke(ctx, "/containerd.services.leases.v1.Leases/List", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.leases.v1.Leases/List", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *leasesClient) AddResource(ctx context.Context, in *AddResourceRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.leases.v1.Leases/AddResource", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *leasesClient) DeleteResource(ctx context.Context, in *DeleteResourceRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.leases.v1.Leases/DeleteResource", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Leases service +func (c *leasesClient) ListResources(ctx context.Context, in *ListResourcesRequest, opts ...grpc.CallOption) (*ListResourcesResponse, error) { + out := new(ListResourcesResponse) + err := c.cc.Invoke(ctx, "/containerd.services.leases.v1.Leases/ListResources", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} +// LeasesServer is the server API for Leases service. type LeasesServer interface { // Create creates a new lease for managing changes to metadata. A lease // can be used to protect objects from being removed. @@ -185,10 +644,39 @@ type LeasesServer interface { // Delete deletes the lease and makes any unreferenced objects created // during the lease eligible for garbage collection if not referenced // or retained by other resources during the lease. - Delete(context.Context, *DeleteRequest) (*google_protobuf1.Empty, error) + Delete(context.Context, *DeleteRequest) (*types.Empty, error) // List lists all active leases, returning the full list of // leases and optionally including the referenced resources. List(context.Context, *ListRequest) (*ListResponse, error) + // AddResource references the resource by the provided lease. + AddResource(context.Context, *AddResourceRequest) (*types.Empty, error) + // DeleteResource dereferences the resource by the provided lease. + DeleteResource(context.Context, *DeleteResourceRequest) (*types.Empty, error) + // ListResources lists all the resources referenced by the lease. + ListResources(context.Context, *ListResourcesRequest) (*ListResourcesResponse, error) +} + +// UnimplementedLeasesServer can be embedded to have forward compatible implementations. +type UnimplementedLeasesServer struct { +} + +func (*UnimplementedLeasesServer) Create(ctx context.Context, req *CreateRequest) (*CreateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") +} +func (*UnimplementedLeasesServer) Delete(ctx context.Context, req *DeleteRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") +} +func (*UnimplementedLeasesServer) List(ctx context.Context, req *ListRequest) (*ListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (*UnimplementedLeasesServer) AddResource(ctx context.Context, req *AddResourceRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddResource not implemented") +} +func (*UnimplementedLeasesServer) DeleteResource(ctx context.Context, req *DeleteResourceRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteResource not implemented") +} +func (*UnimplementedLeasesServer) ListResources(ctx context.Context, req *ListResourcesRequest) (*ListResourcesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListResources not implemented") } func RegisterLeasesServer(s *grpc.Server, srv LeasesServer) { @@ -249,6 +737,60 @@ func _Leases_List_Handler(srv interface{}, ctx context.Context, dec func(interfa return interceptor(ctx, in, info, handler) } +func _Leases_AddResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LeasesServer).AddResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/containerd.services.leases.v1.Leases/AddResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LeasesServer).AddResource(ctx, req.(*AddResourceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Leases_DeleteResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LeasesServer).DeleteResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/containerd.services.leases.v1.Leases/DeleteResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LeasesServer).DeleteResource(ctx, req.(*DeleteResourceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Leases_ListResources_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListResourcesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LeasesServer).ListResources(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/containerd.services.leases.v1.Leases/ListResources", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LeasesServer).ListResources(ctx, req.(*ListResourcesRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Leases_serviceDesc = grpc.ServiceDesc{ ServiceName: "containerd.services.leases.v1.Leases", HandlerType: (*LeasesServer)(nil), @@ -265,6 +807,18 @@ var _Leases_serviceDesc = grpc.ServiceDesc{ MethodName: "List", Handler: _Leases_List_Handler, }, + { + MethodName: "AddResource", + Handler: _Leases_AddResource_Handler, + }, + { + MethodName: "DeleteResource", + Handler: _Leases_DeleteResource_Handler, + }, + { + MethodName: "ListResources", + Handler: _Leases_ListResources_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "github.com/containerd/containerd/api/services/leases/v1/leases.proto", @@ -273,7 +827,7 @@ var _Leases_serviceDesc = grpc.ServiceDesc{ func (m *Lease) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -281,48 +835,60 @@ func (m *Lease) Marshal() (dAtA []byte, err error) { } func (m *Lease) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Lease) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - dAtA[i] = 0x12 - i++ - i = encodeVarintLeases(dAtA, i, uint64(types.SizeOfStdTime(m.CreatedAt))) - n1, err := types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x1a - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovLeases(uint64(len(k))) + 1 + len(v) + sovLeases(uint64(len(v))) - i = encodeVarintLeases(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintLeases(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) i = encodeVarintLeases(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintLeases(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintLeases(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a } } - return i, nil + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintLeases(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *CreateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -330,40 +896,52 @@ func (m *CreateRequest) Marshal() (dAtA []byte, err error) { } func (m *CreateRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x1a - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovLeases(uint64(len(k))) + 1 + len(v) + sovLeases(uint64(len(v))) - i = encodeVarintLeases(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintLeases(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) i = encodeVarintLeases(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintLeases(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintLeases(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a } } - return i, nil + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *CreateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -371,27 +949,38 @@ func (m *CreateResponse) Marshal() (dAtA []byte, err error) { } func (m *CreateResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Lease != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintLeases(dAtA, i, uint64(m.Lease.Size())) - n2, err := m.Lease.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Lease.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLeases(dAtA, i, uint64(size)) } - i += n2 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *DeleteRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -399,33 +988,43 @@ func (m *DeleteRequest) Marshal() (dAtA []byte, err error) { } func (m *DeleteRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Sync { - dAtA[i] = 0x10 - i++ + i-- if m.Sync { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x10 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -433,32 +1032,35 @@ func (m *ListRequest) Marshal() (dAtA []byte, err error) { } func (m *ListRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Filters) > 0 { - for _, s := range m.Filters { + for iNdEx := len(m.Filters) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Filters[iNdEx]) + copy(dAtA[i:], m.Filters[iNdEx]) + i = encodeVarintLeases(dAtA, i, uint64(len(m.Filters[iNdEx]))) + i-- dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - return i, nil + return len(dAtA) - i, nil } func (m *ListResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -466,42 +1068,262 @@ func (m *ListResponse) Marshal() (dAtA []byte, err error) { } func (m *ListResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Leases) > 0 { - for _, msg := range m.Leases { + for iNdEx := len(m.Leases) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Leases[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLeases(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0xa - i++ - i = encodeVarintLeases(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + } + return len(dAtA) - i, nil +} + +func (m *Resource) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Resource) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Resource) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = encodeVarintLeases(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x12 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AddResourceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AddResourceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AddResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLeases(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *DeleteResourceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DeleteResourceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLeases(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ListResourcesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListResourcesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListResourcesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ListResourcesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListResourcesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListResourcesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Resources) > 0 { + for iNdEx := len(m.Resources) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Resources[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLeases(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func encodeVarintLeases(dAtA []byte, offset int, v uint64) int { + offset -= sovLeases(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Lease) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) if l > 0 { n += 1 + l + sovLeases(uint64(l)) } - l = types.SizeOfStdTime(m.CreatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt) n += 1 + l + sovLeases(uint64(l)) if len(m.Labels) > 0 { for k, v := range m.Labels { @@ -511,10 +1333,16 @@ func (m *Lease) Size() (n int) { n += mapEntrySize + 1 + sovLeases(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -529,20 +1357,32 @@ func (m *CreateRequest) Size() (n int) { n += mapEntrySize + 1 + sovLeases(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Lease != nil { l = m.Lease.Size() n += 1 + l + sovLeases(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DeleteRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -552,10 +1392,16 @@ func (m *DeleteRequest) Size() (n int) { if m.Sync { n += 2 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Filters) > 0 { @@ -564,10 +1410,16 @@ func (m *ListRequest) Size() (n int) { n += 1 + l + sovLeases(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Leases) > 0 { @@ -576,19 +1428,105 @@ func (m *ListResponse) Size() (n int) { n += 1 + l + sovLeases(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } -func sovLeases(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break +func (m *Resource) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovLeases(uint64(l)) + } + l = len(m.Type) + if l > 0 { + n += 1 + l + sovLeases(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *AddResourceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovLeases(uint64(l)) + } + l = m.Resource.Size() + n += 1 + l + sovLeases(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *DeleteResourceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovLeases(uint64(l)) + } + l = m.Resource.Size() + n += 1 + l + sovLeases(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *ListResourcesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovLeases(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *ListResourcesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Resources) > 0 { + for _, e := range m.Resources { + l = e.Size() + n += 1 + l + sovLeases(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } + +func sovLeases(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} func sozLeases(x uint64) (n int) { return sovLeases(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } @@ -600,7 +1538,7 @@ func (this *Lease) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -608,8 +1546,9 @@ func (this *Lease) String() string { mapStringForLabels += "}" s := strings.Join([]string{`&Lease{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, - `CreatedAt:` + strings.Replace(strings.Replace(this.CreatedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `CreatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.CreatedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -622,7 +1561,7 @@ func (this *CreateRequest) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -631,6 +1570,7 @@ func (this *CreateRequest) String() string { s := strings.Join([]string{`&CreateRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -640,7 +1580,8 @@ func (this *CreateResponse) String() string { return "nil" } s := strings.Join([]string{`&CreateResponse{`, - `Lease:` + strings.Replace(fmt.Sprintf("%v", this.Lease), "Lease", "Lease", 1) + `,`, + `Lease:` + strings.Replace(this.Lease.String(), "Lease", "Lease", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -652,6 +1593,7 @@ func (this *DeleteRequest) String() string { s := strings.Join([]string{`&DeleteRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Sync:` + fmt.Sprintf("%v", this.Sync) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -662,6 +1604,7 @@ func (this *ListRequest) String() string { } s := strings.Join([]string{`&ListRequest{`, `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -670,41 +1613,110 @@ func (this *ListResponse) String() string { if this == nil { return "nil" } + repeatedStringForLeases := "[]*Lease{" + for _, f := range this.Leases { + repeatedStringForLeases += strings.Replace(f.String(), "Lease", "Lease", 1) + "," + } + repeatedStringForLeases += "}" s := strings.Join([]string{`&ListResponse{`, - `Leases:` + strings.Replace(fmt.Sprintf("%v", this.Leases), "Lease", "Lease", 1) + `,`, + `Leases:` + repeatedStringForLeases + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s } -func valueToStringLeases(v interface{}) string { - rv := reflect.ValueOf(v) - if rv.IsNil() { +func (this *Resource) String() string { + if this == nil { return "nil" } - pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("*%v", pv) + s := strings.Join([]string{`&Resource{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Type:` + fmt.Sprintf("%v", this.Type) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s } -func (m *Lease) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLeases - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) +func (this *AddResourceRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&AddResourceRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Resource:` + strings.Replace(strings.Replace(this.Resource.String(), "Resource", "Resource", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *DeleteResourceRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&DeleteResourceRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `Resource:` + strings.Replace(strings.Replace(this.Resource.String(), "Resource", "Resource", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *ListResourcesRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListResourcesRequest{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *ListResourcesResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForResources := "[]Resource{" + for _, f := range this.Resources { + repeatedStringForResources += strings.Replace(strings.Replace(f.String(), "Resource", "Resource", 1), `&`, ``, 1) + "," + } + repeatedStringForResources += "}" + s := strings.Join([]string{`&ListResourcesResponse{`, + `Resources:` + repeatedStringForResources + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringLeases(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Lease) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Lease: wiretype end group for non-group") @@ -727,7 +1739,7 @@ func (m *Lease) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -737,6 +1749,9 @@ func (m *Lease) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLeases } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLeases + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -756,7 +1771,7 @@ func (m *Lease) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -765,10 +1780,13 @@ func (m *Lease) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLeases } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLeases + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -786,7 +1804,7 @@ func (m *Lease) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -795,6 +1813,9 @@ func (m *Lease) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLeases } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLeases + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -815,7 +1836,7 @@ func (m *Lease) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -832,7 +1853,7 @@ func (m *Lease) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -842,6 +1863,9 @@ func (m *Lease) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLeases } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthLeases + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -858,7 +1882,7 @@ func (m *Lease) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -868,6 +1892,9 @@ func (m *Lease) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLeases } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthLeases + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -879,7 +1906,7 @@ func (m *Lease) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLeases } if (iNdEx + skippy) > postIndex { @@ -888,7 +1915,575 @@ func (m *Lease) Unmarshal(dAtA []byte) error { iNdEx += skippy } } - m.Labels[mapkey] = mapvalue + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CreateRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CreateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLeases + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLeases + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthLeases + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthLeases + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthLeases + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthLeases + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CreateResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CreateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLeases + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Lease == nil { + m.Lease = &Lease{} + } + if err := m.Lease.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *DeleteRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeleteRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLeases + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sync", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Sync = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLeases + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filters = append(m.Filters, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLeases(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLeases + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Leases", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLeases + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Leases = append(m.Leases, &Lease{}) + if err := m.Leases[len(m.Leases)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -896,12 +2491,13 @@ func (m *Lease) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLeases } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -911,7 +2507,7 @@ func (m *Lease) Unmarshal(dAtA []byte) error { } return nil } -func (m *CreateRequest) Unmarshal(dAtA []byte) error { +func (m *Resource) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -926,7 +2522,7 @@ func (m *CreateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -934,10 +2530,10 @@ func (m *CreateRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateRequest: wiretype end group for non-group") + return fmt.Errorf("proto: Resource: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: Resource: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -954,7 +2550,7 @@ func (m *CreateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -964,16 +2560,19 @@ func (m *CreateRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLeases } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLeases + } if postIndex > l { return io.ErrUnexpectedEOF } m.ID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLeases @@ -983,109 +2582,23 @@ func (m *CreateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthLeases } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Labels == nil { - m.Labels = make(map[string]string) - } - var mapkey string - var mapvalue string - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLeases - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLeases - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthLeases - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var stringLenmapvalue uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowLeases - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthLeases - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue - } else { - iNdEx = entryPreIndex - skippy, err := skipLeases(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthLeases - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.Labels[mapkey] = mapvalue + m.Type = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -1093,12 +2606,13 @@ func (m *CreateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLeases } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1108,7 +2622,7 @@ func (m *CreateRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *CreateResponse) Unmarshal(dAtA []byte) error { +func (m *AddResourceRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1123,7 +2637,7 @@ func (m *CreateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1131,15 +2645,47 @@ func (m *CreateResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateResponse: wiretype end group for non-group") + return fmt.Errorf("proto: AddResourceRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: AddResourceRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Lease", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLeases + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLeases + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1151,7 +2697,7 @@ func (m *CreateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1160,13 +2706,13 @@ func (m *CreateResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLeases } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLeases + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Lease == nil { - m.Lease = &Lease{} - } - if err := m.Lease.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Resource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1176,12 +2722,13 @@ func (m *CreateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLeases } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1191,7 +2738,7 @@ func (m *CreateResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *DeleteRequest) Unmarshal(dAtA []byte) error { +func (m *DeleteResourceRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1206,7 +2753,7 @@ func (m *DeleteRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1214,10 +2761,10 @@ func (m *DeleteRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: DeleteRequest: wiretype end group for non-group") + return fmt.Errorf("proto: DeleteResourceRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: DeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: DeleteResourceRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -1234,7 +2781,7 @@ func (m *DeleteRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1244,16 +2791,19 @@ func (m *DeleteRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLeases } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLeases + } if postIndex > l { return io.ErrUnexpectedEOF } m.ID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Sync", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) } - var v int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLeases @@ -1263,24 +2813,38 @@ func (m *DeleteRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - m.Sync = bool(v != 0) + if msglen < 0 { + return ErrInvalidLengthLeases + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLeases + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Resource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLeases(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLeases } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1290,7 +2854,7 @@ func (m *DeleteRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListRequest) Unmarshal(dAtA []byte) error { +func (m *ListResourcesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1305,7 +2869,7 @@ func (m *ListRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1313,15 +2877,15 @@ func (m *ListRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListRequest: wiretype end group for non-group") + return fmt.Errorf("proto: ListResourcesRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListResourcesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -1333,7 +2897,7 @@ func (m *ListRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1343,10 +2907,13 @@ func (m *ListRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLeases } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLeases + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Filters = append(m.Filters, string(dAtA[iNdEx:postIndex])) + m.ID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -1354,12 +2921,13 @@ func (m *ListRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLeases } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1369,7 +2937,7 @@ func (m *ListRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListResponse) Unmarshal(dAtA []byte) error { +func (m *ListResourcesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -1384,7 +2952,7 @@ func (m *ListResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1392,15 +2960,15 @@ func (m *ListResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListResponse: wiretype end group for non-group") + return fmt.Errorf("proto: ListResourcesResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListResourcesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Leases", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Resources", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1412,7 +2980,7 @@ func (m *ListResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1421,11 +2989,14 @@ func (m *ListResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLeases } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLeases + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Leases = append(m.Leases, &Lease{}) - if err := m.Leases[len(m.Leases)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Resources = append(m.Resources, Resource{}) + if err := m.Resources[len(m.Resources)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1435,12 +3006,13 @@ func (m *ListResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLeases } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1453,6 +3025,7 @@ func (m *ListResponse) Unmarshal(dAtA []byte) error { func skipLeases(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1484,10 +3057,8 @@ func skipLeases(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1504,94 +3075,34 @@ func skipLeases(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthLeases } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLeases - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipLeases(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupLeases + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthLeases + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthLeases = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowLeases = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthLeases = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowLeases = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupLeases = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/leases/v1/leases.proto", fileDescriptorLeases) -} - -var fileDescriptorLeases = []byte{ - // 515 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0xdf, 0x8a, 0xd3, 0x40, - 0x14, 0xc6, 0x3b, 0xe9, 0x36, 0x6e, 0x4f, 0x5d, 0x91, 0x61, 0x59, 0x4a, 0xc4, 0xb4, 0x04, 0xc1, - 0xe2, 0x9f, 0x89, 0x5b, 0x6f, 0xd6, 0x5d, 0x11, 0xec, 0x76, 0x41, 0x21, 0x88, 0x04, 0x2f, 0x16, - 0x6f, 0x96, 0x34, 0x3d, 0x1b, 0x83, 0x69, 0x12, 0x33, 0xd3, 0x42, 0xef, 0x7c, 0x04, 0x1f, 0xc1, - 0x87, 0xf0, 0x21, 0x7a, 0xe9, 0xa5, 0x57, 0xab, 0x9b, 0x3b, 0xdf, 0x42, 0x32, 0x93, 0xb0, 0x7f, - 0x44, 0x5b, 0x65, 0xef, 0xce, 0xcc, 0x7c, 0xdf, 0x99, 0xdf, 0xf9, 0xc2, 0x04, 0x86, 0x41, 0x28, - 0xde, 0x4d, 0x47, 0xcc, 0x4f, 0x26, 0xb6, 0x9f, 0xc4, 0xc2, 0x0b, 0x63, 0xcc, 0xc6, 0xe7, 0x4b, - 0x2f, 0x0d, 0x6d, 0x8e, 0xd9, 0x2c, 0xf4, 0x91, 0xdb, 0x11, 0x7a, 0x1c, 0xb9, 0x3d, 0xdb, 0x2e, - 0x2b, 0x96, 0x66, 0x89, 0x48, 0xe8, 0xed, 0x33, 0x3d, 0xab, 0xb4, 0xac, 0x54, 0xcc, 0xb6, 0x8d, - 0xcd, 0x20, 0x09, 0x12, 0xa9, 0xb4, 0x8b, 0x4a, 0x99, 0x8c, 0x5b, 0x41, 0x92, 0x04, 0x11, 0xda, - 0x72, 0x35, 0x9a, 0x1e, 0xdb, 0x38, 0x49, 0xc5, 0xbc, 0x3c, 0xec, 0x5c, 0x3e, 0x14, 0xe1, 0x04, - 0xb9, 0xf0, 0x26, 0xa9, 0x12, 0x58, 0x3f, 0x09, 0x34, 0x9c, 0xe2, 0x06, 0xba, 0x05, 0x5a, 0x38, - 0x6e, 0x93, 0x2e, 0xe9, 0x35, 0x07, 0x7a, 0x7e, 0xd2, 0xd1, 0x5e, 0x0e, 0x5d, 0x2d, 0x1c, 0xd3, - 0x7d, 0x00, 0x3f, 0x43, 0x4f, 0xe0, 0xf8, 0xc8, 0x13, 0x6d, 0xad, 0x4b, 0x7a, 0xad, 0xbe, 0xc1, - 0x54, 0x5f, 0x56, 0xf5, 0x65, 0x6f, 0xaa, 0xbe, 0x83, 0xf5, 0xc5, 0x49, 0xa7, 0xf6, 0xe9, 0x7b, - 0x87, 0xb8, 0xcd, 0xd2, 0xf7, 0x5c, 0xd0, 0x17, 0xa0, 0x47, 0xde, 0x08, 0x23, 0xde, 0xae, 0x77, - 0xeb, 0xbd, 0x56, 0xff, 0x11, 0xfb, 0xeb, 0xa8, 0x4c, 0x22, 0x31, 0x47, 0x5a, 0x0e, 0x62, 0x91, - 0xcd, 0xdd, 0xd2, 0x6f, 0x3c, 0x81, 0xd6, 0xb9, 0x6d, 0x7a, 0x13, 0xea, 0xef, 0x71, 0xae, 0xb0, - 0xdd, 0xa2, 0xa4, 0x9b, 0xd0, 0x98, 0x79, 0xd1, 0x14, 0x25, 0x6a, 0xd3, 0x55, 0x8b, 0x5d, 0x6d, - 0x87, 0x58, 0x5f, 0x08, 0x6c, 0xec, 0x4b, 0x24, 0x17, 0x3f, 0x4c, 0x91, 0x8b, 0x3f, 0xce, 0xfc, - 0xfa, 0x12, 0xee, 0xce, 0x12, 0xdc, 0x0b, 0x5d, 0xaf, 0x1a, 0xdb, 0x81, 0x1b, 0x55, 0x7f, 0x9e, - 0x26, 0x31, 0x47, 0xba, 0x0b, 0x0d, 0x79, 0xb7, 0xf4, 0xb7, 0xfa, 0x77, 0x56, 0x09, 0xd3, 0x55, - 0x16, 0x6b, 0x0f, 0x36, 0x86, 0x18, 0xe1, 0xf2, 0x0c, 0x28, 0xac, 0xf1, 0x79, 0xec, 0x4b, 0x9e, - 0x75, 0x57, 0xd6, 0xd6, 0x5d, 0x68, 0x39, 0x21, 0x17, 0x95, 0xb5, 0x0d, 0xd7, 0x8e, 0xc3, 0x48, - 0x60, 0xc6, 0xdb, 0xa4, 0x5b, 0xef, 0x35, 0xdd, 0x6a, 0x69, 0x39, 0x70, 0x5d, 0x09, 0x4b, 0xe2, - 0xa7, 0xa0, 0x2b, 0x1e, 0x29, 0x5c, 0x15, 0xb9, 0xf4, 0xf4, 0x3f, 0x6b, 0xa0, 0xcb, 0x1d, 0x4e, - 0x11, 0x74, 0x15, 0x06, 0x7d, 0xf0, 0x2f, 0xdf, 0xc4, 0x78, 0xb8, 0xa2, 0xba, 0xe4, 0x7d, 0x05, - 0xba, 0x4a, 0x69, 0xe9, 0x35, 0x17, 0xc2, 0x34, 0xb6, 0x7e, 0x7b, 0x18, 0x07, 0xc5, 0x6b, 0xa4, - 0x47, 0xb0, 0x56, 0xe4, 0x41, 0xef, 0x2d, 0x9b, 0xfb, 0x2c, 0x5d, 0xe3, 0xfe, 0x4a, 0x5a, 0x05, - 0x3c, 0x38, 0x5c, 0x9c, 0x9a, 0xb5, 0x6f, 0xa7, 0x66, 0xed, 0x63, 0x6e, 0x92, 0x45, 0x6e, 0x92, - 0xaf, 0xb9, 0x49, 0x7e, 0xe4, 0x26, 0x79, 0xfb, 0xec, 0x3f, 0x7f, 0x4d, 0x7b, 0xaa, 0x3a, 0xac, - 0x8d, 0x74, 0x39, 0xcc, 0xe3, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x14, 0x74, 0xdd, 0x12, 0xe5, - 0x04, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto b/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto index 2df4b06239bc9..6aa61faedf778 100644 --- a/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto +++ b/vendor/github.com/containerd/containerd/api/services/leases/v1/leases.proto @@ -1,3 +1,18 @@ +/* + Copyright The containerd Authors. + + 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. +*/ syntax = "proto3"; package containerd.services.leases.v1; @@ -22,6 +37,15 @@ service Leases { // List lists all active leases, returning the full list of // leases and optionally including the referenced resources. rpc List(ListRequest) returns (ListResponse); + + // AddResource references the resource by the provided lease. + rpc AddResource(AddResourceRequest) returns (google.protobuf.Empty); + + // DeleteResource dereferences the resource by the provided lease. + rpc DeleteResource(DeleteResourceRequest) returns (google.protobuf.Empty); + + // ListResources lists all the resources referenced by the lease. + rpc ListResources(ListResourcesRequest) returns (ListResourcesResponse); } // Lease is an object which retains resources while it exists. @@ -62,3 +86,32 @@ message ListRequest { message ListResponse { repeated Lease leases = 1; } + +message Resource { + string id = 1; + + // For snapshotter resource, there are many snapshotter types here, like + // overlayfs, devmapper etc. The type will be formatted with type, + // like "snapshotter/overlayfs". + string type = 2; +} + +message AddResourceRequest { + string id = 1; + + Resource resource = 2 [(gogoproto.nullable) = false]; +} + +message DeleteResourceRequest { + string id = 1; + + Resource resource = 2 [(gogoproto.nullable) = false]; +} + +message ListResourcesRequest { + string id = 1; +} + +message ListResourcesResponse { + repeated Resource resources = 1 [(gogoproto.nullable) = false]; +} diff --git a/vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.pb.go b/vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.pb.go index f471f1c127b38..76f9e117266e1 100644 --- a/vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.pb.go @@ -1,42 +1,23 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto -/* - Package namespaces is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto - - It has these top-level messages: - Namespace - GetNamespaceRequest - GetNamespaceResponse - ListNamespacesRequest - ListNamespacesResponse - CreateNamespaceRequest - CreateNamespaceResponse - UpdateNamespaceRequest - UpdateNamespaceResponse - DeleteNamespaceRequest -*/ package namespaces -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import google_protobuf1 "github.com/gogo/protobuf/types" -import google_protobuf2 "github.com/gogo/protobuf/types" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + context "context" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + types "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -47,7 +28,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Namespace struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` @@ -57,60 +38,277 @@ type Namespace struct { // // Note that to add a new value to this field, read the existing set and // include the entire result in the update call. - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Namespace) Reset() { *m = Namespace{} } +func (*Namespace) ProtoMessage() {} +func (*Namespace) Descriptor() ([]byte, []int) { + return fileDescriptor_8c41761eaeea4fd3, []int{0} +} +func (m *Namespace) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Namespace) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Namespace.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Namespace) XXX_Merge(src proto.Message) { + xxx_messageInfo_Namespace.Merge(m, src) +} +func (m *Namespace) XXX_Size() int { + return m.Size() +} +func (m *Namespace) XXX_DiscardUnknown() { + xxx_messageInfo_Namespace.DiscardUnknown(m) } -func (m *Namespace) Reset() { *m = Namespace{} } -func (*Namespace) ProtoMessage() {} -func (*Namespace) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{0} } +var xxx_messageInfo_Namespace proto.InternalMessageInfo type GetNamespaceRequest struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetNamespaceRequest) Reset() { *m = GetNamespaceRequest{} } +func (*GetNamespaceRequest) ProtoMessage() {} +func (*GetNamespaceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8c41761eaeea4fd3, []int{1} +} +func (m *GetNamespaceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetNamespaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetNamespaceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetNamespaceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetNamespaceRequest.Merge(m, src) +} +func (m *GetNamespaceRequest) XXX_Size() int { + return m.Size() +} +func (m *GetNamespaceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetNamespaceRequest.DiscardUnknown(m) } -func (m *GetNamespaceRequest) Reset() { *m = GetNamespaceRequest{} } -func (*GetNamespaceRequest) ProtoMessage() {} -func (*GetNamespaceRequest) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{1} } +var xxx_messageInfo_GetNamespaceRequest proto.InternalMessageInfo type GetNamespaceResponse struct { - Namespace Namespace `protobuf:"bytes,1,opt,name=namespace" json:"namespace"` + Namespace Namespace `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetNamespaceResponse) Reset() { *m = GetNamespaceResponse{} } +func (*GetNamespaceResponse) ProtoMessage() {} +func (*GetNamespaceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_8c41761eaeea4fd3, []int{2} +} +func (m *GetNamespaceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetNamespaceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetNamespaceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetNamespaceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetNamespaceResponse.Merge(m, src) +} +func (m *GetNamespaceResponse) XXX_Size() int { + return m.Size() +} +func (m *GetNamespaceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetNamespaceResponse.DiscardUnknown(m) } -func (m *GetNamespaceResponse) Reset() { *m = GetNamespaceResponse{} } -func (*GetNamespaceResponse) ProtoMessage() {} -func (*GetNamespaceResponse) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{2} } +var xxx_messageInfo_GetNamespaceResponse proto.InternalMessageInfo type ListNamespacesRequest struct { - Filter string `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` + Filter string `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListNamespacesRequest) Reset() { *m = ListNamespacesRequest{} } +func (*ListNamespacesRequest) ProtoMessage() {} +func (*ListNamespacesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8c41761eaeea4fd3, []int{3} +} +func (m *ListNamespacesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListNamespacesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListNamespacesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListNamespacesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListNamespacesRequest.Merge(m, src) +} +func (m *ListNamespacesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListNamespacesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListNamespacesRequest.DiscardUnknown(m) } -func (m *ListNamespacesRequest) Reset() { *m = ListNamespacesRequest{} } -func (*ListNamespacesRequest) ProtoMessage() {} -func (*ListNamespacesRequest) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{3} } +var xxx_messageInfo_ListNamespacesRequest proto.InternalMessageInfo type ListNamespacesResponse struct { - Namespaces []Namespace `protobuf:"bytes,1,rep,name=namespaces" json:"namespaces"` + Namespaces []Namespace `protobuf:"bytes,1,rep,name=namespaces,proto3" json:"namespaces"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListNamespacesResponse) Reset() { *m = ListNamespacesResponse{} } +func (*ListNamespacesResponse) ProtoMessage() {} +func (*ListNamespacesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_8c41761eaeea4fd3, []int{4} +} +func (m *ListNamespacesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListNamespacesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListNamespacesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListNamespacesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListNamespacesResponse.Merge(m, src) +} +func (m *ListNamespacesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListNamespacesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListNamespacesResponse.DiscardUnknown(m) } -func (m *ListNamespacesResponse) Reset() { *m = ListNamespacesResponse{} } -func (*ListNamespacesResponse) ProtoMessage() {} -func (*ListNamespacesResponse) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{4} } +var xxx_messageInfo_ListNamespacesResponse proto.InternalMessageInfo type CreateNamespaceRequest struct { - Namespace Namespace `protobuf:"bytes,1,opt,name=namespace" json:"namespace"` + Namespace Namespace `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateNamespaceRequest) Reset() { *m = CreateNamespaceRequest{} } +func (*CreateNamespaceRequest) ProtoMessage() {} +func (*CreateNamespaceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8c41761eaeea4fd3, []int{5} +} +func (m *CreateNamespaceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateNamespaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateNamespaceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateNamespaceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateNamespaceRequest.Merge(m, src) +} +func (m *CreateNamespaceRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateNamespaceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateNamespaceRequest.DiscardUnknown(m) } -func (m *CreateNamespaceRequest) Reset() { *m = CreateNamespaceRequest{} } -func (*CreateNamespaceRequest) ProtoMessage() {} -func (*CreateNamespaceRequest) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{5} } +var xxx_messageInfo_CreateNamespaceRequest proto.InternalMessageInfo type CreateNamespaceResponse struct { - Namespace Namespace `protobuf:"bytes,1,opt,name=namespace" json:"namespace"` + Namespace Namespace `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateNamespaceResponse) Reset() { *m = CreateNamespaceResponse{} } +func (*CreateNamespaceResponse) ProtoMessage() {} +func (*CreateNamespaceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_8c41761eaeea4fd3, []int{6} +} +func (m *CreateNamespaceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateNamespaceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateNamespaceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateNamespaceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateNamespaceResponse.Merge(m, src) +} +func (m *CreateNamespaceResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateNamespaceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateNamespaceResponse.DiscardUnknown(m) } -func (m *CreateNamespaceResponse) Reset() { *m = CreateNamespaceResponse{} } -func (*CreateNamespaceResponse) ProtoMessage() {} -func (*CreateNamespaceResponse) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{6} } +var xxx_messageInfo_CreateNamespaceResponse proto.InternalMessageInfo // UpdateNamespaceRequest updates the metadata for a namespace. // @@ -121,38 +319,132 @@ type UpdateNamespaceRequest struct { // Namespace provides the target value, as declared by the mask, for the update. // // The namespace field must be set. - Namespace Namespace `protobuf:"bytes,1,opt,name=namespace" json:"namespace"` + Namespace Namespace `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace"` // UpdateMask specifies which fields to perform the update on. If empty, // the operation applies to all fields. // // For the most part, this applies only to selectively updating labels on // the namespace. While field masks are typically limited to ascii alphas // and digits, we just take everything after the "labels." as the map key. - UpdateMask *google_protobuf2.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask" json:"update_mask,omitempty"` + UpdateMask *types.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateNamespaceRequest) Reset() { *m = UpdateNamespaceRequest{} } +func (*UpdateNamespaceRequest) ProtoMessage() {} +func (*UpdateNamespaceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8c41761eaeea4fd3, []int{7} +} +func (m *UpdateNamespaceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateNamespaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateNamespaceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateNamespaceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateNamespaceRequest.Merge(m, src) +} +func (m *UpdateNamespaceRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateNamespaceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateNamespaceRequest.DiscardUnknown(m) } -func (m *UpdateNamespaceRequest) Reset() { *m = UpdateNamespaceRequest{} } -func (*UpdateNamespaceRequest) ProtoMessage() {} -func (*UpdateNamespaceRequest) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{7} } +var xxx_messageInfo_UpdateNamespaceRequest proto.InternalMessageInfo type UpdateNamespaceResponse struct { - Namespace Namespace `protobuf:"bytes,1,opt,name=namespace" json:"namespace"` + Namespace Namespace `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateNamespaceResponse) Reset() { *m = UpdateNamespaceResponse{} } +func (*UpdateNamespaceResponse) ProtoMessage() {} +func (*UpdateNamespaceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_8c41761eaeea4fd3, []int{8} +} +func (m *UpdateNamespaceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateNamespaceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateNamespaceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateNamespaceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateNamespaceResponse.Merge(m, src) +} +func (m *UpdateNamespaceResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateNamespaceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateNamespaceResponse.DiscardUnknown(m) } -func (m *UpdateNamespaceResponse) Reset() { *m = UpdateNamespaceResponse{} } -func (*UpdateNamespaceResponse) ProtoMessage() {} -func (*UpdateNamespaceResponse) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{8} } +var xxx_messageInfo_UpdateNamespaceResponse proto.InternalMessageInfo type DeleteNamespaceRequest struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteNamespaceRequest) Reset() { *m = DeleteNamespaceRequest{} } +func (*DeleteNamespaceRequest) ProtoMessage() {} +func (*DeleteNamespaceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8c41761eaeea4fd3, []int{9} +} +func (m *DeleteNamespaceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteNamespaceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteNamespaceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteNamespaceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteNamespaceRequest.Merge(m, src) +} +func (m *DeleteNamespaceRequest) XXX_Size() int { + return m.Size() +} +func (m *DeleteNamespaceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteNamespaceRequest.DiscardUnknown(m) } -func (m *DeleteNamespaceRequest) Reset() { *m = DeleteNamespaceRequest{} } -func (*DeleteNamespaceRequest) ProtoMessage() {} -func (*DeleteNamespaceRequest) Descriptor() ([]byte, []int) { return fileDescriptorNamespace, []int{9} } +var xxx_messageInfo_DeleteNamespaceRequest proto.InternalMessageInfo func init() { proto.RegisterType((*Namespace)(nil), "containerd.services.namespaces.v1.Namespace") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.namespaces.v1.Namespace.LabelsEntry") proto.RegisterType((*GetNamespaceRequest)(nil), "containerd.services.namespaces.v1.GetNamespaceRequest") proto.RegisterType((*GetNamespaceResponse)(nil), "containerd.services.namespaces.v1.GetNamespaceResponse") proto.RegisterType((*ListNamespacesRequest)(nil), "containerd.services.namespaces.v1.ListNamespacesRequest") @@ -164,6 +456,49 @@ func init() { proto.RegisterType((*DeleteNamespaceRequest)(nil), "containerd.services.namespaces.v1.DeleteNamespaceRequest") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto", fileDescriptor_8c41761eaeea4fd3) +} + +var fileDescriptor_8c41761eaeea4fd3 = []byte{ + // 551 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xcd, 0x6e, 0xd3, 0x4c, + 0x14, 0xcd, 0x24, 0xf9, 0x2c, 0xe5, 0x7a, 0xf3, 0x69, 0x08, 0x26, 0x32, 0x92, 0x09, 0x5e, 0x15, + 0xa9, 0x1a, 0xab, 0x41, 0x82, 0xfe, 0xec, 0x0a, 0x6d, 0x17, 0x14, 0x84, 0x2c, 0x21, 0x21, 0x58, + 0x80, 0x93, 0x4c, 0x5c, 0x13, 0xc7, 0x36, 0x9e, 0xb1, 0xa5, 0x88, 0x05, 0xbc, 0x0d, 0x1b, 0x1e, + 0x24, 0x4b, 0x96, 0xac, 0x50, 0x9b, 0x27, 0x41, 0x33, 0x76, 0xe2, 0xd0, 0x18, 0xe1, 0x06, 0xca, + 0xee, 0x5e, 0x7b, 0xce, 0x3d, 0x67, 0xae, 0xce, 0xb1, 0xe1, 0x89, 0xeb, 0xf1, 0xb3, 0xa4, 0x4f, + 0x06, 0xe1, 0xc4, 0x1a, 0x84, 0x01, 0x77, 0xbc, 0x80, 0xc6, 0xc3, 0xd5, 0xd2, 0x89, 0x3c, 0x8b, + 0xd1, 0x38, 0xf5, 0x06, 0x94, 0x59, 0x81, 0x33, 0xa1, 0x2c, 0x72, 0x44, 0x99, 0xee, 0x14, 0x1d, + 0x89, 0xe2, 0x90, 0x87, 0xf8, 0x6e, 0x01, 0x23, 0x0b, 0x08, 0x29, 0x20, 0x24, 0xdd, 0xd1, 0xdb, + 0x6e, 0xe8, 0x86, 0xf2, 0xb4, 0x25, 0xaa, 0x0c, 0xa8, 0xdf, 0x76, 0xc3, 0xd0, 0xf5, 0xa9, 0x25, + 0xbb, 0x7e, 0x32, 0xb2, 0xe8, 0x24, 0xe2, 0xd3, 0xfc, 0x65, 0xf7, 0xf2, 0xcb, 0x91, 0x47, 0xfd, + 0xe1, 0x9b, 0x89, 0xc3, 0xc6, 0xd9, 0x09, 0xf3, 0x0b, 0x82, 0xd6, 0xb3, 0x05, 0x0d, 0xc6, 0xd0, + 0x14, 0x9c, 0x1d, 0xd4, 0x45, 0x5b, 0x2d, 0x5b, 0xd6, 0xf8, 0x39, 0x28, 0xbe, 0xd3, 0xa7, 0x3e, + 0xeb, 0xd4, 0xbb, 0x8d, 0x2d, 0xb5, 0xb7, 0x4b, 0x7e, 0x2b, 0x95, 0x2c, 0x27, 0x92, 0x53, 0x09, + 0x3d, 0x0a, 0x78, 0x3c, 0xb5, 0xf3, 0x39, 0xfa, 0x1e, 0xa8, 0x2b, 0x8f, 0xf1, 0xff, 0xd0, 0x18, + 0xd3, 0x69, 0xce, 0x29, 0x4a, 0xdc, 0x86, 0xff, 0x52, 0xc7, 0x4f, 0x68, 0xa7, 0x2e, 0x9f, 0x65, + 0xcd, 0x7e, 0x7d, 0x17, 0x99, 0xf7, 0xe0, 0xc6, 0x09, 0xe5, 0xcb, 0xf1, 0x36, 0x7d, 0x9f, 0x50, + 0xc6, 0xcb, 0x74, 0x9b, 0x67, 0xd0, 0xfe, 0xf9, 0x28, 0x8b, 0xc2, 0x80, 0x89, 0xfb, 0xb4, 0x96, + 0x62, 0x25, 0x40, 0xed, 0x6d, 0x5f, 0xe5, 0x4a, 0x87, 0xcd, 0xd9, 0xf7, 0x3b, 0x35, 0xbb, 0x18, + 0x62, 0x5a, 0x70, 0xf3, 0xd4, 0x63, 0x05, 0x15, 0x5b, 0xc8, 0xd2, 0x40, 0x19, 0x79, 0x3e, 0xa7, + 0x71, 0x2e, 0x2c, 0xef, 0x4c, 0x1f, 0xb4, 0xcb, 0x80, 0x5c, 0x9c, 0x0d, 0x50, 0xd0, 0x76, 0x90, + 0x5c, 0xf8, 0x26, 0xea, 0x56, 0xa6, 0x98, 0xef, 0x40, 0x7b, 0x14, 0x53, 0x87, 0xd3, 0xb5, 0xb5, + 0xfd, 0xfd, 0x55, 0x8c, 0xe1, 0xd6, 0x1a, 0xd7, 0xb5, 0xed, 0xfd, 0x33, 0x02, 0xed, 0x45, 0x34, + 0xfc, 0x27, 0x37, 0xc3, 0x07, 0xa0, 0x26, 0x92, 0x4b, 0xa6, 0x47, 0x3a, 0x53, 0xed, 0xe9, 0x24, + 0x0b, 0x18, 0x59, 0x04, 0x8c, 0x1c, 0x8b, 0x80, 0x3d, 0x75, 0xd8, 0xd8, 0x86, 0xec, 0xb8, 0xa8, + 0xc5, 0x5a, 0xd6, 0x84, 0x5e, 0xdb, 0x5a, 0xb6, 0x41, 0x7b, 0x4c, 0x7d, 0x5a, 0xb2, 0x95, 0x92, + 0x98, 0xf4, 0xce, 0x9b, 0x00, 0x85, 0x11, 0x71, 0x0a, 0x8d, 0x13, 0xca, 0xf1, 0x83, 0x0a, 0x12, + 0x4a, 0x82, 0xa8, 0x3f, 0xbc, 0x32, 0x2e, 0x5f, 0xc3, 0x07, 0x68, 0x8a, 0x48, 0xe0, 0x2a, 0x5f, + 0x97, 0xd2, 0xb0, 0xe9, 0x7b, 0x1b, 0x20, 0x73, 0xf2, 0x8f, 0xa0, 0x64, 0xae, 0xc5, 0x55, 0x86, + 0x94, 0x87, 0x49, 0xdf, 0xdf, 0x04, 0x5a, 0x08, 0xc8, 0xfc, 0x51, 0x49, 0x40, 0xb9, 0xe7, 0x2b, + 0x09, 0xf8, 0x95, 0x0b, 0x5f, 0x83, 0x92, 0x79, 0xa6, 0x92, 0x80, 0x72, 0x7b, 0xe9, 0xda, 0x5a, + 0x1a, 0x8e, 0xc4, 0xbf, 0xe8, 0xf0, 0xed, 0xec, 0xc2, 0xa8, 0x7d, 0xbb, 0x30, 0x6a, 0x9f, 0xe6, + 0x06, 0x9a, 0xcd, 0x0d, 0xf4, 0x75, 0x6e, 0xa0, 0xf3, 0xb9, 0x81, 0x5e, 0x1d, 0xff, 0xc1, 0x2f, + 0xf4, 0xa0, 0xe8, 0x5e, 0xd6, 0xfa, 0x8a, 0xe4, 0xbc, 0xff, 0x23, 0x00, 0x00, 0xff, 0xff, 0x4f, + 0x4a, 0x87, 0xf3, 0x95, 0x07, 0x00, 0x00, +} + // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn @@ -172,14 +507,15 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Namespaces service - +// NamespacesClient is the client API for Namespaces service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type NamespacesClient interface { Get(ctx context.Context, in *GetNamespaceRequest, opts ...grpc.CallOption) (*GetNamespaceResponse, error) List(ctx context.Context, in *ListNamespacesRequest, opts ...grpc.CallOption) (*ListNamespacesResponse, error) Create(ctx context.Context, in *CreateNamespaceRequest, opts ...grpc.CallOption) (*CreateNamespaceResponse, error) Update(ctx context.Context, in *UpdateNamespaceRequest, opts ...grpc.CallOption) (*UpdateNamespaceResponse, error) - Delete(ctx context.Context, in *DeleteNamespaceRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + Delete(ctx context.Context, in *DeleteNamespaceRequest, opts ...grpc.CallOption) (*types.Empty, error) } type namespacesClient struct { @@ -192,7 +528,7 @@ func NewNamespacesClient(cc *grpc.ClientConn) NamespacesClient { func (c *namespacesClient) Get(ctx context.Context, in *GetNamespaceRequest, opts ...grpc.CallOption) (*GetNamespaceResponse, error) { out := new(GetNamespaceResponse) - err := grpc.Invoke(ctx, "/containerd.services.namespaces.v1.Namespaces/Get", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.namespaces.v1.Namespaces/Get", in, out, opts...) if err != nil { return nil, err } @@ -201,7 +537,7 @@ func (c *namespacesClient) Get(ctx context.Context, in *GetNamespaceRequest, opt func (c *namespacesClient) List(ctx context.Context, in *ListNamespacesRequest, opts ...grpc.CallOption) (*ListNamespacesResponse, error) { out := new(ListNamespacesResponse) - err := grpc.Invoke(ctx, "/containerd.services.namespaces.v1.Namespaces/List", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.namespaces.v1.Namespaces/List", in, out, opts...) if err != nil { return nil, err } @@ -210,7 +546,7 @@ func (c *namespacesClient) List(ctx context.Context, in *ListNamespacesRequest, func (c *namespacesClient) Create(ctx context.Context, in *CreateNamespaceRequest, opts ...grpc.CallOption) (*CreateNamespaceResponse, error) { out := new(CreateNamespaceResponse) - err := grpc.Invoke(ctx, "/containerd.services.namespaces.v1.Namespaces/Create", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.namespaces.v1.Namespaces/Create", in, out, opts...) if err != nil { return nil, err } @@ -219,30 +555,49 @@ func (c *namespacesClient) Create(ctx context.Context, in *CreateNamespaceReques func (c *namespacesClient) Update(ctx context.Context, in *UpdateNamespaceRequest, opts ...grpc.CallOption) (*UpdateNamespaceResponse, error) { out := new(UpdateNamespaceResponse) - err := grpc.Invoke(ctx, "/containerd.services.namespaces.v1.Namespaces/Update", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.namespaces.v1.Namespaces/Update", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *namespacesClient) Delete(ctx context.Context, in *DeleteNamespaceRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { - out := new(google_protobuf1.Empty) - err := grpc.Invoke(ctx, "/containerd.services.namespaces.v1.Namespaces/Delete", in, out, c.cc, opts...) +func (c *namespacesClient) Delete(ctx context.Context, in *DeleteNamespaceRequest, opts ...grpc.CallOption) (*types.Empty, error) { + out := new(types.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.namespaces.v1.Namespaces/Delete", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Namespaces service - +// NamespacesServer is the server API for Namespaces service. type NamespacesServer interface { Get(context.Context, *GetNamespaceRequest) (*GetNamespaceResponse, error) List(context.Context, *ListNamespacesRequest) (*ListNamespacesResponse, error) Create(context.Context, *CreateNamespaceRequest) (*CreateNamespaceResponse, error) Update(context.Context, *UpdateNamespaceRequest) (*UpdateNamespaceResponse, error) - Delete(context.Context, *DeleteNamespaceRequest) (*google_protobuf1.Empty, error) + Delete(context.Context, *DeleteNamespaceRequest) (*types.Empty, error) +} + +// UnimplementedNamespacesServer can be embedded to have forward compatible implementations. +type UnimplementedNamespacesServer struct { +} + +func (*UnimplementedNamespacesServer) Get(ctx context.Context, req *GetNamespaceRequest) (*GetNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (*UnimplementedNamespacesServer) List(ctx context.Context, req *ListNamespacesRequest) (*ListNamespacesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (*UnimplementedNamespacesServer) Create(ctx context.Context, req *CreateNamespaceRequest) (*CreateNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") +} +func (*UnimplementedNamespacesServer) Update(ctx context.Context, req *UpdateNamespaceRequest) (*UpdateNamespaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (*UnimplementedNamespacesServer) Delete(ctx context.Context, req *DeleteNamespaceRequest) (*types.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") } func RegisterNamespacesServer(s *grpc.Server, srv NamespacesServer) { @@ -371,7 +726,7 @@ var _Namespaces_serviceDesc = grpc.ServiceDesc{ func (m *Namespace) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -379,40 +734,52 @@ func (m *Namespace) Marshal() (dAtA []byte, err error) { } func (m *Namespace) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Namespace) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x12 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovNamespace(uint64(len(k))) + 1 + len(v) + sovNamespace(uint64(len(v))) - i = encodeVarintNamespace(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintNamespace(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintNamespace(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintNamespace(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintNamespace(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - return i, nil + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *GetNamespaceRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -420,23 +787,33 @@ func (m *GetNamespaceRequest) Marshal() (dAtA []byte, err error) { } func (m *GetNamespaceRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetNamespaceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Name) + copy(dAtA[i:], m.Name) i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *GetNamespaceResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -444,25 +821,36 @@ func (m *GetNamespaceResponse) Marshal() (dAtA []byte, err error) { } func (m *GetNamespaceResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetNamespaceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintNamespace(dAtA, i, uint64(m.Namespace.Size())) - n1, err := m.Namespace.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n1 - return i, nil + { + size, err := m.Namespace.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintNamespace(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *ListNamespacesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -470,23 +858,33 @@ func (m *ListNamespacesRequest) Marshal() (dAtA []byte, err error) { } func (m *ListNamespacesRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListNamespacesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Filter) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Filter) + copy(dAtA[i:], m.Filter) i = encodeVarintNamespace(dAtA, i, uint64(len(m.Filter))) - i += copy(dAtA[i:], m.Filter) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListNamespacesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -494,29 +892,40 @@ func (m *ListNamespacesResponse) Marshal() (dAtA []byte, err error) { } func (m *ListNamespacesResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListNamespacesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Namespaces) > 0 { - for _, msg := range m.Namespaces { - dAtA[i] = 0xa - i++ - i = encodeVarintNamespace(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Namespaces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Namespaces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintNamespace(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *CreateNamespaceRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -524,25 +933,36 @@ func (m *CreateNamespaceRequest) Marshal() (dAtA []byte, err error) { } func (m *CreateNamespaceRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateNamespaceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintNamespace(dAtA, i, uint64(m.Namespace.Size())) - n2, err := m.Namespace.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Namespace.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintNamespace(dAtA, i, uint64(size)) } - i += n2 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *CreateNamespaceResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -550,25 +970,36 @@ func (m *CreateNamespaceResponse) Marshal() (dAtA []byte, err error) { } func (m *CreateNamespaceResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateNamespaceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintNamespace(dAtA, i, uint64(m.Namespace.Size())) - n3, err := m.Namespace.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Namespace.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintNamespace(dAtA, i, uint64(size)) } - i += n3 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *UpdateNamespaceRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -576,35 +1007,48 @@ func (m *UpdateNamespaceRequest) Marshal() (dAtA []byte, err error) { } func (m *UpdateNamespaceRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateNamespaceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintNamespace(dAtA, i, uint64(m.Namespace.Size())) - n4, err := m.Namespace.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n4 if m.UpdateMask != nil { + { + size, err := m.UpdateMask.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintNamespace(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintNamespace(dAtA, i, uint64(m.UpdateMask.Size())) - n5, err := m.UpdateMask.MarshalTo(dAtA[i:]) + } + { + size, err := m.Namespace.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n5 + i -= size + i = encodeVarintNamespace(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *UpdateNamespaceResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -612,25 +1056,36 @@ func (m *UpdateNamespaceResponse) Marshal() (dAtA []byte, err error) { } func (m *UpdateNamespaceResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateNamespaceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintNamespace(dAtA, i, uint64(m.Namespace.Size())) - n6, err := m.Namespace.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Namespace.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintNamespace(dAtA, i, uint64(size)) } - i += n6 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *DeleteNamespaceRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -638,29 +1093,44 @@ func (m *DeleteNamespaceRequest) Marshal() (dAtA []byte, err error) { } func (m *DeleteNamespaceRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteNamespaceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Name) + copy(dAtA[i:], m.Name) i = encodeVarintNamespace(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintNamespace(dAtA []byte, offset int, v uint64) int { + offset -= sovNamespace(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Namespace) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -675,38 +1145,62 @@ func (m *Namespace) Size() (n int) { n += mapEntrySize + 1 + sovNamespace(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *GetNamespaceRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + sovNamespace(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *GetNamespaceResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Namespace.Size() n += 1 + l + sovNamespace(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListNamespacesRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Filter) if l > 0 { n += 1 + l + sovNamespace(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListNamespacesResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Namespaces) > 0 { @@ -715,26 +1209,44 @@ func (m *ListNamespacesResponse) Size() (n int) { n += 1 + l + sovNamespace(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateNamespaceRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Namespace.Size() n += 1 + l + sovNamespace(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateNamespaceResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Namespace.Size() n += 1 + l + sovNamespace(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateNamespaceRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Namespace.Size() @@ -743,36 +1255,44 @@ func (m *UpdateNamespaceRequest) Size() (n int) { l = m.UpdateMask.Size() n += 1 + l + sovNamespace(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateNamespaceResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Namespace.Size() n += 1 + l + sovNamespace(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DeleteNamespaceRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + sovNamespace(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovNamespace(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozNamespace(x uint64) (n int) { return sovNamespace(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -785,7 +1305,7 @@ func (this *Namespace) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -794,6 +1314,7 @@ func (this *Namespace) String() string { s := strings.Join([]string{`&Namespace{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -804,6 +1325,7 @@ func (this *GetNamespaceRequest) String() string { } s := strings.Join([]string{`&GetNamespaceRequest{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -814,6 +1336,7 @@ func (this *GetNamespaceResponse) String() string { } s := strings.Join([]string{`&GetNamespaceResponse{`, `Namespace:` + strings.Replace(strings.Replace(this.Namespace.String(), "Namespace", "Namespace", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -824,6 +1347,7 @@ func (this *ListNamespacesRequest) String() string { } s := strings.Join([]string{`&ListNamespacesRequest{`, `Filter:` + fmt.Sprintf("%v", this.Filter) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -832,8 +1356,14 @@ func (this *ListNamespacesResponse) String() string { if this == nil { return "nil" } + repeatedStringForNamespaces := "[]Namespace{" + for _, f := range this.Namespaces { + repeatedStringForNamespaces += strings.Replace(strings.Replace(f.String(), "Namespace", "Namespace", 1), `&`, ``, 1) + "," + } + repeatedStringForNamespaces += "}" s := strings.Join([]string{`&ListNamespacesResponse{`, - `Namespaces:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Namespaces), "Namespace", "Namespace", 1), `&`, ``, 1) + `,`, + `Namespaces:` + repeatedStringForNamespaces + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -844,6 +1374,7 @@ func (this *CreateNamespaceRequest) String() string { } s := strings.Join([]string{`&CreateNamespaceRequest{`, `Namespace:` + strings.Replace(strings.Replace(this.Namespace.String(), "Namespace", "Namespace", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -854,6 +1385,7 @@ func (this *CreateNamespaceResponse) String() string { } s := strings.Join([]string{`&CreateNamespaceResponse{`, `Namespace:` + strings.Replace(strings.Replace(this.Namespace.String(), "Namespace", "Namespace", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -864,7 +1396,8 @@ func (this *UpdateNamespaceRequest) String() string { } s := strings.Join([]string{`&UpdateNamespaceRequest{`, `Namespace:` + strings.Replace(strings.Replace(this.Namespace.String(), "Namespace", "Namespace", 1), `&`, ``, 1) + `,`, - `UpdateMask:` + strings.Replace(fmt.Sprintf("%v", this.UpdateMask), "FieldMask", "google_protobuf2.FieldMask", 1) + `,`, + `UpdateMask:` + strings.Replace(fmt.Sprintf("%v", this.UpdateMask), "FieldMask", "types.FieldMask", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -875,6 +1408,7 @@ func (this *UpdateNamespaceResponse) String() string { } s := strings.Join([]string{`&UpdateNamespaceResponse{`, `Namespace:` + strings.Replace(strings.Replace(this.Namespace.String(), "Namespace", "Namespace", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -885,6 +1419,7 @@ func (this *DeleteNamespaceRequest) String() string { } s := strings.Join([]string{`&DeleteNamespaceRequest{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -912,7 +1447,7 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -940,7 +1475,7 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -950,6 +1485,9 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -969,7 +1507,7 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -978,6 +1516,9 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -998,7 +1539,7 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1015,7 +1556,7 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1025,6 +1566,9 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthNamespace + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -1041,7 +1585,7 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1051,6 +1595,9 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthNamespace + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -1062,7 +1609,7 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > postIndex { @@ -1079,12 +1626,13 @@ func (m *Namespace) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1109,7 +1657,7 @@ func (m *GetNamespaceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1137,7 +1685,7 @@ func (m *GetNamespaceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1147,6 +1695,9 @@ func (m *GetNamespaceRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1158,12 +1709,13 @@ func (m *GetNamespaceRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1188,7 +1740,7 @@ func (m *GetNamespaceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1216,7 +1768,7 @@ func (m *GetNamespaceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1225,6 +1777,9 @@ func (m *GetNamespaceResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1238,12 +1793,13 @@ func (m *GetNamespaceResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1268,7 +1824,7 @@ func (m *ListNamespacesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1296,7 +1852,7 @@ func (m *ListNamespacesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1306,6 +1862,9 @@ func (m *ListNamespacesRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1317,12 +1876,13 @@ func (m *ListNamespacesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1347,7 +1907,7 @@ func (m *ListNamespacesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1375,7 +1935,7 @@ func (m *ListNamespacesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1384,6 +1944,9 @@ func (m *ListNamespacesResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1398,12 +1961,13 @@ func (m *ListNamespacesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1428,7 +1992,7 @@ func (m *CreateNamespaceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1456,7 +2020,7 @@ func (m *CreateNamespaceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1465,6 +2029,9 @@ func (m *CreateNamespaceRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1478,12 +2045,13 @@ func (m *CreateNamespaceRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1508,7 +2076,7 @@ func (m *CreateNamespaceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1536,7 +2104,7 @@ func (m *CreateNamespaceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1545,6 +2113,9 @@ func (m *CreateNamespaceResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1558,12 +2129,13 @@ func (m *CreateNamespaceResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1588,7 +2160,7 @@ func (m *UpdateNamespaceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1616,7 +2188,7 @@ func (m *UpdateNamespaceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1625,6 +2197,9 @@ func (m *UpdateNamespaceRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1646,7 +2221,7 @@ func (m *UpdateNamespaceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1655,11 +2230,14 @@ func (m *UpdateNamespaceRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } if m.UpdateMask == nil { - m.UpdateMask = &google_protobuf2.FieldMask{} + m.UpdateMask = &types.FieldMask{} } if err := m.UpdateMask.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -1671,12 +2249,13 @@ func (m *UpdateNamespaceRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1701,7 +2280,7 @@ func (m *UpdateNamespaceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1729,7 +2308,7 @@ func (m *UpdateNamespaceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1738,6 +2317,9 @@ func (m *UpdateNamespaceResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1751,12 +2333,13 @@ func (m *UpdateNamespaceResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1781,7 +2364,7 @@ func (m *DeleteNamespaceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1809,7 +2392,7 @@ func (m *DeleteNamespaceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1819,6 +2402,9 @@ func (m *DeleteNamespaceRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthNamespace } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthNamespace + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1830,12 +2416,13 @@ func (m *DeleteNamespaceRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthNamespace } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1848,6 +2435,7 @@ func (m *DeleteNamespaceRequest) Unmarshal(dAtA []byte) error { func skipNamespace(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1879,10 +2467,8 @@ func skipNamespace(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1899,96 +2485,34 @@ func skipNamespace(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthNamespace } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowNamespace - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipNamespace(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupNamespace + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthNamespace + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthNamespace = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowNamespace = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthNamespace = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowNamespace = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupNamespace = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto", fileDescriptorNamespace) -} - -var fileDescriptorNamespace = []byte{ - // 551 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0xcd, 0x6e, 0xd3, 0x4c, - 0x14, 0xcd, 0x24, 0xf9, 0x2c, 0xe5, 0x7a, 0xf3, 0x69, 0x08, 0x26, 0x32, 0x92, 0x09, 0x5e, 0x15, - 0xa9, 0x1a, 0xab, 0x41, 0x82, 0xfe, 0xec, 0x0a, 0x6d, 0x17, 0x14, 0x84, 0x2c, 0x21, 0x21, 0x58, - 0x80, 0x93, 0x4c, 0x5c, 0x13, 0xc7, 0x36, 0x9e, 0xb1, 0xa5, 0x88, 0x05, 0xbc, 0x0d, 0x1b, 0x1e, - 0x24, 0x4b, 0x96, 0xac, 0x50, 0x9b, 0x27, 0x41, 0x33, 0x76, 0xe2, 0xd0, 0x18, 0xe1, 0x06, 0xca, - 0xee, 0x5e, 0x7b, 0xce, 0x3d, 0x67, 0xae, 0xce, 0xb1, 0xe1, 0x89, 0xeb, 0xf1, 0xb3, 0xa4, 0x4f, - 0x06, 0xe1, 0xc4, 0x1a, 0x84, 0x01, 0x77, 0xbc, 0x80, 0xc6, 0xc3, 0xd5, 0xd2, 0x89, 0x3c, 0x8b, - 0xd1, 0x38, 0xf5, 0x06, 0x94, 0x59, 0x81, 0x33, 0xa1, 0x2c, 0x72, 0x44, 0x99, 0xee, 0x14, 0x1d, - 0x89, 0xe2, 0x90, 0x87, 0xf8, 0x6e, 0x01, 0x23, 0x0b, 0x08, 0x29, 0x20, 0x24, 0xdd, 0xd1, 0xdb, - 0x6e, 0xe8, 0x86, 0xf2, 0xb4, 0x25, 0xaa, 0x0c, 0xa8, 0xdf, 0x76, 0xc3, 0xd0, 0xf5, 0xa9, 0x25, - 0xbb, 0x7e, 0x32, 0xb2, 0xe8, 0x24, 0xe2, 0xd3, 0xfc, 0x65, 0xf7, 0xf2, 0xcb, 0x91, 0x47, 0xfd, - 0xe1, 0x9b, 0x89, 0xc3, 0xc6, 0xd9, 0x09, 0xf3, 0x0b, 0x82, 0xd6, 0xb3, 0x05, 0x0d, 0xc6, 0xd0, - 0x14, 0x9c, 0x1d, 0xd4, 0x45, 0x5b, 0x2d, 0x5b, 0xd6, 0xf8, 0x39, 0x28, 0xbe, 0xd3, 0xa7, 0x3e, - 0xeb, 0xd4, 0xbb, 0x8d, 0x2d, 0xb5, 0xb7, 0x4b, 0x7e, 0x2b, 0x95, 0x2c, 0x27, 0x92, 0x53, 0x09, - 0x3d, 0x0a, 0x78, 0x3c, 0xb5, 0xf3, 0x39, 0xfa, 0x1e, 0xa8, 0x2b, 0x8f, 0xf1, 0xff, 0xd0, 0x18, - 0xd3, 0x69, 0xce, 0x29, 0x4a, 0xdc, 0x86, 0xff, 0x52, 0xc7, 0x4f, 0x68, 0xa7, 0x2e, 0x9f, 0x65, - 0xcd, 0x7e, 0x7d, 0x17, 0x99, 0xf7, 0xe0, 0xc6, 0x09, 0xe5, 0xcb, 0xf1, 0x36, 0x7d, 0x9f, 0x50, - 0xc6, 0xcb, 0x74, 0x9b, 0x67, 0xd0, 0xfe, 0xf9, 0x28, 0x8b, 0xc2, 0x80, 0x89, 0xfb, 0xb4, 0x96, - 0x62, 0x25, 0x40, 0xed, 0x6d, 0x5f, 0xe5, 0x4a, 0x87, 0xcd, 0xd9, 0xf7, 0x3b, 0x35, 0xbb, 0x18, - 0x62, 0x5a, 0x70, 0xf3, 0xd4, 0x63, 0x05, 0x15, 0x5b, 0xc8, 0xd2, 0x40, 0x19, 0x79, 0x3e, 0xa7, - 0x71, 0x2e, 0x2c, 0xef, 0x4c, 0x1f, 0xb4, 0xcb, 0x80, 0x5c, 0x9c, 0x0d, 0x50, 0xd0, 0x76, 0x90, - 0x5c, 0xf8, 0x26, 0xea, 0x56, 0xa6, 0x98, 0xef, 0x40, 0x7b, 0x14, 0x53, 0x87, 0xd3, 0xb5, 0xb5, - 0xfd, 0xfd, 0x55, 0x8c, 0xe1, 0xd6, 0x1a, 0xd7, 0xb5, 0xed, 0xfd, 0x33, 0x02, 0xed, 0x45, 0x34, - 0xfc, 0x27, 0x37, 0xc3, 0x07, 0xa0, 0x26, 0x92, 0x4b, 0xa6, 0x47, 0x3a, 0x53, 0xed, 0xe9, 0x24, - 0x0b, 0x18, 0x59, 0x04, 0x8c, 0x1c, 0x8b, 0x80, 0x3d, 0x75, 0xd8, 0xd8, 0x86, 0xec, 0xb8, 0xa8, - 0xc5, 0x5a, 0xd6, 0x84, 0x5e, 0xdb, 0x5a, 0xb6, 0x41, 0x7b, 0x4c, 0x7d, 0x5a, 0xb2, 0x95, 0x92, - 0x98, 0xf4, 0xce, 0x9b, 0x00, 0x85, 0x11, 0x71, 0x0a, 0x8d, 0x13, 0xca, 0xf1, 0x83, 0x0a, 0x12, - 0x4a, 0x82, 0xa8, 0x3f, 0xbc, 0x32, 0x2e, 0x5f, 0xc3, 0x07, 0x68, 0x8a, 0x48, 0xe0, 0x2a, 0x5f, - 0x97, 0xd2, 0xb0, 0xe9, 0x7b, 0x1b, 0x20, 0x73, 0xf2, 0x8f, 0xa0, 0x64, 0xae, 0xc5, 0x55, 0x86, - 0x94, 0x87, 0x49, 0xdf, 0xdf, 0x04, 0x5a, 0x08, 0xc8, 0xfc, 0x51, 0x49, 0x40, 0xb9, 0xe7, 0x2b, - 0x09, 0xf8, 0x95, 0x0b, 0x5f, 0x83, 0x92, 0x79, 0xa6, 0x92, 0x80, 0x72, 0x7b, 0xe9, 0xda, 0x5a, - 0x1a, 0x8e, 0xc4, 0xbf, 0xe8, 0xf0, 0xed, 0xec, 0xc2, 0xa8, 0x7d, 0xbb, 0x30, 0x6a, 0x9f, 0xe6, - 0x06, 0x9a, 0xcd, 0x0d, 0xf4, 0x75, 0x6e, 0xa0, 0xf3, 0xb9, 0x81, 0x5e, 0x1d, 0xff, 0xc1, 0x2f, - 0xf4, 0xa0, 0xe8, 0x5e, 0xd6, 0xfa, 0x8a, 0xe4, 0xbc, 0xff, 0x23, 0x00, 0x00, 0xff, 0xff, 0x4f, - 0x4a, 0x87, 0xf3, 0x95, 0x07, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto b/vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto index c22eebaf1554a..90e3051238b6f 100644 --- a/vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto +++ b/vendor/github.com/containerd/containerd/api/services/namespaces/v1/namespace.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.namespaces.v1; diff --git a/vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.pb.go b/vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.pb.go index 1693af0ff4bd4..046c97b015fee 100644 --- a/vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.pb.go @@ -1,55 +1,26 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto -/* - Package snapshots is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto - - It has these top-level messages: - PrepareSnapshotRequest - PrepareSnapshotResponse - ViewSnapshotRequest - ViewSnapshotResponse - MountsRequest - MountsResponse - RemoveSnapshotRequest - CommitSnapshotRequest - StatSnapshotRequest - Info - StatSnapshotResponse - UpdateSnapshotRequest - UpdateSnapshotResponse - ListSnapshotsRequest - ListSnapshotsResponse - UsageRequest - UsageResponse -*/ package snapshots -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import google_protobuf1 "github.com/gogo/protobuf/types" -import google_protobuf2 "github.com/gogo/protobuf/types" -import _ "github.com/gogo/protobuf/types" -import containerd_types "github.com/containerd/containerd/api/types" - -import time "time" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + context "context" + fmt "fmt" + types "github.com/containerd/containerd/api/types" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types1 "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -61,7 +32,7 @@ var _ = time.Kitchen // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Kind int32 @@ -78,6 +49,7 @@ var Kind_name = map[int32]string{ 2: "ACTIVE", 3: "COMMITTED", } + var Kind_value = map[string]int32{ "UNKNOWN": 0, "VIEW": 1, @@ -88,7 +60,10 @@ var Kind_value = map[string]int32{ func (x Kind) String() string { return proto.EnumName(Kind_name, int32(x)) } -func (Kind) EnumDescriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{0} } + +func (Kind) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{0} +} type PrepareSnapshotRequest struct { Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` @@ -97,20 +72,82 @@ type PrepareSnapshotRequest struct { // Labels are arbitrary data on snapshots. // // The combined size of a key/value pair cannot exceed 4096 bytes. - Labels map[string]string `protobuf:"bytes,4,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PrepareSnapshotRequest) Reset() { *m = PrepareSnapshotRequest{} } +func (*PrepareSnapshotRequest) ProtoMessage() {} +func (*PrepareSnapshotRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{0} +} +func (m *PrepareSnapshotRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PrepareSnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PrepareSnapshotRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PrepareSnapshotRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PrepareSnapshotRequest.Merge(m, src) +} +func (m *PrepareSnapshotRequest) XXX_Size() int { + return m.Size() +} +func (m *PrepareSnapshotRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PrepareSnapshotRequest.DiscardUnknown(m) } -func (m *PrepareSnapshotRequest) Reset() { *m = PrepareSnapshotRequest{} } -func (*PrepareSnapshotRequest) ProtoMessage() {} -func (*PrepareSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{0} } +var xxx_messageInfo_PrepareSnapshotRequest proto.InternalMessageInfo type PrepareSnapshotResponse struct { - Mounts []*containerd_types.Mount `protobuf:"bytes,1,rep,name=mounts" json:"mounts,omitempty"` + Mounts []*types.Mount `protobuf:"bytes,1,rep,name=mounts,proto3" json:"mounts,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PrepareSnapshotResponse) Reset() { *m = PrepareSnapshotResponse{} } +func (*PrepareSnapshotResponse) ProtoMessage() {} +func (*PrepareSnapshotResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{1} +} +func (m *PrepareSnapshotResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PrepareSnapshotResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PrepareSnapshotResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PrepareSnapshotResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PrepareSnapshotResponse.Merge(m, src) +} +func (m *PrepareSnapshotResponse) XXX_Size() int { + return m.Size() +} +func (m *PrepareSnapshotResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PrepareSnapshotResponse.DiscardUnknown(m) } -func (m *PrepareSnapshotResponse) Reset() { *m = PrepareSnapshotResponse{} } -func (*PrepareSnapshotResponse) ProtoMessage() {} -func (*PrepareSnapshotResponse) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{1} } +var xxx_messageInfo_PrepareSnapshotResponse proto.InternalMessageInfo type ViewSnapshotRequest struct { Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` @@ -119,46 +156,201 @@ type ViewSnapshotRequest struct { // Labels are arbitrary data on snapshots. // // The combined size of a key/value pair cannot exceed 4096 bytes. - Labels map[string]string `protobuf:"bytes,4,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ViewSnapshotRequest) Reset() { *m = ViewSnapshotRequest{} } +func (*ViewSnapshotRequest) ProtoMessage() {} +func (*ViewSnapshotRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{2} +} +func (m *ViewSnapshotRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ViewSnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ViewSnapshotRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ViewSnapshotRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ViewSnapshotRequest.Merge(m, src) +} +func (m *ViewSnapshotRequest) XXX_Size() int { + return m.Size() +} +func (m *ViewSnapshotRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ViewSnapshotRequest.DiscardUnknown(m) } -func (m *ViewSnapshotRequest) Reset() { *m = ViewSnapshotRequest{} } -func (*ViewSnapshotRequest) ProtoMessage() {} -func (*ViewSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{2} } +var xxx_messageInfo_ViewSnapshotRequest proto.InternalMessageInfo type ViewSnapshotResponse struct { - Mounts []*containerd_types.Mount `protobuf:"bytes,1,rep,name=mounts" json:"mounts,omitempty"` + Mounts []*types.Mount `protobuf:"bytes,1,rep,name=mounts,proto3" json:"mounts,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ViewSnapshotResponse) Reset() { *m = ViewSnapshotResponse{} } +func (*ViewSnapshotResponse) ProtoMessage() {} +func (*ViewSnapshotResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{3} +} +func (m *ViewSnapshotResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ViewSnapshotResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ViewSnapshotResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ViewSnapshotResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ViewSnapshotResponse.Merge(m, src) +} +func (m *ViewSnapshotResponse) XXX_Size() int { + return m.Size() +} +func (m *ViewSnapshotResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ViewSnapshotResponse.DiscardUnknown(m) } -func (m *ViewSnapshotResponse) Reset() { *m = ViewSnapshotResponse{} } -func (*ViewSnapshotResponse) ProtoMessage() {} -func (*ViewSnapshotResponse) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{3} } +var xxx_messageInfo_ViewSnapshotResponse proto.InternalMessageInfo type MountsRequest struct { - Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MountsRequest) Reset() { *m = MountsRequest{} } +func (*MountsRequest) ProtoMessage() {} +func (*MountsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{4} +} +func (m *MountsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MountsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MountsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MountsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MountsRequest.Merge(m, src) +} +func (m *MountsRequest) XXX_Size() int { + return m.Size() +} +func (m *MountsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MountsRequest.DiscardUnknown(m) } -func (m *MountsRequest) Reset() { *m = MountsRequest{} } -func (*MountsRequest) ProtoMessage() {} -func (*MountsRequest) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{4} } +var xxx_messageInfo_MountsRequest proto.InternalMessageInfo type MountsResponse struct { - Mounts []*containerd_types.Mount `protobuf:"bytes,1,rep,name=mounts" json:"mounts,omitempty"` + Mounts []*types.Mount `protobuf:"bytes,1,rep,name=mounts,proto3" json:"mounts,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MountsResponse) Reset() { *m = MountsResponse{} } +func (*MountsResponse) ProtoMessage() {} +func (*MountsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{5} +} +func (m *MountsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MountsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MountsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MountsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MountsResponse.Merge(m, src) +} +func (m *MountsResponse) XXX_Size() int { + return m.Size() +} +func (m *MountsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MountsResponse.DiscardUnknown(m) } -func (m *MountsResponse) Reset() { *m = MountsResponse{} } -func (*MountsResponse) ProtoMessage() {} -func (*MountsResponse) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{5} } +var xxx_messageInfo_MountsResponse proto.InternalMessageInfo type RemoveSnapshotRequest struct { - Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RemoveSnapshotRequest) Reset() { *m = RemoveSnapshotRequest{} } +func (*RemoveSnapshotRequest) ProtoMessage() {} +func (*RemoveSnapshotRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{6} +} +func (m *RemoveSnapshotRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveSnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveSnapshotRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveSnapshotRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveSnapshotRequest.Merge(m, src) +} +func (m *RemoveSnapshotRequest) XXX_Size() int { + return m.Size() +} +func (m *RemoveSnapshotRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveSnapshotRequest.DiscardUnknown(m) } -func (m *RemoveSnapshotRequest) Reset() { *m = RemoveSnapshotRequest{} } -func (*RemoveSnapshotRequest) ProtoMessage() {} -func (*RemoveSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{6} } +var xxx_messageInfo_RemoveSnapshotRequest proto.InternalMessageInfo type CommitSnapshotRequest struct { Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` @@ -167,117 +359,482 @@ type CommitSnapshotRequest struct { // Labels are arbitrary data on snapshots. // // The combined size of a key/value pair cannot exceed 4096 bytes. - Labels map[string]string `protobuf:"bytes,4,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CommitSnapshotRequest) Reset() { *m = CommitSnapshotRequest{} } +func (*CommitSnapshotRequest) ProtoMessage() {} +func (*CommitSnapshotRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{7} +} +func (m *CommitSnapshotRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommitSnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommitSnapshotRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CommitSnapshotRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommitSnapshotRequest.Merge(m, src) +} +func (m *CommitSnapshotRequest) XXX_Size() int { + return m.Size() +} +func (m *CommitSnapshotRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CommitSnapshotRequest.DiscardUnknown(m) } -func (m *CommitSnapshotRequest) Reset() { *m = CommitSnapshotRequest{} } -func (*CommitSnapshotRequest) ProtoMessage() {} -func (*CommitSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{7} } +var xxx_messageInfo_CommitSnapshotRequest proto.InternalMessageInfo type StatSnapshotRequest struct { - Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StatSnapshotRequest) Reset() { *m = StatSnapshotRequest{} } +func (*StatSnapshotRequest) ProtoMessage() {} +func (*StatSnapshotRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{8} +} +func (m *StatSnapshotRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StatSnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StatSnapshotRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StatSnapshotRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_StatSnapshotRequest.Merge(m, src) +} +func (m *StatSnapshotRequest) XXX_Size() int { + return m.Size() +} +func (m *StatSnapshotRequest) XXX_DiscardUnknown() { + xxx_messageInfo_StatSnapshotRequest.DiscardUnknown(m) } -func (m *StatSnapshotRequest) Reset() { *m = StatSnapshotRequest{} } -func (*StatSnapshotRequest) ProtoMessage() {} -func (*StatSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{8} } +var xxx_messageInfo_StatSnapshotRequest proto.InternalMessageInfo type Info struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Parent string `protobuf:"bytes,2,opt,name=parent,proto3" json:"parent,omitempty"` Kind Kind `protobuf:"varint,3,opt,name=kind,proto3,enum=containerd.services.snapshots.v1.Kind" json:"kind,omitempty"` // CreatedAt provides the time at which the snapshot was created. - CreatedAt time.Time `protobuf:"bytes,4,opt,name=created_at,json=createdAt,stdtime" json:"created_at"` + CreatedAt time.Time `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at"` // UpdatedAt provides the time the info was last updated. - UpdatedAt time.Time `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,stdtime" json:"updated_at"` + UpdatedAt time.Time `protobuf:"bytes,5,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at"` // Labels are arbitrary data on snapshots. // // The combined size of a key/value pair cannot exceed 4096 bytes. - Labels map[string]string `protobuf:"bytes,6,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,6,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Info) Reset() { *m = Info{} } +func (*Info) ProtoMessage() {} +func (*Info) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{9} +} +func (m *Info) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Info) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Info.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Info) XXX_Merge(src proto.Message) { + xxx_messageInfo_Info.Merge(m, src) +} +func (m *Info) XXX_Size() int { + return m.Size() +} +func (m *Info) XXX_DiscardUnknown() { + xxx_messageInfo_Info.DiscardUnknown(m) } -func (m *Info) Reset() { *m = Info{} } -func (*Info) ProtoMessage() {} -func (*Info) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{9} } +var xxx_messageInfo_Info proto.InternalMessageInfo type StatSnapshotResponse struct { - Info Info `protobuf:"bytes,1,opt,name=info" json:"info"` + Info Info `protobuf:"bytes,1,opt,name=info,proto3" json:"info"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StatSnapshotResponse) Reset() { *m = StatSnapshotResponse{} } +func (*StatSnapshotResponse) ProtoMessage() {} +func (*StatSnapshotResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{10} +} +func (m *StatSnapshotResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StatSnapshotResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StatSnapshotResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StatSnapshotResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_StatSnapshotResponse.Merge(m, src) +} +func (m *StatSnapshotResponse) XXX_Size() int { + return m.Size() +} +func (m *StatSnapshotResponse) XXX_DiscardUnknown() { + xxx_messageInfo_StatSnapshotResponse.DiscardUnknown(m) } -func (m *StatSnapshotResponse) Reset() { *m = StatSnapshotResponse{} } -func (*StatSnapshotResponse) ProtoMessage() {} -func (*StatSnapshotResponse) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{10} } +var xxx_messageInfo_StatSnapshotResponse proto.InternalMessageInfo type UpdateSnapshotRequest struct { Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` - Info Info `protobuf:"bytes,2,opt,name=info" json:"info"` + Info Info `protobuf:"bytes,2,opt,name=info,proto3" json:"info"` // UpdateMask specifies which fields to perform the update on. If empty, // the operation applies to all fields. // // In info, Name, Parent, Kind, Created are immutable, // other field may be updated using this mask. // If no mask is provided, all mutable field are updated. - UpdateMask *google_protobuf2.FieldMask `protobuf:"bytes,3,opt,name=update_mask,json=updateMask" json:"update_mask,omitempty"` + UpdateMask *types1.FieldMask `protobuf:"bytes,3,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateSnapshotRequest) Reset() { *m = UpdateSnapshotRequest{} } +func (*UpdateSnapshotRequest) ProtoMessage() {} +func (*UpdateSnapshotRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{11} +} +func (m *UpdateSnapshotRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateSnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateSnapshotRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateSnapshotRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateSnapshotRequest.Merge(m, src) +} +func (m *UpdateSnapshotRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateSnapshotRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateSnapshotRequest.DiscardUnknown(m) } -func (m *UpdateSnapshotRequest) Reset() { *m = UpdateSnapshotRequest{} } -func (*UpdateSnapshotRequest) ProtoMessage() {} -func (*UpdateSnapshotRequest) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{11} } +var xxx_messageInfo_UpdateSnapshotRequest proto.InternalMessageInfo type UpdateSnapshotResponse struct { - Info Info `protobuf:"bytes,1,opt,name=info" json:"info"` + Info Info `protobuf:"bytes,1,opt,name=info,proto3" json:"info"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateSnapshotResponse) Reset() { *m = UpdateSnapshotResponse{} } +func (*UpdateSnapshotResponse) ProtoMessage() {} +func (*UpdateSnapshotResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{12} +} +func (m *UpdateSnapshotResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateSnapshotResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateSnapshotResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateSnapshotResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateSnapshotResponse.Merge(m, src) +} +func (m *UpdateSnapshotResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateSnapshotResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateSnapshotResponse.DiscardUnknown(m) } -func (m *UpdateSnapshotResponse) Reset() { *m = UpdateSnapshotResponse{} } -func (*UpdateSnapshotResponse) ProtoMessage() {} -func (*UpdateSnapshotResponse) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{12} } +var xxx_messageInfo_UpdateSnapshotResponse proto.InternalMessageInfo type ListSnapshotsRequest struct { Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` + // Filters contains one or more filters using the syntax defined in the + // containerd filter package. + // + // The returned result will be those that match any of the provided + // filters. Expanded, images that match the following will be + // returned: + // + // filters[0] or filters[1] or ... or filters[n-1] or filters[n] + // + // If filters is zero-length or nil, all items will be returned. + Filters []string `protobuf:"bytes,2,rep,name=filters,proto3" json:"filters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListSnapshotsRequest) Reset() { *m = ListSnapshotsRequest{} } +func (*ListSnapshotsRequest) ProtoMessage() {} +func (*ListSnapshotsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{13} +} +func (m *ListSnapshotsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListSnapshotsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListSnapshotsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListSnapshotsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListSnapshotsRequest.Merge(m, src) +} +func (m *ListSnapshotsRequest) XXX_Size() int { + return m.Size() +} +func (m *ListSnapshotsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListSnapshotsRequest.DiscardUnknown(m) } -func (m *ListSnapshotsRequest) Reset() { *m = ListSnapshotsRequest{} } -func (*ListSnapshotsRequest) ProtoMessage() {} -func (*ListSnapshotsRequest) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{13} } +var xxx_messageInfo_ListSnapshotsRequest proto.InternalMessageInfo type ListSnapshotsResponse struct { - Info []Info `protobuf:"bytes,1,rep,name=info" json:"info"` + Info []Info `protobuf:"bytes,1,rep,name=info,proto3" json:"info"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListSnapshotsResponse) Reset() { *m = ListSnapshotsResponse{} } +func (*ListSnapshotsResponse) ProtoMessage() {} +func (*ListSnapshotsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{14} +} +func (m *ListSnapshotsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListSnapshotsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListSnapshotsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListSnapshotsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListSnapshotsResponse.Merge(m, src) +} +func (m *ListSnapshotsResponse) XXX_Size() int { + return m.Size() +} +func (m *ListSnapshotsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListSnapshotsResponse.DiscardUnknown(m) } -func (m *ListSnapshotsResponse) Reset() { *m = ListSnapshotsResponse{} } -func (*ListSnapshotsResponse) ProtoMessage() {} -func (*ListSnapshotsResponse) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{14} } +var xxx_messageInfo_ListSnapshotsResponse proto.InternalMessageInfo type UsageRequest struct { - Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UsageRequest) Reset() { *m = UsageRequest{} } +func (*UsageRequest) ProtoMessage() {} +func (*UsageRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{15} +} +func (m *UsageRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UsageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UsageRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UsageRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UsageRequest.Merge(m, src) +} +func (m *UsageRequest) XXX_Size() int { + return m.Size() +} +func (m *UsageRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UsageRequest.DiscardUnknown(m) } -func (m *UsageRequest) Reset() { *m = UsageRequest{} } -func (*UsageRequest) ProtoMessage() {} -func (*UsageRequest) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{15} } +var xxx_messageInfo_UsageRequest proto.InternalMessageInfo type UsageResponse struct { - Size_ int64 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` - Inodes int64 `protobuf:"varint,2,opt,name=inodes,proto3" json:"inodes,omitempty"` + Size_ int64 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` + Inodes int64 `protobuf:"varint,2,opt,name=inodes,proto3" json:"inodes,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UsageResponse) Reset() { *m = UsageResponse{} } +func (*UsageResponse) ProtoMessage() {} +func (*UsageResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{16} +} +func (m *UsageResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UsageResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UsageResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UsageResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UsageResponse.Merge(m, src) +} +func (m *UsageResponse) XXX_Size() int { + return m.Size() +} +func (m *UsageResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UsageResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_UsageResponse proto.InternalMessageInfo + +type CleanupRequest struct { + Snapshotter string `protobuf:"bytes,1,opt,name=snapshotter,proto3" json:"snapshotter,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CleanupRequest) Reset() { *m = CleanupRequest{} } +func (*CleanupRequest) ProtoMessage() {} +func (*CleanupRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cfc0ddf12791f168, []int{17} +} +func (m *CleanupRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CleanupRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CleanupRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CleanupRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CleanupRequest.Merge(m, src) +} +func (m *CleanupRequest) XXX_Size() int { + return m.Size() +} +func (m *CleanupRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CleanupRequest.DiscardUnknown(m) } -func (m *UsageResponse) Reset() { *m = UsageResponse{} } -func (*UsageResponse) ProtoMessage() {} -func (*UsageResponse) Descriptor() ([]byte, []int) { return fileDescriptorSnapshots, []int{16} } +var xxx_messageInfo_CleanupRequest proto.InternalMessageInfo func init() { + proto.RegisterEnum("containerd.services.snapshots.v1.Kind", Kind_name, Kind_value) proto.RegisterType((*PrepareSnapshotRequest)(nil), "containerd.services.snapshots.v1.PrepareSnapshotRequest") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.snapshots.v1.PrepareSnapshotRequest.LabelsEntry") proto.RegisterType((*PrepareSnapshotResponse)(nil), "containerd.services.snapshots.v1.PrepareSnapshotResponse") proto.RegisterType((*ViewSnapshotRequest)(nil), "containerd.services.snapshots.v1.ViewSnapshotRequest") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.snapshots.v1.ViewSnapshotRequest.LabelsEntry") proto.RegisterType((*ViewSnapshotResponse)(nil), "containerd.services.snapshots.v1.ViewSnapshotResponse") proto.RegisterType((*MountsRequest)(nil), "containerd.services.snapshots.v1.MountsRequest") proto.RegisterType((*MountsResponse)(nil), "containerd.services.snapshots.v1.MountsResponse") proto.RegisterType((*RemoveSnapshotRequest)(nil), "containerd.services.snapshots.v1.RemoveSnapshotRequest") proto.RegisterType((*CommitSnapshotRequest)(nil), "containerd.services.snapshots.v1.CommitSnapshotRequest") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.snapshots.v1.CommitSnapshotRequest.LabelsEntry") proto.RegisterType((*StatSnapshotRequest)(nil), "containerd.services.snapshots.v1.StatSnapshotRequest") proto.RegisterType((*Info)(nil), "containerd.services.snapshots.v1.Info") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.snapshots.v1.Info.LabelsEntry") proto.RegisterType((*StatSnapshotResponse)(nil), "containerd.services.snapshots.v1.StatSnapshotResponse") proto.RegisterType((*UpdateSnapshotRequest)(nil), "containerd.services.snapshots.v1.UpdateSnapshotRequest") proto.RegisterType((*UpdateSnapshotResponse)(nil), "containerd.services.snapshots.v1.UpdateSnapshotResponse") @@ -285,7 +842,81 @@ func init() { proto.RegisterType((*ListSnapshotsResponse)(nil), "containerd.services.snapshots.v1.ListSnapshotsResponse") proto.RegisterType((*UsageRequest)(nil), "containerd.services.snapshots.v1.UsageRequest") proto.RegisterType((*UsageResponse)(nil), "containerd.services.snapshots.v1.UsageResponse") - proto.RegisterEnum("containerd.services.snapshots.v1.Kind", Kind_name, Kind_value) + proto.RegisterType((*CleanupRequest)(nil), "containerd.services.snapshots.v1.CleanupRequest") +} + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto", fileDescriptor_cfc0ddf12791f168) +} + +var fileDescriptor_cfc0ddf12791f168 = []byte{ + // 1047 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xf7, 0xda, 0x5b, 0x27, 0x79, 0x4e, 0x82, 0x99, 0x3a, 0xae, 0xb5, 0x20, 0x67, 0xe5, 0x03, + 0x8a, 0x38, 0xec, 0xb6, 0x46, 0xb4, 0x69, 0x73, 0xc1, 0x71, 0x0c, 0x72, 0xd2, 0xa4, 0x68, 0xf3, + 0xa7, 0x4d, 0x41, 0x54, 0x1b, 0x7b, 0xec, 0xac, 0xec, 0xfd, 0x83, 0x67, 0xec, 0xca, 0x20, 0x21, + 0x8e, 0x55, 0x4e, 0x7c, 0x81, 0x9c, 0xe0, 0x43, 0x20, 0x3e, 0x41, 0x8e, 0x48, 0x5c, 0x38, 0x01, + 0xcd, 0x97, 0xe0, 0x84, 0x40, 0x33, 0x3b, 0xeb, 0x7f, 0x71, 0xe5, 0xb5, 0x6b, 0x6e, 0x33, 0x3b, + 0xf3, 0xde, 0xfb, 0xbd, 0xdf, 0x9b, 0xf7, 0x9b, 0x59, 0xd8, 0xad, 0x5b, 0xf4, 0xbc, 0x7d, 0xa6, + 0x55, 0x5c, 0x5b, 0xaf, 0xb8, 0x0e, 0x35, 0x2d, 0x07, 0xb7, 0xaa, 0x83, 0x43, 0xd3, 0xb3, 0x74, + 0x82, 0x5b, 0x1d, 0xab, 0x82, 0x89, 0x4e, 0x1c, 0xd3, 0x23, 0xe7, 0x2e, 0x25, 0x7a, 0xe7, 0x5e, + 0x7f, 0xa2, 0x79, 0x2d, 0x97, 0xba, 0x48, 0xed, 0x5b, 0x69, 0x81, 0x85, 0xd6, 0xdf, 0xd4, 0xb9, + 0xa7, 0xa4, 0xea, 0x6e, 0xdd, 0xe5, 0x9b, 0x75, 0x36, 0xf2, 0xed, 0x94, 0xf7, 0xea, 0xae, 0x5b, + 0x6f, 0x62, 0x9d, 0xcf, 0xce, 0xda, 0x35, 0x1d, 0xdb, 0x1e, 0xed, 0x8a, 0x45, 0x75, 0x74, 0xb1, + 0x66, 0xe1, 0x66, 0xf5, 0x85, 0x6d, 0x92, 0x86, 0xd8, 0xb1, 0x3e, 0xba, 0x83, 0x5a, 0x36, 0x26, + 0xd4, 0xb4, 0x3d, 0xb1, 0xe1, 0x7e, 0xa8, 0x1c, 0x69, 0xd7, 0xc3, 0x44, 0xb7, 0xdd, 0xb6, 0x43, + 0x7d, 0xbb, 0xdc, 0x3f, 0x12, 0xa4, 0x3f, 0x6f, 0x61, 0xcf, 0x6c, 0xe1, 0x43, 0x91, 0x85, 0x81, + 0xbf, 0x6e, 0x63, 0x42, 0x91, 0x0a, 0x89, 0x20, 0x31, 0x8a, 0x5b, 0x19, 0x49, 0x95, 0x36, 0x96, + 0x8c, 0xc1, 0x4f, 0x28, 0x09, 0xb1, 0x06, 0xee, 0x66, 0xa2, 0x7c, 0x85, 0x0d, 0x51, 0x1a, 0xe2, + 0xcc, 0x95, 0x43, 0x33, 0x31, 0xfe, 0x51, 0xcc, 0xd0, 0x97, 0x10, 0x6f, 0x9a, 0x67, 0xb8, 0x49, + 0x32, 0xb2, 0x1a, 0xdb, 0x48, 0xe4, 0x77, 0xb4, 0x49, 0x3c, 0x6a, 0xe3, 0x51, 0x69, 0x8f, 0xb9, + 0x9b, 0x92, 0x43, 0x5b, 0x5d, 0x43, 0xf8, 0x54, 0x1e, 0x42, 0x62, 0xe0, 0x73, 0x00, 0x4b, 0xea, + 0xc3, 0x4a, 0xc1, 0xad, 0x8e, 0xd9, 0x6c, 0x63, 0x01, 0xd5, 0x9f, 0x3c, 0x8a, 0x6e, 0x4a, 0xb9, + 0x5d, 0xb8, 0x73, 0x23, 0x10, 0xf1, 0x5c, 0x87, 0x60, 0xa4, 0x43, 0x9c, 0x33, 0x45, 0x32, 0x12, + 0xc7, 0x7c, 0x67, 0x10, 0x33, 0x67, 0x52, 0xdb, 0x67, 0xeb, 0x86, 0xd8, 0x96, 0xfb, 0x5b, 0x82, + 0xdb, 0x27, 0x16, 0x7e, 0xf9, 0x7f, 0x12, 0x79, 0x3a, 0x42, 0x64, 0x61, 0x32, 0x91, 0x63, 0x20, + 0xcd, 0x9b, 0xc5, 0xcf, 0x20, 0x35, 0x1c, 0x65, 0x56, 0x0a, 0x8b, 0xb0, 0xc2, 0x3f, 0x90, 0xb7, + 0xe0, 0x2e, 0x57, 0x80, 0xd5, 0xc0, 0xc9, 0xac, 0x38, 0xf6, 0x60, 0xcd, 0xc0, 0xb6, 0xdb, 0x99, + 0x47, 0x53, 0xb0, 0x73, 0xb1, 0x56, 0x74, 0x6d, 0xdb, 0xa2, 0xd3, 0x7b, 0x43, 0x20, 0x3b, 0xa6, + 0x1d, 0x50, 0xce, 0xc7, 0x41, 0x84, 0x58, 0xbf, 0x32, 0x5f, 0x8c, 0x9c, 0x8a, 0xe2, 0xe4, 0x53, + 0x31, 0x16, 0xd0, 0xbc, 0xcf, 0x45, 0x19, 0x6e, 0x1f, 0x52, 0x93, 0xce, 0x83, 0xc4, 0x7f, 0xa3, + 0x20, 0x97, 0x9d, 0x9a, 0xdb, 0x63, 0x44, 0x1a, 0x60, 0xa4, 0xdf, 0x2d, 0xd1, 0xa1, 0x6e, 0x79, + 0x04, 0x72, 0xc3, 0x72, 0xaa, 0x9c, 0xaa, 0xd5, 0xfc, 0x07, 0x93, 0x59, 0xd9, 0xb3, 0x9c, 0xaa, + 0xc1, 0x6d, 0x50, 0x11, 0xa0, 0xd2, 0xc2, 0x26, 0xc5, 0xd5, 0x17, 0x26, 0xcd, 0xc8, 0xaa, 0xb4, + 0x91, 0xc8, 0x2b, 0x9a, 0xaf, 0xc3, 0x5a, 0xa0, 0xc3, 0xda, 0x51, 0xa0, 0xc3, 0xdb, 0x8b, 0x57, + 0x7f, 0xac, 0x47, 0x7e, 0xf8, 0x73, 0x5d, 0x32, 0x96, 0x84, 0x5d, 0x81, 0x32, 0x27, 0x6d, 0xaf, + 0x1a, 0x38, 0xb9, 0x35, 0x8d, 0x13, 0x61, 0x57, 0xa0, 0x68, 0xb7, 0x57, 0xdd, 0x38, 0xaf, 0x6e, + 0x7e, 0x72, 0x1e, 0x8c, 0xa9, 0x79, 0x17, 0xf3, 0x19, 0xa4, 0x86, 0x8b, 0x29, 0x9a, 0xeb, 0x13, + 0x90, 0x2d, 0xa7, 0xe6, 0x72, 0x27, 0x89, 0x30, 0x24, 0x33, 0x70, 0xdb, 0x32, 0xcb, 0xd4, 0xe0, + 0x96, 0xb9, 0x9f, 0x25, 0x58, 0x3b, 0xe6, 0xe9, 0x4e, 0x7f, 0x52, 0x82, 0xe8, 0xd1, 0x59, 0xa3, + 0xa3, 0x2d, 0x48, 0xf8, 0x5c, 0xf3, 0x0b, 0x97, 0x9f, 0x95, 0x71, 0x45, 0xfa, 0x94, 0xdd, 0xc9, + 0xfb, 0x26, 0x69, 0x18, 0xa2, 0xa4, 0x6c, 0x9c, 0x7b, 0x0e, 0xe9, 0x51, 0xe4, 0x73, 0xa3, 0xc5, + 0x80, 0xd4, 0x63, 0x8b, 0xf4, 0x08, 0x9f, 0x42, 0x13, 0x33, 0xb0, 0x50, 0xb3, 0x9a, 0x14, 0xb7, + 0x48, 0x26, 0xaa, 0xc6, 0x36, 0x96, 0x8c, 0x60, 0x9a, 0x3b, 0x85, 0xb5, 0x11, 0x9f, 0x37, 0xe0, + 0xc6, 0x66, 0x84, 0xbb, 0x0d, 0xcb, 0xc7, 0xc4, 0xac, 0xe3, 0xb7, 0xe9, 0xf2, 0x2d, 0x58, 0x11, + 0x3e, 0x04, 0x2c, 0x04, 0x32, 0xb1, 0xbe, 0xf1, 0xbb, 0x3d, 0x66, 0xf0, 0x31, 0xeb, 0x76, 0xcb, + 0x71, 0xab, 0x98, 0x70, 0xcb, 0x98, 0x21, 0x66, 0xb9, 0x3c, 0xac, 0x16, 0x9b, 0xd8, 0x74, 0xda, + 0x5e, 0x68, 0x08, 0x1f, 0xbe, 0x92, 0x40, 0x66, 0x4d, 0x8f, 0xde, 0x87, 0x85, 0xe3, 0x83, 0xbd, + 0x83, 0x27, 0x4f, 0x0f, 0x92, 0x11, 0xe5, 0x9d, 0x8b, 0x4b, 0x35, 0xc1, 0x3e, 0x1f, 0x3b, 0x0d, + 0xc7, 0x7d, 0xe9, 0xa0, 0x34, 0xc8, 0x27, 0xe5, 0xd2, 0xd3, 0xa4, 0xa4, 0x2c, 0x5f, 0x5c, 0xaa, + 0x8b, 0x6c, 0x89, 0x5d, 0x78, 0x48, 0x81, 0x78, 0xa1, 0x78, 0x54, 0x3e, 0x29, 0x25, 0xa3, 0xca, + 0xea, 0xc5, 0xa5, 0x0a, 0x6c, 0xa5, 0x50, 0xa1, 0x56, 0x07, 0x23, 0x15, 0x96, 0x8a, 0x4f, 0xf6, + 0xf7, 0xcb, 0x47, 0x47, 0xa5, 0x9d, 0x64, 0x4c, 0x79, 0xf7, 0xe2, 0x52, 0x5d, 0x61, 0xcb, 0xbe, + 0xf2, 0x52, 0x5c, 0x55, 0x96, 0x5f, 0xfd, 0x98, 0x8d, 0xfc, 0xf2, 0x53, 0x96, 0x23, 0xc8, 0xff, + 0xb6, 0x08, 0x4b, 0xbd, 0xba, 0xa0, 0xef, 0x60, 0x41, 0x3c, 0x4c, 0xd0, 0xe6, 0xac, 0x8f, 0x25, + 0xe5, 0xe1, 0x0c, 0x96, 0x82, 0xf8, 0x36, 0xc8, 0x3c, 0xc3, 0x8f, 0x67, 0x7a, 0x60, 0x28, 0xf7, + 0xa7, 0x35, 0x13, 0x61, 0x1b, 0x10, 0xf7, 0xef, 0x6e, 0xa4, 0x4f, 0xf6, 0x30, 0xf4, 0x54, 0x50, + 0xee, 0x86, 0x37, 0x10, 0xc1, 0x4e, 0x21, 0xee, 0x17, 0x03, 0x3d, 0x98, 0xf1, 0xc2, 0x54, 0xd2, + 0x37, 0x74, 0xa2, 0xc4, 0x1e, 0xf6, 0xcc, 0xb5, 0xff, 0x80, 0x08, 0xe3, 0x7a, 0xec, 0x53, 0xe3, + 0x8d, 0xae, 0xdb, 0x20, 0x33, 0x1d, 0x0e, 0x53, 0x99, 0x31, 0x97, 0x6f, 0x98, 0xca, 0x8c, 0x95, + 0xf9, 0x6f, 0x21, 0xee, 0x2b, 0x5d, 0x98, 0x8c, 0xc6, 0xaa, 0xb9, 0xb2, 0x39, 0xbd, 0xa1, 0x08, + 0xde, 0x05, 0x99, 0xc9, 0x16, 0x0a, 0x01, 0x7e, 0x9c, 0x64, 0x2a, 0x0f, 0xa6, 0xb6, 0xf3, 0x03, + 0xdf, 0x95, 0xd0, 0x39, 0xdc, 0xe2, 0x92, 0x84, 0xb4, 0x10, 0xe8, 0x07, 0xf4, 0x4f, 0xd1, 0x43, + 0xef, 0x17, 0x49, 0x1e, 0xc2, 0x82, 0xd0, 0x2f, 0x14, 0xe2, 0x2c, 0x0f, 0x4b, 0xdd, 0x9b, 0x4e, + 0xcb, 0xf6, 0x57, 0x57, 0xaf, 0xb3, 0x91, 0xdf, 0x5f, 0x67, 0x23, 0xdf, 0x5f, 0x67, 0xa5, 0xab, + 0xeb, 0xac, 0xf4, 0xeb, 0x75, 0x56, 0xfa, 0xeb, 0x3a, 0x2b, 0x3d, 0xdf, 0x99, 0xfd, 0xb7, 0x78, + 0xab, 0x37, 0x79, 0x16, 0x39, 0x8b, 0xf3, 0x88, 0x1f, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0x20, + 0x9c, 0x85, 0x7f, 0x67, 0x0f, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -296,18 +927,20 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Snapshots service - +// SnapshotsClient is the client API for Snapshots service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type SnapshotsClient interface { Prepare(ctx context.Context, in *PrepareSnapshotRequest, opts ...grpc.CallOption) (*PrepareSnapshotResponse, error) View(ctx context.Context, in *ViewSnapshotRequest, opts ...grpc.CallOption) (*ViewSnapshotResponse, error) Mounts(ctx context.Context, in *MountsRequest, opts ...grpc.CallOption) (*MountsResponse, error) - Commit(ctx context.Context, in *CommitSnapshotRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) - Remove(ctx context.Context, in *RemoveSnapshotRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + Commit(ctx context.Context, in *CommitSnapshotRequest, opts ...grpc.CallOption) (*types1.Empty, error) + Remove(ctx context.Context, in *RemoveSnapshotRequest, opts ...grpc.CallOption) (*types1.Empty, error) Stat(ctx context.Context, in *StatSnapshotRequest, opts ...grpc.CallOption) (*StatSnapshotResponse, error) Update(ctx context.Context, in *UpdateSnapshotRequest, opts ...grpc.CallOption) (*UpdateSnapshotResponse, error) List(ctx context.Context, in *ListSnapshotsRequest, opts ...grpc.CallOption) (Snapshots_ListClient, error) Usage(ctx context.Context, in *UsageRequest, opts ...grpc.CallOption) (*UsageResponse, error) + Cleanup(ctx context.Context, in *CleanupRequest, opts ...grpc.CallOption) (*types1.Empty, error) } type snapshotsClient struct { @@ -320,7 +953,7 @@ func NewSnapshotsClient(cc *grpc.ClientConn) SnapshotsClient { func (c *snapshotsClient) Prepare(ctx context.Context, in *PrepareSnapshotRequest, opts ...grpc.CallOption) (*PrepareSnapshotResponse, error) { out := new(PrepareSnapshotResponse) - err := grpc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Prepare", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Prepare", in, out, opts...) if err != nil { return nil, err } @@ -329,7 +962,7 @@ func (c *snapshotsClient) Prepare(ctx context.Context, in *PrepareSnapshotReques func (c *snapshotsClient) View(ctx context.Context, in *ViewSnapshotRequest, opts ...grpc.CallOption) (*ViewSnapshotResponse, error) { out := new(ViewSnapshotResponse) - err := grpc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/View", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/View", in, out, opts...) if err != nil { return nil, err } @@ -338,25 +971,25 @@ func (c *snapshotsClient) View(ctx context.Context, in *ViewSnapshotRequest, opt func (c *snapshotsClient) Mounts(ctx context.Context, in *MountsRequest, opts ...grpc.CallOption) (*MountsResponse, error) { out := new(MountsResponse) - err := grpc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Mounts", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Mounts", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *snapshotsClient) Commit(ctx context.Context, in *CommitSnapshotRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { - out := new(google_protobuf1.Empty) - err := grpc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Commit", in, out, c.cc, opts...) +func (c *snapshotsClient) Commit(ctx context.Context, in *CommitSnapshotRequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Commit", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *snapshotsClient) Remove(ctx context.Context, in *RemoveSnapshotRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { - out := new(google_protobuf1.Empty) - err := grpc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Remove", in, out, c.cc, opts...) +func (c *snapshotsClient) Remove(ctx context.Context, in *RemoveSnapshotRequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Remove", in, out, opts...) if err != nil { return nil, err } @@ -365,7 +998,7 @@ func (c *snapshotsClient) Remove(ctx context.Context, in *RemoveSnapshotRequest, func (c *snapshotsClient) Stat(ctx context.Context, in *StatSnapshotRequest, opts ...grpc.CallOption) (*StatSnapshotResponse, error) { out := new(StatSnapshotResponse) - err := grpc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Stat", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Stat", in, out, opts...) if err != nil { return nil, err } @@ -374,7 +1007,7 @@ func (c *snapshotsClient) Stat(ctx context.Context, in *StatSnapshotRequest, opt func (c *snapshotsClient) Update(ctx context.Context, in *UpdateSnapshotRequest, opts ...grpc.CallOption) (*UpdateSnapshotResponse, error) { out := new(UpdateSnapshotResponse) - err := grpc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Update", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Update", in, out, opts...) if err != nil { return nil, err } @@ -382,7 +1015,7 @@ func (c *snapshotsClient) Update(ctx context.Context, in *UpdateSnapshotRequest, } func (c *snapshotsClient) List(ctx context.Context, in *ListSnapshotsRequest, opts ...grpc.CallOption) (Snapshots_ListClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Snapshots_serviceDesc.Streams[0], c.cc, "/containerd.services.snapshots.v1.Snapshots/List", opts...) + stream, err := c.cc.NewStream(ctx, &_Snapshots_serviceDesc.Streams[0], "/containerd.services.snapshots.v1.Snapshots/List", opts...) if err != nil { return nil, err } @@ -415,25 +1048,69 @@ func (x *snapshotsListClient) Recv() (*ListSnapshotsResponse, error) { func (c *snapshotsClient) Usage(ctx context.Context, in *UsageRequest, opts ...grpc.CallOption) (*UsageResponse, error) { out := new(UsageResponse) - err := grpc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Usage", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Usage", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Snapshots service +func (c *snapshotsClient) Cleanup(ctx context.Context, in *CleanupRequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.snapshots.v1.Snapshots/Cleanup", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} +// SnapshotsServer is the server API for Snapshots service. type SnapshotsServer interface { Prepare(context.Context, *PrepareSnapshotRequest) (*PrepareSnapshotResponse, error) View(context.Context, *ViewSnapshotRequest) (*ViewSnapshotResponse, error) Mounts(context.Context, *MountsRequest) (*MountsResponse, error) - Commit(context.Context, *CommitSnapshotRequest) (*google_protobuf1.Empty, error) - Remove(context.Context, *RemoveSnapshotRequest) (*google_protobuf1.Empty, error) + Commit(context.Context, *CommitSnapshotRequest) (*types1.Empty, error) + Remove(context.Context, *RemoveSnapshotRequest) (*types1.Empty, error) Stat(context.Context, *StatSnapshotRequest) (*StatSnapshotResponse, error) Update(context.Context, *UpdateSnapshotRequest) (*UpdateSnapshotResponse, error) List(*ListSnapshotsRequest, Snapshots_ListServer) error Usage(context.Context, *UsageRequest) (*UsageResponse, error) + Cleanup(context.Context, *CleanupRequest) (*types1.Empty, error) +} + +// UnimplementedSnapshotsServer can be embedded to have forward compatible implementations. +type UnimplementedSnapshotsServer struct { +} + +func (*UnimplementedSnapshotsServer) Prepare(ctx context.Context, req *PrepareSnapshotRequest) (*PrepareSnapshotResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Prepare not implemented") +} +func (*UnimplementedSnapshotsServer) View(ctx context.Context, req *ViewSnapshotRequest) (*ViewSnapshotResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method View not implemented") +} +func (*UnimplementedSnapshotsServer) Mounts(ctx context.Context, req *MountsRequest) (*MountsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Mounts not implemented") +} +func (*UnimplementedSnapshotsServer) Commit(ctx context.Context, req *CommitSnapshotRequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Commit not implemented") +} +func (*UnimplementedSnapshotsServer) Remove(ctx context.Context, req *RemoveSnapshotRequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Remove not implemented") +} +func (*UnimplementedSnapshotsServer) Stat(ctx context.Context, req *StatSnapshotRequest) (*StatSnapshotResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Stat not implemented") +} +func (*UnimplementedSnapshotsServer) Update(ctx context.Context, req *UpdateSnapshotRequest) (*UpdateSnapshotResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (*UnimplementedSnapshotsServer) List(req *ListSnapshotsRequest, srv Snapshots_ListServer) error { + return status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (*UnimplementedSnapshotsServer) Usage(ctx context.Context, req *UsageRequest) (*UsageResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Usage not implemented") +} +func (*UnimplementedSnapshotsServer) Cleanup(ctx context.Context, req *CleanupRequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Cleanup not implemented") } func RegisterSnapshotsServer(s *grpc.Server, srv SnapshotsServer) { @@ -605,6 +1282,24 @@ func _Snapshots_Usage_Handler(srv interface{}, ctx context.Context, dec func(int return interceptor(ctx, in, info, handler) } +func _Snapshots_Cleanup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CleanupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SnapshotsServer).Cleanup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/containerd.services.snapshots.v1.Snapshots/Cleanup", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SnapshotsServer).Cleanup(ctx, req.(*CleanupRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Snapshots_serviceDesc = grpc.ServiceDesc{ ServiceName: "containerd.services.snapshots.v1.Snapshots", HandlerType: (*SnapshotsServer)(nil), @@ -641,6 +1336,10 @@ var _Snapshots_serviceDesc = grpc.ServiceDesc{ MethodName: "Usage", Handler: _Snapshots_Usage_Handler, }, + { + MethodName: "Cleanup", + Handler: _Snapshots_Cleanup_Handler, + }, }, Streams: []grpc.StreamDesc{ { @@ -655,7 +1354,7 @@ var _Snapshots_serviceDesc = grpc.ServiceDesc{ func (m *PrepareSnapshotRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -663,52 +1362,66 @@ func (m *PrepareSnapshotRequest) Marshal() (dAtA []byte, err error) { } func (m *PrepareSnapshotRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PrepareSnapshotRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Snapshotter) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) - i += copy(dAtA[i:], m.Snapshotter) - } - if len(m.Key) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) - } - if len(m.Parent) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Parent))) - i += copy(dAtA[i:], m.Parent) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x22 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovSnapshots(uint64(len(k))) + 1 + len(v) + sovSnapshots(uint64(len(v))) - i = encodeVarintSnapshots(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) i = encodeVarintSnapshots(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintSnapshots(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintSnapshots(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 } } - return i, nil + if len(m.Parent) > 0 { + i -= len(m.Parent) + copy(dAtA[i:], m.Parent) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Parent))) + i-- + dAtA[i] = 0x1a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x12 + } + if len(m.Snapshotter) > 0 { + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *PrepareSnapshotResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -716,29 +1429,40 @@ func (m *PrepareSnapshotResponse) Marshal() (dAtA []byte, err error) { } func (m *PrepareSnapshotResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PrepareSnapshotResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Mounts) > 0 { - for _, msg := range m.Mounts { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Mounts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Mounts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshots(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *ViewSnapshotRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -746,52 +1470,66 @@ func (m *ViewSnapshotRequest) Marshal() (dAtA []byte, err error) { } func (m *ViewSnapshotRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ViewSnapshotRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Snapshotter) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) - i += copy(dAtA[i:], m.Snapshotter) - } - if len(m.Key) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) - } - if len(m.Parent) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Parent))) - i += copy(dAtA[i:], m.Parent) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x22 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovSnapshots(uint64(len(k))) + 1 + len(v) + sovSnapshots(uint64(len(v))) - i = encodeVarintSnapshots(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) i = encodeVarintSnapshots(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintSnapshots(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintSnapshots(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 } } - return i, nil + if len(m.Parent) > 0 { + i -= len(m.Parent) + copy(dAtA[i:], m.Parent) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Parent))) + i-- + dAtA[i] = 0x1a + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x12 + } + if len(m.Snapshotter) > 0 { + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *ViewSnapshotResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -799,29 +1537,40 @@ func (m *ViewSnapshotResponse) Marshal() (dAtA []byte, err error) { } func (m *ViewSnapshotResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ViewSnapshotResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Mounts) > 0 { - for _, msg := range m.Mounts { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Mounts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Mounts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshots(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *MountsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -829,29 +1578,40 @@ func (m *MountsRequest) Marshal() (dAtA []byte, err error) { } func (m *MountsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MountsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Snapshotter) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) - i += copy(dAtA[i:], m.Snapshotter) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Key) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Key) + copy(dAtA[i:], m.Key) i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.Snapshotter) > 0 { + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *MountsResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -859,29 +1619,40 @@ func (m *MountsResponse) Marshal() (dAtA []byte, err error) { } func (m *MountsResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MountsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Mounts) > 0 { - for _, msg := range m.Mounts { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Mounts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Mounts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshots(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *RemoveSnapshotRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -889,29 +1660,40 @@ func (m *RemoveSnapshotRequest) Marshal() (dAtA []byte, err error) { } func (m *RemoveSnapshotRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveSnapshotRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Snapshotter) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) - i += copy(dAtA[i:], m.Snapshotter) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Key) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Key) + copy(dAtA[i:], m.Key) i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) + i-- + dAtA[i] = 0x12 + } + if len(m.Snapshotter) > 0 { + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *CommitSnapshotRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -919,52 +1701,66 @@ func (m *CommitSnapshotRequest) Marshal() (dAtA []byte, err error) { } func (m *CommitSnapshotRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommitSnapshotRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Snapshotter) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) - i += copy(dAtA[i:], m.Snapshotter) - } - if len(m.Name) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } - if len(m.Key) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x22 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovSnapshots(uint64(len(k))) + 1 + len(v) + sovSnapshots(uint64(len(v))) - i = encodeVarintSnapshots(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) i = encodeVarintSnapshots(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintSnapshots(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintSnapshots(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 } } - return i, nil + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0x1a + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Snapshotter) > 0 { + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *StatSnapshotRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -972,29 +1768,40 @@ func (m *StatSnapshotRequest) Marshal() (dAtA []byte, err error) { } func (m *StatSnapshotRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StatSnapshotRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Snapshotter) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) - i += copy(dAtA[i:], m.Snapshotter) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Key) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Key) + copy(dAtA[i:], m.Key) i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) + i-- + dAtA[i] = 0x12 + } + if len(m.Snapshotter) > 0 { + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Info) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1002,67 +1809,80 @@ func (m *Info) Marshal() (dAtA []byte, err error) { } func (m *Info) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Info) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Parent) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Parent))) - i += copy(dAtA[i:], m.Parent) + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintSnapshots(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintSnapshots(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintSnapshots(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x32 + } } - if m.Kind != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(m.Kind)) + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt):]) + if err1 != nil { + return 0, err1 } + i -= n1 + i = encodeVarintSnapshots(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x2a + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintSnapshots(dAtA, i, uint64(n2)) + i-- dAtA[i] = 0x22 - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(types.SizeOfStdTime(m.CreatedAt))) - n1, err := types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) - if err != nil { - return 0, err + if m.Kind != 0 { + i = encodeVarintSnapshots(dAtA, i, uint64(m.Kind)) + i-- + dAtA[i] = 0x18 } - i += n1 - dAtA[i] = 0x2a - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(types.SizeOfStdTime(m.UpdatedAt))) - n2, err := types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i:]) - if err != nil { - return 0, err + if len(m.Parent) > 0 { + i -= len(m.Parent) + copy(dAtA[i:], m.Parent) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Parent))) + i-- + dAtA[i] = 0x12 } - i += n2 - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x32 - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovSnapshots(uint64(len(k))) + 1 + len(v) + sovSnapshots(uint64(len(v))) - i = encodeVarintSnapshots(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) - } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *StatSnapshotResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1070,25 +1890,36 @@ func (m *StatSnapshotResponse) Marshal() (dAtA []byte, err error) { } func (m *StatSnapshotResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StatSnapshotResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(m.Info.Size())) - n3, err := m.Info.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Info.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshots(dAtA, i, uint64(size)) } - i += n3 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *UpdateSnapshotRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1096,41 +1927,55 @@ func (m *UpdateSnapshotRequest) Marshal() (dAtA []byte, err error) { } func (m *UpdateSnapshotRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateSnapshotRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Snapshotter) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) - i += copy(dAtA[i:], m.Snapshotter) - } - dAtA[i] = 0x12 - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(m.Info.Size())) - n4, err := m.Info.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n4 if m.UpdateMask != nil { + { + size, err := m.UpdateMask.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshots(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x1a - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(m.UpdateMask.Size())) - n5, err := m.UpdateMask.MarshalTo(dAtA[i:]) + } + { + size, err := m.Info.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n5 + i -= size + i = encodeVarintSnapshots(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Snapshotter) > 0 { + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *UpdateSnapshotResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1138,25 +1983,36 @@ func (m *UpdateSnapshotResponse) Marshal() (dAtA []byte, err error) { } func (m *UpdateSnapshotResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateSnapshotResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(m.Info.Size())) - n6, err := m.Info.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + { + size, err := m.Info.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshots(dAtA, i, uint64(size)) } - i += n6 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *ListSnapshotsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1164,23 +2020,42 @@ func (m *ListSnapshotsRequest) Marshal() (dAtA []byte, err error) { } func (m *ListSnapshotsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListSnapshotsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Filters) > 0 { + for iNdEx := len(m.Filters) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Filters[iNdEx]) + copy(dAtA[i:], m.Filters[iNdEx]) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Filters[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } if len(m.Snapshotter) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) - i += copy(dAtA[i:], m.Snapshotter) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListSnapshotsResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1188,29 +2063,40 @@ func (m *ListSnapshotsResponse) Marshal() (dAtA []byte, err error) { } func (m *ListSnapshotsResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListSnapshotsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Info) > 0 { - for _, msg := range m.Info { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Info) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Info[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshots(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *UsageRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1218,29 +2104,40 @@ func (m *UsageRequest) Marshal() (dAtA []byte, err error) { } func (m *UsageRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UsageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Snapshotter) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) - i += copy(dAtA[i:], m.Snapshotter) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Key) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Key) + copy(dAtA[i:], m.Key) i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.Snapshotter) > 0 { + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *UsageResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1248,33 +2145,81 @@ func (m *UsageResponse) Marshal() (dAtA []byte, err error) { } func (m *UsageResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UsageResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Size_ != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintSnapshots(dAtA, i, uint64(m.Size_)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Inodes != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintSnapshots(dAtA, i, uint64(m.Inodes)) + i-- + dAtA[i] = 0x10 + } + if m.Size_ != 0 { + i = encodeVarintSnapshots(dAtA, i, uint64(m.Size_)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *CleanupRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CleanupRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CleanupRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Snapshotter) > 0 { + i -= len(m.Snapshotter) + copy(dAtA[i:], m.Snapshotter) + i = encodeVarintSnapshots(dAtA, i, uint64(len(m.Snapshotter))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintSnapshots(dAtA []byte, offset int, v uint64) int { + offset -= sovSnapshots(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *PrepareSnapshotRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Snapshotter) @@ -1297,10 +2242,16 @@ func (m *PrepareSnapshotRequest) Size() (n int) { n += mapEntrySize + 1 + sovSnapshots(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *PrepareSnapshotResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Mounts) > 0 { @@ -1309,10 +2260,16 @@ func (m *PrepareSnapshotResponse) Size() (n int) { n += 1 + l + sovSnapshots(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ViewSnapshotRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Snapshotter) @@ -1335,10 +2292,16 @@ func (m *ViewSnapshotRequest) Size() (n int) { n += mapEntrySize + 1 + sovSnapshots(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ViewSnapshotResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Mounts) > 0 { @@ -1347,10 +2310,16 @@ func (m *ViewSnapshotResponse) Size() (n int) { n += 1 + l + sovSnapshots(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *MountsRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Snapshotter) @@ -1361,10 +2330,16 @@ func (m *MountsRequest) Size() (n int) { if l > 0 { n += 1 + l + sovSnapshots(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *MountsResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Mounts) > 0 { @@ -1373,10 +2348,16 @@ func (m *MountsResponse) Size() (n int) { n += 1 + l + sovSnapshots(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *RemoveSnapshotRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Snapshotter) @@ -1387,10 +2368,16 @@ func (m *RemoveSnapshotRequest) Size() (n int) { if l > 0 { n += 1 + l + sovSnapshots(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CommitSnapshotRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Snapshotter) @@ -1413,10 +2400,16 @@ func (m *CommitSnapshotRequest) Size() (n int) { n += mapEntrySize + 1 + sovSnapshots(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StatSnapshotRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Snapshotter) @@ -1427,10 +2420,16 @@ func (m *StatSnapshotRequest) Size() (n int) { if l > 0 { n += 1 + l + sovSnapshots(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *Info) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -1444,9 +2443,9 @@ func (m *Info) Size() (n int) { if m.Kind != 0 { n += 1 + sovSnapshots(uint64(m.Kind)) } - l = types.SizeOfStdTime(m.CreatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt) n += 1 + l + sovSnapshots(uint64(l)) - l = types.SizeOfStdTime(m.UpdatedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt) n += 1 + l + sovSnapshots(uint64(l)) if len(m.Labels) > 0 { for k, v := range m.Labels { @@ -1456,18 +2455,30 @@ func (m *Info) Size() (n int) { n += mapEntrySize + 1 + sovSnapshots(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StatSnapshotResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Info.Size() n += 1 + l + sovSnapshots(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateSnapshotRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Snapshotter) @@ -1480,28 +2491,52 @@ func (m *UpdateSnapshotRequest) Size() (n int) { l = m.UpdateMask.Size() n += 1 + l + sovSnapshots(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateSnapshotResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Info.Size() n += 1 + l + sovSnapshots(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListSnapshotsRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Snapshotter) if l > 0 { n += 1 + l + sovSnapshots(uint64(l)) } + if len(m.Filters) > 0 { + for _, s := range m.Filters { + l = len(s) + n += 1 + l + sovSnapshots(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListSnapshotsResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Info) > 0 { @@ -1510,10 +2545,16 @@ func (m *ListSnapshotsResponse) Size() (n int) { n += 1 + l + sovSnapshots(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UsageRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Snapshotter) @@ -1524,30 +2565,48 @@ func (m *UsageRequest) Size() (n int) { if l > 0 { n += 1 + l + sovSnapshots(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UsageResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Size_ != 0 { n += 1 + sovSnapshots(uint64(m.Size_)) } - if m.Inodes != 0 { - n += 1 + sovSnapshots(uint64(m.Inodes)) + if m.Inodes != 0 { + n += 1 + sovSnapshots(uint64(m.Inodes)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CleanupRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Snapshotter) + if l > 0 { + n += 1 + l + sovSnapshots(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) } return n } func sovSnapshots(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozSnapshots(x uint64) (n int) { return sovSnapshots(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -1560,7 +2619,7 @@ func (this *PrepareSnapshotRequest) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -1571,6 +2630,7 @@ func (this *PrepareSnapshotRequest) String() string { `Key:` + fmt.Sprintf("%v", this.Key) + `,`, `Parent:` + fmt.Sprintf("%v", this.Parent) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1579,8 +2639,14 @@ func (this *PrepareSnapshotResponse) String() string { if this == nil { return "nil" } + repeatedStringForMounts := "[]*Mount{" + for _, f := range this.Mounts { + repeatedStringForMounts += strings.Replace(fmt.Sprintf("%v", f), "Mount", "types.Mount", 1) + "," + } + repeatedStringForMounts += "}" s := strings.Join([]string{`&PrepareSnapshotResponse{`, - `Mounts:` + strings.Replace(fmt.Sprintf("%v", this.Mounts), "Mount", "containerd_types.Mount", 1) + `,`, + `Mounts:` + repeatedStringForMounts + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1593,7 +2659,7 @@ func (this *ViewSnapshotRequest) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -1604,6 +2670,7 @@ func (this *ViewSnapshotRequest) String() string { `Key:` + fmt.Sprintf("%v", this.Key) + `,`, `Parent:` + fmt.Sprintf("%v", this.Parent) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1612,8 +2679,14 @@ func (this *ViewSnapshotResponse) String() string { if this == nil { return "nil" } + repeatedStringForMounts := "[]*Mount{" + for _, f := range this.Mounts { + repeatedStringForMounts += strings.Replace(fmt.Sprintf("%v", f), "Mount", "types.Mount", 1) + "," + } + repeatedStringForMounts += "}" s := strings.Join([]string{`&ViewSnapshotResponse{`, - `Mounts:` + strings.Replace(fmt.Sprintf("%v", this.Mounts), "Mount", "containerd_types.Mount", 1) + `,`, + `Mounts:` + repeatedStringForMounts + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1625,6 +2698,7 @@ func (this *MountsRequest) String() string { s := strings.Join([]string{`&MountsRequest{`, `Snapshotter:` + fmt.Sprintf("%v", this.Snapshotter) + `,`, `Key:` + fmt.Sprintf("%v", this.Key) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1633,8 +2707,14 @@ func (this *MountsResponse) String() string { if this == nil { return "nil" } + repeatedStringForMounts := "[]*Mount{" + for _, f := range this.Mounts { + repeatedStringForMounts += strings.Replace(fmt.Sprintf("%v", f), "Mount", "types.Mount", 1) + "," + } + repeatedStringForMounts += "}" s := strings.Join([]string{`&MountsResponse{`, - `Mounts:` + strings.Replace(fmt.Sprintf("%v", this.Mounts), "Mount", "containerd_types.Mount", 1) + `,`, + `Mounts:` + repeatedStringForMounts + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1646,6 +2726,7 @@ func (this *RemoveSnapshotRequest) String() string { s := strings.Join([]string{`&RemoveSnapshotRequest{`, `Snapshotter:` + fmt.Sprintf("%v", this.Snapshotter) + `,`, `Key:` + fmt.Sprintf("%v", this.Key) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1658,7 +2739,7 @@ func (this *CommitSnapshotRequest) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -1669,6 +2750,7 @@ func (this *CommitSnapshotRequest) String() string { `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Key:` + fmt.Sprintf("%v", this.Key) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1680,6 +2762,7 @@ func (this *StatSnapshotRequest) String() string { s := strings.Join([]string{`&StatSnapshotRequest{`, `Snapshotter:` + fmt.Sprintf("%v", this.Snapshotter) + `,`, `Key:` + fmt.Sprintf("%v", this.Key) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1692,7 +2775,7 @@ func (this *Info) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -1702,9 +2785,10 @@ func (this *Info) String() string { `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Parent:` + fmt.Sprintf("%v", this.Parent) + `,`, `Kind:` + fmt.Sprintf("%v", this.Kind) + `,`, - `CreatedAt:` + strings.Replace(strings.Replace(this.CreatedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, - `UpdatedAt:` + strings.Replace(strings.Replace(this.UpdatedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `CreatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.CreatedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, + `UpdatedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.UpdatedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, `Labels:` + mapStringForLabels + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1715,6 +2799,7 @@ func (this *StatSnapshotResponse) String() string { } s := strings.Join([]string{`&StatSnapshotResponse{`, `Info:` + strings.Replace(strings.Replace(this.Info.String(), "Info", "Info", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1726,7 +2811,8 @@ func (this *UpdateSnapshotRequest) String() string { s := strings.Join([]string{`&UpdateSnapshotRequest{`, `Snapshotter:` + fmt.Sprintf("%v", this.Snapshotter) + `,`, `Info:` + strings.Replace(strings.Replace(this.Info.String(), "Info", "Info", 1), `&`, ``, 1) + `,`, - `UpdateMask:` + strings.Replace(fmt.Sprintf("%v", this.UpdateMask), "FieldMask", "google_protobuf2.FieldMask", 1) + `,`, + `UpdateMask:` + strings.Replace(fmt.Sprintf("%v", this.UpdateMask), "FieldMask", "types1.FieldMask", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1737,6 +2823,7 @@ func (this *UpdateSnapshotResponse) String() string { } s := strings.Join([]string{`&UpdateSnapshotResponse{`, `Info:` + strings.Replace(strings.Replace(this.Info.String(), "Info", "Info", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1747,6 +2834,8 @@ func (this *ListSnapshotsRequest) String() string { } s := strings.Join([]string{`&ListSnapshotsRequest{`, `Snapshotter:` + fmt.Sprintf("%v", this.Snapshotter) + `,`, + `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1755,8 +2844,14 @@ func (this *ListSnapshotsResponse) String() string { if this == nil { return "nil" } + repeatedStringForInfo := "[]Info{" + for _, f := range this.Info { + repeatedStringForInfo += strings.Replace(strings.Replace(f.String(), "Info", "Info", 1), `&`, ``, 1) + "," + } + repeatedStringForInfo += "}" s := strings.Join([]string{`&ListSnapshotsResponse{`, - `Info:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Info), "Info", "Info", 1), `&`, ``, 1) + `,`, + `Info:` + repeatedStringForInfo + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1768,6 +2863,7 @@ func (this *UsageRequest) String() string { s := strings.Join([]string{`&UsageRequest{`, `Snapshotter:` + fmt.Sprintf("%v", this.Snapshotter) + `,`, `Key:` + fmt.Sprintf("%v", this.Key) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1779,6 +2875,18 @@ func (this *UsageResponse) String() string { s := strings.Join([]string{`&UsageResponse{`, `Size_:` + fmt.Sprintf("%v", this.Size_) + `,`, `Inodes:` + fmt.Sprintf("%v", this.Inodes) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *CleanupRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CleanupRequest{`, + `Snapshotter:` + fmt.Sprintf("%v", this.Snapshotter) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1806,7 +2914,7 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1834,7 +2942,7 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1844,6 +2952,9 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1863,7 +2974,7 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1873,6 +2984,9 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1892,7 +3006,7 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1902,6 +3016,9 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1921,7 +3038,7 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1930,6 +3047,9 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1950,7 +3070,7 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1967,7 +3087,7 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1977,6 +3097,9 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthSnapshots + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -1993,7 +3116,7 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2003,6 +3126,9 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthSnapshots + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -2014,7 +3140,7 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > postIndex { @@ -2031,12 +3157,13 @@ func (m *PrepareSnapshotRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2061,7 +3188,7 @@ func (m *PrepareSnapshotResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2089,7 +3216,7 @@ func (m *PrepareSnapshotResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2098,10 +3225,13 @@ func (m *PrepareSnapshotResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Mounts = append(m.Mounts, &containerd_types.Mount{}) + m.Mounts = append(m.Mounts, &types.Mount{}) if err := m.Mounts[len(m.Mounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -2112,12 +3242,13 @@ func (m *PrepareSnapshotResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2142,7 +3273,7 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2170,7 +3301,7 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2180,6 +3311,9 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2199,7 +3333,7 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2209,6 +3343,9 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2228,7 +3365,7 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2238,6 +3375,9 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2257,7 +3397,7 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2266,6 +3406,9 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2286,7 +3429,7 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2303,7 +3446,7 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2313,6 +3456,9 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthSnapshots + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -2329,7 +3475,7 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2339,6 +3485,9 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthSnapshots + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -2350,7 +3499,7 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > postIndex { @@ -2367,12 +3516,13 @@ func (m *ViewSnapshotRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2397,7 +3547,7 @@ func (m *ViewSnapshotResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2425,7 +3575,7 @@ func (m *ViewSnapshotResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2434,10 +3584,13 @@ func (m *ViewSnapshotResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Mounts = append(m.Mounts, &containerd_types.Mount{}) + m.Mounts = append(m.Mounts, &types.Mount{}) if err := m.Mounts[len(m.Mounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -2448,12 +3601,13 @@ func (m *ViewSnapshotResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2478,7 +3632,7 @@ func (m *MountsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2506,7 +3660,7 @@ func (m *MountsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2516,6 +3670,9 @@ func (m *MountsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2535,7 +3692,7 @@ func (m *MountsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2545,6 +3702,9 @@ func (m *MountsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2556,12 +3716,13 @@ func (m *MountsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2586,7 +3747,7 @@ func (m *MountsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2614,7 +3775,7 @@ func (m *MountsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2623,10 +3784,13 @@ func (m *MountsResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Mounts = append(m.Mounts, &containerd_types.Mount{}) + m.Mounts = append(m.Mounts, &types.Mount{}) if err := m.Mounts[len(m.Mounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -2637,12 +3801,13 @@ func (m *MountsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2667,7 +3832,7 @@ func (m *RemoveSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2695,7 +3860,7 @@ func (m *RemoveSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2705,6 +3870,9 @@ func (m *RemoveSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2724,7 +3892,7 @@ func (m *RemoveSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2734,6 +3902,9 @@ func (m *RemoveSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2745,12 +3916,13 @@ func (m *RemoveSnapshotRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2775,7 +3947,7 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2803,7 +3975,7 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2813,6 +3985,9 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2832,7 +4007,7 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2842,6 +4017,9 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2861,7 +4039,7 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2871,6 +4049,9 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2890,7 +4071,7 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2899,6 +4080,9 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2919,7 +4103,7 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2936,7 +4120,7 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2946,6 +4130,9 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthSnapshots + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -2962,7 +4149,7 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2972,6 +4159,9 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthSnapshots + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -2983,7 +4173,7 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > postIndex { @@ -3000,12 +4190,13 @@ func (m *CommitSnapshotRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3030,7 +4221,7 @@ func (m *StatSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3058,7 +4249,7 @@ func (m *StatSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3068,6 +4259,9 @@ func (m *StatSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3087,7 +4281,7 @@ func (m *StatSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3097,6 +4291,9 @@ func (m *StatSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3108,12 +4305,13 @@ func (m *StatSnapshotRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3138,7 +4336,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3166,7 +4364,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3176,6 +4374,9 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3195,7 +4396,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3205,6 +4406,9 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3224,7 +4428,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Kind |= (Kind(b) & 0x7F) << shift + m.Kind |= Kind(b&0x7F) << shift if b < 0x80 { break } @@ -3243,7 +4447,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3252,10 +4456,13 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -3273,7 +4480,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3282,10 +4489,13 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -3303,7 +4513,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3312,6 +4522,9 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3332,7 +4545,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3349,7 +4562,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3359,6 +4572,9 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthSnapshots + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -3375,7 +4591,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3385,6 +4601,9 @@ func (m *Info) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthSnapshots + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -3396,7 +4615,7 @@ func (m *Info) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > postIndex { @@ -3413,12 +4632,13 @@ func (m *Info) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3443,7 +4663,7 @@ func (m *StatSnapshotResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3471,7 +4691,7 @@ func (m *StatSnapshotResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3480,6 +4700,9 @@ func (m *StatSnapshotResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3493,12 +4716,13 @@ func (m *StatSnapshotResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3523,7 +4747,7 @@ func (m *UpdateSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3551,7 +4775,7 @@ func (m *UpdateSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3561,6 +4785,9 @@ func (m *UpdateSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3580,7 +4807,7 @@ func (m *UpdateSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3589,6 +4816,9 @@ func (m *UpdateSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3610,7 +4840,7 @@ func (m *UpdateSnapshotRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3619,11 +4849,14 @@ func (m *UpdateSnapshotRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } if m.UpdateMask == nil { - m.UpdateMask = &google_protobuf2.FieldMask{} + m.UpdateMask = &types1.FieldMask{} } if err := m.UpdateMask.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -3635,12 +4868,13 @@ func (m *UpdateSnapshotRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3665,7 +4899,7 @@ func (m *UpdateSnapshotResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3693,7 +4927,7 @@ func (m *UpdateSnapshotResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3702,6 +4936,9 @@ func (m *UpdateSnapshotResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3715,12 +4952,13 @@ func (m *UpdateSnapshotResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3745,7 +4983,7 @@ func (m *ListSnapshotsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3773,7 +5011,7 @@ func (m *ListSnapshotsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3783,23 +5021,59 @@ func (m *ListSnapshotsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } m.Snapshotter = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshots + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSnapshots + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Filters = append(m.Filters, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSnapshots(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3824,7 +5098,7 @@ func (m *ListSnapshotsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3852,7 +5126,7 @@ func (m *ListSnapshotsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3861,6 +5135,9 @@ func (m *ListSnapshotsResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3875,12 +5152,13 @@ func (m *ListSnapshotsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3905,7 +5183,7 @@ func (m *UsageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3933,7 +5211,7 @@ func (m *UsageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3943,6 +5221,9 @@ func (m *UsageRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3962,7 +5243,7 @@ func (m *UsageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3972,6 +5253,9 @@ func (m *UsageRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshots } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3983,12 +5267,13 @@ func (m *UsageRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4013,7 +5298,7 @@ func (m *UsageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4041,7 +5326,7 @@ func (m *UsageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Size_ |= (int64(b) & 0x7F) << shift + m.Size_ |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -4060,23 +5345,107 @@ func (m *UsageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Inodes |= (int64(b) & 0x7F) << shift + m.Inodes |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSnapshots(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSnapshots + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CleanupRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshots + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CleanupRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CleanupRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Snapshotter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSnapshots + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSnapshots + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSnapshots + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Snapshotter = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipSnapshots(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshots } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4089,6 +5458,7 @@ func (m *UsageResponse) Unmarshal(dAtA []byte) error { func skipSnapshots(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -4120,10 +5490,8 @@ func skipSnapshots(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -4140,124 +5508,34 @@ func skipSnapshots(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthSnapshots } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowSnapshots - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipSnapshots(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSnapshots + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthSnapshots + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthSnapshots = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowSnapshots = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthSnapshots = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSnapshots = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSnapshots = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto", fileDescriptorSnapshots) -} - -var fileDescriptorSnapshots = []byte{ - // 1007 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4f, 0x6f, 0x1a, 0x47, - 0x14, 0x67, 0x60, 0x8d, 0xe3, 0x87, 0xed, 0xd2, 0x09, 0x26, 0x68, 0x5b, 0xe1, 0x15, 0x87, 0xca, - 0xea, 0x61, 0x37, 0xa1, 0x6a, 0xe2, 0xc4, 0x97, 0x62, 0x4c, 0x2b, 0xec, 0xd8, 0xa9, 0x36, 0xb6, - 0x13, 0xa7, 0x55, 0xa3, 0x35, 0x8c, 0xf1, 0x0a, 0x76, 0x97, 0x32, 0x03, 0x11, 0xad, 0x54, 0xf5, - 0x18, 0xf9, 0xd4, 0x2f, 0xe0, 0x53, 0xfb, 0x21, 0xaa, 0x7e, 0x02, 0x1f, 0x7b, 0xec, 0xa9, 0x6d, - 0xfc, 0x25, 0x7a, 0xea, 0x1f, 0xcd, 0xec, 0x2c, 0x60, 0x4c, 0xc5, 0x82, 0xc9, 0x6d, 0x66, 0x67, - 0x7e, 0xef, 0xfd, 0xe6, 0xf7, 0xe6, 0xbd, 0x37, 0x0b, 0xdb, 0x35, 0x9b, 0x9d, 0xb6, 0x8f, 0xf5, - 0x8a, 0xe7, 0x18, 0x15, 0xcf, 0x65, 0x96, 0xed, 0x92, 0x56, 0x75, 0x70, 0x68, 0x35, 0x6d, 0x83, - 0x92, 0x56, 0xc7, 0xae, 0x10, 0x6a, 0x50, 0xd7, 0x6a, 0xd2, 0x53, 0x8f, 0x51, 0xa3, 0x73, 0xaf, - 0x3f, 0xd1, 0x9b, 0x2d, 0x8f, 0x79, 0x58, 0xeb, 0xa3, 0xf4, 0x00, 0xa1, 0xf7, 0x37, 0x75, 0xee, - 0xa9, 0xa9, 0x9a, 0x57, 0xf3, 0xc4, 0x66, 0x83, 0x8f, 0x7c, 0x9c, 0xfa, 0x5e, 0xcd, 0xf3, 0x6a, - 0x0d, 0x62, 0x88, 0xd9, 0x71, 0xfb, 0xc4, 0x20, 0x4e, 0x93, 0x75, 0xe5, 0xa2, 0x36, 0xbc, 0x78, - 0x62, 0x93, 0x46, 0xf5, 0xa5, 0x63, 0xd1, 0xba, 0xdc, 0xb1, 0x3a, 0xbc, 0x83, 0xd9, 0x0e, 0xa1, - 0xcc, 0x72, 0x9a, 0x72, 0xc3, 0xfd, 0x50, 0x67, 0x64, 0xdd, 0x26, 0xa1, 0x86, 0xe3, 0xb5, 0x5d, - 0xe6, 0xe3, 0x72, 0x7f, 0x23, 0x48, 0x7f, 0xde, 0x22, 0x4d, 0xab, 0x45, 0x9e, 0xca, 0x53, 0x98, - 0xe4, 0xeb, 0x36, 0xa1, 0x0c, 0x6b, 0x90, 0x08, 0x0e, 0xc6, 0x48, 0x2b, 0x83, 0x34, 0xb4, 0xb6, - 0x60, 0x0e, 0x7e, 0xc2, 0x49, 0x88, 0xd5, 0x49, 0x37, 0x13, 0x15, 0x2b, 0x7c, 0x88, 0xd3, 0x10, - 0xe7, 0xa6, 0x5c, 0x96, 0x89, 0x89, 0x8f, 0x72, 0x86, 0xbf, 0x84, 0x78, 0xc3, 0x3a, 0x26, 0x0d, - 0x9a, 0x51, 0xb4, 0xd8, 0x5a, 0x22, 0xbf, 0xa5, 0x8f, 0xd3, 0x51, 0x1f, 0xcd, 0x4a, 0x7f, 0x2c, - 0xcc, 0x94, 0x5c, 0xd6, 0xea, 0x9a, 0xd2, 0xa6, 0xfa, 0x10, 0x12, 0x03, 0x9f, 0x03, 0x5a, 0xa8, - 0x4f, 0x2b, 0x05, 0x73, 0x1d, 0xab, 0xd1, 0x26, 0x92, 0xaa, 0x3f, 0x79, 0x14, 0x5d, 0x47, 0xb9, - 0x6d, 0xb8, 0x73, 0xcd, 0x11, 0x6d, 0x7a, 0x2e, 0x25, 0xd8, 0x80, 0xb8, 0x50, 0x8a, 0x66, 0x90, - 0xe0, 0x7c, 0x67, 0x90, 0xb3, 0x50, 0x52, 0xdf, 0xe5, 0xeb, 0xa6, 0xdc, 0x96, 0xfb, 0x0b, 0xc1, - 0xed, 0x43, 0x9b, 0xbc, 0x7a, 0x9b, 0x42, 0x1e, 0x0d, 0x09, 0x59, 0x18, 0x2f, 0xe4, 0x08, 0x4a, - 0xb3, 0x56, 0xf1, 0x33, 0x48, 0x5d, 0xf5, 0x32, 0xad, 0x84, 0x45, 0x58, 0x12, 0x1f, 0xe8, 0x0d, - 0xb4, 0xcb, 0x15, 0x60, 0x39, 0x30, 0x32, 0x2d, 0x8f, 0x1d, 0x58, 0x31, 0x89, 0xe3, 0x75, 0x66, - 0x91, 0x14, 0xfc, 0x5e, 0xac, 0x14, 0x3d, 0xc7, 0xb1, 0xd9, 0xe4, 0xd6, 0x30, 0x28, 0xae, 0xe5, - 0x04, 0x92, 0x8b, 0x71, 0xe0, 0x21, 0xd6, 0x8f, 0xcc, 0x17, 0x43, 0xb7, 0xa2, 0x38, 0xfe, 0x56, - 0x8c, 0x24, 0x34, 0xeb, 0x7b, 0x51, 0x86, 0xdb, 0x4f, 0x99, 0xc5, 0x66, 0x21, 0xe2, 0xbf, 0x51, - 0x50, 0xca, 0xee, 0x89, 0xd7, 0x53, 0x04, 0x0d, 0x28, 0xd2, 0xcf, 0x96, 0xe8, 0x95, 0x6c, 0x79, - 0x04, 0x4a, 0xdd, 0x76, 0xab, 0x42, 0xaa, 0xe5, 0xfc, 0x07, 0xe3, 0x55, 0xd9, 0xb1, 0xdd, 0xaa, - 0x29, 0x30, 0xb8, 0x08, 0x50, 0x69, 0x11, 0x8b, 0x91, 0xea, 0x4b, 0x8b, 0x65, 0x14, 0x0d, 0xad, - 0x25, 0xf2, 0xaa, 0xee, 0xd7, 0x61, 0x3d, 0xa8, 0xc3, 0xfa, 0x7e, 0x50, 0x87, 0x37, 0x6f, 0x5d, - 0xfc, 0xbe, 0x1a, 0xf9, 0xe1, 0x8f, 0x55, 0x64, 0x2e, 0x48, 0x5c, 0x81, 0x71, 0x23, 0xed, 0x66, - 0x35, 0x30, 0x32, 0x37, 0x89, 0x11, 0x89, 0x2b, 0x30, 0xbc, 0xdd, 0x8b, 0x6e, 0x5c, 0x44, 0x37, - 0x3f, 0xfe, 0x1c, 0x5c, 0xa9, 0x59, 0x07, 0xf3, 0x39, 0xa4, 0xae, 0x06, 0x53, 0x26, 0xd7, 0x27, - 0xa0, 0xd8, 0xee, 0x89, 0x27, 0x8c, 0x24, 0xc2, 0x88, 0xcc, 0xc9, 0x6d, 0x2a, 0xfc, 0xa4, 0xa6, - 0x40, 0xe6, 0x7e, 0x46, 0xb0, 0x72, 0x20, 0x8e, 0x3b, 0xf9, 0x4d, 0x09, 0xbc, 0x47, 0xa7, 0xf5, - 0x8e, 0x37, 0x20, 0xe1, 0x6b, 0x2d, 0x1a, 0xae, 0xb8, 0x2b, 0xa3, 0x82, 0xf4, 0x29, 0xef, 0xc9, - 0xbb, 0x16, 0xad, 0x9b, 0x32, 0xa4, 0x7c, 0x9c, 0x7b, 0x01, 0xe9, 0x61, 0xe6, 0x33, 0x93, 0x65, - 0x1d, 0x52, 0x8f, 0x6d, 0xda, 0x13, 0x3c, 0x7c, 0x4d, 0xcc, 0x1d, 0xc1, 0xca, 0x10, 0xf2, 0x1a, - 0xa9, 0xd8, 0x94, 0xa4, 0x36, 0x61, 0xf1, 0x80, 0x5a, 0x35, 0x72, 0x93, 0x5c, 0xde, 0x80, 0x25, - 0x69, 0x43, 0xd2, 0xc2, 0xa0, 0x50, 0xfb, 0x1b, 0x3f, 0xa7, 0x63, 0xa6, 0x18, 0xf3, 0x9c, 0xb6, - 0x5d, 0xaf, 0x4a, 0xa8, 0x40, 0xc6, 0x4c, 0x39, 0xfb, 0xf0, 0x35, 0x02, 0x85, 0xa7, 0x29, 0x7e, - 0x1f, 0xe6, 0x0f, 0xf6, 0x76, 0xf6, 0x9e, 0x3c, 0xdb, 0x4b, 0x46, 0xd4, 0x77, 0xce, 0xce, 0xb5, - 0x04, 0xff, 0x7c, 0xe0, 0xd6, 0x5d, 0xef, 0x95, 0x8b, 0xd3, 0xa0, 0x1c, 0x96, 0x4b, 0xcf, 0x92, - 0x48, 0x5d, 0x3c, 0x3b, 0xd7, 0x6e, 0xf1, 0x25, 0xde, 0xa2, 0xb0, 0x0a, 0xf1, 0x42, 0x71, 0xbf, - 0x7c, 0x58, 0x4a, 0x46, 0xd5, 0xe5, 0xb3, 0x73, 0x0d, 0xf8, 0x4a, 0xa1, 0xc2, 0xec, 0x0e, 0xc1, - 0x1a, 0x2c, 0x14, 0x9f, 0xec, 0xee, 0x96, 0xf7, 0xf7, 0x4b, 0x5b, 0xc9, 0x98, 0xfa, 0xee, 0xd9, - 0xb9, 0xb6, 0xc4, 0x97, 0xfd, 0x5a, 0xc9, 0x48, 0x55, 0x5d, 0x7c, 0xfd, 0x63, 0x36, 0xf2, 0xcb, - 0x4f, 0x59, 0xc1, 0x20, 0xff, 0xcf, 0x3c, 0x2c, 0xf4, 0x34, 0xc6, 0xdf, 0xc1, 0xbc, 0x7c, 0x4a, - 0xe0, 0xf5, 0x69, 0x9f, 0x37, 0xea, 0xc3, 0x29, 0x90, 0x52, 0xc4, 0x36, 0x28, 0xe2, 0x84, 0x1f, - 0x4f, 0xf5, 0x24, 0x50, 0xef, 0x4f, 0x0a, 0x93, 0x6e, 0xeb, 0x10, 0xf7, 0xbb, 0x2d, 0x36, 0xc6, - 0x5b, 0xb8, 0xd2, 0xdc, 0xd5, 0xbb, 0xe1, 0x01, 0xd2, 0xd9, 0x11, 0xc4, 0xfd, 0x60, 0xe0, 0x07, - 0x53, 0xb6, 0x38, 0x35, 0x7d, 0x2d, 0xb3, 0x4b, 0xfc, 0x29, 0xce, 0x4d, 0xfb, 0x2d, 0x3f, 0x8c, - 0xe9, 0x91, 0x8f, 0x83, 0xff, 0x35, 0xdd, 0x06, 0x85, 0x57, 0xce, 0x30, 0x91, 0x19, 0xd1, 0x2e, - 0xc3, 0x44, 0x66, 0x64, 0x61, 0xfe, 0x16, 0xe2, 0x7e, 0x6d, 0x0a, 0x73, 0xa2, 0x91, 0xf5, 0x57, - 0x5d, 0x9f, 0x1c, 0x28, 0x9d, 0x77, 0x41, 0xe1, 0x25, 0x08, 0x87, 0x20, 0x3f, 0xaa, 0xc8, 0xa9, - 0x0f, 0x26, 0xc6, 0xf9, 0x8e, 0xef, 0x22, 0x7c, 0x0a, 0x73, 0xa2, 0xbc, 0x60, 0x3d, 0x04, 0xfb, - 0x81, 0x5a, 0xa6, 0x1a, 0xa1, 0xf7, 0xfb, 0xbe, 0x36, 0xbf, 0xba, 0x78, 0x93, 0x8d, 0xfc, 0xf6, - 0x26, 0x1b, 0xf9, 0xfe, 0x32, 0x8b, 0x2e, 0x2e, 0xb3, 0xe8, 0xd7, 0xcb, 0x2c, 0xfa, 0xf3, 0x32, - 0x8b, 0x5e, 0x6c, 0x4d, 0xff, 0xcf, 0xb9, 0xd1, 0x9b, 0x3c, 0x8f, 0x1c, 0xc7, 0xc5, 0x55, 0xfa, - 0xe8, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8e, 0xa0, 0xb2, 0xda, 0xc4, 0x0e, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto b/vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto index 0e62add3bf0ee..dfb8ff1e70a9b 100644 --- a/vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto +++ b/vendor/github.com/containerd/containerd/api/services/snapshots/v1/snapshots.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.snapshots.v1; @@ -21,6 +37,7 @@ service Snapshots { rpc Update(UpdateSnapshotRequest) returns (UpdateSnapshotResponse); rpc List(ListSnapshotsRequest) returns (stream ListSnapshotsResponse); rpc Usage(UsageRequest) returns (UsageResponse); + rpc Cleanup(CleanupRequest) returns (google.protobuf.Empty); } message PrepareSnapshotRequest { @@ -133,6 +150,18 @@ message UpdateSnapshotResponse { message ListSnapshotsRequest{ string snapshotter = 1; + + // Filters contains one or more filters using the syntax defined in the + // containerd filter package. + // + // The returned result will be those that match any of the provided + // filters. Expanded, images that match the following will be + // returned: + // + // filters[0] or filters[1] or ... or filters[n-1] or filters[n] + // + // If filters is zero-length or nil, all items will be returned. + repeated string filters = 2; } message ListSnapshotsResponse { @@ -148,3 +177,7 @@ message UsageResponse { int64 size = 1; int64 inodes = 2; } + +message CleanupRequest { + string snapshotter = 1; +} diff --git a/vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.pb.go b/vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.pb.go index 0dfee91568e93..4904e6508d52c 100644 --- a/vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.pb.go @@ -1,68 +1,28 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/tasks/v1/tasks.proto -/* - Package tasks is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/tasks/v1/tasks.proto - - It has these top-level messages: - CreateTaskRequest - CreateTaskResponse - StartRequest - StartResponse - DeleteTaskRequest - DeleteResponse - DeleteProcessRequest - GetRequest - GetResponse - ListTasksRequest - ListTasksResponse - KillRequest - ExecProcessRequest - ExecProcessResponse - ResizePtyRequest - CloseIORequest - PauseTaskRequest - ResumeTaskRequest - ListPidsRequest - ListPidsResponse - CheckpointTaskRequest - CheckpointTaskResponse - UpdateTaskRequest - MetricsRequest - MetricsResponse - WaitRequest - WaitResponse -*/ package tasks -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/gogo/protobuf/types" -import google_protobuf1 "github.com/gogo/protobuf/types" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import containerd_types "github.com/containerd/containerd/api/types" -import containerd_types1 "github.com/containerd/containerd/api/types" -import containerd_types2 "github.com/containerd/containerd/api/types" -import containerd_v1_types "github.com/containerd/containerd/api/types/task" -import _ "github.com/gogo/protobuf/types" - -import time "time" -import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + context "context" + fmt "fmt" + types "github.com/containerd/containerd/api/types" + task "github.com/containerd/containerd/api/types/task" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types1 "github.com/gogo/protobuf/types" + github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -74,7 +34,7 @@ var _ = time.Kitchen // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type CreateTaskRequest struct { ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` @@ -84,116 +44,488 @@ type CreateTaskRequest struct { // These are for mounts that cannot be performed in the user namespace. // Typically, these mounts should be resolved from snapshots specified on // the container object. - Rootfs []*containerd_types.Mount `protobuf:"bytes,3,rep,name=rootfs" json:"rootfs,omitempty"` - Stdin string `protobuf:"bytes,4,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,5,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,6,opt,name=stderr,proto3" json:"stderr,omitempty"` - Terminal bool `protobuf:"varint,7,opt,name=terminal,proto3" json:"terminal,omitempty"` - Checkpoint *containerd_types2.Descriptor `protobuf:"bytes,8,opt,name=checkpoint" json:"checkpoint,omitempty"` - Options *google_protobuf1.Any `protobuf:"bytes,9,opt,name=options" json:"options,omitempty"` + Rootfs []*types.Mount `protobuf:"bytes,3,rep,name=rootfs,proto3" json:"rootfs,omitempty"` + Stdin string `protobuf:"bytes,4,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,5,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,6,opt,name=stderr,proto3" json:"stderr,omitempty"` + Terminal bool `protobuf:"varint,7,opt,name=terminal,proto3" json:"terminal,omitempty"` + Checkpoint *types.Descriptor `protobuf:"bytes,8,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + Options *types1.Any `protobuf:"bytes,9,opt,name=options,proto3" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateTaskRequest) Reset() { *m = CreateTaskRequest{} } +func (*CreateTaskRequest) ProtoMessage() {} +func (*CreateTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{0} +} +func (m *CreateTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateTaskRequest.Merge(m, src) +} +func (m *CreateTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateTaskRequest.DiscardUnknown(m) } -func (m *CreateTaskRequest) Reset() { *m = CreateTaskRequest{} } -func (*CreateTaskRequest) ProtoMessage() {} -func (*CreateTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{0} } +var xxx_messageInfo_CreateTaskRequest proto.InternalMessageInfo type CreateTaskResponse struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateTaskResponse) Reset() { *m = CreateTaskResponse{} } +func (*CreateTaskResponse) ProtoMessage() {} +func (*CreateTaskResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{1} +} +func (m *CreateTaskResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateTaskResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateTaskResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateTaskResponse.Merge(m, src) +} +func (m *CreateTaskResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateTaskResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateTaskResponse.DiscardUnknown(m) } -func (m *CreateTaskResponse) Reset() { *m = CreateTaskResponse{} } -func (*CreateTaskResponse) ProtoMessage() {} -func (*CreateTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{1} } +var xxx_messageInfo_CreateTaskResponse proto.InternalMessageInfo type StartRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StartRequest) Reset() { *m = StartRequest{} } +func (*StartRequest) ProtoMessage() {} +func (*StartRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{2} +} +func (m *StartRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StartRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StartRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StartRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_StartRequest.Merge(m, src) +} +func (m *StartRequest) XXX_Size() int { + return m.Size() +} +func (m *StartRequest) XXX_DiscardUnknown() { + xxx_messageInfo_StartRequest.DiscardUnknown(m) } -func (m *StartRequest) Reset() { *m = StartRequest{} } -func (*StartRequest) ProtoMessage() {} -func (*StartRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{2} } +var xxx_messageInfo_StartRequest proto.InternalMessageInfo type StartResponse struct { - Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StartResponse) Reset() { *m = StartResponse{} } +func (*StartResponse) ProtoMessage() {} +func (*StartResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{3} +} +func (m *StartResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StartResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StartResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StartResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_StartResponse.Merge(m, src) +} +func (m *StartResponse) XXX_Size() int { + return m.Size() +} +func (m *StartResponse) XXX_DiscardUnknown() { + xxx_messageInfo_StartResponse.DiscardUnknown(m) } -func (m *StartResponse) Reset() { *m = StartResponse{} } -func (*StartResponse) ProtoMessage() {} -func (*StartResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{3} } +var xxx_messageInfo_StartResponse proto.InternalMessageInfo type DeleteTaskRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteTaskRequest) Reset() { *m = DeleteTaskRequest{} } +func (*DeleteTaskRequest) ProtoMessage() {} +func (*DeleteTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{4} +} +func (m *DeleteTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteTaskRequest.Merge(m, src) +} +func (m *DeleteTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *DeleteTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteTaskRequest.DiscardUnknown(m) } -func (m *DeleteTaskRequest) Reset() { *m = DeleteTaskRequest{} } -func (*DeleteTaskRequest) ProtoMessage() {} -func (*DeleteTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{4} } +var xxx_messageInfo_DeleteTaskRequest proto.InternalMessageInfo type DeleteResponse struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` - ExitStatus uint32 `protobuf:"varint,3,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` - ExitedAt time.Time `protobuf:"bytes,4,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + ExitStatus uint32 `protobuf:"varint,3,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,4,opt,name=exited_at,json=exitedAt,proto3,stdtime" json:"exited_at"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteResponse) Reset() { *m = DeleteResponse{} } +func (*DeleteResponse) ProtoMessage() {} +func (*DeleteResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{5} +} +func (m *DeleteResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteResponse.Merge(m, src) +} +func (m *DeleteResponse) XXX_Size() int { + return m.Size() +} +func (m *DeleteResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteResponse.DiscardUnknown(m) } -func (m *DeleteResponse) Reset() { *m = DeleteResponse{} } -func (*DeleteResponse) ProtoMessage() {} -func (*DeleteResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{5} } +var xxx_messageInfo_DeleteResponse proto.InternalMessageInfo type DeleteProcessRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteProcessRequest) Reset() { *m = DeleteProcessRequest{} } +func (*DeleteProcessRequest) ProtoMessage() {} +func (*DeleteProcessRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{6} +} +func (m *DeleteProcessRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteProcessRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteProcessRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteProcessRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteProcessRequest.Merge(m, src) +} +func (m *DeleteProcessRequest) XXX_Size() int { + return m.Size() +} +func (m *DeleteProcessRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteProcessRequest.DiscardUnknown(m) } -func (m *DeleteProcessRequest) Reset() { *m = DeleteProcessRequest{} } -func (*DeleteProcessRequest) ProtoMessage() {} -func (*DeleteProcessRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{6} } +var xxx_messageInfo_DeleteProcessRequest proto.InternalMessageInfo type GetRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetRequest) Reset() { *m = GetRequest{} } +func (*GetRequest) ProtoMessage() {} +func (*GetRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{7} +} +func (m *GetRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetRequest.Merge(m, src) +} +func (m *GetRequest) XXX_Size() int { + return m.Size() +} +func (m *GetRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetRequest.DiscardUnknown(m) } -func (m *GetRequest) Reset() { *m = GetRequest{} } -func (*GetRequest) ProtoMessage() {} -func (*GetRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{7} } +var xxx_messageInfo_GetRequest proto.InternalMessageInfo type GetResponse struct { - Process *containerd_v1_types.Process `protobuf:"bytes,1,opt,name=process" json:"process,omitempty"` + Process *task.Process `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetResponse) Reset() { *m = GetResponse{} } +func (*GetResponse) ProtoMessage() {} +func (*GetResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{8} +} +func (m *GetResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetResponse.Merge(m, src) +} +func (m *GetResponse) XXX_Size() int { + return m.Size() +} +func (m *GetResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetResponse.DiscardUnknown(m) } -func (m *GetResponse) Reset() { *m = GetResponse{} } -func (*GetResponse) ProtoMessage() {} -func (*GetResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{8} } +var xxx_messageInfo_GetResponse proto.InternalMessageInfo type ListTasksRequest struct { - Filter string `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` + Filter string `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListTasksRequest) Reset() { *m = ListTasksRequest{} } +func (*ListTasksRequest) ProtoMessage() {} +func (*ListTasksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{9} +} +func (m *ListTasksRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListTasksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListTasksRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListTasksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListTasksRequest.Merge(m, src) +} +func (m *ListTasksRequest) XXX_Size() int { + return m.Size() +} +func (m *ListTasksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListTasksRequest.DiscardUnknown(m) } -func (m *ListTasksRequest) Reset() { *m = ListTasksRequest{} } -func (*ListTasksRequest) ProtoMessage() {} -func (*ListTasksRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{9} } +var xxx_messageInfo_ListTasksRequest proto.InternalMessageInfo type ListTasksResponse struct { - Tasks []*containerd_v1_types.Process `protobuf:"bytes,1,rep,name=tasks" json:"tasks,omitempty"` + Tasks []*task.Process `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListTasksResponse) Reset() { *m = ListTasksResponse{} } +func (*ListTasksResponse) ProtoMessage() {} +func (*ListTasksResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{10} +} +func (m *ListTasksResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListTasksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListTasksResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListTasksResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListTasksResponse.Merge(m, src) +} +func (m *ListTasksResponse) XXX_Size() int { + return m.Size() +} +func (m *ListTasksResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListTasksResponse.DiscardUnknown(m) } -func (m *ListTasksResponse) Reset() { *m = ListTasksResponse{} } -func (*ListTasksResponse) ProtoMessage() {} -func (*ListTasksResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{10} } +var xxx_messageInfo_ListTasksResponse proto.InternalMessageInfo type KillRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` - Signal uint32 `protobuf:"varint,3,opt,name=signal,proto3" json:"signal,omitempty"` - All bool `protobuf:"varint,4,opt,name=all,proto3" json:"all,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Signal uint32 `protobuf:"varint,3,opt,name=signal,proto3" json:"signal,omitempty"` + All bool `protobuf:"varint,4,opt,name=all,proto3" json:"all,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *KillRequest) Reset() { *m = KillRequest{} } +func (*KillRequest) ProtoMessage() {} +func (*KillRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{11} +} +func (m *KillRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KillRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_KillRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *KillRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_KillRequest.Merge(m, src) +} +func (m *KillRequest) XXX_Size() int { + return m.Size() +} +func (m *KillRequest) XXX_DiscardUnknown() { + xxx_messageInfo_KillRequest.DiscardUnknown(m) } -func (m *KillRequest) Reset() { *m = KillRequest{} } -func (*KillRequest) ProtoMessage() {} -func (*KillRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{11} } +var xxx_messageInfo_KillRequest proto.InternalMessageInfo type ExecProcessRequest struct { ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` @@ -204,136 +536,602 @@ type ExecProcessRequest struct { // Spec for starting a process in the target container. // // For runc, this is a process spec, for example. - Spec *google_protobuf1.Any `protobuf:"bytes,6,opt,name=spec" json:"spec,omitempty"` + Spec *types1.Any `protobuf:"bytes,6,opt,name=spec,proto3" json:"spec,omitempty"` // id of the exec process - ExecID string `protobuf:"bytes,7,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + ExecID string `protobuf:"bytes,7,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ExecProcessRequest) Reset() { *m = ExecProcessRequest{} } +func (*ExecProcessRequest) ProtoMessage() {} +func (*ExecProcessRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{12} +} +func (m *ExecProcessRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExecProcessRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExecProcessRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ExecProcessRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExecProcessRequest.Merge(m, src) +} +func (m *ExecProcessRequest) XXX_Size() int { + return m.Size() +} +func (m *ExecProcessRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ExecProcessRequest.DiscardUnknown(m) } -func (m *ExecProcessRequest) Reset() { *m = ExecProcessRequest{} } -func (*ExecProcessRequest) ProtoMessage() {} -func (*ExecProcessRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{12} } +var xxx_messageInfo_ExecProcessRequest proto.InternalMessageInfo type ExecProcessResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ExecProcessResponse) Reset() { *m = ExecProcessResponse{} } +func (*ExecProcessResponse) ProtoMessage() {} +func (*ExecProcessResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{13} +} +func (m *ExecProcessResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExecProcessResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExecProcessResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ExecProcessResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExecProcessResponse.Merge(m, src) +} +func (m *ExecProcessResponse) XXX_Size() int { + return m.Size() +} +func (m *ExecProcessResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ExecProcessResponse.DiscardUnknown(m) } -func (m *ExecProcessResponse) Reset() { *m = ExecProcessResponse{} } -func (*ExecProcessResponse) ProtoMessage() {} -func (*ExecProcessResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{13} } +var xxx_messageInfo_ExecProcessResponse proto.InternalMessageInfo type ResizePtyRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` - Width uint32 `protobuf:"varint,3,opt,name=width,proto3" json:"width,omitempty"` - Height uint32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Width uint32 `protobuf:"varint,3,opt,name=width,proto3" json:"width,omitempty"` + Height uint32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ResizePtyRequest) Reset() { *m = ResizePtyRequest{} } +func (*ResizePtyRequest) ProtoMessage() {} +func (*ResizePtyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{14} +} +func (m *ResizePtyRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResizePtyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResizePtyRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResizePtyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResizePtyRequest.Merge(m, src) +} +func (m *ResizePtyRequest) XXX_Size() int { + return m.Size() +} +func (m *ResizePtyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ResizePtyRequest.DiscardUnknown(m) } -func (m *ResizePtyRequest) Reset() { *m = ResizePtyRequest{} } -func (*ResizePtyRequest) ProtoMessage() {} -func (*ResizePtyRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{14} } +var xxx_messageInfo_ResizePtyRequest proto.InternalMessageInfo type CloseIORequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` - Stdin bool `protobuf:"varint,3,opt,name=stdin,proto3" json:"stdin,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + Stdin bool `protobuf:"varint,3,opt,name=stdin,proto3" json:"stdin,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CloseIORequest) Reset() { *m = CloseIORequest{} } +func (*CloseIORequest) ProtoMessage() {} +func (*CloseIORequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{15} +} +func (m *CloseIORequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CloseIORequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CloseIORequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CloseIORequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CloseIORequest.Merge(m, src) +} +func (m *CloseIORequest) XXX_Size() int { + return m.Size() +} +func (m *CloseIORequest) XXX_DiscardUnknown() { + xxx_messageInfo_CloseIORequest.DiscardUnknown(m) } -func (m *CloseIORequest) Reset() { *m = CloseIORequest{} } -func (*CloseIORequest) ProtoMessage() {} -func (*CloseIORequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{15} } +var xxx_messageInfo_CloseIORequest proto.InternalMessageInfo type PauseTaskRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PauseTaskRequest) Reset() { *m = PauseTaskRequest{} } +func (*PauseTaskRequest) ProtoMessage() {} +func (*PauseTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{16} +} +func (m *PauseTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PauseTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PauseTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PauseTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PauseTaskRequest.Merge(m, src) +} +func (m *PauseTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *PauseTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PauseTaskRequest.DiscardUnknown(m) } -func (m *PauseTaskRequest) Reset() { *m = PauseTaskRequest{} } -func (*PauseTaskRequest) ProtoMessage() {} -func (*PauseTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{16} } +var xxx_messageInfo_PauseTaskRequest proto.InternalMessageInfo type ResumeTaskRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ResumeTaskRequest) Reset() { *m = ResumeTaskRequest{} } +func (*ResumeTaskRequest) ProtoMessage() {} +func (*ResumeTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{17} +} +func (m *ResumeTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResumeTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResumeTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResumeTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResumeTaskRequest.Merge(m, src) +} +func (m *ResumeTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *ResumeTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ResumeTaskRequest.DiscardUnknown(m) } -func (m *ResumeTaskRequest) Reset() { *m = ResumeTaskRequest{} } -func (*ResumeTaskRequest) ProtoMessage() {} -func (*ResumeTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{17} } +var xxx_messageInfo_ResumeTaskRequest proto.InternalMessageInfo type ListPidsRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListPidsRequest) Reset() { *m = ListPidsRequest{} } +func (*ListPidsRequest) ProtoMessage() {} +func (*ListPidsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{18} +} +func (m *ListPidsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListPidsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListPidsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListPidsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListPidsRequest.Merge(m, src) +} +func (m *ListPidsRequest) XXX_Size() int { + return m.Size() +} +func (m *ListPidsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListPidsRequest.DiscardUnknown(m) } -func (m *ListPidsRequest) Reset() { *m = ListPidsRequest{} } -func (*ListPidsRequest) ProtoMessage() {} -func (*ListPidsRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{18} } +var xxx_messageInfo_ListPidsRequest proto.InternalMessageInfo type ListPidsResponse struct { // Processes includes the process ID and additional process information - Processes []*containerd_v1_types.ProcessInfo `protobuf:"bytes,1,rep,name=processes" json:"processes,omitempty"` + Processes []*task.ProcessInfo `protobuf:"bytes,1,rep,name=processes,proto3" json:"processes,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListPidsResponse) Reset() { *m = ListPidsResponse{} } +func (*ListPidsResponse) ProtoMessage() {} +func (*ListPidsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{19} +} +func (m *ListPidsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListPidsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListPidsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListPidsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListPidsResponse.Merge(m, src) +} +func (m *ListPidsResponse) XXX_Size() int { + return m.Size() +} +func (m *ListPidsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListPidsResponse.DiscardUnknown(m) } -func (m *ListPidsResponse) Reset() { *m = ListPidsResponse{} } -func (*ListPidsResponse) ProtoMessage() {} -func (*ListPidsResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{19} } +var xxx_messageInfo_ListPidsResponse proto.InternalMessageInfo type CheckpointTaskRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ParentCheckpoint github_com_opencontainers_go_digest.Digest `protobuf:"bytes,2,opt,name=parent_checkpoint,json=parentCheckpoint,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"parent_checkpoint"` - Options *google_protobuf1.Any `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ParentCheckpoint github_com_opencontainers_go_digest.Digest `protobuf:"bytes,2,opt,name=parent_checkpoint,json=parentCheckpoint,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"parent_checkpoint"` + Options *types1.Any `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CheckpointTaskRequest) Reset() { *m = CheckpointTaskRequest{} } +func (*CheckpointTaskRequest) ProtoMessage() {} +func (*CheckpointTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{20} +} +func (m *CheckpointTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CheckpointTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CheckpointTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CheckpointTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CheckpointTaskRequest.Merge(m, src) +} +func (m *CheckpointTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *CheckpointTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CheckpointTaskRequest.DiscardUnknown(m) } -func (m *CheckpointTaskRequest) Reset() { *m = CheckpointTaskRequest{} } -func (*CheckpointTaskRequest) ProtoMessage() {} -func (*CheckpointTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{20} } +var xxx_messageInfo_CheckpointTaskRequest proto.InternalMessageInfo type CheckpointTaskResponse struct { - Descriptors []*containerd_types2.Descriptor `protobuf:"bytes,1,rep,name=descriptors" json:"descriptors,omitempty"` + Descriptors []*types.Descriptor `protobuf:"bytes,1,rep,name=descriptors,proto3" json:"descriptors,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CheckpointTaskResponse) Reset() { *m = CheckpointTaskResponse{} } +func (*CheckpointTaskResponse) ProtoMessage() {} +func (*CheckpointTaskResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{21} +} +func (m *CheckpointTaskResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CheckpointTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CheckpointTaskResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CheckpointTaskResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CheckpointTaskResponse.Merge(m, src) +} +func (m *CheckpointTaskResponse) XXX_Size() int { + return m.Size() +} +func (m *CheckpointTaskResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CheckpointTaskResponse.DiscardUnknown(m) } -func (m *CheckpointTaskResponse) Reset() { *m = CheckpointTaskResponse{} } -func (*CheckpointTaskResponse) ProtoMessage() {} -func (*CheckpointTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{21} } +var xxx_messageInfo_CheckpointTaskResponse proto.InternalMessageInfo type UpdateTaskRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - Resources *google_protobuf1.Any `protobuf:"bytes,2,opt,name=resources" json:"resources,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + Resources *types1.Any `protobuf:"bytes,2,opt,name=resources,proto3" json:"resources,omitempty"` + Annotations map[string]string `protobuf:"bytes,3,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateTaskRequest) Reset() { *m = UpdateTaskRequest{} } +func (*UpdateTaskRequest) ProtoMessage() {} +func (*UpdateTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{22} +} +func (m *UpdateTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateTaskRequest.Merge(m, src) +} +func (m *UpdateTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateTaskRequest.DiscardUnknown(m) } -func (m *UpdateTaskRequest) Reset() { *m = UpdateTaskRequest{} } -func (*UpdateTaskRequest) ProtoMessage() {} -func (*UpdateTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{22} } +var xxx_messageInfo_UpdateTaskRequest proto.InternalMessageInfo type MetricsRequest struct { - Filters []string `protobuf:"bytes,1,rep,name=filters" json:"filters,omitempty"` + Filters []string `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MetricsRequest) Reset() { *m = MetricsRequest{} } +func (*MetricsRequest) ProtoMessage() {} +func (*MetricsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{23} +} +func (m *MetricsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MetricsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MetricsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MetricsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_MetricsRequest.Merge(m, src) +} +func (m *MetricsRequest) XXX_Size() int { + return m.Size() +} +func (m *MetricsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_MetricsRequest.DiscardUnknown(m) } -func (m *MetricsRequest) Reset() { *m = MetricsRequest{} } -func (*MetricsRequest) ProtoMessage() {} -func (*MetricsRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{23} } +var xxx_messageInfo_MetricsRequest proto.InternalMessageInfo type MetricsResponse struct { - Metrics []*containerd_types1.Metric `protobuf:"bytes,1,rep,name=metrics" json:"metrics,omitempty"` + Metrics []*types.Metric `protobuf:"bytes,1,rep,name=metrics,proto3" json:"metrics,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MetricsResponse) Reset() { *m = MetricsResponse{} } +func (*MetricsResponse) ProtoMessage() {} +func (*MetricsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{24} +} +func (m *MetricsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MetricsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MetricsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MetricsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MetricsResponse.Merge(m, src) +} +func (m *MetricsResponse) XXX_Size() int { + return m.Size() +} +func (m *MetricsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MetricsResponse.DiscardUnknown(m) } -func (m *MetricsResponse) Reset() { *m = MetricsResponse{} } -func (*MetricsResponse) ProtoMessage() {} -func (*MetricsResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{24} } +var xxx_messageInfo_MetricsResponse proto.InternalMessageInfo type WaitRequest struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WaitRequest) Reset() { *m = WaitRequest{} } +func (*WaitRequest) ProtoMessage() {} +func (*WaitRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{25} +} +func (m *WaitRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WaitRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WaitRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WaitRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WaitRequest.Merge(m, src) +} +func (m *WaitRequest) XXX_Size() int { + return m.Size() +} +func (m *WaitRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WaitRequest.DiscardUnknown(m) } -func (m *WaitRequest) Reset() { *m = WaitRequest{} } -func (*WaitRequest) ProtoMessage() {} -func (*WaitRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{25} } +var xxx_messageInfo_WaitRequest proto.InternalMessageInfo type WaitResponse struct { - ExitStatus uint32 `protobuf:"varint,1,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` - ExitedAt time.Time `protobuf:"bytes,2,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` + ExitStatus uint32 `protobuf:"varint,1,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,2,opt,name=exited_at,json=exitedAt,proto3,stdtime" json:"exited_at"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WaitResponse) Reset() { *m = WaitResponse{} } +func (*WaitResponse) ProtoMessage() {} +func (*WaitResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_310e7127b8a26f14, []int{26} +} +func (m *WaitResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WaitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WaitResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WaitResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WaitResponse.Merge(m, src) +} +func (m *WaitResponse) XXX_Size() int { + return m.Size() +} +func (m *WaitResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WaitResponse.DiscardUnknown(m) } -func (m *WaitResponse) Reset() { *m = WaitResponse{} } -func (*WaitResponse) ProtoMessage() {} -func (*WaitResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{26} } +var xxx_messageInfo_WaitResponse proto.InternalMessageInfo func init() { proto.RegisterType((*CreateTaskRequest)(nil), "containerd.services.tasks.v1.CreateTaskRequest") @@ -359,12 +1157,107 @@ func init() { proto.RegisterType((*CheckpointTaskRequest)(nil), "containerd.services.tasks.v1.CheckpointTaskRequest") proto.RegisterType((*CheckpointTaskResponse)(nil), "containerd.services.tasks.v1.CheckpointTaskResponse") proto.RegisterType((*UpdateTaskRequest)(nil), "containerd.services.tasks.v1.UpdateTaskRequest") + proto.RegisterMapType((map[string]string)(nil), "containerd.services.tasks.v1.UpdateTaskRequest.AnnotationsEntry") proto.RegisterType((*MetricsRequest)(nil), "containerd.services.tasks.v1.MetricsRequest") proto.RegisterType((*MetricsResponse)(nil), "containerd.services.tasks.v1.MetricsResponse") proto.RegisterType((*WaitRequest)(nil), "containerd.services.tasks.v1.WaitRequest") proto.RegisterType((*WaitResponse)(nil), "containerd.services.tasks.v1.WaitResponse") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/tasks/v1/tasks.proto", fileDescriptor_310e7127b8a26f14) +} + +var fileDescriptor_310e7127b8a26f14 = []byte{ + // 1376 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x5b, 0x6f, 0x1b, 0x45, + 0x14, 0xee, 0xfa, 0xee, 0xe3, 0xa4, 0x4d, 0x96, 0x34, 0x98, 0xa5, 0x8a, 0xc3, 0xf2, 0x62, 0x02, + 0x5d, 0x53, 0x17, 0x55, 0x55, 0x5b, 0x55, 0xe4, 0x46, 0x64, 0x41, 0xd5, 0x74, 0x5b, 0xa0, 0xaa, + 0x84, 0xc2, 0xc6, 0x3b, 0x71, 0x46, 0xb1, 0x77, 0xb6, 0x3b, 0xe3, 0xb4, 0xe6, 0x05, 0x7e, 0x42, + 0x5f, 0x79, 0x81, 0xbf, 0x93, 0x47, 0x1e, 0x11, 0xaa, 0x02, 0xf5, 0xbf, 0xe0, 0x0d, 0xcd, 0x65, + 0xd7, 0x1b, 0x3b, 0xf6, 0x3a, 0x4d, 0xc3, 0x4b, 0x32, 0x33, 0x7b, 0xce, 0x99, 0x33, 0xdf, 0xb9, + 0x7d, 0x09, 0xac, 0xb5, 0x30, 0xdb, 0xef, 0xee, 0x5a, 0x4d, 0xd2, 0xa9, 0x35, 0x89, 0xc7, 0x1c, + 0xec, 0xa1, 0xc0, 0x8d, 0x2f, 0x1d, 0x1f, 0xd7, 0x28, 0x0a, 0x0e, 0x71, 0x13, 0xd1, 0x1a, 0x73, + 0xe8, 0x01, 0xad, 0x1d, 0xde, 0x90, 0x0b, 0xcb, 0x0f, 0x08, 0x23, 0xfa, 0xb5, 0x81, 0xb4, 0x15, + 0x4a, 0x5a, 0x52, 0xe0, 0xf0, 0x86, 0xf1, 0x61, 0x8b, 0x90, 0x56, 0x1b, 0xd5, 0x84, 0xec, 0x6e, + 0x77, 0xaf, 0x86, 0x3a, 0x3e, 0xeb, 0x49, 0x55, 0xe3, 0x83, 0xe1, 0x8f, 0x8e, 0x17, 0x7e, 0x5a, + 0x68, 0x91, 0x16, 0x11, 0xcb, 0x1a, 0x5f, 0xa9, 0xd3, 0x5b, 0x53, 0xf9, 0xcb, 0x7a, 0x3e, 0xa2, + 0xb5, 0x0e, 0xe9, 0x7a, 0x4c, 0xe9, 0xdd, 0x3e, 0x8b, 0x1e, 0x62, 0x01, 0x6e, 0xaa, 0xd7, 0x19, + 0x77, 0xcf, 0xa0, 0xe9, 0x22, 0xda, 0x0c, 0xb0, 0xcf, 0x48, 0xa0, 0x94, 0xef, 0x9c, 0x41, 0x99, + 0x23, 0x26, 0x7e, 0x28, 0xdd, 0xca, 0x30, 0x36, 0x0c, 0x77, 0x10, 0x65, 0x4e, 0xc7, 0x97, 0x02, + 0xe6, 0x51, 0x0a, 0xe6, 0xd7, 0x03, 0xe4, 0x30, 0xf4, 0xc4, 0xa1, 0x07, 0x36, 0x7a, 0xde, 0x45, + 0x94, 0xe9, 0x75, 0x98, 0x89, 0xcc, 0xef, 0x60, 0xb7, 0xac, 0x2d, 0x6b, 0xd5, 0xe2, 0xda, 0x95, + 0xfe, 0x71, 0xa5, 0xb4, 0x1e, 0x9e, 0x37, 0x36, 0xec, 0x52, 0x24, 0xd4, 0x70, 0xf5, 0x1a, 0xe4, + 0x02, 0x42, 0xd8, 0x1e, 0x2d, 0xa7, 0x97, 0xd3, 0xd5, 0x52, 0xfd, 0x7d, 0x2b, 0x16, 0x52, 0xe1, + 0x9d, 0xf5, 0x80, 0x83, 0x69, 0x2b, 0x31, 0x7d, 0x01, 0xb2, 0x94, 0xb9, 0xd8, 0x2b, 0x67, 0xb8, + 0x75, 0x5b, 0x6e, 0xf4, 0x45, 0xc8, 0x51, 0xe6, 0x92, 0x2e, 0x2b, 0x67, 0xc5, 0xb1, 0xda, 0xa9, + 0x73, 0x14, 0x04, 0xe5, 0x5c, 0x74, 0x8e, 0x82, 0x40, 0x37, 0xa0, 0xc0, 0x50, 0xd0, 0xc1, 0x9e, + 0xd3, 0x2e, 0xe7, 0x97, 0xb5, 0x6a, 0xc1, 0x8e, 0xf6, 0xfa, 0x3d, 0x80, 0xe6, 0x3e, 0x6a, 0x1e, + 0xf8, 0x04, 0x7b, 0xac, 0x5c, 0x58, 0xd6, 0xaa, 0xa5, 0xfa, 0xb5, 0x51, 0xb7, 0x36, 0x22, 0xc4, + 0xed, 0x98, 0xbc, 0x6e, 0x41, 0x9e, 0xf8, 0x0c, 0x13, 0x8f, 0x96, 0x8b, 0x42, 0x75, 0xc1, 0x92, + 0x68, 0x5a, 0x21, 0x9a, 0xd6, 0xaa, 0xd7, 0xb3, 0x43, 0x21, 0xf3, 0x19, 0xe8, 0x71, 0x24, 0xa9, + 0x4f, 0x3c, 0x8a, 0xde, 0x0a, 0xca, 0x39, 0x48, 0xfb, 0xd8, 0x2d, 0xa7, 0x96, 0xb5, 0xea, 0xac, + 0xcd, 0x97, 0x66, 0x0b, 0x66, 0x1e, 0x33, 0x27, 0x60, 0xe7, 0x09, 0xd0, 0xc7, 0x90, 0x47, 0x2f, + 0x51, 0x73, 0x47, 0x59, 0x2e, 0xae, 0x41, 0xff, 0xb8, 0x92, 0xdb, 0x7c, 0x89, 0x9a, 0x8d, 0x0d, + 0x3b, 0xc7, 0x3f, 0x35, 0x5c, 0xf3, 0x23, 0x98, 0x55, 0x17, 0x29, 0xff, 0x95, 0x2f, 0xda, 0xc0, + 0x97, 0x2d, 0x98, 0xdf, 0x40, 0x6d, 0x74, 0xee, 0x8c, 0x31, 0x7f, 0xd3, 0xe0, 0xb2, 0xb4, 0x14, + 0xdd, 0xb6, 0x08, 0xa9, 0x48, 0x39, 0xd7, 0x3f, 0xae, 0xa4, 0x1a, 0x1b, 0x76, 0x0a, 0x9f, 0x82, + 0x88, 0x5e, 0x81, 0x12, 0x7a, 0x89, 0xd9, 0x0e, 0x65, 0x0e, 0xeb, 0xf2, 0x9c, 0xe3, 0x5f, 0x80, + 0x1f, 0x3d, 0x16, 0x27, 0xfa, 0x2a, 0x14, 0xf9, 0x0e, 0xb9, 0x3b, 0x0e, 0x13, 0x29, 0x56, 0xaa, + 0x1b, 0x23, 0x01, 0x7c, 0x12, 0x96, 0xc3, 0x5a, 0xe1, 0xe8, 0xb8, 0x72, 0xe9, 0xd5, 0xdf, 0x15, + 0xcd, 0x2e, 0x48, 0xb5, 0x55, 0x66, 0x12, 0x58, 0x90, 0xfe, 0x6d, 0x07, 0xa4, 0x89, 0x28, 0xbd, + 0x70, 0xf4, 0x11, 0xc0, 0x16, 0xba, 0xf8, 0x20, 0x6f, 0x42, 0x49, 0x5c, 0xa3, 0x40, 0xbf, 0x05, + 0x79, 0x5f, 0x3e, 0x50, 0x5c, 0x31, 0x54, 0x23, 0x87, 0x37, 0x54, 0x99, 0x84, 0x20, 0x84, 0xc2, + 0xe6, 0x0a, 0xcc, 0x7d, 0x83, 0x29, 0xe3, 0x69, 0x10, 0x41, 0xb3, 0x08, 0xb9, 0x3d, 0xdc, 0x66, + 0x28, 0x90, 0xde, 0xda, 0x6a, 0xc7, 0x93, 0x26, 0x26, 0x1b, 0xd5, 0x46, 0x56, 0xb4, 0xf8, 0xb2, + 0x26, 0x3a, 0xc6, 0xe4, 0x6b, 0xa5, 0xa8, 0xf9, 0x4a, 0x83, 0xd2, 0xd7, 0xb8, 0xdd, 0xbe, 0x68, + 0x90, 0x44, 0xc3, 0xc1, 0x2d, 0xde, 0x56, 0x64, 0x6e, 0xa9, 0x1d, 0x4f, 0x45, 0xa7, 0xdd, 0x16, + 0x19, 0x55, 0xb0, 0xf9, 0xd2, 0xfc, 0x57, 0x03, 0x9d, 0x2b, 0xbf, 0x83, 0x2c, 0x89, 0x7a, 0x62, + 0xea, 0xf4, 0x9e, 0x98, 0x1e, 0xd3, 0x13, 0x33, 0x63, 0x7b, 0x62, 0x76, 0xa8, 0x27, 0x56, 0x21, + 0x43, 0x7d, 0xd4, 0x14, 0x5d, 0x74, 0x5c, 0x4b, 0x13, 0x12, 0x71, 0x94, 0xf2, 0x63, 0x53, 0xe9, + 0x2a, 0xbc, 0x77, 0xe2, 0xe9, 0x32, 0xb2, 0xe6, 0xaf, 0x1a, 0xcc, 0xd9, 0x88, 0xe2, 0x9f, 0xd0, + 0x36, 0xeb, 0x5d, 0x78, 0xa8, 0x16, 0x20, 0xfb, 0x02, 0xbb, 0x6c, 0x5f, 0x45, 0x4a, 0x6e, 0x38, + 0x3a, 0xfb, 0x08, 0xb7, 0xf6, 0x65, 0xf5, 0xcf, 0xda, 0x6a, 0x67, 0xfe, 0x0c, 0x97, 0xd7, 0xdb, + 0x84, 0xa2, 0xc6, 0xc3, 0xff, 0xc3, 0x31, 0x19, 0xce, 0xb4, 0x88, 0x82, 0xdc, 0x98, 0x5f, 0xc1, + 0xdc, 0xb6, 0xd3, 0xa5, 0xe7, 0xee, 0x9f, 0x5b, 0x30, 0x6f, 0x23, 0xda, 0xed, 0x9c, 0xdb, 0xd0, + 0x26, 0x5c, 0xe1, 0xc5, 0xb9, 0x8d, 0xdd, 0xf3, 0x24, 0xaf, 0x69, 0xcb, 0x7e, 0x20, 0xcd, 0xa8, + 0x12, 0xbf, 0x0f, 0x45, 0xd5, 0x2e, 0x50, 0x58, 0xe6, 0xcb, 0x93, 0xca, 0xbc, 0xe1, 0xed, 0x11, + 0x7b, 0xa0, 0x62, 0xbe, 0xd6, 0xe0, 0xea, 0x7a, 0x34, 0x93, 0xcf, 0xcb, 0x51, 0x76, 0x60, 0xde, + 0x77, 0x02, 0xe4, 0xb1, 0x9d, 0x18, 0x2f, 0x90, 0xe1, 0xab, 0xf3, 0xfe, 0xff, 0xd7, 0x71, 0x65, + 0x25, 0xc6, 0xb6, 0x88, 0x8f, 0xbc, 0x48, 0x9d, 0xd6, 0x5a, 0xe4, 0xba, 0x8b, 0x5b, 0x88, 0x32, + 0x6b, 0x43, 0xfc, 0xb2, 0xe7, 0xa4, 0xb1, 0xf5, 0x53, 0x39, 0x43, 0x7a, 0x1a, 0xce, 0xf0, 0x14, + 0x16, 0x87, 0x5f, 0x17, 0x01, 0x57, 0x1a, 0x30, 0xc1, 0x53, 0x3b, 0xe4, 0x08, 0x79, 0x89, 0x2b, + 0x98, 0xbf, 0xa7, 0x60, 0xfe, 0x5b, 0xdf, 0x7d, 0x07, 0xc4, 0xae, 0x0e, 0xc5, 0x00, 0x51, 0xd2, + 0x0d, 0x9a, 0x88, 0x0a, 0xb0, 0xc6, 0xbd, 0x6a, 0x20, 0xa6, 0xef, 0x42, 0xc9, 0xf1, 0x3c, 0xc2, + 0x9c, 0x10, 0x0b, 0xee, 0xfd, 0x97, 0xd6, 0x24, 0x92, 0x6f, 0x8d, 0x78, 0x6b, 0xad, 0x0e, 0x4c, + 0x6c, 0x7a, 0x2c, 0xe8, 0xd9, 0x71, 0xa3, 0xc6, 0x7d, 0x98, 0x1b, 0x16, 0xe0, 0xcd, 0xf9, 0x00, + 0xf5, 0xd4, 0xec, 0xe1, 0x4b, 0x5e, 0x82, 0x87, 0x4e, 0xbb, 0x8b, 0xc2, 0x8e, 0x2a, 0x36, 0x77, + 0x52, 0xb7, 0x35, 0x73, 0x05, 0x2e, 0x3f, 0x90, 0x2c, 0x3d, 0x44, 0xa7, 0x0c, 0x79, 0x39, 0xae, + 0x24, 0xde, 0x45, 0x3b, 0xdc, 0xf2, 0x0a, 0x89, 0x64, 0xa3, 0xe1, 0x95, 0x57, 0x24, 0x5f, 0x05, + 0xa7, 0x7c, 0x0a, 0xe1, 0x15, 0x02, 0x76, 0x28, 0x68, 0xee, 0x41, 0xe9, 0x7b, 0x07, 0x5f, 0xfc, + 0x80, 0x0f, 0x60, 0x46, 0xde, 0xa3, 0x7c, 0x1d, 0x22, 0x4b, 0xda, 0x64, 0xb2, 0x94, 0x7a, 0x1b, + 0xb2, 0x54, 0x7f, 0x3d, 0x03, 0x59, 0x31, 0xde, 0xf5, 0x03, 0xc8, 0x49, 0x22, 0xac, 0xd7, 0x26, + 0x47, 0x7c, 0xe4, 0x0f, 0x0f, 0xe3, 0xf3, 0xe9, 0x15, 0xd4, 0xd3, 0x7e, 0x84, 0xac, 0x20, 0xac, + 0xfa, 0xca, 0x64, 0xd5, 0x38, 0x7d, 0x36, 0x3e, 0x9d, 0x4a, 0x56, 0xdd, 0xd0, 0x82, 0x9c, 0x64, + 0x81, 0x49, 0xcf, 0x19, 0x61, 0xc5, 0xc6, 0x67, 0xd3, 0x28, 0x44, 0x17, 0x3d, 0x87, 0xd9, 0x13, + 0x74, 0x53, 0xaf, 0x4f, 0xa3, 0x7e, 0x92, 0x75, 0x9c, 0xf1, 0xca, 0x67, 0x90, 0xde, 0x42, 0x4c, + 0xaf, 0x4e, 0x56, 0x1a, 0x70, 0x52, 0xe3, 0x93, 0x29, 0x24, 0x23, 0xdc, 0x32, 0x7c, 0x1c, 0xe8, + 0xd6, 0x64, 0x95, 0x61, 0x0a, 0x69, 0xd4, 0xa6, 0x96, 0x57, 0x17, 0x35, 0x20, 0xc3, 0x19, 0xa1, + 0x9e, 0xe0, 0x5b, 0x8c, 0x35, 0x1a, 0x8b, 0x23, 0xc9, 0xbd, 0xd9, 0xf1, 0x59, 0x4f, 0xdf, 0x86, + 0x0c, 0x2f, 0x25, 0x3d, 0x21, 0x0f, 0x47, 0xd9, 0xde, 0x58, 0x8b, 0x8f, 0xa1, 0x18, 0x11, 0xa1, + 0x24, 0x28, 0x86, 0x19, 0xd3, 0x58, 0xa3, 0x0f, 0x21, 0xaf, 0x28, 0x8c, 0x9e, 0x10, 0xef, 0x93, + 0x4c, 0x67, 0x82, 0xc1, 0xac, 0xa0, 0x24, 0x49, 0x1e, 0x0e, 0xf3, 0x96, 0xb1, 0x06, 0x1f, 0x41, + 0x4e, 0x72, 0x93, 0xa4, 0xa2, 0x19, 0x61, 0x30, 0x63, 0x4d, 0x62, 0x28, 0x84, 0xf4, 0x42, 0xbf, + 0x9e, 0x9c, 0x23, 0x31, 0x36, 0x63, 0x58, 0xd3, 0x8a, 0xab, 0x8c, 0x7a, 0x01, 0x10, 0x1b, 0xea, + 0x37, 0x13, 0x20, 0x3e, 0x8d, 0x9e, 0x18, 0x5f, 0x9c, 0x4d, 0x49, 0x5d, 0xfc, 0x08, 0x72, 0x72, + 0x0c, 0x26, 0xc1, 0x36, 0x32, 0x2c, 0xc7, 0xc2, 0xb6, 0x07, 0x79, 0x35, 0xba, 0x92, 0x72, 0xe5, + 0xe4, 0x34, 0x34, 0xae, 0x4f, 0x29, 0xad, 0x5c, 0xff, 0x01, 0x32, 0x7c, 0xe6, 0x24, 0x55, 0x61, + 0x6c, 0xfe, 0x19, 0x2b, 0xd3, 0x88, 0x4a, 0xf3, 0x6b, 0xdf, 0x1d, 0xbd, 0x59, 0xba, 0xf4, 0xe7, + 0x9b, 0xa5, 0x4b, 0xbf, 0xf4, 0x97, 0xb4, 0xa3, 0xfe, 0x92, 0xf6, 0x47, 0x7f, 0x49, 0xfb, 0xa7, + 0xbf, 0xa4, 0x3d, 0xbb, 0xf7, 0x76, 0xff, 0x7e, 0xbc, 0x2b, 0x16, 0x4f, 0x53, 0xbb, 0x39, 0x01, + 0xd8, 0xcd, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x55, 0xbf, 0x54, 0xc7, 0x14, 0x00, 0x00, +} + // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn @@ -373,8 +1266,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Tasks service - +// TasksClient is the client API for Tasks service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type TasksClient interface { // Create a task. Create(ctx context.Context, in *CreateTaskRequest, opts ...grpc.CallOption) (*CreateTaskResponse, error) @@ -386,15 +1280,15 @@ type TasksClient interface { Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) List(ctx context.Context, in *ListTasksRequest, opts ...grpc.CallOption) (*ListTasksResponse, error) // Kill a task or process. - Kill(ctx context.Context, in *KillRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) - Exec(ctx context.Context, in *ExecProcessRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) - ResizePty(ctx context.Context, in *ResizePtyRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) - CloseIO(ctx context.Context, in *CloseIORequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) - Pause(ctx context.Context, in *PauseTaskRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) - Resume(ctx context.Context, in *ResumeTaskRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) + Kill(ctx context.Context, in *KillRequest, opts ...grpc.CallOption) (*types1.Empty, error) + Exec(ctx context.Context, in *ExecProcessRequest, opts ...grpc.CallOption) (*types1.Empty, error) + ResizePty(ctx context.Context, in *ResizePtyRequest, opts ...grpc.CallOption) (*types1.Empty, error) + CloseIO(ctx context.Context, in *CloseIORequest, opts ...grpc.CallOption) (*types1.Empty, error) + Pause(ctx context.Context, in *PauseTaskRequest, opts ...grpc.CallOption) (*types1.Empty, error) + Resume(ctx context.Context, in *ResumeTaskRequest, opts ...grpc.CallOption) (*types1.Empty, error) ListPids(ctx context.Context, in *ListPidsRequest, opts ...grpc.CallOption) (*ListPidsResponse, error) Checkpoint(ctx context.Context, in *CheckpointTaskRequest, opts ...grpc.CallOption) (*CheckpointTaskResponse, error) - Update(ctx context.Context, in *UpdateTaskRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) + Update(ctx context.Context, in *UpdateTaskRequest, opts ...grpc.CallOption) (*types1.Empty, error) Metrics(ctx context.Context, in *MetricsRequest, opts ...grpc.CallOption) (*MetricsResponse, error) Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*WaitResponse, error) } @@ -409,7 +1303,7 @@ func NewTasksClient(cc *grpc.ClientConn) TasksClient { func (c *tasksClient) Create(ctx context.Context, in *CreateTaskRequest, opts ...grpc.CallOption) (*CreateTaskResponse, error) { out := new(CreateTaskResponse) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Create", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Create", in, out, opts...) if err != nil { return nil, err } @@ -418,7 +1312,7 @@ func (c *tasksClient) Create(ctx context.Context, in *CreateTaskRequest, opts .. func (c *tasksClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) { out := new(StartResponse) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Start", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Start", in, out, opts...) if err != nil { return nil, err } @@ -427,7 +1321,7 @@ func (c *tasksClient) Start(ctx context.Context, in *StartRequest, opts ...grpc. func (c *tasksClient) Delete(ctx context.Context, in *DeleteTaskRequest, opts ...grpc.CallOption) (*DeleteResponse, error) { out := new(DeleteResponse) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Delete", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Delete", in, out, opts...) if err != nil { return nil, err } @@ -436,7 +1330,7 @@ func (c *tasksClient) Delete(ctx context.Context, in *DeleteTaskRequest, opts .. func (c *tasksClient) DeleteProcess(ctx context.Context, in *DeleteProcessRequest, opts ...grpc.CallOption) (*DeleteResponse, error) { out := new(DeleteResponse) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/DeleteProcess", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/DeleteProcess", in, out, opts...) if err != nil { return nil, err } @@ -445,7 +1339,7 @@ func (c *tasksClient) DeleteProcess(ctx context.Context, in *DeleteProcessReques func (c *tasksClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) { out := new(GetResponse) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Get", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Get", in, out, opts...) if err != nil { return nil, err } @@ -454,61 +1348,61 @@ func (c *tasksClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.Call func (c *tasksClient) List(ctx context.Context, in *ListTasksRequest, opts ...grpc.CallOption) (*ListTasksResponse, error) { out := new(ListTasksResponse) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/List", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/List", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *tasksClient) Kill(ctx context.Context, in *KillRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) { - out := new(google_protobuf.Empty) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Kill", in, out, c.cc, opts...) +func (c *tasksClient) Kill(ctx context.Context, in *KillRequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Kill", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *tasksClient) Exec(ctx context.Context, in *ExecProcessRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) { - out := new(google_protobuf.Empty) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Exec", in, out, c.cc, opts...) +func (c *tasksClient) Exec(ctx context.Context, in *ExecProcessRequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Exec", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *tasksClient) ResizePty(ctx context.Context, in *ResizePtyRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) { - out := new(google_protobuf.Empty) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/ResizePty", in, out, c.cc, opts...) +func (c *tasksClient) ResizePty(ctx context.Context, in *ResizePtyRequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/ResizePty", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *tasksClient) CloseIO(ctx context.Context, in *CloseIORequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) { - out := new(google_protobuf.Empty) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/CloseIO", in, out, c.cc, opts...) +func (c *tasksClient) CloseIO(ctx context.Context, in *CloseIORequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/CloseIO", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *tasksClient) Pause(ctx context.Context, in *PauseTaskRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) { - out := new(google_protobuf.Empty) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Pause", in, out, c.cc, opts...) +func (c *tasksClient) Pause(ctx context.Context, in *PauseTaskRequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Pause", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *tasksClient) Resume(ctx context.Context, in *ResumeTaskRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) { - out := new(google_protobuf.Empty) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Resume", in, out, c.cc, opts...) +func (c *tasksClient) Resume(ctx context.Context, in *ResumeTaskRequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Resume", in, out, opts...) if err != nil { return nil, err } @@ -517,7 +1411,7 @@ func (c *tasksClient) Resume(ctx context.Context, in *ResumeTaskRequest, opts .. func (c *tasksClient) ListPids(ctx context.Context, in *ListPidsRequest, opts ...grpc.CallOption) (*ListPidsResponse, error) { out := new(ListPidsResponse) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/ListPids", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/ListPids", in, out, opts...) if err != nil { return nil, err } @@ -526,16 +1420,16 @@ func (c *tasksClient) ListPids(ctx context.Context, in *ListPidsRequest, opts .. func (c *tasksClient) Checkpoint(ctx context.Context, in *CheckpointTaskRequest, opts ...grpc.CallOption) (*CheckpointTaskResponse, error) { out := new(CheckpointTaskResponse) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Checkpoint", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Checkpoint", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *tasksClient) Update(ctx context.Context, in *UpdateTaskRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) { - out := new(google_protobuf.Empty) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Update", in, out, c.cc, opts...) +func (c *tasksClient) Update(ctx context.Context, in *UpdateTaskRequest, opts ...grpc.CallOption) (*types1.Empty, error) { + out := new(types1.Empty) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Update", in, out, opts...) if err != nil { return nil, err } @@ -544,7 +1438,7 @@ func (c *tasksClient) Update(ctx context.Context, in *UpdateTaskRequest, opts .. func (c *tasksClient) Metrics(ctx context.Context, in *MetricsRequest, opts ...grpc.CallOption) (*MetricsResponse, error) { out := new(MetricsResponse) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Metrics", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Metrics", in, out, opts...) if err != nil { return nil, err } @@ -553,15 +1447,14 @@ func (c *tasksClient) Metrics(ctx context.Context, in *MetricsRequest, opts ...g func (c *tasksClient) Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*WaitResponse, error) { out := new(WaitResponse) - err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Wait", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Wait", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Tasks service - +// TasksServer is the server API for Tasks service. type TasksServer interface { // Create a task. Create(context.Context, *CreateTaskRequest) (*CreateTaskResponse, error) @@ -573,19 +1466,75 @@ type TasksServer interface { Get(context.Context, *GetRequest) (*GetResponse, error) List(context.Context, *ListTasksRequest) (*ListTasksResponse, error) // Kill a task or process. - Kill(context.Context, *KillRequest) (*google_protobuf.Empty, error) - Exec(context.Context, *ExecProcessRequest) (*google_protobuf.Empty, error) - ResizePty(context.Context, *ResizePtyRequest) (*google_protobuf.Empty, error) - CloseIO(context.Context, *CloseIORequest) (*google_protobuf.Empty, error) - Pause(context.Context, *PauseTaskRequest) (*google_protobuf.Empty, error) - Resume(context.Context, *ResumeTaskRequest) (*google_protobuf.Empty, error) + Kill(context.Context, *KillRequest) (*types1.Empty, error) + Exec(context.Context, *ExecProcessRequest) (*types1.Empty, error) + ResizePty(context.Context, *ResizePtyRequest) (*types1.Empty, error) + CloseIO(context.Context, *CloseIORequest) (*types1.Empty, error) + Pause(context.Context, *PauseTaskRequest) (*types1.Empty, error) + Resume(context.Context, *ResumeTaskRequest) (*types1.Empty, error) ListPids(context.Context, *ListPidsRequest) (*ListPidsResponse, error) Checkpoint(context.Context, *CheckpointTaskRequest) (*CheckpointTaskResponse, error) - Update(context.Context, *UpdateTaskRequest) (*google_protobuf.Empty, error) + Update(context.Context, *UpdateTaskRequest) (*types1.Empty, error) Metrics(context.Context, *MetricsRequest) (*MetricsResponse, error) Wait(context.Context, *WaitRequest) (*WaitResponse, error) } +// UnimplementedTasksServer can be embedded to have forward compatible implementations. +type UnimplementedTasksServer struct { +} + +func (*UnimplementedTasksServer) Create(ctx context.Context, req *CreateTaskRequest) (*CreateTaskResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Create not implemented") +} +func (*UnimplementedTasksServer) Start(ctx context.Context, req *StartRequest) (*StartResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Start not implemented") +} +func (*UnimplementedTasksServer) Delete(ctx context.Context, req *DeleteTaskRequest) (*DeleteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented") +} +func (*UnimplementedTasksServer) DeleteProcess(ctx context.Context, req *DeleteProcessRequest) (*DeleteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteProcess not implemented") +} +func (*UnimplementedTasksServer) Get(ctx context.Context, req *GetRequest) (*GetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (*UnimplementedTasksServer) List(ctx context.Context, req *ListTasksRequest) (*ListTasksResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (*UnimplementedTasksServer) Kill(ctx context.Context, req *KillRequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Kill not implemented") +} +func (*UnimplementedTasksServer) Exec(ctx context.Context, req *ExecProcessRequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Exec not implemented") +} +func (*UnimplementedTasksServer) ResizePty(ctx context.Context, req *ResizePtyRequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method ResizePty not implemented") +} +func (*UnimplementedTasksServer) CloseIO(ctx context.Context, req *CloseIORequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method CloseIO not implemented") +} +func (*UnimplementedTasksServer) Pause(ctx context.Context, req *PauseTaskRequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Pause not implemented") +} +func (*UnimplementedTasksServer) Resume(ctx context.Context, req *ResumeTaskRequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Resume not implemented") +} +func (*UnimplementedTasksServer) ListPids(ctx context.Context, req *ListPidsRequest) (*ListPidsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListPids not implemented") +} +func (*UnimplementedTasksServer) Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*CheckpointTaskResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Checkpoint not implemented") +} +func (*UnimplementedTasksServer) Update(ctx context.Context, req *UpdateTaskRequest) (*types1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Update not implemented") +} +func (*UnimplementedTasksServer) Metrics(ctx context.Context, req *MetricsRequest) (*MetricsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Metrics not implemented") +} +func (*UnimplementedTasksServer) Wait(ctx context.Context, req *WaitRequest) (*WaitResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Wait not implemented") +} + func RegisterTasksServer(s *grpc.Server, srv TasksServer) { s.RegisterService(&_Tasks_serviceDesc, srv) } @@ -976,7 +1925,7 @@ var _Tasks_serviceDesc = grpc.ServiceDesc{ func (m *CreateTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -984,83 +1933,102 @@ func (m *CreateTaskRequest) Marshal() (dAtA []byte, err error) { } func (m *CreateTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Rootfs) > 0 { - for _, msg := range m.Rootfs { - dAtA[i] = 0x1a - i++ - i = encodeVarintTasks(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Options != nil { + { + size, err := m.Options.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x4a } - if len(m.Stdin) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.Stdin))) - i += copy(dAtA[i:], m.Stdin) - } - if len(m.Stdout) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.Stdout))) - i += copy(dAtA[i:], m.Stdout) - } - if len(m.Stderr) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.Stderr))) - i += copy(dAtA[i:], m.Stderr) + if m.Checkpoint != nil { + { + size, err := m.Checkpoint.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 } if m.Terminal { - dAtA[i] = 0x38 - i++ + i-- if m.Terminal { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x38 } - if m.Checkpoint != nil { - dAtA[i] = 0x42 - i++ - i = encodeVarintTasks(dAtA, i, uint64(m.Checkpoint.Size())) - n1, err := m.Checkpoint.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = encodeVarintTasks(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x32 } - if m.Options != nil { - dAtA[i] = 0x4a - i++ - i = encodeVarintTasks(dAtA, i, uint64(m.Options.Size())) - n2, err := m.Options.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 + if len(m.Stdout) > 0 { + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) + i = encodeVarintTasks(dAtA, i, uint64(len(m.Stdout))) + i-- + dAtA[i] = 0x2a } - return i, nil -} + if len(m.Stdin) > 0 { + i -= len(m.Stdin) + copy(dAtA[i:], m.Stdin) + i = encodeVarintTasks(dAtA, i, uint64(len(m.Stdin))) + i-- + dAtA[i] = 0x22 + } + if len(m.Rootfs) > 0 { + for iNdEx := len(m.Rootfs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Rootfs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} func (m *CreateTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1068,28 +2036,38 @@ func (m *CreateTaskResponse) Marshal() (dAtA []byte, err error) { } func (m *CreateTaskResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Pid != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintTasks(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 } - return i, nil + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *StartRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1097,29 +2075,40 @@ func (m *StartRequest) Marshal() (dAtA []byte, err error) { } func (m *StartRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StartRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ExecID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *StartResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1127,22 +2116,31 @@ func (m *StartResponse) Marshal() (dAtA []byte, err error) { } func (m *StartResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StartResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Pid != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintTasks(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *DeleteTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1150,23 +2148,33 @@ func (m *DeleteTaskRequest) Marshal() (dAtA []byte, err error) { } func (m *DeleteTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *DeleteResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1174,41 +2182,51 @@ func (m *DeleteResponse) Marshal() (dAtA []byte, err error) { } func (m *DeleteResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if m.Pid != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintTasks(dAtA, i, uint64(m.Pid)) + n3, err3 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExitedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt):]) + if err3 != nil { + return 0, err3 } + i -= n3 + i = encodeVarintTasks(dAtA, i, uint64(n3)) + i-- + dAtA[i] = 0x22 if m.ExitStatus != 0 { - dAtA[i] = 0x18 - i++ i = encodeVarintTasks(dAtA, i, uint64(m.ExitStatus)) + i-- + dAtA[i] = 0x18 } - dAtA[i] = 0x22 - i++ - i = encodeVarintTasks(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) - n3, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) - if err != nil { - return 0, err + if m.Pid != 0 { + i = encodeVarintTasks(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - i += n3 - return i, nil + return len(dAtA) - i, nil } func (m *DeleteProcessRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1216,29 +2234,40 @@ func (m *DeleteProcessRequest) Marshal() (dAtA []byte, err error) { } func (m *DeleteProcessRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteProcessRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ExecID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) + i-- + dAtA[i] = 0x12 + } + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *GetRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1246,29 +2275,40 @@ func (m *GetRequest) Marshal() (dAtA []byte, err error) { } func (m *GetRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ExecID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) + i-- + dAtA[i] = 0x12 + } + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *GetResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1276,27 +2316,38 @@ func (m *GetResponse) Marshal() (dAtA []byte, err error) { } func (m *GetResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Process != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(m.Process.Size())) - n4, err := m.Process.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Process.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) } - i += n4 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListTasksRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1304,23 +2355,33 @@ func (m *ListTasksRequest) Marshal() (dAtA []byte, err error) { } func (m *ListTasksRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListTasksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Filter) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Filter) + copy(dAtA[i:], m.Filter) i = encodeVarintTasks(dAtA, i, uint64(len(m.Filter))) - i += copy(dAtA[i:], m.Filter) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListTasksResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1328,29 +2389,40 @@ func (m *ListTasksResponse) Marshal() (dAtA []byte, err error) { } func (m *ListTasksResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListTasksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Tasks) > 0 { - for _, msg := range m.Tasks { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Tasks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Tasks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *KillRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1358,44 +2430,55 @@ func (m *KillRequest) Marshal() (dAtA []byte, err error) { } func (m *KillRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KillRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) - } - if len(m.ExecID) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) - } - if m.Signal != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintTasks(dAtA, i, uint64(m.Signal)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.All { - dAtA[i] = 0x20 - i++ + i-- if m.All { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x20 + } + if m.Signal != 0 { + i = encodeVarintTasks(dAtA, i, uint64(m.Signal)) + i-- + dAtA[i] = 0x18 } - return i, nil + if len(m.ExecID) > 0 { + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) + i-- + dAtA[i] = 0x12 + } + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *ExecProcessRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1403,67 +2486,83 @@ func (m *ExecProcessRequest) Marshal() (dAtA []byte, err error) { } func (m *ExecProcessRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExecProcessRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) - } - if len(m.Stdin) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.Stdin))) - i += copy(dAtA[i:], m.Stdin) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Stdout) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.Stdout))) - i += copy(dAtA[i:], m.Stdout) + if len(m.ExecID) > 0 { + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) + i-- + dAtA[i] = 0x3a } - if len(m.Stderr) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.Stderr))) - i += copy(dAtA[i:], m.Stderr) + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 } if m.Terminal { - dAtA[i] = 0x28 - i++ + i-- if m.Terminal { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x28 } - if m.Spec != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintTasks(dAtA, i, uint64(m.Spec.Size())) - n5, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n5 + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = encodeVarintTasks(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x22 } - if len(m.ExecID) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) + if len(m.Stdout) > 0 { + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) + i = encodeVarintTasks(dAtA, i, uint64(len(m.Stdout))) + i-- + dAtA[i] = 0x1a } - return i, nil + if len(m.Stdin) > 0 { + i -= len(m.Stdin) + copy(dAtA[i:], m.Stdin) + i = encodeVarintTasks(dAtA, i, uint64(len(m.Stdin))) + i-- + dAtA[i] = 0x12 + } + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *ExecProcessResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1471,17 +2570,26 @@ func (m *ExecProcessResponse) Marshal() (dAtA []byte, err error) { } func (m *ExecProcessResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExecProcessResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + return len(dAtA) - i, nil } func (m *ResizePtyRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1489,39 +2597,50 @@ func (m *ResizePtyRequest) Marshal() (dAtA []byte, err error) { } func (m *ResizePtyRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResizePtyRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.ExecID) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) + if m.Height != 0 { + i = encodeVarintTasks(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x20 } if m.Width != 0 { - dAtA[i] = 0x18 - i++ i = encodeVarintTasks(dAtA, i, uint64(m.Width)) + i-- + dAtA[i] = 0x18 } - if m.Height != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintTasks(dAtA, i, uint64(m.Height)) + if len(m.ExecID) > 0 { + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) + i-- + dAtA[i] = 0x12 + } + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *CloseIORequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1529,39 +2648,50 @@ func (m *CloseIORequest) Marshal() (dAtA []byte, err error) { } func (m *CloseIORequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CloseIORequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) - } - if len(m.ExecID) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Stdin { - dAtA[i] = 0x18 - i++ + i-- if m.Stdin { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x18 + } + if len(m.ExecID) > 0 { + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) + i-- + dAtA[i] = 0x12 + } + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *PauseTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1569,23 +2699,33 @@ func (m *PauseTaskRequest) Marshal() (dAtA []byte, err error) { } func (m *PauseTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PauseTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ResumeTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1593,23 +2733,33 @@ func (m *ResumeTaskRequest) Marshal() (dAtA []byte, err error) { } func (m *ResumeTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResumeTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListPidsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1617,23 +2767,33 @@ func (m *ListPidsRequest) Marshal() (dAtA []byte, err error) { } func (m *ListPidsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListPidsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListPidsResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1641,29 +2801,40 @@ func (m *ListPidsResponse) Marshal() (dAtA []byte, err error) { } func (m *ListPidsResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListPidsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Processes) > 0 { - for _, msg := range m.Processes { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Processes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Processes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *CheckpointTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1671,39 +2842,52 @@ func (m *CheckpointTaskRequest) Marshal() (dAtA []byte, err error) { } func (m *CheckpointTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CheckpointTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Options != nil { + { + size, err := m.Options.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } if len(m.ParentCheckpoint) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ParentCheckpoint) + copy(dAtA[i:], m.ParentCheckpoint) i = encodeVarintTasks(dAtA, i, uint64(len(m.ParentCheckpoint))) - i += copy(dAtA[i:], m.ParentCheckpoint) + i-- + dAtA[i] = 0x12 } - if m.Options != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintTasks(dAtA, i, uint64(m.Options.Size())) - n6, err := m.Options.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n6 + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *CheckpointTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1711,29 +2895,40 @@ func (m *CheckpointTaskResponse) Marshal() (dAtA []byte, err error) { } func (m *CheckpointTaskResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CheckpointTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Descriptors) > 0 { - for _, msg := range m.Descriptors { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Descriptors) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Descriptors[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *UpdateTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1741,33 +2936,64 @@ func (m *UpdateTaskRequest) Marshal() (dAtA []byte, err error) { } func (m *UpdateTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Annotations) > 0 { + for k := range m.Annotations { + v := m.Annotations[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintTasks(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintTasks(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintTasks(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } } if m.Resources != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintTasks(dAtA, i, uint64(m.Resources.Size())) - n7, err := m.Resources.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Resources.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) } - i += n7 + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *MetricsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1775,32 +3001,35 @@ func (m *MetricsRequest) Marshal() (dAtA []byte, err error) { } func (m *MetricsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MetricsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Filters) > 0 { - for _, s := range m.Filters { + for iNdEx := len(m.Filters) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Filters[iNdEx]) + copy(dAtA[i:], m.Filters[iNdEx]) + i = encodeVarintTasks(dAtA, i, uint64(len(m.Filters[iNdEx]))) + i-- dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - return i, nil + return len(dAtA) - i, nil } func (m *MetricsResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1808,29 +3037,40 @@ func (m *MetricsResponse) Marshal() (dAtA []byte, err error) { } func (m *MetricsResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MetricsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Metrics) > 0 { - for _, msg := range m.Metrics { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Metrics) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Metrics[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTasks(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *WaitRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1838,29 +3078,40 @@ func (m *WaitRequest) Marshal() (dAtA []byte, err error) { } func (m *WaitRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WaitRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ExecID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *WaitResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1868,36 +3119,50 @@ func (m *WaitResponse) Marshal() (dAtA []byte, err error) { } func (m *WaitResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WaitResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.ExitStatus != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTasks(dAtA, i, uint64(m.ExitStatus)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExitedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt):]) + if err8 != nil { + return 0, err8 } + i -= n8 + i = encodeVarintTasks(dAtA, i, uint64(n8)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintTasks(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) - n8, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) - if err != nil { - return 0, err + if m.ExitStatus != 0 { + i = encodeVarintTasks(dAtA, i, uint64(m.ExitStatus)) + i-- + dAtA[i] = 0x8 } - i += n8 - return i, nil + return len(dAtA) - i, nil } func encodeVarintTasks(dAtA []byte, offset int, v uint64) int { + offset -= sovTasks(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *CreateTaskRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -1933,10 +3198,16 @@ func (m *CreateTaskRequest) Size() (n int) { l = m.Options.Size() n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateTaskResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -1946,10 +3217,16 @@ func (m *CreateTaskResponse) Size() (n int) { if m.Pid != 0 { n += 1 + sovTasks(uint64(m.Pid)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StartRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -1960,29 +3237,47 @@ func (m *StartRequest) Size() (n int) { if l > 0 { n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StartResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Pid != 0 { n += 1 + sovTasks(uint64(m.Pid)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DeleteTaskRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) if l > 0 { n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DeleteResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1995,12 +3290,18 @@ func (m *DeleteResponse) Size() (n int) { if m.ExitStatus != 0 { n += 1 + sovTasks(uint64(m.ExitStatus)) } - l = types.SizeOfStdTime(m.ExitedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt) n += 1 + l + sovTasks(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DeleteProcessRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -2011,10 +3312,16 @@ func (m *DeleteProcessRequest) Size() (n int) { if l > 0 { n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *GetRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -2025,30 +3332,48 @@ func (m *GetRequest) Size() (n int) { if l > 0 { n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *GetResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Process != nil { l = m.Process.Size() n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListTasksRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Filter) if l > 0 { n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListTasksResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Tasks) > 0 { @@ -2057,10 +3382,16 @@ func (m *ListTasksResponse) Size() (n int) { n += 1 + l + sovTasks(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *KillRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -2077,10 +3408,16 @@ func (m *KillRequest) Size() (n int) { if m.All { n += 2 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ExecProcessRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -2110,16 +3447,28 @@ func (m *ExecProcessRequest) Size() (n int) { if l > 0 { n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ExecProcessResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ResizePtyRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -2136,10 +3485,16 @@ func (m *ResizePtyRequest) Size() (n int) { if m.Height != 0 { n += 1 + sovTasks(uint64(m.Height)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CloseIORequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -2153,40 +3508,64 @@ func (m *CloseIORequest) Size() (n int) { if m.Stdin { n += 2 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *PauseTaskRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) if l > 0 { n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ResumeTaskRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) if l > 0 { n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListPidsRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) if l > 0 { n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListPidsResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Processes) > 0 { @@ -2195,10 +3574,16 @@ func (m *ListPidsResponse) Size() (n int) { n += 1 + l + sovTasks(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CheckpointTaskRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -2213,10 +3598,16 @@ func (m *CheckpointTaskRequest) Size() (n int) { l = m.Options.Size() n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CheckpointTaskResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Descriptors) > 0 { @@ -2225,10 +3616,16 @@ func (m *CheckpointTaskResponse) Size() (n int) { n += 1 + l + sovTasks(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateTaskRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -2239,10 +3636,24 @@ func (m *UpdateTaskRequest) Size() (n int) { l = m.Resources.Size() n += 1 + l + sovTasks(uint64(l)) } + if len(m.Annotations) > 0 { + for k, v := range m.Annotations { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovTasks(uint64(len(k))) + 1 + len(v) + sovTasks(uint64(len(v))) + n += mapEntrySize + 1 + sovTasks(uint64(mapEntrySize)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *MetricsRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Filters) > 0 { @@ -2251,10 +3662,16 @@ func (m *MetricsRequest) Size() (n int) { n += 1 + l + sovTasks(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *MetricsResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Metrics) > 0 { @@ -2263,10 +3680,16 @@ func (m *MetricsResponse) Size() (n int) { n += 1 + l + sovTasks(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *WaitRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -2277,29 +3700,31 @@ func (m *WaitRequest) Size() (n int) { if l > 0 { n += 1 + l + sovTasks(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *WaitResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.ExitStatus != 0 { n += 1 + sovTasks(uint64(m.ExitStatus)) } - l = types.SizeOfStdTime(m.ExitedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt) n += 1 + l + sovTasks(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovTasks(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozTasks(x uint64) (n int) { return sovTasks(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -2308,15 +3733,21 @@ func (this *CreateTaskRequest) String() string { if this == nil { return "nil" } + repeatedStringForRootfs := "[]*Mount{" + for _, f := range this.Rootfs { + repeatedStringForRootfs += strings.Replace(fmt.Sprintf("%v", f), "Mount", "types.Mount", 1) + "," + } + repeatedStringForRootfs += "}" s := strings.Join([]string{`&CreateTaskRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, - `Rootfs:` + strings.Replace(fmt.Sprintf("%v", this.Rootfs), "Mount", "containerd_types.Mount", 1) + `,`, + `Rootfs:` + repeatedStringForRootfs + `,`, `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, - `Checkpoint:` + strings.Replace(fmt.Sprintf("%v", this.Checkpoint), "Descriptor", "containerd_types2.Descriptor", 1) + `,`, - `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "google_protobuf1.Any", 1) + `,`, + `Checkpoint:` + strings.Replace(fmt.Sprintf("%v", this.Checkpoint), "Descriptor", "types.Descriptor", 1) + `,`, + `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "types1.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2328,6 +3759,7 @@ func (this *CreateTaskResponse) String() string { s := strings.Join([]string{`&CreateTaskResponse{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2339,6 +3771,7 @@ func (this *StartRequest) String() string { s := strings.Join([]string{`&StartRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2349,6 +3782,7 @@ func (this *StartResponse) String() string { } s := strings.Join([]string{`&StartResponse{`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2359,6 +3793,7 @@ func (this *DeleteTaskRequest) String() string { } s := strings.Join([]string{`&DeleteTaskRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2371,7 +3806,8 @@ func (this *DeleteResponse) String() string { `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, - `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ExitedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2383,6 +3819,7 @@ func (this *DeleteProcessRequest) String() string { s := strings.Join([]string{`&DeleteProcessRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2394,6 +3831,7 @@ func (this *GetRequest) String() string { s := strings.Join([]string{`&GetRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2403,7 +3841,8 @@ func (this *GetResponse) String() string { return "nil" } s := strings.Join([]string{`&GetResponse{`, - `Process:` + strings.Replace(fmt.Sprintf("%v", this.Process), "Process", "containerd_v1_types.Process", 1) + `,`, + `Process:` + strings.Replace(fmt.Sprintf("%v", this.Process), "Process", "task.Process", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2414,6 +3853,7 @@ func (this *ListTasksRequest) String() string { } s := strings.Join([]string{`&ListTasksRequest{`, `Filter:` + fmt.Sprintf("%v", this.Filter) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2422,8 +3862,14 @@ func (this *ListTasksResponse) String() string { if this == nil { return "nil" } + repeatedStringForTasks := "[]*Process{" + for _, f := range this.Tasks { + repeatedStringForTasks += strings.Replace(fmt.Sprintf("%v", f), "Process", "task.Process", 1) + "," + } + repeatedStringForTasks += "}" s := strings.Join([]string{`&ListTasksResponse{`, - `Tasks:` + strings.Replace(fmt.Sprintf("%v", this.Tasks), "Process", "containerd_v1_types.Process", 1) + `,`, + `Tasks:` + repeatedStringForTasks + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2437,6 +3883,7 @@ func (this *KillRequest) String() string { `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, `Signal:` + fmt.Sprintf("%v", this.Signal) + `,`, `All:` + fmt.Sprintf("%v", this.All) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2451,8 +3898,9 @@ func (this *ExecProcessRequest) String() string { `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "Any", "google_protobuf1.Any", 1) + `,`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "Any", "types1.Any", 1) + `,`, `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2462,6 +3910,7 @@ func (this *ExecProcessResponse) String() string { return "nil" } s := strings.Join([]string{`&ExecProcessResponse{`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2475,6 +3924,7 @@ func (this *ResizePtyRequest) String() string { `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, `Width:` + fmt.Sprintf("%v", this.Width) + `,`, `Height:` + fmt.Sprintf("%v", this.Height) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2487,6 +3937,7 @@ func (this *CloseIORequest) String() string { `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2497,6 +3948,7 @@ func (this *PauseTaskRequest) String() string { } s := strings.Join([]string{`&PauseTaskRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2507,6 +3959,7 @@ func (this *ResumeTaskRequest) String() string { } s := strings.Join([]string{`&ResumeTaskRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2517,6 +3970,7 @@ func (this *ListPidsRequest) String() string { } s := strings.Join([]string{`&ListPidsRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2525,8 +3979,14 @@ func (this *ListPidsResponse) String() string { if this == nil { return "nil" } + repeatedStringForProcesses := "[]*ProcessInfo{" + for _, f := range this.Processes { + repeatedStringForProcesses += strings.Replace(fmt.Sprintf("%v", f), "ProcessInfo", "task.ProcessInfo", 1) + "," + } + repeatedStringForProcesses += "}" s := strings.Join([]string{`&ListPidsResponse{`, - `Processes:` + strings.Replace(fmt.Sprintf("%v", this.Processes), "ProcessInfo", "containerd_v1_types.ProcessInfo", 1) + `,`, + `Processes:` + repeatedStringForProcesses + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2538,7 +3998,8 @@ func (this *CheckpointTaskRequest) String() string { s := strings.Join([]string{`&CheckpointTaskRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `ParentCheckpoint:` + fmt.Sprintf("%v", this.ParentCheckpoint) + `,`, - `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "google_protobuf1.Any", 1) + `,`, + `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "types1.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2547,8 +4008,14 @@ func (this *CheckpointTaskResponse) String() string { if this == nil { return "nil" } + repeatedStringForDescriptors := "[]*Descriptor{" + for _, f := range this.Descriptors { + repeatedStringForDescriptors += strings.Replace(fmt.Sprintf("%v", f), "Descriptor", "types.Descriptor", 1) + "," + } + repeatedStringForDescriptors += "}" s := strings.Join([]string{`&CheckpointTaskResponse{`, - `Descriptors:` + strings.Replace(fmt.Sprintf("%v", this.Descriptors), "Descriptor", "containerd_types2.Descriptor", 1) + `,`, + `Descriptors:` + repeatedStringForDescriptors + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2557,9 +4024,21 @@ func (this *UpdateTaskRequest) String() string { if this == nil { return "nil" } + keysForAnnotations := make([]string, 0, len(this.Annotations)) + for k, _ := range this.Annotations { + keysForAnnotations = append(keysForAnnotations, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForAnnotations) + mapStringForAnnotations := "map[string]string{" + for _, k := range keysForAnnotations { + mapStringForAnnotations += fmt.Sprintf("%v: %v,", k, this.Annotations[k]) + } + mapStringForAnnotations += "}" s := strings.Join([]string{`&UpdateTaskRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, - `Resources:` + strings.Replace(fmt.Sprintf("%v", this.Resources), "Any", "google_protobuf1.Any", 1) + `,`, + `Resources:` + strings.Replace(fmt.Sprintf("%v", this.Resources), "Any", "types1.Any", 1) + `,`, + `Annotations:` + mapStringForAnnotations + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2570,6 +4049,7 @@ func (this *MetricsRequest) String() string { } s := strings.Join([]string{`&MetricsRequest{`, `Filters:` + fmt.Sprintf("%v", this.Filters) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2578,8 +4058,14 @@ func (this *MetricsResponse) String() string { if this == nil { return "nil" } + repeatedStringForMetrics := "[]*Metric{" + for _, f := range this.Metrics { + repeatedStringForMetrics += strings.Replace(fmt.Sprintf("%v", f), "Metric", "types.Metric", 1) + "," + } + repeatedStringForMetrics += "}" s := strings.Join([]string{`&MetricsResponse{`, - `Metrics:` + strings.Replace(fmt.Sprintf("%v", this.Metrics), "Metric", "containerd_types1.Metric", 1) + `,`, + `Metrics:` + repeatedStringForMetrics + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2591,6 +4077,7 @@ func (this *WaitRequest) String() string { s := strings.Join([]string{`&WaitRequest{`, `ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`, `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2601,7 +4088,8 @@ func (this *WaitResponse) String() string { } s := strings.Join([]string{`&WaitResponse{`, `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, - `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ExitedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2629,7 +4117,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2657,7 +4145,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2667,6 +4155,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2686,7 +4177,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2695,10 +4186,13 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Rootfs = append(m.Rootfs, &containerd_types.Mount{}) + m.Rootfs = append(m.Rootfs, &types.Mount{}) if err := m.Rootfs[len(m.Rootfs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -2717,7 +4211,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2727,6 +4221,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2746,7 +4243,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2756,6 +4253,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2775,7 +4275,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2785,6 +4285,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2804,7 +4307,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2824,7 +4327,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2833,11 +4336,14 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Checkpoint == nil { - m.Checkpoint = &containerd_types2.Descriptor{} + m.Checkpoint = &types.Descriptor{} } if err := m.Checkpoint.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -2857,7 +4363,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2866,11 +4372,14 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Options == nil { - m.Options = &google_protobuf1.Any{} + m.Options = &types1.Any{} } if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -2882,12 +4391,13 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2912,7 +4422,7 @@ func (m *CreateTaskResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2940,7 +4450,7 @@ func (m *CreateTaskResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2950,6 +4460,9 @@ func (m *CreateTaskResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2969,7 +4482,7 @@ func (m *CreateTaskResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -2980,12 +4493,13 @@ func (m *CreateTaskResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3010,7 +4524,7 @@ func (m *StartRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3038,7 +4552,7 @@ func (m *StartRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3048,6 +4562,9 @@ func (m *StartRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3067,7 +4584,7 @@ func (m *StartRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3077,6 +4594,9 @@ func (m *StartRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3088,12 +4608,13 @@ func (m *StartRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3118,7 +4639,7 @@ func (m *StartResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3146,7 +4667,7 @@ func (m *StartResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -3157,12 +4678,13 @@ func (m *StartResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3187,7 +4709,7 @@ func (m *DeleteTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3215,7 +4737,7 @@ func (m *DeleteTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3225,6 +4747,9 @@ func (m *DeleteTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3236,12 +4761,13 @@ func (m *DeleteTaskRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3266,7 +4792,7 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3294,7 +4820,7 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3304,6 +4830,9 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3323,7 +4852,7 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -3342,7 +4871,7 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ExitStatus |= (uint32(b) & 0x7F) << shift + m.ExitStatus |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -3361,7 +4890,7 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3370,10 +4899,13 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -3383,12 +4915,13 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3413,7 +4946,7 @@ func (m *DeleteProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3441,7 +4974,7 @@ func (m *DeleteProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3451,6 +4984,9 @@ func (m *DeleteProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3470,7 +5006,7 @@ func (m *DeleteProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3480,6 +5016,9 @@ func (m *DeleteProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3491,12 +5030,13 @@ func (m *DeleteProcessRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3521,7 +5061,7 @@ func (m *GetRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3549,7 +5089,7 @@ func (m *GetRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3559,6 +5099,9 @@ func (m *GetRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3578,7 +5121,7 @@ func (m *GetRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3588,6 +5131,9 @@ func (m *GetRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3599,12 +5145,13 @@ func (m *GetRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3629,7 +5176,7 @@ func (m *GetResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3657,7 +5204,7 @@ func (m *GetResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3666,11 +5213,14 @@ func (m *GetResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Process == nil { - m.Process = &containerd_v1_types.Process{} + m.Process = &task.Process{} } if err := m.Process.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -3682,12 +5232,13 @@ func (m *GetResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3712,7 +5263,7 @@ func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3740,7 +5291,7 @@ func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3750,6 +5301,9 @@ func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3761,12 +5315,13 @@ func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3791,7 +5346,7 @@ func (m *ListTasksResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3819,7 +5374,7 @@ func (m *ListTasksResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3828,10 +5383,13 @@ func (m *ListTasksResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Tasks = append(m.Tasks, &containerd_v1_types.Process{}) + m.Tasks = append(m.Tasks, &task.Process{}) if err := m.Tasks[len(m.Tasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -3842,12 +5400,13 @@ func (m *ListTasksResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3872,7 +5431,7 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3900,7 +5459,7 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3910,6 +5469,9 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3929,7 +5491,7 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3939,6 +5501,9 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3958,7 +5523,7 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Signal |= (uint32(b) & 0x7F) << shift + m.Signal |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -3977,7 +5542,7 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3989,12 +5554,13 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4019,7 +5585,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4047,7 +5613,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4057,6 +5623,9 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4076,7 +5645,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4086,6 +5655,9 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4105,7 +5677,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4115,6 +5687,9 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4134,7 +5709,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4144,6 +5719,9 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4163,7 +5741,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4183,7 +5761,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4192,11 +5770,14 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Spec == nil { - m.Spec = &google_protobuf1.Any{} + m.Spec = &types1.Any{} } if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -4216,7 +5797,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4226,6 +5807,9 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4237,12 +5821,13 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4267,7 +5852,7 @@ func (m *ExecProcessResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4287,12 +5872,13 @@ func (m *ExecProcessResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4317,7 +5903,7 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4345,7 +5931,7 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4355,6 +5941,9 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4374,7 +5963,7 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4384,6 +5973,9 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4403,7 +5995,7 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Width |= (uint32(b) & 0x7F) << shift + m.Width |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -4422,7 +6014,7 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Height |= (uint32(b) & 0x7F) << shift + m.Height |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -4433,12 +6025,13 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4463,7 +6056,7 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4491,7 +6084,7 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4501,6 +6094,9 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4520,7 +6116,7 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4530,6 +6126,9 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4549,7 +6148,7 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4561,12 +6160,13 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4591,7 +6191,7 @@ func (m *PauseTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4619,7 +6219,7 @@ func (m *PauseTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4629,6 +6229,9 @@ func (m *PauseTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4640,12 +6243,13 @@ func (m *PauseTaskRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4670,7 +6274,7 @@ func (m *ResumeTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4698,7 +6302,7 @@ func (m *ResumeTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4708,6 +6312,9 @@ func (m *ResumeTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4719,12 +6326,13 @@ func (m *ResumeTaskRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4749,7 +6357,7 @@ func (m *ListPidsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4777,7 +6385,7 @@ func (m *ListPidsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4787,6 +6395,9 @@ func (m *ListPidsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4798,12 +6409,13 @@ func (m *ListPidsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4828,7 +6440,7 @@ func (m *ListPidsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4856,7 +6468,7 @@ func (m *ListPidsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4865,10 +6477,13 @@ func (m *ListPidsResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Processes = append(m.Processes, &containerd_v1_types.ProcessInfo{}) + m.Processes = append(m.Processes, &task.ProcessInfo{}) if err := m.Processes[len(m.Processes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -4879,12 +6494,13 @@ func (m *ListPidsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4909,7 +6525,7 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4937,7 +6553,7 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4947,6 +6563,9 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4966,7 +6585,7 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4976,6 +6595,9 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4995,7 +6617,7 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5004,11 +6626,14 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Options == nil { - m.Options = &google_protobuf1.Any{} + m.Options = &types1.Any{} } if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -5020,12 +6645,13 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -5050,7 +6676,7 @@ func (m *CheckpointTaskResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5078,7 +6704,7 @@ func (m *CheckpointTaskResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5087,10 +6713,13 @@ func (m *CheckpointTaskResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Descriptors = append(m.Descriptors, &containerd_types2.Descriptor{}) + m.Descriptors = append(m.Descriptors, &types.Descriptor{}) if err := m.Descriptors[len(m.Descriptors)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -5101,12 +6730,13 @@ func (m *CheckpointTaskResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -5131,7 +6761,7 @@ func (m *UpdateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5159,7 +6789,7 @@ func (m *UpdateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5169,6 +6799,9 @@ func (m *UpdateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5188,7 +6821,7 @@ func (m *UpdateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5197,28 +6830,159 @@ func (m *UpdateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Resources == nil { - m.Resources = &google_protobuf1.Any{} + m.Resources = &types1.Any{} } if err := m.Resources.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTasks + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTasks + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Annotations == nil { + m.Annotations = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTasks + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTasks + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthTasks + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthTasks + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTasks + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthTasks + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthTasks + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipTasks(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTasks + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Annotations[mapkey] = mapvalue + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTasks(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -5243,7 +7007,7 @@ func (m *MetricsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5271,7 +7035,7 @@ func (m *MetricsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5281,6 +7045,9 @@ func (m *MetricsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5292,12 +7059,13 @@ func (m *MetricsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -5322,7 +7090,7 @@ func (m *MetricsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5350,7 +7118,7 @@ func (m *MetricsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5359,10 +7127,13 @@ func (m *MetricsResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Metrics = append(m.Metrics, &containerd_types1.Metric{}) + m.Metrics = append(m.Metrics, &types.Metric{}) if err := m.Metrics[len(m.Metrics)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -5373,12 +7144,13 @@ func (m *MetricsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -5403,7 +7175,7 @@ func (m *WaitRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5431,7 +7203,7 @@ func (m *WaitRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5441,6 +7213,9 @@ func (m *WaitRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5460,7 +7235,7 @@ func (m *WaitRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5470,6 +7245,9 @@ func (m *WaitRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5481,12 +7259,13 @@ func (m *WaitRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -5511,7 +7290,7 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5539,7 +7318,7 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ExitStatus |= (uint32(b) & 0x7F) << shift + m.ExitStatus |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -5558,7 +7337,7 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5567,10 +7346,13 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTasks } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTasks + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -5580,12 +7362,13 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTasks } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -5598,6 +7381,7 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { func skipTasks(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -5629,10 +7413,8 @@ func skipTasks(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -5649,144 +7431,34 @@ func skipTasks(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthTasks } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTasks - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipTasks(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTasks + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthTasks + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthTasks = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowTasks = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthTasks = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTasks = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTasks = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/tasks/v1/tasks.proto", fileDescriptorTasks) -} - -var fileDescriptorTasks = []byte{ - // 1318 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x4b, 0x6f, 0x1b, 0x45, - 0x1c, 0xef, 0xfa, 0xed, 0xbf, 0x93, 0x36, 0x59, 0xd2, 0x60, 0x96, 0x2a, 0x0e, 0xcb, 0xc5, 0x04, - 0xba, 0x4b, 0x5d, 0x54, 0x21, 0x5a, 0x21, 0x35, 0x0f, 0x22, 0x0b, 0xaa, 0xa6, 0xdb, 0x02, 0x55, - 0x25, 0x14, 0xb6, 0xbb, 0x13, 0x67, 0x14, 0x7b, 0x67, 0xbb, 0x33, 0x4e, 0x1b, 0x38, 0xc0, 0x47, - 0xe8, 0x95, 0x0b, 0x9f, 0x27, 0x47, 0x8e, 0x08, 0x55, 0x81, 0xfa, 0x5b, 0x70, 0x43, 0xf3, 0xd8, - 0xcd, 0xc6, 0x8e, 0xbd, 0x4e, 0xd3, 0x70, 0x69, 0x67, 0x66, 0xff, 0xaf, 0xf9, 0xcd, 0xff, 0xf1, - 0x73, 0x60, 0xb5, 0x83, 0xd9, 0x6e, 0xff, 0xa9, 0xe5, 0x91, 0x9e, 0xed, 0x91, 0x80, 0xb9, 0x38, - 0x40, 0x91, 0x9f, 0x5e, 0xba, 0x21, 0xb6, 0x29, 0x8a, 0xf6, 0xb1, 0x87, 0xa8, 0xcd, 0x5c, 0xba, - 0x47, 0xed, 0xfd, 0x1b, 0x72, 0x61, 0x85, 0x11, 0x61, 0x44, 0xbf, 0x76, 0x2c, 0x6d, 0xc5, 0x92, - 0x96, 0x14, 0xd8, 0xbf, 0x61, 0xbc, 0xdf, 0x21, 0xa4, 0xd3, 0x45, 0xb6, 0x90, 0x7d, 0xda, 0xdf, - 0xb1, 0x51, 0x2f, 0x64, 0x07, 0x52, 0xd5, 0x78, 0x6f, 0xf8, 0xa3, 0x1b, 0xc4, 0x9f, 0x16, 0x3a, - 0xa4, 0x43, 0xc4, 0xd2, 0xe6, 0x2b, 0x75, 0x7a, 0x6b, 0xaa, 0x78, 0xd9, 0x41, 0x88, 0xa8, 0xdd, - 0x23, 0xfd, 0x80, 0x29, 0xbd, 0xcf, 0xcf, 0xa2, 0x87, 0x58, 0x84, 0x3d, 0x75, 0x3b, 0xe3, 0xf6, - 0x19, 0x34, 0x7d, 0x44, 0xbd, 0x08, 0x87, 0x8c, 0x44, 0x4a, 0xf9, 0x8b, 0x33, 0x28, 0x73, 0xc4, - 0xc4, 0x3f, 0x4a, 0xb7, 0x31, 0x8c, 0x0d, 0xc3, 0x3d, 0x44, 0x99, 0xdb, 0x0b, 0xa5, 0x80, 0x79, - 0x98, 0x83, 0xf9, 0xb5, 0x08, 0xb9, 0x0c, 0x3d, 0x72, 0xe9, 0x9e, 0x83, 0x9e, 0xf5, 0x11, 0x65, - 0x7a, 0x0b, 0x66, 0x12, 0xf3, 0xdb, 0xd8, 0xaf, 0x6b, 0xcb, 0x5a, 0xb3, 0xba, 0x7a, 0x65, 0x70, - 0xd4, 0xa8, 0xad, 0xc5, 0xe7, 0xed, 0x75, 0xa7, 0x96, 0x08, 0xb5, 0x7d, 0xdd, 0x86, 0x52, 0x44, - 0x08, 0xdb, 0xa1, 0xf5, 0xfc, 0x72, 0xbe, 0x59, 0x6b, 0xbd, 0x6b, 0xa5, 0x9e, 0x54, 0x44, 0x67, - 0xdd, 0xe3, 0x60, 0x3a, 0x4a, 0x4c, 0x5f, 0x80, 0x22, 0x65, 0x3e, 0x0e, 0xea, 0x05, 0x6e, 0xdd, - 0x91, 0x1b, 0x7d, 0x11, 0x4a, 0x94, 0xf9, 0xa4, 0xcf, 0xea, 0x45, 0x71, 0xac, 0x76, 0xea, 0x1c, - 0x45, 0x51, 0xbd, 0x94, 0x9c, 0xa3, 0x28, 0xd2, 0x0d, 0xa8, 0x30, 0x14, 0xf5, 0x70, 0xe0, 0x76, - 0xeb, 0xe5, 0x65, 0xad, 0x59, 0x71, 0x92, 0xbd, 0x7e, 0x07, 0xc0, 0xdb, 0x45, 0xde, 0x5e, 0x48, - 0x70, 0xc0, 0xea, 0x95, 0x65, 0xad, 0x59, 0x6b, 0x5d, 0x1b, 0x0d, 0x6b, 0x3d, 0x41, 0xdc, 0x49, - 0xc9, 0xeb, 0x16, 0x94, 0x49, 0xc8, 0x30, 0x09, 0x68, 0xbd, 0x2a, 0x54, 0x17, 0x2c, 0x89, 0xa6, - 0x15, 0xa3, 0x69, 0xdd, 0x0d, 0x0e, 0x9c, 0x58, 0xc8, 0x7c, 0x02, 0x7a, 0x1a, 0x49, 0x1a, 0x92, - 0x80, 0xa2, 0x37, 0x82, 0x72, 0x0e, 0xf2, 0x21, 0xf6, 0xeb, 0xb9, 0x65, 0xad, 0x39, 0xeb, 0xf0, - 0xa5, 0xd9, 0x81, 0x99, 0x87, 0xcc, 0x8d, 0xd8, 0x79, 0x1e, 0xe8, 0x43, 0x28, 0xa3, 0x17, 0xc8, - 0xdb, 0x56, 0x96, 0xab, 0xab, 0x30, 0x38, 0x6a, 0x94, 0x36, 0x5e, 0x20, 0xaf, 0xbd, 0xee, 0x94, - 0xf8, 0xa7, 0xb6, 0x6f, 0x7e, 0x00, 0xb3, 0xca, 0x91, 0x8a, 0x5f, 0xc5, 0xa2, 0x1d, 0xc7, 0xb2, - 0x09, 0xf3, 0xeb, 0xa8, 0x8b, 0xce, 0x9d, 0x31, 0xe6, 0xef, 0x1a, 0x5c, 0x96, 0x96, 0x12, 0x6f, - 0x8b, 0x90, 0x4b, 0x94, 0x4b, 0x83, 0xa3, 0x46, 0xae, 0xbd, 0xee, 0xe4, 0xf0, 0x29, 0x88, 0xe8, - 0x0d, 0xa8, 0xa1, 0x17, 0x98, 0x6d, 0x53, 0xe6, 0xb2, 0x3e, 0xcf, 0x39, 0xfe, 0x05, 0xf8, 0xd1, - 0x43, 0x71, 0xa2, 0xdf, 0x85, 0x2a, 0xdf, 0x21, 0x7f, 0xdb, 0x65, 0x22, 0xc5, 0x6a, 0x2d, 0x63, - 0xe4, 0x01, 0x1f, 0xc5, 0xe5, 0xb0, 0x5a, 0x39, 0x3c, 0x6a, 0x5c, 0x7a, 0xf9, 0x77, 0x43, 0x73, - 0x2a, 0x52, 0xed, 0x2e, 0x33, 0x09, 0x2c, 0xc8, 0xf8, 0xb6, 0x22, 0xe2, 0x21, 0x4a, 0x2f, 0x1c, - 0x7d, 0x04, 0xb0, 0x89, 0x2e, 0xfe, 0x91, 0x37, 0xa0, 0x26, 0xdc, 0x28, 0xd0, 0x6f, 0x41, 0x39, - 0x94, 0x17, 0x14, 0x2e, 0x86, 0x6a, 0x64, 0xff, 0x86, 0x2a, 0x93, 0x18, 0x84, 0x58, 0xd8, 0x5c, - 0x81, 0xb9, 0x6f, 0x30, 0x65, 0x3c, 0x0d, 0x12, 0x68, 0x16, 0xa1, 0xb4, 0x83, 0xbb, 0x0c, 0x45, - 0x32, 0x5a, 0x47, 0xed, 0x78, 0xd2, 0xa4, 0x64, 0x93, 0xda, 0x28, 0x8a, 0x16, 0x5f, 0xd7, 0x44, - 0xc7, 0x98, 0xec, 0x56, 0x8a, 0x9a, 0x2f, 0x35, 0xa8, 0x7d, 0x8d, 0xbb, 0xdd, 0x8b, 0x06, 0x49, - 0x34, 0x1c, 0xdc, 0xe1, 0x6d, 0x45, 0xe6, 0x96, 0xda, 0xf1, 0x54, 0x74, 0xbb, 0x5d, 0x91, 0x51, - 0x15, 0x87, 0x2f, 0xcd, 0x7f, 0x35, 0xd0, 0xb9, 0xf2, 0x5b, 0xc8, 0x92, 0xa4, 0x27, 0xe6, 0x4e, - 0xef, 0x89, 0xf9, 0x31, 0x3d, 0xb1, 0x30, 0xb6, 0x27, 0x16, 0x87, 0x7a, 0x62, 0x13, 0x0a, 0x34, - 0x44, 0x9e, 0xe8, 0xa2, 0xe3, 0x5a, 0x9a, 0x90, 0x48, 0xa3, 0x54, 0x1e, 0x9b, 0x4a, 0x57, 0xe1, - 0x9d, 0x13, 0x57, 0x97, 0x2f, 0x6b, 0xfe, 0xa6, 0xc1, 0x9c, 0x83, 0x28, 0xfe, 0x09, 0x6d, 0xb1, - 0x83, 0x0b, 0x7f, 0xaa, 0x05, 0x28, 0x3e, 0xc7, 0x3e, 0xdb, 0x55, 0x2f, 0x25, 0x37, 0x1c, 0x9d, - 0x5d, 0x84, 0x3b, 0xbb, 0xb2, 0xfa, 0x67, 0x1d, 0xb5, 0x33, 0x7f, 0x81, 0xcb, 0x6b, 0x5d, 0x42, - 0x51, 0xfb, 0xfe, 0xff, 0x11, 0x98, 0x7c, 0xce, 0xbc, 0x78, 0x05, 0xb9, 0x31, 0xbf, 0x82, 0xb9, - 0x2d, 0xb7, 0x4f, 0xcf, 0xdd, 0x3f, 0x37, 0x61, 0xde, 0x41, 0xb4, 0xdf, 0x3b, 0xb7, 0xa1, 0x0d, - 0xb8, 0xc2, 0x8b, 0x73, 0x0b, 0xfb, 0xe7, 0x49, 0x5e, 0xd3, 0x91, 0xfd, 0x40, 0x9a, 0x51, 0x25, - 0xfe, 0x25, 0x54, 0x55, 0xbb, 0x40, 0x71, 0x99, 0x2f, 0x4f, 0x2a, 0xf3, 0x76, 0xb0, 0x43, 0x9c, - 0x63, 0x15, 0xf3, 0x95, 0x06, 0x57, 0xd7, 0x92, 0x99, 0x7c, 0x5e, 0x8e, 0xb2, 0x0d, 0xf3, 0xa1, - 0x1b, 0xa1, 0x80, 0x6d, 0xa7, 0x78, 0x81, 0x7c, 0xbe, 0x16, 0xef, 0xff, 0x7f, 0x1d, 0x35, 0x56, - 0x52, 0x6c, 0x8b, 0x84, 0x28, 0x48, 0xd4, 0xa9, 0xdd, 0x21, 0xd7, 0x7d, 0xdc, 0x41, 0x94, 0x59, - 0xeb, 0xe2, 0x3f, 0x67, 0x4e, 0x1a, 0x5b, 0x3b, 0x95, 0x33, 0xe4, 0xa7, 0xe1, 0x0c, 0x8f, 0x61, - 0x71, 0xf8, 0x76, 0x09, 0x70, 0xb5, 0x63, 0x26, 0x78, 0x6a, 0x87, 0x1c, 0x21, 0x2f, 0x69, 0x05, - 0xf3, 0x67, 0x98, 0xff, 0x36, 0xf4, 0xdf, 0x02, 0xaf, 0x6b, 0x41, 0x35, 0x42, 0x94, 0xf4, 0x23, - 0x0f, 0x51, 0x81, 0xd5, 0xb8, 0x4b, 0x1d, 0x8b, 0x99, 0x2b, 0x70, 0xf9, 0x9e, 0x24, 0xc0, 0xb1, - 0xe7, 0x3a, 0x94, 0xe5, 0x24, 0x90, 0x57, 0xa9, 0x3a, 0xf1, 0x96, 0x27, 0x5f, 0x22, 0x9b, 0xcc, - 0x85, 0xb2, 0xe2, 0xcf, 0xea, 0xde, 0xf5, 0x53, 0xb8, 0xa4, 0x10, 0x70, 0x62, 0x41, 0x73, 0x07, - 0x6a, 0xdf, 0xbb, 0xf8, 0xe2, 0x67, 0x67, 0x04, 0x33, 0xd2, 0x8f, 0x8a, 0x75, 0x88, 0x87, 0x68, - 0x93, 0x79, 0x48, 0xee, 0x4d, 0x78, 0x48, 0xeb, 0xd5, 0x0c, 0x14, 0xc5, 0xe4, 0xd4, 0xf7, 0xa0, - 0x24, 0x39, 0xa6, 0x6e, 0x5b, 0x93, 0x7e, 0x31, 0x59, 0x23, 0x9c, 0xde, 0xf8, 0x74, 0x7a, 0x05, - 0x75, 0xb5, 0x1f, 0xa1, 0x28, 0xb8, 0xa0, 0xbe, 0x32, 0x59, 0x35, 0xcd, 0x4c, 0x8d, 0x8f, 0xa7, - 0x92, 0x55, 0x1e, 0x3a, 0x50, 0x92, 0x04, 0x2b, 0xeb, 0x3a, 0x23, 0x84, 0xd3, 0xf8, 0x64, 0x1a, - 0x85, 0xc4, 0xd1, 0x33, 0x98, 0x3d, 0xc1, 0xe4, 0xf4, 0xd6, 0x34, 0xea, 0x27, 0x07, 0xfa, 0x19, - 0x5d, 0x3e, 0x81, 0xfc, 0x26, 0x62, 0x7a, 0x73, 0xb2, 0xd2, 0x31, 0xdd, 0x33, 0x3e, 0x9a, 0x42, - 0x32, 0xc1, 0xad, 0xc0, 0x3b, 0xad, 0x6e, 0x4d, 0x56, 0x19, 0x66, 0x67, 0x86, 0x3d, 0xb5, 0xbc, - 0x72, 0xd4, 0x86, 0x02, 0x27, 0x5b, 0x7a, 0x46, 0x6c, 0x29, 0x42, 0x66, 0x2c, 0x8e, 0x24, 0xf7, - 0x06, 0xff, 0xb1, 0xae, 0x6f, 0x41, 0x81, 0x97, 0x92, 0x9e, 0x91, 0x87, 0xa3, 0x44, 0x6a, 0xac, - 0xc5, 0x87, 0x50, 0x4d, 0x38, 0x46, 0x16, 0x14, 0xc3, 0x64, 0x64, 0xac, 0xd1, 0xfb, 0x50, 0x56, - 0xec, 0x40, 0xcf, 0x78, 0xef, 0x93, 0x24, 0x62, 0x82, 0xc1, 0xa2, 0x98, 0xf6, 0x59, 0x11, 0x0e, - 0x53, 0x82, 0xb1, 0x06, 0x1f, 0x40, 0x49, 0x8e, 0xfd, 0xac, 0xa2, 0x19, 0x21, 0x07, 0x63, 0x4d, - 0x62, 0xa8, 0xc4, 0x93, 0x5b, 0xbf, 0x9e, 0x9d, 0x23, 0x29, 0xa2, 0x60, 0x58, 0xd3, 0x8a, 0xab, - 0x8c, 0x7a, 0x0e, 0x90, 0x9a, 0x97, 0x37, 0x33, 0x20, 0x3e, 0x6d, 0xf2, 0x1b, 0x9f, 0x9d, 0x4d, - 0x49, 0x39, 0x7e, 0x00, 0x25, 0x39, 0x10, 0xb3, 0x60, 0x1b, 0x19, 0x9b, 0x63, 0x61, 0xdb, 0x81, - 0xb2, 0x1a, 0x5d, 0x59, 0xb9, 0x72, 0x72, 0x1a, 0x1a, 0xd7, 0xa7, 0x94, 0x56, 0xa1, 0xff, 0x00, - 0x05, 0x3e, 0x73, 0xb2, 0xaa, 0x30, 0x35, 0xff, 0x8c, 0x95, 0x69, 0x44, 0xa5, 0xf9, 0xd5, 0xef, - 0x0e, 0x5f, 0x2f, 0x5d, 0xfa, 0xf3, 0xf5, 0xd2, 0xa5, 0x5f, 0x07, 0x4b, 0xda, 0xe1, 0x60, 0x49, - 0xfb, 0x63, 0xb0, 0xa4, 0xfd, 0x33, 0x58, 0xd2, 0x9e, 0xdc, 0x79, 0xb3, 0xbf, 0xec, 0xdd, 0x16, - 0x8b, 0xc7, 0xb9, 0xa7, 0x25, 0x01, 0xd8, 0xcd, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x85, 0xa2, - 0x4f, 0xd1, 0x22, 0x14, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.proto b/vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.proto index 90793cbabad7b..2fe72c64f8241 100644 --- a/vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.proto +++ b/vendor/github.com/containerd/containerd/api/services/tasks/v1/tasks.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.tasks.v1; @@ -188,6 +204,7 @@ message CheckpointTaskResponse { message UpdateTaskRequest { string container_id = 1; google.protobuf.Any resources = 2; + map annotations = 3; } message MetricsRequest { diff --git a/vendor/github.com/containerd/containerd/api/services/version/v1/version.pb.go b/vendor/github.com/containerd/containerd/api/services/version/v1/version.pb.go index 829987c416c00..b742c6ae62f57 100644 --- a/vendor/github.com/containerd/containerd/api/services/version/v1/version.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/version/v1/version.pb.go @@ -1,31 +1,22 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/services/version/v1/version.proto -/* - Package version is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/services/version/v1/version.proto - - It has these top-level messages: - VersionResponse -*/ package version -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/gogo/protobuf/types" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + context "context" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + types "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -36,21 +27,76 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type VersionResponse struct { - Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` - Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"` + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *VersionResponse) Reset() { *m = VersionResponse{} } -func (*VersionResponse) ProtoMessage() {} -func (*VersionResponse) Descriptor() ([]byte, []int) { return fileDescriptorVersion, []int{0} } +func (m *VersionResponse) Reset() { *m = VersionResponse{} } +func (*VersionResponse) ProtoMessage() {} +func (*VersionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_128109001e578ffe, []int{0} +} +func (m *VersionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VersionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_VersionResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *VersionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_VersionResponse.Merge(m, src) +} +func (m *VersionResponse) XXX_Size() int { + return m.Size() +} +func (m *VersionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_VersionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_VersionResponse proto.InternalMessageInfo func init() { proto.RegisterType((*VersionResponse)(nil), "containerd.services.version.v1.VersionResponse") } +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/services/version/v1/version.proto", fileDescriptor_128109001e578ffe) +} + +var fileDescriptor_128109001e578ffe = []byte{ + // 243 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x4b, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x17, 0xa7, 0x16, 0x95, 0x65, 0x26, 0xa7, 0x16, 0xeb, + 0x97, 0xa5, 0x16, 0x15, 0x67, 0xe6, 0xe7, 0xe9, 0x97, 0x19, 0xc2, 0x98, 0x7a, 0x05, 0x45, 0xf9, + 0x25, 0xf9, 0x42, 0x72, 0x08, 0x1d, 0x7a, 0x30, 0xd5, 0x7a, 0x30, 0x25, 0x65, 0x86, 0x52, 0xd2, + 0xe9, 0xf9, 0xf9, 0xe9, 0x39, 0xa9, 0xfa, 0x60, 0xd5, 0x49, 0xa5, 0x69, 0xfa, 0xa9, 0xb9, 0x05, + 0x25, 0x95, 0x10, 0xcd, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, 0x11, + 0x55, 0x72, 0xe7, 0xe2, 0x0f, 0x83, 0x18, 0x10, 0x94, 0x5a, 0x5c, 0x90, 0x9f, 0x57, 0x9c, 0x2a, + 0x24, 0xc1, 0xc5, 0x0e, 0x35, 0x53, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, 0x15, 0x92, + 0xe2, 0xe2, 0x28, 0x4a, 0x2d, 0xcb, 0x04, 0x4b, 0x31, 0x81, 0xa5, 0xe0, 0x7c, 0xa3, 0x58, 0x2e, + 0x76, 0xa8, 0x41, 0x42, 0x41, 0x08, 0xa6, 0x98, 0x1e, 0xc4, 0x49, 0x7a, 0x30, 0x27, 0xe9, 0xb9, + 0x82, 0x9c, 0x24, 0xa5, 0xaf, 0x87, 0xdf, 0x2b, 0x7a, 0x68, 0x8e, 0x72, 0x8a, 0x3a, 0xf1, 0x50, + 0x8e, 0xe1, 0xc6, 0x43, 0x39, 0x86, 0x86, 0x47, 0x72, 0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, + 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0x63, 0x94, 0x03, 0xb9, 0x81, 0x6b, 0x0d, 0x65, 0x46, 0x30, + 0x26, 0xb1, 0x81, 0x9d, 0x67, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x95, 0x0d, 0x52, 0x23, 0xa9, + 0x01, 0x00, 0x00, +} + // Reference imports to suppress errors if they are not otherwise used. var _ context.Context var _ grpc.ClientConn @@ -59,10 +105,11 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Version service - +// VersionClient is the client API for Version service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type VersionClient interface { - Version(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*VersionResponse, error) + Version(ctx context.Context, in *types.Empty, opts ...grpc.CallOption) (*VersionResponse, error) } type versionClient struct { @@ -73,19 +120,26 @@ func NewVersionClient(cc *grpc.ClientConn) VersionClient { return &versionClient{cc} } -func (c *versionClient) Version(ctx context.Context, in *google_protobuf.Empty, opts ...grpc.CallOption) (*VersionResponse, error) { +func (c *versionClient) Version(ctx context.Context, in *types.Empty, opts ...grpc.CallOption) (*VersionResponse, error) { out := new(VersionResponse) - err := grpc.Invoke(ctx, "/containerd.services.version.v1.Version/Version", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/containerd.services.version.v1.Version/Version", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Version service - +// VersionServer is the server API for Version service. type VersionServer interface { - Version(context.Context, *google_protobuf.Empty) (*VersionResponse, error) + Version(context.Context, *types.Empty) (*VersionResponse, error) +} + +// UnimplementedVersionServer can be embedded to have forward compatible implementations. +type UnimplementedVersionServer struct { +} + +func (*UnimplementedVersionServer) Version(ctx context.Context, req *types.Empty) (*VersionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") } func RegisterVersionServer(s *grpc.Server, srv VersionServer) { @@ -93,7 +147,7 @@ func RegisterVersionServer(s *grpc.Server, srv VersionServer) { } func _Version_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(google_protobuf.Empty) + in := new(types.Empty) if err := dec(in); err != nil { return nil, err } @@ -105,7 +159,7 @@ func _Version_Version_Handler(srv interface{}, ctx context.Context, dec func(int FullMethod: "/containerd.services.version.v1.Version/Version", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(VersionServer).Version(ctx, req.(*google_protobuf.Empty)) + return srv.(VersionServer).Version(ctx, req.(*types.Empty)) } return interceptor(ctx, in, info, handler) } @@ -126,7 +180,7 @@ var _Version_serviceDesc = grpc.ServiceDesc{ func (m *VersionResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -134,35 +188,51 @@ func (m *VersionResponse) Marshal() (dAtA []byte, err error) { } func (m *VersionResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VersionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Version) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintVersion(dAtA, i, uint64(len(m.Version))) - i += copy(dAtA[i:], m.Version) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Revision) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Revision) + copy(dAtA[i:], m.Revision) i = encodeVarintVersion(dAtA, i, uint64(len(m.Revision))) - i += copy(dAtA[i:], m.Revision) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintVersion(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func encodeVarintVersion(dAtA []byte, offset int, v uint64) int { + offset -= sovVersion(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *VersionResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Version) @@ -173,18 +243,14 @@ func (m *VersionResponse) Size() (n int) { if l > 0 { n += 1 + l + sovVersion(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovVersion(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozVersion(x uint64) (n int) { return sovVersion(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -196,6 +262,7 @@ func (this *VersionResponse) String() string { s := strings.Join([]string{`&VersionResponse{`, `Version:` + fmt.Sprintf("%v", this.Version) + `,`, `Revision:` + fmt.Sprintf("%v", this.Revision) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -223,7 +290,7 @@ func (m *VersionResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -251,7 +318,7 @@ func (m *VersionResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -261,6 +328,9 @@ func (m *VersionResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthVersion } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthVersion + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -280,7 +350,7 @@ func (m *VersionResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -290,6 +360,9 @@ func (m *VersionResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthVersion } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthVersion + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -301,12 +374,13 @@ func (m *VersionResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthVersion } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -319,6 +393,7 @@ func (m *VersionResponse) Unmarshal(dAtA []byte) error { func skipVersion(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -350,10 +425,8 @@ func skipVersion(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -370,77 +443,34 @@ func skipVersion(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthVersion } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowVersion - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipVersion(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupVersion + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthVersion + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthVersion = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowVersion = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthVersion = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowVersion = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupVersion = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/services/version/v1/version.proto", fileDescriptorVersion) -} - -var fileDescriptorVersion = []byte{ - // 243 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x4b, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, - 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x17, 0xa7, 0x16, 0x95, 0x65, 0x26, 0xa7, 0x16, 0xeb, - 0x97, 0xa5, 0x16, 0x15, 0x67, 0xe6, 0xe7, 0xe9, 0x97, 0x19, 0xc2, 0x98, 0x7a, 0x05, 0x45, 0xf9, - 0x25, 0xf9, 0x42, 0x72, 0x08, 0x1d, 0x7a, 0x30, 0xd5, 0x7a, 0x30, 0x25, 0x65, 0x86, 0x52, 0xd2, - 0xe9, 0xf9, 0xf9, 0xe9, 0x39, 0xa9, 0xfa, 0x60, 0xd5, 0x49, 0xa5, 0x69, 0xfa, 0xa9, 0xb9, 0x05, - 0x25, 0x95, 0x10, 0xcd, 0x52, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, 0x11, - 0x55, 0x72, 0xe7, 0xe2, 0x0f, 0x83, 0x18, 0x10, 0x94, 0x5a, 0x5c, 0x90, 0x9f, 0x57, 0x9c, 0x2a, - 0x24, 0xc1, 0xc5, 0x0e, 0x35, 0x53, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, 0x15, 0x92, - 0xe2, 0xe2, 0x28, 0x4a, 0x2d, 0xcb, 0x04, 0x4b, 0x31, 0x81, 0xa5, 0xe0, 0x7c, 0xa3, 0x58, 0x2e, - 0x76, 0xa8, 0x41, 0x42, 0x41, 0x08, 0xa6, 0x98, 0x1e, 0xc4, 0x49, 0x7a, 0x30, 0x27, 0xe9, 0xb9, - 0x82, 0x9c, 0x24, 0xa5, 0xaf, 0x87, 0xdf, 0x2b, 0x7a, 0x68, 0x8e, 0x72, 0x8a, 0x3a, 0xf1, 0x50, - 0x8e, 0xe1, 0xc6, 0x43, 0x39, 0x86, 0x86, 0x47, 0x72, 0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, - 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0x63, 0x94, 0x03, 0xb9, 0x81, 0x6b, 0x0d, 0x65, 0x46, 0x30, - 0x26, 0xb1, 0x81, 0x9d, 0x67, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x95, 0x0d, 0x52, 0x23, 0xa9, - 0x01, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/services/version/v1/version.proto b/vendor/github.com/containerd/containerd/api/services/version/v1/version.proto index 0e4c3d1e0811c..97681bb86ee79 100644 --- a/vendor/github.com/containerd/containerd/api/services/version/v1/version.proto +++ b/vendor/github.com/containerd/containerd/api/services/version/v1/version.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.services.version.v1; diff --git a/vendor/github.com/containerd/containerd/api/types/descriptor.pb.go b/vendor/github.com/containerd/containerd/api/types/descriptor.pb.go index 93e88c0dc275a..fe71dbf433000 100644 --- a/vendor/github.com/containerd/containerd/api/types/descriptor.pb.go +++ b/vendor/github.com/containerd/containerd/api/types/descriptor.pb.go @@ -1,35 +1,19 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/types/descriptor.proto -/* - Package types is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/types/descriptor.proto - github.com/containerd/containerd/api/types/metrics.proto - github.com/containerd/containerd/api/types/mount.proto - github.com/containerd/containerd/api/types/platform.proto - - It has these top-level messages: - Descriptor - Metric - Mount - Platform -*/ package types -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" - -import github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + github_com_opencontainers_go_digest "github.com/opencontainers/go-digest" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -40,7 +24,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // Descriptor describes a blob in a content store. // @@ -48,22 +32,84 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package // oci descriptor found in a manifest. // See https://godoc.org/github.com/opencontainers/image-spec/specs-go/v1#Descriptor type Descriptor struct { - MediaType string `protobuf:"bytes,1,opt,name=media_type,json=mediaType,proto3" json:"media_type,omitempty"` - Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,2,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` - Size_ int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` + MediaType string `protobuf:"bytes,1,opt,name=media_type,json=mediaType,proto3" json:"media_type,omitempty"` + Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,2,opt,name=digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"digest"` + Size_ int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` + Annotations map[string]string `protobuf:"bytes,5,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *Descriptor) Reset() { *m = Descriptor{} } -func (*Descriptor) ProtoMessage() {} -func (*Descriptor) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{0} } +func (m *Descriptor) Reset() { *m = Descriptor{} } +func (*Descriptor) ProtoMessage() {} +func (*Descriptor) Descriptor() ([]byte, []int) { + return fileDescriptor_37f958df3707db9e, []int{0} +} +func (m *Descriptor) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Descriptor) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Descriptor.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Descriptor) XXX_Merge(src proto.Message) { + xxx_messageInfo_Descriptor.Merge(m, src) +} +func (m *Descriptor) XXX_Size() int { + return m.Size() +} +func (m *Descriptor) XXX_DiscardUnknown() { + xxx_messageInfo_Descriptor.DiscardUnknown(m) +} + +var xxx_messageInfo_Descriptor proto.InternalMessageInfo func init() { proto.RegisterType((*Descriptor)(nil), "containerd.types.Descriptor") + proto.RegisterMapType((map[string]string)(nil), "containerd.types.Descriptor.AnnotationsEntry") +} + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/types/descriptor.proto", fileDescriptor_37f958df3707db9e) } + +var fileDescriptor_37f958df3707db9e = []byte{ + // 311 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xa7, 0xa4, 0x16, + 0x27, 0x17, 0x65, 0x16, 0x94, 0xe4, 0x17, 0xe9, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, + 0x94, 0xe9, 0x81, 0x95, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf5, 0x41, 0x2c, 0x88, + 0x3a, 0xa5, 0x39, 0x4c, 0x5c, 0x5c, 0x2e, 0x70, 0xcd, 0x42, 0xb2, 0x5c, 0x5c, 0xb9, 0xa9, 0x29, + 0x99, 0x89, 0xf1, 0x20, 0x3d, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x9c, 0x60, 0x91, 0x90, + 0xca, 0x82, 0x54, 0x21, 0x2f, 0x2e, 0xb6, 0x94, 0xcc, 0xf4, 0xd4, 0xe2, 0x12, 0x09, 0x26, 0x90, + 0x94, 0x93, 0xd1, 0x89, 0x7b, 0xf2, 0x0c, 0xb7, 0xee, 0xc9, 0x6b, 0x21, 0x39, 0x35, 0xbf, 0x20, + 0x35, 0x0f, 0x6e, 0x79, 0xb1, 0x7e, 0x7a, 0xbe, 0x2e, 0x44, 0x8b, 0x9e, 0x0b, 0x98, 0x0a, 0x82, + 0x9a, 0x20, 0x24, 0xc4, 0xc5, 0x52, 0x9c, 0x59, 0x95, 0x2a, 0xc1, 0xac, 0xc0, 0xa8, 0xc1, 0x1c, + 0x04, 0x66, 0x0b, 0xf9, 0x73, 0x71, 0x27, 0xe6, 0xe5, 0xe5, 0x97, 0x24, 0x96, 0x64, 0xe6, 0xe7, + 0x15, 0x4b, 0xb0, 0x2a, 0x30, 0x6b, 0x70, 0x1b, 0xe9, 0xea, 0xa1, 0xfb, 0x45, 0x0f, 0xe1, 0x62, + 0x3d, 0x47, 0x84, 0x7a, 0xd7, 0xbc, 0x92, 0xa2, 0xca, 0x20, 0x64, 0x13, 0xa4, 0xec, 0xb8, 0x04, + 0xd0, 0x15, 0x08, 0x09, 0x70, 0x31, 0x67, 0xa7, 0x56, 0x42, 0x3d, 0x07, 0x62, 0x0a, 0x89, 0x70, + 0xb1, 0x96, 0x25, 0xe6, 0x94, 0xa6, 0x42, 0x7c, 0x15, 0x04, 0xe1, 0x58, 0x31, 0x59, 0x30, 0x3a, + 0x79, 0x9d, 0x78, 0x28, 0xc7, 0x70, 0xe3, 0xa1, 0x1c, 0x43, 0xc3, 0x23, 0x39, 0xc6, 0x13, 0x8f, + 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x31, 0xca, 0x80, 0xf8, 0xd8, 0xb1, + 0x06, 0x93, 0x11, 0x0c, 0x49, 0x6c, 0xe0, 0x30, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x22, + 0x8a, 0x20, 0x4a, 0xda, 0x01, 0x00, 0x00, +} + func (m *Descriptor) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -71,40 +117,75 @@ func (m *Descriptor) Marshal() (dAtA []byte, err error) { } func (m *Descriptor) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Descriptor) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.MediaType) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintDescriptor(dAtA, i, uint64(len(m.MediaType))) - i += copy(dAtA[i:], m.MediaType) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Digest) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintDescriptor(dAtA, i, uint64(len(m.Digest))) - i += copy(dAtA[i:], m.Digest) + if len(m.Annotations) > 0 { + for k := range m.Annotations { + v := m.Annotations[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintDescriptor(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintDescriptor(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintDescriptor(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a + } } if m.Size_ != 0 { - dAtA[i] = 0x18 - i++ i = encodeVarintDescriptor(dAtA, i, uint64(m.Size_)) + i-- + dAtA[i] = 0x18 + } + if len(m.Digest) > 0 { + i -= len(m.Digest) + copy(dAtA[i:], m.Digest) + i = encodeVarintDescriptor(dAtA, i, uint64(len(m.Digest))) + i-- + dAtA[i] = 0x12 + } + if len(m.MediaType) > 0 { + i -= len(m.MediaType) + copy(dAtA[i:], m.MediaType) + i = encodeVarintDescriptor(dAtA, i, uint64(len(m.MediaType))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintDescriptor(dAtA []byte, offset int, v uint64) int { + offset -= sovDescriptor(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Descriptor) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.MediaType) @@ -118,18 +199,22 @@ func (m *Descriptor) Size() (n int) { if m.Size_ != 0 { n += 1 + sovDescriptor(uint64(m.Size_)) } + if len(m.Annotations) > 0 { + for k, v := range m.Annotations { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovDescriptor(uint64(len(k))) + 1 + len(v) + sovDescriptor(uint64(len(v))) + n += mapEntrySize + 1 + sovDescriptor(uint64(mapEntrySize)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovDescriptor(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozDescriptor(x uint64) (n int) { return sovDescriptor(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -138,10 +223,22 @@ func (this *Descriptor) String() string { if this == nil { return "nil" } + keysForAnnotations := make([]string, 0, len(this.Annotations)) + for k, _ := range this.Annotations { + keysForAnnotations = append(keysForAnnotations, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForAnnotations) + mapStringForAnnotations := "map[string]string{" + for _, k := range keysForAnnotations { + mapStringForAnnotations += fmt.Sprintf("%v: %v,", k, this.Annotations[k]) + } + mapStringForAnnotations += "}" s := strings.Join([]string{`&Descriptor{`, `MediaType:` + fmt.Sprintf("%v", this.MediaType) + `,`, `Digest:` + fmt.Sprintf("%v", this.Digest) + `,`, `Size_:` + fmt.Sprintf("%v", this.Size_) + `,`, + `Annotations:` + mapStringForAnnotations + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -169,7 +266,7 @@ func (m *Descriptor) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -197,7 +294,7 @@ func (m *Descriptor) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -207,6 +304,9 @@ func (m *Descriptor) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDescriptor } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDescriptor + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -226,7 +326,7 @@ func (m *Descriptor) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -236,6 +336,9 @@ func (m *Descriptor) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDescriptor } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDescriptor + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -255,23 +358,151 @@ func (m *Descriptor) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Size_ |= (int64(b) & 0x7F) << shift + m.Size_ |= int64(b&0x7F) << shift if b < 0x80 { break } } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDescriptor + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDescriptor + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDescriptor + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Annotations == nil { + m.Annotations = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDescriptor + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDescriptor + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthDescriptor + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthDescriptor + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDescriptor + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthDescriptor + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthDescriptor + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipDescriptor(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthDescriptor + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Annotations[mapkey] = mapvalue + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipDescriptor(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDescriptor } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -284,6 +515,7 @@ func (m *Descriptor) Unmarshal(dAtA []byte) error { func skipDescriptor(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -315,10 +547,8 @@ func skipDescriptor(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -335,76 +565,34 @@ func skipDescriptor(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthDescriptor } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowDescriptor - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipDescriptor(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupDescriptor + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthDescriptor + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthDescriptor = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowDescriptor = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthDescriptor = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDescriptor = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupDescriptor = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/types/descriptor.proto", fileDescriptorDescriptor) -} - -var fileDescriptorDescriptor = []byte{ - // 234 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, - 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xa7, 0xa4, 0x16, - 0x27, 0x17, 0x65, 0x16, 0x94, 0xe4, 0x17, 0xe9, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, - 0x94, 0xe9, 0x81, 0x95, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf5, 0x41, 0x2c, 0x88, - 0x3a, 0xa5, 0x6e, 0x46, 0x2e, 0x2e, 0x17, 0xb8, 0x66, 0x21, 0x59, 0x2e, 0xae, 0xdc, 0xd4, 0x94, - 0xcc, 0xc4, 0x78, 0x90, 0x1e, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x4e, 0xb0, 0x48, 0x48, - 0x65, 0x41, 0xaa, 0x90, 0x17, 0x17, 0x5b, 0x4a, 0x66, 0x7a, 0x6a, 0x71, 0x89, 0x04, 0x13, 0x48, - 0xca, 0xc9, 0xe8, 0xc4, 0x3d, 0x79, 0x86, 0x5b, 0xf7, 0xe4, 0xb5, 0x90, 0x9c, 0x9a, 0x5f, 0x90, - 0x9a, 0x07, 0xb7, 0xbc, 0x58, 0x3f, 0x3d, 0x5f, 0x17, 0xa2, 0x45, 0xcf, 0x05, 0x4c, 0x05, 0x41, - 0x4d, 0x10, 0x12, 0xe2, 0x62, 0x29, 0xce, 0xac, 0x4a, 0x95, 0x60, 0x56, 0x60, 0xd4, 0x60, 0x0e, - 0x02, 0xb3, 0x9d, 0xbc, 0x4e, 0x3c, 0x94, 0x63, 0xb8, 0xf1, 0x50, 0x8e, 0xa1, 0xe1, 0x91, 0x1c, - 0xe3, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x18, 0x65, 0x40, - 0x7c, 0x60, 0x58, 0x83, 0xc9, 0x08, 0x86, 0x24, 0x36, 0xb0, 0x17, 0x8d, 0x01, 0x01, 0x00, 0x00, - 0xff, 0xff, 0xea, 0xac, 0x78, 0x9a, 0x49, 0x01, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/types/descriptor.proto b/vendor/github.com/containerd/containerd/api/types/descriptor.proto index 5c00dca4f1bd1..a841d1bb22d44 100644 --- a/vendor/github.com/containerd/containerd/api/types/descriptor.proto +++ b/vendor/github.com/containerd/containerd/api/types/descriptor.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.types; @@ -15,4 +31,5 @@ message Descriptor { string media_type = 1; string digest = 2 [(gogoproto.customtype) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false]; int64 size = 3; + map annotations = 5; } diff --git a/vendor/github.com/containerd/containerd/api/types/metrics.pb.go b/vendor/github.com/containerd/containerd/api/types/metrics.pb.go index 52e9f40a5a93b..75773e442ab74 100644 --- a/vendor/github.com/containerd/containerd/api/types/metrics.pb.go +++ b/vendor/github.com/containerd/containerd/api/types/metrics.pb.go @@ -3,22 +3,18 @@ package types -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import google_protobuf1 "github.com/gogo/protobuf/types" -import _ "github.com/gogo/protobuf/types" - -import time "time" - -import types1 "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -26,23 +22,86 @@ var _ = fmt.Errorf var _ = math.Inf var _ = time.Kitchen +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type Metric struct { - Timestamp time.Time `protobuf:"bytes,1,opt,name=timestamp,stdtime" json:"timestamp"` - ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Data *google_protobuf1.Any `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"` + Timestamp time.Time `protobuf:"bytes,1,opt,name=timestamp,proto3,stdtime" json:"timestamp"` + ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Data *types.Any `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Metric) Reset() { *m = Metric{} } +func (*Metric) ProtoMessage() {} +func (*Metric) Descriptor() ([]byte, []int) { + return fileDescriptor_8d594d87edf6e6bc, []int{0} +} +func (m *Metric) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Metric) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Metric.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Metric) XXX_Merge(src proto.Message) { + xxx_messageInfo_Metric.Merge(m, src) +} +func (m *Metric) XXX_Size() int { + return m.Size() +} +func (m *Metric) XXX_DiscardUnknown() { + xxx_messageInfo_Metric.DiscardUnknown(m) } -func (m *Metric) Reset() { *m = Metric{} } -func (*Metric) ProtoMessage() {} -func (*Metric) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{0} } +var xxx_messageInfo_Metric proto.InternalMessageInfo func init() { proto.RegisterType((*Metric)(nil), "containerd.types.Metric") } + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/types/metrics.proto", fileDescriptor_8d594d87edf6e6bc) +} + +var fileDescriptor_8d594d87edf6e6bc = []byte{ + // 258 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x48, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xe7, 0xa6, 0x96, + 0x14, 0x65, 0x26, 0x17, 0xeb, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0xd4, 0xe8, 0x81, + 0xe5, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x94, 0x64, + 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x3e, 0x98, 0x97, 0x54, 0x9a, 0xa6, 0x9f, 0x98, 0x57, 0x09, + 0x95, 0x92, 0x47, 0x97, 0x2a, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0x80, 0x28, 0x50, + 0xea, 0x63, 0xe4, 0x62, 0xf3, 0x05, 0xdb, 0x2a, 0xe4, 0xc4, 0xc5, 0x09, 0x97, 0x95, 0x60, 0x54, + 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd2, 0x83, 0xe8, 0xd7, 0x83, 0xe9, 0xd7, 0x0b, 0x81, 0xa9, 0x70, + 0xe2, 0x38, 0x71, 0x4f, 0x9e, 0x61, 0xc2, 0x7d, 0x79, 0xc6, 0x20, 0x84, 0x36, 0x21, 0x31, 0x2e, + 0xa6, 0xcc, 0x14, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0x3c, + 0x5d, 0x82, 0x98, 0x32, 0x53, 0x84, 0x34, 0xb8, 0x58, 0x52, 0x12, 0x4b, 0x12, 0x25, 0x98, 0xc1, + 0xc6, 0x8a, 0x60, 0x18, 0xeb, 0x98, 0x57, 0x19, 0x04, 0x56, 0xe1, 0xe4, 0x75, 0xe2, 0xa1, 0x1c, + 0xc3, 0x8d, 0x87, 0x72, 0x0c, 0x0d, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, + 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x28, 0x03, 0xe2, 0x03, 0xd2, 0x1a, 0x4c, 0x46, 0x30, 0x24, + 0xb1, 0x81, 0x6d, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xde, 0x0d, 0x02, 0xfe, 0x85, 0x01, + 0x00, 0x00, +} + func (m *Metric) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -50,50 +109,67 @@ func (m *Metric) Marshal() (dAtA []byte, err error) { } func (m *Metric) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Metric) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintMetrics(dAtA, i, uint64(types1.SizeOfStdTime(m.Timestamp))) - n1, err := types1.StdTimeMarshalTo(m.Timestamp, dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Data != nil { + { + size, err := m.Data.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMetrics(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } - i += n1 if len(m.ID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ID) + copy(dAtA[i:], m.ID) i = encodeVarintMetrics(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + i-- + dAtA[i] = 0x12 } - if m.Data != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintMetrics(dAtA, i, uint64(m.Data.Size())) - n2, err := m.Data.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err2 != nil { + return 0, err2 } - return i, nil + i -= n2 + i = encodeVarintMetrics(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func encodeVarintMetrics(dAtA []byte, offset int, v uint64) int { + offset -= sovMetrics(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Metric) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l - l = types1.SizeOfStdTime(m.Timestamp) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp) n += 1 + l + sovMetrics(uint64(l)) l = len(m.ID) if l > 0 { @@ -103,18 +179,14 @@ func (m *Metric) Size() (n int) { l = m.Data.Size() n += 1 + l + sovMetrics(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovMetrics(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozMetrics(x uint64) (n int) { return sovMetrics(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -124,9 +196,10 @@ func (this *Metric) String() string { return "nil" } s := strings.Join([]string{`&Metric{`, - `Timestamp:` + strings.Replace(strings.Replace(this.Timestamp.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, + `Timestamp:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Timestamp), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, - `Data:` + strings.Replace(fmt.Sprintf("%v", this.Data), "Any", "google_protobuf1.Any", 1) + `,`, + `Data:` + strings.Replace(fmt.Sprintf("%v", this.Data), "Any", "types.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -154,7 +227,7 @@ func (m *Metric) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -182,7 +255,7 @@ func (m *Metric) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -191,10 +264,13 @@ func (m *Metric) Unmarshal(dAtA []byte) error { return ErrInvalidLengthMetrics } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types1.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -212,7 +288,7 @@ func (m *Metric) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -222,6 +298,9 @@ func (m *Metric) Unmarshal(dAtA []byte) error { return ErrInvalidLengthMetrics } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -241,7 +320,7 @@ func (m *Metric) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -250,11 +329,14 @@ func (m *Metric) Unmarshal(dAtA []byte) error { return ErrInvalidLengthMetrics } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMetrics + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Data == nil { - m.Data = &google_protobuf1.Any{} + m.Data = &types.Any{} } if err := m.Data.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -266,12 +348,13 @@ func (m *Metric) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthMetrics } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -284,6 +367,7 @@ func (m *Metric) Unmarshal(dAtA []byte) error { func skipMetrics(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -315,10 +399,8 @@ func skipMetrics(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -335,78 +417,34 @@ func skipMetrics(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthMetrics } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowMetrics - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipMetrics(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMetrics + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthMetrics + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthMetrics = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowMetrics = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthMetrics = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMetrics = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMetrics = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/types/metrics.proto", fileDescriptorMetrics) -} - -var fileDescriptorMetrics = []byte{ - // 258 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x48, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, - 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xe7, 0xa6, 0x96, - 0x14, 0x65, 0x26, 0x17, 0xeb, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0xd4, 0xe8, 0x81, - 0xe5, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x94, 0x64, - 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x3e, 0x98, 0x97, 0x54, 0x9a, 0xa6, 0x9f, 0x98, 0x57, 0x09, - 0x95, 0x92, 0x47, 0x97, 0x2a, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0x80, 0x28, 0x50, - 0xea, 0x63, 0xe4, 0x62, 0xf3, 0x05, 0xdb, 0x2a, 0xe4, 0xc4, 0xc5, 0x09, 0x97, 0x95, 0x60, 0x54, - 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd2, 0x83, 0xe8, 0xd7, 0x83, 0xe9, 0xd7, 0x0b, 0x81, 0xa9, 0x70, - 0xe2, 0x38, 0x71, 0x4f, 0x9e, 0x61, 0xc2, 0x7d, 0x79, 0xc6, 0x20, 0x84, 0x36, 0x21, 0x31, 0x2e, - 0xa6, 0xcc, 0x14, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0x3c, - 0x5d, 0x82, 0x98, 0x32, 0x53, 0x84, 0x34, 0xb8, 0x58, 0x52, 0x12, 0x4b, 0x12, 0x25, 0x98, 0xc1, - 0xc6, 0x8a, 0x60, 0x18, 0xeb, 0x98, 0x57, 0x19, 0x04, 0x56, 0xe1, 0xe4, 0x75, 0xe2, 0xa1, 0x1c, - 0xc3, 0x8d, 0x87, 0x72, 0x0c, 0x0d, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, - 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x28, 0x03, 0xe2, 0x03, 0xd2, 0x1a, 0x4c, 0x46, 0x30, 0x24, - 0xb1, 0x81, 0x6d, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xde, 0x0d, 0x02, 0xfe, 0x85, 0x01, - 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/types/metrics.proto b/vendor/github.com/containerd/containerd/api/types/metrics.proto index 0e631d2ac3527..b8bc673267fc5 100644 --- a/vendor/github.com/containerd/containerd/api/types/metrics.proto +++ b/vendor/github.com/containerd/containerd/api/types/metrics.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.types; diff --git a/vendor/github.com/containerd/containerd/api/types/mount.pb.go b/vendor/github.com/containerd/containerd/api/types/mount.pb.go index f7a9c3c1fe2ae..d0a0bee761f03 100644 --- a/vendor/github.com/containerd/containerd/api/types/mount.pb.go +++ b/vendor/github.com/containerd/containerd/api/types/mount.pb.go @@ -3,22 +3,27 @@ package types -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + // Mount describes mounts for a container. // // This type is the lingua franca of ContainerD. All services provide mounts @@ -35,20 +40,73 @@ type Mount struct { // Target path in container Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` // Options specifies zero or more fstab style mount options. - Options []string `protobuf:"bytes,4,rep,name=options" json:"options,omitempty"` + Options []string `protobuf:"bytes,4,rep,name=options,proto3" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Mount) Reset() { *m = Mount{} } +func (*Mount) ProtoMessage() {} +func (*Mount) Descriptor() ([]byte, []int) { + return fileDescriptor_920196890d4a7b9f, []int{0} +} +func (m *Mount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Mount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Mount.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Mount) XXX_Merge(src proto.Message) { + xxx_messageInfo_Mount.Merge(m, src) +} +func (m *Mount) XXX_Size() int { + return m.Size() +} +func (m *Mount) XXX_DiscardUnknown() { + xxx_messageInfo_Mount.DiscardUnknown(m) } -func (m *Mount) Reset() { *m = Mount{} } -func (*Mount) ProtoMessage() {} -func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorMount, []int{0} } +var xxx_messageInfo_Mount proto.InternalMessageInfo func init() { proto.RegisterType((*Mount)(nil), "containerd.types.Mount") } + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/types/mount.proto", fileDescriptor_920196890d4a7b9f) +} + +var fileDescriptor_920196890d4a7b9f = []byte{ + // 202 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4b, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xe7, 0xe6, 0x97, + 0xe6, 0x95, 0xe8, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0x54, 0xe8, 0x81, 0x65, 0xa5, + 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x52, 0x2a, 0x17, 0xab, + 0x2f, 0x48, 0x9b, 0x90, 0x10, 0x17, 0x0b, 0x48, 0x9d, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, + 0x98, 0x2d, 0x24, 0xc6, 0xc5, 0x56, 0x9c, 0x5f, 0x5a, 0x94, 0x9c, 0x2a, 0xc1, 0x04, 0x16, 0x85, + 0xf2, 0x40, 0xe2, 0x25, 0x89, 0x45, 0xe9, 0xa9, 0x25, 0x12, 0xcc, 0x10, 0x71, 0x08, 0x4f, 0x48, + 0x82, 0x8b, 0x3d, 0xbf, 0xa0, 0x24, 0x33, 0x3f, 0xaf, 0x58, 0x82, 0x45, 0x81, 0x59, 0x83, 0x33, + 0x08, 0xc6, 0x75, 0xf2, 0x3a, 0xf1, 0x50, 0x8e, 0xe1, 0xc6, 0x43, 0x39, 0x86, 0x86, 0x47, 0x72, + 0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0x63, 0x94, 0x01, + 0xf1, 0x1e, 0xb4, 0x06, 0x93, 0x11, 0x0c, 0x49, 0x6c, 0x60, 0xb7, 0x1b, 0x03, 0x02, 0x00, 0x00, + 0xff, 0xff, 0x82, 0x1c, 0x02, 0x18, 0x1d, 0x01, 0x00, 0x00, +} + func (m *Mount) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -56,56 +114,67 @@ func (m *Mount) Marshal() (dAtA []byte, err error) { } func (m *Mount) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Mount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Type) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintMount(dAtA, i, uint64(len(m.Type))) - i += copy(dAtA[i:], m.Type) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Source) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintMount(dAtA, i, uint64(len(m.Source))) - i += copy(dAtA[i:], m.Source) + if len(m.Options) > 0 { + for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Options[iNdEx]) + copy(dAtA[i:], m.Options[iNdEx]) + i = encodeVarintMount(dAtA, i, uint64(len(m.Options[iNdEx]))) + i-- + dAtA[i] = 0x22 + } } if len(m.Target) > 0 { - dAtA[i] = 0x1a - i++ + i -= len(m.Target) + copy(dAtA[i:], m.Target) i = encodeVarintMount(dAtA, i, uint64(len(m.Target))) - i += copy(dAtA[i:], m.Target) + i-- + dAtA[i] = 0x1a } - if len(m.Options) > 0 { - for _, s := range m.Options { - dAtA[i] = 0x22 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } + if len(m.Source) > 0 { + i -= len(m.Source) + copy(dAtA[i:], m.Source) + i = encodeVarintMount(dAtA, i, uint64(len(m.Source))) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = encodeVarintMount(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func encodeVarintMount(dAtA []byte, offset int, v uint64) int { + offset -= sovMount(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Mount) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Type) @@ -126,18 +195,14 @@ func (m *Mount) Size() (n int) { n += 1 + l + sovMount(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovMount(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozMount(x uint64) (n int) { return sovMount(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -151,6 +216,7 @@ func (this *Mount) String() string { `Source:` + fmt.Sprintf("%v", this.Source) + `,`, `Target:` + fmt.Sprintf("%v", this.Target) + `,`, `Options:` + fmt.Sprintf("%v", this.Options) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -178,7 +244,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -206,7 +272,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -216,6 +282,9 @@ func (m *Mount) Unmarshal(dAtA []byte) error { return ErrInvalidLengthMount } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMount + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -235,7 +304,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -245,6 +314,9 @@ func (m *Mount) Unmarshal(dAtA []byte) error { return ErrInvalidLengthMount } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMount + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -264,7 +336,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -274,6 +346,9 @@ func (m *Mount) Unmarshal(dAtA []byte) error { return ErrInvalidLengthMount } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMount + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -293,7 +368,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -303,6 +378,9 @@ func (m *Mount) Unmarshal(dAtA []byte) error { return ErrInvalidLengthMount } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMount + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -314,12 +392,13 @@ func (m *Mount) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthMount } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -332,6 +411,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { func skipMount(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -363,10 +443,8 @@ func skipMount(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -383,74 +461,34 @@ func skipMount(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthMount } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowMount - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipMount(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMount + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthMount + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthMount = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowMount = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthMount = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMount = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMount = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/types/mount.proto", fileDescriptorMount) -} - -var fileDescriptorMount = []byte{ - // 202 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4b, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, - 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0xe7, 0xe6, 0x97, - 0xe6, 0x95, 0xe8, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0x54, 0xe8, 0x81, 0x65, 0xa5, - 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x92, 0xfa, 0x20, 0x16, 0x44, 0x9d, 0x52, 0x2a, 0x17, 0xab, - 0x2f, 0x48, 0x9b, 0x90, 0x10, 0x17, 0x0b, 0x48, 0x9d, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, - 0x98, 0x2d, 0x24, 0xc6, 0xc5, 0x56, 0x9c, 0x5f, 0x5a, 0x94, 0x9c, 0x2a, 0xc1, 0x04, 0x16, 0x85, - 0xf2, 0x40, 0xe2, 0x25, 0x89, 0x45, 0xe9, 0xa9, 0x25, 0x12, 0xcc, 0x10, 0x71, 0x08, 0x4f, 0x48, - 0x82, 0x8b, 0x3d, 0xbf, 0xa0, 0x24, 0x33, 0x3f, 0xaf, 0x58, 0x82, 0x45, 0x81, 0x59, 0x83, 0x33, - 0x08, 0xc6, 0x75, 0xf2, 0x3a, 0xf1, 0x50, 0x8e, 0xe1, 0xc6, 0x43, 0x39, 0x86, 0x86, 0x47, 0x72, - 0x8c, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0x63, 0x94, 0x01, - 0xf1, 0x1e, 0xb4, 0x06, 0x93, 0x11, 0x0c, 0x49, 0x6c, 0x60, 0xb7, 0x1b, 0x03, 0x02, 0x00, 0x00, - 0xff, 0xff, 0x82, 0x1c, 0x02, 0x18, 0x1d, 0x01, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/types/mount.proto b/vendor/github.com/containerd/containerd/api/types/mount.proto index cd80e44a2c0e2..41ab13313871e 100644 --- a/vendor/github.com/containerd/containerd/api/types/mount.proto +++ b/vendor/github.com/containerd/containerd/api/types/mount.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.types; diff --git a/vendor/github.com/containerd/containerd/api/types/platform.pb.go b/vendor/github.com/containerd/containerd/api/types/platform.pb.go index ba9a3bf88175d..a0f78c8a769d6 100644 --- a/vendor/github.com/containerd/containerd/api/types/platform.pb.go +++ b/vendor/github.com/containerd/containerd/api/types/platform.pb.go @@ -3,41 +3,99 @@ package types -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + // Platform follows the structure of the OCI platform specification, from // descriptors. type Platform struct { - OS string `protobuf:"bytes,1,opt,name=os,proto3" json:"os,omitempty"` - Architecture string `protobuf:"bytes,2,opt,name=architecture,proto3" json:"architecture,omitempty"` - Variant string `protobuf:"bytes,3,opt,name=variant,proto3" json:"variant,omitempty"` + OS string `protobuf:"bytes,1,opt,name=os,proto3" json:"os,omitempty"` + Architecture string `protobuf:"bytes,2,opt,name=architecture,proto3" json:"architecture,omitempty"` + Variant string `protobuf:"bytes,3,opt,name=variant,proto3" json:"variant,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *Platform) Reset() { *m = Platform{} } -func (*Platform) ProtoMessage() {} -func (*Platform) Descriptor() ([]byte, []int) { return fileDescriptorPlatform, []int{0} } +func (m *Platform) Reset() { *m = Platform{} } +func (*Platform) ProtoMessage() {} +func (*Platform) Descriptor() ([]byte, []int) { + return fileDescriptor_24ba7a4b83e2367e, []int{0} +} +func (m *Platform) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Platform) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Platform.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Platform) XXX_Merge(src proto.Message) { + xxx_messageInfo_Platform.Merge(m, src) +} +func (m *Platform) XXX_Size() int { + return m.Size() +} +func (m *Platform) XXX_DiscardUnknown() { + xxx_messageInfo_Platform.DiscardUnknown(m) +} + +var xxx_messageInfo_Platform proto.InternalMessageInfo func init() { proto.RegisterType((*Platform)(nil), "containerd.types.Platform") } + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/types/platform.proto", fileDescriptor_24ba7a4b83e2367e) +} + +var fileDescriptor_24ba7a4b83e2367e = []byte{ + // 205 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0x17, 0xe4, 0x24, + 0x96, 0xa4, 0xe5, 0x17, 0xe5, 0xea, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0x14, 0xe9, + 0x81, 0x15, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf5, 0x41, 0x2c, 0x88, 0x3a, 0xa5, + 0x04, 0x2e, 0x8e, 0x00, 0xa8, 0x4e, 0x21, 0x31, 0x2e, 0xa6, 0xfc, 0x62, 0x09, 0x46, 0x05, 0x46, + 0x0d, 0x4e, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0xfc, 0x83, 0x83, 0x98, 0xf2, 0x8b, 0x85, 0x94, + 0xb8, 0x78, 0x12, 0x8b, 0x92, 0x33, 0x32, 0x4b, 0x52, 0x93, 0x4b, 0x4a, 0x8b, 0x52, 0x25, 0x98, + 0x40, 0x2a, 0x82, 0x50, 0xc4, 0x84, 0x24, 0xb8, 0xd8, 0xcb, 0x12, 0x8b, 0x32, 0x13, 0xf3, 0x4a, + 0x24, 0x98, 0xc1, 0xd2, 0x30, 0xae, 0x93, 0xd7, 0x89, 0x87, 0x72, 0x0c, 0x37, 0x1e, 0xca, 0x31, + 0x34, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, + 0x18, 0xa3, 0x0c, 0x88, 0xf7, 0x9e, 0x35, 0x98, 0x8c, 0x60, 0x48, 0x62, 0x03, 0x3b, 0xdb, 0x18, + 0x10, 0x00, 0x00, 0xff, 0xff, 0x05, 0xaa, 0xda, 0xa1, 0x1b, 0x01, 0x00, 0x00, +} + func (m *Platform) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -45,41 +103,58 @@ func (m *Platform) Marshal() (dAtA []byte, err error) { } func (m *Platform) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Platform) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.OS) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintPlatform(dAtA, i, uint64(len(m.OS))) - i += copy(dAtA[i:], m.OS) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Variant) > 0 { + i -= len(m.Variant) + copy(dAtA[i:], m.Variant) + i = encodeVarintPlatform(dAtA, i, uint64(len(m.Variant))) + i-- + dAtA[i] = 0x1a } if len(m.Architecture) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Architecture) + copy(dAtA[i:], m.Architecture) i = encodeVarintPlatform(dAtA, i, uint64(len(m.Architecture))) - i += copy(dAtA[i:], m.Architecture) + i-- + dAtA[i] = 0x12 } - if len(m.Variant) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintPlatform(dAtA, i, uint64(len(m.Variant))) - i += copy(dAtA[i:], m.Variant) + if len(m.OS) > 0 { + i -= len(m.OS) + copy(dAtA[i:], m.OS) + i = encodeVarintPlatform(dAtA, i, uint64(len(m.OS))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintPlatform(dAtA []byte, offset int, v uint64) int { + offset -= sovPlatform(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Platform) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.OS) @@ -94,18 +169,14 @@ func (m *Platform) Size() (n int) { if l > 0 { n += 1 + l + sovPlatform(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovPlatform(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozPlatform(x uint64) (n int) { return sovPlatform(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -118,6 +189,7 @@ func (this *Platform) String() string { `OS:` + fmt.Sprintf("%v", this.OS) + `,`, `Architecture:` + fmt.Sprintf("%v", this.Architecture) + `,`, `Variant:` + fmt.Sprintf("%v", this.Variant) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -145,7 +217,7 @@ func (m *Platform) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -173,7 +245,7 @@ func (m *Platform) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -183,6 +255,9 @@ func (m *Platform) Unmarshal(dAtA []byte) error { return ErrInvalidLengthPlatform } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlatform + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -202,7 +277,7 @@ func (m *Platform) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -212,6 +287,9 @@ func (m *Platform) Unmarshal(dAtA []byte) error { return ErrInvalidLengthPlatform } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlatform + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -231,7 +309,7 @@ func (m *Platform) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -241,6 +319,9 @@ func (m *Platform) Unmarshal(dAtA []byte) error { return ErrInvalidLengthPlatform } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlatform + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -252,12 +333,13 @@ func (m *Platform) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthPlatform } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -270,6 +352,7 @@ func (m *Platform) Unmarshal(dAtA []byte) error { func skipPlatform(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -301,10 +384,8 @@ func skipPlatform(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -321,74 +402,34 @@ func skipPlatform(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthPlatform } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowPlatform - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipPlatform(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupPlatform + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthPlatform + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthPlatform = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowPlatform = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthPlatform = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowPlatform = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupPlatform = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/types/platform.proto", fileDescriptorPlatform) -} - -var fileDescriptorPlatform = []byte{ - // 205 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, - 0x4a, 0x41, 0x66, 0x26, 0x16, 0x64, 0xea, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0x17, 0xe4, 0x24, - 0x96, 0xa4, 0xe5, 0x17, 0xe5, 0xea, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0x09, 0x20, 0x14, 0xe9, - 0x81, 0x15, 0x48, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x25, 0xf5, 0x41, 0x2c, 0x88, 0x3a, 0xa5, - 0x04, 0x2e, 0x8e, 0x00, 0xa8, 0x4e, 0x21, 0x31, 0x2e, 0xa6, 0xfc, 0x62, 0x09, 0x46, 0x05, 0x46, - 0x0d, 0x4e, 0x27, 0xb6, 0x47, 0xf7, 0xe4, 0x99, 0xfc, 0x83, 0x83, 0x98, 0xf2, 0x8b, 0x85, 0x94, - 0xb8, 0x78, 0x12, 0x8b, 0x92, 0x33, 0x32, 0x4b, 0x52, 0x93, 0x4b, 0x4a, 0x8b, 0x52, 0x25, 0x98, - 0x40, 0x2a, 0x82, 0x50, 0xc4, 0x84, 0x24, 0xb8, 0xd8, 0xcb, 0x12, 0x8b, 0x32, 0x13, 0xf3, 0x4a, - 0x24, 0x98, 0xc1, 0xd2, 0x30, 0xae, 0x93, 0xd7, 0x89, 0x87, 0x72, 0x0c, 0x37, 0x1e, 0xca, 0x31, - 0x34, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, - 0x18, 0xa3, 0x0c, 0x88, 0xf7, 0x9e, 0x35, 0x98, 0x8c, 0x60, 0x48, 0x62, 0x03, 0x3b, 0xdb, 0x18, - 0x10, 0x00, 0x00, 0xff, 0xff, 0x05, 0xaa, 0xda, 0xa1, 0x1b, 0x01, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/types/platform.proto b/vendor/github.com/containerd/containerd/api/types/platform.proto index 4cf9834bdc494..7813606841104 100644 --- a/vendor/github.com/containerd/containerd/api/types/platform.proto +++ b/vendor/github.com/containerd/containerd/api/types/platform.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.types; diff --git a/vendor/github.com/containerd/containerd/api/types/task/task.pb.go b/vendor/github.com/containerd/containerd/api/types/task/task.pb.go index 437abe8f4cae9..f511bbd058c85 100644 --- a/vendor/github.com/containerd/containerd/api/types/task/task.pb.go +++ b/vendor/github.com/containerd/containerd/api/types/task/task.pb.go @@ -1,34 +1,20 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/api/types/task/task.proto -/* - Package task is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/api/types/task/task.proto - - It has these top-level messages: - Process - ProcessInfo -*/ package task -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import _ "github.com/gogo/protobuf/types" -import google_protobuf2 "github.com/gogo/protobuf/types" - -import time "time" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -40,7 +26,7 @@ var _ = time.Kitchen // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Status int32 @@ -61,6 +47,7 @@ var Status_name = map[int32]string{ 4: "PAUSED", 5: "PAUSING", } + var Status_value = map[string]int32{ "UNKNOWN": 0, "CREATED": 1, @@ -73,24 +60,58 @@ var Status_value = map[string]int32{ func (x Status) String() string { return proto.EnumName(Status_name, int32(x)) } -func (Status) EnumDescriptor() ([]byte, []int) { return fileDescriptorTask, []int{0} } + +func (Status) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_391ef18c8ab0dc16, []int{0} +} type Process struct { - ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` - Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=containerd.v1.types.Status" json:"status,omitempty"` - Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"` - Terminal bool `protobuf:"varint,8,opt,name=terminal,proto3" json:"terminal,omitempty"` - ExitStatus uint32 `protobuf:"varint,9,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` - ExitedAt time.Time `protobuf:"bytes,10,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` + ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=containerd.v1.types.Status" json:"status,omitempty"` + Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"` + Terminal bool `protobuf:"varint,8,opt,name=terminal,proto3" json:"terminal,omitempty"` + ExitStatus uint32 `protobuf:"varint,9,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,10,opt,name=exited_at,json=exitedAt,proto3,stdtime" json:"exited_at"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Process) Reset() { *m = Process{} } +func (*Process) ProtoMessage() {} +func (*Process) Descriptor() ([]byte, []int) { + return fileDescriptor_391ef18c8ab0dc16, []int{0} +} +func (m *Process) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Process) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Process.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Process) XXX_Merge(src proto.Message) { + xxx_messageInfo_Process.Merge(m, src) +} +func (m *Process) XXX_Size() int { + return m.Size() +} +func (m *Process) XXX_DiscardUnknown() { + xxx_messageInfo_Process.DiscardUnknown(m) } -func (m *Process) Reset() { *m = Process{} } -func (*Process) ProtoMessage() {} -func (*Process) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{0} } +var xxx_messageInfo_Process proto.InternalMessageInfo type ProcessInfo struct { // PID is the process ID. @@ -98,22 +119,97 @@ type ProcessInfo struct { // Info contains additional process information. // // Info varies by platform. - Info *google_protobuf2.Any `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"` + Info *types.Any `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ProcessInfo) Reset() { *m = ProcessInfo{} } -func (*ProcessInfo) ProtoMessage() {} -func (*ProcessInfo) Descriptor() ([]byte, []int) { return fileDescriptorTask, []int{1} } +func (m *ProcessInfo) Reset() { *m = ProcessInfo{} } +func (*ProcessInfo) ProtoMessage() {} +func (*ProcessInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_391ef18c8ab0dc16, []int{1} +} +func (m *ProcessInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProcessInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProcessInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ProcessInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProcessInfo.Merge(m, src) +} +func (m *ProcessInfo) XXX_Size() int { + return m.Size() +} +func (m *ProcessInfo) XXX_DiscardUnknown() { + xxx_messageInfo_ProcessInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_ProcessInfo proto.InternalMessageInfo func init() { + proto.RegisterEnum("containerd.v1.types.Status", Status_name, Status_value) proto.RegisterType((*Process)(nil), "containerd.v1.types.Process") proto.RegisterType((*ProcessInfo)(nil), "containerd.v1.types.ProcessInfo") - proto.RegisterEnum("containerd.v1.types.Status", Status_name, Status_value) } + +func init() { + proto.RegisterFile("github.com/containerd/containerd/api/types/task/task.proto", fileDescriptor_391ef18c8ab0dc16) +} + +var fileDescriptor_391ef18c8ab0dc16 = []byte{ + // 545 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x3f, 0x6f, 0xd3, 0x40, + 0x18, 0xc6, 0x7d, 0x6e, 0xeb, 0xa6, 0xe7, 0xb6, 0x18, 0x13, 0x55, 0xc6, 0x20, 0xdb, 0xea, 0x64, + 0x31, 0xd8, 0x22, 0xdd, 0xd8, 0xf2, 0x4f, 0xc8, 0x42, 0x72, 0x23, 0x27, 0x11, 0x6c, 0x91, 0x13, + 0x5f, 0xcc, 0xa9, 0xcd, 0x9d, 0x65, 0x9f, 0x81, 0x6c, 0x8c, 0xa8, 0x13, 0x5f, 0xa0, 0x13, 0x7c, + 0x0a, 0x3e, 0x41, 0x46, 0x26, 0xc4, 0x14, 0xa8, 0x3f, 0x09, 0x3a, 0xdb, 0x49, 0x23, 0x60, 0x39, + 0xbd, 0xef, 0xf3, 0x7b, 0xee, 0xbd, 0xf7, 0x1e, 0xf8, 0x22, 0xc6, 0xec, 0x6d, 0x3e, 0x75, 0x66, + 0x74, 0xe1, 0xce, 0x28, 0x61, 0x21, 0x26, 0x28, 0x8d, 0x76, 0xcb, 0x30, 0xc1, 0x2e, 0x5b, 0x26, + 0x28, 0x73, 0x59, 0x98, 0x5d, 0x95, 0x87, 0x93, 0xa4, 0x94, 0x51, 0xf5, 0xd1, 0xbd, 0xcb, 0x79, + 0xf7, 0xdc, 0x29, 0x4d, 0x7a, 0x33, 0xa6, 0x31, 0x2d, 0xb9, 0xcb, 0xab, 0xca, 0xaa, 0x9b, 0x31, + 0xa5, 0xf1, 0x35, 0x72, 0xcb, 0x6e, 0x9a, 0xcf, 0x5d, 0x86, 0x17, 0x28, 0x63, 0xe1, 0x22, 0xa9, + 0x0d, 0x8f, 0xff, 0x36, 0x84, 0x64, 0x59, 0xa1, 0xf3, 0x42, 0x84, 0x87, 0x83, 0x94, 0xce, 0x50, + 0x96, 0xa9, 0x2d, 0x78, 0xbc, 0x7d, 0x74, 0x82, 0x23, 0x0d, 0x58, 0xc0, 0x3e, 0xea, 0x3c, 0x28, + 0xd6, 0xa6, 0xdc, 0xdd, 0xe8, 0x5e, 0x2f, 0x90, 0xb7, 0x26, 0x2f, 0x52, 0xcf, 0xa0, 0x88, 0x23, + 0x4d, 0x2c, 0x9d, 0x52, 0xb1, 0x36, 0x45, 0xaf, 0x17, 0x88, 0x38, 0x52, 0x15, 0xb8, 0x97, 0xe0, + 0x48, 0xdb, 0xb3, 0x80, 0x7d, 0x12, 0xf0, 0x52, 0xbd, 0x80, 0x52, 0xc6, 0x42, 0x96, 0x67, 0xda, + 0xbe, 0x05, 0xec, 0xd3, 0xd6, 0x13, 0xe7, 0x3f, 0x3f, 0x74, 0x86, 0xa5, 0x25, 0xa8, 0xad, 0x6a, + 0x13, 0x1e, 0x64, 0x2c, 0xc2, 0x44, 0x3b, 0xe0, 0x2f, 0x04, 0x55, 0xa3, 0x9e, 0xf1, 0x51, 0x11, + 0xcd, 0x99, 0x26, 0x95, 0x72, 0xdd, 0xd5, 0x3a, 0x4a, 0x53, 0xed, 0x70, 0xab, 0xa3, 0x34, 0x55, + 0x75, 0xd8, 0x60, 0x28, 0x5d, 0x60, 0x12, 0x5e, 0x6b, 0x0d, 0x0b, 0xd8, 0x8d, 0x60, 0xdb, 0xab, + 0x26, 0x94, 0xd1, 0x07, 0xcc, 0x26, 0xf5, 0x6e, 0x47, 0xe5, 0xc2, 0x90, 0x4b, 0xd5, 0x2a, 0x6a, + 0x1b, 0x1e, 0xf1, 0x0e, 0x45, 0x93, 0x90, 0x69, 0xd0, 0x02, 0xb6, 0xdc, 0xd2, 0x9d, 0x2a, 0x50, + 0x67, 0x13, 0xa8, 0x33, 0xda, 0x24, 0xde, 0x69, 0xac, 0xd6, 0xa6, 0xf0, 0xf9, 0x97, 0x09, 0x82, + 0x46, 0x75, 0xad, 0xcd, 0xce, 0x3d, 0x28, 0xd7, 0x19, 0x7b, 0x64, 0x4e, 0x37, 0xd9, 0x80, 0xfb, + 0x6c, 0x6c, 0xb8, 0x8f, 0xc9, 0x9c, 0x96, 0x39, 0xca, 0xad, 0xe6, 0x3f, 0xe3, 0xdb, 0x64, 0x19, + 0x94, 0x8e, 0x67, 0x3f, 0x00, 0x94, 0xea, 0xc5, 0x0c, 0x78, 0x38, 0xf6, 0x5f, 0xf9, 0x97, 0xaf, + 0x7d, 0x45, 0xd0, 0x1f, 0xde, 0xdc, 0x5a, 0x27, 0x15, 0x18, 0x93, 0x2b, 0x42, 0xdf, 0x13, 0xce, + 0xbb, 0x41, 0xbf, 0x3d, 0xea, 0xf7, 0x14, 0xb0, 0xcb, 0xbb, 0x29, 0x0a, 0x19, 0x8a, 0x38, 0x0f, + 0xc6, 0xbe, 0xef, 0xf9, 0x2f, 0x15, 0x71, 0x97, 0x07, 0x39, 0x21, 0x98, 0xc4, 0x9c, 0x0f, 0x47, + 0x97, 0x83, 0x41, 0xbf, 0xa7, 0xec, 0xed, 0xf2, 0x21, 0xa3, 0x49, 0x82, 0x22, 0xf5, 0x29, 0x94, + 0x06, 0xed, 0xf1, 0xb0, 0xdf, 0x53, 0xf6, 0x75, 0xe5, 0xe6, 0xd6, 0x3a, 0xae, 0xf0, 0x20, 0xcc, + 0xb3, 0x6a, 0x3a, 0xa7, 0x7c, 0xfa, 0xc1, 0xee, 0x6d, 0x8e, 0x31, 0x89, 0xf5, 0xd3, 0x4f, 0x5f, + 0x0c, 0xe1, 0xdb, 0x57, 0xa3, 0xfe, 0x4d, 0x47, 0x5b, 0xdd, 0x19, 0xc2, 0xcf, 0x3b, 0x43, 0xf8, + 0x58, 0x18, 0x60, 0x55, 0x18, 0xe0, 0x7b, 0x61, 0x80, 0xdf, 0x85, 0x01, 0xde, 0x08, 0x53, 0xa9, + 0x0c, 0xe2, 0xe2, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc3, 0x32, 0xd2, 0x86, 0x50, 0x03, 0x00, + 0x00, +} + func (m *Process) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -121,80 +217,94 @@ func (m *Process) Marshal() (dAtA []byte, err error) { } func (m *Process) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Process) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.ID) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExitedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt):]) + if err1 != nil { + return 0, err1 } - if m.Pid != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintTask(dAtA, i, uint64(m.Pid)) - } - if m.Status != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintTask(dAtA, i, uint64(m.Status)) - } - if len(m.Stdin) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.Stdin))) - i += copy(dAtA[i:], m.Stdin) - } - if len(m.Stdout) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.Stdout))) - i += copy(dAtA[i:], m.Stdout) - } - if len(m.Stderr) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintTask(dAtA, i, uint64(len(m.Stderr))) - i += copy(dAtA[i:], m.Stderr) + i -= n1 + i = encodeVarintTask(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x52 + if m.ExitStatus != 0 { + i = encodeVarintTask(dAtA, i, uint64(m.ExitStatus)) + i-- + dAtA[i] = 0x48 } if m.Terminal { - dAtA[i] = 0x40 - i++ + i-- if m.Terminal { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x40 } - if m.ExitStatus != 0 { - dAtA[i] = 0x48 - i++ - i = encodeVarintTask(dAtA, i, uint64(m.ExitStatus)) + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = encodeVarintTask(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x3a } - dAtA[i] = 0x52 - i++ - i = encodeVarintTask(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) - n1, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) - if err != nil { - return 0, err + if len(m.Stdout) > 0 { + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) + i = encodeVarintTask(dAtA, i, uint64(len(m.Stdout))) + i-- + dAtA[i] = 0x32 + } + if len(m.Stdin) > 0 { + i -= len(m.Stdin) + copy(dAtA[i:], m.Stdin) + i = encodeVarintTask(dAtA, i, uint64(len(m.Stdin))) + i-- + dAtA[i] = 0x2a + } + if m.Status != 0 { + i = encodeVarintTask(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x20 + } + if m.Pid != 0 { + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x18 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0x12 + } + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTask(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - i += n1 - return i, nil + return len(dAtA) - i, nil } func (m *ProcessInfo) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -202,38 +312,54 @@ func (m *ProcessInfo) Marshal() (dAtA []byte, err error) { } func (m *ProcessInfo) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProcessInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Pid != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Info != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintTask(dAtA, i, uint64(m.Info.Size())) - n2, err := m.Info.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Info.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTask(dAtA, i, uint64(size)) } - i += n2 + i-- + dAtA[i] = 0x12 + } + if m.Pid != 0 { + i = encodeVarintTask(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func encodeVarintTask(dAtA []byte, offset int, v uint64) int { + offset -= sovTask(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Process) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -268,12 +394,18 @@ func (m *Process) Size() (n int) { if m.ExitStatus != 0 { n += 1 + sovTask(uint64(m.ExitStatus)) } - l = types.SizeOfStdTime(m.ExitedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt) n += 1 + l + sovTask(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ProcessInfo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Pid != 0 { @@ -283,18 +415,14 @@ func (m *ProcessInfo) Size() (n int) { l = m.Info.Size() n += 1 + l + sovTask(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovTask(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozTask(x uint64) (n int) { return sovTask(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -313,7 +441,8 @@ func (this *Process) String() string { `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, - `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf1.Timestamp", 1), `&`, ``, 1) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ExitedAt), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -324,7 +453,8 @@ func (this *ProcessInfo) String() string { } s := strings.Join([]string{`&ProcessInfo{`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, - `Info:` + strings.Replace(fmt.Sprintf("%v", this.Info), "Any", "google_protobuf2.Any", 1) + `,`, + `Info:` + strings.Replace(fmt.Sprintf("%v", this.Info), "Any", "types.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -352,7 +482,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -380,7 +510,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -390,6 +520,9 @@ func (m *Process) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -409,7 +542,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -419,6 +552,9 @@ func (m *Process) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -438,7 +574,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -457,7 +593,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Status |= (Status(b) & 0x7F) << shift + m.Status |= Status(b&0x7F) << shift if b < 0x80 { break } @@ -476,7 +612,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -486,6 +622,9 @@ func (m *Process) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -505,7 +644,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -515,6 +654,9 @@ func (m *Process) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -534,7 +676,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -544,6 +686,9 @@ func (m *Process) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -563,7 +708,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -583,7 +728,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ExitStatus |= (uint32(b) & 0x7F) << shift + m.ExitStatus |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -602,7 +747,7 @@ func (m *Process) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -611,10 +756,13 @@ func (m *Process) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -624,12 +772,13 @@ func (m *Process) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -654,7 +803,7 @@ func (m *ProcessInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -682,7 +831,7 @@ func (m *ProcessInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -701,7 +850,7 @@ func (m *ProcessInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -710,11 +859,14 @@ func (m *ProcessInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTask } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTask + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Info == nil { - m.Info = &google_protobuf2.Any{} + m.Info = &types.Any{} } if err := m.Info.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -726,12 +878,13 @@ func (m *ProcessInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -744,6 +897,7 @@ func (m *ProcessInfo) Unmarshal(dAtA []byte) error { func skipTask(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -775,10 +929,8 @@ func skipTask(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -795,96 +947,34 @@ func skipTask(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthTask } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTask - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipTask(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTask + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthTask + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthTask = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowTask = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthTask = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTask = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTask = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/api/types/task/task.proto", fileDescriptorTask) -} - -var fileDescriptorTask = []byte{ - // 545 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x3f, 0x6f, 0xd3, 0x40, - 0x18, 0xc6, 0x7d, 0x6e, 0xeb, 0xa6, 0xe7, 0xb6, 0x18, 0x13, 0x55, 0xc6, 0x20, 0xdb, 0xea, 0x64, - 0x31, 0xd8, 0x22, 0xdd, 0xd8, 0xf2, 0x4f, 0xc8, 0x42, 0x72, 0x23, 0x27, 0x11, 0x6c, 0x91, 0x13, - 0x5f, 0xcc, 0xa9, 0xcd, 0x9d, 0x65, 0x9f, 0x81, 0x6c, 0x8c, 0xa8, 0x13, 0x5f, 0xa0, 0x13, 0x7c, - 0x0a, 0x3e, 0x41, 0x46, 0x26, 0xc4, 0x14, 0xa8, 0x3f, 0x09, 0x3a, 0xdb, 0x49, 0x23, 0x60, 0x39, - 0xbd, 0xef, 0xf3, 0x7b, 0xee, 0xbd, 0xf7, 0x1e, 0xf8, 0x22, 0xc6, 0xec, 0x6d, 0x3e, 0x75, 0x66, - 0x74, 0xe1, 0xce, 0x28, 0x61, 0x21, 0x26, 0x28, 0x8d, 0x76, 0xcb, 0x30, 0xc1, 0x2e, 0x5b, 0x26, - 0x28, 0x73, 0x59, 0x98, 0x5d, 0x95, 0x87, 0x93, 0xa4, 0x94, 0x51, 0xf5, 0xd1, 0xbd, 0xcb, 0x79, - 0xf7, 0xdc, 0x29, 0x4d, 0x7a, 0x33, 0xa6, 0x31, 0x2d, 0xb9, 0xcb, 0xab, 0xca, 0xaa, 0x9b, 0x31, - 0xa5, 0xf1, 0x35, 0x72, 0xcb, 0x6e, 0x9a, 0xcf, 0x5d, 0x86, 0x17, 0x28, 0x63, 0xe1, 0x22, 0xa9, - 0x0d, 0x8f, 0xff, 0x36, 0x84, 0x64, 0x59, 0xa1, 0xf3, 0x42, 0x84, 0x87, 0x83, 0x94, 0xce, 0x50, - 0x96, 0xa9, 0x2d, 0x78, 0xbc, 0x7d, 0x74, 0x82, 0x23, 0x0d, 0x58, 0xc0, 0x3e, 0xea, 0x3c, 0x28, - 0xd6, 0xa6, 0xdc, 0xdd, 0xe8, 0x5e, 0x2f, 0x90, 0xb7, 0x26, 0x2f, 0x52, 0xcf, 0xa0, 0x88, 0x23, - 0x4d, 0x2c, 0x9d, 0x52, 0xb1, 0x36, 0x45, 0xaf, 0x17, 0x88, 0x38, 0x52, 0x15, 0xb8, 0x97, 0xe0, - 0x48, 0xdb, 0xb3, 0x80, 0x7d, 0x12, 0xf0, 0x52, 0xbd, 0x80, 0x52, 0xc6, 0x42, 0x96, 0x67, 0xda, - 0xbe, 0x05, 0xec, 0xd3, 0xd6, 0x13, 0xe7, 0x3f, 0x3f, 0x74, 0x86, 0xa5, 0x25, 0xa8, 0xad, 0x6a, - 0x13, 0x1e, 0x64, 0x2c, 0xc2, 0x44, 0x3b, 0xe0, 0x2f, 0x04, 0x55, 0xa3, 0x9e, 0xf1, 0x51, 0x11, - 0xcd, 0x99, 0x26, 0x95, 0x72, 0xdd, 0xd5, 0x3a, 0x4a, 0x53, 0xed, 0x70, 0xab, 0xa3, 0x34, 0x55, - 0x75, 0xd8, 0x60, 0x28, 0x5d, 0x60, 0x12, 0x5e, 0x6b, 0x0d, 0x0b, 0xd8, 0x8d, 0x60, 0xdb, 0xab, - 0x26, 0x94, 0xd1, 0x07, 0xcc, 0x26, 0xf5, 0x6e, 0x47, 0xe5, 0xc2, 0x90, 0x4b, 0xd5, 0x2a, 0x6a, - 0x1b, 0x1e, 0xf1, 0x0e, 0x45, 0x93, 0x90, 0x69, 0xd0, 0x02, 0xb6, 0xdc, 0xd2, 0x9d, 0x2a, 0x50, - 0x67, 0x13, 0xa8, 0x33, 0xda, 0x24, 0xde, 0x69, 0xac, 0xd6, 0xa6, 0xf0, 0xf9, 0x97, 0x09, 0x82, - 0x46, 0x75, 0xad, 0xcd, 0xce, 0x3d, 0x28, 0xd7, 0x19, 0x7b, 0x64, 0x4e, 0x37, 0xd9, 0x80, 0xfb, - 0x6c, 0x6c, 0xb8, 0x8f, 0xc9, 0x9c, 0x96, 0x39, 0xca, 0xad, 0xe6, 0x3f, 0xe3, 0xdb, 0x64, 0x19, - 0x94, 0x8e, 0x67, 0x3f, 0x00, 0x94, 0xea, 0xc5, 0x0c, 0x78, 0x38, 0xf6, 0x5f, 0xf9, 0x97, 0xaf, - 0x7d, 0x45, 0xd0, 0x1f, 0xde, 0xdc, 0x5a, 0x27, 0x15, 0x18, 0x93, 0x2b, 0x42, 0xdf, 0x13, 0xce, - 0xbb, 0x41, 0xbf, 0x3d, 0xea, 0xf7, 0x14, 0xb0, 0xcb, 0xbb, 0x29, 0x0a, 0x19, 0x8a, 0x38, 0x0f, - 0xc6, 0xbe, 0xef, 0xf9, 0x2f, 0x15, 0x71, 0x97, 0x07, 0x39, 0x21, 0x98, 0xc4, 0x9c, 0x0f, 0x47, - 0x97, 0x83, 0x41, 0xbf, 0xa7, 0xec, 0xed, 0xf2, 0x21, 0xa3, 0x49, 0x82, 0x22, 0xf5, 0x29, 0x94, - 0x06, 0xed, 0xf1, 0xb0, 0xdf, 0x53, 0xf6, 0x75, 0xe5, 0xe6, 0xd6, 0x3a, 0xae, 0xf0, 0x20, 0xcc, - 0xb3, 0x6a, 0x3a, 0xa7, 0x7c, 0xfa, 0xc1, 0xee, 0x6d, 0x8e, 0x31, 0x89, 0xf5, 0xd3, 0x4f, 0x5f, - 0x0c, 0xe1, 0xdb, 0x57, 0xa3, 0xfe, 0x4d, 0x47, 0x5b, 0xdd, 0x19, 0xc2, 0xcf, 0x3b, 0x43, 0xf8, - 0x58, 0x18, 0x60, 0x55, 0x18, 0xe0, 0x7b, 0x61, 0x80, 0xdf, 0x85, 0x01, 0xde, 0x08, 0x53, 0xa9, - 0x0c, 0xe2, 0xe2, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc3, 0x32, 0xd2, 0x86, 0x50, 0x03, 0x00, - 0x00, -} diff --git a/vendor/github.com/containerd/containerd/api/types/task/task.proto b/vendor/github.com/containerd/containerd/api/types/task/task.proto index da91cb033efb5..df08dfd99d943 100644 --- a/vendor/github.com/containerd/containerd/api/types/task/task.proto +++ b/vendor/github.com/containerd/containerd/api/types/task/task.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.v1.types; diff --git a/vendor/github.com/containerd/containerd/archive/compression/compression.go b/vendor/github.com/containerd/containerd/archive/compression/compression.go index 60c80e98a5954..a883e4d58cef5 100644 --- a/vendor/github.com/containerd/containerd/archive/compression/compression.go +++ b/vendor/github.com/containerd/containerd/archive/compression/compression.go @@ -29,6 +29,7 @@ import ( "sync" "github.com/containerd/containerd/log" + "github.com/klauspost/compress/zstd" ) type ( @@ -41,6 +42,8 @@ const ( Uncompressed Compression = iota // Gzip is gzip compression algorithm. Gzip + // Zstd is zstd compression algorithm. + Zstd ) const disablePigzEnv = "CONTAINERD_DISABLE_PIGZ" @@ -126,6 +129,7 @@ func (r *bufferedReader) Peek(n int) ([]byte, error) { func DetectCompression(source []byte) Compression { for compression, m := range map[Compression][]byte{ Gzip: {0x1F, 0x8B, 0x08}, + Zstd: {0x28, 0xb5, 0x2f, 0xfd}, } { if len(source) < len(m) { // Len too short @@ -174,19 +178,34 @@ func DecompressStream(archive io.Reader) (DecompressReadCloser, error) { return gzReader.Close() }, }, nil + case Zstd: + zstdReader, err := zstd.NewReader(buf) + if err != nil { + return nil, err + } + return &readCloserWrapper{ + Reader: zstdReader, + compression: compression, + closer: func() error { + zstdReader.Close() + return nil + }, + }, nil default: return nil, fmt.Errorf("unsupported compression format %s", (&compression).Extension()) } } -// CompressStream compresseses the dest with specified compression algorithm. +// CompressStream compresses the dest with specified compression algorithm. func CompressStream(dest io.Writer, compression Compression) (io.WriteCloser, error) { switch compression { case Uncompressed: return &writeCloserWrapper{dest, nil}, nil case Gzip: return gzip.NewWriter(dest), nil + case Zstd: + return zstd.NewWriter(dest) default: return nil, fmt.Errorf("unsupported compression format %s", (&compression).Extension()) } @@ -197,6 +216,8 @@ func (compression *Compression) Extension() string { switch *compression { case Gzip: return "gz" + case Zstd: + return "zst" } return "" } diff --git a/vendor/github.com/containerd/containerd/archive/strconv.go b/vendor/github.com/containerd/containerd/archive/strconv.go deleted file mode 100644 index 13746e4b64db1..0000000000000 --- a/vendor/github.com/containerd/containerd/archive/strconv.go +++ /dev/null @@ -1,68 +0,0 @@ -// +build windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package archive - -import ( - "strconv" - "strings" - "time" - - "archive/tar" -) - -// Forked from https://github.com/golang/go/blob/master/src/archive/tar/strconv.go -// as archive/tar doesn't support CreationTime, but does handle PAX time parsing, -// and there's no need to re-invent the wheel. - -// parsePAXTime takes a string of the form %d.%d as described in the PAX -// specification. Note that this implementation allows for negative timestamps, -// which is allowed for by the PAX specification, but not always portable. -func parsePAXTime(s string) (time.Time, error) { - const maxNanoSecondDigits = 9 - - // Split string into seconds and sub-seconds parts. - ss, sn := s, "" - if pos := strings.IndexByte(s, '.'); pos >= 0 { - ss, sn = s[:pos], s[pos+1:] - } - - // Parse the seconds. - secs, err := strconv.ParseInt(ss, 10, 64) - if err != nil { - return time.Time{}, tar.ErrHeader - } - if len(sn) == 0 { - return time.Unix(secs, 0), nil // No sub-second values - } - - // Parse the nanoseconds. - if strings.Trim(sn, "0123456789") != "" { - return time.Time{}, tar.ErrHeader - } - if len(sn) < maxNanoSecondDigits { - sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad - } else { - sn = sn[:maxNanoSecondDigits] // Right truncate - } - nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed - if len(ss) > 0 && ss[0] == '-' { - return time.Unix(secs, -nsecs), nil // Negative correction - } - return time.Unix(secs, nsecs), nil -} diff --git a/vendor/github.com/containerd/containerd/archive/tar.go b/vendor/github.com/containerd/containerd/archive/tar.go index ac04e3e023826..78a287317f0c9 100644 --- a/vendor/github.com/containerd/containerd/archive/tar.go +++ b/vendor/github.com/containerd/containerd/archive/tar.go @@ -19,9 +19,7 @@ package archive import ( "archive/tar" "context" - "fmt" "io" - "io/ioutil" "os" "path/filepath" "runtime" @@ -65,13 +63,34 @@ func Diff(ctx context.Context, a, b string) io.ReadCloser { } // WriteDiff writes a tar stream of the computed difference between the -// provided directories. +// provided paths. // // Produces a tar using OCI style file markers for deletions. Deleted // files will be prepended with the prefix ".wh.". This style is // based off AUFS whiteouts. // See https://github.com/opencontainers/image-spec/blob/master/layer.md -func WriteDiff(ctx context.Context, w io.Writer, a, b string) error { +func WriteDiff(ctx context.Context, w io.Writer, a, b string, opts ...WriteDiffOpt) error { + var options WriteDiffOptions + for _, opt := range opts { + if err := opt(&options); err != nil { + return errors.Wrap(err, "failed to apply option") + } + } + if options.writeDiffFunc == nil { + options.writeDiffFunc = writeDiffNaive + } + + return options.writeDiffFunc(ctx, w, a, b, options) +} + +// writeDiffNaive writes a tar stream of the computed difference between the +// provided directories on disk. +// +// Produces a tar using OCI style file markers for deletions. Deleted +// files will be prepended with the prefix ".wh.". This style is +// based off AUFS whiteouts. +// See https://github.com/opencontainers/image-spec/blob/master/layer.md +func writeDiffNaive(ctx context.Context, w io.Writer, a, b string, _ WriteDiffOptions) error { cw := newChangeWriter(w, b) err := fs.Changes(ctx, a, b, cw.HandleChange) if err != nil { @@ -91,16 +110,11 @@ const ( // archives. whiteoutMetaPrefix = whiteoutPrefix + whiteoutPrefix - // whiteoutLinkDir is a directory AUFS uses for storing hardlink links to other - // layers. Normally these should not go into exported archives and all changed - // hardlinks should be copied to the top layer. - whiteoutLinkDir = whiteoutMetaPrefix + "plnk" - // whiteoutOpaqueDir file means directory has been made opaque - meaning // readdir calls to this directory do not follow to lower layers. whiteoutOpaqueDir = whiteoutMetaPrefix + ".opq" - paxSchilyXattr = "SCHILY.xattrs." + paxSchilyXattr = "SCHILY.xattr." ) // Apply applies a tar stream of an OCI style diff tar. @@ -117,25 +131,69 @@ func Apply(ctx context.Context, root string, r io.Reader, opts ...ApplyOpt) (int if options.Filter == nil { options.Filter = all } + if options.applyFunc == nil { + options.applyFunc = applyNaive + } - return apply(ctx, root, tar.NewReader(r), options) + return options.applyFunc(ctx, root, r, options) } -// applyNaive applies a tar stream of an OCI style diff tar. +// applyNaive applies a tar stream of an OCI style diff tar to a directory +// applying each file as either a whole file or whiteout. // See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets -func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { +func applyNaive(ctx context.Context, root string, r io.Reader, options ApplyOptions) (size int64, err error) { var ( dirs []*tar.Header + tr = tar.NewReader(r) + // Used for handling opaque directory markers which // may occur out of order unpackedPaths = make(map[string]struct{}) - // Used for aufs plink directory - aufsTempdir = "" - aufsHardlinks = make(map[string]*tar.Header) + convertWhiteout = options.ConvertWhiteout ) + if convertWhiteout == nil { + // handle whiteouts by removing the target files + convertWhiteout = func(hdr *tar.Header, path string) (bool, error) { + base := filepath.Base(path) + dir := filepath.Dir(path) + if base == whiteoutOpaqueDir { + _, err := os.Lstat(dir) + if err != nil { + return false, err + } + err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + if os.IsNotExist(err) { + err = nil // parent was deleted + } + return err + } + if path == dir { + return nil + } + if _, exists := unpackedPaths[path]; !exists { + err := os.RemoveAll(path) + return err + } + return nil + }) + return false, err + } + + if strings.HasPrefix(base, whiteoutPrefix) { + originalBase := base[len(whiteoutPrefix):] + originalPath := filepath.Join(dir, originalBase) + + return false, os.RemoveAll(originalPath) + } + + return true, nil + } + } + // Iterate through the files in the archive. for { select { @@ -193,85 +251,21 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO if base == "" { parentPath = filepath.Dir(path) } - if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) { - err = mkdirAll(parentPath, 0700) - if err != nil { - return 0, err - } + if err := mkparent(ctx, parentPath, root, options.Parents); err != nil { + return 0, err } } - // Skip AUFS metadata dirs - if strings.HasPrefix(hdr.Name, whiteoutMetaPrefix) { - // Regular files inside /.wh..wh.plnk can be used as hardlink targets - // We don't want this directory, but we need the files in them so that - // such hardlinks can be resolved. - if strings.HasPrefix(hdr.Name, whiteoutLinkDir) && hdr.Typeflag == tar.TypeReg { - basename := filepath.Base(hdr.Name) - aufsHardlinks[basename] = hdr - if aufsTempdir == "" { - if aufsTempdir, err = ioutil.TempDir(os.Getenv("XDG_RUNTIME_DIR"), "dockerplnk"); err != nil { - return 0, err - } - defer os.RemoveAll(aufsTempdir) - } - p, err := fs.RootPath(aufsTempdir, basename) - if err != nil { - return 0, err - } - if err := createTarFile(ctx, p, root, hdr, tr); err != nil { - return 0, err - } - } - - if hdr.Name != whiteoutOpaqueDir { - continue - } + // Naive whiteout convert function which handles whiteout files by + // removing the target files. + if err := validateWhiteout(path); err != nil { + return 0, err } - - if strings.HasPrefix(base, whiteoutPrefix) { - dir := filepath.Dir(path) - if base == whiteoutOpaqueDir { - _, err := os.Lstat(dir) - if err != nil { - return 0, err - } - err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - if os.IsNotExist(err) { - err = nil // parent was deleted - } - return err - } - if path == dir { - return nil - } - if _, exists := unpackedPaths[path]; !exists { - err := os.RemoveAll(path) - return err - } - return nil - }) - if err != nil { - return 0, err - } - continue - } - - originalBase := base[len(whiteoutPrefix):] - originalPath := filepath.Join(dir, originalBase) - - // Ensure originalPath is under dir - if dir[len(dir)-1] != filepath.Separator { - dir += string(filepath.Separator) - } - if !strings.HasPrefix(originalPath, dir) { - return 0, errors.Wrapf(errInvalidArchive, "invalid whiteout name: %v", base) - } - - if err := os.RemoveAll(originalPath); err != nil { - return 0, err - } + writeFile, err := convertWhiteout(hdr, path) + if err != nil { + return 0, errors.Wrapf(err, "failed to convert whiteout file %q", hdr.Name) + } + if !writeFile { continue } // If path exits we almost always just want to remove and replace it. @@ -289,26 +283,6 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO srcData := io.Reader(tr) srcHdr := hdr - // Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so - // we manually retarget these into the temporary files we extracted them into - if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), whiteoutLinkDir) { - linkBasename := filepath.Base(hdr.Linkname) - srcHdr = aufsHardlinks[linkBasename] - if srcHdr == nil { - return 0, fmt.Errorf("Invalid aufs hardlink") - } - p, err := fs.RootPath(aufsTempdir, linkBasename) - if err != nil { - return 0, err - } - tmpFile, err := os.Open(p) - if err != nil { - return 0, err - } - defer tmpFile.Close() - srcData = tmpFile - } - if err := createTarFile(ctx, path, root, srcHdr, srcData); err != nil { return 0, err } @@ -410,7 +384,7 @@ func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header if strings.HasPrefix(key, paxSchilyXattr) { key = key[len(paxSchilyXattr):] if err := setxattr(path, key, value); err != nil { - if errors.Cause(err) == syscall.ENOTSUP { + if errors.Is(err, syscall.ENOTSUP) { log.G(ctx).WithError(err).Warnf("ignored xattr %s in archive", key) continue } @@ -419,15 +393,74 @@ func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header } } - // There is no LChmod, so ignore mode for symlink. Also, this - // must happen after chown, as that can modify the file mode - if err := handleLChmod(hdr, path, hdrInfo); err != nil { + // call lchmod after lchown since lchown can modify the file mode + if err := lchmod(path, hdrInfo.Mode()); err != nil { return err } return chtimes(path, boundTime(latestTime(hdr.AccessTime, hdr.ModTime)), boundTime(hdr.ModTime)) } +func mkparent(ctx context.Context, path, root string, parents []string) error { + if dir, err := os.Lstat(path); err == nil { + if dir.IsDir() { + return nil + } + return &os.PathError{ + Op: "mkparent", + Path: path, + Err: syscall.ENOTDIR, + } + } else if !os.IsNotExist(err) { + return err + } + + i := len(path) + for i > len(root) && !os.IsPathSeparator(path[i-1]) { + i-- + } + + if i > len(root)+1 { + if err := mkparent(ctx, path[:i-1], root, parents); err != nil { + return err + } + } + + if err := mkdir(path, 0755); err != nil { + // Check that still doesn't exist + dir, err1 := os.Lstat(path) + if err1 == nil && dir.IsDir() { + return nil + } + return err + } + + for _, p := range parents { + ppath, err := fs.RootPath(p, path[len(root):]) + if err != nil { + return err + } + + dir, err := os.Lstat(ppath) + if err == nil { + if !dir.IsDir() { + // Replaced, do not copy attributes + break + } + if err := copyDirInfo(dir, path); err != nil { + return err + } + return copyUpXAttrs(path, ppath) + } else if !os.IsNotExist(err) { + return err + } + } + + log.G(ctx).Debugf("parent directory %q not found: default permissions(0755) used", path) + + return nil +} + type changeWriter struct { tw *tar.Writer source string @@ -493,6 +526,12 @@ func (cw *changeWriter) HandleChange(k fs.ChangeKind, p string, f os.FileInfo, e hdr.Mode = int64(chmodTarEntry(os.FileMode(hdr.Mode))) + // truncate timestamp for compatibility. without PAX stdlib rounds timestamps instead + hdr.Format = tar.FormatPAX + hdr.ModTime = hdr.ModTime.Truncate(time.Second) + hdr.AccessTime = time.Time{} + hdr.ChangeTime = time.Time{} + name := p if strings.HasPrefix(name, string(filepath.Separator)) { name, err = filepath.Rel(string(filepath.Separator), name) @@ -598,6 +637,9 @@ func (cw *changeWriter) Close() error { } func (cw *changeWriter) includeParents(hdr *tar.Header) error { + if cw.addedDirs == nil { + return nil + } name := strings.TrimRight(hdr.Name, "/") fname := filepath.Join(cw.source, name) parent := filepath.Dir(name) @@ -684,3 +726,26 @@ func hardlinkRootPath(root, linkname string) (string, error) { } return targetPath, nil } + +func validateWhiteout(path string) error { + base := filepath.Base(path) + dir := filepath.Dir(path) + + if base == whiteoutOpaqueDir { + return nil + } + + if strings.HasPrefix(base, whiteoutPrefix) { + originalBase := base[len(whiteoutPrefix):] + originalPath := filepath.Join(dir, originalBase) + + // Ensure originalPath is under dir + if dir[len(dir)-1] != filepath.Separator { + dir += string(filepath.Separator) + } + if !strings.HasPrefix(originalPath, dir) { + return errors.Wrapf(errInvalidArchive, "invalid whiteout name: %v", base) + } + } + return nil +} diff --git a/vendor/github.com/containerd/containerd/archive/tar_freebsd.go b/vendor/github.com/containerd/containerd/archive/tar_freebsd.go new file mode 100644 index 0000000000000..ce4dffd6dd9f4 --- /dev/null +++ b/vendor/github.com/containerd/containerd/archive/tar_freebsd.go @@ -0,0 +1,48 @@ +// +build freebsd + +/* + Copyright The containerd Authors. + + 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. +*/ + +package archive + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// mknod wraps unix.Mknod. FreeBSD's unix.Mknod signature is different from +// other Unix and Unix-like operating systems. +func mknod(path string, mode uint32, dev uint64) error { + return unix.Mknod(path, mode, dev) +} + +// lsetxattrCreate wraps unix.Lsetxattr with FreeBSD-specific flags and errors +func lsetxattrCreate(link string, attr string, data []byte) error { + err := unix.Lsetxattr(link, attr, data, 0) + if err == unix.ENOTSUP || err == unix.EEXIST { + return nil + } + return err +} + +func lchmod(path string, mode os.FileMode) error { + err := unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), unix.AT_SYMLINK_NOFOLLOW) + if err != nil { + err = &os.PathError{Op: "lchmod", Path: path, Err: err} + } + return err +} diff --git a/vendor/github.com/containerd/containerd/archive/tar_mostunix.go b/vendor/github.com/containerd/containerd/archive/tar_mostunix.go new file mode 100644 index 0000000000000..9cd1f0fa8f845 --- /dev/null +++ b/vendor/github.com/containerd/containerd/archive/tar_mostunix.go @@ -0,0 +1,55 @@ +// +build !windows,!freebsd + +/* + Copyright The containerd Authors. + + 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. +*/ + +package archive + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// mknod wraps Unix.Mknod and casts dev to int +func mknod(path string, mode uint32, dev uint64) error { + return unix.Mknod(path, mode, int(dev)) +} + +// lsetxattrCreate wraps unix.Lsetxattr, passes the unix.XATTR_CREATE flag on +// supported operating systems,and ignores appropriate errors +func lsetxattrCreate(link string, attr string, data []byte) error { + err := unix.Lsetxattr(link, attr, data, unix.XATTR_CREATE) + if err == unix.ENOTSUP || err == unix.ENODATA || err == unix.EEXIST { + return nil + } + return err +} + +// lchmod checks for symlink and changes the mode if not a symlink +func lchmod(path string, mode os.FileMode) error { + fi, err := os.Lstat(path) + if err != nil { + return err + } + + if fi.Mode()&os.ModeSymlink == 0 { + if err := os.Chmod(path, mode); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/containerd/containerd/archive/tar_opts.go b/vendor/github.com/containerd/containerd/archive/tar_opts.go index a08bc102a7fe4..58985555a50a6 100644 --- a/vendor/github.com/containerd/containerd/archive/tar_opts.go +++ b/vendor/github.com/containerd/containerd/archive/tar_opts.go @@ -16,7 +16,20 @@ package archive -import "archive/tar" +import ( + "archive/tar" + "context" + "io" +) + +// ApplyOptions provides additional options for an Apply operation +type ApplyOptions struct { + Filter Filter // Filter tar headers + ConvertWhiteout ConvertWhiteout // Convert whiteout files + Parents []string // Parent directories to handle inherited attributes without CoW + + applyFunc func(context.Context, string, io.Reader, ApplyOptions) (int64, error) +} // ApplyOpt allows setting mutable archive apply properties on creation type ApplyOpt func(options *ApplyOptions) error @@ -24,6 +37,9 @@ type ApplyOpt func(options *ApplyOptions) error // Filter specific files from the archive type Filter func(*tar.Header) (bool, error) +// ConvertWhiteout converts whiteout files from the archive +type ConvertWhiteout func(*tar.Header, string) (bool, error) + // all allows all files func all(_ *tar.Header) (bool, error) { return true, nil @@ -36,3 +52,34 @@ func WithFilter(f Filter) ApplyOpt { return nil } } + +// WithConvertWhiteout uses the convert function to convert the whiteout files. +func WithConvertWhiteout(c ConvertWhiteout) ApplyOpt { + return func(options *ApplyOptions) error { + options.ConvertWhiteout = c + return nil + } +} + +// WithParents provides parent directories for resolving inherited attributes +// directory from the filesystem. +// Inherited attributes are searched from first to last, making the first +// element in the list the most immediate parent directory. +// NOTE: When applying to a filesystem which supports CoW, file attributes +// should be inherited by the filesystem. +func WithParents(p []string) ApplyOpt { + return func(options *ApplyOptions) error { + options.Parents = p + return nil + } +} + +// WriteDiffOptions provides additional options for a WriteDiff operation +type WriteDiffOptions struct { + ParentLayers []string // Windows needs the full list of parent layers + + writeDiffFunc func(context.Context, io.Writer, string, string, WriteDiffOptions) error +} + +// WriteDiffOpt allows setting mutable archive write properties on creation +type WriteDiffOpt func(options *WriteDiffOptions) error diff --git a/vendor/github.com/containerd/containerd/archive/tar_opts_linux.go b/vendor/github.com/containerd/containerd/archive/tar_opts_linux.go new file mode 100644 index 0000000000000..38ef9e9bc7893 --- /dev/null +++ b/vendor/github.com/containerd/containerd/archive/tar_opts_linux.go @@ -0,0 +1,59 @@ +// +build linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package archive + +import ( + "archive/tar" + "os" + "path/filepath" + "strings" + + "golang.org/x/sys/unix" +) + +// AufsConvertWhiteout converts whiteout files for aufs. +func AufsConvertWhiteout(_ *tar.Header, _ string) (bool, error) { + return true, nil +} + +// OverlayConvertWhiteout converts whiteout files for overlay. +func OverlayConvertWhiteout(hdr *tar.Header, path string) (bool, error) { + base := filepath.Base(path) + dir := filepath.Dir(path) + + // if a directory is marked as opaque, we need to translate that to overlay + if base == whiteoutOpaqueDir { + // don't write the file itself + return false, unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0) + } + + // if a file was deleted and we are using overlay, we need to create a character device + if strings.HasPrefix(base, whiteoutPrefix) { + originalBase := base[len(whiteoutPrefix):] + originalPath := filepath.Join(dir, originalBase) + + if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil { + return false, err + } + // don't write the file itself + return false, os.Chown(originalPath, hdr.Uid, hdr.Gid) + } + + return true, nil +} diff --git a/vendor/github.com/containerd/containerd/archive/tar_opts_unix.go b/vendor/github.com/containerd/containerd/archive/tar_opts_unix.go deleted file mode 100644 index 173826967945b..0000000000000 --- a/vendor/github.com/containerd/containerd/archive/tar_opts_unix.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package archive - -// ApplyOptions provides additional options for an Apply operation -type ApplyOptions struct { - Filter Filter // Filter tar headers -} diff --git a/vendor/github.com/containerd/containerd/archive/tar_opts_windows.go b/vendor/github.com/containerd/containerd/archive/tar_opts_windows.go index e4b15a1634b5c..d3b8f4fb93e1f 100644 --- a/vendor/github.com/containerd/containerd/archive/tar_opts_windows.go +++ b/vendor/github.com/containerd/containerd/archive/tar_opts_windows.go @@ -18,20 +18,17 @@ package archive -// ApplyOptions provides additional options for an Apply operation -type ApplyOptions struct { - ParentLayerPaths []string // Parent layer paths used for Windows layer apply - IsWindowsContainerLayer bool // True if the tar stream to be applied is a Windows Container Layer - Filter Filter // Filter tar headers -} +import ( + "context" + "io" -// WithParentLayers adds parent layers to the apply process this is required -// for all Windows layers except the base layer. -func WithParentLayers(parentPaths []string) ApplyOpt { - return func(options *ApplyOptions) error { - options.ParentLayerPaths = parentPaths - return nil - } + "github.com/Microsoft/hcsshim/pkg/ociwclayer" +) + +// applyWindowsLayer applies a tar stream of an OCI style diff tar of a Windows layer +// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets +func applyWindowsLayer(ctx context.Context, root string, r io.Reader, options ApplyOptions) (size int64, err error) { + return ociwclayer.ImportLayerFromTar(ctx, r, root, options.Parents) } // AsWindowsContainerLayer indicates that the tar stream to apply is that of @@ -39,7 +36,37 @@ func WithParentLayers(parentPaths []string) ApplyOpt { // SeRestorePrivilege. func AsWindowsContainerLayer() ApplyOpt { return func(options *ApplyOptions) error { - options.IsWindowsContainerLayer = true + options.applyFunc = applyWindowsLayer + return nil + } +} + +// writeDiffWindowsLayers writes a tar stream of the computed difference between the +// provided Windows layers +// +// Produces a tar using OCI style file markers for deletions. Deleted +// files will be prepended with the prefix ".wh.". This style is +// based off AUFS whiteouts. +// See https://github.com/opencontainers/image-spec/blob/master/layer.md +func writeDiffWindowsLayers(ctx context.Context, w io.Writer, _, layer string, options WriteDiffOptions) error { + return ociwclayer.ExportLayerToTar(ctx, w, layer, options.ParentLayers) +} + +// AsWindowsContainerLayerPair indicates that the paths to diff are a pair of +// Windows Container Layers. The caller must be holding SeBackupPrivilege. +func AsWindowsContainerLayerPair() WriteDiffOpt { + return func(options *WriteDiffOptions) error { + options.writeDiffFunc = writeDiffWindowsLayers + return nil + } +} + +// WithParentLayers provides the Windows Container Layers that are the parents +// of the target (right-hand, "upper") layer, if any. The source (left-hand, "lower") +// layer passed to WriteDiff should be "" in this case. +func WithParentLayers(p []string) WriteDiffOpt { + return func(options *WriteDiffOptions) error { + options.ParentLayers = p return nil } } diff --git a/vendor/github.com/containerd/containerd/archive/tar_unix.go b/vendor/github.com/containerd/containerd/archive/tar_unix.go index 022dd6d4f4946..cd2be74a0838f 100644 --- a/vendor/github.com/containerd/containerd/archive/tar_unix.go +++ b/vendor/github.com/containerd/containerd/archive/tar_unix.go @@ -20,13 +20,13 @@ package archive import ( "archive/tar" - "context" "os" - "sync" + "strings" "syscall" + "github.com/containerd/containerd/pkg/userns" + "github.com/containerd/continuity/fs" "github.com/containerd/continuity/sysx" - "github.com/opencontainers/runc/libcontainer/system" "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -74,10 +74,6 @@ func openFile(name string, flag int, perm os.FileMode) (*os.File, error) { return f, err } -func mkdirAll(path string, perm os.FileMode) error { - return os.MkdirAll(path, perm) -} - func mkdir(path string, perm os.FileMode) error { if err := os.Mkdir(path, perm); err != nil { return err @@ -87,21 +83,11 @@ func mkdir(path string, perm os.FileMode) error { return os.Chmod(path, perm) } -var ( - inUserNS bool - nsOnce sync.Once -) - -func setInUserNS() { - inUserNS = system.RunningInUserNS() -} - func skipFile(hdr *tar.Header) bool { switch hdr.Typeflag { case tar.TypeBlock, tar.TypeChar: // cannot create a device if running in user namespace - nsOnce.Do(setInUserNS) - return inUserNS + return userns.RunningInUserNS() default: return false } @@ -122,22 +108,7 @@ func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { mode |= unix.S_IFIFO } - return unix.Mknod(path, mode, int(unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor)))) -} - -func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error { - if hdr.Typeflag == tar.TypeLink { - if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { - if err := os.Chmod(path, hdrInfo.Mode()); err != nil { - return err - } - } - } else if hdr.Typeflag != tar.TypeSymlink { - if err := os.Chmod(path, hdrInfo.Mode()); err != nil { - return err - } - } - return nil + return mknod(path, mode, unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor))) } func getxattr(path, attr string) ([]byte, error) { @@ -149,11 +120,71 @@ func getxattr(path, attr string) ([]byte, error) { } func setxattr(path, key, value string) error { - return sysx.LSetxattr(path, key, []byte(value), 0) + // Do not set trusted attributes + if strings.HasPrefix(key, "trusted.") { + return errors.Wrap(unix.ENOTSUP, "admin attributes from archive not supported") + } + return unix.Lsetxattr(path, key, []byte(value), 0) } -// apply applies a tar stream of an OCI style diff tar. -// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets -func apply(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { - return applyNaive(ctx, root, tr, options) +func copyDirInfo(fi os.FileInfo, path string) error { + st := fi.Sys().(*syscall.Stat_t) + if err := os.Lchown(path, int(st.Uid), int(st.Gid)); err != nil { + if os.IsPermission(err) { + // Normally if uid/gid are the same this would be a no-op, but some + // filesystems may still return EPERM... for instance NFS does this. + // In such a case, this is not an error. + if dstStat, err2 := os.Lstat(path); err2 == nil { + st2 := dstStat.Sys().(*syscall.Stat_t) + if st.Uid == st2.Uid && st.Gid == st2.Gid { + err = nil + } + } + } + if err != nil { + return errors.Wrapf(err, "failed to chown %s", path) + } + } + + if err := os.Chmod(path, fi.Mode()); err != nil { + return errors.Wrapf(err, "failed to chmod %s", path) + } + + timespec := []unix.Timespec{ + unix.NsecToTimespec(syscall.TimespecToNsec(fs.StatAtime(st))), + unix.NsecToTimespec(syscall.TimespecToNsec(fs.StatMtime(st))), + } + if err := unix.UtimesNanoAt(unix.AT_FDCWD, path, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil { + return errors.Wrapf(err, "failed to utime %s", path) + } + + return nil +} + +func copyUpXAttrs(dst, src string) error { + xattrKeys, err := sysx.LListxattr(src) + if err != nil { + if err == unix.ENOTSUP || err == sysx.ENODATA { + return nil + } + return errors.Wrapf(err, "failed to list xattrs on %s", src) + } + for _, xattr := range xattrKeys { + // Do not copy up trusted attributes + if strings.HasPrefix(xattr, "trusted.") { + continue + } + data, err := sysx.LGetxattr(src, xattr) + if err != nil { + if err == unix.ENOTSUP || err == sysx.ENODATA { + continue + } + return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) + } + if err := lsetxattrCreate(dst, xattr, data); err != nil { + return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) + } + } + + return nil } diff --git a/vendor/github.com/containerd/containerd/archive/tar_windows.go b/vendor/github.com/containerd/containerd/archive/tar_windows.go index a3f585ac814b4..3184070ac5e60 100644 --- a/vendor/github.com/containerd/containerd/archive/tar_windows.go +++ b/vendor/github.com/containerd/containerd/archive/tar_windows.go @@ -20,49 +20,12 @@ package archive import ( "archive/tar" - "bufio" - "context" - "encoding/base64" - "errors" "fmt" - "io" "os" - "path" - "path/filepath" - "strconv" "strings" - "syscall" - "github.com/Microsoft/go-winio" - "github.com/Microsoft/hcsshim" "github.com/containerd/containerd/sys" -) - -const ( - // MSWINDOWS pax vendor extensions - hdrMSWindowsPrefix = "MSWINDOWS." - - hdrFileAttributes = hdrMSWindowsPrefix + "fileattr" - hdrSecurityDescriptor = hdrMSWindowsPrefix + "sd" - hdrRawSecurityDescriptor = hdrMSWindowsPrefix + "rawsd" - hdrMountPoint = hdrMSWindowsPrefix + "mountpoint" - hdrEaPrefix = hdrMSWindowsPrefix + "xattr." - - // LIBARCHIVE pax vendor extensions - hdrLibArchivePrefix = "LIBARCHIVE." - - hdrCreateTime = hdrLibArchivePrefix + "creationtime" -) - -var ( - // mutatedFiles is a list of files that are mutated by the import process - // and must be backed up and restored. - mutatedFiles = map[string]string{ - "UtilityVM/Files/EFI/Microsoft/Boot/BCD": "bcd.bak", - "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG": "bcd.log.bak", - "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG1": "bcd.log1.bak", - "UtilityVM/Files/EFI/Microsoft/Boot/BCD.LOG2": "bcd.log2.bak", - } + "github.com/pkg/errors" ) // tarName returns platform-specific filepath @@ -74,7 +37,7 @@ func tarName(p string) (string, error) { // in file names, it is mostly safe to replace however we must // check just in case if strings.Contains(p, "/") { - return "", fmt.Errorf("Windows path contains forward slash: %s", p) + return "", fmt.Errorf("windows path contains forward slash: %s", p) } return strings.Replace(p, string(os.PathSeparator), "/", -1), nil @@ -107,10 +70,6 @@ func openFile(name string, flag int, perm os.FileMode) (*os.File, error) { return sys.OpenFileSequential(name, flag, perm) } -func mkdirAll(path string, perm os.FileMode) error { - return sys.MkdirAll(path, perm) -} - func mkdir(path string, perm os.FileMode) error { return os.Mkdir(path, perm) } @@ -130,11 +89,7 @@ func skipFile(hdr *tar.Header) bool { // specific or Linux-specific, this warning should be changed to an error // to cater for the situation where someone does manage to upload a Linux // image but have it tagged as Windows inadvertently. - if strings.Contains(hdr.Name, ":") { - return true - } - - return false + return strings.Contains(hdr.Name, ":") } // handleTarTypeBlockCharFifo is an OS-specific helper function used by @@ -143,7 +98,7 @@ func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { return nil } -func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error { +func lchmod(path string, mode os.FileMode) error { return nil } @@ -157,293 +112,13 @@ func setxattr(path, key, value string) error { return errors.New("xattrs not supported on Windows") } -// apply applies a tar stream of an OCI style diff tar of a Windows layer. -// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets -func apply(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { - if options.IsWindowsContainerLayer { - return applyWindowsLayer(ctx, root, tr, options) - } - return applyNaive(ctx, root, tr, options) -} - -// applyWindowsLayer applies a tar stream of an OCI style diff tar of a Windows layer. -// See https://github.com/opencontainers/image-spec/blob/master/layer.md#applying-changesets -func applyWindowsLayer(ctx context.Context, root string, tr *tar.Reader, options ApplyOptions) (size int64, err error) { - home, id := filepath.Split(root) - info := hcsshim.DriverInfo{ - HomeDir: home, - } - - w, err := hcsshim.NewLayerWriter(info, id, options.ParentLayerPaths) - if err != nil { - return 0, err - } - defer func() { - if err2 := w.Close(); err2 != nil { - // This error should not be discarded as a failure here - // could result in an invalid layer on disk - if err == nil { - err = err2 - } - } - }() - - buf := bufio.NewWriter(nil) - hdr, nextErr := tr.Next() - // Iterate through the files in the archive. - for { - select { - case <-ctx.Done(): - return 0, ctx.Err() - default: - } - - if nextErr == io.EOF { - // end of tar archive - break - } - if nextErr != nil { - return 0, nextErr - } - - // Note: path is used instead of filepath to prevent OS specific handling - // of the tar path - base := path.Base(hdr.Name) - if strings.HasPrefix(base, whiteoutPrefix) { - dir := path.Dir(hdr.Name) - originalBase := base[len(whiteoutPrefix):] - originalPath := path.Join(dir, originalBase) - if err := w.Remove(filepath.FromSlash(originalPath)); err != nil { - return 0, err - } - hdr, nextErr = tr.Next() - } else if hdr.Typeflag == tar.TypeLink { - err := w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname)) - if err != nil { - return 0, err - } - hdr, nextErr = tr.Next() - } else { - name, fileSize, fileInfo, err := fileInfoFromHeader(hdr) - if err != nil { - return 0, err - } - if err := w.Add(filepath.FromSlash(name), fileInfo); err != nil { - return 0, err - } - size += fileSize - hdr, nextErr = tarToBackupStreamWithMutatedFiles(buf, w, tr, hdr, root) - } - } - - return -} - -// fileInfoFromHeader retrieves basic Win32 file information from a tar header, using the additional metadata written by -// WriteTarFileFromBackupStream. -func fileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *winio.FileBasicInfo, err error) { - name = hdr.Name - if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA { - size = hdr.Size - } - fileInfo = &winio.FileBasicInfo{ - LastAccessTime: syscall.NsecToFiletime(hdr.AccessTime.UnixNano()), - LastWriteTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()), - ChangeTime: syscall.NsecToFiletime(hdr.ChangeTime.UnixNano()), - - // Default CreationTime to ModTime, updated below if MSWINDOWS.createtime exists - CreationTime: syscall.NsecToFiletime(hdr.ModTime.UnixNano()), - } - if attrStr, ok := hdr.PAXRecords[hdrFileAttributes]; ok { - attr, err := strconv.ParseUint(attrStr, 10, 32) - if err != nil { - return "", 0, nil, err - } - fileInfo.FileAttributes = uint32(attr) - } else { - if hdr.Typeflag == tar.TypeDir { - fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY - } - } - if createStr, ok := hdr.PAXRecords[hdrCreateTime]; ok { - createTime, err := parsePAXTime(createStr) - if err != nil { - return "", 0, nil, err - } - fileInfo.CreationTime = syscall.NsecToFiletime(createTime.UnixNano()) - } - return -} - -// tarToBackupStreamWithMutatedFiles reads data from a tar stream and -// writes it to a backup stream, and also saves any files that will be mutated -// by the import layer process to a backup location. -func tarToBackupStreamWithMutatedFiles(buf *bufio.Writer, w io.Writer, t *tar.Reader, hdr *tar.Header, root string) (nextHdr *tar.Header, err error) { - var ( - bcdBackup *os.File - bcdBackupWriter *winio.BackupFileWriter - ) - if backupPath, ok := mutatedFiles[hdr.Name]; ok { - bcdBackup, err = os.Create(filepath.Join(root, backupPath)) - if err != nil { - return nil, err - } - defer func() { - cerr := bcdBackup.Close() - if err == nil { - err = cerr - } - }() - - bcdBackupWriter = winio.NewBackupFileWriter(bcdBackup, false) - defer func() { - cerr := bcdBackupWriter.Close() - if err == nil { - err = cerr - } - }() - - buf.Reset(io.MultiWriter(w, bcdBackupWriter)) - } else { - buf.Reset(w) +func copyDirInfo(fi os.FileInfo, path string) error { + if err := os.Chmod(path, fi.Mode()); err != nil { + return errors.Wrapf(err, "failed to chmod %s", path) } - - defer func() { - ferr := buf.Flush() - if err == nil { - err = ferr - } - }() - - return writeBackupStreamFromTarFile(buf, t, hdr) + return nil } -// writeBackupStreamFromTarFile writes a Win32 backup stream from the current tar file. Since this function may process multiple -// tar file entries in order to collect all the alternate data streams for the file, it returns the next -// tar file that was not processed, or io.EOF is there are no more. -func writeBackupStreamFromTarFile(w io.Writer, t *tar.Reader, hdr *tar.Header) (*tar.Header, error) { - bw := winio.NewBackupStreamWriter(w) - var sd []byte - var err error - // Maintaining old SDDL-based behavior for backward compatibility. All new tar headers written - // by this library will have raw binary for the security descriptor. - if sddl, ok := hdr.PAXRecords[hdrSecurityDescriptor]; ok { - sd, err = winio.SddlToSecurityDescriptor(sddl) - if err != nil { - return nil, err - } - } - if sdraw, ok := hdr.PAXRecords[hdrRawSecurityDescriptor]; ok { - sd, err = base64.StdEncoding.DecodeString(sdraw) - if err != nil { - return nil, err - } - } - if len(sd) != 0 { - bhdr := winio.BackupHeader{ - Id: winio.BackupSecurity, - Size: int64(len(sd)), - } - err := bw.WriteHeader(&bhdr) - if err != nil { - return nil, err - } - _, err = bw.Write(sd) - if err != nil { - return nil, err - } - } - var eas []winio.ExtendedAttribute - for k, v := range hdr.PAXRecords { - if !strings.HasPrefix(k, hdrEaPrefix) { - continue - } - data, err := base64.StdEncoding.DecodeString(v) - if err != nil { - return nil, err - } - eas = append(eas, winio.ExtendedAttribute{ - Name: k[len(hdrEaPrefix):], - Value: data, - }) - } - if len(eas) != 0 { - eadata, err := winio.EncodeExtendedAttributes(eas) - if err != nil { - return nil, err - } - bhdr := winio.BackupHeader{ - Id: winio.BackupEaData, - Size: int64(len(eadata)), - } - err = bw.WriteHeader(&bhdr) - if err != nil { - return nil, err - } - _, err = bw.Write(eadata) - if err != nil { - return nil, err - } - } - if hdr.Typeflag == tar.TypeSymlink { - _, isMountPoint := hdr.PAXRecords[hdrMountPoint] - rp := winio.ReparsePoint{ - Target: filepath.FromSlash(hdr.Linkname), - IsMountPoint: isMountPoint, - } - reparse := winio.EncodeReparsePoint(&rp) - bhdr := winio.BackupHeader{ - Id: winio.BackupReparseData, - Size: int64(len(reparse)), - } - err := bw.WriteHeader(&bhdr) - if err != nil { - return nil, err - } - _, err = bw.Write(reparse) - if err != nil { - return nil, err - } - } - - buf := bufPool.Get().(*[]byte) - defer bufPool.Put(buf) - - if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA { - bhdr := winio.BackupHeader{ - Id: winio.BackupData, - Size: hdr.Size, - } - err := bw.WriteHeader(&bhdr) - if err != nil { - return nil, err - } - _, err = io.CopyBuffer(bw, t, *buf) - if err != nil { - return nil, err - } - } - // Copy all the alternate data streams and return the next non-ADS header. - for { - ahdr, err := t.Next() - if err != nil { - return nil, err - } - if ahdr.Typeflag != tar.TypeReg || !strings.HasPrefix(ahdr.Name, hdr.Name+":") { - return ahdr, nil - } - bhdr := winio.BackupHeader{ - Id: winio.BackupAlternateData, - Size: ahdr.Size, - Name: ahdr.Name[len(hdr.Name):] + ":$DATA", - } - err = bw.WriteHeader(&bhdr) - if err != nil { - return nil, err - } - _, err = io.CopyBuffer(bw, t, *buf) - if err != nil { - return nil, err - } - } +func copyUpXAttrs(dst, src string) error { + return nil } diff --git a/vendor/github.com/containerd/containerd/archive/time_darwin.go b/vendor/github.com/containerd/containerd/archive/time_darwin.go deleted file mode 100644 index 9c2b656b04b74..0000000000000 --- a/vendor/github.com/containerd/containerd/archive/time_darwin.go +++ /dev/null @@ -1,30 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package archive - -import ( - "time" - - "github.com/pkg/errors" -) - -// as at MacOS 10.12 there is apparently no way to set timestamps -// with nanosecond precision. We could fall back to utimes/lutimes -// and lose the precision as a temporary workaround. -func chtimes(path string, atime, mtime time.Time) error { - return errors.New("OSX missing UtimesNanoAt") -} diff --git a/vendor/github.com/containerd/containerd/archive/time_unix.go b/vendor/github.com/containerd/containerd/archive/time_unix.go index 4a69cb7d0ed6a..e05ca719c2e6e 100644 --- a/vendor/github.com/containerd/containerd/archive/time_unix.go +++ b/vendor/github.com/containerd/containerd/archive/time_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris +// +build !windows /* Copyright The containerd Authors. @@ -32,7 +32,7 @@ func chtimes(path string, atime, mtime time.Time) error { utimes[1] = unix.NsecToTimespec(mtime.UnixNano()) if err := unix.UtimesNanoAt(unix.AT_FDCWD, path, utimes[0:], unix.AT_SYMLINK_NOFOLLOW); err != nil { - return errors.Wrap(err, "failed call to UtimesNanoAt") + return errors.Wrapf(err, "failed call to UtimesNanoAt for %s", path) } return nil diff --git a/vendor/github.com/containerd/containerd/cio/io.go b/vendor/github.com/containerd/containerd/cio/io.go index a9c6d2b156c62..8ee13edda4a37 100644 --- a/vendor/github.com/containerd/containerd/cio/io.go +++ b/vendor/github.com/containerd/containerd/cio/io.go @@ -18,10 +18,13 @@ package cio import ( "context" + "errors" "fmt" "io" + "net/url" "os" "path/filepath" + "strings" "sync" "github.com/containerd/containerd/defaults" @@ -77,7 +80,7 @@ type FIFOSet struct { // Close the FIFOSet func (f *FIFOSet) Close() error { - if f.close != nil { + if f != nil && f.close != nil { return f.close() } return nil @@ -222,46 +225,121 @@ type DirectIO struct { cio } -var _ IO = &DirectIO{} +var ( + _ IO = &DirectIO{} + _ IO = &logURI{} +) + +// LogURI provides the raw logging URI +func LogURI(uri *url.URL) Creator { + return func(_ string) (IO, error) { + return &logURI{ + config: Config{ + Stdout: uri.String(), + Stderr: uri.String(), + }, + }, nil + } +} + +// BinaryIO forwards container STDOUT|STDERR directly to a logging binary +func BinaryIO(binary string, args map[string]string) Creator { + return func(_ string) (IO, error) { + uri, err := LogURIGenerator("binary", binary, args) + if err != nil { + return nil, err + } + + res := uri.String() + return &logURI{ + config: Config{ + Stdout: res, + Stderr: res, + }, + }, nil + } +} + +// TerminalBinaryIO forwards container STDOUT|STDERR directly to a logging binary +// It also sets the terminal option to true +func TerminalBinaryIO(binary string, args map[string]string) Creator { + return func(_ string) (IO, error) { + uri, err := LogURIGenerator("binary", binary, args) + if err != nil { + return nil, err + } + + res := uri.String() + return &logURI{ + config: Config{ + Stdout: res, + Stderr: res, + Terminal: true, + }, + }, nil + } +} // LogFile creates a file on disk that logs the task's STDOUT,STDERR. // If the log file already exists, the logs will be appended to the file. func LogFile(path string) Creator { return func(_ string) (IO, error) { - if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { - return nil, err - } - f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + uri, err := LogURIGenerator("file", path, nil) if err != nil { return nil, err } - f.Close() - return &logIO{ + + res := uri.String() + return &logURI{ config: Config{ - Stdout: path, - Stderr: path, + Stdout: res, + Stderr: res, }, }, nil } } -type logIO struct { +// LogURIGenerator is the helper to generate log uri with specific scheme. +func LogURIGenerator(scheme string, path string, args map[string]string) (*url.URL, error) { + path = filepath.Clean(path) + if !strings.HasPrefix(path, "/") { + return nil, errors.New("absolute path needed") + } + + uri := &url.URL{ + Scheme: scheme, + Path: path, + } + + if len(args) == 0 { + return uri, nil + } + + q := uri.Query() + for k, v := range args { + q.Set(k, v) + } + uri.RawQuery = q.Encode() + return uri, nil +} + +type logURI struct { config Config } -func (l *logIO) Config() Config { +func (l *logURI) Config() Config { return l.config } -func (l *logIO) Cancel() { +func (l *logURI) Cancel() { } -func (l *logIO) Wait() { +func (l *logURI) Wait() { } -func (l *logIO) Close() error { +func (l *logURI) Close() error { return nil } @@ -275,3 +353,7 @@ func Load(set *FIFOSet) (IO, error) { closers: []io.Closer{set}, }, nil } + +func (p *pipes) closers() []io.Closer { + return []io.Closer{p.Stdin, p.Stdout, p.Stderr} +} diff --git a/vendor/github.com/containerd/containerd/cio/io_unix.go b/vendor/github.com/containerd/containerd/cio/io_unix.go index 3ab2a30b0ca02..8b600673fe3b7 100644 --- a/vendor/github.com/containerd/containerd/cio/io_unix.go +++ b/vendor/github.com/containerd/containerd/cio/io_unix.go @@ -72,17 +72,19 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { } var wg = &sync.WaitGroup{} - wg.Add(1) - go func() { - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - - io.CopyBuffer(ioset.Stdout, pipes.Stdout, *p) - pipes.Stdout.Close() - wg.Done() - }() + if fifos.Stdout != "" { + wg.Add(1) + go func() { + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) - if !fifos.Terminal { + io.CopyBuffer(ioset.Stdout, pipes.Stdout, *p) + pipes.Stdout.Close() + wg.Done() + }() + } + + if !fifos.Terminal && fifos.Stderr != "" { wg.Add(1) go func() { p := bufPool.Get().(*[]byte) @@ -101,38 +103,36 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { }, nil } -func openFifos(ctx context.Context, fifos *FIFOSet) (pipes, error) { - var err error +func openFifos(ctx context.Context, fifos *FIFOSet) (f pipes, retErr error) { defer func() { - if err != nil { + if retErr != nil { fifos.Close() } }() - var f pipes if fifos.Stdin != "" { - if f.Stdin, err = fifo.OpenFifo(ctx, fifos.Stdin, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - return f, errors.Wrapf(err, "failed to open stdin fifo") + if f.Stdin, retErr = fifo.OpenFifo(ctx, fifos.Stdin, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil { + return f, errors.Wrapf(retErr, "failed to open stdin fifo") } defer func() { - if err != nil && f.Stdin != nil { + if retErr != nil && f.Stdin != nil { f.Stdin.Close() } }() } if fifos.Stdout != "" { - if f.Stdout, err = fifo.OpenFifo(ctx, fifos.Stdout, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - return f, errors.Wrapf(err, "failed to open stdout fifo") + if f.Stdout, retErr = fifo.OpenFifo(ctx, fifos.Stdout, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil { + return f, errors.Wrapf(retErr, "failed to open stdout fifo") } defer func() { - if err != nil && f.Stdout != nil { + if retErr != nil && f.Stdout != nil { f.Stdout.Close() } }() } - if fifos.Stderr != "" { - if f.Stderr, err = fifo.OpenFifo(ctx, fifos.Stderr, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); err != nil { - return f, errors.Wrapf(err, "failed to open stderr fifo") + if !fifos.Terminal && fifos.Stderr != "" { + if f.Stderr, retErr = fifo.OpenFifo(ctx, fifos.Stderr, syscall.O_RDONLY|syscall.O_CREAT|syscall.O_NONBLOCK, 0700); retErr != nil { + return f, errors.Wrapf(retErr, "failed to open stderr fifo") } } return f, nil @@ -152,7 +152,3 @@ func NewDirectIO(ctx context.Context, fifos *FIFOSet) (*DirectIO, error) { }, }, err } - -func (p *pipes) closers() []io.Closer { - return []io.Closer{p.Stdin, p.Stdout, p.Stderr} -} diff --git a/vendor/github.com/containerd/containerd/cio/io_windows.go b/vendor/github.com/containerd/containerd/cio/io_windows.go index 5208f3eaaded9..ded475788f692 100644 --- a/vendor/github.com/containerd/containerd/cio/io_windows.go +++ b/vendor/github.com/containerd/containerd/cio/io_windows.go @@ -17,9 +17,9 @@ package cio import ( + "context" "fmt" "io" - "net" winio "github.com/Microsoft/go-winio" "github.com/containerd/containerd/log" @@ -30,30 +30,33 @@ const pipeRoot = `\\.\pipe` // NewFIFOSetInDir returns a new set of fifos for the task func NewFIFOSetInDir(_, id string, terminal bool) (*FIFOSet, error) { + stderrPipe := "" + if !terminal { + stderrPipe = fmt.Sprintf(`%s\ctr-%s-stderr`, pipeRoot, id) + } return NewFIFOSet(Config{ Terminal: terminal, Stdin: fmt.Sprintf(`%s\ctr-%s-stdin`, pipeRoot, id), Stdout: fmt.Sprintf(`%s\ctr-%s-stdout`, pipeRoot, id), - Stderr: fmt.Sprintf(`%s\ctr-%s-stderr`, pipeRoot, id), + Stderr: stderrPipe, }, nil), nil } -func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { - var ( - set []io.Closer - ) +func copyIO(fifos *FIFOSet, ioset *Streams) (_ *cio, retErr error) { + cios := &cio{config: fifos.Config} + + defer func() { + if retErr != nil { + _ = cios.Close() + } + }() if fifos.Stdin != "" { l, err := winio.ListenPipe(fifos.Stdin, nil) if err != nil { return nil, errors.Wrapf(err, "failed to create stdin pipe %s", fifos.Stdin) } - defer func(l net.Listener) { - if err != nil { - l.Close() - } - }(l) - set = append(set, l) + cios.closers = append(cios.closers, l) go func() { c, err := l.Accept() @@ -76,12 +79,7 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { if err != nil { return nil, errors.Wrapf(err, "failed to create stdout pipe %s", fifos.Stdout) } - defer func(l net.Listener) { - if err != nil { - l.Close() - } - }(l) - set = append(set, l) + cios.closers = append(cios.closers, l) go func() { c, err := l.Accept() @@ -104,12 +102,7 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { if err != nil { return nil, errors.Wrapf(err, "failed to create stderr pipe %s", fifos.Stderr) } - defer func(l net.Listener) { - if err != nil { - l.Close() - } - }(l) - set = append(set, l) + cios.closers = append(cios.closers, l) go func() { c, err := l.Accept() @@ -127,7 +120,7 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { }() } - return &cio{config: fifos.Config, closers: set}, nil + return cios, nil } // NewDirectIO returns an IO implementation that exposes the IO streams as io.ReadCloser @@ -144,3 +137,22 @@ func NewDirectIO(stdin io.WriteCloser, stdout, stderr io.ReadCloser, terminal bo }, } } + +// NewDirectIOFromFIFOSet returns an IO implementation that exposes the IO streams as io.ReadCloser +// and io.WriteCloser. +func NewDirectIOFromFIFOSet(ctx context.Context, stdin io.WriteCloser, stdout, stderr io.ReadCloser, fifos *FIFOSet) *DirectIO { + _, cancel := context.WithCancel(ctx) + pipes := pipes{ + Stdin: stdin, + Stdout: stdout, + Stderr: stderr, + } + return &DirectIO{ + pipes: pipes, + cio: cio{ + config: fifos.Config, + closers: append(pipes.closers(), fifos), + cancel: cancel, + }, + } +} diff --git a/vendor/github.com/containerd/containerd/client.go b/vendor/github.com/containerd/containerd/client.go index fd20c3dd0e4ec..ace9bf6d14da0 100644 --- a/vendor/github.com/containerd/containerd/client.go +++ b/vendor/github.com/containerd/containerd/client.go @@ -17,11 +17,14 @@ package containerd import ( + "bytes" "context" + "encoding/json" "fmt" "net/http" "runtime" "strconv" + "strings" "sync" "time" @@ -36,6 +39,7 @@ import ( snapshotsapi "github.com/containerd/containerd/api/services/snapshots/v1" "github.com/containerd/containerd/api/services/tasks/v1" versionservice "github.com/containerd/containerd/api/services/version/v1" + apitypes "github.com/containerd/containerd/api/types" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/content" contentproxy "github.com/containerd/containerd/content/proxy" @@ -51,7 +55,7 @@ import ( "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes/docker" - "github.com/containerd/containerd/remotes/docker/schema1" + "github.com/containerd/containerd/services/introspection" "github.com/containerd/containerd/snapshots" snproxy "github.com/containerd/containerd/snapshots/proxy" "github.com/containerd/typeurl" @@ -59,7 +63,9 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "golang.org/x/sync/semaphore" "google.golang.org/grpc" + "google.golang.org/grpc/backoff" "google.golang.org/grpc/health/grpc_health_v1" ) @@ -85,23 +91,38 @@ func New(address string, opts ...ClientOpt) (*Client, error) { if copts.timeout == 0 { copts.timeout = 10 * time.Second } - rt := fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS) + + c := &Client{ + defaultns: copts.defaultns, + } + if copts.defaultRuntime != "" { - rt = copts.defaultRuntime + c.runtime = copts.defaultRuntime + } else { + c.runtime = defaults.DefaultRuntime } - c := &Client{ - runtime: rt, + + if copts.defaultPlatform != nil { + c.platform = copts.defaultPlatform + } else { + c.platform = platforms.Default() } + if copts.services != nil { c.services = *copts.services } if address != "" { + backoffConfig := backoff.DefaultConfig + backoffConfig.MaxDelay = 3 * time.Second + connParams := grpc.ConnectParams{ + Backoff: backoffConfig, + } gopts := []grpc.DialOption{ grpc.WithBlock(), grpc.WithInsecure(), grpc.FailOnNonTempDialError(true), - grpc.WithBackoffMaxDelay(3 * time.Second), - grpc.WithDialer(dialer.Dialer), + grpc.WithConnectParams(connParams), + grpc.WithContextDialer(dialer.ContextDialer), // TODO(stevvooe): We may need to allow configuration of this on the client. grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)), @@ -133,8 +154,18 @@ func New(address string, opts ...ClientOpt) (*Client, error) { c.conn, c.connector = conn, connector } if copts.services == nil && c.conn == nil { - return nil, errors.New("no grpc connection or services is available") + return nil, errors.Wrap(errdefs.ErrUnavailable, "no grpc connection or services is available") + } + + // check namespace labels for default runtime + if copts.defaultRuntime == "" && c.defaultns != "" { + if label, err := c.GetLabel(context.Background(), defaults.DefaultRuntimeNSLabel); err != nil { + return nil, err + } else if label != "" { + c.runtime = label + } } + return c, nil } @@ -148,9 +179,20 @@ func NewWithConn(conn *grpc.ClientConn, opts ...ClientOpt) (*Client, error) { } } c := &Client{ - conn: conn, - runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS), + defaultns: copts.defaultns, + conn: conn, + runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS), + } + + // check namespace labels for default runtime + if copts.defaultRuntime == "" && c.defaultns != "" { + if label, err := c.GetLabel(context.Background(), defaults.DefaultRuntimeNSLabel); err != nil { + return nil, err + } else if label != "" { + c.runtime = label + } } + if copts.services != nil { c.services = *copts.services } @@ -164,13 +206,15 @@ type Client struct { connMu sync.Mutex conn *grpc.ClientConn runtime string + defaultns string + platform platforms.MatchComparer connector func() (*grpc.ClientConn, error) } // Reconnect re-establishes the GRPC connection to the containerd daemon func (c *Client) Reconnect() error { if c.connector == nil { - return errors.New("unable to reconnect to containerd, no connector available") + return errors.Wrap(errdefs.ErrUnavailable, "unable to reconnect to containerd, no connector available") } c.connMu.Lock() defer c.connMu.Unlock() @@ -183,6 +227,11 @@ func (c *Client) Reconnect() error { return nil } +// Runtime returns the name of the runtime being used +func (c *Client) Runtime() string { + return c.runtime +} + // IsServing returns true if the client can successfully connect to the // containerd daemon and the healthcheck service returns the SERVING // response. @@ -193,10 +242,10 @@ func (c *Client) IsServing(ctx context.Context) (bool, error) { c.connMu.Lock() if c.conn == nil { c.connMu.Unlock() - return false, errors.New("no grpc connection available") + return false, errors.Wrap(errdefs.ErrUnavailable, "no grpc connection available") } c.connMu.Unlock() - r, err := c.HealthService().Check(ctx, &grpc_health_v1.HealthCheckRequest{}, grpc.FailFast(false)) + r, err := c.HealthService().Check(ctx, &grpc_health_v1.HealthCheckRequest{}, grpc.WaitForReady(true)) if err != nil { return false, err } @@ -265,13 +314,20 @@ type RemoteContext struct { PlatformMatcher platforms.MatchComparer // Unpack is done after an image is pulled to extract into a snapshotter. + // It is done simultaneously for schema 2 images when they are pulled. // If an image is not unpacked on pull, it can be unpacked any time // afterwards. Unpacking is required to run an image. Unpack bool + // UnpackOpts handles options to the unpack call. + UnpackOpts []UnpackOpt + // Snapshotter used for unpacking Snapshotter string + // SnapshotterOpts are additional options to be passed to a snapshotter during pull + SnapshotterOpts []snapshots.Opt + // Labels to be applied to the created image Labels map[string]string @@ -280,6 +336,12 @@ type RemoteContext struct { // handlers. BaseHandlers []images.Handler + // HandlerWrapper wraps the handler which gets sent to dispatch. + // Unlike BaseHandlers, this can run before and after the built + // in handlers, allowing operations to run on the descriptor + // after it has completed transferring. + HandlerWrapper func(images.Handler) images.Handler + // ConvertSchema1 is whether to convert Docker registry schema 1 // manifests. If this option is false then any image which resolves // to schema 1 will return an error since schema 1 is not supported. @@ -290,6 +352,19 @@ type RemoteContext struct { // platforms will be used to create a PlatformMatcher with no ordering // preference. Platforms []string + + // MaxConcurrentDownloads is the max concurrent content downloads for each pull. + MaxConcurrentDownloads int + + // MaxConcurrentUploadedLayers is the max concurrent uploaded layers for each push. + MaxConcurrentUploadedLayers int + + // AllMetadata downloads all manifests and known-configuration files + AllMetadata bool + + // ChildLabelMap sets the labels used to reference child objects in the content + // store. By default, all GC reference labels will be set for all fetched content. + ChildLabelMap func(ocispec.Descriptor) []string } func defaultRemoteContext() *RemoteContext { @@ -297,7 +372,6 @@ func defaultRemoteContext() *RemoteContext { Resolver: docker.NewResolver(docker.ResolverOptions{ Client: http.DefaultClient, }), - Snapshotter: DefaultSnapshotter, } } @@ -312,7 +386,7 @@ func (c *Client) Fetch(ctx context.Context, ref string, opts ...RemoteOpt) (imag } if fetchCtx.Unpack { - return images.Image{}, errors.New("unpack on fetch not supported, try pull") + return images.Image{}, errors.Wrap(errdefs.ErrNotImplemented, "unpack on fetch not supported, try pull") } if fetchCtx.PlatformMatcher == nil { @@ -338,132 +412,11 @@ func (c *Client) Fetch(ctx context.Context, ref string, opts ...RemoteOpt) (imag } defer done(ctx) - return c.fetch(ctx, fetchCtx, ref, 0) -} - -// Pull downloads the provided content into containerd's content store -// and returns a platform specific image object -func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image, error) { - pullCtx := defaultRemoteContext() - for _, o := range opts { - if err := o(c, pullCtx); err != nil { - return nil, err - } - } - - if pullCtx.PlatformMatcher == nil { - if len(pullCtx.Platforms) > 1 { - return nil, errors.New("cannot pull multiplatform image locally, try Fetch") - } else if len(pullCtx.Platforms) == 0 { - pullCtx.PlatformMatcher = platforms.Default() - } else { - p, err := platforms.Parse(pullCtx.Platforms[0]) - if err != nil { - return nil, errors.Wrapf(err, "invalid platform %s", pullCtx.Platforms[0]) - } - - pullCtx.PlatformMatcher = platforms.Only(p) - } - } - - ctx, done, err := c.WithLease(ctx) + img, err := c.fetch(ctx, fetchCtx, ref, 0) if err != nil { - return nil, err - } - defer done(ctx) - - img, err := c.fetch(ctx, pullCtx, ref, 1) - if err != nil { - return nil, err - } - - i := NewImageWithPlatform(c, img, pullCtx.PlatformMatcher) - - if pullCtx.Unpack { - if err := i.Unpack(ctx, pullCtx.Snapshotter); err != nil { - return nil, errors.Wrapf(err, "failed to unpack image on snapshotter %s", pullCtx.Snapshotter) - } - } - - return i, nil -} - -func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, limit int) (images.Image, error) { - store := c.ContentStore() - name, desc, err := rCtx.Resolver.Resolve(ctx, ref) - if err != nil { - return images.Image{}, errors.Wrapf(err, "failed to resolve reference %q", ref) - } - - fetcher, err := rCtx.Resolver.Fetcher(ctx, name) - if err != nil { - return images.Image{}, errors.Wrapf(err, "failed to get fetcher for %q", name) - } - - var ( - schema1Converter *schema1.Converter - handler images.Handler - ) - if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 { - schema1Converter = schema1.NewConverter(store, fetcher) - handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...) - } else { - // Get all the children for a descriptor - childrenHandler := images.ChildrenHandler(store) - // Set any children labels for that content - childrenHandler = images.SetChildrenLabels(store, childrenHandler) - // Filter children by platforms - childrenHandler = images.FilterPlatforms(childrenHandler, rCtx.PlatformMatcher) - // Sort and limit manifests if a finite number is needed - if limit > 0 { - childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit) - } - - handler = images.Handlers(append(rCtx.BaseHandlers, - remotes.FetchHandler(store, fetcher), - childrenHandler, - )...) - } - - if err := images.Dispatch(ctx, handler, desc); err != nil { return images.Image{}, err } - if schema1Converter != nil { - desc, err = schema1Converter.Convert(ctx) - if err != nil { - return images.Image{}, err - } - } - - img := images.Image{ - Name: name, - Target: desc, - Labels: rCtx.Labels, - } - - is := c.ImageService() - for { - if created, err := is.Create(ctx, img); err != nil { - if !errdefs.IsAlreadyExists(err) { - return images.Image{}, err - } - - updated, err := is.Update(ctx, img) - if err != nil { - // if image was removed, try create again - if errdefs.IsNotFound(err) { - continue - } - return images.Image{}, err - } - - img = updated - } else { - img = created - } - - return img, nil - } + return c.createNewImage(ctx, img) } // Push uploads the provided content to a remote resource @@ -490,12 +443,36 @@ func (c *Client) Push(ctx context.Context, ref string, desc ocispec.Descriptor, } } + // Annotate ref with digest to push only push tag for single digest + if !strings.Contains(ref, "@") { + ref = ref + "@" + desc.Digest.String() + } + pusher, err := pushCtx.Resolver.Pusher(ctx, ref) if err != nil { return err } - return remotes.PushContent(ctx, pusher, desc, c.ContentStore(), pushCtx.PlatformMatcher, pushCtx.BaseHandlers...) + var wrapper func(images.Handler) images.Handler + + if len(pushCtx.BaseHandlers) > 0 { + wrapper = func(h images.Handler) images.Handler { + h = images.Handlers(append(pushCtx.BaseHandlers, h)...) + if pushCtx.HandlerWrapper != nil { + h = pushCtx.HandlerWrapper(h) + } + return h + } + } else if pushCtx.HandlerWrapper != nil { + wrapper = pushCtx.HandlerWrapper + } + + var limiter *semaphore.Weighted + if pushCtx.MaxConcurrentUploadedLayers > 0 { + limiter = semaphore.NewWeighted(int64(pushCtx.MaxConcurrentUploadedLayers)) + } + + return remotes.PushContent(ctx, pusher, desc, c.ContentStore(), limiter, pushCtx.PlatformMatcher, wrapper) } // GetImage returns an existing image @@ -520,6 +497,66 @@ func (c *Client) ListImages(ctx context.Context, filters ...string) ([]Image, er return images, nil } +// Restore restores a container from a checkpoint +func (c *Client) Restore(ctx context.Context, id string, checkpoint Image, opts ...RestoreOpts) (Container, error) { + store := c.ContentStore() + index, err := decodeIndex(ctx, store, checkpoint.Target()) + if err != nil { + return nil, err + } + + ctx, done, err := c.WithLease(ctx) + if err != nil { + return nil, err + } + defer done(ctx) + + copts := []NewContainerOpts{} + for _, o := range opts { + copts = append(copts, o(ctx, id, c, checkpoint, index)) + } + + ctr, err := c.NewContainer(ctx, id, copts...) + if err != nil { + return nil, err + } + + return ctr, nil +} + +func writeIndex(ctx context.Context, index *ocispec.Index, client *Client, ref string) (d ocispec.Descriptor, err error) { + labels := map[string]string{} + for i, m := range index.Manifests { + labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = m.Digest.String() + } + data, err := json.Marshal(index) + if err != nil { + return ocispec.Descriptor{}, err + } + return writeContent(ctx, client.ContentStore(), ocispec.MediaTypeImageIndex, ref, bytes.NewReader(data), content.WithLabels(labels)) +} + +// GetLabel gets a label value from namespace store +// If there is no default label, an empty string returned with nil error +func (c *Client) GetLabel(ctx context.Context, label string) (string, error) { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + if c.defaultns == "" { + return "", err + } + ns = c.defaultns + } + + srv := c.NamespaceService() + labels, err := srv.Labels(ctx, ns) + if err != nil { + return "", err + } + + value := labels[label] + return value, nil +} + // Subscribe to events that match one or more of the provided filters. // // Callers should listen on both the envelope and errs channels. If the errs @@ -573,6 +610,10 @@ func (c *Client) ContentStore() content.Store { // SnapshotService returns the underlying snapshotter for the provided snapshotter name func (c *Client) SnapshotService(snapshotterName string) snapshots.Snapshotter { + snapshotterName, err := c.resolveSnapshotterName(context.Background(), snapshotterName) + if err != nil { + snapshotterName = DefaultSnapshotter + } if c.snapshotters != nil { return c.snapshotters[snapshotterName] } @@ -612,10 +653,13 @@ func (c *Client) DiffService() DiffService { } // IntrospectionService returns the underlying Introspection Client -func (c *Client) IntrospectionService() introspectionapi.IntrospectionClient { +func (c *Client) IntrospectionService() introspection.Service { + if c.introspectionService != nil { + return c.introspectionService + } c.connMu.Lock() defer c.connMu.Unlock() - return introspectionapi.NewIntrospectionClient(c.conn) + return introspection.NewIntrospectionServiceFromClient(introspectionapi.NewIntrospectionClient(c.conn)) } // LeasesService returns the underlying Leases Client @@ -652,6 +696,13 @@ func (c *Client) VersionService() versionservice.VersionClient { return versionservice.NewVersionClient(c.conn) } +// Conn returns the underlying GRPC connection object +func (c *Client) Conn() *grpc.ClientConn { + c.connMu.Lock() + defer c.connMu.Unlock() + return c.conn +} + // Version of containerd type Version struct { // Version number @@ -665,7 +716,7 @@ func (c *Client) Version(ctx context.Context) (Version, error) { c.connMu.Lock() if c.conn == nil { c.connMu.Unlock() - return Version{}, errors.New("no grpc connection available") + return Version{}, errors.Wrap(errdefs.ErrUnavailable, "no grpc connection available") } c.connMu.Unlock() response, err := c.VersionService().Version(ctx, &ptypes.Empty{}) @@ -677,3 +728,106 @@ func (c *Client) Version(ctx context.Context) (Version, error) { Revision: response.Revision, }, nil } + +// ServerInfo represents the introspected server information +type ServerInfo struct { + UUID string +} + +// Server returns server information from the introspection service +func (c *Client) Server(ctx context.Context) (ServerInfo, error) { + c.connMu.Lock() + if c.conn == nil { + c.connMu.Unlock() + return ServerInfo{}, errors.Wrap(errdefs.ErrUnavailable, "no grpc connection available") + } + c.connMu.Unlock() + + response, err := c.IntrospectionService().Server(ctx, &ptypes.Empty{}) + if err != nil { + return ServerInfo{}, err + } + return ServerInfo{ + UUID: response.UUID, + }, nil +} + +func (c *Client) resolveSnapshotterName(ctx context.Context, name string) (string, error) { + if name == "" { + label, err := c.GetLabel(ctx, defaults.DefaultSnapshotterNSLabel) + if err != nil { + return "", err + } + + if label != "" { + name = label + } else { + name = DefaultSnapshotter + } + } + + return name, nil +} + +func (c *Client) getSnapshotter(ctx context.Context, name string) (snapshots.Snapshotter, error) { + name, err := c.resolveSnapshotterName(ctx, name) + if err != nil { + return nil, err + } + + s := c.SnapshotService(name) + if s == nil { + return nil, errors.Wrapf(errdefs.ErrNotFound, "snapshotter %s was not found", name) + } + + return s, nil +} + +// CheckRuntime returns true if the current runtime matches the expected +// runtime. Providing various parts of the runtime schema will match those +// parts of the expected runtime +func CheckRuntime(current, expected string) bool { + cp := strings.Split(current, ".") + l := len(cp) + for i, p := range strings.Split(expected, ".") { + if i > l { + return false + } + if p != cp[i] { + return false + } + } + return true +} + +// GetSnapshotterSupportedPlatforms returns a platform matchers which represents the +// supported platforms for the given snapshotters +func (c *Client) GetSnapshotterSupportedPlatforms(ctx context.Context, snapshotterName string) (platforms.MatchComparer, error) { + filters := []string{fmt.Sprintf("type==%s, id==%s", plugin.SnapshotPlugin, snapshotterName)} + in := c.IntrospectionService() + + resp, err := in.Plugins(ctx, filters) + if err != nil { + return nil, err + } + + if len(resp.Plugins) <= 0 { + return nil, fmt.Errorf("inspection service could not find snapshotter %s plugin", snapshotterName) + } + + sn := resp.Plugins[0] + snPlatforms := toPlatforms(sn.Platforms) + return platforms.Any(snPlatforms...), nil +} + +func toPlatforms(pt []apitypes.Platform) []ocispec.Platform { + platforms := make([]ocispec.Platform, len(pt)) + for i, p := range pt { + platforms[i] = ocispec.Platform{ + Architecture: p.Architecture, + OS: p.OS, + Variant: p.Variant, + } + } + return platforms +} diff --git a/vendor/github.com/containerd/containerd/client_opts.go b/vendor/github.com/containerd/containerd/client_opts.go index b7431ad299e2e..44feaa30abbcf 100644 --- a/vendor/github.com/containerd/containerd/client_opts.go +++ b/vendor/github.com/containerd/containerd/client_opts.go @@ -22,15 +22,19 @@ import ( "github.com/containerd/containerd/images" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/remotes" + "github.com/containerd/containerd/snapshots" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "google.golang.org/grpc" ) type clientOpts struct { - defaultns string - defaultRuntime string - services *services - dialOptions []grpc.DialOption - timeout time.Duration + defaultns string + defaultRuntime string + defaultPlatform platforms.MatchComparer + services *services + dialOptions []grpc.DialOption + timeout time.Duration } // ClientOpt allows callers to set options on the containerd client @@ -55,6 +59,14 @@ func WithDefaultRuntime(rt string) ClientOpt { } } +// WithDefaultPlatform sets the default platform matcher on the client +func WithDefaultPlatform(platform platforms.MatchComparer) ClientOpt { + return func(c *clientOpts) error { + c.defaultPlatform = platform + return nil + } +} + // WithDialOpts allows grpc.DialOptions to be set on the connection func WithDialOpts(opts []grpc.DialOption) ClientOpt { return func(c *clientOpts) error { @@ -121,10 +133,19 @@ func WithPullUnpack(_ *Client, c *RemoteContext) error { return nil } -// WithPullSnapshotter specifies snapshotter name used for unpacking -func WithPullSnapshotter(snapshotterName string) RemoteOpt { +// WithUnpackOpts is used to add unpack options to the unpacker. +func WithUnpackOpts(opts []UnpackOpt) RemoteOpt { + return func(_ *Client, c *RemoteContext) error { + c.UnpackOpts = append(c.UnpackOpts, opts...) + return nil + } +} + +// WithPullSnapshotter specifies snapshotter name used for unpacking. +func WithPullSnapshotter(snapshotterName string, opts ...snapshots.Opt) RemoteOpt { return func(_ *Client, c *RemoteContext) error { c.Snapshotter = snapshotterName + c.SnapshotterOpts = opts return nil } } @@ -155,6 +176,18 @@ func WithPullLabels(labels map[string]string) RemoteOpt { } } +// WithChildLabelMap sets the map function used to define the labels set +// on referenced child content in the content store. This can be used +// to overwrite the default GC labels or filter which labels get set +// for content. +// The default is `images.ChildGCLabels`. +func WithChildLabelMap(fn func(ocispec.Descriptor) []string) RemoteOpt { + return func(_ *Client, c *RemoteContext) error { + c.ChildLabelMap = fn + return nil + } +} + // WithSchema1Conversion is used to convert Docker registry schema 1 // manifests to oci manifests on pull. Without this option schema 1 // manifests will return a not supported error. @@ -178,3 +211,35 @@ func WithImageHandler(h images.Handler) RemoteOpt { return nil } } + +// WithImageHandlerWrapper wraps the handlers to be called on dispatch. +func WithImageHandlerWrapper(w func(images.Handler) images.Handler) RemoteOpt { + return func(client *Client, c *RemoteContext) error { + c.HandlerWrapper = w + return nil + } +} + +// WithMaxConcurrentDownloads sets max concurrent download limit. +func WithMaxConcurrentDownloads(max int) RemoteOpt { + return func(client *Client, c *RemoteContext) error { + c.MaxConcurrentDownloads = max + return nil + } +} + +// WithMaxConcurrentUploadedLayers sets max concurrent uploaded layer limit. +func WithMaxConcurrentUploadedLayers(max int) RemoteOpt { + return func(client *Client, c *RemoteContext) error { + c.MaxConcurrentUploadedLayers = max + return nil + } +} + +// WithAllMetadata downloads all manifests and known-configuration files +func WithAllMetadata() RemoteOpt { + return func(_ *Client, c *RemoteContext) error { + c.AllMetadata = true + return nil + } +} diff --git a/vendor/github.com/containerd/containerd/container.go b/vendor/github.com/containerd/containerd/container.go index 3c09b2dbc62db..d5da55e51892b 100644 --- a/vendor/github.com/containerd/containerd/container.go +++ b/vendor/github.com/containerd/containerd/container.go @@ -25,21 +25,34 @@ import ( "github.com/containerd/containerd/api/services/tasks/v1" "github.com/containerd/containerd/api/types" + tasktypes "github.com/containerd/containerd/api/types/task" "github.com/containerd/containerd/cio" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" "github.com/containerd/containerd/oci" + "github.com/containerd/containerd/runtime/v2/runc/options" + "github.com/containerd/fifo" "github.com/containerd/typeurl" prototypes "github.com/gogo/protobuf/types" + ver "github.com/opencontainers/image-spec/specs-go" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" ) +const ( + checkpointImageNameLabel = "org.opencontainers.image.ref.name" + checkpointRuntimeNameLabel = "io.containerd.checkpoint.runtime" + checkpointSnapshotterNameLabel = "io.containerd.checkpoint.snapshotter" +) + // Container is a metadata object for container resources and task creation type Container interface { // ID identifies the container ID() string // Info returns the underlying container record type - Info(context.Context) (containers.Container, error) + Info(context.Context, ...InfoOpts) (containers.Container, error) // Delete removes the container Delete(context.Context, ...DeleteOpts) error // NewTask creates a new task based on the container metadata @@ -64,20 +77,24 @@ type Container interface { Extensions(context.Context) (map[string]prototypes.Any, error) // Update a container Update(context.Context, ...UpdateContainerOpts) error + // Checkpoint creates a checkpoint image of the current container + Checkpoint(context.Context, string, ...CheckpointOpts) (Image, error) } func containerFromRecord(client *Client, c containers.Container) *container { return &container{ - client: client, - id: c.ID, + client: client, + id: c.ID, + metadata: c, } } var _ = (Container)(&container{}) type container struct { - client *Client - id string + client *Client + id string + metadata containers.Container } // ID returns the container's unique id @@ -85,8 +102,22 @@ func (c *container) ID() string { return c.id } -func (c *container) Info(ctx context.Context) (containers.Container, error) { - return c.get(ctx) +func (c *container) Info(ctx context.Context, opts ...InfoOpts) (containers.Container, error) { + i := &InfoConfig{ + // default to refreshing the container's local metadata + Refresh: true, + } + for _, o := range opts { + o(i) + } + if i.Refresh { + metadata, err := c.get(ctx) + if err != nil { + return c.metadata, err + } + c.metadata = metadata + } + return c.metadata, nil } func (c *container) Extensions(ctx context.Context) (map[string]prototypes.Any, error) { @@ -205,11 +236,25 @@ func (c *container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...N } // get the rootfs from the snapshotter and add it to the request - mounts, err := c.client.SnapshotService(r.Snapshotter).Mounts(ctx, r.SnapshotKey) + s, err := c.client.getSnapshotter(ctx, r.Snapshotter) + if err != nil { + return nil, err + } + mounts, err := s.Mounts(ctx, r.SnapshotKey) + if err != nil { + return nil, err + } + spec, err := c.Spec(ctx) if err != nil { return nil, err } for _, m := range mounts { + if spec.Linux != nil && spec.Linux.MountLabel != "" { + context := label.FormatMountLabel("", spec.Linux.MountLabel) + if context != "" { + m.Options = append(m.Options, context) + } + } request.Rootfs = append(request.Rootfs, &types.Mount{ Type: m.Type, Source: m.Source, @@ -217,7 +262,9 @@ func (c *container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...N }) } } - var info TaskInfo + info := TaskInfo{ + runtime: r.Runtime.Name, + } for _, o := range opts { if err := o(ctx, c.client, &info); err != nil { return nil, err @@ -243,6 +290,7 @@ func (c *container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...N client: c.client, io: i, id: c.id, + c: c, } if info.Checkpoint != nil { request.Checkpoint = info.Checkpoint @@ -272,6 +320,70 @@ func (c *container) Update(ctx context.Context, opts ...UpdateContainerOpts) err return nil } +func (c *container) Checkpoint(ctx context.Context, ref string, opts ...CheckpointOpts) (Image, error) { + index := &ocispec.Index{ + Versioned: ver.Versioned{ + SchemaVersion: 2, + }, + Annotations: make(map[string]string), + } + copts := &options.CheckpointOptions{ + Exit: false, + OpenTcp: false, + ExternalUnixSockets: false, + Terminal: false, + FileLocks: true, + EmptyNamespaces: nil, + } + info, err := c.Info(ctx) + if err != nil { + return nil, err + } + + img, err := c.Image(ctx) + if err != nil { + return nil, err + } + + ctx, done, err := c.client.WithLease(ctx) + if err != nil { + return nil, err + } + defer done(ctx) + + // add image name to manifest + index.Annotations[checkpointImageNameLabel] = img.Name() + // add runtime info to index + index.Annotations[checkpointRuntimeNameLabel] = info.Runtime.Name + // add snapshotter info to index + index.Annotations[checkpointSnapshotterNameLabel] = info.Snapshotter + + // process remaining opts + for _, o := range opts { + if err := o(ctx, c.client, &info, index, copts); err != nil { + err = errdefs.FromGRPC(err) + if !errdefs.IsAlreadyExists(err) { + return nil, err + } + } + } + + desc, err := writeIndex(ctx, index, c.client, c.ID()+"index") + if err != nil { + return nil, err + } + i := images.Image{ + Name: ref, + Target: desc, + } + checkpoint, err := c.client.ImageService().Create(ctx, i) + if err != nil { + return nil, err + } + + return NewImage(c.client, checkpoint), nil +} + func (c *container) loadTask(ctx context.Context, ioAttach cio.Attach) (Task, error) { response, err := c.client.TaskService().Get(ctx, &tasks.GetRequest{ ContainerID: c.id, @@ -284,7 +396,9 @@ func (c *container) loadTask(ctx context.Context, ioAttach cio.Attach) (Task, er return nil, err } var i cio.IO - if ioAttach != nil { + if ioAttach != nil && response.Process.Status != tasktypes.StatusUnknown { + // Do not attach IO for task in unknown state, because there + // are no fifo paths anyway. if i, err = attachExistingIO(response, ioAttach); err != nil { return nil, err } @@ -294,6 +408,7 @@ func (c *container) loadTask(ctx context.Context, ioAttach cio.Attach) (Task, er io: i, id: response.Process.ID, pid: response.Process.Pid, + c: c, } return t, nil } @@ -310,14 +425,33 @@ func attachExistingIO(response *tasks.GetResponse, ioAttach cio.Attach) (cio.IO, // loadFifos loads the containers fifos func loadFifos(response *tasks.GetResponse) *cio.FIFOSet { - path := getFifoDir([]string{ + fifos := []string{ response.Process.Stdin, response.Process.Stdout, response.Process.Stderr, - }) + } closer := func() error { - return os.RemoveAll(path) + var ( + err error + dirs = map[string]struct{}{} + ) + for _, f := range fifos { + if isFifo, _ := fifo.IsFifo(f); isFifo { + if rerr := os.Remove(f); err == nil { + err = rerr + } + dirs[filepath.Dir(f)] = struct{}{} + } + } + for dir := range dirs { + // we ignore errors here because we don't + // want to remove the directory if it isn't + // empty + os.Remove(dir) + } + return err } + return cio.NewFIFOSet(cio.Config{ Stdin: response.Process.Stdin, Stdout: response.Process.Stdout, @@ -325,14 +459,3 @@ func loadFifos(response *tasks.GetResponse) *cio.FIFOSet { Terminal: response.Process.Terminal, }, closer) } - -// getFifoDir looks for any non-empty path for a stdio fifo -// and returns the dir for where it is located -func getFifoDir(paths []string) string { - for _, p := range paths { - if p != "" { - return filepath.Dir(p) - } - } - return "" -} diff --git a/vendor/github.com/containerd/containerd/container_checkpoint_opts.go b/vendor/github.com/containerd/containerd/container_checkpoint_opts.go new file mode 100644 index 0000000000000..510863681cd2b --- /dev/null +++ b/vendor/github.com/containerd/containerd/container_checkpoint_opts.go @@ -0,0 +1,156 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "bytes" + "context" + "fmt" + "runtime" + + tasks "github.com/containerd/containerd/api/services/tasks/v1" + "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/diff" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/platforms" + "github.com/containerd/containerd/rootfs" + "github.com/containerd/containerd/runtime/v2/runc/options" + "github.com/containerd/typeurl" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +var ( + // ErrCheckpointRWUnsupported is returned if the container runtime does not support checkpoint + ErrCheckpointRWUnsupported = errors.New("rw checkpoint is only supported on v2 runtimes") + // ErrMediaTypeNotFound returns an error when a media type in the manifest is unknown + ErrMediaTypeNotFound = errors.New("media type not found") +) + +// CheckpointOpts are options to manage the checkpoint operation +type CheckpointOpts func(context.Context, *Client, *containers.Container, *imagespec.Index, *options.CheckpointOptions) error + +// WithCheckpointImage includes the container image in the checkpoint +func WithCheckpointImage(ctx context.Context, client *Client, c *containers.Container, index *imagespec.Index, copts *options.CheckpointOptions) error { + ir, err := client.ImageService().Get(ctx, c.Image) + if err != nil { + return err + } + index.Manifests = append(index.Manifests, ir.Target) + return nil +} + +// WithCheckpointTask includes the running task +func WithCheckpointTask(ctx context.Context, client *Client, c *containers.Container, index *imagespec.Index, copts *options.CheckpointOptions) error { + any, err := typeurl.MarshalAny(copts) + if err != nil { + return nil + } + task, err := client.TaskService().Checkpoint(ctx, &tasks.CheckpointTaskRequest{ + ContainerID: c.ID, + Options: any, + }) + if err != nil { + return err + } + for _, d := range task.Descriptors { + platformSpec := platforms.DefaultSpec() + index.Manifests = append(index.Manifests, imagespec.Descriptor{ + MediaType: d.MediaType, + Size: d.Size_, + Digest: d.Digest, + Platform: &platformSpec, + Annotations: d.Annotations, + }) + } + // save copts + data, err := any.Marshal() + if err != nil { + return err + } + r := bytes.NewReader(data) + desc, err := writeContent(ctx, client.ContentStore(), images.MediaTypeContainerd1CheckpointOptions, c.ID+"-checkpoint-options", r) + if err != nil { + return err + } + desc.Platform = &imagespec.Platform{ + OS: runtime.GOOS, + Architecture: runtime.GOARCH, + } + index.Manifests = append(index.Manifests, desc) + return nil +} + +// WithCheckpointRuntime includes the container runtime info +func WithCheckpointRuntime(ctx context.Context, client *Client, c *containers.Container, index *imagespec.Index, copts *options.CheckpointOptions) error { + if c.Runtime.Options != nil { + data, err := c.Runtime.Options.Marshal() + if err != nil { + return err + } + r := bytes.NewReader(data) + desc, err := writeContent(ctx, client.ContentStore(), images.MediaTypeContainerd1CheckpointRuntimeOptions, c.ID+"-runtime-options", r) + if err != nil { + return err + } + desc.Platform = &imagespec.Platform{ + OS: runtime.GOOS, + Architecture: runtime.GOARCH, + } + index.Manifests = append(index.Manifests, desc) + } + return nil +} + +// WithCheckpointRW includes the rw in the checkpoint +func WithCheckpointRW(ctx context.Context, client *Client, c *containers.Container, index *imagespec.Index, copts *options.CheckpointOptions) error { + diffOpts := []diff.Opt{ + diff.WithReference(fmt.Sprintf("checkpoint-rw-%s", c.SnapshotKey)), + } + rw, err := rootfs.CreateDiff(ctx, + c.SnapshotKey, + client.SnapshotService(c.Snapshotter), + client.DiffService(), + diffOpts..., + ) + if err != nil { + return err + + } + rw.Platform = &imagespec.Platform{ + OS: runtime.GOOS, + Architecture: runtime.GOARCH, + } + index.Manifests = append(index.Manifests, rw) + return nil +} + +// WithCheckpointTaskExit causes the task to exit after checkpoint +func WithCheckpointTaskExit(ctx context.Context, client *Client, c *containers.Container, index *imagespec.Index, copts *options.CheckpointOptions) error { + copts.Exit = true + return nil +} + +// GetIndexByMediaType returns the index in a manifest for the specified media type +func GetIndexByMediaType(index *imagespec.Index, mt string) (*imagespec.Descriptor, error) { + for _, d := range index.Manifests { + if d.MediaType == mt { + return &d, nil + } + } + return nil, ErrMediaTypeNotFound +} diff --git a/vendor/github.com/containerd/containerd/container_opts.go b/vendor/github.com/containerd/containerd/container_opts.go index ca4bf67486a1f..024d6e10b6767 100644 --- a/vendor/github.com/containerd/containerd/container_opts.go +++ b/vendor/github.com/containerd/containerd/container_opts.go @@ -18,14 +18,19 @@ package containerd import ( "context" + "encoding/json" + "fmt" "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" "github.com/containerd/containerd/oci" - "github.com/containerd/containerd/platforms" + "github.com/containerd/containerd/snapshots" "github.com/containerd/typeurl" "github.com/gogo/protobuf/types" "github.com/opencontainers/image-spec/identity" + v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -38,6 +43,15 @@ type NewContainerOpts func(ctx context.Context, client *Client, c *containers.Co // UpdateContainerOpts allows the caller to set additional options when updating a container type UpdateContainerOpts func(ctx context.Context, client *Client, c *containers.Container) error +// InfoOpts controls how container metadata is fetched and returned +type InfoOpts func(*InfoConfig) + +// InfoConfig specifies how container metadata is fetched +type InfoConfig struct { + // Refresh will to a fetch of the latest container metadata + Refresh bool +} + // WithRuntime allows a user to specify the runtime name and additional options that should // be used to create tasks for the container func WithRuntime(name string, options interface{}) NewContainerOpts { @@ -68,7 +82,17 @@ func WithImage(i Image) NewContainerOpts { } } -// WithContainerLabels adds the provided labels to the container +// WithImageName allows setting the image name as the base for the container +func WithImageName(n string) NewContainerOpts { + return func(ctx context.Context, _ *Client, c *containers.Container) error { + c.Image = n + return nil + } +} + +// WithContainerLabels sets the provided labels to the container. +// The existing labels are cleared. +// Use WithAdditionalContainerLabels to preserve the existing labels. func WithContainerLabels(labels map[string]string) NewContainerOpts { return func(_ context.Context, _ *Client, c *containers.Container) error { c.Labels = labels @@ -76,6 +100,54 @@ func WithContainerLabels(labels map[string]string) NewContainerOpts { } } +// WithImageConfigLabels sets the image config labels on the container. +// The existing labels are cleared as this is expected to be the first +// operation in setting up a container's labels. Use WithAdditionalContainerLabels +// to add/overwrite the existing image config labels. +func WithImageConfigLabels(image Image) NewContainerOpts { + return func(ctx context.Context, _ *Client, c *containers.Container) error { + ic, err := image.Config(ctx) + if err != nil { + return err + } + var ( + ociimage v1.Image + config v1.ImageConfig + ) + switch ic.MediaType { + case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: + p, err := content.ReadBlob(ctx, image.ContentStore(), ic) + if err != nil { + return err + } + + if err := json.Unmarshal(p, &ociimage); err != nil { + return err + } + config = ociimage.Config + default: + return fmt.Errorf("unknown image config media type %s", ic.MediaType) + } + c.Labels = config.Labels + return nil + } +} + +// WithAdditionalContainerLabels adds the provided labels to the container +// The existing labels are preserved as long as they do not conflict with the added labels. +func WithAdditionalContainerLabels(labels map[string]string) NewContainerOpts { + return func(_ context.Context, _ *Client, c *containers.Container) error { + if c.Labels == nil { + c.Labels = labels + return nil + } + for k, v := range labels { + c.Labels[k] = v + } + return nil + } +} + // WithImageStopSignal sets a well-known containerd label (StopSignalLabel) // on the container for storing the stop signal specified in the OCI image // config @@ -106,9 +178,17 @@ func WithSnapshotter(name string) NewContainerOpts { // WithSnapshot uses an existing root filesystem for the container func WithSnapshot(id string) NewContainerOpts { return func(ctx context.Context, client *Client, c *containers.Container) error { - setSnapshotterIfEmpty(c) // check that the snapshot exists, if not, fail on creation - if _, err := client.SnapshotService(c.Snapshotter).Mounts(ctx, id); err != nil { + var err error + c.Snapshotter, err = client.resolveSnapshotterName(ctx, c.Snapshotter) + if err != nil { + return err + } + s, err := client.getSnapshotter(ctx, c.Snapshotter) + if err != nil { + return err + } + if _, err := s.Mounts(ctx, id); err != nil { return err } c.SnapshotKey = id @@ -118,15 +198,23 @@ func WithSnapshot(id string) NewContainerOpts { // WithNewSnapshot allocates a new snapshot to be used by the container as the // root filesystem in read-write mode -func WithNewSnapshot(id string, i Image) NewContainerOpts { +func WithNewSnapshot(id string, i Image, opts ...snapshots.Opt) NewContainerOpts { return func(ctx context.Context, client *Client, c *containers.Container) error { - diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), platforms.Default()) + diffIDs, err := i.RootFS(ctx) if err != nil { return err } - setSnapshotterIfEmpty(c) + parent := identity.ChainID(diffIDs).String() - if _, err := client.SnapshotService(c.Snapshotter).Prepare(ctx, id, parent); err != nil { + c.Snapshotter, err = client.resolveSnapshotterName(ctx, c.Snapshotter) + if err != nil { + return err + } + s, err := client.getSnapshotter(ctx, c.Snapshotter) + if err != nil { + return err + } + if _, err := s.Prepare(ctx, id, parent, opts...); err != nil { return err } c.SnapshotKey = id @@ -141,22 +229,36 @@ func WithSnapshotCleanup(ctx context.Context, client *Client, c containers.Conta if c.Snapshotter == "" { return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Snapshotter must be set to cleanup rootfs snapshot") } - return client.SnapshotService(c.Snapshotter).Remove(ctx, c.SnapshotKey) + s, err := client.getSnapshotter(ctx, c.Snapshotter) + if err != nil { + return err + } + if err := s.Remove(ctx, c.SnapshotKey); err != nil && !errdefs.IsNotFound(err) { + return err + } } return nil } // WithNewSnapshotView allocates a new snapshot to be used by the container as the // root filesystem in read-only mode -func WithNewSnapshotView(id string, i Image) NewContainerOpts { +func WithNewSnapshotView(id string, i Image, opts ...snapshots.Opt) NewContainerOpts { return func(ctx context.Context, client *Client, c *containers.Container) error { - diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), platforms.Default()) + diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), client.platform) if err != nil { return err } - setSnapshotterIfEmpty(c) + parent := identity.ChainID(diffIDs).String() - if _, err := client.SnapshotService(c.Snapshotter).View(ctx, id, parent); err != nil { + c.Snapshotter, err = client.resolveSnapshotterName(ctx, c.Snapshotter) + if err != nil { + return err + } + s, err := client.getSnapshotter(ctx, c.Snapshotter) + if err != nil { + return err + } + if _, err := s.View(ctx, id, parent, opts...); err != nil { return err } c.SnapshotKey = id @@ -165,12 +267,6 @@ func WithNewSnapshotView(id string, i Image) NewContainerOpts { } } -func setSnapshotterIfEmpty(c *containers.Container) { - if c.Snapshotter == "" { - c.Snapshotter = DefaultSnapshotter - } -} - // WithContainerExtension appends extension data to the container object. // Use this to decorate the container object with additional data for the client // integration. @@ -185,7 +281,7 @@ func WithContainerExtension(name string, extension interface{}) NewContainerOpts any, err := typeurl.MarshalAny(extension) if err != nil { - if errors.Cause(err) == typeurl.ErrNotFound { + if errors.Is(err, typeurl.ErrNotFound) { return errors.Wrapf(err, "extension %q is not registered with the typeurl package, see `typeurl.Register`", name) } return errors.Wrap(err, "error marshalling extension") @@ -223,3 +319,8 @@ func WithSpec(s *oci.Spec, opts ...oci.SpecOpts) NewContainerOpts { return err } } + +// WithoutRefreshedMetadata will use the current metadata attached to the container object +func WithoutRefreshedMetadata(i *InfoConfig) { + i.Refresh = false +} diff --git a/vendor/github.com/containerd/containerd/container_opts_unix.go b/vendor/github.com/containerd/containerd/container_opts_unix.go index c0622f67fe8df..b109a10ecb4bb 100644 --- a/vendor/github.com/containerd/containerd/container_opts_unix.go +++ b/vendor/github.com/containerd/containerd/container_opts_unix.go @@ -26,81 +26,11 @@ import ( "syscall" "github.com/containerd/containerd/containers" - "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/images" "github.com/containerd/containerd/mount" - "github.com/containerd/containerd/platforms" - "github.com/gogo/protobuf/proto" - protobuf "github.com/gogo/protobuf/types" "github.com/opencontainers/image-spec/identity" - "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" ) -// WithCheckpoint allows a container to be created from the checkpointed information -// provided by the descriptor. The image, snapshot, and runtime specifications are -// restored on the container -func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts { - // set image and rw, and spec - return func(ctx context.Context, client *Client, c *containers.Container) error { - var ( - desc = im.Target() - store = client.ContentStore() - ) - index, err := decodeIndex(ctx, store, desc) - if err != nil { - return err - } - var rw *v1.Descriptor - for _, m := range index.Manifests { - switch m.MediaType { - case v1.MediaTypeImageLayer: - fk := m - rw = &fk - case images.MediaTypeDockerSchema2Manifest, images.MediaTypeDockerSchema2ManifestList: - config, err := images.Config(ctx, store, m, platforms.Default()) - if err != nil { - return errors.Wrap(err, "unable to resolve image config") - } - diffIDs, err := images.RootFS(ctx, store, config) - if err != nil { - return errors.Wrap(err, "unable to get rootfs") - } - setSnapshotterIfEmpty(c) - if _, err := client.SnapshotService(c.Snapshotter).Prepare(ctx, snapshotKey, identity.ChainID(diffIDs).String()); err != nil { - if !errdefs.IsAlreadyExists(err) { - return err - } - } - c.Image = index.Annotations["image.name"] - case images.MediaTypeContainerd1CheckpointConfig: - data, err := content.ReadBlob(ctx, store, m) - if err != nil { - return errors.Wrap(err, "unable to read checkpoint config") - } - var any protobuf.Any - if err := proto.Unmarshal(data, &any); err != nil { - return err - } - c.Spec = &any - } - } - if rw != nil { - // apply the rw snapshot to the new rw layer - mounts, err := client.SnapshotService(c.Snapshotter).Mounts(ctx, snapshotKey) - if err != nil { - return errors.Wrapf(err, "unable to get mounts for %s", snapshotKey) - } - if _, err := client.DiffService().Apply(ctx, *rw, mounts); err != nil { - return errors.Wrap(err, "unable to apply rw diff") - } - } - c.SnapshotKey = snapshotKey - return nil - } -} - // WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the // filesystem to be used by a container with user namespaces func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts { @@ -114,18 +44,23 @@ func WithRemappedSnapshotView(id string, i Image, uid, gid uint32) NewContainerO func withRemappedSnapshotBase(id string, i Image, uid, gid uint32, readonly bool) NewContainerOpts { return func(ctx context.Context, client *Client, c *containers.Container) error { - diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), platforms.Default()) + diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), client.platform) if err != nil { return err } - setSnapshotterIfEmpty(c) - var ( - snapshotter = client.SnapshotService(c.Snapshotter) - parent = identity.ChainID(diffIDs).String() - usernsID = fmt.Sprintf("%s-%d-%d", parent, uid, gid) + parent = identity.ChainID(diffIDs).String() + usernsID = fmt.Sprintf("%s-%d-%d", parent, uid, gid) ) + c.Snapshotter, err = client.resolveSnapshotterName(ctx, c.Snapshotter) + if err != nil { + return err + } + snapshotter, err := client.getSnapshotter(ctx, c.Snapshotter) + if err != nil { + return err + } if _, err := snapshotter.Stat(ctx, usernsID); err == nil { if _, err := snapshotter.Prepare(ctx, id, usernsID); err == nil { c.SnapshotKey = id diff --git a/vendor/github.com/containerd/containerd/container_restore_opts.go b/vendor/github.com/containerd/containerd/container_restore_opts.go new file mode 100644 index 0000000000000..fb60e8de9d695 --- /dev/null +++ b/vendor/github.com/containerd/containerd/container_restore_opts.go @@ -0,0 +1,149 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "context" + + "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/gogo/protobuf/proto" + ptypes "github.com/gogo/protobuf/types" + "github.com/opencontainers/image-spec/identity" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +var ( + // ErrImageNameNotFoundInIndex is returned when the image name is not found in the index + ErrImageNameNotFoundInIndex = errors.New("image name not found in index") + // ErrRuntimeNameNotFoundInIndex is returned when the runtime is not found in the index + ErrRuntimeNameNotFoundInIndex = errors.New("runtime not found in index") + // ErrSnapshotterNameNotFoundInIndex is returned when the snapshotter is not found in the index + ErrSnapshotterNameNotFoundInIndex = errors.New("snapshotter not found in index") +) + +// RestoreOpts are options to manage the restore operation +type RestoreOpts func(context.Context, string, *Client, Image, *imagespec.Index) NewContainerOpts + +// WithRestoreImage restores the image for the container +func WithRestoreImage(ctx context.Context, id string, client *Client, checkpoint Image, index *imagespec.Index) NewContainerOpts { + return func(ctx context.Context, client *Client, c *containers.Container) error { + name, ok := index.Annotations[checkpointImageNameLabel] + if !ok || name == "" { + return ErrRuntimeNameNotFoundInIndex + } + snapshotter, ok := index.Annotations[checkpointSnapshotterNameLabel] + if !ok || name == "" { + return ErrSnapshotterNameNotFoundInIndex + } + i, err := client.GetImage(ctx, name) + if err != nil { + return err + } + + diffIDs, err := i.(*image).i.RootFS(ctx, client.ContentStore(), client.platform) + if err != nil { + return err + } + parent := identity.ChainID(diffIDs).String() + if _, err := client.SnapshotService(snapshotter).Prepare(ctx, id, parent); err != nil { + return err + } + c.Image = i.Name() + c.SnapshotKey = id + c.Snapshotter = snapshotter + return nil + } +} + +// WithRestoreRuntime restores the runtime for the container +func WithRestoreRuntime(ctx context.Context, id string, client *Client, checkpoint Image, index *imagespec.Index) NewContainerOpts { + return func(ctx context.Context, client *Client, c *containers.Container) error { + name, ok := index.Annotations[checkpointRuntimeNameLabel] + if !ok { + return ErrRuntimeNameNotFoundInIndex + } + + // restore options if present + m, err := GetIndexByMediaType(index, images.MediaTypeContainerd1CheckpointRuntimeOptions) + if err != nil { + if err != ErrMediaTypeNotFound { + return err + } + } + var options ptypes.Any + if m != nil { + store := client.ContentStore() + data, err := content.ReadBlob(ctx, store, *m) + if err != nil { + return errors.Wrap(err, "unable to read checkpoint runtime") + } + if err := proto.Unmarshal(data, &options); err != nil { + return err + } + } + + c.Runtime = containers.RuntimeInfo{ + Name: name, + Options: &options, + } + return nil + } +} + +// WithRestoreSpec restores the spec from the checkpoint for the container +func WithRestoreSpec(ctx context.Context, id string, client *Client, checkpoint Image, index *imagespec.Index) NewContainerOpts { + return func(ctx context.Context, client *Client, c *containers.Container) error { + m, err := GetIndexByMediaType(index, images.MediaTypeContainerd1CheckpointConfig) + if err != nil { + return err + } + store := client.ContentStore() + data, err := content.ReadBlob(ctx, store, *m) + if err != nil { + return errors.Wrap(err, "unable to read checkpoint config") + } + var any ptypes.Any + if err := proto.Unmarshal(data, &any); err != nil { + return err + } + c.Spec = &any + return nil + } +} + +// WithRestoreRW restores the rw layer from the checkpoint for the container +func WithRestoreRW(ctx context.Context, id string, client *Client, checkpoint Image, index *imagespec.Index) NewContainerOpts { + return func(ctx context.Context, client *Client, c *containers.Container) error { + // apply rw layer + rw, err := GetIndexByMediaType(index, imagespec.MediaTypeImageLayerGzip) + if err != nil { + return err + } + mounts, err := client.SnapshotService(c.Snapshotter).Mounts(ctx, c.SnapshotKey) + if err != nil { + return err + } + + if _, err := client.DiffService().Apply(ctx, *rw, mounts); err != nil { + return err + } + return nil + } +} diff --git a/vendor/github.com/containerd/containerd/containers/containers.go b/vendor/github.com/containerd/containerd/containers/containers.go index a658b57082d70..7174bbd6aa6fb 100644 --- a/vendor/github.com/containerd/containerd/containers/containers.go +++ b/vendor/github.com/containerd/containerd/containers/containers.go @@ -49,7 +49,7 @@ type Container struct { // This property is required and immutable. Runtime RuntimeInfo - // Spec should carry the the runtime specification used to implement the + // Spec should carry the runtime specification used to implement the // container. // // This field is required but mutable. @@ -86,6 +86,10 @@ type RuntimeInfo struct { // Store interacts with the underlying container storage type Store interface { + // Get a container using the id. + // + // Container object is returned on success. If the id is not known to the + // store, an error will be returned. Get(ctx context.Context, id string) (Container, error) // List returns containers that match one or more of the provided filters. diff --git a/vendor/github.com/containerd/containerd/content/adaptor.go b/vendor/github.com/containerd/containerd/content/adaptor.go new file mode 100644 index 0000000000000..88bad2610e8af --- /dev/null +++ b/vendor/github.com/containerd/containerd/content/adaptor.go @@ -0,0 +1,52 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package content + +import ( + "strings" + + "github.com/containerd/containerd/filters" +) + +// AdaptInfo returns `filters.Adaptor` that handles `content.Info`. +func AdaptInfo(info Info) filters.Adaptor { + return filters.AdapterFunc(func(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "digest": + return info.Digest.String(), true + case "size": + // TODO: support size based filtering + case "labels": + return checkMap(fieldpath[1:], info.Labels) + } + + return "", false + }) +} + +func checkMap(fieldpath []string, m map[string]string) (string, bool) { + if len(m) == 0 { + return "", false + } + + value, ok := m[strings.Join(fieldpath, ".")] + return value, ok +} diff --git a/vendor/github.com/containerd/containerd/content/content.go b/vendor/github.com/containerd/containerd/content/content.go index aabf4c8f31ea5..ff17a8417b39b 100644 --- a/vendor/github.com/containerd/containerd/content/content.go +++ b/vendor/github.com/containerd/containerd/content/content.go @@ -37,7 +37,7 @@ type Provider interface { // ReaderAt only requires desc.Digest to be set. // Other fields in the descriptor may be used internally for resolving // the location of the actual data. - ReaderAt(ctx context.Context, dec ocispec.Descriptor) (ReaderAt, error) + ReaderAt(ctx context.Context, desc ocispec.Descriptor) (ReaderAt, error) } // Ingester writes content @@ -110,8 +110,9 @@ type IngestManager interface { // Writer handles the write of content into a content store type Writer interface { - // Close is expected to be called after Commit() when commission is needed. - // Closing a writer without commit allows resuming or aborting. + // Close closes the writer, if the writer has not been + // committed this allows resuming or aborting. + // Calling Close on a closed writer will not error. io.WriteCloser // Digest may return empty digest or panics until committed. @@ -119,6 +120,8 @@ type Writer interface { // Commit commits the blob (but no roll-back is guaranteed on an error). // size and expected can be zero-value when unknown. + // Commit always closes the writer, even on error. + // ErrAlreadyExists aborts the writer. Commit(ctx context.Context, size int64, expected digest.Digest, opts ...Opt) error // Status returns the current state of write diff --git a/vendor/github.com/containerd/containerd/content/helpers.go b/vendor/github.com/containerd/containerd/content/helpers.go index 3e231408d55ec..00fae1fc80d87 100644 --- a/vendor/github.com/containerd/containerd/content/helpers.go +++ b/vendor/github.com/containerd/containerd/content/helpers.go @@ -55,7 +55,14 @@ func ReadBlob(ctx context.Context, provider Provider, desc ocispec.Descriptor) ( p := make([]byte, ra.Size()) - _, err = ra.ReadAt(p, 0) + n, err := ra.ReadAt(p, 0) + if err == io.EOF { + if int64(n) != ra.Size() { + err = io.ErrUnexpectedEOF + } else { + err = nil + } + } return p, err } @@ -137,9 +144,14 @@ func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected dige } } - if _, err := copyWithBuffer(cw, r); err != nil { + copied, err := copyWithBuffer(cw, r) + if err != nil { return errors.Wrap(err, "failed to copy") } + if size != 0 && copied < size-ws.Offset { + // Short writes would return its own error, this indicates a read failure + return errors.Wrapf(io.ErrUnexpectedEOF, "failed to read expected number of bytes") + } if err := cw.Commit(ctx, size, expected, opts...); err != nil { if !errdefs.IsAlreadyExists(err) { @@ -158,8 +170,37 @@ func CopyReaderAt(cw Writer, ra ReaderAt, n int64) error { return err } - _, err = copyWithBuffer(cw, io.NewSectionReader(ra, ws.Offset, n)) - return err + copied, err := copyWithBuffer(cw, io.NewSectionReader(ra, ws.Offset, n)) + if err != nil { + return errors.Wrap(err, "failed to copy") + } + if copied < n { + // Short writes would return its own error, this indicates a read failure + return errors.Wrap(io.ErrUnexpectedEOF, "failed to read expected number of bytes") + } + return nil +} + +// CopyReader copies to a writer from a given reader, returning +// the number of bytes copied. +// Note: if the writer has a non-zero offset, the total number +// of bytes read may be greater than those copied if the reader +// is not an io.Seeker. +// This copy does not commit the writer. +func CopyReader(cw Writer, r io.Reader) (int64, error) { + ws, err := cw.Status() + if err != nil { + return 0, errors.Wrap(err, "failed to get status") + } + + if ws.Offset > 0 { + r, err = seekReader(r, ws.Offset, 0) + if err != nil { + return 0, errors.Wrapf(err, "unable to resume write to %v", ws.Ref) + } + } + + return copyWithBuffer(cw, r) } // seekReader attempts to seek the reader to the given offset, either by @@ -200,9 +241,47 @@ func seekReader(r io.Reader, offset, size int64) (io.Reader, error) { return r, nil } +// copyWithBuffer is very similar to io.CopyBuffer https://golang.org/pkg/io/#CopyBuffer +// but instead of using Read to read from the src, we use ReadAtLeast to make sure we have +// a full buffer before we do a write operation to dst to reduce overheads associated +// with the write operations of small buffers. func copyWithBuffer(dst io.Writer, src io.Reader) (written int64, err error) { - buf := bufPool.Get().(*[]byte) - written, err = io.CopyBuffer(dst, src, *buf) - bufPool.Put(buf) + // If the reader has a WriteTo method, use it to do the copy. + // Avoids an allocation and a copy. + if wt, ok := src.(io.WriterTo); ok { + return wt.WriteTo(dst) + } + // Similarly, if the writer has a ReadFrom method, use it to do the copy. + if rt, ok := dst.(io.ReaderFrom); ok { + return rt.ReadFrom(src) + } + bufRef := bufPool.Get().(*[]byte) + defer bufPool.Put(bufRef) + buf := *bufRef + for { + nr, er := io.ReadAtLeast(src, buf, len(buf)) + if nr > 0 { + nw, ew := dst.Write(buf[0:nr]) + if nw > 0 { + written += int64(nw) + } + if ew != nil { + err = ew + break + } + if nr != nw { + err = io.ErrShortWrite + break + } + } + if er != nil { + // If an EOF happens after reading fewer than the requested bytes, + // ReadAtLeast returns ErrUnexpectedEOF. + if er != io.EOF && er != io.ErrUnexpectedEOF { + err = er + } + break + } + } return } diff --git a/vendor/github.com/containerd/containerd/content/local/locks.go b/vendor/github.com/containerd/containerd/content/local/locks.go index 411c29a9d914e..d1d2d564df247 100644 --- a/vendor/github.com/containerd/containerd/content/local/locks.go +++ b/vendor/github.com/containerd/containerd/content/local/locks.go @@ -18,6 +18,7 @@ package local import ( "sync" + "time" "github.com/containerd/containerd/errdefs" "github.com/pkg/errors" @@ -25,9 +26,13 @@ import ( // Handles locking references +type lock struct { + since time.Time +} + var ( // locks lets us lock in process - locks = map[string]struct{}{} + locks = make(map[string]*lock) locksMu sync.Mutex ) @@ -35,11 +40,11 @@ func tryLock(ref string) error { locksMu.Lock() defer locksMu.Unlock() - if _, ok := locks[ref]; ok { - return errors.Wrapf(errdefs.ErrUnavailable, "ref %s locked", ref) + if v, ok := locks[ref]; ok { + return errors.Wrapf(errdefs.ErrUnavailable, "ref %s locked since %s", ref, v.since) } - locks[ref] = struct{}{} + locks[ref] = &lock{time.Now()} return nil } @@ -47,7 +52,5 @@ func unlock(ref string) { locksMu.Lock() defer locksMu.Unlock() - if _, ok := locks[ref]; ok { - delete(locks, ref) - } + delete(locks, ref) } diff --git a/vendor/github.com/containerd/containerd/content/local/readerat.go b/vendor/github.com/containerd/containerd/content/local/readerat.go index 42b99dc42bb5e..5d3ae03903822 100644 --- a/vendor/github.com/containerd/containerd/content/local/readerat.go +++ b/vendor/github.com/containerd/containerd/content/local/readerat.go @@ -18,6 +18,11 @@ package local import ( "os" + + "github.com/pkg/errors" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" ) // readerat implements io.ReaderAt in a completely stateless manner by opening @@ -27,6 +32,29 @@ type sizeReaderAt struct { fp *os.File } +// OpenReader creates ReaderAt from a file +func OpenReader(p string) (content.ReaderAt, error) { + fi, err := os.Stat(p) + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + + return nil, errors.Wrap(errdefs.ErrNotFound, "blob not found") + } + + fp, err := os.Open(p) + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + + return nil, errors.Wrap(errdefs.ErrNotFound, "blob not found") + } + + return sizeReaderAt{size: fi.Size(), fp: fp}, nil +} + func (ra sizeReaderAt) ReadAt(p []byte, offset int64) (int, error) { return ra.fp.ReadAt(p, offset) } diff --git a/vendor/github.com/containerd/containerd/content/local/store.go b/vendor/github.com/containerd/containerd/content/local/store.go index 7fa9bb736a9e3..314d913673bb1 100644 --- a/vendor/github.com/containerd/containerd/content/local/store.go +++ b/vendor/github.com/containerd/containerd/content/local/store.go @@ -33,8 +33,8 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/filters" "github.com/containerd/containerd/log" + "github.com/sirupsen/logrus" - "github.com/containerd/continuity" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -92,7 +92,11 @@ func NewLabeledStore(root string, ls LabelStore) (content.Store, error) { } func (s *store) Info(ctx context.Context, dgst digest.Digest) (content.Info, error) { - p := s.blobPath(dgst) + p, err := s.blobPath(dgst) + if err != nil { + return content.Info{}, errors.Wrapf(err, "calculating blob info path") + } + fi, err := os.Stat(p) if err != nil { if os.IsNotExist(err) { @@ -123,26 +127,17 @@ func (s *store) info(dgst digest.Digest, fi os.FileInfo, labels map[string]strin // ReaderAt returns an io.ReaderAt for the blob. func (s *store) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) { - p := s.blobPath(desc.Digest) - fi, err := os.Stat(p) + p, err := s.blobPath(desc.Digest) if err != nil { - if !os.IsNotExist(err) { - return nil, err - } - - return nil, errors.Wrapf(errdefs.ErrNotFound, "blob %s expected at %s", desc.Digest, p) + return nil, errors.Wrapf(err, "calculating blob path for ReaderAt") } - fp, err := os.Open(p) + reader, err := OpenReader(p) if err != nil { - if !os.IsNotExist(err) { - return nil, err - } - - return nil, errors.Wrapf(errdefs.ErrNotFound, "blob %s expected at %s", desc.Digest, p) + return nil, errors.Wrapf(err, "blob %s expected at %s", desc.Digest, p) } - return sizeReaderAt{size: fi.Size(), fp: fp}, nil + return reader, nil } // Delete removes a blob by its digest. @@ -150,7 +145,12 @@ func (s *store) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content. // While this is safe to do concurrently, safe exist-removal logic must hold // some global lock on the store. func (s *store) Delete(ctx context.Context, dgst digest.Digest) error { - if err := os.RemoveAll(s.blobPath(dgst)); err != nil { + bp, err := s.blobPath(dgst) + if err != nil { + return errors.Wrapf(err, "calculating blob path for delete") + } + + if err := os.RemoveAll(bp); err != nil { if !os.IsNotExist(err) { return err } @@ -166,7 +166,11 @@ func (s *store) Update(ctx context.Context, info content.Info, fieldpaths ...str return content.Info{}, errors.Wrapf(errdefs.ErrFailedPrecondition, "update not supported on immutable content store") } - p := s.blobPath(info.Digest) + p, err := s.blobPath(info.Digest) + if err != nil { + return content.Info{}, errors.Wrapf(err, "calculating blob path for update") + } + fi, err := os.Stat(p) if err != nil { if os.IsNotExist(err) { @@ -224,9 +228,14 @@ func (s *store) Update(ctx context.Context, info content.Info, fieldpaths ...str return info, nil } -func (s *store) Walk(ctx context.Context, fn content.WalkFunc, filters ...string) error { - // TODO: Support filters +func (s *store) Walk(ctx context.Context, fn content.WalkFunc, fs ...string) error { root := filepath.Join(s.root, "blobs") + + filter, err := filters.ParseAll(fs...) + if err != nil { + return err + } + var alg digest.Algorithm return filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { if err != nil { @@ -270,7 +279,12 @@ func (s *store) Walk(ctx context.Context, fn content.WalkFunc, filters ...string return err } } - return fn(s.info(dgst, fi, labels)) + + info := s.info(dgst, fi, labels) + if !filter.Match(content.AdaptInfo(info)) { + return nil + } + return fn(info) }) } @@ -451,7 +465,6 @@ func (s *store) Writer(ctx context.Context, opts ...content.WriterOpt) (content. } var lockErr error for count := uint64(0); count < 10; count++ { - time.Sleep(time.Millisecond * time.Duration(rand.Intn(1< 0 && status.Total > 0 && total != status.Total { + return status, errors.Errorf("provided total differs from status: %v != %v", total, status.Total) + } + + // TODO(stevvooe): slow slow slow!!, send to goroutine or use resumable hashes + fp, err := os.Open(data) + if err != nil { + return status, err + } + + p := bufPool.Get().(*[]byte) + status.Offset, err = io.CopyBuffer(digester.Hash(), fp, *p) + bufPool.Put(p) + fp.Close() + return status, err +} + // writer provides the main implementation of the Writer method. The caller // must hold the lock correctly and release on error if there is a problem. func (s *store) writer(ctx context.Context, ref string, total int64, expected digest.Digest) (content.Writer, error) { // TODO(stevvooe): Need to actually store expected here. We have // code in the service that shouldn't be dealing with this. if expected != "" { - p := s.blobPath(expected) + p, err := s.blobPath(expected) + if err != nil { + return nil, errors.Wrap(err, "calculating expected blob path for writer") + } if _, err := os.Stat(p); err == nil { return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", expected) } @@ -498,46 +544,25 @@ func (s *store) writer(ctx context.Context, ref string, total int64, expected di updatedAt time.Time ) + foundValidIngest := false // ensure that the ingest path has been created. if err := os.Mkdir(path, 0755); err != nil { if !os.IsExist(err) { return nil, err } - - status, err := s.status(path) - if err != nil { - return nil, errors.Wrap(err, "failed reading status of resume write") - } - - if ref != status.Ref { - // NOTE(stevvooe): This is fairly catastrophic. Either we have some - // layout corruption or a hash collision for the ref key. - return nil, errors.Wrapf(err, "ref key does not match: %v != %v", ref, status.Ref) - } - - if total > 0 && status.Total > 0 && total != status.Total { - return nil, errors.Errorf("provided total differs from status: %v != %v", total, status.Total) - } - - // TODO(stevvooe): slow slow slow!!, send to goroutine or use resumable hashes - fp, err := os.Open(data) - if err != nil { - return nil, err - } - defer fp.Close() - - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - - offset, err = io.CopyBuffer(digester.Hash(), fp, *p) - if err != nil { - return nil, err + status, err := s.resumeStatus(ref, total, digester) + if err == nil { + foundValidIngest = true + updatedAt = status.UpdatedAt + startedAt = status.StartedAt + total = status.Total + offset = status.Offset + } else { + logrus.Infof("failed to resume the status from path %s: %s. will recreate them", path, err.Error()) } + } - updatedAt = status.UpdatedAt - startedAt = status.StartedAt - total = status.Total - } else { + if !foundValidIngest { startedAt = time.Now() updatedAt = startedAt @@ -547,11 +572,11 @@ func (s *store) writer(ctx context.Context, ref string, total int64, expected di return nil, err } - if writeTimestampFile(filepath.Join(path, "startedat"), startedAt); err != nil { + if err := writeTimestampFile(filepath.Join(path, "startedat"), startedAt); err != nil { return nil, err } - if writeTimestampFile(filepath.Join(path, "updatedat"), startedAt); err != nil { + if err := writeTimestampFile(filepath.Join(path, "updatedat"), startedAt); err != nil { return nil, err } @@ -599,11 +624,17 @@ func (s *store) Abort(ctx context.Context, ref string) error { return nil } -func (s *store) blobPath(dgst digest.Digest) string { - return filepath.Join(s.root, "blobs", dgst.Algorithm().String(), dgst.Hex()) +func (s *store) blobPath(dgst digest.Digest) (string, error) { + if err := dgst.Validate(); err != nil { + return "", errors.Wrapf(errdefs.ErrInvalidArgument, "cannot calculate blob path from invalid digest: %v", err) + } + + return filepath.Join(s.root, "blobs", dgst.Algorithm().String(), dgst.Hex()), nil } func (s *store) ingestRoot(ref string) string { + // we take a digest of the ref to keep the ingest paths constant length. + // Note that this is not the current or potential digest of incoming content. dgst := digest.FromString(ref) return filepath.Join(s.root, "ingest", dgst.Hex()) } @@ -652,6 +683,19 @@ func writeTimestampFile(p string, t time.Time) error { if err != nil { return err } + return atomicWrite(p, b, 0666) +} - return continuity.AtomicWriteFile(p, b, 0666) +func atomicWrite(path string, data []byte, mode os.FileMode) error { + tmp := fmt.Sprintf("%s.tmp", path) + f, err := os.OpenFile(tmp, os.O_RDWR|os.O_CREATE|os.O_TRUNC|os.O_SYNC, mode) + if err != nil { + return errors.Wrap(err, "create tmp file") + } + _, err = f.Write(data) + f.Close() + if err != nil { + return errors.Wrap(err, "write atomic data") + } + return os.Rename(tmp, path) } diff --git a/vendor/github.com/containerd/containerd/content/local/store_bsd.go b/vendor/github.com/containerd/containerd/content/local/store_bsd.go new file mode 100644 index 0000000000000..da149a2fdae10 --- /dev/null +++ b/vendor/github.com/containerd/containerd/content/local/store_bsd.go @@ -0,0 +1,33 @@ +// +build darwin freebsd netbsd + +/* + Copyright The containerd Authors. + + 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. +*/ + +package local + +import ( + "os" + "syscall" + "time" +) + +func getATime(fi os.FileInfo) time.Time { + if st, ok := fi.Sys().(*syscall.Stat_t); ok { + return time.Unix(int64(st.Atimespec.Sec), int64(st.Atimespec.Nsec)) //nolint: unconvert // int64 conversions ensure the line compiles for 32-bit systems as well. + } + + return fi.ModTime() +} diff --git a/vendor/github.com/containerd/containerd/content/local/store_openbsd.go b/vendor/github.com/containerd/containerd/content/local/store_openbsd.go new file mode 100644 index 0000000000000..f34f0dad2b986 --- /dev/null +++ b/vendor/github.com/containerd/containerd/content/local/store_openbsd.go @@ -0,0 +1,33 @@ +// +build openbsd + +/* + Copyright The containerd Authors. + + 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. +*/ + +package local + +import ( + "os" + "syscall" + "time" +) + +func getATime(fi os.FileInfo) time.Time { + if st, ok := fi.Sys().(*syscall.Stat_t); ok { + return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) //nolint: unconvert // int64 conversions ensure the line compiles for 32-bit systems as well. + } + + return fi.ModTime() +} diff --git a/vendor/github.com/containerd/containerd/content/local/store_unix.go b/vendor/github.com/containerd/containerd/content/local/store_unix.go index f5f34fd0cde71..69a74bab0eab0 100644 --- a/vendor/github.com/containerd/containerd/content/local/store_unix.go +++ b/vendor/github.com/containerd/containerd/content/local/store_unix.go @@ -1,4 +1,4 @@ -// +build linux solaris darwin freebsd +// +build linux solaris /* Copyright The containerd Authors. @@ -22,13 +22,11 @@ import ( "os" "syscall" "time" - - "github.com/containerd/containerd/sys" ) func getATime(fi os.FileInfo) time.Time { if st, ok := fi.Sys().(*syscall.Stat_t); ok { - return sys.StatATimeAsTime(st) + return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) //nolint: unconvert // int64 conversions ensure the line compiles for 32-bit systems as well. } return fi.ModTime() diff --git a/vendor/github.com/containerd/containerd/content/local/writer.go b/vendor/github.com/containerd/containerd/content/local/writer.go index 10df4a4c54084..0a11f4d912eac 100644 --- a/vendor/github.com/containerd/containerd/content/local/writer.go +++ b/vendor/github.com/containerd/containerd/content/local/writer.go @@ -26,6 +26,7 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/log" "github.com/opencontainers/go-digest" "github.com/pkg/errors" ) @@ -73,6 +74,9 @@ func (w *writer) Write(p []byte) (n int, err error) { } func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error { + // Ensure even on error the writer is fully closed + defer unlock(w.ref) + var base content.Info for _, opt := range opts { if err := opt(&base); err != nil { @@ -80,48 +84,39 @@ func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest, } } - if w.fp == nil { + fp := w.fp + w.fp = nil + + if fp == nil { return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot commit on closed writer") } - if err := w.fp.Sync(); err != nil { + if err := fp.Sync(); err != nil { + fp.Close() return errors.Wrap(err, "sync failed") } - fi, err := w.fp.Stat() + fi, err := fp.Stat() + closeErr := fp.Close() if err != nil { return errors.Wrap(err, "stat on ingest file failed") } - - // change to readonly, more important for read, but provides _some_ - // protection from this point on. We use the existing perms with a mask - // only allowing reads honoring the umask on creation. - // - // This removes write and exec, only allowing read per the creation umask. - // - // NOTE: Windows does not support this operation - if runtime.GOOS != "windows" { - if err := w.fp.Chmod((fi.Mode() & os.ModePerm) &^ 0333); err != nil { - return errors.Wrap(err, "failed to change ingest file permissions") - } + if closeErr != nil { + return errors.Wrap(err, "failed to close ingest file") } if size > 0 && size != fi.Size() { - return errors.Errorf("unexpected commit size %d, expected %d", fi.Size(), size) - } - - if err := w.fp.Close(); err != nil { - return errors.Wrap(err, "failed closing ingest") + return errors.Wrapf(errdefs.ErrFailedPrecondition, "unexpected commit size %d, expected %d", fi.Size(), size) } dgst := w.digester.Digest() if expected != "" && expected != dgst { - return errors.Errorf("unexpected commit digest %s, expected %s", dgst, expected) + return errors.Wrapf(errdefs.ErrFailedPrecondition, "unexpected commit digest %s, expected %s", dgst, expected) } var ( - ingest = filepath.Join(w.path, "data") - target = w.s.blobPath(dgst) + ingest = filepath.Join(w.path, "data") + target, _ = w.s.blobPath(dgst) // ignore error because we calculated this dgst ) // make sure parent directories of blob exist @@ -129,27 +124,48 @@ func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest, return err } - // clean up!! - defer os.RemoveAll(w.path) - if _, err := os.Stat(target); err == nil { // collision with the target file! + if err := os.RemoveAll(w.path); err != nil { + log.G(ctx).WithField("ref", w.ref).WithField("path", w.path).Errorf("failed to remove ingest directory") + } return errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", dgst) } + if err := os.Rename(ingest, target); err != nil { return err } + + // Ingest has now been made available in the content store, attempt to complete + // setting metadata but errors should only be logged and not returned since + // the content store cannot be cleanly rolled back. + commitTime := time.Now() if err := os.Chtimes(target, commitTime, commitTime); err != nil { - return err + log.G(ctx).WithField("digest", dgst).Errorf("failed to change file time to commit time") } - w.fp = nil - unlock(w.ref) + // clean up!! + if err := os.RemoveAll(w.path); err != nil { + log.G(ctx).WithField("ref", w.ref).WithField("path", w.path).Errorf("failed to remove ingest directory") + } if w.s.ls != nil && base.Labels != nil { if err := w.s.ls.Set(dgst, base.Labels); err != nil { - return err + log.G(ctx).WithField("digest", dgst).Errorf("failed to set labels") + } + } + + // change to readonly, more important for read, but provides _some_ + // protection from this point on. We use the existing perms with a mask + // only allowing reads honoring the umask on creation. + // + // This removes write and exec, only allowing read per the creation umask. + // + // NOTE: Windows does not support this operation + if runtime.GOOS != "windows" { + if err := os.Chmod(target, (fi.Mode()&os.ModePerm)&^0333); err != nil { + log.G(ctx).WithField("ref", w.ref).Errorf("failed to make readonly") } } diff --git a/vendor/github.com/containerd/containerd/content/proxy/content_reader.go b/vendor/github.com/containerd/containerd/content/proxy/content_reader.go index b06e48fa97235..2947a7c8217d5 100644 --- a/vendor/github.com/containerd/containerd/content/proxy/content_reader.go +++ b/vendor/github.com/containerd/containerd/content/proxy/content_reader.go @@ -40,7 +40,13 @@ func (ra *remoteReaderAt) ReadAt(p []byte, off int64) (n int, err error) { Offset: off, Size_: int64(len(p)), } - rc, err := ra.client.Read(ra.ctx, rr) + // we need a child context with cancel, or the eventually called + // grpc.NewStream will leak the goroutine until the whole thing is cleared. + // See comment at https://godoc.org/google.golang.org/grpc#ClientConn.NewStream + childCtx, cancel := context.WithCancel(ra.ctx) + // we MUST cancel the child context; see comment above + defer cancel() + rc, err := ra.client.Read(childCtx, rr) if err != nil { return 0, err } diff --git a/vendor/github.com/containerd/containerd/content/proxy/content_writer.go b/vendor/github.com/containerd/containerd/content/proxy/content_writer.go index 5434a156864cd..8423335988783 100644 --- a/vendor/github.com/containerd/containerd/content/proxy/content_writer.go +++ b/vendor/github.com/containerd/containerd/content/proxy/content_writer.go @@ -97,7 +97,14 @@ func (rw *remoteWriter) Write(p []byte) (n int, err error) { return } -func (rw *remoteWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error { +func (rw *remoteWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) (err error) { + defer func() { + err1 := rw.Close() + if err == nil { + err = err1 + } + }() + var base content.Info for _, opt := range opts { if err := opt(&base); err != nil { diff --git a/vendor/github.com/containerd/containerd/contrib/nvidia/nvidia.go b/vendor/github.com/containerd/containerd/contrib/nvidia/nvidia.go new file mode 100644 index 0000000000000..6a351771ffc38 --- /dev/null +++ b/vendor/github.com/containerd/containerd/contrib/nvidia/nvidia.go @@ -0,0 +1,211 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package nvidia + +import ( + "context" + "fmt" + "os" + "os/exec" + "strconv" + "strings" + + "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/oci" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// NvidiaCLI is the path to the Nvidia helper binary +const NvidiaCLI = "nvidia-container-cli" + +// Capability specifies capabilities for the gpu inside the container +// Detailed explanation of options can be found: +// https://github.com/nvidia/nvidia-container-runtime#supported-driver-capabilities +type Capability string + +const ( + // Compute capability + Compute Capability = "compute" + // Compat32 capability + Compat32 Capability = "compat32" + // Graphics capability + Graphics Capability = "graphics" + // Utility capability + Utility Capability = "utility" + // Video capability + Video Capability = "video" + // Display capability + Display Capability = "display" +) + +// AllCaps returns the complete list of supported Nvidia capabilities. +func AllCaps() []Capability { + return []Capability{ + Compute, + Compat32, + Graphics, + Utility, + Video, + Display, + } +} + +// WithGPUs adds NVIDIA gpu support to a container +func WithGPUs(opts ...Opts) oci.SpecOpts { + return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { + c := &config{} + for _, o := range opts { + if err := o(c); err != nil { + return err + } + } + if c.OCIHookPath == "" { + path, err := exec.LookPath("containerd") + if err != nil { + return err + } + c.OCIHookPath = path + } + nvidiaPath, err := exec.LookPath(NvidiaCLI) + if err != nil { + return err + } + if s.Hooks == nil { + s.Hooks = &specs.Hooks{} + } + s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{ + Path: c.OCIHookPath, + Args: append([]string{ + "containerd", + "oci-hook", + "--", + nvidiaPath, + // ensures the required kernel modules are properly loaded + "--load-kmods", + }, c.args()...), + Env: os.Environ(), + }) + return nil + } +} + +type config struct { + Devices []string + Capabilities []Capability + LoadKmods bool + LDCache string + LDConfig string + Requirements []string + OCIHookPath string +} + +func (c *config) args() []string { + var args []string + + if c.LoadKmods { + args = append(args, "--load-kmods") + } + if c.LDCache != "" { + args = append(args, fmt.Sprintf("--ldcache=%s", c.LDCache)) + } + args = append(args, + "configure", + ) + if len(c.Devices) > 0 { + args = append(args, fmt.Sprintf("--device=%s", strings.Join(c.Devices, ","))) + } + for _, c := range c.Capabilities { + args = append(args, fmt.Sprintf("--%s", c)) + } + if c.LDConfig != "" { + args = append(args, fmt.Sprintf("--ldconfig=%s", c.LDConfig)) + } + for _, r := range c.Requirements { + args = append(args, fmt.Sprintf("--require=%s", r)) + } + args = append(args, "--pid={{pid}}", "{{rootfs}}") + return args +} + +// Opts are options for configuring gpu support +type Opts func(*config) error + +// WithDevices adds the provided device indexes to the container +func WithDevices(ids ...int) Opts { + return func(c *config) error { + for _, i := range ids { + c.Devices = append(c.Devices, strconv.Itoa(i)) + } + return nil + } +} + +// WithDeviceUUIDs adds the specific device UUID to the container +func WithDeviceUUIDs(uuids ...string) Opts { + return func(c *config) error { + c.Devices = append(c.Devices, uuids...) + return nil + } +} + +// WithAllDevices adds all gpus to the container +func WithAllDevices(c *config) error { + c.Devices = []string{"all"} + return nil +} + +// WithAllCapabilities adds all capabilities to the container for the gpus +func WithAllCapabilities(c *config) error { + c.Capabilities = AllCaps() + return nil +} + +// WithCapabilities adds the specified capabilities to the container for the gpus +func WithCapabilities(caps ...Capability) Opts { + return func(c *config) error { + c.Capabilities = append(c.Capabilities, caps...) + return nil + } +} + +// WithRequiredCUDAVersion sets the required cuda version +func WithRequiredCUDAVersion(major, minor int) Opts { + return func(c *config) error { + c.Requirements = append(c.Requirements, fmt.Sprintf("cuda>=%d.%d", major, minor)) + return nil + } +} + +// WithOCIHookPath sets the hook path for the binary +func WithOCIHookPath(path string) Opts { + return func(c *config) error { + c.OCIHookPath = path + return nil + } +} + +// WithLookupOCIHookPath sets the hook path for the binary via a binary name +func WithLookupOCIHookPath(name string) Opts { + return func(c *config) error { + path, err := exec.LookPath(name) + if err != nil { + return err + } + c.OCIHookPath = path + return nil + } +} diff --git a/vendor/github.com/containerd/containerd/contrib/seccomp/seccomp.go b/vendor/github.com/containerd/containerd/contrib/seccomp/seccomp.go deleted file mode 100644 index 4619681f45922..0000000000000 --- a/vendor/github.com/containerd/containerd/contrib/seccomp/seccomp.go +++ /dev/null @@ -1,56 +0,0 @@ -// +build linux - -/* - Copyright The containerd Authors. - - 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. -*/ - -package seccomp - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - - "github.com/containerd/containerd/containers" - "github.com/containerd/containerd/oci" - "github.com/opencontainers/runtime-spec/specs-go" -) - -// WithProfile receives the name of a file stored on disk comprising a json -// formatted seccomp profile, as specified by the opencontainers/runtime-spec. -// The profile is read from the file, unmarshaled, and set to the spec. -func WithProfile(profile string) oci.SpecOpts { - return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { - s.Linux.Seccomp = &specs.LinuxSeccomp{} - f, err := ioutil.ReadFile(profile) - if err != nil { - return fmt.Errorf("Cannot load seccomp profile %q: %v", profile, err) - } - if err := json.Unmarshal(f, s.Linux.Seccomp); err != nil { - return fmt.Errorf("Decoding seccomp profile failed %q: %v", profile, err) - } - return nil - } -} - -// WithDefaultProfile sets the default seccomp profile to the spec. -// Note: must follow the setting of process capabilities -func WithDefaultProfile() oci.SpecOpts { - return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { - s.Linux.Seccomp = DefaultProfile(s) - return nil - } -} diff --git a/vendor/github.com/containerd/containerd/contrib/seccomp/seccomp_default.go b/vendor/github.com/containerd/containerd/contrib/seccomp/seccomp_default.go deleted file mode 100644 index 11b446a6ed10b..0000000000000 --- a/vendor/github.com/containerd/containerd/contrib/seccomp/seccomp_default.go +++ /dev/null @@ -1,581 +0,0 @@ -// +build linux - -/* - Copyright The containerd Authors. - - 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. -*/ - -package seccomp - -import ( - "runtime" - "syscall" - - "github.com/opencontainers/runtime-spec/specs-go" -) - -func arches() []specs.Arch { - switch runtime.GOARCH { - case "amd64": - return []specs.Arch{specs.ArchX86_64, specs.ArchX86, specs.ArchX32} - case "arm64": - return []specs.Arch{specs.ArchARM, specs.ArchAARCH64} - case "mips64": - return []specs.Arch{specs.ArchMIPS, specs.ArchMIPS64, specs.ArchMIPS64N32} - case "mips64n32": - return []specs.Arch{specs.ArchMIPS, specs.ArchMIPS64, specs.ArchMIPS64N32} - case "mipsel64": - return []specs.Arch{specs.ArchMIPSEL, specs.ArchMIPSEL64, specs.ArchMIPSEL64N32} - case "mipsel64n32": - return []specs.Arch{specs.ArchMIPSEL, specs.ArchMIPSEL64, specs.ArchMIPSEL64N32} - case "s390x": - return []specs.Arch{specs.ArchS390, specs.ArchS390X} - default: - return []specs.Arch{} - } -} - -// DefaultProfile defines the whitelist for the default seccomp profile. -func DefaultProfile(sp *specs.Spec) *specs.LinuxSeccomp { - syscalls := []specs.LinuxSyscall{ - { - Names: []string{ - "accept", - "accept4", - "access", - "alarm", - "alarm", - "bind", - "brk", - "capget", - "capset", - "chdir", - "chmod", - "chown", - "chown32", - "clock_getres", - "clock_gettime", - "clock_nanosleep", - "close", - "connect", - "copy_file_range", - "creat", - "dup", - "dup2", - "dup3", - "epoll_create", - "epoll_create1", - "epoll_ctl", - "epoll_ctl_old", - "epoll_pwait", - "epoll_wait", - "epoll_wait_old", - "eventfd", - "eventfd2", - "execve", - "execveat", - "exit", - "exit_group", - "faccessat", - "fadvise64", - "fadvise64_64", - "fallocate", - "fanotify_mark", - "fchdir", - "fchmod", - "fchmodat", - "fchown", - "fchown32", - "fchownat", - "fcntl", - "fcntl64", - "fdatasync", - "fgetxattr", - "flistxattr", - "flock", - "fork", - "fremovexattr", - "fsetxattr", - "fstat", - "fstat64", - "fstatat64", - "fstatfs", - "fstatfs64", - "fsync", - "ftruncate", - "ftruncate64", - "futex", - "futimesat", - "getcpu", - "getcwd", - "getdents", - "getdents64", - "getegid", - "getegid32", - "geteuid", - "geteuid32", - "getgid", - "getgid32", - "getgroups", - "getgroups32", - "getitimer", - "getpeername", - "getpgid", - "getpgrp", - "getpid", - "getppid", - "getpriority", - "getrandom", - "getresgid", - "getresgid32", - "getresuid", - "getresuid32", - "getrlimit", - "get_robust_list", - "getrusage", - "getsid", - "getsockname", - "getsockopt", - "get_thread_area", - "gettid", - "gettimeofday", - "getuid", - "getuid32", - "getxattr", - "inotify_add_watch", - "inotify_init", - "inotify_init1", - "inotify_rm_watch", - "io_cancel", - "ioctl", - "io_destroy", - "io_getevents", - "ioprio_get", - "ioprio_set", - "io_setup", - "io_submit", - "ipc", - "kill", - "lchown", - "lchown32", - "lgetxattr", - "link", - "linkat", - "listen", - "listxattr", - "llistxattr", - "_llseek", - "lremovexattr", - "lseek", - "lsetxattr", - "lstat", - "lstat64", - "madvise", - "memfd_create", - "mincore", - "mkdir", - "mkdirat", - "mknod", - "mknodat", - "mlock", - "mlock2", - "mlockall", - "mmap", - "mmap2", - "mprotect", - "mq_getsetattr", - "mq_notify", - "mq_open", - "mq_timedreceive", - "mq_timedsend", - "mq_unlink", - "mremap", - "msgctl", - "msgget", - "msgrcv", - "msgsnd", - "msync", - "munlock", - "munlockall", - "munmap", - "nanosleep", - "newfstatat", - "_newselect", - "open", - "openat", - "pause", - "pipe", - "pipe2", - "poll", - "ppoll", - "prctl", - "pread64", - "preadv", - "prlimit64", - "pselect6", - "pwrite64", - "pwritev", - "read", - "readahead", - "readlink", - "readlinkat", - "readv", - "recv", - "recvfrom", - "recvmmsg", - "recvmsg", - "remap_file_pages", - "removexattr", - "rename", - "renameat", - "renameat2", - "restart_syscall", - "rmdir", - "rt_sigaction", - "rt_sigpending", - "rt_sigprocmask", - "rt_sigqueueinfo", - "rt_sigreturn", - "rt_sigsuspend", - "rt_sigtimedwait", - "rt_tgsigqueueinfo", - "sched_getaffinity", - "sched_getattr", - "sched_getparam", - "sched_get_priority_max", - "sched_get_priority_min", - "sched_getscheduler", - "sched_rr_get_interval", - "sched_setaffinity", - "sched_setattr", - "sched_setparam", - "sched_setscheduler", - "sched_yield", - "seccomp", - "select", - "semctl", - "semget", - "semop", - "semtimedop", - "send", - "sendfile", - "sendfile64", - "sendmmsg", - "sendmsg", - "sendto", - "setfsgid", - "setfsgid32", - "setfsuid", - "setfsuid32", - "setgid", - "setgid32", - "setgroups", - "setgroups32", - "setitimer", - "setpgid", - "setpriority", - "setregid", - "setregid32", - "setresgid", - "setresgid32", - "setresuid", - "setresuid32", - "setreuid", - "setreuid32", - "setrlimit", - "set_robust_list", - "setsid", - "setsockopt", - "set_thread_area", - "set_tid_address", - "setuid", - "setuid32", - "setxattr", - "shmat", - "shmctl", - "shmdt", - "shmget", - "shutdown", - "sigaltstack", - "signalfd", - "signalfd4", - "sigreturn", - "socket", - "socketcall", - "socketpair", - "splice", - "stat", - "stat64", - "statfs", - "statfs64", - "symlink", - "symlinkat", - "sync", - "sync_file_range", - "syncfs", - "sysinfo", - "syslog", - "tee", - "tgkill", - "time", - "timer_create", - "timer_delete", - "timerfd_create", - "timerfd_gettime", - "timerfd_settime", - "timer_getoverrun", - "timer_gettime", - "timer_settime", - "times", - "tkill", - "truncate", - "truncate64", - "ugetrlimit", - "umask", - "uname", - "unlink", - "unlinkat", - "utime", - "utimensat", - "utimes", - "vfork", - "vmsplice", - "wait4", - "waitid", - "waitpid", - "write", - "writev", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }, - { - Names: []string{"personality"}, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{ - { - Index: 0, - Value: 0x0, - Op: specs.OpEqualTo, - }, - }, - }, - { - Names: []string{"personality"}, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{ - { - Index: 0, - Value: 0x0008, - Op: specs.OpEqualTo, - }, - }, - }, - { - Names: []string{"personality"}, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{ - { - Index: 0, - Value: 0xffffffff, - Op: specs.OpEqualTo, - }, - }, - }, - } - - s := &specs.LinuxSeccomp{ - DefaultAction: specs.ActErrno, - Architectures: arches(), - Syscalls: syscalls, - } - - // include by arch - switch runtime.GOARCH { - case "arm", "arm64": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "arm_fadvise64_64", - "arm_sync_file_range", - "breakpoint", - "cacheflush", - "set_tls", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "amd64": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "arch_prctl", - "modify_ldt", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "386": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "modify_ldt", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "s390", "s390x": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "s390_pci_mmio_read", - "s390_pci_mmio_write", - "s390_runtime_instr", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - } - - admin := false - for _, c := range sp.Process.Capabilities.Bounding { - switch c { - case "CAP_DAC_READ_SEARCH": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{"open_by_handle_at"}, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "CAP_SYS_ADMIN": - admin = true - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "bpf", - "clone", - "fanotify_init", - "lookup_dcookie", - "mount", - "name_to_handle_at", - "perf_event_open", - "setdomainname", - "sethostname", - "setns", - "umount", - "umount2", - "unshare", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "CAP_SYS_BOOT": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{"reboot"}, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "CAP_SYS_CHROOT": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{"chroot"}, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "CAP_SYS_MODULE": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "delete_module", - "init_module", - "finit_module", - "query_module", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "CAP_SYS_PACCT": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{"acct"}, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "CAP_SYS_PTRACE": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "kcmp", - "process_vm_readv", - "process_vm_writev", - "ptrace", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "CAP_SYS_RAWIO": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "iopl", - "ioperm", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "CAP_SYS_TIME": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "settimeofday", - "stime", - "adjtimex", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - case "CAP_SYS_TTY_CONFIG": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{"vhangup"}, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{}, - }) - } - } - - if !admin { - switch runtime.GOARCH { - case "s390", "s390x": - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "clone", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{ - { - Index: 1, - Value: syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWUSER | syscall.CLONE_NEWPID | syscall.CLONE_NEWNET, - ValueTwo: 0, - Op: specs.OpMaskedEqual, - }, - }, - }) - default: - s.Syscalls = append(s.Syscalls, specs.LinuxSyscall{ - Names: []string{ - "clone", - }, - Action: specs.ActAllow, - Args: []specs.LinuxSeccompArg{ - { - Index: 0, - Value: syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWUSER | syscall.CLONE_NEWPID | syscall.CLONE_NEWNET, - ValueTwo: 0, - Op: specs.OpMaskedEqual, - }, - }, - }) - } - } - - return s -} diff --git a/vendor/github.com/containerd/containerd/defaults/defaults.go b/vendor/github.com/containerd/containerd/defaults/defaults.go index 7040f5b85a70d..6f5b122ecf936 100644 --- a/vendor/github.com/containerd/containerd/defaults/defaults.go +++ b/vendor/github.com/containerd/containerd/defaults/defaults.go @@ -23,4 +23,10 @@ const ( // DefaultMaxSendMsgSize defines the default maximum message size for // sending protobufs passed over the GRPC API. DefaultMaxSendMsgSize = 16 << 20 + // DefaultRuntimeNSLabel defines the namespace label to check for the + // default runtime + DefaultRuntimeNSLabel = "containerd.io/defaults/runtime" + // DefaultSnapshotterNSLabel defines the namespace label to check for the + // default snapshotter + DefaultSnapshotterNSLabel = "containerd.io/defaults/snapshotter" ) diff --git a/vendor/github.com/containerd/containerd/defaults/defaults_unix.go b/vendor/github.com/containerd/containerd/defaults/defaults_unix.go index 30ed42235ef0c..6b69cd06b9b83 100644 --- a/vendor/github.com/containerd/containerd/defaults/defaults_unix.go +++ b/vendor/github.com/containerd/containerd/defaults/defaults_unix.go @@ -32,4 +32,8 @@ const ( // DefaultFIFODir is the default location used by client-side cio library // to store FIFOs. DefaultFIFODir = "/run/containerd/fifo" + // DefaultRuntime is the default linux runtime + DefaultRuntime = "io.containerd.runc.v2" + // DefaultConfigDir is the default location for config files. + DefaultConfigDir = "/etc/containerd" ) diff --git a/vendor/github.com/containerd/containerd/defaults/defaults_windows.go b/vendor/github.com/containerd/containerd/defaults/defaults_windows.go index 983bf762f7e53..a80700075f992 100644 --- a/vendor/github.com/containerd/containerd/defaults/defaults_windows.go +++ b/vendor/github.com/containerd/containerd/defaults/defaults_windows.go @@ -26,10 +26,13 @@ import ( var ( // DefaultRootDir is the default location used by containerd to store // persistent data - DefaultRootDir = filepath.Join(os.Getenv("programfiles"), "containerd", "root") + DefaultRootDir = filepath.Join(os.Getenv("ProgramData"), "containerd", "root") // DefaultStateDir is the default location used by containerd to store // transient data - DefaultStateDir = filepath.Join(os.Getenv("programfiles"), "containerd", "state") + DefaultStateDir = filepath.Join(os.Getenv("ProgramData"), "containerd", "state") + + // DefaultConfigDir is the default location for config files. + DefaultConfigDir = filepath.Join(os.Getenv("programfiles"), "containerd") ) const ( @@ -40,4 +43,6 @@ const ( // DefaultFIFODir is the default location used by client-side cio library // to store FIFOs. Unused on Windows. DefaultFIFODir = "" + // DefaultRuntime is the default windows runtime + DefaultRuntime = "io.containerd.runhcs.v1" ) diff --git a/vendor/github.com/containerd/containerd/diff.go b/vendor/github.com/containerd/containerd/diff.go index 8d1219e351b41..445df019220c8 100644 --- a/vendor/github.com/containerd/containerd/diff.go +++ b/vendor/github.com/containerd/containerd/diff.go @@ -45,10 +45,17 @@ type diffRemote struct { client diffapi.DiffClient } -func (r *diffRemote) Apply(ctx context.Context, diff ocispec.Descriptor, mounts []mount.Mount) (ocispec.Descriptor, error) { +func (r *diffRemote) Apply(ctx context.Context, desc ocispec.Descriptor, mounts []mount.Mount, opts ...diff.ApplyOpt) (ocispec.Descriptor, error) { + var config diff.ApplyConfig + for _, opt := range opts { + if err := opt(ctx, desc, &config); err != nil { + return ocispec.Descriptor{}, err + } + } req := &diffapi.ApplyRequest{ - Diff: fromDescriptor(diff), - Mounts: fromMounts(mounts), + Diff: fromDescriptor(desc), + Mounts: fromMounts(mounts), + Payloads: config.ProcessorPayloads, } resp, err := r.client.Apply(ctx, req) if err != nil { @@ -80,17 +87,19 @@ func (r *diffRemote) Compare(ctx context.Context, a, b []mount.Mount, opts ...di func toDescriptor(d *types.Descriptor) ocispec.Descriptor { return ocispec.Descriptor{ - MediaType: d.MediaType, - Digest: d.Digest, - Size: d.Size_, + MediaType: d.MediaType, + Digest: d.Digest, + Size: d.Size_, + Annotations: d.Annotations, } } func fromDescriptor(d ocispec.Descriptor) *types.Descriptor { return &types.Descriptor{ - MediaType: d.MediaType, - Digest: d.Digest, - Size_: d.Size, + MediaType: d.MediaType, + Digest: d.Digest, + Size_: d.Size, + Annotations: d.Annotations, } } diff --git a/vendor/github.com/containerd/containerd/diff/diff.go b/vendor/github.com/containerd/containerd/diff/diff.go index 2b6f01c74efb7..17aab616e543d 100644 --- a/vendor/github.com/containerd/containerd/diff/diff.go +++ b/vendor/github.com/containerd/containerd/diff/diff.go @@ -20,6 +20,7 @@ import ( "context" "github.com/containerd/containerd/mount" + "github.com/gogo/protobuf/types" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -51,6 +52,15 @@ type Comparer interface { Compare(ctx context.Context, lower, upper []mount.Mount, opts ...Opt) (ocispec.Descriptor, error) } +// ApplyConfig is used to hold parameters needed for a apply operation +type ApplyConfig struct { + // ProcessorPayloads specifies the payload sent to various processors + ProcessorPayloads map[string]*types.Any +} + +// ApplyOpt is used to configure an Apply operation +type ApplyOpt func(context.Context, ocispec.Descriptor, *ApplyConfig) error + // Applier allows applying diffs between mounts type Applier interface { // Apply applies the content referred to by the given descriptor to @@ -58,7 +68,7 @@ type Applier interface { // implementation and content descriptor. For example, in the common // case the descriptor is a file system difference in tar format, // that tar would be applied on top of the mounts. - Apply(ctx context.Context, desc ocispec.Descriptor, mount []mount.Mount) (ocispec.Descriptor, error) + Apply(ctx context.Context, desc ocispec.Descriptor, mount []mount.Mount, opts ...ApplyOpt) (ocispec.Descriptor, error) } // WithMediaType sets the media type to use for creating the diff, without @@ -87,3 +97,11 @@ func WithLabels(labels map[string]string) Opt { return nil } } + +// WithPayloads sets the apply processor payloads to the config +func WithPayloads(payloads map[string]*types.Any) ApplyOpt { + return func(_ context.Context, _ ocispec.Descriptor, c *ApplyConfig) error { + c.ProcessorPayloads = payloads + return nil + } +} diff --git a/vendor/github.com/containerd/containerd/diff/stream.go b/vendor/github.com/containerd/containerd/diff/stream.go new file mode 100644 index 0000000000000..655f9ce838629 --- /dev/null +++ b/vendor/github.com/containerd/containerd/diff/stream.go @@ -0,0 +1,191 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package diff + +import ( + "context" + "io" + "os" + + "github.com/containerd/containerd/archive/compression" + "github.com/containerd/containerd/images" + "github.com/gogo/protobuf/types" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +var ( + handlers []Handler + + // ErrNoProcessor is returned when no stream processor is available for a media-type + ErrNoProcessor = errors.New("no processor for media-type") +) + +func init() { + // register the default compression handler + RegisterProcessor(compressedHandler) +} + +// RegisterProcessor registers a stream processor for media-types +func RegisterProcessor(handler Handler) { + handlers = append(handlers, handler) +} + +// GetProcessor returns the processor for a media-type +func GetProcessor(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error) { + // reverse this list so that user configured handlers come up first + for i := len(handlers) - 1; i >= 0; i-- { + processor, ok := handlers[i](ctx, stream.MediaType()) + if ok { + return processor(ctx, stream, payloads) + } + } + return nil, ErrNoProcessor +} + +// Handler checks a media-type and initializes the processor +type Handler func(ctx context.Context, mediaType string) (StreamProcessorInit, bool) + +// StaticHandler returns the processor init func for a static media-type +func StaticHandler(expectedMediaType string, fn StreamProcessorInit) Handler { + return func(ctx context.Context, mediaType string) (StreamProcessorInit, bool) { + if mediaType == expectedMediaType { + return fn, true + } + return nil, false + } +} + +// StreamProcessorInit returns the initialized stream processor +type StreamProcessorInit func(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error) + +// RawProcessor provides access to direct fd for processing +type RawProcessor interface { + // File returns the fd for the read stream of the underlying processor + File() *os.File +} + +// StreamProcessor handles processing a content stream and transforming it into a different media-type +type StreamProcessor interface { + io.ReadCloser + + // MediaType is the resulting media-type that the processor processes the stream into + MediaType() string +} + +func compressedHandler(ctx context.Context, mediaType string) (StreamProcessorInit, bool) { + compressed, err := images.DiffCompression(ctx, mediaType) + if err != nil { + return nil, false + } + if compressed != "" { + return func(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error) { + ds, err := compression.DecompressStream(stream) + if err != nil { + return nil, err + } + + return &compressedProcessor{ + rc: ds, + }, nil + }, true + } + return func(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error) { + return &stdProcessor{ + rc: stream, + }, nil + }, true +} + +// NewProcessorChain initialized the root StreamProcessor +func NewProcessorChain(mt string, r io.Reader) StreamProcessor { + return &processorChain{ + mt: mt, + rc: r, + } +} + +type processorChain struct { + mt string + rc io.Reader +} + +func (c *processorChain) MediaType() string { + return c.mt +} + +func (c *processorChain) Read(p []byte) (int, error) { + return c.rc.Read(p) +} + +func (c *processorChain) Close() error { + return nil +} + +type stdProcessor struct { + rc StreamProcessor +} + +func (c *stdProcessor) MediaType() string { + return ocispec.MediaTypeImageLayer +} + +func (c *stdProcessor) Read(p []byte) (int, error) { + return c.rc.Read(p) +} + +func (c *stdProcessor) Close() error { + return nil +} + +type compressedProcessor struct { + rc io.ReadCloser +} + +func (c *compressedProcessor) MediaType() string { + return ocispec.MediaTypeImageLayer +} + +func (c *compressedProcessor) Read(p []byte) (int, error) { + return c.rc.Read(p) +} + +func (c *compressedProcessor) Close() error { + return c.rc.Close() +} + +// BinaryHandler creates a new stream processor handler which calls out to the given binary. +// The id is used to identify the stream processor and allows the caller to send +// payloads specific for that stream processor (i.e. decryption keys for decrypt stream processor). +// The binary will be called for the provided mediaTypes and return the given media type. +func BinaryHandler(id, returnsMediaType string, mediaTypes []string, path string, args, env []string) Handler { + set := make(map[string]struct{}, len(mediaTypes)) + for _, m := range mediaTypes { + set[m] = struct{}{} + } + return func(_ context.Context, mediaType string) (StreamProcessorInit, bool) { + if _, ok := set[mediaType]; ok { + return func(ctx context.Context, stream StreamProcessor, payloads map[string]*types.Any) (StreamProcessor, error) { + payload := payloads[id] + return NewBinaryProcessor(ctx, mediaType, returnsMediaType, stream, path, args, env, payload) + }, true + } + return nil, false + } +} + +const mediaTypeEnvVar = "STREAM_PROCESSOR_MEDIATYPE" diff --git a/vendor/github.com/containerd/containerd/diff/stream_unix.go b/vendor/github.com/containerd/containerd/diff/stream_unix.go new file mode 100644 index 0000000000000..d79fd71544a5d --- /dev/null +++ b/vendor/github.com/containerd/containerd/diff/stream_unix.go @@ -0,0 +1,147 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package diff + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "os/exec" + "sync" + + "github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/types" + "github.com/pkg/errors" +) + +// NewBinaryProcessor returns a binary processor for use with processing content streams +func NewBinaryProcessor(ctx context.Context, imt, rmt string, stream StreamProcessor, name string, args, env []string, payload *types.Any) (StreamProcessor, error) { + cmd := exec.CommandContext(ctx, name, args...) + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, env...) + + var payloadC io.Closer + if payload != nil { + data, err := proto.Marshal(payload) + if err != nil { + return nil, err + } + r, w, err := os.Pipe() + if err != nil { + return nil, err + } + go func() { + io.Copy(w, bytes.NewReader(data)) + w.Close() + }() + + cmd.ExtraFiles = append(cmd.ExtraFiles, r) + payloadC = r + } + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", mediaTypeEnvVar, imt)) + var ( + stdin io.Reader + closer func() error + err error + ) + if f, ok := stream.(RawProcessor); ok { + stdin = f.File() + closer = f.File().Close + } else { + stdin = stream + } + cmd.Stdin = stdin + r, w, err := os.Pipe() + if err != nil { + return nil, err + } + cmd.Stdout = w + + stderr := bytes.NewBuffer(nil) + cmd.Stderr = stderr + + if err := cmd.Start(); err != nil { + return nil, err + } + p := &binaryProcessor{ + cmd: cmd, + r: r, + mt: rmt, + stderr: stderr, + } + go p.wait() + + // close after start and dup + w.Close() + if closer != nil { + closer() + } + if payloadC != nil { + payloadC.Close() + } + return p, nil +} + +type binaryProcessor struct { + cmd *exec.Cmd + r *os.File + mt string + stderr *bytes.Buffer + + mu sync.Mutex + err error +} + +func (c *binaryProcessor) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +func (c *binaryProcessor) wait() { + if err := c.cmd.Wait(); err != nil { + if _, ok := err.(*exec.ExitError); ok { + c.mu.Lock() + c.err = errors.New(c.stderr.String()) + c.mu.Unlock() + } + } +} + +func (c *binaryProcessor) File() *os.File { + return c.r +} + +func (c *binaryProcessor) MediaType() string { + return c.mt +} + +func (c *binaryProcessor) Read(p []byte) (int, error) { + return c.r.Read(p) +} + +func (c *binaryProcessor) Close() error { + err := c.r.Close() + if kerr := c.cmd.Process.Kill(); err == nil { + err = kerr + } + return err +} diff --git a/vendor/github.com/containerd/containerd/diff/stream_windows.go b/vendor/github.com/containerd/containerd/diff/stream_windows.go new file mode 100644 index 0000000000000..19dcbacf5dd94 --- /dev/null +++ b/vendor/github.com/containerd/containerd/diff/stream_windows.go @@ -0,0 +1,166 @@ +// +build windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package diff + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "sync" + + winio "github.com/Microsoft/go-winio" + "github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/types" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const processorPipe = "STREAM_PROCESSOR_PIPE" + +// NewBinaryProcessor returns a binary processor for use with processing content streams +func NewBinaryProcessor(ctx context.Context, imt, rmt string, stream StreamProcessor, name string, args, env []string, payload *types.Any) (StreamProcessor, error) { + cmd := exec.CommandContext(ctx, name, args...) + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, env...) + + if payload != nil { + data, err := proto.Marshal(payload) + if err != nil { + return nil, err + } + up, err := getUiqPath() + if err != nil { + return nil, err + } + path := fmt.Sprintf("\\\\.\\pipe\\containerd-processor-%s-pipe", up) + l, err := winio.ListenPipe(path, nil) + if err != nil { + return nil, err + } + go func() { + defer l.Close() + conn, err := l.Accept() + if err != nil { + logrus.WithError(err).Error("accept npipe connection") + return + } + io.Copy(conn, bytes.NewReader(data)) + conn.Close() + }() + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", processorPipe, path)) + } + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", mediaTypeEnvVar, imt)) + var ( + stdin io.Reader + closer func() error + err error + ) + if f, ok := stream.(RawProcessor); ok { + stdin = f.File() + closer = f.File().Close + } else { + stdin = stream + } + cmd.Stdin = stdin + r, w, err := os.Pipe() + if err != nil { + return nil, err + } + cmd.Stdout = w + stderr := bytes.NewBuffer(nil) + cmd.Stderr = stderr + + if err := cmd.Start(); err != nil { + return nil, err + } + p := &binaryProcessor{ + cmd: cmd, + r: r, + mt: rmt, + stderr: stderr, + } + go p.wait() + + // close after start and dup + w.Close() + if closer != nil { + closer() + } + return p, nil +} + +type binaryProcessor struct { + cmd *exec.Cmd + r *os.File + mt string + stderr *bytes.Buffer + + mu sync.Mutex + err error +} + +func (c *binaryProcessor) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +func (c *binaryProcessor) wait() { + if err := c.cmd.Wait(); err != nil { + if _, ok := err.(*exec.ExitError); ok { + c.mu.Lock() + c.err = errors.New(c.stderr.String()) + c.mu.Unlock() + } + } +} + +func (c *binaryProcessor) File() *os.File { + return c.r +} + +func (c *binaryProcessor) MediaType() string { + return c.mt +} + +func (c *binaryProcessor) Read(p []byte) (int, error) { + return c.r.Read(p) +} + +func (c *binaryProcessor) Close() error { + err := c.r.Close() + if kerr := c.cmd.Process.Kill(); err == nil { + err = kerr + } + return err +} + +func getUiqPath() (string, error) { + dir, err := ioutil.TempDir("", "") + if err != nil { + return "", err + } + os.Remove(dir) + return filepath.Base(dir), nil +} diff --git a/vendor/github.com/containerd/containerd/errdefs/errors.go b/vendor/github.com/containerd/containerd/errdefs/errors.go index 40427fc5a54e7..05a35228ca420 100644 --- a/vendor/github.com/containerd/containerd/errdefs/errors.go +++ b/vendor/github.com/containerd/containerd/errdefs/errors.go @@ -26,7 +26,11 @@ // client-side errors to the correct types. package errdefs -import "github.com/pkg/errors" +import ( + "context" + + "github.com/pkg/errors" +) // Definitions of common error types used throughout containerd. All containerd // errors returned by most packages will map into one of these errors classes. @@ -47,32 +51,43 @@ var ( // IsInvalidArgument returns true if the error is due to an invalid argument func IsInvalidArgument(err error) bool { - return errors.Cause(err) == ErrInvalidArgument + return errors.Is(err, ErrInvalidArgument) } // IsNotFound returns true if the error is due to a missing object func IsNotFound(err error) bool { - return errors.Cause(err) == ErrNotFound + return errors.Is(err, ErrNotFound) } // IsAlreadyExists returns true if the error is due to an already existing // metadata item func IsAlreadyExists(err error) bool { - return errors.Cause(err) == ErrAlreadyExists + return errors.Is(err, ErrAlreadyExists) } // IsFailedPrecondition returns true if an operation could not proceed to the // lack of a particular condition func IsFailedPrecondition(err error) bool { - return errors.Cause(err) == ErrFailedPrecondition + return errors.Is(err, ErrFailedPrecondition) } // IsUnavailable returns true if the error is due to a resource being unavailable func IsUnavailable(err error) bool { - return errors.Cause(err) == ErrUnavailable + return errors.Is(err, ErrUnavailable) } // IsNotImplemented returns true if the error is due to not being implemented func IsNotImplemented(err error) bool { - return errors.Cause(err) == ErrNotImplemented + return errors.Is(err, ErrNotImplemented) +} + +// IsCanceled returns true if the error is due to `context.Canceled`. +func IsCanceled(err error) bool { + return errors.Is(err, context.Canceled) +} + +// IsDeadlineExceeded returns true if the error is due to +// `context.DeadlineExceeded`. +func IsDeadlineExceeded(err error) bool { + return errors.Is(err, context.DeadlineExceeded) } diff --git a/vendor/github.com/containerd/containerd/errdefs/grpc.go b/vendor/github.com/containerd/containerd/errdefs/grpc.go index 4eab03ab868c0..209f63bd0fc0d 100644 --- a/vendor/github.com/containerd/containerd/errdefs/grpc.go +++ b/vendor/github.com/containerd/containerd/errdefs/grpc.go @@ -17,6 +17,7 @@ package errdefs import ( + "context" "strings" "github.com/pkg/errors" @@ -55,6 +56,10 @@ func ToGRPC(err error) error { return status.Errorf(codes.Unavailable, err.Error()) case IsNotImplemented(err): return status.Errorf(codes.Unimplemented, err.Error()) + case IsCanceled(err): + return status.Errorf(codes.Canceled, err.Error()) + case IsDeadlineExceeded(err): + return status.Errorf(codes.DeadlineExceeded, err.Error()) } return err @@ -89,13 +94,17 @@ func FromGRPC(err error) error { cls = ErrFailedPrecondition case codes.Unimplemented: cls = ErrNotImplemented + case codes.Canceled: + cls = context.Canceled + case codes.DeadlineExceeded: + cls = context.DeadlineExceeded default: cls = ErrUnknown } msg := rebaseMessage(cls, err) if msg != "" { - err = errors.Wrapf(cls, msg) + err = errors.Wrap(cls, msg) } else { err = errors.WithStack(cls) } diff --git a/vendor/github.com/containerd/containerd/events.go b/vendor/github.com/containerd/containerd/events.go index 92e9cd5104578..3577b7c3a9fc0 100644 --- a/vendor/github.com/containerd/containerd/events.go +++ b/vendor/github.com/containerd/containerd/events.go @@ -110,6 +110,9 @@ func (e *eventRemote) Subscribe(ctx context.Context, filters ...string) (ch <-ch Event: ev.Event, }: case <-ctx.Done(): + if cerr := ctx.Err(); cerr != context.Canceled { + errq <- cerr + } return } } diff --git a/vendor/github.com/containerd/containerd/events/exchange/exchange.go b/vendor/github.com/containerd/containerd/events/exchange/exchange.go index 95d21b7df6bfe..eb27bf29b3075 100644 --- a/vendor/github.com/containerd/containerd/events/exchange/exchange.go +++ b/vendor/github.com/containerd/containerd/events/exchange/exchange.go @@ -50,7 +50,7 @@ var _ events.Publisher = &Exchange{} var _ events.Forwarder = &Exchange{} var _ events.Subscriber = &Exchange{} -// Forward accepts an envelope to be direcly distributed on the exchange. +// Forward accepts an envelope to be directly distributed on the exchange. // // This is useful when an event is forwarded on behalf of another namespace or // when the event is propagated on behalf of another publisher. @@ -138,10 +138,10 @@ func (e *Exchange) Subscribe(ctx context.Context, fs ...string) (ch <-chan *even ) closeAll := func() { - defer close(errq) - defer e.broadcaster.Remove(dst) - defer queue.Close() - defer channel.Close() + channel.Close() + queue.Close() + e.broadcaster.Remove(dst) + close(errq) } ch = evch @@ -225,7 +225,7 @@ func validateTopic(topic string) error { } func validateEnvelope(envelope *events.Envelope) error { - if err := namespaces.Validate(envelope.Namespace); err != nil { + if err := identifiers.Validate(envelope.Namespace); err != nil { return errors.Wrapf(err, "event envelope has invalid namespace") } diff --git a/vendor/github.com/containerd/containerd/export.go b/vendor/github.com/containerd/containerd/export.go index bfc25316ca943..81f199226d4ae 100644 --- a/vendor/github.com/containerd/containerd/export.go +++ b/vendor/github.com/containerd/containerd/export.go @@ -20,39 +20,12 @@ import ( "context" "io" - "github.com/containerd/containerd/images" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" + "github.com/containerd/containerd/images/archive" ) -type exportOpts struct { -} - -// ExportOpt allows the caller to specify export-specific options -type ExportOpt func(c *exportOpts) error - -func resolveExportOpt(opts ...ExportOpt) (exportOpts, error) { - var eopts exportOpts - for _, o := range opts { - if err := o(&eopts); err != nil { - return eopts, err - } - } - return eopts, nil -} - -// Export exports an image to a Tar stream. -// OCI format is used by default. -// It is up to caller to put "org.opencontainers.image.ref.name" annotation to desc. -// TODO(AkihiroSuda): support exporting multiple descriptors at once to a single archive stream. -func (c *Client) Export(ctx context.Context, exporter images.Exporter, desc ocispec.Descriptor, opts ...ExportOpt) (io.ReadCloser, error) { - _, err := resolveExportOpt(opts...) // unused now - if err != nil { - return nil, err - } - pr, pw := io.Pipe() - go func() { - pw.CloseWithError(errors.Wrap(exporter.Export(ctx, c.ContentStore(), desc, pw), "export failed")) - }() - return pr, nil +// Export exports images to a Tar stream. +// The tar archive is in OCI format with a Docker compatible manifest +// when a single target platform is given. +func (c *Client) Export(ctx context.Context, w io.Writer, opts ...archive.ExportOpt) error { + return archive.Export(ctx, c.ContentStore(), w, opts...) } diff --git a/vendor/github.com/containerd/containerd/filters/parser.go b/vendor/github.com/containerd/containerd/filters/parser.go index 9dced523b1a59..0825d668caf0f 100644 --- a/vendor/github.com/containerd/containerd/filters/parser.go +++ b/vendor/github.com/containerd/containerd/filters/parser.go @@ -71,7 +71,7 @@ func ParseAll(ss ...string) (Filter, error) { for _, s := range ss { f, err := Parse(s) if err != nil { - return nil, errors.Wrapf(errdefs.ErrInvalidArgument, err.Error()) + return nil, errors.Wrap(errdefs.ErrInvalidArgument, err.Error()) } fs = append(fs, f) @@ -209,6 +209,8 @@ func (p *parser) field() (string, error) { return s, nil case tokenQuoted: return p.unquote(pos, s, false) + case tokenIllegal: + return "", p.mkerr(pos, p.scanner.err) } return "", p.mkerr(pos, "expected field or quoted") @@ -228,6 +230,8 @@ func (p *parser) operator() (operator, error) { default: return 0, p.mkerr(pos, "unsupported operator %q", s) } + case tokenIllegal: + return 0, p.mkerr(pos, p.scanner.err) } return 0, p.mkerr(pos, `expected an operator ("=="|"!="|"~=")`) @@ -241,6 +245,8 @@ func (p *parser) value(allowAltQuotes bool) (string, error) { return s, nil case tokenQuoted: return p.unquote(pos, s, allowAltQuotes) + case tokenIllegal: + return "", p.mkerr(pos, p.scanner.err) } return "", p.mkerr(pos, "expected value or quoted") diff --git a/vendor/github.com/containerd/containerd/filters/scanner.go b/vendor/github.com/containerd/containerd/filters/scanner.go index c3961962cc6d0..6a485467b8a03 100644 --- a/vendor/github.com/containerd/containerd/filters/scanner.go +++ b/vendor/github.com/containerd/containerd/filters/scanner.go @@ -17,7 +17,6 @@ package filters import ( - "fmt" "unicode" "unicode/utf8" ) @@ -64,6 +63,7 @@ type scanner struct { pos int ppos int // bounds the current rune in the string value bool + err string } func (s *scanner) init(input string) { @@ -82,12 +82,14 @@ func (s *scanner) next() rune { s.ppos += w if r == utf8.RuneError { if w > 0 { + s.error("rune error") return tokenIllegal } return tokenEOF } if r == 0 { + s.error("unexpected null") return tokenIllegal } @@ -114,7 +116,9 @@ chomp: case ch == tokenEOF: case ch == tokenIllegal: case isQuoteRune(ch): - s.scanQuoted(ch) + if !s.scanQuoted(ch) { + return pos, tokenIllegal, s.input[pos:s.ppos] + } return pos, tokenQuoted, s.input[pos:s.ppos] case isSeparatorRune(ch): s.value = false @@ -172,55 +176,64 @@ func (s *scanner) scanValue() { } } -func (s *scanner) scanQuoted(quote rune) { +func (s *scanner) scanQuoted(quote rune) bool { + var illegal bool ch := s.next() // read character after quote for ch != quote { if ch == '\n' || ch < 0 { - s.error("literal not terminated") - return + s.error("quoted literal not terminated") + return false } if ch == '\\' { - ch = s.scanEscape(quote) + var legal bool + ch, legal = s.scanEscape(quote) + if !legal { + illegal = true + } } else { ch = s.next() } } - return + return !illegal } -func (s *scanner) scanEscape(quote rune) rune { - ch := s.next() // read character after '/' +func (s *scanner) scanEscape(quote rune) (ch rune, legal bool) { + ch = s.next() // read character after '/' switch ch { case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote: // nothing to do ch = s.next() + legal = true case '0', '1', '2', '3', '4', '5', '6', '7': - ch = s.scanDigits(ch, 8, 3) + ch, legal = s.scanDigits(ch, 8, 3) case 'x': - ch = s.scanDigits(s.next(), 16, 2) + ch, legal = s.scanDigits(s.next(), 16, 2) case 'u': - ch = s.scanDigits(s.next(), 16, 4) + ch, legal = s.scanDigits(s.next(), 16, 4) case 'U': - ch = s.scanDigits(s.next(), 16, 8) + ch, legal = s.scanDigits(s.next(), 16, 8) default: - s.error("illegal char escape") + s.error("illegal escape sequence") } - return ch + return } -func (s *scanner) scanDigits(ch rune, base, n int) rune { +func (s *scanner) scanDigits(ch rune, base, n int) (rune, bool) { for n > 0 && digitVal(ch) < base { ch = s.next() n-- } if n > 0 { - s.error("illegal char escape") + s.error("illegal numeric escape sequence") + return ch, false } - return ch + return ch, true } func (s *scanner) error(msg string) { - fmt.Println("error fixme", msg) + if s.err == "" { + s.err = msg + } } func digitVal(ch rune) int { diff --git a/vendor/github.com/containerd/containerd/gc/gc.go b/vendor/github.com/containerd/containerd/gc/gc.go index 35a1712cb3e4b..4f71cb305c5dd 100644 --- a/vendor/github.com/containerd/containerd/gc/gc.go +++ b/vendor/github.com/containerd/containerd/gc/gc.go @@ -30,6 +30,11 @@ import ( // ResourceType represents type of resource at a node type ResourceType uint8 +// ResourceMax represents the max resource. +// Upper bits are stripped out during the mark phase, allowing the upper 3 bits +// to be used by the caller reference function. +const ResourceMax = ResourceType(0x1F) + // Node presents a resource which has a type and key, // this node can be used to lookup other nodes. type Node struct { @@ -80,6 +85,8 @@ func Tricolor(roots []Node, refs func(ref Node) ([]Node, error)) (map[Node]struc } } + // strip bits above max resource type + id.Type = id.Type & ResourceMax // mark as black when done reachable[id] = struct{}{} } @@ -165,7 +172,7 @@ func ConcurrentMark(ctx context.Context, root <-chan Node, refs func(context.Con return seen, nil } -// Sweep removes all nodes returned through the channel which are not in +// Sweep removes all nodes returned through the slice which are not in // the reachable set by calling the provided remove function. func Sweep(reachable map[Node]struct{}, all []Node, remove func(Node) error) error { // All black objects are now reachable, and all white objects are diff --git a/vendor/github.com/containerd/containerd/go.mod b/vendor/github.com/containerd/containerd/go.mod new file mode 100644 index 0000000000000..106cbd339408d --- /dev/null +++ b/vendor/github.com/containerd/containerd/go.mod @@ -0,0 +1,81 @@ +module github.com/containerd/containerd + +go 1.16 + +require ( + github.com/Microsoft/go-winio v0.4.17 + github.com/Microsoft/hcsshim v0.8.23 + github.com/containerd/aufs v1.0.0 + github.com/containerd/btrfs v1.0.0 + github.com/containerd/cgroups v1.0.1 + github.com/containerd/console v1.0.2 + github.com/containerd/continuity v0.1.0 + github.com/containerd/fifo v1.0.0 + github.com/containerd/go-cni v1.0.2 + github.com/containerd/go-runc v1.0.0 + github.com/containerd/imgcrypt v1.1.1 + github.com/containerd/nri v0.1.0 + github.com/containerd/ttrpc v1.1.0 + github.com/containerd/typeurl v1.0.2 + github.com/containerd/zfs v1.0.0 + github.com/containernetworking/plugins v0.9.1 + github.com/coreos/go-systemd/v22 v22.3.2 + github.com/davecgh/go-spew v1.1.1 + github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c + github.com/docker/go-metrics v0.0.1 + github.com/docker/go-units v0.4.0 + github.com/emicklei/go-restful v2.9.5+incompatible + github.com/fsnotify/fsnotify v1.4.9 + github.com/gogo/googleapis v1.4.0 + github.com/gogo/protobuf v1.3.2 + github.com/golang/protobuf v1.5.0 + github.com/google/go-cmp v0.5.5 + github.com/google/uuid v1.2.0 + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 + github.com/hashicorp/go-multierror v1.0.0 + github.com/imdario/mergo v0.3.12 + github.com/klauspost/compress v1.11.13 + github.com/moby/locker v1.0.1 + github.com/moby/sys/mountinfo v0.4.1 + github.com/moby/sys/symlink v0.1.0 + github.com/opencontainers/go-digest v1.0.0 + github.com/opencontainers/image-spec v1.0.1 + github.com/opencontainers/runc v1.0.2 + github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 + github.com/opencontainers/selinux v1.8.2 + github.com/pelletier/go-toml v1.8.1 + github.com/pkg/errors v0.9.1 + github.com/prometheus/client_golang v1.7.1 + github.com/satori/go.uuid v1.2.0 // indirect + github.com/sirupsen/logrus v1.8.1 + github.com/stretchr/testify v1.6.1 + github.com/tchap/go-patricia v2.2.6+incompatible + github.com/urfave/cli v1.22.2 + go.etcd.io/bbolt v1.3.5 + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 + golang.org/x/sync v0.0.0-20201207232520-09787c993a3a + golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 + google.golang.org/grpc v1.33.2 + gotest.tools/v3 v3.0.3 + k8s.io/api v0.20.6 + k8s.io/apimachinery v0.20.6 + k8s.io/apiserver v0.20.6 + k8s.io/client-go v0.20.6 + k8s.io/component-base v0.20.6 + k8s.io/cri-api v0.20.6 + k8s.io/klog/v2 v2.4.0 + k8s.io/utils v0.0.0-20201110183641-67b214c5f920 +) + +// When updating replace rules, make sure to also update the rules in integration/client/go.mod +replace ( + // prevent transitional dependencies due to containerd having a circular + // dependency on itself through plugins. see .empty-mod/go.mod for details + github.com/containerd/containerd => ./.empty-mod/ + github.com/gogo/googleapis => github.com/gogo/googleapis v1.3.2 + github.com/golang/protobuf => github.com/golang/protobuf v1.3.5 + // urfave/cli must be <= v1.22.1 due to a regression: https://github.com/urfave/cli/issues/1092 + github.com/urfave/cli => github.com/urfave/cli v1.22.1 + google.golang.org/genproto => google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 + google.golang.org/grpc => google.golang.org/grpc v1.27.1 +) diff --git a/vendor/github.com/containerd/containerd/identifiers/validate.go b/vendor/github.com/containerd/containerd/identifiers/validate.go index c58513c0258f7..f52317b491b81 100644 --- a/vendor/github.com/containerd/containerd/identifiers/validate.go +++ b/vendor/github.com/containerd/containerd/identifiers/validate.go @@ -42,13 +42,13 @@ var ( identifierRe = regexp.MustCompile(reAnchor(alphanum + reGroup(separators+reGroup(alphanum)) + "*")) ) -// Validate return nil if the string s is a valid identifier. +// Validate returns nil if the string s is a valid identifier. // -// identifiers must be valid domain names according to RFC 1035, section 2.3.1. To -// enforce case insensitvity, all characters must be lower case. +// identifiers are similar to the domain name rules according to RFC 1035, section 2.3.1. However +// rules in this package are relaxed to allow numerals to follow period (".") and mixed case is +// allowed. // -// In general, identifiers that pass this validation, should be safe for use as -// a domain names or filesystem path component. +// In general identifiers that pass this validation should be safe for use as filesystem path components. func Validate(s string) error { if len(s) == 0 { return errors.Wrapf(errdefs.ErrInvalidArgument, "identifier must not be empty") diff --git a/vendor/github.com/containerd/containerd/image.go b/vendor/github.com/containerd/containerd/image.go index 62fba9de754a1..f35f0edcaaac8 100644 --- a/vendor/github.com/containerd/containerd/image.go +++ b/vendor/github.com/containerd/containerd/image.go @@ -18,17 +18,23 @@ package containerd import ( "context" + "encoding/json" "fmt" + "strings" + "sync/atomic" "github.com/containerd/containerd/content" + "github.com/containerd/containerd/diff" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/rootfs" - digest "github.com/opencontainers/go-digest" + "github.com/containerd/containerd/snapshots" + "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/identity" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" + "golang.org/x/sync/semaphore" ) // Image describes an image used by containers @@ -40,17 +46,64 @@ type Image interface { // Labels of the image Labels() map[string]string // Unpack unpacks the image's content into a snapshot - Unpack(context.Context, string) error + Unpack(context.Context, string, ...UnpackOpt) error // RootFS returns the unpacked diffids that make up images rootfs. RootFS(ctx context.Context) ([]digest.Digest, error) // Size returns the total size of the image's packed resources. Size(ctx context.Context) (int64, error) + // Usage returns a usage calculation for the image. + Usage(context.Context, ...UsageOpt) (int64, error) // Config descriptor for the image. Config(ctx context.Context) (ocispec.Descriptor, error) // IsUnpacked returns whether or not an image is unpacked. IsUnpacked(context.Context, string) (bool, error) // ContentStore provides a content store which contains image blob data ContentStore() content.Store + // Metadata returns the underlying image metadata + Metadata() images.Image +} + +type usageOptions struct { + manifestLimit *int + manifestOnly bool + snapshots bool +} + +// UsageOpt is used to configure the usage calculation +type UsageOpt func(*usageOptions) error + +// WithUsageManifestLimit sets the limit to the number of manifests which will +// be walked for usage. Setting this value to 0 will require all manifests to +// be walked, returning ErrNotFound if manifests are missing. +// NOTE: By default all manifests which exist will be walked +// and any non-existent manifests and their subobjects will be ignored. +func WithUsageManifestLimit(i int) UsageOpt { + // If 0 then don't filter any manifests + // By default limits to current platform + return func(o *usageOptions) error { + o.manifestLimit = &i + return nil + } +} + +// WithSnapshotUsage will check for referenced snapshots from the image objects +// and include the snapshot size in the total usage. +func WithSnapshotUsage() UsageOpt { + return func(o *usageOptions) error { + o.snapshots = true + return nil + } +} + +// WithManifestUsage is used to get the usage for an image based on what is +// reported by the manifests rather than what exists in the content store. +// NOTE: This function is best used with the manifest limit set to get a +// consistent value, otherwise non-existent manifests will be excluded. +func WithManifestUsage() UsageOpt { + return func(o *usageOptions) error { + o.manifestOnly = true + return nil + } } var _ = (Image)(&image{}) @@ -60,7 +113,7 @@ func NewImage(client *Client, i images.Image) Image { return &image{ client: client, i: i, - platform: platforms.Default(), + platform: client.platform, } } @@ -80,6 +133,10 @@ type image struct { platform platforms.MatchComparer } +func (i *image) Metadata() images.Image { + return i.i +} + func (i *image) Name() string { return i.i.Name } @@ -98,8 +155,97 @@ func (i *image) RootFS(ctx context.Context) ([]digest.Digest, error) { } func (i *image) Size(ctx context.Context) (int64, error) { - provider := i.client.ContentStore() - return i.i.Size(ctx, provider, i.platform) + return i.Usage(ctx, WithUsageManifestLimit(1), WithManifestUsage()) +} + +func (i *image) Usage(ctx context.Context, opts ...UsageOpt) (int64, error) { + var config usageOptions + for _, opt := range opts { + if err := opt(&config); err != nil { + return 0, err + } + } + + var ( + provider = i.client.ContentStore() + handler = images.ChildrenHandler(provider) + size int64 + mustExist bool + ) + + if config.manifestLimit != nil { + handler = images.LimitManifests(handler, i.platform, *config.manifestLimit) + mustExist = true + } + + var wh images.HandlerFunc = func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + var usage int64 + children, err := handler(ctx, desc) + if err != nil { + if !errdefs.IsNotFound(err) || mustExist { + return nil, err + } + if !config.manifestOnly { + // Do not count size of non-existent objects + desc.Size = 0 + } + } else if config.snapshots || !config.manifestOnly { + info, err := provider.Info(ctx, desc.Digest) + if err != nil { + if !errdefs.IsNotFound(err) { + return nil, err + } + if !config.manifestOnly { + // Do not count size of non-existent objects + desc.Size = 0 + } + } else if info.Size > desc.Size { + // Count actual usage, Size may be unset or -1 + desc.Size = info.Size + } + + if config.snapshots { + for k, v := range info.Labels { + const prefix = "containerd.io/gc.ref.snapshot." + if !strings.HasPrefix(k, prefix) { + continue + } + + sn := i.client.SnapshotService(k[len(prefix):]) + if sn == nil { + continue + } + + u, err := sn.Usage(ctx, v) + if err != nil { + if !errdefs.IsNotFound(err) && !errdefs.IsInvalidArgument(err) { + return nil, err + } + } else { + usage += u.Size + } + } + } + } + + // Ignore unknown sizes. Generally unknown sizes should + // never be set in manifests, however, the usage + // calculation does not need to enforce this. + if desc.Size >= 0 { + usage += desc.Size + } + + atomic.AddInt64(&size, usage) + + return children, nil + } + + l := semaphore.NewWeighted(3) + if err := images.Dispatch(ctx, wh, l, i.i.Target); err != nil { + return 0, err + } + + return size, nil } func (i *image) Config(ctx context.Context) (ocispec.Descriptor, error) { @@ -108,7 +254,10 @@ func (i *image) Config(ctx context.Context) (ocispec.Descriptor, error) { } func (i *image) IsUnpacked(ctx context.Context, snapshotterName string) (bool, error) { - sn := i.client.SnapshotService(snapshotterName) + sn, err := i.client.getSnapshotter(ctx, snapshotterName) + if err != nil { + return false, err + } cs := i.client.ContentStore() diffs, err := i.i.RootFS(ctx, cs, i.platform) @@ -127,28 +276,75 @@ func (i *image) IsUnpacked(ctx context.Context, snapshotterName string) (bool, e return false, nil } -func (i *image) Unpack(ctx context.Context, snapshotterName string) error { +// UnpackConfig provides configuration for the unpack of an image +type UnpackConfig struct { + // ApplyOpts for applying a diff to a snapshotter + ApplyOpts []diff.ApplyOpt + // SnapshotOpts for configuring a snapshotter + SnapshotOpts []snapshots.Opt + // CheckPlatformSupported is whether to validate that a snapshotter + // supports an image's platform before unpacking + CheckPlatformSupported bool +} + +// UnpackOpt provides configuration for unpack +type UnpackOpt func(context.Context, *UnpackConfig) error + +// WithSnapshotterPlatformCheck sets `CheckPlatformSupported` on the UnpackConfig +func WithSnapshotterPlatformCheck() UnpackOpt { + return func(ctx context.Context, uc *UnpackConfig) error { + uc.CheckPlatformSupported = true + return nil + } +} + +func (i *image) Unpack(ctx context.Context, snapshotterName string, opts ...UnpackOpt) error { ctx, done, err := i.client.WithLease(ctx) if err != nil { return err } defer done(ctx) - layers, err := i.getLayers(ctx, i.platform) + var config UnpackConfig + for _, o := range opts { + if err := o(ctx, &config); err != nil { + return err + } + } + + manifest, err := i.getManifest(ctx, i.platform) + if err != nil { + return err + } + + layers, err := i.getLayers(ctx, i.platform, manifest) if err != nil { return err } var ( - sn = i.client.SnapshotService(snapshotterName) a = i.client.DiffService() cs = i.client.ContentStore() chain []digest.Digest unpacked bool ) + snapshotterName, err = i.client.resolveSnapshotterName(ctx, snapshotterName) + if err != nil { + return err + } + sn, err := i.client.getSnapshotter(ctx, snapshotterName) + if err != nil { + return err + } + if config.CheckPlatformSupported { + if err := i.checkSnapshotterSupport(ctx, snapshotterName, manifest); err != nil { + return err + } + } + for _, layer := range layers { - unpacked, err = rootfs.ApplyLayer(ctx, layer, chain, sn, a) + unpacked, err = rootfs.ApplyLayerWithOpts(ctx, layer, chain, sn, a, config.SnapshotOpts, config.ApplyOpts) if err != nil { return err } @@ -170,36 +366,35 @@ func (i *image) Unpack(ctx context.Context, snapshotterName string) error { chain = append(chain, layer.Diff.Digest) } - if unpacked { - desc, err := i.i.Config(ctx, cs, i.platform) - if err != nil { - return err - } + desc, err := i.i.Config(ctx, cs, i.platform) + if err != nil { + return err + } - rootfs := identity.ChainID(chain).String() + rootfs := identity.ChainID(chain).String() - cinfo := content.Info{ - Digest: desc.Digest, - Labels: map[string]string{ - fmt.Sprintf("containerd.io/gc.ref.snapshot.%s", snapshotterName): rootfs, - }, - } - if _, err := cs.Update(ctx, cinfo, fmt.Sprintf("labels.containerd.io/gc.ref.snapshot.%s", snapshotterName)); err != nil { - return err - } + cinfo := content.Info{ + Digest: desc.Digest, + Labels: map[string]string{ + fmt.Sprintf("containerd.io/gc.ref.snapshot.%s", snapshotterName): rootfs, + }, } - return nil + _, err = cs.Update(ctx, cinfo, fmt.Sprintf("labels.containerd.io/gc.ref.snapshot.%s", snapshotterName)) + return err } -func (i *image) getLayers(ctx context.Context, platform platforms.MatchComparer) ([]rootfs.Layer, error) { - cs := i.client.ContentStore() - +func (i *image) getManifest(ctx context.Context, platform platforms.MatchComparer) (ocispec.Manifest, error) { + cs := i.ContentStore() manifest, err := images.Manifest(ctx, cs, i.i.Target, platform) if err != nil { - return nil, err + return ocispec.Manifest{}, err } + return manifest, nil +} +func (i *image) getLayers(ctx context.Context, platform platforms.MatchComparer, manifest ocispec.Manifest) ([]rootfs.Layer, error) { + cs := i.ContentStore() diffIDs, err := i.i.RootFS(ctx, cs, platform) if err != nil { return nil, errors.Wrap(err, "failed to resolve rootfs") @@ -219,6 +414,37 @@ func (i *image) getLayers(ctx context.Context, platform platforms.MatchComparer) return layers, nil } +func (i *image) getManifestPlatform(ctx context.Context, manifest ocispec.Manifest) (ocispec.Platform, error) { + cs := i.ContentStore() + p, err := content.ReadBlob(ctx, cs, manifest.Config) + if err != nil { + return ocispec.Platform{}, err + } + + var image ocispec.Image + if err := json.Unmarshal(p, &image); err != nil { + return ocispec.Platform{}, err + } + return platforms.Normalize(ocispec.Platform{OS: image.OS, Architecture: image.Architecture}), nil +} + +func (i *image) checkSnapshotterSupport(ctx context.Context, snapshotterName string, manifest ocispec.Manifest) error { + snapshotterPlatformMatcher, err := i.client.GetSnapshotterSupportedPlatforms(ctx, snapshotterName) + if err != nil { + return err + } + + manifestPlatform, err := i.getManifestPlatform(ctx, manifest) + if err != nil { + return err + } + + if snapshotterPlatformMatcher.Match(manifestPlatform) { + return nil + } + return fmt.Errorf("snapshotter %s does not support platform %s for image %s", snapshotterName, manifestPlatform, manifest.Config.Digest) +} + func (i *image) ContentStore() content.Store { return i.client.ContentStore() } diff --git a/vendor/github.com/containerd/containerd/image_store.go b/vendor/github.com/containerd/containerd/image_store.go index 3676bdadad003..fd79e8929f528 100644 --- a/vendor/github.com/containerd/containerd/image_store.go +++ b/vendor/github.com/containerd/containerd/image_store.go @@ -137,16 +137,18 @@ func imagesFromProto(imagespb []imagesapi.Image) []images.Image { func descFromProto(desc *types.Descriptor) ocispec.Descriptor { return ocispec.Descriptor{ - MediaType: desc.MediaType, - Size: desc.Size_, - Digest: desc.Digest, + MediaType: desc.MediaType, + Size: desc.Size_, + Digest: desc.Digest, + Annotations: desc.Annotations, } } func descToProto(desc *ocispec.Descriptor) types.Descriptor { return types.Descriptor{ - MediaType: desc.MediaType, - Size_: desc.Size, - Digest: desc.Digest, + MediaType: desc.MediaType, + Size_: desc.Size, + Digest: desc.Digest, + Annotations: desc.Annotations, } } diff --git a/vendor/github.com/containerd/containerd/images/annotations.go b/vendor/github.com/containerd/containerd/images/annotations.go new file mode 100644 index 0000000000000..47d92104cddc6 --- /dev/null +++ b/vendor/github.com/containerd/containerd/images/annotations.go @@ -0,0 +1,23 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package images + +const ( + // AnnotationImageName is an annotation on a Descriptor in an index.json + // containing the `Name` value as used by an `Image` struct + AnnotationImageName = "io.containerd.image.name" +) diff --git a/vendor/github.com/containerd/containerd/images/archive/exporter.go b/vendor/github.com/containerd/containerd/images/archive/exporter.go new file mode 100644 index 0000000000000..7801b2552cda7 --- /dev/null +++ b/vendor/github.com/containerd/containerd/images/archive/exporter.go @@ -0,0 +1,501 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package archive + +import ( + "archive/tar" + "context" + "encoding/json" + "io" + "path" + "sort" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/platforms" + digest "github.com/opencontainers/go-digest" + ocispecs "github.com/opencontainers/image-spec/specs-go" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +type exportOptions struct { + manifests []ocispec.Descriptor + platform platforms.MatchComparer + allPlatforms bool + skipDockerManifest bool + blobRecordOptions blobRecordOptions +} + +// ExportOpt defines options for configuring exported descriptors +type ExportOpt func(context.Context, *exportOptions) error + +// WithPlatform defines the platform to require manifest lists have +// not exporting all platforms. +// Additionally, platform is used to resolve image configs for +// Docker v1.1, v1.2 format compatibility. +func WithPlatform(p platforms.MatchComparer) ExportOpt { + return func(ctx context.Context, o *exportOptions) error { + o.platform = p + return nil + } +} + +// WithAllPlatforms exports all manifests from a manifest list. +// Missing content will fail the export. +func WithAllPlatforms() ExportOpt { + return func(ctx context.Context, o *exportOptions) error { + o.allPlatforms = true + return nil + } +} + +// WithSkipDockerManifest skips creation of the Docker compatible +// manifest.json file. +func WithSkipDockerManifest() ExportOpt { + return func(ctx context.Context, o *exportOptions) error { + o.skipDockerManifest = true + return nil + } +} + +// WithImage adds the provided images to the exported archive. +func WithImage(is images.Store, name string) ExportOpt { + return func(ctx context.Context, o *exportOptions) error { + img, err := is.Get(ctx, name) + if err != nil { + return err + } + + img.Target.Annotations = addNameAnnotation(name, img.Target.Annotations) + o.manifests = append(o.manifests, img.Target) + + return nil + } +} + +// WithManifest adds a manifest to the exported archive. +// When names are given they will be set on the manifest in the +// exported archive, creating an index record for each name. +// When no names are provided, it is up to caller to put name annotation to +// on the manifest descriptor if needed. +func WithManifest(manifest ocispec.Descriptor, names ...string) ExportOpt { + return func(ctx context.Context, o *exportOptions) error { + if len(names) == 0 { + o.manifests = append(o.manifests, manifest) + } + for _, name := range names { + mc := manifest + mc.Annotations = addNameAnnotation(name, manifest.Annotations) + o.manifests = append(o.manifests, mc) + } + + return nil + } +} + +// BlobFilter returns false if the blob should not be included in the archive. +type BlobFilter func(ocispec.Descriptor) bool + +// WithBlobFilter specifies BlobFilter. +func WithBlobFilter(f BlobFilter) ExportOpt { + return func(ctx context.Context, o *exportOptions) error { + o.blobRecordOptions.blobFilter = f + return nil + } +} + +// WithSkipNonDistributableBlobs excludes non-distributable blobs such as Windows base layers. +func WithSkipNonDistributableBlobs() ExportOpt { + f := func(desc ocispec.Descriptor) bool { + return !images.IsNonDistributable(desc.MediaType) + } + return WithBlobFilter(f) +} + +func addNameAnnotation(name string, base map[string]string) map[string]string { + annotations := map[string]string{} + for k, v := range base { + annotations[k] = v + } + + annotations[images.AnnotationImageName] = name + annotations[ocispec.AnnotationRefName] = ociReferenceName(name) + + return annotations +} + +// Export implements Exporter. +func Export(ctx context.Context, store content.Provider, writer io.Writer, opts ...ExportOpt) error { + var eo exportOptions + for _, opt := range opts { + if err := opt(ctx, &eo); err != nil { + return err + } + } + + records := []tarRecord{ + ociLayoutFile(""), + ociIndexRecord(eo.manifests), + } + + algorithms := map[string]struct{}{} + dManifests := map[digest.Digest]*exportManifest{} + resolvedIndex := map[digest.Digest]digest.Digest{} + for _, desc := range eo.manifests { + switch desc.MediaType { + case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: + mt, ok := dManifests[desc.Digest] + if !ok { + // TODO(containerd): Skip if already added + r, err := getRecords(ctx, store, desc, algorithms, &eo.blobRecordOptions) + if err != nil { + return err + } + records = append(records, r...) + + mt = &exportManifest{ + manifest: desc, + } + dManifests[desc.Digest] = mt + } + + name := desc.Annotations[images.AnnotationImageName] + if name != "" && !eo.skipDockerManifest { + mt.names = append(mt.names, name) + } + case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: + d, ok := resolvedIndex[desc.Digest] + if !ok { + records = append(records, blobRecord(store, desc, &eo.blobRecordOptions)) + + p, err := content.ReadBlob(ctx, store, desc) + if err != nil { + return err + } + + var index ocispec.Index + if err := json.Unmarshal(p, &index); err != nil { + return err + } + + var manifests []ocispec.Descriptor + for _, m := range index.Manifests { + if eo.platform != nil { + if m.Platform == nil || eo.platform.Match(*m.Platform) { + manifests = append(manifests, m) + } else if !eo.allPlatforms { + continue + } + } + + r, err := getRecords(ctx, store, m, algorithms, &eo.blobRecordOptions) + if err != nil { + return err + } + + records = append(records, r...) + } + + if !eo.skipDockerManifest { + if len(manifests) >= 1 { + if len(manifests) > 1 { + sort.SliceStable(manifests, func(i, j int) bool { + if manifests[i].Platform == nil { + return false + } + if manifests[j].Platform == nil { + return true + } + return eo.platform.Less(*manifests[i].Platform, *manifests[j].Platform) + }) + } + d = manifests[0].Digest + dManifests[d] = &exportManifest{ + manifest: manifests[0], + } + } else if eo.platform != nil { + return errors.Wrap(errdefs.ErrNotFound, "no manifest found for platform") + } + } + resolvedIndex[desc.Digest] = d + } + if d != "" { + if name := desc.Annotations[images.AnnotationImageName]; name != "" { + mt := dManifests[d] + mt.names = append(mt.names, name) + } + + } + default: + return errors.Wrap(errdefs.ErrInvalidArgument, "only manifests may be exported") + } + } + + if len(dManifests) > 0 { + tr, err := manifestsRecord(ctx, store, dManifests) + if err != nil { + return errors.Wrap(err, "unable to create manifests file") + } + + records = append(records, tr) + } + + if len(algorithms) > 0 { + records = append(records, directoryRecord("blobs/", 0755)) + for alg := range algorithms { + records = append(records, directoryRecord("blobs/"+alg+"/", 0755)) + } + } + + tw := tar.NewWriter(writer) + defer tw.Close() + return writeTar(ctx, tw, records) +} + +func getRecords(ctx context.Context, store content.Provider, desc ocispec.Descriptor, algorithms map[string]struct{}, brOpts *blobRecordOptions) ([]tarRecord, error) { + var records []tarRecord + exportHandler := func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + records = append(records, blobRecord(store, desc, brOpts)) + algorithms[desc.Digest.Algorithm().String()] = struct{}{} + return nil, nil + } + + childrenHandler := images.ChildrenHandler(store) + + handlers := images.Handlers( + childrenHandler, + images.HandlerFunc(exportHandler), + ) + + // Walk sequentially since the number of fetches is likely one and doing in + // parallel requires locking the export handler + if err := images.Walk(ctx, handlers, desc); err != nil { + return nil, err + } + + return records, nil +} + +type tarRecord struct { + Header *tar.Header + CopyTo func(context.Context, io.Writer) (int64, error) +} + +type blobRecordOptions struct { + blobFilter BlobFilter +} + +func blobRecord(cs content.Provider, desc ocispec.Descriptor, opts *blobRecordOptions) tarRecord { + if opts != nil && opts.blobFilter != nil && !opts.blobFilter(desc) { + return tarRecord{} + } + path := path.Join("blobs", desc.Digest.Algorithm().String(), desc.Digest.Encoded()) + return tarRecord{ + Header: &tar.Header{ + Name: path, + Mode: 0444, + Size: desc.Size, + Typeflag: tar.TypeReg, + }, + CopyTo: func(ctx context.Context, w io.Writer) (int64, error) { + r, err := cs.ReaderAt(ctx, desc) + if err != nil { + return 0, errors.Wrap(err, "failed to get reader") + } + defer r.Close() + + // Verify digest + dgstr := desc.Digest.Algorithm().Digester() + + n, err := io.Copy(io.MultiWriter(w, dgstr.Hash()), content.NewReader(r)) + if err != nil { + return 0, errors.Wrap(err, "failed to copy to tar") + } + if dgstr.Digest() != desc.Digest { + return 0, errors.Errorf("unexpected digest %s copied", dgstr.Digest()) + } + return n, nil + }, + } +} + +func directoryRecord(name string, mode int64) tarRecord { + return tarRecord{ + Header: &tar.Header{ + Name: name, + Mode: mode, + Typeflag: tar.TypeDir, + }, + } +} + +func ociLayoutFile(version string) tarRecord { + if version == "" { + version = ocispec.ImageLayoutVersion + } + layout := ocispec.ImageLayout{ + Version: version, + } + + b, err := json.Marshal(layout) + if err != nil { + panic(err) + } + + return tarRecord{ + Header: &tar.Header{ + Name: ocispec.ImageLayoutFile, + Mode: 0444, + Size: int64(len(b)), + Typeflag: tar.TypeReg, + }, + CopyTo: func(ctx context.Context, w io.Writer) (int64, error) { + n, err := w.Write(b) + return int64(n), err + }, + } + +} + +func ociIndexRecord(manifests []ocispec.Descriptor) tarRecord { + index := ocispec.Index{ + Versioned: ocispecs.Versioned{ + SchemaVersion: 2, + }, + Manifests: manifests, + } + + b, err := json.Marshal(index) + if err != nil { + panic(err) + } + + return tarRecord{ + Header: &tar.Header{ + Name: "index.json", + Mode: 0644, + Size: int64(len(b)), + Typeflag: tar.TypeReg, + }, + CopyTo: func(ctx context.Context, w io.Writer) (int64, error) { + n, err := w.Write(b) + return int64(n), err + }, + } +} + +type exportManifest struct { + manifest ocispec.Descriptor + names []string +} + +func manifestsRecord(ctx context.Context, store content.Provider, manifests map[digest.Digest]*exportManifest) (tarRecord, error) { + mfsts := make([]struct { + Config string + RepoTags []string + Layers []string + }, len(manifests)) + + var i int + for _, m := range manifests { + p, err := content.ReadBlob(ctx, store, m.manifest) + if err != nil { + return tarRecord{}, err + } + + var manifest ocispec.Manifest + if err := json.Unmarshal(p, &manifest); err != nil { + return tarRecord{}, err + } + if err := manifest.Config.Digest.Validate(); err != nil { + return tarRecord{}, errors.Wrapf(err, "invalid manifest %q", m.manifest.Digest) + } + + dgst := manifest.Config.Digest + mfsts[i].Config = path.Join("blobs", dgst.Algorithm().String(), dgst.Encoded()) + for _, l := range manifest.Layers { + path := path.Join("blobs", l.Digest.Algorithm().String(), l.Digest.Encoded()) + mfsts[i].Layers = append(mfsts[i].Layers, path) + } + + for _, name := range m.names { + nname, err := familiarizeReference(name) + if err != nil { + return tarRecord{}, err + } + + mfsts[i].RepoTags = append(mfsts[i].RepoTags, nname) + } + + i++ + } + + b, err := json.Marshal(mfsts) + if err != nil { + return tarRecord{}, err + } + + return tarRecord{ + Header: &tar.Header{ + Name: "manifest.json", + Mode: 0644, + Size: int64(len(b)), + Typeflag: tar.TypeReg, + }, + CopyTo: func(ctx context.Context, w io.Writer) (int64, error) { + n, err := w.Write(b) + return int64(n), err + }, + }, nil +} + +func writeTar(ctx context.Context, tw *tar.Writer, recordsWithEmpty []tarRecord) error { + var records []tarRecord + for _, r := range recordsWithEmpty { + if r.Header != nil { + records = append(records, r) + } + } + sort.Slice(records, func(i, j int) bool { + return records[i].Header.Name < records[j].Header.Name + }) + + var last string + for _, record := range records { + if record.Header.Name == last { + continue + } + last = record.Header.Name + if err := tw.WriteHeader(record.Header); err != nil { + return err + } + if record.CopyTo != nil { + n, err := record.CopyTo(ctx, tw) + if err != nil { + return err + } + if n != record.Header.Size { + return errors.Errorf("unexpected copy size for %s", record.Header.Name) + } + } else if record.Header.Size > 0 { + return errors.Errorf("no content to write to record with non-zero size for %s", record.Header.Name) + } + } + return nil +} diff --git a/vendor/github.com/containerd/containerd/images/archive/importer.go b/vendor/github.com/containerd/containerd/images/archive/importer.go index c1522df461d03..2d046589f8dd6 100644 --- a/vendor/github.com/containerd/containerd/images/archive/importer.go +++ b/vendor/github.com/containerd/containerd/images/archive/importer.go @@ -22,12 +22,14 @@ import ( "bytes" "context" "encoding/json" + "fmt" "io" "io/ioutil" "path" "github.com/containerd/containerd/archive/compression" "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/log" digest "github.com/opencontainers/go-digest" @@ -36,15 +38,30 @@ import ( "github.com/pkg/errors" ) -// ImportIndex imports an index from a tar achive image bundle +type importOpts struct { + compress bool +} + +// ImportOpt is an option for importing an OCI index +type ImportOpt func(*importOpts) error + +// WithImportCompression compresses uncompressed layers on import. +// This is used for import formats which do not include the manifest. +func WithImportCompression() ImportOpt { + return func(io *importOpts) error { + io.compress = true + return nil + } +} + +// ImportIndex imports an index from a tar archive image bundle // - implements Docker v1.1, v1.2 and OCI v1. // - prefers OCI v1 when provided // - creates OCI index for Docker formats // - normalizes Docker references and adds as OCI ref name // e.g. alpine:latest -> docker.io/library/alpine:latest // - existing OCI reference names are untouched -// - TODO: support option to compress layers on ingest -func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (ocispec.Descriptor, error) { +func ImportIndex(ctx context.Context, store content.Store, reader io.Reader, opts ...ImportOpt) (ocispec.Descriptor, error) { var ( tr = tar.NewReader(reader) @@ -56,7 +73,15 @@ func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (oc } symlinks = make(map[string]string) blobs = make(map[string]ocispec.Descriptor) + iopts importOpts ) + + for _, o := range opts { + if err := o(&iopts); err != nil { + return ocispec.Descriptor{}, err + } + } + for { hdr, err := tr.Next() if err == io.EOF { @@ -99,7 +124,7 @@ func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (oc } // If OCI layout was given, interpret the tar as an OCI layout. - // When not provided, the layout of the tar will be interpretted + // When not provided, the layout of the tar will be interpreted // as Docker v1.1 or v1.2. if ociLayout.Version != "" { if ociLayout.Version != ocispec.ImageLayoutVersion { @@ -137,22 +162,26 @@ func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (oc if !ok { return ocispec.Descriptor{}, errors.Errorf("image config %q not found", mfst.Config) } - config.MediaType = ocispec.MediaTypeImageConfig + config.MediaType = images.MediaTypeDockerSchema2Config - layers, err := resolveLayers(ctx, store, mfst.Layers, blobs) + layers, err := resolveLayers(ctx, store, mfst.Layers, blobs, iopts.compress) if err != nil { return ocispec.Descriptor{}, errors.Wrap(err, "failed to resolve layers") } - manifest := ocispec.Manifest{ - Versioned: specs.Versioned{ - SchemaVersion: 2, - }, - Config: config, - Layers: layers, + manifest := struct { + SchemaVersion int `json:"schemaVersion"` + MediaType string `json:"mediaType"` + Config ocispec.Descriptor `json:"config"` + Layers []ocispec.Descriptor `json:"layers"` + }{ + SchemaVersion: 2, + MediaType: images.MediaTypeDockerSchema2Manifest, + Config: config, + Layers: layers, } - desc, err := writeManifest(ctx, store, manifest, ocispec.MediaTypeImageManifest) + desc, err := writeManifest(ctx, store, manifest, manifest.MediaType) if err != nil { return ocispec.Descriptor{}, errors.Wrap(err, "write docker manifest") } @@ -164,7 +193,7 @@ func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (oc if len(platforms) > 0 { // Only one platform can be resolved from non-index manifest, // The platform can only come from the config included above, - // if the config has no platform it can be safely ommitted. + // if the config has no platform it can be safely omitted. desc.Platform = &platforms[0] } @@ -181,7 +210,8 @@ func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (oc } mfstdesc.Annotations = map[string]string{ - ocispec.AnnotationRefName: normalized, + images.AnnotationImageName: normalized, + ocispec.AnnotationRefName: ociReferenceName(normalized), } idx.Manifests = append(idx.Manifests, mfstdesc) @@ -197,10 +227,7 @@ func onUntarJSON(r io.Reader, j interface{}) error { if err != nil { return err } - if err := json.Unmarshal(b, j); err != nil { - return err - } - return nil + return json.Unmarshal(b, j) } func onUntarBlob(ctx context.Context, r io.Reader, store content.Ingester, size int64, ref string) (digest.Digest, error) { @@ -213,36 +240,118 @@ func onUntarBlob(ctx context.Context, r io.Reader, store content.Ingester, size return dgstr.Digest(), nil } -func resolveLayers(ctx context.Context, store content.Store, layerFiles []string, blobs map[string]ocispec.Descriptor) ([]ocispec.Descriptor, error) { - var layers []ocispec.Descriptor - for _, f := range layerFiles { +func resolveLayers(ctx context.Context, store content.Store, layerFiles []string, blobs map[string]ocispec.Descriptor, compress bool) ([]ocispec.Descriptor, error) { + layers := make([]ocispec.Descriptor, len(layerFiles)) + descs := map[digest.Digest]*ocispec.Descriptor{} + filters := []string{} + for i, f := range layerFiles { desc, ok := blobs[f] if !ok { return nil, errors.Errorf("layer %q not found", f) } + layers[i] = desc + descs[desc.Digest] = &layers[i] + filters = append(filters, "labels.\"containerd.io/uncompressed\"=="+desc.Digest.String()) + } + + err := store.Walk(ctx, func(info content.Info) error { + dgst, ok := info.Labels["containerd.io/uncompressed"] + if ok { + desc := descs[digest.Digest(dgst)] + if desc != nil { + desc.MediaType = images.MediaTypeDockerSchema2LayerGzip + desc.Digest = info.Digest + desc.Size = info.Size + } + } + return nil + }, filters...) + if err != nil { + return nil, errors.Wrap(err, "failure checking for compressed blobs") + } + for i, desc := range layers { + if desc.MediaType != "" { + continue + } // Open blob, resolve media type ra, err := store.ReaderAt(ctx, desc) if err != nil { - return nil, errors.Wrapf(err, "failed to open %q (%s)", f, desc.Digest) + return nil, errors.Wrapf(err, "failed to open %q (%s)", layerFiles[i], desc.Digest) } s, err := compression.DecompressStream(content.NewReader(ra)) if err != nil { - return nil, errors.Wrapf(err, "failed to detect compression for %q", f) + return nil, errors.Wrapf(err, "failed to detect compression for %q", layerFiles[i]) } if s.GetCompression() == compression.Uncompressed { - // TODO: Support compressing and writing back to content store - desc.MediaType = ocispec.MediaTypeImageLayer + if compress { + ref := fmt.Sprintf("compress-blob-%s-%s", desc.Digest.Algorithm().String(), desc.Digest.Encoded()) + labels := map[string]string{ + "containerd.io/uncompressed": desc.Digest.String(), + } + layers[i], err = compressBlob(ctx, store, s, ref, content.WithLabels(labels)) + if err != nil { + s.Close() + return nil, err + } + layers[i].MediaType = images.MediaTypeDockerSchema2LayerGzip + } else { + layers[i].MediaType = images.MediaTypeDockerSchema2Layer + } } else { - desc.MediaType = ocispec.MediaTypeImageLayerGzip + layers[i].MediaType = images.MediaTypeDockerSchema2LayerGzip } s.Close() - layers = append(layers, desc) } return layers, nil } +func compressBlob(ctx context.Context, cs content.Store, r io.Reader, ref string, opts ...content.Opt) (desc ocispec.Descriptor, err error) { + w, err := content.OpenWriter(ctx, cs, content.WithRef(ref)) + if err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "failed to open writer") + } + + defer func() { + w.Close() + if err != nil { + cs.Abort(ctx, ref) + } + }() + if err := w.Truncate(0); err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "failed to truncate writer") + } + + cw, err := compression.CompressStream(w, compression.Gzip) + if err != nil { + return ocispec.Descriptor{}, err + } + + if _, err := io.Copy(cw, r); err != nil { + return ocispec.Descriptor{}, err + } + if err := cw.Close(); err != nil { + return ocispec.Descriptor{}, err + } + + cst, err := w.Status() + if err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "failed to get writer status") + } + + desc.Digest = w.Digest() + desc.Size = cst.Offset + + if err := w.Commit(ctx, desc.Size, desc.Digest, opts...); err != nil { + if !errdefs.IsAlreadyExists(err) { + return ocispec.Descriptor{}, errors.Wrap(err, "failed to commit") + } + } + + return desc, nil +} + func writeManifest(ctx context.Context, cs content.Ingester, manifest interface{}, mediaType string) (ocispec.Descriptor, error) { manifestBytes, err := json.Marshal(manifest) if err != nil { diff --git a/vendor/github.com/containerd/containerd/images/archive/reference.go b/vendor/github.com/containerd/containerd/images/archive/reference.go index 0b1310181e426..ce9fe98f91b03 100644 --- a/vendor/github.com/containerd/containerd/images/archive/reference.go +++ b/vendor/github.com/containerd/containerd/images/archive/reference.go @@ -19,8 +19,9 @@ package archive import ( "strings" - "github.com/containerd/cri/pkg/util" - digest "github.com/opencontainers/go-digest" + "github.com/containerd/containerd/reference" + distref "github.com/containerd/containerd/reference/docker" + "github.com/opencontainers/go-digest" "github.com/pkg/errors" ) @@ -69,7 +70,7 @@ func isImagePrefix(s, prefix string) bool { func normalizeReference(ref string) (string, error) { // TODO: Replace this function to not depend on reference package - normalized, err := util.NormalizeImageRef(ref) + normalized, err := distref.ParseDockerRef(ref) if err != nil { return "", errors.Wrapf(err, "normalize image ref %q", ref) } @@ -77,6 +78,31 @@ func normalizeReference(ref string) (string, error) { return normalized.String(), nil } +func familiarizeReference(ref string) (string, error) { + named, err := distref.ParseNormalizedNamed(ref) + if err != nil { + return "", errors.Wrapf(err, "failed to parse %q", ref) + } + named = distref.TagNameOnly(named) + + return distref.FamiliarString(named), nil +} + +func ociReferenceName(name string) string { + // OCI defines the reference name as only a tag excluding the + // repository. The containerd annotation contains the full image name + // since the tag is insufficient for correctly naming and referring to an + // image + var ociRef string + if spec, err := reference.Parse(name); err == nil { + ociRef = spec.Object + } else { + ociRef = name + } + + return ociRef +} + // DigestTranslator creates a digest reference by adding the // digest to an image name func DigestTranslator(prefix string) func(digest.Digest) string { diff --git a/vendor/github.com/containerd/containerd/images/diffid.go b/vendor/github.com/containerd/containerd/images/diffid.go new file mode 100644 index 0000000000000..56193cc289f7b --- /dev/null +++ b/vendor/github.com/containerd/containerd/images/diffid.go @@ -0,0 +1,81 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package images + +import ( + "context" + "io" + + "github.com/containerd/containerd/archive/compression" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/labels" + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/sirupsen/logrus" +) + +// GetDiffID gets the diff ID of the layer blob descriptor. +func GetDiffID(ctx context.Context, cs content.Store, desc ocispec.Descriptor) (digest.Digest, error) { + switch desc.MediaType { + case + // If the layer is already uncompressed, we can just return its digest + MediaTypeDockerSchema2Layer, + ocispec.MediaTypeImageLayer, + MediaTypeDockerSchema2LayerForeign, + ocispec.MediaTypeImageLayerNonDistributable: + return desc.Digest, nil + } + info, err := cs.Info(ctx, desc.Digest) + if err != nil { + return "", err + } + v, ok := info.Labels[labels.LabelUncompressed] + if ok { + // Fast path: if the image is already unpacked, we can use the label value + return digest.Parse(v) + } + // if the image is not unpacked, we may not have the label + ra, err := cs.ReaderAt(ctx, desc) + if err != nil { + return "", err + } + defer ra.Close() + r := content.NewReader(ra) + uR, err := compression.DecompressStream(r) + if err != nil { + return "", err + } + defer uR.Close() + digester := digest.Canonical.Digester() + hashW := digester.Hash() + if _, err := io.Copy(hashW, uR); err != nil { + return "", err + } + if err := ra.Close(); err != nil { + return "", err + } + digest := digester.Digest() + // memorize the computed value + if info.Labels == nil { + info.Labels = make(map[string]string) + } + info.Labels[labels.LabelUncompressed] = digest.String() + if _, err := cs.Update(ctx, info, "labels"); err != nil { + logrus.WithError(err).Warnf("failed to set %s label for %s", labels.LabelUncompressed, desc.Digest) + } + return digest, nil +} diff --git a/vendor/github.com/containerd/containerd/images/handlers.go b/vendor/github.com/containerd/containerd/images/handlers.go index 230a9caf85f5b..05a9017bc27eb 100644 --- a/vendor/github.com/containerd/containerd/images/handlers.go +++ b/vendor/github.com/containerd/containerd/images/handlers.go @@ -22,10 +22,12 @@ import ( "sort" "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/platforms" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" ) var ( @@ -62,7 +64,7 @@ func Handlers(handlers ...Handler) HandlerFunc { for _, handler := range handlers { ch, err := handler.Handle(ctx, desc) if err != nil { - if errors.Cause(err) == ErrStopHandler { + if errors.Is(err, ErrStopHandler) { break } return nil, err @@ -85,7 +87,7 @@ func Walk(ctx context.Context, handler Handler, descs ...ocispec.Descriptor) err children, err := handler.Handle(ctx, desc) if err != nil { - if errors.Cause(err) == ErrSkipDesc { + if errors.Is(err, ErrSkipDesc) { continue // don't traverse the children. } return err @@ -108,28 +110,40 @@ func Walk(ctx context.Context, handler Handler, descs ...ocispec.Descriptor) err // handler may return `ErrSkipDesc` to signal to the dispatcher to not traverse // any children. // +// A concurrency limiter can be passed in to limit the number of concurrent +// handlers running. When limiter is nil, there is no limit. +// // Typically, this function will be used with `FetchHandler`, often composed // with other handlers. // // If any handler returns an error, the dispatch session will be canceled. -func Dispatch(ctx context.Context, handler Handler, descs ...ocispec.Descriptor) error { - eg, ctx := errgroup.WithContext(ctx) +func Dispatch(ctx context.Context, handler Handler, limiter *semaphore.Weighted, descs ...ocispec.Descriptor) error { + eg, ctx2 := errgroup.WithContext(ctx) for _, desc := range descs { desc := desc + if limiter != nil { + if err := limiter.Acquire(ctx, 1); err != nil { + return err + } + } + eg.Go(func() error { desc := desc - children, err := handler.Handle(ctx, desc) + children, err := handler.Handle(ctx2, desc) + if limiter != nil { + limiter.Release(1) + } if err != nil { - if errors.Cause(err) == ErrSkipDesc { + if errors.Is(err, ErrSkipDesc) { return nil // don't traverse the children. } return err } if len(children) > 0 { - return Dispatch(ctx, handler, children...) + return Dispatch(ctx2, handler, limiter, children...) } return nil @@ -156,6 +170,19 @@ func ChildrenHandler(provider content.Provider) HandlerFunc { // the children returned by the handler and passes through the children. // Must follow a handler that returns the children to be labeled. func SetChildrenLabels(manager content.Manager, f HandlerFunc) HandlerFunc { + return SetChildrenMappedLabels(manager, f, nil) +} + +// SetChildrenMappedLabels is a handler wrapper which sets labels for the content on +// the children returned by the handler and passes through the children. +// Must follow a handler that returns the children to be labeled. +// The label map allows the caller to control the labels per child descriptor. +// For returned labels, the index of the child will be appended to the end +// except for the first index when the returned label does not end with '.'. +func SetChildrenMappedLabels(manager content.Manager, f HandlerFunc, labelMap func(ocispec.Descriptor) []string) HandlerFunc { + if labelMap == nil { + labelMap = ChildGCLabels + } return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { children, err := f(ctx, desc) if err != nil { @@ -163,14 +190,26 @@ func SetChildrenLabels(manager content.Manager, f HandlerFunc) HandlerFunc { } if len(children) > 0 { - info := content.Info{ - Digest: desc.Digest, - Labels: map[string]string{}, - } - fields := []string{} - for i, ch := range children { - info.Labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = ch.Digest.String() - fields = append(fields, fmt.Sprintf("labels.containerd.io/gc.ref.content.%d", i)) + var ( + info = content.Info{ + Digest: desc.Digest, + Labels: map[string]string{}, + } + fields = []string{} + keys = map[string]uint{} + ) + for _, ch := range children { + labelKeys := labelMap(ch) + for _, key := range labelKeys { + idx := keys[key] + keys[key] = idx + 1 + if idx > 0 || key[len(key)-1] == '.' { + key = fmt.Sprintf("%s%d", key, idx) + } + + info.Labels[key] = ch.Digest.String() + fields = append(fields, "labels."+key) + } } _, err := manager.Update(ctx, info, fields...) @@ -213,6 +252,7 @@ func FilterPlatforms(f HandlerFunc, m platforms.Matcher) HandlerFunc { // The results will be ordered according to the comparison operator and // use the ordering in the manifests for equal matches. // A limit of 0 or less is considered no limit. +// A not found error is returned if no manifest is matched. func LimitManifests(f HandlerFunc, m platforms.MatchComparer, n int) HandlerFunc { return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { children, err := f(ctx, desc) @@ -232,8 +272,13 @@ func LimitManifests(f HandlerFunc, m platforms.MatchComparer, n int) HandlerFunc return m.Less(*children[i].Platform, *children[j].Platform) }) - if n > 0 && len(children) > n { - children = children[:n] + if n > 0 { + if len(children) == 0 { + return children, errors.Wrap(errdefs.ErrNotFound, "no match for platform in manifest") + } + if len(children) > n { + children = children[:n] + } } default: // only limit manifests from an index diff --git a/vendor/github.com/containerd/containerd/images/image.go b/vendor/github.com/containerd/containerd/images/image.go index f72684d82946e..2e5cd61c9a7aa 100644 --- a/vendor/github.com/containerd/containerd/images/image.go +++ b/vendor/github.com/containerd/containerd/images/image.go @@ -19,8 +19,8 @@ package images import ( "context" "encoding/json" + "fmt" "sort" - "strings" "time" "github.com/containerd/containerd/content" @@ -119,7 +119,7 @@ func (image *Image) Size(ctx context.Context, provider content.Provider, platfor } size += desc.Size return nil, nil - }), FilterPlatforms(ChildrenHandler(provider), platform)), image.Target) + }), LimitManifests(FilterPlatforms(ChildrenHandler(provider), platform), platform, 1)), image.Target) } type platformManifest struct { @@ -142,6 +142,7 @@ type platformManifest struct { // this direction because this abstraction is not needed.` func Manifest(ctx context.Context, provider content.Provider, image ocispec.Descriptor, platform platforms.MatchComparer) (ocispec.Manifest, error) { var ( + limit = 1 m []platformManifest wasIndex bool ) @@ -154,6 +155,10 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc return nil, err } + if err := validateMediaType(p, desc.MediaType); err != nil { + return nil, errors.Wrapf(err, "manifest: invalid desc %s", desc.Digest) + } + var manifest ocispec.Manifest if err := json.Unmarshal(p, &manifest); err != nil { return nil, err @@ -194,6 +199,10 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc return nil, err } + if err := validateMediaType(p, desc.MediaType); err != nil { + return nil, errors.Wrapf(err, "manifest: invalid desc %s", desc.Digest) + } + var idx ocispec.Index if err := json.Unmarshal(p, &idx); err != nil { return nil, err @@ -210,10 +219,22 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc } } + sort.SliceStable(descs, func(i, j int) bool { + if descs[i].Platform == nil { + return false + } + if descs[j].Platform == nil { + return true + } + return platform.Less(*descs[i].Platform, *descs[j].Platform) + }) + wasIndex = true + if len(descs) > limit { + return descs[:limit], nil + } return descs, nil - } return nil, errors.Wrapf(errdefs.ErrNotFound, "unexpected media type %v for %v", desc.MediaType, desc.Digest) }), image); err != nil { @@ -227,17 +248,6 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc } return ocispec.Manifest{}, err } - - sort.SliceStable(m, func(i, j int) bool { - if m[i].p == nil { - return false - } - if m[j].p == nil { - return true - } - return platform.Less(*m[i].p, *m[j].p) - }) - return *m[0].m, nil } @@ -288,7 +298,7 @@ func Platforms(ctx context.Context, provider content.Provider, image ocispec.Des // If available is true, the caller can assume that required represents the // complete set of content required for the image. // -// missing will have the components that are part of required but not avaiiable +// missing will have the components that are part of required but not available // in the provider. // // If there is a problem resolving content, an error will be returned. @@ -335,6 +345,10 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr return nil, err } + if err := validateMediaType(p, desc.MediaType); err != nil { + return nil, errors.Wrapf(err, "children: invalid desc %s", desc.Digest) + } + // TODO(stevvooe): We just assume oci manifest, for now. There may be // subtle differences from the docker version. var manifest ocispec.Manifest @@ -350,27 +364,65 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr return nil, err } + if err := validateMediaType(p, desc.MediaType); err != nil { + return nil, errors.Wrapf(err, "children: invalid desc %s", desc.Digest) + } + var index ocispec.Index if err := json.Unmarshal(p, &index); err != nil { return nil, err } descs = append(descs, index.Manifests...) - case MediaTypeDockerSchema2Layer, MediaTypeDockerSchema2LayerGzip, - MediaTypeDockerSchema2LayerForeign, MediaTypeDockerSchema2LayerForeignGzip, - MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig, - ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerGzip, - ocispec.MediaTypeImageLayerNonDistributable, ocispec.MediaTypeImageLayerNonDistributableGzip, - MediaTypeContainerd1Checkpoint, MediaTypeContainerd1CheckpointConfig: - // childless data types. - return nil, nil default: - log.G(ctx).Warnf("encountered unknown type %v; children may not be fetched", desc.MediaType) + if IsLayerType(desc.MediaType) || IsKnownConfig(desc.MediaType) { + // childless data types. + return nil, nil + } + log.G(ctx).Debugf("encountered unknown type %v; children may not be fetched", desc.MediaType) } return descs, nil } +// unknownDocument represents a manifest, manifest list, or index that has not +// yet been validated. +type unknownDocument struct { + MediaType string `json:"mediaType,omitempty"` + Config json.RawMessage `json:"config,omitempty"` + Layers json.RawMessage `json:"layers,omitempty"` + Manifests json.RawMessage `json:"manifests,omitempty"` + FSLayers json.RawMessage `json:"fsLayers,omitempty"` // schema 1 +} + +// validateMediaType returns an error if the byte slice is invalid JSON or if +// the media type identifies the blob as one format but it contains elements of +// another format. +func validateMediaType(b []byte, mt string) error { + var doc unknownDocument + if err := json.Unmarshal(b, &doc); err != nil { + return err + } + if len(doc.FSLayers) != 0 { + return fmt.Errorf("media-type: schema 1 not supported") + } + switch mt { + case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: + if len(doc.Manifests) != 0 || + doc.MediaType == MediaTypeDockerSchema2ManifestList || + doc.MediaType == ocispec.MediaTypeImageIndex { + return fmt.Errorf("media-type: expected manifest but found index (%s)", mt) + } + case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: + if len(doc.Config) != 0 || len(doc.Layers) != 0 || + doc.MediaType == MediaTypeDockerSchema2Manifest || + doc.MediaType == ocispec.MediaTypeImageManifest { + return fmt.Errorf("media-type: expected index but found manifest (%s)", mt) + } + } + return nil +} + // RootFS returns the unpacked diffids that make up and images rootfs. // // These are used to verify that a set of layers unpacked to the expected @@ -387,22 +439,3 @@ func RootFS(ctx context.Context, provider content.Provider, configDesc ocispec.D } return config.RootFS.DiffIDs, nil } - -// IsCompressedDiff returns true if mediaType is a known compressed diff media type. -// It returns false if the media type is a diff, but not compressed. If the media type -// is not a known diff type, it returns errdefs.ErrNotImplemented -func IsCompressedDiff(ctx context.Context, mediaType string) (bool, error) { - switch mediaType { - case ocispec.MediaTypeImageLayer, MediaTypeDockerSchema2Layer: - case ocispec.MediaTypeImageLayerGzip, MediaTypeDockerSchema2LayerGzip: - return true, nil - default: - // Still apply all generic media types *.tar[.+]gzip and *.tar - if strings.HasSuffix(mediaType, ".tar.gzip") || strings.HasSuffix(mediaType, ".tar+gzip") { - return true, nil - } else if !strings.HasSuffix(mediaType, ".tar") { - return false, errdefs.ErrNotImplemented - } - } - return false, nil -} diff --git a/vendor/github.com/containerd/containerd/images/mediatypes.go b/vendor/github.com/containerd/containerd/images/mediatypes.go index ca4ca071b32ad..785d71291e29f 100644 --- a/vendor/github.com/containerd/containerd/images/mediatypes.go +++ b/vendor/github.com/containerd/containerd/images/mediatypes.go @@ -16,6 +16,16 @@ package images +import ( + "context" + "sort" + "strings" + + "github.com/containerd/containerd/errdefs" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + // mediatype definitions for image components handled in containerd. // // oci components are generally referenced directly, although we may centralize @@ -29,11 +39,166 @@ const ( MediaTypeDockerSchema2Manifest = "application/vnd.docker.distribution.manifest.v2+json" MediaTypeDockerSchema2ManifestList = "application/vnd.docker.distribution.manifest.list.v2+json" // Checkpoint/Restore Media Types - MediaTypeContainerd1Checkpoint = "application/vnd.containerd.container.criu.checkpoint.criu.tar" - MediaTypeContainerd1CheckpointPreDump = "application/vnd.containerd.container.criu.checkpoint.predump.tar" - MediaTypeContainerd1Resource = "application/vnd.containerd.container.resource.tar" - MediaTypeContainerd1RW = "application/vnd.containerd.container.rw.tar" - MediaTypeContainerd1CheckpointConfig = "application/vnd.containerd.container.checkpoint.config.v1+proto" + MediaTypeContainerd1Checkpoint = "application/vnd.containerd.container.criu.checkpoint.criu.tar" + MediaTypeContainerd1CheckpointPreDump = "application/vnd.containerd.container.criu.checkpoint.predump.tar" + MediaTypeContainerd1Resource = "application/vnd.containerd.container.resource.tar" + MediaTypeContainerd1RW = "application/vnd.containerd.container.rw.tar" + MediaTypeContainerd1CheckpointConfig = "application/vnd.containerd.container.checkpoint.config.v1+proto" + MediaTypeContainerd1CheckpointOptions = "application/vnd.containerd.container.checkpoint.options.v1+proto" + MediaTypeContainerd1CheckpointRuntimeName = "application/vnd.containerd.container.checkpoint.runtime.name" + MediaTypeContainerd1CheckpointRuntimeOptions = "application/vnd.containerd.container.checkpoint.runtime.options+proto" // Legacy Docker schema1 manifest MediaTypeDockerSchema1Manifest = "application/vnd.docker.distribution.manifest.v1+prettyjws" + // Encypted media types + MediaTypeImageLayerEncrypted = ocispec.MediaTypeImageLayer + "+encrypted" + MediaTypeImageLayerGzipEncrypted = ocispec.MediaTypeImageLayerGzip + "+encrypted" ) + +// DiffCompression returns the compression as defined by the layer diff media +// type. For Docker media types without compression, "unknown" is returned to +// indicate that the media type may be compressed. If the media type is not +// recognized as a layer diff, then it returns errdefs.ErrNotImplemented +func DiffCompression(ctx context.Context, mediaType string) (string, error) { + base, ext := parseMediaTypes(mediaType) + switch base { + case MediaTypeDockerSchema2Layer, MediaTypeDockerSchema2LayerForeign: + if len(ext) > 0 { + // Type is wrapped + return "", nil + } + // These media types may have been compressed but failed to + // use the correct media type. The decompression function + // should detect and handle this case. + return "unknown", nil + case MediaTypeDockerSchema2LayerGzip, MediaTypeDockerSchema2LayerForeignGzip: + if len(ext) > 0 { + // Type is wrapped + return "", nil + } + return "gzip", nil + case ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerNonDistributable: + if len(ext) > 0 { + switch ext[len(ext)-1] { + case "gzip": + return "gzip", nil + case "zstd": + return "zstd", nil + } + } + return "", nil + default: + return "", errors.Wrapf(errdefs.ErrNotImplemented, "unrecognised mediatype %s", mediaType) + } +} + +// parseMediaTypes splits the media type into the base type and +// an array of sorted extensions +func parseMediaTypes(mt string) (string, []string) { + if mt == "" { + return "", []string{} + } + + s := strings.Split(mt, "+") + ext := s[1:] + sort.Strings(ext) + + return s[0], ext +} + +// IsNonDistributable returns true if the media type is non-distributable. +func IsNonDistributable(mt string) bool { + return strings.HasPrefix(mt, "application/vnd.oci.image.layer.nondistributable.") || + strings.HasPrefix(mt, "application/vnd.docker.image.rootfs.foreign.") +} + +// IsLayerType returns true if the media type is a layer +func IsLayerType(mt string) bool { + if strings.HasPrefix(mt, "application/vnd.oci.image.layer.") { + return true + } + + // Parse Docker media types, strip off any + suffixes first + base, _ := parseMediaTypes(mt) + switch base { + case MediaTypeDockerSchema2Layer, MediaTypeDockerSchema2LayerGzip, + MediaTypeDockerSchema2LayerForeign, MediaTypeDockerSchema2LayerForeignGzip: + return true + } + return false +} + +// IsDockerType returns true if the media type has "application/vnd.docker." prefix +func IsDockerType(mt string) bool { + return strings.HasPrefix(mt, "application/vnd.docker.") +} + +// IsManifestType returns true if the media type is an OCI-compatible manifest. +// No support for schema1 manifest. +func IsManifestType(mt string) bool { + switch mt { + case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: + return true + default: + return false + } +} + +// IsIndexType returns true if the media type is an OCI-compatible index. +func IsIndexType(mt string) bool { + switch mt { + case ocispec.MediaTypeImageIndex, MediaTypeDockerSchema2ManifestList: + return true + default: + return false + } +} + +// IsConfigType returns true if the media type is an OCI-compatible image config. +// No support for containerd checkpoint configs. +func IsConfigType(mt string) bool { + switch mt { + case MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig: + return true + default: + return false + } +} + +// IsKnownConfig returns true if the media type is a known config type, +// including containerd checkpoint configs +func IsKnownConfig(mt string) bool { + switch mt { + case MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig, + MediaTypeContainerd1Checkpoint, MediaTypeContainerd1CheckpointConfig: + return true + } + return false +} + +// ChildGCLabels returns the label for a given descriptor to reference it +func ChildGCLabels(desc ocispec.Descriptor) []string { + mt := desc.MediaType + if IsKnownConfig(mt) { + return []string{"containerd.io/gc.ref.content.config"} + } + + switch mt { + case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: + return []string{"containerd.io/gc.ref.content.m."} + } + + if IsLayerType(mt) { + return []string{"containerd.io/gc.ref.content.l."} + } + + return []string{"containerd.io/gc.ref.content."} +} + +// ChildGCLabelsFilterLayers returns the labels for a given descriptor to +// reference it, skipping layer media types +func ChildGCLabelsFilterLayers(desc ocispec.Descriptor) []string { + if IsLayerType(desc.MediaType) { + return nil + } + return ChildGCLabels(desc) +} diff --git a/vendor/github.com/containerd/containerd/import.go b/vendor/github.com/containerd/containerd/import.go index 3650568240976..6080161f8412a 100644 --- a/vendor/github.com/containerd/containerd/import.go +++ b/vendor/github.com/containerd/containerd/import.go @@ -25,14 +25,17 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/images/archive" + "github.com/containerd/containerd/platforms" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) type importOpts struct { - indexName string - imageRefT func(string) string - dgstRefT func(digest.Digest) string + indexName string + imageRefT func(string) string + dgstRefT func(digest.Digest) string + allPlatforms bool + compress bool } // ImportOpt allows the caller to specify import specific options @@ -64,9 +67,26 @@ func WithIndexName(name string) ImportOpt { } } +// WithAllPlatforms is used to import content for all platforms. +func WithAllPlatforms(allPlatforms bool) ImportOpt { + return func(c *importOpts) error { + c.allPlatforms = allPlatforms + return nil + } +} + +// WithImportCompression compresses uncompressed layers on import. +// This is used for import formats which do not include the manifest. +func WithImportCompression() ImportOpt { + return func(c *importOpts) error { + c.compress = true + return nil + } +} + // Import imports an image from a Tar stream using reader. // Caller needs to specify importer. Future version may use oci.v1 as the default. -// Note that unreferrenced blobs may be imported to the content store as well. +// Note that unreferenced blobs may be imported to the content store as well. func (c *Client) Import(ctx context.Context, reader io.Reader, opts ...ImportOpt) ([]images.Image, error) { var iopts importOpts for _, o := range opts { @@ -81,7 +101,12 @@ func (c *Client) Import(ctx context.Context, reader io.Reader, opts ...ImportOpt } defer done(ctx) - index, err := archive.ImportIndex(ctx, c.ContentStore(), reader) + var aio []archive.ImportOpt + if iopts.compress { + aio = append(aio, archive.WithImportCompression()) + } + + index, err := archive.ImportIndex(ctx, c.ContentStore(), reader, aio...) if err != nil { return nil, err } @@ -98,9 +123,12 @@ func (c *Client) Import(ctx context.Context, reader io.Reader, opts ...ImportOpt Target: index, }) } + var platformMatcher = platforms.All + if !iopts.allPlatforms { + platformMatcher = c.platform + } - var handler images.HandlerFunc - handler = func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + var handler images.HandlerFunc = func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { // Only save images at top level if desc.Digest != index.Digest { return images.Children(ctx, cs, desc) @@ -117,16 +145,12 @@ func (c *Client) Import(ctx context.Context, reader io.Reader, opts ...ImportOpt } for _, m := range idx.Manifests { - if ref := m.Annotations[ocispec.AnnotationRefName]; ref != "" { - if iopts.imageRefT != nil { - ref = iopts.imageRefT(ref) - } - if ref != "" { - imgs = append(imgs, images.Image{ - Name: ref, - Target: m, - }) - } + name := imageName(m.Annotations, iopts.imageRefT) + if name != "" { + imgs = append(imgs, images.Image{ + Name: name, + Target: m, + }) } if iopts.dgstRefT != nil { ref := iopts.dgstRefT(m.Digest) @@ -142,6 +166,7 @@ func (c *Client) Import(ctx context.Context, reader io.Reader, opts ...ImportOpt return idx.Manifests, nil } + handler = images.FilterPlatforms(handler, platformMatcher) handler = images.SetChildrenLabels(cs, handler) if err := images.Walk(ctx, handler, index); err != nil { return nil, err @@ -164,3 +189,17 @@ func (c *Client) Import(ctx context.Context, reader io.Reader, opts ...ImportOpt return imgs, nil } + +func imageName(annotations map[string]string, ociCleanup func(string) string) string { + name := annotations[images.AnnotationImageName] + if name != "" { + return name + } + name = annotations[ocispec.AnnotationRefName] + if name != "" { + if ociCleanup != nil { + name = ociCleanup(name) + } + } + return name +} diff --git a/vendor/github.com/containerd/containerd/install.go b/vendor/github.com/containerd/containerd/install.go index 5e4c6a2c8d40e..7a8311c832183 100644 --- a/vendor/github.com/containerd/containerd/install.go +++ b/vendor/github.com/containerd/containerd/install.go @@ -21,13 +21,13 @@ import ( "context" "os" "path/filepath" + "runtime" + "strings" - introspectionapi "github.com/containerd/containerd/api/services/introspection/v1" "github.com/containerd/containerd/archive" "github.com/containerd/containerd/archive/compression" "github.com/containerd/containerd/content" "github.com/containerd/containerd/images" - "github.com/containerd/containerd/platforms" "github.com/pkg/errors" ) @@ -43,12 +43,21 @@ func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) } var ( cs = image.ContentStore() - platform = platforms.Default() + platform = c.platform ) manifest, err := images.Manifest(ctx, cs, image.Target(), platform) if err != nil { return err } + + var binDir, libDir string + if runtime.GOOS == "windows" { + binDir = "Files\\bin" + libDir = "Files\\lib" + } else { + binDir = "bin" + libDir = "lib" + } for _, layer := range manifest.Layers { ra, err := cs.ReaderAt(ctx, layer) if err != nil { @@ -59,12 +68,16 @@ func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) if err != nil { return err } - defer r.Close() if _, err := archive.Apply(ctx, path, r, archive.WithFilter(func(hdr *tar.Header) (bool, error) { d := filepath.Dir(hdr.Name) - result := d == "bin" + result := d == binDir + if config.Libs { - result = result || d == "lib" + result = result || d == libDir + } + + if runtime.GOOS == "windows" { + hdr.Name = strings.Replace(hdr.Name, "Files", "", 1) } if result && !config.Replace { if _, err := os.Lstat(filepath.Join(path, hdr.Name)); err == nil { @@ -73,8 +86,10 @@ func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) } return result, nil })); err != nil { + r.Close() return err } + r.Close() } return nil } @@ -83,11 +98,8 @@ func (c *Client) getInstallPath(ctx context.Context, config InstallConfig) (stri if config.Path != "" { return config.Path, nil } - resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{ - Filters: []string{ - "id==opt", - }, - }) + filters := []string{"id==opt"} + resp, err := c.IntrospectionService().Plugins(ctx, filters) if err != nil { return "", err } diff --git a/vendor/github.com/containerd/containerd/labels/labels.go b/vendor/github.com/containerd/containerd/labels/labels.go new file mode 100644 index 0000000000000..d76ff2cf9c999 --- /dev/null +++ b/vendor/github.com/containerd/containerd/labels/labels.go @@ -0,0 +1,21 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package labels + +// LabelUncompressed is added to compressed layer contents. +// The value is digest of the uncompressed content. +const LabelUncompressed = "containerd.io/uncompressed" diff --git a/vendor/github.com/containerd/containerd/lease.go b/vendor/github.com/containerd/containerd/lease.go index d46b79d9f15dd..07ea6d932419f 100644 --- a/vendor/github.com/containerd/containerd/lease.go +++ b/vendor/github.com/containerd/containerd/lease.go @@ -24,19 +24,27 @@ import ( ) // WithLease attaches a lease on the context -func (c *Client) WithLease(ctx context.Context) (context.Context, func(context.Context) error, error) { +func (c *Client) WithLease(ctx context.Context, opts ...leases.Opt) (context.Context, func(context.Context) error, error) { + nop := func(context.Context) error { return nil } + _, ok := leases.FromContext(ctx) if ok { - return ctx, func(context.Context) error { - return nil - }, nil + return ctx, nop, nil } ls := c.LeasesService() - l, err := ls.Create(ctx, leases.WithRandomID(), leases.WithExpiration(24*time.Hour)) + if len(opts) == 0 { + // Use default lease configuration if no options provided + opts = []leases.Opt{ + leases.WithRandomID(), + leases.WithExpiration(24 * time.Hour), + } + } + + l, err := ls.Create(ctx, opts...) if err != nil { - return nil, nil, err + return ctx, nop, err } ctx = leases.WithLease(ctx, l.ID) diff --git a/vendor/github.com/containerd/containerd/leases/lease.go b/vendor/github.com/containerd/containerd/leases/lease.go index 909b4ea0bb184..058d065594f35 100644 --- a/vendor/github.com/containerd/containerd/leases/lease.go +++ b/vendor/github.com/containerd/containerd/leases/lease.go @@ -32,6 +32,9 @@ type Manager interface { Create(context.Context, ...Opt) (Lease, error) Delete(context.Context, Lease, ...DeleteOpt) error List(context.Context, ...string) ([]Lease, error) + AddResource(context.Context, Lease, Resource) error + DeleteResource(context.Context, Lease, Resource) error + ListResources(context.Context, Lease) ([]Resource, error) } // Lease retains resources to prevent cleanup before @@ -42,6 +45,13 @@ type Lease struct { Labels map[string]string } +// Resource represents low level resource of image, like content, ingest and +// snapshotter. +type Resource struct { + ID string + Type string +} + // DeleteOptions provide options on image delete type DeleteOptions struct { Synchronous bool diff --git a/vendor/github.com/containerd/containerd/leases/proxy/manager.go b/vendor/github.com/containerd/containerd/leases/proxy/manager.go index 30afe5368e7ae..96cd5e653ba47 100644 --- a/vendor/github.com/containerd/containerd/leases/proxy/manager.go +++ b/vendor/github.com/containerd/containerd/leases/proxy/manager.go @@ -91,3 +91,43 @@ func (pm *proxyManager) List(ctx context.Context, filters ...string) ([]leases.L return l, nil } + +func (pm *proxyManager) AddResource(ctx context.Context, lease leases.Lease, r leases.Resource) error { + _, err := pm.client.AddResource(ctx, &leasesapi.AddResourceRequest{ + ID: lease.ID, + Resource: leasesapi.Resource{ + ID: r.ID, + Type: r.Type, + }, + }) + return errdefs.FromGRPC(err) +} + +func (pm *proxyManager) DeleteResource(ctx context.Context, lease leases.Lease, r leases.Resource) error { + _, err := pm.client.DeleteResource(ctx, &leasesapi.DeleteResourceRequest{ + ID: lease.ID, + Resource: leasesapi.Resource{ + ID: r.ID, + Type: r.Type, + }, + }) + return errdefs.FromGRPC(err) +} + +func (pm *proxyManager) ListResources(ctx context.Context, lease leases.Lease) ([]leases.Resource, error) { + resp, err := pm.client.ListResources(ctx, &leasesapi.ListResourcesRequest{ + ID: lease.ID, + }) + if err != nil { + return nil, errdefs.FromGRPC(err) + } + + rs := make([]leases.Resource, 0, len(resp.Resources)) + for _, i := range resp.Resources { + rs = append(rs, leases.Resource{ + ID: i.ID, + Type: i.Type, + }) + } + return rs, nil +} diff --git a/vendor/github.com/containerd/containerd/log/context.go b/vendor/github.com/containerd/containerd/log/context.go index 3fab96b858628..37b6a7d1c8a65 100644 --- a/vendor/github.com/containerd/containerd/log/context.go +++ b/vendor/github.com/containerd/containerd/log/context.go @@ -18,7 +18,6 @@ package log import ( "context" - "sync/atomic" "github.com/sirupsen/logrus" ) @@ -30,7 +29,7 @@ var ( // messages. G = GetLogger - // L is an alias for the the standard logger. + // L is an alias for the standard logger. L = logrus.NewEntry(logrus.StandardLogger()) ) @@ -38,22 +37,17 @@ type ( loggerKey struct{} ) -// TraceLevel is the log level for tracing. Trace level is lower than debug level, -// and is usually used to trace detailed behavior of the program. -const TraceLevel = logrus.Level(uint32(logrus.DebugLevel + 1)) +const ( + // RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to + // ensure the formatted time is always the same number of characters. + RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" -// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to -// ensure the formatted time is always the same number of characters. -const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" + // TextFormat represents the text logging format + TextFormat = "text" -// ParseLevel takes a string level and returns the Logrus log level constant. -// It supports trace level. -func ParseLevel(lvl string) (logrus.Level, error) { - if lvl == "trace" { - return TraceLevel, nil - } - return logrus.ParseLevel(lvl) -} + // JSONFormat represents the JSON logging format + JSONFormat = "json" +) // WithLogger returns a new context with the provided logger. Use in // combination with logger.WithField(s) for great effect. @@ -72,19 +66,3 @@ func GetLogger(ctx context.Context) *logrus.Entry { return logger.(*logrus.Entry) } - -// Trace logs a message at level Trace with the log entry passed-in. -func Trace(e *logrus.Entry, args ...interface{}) { - level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level))) - if level >= TraceLevel { - e.Debug(args...) - } -} - -// Tracef logs a message at level Trace with the log entry passed-in. -func Tracef(e *logrus.Entry, format string, args ...interface{}) { - level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level))) - if level >= TraceLevel { - e.Debugf(format, args...) - } -} diff --git a/vendor/github.com/containerd/containerd/metadata/adaptors.go b/vendor/github.com/containerd/containerd/metadata/adaptors.go index 38539fdc12ac4..c5d576f844a10 100644 --- a/vendor/github.com/containerd/containerd/metadata/adaptors.go +++ b/vendor/github.com/containerd/containerd/metadata/adaptors.go @@ -24,6 +24,7 @@ import ( "github.com/containerd/containerd/filters" "github.com/containerd/containerd/images" "github.com/containerd/containerd/leases" + "github.com/containerd/containerd/snapshots" ) func adaptImage(o interface{}) filters.Adaptor { @@ -51,6 +52,8 @@ func adaptImage(o interface{}) filters.Adaptor { return checkMap(fieldpath[1:], obj.Labels) // TODO(stevvooe): Greater/Less than filters would be awesome for // size. Let's do it! + case "annotations": + return checkMap(fieldpath[1:], obj.Target.Annotations) } return "", false @@ -87,50 +90,59 @@ func adaptContainer(o interface{}) filters.Adaptor { }) } -func adaptContentInfo(info content.Info) filters.Adaptor { +func adaptContentStatus(status content.Status) filters.Adaptor { return filters.AdapterFunc(func(fieldpath []string) (string, bool) { if len(fieldpath) == 0 { return "", false } - switch fieldpath[0] { - case "digest": - return info.Digest.String(), true - case "size": - // TODO: support size based filtering - case "labels": - return checkMap(fieldpath[1:], info.Labels) + case "ref": + return status.Ref, true } return "", false }) } -func adaptContentStatus(status content.Status) filters.Adaptor { +func adaptLease(lease leases.Lease) filters.Adaptor { return filters.AdapterFunc(func(fieldpath []string) (string, bool) { if len(fieldpath) == 0 { return "", false } + switch fieldpath[0] { - case "ref": - return status.Ref, true + case "id": + return lease.ID, len(lease.ID) > 0 + case "labels": + return checkMap(fieldpath[1:], lease.Labels) } return "", false }) } -func adaptLease(lease leases.Lease) filters.Adaptor { +func adaptSnapshot(info snapshots.Info) filters.Adaptor { return filters.AdapterFunc(func(fieldpath []string) (string, bool) { if len(fieldpath) == 0 { return "", false } switch fieldpath[0] { - case "id": - return lease.ID, len(lease.ID) > 0 + case "kind": + switch info.Kind { + case snapshots.KindActive: + return "active", true + case snapshots.KindView: + return "view", true + case snapshots.KindCommitted: + return "committed", true + } + case "name": + return info.Name, true + case "parent": + return info.Parent, true case "labels": - return checkMap(fieldpath[1:], lease.Labels) + return checkMap(fieldpath[1:], info.Labels) } return "", false diff --git a/vendor/github.com/containerd/containerd/metadata/boltutil/helpers.go b/vendor/github.com/containerd/containerd/metadata/boltutil/helpers.go index 1240188577be1..6995917c92d7b 100644 --- a/vendor/github.com/containerd/containerd/metadata/boltutil/helpers.go +++ b/vendor/github.com/containerd/containerd/metadata/boltutil/helpers.go @@ -19,20 +19,34 @@ package boltutil import ( "time" + "github.com/gogo/protobuf/proto" + "github.com/gogo/protobuf/types" "github.com/pkg/errors" bolt "go.etcd.io/bbolt" ) var ( - bucketKeyLabels = []byte("labels") - bucketKeyCreatedAt = []byte("createdat") - bucketKeyUpdatedAt = []byte("updatedat") + bucketKeyAnnotations = []byte("annotations") + bucketKeyLabels = []byte("labels") + bucketKeyCreatedAt = []byte("createdat") + bucketKeyUpdatedAt = []byte("updatedat") + bucketKeyExtensions = []byte("extensions") ) // ReadLabels reads the labels key from the bucket // Uses the key "labels" func ReadLabels(bkt *bolt.Bucket) (map[string]string, error) { - lbkt := bkt.Bucket(bucketKeyLabels) + return readMap(bkt, bucketKeyLabels) +} + +// ReadAnnotations reads the OCI Descriptor Annotations key from the bucket +// Uses the key "annotations" +func ReadAnnotations(bkt *bolt.Bucket) (map[string]string, error) { + return readMap(bkt, bucketKeyAnnotations) +} + +func readMap(bkt *bolt.Bucket, bucketName []byte) (map[string]string, error) { + lbkt := bkt.Bucket(bucketName) if lbkt == nil { return nil, nil } @@ -53,9 +67,18 @@ func ReadLabels(bkt *bolt.Bucket) (map[string]string, error) { // bucket. Typically, this removes zero-value entries. // Uses the key "labels" func WriteLabels(bkt *bolt.Bucket, labels map[string]string) error { + return writeMap(bkt, bucketKeyLabels, labels) +} + +// WriteAnnotations writes the OCI Descriptor Annotations +func WriteAnnotations(bkt *bolt.Bucket, labels map[string]string) error { + return writeMap(bkt, bucketKeyAnnotations, labels) +} + +func writeMap(bkt *bolt.Bucket, bucketName []byte, labels map[string]string) error { // Remove existing labels to keep from merging - if lbkt := bkt.Bucket(bucketKeyLabels); lbkt != nil { - if err := bkt.DeleteBucket(bucketKeyLabels); err != nil { + if lbkt := bkt.Bucket(bucketName); lbkt != nil { + if err := bkt.DeleteBucket(bucketName); err != nil { return err } } @@ -64,7 +87,7 @@ func WriteLabels(bkt *bolt.Bucket, labels map[string]string) error { return nil } - lbkt, err := bkt.CreateBucket(bucketKeyLabels) + lbkt, err := bkt.CreateBucket(bucketName) if err != nil { return err } @@ -125,3 +148,88 @@ func WriteTimestamps(bkt *bolt.Bucket, created, updated time.Time) error { return nil } + +// WriteExtensions will write a KV map to the given bucket, +// where `K` is a string key and `V` is a protobuf's Any type that represents a generic extension. +func WriteExtensions(bkt *bolt.Bucket, extensions map[string]types.Any) error { + if len(extensions) == 0 { + return nil + } + + ebkt, err := bkt.CreateBucketIfNotExists(bucketKeyExtensions) + if err != nil { + return err + } + + for name, ext := range extensions { + p, err := proto.Marshal(&ext) + if err != nil { + return err + } + + if err := ebkt.Put([]byte(name), p); err != nil { + return err + } + } + + return nil +} + +// ReadExtensions will read back a map of extensions from the given bucket, previously written by WriteExtensions +func ReadExtensions(bkt *bolt.Bucket) (map[string]types.Any, error) { + var ( + extensions = make(map[string]types.Any) + ebkt = bkt.Bucket(bucketKeyExtensions) + ) + + if ebkt == nil { + return extensions, nil + } + + if err := ebkt.ForEach(func(k, v []byte) error { + var t types.Any + if err := proto.Unmarshal(v, &t); err != nil { + return err + } + + extensions[string(k)] = t + return nil + }); err != nil { + return nil, err + } + + return extensions, nil +} + +// WriteAny write a protobuf's Any type to the bucket +func WriteAny(bkt *bolt.Bucket, name []byte, any *types.Any) error { + if any == nil { + return nil + } + + data, err := proto.Marshal(any) + if err != nil { + return err + } + + if err := bkt.Put(name, data); err != nil { + return err + } + + return nil +} + +// ReadAny reads back protobuf's Any type from the bucket +func ReadAny(bkt *bolt.Bucket, name []byte) (*types.Any, error) { + bytes := bkt.Get(name) + if bytes == nil { + return nil, nil + } + + out := types.Any{} + if err := proto.Unmarshal(bytes, &out); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal any") + } + + return &out, nil +} diff --git a/vendor/github.com/containerd/containerd/metadata/buckets.go b/vendor/github.com/containerd/containerd/metadata/buckets.go index 51d40a7f36017..fa947fb2504e0 100644 --- a/vendor/github.com/containerd/containerd/metadata/buckets.go +++ b/vendor/github.com/containerd/containerd/metadata/buckets.go @@ -14,13 +14,11 @@ limitations under the License. */ -package metadata - -import ( - digest "github.com/opencontainers/go-digest" - bolt "go.etcd.io/bbolt" -) - +// Package metadata stores all labels and object specific metadata by namespace. +// This package also contains the main garbage collection logic for cleaning up +// resources consistently and atomically. Resources used by backends will be +// tracked in the metadata store to be exposed to consumers of this package. +// // The layout where a "/" delineates a bucket is described in the following // section. Please try to follow this as closely as possible when adding // functionality. We can bolster this with helpers and more structure if that @@ -43,6 +41,84 @@ import ( // // key: object-specific key identifying the storage bucket for the objects // contents. +// +// Below is the current database schema. This should be updated each time +// the structure is changed in addition to adding a migration and incrementing +// the database version. Note that `╘══*...*` refers to maps with arbitrary +// keys. +// ├──version : - Latest version, see migrations +// └──v1 - Schema version bucket +// ╘══*namespace* +// ├──labels +// │  ╘══*key* : - Label value +// ├──image +// │  ╘══*image name* +// │   ├──createdat : - Created at +// │   ├──updatedat : - Updated at +// │   ├──target +// │   │  ├──digest : - Descriptor digest +// │   │  ├──mediatype : - Descriptor media type +// │   │  └──size : - Descriptor size +// │   └──labels +// │   ╘══*key* : - Label value +// ├──containers +// │  ╘══*container id* +// │   ├──createdat : - Created at +// │   ├──updatedat : - Updated at +// │   ├──spec : - Proto marshaled spec +// │   ├──image : - Image name +// │   ├──snapshotter : - Snapshotter name +// │   ├──snapshotKey : - Snapshot key +// │   ├──runtime +// │   │  ├──name : - Runtime name +// │   │  ├──extensions +// │   │  │  ╘══*name* : - Proto marshaled extension +// │   │  └──options : - Proto marshaled options +// │   └──labels +// │   ╘══*key* : - Label value +// ├──snapshots +// │  ╘══*snapshotter* +// │   ╘══*snapshot key* +// │    ├──name : - Snapshot name in backend +// │   ├──createdat : - Created at +// │   ├──updatedat : - Updated at +// │    ├──parent : - Parent snapshot name +// │   ├──children +// │   │  ╘══*snapshot key* : - Child snapshot reference +// │   └──labels +// │   ╘══*key* : - Label value +// ├──content +// │  ├──blob +// │  │ ╘══*blob digest* +// │  │ ├──createdat : - Created at +// │  │ ├──updatedat : - Updated at +// │  │   ├──size : - Blob size +// │  │ └──labels +// │  │ ╘══*key* : - Label value +// │  └──ingests +// │   ╘══*ingest reference* +// │    ├──ref : - Ingest reference in backend +// │   ├──expireat : - Time to expire ingest +// │   └──expected : - Expected commit digest +// └──leases +// ╘══*lease id* +//   ├──createdat : - Created at +// ├──labels +// │ ╘══*key* : - Label value +//   ├──snapshots +// │  ╘══*snapshotter* +// │   ╘══*snapshot key* : - Snapshot reference +//   ├──content +// │  ╘══*blob digest* : - Content blob reference +// └──ingests +//   ╘══*ingest reference* : - Content ingest reference +package metadata + +import ( + digest "github.com/opencontainers/go-digest" + bolt "go.etcd.io/bbolt" +) + var ( bucketKeyVersion = []byte(schemaVersion) bucketKeyDBVersion = []byte("version") // stores the version of the schema diff --git a/vendor/github.com/containerd/containerd/metadata/containers.go b/vendor/github.com/containerd/containerd/metadata/containers.go index 6e5622c36f577..26e86d8583b39 100644 --- a/vendor/github.com/containerd/containerd/metadata/containers.go +++ b/vendor/github.com/containerd/containerd/metadata/containers.go @@ -19,6 +19,7 @@ package metadata import ( "context" "strings" + "sync/atomic" "time" "github.com/containerd/containerd/containers" @@ -35,13 +36,13 @@ import ( ) type containerStore struct { - tx *bolt.Tx + db *DB } // NewContainerStore returns a Store backed by an underlying bolt DB -func NewContainerStore(tx *bolt.Tx) containers.Store { +func NewContainerStore(db *DB) containers.Store { return &containerStore{ - tx: tx, + db: db, } } @@ -51,14 +52,21 @@ func (s *containerStore) Get(ctx context.Context, id string) (containers.Contain return containers.Container{}, err } - bkt := getContainerBucket(s.tx, namespace, id) - if bkt == nil { - return containers.Container{}, errors.Wrapf(errdefs.ErrNotFound, "container %q in namespace %q", id, namespace) - } - container := containers.Container{ID: id} - if err := readContainer(&container, bkt); err != nil { - return containers.Container{}, errors.Wrapf(err, "failed to read container %q", id) + + if err := view(ctx, s.db, func(tx *bolt.Tx) error { + bkt := getContainerBucket(tx, namespace, id) + if bkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "container %q in namespace %q", id, namespace) + } + + if err := readContainer(&container, bkt); err != nil { + return errors.Wrapf(err, "failed to read container %q", id) + } + + return nil + }); err != nil { + return containers.Container{}, err } return container, nil @@ -72,30 +80,33 @@ func (s *containerStore) List(ctx context.Context, fs ...string) ([]containers.C filter, err := filters.ParseAll(fs...) if err != nil { - return nil, errors.Wrapf(errdefs.ErrInvalidArgument, err.Error()) - } - - bkt := getContainersBucket(s.tx, namespace) - if bkt == nil { - return nil, nil // empty store + return nil, errors.Wrap(errdefs.ErrInvalidArgument, err.Error()) } var m []containers.Container - if err := bkt.ForEach(func(k, v []byte) error { - cbkt := bkt.Bucket(k) - if cbkt == nil { - return nil - } - container := containers.Container{ID: string(k)} - if err := readContainer(&container, cbkt); err != nil { - return errors.Wrapf(err, "failed to read container %q", string(k)) + if err := view(ctx, s.db, func(tx *bolt.Tx) error { + bkt := getContainersBucket(tx, namespace) + if bkt == nil { + return nil // empty store } - if filter.Match(adaptContainer(container)) { - m = append(m, container) - } - return nil + return bkt.ForEach(func(k, v []byte) error { + cbkt := bkt.Bucket(k) + if cbkt == nil { + return nil + } + container := containers.Container{ID: string(k)} + + if err := readContainer(&container, cbkt); err != nil { + return errors.Wrapf(err, "failed to read container %q", string(k)) + } + + if filter.Match(adaptContainer(container)) { + m = append(m, container) + } + return nil + }) }); err != nil { return nil, err } @@ -113,23 +124,29 @@ func (s *containerStore) Create(ctx context.Context, container containers.Contai return containers.Container{}, errors.Wrap(err, "create container failed validation") } - bkt, err := createContainersBucket(s.tx, namespace) - if err != nil { - return containers.Container{}, err - } + if err := update(ctx, s.db, func(tx *bolt.Tx) error { + bkt, err := createContainersBucket(tx, namespace) + if err != nil { + return err + } - cbkt, err := bkt.CreateBucket([]byte(container.ID)) - if err != nil { - if err == bolt.ErrBucketExists { - err = errors.Wrapf(errdefs.ErrAlreadyExists, "container %q", container.ID) + cbkt, err := bkt.CreateBucket([]byte(container.ID)) + if err != nil { + if err == bolt.ErrBucketExists { + err = errors.Wrapf(errdefs.ErrAlreadyExists, "container %q", container.ID) + } + return err + } + + container.CreatedAt = time.Now().UTC() + container.UpdatedAt = container.CreatedAt + if err := writeContainer(cbkt, &container); err != nil { + return errors.Wrapf(err, "failed to write container %q", container.ID) } - return containers.Container{}, err - } - container.CreatedAt = time.Now().UTC() - container.UpdatedAt = container.CreatedAt - if err := writeContainer(cbkt, &container); err != nil { - return containers.Container{}, errors.Wrapf(err, "failed to write container %q", container.ID) + return nil + }); err != nil { + return containers.Container{}, err } return container, nil @@ -145,85 +162,91 @@ func (s *containerStore) Update(ctx context.Context, container containers.Contai return containers.Container{}, errors.Wrapf(errdefs.ErrInvalidArgument, "must specify a container id") } - bkt := getContainersBucket(s.tx, namespace) - if bkt == nil { - return containers.Container{}, errors.Wrapf(errdefs.ErrNotFound, "cannot update container %q in namespace %q", container.ID, namespace) - } - - cbkt := bkt.Bucket([]byte(container.ID)) - if cbkt == nil { - return containers.Container{}, errors.Wrapf(errdefs.ErrNotFound, "container %q", container.ID) - } - var updated containers.Container - if err := readContainer(&updated, cbkt); err != nil { - return updated, errors.Wrapf(err, "failed to read container %q", container.ID) - } - createdat := updated.CreatedAt - updated.ID = container.ID - - if len(fieldpaths) == 0 { - // only allow updates to these field on full replace. - fieldpaths = []string{"labels", "spec", "extensions", "image", "snapshotkey"} - - // Fields that are immutable must cause an error when no field paths - // are provided. This allows these fields to become mutable in the - // future. - if updated.Snapshotter != container.Snapshotter { - return containers.Container{}, errors.Wrapf(errdefs.ErrInvalidArgument, "container.Snapshotter field is immutable") + if err := update(ctx, s.db, func(tx *bolt.Tx) error { + bkt := getContainersBucket(tx, namespace) + if bkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "cannot update container %q in namespace %q", container.ID, namespace) } - if updated.Runtime.Name != container.Runtime.Name { - return containers.Container{}, errors.Wrapf(errdefs.ErrInvalidArgument, "container.Runtime.Name field is immutable") + cbkt := bkt.Bucket([]byte(container.ID)) + if cbkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "container %q", container.ID) } - } - // apply the field mask. If you update this code, you better follow the - // field mask rules in field_mask.proto. If you don't know what this - // is, do not update this code. - for _, path := range fieldpaths { - if strings.HasPrefix(path, "labels.") { - if updated.Labels == nil { - updated.Labels = map[string]string{} + if err := readContainer(&updated, cbkt); err != nil { + return errors.Wrapf(err, "failed to read container %q", container.ID) + } + createdat := updated.CreatedAt + updated.ID = container.ID + + if len(fieldpaths) == 0 { + // only allow updates to these field on full replace. + fieldpaths = []string{"labels", "spec", "extensions", "image", "snapshotkey"} + + // Fields that are immutable must cause an error when no field paths + // are provided. This allows these fields to become mutable in the + // future. + if updated.Snapshotter != container.Snapshotter { + return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Snapshotter field is immutable") + } + + if updated.Runtime.Name != container.Runtime.Name { + return errors.Wrapf(errdefs.ErrInvalidArgument, "container.Runtime.Name field is immutable") } - key := strings.TrimPrefix(path, "labels.") - updated.Labels[key] = container.Labels[key] - continue } - if strings.HasPrefix(path, "extensions.") { - if updated.Extensions == nil { - updated.Extensions = map[string]types.Any{} + // apply the field mask. If you update this code, you better follow the + // field mask rules in field_mask.proto. If you don't know what this + // is, do not update this code. + for _, path := range fieldpaths { + if strings.HasPrefix(path, "labels.") { + if updated.Labels == nil { + updated.Labels = map[string]string{} + } + key := strings.TrimPrefix(path, "labels.") + updated.Labels[key] = container.Labels[key] + continue + } + + if strings.HasPrefix(path, "extensions.") { + if updated.Extensions == nil { + updated.Extensions = map[string]types.Any{} + } + key := strings.TrimPrefix(path, "extensions.") + updated.Extensions[key] = container.Extensions[key] + continue + } + + switch path { + case "labels": + updated.Labels = container.Labels + case "spec": + updated.Spec = container.Spec + case "extensions": + updated.Extensions = container.Extensions + case "image": + updated.Image = container.Image + case "snapshotkey": + updated.SnapshotKey = container.SnapshotKey + default: + return errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on %q", path, container.ID) } - key := strings.TrimPrefix(path, "extensions.") - updated.Extensions[key] = container.Extensions[key] - continue } - switch path { - case "labels": - updated.Labels = container.Labels - case "spec": - updated.Spec = container.Spec - case "extensions": - updated.Extensions = container.Extensions - case "image": - updated.Image = container.Image - case "snapshotkey": - updated.SnapshotKey = container.SnapshotKey - default: - return containers.Container{}, errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on %q", path, container.ID) + if err := validateContainer(&updated); err != nil { + return errors.Wrap(err, "update failed validation") } - } - if err := validateContainer(&updated); err != nil { - return containers.Container{}, errors.Wrap(err, "update failed validation") - } + updated.CreatedAt = createdat + updated.UpdatedAt = time.Now().UTC() + if err := writeContainer(cbkt, &updated); err != nil { + return errors.Wrapf(err, "failed to write container %q", container.ID) + } - updated.CreatedAt = createdat - updated.UpdatedAt = time.Now().UTC() - if err := writeContainer(cbkt, &updated); err != nil { - return containers.Container{}, errors.Wrapf(err, "failed to write container %q", container.ID) + return nil + }); err != nil { + return containers.Container{}, err } return updated, nil @@ -235,15 +258,23 @@ func (s *containerStore) Delete(ctx context.Context, id string) error { return err } - bkt := getContainersBucket(s.tx, namespace) - if bkt == nil { - return errors.Wrapf(errdefs.ErrNotFound, "cannot delete container %q in namespace %q", id, namespace) - } + return update(ctx, s.db, func(tx *bolt.Tx) error { + bkt := getContainersBucket(tx, namespace) + if bkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "cannot delete container %q in namespace %q", id, namespace) + } - if err := bkt.DeleteBucket([]byte(id)); err == bolt.ErrBucketNotFound { - return errors.Wrapf(errdefs.ErrNotFound, "container %v", id) - } - return err + if err := bkt.DeleteBucket([]byte(id)); err != nil { + if err == bolt.ErrBucketNotFound { + err = errors.Wrapf(errdefs.ErrNotFound, "container %v", id) + } + return err + } + + atomic.AddUint32(&s.db.dirty, 1) + + return nil + }) } func validateContainer(container *containers.Container) error { @@ -259,7 +290,7 @@ func validateContainer(container *containers.Container) error { // image has no validation for k, v := range container.Labels { - if err := labels.Validate(k, v); err == nil { + if err := labels.Validate(k, v); err != nil { return errors.Wrapf(err, "containers.Labels") } } @@ -305,16 +336,11 @@ func readContainer(container *containers.Container, bkt *bolt.Bucket) error { container.Runtime.Name = string(n) } - obkt := rbkt.Get(bucketKeyOptions) - if obkt == nil { - return nil - } - - var any types.Any - if err := proto.Unmarshal(obkt, &any); err != nil { + any, err := boltutil.ReadAny(rbkt, bucketKeyOptions) + if err != nil { return err } - container.Runtime.Options = &any + container.Runtime.Options = any case string(bucketKeySpec): var any types.Any if err := proto.Unmarshal(v, &any); err != nil { @@ -326,22 +352,8 @@ func readContainer(container *containers.Container, bkt *bolt.Bucket) error { case string(bucketKeySnapshotter): container.Snapshotter = string(v) case string(bucketKeyExtensions): - ebkt := bkt.Bucket(bucketKeyExtensions) - if ebkt == nil { - return nil - } - - extensions := make(map[string]types.Any) - if err := ebkt.ForEach(func(k, v []byte) error { - var a types.Any - if err := proto.Unmarshal(v, &a); err != nil { - return err - } - - extensions[string(k)] = a - return nil - }); err != nil { - + extensions, err := boltutil.ReadExtensions(bkt) + if err != nil { return err } @@ -357,15 +369,8 @@ func writeContainer(bkt *bolt.Bucket, container *containers.Container) error { return err } - if container.Spec != nil { - spec, err := container.Spec.Marshal() - if err != nil { - return err - } - - if err := bkt.Put(bucketKeySpec, spec); err != nil { - return err - } + if err := boltutil.WriteAny(bkt, bucketKeySpec, container.Spec); err != nil { + return err } for _, v := range [][2][]byte{ @@ -393,33 +398,12 @@ func writeContainer(bkt *bolt.Bucket, container *containers.Container) error { return err } - if len(container.Extensions) > 0 { - ebkt, err := bkt.CreateBucketIfNotExists(bucketKeyExtensions) - if err != nil { - return err - } - - for name, ext := range container.Extensions { - p, err := proto.Marshal(&ext) - if err != nil { - return err - } - - if err := ebkt.Put([]byte(name), p); err != nil { - return err - } - } + if err := boltutil.WriteExtensions(bkt, container.Extensions); err != nil { + return err } - if container.Runtime.Options != nil { - data, err := proto.Marshal(container.Runtime.Options) - if err != nil { - return err - } - - if err := rbkt.Put(bucketKeyOptions, data); err != nil { - return err - } + if err := boltutil.WriteAny(rbkt, bucketKeyOptions, container.Runtime.Options); err != nil { + return err } return boltutil.WriteLabels(bkt, container.Labels) diff --git a/vendor/github.com/containerd/containerd/metadata/content.go b/vendor/github.com/containerd/containerd/metadata/content.go index f51b0aaddeb24..ee68ccfe1e4e2 100644 --- a/vendor/github.com/containerd/containerd/metadata/content.go +++ b/vendor/github.com/containerd/containerd/metadata/content.go @@ -21,6 +21,7 @@ import ( "encoding/binary" "strings" "sync" + "sync/atomic" "time" "github.com/containerd/containerd/content" @@ -38,16 +39,31 @@ import ( type contentStore struct { content.Store - db *DB - l sync.RWMutex + db *DB + shared bool + l sync.RWMutex } // newContentStore returns a namespaced content store using an existing // content store interface. -func newContentStore(db *DB, cs content.Store) *contentStore { +// policy defines the sharing behavior for content between namespaces. Both +// modes will result in shared storage in the backend for committed. Choose +// "shared" to prevent separate namespaces from having to pull the same content +// twice. Choose "isolated" if the content must not be shared between +// namespaces. +// +// If the policy is "shared", writes will try to resolve the "expected" digest +// against the backend, allowing imports of content from other namespaces. In +// "isolated" mode, the client must prove they have the content by providing +// the entire blob before the content can be added to another namespace. +// +// Since we have only two policies right now, it's simpler using bool to +// represent it internally. +func newContentStore(db *DB, shared bool, cs content.Store) *contentStore { return &contentStore{ - Store: cs, - db: db, + Store: cs, + db: db, + shared: shared, } } @@ -165,7 +181,7 @@ func (cs *contentStore) Walk(ctx context.Context, fn content.WalkFunc, fs ...str if err := readInfo(&info, bkt.Bucket(k)); err != nil { return err } - if filter.Match(adaptContentInfo(info)) { + if filter.Match(content.AdaptInfo(info)) { infos = append(infos, info) } return nil @@ -206,9 +222,8 @@ func (cs *contentStore) Delete(ctx context.Context, dgst digest.Digest) error { } // Mark content store as dirty for triggering garbage collection - cs.db.dirtyL.Lock() + atomic.AddUint32(&cs.db.dirty, 1) cs.db.dirtyCS = true - cs.db.dirtyL.Unlock() return nil }) @@ -383,13 +398,15 @@ func (cs *contentStore) Writer(ctx context.Context, opts ...content.WriterOpt) ( return nil } - if st, err := cs.Store.Info(ctx, wOpts.Desc.Digest); err == nil { - // Ensure the expected size is the same, it is likely - // an error if the size is mismatched but the caller - // must resolve this on commit - if wOpts.Desc.Size == 0 || wOpts.Desc.Size == st.Size { - shared = true - wOpts.Desc.Size = st.Size + if cs.shared { + if st, err := cs.Store.Info(ctx, wOpts.Desc.Digest); err == nil { + // Ensure the expected size is the same, it is likely + // an error if the size is mismatched but the caller + // must resolve this on commit + if wOpts.Desc.Size == 0 || wOpts.Desc.Size == st.Size { + shared = true + wOpts.Desc.Size = st.Size + } } } } @@ -534,13 +551,13 @@ func (nw *namespacedWriter) createAndCopy(ctx context.Context, desc ocispec.Desc if desc.Size > 0 { ra, err := nw.provider.ReaderAt(ctx, nw.desc) if err != nil { + w.Close() return err } defer ra.Close() if err := content.CopyReaderAt(w, ra, desc.Size); err != nil { - nw.w.Close() - nw.w = nil + w.Close() return err } } @@ -550,18 +567,14 @@ func (nw *namespacedWriter) createAndCopy(ctx context.Context, desc ocispec.Desc } func (nw *namespacedWriter) Commit(ctx context.Context, size int64, expected digest.Digest, opts ...content.Opt) error { + ctx = namespaces.WithNamespace(ctx, nw.namespace) + nw.l.RLock() defer nw.l.RUnlock() var innerErr error if err := update(ctx, nw.db, func(tx *bolt.Tx) error { - bkt := getIngestsBucket(tx, nw.namespace) - if bkt != nil { - if err := bkt.DeleteBucket([]byte(nw.ref)); err != nil && err != bolt.ErrBucketNotFound { - return err - } - } dgst, err := nw.commit(ctx, tx, size, expected, opts...) if err != nil { if !errdefs.IsAlreadyExists(err) { @@ -569,6 +582,12 @@ func (nw *namespacedWriter) Commit(ctx context.Context, size int64, expected dig } innerErr = err } + bkt := getIngestsBucket(tx, nw.namespace) + if bkt != nil { + if err := bkt.DeleteBucket([]byte(nw.ref)); err != nil && err != bolt.ErrBucketNotFound { + return err + } + } if err := removeIngestLease(ctx, tx, nw.ref); err != nil { return err } @@ -584,37 +603,45 @@ func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64, var base content.Info for _, opt := range opts { if err := opt(&base); err != nil { + if nw.w != nil { + nw.w.Close() + } return "", err } } if err := validateInfo(&base); err != nil { + if nw.w != nil { + nw.w.Close() + } return "", err } var actual digest.Digest if nw.w == nil { if size != 0 && size != nw.desc.Size { - return "", errors.Errorf("%q failed size validation: %v != %v", nw.ref, nw.desc.Size, size) + return "", errors.Wrapf(errdefs.ErrFailedPrecondition, "%q failed size validation: %v != %v", nw.ref, nw.desc.Size, size) } if expected != "" && expected != nw.desc.Digest { - return "", errors.Errorf("%q unexpected digest", nw.ref) + return "", errors.Wrapf(errdefs.ErrFailedPrecondition, "%q unexpected digest", nw.ref) } size = nw.desc.Size actual = nw.desc.Digest } else { status, err := nw.w.Status() if err != nil { + nw.w.Close() return "", err } if size != 0 && size != status.Offset { - return "", errors.Errorf("%q failed size validation: %v != %v", nw.ref, status.Offset, size) + nw.w.Close() + return "", errors.Wrapf(errdefs.ErrFailedPrecondition, "%q failed size validation: %v != %v", nw.ref, status.Offset, size) } size = status.Offset - actual = nw.w.Digest() if err := nw.w.Commit(ctx, size, expected); err != nil && !errdefs.IsAlreadyExists(err) { return "", err } + actual = nw.w.Digest() } bkt, err := createBlobBucket(tx, nw.namespace, actual) @@ -681,7 +708,7 @@ func (cs *contentStore) checkAccess(ctx context.Context, dgst digest.Digest) err func validateInfo(info *content.Info) error { for k, v := range info.Labels { - if err := labels.Validate(k, v); err == nil { + if err := labels.Validate(k, v); err != nil { return errors.Wrapf(err, "info.Labels") } } @@ -742,11 +769,7 @@ func writeExpireAt(expire time.Time, bkt *bolt.Bucket) error { if err != nil { return err } - if err := bkt.Put(bucketKeyExpireAt, expireAt); err != nil { - return err - } - - return nil + return bkt.Put(bucketKeyExpireAt, expireAt) } func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, err error) { @@ -754,7 +777,7 @@ func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, er t1 := time.Now() defer func() { if err == nil { - d = time.Now().Sub(t1) + d = time.Since(t1) } cs.l.Unlock() }() diff --git a/vendor/github.com/containerd/containerd/metadata/db.go b/vendor/github.com/containerd/containerd/metadata/db.go index 507d6d22d0110..40d045f05d3e5 100644 --- a/vendor/github.com/containerd/containerd/metadata/db.go +++ b/vendor/github.com/containerd/containerd/metadata/db.go @@ -21,6 +21,7 @@ import ( "encoding/binary" "strings" "sync" + "sync/atomic" "time" "github.com/containerd/containerd/content" @@ -46,6 +47,19 @@ const ( dbVersion = 3 ) +// DBOpt configures how we set up the DB +type DBOpt func(*dbOptions) + +// WithPolicyIsolated isolates contents between namespaces +func WithPolicyIsolated(o *dbOptions) { + o.shared = false +} + +// dbOptions configure db options. +type dbOptions struct { + shared bool +} + // DB represents a metadata database backed by a bolt // database. The database is fully namespaced and stores // image, container, namespace, snapshot, and content data @@ -62,29 +76,44 @@ type DB struct { // sweep phases without preventing read transactions. wlock sync.RWMutex - // dirty flags and lock keeps track of datastores which have had deletions - // since the last garbage collection. These datastores will will be garbage - // collected during the next garbage collection. - dirtyL sync.Mutex + // dirty flag indicates that references have been removed which require + // a garbage collection to ensure the database is clean. This tracks + // the number of dirty operations. This should be updated and read + // atomically if outside of wlock.Lock. + dirty uint32 + + // dirtySS and dirtyCS flags keeps track of datastores which have had + // deletions since the last garbage collection. These datastores will + // be garbage collected during the next garbage collection. These + // should only be updated inside of a write transaction or wlock.Lock. dirtySS map[string]struct{} dirtyCS bool // mutationCallbacks are called after each mutation with the flag // set indicating whether any dirty flags are set mutationCallbacks []func(bool) + + dbopts dbOptions } // NewDB creates a new metadata database using the provided // bolt database, content store, and snapshotters. -func NewDB(db *bolt.DB, cs content.Store, ss map[string]snapshots.Snapshotter) *DB { +func NewDB(db *bolt.DB, cs content.Store, ss map[string]snapshots.Snapshotter, opts ...DBOpt) *DB { m := &DB{ db: db, ss: make(map[string]*snapshotter, len(ss)), dirtySS: map[string]struct{}{}, + dbopts: dbOptions{ + shared: true, + }, + } + + for _, opt := range opts { + opt(&m.dbopts) } // Initialize data stores - m.cs = newContentStore(m, cs) + m.cs = newContentStore(m, m.dbopts.shared, cs) for name, sn := range ss { m.ss[name] = newSnapshotter(m, name, sn) } @@ -140,7 +169,7 @@ func (m *DB) Init(ctx context.Context) error { } } - // Previous version fo database found + // Previous version of database found if schema != "v0" { updates := migrations[i:] @@ -154,7 +183,7 @@ func (m *DB) Init(ctx context.Context) error { if err := m.migrate(tx); err != nil { return errors.Wrapf(err, "failed to migrate to %s.%d", m.schema, m.version) } - log.G(ctx).WithField("d", time.Now().Sub(t0)).Debugf("finished database migration to %s.%d", m.schema, m.version) + log.G(ctx).WithField("d", time.Since(t0)).Debugf("finished database migration to %s.%d", m.schema, m.version) } } @@ -215,12 +244,10 @@ func (m *DB) Update(fn func(*bolt.Tx) error) error { defer m.wlock.RUnlock() err := m.db.Update(fn) if err == nil { - m.dirtyL.Lock() - dirty := m.dirtyCS || len(m.dirtySS) > 0 + dirty := atomic.LoadUint32(&m.dirty) > 0 for _, fn := range m.mutationCallbacks { fn(dirty) } - m.dirtyL.Unlock() } return err @@ -232,9 +259,9 @@ func (m *DB) Update(fn func(*bolt.Tx) error) error { // The callback function is an argument for whether a deletion has occurred // since the last garbage collection. func (m *DB) RegisterMutationCallback(fn func(bool)) { - m.dirtyL.Lock() + m.wlock.Lock() m.mutationCallbacks = append(m.mutationCallbacks, fn) - m.dirtyL.Unlock() + m.wlock.Unlock() } // GCStats holds the duration for the different phases of the garbage collector @@ -260,8 +287,6 @@ func (m *DB) GarbageCollect(ctx context.Context) (gc.Stats, error) { return nil, err } - m.dirtyL.Lock() - if err := m.db.Update(func(tx *bolt.Tx) error { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -287,7 +312,6 @@ func (m *DB) GarbageCollect(ctx context.Context) (gc.Stats, error) { return nil }); err != nil { - m.dirtyL.Unlock() m.wlock.Unlock() return nil, err } @@ -295,6 +319,9 @@ func (m *DB) GarbageCollect(ctx context.Context) (gc.Stats, error) { var stats GCStats var wg sync.WaitGroup + // reset dirty, no need for atomic inside of wlock.Lock + m.dirty = 0 + if len(m.dirtySS) > 0 { var sl sync.Mutex stats.SnapshotD = map[string]time.Duration{} @@ -306,7 +333,7 @@ func (m *DB) GarbageCollect(ctx context.Context) (gc.Stats, error) { m.cleanupSnapshotter(snapshotterName) sl.Lock() - stats.SnapshotD[snapshotterName] = time.Now().Sub(st1) + stats.SnapshotD[snapshotterName] = time.Since(st1) sl.Unlock() wg.Done() @@ -321,15 +348,13 @@ func (m *DB) GarbageCollect(ctx context.Context) (gc.Stats, error) { go func() { ct1 := time.Now() m.cleanupContent() - stats.ContentD = time.Now().Sub(ct1) + stats.ContentD = time.Since(ct1) wg.Done() }() m.dirtyCS = false } - m.dirtyL.Unlock() - - stats.MetaD = time.Now().Sub(t1) + stats.MetaD = time.Since(t1) m.wlock.Unlock() wg.Wait() diff --git a/vendor/github.com/containerd/containerd/metadata/gc.go b/vendor/github.com/containerd/containerd/metadata/gc.go index a670ce1a3e097..afe16c9222d77 100644 --- a/vendor/github.com/containerd/containerd/metadata/gc.go +++ b/vendor/github.com/containerd/containerd/metadata/gc.go @@ -46,11 +46,17 @@ const ( ResourceIngest ) +const ( + resourceContentFlat = ResourceContent | 0x20 + resourceSnapshotFlat = ResourceSnapshot | 0x20 +) + var ( labelGCRoot = []byte("containerd.io/gc.root") labelGCSnapRef = []byte("containerd.io/gc.ref.snapshot.") labelGCContentRef = []byte("containerd.io/gc.ref.content") labelGCExpire = []byte("containerd.io/gc.expire") + labelGCFlat = []byte("containerd.io/gc.flat") ) func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { @@ -64,6 +70,18 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { // iterate through each namespace v1c := v1bkt.Cursor() + // cerr indicates the scan did not successfully send all + // the roots. The scan does not need to be cancelled but + // must return error at the end. + var cerr error + fn := func(n gc.Node) { + select { + case nc <- n: + case <-ctx.Done(): + cerr = ctx.Err() + } + } + for k, v := v1c.First(); k != nil; k, v = v1c.Next() { if v != nil { continue @@ -78,6 +96,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { return nil } libkt := lbkt.Bucket(k) + var flat bool if lblbkt := libkt.Bucket(bucketKeyObjectLabels); lblbkt != nil { if expV := lblbkt.Get(labelGCExpire); expV != nil { @@ -90,33 +109,39 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { return nil } } - } - select { - case nc <- gcnode(ResourceLease, ns, string(k)): - case <-ctx.Done(): - return ctx.Err() + if flatV := lblbkt.Get(labelGCFlat); flatV != nil { + flat = true + } } + fn(gcnode(ResourceLease, ns, string(k))) + // Emit content and snapshots as roots instead of implementing // in references. Since leases cannot be referenced there is // no need to allow the lookup to be recursive, handling here // therefore reduces the number of database seeks. + ctype := ResourceContent + if flat { + ctype = resourceContentFlat + } + cbkt := libkt.Bucket(bucketKeyObjectContent) if cbkt != nil { if err := cbkt.ForEach(func(k, v []byte) error { - select { - case nc <- gcnode(ResourceContent, ns, string(k)): - case <-ctx.Done(): - return ctx.Err() - } + fn(gcnode(ctype, ns, string(k))) return nil }); err != nil { return err } } + stype := ResourceSnapshot + if flat { + stype = resourceSnapshotFlat + } + sbkt := libkt.Bucket(bucketKeyObjectSnapshots) if sbkt != nil { if err := sbkt.ForEach(func(sk, sv []byte) error { @@ -126,11 +151,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { snbkt := sbkt.Bucket(sk) return snbkt.ForEach(func(k, v []byte) error { - select { - case nc <- gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)): - case <-ctx.Done(): - return ctx.Err() - } + fn(gcnode(stype, ns, fmt.Sprintf("%s/%s", sk, k))) return nil }) }); err != nil { @@ -141,11 +162,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { ibkt := libkt.Bucket(bucketKeyObjectIngests) if ibkt != nil { if err := ibkt.ForEach(func(k, v []byte) error { - select { - case nc <- gcnode(ResourceIngest, ns, string(k)): - case <-ctx.Done(): - return ctx.Err() - } + fn(gcnode(ResourceIngest, ns, string(k))) return nil }); err != nil { return err @@ -168,18 +185,9 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { target := ibkt.Bucket(k).Bucket(bucketKeyTarget) if target != nil { contentKey := string(target.Get(bucketKeyDigest)) - select { - case nc <- gcnode(ResourceContent, ns, contentKey): - case <-ctx.Done(): - return ctx.Err() - } + fn(gcnode(ResourceContent, ns, contentKey)) } - return sendSnapshotRefs(ns, ibkt.Bucket(k), func(n gc.Node) { - select { - case nc <- n: - case <-ctx.Done(): - } - }) + return sendLabelRefs(ns, ibkt.Bucket(k), fn) }); err != nil { return err } @@ -200,11 +208,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { if ea == nil || expThreshold.After(*ea) { return nil } - select { - case nc <- gcnode(ResourceIngest, ns, string(k)): - case <-ctx.Done(): - return ctx.Err() - } + fn(gcnode(ResourceIngest, ns, string(k))) return nil }); err != nil { return err @@ -216,7 +220,12 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { if v != nil { return nil } - return sendRootRef(ctx, nc, gcnode(ResourceContent, ns, string(k)), cbkt.Bucket(k)) + + if isRootRef(cbkt.Bucket(k)) { + fn(gcnode(ResourceContent, ns, string(k))) + } + + return nil }); err != nil { return err } @@ -229,23 +238,15 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { if v != nil { return nil } - snapshotter := string(cbkt.Bucket(k).Get(bucketKeySnapshotter)) + + cibkt := cbkt.Bucket(k) + snapshotter := string(cibkt.Get(bucketKeySnapshotter)) if snapshotter != "" { - ss := string(cbkt.Bucket(k).Get(bucketKeySnapshotKey)) - select { - case nc <- gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", snapshotter, ss)): - case <-ctx.Done(): - return ctx.Err() - } + ss := string(cibkt.Get(bucketKeySnapshotKey)) + fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", snapshotter, ss))) } - // TODO: Send additional snapshot refs through labels - return sendSnapshotRefs(ns, cbkt.Bucket(k), func(n gc.Node) { - select { - case nc <- n: - case <-ctx.Done(): - } - }) + return sendLabelRefs(ns, cibkt, fn) }); err != nil { return err } @@ -263,30 +264,30 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { if v != nil { return nil } - - return sendRootRef(ctx, nc, gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k)), snbkt.Bucket(k)) + if isRootRef(snbkt.Bucket(k)) { + fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", sk, k))) + } + return nil }) }); err != nil { return err } } } - return nil + return cerr } func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node)) error { - if node.Type == ResourceContent { + switch node.Type { + case ResourceContent: bkt := getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectContent, bucketKeyObjectBlob, []byte(node.Key)) if bkt == nil { // Node may be created from dead edge return nil } - if err := sendSnapshotRefs(node.Namespace, bkt, fn); err != nil { - return err - } - return sendContentRefs(node.Namespace, bkt, fn) - } else if node.Type == ResourceSnapshot { + return sendLabelRefs(node.Namespace, bkt, fn) + case ResourceSnapshot, resourceSnapshotFlat: parts := strings.SplitN(node.Key, "/", 2) if len(parts) != 2 { return errors.Errorf("invalid snapshot gc key %s", node.Key) @@ -296,20 +297,21 @@ func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node) bkt := getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectSnapshots, []byte(ss), []byte(name)) if bkt == nil { - getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectSnapshots).ForEach(func(k, v []byte) error { - return nil - }) - // Node may be created from dead edge return nil } if pv := bkt.Get(bucketKeyParent); len(pv) > 0 { - fn(gcnode(ResourceSnapshot, node.Namespace, fmt.Sprintf("%s/%s", ss, pv))) + fn(gcnode(node.Type, node.Namespace, fmt.Sprintf("%s/%s", ss, pv))) } - return sendSnapshotRefs(node.Namespace, bkt, fn) - } else if node.Type == ResourceIngest { + // Do not send labeled references for flat snapshot refs + if node.Type == resourceSnapshotFlat { + return nil + } + + return sendLabelRefs(node.Namespace, bkt, fn) + case ResourceIngest: // Send expected value bkt := getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectContent, bucketKeyObjectIngests, []byte(node.Key)) if bkt == nil { @@ -460,25 +462,8 @@ func remove(ctx context.Context, tx *bolt.Tx, node gc.Node) error { return nil } -// sendSnapshotRefs sends all snapshot references referred to by the labels in the bkt -func sendSnapshotRefs(ns string, bkt *bolt.Bucket, fn func(gc.Node)) error { - lbkt := bkt.Bucket(bucketKeyObjectLabels) - if lbkt != nil { - lc := lbkt.Cursor() - - for k, v := lc.Seek(labelGCSnapRef); k != nil && strings.HasPrefix(string(k), string(labelGCSnapRef)); k, v = lc.Next() { - snapshotter := k[len(labelGCSnapRef):] - if i := bytes.IndexByte(snapshotter, '/'); i >= 0 { - snapshotter = snapshotter[:i] - } - fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", snapshotter, v))) - } - } - return nil -} - -// sendContentRefs sends all content references referred to by the labels in the bkt -func sendContentRefs(ns string, bkt *bolt.Bucket, fn func(gc.Node)) error { +// sendLabelRefs sends all snapshot and content references referred to by the labels in the bkt +func sendLabelRefs(ns string, bkt *bolt.Bucket, fn func(gc.Node)) error { lbkt := bkt.Bucket(bucketKeyObjectLabels) if lbkt != nil { lc := lbkt.Cursor() @@ -494,6 +479,15 @@ func sendContentRefs(ns string, bkt *bolt.Bucket, fn func(gc.Node)) error { fn(gcnode(ResourceContent, ns, string(v))) } + + for k, v := lc.Seek(labelGCSnapRef); k != nil && strings.HasPrefix(string(k), string(labelGCSnapRef)); k, v = lc.Next() { + snapshotter := k[len(labelGCSnapRef):] + if i := bytes.IndexByte(snapshotter, '/'); i >= 0 { + snapshotter = snapshotter[:i] + } + fn(gcnode(ResourceSnapshot, ns, fmt.Sprintf("%s/%s", snapshotter, v))) + } + } return nil } @@ -510,17 +504,6 @@ func isRootRef(bkt *bolt.Bucket) bool { return false } -func sendRootRef(ctx context.Context, nc chan<- gc.Node, n gc.Node, bkt *bolt.Bucket) error { - if isRootRef(bkt) { - select { - case nc <- n: - case <-ctx.Done(): - return ctx.Err() - } - } - return nil -} - func gcnode(t gc.ResourceType, ns, key string) gc.Node { return gc.Node{ Type: t, diff --git a/vendor/github.com/containerd/containerd/metadata/images.go b/vendor/github.com/containerd/containerd/metadata/images.go index 6ae9dc85541c9..cace4e1801279 100644 --- a/vendor/github.com/containerd/containerd/metadata/images.go +++ b/vendor/github.com/containerd/containerd/metadata/images.go @@ -21,6 +21,7 @@ import ( "encoding/binary" "fmt" "strings" + "sync/atomic" "time" "github.com/containerd/containerd/errdefs" @@ -84,7 +85,7 @@ func (s *imageStore) List(ctx context.Context, fs ...string) ([]images.Image, er filter, err := filters.ParseAll(fs...) if err != nil { - return nil, errors.Wrapf(errdefs.ErrInvalidArgument, err.Error()) + return nil, errors.Wrap(errdefs.ErrInvalidArgument, err.Error()) } var m []images.Image @@ -192,6 +193,14 @@ func (s *imageStore) Update(ctx context.Context, image images.Image, fieldpaths key := strings.TrimPrefix(path, "labels.") updated.Labels[key] = image.Labels[key] continue + } else if strings.HasPrefix(path, "annotations.") { + if updated.Target.Annotations == nil { + updated.Target.Annotations = map[string]string{} + } + + key := strings.TrimPrefix(path, "annotations.") + updated.Target.Annotations[key] = image.Target.Annotations[key] + continue } switch path { @@ -204,6 +213,8 @@ func (s *imageStore) Update(ctx context.Context, image images.Image, fieldpaths // make sense to modify the size or digest without touching the // mediatype, as well, for example. updated.Target = image.Target + case "annotations": + updated.Target.Annotations = image.Target.Annotations default: return errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on image %q", path, image.Name) } @@ -239,19 +250,16 @@ func (s *imageStore) Delete(ctx context.Context, name string, opts ...images.Del return errors.Wrapf(errdefs.ErrNotFound, "image %q", name) } - err = bkt.DeleteBucket([]byte(name)) - if err == bolt.ErrBucketNotFound { - return errors.Wrapf(errdefs.ErrNotFound, "image %q", name) + if err = bkt.DeleteBucket([]byte(name)); err != nil { + if err == bolt.ErrBucketNotFound { + err = errors.Wrapf(errdefs.ErrNotFound, "image %q", name) + } + return err } - // A reference to a piece of content has been removed, - // mark content store as dirty for triggering garbage - // collection - s.db.dirtyL.Lock() - s.db.dirtyCS = true - s.db.dirtyL.Unlock() + atomic.AddUint32(&s.db.dirty, 1) - return err + return nil }) } @@ -298,6 +306,11 @@ func readImage(image *images.Image, bkt *bolt.Bucket) error { } image.Labels = labels + image.Target.Annotations, err = boltutil.ReadAnnotations(bkt) + if err != nil { + return err + } + tbkt := bkt.Bucket(bucketKeyTarget) if tbkt == nil { return errors.New("unable to read target bucket") @@ -331,6 +344,10 @@ func writeImage(bkt *bolt.Bucket, image *images.Image) error { return errors.Wrapf(err, "writing labels for image %v", image.Name) } + if err := boltutil.WriteAnnotations(bkt, image.Target.Annotations); err != nil { + return errors.Wrapf(err, "writing Annotations for image %v", image.Name) + } + // write the target bucket tbkt, err := bkt.CreateBucketIfNotExists(bucketKeyTarget) if err != nil { diff --git a/vendor/github.com/containerd/containerd/metadata/leases.go b/vendor/github.com/containerd/containerd/metadata/leases.go index 635c3b3c37e4e..60da06b0f00e0 100644 --- a/vendor/github.com/containerd/containerd/metadata/leases.go +++ b/vendor/github.com/containerd/containerd/metadata/leases.go @@ -18,6 +18,9 @@ package metadata import ( "context" + "fmt" + "strings" + "sync/atomic" "time" "github.com/containerd/containerd/errdefs" @@ -30,17 +33,17 @@ import ( bolt "go.etcd.io/bbolt" ) -// LeaseManager manages the create/delete lifecyle of leases +// LeaseManager manages the create/delete lifecycle of leases // and also returns existing leases type LeaseManager struct { - tx *bolt.Tx + db *DB } // NewLeaseManager creates a new lease manager for managing leases using // the provided database transaction. -func NewLeaseManager(tx *bolt.Tx) *LeaseManager { +func NewLeaseManager(db *DB) *LeaseManager { return &LeaseManager{ - tx: tx, + db: db, } } @@ -61,56 +64,66 @@ func (lm *LeaseManager) Create(ctx context.Context, opts ...leases.Opt) (leases. return leases.Lease{}, err } - topbkt, err := createBucketIfNotExists(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) - if err != nil { - return leases.Lease{}, err - } + if err := update(ctx, lm.db, func(tx *bolt.Tx) error { + topbkt, err := createBucketIfNotExists(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) + if err != nil { + return err + } - txbkt, err := topbkt.CreateBucket([]byte(l.ID)) - if err != nil { - if err == bolt.ErrBucketExists { - err = errdefs.ErrAlreadyExists + txbkt, err := topbkt.CreateBucket([]byte(l.ID)) + if err != nil { + if err == bolt.ErrBucketExists { + err = errdefs.ErrAlreadyExists + } + return errors.Wrapf(err, "lease %q", l.ID) } - return leases.Lease{}, errors.Wrapf(err, "lease %q", l.ID) - } - t := time.Now().UTC() - createdAt, err := t.MarshalBinary() - if err != nil { - return leases.Lease{}, err - } - if err := txbkt.Put(bucketKeyCreatedAt, createdAt); err != nil { - return leases.Lease{}, err - } + t := time.Now().UTC() + createdAt, err := t.MarshalBinary() + if err != nil { + return err + } + if err := txbkt.Put(bucketKeyCreatedAt, createdAt); err != nil { + return err + } - if l.Labels != nil { - if err := boltutil.WriteLabels(txbkt, l.Labels); err != nil { - return leases.Lease{}, err + if l.Labels != nil { + if err := boltutil.WriteLabels(txbkt, l.Labels); err != nil { + return err + } } - } - l.CreatedAt = t + l.CreatedAt = t + return nil + }); err != nil { + return leases.Lease{}, err + } return l, nil } -// Delete delets the lease with the provided lease ID +// Delete deletes the lease with the provided lease ID func (lm *LeaseManager) Delete(ctx context.Context, lease leases.Lease, _ ...leases.DeleteOpt) error { namespace, err := namespaces.NamespaceRequired(ctx) if err != nil { return err } - topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) - if topbkt == nil { - return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) - } - if err := topbkt.DeleteBucket([]byte(lease.ID)); err != nil { - if err == bolt.ErrBucketNotFound { - err = errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) + return update(ctx, lm.db, func(tx *bolt.Tx) error { + topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) + if topbkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) } - return err - } - return nil + if err := topbkt.DeleteBucket([]byte(lease.ID)); err != nil { + if err == bolt.ErrBucketNotFound { + err = errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) + } + return err + } + + atomic.AddUint32(&lm.db.dirty, 1) + + return nil + }) } // List lists all active leases @@ -122,49 +135,189 @@ func (lm *LeaseManager) List(ctx context.Context, fs ...string) ([]leases.Lease, filter, err := filters.ParseAll(fs...) if err != nil { - return nil, errors.Wrapf(errdefs.ErrInvalidArgument, err.Error()) + return nil, errors.Wrap(errdefs.ErrInvalidArgument, err.Error()) } var ll []leases.Lease - topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) - if topbkt == nil { - return ll, nil - } + if err := view(ctx, lm.db, func(tx *bolt.Tx) error { + topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) + if topbkt == nil { + return nil + } + + return topbkt.ForEach(func(k, v []byte) error { + if v != nil { + return nil + } + txbkt := topbkt.Bucket(k) + + l := leases.Lease{ + ID: string(k), + } + + if v := txbkt.Get(bucketKeyCreatedAt); v != nil { + t := &l.CreatedAt + if err := t.UnmarshalBinary(v); err != nil { + return err + } + } + + labels, err := boltutil.ReadLabels(txbkt) + if err != nil { + return err + } + l.Labels = labels + + if filter.Match(adaptLease(l)) { + ll = append(ll, l) + } - if err := topbkt.ForEach(func(k, v []byte) error { - if v != nil { return nil + }) + }); err != nil { + return nil, err + } + + return ll, nil +} + +// AddResource references the resource by the provided lease. +func (lm *LeaseManager) AddResource(ctx context.Context, lease leases.Lease, r leases.Resource) error { + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return err + } + + return update(ctx, lm.db, func(tx *bolt.Tx) error { + topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lease.ID)) + if topbkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) } - txbkt := topbkt.Bucket(k) - l := leases.Lease{ - ID: string(k), + keys, ref, err := parseLeaseResource(r) + if err != nil { + return err } - if v := txbkt.Get(bucketKeyCreatedAt); v != nil { - t := &l.CreatedAt - if err := t.UnmarshalBinary(v); err != nil { + bkt := topbkt + for _, key := range keys { + bkt, err = bkt.CreateBucketIfNotExists([]byte(key)) + if err != nil { return err } } + return bkt.Put([]byte(ref), nil) + }) +} - labels, err := boltutil.ReadLabels(txbkt) +// DeleteResource dereferences the resource by the provided lease. +func (lm *LeaseManager) DeleteResource(ctx context.Context, lease leases.Lease, r leases.Resource) error { + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return err + } + + return update(ctx, lm.db, func(tx *bolt.Tx) error { + topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lease.ID)) + if topbkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) + } + + keys, ref, err := parseLeaseResource(r) if err != nil { return err } - l.Labels = labels - if filter.Match(adaptLease(l)) { - ll = append(ll, l) + bkt := topbkt + for _, key := range keys { + if bkt == nil { + break + } + bkt = bkt.Bucket([]byte(key)) + } + + if bkt != nil { + if err := bkt.Delete([]byte(ref)); err != nil { + return err + } } + atomic.AddUint32(&lm.db.dirty, 1) + return nil - }); err != nil { + }) +} + +// ListResources lists all the resources referenced by the lease. +func (lm *LeaseManager) ListResources(ctx context.Context, lease leases.Lease) ([]leases.Resource, error) { + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { return nil, err } - return ll, nil + var rs []leases.Resource + + if err := view(ctx, lm.db, func(tx *bolt.Tx) error { + + topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lease.ID)) + if topbkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID) + } + + // content resources + if cbkt := topbkt.Bucket(bucketKeyObjectContent); cbkt != nil { + if err := cbkt.ForEach(func(k, _ []byte) error { + rs = append(rs, leases.Resource{ + ID: string(k), + Type: string(bucketKeyObjectContent), + }) + + return nil + }); err != nil { + return err + } + } + + // ingest resources + if lbkt := topbkt.Bucket(bucketKeyObjectIngests); lbkt != nil { + if err := lbkt.ForEach(func(k, _ []byte) error { + rs = append(rs, leases.Resource{ + ID: string(k), + Type: string(bucketKeyObjectIngests), + }) + + return nil + }); err != nil { + return err + } + } + + // snapshot resources + if sbkt := topbkt.Bucket(bucketKeyObjectSnapshots); sbkt != nil { + if err := sbkt.ForEach(func(sk, sv []byte) error { + if sv != nil { + return nil + } + + snbkt := sbkt.Bucket(sk) + return snbkt.ForEach(func(k, _ []byte) error { + rs = append(rs, leases.Resource{ + ID: string(k), + Type: fmt.Sprintf("%s/%s", bucketKeyObjectSnapshots, sk), + }) + return nil + }) + }); err != nil { + return err + } + } + + return nil + }); err != nil { + return nil, err + } + return rs, nil } func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error { @@ -307,3 +460,36 @@ func removeIngestLease(ctx context.Context, tx *bolt.Tx, ref string) error { return bkt.Delete([]byte(ref)) } + +func parseLeaseResource(r leases.Resource) ([]string, string, error) { + var ( + ref = r.ID + typ = r.Type + keys = strings.Split(typ, "/") + ) + + switch k := keys[0]; k { + case string(bucketKeyObjectContent), + string(bucketKeyObjectIngests): + + if len(keys) != 1 { + return nil, "", errors.Wrapf(errdefs.ErrInvalidArgument, "invalid resource type %s", typ) + } + + if k == string(bucketKeyObjectContent) { + dgst, err := digest.Parse(ref) + if err != nil { + return nil, "", errors.Wrapf(errdefs.ErrInvalidArgument, "invalid content resource id %s: %v", ref, err) + } + ref = dgst.String() + } + case string(bucketKeyObjectSnapshots): + if len(keys) != 2 { + return nil, "", errors.Wrapf(errdefs.ErrInvalidArgument, "invalid snapshot resource type %s", typ) + } + default: + return nil, "", errors.Wrapf(errdefs.ErrNotImplemented, "resource type %s not supported yet", typ) + } + + return keys, ref, nil +} diff --git a/vendor/github.com/containerd/containerd/metadata/namespaces.go b/vendor/github.com/containerd/containerd/metadata/namespaces.go index 74951eb5c5b62..165c09fcac164 100644 --- a/vendor/github.com/containerd/containerd/metadata/namespaces.go +++ b/vendor/github.com/containerd/containerd/metadata/namespaces.go @@ -18,8 +18,10 @@ package metadata import ( "context" + "strings" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/identifiers" l "github.com/containerd/containerd/labels" "github.com/containerd/containerd/namespaces" "github.com/pkg/errors" @@ -41,7 +43,7 @@ func (s *namespaceStore) Create(ctx context.Context, namespace string, labels ma return err } - if err := namespaces.Validate(namespace); err != nil { + if err := identifiers.Validate(namespace); err != nil { return err } @@ -129,12 +131,27 @@ func (s *namespaceStore) List(ctx context.Context) ([]string, error) { return namespaces, nil } -func (s *namespaceStore) Delete(ctx context.Context, namespace string) error { +func (s *namespaceStore) Delete(ctx context.Context, namespace string, opts ...namespaces.DeleteOpts) error { + i := &namespaces.DeleteInfo{ + Name: namespace, + } + for _, o := range opts { + if err := o(ctx, i); err != nil { + return err + } + } bkt := getBucket(s.tx, bucketKeyVersion) - if empty, err := s.namespaceEmpty(ctx, namespace); err != nil { + types, err := s.listNs(namespace) + if err != nil { return err - } else if !empty { - return errors.Wrapf(errdefs.ErrFailedPrecondition, "namespace %q must be empty", namespace) + } + + if len(types) > 0 { + return errors.Wrapf( + errdefs.ErrFailedPrecondition, + "namespace %q must be empty, but it still has %s", + namespace, strings.Join(types, ", "), + ) } if err := bkt.DeleteBucket([]byte(namespace)); err != nil { @@ -148,32 +165,35 @@ func (s *namespaceStore) Delete(ctx context.Context, namespace string) error { return nil } -func (s *namespaceStore) namespaceEmpty(ctx context.Context, namespace string) (bool, error) { - // Get all data buckets - buckets := []*bolt.Bucket{ - getImagesBucket(s.tx, namespace), - getBlobsBucket(s.tx, namespace), - getContainersBucket(s.tx, namespace), +// listNs returns the types of the remaining objects inside the given namespace. +// It doesn't return exact objects due to performance concerns. +func (s *namespaceStore) listNs(namespace string) ([]string, error) { + var out []string + + if !isBucketEmpty(getImagesBucket(s.tx, namespace)) { + out = append(out, "images") + } + if !isBucketEmpty(getBlobsBucket(s.tx, namespace)) { + out = append(out, "blobs") } + if !isBucketEmpty(getContainersBucket(s.tx, namespace)) { + out = append(out, "containers") + } + if snbkt := getSnapshottersBucket(s.tx, namespace); snbkt != nil { if err := snbkt.ForEach(func(k, v []byte) error { if v == nil { - buckets = append(buckets, snbkt.Bucket(k)) + if !isBucketEmpty(snbkt.Bucket(k)) { + out = append(out, "snapshot-"+string(k)) + } } return nil }); err != nil { - return false, err - } - } - - // Ensure data buckets are empty - for _, bkt := range buckets { - if !isBucketEmpty(bkt) { - return false, nil + return nil, err } } - return true, nil + return out, nil } func isBucketEmpty(bkt *bolt.Bucket) bool { diff --git a/vendor/github.com/containerd/containerd/metadata/snapshot.go b/vendor/github.com/containerd/containerd/metadata/snapshot.go index 75b80b0bc59c3..389aeda45eca6 100644 --- a/vendor/github.com/containerd/containerd/metadata/snapshot.go +++ b/vendor/github.com/containerd/containerd/metadata/snapshot.go @@ -21,9 +21,11 @@ import ( "fmt" "strings" "sync" + "sync/atomic" "time" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/filters" "github.com/containerd/containerd/labels" "github.com/containerd/containerd/log" "github.com/containerd/containerd/metadata/boltutil" @@ -34,6 +36,11 @@ import ( bolt "go.etcd.io/bbolt" ) +const ( + inheritedLabelsPrefix = "containerd.io/snapshot/" + labelSnapshotRef = "containerd.io/snapshot.ref" +) + type snapshotter struct { snapshots.Snapshotter name string @@ -153,6 +160,7 @@ func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpath local = snapshots.Info{ Name: info.Name, } + updated bool ) if err := update(ctx, s.db, func(tx *bolt.Tx) error { bkt := getSnapshotterBucket(tx, ns, s.name) @@ -209,13 +217,24 @@ func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpath bkey = string(sbkt.Get(bucketKeyName)) local.Parent = string(sbkt.Get(bucketKeyParent)) + inner := snapshots.Info{ + Name: bkey, + Labels: snapshots.FilterInheritedLabels(local.Labels), + } + + // NOTE: Perform this inside the transaction to reduce the + // chances of out of sync data. The backend snapshotters + // should perform the Update as fast as possible. + if info, err = s.Snapshotter.Update(ctx, inner, fieldpaths...); err != nil { + return err + } + updated = true + return nil }); err != nil { - return snapshots.Info{}, err - } - - info, err = s.Snapshotter.Stat(ctx, bkey) - if err != nil { + if updated { + log.G(ctx).WithField("snapshotter", s.name).WithField("key", local.Name).WithError(err).Error("transaction failed after updating snapshot backend") + } return snapshots.Info{}, err } @@ -232,7 +251,7 @@ func overlayInfo(info, overlay snapshots.Info) snapshots.Info { info.Labels = overlay.Labels } else { for k, v := range overlay.Labels { - overlay.Labels[k] = v + info.Labels[k] = v } } return info @@ -282,31 +301,158 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re return nil, err } - var m []mount.Mount + var ( + target = base.Labels[labelSnapshotRef] + bparent string + bkey string + bopts = []snapshots.Opt{ + snapshots.WithLabels(snapshots.FilterInheritedLabels(base.Labels)), + } + ) + if err := update(ctx, s.db, func(tx *bolt.Tx) error { bkt, err := createSnapshotterBucket(tx, ns, s.name) if err != nil { return err } - bbkt, err := bkt.CreateBucket([]byte(key)) - if err != nil { - if err == bolt.ErrBucketExists { - err = errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %q", key) + // Check if target exists, if so, return already exists + if target != "" { + if tbkt := bkt.Bucket([]byte(target)); tbkt != nil { + return errors.Wrapf(errdefs.ErrAlreadyExists, "target snapshot %q", target) } + } + + if bbkt := bkt.Bucket([]byte(key)); bbkt != nil { + return errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %q", key) + } + + if parent != "" { + pbkt := bkt.Bucket([]byte(parent)) + if pbkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "parent snapshot %v does not exist", parent) + } + bparent = string(pbkt.Get(bucketKeyName)) + } + + sid, err := bkt.NextSequence() + if err != nil { return err } + bkey = createKey(sid, ns, key) + + return err + }); err != nil { + return nil, err + } + + var ( + m []mount.Mount + created string + rerr error + ) + if readonly { + m, err = s.Snapshotter.View(ctx, bkey, bparent, bopts...) + } else { + m, err = s.Snapshotter.Prepare(ctx, bkey, bparent, bopts...) + } + + // An already exists error should indicate the backend found a snapshot + // matching a provided target reference. + if errdefs.IsAlreadyExists(err) { + if target != "" { + var tinfo *snapshots.Info + filter := fmt.Sprintf(`labels."containerd.io/snapshot.ref"==%s,parent==%q`, target, bparent) + if err := s.Snapshotter.Walk(ctx, func(ctx context.Context, i snapshots.Info) error { + if tinfo == nil && i.Kind == snapshots.KindCommitted { + if i.Labels["containerd.io/snapshot.ref"] != target { + // Walk did not respect filter + return nil + } + if i.Parent != bparent { + // Walk did not respect filter + return nil + } + tinfo = &i + } + return nil + + }, filter); err != nil { + return nil, errors.Wrap(err, "failed walking backend snapshots") + } + + if tinfo == nil { + return nil, errors.Wrapf(errdefs.ErrNotFound, "target snapshot %q in backend", target) + } + + key = target + bkey = tinfo.Name + bparent = tinfo.Parent + base.Created = tinfo.Created + base.Updated = tinfo.Updated + if base.Labels == nil { + base.Labels = tinfo.Labels + } else { + for k, v := range tinfo.Labels { + if _, ok := base.Labels[k]; !ok { + base.Labels[k] = v + } + } + } + + // Propagate this error after the final update + rerr = errors.Wrapf(errdefs.ErrAlreadyExists, "target snapshot %q from snapshotter", target) + } else { + // This condition is unexpected as the key provided is expected + // to be new and unique, return as unknown response from backend + // to avoid confusing callers handling already exists. + return nil, errors.Wrapf(errdefs.ErrUnknown, "unexpected error from snapshotter: %v", err) + } + } else if err != nil { + return nil, err + } else { + ts := time.Now().UTC() + base.Created = ts + base.Updated = ts + created = bkey + } + + if txerr := update(ctx, s.db, func(tx *bolt.Tx) error { + bkt := getSnapshotterBucket(tx, ns, s.name) + if bkt == nil { + return errors.Wrapf(errdefs.ErrNotFound, "can not find snapshotter %q", s.name) + } + if err := addSnapshotLease(ctx, tx, s.name, key); err != nil { return err } - var bparent string + bbkt, err := bkt.CreateBucket([]byte(key)) + if err != nil { + if err != bolt.ErrBucketExists { + return err + } + if rerr == nil { + rerr = errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %q", key) + } + return nil + } + if parent != "" { pbkt := bkt.Bucket([]byte(parent)) if pbkt == nil { return errors.Wrapf(errdefs.ErrNotFound, "parent snapshot %v does not exist", parent) } - bparent = string(pbkt.Get(bucketKeyName)) + + // Ensure the backend's parent matches the metadata store's parent + // If it is mismatched, then a target was provided for a snapshotter + // which has a different parent then requested. + // NOTE: The backend snapshotter is responsible for enforcing the + // uniqueness of the reference relationships, the metadata store + // can only error out to prevent inconsistent data. + if bparent != string(pbkt.Get(bucketKeyName)) { + return errors.Wrapf(errdefs.ErrInvalidArgument, "mismatched parent %s from target %s", parent, target) + } cbkt, err := pbkt.CreateBucketIfNotExists(bucketKeyChildren) if err != nil { @@ -321,34 +467,28 @@ func (s *snapshotter) createSnapshot(ctx context.Context, key, parent string, re } } - sid, err := bkt.NextSequence() - if err != nil { - return err - } - bkey := createKey(sid, ns, key) - if err := bbkt.Put(bucketKeyName, []byte(bkey)); err != nil { - return err - } - - ts := time.Now().UTC() - if err := boltutil.WriteTimestamps(bbkt, ts, ts); err != nil { + if err := boltutil.WriteTimestamps(bbkt, base.Created, base.Updated); err != nil { return err } if err := boltutil.WriteLabels(bbkt, base.Labels); err != nil { return err } - // TODO: Consider doing this outside of transaction to lessen - // metadata lock time - if readonly { - m, err = s.Snapshotter.View(ctx, bkey, bparent) - } else { - m, err = s.Snapshotter.Prepare(ctx, bkey, bparent) + return bbkt.Put(bucketKeyName, []byte(bkey)) + }); txerr != nil { + rerr = txerr + } + + if rerr != nil { + // If the created reference is not stored, attempt clean up + if created != "" { + if err := s.Snapshotter.Remove(ctx, created); err != nil { + log.G(ctx).WithField("snapshotter", s.name).WithField("key", created).WithError(err).Error("failed to cleanup unreferenced snapshot") + } } - return err - }); err != nil { - return nil, err + return nil, rerr } + return m, nil } @@ -372,7 +512,8 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap return err } - return update(ctx, s.db, func(tx *bolt.Tx) error { + var bname string + if err := update(ctx, s.db, func(tx *bolt.Tx) error { bkt := getSnapshotterBucket(tx, ns, s.name) if bkt == nil { return errors.Wrapf(errdefs.ErrNotFound, @@ -445,10 +586,40 @@ func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap return err } - // TODO: Consider doing this outside of transaction to lessen - // metadata lock time - return s.Snapshotter.Commit(ctx, nameKey, bkey) - }) + inheritedOpt := snapshots.WithLabels(snapshots.FilterInheritedLabels(base.Labels)) + + // NOTE: Backend snapshotters should commit fast and reliably to + // prevent metadata store locking and minimizing rollbacks. + // This operation should be done in the transaction to minimize the + // risk of the committed keys becoming out of sync. If this operation + // succeed and the overall transaction fails then the risk of out of + // sync data is higher and may require manual cleanup. + if err := s.Snapshotter.Commit(ctx, nameKey, bkey, inheritedOpt); err != nil { + if errdefs.IsNotFound(err) { + log.G(ctx).WithField("snapshotter", s.name).WithField("key", key).WithError(err).Error("uncommittable snapshot: missing in backend, snapshot should be removed") + } + // NOTE: Consider handling already exists here from the backend. Currently + // already exists from the backend may be confusing to the client since it + // may require the client to re-attempt from prepare. However, if handling + // here it is not clear what happened with the existing backend key and + // whether the already prepared snapshot would still be used or must be + // discarded. It is best that all implementations of the snapshotter + // interface behave the same, in which case the backend should handle the + // mapping of duplicates and not error. + return err + } + bname = nameKey + + return nil + }); err != nil { + if bname != "" { + log.G(ctx).WithField("snapshotter", s.name).WithField("key", key).WithField("bname", bname).WithError(err).Error("uncommittable snapshot: transaction failed after commit, snapshot should be removed") + + } + return err + } + + return nil } @@ -500,9 +671,8 @@ func (s *snapshotter) Remove(ctx context.Context, key string) error { } // Mark snapshotter as dirty for triggering garbage collection - s.db.dirtyL.Lock() + atomic.AddUint32(&s.db.dirty, 1) s.db.dirtySS[s.name] = struct{}{} - s.db.dirtyL.Unlock() return nil }) @@ -513,7 +683,7 @@ type infoPair struct { info snapshots.Info } -func (s *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error { +func (s *snapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, fs ...string) error { ns, err := namespaces.NamespaceRequired(ctx) if err != nil { return err @@ -525,6 +695,11 @@ func (s *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapsho lastKey string ) + filter, err := filters.ParseAll(fs...) + if err != nil { + return err + } + for { if err := view(ctx, s.db, func(tx *bolt.Tx) error { bkt := getSnapshotterBucket(tx, ns, s.name) @@ -587,8 +762,11 @@ func (s *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapsho return err } - if err := fn(ctx, overlayInfo(info, pair.info)); err != nil { - return err + info = overlayInfo(info, pair.info) + if filter.Match(adaptSnapshot(info)) { + if err := fn(ctx, info); err != nil { + return err + } } } @@ -613,22 +791,21 @@ func validateSnapshot(info *snapshots.Info) error { return nil } -type cleaner interface { - Cleanup(ctx context.Context) error -} - func (s *snapshotter) garbageCollect(ctx context.Context) (d time.Duration, err error) { s.l.Lock() t1 := time.Now() defer func() { s.l.Unlock() if err == nil { - if c, ok := s.Snapshotter.(cleaner); ok { + if c, ok := s.Snapshotter.(snapshots.Cleaner); ok { err = c.Cleanup(ctx) + if errdefs.IsNotImplemented(err) { + err = nil + } } } if err == nil { - d = time.Now().Sub(t1) + d = time.Since(t1) } }() diff --git a/vendor/github.com/containerd/containerd/mount/lookup_unix.go b/vendor/github.com/containerd/containerd/mount/lookup_unix.go index e8b0a0b483be4..a2d51ce5849e4 100644 --- a/vendor/github.com/containerd/containerd/mount/lookup_unix.go +++ b/vendor/github.com/containerd/containerd/mount/lookup_unix.go @@ -20,9 +20,8 @@ package mount import ( "path/filepath" - "sort" - "strings" + "github.com/moby/sys/mountinfo" "github.com/pkg/errors" ) @@ -30,24 +29,21 @@ import ( func Lookup(dir string) (Info, error) { dir = filepath.Clean(dir) - mounts, err := Self() + m, err := mountinfo.GetMounts(mountinfo.ParentsFilter(dir)) if err != nil { - return Info{}, err + return Info{}, errors.Wrapf(err, "failed to find the mount info for %q", dir) + } + if len(m) == 0 { + return Info{}, errors.Errorf("failed to find the mount info for %q", dir) } - // Sort descending order by Info.Mountpoint - sort.SliceStable(mounts, func(i, j int) bool { - return mounts[j].Mountpoint < mounts[i].Mountpoint - }) - for _, m := range mounts { - // Note that m.{Major, Minor} are generally unreliable for our purpose here - // https://www.spinics.net/lists/linux-btrfs/msg58908.html - // Note that device number is not checked here, because for overlayfs files - // may have different device number with the mountpoint. - if strings.HasPrefix(dir, m.Mountpoint) { - return m, nil + // find the longest matching mount point + var idx, maxlen int + for i := range m { + if len(m[i].Mountpoint) > maxlen { + maxlen = len(m[i].Mountpoint) + idx = i } } - - return Info{}, errors.Errorf("failed to find the mount info for %q", dir) + return *m[idx], nil } diff --git a/vendor/github.com/containerd/containerd/mount/losetup_linux.go b/vendor/github.com/containerd/containerd/mount/losetup_linux.go new file mode 100644 index 0000000000000..e99e962f12c76 --- /dev/null +++ b/vendor/github.com/containerd/containerd/mount/losetup_linux.go @@ -0,0 +1,208 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package mount + +import ( + "fmt" + "math/rand" + "os" + "strings" + "syscall" + "time" + "unsafe" + + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +const ( + loopControlPath = "/dev/loop-control" + loopDevFormat = "/dev/loop%d" + + ebusyString = "device or resource busy" +) + +// LoopParams parameters to control loop device setup +type LoopParams struct { + // Loop device should forbid write + Readonly bool + // Loop device is automatically cleared by kernel when the + // last opener closes it + Autoclear bool + // Use direct IO to access the loop backing file + Direct bool +} + +func ioctl(fd, req, args uintptr) (uintptr, uintptr, error) { + r1, r2, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, req, args) + if errno != 0 { + return 0, 0, errno + } + + return r1, r2, nil +} + +func getFreeLoopDev() (uint32, error) { + ctrl, err := os.OpenFile(loopControlPath, os.O_RDWR, 0) + if err != nil { + return 0, errors.Errorf("could not open %v: %v", loopControlPath, err) + } + defer ctrl.Close() + num, _, err := ioctl(ctrl.Fd(), unix.LOOP_CTL_GET_FREE, 0) + if err != nil { + return 0, errors.Wrap(err, "could not get free loop device") + } + return uint32(num), nil +} + +// setupLoopDev attaches the backing file to the loop device and returns +// the file handle for the loop device. The caller is responsible for +// closing the file handle. +func setupLoopDev(backingFile, loopDev string, param LoopParams) (_ *os.File, retErr error) { + // 1. Open backing file and loop device + flags := os.O_RDWR + if param.Readonly { + flags = os.O_RDONLY + } + + back, err := os.OpenFile(backingFile, flags, 0) + if err != nil { + return nil, errors.Wrapf(err, "could not open backing file: %s", backingFile) + } + defer back.Close() + + loop, err := os.OpenFile(loopDev, flags, 0) + if err != nil { + return nil, errors.Wrapf(err, "could not open loop device: %s", loopDev) + } + defer func() { + if retErr != nil { + loop.Close() + } + }() + + // 2. Set FD + if _, _, err = ioctl(loop.Fd(), unix.LOOP_SET_FD, back.Fd()); err != nil { + return nil, errors.Wrapf(err, "could not set loop fd for device: %s", loopDev) + } + + // 3. Set Info + info := unix.LoopInfo64{} + copy(info.File_name[:], backingFile) + if param.Readonly { + info.Flags |= unix.LO_FLAGS_READ_ONLY + } + + if param.Autoclear { + info.Flags |= unix.LO_FLAGS_AUTOCLEAR + } + + if param.Direct { + info.Flags |= unix.LO_FLAGS_DIRECT_IO + } + + _, _, err = ioctl(loop.Fd(), unix.LOOP_SET_STATUS64, uintptr(unsafe.Pointer(&info))) + if err == nil { + return loop, nil + } + + if param.Direct { + // Retry w/o direct IO flag in case kernel does not support it. The downside is that + // it will suffer from double cache problem. + info.Flags &= ^(uint32(unix.LO_FLAGS_DIRECT_IO)) + _, _, err = ioctl(loop.Fd(), unix.LOOP_SET_STATUS64, uintptr(unsafe.Pointer(&info))) + if err == nil { + return loop, nil + } + } + + _, _, _ = ioctl(loop.Fd(), unix.LOOP_CLR_FD, 0) + return nil, errors.Errorf("failed to set loop device info: %v", err) +} + +// setupLoop looks for (and possibly creates) a free loop device, and +// then attaches backingFile to it. +// +// When autoclear is true, caller should take care to close it when +// done with the loop device. The loop device file handle keeps +// loFlagsAutoclear in effect and we rely on it to clean up the loop +// device. If caller closes the file handle after mounting the device, +// kernel will clear the loop device after it is umounted. Otherwise +// the loop device is cleared when the file handle is closed. +// +// When autoclear is false, caller should be responsible to remove +// the loop device when done with it. +// +// Upon success, the file handle to the loop device is returned. +func setupLoop(backingFile string, param LoopParams) (*os.File, error) { + for retry := 1; retry < 100; retry++ { + num, err := getFreeLoopDev() + if err != nil { + return nil, err + } + + loopDev := fmt.Sprintf(loopDevFormat, num) + file, err := setupLoopDev(backingFile, loopDev, param) + if err != nil { + // Per util-linux/sys-utils/losetup.c:create_loop(), + // free loop device can race and we end up failing + // with EBUSY when trying to set it up. + if strings.Contains(err.Error(), ebusyString) { + // Fallback a bit to avoid live lock + time.Sleep(time.Millisecond * time.Duration(rand.Intn(retry*10))) + continue + } + return nil, err + } + + return file, nil + } + + return nil, errors.New("timeout creating new loopback device") +} + +func removeLoop(loopdev string) error { + file, err := os.Open(loopdev) + if err != nil { + return err + } + defer file.Close() + + _, _, err = ioctl(file.Fd(), unix.LOOP_CLR_FD, 0) + return err +} + +// AttachLoopDevice attaches a specified backing file to a loop device +func AttachLoopDevice(backingFile string) (string, error) { + file, err := setupLoop(backingFile, LoopParams{}) + if err != nil { + return "", err + } + defer file.Close() + return file.Name(), nil +} + +// DetachLoopDevice detaches the provided loop devices +func DetachLoopDevice(devices ...string) error { + for _, dev := range devices { + if err := removeLoop(dev); err != nil { + return errors.Wrapf(err, "failed to remove loop device: %s", dev) + } + } + + return nil +} diff --git a/vendor/github.com/containerd/containerd/mount/mount_freebsd.go b/vendor/github.com/containerd/containerd/mount/mount_freebsd.go new file mode 100644 index 0000000000000..d52450013c06a --- /dev/null +++ b/vendor/github.com/containerd/containerd/mount/mount_freebsd.go @@ -0,0 +1,137 @@ +// +build freebsd + +/* + Copyright The containerd Authors. + + 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. +*/ + +package mount + +import ( + "os" + "os/exec" + "time" + + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +var ( + // ErrNotImplementOnUnix is returned for methods that are not implemented + ErrNotImplementOnUnix = errors.New("not implemented under unix") +) + +// Mount to the provided target path. +func (m *Mount) Mount(target string) error { + // The "syscall" and "golang.org/x/sys/unix" packages do not define a Mount + // function for FreeBSD, so instead we execute mount(8) and trust it to do + // the right thing + return m.mountWithHelper(target) +} + +func (m *Mount) mountWithHelper(target string) error { + // target: "/foo/target" + // command: "mount -o ro -t nullfs /foo/source /foo/merged" + // Note: FreeBSD mount(8) is particular about the order of flags and arguments + var args []string + for _, o := range m.Options { + args = append(args, "-o", o) + } + args = append(args, "-t", m.Type) + args = append(args, m.Source, target) + + infoBeforeMount, err := Lookup(target) + if err != nil { + return err + } + + // cmd.CombinedOutput() may intermittently return ECHILD because of our signal handling in shim. + // See #4387 and wait(2). + const retriesOnECHILD = 10 + for i := 0; i < retriesOnECHILD; i++ { + cmd := exec.Command("mount", args...) + out, err := cmd.CombinedOutput() + if err == nil { + return nil + } + if !errors.Is(err, unix.ECHILD) { + return errors.Wrapf(err, "mount [%v] failed: %q", args, string(out)) + } + // We got ECHILD, we are not sure whether the mount was successful. + // If the mount ID has changed, we are sure we got some new mount, but still not sure it is fully completed. + // So we attempt to unmount the new mount before retrying. + infoAfterMount, err := Lookup(target) + if err != nil { + return err + } + if infoAfterMount.ID != infoBeforeMount.ID { + _ = unmount(target, 0) + } + } + return errors.Errorf("mount [%v] failed with ECHILD (retired %d times)", args, retriesOnECHILD) +} + +// Unmount the provided mount path with the flags +func Unmount(target string, flags int) error { + if err := unmount(target, flags); err != nil && err != unix.EINVAL { + return err + } + return nil +} + +func unmount(target string, flags int) error { + for i := 0; i < 50; i++ { + if err := unix.Unmount(target, flags); err != nil { + switch err { + case unix.EBUSY: + time.Sleep(50 * time.Millisecond) + continue + default: + return err + } + } + return nil + } + return errors.Wrapf(unix.EBUSY, "failed to unmount target %s", target) +} + +// UnmountAll repeatedly unmounts the given mount point until there +// are no mounts remaining (EINVAL is returned by mount), which is +// useful for undoing a stack of mounts on the same mount point. +// UnmountAll all is noop when the first argument is an empty string. +// This is done when the containerd client did not specify any rootfs +// mounts (e.g. because the rootfs is managed outside containerd) +// UnmountAll is noop when the mount path does not exist. +func UnmountAll(mount string, flags int) error { + if mount == "" { + return nil + } + if _, err := os.Stat(mount); os.IsNotExist(err) { + return nil + } + + for { + if err := unmount(mount, flags); err != nil { + // EINVAL is returned if the target is not a + // mount point, indicating that we are + // done. It can also indicate a few other + // things (such as invalid flags) which we + // unfortunately end up squelching here too. + if err == unix.EINVAL { + return nil + } + return err + } + } +} diff --git a/vendor/github.com/containerd/containerd/mount/mount_linux.go b/vendor/github.com/containerd/containerd/mount/mount_linux.go index b5a16148acf87..3f05ebc891acc 100644 --- a/vendor/github.com/containerd/containerd/mount/mount_linux.go +++ b/vendor/github.com/containerd/containerd/mount/mount_linux.go @@ -19,6 +19,7 @@ package mount import ( "fmt" "os" + "os/exec" "path" "strings" "time" @@ -28,14 +29,27 @@ import ( "golang.org/x/sys/unix" ) -var pagesize = 4096 +var ( + pagesize = 4096 + allowedHelperBinaries = []string{"mount.fuse", "mount.fuse3"} +) func init() { pagesize = os.Getpagesize() } -// Mount to the provided target path -func (m *Mount) Mount(target string) error { +// Mount to the provided target path. +// +// If m.Type starts with "fuse." or "fuse3.", "mount.fuse" or "mount.fuse3" +// helper binary is called. +func (m *Mount) Mount(target string) (err error) { + for _, helperBinary := range allowedHelperBinaries { + // helperBinary = "mount.fuse", typePrefix = "fuse." + typePrefix := strings.TrimPrefix(helperBinary, "mount.") + "." + if strings.HasPrefix(m.Type, typePrefix) { + return m.mountWithHelper(helperBinary, typePrefix, target) + } + } var ( chdir string options = m.Options @@ -48,7 +62,7 @@ func (m *Mount) Mount(target string) error { chdir, options = compactLowerdirOption(options) } - flags, data := parseMountOptions(options) + flags, data, losetup := parseMountOptions(options) if len(data) > pagesize { return errors.Errorf("mount options is too long") } @@ -63,7 +77,20 @@ func (m *Mount) Mount(target string) error { if flags&unix.MS_REMOUNT == 0 || data != "" { // Initial call applying all non-propagation flags for mount // or remount with changed data - if err := mountAt(chdir, m.Source, target, m.Type, uintptr(oflags), data); err != nil { + source := m.Source + if losetup { + loFile, err := setupLoop(m.Source, LoopParams{ + Readonly: oflags&unix.MS_RDONLY == unix.MS_RDONLY, + Autoclear: true}) + if err != nil { + return err + } + defer loFile.Close() + + // Mount the loop device instead + source = loFile.Name() + } + if err := mountAt(chdir, source, target, m.Type, uintptr(oflags), data); err != nil { return err } } @@ -92,7 +119,39 @@ func Unmount(target string, flags int) error { return nil } +// fuseSuperMagic is defined in statfs(2) +const fuseSuperMagic = 0x65735546 + +func isFUSE(dir string) bool { + var st unix.Statfs_t + if err := unix.Statfs(dir, &st); err != nil { + return false + } + return st.Type == fuseSuperMagic +} + +// unmountFUSE attempts to unmount using fusermount/fusermount3 helper binary. +// +// For FUSE mounts, using these helper binaries is preferred, see: +// https://github.com/containerd/containerd/pull/3765#discussion_r342083514 +func unmountFUSE(target string) error { + var err error + for _, helperBinary := range []string{"fusermount3", "fusermount"} { + cmd := exec.Command(helperBinary, "-u", target) + err = cmd.Run() + if err == nil { + return nil + } + } + return err +} + func unmount(target string, flags int) error { + if isFUSE(target) { + if err := unmountFUSE(target); err == nil { + return nil + } + } for i := 0; i < 50; i++ { if err := unix.Unmount(target, flags); err != nil { switch err { @@ -111,7 +170,18 @@ func unmount(target string, flags int) error { // UnmountAll repeatedly unmounts the given mount point until there // are no mounts remaining (EINVAL is returned by mount), which is // useful for undoing a stack of mounts on the same mount point. +// UnmountAll all is noop when the first argument is an empty string. +// This is done when the containerd client did not specify any rootfs +// mounts (e.g. because the rootfs is managed outside containerd) +// UnmountAll is noop when the mount path does not exist. func UnmountAll(mount string, flags int) error { + if mount == "" { + return nil + } + if _, err := os.Stat(mount); os.IsNotExist(err) { + return nil + } + for { if err := unmount(mount, flags); err != nil { // EINVAL is returned if the target is not a @@ -129,11 +199,13 @@ func UnmountAll(mount string, flags int) error { // parseMountOptions takes fstab style mount options and parses them for // use with a standard mount() syscall -func parseMountOptions(options []string) (int, string) { +func parseMountOptions(options []string) (int, string, bool) { var ( - flag int - data []string + flag int + losetup bool + data []string ) + loopOpt := "loop" flags := map[string]struct { clear bool flag int @@ -174,11 +246,13 @@ func parseMountOptions(options []string) (int, string) { } else { flag |= f.flag } + } else if o == loopOpt { + losetup = true } else { data = append(data, o) } } - return flag, strings.Join(data, ",") + return flag, strings.Join(data, ","), losetup } // compactLowerdirOption updates overlay lowdir option and returns the common @@ -306,3 +380,45 @@ func mountAt(chdir string, source, target, fstype string, flags uintptr, data st } return errors.Wrap(sys.FMountat(f.Fd(), source, target, fstype, flags, data), "failed to mountat") } + +func (m *Mount) mountWithHelper(helperBinary, typePrefix, target string) error { + // helperBinary: "mount.fuse3" + // target: "/foo/merged" + // m.Type: "fuse3.fuse-overlayfs" + // command: "mount.fuse3 overlay /foo/merged -o lowerdir=/foo/lower2:/foo/lower1,upperdir=/foo/upper,workdir=/foo/work -t fuse-overlayfs" + args := []string{m.Source, target} + for _, o := range m.Options { + args = append(args, "-o", o) + } + args = append(args, "-t", strings.TrimPrefix(m.Type, typePrefix)) + + infoBeforeMount, err := Lookup(target) + if err != nil { + return err + } + + // cmd.CombinedOutput() may intermittently return ECHILD because of our signal handling in shim. + // See #4387 and wait(2). + const retriesOnECHILD = 10 + for i := 0; i < retriesOnECHILD; i++ { + cmd := exec.Command(helperBinary, args...) + out, err := cmd.CombinedOutput() + if err == nil { + return nil + } + if !errors.Is(err, unix.ECHILD) { + return errors.Wrapf(err, "mount helper [%s %v] failed: %q", helperBinary, args, string(out)) + } + // We got ECHILD, we are not sure whether the mount was successful. + // If the mount ID has changed, we are sure we got some new mount, but still not sure it is fully completed. + // So we attempt to unmount the new mount before retrying. + infoAfterMount, err := Lookup(target) + if err != nil { + return err + } + if infoAfterMount.ID != infoBeforeMount.ID { + _ = unmount(target, 0) + } + } + return errors.Errorf("mount helper [%s %v] failed with ECHILD (retired %d times)", helperBinary, args, retriesOnECHILD) +} diff --git a/vendor/github.com/containerd/containerd/mount/mount_unix.go b/vendor/github.com/containerd/containerd/mount/mount_unix.go index 6741293f89f8a..ecf569d9c4a0b 100644 --- a/vendor/github.com/containerd/containerd/mount/mount_unix.go +++ b/vendor/github.com/containerd/containerd/mount/mount_unix.go @@ -1,4 +1,4 @@ -// +build darwin freebsd +// +build darwin openbsd /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/containerd/mount/mountinfo.go b/vendor/github.com/containerd/containerd/mount/mountinfo.go index e7a68402f51f9..a42238d867fd0 100644 --- a/vendor/github.com/containerd/containerd/mount/mountinfo.go +++ b/vendor/github.com/containerd/containerd/mount/mountinfo.go @@ -16,41 +16,8 @@ package mount +import "github.com/moby/sys/mountinfo" + // Info reveals information about a particular mounted filesystem. This // struct is populated from the content in the /proc//mountinfo file. -type Info struct { - // ID is a unique identifier of the mount (may be reused after umount). - ID int - - // Parent indicates the ID of the mount parent (or of self for the top of the - // mount tree). - Parent int - - // Major indicates one half of the device ID which identifies the device class. - Major int - - // Minor indicates one half of the device ID which identifies a specific - // instance of device. - Minor int - - // Root of the mount within the filesystem. - Root string - - // Mountpoint indicates the mount point relative to the process's root. - Mountpoint string - - // Options represents mount-specific options. - Options string - - // Optional represents optional fields. - Optional string - - // FSType indicates the type of filesystem, such as EXT3. - FSType string - - // Source indicates filesystem specific information or "none". - Source string - - // VFSOptions represents per super block options. - VFSOptions string -} +type Info = mountinfo.Info diff --git a/vendor/github.com/containerd/containerd/mount/mountinfo_freebsd.go b/vendor/github.com/containerd/containerd/mount/mountinfo_freebsd.go deleted file mode 100644 index bbe79767e3341..0000000000000 --- a/vendor/github.com/containerd/containerd/mount/mountinfo_freebsd.go +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package mount - -/* -#include -#include -#include -*/ -import "C" - -import ( - "fmt" - "reflect" - "unsafe" -) - -// Self retrieves a list of mounts for the current running process. -func Self() ([]Info, error) { - var rawEntries *C.struct_statfs - - count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT)) - if count == 0 { - return nil, fmt.Errorf("Failed to call getmntinfo") - } - - var entries []C.struct_statfs - header := (*reflect.SliceHeader)(unsafe.Pointer(&entries)) - header.Cap = count - header.Len = count - header.Data = uintptr(unsafe.Pointer(rawEntries)) - - var out []Info - for _, entry := range entries { - var mountinfo Info - mountinfo.Mountpoint = C.GoString(&entry.f_mntonname[0]) - mountinfo.Source = C.GoString(&entry.f_mntfromname[0]) - mountinfo.FSType = C.GoString(&entry.f_fstypename[0]) - out = append(out, mountinfo) - } - return out, nil -} - -// PID collects the mounts for a specific process ID. -func PID(pid int) ([]Info, error) { - return nil, fmt.Errorf("mountinfo.PID is not implemented on freebsd") -} diff --git a/vendor/github.com/containerd/containerd/mount/mountinfo_linux.go b/vendor/github.com/containerd/containerd/mount/mountinfo_linux.go deleted file mode 100644 index a986f8f4ea5d5..0000000000000 --- a/vendor/github.com/containerd/containerd/mount/mountinfo_linux.go +++ /dev/null @@ -1,135 +0,0 @@ -// +build linux - -/* - Copyright The containerd Authors. - - 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. -*/ - -package mount - -import ( - "bufio" - "fmt" - "io" - "os" - "strconv" - "strings" -) - -// Self retrieves a list of mounts for the current running process. -func Self() ([]Info, error) { - f, err := os.Open("/proc/self/mountinfo") - if err != nil { - return nil, err - } - defer f.Close() - - return parseInfoFile(f) -} - -func parseInfoFile(r io.Reader) ([]Info, error) { - s := bufio.NewScanner(r) - out := []Info{} - - for s.Scan() { - if err := s.Err(); err != nil { - return nil, err - } - - /* - 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue - (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) - (1) mount ID: unique identifier of the mount (may be reused after umount) - (2) parent ID: ID of parent (or of self for the top of the mount tree) - (3) major:minor: value of st_dev for files on filesystem - (4) root: root of the mount within the filesystem - (5) mount point: mount point relative to the process's root - (6) mount options: per mount options - (7) optional fields: zero or more fields of the form "tag[:value]" - (8) separator: marks the end of the optional fields - (9) filesystem type: name of filesystem of the form "type[.subtype]" - (10) mount source: filesystem specific information or "none" - (11) super options: per super block options - */ - - text := s.Text() - fields := strings.Split(text, " ") - numFields := len(fields) - if numFields < 10 { - // should be at least 10 fields - return nil, fmt.Errorf("Parsing '%s' failed: not enough fields (%d)", text, numFields) - } - p := Info{} - // ignore any numbers parsing errors, as there should not be any - p.ID, _ = strconv.Atoi(fields[0]) - p.Parent, _ = strconv.Atoi(fields[1]) - mm := strings.Split(fields[2], ":") - if len(mm) != 2 { - return nil, fmt.Errorf("Parsing '%s' failed: unexpected minor:major pair %s", text, mm) - } - p.Major, _ = strconv.Atoi(mm[0]) - p.Minor, _ = strconv.Atoi(mm[1]) - - p.Root = fields[3] - p.Mountpoint = fields[4] - p.Options = fields[5] - - // one or more optional fields, when a separator (-) - i := 6 - for ; i < numFields && fields[i] != "-"; i++ { - switch i { - case 6: - p.Optional = fields[6] - default: - /* NOTE there might be more optional fields before the separator - such as fields[7]...fields[N] (where N < separatorIndex), - although as of Linux kernel 4.15 the only known ones are - mount propagation flags in fields[6]. The correct - behavior is to ignore any unknown optional fields. - */ - } - } - if i == numFields { - return nil, fmt.Errorf("Parsing '%s' failed: missing separator ('-')", text) - } - // There should be 3 fields after the separator... - if i+4 > numFields { - return nil, fmt.Errorf("Parsing '%s' failed: not enough fields after a separator", text) - } - // ... but in Linux <= 3.9 mounting a cifs with spaces in a share name - // (like "//serv/My Documents") _may_ end up having a space in the last field - // of mountinfo (like "unc=//serv/My Documents"). Since kernel 3.10-rc1, cifs - // option unc= is ignored, so a space should not appear. In here we ignore - // those "extra" fields caused by extra spaces. - p.FSType = fields[i+1] - p.Source = fields[i+2] - p.VFSOptions = fields[i+3] - - out = append(out, p) - } - return out, nil -} - -// PID collects the mounts for a specific process ID. If the process -// ID is unknown, it is better to use `Self` which will inspect -// "/proc/self/mountinfo" instead. -func PID(pid int) ([]Info, error) { - f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) - if err != nil { - return nil, err - } - defer f.Close() - - return parseInfoFile(f) -} diff --git a/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go b/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go deleted file mode 100644 index eba602f1a6483..0000000000000 --- a/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build !linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo - -/* - Copyright The containerd Authors. - - 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. -*/ - -package mount - -import ( - "fmt" - "runtime" -) - -// Self retrieves a list of mounts for the current running process. -func Self() ([]Info, error) { - return nil, fmt.Errorf("mountinfo.Self is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) -} - -// PID collects the mounts for a specific process ID. -func PID(pid int) ([]Info, error) { - return nil, fmt.Errorf("mountinfo.PID is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) -} diff --git a/vendor/github.com/containerd/containerd/mount/temp_unix.go b/vendor/github.com/containerd/containerd/mount/temp_unix.go index 3d490e8a70c71..ed190b8f9db57 100644 --- a/vendor/github.com/containerd/containerd/mount/temp_unix.go +++ b/vendor/github.com/containerd/containerd/mount/temp_unix.go @@ -22,7 +22,8 @@ import ( "os" "path/filepath" "sort" - "strings" + + "github.com/moby/sys/mountinfo" ) // SetTempMountLocation sets the temporary mount location @@ -40,23 +41,20 @@ func SetTempMountLocation(root string) error { // CleanupTempMounts all temp mounts and remove the directories func CleanupTempMounts(flags int) (warnings []error, err error) { - mounts, err := Self() + mounts, err := mountinfo.GetMounts(mountinfo.PrefixFilter(tempMountLocation)) if err != nil { return nil, err } - var toUnmount []string - for _, m := range mounts { - if strings.HasPrefix(m.Mountpoint, tempMountLocation) { - toUnmount = append(toUnmount, m.Mountpoint) - } - } - sort.Sort(sort.Reverse(sort.StringSlice(toUnmount))) - for _, path := range toUnmount { - if err := UnmountAll(path, flags); err != nil { + // Make the deepest mount be first + sort.Slice(mounts, func(i, j int) bool { + return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint) + }) + for _, mount := range mounts { + if err := UnmountAll(mount.Mountpoint, flags); err != nil { warnings = append(warnings, err) continue } - if err := os.Remove(path); err != nil { + if err := os.Remove(mount.Mountpoint); err != nil { warnings = append(warnings, err) } } diff --git a/vendor/github.com/containerd/containerd/namespaces.go b/vendor/github.com/containerd/containerd/namespaces.go index eea70ca33a3e1..4c66406b084ea 100644 --- a/vendor/github.com/containerd/containerd/namespaces.go +++ b/vendor/github.com/containerd/containerd/namespaces.go @@ -100,10 +100,18 @@ func (r *remoteNamespaces) List(ctx context.Context) ([]string, error) { return namespaces, nil } -func (r *remoteNamespaces) Delete(ctx context.Context, namespace string) error { - var req api.DeleteNamespaceRequest - - req.Name = namespace +func (r *remoteNamespaces) Delete(ctx context.Context, namespace string, opts ...namespaces.DeleteOpts) error { + i := namespaces.DeleteInfo{ + Name: namespace, + } + for _, o := range opts { + if err := o(ctx, &i); err != nil { + return err + } + } + req := api.DeleteNamespaceRequest{ + Name: namespace, + } _, err := r.client.Delete(ctx, &req) if err != nil { return errdefs.FromGRPC(err) diff --git a/vendor/github.com/containerd/containerd/namespaces/context.go b/vendor/github.com/containerd/containerd/namespaces/context.go index cc5621a68fb15..b53c9012c1b4c 100644 --- a/vendor/github.com/containerd/containerd/namespaces/context.go +++ b/vendor/github.com/containerd/containerd/namespaces/context.go @@ -21,6 +21,7 @@ import ( "os" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/identifiers" "github.com/pkg/errors" ) @@ -36,10 +37,9 @@ type namespaceKey struct{} // WithNamespace sets a given namespace on the context func WithNamespace(ctx context.Context, namespace string) context.Context { ctx = context.WithValue(ctx, namespaceKey{}, namespace) // set our key for namespace - - // also store on the grpc headers so it gets picked up by any clients that + // also store on the grpc and ttrpc headers so it gets picked up by any clients that // are using this. - return withGRPCNamespaceHeader(ctx, namespace) + return withTTRPCNamespaceHeader(withGRPCNamespaceHeader(ctx, namespace), namespace) } // NamespaceFromEnv uses the namespace defined in CONTAINERD_NAMESPACE or @@ -58,22 +58,21 @@ func NamespaceFromEnv(ctx context.Context) context.Context { func Namespace(ctx context.Context) (string, bool) { namespace, ok := ctx.Value(namespaceKey{}).(string) if !ok { - return fromGRPCHeader(ctx) + if namespace, ok = fromGRPCHeader(ctx); !ok { + return fromTTRPCHeader(ctx) + } } - return namespace, ok } -// NamespaceRequired returns the valid namepace from the context or an error. +// NamespaceRequired returns the valid namespace from the context or an error. func NamespaceRequired(ctx context.Context) (string, error) { namespace, ok := Namespace(ctx) if !ok || namespace == "" { return "", errors.Wrapf(errdefs.ErrFailedPrecondition, "namespace is required") } - - if err := Validate(namespace); err != nil { + if err := identifiers.Validate(namespace); err != nil { return "", errors.Wrap(err, "namespace validation") } - return namespace, nil } diff --git a/vendor/github.com/containerd/containerd/namespaces/store.go b/vendor/github.com/containerd/containerd/namespaces/store.go index 0b5c985691977..5936772cb4cb2 100644 --- a/vendor/github.com/containerd/containerd/namespaces/store.go +++ b/vendor/github.com/containerd/containerd/namespaces/store.go @@ -33,5 +33,14 @@ type Store interface { List(ctx context.Context) ([]string, error) // Delete removes the namespace. The namespace must be empty to be deleted. - Delete(ctx context.Context, namespace string) error + Delete(ctx context.Context, namespace string, opts ...DeleteOpts) error } + +// DeleteInfo specifies information for the deletion of a namespace +type DeleteInfo struct { + // Name of the namespace + Name string +} + +// DeleteOpts allows the caller to set options for namespace deletion +type DeleteOpts func(context.Context, *DeleteInfo) error diff --git a/vendor/github.com/containerd/containerd/namespaces/ttrpc.go b/vendor/github.com/containerd/containerd/namespaces/ttrpc.go new file mode 100644 index 0000000000000..bcd2643cf5ea2 --- /dev/null +++ b/vendor/github.com/containerd/containerd/namespaces/ttrpc.go @@ -0,0 +1,51 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package namespaces + +import ( + "context" + + "github.com/containerd/ttrpc" +) + +const ( + // TTRPCHeader defines the header name for specifying a containerd namespace + TTRPCHeader = "containerd-namespace-ttrpc" +) + +func copyMetadata(src ttrpc.MD) ttrpc.MD { + md := ttrpc.MD{} + for k, v := range src { + md[k] = append(md[k], v...) + } + return md +} + +func withTTRPCNamespaceHeader(ctx context.Context, namespace string) context.Context { + md, ok := ttrpc.GetMetadata(ctx) + if !ok { + md = ttrpc.MD{} + } else { + md = copyMetadata(md) + } + md.Set(TTRPCHeader, namespace) + return ttrpc.WithMetadata(ctx, md) +} + +func fromTTRPCHeader(ctx context.Context) (string, bool) { + return ttrpc.GetMetadataValue(ctx, TTRPCHeader) +} diff --git a/vendor/github.com/containerd/containerd/namespaces/validate.go b/vendor/github.com/containerd/containerd/namespaces/validate.go deleted file mode 100644 index 222da3ea43597..0000000000000 --- a/vendor/github.com/containerd/containerd/namespaces/validate.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -// Package namespaces provides tools for working with namespaces across -// containerd. -// -// Namespaces collect resources such as containers and images, into a unique -// identifier space. This means that two applications can use the same -// identifiers and not conflict while using containerd. -// -// This package can be used to ensure that client and server functions -// correctly store the namespace on the context. -package namespaces - -import ( - "regexp" - - "github.com/containerd/containerd/errdefs" - "github.com/pkg/errors" -) - -const ( - maxLength = 76 - alpha = `[A-Za-z]` - alphanum = `[A-Za-z0-9]+` - label = alpha + alphanum + `(:?[-]+` + alpha + alphanum + `)*` -) - -var ( - // namespaceRe validates that a namespace matches valid identifiers. - // - // Rules for domains, defined in RFC 1035, section 2.3.1, are used for - // namespaces. - namespaceRe = regexp.MustCompile(reAnchor(label + reGroup("[.]"+reGroup(label)) + "*")) -) - -// Validate returns nil if the string s is a valid namespace. -// -// To allow such namespace identifiers to be used across various contexts -// safely, the character set has been restricted to that defined for domains in -// RFC 1035, section 2.3.1. This will make namespace identifiers safe for use -// across networks, filesystems and other media. -// -// The identifier specification departs from RFC 1035 in that it allows -// "labels" to start with number and only enforces a total length restriction -// of 76 characters. -// -// While the character set may be expanded in the future, namespace identifiers -// are guaranteed to be safely used as filesystem path components. -// -// For the most part, this doesn't need to be called directly when using the -// context-oriented functions. -func Validate(s string) error { - if len(s) > maxLength { - return errors.Wrapf(errdefs.ErrInvalidArgument, "namespace %q greater than maximum length (%d characters)", s, maxLength) - } - - if !namespaceRe.MatchString(s) { - return errors.Wrapf(errdefs.ErrInvalidArgument, "namespace %q must match %v", s, namespaceRe) - } - return nil -} - -func reGroup(s string) string { - return `(?:` + s + `)` -} - -func reAnchor(s string) string { - return `^` + s + `$` -} diff --git a/vendor/github.com/containerd/containerd/oci/spec.go b/vendor/github.com/containerd/containerd/oci/spec.go index b83f40ac65a31..035bb7e7d83d0 100644 --- a/vendor/github.com/containerd/containerd/oci/spec.go +++ b/vendor/github.com/containerd/containerd/oci/spec.go @@ -78,7 +78,7 @@ func generateDefaultSpecWithPlatform(ctx context.Context, platform, id string, s return err } -// ApplyOpts applys the options to the given spec, injecting data from the +// ApplyOpts applies the options to the given spec, injecting data from the // context, client and container instance. func ApplyOpts(ctx context.Context, client Client, c *containers.Container, s *Spec, opts ...SpecOpts) error { for _, o := range opts { @@ -141,7 +141,6 @@ func populateDefaultUnixSpec(ctx context.Context, s *Spec, id string) error { Path: defaultRootfsPath, }, Process: &specs.Process{ - Env: defaultUnixEnv, Cwd: "/", NoNewPrivileges: true, User: specs.User{ @@ -209,6 +208,7 @@ func populateDefaultUnixSpec(ctx context.Context, s *Spec, id string) error { Linux: &specs.Linux{ MaskedPaths: []string{ "/proc/acpi", + "/proc/asound", "/proc/kcore", "/proc/keys", "/proc/latency_stats", @@ -219,7 +219,6 @@ func populateDefaultUnixSpec(ctx context.Context, s *Spec, id string) error { "/proc/scsi", }, ReadonlyPaths: []string{ - "/proc/asound", "/proc/bus", "/proc/fs", "/proc/irq", @@ -247,17 +246,8 @@ func populateDefaultWindowsSpec(ctx context.Context, s *Spec, id string) error { Root: &specs.Root{}, Process: &specs.Process{ Cwd: `C:\`, - ConsoleSize: &specs.Box{ - Width: 80, - Height: 20, - }, - }, - Windows: &specs.Windows{ - IgnoreFlushesDuringBoot: true, - Network: &specs.WindowsNetwork{ - AllowUnqualifiedDNSQuery: true, - }, }, + Windows: &specs.Windows{}, } return nil } diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts.go b/vendor/github.com/containerd/containerd/oci/spec_opts.go index 8b599f80586c6..5a952f6166459 100644 --- a/vendor/github.com/containerd/containerd/oci/spec_opts.go +++ b/vendor/github.com/containerd/containerd/oci/spec_opts.go @@ -17,6 +17,7 @@ package oci import ( + "bufio" "context" "encoding/json" "fmt" @@ -33,11 +34,10 @@ import ( "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/platforms" "github.com/containerd/continuity/fs" - "github.com/opencontainers/image-spec/specs-go/v1" + v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runc/libcontainer/user" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "github.com/syndtr/gocapability/capability" ) // SpecOpts sets spec specific information to a newly generated OCI spec @@ -76,6 +76,35 @@ func setLinux(s *Spec) { } } +// nolint +func setResources(s *Spec) { + if s.Linux != nil { + if s.Linux.Resources == nil { + s.Linux.Resources = &specs.LinuxResources{} + } + } + if s.Windows != nil { + if s.Windows.Resources == nil { + s.Windows.Resources = &specs.WindowsResources{} + } + } +} + +// nolint +func setCPU(s *Spec) { + setResources(s) + if s.Linux != nil { + if s.Linux.Resources.CPU == nil { + s.Linux.Resources.CPU = &specs.LinuxCPU{} + } + } + if s.Windows != nil { + if s.Windows.Resources.CPU == nil { + s.Windows.Resources.CPU = &specs.WindowsCPUResources{} + } + } +} + // setCapabilities sets Linux Capabilities to empty if unset func setCapabilities(s *Spec) { setProcess(s) @@ -104,7 +133,7 @@ func WithDefaultSpecForPlatform(platform string) SpecOpts { } } -// WithSpecFromBytes loads the the spec from the provided byte slice. +// WithSpecFromBytes loads the spec from the provided byte slice. func WithSpecFromBytes(p []byte) SpecOpts { return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { *s = Spec{} // make sure spec is cleared. @@ -137,12 +166,21 @@ func WithEnv(environmentVariables []string) SpecOpts { } } +// WithDefaultPathEnv sets the $PATH environment variable to the +// default PATH defined in this package. +func WithDefaultPathEnv(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Process.Env = replaceOrAppendEnvValues(s.Process.Env, defaultUnixEnv) + return nil +} + // replaceOrAppendEnvValues returns the defaults with the overrides either // replaced by env key or appended to the list func replaceOrAppendEnvValues(defaults, overrides []string) []string { cache := make(map[string]int, len(defaults)) + results := make([]string, 0, len(defaults)) for i, e := range defaults { parts := strings.SplitN(e, "=", 2) + results = append(results, e) cache[parts[0]] = i } @@ -150,7 +188,7 @@ func replaceOrAppendEnvValues(defaults, overrides []string) []string { // Values w/o = means they want this env to be removed/unset. if !strings.Contains(value, "=") { if i, exists := cache[value]; exists { - defaults[i] = "" // Used to indicate it should be removed + results[i] = "" // Used to indicate it should be removed } continue } @@ -158,21 +196,21 @@ func replaceOrAppendEnvValues(defaults, overrides []string) []string { // Just do a normal set/update parts := strings.SplitN(value, "=", 2) if i, exists := cache[parts[0]]; exists { - defaults[i] = value + results[i] = value } else { - defaults = append(defaults, value) + results = append(results, value) } } // Now remove all entries that we want to "unset" - for i := 0; i < len(defaults); i++ { - if defaults[i] == "" { - defaults = append(defaults[:i], defaults[i+1:]...) + for i := 0; i < len(results); i++ { + if results[i] == "" { + results = append(results[:i], results[i+1:]...) i-- } } - return defaults + return results } // WithProcessArgs replaces the args on the generated spec @@ -235,6 +273,28 @@ func WithMounts(mounts []specs.Mount) SpecOpts { } } +// WithoutMounts removes mounts +func WithoutMounts(dests ...string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + var ( + mounts []specs.Mount + current = s.Mounts + ) + mLoop: + for _, m := range current { + mDestination := filepath.Clean(m.Destination) + for _, dest := range dests { + if mDestination == dest { + continue mLoop + } + } + mounts = append(mounts, m) + } + s.Mounts = mounts + return nil + } +} + // WithHostNamespace allows a task to run inside the host's linux namespace func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts { return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { @@ -256,10 +316,7 @@ func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts { setLinux(s) for i, n := range s.Linux.Namespaces { if n.Type == ns.Type { - before := s.Linux.Namespaces[:i] - after := s.Linux.Namespaces[i+1:] - s.Linux.Namespaces = append(before, ns) - s.Linux.Namespaces = append(s.Linux.Namespaces, after...) + s.Linux.Namespaces[i] = ns return nil } } @@ -310,7 +367,11 @@ func WithImageConfigArgs(image Image, args []string) SpecOpts { setProcess(s) if s.Linux != nil { - s.Process.Env = append(s.Process.Env, config.Env...) + defaults := config.Env + if len(defaults) == 0 { + defaults = defaultUnixEnv + } + s.Process.Env = replaceOrAppendEnvValues(defaults, s.Process.Env) cmd := config.Cmd if len(args) > 0 { cmd = args @@ -332,8 +393,14 @@ func WithImageConfigArgs(image Image, args []string) SpecOpts { // even if there is no specified user in the image config return WithAdditionalGIDs("root")(ctx, client, c, s) } else if s.Windows != nil { - s.Process.Env = config.Env - s.Process.Args = append(config.Entrypoint, config.Cmd...) + s.Process.Env = replaceOrAppendEnvValues(config.Env, s.Process.Env) + cmd := config.Cmd + if len(args) > 0 { + cmd = args + } + s.Process.Args = append(config.Entrypoint, cmd...) + + s.Process.Cwd = config.WorkingDir s.Process.User = specs.User{ Username: config.User, } @@ -405,7 +472,7 @@ func WithHostLocaltime(_ context.Context, _ Client, _ *containers.Container, s * // WithUserNamespace sets the uid and gid mappings for the task // this can be called multiple times to add more mappings to the generated spec -func WithUserNamespace(container, host, size uint32) SpecOpts { +func WithUserNamespace(uidMap, gidMap []specs.LinuxIDMapping) SpecOpts { return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { var hasUserns bool setLinux(s) @@ -420,13 +487,8 @@ func WithUserNamespace(container, host, size uint32) SpecOpts { Type: specs.UserNamespace, }) } - mapping := specs.LinuxIDMapping{ - ContainerID: container, - HostID: host, - Size: size, - } - s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping) - s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping) + s.Linux.UIDMappings = append(s.Linux.UIDMappings, uidMap...) + s.Linux.GIDMappings = append(s.Linux.GIDMappings, gidMap...) return nil } } @@ -492,7 +554,7 @@ func WithUser(userstr string) SpecOpts { } f := func(root string) error { if username != "" { - user, err := getUserFromPath(root, func(u user.User) bool { + user, err := UserFromPath(root, func(u user.User) bool { return u.Name == username }) if err != nil { @@ -501,7 +563,7 @@ func WithUser(userstr string) SpecOpts { uid = uint32(user.Uid) } if groupname != "" { - gid, err = getGIDFromPath(root, func(g user.Group) bool { + gid, err = GIDFromPath(root, func(g user.Group) bool { return g.Name == groupname }) if err != nil { @@ -556,11 +618,11 @@ func WithUserID(uid uint32) SpecOpts { if !isRootfsAbs(s.Root.Path) { return errors.Errorf("rootfs absolute path is required") } - user, err := getUserFromPath(s.Root.Path, func(u user.User) bool { + user, err := UserFromPath(s.Root.Path, func(u user.User) bool { return u.Uid == int(uid) }) if err != nil { - if os.IsNotExist(err) || err == errNoUsersFound { + if os.IsNotExist(err) || err == ErrNoUsersFound { s.Process.User.UID, s.Process.User.GID = uid, 0 return nil } @@ -582,11 +644,11 @@ func WithUserID(uid uint32) SpecOpts { return err } return mount.WithTempMount(ctx, mounts, func(root string) error { - user, err := getUserFromPath(root, func(u user.User) bool { + user, err := UserFromPath(root, func(u user.User) bool { return u.Uid == int(uid) }) if err != nil { - if os.IsNotExist(err) || err == errNoUsersFound { + if os.IsNotExist(err) || err == ErrNoUsersFound { s.Process.User.UID, s.Process.User.GID = uid, 0 return nil } @@ -599,7 +661,7 @@ func WithUserID(uid uint32) SpecOpts { } // WithUsername sets the correct UID and GID for the container -// based on the the image's /etc/passwd contents. If /etc/passwd +// based on the image's /etc/passwd contents. If /etc/passwd // does not exist, or the username is not found in /etc/passwd, // it returns error. func WithUsername(username string) SpecOpts { @@ -610,7 +672,7 @@ func WithUsername(username string) SpecOpts { if !isRootfsAbs(s.Root.Path) { return errors.Errorf("rootfs absolute path is required") } - user, err := getUserFromPath(s.Root.Path, func(u user.User) bool { + user, err := UserFromPath(s.Root.Path, func(u user.User) bool { return u.Name == username }) if err != nil { @@ -631,7 +693,7 @@ func WithUsername(username string) SpecOpts { return err } return mount.WithTempMount(ctx, mounts, func(root string) error { - user, err := getUserFromPath(root, func(u user.User) bool { + user, err := UserFromPath(root, func(u user.User) bool { return u.Name == username }) if err != nil { @@ -663,11 +725,11 @@ func WithAdditionalGIDs(userstr string) SpecOpts { var username string uid, err := strconv.Atoi(userstr) if err == nil { - user, err := getUserFromPath(root, func(u user.User) bool { + user, err := UserFromPath(root, func(u user.User) bool { return u.Uid == uid }) if err != nil { - if os.IsNotExist(err) || err == errNoUsersFound { + if os.IsNotExist(err) || err == ErrNoUsersFound { return nil } return err @@ -732,23 +794,62 @@ func WithCapabilities(caps []string) SpecOpts { } } -// WithAllCapabilities sets all linux capabilities for the process -var WithAllCapabilities = WithCapabilities(getAllCapabilities()) - -func getAllCapabilities() []string { - last := capability.CAP_LAST_CAP - // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap - if last == capability.Cap(63) { - last = capability.CAP_BLOCK_SUSPEND +func capsContain(caps []string, s string) bool { + for _, c := range caps { + if c == s { + return true + } } - var caps []string - for _, cap := range capability.List() { - if cap > last { + return false +} + +func removeCap(caps *[]string, s string) { + var newcaps []string + for _, c := range *caps { + if c == s { continue } - caps = append(caps, "CAP_"+strings.ToUpper(cap.String())) + newcaps = append(newcaps, c) + } + *caps = newcaps +} + +// WithAddedCapabilities adds the provided capabilities +func WithAddedCapabilities(caps []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setCapabilities(s) + for _, c := range caps { + for _, cl := range []*[]string{ + &s.Process.Capabilities.Bounding, + &s.Process.Capabilities.Effective, + &s.Process.Capabilities.Permitted, + &s.Process.Capabilities.Inheritable, + } { + if !capsContain(*cl, c) { + *cl = append(*cl, c) + } + } + } + return nil + } +} + +// WithDroppedCapabilities removes the provided capabilities +func WithDroppedCapabilities(caps []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setCapabilities(s) + for _, c := range caps { + for _, cl := range []*[]string{ + &s.Process.Capabilities.Bounding, + &s.Process.Capabilities.Effective, + &s.Process.Capabilities.Permitted, + &s.Process.Capabilities.Inheritable, + } { + removeCap(cl, c) + } + } + return nil } - return caps } // WithAmbientCapabilities set the Linux ambient capabilities for the process @@ -763,9 +864,12 @@ func WithAmbientCapabilities(caps []string) SpecOpts { } } -var errNoUsersFound = errors.New("no users found") +// ErrNoUsersFound can be returned from UserFromPath +var ErrNoUsersFound = errors.New("no users found") -func getUserFromPath(root string, filter func(user.User) bool) (user.User, error) { +// UserFromPath inspects the user object using /etc/passwd in the specified rootfs. +// filter can be nil. +func UserFromPath(root string, filter func(user.User) bool) (user.User, error) { ppath, err := fs.RootPath(root, "/etc/passwd") if err != nil { return user.User{}, err @@ -775,14 +879,17 @@ func getUserFromPath(root string, filter func(user.User) bool) (user.User, error return user.User{}, err } if len(users) == 0 { - return user.User{}, errNoUsersFound + return user.User{}, ErrNoUsersFound } return users[0], nil } -var errNoGroupsFound = errors.New("no groups found") +// ErrNoGroupsFound can be returned from GIDFromPath +var ErrNoGroupsFound = errors.New("no groups found") -func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) { +// GIDFromPath inspects the GID using /etc/passwd in the specified rootfs. +// filter can be nil. +func GIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) { gpath, err := fs.RootPath(root, "/etc/group") if err != nil { return 0, err @@ -792,7 +899,7 @@ func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err return 0, err } if len(groups) == 0 { - return 0, errNoGroupsFound + return 0, ErrNoGroupsFound } g := groups[0] return uint32(g.Gid), nil @@ -842,16 +949,13 @@ func WithReadonlyPaths(paths []string) SpecOpts { // WithWriteableSysfs makes any sysfs mounts writeable func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - for i, m := range s.Mounts { + for _, m := range s.Mounts { if m.Type == "sysfs" { - var options []string - for _, o := range m.Options { + for i, o := range m.Options { if o == "ro" { - o = "rw" + m.Options[i] = "rw" } - options = append(options, o) } - s.Mounts[i].Options = options } } return nil @@ -859,16 +963,13 @@ func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s // WithWriteableCgroupfs makes any cgroup mounts writeable func WithWriteableCgroupfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - for i, m := range s.Mounts { + for _, m := range s.Mounts { if m.Type == "cgroup" { - var options []string - for _, o := range m.Options { + for i, o := range m.Options { if o == "ro" { - o = "rw" + m.Options[i] = "rw" } - options = append(options, o) } - s.Mounts[i].Options = options } } return nil @@ -910,6 +1011,21 @@ func WithParentCgroupDevices(_ context.Context, _ Client, _ *containers.Containe return nil } +// WithAllDevicesAllowed permits READ WRITE MKNOD on all devices nodes for the container +func WithAllDevicesAllowed(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + if s.Linux.Resources == nil { + s.Linux.Resources = &specs.LinuxResources{} + } + s.Linux.Resources.Devices = []specs.LinuxDeviceCgroup{ + { + Allow: true, + Access: rwm, + }, + } + return nil +} + // WithDefaultUnixDevices adds the default devices for unix such as /dev/null, /dev/random to // the container's resource cgroup spec func WithDefaultUnixDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { @@ -1004,9 +1120,8 @@ func WithDefaultUnixDevices(_ context.Context, _ Client, _ *containers.Container } // WithPrivileged sets up options for a privileged container -// TODO(justincormack) device handling var WithPrivileged = Compose( - WithAllCapabilities, + WithAllCurrentCapabilities, WithMaskedPaths(nil), WithReadonlyPaths(nil), WithWriteableSysfs, @@ -1026,3 +1141,126 @@ func WithWindowsHyperV(_ context.Context, _ Client, _ *containers.Container, s * } return nil } + +// WithMemoryLimit sets the `Linux.LinuxResources.Memory.Limit` section to the +// `limit` specified if the `Linux` section is not `nil`. Additionally sets the +// `Windows.WindowsResources.Memory.Limit` section if the `Windows` section is +// not `nil`. +func WithMemoryLimit(limit uint64) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + if s.Linux != nil { + if s.Linux.Resources == nil { + s.Linux.Resources = &specs.LinuxResources{} + } + if s.Linux.Resources.Memory == nil { + s.Linux.Resources.Memory = &specs.LinuxMemory{} + } + l := int64(limit) + s.Linux.Resources.Memory.Limit = &l + } + if s.Windows != nil { + if s.Windows.Resources == nil { + s.Windows.Resources = &specs.WindowsResources{} + } + if s.Windows.Resources.Memory == nil { + s.Windows.Resources.Memory = &specs.WindowsMemoryResources{} + } + s.Windows.Resources.Memory.Limit = &limit + } + return nil + } +} + +// WithAnnotations appends or replaces the annotations on the spec with the +// provided annotations +func WithAnnotations(annotations map[string]string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + if s.Annotations == nil { + s.Annotations = make(map[string]string) + } + for k, v := range annotations { + s.Annotations[k] = v + } + return nil + } +} + +// WithLinuxDevices adds the provided linux devices to the spec +func WithLinuxDevices(devices []specs.LinuxDevice) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + s.Linux.Devices = append(s.Linux.Devices, devices...) + return nil + } +} + +// WithLinuxDevice adds the device specified by path to the spec +func WithLinuxDevice(path, permissions string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + setResources(s) + + dev, err := deviceFromPath(path) + if err != nil { + return err + } + + s.Linux.Devices = append(s.Linux.Devices, *dev) + + s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, specs.LinuxDeviceCgroup{ + Type: dev.Type, + Allow: true, + Major: &dev.Major, + Minor: &dev.Minor, + Access: permissions, + }) + + return nil + } +} + +// WithEnvFile adds environment variables from a file to the container's spec +func WithEnvFile(path string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + var vars []string + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + + sc := bufio.NewScanner(f) + for sc.Scan() { + vars = append(vars, sc.Text()) + } + if err = sc.Err(); err != nil { + return err + } + return WithEnv(vars)(nil, nil, nil, s) + } +} + +// ErrNoShmMount is returned when there is no /dev/shm mount specified in the config +// and an Opts was trying to set a configuration value on the mount. +var ErrNoShmMount = errors.New("no /dev/shm mount specified") + +// WithDevShmSize sets the size of the /dev/shm mount for the container. +// +// The size value is specified in kb, kilobytes. +func WithDevShmSize(kb int64) SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + for _, m := range s.Mounts { + if m.Source == "shm" && m.Type == "tmpfs" { + for i, o := range m.Options { + if strings.HasPrefix(o, "size=") { + m.Options[i] = fmt.Sprintf("size=%dk", kb) + return nil + } + } + m.Options = append(m.Options, fmt.Sprintf("size=%dk", kb)) + return nil + } + } + return ErrNoShmMount + } +} diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_linux.go b/vendor/github.com/containerd/containerd/oci/spec_opts_linux.go new file mode 100644 index 0000000000000..ae8c0a7d8f985 --- /dev/null +++ b/vendor/github.com/containerd/containerd/oci/spec_opts_linux.go @@ -0,0 +1,145 @@ +// +build linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package oci + +import ( + "context" + + "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/pkg/cap" + specs "github.com/opencontainers/runtime-spec/specs-go" +) + +// WithHostDevices adds all the hosts device nodes to the container's spec +func WithHostDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + + devs, err := HostDevices() + if err != nil { + return err + } + s.Linux.Devices = append(s.Linux.Devices, devs...) + return nil +} + +// WithDevices recursively adds devices from the passed in path and associated cgroup rules for that device. +// If devicePath is a dir it traverses the dir to add all devices in that dir. +// If devicePath is not a dir, it attempts to add the single device. +// If containerPath is not set then the device path is used for the container path. +func WithDevices(devicePath, containerPath, permissions string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + devs, err := getDevices(devicePath, containerPath) + if err != nil { + return err + } + for i := range devs { + s.Linux.Devices = append(s.Linux.Devices, devs[i]) + s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, specs.LinuxDeviceCgroup{ + Allow: true, + Type: devs[i].Type, + Major: &devs[i].Major, + Minor: &devs[i].Minor, + Access: permissions, + }) + } + return nil + } +} + +// WithMemorySwap sets the container's swap in bytes +func WithMemorySwap(swap int64) SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + setResources(s) + if s.Linux.Resources.Memory == nil { + s.Linux.Resources.Memory = &specs.LinuxMemory{} + } + s.Linux.Resources.Memory.Swap = &swap + return nil + } +} + +// WithPidsLimit sets the container's pid limit or maximum +func WithPidsLimit(limit int64) SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + setResources(s) + if s.Linux.Resources.Pids == nil { + s.Linux.Resources.Pids = &specs.LinuxPids{} + } + s.Linux.Resources.Pids.Limit = limit + return nil + } +} + +// WithCPUShares sets the container's cpu shares +func WithCPUShares(shares uint64) SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + setCPU(s) + s.Linux.Resources.CPU.Shares = &shares + return nil + } +} + +// WithCPUs sets the container's cpus/cores for use by the container +func WithCPUs(cpus string) SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + setCPU(s) + s.Linux.Resources.CPU.Cpus = cpus + return nil + } +} + +// WithCPUsMems sets the container's cpu mems for use by the container +func WithCPUsMems(mems string) SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + setCPU(s) + s.Linux.Resources.CPU.Mems = mems + return nil + } +} + +// WithCPUCFS sets the container's Completely fair scheduling (CFS) quota and period +func WithCPUCFS(quota int64, period uint64) SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + setCPU(s) + s.Linux.Resources.CPU.Quota = "a + s.Linux.Resources.CPU.Period = &period + return nil + } +} + +// WithAllCurrentCapabilities propagates the effective capabilities of the caller process to the container process. +// The capability set may differ from WithAllKnownCapabilities when running in a container. +var WithAllCurrentCapabilities = func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { + caps, err := cap.Current() + if err != nil { + return err + } + return WithCapabilities(caps)(ctx, client, c, s) +} + +// WithAllKnownCapabilities sets all the the known linux capabilities for the container process +var WithAllKnownCapabilities = func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { + caps := cap.Known() + return WithCapabilities(caps)(ctx, client, c, s) +} + +// WithoutRunMount removes the `/run` inside the spec +func WithoutRunMount(ctx context.Context, client Client, c *containers.Container, s *Spec) error { + return WithoutMounts("/run")(ctx, client, c, s) +} diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_nonlinux.go b/vendor/github.com/containerd/containerd/oci/spec_opts_nonlinux.go new file mode 100644 index 0000000000000..77a1636387dcc --- /dev/null +++ b/vendor/github.com/containerd/containerd/oci/spec_opts_nonlinux.go @@ -0,0 +1,38 @@ +// +build !linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package oci + +import ( + "context" + + "github.com/containerd/containerd/containers" +) + +// WithAllCurrentCapabilities propagates the effective capabilities of the caller process to the container process. +// The capability set may differ from WithAllKnownCapabilities when running in a container. +//nolint: deadcode, unused +var WithAllCurrentCapabilities = func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { + return WithCapabilities(nil)(ctx, client, c, s) +} + +// WithAllKnownCapabilities sets all the the known linux capabilities for the container process +//nolint: deadcode, unused +var WithAllKnownCapabilities = func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { + return WithCapabilities(nil)(ctx, client, c, s) +} diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go b/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go new file mode 100644 index 0000000000000..80a522356761c --- /dev/null +++ b/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go @@ -0,0 +1,58 @@ +// +build !linux,!windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package oci + +import ( + "context" + + "github.com/containerd/containerd/containers" +) + +// WithHostDevices adds all the hosts device nodes to the container's spec +func WithHostDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + + devs, err := HostDevices() + if err != nil { + return err + } + s.Linux.Devices = append(s.Linux.Devices, devs...) + return nil +} + +// WithDevices recursively adds devices from the passed in path and associated cgroup rules for that device. +// If devicePath is a dir it traverses the dir to add all devices in that dir. +// If devicePath is not a dir, it attempts to add the single device. +func WithDevices(devicePath, containerPath, permissions string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + devs, err := getDevices(devicePath, containerPath) + if err != nil { + return err + } + s.Linux.Devices = append(s.Linux.Devices, devs...) + return nil + } +} + +// WithCPUCFS sets the container's Completely fair scheduling (CFS) quota and period +func WithCPUCFS(quota int64, period uint64) SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + return nil + } +} diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go b/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go new file mode 100644 index 0000000000000..126a89ec83a96 --- /dev/null +++ b/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go @@ -0,0 +1,79 @@ +// +build windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package oci + +import ( + "context" + + "github.com/containerd/containerd/containers" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +// WithWindowsCPUCount sets the `Windows.Resources.CPU.Count` section to the +// `count` specified. +func WithWindowsCPUCount(count uint64) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + if s.Windows.Resources == nil { + s.Windows.Resources = &specs.WindowsResources{} + } + if s.Windows.Resources.CPU == nil { + s.Windows.Resources.CPU = &specs.WindowsCPUResources{} + } + s.Windows.Resources.CPU.Count = &count + return nil + } +} + +// WithWindowsIgnoreFlushesDuringBoot sets `Windows.IgnoreFlushesDuringBoot`. +func WithWindowsIgnoreFlushesDuringBoot() SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + if s.Windows == nil { + s.Windows = &specs.Windows{} + } + s.Windows.IgnoreFlushesDuringBoot = true + return nil + } +} + +// WithWindowNetworksAllowUnqualifiedDNSQuery sets `Windows.Network.AllowUnqualifiedDNSQuery`. +func WithWindowNetworksAllowUnqualifiedDNSQuery() SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + if s.Windows == nil { + s.Windows = &specs.Windows{} + } + if s.Windows.Network == nil { + s.Windows.Network = &specs.WindowsNetwork{} + } + + s.Windows.Network.AllowUnqualifiedDNSQuery = true + return nil + } +} + +// WithHostDevices adds all the hosts device nodes to the container's spec +// +// Not supported on windows +func WithHostDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + return nil +} + +func deviceFromPath(path string) (*specs.LinuxDevice, error) { + return nil, errors.New("device from path not supported on Windows") +} diff --git a/vendor/github.com/containerd/containerd/oci/utils_unix.go b/vendor/github.com/containerd/containerd/oci/utils_unix.go new file mode 100644 index 0000000000000..108cacf5b8bfd --- /dev/null +++ b/vendor/github.com/containerd/containerd/oci/utils_unix.go @@ -0,0 +1,137 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package oci + +import ( + "io/ioutil" + "os" + "path/filepath" + + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +var errNotADevice = errors.New("not a device node") + +// HostDevices returns all devices that can be found under /dev directory. +func HostDevices() ([]specs.LinuxDevice, error) { + return getDevices("/dev", "") +} + +func getDevices(path, containerPath string) ([]specs.LinuxDevice, error) { + stat, err := os.Stat(path) + if err != nil { + return nil, errors.Wrap(err, "error stating device path") + } + + if !stat.IsDir() { + dev, err := deviceFromPath(path) + if err != nil { + return nil, err + } + if containerPath != "" { + dev.Path = containerPath + } + return []specs.LinuxDevice{*dev}, nil + } + + files, err := ioutil.ReadDir(path) + if err != nil { + return nil, err + } + var out []specs.LinuxDevice + for _, f := range files { + switch { + case f.IsDir(): + switch f.Name() { + // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825 + // ".udev" added to address https://github.com/opencontainers/runc/issues/2093 + case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts", ".udev": + continue + default: + var cp string + if containerPath != "" { + cp = filepath.Join(containerPath, filepath.Base(f.Name())) + } + sub, err := getDevices(filepath.Join(path, f.Name()), cp) + if err != nil { + return nil, err + } + + out = append(out, sub...) + continue + } + case f.Name() == "console": + continue + } + device, err := deviceFromPath(filepath.Join(path, f.Name())) + if err != nil { + if err == errNotADevice { + continue + } + if os.IsNotExist(err) { + continue + } + return nil, err + } + if containerPath != "" { + device.Path = filepath.Join(containerPath, filepath.Base(f.Name())) + } + out = append(out, *device) + } + return out, nil +} + +func deviceFromPath(path string) (*specs.LinuxDevice, error) { + var stat unix.Stat_t + if err := unix.Lstat(path, &stat); err != nil { + return nil, err + } + + var ( + devNumber = uint64(stat.Rdev) //nolint: unconvert // the type is 32bit on mips. + major = unix.Major(devNumber) + minor = unix.Minor(devNumber) + ) + if major == 0 { + return nil, errNotADevice + } + + var ( + devType string + mode = stat.Mode + ) + switch { + case mode&unix.S_IFBLK == unix.S_IFBLK: + devType = "b" + case mode&unix.S_IFCHR == unix.S_IFCHR: + devType = "c" + } + fm := os.FileMode(mode &^ unix.S_IFMT) + return &specs.LinuxDevice{ + Type: devType, + Path: path, + Major: int64(major), + Minor: int64(minor), + FileMode: &fm, + UID: &stat.Uid, + GID: &stat.Gid, + }, nil +} diff --git a/vendor/github.com/containerd/containerd/pkg/apparmor/apparmor.go b/vendor/github.com/containerd/containerd/pkg/apparmor/apparmor.go new file mode 100644 index 0000000000000..dd4d860c0ee6d --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/apparmor/apparmor.go @@ -0,0 +1,27 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package apparmor + +// HostSupports returns true if apparmor is enabled for the host, // On non-Linux returns false +// On Linux returns true if apparmor_parser is enabled, and if we +// are not running docker-in-docker. +// +// It is a modified version of libcontainer/apparmor.IsEnabled(), which does not +// check for apparmor_parser to be present, or if we're running docker-in-docker. +func HostSupports() bool { + return hostSupports() +} diff --git a/vendor/github.com/containerd/containerd/pkg/apparmor/apparmor_linux.go b/vendor/github.com/containerd/containerd/pkg/apparmor/apparmor_linux.go new file mode 100644 index 0000000000000..ee3858583e838 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/apparmor/apparmor_linux.go @@ -0,0 +1,48 @@ +// +build linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package apparmor + +import ( + "io/ioutil" + "os" + "sync" +) + +var ( + appArmorSupported bool + checkAppArmor sync.Once +) + +// hostSupports returns true if apparmor is enabled for the host, if +// apparmor_parser is enabled, and if we are not running docker-in-docker. +// +// It is a modified version of libcontainer/apparmor.IsEnabled(), which does not +// check for apparmor_parser to be present, or if we're running docker-in-docker. +func hostSupports() bool { + checkAppArmor.Do(func() { + // see https://github.com/docker/docker/commit/de191e86321f7d3136ff42ff75826b8107399497 + if _, err := os.Stat("/sys/kernel/security/apparmor"); err == nil && os.Getenv("container") == "" { + if _, err = os.Stat("/sbin/apparmor_parser"); err == nil { + buf, err := ioutil.ReadFile("/sys/module/apparmor/parameters/enabled") + appArmorSupported = err == nil && len(buf) > 1 && buf[0] == 'Y' + } + } + }) + return appArmorSupported +} diff --git a/vendor/github.com/containerd/containerd/pkg/apparmor/apparmor_unsupported.go b/vendor/github.com/containerd/containerd/pkg/apparmor/apparmor_unsupported.go new file mode 100644 index 0000000000000..428d36424d591 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/apparmor/apparmor_unsupported.go @@ -0,0 +1,23 @@ +// +build !linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package apparmor + +func hostSupports() bool { + return false +} diff --git a/vendor/github.com/containerd/containerd/pkg/cap/cap_linux.go b/vendor/github.com/containerd/containerd/pkg/cap/cap_linux.go new file mode 100644 index 0000000000000..35772a4d857d1 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/cap/cap_linux.go @@ -0,0 +1,192 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +// Package cap provides Linux capability utility +package cap + +import ( + "bufio" + "io" + "os" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +// FromNumber returns a cap string like "CAP_SYS_ADMIN" +// that corresponds to the given number like 21. +// +// FromNumber returns an empty string for unknown cap number. +func FromNumber(num int) string { + if num < 0 || num > len(capsLatest)-1 { + return "" + } + return capsLatest[num] +} + +// FromBitmap parses an uint64 bitmap into string slice like +// []{"CAP_SYS_ADMIN", ...}. +// +// Unknown cap numbers are returned as []int. +func FromBitmap(v uint64) ([]string, []int) { + var ( + res []string + unknown []int + ) + for i := 0; i <= 63; i++ { + if b := (v >> i) & 0x1; b == 0x1 { + if s := FromNumber(i); s != "" { + res = append(res, s) + } else { + unknown = append(unknown, i) + } + } + } + return res, unknown +} + +// Type is the type of capability +type Type int + +const ( + // Effective is CapEff + Effective Type = 1 << iota + // Permitted is CapPrm + Permitted + // Inheritable is CapInh + Inheritable + // Bounding is CapBnd + Bounding + // Ambient is CapAmb + Ambient +) + +// ParseProcPIDStatus returns uint64 bitmap value from /proc//status file +func ParseProcPIDStatus(r io.Reader) (map[Type]uint64, error) { + res := make(map[Type]uint64) + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := scanner.Text() + pair := strings.SplitN(line, ":", 2) + if len(pair) != 2 { + continue + } + k := strings.TrimSpace(pair[0]) + v := strings.TrimSpace(pair[1]) + switch k { + case "CapInh", "CapPrm", "CapEff", "CapBnd", "CapAmb": + ui64, err := strconv.ParseUint(v, 16, 64) + if err != nil { + return nil, errors.Errorf("failed to parse line %q", line) + } + switch k { + case "CapInh": + res[Inheritable] = ui64 + case "CapPrm": + res[Permitted] = ui64 + case "CapEff": + res[Effective] = ui64 + case "CapBnd": + res[Bounding] = ui64 + case "CapAmb": + res[Ambient] = ui64 + } + } + } + if err := scanner.Err(); err != nil { + return nil, err + } + return res, nil +} + +// Current returns the list of the effective and the known caps of +// the current process. +// +// The result is like []string{"CAP_SYS_ADMIN", ...}. +// +// The result does not contain caps that are not recognized by +// the "github.com/syndtr/gocapability" library. +func Current() ([]string, error) { + f, err := os.Open("/proc/self/status") + if err != nil { + return nil, err + } + defer f.Close() + caps, err := ParseProcPIDStatus(f) + if err != nil { + return nil, err + } + capEff := caps[Effective] + names, _ := FromBitmap(capEff) + return names, nil +} + +var ( + // caps35 is the caps of kernel 3.5 (37 entries) + caps35 = []string{ + "CAP_CHOWN", // 2.2 + "CAP_DAC_OVERRIDE", // 2.2 + "CAP_DAC_READ_SEARCH", // 2.2 + "CAP_FOWNER", // 2.2 + "CAP_FSETID", // 2.2 + "CAP_KILL", // 2.2 + "CAP_SETGID", // 2.2 + "CAP_SETUID", // 2.2 + "CAP_SETPCAP", // 2.2 + "CAP_LINUX_IMMUTABLE", // 2.2 + "CAP_NET_BIND_SERVICE", // 2.2 + "CAP_NET_BROADCAST", // 2.2 + "CAP_NET_ADMIN", // 2.2 + "CAP_NET_RAW", // 2.2 + "CAP_IPC_LOCK", // 2.2 + "CAP_IPC_OWNER", // 2.2 + "CAP_SYS_MODULE", // 2.2 + "CAP_SYS_RAWIO", // 2.2 + "CAP_SYS_CHROOT", // 2.2 + "CAP_SYS_PTRACE", // 2.2 + "CAP_SYS_PACCT", // 2.2 + "CAP_SYS_ADMIN", // 2.2 + "CAP_SYS_BOOT", // 2.2 + "CAP_SYS_NICE", // 2.2 + "CAP_SYS_RESOURCE", // 2.2 + "CAP_SYS_TIME", // 2.2 + "CAP_SYS_TTY_CONFIG", // 2.2 + "CAP_MKNOD", // 2.4 + "CAP_LEASE", // 2.4 + "CAP_AUDIT_WRITE", // 2.6.11 + "CAP_AUDIT_CONTROL", // 2.6.11 + "CAP_SETFCAP", // 2.6.24 + "CAP_MAC_OVERRIDE", // 2.6.25 + "CAP_MAC_ADMIN", // 2.6.25 + "CAP_SYSLOG", // 2.6.37 + "CAP_WAKE_ALARM", // 3.0 + "CAP_BLOCK_SUSPEND", // 3.5 + } + // caps316 is the caps of kernel 3.16 (38 entries) + caps316 = append(caps35, "CAP_AUDIT_READ") + // caps58 is the caps of kernel 5.8 (40 entries) + caps58 = append(caps316, []string{"CAP_PERFMON", "CAP_BPF"}...) + // caps59 is the caps of kernel 5.9 (41 entries) + caps59 = append(caps58, "CAP_CHECKPOINT_RESTORE") + capsLatest = caps59 +) + +// Known returns the known cap strings of the latest kernel. +// The current latest kernel is 5.9. +func Known() []string { + return capsLatest +} diff --git a/vendor/github.com/containerd/containerd/pkg/dialer/dialer.go b/vendor/github.com/containerd/containerd/pkg/dialer/dialer.go index 766d34493445c..aa604baab9217 100644 --- a/vendor/github.com/containerd/containerd/pkg/dialer/dialer.go +++ b/vendor/github.com/containerd/containerd/pkg/dialer/dialer.go @@ -17,6 +17,7 @@ package dialer import ( + "context" "net" "time" @@ -28,8 +29,19 @@ type dialResult struct { err error } +// ContextDialer returns a GRPC net.Conn connected to the provided address +func ContextDialer(ctx context.Context, address string) (net.Conn, error) { + if deadline, ok := ctx.Deadline(); ok { + return timeoutDialer(address, time.Until(deadline)) + } + return timeoutDialer(address, 0) +} + // Dialer returns a GRPC net.Conn connected to the provided address -func Dialer(address string, timeout time.Duration) (net.Conn, error) { +// Deprecated: use ContextDialer and grpc.WithContextDialer. +var Dialer = timeoutDialer + +func timeoutDialer(address string, timeout time.Duration) (net.Conn, error) { var ( stopC = make(chan struct{}) synC = make(chan *dialResult) diff --git a/vendor/github.com/containerd/containerd/pkg/dialer/dialer_windows.go b/vendor/github.com/containerd/containerd/pkg/dialer/dialer_windows.go index 64d30dea0cd08..4dd296ebc3e59 100644 --- a/vendor/github.com/containerd/containerd/pkg/dialer/dialer_windows.go +++ b/vendor/github.com/containerd/containerd/pkg/dialer/dialer_windows.go @@ -19,21 +19,13 @@ package dialer import ( "net" "os" - "syscall" "time" winio "github.com/Microsoft/go-winio" ) func isNoent(err error) bool { - if err != nil { - if oerr, ok := err.(*os.PathError); ok { - if oerr.Err == syscall.ENOENT { - return true - } - } - } - return false + return os.IsNotExist(err) } func dialer(address string, timeout time.Duration) (net.Conn, error) { diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/deleted_state.go b/vendor/github.com/containerd/containerd/pkg/process/deleted_state.go similarity index 84% rename from vendor/github.com/containerd/containerd/runtime/v1/linux/proc/deleted_state.go rename to vendor/github.com/containerd/containerd/pkg/process/deleted_state.go index ab89c3ecbaf68..eb7baf737cf8a 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/deleted_state.go +++ b/vendor/github.com/containerd/containerd/pkg/process/deleted_state.go @@ -16,13 +16,13 @@ limitations under the License. */ -package proc +package process import ( "context" "github.com/containerd/console" - "github.com/containerd/containerd/runtime/proc" + "github.com/containerd/containerd/errdefs" google_protobuf "github.com/gogo/protobuf/types" "github.com/pkg/errors" ) @@ -55,17 +55,21 @@ func (s *deletedState) Start(ctx context.Context) error { } func (s *deletedState) Delete(ctx context.Context) error { - return errors.Errorf("cannot delete a deleted process") + return errors.Wrap(errdefs.ErrNotFound, "cannot delete a deleted process") } func (s *deletedState) Kill(ctx context.Context, sig uint32, all bool) error { - return errors.Errorf("cannot kill a deleted process") + return errors.Wrap(errdefs.ErrNotFound, "cannot kill a deleted process") } func (s *deletedState) SetExited(status int) { // no op } -func (s *deletedState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { +func (s *deletedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { return nil, errors.Errorf("cannot exec in a deleted state") } + +func (s *deletedState) Status(ctx context.Context) (string, error) { + return "stopped", nil +} diff --git a/vendor/github.com/containerd/containerd/pkg/process/exec.go b/vendor/github.com/containerd/containerd/pkg/process/exec.go new file mode 100644 index 0000000000000..7790a4979f7ac --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/process/exec.go @@ -0,0 +1,264 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package process + +import ( + "context" + "fmt" + "io" + "os" + "path/filepath" + "sync" + "syscall" + "time" + + "golang.org/x/sys/unix" + + "github.com/containerd/console" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/pkg/stdio" + "github.com/containerd/fifo" + runc "github.com/containerd/go-runc" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +type execProcess struct { + wg sync.WaitGroup + + execState execState + + mu sync.Mutex + id string + console console.Console + io *processIO + status int + exited time.Time + pid safePid + closers []io.Closer + stdin io.Closer + stdio stdio.Stdio + path string + spec specs.Process + + parent *Init + waitBlock chan struct{} +} + +func (e *execProcess) Wait() { + <-e.waitBlock +} + +func (e *execProcess) ID() string { + return e.id +} + +func (e *execProcess) Pid() int { + return e.pid.get() +} + +func (e *execProcess) ExitStatus() int { + e.mu.Lock() + defer e.mu.Unlock() + return e.status +} + +func (e *execProcess) ExitedAt() time.Time { + e.mu.Lock() + defer e.mu.Unlock() + return e.exited +} + +func (e *execProcess) SetExited(status int) { + e.mu.Lock() + defer e.mu.Unlock() + + e.execState.SetExited(status) +} + +func (e *execProcess) setExited(status int) { + e.status = status + e.exited = time.Now() + e.parent.Platform.ShutdownConsole(context.Background(), e.console) + close(e.waitBlock) +} + +func (e *execProcess) Delete(ctx context.Context) error { + e.mu.Lock() + defer e.mu.Unlock() + + return e.execState.Delete(ctx) +} + +func (e *execProcess) delete(ctx context.Context) error { + waitTimeout(ctx, &e.wg, 2*time.Second) + if e.io != nil { + for _, c := range e.closers { + c.Close() + } + e.io.Close() + } + pidfile := filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id)) + // silently ignore error + os.Remove(pidfile) + return nil +} + +func (e *execProcess) Resize(ws console.WinSize) error { + e.mu.Lock() + defer e.mu.Unlock() + + return e.execState.Resize(ws) +} + +func (e *execProcess) resize(ws console.WinSize) error { + if e.console == nil { + return nil + } + return e.console.Resize(ws) +} + +func (e *execProcess) Kill(ctx context.Context, sig uint32, _ bool) error { + e.mu.Lock() + defer e.mu.Unlock() + + return e.execState.Kill(ctx, sig, false) +} + +func (e *execProcess) kill(ctx context.Context, sig uint32, _ bool) error { + pid := e.pid.get() + switch { + case pid == 0: + return errors.Wrap(errdefs.ErrFailedPrecondition, "process not created") + case !e.exited.IsZero(): + return errors.Wrapf(errdefs.ErrNotFound, "process already finished") + default: + if err := unix.Kill(pid, syscall.Signal(sig)); err != nil { + return errors.Wrapf(checkKillError(err), "exec kill error") + } + } + return nil +} + +func (e *execProcess) Stdin() io.Closer { + return e.stdin +} + +func (e *execProcess) Stdio() stdio.Stdio { + return e.stdio +} + +func (e *execProcess) Start(ctx context.Context) error { + e.mu.Lock() + defer e.mu.Unlock() + + return e.execState.Start(ctx) +} + +func (e *execProcess) start(ctx context.Context) (err error) { + // The reaper may receive exit signal right after + // the container is started, before the e.pid is updated. + // In that case, we want to block the signal handler to + // access e.pid until it is updated. + e.pid.Lock() + defer e.pid.Unlock() + + var ( + socket *runc.Socket + pio *processIO + pidFile = newExecPidFile(e.path, e.id) + ) + if e.stdio.Terminal { + if socket, err = runc.NewTempConsoleSocket(); err != nil { + return errors.Wrap(err, "failed to create runc console socket") + } + defer socket.Close() + } else { + if pio, err = createIO(ctx, e.id, e.parent.IoUID, e.parent.IoGID, e.stdio); err != nil { + return errors.Wrap(err, "failed to create init process I/O") + } + e.io = pio + } + opts := &runc.ExecOpts{ + PidFile: pidFile.Path(), + Detach: true, + } + if pio != nil { + opts.IO = pio.IO() + } + if socket != nil { + opts.ConsoleSocket = socket + } + if err := e.parent.runtime.Exec(ctx, e.parent.id, e.spec, opts); err != nil { + close(e.waitBlock) + return e.parent.runtimeError(err, "OCI runtime exec failed") + } + if e.stdio.Stdin != "" { + if err := e.openStdin(e.stdio.Stdin); err != nil { + return err + } + } + ctx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + if socket != nil { + console, err := socket.ReceiveMaster() + if err != nil { + return errors.Wrap(err, "failed to retrieve console master") + } + if e.console, err = e.parent.Platform.CopyConsole(ctx, console, e.id, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg); err != nil { + return errors.Wrap(err, "failed to start console copy") + } + } else { + if err := pio.Copy(ctx, &e.wg); err != nil { + return errors.Wrap(err, "failed to start io pipe copy") + } + } + pid, err := pidFile.Read() + if err != nil { + return errors.Wrap(err, "failed to retrieve OCI runtime exec pid") + } + e.pid.pid = pid + return nil +} + +func (e *execProcess) openStdin(path string) error { + sc, err := fifo.OpenFifo(context.Background(), path, syscall.O_WRONLY|syscall.O_NONBLOCK, 0) + if err != nil { + return errors.Wrapf(err, "failed to open stdin fifo %s", path) + } + e.stdin = sc + e.closers = append(e.closers, sc) + return nil +} + +func (e *execProcess) Status(ctx context.Context) (string, error) { + s, err := e.parent.Status(ctx) + if err != nil { + return "", err + } + // if the container as a whole is in the pausing/paused state, so are all + // other processes inside the container, use container state here + switch s { + case "paused", "pausing": + return s, nil + } + e.mu.Lock() + defer e.mu.Unlock() + return e.execState.Status(ctx) +} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec_state.go b/vendor/github.com/containerd/containerd/pkg/process/exec_state.go similarity index 81% rename from vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec_state.go rename to vendor/github.com/containerd/containerd/pkg/process/exec_state.go index ac5467552844e..c97b4001b4734 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec_state.go +++ b/vendor/github.com/containerd/containerd/pkg/process/exec_state.go @@ -16,7 +16,7 @@ limitations under the License. */ -package proc +package process import ( "context" @@ -25,6 +25,15 @@ import ( "github.com/pkg/errors" ) +type execState interface { + Resize(console.WinSize) error + Start(context.Context) error + Delete(context.Context) error + Kill(context.Context, uint32, bool) error + SetExited(int) + Status(context.Context) (string, error) +} + type execCreatedState struct { p *execProcess } @@ -32,11 +41,11 @@ type execCreatedState struct { func (s *execCreatedState) transition(name string) error { switch name { case "running": - s.p.State = &execRunningState{p: s.p} + s.p.execState = &execRunningState{p: s.p} case "stopped": - s.p.State = &execStoppedState{p: s.p} + s.p.execState = &execStoppedState{p: s.p} case "deleted": - s.p.State = &deletedState{} + s.p.execState = &deletedState{} default: return errors.Errorf("invalid state transition %q to %q", stateName(s), name) } @@ -44,15 +53,10 @@ func (s *execCreatedState) transition(name string) error { } func (s *execCreatedState) Resize(ws console.WinSize) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return s.p.resize(ws) } func (s *execCreatedState) Start(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() if err := s.p.start(ctx); err != nil { return err } @@ -63,22 +67,15 @@ func (s *execCreatedState) Delete(ctx context.Context) error { if err := s.p.delete(ctx); err != nil { return err } - s.p.mu.Lock() - defer s.p.mu.Unlock() + return s.transition("deleted") } func (s *execCreatedState) Kill(ctx context.Context, sig uint32, all bool) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return s.p.kill(ctx, sig, all) } func (s *execCreatedState) SetExited(status int) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - s.p.setExited(status) if err := s.transition("stopped"); err != nil { @@ -86,6 +83,10 @@ func (s *execCreatedState) SetExited(status int) { } } +func (s *execCreatedState) Status(ctx context.Context) (string, error) { + return "created", nil +} + type execRunningState struct { p *execProcess } @@ -93,7 +94,7 @@ type execRunningState struct { func (s *execRunningState) transition(name string) error { switch name { case "stopped": - s.p.State = &execStoppedState{p: s.p} + s.p.execState = &execStoppedState{p: s.p} default: return errors.Errorf("invalid state transition %q to %q", stateName(s), name) } @@ -101,37 +102,22 @@ func (s *execRunningState) transition(name string) error { } func (s *execRunningState) Resize(ws console.WinSize) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return s.p.resize(ws) } func (s *execRunningState) Start(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return errors.Errorf("cannot start a running process") } func (s *execRunningState) Delete(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return errors.Errorf("cannot delete a running process") } func (s *execRunningState) Kill(ctx context.Context, sig uint32, all bool) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return s.p.kill(ctx, sig, all) } func (s *execRunningState) SetExited(status int) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - s.p.setExited(status) if err := s.transition("stopped"); err != nil { @@ -139,6 +125,10 @@ func (s *execRunningState) SetExited(status int) { } } +func (s *execRunningState) Status(ctx context.Context) (string, error) { + return "running", nil +} + type execStoppedState struct { p *execProcess } @@ -146,7 +136,7 @@ type execStoppedState struct { func (s *execStoppedState) transition(name string) error { switch name { case "deleted": - s.p.State = &deletedState{} + s.p.execState = &deletedState{} default: return errors.Errorf("invalid state transition %q to %q", stateName(s), name) } @@ -154,16 +144,10 @@ func (s *execStoppedState) transition(name string) error { } func (s *execStoppedState) Resize(ws console.WinSize) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return errors.Errorf("cannot resize a stopped container") } func (s *execStoppedState) Start(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return errors.Errorf("cannot start a stopped process") } @@ -171,18 +155,18 @@ func (s *execStoppedState) Delete(ctx context.Context) error { if err := s.p.delete(ctx); err != nil { return err } - s.p.mu.Lock() - defer s.p.mu.Unlock() + return s.transition("deleted") } func (s *execStoppedState) Kill(ctx context.Context, sig uint32, all bool) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return s.p.kill(ctx, sig, all) } func (s *execStoppedState) SetExited(status int) { // no op } + +func (s *execStoppedState) Status(ctx context.Context) (string, error) { + return "stopped", nil +} diff --git a/vendor/github.com/containerd/containerd/pkg/process/init.go b/vendor/github.com/containerd/containerd/pkg/process/init.go new file mode 100644 index 0000000000000..a9462384f2136 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/process/init.go @@ -0,0 +1,498 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package process + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/containerd/console" + "github.com/containerd/containerd/log" + "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/pkg/stdio" + "github.com/containerd/fifo" + runc "github.com/containerd/go-runc" + google_protobuf "github.com/gogo/protobuf/types" + specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// Init represents an initial process for a container +type Init struct { + wg sync.WaitGroup + initState initState + + // mu is used to ensure that `Start()` and `Exited()` calls return in + // the right order when invoked in separate go routines. + // This is the case within the shim implementation as it makes use of + // the reaper interface. + mu sync.Mutex + + waitBlock chan struct{} + + WorkDir string + + id string + Bundle string + console console.Console + Platform stdio.Platform + io *processIO + runtime *runc.Runc + // pausing preserves the pausing state. + pausing *atomicBool + status int + exited time.Time + pid int + closers []io.Closer + stdin io.Closer + stdio stdio.Stdio + Rootfs string + IoUID int + IoGID int + NoPivotRoot bool + NoNewKeyring bool + CriuWorkPath string +} + +// NewRunc returns a new runc instance for a process +func NewRunc(root, path, namespace, runtime, criu string, systemd bool) *runc.Runc { + if root == "" { + root = RuncRoot + } + return &runc.Runc{ + Command: runtime, + Log: filepath.Join(path, "log.json"), + LogFormat: runc.JSON, + PdeathSignal: unix.SIGKILL, + Root: filepath.Join(root, namespace), + Criu: criu, + SystemdCgroup: systemd, + } +} + +// New returns a new process +func New(id string, runtime *runc.Runc, stdio stdio.Stdio) *Init { + p := &Init{ + id: id, + runtime: runtime, + pausing: new(atomicBool), + stdio: stdio, + status: 0, + waitBlock: make(chan struct{}), + } + p.initState = &createdState{p: p} + return p +} + +// Create the process with the provided config +func (p *Init) Create(ctx context.Context, r *CreateConfig) error { + var ( + err error + socket *runc.Socket + pio *processIO + pidFile = newPidFile(p.Bundle) + ) + + if r.Terminal { + if socket, err = runc.NewTempConsoleSocket(); err != nil { + return errors.Wrap(err, "failed to create OCI runtime console socket") + } + defer socket.Close() + } else { + if pio, err = createIO(ctx, p.id, p.IoUID, p.IoGID, p.stdio); err != nil { + return errors.Wrap(err, "failed to create init process I/O") + } + p.io = pio + } + if r.Checkpoint != "" { + return p.createCheckpointedState(r, pidFile) + } + opts := &runc.CreateOpts{ + PidFile: pidFile.Path(), + NoPivot: p.NoPivotRoot, + NoNewKeyring: p.NoNewKeyring, + } + if p.io != nil { + opts.IO = p.io.IO() + } + if socket != nil { + opts.ConsoleSocket = socket + } + if err := p.runtime.Create(ctx, r.ID, r.Bundle, opts); err != nil { + return p.runtimeError(err, "OCI runtime create failed") + } + if r.Stdin != "" { + if err := p.openStdin(r.Stdin); err != nil { + return err + } + } + ctx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + if socket != nil { + console, err := socket.ReceiveMaster() + if err != nil { + return errors.Wrap(err, "failed to retrieve console master") + } + console, err = p.Platform.CopyConsole(ctx, console, p.id, r.Stdin, r.Stdout, r.Stderr, &p.wg) + if err != nil { + return errors.Wrap(err, "failed to start console copy") + } + p.console = console + } else { + if err := pio.Copy(ctx, &p.wg); err != nil { + return errors.Wrap(err, "failed to start io pipe copy") + } + } + pid, err := pidFile.Read() + if err != nil { + return errors.Wrap(err, "failed to retrieve OCI runtime container pid") + } + p.pid = pid + return nil +} + +func (p *Init) openStdin(path string) error { + sc, err := fifo.OpenFifo(context.Background(), path, unix.O_WRONLY|unix.O_NONBLOCK, 0) + if err != nil { + return errors.Wrapf(err, "failed to open stdin fifo %s", path) + } + p.stdin = sc + p.closers = append(p.closers, sc) + return nil +} + +func (p *Init) createCheckpointedState(r *CreateConfig, pidFile *pidFile) error { + opts := &runc.RestoreOpts{ + CheckpointOpts: runc.CheckpointOpts{ + ImagePath: r.Checkpoint, + WorkDir: p.CriuWorkPath, + ParentPath: r.ParentCheckpoint, + }, + PidFile: pidFile.Path(), + NoPivot: p.NoPivotRoot, + Detach: true, + NoSubreaper: true, + } + + if p.io != nil { + opts.IO = p.io.IO() + } + + p.initState = &createdCheckpointState{ + p: p, + opts: opts, + } + return nil +} + +// Wait for the process to exit +func (p *Init) Wait() { + <-p.waitBlock +} + +// ID of the process +func (p *Init) ID() string { + return p.id +} + +// Pid of the process +func (p *Init) Pid() int { + return p.pid +} + +// ExitStatus of the process +func (p *Init) ExitStatus() int { + p.mu.Lock() + defer p.mu.Unlock() + + return p.status +} + +// ExitedAt at time when the process exited +func (p *Init) ExitedAt() time.Time { + p.mu.Lock() + defer p.mu.Unlock() + + return p.exited +} + +// Status of the process +func (p *Init) Status(ctx context.Context) (string, error) { + if p.pausing.get() { + return "pausing", nil + } + + p.mu.Lock() + defer p.mu.Unlock() + + return p.initState.Status(ctx) +} + +// Start the init process +func (p *Init) Start(ctx context.Context) error { + p.mu.Lock() + defer p.mu.Unlock() + + return p.initState.Start(ctx) +} + +func (p *Init) start(ctx context.Context) error { + err := p.runtime.Start(ctx, p.id) + return p.runtimeError(err, "OCI runtime start failed") +} + +// SetExited of the init process with the next status +func (p *Init) SetExited(status int) { + p.mu.Lock() + defer p.mu.Unlock() + + p.initState.SetExited(status) +} + +func (p *Init) setExited(status int) { + p.exited = time.Now() + p.status = status + p.Platform.ShutdownConsole(context.Background(), p.console) + close(p.waitBlock) +} + +// Delete the init process +func (p *Init) Delete(ctx context.Context) error { + p.mu.Lock() + defer p.mu.Unlock() + + return p.initState.Delete(ctx) +} + +func (p *Init) delete(ctx context.Context) error { + waitTimeout(ctx, &p.wg, 2*time.Second) + err := p.runtime.Delete(ctx, p.id, nil) + // ignore errors if a runtime has already deleted the process + // but we still hold metadata and pipes + // + // this is common during a checkpoint, runc will delete the container state + // after a checkpoint and the container will no longer exist within runc + if err != nil { + if strings.Contains(err.Error(), "does not exist") { + err = nil + } else { + err = p.runtimeError(err, "failed to delete task") + } + } + if p.io != nil { + for _, c := range p.closers { + c.Close() + } + p.io.Close() + } + if err2 := mount.UnmountAll(p.Rootfs, 0); err2 != nil { + log.G(ctx).WithError(err2).Warn("failed to cleanup rootfs mount") + if err == nil { + err = errors.Wrap(err2, "failed rootfs umount") + } + } + return err +} + +// Resize the init processes console +func (p *Init) Resize(ws console.WinSize) error { + p.mu.Lock() + defer p.mu.Unlock() + + if p.console == nil { + return nil + } + return p.console.Resize(ws) +} + +// Pause the init process and all its child processes +func (p *Init) Pause(ctx context.Context) error { + p.mu.Lock() + defer p.mu.Unlock() + + return p.initState.Pause(ctx) +} + +// Resume the init process and all its child processes +func (p *Init) Resume(ctx context.Context) error { + p.mu.Lock() + defer p.mu.Unlock() + + return p.initState.Resume(ctx) +} + +// Kill the init process +func (p *Init) Kill(ctx context.Context, signal uint32, all bool) error { + p.mu.Lock() + defer p.mu.Unlock() + + return p.initState.Kill(ctx, signal, all) +} + +func (p *Init) kill(ctx context.Context, signal uint32, all bool) error { + err := p.runtime.Kill(ctx, p.id, int(signal), &runc.KillOpts{ + All: all, + }) + return checkKillError(err) +} + +// KillAll processes belonging to the init process +func (p *Init) KillAll(ctx context.Context) error { + p.mu.Lock() + defer p.mu.Unlock() + + err := p.runtime.Kill(ctx, p.id, int(unix.SIGKILL), &runc.KillOpts{ + All: true, + }) + return p.runtimeError(err, "OCI runtime killall failed") +} + +// Stdin of the process +func (p *Init) Stdin() io.Closer { + return p.stdin +} + +// Runtime returns the OCI runtime configured for the init process +func (p *Init) Runtime() *runc.Runc { + return p.runtime +} + +// Exec returns a new child process +func (p *Init) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + p.mu.Lock() + defer p.mu.Unlock() + + return p.initState.Exec(ctx, path, r) +} + +// exec returns a new exec'd process +func (p *Init) exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + // process exec request + var spec specs.Process + if err := json.Unmarshal(r.Spec.Value, &spec); err != nil { + return nil, err + } + spec.Terminal = r.Terminal + + e := &execProcess{ + id: r.ID, + path: path, + parent: p, + spec: spec, + stdio: stdio.Stdio{ + Stdin: r.Stdin, + Stdout: r.Stdout, + Stderr: r.Stderr, + Terminal: r.Terminal, + }, + waitBlock: make(chan struct{}), + } + e.execState = &execCreatedState{p: e} + return e, nil +} + +// Checkpoint the init process +func (p *Init) Checkpoint(ctx context.Context, r *CheckpointConfig) error { + p.mu.Lock() + defer p.mu.Unlock() + + return p.initState.Checkpoint(ctx, r) +} + +func (p *Init) checkpoint(ctx context.Context, r *CheckpointConfig) error { + var actions []runc.CheckpointAction + if !r.Exit { + actions = append(actions, runc.LeaveRunning) + } + // keep criu work directory if criu work dir is set + work := r.WorkDir + if work == "" { + work = filepath.Join(p.WorkDir, "criu-work") + defer os.RemoveAll(work) + } + if err := p.runtime.Checkpoint(ctx, p.id, &runc.CheckpointOpts{ + WorkDir: work, + ImagePath: r.Path, + AllowOpenTCP: r.AllowOpenTCP, + AllowExternalUnixSockets: r.AllowExternalUnixSockets, + AllowTerminal: r.AllowTerminal, + FileLocks: r.FileLocks, + EmptyNamespaces: r.EmptyNamespaces, + }, actions...); err != nil { + dumpLog := filepath.Join(p.Bundle, "criu-dump.log") + if cerr := copyFile(dumpLog, filepath.Join(work, "dump.log")); cerr != nil { + log.G(ctx).WithError(cerr).Error("failed to copy dump.log to criu-dump.log") + } + return fmt.Errorf("%s path= %s", criuError(err), dumpLog) + } + return nil +} + +// Update the processes resource configuration +func (p *Init) Update(ctx context.Context, r *google_protobuf.Any) error { + p.mu.Lock() + defer p.mu.Unlock() + + return p.initState.Update(ctx, r) +} + +func (p *Init) update(ctx context.Context, r *google_protobuf.Any) error { + var resources specs.LinuxResources + if err := json.Unmarshal(r.Value, &resources); err != nil { + return err + } + return p.runtime.Update(ctx, p.id, &resources) +} + +// Stdio of the process +func (p *Init) Stdio() stdio.Stdio { + return p.stdio +} + +func (p *Init) runtimeError(rErr error, msg string) error { + if rErr == nil { + return nil + } + + rMsg, err := getLastRuntimeError(p.runtime) + switch { + case err != nil: + return errors.Wrapf(rErr, "%s: %s (%s)", msg, "unable to retrieve OCI runtime error", err.Error()) + case rMsg == "": + return errors.Wrap(rErr, msg) + default: + return errors.Errorf("%s: %s", msg, rMsg) + } +} + +func withConditionalIO(c stdio.Stdio) runc.IOOpt { + return func(o *runc.IOOption) { + o.OpenStdin = c.Stdin != "" + o.OpenStdout = c.Stdout != "" + o.OpenStderr = c.Stderr != "" + } +} diff --git a/vendor/github.com/containerd/containerd/pkg/process/init_state.go b/vendor/github.com/containerd/containerd/pkg/process/init_state.go new file mode 100644 index 0000000000000..5273a5d7352be --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/process/init_state.go @@ -0,0 +1,414 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package process + +import ( + "context" + + runc "github.com/containerd/go-runc" + google_protobuf "github.com/gogo/protobuf/types" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type initState interface { + Start(context.Context) error + Delete(context.Context) error + Pause(context.Context) error + Resume(context.Context) error + Update(context.Context, *google_protobuf.Any) error + Checkpoint(context.Context, *CheckpointConfig) error + Exec(context.Context, string, *ExecConfig) (Process, error) + Kill(context.Context, uint32, bool) error + SetExited(int) + Status(context.Context) (string, error) +} + +type createdState struct { + p *Init +} + +func (s *createdState) transition(name string) error { + switch name { + case "running": + s.p.initState = &runningState{p: s.p} + case "stopped": + s.p.initState = &stoppedState{p: s.p} + case "deleted": + s.p.initState = &deletedState{} + default: + return errors.Errorf("invalid state transition %q to %q", stateName(s), name) + } + return nil +} + +func (s *createdState) Pause(ctx context.Context) error { + return errors.Errorf("cannot pause task in created state") +} + +func (s *createdState) Resume(ctx context.Context) error { + return errors.Errorf("cannot resume task in created state") +} + +func (s *createdState) Update(ctx context.Context, r *google_protobuf.Any) error { + return s.p.update(ctx, r) +} + +func (s *createdState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { + return errors.Errorf("cannot checkpoint a task in created state") +} + +func (s *createdState) Start(ctx context.Context) error { + if err := s.p.start(ctx); err != nil { + return err + } + return s.transition("running") +} + +func (s *createdState) Delete(ctx context.Context) error { + if err := s.p.delete(ctx); err != nil { + return err + } + return s.transition("deleted") +} + +func (s *createdState) Kill(ctx context.Context, sig uint32, all bool) error { + return s.p.kill(ctx, sig, all) +} + +func (s *createdState) SetExited(status int) { + s.p.setExited(status) + + if err := s.transition("stopped"); err != nil { + panic(err) + } +} + +func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + return s.p.exec(ctx, path, r) +} + +func (s *createdState) Status(ctx context.Context) (string, error) { + return "created", nil +} + +type createdCheckpointState struct { + p *Init + opts *runc.RestoreOpts +} + +func (s *createdCheckpointState) transition(name string) error { + switch name { + case "running": + s.p.initState = &runningState{p: s.p} + case "stopped": + s.p.initState = &stoppedState{p: s.p} + case "deleted": + s.p.initState = &deletedState{} + default: + return errors.Errorf("invalid state transition %q to %q", stateName(s), name) + } + return nil +} + +func (s *createdCheckpointState) Pause(ctx context.Context) error { + return errors.Errorf("cannot pause task in created state") +} + +func (s *createdCheckpointState) Resume(ctx context.Context) error { + return errors.Errorf("cannot resume task in created state") +} + +func (s *createdCheckpointState) Update(ctx context.Context, r *google_protobuf.Any) error { + return s.p.update(ctx, r) +} + +func (s *createdCheckpointState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { + return errors.Errorf("cannot checkpoint a task in created state") +} + +func (s *createdCheckpointState) Start(ctx context.Context) error { + p := s.p + sio := p.stdio + + var ( + err error + socket *runc.Socket + ) + if sio.Terminal { + if socket, err = runc.NewTempConsoleSocket(); err != nil { + return errors.Wrap(err, "failed to create OCI runtime console socket") + } + defer socket.Close() + s.opts.ConsoleSocket = socket + } + + if _, err := s.p.runtime.Restore(ctx, p.id, p.Bundle, s.opts); err != nil { + return p.runtimeError(err, "OCI runtime restore failed") + } + if sio.Stdin != "" { + if err := p.openStdin(sio.Stdin); err != nil { + return errors.Wrapf(err, "failed to open stdin fifo %s", sio.Stdin) + } + } + if socket != nil { + console, err := socket.ReceiveMaster() + if err != nil { + return errors.Wrap(err, "failed to retrieve console master") + } + console, err = p.Platform.CopyConsole(ctx, console, p.id, sio.Stdin, sio.Stdout, sio.Stderr, &p.wg) + if err != nil { + return errors.Wrap(err, "failed to start console copy") + } + p.console = console + } else { + if err := p.io.Copy(ctx, &p.wg); err != nil { + return errors.Wrap(err, "failed to start io pipe copy") + } + } + pid, err := runc.ReadPidFile(s.opts.PidFile) + if err != nil { + return errors.Wrap(err, "failed to retrieve OCI runtime container pid") + } + p.pid = pid + return s.transition("running") +} + +func (s *createdCheckpointState) Delete(ctx context.Context) error { + if err := s.p.delete(ctx); err != nil { + return err + } + return s.transition("deleted") +} + +func (s *createdCheckpointState) Kill(ctx context.Context, sig uint32, all bool) error { + return s.p.kill(ctx, sig, all) +} + +func (s *createdCheckpointState) SetExited(status int) { + s.p.setExited(status) + + if err := s.transition("stopped"); err != nil { + panic(err) + } +} + +func (s *createdCheckpointState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + return nil, errors.Errorf("cannot exec in a created state") +} + +func (s *createdCheckpointState) Status(ctx context.Context) (string, error) { + return "created", nil +} + +type runningState struct { + p *Init +} + +func (s *runningState) transition(name string) error { + switch name { + case "stopped": + s.p.initState = &stoppedState{p: s.p} + case "paused": + s.p.initState = &pausedState{p: s.p} + default: + return errors.Errorf("invalid state transition %q to %q", stateName(s), name) + } + return nil +} + +func (s *runningState) Pause(ctx context.Context) error { + s.p.pausing.set(true) + // NOTE "pausing" will be returned in the short window + // after `transition("paused")`, before `pausing` is reset + // to false. That doesn't break the state machine, just + // delays the "paused" state a little bit. + defer s.p.pausing.set(false) + + if err := s.p.runtime.Pause(ctx, s.p.id); err != nil { + return s.p.runtimeError(err, "OCI runtime pause failed") + } + + return s.transition("paused") +} + +func (s *runningState) Resume(ctx context.Context) error { + return errors.Errorf("cannot resume a running process") +} + +func (s *runningState) Update(ctx context.Context, r *google_protobuf.Any) error { + return s.p.update(ctx, r) +} + +func (s *runningState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { + return s.p.checkpoint(ctx, r) +} + +func (s *runningState) Start(ctx context.Context) error { + return errors.Errorf("cannot start a running process") +} + +func (s *runningState) Delete(ctx context.Context) error { + return errors.Errorf("cannot delete a running process") +} + +func (s *runningState) Kill(ctx context.Context, sig uint32, all bool) error { + return s.p.kill(ctx, sig, all) +} + +func (s *runningState) SetExited(status int) { + s.p.setExited(status) + + if err := s.transition("stopped"); err != nil { + panic(err) + } +} + +func (s *runningState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + return s.p.exec(ctx, path, r) +} + +func (s *runningState) Status(ctx context.Context) (string, error) { + return "running", nil +} + +type pausedState struct { + p *Init +} + +func (s *pausedState) transition(name string) error { + switch name { + case "running": + s.p.initState = &runningState{p: s.p} + case "stopped": + s.p.initState = &stoppedState{p: s.p} + default: + return errors.Errorf("invalid state transition %q to %q", stateName(s), name) + } + return nil +} + +func (s *pausedState) Pause(ctx context.Context) error { + return errors.Errorf("cannot pause a paused container") +} + +func (s *pausedState) Resume(ctx context.Context) error { + if err := s.p.runtime.Resume(ctx, s.p.id); err != nil { + return s.p.runtimeError(err, "OCI runtime resume failed") + } + + return s.transition("running") +} + +func (s *pausedState) Update(ctx context.Context, r *google_protobuf.Any) error { + return s.p.update(ctx, r) +} + +func (s *pausedState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { + return s.p.checkpoint(ctx, r) +} + +func (s *pausedState) Start(ctx context.Context) error { + return errors.Errorf("cannot start a paused process") +} + +func (s *pausedState) Delete(ctx context.Context) error { + return errors.Errorf("cannot delete a paused process") +} + +func (s *pausedState) Kill(ctx context.Context, sig uint32, all bool) error { + return s.p.kill(ctx, sig, all) +} + +func (s *pausedState) SetExited(status int) { + s.p.setExited(status) + + if err := s.p.runtime.Resume(context.Background(), s.p.id); err != nil { + logrus.WithError(err).Error("resuming exited container from paused state") + } + + if err := s.transition("stopped"); err != nil { + panic(err) + } +} + +func (s *pausedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + return nil, errors.Errorf("cannot exec in a paused state") +} + +func (s *pausedState) Status(ctx context.Context) (string, error) { + return "paused", nil +} + +type stoppedState struct { + p *Init +} + +func (s *stoppedState) transition(name string) error { + switch name { + case "deleted": + s.p.initState = &deletedState{} + default: + return errors.Errorf("invalid state transition %q to %q", stateName(s), name) + } + return nil +} + +func (s *stoppedState) Pause(ctx context.Context) error { + return errors.Errorf("cannot pause a stopped container") +} + +func (s *stoppedState) Resume(ctx context.Context) error { + return errors.Errorf("cannot resume a stopped container") +} + +func (s *stoppedState) Update(ctx context.Context, r *google_protobuf.Any) error { + return errors.Errorf("cannot update a stopped container") +} + +func (s *stoppedState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { + return errors.Errorf("cannot checkpoint a stopped container") +} + +func (s *stoppedState) Start(ctx context.Context) error { + return errors.Errorf("cannot start a stopped process") +} + +func (s *stoppedState) Delete(ctx context.Context) error { + if err := s.p.delete(ctx); err != nil { + return err + } + return s.transition("deleted") +} + +func (s *stoppedState) Kill(ctx context.Context, sig uint32, all bool) error { + return s.p.kill(ctx, sig, all) +} + +func (s *stoppedState) SetExited(status int) { + // no op +} + +func (s *stoppedState) Exec(ctx context.Context, path string, r *ExecConfig) (Process, error) { + return nil, errors.Errorf("cannot exec in a stopped state") +} + +func (s *stoppedState) Status(ctx context.Context) (string, error) { + return "stopped", nil +} diff --git a/vendor/github.com/containerd/containerd/pkg/process/io.go b/vendor/github.com/containerd/containerd/pkg/process/io.go new file mode 100644 index 0000000000000..d1c5b96330a01 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/process/io.go @@ -0,0 +1,439 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package process + +import ( + "context" + "fmt" + "io" + "net/url" + "os" + "os/exec" + "path/filepath" + "sync" + "sync/atomic" + "syscall" + "time" + + "github.com/containerd/containerd/log" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/pkg/stdio" + "github.com/containerd/fifo" + runc "github.com/containerd/go-runc" + "github.com/hashicorp/go-multierror" + "github.com/pkg/errors" +) + +const binaryIOProcTermTimeout = 12 * time.Second // Give logger process solid 10 seconds for cleanup + +var bufPool = sync.Pool{ + New: func() interface{} { + // setting to 4096 to align with PIPE_BUF + // http://man7.org/linux/man-pages/man7/pipe.7.html + buffer := make([]byte, 4096) + return &buffer + }, +} + +type processIO struct { + io runc.IO + + uri *url.URL + copy bool + stdio stdio.Stdio +} + +func (p *processIO) Close() error { + if p.io != nil { + return p.io.Close() + } + return nil +} + +func (p *processIO) IO() runc.IO { + return p.io +} + +func (p *processIO) Copy(ctx context.Context, wg *sync.WaitGroup) error { + if !p.copy { + return nil + } + var cwg sync.WaitGroup + if err := copyPipes(ctx, p.IO(), p.stdio.Stdin, p.stdio.Stdout, p.stdio.Stderr, wg, &cwg); err != nil { + return errors.Wrap(err, "unable to copy pipes") + } + cwg.Wait() + return nil +} + +func createIO(ctx context.Context, id string, ioUID, ioGID int, stdio stdio.Stdio) (*processIO, error) { + pio := &processIO{ + stdio: stdio, + } + if stdio.IsNull() { + i, err := runc.NewNullIO() + if err != nil { + return nil, err + } + pio.io = i + return pio, nil + } + u, err := url.Parse(stdio.Stdout) + if err != nil { + return nil, errors.Wrap(err, "unable to parse stdout uri") + } + if u.Scheme == "" { + u.Scheme = "fifo" + } + pio.uri = u + switch u.Scheme { + case "fifo": + pio.copy = true + pio.io, err = runc.NewPipeIO(ioUID, ioGID, withConditionalIO(stdio)) + case "binary": + pio.io, err = NewBinaryIO(ctx, id, u) + case "file": + filePath := u.Path + if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil { + return nil, err + } + var f *os.File + f, err = os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return nil, err + } + f.Close() + pio.stdio.Stdout = filePath + pio.stdio.Stderr = filePath + pio.copy = true + pio.io, err = runc.NewPipeIO(ioUID, ioGID, withConditionalIO(stdio)) + default: + return nil, errors.Errorf("unknown STDIO scheme %s", u.Scheme) + } + if err != nil { + return nil, err + } + return pio, nil +} + +func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) error { + var sameFile *countingWriteCloser + for _, i := range []struct { + name string + dest func(wc io.WriteCloser, rc io.Closer) + }{ + { + name: stdout, + dest: func(wc io.WriteCloser, rc io.Closer) { + wg.Add(1) + cwg.Add(1) + go func() { + cwg.Done() + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + if _, err := io.CopyBuffer(wc, rio.Stdout(), *p); err != nil { + log.G(ctx).Warn("error copying stdout") + } + wg.Done() + wc.Close() + if rc != nil { + rc.Close() + } + }() + }, + }, { + name: stderr, + dest: func(wc io.WriteCloser, rc io.Closer) { + wg.Add(1) + cwg.Add(1) + go func() { + cwg.Done() + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + if _, err := io.CopyBuffer(wc, rio.Stderr(), *p); err != nil { + log.G(ctx).Warn("error copying stderr") + } + wg.Done() + wc.Close() + if rc != nil { + rc.Close() + } + }() + }, + }, + } { + ok, err := fifo.IsFifo(i.name) + if err != nil { + return err + } + var ( + fw io.WriteCloser + fr io.Closer + ) + if ok { + if fw, err = fifo.OpenFifo(ctx, i.name, syscall.O_WRONLY, 0); err != nil { + return errors.Wrapf(err, "containerd-shim: opening w/o fifo %q failed", i.name) + } + if fr, err = fifo.OpenFifo(ctx, i.name, syscall.O_RDONLY, 0); err != nil { + return errors.Wrapf(err, "containerd-shim: opening r/o fifo %q failed", i.name) + } + } else { + if sameFile != nil { + sameFile.count++ + i.dest(sameFile, nil) + continue + } + if fw, err = os.OpenFile(i.name, syscall.O_WRONLY|syscall.O_APPEND, 0); err != nil { + return errors.Wrapf(err, "containerd-shim: opening file %q failed", i.name) + } + if stdout == stderr { + sameFile = &countingWriteCloser{ + WriteCloser: fw, + count: 1, + } + } + } + i.dest(fw, fr) + } + if stdin == "" { + return nil + } + f, err := fifo.OpenFifo(context.Background(), stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0) + if err != nil { + return fmt.Errorf("containerd-shim: opening %s failed: %s", stdin, err) + } + cwg.Add(1) + go func() { + cwg.Done() + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + + io.CopyBuffer(rio.Stdin(), f, *p) + rio.Stdin().Close() + f.Close() + }() + return nil +} + +// countingWriteCloser masks io.Closer() until close has been invoked a certain number of times. +type countingWriteCloser struct { + io.WriteCloser + count int64 +} + +func (c *countingWriteCloser) Close() error { + if atomic.AddInt64(&c.count, -1) > 0 { + return nil + } + return c.WriteCloser.Close() +} + +// NewBinaryIO runs a custom binary process for pluggable shim logging +func NewBinaryIO(ctx context.Context, id string, uri *url.URL) (_ runc.IO, err error) { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, err + } + + var closers []func() error + defer func() { + if err == nil { + return + } + result := multierror.Append(err) + for _, fn := range closers { + result = multierror.Append(result, fn()) + } + err = multierror.Flatten(result) + }() + + out, err := newPipe() + if err != nil { + return nil, errors.Wrap(err, "failed to create stdout pipes") + } + closers = append(closers, out.Close) + + serr, err := newPipe() + if err != nil { + return nil, errors.Wrap(err, "failed to create stderr pipes") + } + closers = append(closers, serr.Close) + + r, w, err := os.Pipe() + if err != nil { + return nil, err + } + closers = append(closers, r.Close, w.Close) + + cmd := NewBinaryCmd(uri, id, ns) + cmd.ExtraFiles = append(cmd.ExtraFiles, out.r, serr.r, w) + // don't need to register this with the reaper or wait when + // running inside a shim + if err := cmd.Start(); err != nil { + return nil, errors.Wrap(err, "failed to start binary process") + } + closers = append(closers, func() error { return cmd.Process.Kill() }) + + // close our side of the pipe after start + if err := w.Close(); err != nil { + return nil, errors.Wrap(err, "failed to close write pipe after start") + } + + // wait for the logging binary to be ready + b := make([]byte, 1) + if _, err := r.Read(b); err != nil && err != io.EOF { + return nil, errors.Wrap(err, "failed to read from logging binary") + } + + return &binaryIO{ + cmd: cmd, + out: out, + err: serr, + }, nil +} + +type binaryIO struct { + cmd *exec.Cmd + out, err *pipe +} + +func (b *binaryIO) CloseAfterStart() error { + var ( + result *multierror.Error + ) + + for _, v := range []*pipe{b.out, b.err} { + if v != nil { + if err := v.r.Close(); err != nil { + result = multierror.Append(result, err) + } + } + } + + return result.ErrorOrNil() +} + +func (b *binaryIO) Close() error { + var ( + result *multierror.Error + ) + + for _, v := range []*pipe{b.out, b.err} { + if v != nil { + if err := v.Close(); err != nil { + result = multierror.Append(result, err) + } + } + } + + if err := b.cancel(); err != nil { + result = multierror.Append(result, err) + } + + return result.ErrorOrNil() +} + +func (b *binaryIO) cancel() error { + if b.cmd == nil || b.cmd.Process == nil { + return nil + } + + // Send SIGTERM first, so logger process has a chance to flush and exit properly + if err := b.cmd.Process.Signal(syscall.SIGTERM); err != nil { + result := multierror.Append(errors.Wrap(err, "failed to send SIGTERM")) + + log.L.WithError(err).Warn("failed to send SIGTERM signal, killing logging shim") + + if err := b.cmd.Process.Kill(); err != nil { + result = multierror.Append(result, errors.Wrap(err, "failed to kill process after faulty SIGTERM")) + } + + return result.ErrorOrNil() + } + + done := make(chan error, 1) + go func() { + done <- b.cmd.Wait() + }() + + select { + case err := <-done: + return err + case <-time.After(binaryIOProcTermTimeout): + log.L.Warn("failed to wait for shim logger process to exit, killing") + + err := b.cmd.Process.Kill() + if err != nil { + return errors.Wrap(err, "failed to kill shim logger process") + } + + return nil + } +} + +func (b *binaryIO) Stdin() io.WriteCloser { + return nil +} + +func (b *binaryIO) Stdout() io.ReadCloser { + return nil +} + +func (b *binaryIO) Stderr() io.ReadCloser { + return nil +} + +func (b *binaryIO) Set(cmd *exec.Cmd) { + if b.out != nil { + cmd.Stdout = b.out.w + } + if b.err != nil { + cmd.Stderr = b.err.w + } +} + +func newPipe() (*pipe, error) { + r, w, err := os.Pipe() + if err != nil { + return nil, err + } + return &pipe{ + r: r, + w: w, + }, nil +} + +type pipe struct { + r *os.File + w *os.File +} + +func (p *pipe) Close() error { + var result *multierror.Error + + if err := p.w.Close(); err != nil { + result = multierror.Append(result, errors.Wrap(err, "failed to close write pipe")) + } + + if err := p.r.Close(); err != nil { + result = multierror.Append(result, errors.Wrap(err, "failed to close read pipe")) + } + + return multierror.Prefix(result.ErrorOrNil(), "pipe:") +} diff --git a/vendor/github.com/containerd/containerd/pkg/process/io_util.go b/vendor/github.com/containerd/containerd/pkg/process/io_util.go new file mode 100644 index 0000000000000..72bbf9233f8f2 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/process/io_util.go @@ -0,0 +1,53 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package process + +import ( + "net/url" + "os" + "os/exec" +) + +// NewBinaryCmd returns a Cmd to be used to start a logging binary. +// The Cmd is generated from the provided uri, and the container ID and +// namespace are appended to the Cmd environment. +func NewBinaryCmd(binaryURI *url.URL, id, ns string) *exec.Cmd { + var args []string + for k, vs := range binaryURI.Query() { + args = append(args, k) + if len(vs) > 0 { + args = append(args, vs[0]) + } + } + + cmd := exec.Command(binaryURI.Path, args...) + + cmd.Env = append(cmd.Env, + "CONTAINER_ID="+id, + "CONTAINER_NAMESPACE="+ns, + ) + + return cmd +} + +// CloseFiles closes any files passed in. +// It it used for cleanup in the event of unexpected errors. +func CloseFiles(files ...*os.File) { + for _, file := range files { + file.Close() + } +} diff --git a/vendor/github.com/containerd/containerd/pkg/process/process.go b/vendor/github.com/containerd/containerd/pkg/process/process.go new file mode 100644 index 0000000000000..7cebb9b309072 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/process/process.go @@ -0,0 +1,56 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package process + +import ( + "context" + "io" + "time" + + "github.com/containerd/console" + "github.com/containerd/containerd/pkg/stdio" +) + +// Process on a system +type Process interface { + // ID returns the id for the process + ID() string + // Pid returns the pid for the process + Pid() int + // ExitStatus returns the exit status + ExitStatus() int + // ExitedAt is the time the process exited + ExitedAt() time.Time + // Stdin returns the process STDIN + Stdin() io.Closer + // Stdio returns io information for the container + Stdio() stdio.Stdio + // Status returns the process status + Status(context.Context) (string, error) + // Wait blocks until the process has exited + Wait() + // Resize resizes the process console + Resize(ws console.WinSize) error + // Start execution of the process + Start(context.Context) error + // Delete deletes the process and its resourcess + Delete(context.Context) error + // Kill kills the process + Kill(context.Context, uint32, bool) error + // SetExited sets the exit status for the process + SetExited(status int) +} diff --git a/vendor/github.com/containerd/containerd/pkg/process/types.go b/vendor/github.com/containerd/containerd/pkg/process/types.go new file mode 100644 index 0000000000000..03477038ab97f --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/process/types.go @@ -0,0 +1,66 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package process + +import ( + google_protobuf "github.com/gogo/protobuf/types" +) + +// Mount holds filesystem mount configuration +type Mount struct { + Type string + Source string + Target string + Options []string +} + +// CreateConfig hold task creation configuration +type CreateConfig struct { + ID string + Bundle string + Runtime string + Rootfs []Mount + Terminal bool + Stdin string + Stdout string + Stderr string + Checkpoint string + ParentCheckpoint string + Options *google_protobuf.Any +} + +// ExecConfig holds exec creation configuration +type ExecConfig struct { + ID string + Terminal bool + Stdin string + Stdout string + Stderr string + Spec *google_protobuf.Any +} + +// CheckpointConfig holds task checkpoint configuration +type CheckpointConfig struct { + WorkDir string + Path string + Exit bool + AllowOpenTCP bool + AllowExternalUnixSockets bool + AllowTerminal bool + FileLocks bool + EmptyNamespaces []string +} diff --git a/vendor/github.com/containerd/containerd/pkg/process/utils.go b/vendor/github.com/containerd/containerd/pkg/process/utils.go new file mode 100644 index 0000000000000..6536ac5a88474 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/process/utils.go @@ -0,0 +1,202 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package process + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/containerd/containerd/errdefs" + runc "github.com/containerd/go-runc" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +const ( + // RuncRoot is the path to the root runc state directory + RuncRoot = "/run/containerd/runc" + // InitPidFile name of the file that contains the init pid + InitPidFile = "init.pid" +) + +// safePid is a thread safe wrapper for pid. +type safePid struct { + sync.Mutex + pid int +} + +func (s *safePid) get() int { + s.Lock() + defer s.Unlock() + return s.pid +} + +type atomicBool int32 + +func (ab *atomicBool) set(b bool) { + if b { + atomic.StoreInt32((*int32)(ab), 1) + } else { + atomic.StoreInt32((*int32)(ab), 0) + } +} + +func (ab *atomicBool) get() bool { + return atomic.LoadInt32((*int32)(ab)) == 1 +} + +// TODO(mlaventure): move to runc package? +func getLastRuntimeError(r *runc.Runc) (string, error) { + if r.Log == "" { + return "", nil + } + + f, err := os.OpenFile(r.Log, os.O_RDONLY, 0400) + if err != nil { + return "", err + } + defer f.Close() + + var ( + errMsg string + log struct { + Level string + Msg string + Time time.Time + } + ) + + dec := json.NewDecoder(f) + for err = nil; err == nil; { + if err = dec.Decode(&log); err != nil && err != io.EOF { + return "", err + } + if log.Level == "error" { + errMsg = strings.TrimSpace(log.Msg) + } + } + + return errMsg, nil +} + +// criuError returns only the first line of the error message from criu +// it tries to add an invalid dump log location when returning the message +func criuError(err error) string { + parts := strings.Split(err.Error(), "\n") + return parts[0] +} + +func copyFile(to, from string) error { + ff, err := os.Open(from) + if err != nil { + return err + } + defer ff.Close() + tt, err := os.Create(to) + if err != nil { + return err + } + defer tt.Close() + + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + _, err = io.CopyBuffer(tt, ff, *p) + return err +} + +func checkKillError(err error) error { + if err == nil { + return nil + } + if strings.Contains(err.Error(), "os: process already finished") || + strings.Contains(err.Error(), "container not running") || + strings.Contains(strings.ToLower(err.Error()), "no such process") || + err == unix.ESRCH { + return errors.Wrapf(errdefs.ErrNotFound, "process already finished") + } else if strings.Contains(err.Error(), "does not exist") { + return errors.Wrapf(errdefs.ErrNotFound, "no such container") + } + return errors.Wrapf(err, "unknown error after kill") +} + +func newPidFile(bundle string) *pidFile { + return &pidFile{ + path: filepath.Join(bundle, InitPidFile), + } +} + +func newExecPidFile(bundle, id string) *pidFile { + return &pidFile{ + path: filepath.Join(bundle, fmt.Sprintf("%s.pid", id)), + } +} + +type pidFile struct { + path string +} + +func (p *pidFile) Path() string { + return p.path +} + +func (p *pidFile) Read() (int, error) { + return runc.ReadPidFile(p.path) +} + +// waitTimeout handles waiting on a waitgroup with a specified timeout. +// this is commonly used for waiting on IO to finish after a process has exited +func waitTimeout(ctx context.Context, wg *sync.WaitGroup, timeout time.Duration) error { + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + select { + case <-done: + return nil + case <-ctx.Done(): + return ctx.Err() + } +} + +func stateName(v interface{}) string { + switch v.(type) { + case *runningState, *execRunningState: + return "running" + case *createdState, *execCreatedState, *createdCheckpointState: + return "created" + case *pausedState: + return "paused" + case *deletedState: + return "deleted" + case *stoppedState: + return "stopped" + } + panic(errors.Errorf("invalid state %v", v)) +} diff --git a/vendor/github.com/containerd/containerd/pkg/seccomp/seccomp.go b/vendor/github.com/containerd/containerd/pkg/seccomp/seccomp.go new file mode 100644 index 0000000000000..74982358a83cd --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/seccomp/seccomp.go @@ -0,0 +1,25 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package seccomp + +// IsEnabled checks whether seccomp support is enabled. On Linux, it returns +// true if the kernel has been configured to support seccomp (kernel options +// CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER are set). On non-Linux, it always +// returns false. +func IsEnabled() bool { + return isEnabled() +} diff --git a/vendor/github.com/containerd/containerd/pkg/seccomp/seccomp_linux.go b/vendor/github.com/containerd/containerd/pkg/seccomp/seccomp_linux.go new file mode 100644 index 0000000000000..a23b492c6fbeb --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/seccomp/seccomp_linux.go @@ -0,0 +1,80 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +/* + Copyright The runc Authors. + + 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. +*/ + +package seccomp + +import ( + "sync" + + "golang.org/x/sys/unix" +) + +var ( + enabled bool + enabledOnce sync.Once +) + +// isEnabled returns whether the kernel has been configured to support seccomp +// (including the check for CONFIG_SECCOMP_FILTER kernel option). +func isEnabled() bool { + // Excerpts from prctl(2), section ERRORS: + // + // EACCES + // option is PR_SET_SECCOMP and arg2 is SECCOMP_MODE_FILTER, but + // the process does not have the CAP_SYS_ADMIN capability or has + // not set the no_new_privs attribute <...>. + // <...> + // EFAULT + // option is PR_SET_SECCOMP, arg2 is SECCOMP_MODE_FILTER, the + // system was built with CONFIG_SECCOMP_FILTER, and arg3 is an + // invalid address. + // <...> + // EINVAL + // option is PR_SET_SECCOMP or PR_GET_SECCOMP, and the kernel + // was not configured with CONFIG_SECCOMP. + // + // EINVAL + // option is PR_SET_SECCOMP, arg2 is SECCOMP_MODE_FILTER, + // and the kernel was not configured with CONFIG_SECCOMP_FILTER. + // + // + // Meaning, in case these kernel options are set (this is what we check + // for here), we will get some other error (most probably EACCES or + // EFAULT). IOW, EINVAL means "seccomp not supported", any other error + // means it is supported. + + enabledOnce.Do(func() { + enabled = unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0) != unix.EINVAL + }) + + return enabled +} diff --git a/vendor/github.com/containerd/containerd/pkg/seccomp/seccomp_unsupported.go b/vendor/github.com/containerd/containerd/pkg/seccomp/seccomp_unsupported.go new file mode 100644 index 0000000000000..87b133426ce42 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/seccomp/seccomp_unsupported.go @@ -0,0 +1,23 @@ +// +build !linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package seccomp + +func isEnabled() bool { + return false +} diff --git a/vendor/github.com/containerd/containerd/pkg/stdio/platform.go b/vendor/github.com/containerd/containerd/pkg/stdio/platform.go new file mode 100644 index 0000000000000..ca8688fc00a45 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/stdio/platform.go @@ -0,0 +1,33 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package stdio + +import ( + "context" + "sync" + + "github.com/containerd/console" +) + +// Platform handles platform-specific behavior that may differs across +// platform implementations +type Platform interface { + CopyConsole(ctx context.Context, console console.Console, id, stdin, stdout, stderr string, + wg *sync.WaitGroup) (console.Console, error) + ShutdownConsole(ctx context.Context, console console.Console) error + Close() error +} diff --git a/vendor/github.com/containerd/containerd/pkg/stdio/stdio.go b/vendor/github.com/containerd/containerd/pkg/stdio/stdio.go new file mode 100644 index 0000000000000..b02e77dcdd7dd --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/stdio/stdio.go @@ -0,0 +1,30 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package stdio + +// Stdio of a process +type Stdio struct { + Stdin string + Stdout string + Stderr string + Terminal bool +} + +// IsNull returns true if the stdio is not defined +func (s Stdio) IsNull() bool { + return s.Stdin == "" && s.Stdout == "" && s.Stderr == "" +} diff --git a/vendor/github.com/containerd/containerd/pkg/userns/userns_linux.go b/vendor/github.com/containerd/containerd/pkg/userns/userns_linux.go new file mode 100644 index 0000000000000..6656465efbca5 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/userns/userns_linux.go @@ -0,0 +1,62 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package userns + +import ( + "bufio" + "fmt" + "os" + "sync" +) + +var ( + inUserNS bool + nsOnce sync.Once +) + +// RunningInUserNS detects whether we are currently running in a user namespace. +// Originally copied from github.com/lxc/lxd/shared/util.go +func RunningInUserNS() bool { + nsOnce.Do(func() { + file, err := os.Open("/proc/self/uid_map") + if err != nil { + // This kernel-provided file only exists if user namespaces are supported + return + } + defer file.Close() + + buf := bufio.NewReader(file) + l, _, err := buf.ReadLine() + if err != nil { + return + } + + line := string(l) + var a, b, c int64 + fmt.Sscanf(line, "%d %d %d", &a, &b, &c) + + /* + * We assume we are in the initial user namespace if we have a full + * range - 4294967295 uids starting at uid 0. + */ + if a == 0 && b == 0 && c == 4294967295 { + return + } + inUserNS = true + }) + return inUserNS +} diff --git a/vendor/github.com/containerd/containerd/pkg/userns/userns_unsupported.go b/vendor/github.com/containerd/containerd/pkg/userns/userns_unsupported.go new file mode 100644 index 0000000000000..aab756fd2a605 --- /dev/null +++ b/vendor/github.com/containerd/containerd/pkg/userns/userns_unsupported.go @@ -0,0 +1,25 @@ +// +build !linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package userns + +// RunningInUserNS is a stub for non-Linux systems +// Always returns false +func RunningInUserNS() bool { + return false +} diff --git a/vendor/github.com/containerd/containerd/platforms/compare.go b/vendor/github.com/containerd/containerd/platforms/compare.go index 8259bbc851c48..c7657e1869bec 100644 --- a/vendor/github.com/containerd/containerd/platforms/compare.go +++ b/vendor/github.com/containerd/containerd/platforms/compare.go @@ -16,7 +16,12 @@ package platforms -import specs "github.com/opencontainers/image-spec/specs-go/v1" +import ( + "strconv" + "strings" + + specs "github.com/opencontainers/image-spec/specs-go/v1" +) // MatchComparer is able to match and compare platforms to // filter and sort platforms. @@ -26,66 +31,70 @@ type MatchComparer interface { Less(specs.Platform, specs.Platform) bool } -// Only returns a match comparer for a single platform -// using default resolution logic for the platform. -// -// For ARMv7, will also match ARMv6 and ARMv5 -// For ARMv6, will also match ARMv5 -func Only(platform specs.Platform) MatchComparer { - platform = Normalize(platform) - if platform.Architecture == "arm" { - if platform.Variant == "v7" { - return orderedPlatformComparer{ - matchers: []Matcher{ - &matcher{ - Platform: platform, - }, - &matcher{ - Platform: specs.Platform{ - Architecture: platform.Architecture, - OS: platform.OS, - OSVersion: platform.OSVersion, - OSFeatures: platform.OSFeatures, - Variant: "v6", - }, - }, - &matcher{ - Platform: specs.Platform{ - Architecture: platform.Architecture, - OS: platform.OS, - OSVersion: platform.OSVersion, - OSFeatures: platform.OSFeatures, - Variant: "v5", - }, - }, - }, +// platformVector returns an (ordered) vector of appropriate specs.Platform +// objects to try matching for the given platform object (see platforms.Only). +func platformVector(platform specs.Platform) []specs.Platform { + vector := []specs.Platform{platform} + + switch platform.Architecture { + case "amd64": + vector = append(vector, specs.Platform{ + Architecture: "386", + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: platform.Variant, + }) + case "arm": + if armVersion, err := strconv.Atoi(strings.TrimPrefix(platform.Variant, "v")); err == nil && armVersion > 5 { + for armVersion--; armVersion >= 5; armVersion-- { + vector = append(vector, specs.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: "v" + strconv.Itoa(armVersion), + }) } } - if platform.Variant == "v6" { - return orderedPlatformComparer{ - matchers: []Matcher{ - &matcher{ - Platform: platform, - }, - &matcher{ - Platform: specs.Platform{ - Architecture: platform.Architecture, - OS: platform.OS, - OSVersion: platform.OSVersion, - OSFeatures: platform.OSFeatures, - Variant: "v5", - }, - }, - }, - } + case "arm64": + variant := platform.Variant + if variant == "" { + variant = "v8" } + vector = append(vector, platformVector(specs.Platform{ + Architecture: "arm", + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: variant, + })...) } - return singlePlatformComparer{ - Matcher: &matcher{ - Platform: platform, - }, - } + return vector +} + +// Only returns a match comparer for a single platform +// using default resolution logic for the platform. +// +// For arm/v8, will also match arm/v7, arm/v6 and arm/v5 +// For arm/v7, will also match arm/v6 and arm/v5 +// For arm/v6, will also match arm/v5 +// For amd64, will also match 386 +func Only(platform specs.Platform) MatchComparer { + return Ordered(platformVector(Normalize(platform))...) +} + +// OnlyStrict returns a match comparer for a single platform. +// +// Unlike Only, OnlyStrict does not match sub platforms. +// So, "arm/vN" will not match "arm/vM" where M < N, +// and "amd64" will not also match "386". +// +// OnlyStrict matches non-canonical forms. +// So, "arm64" matches "arm/64/v8". +func OnlyStrict(platform specs.Platform) MatchComparer { + return Ordered(Normalize(platform)) } // Ordered returns a platform MatchComparer which matches any of the platforms @@ -116,14 +125,6 @@ func Any(platforms ...specs.Platform) MatchComparer { // with preference for ordering. var All MatchComparer = allPlatformComparer{} -type singlePlatformComparer struct { - Matcher -} - -func (c singlePlatformComparer) Less(p1, p2 specs.Platform) bool { - return c.Match(p1) && !c.Match(p2) -} - type orderedPlatformComparer struct { matchers []Matcher } diff --git a/vendor/github.com/containerd/containerd/platforms/cpuinfo.go b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go index a5c5ab42b91fe..4a7177e313845 100644 --- a/vendor/github.com/containerd/containerd/platforms/cpuinfo.go +++ b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go @@ -21,6 +21,7 @@ import ( "os" "runtime" "strings" + "sync" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/log" @@ -28,14 +29,18 @@ import ( ) // Present the ARM instruction set architecture, eg: v7, v8 -var cpuVariant string +// Don't use this value directly; call cpuVariant() instead. +var cpuVariantValue string -func init() { - if isArmArch(runtime.GOARCH) { - cpuVariant = getCPUVariant() - } else { - cpuVariant = "" - } +var cpuVariantOnce sync.Once + +func cpuVariant() string { + cpuVariantOnce.Do(func() { + if isArmArch(runtime.GOARCH) { + cpuVariantValue = getCPUVariant() + } + }) + return cpuVariantValue } // For Linux, the kernel has already detected the ABI, ISA and Features. @@ -74,22 +79,47 @@ func getCPUInfo(pattern string) (info string, err error) { } func getCPUVariant() string { + if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { + // Windows/Darwin only supports v7 for ARM32 and v8 for ARM64 and so we can use + // runtime.GOARCH to determine the variants + var variant string + switch runtime.GOARCH { + case "arm64": + variant = "v8" + case "arm": + variant = "v7" + default: + variant = "unknown" + } + + return variant + } + variant, err := getCPUInfo("Cpu architecture") if err != nil { log.L.WithError(err).Error("failure getting variant") return "" } - switch variant { - case "8": + // handle edge case for Raspberry Pi ARMv6 devices (which due to a kernel quirk, report "CPU architecture: 7") + // https://www.raspberrypi.org/forums/viewtopic.php?t=12614 + if runtime.GOARCH == "arm" && variant == "7" { + model, err := getCPUInfo("model name") + if err == nil && strings.HasPrefix(strings.ToLower(model), "armv6-compatible") { + variant = "6" + } + } + + switch strings.ToLower(variant) { + case "8", "aarch64": variant = "v8" - case "7", "7M", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)": + case "7", "7m", "?(12)", "?(13)", "?(14)", "?(15)", "?(16)", "?(17)": variant = "v7" - case "6", "6TEJ": + case "6", "6tej": variant = "v6" - case "5", "5T", "5TE", "5TEJ": + case "5", "5t", "5te", "5tej": variant = "v5" - case "4", "4T": + case "4", "4t": variant = "v4" case "3": variant = "v3" diff --git a/vendor/github.com/containerd/containerd/platforms/database.go b/vendor/github.com/containerd/containerd/platforms/database.go index 8e85448ed028c..6ede94061eb88 100644 --- a/vendor/github.com/containerd/containerd/platforms/database.go +++ b/vendor/github.com/containerd/containerd/platforms/database.go @@ -28,7 +28,7 @@ func isLinuxOS(os string) bool { return os == "linux" } -// These function are generated from from https://golang.org/src/go/build/syslist.go. +// These function are generated from https://golang.org/src/go/build/syslist.go. // // We use switch statements because they are slightly faster than map lookups // and use a little less memory. @@ -38,7 +38,7 @@ func isLinuxOS(os string) bool { // The OS value should be normalized before calling this function. func isKnownOS(os string) bool { switch os { - case "android", "darwin", "dragonfly", "freebsd", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos": + case "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "js", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos": return true } return false @@ -60,7 +60,7 @@ func isArmArch(arch string) bool { // The arch value should be normalized before being passed to this function. func isKnownArch(arch string) bool { switch arch { - case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "s390", "s390x", "sparc", "sparc64": + case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm": return true } return false diff --git a/vendor/github.com/containerd/containerd/platforms/defaults.go b/vendor/github.com/containerd/containerd/platforms/defaults.go index a14d80e58cbf7..cb77fbc9f7b96 100644 --- a/vendor/github.com/containerd/containerd/platforms/defaults.go +++ b/vendor/github.com/containerd/containerd/platforms/defaults.go @@ -33,6 +33,11 @@ func DefaultSpec() specs.Platform { OS: runtime.GOOS, Architecture: runtime.GOARCH, // The Variant field will be empty if arch != ARM. - Variant: cpuVariant, + Variant: cpuVariant(), } } + +// DefaultStrict returns strict form of Default. +func DefaultStrict() MatchComparer { + return OnlyStrict(DefaultSpec()) +} diff --git a/vendor/github.com/containerd/containerd/platforms/defaults_windows.go b/vendor/github.com/containerd/containerd/platforms/defaults_windows.go index 0defbd36c0424..0c380e3b7c113 100644 --- a/vendor/github.com/containerd/containerd/platforms/defaults_windows.go +++ b/vendor/github.com/containerd/containerd/platforms/defaults_windows.go @@ -19,13 +19,63 @@ package platforms import ( + "fmt" + "runtime" + "strconv" + "strings" + + imagespec "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/image-spec/specs-go/v1" + "golang.org/x/sys/windows" ) -// Default returns the default matcher for the platform. +type matchComparer struct { + defaults Matcher + osVersionPrefix string +} + +// Match matches platform with the same windows major, minor +// and build version. +func (m matchComparer) Match(p imagespec.Platform) bool { + if m.defaults.Match(p) { + // TODO(windows): Figure out whether OSVersion is deprecated. + return strings.HasPrefix(p.OSVersion, m.osVersionPrefix) + } + return false +} + +// Less sorts matched platforms in front of other platforms. +// For matched platforms, it puts platforms with larger revision +// number in front. +func (m matchComparer) Less(p1, p2 imagespec.Platform) bool { + m1, m2 := m.Match(p1), m.Match(p2) + if m1 && m2 { + r1, r2 := revision(p1.OSVersion), revision(p2.OSVersion) + return r1 > r2 + } + return m1 && !m2 +} + +func revision(v string) int { + parts := strings.Split(v, ".") + if len(parts) < 4 { + return 0 + } + r, err := strconv.Atoi(parts[3]) + if err != nil { + return 0 + } + return r +} + +// Default returns the current platform's default platform specification. func Default() MatchComparer { - return Ordered(DefaultSpec(), specs.Platform{ - OS: "linux", - Architecture: "amd64", - }) + major, minor, build := windows.RtlGetNtVersionNumbers() + return matchComparer{ + defaults: Ordered(DefaultSpec(), specs.Platform{ + OS: "linux", + Architecture: runtime.GOARCH, + }), + osVersionPrefix: fmt.Sprintf("%d.%d.%d", major, minor, build), + } } diff --git a/vendor/github.com/containerd/containerd/platforms/platforms.go b/vendor/github.com/containerd/containerd/platforms/platforms.go index 2c2cc1102e189..088bdea05086d 100644 --- a/vendor/github.com/containerd/containerd/platforms/platforms.go +++ b/vendor/github.com/containerd/containerd/platforms/platforms.go @@ -130,7 +130,7 @@ type Matcher interface { // specification. The returned matcher only looks for equality based on os, // architecture and variant. // -// One may implement their own matcher if this doesn't provide the the required +// One may implement their own matcher if this doesn't provide the required // functionality. // // Applications should opt to use `Match` over directly parsing specifiers. @@ -189,9 +189,8 @@ func Parse(specifier string) (specs.Platform, error) { if isKnownOS(p.OS) { // picks a default architecture p.Architecture = runtime.GOARCH - if p.Architecture == "arm" { - // TODO(stevvooe): Resolve arm variant, if not v6 (default) - return specs.Platform{}, errors.Wrapf(errdefs.ErrNotImplemented, "arm support not fully implemented") + if p.Architecture == "arm" && cpuVariant() != "v7" { + p.Variant = cpuVariant() } return p, nil diff --git a/vendor/github.com/containerd/containerd/plugin/context.go b/vendor/github.com/containerd/containerd/plugin/context.go index 1211c907ef671..75b7366fca36e 100644 --- a/vendor/github.com/containerd/containerd/plugin/context.go +++ b/vendor/github.com/containerd/containerd/plugin/context.go @@ -28,12 +28,13 @@ import ( // InitContext is used for plugin inititalization type InitContext struct { - Context context.Context - Root string - State string - Config interface{} - Address string - Events *exchange.Exchange + Context context.Context + Root string + State string + Config interface{} + Address string + TTRPCAddress string + Events *exchange.Exchange Meta *Meta // plugins can fill in metadata at init. diff --git a/vendor/github.com/containerd/containerd/plugin/plugin.go b/vendor/github.com/containerd/containerd/plugin/plugin.go index 0847ae7f28f6e..2674edeb1b9a6 100644 --- a/vendor/github.com/containerd/containerd/plugin/plugin.go +++ b/vendor/github.com/containerd/containerd/plugin/plugin.go @@ -20,6 +20,7 @@ import ( "fmt" "sync" + "github.com/containerd/ttrpc" "github.com/pkg/errors" "google.golang.org/grpc" ) @@ -29,7 +30,8 @@ var ( ErrNoType = errors.New("plugin: no type") // ErrNoPluginID is returned when no id is specified ErrNoPluginID = errors.New("plugin: no id") - + // ErrIDRegistered is returned when a duplicate id is already registered + ErrIDRegistered = errors.New("plugin: id already registered") // ErrSkipPlugin is used when a plugin is not initialized and should not be loaded, // this allows the plugin loader differentiate between a plugin which is configured // not to load and one that fails to load. @@ -42,10 +44,7 @@ var ( // IsSkipPlugin returns true if the error is skipping the plugin func IsSkipPlugin(err error) bool { - if errors.Cause(err) == ErrSkipPlugin { - return true - } - return false + return errors.Is(err, ErrSkipPlugin) } // Type is the type of the plugin @@ -78,6 +77,15 @@ const ( GCPlugin Type = "io.containerd.gc.v1" ) +const ( + // RuntimeLinuxV1 is the legacy linux runtime + RuntimeLinuxV1 = "io.containerd.runtime.v1.linux" + // RuntimeRuncV1 is the runc runtime that supports a single container + RuntimeRuncV1 = "io.containerd.runc.v1" + // RuntimeRuncV2 is the runc runtime that supports multiple containers per shim + RuntimeRuncV2 = "io.containerd.runc.v2" +) + // Registration contains information for registering a plugin type Registration struct { // Type of the plugin @@ -93,6 +101,8 @@ type Registration struct { // context are passed in. The init function may modify the registration to // add exports, capabilities and platform support declarations. InitFn func(*InitContext) (interface{}, error) + // Disable the plugin from loading + Disable bool } // Init the registered plugin @@ -117,6 +127,16 @@ type Service interface { Register(*grpc.Server) error } +// TTRPCService allows TTRPC services to be registered with the underlying server +type TTRPCService interface { + RegisterTTRPC(*ttrpc.Server) error +} + +// TCPService allows GRPC services to be registered with the underlying tcp server +type TCPService interface { + RegisterTCP(*grpc.Server) error +} + var register = struct { sync.RWMutex r []*Registration @@ -140,12 +160,16 @@ func Load(path string) (err error) { func Register(r *Registration) { register.Lock() defer register.Unlock() + if r.Type == "" { panic(ErrNoType) } if r.ID == "" { panic(ErrNoPluginID) } + if err := checkUnique(r); err != nil { + panic(err) + } var last bool for _, requires := range r.Requires { @@ -160,24 +184,36 @@ func Register(r *Registration) { register.r = append(register.r, r) } +func checkUnique(r *Registration) error { + for _, registered := range register.r { + if r.URI() == registered.URI() { + return errors.Wrap(ErrIDRegistered, r.URI()) + } + } + return nil +} + +// DisableFilter filters out disabled plugins +type DisableFilter func(r *Registration) bool + // Graph returns an ordered list of registered plugins for initialization. // Plugins in disableList specified by id will be disabled. -func Graph(disableList []string) (ordered []*Registration) { +func Graph(filter DisableFilter) (ordered []*Registration) { register.RLock() defer register.RUnlock() - for _, d := range disableList { - for i, r := range register.r { - if r.ID == d { - register.r = append(register.r[:i], register.r[i+1:]...) - break - } + + for _, r := range register.r { + if filter(r) { + r.Disable = true } } added := map[*Registration]bool{} for _, r := range register.r { - - children(r.ID, r.Requires, added, &ordered) + if r.Disable { + continue + } + children(r, added, &ordered) if !added[r] { ordered = append(ordered, r) added[r] = true @@ -186,11 +222,13 @@ func Graph(disableList []string) (ordered []*Registration) { return ordered } -func children(id string, types []Type, added map[*Registration]bool, ordered *[]*Registration) { - for _, t := range types { +func children(reg *Registration, added map[*Registration]bool, ordered *[]*Registration) { + for _, t := range reg.Requires { for _, r := range register.r { - if r.ID != id && (t == "*" || r.Type == t) { - children(r.ID, r.Requires, added, ordered) + if !r.Disable && + r.URI() != reg.URI() && + (t == "*" || r.Type == t) { + children(r, added, ordered) if !added[r] { *ordered = append(*ordered, r) added[r] = true diff --git a/vendor/github.com/containerd/containerd/plugin/plugin_go18.go b/vendor/github.com/containerd/containerd/plugin/plugin_go18.go index 5b82db868584b..927fe61965aeb 100644 --- a/vendor/github.com/containerd/containerd/plugin/plugin_go18.go +++ b/vendor/github.com/containerd/containerd/plugin/plugin_go18.go @@ -1,4 +1,4 @@ -// +build go1.8,!windows,amd64,!static_build +// +build go1.8,!windows,amd64,!static_build,!gccgo /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/containerd/plugin/plugin_other.go b/vendor/github.com/containerd/containerd/plugin/plugin_other.go index 2978f60fd3b4a..0c5e14165cffd 100644 --- a/vendor/github.com/containerd/containerd/plugin/plugin_other.go +++ b/vendor/github.com/containerd/containerd/plugin/plugin_other.go @@ -1,4 +1,4 @@ -// +build !go1.8 windows !amd64 static_build +// +build !go1.8 windows !amd64 static_build gccgo /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/containerd/process.go b/vendor/github.com/containerd/containerd/process.go index ff7d838cde05d..5b302569babe8 100644 --- a/vendor/github.com/containerd/containerd/process.go +++ b/vendor/github.com/containerd/containerd/process.go @@ -44,7 +44,7 @@ type Process interface { Wait(context.Context) (<-chan ExitStatus, error) // CloseIO allows various pipes to be closed on the process CloseIO(context.Context, ...IOCloserOpts) error - // Resize changes the width and heigh of the process's terminal + // Resize changes the width and height of the process's terminal Resize(ctx context.Context, w, h uint32) error // IO returns the io set for the process IO() cio.IO @@ -52,7 +52,16 @@ type Process interface { Status(context.Context) (Status, error) } -// ExitStatus encapsulates a process' exit status. +// NewExitStatus populates an ExitStatus +func NewExitStatus(code uint32, t time.Time, err error) *ExitStatus { + return &ExitStatus{ + code: code, + exitedAt: t, + err: err, + } +} + +// ExitStatus encapsulates a process's exit status. // It is used by `Wait()` to return either a process exit code or an error type ExitStatus struct { code uint32 @@ -111,9 +120,11 @@ func (p *process) Start(ctx context.Context) error { ExecID: p.id, }) if err != nil { - p.io.Cancel() - p.io.Wait() - p.io.Close() + if p.io != nil { + p.io.Cancel() + p.io.Wait() + p.io.Close() + } return errdefs.FromGRPC(err) } p.pid = r.Pid diff --git a/vendor/github.com/containerd/containerd/pull.go b/vendor/github.com/containerd/containerd/pull.go new file mode 100644 index 0000000000000..36365513f69bf --- /dev/null +++ b/vendor/github.com/containerd/containerd/pull.go @@ -0,0 +1,255 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "context" + + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/platforms" + "github.com/containerd/containerd/remotes" + "github.com/containerd/containerd/remotes/docker" + "github.com/containerd/containerd/remotes/docker/schema1" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" +) + +// Pull downloads the provided content into containerd's content store +// and returns a platform specific image object +func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (_ Image, retErr error) { + pullCtx := defaultRemoteContext() + for _, o := range opts { + if err := o(c, pullCtx); err != nil { + return nil, err + } + } + + if pullCtx.PlatformMatcher == nil { + if len(pullCtx.Platforms) > 1 { + return nil, errors.New("cannot pull multiplatform image locally, try Fetch") + } else if len(pullCtx.Platforms) == 0 { + pullCtx.PlatformMatcher = c.platform + } else { + p, err := platforms.Parse(pullCtx.Platforms[0]) + if err != nil { + return nil, errors.Wrapf(err, "invalid platform %s", pullCtx.Platforms[0]) + } + + pullCtx.PlatformMatcher = platforms.Only(p) + } + } + + ctx, done, err := c.WithLease(ctx) + if err != nil { + return nil, err + } + defer done(ctx) + + var unpacks int32 + var unpackEg *errgroup.Group + var unpackWrapper func(f images.Handler) images.Handler + + if pullCtx.Unpack { + // unpacker only supports schema 2 image, for schema 1 this is noop. + u, err := c.newUnpacker(ctx, pullCtx) + if err != nil { + return nil, errors.Wrap(err, "create unpacker") + } + unpackWrapper, unpackEg = u.handlerWrapper(ctx, pullCtx, &unpacks) + defer func() { + if err := unpackEg.Wait(); err != nil { + if retErr == nil { + retErr = errors.Wrap(err, "unpack") + } + } + }() + wrapper := pullCtx.HandlerWrapper + pullCtx.HandlerWrapper = func(h images.Handler) images.Handler { + if wrapper == nil { + return unpackWrapper(h) + } + return unpackWrapper(wrapper(h)) + } + } + + img, err := c.fetch(ctx, pullCtx, ref, 1) + if err != nil { + return nil, err + } + + // NOTE(fuweid): unpacker defers blobs download. before create image + // record in ImageService, should wait for unpacking(including blobs + // download). + if pullCtx.Unpack { + if unpackEg != nil { + if err := unpackEg.Wait(); err != nil { + return nil, err + } + } + } + + img, err = c.createNewImage(ctx, img) + if err != nil { + return nil, err + } + + i := NewImageWithPlatform(c, img, pullCtx.PlatformMatcher) + + if pullCtx.Unpack { + if unpacks == 0 { + // Try to unpack is none is done previously. + // This is at least required for schema 1 image. + if err := i.Unpack(ctx, pullCtx.Snapshotter, pullCtx.UnpackOpts...); err != nil { + return nil, errors.Wrapf(err, "failed to unpack image on snapshotter %s", pullCtx.Snapshotter) + } + } + } + + return i, nil +} + +func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, limit int) (images.Image, error) { + store := c.ContentStore() + name, desc, err := rCtx.Resolver.Resolve(ctx, ref) + if err != nil { + return images.Image{}, errors.Wrapf(err, "failed to resolve reference %q", ref) + } + + fetcher, err := rCtx.Resolver.Fetcher(ctx, name) + if err != nil { + return images.Image{}, errors.Wrapf(err, "failed to get fetcher for %q", name) + } + + var ( + handler images.Handler + + isConvertible bool + converterFunc func(context.Context, ocispec.Descriptor) (ocispec.Descriptor, error) + limiter *semaphore.Weighted + ) + + if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 { + schema1Converter := schema1.NewConverter(store, fetcher) + + handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...) + + isConvertible = true + + converterFunc = func(ctx context.Context, _ ocispec.Descriptor) (ocispec.Descriptor, error) { + return schema1Converter.Convert(ctx) + } + } else { + // Get all the children for a descriptor + childrenHandler := images.ChildrenHandler(store) + // Set any children labels for that content + childrenHandler = images.SetChildrenMappedLabels(store, childrenHandler, rCtx.ChildLabelMap) + if rCtx.AllMetadata { + // Filter manifests by platforms but allow to handle manifest + // and configuration for not-target platforms + childrenHandler = remotes.FilterManifestByPlatformHandler(childrenHandler, rCtx.PlatformMatcher) + } else { + // Filter children by platforms if specified. + childrenHandler = images.FilterPlatforms(childrenHandler, rCtx.PlatformMatcher) + } + // Sort and limit manifests if a finite number is needed + if limit > 0 { + childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit) + } + + // set isConvertible to true if there is application/octet-stream media type + convertibleHandler := images.HandlerFunc( + func(_ context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + if desc.MediaType == docker.LegacyConfigMediaType { + isConvertible = true + } + + return []ocispec.Descriptor{}, nil + }, + ) + + appendDistSrcLabelHandler, err := docker.AppendDistributionSourceLabel(store, ref) + if err != nil { + return images.Image{}, err + } + + handlers := append(rCtx.BaseHandlers, + remotes.FetchHandler(store, fetcher), + convertibleHandler, + childrenHandler, + appendDistSrcLabelHandler, + ) + + handler = images.Handlers(handlers...) + + converterFunc = func(ctx context.Context, desc ocispec.Descriptor) (ocispec.Descriptor, error) { + return docker.ConvertManifest(ctx, store, desc) + } + } + + if rCtx.HandlerWrapper != nil { + handler = rCtx.HandlerWrapper(handler) + } + + if rCtx.MaxConcurrentDownloads > 0 { + limiter = semaphore.NewWeighted(int64(rCtx.MaxConcurrentDownloads)) + } + + if err := images.Dispatch(ctx, handler, limiter, desc); err != nil { + return images.Image{}, err + } + + if isConvertible { + if desc, err = converterFunc(ctx, desc); err != nil { + return images.Image{}, err + } + } + + return images.Image{ + Name: name, + Target: desc, + Labels: rCtx.Labels, + }, nil +} + +func (c *Client) createNewImage(ctx context.Context, img images.Image) (images.Image, error) { + is := c.ImageService() + for { + if created, err := is.Create(ctx, img); err != nil { + if !errdefs.IsAlreadyExists(err) { + return images.Image{}, err + } + + updated, err := is.Update(ctx, img) + if err != nil { + // if image was removed, try create again + if errdefs.IsNotFound(err) { + continue + } + return images.Image{}, err + } + + img = updated + } else { + img = created + } + + return img, nil + } +} diff --git a/vendor/github.com/containerd/containerd/reference/docker/reference.go b/vendor/github.com/containerd/containerd/reference/docker/reference.go new file mode 100644 index 0000000000000..0998639b0305c --- /dev/null +++ b/vendor/github.com/containerd/containerd/reference/docker/reference.go @@ -0,0 +1,797 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +// Package docker provides a general type to represent any way of referencing images within the registry. +// Its main purpose is to abstract tags and digests (content-addressable hash). +// +// Grammar +// +// reference := name [ ":" tag ] [ "@" digest ] +// name := [domain '/'] path-component ['/' path-component]* +// domain := domain-component ['.' domain-component]* [':' port-number] +// domain-component := /([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])/ +// port-number := /[0-9]+/ +// path-component := alpha-numeric [separator alpha-numeric]* +// alpha-numeric := /[a-z0-9]+/ +// separator := /[_.]|__|[-]*/ +// +// tag := /[\w][\w.-]{0,127}/ +// +// digest := digest-algorithm ":" digest-hex +// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]* +// digest-algorithm-separator := /[+.-_]/ +// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/ +// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value +// +// identifier := /[a-f0-9]{64}/ +// short-identifier := /[a-f0-9]{6,64}/ +package docker + +import ( + "errors" + "fmt" + "path" + "regexp" + "strings" + + "github.com/opencontainers/go-digest" +) + +const ( + // NameTotalLengthMax is the maximum total number of characters in a repository name. + NameTotalLengthMax = 255 +) + +var ( + // ErrReferenceInvalidFormat represents an error while trying to parse a string as a reference. + ErrReferenceInvalidFormat = errors.New("invalid reference format") + + // ErrTagInvalidFormat represents an error while trying to parse a string as a tag. + ErrTagInvalidFormat = errors.New("invalid tag format") + + // ErrDigestInvalidFormat represents an error while trying to parse a string as a tag. + ErrDigestInvalidFormat = errors.New("invalid digest format") + + // ErrNameContainsUppercase is returned for invalid repository names that contain uppercase characters. + ErrNameContainsUppercase = errors.New("repository name must be lowercase") + + // ErrNameEmpty is returned for empty, invalid repository names. + ErrNameEmpty = errors.New("repository name must have at least one component") + + // ErrNameTooLong is returned when a repository name is longer than NameTotalLengthMax. + ErrNameTooLong = fmt.Errorf("repository name must not be more than %v characters", NameTotalLengthMax) + + // ErrNameNotCanonical is returned when a name is not canonical. + ErrNameNotCanonical = errors.New("repository name must be canonical") +) + +// Reference is an opaque object reference identifier that may include +// modifiers such as a hostname, name, tag, and digest. +type Reference interface { + // String returns the full reference + String() string +} + +// Field provides a wrapper type for resolving correct reference types when +// working with encoding. +type Field struct { + reference Reference +} + +// AsField wraps a reference in a Field for encoding. +func AsField(reference Reference) Field { + return Field{reference} +} + +// Reference unwraps the reference type from the field to +// return the Reference object. This object should be +// of the appropriate type to further check for different +// reference types. +func (f Field) Reference() Reference { + return f.reference +} + +// MarshalText serializes the field to byte text which +// is the string of the reference. +func (f Field) MarshalText() (p []byte, err error) { + return []byte(f.reference.String()), nil +} + +// UnmarshalText parses text bytes by invoking the +// reference parser to ensure the appropriately +// typed reference object is wrapped by field. +func (f *Field) UnmarshalText(p []byte) error { + r, err := Parse(string(p)) + if err != nil { + return err + } + + f.reference = r + return nil +} + +// Named is an object with a full name +type Named interface { + Reference + Name() string +} + +// Tagged is an object which has a tag +type Tagged interface { + Reference + Tag() string +} + +// NamedTagged is an object including a name and tag. +type NamedTagged interface { + Named + Tag() string +} + +// Digested is an object which has a digest +// in which it can be referenced by +type Digested interface { + Reference + Digest() digest.Digest +} + +// Canonical reference is an object with a fully unique +// name including a name with domain and digest +type Canonical interface { + Named + Digest() digest.Digest +} + +// namedRepository is a reference to a repository with a name. +// A namedRepository has both domain and path components. +type namedRepository interface { + Named + Domain() string + Path() string +} + +// Domain returns the domain part of the Named reference +func Domain(named Named) string { + if r, ok := named.(namedRepository); ok { + return r.Domain() + } + domain, _ := splitDomain(named.Name()) + return domain +} + +// Path returns the name without the domain part of the Named reference +func Path(named Named) (name string) { + if r, ok := named.(namedRepository); ok { + return r.Path() + } + _, path := splitDomain(named.Name()) + return path +} + +func splitDomain(name string) (string, string) { + match := anchoredNameRegexp.FindStringSubmatch(name) + if len(match) != 3 { + return "", name + } + return match[1], match[2] +} + +// SplitHostname splits a named reference into a +// hostname and name string. If no valid hostname is +// found, the hostname is empty and the full value +// is returned as name +// DEPRECATED: Use Domain or Path +func SplitHostname(named Named) (string, string) { + if r, ok := named.(namedRepository); ok { + return r.Domain(), r.Path() + } + return splitDomain(named.Name()) +} + +// Parse parses s and returns a syntactically valid Reference. +// If an error was encountered it is returned, along with a nil Reference. +// NOTE: Parse will not handle short digests. +func Parse(s string) (Reference, error) { + matches := ReferenceRegexp.FindStringSubmatch(s) + if matches == nil { + if s == "" { + return nil, ErrNameEmpty + } + if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil { + return nil, ErrNameContainsUppercase + } + return nil, ErrReferenceInvalidFormat + } + + if len(matches[1]) > NameTotalLengthMax { + return nil, ErrNameTooLong + } + + var repo repository + + nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1]) + if len(nameMatch) == 3 { + repo.domain = nameMatch[1] + repo.path = nameMatch[2] + } else { + repo.domain = "" + repo.path = matches[1] + } + + ref := reference{ + namedRepository: repo, + tag: matches[2], + } + if matches[3] != "" { + var err error + ref.digest, err = digest.Parse(matches[3]) + if err != nil { + return nil, err + } + } + + r := getBestReferenceType(ref) + if r == nil { + return nil, ErrNameEmpty + } + + return r, nil +} + +// ParseNamed parses s and returns a syntactically valid reference implementing +// the Named interface. The reference must have a name and be in the canonical +// form, otherwise an error is returned. +// If an error was encountered it is returned, along with a nil Reference. +// NOTE: ParseNamed will not handle short digests. +func ParseNamed(s string) (Named, error) { + named, err := ParseNormalizedNamed(s) + if err != nil { + return nil, err + } + if named.String() != s { + return nil, ErrNameNotCanonical + } + return named, nil +} + +// WithName returns a named object representing the given string. If the input +// is invalid ErrReferenceInvalidFormat will be returned. +func WithName(name string) (Named, error) { + if len(name) > NameTotalLengthMax { + return nil, ErrNameTooLong + } + + match := anchoredNameRegexp.FindStringSubmatch(name) + if match == nil || len(match) != 3 { + return nil, ErrReferenceInvalidFormat + } + return repository{ + domain: match[1], + path: match[2], + }, nil +} + +// WithTag combines the name from "name" and the tag from "tag" to form a +// reference incorporating both the name and the tag. +func WithTag(name Named, tag string) (NamedTagged, error) { + if !anchoredTagRegexp.MatchString(tag) { + return nil, ErrTagInvalidFormat + } + var repo repository + if r, ok := name.(namedRepository); ok { + repo.domain = r.Domain() + repo.path = r.Path() + } else { + repo.path = name.Name() + } + if canonical, ok := name.(Canonical); ok { + return reference{ + namedRepository: repo, + tag: tag, + digest: canonical.Digest(), + }, nil + } + return taggedReference{ + namedRepository: repo, + tag: tag, + }, nil +} + +// WithDigest combines the name from "name" and the digest from "digest" to form +// a reference incorporating both the name and the digest. +func WithDigest(name Named, digest digest.Digest) (Canonical, error) { + if !anchoredDigestRegexp.MatchString(digest.String()) { + return nil, ErrDigestInvalidFormat + } + var repo repository + if r, ok := name.(namedRepository); ok { + repo.domain = r.Domain() + repo.path = r.Path() + } else { + repo.path = name.Name() + } + if tagged, ok := name.(Tagged); ok { + return reference{ + namedRepository: repo, + tag: tagged.Tag(), + digest: digest, + }, nil + } + return canonicalReference{ + namedRepository: repo, + digest: digest, + }, nil +} + +// TrimNamed removes any tag or digest from the named reference. +func TrimNamed(ref Named) Named { + domain, path := SplitHostname(ref) + return repository{ + domain: domain, + path: path, + } +} + +func getBestReferenceType(ref reference) Reference { + if ref.Name() == "" { + // Allow digest only references + if ref.digest != "" { + return digestReference(ref.digest) + } + return nil + } + if ref.tag == "" { + if ref.digest != "" { + return canonicalReference{ + namedRepository: ref.namedRepository, + digest: ref.digest, + } + } + return ref.namedRepository + } + if ref.digest == "" { + return taggedReference{ + namedRepository: ref.namedRepository, + tag: ref.tag, + } + } + + return ref +} + +type reference struct { + namedRepository + tag string + digest digest.Digest +} + +func (r reference) String() string { + return r.Name() + ":" + r.tag + "@" + r.digest.String() +} + +func (r reference) Tag() string { + return r.tag +} + +func (r reference) Digest() digest.Digest { + return r.digest +} + +type repository struct { + domain string + path string +} + +func (r repository) String() string { + return r.Name() +} + +func (r repository) Name() string { + if r.domain == "" { + return r.path + } + return r.domain + "/" + r.path +} + +func (r repository) Domain() string { + return r.domain +} + +func (r repository) Path() string { + return r.path +} + +type digestReference digest.Digest + +func (d digestReference) String() string { + return digest.Digest(d).String() +} + +func (d digestReference) Digest() digest.Digest { + return digest.Digest(d) +} + +type taggedReference struct { + namedRepository + tag string +} + +func (t taggedReference) String() string { + return t.Name() + ":" + t.tag +} + +func (t taggedReference) Tag() string { + return t.tag +} + +type canonicalReference struct { + namedRepository + digest digest.Digest +} + +func (c canonicalReference) String() string { + return c.Name() + "@" + c.digest.String() +} + +func (c canonicalReference) Digest() digest.Digest { + return c.digest +} + +var ( + // alphaNumericRegexp defines the alpha numeric atom, typically a + // component of names. This only allows lower case characters and digits. + alphaNumericRegexp = match(`[a-z0-9]+`) + + // separatorRegexp defines the separators allowed to be embedded in name + // components. This allow one period, one or two underscore and multiple + // dashes. + separatorRegexp = match(`(?:[._]|__|[-]*)`) + + // nameComponentRegexp restricts registry path component names to start + // with at least one letter or number, with following parts able to be + // separated by one period, one or two underscore and multiple dashes. + nameComponentRegexp = expression( + alphaNumericRegexp, + optional(repeated(separatorRegexp, alphaNumericRegexp))) + + // domainComponentRegexp restricts the registry domain component of a + // repository name to start with a component as defined by DomainRegexp + // and followed by an optional port. + domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`) + + // DomainRegexp defines the structure of potential domain components + // that may be part of image names. This is purposely a subset of what is + // allowed by DNS to ensure backwards compatibility with Docker image + // names. + DomainRegexp = expression( + domainComponentRegexp, + optional(repeated(literal(`.`), domainComponentRegexp)), + optional(literal(`:`), match(`[0-9]+`))) + + // TagRegexp matches valid tag names. From docker/docker:graph/tags.go. + TagRegexp = match(`[\w][\w.-]{0,127}`) + + // anchoredTagRegexp matches valid tag names, anchored at the start and + // end of the matched string. + anchoredTagRegexp = anchored(TagRegexp) + + // DigestRegexp matches valid digests. + DigestRegexp = match(`[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,}`) + + // anchoredDigestRegexp matches valid digests, anchored at the start and + // end of the matched string. + anchoredDigestRegexp = anchored(DigestRegexp) + + // NameRegexp is the format for the name component of references. The + // regexp has capturing groups for the domain and name part omitting + // the separating forward slash from either. + NameRegexp = expression( + optional(DomainRegexp, literal(`/`)), + nameComponentRegexp, + optional(repeated(literal(`/`), nameComponentRegexp))) + + // anchoredNameRegexp is used to parse a name value, capturing the + // domain and trailing components. + anchoredNameRegexp = anchored( + optional(capture(DomainRegexp), literal(`/`)), + capture(nameComponentRegexp, + optional(repeated(literal(`/`), nameComponentRegexp)))) + + // ReferenceRegexp is the full supported format of a reference. The regexp + // is anchored and has capturing groups for name, tag, and digest + // components. + ReferenceRegexp = anchored(capture(NameRegexp), + optional(literal(":"), capture(TagRegexp)), + optional(literal("@"), capture(DigestRegexp))) + + // IdentifierRegexp is the format for string identifier used as a + // content addressable identifier using sha256. These identifiers + // are like digests without the algorithm, since sha256 is used. + IdentifierRegexp = match(`([a-f0-9]{64})`) + + // ShortIdentifierRegexp is the format used to represent a prefix + // of an identifier. A prefix may be used to match a sha256 identifier + // within a list of trusted identifiers. + ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`) + + // anchoredIdentifierRegexp is used to check or match an + // identifier value, anchored at start and end of string. + anchoredIdentifierRegexp = anchored(IdentifierRegexp) +) + +// match compiles the string to a regular expression. +var match = regexp.MustCompile + +// literal compiles s into a literal regular expression, escaping any regexp +// reserved characters. +func literal(s string) *regexp.Regexp { + re := match(regexp.QuoteMeta(s)) + + if _, complete := re.LiteralPrefix(); !complete { + panic("must be a literal") + } + + return re +} + +// expression defines a full expression, where each regular expression must +// follow the previous. +func expression(res ...*regexp.Regexp) *regexp.Regexp { + var s string + for _, re := range res { + s += re.String() + } + + return match(s) +} + +// optional wraps the expression in a non-capturing group and makes the +// production optional. +func optional(res ...*regexp.Regexp) *regexp.Regexp { + return match(group(expression(res...)).String() + `?`) +} + +// repeated wraps the regexp in a non-capturing group to get one or more +// matches. +func repeated(res ...*regexp.Regexp) *regexp.Regexp { + return match(group(expression(res...)).String() + `+`) +} + +// group wraps the regexp in a non-capturing group. +func group(res ...*regexp.Regexp) *regexp.Regexp { + return match(`(?:` + expression(res...).String() + `)`) +} + +// capture wraps the expression in a capturing group. +func capture(res ...*regexp.Regexp) *regexp.Regexp { + return match(`(` + expression(res...).String() + `)`) +} + +// anchored anchors the regular expression by adding start and end delimiters. +func anchored(res ...*regexp.Regexp) *regexp.Regexp { + return match(`^` + expression(res...).String() + `$`) +} + +var ( + legacyDefaultDomain = "index.docker.io" + defaultDomain = "docker.io" + officialRepoName = "library" + defaultTag = "latest" +) + +// normalizedNamed represents a name which has been +// normalized and has a familiar form. A familiar name +// is what is used in Docker UI. An example normalized +// name is "docker.io/library/ubuntu" and corresponding +// familiar name of "ubuntu". +type normalizedNamed interface { + Named + Familiar() Named +} + +// ParseNormalizedNamed parses a string into a named reference +// transforming a familiar name from Docker UI to a fully +// qualified reference. If the value may be an identifier +// use ParseAnyReference. +func ParseNormalizedNamed(s string) (Named, error) { + if ok := anchoredIdentifierRegexp.MatchString(s); ok { + return nil, fmt.Errorf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings", s) + } + domain, remainder := splitDockerDomain(s) + var remoteName string + if tagSep := strings.IndexRune(remainder, ':'); tagSep > -1 { + remoteName = remainder[:tagSep] + } else { + remoteName = remainder + } + if strings.ToLower(remoteName) != remoteName { + return nil, errors.New("invalid reference format: repository name must be lowercase") + } + + ref, err := Parse(domain + "/" + remainder) + if err != nil { + return nil, err + } + named, isNamed := ref.(Named) + if !isNamed { + return nil, fmt.Errorf("reference %s has no name", ref.String()) + } + return named, nil +} + +// ParseDockerRef normalizes the image reference following the docker convention. This is added +// mainly for backward compatibility. +// The reference returned can only be either tagged or digested. For reference contains both tag +// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@ +// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as +// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa. +func ParseDockerRef(ref string) (Named, error) { + named, err := ParseNormalizedNamed(ref) + if err != nil { + return nil, err + } + if _, ok := named.(NamedTagged); ok { + if canonical, ok := named.(Canonical); ok { + // The reference is both tagged and digested, only + // return digested. + newNamed, err := WithName(canonical.Name()) + if err != nil { + return nil, err + } + newCanonical, err := WithDigest(newNamed, canonical.Digest()) + if err != nil { + return nil, err + } + return newCanonical, nil + } + } + return TagNameOnly(named), nil +} + +// splitDockerDomain splits a repository name to domain and remotename string. +// If no valid domain is found, the default domain is used. Repository name +// needs to be already validated before. +func splitDockerDomain(name string) (domain, remainder string) { + i := strings.IndexRune(name, '/') + if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") { + domain, remainder = defaultDomain, name + } else { + domain, remainder = name[:i], name[i+1:] + } + if domain == legacyDefaultDomain { + domain = defaultDomain + } + if domain == defaultDomain && !strings.ContainsRune(remainder, '/') { + remainder = officialRepoName + "/" + remainder + } + return +} + +// familiarizeName returns a shortened version of the name familiar +// to to the Docker UI. Familiar names have the default domain +// "docker.io" and "library/" repository prefix removed. +// For example, "docker.io/library/redis" will have the familiar +// name "redis" and "docker.io/dmcgowan/myapp" will be "dmcgowan/myapp". +// Returns a familiarized named only reference. +func familiarizeName(named namedRepository) repository { + repo := repository{ + domain: named.Domain(), + path: named.Path(), + } + + if repo.domain == defaultDomain { + repo.domain = "" + // Handle official repositories which have the pattern "library/" + if split := strings.Split(repo.path, "/"); len(split) == 2 && split[0] == officialRepoName { + repo.path = split[1] + } + } + return repo +} + +func (r reference) Familiar() Named { + return reference{ + namedRepository: familiarizeName(r.namedRepository), + tag: r.tag, + digest: r.digest, + } +} + +func (r repository) Familiar() Named { + return familiarizeName(r) +} + +func (t taggedReference) Familiar() Named { + return taggedReference{ + namedRepository: familiarizeName(t.namedRepository), + tag: t.tag, + } +} + +func (c canonicalReference) Familiar() Named { + return canonicalReference{ + namedRepository: familiarizeName(c.namedRepository), + digest: c.digest, + } +} + +// TagNameOnly adds the default tag "latest" to a reference if it only has +// a repo name. +func TagNameOnly(ref Named) Named { + if IsNameOnly(ref) { + namedTagged, err := WithTag(ref, defaultTag) + if err != nil { + // Default tag must be valid, to create a NamedTagged + // type with non-validated input the WithTag function + // should be used instead + panic(err) + } + return namedTagged + } + return ref +} + +// ParseAnyReference parses a reference string as a possible identifier, +// full digest, or familiar name. +func ParseAnyReference(ref string) (Reference, error) { + if ok := anchoredIdentifierRegexp.MatchString(ref); ok { + return digestReference("sha256:" + ref), nil + } + if dgst, err := digest.Parse(ref); err == nil { + return digestReference(dgst), nil + } + + return ParseNormalizedNamed(ref) +} + +// IsNameOnly returns true if reference only contains a repo name. +func IsNameOnly(ref Named) bool { + if _, ok := ref.(NamedTagged); ok { + return false + } + if _, ok := ref.(Canonical); ok { + return false + } + return true +} + +// FamiliarName returns the familiar name string +// for the given named, familiarizing if needed. +func FamiliarName(ref Named) string { + if nn, ok := ref.(normalizedNamed); ok { + return nn.Familiar().Name() + } + return ref.Name() +} + +// FamiliarString returns the familiar string representation +// for the given reference, familiarizing if needed. +func FamiliarString(ref Reference) string { + if nn, ok := ref.(normalizedNamed); ok { + return nn.Familiar().String() + } + return ref.String() +} + +// FamiliarMatch reports whether ref matches the specified pattern. +// See https://godoc.org/path#Match for supported patterns. +func FamiliarMatch(pattern string, ref Reference) (bool, error) { + matched, err := path.Match(pattern, FamiliarString(ref)) + if namedRef, isNamed := ref.(Named); isNamed && !matched { + matched, _ = path.Match(pattern, FamiliarName(namedRef)) + } + return matched, err +} diff --git a/vendor/github.com/containerd/containerd/reference/reference.go b/vendor/github.com/containerd/containerd/reference/reference.go index 79f165de02656..a4bf6da6019a7 100644 --- a/vendor/github.com/containerd/containerd/reference/reference.go +++ b/vendor/github.com/containerd/containerd/reference/reference.go @@ -85,6 +85,10 @@ var splitRe = regexp.MustCompile(`[:@]`) // Parse parses the string into a structured ref. func Parse(s string) (Spec, error) { + if strings.Contains(s, "://") { + return Spec{}, ErrInvalid + } + u, err := url.Parse("dummy://" + s) if err != nil { return Spec{}, err @@ -124,7 +128,7 @@ func (r Spec) Hostname() string { i := strings.Index(r.Locator, "/") if i < 0 { - i = len(r.Locator) + 1 + return r.Locator } return r.Locator[:i] } diff --git a/vendor/github.com/containerd/containerd/remotes/docker/auth.go b/vendor/github.com/containerd/containerd/remotes/docker/auth.go deleted file mode 100644 index 80bcb9dcf76a7..0000000000000 --- a/vendor/github.com/containerd/containerd/remotes/docker/auth.go +++ /dev/null @@ -1,198 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package docker - -import ( - "net/http" - "sort" - "strings" -) - -type authenticationScheme byte - -const ( - basicAuth authenticationScheme = 1 << iota // Defined in RFC 7617 - digestAuth // Defined in RFC 7616 - bearerAuth // Defined in RFC 6750 -) - -// challenge carries information from a WWW-Authenticate response header. -// See RFC 2617. -type challenge struct { - // scheme is the auth-scheme according to RFC 2617 - scheme authenticationScheme - - // parameters are the auth-params according to RFC 2617 - parameters map[string]string -} - -type byScheme []challenge - -func (bs byScheme) Len() int { return len(bs) } -func (bs byScheme) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] } - -// Sort in priority order: token > digest > basic -func (bs byScheme) Less(i, j int) bool { return bs[i].scheme > bs[j].scheme } - -// Octet types from RFC 2616. -type octetType byte - -var octetTypes [256]octetType - -const ( - isToken octetType = 1 << iota - isSpace -) - -func init() { - // OCTET = - // CHAR = - // CTL = - // CR = - // LF = - // SP = - // HT = - // <"> = - // CRLF = CR LF - // LWS = [CRLF] 1*( SP | HT ) - // TEXT = - // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> - // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT - // token = 1* - // qdtext = > - - for c := 0; c < 256; c++ { - var t octetType - isCtl := c <= 31 || c == 127 - isChar := 0 <= c && c <= 127 - isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 - if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { - t |= isSpace - } - if isChar && !isCtl && !isSeparator { - t |= isToken - } - octetTypes[c] = t - } -} - -func parseAuthHeader(header http.Header) []challenge { - challenges := []challenge{} - for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] { - v, p := parseValueAndParams(h) - var s authenticationScheme - switch v { - case "basic": - s = basicAuth - case "digest": - s = digestAuth - case "bearer": - s = bearerAuth - default: - continue - } - challenges = append(challenges, challenge{scheme: s, parameters: p}) - } - sort.Stable(byScheme(challenges)) - return challenges -} - -func parseValueAndParams(header string) (value string, params map[string]string) { - params = make(map[string]string) - value, s := expectToken(header) - if value == "" { - return - } - value = strings.ToLower(value) - for { - var pkey string - pkey, s = expectToken(skipSpace(s)) - if pkey == "" { - return - } - if !strings.HasPrefix(s, "=") { - return - } - var pvalue string - pvalue, s = expectTokenOrQuoted(s[1:]) - if pvalue == "" { - return - } - pkey = strings.ToLower(pkey) - params[pkey] = pvalue - s = skipSpace(s) - if !strings.HasPrefix(s, ",") { - return - } - s = s[1:] - } -} - -func skipSpace(s string) (rest string) { - i := 0 - for ; i < len(s); i++ { - if octetTypes[s[i]]&isSpace == 0 { - break - } - } - return s[i:] -} - -func expectToken(s string) (token, rest string) { - i := 0 - for ; i < len(s); i++ { - if octetTypes[s[i]]&isToken == 0 { - break - } - } - return s[:i], s[i:] -} - -func expectTokenOrQuoted(s string) (value string, rest string) { - if !strings.HasPrefix(s, "\"") { - return expectToken(s) - } - s = s[1:] - for i := 0; i < len(s); i++ { - switch s[i] { - case '"': - return s[:i], s[i+1:] - case '\\': - p := make([]byte, len(s)-1) - j := copy(p, s[:i]) - escape := true - for i = i + 1; i < len(s); i++ { - b := s[i] - switch { - case escape: - escape = false - p[j] = b - j++ - case b == '\\': - escape = true - case b == '"': - return string(p[:j]), s[i+1:] - default: - p[j] = b - j++ - } - } - return "", "" - } - } - return "", "" -} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/auth/fetch.go b/vendor/github.com/containerd/containerd/remotes/docker/auth/fetch.go new file mode 100644 index 0000000000000..8b0a87e755fc8 --- /dev/null +++ b/vendor/github.com/containerd/containerd/remotes/docker/auth/fetch.go @@ -0,0 +1,209 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package auth + +import ( + "context" + "encoding/json" + "net/http" + "net/url" + "strings" + "time" + + "github.com/containerd/containerd/log" + remoteserrors "github.com/containerd/containerd/remotes/errors" + "github.com/containerd/containerd/version" + "github.com/pkg/errors" + "golang.org/x/net/context/ctxhttp" +) + +var ( + // ErrNoToken is returned if a request is successful but the body does not + // contain an authorization token. + ErrNoToken = errors.New("authorization server did not include a token in the response") +) + +// GenerateTokenOptions generates options for fetching a token based on a challenge +func GenerateTokenOptions(ctx context.Context, host, username, secret string, c Challenge) (TokenOptions, error) { + realm, ok := c.Parameters["realm"] + if !ok { + return TokenOptions{}, errors.New("no realm specified for token auth challenge") + } + + realmURL, err := url.Parse(realm) + if err != nil { + return TokenOptions{}, errors.Wrap(err, "invalid token auth challenge realm") + } + + to := TokenOptions{ + Realm: realmURL.String(), + Service: c.Parameters["service"], + Username: username, + Secret: secret, + } + + scope, ok := c.Parameters["scope"] + if ok { + to.Scopes = append(to.Scopes, scope) + } else { + log.G(ctx).WithField("host", host).Debug("no scope specified for token auth challenge") + } + + return to, nil +} + +// TokenOptions are options for requesting a token +type TokenOptions struct { + Realm string + Service string + Scopes []string + Username string + Secret string +} + +// OAuthTokenResponse is response from fetching token with a OAuth POST request +type OAuthTokenResponse struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + ExpiresIn int `json:"expires_in"` + IssuedAt time.Time `json:"issued_at"` + Scope string `json:"scope"` +} + +// FetchTokenWithOAuth fetches a token using a POST request +func FetchTokenWithOAuth(ctx context.Context, client *http.Client, headers http.Header, clientID string, to TokenOptions) (*OAuthTokenResponse, error) { + form := url.Values{} + if len(to.Scopes) > 0 { + form.Set("scope", strings.Join(to.Scopes, " ")) + } + form.Set("service", to.Service) + form.Set("client_id", clientID) + + if to.Username == "" { + form.Set("grant_type", "refresh_token") + form.Set("refresh_token", to.Secret) + } else { + form.Set("grant_type", "password") + form.Set("username", to.Username) + form.Set("password", to.Secret) + } + + req, err := http.NewRequest("POST", to.Realm, strings.NewReader(form.Encode())) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8") + for k, v := range headers { + req.Header[k] = append(req.Header[k], v...) + } + if len(req.Header.Get("User-Agent")) == 0 { + req.Header.Set("User-Agent", "containerd/"+version.Version) + } + + resp, err := ctxhttp.Do(ctx, client, req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + return nil, errors.WithStack(remoteserrors.NewUnexpectedStatusErr(resp)) + } + + decoder := json.NewDecoder(resp.Body) + + var tr OAuthTokenResponse + if err = decoder.Decode(&tr); err != nil { + return nil, errors.Wrap(err, "unable to decode token response") + } + + if tr.AccessToken == "" { + return nil, errors.WithStack(ErrNoToken) + } + + return &tr, nil +} + +// FetchTokenResponse is response from fetching token with GET request +type FetchTokenResponse struct { + Token string `json:"token"` + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + IssuedAt time.Time `json:"issued_at"` + RefreshToken string `json:"refresh_token"` +} + +// FetchToken fetches a token using a GET request +func FetchToken(ctx context.Context, client *http.Client, headers http.Header, to TokenOptions) (*FetchTokenResponse, error) { + req, err := http.NewRequest("GET", to.Realm, nil) + if err != nil { + return nil, err + } + + for k, v := range headers { + req.Header[k] = append(req.Header[k], v...) + } + if len(req.Header.Get("User-Agent")) == 0 { + req.Header.Set("User-Agent", "containerd/"+version.Version) + } + + reqParams := req.URL.Query() + + if to.Service != "" { + reqParams.Add("service", to.Service) + } + + for _, scope := range to.Scopes { + reqParams.Add("scope", scope) + } + + if to.Secret != "" { + req.SetBasicAuth(to.Username, to.Secret) + } + + req.URL.RawQuery = reqParams.Encode() + + resp, err := ctxhttp.Do(ctx, client, req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + return nil, errors.WithStack(remoteserrors.NewUnexpectedStatusErr(resp)) + } + + decoder := json.NewDecoder(resp.Body) + + var tr FetchTokenResponse + if err = decoder.Decode(&tr); err != nil { + return nil, errors.Wrap(err, "unable to decode token response") + } + + // `access_token` is equivalent to `token` and if both are specified + // the choice is undefined. Canonicalize `access_token` by sticking + // things in `token`. + if tr.AccessToken != "" { + tr.Token = tr.AccessToken + } + + if tr.Token == "" { + return nil, errors.WithStack(ErrNoToken) + } + + return &tr, nil +} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/auth/parse.go b/vendor/github.com/containerd/containerd/remotes/docker/auth/parse.go new file mode 100644 index 0000000000000..223fa2d0524f0 --- /dev/null +++ b/vendor/github.com/containerd/containerd/remotes/docker/auth/parse.go @@ -0,0 +1,203 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package auth + +import ( + "net/http" + "sort" + "strings" +) + +// AuthenticationScheme defines scheme of the authentication method +type AuthenticationScheme byte + +const ( + // BasicAuth is scheme for Basic HTTP Authentication RFC 7617 + BasicAuth AuthenticationScheme = 1 << iota + // DigestAuth is scheme for HTTP Digest Access Authentication RFC 7616 + DigestAuth + // BearerAuth is scheme for OAuth 2.0 Bearer Tokens RFC 6750 + BearerAuth +) + +// Challenge carries information from a WWW-Authenticate response header. +// See RFC 2617. +type Challenge struct { + // scheme is the auth-scheme according to RFC 2617 + Scheme AuthenticationScheme + + // parameters are the auth-params according to RFC 2617 + Parameters map[string]string +} + +type byScheme []Challenge + +func (bs byScheme) Len() int { return len(bs) } +func (bs byScheme) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] } + +// Sort in priority order: token > digest > basic +func (bs byScheme) Less(i, j int) bool { return bs[i].Scheme > bs[j].Scheme } + +// Octet types from RFC 2616. +type octetType byte + +var octetTypes [256]octetType + +const ( + isToken octetType = 1 << iota + isSpace +) + +func init() { + // OCTET = + // CHAR = + // CTL = + // CR = + // LF = + // SP = + // HT = + // <"> = + // CRLF = CR LF + // LWS = [CRLF] 1*( SP | HT ) + // TEXT = + // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> + // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT + // token = 1* + // qdtext = > + + for c := 0; c < 256; c++ { + var t octetType + isCtl := c <= 31 || c == 127 + isChar := 0 <= c && c <= 127 + isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) + if strings.ContainsRune(" \t\r\n", rune(c)) { + t |= isSpace + } + if isChar && !isCtl && !isSeparator { + t |= isToken + } + octetTypes[c] = t + } +} + +// ParseAuthHeader parses challenges from WWW-Authenticate header +func ParseAuthHeader(header http.Header) []Challenge { + challenges := []Challenge{} + for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] { + v, p := parseValueAndParams(h) + var s AuthenticationScheme + switch v { + case "basic": + s = BasicAuth + case "digest": + s = DigestAuth + case "bearer": + s = BearerAuth + default: + continue + } + challenges = append(challenges, Challenge{Scheme: s, Parameters: p}) + } + sort.Stable(byScheme(challenges)) + return challenges +} + +func parseValueAndParams(header string) (value string, params map[string]string) { + params = make(map[string]string) + value, s := expectToken(header) + if value == "" { + return + } + value = strings.ToLower(value) + for { + var pkey string + pkey, s = expectToken(skipSpace(s)) + if pkey == "" { + return + } + if !strings.HasPrefix(s, "=") { + return + } + var pvalue string + pvalue, s = expectTokenOrQuoted(s[1:]) + if pvalue == "" { + return + } + pkey = strings.ToLower(pkey) + params[pkey] = pvalue + s = skipSpace(s) + if !strings.HasPrefix(s, ",") { + return + } + s = s[1:] + } +} + +func skipSpace(s string) (rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isSpace == 0 { + break + } + } + return s[i:] +} + +func expectToken(s string) (token, rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isToken == 0 { + break + } + } + return s[:i], s[i:] +} + +func expectTokenOrQuoted(s string) (value string, rest string) { + if !strings.HasPrefix(s, "\"") { + return expectToken(s) + } + s = s[1:] + for i := 0; i < len(s); i++ { + switch s[i] { + case '"': + return s[:i], s[i+1:] + case '\\': + p := make([]byte, len(s)-1) + j := copy(p, s[:i]) + escape := true + for i = i + 1; i < len(s); i++ { + b := s[i] + switch { + case escape: + escape = false + p[j] = b + j++ + case b == '\\': + escape = true + case b == '"': + return string(p[:j]), s[i+1:] + default: + p[j] = b + j++ + } + } + return "", "" + } + } + return "", "" +} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go b/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go index bb691f183f8a6..67e4aea8da8f5 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go @@ -19,281 +19,297 @@ package docker import ( "context" "encoding/base64" - "encoding/json" "fmt" - "io" - "io/ioutil" "net/http" - "net/url" "strings" "sync" - "time" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/log" + "github.com/containerd/containerd/remotes/docker/auth" + remoteerrors "github.com/containerd/containerd/remotes/errors" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "golang.org/x/net/context/ctxhttp" ) type dockerAuthorizer struct { credentials func(string) (string, string, error) client *http.Client + header http.Header mu sync.Mutex - auth map[string]string + // indexed by host name + handlers map[string]*authHandler } // NewAuthorizer creates a Docker authorizer using the provided function to // get credentials for the token server or basic auth. +// Deprecated: Use NewDockerAuthorizer func NewAuthorizer(client *http.Client, f func(string) (string, string, error)) Authorizer { - if client == nil { - client = http.DefaultClient + return NewDockerAuthorizer(WithAuthClient(client), WithAuthCreds(f)) +} + +type authorizerConfig struct { + credentials func(string) (string, string, error) + client *http.Client + header http.Header +} + +// AuthorizerOpt configures an authorizer +type AuthorizerOpt func(*authorizerConfig) + +// WithAuthClient provides the HTTP client for the authorizer +func WithAuthClient(client *http.Client) AuthorizerOpt { + return func(opt *authorizerConfig) { + opt.client = client + } +} + +// WithAuthCreds provides a credential function to the authorizer +func WithAuthCreds(creds func(string) (string, string, error)) AuthorizerOpt { + return func(opt *authorizerConfig) { + opt.credentials = creds + } +} + +// WithAuthHeader provides HTTP headers for authorization +func WithAuthHeader(hdr http.Header) AuthorizerOpt { + return func(opt *authorizerConfig) { + opt.header = hdr } +} + +// NewDockerAuthorizer creates an authorizer using Docker's registry +// authentication spec. +// See https://docs.docker.com/registry/spec/auth/ +func NewDockerAuthorizer(opts ...AuthorizerOpt) Authorizer { + var ao authorizerConfig + for _, opt := range opts { + opt(&ao) + } + + if ao.client == nil { + ao.client = http.DefaultClient + } + return &dockerAuthorizer{ - credentials: f, - client: client, - auth: map[string]string{}, + credentials: ao.credentials, + client: ao.client, + header: ao.header, + handlers: make(map[string]*authHandler), } } +// Authorize handles auth request. func (a *dockerAuthorizer) Authorize(ctx context.Context, req *http.Request) error { - // TODO: Lookup matching challenge and scope rather than just host - if auth := a.getAuth(req.URL.Host); auth != "" { - req.Header.Set("Authorization", auth) + // skip if there is no auth handler + ah := a.getAuthHandler(req.URL.Host) + if ah == nil { + return nil } + auth, err := ah.authorize(ctx) + if err != nil { + return err + } + + req.Header.Set("Authorization", auth) return nil } +func (a *dockerAuthorizer) getAuthHandler(host string) *authHandler { + a.mu.Lock() + defer a.mu.Unlock() + + return a.handlers[host] +} + func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.Response) error { last := responses[len(responses)-1] host := last.Request.URL.Host - for _, c := range parseAuthHeader(last.Header) { - if c.scheme == bearerAuth { + + a.mu.Lock() + defer a.mu.Unlock() + for _, c := range auth.ParseAuthHeader(last.Header) { + if c.Scheme == auth.BearerAuth { if err := invalidAuthorization(c, responses); err != nil { - // TODO: Clear token - a.setAuth(host, "") + delete(a.handlers, host) return err } - // TODO(dmcg): Store challenge, not token - // Move token fetching to authorize - if err := a.setTokenAuth(ctx, host, c.parameters); err != nil { + // reuse existing handler + // + // assume that one registry will return the common + // challenge information, including realm and service. + // and the resource scope is only different part + // which can be provided by each request. + if _, ok := a.handlers[host]; ok { + return nil + } + + var username, secret string + if a.credentials != nil { + var err error + username, secret, err = a.credentials(host) + if err != nil { + return err + } + } + + common, err := auth.GenerateTokenOptions(ctx, host, username, secret, c) + if err != nil { return err } + a.handlers[host] = newAuthHandler(a.client, a.header, c.Scheme, common) return nil - } else if c.scheme == basicAuth { - // TODO: Resolve credentials on authorize + } else if c.Scheme == auth.BasicAuth && a.credentials != nil { username, secret, err := a.credentials(host) if err != nil { return err } + if username != "" && secret != "" { - auth := username + ":" + secret - a.setAuth(host, fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(auth)))) + common := auth.TokenOptions{ + Username: username, + Secret: secret, + } + + a.handlers[host] = newAuthHandler(a.client, a.header, c.Scheme, common) return nil } } } - return errors.Wrap(errdefs.ErrNotImplemented, "failed to find supported auth scheme") } -func (a *dockerAuthorizer) getAuth(host string) string { - a.mu.Lock() - defer a.mu.Unlock() - - return a.auth[host] -} - -func (a *dockerAuthorizer) setAuth(host string, auth string) bool { - a.mu.Lock() - defer a.mu.Unlock() - - changed := a.auth[host] != auth - a.auth[host] = auth - - return changed +// authResult is used to control limit rate. +type authResult struct { + sync.WaitGroup + token string + err error } -func (a *dockerAuthorizer) setTokenAuth(ctx context.Context, host string, params map[string]string) error { - realm, ok := params["realm"] - if !ok { - return errors.New("no realm specified for token auth challenge") - } - - realmURL, err := url.Parse(realm) - if err != nil { - return errors.Wrap(err, "invalid token auth challenge realm") - } - - to := tokenOptions{ - realm: realmURL.String(), - service: params["service"], - } +// authHandler is used to handle auth request per registry server. +type authHandler struct { + sync.Mutex - to.scopes = getTokenScopes(ctx, params) - if len(to.scopes) == 0 { - return errors.Errorf("no scope specified for token auth challenge") - } + header http.Header - if a.credentials != nil { - to.username, to.secret, err = a.credentials(host) - if err != nil { - return err - } - } - - var token string - if to.secret != "" { - // Credential information is provided, use oauth POST endpoint - token, err = a.fetchTokenWithOAuth(ctx, to) - if err != nil { - return errors.Wrap(err, "failed to fetch oauth token") - } - } else { - // Do request anonymously - token, err = a.fetchToken(ctx, to) - if err != nil { - return errors.Wrap(err, "failed to fetch anonymous token") - } - } - a.setAuth(host, fmt.Sprintf("Bearer %s", token)) + client *http.Client - return nil -} + // only support basic and bearer schemes + scheme auth.AuthenticationScheme -type tokenOptions struct { - realm string - service string - scopes []string - username string - secret string -} + // common contains common challenge answer + common auth.TokenOptions -type postTokenResponse struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - ExpiresIn int `json:"expires_in"` - IssuedAt time.Time `json:"issued_at"` - Scope string `json:"scope"` + // scopedTokens caches token indexed by scopes, which used in + // bearer auth case + scopedTokens map[string]*authResult } -func (a *dockerAuthorizer) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (string, error) { - form := url.Values{} - form.Set("scope", strings.Join(to.scopes, " ")) - form.Set("service", to.service) - // TODO: Allow setting client_id - form.Set("client_id", "containerd-client") - - if to.username == "" { - form.Set("grant_type", "refresh_token") - form.Set("refresh_token", to.secret) - } else { - form.Set("grant_type", "password") - form.Set("username", to.username) - form.Set("password", to.secret) +func newAuthHandler(client *http.Client, hdr http.Header, scheme auth.AuthenticationScheme, opts auth.TokenOptions) *authHandler { + return &authHandler{ + header: hdr, + client: client, + scheme: scheme, + common: opts, + scopedTokens: map[string]*authResult{}, } +} - resp, err := ctxhttp.PostForm(ctx, a.client, to.realm, form) - if err != nil { - return "", err - } - defer resp.Body.Close() - - // Registries without support for POST may return 404 for POST /v2/token. - // As of September 2017, GCR is known to return 404. - // As of February 2018, JFrog Artifactory is known to return 401. - if (resp.StatusCode == 405 && to.username != "") || resp.StatusCode == 404 || resp.StatusCode == 401 { - return a.fetchToken(ctx, to) - } else if resp.StatusCode < 200 || resp.StatusCode >= 400 { - b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB - log.G(ctx).WithFields(logrus.Fields{ - "status": resp.Status, - "body": string(b), - }).Debugf("token request failed") - // TODO: handle error body and write debug output - return "", errors.Errorf("unexpected status: %s", resp.Status) +func (ah *authHandler) authorize(ctx context.Context) (string, error) { + switch ah.scheme { + case auth.BasicAuth: + return ah.doBasicAuth(ctx) + case auth.BearerAuth: + return ah.doBearerAuth(ctx) + default: + return "", errors.Wrapf(errdefs.ErrNotImplemented, "failed to find supported auth scheme: %s", string(ah.scheme)) } +} - decoder := json.NewDecoder(resp.Body) +func (ah *authHandler) doBasicAuth(ctx context.Context) (string, error) { + username, secret := ah.common.Username, ah.common.Secret - var tr postTokenResponse - if err = decoder.Decode(&tr); err != nil { - return "", fmt.Errorf("unable to decode token response: %s", err) + if username == "" || secret == "" { + return "", fmt.Errorf("failed to handle basic auth because missing username or secret") } - return tr.AccessToken, nil + auth := base64.StdEncoding.EncodeToString([]byte(username + ":" + secret)) + return fmt.Sprintf("Basic %s", auth), nil } -type getTokenResponse struct { - Token string `json:"token"` - AccessToken string `json:"access_token"` - ExpiresIn int `json:"expires_in"` - IssuedAt time.Time `json:"issued_at"` - RefreshToken string `json:"refresh_token"` -} - -// getToken fetches a token using a GET request -func (a *dockerAuthorizer) fetchToken(ctx context.Context, to tokenOptions) (string, error) { - req, err := http.NewRequest("GET", to.realm, nil) - if err != nil { - return "", err - } +func (ah *authHandler) doBearerAuth(ctx context.Context) (token string, err error) { + // copy common tokenOptions + to := ah.common - reqParams := req.URL.Query() + to.Scopes = GetTokenScopes(ctx, to.Scopes) - if to.service != "" { - reqParams.Add("service", to.service) - } + // Docs: https://docs.docker.com/registry/spec/auth/scope + scoped := strings.Join(to.Scopes, " ") - for _, scope := range to.scopes { - reqParams.Add("scope", scope) + ah.Lock() + if r, exist := ah.scopedTokens[scoped]; exist { + ah.Unlock() + r.Wait() + return r.token, r.err } - if to.secret != "" { - req.SetBasicAuth(to.username, to.secret) + // only one fetch token job + r := new(authResult) + r.Add(1) + ah.scopedTokens[scoped] = r + ah.Unlock() + + defer func() { + token = fmt.Sprintf("Bearer %s", token) + r.token, r.err = token, err + r.Done() + }() + + // fetch token for the resource scope + if to.Secret != "" { + defer func() { + err = errors.Wrap(err, "failed to fetch oauth token") + }() + // credential information is provided, use oauth POST endpoint + // TODO: Allow setting client_id + resp, err := auth.FetchTokenWithOAuth(ctx, ah.client, ah.header, "containerd-client", to) + if err != nil { + var errStatus remoteerrors.ErrUnexpectedStatus + if errors.As(err, &errStatus) { + // Registries without support for POST may return 404 for POST /v2/token. + // As of September 2017, GCR is known to return 404. + // As of February 2018, JFrog Artifactory is known to return 401. + if (errStatus.StatusCode == 405 && to.Username != "") || errStatus.StatusCode == 404 || errStatus.StatusCode == 401 { + resp, err := auth.FetchToken(ctx, ah.client, ah.header, to) + if err != nil { + return "", err + } + return resp.Token, nil + } + log.G(ctx).WithFields(logrus.Fields{ + "status": errStatus.Status, + "body": string(errStatus.Body), + }).Debugf("token request failed") + } + return "", err + } + return resp.AccessToken, nil } - - req.URL.RawQuery = reqParams.Encode() - - resp, err := ctxhttp.Do(ctx, a.client, req) + // do request anonymously + resp, err := auth.FetchToken(ctx, ah.client, ah.header, to) if err != nil { - return "", err - } - defer resp.Body.Close() - - if resp.StatusCode < 200 || resp.StatusCode >= 400 { - // TODO: handle error body and write debug output - return "", errors.Errorf("unexpected status: %s", resp.Status) - } - - decoder := json.NewDecoder(resp.Body) - - var tr getTokenResponse - if err = decoder.Decode(&tr); err != nil { - return "", fmt.Errorf("unable to decode token response: %s", err) + return "", errors.Wrap(err, "failed to fetch anonymous token") } - - // `access_token` is equivalent to `token` and if both are specified - // the choice is undefined. Canonicalize `access_token` by sticking - // things in `token`. - if tr.AccessToken != "" { - tr.Token = tr.AccessToken - } - - if tr.Token == "" { - return "", ErrNoToken - } - - return tr.Token, nil + return resp.Token, nil } -func invalidAuthorization(c challenge, responses []*http.Response) error { - errStr := c.parameters["error"] +func invalidAuthorization(c auth.Challenge, responses []*http.Response) error { + errStr := c.Parameters["error"] if errStr == "" { return nil } diff --git a/vendor/github.com/containerd/containerd/remotes/docker/converter.go b/vendor/github.com/containerd/containerd/remotes/docker/converter.go new file mode 100644 index 0000000000000..43e6b372c12bb --- /dev/null +++ b/vendor/github.com/containerd/containerd/remotes/docker/converter.go @@ -0,0 +1,88 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package docker + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/log" + "github.com/containerd/containerd/remotes" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +// LegacyConfigMediaType should be replaced by OCI image spec. +// +// More detail: docker/distribution#1622 +const LegacyConfigMediaType = "application/octet-stream" + +// ConvertManifest changes application/octet-stream to schema2 config media type if need. +// +// NOTE: +// 1. original manifest will be deleted by next gc round. +// 2. don't cover manifest list. +func ConvertManifest(ctx context.Context, store content.Store, desc ocispec.Descriptor) (ocispec.Descriptor, error) { + if !(desc.MediaType == images.MediaTypeDockerSchema2Manifest || + desc.MediaType == ocispec.MediaTypeImageManifest) { + + log.G(ctx).Warnf("do nothing for media type: %s", desc.MediaType) + return desc, nil + } + + // read manifest data + mb, err := content.ReadBlob(ctx, store, desc) + if err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "failed to read index data") + } + + var manifest ocispec.Manifest + if err := json.Unmarshal(mb, &manifest); err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "failed to unmarshal data into manifest") + } + + // check config media type + if manifest.Config.MediaType != LegacyConfigMediaType { + return desc, nil + } + + manifest.Config.MediaType = images.MediaTypeDockerSchema2Config + data, err := json.MarshalIndent(manifest, "", " ") + if err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "failed to marshal manifest") + } + + // update manifest with gc labels + desc.Digest = digest.Canonical.FromBytes(data) + desc.Size = int64(len(data)) + + labels := map[string]string{} + for i, c := range append([]ocispec.Descriptor{manifest.Config}, manifest.Layers...) { + labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = c.Digest.String() + } + + ref := remotes.MakeRefKey(ctx, desc) + if err := content.WriteBlob(ctx, store, ref, bytes.NewReader(data), desc, content.WithLabels(labels)); err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "failed to update content") + } + return desc, nil +} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/errcode.go b/vendor/github.com/containerd/containerd/remotes/docker/errcode.go new file mode 100644 index 0000000000000..8c623bcbef729 --- /dev/null +++ b/vendor/github.com/containerd/containerd/remotes/docker/errcode.go @@ -0,0 +1,283 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package docker + +import ( + "encoding/json" + "fmt" + "strings" +) + +// ErrorCoder is the base interface for ErrorCode and Error allowing +// users of each to just call ErrorCode to get the real ID of each +type ErrorCoder interface { + ErrorCode() ErrorCode +} + +// ErrorCode represents the error type. The errors are serialized via strings +// and the integer format may change and should *never* be exported. +type ErrorCode int + +var _ error = ErrorCode(0) + +// ErrorCode just returns itself +func (ec ErrorCode) ErrorCode() ErrorCode { + return ec +} + +// Error returns the ID/Value +func (ec ErrorCode) Error() string { + // NOTE(stevvooe): Cannot use message here since it may have unpopulated args. + return strings.ToLower(strings.Replace(ec.String(), "_", " ", -1)) +} + +// Descriptor returns the descriptor for the error code. +func (ec ErrorCode) Descriptor() ErrorDescriptor { + d, ok := errorCodeToDescriptors[ec] + + if !ok { + return ErrorCodeUnknown.Descriptor() + } + + return d +} + +// String returns the canonical identifier for this error code. +func (ec ErrorCode) String() string { + return ec.Descriptor().Value +} + +// Message returned the human-readable error message for this error code. +func (ec ErrorCode) Message() string { + return ec.Descriptor().Message +} + +// MarshalText encodes the receiver into UTF-8-encoded text and returns the +// result. +func (ec ErrorCode) MarshalText() (text []byte, err error) { + return []byte(ec.String()), nil +} + +// UnmarshalText decodes the form generated by MarshalText. +func (ec *ErrorCode) UnmarshalText(text []byte) error { + desc, ok := idToDescriptors[string(text)] + + if !ok { + desc = ErrorCodeUnknown.Descriptor() + } + + *ec = desc.Code + + return nil +} + +// WithMessage creates a new Error struct based on the passed-in info and +// overrides the Message property. +func (ec ErrorCode) WithMessage(message string) Error { + return Error{ + Code: ec, + Message: message, + } +} + +// WithDetail creates a new Error struct based on the passed-in info and +// set the Detail property appropriately +func (ec ErrorCode) WithDetail(detail interface{}) Error { + return Error{ + Code: ec, + Message: ec.Message(), + }.WithDetail(detail) +} + +// WithArgs creates a new Error struct and sets the Args slice +func (ec ErrorCode) WithArgs(args ...interface{}) Error { + return Error{ + Code: ec, + Message: ec.Message(), + }.WithArgs(args...) +} + +// Error provides a wrapper around ErrorCode with extra Details provided. +type Error struct { + Code ErrorCode `json:"code"` + Message string `json:"message"` + Detail interface{} `json:"detail,omitempty"` + + // TODO(duglin): See if we need an "args" property so we can do the + // variable substitution right before showing the message to the user +} + +var _ error = Error{} + +// ErrorCode returns the ID/Value of this Error +func (e Error) ErrorCode() ErrorCode { + return e.Code +} + +// Error returns a human readable representation of the error. +func (e Error) Error() string { + return fmt.Sprintf("%s: %s", e.Code.Error(), e.Message) +} + +// WithDetail will return a new Error, based on the current one, but with +// some Detail info added +func (e Error) WithDetail(detail interface{}) Error { + return Error{ + Code: e.Code, + Message: e.Message, + Detail: detail, + } +} + +// WithArgs uses the passed-in list of interface{} as the substitution +// variables in the Error's Message string, but returns a new Error +func (e Error) WithArgs(args ...interface{}) Error { + return Error{ + Code: e.Code, + Message: fmt.Sprintf(e.Code.Message(), args...), + Detail: e.Detail, + } +} + +// ErrorDescriptor provides relevant information about a given error code. +type ErrorDescriptor struct { + // Code is the error code that this descriptor describes. + Code ErrorCode + + // Value provides a unique, string key, often captilized with + // underscores, to identify the error code. This value is used as the + // keyed value when serializing api errors. + Value string + + // Message is a short, human readable description of the error condition + // included in API responses. + Message string + + // Description provides a complete account of the errors purpose, suitable + // for use in documentation. + Description string + + // HTTPStatusCode provides the http status code that is associated with + // this error condition. + HTTPStatusCode int +} + +// ParseErrorCode returns the value by the string error code. +// `ErrorCodeUnknown` will be returned if the error is not known. +func ParseErrorCode(value string) ErrorCode { + ed, ok := idToDescriptors[value] + if ok { + return ed.Code + } + + return ErrorCodeUnknown +} + +// Errors provides the envelope for multiple errors and a few sugar methods +// for use within the application. +type Errors []error + +var _ error = Errors{} + +func (errs Errors) Error() string { + switch len(errs) { + case 0: + return "" + case 1: + return errs[0].Error() + default: + msg := "errors:\n" + for _, err := range errs { + msg += err.Error() + "\n" + } + return msg + } +} + +// Len returns the current number of errors. +func (errs Errors) Len() int { + return len(errs) +} + +// MarshalJSON converts slice of error, ErrorCode or Error into a +// slice of Error - then serializes +func (errs Errors) MarshalJSON() ([]byte, error) { + var tmpErrs struct { + Errors []Error `json:"errors,omitempty"` + } + + for _, daErr := range errs { + var err Error + + switch daErr := daErr.(type) { + case ErrorCode: + err = daErr.WithDetail(nil) + case Error: + err = daErr + default: + err = ErrorCodeUnknown.WithDetail(daErr) + + } + + // If the Error struct was setup and they forgot to set the + // Message field (meaning its "") then grab it from the ErrCode + msg := err.Message + if msg == "" { + msg = err.Code.Message() + } + + tmpErrs.Errors = append(tmpErrs.Errors, Error{ + Code: err.Code, + Message: msg, + Detail: err.Detail, + }) + } + + return json.Marshal(tmpErrs) +} + +// UnmarshalJSON deserializes []Error and then converts it into slice of +// Error or ErrorCode +func (errs *Errors) UnmarshalJSON(data []byte) error { + var tmpErrs struct { + Errors []Error + } + + if err := json.Unmarshal(data, &tmpErrs); err != nil { + return err + } + + var newErrs Errors + for _, daErr := range tmpErrs.Errors { + // If Message is empty or exactly matches the Code's message string + // then just use the Code, no need for a full Error struct + if daErr.Detail == nil && (daErr.Message == "" || daErr.Message == daErr.Code.Message()) { + // Error's w/o details get converted to ErrorCode + newErrs = append(newErrs, daErr.Code) + } else { + // Error's w/ details are untouched + newErrs = append(newErrs, Error{ + Code: daErr.Code, + Message: daErr.Message, + Detail: daErr.Detail, + }) + } + } + + *errs = newErrs + return nil +} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/errdesc.go b/vendor/github.com/containerd/containerd/remotes/docker/errdesc.go new file mode 100644 index 0000000000000..b2bd4d82bd876 --- /dev/null +++ b/vendor/github.com/containerd/containerd/remotes/docker/errdesc.go @@ -0,0 +1,154 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package docker + +import ( + "fmt" + "net/http" + "sort" + "sync" +) + +var ( + errorCodeToDescriptors = map[ErrorCode]ErrorDescriptor{} + idToDescriptors = map[string]ErrorDescriptor{} + groupToDescriptors = map[string][]ErrorDescriptor{} +) + +var ( + // ErrorCodeUnknown is a generic error that can be used as a last + // resort if there is no situation-specific error message that can be used + ErrorCodeUnknown = Register("errcode", ErrorDescriptor{ + Value: "UNKNOWN", + Message: "unknown error", + Description: `Generic error returned when the error does not have an + API classification.`, + HTTPStatusCode: http.StatusInternalServerError, + }) + + // ErrorCodeUnsupported is returned when an operation is not supported. + ErrorCodeUnsupported = Register("errcode", ErrorDescriptor{ + Value: "UNSUPPORTED", + Message: "The operation is unsupported.", + Description: `The operation was unsupported due to a missing + implementation or invalid set of parameters.`, + HTTPStatusCode: http.StatusMethodNotAllowed, + }) + + // ErrorCodeUnauthorized is returned if a request requires + // authentication. + ErrorCodeUnauthorized = Register("errcode", ErrorDescriptor{ + Value: "UNAUTHORIZED", + Message: "authentication required", + Description: `The access controller was unable to authenticate + the client. Often this will be accompanied by a + Www-Authenticate HTTP response header indicating how to + authenticate.`, + HTTPStatusCode: http.StatusUnauthorized, + }) + + // ErrorCodeDenied is returned if a client does not have sufficient + // permission to perform an action. + ErrorCodeDenied = Register("errcode", ErrorDescriptor{ + Value: "DENIED", + Message: "requested access to the resource is denied", + Description: `The access controller denied access for the + operation on a resource.`, + HTTPStatusCode: http.StatusForbidden, + }) + + // ErrorCodeUnavailable provides a common error to report unavailability + // of a service or endpoint. + ErrorCodeUnavailable = Register("errcode", ErrorDescriptor{ + Value: "UNAVAILABLE", + Message: "service unavailable", + Description: "Returned when a service is not available", + HTTPStatusCode: http.StatusServiceUnavailable, + }) + + // ErrorCodeTooManyRequests is returned if a client attempts too many + // times to contact a service endpoint. + ErrorCodeTooManyRequests = Register("errcode", ErrorDescriptor{ + Value: "TOOMANYREQUESTS", + Message: "too many requests", + Description: `Returned when a client attempts to contact a + service too many times`, + HTTPStatusCode: http.StatusTooManyRequests, + }) +) + +var nextCode = 1000 +var registerLock sync.Mutex + +// Register will make the passed-in error known to the environment and +// return a new ErrorCode +func Register(group string, descriptor ErrorDescriptor) ErrorCode { + registerLock.Lock() + defer registerLock.Unlock() + + descriptor.Code = ErrorCode(nextCode) + + if _, ok := idToDescriptors[descriptor.Value]; ok { + panic(fmt.Sprintf("ErrorValue %q is already registered", descriptor.Value)) + } + if _, ok := errorCodeToDescriptors[descriptor.Code]; ok { + panic(fmt.Sprintf("ErrorCode %v is already registered", descriptor.Code)) + } + + groupToDescriptors[group] = append(groupToDescriptors[group], descriptor) + errorCodeToDescriptors[descriptor.Code] = descriptor + idToDescriptors[descriptor.Value] = descriptor + + nextCode++ + return descriptor.Code +} + +type byValue []ErrorDescriptor + +func (a byValue) Len() int { return len(a) } +func (a byValue) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byValue) Less(i, j int) bool { return a[i].Value < a[j].Value } + +// GetGroupNames returns the list of Error group names that are registered +func GetGroupNames() []string { + keys := []string{} + + for k := range groupToDescriptors { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} + +// GetErrorCodeGroup returns the named group of error descriptors +func GetErrorCodeGroup(name string) []ErrorDescriptor { + desc := groupToDescriptors[name] + sort.Sort(byValue(desc)) + return desc +} + +// GetErrorAllDescriptors returns a slice of all ErrorDescriptors that are +// registered, irrespective of what group they're in +func GetErrorAllDescriptors() []ErrorDescriptor { + result := []ErrorDescriptor{} + + for _, group := range GetGroupNames() { + result = append(result, GetErrorCodeGroup(group)...) + } + sort.Sort(byValue(result)) + return result +} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go b/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go index 4a2ce3c393b4c..4b2c10e9a37a9 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go @@ -18,11 +18,12 @@ package docker import ( "context" + "encoding/json" "fmt" "io" "io/ioutil" "net/http" - "path" + "net/url" "strings" "github.com/containerd/containerd/errdefs" @@ -30,7 +31,6 @@ import ( "github.com/containerd/containerd/log" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) type dockerFetcher struct { @@ -38,26 +38,50 @@ type dockerFetcher struct { } func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) { - ctx = log.WithLogger(ctx, log.G(ctx).WithFields( - logrus.Fields{ - "base": r.base.String(), - "digest": desc.Digest, - }, - )) - - urls, err := r.getV2URLPaths(ctx, desc) - if err != nil { - return nil, err + ctx = log.WithLogger(ctx, log.G(ctx).WithField("digest", desc.Digest)) + + hosts := r.filterHosts(HostCapabilityPull) + if len(hosts) == 0 { + return nil, errors.Wrap(errdefs.ErrNotFound, "no pull hosts") } - ctx, err = contextWithRepositoryScope(ctx, r.refspec, false) + ctx, err := ContextWithRepositoryScope(ctx, r.refspec, false) if err != nil { return nil, err } return newHTTPReadSeeker(desc.Size, func(offset int64) (io.ReadCloser, error) { - for _, u := range urls { - rc, err := r.open(ctx, u, desc.MediaType, offset) + // firstly try fetch via external urls + for _, us := range desc.URLs { + ctx = log.WithLogger(ctx, log.G(ctx).WithField("url", us)) + + u, err := url.Parse(us) + if err != nil { + log.G(ctx).WithError(err).Debug("failed to parse") + continue + } + if u.Scheme != "http" && u.Scheme != "https" { + log.G(ctx).Debug("non-http(s) alternative url is unsupported") + continue + } + log.G(ctx).Debug("trying alternative url") + + // Try this first, parse it + host := RegistryHost{ + Client: http.DefaultClient, + Host: u.Host, + Scheme: u.Scheme, + Path: u.Path, + Capabilities: HostCapabilityPull, + } + req := r.request(host, http.MethodGet) + // Strip namespace from base + req.path = u.Path + if u.RawQuery != "" { + req.path = req.path + "?" + u.RawQuery + } + + rc, err := r.open(ctx, req, desc.MediaType, offset) if err != nil { if errdefs.IsNotFound(err) { continue // try one of the other urls. @@ -69,32 +93,84 @@ func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.R return rc, nil } - return nil, errors.Wrapf(errdefs.ErrNotFound, - "could not fetch content descriptor %v (%v) from remote", - desc.Digest, desc.MediaType) + // Try manifests endpoints for manifests types + switch desc.MediaType { + case images.MediaTypeDockerSchema2Manifest, images.MediaTypeDockerSchema2ManifestList, + images.MediaTypeDockerSchema1Manifest, + ocispec.MediaTypeImageManifest, ocispec.MediaTypeImageIndex: + + var firstErr error + for _, host := range r.hosts { + req := r.request(host, http.MethodGet, "manifests", desc.Digest.String()) + if err := req.addNamespace(r.refspec.Hostname()); err != nil { + return nil, err + } + + rc, err := r.open(ctx, req, desc.MediaType, offset) + if err != nil { + // Store the error for referencing later + if firstErr == nil { + firstErr = err + } + continue // try another host + } + + return rc, nil + } + + return nil, firstErr + } + + // Finally use blobs endpoints + var firstErr error + for _, host := range r.hosts { + req := r.request(host, http.MethodGet, "blobs", desc.Digest.String()) + if err := req.addNamespace(r.refspec.Hostname()); err != nil { + return nil, err + } + + rc, err := r.open(ctx, req, desc.MediaType, offset) + if err != nil { + // Store the error for referencing later + if firstErr == nil { + firstErr = err + } + continue // try another host + } + + return rc, nil + } + + if errdefs.IsNotFound(firstErr) { + firstErr = errors.Wrapf(errdefs.ErrNotFound, + "could not fetch content descriptor %v (%v) from remote", + desc.Digest, desc.MediaType) + } + + return nil, firstErr }) } -func (r dockerFetcher) open(ctx context.Context, u, mediatype string, offset int64) (io.ReadCloser, error) { - req, err := http.NewRequest(http.MethodGet, u, nil) - if err != nil { - return nil, err - } - - req.Header.Set("Accept", strings.Join([]string{mediatype, `*`}, ", ")) +func (r dockerFetcher) open(ctx context.Context, req *request, mediatype string, offset int64) (_ io.ReadCloser, retErr error) { + req.header.Set("Accept", strings.Join([]string{mediatype, `*/*`}, ", ")) if offset > 0 { // Note: "Accept-Ranges: bytes" cannot be trusted as some endpoints // will return the header without supporting the range. The content // range must always be checked. - req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset)) + req.header.Set("Range", fmt.Sprintf("bytes=%d-", offset)) } - resp, err := r.doRequestWithRetries(ctx, req, nil) + resp, err := req.doWithRetries(ctx, nil) if err != nil { return nil, err } + defer func() { + if retErr != nil { + resp.Body.Close() + } + }() if resp.StatusCode > 299 { // TODO(stevvooe): When doing a offset specific request, we should @@ -102,11 +178,14 @@ func (r dockerFetcher) open(ctx context.Context, u, mediatype string, offset int // can discard the bytes, hiding the seek behavior from the // implementation. - resp.Body.Close() if resp.StatusCode == http.StatusNotFound { - return nil, errors.Wrapf(errdefs.ErrNotFound, "content at %v not found", u) + return nil, errors.Wrapf(errdefs.ErrNotFound, "content at %v not found", req.String()) + } + var registryErr Errors + if err := json.NewDecoder(resp.Body).Decode(®istryErr); err != nil || registryErr.Len() < 1 { + return nil, errors.Errorf("unexpected status code %v: %v", req.String(), resp.Status) } - return nil, errors.Errorf("unexpected status code %v: %v", u, resp.Status) + return nil, errors.Errorf("unexpected status code %v: %s - Server message: %s", req.String(), resp.Status, registryErr.Error()) } if offset > 0 { cr := resp.Header.Get("content-range") @@ -135,30 +214,3 @@ func (r dockerFetcher) open(ctx context.Context, u, mediatype string, offset int return resp.Body, nil } - -// getV2URLPaths generates the candidate urls paths for the object based on the -// set of hints and the provided object id. URLs are returned in the order of -// most to least likely succeed. -func (r *dockerFetcher) getV2URLPaths(ctx context.Context, desc ocispec.Descriptor) ([]string, error) { - var urls []string - - if len(desc.URLs) > 0 { - // handle fetch via external urls. - for _, u := range desc.URLs { - log.G(ctx).WithField("url", u).Debug("adding alternative url") - urls = append(urls, u) - } - } - - switch desc.MediaType { - case images.MediaTypeDockerSchema2Manifest, images.MediaTypeDockerSchema2ManifestList, - images.MediaTypeDockerSchema1Manifest, - ocispec.MediaTypeImageManifest, ocispec.MediaTypeImageIndex: - urls = append(urls, r.url(path.Join("manifests", desc.Digest.String()))) - } - - // always fallback to attempting to get the object out of the blobs store. - urls = append(urls, r.url(path.Join("blobs", desc.Digest.String()))) - - return urls, nil -} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/handler.go b/vendor/github.com/containerd/containerd/remotes/docker/handler.go new file mode 100644 index 0000000000000..529cfbc274bd2 --- /dev/null +++ b/vendor/github.com/containerd/containerd/remotes/docker/handler.go @@ -0,0 +1,154 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package docker + +import ( + "context" + "fmt" + "net/url" + "strings" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/labels" + "github.com/containerd/containerd/log" + "github.com/containerd/containerd/reference" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +var ( + // labelDistributionSource describes the source blob comes from. + labelDistributionSource = "containerd.io/distribution.source" +) + +// AppendDistributionSourceLabel updates the label of blob with distribution source. +func AppendDistributionSourceLabel(manager content.Manager, ref string) (images.HandlerFunc, error) { + refspec, err := reference.Parse(ref) + if err != nil { + return nil, err + } + + u, err := url.Parse("dummy://" + refspec.Locator) + if err != nil { + return nil, err + } + + source, repo := u.Hostname(), strings.TrimPrefix(u.Path, "/") + return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + info, err := manager.Info(ctx, desc.Digest) + if err != nil { + return nil, err + } + + key := distributionSourceLabelKey(source) + + originLabel := "" + if info.Labels != nil { + originLabel = info.Labels[key] + } + value := appendDistributionSourceLabel(originLabel, repo) + + // The repo name has been limited under 256 and the distribution + // label might hit the limitation of label size, when blob data + // is used as the very, very common layer. + if err := labels.Validate(key, value); err != nil { + log.G(ctx).Warnf("skip to append distribution label: %s", err) + return nil, nil + } + + info = content.Info{ + Digest: desc.Digest, + Labels: map[string]string{ + key: value, + }, + } + _, err = manager.Update(ctx, info, fmt.Sprintf("labels.%s", key)) + return nil, err + }, nil +} + +func appendDistributionSourceLabel(originLabel, repo string) string { + repos := []string{} + if originLabel != "" { + repos = strings.Split(originLabel, ",") + } + repos = append(repos, repo) + + // use empty string to present duplicate items + for i := 1; i < len(repos); i++ { + tmp, j := repos[i], i-1 + for ; j >= 0 && repos[j] >= tmp; j-- { + if repos[j] == tmp { + tmp = "" + } + repos[j+1] = repos[j] + } + repos[j+1] = tmp + } + + i := 0 + for ; i < len(repos) && repos[i] == ""; i++ { + } + + return strings.Join(repos[i:], ",") +} + +func distributionSourceLabelKey(source string) string { + return fmt.Sprintf("%s.%s", labelDistributionSource, source) +} + +// selectRepositoryMountCandidate will select the repo which has longest +// common prefix components as the candidate. +func selectRepositoryMountCandidate(refspec reference.Spec, sources map[string]string) string { + u, err := url.Parse("dummy://" + refspec.Locator) + if err != nil { + // NOTE: basically, it won't be error here + return "" + } + + source, target := u.Hostname(), strings.TrimPrefix(u.Path, "/") + repoLabel, ok := sources[distributionSourceLabelKey(source)] + if !ok || repoLabel == "" { + return "" + } + + n, match := 0, "" + components := strings.Split(target, "/") + for _, repo := range strings.Split(repoLabel, ",") { + // the target repo is not a candidate + if repo == target { + continue + } + + if l := commonPrefixComponents(components, repo); l >= n { + n, match = l, repo + } + } + return match +} + +func commonPrefixComponents(components []string, target string) int { + targetComponents := strings.Split(target, "/") + + i := 0 + for ; i < len(components) && i < len(targetComponents); i++ { + if components[i] != targetComponents[i] { + break + } + } + return i +} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go b/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go index 9175b6a7a4935..58c866bcdec17 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go @@ -26,12 +26,16 @@ import ( "github.com/pkg/errors" ) +const maxRetry = 3 + type httpReadSeeker struct { size int64 offset int64 rc io.ReadCloser open func(offset int64) (io.ReadCloser, error) closed bool + + errsWithNoProgress int } func newHTTPReadSeeker(size int64, open func(offset int64) (io.ReadCloser, error)) (io.ReadCloser, error) { @@ -53,6 +57,27 @@ func (hrs *httpReadSeeker) Read(p []byte) (n int, err error) { n, err = rd.Read(p) hrs.offset += int64(n) + if n > 0 || err == nil { + hrs.errsWithNoProgress = 0 + } + if err == io.ErrUnexpectedEOF { + // connection closed unexpectedly. try reconnecting. + if n == 0 { + hrs.errsWithNoProgress++ + if hrs.errsWithNoProgress > maxRetry { + return // too many retries for this offset with no progress + } + } + if hrs.rc != nil { + if clsErr := hrs.rc.Close(); clsErr != nil { + log.L.WithError(clsErr).Errorf("httpReadSeeker: failed to close ReadCloser") + } + hrs.rc = nil + } + if _, err2 := hrs.reader(); err2 == nil { + return n, nil + } + } return } @@ -121,7 +146,7 @@ func (hrs *httpReadSeeker) reader() (io.Reader, error) { rc, err := hrs.open(hrs.offset) if err != nil { - return nil, errors.Wrapf(err, "httpReaderSeeker: failed open") + return nil, errors.Wrapf(err, "httpReadSeeker: failed open") } if hrs.rc != nil { diff --git a/vendor/github.com/containerd/containerd/remotes/docker/pusher.go b/vendor/github.com/containerd/containerd/remotes/docker/pusher.go index c3c0923f08ac1..97ed66a6ab01a 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/pusher.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/pusher.go @@ -21,7 +21,7 @@ import ( "io" "io/ioutil" "net/http" - "path" + "net/url" "strings" "time" @@ -30,6 +30,7 @@ import ( "github.com/containerd/containerd/images" "github.com/containerd/containerd/log" "github.com/containerd/containerd/remotes" + remoteserrors "github.com/containerd/containerd/remotes/errors" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -37,62 +38,93 @@ import ( type dockerPusher struct { *dockerBase - tag string + object string // TODO: namespace tracker tracker StatusTracker } +// Writer implements Ingester API of content store. This allows the client +// to receive ErrUnavailable when there is already an on-going upload. +// Note that the tracker MUST implement StatusTrackLocker interface to avoid +// race condition on StatusTracker. +func (p dockerPusher) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) { + var wOpts content.WriterOpts + for _, opt := range opts { + if err := opt(&wOpts); err != nil { + return nil, err + } + } + if wOpts.Ref == "" { + return nil, errors.Wrap(errdefs.ErrInvalidArgument, "ref must not be empty") + } + return p.push(ctx, wOpts.Desc, wOpts.Ref, true) +} + func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (content.Writer, error) { - ctx, err := contextWithRepositoryScope(ctx, p.refspec, true) + return p.push(ctx, desc, remotes.MakeRefKey(ctx, desc), false) +} + +func (p dockerPusher) push(ctx context.Context, desc ocispec.Descriptor, ref string, unavailableOnFail bool) (content.Writer, error) { + if l, ok := p.tracker.(StatusTrackLocker); ok { + l.Lock(ref) + defer l.Unlock(ref) + } + ctx, err := ContextWithRepositoryScope(ctx, p.refspec, true) if err != nil { return nil, err } - ref := remotes.MakeRefKey(ctx, desc) status, err := p.tracker.GetStatus(ref) if err == nil { - if status.Offset == status.Total { + if status.Committed && status.Offset == status.Total { return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "ref %v", ref) } + if unavailableOnFail { + // Another push of this ref is happening elsewhere. The rest of function + // will continue only when `errdefs.IsNotFound(err) == true` (i.e. there + // is no actively-tracked ref already). + return nil, errors.Wrap(errdefs.ErrUnavailable, "push is on-going") + } // TODO: Handle incomplete status } else if !errdefs.IsNotFound(err) { return nil, errors.Wrap(err, "failed to get status") } + hosts := p.filterHosts(HostCapabilityPush) + if len(hosts) == 0 { + return nil, errors.Wrap(errdefs.ErrNotFound, "no push hosts") + } + var ( isManifest bool - existCheck string + existCheck []string + host = hosts[0] ) switch desc.MediaType { case images.MediaTypeDockerSchema2Manifest, images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageManifest, ocispec.MediaTypeImageIndex: isManifest = true - if p.tag == "" { - existCheck = path.Join("manifests", desc.Digest.String()) - } else { - existCheck = path.Join("manifests", p.tag) - } + existCheck = getManifestPath(p.object, desc.Digest) default: - existCheck = path.Join("blobs", desc.Digest.String()) + existCheck = []string{"blobs", desc.Digest.String()} } - req, err := http.NewRequest(http.MethodHead, p.url(existCheck), nil) - if err != nil { - return nil, err - } + req := p.request(host, http.MethodHead, existCheck...) + req.header.Set("Accept", strings.Join([]string{desc.MediaType, `*/*`}, ", ")) + + log.G(ctx).WithField("url", req.String()).Debugf("checking and pushing to") - req.Header.Set("Accept", strings.Join([]string{desc.MediaType, `*`}, ", ")) - resp, err := p.doRequestWithRetries(ctx, req, nil) + resp, err := req.doWithRetries(ctx, nil) if err != nil { - if errors.Cause(err) != ErrInvalidAuthorization { + if !errors.Is(err, ErrInvalidAuthorization) { return nil, err } log.G(ctx).WithError(err).Debugf("Unable to check existence, continuing with push") } else { if resp.StatusCode == http.StatusOK { var exists bool - if isManifest && p.tag != "" { + if isManifest && existCheck[1] != desc.Digest.String() { dgstHeader := digest.Digest(resp.Header.Get("Docker-Content-Digest")) if dgstHeader == desc.Digest { exists = true @@ -103,80 +135,120 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten if exists { p.tracker.SetStatus(ref, Status{ + Committed: true, Status: content.Status{ - Ref: ref, + Ref: ref, + Total: desc.Size, + Offset: desc.Size, // TODO: Set updated time? }, }) + resp.Body.Close() return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v on remote", desc.Digest) } } else if resp.StatusCode != http.StatusNotFound { - // TODO: log error - return nil, errors.Errorf("unexpected response: %s", resp.Status) + err := remoteserrors.NewUnexpectedStatusErr(resp) + log.G(ctx).WithField("resp", resp).WithField("body", string(err.(remoteserrors.ErrUnexpectedStatus).Body)).Debug("unexpected response") + resp.Body.Close() + return nil, err } + resp.Body.Close() } - // TODO: Lookup related objects for cross repository push - if isManifest { - var putPath string - if p.tag != "" { - putPath = path.Join("manifests", p.tag) - } else { - putPath = path.Join("manifests", desc.Digest.String()) - } - - req, err = http.NewRequest(http.MethodPut, p.url(putPath), nil) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", desc.MediaType) + putPath := getManifestPath(p.object, desc.Digest) + req = p.request(host, http.MethodPut, putPath...) + req.header.Add("Content-Type", desc.MediaType) } else { - // TODO: Do monolithic upload if size is small - // Start upload request - req, err = http.NewRequest(http.MethodPost, p.url("blobs", "uploads")+"/", nil) - if err != nil { - return nil, err + req = p.request(host, http.MethodPost, "blobs", "uploads/") + + var resp *http.Response + if fromRepo := selectRepositoryMountCandidate(p.refspec, desc.Annotations); fromRepo != "" { + preq := requestWithMountFrom(req, desc.Digest.String(), fromRepo) + pctx := ContextWithAppendPullRepositoryScope(ctx, fromRepo) + + // NOTE: the fromRepo might be private repo and + // auth service still can grant token without error. + // but the post request will fail because of 401. + // + // for the private repo, we should remove mount-from + // query and send the request again. + resp, err = preq.doWithRetries(pctx, nil) + if err != nil { + return nil, err + } + + if resp.StatusCode == http.StatusUnauthorized { + log.G(ctx).Debugf("failed to mount from repository %s", fromRepo) + + resp.Body.Close() + resp = nil + } } - resp, err := p.doRequestWithRetries(ctx, req, nil) - if err != nil { - return nil, err + if resp == nil { + resp, err = req.doWithRetries(ctx, nil) + if err != nil { + return nil, err + } } + defer resp.Body.Close() switch resp.StatusCode { case http.StatusOK, http.StatusAccepted, http.StatusNoContent: + case http.StatusCreated: + p.tracker.SetStatus(ref, Status{ + Committed: true, + Status: content.Status{ + Ref: ref, + Total: desc.Size, + Offset: desc.Size, + }, + }) + return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "content %v on remote", desc.Digest) default: - // TODO: log error - return nil, errors.Errorf("unexpected response: %s", resp.Status) + err := remoteserrors.NewUnexpectedStatusErr(resp) + log.G(ctx).WithField("resp", resp).WithField("body", string(err.(remoteserrors.ErrUnexpectedStatus).Body)).Debug("unexpected response") + return nil, err } - location := resp.Header.Get("Location") + var ( + location = resp.Header.Get("Location") + lurl *url.URL + lhost = host + ) // Support paths without host in location if strings.HasPrefix(location, "/") { - // Support location string containing path and query - qmIndex := strings.Index(location, "?") - if qmIndex > 0 { - u := p.base - u.Path = location[:qmIndex] - u.RawQuery = location[qmIndex+1:] - location = u.String() - } else { - u := p.base - u.Path = location - location = u.String() + lurl, err = url.Parse(lhost.Scheme + "://" + lhost.Host + location) + if err != nil { + return nil, errors.Wrapf(err, "unable to parse location %v", location) + } + } else { + if !strings.Contains(location, "://") { + location = lhost.Scheme + "://" + location + } + lurl, err = url.Parse(location) + if err != nil { + return nil, errors.Wrapf(err, "unable to parse location %v", location) } - } - req, err = http.NewRequest(http.MethodPut, location, nil) - if err != nil { - return nil, err + if lurl.Host != lhost.Host || lhost.Scheme != lurl.Scheme { + + lhost.Scheme = lurl.Scheme + lhost.Host = lurl.Host + log.G(ctx).WithField("host", lhost.Host).WithField("scheme", lhost.Scheme).Debug("upload changed destination") + + // Strip authorizer if change to host or scheme + lhost.Authorizer = nil + } } - q := req.URL.Query() + q := lurl.Query() q.Add("digest", desc.Digest.String()) - req.URL.RawQuery = q.Encode() + req = p.request(lhost, http.MethodPut) + req.header.Set("Content-Type", "application/octet-stream") + req.path = lurl.Path + "?" + q.Encode() } p.tracker.SetStatus(ref, Status{ Status: content.Status{ @@ -190,15 +262,25 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten // TODO: Support chunked upload pr, pw := io.Pipe() - respC := make(chan *http.Response, 1) + respC := make(chan response, 1) + body := ioutil.NopCloser(pr) - req.Body = ioutil.NopCloser(pr) - req.ContentLength = desc.Size + req.body = func() (io.ReadCloser, error) { + if body == nil { + return nil, errors.New("cannot reuse body, request must be retried") + } + // Only use the body once since pipe cannot be seeked + ob := body + body = nil + return ob, nil + } + req.size = desc.Size go func() { defer close(respC) - resp, err = p.doRequest(ctx, req) + resp, err := req.doWithRetries(ctx, nil) if err != nil { + respC <- response{err: err} pr.CloseWithError(err) return } @@ -206,10 +288,11 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten switch resp.StatusCode { case http.StatusOK, http.StatusCreated, http.StatusNoContent: default: - // TODO: log error - pr.CloseWithError(errors.Errorf("unexpected response: %s", resp.Status)) + err := remoteserrors.NewUnexpectedStatusErr(resp) + log.G(ctx).WithField("resp", resp).WithField("body", string(err.(remoteserrors.ErrUnexpectedStatus).Body)).Debug("unexpected response") + pr.CloseWithError(err) } - respC <- resp + respC <- response{Response: resp} }() return &pushWriter{ @@ -223,12 +306,36 @@ func (p dockerPusher) Push(ctx context.Context, desc ocispec.Descriptor) (conten }, nil } +func getManifestPath(object string, dgst digest.Digest) []string { + if i := strings.IndexByte(object, '@'); i >= 0 { + if object[i+1:] != dgst.String() { + // use digest, not tag + object = "" + } else { + // strip @ for registry path to make tag + object = object[:i] + } + + } + + if object == "" { + return []string{"manifests", dgst.String()} + } + + return []string{"manifests", object} +} + +type response struct { + *http.Response + err error +} + type pushWriter struct { base *dockerBase ref string pipe *io.PipeWriter - responseC <-chan *http.Response + responseC <-chan response isManifest bool expected digest.Digest @@ -274,20 +381,19 @@ func (pw *pushWriter) Commit(ctx context.Context, size int64, expected digest.Di if err := pw.pipe.Close(); err != nil { return err } - // TODO: Update status to determine committing - // TODO: timeout waiting for response resp := <-pw.responseC - if resp == nil { - return errors.New("no response") + if resp.err != nil { + return resp.err } + defer resp.Response.Body.Close() // 201 is specified return status, some registries return - // 200 or 204. + // 200, 202 or 204. switch resp.StatusCode { - case http.StatusOK, http.StatusCreated, http.StatusNoContent: + case http.StatusOK, http.StatusCreated, http.StatusNoContent, http.StatusAccepted: default: - return errors.Errorf("unexpected status: %s", resp.Status) + return remoteserrors.NewUnexpectedStatusErr(resp.Response) } status, err := pw.tracker.GetStatus(pw.ref) @@ -296,7 +402,7 @@ func (pw *pushWriter) Commit(ctx context.Context, size int64, expected digest.Di } if size > 0 && size != status.Offset { - return errors.Errorf("unxpected size %d, expected %d", status.Offset, size) + return errors.Errorf("unexpected size %d, expected %d", status.Offset, size) } if expected == "" { @@ -312,6 +418,10 @@ func (pw *pushWriter) Commit(ctx context.Context, size int64, expected digest.Di return errors.Errorf("got digest %s, expected %s", actual, expected) } + status.Committed = true + status.UpdatedAt = time.Now() + pw.tracker.SetStatus(pw.ref, status) + return nil } @@ -320,3 +430,16 @@ func (pw *pushWriter) Truncate(size int64) error { // TODO: always error on manifest return errors.New("cannot truncate remote upload") } + +func requestWithMountFrom(req *request, mount, from string) *request { + creq := *req + + sep := "?" + if strings.Contains(creq.path, sep) { + sep = "&" + } + + creq.path = creq.path + sep + "mount=" + mount + "&from=" + from + + return &creq +} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/registry.go b/vendor/github.com/containerd/containerd/remotes/docker/registry.go new file mode 100644 index 0000000000000..1e77d4c86cd3d --- /dev/null +++ b/vendor/github.com/containerd/containerd/remotes/docker/registry.go @@ -0,0 +1,245 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package docker + +import ( + "net" + "net/http" + + "github.com/pkg/errors" +) + +// HostCapabilities represent the capabilities of the registry +// host. This also represents the set of operations for which +// the registry host may be trusted to perform. +// +// For example pushing is a capability which should only be +// performed on an upstream source, not a mirror. +// Resolving (the process of converting a name into a digest) +// must be considered a trusted operation and only done by +// a host which is trusted (or more preferably by secure process +// which can prove the provenance of the mapping). A public +// mirror should never be trusted to do a resolve action. +// +// | Registry Type | Pull | Resolve | Push | +// |------------------|------|---------|------| +// | Public Registry | yes | yes | yes | +// | Private Registry | yes | yes | yes | +// | Public Mirror | yes | no | no | +// | Private Mirror | yes | yes | no | +type HostCapabilities uint8 + +const ( + // HostCapabilityPull represents the capability to fetch manifests + // and blobs by digest + HostCapabilityPull HostCapabilities = 1 << iota + + // HostCapabilityResolve represents the capability to fetch manifests + // by name + HostCapabilityResolve + + // HostCapabilityPush represents the capability to push blobs and + // manifests + HostCapabilityPush + + // Reserved for future capabilities (i.e. search, catalog, remove) +) + +// Has checks whether the capabilities list has the provide capability +func (c HostCapabilities) Has(t HostCapabilities) bool { + return c&t == t +} + +// RegistryHost represents a complete configuration for a registry +// host, representing the capabilities, authorizations, connection +// configuration, and location. +type RegistryHost struct { + Client *http.Client + Authorizer Authorizer + Host string + Scheme string + Path string + Capabilities HostCapabilities + Header http.Header +} + +func (h RegistryHost) isProxy(refhost string) bool { + if refhost != h.Host { + if refhost != "docker.io" || h.Host != "registry-1.docker.io" { + return true + } + } + return false +} + +// RegistryHosts fetches the registry hosts for a given namespace, +// provided by the host component of an distribution image reference. +type RegistryHosts func(string) ([]RegistryHost, error) + +// Registries joins multiple registry configuration functions, using the same +// order as provided within the arguments. When an empty registry configuration +// is returned with a nil error, the next function will be called. +// NOTE: This function will not join configurations, as soon as a non-empty +// configuration is returned from a configuration function, it will be returned +// to the caller. +func Registries(registries ...RegistryHosts) RegistryHosts { + return func(host string) ([]RegistryHost, error) { + for _, registry := range registries { + config, err := registry(host) + if err != nil { + return config, err + } + if len(config) > 0 { + return config, nil + } + } + return nil, nil + } +} + +type registryOpts struct { + authorizer Authorizer + plainHTTP func(string) (bool, error) + host func(string) (string, error) + client *http.Client +} + +// RegistryOpt defines a registry default option +type RegistryOpt func(*registryOpts) + +// WithPlainHTTP configures registries to use plaintext http scheme +// for the provided host match function. +func WithPlainHTTP(f func(string) (bool, error)) RegistryOpt { + return func(opts *registryOpts) { + opts.plainHTTP = f + } +} + +// WithAuthorizer configures the default authorizer for a registry +func WithAuthorizer(a Authorizer) RegistryOpt { + return func(opts *registryOpts) { + opts.authorizer = a + } +} + +// WithHostTranslator defines the default translator to use for registry hosts +func WithHostTranslator(h func(string) (string, error)) RegistryOpt { + return func(opts *registryOpts) { + opts.host = h + } +} + +// WithClient configures the default http client for a registry +func WithClient(c *http.Client) RegistryOpt { + return func(opts *registryOpts) { + opts.client = c + } +} + +// ConfigureDefaultRegistries is used to create a default configuration for +// registries. For more advanced configurations or per-domain setups, +// the RegistryHosts interface should be used directly. +// NOTE: This function will always return a non-empty value or error +func ConfigureDefaultRegistries(ropts ...RegistryOpt) RegistryHosts { + var opts registryOpts + for _, opt := range ropts { + opt(&opts) + } + + return func(host string) ([]RegistryHost, error) { + config := RegistryHost{ + Client: opts.client, + Authorizer: opts.authorizer, + Host: host, + Scheme: "https", + Path: "/v2", + Capabilities: HostCapabilityPull | HostCapabilityResolve | HostCapabilityPush, + } + + if config.Client == nil { + config.Client = http.DefaultClient + } + + if opts.plainHTTP != nil { + match, err := opts.plainHTTP(host) + if err != nil { + return nil, err + } + if match { + config.Scheme = "http" + } + } + + if opts.host != nil { + var err error + config.Host, err = opts.host(config.Host) + if err != nil { + return nil, err + } + } else if host == "docker.io" { + config.Host = "registry-1.docker.io" + } + + return []RegistryHost{config}, nil + } +} + +// MatchAllHosts is a host match function which is always true. +func MatchAllHosts(string) (bool, error) { + return true, nil +} + +// MatchLocalhost is a host match function which returns true for +// localhost. +// +// Note: this does not handle matching of ip addresses in octal, +// decimal or hex form. +func MatchLocalhost(host string) (bool, error) { + switch { + case host == "::1": + return true, nil + case host == "[::1]": + return true, nil + } + h, p, err := net.SplitHostPort(host) + + // addrError helps distinguish between errors of form + // "no colon in address" and "too many colons in address". + // The former is fine as the host string need not have a + // port. Latter needs to be handled. + addrError := &net.AddrError{ + Err: "missing port in address", + Addr: host, + } + if err != nil { + if err.Error() != addrError.Error() { + return false, err + } + // host string without any port specified + h = host + } else if len(p) == 0 { + return false, errors.New("invalid host name format") + } + + // use ipv4 dotted decimal for further checking + if h == "localhost" { + h = "127.0.0.1" + } + ip := net.ParseIP(h) + + return ip.IsLoopback(), nil +} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/resolver.go b/vendor/github.com/containerd/containerd/remotes/docker/resolver.go index 5cccdecba082c..1be9e1d05c678 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/resolver.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/resolver.go @@ -18,10 +18,12 @@ package docker import ( "context" + "fmt" + "io" + "io/ioutil" "net/http" "net/url" "path" - "strconv" "strings" "github.com/containerd/containerd/errdefs" @@ -29,6 +31,8 @@ import ( "github.com/containerd/containerd/log" "github.com/containerd/containerd/reference" "github.com/containerd/containerd/remotes" + "github.com/containerd/containerd/remotes/docker/schema1" + "github.com/containerd/containerd/version" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -37,13 +41,22 @@ import ( ) var ( - // ErrNoToken is returned if a request is successful but the body does not - // contain an authorization token. - ErrNoToken = errors.New("authorization server did not include a token in the response") - // ErrInvalidAuthorization is used when credentials are passed to a server but // those credentials are rejected. ErrInvalidAuthorization = errors.New("authorization failed") + + // MaxManifestSize represents the largest size accepted from a registry + // during resolution. Larger manifests may be accepted using a + // resolution method other than the registry. + // + // NOTE: The max supported layers by some runtimes is 128 and individual + // layers will not contribute more than 256 bytes, making a + // reasonable limit for a large image manifests of 32K bytes. + // 4M bytes represents a much larger upper bound for images which may + // contain large annotations or be non-images. A proper manifest + // design puts large metadata in subobjects, as is consistent the + // intent of the manifest design. + MaxManifestSize int64 = 4 * 1048 * 1048 ) // Authorizer is used to authorize HTTP requests based on 401 HTTP responses. @@ -70,28 +83,38 @@ type Authorizer interface { // ResolverOptions are used to configured a new Docker register resolver type ResolverOptions struct { + // Hosts returns registry host configurations for a namespace. + Hosts RegistryHosts + + // Headers are the HTTP request header fields sent by the resolver + Headers http.Header + + // Tracker is used to track uploads to the registry. This is used + // since the registry does not have upload tracking and the existing + // mechanism for getting blob upload status is expensive. + Tracker StatusTracker + // Authorizer is used to authorize registry requests + // Deprecated: use Hosts Authorizer Authorizer // Credentials provides username and secret given a host. // If username is empty but a secret is given, that secret - // is interpretted as a long lived token. - // Deprecated: use Authorizer + // is interpreted as a long lived token. + // Deprecated: use Hosts Credentials func(string) (string, string, error) // Host provides the hostname given a namespace. + // Deprecated: use Hosts Host func(string) (string, error) // PlainHTTP specifies to use plain http and not https + // Deprecated: use Hosts PlainHTTP bool // Client is the http client to used when making registry requests + // Deprecated: use Hosts Client *http.Client - - // Tracker is used to track uploads to the registry. This is used - // since the registry does not have upload tracking and the existing - // mechanism for getting blob upload status is expensive. - Tracker StatusTracker } // DefaultHost is the default host function. @@ -103,11 +126,10 @@ func DefaultHost(ns string) (string, error) { } type dockerResolver struct { - auth Authorizer - host func(string) (string, error) - plainHTTP bool - client *http.Client - tracker StatusTracker + hosts RegistryHosts + header http.Header + resolveHeader http.Header + tracker StatusTracker } // NewResolver returns a new resolver to a Docker registry @@ -115,45 +137,102 @@ func NewResolver(options ResolverOptions) remotes.Resolver { if options.Tracker == nil { options.Tracker = NewInMemoryTracker() } - if options.Host == nil { - options.Host = DefaultHost + + if options.Headers == nil { + options.Headers = make(http.Header) + } + if _, ok := options.Headers["User-Agent"]; !ok { + options.Headers.Set("User-Agent", "containerd/"+version.Version) + } + + resolveHeader := http.Header{} + if _, ok := options.Headers["Accept"]; !ok { + // set headers for all the types we support for resolution. + resolveHeader.Set("Accept", strings.Join([]string{ + images.MediaTypeDockerSchema2Manifest, + images.MediaTypeDockerSchema2ManifestList, + ocispec.MediaTypeImageManifest, + ocispec.MediaTypeImageIndex, "*/*"}, ", ")) + } else { + resolveHeader["Accept"] = options.Headers["Accept"] + delete(options.Headers, "Accept") } - if options.Authorizer == nil { - options.Authorizer = NewAuthorizer(options.Client, options.Credentials) + + if options.Hosts == nil { + opts := []RegistryOpt{} + if options.Host != nil { + opts = append(opts, WithHostTranslator(options.Host)) + } + + if options.Authorizer == nil { + options.Authorizer = NewDockerAuthorizer( + WithAuthClient(options.Client), + WithAuthHeader(options.Headers), + WithAuthCreds(options.Credentials)) + } + opts = append(opts, WithAuthorizer(options.Authorizer)) + + if options.Client != nil { + opts = append(opts, WithClient(options.Client)) + } + if options.PlainHTTP { + opts = append(opts, WithPlainHTTP(MatchAllHosts)) + } else { + opts = append(opts, WithPlainHTTP(MatchLocalhost)) + } + options.Hosts = ConfigureDefaultRegistries(opts...) } return &dockerResolver{ - auth: options.Authorizer, - host: options.Host, - plainHTTP: options.PlainHTTP, - client: options.Client, - tracker: options.Tracker, + hosts: options.Hosts, + header: options.Headers, + resolveHeader: resolveHeader, + tracker: options.Tracker, + } +} + +func getManifestMediaType(resp *http.Response) string { + // Strip encoding data (manifests should always be ascii JSON) + contentType := resp.Header.Get("Content-Type") + if sp := strings.IndexByte(contentType, ';'); sp != -1 { + contentType = contentType[0:sp] + } + + // As of Apr 30 2019 the registry.access.redhat.com registry does not specify + // the content type of any data but uses schema1 manifests. + if contentType == "text/plain" { + contentType = images.MediaTypeDockerSchema1Manifest } + return contentType +} + +type countingReader struct { + reader io.Reader + bytesRead int64 +} + +func (r *countingReader) Read(p []byte) (int, error) { + n, err := r.reader.Read(p) + r.bytesRead += int64(n) + return n, err } var _ remotes.Resolver = &dockerResolver{} func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocispec.Descriptor, error) { - refspec, err := reference.Parse(ref) + base, err := r.resolveDockerBase(ref) if err != nil { return "", ocispec.Descriptor{}, err } - + refspec := base.refspec if refspec.Object == "" { return "", ocispec.Descriptor{}, reference.ErrObjectRequired } - base, err := r.base(refspec) - if err != nil { - return "", ocispec.Descriptor{}, err - } - - fetcher := dockerFetcher{ - dockerBase: base, - } - var ( - urls []string - dgst = refspec.Digest() + firstErr error + paths [][]string + dgst = refspec.Digest() + caps = HostCapabilityPull ) if dgst != "" { @@ -164,98 +243,159 @@ func (r *dockerResolver) Resolve(ctx context.Context, ref string) (string, ocisp } // turns out, we have a valid digest, make a url. - urls = append(urls, fetcher.url("manifests", dgst.String())) + paths = append(paths, []string{"manifests", dgst.String()}) // fallback to blobs on not found. - urls = append(urls, fetcher.url("blobs", dgst.String())) + paths = append(paths, []string{"blobs", dgst.String()}) } else { - urls = append(urls, fetcher.url("manifests", refspec.Object)) + // Add + paths = append(paths, []string{"manifests", refspec.Object}) + caps |= HostCapabilityResolve + } + + hosts := base.filterHosts(caps) + if len(hosts) == 0 { + return "", ocispec.Descriptor{}, errors.Wrap(errdefs.ErrNotFound, "no resolve hosts") } - ctx, err = contextWithRepositoryScope(ctx, refspec, false) + ctx, err = ContextWithRepositoryScope(ctx, refspec, false) if err != nil { return "", ocispec.Descriptor{}, err } - for _, u := range urls { - req, err := http.NewRequest(http.MethodHead, u, nil) - if err != nil { - return "", ocispec.Descriptor{}, err - } - // set headers for all the types we support for resolution. - req.Header.Set("Accept", strings.Join([]string{ - images.MediaTypeDockerSchema2Manifest, - images.MediaTypeDockerSchema2ManifestList, - ocispec.MediaTypeImageManifest, - ocispec.MediaTypeImageIndex, "*"}, ", ")) + for _, u := range paths { + for _, host := range hosts { + ctx := log.WithLogger(ctx, log.G(ctx).WithField("host", host.Host)) - log.G(ctx).Debug("resolving") - resp, err := fetcher.doRequestWithRetries(ctx, req, nil) - if err != nil { - if errors.Cause(err) == ErrInvalidAuthorization { - err = errors.Wrapf(err, "pull access denied, repository does not exist or may require authorization") + req := base.request(host, http.MethodHead, u...) + if err := req.addNamespace(base.refspec.Hostname()); err != nil { + return "", ocispec.Descriptor{}, err } - return "", ocispec.Descriptor{}, err - } - resp.Body.Close() // don't care about body contents. - if resp.StatusCode > 299 { - if resp.StatusCode == http.StatusNotFound { - continue + for key, value := range r.resolveHeader { + req.header[key] = append(req.header[key], value...) } - return "", ocispec.Descriptor{}, errors.Errorf("unexpected status code %v: %v", u, resp.Status) - } - // this is the only point at which we trust the registry. we use the - // content headers to assemble a descriptor for the name. when this becomes - // more robust, we mostly get this information from a secure trust store. - dgstHeader := digest.Digest(resp.Header.Get("Docker-Content-Digest")) - - if dgstHeader != "" { - if err := dgstHeader.Validate(); err != nil { - return "", ocispec.Descriptor{}, errors.Wrapf(err, "%q in header not a valid digest", dgstHeader) + log.G(ctx).Debug("resolving") + resp, err := req.doWithRetries(ctx, nil) + if err != nil { + if errors.Is(err, ErrInvalidAuthorization) { + err = errors.Wrapf(err, "pull access denied, repository does not exist or may require authorization") + } + // Store the error for referencing later + if firstErr == nil { + firstErr = err + } + log.G(ctx).WithError(err).Info("trying next host") + continue // try another host + } + resp.Body.Close() // don't care about body contents. + + if resp.StatusCode > 299 { + if resp.StatusCode == http.StatusNotFound { + log.G(ctx).Info("trying next host - response was http.StatusNotFound") + continue + } + if resp.StatusCode > 399 { + // Set firstErr when encountering the first non-404 status code. + if firstErr == nil { + firstErr = errors.Errorf("pulling from host %s failed with status code %v: %v", host.Host, u, resp.Status) + } + continue // try another host + } + return "", ocispec.Descriptor{}, errors.Errorf("pulling from host %s failed with unexpected status code %v: %v", host.Host, u, resp.Status) + } + size := resp.ContentLength + contentType := getManifestMediaType(resp) + + // if no digest was provided, then only a resolve + // trusted registry was contacted, in this case use + // the digest header (or content from GET) + if dgst == "" { + // this is the only point at which we trust the registry. we use the + // content headers to assemble a descriptor for the name. when this becomes + // more robust, we mostly get this information from a secure trust store. + dgstHeader := digest.Digest(resp.Header.Get("Docker-Content-Digest")) + + if dgstHeader != "" && size != -1 { + if err := dgstHeader.Validate(); err != nil { + return "", ocispec.Descriptor{}, errors.Wrapf(err, "%q in header not a valid digest", dgstHeader) + } + dgst = dgstHeader + } + } + if dgst == "" || size == -1 { + log.G(ctx).Debug("no Docker-Content-Digest header, fetching manifest instead") + + req = base.request(host, http.MethodGet, u...) + if err := req.addNamespace(base.refspec.Hostname()); err != nil { + return "", ocispec.Descriptor{}, err + } + + for key, value := range r.resolveHeader { + req.header[key] = append(req.header[key], value...) + } + + resp, err := req.doWithRetries(ctx, nil) + if err != nil { + return "", ocispec.Descriptor{}, err + } + defer resp.Body.Close() + + bodyReader := countingReader{reader: resp.Body} + + contentType = getManifestMediaType(resp) + if dgst == "" { + if contentType == images.MediaTypeDockerSchema1Manifest { + b, err := schema1.ReadStripSignature(&bodyReader) + if err != nil { + return "", ocispec.Descriptor{}, err + } + + dgst = digest.FromBytes(b) + } else { + dgst, err = digest.FromReader(&bodyReader) + if err != nil { + return "", ocispec.Descriptor{}, err + } + } + } else if _, err := io.Copy(ioutil.Discard, &bodyReader); err != nil { + return "", ocispec.Descriptor{}, err + } + size = bodyReader.bytesRead + } + // Prevent resolving to excessively large manifests + if size > MaxManifestSize { + if firstErr == nil { + firstErr = errors.Wrapf(errdefs.ErrNotFound, "rejecting %d byte manifest for %s", size, ref) + } + continue } - dgst = dgstHeader - } - - if dgst == "" { - return "", ocispec.Descriptor{}, errors.Errorf("could not resolve digest for %v", ref) - } - - var ( - size int64 - sizeHeader = resp.Header.Get("Content-Length") - ) - size, err = strconv.ParseInt(sizeHeader, 10, 64) - if err != nil { + desc := ocispec.Descriptor{ + Digest: dgst, + MediaType: contentType, + Size: size, + } - return "", ocispec.Descriptor{}, errors.Wrapf(err, "invalid size header: %q", sizeHeader) - } - if size < 0 { - return "", ocispec.Descriptor{}, errors.Errorf("%q in header not a valid size", sizeHeader) + log.G(ctx).WithField("desc.digest", desc.Digest).Debug("resolved") + return ref, desc, nil } + } - desc := ocispec.Descriptor{ - Digest: dgst, - MediaType: resp.Header.Get("Content-Type"), // need to strip disposition? - Size: size, - } + // If above loop terminates without return, then there was an error. + // "firstErr" contains the first non-404 error. That is, "firstErr == nil" + // means that either no registries were given or each registry returned 404. - log.G(ctx).WithField("desc.digest", desc.Digest).Debug("resolved") - return ref, desc, nil + if firstErr == nil { + firstErr = errors.Wrap(errdefs.ErrNotFound, ref) } - return "", ocispec.Descriptor{}, errors.Errorf("%v not found", ref) + return "", ocispec.Descriptor{}, firstErr } func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) { - refspec, err := reference.Parse(ref) - if err != nil { - return nil, err - } - - base, err := r.base(refspec) + base, err := r.resolveDockerBase(ref) if err != nil { return nil, err } @@ -266,79 +406,84 @@ func (r *dockerResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetch } func (r *dockerResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) { - refspec, err := reference.Parse(ref) + base, err := r.resolveDockerBase(ref) if err != nil { return nil, err } - // Manifests can be pushed by digest like any other object, but the passed in - // reference cannot take a digest without the associated content. A tag is allowed - // and will be used to tag pushed manifests. - if refspec.Object != "" && strings.Contains(refspec.Object, "@") { - return nil, errors.New("cannot use digest reference for push locator") - } + return dockerPusher{ + dockerBase: base, + object: base.refspec.Object, + tracker: r.tracker, + }, nil +} - base, err := r.base(refspec) +func (r *dockerResolver) resolveDockerBase(ref string) (*dockerBase, error) { + refspec, err := reference.Parse(ref) if err != nil { return nil, err } - return dockerPusher{ - dockerBase: base, - tag: refspec.Object, - tracker: r.tracker, - }, nil + return r.base(refspec) } type dockerBase struct { - refspec reference.Spec - base url.URL - - client *http.Client - auth Authorizer + refspec reference.Spec + repository string + hosts []RegistryHost + header http.Header } func (r *dockerResolver) base(refspec reference.Spec) (*dockerBase, error) { - var ( - err error - base url.URL - ) - host := refspec.Hostname() - base.Host = host - if r.host != nil { - base.Host, err = r.host(host) - if err != nil { - return nil, err - } - } - - base.Scheme = "https" - if r.plainHTTP || strings.HasPrefix(base.Host, "localhost:") { - base.Scheme = "http" + hosts, err := r.hosts(host) + if err != nil { + return nil, err } - - prefix := strings.TrimPrefix(refspec.Locator, host+"/") - base.Path = path.Join("/v2", prefix) - return &dockerBase{ - refspec: refspec, - base: base, - client: r.client, - auth: r.auth, + refspec: refspec, + repository: strings.TrimPrefix(refspec.Locator, host+"/"), + hosts: hosts, + header: r.header, }, nil } -func (r *dockerBase) url(ps ...string) string { - url := r.base - url.Path = path.Join(url.Path, path.Join(ps...)) - return url.String() +func (r *dockerBase) filterHosts(caps HostCapabilities) (hosts []RegistryHost) { + for _, host := range r.hosts { + if host.Capabilities.Has(caps) { + hosts = append(hosts, host) + } + } + return +} + +func (r *dockerBase) request(host RegistryHost, method string, ps ...string) *request { + header := r.header.Clone() + if header == nil { + header = http.Header{} + } + + for key, value := range host.Header { + header[key] = append(header[key], value...) + } + parts := append([]string{"/", host.Path, r.repository}, ps...) + p := path.Join(parts...) + // Join strips trailing slash, re-add ending "/" if included + if len(parts) > 0 && strings.HasSuffix(parts[len(parts)-1], "/") { + p = p + "/" + } + return &request{ + method: method, + path: p, + header: header, + host: host, + } } -func (r *dockerBase) authorize(ctx context.Context, req *http.Request) error { +func (r *request) authorize(ctx context.Context, req *http.Request) error { // Check if has header for host - if r.auth != nil { - if err := r.auth.Authorize(ctx, req); err != nil { + if r.host.Authorizer != nil { + if err := r.host.Authorizer.Authorize(ctx, req); err != nil { return err } } @@ -346,80 +491,177 @@ func (r *dockerBase) authorize(ctx context.Context, req *http.Request) error { return nil } -func (r *dockerBase) doRequest(ctx context.Context, req *http.Request) (*http.Response, error) { - ctx = log.WithLogger(ctx, log.G(ctx).WithField("url", req.URL.String())) - log.G(ctx).WithField("request.headers", req.Header).WithField("request.method", req.Method).Debug("do request") +func (r *request) addNamespace(ns string) (err error) { + if !r.host.isProxy(ns) { + return nil + } + var q url.Values + // Parse query + if i := strings.IndexByte(r.path, '?'); i > 0 { + r.path = r.path[:i+1] + q, err = url.ParseQuery(r.path[i+1:]) + if err != nil { + return + } + } else { + r.path = r.path + "?" + q = url.Values{} + } + q.Add("ns", ns) + + r.path = r.path + q.Encode() + + return +} + +type request struct { + method string + path string + header http.Header + host RegistryHost + body func() (io.ReadCloser, error) + size int64 +} + +func (r *request) do(ctx context.Context) (*http.Response, error) { + u := r.host.Scheme + "://" + r.host.Host + r.path + req, err := http.NewRequest(r.method, u, nil) + if err != nil { + return nil, err + } + req.Header = http.Header{} // headers need to be copied to avoid concurrent map access + for k, v := range r.header { + req.Header[k] = v + } + if r.body != nil { + body, err := r.body() + if err != nil { + return nil, err + } + req.Body = body + req.GetBody = r.body + if r.size > 0 { + req.ContentLength = r.size + } + } + + ctx = log.WithLogger(ctx, log.G(ctx).WithField("url", u)) + log.G(ctx).WithFields(requestFields(req)).Debug("do request") if err := r.authorize(ctx, req); err != nil { return nil, errors.Wrap(err, "failed to authorize") } - resp, err := ctxhttp.Do(ctx, r.client, req) + + var client = &http.Client{} + if r.host.Client != nil { + *client = *r.host.Client + } + if client.CheckRedirect == nil { + client.CheckRedirect = func(req *http.Request, via []*http.Request) error { + if len(via) >= 10 { + return errors.New("stopped after 10 redirects") + } + return errors.Wrap(r.authorize(ctx, req), "failed to authorize redirect") + } + } + + resp, err := ctxhttp.Do(ctx, client, req) if err != nil { return nil, errors.Wrap(err, "failed to do request") } - log.G(ctx).WithFields(logrus.Fields{ - "status": resp.Status, - "response.headers": resp.Header, - }).Debug("fetch response received") + log.G(ctx).WithFields(responseFields(resp)).Debug("fetch response received") return resp, nil } -func (r *dockerBase) doRequestWithRetries(ctx context.Context, req *http.Request, responses []*http.Response) (*http.Response, error) { - resp, err := r.doRequest(ctx, req) +func (r *request) doWithRetries(ctx context.Context, responses []*http.Response) (*http.Response, error) { + resp, err := r.do(ctx) if err != nil { return nil, err } responses = append(responses, resp) - req, err = r.retryRequest(ctx, req, responses) + retry, err := r.retryRequest(ctx, responses) if err != nil { resp.Body.Close() return nil, err } - if req != nil { + if retry { resp.Body.Close() - return r.doRequestWithRetries(ctx, req, responses) + return r.doWithRetries(ctx, responses) } return resp, err } -func (r *dockerBase) retryRequest(ctx context.Context, req *http.Request, responses []*http.Response) (*http.Request, error) { +func (r *request) retryRequest(ctx context.Context, responses []*http.Response) (bool, error) { if len(responses) > 5 { - return nil, nil + return false, nil } last := responses[len(responses)-1] - if last.StatusCode == http.StatusUnauthorized { + switch last.StatusCode { + case http.StatusUnauthorized: log.G(ctx).WithField("header", last.Header.Get("WWW-Authenticate")).Debug("Unauthorized") - if r.auth != nil { - if err := r.auth.AddResponses(ctx, responses); err == nil { - return copyRequest(req) + if r.host.Authorizer != nil { + if err := r.host.Authorizer.AddResponses(ctx, responses); err == nil { + return true, nil } else if !errdefs.IsNotImplemented(err) { - return nil, err + return false, err } } - return nil, nil - } else if last.StatusCode == http.StatusMethodNotAllowed && req.Method == http.MethodHead { + return false, nil + case http.StatusMethodNotAllowed: // Support registries which have not properly implemented the HEAD method for // manifests endpoint - if strings.Contains(req.URL.Path, "/manifests/") { - // TODO: copy request? - req.Method = http.MethodGet - return copyRequest(req) + if r.method == http.MethodHead && strings.Contains(r.path, "/manifests/") { + r.method = http.MethodGet + return true, nil } + case http.StatusRequestTimeout, http.StatusTooManyRequests: + return true, nil } // TODO: Handle 50x errors accounting for attempt history - return nil, nil + return false, nil } -func copyRequest(req *http.Request) (*http.Request, error) { - ireq := *req - if ireq.GetBody != nil { - var err error - ireq.Body, err = ireq.GetBody() - if err != nil { - return nil, err +func (r *request) String() string { + return r.host.Scheme + "://" + r.host.Host + r.path +} + +func requestFields(req *http.Request) logrus.Fields { + fields := map[string]interface{}{ + "request.method": req.Method, + } + for k, vals := range req.Header { + k = strings.ToLower(k) + if k == "authorization" { + continue } + for i, v := range vals { + field := "request.header." + k + if i > 0 { + field = fmt.Sprintf("%s.%d", field, i) + } + fields[field] = v + } + } + + return logrus.Fields(fields) +} + +func responseFields(resp *http.Response) logrus.Fields { + fields := map[string]interface{}{ + "response.status": resp.Status, } - return &ireq, nil + for k, vals := range resp.Header { + k = strings.ToLower(k) + for i, v := range vals { + field := "response.header." + k + if i > 0 { + field = fmt.Sprintf("%s.%d", field, i) + } + fields[field] = v + } + } + + return logrus.Fields(fields) } diff --git a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go index 45ac1933fd9f4..f15a9acf3e81c 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "io/ioutil" + "strconv" "strings" "sync" "time" @@ -42,7 +43,10 @@ import ( "github.com/pkg/errors" ) -const manifestSizeLimit = 8e6 // 8MB +const ( + manifestSizeLimit = 8e6 // 8MB + labelDockerSchema1EmptyLayer = "containerd.io/docker.schema1.empty-layer" +) type blobState struct { diffID digest.Digest @@ -212,17 +216,28 @@ func (c *Converter) Convert(ctx context.Context, opts ...ConvertOpt) (ocispec.De ref := remotes.MakeRefKey(ctx, desc) if err := content.WriteBlob(ctx, c.contentStore, ref, bytes.NewReader(mb), desc, content.WithLabels(labels)); err != nil { - return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config") + return ocispec.Descriptor{}, errors.Wrap(err, "failed to write image manifest") } ref = remotes.MakeRefKey(ctx, config) if err := content.WriteBlob(ctx, c.contentStore, ref, bytes.NewReader(b), config); err != nil { - return ocispec.Descriptor{}, errors.Wrap(err, "failed to write config") + return ocispec.Descriptor{}, errors.Wrap(err, "failed to write image config") } return desc, nil } +// ReadStripSignature reads in a schema1 manifest and returns a byte array +// with the "signatures" field stripped +func ReadStripSignature(schema1Blob io.Reader) ([]byte, error) { + b, err := ioutil.ReadAll(io.LimitReader(schema1Blob, manifestSizeLimit)) // limit to 8MB + if err != nil { + return nil, err + } + + return stripSignature(b) +} + func (c *Converter) fetchManifest(ctx context.Context, desc ocispec.Descriptor) error { log.G(ctx).Debug("fetch schema 1") @@ -231,21 +246,19 @@ func (c *Converter) fetchManifest(ctx context.Context, desc ocispec.Descriptor) return err } - b, err := ioutil.ReadAll(io.LimitReader(rc, manifestSizeLimit)) // limit to 8MB + b, err := ReadStripSignature(rc) rc.Close() if err != nil { return err } - b, err = stripSignature(b) - if err != nil { - return err - } - var m manifest if err := json.Unmarshal(b, &m); err != nil { return err } + if len(m.Manifests) != 0 || len(m.Layers) != 0 { + return errors.New("converter: expected schema1 document but found extra keys") + } c.pulledManifest = &m return nil @@ -353,10 +366,11 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro Digest: desc.Digest, Labels: map[string]string{ "containerd.io/uncompressed": state.diffID.String(), + labelDockerSchema1EmptyLayer: strconv.FormatBool(state.empty), }, } - if _, err := c.contentStore.Update(ctx, cinfo, "labels.containerd.io/uncompressed"); err != nil { + if _, err := c.contentStore.Update(ctx, cinfo, "labels.containerd.io/uncompressed", fmt.Sprintf("labels.%s", labelDockerSchema1EmptyLayer)); err != nil { return errors.Wrap(err, "failed to update uncompressed label") } @@ -380,7 +394,18 @@ func (c *Converter) reuseLabelBlobState(ctx context.Context, desc ocispec.Descri return false, nil } - bState := blobState{empty: false} + emptyVal, ok := cinfo.Labels[labelDockerSchema1EmptyLayer] + if !ok { + return false, nil + } + + isEmpty, err := strconv.ParseBool(emptyVal) + if err != nil { + log.G(ctx).WithField("id", desc.Digest).Warnf("failed to parse bool from label %s: %v", labelDockerSchema1EmptyLayer, isEmpty) + return false, nil + } + + bState := blobState{empty: isEmpty} if bState.diffID, err = digest.Parse(diffID); err != nil { log.G(ctx).WithField("id", desc.Digest).Warnf("failed to parse digest from label containerd.io/uncompressed: %v", diffID) @@ -450,8 +475,10 @@ type history struct { } type manifest struct { - FSLayers []fsLayer `json:"fsLayers"` - History []history `json:"history"` + FSLayers []fsLayer `json:"fsLayers"` + History []history `json:"history"` + Layers json.RawMessage `json:"layers,omitempty"` // OCI manifest + Manifests json.RawMessage `json:"manifests,omitempty"` // OCI index } type v1History struct { diff --git a/vendor/github.com/containerd/containerd/remotes/docker/scope.go b/vendor/github.com/containerd/containerd/remotes/docker/scope.go index 52c2443118846..fe57f023d35a6 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/scope.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/scope.go @@ -18,6 +18,7 @@ package docker import ( "context" + "fmt" "net/url" "sort" "strings" @@ -25,10 +26,10 @@ import ( "github.com/containerd/containerd/reference" ) -// repositoryScope returns a repository scope string such as "repository:foo/bar:pull" +// RepositoryScope returns a repository scope string such as "repository:foo/bar:pull" // for "host/foo/bar:baz". // When push is true, both pull and push are added to the scope. -func repositoryScope(refspec reference.Spec, push bool) (string, error) { +func RepositoryScope(refspec reference.Spec, push bool) (string, error) { u, err := url.Parse("dummy://" + refspec.Locator) if err != nil { return "", err @@ -44,33 +45,53 @@ func repositoryScope(refspec reference.Spec, push bool) (string, error) { // value: []string (e.g. {"registry:foo/bar:pull"}) type tokenScopesKey struct{} -// contextWithRepositoryScope returns a context with tokenScopesKey{} and the repository scope value. -func contextWithRepositoryScope(ctx context.Context, refspec reference.Spec, push bool) (context.Context, error) { - s, err := repositoryScope(refspec, push) +// ContextWithRepositoryScope returns a context with tokenScopesKey{} and the repository scope value. +func ContextWithRepositoryScope(ctx context.Context, refspec reference.Spec, push bool) (context.Context, error) { + s, err := RepositoryScope(refspec, push) if err != nil { return nil, err } - return context.WithValue(ctx, tokenScopesKey{}, []string{s}), nil + return WithScope(ctx, s), nil } -// getTokenScopes returns deduplicated and sorted scopes from ctx.Value(tokenScopesKey{}) and params["scope"]. -func getTokenScopes(ctx context.Context, params map[string]string) []string { +// WithScope appends a custom registry auth scope to the context. +func WithScope(ctx context.Context, scope string) context.Context { + var scopes []string + if v := ctx.Value(tokenScopesKey{}); v != nil { + scopes = v.([]string) + scopes = append(scopes, scope) + } else { + scopes = []string{scope} + } + return context.WithValue(ctx, tokenScopesKey{}, scopes) +} + +// ContextWithAppendPullRepositoryScope is used to append repository pull +// scope into existing scopes indexed by the tokenScopesKey{}. +func ContextWithAppendPullRepositoryScope(ctx context.Context, repo string) context.Context { + return WithScope(ctx, fmt.Sprintf("repository:%s:pull", repo)) +} + +// GetTokenScopes returns deduplicated and sorted scopes from ctx.Value(tokenScopesKey{}) and common scopes. +func GetTokenScopes(ctx context.Context, common []string) []string { var scopes []string if x := ctx.Value(tokenScopesKey{}); x != nil { scopes = append(scopes, x.([]string)...) } - if scope, ok := params["scope"]; ok { - for _, s := range scopes { - // Note: this comparison is unaware of the scope grammar (https://docs.docker.com/registry/spec/auth/scope/) - // So, "repository:foo/bar:pull,push" != "repository:foo/bar:push,pull", although semantically they are equal. - if s == scope { - // already appended - goto Sort - } + + scopes = append(scopes, common...) + sort.Strings(scopes) + + l := 0 + for idx := 1; idx < len(scopes); idx++ { + // Note: this comparison is unaware of the scope grammar (https://docs.docker.com/registry/spec/auth/scope/) + // So, "repository:foo/bar:pull,push" != "repository:foo/bar:push,pull", although semantically they are equal. + if scopes[l] == scopes[idx] { + continue } - scopes = append(scopes, scope) + + l++ + scopes[l] = scopes[idx] } -Sort: - sort.Strings(scopes) - return scopes + return scopes[:l+1] } diff --git a/vendor/github.com/containerd/containerd/remotes/docker/status.go b/vendor/github.com/containerd/containerd/remotes/docker/status.go index 8069d6767133e..9751edac7f99e 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/status.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/status.go @@ -21,6 +21,7 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" + "github.com/moby/locker" "github.com/pkg/errors" ) @@ -28,6 +29,8 @@ import ( type Status struct { content.Status + Committed bool + // UploadUUID is used by the Docker registry to reference blob uploads UploadUUID string } @@ -38,15 +41,24 @@ type StatusTracker interface { SetStatus(string, Status) } +// StatusTrackLocker to track status of operations with lock +type StatusTrackLocker interface { + StatusTracker + Lock(string) + Unlock(string) +} + type memoryStatusTracker struct { statuses map[string]Status m sync.Mutex + locker *locker.Locker } // NewInMemoryTracker returns a StatusTracker that tracks content status in-memory -func NewInMemoryTracker() StatusTracker { +func NewInMemoryTracker() StatusTrackLocker { return &memoryStatusTracker{ statuses: map[string]Status{}, + locker: locker.New(), } } @@ -65,3 +77,11 @@ func (t *memoryStatusTracker) SetStatus(ref string, status Status) { t.statuses[ref] = status t.m.Unlock() } + +func (t *memoryStatusTracker) Lock(ref string) { + t.locker.Lock(ref) +} + +func (t *memoryStatusTracker) Unlock(ref string) { + t.locker.Unlock(ref) +} diff --git a/vendor/github.com/containerd/containerd/remotes/errors/errors.go b/vendor/github.com/containerd/containerd/remotes/errors/errors.go new file mode 100644 index 0000000000000..519dbac105f21 --- /dev/null +++ b/vendor/github.com/containerd/containerd/remotes/errors/errors.go @@ -0,0 +1,56 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package errors + +import ( + "fmt" + "io" + "io/ioutil" + "net/http" +) + +var _ error = ErrUnexpectedStatus{} + +// ErrUnexpectedStatus is returned if a registry API request returned with unexpected HTTP status +type ErrUnexpectedStatus struct { + Status string + StatusCode int + Body []byte + RequestURL, RequestMethod string +} + +func (e ErrUnexpectedStatus) Error() string { + return fmt.Sprintf("unexpected status: %s", e.Status) +} + +// NewUnexpectedStatusErr creates an ErrUnexpectedStatus from HTTP response +func NewUnexpectedStatusErr(resp *http.Response) error { + var b []byte + if resp.Body != nil { + b, _ = ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB + } + err := ErrUnexpectedStatus{ + Body: b, + Status: resp.Status, + StatusCode: resp.StatusCode, + RequestMethod: resp.Request.Method, + } + if resp.Request.URL != nil { + err.RequestURL = resp.Request.URL.String() + } + return err +} diff --git a/vendor/github.com/containerd/containerd/remotes/handlers.go b/vendor/github.com/containerd/containerd/remotes/handlers.go index 77310fb62cb5c..8f79c608e68ce 100644 --- a/vendor/github.com/containerd/containerd/remotes/handlers.go +++ b/vendor/github.com/containerd/containerd/remotes/handlers.go @@ -31,30 +31,57 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/sync/semaphore" ) +type refKeyPrefix struct{} + +// WithMediaTypeKeyPrefix adds a custom key prefix for a media type which is used when storing +// data in the content store from the FetchHandler. +// +// Used in `MakeRefKey` to determine what the key prefix should be. +func WithMediaTypeKeyPrefix(ctx context.Context, mediaType, prefix string) context.Context { + var values map[string]string + if v := ctx.Value(refKeyPrefix{}); v != nil { + values = v.(map[string]string) + } else { + values = make(map[string]string) + } + + values[mediaType] = prefix + return context.WithValue(ctx, refKeyPrefix{}, values) +} + // MakeRefKey returns a unique reference for the descriptor. This reference can be // used to lookup ongoing processes related to the descriptor. This function // may look to the context to namespace the reference appropriately. func MakeRefKey(ctx context.Context, desc ocispec.Descriptor) string { - // TODO(stevvooe): Need better remote key selection here. Should be a - // product of the context, which may include information about the ongoing - // fetch process. - switch desc.MediaType { - case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: - return "manifest-" + desc.Digest.String() - case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: - return "index-" + desc.Digest.String() - case images.MediaTypeDockerSchema2Layer, images.MediaTypeDockerSchema2LayerGzip, - images.MediaTypeDockerSchema2LayerForeign, images.MediaTypeDockerSchema2LayerForeignGzip, - ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerGzip, - ocispec.MediaTypeImageLayerNonDistributable, ocispec.MediaTypeImageLayerNonDistributableGzip: - return "layer-" + desc.Digest.String() - case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig: - return "config-" + desc.Digest.String() + key := desc.Digest.String() + if desc.Annotations != nil { + if name, ok := desc.Annotations[ocispec.AnnotationRefName]; ok { + key = fmt.Sprintf("%s@%s", name, desc.Digest.String()) + } + } + + if v := ctx.Value(refKeyPrefix{}); v != nil { + values := v.(map[string]string) + if prefix := values[desc.MediaType]; prefix != "" { + return prefix + "-" + key + } + } + + switch mt := desc.MediaType; { + case mt == images.MediaTypeDockerSchema2Manifest || mt == ocispec.MediaTypeImageManifest: + return "manifest-" + key + case mt == images.MediaTypeDockerSchema2ManifestList || mt == ocispec.MediaTypeImageIndex: + return "index-" + key + case images.IsLayerType(mt): + return "layer-" + key + case images.IsKnownConfig(mt): + return "config-" + key default: - log.G(ctx).Warnf("reference for unknown type: %s", desc.MediaType) - return "unknown-" + desc.Digest.String() + log.G(ctx).Warnf("reference for unknown type: %s", mt) + return "unknown-" + key } } @@ -96,6 +123,12 @@ func fetch(ctx context.Context, ingester content.Ingester, fetcher Fetcher, desc return err } + if desc.Size == 0 { + // most likely a poorly configured registry/web front end which responded with no + // Content-Length header; unable (not to mention useless) to commit a 0-length entry + // into the content store. Error out here otherwise the error sent back is confusing + return errors.Wrapf(errdefs.ErrInvalidArgument, "unable to fetch descriptor (%s) which reports content size of zero", desc.Digest) + } if ws.Offset == desc.Size { // If writer is already complete, commit and return err := cw.Commit(ctx, desc.Size, desc.Digest) @@ -132,7 +165,15 @@ func PushHandler(pusher Pusher, provider content.Provider) images.HandlerFunc { func push(ctx context.Context, provider content.Provider, pusher Pusher, desc ocispec.Descriptor) error { log.G(ctx).Debug("push") - cw, err := pusher.Push(ctx, desc) + var ( + cw content.Writer + err error + ) + if cs, ok := pusher.(content.Ingester); ok { + cw, err = content.OpenWriter(ctx, cs, content.WithRef(MakeRefKey(ctx, desc)), content.WithDescriptor(desc)) + } else { + cw, err = pusher.Push(ctx, desc) + } if err != nil { if !errdefs.IsAlreadyExists(err) { return err @@ -156,7 +197,8 @@ func push(ctx context.Context, provider content.Provider, pusher Pusher, desc oc // // Base handlers can be provided which will be called before any push specific // handlers. -func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, provider content.Provider, platform platforms.MatchComparer, baseHandlers ...images.Handler) error { +func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, store content.Store, limiter *semaphore.Weighted, platform platforms.MatchComparer, wrapper func(h images.Handler) images.Handler) error { + var m sync.Mutex manifestStack := []ocispec.Descriptor{} @@ -173,15 +215,22 @@ func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, pr } }) - pushHandler := PushHandler(pusher, provider) + pushHandler := PushHandler(pusher, store) + + platformFilterhandler := images.FilterPlatforms(images.ChildrenHandler(store), platform) + + annotateHandler := annotateDistributionSourceHandler(platformFilterhandler, store) - handlers := append(baseHandlers, - images.FilterPlatforms(images.ChildrenHandler(provider), platform), + var handler images.Handler = images.Handlers( + annotateHandler, filterHandler, pushHandler, ) + if wrapper != nil { + handler = wrapper(handler) + } - if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil { + if err := images.Dispatch(ctx, handler, limiter, desc); err != nil { return err } @@ -203,3 +252,80 @@ func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, pr return nil } + +// FilterManifestByPlatformHandler allows Handler to handle non-target +// platform's manifest and configuration data. +func FilterManifestByPlatformHandler(f images.HandlerFunc, m platforms.Matcher) images.HandlerFunc { + return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + children, err := f(ctx, desc) + if err != nil { + return nil, err + } + + // no platform information + if desc.Platform == nil || m == nil { + return children, nil + } + + var descs []ocispec.Descriptor + switch desc.MediaType { + case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: + if m.Match(*desc.Platform) { + descs = children + } else { + for _, child := range children { + if child.MediaType == images.MediaTypeDockerSchema2Config || + child.MediaType == ocispec.MediaTypeImageConfig { + + descs = append(descs, child) + } + } + } + default: + descs = children + } + return descs, nil + } +} + +// annotateDistributionSourceHandler add distribution source label into +// annotation of config or blob descriptor. +func annotateDistributionSourceHandler(f images.HandlerFunc, manager content.Manager) images.HandlerFunc { + return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + children, err := f(ctx, desc) + if err != nil { + return nil, err + } + + // only add distribution source for the config or blob data descriptor + switch desc.MediaType { + case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest, + images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: + default: + return children, nil + } + + for i := range children { + child := children[i] + + info, err := manager.Info(ctx, child.Digest) + if err != nil { + return nil, err + } + + for k, v := range info.Labels { + if !strings.HasPrefix(k, "containerd.io/distribution.source.") { + continue + } + + if child.Annotations == nil { + child.Annotations = map[string]string{} + } + child.Annotations[k] = v + } + + children[i] = child + } + return children, nil + } +} diff --git a/vendor/github.com/containerd/containerd/remotes/resolver.go b/vendor/github.com/containerd/containerd/remotes/resolver.go index a9b2b78aa8fac..624b14f05d6c3 100644 --- a/vendor/github.com/containerd/containerd/remotes/resolver.go +++ b/vendor/github.com/containerd/containerd/remotes/resolver.go @@ -45,6 +45,8 @@ type Resolver interface { Fetcher(ctx context.Context, ref string) (Fetcher, error) // Pusher returns a new pusher for the provided reference + // The returned Pusher should satisfy content.Ingester and concurrent attempts + // to push the same blob using the Ingester API should result in ErrUnavailable. Pusher(ctx context.Context, ref string) (Pusher, error) } @@ -72,9 +74,9 @@ func (fn FetcherFunc) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.Re // PusherFunc allows package users to implement a Pusher with just a // function. -type PusherFunc func(ctx context.Context, desc ocispec.Descriptor, r io.Reader) error +type PusherFunc func(ctx context.Context, desc ocispec.Descriptor) (content.Writer, error) // Push content -func (fn PusherFunc) Push(ctx context.Context, desc ocispec.Descriptor, r io.Reader) error { - return fn(ctx, desc, r) +func (fn PusherFunc) Push(ctx context.Context, desc ocispec.Descriptor) (content.Writer, error) { + return fn(ctx, desc) } diff --git a/vendor/github.com/containerd/containerd/rootfs/apply.go b/vendor/github.com/containerd/containerd/rootfs/apply.go index 3ea830f6b4236..f1ca624bf98fc 100644 --- a/vendor/github.com/containerd/containerd/rootfs/apply.go +++ b/vendor/github.com/containerd/containerd/rootfs/apply.go @@ -48,6 +48,14 @@ type Layer struct { // Layers are applied in order they are given, making the first layer the // bottom-most layer in the layer chain. func ApplyLayers(ctx context.Context, layers []Layer, sn snapshots.Snapshotter, a diff.Applier) (digest.Digest, error) { + return ApplyLayersWithOpts(ctx, layers, sn, a, nil) +} + +// ApplyLayersWithOpts applies all the layers using the given snapshotter, applier, and apply opts. +// The returned result is a chain id digest representing all the applied layers. +// Layers are applied in order they are given, making the first layer the +// bottom-most layer in the layer chain. +func ApplyLayersWithOpts(ctx context.Context, layers []Layer, sn snapshots.Snapshotter, a diff.Applier, applyOpts []diff.ApplyOpt) (digest.Digest, error) { chain := make([]digest.Digest, len(layers)) for i, layer := range layers { chain[i] = layer.Diff.Digest @@ -63,7 +71,7 @@ func ApplyLayers(ctx context.Context, layers []Layer, sn snapshots.Snapshotter, return "", errors.Wrapf(err, "failed to stat snapshot %s", chainID) } - if err := applyLayers(ctx, layers, chain, sn, a); err != nil && !errdefs.IsAlreadyExists(err) { + if err := applyLayers(ctx, layers, chain, sn, a, nil, applyOpts); err != nil && !errdefs.IsAlreadyExists(err) { return "", err } } @@ -75,6 +83,13 @@ func ApplyLayers(ctx context.Context, layers []Layer, sn snapshots.Snapshotter, // using the provided snapshotter and applier. If the layer was unpacked true // is returned, if the layer already exists false is returned. func ApplyLayer(ctx context.Context, layer Layer, chain []digest.Digest, sn snapshots.Snapshotter, a diff.Applier, opts ...snapshots.Opt) (bool, error) { + return ApplyLayerWithOpts(ctx, layer, chain, sn, a, opts, nil) +} + +// ApplyLayerWithOpts applies a single layer on top of the given provided layer chain, +// using the provided snapshotter, applier, and apply opts. If the layer was unpacked true +// is returned, if the layer already exists false is returned. +func ApplyLayerWithOpts(ctx context.Context, layer Layer, chain []digest.Digest, sn snapshots.Snapshotter, a diff.Applier, opts []snapshots.Opt, applyOpts []diff.ApplyOpt) (bool, error) { var ( chainID = identity.ChainID(append(chain, layer.Diff.Digest)).String() applied bool @@ -84,7 +99,7 @@ func ApplyLayer(ctx context.Context, layer Layer, chain []digest.Digest, sn snap return false, errors.Wrapf(err, "failed to stat snapshot %s", chainID) } - if err := applyLayers(ctx, []Layer{layer}, append(chain, layer.Diff.Digest), sn, a, opts...); err != nil { + if err := applyLayers(ctx, []Layer{layer}, append(chain, layer.Diff.Digest), sn, a, opts, applyOpts); err != nil { if !errdefs.IsAlreadyExists(err) { return false, err } @@ -93,9 +108,10 @@ func ApplyLayer(ctx context.Context, layer Layer, chain []digest.Digest, sn snap } } return applied, nil + } -func applyLayers(ctx context.Context, layers []Layer, chain []digest.Digest, sn snapshots.Snapshotter, a diff.Applier, opts ...snapshots.Opt) error { +func applyLayers(ctx context.Context, layers []Layer, chain []digest.Digest, sn snapshots.Snapshotter, a diff.Applier, opts []snapshots.Opt, applyOpts []diff.ApplyOpt) error { var ( parent = identity.ChainID(chain[:len(chain)-1]) chainID = identity.ChainID(chain) @@ -107,13 +123,13 @@ func applyLayers(ctx context.Context, layers []Layer, chain []digest.Digest, sn ) for { - key = fmt.Sprintf("extract-%s %s", uniquePart(), chainID) + key = fmt.Sprintf(snapshots.UnpackKeyFormat, uniquePart(), chainID) // Prepare snapshot with from parent, label as root mounts, err = sn.Prepare(ctx, key, parent.String(), opts...) if err != nil { if errdefs.IsNotFound(err) && len(layers) > 1 { - if err := applyLayers(ctx, layers[:len(layers)-1], chain[:len(chain)-1], sn, a); err != nil { + if err := applyLayers(ctx, layers[:len(layers)-1], chain[:len(chain)-1], sn, a, opts, applyOpts); err != nil { if !errdefs.IsAlreadyExists(err) { return err } @@ -144,7 +160,7 @@ func applyLayers(ctx context.Context, layers []Layer, chain []digest.Digest, sn } }() - diff, err = a.Apply(ctx, layer.Blob, mounts) + diff, err = a.Apply(ctx, layer.Blob, mounts, applyOpts...) if err != nil { err = errors.Wrapf(err, "failed to extract layer %s", layer.Diff.Digest) return err diff --git a/vendor/github.com/containerd/containerd/rootfs/diff.go b/vendor/github.com/containerd/containerd/rootfs/diff.go index b3e6ba8a33b61..f396c73ab0948 100644 --- a/vendor/github.com/containerd/containerd/rootfs/diff.go +++ b/vendor/github.com/containerd/containerd/rootfs/diff.go @@ -22,6 +22,7 @@ import ( "github.com/containerd/containerd/diff" "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/snapshots" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -31,6 +32,13 @@ import ( // the content creation and the provided snapshotter and mount differ are used // for calculating the diff. The descriptor for the layer diff is returned. func CreateDiff(ctx context.Context, snapshotID string, sn snapshots.Snapshotter, d diff.Comparer, opts ...diff.Opt) (ocispec.Descriptor, error) { + // dctx is used to handle cleanup things just in case the param ctx + // has been canceled, which causes that the defer cleanup fails. + dctx := context.Background() + if ns, ok := namespaces.Namespace(ctx); ok { + dctx = namespaces.WithNamespace(dctx, ns) + } + info, err := sn.Stat(ctx, snapshotID) if err != nil { return ocispec.Descriptor{}, err @@ -41,7 +49,7 @@ func CreateDiff(ctx context.Context, snapshotID string, sn snapshots.Snapshotter if err != nil { return ocispec.Descriptor{}, err } - defer sn.Remove(ctx, lowerKey) + defer sn.Remove(dctx, lowerKey) var upper []mount.Mount if info.Kind == snapshots.KindActive { @@ -55,7 +63,7 @@ func CreateDiff(ctx context.Context, snapshotID string, sn snapshots.Snapshotter if err != nil { return ocispec.Descriptor{}, err } - defer sn.Remove(ctx, upperKey) + defer sn.Remove(dctx, upperKey) } return d.Compare(ctx, lower, upper, opts...) diff --git a/vendor/github.com/containerd/containerd/rootfs/init.go b/vendor/github.com/containerd/containerd/rootfs/init.go index 325e5531d5047..9316b9ddae9f5 100644 --- a/vendor/github.com/containerd/containerd/rootfs/init.go +++ b/vendor/github.com/containerd/containerd/rootfs/init.go @@ -67,7 +67,7 @@ func InitRootFS(ctx context.Context, name string, parent digest.Digest, readonly return snapshotter.Prepare(ctx, name, parentS) } -func createInitLayer(ctx context.Context, parent, initName string, initFn func(string) error, snapshotter snapshots.Snapshotter, mounter Mounter) (string, error) { +func createInitLayer(ctx context.Context, parent, initName string, initFn func(string) error, snapshotter snapshots.Snapshotter, mounter Mounter) (_ string, retErr error) { initS := fmt.Sprintf("%s %s", parent, initName) if _, err := snapshotter.Stat(ctx, initS); err == nil { return initS, nil @@ -87,7 +87,7 @@ func createInitLayer(ctx context.Context, parent, initName string, initFn func(s } defer func() { - if err != nil { + if retErr != nil { if rerr := snapshotter.Remove(ctx, td); rerr != nil { log.G(ctx).Errorf("Failed to remove snapshot %s: %v", td, rerr) } diff --git a/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.pb.go b/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.pb.go index c13d233599bd1..46d31ff59a514 100644 --- a/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.pb.go +++ b/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.pb.go @@ -1,30 +1,17 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/runtime/linux/runctypes/runc.proto -/* - Package runctypes is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/runtime/linux/runctypes/runc.proto - - It has these top-level messages: - RuncOptions - CreateOptions - CheckpointOptions - ProcessDetails -*/ package runctypes -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -35,58 +22,186 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type RuncOptions struct { - Runtime string `protobuf:"bytes,1,opt,name=runtime,proto3" json:"runtime,omitempty"` - RuntimeRoot string `protobuf:"bytes,2,opt,name=runtime_root,json=runtimeRoot,proto3" json:"runtime_root,omitempty"` - CriuPath string `protobuf:"bytes,3,opt,name=criu_path,json=criuPath,proto3" json:"criu_path,omitempty"` - SystemdCgroup bool `protobuf:"varint,4,opt,name=systemd_cgroup,json=systemdCgroup,proto3" json:"systemd_cgroup,omitempty"` + Runtime string `protobuf:"bytes,1,opt,name=runtime,proto3" json:"runtime,omitempty"` + RuntimeRoot string `protobuf:"bytes,2,opt,name=runtime_root,json=runtimeRoot,proto3" json:"runtime_root,omitempty"` + CriuPath string `protobuf:"bytes,3,opt,name=criu_path,json=criuPath,proto3" json:"criu_path,omitempty"` + SystemdCgroup bool `protobuf:"varint,4,opt,name=systemd_cgroup,json=systemdCgroup,proto3" json:"systemd_cgroup,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *RuncOptions) Reset() { *m = RuncOptions{} } -func (*RuncOptions) ProtoMessage() {} -func (*RuncOptions) Descriptor() ([]byte, []int) { return fileDescriptorRunc, []int{0} } +func (m *RuncOptions) Reset() { *m = RuncOptions{} } +func (*RuncOptions) ProtoMessage() {} +func (*RuncOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_d20e2ba8b3cc58b9, []int{0} +} +func (m *RuncOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RuncOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RuncOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RuncOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_RuncOptions.Merge(m, src) +} +func (m *RuncOptions) XXX_Size() int { + return m.Size() +} +func (m *RuncOptions) XXX_DiscardUnknown() { + xxx_messageInfo_RuncOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_RuncOptions proto.InternalMessageInfo type CreateOptions struct { - NoPivotRoot bool `protobuf:"varint,1,opt,name=no_pivot_root,json=noPivotRoot,proto3" json:"no_pivot_root,omitempty"` - OpenTcp bool `protobuf:"varint,2,opt,name=open_tcp,json=openTcp,proto3" json:"open_tcp,omitempty"` - ExternalUnixSockets bool `protobuf:"varint,3,opt,name=external_unix_sockets,json=externalUnixSockets,proto3" json:"external_unix_sockets,omitempty"` - Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` - FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"` - EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces" json:"empty_namespaces,omitempty"` - CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"` - NoNewKeyring bool `protobuf:"varint,8,opt,name=no_new_keyring,json=noNewKeyring,proto3" json:"no_new_keyring,omitempty"` - ShimCgroup string `protobuf:"bytes,9,opt,name=shim_cgroup,json=shimCgroup,proto3" json:"shim_cgroup,omitempty"` - IoUid uint32 `protobuf:"varint,10,opt,name=io_uid,json=ioUid,proto3" json:"io_uid,omitempty"` - IoGid uint32 `protobuf:"varint,11,opt,name=io_gid,json=ioGid,proto3" json:"io_gid,omitempty"` + NoPivotRoot bool `protobuf:"varint,1,opt,name=no_pivot_root,json=noPivotRoot,proto3" json:"no_pivot_root,omitempty"` + OpenTcp bool `protobuf:"varint,2,opt,name=open_tcp,json=openTcp,proto3" json:"open_tcp,omitempty"` + ExternalUnixSockets bool `protobuf:"varint,3,opt,name=external_unix_sockets,json=externalUnixSockets,proto3" json:"external_unix_sockets,omitempty"` + Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` + FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"` + EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces,proto3" json:"empty_namespaces,omitempty"` + CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"` + NoNewKeyring bool `protobuf:"varint,8,opt,name=no_new_keyring,json=noNewKeyring,proto3" json:"no_new_keyring,omitempty"` + ShimCgroup string `protobuf:"bytes,9,opt,name=shim_cgroup,json=shimCgroup,proto3" json:"shim_cgroup,omitempty"` + IoUid uint32 `protobuf:"varint,10,opt,name=io_uid,json=ioUid,proto3" json:"io_uid,omitempty"` + IoGid uint32 `protobuf:"varint,11,opt,name=io_gid,json=ioGid,proto3" json:"io_gid,omitempty"` + CriuWorkPath string `protobuf:"bytes,12,opt,name=criu_work_path,json=criuWorkPath,proto3" json:"criu_work_path,omitempty"` + CriuImagePath string `protobuf:"bytes,13,opt,name=criu_image_path,json=criuImagePath,proto3" json:"criu_image_path,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateOptions) Reset() { *m = CreateOptions{} } +func (*CreateOptions) ProtoMessage() {} +func (*CreateOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_d20e2ba8b3cc58b9, []int{1} +} +func (m *CreateOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateOptions.Merge(m, src) +} +func (m *CreateOptions) XXX_Size() int { + return m.Size() +} +func (m *CreateOptions) XXX_DiscardUnknown() { + xxx_messageInfo_CreateOptions.DiscardUnknown(m) } -func (m *CreateOptions) Reset() { *m = CreateOptions{} } -func (*CreateOptions) ProtoMessage() {} -func (*CreateOptions) Descriptor() ([]byte, []int) { return fileDescriptorRunc, []int{1} } +var xxx_messageInfo_CreateOptions proto.InternalMessageInfo type CheckpointOptions struct { - Exit bool `protobuf:"varint,1,opt,name=exit,proto3" json:"exit,omitempty"` - OpenTcp bool `protobuf:"varint,2,opt,name=open_tcp,json=openTcp,proto3" json:"open_tcp,omitempty"` - ExternalUnixSockets bool `protobuf:"varint,3,opt,name=external_unix_sockets,json=externalUnixSockets,proto3" json:"external_unix_sockets,omitempty"` - Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` - FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"` - EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces" json:"empty_namespaces,omitempty"` - CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"` + Exit bool `protobuf:"varint,1,opt,name=exit,proto3" json:"exit,omitempty"` + OpenTcp bool `protobuf:"varint,2,opt,name=open_tcp,json=openTcp,proto3" json:"open_tcp,omitempty"` + ExternalUnixSockets bool `protobuf:"varint,3,opt,name=external_unix_sockets,json=externalUnixSockets,proto3" json:"external_unix_sockets,omitempty"` + Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` + FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"` + EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces,proto3" json:"empty_namespaces,omitempty"` + CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"` + WorkPath string `protobuf:"bytes,8,opt,name=work_path,json=workPath,proto3" json:"work_path,omitempty"` + ImagePath string `protobuf:"bytes,9,opt,name=image_path,json=imagePath,proto3" json:"image_path,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *CheckpointOptions) Reset() { *m = CheckpointOptions{} } -func (*CheckpointOptions) ProtoMessage() {} -func (*CheckpointOptions) Descriptor() ([]byte, []int) { return fileDescriptorRunc, []int{2} } +func (m *CheckpointOptions) Reset() { *m = CheckpointOptions{} } +func (*CheckpointOptions) ProtoMessage() {} +func (*CheckpointOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_d20e2ba8b3cc58b9, []int{2} +} +func (m *CheckpointOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CheckpointOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CheckpointOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CheckpointOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_CheckpointOptions.Merge(m, src) +} +func (m *CheckpointOptions) XXX_Size() int { + return m.Size() +} +func (m *CheckpointOptions) XXX_DiscardUnknown() { + xxx_messageInfo_CheckpointOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_CheckpointOptions proto.InternalMessageInfo type ProcessDetails struct { - ExecID string `protobuf:"bytes,1,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + ExecID string `protobuf:"bytes,1,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ProcessDetails) Reset() { *m = ProcessDetails{} } +func (*ProcessDetails) ProtoMessage() {} +func (*ProcessDetails) Descriptor() ([]byte, []int) { + return fileDescriptor_d20e2ba8b3cc58b9, []int{3} +} +func (m *ProcessDetails) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProcessDetails) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProcessDetails.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ProcessDetails) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProcessDetails.Merge(m, src) +} +func (m *ProcessDetails) XXX_Size() int { + return m.Size() +} +func (m *ProcessDetails) XXX_DiscardUnknown() { + xxx_messageInfo_ProcessDetails.DiscardUnknown(m) } -func (m *ProcessDetails) Reset() { *m = ProcessDetails{} } -func (*ProcessDetails) ProtoMessage() {} -func (*ProcessDetails) Descriptor() ([]byte, []int) { return fileDescriptorRunc, []int{3} } +var xxx_messageInfo_ProcessDetails proto.InternalMessageInfo func init() { proto.RegisterType((*RuncOptions)(nil), "containerd.linux.runc.RuncOptions") @@ -94,10 +209,57 @@ func init() { proto.RegisterType((*CheckpointOptions)(nil), "containerd.linux.runc.CheckpointOptions") proto.RegisterType((*ProcessDetails)(nil), "containerd.linux.runc.ProcessDetails") } + +func init() { + proto.RegisterFile("github.com/containerd/containerd/runtime/linux/runctypes/runc.proto", fileDescriptor_d20e2ba8b3cc58b9) +} + +var fileDescriptor_d20e2ba8b3cc58b9 = []byte{ + // 604 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x94, 0xcf, 0x6e, 0xd3, 0x40, + 0x10, 0xc6, 0xeb, 0xfe, 0x49, 0x9c, 0x49, 0xd2, 0xc2, 0x42, 0x25, 0xd3, 0xaa, 0x69, 0x08, 0x7f, + 0x14, 0x2e, 0xa9, 0x04, 0xe2, 0xc4, 0xad, 0x29, 0x42, 0x15, 0x50, 0x2a, 0x43, 0x05, 0x42, 0x48, + 0x2b, 0x77, 0x3d, 0x24, 0xab, 0xc4, 0x3b, 0x96, 0x77, 0x4d, 0x92, 0x1b, 0x4f, 0xc0, 0x0b, 0xf1, + 0x02, 0x3d, 0x21, 0x8e, 0x9c, 0x10, 0xcd, 0x93, 0xa0, 0x5d, 0xc7, 0x69, 0xcf, 0x1c, 0xb9, 0xcd, + 0xfc, 0xe6, 0xb3, 0x67, 0xf4, 0x7d, 0xb2, 0xa1, 0x3f, 0x90, 0x66, 0x98, 0x9f, 0xf7, 0x04, 0x25, + 0x07, 0x82, 0x94, 0x89, 0xa4, 0xc2, 0x2c, 0xbe, 0x5e, 0x66, 0xb9, 0x32, 0x32, 0xc1, 0x83, 0xb1, + 0x54, 0xf9, 0xd4, 0x76, 0xc2, 0xcc, 0x52, 0xd4, 0xae, 0xea, 0xa5, 0x19, 0x19, 0x62, 0xdb, 0x57, + 0xf2, 0x9e, 0x93, 0xf5, 0xec, 0x70, 0xe7, 0xf6, 0x80, 0x06, 0xe4, 0x14, 0x07, 0xb6, 0x2a, 0xc4, + 0x9d, 0x6f, 0x1e, 0xd4, 0xc3, 0x5c, 0x89, 0x37, 0xa9, 0x91, 0xa4, 0x34, 0x0b, 0xa0, 0xba, 0x58, + 0x11, 0x78, 0x6d, 0xaf, 0x5b, 0x0b, 0xcb, 0x96, 0xdd, 0x85, 0xc6, 0xa2, 0xe4, 0x19, 0x91, 0x09, + 0x56, 0xdd, 0xb8, 0xbe, 0x60, 0x21, 0x91, 0x61, 0xbb, 0x50, 0x13, 0x99, 0xcc, 0x79, 0x1a, 0x99, + 0x61, 0xb0, 0xe6, 0xe6, 0xbe, 0x05, 0xa7, 0x91, 0x19, 0xb2, 0x07, 0xb0, 0xa9, 0x67, 0xda, 0x60, + 0x12, 0x73, 0x31, 0xc8, 0x28, 0x4f, 0x83, 0xf5, 0xb6, 0xd7, 0xf5, 0xc3, 0xe6, 0x82, 0xf6, 0x1d, + 0xec, 0xfc, 0x58, 0x83, 0x66, 0x3f, 0xc3, 0xc8, 0x60, 0x79, 0x52, 0x07, 0x9a, 0x8a, 0x78, 0x2a, + 0xbf, 0x90, 0x29, 0x36, 0x7b, 0xee, 0xb9, 0xba, 0xa2, 0x53, 0xcb, 0xdc, 0xe6, 0x3b, 0xe0, 0x53, + 0x8a, 0x8a, 0x1b, 0x91, 0xba, 0xc3, 0xfc, 0xb0, 0x6a, 0xfb, 0x77, 0x22, 0x65, 0x8f, 0x61, 0x1b, + 0xa7, 0x06, 0x33, 0x15, 0x8d, 0x79, 0xae, 0xe4, 0x94, 0x6b, 0x12, 0x23, 0x34, 0xda, 0x1d, 0xe8, + 0x87, 0xb7, 0xca, 0xe1, 0x99, 0x92, 0xd3, 0xb7, 0xc5, 0x88, 0xed, 0x80, 0x6f, 0x30, 0x4b, 0xa4, + 0x8a, 0xc6, 0x8b, 0x2b, 0x97, 0x3d, 0xdb, 0x03, 0xf8, 0x2c, 0xc7, 0xc8, 0xc7, 0x24, 0x46, 0x3a, + 0xd8, 0x70, 0xd3, 0x9a, 0x25, 0xaf, 0x2c, 0x60, 0x8f, 0xe0, 0x06, 0x26, 0xa9, 0x99, 0x71, 0x15, + 0x25, 0xa8, 0xd3, 0x48, 0xa0, 0x0e, 0x2a, 0xed, 0xb5, 0x6e, 0x2d, 0xdc, 0x72, 0xfc, 0x64, 0x89, + 0xad, 0xa3, 0x85, 0x13, 0x9a, 0x27, 0x14, 0x63, 0x50, 0x2d, 0x1c, 0x5d, 0xb0, 0xd7, 0x14, 0x23, + 0xbb, 0x0f, 0x9b, 0x8a, 0xb8, 0xc2, 0x09, 0x1f, 0xe1, 0x2c, 0x93, 0x6a, 0x10, 0xf8, 0x6e, 0x61, + 0x43, 0xd1, 0x09, 0x4e, 0x5e, 0x16, 0x8c, 0xed, 0x43, 0x5d, 0x0f, 0x65, 0x52, 0xfa, 0x5a, 0x73, + 0xef, 0x01, 0x8b, 0x0a, 0x53, 0xd9, 0x36, 0x54, 0x24, 0xf1, 0x5c, 0xc6, 0x01, 0xb4, 0xbd, 0x6e, + 0x33, 0xdc, 0x90, 0x74, 0x26, 0xe3, 0x05, 0x1e, 0xc8, 0x38, 0xa8, 0x97, 0xf8, 0x85, 0x8c, 0xed, + 0x52, 0x17, 0xe3, 0x84, 0xb2, 0x51, 0x91, 0x65, 0xc3, 0xbd, 0xb1, 0x61, 0xe9, 0x7b, 0xca, 0x46, + 0x2e, 0xcf, 0x87, 0xb0, 0xe5, 0x54, 0x32, 0x89, 0x06, 0x58, 0xc8, 0x9a, 0x4e, 0xd6, 0xb4, 0xf8, + 0xd8, 0x52, 0xab, 0xeb, 0x7c, 0x5f, 0x85, 0x9b, 0xfd, 0x21, 0x8a, 0x51, 0x4a, 0x52, 0x99, 0x32, + 0x54, 0x06, 0xeb, 0x38, 0x95, 0x65, 0x96, 0xae, 0xfe, 0x6f, 0x43, 0xdc, 0x85, 0xda, 0x95, 0x95, + 0x7e, 0xf1, 0x59, 0x4c, 0x4a, 0x1b, 0xf7, 0x00, 0xae, 0x39, 0x58, 0x44, 0x57, 0x93, 0x4b, 0xf7, + 0x9e, 0xc2, 0xe6, 0x69, 0x46, 0x02, 0xb5, 0x3e, 0x42, 0x13, 0xc9, 0xb1, 0x66, 0xf7, 0xa0, 0x8a, + 0x53, 0x14, 0x5c, 0xc6, 0xc5, 0x17, 0x7a, 0x08, 0xf3, 0xdf, 0xfb, 0x95, 0xe7, 0x53, 0x14, 0xc7, + 0x47, 0x61, 0xc5, 0x8e, 0x8e, 0xe3, 0xc3, 0x4f, 0x17, 0x97, 0xad, 0x95, 0x5f, 0x97, 0xad, 0x95, + 0xaf, 0xf3, 0x96, 0x77, 0x31, 0x6f, 0x79, 0x3f, 0xe7, 0x2d, 0xef, 0xcf, 0xbc, 0xe5, 0x7d, 0x3c, + 0xfc, 0xd7, 0x5f, 0xcc, 0xb3, 0x65, 0xf5, 0x61, 0xe5, 0xbc, 0xe2, 0xfe, 0x1e, 0x4f, 0xfe, 0x06, + 0x00, 0x00, 0xff, 0xff, 0x7f, 0x24, 0x6f, 0x2e, 0xb1, 0x04, 0x00, 0x00, +} + func (m *RuncOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -105,45 +267,57 @@ func (m *RuncOptions) Marshal() (dAtA []byte, err error) { } func (m *RuncOptions) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RuncOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Runtime) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintRunc(dAtA, i, uint64(len(m.Runtime))) - i += copy(dAtA[i:], m.Runtime) - } - if len(m.RuntimeRoot) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintRunc(dAtA, i, uint64(len(m.RuntimeRoot))) - i += copy(dAtA[i:], m.RuntimeRoot) - } - if len(m.CriuPath) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintRunc(dAtA, i, uint64(len(m.CriuPath))) - i += copy(dAtA[i:], m.CriuPath) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.SystemdCgroup { - dAtA[i] = 0x20 - i++ + i-- if m.SystemdCgroup { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x20 + } + if len(m.CriuPath) > 0 { + i -= len(m.CriuPath) + copy(dAtA[i:], m.CriuPath) + i = encodeVarintRunc(dAtA, i, uint64(len(m.CriuPath))) + i-- + dAtA[i] = 0x1a + } + if len(m.RuntimeRoot) > 0 { + i -= len(m.RuntimeRoot) + copy(dAtA[i:], m.RuntimeRoot) + i = encodeVarintRunc(dAtA, i, uint64(len(m.RuntimeRoot))) + i-- + dAtA[i] = 0x12 + } + if len(m.Runtime) > 0 { + i -= len(m.Runtime) + copy(dAtA[i:], m.Runtime) + i = encodeVarintRunc(dAtA, i, uint64(len(m.Runtime))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *CreateOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -151,114 +325,133 @@ func (m *CreateOptions) Marshal() (dAtA []byte, err error) { } func (m *CreateOptions) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.NoPivotRoot { - dAtA[i] = 0x8 - i++ - if m.NoPivotRoot { + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.CriuImagePath) > 0 { + i -= len(m.CriuImagePath) + copy(dAtA[i:], m.CriuImagePath) + i = encodeVarintRunc(dAtA, i, uint64(len(m.CriuImagePath))) + i-- + dAtA[i] = 0x6a + } + if len(m.CriuWorkPath) > 0 { + i -= len(m.CriuWorkPath) + copy(dAtA[i:], m.CriuWorkPath) + i = encodeVarintRunc(dAtA, i, uint64(len(m.CriuWorkPath))) + i-- + dAtA[i] = 0x62 + } + if m.IoGid != 0 { + i = encodeVarintRunc(dAtA, i, uint64(m.IoGid)) + i-- + dAtA[i] = 0x58 + } + if m.IoUid != 0 { + i = encodeVarintRunc(dAtA, i, uint64(m.IoUid)) + i-- + dAtA[i] = 0x50 + } + if len(m.ShimCgroup) > 0 { + i -= len(m.ShimCgroup) + copy(dAtA[i:], m.ShimCgroup) + i = encodeVarintRunc(dAtA, i, uint64(len(m.ShimCgroup))) + i-- + dAtA[i] = 0x4a + } + if m.NoNewKeyring { + i-- + if m.NoNewKeyring { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x40 } - if m.OpenTcp { - dAtA[i] = 0x10 - i++ - if m.OpenTcp { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + if len(m.CgroupsMode) > 0 { + i -= len(m.CgroupsMode) + copy(dAtA[i:], m.CgroupsMode) + i = encodeVarintRunc(dAtA, i, uint64(len(m.CgroupsMode))) + i-- + dAtA[i] = 0x3a + } + if len(m.EmptyNamespaces) > 0 { + for iNdEx := len(m.EmptyNamespaces) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.EmptyNamespaces[iNdEx]) + copy(dAtA[i:], m.EmptyNamespaces[iNdEx]) + i = encodeVarintRunc(dAtA, i, uint64(len(m.EmptyNamespaces[iNdEx]))) + i-- + dAtA[i] = 0x32 } - i++ } - if m.ExternalUnixSockets { - dAtA[i] = 0x18 - i++ - if m.ExternalUnixSockets { + if m.FileLocks { + i-- + if m.FileLocks { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x28 } if m.Terminal { - dAtA[i] = 0x20 - i++ + i-- if m.Terminal { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x20 } - if m.FileLocks { - dAtA[i] = 0x28 - i++ - if m.FileLocks { + if m.ExternalUnixSockets { + i-- + if m.ExternalUnixSockets { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x18 } - if len(m.EmptyNamespaces) > 0 { - for _, s := range m.EmptyNamespaces { - dAtA[i] = 0x32 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + if m.OpenTcp { + i-- + if m.OpenTcp { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } + i-- + dAtA[i] = 0x10 } - if len(m.CgroupsMode) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintRunc(dAtA, i, uint64(len(m.CgroupsMode))) - i += copy(dAtA[i:], m.CgroupsMode) - } - if m.NoNewKeyring { - dAtA[i] = 0x40 - i++ - if m.NoNewKeyring { + if m.NoPivotRoot { + i-- + if m.NoPivotRoot { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ - } - if len(m.ShimCgroup) > 0 { - dAtA[i] = 0x4a - i++ - i = encodeVarintRunc(dAtA, i, uint64(len(m.ShimCgroup))) - i += copy(dAtA[i:], m.ShimCgroup) - } - if m.IoUid != 0 { - dAtA[i] = 0x50 - i++ - i = encodeVarintRunc(dAtA, i, uint64(m.IoUid)) - } - if m.IoGid != 0 { - dAtA[i] = 0x58 - i++ - i = encodeVarintRunc(dAtA, i, uint64(m.IoGid)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *CheckpointOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -266,88 +459,106 @@ func (m *CheckpointOptions) Marshal() (dAtA []byte, err error) { } func (m *CheckpointOptions) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CheckpointOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Exit { - dAtA[i] = 0x8 - i++ - if m.Exit { + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.ImagePath) > 0 { + i -= len(m.ImagePath) + copy(dAtA[i:], m.ImagePath) + i = encodeVarintRunc(dAtA, i, uint64(len(m.ImagePath))) + i-- + dAtA[i] = 0x4a + } + if len(m.WorkPath) > 0 { + i -= len(m.WorkPath) + copy(dAtA[i:], m.WorkPath) + i = encodeVarintRunc(dAtA, i, uint64(len(m.WorkPath))) + i-- + dAtA[i] = 0x42 + } + if len(m.CgroupsMode) > 0 { + i -= len(m.CgroupsMode) + copy(dAtA[i:], m.CgroupsMode) + i = encodeVarintRunc(dAtA, i, uint64(len(m.CgroupsMode))) + i-- + dAtA[i] = 0x3a + } + if len(m.EmptyNamespaces) > 0 { + for iNdEx := len(m.EmptyNamespaces) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.EmptyNamespaces[iNdEx]) + copy(dAtA[i:], m.EmptyNamespaces[iNdEx]) + i = encodeVarintRunc(dAtA, i, uint64(len(m.EmptyNamespaces[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if m.FileLocks { + i-- + if m.FileLocks { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x28 } - if m.OpenTcp { - dAtA[i] = 0x10 - i++ - if m.OpenTcp { + if m.Terminal { + i-- + if m.Terminal { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x20 } if m.ExternalUnixSockets { - dAtA[i] = 0x18 - i++ + i-- if m.ExternalUnixSockets { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x18 } - if m.Terminal { - dAtA[i] = 0x20 - i++ - if m.Terminal { + if m.OpenTcp { + i-- + if m.OpenTcp { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x10 } - if m.FileLocks { - dAtA[i] = 0x28 - i++ - if m.FileLocks { + if m.Exit { + i-- + if m.Exit { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ - } - if len(m.EmptyNamespaces) > 0 { - for _, s := range m.EmptyNamespaces { - dAtA[i] = 0x32 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.CgroupsMode) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintRunc(dAtA, i, uint64(len(m.CgroupsMode))) - i += copy(dAtA[i:], m.CgroupsMode) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *ProcessDetails) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -355,29 +566,44 @@ func (m *ProcessDetails) Marshal() (dAtA []byte, err error) { } func (m *ProcessDetails) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProcessDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ExecID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) i = encodeVarintRunc(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintRunc(dAtA []byte, offset int, v uint64) int { + offset -= sovRunc(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *RuncOptions) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Runtime) @@ -395,10 +621,16 @@ func (m *RuncOptions) Size() (n int) { if m.SystemdCgroup { n += 2 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateOptions) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.NoPivotRoot { @@ -439,10 +671,24 @@ func (m *CreateOptions) Size() (n int) { if m.IoGid != 0 { n += 1 + sovRunc(uint64(m.IoGid)) } + l = len(m.CriuWorkPath) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + l = len(m.CriuImagePath) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CheckpointOptions) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Exit { @@ -470,28 +716,38 @@ func (m *CheckpointOptions) Size() (n int) { if l > 0 { n += 1 + l + sovRunc(uint64(l)) } + l = len(m.WorkPath) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + l = len(m.ImagePath) + if l > 0 { + n += 1 + l + sovRunc(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ProcessDetails) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ExecID) if l > 0 { n += 1 + l + sovRunc(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovRunc(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozRunc(x uint64) (n int) { return sovRunc(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -505,6 +761,7 @@ func (this *RuncOptions) String() string { `RuntimeRoot:` + fmt.Sprintf("%v", this.RuntimeRoot) + `,`, `CriuPath:` + fmt.Sprintf("%v", this.CriuPath) + `,`, `SystemdCgroup:` + fmt.Sprintf("%v", this.SystemdCgroup) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -525,6 +782,9 @@ func (this *CreateOptions) String() string { `ShimCgroup:` + fmt.Sprintf("%v", this.ShimCgroup) + `,`, `IoUid:` + fmt.Sprintf("%v", this.IoUid) + `,`, `IoGid:` + fmt.Sprintf("%v", this.IoGid) + `,`, + `CriuWorkPath:` + fmt.Sprintf("%v", this.CriuWorkPath) + `,`, + `CriuImagePath:` + fmt.Sprintf("%v", this.CriuImagePath) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -541,6 +801,9 @@ func (this *CheckpointOptions) String() string { `FileLocks:` + fmt.Sprintf("%v", this.FileLocks) + `,`, `EmptyNamespaces:` + fmt.Sprintf("%v", this.EmptyNamespaces) + `,`, `CgroupsMode:` + fmt.Sprintf("%v", this.CgroupsMode) + `,`, + `WorkPath:` + fmt.Sprintf("%v", this.WorkPath) + `,`, + `ImagePath:` + fmt.Sprintf("%v", this.ImagePath) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -551,6 +814,7 @@ func (this *ProcessDetails) String() string { } s := strings.Join([]string{`&ProcessDetails{`, `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -578,7 +842,7 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -606,7 +870,7 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -616,6 +880,9 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRunc } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -635,7 +902,7 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -645,6 +912,9 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRunc } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -664,7 +934,7 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -674,6 +944,9 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRunc } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -693,7 +966,7 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -705,12 +978,13 @@ func (m *RuncOptions) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRunc } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -735,7 +1009,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -763,7 +1037,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -783,7 +1057,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -803,7 +1077,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -823,7 +1097,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -843,7 +1117,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -863,7 +1137,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -873,6 +1147,9 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRunc } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -892,7 +1169,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -902,6 +1179,9 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRunc } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -921,7 +1201,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -941,7 +1221,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -951,6 +1231,9 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRunc } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -970,7 +1253,7 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.IoUid |= (uint32(b) & 0x7F) << shift + m.IoUid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -989,23 +1272,88 @@ func (m *CreateOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.IoGid |= (uint32(b) & 0x7F) << shift + m.IoGid |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CriuWorkPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CriuWorkPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CriuImagePath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CriuImagePath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRunc(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRunc } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1030,7 +1378,7 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1058,7 +1406,7 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1078,7 +1426,7 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1098,7 +1446,7 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1118,7 +1466,7 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1138,7 +1486,7 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1158,7 +1506,7 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1168,6 +1516,9 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRunc } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1187,7 +1538,7 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1197,23 +1548,91 @@ func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRunc } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } if postIndex > l { return io.ErrUnexpectedEOF } m.CgroupsMode = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.WorkPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ImagePath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunc + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthRunc + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ImagePath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipRunc(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRunc } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1238,7 +1657,7 @@ func (m *ProcessDetails) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1266,7 +1685,7 @@ func (m *ProcessDetails) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1276,6 +1695,9 @@ func (m *ProcessDetails) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRunc } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRunc + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1287,12 +1709,13 @@ func (m *ProcessDetails) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRunc } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1305,6 +1728,7 @@ func (m *ProcessDetails) Unmarshal(dAtA []byte) error { func skipRunc(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1336,10 +1760,8 @@ func skipRunc(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1356,95 +1778,34 @@ func skipRunc(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthRunc } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowRunc - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipRunc(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupRunc + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthRunc + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthRunc = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowRunc = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthRunc = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowRunc = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupRunc = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/runtime/linux/runctypes/runc.proto", fileDescriptorRunc) -} - -var fileDescriptorRunc = []byte{ - // 541 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x93, 0xc1, 0x6e, 0xd3, 0x40, - 0x10, 0x86, 0x6b, 0xda, 0x26, 0xce, 0xa4, 0x29, 0xb0, 0x50, 0xc9, 0x14, 0x91, 0x86, 0x00, 0x52, - 0xb8, 0xa4, 0x12, 0x88, 0x13, 0xb7, 0xa6, 0x08, 0x55, 0x40, 0xa9, 0x0c, 0x95, 0x10, 0x42, 0x5a, - 0xb9, 0xeb, 0x21, 0x59, 0xc5, 0xde, 0x59, 0x79, 0xd7, 0xd4, 0xb9, 0xf5, 0x09, 0x78, 0xae, 0x1e, - 0x39, 0x72, 0x42, 0x34, 0x2f, 0x02, 0xf2, 0xda, 0x0e, 0x9c, 0x39, 0x72, 0xfb, 0xe7, 0xfb, 0xc7, - 0x9e, 0xd1, 0xbf, 0x1a, 0x98, 0x4c, 0xa5, 0x9d, 0xe5, 0x67, 0x63, 0x41, 0xe9, 0xbe, 0x20, 0x65, - 0x23, 0xa9, 0x30, 0x8b, 0xff, 0x96, 0x59, 0xae, 0xac, 0x4c, 0x71, 0x3f, 0x91, 0x2a, 0x2f, 0xca, - 0x4a, 0xd8, 0x85, 0x46, 0xe3, 0xd4, 0x58, 0x67, 0x64, 0x89, 0xed, 0xfc, 0x69, 0x1f, 0xbb, 0xb6, - 0x71, 0x69, 0xee, 0xde, 0x9e, 0xd2, 0x94, 0x5c, 0xc7, 0x7e, 0xa9, 0xaa, 0xe6, 0xe1, 0x57, 0x0f, - 0xba, 0x61, 0xae, 0xc4, 0x5b, 0x6d, 0x25, 0x29, 0xc3, 0x02, 0x68, 0xd7, 0x23, 0x02, 0x6f, 0xe0, - 0x8d, 0x3a, 0x61, 0x53, 0xb2, 0xfb, 0xb0, 0x55, 0x4b, 0x9e, 0x11, 0xd9, 0xe0, 0x9a, 0xb3, 0xbb, - 0x35, 0x0b, 0x89, 0x2c, 0xbb, 0x0b, 0x1d, 0x91, 0xc9, 0x9c, 0xeb, 0xc8, 0xce, 0x82, 0x75, 0xe7, - 0xfb, 0x25, 0x38, 0x89, 0xec, 0x8c, 0x3d, 0x82, 0x6d, 0xb3, 0x30, 0x16, 0xd3, 0x98, 0x8b, 0x69, - 0x46, 0xb9, 0x0e, 0x36, 0x06, 0xde, 0xc8, 0x0f, 0x7b, 0x35, 0x9d, 0x38, 0x38, 0xbc, 0x58, 0x87, - 0xde, 0x24, 0xc3, 0xc8, 0x62, 0xb3, 0xd2, 0x10, 0x7a, 0x8a, 0xb8, 0x96, 0x5f, 0xc8, 0x56, 0x93, - 0x3d, 0xf7, 0x5d, 0x57, 0xd1, 0x49, 0xc9, 0xdc, 0xe4, 0x3b, 0xe0, 0x93, 0x46, 0xc5, 0xad, 0xd0, - 0x6e, 0x31, 0x3f, 0x6c, 0x97, 0xf5, 0x7b, 0xa1, 0xd9, 0x13, 0xd8, 0xc1, 0xc2, 0x62, 0xa6, 0xa2, - 0x84, 0xe7, 0x4a, 0x16, 0xdc, 0x90, 0x98, 0xa3, 0x35, 0x6e, 0x41, 0x3f, 0xbc, 0xd5, 0x98, 0xa7, - 0x4a, 0x16, 0xef, 0x2a, 0x8b, 0xed, 0x82, 0x6f, 0x31, 0x4b, 0xa5, 0x8a, 0x92, 0x7a, 0xcb, 0x55, - 0xcd, 0xee, 0x01, 0x7c, 0x96, 0x09, 0xf2, 0x84, 0xc4, 0xdc, 0x04, 0x9b, 0xce, 0xed, 0x94, 0xe4, - 0x75, 0x09, 0xd8, 0x63, 0xb8, 0x81, 0xa9, 0xb6, 0x0b, 0xae, 0xa2, 0x14, 0x8d, 0x8e, 0x04, 0x9a, - 0xa0, 0x35, 0x58, 0x1f, 0x75, 0xc2, 0xeb, 0x8e, 0x1f, 0xaf, 0x70, 0x99, 0x68, 0x95, 0x84, 0xe1, - 0x29, 0xc5, 0x18, 0xb4, 0xab, 0x44, 0x6b, 0xf6, 0x86, 0x62, 0x64, 0x0f, 0x61, 0x5b, 0x11, 0x57, - 0x78, 0xce, 0xe7, 0xb8, 0xc8, 0xa4, 0x9a, 0x06, 0xbe, 0x1b, 0xb8, 0xa5, 0xe8, 0x18, 0xcf, 0x5f, - 0x55, 0x8c, 0xed, 0x41, 0xd7, 0xcc, 0x64, 0xda, 0xe4, 0xda, 0x71, 0xff, 0x81, 0x12, 0x55, 0xa1, - 0xb2, 0x1d, 0x68, 0x49, 0xe2, 0xb9, 0x8c, 0x03, 0x18, 0x78, 0xa3, 0x5e, 0xb8, 0x29, 0xe9, 0x54, - 0xc6, 0x35, 0x9e, 0xca, 0x38, 0xe8, 0x36, 0xf8, 0xa5, 0x8c, 0x87, 0xbf, 0x3c, 0xb8, 0x39, 0x99, - 0xa1, 0x98, 0x6b, 0x92, 0xca, 0x36, 0xcf, 0xc0, 0x60, 0x03, 0x0b, 0xd9, 0xa4, 0xef, 0xf4, 0xff, - 0x1a, 0xfb, 0xf0, 0x19, 0x6c, 0x9f, 0x64, 0x24, 0xd0, 0x98, 0x43, 0xb4, 0x91, 0x4c, 0x0c, 0x7b, - 0x00, 0x6d, 0x2c, 0x50, 0x70, 0x19, 0x57, 0x77, 0x71, 0x00, 0xcb, 0x1f, 0x7b, 0xad, 0x17, 0x05, - 0x8a, 0xa3, 0xc3, 0xb0, 0x55, 0x5a, 0x47, 0xf1, 0xc1, 0xa7, 0xcb, 0xab, 0xfe, 0xda, 0xf7, 0xab, - 0xfe, 0xda, 0xc5, 0xb2, 0xef, 0x5d, 0x2e, 0xfb, 0xde, 0xb7, 0x65, 0xdf, 0xfb, 0xb9, 0xec, 0x7b, - 0x1f, 0x0f, 0xfe, 0xf5, 0xb0, 0x9f, 0xaf, 0xd4, 0x87, 0xb5, 0xb3, 0x96, 0xbb, 0xd9, 0xa7, 0xbf, - 0x03, 0x00, 0x00, 0xff, 0xff, 0x18, 0xa1, 0x4b, 0x5b, 0x27, 0x04, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.proto b/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.proto index ddd3f8d112892..78e3abf4cb776 100644 --- a/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.proto +++ b/vendor/github.com/containerd/containerd/runtime/linux/runctypes/runc.proto @@ -25,6 +25,8 @@ message CreateOptions { string shim_cgroup = 9; uint32 io_uid = 10; uint32 io_gid = 11; + string criu_work_path = 12; + string criu_image_path = 13; } message CheckpointOptions { @@ -35,6 +37,8 @@ message CheckpointOptions { bool file_locks = 5; repeated string empty_namespaces = 6; string cgroups_mode = 7; + string work_path = 8; + string image_path = 9; } message ProcessDetails { diff --git a/vendor/github.com/containerd/containerd/runtime/proc/proc.go b/vendor/github.com/containerd/containerd/runtime/proc/proc.go deleted file mode 100644 index 02bc9bda816f8..0000000000000 --- a/vendor/github.com/containerd/containerd/runtime/proc/proc.go +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package proc - -import ( - "context" - "io" - "sync" - "time" - - "github.com/containerd/console" -) - -// Stdio of a process -type Stdio struct { - Stdin string - Stdout string - Stderr string - Terminal bool -} - -// IsNull returns true if the stdio is not defined -func (s Stdio) IsNull() bool { - return s.Stdin == "" && s.Stdout == "" && s.Stderr == "" -} - -// Process on a system -type Process interface { - State - // ID returns the id for the process - ID() string - // Pid returns the pid for the process - Pid() int - // ExitStatus returns the exit status - ExitStatus() int - // ExitedAt is the time the process exited - ExitedAt() time.Time - // Stdin returns the process STDIN - Stdin() io.Closer - // Stdio returns io information for the container - Stdio() Stdio - // Status returns the process status - Status(context.Context) (string, error) - // Wait blocks until the process has exited - Wait() -} - -// State of a process -type State interface { - // Resize resizes the process console - Resize(ws console.WinSize) error - // Start execution of the process - Start(context.Context) error - // Delete deletes the process and its resourcess - Delete(context.Context) error - // Kill kills the process - Kill(context.Context, uint32, bool) error - // SetExited sets the exit status for the process - SetExited(status int) -} - -// Platform handles platform-specific behavior that may differs across -// platform implementations -type Platform interface { - CopyConsole(ctx context.Context, console console.Console, stdin, stdout, stderr string, - wg, cwg *sync.WaitGroup) (console.Console, error) - ShutdownConsole(ctx context.Context, console console.Console) error - Close() error -} diff --git a/vendor/github.com/containerd/containerd/runtime/runtime.go b/vendor/github.com/containerd/containerd/runtime/runtime.go index 1b3f87c152794..3d758fb97ac6f 100644 --- a/vendor/github.com/containerd/containerd/runtime/runtime.go +++ b/vendor/github.com/containerd/containerd/runtime/runtime.go @@ -63,10 +63,14 @@ type PlatformRuntime interface { // ID of the runtime ID() string // Create creates a task with the provided id and options. - Create(ctx context.Context, id string, opts CreateOpts) (Task, error) + Create(ctx context.Context, taskID string, opts CreateOpts) (Task, error) // Get returns a task. - Get(context.Context, string) (Task, error) + Get(ctx context.Context, taskID string) (Task, error) // Tasks returns all the current tasks for the runtime. // Any container runs at most one task at a time. - Tasks(context.Context, bool) ([]Task, error) + Tasks(ctx context.Context, all bool) ([]Task, error) + // Add adds a task into runtime. + Add(ctx context.Context, task Task) error + // Delete remove a task. + Delete(ctx context.Context, taskID string) } diff --git a/vendor/github.com/containerd/containerd/runtime/task.go b/vendor/github.com/containerd/containerd/runtime/task.go index 981e290c68d12..c9876ed4a5dc9 100644 --- a/vendor/github.com/containerd/containerd/runtime/task.go +++ b/vendor/github.com/containerd/containerd/runtime/task.go @@ -33,45 +33,48 @@ type TaskInfo struct { // Process is a runtime object for an executing process inside a container type Process interface { + // ID of the process ID() string // State returns the process state - State(context.Context) (State, error) + State(ctx context.Context) (State, error) // Kill signals a container - Kill(context.Context, uint32, bool) error - // Pty resizes the processes pty/console - ResizePty(context.Context, ConsoleSize) error - // CloseStdin closes the processes stdin - CloseIO(context.Context) error + Kill(ctx context.Context, signal uint32, all bool) error + // ResizePty resizes the processes pty/console + ResizePty(ctx context.Context, size ConsoleSize) error + // CloseIO closes the processes IO + CloseIO(ctx context.Context) error // Start the container's user defined process - Start(context.Context) error + Start(ctx context.Context) error // Wait for the process to exit - Wait(context.Context) (*Exit, error) + Wait(ctx context.Context) (*Exit, error) // Delete deletes the process - Delete(context.Context) (*Exit, error) + Delete(ctx context.Context) (*Exit, error) } // Task is the runtime object for an executing container type Task interface { Process + // PID of the process + PID() uint32 // Namespace that the task exists in Namespace() string // Pause pauses the container process - Pause(context.Context) error + Pause(ctx context.Context) error // Resume unpauses the container process - Resume(context.Context) error + Resume(ctx context.Context) error // Exec adds a process into the container - Exec(context.Context, string, ExecOpts) (Process, error) + Exec(ctx context.Context, id string, opts ExecOpts) (Process, error) // Pids returns all pids - Pids(context.Context) ([]ProcessInfo, error) + Pids(ctx context.Context) ([]ProcessInfo, error) // Checkpoint checkpoints a container to an image with live system data - Checkpoint(context.Context, string, *types.Any) error + Checkpoint(ctx context.Context, path string, opts *types.Any) error // Update sets the provided resources to a running task - Update(context.Context, *types.Any) error + Update(ctx context.Context, resources *types.Any, annotations map[string]string) error // Process returns a process within the task for the provided id - Process(context.Context, string) (Process, error) + Process(ctx context.Context, id string) (Process, error) // Stats returns runtime specific metrics for a task - Stats(context.Context) (*types.Any, error) + Stats(ctx context.Context) (*types.Any, error) } // ExecOpts provides additional options for additional processes running in a task diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/bundle.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/bundle.go index d73866a2fd890..48d81e8e0957e 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/bundle.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/linux/bundle.go @@ -20,6 +20,9 @@ package linux import ( "context" + "crypto/sha256" + "encoding/json" + "fmt" "io/ioutil" "os" "path/filepath" @@ -28,6 +31,7 @@ import ( "github.com/containerd/containerd/runtime/linux/runctypes" "github.com/containerd/containerd/runtime/v1/shim" "github.com/containerd/containerd/runtime/v1/shim/client" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -46,7 +50,7 @@ func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) { return nil, err } path = filepath.Join(path, id) - if err := os.Mkdir(path, 0711); err != nil { + if err := os.Mkdir(path, 0700); err != nil { return nil, err } defer func() { @@ -54,6 +58,9 @@ func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) { os.RemoveAll(path) } }() + if err := prepareBundleDirectoryPermissions(path, spec); err != nil { + return nil, err + } workDir = filepath.Join(workDir, id) if err := os.MkdirAll(workDir, 0711); err != nil { return nil, err @@ -63,7 +70,8 @@ func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) { os.RemoveAll(workDir) } }() - if err := os.Mkdir(filepath.Join(path, "rootfs"), 0711); err != nil { + rootfs := filepath.Join(path, "rootfs") + if err := os.MkdirAll(rootfs, 0711); err != nil { return nil, err } err = ioutil.WriteFile(filepath.Join(path, configFilename), spec, 0666) @@ -74,6 +82,55 @@ func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) { }, err } +// prepareBundleDirectoryPermissions prepares the permissions of the bundle +// directory. When user namespaces are enabled, the permissions are modified +// to allow the remapped root GID to access the bundle. +func prepareBundleDirectoryPermissions(path string, spec []byte) error { + gid, err := remappedGID(spec) + if err != nil { + return err + } + if gid == 0 { + return nil + } + if err := os.Chown(path, -1, int(gid)); err != nil { + return err + } + return os.Chmod(path, 0710) +} + +// ociSpecUserNS is a subset of specs.Spec used to reduce garbage during +// unmarshal. +type ociSpecUserNS struct { + Linux *linuxSpecUserNS +} + +// linuxSpecUserNS is a subset of specs.Linux used to reduce garbage during +// unmarshal. +type linuxSpecUserNS struct { + GIDMappings []specs.LinuxIDMapping +} + +// remappedGID reads the remapped GID 0 from the OCI spec, if it exists. If +// there is no remapping, remappedGID returns 0. If the spec cannot be parsed, +// remappedGID returns an error. +func remappedGID(spec []byte) (uint32, error) { + var ociSpec ociSpecUserNS + err := json.Unmarshal(spec, &ociSpec) + if err != nil { + return 0, err + } + if ociSpec.Linux == nil || len(ociSpec.Linux.GIDMappings) == 0 { + return 0, nil + } + for _, mapping := range ociSpec.Linux.GIDMappings { + if mapping.ContainerID == 0 { + return mapping.HostID, nil + } + } + return 0, nil +} + type bundle struct { id string path string @@ -88,7 +145,7 @@ func ShimRemote(c *Config, daemonAddress, cgroup string, exitHandler func()) Shi return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) { config := b.shimConfig(ns, c, ropts) return config, - client.WithStart(c.Shim, b.shimAddress(ns), daemonAddress, cgroup, c.ShimDebug, exitHandler) + client.WithStart(c.Shim, b.shimAddress(ns, daemonAddress), daemonAddress, cgroup, c.ShimDebug, exitHandler) } } @@ -102,7 +159,7 @@ func ShimLocal(c *Config, exchange *exchange.Exchange) ShimOpt { // ShimConnect is a ShimOpt for connecting to an existing remote shim func ShimConnect(c *Config, onClose func()) ShimOpt { return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) { - return b.shimConfig(ns, c, ropts), client.WithConnect(b.shimAddress(ns), onClose) + return b.shimConfig(ns, c, ropts), client.WithConnect(b.decideShimAddress(ns), onClose) } } @@ -114,22 +171,51 @@ func (b *bundle) NewShimClient(ctx context.Context, namespace string, getClientO // Delete deletes the bundle from disk func (b *bundle) Delete() error { - err := os.RemoveAll(b.path) + address, _ := b.loadAddress() + if address != "" { + // we don't care about errors here + client.RemoveSocket(address) + } + err := atomicDelete(b.path) if err == nil { - return os.RemoveAll(b.workDir) + return atomicDelete(b.workDir) } // error removing the bundle path; still attempt removing work dir - err2 := os.RemoveAll(b.workDir) + err2 := atomicDelete(b.workDir) if err2 == nil { return err } return errors.Wrapf(err, "Failed to remove both bundle and workdir locations: %v", err2) } -func (b *bundle) shimAddress(namespace string) string { +func (b *bundle) legacyShimAddress(namespace string) string { return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock") } +const socketRoot = "/run/containerd" + +func (b *bundle) shimAddress(namespace, socketPath string) string { + d := sha256.Sum256([]byte(filepath.Join(socketPath, namespace, b.id))) + return fmt.Sprintf("unix://%s/%x", filepath.Join(socketRoot, "s"), d) +} + +func (b *bundle) loadAddress() (string, error) { + addressPath := filepath.Join(b.path, "address") + data, err := ioutil.ReadFile(addressPath) + if err != nil { + return "", err + } + return string(data), nil +} + +func (b *bundle) decideShimAddress(namespace string) string { + address, err := b.loadAddress() + if err != nil { + return b.legacyShimAddress(namespace) + } + return address +} + func (b *bundle) shimConfig(namespace string, c *Config, runcOptions *runctypes.RuncOptions) shim.Config { var ( criuPath string @@ -152,3 +238,16 @@ func (b *bundle) shimConfig(namespace string, c *Config, runcOptions *runctypes. SystemdCgroup: systemdCgroup, } } + +// atomicDelete renames the path to a hidden file before removal +func atomicDelete(path string) error { + // create a hidden dir for an atomic removal + atomicPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path))) + if err := os.Rename(path, atomicPath); err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + return os.RemoveAll(atomicPath) +} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec.go deleted file mode 100644 index 96c425dd96837..0000000000000 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec.go +++ /dev/null @@ -1,220 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package proc - -import ( - "context" - "fmt" - "io" - "os" - "path/filepath" - "sync" - "syscall" - "time" - - "golang.org/x/sys/unix" - - "github.com/containerd/console" - "github.com/containerd/containerd/runtime/proc" - "github.com/containerd/fifo" - runc "github.com/containerd/go-runc" - specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" -) - -type execProcess struct { - wg sync.WaitGroup - - proc.State - - mu sync.Mutex - id string - console console.Console - io runc.IO - status int - exited time.Time - pid int - closers []io.Closer - stdin io.Closer - stdio proc.Stdio - path string - spec specs.Process - - parent *Init - waitBlock chan struct{} -} - -func (e *execProcess) Wait() { - <-e.waitBlock -} - -func (e *execProcess) ID() string { - return e.id -} - -func (e *execProcess) Pid() int { - e.mu.Lock() - defer e.mu.Unlock() - return e.pid -} - -func (e *execProcess) ExitStatus() int { - e.mu.Lock() - defer e.mu.Unlock() - return e.status -} - -func (e *execProcess) ExitedAt() time.Time { - e.mu.Lock() - defer e.mu.Unlock() - return e.exited -} - -func (e *execProcess) setExited(status int) { - e.status = status - e.exited = time.Now() - e.parent.Platform.ShutdownConsole(context.Background(), e.console) - close(e.waitBlock) -} - -func (e *execProcess) delete(ctx context.Context) error { - e.wg.Wait() - if e.io != nil { - for _, c := range e.closers { - c.Close() - } - e.io.Close() - } - pidfile := filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id)) - // silently ignore error - os.Remove(pidfile) - return nil -} - -func (e *execProcess) resize(ws console.WinSize) error { - if e.console == nil { - return nil - } - return e.console.Resize(ws) -} - -func (e *execProcess) kill(ctx context.Context, sig uint32, _ bool) error { - pid := e.pid - if pid != 0 { - if err := unix.Kill(pid, syscall.Signal(sig)); err != nil { - return errors.Wrapf(checkKillError(err), "exec kill error") - } - } - return nil -} - -func (e *execProcess) Stdin() io.Closer { - return e.stdin -} - -func (e *execProcess) Stdio() proc.Stdio { - return e.stdio -} - -func (e *execProcess) start(ctx context.Context) (err error) { - var ( - socket *runc.Socket - pidfile = filepath.Join(e.path, fmt.Sprintf("%s.pid", e.id)) - ) - if e.stdio.Terminal { - if socket, err = runc.NewTempConsoleSocket(); err != nil { - return errors.Wrap(err, "failed to create runc console socket") - } - defer socket.Close() - } else if e.stdio.IsNull() { - if e.io, err = runc.NewNullIO(); err != nil { - return errors.Wrap(err, "creating new NULL IO") - } - } else { - if e.io, err = runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID, withConditionalIO(e.stdio)); err != nil { - return errors.Wrap(err, "failed to create runc io pipes") - } - } - opts := &runc.ExecOpts{ - PidFile: pidfile, - IO: e.io, - Detach: true, - } - if socket != nil { - opts.ConsoleSocket = socket - } - if err := e.parent.runtime.Exec(ctx, e.parent.id, e.spec, opts); err != nil { - close(e.waitBlock) - return e.parent.runtimeError(err, "OCI runtime exec failed") - } - if e.stdio.Stdin != "" { - sc, err := fifo.OpenFifo(ctx, e.stdio.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0) - if err != nil { - return errors.Wrapf(err, "failed to open stdin fifo %s", e.stdio.Stdin) - } - e.closers = append(e.closers, sc) - e.stdin = sc - } - var copyWaitGroup sync.WaitGroup - if socket != nil { - console, err := socket.ReceiveMaster() - if err != nil { - return errors.Wrap(err, "failed to retrieve console master") - } - if e.console, err = e.parent.Platform.CopyConsole(ctx, console, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, ©WaitGroup); err != nil { - return errors.Wrap(err, "failed to start console copy") - } - } else if !e.stdio.IsNull() { - if err := copyPipes(ctx, e.io, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, ©WaitGroup); err != nil { - return errors.Wrap(err, "failed to start io pipe copy") - } - } - copyWaitGroup.Wait() - pid, err := runc.ReadPidFile(opts.PidFile) - if err != nil { - return errors.Wrap(err, "failed to retrieve OCI runtime exec pid") - } - e.pid = pid - return nil -} - -func (e *execProcess) Status(ctx context.Context) (string, error) { - s, err := e.parent.Status(ctx) - if err != nil { - return "", err - } - // if the container as a whole is in the pausing/paused state, so are all - // other processes inside the container, use container state here - switch s { - case "paused", "pausing": - return s, nil - } - e.mu.Lock() - defer e.mu.Unlock() - // if we don't have a pid then the exec process has just been created - if e.pid == 0 { - return "created", nil - } - // if we have a pid and it can be signaled, the process is running - if err := unix.Kill(e.pid, 0); err == nil { - return "running", nil - } - // else if we have a pid but it can nolonger be signaled, it has stopped - return "stopped", nil -} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init.go deleted file mode 100644 index 5bf5f83443d88..0000000000000 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init.go +++ /dev/null @@ -1,409 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package proc - -import ( - "context" - "encoding/json" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "sync" - "syscall" - "time" - - "github.com/containerd/console" - "github.com/containerd/containerd/log" - "github.com/containerd/containerd/mount" - "github.com/containerd/containerd/runtime/proc" - "github.com/containerd/fifo" - runc "github.com/containerd/go-runc" - google_protobuf "github.com/gogo/protobuf/types" - specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" -) - -// InitPidFile name of the file that contains the init pid -const InitPidFile = "init.pid" - -// Init represents an initial process for a container -type Init struct { - wg sync.WaitGroup - initState - - // mu is used to ensure that `Start()` and `Exited()` calls return in - // the right order when invoked in separate go routines. - // This is the case within the shim implementation as it makes use of - // the reaper interface. - mu sync.Mutex - - waitBlock chan struct{} - - WorkDir string - - id string - Bundle string - console console.Console - Platform proc.Platform - io runc.IO - runtime *runc.Runc - status int - exited time.Time - pid int - closers []io.Closer - stdin io.Closer - stdio proc.Stdio - Rootfs string - IoUID int - IoGID int - NoPivotRoot bool - NoNewKeyring bool -} - -// NewRunc returns a new runc instance for a process -func NewRunc(root, path, namespace, runtime, criu string, systemd bool) *runc.Runc { - if root == "" { - root = RuncRoot - } - return &runc.Runc{ - Command: runtime, - Log: filepath.Join(path, "log.json"), - LogFormat: runc.JSON, - PdeathSignal: syscall.SIGKILL, - Root: filepath.Join(root, namespace), - Criu: criu, - SystemdCgroup: systemd, - } -} - -// New returns a new process -func New(id string, runtime *runc.Runc, stdio proc.Stdio) *Init { - p := &Init{ - id: id, - runtime: runtime, - stdio: stdio, - status: 0, - waitBlock: make(chan struct{}), - } - p.initState = &createdState{p: p} - return p -} - -// Create the process with the provided config -func (p *Init) Create(ctx context.Context, r *CreateConfig) error { - var ( - err error - socket *runc.Socket - ) - if r.Terminal { - if socket, err = runc.NewTempConsoleSocket(); err != nil { - return errors.Wrap(err, "failed to create OCI runtime console socket") - } - defer socket.Close() - } else if hasNoIO(r) { - if p.io, err = runc.NewNullIO(); err != nil { - return errors.Wrap(err, "creating new NULL IO") - } - } else { - if p.io, err = runc.NewPipeIO(p.IoUID, p.IoGID, withConditionalIO(p.stdio)); err != nil { - return errors.Wrap(err, "failed to create OCI runtime io pipes") - } - } - pidFile := filepath.Join(p.Bundle, InitPidFile) - if r.Checkpoint != "" { - opts := &runc.RestoreOpts{ - CheckpointOpts: runc.CheckpointOpts{ - ImagePath: r.Checkpoint, - WorkDir: p.WorkDir, - ParentPath: r.ParentCheckpoint, - }, - PidFile: pidFile, - IO: p.io, - NoPivot: p.NoPivotRoot, - Detach: true, - NoSubreaper: true, - } - p.initState = &createdCheckpointState{ - p: p, - opts: opts, - } - return nil - } - opts := &runc.CreateOpts{ - PidFile: pidFile, - IO: p.io, - NoPivot: p.NoPivotRoot, - NoNewKeyring: p.NoNewKeyring, - } - if socket != nil { - opts.ConsoleSocket = socket - } - if err := p.runtime.Create(ctx, r.ID, r.Bundle, opts); err != nil { - return p.runtimeError(err, "OCI runtime create failed") - } - if r.Stdin != "" { - sc, err := fifo.OpenFifo(ctx, r.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0) - if err != nil { - return errors.Wrapf(err, "failed to open stdin fifo %s", r.Stdin) - } - p.stdin = sc - p.closers = append(p.closers, sc) - } - var copyWaitGroup sync.WaitGroup - if socket != nil { - console, err := socket.ReceiveMaster() - if err != nil { - return errors.Wrap(err, "failed to retrieve console master") - } - console, err = p.Platform.CopyConsole(ctx, console, r.Stdin, r.Stdout, r.Stderr, &p.wg, ©WaitGroup) - if err != nil { - return errors.Wrap(err, "failed to start console copy") - } - p.console = console - } else if !hasNoIO(r) { - if err := copyPipes(ctx, p.io, r.Stdin, r.Stdout, r.Stderr, &p.wg, ©WaitGroup); err != nil { - return errors.Wrap(err, "failed to start io pipe copy") - } - } - - copyWaitGroup.Wait() - pid, err := runc.ReadPidFile(pidFile) - if err != nil { - return errors.Wrap(err, "failed to retrieve OCI runtime container pid") - } - p.pid = pid - return nil -} - -// Wait for the process to exit -func (p *Init) Wait() { - <-p.waitBlock -} - -// ID of the process -func (p *Init) ID() string { - return p.id -} - -// Pid of the process -func (p *Init) Pid() int { - return p.pid -} - -// ExitStatus of the process -func (p *Init) ExitStatus() int { - p.mu.Lock() - defer p.mu.Unlock() - return p.status -} - -// ExitedAt at time when the process exited -func (p *Init) ExitedAt() time.Time { - p.mu.Lock() - defer p.mu.Unlock() - return p.exited -} - -// Status of the process -func (p *Init) Status(ctx context.Context) (string, error) { - p.mu.Lock() - defer p.mu.Unlock() - c, err := p.runtime.State(ctx, p.id) - if err != nil { - if strings.Contains(err.Error(), "does not exist") { - return "stopped", nil - } - return "", p.runtimeError(err, "OCI runtime state failed") - } - return c.Status, nil -} - -func (p *Init) start(context context.Context) error { - err := p.runtime.Start(context, p.id) - return p.runtimeError(err, "OCI runtime start failed") -} - -func (p *Init) setExited(status int) { - p.exited = time.Now() - p.status = status - p.Platform.ShutdownConsole(context.Background(), p.console) - close(p.waitBlock) -} - -func (p *Init) delete(context context.Context) error { - p.wg.Wait() - err := p.runtime.Delete(context, p.id, nil) - // ignore errors if a runtime has already deleted the process - // but we still hold metadata and pipes - // - // this is common during a checkpoint, runc will delete the container state - // after a checkpoint and the container will no longer exist within runc - if err != nil { - if strings.Contains(err.Error(), "does not exist") { - err = nil - } else { - err = p.runtimeError(err, "failed to delete task") - } - } - if p.io != nil { - for _, c := range p.closers { - c.Close() - } - p.io.Close() - } - if err2 := mount.UnmountAll(p.Rootfs, 0); err2 != nil { - log.G(context).WithError(err2).Warn("failed to cleanup rootfs mount") - if err == nil { - err = errors.Wrap(err2, "failed rootfs umount") - } - } - return err -} - -func (p *Init) resize(ws console.WinSize) error { - if p.console == nil { - return nil - } - return p.console.Resize(ws) -} - -func (p *Init) pause(context context.Context) error { - err := p.runtime.Pause(context, p.id) - return p.runtimeError(err, "OCI runtime pause failed") -} - -func (p *Init) resume(context context.Context) error { - err := p.runtime.Resume(context, p.id) - return p.runtimeError(err, "OCI runtime resume failed") -} - -func (p *Init) kill(context context.Context, signal uint32, all bool) error { - err := p.runtime.Kill(context, p.id, int(signal), &runc.KillOpts{ - All: all, - }) - return checkKillError(err) -} - -// KillAll processes belonging to the init process -func (p *Init) KillAll(context context.Context) error { - err := p.runtime.Kill(context, p.id, int(syscall.SIGKILL), &runc.KillOpts{ - All: true, - }) - return p.runtimeError(err, "OCI runtime killall failed") -} - -// Stdin of the process -func (p *Init) Stdin() io.Closer { - return p.stdin -} - -// Runtime returns the OCI runtime configured for the init process -func (p *Init) Runtime() *runc.Runc { - return p.runtime -} - -// exec returns a new exec'd process -func (p *Init) exec(context context.Context, path string, r *ExecConfig) (proc.Process, error) { - // process exec request - var spec specs.Process - if err := json.Unmarshal(r.Spec.Value, &spec); err != nil { - return nil, err - } - spec.Terminal = r.Terminal - - e := &execProcess{ - id: r.ID, - path: path, - parent: p, - spec: spec, - stdio: proc.Stdio{ - Stdin: r.Stdin, - Stdout: r.Stdout, - Stderr: r.Stderr, - Terminal: r.Terminal, - }, - waitBlock: make(chan struct{}), - } - e.State = &execCreatedState{p: e} - return e, nil -} - -func (p *Init) checkpoint(context context.Context, r *CheckpointConfig) error { - var actions []runc.CheckpointAction - if !r.Exit { - actions = append(actions, runc.LeaveRunning) - } - work := filepath.Join(p.WorkDir, "criu-work") - defer os.RemoveAll(work) - if err := p.runtime.Checkpoint(context, p.id, &runc.CheckpointOpts{ - WorkDir: work, - ImagePath: r.Path, - AllowOpenTCP: r.AllowOpenTCP, - AllowExternalUnixSockets: r.AllowExternalUnixSockets, - AllowTerminal: r.AllowTerminal, - FileLocks: r.FileLocks, - EmptyNamespaces: r.EmptyNamespaces, - }, actions...); err != nil { - dumpLog := filepath.Join(p.Bundle, "criu-dump.log") - if cerr := copyFile(dumpLog, filepath.Join(work, "dump.log")); cerr != nil { - log.G(context).Error(err) - } - return fmt.Errorf("%s path= %s", criuError(err), dumpLog) - } - return nil -} - -func (p *Init) update(context context.Context, r *google_protobuf.Any) error { - var resources specs.LinuxResources - if err := json.Unmarshal(r.Value, &resources); err != nil { - return err - } - return p.runtime.Update(context, p.id, &resources) -} - -// Stdio of the process -func (p *Init) Stdio() proc.Stdio { - return p.stdio -} - -func (p *Init) runtimeError(rErr error, msg string) error { - if rErr == nil { - return nil - } - - rMsg, err := getLastRuntimeError(p.runtime) - switch { - case err != nil: - return errors.Wrapf(rErr, "%s: %s (%s)", msg, "unable to retrieve OCI runtime error", err.Error()) - case rMsg == "": - return errors.Wrap(rErr, msg) - default: - return errors.Errorf("%s: %s", msg, rMsg) - } -} - -func withConditionalIO(c proc.Stdio) runc.IOOpt { - return func(o *runc.IOOption) { - o.OpenStdin = c.Stdin != "" - o.OpenStdout = c.Stdout != "" - o.OpenStderr = c.Stderr != "" - } -} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init_state.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init_state.go deleted file mode 100644 index 6a6b448d3a34a..0000000000000 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init_state.go +++ /dev/null @@ -1,545 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package proc - -import ( - "context" - "sync" - "syscall" - - "github.com/containerd/console" - "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/runtime/proc" - "github.com/containerd/fifo" - runc "github.com/containerd/go-runc" - google_protobuf "github.com/gogo/protobuf/types" - "github.com/pkg/errors" -) - -type initState interface { - proc.State - - Pause(context.Context) error - Resume(context.Context) error - Update(context.Context, *google_protobuf.Any) error - Checkpoint(context.Context, *CheckpointConfig) error - Exec(context.Context, string, *ExecConfig) (proc.Process, error) -} - -type createdState struct { - p *Init -} - -func (s *createdState) transition(name string) error { - switch name { - case "running": - s.p.initState = &runningState{p: s.p} - case "stopped": - s.p.initState = &stoppedState{p: s.p} - case "deleted": - s.p.initState = &deletedState{} - default: - return errors.Errorf("invalid state transition %q to %q", stateName(s), name) - } - return nil -} - -func (s *createdState) Pause(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot pause task in created state") -} - -func (s *createdState) Resume(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot resume task in created state") -} - -func (s *createdState) Update(context context.Context, r *google_protobuf.Any) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.update(context, r) -} - -func (s *createdState) Checkpoint(context context.Context, r *CheckpointConfig) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot checkpoint a task in created state") -} - -func (s *createdState) Resize(ws console.WinSize) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.resize(ws) -} - -func (s *createdState) Start(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - if err := s.p.start(ctx); err != nil { - return err - } - return s.transition("running") -} - -func (s *createdState) Delete(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - if err := s.p.delete(ctx); err != nil { - return err - } - return s.transition("deleted") -} - -func (s *createdState) Kill(ctx context.Context, sig uint32, all bool) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.kill(ctx, sig, all) -} - -func (s *createdState) SetExited(status int) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - s.p.setExited(status) - - if err := s.transition("stopped"); err != nil { - panic(err) - } -} - -func (s *createdState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return s.p.exec(ctx, path, r) -} - -type createdCheckpointState struct { - p *Init - opts *runc.RestoreOpts -} - -func (s *createdCheckpointState) transition(name string) error { - switch name { - case "running": - s.p.initState = &runningState{p: s.p} - case "stopped": - s.p.initState = &stoppedState{p: s.p} - case "deleted": - s.p.initState = &deletedState{} - default: - return errors.Errorf("invalid state transition %q to %q", stateName(s), name) - } - return nil -} - -func (s *createdCheckpointState) Pause(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot pause task in created state") -} - -func (s *createdCheckpointState) Resume(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot resume task in created state") -} - -func (s *createdCheckpointState) Update(context context.Context, r *google_protobuf.Any) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.update(context, r) -} - -func (s *createdCheckpointState) Checkpoint(context context.Context, r *CheckpointConfig) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot checkpoint a task in created state") -} - -func (s *createdCheckpointState) Resize(ws console.WinSize) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.resize(ws) -} - -func (s *createdCheckpointState) Start(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - p := s.p - sio := p.stdio - - var ( - err error - socket *runc.Socket - ) - if sio.Terminal { - if socket, err = runc.NewTempConsoleSocket(); err != nil { - return errors.Wrap(err, "failed to create OCI runtime console socket") - } - defer socket.Close() - s.opts.ConsoleSocket = socket - } - - if _, err := s.p.runtime.Restore(ctx, p.id, p.Bundle, s.opts); err != nil { - return p.runtimeError(err, "OCI runtime restore failed") - } - if sio.Stdin != "" { - sc, err := fifo.OpenFifo(ctx, sio.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0) - if err != nil { - return errors.Wrapf(err, "failed to open stdin fifo %s", sio.Stdin) - } - p.stdin = sc - p.closers = append(p.closers, sc) - } - var copyWaitGroup sync.WaitGroup - if socket != nil { - console, err := socket.ReceiveMaster() - if err != nil { - return errors.Wrap(err, "failed to retrieve console master") - } - console, err = p.Platform.CopyConsole(ctx, console, sio.Stdin, sio.Stdout, sio.Stderr, &p.wg, ©WaitGroup) - if err != nil { - return errors.Wrap(err, "failed to start console copy") - } - p.console = console - } else if !sio.IsNull() { - if err := copyPipes(ctx, p.io, sio.Stdin, sio.Stdout, sio.Stderr, &p.wg, ©WaitGroup); err != nil { - return errors.Wrap(err, "failed to start io pipe copy") - } - } - - copyWaitGroup.Wait() - pid, err := runc.ReadPidFile(s.opts.PidFile) - if err != nil { - return errors.Wrap(err, "failed to retrieve OCI runtime container pid") - } - p.pid = pid - return s.transition("running") -} - -func (s *createdCheckpointState) Delete(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - if err := s.p.delete(ctx); err != nil { - return err - } - return s.transition("deleted") -} - -func (s *createdCheckpointState) Kill(ctx context.Context, sig uint32, all bool) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.kill(ctx, sig, all) -} - -func (s *createdCheckpointState) SetExited(status int) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - s.p.setExited(status) - - if err := s.transition("stopped"); err != nil { - panic(err) - } -} - -func (s *createdCheckpointState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return nil, errors.Errorf("cannot exec in a created state") -} - -type runningState struct { - p *Init -} - -func (s *runningState) transition(name string) error { - switch name { - case "stopped": - s.p.initState = &stoppedState{p: s.p} - case "paused": - s.p.initState = &pausedState{p: s.p} - default: - return errors.Errorf("invalid state transition %q to %q", stateName(s), name) - } - return nil -} - -func (s *runningState) Pause(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - if err := s.p.pause(ctx); err != nil { - return err - } - return s.transition("paused") -} - -func (s *runningState) Resume(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot resume a running process") -} - -func (s *runningState) Update(context context.Context, r *google_protobuf.Any) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.update(context, r) -} - -func (s *runningState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.checkpoint(ctx, r) -} - -func (s *runningState) Resize(ws console.WinSize) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.resize(ws) -} - -func (s *runningState) Start(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot start a running process") -} - -func (s *runningState) Delete(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot delete a running process") -} - -func (s *runningState) Kill(ctx context.Context, sig uint32, all bool) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.kill(ctx, sig, all) -} - -func (s *runningState) SetExited(status int) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - s.p.setExited(status) - - if err := s.transition("stopped"); err != nil { - panic(err) - } -} - -func (s *runningState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - return s.p.exec(ctx, path, r) -} - -type pausedState struct { - p *Init -} - -func (s *pausedState) transition(name string) error { - switch name { - case "running": - s.p.initState = &runningState{p: s.p} - case "stopped": - s.p.initState = &stoppedState{p: s.p} - default: - return errors.Errorf("invalid state transition %q to %q", stateName(s), name) - } - return nil -} - -func (s *pausedState) Pause(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot pause a paused container") -} - -func (s *pausedState) Resume(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - if err := s.p.resume(ctx); err != nil { - return err - } - return s.transition("running") -} - -func (s *pausedState) Update(context context.Context, r *google_protobuf.Any) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.update(context, r) -} - -func (s *pausedState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.checkpoint(ctx, r) -} - -func (s *pausedState) Resize(ws console.WinSize) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.resize(ws) -} - -func (s *pausedState) Start(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot start a paused process") -} - -func (s *pausedState) Delete(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot delete a paused process") -} - -func (s *pausedState) Kill(ctx context.Context, sig uint32, all bool) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return s.p.kill(ctx, sig, all) -} - -func (s *pausedState) SetExited(status int) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - s.p.setExited(status) - - if err := s.transition("stopped"); err != nil { - panic(err) - } -} - -func (s *pausedState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return nil, errors.Errorf("cannot exec in a paused state") -} - -type stoppedState struct { - p *Init -} - -func (s *stoppedState) transition(name string) error { - switch name { - case "deleted": - s.p.initState = &deletedState{} - default: - return errors.Errorf("invalid state transition %q to %q", stateName(s), name) - } - return nil -} - -func (s *stoppedState) Pause(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot pause a stopped container") -} - -func (s *stoppedState) Resume(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot resume a stopped container") -} - -func (s *stoppedState) Update(context context.Context, r *google_protobuf.Any) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot update a stopped container") -} - -func (s *stoppedState) Checkpoint(ctx context.Context, r *CheckpointConfig) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot checkpoint a stopped container") -} - -func (s *stoppedState) Resize(ws console.WinSize) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot resize a stopped container") -} - -func (s *stoppedState) Start(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return errors.Errorf("cannot start a stopped process") -} - -func (s *stoppedState) Delete(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() - if err := s.p.delete(ctx); err != nil { - return err - } - return s.transition("deleted") -} - -func (s *stoppedState) Kill(ctx context.Context, sig uint32, all bool) error { - return errdefs.ToGRPCf(errdefs.ErrNotFound, "process %s not found", s.p.id) -} - -func (s *stoppedState) SetExited(status int) { - // no op -} - -func (s *stoppedState) Exec(ctx context.Context, path string, r *ExecConfig) (proc.Process, error) { - s.p.mu.Lock() - defer s.p.mu.Unlock() - - return nil, errors.Errorf("cannot exec in a stopped state") -} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/io.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/io.go deleted file mode 100644 index 71f6ee1bb3a60..0000000000000 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/io.go +++ /dev/null @@ -1,145 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package proc - -import ( - "context" - "fmt" - "io" - "os" - "sync" - "syscall" - - "github.com/containerd/fifo" - runc "github.com/containerd/go-runc" -) - -var bufPool = sync.Pool{ - New: func() interface{} { - buffer := make([]byte, 32<<10) - return &buffer - }, -} - -func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) error { - var sameFile io.WriteCloser - for _, i := range []struct { - name string - dest func(wc io.WriteCloser, rc io.Closer) - }{ - { - name: stdout, - dest: func(wc io.WriteCloser, rc io.Closer) { - wg.Add(1) - cwg.Add(1) - go func() { - cwg.Done() - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - io.CopyBuffer(wc, rio.Stdout(), *p) - wg.Done() - wc.Close() - if rc != nil { - rc.Close() - } - }() - }, - }, { - name: stderr, - dest: func(wc io.WriteCloser, rc io.Closer) { - wg.Add(1) - cwg.Add(1) - go func() { - cwg.Done() - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - io.CopyBuffer(wc, rio.Stderr(), *p) - wg.Done() - wc.Close() - if rc != nil { - rc.Close() - } - }() - }, - }, - } { - ok, err := isFifo(i.name) - if err != nil { - return err - } - var ( - fw io.WriteCloser - fr io.Closer - ) - if ok { - if fw, err = fifo.OpenFifo(ctx, i.name, syscall.O_WRONLY, 0); err != nil { - return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err) - } - if fr, err = fifo.OpenFifo(ctx, i.name, syscall.O_RDONLY, 0); err != nil { - return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err) - } - } else { - if sameFile != nil { - i.dest(sameFile, nil) - continue - } - if fw, err = os.OpenFile(i.name, syscall.O_WRONLY|syscall.O_APPEND, 0); err != nil { - return fmt.Errorf("containerd-shim: opening %s failed: %s", i.name, err) - } - if stdout == stderr { - sameFile = fw - } - } - i.dest(fw, fr) - } - if stdin == "" { - return nil - } - f, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0) - if err != nil { - return fmt.Errorf("containerd-shim: opening %s failed: %s", stdin, err) - } - cwg.Add(1) - go func() { - cwg.Done() - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - - io.CopyBuffer(rio.Stdin(), f, *p) - rio.Stdin().Close() - f.Close() - }() - return nil -} - -// isFifo checks if a file is a fifo -// if the file does not exist then it returns false -func isFifo(path string) (bool, error) { - stat, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - if stat.Mode()&os.ModeNamedPipe == os.ModeNamedPipe { - return true, nil - } - return false, nil -} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/process.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/process.go deleted file mode 100644 index 53252ec604f5f..0000000000000 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/process.go +++ /dev/null @@ -1,42 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package proc - -import ( - "github.com/pkg/errors" -) - -// RuncRoot is the path to the root runc state directory -const RuncRoot = "/run/containerd/runc" - -func stateName(v interface{}) string { - switch v.(type) { - case *runningState, *execRunningState: - return "running" - case *createdState, *execCreatedState, *createdCheckpointState: - return "created" - case *pausedState: - return "paused" - case *deletedState: - return "deleted" - case *stoppedState: - return "stopped" - } - panic(errors.Errorf("invalid state %v", v)) -} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/types.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/types.go deleted file mode 100644 index 2bea98dc8e0a2..0000000000000 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/types.go +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package proc - -import ( - google_protobuf "github.com/gogo/protobuf/types" -) - -// Mount holds filesystem mount configuration -type Mount struct { - Type string - Source string - Target string - Options []string -} - -// CreateConfig hold task creation configuration -type CreateConfig struct { - ID string - Bundle string - Runtime string - Rootfs []Mount - Terminal bool - Stdin string - Stdout string - Stderr string - Checkpoint string - ParentCheckpoint string - Options *google_protobuf.Any -} - -// ExecConfig holds exec creation configuration -type ExecConfig struct { - ID string - Terminal bool - Stdin string - Stdout string - Stderr string - Spec *google_protobuf.Any -} - -// CheckpointConfig holds task checkpoint configuration -type CheckpointConfig struct { - Path string - Exit bool - AllowOpenTCP bool - AllowExternalUnixSockets bool - AllowTerminal bool - FileLocks bool - EmptyNamespaces []string -} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/utils.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/utils.go deleted file mode 100644 index 3d0334c450644..0000000000000 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/utils.go +++ /dev/null @@ -1,104 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package proc - -import ( - "encoding/json" - "io" - "os" - "strings" - "time" - - "github.com/containerd/containerd/errdefs" - runc "github.com/containerd/go-runc" - "github.com/pkg/errors" - "golang.org/x/sys/unix" -) - -// TODO(mlaventure): move to runc package? -func getLastRuntimeError(r *runc.Runc) (string, error) { - if r.Log == "" { - return "", nil - } - - f, err := os.OpenFile(r.Log, os.O_RDONLY, 0400) - if err != nil { - return "", err - } - - var ( - errMsg string - log struct { - Level string - Msg string - Time time.Time - } - ) - - dec := json.NewDecoder(f) - for err = nil; err == nil; { - if err = dec.Decode(&log); err != nil && err != io.EOF { - return "", err - } - if log.Level == "error" { - errMsg = strings.TrimSpace(log.Msg) - } - } - - return errMsg, nil -} - -// criuError returns only the first line of the error message from criu -// it tries to add an invalid dump log location when returning the message -func criuError(err error) string { - parts := strings.Split(err.Error(), "\n") - return parts[0] -} - -func copyFile(to, from string) error { - ff, err := os.Open(from) - if err != nil { - return err - } - defer ff.Close() - tt, err := os.Create(to) - if err != nil { - return err - } - defer tt.Close() - - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - _, err = io.CopyBuffer(tt, ff, *p) - return err -} - -func checkKillError(err error) error { - if err == nil { - return nil - } - if strings.Contains(err.Error(), "os: process already finished") || err == unix.ESRCH { - return errors.Wrapf(errdefs.ErrNotFound, "process already finished") - } - return errors.Wrapf(err, "unknown error after kill") -} - -func hasNoIO(r *CreateConfig) bool { - return r.Stdin == "" && r.Stdout == "" && r.Stderr == "" -} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/process.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/process.go index 2c60b674a9fbf..c2777452d2b27 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/process.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/linux/process.go @@ -62,7 +62,7 @@ func (p *Process) State(ctx context.Context) (runtime.State, error) { ID: p.id, }) if err != nil { - if errors.Cause(err) != ttrpc.ErrClosed { + if !errors.Is(err, ttrpc.ErrClosed) { return runtime.State{}, errdefs.FromGRPC(err) } diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go index d19b8e5168098..aa6d3f31450c0 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go @@ -21,6 +21,7 @@ package linux import ( "context" "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -36,11 +37,12 @@ import ( "github.com/containerd/containerd/metadata" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/pkg/process" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime/linux/runctypes" - "github.com/containerd/containerd/runtime/v1/linux/proc" + v1 "github.com/containerd/containerd/runtime/v1" shim "github.com/containerd/containerd/runtime/v1/shim/v1" runc "github.com/containerd/go-runc" "github.com/containerd/typeurl" @@ -48,7 +50,6 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" - bolt "go.etcd.io/bbolt" "golang.org/x/sys/unix" ) @@ -61,6 +62,9 @@ const ( configFilename = "config.json" defaultRuntime = "runc" defaultShim = "containerd-shim" + + // cleanupTimeout is default timeout for cleanup operations + cleanupTimeout = 1 * time.Minute ) func init() { @@ -110,13 +114,13 @@ func New(ic *plugin.InitContext) (interface{}, error) { } cfg := ic.Config.(*Config) r := &Runtime{ - root: ic.Root, - state: ic.State, - tasks: runtime.NewTaskList(), - db: m.(*metadata.DB), - address: ic.Address, - events: ic.Events, - config: cfg, + root: ic.Root, + state: ic.State, + tasks: runtime.NewTaskList(), + containers: metadata.NewContainerStore(m.(*metadata.DB)), + address: ic.Address, + events: ic.Events, + config: cfg, } tasks, err := r.restoreTasks(ic.Context) if err != nil { @@ -136,9 +140,9 @@ type Runtime struct { state string address string - tasks *runtime.TaskList - db *metadata.DB - events *exchange.Exchange + tasks *runtime.TaskList + containers containers.Store + events *exchange.Exchange config *Config } @@ -189,18 +193,13 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts } exitHandler := func() { log.G(ctx).WithField("id", id).Info("shim reaped") - t, err := r.tasks.Get(ctx, id) - if err != nil { + + if _, err := r.tasks.Get(ctx, id); err != nil { // Task was never started or was already successfully deleted return } - lc := t.(*Task) - log.G(ctx).WithFields(logrus.Fields{ - "id": id, - "namespace": namespace, - }).Warn("cleaning up after killed shim") - if err = r.cleanupAfterDeadShim(context.Background(), bundle, namespace, id, lc.pid); err != nil { + if err = r.cleanupAfterDeadShim(context.Background(), bundle, namespace, id); err != nil { log.G(ctx).WithError(err).WithFields(logrus.Fields{ "id": id, "namespace": namespace, @@ -216,8 +215,11 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts } defer func() { if err != nil { - if kerr := s.KillShim(ctx); kerr != nil { - log.G(ctx).WithError(err).Error("failed to kill shim") + deferCtx, deferCancel := context.WithTimeout( + namespaces.WithNamespace(context.TODO(), namespace), cleanupTimeout) + defer deferCancel() + if kerr := s.KillShim(deferCtx); kerr != nil { + log.G(ctx).WithError(kerr).Error("failed to kill shim") } } }() @@ -288,6 +290,10 @@ func (r *Runtime) restoreTasks(ctx context.Context) ([]*Task, error) { continue } name := namespace.Name() + // skip hidden directories + if len(name) > 0 && name[0] == '.' { + continue + } log.G(ctx).WithField("namespace", name).Debug("loading tasks in namespace") tasks, err := r.loadTasks(ctx, name) if err != nil { @@ -303,6 +309,16 @@ func (r *Runtime) Get(ctx context.Context, id string) (runtime.Task, error) { return r.tasks.Get(ctx, id) } +// Add a runtime task +func (r *Runtime) Add(ctx context.Context, task runtime.Task) error { + return r.tasks.Add(ctx, task) +} + +// Delete a runtime task +func (r *Runtime) Delete(ctx context.Context, id string) { + r.tasks.Delete(ctx, id) +} + func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) { dir, err := ioutil.ReadDir(filepath.Join(r.state, ns)) if err != nil { @@ -314,16 +330,26 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) { continue } id := path.Name() + // skip hidden directories + if len(id) > 0 && id[0] == '.' { + continue + } bundle := loadBundle( id, filepath.Join(r.state, ns, id), filepath.Join(r.root, ns, id), ) ctx = namespaces.WithNamespace(ctx, ns) - pid, _ := runc.ReadPidFile(filepath.Join(bundle.path, proc.InitPidFile)) + pid, _ := runc.ReadPidFile(filepath.Join(bundle.path, process.InitPidFile)) + shimExit := make(chan struct{}) s, err := bundle.NewShimClient(ctx, ns, ShimConnect(r.config, func() { - err := r.cleanupAfterDeadShim(ctx, bundle, ns, id, pid) - if err != nil { + defer close(shimExit) + if _, err := r.tasks.Get(ctx, id); err != nil { + // Task was never started or was already successfully deleted + return + } + + if err := r.cleanupAfterDeadShim(ctx, bundle, ns, id); err != nil { log.G(ctx).WithError(err).WithField("bundle", bundle.path). Error("cleaning up after dead shim") } @@ -333,7 +359,7 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) { "id": id, "namespace": ns, }).Error("connecting to shim") - err := r.cleanupAfterDeadShim(ctx, bundle, ns, id, pid) + err := r.cleanupAfterDeadShim(ctx, bundle, ns, id) if err != nil { log.G(ctx).WithError(err).WithField("bundle", bundle.path). Error("cleaning up after dead shim") @@ -341,6 +367,50 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) { continue } + logDirPath := filepath.Join(r.root, ns, id) + + copyAndClose := func(dst io.Writer, src io.ReadWriteCloser) { + copyDone := make(chan struct{}) + go func() { + io.Copy(dst, src) + close(copyDone) + }() + select { + case <-shimExit: + case <-copyDone: + } + src.Close() + } + shimStdoutLog, err := v1.OpenShimStdoutLog(ctx, logDirPath) + if err != nil { + log.G(ctx).WithError(err).WithFields(logrus.Fields{ + "id": id, + "namespace": ns, + "logDirPath": logDirPath, + }).Error("opening shim stdout log pipe") + continue + } + if r.config.ShimDebug { + go copyAndClose(os.Stdout, shimStdoutLog) + } else { + go copyAndClose(ioutil.Discard, shimStdoutLog) + } + + shimStderrLog, err := v1.OpenShimStderrLog(ctx, logDirPath) + if err != nil { + log.G(ctx).WithError(err).WithFields(logrus.Fields{ + "id": id, + "namespace": ns, + "logDirPath": logDirPath, + }).Error("opening shim stderr log pipe") + continue + } + if r.config.ShimDebug { + go copyAndClose(os.Stderr, shimStderrLog) + } else { + go copyAndClose(ioutil.Discard, shimStderrLog) + } + t, err := newTask(id, ns, pid, s, r.events, r.tasks, bundle) if err != nil { log.G(ctx).WithError(err).Error("loading task type") @@ -351,7 +421,13 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) { return o, nil } -func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, id string, pid int) error { +func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, id string) error { + log.G(ctx).WithFields(logrus.Fields{ + "id": id, + "namespace": ns, + }).Warn("cleaning up after shim dead") + + pid, _ := runc.ReadPidFile(filepath.Join(bundle.path, process.InitPidFile)) ctx = namespaces.WithNamespace(ctx, ns) if err := r.terminate(ctx, bundle, ns, id); err != nil { if r.config.ShimDebug { @@ -374,6 +450,10 @@ func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, if err := bundle.Delete(); err != nil { log.G(ctx).WithError(err).Error("delete bundle") } + // kill shim + if shimPid, err := runc.ReadPidFile(filepath.Join(bundle.path, "shim.pid")); err == nil && shimPid > 0 { + unix.Kill(shimPid, unix.SIGKILL) + } r.events.Publish(ctx, runtime.TaskDeleteEventTopic, &eventstypes.TaskDelete{ ContainerID: id, @@ -412,7 +492,7 @@ func (r *Runtime) getRuntime(ctx context.Context, ns, id string) (*runc.Runc, er var ( cmd = r.config.Runtime - root = proc.RuncRoot + root = process.RuncRoot ) if ropts != nil { if ropts.Runtime != "" { @@ -433,14 +513,8 @@ func (r *Runtime) getRuntime(ctx context.Context, ns, id string) (*runc.Runc, er } func (r *Runtime) getRuncOptions(ctx context.Context, id string) (*runctypes.RuncOptions, error) { - var container containers.Container - - if err := r.db.View(func(tx *bolt.Tx) error { - store := metadata.NewContainerStore(tx) - var err error - container, err = store.Get(ctx, id) - return err - }); err != nil { + container, err := r.containers.Get(ctx, id) + if err != nil { return nil, err } diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/task.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/task.go index 38da35c085f8f..1e9e50ca5b046 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/task.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/linux/task.go @@ -84,11 +84,19 @@ func (t *Task) Namespace() string { return t.namespace } +// PID of the task +func (t *Task) PID() uint32 { + return uint32(t.pid) +} + // Delete the task and return the exit status func (t *Task) Delete(ctx context.Context) (*runtime.Exit, error) { - rsp, err := t.shim.Delete(ctx, empty) - if err != nil { - return nil, errdefs.FromGRPC(err) + rsp, shimErr := t.shim.Delete(ctx, empty) + if shimErr != nil { + shimErr = errdefs.FromGRPC(shimErr) + if !errdefs.IsNotFound(shimErr) { + return nil, shimErr + } } t.tasks.Delete(ctx, t.id) if err := t.shim.KillShim(ctx); err != nil { @@ -97,6 +105,9 @@ func (t *Task) Delete(ctx context.Context) (*runtime.Exit, error) { if err := t.bundle.Delete(); err != nil { log.G(ctx).WithError(err).Error("failed to delete bundle") } + if shimErr != nil { + return nil, shimErr + } t.events.Publish(ctx, runtime.TaskDeleteEventTopic, &eventstypes.TaskDelete{ ContainerID: t.id, ExitStatus: rsp.ExitStatus, @@ -124,11 +135,15 @@ func (t *Task) Start(ctx context.Context) error { t.pid = int(r.Pid) if !hasCgroup { cg, err := cgroups.Load(cgroups.V1, cgroups.PidPath(t.pid)) - if err != nil { + if err != nil && err != cgroups.ErrCgroupDeleted { return err } t.mu.Lock() - t.cg = cg + if err == cgroups.ErrCgroupDeleted { + t.cg = nil + } else { + t.cg = cg + } t.mu.Unlock() } t.events.Publish(ctx, runtime.TaskStartEventTopic, &eventstypes.TaskStart{ @@ -144,7 +159,7 @@ func (t *Task) State(ctx context.Context) (runtime.State, error) { ID: t.id, }) if err != nil { - if errors.Cause(err) != ttrpc.ErrClosed { + if !errors.Is(err, ttrpc.ErrClosed) { return runtime.State{}, errdefs.FromGRPC(err) } return runtime.State{}, errdefs.ErrNotFound @@ -291,7 +306,7 @@ func (t *Task) Checkpoint(ctx context.Context, path string, options *types.Any) } // Update changes runtime information of a running task -func (t *Task) Update(ctx context.Context, resources *types.Any) error { +func (t *Task) Update(ctx context.Context, resources *types.Any, _ map[string]string) error { if _, err := t.shim.Update(ctx, &shim.UpdateTaskRequest{ Resources: resources, }); err != nil { diff --git a/vendor/github.com/containerd/containerd/runtime/v1/shim.go b/vendor/github.com/containerd/containerd/runtime/v1/shim.go new file mode 100644 index 0000000000000..0a2018b4c29d5 --- /dev/null +++ b/vendor/github.com/containerd/containerd/runtime/v1/shim.go @@ -0,0 +1,38 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package v1 + +import ( + "context" + "io" + "path/filepath" + + "github.com/containerd/fifo" + "golang.org/x/sys/unix" +) + +// OpenShimStdoutLog opens the shim log for reading +func OpenShimStdoutLog(ctx context.Context, logDirPath string) (io.ReadWriteCloser, error) { + return fifo.OpenFifo(ctx, filepath.Join(logDirPath, "shim.stdout.log"), unix.O_RDWR|unix.O_CREAT, 0700) +} + +// OpenShimStderrLog opens the shim log +func OpenShimStderrLog(ctx context.Context, logDirPath string) (io.ReadWriteCloser, error) { + return fifo.OpenFifo(ctx, filepath.Join(logDirPath, "shim.stderr.log"), unix.O_RDWR|unix.O_CREAT, 0700) +} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/shim/client/client.go b/vendor/github.com/containerd/containerd/runtime/v1/shim/client/client.go index 015d88c2dc66c..a8afb0e4832fd 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/shim/client/client.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/shim/client/client.go @@ -20,10 +20,14 @@ package client import ( "context" + "fmt" "io" + "io/ioutil" "net" "os" "os/exec" + "path/filepath" + "strconv" "strings" "sync" "syscall" @@ -37,6 +41,8 @@ import ( "github.com/containerd/containerd/events" "github.com/containerd/containerd/log" + "github.com/containerd/containerd/pkg/dialer" + v1 "github.com/containerd/containerd/runtime/v1" "github.com/containerd/containerd/runtime/v1/shim" shimapi "github.com/containerd/containerd/runtime/v1/shim/v1" "github.com/containerd/containerd/sys" @@ -53,16 +59,43 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa return func(ctx context.Context, config shim.Config) (_ shimapi.ShimService, _ io.Closer, err error) { socket, err := newSocket(address) if err != nil { - return nil, nil, err + if !eaddrinuse(err) { + return nil, nil, err + } + if err := RemoveSocket(address); err != nil { + return nil, nil, errors.Wrap(err, "remove already used socket") + } + if socket, err = newSocket(address); err != nil { + return nil, nil, err + } } - defer socket.Close() + f, err := socket.File() if err != nil { return nil, nil, errors.Wrapf(err, "failed to get fd for socket %s", address) } defer f.Close() - cmd, err := newCommand(binary, daemonAddress, debug, config, f) + stdoutCopy := ioutil.Discard + stderrCopy := ioutil.Discard + stdoutLog, err := v1.OpenShimStdoutLog(ctx, config.WorkDir) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to create stdout log") + } + + stderrLog, err := v1.OpenShimStderrLog(ctx, config.WorkDir) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to create stderr log") + } + if debug { + stdoutCopy = os.Stdout + stderrCopy = os.Stderr + } + + go io.Copy(stdoutCopy, stdoutLog) + go io.Copy(stderrCopy, stderrLog) + + cmd, err := newCommand(binary, daemonAddress, debug, config, f, stdoutLog, stderrLog) if err != nil { return nil, nil, err } @@ -77,12 +110,27 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa go func() { cmd.Wait() exitHandler() + if stdoutLog != nil { + stdoutLog.Close() + } + if stderrLog != nil { + stderrLog.Close() + } + socket.Close() + RemoveSocket(address) }() log.G(ctx).WithFields(logrus.Fields{ "pid": cmd.Process.Pid, "address": address, "debug": debug, }).Infof("shim %s started", binary) + + if err := writeFile(filepath.Join(config.Path, "address"), address); err != nil { + return nil, nil, err + } + if err := writeFile(filepath.Join(config.Path, "shim.pid"), strconv.Itoa(cmd.Process.Pid)); err != nil { + return nil, nil, err + } // set shim in cgroup if it is provided if cgroup != "" { if err := setCgroup(cgroup, cmd); err != nil { @@ -93,8 +141,8 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa "address": address, }).Infof("shim placed in cgroup %s", cgroup) } - if err = sys.SetOOMScore(cmd.Process.Pid, sys.OOMScoreMaxKillable); err != nil { - return nil, nil, errors.Wrap(err, "failed to set OOM Score on shim") + if err = setupOOMScore(cmd.Process.Pid); err != nil { + return nil, nil, err } c, clo, err := WithConnect(address, func() {})(ctx, config) if err != nil { @@ -104,7 +152,43 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa } } -func newCommand(binary, daemonAddress string, debug bool, config shim.Config, socket *os.File) (*exec.Cmd, error) { +func eaddrinuse(err error) bool { + cause := errors.Cause(err) + netErr, ok := cause.(*net.OpError) + if !ok { + return false + } + if netErr.Op != "listen" { + return false + } + syscallErr, ok := netErr.Err.(*os.SyscallError) + if !ok { + return false + } + errno, ok := syscallErr.Err.(syscall.Errno) + if !ok { + return false + } + return errno == syscall.EADDRINUSE +} + +// setupOOMScore gets containerd's oom score and adds +1 to it +// to ensure a shim has a lower* score than the daemons +// if not already at the maximum OOM Score +func setupOOMScore(shimPid int) error { + pid := os.Getpid() + score, err := sys.GetOOMScoreAdj(pid) + if err != nil { + return errors.Wrap(err, "get daemon OOM score") + } + shimScore := score + 1 + if err := sys.AdjustOOMScore(shimPid, shimScore); err != nil { + return errors.Wrap(err, "set shim OOM score") + } + return nil +} + +func newCommand(binary, daemonAddress string, debug bool, config shim.Config, socket *os.File, stdout, stderr io.Writer) (*exec.Cmd, error) { selfExe, err := os.Executable() if err != nil { return nil, err @@ -137,43 +221,101 @@ func newCommand(binary, daemonAddress string, debug bool, config shim.Config, so cmd.SysProcAttr = getSysProcAttr() cmd.ExtraFiles = append(cmd.ExtraFiles, socket) cmd.Env = append(os.Environ(), "GOMAXPROCS=2") - if debug { - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - } + cmd.Stdout = stdout + cmd.Stderr = stderr return cmd, nil } +// writeFile writes a address file atomically +func writeFile(path, address string) error { + path, err := filepath.Abs(path) + if err != nil { + return err + } + tempPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path))) + f, err := os.OpenFile(tempPath, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666) + if err != nil { + return err + } + _, err = f.WriteString(address) + f.Close() + if err != nil { + return err + } + return os.Rename(tempPath, path) +} + +const ( + abstractSocketPrefix = "\x00" + socketPathLimit = 106 +) + +type socket string + +func (s socket) isAbstract() bool { + return !strings.HasPrefix(string(s), "unix://") +} + +func (s socket) path() string { + path := strings.TrimPrefix(string(s), "unix://") + // if there was no trim performed, we assume an abstract socket + if len(path) == len(s) { + path = abstractSocketPrefix + path + } + return path +} + func newSocket(address string) (*net.UnixListener, error) { - if len(address) > 106 { - return nil, errors.Errorf("%q: unix socket path too long (> 106)", address) + if len(address) > socketPathLimit { + return nil, errors.Errorf("%q: unix socket path too long (> %d)", address, socketPathLimit) } - l, err := net.Listen("unix", "\x00"+address) + var ( + sock = socket(address) + path = sock.path() + ) + if !sock.isAbstract() { + if err := os.MkdirAll(filepath.Dir(path), 0600); err != nil { + return nil, errors.Wrapf(err, "%s", path) + } + } + l, err := net.Listen("unix", path) if err != nil { - return nil, errors.Wrapf(err, "failed to listen to abstract unix socket %q", address) + return nil, errors.Wrapf(err, "failed to listen to unix socket %q (abstract: %t)", address, sock.isAbstract()) + } + if err := os.Chmod(path, 0600); err != nil { + l.Close() + return nil, err } return l.(*net.UnixListener), nil } +// RemoveSocket removes the socket at the specified address if +// it exists on the filesystem +func RemoveSocket(address string) error { + sock := socket(address) + if !sock.isAbstract() { + return os.Remove(sock.path()) + } + return nil +} + func connect(address string, d func(string, time.Duration) (net.Conn, error)) (net.Conn, error) { return d(address, 100*time.Second) } -func annonDialer(address string, timeout time.Duration) (net.Conn, error) { - address = strings.TrimPrefix(address, "unix://") - return net.DialTimeout("unix", "\x00"+address, timeout) +func anonDialer(address string, timeout time.Duration) (net.Conn, error) { + return dialer.Dialer(socket(address).path(), timeout) } // WithConnect connects to an existing shim func WithConnect(address string, onClose func()) Opt { return func(ctx context.Context, config shim.Config) (shimapi.ShimService, io.Closer, error) { - conn, err := connect(address, annonDialer) + conn, err := connect(address, anonDialer) if err != nil { return nil, nil, err } - client := ttrpc.NewClient(conn) - client.OnClose(onClose) + client := ttrpc.NewClient(conn, ttrpc.WithOnClose(onClose)) return shimapi.NewShimClient(client), conn, nil } } @@ -233,7 +375,7 @@ func (c *Client) KillShim(ctx context.Context) error { return c.signalShim(ctx, unix.SIGKILL) } -// Close the cient connection +// Close the client connection func (c *Client) Close() error { if c.c == nil { return nil @@ -258,21 +400,31 @@ func (c *Client) signalShim(ctx context.Context, sig syscall.Signal) error { select { case <-ctx.Done(): return ctx.Err() - case <-c.waitForExit(pid): + case <-c.waitForExit(ctx, pid): return nil } } -func (c *Client) waitForExit(pid int) <-chan struct{} { - c.exitOnce.Do(func() { +func (c *Client) waitForExit(ctx context.Context, pid int) <-chan struct{} { + go c.exitOnce.Do(func() { + defer close(c.exitCh) + + ticker := time.NewTicker(10 * time.Millisecond) + defer ticker.Stop() + for { // use kill(pid, 0) here because the shim could have been reparented // and we are no longer able to waitpid(pid, ...) on the shim if err := unix.Kill(pid, 0); err == unix.ESRCH { - close(c.exitCh) return } - time.Sleep(10 * time.Millisecond) + + select { + case <-ticker.C: + case <-ctx.Done(): + log.G(ctx).WithField("pid", pid).Warn("timed out while waiting for shim to exit") + return + } } }) return c.exitCh diff --git a/vendor/github.com/containerd/containerd/runtime/v1/shim/reaper.go b/vendor/github.com/containerd/containerd/runtime/v1/shim/reaper.go deleted file mode 100644 index 2937f1a9ef201..0000000000000 --- a/vendor/github.com/containerd/containerd/runtime/v1/shim/reaper.go +++ /dev/null @@ -1,110 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package shim - -import ( - "os/exec" - "sync" - "time" - - "github.com/containerd/containerd/sys" - runc "github.com/containerd/go-runc" - "github.com/pkg/errors" -) - -// ErrNoSuchProcess is returned when the process no longer exists -var ErrNoSuchProcess = errors.New("no such process") - -const bufferSize = 32 - -// Reap should be called when the process receives an SIGCHLD. Reap will reap -// all exited processes and close their wait channels -func Reap() error { - now := time.Now() - exits, err := sys.Reap(false) - Default.Lock() - for c := range Default.subscribers { - for _, e := range exits { - c <- runc.Exit{ - Timestamp: now, - Pid: e.Pid, - Status: e.Status, - } - } - - } - Default.Unlock() - return err -} - -// Default is the default monitor initialized for the package -var Default = &Monitor{ - subscribers: make(map[chan runc.Exit]struct{}), -} - -// Monitor monitors the underlying system for process status changes -type Monitor struct { - sync.Mutex - - subscribers map[chan runc.Exit]struct{} -} - -// Start starts the command a registers the process with the reaper -func (m *Monitor) Start(c *exec.Cmd) (chan runc.Exit, error) { - ec := m.Subscribe() - if err := c.Start(); err != nil { - m.Unsubscribe(ec) - return nil, err - } - return ec, nil -} - -// Wait blocks until a process is signal as dead. -// User should rely on the value of the exit status to determine if the -// command was successful or not. -func (m *Monitor) Wait(c *exec.Cmd, ec chan runc.Exit) (int, error) { - for e := range ec { - if e.Pid == c.Process.Pid { - // make sure we flush all IO - c.Wait() - m.Unsubscribe(ec) - return e.Status, nil - } - } - // return no such process if the ec channel is closed and no more exit - // events will be sent - return -1, ErrNoSuchProcess -} - -// Subscribe to process exit changes -func (m *Monitor) Subscribe() chan runc.Exit { - c := make(chan runc.Exit, bufferSize) - m.Lock() - m.subscribers[c] = struct{}{} - m.Unlock() - return c -} - -// Unsubscribe to process exit changes -func (m *Monitor) Unsubscribe(c chan runc.Exit) { - m.Lock() - delete(m.subscribers, c) - close(c) - m.Unlock() -} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/shim/service.go b/vendor/github.com/containerd/containerd/runtime/v1/shim/service.go index d76d5803d6830..50ac869d49976 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/shim/service.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/shim/service.go @@ -35,11 +35,12 @@ import ( "github.com/containerd/containerd/log" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/pkg/process" + "github.com/containerd/containerd/pkg/stdio" "github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime/linux/runctypes" - rproc "github.com/containerd/containerd/runtime/proc" - "github.com/containerd/containerd/runtime/v1/linux/proc" shimapi "github.com/containerd/containerd/runtime/v1/shim/v1" + "github.com/containerd/containerd/sys/reaper" runc "github.com/containerd/go-runc" "github.com/containerd/typeurl" ptypes "github.com/gogo/protobuf/types" @@ -54,7 +55,7 @@ var ( empty = &ptypes.Empty{} bufPool = sync.Pool{ New: func() interface{} { - buffer := make([]byte, 32<<10) + buffer := make([]byte, 4096) return &buffer }, } @@ -84,9 +85,9 @@ func NewService(config Config, publisher events.Publisher) (*Service, error) { s := &Service{ config: config, context: ctx, - processes: make(map[string]rproc.Process), + processes: make(map[string]process.Process), events: make(chan interface{}, 128), - ec: Default.Subscribe(), + ec: reaper.Default.Subscribe(), } go s.processExits() if err := s.initPlatform(); err != nil { @@ -102,9 +103,9 @@ type Service struct { config Config context context.Context - processes map[string]rproc.Process + processes map[string]process.Process events chan interface{} - platform rproc.Platform + platform stdio.Platform ec chan runc.Exit // Filled by Create() @@ -114,12 +115,9 @@ type Service struct { // Create a new initial process and container with the underlying OCI runtime func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (_ *shimapi.CreateTaskResponse, err error) { - s.mu.Lock() - defer s.mu.Unlock() - - var mounts []proc.Mount + var mounts []process.Mount for _, m := range r.Rootfs { - mounts = append(mounts, proc.Mount{ + mounts = append(mounts, process.Mount{ Type: m.Type, Source: m.Source, Target: m.Target, @@ -127,7 +125,15 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (_ * }) } - config := &proc.CreateConfig{ + rootfs := "" + if len(mounts) > 0 { + rootfs = filepath.Join(r.Bundle, "rootfs") + if err := os.Mkdir(rootfs, 0711); err != nil && !os.IsExist(err) { + return nil, err + } + } + + config := &process.CreateConfig{ ID: r.ID, Bundle: r.Bundle, Runtime: r.Runtime, @@ -140,7 +146,6 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (_ * ParentCheckpoint: r.ParentCheckpoint, Options: r.Options, } - rootfs := filepath.Join(r.Bundle, "rootfs") defer func() { if err != nil { if err2 := mount.UnmountAll(rootfs, 0); err2 != nil { @@ -158,6 +163,10 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (_ * return nil, errors.Wrapf(err, "failed to mount rootfs component %v", m) } } + + s.mu.Lock() + defer s.mu.Unlock() + process, err := newInit( ctx, s.config.Path, @@ -168,6 +177,7 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (_ * s.config.SystemdCgroup, s.platform, config, + rootfs, ) if err != nil { return nil, errdefs.ToGRPC(err) @@ -187,11 +197,9 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (_ * // Start a process func (s *Service) Start(ctx context.Context, r *shimapi.StartRequest) (*shimapi.StartResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - p := s.processes[r.ID] - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process %s", r.ID) + p, err := s.getExecProcess(r.ID) + if err != nil { + return nil, err } if err := p.Start(ctx); err != nil { return nil, err @@ -204,16 +212,16 @@ func (s *Service) Start(ctx context.Context, r *shimapi.StartRequest) (*shimapi. // Delete the initial process and container func (s *Service) Delete(ctx context.Context, r *ptypes.Empty) (*shimapi.DeleteResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - p := s.processes[s.id] - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") + p, err := s.getInitProcess() + if err != nil { + return nil, err } if err := p.Delete(ctx); err != nil { - return nil, err + return nil, errdefs.ToGRPC(err) } + s.mu.Lock() delete(s.processes, s.id) + s.mu.Unlock() s.platform.Close() return &shimapi.DeleteResponse{ ExitStatus: uint32(p.ExitStatus()), @@ -227,14 +235,12 @@ func (s *Service) DeleteProcess(ctx context.Context, r *shimapi.DeleteProcessReq if r.ID == s.id { return nil, status.Errorf(codes.InvalidArgument, "cannot delete init process with DeleteProcess") } - s.mu.Lock() - p := s.processes[r.ID] - s.mu.Unlock() - if p == nil { - return nil, errors.Wrapf(errdefs.ErrNotFound, "process %s", r.ID) + p, err := s.getExecProcess(r.ID) + if err != nil { + return nil, err } if err := p.Delete(ctx); err != nil { - return nil, err + return nil, errdefs.ToGRPC(err) } s.mu.Lock() delete(s.processes, r.ID) @@ -249,18 +255,19 @@ func (s *Service) DeleteProcess(ctx context.Context, r *shimapi.DeleteProcessReq // Exec an additional process inside the container func (s *Service) Exec(ctx context.Context, r *shimapi.ExecProcessRequest) (*ptypes.Empty, error) { s.mu.Lock() - defer s.mu.Unlock() if p := s.processes[r.ID]; p != nil { + s.mu.Unlock() return nil, errdefs.ToGRPCf(errdefs.ErrAlreadyExists, "id %s", r.ID) } p := s.processes[s.id] + s.mu.Unlock() if p == nil { return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") } - process, err := p.(*proc.Init).Exec(ctx, s.config.Path, &proc.ExecConfig{ + process, err := p.(*process.Init).Exec(ctx, s.config.Path, &process.ExecConfig{ ID: r.ID, Terminal: r.Terminal, Stdin: r.Stdin, @@ -271,14 +278,14 @@ func (s *Service) Exec(ctx context.Context, r *shimapi.ExecProcessRequest) (*pty if err != nil { return nil, errdefs.ToGRPC(err) } + s.mu.Lock() s.processes[r.ID] = process + s.mu.Unlock() return empty, nil } // ResizePty of a process func (s *Service) ResizePty(ctx context.Context, r *shimapi.ResizePtyRequest) (*ptypes.Empty, error) { - s.mu.Lock() - defer s.mu.Unlock() if r.ID == "" { return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "id not provided") } @@ -286,7 +293,9 @@ func (s *Service) ResizePty(ctx context.Context, r *shimapi.ResizePtyRequest) (* Width: uint16(r.Width), Height: uint16(r.Height), } + s.mu.Lock() p := s.processes[r.ID] + s.mu.Unlock() if p == nil { return nil, errors.Errorf("process does not exist %s", r.ID) } @@ -298,11 +307,9 @@ func (s *Service) ResizePty(ctx context.Context, r *shimapi.ResizePtyRequest) (* // State returns runtime state information for a process func (s *Service) State(ctx context.Context, r *shimapi.StateRequest) (*shimapi.StateResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - p := s.processes[r.ID] - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process id %s", r.ID) + p, err := s.getExecProcess(r.ID) + if err != nil { + return nil, err } st, err := p.Status(ctx) if err != nil { @@ -338,13 +345,11 @@ func (s *Service) State(ctx context.Context, r *shimapi.StateRequest) (*shimapi. // Pause the container func (s *Service) Pause(ctx context.Context, r *ptypes.Empty) (*ptypes.Empty, error) { - s.mu.Lock() - defer s.mu.Unlock() - p := s.processes[s.id] - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") + p, err := s.getInitProcess() + if err != nil { + return nil, err } - if err := p.(*proc.Init).Pause(ctx); err != nil { + if err := p.(*process.Init).Pause(ctx); err != nil { return nil, err } return empty, nil @@ -352,13 +357,11 @@ func (s *Service) Pause(ctx context.Context, r *ptypes.Empty) (*ptypes.Empty, er // Resume the container func (s *Service) Resume(ctx context.Context, r *ptypes.Empty) (*ptypes.Empty, error) { - s.mu.Lock() - defer s.mu.Unlock() - p := s.processes[s.id] - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") + p, err := s.getInitProcess() + if err != nil { + return nil, err } - if err := p.(*proc.Init).Resume(ctx); err != nil { + if err := p.(*process.Init).Resume(ctx); err != nil { return nil, err } return empty, nil @@ -366,12 +369,10 @@ func (s *Service) Resume(ctx context.Context, r *ptypes.Empty) (*ptypes.Empty, e // Kill a process with the provided signal func (s *Service) Kill(ctx context.Context, r *shimapi.KillRequest) (*ptypes.Empty, error) { - s.mu.Lock() - defer s.mu.Unlock() if r.ID == "" { - p := s.processes[s.id] - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") + p, err := s.getInitProcess() + if err != nil { + return nil, err } if err := p.Kill(ctx, r.Signal, r.All); err != nil { return nil, errdefs.ToGRPC(err) @@ -379,9 +380,9 @@ func (s *Service) Kill(ctx context.Context, r *shimapi.KillRequest) (*ptypes.Emp return empty, nil } - p := s.processes[r.ID] - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process id %s not found", r.ID) + p, err := s.getExecProcess(r.ID) + if err != nil { + return nil, err } if err := p.Kill(ctx, r.Signal, r.All); err != nil { return nil, errdefs.ToGRPC(err) @@ -396,6 +397,9 @@ func (s *Service) ListPids(ctx context.Context, r *shimapi.ListPidsRequest) (*sh return nil, errdefs.ToGRPC(err) } var processes []*task.ProcessInfo + + s.mu.Lock() + defer s.mu.Unlock() for _, pid := range pids { pInfo := task.ProcessInfo{ Pid: pid, @@ -422,11 +426,9 @@ func (s *Service) ListPids(ctx context.Context, r *shimapi.ListPidsRequest) (*sh // CloseIO of a process func (s *Service) CloseIO(ctx context.Context, r *shimapi.CloseIORequest) (*ptypes.Empty, error) { - s.mu.Lock() - defer s.mu.Unlock() - p := s.processes[r.ID] - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process does not exist %s", r.ID) + p, err := s.getExecProcess(r.ID) + if err != nil { + return nil, err } if stdin := p.Stdin(); stdin != nil { if err := stdin.Close(); err != nil { @@ -438,11 +440,9 @@ func (s *Service) CloseIO(ctx context.Context, r *shimapi.CloseIORequest) (*ptyp // Checkpoint the container func (s *Service) Checkpoint(ctx context.Context, r *shimapi.CheckpointTaskRequest) (*ptypes.Empty, error) { - s.mu.Lock() - defer s.mu.Unlock() - p := s.processes[s.id] - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") + p, err := s.getInitProcess() + if err != nil { + return nil, err } var options runctypes.CheckpointOptions if r.Options != nil { @@ -452,7 +452,7 @@ func (s *Service) Checkpoint(ctx context.Context, r *shimapi.CheckpointTaskReque } options = *v.(*runctypes.CheckpointOptions) } - if err := p.(*proc.Init).Checkpoint(ctx, &proc.CheckpointConfig{ + if err := p.(*process.Init).Checkpoint(ctx, &process.CheckpointConfig{ Path: r.Path, Exit: options.Exit, AllowOpenTCP: options.OpenTcp, @@ -460,6 +460,7 @@ func (s *Service) Checkpoint(ctx context.Context, r *shimapi.CheckpointTaskReque AllowTerminal: options.Terminal, FileLocks: options.FileLocks, EmptyNamespaces: options.EmptyNamespaces, + WorkDir: options.WorkPath, }); err != nil { return nil, errdefs.ToGRPC(err) } @@ -475,13 +476,11 @@ func (s *Service) ShimInfo(ctx context.Context, r *ptypes.Empty) (*shimapi.ShimI // Update a running container func (s *Service) Update(ctx context.Context, r *shimapi.UpdateTaskRequest) (*ptypes.Empty, error) { - s.mu.Lock() - defer s.mu.Unlock() - p := s.processes[s.id] - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") + p, err := s.getInitProcess() + if err != nil { + return nil, err } - if err := p.(*proc.Init).Update(ctx, r.Resources); err != nil { + if err := p.(*process.Init).Update(ctx, r.Resources); err != nil { return nil, errdefs.ToGRPC(err) } return empty, nil @@ -489,11 +488,9 @@ func (s *Service) Update(ctx context.Context, r *shimapi.UpdateTaskRequest) (*pt // Wait for a process to exit func (s *Service) Wait(ctx context.Context, r *shimapi.WaitRequest) (*shimapi.WaitResponse, error) { - s.mu.Lock() - p := s.processes[r.ID] - s.mu.Unlock() - if p == nil { - return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") + p, err := s.getExecProcess(r.ID) + if err != nil { + return nil, err } p.Wait() @@ -510,67 +507,67 @@ func (s *Service) processExits() { } func (s *Service) checkProcesses(e runc.Exit) { + var p process.Process s.mu.Lock() - defer s.mu.Unlock() - - shouldKillAll, err := shouldKillAllOnExit(s.bundle) - if err != nil { - log.G(s.context).WithError(err).Error("failed to check shouldKillAll") + for _, proc := range s.processes { + if proc.Pid() == e.Pid { + p = proc + break + } } - - for _, p := range s.processes { - if p.Pid() == e.Pid { - - if shouldKillAll { - if ip, ok := p.(*proc.Init); ok { - // Ensure all children are killed - if err := ip.KillAll(s.context); err != nil { - log.G(s.context).WithError(err).WithField("id", ip.ID()). - Error("failed to kill init's children") - } - } - } - p.SetExited(e.Status) - s.events <- &eventstypes.TaskExit{ - ContainerID: s.id, - ID: p.ID(), - Pid: uint32(e.Pid), - ExitStatus: uint32(e.Status), - ExitedAt: p.ExitedAt(), + s.mu.Unlock() + if p == nil { + log.G(s.context).Debugf("process with id:%d wasn't found", e.Pid) + return + } + if ip, ok := p.(*process.Init); ok { + // Ensure all children are killed + if shouldKillAllOnExit(s.context, s.bundle) { + if err := ip.KillAll(s.context); err != nil { + log.G(s.context).WithError(err).WithField("id", ip.ID()). + Error("failed to kill init's children") } - return } } + + p.SetExited(e.Status) + s.events <- &eventstypes.TaskExit{ + ContainerID: s.id, + ID: p.ID(), + Pid: uint32(e.Pid), + ExitStatus: uint32(e.Status), + ExitedAt: p.ExitedAt(), + } } -func shouldKillAllOnExit(bundlePath string) (bool, error) { +func shouldKillAllOnExit(ctx context.Context, bundlePath string) bool { var bundleSpec specs.Spec bundleConfigContents, err := ioutil.ReadFile(filepath.Join(bundlePath, "config.json")) if err != nil { - return false, err + log.G(ctx).WithError(err).Error("shouldKillAllOnExit: failed to read config.json") + return true + } + if err := json.Unmarshal(bundleConfigContents, &bundleSpec); err != nil { + log.G(ctx).WithError(err).Error("shouldKillAllOnExit: failed to unmarshal bundle json") + return true } - json.Unmarshal(bundleConfigContents, &bundleSpec) - if bundleSpec.Linux != nil { for _, ns := range bundleSpec.Linux.Namespaces { - if ns.Type == specs.PIDNamespace { - return false, nil + if ns.Type == specs.PIDNamespace && ns.Path == "" { + return false } } } - - return true, nil + return true } func (s *Service) getContainerPids(ctx context.Context, id string) ([]uint32, error) { - s.mu.Lock() - defer s.mu.Unlock() - p := s.processes[s.id] - if p == nil { - return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "container must be created") + p, err := s.getInitProcess() + if err != nil { + return nil, err } - ps, err := p.(*proc.Init).Runtime().Ps(ctx, id) + ps, err := p.(*process.Init).Runtime().Ps(ctx, id) if err != nil { return nil, err } @@ -589,6 +586,30 @@ func (s *Service) forward(publisher events.Publisher) { } } +// getInitProcess returns initial process +func (s *Service) getInitProcess() (process.Process, error) { + s.mu.Lock() + defer s.mu.Unlock() + + p := s.processes[s.id] + if p == nil { + return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") + } + return p, nil +} + +// getExecProcess returns exec process +func (s *Service) getExecProcess(id string) (process.Process, error) { + s.mu.Lock() + defer s.mu.Unlock() + + p := s.processes[id] + if p == nil { + return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process %s does not exist", id) + } + return p, nil +} + func getTopic(ctx context.Context, e interface{}) string { switch e.(type) { case *eventstypes.TaskCreate: @@ -617,7 +638,7 @@ func getTopic(ctx context.Context, e interface{}) string { return runtime.TaskUnknownTopic } -func newInit(ctx context.Context, path, workDir, runtimeRoot, namespace, criu string, systemdCgroup bool, platform rproc.Platform, r *proc.CreateConfig) (*proc.Init, error) { +func newInit(ctx context.Context, path, workDir, runtimeRoot, namespace, criu string, systemdCgroup bool, platform stdio.Platform, r *process.CreateConfig, rootfs string) (*process.Init, error) { var options runctypes.CreateOptions if r.Options != nil { v, err := typeurl.UnmarshalAny(r.Options) @@ -627,9 +648,8 @@ func newInit(ctx context.Context, path, workDir, runtimeRoot, namespace, criu st options = *v.(*runctypes.CreateOptions) } - rootfs := filepath.Join(path, "rootfs") - runtime := proc.NewRunc(runtimeRoot, path, namespace, r.Runtime, criu, systemdCgroup) - p := proc.New(r.ID, runtime, rproc.Stdio{ + runtime := process.NewRunc(runtimeRoot, path, namespace, r.Runtime, criu, systemdCgroup) + p := process.New(r.ID, runtime, stdio.Stdio{ Stdin: r.Stdin, Stdout: r.Stdout, Stderr: r.Stderr, @@ -643,5 +663,11 @@ func newInit(ctx context.Context, path, workDir, runtimeRoot, namespace, criu st p.IoGID = int(options.IoGid) p.NoPivotRoot = options.NoPivotRoot p.NoNewKeyring = options.NoNewKeyring + p.CriuWorkPath = options.CriuWorkPath + if p.CriuWorkPath == "" { + // if criu work path not set, use container WorkDir + p.CriuWorkPath = p.WorkDir + } + return p, nil } diff --git a/vendor/github.com/containerd/containerd/runtime/v1/shim/service_linux.go b/vendor/github.com/containerd/containerd/runtime/v1/shim/service_linux.go index 18ae6503b4ee0..c0950049d6060 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/shim/service_linux.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/shim/service_linux.go @@ -19,10 +19,14 @@ package shim import ( "context" "io" + "net/url" + "os" "sync" "syscall" "github.com/containerd/console" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/pkg/process" "github.com/containerd/fifo" "github.com/pkg/errors" ) @@ -31,7 +35,7 @@ type linuxPlatform struct { epoller *console.Epoller } -func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) (console.Console, error) { +func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console, id, stdin, stdout, stderr string, wg *sync.WaitGroup) (cons console.Console, retErr error) { if p.epoller == nil { return nil, errors.New("uninitialized epoller") } @@ -40,6 +44,7 @@ func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console if err != nil { return nil, err } + var cwg sync.WaitGroup if stdin != "" { in, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY, 0) @@ -49,32 +54,107 @@ func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console cwg.Add(1) go func() { cwg.Done() - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - io.CopyBuffer(epollConsole, in, *p) + bp := bufPool.Get().(*[]byte) + defer bufPool.Put(bp) + io.CopyBuffer(epollConsole, in, *bp) + // we need to shutdown epollConsole when pipe broken + epollConsole.Shutdown(p.epoller.CloseConsole) + epollConsole.Close() }() } - outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0) + uri, err := url.Parse(stdout) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to parse stdout uri") } - outr, err := fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY, 0) - if err != nil { - return nil, err + + switch uri.Scheme { + case "binary": + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, err + } + + cmd := process.NewBinaryCmd(uri, id, ns) + + // In case of unexpected errors during logging binary start, close open pipes + var filesToClose []*os.File + + defer func() { + if retErr != nil { + process.CloseFiles(filesToClose...) + } + }() + + // Create pipe to be used by logging binary for Stdout + outR, outW, err := os.Pipe() + if err != nil { + return nil, errors.Wrap(err, "failed to create stdout pipes") + } + filesToClose = append(filesToClose, outR) + + // Stderr is created for logging binary but unused when terminal is true + serrR, _, err := os.Pipe() + if err != nil { + return nil, errors.Wrap(err, "failed to create stderr pipes") + } + filesToClose = append(filesToClose, serrR) + + r, w, err := os.Pipe() + if err != nil { + return nil, err + } + filesToClose = append(filesToClose, r) + + cmd.ExtraFiles = append(cmd.ExtraFiles, outR, serrR, w) + + wg.Add(1) + cwg.Add(1) + go func() { + cwg.Done() + io.Copy(outW, epollConsole) + outW.Close() + wg.Done() + }() + + if err := cmd.Start(); err != nil { + return nil, errors.Wrap(err, "failed to start logging binary process") + } + + // Close our side of the pipe after start + if err := w.Close(); err != nil { + return nil, errors.Wrap(err, "failed to close write pipe after start") + } + + // Wait for the logging binary to be ready + b := make([]byte, 1) + if _, err := r.Read(b); err != nil && err != io.EOF { + return nil, errors.Wrap(err, "failed to read from logging binary") + } + cwg.Wait() + + default: + outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0) + if err != nil { + return nil, err + } + outr, err := fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY, 0) + if err != nil { + return nil, err + } + wg.Add(1) + cwg.Add(1) + go func() { + cwg.Done() + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + io.CopyBuffer(outw, epollConsole, *p) + outw.Close() + outr.Close() + wg.Done() + }() + cwg.Wait() } - wg.Add(1) - cwg.Add(1) - go func() { - cwg.Done() - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - io.CopyBuffer(outw, epollConsole, *p) - epollConsole.Close() - outr.Close() - outw.Close() - wg.Done() - }() return epollConsole, nil } diff --git a/vendor/github.com/containerd/containerd/runtime/v1/shim/service_unix.go b/vendor/github.com/containerd/containerd/runtime/v1/shim/service_unix.go index 708e45c296330..d96ecd60daf62 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/shim/service_unix.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/shim/service_unix.go @@ -21,17 +21,23 @@ package shim import ( "context" "io" + "net/url" + "os" "sync" "syscall" "github.com/containerd/console" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/pkg/process" "github.com/containerd/fifo" + "github.com/pkg/errors" ) type unixPlatform struct { } -func (p *unixPlatform) CopyConsole(ctx context.Context, console console.Console, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) (console.Console, error) { +func (p *unixPlatform) CopyConsole(ctx context.Context, console console.Console, id, stdin, stdout, stderr string, wg *sync.WaitGroup) (cons console.Console, retErr error) { + var cwg sync.WaitGroup if stdin != "" { in, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY, 0) if err != nil { @@ -46,27 +52,97 @@ func (p *unixPlatform) CopyConsole(ctx context.Context, console console.Console, io.CopyBuffer(console, in, *p) }() } - outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0) + uri, err := url.Parse(stdout) if err != nil { - return nil, err + return nil, errors.Wrap(err, "unable to parse stdout uri") } - outr, err := fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY, 0) - if err != nil { - return nil, err + + switch uri.Scheme { + case "binary": + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, err + } + cmd := process.NewBinaryCmd(uri, id, ns) + + // In case of unexpected errors during logging binary start, close open pipes + var filesToClose []*os.File + + defer func() { + if retErr != nil { + process.CloseFiles(filesToClose...) + } + }() + + // Create pipe to be used by logging binary for Stdout + outR, outW, err := os.Pipe() + if err != nil { + return nil, errors.Wrap(err, "failed to create stdout pipes") + } + filesToClose = append(filesToClose, outR) + + // Stderr is created for logging binary but unused when terminal is true + serrR, _, err := os.Pipe() + if err != nil { + return nil, errors.Wrap(err, "failed to create stderr pipes") + } + filesToClose = append(filesToClose, serrR) + + r, w, err := os.Pipe() + if err != nil { + return nil, err + } + filesToClose = append(filesToClose, r) + + cmd.ExtraFiles = append(cmd.ExtraFiles, outR, serrR, w) + + wg.Add(1) + cwg.Add(1) + go func() { + cwg.Done() + io.Copy(outW, console) + outW.Close() + wg.Done() + }() + + if err := cmd.Start(); err != nil { + return nil, errors.Wrap(err, "failed to start logging binary process") + } + + // Close our side of the pipe after start + if err := w.Close(); err != nil { + return nil, errors.Wrap(err, "failed to close write pipe after start") + } + + // Wait for the logging binary to be ready + b := make([]byte, 1) + if _, err := r.Read(b); err != nil && err != io.EOF { + return nil, errors.Wrap(err, "failed to read from logging binary") + } + cwg.Wait() + + default: + outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0) + if err != nil { + return nil, err + } + outr, err := fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY, 0) + if err != nil { + return nil, err + } + wg.Add(1) + cwg.Add(1) + go func() { + cwg.Done() + p := bufPool.Get().(*[]byte) + defer bufPool.Put(p) + io.CopyBuffer(outw, console, *p) + outw.Close() + outr.Close() + wg.Done() + }() + cwg.Wait() } - wg.Add(1) - cwg.Add(1) - go func() { - cwg.Done() - p := bufPool.Get().(*[]byte) - defer bufPool.Put(p) - - io.CopyBuffer(outw, console, *p) - console.Close() - outr.Close() - outw.Close() - wg.Done() - }() return console, nil } diff --git a/vendor/github.com/containerd/containerd/runtime/v1/shim/v1/shim.pb.go b/vendor/github.com/containerd/containerd/runtime/v1/shim/v1/shim.pb.go index 9bd2889ba7a3d..dbc82599d6bdd 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/shim/v1/shim.pb.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/shim/v1/shim.pb.go @@ -1,58 +1,24 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto -/* - Package shim is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto - - It has these top-level messages: - CreateTaskRequest - CreateTaskResponse - DeleteResponse - DeleteProcessRequest - ExecProcessRequest - ExecProcessResponse - ResizePtyRequest - StateRequest - StateResponse - KillRequest - CloseIORequest - ListPidsRequest - ListPidsResponse - CheckpointTaskRequest - ShimInfoResponse - UpdateTaskRequest - StartRequest - StartResponse - WaitRequest - WaitResponse -*/ package shim -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/gogo/protobuf/types" -import google_protobuf1 "github.com/gogo/protobuf/types" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import _ "github.com/gogo/protobuf/types" -import containerd_types "github.com/containerd/containerd/api/types" -import containerd_v1_types "github.com/containerd/containerd/api/types/task" - -import time "time" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" - -import context "context" -import ttrpc "github.com/containerd/ttrpc" - -import io "io" +import ( + context "context" + fmt "fmt" + types "github.com/containerd/containerd/api/types" + task "github.com/containerd/containerd/api/types/task" + github_com_containerd_ttrpc "github.com/containerd/ttrpc" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types1 "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -64,200 +30,820 @@ var _ = time.Kitchen // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type CreateTaskRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` - Runtime string `protobuf:"bytes,3,opt,name=runtime,proto3" json:"runtime,omitempty"` - Rootfs []*containerd_types.Mount `protobuf:"bytes,4,rep,name=rootfs" json:"rootfs,omitempty"` - Terminal bool `protobuf:"varint,5,opt,name=terminal,proto3" json:"terminal,omitempty"` - Stdin string `protobuf:"bytes,6,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,7,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,8,opt,name=stderr,proto3" json:"stderr,omitempty"` - Checkpoint string `protobuf:"bytes,9,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` - ParentCheckpoint string `protobuf:"bytes,10,opt,name=parent_checkpoint,json=parentCheckpoint,proto3" json:"parent_checkpoint,omitempty"` - Options *google_protobuf.Any `protobuf:"bytes,11,opt,name=options" json:"options,omitempty"` -} - -func (m *CreateTaskRequest) Reset() { *m = CreateTaskRequest{} } -func (*CreateTaskRequest) ProtoMessage() {} -func (*CreateTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{0} } + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` + Runtime string `protobuf:"bytes,3,opt,name=runtime,proto3" json:"runtime,omitempty"` + Rootfs []*types.Mount `protobuf:"bytes,4,rep,name=rootfs,proto3" json:"rootfs,omitempty"` + Terminal bool `protobuf:"varint,5,opt,name=terminal,proto3" json:"terminal,omitempty"` + Stdin string `protobuf:"bytes,6,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,7,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,8,opt,name=stderr,proto3" json:"stderr,omitempty"` + Checkpoint string `protobuf:"bytes,9,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + ParentCheckpoint string `protobuf:"bytes,10,opt,name=parent_checkpoint,json=parentCheckpoint,proto3" json:"parent_checkpoint,omitempty"` + Options *types1.Any `protobuf:"bytes,11,opt,name=options,proto3" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateTaskRequest) Reset() { *m = CreateTaskRequest{} } +func (*CreateTaskRequest) ProtoMessage() {} +func (*CreateTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{0} +} +func (m *CreateTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateTaskRequest.Merge(m, src) +} +func (m *CreateTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateTaskRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_CreateTaskRequest proto.InternalMessageInfo type CreateTaskResponse struct { - Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CreateTaskResponse) Reset() { *m = CreateTaskResponse{} } +func (*CreateTaskResponse) ProtoMessage() {} +func (*CreateTaskResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{1} +} +func (m *CreateTaskResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateTaskResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateTaskResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateTaskResponse.Merge(m, src) +} +func (m *CreateTaskResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateTaskResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateTaskResponse.DiscardUnknown(m) } -func (m *CreateTaskResponse) Reset() { *m = CreateTaskResponse{} } -func (*CreateTaskResponse) ProtoMessage() {} -func (*CreateTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{1} } +var xxx_messageInfo_CreateTaskResponse proto.InternalMessageInfo type DeleteResponse struct { - Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` - ExitStatus uint32 `protobuf:"varint,2,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` - ExitedAt time.Time `protobuf:"bytes,3,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + ExitStatus uint32 `protobuf:"varint,2,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,3,opt,name=exited_at,json=exitedAt,proto3,stdtime" json:"exited_at"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteResponse) Reset() { *m = DeleteResponse{} } +func (*DeleteResponse) ProtoMessage() {} +func (*DeleteResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{2} +} +func (m *DeleteResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteResponse.Merge(m, src) +} +func (m *DeleteResponse) XXX_Size() int { + return m.Size() +} +func (m *DeleteResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteResponse.DiscardUnknown(m) } -func (m *DeleteResponse) Reset() { *m = DeleteResponse{} } -func (*DeleteResponse) ProtoMessage() {} -func (*DeleteResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{2} } +var xxx_messageInfo_DeleteResponse proto.InternalMessageInfo type DeleteProcessRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeleteProcessRequest) Reset() { *m = DeleteProcessRequest{} } +func (*DeleteProcessRequest) ProtoMessage() {} +func (*DeleteProcessRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{3} +} +func (m *DeleteProcessRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DeleteProcessRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DeleteProcessRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DeleteProcessRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeleteProcessRequest.Merge(m, src) +} +func (m *DeleteProcessRequest) XXX_Size() int { + return m.Size() +} +func (m *DeleteProcessRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DeleteProcessRequest.DiscardUnknown(m) } -func (m *DeleteProcessRequest) Reset() { *m = DeleteProcessRequest{} } -func (*DeleteProcessRequest) ProtoMessage() {} -func (*DeleteProcessRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{3} } +var xxx_messageInfo_DeleteProcessRequest proto.InternalMessageInfo type ExecProcessRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Terminal bool `protobuf:"varint,2,opt,name=terminal,proto3" json:"terminal,omitempty"` - Stdin string `protobuf:"bytes,3,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,4,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,5,opt,name=stderr,proto3" json:"stderr,omitempty"` - Spec *google_protobuf.Any `protobuf:"bytes,6,opt,name=spec" json:"spec,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Terminal bool `protobuf:"varint,2,opt,name=terminal,proto3" json:"terminal,omitempty"` + Stdin string `protobuf:"bytes,3,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,4,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,5,opt,name=stderr,proto3" json:"stderr,omitempty"` + Spec *types1.Any `protobuf:"bytes,6,opt,name=spec,proto3" json:"spec,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ExecProcessRequest) Reset() { *m = ExecProcessRequest{} } +func (*ExecProcessRequest) ProtoMessage() {} +func (*ExecProcessRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{4} +} +func (m *ExecProcessRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExecProcessRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExecProcessRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ExecProcessRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExecProcessRequest.Merge(m, src) +} +func (m *ExecProcessRequest) XXX_Size() int { + return m.Size() +} +func (m *ExecProcessRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ExecProcessRequest.DiscardUnknown(m) } -func (m *ExecProcessRequest) Reset() { *m = ExecProcessRequest{} } -func (*ExecProcessRequest) ProtoMessage() {} -func (*ExecProcessRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{4} } +var xxx_messageInfo_ExecProcessRequest proto.InternalMessageInfo type ExecProcessResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ExecProcessResponse) Reset() { *m = ExecProcessResponse{} } -func (*ExecProcessResponse) ProtoMessage() {} -func (*ExecProcessResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{5} } +func (m *ExecProcessResponse) Reset() { *m = ExecProcessResponse{} } +func (*ExecProcessResponse) ProtoMessage() {} +func (*ExecProcessResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{5} +} +func (m *ExecProcessResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExecProcessResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExecProcessResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ExecProcessResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExecProcessResponse.Merge(m, src) +} +func (m *ExecProcessResponse) XXX_Size() int { + return m.Size() +} +func (m *ExecProcessResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ExecProcessResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ExecProcessResponse proto.InternalMessageInfo type ResizePtyRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Width uint32 `protobuf:"varint,2,opt,name=width,proto3" json:"width,omitempty"` - Height uint32 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Width uint32 `protobuf:"varint,2,opt,name=width,proto3" json:"width,omitempty"` + Height uint32 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ResizePtyRequest) Reset() { *m = ResizePtyRequest{} } +func (*ResizePtyRequest) ProtoMessage() {} +func (*ResizePtyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{6} +} +func (m *ResizePtyRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResizePtyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResizePtyRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResizePtyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResizePtyRequest.Merge(m, src) +} +func (m *ResizePtyRequest) XXX_Size() int { + return m.Size() +} +func (m *ResizePtyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ResizePtyRequest.DiscardUnknown(m) } -func (m *ResizePtyRequest) Reset() { *m = ResizePtyRequest{} } -func (*ResizePtyRequest) ProtoMessage() {} -func (*ResizePtyRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{6} } +var xxx_messageInfo_ResizePtyRequest proto.InternalMessageInfo type StateRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StateRequest) Reset() { *m = StateRequest{} } +func (*StateRequest) ProtoMessage() {} +func (*StateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{7} +} +func (m *StateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_StateRequest.Merge(m, src) +} +func (m *StateRequest) XXX_Size() int { + return m.Size() +} +func (m *StateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_StateRequest.DiscardUnknown(m) } -func (m *StateRequest) Reset() { *m = StateRequest{} } -func (*StateRequest) ProtoMessage() {} -func (*StateRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{7} } +var xxx_messageInfo_StateRequest proto.InternalMessageInfo type StateResponse struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` - Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` - Status containerd_v1_types.Status `protobuf:"varint,4,opt,name=status,proto3,enum=containerd.v1.types.Status" json:"status,omitempty"` - Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"` - Terminal bool `protobuf:"varint,8,opt,name=terminal,proto3" json:"terminal,omitempty"` - ExitStatus uint32 `protobuf:"varint,9,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` - ExitedAt time.Time `protobuf:"bytes,10,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` -} - -func (m *StateResponse) Reset() { *m = StateResponse{} } -func (*StateResponse) ProtoMessage() {} -func (*StateResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{8} } + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Bundle string `protobuf:"bytes,2,opt,name=bundle,proto3" json:"bundle,omitempty"` + Pid uint32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + Status task.Status `protobuf:"varint,4,opt,name=status,proto3,enum=containerd.v1.types.Status" json:"status,omitempty"` + Stdin string `protobuf:"bytes,5,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,6,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,7,opt,name=stderr,proto3" json:"stderr,omitempty"` + Terminal bool `protobuf:"varint,8,opt,name=terminal,proto3" json:"terminal,omitempty"` + ExitStatus uint32 `protobuf:"varint,9,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,10,opt,name=exited_at,json=exitedAt,proto3,stdtime" json:"exited_at"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StateResponse) Reset() { *m = StateResponse{} } +func (*StateResponse) ProtoMessage() {} +func (*StateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{8} +} +func (m *StateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_StateResponse.Merge(m, src) +} +func (m *StateResponse) XXX_Size() int { + return m.Size() +} +func (m *StateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_StateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_StateResponse proto.InternalMessageInfo type KillRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Signal uint32 `protobuf:"varint,2,opt,name=signal,proto3" json:"signal,omitempty"` - All bool `protobuf:"varint,3,opt,name=all,proto3" json:"all,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Signal uint32 `protobuf:"varint,2,opt,name=signal,proto3" json:"signal,omitempty"` + All bool `protobuf:"varint,3,opt,name=all,proto3" json:"all,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *KillRequest) Reset() { *m = KillRequest{} } +func (*KillRequest) ProtoMessage() {} +func (*KillRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{9} +} +func (m *KillRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KillRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_KillRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *KillRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_KillRequest.Merge(m, src) +} +func (m *KillRequest) XXX_Size() int { + return m.Size() +} +func (m *KillRequest) XXX_DiscardUnknown() { + xxx_messageInfo_KillRequest.DiscardUnknown(m) } -func (m *KillRequest) Reset() { *m = KillRequest{} } -func (*KillRequest) ProtoMessage() {} -func (*KillRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{9} } +var xxx_messageInfo_KillRequest proto.InternalMessageInfo type CloseIORequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Stdin bool `protobuf:"varint,2,opt,name=stdin,proto3" json:"stdin,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Stdin bool `protobuf:"varint,2,opt,name=stdin,proto3" json:"stdin,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CloseIORequest) Reset() { *m = CloseIORequest{} } +func (*CloseIORequest) ProtoMessage() {} +func (*CloseIORequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{10} +} +func (m *CloseIORequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CloseIORequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CloseIORequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CloseIORequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CloseIORequest.Merge(m, src) +} +func (m *CloseIORequest) XXX_Size() int { + return m.Size() +} +func (m *CloseIORequest) XXX_DiscardUnknown() { + xxx_messageInfo_CloseIORequest.DiscardUnknown(m) } -func (m *CloseIORequest) Reset() { *m = CloseIORequest{} } -func (*CloseIORequest) ProtoMessage() {} -func (*CloseIORequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{10} } +var xxx_messageInfo_CloseIORequest proto.InternalMessageInfo type ListPidsRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListPidsRequest) Reset() { *m = ListPidsRequest{} } +func (*ListPidsRequest) ProtoMessage() {} +func (*ListPidsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{11} +} +func (m *ListPidsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListPidsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListPidsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListPidsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListPidsRequest.Merge(m, src) +} +func (m *ListPidsRequest) XXX_Size() int { + return m.Size() +} +func (m *ListPidsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListPidsRequest.DiscardUnknown(m) } -func (m *ListPidsRequest) Reset() { *m = ListPidsRequest{} } -func (*ListPidsRequest) ProtoMessage() {} -func (*ListPidsRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{11} } +var xxx_messageInfo_ListPidsRequest proto.InternalMessageInfo type ListPidsResponse struct { - Processes []*containerd_v1_types.ProcessInfo `protobuf:"bytes,1,rep,name=processes" json:"processes,omitempty"` + Processes []*task.ProcessInfo `protobuf:"bytes,1,rep,name=processes,proto3" json:"processes,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListPidsResponse) Reset() { *m = ListPidsResponse{} } +func (*ListPidsResponse) ProtoMessage() {} +func (*ListPidsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{12} +} +func (m *ListPidsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListPidsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListPidsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListPidsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListPidsResponse.Merge(m, src) +} +func (m *ListPidsResponse) XXX_Size() int { + return m.Size() +} +func (m *ListPidsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListPidsResponse.DiscardUnknown(m) } -func (m *ListPidsResponse) Reset() { *m = ListPidsResponse{} } -func (*ListPidsResponse) ProtoMessage() {} -func (*ListPidsResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{12} } +var xxx_messageInfo_ListPidsResponse proto.InternalMessageInfo type CheckpointTaskRequest struct { - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Options *google_protobuf.Any `protobuf:"bytes,2,opt,name=options" json:"options,omitempty"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Options *types1.Any `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CheckpointTaskRequest) Reset() { *m = CheckpointTaskRequest{} } +func (*CheckpointTaskRequest) ProtoMessage() {} +func (*CheckpointTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{13} +} +func (m *CheckpointTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CheckpointTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CheckpointTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CheckpointTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CheckpointTaskRequest.Merge(m, src) +} +func (m *CheckpointTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *CheckpointTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CheckpointTaskRequest.DiscardUnknown(m) } -func (m *CheckpointTaskRequest) Reset() { *m = CheckpointTaskRequest{} } -func (*CheckpointTaskRequest) ProtoMessage() {} -func (*CheckpointTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{13} } +var xxx_messageInfo_CheckpointTaskRequest proto.InternalMessageInfo type ShimInfoResponse struct { - ShimPid uint32 `protobuf:"varint,1,opt,name=shim_pid,json=shimPid,proto3" json:"shim_pid,omitempty"` + ShimPid uint32 `protobuf:"varint,1,opt,name=shim_pid,json=shimPid,proto3" json:"shim_pid,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ShimInfoResponse) Reset() { *m = ShimInfoResponse{} } +func (*ShimInfoResponse) ProtoMessage() {} +func (*ShimInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{14} +} +func (m *ShimInfoResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ShimInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ShimInfoResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ShimInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ShimInfoResponse.Merge(m, src) +} +func (m *ShimInfoResponse) XXX_Size() int { + return m.Size() +} +func (m *ShimInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ShimInfoResponse.DiscardUnknown(m) } -func (m *ShimInfoResponse) Reset() { *m = ShimInfoResponse{} } -func (*ShimInfoResponse) ProtoMessage() {} -func (*ShimInfoResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{14} } +var xxx_messageInfo_ShimInfoResponse proto.InternalMessageInfo type UpdateTaskRequest struct { - Resources *google_protobuf.Any `protobuf:"bytes,1,opt,name=resources" json:"resources,omitempty"` + Resources *types1.Any `protobuf:"bytes,1,opt,name=resources,proto3" json:"resources,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateTaskRequest) Reset() { *m = UpdateTaskRequest{} } +func (*UpdateTaskRequest) ProtoMessage() {} +func (*UpdateTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{15} +} +func (m *UpdateTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateTaskRequest.Merge(m, src) +} +func (m *UpdateTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateTaskRequest.DiscardUnknown(m) } -func (m *UpdateTaskRequest) Reset() { *m = UpdateTaskRequest{} } -func (*UpdateTaskRequest) ProtoMessage() {} -func (*UpdateTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{15} } +var xxx_messageInfo_UpdateTaskRequest proto.InternalMessageInfo type StartRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StartRequest) Reset() { *m = StartRequest{} } +func (*StartRequest) ProtoMessage() {} +func (*StartRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{16} +} +func (m *StartRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StartRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StartRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StartRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_StartRequest.Merge(m, src) +} +func (m *StartRequest) XXX_Size() int { + return m.Size() +} +func (m *StartRequest) XXX_DiscardUnknown() { + xxx_messageInfo_StartRequest.DiscardUnknown(m) } -func (m *StartRequest) Reset() { *m = StartRequest{} } -func (*StartRequest) ProtoMessage() {} -func (*StartRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{16} } +var xxx_messageInfo_StartRequest proto.InternalMessageInfo type StartResponse struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StartResponse) Reset() { *m = StartResponse{} } +func (*StartResponse) ProtoMessage() {} +func (*StartResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{17} +} +func (m *StartResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StartResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StartResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StartResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_StartResponse.Merge(m, src) +} +func (m *StartResponse) XXX_Size() int { + return m.Size() +} +func (m *StartResponse) XXX_DiscardUnknown() { + xxx_messageInfo_StartResponse.DiscardUnknown(m) } -func (m *StartResponse) Reset() { *m = StartResponse{} } -func (*StartResponse) ProtoMessage() {} -func (*StartResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{17} } +var xxx_messageInfo_StartResponse proto.InternalMessageInfo type WaitRequest struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WaitRequest) Reset() { *m = WaitRequest{} } +func (*WaitRequest) ProtoMessage() {} +func (*WaitRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{18} +} +func (m *WaitRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WaitRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WaitRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WaitRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WaitRequest.Merge(m, src) +} +func (m *WaitRequest) XXX_Size() int { + return m.Size() +} +func (m *WaitRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WaitRequest.DiscardUnknown(m) } -func (m *WaitRequest) Reset() { *m = WaitRequest{} } -func (*WaitRequest) ProtoMessage() {} -func (*WaitRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{18} } +var xxx_messageInfo_WaitRequest proto.InternalMessageInfo type WaitResponse struct { - ExitStatus uint32 `protobuf:"varint,1,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` - ExitedAt time.Time `protobuf:"bytes,2,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"` + ExitStatus uint32 `protobuf:"varint,1,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"` + ExitedAt time.Time `protobuf:"bytes,2,opt,name=exited_at,json=exitedAt,proto3,stdtime" json:"exited_at"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WaitResponse) Reset() { *m = WaitResponse{} } +func (*WaitResponse) ProtoMessage() {} +func (*WaitResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_be1b2ef30ea3b8ef, []int{19} +} +func (m *WaitResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WaitResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WaitResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WaitResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WaitResponse.Merge(m, src) +} +func (m *WaitResponse) XXX_Size() int { + return m.Size() +} +func (m *WaitResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WaitResponse.DiscardUnknown(m) } -func (m *WaitResponse) Reset() { *m = WaitResponse{} } -func (*WaitResponse) ProtoMessage() {} -func (*WaitResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{19} } +var xxx_messageInfo_WaitResponse proto.InternalMessageInfo func init() { proto.RegisterType((*CreateTaskRequest)(nil), "containerd.runtime.linux.shim.v1.CreateTaskRequest") @@ -281,10 +867,90 @@ func init() { proto.RegisterType((*WaitRequest)(nil), "containerd.runtime.linux.shim.v1.WaitRequest") proto.RegisterType((*WaitResponse)(nil), "containerd.runtime.linux.shim.v1.WaitResponse") } + +func init() { + proto.RegisterFile("github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto", fileDescriptor_be1b2ef30ea3b8ef) +} + +var fileDescriptor_be1b2ef30ea3b8ef = []byte{ + // 1133 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x4f, 0x4f, 0x1b, 0x47, + 0x14, 0x67, 0x17, 0xff, 0x7d, 0x8e, 0x29, 0x4c, 0x09, 0xdd, 0x38, 0x92, 0xb1, 0x56, 0x6a, 0x44, + 0x55, 0x65, 0x5d, 0x4c, 0x95, 0xa4, 0xad, 0x84, 0x04, 0x24, 0xaa, 0x50, 0x1b, 0x05, 0x2d, 0xa4, + 0x89, 0x5a, 0x55, 0x68, 0xf1, 0x0e, 0xf6, 0x08, 0x7b, 0x67, 0xb3, 0x33, 0x4b, 0xa1, 0xa7, 0x9e, + 0x7a, 0xee, 0xc7, 0xe9, 0x47, 0xe0, 0x90, 0x43, 0x8f, 0x3d, 0xa5, 0x0d, 0xf7, 0x7e, 0x87, 0x6a, + 0xfe, 0x18, 0xaf, 0x6d, 0x36, 0xbb, 0x70, 0xc1, 0xfb, 0x66, 0x7e, 0x6f, 0xe6, 0xcd, 0xfb, 0xfd, + 0xe6, 0xbd, 0x01, 0x36, 0x7b, 0x84, 0xf7, 0xe3, 0x23, 0xa7, 0x4b, 0x87, 0xed, 0x2e, 0x0d, 0xb8, + 0x47, 0x02, 0x1c, 0xf9, 0xc9, 0xcf, 0x28, 0x0e, 0x38, 0x19, 0xe2, 0xf6, 0xe9, 0x7a, 0x9b, 0xf5, + 0xc9, 0x70, 0xf4, 0xeb, 0x84, 0x11, 0xe5, 0x14, 0xb5, 0xc6, 0x48, 0x47, 0x23, 0x9d, 0x01, 0x09, + 0xe2, 0x33, 0x47, 0x82, 0x4e, 0xd7, 0x1b, 0xf7, 0x7a, 0x94, 0xf6, 0x06, 0xb8, 0x2d, 0xf1, 0x47, + 0xf1, 0x71, 0xdb, 0x0b, 0xce, 0x95, 0x73, 0xe3, 0xfe, 0xf4, 0x14, 0x1e, 0x86, 0x7c, 0x34, 0xb9, + 0xdc, 0xa3, 0x3d, 0x2a, 0x3f, 0xdb, 0xe2, 0x4b, 0x8f, 0xae, 0x4e, 0xbb, 0x88, 0x1d, 0x19, 0xf7, + 0x86, 0xa1, 0x06, 0x3c, 0xca, 0x3c, 0x90, 0x17, 0x92, 0x36, 0x3f, 0x0f, 0x31, 0x6b, 0x0f, 0x69, + 0x1c, 0x70, 0xed, 0xf7, 0xf5, 0x0d, 0xfc, 0xb8, 0xc7, 0x4e, 0xe4, 0x1f, 0xe5, 0x6b, 0xff, 0x67, + 0xc2, 0xd2, 0x4e, 0x84, 0x3d, 0x8e, 0x0f, 0x3c, 0x76, 0xe2, 0xe2, 0x37, 0x31, 0x66, 0x1c, 0xad, + 0x80, 0x49, 0x7c, 0xcb, 0x68, 0x19, 0x6b, 0xd5, 0xed, 0xd2, 0xe5, 0xbb, 0x55, 0x73, 0xf7, 0xa9, + 0x6b, 0x12, 0x1f, 0xad, 0x40, 0xe9, 0x28, 0x0e, 0xfc, 0x01, 0xb6, 0x4c, 0x31, 0xe7, 0x6a, 0x0b, + 0x59, 0x50, 0xd6, 0x19, 0xb4, 0xe6, 0xe5, 0xc4, 0xc8, 0x44, 0x6d, 0x28, 0x45, 0x94, 0xf2, 0x63, + 0x66, 0x15, 0x5a, 0xf3, 0x6b, 0xb5, 0xce, 0x27, 0x4e, 0x22, 0xeb, 0x32, 0x24, 0xe7, 0xb9, 0x38, + 0x8a, 0xab, 0x61, 0xa8, 0x01, 0x15, 0x8e, 0xa3, 0x21, 0x09, 0xbc, 0x81, 0x55, 0x6c, 0x19, 0x6b, + 0x15, 0xf7, 0xca, 0x46, 0xcb, 0x50, 0x64, 0xdc, 0x27, 0x81, 0x55, 0x92, 0x9b, 0x28, 0x43, 0x04, + 0xc5, 0xb8, 0x4f, 0x63, 0x6e, 0x95, 0x55, 0x50, 0xca, 0xd2, 0xe3, 0x38, 0x8a, 0xac, 0xca, 0xd5, + 0x38, 0x8e, 0x22, 0xd4, 0x04, 0xe8, 0xf6, 0x71, 0xf7, 0x24, 0xa4, 0x24, 0xe0, 0x56, 0x55, 0xce, + 0x25, 0x46, 0xd0, 0xe7, 0xb0, 0x14, 0x7a, 0x11, 0x0e, 0xf8, 0x61, 0x02, 0x06, 0x12, 0xb6, 0xa8, + 0x26, 0x76, 0xc6, 0x60, 0x07, 0xca, 0x34, 0xe4, 0x84, 0x06, 0xcc, 0xaa, 0xb5, 0x8c, 0xb5, 0x5a, + 0x67, 0xd9, 0x51, 0x34, 0x3b, 0x23, 0x9a, 0x9d, 0xad, 0xe0, 0xdc, 0x1d, 0x81, 0xec, 0x07, 0x80, + 0x92, 0xe9, 0x66, 0x21, 0x0d, 0x18, 0x46, 0x8b, 0x30, 0x1f, 0xea, 0x84, 0xd7, 0x5d, 0xf1, 0x69, + 0xff, 0x6e, 0xc0, 0xc2, 0x53, 0x3c, 0xc0, 0x1c, 0xa7, 0x83, 0xd0, 0x2a, 0xd4, 0xf0, 0x19, 0xe1, + 0x87, 0x8c, 0x7b, 0x3c, 0x66, 0x92, 0x93, 0xba, 0x0b, 0x62, 0x68, 0x5f, 0x8e, 0xa0, 0x2d, 0xa8, + 0x0a, 0x0b, 0xfb, 0x87, 0x1e, 0x97, 0xcc, 0xd4, 0x3a, 0x8d, 0x99, 0xf8, 0x0e, 0x46, 0x32, 0xdc, + 0xae, 0x5c, 0xbc, 0x5b, 0x9d, 0xfb, 0xe3, 0x9f, 0x55, 0xc3, 0xad, 0x28, 0xb7, 0x2d, 0x6e, 0x3b, + 0xb0, 0xac, 0xe2, 0xd8, 0x8b, 0x68, 0x17, 0x33, 0x96, 0x21, 0x11, 0xfb, 0x4f, 0x03, 0xd0, 0xb3, + 0x33, 0xdc, 0xcd, 0x07, 0x9f, 0xa0, 0xdb, 0x4c, 0xa3, 0x7b, 0xfe, 0x7a, 0xba, 0x0b, 0x29, 0x74, + 0x17, 0x27, 0xe8, 0x5e, 0x83, 0x02, 0x0b, 0x71, 0x57, 0x6a, 0x26, 0x8d, 0x1e, 0x89, 0xb0, 0xef, + 0xc2, 0xc7, 0x13, 0x91, 0xab, 0xbc, 0xdb, 0xaf, 0x61, 0xd1, 0xc5, 0x8c, 0xfc, 0x8a, 0xf7, 0xf8, + 0x79, 0xd6, 0x71, 0x96, 0xa1, 0xf8, 0x0b, 0xf1, 0x79, 0x5f, 0x73, 0xa1, 0x0c, 0x11, 0x5a, 0x1f, + 0x93, 0x5e, 0x5f, 0x71, 0x50, 0x77, 0xb5, 0x65, 0x3f, 0x80, 0x3b, 0x82, 0x28, 0x9c, 0x95, 0xd3, + 0xb7, 0x26, 0xd4, 0x35, 0x50, 0x6b, 0xe1, 0xa6, 0x17, 0x54, 0x6b, 0x67, 0x7e, 0xac, 0x9d, 0x0d, + 0x91, 0x2e, 0x29, 0x1b, 0x91, 0xc6, 0x85, 0xce, 0xfd, 0xe4, 0xc5, 0x3c, 0x5d, 0xd7, 0x77, 0x53, + 0xe9, 0xc8, 0xd5, 0xd0, 0x31, 0x23, 0xc5, 0xeb, 0x19, 0x29, 0xa5, 0x30, 0x52, 0x9e, 0x60, 0x24, + 0xc9, 0x79, 0x65, 0x8a, 0xf3, 0x29, 0x49, 0x57, 0x3f, 0x2c, 0x69, 0xb8, 0x95, 0xa4, 0x5f, 0x40, + 0xed, 0x3b, 0x32, 0x18, 0xe4, 0x28, 0x76, 0x8c, 0xf4, 0x46, 0xc2, 0xac, 0xbb, 0xda, 0x12, 0xb9, + 0xf4, 0x06, 0x03, 0x99, 0xcb, 0x8a, 0x2b, 0x3e, 0xed, 0x4d, 0x58, 0xd8, 0x19, 0x50, 0x86, 0x77, + 0x5f, 0xe4, 0xd0, 0x87, 0x4a, 0xa0, 0xd2, 0xba, 0x32, 0xec, 0xcf, 0xe0, 0xa3, 0xef, 0x09, 0xe3, + 0x7b, 0xc4, 0xcf, 0xbc, 0x5e, 0x2e, 0x2c, 0x8e, 0xa1, 0x5a, 0x0c, 0x9b, 0x50, 0x0d, 0x95, 0x66, + 0x31, 0xb3, 0x0c, 0x59, 0x66, 0x5b, 0xd7, 0xb2, 0xa9, 0x95, 0xbd, 0x1b, 0x1c, 0x53, 0x77, 0xec, + 0x62, 0xff, 0x04, 0x77, 0xc7, 0x15, 0x2d, 0xd9, 0x06, 0x10, 0x14, 0x42, 0x8f, 0xf7, 0x55, 0x18, + 0xae, 0xfc, 0x4e, 0x16, 0x3c, 0x33, 0x4f, 0xc1, 0x7b, 0x08, 0x8b, 0xfb, 0x7d, 0x32, 0x94, 0x7b, + 0x8e, 0x02, 0xbe, 0x07, 0x15, 0xd1, 0x62, 0x0f, 0xc7, 0xe5, 0xac, 0x2c, 0xec, 0x3d, 0xe2, 0xdb, + 0xdf, 0xc2, 0xd2, 0xcb, 0xd0, 0x9f, 0x6a, 0x47, 0x1d, 0xa8, 0x46, 0x98, 0xd1, 0x38, 0xea, 0xca, + 0x03, 0xa6, 0xef, 0x3a, 0x86, 0xe9, 0xbb, 0x15, 0xf1, 0xac, 0x84, 0x7e, 0x25, 0xaf, 0x96, 0xc0, + 0x65, 0x5c, 0x2d, 0x7d, 0x85, 0xcc, 0x71, 0x8d, 0xfe, 0x14, 0x6a, 0xaf, 0x3c, 0x92, 0xb9, 0x43, + 0x04, 0x77, 0x14, 0x4c, 0x6f, 0x30, 0x25, 0x71, 0xe3, 0xc3, 0x12, 0x37, 0x6f, 0x23, 0xf1, 0xce, + 0xdb, 0x1a, 0x14, 0x44, 0xda, 0x51, 0x1f, 0x8a, 0xb2, 0x72, 0x20, 0xc7, 0xc9, 0x7a, 0xee, 0x38, + 0xc9, 0x5a, 0xd4, 0x68, 0xe7, 0xc6, 0xeb, 0x63, 0x31, 0x28, 0xa9, 0xce, 0x86, 0x36, 0xb2, 0x5d, + 0x67, 0x9e, 0x1c, 0x8d, 0x2f, 0x6f, 0xe6, 0xa4, 0x37, 0x55, 0xc7, 0x8b, 0x78, 0xce, 0xe3, 0x5d, + 0xc9, 0x21, 0xe7, 0xf1, 0x12, 0xb2, 0x70, 0xa1, 0xa4, 0xfa, 0x20, 0x5a, 0x99, 0xe1, 0xe2, 0x99, + 0x78, 0xfb, 0x35, 0xbe, 0xc8, 0x5e, 0x72, 0xaa, 0xa3, 0x9f, 0x43, 0x7d, 0xa2, 0xb7, 0xa2, 0x47, + 0x79, 0x97, 0x98, 0xec, 0xae, 0xb7, 0xd8, 0xfa, 0x0d, 0x54, 0x46, 0x75, 0x04, 0xad, 0x67, 0x7b, + 0x4f, 0x95, 0xa7, 0x46, 0xe7, 0x26, 0x2e, 0x7a, 0xcb, 0xc7, 0x50, 0xdc, 0xf3, 0x62, 0x96, 0x9e, + 0xc0, 0x94, 0x71, 0xf4, 0x04, 0x4a, 0x2e, 0x66, 0xf1, 0xf0, 0xe6, 0x9e, 0x3f, 0x03, 0x24, 0xde, + 0x6a, 0x8f, 0x73, 0x48, 0xec, 0xba, 0x3a, 0x98, 0xba, 0xfc, 0x73, 0x28, 0x88, 0x46, 0x82, 0x1e, + 0x66, 0x2f, 0x9c, 0x68, 0x38, 0xa9, 0xcb, 0x1d, 0x40, 0x41, 0xbc, 0x3f, 0x50, 0x8e, 0xab, 0x30, + 0xfb, 0xc2, 0x4a, 0x5d, 0xf5, 0x15, 0x54, 0xaf, 0x9e, 0x2f, 0x28, 0x07, 0x6f, 0xd3, 0x6f, 0x9d, + 0xd4, 0x85, 0xf7, 0xa1, 0xac, 0xbb, 0x1e, 0xca, 0xa1, 0xbf, 0xc9, 0x06, 0x99, 0xba, 0xe8, 0x0f, + 0x50, 0x19, 0xb5, 0x8b, 0x54, 0xb6, 0x73, 0x1c, 0x62, 0xa6, 0xe5, 0xbc, 0x84, 0x92, 0xea, 0x2b, + 0x79, 0xaa, 0xd3, 0x4c, 0x07, 0x4a, 0x0d, 0x17, 0x43, 0x41, 0xd4, 0xf6, 0x3c, 0x0a, 0x48, 0xb4, + 0x8a, 0x86, 0x93, 0x17, 0xae, 0xa2, 0xdf, 0x76, 0x2f, 0xde, 0x37, 0xe7, 0xfe, 0x7e, 0xdf, 0x9c, + 0xfb, 0xed, 0xb2, 0x69, 0x5c, 0x5c, 0x36, 0x8d, 0xbf, 0x2e, 0x9b, 0xc6, 0xbf, 0x97, 0x4d, 0xe3, + 0xc7, 0x27, 0xb7, 0xf8, 0x27, 0xf8, 0x1b, 0xf1, 0xfb, 0xda, 0x3c, 0x2a, 0xc9, 0xc3, 0x6c, 0xfc, + 0x1f, 0x00, 0x00, 0xff, 0xff, 0x64, 0x52, 0x86, 0xc0, 0x49, 0x0f, 0x00, 0x00, +} + func (m *CreateTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -292,120 +958,150 @@ func (m *CreateTaskRequest) Marshal() (dAtA []byte, err error) { } func (m *CreateTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Bundle) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Bundle))) - i += copy(dAtA[i:], m.Bundle) - } - if len(m.Runtime) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Runtime))) - i += copy(dAtA[i:], m.Runtime) - } - if len(m.Rootfs) > 0 { - for _, msg := range m.Rootfs { - dAtA[i] = 0x22 - i++ - i = encodeVarintShim(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Options != nil { + { + size, err := m.Options.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintShim(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x5a + } + if len(m.ParentCheckpoint) > 0 { + i -= len(m.ParentCheckpoint) + copy(dAtA[i:], m.ParentCheckpoint) + i = encodeVarintShim(dAtA, i, uint64(len(m.ParentCheckpoint))) + i-- + dAtA[i] = 0x52 + } + if len(m.Checkpoint) > 0 { + i -= len(m.Checkpoint) + copy(dAtA[i:], m.Checkpoint) + i = encodeVarintShim(dAtA, i, uint64(len(m.Checkpoint))) + i-- + dAtA[i] = 0x4a + } + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = encodeVarintShim(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x42 + } + if len(m.Stdout) > 0 { + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdout))) + i-- + dAtA[i] = 0x3a + } + if len(m.Stdin) > 0 { + i -= len(m.Stdin) + copy(dAtA[i:], m.Stdin) + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdin))) + i-- + dAtA[i] = 0x32 } if m.Terminal { - dAtA[i] = 0x28 - i++ + i-- if m.Terminal { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ - } - if len(m.Stdin) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Stdin))) - i += copy(dAtA[i:], m.Stdin) - } - if len(m.Stdout) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Stdout))) - i += copy(dAtA[i:], m.Stdout) + i-- + dAtA[i] = 0x28 } - if len(m.Stderr) > 0 { - dAtA[i] = 0x42 - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Stderr))) - i += copy(dAtA[i:], m.Stderr) + if len(m.Rootfs) > 0 { + for iNdEx := len(m.Rootfs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Rootfs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintShim(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } } - if len(m.Checkpoint) > 0 { - dAtA[i] = 0x4a - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Checkpoint))) - i += copy(dAtA[i:], m.Checkpoint) + if len(m.Runtime) > 0 { + i -= len(m.Runtime) + copy(dAtA[i:], m.Runtime) + i = encodeVarintShim(dAtA, i, uint64(len(m.Runtime))) + i-- + dAtA[i] = 0x1a } - if len(m.ParentCheckpoint) > 0 { - dAtA[i] = 0x52 - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.ParentCheckpoint))) - i += copy(dAtA[i:], m.ParentCheckpoint) + if len(m.Bundle) > 0 { + i -= len(m.Bundle) + copy(dAtA[i:], m.Bundle) + i = encodeVarintShim(dAtA, i, uint64(len(m.Bundle))) + i-- + dAtA[i] = 0x12 } - if m.Options != nil { - dAtA[i] = 0x5a - i++ - i = encodeVarintShim(dAtA, i, uint64(m.Options.Size())) - n1, err := m.Options.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *CreateTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *CreateTaskResponse) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *CreateTaskResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Pid != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintShim(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *DeleteResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -413,35 +1109,44 @@ func (m *DeleteResponse) Marshal() (dAtA []byte, err error) { } func (m *DeleteResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Pid != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintShim(dAtA, i, uint64(m.Pid)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExitedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintShim(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x1a if m.ExitStatus != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintShim(dAtA, i, uint64(m.ExitStatus)) + i-- + dAtA[i] = 0x10 } - dAtA[i] = 0x1a - i++ - i = encodeVarintShim(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) - n2, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) - if err != nil { - return 0, err + if m.Pid != 0 { + i = encodeVarintShim(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x8 } - i += n2 - return i, nil + return len(dAtA) - i, nil } func (m *DeleteProcessRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -449,23 +1154,33 @@ func (m *DeleteProcessRequest) Marshal() (dAtA []byte, err error) { } func (m *DeleteProcessRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DeleteProcessRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ID) + copy(dAtA[i:], m.ID) i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ExecProcessRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -473,61 +1188,76 @@ func (m *ExecProcessRequest) Marshal() (dAtA []byte, err error) { } func (m *ExecProcessRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExecProcessRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if m.Terminal { - dAtA[i] = 0x10 - i++ - if m.Terminal { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintShim(dAtA, i, uint64(size)) } - i++ + i-- + dAtA[i] = 0x32 } - if len(m.Stdin) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Stdin))) - i += copy(dAtA[i:], m.Stdin) + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = encodeVarintShim(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x2a } if len(m.Stdout) > 0 { - dAtA[i] = 0x22 - i++ + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) i = encodeVarintShim(dAtA, i, uint64(len(m.Stdout))) - i += copy(dAtA[i:], m.Stdout) + i-- + dAtA[i] = 0x22 } - if len(m.Stderr) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Stderr))) - i += copy(dAtA[i:], m.Stderr) + if len(m.Stdin) > 0 { + i -= len(m.Stdin) + copy(dAtA[i:], m.Stdin) + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdin))) + i-- + dAtA[i] = 0x1a } - if m.Spec != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintShim(dAtA, i, uint64(m.Spec.Size())) - n3, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Terminal { + i-- + if m.Terminal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } - i += n3 + i-- + dAtA[i] = 0x10 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ExecProcessResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -535,17 +1265,26 @@ func (m *ExecProcessResponse) Marshal() (dAtA []byte, err error) { } func (m *ExecProcessResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExecProcessResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + return len(dAtA) - i, nil } func (m *ResizePtyRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -553,33 +1292,43 @@ func (m *ResizePtyRequest) Marshal() (dAtA []byte, err error) { } func (m *ResizePtyRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResizePtyRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Height != 0 { + i = encodeVarintShim(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x18 } if m.Width != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintShim(dAtA, i, uint64(m.Width)) + i-- + dAtA[i] = 0x10 } - if m.Height != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintShim(dAtA, i, uint64(m.Height)) + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *StateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -587,23 +1336,33 @@ func (m *StateRequest) Marshal() (dAtA []byte, err error) { } func (m *StateRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ID) + copy(dAtA[i:], m.ID) i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *StateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -611,80 +1370,94 @@ func (m *StateResponse) Marshal() (dAtA []byte, err error) { } func (m *StateResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - } - if len(m.Bundle) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Bundle))) - i += copy(dAtA[i:], m.Bundle) - } - if m.Pid != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintShim(dAtA, i, uint64(m.Pid)) - } - if m.Status != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintShim(dAtA, i, uint64(m.Status)) - } - if len(m.Stdin) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Stdin))) - i += copy(dAtA[i:], m.Stdin) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Stdout) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Stdout))) - i += copy(dAtA[i:], m.Stdout) + n4, err4 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExitedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt):]) + if err4 != nil { + return 0, err4 } - if len(m.Stderr) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Stderr))) - i += copy(dAtA[i:], m.Stderr) + i -= n4 + i = encodeVarintShim(dAtA, i, uint64(n4)) + i-- + dAtA[i] = 0x52 + if m.ExitStatus != 0 { + i = encodeVarintShim(dAtA, i, uint64(m.ExitStatus)) + i-- + dAtA[i] = 0x48 } if m.Terminal { - dAtA[i] = 0x40 - i++ + i-- if m.Terminal { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x40 } - if m.ExitStatus != 0 { - dAtA[i] = 0x48 - i++ - i = encodeVarintShim(dAtA, i, uint64(m.ExitStatus)) + if len(m.Stderr) > 0 { + i -= len(m.Stderr) + copy(dAtA[i:], m.Stderr) + i = encodeVarintShim(dAtA, i, uint64(len(m.Stderr))) + i-- + dAtA[i] = 0x3a } - dAtA[i] = 0x52 - i++ - i = encodeVarintShim(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) - n4, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) - if err != nil { - return 0, err + if len(m.Stdout) > 0 { + i -= len(m.Stdout) + copy(dAtA[i:], m.Stdout) + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdout))) + i-- + dAtA[i] = 0x32 + } + if len(m.Stdin) > 0 { + i -= len(m.Stdin) + copy(dAtA[i:], m.Stdin) + i = encodeVarintShim(dAtA, i, uint64(len(m.Stdin))) + i-- + dAtA[i] = 0x2a + } + if m.Status != 0 { + i = encodeVarintShim(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x20 + } + if m.Pid != 0 { + i = encodeVarintShim(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x18 + } + if len(m.Bundle) > 0 { + i -= len(m.Bundle) + copy(dAtA[i:], m.Bundle) + i = encodeVarintShim(dAtA, i, uint64(len(m.Bundle))) + i-- + dAtA[i] = 0x12 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - i += n4 - return i, nil + return len(dAtA) - i, nil } func (m *KillRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -692,38 +1465,48 @@ func (m *KillRequest) Marshal() (dAtA []byte, err error) { } func (m *KillRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KillRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - } - if m.Signal != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintShim(dAtA, i, uint64(m.Signal)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.All { - dAtA[i] = 0x18 - i++ + i-- if m.All { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x18 + } + if m.Signal != 0 { + i = encodeVarintShim(dAtA, i, uint64(m.Signal)) + i-- + dAtA[i] = 0x10 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *CloseIORequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -731,33 +1514,43 @@ func (m *CloseIORequest) Marshal() (dAtA []byte, err error) { } func (m *CloseIORequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CloseIORequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Stdin { - dAtA[i] = 0x10 - i++ + i-- if m.Stdin { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x10 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListPidsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -765,23 +1558,33 @@ func (m *ListPidsRequest) Marshal() (dAtA []byte, err error) { } func (m *ListPidsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListPidsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ID) + copy(dAtA[i:], m.ID) i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ListPidsResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -789,29 +1592,40 @@ func (m *ListPidsResponse) Marshal() (dAtA []byte, err error) { } func (m *ListPidsResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListPidsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Processes) > 0 { - for _, msg := range m.Processes { - dAtA[i] = 0xa - i++ - i = encodeVarintShim(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Processes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Processes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintShim(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *CheckpointTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -819,33 +1633,45 @@ func (m *CheckpointTaskRequest) Marshal() (dAtA []byte, err error) { } func (m *CheckpointTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CheckpointTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Path) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.Path))) - i += copy(dAtA[i:], m.Path) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Options != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintShim(dAtA, i, uint64(m.Options.Size())) - n5, err := m.Options.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Options.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintShim(dAtA, i, uint64(size)) } - i += n5 + i-- + dAtA[i] = 0x12 + } + if len(m.Path) > 0 { + i -= len(m.Path) + copy(dAtA[i:], m.Path) + i = encodeVarintShim(dAtA, i, uint64(len(m.Path))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ShimInfoResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -853,22 +1679,31 @@ func (m *ShimInfoResponse) Marshal() (dAtA []byte, err error) { } func (m *ShimInfoResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ShimInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.ShimPid != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintShim(dAtA, i, uint64(m.ShimPid)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *UpdateTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -876,27 +1711,38 @@ func (m *UpdateTaskRequest) Marshal() (dAtA []byte, err error) { } func (m *UpdateTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Resources != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintShim(dAtA, i, uint64(m.Resources.Size())) - n6, err := m.Resources.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Resources.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintShim(dAtA, i, uint64(size)) } - i += n6 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *StartRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -904,23 +1750,33 @@ func (m *StartRequest) Marshal() (dAtA []byte, err error) { } func (m *StartRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StartRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ID) + copy(dAtA[i:], m.ID) i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *StartResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -928,28 +1784,38 @@ func (m *StartResponse) Marshal() (dAtA []byte, err error) { } func (m *StartResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StartResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int - _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Pid != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintShim(dAtA, i, uint64(m.Pid)) + i-- + dAtA[i] = 0x10 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *WaitRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -957,23 +1823,33 @@ func (m *WaitRequest) Marshal() (dAtA []byte, err error) { } func (m *WaitRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WaitRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ID) + copy(dAtA[i:], m.ID) i = encodeVarintShim(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *WaitResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -981,36 +1857,50 @@ func (m *WaitResponse) Marshal() (dAtA []byte, err error) { } func (m *WaitResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WaitResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.ExitStatus != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintShim(dAtA, i, uint64(m.ExitStatus)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExitedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt):]) + if err7 != nil { + return 0, err7 } + i -= n7 + i = encodeVarintShim(dAtA, i, uint64(n7)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintShim(dAtA, i, uint64(types.SizeOfStdTime(m.ExitedAt))) - n7, err := types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:]) - if err != nil { - return 0, err + if m.ExitStatus != 0 { + i = encodeVarintShim(dAtA, i, uint64(m.ExitStatus)) + i-- + dAtA[i] = 0x8 } - i += n7 - return i, nil + return len(dAtA) - i, nil } func encodeVarintShim(dAtA []byte, offset int, v uint64) int { + offset -= sovShim(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *CreateTaskRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1058,19 +1948,31 @@ func (m *CreateTaskRequest) Size() (n int) { l = m.Options.Size() n += 1 + l + sovShim(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CreateTaskResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Pid != 0 { n += 1 + sovShim(uint64(m.Pid)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DeleteResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Pid != 0 { @@ -1079,22 +1981,34 @@ func (m *DeleteResponse) Size() (n int) { if m.ExitStatus != 0 { n += 1 + sovShim(uint64(m.ExitStatus)) } - l = types.SizeOfStdTime(m.ExitedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt) n += 1 + l + sovShim(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DeleteProcessRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) if l > 0 { n += 1 + l + sovShim(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ExecProcessRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1120,16 +2034,28 @@ func (m *ExecProcessRequest) Size() (n int) { l = m.Spec.Size() n += 1 + l + sovShim(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ExecProcessResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ResizePtyRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1142,20 +2068,32 @@ func (m *ResizePtyRequest) Size() (n int) { if m.Height != 0 { n += 1 + sovShim(uint64(m.Height)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StateRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) if l > 0 { n += 1 + l + sovShim(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StateResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1190,12 +2128,18 @@ func (m *StateResponse) Size() (n int) { if m.ExitStatus != 0 { n += 1 + sovShim(uint64(m.ExitStatus)) } - l = types.SizeOfStdTime(m.ExitedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt) n += 1 + l + sovShim(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *KillRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1208,10 +2152,16 @@ func (m *KillRequest) Size() (n int) { if m.All { n += 2 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CloseIORequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1221,20 +2171,32 @@ func (m *CloseIORequest) Size() (n int) { if m.Stdin { n += 2 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListPidsRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) if l > 0 { n += 1 + l + sovShim(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ListPidsResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Processes) > 0 { @@ -1243,10 +2205,16 @@ func (m *ListPidsResponse) Size() (n int) { n += 1 + l + sovShim(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *CheckpointTaskRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Path) @@ -1257,39 +2225,63 @@ func (m *CheckpointTaskRequest) Size() (n int) { l = m.Options.Size() n += 1 + l + sovShim(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ShimInfoResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.ShimPid != 0 { n += 1 + sovShim(uint64(m.ShimPid)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UpdateTaskRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Resources != nil { l = m.Resources.Size() n += 1 + l + sovShim(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StartRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) if l > 0 { n += 1 + l + sovShim(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StartResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1299,39 +2291,47 @@ func (m *StartResponse) Size() (n int) { if m.Pid != 0 { n += 1 + sovShim(uint64(m.Pid)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *WaitRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) if l > 0 { n += 1 + l + sovShim(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *WaitResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.ExitStatus != 0 { n += 1 + sovShim(uint64(m.ExitStatus)) } - l = types.SizeOfStdTime(m.ExitedAt) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt) n += 1 + l + sovShim(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovShim(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozShim(x uint64) (n int) { return sovShim(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -1340,18 +2340,24 @@ func (this *CreateTaskRequest) String() string { if this == nil { return "nil" } + repeatedStringForRootfs := "[]*Mount{" + for _, f := range this.Rootfs { + repeatedStringForRootfs += strings.Replace(fmt.Sprintf("%v", f), "Mount", "types.Mount", 1) + "," + } + repeatedStringForRootfs += "}" s := strings.Join([]string{`&CreateTaskRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Bundle:` + fmt.Sprintf("%v", this.Bundle) + `,`, `Runtime:` + fmt.Sprintf("%v", this.Runtime) + `,`, - `Rootfs:` + strings.Replace(fmt.Sprintf("%v", this.Rootfs), "Mount", "containerd_types.Mount", 1) + `,`, + `Rootfs:` + repeatedStringForRootfs + `,`, `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, `Checkpoint:` + fmt.Sprintf("%v", this.Checkpoint) + `,`, `ParentCheckpoint:` + fmt.Sprintf("%v", this.ParentCheckpoint) + `,`, - `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "google_protobuf.Any", 1) + `,`, + `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "types1.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1362,6 +2368,7 @@ func (this *CreateTaskResponse) String() string { } s := strings.Join([]string{`&CreateTaskResponse{`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1373,7 +2380,8 @@ func (this *DeleteResponse) String() string { s := strings.Join([]string{`&DeleteResponse{`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, - `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ExitedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1384,6 +2392,7 @@ func (this *DeleteProcessRequest) String() string { } s := strings.Join([]string{`&DeleteProcessRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1398,7 +2407,8 @@ func (this *ExecProcessRequest) String() string { `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "Any", "google_protobuf.Any", 1) + `,`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "Any", "types1.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1408,6 +2418,7 @@ func (this *ExecProcessResponse) String() string { return "nil" } s := strings.Join([]string{`&ExecProcessResponse{`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1420,6 +2431,7 @@ func (this *ResizePtyRequest) String() string { `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Width:` + fmt.Sprintf("%v", this.Width) + `,`, `Height:` + fmt.Sprintf("%v", this.Height) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1430,6 +2442,7 @@ func (this *StateRequest) String() string { } s := strings.Join([]string{`&StateRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1448,7 +2461,8 @@ func (this *StateResponse) String() string { `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, - `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ExitedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1461,6 +2475,7 @@ func (this *KillRequest) String() string { `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Signal:` + fmt.Sprintf("%v", this.Signal) + `,`, `All:` + fmt.Sprintf("%v", this.All) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1472,6 +2487,7 @@ func (this *CloseIORequest) String() string { s := strings.Join([]string{`&CloseIORequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1482,6 +2498,7 @@ func (this *ListPidsRequest) String() string { } s := strings.Join([]string{`&ListPidsRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1490,8 +2507,14 @@ func (this *ListPidsResponse) String() string { if this == nil { return "nil" } + repeatedStringForProcesses := "[]*ProcessInfo{" + for _, f := range this.Processes { + repeatedStringForProcesses += strings.Replace(fmt.Sprintf("%v", f), "ProcessInfo", "task.ProcessInfo", 1) + "," + } + repeatedStringForProcesses += "}" s := strings.Join([]string{`&ListPidsResponse{`, - `Processes:` + strings.Replace(fmt.Sprintf("%v", this.Processes), "ProcessInfo", "containerd_v1_types.ProcessInfo", 1) + `,`, + `Processes:` + repeatedStringForProcesses + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1502,7 +2525,8 @@ func (this *CheckpointTaskRequest) String() string { } s := strings.Join([]string{`&CheckpointTaskRequest{`, `Path:` + fmt.Sprintf("%v", this.Path) + `,`, - `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "google_protobuf.Any", 1) + `,`, + `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "Any", "types1.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1513,6 +2537,7 @@ func (this *ShimInfoResponse) String() string { } s := strings.Join([]string{`&ShimInfoResponse{`, `ShimPid:` + fmt.Sprintf("%v", this.ShimPid) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1522,7 +2547,8 @@ func (this *UpdateTaskRequest) String() string { return "nil" } s := strings.Join([]string{`&UpdateTaskRequest{`, - `Resources:` + strings.Replace(fmt.Sprintf("%v", this.Resources), "Any", "google_protobuf.Any", 1) + `,`, + `Resources:` + strings.Replace(fmt.Sprintf("%v", this.Resources), "Any", "types1.Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1533,6 +2559,7 @@ func (this *StartRequest) String() string { } s := strings.Join([]string{`&StartRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1544,6 +2571,7 @@ func (this *StartResponse) String() string { s := strings.Join([]string{`&StartResponse{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Pid:` + fmt.Sprintf("%v", this.Pid) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1554,6 +2582,7 @@ func (this *WaitRequest) String() string { } s := strings.Join([]string{`&WaitRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1564,7 +2593,8 @@ func (this *WaitResponse) String() string { } s := strings.Join([]string{`&WaitResponse{`, `ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`, - `ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`, + `ExitedAt:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ExitedAt), "Timestamp", "types1.Timestamp", 1), `&`, ``, 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1582,23 +2612,23 @@ type ShimService interface { State(ctx context.Context, req *StateRequest) (*StateResponse, error) Create(ctx context.Context, req *CreateTaskRequest) (*CreateTaskResponse, error) Start(ctx context.Context, req *StartRequest) (*StartResponse, error) - Delete(ctx context.Context, req *google_protobuf1.Empty) (*DeleteResponse, error) + Delete(ctx context.Context, req *types1.Empty) (*DeleteResponse, error) DeleteProcess(ctx context.Context, req *DeleteProcessRequest) (*DeleteResponse, error) ListPids(ctx context.Context, req *ListPidsRequest) (*ListPidsResponse, error) - Pause(ctx context.Context, req *google_protobuf1.Empty) (*google_protobuf1.Empty, error) - Resume(ctx context.Context, req *google_protobuf1.Empty) (*google_protobuf1.Empty, error) - Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*google_protobuf1.Empty, error) - Kill(ctx context.Context, req *KillRequest) (*google_protobuf1.Empty, error) - Exec(ctx context.Context, req *ExecProcessRequest) (*google_protobuf1.Empty, error) - ResizePty(ctx context.Context, req *ResizePtyRequest) (*google_protobuf1.Empty, error) - CloseIO(ctx context.Context, req *CloseIORequest) (*google_protobuf1.Empty, error) - ShimInfo(ctx context.Context, req *google_protobuf1.Empty) (*ShimInfoResponse, error) - Update(ctx context.Context, req *UpdateTaskRequest) (*google_protobuf1.Empty, error) + Pause(ctx context.Context, req *types1.Empty) (*types1.Empty, error) + Resume(ctx context.Context, req *types1.Empty) (*types1.Empty, error) + Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*types1.Empty, error) + Kill(ctx context.Context, req *KillRequest) (*types1.Empty, error) + Exec(ctx context.Context, req *ExecProcessRequest) (*types1.Empty, error) + ResizePty(ctx context.Context, req *ResizePtyRequest) (*types1.Empty, error) + CloseIO(ctx context.Context, req *CloseIORequest) (*types1.Empty, error) + ShimInfo(ctx context.Context, req *types1.Empty) (*ShimInfoResponse, error) + Update(ctx context.Context, req *UpdateTaskRequest) (*types1.Empty, error) Wait(ctx context.Context, req *WaitRequest) (*WaitResponse, error) } -func RegisterShimService(srv *ttrpc.Server, svc ShimService) { - srv.Register("containerd.runtime.linux.shim.v1.Shim", map[string]ttrpc.Method{ +func RegisterShimService(srv *github_com_containerd_ttrpc.Server, svc ShimService) { + srv.Register("containerd.runtime.linux.shim.v1.Shim", map[string]github_com_containerd_ttrpc.Method{ "State": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { var req StateRequest if err := unmarshal(&req); err != nil { @@ -1621,7 +2651,7 @@ func RegisterShimService(srv *ttrpc.Server, svc ShimService) { return svc.Start(ctx, &req) }, "Delete": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { - var req google_protobuf1.Empty + var req types1.Empty if err := unmarshal(&req); err != nil { return nil, err } @@ -1642,14 +2672,14 @@ func RegisterShimService(srv *ttrpc.Server, svc ShimService) { return svc.ListPids(ctx, &req) }, "Pause": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { - var req google_protobuf1.Empty + var req types1.Empty if err := unmarshal(&req); err != nil { return nil, err } return svc.Pause(ctx, &req) }, "Resume": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { - var req google_protobuf1.Empty + var req types1.Empty if err := unmarshal(&req); err != nil { return nil, err } @@ -1691,7 +2721,7 @@ func RegisterShimService(srv *ttrpc.Server, svc ShimService) { return svc.CloseIO(ctx, &req) }, "ShimInfo": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) { - var req google_protobuf1.Empty + var req types1.Empty if err := unmarshal(&req); err != nil { return nil, err } @@ -1715,10 +2745,10 @@ func RegisterShimService(srv *ttrpc.Server, svc ShimService) { } type shimClient struct { - client *ttrpc.Client + client *github_com_containerd_ttrpc.Client } -func NewShimClient(client *ttrpc.Client) ShimService { +func NewShimClient(client *github_com_containerd_ttrpc.Client) ShimService { return &shimClient{ client: client, } @@ -1748,7 +2778,7 @@ func (c *shimClient) Start(ctx context.Context, req *StartRequest) (*StartRespon return &resp, nil } -func (c *shimClient) Delete(ctx context.Context, req *google_protobuf1.Empty) (*DeleteResponse, error) { +func (c *shimClient) Delete(ctx context.Context, req *types1.Empty) (*DeleteResponse, error) { var resp DeleteResponse if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Delete", req, &resp); err != nil { return nil, err @@ -1772,63 +2802,63 @@ func (c *shimClient) ListPids(ctx context.Context, req *ListPidsRequest) (*ListP return &resp, nil } -func (c *shimClient) Pause(ctx context.Context, req *google_protobuf1.Empty) (*google_protobuf1.Empty, error) { - var resp google_protobuf1.Empty +func (c *shimClient) Pause(ctx context.Context, req *types1.Empty) (*types1.Empty, error) { + var resp types1.Empty if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Pause", req, &resp); err != nil { return nil, err } return &resp, nil } -func (c *shimClient) Resume(ctx context.Context, req *google_protobuf1.Empty) (*google_protobuf1.Empty, error) { - var resp google_protobuf1.Empty +func (c *shimClient) Resume(ctx context.Context, req *types1.Empty) (*types1.Empty, error) { + var resp types1.Empty if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Resume", req, &resp); err != nil { return nil, err } return &resp, nil } -func (c *shimClient) Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*google_protobuf1.Empty, error) { - var resp google_protobuf1.Empty +func (c *shimClient) Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*types1.Empty, error) { + var resp types1.Empty if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Checkpoint", req, &resp); err != nil { return nil, err } return &resp, nil } -func (c *shimClient) Kill(ctx context.Context, req *KillRequest) (*google_protobuf1.Empty, error) { - var resp google_protobuf1.Empty +func (c *shimClient) Kill(ctx context.Context, req *KillRequest) (*types1.Empty, error) { + var resp types1.Empty if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Kill", req, &resp); err != nil { return nil, err } return &resp, nil } -func (c *shimClient) Exec(ctx context.Context, req *ExecProcessRequest) (*google_protobuf1.Empty, error) { - var resp google_protobuf1.Empty +func (c *shimClient) Exec(ctx context.Context, req *ExecProcessRequest) (*types1.Empty, error) { + var resp types1.Empty if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Exec", req, &resp); err != nil { return nil, err } return &resp, nil } -func (c *shimClient) ResizePty(ctx context.Context, req *ResizePtyRequest) (*google_protobuf1.Empty, error) { - var resp google_protobuf1.Empty +func (c *shimClient) ResizePty(ctx context.Context, req *ResizePtyRequest) (*types1.Empty, error) { + var resp types1.Empty if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "ResizePty", req, &resp); err != nil { return nil, err } return &resp, nil } -func (c *shimClient) CloseIO(ctx context.Context, req *CloseIORequest) (*google_protobuf1.Empty, error) { - var resp google_protobuf1.Empty +func (c *shimClient) CloseIO(ctx context.Context, req *CloseIORequest) (*types1.Empty, error) { + var resp types1.Empty if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "CloseIO", req, &resp); err != nil { return nil, err } return &resp, nil } -func (c *shimClient) ShimInfo(ctx context.Context, req *google_protobuf1.Empty) (*ShimInfoResponse, error) { +func (c *shimClient) ShimInfo(ctx context.Context, req *types1.Empty) (*ShimInfoResponse, error) { var resp ShimInfoResponse if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "ShimInfo", req, &resp); err != nil { return nil, err @@ -1836,8 +2866,8 @@ func (c *shimClient) ShimInfo(ctx context.Context, req *google_protobuf1.Empty) return &resp, nil } -func (c *shimClient) Update(ctx context.Context, req *UpdateTaskRequest) (*google_protobuf1.Empty, error) { - var resp google_protobuf1.Empty +func (c *shimClient) Update(ctx context.Context, req *UpdateTaskRequest) (*types1.Empty, error) { + var resp types1.Empty if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Update", req, &resp); err != nil { return nil, err } @@ -1866,7 +2896,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1894,7 +2924,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1904,6 +2934,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1923,7 +2956,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1933,6 +2966,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1952,7 +2988,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1962,6 +2998,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1981,7 +3020,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1990,10 +3029,13 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Rootfs = append(m.Rootfs, &containerd_types.Mount{}) + m.Rootfs = append(m.Rootfs, &types.Mount{}) if err := m.Rootfs[len(m.Rootfs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -2012,7 +3054,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2032,7 +3074,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2042,6 +3084,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2061,7 +3106,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2071,6 +3116,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2090,7 +3138,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2100,6 +3148,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2119,7 +3170,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2129,6 +3180,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2148,7 +3202,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2158,6 +3212,9 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2177,7 +3234,7 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2186,11 +3243,14 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Options == nil { - m.Options = &google_protobuf.Any{} + m.Options = &types1.Any{} } if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -2202,12 +3262,13 @@ func (m *CreateTaskRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2232,7 +3293,7 @@ func (m *CreateTaskResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2260,7 +3321,7 @@ func (m *CreateTaskResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -2271,12 +3332,13 @@ func (m *CreateTaskResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2301,7 +3363,7 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2329,7 +3391,7 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -2348,7 +3410,7 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ExitStatus |= (uint32(b) & 0x7F) << shift + m.ExitStatus |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -2367,7 +3429,7 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2376,10 +3438,13 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -2389,12 +3454,13 @@ func (m *DeleteResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2419,7 +3485,7 @@ func (m *DeleteProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2447,7 +3513,7 @@ func (m *DeleteProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2457,6 +3523,9 @@ func (m *DeleteProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2468,12 +3537,13 @@ func (m *DeleteProcessRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2498,7 +3568,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2526,7 +3596,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2536,6 +3606,9 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2555,7 +3628,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2575,7 +3648,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2585,6 +3658,9 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2604,7 +3680,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2614,6 +3690,9 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2633,7 +3712,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2643,6 +3722,9 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2662,7 +3744,7 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2671,11 +3753,14 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Spec == nil { - m.Spec = &google_protobuf.Any{} + m.Spec = &types1.Any{} } if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -2687,12 +3772,13 @@ func (m *ExecProcessRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2717,7 +3803,7 @@ func (m *ExecProcessResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2737,12 +3823,13 @@ func (m *ExecProcessResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2767,7 +3854,7 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2795,7 +3882,7 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2805,6 +3892,9 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2824,7 +3914,7 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Width |= (uint32(b) & 0x7F) << shift + m.Width |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -2843,7 +3933,7 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Height |= (uint32(b) & 0x7F) << shift + m.Height |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -2854,12 +3944,13 @@ func (m *ResizePtyRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2884,7 +3975,7 @@ func (m *StateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2912,7 +4003,7 @@ func (m *StateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2922,6 +4013,9 @@ func (m *StateRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2933,12 +4027,13 @@ func (m *StateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2963,7 +4058,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2991,7 +4086,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3001,6 +4096,9 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3020,7 +4118,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3030,6 +4128,9 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3049,7 +4150,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -3068,7 +4169,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Status |= (containerd_v1_types.Status(b) & 0x7F) << shift + m.Status |= task.Status(b&0x7F) << shift if b < 0x80 { break } @@ -3087,7 +4188,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3097,6 +4198,9 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3116,7 +4220,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3126,6 +4230,9 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3145,7 +4252,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3155,6 +4262,9 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3174,7 +4284,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3194,7 +4304,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ExitStatus |= (uint32(b) & 0x7F) << shift + m.ExitStatus |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -3213,7 +4323,7 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3222,10 +4332,13 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -3235,12 +4348,13 @@ func (m *StateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3265,7 +4379,7 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3293,7 +4407,7 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3303,6 +4417,9 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3322,7 +4439,7 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Signal |= (uint32(b) & 0x7F) << shift + m.Signal |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -3341,7 +4458,7 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3353,12 +4470,13 @@ func (m *KillRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3383,7 +4501,7 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3411,7 +4529,7 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3421,6 +4539,9 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3440,7 +4561,7 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3452,12 +4573,13 @@ func (m *CloseIORequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3482,7 +4604,7 @@ func (m *ListPidsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3510,7 +4632,7 @@ func (m *ListPidsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3520,6 +4642,9 @@ func (m *ListPidsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3531,12 +4656,13 @@ func (m *ListPidsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3561,7 +4687,7 @@ func (m *ListPidsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3589,7 +4715,7 @@ func (m *ListPidsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3598,10 +4724,13 @@ func (m *ListPidsResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Processes = append(m.Processes, &containerd_v1_types.ProcessInfo{}) + m.Processes = append(m.Processes, &task.ProcessInfo{}) if err := m.Processes[len(m.Processes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -3612,12 +4741,13 @@ func (m *ListPidsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3642,7 +4772,7 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3670,7 +4800,7 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3680,6 +4810,9 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3699,7 +4832,7 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3708,11 +4841,14 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Options == nil { - m.Options = &google_protobuf.Any{} + m.Options = &types1.Any{} } if err := m.Options.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -3724,12 +4860,13 @@ func (m *CheckpointTaskRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3754,7 +4891,7 @@ func (m *ShimInfoResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3782,7 +4919,7 @@ func (m *ShimInfoResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ShimPid |= (uint32(b) & 0x7F) << shift + m.ShimPid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -3793,12 +4930,13 @@ func (m *ShimInfoResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3823,7 +4961,7 @@ func (m *UpdateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3851,7 +4989,7 @@ func (m *UpdateTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3860,11 +4998,14 @@ func (m *UpdateTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Resources == nil { - m.Resources = &google_protobuf.Any{} + m.Resources = &types1.Any{} } if err := m.Resources.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -3876,12 +5017,13 @@ func (m *UpdateTaskRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3906,7 +5048,7 @@ func (m *StartRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3934,7 +5076,7 @@ func (m *StartRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3944,6 +5086,9 @@ func (m *StartRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3955,12 +5100,13 @@ func (m *StartRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3985,7 +5131,7 @@ func (m *StartResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4013,7 +5159,7 @@ func (m *StartResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4023,6 +5169,9 @@ func (m *StartResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4042,7 +5191,7 @@ func (m *StartResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Pid |= (uint32(b) & 0x7F) << shift + m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -4053,12 +5202,13 @@ func (m *StartResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4083,7 +5233,7 @@ func (m *WaitRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4111,7 +5261,7 @@ func (m *WaitRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4121,6 +5271,9 @@ func (m *WaitRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4132,12 +5285,13 @@ func (m *WaitRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4162,7 +5316,7 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4190,7 +5344,7 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ExitStatus |= (uint32(b) & 0x7F) << shift + m.ExitStatus |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -4209,7 +5363,7 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4218,10 +5372,13 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthShim } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthShim + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -4231,12 +5388,13 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthShim } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -4249,6 +5407,7 @@ func (m *WaitResponse) Unmarshal(dAtA []byte) error { func skipShim(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -4280,10 +5439,8 @@ func skipShim(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -4300,132 +5457,34 @@ func skipShim(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthShim } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowShim - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipShim(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupShim + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthShim + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthShim = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowShim = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthShim = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowShim = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupShim = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto", fileDescriptorShim) -} - -var fileDescriptorShim = []byte{ - // 1133 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x4f, 0x4f, 0x1b, 0x47, - 0x14, 0x67, 0x17, 0xff, 0x7d, 0x8e, 0x29, 0x4c, 0x09, 0xdd, 0x38, 0x92, 0xb1, 0x56, 0x6a, 0x44, - 0x55, 0x65, 0x5d, 0x4c, 0x95, 0xa4, 0xad, 0x84, 0x04, 0x24, 0xaa, 0x50, 0x1b, 0x05, 0x2d, 0xa4, - 0x89, 0x5a, 0x55, 0x68, 0xf1, 0x0e, 0xf6, 0x08, 0x7b, 0x67, 0xb3, 0x33, 0x4b, 0xa1, 0xa7, 0x9e, - 0x7a, 0xee, 0xc7, 0xe9, 0x47, 0xe0, 0x90, 0x43, 0x8f, 0x3d, 0xa5, 0x0d, 0xf7, 0x7e, 0x87, 0x6a, - 0xfe, 0x18, 0xaf, 0x6d, 0x36, 0xbb, 0x70, 0xc1, 0xfb, 0x66, 0x7e, 0x6f, 0xe6, 0xcd, 0xfb, 0xfd, - 0xe6, 0xbd, 0x01, 0x36, 0x7b, 0x84, 0xf7, 0xe3, 0x23, 0xa7, 0x4b, 0x87, 0xed, 0x2e, 0x0d, 0xb8, - 0x47, 0x02, 0x1c, 0xf9, 0xc9, 0xcf, 0x28, 0x0e, 0x38, 0x19, 0xe2, 0xf6, 0xe9, 0x7a, 0x9b, 0xf5, - 0xc9, 0x70, 0xf4, 0xeb, 0x84, 0x11, 0xe5, 0x14, 0xb5, 0xc6, 0x48, 0x47, 0x23, 0x9d, 0x01, 0x09, - 0xe2, 0x33, 0x47, 0x82, 0x4e, 0xd7, 0x1b, 0xf7, 0x7a, 0x94, 0xf6, 0x06, 0xb8, 0x2d, 0xf1, 0x47, - 0xf1, 0x71, 0xdb, 0x0b, 0xce, 0x95, 0x73, 0xe3, 0xfe, 0xf4, 0x14, 0x1e, 0x86, 0x7c, 0x34, 0xb9, - 0xdc, 0xa3, 0x3d, 0x2a, 0x3f, 0xdb, 0xe2, 0x4b, 0x8f, 0xae, 0x4e, 0xbb, 0x88, 0x1d, 0x19, 0xf7, - 0x86, 0xa1, 0x06, 0x3c, 0xca, 0x3c, 0x90, 0x17, 0x92, 0x36, 0x3f, 0x0f, 0x31, 0x6b, 0x0f, 0x69, - 0x1c, 0x70, 0xed, 0xf7, 0xf5, 0x0d, 0xfc, 0xb8, 0xc7, 0x4e, 0xe4, 0x1f, 0xe5, 0x6b, 0xff, 0x67, - 0xc2, 0xd2, 0x4e, 0x84, 0x3d, 0x8e, 0x0f, 0x3c, 0x76, 0xe2, 0xe2, 0x37, 0x31, 0x66, 0x1c, 0xad, - 0x80, 0x49, 0x7c, 0xcb, 0x68, 0x19, 0x6b, 0xd5, 0xed, 0xd2, 0xe5, 0xbb, 0x55, 0x73, 0xf7, 0xa9, - 0x6b, 0x12, 0x1f, 0xad, 0x40, 0xe9, 0x28, 0x0e, 0xfc, 0x01, 0xb6, 0x4c, 0x31, 0xe7, 0x6a, 0x0b, - 0x59, 0x50, 0xd6, 0x19, 0xb4, 0xe6, 0xe5, 0xc4, 0xc8, 0x44, 0x6d, 0x28, 0x45, 0x94, 0xf2, 0x63, - 0x66, 0x15, 0x5a, 0xf3, 0x6b, 0xb5, 0xce, 0x27, 0x4e, 0x22, 0xeb, 0x32, 0x24, 0xe7, 0xb9, 0x38, - 0x8a, 0xab, 0x61, 0xa8, 0x01, 0x15, 0x8e, 0xa3, 0x21, 0x09, 0xbc, 0x81, 0x55, 0x6c, 0x19, 0x6b, - 0x15, 0xf7, 0xca, 0x46, 0xcb, 0x50, 0x64, 0xdc, 0x27, 0x81, 0x55, 0x92, 0x9b, 0x28, 0x43, 0x04, - 0xc5, 0xb8, 0x4f, 0x63, 0x6e, 0x95, 0x55, 0x50, 0xca, 0xd2, 0xe3, 0x38, 0x8a, 0xac, 0xca, 0xd5, - 0x38, 0x8e, 0x22, 0xd4, 0x04, 0xe8, 0xf6, 0x71, 0xf7, 0x24, 0xa4, 0x24, 0xe0, 0x56, 0x55, 0xce, - 0x25, 0x46, 0xd0, 0xe7, 0xb0, 0x14, 0x7a, 0x11, 0x0e, 0xf8, 0x61, 0x02, 0x06, 0x12, 0xb6, 0xa8, - 0x26, 0x76, 0xc6, 0x60, 0x07, 0xca, 0x34, 0xe4, 0x84, 0x06, 0xcc, 0xaa, 0xb5, 0x8c, 0xb5, 0x5a, - 0x67, 0xd9, 0x51, 0x34, 0x3b, 0x23, 0x9a, 0x9d, 0xad, 0xe0, 0xdc, 0x1d, 0x81, 0xec, 0x07, 0x80, - 0x92, 0xe9, 0x66, 0x21, 0x0d, 0x18, 0x46, 0x8b, 0x30, 0x1f, 0xea, 0x84, 0xd7, 0x5d, 0xf1, 0x69, - 0xff, 0x6e, 0xc0, 0xc2, 0x53, 0x3c, 0xc0, 0x1c, 0xa7, 0x83, 0xd0, 0x2a, 0xd4, 0xf0, 0x19, 0xe1, - 0x87, 0x8c, 0x7b, 0x3c, 0x66, 0x92, 0x93, 0xba, 0x0b, 0x62, 0x68, 0x5f, 0x8e, 0xa0, 0x2d, 0xa8, - 0x0a, 0x0b, 0xfb, 0x87, 0x1e, 0x97, 0xcc, 0xd4, 0x3a, 0x8d, 0x99, 0xf8, 0x0e, 0x46, 0x32, 0xdc, - 0xae, 0x5c, 0xbc, 0x5b, 0x9d, 0xfb, 0xe3, 0x9f, 0x55, 0xc3, 0xad, 0x28, 0xb7, 0x2d, 0x6e, 0x3b, - 0xb0, 0xac, 0xe2, 0xd8, 0x8b, 0x68, 0x17, 0x33, 0x96, 0x21, 0x11, 0xfb, 0x4f, 0x03, 0xd0, 0xb3, - 0x33, 0xdc, 0xcd, 0x07, 0x9f, 0xa0, 0xdb, 0x4c, 0xa3, 0x7b, 0xfe, 0x7a, 0xba, 0x0b, 0x29, 0x74, - 0x17, 0x27, 0xe8, 0x5e, 0x83, 0x02, 0x0b, 0x71, 0x57, 0x6a, 0x26, 0x8d, 0x1e, 0x89, 0xb0, 0xef, - 0xc2, 0xc7, 0x13, 0x91, 0xab, 0xbc, 0xdb, 0xaf, 0x61, 0xd1, 0xc5, 0x8c, 0xfc, 0x8a, 0xf7, 0xf8, - 0x79, 0xd6, 0x71, 0x96, 0xa1, 0xf8, 0x0b, 0xf1, 0x79, 0x5f, 0x73, 0xa1, 0x0c, 0x11, 0x5a, 0x1f, - 0x93, 0x5e, 0x5f, 0x71, 0x50, 0x77, 0xb5, 0x65, 0x3f, 0x80, 0x3b, 0x82, 0x28, 0x9c, 0x95, 0xd3, - 0xb7, 0x26, 0xd4, 0x35, 0x50, 0x6b, 0xe1, 0xa6, 0x17, 0x54, 0x6b, 0x67, 0x7e, 0xac, 0x9d, 0x0d, - 0x91, 0x2e, 0x29, 0x1b, 0x91, 0xc6, 0x85, 0xce, 0xfd, 0xe4, 0xc5, 0x3c, 0x5d, 0xd7, 0x77, 0x53, - 0xe9, 0xc8, 0xd5, 0xd0, 0x31, 0x23, 0xc5, 0xeb, 0x19, 0x29, 0xa5, 0x30, 0x52, 0x9e, 0x60, 0x24, - 0xc9, 0x79, 0x65, 0x8a, 0xf3, 0x29, 0x49, 0x57, 0x3f, 0x2c, 0x69, 0xb8, 0x95, 0xa4, 0x5f, 0x40, - 0xed, 0x3b, 0x32, 0x18, 0xe4, 0x28, 0x76, 0x8c, 0xf4, 0x46, 0xc2, 0xac, 0xbb, 0xda, 0x12, 0xb9, - 0xf4, 0x06, 0x03, 0x99, 0xcb, 0x8a, 0x2b, 0x3e, 0xed, 0x4d, 0x58, 0xd8, 0x19, 0x50, 0x86, 0x77, - 0x5f, 0xe4, 0xd0, 0x87, 0x4a, 0xa0, 0xd2, 0xba, 0x32, 0xec, 0xcf, 0xe0, 0xa3, 0xef, 0x09, 0xe3, - 0x7b, 0xc4, 0xcf, 0xbc, 0x5e, 0x2e, 0x2c, 0x8e, 0xa1, 0x5a, 0x0c, 0x9b, 0x50, 0x0d, 0x95, 0x66, - 0x31, 0xb3, 0x0c, 0x59, 0x66, 0x5b, 0xd7, 0xb2, 0xa9, 0x95, 0xbd, 0x1b, 0x1c, 0x53, 0x77, 0xec, - 0x62, 0xff, 0x04, 0x77, 0xc7, 0x15, 0x2d, 0xd9, 0x06, 0x10, 0x14, 0x42, 0x8f, 0xf7, 0x55, 0x18, - 0xae, 0xfc, 0x4e, 0x16, 0x3c, 0x33, 0x4f, 0xc1, 0x7b, 0x08, 0x8b, 0xfb, 0x7d, 0x32, 0x94, 0x7b, - 0x8e, 0x02, 0xbe, 0x07, 0x15, 0xd1, 0x62, 0x0f, 0xc7, 0xe5, 0xac, 0x2c, 0xec, 0x3d, 0xe2, 0xdb, - 0xdf, 0xc2, 0xd2, 0xcb, 0xd0, 0x9f, 0x6a, 0x47, 0x1d, 0xa8, 0x46, 0x98, 0xd1, 0x38, 0xea, 0xca, - 0x03, 0xa6, 0xef, 0x3a, 0x86, 0xe9, 0xbb, 0x15, 0xf1, 0xac, 0x84, 0x7e, 0x25, 0xaf, 0x96, 0xc0, - 0x65, 0x5c, 0x2d, 0x7d, 0x85, 0xcc, 0x71, 0x8d, 0xfe, 0x14, 0x6a, 0xaf, 0x3c, 0x92, 0xb9, 0x43, - 0x04, 0x77, 0x14, 0x4c, 0x6f, 0x30, 0x25, 0x71, 0xe3, 0xc3, 0x12, 0x37, 0x6f, 0x23, 0xf1, 0xce, - 0xdb, 0x1a, 0x14, 0x44, 0xda, 0x51, 0x1f, 0x8a, 0xb2, 0x72, 0x20, 0xc7, 0xc9, 0x7a, 0xee, 0x38, - 0xc9, 0x5a, 0xd4, 0x68, 0xe7, 0xc6, 0xeb, 0x63, 0x31, 0x28, 0xa9, 0xce, 0x86, 0x36, 0xb2, 0x5d, - 0x67, 0x9e, 0x1c, 0x8d, 0x2f, 0x6f, 0xe6, 0xa4, 0x37, 0x55, 0xc7, 0x8b, 0x78, 0xce, 0xe3, 0x5d, - 0xc9, 0x21, 0xe7, 0xf1, 0x12, 0xb2, 0x70, 0xa1, 0xa4, 0xfa, 0x20, 0x5a, 0x99, 0xe1, 0xe2, 0x99, - 0x78, 0xfb, 0x35, 0xbe, 0xc8, 0x5e, 0x72, 0xaa, 0xa3, 0x9f, 0x43, 0x7d, 0xa2, 0xb7, 0xa2, 0x47, - 0x79, 0x97, 0x98, 0xec, 0xae, 0xb7, 0xd8, 0xfa, 0x0d, 0x54, 0x46, 0x75, 0x04, 0xad, 0x67, 0x7b, - 0x4f, 0x95, 0xa7, 0x46, 0xe7, 0x26, 0x2e, 0x7a, 0xcb, 0xc7, 0x50, 0xdc, 0xf3, 0x62, 0x96, 0x9e, - 0xc0, 0x94, 0x71, 0xf4, 0x04, 0x4a, 0x2e, 0x66, 0xf1, 0xf0, 0xe6, 0x9e, 0x3f, 0x03, 0x24, 0xde, - 0x6a, 0x8f, 0x73, 0x48, 0xec, 0xba, 0x3a, 0x98, 0xba, 0xfc, 0x73, 0x28, 0x88, 0x46, 0x82, 0x1e, - 0x66, 0x2f, 0x9c, 0x68, 0x38, 0xa9, 0xcb, 0x1d, 0x40, 0x41, 0xbc, 0x3f, 0x50, 0x8e, 0xab, 0x30, - 0xfb, 0xc2, 0x4a, 0x5d, 0xf5, 0x15, 0x54, 0xaf, 0x9e, 0x2f, 0x28, 0x07, 0x6f, 0xd3, 0x6f, 0x9d, - 0xd4, 0x85, 0xf7, 0xa1, 0xac, 0xbb, 0x1e, 0xca, 0xa1, 0xbf, 0xc9, 0x06, 0x99, 0xba, 0xe8, 0x0f, - 0x50, 0x19, 0xb5, 0x8b, 0x54, 0xb6, 0x73, 0x1c, 0x62, 0xa6, 0xe5, 0xbc, 0x84, 0x92, 0xea, 0x2b, - 0x79, 0xaa, 0xd3, 0x4c, 0x07, 0x4a, 0x0d, 0x17, 0x43, 0x41, 0xd4, 0xf6, 0x3c, 0x0a, 0x48, 0xb4, - 0x8a, 0x86, 0x93, 0x17, 0xae, 0xa2, 0xdf, 0x76, 0x2f, 0xde, 0x37, 0xe7, 0xfe, 0x7e, 0xdf, 0x9c, - 0xfb, 0xed, 0xb2, 0x69, 0x5c, 0x5c, 0x36, 0x8d, 0xbf, 0x2e, 0x9b, 0xc6, 0xbf, 0x97, 0x4d, 0xe3, - 0xc7, 0x27, 0xb7, 0xf8, 0x27, 0xf8, 0x1b, 0xf1, 0xfb, 0xda, 0x3c, 0x2a, 0xc9, 0xc3, 0x6c, 0xfc, - 0x1f, 0x00, 0x00, 0xff, 0xff, 0x64, 0x52, 0x86, 0xc0, 0x49, 0x0f, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto b/vendor/github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto index 516d914b9e1c7..b44418a690bfd 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto +++ b/vendor/github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + syntax = "proto3"; package containerd.runtime.linux.shim.v1; diff --git a/vendor/github.com/containerd/containerd/runtime/v2/README.md b/vendor/github.com/containerd/containerd/runtime/v2/README.md new file mode 100644 index 0000000000000..aaa878bfde71e --- /dev/null +++ b/vendor/github.com/containerd/containerd/runtime/v2/README.md @@ -0,0 +1,254 @@ +# Runtime v2 + +Runtime v2 introduces a first class shim API for runtime authors to integrate with containerd. +The shim API is minimal and scoped to the execution lifecycle of a container. + +## Binary Naming + +Users specify the runtime they wish to use when creating a container. +The runtime can also be changed via a container update. + +```bash +> ctr run --runtime io.containerd.runc.v1 +``` + +When a user specifies a runtime name, `io.containerd.runc.v1`, they will specify the name and version of the runtime. +This will be translated by containerd into a binary name for the shim. + +`io.containerd.runc.v1` -> `containerd-shim-runc-v1` + +containerd keeps the `containerd-shim-*` prefix so that users can `ps aux | grep containerd-shim` to see running shims on their system. + +## Shim Authoring + +This section is dedicated to runtime authors wishing to build a shim. +It will detail how the API works and different considerations when building shim. + +### Commands + +Container information is provided to a shim in two ways. +The OCI Runtime Bundle and on the `Create` rpc request. + +#### `start` + +Each shim MUST implement a `start` subcommand. +This command will launch new shims. +The start command MUST accept the following flags: + +* `-namespace` the namespace for the container +* `-address` the address of the containerd's main socket +* `-publish-binary` the binary path to publish events back to containerd +* `-id` the id of the container + +The start command, as well as all binary calls to the shim, has the bundle for the container set as the `cwd`. + +The start command MUST return an address to a shim for containerd to issue API requests for container operations. + +The start command can either start a new shim or return an address to an existing shim based on the shim's logic. + +#### `delete` + +Each shim MUST implement a `delete` subcommand. +This command allows containerd to delete any container resources created, mounted, and/or run by a shim when containerd can no longer communicate over rpc. +This happens if a shim is SIGKILL'd with a running container. +These resources will need to be cleaned up when containerd looses the connection to a shim. +This is also used when containerd boots and reconnects to shims. +If a bundle is still on disk but containerd cannot connect to a shim, the delete command is invoked. + +The delete command MUST accept the following flags: + +* `-namespace` the namespace for the container +* `-address` the address of the containerd's main socket +* `-publish-binary` the binary path to publish events back to containerd +* `-id` the id of the container +* `-bundle` the path to the bundle to delete. On non-Windows platforms this will match `cwd` + +The delete command will be executed in the container's bundle as its `cwd` except for on the Windows platform. + +### Host Level Shim Configuration + +containerd does not provide any host level configuration for shims via the API. +If a shim needs configuration from the user with host level information across all instances, a shim specific configuration file can be setup. + +### Container Level Shim Configuration + +On the create request, there is a generic `*protobuf.Any` that allows a user to specify container level configuration for the shim. + +```proto +message CreateTaskRequest { + string id = 1; + ... + google.protobuf.Any options = 10; +} +``` + +A shim author can create their own protobuf message for configuration and clients can import and provide this information is needed. + +### I/O + +I/O for a container is provided by the client to the shim via fifo on Linux, named pipes on Windows, or log files on disk. +The paths to these files are provided on the `Create` rpc for the initial creation and on the `Exec` rpc for additional processes. + +```proto +message CreateTaskRequest { + string id = 1; + bool terminal = 4; + string stdin = 5; + string stdout = 6; + string stderr = 7; +} +``` + +```proto +message ExecProcessRequest { + string id = 1; + string exec_id = 2; + bool terminal = 3; + string stdin = 4; + string stdout = 5; + string stderr = 6; +} +``` + +Containers that are to be launched with an interactive terminal will have the `terminal` field set to `true`, data is still copied over the files(fifos,pipes) in the same way as non interactive containers. + +### Root Filesystems + +The root filesystem for the containers is provided by on the `Create` rpc. +Shims are responsible for managing the lifecycle of the filesystem mount during the lifecycle of a container. + +```proto +message CreateTaskRequest { + string id = 1; + string bundle = 2; + repeated containerd.types.Mount rootfs = 3; + ... +} +``` + +The mount protobuf message is: + +```proto +message Mount { + // Type defines the nature of the mount. + string type = 1; + // Source specifies the name of the mount. Depending on mount type, this + // may be a volume name or a host path, or even ignored. + string source = 2; + // Target path in container + string target = 3; + // Options specifies zero or more fstab style mount options. + repeated string options = 4; +} +``` + +Shims are responsible for mounting the filesystem into the `rootfs/` directory of the bundle. +Shims are also responsible for unmounting of the filesystem. +During a `delete` binary call, the shim MUST ensure that filesystem is also unmounted. +Filesystems are provided by the containerd snapshotters. + +### Events + +The Runtime v2 supports an async event model. In order for the an upstream caller (such as Docker) to get these events in the correct order a Runtime v2 shim MUST implement the following events where `Compliance=MUST`. This avoids race conditions between the shim and shim client where for example a call to `Start` can signal a `TaskExitEventTopic` before even returning the results from the `Start` call. With these guarantees of a Runtime v2 shim a call to `Start` is required to have published the async event `TaskStartEventTopic` before the shim can publish the `TaskExitEventTopic`. + +#### Tasks + +| Topic | Compliance | Description | +| ----- | ---------- | ----------- | +| `runtime.TaskCreateEventTopic` | MUST | When a task is successfully created | +| `runtime.TaskStartEventTopic` | MUST (follow `TaskCreateEventTopic`) | When a task is successfully started | +| `runtime.TaskExitEventTopic` | MUST (follow `TaskStartEventTopic`) | When a task exits expected or unexpected | +| `runtime.TaskDeleteEventTopic` | MUST (follow `TaskExitEventTopic` or `TaskCreateEventTopic` if never started) | When a task is removed from a shim | +| `runtime.TaskPausedEventTopic` | SHOULD | When a task is successfully paused | +| `runtime.TaskResumedEventTopic` | SHOULD (follow `TaskPausedEventTopic`) | When a task is successfully resumed | +| `runtime.TaskCheckpointedEventTopic` | SHOULD | When a task is checkpointed | +| `runtime.TaskOOMEventTopic` | SHOULD | If the shim collects Out of Memory events | + +#### Execs + +| Topic | Compliance | Description | +| ----- | ---------- | ----------- | +| `runtime.TaskExecAddedEventTopic` | MUST (follow `TaskCreateEventTopic` ) | When an exec is successfully added | +| `runtime.TaskExecStartedEventTopic` | MUST (follow `TaskExecAddedEventTopic`) | When an exec is successfully started | +| `runtime.TaskExitEventTopic` | MUST (follow `TaskExecStartedEventTopic`) | When an exec (other than the init exec) exits expected or unexpected | +| `runtime.TaskDeleteEventTopic` | SHOULD (follow `TaskExitEventTopic` or `TaskExecAddedEventTopic` if never started) | When an exec is removed from a shim | + +#### Logging + +Shims may support pluggable logging via STDIO URIs. +Current supported schemes for logging are: + +* fifo - Linux +* binary - Linux & Windows +* file - Linux & Windows +* npipe - Windows + +Binary logging has the ability to forward a container's STDIO to an external binary for consumption. +A sample logging driver that forwards the container's STDOUT and STDERR to `journald` is: + +```go +package main + +import ( + "bufio" + "context" + "fmt" + "io" + "sync" + + "github.com/containerd/containerd/runtime/v2/logging" + "github.com/coreos/go-systemd/journal" +) + +func main() { + logging.Run(log) +} + +func log(ctx context.Context, config *logging.Config, ready func() error) error { + // construct any log metadata for the container + vars := map[string]string{ + "SYSLOG_IDENTIFIER": fmt.Sprintf("%s:%s", config.Namespace, config.ID), + } + var wg sync.WaitGroup + wg.Add(2) + // forward both stdout and stderr to the journal + go copy(&wg, config.Stdout, journal.PriInfo, vars) + go copy(&wg, config.Stderr, journal.PriErr, vars) + + // signal that we are ready and setup for the container to be started + if err := ready(); err != nil { + return err + } + wg.Wait() + return nil +} + +func copy(wg *sync.WaitGroup, r io.Reader, pri journal.Priority, vars map[string]string) { + defer wg.Done() + s := bufio.NewScanner(r) + for s.Scan() { + journal.Send(s.Text(), pri, vars) + } +} +``` + +### Other + +#### Unsupported rpcs + +If a shim does not or cannot implement an rpc call, it MUST return a `github.com/containerd/containerd/errdefs.ErrNotImplemented` error. + +#### Debugging and Shim Logs + +A fifo on unix or named pipe on Windows will be provided to the shim. +It can be located inside the `cwd` of the shim named "log". +The shims can use the existing `github.com/containerd/containerd/log` package to log debug messages. +Messages will automatically be output in the containerd's daemon logs with the correct fields and runtime set. + +#### ttrpc + +[ttrpc](https://github.com/containerd/ttrpc) is the only currently supported protocol for shims. +It works with standard protobufs and GRPC services as well as generating clients. +The only difference between grpc and ttrpc is the wire protocol. +ttrpc removes the http stack in order to save memory and binary size to keep shims small. +It is recommended to use ttrpc in your shim but grpc support is also in development. diff --git a/vendor/github.com/containerd/containerd/runtime/v2/runc/options/doc.go b/vendor/github.com/containerd/containerd/runtime/v2/runc/options/doc.go new file mode 100644 index 0000000000000..ffff495cbfb81 --- /dev/null +++ b/vendor/github.com/containerd/containerd/runtime/v2/runc/options/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package options diff --git a/vendor/github.com/containerd/containerd/runtime/v2/runc/options/oci.pb.go b/vendor/github.com/containerd/containerd/runtime/v2/runc/options/oci.pb.go new file mode 100644 index 0000000000000..c9c44742a2588 --- /dev/null +++ b/vendor/github.com/containerd/containerd/runtime/v2/runc/options/oci.pb.go @@ -0,0 +1,1458 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/runtime/v2/runc/options/oci.proto + +package options + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Options struct { + // disable pivot root when creating a container + NoPivotRoot bool `protobuf:"varint,1,opt,name=no_pivot_root,json=noPivotRoot,proto3" json:"no_pivot_root,omitempty"` + // create a new keyring for the container + NoNewKeyring bool `protobuf:"varint,2,opt,name=no_new_keyring,json=noNewKeyring,proto3" json:"no_new_keyring,omitempty"` + // place the shim in a cgroup + ShimCgroup string `protobuf:"bytes,3,opt,name=shim_cgroup,json=shimCgroup,proto3" json:"shim_cgroup,omitempty"` + // set the I/O's pipes uid + IoUid uint32 `protobuf:"varint,4,opt,name=io_uid,json=ioUid,proto3" json:"io_uid,omitempty"` + // set the I/O's pipes gid + IoGid uint32 `protobuf:"varint,5,opt,name=io_gid,json=ioGid,proto3" json:"io_gid,omitempty"` + // binary name of the runc binary + BinaryName string `protobuf:"bytes,6,opt,name=binary_name,json=binaryName,proto3" json:"binary_name,omitempty"` + // runc root directory + Root string `protobuf:"bytes,7,opt,name=root,proto3" json:"root,omitempty"` + // criu binary path + CriuPath string `protobuf:"bytes,8,opt,name=criu_path,json=criuPath,proto3" json:"criu_path,omitempty"` + // enable systemd cgroups + SystemdCgroup bool `protobuf:"varint,9,opt,name=systemd_cgroup,json=systemdCgroup,proto3" json:"systemd_cgroup,omitempty"` + // criu image path + CriuImagePath string `protobuf:"bytes,10,opt,name=criu_image_path,json=criuImagePath,proto3" json:"criu_image_path,omitempty"` + // criu work path + CriuWorkPath string `protobuf:"bytes,11,opt,name=criu_work_path,json=criuWorkPath,proto3" json:"criu_work_path,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Options) Reset() { *m = Options{} } +func (*Options) ProtoMessage() {} +func (*Options) Descriptor() ([]byte, []int) { + return fileDescriptor_4e5440d739e9a863, []int{0} +} +func (m *Options) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Options) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Options.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Options) XXX_Merge(src proto.Message) { + xxx_messageInfo_Options.Merge(m, src) +} +func (m *Options) XXX_Size() int { + return m.Size() +} +func (m *Options) XXX_DiscardUnknown() { + xxx_messageInfo_Options.DiscardUnknown(m) +} + +var xxx_messageInfo_Options proto.InternalMessageInfo + +type CheckpointOptions struct { + // exit the container after a checkpoint + Exit bool `protobuf:"varint,1,opt,name=exit,proto3" json:"exit,omitempty"` + // checkpoint open tcp connections + OpenTcp bool `protobuf:"varint,2,opt,name=open_tcp,json=openTcp,proto3" json:"open_tcp,omitempty"` + // checkpoint external unix sockets + ExternalUnixSockets bool `protobuf:"varint,3,opt,name=external_unix_sockets,json=externalUnixSockets,proto3" json:"external_unix_sockets,omitempty"` + // checkpoint terminals (ptys) + Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"` + // allow checkpointing of file locks + FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"` + // restore provided namespaces as empty namespaces + EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces,proto3" json:"empty_namespaces,omitempty"` + // set the cgroups mode, soft, full, strict + CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"` + // checkpoint image path + ImagePath string `protobuf:"bytes,8,opt,name=image_path,json=imagePath,proto3" json:"image_path,omitempty"` + // checkpoint work path + WorkPath string `protobuf:"bytes,9,opt,name=work_path,json=workPath,proto3" json:"work_path,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CheckpointOptions) Reset() { *m = CheckpointOptions{} } +func (*CheckpointOptions) ProtoMessage() {} +func (*CheckpointOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_4e5440d739e9a863, []int{1} +} +func (m *CheckpointOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CheckpointOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CheckpointOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CheckpointOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_CheckpointOptions.Merge(m, src) +} +func (m *CheckpointOptions) XXX_Size() int { + return m.Size() +} +func (m *CheckpointOptions) XXX_DiscardUnknown() { + xxx_messageInfo_CheckpointOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_CheckpointOptions proto.InternalMessageInfo + +type ProcessDetails struct { + // exec process id if the process is managed by a shim + ExecID string `protobuf:"bytes,1,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ProcessDetails) Reset() { *m = ProcessDetails{} } +func (*ProcessDetails) ProtoMessage() {} +func (*ProcessDetails) Descriptor() ([]byte, []int) { + return fileDescriptor_4e5440d739e9a863, []int{2} +} +func (m *ProcessDetails) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProcessDetails) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProcessDetails.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ProcessDetails) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProcessDetails.Merge(m, src) +} +func (m *ProcessDetails) XXX_Size() int { + return m.Size() +} +func (m *ProcessDetails) XXX_DiscardUnknown() { + xxx_messageInfo_ProcessDetails.DiscardUnknown(m) +} + +var xxx_messageInfo_ProcessDetails proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Options)(nil), "containerd.runc.v1.Options") + proto.RegisterType((*CheckpointOptions)(nil), "containerd.runc.v1.CheckpointOptions") + proto.RegisterType((*ProcessDetails)(nil), "containerd.runc.v1.ProcessDetails") +} + +func init() { + proto.RegisterFile("github.com/containerd/containerd/runtime/v2/runc/options/oci.proto", fileDescriptor_4e5440d739e9a863) +} + +var fileDescriptor_4e5440d739e9a863 = []byte{ + // 587 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0xcf, 0x6e, 0xd3, 0x40, + 0x10, 0x87, 0xeb, 0xfe, 0x49, 0xec, 0x4d, 0x93, 0xc2, 0x42, 0x25, 0xd3, 0x8a, 0x34, 0x94, 0x82, + 0xc2, 0x25, 0x11, 0x45, 0x9c, 0xb8, 0xa0, 0xb6, 0x08, 0x55, 0x40, 0xa9, 0x0c, 0x15, 0xa8, 0x97, + 0x95, 0xbb, 0x1e, 0x9c, 0x51, 0xe2, 0x1d, 0xcb, 0xbb, 0x69, 0xd2, 0x1b, 0xef, 0xc5, 0x0b, 0xf4, + 0xc8, 0x91, 0x13, 0xa2, 0xb9, 0xf1, 0x16, 0x68, 0xd7, 0x4e, 0xdb, 0x33, 0x27, 0xcf, 0x7e, 0xf3, + 0xf3, 0x78, 0xfd, 0xad, 0x96, 0xed, 0xa5, 0x68, 0x06, 0xe3, 0xb3, 0x9e, 0xa4, 0xac, 0x2f, 0x49, + 0x99, 0x18, 0x15, 0x14, 0xc9, 0xed, 0xb2, 0x18, 0x2b, 0x83, 0x19, 0xf4, 0xcf, 0x77, 0x6d, 0x29, + 0xfb, 0x94, 0x1b, 0x24, 0xa5, 0xfb, 0x24, 0xb1, 0x97, 0x17, 0x64, 0x88, 0xf3, 0x9b, 0x74, 0xcf, + 0x46, 0x7a, 0xe7, 0xcf, 0x37, 0xee, 0xa7, 0x94, 0x92, 0x6b, 0xf7, 0x6d, 0x55, 0x26, 0xb7, 0xff, + 0x2e, 0xb2, 0xfa, 0xc7, 0xf2, 0x7d, 0xbe, 0xcd, 0x9a, 0x8a, 0x44, 0x8e, 0xe7, 0x64, 0x44, 0x41, + 0x64, 0x42, 0xaf, 0xe3, 0x75, 0xfd, 0xa8, 0xa1, 0xe8, 0xd8, 0xb2, 0x88, 0xc8, 0xf0, 0x1d, 0xd6, + 0x52, 0x24, 0x14, 0x4c, 0xc4, 0x10, 0x2e, 0x0a, 0x54, 0x69, 0xb8, 0xe8, 0x42, 0xab, 0x8a, 0x8e, + 0x60, 0xf2, 0xae, 0x64, 0x7c, 0x8b, 0x35, 0xf4, 0x00, 0x33, 0x21, 0xd3, 0x82, 0xc6, 0x79, 0xb8, + 0xd4, 0xf1, 0xba, 0x41, 0xc4, 0x2c, 0xda, 0x77, 0x84, 0xaf, 0xb3, 0x1a, 0x92, 0x18, 0x63, 0x12, + 0x2e, 0x77, 0xbc, 0x6e, 0x33, 0x5a, 0x41, 0x3a, 0xc1, 0xa4, 0xc2, 0x29, 0x26, 0xe1, 0xca, 0x1c, + 0xbf, 0xc5, 0xc4, 0x8e, 0x3b, 0x43, 0x15, 0x17, 0x17, 0x42, 0xc5, 0x19, 0x84, 0xb5, 0x72, 0x5c, + 0x89, 0x8e, 0xe2, 0x0c, 0x38, 0x67, 0xcb, 0x6e, 0xc3, 0x75, 0xd7, 0x71, 0x35, 0xdf, 0x64, 0x81, + 0x2c, 0x70, 0x2c, 0xf2, 0xd8, 0x0c, 0x42, 0xdf, 0x35, 0x7c, 0x0b, 0x8e, 0x63, 0x33, 0xe0, 0x4f, + 0x58, 0x4b, 0x5f, 0x68, 0x03, 0x59, 0x32, 0xdf, 0x63, 0xe0, 0x7e, 0xa3, 0x59, 0xd1, 0x6a, 0x9b, + 0x4f, 0xd9, 0x9a, 0x9b, 0x81, 0x59, 0x9c, 0x42, 0x39, 0x89, 0xb9, 0x49, 0x4d, 0x8b, 0x0f, 0x2d, + 0x75, 0xe3, 0x76, 0x58, 0xcb, 0xe5, 0x26, 0x54, 0x0c, 0xcb, 0x58, 0xc3, 0xc5, 0x56, 0x2d, 0xfd, + 0x42, 0xc5, 0xd0, 0xa6, 0xb6, 0x7f, 0x2c, 0xb2, 0xbb, 0xfb, 0x03, 0x90, 0xc3, 0x9c, 0x50, 0x99, + 0xb9, 0x75, 0xce, 0x96, 0x61, 0x8a, 0x73, 0xd9, 0xae, 0xe6, 0x0f, 0x98, 0x4f, 0x39, 0x28, 0x61, + 0x64, 0x5e, 0xf9, 0xad, 0xdb, 0xf5, 0x67, 0x99, 0xf3, 0x5d, 0xb6, 0x0e, 0x53, 0x03, 0x85, 0x8a, + 0x47, 0x62, 0xac, 0x70, 0x2a, 0x34, 0xc9, 0x21, 0x18, 0xed, 0x24, 0xfb, 0xd1, 0xbd, 0x79, 0xf3, + 0x44, 0xe1, 0xf4, 0x53, 0xd9, 0xe2, 0x1b, 0xcc, 0x37, 0x50, 0x64, 0xa8, 0xe2, 0x91, 0xf3, 0xed, + 0x47, 0xd7, 0x6b, 0xfe, 0x90, 0xb1, 0x6f, 0x38, 0x02, 0x31, 0x22, 0x39, 0xd4, 0x4e, 0xbb, 0x1f, + 0x05, 0x96, 0xbc, 0xb7, 0x80, 0x3f, 0x63, 0x77, 0x20, 0xcb, 0x4d, 0x69, 0x5e, 0xe7, 0xb1, 0x04, + 0x1d, 0xd6, 0x3a, 0x4b, 0xdd, 0x20, 0x5a, 0x73, 0xfc, 0xe8, 0x1a, 0xf3, 0x47, 0x6c, 0xb5, 0x74, + 0xa9, 0x45, 0x46, 0x09, 0x54, 0x87, 0xd1, 0xa8, 0xd8, 0x07, 0x4a, 0xc0, 0x7e, 0xec, 0x96, 0xca, + 0xf2, 0x50, 0x02, 0xbc, 0xd6, 0xb8, 0xc9, 0x82, 0x1b, 0x83, 0x41, 0x79, 0x64, 0x93, 0xb9, 0xbd, + 0x97, 0xac, 0x75, 0x5c, 0x90, 0x04, 0xad, 0x0f, 0xc0, 0xc4, 0x38, 0xd2, 0xfc, 0x31, 0xab, 0xc3, + 0x14, 0xa4, 0xc0, 0xc4, 0xc9, 0x0b, 0xf6, 0xd8, 0xec, 0xf7, 0x56, 0xed, 0xcd, 0x14, 0xe4, 0xe1, + 0x41, 0x54, 0xb3, 0xad, 0xc3, 0x64, 0xef, 0xf4, 0xf2, 0xaa, 0xbd, 0xf0, 0xeb, 0xaa, 0xbd, 0xf0, + 0x7d, 0xd6, 0xf6, 0x2e, 0x67, 0x6d, 0xef, 0xe7, 0xac, 0xed, 0xfd, 0x99, 0xb5, 0xbd, 0xd3, 0xd7, + 0xff, 0x7b, 0xd1, 0x5e, 0x55, 0xcf, 0xaf, 0x0b, 0x67, 0x35, 0x77, 0x8b, 0x5e, 0xfc, 0x0b, 0x00, + 0x00, 0xff, 0xff, 0x90, 0x50, 0x79, 0xf2, 0xb5, 0x03, 0x00, 0x00, +} + +func (m *Options) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Options) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Options) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.CriuWorkPath) > 0 { + i -= len(m.CriuWorkPath) + copy(dAtA[i:], m.CriuWorkPath) + i = encodeVarintOci(dAtA, i, uint64(len(m.CriuWorkPath))) + i-- + dAtA[i] = 0x5a + } + if len(m.CriuImagePath) > 0 { + i -= len(m.CriuImagePath) + copy(dAtA[i:], m.CriuImagePath) + i = encodeVarintOci(dAtA, i, uint64(len(m.CriuImagePath))) + i-- + dAtA[i] = 0x52 + } + if m.SystemdCgroup { + i-- + if m.SystemdCgroup { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x48 + } + if len(m.CriuPath) > 0 { + i -= len(m.CriuPath) + copy(dAtA[i:], m.CriuPath) + i = encodeVarintOci(dAtA, i, uint64(len(m.CriuPath))) + i-- + dAtA[i] = 0x42 + } + if len(m.Root) > 0 { + i -= len(m.Root) + copy(dAtA[i:], m.Root) + i = encodeVarintOci(dAtA, i, uint64(len(m.Root))) + i-- + dAtA[i] = 0x3a + } + if len(m.BinaryName) > 0 { + i -= len(m.BinaryName) + copy(dAtA[i:], m.BinaryName) + i = encodeVarintOci(dAtA, i, uint64(len(m.BinaryName))) + i-- + dAtA[i] = 0x32 + } + if m.IoGid != 0 { + i = encodeVarintOci(dAtA, i, uint64(m.IoGid)) + i-- + dAtA[i] = 0x28 + } + if m.IoUid != 0 { + i = encodeVarintOci(dAtA, i, uint64(m.IoUid)) + i-- + dAtA[i] = 0x20 + } + if len(m.ShimCgroup) > 0 { + i -= len(m.ShimCgroup) + copy(dAtA[i:], m.ShimCgroup) + i = encodeVarintOci(dAtA, i, uint64(len(m.ShimCgroup))) + i-- + dAtA[i] = 0x1a + } + if m.NoNewKeyring { + i-- + if m.NoNewKeyring { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.NoPivotRoot { + i-- + if m.NoPivotRoot { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *CheckpointOptions) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CheckpointOptions) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CheckpointOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.WorkPath) > 0 { + i -= len(m.WorkPath) + copy(dAtA[i:], m.WorkPath) + i = encodeVarintOci(dAtA, i, uint64(len(m.WorkPath))) + i-- + dAtA[i] = 0x4a + } + if len(m.ImagePath) > 0 { + i -= len(m.ImagePath) + copy(dAtA[i:], m.ImagePath) + i = encodeVarintOci(dAtA, i, uint64(len(m.ImagePath))) + i-- + dAtA[i] = 0x42 + } + if len(m.CgroupsMode) > 0 { + i -= len(m.CgroupsMode) + copy(dAtA[i:], m.CgroupsMode) + i = encodeVarintOci(dAtA, i, uint64(len(m.CgroupsMode))) + i-- + dAtA[i] = 0x3a + } + if len(m.EmptyNamespaces) > 0 { + for iNdEx := len(m.EmptyNamespaces) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.EmptyNamespaces[iNdEx]) + copy(dAtA[i:], m.EmptyNamespaces[iNdEx]) + i = encodeVarintOci(dAtA, i, uint64(len(m.EmptyNamespaces[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if m.FileLocks { + i-- + if m.FileLocks { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if m.Terminal { + i-- + if m.Terminal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if m.ExternalUnixSockets { + i-- + if m.ExternalUnixSockets { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.OpenTcp { + i-- + if m.OpenTcp { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if m.Exit { + i-- + if m.Exit { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ProcessDetails) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProcessDetails) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProcessDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.ExecID) > 0 { + i -= len(m.ExecID) + copy(dAtA[i:], m.ExecID) + i = encodeVarintOci(dAtA, i, uint64(len(m.ExecID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintOci(dAtA []byte, offset int, v uint64) int { + offset -= sovOci(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Options) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.NoPivotRoot { + n += 2 + } + if m.NoNewKeyring { + n += 2 + } + l = len(m.ShimCgroup) + if l > 0 { + n += 1 + l + sovOci(uint64(l)) + } + if m.IoUid != 0 { + n += 1 + sovOci(uint64(m.IoUid)) + } + if m.IoGid != 0 { + n += 1 + sovOci(uint64(m.IoGid)) + } + l = len(m.BinaryName) + if l > 0 { + n += 1 + l + sovOci(uint64(l)) + } + l = len(m.Root) + if l > 0 { + n += 1 + l + sovOci(uint64(l)) + } + l = len(m.CriuPath) + if l > 0 { + n += 1 + l + sovOci(uint64(l)) + } + if m.SystemdCgroup { + n += 2 + } + l = len(m.CriuImagePath) + if l > 0 { + n += 1 + l + sovOci(uint64(l)) + } + l = len(m.CriuWorkPath) + if l > 0 { + n += 1 + l + sovOci(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *CheckpointOptions) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Exit { + n += 2 + } + if m.OpenTcp { + n += 2 + } + if m.ExternalUnixSockets { + n += 2 + } + if m.Terminal { + n += 2 + } + if m.FileLocks { + n += 2 + } + if len(m.EmptyNamespaces) > 0 { + for _, s := range m.EmptyNamespaces { + l = len(s) + n += 1 + l + sovOci(uint64(l)) + } + } + l = len(m.CgroupsMode) + if l > 0 { + n += 1 + l + sovOci(uint64(l)) + } + l = len(m.ImagePath) + if l > 0 { + n += 1 + l + sovOci(uint64(l)) + } + l = len(m.WorkPath) + if l > 0 { + n += 1 + l + sovOci(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *ProcessDetails) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ExecID) + if l > 0 { + n += 1 + l + sovOci(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovOci(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozOci(x uint64) (n int) { + return sovOci(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Options) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Options{`, + `NoPivotRoot:` + fmt.Sprintf("%v", this.NoPivotRoot) + `,`, + `NoNewKeyring:` + fmt.Sprintf("%v", this.NoNewKeyring) + `,`, + `ShimCgroup:` + fmt.Sprintf("%v", this.ShimCgroup) + `,`, + `IoUid:` + fmt.Sprintf("%v", this.IoUid) + `,`, + `IoGid:` + fmt.Sprintf("%v", this.IoGid) + `,`, + `BinaryName:` + fmt.Sprintf("%v", this.BinaryName) + `,`, + `Root:` + fmt.Sprintf("%v", this.Root) + `,`, + `CriuPath:` + fmt.Sprintf("%v", this.CriuPath) + `,`, + `SystemdCgroup:` + fmt.Sprintf("%v", this.SystemdCgroup) + `,`, + `CriuImagePath:` + fmt.Sprintf("%v", this.CriuImagePath) + `,`, + `CriuWorkPath:` + fmt.Sprintf("%v", this.CriuWorkPath) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *CheckpointOptions) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CheckpointOptions{`, + `Exit:` + fmt.Sprintf("%v", this.Exit) + `,`, + `OpenTcp:` + fmt.Sprintf("%v", this.OpenTcp) + `,`, + `ExternalUnixSockets:` + fmt.Sprintf("%v", this.ExternalUnixSockets) + `,`, + `Terminal:` + fmt.Sprintf("%v", this.Terminal) + `,`, + `FileLocks:` + fmt.Sprintf("%v", this.FileLocks) + `,`, + `EmptyNamespaces:` + fmt.Sprintf("%v", this.EmptyNamespaces) + `,`, + `CgroupsMode:` + fmt.Sprintf("%v", this.CgroupsMode) + `,`, + `ImagePath:` + fmt.Sprintf("%v", this.ImagePath) + `,`, + `WorkPath:` + fmt.Sprintf("%v", this.WorkPath) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *ProcessDetails) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ProcessDetails{`, + `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringOci(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Options) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Options: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Options: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NoPivotRoot", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.NoPivotRoot = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NoNewKeyring", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.NoNewKeyring = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShimCgroup", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ShimCgroup = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IoUid", wireType) + } + m.IoUid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.IoUid |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IoGid", wireType) + } + m.IoGid = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.IoGid |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BinaryName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.BinaryName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Root", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Root = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CriuPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CriuPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SystemdCgroup", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.SystemdCgroup = bool(v != 0) + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CriuImagePath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CriuImagePath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CriuWorkPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CriuWorkPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthOci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CheckpointOptions) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CheckpointOptions: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CheckpointOptions: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Exit", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Exit = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OpenTcp", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.OpenTcp = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExternalUnixSockets", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ExternalUnixSockets = bool(v != 0) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Terminal", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Terminal = bool(v != 0) + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field FileLocks", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.FileLocks = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EmptyNamespaces", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EmptyNamespaces = append(m.EmptyNamespaces, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CgroupsMode", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CgroupsMode = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ImagePath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ImagePath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.WorkPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthOci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ProcessDetails) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProcessDetails: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProcessDetails: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOci + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOci + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOci + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ExecID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOci(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthOci + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipOci(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowOci + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowOci + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowOci + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthOci + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupOci + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthOci + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthOci = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowOci = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupOci = fmt.Errorf("proto: unexpected end of group") +) diff --git a/vendor/github.com/containerd/containerd/runtime/v2/runc/options/oci.proto b/vendor/github.com/containerd/containerd/runtime/v2/runc/options/oci.proto new file mode 100644 index 0000000000000..6b4bcf462c4cd --- /dev/null +++ b/vendor/github.com/containerd/containerd/runtime/v2/runc/options/oci.proto @@ -0,0 +1,58 @@ +syntax = "proto3"; + +package containerd.runc.v1; + +import weak "gogoproto/gogo.proto"; + +option go_package = "github.com/containerd/containerd/runtime/v2/runc/options;options"; + +message Options { + // disable pivot root when creating a container + bool no_pivot_root = 1; + // create a new keyring for the container + bool no_new_keyring = 2; + // place the shim in a cgroup + string shim_cgroup = 3; + // set the I/O's pipes uid + uint32 io_uid = 4; + // set the I/O's pipes gid + uint32 io_gid = 5; + // binary name of the runc binary + string binary_name = 6; + // runc root directory + string root = 7; + // criu binary path + string criu_path = 8; + // enable systemd cgroups + bool systemd_cgroup = 9; + // criu image path + string criu_image_path = 10; + // criu work path + string criu_work_path = 11; +} + +message CheckpointOptions { + // exit the container after a checkpoint + bool exit = 1; + // checkpoint open tcp connections + bool open_tcp = 2; + // checkpoint external unix sockets + bool external_unix_sockets = 3; + // checkpoint terminals (ptys) + bool terminal = 4; + // allow checkpointing of file locks + bool file_locks = 5; + // restore provided namespaces as empty namespaces + repeated string empty_namespaces = 6; + // set the cgroups mode, soft, full, strict + string cgroups_mode = 7; + // checkpoint image path + string image_path = 8; + // checkpoint work path + string work_path = 9; +} + +message ProcessDetails { + // exec process id if the process is managed by a shim + string exec_id = 1; +} diff --git a/vendor/github.com/containerd/containerd/services.go b/vendor/github.com/containerd/containerd/services.go index 395fc306510f6..d8fced55917d8 100644 --- a/vendor/github.com/containerd/containerd/services.go +++ b/vendor/github.com/containerd/containerd/services.go @@ -20,6 +20,7 @@ import ( containersapi "github.com/containerd/containerd/api/services/containers/v1" "github.com/containerd/containerd/api/services/diff/v1" imagesapi "github.com/containerd/containerd/api/services/images/v1" + introspectionapi "github.com/containerd/containerd/api/services/introspection/v1" namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1" "github.com/containerd/containerd/api/services/tasks/v1" "github.com/containerd/containerd/containers" @@ -27,19 +28,21 @@ import ( "github.com/containerd/containerd/images" "github.com/containerd/containerd/leases" "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/services/introspection" "github.com/containerd/containerd/snapshots" ) type services struct { - contentStore content.Store - imageStore images.Store - containerStore containers.Store - namespaceStore namespaces.Store - snapshotters map[string]snapshots.Snapshotter - taskService tasks.TasksClient - diffService DiffService - eventService EventService - leasesService leases.Manager + contentStore content.Store + imageStore images.Store + containerStore containers.Store + namespaceStore namespaces.Store + snapshotters map[string]snapshots.Snapshotter + taskService tasks.TasksClient + diffService DiffService + eventService EventService + leasesService leases.Manager + introspectionService introspection.Service } // ServicesOpt allows callers to set options on the services @@ -110,3 +113,10 @@ func WithLeasesService(leasesService leases.Manager) ServicesOpt { s.leasesService = leasesService } } + +// WithIntrospectionService sets the introspection service. +func WithIntrospectionService(in introspectionapi.IntrospectionClient) ServicesOpt { + return func(s *services) { + s.introspectionService = introspection.NewIntrospectionServiceFromClient(in) + } +} diff --git a/vendor/github.com/containerd/containerd/services/content/contentserver/contentserver.go b/vendor/github.com/containerd/containerd/services/content/contentserver/contentserver.go new file mode 100644 index 0000000000000..7b6efdb3a5380 --- /dev/null +++ b/vendor/github.com/containerd/containerd/services/content/contentserver/contentserver.go @@ -0,0 +1,463 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package contentserver + +import ( + "context" + "io" + "sync" + + api "github.com/containerd/containerd/api/services/content/v1" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/log" + ptypes "github.com/gogo/protobuf/types" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type service struct { + store content.Store +} + +var bufPool = sync.Pool{ + New: func() interface{} { + buffer := make([]byte, 1<<20) + return &buffer + }, +} + +// New returns the content GRPC server +func New(cs content.Store) api.ContentServer { + return &service{store: cs} +} + +func (s *service) Register(server *grpc.Server) error { + api.RegisterContentServer(server, s) + return nil +} + +func (s *service) Info(ctx context.Context, req *api.InfoRequest) (*api.InfoResponse, error) { + if err := req.Digest.Validate(); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "%q failed validation", req.Digest) + } + + bi, err := s.store.Info(ctx, req.Digest) + if err != nil { + return nil, errdefs.ToGRPC(err) + } + + return &api.InfoResponse{ + Info: infoToGRPC(bi), + }, nil +} + +func (s *service) Update(ctx context.Context, req *api.UpdateRequest) (*api.UpdateResponse, error) { + if err := req.Info.Digest.Validate(); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "%q failed validation", req.Info.Digest) + } + + info, err := s.store.Update(ctx, infoFromGRPC(req.Info), req.UpdateMask.GetPaths()...) + if err != nil { + return nil, errdefs.ToGRPC(err) + } + + return &api.UpdateResponse{ + Info: infoToGRPC(info), + }, nil +} + +func (s *service) List(req *api.ListContentRequest, session api.Content_ListServer) error { + var ( + buffer []api.Info + sendBlock = func(block []api.Info) error { + // send last block + return session.Send(&api.ListContentResponse{ + Info: block, + }) + } + ) + + if err := s.store.Walk(session.Context(), func(info content.Info) error { + buffer = append(buffer, api.Info{ + Digest: info.Digest, + Size_: info.Size, + CreatedAt: info.CreatedAt, + Labels: info.Labels, + }) + + if len(buffer) >= 100 { + if err := sendBlock(buffer); err != nil { + return err + } + + buffer = buffer[:0] + } + + return nil + }, req.Filters...); err != nil { + return errdefs.ToGRPC(err) + } + + if len(buffer) > 0 { + // send last block + if err := sendBlock(buffer); err != nil { + return err + } + } + + return nil +} + +func (s *service) Delete(ctx context.Context, req *api.DeleteContentRequest) (*ptypes.Empty, error) { + log.G(ctx).WithField("digest", req.Digest).Debugf("delete content") + if err := req.Digest.Validate(); err != nil { + return nil, status.Errorf(codes.InvalidArgument, err.Error()) + } + + if err := s.store.Delete(ctx, req.Digest); err != nil { + return nil, errdefs.ToGRPC(err) + } + + return &ptypes.Empty{}, nil +} + +func (s *service) Read(req *api.ReadContentRequest, session api.Content_ReadServer) error { + if err := req.Digest.Validate(); err != nil { + return status.Errorf(codes.InvalidArgument, "%v: %v", req.Digest, err) + } + + oi, err := s.store.Info(session.Context(), req.Digest) + if err != nil { + return errdefs.ToGRPC(err) + } + + ra, err := s.store.ReaderAt(session.Context(), ocispec.Descriptor{Digest: req.Digest}) + if err != nil { + return errdefs.ToGRPC(err) + } + defer ra.Close() + + var ( + offset = req.Offset + // size is read size, not the expected size of the blob (oi.Size), which the caller might not be aware of. + // offset+size can be larger than oi.Size. + size = req.Size_ + + // TODO(stevvooe): Using the global buffer pool. At 32KB, it is probably + // little inefficient for work over a fast network. We can tune this later. + p = bufPool.Get().(*[]byte) + ) + defer bufPool.Put(p) + + if offset < 0 { + offset = 0 + } + + if offset > oi.Size { + return status.Errorf(codes.OutOfRange, "read past object length %v bytes", oi.Size) + } + + if size <= 0 || offset+size > oi.Size { + size = oi.Size - offset + } + + _, err = io.CopyBuffer( + &readResponseWriter{session: session}, + io.NewSectionReader(ra, offset, size), *p) + return errdefs.ToGRPC(err) +} + +// readResponseWriter is a writer that places the output into ReadContentRequest messages. +// +// This allows io.CopyBuffer to do the heavy lifting of chunking the responses +// into the buffer size. +type readResponseWriter struct { + offset int64 + session api.Content_ReadServer +} + +func (rw *readResponseWriter) Write(p []byte) (n int, err error) { + if err := rw.session.Send(&api.ReadContentResponse{ + Offset: rw.offset, + Data: p, + }); err != nil { + return 0, err + } + + rw.offset += int64(len(p)) + return len(p), nil +} + +func (s *service) Status(ctx context.Context, req *api.StatusRequest) (*api.StatusResponse, error) { + status, err := s.store.Status(ctx, req.Ref) + if err != nil { + return nil, errdefs.ToGRPCf(err, "could not get status for ref %q", req.Ref) + } + + var resp api.StatusResponse + resp.Status = &api.Status{ + StartedAt: status.StartedAt, + UpdatedAt: status.UpdatedAt, + Ref: status.Ref, + Offset: status.Offset, + Total: status.Total, + Expected: status.Expected, + } + + return &resp, nil +} + +func (s *service) ListStatuses(ctx context.Context, req *api.ListStatusesRequest) (*api.ListStatusesResponse, error) { + statuses, err := s.store.ListStatuses(ctx, req.Filters...) + if err != nil { + return nil, errdefs.ToGRPC(err) + } + + var resp api.ListStatusesResponse + for _, status := range statuses { + resp.Statuses = append(resp.Statuses, api.Status{ + StartedAt: status.StartedAt, + UpdatedAt: status.UpdatedAt, + Ref: status.Ref, + Offset: status.Offset, + Total: status.Total, + Expected: status.Expected, + }) + } + + return &resp, nil +} + +func (s *service) Write(session api.Content_WriteServer) (err error) { + var ( + ctx = session.Context() + msg api.WriteContentResponse + req *api.WriteContentRequest + ref string + total int64 + expected digest.Digest + ) + + defer func(msg *api.WriteContentResponse) { + // pump through the last message if no error was encountered + if err != nil { + if s, ok := status.FromError(err); ok && s.Code() != codes.AlreadyExists { + // TODO(stevvooe): Really need a log line here to track which + // errors are actually causing failure on the server side. May want + // to configure the service with an interceptor to make this work + // identically across all GRPC methods. + // + // This is pretty noisy, so we can remove it but leave it for now. + log.G(ctx).WithError(err).Error("(*service).Write failed") + } + + return + } + + err = session.Send(msg) + }(&msg) + + // handle the very first request! + req, err = session.Recv() + if err != nil { + return err + } + + ref = req.Ref + + if ref == "" { + return status.Errorf(codes.InvalidArgument, "first message must have a reference") + } + + fields := logrus.Fields{ + "ref": ref, + } + total = req.Total + expected = req.Expected + if total > 0 { + fields["total"] = total + } + + if expected != "" { + fields["expected"] = expected + } + + ctx = log.WithLogger(ctx, log.G(ctx).WithFields(fields)) + + log.G(ctx).Debug("(*service).Write started") + // this action locks the writer for the session. + wr, err := s.store.Writer(ctx, + content.WithRef(ref), + content.WithDescriptor(ocispec.Descriptor{Size: total, Digest: expected})) + if err != nil { + return errdefs.ToGRPC(err) + } + defer wr.Close() + + for { + msg.Action = req.Action + ws, err := wr.Status() + if err != nil { + return errdefs.ToGRPC(err) + } + + msg.Offset = ws.Offset // always set the offset. + + // NOTE(stevvooe): In general, there are two cases underwhich a remote + // writer is used. + // + // For pull, we almost always have this before fetching large content, + // through descriptors. We allow predeclaration of the expected size + // and digest. + // + // For push, it is more complex. If we want to cut through content into + // storage, we may have no expectation until we are done processing the + // content. The case here is the following: + // + // 1. Start writing content. + // 2. Compress inline. + // 3. Validate digest and size (maybe). + // + // Supporting these two paths is quite awkward but it lets both API + // users use the same writer style for each with a minimum of overhead. + if req.Expected != "" { + if expected != "" && expected != req.Expected { + log.G(ctx).Debugf("commit digest differs from writer digest: %v != %v", req.Expected, expected) + } + expected = req.Expected + + if _, err := s.store.Info(session.Context(), req.Expected); err == nil { + if err := wr.Close(); err != nil { + log.G(ctx).WithError(err).Error("failed to close writer") + } + if err := s.store.Abort(session.Context(), ref); err != nil { + log.G(ctx).WithError(err).Error("failed to abort write") + } + + return status.Errorf(codes.AlreadyExists, "blob with expected digest %v exists", req.Expected) + } + } + + if req.Total > 0 { + // Update the expected total. Typically, this could be seen at + // negotiation time or on a commit message. + if total > 0 && req.Total != total { + log.G(ctx).Debugf("commit size differs from writer size: %v != %v", req.Total, total) + } + total = req.Total + } + + switch req.Action { + case api.WriteActionStat: + msg.Digest = wr.Digest() + msg.StartedAt = ws.StartedAt + msg.UpdatedAt = ws.UpdatedAt + msg.Total = total + case api.WriteActionWrite, api.WriteActionCommit: + if req.Offset > 0 { + // validate the offset if provided + if req.Offset != ws.Offset { + return status.Errorf(codes.OutOfRange, "write @%v must occur at current offset %v", req.Offset, ws.Offset) + } + } + + if req.Offset == 0 && ws.Offset > 0 { + if err := wr.Truncate(req.Offset); err != nil { + return errors.Wrapf(err, "truncate failed") + } + msg.Offset = req.Offset + } + + // issue the write if we actually have data. + if len(req.Data) > 0 { + // While this looks like we could use io.WriterAt here, because we + // maintain the offset as append only, we just issue the write. + n, err := wr.Write(req.Data) + if err != nil { + return errdefs.ToGRPC(err) + } + + if n != len(req.Data) { + // TODO(stevvooe): Perhaps, we can recover this by including it + // in the offset on the write return. + return status.Errorf(codes.DataLoss, "wrote %v of %v bytes", n, len(req.Data)) + } + + msg.Offset += int64(n) + } + + if req.Action == api.WriteActionCommit { + var opts []content.Opt + if req.Labels != nil { + opts = append(opts, content.WithLabels(req.Labels)) + } + if err := wr.Commit(ctx, total, expected, opts...); err != nil { + return errdefs.ToGRPC(err) + } + } + + msg.Digest = wr.Digest() + } + + if err := session.Send(&msg); err != nil { + return err + } + + req, err = session.Recv() + if err != nil { + if err == io.EOF { + return nil + } + + return err + } + } +} + +func (s *service) Abort(ctx context.Context, req *api.AbortRequest) (*ptypes.Empty, error) { + if err := s.store.Abort(ctx, req.Ref); err != nil { + return nil, errdefs.ToGRPC(err) + } + + return &ptypes.Empty{}, nil +} + +func infoToGRPC(info content.Info) api.Info { + return api.Info{ + Digest: info.Digest, + Size_: info.Size, + CreatedAt: info.CreatedAt, + UpdatedAt: info.UpdatedAt, + Labels: info.Labels, + } +} + +func infoFromGRPC(info api.Info) content.Info { + return content.Info{ + Digest: info.Digest, + Size: info.Size_, + CreatedAt: info.CreatedAt, + UpdatedAt: info.UpdatedAt, + Labels: info.Labels, + } +} diff --git a/vendor/github.com/containerd/containerd/services/introspection/introspection.go b/vendor/github.com/containerd/containerd/services/introspection/introspection.go new file mode 100644 index 0000000000000..71758fad88b86 --- /dev/null +++ b/vendor/github.com/containerd/containerd/services/introspection/introspection.go @@ -0,0 +1,66 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package introspection + +import ( + context "context" + + api "github.com/containerd/containerd/api/services/introspection/v1" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/log" + ptypes "github.com/gogo/protobuf/types" +) + +// Service defines the instrospection service interface +type Service interface { + Plugins(context.Context, []string) (*api.PluginsResponse, error) + Server(context.Context, *ptypes.Empty) (*api.ServerResponse, error) +} + +type introspectionRemote struct { + client api.IntrospectionClient +} + +var _ = (Service)(&introspectionRemote{}) + +// NewIntrospectionServiceFromClient creates a new introspection service from an API client +func NewIntrospectionServiceFromClient(c api.IntrospectionClient) Service { + return &introspectionRemote{client: c} +} + +func (i *introspectionRemote) Plugins(ctx context.Context, filters []string) (*api.PluginsResponse, error) { + log.G(ctx).WithField("filters", filters).Debug("remote introspection plugin filters") + resp, err := i.client.Plugins(ctx, &api.PluginsRequest{ + Filters: filters, + }) + + if err != nil { + return nil, errdefs.FromGRPC(err) + } + + return resp, nil +} + +func (i *introspectionRemote) Server(ctx context.Context, in *ptypes.Empty) (*api.ServerResponse, error) { + resp, err := i.client.Server(ctx, in) + + if err != nil { + return nil, errdefs.FromGRPC(err) + } + + return resp, nil +} diff --git a/vendor/github.com/containerd/containerd/services/introspection/local.go b/vendor/github.com/containerd/containerd/services/introspection/local.go new file mode 100644 index 0000000000000..988f1e834bdbc --- /dev/null +++ b/vendor/github.com/containerd/containerd/services/introspection/local.go @@ -0,0 +1,231 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package introspection + +import ( + context "context" + "io/ioutil" + "os" + "path/filepath" + "sync" + + api "github.com/containerd/containerd/api/services/introspection/v1" + "github.com/containerd/containerd/api/types" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/filters" + "github.com/containerd/containerd/plugin" + "github.com/containerd/containerd/services" + "github.com/gogo/googleapis/google/rpc" + ptypes "github.com/gogo/protobuf/types" + "github.com/google/uuid" + "google.golang.org/grpc" + "google.golang.org/grpc/status" +) + +func init() { + plugin.Register(&plugin.Registration{ + Type: plugin.ServicePlugin, + ID: services.IntrospectionService, + Requires: []plugin.Type{}, + InitFn: func(ic *plugin.InitContext) (interface{}, error) { + // this service works by using the plugin context up till the point + // this service is initialized. Since we require this service last, + // it should provide the full set of plugins. + pluginsPB := pluginsToPB(ic.GetAll()) + return &Local{ + plugins: pluginsPB, + root: ic.Root, + }, nil + }, + }) +} + +// Local is a local implementation of the introspection service +type Local struct { + mu sync.Mutex + plugins []api.Plugin + root string +} + +var _ = (api.IntrospectionClient)(&Local{}) + +// UpdateLocal updates the local introspection service +func (l *Local) UpdateLocal(root string, plugins []api.Plugin) { + l.mu.Lock() + defer l.mu.Unlock() + l.root = root + l.plugins = plugins +} + +// Plugins returns the locally defined plugins +func (l *Local) Plugins(ctx context.Context, req *api.PluginsRequest, _ ...grpc.CallOption) (*api.PluginsResponse, error) { + filter, err := filters.ParseAll(req.Filters...) + if err != nil { + return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, err.Error()) + } + + var plugins []api.Plugin + allPlugins := l.getPlugins() + for _, p := range allPlugins { + if !filter.Match(adaptPlugin(p)) { + continue + } + + plugins = append(plugins, p) + } + + return &api.PluginsResponse{ + Plugins: plugins, + }, nil +} + +func (l *Local) getPlugins() []api.Plugin { + l.mu.Lock() + defer l.mu.Unlock() + return l.plugins +} + +// Server returns the local server information +func (l *Local) Server(ctx context.Context, _ *ptypes.Empty, _ ...grpc.CallOption) (*api.ServerResponse, error) { + u, err := l.getUUID() + if err != nil { + return nil, errdefs.ToGRPC(err) + } + return &api.ServerResponse{ + UUID: u, + }, nil +} + +func (l *Local) getUUID() (string, error) { + l.mu.Lock() + defer l.mu.Unlock() + + data, err := ioutil.ReadFile(l.uuidPath()) + if err != nil { + if os.IsNotExist(err) { + return l.generateUUID() + } + return "", err + } + u := string(data) + if _, err := uuid.Parse(u); err != nil { + return "", err + } + return u, nil +} + +func (l *Local) generateUUID() (string, error) { + u, err := uuid.NewRandom() + if err != nil { + return "", err + } + path := l.uuidPath() + if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { + return "", err + } + uu := u.String() + if err := ioutil.WriteFile(path, []byte(uu), 0666); err != nil { + return "", err + } + return uu, nil +} + +func (l *Local) uuidPath() string { + return filepath.Join(l.root, "uuid") +} + +func adaptPlugin(o interface{}) filters.Adaptor { + obj := o.(api.Plugin) + return filters.AdapterFunc(func(fieldpath []string) (string, bool) { + if len(fieldpath) == 0 { + return "", false + } + + switch fieldpath[0] { + case "type": + return obj.Type, len(obj.Type) > 0 + case "id": + return obj.ID, len(obj.ID) > 0 + case "platforms": + // TODO(stevvooe): Another case here where have multiple values. + // May need to refactor the filter system to allow filtering by + // platform, if this is required. + case "capabilities": + // TODO(stevvooe): Need a better way to match against + // collections. We can only return "the value" but really it + // would be best if we could return a set of values for the + // path, any of which could match. + } + + return "", false + }) +} + +func pluginsToPB(plugins []*plugin.Plugin) []api.Plugin { + var pluginsPB []api.Plugin + for _, p := range plugins { + var platforms []types.Platform + for _, p := range p.Meta.Platforms { + platforms = append(platforms, types.Platform{ + OS: p.OS, + Architecture: p.Architecture, + Variant: p.Variant, + }) + } + + var requires []string + for _, r := range p.Registration.Requires { + requires = append(requires, r.String()) + } + + var initErr *rpc.Status + if err := p.Err(); err != nil { + st, ok := status.FromError(errdefs.ToGRPC(err)) + if ok { + var details []*ptypes.Any + for _, d := range st.Proto().Details { + details = append(details, &ptypes.Any{ + TypeUrl: d.TypeUrl, + Value: d.Value, + }) + } + initErr = &rpc.Status{ + Code: int32(st.Code()), + Message: st.Message(), + Details: details, + } + } else { + initErr = &rpc.Status{ + Code: int32(rpc.UNKNOWN), + Message: err.Error(), + } + } + } + + pluginsPB = append(pluginsPB, api.Plugin{ + Type: p.Registration.Type.String(), + ID: p.Registration.ID, + Requires: requires, + Platforms: platforms, + Capabilities: p.Meta.Capabilities, + Exports: p.Meta.Exports, + InitErr: initErr, + }) + } + + return pluginsPB +} diff --git a/vendor/github.com/containerd/containerd/services/introspection/service.go b/vendor/github.com/containerd/containerd/services/introspection/service.go new file mode 100644 index 0000000000000..b8c54afc028bd --- /dev/null +++ b/vendor/github.com/containerd/containerd/services/introspection/service.go @@ -0,0 +1,85 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package introspection + +import ( + context "context" + + api "github.com/containerd/containerd/api/services/introspection/v1" + "github.com/containerd/containerd/plugin" + "github.com/containerd/containerd/services" + ptypes "github.com/gogo/protobuf/types" + "github.com/pkg/errors" + "google.golang.org/grpc" +) + +func init() { + plugin.Register(&plugin.Registration{ + Type: plugin.GRPCPlugin, + ID: "introspection", + Requires: []plugin.Type{"*"}, + InitFn: func(ic *plugin.InitContext) (interface{}, error) { + // this service works by using the plugin context up till the point + // this service is initialized. Since we require this service last, + // it should provide the full set of plugins. + plugins, err := ic.GetByType(plugin.ServicePlugin) + if err != nil { + return nil, err + } + p, ok := plugins[services.IntrospectionService] + if !ok { + return nil, errors.New("introspection service not found") + } + + i, err := p.Instance() + if err != nil { + return nil, err + } + + allPluginsPB := pluginsToPB(ic.GetAll()) + + localClient, ok := i.(*Local) + if !ok { + return nil, errors.Errorf("Could not create a local client for introspection service") + } + localClient.UpdateLocal(ic.Root, allPluginsPB) + + return &server{ + local: localClient, + }, nil + }, + }) +} + +type server struct { + local api.IntrospectionClient +} + +var _ = (api.IntrospectionServer)(&server{}) + +func (s *server) Register(server *grpc.Server) error { + api.RegisterIntrospectionServer(server, s) + return nil +} + +func (s *server) Plugins(ctx context.Context, req *api.PluginsRequest) (*api.PluginsResponse, error) { + return s.local.Plugins(ctx, req) +} + +func (s *server) Server(ctx context.Context, empty *ptypes.Empty) (*api.ServerResponse, error) { + return s.local.Server(ctx, empty) +} diff --git a/vendor/github.com/containerd/containerd/services/server/config.go b/vendor/github.com/containerd/containerd/services/server/config.go deleted file mode 100644 index a91d5fffd9ee1..0000000000000 --- a/vendor/github.com/containerd/containerd/services/server/config.go +++ /dev/null @@ -1,109 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package server - -import ( - "github.com/BurntSushi/toml" - "github.com/containerd/containerd/errdefs" - "github.com/pkg/errors" -) - -// Config provides containerd configuration data for the server -type Config struct { - // Root is the path to a directory where containerd will store persistent data - Root string `toml:"root"` - // State is the path to a directory where containerd will store transient data - State string `toml:"state"` - // GRPC configuration settings - GRPC GRPCConfig `toml:"grpc"` - // Debug and profiling settings - Debug Debug `toml:"debug"` - // Metrics and monitoring settings - Metrics MetricsConfig `toml:"metrics"` - // DisabledPlugins are IDs of plugins to disable. Disabled plugins won't be - // initialized and started. - DisabledPlugins []string `toml:"disabled_plugins"` - // Plugins provides plugin specific configuration for the initialization of a plugin - Plugins map[string]toml.Primitive `toml:"plugins"` - // OOMScore adjust the containerd's oom score - OOMScore int `toml:"oom_score"` - // Cgroup specifies cgroup information for the containerd daemon process - Cgroup CgroupConfig `toml:"cgroup"` - // ProxyPlugins configures plugins which are communicated to over GRPC - ProxyPlugins map[string]ProxyPlugin `toml:"proxy_plugins"` - - md toml.MetaData -} - -// GRPCConfig provides GRPC configuration for the socket -type GRPCConfig struct { - Address string `toml:"address"` - UID int `toml:"uid"` - GID int `toml:"gid"` - MaxRecvMsgSize int `toml:"max_recv_message_size"` - MaxSendMsgSize int `toml:"max_send_message_size"` -} - -// Debug provides debug configuration -type Debug struct { - Address string `toml:"address"` - UID int `toml:"uid"` - GID int `toml:"gid"` - Level string `toml:"level"` -} - -// MetricsConfig provides metrics configuration -type MetricsConfig struct { - Address string `toml:"address"` - GRPCHistogram bool `toml:"grpc_histogram"` -} - -// CgroupConfig provides cgroup configuration -type CgroupConfig struct { - Path string `toml:"path"` -} - -// ProxyPlugin provides a proxy plugin configuration -type ProxyPlugin struct { - Type string `toml:"type"` - Address string `toml:"address"` -} - -// Decode unmarshals a plugin specific configuration by plugin id -func (c *Config) Decode(id string, v interface{}) (interface{}, error) { - data, ok := c.Plugins[id] - if !ok { - return v, nil - } - if err := c.md.PrimitiveDecode(data, v); err != nil { - return nil, err - } - return v, nil -} - -// LoadConfig loads the containerd server config from the provided path -func LoadConfig(path string, v *Config) error { - if v == nil { - return errors.Wrapf(errdefs.ErrInvalidArgument, "argument v must not be nil") - } - md, err := toml.DecodeFile(path, v) - if err != nil { - return err - } - v.md = md - return nil -} diff --git a/vendor/github.com/containerd/containerd/services/server/config/config.go b/vendor/github.com/containerd/containerd/services/server/config/config.go new file mode 100644 index 0000000000000..6aafe36a5d6cd --- /dev/null +++ b/vendor/github.com/containerd/containerd/services/server/config/config.go @@ -0,0 +1,372 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package config + +import ( + "path/filepath" + "strings" + + "github.com/imdario/mergo" + "github.com/pelletier/go-toml" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/plugin" +) + +// NOTE: Any new map fields added also need to be handled in mergeConfig. + +// Config provides containerd configuration data for the server +type Config struct { + // Version of the config file + Version int `toml:"version"` + // Root is the path to a directory where containerd will store persistent data + Root string `toml:"root"` + // State is the path to a directory where containerd will store transient data + State string `toml:"state"` + // PluginDir is the directory for dynamic plugins to be stored + PluginDir string `toml:"plugin_dir"` + // GRPC configuration settings + GRPC GRPCConfig `toml:"grpc"` + // TTRPC configuration settings + TTRPC TTRPCConfig `toml:"ttrpc"` + // Debug and profiling settings + Debug Debug `toml:"debug"` + // Metrics and monitoring settings + Metrics MetricsConfig `toml:"metrics"` + // DisabledPlugins are IDs of plugins to disable. Disabled plugins won't be + // initialized and started. + DisabledPlugins []string `toml:"disabled_plugins"` + // RequiredPlugins are IDs of required plugins. Containerd exits if any + // required plugin doesn't exist or fails to be initialized or started. + RequiredPlugins []string `toml:"required_plugins"` + // Plugins provides plugin specific configuration for the initialization of a plugin + Plugins map[string]toml.Tree `toml:"plugins"` + // OOMScore adjust the containerd's oom score + OOMScore int `toml:"oom_score"` + // Cgroup specifies cgroup information for the containerd daemon process + Cgroup CgroupConfig `toml:"cgroup"` + // ProxyPlugins configures plugins which are communicated to over GRPC + ProxyPlugins map[string]ProxyPlugin `toml:"proxy_plugins"` + // Timeouts specified as a duration + Timeouts map[string]string `toml:"timeouts"` + // Imports are additional file path list to config files that can overwrite main config file fields + Imports []string `toml:"imports"` + + StreamProcessors map[string]StreamProcessor `toml:"stream_processors"` +} + +// StreamProcessor provides configuration for diff content processors +type StreamProcessor struct { + // Accepts specific media-types + Accepts []string `toml:"accepts"` + // Returns the media-type + Returns string `toml:"returns"` + // Path or name of the binary + Path string `toml:"path"` + // Args to the binary + Args []string `toml:"args"` + // Environment variables for the binary + Env []string `toml:"env"` +} + +// GetVersion returns the config file's version +func (c *Config) GetVersion() int { + if c.Version == 0 { + return 1 + } + return c.Version +} + +// ValidateV2 validates the config for a v2 file +func (c *Config) ValidateV2() error { + version := c.GetVersion() + if version < 2 { + logrus.Warnf("deprecated version : `%d`, please switch to version `2`", version) + return nil + } + for _, p := range c.DisabledPlugins { + if len(strings.Split(p, ".")) < 4 { + return errors.Errorf("invalid disabled plugin URI %q expect io.containerd.x.vx", p) + } + } + for _, p := range c.RequiredPlugins { + if len(strings.Split(p, ".")) < 4 { + return errors.Errorf("invalid required plugin URI %q expect io.containerd.x.vx", p) + } + } + for p := range c.Plugins { + if len(strings.Split(p, ".")) < 4 { + return errors.Errorf("invalid plugin key URI %q expect io.containerd.x.vx", p) + } + } + return nil +} + +// GRPCConfig provides GRPC configuration for the socket +type GRPCConfig struct { + Address string `toml:"address"` + TCPAddress string `toml:"tcp_address"` + TCPTLSCert string `toml:"tcp_tls_cert"` + TCPTLSKey string `toml:"tcp_tls_key"` + UID int `toml:"uid"` + GID int `toml:"gid"` + MaxRecvMsgSize int `toml:"max_recv_message_size"` + MaxSendMsgSize int `toml:"max_send_message_size"` +} + +// TTRPCConfig provides TTRPC configuration for the socket +type TTRPCConfig struct { + Address string `toml:"address"` + UID int `toml:"uid"` + GID int `toml:"gid"` +} + +// Debug provides debug configuration +type Debug struct { + Address string `toml:"address"` + UID int `toml:"uid"` + GID int `toml:"gid"` + Level string `toml:"level"` + // Format represents the logging format + Format string `toml:"format"` +} + +// MetricsConfig provides metrics configuration +type MetricsConfig struct { + Address string `toml:"address"` + GRPCHistogram bool `toml:"grpc_histogram"` +} + +// CgroupConfig provides cgroup configuration +type CgroupConfig struct { + Path string `toml:"path"` +} + +// ProxyPlugin provides a proxy plugin configuration +type ProxyPlugin struct { + Type string `toml:"type"` + Address string `toml:"address"` +} + +// BoltConfig defines the configuration values for the bolt plugin, which is +// loaded here, rather than back registered in the metadata package. +type BoltConfig struct { + // ContentSharingPolicy sets the sharing policy for content between + // namespaces. + // + // The default mode "shared" will make blobs available in all + // namespaces once it is pulled into any namespace. The blob will be pulled + // into the namespace if a writer is opened with the "Expected" digest that + // is already present in the backend. + // + // The alternative mode, "isolated" requires that clients prove they have + // access to the content by providing all of the content to the ingest + // before the blob is added to the namespace. + // + // Both modes share backing data, while "shared" will reduce total + // bandwidth across namespaces, at the cost of allowing access to any blob + // just by knowing its digest. + ContentSharingPolicy string `toml:"content_sharing_policy"` +} + +const ( + // SharingPolicyShared represents the "shared" sharing policy + SharingPolicyShared = "shared" + // SharingPolicyIsolated represents the "isolated" sharing policy + SharingPolicyIsolated = "isolated" +) + +// Validate validates if BoltConfig is valid +func (bc *BoltConfig) Validate() error { + switch bc.ContentSharingPolicy { + case SharingPolicyShared, SharingPolicyIsolated: + return nil + default: + return errors.Wrapf(errdefs.ErrInvalidArgument, "unknown policy: %s", bc.ContentSharingPolicy) + } +} + +// Decode unmarshals a plugin specific configuration by plugin id +func (c *Config) Decode(p *plugin.Registration) (interface{}, error) { + id := p.URI() + if c.GetVersion() == 1 { + id = p.ID + } + data, ok := c.Plugins[id] + if !ok { + return p.Config, nil + } + if err := data.Unmarshal(p.Config); err != nil { + return nil, err + } + return p.Config, nil +} + +// LoadConfig loads the containerd server config from the provided path +func LoadConfig(path string, out *Config) error { + if out == nil { + return errors.Wrapf(errdefs.ErrInvalidArgument, "argument out must not be nil") + } + + var ( + loaded = map[string]bool{} + pending = []string{path} + ) + + for len(pending) > 0 { + path, pending = pending[0], pending[1:] + + // Check if a file at the given path already loaded to prevent circular imports + if _, ok := loaded[path]; ok { + continue + } + + config, err := loadConfigFile(path) + if err != nil { + return err + } + + if err := mergeConfig(out, config); err != nil { + return err + } + + imports, err := resolveImports(path, config.Imports) + if err != nil { + return err + } + + loaded[path] = true + pending = append(pending, imports...) + } + + // Fix up the list of config files loaded + out.Imports = []string{} + for path := range loaded { + out.Imports = append(out.Imports, path) + } + + err := out.ValidateV2() + if err != nil { + return errors.Wrapf(err, "failed to load TOML from %s", path) + } + return nil +} + +// loadConfigFile decodes a TOML file at the given path +func loadConfigFile(path string) (*Config, error) { + config := &Config{} + + file, err := toml.LoadFile(path) + if err != nil { + return nil, errors.Wrapf(err, "failed to load TOML: %s", path) + } + + if err := file.Unmarshal(config); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal TOML") + } + + return config, nil +} + +// resolveImports resolves import strings list to absolute paths list: +// - If path contains *, glob pattern matching applied +// - Non abs path is relative to parent config file directory +// - Abs paths returned as is +func resolveImports(parent string, imports []string) ([]string, error) { + var out []string + + for _, path := range imports { + if strings.Contains(path, "*") { + matches, err := filepath.Glob(path) + if err != nil { + return nil, err + } + + out = append(out, matches...) + } else { + path = filepath.Clean(path) + if !filepath.IsAbs(path) { + path = filepath.Join(filepath.Dir(parent), path) + } + + out = append(out, path) + } + } + + return out, nil +} + +// mergeConfig merges Config structs with the following rules: +// 'to' 'from' 'result' +// "" "value" "value" +// "value" "" "value" +// 1 0 1 +// 0 1 1 +// []{"1"} []{"2"} []{"1","2"} +// []{"1"} []{} []{"1"} +// Maps merged by keys, but values are replaced entirely. +func mergeConfig(to, from *Config) error { + err := mergo.Merge(to, from, mergo.WithOverride, mergo.WithAppendSlice) + if err != nil { + return err + } + + // Replace entire sections instead of merging map's values. + for k, v := range from.Plugins { + to.Plugins[k] = v + } + + for k, v := range from.StreamProcessors { + to.StreamProcessors[k] = v + } + + for k, v := range from.ProxyPlugins { + to.ProxyPlugins[k] = v + } + + for k, v := range from.Timeouts { + to.Timeouts[k] = v + } + + return nil +} + +// V1DisabledFilter matches based on ID +func V1DisabledFilter(list []string) plugin.DisableFilter { + set := make(map[string]struct{}, len(list)) + for _, l := range list { + set[l] = struct{}{} + } + return func(r *plugin.Registration) bool { + _, ok := set[r.ID] + return ok + } +} + +// V2DisabledFilter matches based on URI +func V2DisabledFilter(list []string) plugin.DisableFilter { + set := make(map[string]struct{}, len(list)) + for _, l := range list { + set[l] = struct{}{} + } + return func(r *plugin.Registration) bool { + _, ok := set[r.URI()] + return ok + } +} diff --git a/vendor/github.com/containerd/containerd/services/server/server.go b/vendor/github.com/containerd/containerd/services/server/server.go deleted file mode 100644 index ed10766f3d9c1..0000000000000 --- a/vendor/github.com/containerd/containerd/services/server/server.go +++ /dev/null @@ -1,362 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package server - -import ( - "context" - "expvar" - "io" - "net" - "net/http" - "net/http/pprof" - "os" - "path/filepath" - "strings" - "sync" - "time" - - csapi "github.com/containerd/containerd/api/services/content/v1" - ssapi "github.com/containerd/containerd/api/services/snapshots/v1" - "github.com/containerd/containerd/content" - "github.com/containerd/containerd/content/local" - csproxy "github.com/containerd/containerd/content/proxy" - "github.com/containerd/containerd/defaults" - "github.com/containerd/containerd/events/exchange" - "github.com/containerd/containerd/log" - "github.com/containerd/containerd/metadata" - "github.com/containerd/containerd/pkg/dialer" - "github.com/containerd/containerd/plugin" - "github.com/containerd/containerd/snapshots" - ssproxy "github.com/containerd/containerd/snapshots/proxy" - metrics "github.com/docker/go-metrics" - grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" - "github.com/pkg/errors" - bolt "go.etcd.io/bbolt" - "google.golang.org/grpc" -) - -// New creates and initializes a new containerd server -func New(ctx context.Context, config *Config) (*Server, error) { - switch { - case config.Root == "": - return nil, errors.New("root must be specified") - case config.State == "": - return nil, errors.New("state must be specified") - case config.Root == config.State: - return nil, errors.New("root and state must be different paths") - } - - if err := os.MkdirAll(config.Root, 0711); err != nil { - return nil, err - } - if err := os.MkdirAll(config.State, 0711); err != nil { - return nil, err - } - if err := apply(ctx, config); err != nil { - return nil, err - } - plugins, err := LoadPlugins(ctx, config) - if err != nil { - return nil, err - } - - serverOpts := []grpc.ServerOption{ - grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor), - grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor), - } - if config.GRPC.MaxRecvMsgSize > 0 { - serverOpts = append(serverOpts, grpc.MaxRecvMsgSize(config.GRPC.MaxRecvMsgSize)) - } - if config.GRPC.MaxSendMsgSize > 0 { - serverOpts = append(serverOpts, grpc.MaxSendMsgSize(config.GRPC.MaxSendMsgSize)) - } - rpc := grpc.NewServer(serverOpts...) - var ( - services []plugin.Service - s = &Server{ - rpc: rpc, - events: exchange.NewExchange(), - config: config, - } - initialized = plugin.NewPluginSet() - ) - for _, p := range plugins { - id := p.URI() - log.G(ctx).WithField("type", p.Type).Infof("loading plugin %q...", id) - - initContext := plugin.NewContext( - ctx, - p, - initialized, - config.Root, - config.State, - ) - initContext.Events = s.events - initContext.Address = config.GRPC.Address - - // load the plugin specific configuration if it is provided - if p.Config != nil { - pluginConfig, err := config.Decode(p.ID, p.Config) - if err != nil { - return nil, err - } - initContext.Config = pluginConfig - } - result := p.Init(initContext) - if err := initialized.Add(result); err != nil { - return nil, errors.Wrapf(err, "could not add plugin result to plugin set") - } - - instance, err := result.Instance() - if err != nil { - if plugin.IsSkipPlugin(err) { - log.G(ctx).WithField("type", p.Type).Infof("skip loading plugin %q...", id) - } else { - log.G(ctx).WithError(err).Warnf("failed to load plugin %s", id) - } - continue - } - // check for grpc services that should be registered with the server - if service, ok := instance.(plugin.Service); ok { - services = append(services, service) - } - s.plugins = append(s.plugins, result) - } - // register services after all plugins have been initialized - for _, service := range services { - if err := service.Register(rpc); err != nil { - return nil, err - } - } - return s, nil -} - -// Server is the containerd main daemon -type Server struct { - rpc *grpc.Server - events *exchange.Exchange - config *Config - plugins []*plugin.Plugin -} - -// ServeGRPC provides the containerd grpc APIs on the provided listener -func (s *Server) ServeGRPC(l net.Listener) error { - if s.config.Metrics.GRPCHistogram { - // enable grpc time histograms to measure rpc latencies - grpc_prometheus.EnableHandlingTimeHistogram() - } - // before we start serving the grpc API register the grpc_prometheus metrics - // handler. This needs to be the last service registered so that it can collect - // metrics for every other service - grpc_prometheus.Register(s.rpc) - return trapClosedConnErr(s.rpc.Serve(l)) -} - -// ServeMetrics provides a prometheus endpoint for exposing metrics -func (s *Server) ServeMetrics(l net.Listener) error { - m := http.NewServeMux() - m.Handle("/v1/metrics", metrics.Handler()) - return trapClosedConnErr(http.Serve(l, m)) -} - -// ServeDebug provides a debug endpoint -func (s *Server) ServeDebug(l net.Listener) error { - // don't use the default http server mux to make sure nothing gets registered - // that we don't want to expose via containerd - m := http.NewServeMux() - m.Handle("/debug/vars", expvar.Handler()) - m.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index)) - m.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline)) - m.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile)) - m.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) - m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) - return trapClosedConnErr(http.Serve(l, m)) -} - -// Stop the containerd server canceling any open connections -func (s *Server) Stop() { - s.rpc.Stop() - for i := len(s.plugins) - 1; i >= 0; i-- { - p := s.plugins[i] - instance, err := p.Instance() - if err != nil { - log.L.WithError(err).WithField("id", p.Registration.ID). - Errorf("could not get plugin instance") - continue - } - closer, ok := instance.(io.Closer) - if !ok { - continue - } - if err := closer.Close(); err != nil { - log.L.WithError(err).WithField("id", p.Registration.ID). - Errorf("failed to close plugin") - } - } -} - -// LoadPlugins loads all plugins into containerd and generates an ordered graph -// of all plugins. -func LoadPlugins(ctx context.Context, config *Config) ([]*plugin.Registration, error) { - // load all plugins into containerd - if err := plugin.Load(filepath.Join(config.Root, "plugins")); err != nil { - return nil, err - } - // load additional plugins that don't automatically register themselves - plugin.Register(&plugin.Registration{ - Type: plugin.ContentPlugin, - ID: "content", - InitFn: func(ic *plugin.InitContext) (interface{}, error) { - ic.Meta.Exports["root"] = ic.Root - return local.NewStore(ic.Root) - }, - }) - plugin.Register(&plugin.Registration{ - Type: plugin.MetadataPlugin, - ID: "bolt", - Requires: []plugin.Type{ - plugin.ContentPlugin, - plugin.SnapshotPlugin, - }, - InitFn: func(ic *plugin.InitContext) (interface{}, error) { - if err := os.MkdirAll(ic.Root, 0711); err != nil { - return nil, err - } - cs, err := ic.Get(plugin.ContentPlugin) - if err != nil { - return nil, err - } - - snapshottersRaw, err := ic.GetByType(plugin.SnapshotPlugin) - if err != nil { - return nil, err - } - - snapshotters := make(map[string]snapshots.Snapshotter) - for name, sn := range snapshottersRaw { - sn, err := sn.Instance() - if err != nil { - log.G(ic.Context).WithError(err). - Warnf("could not use snapshotter %v in metadata plugin", name) - continue - } - snapshotters[name] = sn.(snapshots.Snapshotter) - } - - path := filepath.Join(ic.Root, "meta.db") - ic.Meta.Exports["path"] = path - - db, err := bolt.Open(path, 0644, nil) - if err != nil { - return nil, err - } - mdb := metadata.NewDB(db, cs.(content.Store), snapshotters) - if err := mdb.Init(ic.Context); err != nil { - return nil, err - } - return mdb, nil - }, - }) - - clients := &proxyClients{} - for name, pp := range config.ProxyPlugins { - var ( - t plugin.Type - f func(*grpc.ClientConn) interface{} - - address = pp.Address - ) - - switch pp.Type { - case string(plugin.SnapshotPlugin), "snapshot": - t = plugin.SnapshotPlugin - ssname := name - f = func(conn *grpc.ClientConn) interface{} { - return ssproxy.NewSnapshotter(ssapi.NewSnapshotsClient(conn), ssname) - } - - case string(plugin.ContentPlugin), "content": - t = plugin.ContentPlugin - f = func(conn *grpc.ClientConn) interface{} { - return csproxy.NewContentStore(csapi.NewContentClient(conn)) - } - default: - log.G(ctx).WithField("type", pp.Type).Warn("unknown proxy plugin type") - } - - plugin.Register(&plugin.Registration{ - Type: t, - ID: name, - InitFn: func(ic *plugin.InitContext) (interface{}, error) { - ic.Meta.Exports["address"] = address - conn, err := clients.getClient(address) - if err != nil { - return nil, err - } - return f(conn), nil - }, - }) - - } - - // return the ordered graph for plugins - return plugin.Graph(config.DisabledPlugins), nil -} - -type proxyClients struct { - m sync.Mutex - clients map[string]*grpc.ClientConn -} - -func (pc *proxyClients) getClient(address string) (*grpc.ClientConn, error) { - pc.m.Lock() - defer pc.m.Unlock() - if pc.clients == nil { - pc.clients = map[string]*grpc.ClientConn{} - } else if c, ok := pc.clients[address]; ok { - return c, nil - } - - gopts := []grpc.DialOption{ - grpc.WithInsecure(), - grpc.WithBackoffMaxDelay(3 * time.Second), - grpc.WithDialer(dialer.Dialer), - - // TODO(stevvooe): We may need to allow configuration of this on the client. - grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)), - grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)), - } - - conn, err := grpc.Dial(dialer.DialAddress(address), gopts...) - if err != nil { - return nil, errors.Wrapf(err, "failed to dial %q", address) - } - - pc.clients[address] = conn - - return conn, nil -} - -func trapClosedConnErr(err error) error { - if err == nil { - return nil - } - if strings.Contains(err.Error(), "use of closed network connection") { - return nil - } - return err -} diff --git a/vendor/github.com/containerd/containerd/services/server/server_linux.go b/vendor/github.com/containerd/containerd/services/server/server_linux.go deleted file mode 100644 index c45ccd3d5123d..0000000000000 --- a/vendor/github.com/containerd/containerd/services/server/server_linux.go +++ /dev/null @@ -1,54 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package server - -import ( - "context" - "os" - - "github.com/containerd/cgroups" - "github.com/containerd/containerd/log" - "github.com/containerd/containerd/sys" - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -// apply sets config settings on the server process -func apply(ctx context.Context, config *Config) error { - if config.OOMScore != 0 { - log.G(ctx).Debugf("changing OOM score to %d", config.OOMScore) - if err := sys.SetOOMScore(os.Getpid(), config.OOMScore); err != nil { - log.G(ctx).WithError(err).Errorf("failed to change OOM score to %d", config.OOMScore) - } - } - if config.Cgroup.Path != "" { - cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(config.Cgroup.Path)) - if err != nil { - if err != cgroups.ErrCgroupDeleted { - return err - } - if cg, err = cgroups.New(cgroups.V1, cgroups.StaticPath(config.Cgroup.Path), &specs.LinuxResources{}); err != nil { - return err - } - } - if err := cg.Add(cgroups.Process{ - Pid: os.Getpid(), - }); err != nil { - return err - } - } - return nil -} diff --git a/vendor/github.com/containerd/containerd/services/server/server_solaris.go b/vendor/github.com/containerd/containerd/services/server/server_solaris.go deleted file mode 100644 index 0dbbb9feac5f9..0000000000000 --- a/vendor/github.com/containerd/containerd/services/server/server_solaris.go +++ /dev/null @@ -1,23 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package server - -import "context" - -func apply(_ context.Context, _ *Config) error { - return nil -} diff --git a/vendor/github.com/containerd/containerd/services/server/server_unsupported.go b/vendor/github.com/containerd/containerd/services/server/server_unsupported.go deleted file mode 100644 index c6211dbb2a829..0000000000000 --- a/vendor/github.com/containerd/containerd/services/server/server_unsupported.go +++ /dev/null @@ -1,25 +0,0 @@ -// +build !linux,!windows,!solaris - -/* - Copyright The containerd Authors. - - 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. -*/ - -package server - -import "context" - -func apply(_ context.Context, _ *Config) error { - return nil -} diff --git a/vendor/github.com/containerd/containerd/services/server/server_windows.go b/vendor/github.com/containerd/containerd/services/server/server_windows.go deleted file mode 100644 index ac0b8481c25d3..0000000000000 --- a/vendor/github.com/containerd/containerd/services/server/server_windows.go +++ /dev/null @@ -1,27 +0,0 @@ -// +build windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package server - -import ( - "context" -) - -func apply(_ context.Context, _ *Config) error { - return nil -} diff --git a/vendor/github.com/containerd/containerd/services/services.go b/vendor/github.com/containerd/containerd/services/services.go new file mode 100644 index 0000000000000..27f47a5cef4e2 --- /dev/null +++ b/vendor/github.com/containerd/containerd/services/services.go @@ -0,0 +1,38 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package services + +const ( + // ContentService is id of content service. + ContentService = "content-service" + // SnapshotsService is id of snapshots service. + SnapshotsService = "snapshots-service" + // ImagesService is id of images service. + ImagesService = "images-service" + // ContainersService is id of containers service. + ContainersService = "containers-service" + // TasksService is id of tasks service. + TasksService = "tasks-service" + // NamespacesService is id of namespaces service. + NamespacesService = "namespaces-service" + // LeasesService is id of leases service. + LeasesService = "leases-service" + // DiffService is id of diff service. + DiffService = "diff-service" + // IntrospectionService is the id of introspection service + IntrospectionService = "introspection-service" +) diff --git a/vendor/github.com/containerd/containerd/signal_map_linux.go b/vendor/github.com/containerd/containerd/signal_map_linux.go deleted file mode 100644 index 554011074c092..0000000000000 --- a/vendor/github.com/containerd/containerd/signal_map_linux.go +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package containerd - -import ( - "syscall" - - "golang.org/x/sys/unix" -) - -var signalMap = map[string]syscall.Signal{ - "ABRT": unix.SIGABRT, - "ALRM": unix.SIGALRM, - "BUS": unix.SIGBUS, - "CHLD": unix.SIGCHLD, - "CLD": unix.SIGCLD, - "CONT": unix.SIGCONT, - "FPE": unix.SIGFPE, - "HUP": unix.SIGHUP, - "ILL": unix.SIGILL, - "INT": unix.SIGINT, - "IO": unix.SIGIO, - "IOT": unix.SIGIOT, - "KILL": unix.SIGKILL, - "PIPE": unix.SIGPIPE, - "POLL": unix.SIGPOLL, - "PROF": unix.SIGPROF, - "PWR": unix.SIGPWR, - "QUIT": unix.SIGQUIT, - "SEGV": unix.SIGSEGV, - "STKFLT": unix.SIGSTKFLT, - "STOP": unix.SIGSTOP, - "SYS": unix.SIGSYS, - "TERM": unix.SIGTERM, - "TRAP": unix.SIGTRAP, - "TSTP": unix.SIGTSTP, - "TTIN": unix.SIGTTIN, - "TTOU": unix.SIGTTOU, - "URG": unix.SIGURG, - "USR1": unix.SIGUSR1, - "USR2": unix.SIGUSR2, - "VTALRM": unix.SIGVTALRM, - "WINCH": unix.SIGWINCH, - "XCPU": unix.SIGXCPU, - "XFSZ": unix.SIGXFSZ, -} diff --git a/vendor/github.com/containerd/containerd/signal_map_unix.go b/vendor/github.com/containerd/containerd/signal_map_unix.go deleted file mode 100644 index 62ccba9ace330..0000000000000 --- a/vendor/github.com/containerd/containerd/signal_map_unix.go +++ /dev/null @@ -1,58 +0,0 @@ -// +build darwin freebsd solaris - -/* - Copyright The containerd Authors. - - 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. -*/ - -package containerd - -import ( - "syscall" - - "golang.org/x/sys/unix" -) - -var signalMap = map[string]syscall.Signal{ - "ABRT": unix.SIGABRT, - "ALRM": unix.SIGALRM, - "BUS": unix.SIGBUS, - "CHLD": unix.SIGCHLD, - "CONT": unix.SIGCONT, - "FPE": unix.SIGFPE, - "HUP": unix.SIGHUP, - "ILL": unix.SIGILL, - "INT": unix.SIGINT, - "IO": unix.SIGIO, - "IOT": unix.SIGIOT, - "KILL": unix.SIGKILL, - "PIPE": unix.SIGPIPE, - "PROF": unix.SIGPROF, - "QUIT": unix.SIGQUIT, - "SEGV": unix.SIGSEGV, - "STOP": unix.SIGSTOP, - "SYS": unix.SIGSYS, - "TERM": unix.SIGTERM, - "TRAP": unix.SIGTRAP, - "TSTP": unix.SIGTSTP, - "TTIN": unix.SIGTTIN, - "TTOU": unix.SIGTTOU, - "URG": unix.SIGURG, - "USR1": unix.SIGUSR1, - "USR2": unix.SIGUSR2, - "VTALRM": unix.SIGVTALRM, - "WINCH": unix.SIGWINCH, - "XCPU": unix.SIGXCPU, - "XFSZ": unix.SIGXFSZ, -} diff --git a/vendor/github.com/containerd/containerd/signal_map_windows.go b/vendor/github.com/containerd/containerd/signal_map_windows.go deleted file mode 100644 index ef17a8fdb1698..0000000000000 --- a/vendor/github.com/containerd/containerd/signal_map_windows.go +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package containerd - -import ( - "syscall" - - "golang.org/x/sys/windows" -) - -var signalMap = map[string]syscall.Signal{ - "HUP": syscall.Signal(windows.SIGHUP), - "INT": syscall.Signal(windows.SIGINT), - "QUIT": syscall.Signal(windows.SIGQUIT), - "SIGILL": syscall.Signal(windows.SIGILL), - "TRAP": syscall.Signal(windows.SIGTRAP), - "ABRT": syscall.Signal(windows.SIGABRT), - "BUS": syscall.Signal(windows.SIGBUS), - "FPE": syscall.Signal(windows.SIGFPE), - "KILL": syscall.Signal(windows.SIGKILL), - "SEGV": syscall.Signal(windows.SIGSEGV), - "PIPE": syscall.Signal(windows.SIGPIPE), - "ALRM": syscall.Signal(windows.SIGALRM), - "TERM": syscall.Signal(windows.SIGTERM), -} diff --git a/vendor/github.com/containerd/containerd/signals.go b/vendor/github.com/containerd/containerd/signals.go index 32c34309de113..ca64ecd02121f 100644 --- a/vendor/github.com/containerd/containerd/signals.go +++ b/vendor/github.com/containerd/containerd/signals.go @@ -20,13 +20,11 @@ import ( "context" "encoding/json" "fmt" - "strconv" - "strings" "syscall" "github.com/containerd/containerd/content" "github.com/containerd/containerd/images" - "github.com/opencontainers/image-spec/specs-go/v1" + v1 "github.com/opencontainers/image-spec/specs-go/v1" ) // StopSignalLabel is a well-known containerd label for storing the stop @@ -83,23 +81,3 @@ func GetOCIStopSignal(ctx context.Context, image Image, defaultSignal string) (s return config.StopSignal, nil } - -// ParseSignal parses a given string into a syscall.Signal -// it checks that the signal exists in the platform-appropriate signalMap -func ParseSignal(rawSignal string) (syscall.Signal, error) { - s, err := strconv.Atoi(rawSignal) - if err == nil { - sig := syscall.Signal(s) - for _, msig := range signalMap { - if sig == msig { - return sig, nil - } - } - return -1, fmt.Errorf("unknown signal %q", rawSignal) - } - signal, ok := signalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")] - if !ok { - return -1, fmt.Errorf("unknown signal %q", rawSignal) - } - return signal, nil -} diff --git a/vendor/github.com/containerd/containerd/signals_unix.go b/vendor/github.com/containerd/containerd/signals_unix.go new file mode 100644 index 0000000000000..14916a9ff3a5c --- /dev/null +++ b/vendor/github.com/containerd/containerd/signals_unix.go @@ -0,0 +1,43 @@ +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris + +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "fmt" + "strconv" + "strings" + "syscall" + + "golang.org/x/sys/unix" +) + +// ParseSignal parses a given string into a syscall.Signal +// the rawSignal can be a string with "SIG" prefix, +// or a signal number in string format. +func ParseSignal(rawSignal string) (syscall.Signal, error) { + s, err := strconv.Atoi(rawSignal) + if err == nil { + return syscall.Signal(s), nil + } + signal := unix.SignalNum(strings.ToUpper(rawSignal)) + if signal == 0 { + return -1, fmt.Errorf("unknown signal %q", rawSignal) + } + return signal, nil +} diff --git a/vendor/github.com/containerd/containerd/signals_windows.go b/vendor/github.com/containerd/containerd/signals_windows.go new file mode 100644 index 0000000000000..0018e191ed6a5 --- /dev/null +++ b/vendor/github.com/containerd/containerd/signals_windows.go @@ -0,0 +1,63 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "fmt" + "strconv" + "strings" + "syscall" + + "golang.org/x/sys/windows" +) + +var signalMap = map[string]syscall.Signal{ + "HUP": syscall.Signal(windows.SIGHUP), + "INT": syscall.Signal(windows.SIGINT), + "QUIT": syscall.Signal(windows.SIGQUIT), + "SIGILL": syscall.Signal(windows.SIGILL), + "TRAP": syscall.Signal(windows.SIGTRAP), + "ABRT": syscall.Signal(windows.SIGABRT), + "BUS": syscall.Signal(windows.SIGBUS), + "FPE": syscall.Signal(windows.SIGFPE), + "KILL": syscall.Signal(windows.SIGKILL), + "SEGV": syscall.Signal(windows.SIGSEGV), + "PIPE": syscall.Signal(windows.SIGPIPE), + "ALRM": syscall.Signal(windows.SIGALRM), + "TERM": syscall.Signal(windows.SIGTERM), +} + +// ParseSignal parses a given string into a syscall.Signal +// the rawSignal can be a string with "SIG" prefix, +// or a signal number in string format. +func ParseSignal(rawSignal string) (syscall.Signal, error) { + s, err := strconv.Atoi(rawSignal) + if err == nil { + sig := syscall.Signal(s) + for _, msig := range signalMap { + if sig == msig { + return sig, nil + } + } + return -1, fmt.Errorf("unknown signal %q", rawSignal) + } + signal, ok := signalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")] + if !ok { + return -1, fmt.Errorf("unknown signal %q", rawSignal) + } + return signal, nil +} diff --git a/vendor/github.com/containerd/containerd/snapshots/proxy/proxy.go b/vendor/github.com/containerd/containerd/snapshots/proxy/proxy.go index 1e8c2634c0870..00c320c60872b 100644 --- a/vendor/github.com/containerd/containerd/snapshots/proxy/proxy.go +++ b/vendor/github.com/containerd/containerd/snapshots/proxy/proxy.go @@ -153,9 +153,10 @@ func (p *proxySnapshotter) Remove(ctx context.Context, key string) error { return errdefs.FromGRPC(err) } -func (p *proxySnapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error { +func (p *proxySnapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, fs ...string) error { sc, err := p.client.List(ctx, &snapshotsapi.ListSnapshotsRequest{ Snapshotter: p.snapshotterName, + Filters: fs, }) if err != nil { return errdefs.FromGRPC(err) @@ -183,6 +184,13 @@ func (p *proxySnapshotter) Close() error { return nil } +func (p *proxySnapshotter) Cleanup(ctx context.Context) error { + _, err := p.client.Cleanup(ctx, &snapshotsapi.CleanupRequest{ + Snapshotter: p.snapshotterName, + }) + return errdefs.FromGRPC(err) +} + func toKind(kind snapshotsapi.Kind) snapshots.Kind { if kind == snapshotsapi.KindActive { return snapshots.KindActive diff --git a/vendor/github.com/containerd/containerd/snapshots/snapshotter.go b/vendor/github.com/containerd/containerd/snapshots/snapshotter.go index d11252d1e35b5..8b0ea85e65b55 100644 --- a/vendor/github.com/containerd/containerd/snapshots/snapshotter.go +++ b/vendor/github.com/containerd/containerd/snapshots/snapshotter.go @@ -25,6 +25,16 @@ import ( "github.com/containerd/containerd/mount" ) +const ( + // UnpackKeyPrefix is the beginning of the key format used for snapshots that will have + // image content unpacked into them. + UnpackKeyPrefix = "extract" + // UnpackKeyFormat is the format for the snapshotter keys used for extraction + UnpackKeyFormat = UnpackKeyPrefix + "-%s %s" + inheritedLabelsPrefix = "containerd.io/snapshot/" + labelSnapshotRef = "containerd.io/snapshot.ref" +) + // Kind identifies the kind of snapshot. type Kind uint8 @@ -86,10 +96,15 @@ func (k *Kind) UnmarshalJSON(b []byte) error { // Info provides information about a particular snapshot. // JSON marshallability is supported for interactive with tools like ctr, type Info struct { - Kind Kind // active or committed snapshot - Name string // name or key of snapshot - Parent string `json:",omitempty"` // name of parent snapshot - Labels map[string]string `json:",omitempty"` // Labels for snapshot + Kind Kind // active or committed snapshot + Name string // name or key of snapshot + Parent string `json:",omitempty"` // name of parent snapshot + + // Labels for a snapshot. + // + // Note: only labels prefixed with `containerd.io/snapshot/` will be inherited by the + // snapshotter's `Prepare`, `View`, or `Commit` calls. + Labels map[string]string `json:",omitempty"` Created time.Time `json:",omitempty"` // Created time Updated time.Time `json:",omitempty"` // Last update time } @@ -113,6 +128,9 @@ func (u *Usage) Add(other Usage) { u.Inodes += other.Inodes } +// WalkFunc defines the callback for a snapshot walk. +type WalkFunc func(context.Context, Info) error + // Snapshotter defines the methods required to implement a snapshot snapshotter for // allocating, snapshotting and mounting filesystem changesets. The model works // by building up sets of changes with parent-child relationships. @@ -160,9 +178,13 @@ func (u *Usage) Add(other Usage) { // layerPath, tmpDir := getLayerPath(), mkTmpDir() // just a path to layer tar file. // // We start by using a Snapshotter to Prepare a new snapshot transaction, using a -// key and descending from the empty parent "": +// key and descending from the empty parent "". To prevent our layer from being +// garbage collected during unpacking, we add the `containerd.io/gc.root` label: // -// mounts, err := snapshotter.Prepare(ctx, key, "") +// noGcOpt := snapshots.WithLabels(map[string]string{ +// "containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339), +// }) +// mounts, err := snapshotter.Prepare(ctx, key, "", noGcOpt) // if err != nil { ... } // // We get back a list of mounts from Snapshotter.Prepare, with the key identifying @@ -191,15 +213,13 @@ func (u *Usage) Add(other Usage) { // // Now that we've verified and unpacked our layer, we commit the active // snapshot to a name. For this example, we are just going to use the layer -// digest, but in practice, this will probably be the ChainID: +// digest, but in practice, this will probably be the ChainID. This also removes +// the active snapshot: // -// if err := snapshotter.Commit(ctx, digest.String(), key); err != nil { ... } +// if err := snapshotter.Commit(ctx, digest.String(), key, noGcOpt); err != nil { ... } // // Now, we have a layer in the Snapshotter that can be accessed with the digest -// provided during commit. Once you have committed the snapshot, the active -// snapshot can be removed with the following: -// -// snapshotter.Remove(ctx, key) +// provided during commit. // // Importing the Next Layer // @@ -207,7 +227,7 @@ func (u *Usage) Add(other Usage) { // above except that the parent is provided as parent when calling // Manager.Prepare, assuming a clean, unique key identifier: // -// mounts, err := snapshotter.Prepare(ctx, key, parentDigest) +// mounts, err := snapshotter.Prepare(ctx, key, parentDigest, noGcOpt) // // We then mount, apply and commit, as we did above. The new snapshot will be // based on the content of the previous one. @@ -307,9 +327,15 @@ type Snapshotter interface { // removed before proceeding. Remove(ctx context.Context, key string) error - // Walk all snapshots in the snapshotter. For each snapshot in the - // snapshotter, the function will be called. - Walk(ctx context.Context, fn func(context.Context, Info) error) error + // Walk will call the provided function for each snapshot in the + // snapshotter which match the provided filters. If no filters are + // given all items will be walked. + // Filters: + // name + // parent + // kind (active,view,committed) + // labels.(label) + Walk(ctx context.Context, fn WalkFunc, filters ...string) error // Close releases the internal resources. // @@ -320,13 +346,48 @@ type Snapshotter interface { Close() error } +// Cleaner defines a type capable of performing asynchronous resource cleanup. +// The Cleaner interface should be used by snapshotters which implement fast +// removal and deferred resource cleanup. This prevents snapshots from needing +// to perform lengthy resource cleanup before acknowledging a snapshot key +// has been removed and available for re-use. This is also useful when +// performing multi-key removal with the intent of cleaning up all the +// resources after each snapshot key has been removed. +type Cleaner interface { + Cleanup(ctx context.Context) error +} + // Opt allows setting mutable snapshot properties on creation type Opt func(info *Info) error -// WithLabels adds labels to a created snapshot +// WithLabels appends labels to a created snapshot func WithLabels(labels map[string]string) Opt { return func(info *Info) error { - info.Labels = labels + if info.Labels == nil { + info.Labels = make(map[string]string) + } + + for k, v := range labels { + info.Labels[k] = v + } + + return nil + } +} + +// FilterInheritedLabels filters the provided labels by removing any key which +// isn't a snapshot label. Snapshot labels have a prefix of "containerd.io/snapshot/" +// or are the "containerd.io/snapshot.ref" label. +func FilterInheritedLabels(labels map[string]string) map[string]string { + if labels == nil { return nil } + + filtered := make(map[string]string) + for k, v := range labels { + if k == labelSnapshotRef || strings.HasPrefix(k, inheritedLabelsPrefix) { + filtered[k] = v + } + } + return filtered } diff --git a/vendor/github.com/containerd/containerd/snapshotter_opts_unix.go b/vendor/github.com/containerd/containerd/snapshotter_opts_unix.go new file mode 100644 index 0000000000000..1964379d49197 --- /dev/null +++ b/vendor/github.com/containerd/containerd/snapshotter_opts_unix.go @@ -0,0 +1,35 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "fmt" + + "github.com/containerd/containerd/snapshots" +) + +// WithRemapperLabels creates the labels used by any supporting snapshotter +// to shift the filesystem ownership (user namespace mapping) automatically; currently +// supported by the fuse-overlayfs snapshotter +func WithRemapperLabels(ctrUID, hostUID, ctrGID, hostGID, length uint32) snapshots.Opt { + return snapshots.WithLabels(map[string]string{ + "containerd.io/snapshot/uidmapping": fmt.Sprintf("%d:%d:%d", ctrUID, hostUID, length), + "containerd.io/snapshot/gidmapping": fmt.Sprintf("%d:%d:%d", ctrGID, hostGID, length), + }) +} diff --git a/vendor/github.com/containerd/containerd/sys/env.go b/vendor/github.com/containerd/containerd/sys/env.go deleted file mode 100644 index 8450d627582d4..0000000000000 --- a/vendor/github.com/containerd/containerd/sys/env.go +++ /dev/null @@ -1,33 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package sys - -import "golang.org/x/sys/unix" - -// RunningPrivileged returns true if the effective user ID of the -// calling process is 0 -func RunningPrivileged() bool { - return unix.Geteuid() == 0 -} - -// RunningUnprivileged returns true if the effective user ID of the -// calling process is not 0 -func RunningUnprivileged() bool { - return !RunningPrivileged() -} diff --git a/vendor/github.com/containerd/containerd/sys/epoll.go b/vendor/github.com/containerd/containerd/sys/epoll.go index 683f38eea86ab..28d6c2cabc64f 100644 --- a/vendor/github.com/containerd/containerd/sys/epoll.go +++ b/vendor/github.com/containerd/containerd/sys/epoll.go @@ -20,17 +20,14 @@ package sys import "golang.org/x/sys/unix" -// EpollCreate1 directly calls unix.EpollCreate1 -func EpollCreate1(flag int) (int, error) { - return unix.EpollCreate1(flag) -} - -// EpollCtl directly calls unix.EpollCtl -func EpollCtl(epfd int, op int, fd int, event *unix.EpollEvent) error { - return unix.EpollCtl(epfd, op, fd, event) -} - -// EpollWait directly calls unix.EpollWait -func EpollWait(epfd int, events []unix.EpollEvent, msec int) (int, error) { - return unix.EpollWait(epfd, events, msec) -} +// EpollCreate1 is an alias for unix.EpollCreate1 +// Deprecated: use golang.org/x/sys/unix.EpollCreate1 +var EpollCreate1 = unix.EpollCreate1 + +// EpollCtl is an alias for unix.EpollCtl +// Deprecated: use golang.org/x/sys/unix.EpollCtl +var EpollCtl = unix.EpollCtl + +// EpollWait is an alias for unix.EpollWait +// Deprecated: use golang.org/x/sys/unix.EpollWait +var EpollWait = unix.EpollWait diff --git a/vendor/github.com/containerd/containerd/sys/filesys_unix.go b/vendor/github.com/containerd/containerd/sys/filesys_unix.go index 700f44efa9b49..d8329af9fbcac 100644 --- a/vendor/github.com/containerd/containerd/sys/filesys_unix.go +++ b/vendor/github.com/containerd/containerd/sys/filesys_unix.go @@ -24,3 +24,8 @@ import "os" func ForceRemoveAll(path string) error { return os.RemoveAll(path) } + +// MkdirAllWithACL is a wrapper for os.MkdirAll on Unix systems. +func MkdirAllWithACL(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) +} diff --git a/vendor/github.com/containerd/containerd/sys/filesys_windows.go b/vendor/github.com/containerd/containerd/sys/filesys_windows.go index dc880c3427b67..a9198ef399aa9 100644 --- a/vendor/github.com/containerd/containerd/sys/filesys_windows.go +++ b/vendor/github.com/containerd/containerd/sys/filesys_windows.go @@ -22,12 +22,20 @@ import ( "os" "path/filepath" "regexp" + "sort" + "strconv" "strings" "syscall" "unsafe" - winio "github.com/Microsoft/go-winio" "github.com/Microsoft/hcsshim" + "github.com/pkg/errors" + "golang.org/x/sys/windows" +) + +const ( + // SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System + SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)" ) // MkdirAllWithACL is a wrapper for MkdirAll that creates a directory @@ -36,7 +44,8 @@ func MkdirAllWithACL(path string, perm os.FileMode) error { return mkdirall(path, true) } -// MkdirAll implementation that is volume path aware for Windows. +// MkdirAll implementation that is volume path aware for Windows. It can be used +// as a drop-in replacement for os.MkdirAll() func MkdirAll(path string, _ os.FileMode) error { return mkdirall(path, false) } @@ -78,7 +87,7 @@ func mkdirall(path string, adminAndLocalSystem bool) error { if j > 1 { // Create parent - err = mkdirall(path[0:j-1], false) + err = mkdirall(path[0:j-1], adminAndLocalSystem) if err != nil { return err } @@ -106,27 +115,26 @@ func mkdirall(path string, adminAndLocalSystem bool) error { // mkdirWithACL creates a new directory. If there is an error, it will be of // type *PathError. . // -// This is a modified and combined version of os.Mkdir and syscall.Mkdir +// This is a modified and combined version of os.Mkdir and windows.Mkdir // in golang to cater for creating a directory am ACL permitting full // access, with inheritance, to any subfolder/file for Built-in Administrators // and Local System. func mkdirWithACL(name string) error { - sa := syscall.SecurityAttributes{Length: 0} - sddl := "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)" - sd, err := winio.SddlToSecurityDescriptor(sddl) + sa := windows.SecurityAttributes{Length: 0} + sd, err := windows.SecurityDescriptorFromString(SddlAdministratorsLocalSystem) if err != nil { return &os.PathError{Op: "mkdir", Path: name, Err: err} } sa.Length = uint32(unsafe.Sizeof(sa)) sa.InheritHandle = 1 - sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0])) + sa.SecurityDescriptor = sd - namep, err := syscall.UTF16PtrFromString(name) + namep, err := windows.UTF16PtrFromString(name) if err != nil { return &os.PathError{Op: "mkdir", Path: name, Err: err} } - e := syscall.CreateDirectory(namep, &sa) + e := windows.CreateDirectory(namep, &sa) if e != nil { return &os.PathError{Op: "mkdir", Path: name, Err: e} } @@ -149,7 +157,7 @@ func IsAbs(path string) bool { return true } -// The origin of the functions below here are the golang OS and syscall packages, +// The origin of the functions below here are the golang OS and windows packages, // slightly modified to only cope with files, not directories due to the // specific use case. // @@ -181,83 +189,142 @@ func OpenFileSequential(name string, flag int, _ os.FileMode) (*os.File, error) if name == "" { return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} } - r, errf := syscallOpenFileSequential(name, flag, 0) + r, errf := windowsOpenFileSequential(name, flag, 0) if errf == nil { return r, nil } return nil, &os.PathError{Op: "open", Path: name, Err: errf} } -func syscallOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) { - r, e := syscallOpenSequential(name, flag|syscall.O_CLOEXEC, 0) +func windowsOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) { + r, e := windowsOpenSequential(name, flag|windows.O_CLOEXEC, 0) if e != nil { return nil, e } return os.NewFile(uintptr(r), name), nil } -func makeInheritSa() *syscall.SecurityAttributes { - var sa syscall.SecurityAttributes +func makeInheritSa() *windows.SecurityAttributes { + var sa windows.SecurityAttributes sa.Length = uint32(unsafe.Sizeof(sa)) sa.InheritHandle = 1 return &sa } -func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle, err error) { +func windowsOpenSequential(path string, mode int, _ uint32) (fd windows.Handle, err error) { if len(path) == 0 { - return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND + return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND } - pathp, err := syscall.UTF16PtrFromString(path) + pathp, err := windows.UTF16PtrFromString(path) if err != nil { - return syscall.InvalidHandle, err + return windows.InvalidHandle, err } var access uint32 - switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) { - case syscall.O_RDONLY: - access = syscall.GENERIC_READ - case syscall.O_WRONLY: - access = syscall.GENERIC_WRITE - case syscall.O_RDWR: - access = syscall.GENERIC_READ | syscall.GENERIC_WRITE + switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) { + case windows.O_RDONLY: + access = windows.GENERIC_READ + case windows.O_WRONLY: + access = windows.GENERIC_WRITE + case windows.O_RDWR: + access = windows.GENERIC_READ | windows.GENERIC_WRITE } - if mode&syscall.O_CREAT != 0 { - access |= syscall.GENERIC_WRITE + if mode&windows.O_CREAT != 0 { + access |= windows.GENERIC_WRITE } - if mode&syscall.O_APPEND != 0 { - access &^= syscall.GENERIC_WRITE - access |= syscall.FILE_APPEND_DATA + if mode&windows.O_APPEND != 0 { + access &^= windows.GENERIC_WRITE + access |= windows.FILE_APPEND_DATA } - sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE) - var sa *syscall.SecurityAttributes - if mode&syscall.O_CLOEXEC == 0 { + sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE) + var sa *windows.SecurityAttributes + if mode&windows.O_CLOEXEC == 0 { sa = makeInheritSa() } var createmode uint32 switch { - case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL): - createmode = syscall.CREATE_NEW - case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC): - createmode = syscall.CREATE_ALWAYS - case mode&syscall.O_CREAT == syscall.O_CREAT: - createmode = syscall.OPEN_ALWAYS - case mode&syscall.O_TRUNC == syscall.O_TRUNC: - createmode = syscall.TRUNCATE_EXISTING + case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL): + createmode = windows.CREATE_NEW + case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC): + createmode = windows.CREATE_ALWAYS + case mode&windows.O_CREAT == windows.O_CREAT: + createmode = windows.OPEN_ALWAYS + case mode&windows.O_TRUNC == windows.O_TRUNC: + createmode = windows.TRUNCATE_EXISTING default: - createmode = syscall.OPEN_EXISTING + createmode = windows.OPEN_EXISTING } // Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang. - //https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN - h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0) + h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0) return h, e } -// ForceRemoveAll is the same as os.RemoveAll, but uses hcsshim.DestroyLayer in order -// to delete container layers. +// ForceRemoveAll is the same as os.RemoveAll, but is aware of io.containerd.snapshotter.v1.windows +// and uses hcsshim to unmount and delete container layers contained therein, in the correct order, +// when passed a containerd root data directory (i.e. the `--root` directory for containerd). func ForceRemoveAll(path string) error { + // snapshots/windows/windows.go init() + const snapshotPlugin = "io.containerd.snapshotter.v1" + "." + "windows" + // snapshots/windows/windows.go NewSnapshotter() + snapshotDir := filepath.Join(path, snapshotPlugin, "snapshots") + if stat, err := os.Stat(snapshotDir); err == nil && stat.IsDir() { + if err := cleanupWCOWLayers(snapshotDir); err != nil { + return errors.Wrapf(err, "failed to cleanup WCOW layers in %s", snapshotDir) + } + } + + return os.RemoveAll(path) +} + +func cleanupWCOWLayers(root string) error { + // See snapshots/windows/windows.go getSnapshotDir() + var layerNums []int + if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if path != root && info.IsDir() { + if layerNum, err := strconv.Atoi(filepath.Base(path)); err == nil { + layerNums = append(layerNums, layerNum) + } else { + return err + } + return filepath.SkipDir + } + + return nil + }); err != nil { + return err + } + + sort.Sort(sort.Reverse(sort.IntSlice(layerNums))) + + for _, layerNum := range layerNums { + if err := cleanupWCOWLayer(filepath.Join(root, strconv.Itoa(layerNum))); err != nil { + return err + } + } + + return nil +} + +func cleanupWCOWLayer(layerPath string) error { info := hcsshim.DriverInfo{ - HomeDir: filepath.Dir(path), + HomeDir: filepath.Dir(layerPath), + } + + // ERROR_DEV_NOT_EXIST is returned if the layer is not currently prepared. + if err := hcsshim.UnprepareLayer(info, filepath.Base(layerPath)); err != nil { + if hcserror, ok := err.(*hcsshim.HcsError); !ok || hcserror.Err != windows.ERROR_DEV_NOT_EXIST { + return errors.Wrapf(err, "failed to unprepare %s", layerPath) + } + } + + if err := hcsshim.DeactivateLayer(info, filepath.Base(layerPath)); err != nil { + return errors.Wrapf(err, "failed to deactivate %s", layerPath) } - return hcsshim.DestroyLayer(info, filepath.Base(path)) + if err := hcsshim.DestroyLayer(info, filepath.Base(layerPath)); err != nil { + return errors.Wrapf(err, "failed to destroy %s", layerPath) + } + + return nil } diff --git a/vendor/github.com/containerd/containerd/sys/mount_linux.go b/vendor/github.com/containerd/containerd/sys/mount_linux.go index a9eee9b73ac48..a21045529ae07 100644 --- a/vendor/github.com/containerd/containerd/sys/mount_linux.go +++ b/vendor/github.com/containerd/containerd/sys/mount_linux.go @@ -21,6 +21,7 @@ import ( "syscall" "unsafe" + "github.com/containerd/containerd/log" "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -30,9 +31,8 @@ func FMountat(dirfd uintptr, source, target, fstype string, flags uintptr, data var ( sourceP, targetP, fstypeP, dataP *byte pid uintptr - ws unix.WaitStatus err error - errno syscall.Errno + errno, status syscall.Errno ) sourceP, err = syscall.BytePtrFromString(source) @@ -60,37 +60,62 @@ func FMountat(dirfd uintptr, source, target, fstype string, flags uintptr, data runtime.LockOSThread() defer runtime.UnlockOSThread() + var pipefds [2]int + if err := syscall.Pipe2(pipefds[:], syscall.O_CLOEXEC); err != nil { + return errors.Wrap(err, "failed to open pipe") + } + + defer func() { + // close both ends of the pipe in a deferred function, since open file + // descriptor table is shared with child + syscall.Close(pipefds[0]) + syscall.Close(pipefds[1]) + }() + pid, errno = forkAndMountat(dirfd, uintptr(unsafe.Pointer(sourceP)), uintptr(unsafe.Pointer(targetP)), uintptr(unsafe.Pointer(fstypeP)), flags, - uintptr(unsafe.Pointer(dataP))) + uintptr(unsafe.Pointer(dataP)), + pipefds[1], + ) if errno != 0 { return errors.Wrap(errno, "failed to fork thread") } - _, err = unix.Wait4(int(pid), &ws, 0, nil) - for err == syscall.EINTR { - _, err = unix.Wait4(int(pid), &ws, 0, nil) - } + defer func() { + _, err := unix.Wait4(int(pid), nil, 0, nil) + for err == syscall.EINTR { + _, err = unix.Wait4(int(pid), nil, 0, nil) + } - if err != nil { - return errors.Wrapf(err, "failed to find pid=%d process", pid) - } + if err != nil { + log.L.WithError(err).Debugf("failed to find pid=%d process", pid) + } + }() - errno = syscall.Errno(ws.ExitStatus()) + _, _, errno = syscall.RawSyscall(syscall.SYS_READ, + uintptr(pipefds[0]), + uintptr(unsafe.Pointer(&status)), + unsafe.Sizeof(status)) if errno != 0 { - return errors.Wrap(errno, "failed to mount") + return errors.Wrap(errno, "failed to read pipe") } + + if status != 0 { + return errors.Wrap(status, "failed to mount") + } + return nil } // forkAndMountat will fork thread, change working dir and mount. // // precondition: the runtime OS thread must be locked. -func forkAndMountat(dirfd uintptr, source, target, fstype, flags, data uintptr) (pid uintptr, errno syscall.Errno) { +func forkAndMountat(dirfd uintptr, source, target, fstype, flags, data uintptr, pipefd int) (pid uintptr, errno syscall.Errno) { + // block signal during clone beforeFork() @@ -114,6 +139,7 @@ func forkAndMountat(dirfd uintptr, source, target, fstype, flags, data uintptr) _, _, errno = syscall.RawSyscall6(syscall.SYS_MOUNT, source, target, fstype, flags, data, 0) childerr: + _, _, errno = syscall.RawSyscall(syscall.SYS_WRITE, uintptr(pipefd), uintptr(unsafe.Pointer(&errno)), unsafe.Sizeof(errno)) syscall.RawSyscall(syscall.SYS_EXIT, uintptr(errno), 0, 0) panic("unreachable") } diff --git a/vendor/github.com/containerd/containerd/sys/oom_linux.go b/vendor/github.com/containerd/containerd/sys/oom_linux.go new file mode 100644 index 0000000000000..82a347c6f727e --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/oom_linux.go @@ -0,0 +1,83 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import ( + "fmt" + "io/ioutil" + "os" + "strconv" + "strings" + + "github.com/containerd/containerd/pkg/userns" + "golang.org/x/sys/unix" +) + +const ( + // OOMScoreAdjMin is from OOM_SCORE_ADJ_MIN https://github.com/torvalds/linux/blob/v5.10/include/uapi/linux/oom.h#L9 + OOMScoreAdjMin = -1000 + // OOMScoreAdjMax is from OOM_SCORE_ADJ_MAX https://github.com/torvalds/linux/blob/v5.10/include/uapi/linux/oom.h#L10 + OOMScoreAdjMax = 1000 +) + +// AdjustOOMScore sets the oom score for the provided pid. If the provided score +// is out of range (-1000 - 1000), it is clipped to the min/max value. +func AdjustOOMScore(pid, score int) error { + if score > OOMScoreAdjMax { + score = OOMScoreAdjMax + } else if score < OOMScoreAdjMin { + score = OOMScoreAdjMin + } + return SetOOMScore(pid, score) +} + +// SetOOMScore sets the oom score for the provided pid +func SetOOMScore(pid, score int) error { + if score > OOMScoreAdjMax || score < OOMScoreAdjMin { + return fmt.Errorf("value out of range (%d): OOM score must be between %d and %d", score, OOMScoreAdjMin, OOMScoreAdjMax) + } + path := fmt.Sprintf("/proc/%d/oom_score_adj", pid) + f, err := os.OpenFile(path, os.O_WRONLY, 0) + if err != nil { + return err + } + defer f.Close() + if _, err = f.WriteString(strconv.Itoa(score)); err != nil { + if os.IsPermission(err) && (!runningPrivileged() || userns.RunningInUserNS()) { + return nil + } + return err + } + return nil +} + +// GetOOMScoreAdj gets the oom score for a process. It returns 0 (zero) if either +// no oom score is set, or a sore is set to 0. +func GetOOMScoreAdj(pid int) (int, error) { + path := fmt.Sprintf("/proc/%d/oom_score_adj", pid) + data, err := ioutil.ReadFile(path) + if err != nil { + return 0, err + } + return strconv.Atoi(strings.TrimSpace(string(data))) +} + +// runningPrivileged returns true if the effective user ID of the +// calling process is 0 +func runningPrivileged() bool { + return unix.Geteuid() == 0 +} diff --git a/vendor/github.com/containerd/containerd/sys/oom_unix.go b/vendor/github.com/containerd/containerd/sys/oom_unix.go deleted file mode 100644 index 7192efec1cbba..0000000000000 --- a/vendor/github.com/containerd/containerd/sys/oom_unix.go +++ /dev/null @@ -1,47 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package sys - -import ( - "fmt" - "os" - "strconv" - - "github.com/opencontainers/runc/libcontainer/system" -) - -// OOMScoreMaxKillable is the maximum score keeping the process killable by the oom killer -const OOMScoreMaxKillable = -999 - -// SetOOMScore sets the oom score for the provided pid -func SetOOMScore(pid, score int) error { - path := fmt.Sprintf("/proc/%d/oom_score_adj", pid) - f, err := os.OpenFile(path, os.O_WRONLY, 0) - if err != nil { - return err - } - defer f.Close() - if _, err = f.WriteString(strconv.Itoa(score)); err != nil { - if os.IsPermission(err) && (system.RunningInUserNS() || RunningUnprivileged()) { - return nil - } - return err - } - return nil -} diff --git a/vendor/github.com/containerd/containerd/sys/oom_unsupported.go b/vendor/github.com/containerd/containerd/sys/oom_unsupported.go new file mode 100644 index 0000000000000..f5d7e9786b85e --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/oom_unsupported.go @@ -0,0 +1,48 @@ +// +build !linux + +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +const ( + // OOMScoreMaxKillable is not implemented on non Linux + OOMScoreMaxKillable = 0 + // OOMScoreAdjMax is not implemented on non Linux + OOMScoreAdjMax = 0 +) + +// AdjustOOMScore sets the oom score for the provided pid. If the provided score +// is out of range (-1000 - 1000), it is clipped to the min/max value. +// +// Not implemented on Windows +func AdjustOOMScore(pid, score int) error { + return nil +} + +// SetOOMScore sets the oom score for the process +// +// Not implemented on Windows +func SetOOMScore(pid, score int) error { + return nil +} + +// GetOOMScoreAdj gets the oom score for a process +// +// Not implemented on Windows +func GetOOMScoreAdj(pid int) (int, error) { + return 0, nil +} diff --git a/vendor/github.com/containerd/containerd/sys/oom_windows.go b/vendor/github.com/containerd/containerd/sys/oom_windows.go deleted file mode 100644 index f44bcebd1e085..0000000000000 --- a/vendor/github.com/containerd/containerd/sys/oom_windows.go +++ /dev/null @@ -1,24 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package sys - -// SetOOMScore sets the oom score for the process -// -// Not implemented on Windows -func SetOOMScore(pid, score int) error { - return nil -} diff --git a/vendor/github.com/containerd/containerd/sys/proc.go b/vendor/github.com/containerd/containerd/sys/proc.go deleted file mode 100644 index 496eb1fea19e6..0000000000000 --- a/vendor/github.com/containerd/containerd/sys/proc.go +++ /dev/null @@ -1,80 +0,0 @@ -// +build linux - -/* - Copyright The containerd Authors. - - 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. -*/ - -package sys - -import ( - "bufio" - "fmt" - "os" - "strconv" - "strings" - - "github.com/opencontainers/runc/libcontainer/system" -) - -const nanoSecondsPerSecond = 1e9 - -var clockTicksPerSecond = uint64(system.GetClockTicks()) - -// GetSystemCPUUsage returns the host system's cpu usage in -// nanoseconds. An error is returned if the format of the underlying -// file does not match. -// -// Uses /proc/stat defined by POSIX. Looks for the cpu -// statistics line and then sums up the first seven fields -// provided. See `man 5 proc` for details on specific field -// information. -func GetSystemCPUUsage() (uint64, error) { - var line string - f, err := os.Open("/proc/stat") - if err != nil { - return 0, err - } - bufReader := bufio.NewReaderSize(nil, 128) - defer func() { - bufReader.Reset(nil) - f.Close() - }() - bufReader.Reset(f) - err = nil - for err == nil { - line, err = bufReader.ReadString('\n') - if err != nil { - break - } - parts := strings.Fields(line) - switch parts[0] { - case "cpu": - if len(parts) < 8 { - return 0, fmt.Errorf("bad format of cpu stats") - } - var totalClockTicks uint64 - for _, i := range parts[1:8] { - v, err := strconv.ParseUint(i, 10, 64) - if err != nil { - return 0, fmt.Errorf("error parsing cpu stats") - } - totalClockTicks += v - } - return (totalClockTicks * nanoSecondsPerSecond) / - clockTicksPerSecond, nil - } - } - return 0, fmt.Errorf("bad stats format") -} diff --git a/vendor/github.com/containerd/containerd/sys/reaper.go b/vendor/github.com/containerd/containerd/sys/reaper.go deleted file mode 100644 index d08ccccfbca89..0000000000000 --- a/vendor/github.com/containerd/containerd/sys/reaper.go +++ /dev/null @@ -1,69 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package sys - -import ( - "golang.org/x/sys/unix" -) - -// Exit is the wait4 information from an exited process -type Exit struct { - Pid int - Status int -} - -// Reap reaps all child processes for the calling process and returns their -// exit information -func Reap(wait bool) (exits []Exit, err error) { - var ( - ws unix.WaitStatus - rus unix.Rusage - ) - flag := unix.WNOHANG - if wait { - flag = 0 - } - for { - pid, err := unix.Wait4(-1, &ws, flag, &rus) - if err != nil { - if err == unix.ECHILD { - return exits, nil - } - return exits, err - } - if pid <= 0 { - return exits, nil - } - exits = append(exits, Exit{ - Pid: pid, - Status: exitStatus(ws), - }) - } -} - -const exitSignalOffset = 128 - -// exitStatus returns the correct exit status for a process based on if it -// was signaled or exited cleanly -func exitStatus(status unix.WaitStatus) int { - if status.Signaled() { - return exitSignalOffset + int(status.Signal()) - } - return status.ExitStatus() -} diff --git a/vendor/github.com/containerd/containerd/sys/reaper/reaper_unix.go b/vendor/github.com/containerd/containerd/sys/reaper/reaper_unix.go new file mode 100644 index 0000000000000..0033178dfa77a --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/reaper/reaper_unix.go @@ -0,0 +1,248 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package reaper + +import ( + "os/exec" + "sync" + "time" + + runc "github.com/containerd/go-runc" + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// ErrNoSuchProcess is returned when the process no longer exists +var ErrNoSuchProcess = errors.New("no such process") + +const bufferSize = 32 + +type subscriber struct { + sync.Mutex + c chan runc.Exit + closed bool +} + +func (s *subscriber) close() { + s.Lock() + if s.closed { + s.Unlock() + return + } + close(s.c) + s.closed = true + s.Unlock() +} + +func (s *subscriber) do(fn func()) { + s.Lock() + fn() + s.Unlock() +} + +// Reap should be called when the process receives an SIGCHLD. Reap will reap +// all exited processes and close their wait channels +func Reap() error { + now := time.Now() + exits, err := reap(false) + for _, e := range exits { + done := Default.notify(runc.Exit{ + Timestamp: now, + Pid: e.Pid, + Status: e.Status, + }) + + select { + case <-done: + case <-time.After(1 * time.Second): + } + } + return err +} + +// Default is the default monitor initialized for the package +var Default = &Monitor{ + subscribers: make(map[chan runc.Exit]*subscriber), +} + +// Monitor monitors the underlying system for process status changes +type Monitor struct { + sync.Mutex + + subscribers map[chan runc.Exit]*subscriber +} + +// Start starts the command a registers the process with the reaper +func (m *Monitor) Start(c *exec.Cmd) (chan runc.Exit, error) { + ec := m.Subscribe() + if err := c.Start(); err != nil { + m.Unsubscribe(ec) + return nil, err + } + return ec, nil +} + +// Wait blocks until a process is signal as dead. +// User should rely on the value of the exit status to determine if the +// command was successful or not. +func (m *Monitor) Wait(c *exec.Cmd, ec chan runc.Exit) (int, error) { + for e := range ec { + if e.Pid == c.Process.Pid { + // make sure we flush all IO + c.Wait() + m.Unsubscribe(ec) + return e.Status, nil + } + } + // return no such process if the ec channel is closed and no more exit + // events will be sent + return -1, ErrNoSuchProcess +} + +// Subscribe to process exit changes +func (m *Monitor) Subscribe() chan runc.Exit { + c := make(chan runc.Exit, bufferSize) + m.Lock() + m.subscribers[c] = &subscriber{ + c: c, + } + m.Unlock() + return c +} + +// Unsubscribe to process exit changes +func (m *Monitor) Unsubscribe(c chan runc.Exit) { + m.Lock() + s, ok := m.subscribers[c] + if !ok { + m.Unlock() + return + } + s.close() + delete(m.subscribers, c) + m.Unlock() +} + +func (m *Monitor) getSubscribers() map[chan runc.Exit]*subscriber { + out := make(map[chan runc.Exit]*subscriber) + m.Lock() + for k, v := range m.subscribers { + out[k] = v + } + m.Unlock() + return out +} + +func (m *Monitor) notify(e runc.Exit) chan struct{} { + const timeout = 1 * time.Millisecond + var ( + done = make(chan struct{}, 1) + timer = time.NewTimer(timeout) + success = make(map[chan runc.Exit]struct{}) + ) + stop(timer, true) + + go func() { + defer close(done) + + for { + var ( + failed int + subscribers = m.getSubscribers() + ) + for _, s := range subscribers { + s.do(func() { + if s.closed { + return + } + if _, ok := success[s.c]; ok { + return + } + timer.Reset(timeout) + recv := true + select { + case s.c <- e: + success[s.c] = struct{}{} + case <-timer.C: + recv = false + failed++ + } + stop(timer, recv) + }) + } + // all subscribers received the message + if failed == 0 { + return + } + } + }() + return done +} + +func stop(timer *time.Timer, recv bool) { + if !timer.Stop() && recv { + <-timer.C + } +} + +// exit is the wait4 information from an exited process +type exit struct { + Pid int + Status int +} + +// reap reaps all child processes for the calling process and returns their +// exit information +func reap(wait bool) (exits []exit, err error) { + var ( + ws unix.WaitStatus + rus unix.Rusage + ) + flag := unix.WNOHANG + if wait { + flag = 0 + } + for { + pid, err := unix.Wait4(-1, &ws, flag, &rus) + if err != nil { + if err == unix.ECHILD { + return exits, nil + } + return exits, err + } + if pid <= 0 { + return exits, nil + } + exits = append(exits, exit{ + Pid: pid, + Status: exitStatus(ws), + }) + } +} + +const exitSignalOffset = 128 + +// exitStatus returns the correct exit status for a process based on if it +// was signaled or exited cleanly +func exitStatus(status unix.WaitStatus) int { + if status.Signaled() { + return exitSignalOffset + int(status.Signal()) + } + return status.ExitStatus() +} diff --git a/vendor/github.com/containerd/containerd/sys/reaper/reaper_utils_linux.go b/vendor/github.com/containerd/containerd/sys/reaper/reaper_utils_linux.go new file mode 100644 index 0000000000000..cadcdc42c381d --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/reaper/reaper_utils_linux.go @@ -0,0 +1,39 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package reaper + +import ( + "unsafe" + + "golang.org/x/sys/unix" +) + +// SetSubreaper sets the value i as the subreaper setting for the calling process +func SetSubreaper(i int) error { + return unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0) +} + +// GetSubreaper returns the subreaper setting for the calling process +func GetSubreaper() (int, error) { + var i uintptr + + if err := unix.Prctl(unix.PR_GET_CHILD_SUBREAPER, uintptr(unsafe.Pointer(&i)), 0, 0, 0); err != nil { + return -1, err + } + + return int(i), nil +} diff --git a/vendor/github.com/containerd/containerd/sys/reaper_linux.go b/vendor/github.com/containerd/containerd/sys/reaper_linux.go deleted file mode 100644 index ecb0bd031ede7..0000000000000 --- a/vendor/github.com/containerd/containerd/sys/reaper_linux.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package sys - -import ( - "unsafe" - - "golang.org/x/sys/unix" -) - -// If arg2 is nonzero, set the "child subreaper" attribute of the -// calling process; if arg2 is zero, unset the attribute. When a -// process is marked as a child subreaper, all of the children -// that it creates, and their descendants, will be marked as -// having a subreaper. In effect, a subreaper fulfills the role -// of init(1) for its descendant processes. Upon termination of -// a process that is orphaned (i.e., its immediate parent has -// already terminated) and marked as having a subreaper, the -// nearest still living ancestor subreaper will receive a SIGCHLD -// signal and be able to wait(2) on the process to discover its -// termination status. -const setChildSubreaper = 36 - -// SetSubreaper sets the value i as the subreaper setting for the calling process -func SetSubreaper(i int) error { - return unix.Prctl(setChildSubreaper, uintptr(i), 0, 0, 0) -} - -// GetSubreaper returns the subreaper setting for the calling process -func GetSubreaper() (int, error) { - var i uintptr - - if err := unix.Prctl(unix.PR_GET_CHILD_SUBREAPER, uintptr(unsafe.Pointer(&i)), 0, 0, 0); err != nil { - return -1, err - } - - return int(i), nil -} diff --git a/vendor/github.com/containerd/containerd/sys/socket_unix.go b/vendor/github.com/containerd/containerd/sys/socket_unix.go index 90fa55c482f96..b67cc1fa3a71d 100644 --- a/vendor/github.com/containerd/containerd/sys/socket_unix.go +++ b/vendor/github.com/containerd/containerd/sys/socket_unix.go @@ -68,11 +68,11 @@ func GetLocalListener(path string, uid, gid int) (net.Listener, error) { } func mkdirAs(path string, uid, gid int) error { - if _, err := os.Stat(path); err == nil || !os.IsNotExist(err) { + if _, err := os.Stat(path); !os.IsNotExist(err) { return err } - if err := os.Mkdir(path, 0770); err != nil { + if err := os.MkdirAll(path, 0770); err != nil { return err } diff --git a/vendor/github.com/containerd/containerd/sys/stat_bsd.go b/vendor/github.com/containerd/containerd/sys/stat_bsd.go index b9c95d90d783e..4f03cd6cb0f39 100644 --- a/vendor/github.com/containerd/containerd/sys/stat_bsd.go +++ b/vendor/github.com/containerd/containerd/sys/stat_bsd.go @@ -1,4 +1,4 @@ -// +build darwin freebsd +// +build darwin freebsd netbsd /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/containerd/sys/stat_openbsd.go b/vendor/github.com/containerd/containerd/sys/stat_openbsd.go new file mode 100644 index 0000000000000..ec3b9df69aa29 --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/stat_openbsd.go @@ -0,0 +1,45 @@ +// +build openbsd + +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import ( + "syscall" + "time" +) + +// StatAtime returns the Atim +func StatAtime(st *syscall.Stat_t) syscall.Timespec { + return st.Atim +} + +// StatCtime returns the Ctim +func StatCtime(st *syscall.Stat_t) syscall.Timespec { + return st.Ctim +} + +// StatMtime returns the Mtim +func StatMtime(st *syscall.Stat_t) syscall.Timespec { + return st.Mtim +} + +// StatATimeAsTime returns st.Atim as a time.Time +func StatATimeAsTime(st *syscall.Stat_t) time.Time { + // The int64 conversions ensure the line compiles for 32-bit systems as well. + return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) // nolint: unconvert +} diff --git a/vendor/github.com/containerd/containerd/sys/userns_deprecated.go b/vendor/github.com/containerd/containerd/sys/userns_deprecated.go new file mode 100644 index 0000000000000..53acf55477e36 --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/userns_deprecated.go @@ -0,0 +1,23 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import "github.com/containerd/containerd/pkg/userns" + +// RunningInUserNS detects whether we are currently running in a user namespace. +// Deprecated: use github.com/containerd/containerd/pkg/userns.RunningInUserNS instead. +var RunningInUserNS = userns.RunningInUserNS diff --git a/vendor/github.com/containerd/containerd/task.go b/vendor/github.com/containerd/containerd/task.go index 6806e1162081e..4e23fb8616bab 100644 --- a/vendor/github.com/containerd/containerd/task.go +++ b/vendor/github.com/containerd/containerd/task.go @@ -35,13 +35,16 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/oci" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/rootfs" + "github.com/containerd/containerd/runtime/linux/runctypes" + "github.com/containerd/containerd/runtime/v2/runc/options" "github.com/containerd/typeurl" google_protobuf "github.com/gogo/protobuf/types" digest "github.com/opencontainers/go-digest" is "github.com/opencontainers/image-spec/specs-go" - "github.com/opencontainers/image-spec/specs-go/v1" + v1 "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -115,6 +118,13 @@ type CheckpointTaskInfo struct { ParentCheckpoint digest.Digest // Options hold runtime specific settings for checkpointing a task Options interface{} + + runtime string +} + +// Runtime name for the container +func (i *CheckpointTaskInfo) Runtime() string { + return i.runtime } // CheckpointTaskOpts allows the caller to set checkpoint options @@ -129,6 +139,12 @@ type TaskInfo struct { RootFS []mount.Mount // Options hold runtime specific settings for task creation Options interface{} + runtime string +} + +// Runtime name for the container +func (i *TaskInfo) Runtime() string { + return i.runtime } // Task is the executable object within containerd @@ -144,9 +160,11 @@ type Task interface { // Pids returns a list of system specific process ids inside the task Pids(context.Context) ([]ProcessInfo, error) // Checkpoint serializes the runtime and memory information of a task into an - // OCI Index that can be push and pulled from a remote resource. + // OCI Index that can be pushed and pulled from a remote resource. // // Additional software like CRIU maybe required to checkpoint and restore tasks + // NOTE: Checkpoint supports to dump task information to a directory, in this way, + // an empty OCI Index will be returned. Checkpoint(context.Context, ...CheckpointTaskOpts) (Image, error) // Update modifies executing tasks with updated settings Update(context.Context, ...UpdateTaskOpts) error @@ -158,18 +176,26 @@ type Task interface { // For the built in Linux runtime, github.com/containerd/cgroups.Metrics // are returned in protobuf format Metrics(context.Context) (*types.Metric, error) + // Spec returns the current OCI specification for the task + Spec(context.Context) (*oci.Spec, error) } var _ = (Task)(&task{}) type task struct { client *Client + c Container io cio.IO id string pid uint32 } +// Spec returns the current OCI specification for the task +func (t *task) Spec(ctx context.Context) (*oci.Spec, error) { + return t.c.Spec(ctx) +} + // ID of the task func (t *task) ID() string { return t.id @@ -185,8 +211,10 @@ func (t *task) Start(ctx context.Context) error { ContainerID: t.id, }) if err != nil { - t.io.Cancel() - t.io.Close() + if t.io != nil { + t.io.Cancel() + t.io.Close() + } return errdefs.FromGRPC(err) } t.pid = r.Pid @@ -287,6 +315,7 @@ func (t *task) Delete(ctx context.Context, opts ...ProcessDeleteOpts) (*ExitStat return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "task must be stopped before deletion: %s", status.Status) } if t.io != nil { + t.io.Close() t.io.Cancel() t.io.Wait() } @@ -387,17 +416,25 @@ func (t *task) Resize(ctx context.Context, w, h uint32) error { return errdefs.FromGRPC(err) } +// NOTE: Checkpoint supports to dump task information to a directory, in this way, an empty +// OCI Index will be returned. func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Image, error) { ctx, done, err := t.client.WithLease(ctx) if err != nil { return nil, err } defer done(ctx) + cr, err := t.client.ContainerService().Get(ctx, t.id) + if err != nil { + return nil, err + } request := &tasks.CheckpointTaskRequest{ ContainerID: t.id, } - var i CheckpointTaskInfo + i := CheckpointTaskInfo{ + runtime: cr.Runtime.Name, + } for _, o := range opts { if err := o(&i); err != nil { return nil, err @@ -415,15 +452,20 @@ func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Imag } request.Options = any } - // make sure we pause it and resume after all other filesystem operations are completed - if err := t.Pause(ctx); err != nil { - return nil, err - } - defer t.Resume(ctx) - cr, err := t.client.ContainerService().Get(ctx, t.id) + + status, err := t.Status(ctx) if err != nil { return nil, err } + + if status.Status != Paused { + // make sure we pause it and resume after all other filesystem operations are completed + if err := t.Pause(ctx); err != nil { + return nil, err + } + defer t.Resume(ctx) + } + index := v1.Index{ Versioned: is.Versioned{ SchemaVersion: 2, @@ -433,6 +475,12 @@ func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Imag if err := t.checkpointTask(ctx, &index, request); err != nil { return nil, err } + // if checkpoint image path passed, jump checkpoint image, + // return an empty image + if isCheckpointPathExist(cr.Runtime.Name, i.Options) { + return NewImage(t.client, images.Image{}), nil + } + if cr.Image != "" { if err := t.checkpointImage(ctx, &index, cr.Image); err != nil { return nil, err @@ -465,6 +513,8 @@ func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Imag type UpdateTaskInfo struct { // Resources updates a tasks resource constraints Resources interface{} + // Annotations allows arbitrary and/or experimental resource constraints for task update + Annotations map[string]string } // UpdateTaskOpts allows a caller to update task settings @@ -487,11 +537,17 @@ func (t *task) Update(ctx context.Context, opts ...UpdateTaskOpts) error { } request.Resources = any } + if i.Annotations != nil { + request.Annotations = i.Annotations + } _, err := t.client.TaskService().Update(ctx, request) return errdefs.FromGRPC(err) } func (t *task) LoadProcess(ctx context.Context, id string, ioAttach cio.Attach) (Process, error) { + if id == t.id && ioAttach == nil { + return t, nil + } response, err := t.client.TaskService().Get(ctx, &tasks.GetRequest{ ContainerID: t.id, ExecID: id, @@ -542,6 +598,7 @@ func (t *task) checkpointTask(ctx context.Context, index *v1.Index, request *tas if err != nil { return errdefs.FromGRPC(err) } + // NOTE: response.Descriptors can be an empty slice if checkpoint image is jumped // add the checkpoint descriptors to the index for _, d := range response.Descriptors { index.Manifests = append(index.Manifests, v1.Descriptor{ @@ -552,6 +609,7 @@ func (t *task) checkpointTask(ctx context.Context, index *v1.Index, request *tas OS: goruntime.GOOS, Architecture: goruntime.GOARCH, }, + Annotations: d.Annotations, }) } return nil @@ -619,3 +677,24 @@ func writeContent(ctx context.Context, store content.Ingester, mediaType, ref st Size: size, }, nil } + +// isCheckpointPathExist only suitable for runc runtime now +func isCheckpointPathExist(runtime string, v interface{}) bool { + if v == nil { + return false + } + + switch runtime { + case plugin.RuntimeRuncV1, plugin.RuntimeRuncV2: + if opts, ok := v.(*options.CheckpointOptions); ok && opts.ImagePath != "" { + return true + } + + case plugin.RuntimeLinuxV1: + if opts, ok := v.(*runctypes.CheckpointOptions); ok && opts.ImagePath != "" { + return true + } + } + + return false +} diff --git a/vendor/github.com/containerd/containerd/task_opts.go b/vendor/github.com/containerd/containerd/task_opts.go index ce861ea51dde8..e8d99eb512925 100644 --- a/vendor/github.com/containerd/containerd/task_opts.go +++ b/vendor/github.com/containerd/containerd/task_opts.go @@ -27,6 +27,8 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/runtime/linux/runctypes" + "github.com/containerd/containerd/runtime/v2/runc/options" imagespec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -57,9 +59,10 @@ func WithTaskCheckpoint(im Image) NewTaskOpts { for _, m := range index.Manifests { if m.MediaType == images.MediaTypeContainerd1Checkpoint { info.Checkpoint = &types.Descriptor{ - MediaType: m.MediaType, - Size_: m.Size, - Digest: m.Digest, + MediaType: m.MediaType, + Size_: m.Size, + Digest: m.Digest, + Annotations: m.Annotations, } return nil } @@ -89,6 +92,58 @@ func WithCheckpointName(name string) CheckpointTaskOpts { } } +// WithCheckpointImagePath sets image path for checkpoint option +func WithCheckpointImagePath(path string) CheckpointTaskOpts { + return func(r *CheckpointTaskInfo) error { + if CheckRuntime(r.Runtime(), "io.containerd.runc") { + if r.Options == nil { + r.Options = &options.CheckpointOptions{} + } + opts, ok := r.Options.(*options.CheckpointOptions) + if !ok { + return errors.New("invalid v2 shim checkpoint options format") + } + opts.ImagePath = path + } else { + if r.Options == nil { + r.Options = &runctypes.CheckpointOptions{} + } + opts, ok := r.Options.(*runctypes.CheckpointOptions) + if !ok { + return errors.New("invalid v1 shim checkpoint options format") + } + opts.ImagePath = path + } + return nil + } +} + +// WithRestoreImagePath sets image path for create option +func WithRestoreImagePath(path string) NewTaskOpts { + return func(ctx context.Context, c *Client, ti *TaskInfo) error { + if CheckRuntime(ti.Runtime(), "io.containerd.runc") { + if ti.Options == nil { + ti.Options = &options.Options{} + } + opts, ok := ti.Options.(*options.Options) + if !ok { + return errors.New("invalid v2 shim create options format") + } + opts.CriuImagePath = path + } else { + if ti.Options == nil { + ti.Options = &runctypes.CreateOptions{} + } + opts, ok := ti.Options.(*runctypes.CreateOptions) + if !ok { + return errors.New("invalid v1 shim create options format") + } + opts.CriuImagePath = path + } + return nil + } +} + // ProcessDeleteOpts allows the caller to set options for the deletion of a task type ProcessDeleteOpts func(context.Context, Process) error @@ -154,3 +209,11 @@ func WithResources(resources interface{}) UpdateTaskOpts { return nil } } + +// WithAnnotations sets the provided annotations for task updates. +func WithAnnotations(annotations map[string]string) UpdateTaskOpts { + return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error { + r.Annotations = annotations + return nil + } +} diff --git a/vendor/github.com/containerd/containerd/task_opts_unix.go b/vendor/github.com/containerd/containerd/task_opts_unix.go index f8652be3bc69d..a710b358fce4d 100644 --- a/vendor/github.com/containerd/containerd/task_opts_unix.go +++ b/vendor/github.com/containerd/containerd/task_opts_unix.go @@ -22,36 +22,136 @@ import ( "context" "github.com/containerd/containerd/runtime/linux/runctypes" + "github.com/containerd/containerd/runtime/v2/runc/options" "github.com/pkg/errors" ) // WithNoNewKeyring causes tasks not to be created with a new keyring for secret storage. // There is an upper limit on the number of keyrings in a linux system func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error { - if ti.Options == nil { - ti.Options = &runctypes.CreateOptions{} - } - opts, ok := ti.Options.(*runctypes.CreateOptions) - if !ok { - return errors.New("could not cast TaskInfo Options to CreateOptions") + if CheckRuntime(ti.Runtime(), "io.containerd.runc") { + if ti.Options == nil { + ti.Options = &options.Options{} + } + opts, ok := ti.Options.(*options.Options) + if !ok { + return errors.New("invalid v2 shim create options format") + } + opts.NoNewKeyring = true + } else { + if ti.Options == nil { + ti.Options = &runctypes.CreateOptions{} + } + opts, ok := ti.Options.(*runctypes.CreateOptions) + if !ok { + return errors.New("could not cast TaskInfo Options to CreateOptions") + } + opts.NoNewKeyring = true } - - opts.NoNewKeyring = true return nil } // WithNoPivotRoot instructs the runtime not to you pivot_root -func WithNoPivotRoot(_ context.Context, _ *Client, info *TaskInfo) error { - if info.Options == nil { - info.Options = &runctypes.CreateOptions{ - NoPivotRoot: true, +func WithNoPivotRoot(_ context.Context, _ *Client, ti *TaskInfo) error { + if CheckRuntime(ti.Runtime(), "io.containerd.runc") { + if ti.Options == nil { + ti.Options = &options.Options{} + } + opts, ok := ti.Options.(*options.Options) + if !ok { + return errors.New("invalid v2 shim create options format") + } + opts.NoPivotRoot = true + } else { + if ti.Options == nil { + ti.Options = &runctypes.CreateOptions{ + NoPivotRoot: true, + } + return nil + } + opts, ok := ti.Options.(*runctypes.CreateOptions) + if !ok { + return errors.New("invalid options type, expected runctypes.CreateOptions") + } + opts.NoPivotRoot = true + } + return nil +} + +// WithShimCgroup sets the existing cgroup for the shim +func WithShimCgroup(path string) NewTaskOpts { + return func(ctx context.Context, c *Client, ti *TaskInfo) error { + if CheckRuntime(ti.Runtime(), "io.containerd.runc") { + if ti.Options == nil { + ti.Options = &options.Options{} + } + opts, ok := ti.Options.(*options.Options) + if !ok { + return errors.New("invalid v2 shim create options format") + } + opts.ShimCgroup = path + } else { + if ti.Options == nil { + ti.Options = &runctypes.CreateOptions{} + } + opts, ok := ti.Options.(*runctypes.CreateOptions) + if !ok { + return errors.New("could not cast TaskInfo Options to CreateOptions") + } + opts.ShimCgroup = path + } + return nil + } +} + +// WithUIDOwner allows console I/O to work with the remapped UID in user namespace +func WithUIDOwner(uid uint32) NewTaskOpts { + return func(ctx context.Context, c *Client, ti *TaskInfo) error { + if CheckRuntime(ti.Runtime(), "io.containerd.runc") { + if ti.Options == nil { + ti.Options = &options.Options{} + } + opts, ok := ti.Options.(*options.Options) + if !ok { + return errors.New("invalid v2 shim create options format") + } + opts.IoUid = uid + } else { + if ti.Options == nil { + ti.Options = &runctypes.CreateOptions{} + } + opts, ok := ti.Options.(*runctypes.CreateOptions) + if !ok { + return errors.New("could not cast TaskInfo Options to CreateOptions") + } + opts.IoUid = uid } return nil } - opts, ok := info.Options.(*runctypes.CreateOptions) - if !ok { - return errors.New("invalid options type, expected runctypes.CreateOptions") +} + +// WithGIDOwner allows console I/O to work with the remapped GID in user namespace +func WithGIDOwner(gid uint32) NewTaskOpts { + return func(ctx context.Context, c *Client, ti *TaskInfo) error { + if CheckRuntime(ti.Runtime(), "io.containerd.runc") { + if ti.Options == nil { + ti.Options = &options.Options{} + } + opts, ok := ti.Options.(*options.Options) + if !ok { + return errors.New("invalid v2 shim create options format") + } + opts.IoGid = gid + } else { + if ti.Options == nil { + ti.Options = &runctypes.CreateOptions{} + } + opts, ok := ti.Options.(*runctypes.CreateOptions) + if !ok { + return errors.New("could not cast TaskInfo Options to CreateOptions") + } + opts.IoGid = gid + } + return nil } - opts.NoPivotRoot = true - return nil } diff --git a/vendor/github.com/containerd/containerd/unpacker.go b/vendor/github.com/containerd/containerd/unpacker.go new file mode 100644 index 0000000000000..76f5d7b0c4fb6 --- /dev/null +++ b/vendor/github.com/containerd/containerd/unpacker.go @@ -0,0 +1,353 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "math/rand" + "sync" + "sync/atomic" + "time" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/log" + "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/platforms" + "github.com/containerd/containerd/snapshots" + "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/identity" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" +) + +const ( + labelSnapshotRef = "containerd.io/snapshot.ref" +) + +type unpacker struct { + updateCh chan ocispec.Descriptor + snapshotter string + config UnpackConfig + c *Client + limiter *semaphore.Weighted +} + +func (c *Client) newUnpacker(ctx context.Context, rCtx *RemoteContext) (*unpacker, error) { + snapshotter, err := c.resolveSnapshotterName(ctx, rCtx.Snapshotter) + if err != nil { + return nil, err + } + var config UnpackConfig + for _, o := range rCtx.UnpackOpts { + if err := o(ctx, &config); err != nil { + return nil, err + } + } + return &unpacker{ + updateCh: make(chan ocispec.Descriptor, 128), + snapshotter: snapshotter, + config: config, + c: c, + }, nil +} + +func (u *unpacker) unpack( + ctx context.Context, + rCtx *RemoteContext, + h images.Handler, + config ocispec.Descriptor, + layers []ocispec.Descriptor, +) error { + p, err := content.ReadBlob(ctx, u.c.ContentStore(), config) + if err != nil { + return err + } + + var i ocispec.Image + if err := json.Unmarshal(p, &i); err != nil { + return errors.Wrap(err, "unmarshal image config") + } + diffIDs := i.RootFS.DiffIDs + if len(layers) != len(diffIDs) { + return errors.Errorf("number of layers and diffIDs don't match: %d != %d", len(layers), len(diffIDs)) + } + + if u.config.CheckPlatformSupported { + imgPlatform := platforms.Normalize(ocispec.Platform{OS: i.OS, Architecture: i.Architecture}) + snapshotterPlatformMatcher, err := u.c.GetSnapshotterSupportedPlatforms(ctx, u.snapshotter) + if err != nil { + return errors.Wrapf(err, "failed to find supported platforms for snapshotter %s", u.snapshotter) + } + if !snapshotterPlatformMatcher.Match(imgPlatform) { + return fmt.Errorf("snapshotter %s does not support platform %s for image %s", u.snapshotter, imgPlatform, config.Digest) + } + } + + var ( + sn = u.c.SnapshotService(u.snapshotter) + a = u.c.DiffService() + cs = u.c.ContentStore() + + chain []digest.Digest + + fetchOffset int + fetchC []chan struct{} + fetchErr chan error + ) + + // If there is an early return, ensure any ongoing + // fetches get their context cancelled + ctx, cancel := context.WithCancel(ctx) + defer cancel() + +EachLayer: + for i, desc := range layers { + parent := identity.ChainID(chain) + chain = append(chain, diffIDs[i]) + + chainID := identity.ChainID(chain).String() + if _, err := sn.Stat(ctx, chainID); err == nil { + // no need to handle + continue + } else if !errdefs.IsNotFound(err) { + return errors.Wrapf(err, "failed to stat snapshot %s", chainID) + } + + // inherits annotations which are provided as snapshot labels. + labels := snapshots.FilterInheritedLabels(desc.Annotations) + if labels == nil { + labels = make(map[string]string) + } + labels[labelSnapshotRef] = chainID + + var ( + key string + mounts []mount.Mount + opts = append(rCtx.SnapshotterOpts, snapshots.WithLabels(labels)) + ) + + for try := 1; try <= 3; try++ { + // Prepare snapshot with from parent, label as root + key = fmt.Sprintf(snapshots.UnpackKeyFormat, uniquePart(), chainID) + mounts, err = sn.Prepare(ctx, key, parent.String(), opts...) + if err != nil { + if errdefs.IsAlreadyExists(err) { + if _, err := sn.Stat(ctx, chainID); err != nil { + if !errdefs.IsNotFound(err) { + return errors.Wrapf(err, "failed to stat snapshot %s", chainID) + } + // Try again, this should be rare, log it + log.G(ctx).WithField("key", key).WithField("chainid", chainID).Debug("extraction snapshot already exists, chain id not found") + } else { + // no need to handle, snapshot now found with chain id + continue EachLayer + } + } else { + return errors.Wrapf(err, "failed to prepare extraction snapshot %q", key) + } + } else { + break + } + } + if err != nil { + return errors.Wrap(err, "unable to prepare extraction snapshot") + } + + // Abort the snapshot if commit does not happen + abort := func() { + if err := sn.Remove(ctx, key); err != nil { + log.G(ctx).WithError(err).Errorf("failed to cleanup %q", key) + } + } + + if fetchErr == nil { + fetchErr = make(chan error, 1) + fetchOffset = i + fetchC = make([]chan struct{}, len(layers)-fetchOffset) + for i := range fetchC { + fetchC[i] = make(chan struct{}) + } + + go func(i int) { + err := u.fetch(ctx, h, layers[i:], fetchC) + if err != nil { + fetchErr <- err + } + close(fetchErr) + }(i) + } + + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-fetchErr: + if err != nil { + return err + } + case <-fetchC[i-fetchOffset]: + } + + diff, err := a.Apply(ctx, desc, mounts, u.config.ApplyOpts...) + if err != nil { + abort() + return errors.Wrapf(err, "failed to extract layer %s", diffIDs[i]) + } + if diff.Digest != diffIDs[i] { + abort() + return errors.Errorf("wrong diff id calculated on extraction %q", diffIDs[i]) + } + + if err = sn.Commit(ctx, chainID, key, opts...); err != nil { + abort() + if errdefs.IsAlreadyExists(err) { + continue + } + return errors.Wrapf(err, "failed to commit snapshot %s", key) + } + + // Set the uncompressed label after the uncompressed + // digest has been verified through apply. + cinfo := content.Info{ + Digest: desc.Digest, + Labels: map[string]string{ + "containerd.io/uncompressed": diff.Digest.String(), + }, + } + if _, err := cs.Update(ctx, cinfo, "labels.containerd.io/uncompressed"); err != nil { + return err + } + + } + + chainID := identity.ChainID(chain).String() + cinfo := content.Info{ + Digest: config.Digest, + Labels: map[string]string{ + fmt.Sprintf("containerd.io/gc.ref.snapshot.%s", u.snapshotter): chainID, + }, + } + _, err = cs.Update(ctx, cinfo, fmt.Sprintf("labels.containerd.io/gc.ref.snapshot.%s", u.snapshotter)) + if err != nil { + return err + } + log.G(ctx).WithFields(logrus.Fields{ + "config": config.Digest, + "chainID": chainID, + }).Debug("image unpacked") + + return nil +} + +func (u *unpacker) fetch(ctx context.Context, h images.Handler, layers []ocispec.Descriptor, done []chan struct{}) error { + eg, ctx2 := errgroup.WithContext(ctx) + for i, desc := range layers { + desc := desc + i := i + + if u.limiter != nil { + if err := u.limiter.Acquire(ctx, 1); err != nil { + return err + } + } + + eg.Go(func() error { + _, err := h.Handle(ctx2, desc) + if u.limiter != nil { + u.limiter.Release(1) + } + if err != nil && !errors.Is(err, images.ErrSkipDesc) { + return err + } + close(done[i]) + + return nil + }) + } + + return eg.Wait() +} + +func (u *unpacker) handlerWrapper( + uctx context.Context, + rCtx *RemoteContext, + unpacks *int32, +) (func(images.Handler) images.Handler, *errgroup.Group) { + eg, uctx := errgroup.WithContext(uctx) + return func(f images.Handler) images.Handler { + var ( + lock sync.Mutex + layers = map[digest.Digest][]ocispec.Descriptor{} + ) + return images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + children, err := f.Handle(ctx, desc) + if err != nil { + return children, err + } + + switch desc.MediaType { + case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: + var nonLayers []ocispec.Descriptor + var manifestLayers []ocispec.Descriptor + + // Split layers from non-layers, layers will be handled after + // the config + for _, child := range children { + if images.IsLayerType(child.MediaType) { + manifestLayers = append(manifestLayers, child) + } else { + nonLayers = append(nonLayers, child) + } + } + + lock.Lock() + for _, nl := range nonLayers { + layers[nl.Digest] = manifestLayers + } + lock.Unlock() + + children = nonLayers + case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig: + lock.Lock() + l := layers[desc.Digest] + lock.Unlock() + if len(l) > 0 { + atomic.AddInt32(unpacks, 1) + eg.Go(func() error { + return u.unpack(uctx, rCtx, f, desc, l) + }) + } + } + return children, nil + }) + }, eg +} + +func uniquePart() string { + t := time.Now() + var b [3]byte + // Ignore read failures, just decreases uniqueness + rand.Read(b[:]) + return fmt.Sprintf("%d-%s", t.Nanosecond(), base64.URLEncoding.EncodeToString(b[:])) +} diff --git a/vendor/github.com/containerd/containerd/vendor.conf b/vendor/github.com/containerd/containerd/vendor.conf deleted file mode 100644 index c4cd8f1f42aea..0000000000000 --- a/vendor/github.com/containerd/containerd/vendor.conf +++ /dev/null @@ -1,89 +0,0 @@ -github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 -github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 -github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 -github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 -github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c -github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244 -github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 -github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6 -github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098 -github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 -github.com/docker/go-units v0.3.1 -github.com/godbus/dbus c7fdd8b5cd55e87b4e1f4e372cdb1db61dd6c66f -github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823 -github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c -github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563 -github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd -github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 -github.com/matttproud/golang_protobuf_extensions v1.0.0 -github.com/gogo/protobuf v1.0.0 -github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef -github.com/golang/protobuf v1.1.0 -github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 # v1.0.1-45-geba862d -github.com/opencontainers/runc 00dc70017d222b178a002ed30e9321b12647af2d -github.com/sirupsen/logrus v1.0.0 -github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c -golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac -google.golang.org/grpc v1.12.0 -github.com/pkg/errors v0.8.0 -github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7 -golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2 https://github.com/golang/sys -github.com/opencontainers/image-spec v1.0.1 -golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c -github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 -github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 -github.com/Microsoft/go-winio v0.4.10 -github.com/Microsoft/hcsshim v0.7.6 -google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 -golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 -github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a -github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 -gotest.tools v2.1.0 -github.com/google/go-cmp v0.1.0 -go.etcd.io/bbolt v1.3.1-etcd.8 - -# cri dependencies -github.com/containerd/cri 9f39e3289533fc228c5e5fcac0a6dbdd60c6047b # release/1.2 branch -github.com/containerd/go-cni 6d7b509a054a3cb1c35ed1865d4fde2f0cb547cd -github.com/blang/semver v3.1.0 -github.com/containernetworking/cni v0.6.0 -github.com/containernetworking/plugins v0.7.0 -github.com/davecgh/go-spew v1.1.0 -github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621 -github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00 -github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528 -github.com/emicklei/go-restful v2.2.1 -github.com/ghodss/yaml v1.0.0 -github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed -github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c -github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 -github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f -github.com/json-iterator/go 1.1.5 -github.com/modern-go/reflect2 1.0.1 -github.com/modern-go/concurrent 1.0.3 -github.com/opencontainers/runtime-tools v0.6.0 -github.com/opencontainers/selinux b6fa367ed7f534f9ba25391cc2d467085dbb445a -github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 -github.com/tchap/go-patricia v2.2.6 -github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6 -github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b -github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874 -golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067 -golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4 -golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 -gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 -gopkg.in/yaml.v2 v2.2.1 -k8s.io/api 012f271b5d41baad56190c5f1ae19bff16df0fd8 -k8s.io/apimachinery 6429050ef506887d121f3e7306e894f8900d8a63 -k8s.io/apiserver e9312c15296b6c2c923ebd5031ff5d1d5fd022d7 -k8s.io/client-go 37c3c02ec96533daec0dbda1f39a6b1d68505c79 -k8s.io/kubernetes v1.12.0-beta.1 -k8s.io/utils 982821ea41da7e7c15f3d3738921eb2e7e241ccd - -# zfs dependencies -github.com/containerd/zfs 9a0b8b8b5982014b729cd34eb7cd7a11062aa6ec -github.com/mistifyio/go-zfs 166add352731e515512690329794ee593f1aaff2 -github.com/pborman/uuid c65b2f87fee37d1c7854c9164a450713c28d50cd - -# aufs dependencies -github.com/containerd/aufs ffa39970e26ad01d81f540b21e65f9c1841a5f92 diff --git a/vendor/github.com/containerd/containerd/version/version.go b/vendor/github.com/containerd/containerd/version/version.go new file mode 100644 index 0000000000000..b8200307f3a55 --- /dev/null +++ b/vendor/github.com/containerd/containerd/version/version.go @@ -0,0 +1,34 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package version + +import "runtime" + +var ( + // Package is filled at linking time + Package = "github.com/containerd/containerd" + + // Version holds the complete version number. Filled in at linking time. + Version = "1.5.8+unknown" + + // Revision is filled with the VCS (e.g. git) revision being used to build + // the program at linking time. + Revision = "" + + // GoVersion is Go tree's version. + GoVersion = runtime.Version() +) diff --git a/vendor/github.com/containerd/containerd/windows/hcsshimtypes/doc.go b/vendor/github.com/containerd/containerd/windows/hcsshimtypes/doc.go deleted file mode 100644 index 9fe5cd0a4d836..0000000000000 --- a/vendor/github.com/containerd/containerd/windows/hcsshimtypes/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -// Package hcsshimtypes holds the windows runtime specific types -package hcsshimtypes diff --git a/vendor/github.com/containerd/containerd/windows/hcsshimtypes/hcsshim.pb.go b/vendor/github.com/containerd/containerd/windows/hcsshimtypes/hcsshim.pb.go deleted file mode 100644 index 827bb6b29ea26..0000000000000 --- a/vendor/github.com/containerd/containerd/windows/hcsshimtypes/hcsshim.pb.go +++ /dev/null @@ -1,744 +0,0 @@ -// Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: github.com/containerd/containerd/windows/hcsshimtypes/hcsshim.proto - -/* - Package hcsshimtypes is a generated protocol buffer package. - - It is generated from these files: - github.com/containerd/containerd/windows/hcsshimtypes/hcsshim.proto - - It has these top-level messages: - CreateOptions - ProcessDetails -*/ -package hcsshimtypes - -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -import _ "github.com/gogo/protobuf/types" -import _ "github.com/gogo/protobuf/types" - -import time "time" - -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" - -import io "io" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf -var _ = time.Kitchen - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package - -type CreateOptions struct { - TerminateDuration time.Duration `protobuf:"bytes,1,opt,name=terminate_duration,json=terminateDuration,stdduration" json:"terminate_duration"` -} - -func (m *CreateOptions) Reset() { *m = CreateOptions{} } -func (*CreateOptions) ProtoMessage() {} -func (*CreateOptions) Descriptor() ([]byte, []int) { return fileDescriptorHcsshim, []int{0} } - -// ProcessDetails contains additional information about a process -// ProcessDetails is made of the same fields as found in hcsshim.ProcessListItem -type ProcessDetails struct { - ImageName string `protobuf:"bytes,1,opt,name=image_name,json=imageName,proto3" json:"image_name,omitempty"` - CreatedAt time.Time `protobuf:"bytes,2,opt,name=created_at,json=createdAt,stdtime" json:"created_at"` - KernelTime_100Ns uint64 `protobuf:"varint,3,opt,name=kernel_time_100_ns,json=kernelTime100Ns,proto3" json:"kernel_time_100_ns,omitempty"` - MemoryCommitBytes uint64 `protobuf:"varint,4,opt,name=memory_commit_bytes,json=memoryCommitBytes,proto3" json:"memory_commit_bytes,omitempty"` - MemoryWorkingSetPrivateBytes uint64 `protobuf:"varint,5,opt,name=memory_working_set_private_bytes,json=memoryWorkingSetPrivateBytes,proto3" json:"memory_working_set_private_bytes,omitempty"` - MemoryWorkingSetSharedBytes uint64 `protobuf:"varint,6,opt,name=memory_working_set_shared_bytes,json=memoryWorkingSetSharedBytes,proto3" json:"memory_working_set_shared_bytes,omitempty"` - ProcessID uint32 `protobuf:"varint,7,opt,name=process_id,json=processId,proto3" json:"process_id,omitempty"` - UserTime_100Ns uint64 `protobuf:"varint,8,opt,name=user_time_100_ns,json=userTime100Ns,proto3" json:"user_time_100_ns,omitempty"` - ExecID string `protobuf:"bytes,9,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` -} - -func (m *ProcessDetails) Reset() { *m = ProcessDetails{} } -func (*ProcessDetails) ProtoMessage() {} -func (*ProcessDetails) Descriptor() ([]byte, []int) { return fileDescriptorHcsshim, []int{1} } - -func init() { - proto.RegisterType((*CreateOptions)(nil), "containerd.windows.hcsshim.CreateOptions") - proto.RegisterType((*ProcessDetails)(nil), "containerd.windows.hcsshim.ProcessDetails") -} -func (m *CreateOptions) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *CreateOptions) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintHcsshim(dAtA, i, uint64(types.SizeOfStdDuration(m.TerminateDuration))) - n1, err := types.StdDurationMarshalTo(m.TerminateDuration, dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 - return i, nil -} - -func (m *ProcessDetails) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *ProcessDetails) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ImageName) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintHcsshim(dAtA, i, uint64(len(m.ImageName))) - i += copy(dAtA[i:], m.ImageName) - } - dAtA[i] = 0x12 - i++ - i = encodeVarintHcsshim(dAtA, i, uint64(types.SizeOfStdTime(m.CreatedAt))) - n2, err := types.StdTimeMarshalTo(m.CreatedAt, dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 - if m.KernelTime_100Ns != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintHcsshim(dAtA, i, uint64(m.KernelTime_100Ns)) - } - if m.MemoryCommitBytes != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintHcsshim(dAtA, i, uint64(m.MemoryCommitBytes)) - } - if m.MemoryWorkingSetPrivateBytes != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintHcsshim(dAtA, i, uint64(m.MemoryWorkingSetPrivateBytes)) - } - if m.MemoryWorkingSetSharedBytes != 0 { - dAtA[i] = 0x30 - i++ - i = encodeVarintHcsshim(dAtA, i, uint64(m.MemoryWorkingSetSharedBytes)) - } - if m.ProcessID != 0 { - dAtA[i] = 0x38 - i++ - i = encodeVarintHcsshim(dAtA, i, uint64(m.ProcessID)) - } - if m.UserTime_100Ns != 0 { - dAtA[i] = 0x40 - i++ - i = encodeVarintHcsshim(dAtA, i, uint64(m.UserTime_100Ns)) - } - if len(m.ExecID) > 0 { - dAtA[i] = 0x4a - i++ - i = encodeVarintHcsshim(dAtA, i, uint64(len(m.ExecID))) - i += copy(dAtA[i:], m.ExecID) - } - return i, nil -} - -func encodeVarintHcsshim(dAtA []byte, offset int, v uint64) int { - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ - } - dAtA[offset] = uint8(v) - return offset + 1 -} -func (m *CreateOptions) Size() (n int) { - var l int - _ = l - l = types.SizeOfStdDuration(m.TerminateDuration) - n += 1 + l + sovHcsshim(uint64(l)) - return n -} - -func (m *ProcessDetails) Size() (n int) { - var l int - _ = l - l = len(m.ImageName) - if l > 0 { - n += 1 + l + sovHcsshim(uint64(l)) - } - l = types.SizeOfStdTime(m.CreatedAt) - n += 1 + l + sovHcsshim(uint64(l)) - if m.KernelTime_100Ns != 0 { - n += 1 + sovHcsshim(uint64(m.KernelTime_100Ns)) - } - if m.MemoryCommitBytes != 0 { - n += 1 + sovHcsshim(uint64(m.MemoryCommitBytes)) - } - if m.MemoryWorkingSetPrivateBytes != 0 { - n += 1 + sovHcsshim(uint64(m.MemoryWorkingSetPrivateBytes)) - } - if m.MemoryWorkingSetSharedBytes != 0 { - n += 1 + sovHcsshim(uint64(m.MemoryWorkingSetSharedBytes)) - } - if m.ProcessID != 0 { - n += 1 + sovHcsshim(uint64(m.ProcessID)) - } - if m.UserTime_100Ns != 0 { - n += 1 + sovHcsshim(uint64(m.UserTime_100Ns)) - } - l = len(m.ExecID) - if l > 0 { - n += 1 + l + sovHcsshim(uint64(l)) - } - return n -} - -func sovHcsshim(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n -} -func sozHcsshim(x uint64) (n int) { - return sovHcsshim(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (this *CreateOptions) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&CreateOptions{`, - `TerminateDuration:` + strings.Replace(strings.Replace(this.TerminateDuration.String(), "Duration", "google_protobuf1.Duration", 1), `&`, ``, 1) + `,`, - `}`, - }, "") - return s -} -func (this *ProcessDetails) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&ProcessDetails{`, - `ImageName:` + fmt.Sprintf("%v", this.ImageName) + `,`, - `CreatedAt:` + strings.Replace(strings.Replace(this.CreatedAt.String(), "Timestamp", "google_protobuf2.Timestamp", 1), `&`, ``, 1) + `,`, - `KernelTime_100Ns:` + fmt.Sprintf("%v", this.KernelTime_100Ns) + `,`, - `MemoryCommitBytes:` + fmt.Sprintf("%v", this.MemoryCommitBytes) + `,`, - `MemoryWorkingSetPrivateBytes:` + fmt.Sprintf("%v", this.MemoryWorkingSetPrivateBytes) + `,`, - `MemoryWorkingSetSharedBytes:` + fmt.Sprintf("%v", this.MemoryWorkingSetSharedBytes) + `,`, - `ProcessID:` + fmt.Sprintf("%v", this.ProcessID) + `,`, - `UserTime_100Ns:` + fmt.Sprintf("%v", this.UserTime_100Ns) + `,`, - `ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`, - `}`, - }, "") - return s -} -func valueToStringHcsshim(v interface{}) string { - rv := reflect.ValueOf(v) - if rv.IsNil() { - return "nil" - } - pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("*%v", pv) -} -func (m *CreateOptions) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CreateOptions: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CreateOptions: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TerminateDuration", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthHcsshim - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := types.StdDurationUnmarshal(&m.TerminateDuration, dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipHcsshim(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthHcsshim - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ProcessDetails) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ProcessDetails: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ProcessDetails: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ImageName", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthHcsshim - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ImageName = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthHcsshim - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field KernelTime_100Ns", wireType) - } - m.KernelTime_100Ns = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.KernelTime_100Ns |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MemoryCommitBytes", wireType) - } - m.MemoryCommitBytes = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.MemoryCommitBytes |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MemoryWorkingSetPrivateBytes", wireType) - } - m.MemoryWorkingSetPrivateBytes = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.MemoryWorkingSetPrivateBytes |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MemoryWorkingSetSharedBytes", wireType) - } - m.MemoryWorkingSetSharedBytes = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.MemoryWorkingSetSharedBytes |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 7: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ProcessID", wireType) - } - m.ProcessID = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.ProcessID |= (uint32(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 8: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field UserTime_100Ns", wireType) - } - m.UserTime_100Ns = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.UserTime_100Ns |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - case 9: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowHcsshim - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthHcsshim - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ExecID = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipHcsshim(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthHcsshim - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func skipHcsshim(dAtA []byte) (n int, err error) { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowHcsshim - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - wireType := int(wire & 0x7) - switch wireType { - case 0: - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowHcsshim - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - iNdEx++ - if dAtA[iNdEx-1] < 0x80 { - break - } - } - return iNdEx, nil - case 1: - iNdEx += 8 - return iNdEx, nil - case 2: - var length int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowHcsshim - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - length |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - iNdEx += length - if length < 0 { - return 0, ErrInvalidLengthHcsshim - } - return iNdEx, nil - case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowHcsshim - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipHcsshim(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil - case 4: - return iNdEx, nil - case 5: - iNdEx += 4 - return iNdEx, nil - default: - return 0, fmt.Errorf("proto: illegal wireType %d", wireType) - } - } - panic("unreachable") -} - -var ( - ErrInvalidLengthHcsshim = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowHcsshim = fmt.Errorf("proto: integer overflow") -) - -func init() { - proto.RegisterFile("github.com/containerd/containerd/windows/hcsshimtypes/hcsshim.proto", fileDescriptorHcsshim) -} - -var fileDescriptorHcsshim = []byte{ - // 507 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0x41, 0x6f, 0xd3, 0x3c, - 0x18, 0xc7, 0x9b, 0x77, 0x7b, 0xbb, 0xc5, 0xa8, 0xc0, 0x0c, 0x87, 0x52, 0x20, 0xa9, 0xc6, 0x81, - 0x4a, 0xa0, 0xb4, 0x83, 0x23, 0x27, 0xd2, 0x82, 0xd4, 0xcb, 0x98, 0x32, 0x24, 0x10, 0x42, 0xb2, - 0xdc, 0xe4, 0x21, 0xb5, 0x56, 0xc7, 0x91, 0xed, 0xd2, 0xf5, 0xc6, 0x47, 0xe0, 0xc8, 0x47, 0xea, - 0x91, 0x23, 0x12, 0x52, 0x61, 0xf9, 0x24, 0xc8, 0x76, 0xba, 0x8d, 0xc1, 0x89, 0x9b, 0xed, 0xff, - 0xef, 0xf9, 0x3d, 0xf1, 0x63, 0x05, 0x0d, 0x73, 0xa6, 0xa7, 0xf3, 0x49, 0x94, 0x0a, 0xde, 0x4f, - 0x45, 0xa1, 0x29, 0x2b, 0x40, 0x66, 0x97, 0x97, 0x0b, 0x56, 0x64, 0x62, 0xa1, 0xfa, 0xd3, 0x54, - 0xa9, 0x29, 0xe3, 0x7a, 0x59, 0xc2, 0xf9, 0x26, 0x2a, 0xa5, 0xd0, 0x02, 0x77, 0x2e, 0xf0, 0xa8, - 0xc6, 0xa3, 0x9a, 0xe8, 0xdc, 0xce, 0x45, 0x2e, 0x2c, 0xd6, 0x37, 0x2b, 0x57, 0xd1, 0x09, 0x72, - 0x21, 0xf2, 0x19, 0xf4, 0xed, 0x6e, 0x32, 0xff, 0xd0, 0xcf, 0xe6, 0x92, 0x6a, 0x26, 0x8a, 0x3a, - 0x0f, 0xaf, 0xe6, 0x9a, 0x71, 0x50, 0x9a, 0xf2, 0xd2, 0x01, 0xfb, 0x29, 0x6a, 0x0d, 0x25, 0x50, - 0x0d, 0xaf, 0x4a, 0x53, 0xa6, 0x70, 0x82, 0xb0, 0x06, 0xc9, 0x59, 0x41, 0x35, 0x90, 0x8d, 0xad, - 0xed, 0x75, 0xbd, 0xde, 0xb5, 0x27, 0x77, 0x22, 0xa7, 0x8b, 0x36, 0xba, 0x68, 0x54, 0x03, 0xf1, - 0xee, 0x6a, 0x1d, 0x36, 0xbe, 0xfc, 0x08, 0xbd, 0x64, 0xef, 0xbc, 0x7c, 0x13, 0xee, 0x7f, 0xdf, - 0x42, 0xd7, 0x8f, 0xa4, 0x48, 0x41, 0xa9, 0x11, 0x68, 0xca, 0x66, 0x0a, 0xdf, 0x47, 0x88, 0x71, - 0x9a, 0x03, 0x29, 0x28, 0x07, 0xab, 0xf7, 0x13, 0xdf, 0x9e, 0x1c, 0x52, 0x0e, 0x78, 0x88, 0x50, - 0x6a, 0x3f, 0x2b, 0x23, 0x54, 0xb7, 0xff, 0xb3, 0xdd, 0x3b, 0x7f, 0x74, 0x7f, 0xbd, 0xb9, 0x8c, - 0x6b, 0xff, 0xd9, 0xb4, 0xf7, 0xeb, 0xba, 0xe7, 0x1a, 0x3f, 0x42, 0xf8, 0x04, 0x64, 0x01, 0x33, - 0x62, 0x6e, 0x4d, 0x0e, 0x06, 0x03, 0x52, 0xa8, 0xf6, 0x56, 0xd7, 0xeb, 0x6d, 0x27, 0x37, 0x5c, - 0x62, 0x0c, 0x07, 0x83, 0xc1, 0xa1, 0xc2, 0x11, 0xba, 0xc5, 0x81, 0x0b, 0xb9, 0x24, 0xa9, 0xe0, - 0x9c, 0x69, 0x32, 0x59, 0x6a, 0x50, 0xed, 0x6d, 0x4b, 0xef, 0xb9, 0x68, 0x68, 0x93, 0xd8, 0x04, - 0xf8, 0x25, 0xea, 0xd6, 0xfc, 0x42, 0xc8, 0x13, 0x56, 0xe4, 0x44, 0x81, 0x26, 0xa5, 0x64, 0x1f, - 0xcd, 0xe0, 0x5c, 0xf1, 0xff, 0xb6, 0xf8, 0x9e, 0xe3, 0xde, 0x38, 0xec, 0x18, 0xf4, 0x91, 0x83, - 0x9c, 0x67, 0x84, 0xc2, 0xbf, 0x78, 0xd4, 0x94, 0x4a, 0xc8, 0x6a, 0x4d, 0xd3, 0x6a, 0xee, 0x5e, - 0xd5, 0x1c, 0x5b, 0xc6, 0x59, 0x1e, 0x23, 0x54, 0xba, 0x01, 0x13, 0x96, 0xb5, 0x77, 0xba, 0x5e, - 0xaf, 0x15, 0xb7, 0xaa, 0x75, 0xe8, 0xd7, 0x63, 0x1f, 0x8f, 0x12, 0xbf, 0x06, 0xc6, 0x19, 0x7e, - 0x88, 0x6e, 0xce, 0x15, 0xc8, 0xdf, 0xc6, 0xb2, 0x6b, 0x9b, 0xb4, 0xcc, 0xf9, 0xc5, 0x50, 0x1e, - 0xa0, 0x1d, 0x38, 0x85, 0xd4, 0x38, 0x7d, 0xf3, 0x44, 0x31, 0xaa, 0xd6, 0x61, 0xf3, 0xc5, 0x29, - 0xa4, 0xe3, 0x51, 0xd2, 0x34, 0xd1, 0x38, 0x8b, 0xdf, 0xaf, 0xce, 0x82, 0xc6, 0xb7, 0xb3, 0xa0, - 0xf1, 0xa9, 0x0a, 0xbc, 0x55, 0x15, 0x78, 0x5f, 0xab, 0xc0, 0xfb, 0x59, 0x05, 0xde, 0xbb, 0xf8, - 0x9f, 0x7e, 0x8a, 0x67, 0x97, 0x37, 0x6f, 0x1b, 0x93, 0xa6, 0x7d, 0xef, 0xa7, 0xbf, 0x02, 0x00, - 0x00, 0xff, 0xff, 0x1e, 0xd7, 0x2f, 0xa8, 0x63, 0x03, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/containerd/windows/hcsshimtypes/hcsshim.proto b/vendor/github.com/containerd/containerd/windows/hcsshimtypes/hcsshim.proto deleted file mode 100644 index 5934fca7c209b..0000000000000 --- a/vendor/github.com/containerd/containerd/windows/hcsshimtypes/hcsshim.proto +++ /dev/null @@ -1,27 +0,0 @@ -syntax = "proto3"; - -package containerd.windows.hcsshim; - -import weak "gogoproto/gogo.proto"; -import "google/protobuf/duration.proto"; -import "google/protobuf/timestamp.proto"; - -option go_package = "github.com/containerd/containerd/windows/hcsshimtypes;hcsshimtypes"; - -message CreateOptions { - google.protobuf.Duration terminate_duration = 1 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false]; -} - -// ProcessDetails contains additional information about a process -// ProcessDetails is made of the same fields as found in hcsshim.ProcessListItem -message ProcessDetails { - string image_name = 1; - google.protobuf.Timestamp created_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false]; - uint64 kernel_time_100_ns = 3; - uint64 memory_commit_bytes = 4; - uint64 memory_working_set_private_bytes = 5; - uint64 memory_working_set_shared_bytes = 6; - uint32 process_id = 7; - uint64 user_time_100_ns = 8; - string exec_id = 9; -} diff --git a/vendor/github.com/containerd/continuity/LICENSE b/vendor/github.com/containerd/continuity/LICENSE index 8f71f43fee3f7..584149b6ee28c 100644 --- a/vendor/github.com/containerd/continuity/LICENSE +++ b/vendor/github.com/containerd/continuity/LICENSE @@ -1,6 +1,7 @@ + Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -175,28 +176,16 @@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} + Copyright The containerd Authors 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 + https://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. - diff --git a/vendor/github.com/containerd/continuity/README.md b/vendor/github.com/containerd/continuity/README.md index 0e91ce07b58aa..6fa50e159c708 100644 --- a/vendor/github.com/containerd/continuity/README.md +++ b/vendor/github.com/containerd/continuity/README.md @@ -63,6 +63,10 @@ $ stat -c %a Makefile $ ./bin/continuity verify . /tmp/a.pb ``` +## Platforms + +continuity primarily targets Linux. continuity may compile for and work on +other operating systems, but those platforms are not tested. ## Contribution Guide ### Building Proto Package @@ -72,3 +76,13 @@ If you change the proto file you will need to rebuild the generated Go with `go ```console $ go generate ./proto ``` + +## Project details + +continuity is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/continuity/context.go b/vendor/github.com/containerd/continuity/context.go deleted file mode 100644 index 75c98594ac68f..0000000000000 --- a/vendor/github.com/containerd/continuity/context.go +++ /dev/null @@ -1,673 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import ( - "bytes" - "fmt" - "io" - "log" - "os" - "path/filepath" - "strings" - - "github.com/containerd/continuity/devices" - driverpkg "github.com/containerd/continuity/driver" - "github.com/containerd/continuity/pathdriver" - - "github.com/opencontainers/go-digest" -) - -var ( - // ErrNotFound represents the resource not found - ErrNotFound = fmt.Errorf("not found") - // ErrNotSupported represents the resource not supported - ErrNotSupported = fmt.Errorf("not supported") -) - -// Context represents a file system context for accessing resources. The -// responsibility of the context is to convert system specific resources to -// generic Resource objects. Most of this is safe path manipulation, as well -// as extraction of resource details. -type Context interface { - Apply(Resource) error - Verify(Resource) error - Resource(string, os.FileInfo) (Resource, error) - Walk(filepath.WalkFunc) error -} - -// SymlinkPath is intended to give the symlink target value -// in a root context. Target and linkname are absolute paths -// not under the given root. -type SymlinkPath func(root, linkname, target string) (string, error) - -// ContextOptions represents options to create a new context. -type ContextOptions struct { - Digester Digester - Driver driverpkg.Driver - PathDriver pathdriver.PathDriver - Provider ContentProvider -} - -// context represents a file system context for accessing resources. -// Generally, all path qualified access and system considerations should land -// here. -type context struct { - driver driverpkg.Driver - pathDriver pathdriver.PathDriver - root string - digester Digester - provider ContentProvider -} - -// NewContext returns a Context associated with root. The default driver will -// be used, as returned by NewDriver. -func NewContext(root string) (Context, error) { - return NewContextWithOptions(root, ContextOptions{}) -} - -// NewContextWithOptions returns a Context associate with the root. -func NewContextWithOptions(root string, options ContextOptions) (Context, error) { - // normalize to absolute path - pathDriver := options.PathDriver - if pathDriver == nil { - pathDriver = pathdriver.LocalPathDriver - } - - root = pathDriver.FromSlash(root) - root, err := pathDriver.Abs(pathDriver.Clean(root)) - if err != nil { - return nil, err - } - - driver := options.Driver - if driver == nil { - driver, err = driverpkg.NewSystemDriver() - if err != nil { - return nil, err - } - } - - digester := options.Digester - if digester == nil { - digester = simpleDigester{digest.Canonical} - } - - // Check the root directory. Need to be a little careful here. We are - // allowing a link for now, but this may have odd behavior when - // canonicalizing paths. As long as all files are opened through the link - // path, this should be okay. - fi, err := driver.Stat(root) - if err != nil { - return nil, err - } - - if !fi.IsDir() { - return nil, &os.PathError{Op: "NewContext", Path: root, Err: os.ErrInvalid} - } - - return &context{ - root: root, - driver: driver, - pathDriver: pathDriver, - digester: digester, - provider: options.Provider, - }, nil -} - -// Resource returns the resource as path p, populating the entry with info -// from fi. The path p should be the path of the resource in the context, -// typically obtained through Walk or from the value of Resource.Path(). If fi -// is nil, it will be resolved. -func (c *context) Resource(p string, fi os.FileInfo) (Resource, error) { - fp, err := c.fullpath(p) - if err != nil { - return nil, err - } - - if fi == nil { - fi, err = c.driver.Lstat(fp) - if err != nil { - return nil, err - } - } - - base, err := newBaseResource(p, fi) - if err != nil { - return nil, err - } - - base.xattrs, err = c.resolveXAttrs(fp, fi, base) - if err == ErrNotSupported { - log.Printf("resolving xattrs on %s not supported", fp) - } else if err != nil { - return nil, err - } - - // TODO(stevvooe): Handle windows alternate data streams. - - if fi.Mode().IsRegular() { - dgst, err := c.digest(p) - if err != nil { - return nil, err - } - - return newRegularFile(*base, base.paths, fi.Size(), dgst) - } - - if fi.Mode().IsDir() { - return newDirectory(*base) - } - - if fi.Mode()&os.ModeSymlink != 0 { - // We handle relative links vs absolute links by including a - // beginning slash for absolute links. Effectively, the bundle's - // root is treated as the absolute link anchor. - target, err := c.driver.Readlink(fp) - if err != nil { - return nil, err - } - - return newSymLink(*base, target) - } - - if fi.Mode()&os.ModeNamedPipe != 0 { - return newNamedPipe(*base, base.paths) - } - - if fi.Mode()&os.ModeDevice != 0 { - deviceDriver, ok := c.driver.(driverpkg.DeviceInfoDriver) - if !ok { - log.Printf("device extraction not supported %s", fp) - return nil, ErrNotSupported - } - - // character and block devices merely need to recover the - // major/minor device number. - major, minor, err := deviceDriver.DeviceInfo(fi) - if err != nil { - return nil, err - } - - return newDevice(*base, base.paths, major, minor) - } - - log.Printf("%q (%v) is not supported", fp, fi.Mode()) - return nil, ErrNotFound -} - -func (c *context) verifyMetadata(resource, target Resource) error { - if target.Mode() != resource.Mode() { - return fmt.Errorf("resource %q has incorrect mode: %v != %v", target.Path(), target.Mode(), resource.Mode()) - } - - if target.UID() != resource.UID() { - return fmt.Errorf("unexpected uid for %q: %v != %v", target.Path(), target.UID(), resource.GID()) - } - - if target.GID() != resource.GID() { - return fmt.Errorf("unexpected gid for %q: %v != %v", target.Path(), target.GID(), target.GID()) - } - - if xattrer, ok := resource.(XAttrer); ok { - txattrer, tok := target.(XAttrer) - if !tok { - return fmt.Errorf("resource %q has xattrs but target does not support them", resource.Path()) - } - - // For xattrs, only ensure that we have those defined in the resource - // and their values match. We can ignore other xattrs. In other words, - // we only verify that target has the subset defined by resource. - txattrs := txattrer.XAttrs() - for attr, value := range xattrer.XAttrs() { - tvalue, ok := txattrs[attr] - if !ok { - return fmt.Errorf("resource %q target missing xattr %q", resource.Path(), attr) - } - - if !bytes.Equal(value, tvalue) { - return fmt.Errorf("xattr %q value differs for resource %q", attr, resource.Path()) - } - } - } - - switch r := resource.(type) { - case RegularFile: - // TODO(stevvooe): Another reason to use a record-based approach. We - // have to do another type switch to get this to work. This could be - // fixed with an Equal function, but let's study this a little more to - // be sure. - t, ok := target.(RegularFile) - if !ok { - return fmt.Errorf("resource %q target not a regular file", r.Path()) - } - - if t.Size() != r.Size() { - return fmt.Errorf("resource %q target has incorrect size: %v != %v", t.Path(), t.Size(), r.Size()) - } - case Directory: - t, ok := target.(Directory) - if !ok { - return fmt.Errorf("resource %q target not a directory", t.Path()) - } - case SymLink: - t, ok := target.(SymLink) - if !ok { - return fmt.Errorf("resource %q target not a symlink", t.Path()) - } - - if t.Target() != r.Target() { - return fmt.Errorf("resource %q target has mismatched target: %q != %q", t.Path(), t.Target(), r.Target()) - } - case Device: - t, ok := target.(Device) - if !ok { - return fmt.Errorf("resource %q is not a device", t.Path()) - } - - if t.Major() != r.Major() || t.Minor() != r.Minor() { - return fmt.Errorf("resource %q has mismatched major/minor numbers: %d,%d != %d,%d", t.Path(), t.Major(), t.Minor(), r.Major(), r.Minor()) - } - case NamedPipe: - t, ok := target.(NamedPipe) - if !ok { - return fmt.Errorf("resource %q is not a named pipe", t.Path()) - } - default: - return fmt.Errorf("cannot verify resource: %v", resource) - } - - return nil -} - -// Verify the resource in the context. An error will be returned a discrepancy -// is found. -func (c *context) Verify(resource Resource) error { - fp, err := c.fullpath(resource.Path()) - if err != nil { - return err - } - - fi, err := c.driver.Lstat(fp) - if err != nil { - return err - } - - target, err := c.Resource(resource.Path(), fi) - if err != nil { - return err - } - - if target.Path() != resource.Path() { - return fmt.Errorf("resource paths do not match: %q != %q", target.Path(), resource.Path()) - } - - if err := c.verifyMetadata(resource, target); err != nil { - return err - } - - if h, isHardlinkable := resource.(Hardlinkable); isHardlinkable { - hardlinkKey, err := newHardlinkKey(fi) - if err == errNotAHardLink { - if len(h.Paths()) > 1 { - return fmt.Errorf("%q is not a hardlink to %q", h.Paths()[1], resource.Path()) - } - } else if err != nil { - return err - } - - for _, path := range h.Paths()[1:] { - fpLink, err := c.fullpath(path) - if err != nil { - return err - } - - fiLink, err := c.driver.Lstat(fpLink) - if err != nil { - return err - } - - targetLink, err := c.Resource(path, fiLink) - if err != nil { - return err - } - - hardlinkKeyLink, err := newHardlinkKey(fiLink) - if err != nil { - return err - } - - if hardlinkKeyLink != hardlinkKey { - return fmt.Errorf("%q is not a hardlink to %q", path, resource.Path()) - } - - if err := c.verifyMetadata(resource, targetLink); err != nil { - return err - } - } - } - - switch r := resource.(type) { - case RegularFile: - t, ok := target.(RegularFile) - if !ok { - return fmt.Errorf("resource %q target not a regular file", r.Path()) - } - - // TODO(stevvooe): This may need to get a little more sophisticated - // for digest comparison. We may want to actually calculate the - // provided digests, rather than the implementations having an - // overlap. - if !digestsMatch(t.Digests(), r.Digests()) { - return fmt.Errorf("digests for resource %q do not match: %v != %v", t.Path(), t.Digests(), r.Digests()) - } - } - - return nil -} - -func (c *context) checkoutFile(fp string, rf RegularFile) error { - if c.provider == nil { - return fmt.Errorf("no file provider") - } - var ( - r io.ReadCloser - err error - ) - for _, dgst := range rf.Digests() { - r, err = c.provider.Reader(dgst) - if err == nil { - break - } - } - if err != nil { - return fmt.Errorf("file content could not be provided: %v", err) - } - defer r.Close() - - return atomicWriteFile(fp, r, rf.Size(), rf.Mode()) -} - -// Apply the resource to the contexts. An error will be returned if the -// operation fails. Depending on the resource type, the resource may be -// created. For resource that cannot be resolved, an error will be returned. -func (c *context) Apply(resource Resource) error { - fp, err := c.fullpath(resource.Path()) - if err != nil { - return err - } - - if !strings.HasPrefix(fp, c.root) { - return fmt.Errorf("resource %v escapes root", resource) - } - - var chmod = true - fi, err := c.driver.Lstat(fp) - if err != nil { - if !os.IsNotExist(err) { - return err - } - } - - switch r := resource.(type) { - case RegularFile: - if fi == nil { - if err := c.checkoutFile(fp, r); err != nil { - return fmt.Errorf("error checking out file %q: %v", resource.Path(), err) - } - chmod = false - } else { - if !fi.Mode().IsRegular() { - return fmt.Errorf("file %q should be a regular file, but is not", resource.Path()) - } - if fi.Size() != r.Size() { - if err := c.checkoutFile(fp, r); err != nil { - return fmt.Errorf("error checking out file %q: %v", resource.Path(), err) - } - } else { - for _, dgst := range r.Digests() { - f, err := os.Open(fp) - if err != nil { - return fmt.Errorf("failure opening file for read %q: %v", resource.Path(), err) - } - compared, err := dgst.Algorithm().FromReader(f) - if err == nil && dgst != compared { - if err := c.checkoutFile(fp, r); err != nil { - return fmt.Errorf("error checking out file %q: %v", resource.Path(), err) - } - break - } - if err1 := f.Close(); err == nil { - err = err1 - } - if err != nil { - return fmt.Errorf("error checking digest for %q: %v", resource.Path(), err) - } - } - } - } - case Directory: - if fi == nil { - if err := c.driver.Mkdir(fp, resource.Mode()); err != nil { - return err - } - } else if !fi.Mode().IsDir() { - return fmt.Errorf("%q should be a directory, but is not", resource.Path()) - } - - case SymLink: - var target string // only possibly set if target resource is a symlink - - if fi != nil { - if fi.Mode()&os.ModeSymlink != 0 { - target, err = c.driver.Readlink(fp) - if err != nil { - return err - } - } - } - - if target != r.Target() { - if fi != nil { - if err := c.driver.Remove(fp); err != nil { // RemoveAll in case of directory? - return err - } - } - - if err := c.driver.Symlink(r.Target(), fp); err != nil { - return err - } - } - - case Device: - if fi == nil { - if err := c.driver.Mknod(fp, resource.Mode(), int(r.Major()), int(r.Minor())); err != nil { - return err - } - } else if (fi.Mode() & os.ModeDevice) == 0 { - return fmt.Errorf("%q should be a device, but is not", resource.Path()) - } else { - major, minor, err := devices.DeviceInfo(fi) - if err != nil { - return err - } - if major != r.Major() || minor != r.Minor() { - if err := c.driver.Remove(fp); err != nil { - return err - } - - if err := c.driver.Mknod(fp, resource.Mode(), int(r.Major()), int(r.Minor())); err != nil { - return err - } - } - } - - case NamedPipe: - if fi == nil { - if err := c.driver.Mkfifo(fp, resource.Mode()); err != nil { - return err - } - } else if (fi.Mode() & os.ModeNamedPipe) == 0 { - return fmt.Errorf("%q should be a named pipe, but is not", resource.Path()) - } - } - - if h, isHardlinkable := resource.(Hardlinkable); isHardlinkable { - for _, path := range h.Paths() { - if path == resource.Path() { - continue - } - - lp, err := c.fullpath(path) - if err != nil { - return err - } - - if _, fi := c.driver.Lstat(lp); fi == nil { - c.driver.Remove(lp) - } - if err := c.driver.Link(fp, lp); err != nil { - return err - } - } - } - - // Update filemode if file was not created - if chmod { - if err := c.driver.Lchmod(fp, resource.Mode()); err != nil { - return err - } - } - - if err := c.driver.Lchown(fp, resource.UID(), resource.GID()); err != nil { - return err - } - - if xattrer, ok := resource.(XAttrer); ok { - // For xattrs, only ensure that we have those defined in the resource - // and their values are set. We can ignore other xattrs. In other words, - // we only set xattres defined by resource but never remove. - - if _, ok := resource.(SymLink); ok { - lxattrDriver, ok := c.driver.(driverpkg.LXAttrDriver) - if !ok { - return fmt.Errorf("unsupported symlink xattr for resource %q", resource.Path()) - } - if err := lxattrDriver.LSetxattr(fp, xattrer.XAttrs()); err != nil { - return err - } - } else { - xattrDriver, ok := c.driver.(driverpkg.XAttrDriver) - if !ok { - return fmt.Errorf("unsupported xattr for resource %q", resource.Path()) - } - if err := xattrDriver.Setxattr(fp, xattrer.XAttrs()); err != nil { - return err - } - } - } - - return nil -} - -// Walk provides a convenience function to call filepath.Walk correctly for -// the context. Otherwise identical to filepath.Walk, the path argument is -// corrected to be contained within the context. -func (c *context) Walk(fn filepath.WalkFunc) error { - root := c.root - fi, err := c.driver.Lstat(c.root) - if err == nil && fi.Mode()&os.ModeSymlink != 0 { - root, err = c.driver.Readlink(c.root) - if err != nil { - return err - } - } - return c.pathDriver.Walk(root, func(p string, fi os.FileInfo, err error) error { - contained, err := c.containWithRoot(p, root) - return fn(contained, fi, err) - }) -} - -// fullpath returns the system path for the resource, joined with the context -// root. The path p must be a part of the context. -func (c *context) fullpath(p string) (string, error) { - p = c.pathDriver.Join(c.root, p) - if !strings.HasPrefix(p, c.root) { - return "", fmt.Errorf("invalid context path") - } - - return p, nil -} - -// contain cleans and santizes the filesystem path p to be an absolute path, -// effectively relative to the context root. -func (c *context) contain(p string) (string, error) { - return c.containWithRoot(p, c.root) -} - -// containWithRoot cleans and santizes the filesystem path p to be an absolute path, -// effectively relative to the passed root. Extra care should be used when calling this -// instead of contain. This is needed for Walk, as if context root is a symlink, -// it must be evaluated prior to the Walk -func (c *context) containWithRoot(p string, root string) (string, error) { - sanitized, err := c.pathDriver.Rel(root, p) - if err != nil { - return "", err - } - - // ZOMBIES(stevvooe): In certain cases, we may want to remap these to a - // "containment error", so the caller can decide what to do. - return c.pathDriver.Join("/", c.pathDriver.Clean(sanitized)), nil -} - -// digest returns the digest of the file at path p, relative to the root. -func (c *context) digest(p string) (digest.Digest, error) { - f, err := c.driver.Open(c.pathDriver.Join(c.root, p)) - if err != nil { - return "", err - } - defer f.Close() - - return c.digester.Digest(f) -} - -// resolveXAttrs attempts to resolve the extended attributes for the resource -// at the path fp, which is the full path to the resource. If the resource -// cannot have xattrs, nil will be returned. -func (c *context) resolveXAttrs(fp string, fi os.FileInfo, base *resource) (map[string][]byte, error) { - if fi.Mode().IsRegular() || fi.Mode().IsDir() { - xattrDriver, ok := c.driver.(driverpkg.XAttrDriver) - if !ok { - log.Println("xattr extraction not supported") - return nil, ErrNotSupported - } - - return xattrDriver.Getxattr(fp) - } - - if fi.Mode()&os.ModeSymlink != 0 { - lxattrDriver, ok := c.driver.(driverpkg.LXAttrDriver) - if !ok { - log.Println("xattr extraction for symlinks not supported") - return nil, ErrNotSupported - } - - return lxattrDriver.LGetxattr(fp) - } - - return nil, nil -} diff --git a/vendor/github.com/containerd/continuity/devices/devices_unix.go b/vendor/github.com/containerd/continuity/devices/devices_unix.go index 520a5a6f3bfca..225a04b7fb412 100644 --- a/vendor/github.com/containerd/continuity/devices/devices_unix.go +++ b/vendor/github.com/containerd/continuity/devices/devices_unix.go @@ -32,6 +32,7 @@ func DeviceInfo(fi os.FileInfo) (uint64, uint64, error) { return 0, 0, fmt.Errorf("cannot extract device from os.FileInfo") } + //nolint:unconvert dev := uint64(sys.Rdev) return uint64(unix.Major(dev)), uint64(unix.Minor(dev)), nil } @@ -55,7 +56,7 @@ func Mknod(p string, mode os.FileMode, maj, min int) error { m |= unix.S_IFIFO } - return unix.Mknod(p, m, int(dev)) + return mknod(p, m, dev) } // syscallMode returns the syscall-specific mode bits from Go's portable mode bits. diff --git a/vendor/github.com/containerd/continuity/devices/mknod_freebsd.go b/vendor/github.com/containerd/continuity/devices/mknod_freebsd.go new file mode 100644 index 0000000000000..33d18ec8396b0 --- /dev/null +++ b/vendor/github.com/containerd/continuity/devices/mknod_freebsd.go @@ -0,0 +1,25 @@ +// +build freebsd + +/* + Copyright The containerd Authors. + + 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. +*/ + +package devices + +import "golang.org/x/sys/unix" + +func mknod(path string, mode uint32, dev uint64) (err error) { + return unix.Mknod(path, mode, dev) +} diff --git a/vendor/github.com/containerd/continuity/devices/mknod_unix.go b/vendor/github.com/containerd/continuity/devices/mknod_unix.go new file mode 100644 index 0000000000000..d9e7a7a2ba6ab --- /dev/null +++ b/vendor/github.com/containerd/continuity/devices/mknod_unix.go @@ -0,0 +1,25 @@ +// +build linux darwin solaris + +/* + Copyright The containerd Authors. + + 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. +*/ + +package devices + +import "golang.org/x/sys/unix" + +func mknod(path string, mode uint32, dev uint64) (err error) { + return unix.Mknod(path, mode, int(dev)) +} diff --git a/vendor/github.com/containerd/continuity/digests.go b/vendor/github.com/containerd/continuity/digests.go deleted file mode 100644 index bf92275dbd366..0000000000000 --- a/vendor/github.com/containerd/continuity/digests.go +++ /dev/null @@ -1,104 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import ( - "fmt" - "io" - "sort" - - "github.com/opencontainers/go-digest" -) - -// Digester produces a digest for a given read stream -type Digester interface { - Digest(io.Reader) (digest.Digest, error) -} - -// ContentProvider produces a read stream for a given digest -type ContentProvider interface { - Reader(digest.Digest) (io.ReadCloser, error) -} - -type simpleDigester struct { - algorithm digest.Algorithm -} - -func (sd simpleDigester) Digest(r io.Reader) (digest.Digest, error) { - digester := sd.algorithm.Digester() - - if _, err := io.Copy(digester.Hash(), r); err != nil { - return "", err - } - - return digester.Digest(), nil -} - -// uniqifyDigests sorts and uniqifies the provided digest, ensuring that the -// digests are not repeated and no two digests with the same algorithm have -// different values. Because a stable sort is used, this has the effect of -// "zipping" digest collections from multiple resources. -func uniqifyDigests(digests ...digest.Digest) ([]digest.Digest, error) { - sort.Stable(digestSlice(digests)) // stable sort is important for the behavior here. - seen := map[digest.Digest]struct{}{} - algs := map[digest.Algorithm][]digest.Digest{} // detect different digests. - - var out []digest.Digest - // uniqify the digests - for _, d := range digests { - if _, ok := seen[d]; ok { - continue - } - - seen[d] = struct{}{} - algs[d.Algorithm()] = append(algs[d.Algorithm()], d) - - if len(algs[d.Algorithm()]) > 1 { - return nil, fmt.Errorf("conflicting digests for %v found", d.Algorithm()) - } - - out = append(out, d) - } - - return out, nil -} - -// digestsMatch compares the two sets of digests to see if they match. -func digestsMatch(as, bs []digest.Digest) bool { - all := append(as, bs...) - - uniqified, err := uniqifyDigests(all...) - if err != nil { - // the only error uniqifyDigests returns is when the digests disagree. - return false - } - - disjoint := len(as) + len(bs) - if len(uniqified) == disjoint { - // if these two sets have the same cardinality, we know both sides - // didn't share any digests. - return false - } - - return true -} - -type digestSlice []digest.Digest - -func (p digestSlice) Len() int { return len(p) } -func (p digestSlice) Less(i, j int) bool { return p[i] < p[j] } -func (p digestSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/vendor/github.com/containerd/continuity/driver/driver.go b/vendor/github.com/containerd/continuity/driver/driver.go index 327e96af1596d..e5d9d0f87a45e 100644 --- a/vendor/github.com/containerd/continuity/driver/driver.go +++ b/vendor/github.com/containerd/continuity/driver/driver.go @@ -138,6 +138,10 @@ func (d *driver) Lstat(p string) (os.FileInfo, error) { return os.Lstat(p) } +func (d *driver) Readlink(p string) (string, error) { + return os.Readlink(p) +} + func (d *driver) Mkdir(p string, mode os.FileMode) error { return os.Mkdir(p, mode) } diff --git a/vendor/github.com/containerd/continuity/driver/driver_unix.go b/vendor/github.com/containerd/continuity/driver/driver_unix.go index 6cb5d10fb904c..3e58d10af27f9 100644 --- a/vendor/github.com/containerd/continuity/driver/driver_unix.go +++ b/vendor/github.com/containerd/continuity/driver/driver_unix.go @@ -131,8 +131,3 @@ func (d *driver) LSetxattr(path string, attrMap map[string][]byte) error { func (d *driver) DeviceInfo(fi os.FileInfo) (maj uint64, min uint64, err error) { return devices.DeviceInfo(fi) } - -// Readlink was forked on Windows to fix a Golang bug, use the "os" package here -func (d *driver) Readlink(p string) (string, error) { - return os.Readlink(p) -} diff --git a/vendor/github.com/containerd/continuity/driver/driver_windows.go b/vendor/github.com/containerd/continuity/driver/driver_windows.go index f1dcea32afb9a..9baea3ba642d5 100644 --- a/vendor/github.com/containerd/continuity/driver/driver_windows.go +++ b/vendor/github.com/containerd/continuity/driver/driver_windows.go @@ -1,3 +1,5 @@ +// +build go1.13 + /* Copyright The containerd Authors. @@ -14,12 +16,14 @@ limitations under the License. */ +// Go 1.13 is the minimally supported version for Windows. +// Earlier golang releases have bug in os.Readlink +// (see https://github.com/golang/go/issues/30463). + package driver import ( "os" - - "github.com/containerd/continuity/sysx" ) func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error { @@ -35,9 +39,3 @@ func (d *driver) Lchmod(path string, mode os.FileMode) (err error) { // TODO: Use Window's equivalent return os.Chmod(path, mode) } - -// Readlink is forked in order to support Volume paths which are used -// in container layers. -func (d *driver) Readlink(p string) (string, error) { - return sysx.Readlink(p) -} diff --git a/vendor/github.com/containerd/continuity/fs/copy.go b/vendor/github.com/containerd/continuity/fs/copy.go index 42df6a9a541e1..2ee77d1ab2a85 100644 --- a/vendor/github.com/containerd/continuity/fs/copy.go +++ b/vendor/github.com/containerd/continuity/fs/copy.go @@ -32,20 +32,70 @@ var bufferPool = &sync.Pool{ }, } +// XAttrErrorHandlers transform a non-nil xattr error. +// Return nil to ignore an error. +// xattrKey can be empty for listxattr operation. +type XAttrErrorHandler func(dst, src, xattrKey string, err error) error + +type copyDirOpts struct { + xeh XAttrErrorHandler + // xex contains a set of xattrs to exclude when copying + xex map[string]struct{} +} + +type CopyDirOpt func(*copyDirOpts) error + +// WithXAttrErrorHandler allows specifying XAttrErrorHandler +// If nil XAttrErrorHandler is specified (default), CopyDir stops +// on a non-nil xattr error. +func WithXAttrErrorHandler(xeh XAttrErrorHandler) CopyDirOpt { + return func(o *copyDirOpts) error { + o.xeh = xeh + return nil + } +} + +// WithAllowXAttrErrors allows ignoring xattr errors. +func WithAllowXAttrErrors() CopyDirOpt { + xeh := func(dst, src, xattrKey string, err error) error { + return nil + } + return WithXAttrErrorHandler(xeh) +} + +// WithXAttrExclude allows for exclusion of specified xattr during CopyDir operation. +func WithXAttrExclude(keys ...string) CopyDirOpt { + return func(o *copyDirOpts) error { + if o.xex == nil { + o.xex = make(map[string]struct{}, len(keys)) + } + for _, key := range keys { + o.xex[key] = struct{}{} + } + return nil + } +} + // CopyDir copies the directory from src to dst. // Most efficient copy of files is attempted. -func CopyDir(dst, src string) error { +func CopyDir(dst, src string, opts ...CopyDirOpt) error { + var o copyDirOpts + for _, opt := range opts { + if err := opt(&o); err != nil { + return err + } + } inodes := map[uint64]string{} - return copyDirectory(dst, src, inodes) + return copyDirectory(dst, src, inodes, &o) } -func copyDirectory(dst, src string, inodes map[uint64]string) error { +func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) error { stat, err := os.Stat(src) if err != nil { return errors.Wrapf(err, "failed to stat %s", src) } if !stat.IsDir() { - return errors.Errorf("source is not directory") + return errors.Errorf("source %s is not directory", src) } if st, err := os.Stat(dst); err != nil { @@ -69,13 +119,17 @@ func copyDirectory(dst, src string, inodes map[uint64]string) error { return errors.Wrapf(err, "failed to copy file info for %s", dst) } + if err := copyXAttrs(dst, src, o.xex, o.xeh); err != nil { + return errors.Wrap(err, "failed to copy xattrs") + } + for _, fi := range fis { source := filepath.Join(src, fi.Name()) target := filepath.Join(dst, fi.Name()) switch { case fi.IsDir(): - if err := copyDirectory(target, source, inodes); err != nil { + if err := copyDirectory(target, source, inodes, o); err != nil { return err } continue @@ -111,7 +165,7 @@ func copyDirectory(dst, src string, inodes map[uint64]string) error { return errors.Wrap(err, "failed to copy file info") } - if err := copyXAttrs(target, source); err != nil { + if err := copyXAttrs(target, source, o.xex, o.xeh); err != nil { return errors.Wrap(err, "failed to copy xattrs") } } diff --git a/vendor/github.com/containerd/continuity/fs/copy_darwinopenbsdsolaris.go b/vendor/github.com/containerd/continuity/fs/copy_darwinopenbsdsolaris.go new file mode 100644 index 0000000000000..92ccacf9af364 --- /dev/null +++ b/vendor/github.com/containerd/continuity/fs/copy_darwinopenbsdsolaris.go @@ -0,0 +1,40 @@ +// +build darwin openbsd solaris + +/* + Copyright The containerd Authors. + + 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. +*/ + +package fs + +import ( + "os" + "syscall" + + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +func copyDevice(dst string, fi os.FileInfo) error { + st, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return errors.New("unsupported stat type") + } + return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev)) +} + +func utimesNano(name string, atime, mtime syscall.Timespec) error { + timespec := []syscall.Timespec{atime, mtime} + return syscall.UtimesNano(name, timespec) +} diff --git a/vendor/github.com/containerd/continuity/fs/copy_freebsd.go b/vendor/github.com/containerd/continuity/fs/copy_freebsd.go new file mode 100644 index 0000000000000..4b116c95e43d5 --- /dev/null +++ b/vendor/github.com/containerd/continuity/fs/copy_freebsd.go @@ -0,0 +1,42 @@ +// +build freebsd + +/* + Copyright The containerd Authors. + + 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. +*/ + +package fs + +import ( + "os" + "syscall" + + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +func copyDevice(dst string, fi os.FileInfo) error { + st, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return errors.New("unsupported stat type") + } + return unix.Mknod(dst, uint32(fi.Mode()), st.Rdev) +} + +func utimesNano(name string, atime, mtime syscall.Timespec) error { + at := unix.NsecToTimespec(atime.Nano()) + mt := unix.NsecToTimespec(mtime.Nano()) + utimes := [2]unix.Timespec{at, mt} + return unix.UtimesNanoAt(unix.AT_FDCWD, name, utimes[0:], unix.AT_SYMLINK_NOFOLLOW) +} diff --git a/vendor/github.com/containerd/continuity/fs/copy_linux.go b/vendor/github.com/containerd/continuity/fs/copy_linux.go index e041b5661f032..85beaee54a4a4 100644 --- a/vendor/github.com/containerd/continuity/fs/copy_linux.go +++ b/vendor/github.com/containerd/continuity/fs/copy_linux.go @@ -51,7 +51,10 @@ func copyFileInfo(fi os.FileInfo, name string) error { } } - timespec := []unix.Timespec{unix.Timespec(StatAtime(st)), unix.Timespec(StatMtime(st))} + timespec := []unix.Timespec{ + unix.NsecToTimespec(syscall.TimespecToNsec(StatAtime(st))), + unix.NsecToTimespec(syscall.TimespecToNsec(StatMtime(st))), + } if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil { return errors.Wrapf(err, "failed to utime %s", name) } @@ -59,6 +62,8 @@ func copyFileInfo(fi os.FileInfo, name string) error { return nil } +const maxSSizeT = int64(^uint(0) >> 1) + func copyFileContent(dst, src *os.File) error { st, err := src.Stat() if err != nil { @@ -71,7 +76,16 @@ func copyFileContent(dst, src *os.File) error { dstFd := int(dst.Fd()) for size > 0 { - n, err := unix.CopyFileRange(srcFd, nil, dstFd, nil, int(size), 0) + // Ensure that we are never trying to copy more than SSIZE_MAX at a + // time and at the same time avoids overflows when the file is larger + // than 4GB on 32-bit systems. + var copySize int + if size > maxSSizeT { + copySize = int(maxSSizeT) + } else { + copySize = int(size) + } + n, err := unix.CopyFileRange(srcFd, nil, dstFd, nil, copySize, 0) if err != nil { if (err != unix.ENOSYS && err != unix.EXDEV) || !first { return errors.Wrap(err, "copy file range failed") @@ -90,18 +104,37 @@ func copyFileContent(dst, src *os.File) error { return nil } -func copyXAttrs(dst, src string) error { +func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error { xattrKeys, err := sysx.LListxattr(src) if err != nil { - return errors.Wrapf(err, "failed to list xattrs on %s", src) + e := errors.Wrapf(err, "failed to list xattrs on %s", src) + if errorHandler != nil { + e = errorHandler(dst, src, "", e) + } + return e } for _, xattr := range xattrKeys { + if _, exclude := excludes[xattr]; exclude { + continue + } data, err := sysx.LGetxattr(src, xattr) if err != nil { - return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) + e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) + if errorHandler != nil { + if e = errorHandler(dst, src, xattr, e); e == nil { + continue + } + } + return e } if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil { - return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) + e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) + if errorHandler != nil { + if e = errorHandler(dst, src, xattr, e); e == nil { + continue + } + } + return e } } diff --git a/vendor/github.com/containerd/continuity/fs/copy_unix.go b/vendor/github.com/containerd/continuity/fs/copy_unix.go index 1a8ae5ebd1934..923dd5a982f28 100644 --- a/vendor/github.com/containerd/continuity/fs/copy_unix.go +++ b/vendor/github.com/containerd/continuity/fs/copy_unix.go @@ -1,4 +1,4 @@ -// +build solaris darwin freebsd +// +build darwin freebsd openbsd solaris /* Copyright The containerd Authors. @@ -25,7 +25,6 @@ import ( "github.com/containerd/continuity/sysx" "github.com/pkg/errors" - "golang.org/x/sys/unix" ) func copyFileInfo(fi os.FileInfo, name string) error { @@ -53,8 +52,7 @@ func copyFileInfo(fi os.FileInfo, name string) error { } } - timespec := []syscall.Timespec{StatAtime(st), StatMtime(st)} - if err := syscall.UtimesNano(name, timespec); err != nil { + if err := utimesNano(name, StatAtime(st), StatMtime(st)); err != nil { return errors.Wrapf(err, "failed to utime %s", name) } @@ -69,28 +67,39 @@ func copyFileContent(dst, src *os.File) error { return err } -func copyXAttrs(dst, src string) error { +func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error { xattrKeys, err := sysx.LListxattr(src) if err != nil { - return errors.Wrapf(err, "failed to list xattrs on %s", src) + e := errors.Wrapf(err, "failed to list xattrs on %s", src) + if errorHandler != nil { + e = errorHandler(dst, src, "", e) + } + return e } for _, xattr := range xattrKeys { + if _, exclude := excludes[xattr]; exclude { + continue + } data, err := sysx.LGetxattr(src, xattr) if err != nil { - return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) + e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) + if errorHandler != nil { + if e = errorHandler(dst, src, xattr, e); e == nil { + continue + } + } + return e } if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil { - return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) + e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) + if errorHandler != nil { + if e = errorHandler(dst, src, xattr, e); e == nil { + continue + } + } + return e } } return nil } - -func copyDevice(dst string, fi os.FileInfo) error { - st, ok := fi.Sys().(*syscall.Stat_t) - if !ok { - return errors.New("unsupported stat type") - } - return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev)) -} diff --git a/vendor/github.com/containerd/continuity/fs/copy_windows.go b/vendor/github.com/containerd/continuity/fs/copy_windows.go index be8e6489bf961..0081583fd019a 100644 --- a/vendor/github.com/containerd/continuity/fs/copy_windows.go +++ b/vendor/github.com/containerd/continuity/fs/copy_windows.go @@ -40,7 +40,7 @@ func copyFileContent(dst, src *os.File) error { return err } -func copyXAttrs(dst, src string) error { +func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error { return nil } diff --git a/vendor/github.com/containerd/continuity/fs/du_unix.go b/vendor/github.com/containerd/continuity/fs/du_unix.go index e22ffbea378fc..9da43d1bc8e9d 100644 --- a/vendor/github.com/containerd/continuity/fs/du_unix.go +++ b/vendor/github.com/containerd/continuity/fs/du_unix.go @@ -25,6 +25,14 @@ import ( "syscall" ) +// blocksUnitSize is the unit used by `st_blocks` in `stat` in bytes. +// See https://man7.org/linux/man-pages/man2/stat.2.html +// st_blocks +// This field indicates the number of blocks allocated to the +// file, in 512-byte units. (This may be smaller than +// st_size/512 when the file has holes.) +const blocksUnitSize = 512 + type inode struct { // TODO(stevvooe): Can probably reduce memory usage by not tracking // device, but we can leave this right for now. @@ -33,9 +41,9 @@ type inode struct { func newInode(stat *syscall.Stat_t) inode { return inode{ - // Dev is uint32 on darwin/bsd, uint64 on linux/solaris + // Dev is uint32 on darwin/bsd, uint64 on linux/solaris/freebsd dev: uint64(stat.Dev), // nolint: unconvert - // Ino is uint32 on bsd, uint64 on darwin/linux/solaris + // Ino is uint32 on bsd, uint64 on darwin/linux/solaris/freebsd ino: uint64(stat.Ino), // nolint: unconvert } } @@ -59,10 +67,11 @@ func diskUsage(ctx context.Context, roots ...string) (Usage, error) { default: } - inoKey := newInode(fi.Sys().(*syscall.Stat_t)) + stat := fi.Sys().(*syscall.Stat_t) + inoKey := newInode(stat) if _, ok := inodes[inoKey]; !ok { inodes[inoKey] = struct{}{} - size += fi.Size() + size += stat.Blocks * blocksUnitSize } return nil @@ -89,10 +98,11 @@ func diffUsage(ctx context.Context, a, b string) (Usage, error) { } if kind == ChangeKindAdd || kind == ChangeKindModify { - inoKey := newInode(fi.Sys().(*syscall.Stat_t)) + stat := fi.Sys().(*syscall.Stat_t) + inoKey := newInode(stat) if _, ok := inodes[inoKey]; !ok { inodes[inoKey] = struct{}{} - size += fi.Size() + size += stat.Blocks * blocksUnitSize } return nil diff --git a/vendor/github.com/containerd/continuity/fs/path.go b/vendor/github.com/containerd/continuity/fs/path.go index 995981780078b..c26be79898e3d 100644 --- a/vendor/github.com/containerd/continuity/fs/path.go +++ b/vendor/github.com/containerd/continuity/fs/path.go @@ -22,7 +22,6 @@ import ( "io" "os" "path/filepath" - "strings" "github.com/pkg/errors" ) @@ -47,9 +46,8 @@ func pathChange(lower, upper *currentPath) (ChangeKind, string) { if upper == nil { return ChangeKindDelete, lower.path } - // TODO: compare by directory - switch i := strings.Compare(lower.path, upper.path); { + switch i := directoryCompare(lower.path, upper.path); { case i < 0: // File in lower that is not in upper return ChangeKindDelete, lower.path @@ -61,6 +59,35 @@ func pathChange(lower, upper *currentPath) (ChangeKind, string) { } } +func directoryCompare(a, b string) int { + l := len(a) + if len(b) < l { + l = len(b) + } + for i := 0; i < l; i++ { + c1, c2 := a[i], b[i] + if c1 == filepath.Separator { + c1 = byte(0) + } + if c2 == filepath.Separator { + c2 = byte(0) + } + if c1 < c2 { + return -1 + } + if c1 > c2 { + return +1 + } + } + if len(a) < len(b) { + return -1 + } + if len(a) > len(b) { + return +1 + } + return 0 +} + func sameFile(f1, f2 *currentPath) (bool, error) { if os.SameFile(f1.f, f2.f) { return true, nil @@ -90,15 +117,13 @@ func sameFile(f1, f2 *currentPath) (bool, error) { // If the timestamp may have been truncated in both of the // files, check content of file to determine difference if t1.Nanosecond() == 0 && t2.Nanosecond() == 0 { - var eq bool if (f1.f.Mode() & os.ModeSymlink) == os.ModeSymlink { - eq, err = compareSymlinkTarget(f1.fullPath, f2.fullPath) - } else if f1.f.Size() > 0 { - eq, err = compareFileContent(f1.fullPath, f2.fullPath) + return compareSymlinkTarget(f1.fullPath, f2.fullPath) } - if err != nil || !eq { - return eq, err + if f1.f.Size() == 0 { // if file sizes are zero length, the files are the same by definition + return true, nil } + return compareFileContent(f1.fullPath, f2.fullPath) } else if t1.Nanosecond() != t2.Nanosecond() { return false, nil } diff --git a/vendor/github.com/containerd/continuity/fs/stat_bsd.go b/vendor/github.com/containerd/continuity/fs/stat_darwinfreebsd.go similarity index 100% rename from vendor/github.com/containerd/continuity/fs/stat_bsd.go rename to vendor/github.com/containerd/continuity/fs/stat_darwinfreebsd.go diff --git a/vendor/github.com/containerd/continuity/fs/stat_linux.go b/vendor/github.com/containerd/continuity/fs/stat_linux.go deleted file mode 100644 index 4a678dd1fd465..0000000000000 --- a/vendor/github.com/containerd/continuity/fs/stat_linux.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package fs - -import ( - "syscall" - "time" -) - -// StatAtime returns the Atim -func StatAtime(st *syscall.Stat_t) syscall.Timespec { - return st.Atim -} - -// StatCtime returns the Ctim -func StatCtime(st *syscall.Stat_t) syscall.Timespec { - return st.Ctim -} - -// StatMtime returns the Mtim -func StatMtime(st *syscall.Stat_t) syscall.Timespec { - return st.Mtim -} - -// StatATimeAsTime returns st.Atim as a time.Time -func StatATimeAsTime(st *syscall.Stat_t) time.Time { - // The int64 conversions ensure the line compiles for 32-bit systems as well. - return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) // nolint: unconvert -} diff --git a/vendor/github.com/containerd/continuity/fs/stat_linuxopenbsd.go b/vendor/github.com/containerd/continuity/fs/stat_linuxopenbsd.go new file mode 100644 index 0000000000000..c68df6e586cde --- /dev/null +++ b/vendor/github.com/containerd/continuity/fs/stat_linuxopenbsd.go @@ -0,0 +1,45 @@ +// +build linux openbsd + +/* + Copyright The containerd Authors. + + 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. +*/ + +package fs + +import ( + "syscall" + "time" +) + +// StatAtime returns the Atim +func StatAtime(st *syscall.Stat_t) syscall.Timespec { + return st.Atim +} + +// StatCtime returns the Ctim +func StatCtime(st *syscall.Stat_t) syscall.Timespec { + return st.Ctim +} + +// StatMtime returns the Mtim +func StatMtime(st *syscall.Stat_t) syscall.Timespec { + return st.Mtim +} + +// StatATimeAsTime returns st.Atim as a time.Time +func StatATimeAsTime(st *syscall.Stat_t) time.Time { + // The int64 conversions ensure the line compiles for 32-bit systems as well. + return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) // nolint: unconvert +} diff --git a/vendor/github.com/containerd/continuity/go.mod b/vendor/github.com/containerd/continuity/go.mod new file mode 100644 index 0000000000000..35cea6ae8d000 --- /dev/null +++ b/vendor/github.com/containerd/continuity/go.mod @@ -0,0 +1,15 @@ +module github.com/containerd/continuity + +go 1.13 + +require ( + bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898 + github.com/dustin/go-humanize v1.0.0 + github.com/golang/protobuf v1.3.5 + github.com/opencontainers/go-digest v1.0.0 + github.com/pkg/errors v0.9.1 + github.com/sirupsen/logrus v1.7.0 + github.com/spf13/cobra v1.0.0 + golang.org/x/sync v0.0.0-20201207232520-09787c993a3a + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c +) diff --git a/vendor/github.com/containerd/continuity/groups_unix.go b/vendor/github.com/containerd/continuity/groups_unix.go deleted file mode 100644 index 022d8ab783911..0000000000000 --- a/vendor/github.com/containerd/continuity/groups_unix.go +++ /dev/null @@ -1,129 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import ( - "bufio" - "fmt" - "io" - "os" - "strconv" - "strings" -) - -// TODO(stevvooe): This needs a lot of work before we can call it useful. - -type groupIndex struct { - byName map[string]*group - byGID map[int]*group -} - -func getGroupIndex() (*groupIndex, error) { - f, err := os.Open("/etc/group") - if err != nil { - return nil, err - } - defer f.Close() - - groups, err := parseGroups(f) - if err != nil { - return nil, err - } - - return newGroupIndex(groups), nil -} - -func newGroupIndex(groups []group) *groupIndex { - gi := &groupIndex{ - byName: make(map[string]*group), - byGID: make(map[int]*group), - } - - for i, group := range groups { - gi.byGID[group.gid] = &groups[i] - gi.byName[group.name] = &groups[i] - } - - return gi -} - -type group struct { - name string - gid int - members []string -} - -func getGroupName(gid int) (string, error) { - f, err := os.Open("/etc/group") - if err != nil { - return "", err - } - defer f.Close() - - groups, err := parseGroups(f) - if err != nil { - return "", err - } - - for _, group := range groups { - if group.gid == gid { - return group.name, nil - } - } - - return "", fmt.Errorf("no group for gid") -} - -// parseGroups parses an /etc/group file for group names, ids and membership. -// This is unix specific. -func parseGroups(rd io.Reader) ([]group, error) { - var groups []group - scanner := bufio.NewScanner(rd) - - for scanner.Scan() { - if strings.HasPrefix(scanner.Text(), "#") { - continue // skip comment - } - - parts := strings.SplitN(scanner.Text(), ":", 4) - - if len(parts) != 4 { - return nil, fmt.Errorf("bad entry: %q", scanner.Text()) - } - - name, _, sgid, smembers := parts[0], parts[1], parts[2], parts[3] - - gid, err := strconv.Atoi(sgid) - if err != nil { - return nil, fmt.Errorf("bad gid: %q", gid) - } - - members := strings.Split(smembers, ",") - - groups = append(groups, group{ - name: name, - gid: gid, - members: members, - }) - } - - if scanner.Err() != nil { - return nil, scanner.Err() - } - - return groups, nil -} diff --git a/vendor/github.com/containerd/continuity/hardlinks.go b/vendor/github.com/containerd/continuity/hardlinks.go deleted file mode 100644 index d493dd7776bc7..0000000000000 --- a/vendor/github.com/containerd/continuity/hardlinks.go +++ /dev/null @@ -1,73 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import ( - "fmt" - "os" -) - -var ( - errNotAHardLink = fmt.Errorf("invalid hardlink") -) - -type hardlinkManager struct { - hardlinks map[hardlinkKey][]Resource -} - -func newHardlinkManager() *hardlinkManager { - return &hardlinkManager{ - hardlinks: map[hardlinkKey][]Resource{}, - } -} - -// Add attempts to add the resource to the hardlink manager. If the resource -// cannot be considered as a hardlink candidate, errNotAHardLink is returned. -func (hlm *hardlinkManager) Add(fi os.FileInfo, resource Resource) error { - if _, ok := resource.(Hardlinkable); !ok { - return errNotAHardLink - } - - key, err := newHardlinkKey(fi) - if err != nil { - return err - } - - hlm.hardlinks[key] = append(hlm.hardlinks[key], resource) - - return nil -} - -// Merge processes the current state of the hardlink manager and merges any -// shared nodes into hardlinked resources. -func (hlm *hardlinkManager) Merge() ([]Resource, error) { - var resources []Resource - for key, linked := range hlm.hardlinks { - if len(linked) < 1 { - return nil, fmt.Errorf("no hardlink entrys for dev, inode pair: %#v", key) - } - - merged, err := Merge(linked...) - if err != nil { - return nil, fmt.Errorf("error merging hardlink: %v", err) - } - - resources = append(resources, merged) - } - - return resources, nil -} diff --git a/vendor/github.com/containerd/continuity/hardlinks_unix.go b/vendor/github.com/containerd/continuity/hardlinks_unix.go deleted file mode 100644 index a15d1759ee6ce..0000000000000 --- a/vendor/github.com/containerd/continuity/hardlinks_unix.go +++ /dev/null @@ -1,52 +0,0 @@ -// +build linux darwin freebsd solaris - -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import ( - "fmt" - "os" - "syscall" -) - -// hardlinkKey provides a tuple-key for managing hardlinks. This is system- -// specific. -type hardlinkKey struct { - dev uint64 - inode uint64 -} - -// newHardlinkKey returns a hardlink key for the provided file info. If the -// resource does not represent a possible hardlink, errNotAHardLink will be -// returned. -func newHardlinkKey(fi os.FileInfo) (hardlinkKey, error) { - sys, ok := fi.Sys().(*syscall.Stat_t) - if !ok { - return hardlinkKey{}, fmt.Errorf("cannot resolve (*syscall.Stat_t) from os.FileInfo") - } - - if sys.Nlink < 2 { - // NOTE(stevvooe): This is not always true for all filesystems. We - // should somehow detect this and provided a slow "polyfill" that - // leverages os.SameFile if we detect a filesystem where link counts - // is not really supported. - return hardlinkKey{}, errNotAHardLink - } - - return hardlinkKey{dev: uint64(sys.Dev), inode: uint64(sys.Ino)}, nil -} diff --git a/vendor/github.com/containerd/continuity/hardlinks_windows.go b/vendor/github.com/containerd/continuity/hardlinks_windows.go deleted file mode 100644 index 5893f4e1ae26c..0000000000000 --- a/vendor/github.com/containerd/continuity/hardlinks_windows.go +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import "os" - -type hardlinkKey struct{} - -func newHardlinkKey(fi os.FileInfo) (hardlinkKey, error) { - // NOTE(stevvooe): Obviously, this is not yet implemented. However, the - // makings of an implementation are available in src/os/types_windows.go. More - // investigation needs to be done to figure out exactly how to do this. - return hardlinkKey{}, errNotAHardLink -} diff --git a/vendor/github.com/containerd/continuity/ioutils.go b/vendor/github.com/containerd/continuity/ioutils.go deleted file mode 100644 index 503640ebfc86b..0000000000000 --- a/vendor/github.com/containerd/continuity/ioutils.go +++ /dev/null @@ -1,63 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import ( - "bytes" - "io" - "io/ioutil" - "os" - "path/filepath" -) - -// AtomicWriteFile atomically writes data to a file by first writing to a -// temp file and calling rename. -func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { - buf := bytes.NewBuffer(data) - return atomicWriteFile(filename, buf, int64(len(data)), perm) -} - -// atomicWriteFile writes data to a file by first writing to a temp -// file and calling rename. -func atomicWriteFile(filename string, r io.Reader, dataSize int64, perm os.FileMode) error { - f, err := ioutil.TempFile(filepath.Dir(filename), ".tmp-"+filepath.Base(filename)) - if err != nil { - return err - } - err = os.Chmod(f.Name(), perm) - if err != nil { - f.Close() - return err - } - n, err := io.Copy(f, r) - if err == nil && n < dataSize { - f.Close() - return io.ErrShortWrite - } - if err != nil { - f.Close() - return err - } - if err := f.Sync(); err != nil { - f.Close() - return err - } - if err := f.Close(); err != nil { - return err - } - return os.Rename(f.Name(), filename) -} diff --git a/vendor/github.com/containerd/continuity/manifest.go b/vendor/github.com/containerd/continuity/manifest.go deleted file mode 100644 index 8074bbfbb1e22..0000000000000 --- a/vendor/github.com/containerd/continuity/manifest.go +++ /dev/null @@ -1,160 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import ( - "fmt" - "io" - "log" - "os" - "sort" - - pb "github.com/containerd/continuity/proto" - "github.com/golang/protobuf/proto" -) - -// Manifest provides the contents of a manifest. Users of this struct should -// not typically modify any fields directly. -type Manifest struct { - // Resources specifies all the resources for a manifest in order by path. - Resources []Resource -} - -func Unmarshal(p []byte) (*Manifest, error) { - var bm pb.Manifest - - if err := proto.Unmarshal(p, &bm); err != nil { - return nil, err - } - - var m Manifest - for _, b := range bm.Resource { - r, err := fromProto(b) - if err != nil { - return nil, err - } - - m.Resources = append(m.Resources, r) - } - - return &m, nil -} - -func Marshal(m *Manifest) ([]byte, error) { - var bm pb.Manifest - for _, resource := range m.Resources { - bm.Resource = append(bm.Resource, toProto(resource)) - } - - return proto.Marshal(&bm) -} - -func MarshalText(w io.Writer, m *Manifest) error { - var bm pb.Manifest - for _, resource := range m.Resources { - bm.Resource = append(bm.Resource, toProto(resource)) - } - - return proto.MarshalText(w, &bm) -} - -// BuildManifest creates the manifest for the given context -func BuildManifest(ctx Context) (*Manifest, error) { - resourcesByPath := map[string]Resource{} - hardlinks := newHardlinkManager() - - if err := ctx.Walk(func(p string, fi os.FileInfo, err error) error { - if err != nil { - return fmt.Errorf("error walking %s: %v", p, err) - } - - if p == string(os.PathSeparator) { - // skip root - return nil - } - - resource, err := ctx.Resource(p, fi) - if err != nil { - if err == ErrNotFound { - return nil - } - log.Printf("error getting resource %q: %v", p, err) - return err - } - - // add to the hardlink manager - if err := hardlinks.Add(fi, resource); err == nil { - // Resource has been accepted by hardlink manager so we don't add - // it to the resourcesByPath until we merge at the end. - return nil - } else if err != errNotAHardLink { - // handle any other case where we have a proper error. - return fmt.Errorf("adding hardlink %s: %v", p, err) - } - - resourcesByPath[p] = resource - - return nil - }); err != nil { - return nil, err - } - - // merge and post-process the hardlinks. - hardlinked, err := hardlinks.Merge() - if err != nil { - return nil, err - } - - for _, resource := range hardlinked { - resourcesByPath[resource.Path()] = resource - } - - var resources []Resource - for _, resource := range resourcesByPath { - resources = append(resources, resource) - } - - sort.Stable(ByPath(resources)) - - return &Manifest{ - Resources: resources, - }, nil -} - -// VerifyManifest verifies all the resources in a manifest -// against files from the given context. -func VerifyManifest(ctx Context, manifest *Manifest) error { - for _, resource := range manifest.Resources { - if err := ctx.Verify(resource); err != nil { - return err - } - } - - return nil -} - -// ApplyManifest applies on the resources in a manifest to -// the given context. -func ApplyManifest(ctx Context, manifest *Manifest) error { - for _, resource := range manifest.Resources { - if err := ctx.Apply(resource); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/github.com/containerd/continuity/proto/gen.go b/vendor/github.com/containerd/continuity/proto/gen.go deleted file mode 100644 index 63ce10fb53120..0000000000000 --- a/vendor/github.com/containerd/continuity/proto/gen.go +++ /dev/null @@ -1,19 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package proto - -//go:generate protoc --go_out=. manifest.proto diff --git a/vendor/github.com/containerd/continuity/proto/manifest.pb.go b/vendor/github.com/containerd/continuity/proto/manifest.pb.go deleted file mode 100644 index 24317766257ad..0000000000000 --- a/vendor/github.com/containerd/continuity/proto/manifest.pb.go +++ /dev/null @@ -1,181 +0,0 @@ -// Code generated by protoc-gen-go. -// source: manifest.proto -// DO NOT EDIT! - -/* -Package proto is a generated protocol buffer package. - -It is generated from these files: - manifest.proto - -It has these top-level messages: - Manifest - Resource - XAttr - ADSEntry -*/ -package proto - -import proto1 "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto1.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto1.ProtoPackageIsVersion2 // please upgrade the proto package - -// Manifest specifies the entries in a container bundle, keyed and sorted by -// path. -type Manifest struct { - Resource []*Resource `protobuf:"bytes,1,rep,name=resource" json:"resource,omitempty"` -} - -func (m *Manifest) Reset() { *m = Manifest{} } -func (m *Manifest) String() string { return proto1.CompactTextString(m) } -func (*Manifest) ProtoMessage() {} -func (*Manifest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -func (m *Manifest) GetResource() []*Resource { - if m != nil { - return m.Resource - } - return nil -} - -type Resource struct { - // Path specifies the path from the bundle root. If more than one - // path is present, the entry may represent a hardlink, rather than using - // a link target. The path format is operating system specific. - Path []string `protobuf:"bytes,1,rep,name=path" json:"path,omitempty"` - // Uid specifies the user id for the resource. - Uid int64 `protobuf:"varint,2,opt,name=uid" json:"uid,omitempty"` - // Gid specifies the group id for the resource. - Gid int64 `protobuf:"varint,3,opt,name=gid" json:"gid,omitempty"` - // user and group are not currently used but their field numbers have been - // reserved for future use. As such, they are marked as deprecated. - User string `protobuf:"bytes,4,opt,name=user" json:"user,omitempty"` - Group string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"` - // Mode defines the file mode and permissions. We've used the same - // bit-packing from Go's os package, - // http://golang.org/pkg/os/#FileMode, since they've done the work of - // creating a cross-platform layout. - Mode uint32 `protobuf:"varint,6,opt,name=mode" json:"mode,omitempty"` - // Size specifies the size in bytes of the resource. This is only valid - // for regular files. - Size uint64 `protobuf:"varint,7,opt,name=size" json:"size,omitempty"` - // Digest specifies the content digest of the target file. Only valid for - // regular files. The strings are formatted in OCI style, i.e. :. - // For detailed information about the format, please refer to OCI Image Spec: - // https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests-and-verification - // The digests are sorted in lexical order and implementations may choose - // which algorithms they prefer. - Digest []string `protobuf:"bytes,8,rep,name=digest" json:"digest,omitempty"` - // Target defines the target of a hard or soft link. Absolute links start - // with a slash and specify the resource relative to the bundle root. - // Relative links do not start with a slash and are relative to the - // resource path. - Target string `protobuf:"bytes,9,opt,name=target" json:"target,omitempty"` - // Major specifies the major device number for character and block devices. - Major uint64 `protobuf:"varint,10,opt,name=major" json:"major,omitempty"` - // Minor specifies the minor device number for character and block devices. - Minor uint64 `protobuf:"varint,11,opt,name=minor" json:"minor,omitempty"` - // Xattr provides storage for extended attributes for the target resource. - Xattr []*XAttr `protobuf:"bytes,12,rep,name=xattr" json:"xattr,omitempty"` - // Ads stores one or more alternate data streams for the target resource. - Ads []*ADSEntry `protobuf:"bytes,13,rep,name=ads" json:"ads,omitempty"` -} - -func (m *Resource) Reset() { *m = Resource{} } -func (m *Resource) String() string { return proto1.CompactTextString(m) } -func (*Resource) ProtoMessage() {} -func (*Resource) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -func (m *Resource) GetXattr() []*XAttr { - if m != nil { - return m.Xattr - } - return nil -} - -func (m *Resource) GetAds() []*ADSEntry { - if m != nil { - return m.Ads - } - return nil -} - -// XAttr encodes extended attributes for a resource. -type XAttr struct { - // Name specifies the attribute name. - Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - // Data specifies the associated data for the attribute. - Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` -} - -func (m *XAttr) Reset() { *m = XAttr{} } -func (m *XAttr) String() string { return proto1.CompactTextString(m) } -func (*XAttr) ProtoMessage() {} -func (*XAttr) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } - -// ADSEntry encodes information for a Windows Alternate Data Stream. -type ADSEntry struct { - // Name specifices the stream name. - Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - // Data specifies the stream data. - // See also the description about the digest below. - Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - // Digest is a CAS representation of the stream data. - // - // At least one of data or digest MUST be specified, and either one of them - // SHOULD be specified. - // - // How to access the actual data using the digest is implementation-specific, - // and implementations can choose not to implement digest. - // So, digest SHOULD be used only when the stream data is large. - Digest string `protobuf:"bytes,3,opt,name=digest" json:"digest,omitempty"` -} - -func (m *ADSEntry) Reset() { *m = ADSEntry{} } -func (m *ADSEntry) String() string { return proto1.CompactTextString(m) } -func (*ADSEntry) ProtoMessage() {} -func (*ADSEntry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } - -func init() { - proto1.RegisterType((*Manifest)(nil), "proto.Manifest") - proto1.RegisterType((*Resource)(nil), "proto.Resource") - proto1.RegisterType((*XAttr)(nil), "proto.XAttr") - proto1.RegisterType((*ADSEntry)(nil), "proto.ADSEntry") -} - -func init() { proto1.RegisterFile("manifest.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 317 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x90, 0x4f, 0x4b, 0xf3, 0x40, - 0x10, 0xc6, 0x49, 0x93, 0xf4, 0x4d, 0xa7, 0xed, 0xab, 0x2c, 0x52, 0xe6, 0x18, 0x73, 0x0a, 0x08, - 0x15, 0xf4, 0xe0, 0xb9, 0xa2, 0x17, 0xc1, 0xcb, 0x7a, 0xf1, 0xba, 0xba, 0x6b, 0x5c, 0x21, 0xd9, - 0xb0, 0xd9, 0x80, 0xfa, 0xe5, 0xfc, 0x6a, 0x32, 0xb3, 0x69, 0xd1, 0x9b, 0xa7, 0x3c, 0xcf, 0x6f, - 0xfe, 0x64, 0xf6, 0x81, 0xff, 0xad, 0xea, 0xec, 0x8b, 0x19, 0xc2, 0xb6, 0xf7, 0x2e, 0x38, 0x91, - 0xf3, 0xa7, 0xba, 0x82, 0xe2, 0x7e, 0x2a, 0x88, 0x33, 0x28, 0xbc, 0x19, 0xdc, 0xe8, 0x9f, 0x0d, - 0x26, 0x65, 0x5a, 0x2f, 0x2f, 0x8e, 0x62, 0xf3, 0x56, 0x4e, 0x58, 0x1e, 0x1a, 0xaa, 0xaf, 0x19, - 0x14, 0x7b, 0x2c, 0x04, 0x64, 0xbd, 0x0a, 0xaf, 0x3c, 0xb5, 0x90, 0xac, 0xc5, 0x31, 0xa4, 0xa3, - 0xd5, 0x38, 0x2b, 0x93, 0x3a, 0x95, 0x24, 0x89, 0x34, 0x56, 0x63, 0x1a, 0x49, 0x63, 0xb5, 0xd8, - 0x40, 0x36, 0x0e, 0xc6, 0x63, 0x56, 0x26, 0xf5, 0xe2, 0x7a, 0x86, 0x89, 0x64, 0x2f, 0x10, 0xf2, - 0xc6, 0xbb, 0xb1, 0xc7, 0xfc, 0x50, 0x88, 0x80, 0xfe, 0xd4, 0x3a, 0x6d, 0x70, 0x5e, 0x26, 0xf5, - 0x5a, 0xb2, 0x26, 0x36, 0xd8, 0x4f, 0x83, 0xff, 0xca, 0xa4, 0xce, 0x24, 0x6b, 0xb1, 0x81, 0xb9, - 0xb6, 0x8d, 0x19, 0x02, 0x16, 0x7c, 0xd3, 0xe4, 0x88, 0x07, 0xe5, 0x1b, 0x13, 0x70, 0x41, 0xab, - 0xe5, 0xe4, 0xc4, 0x09, 0xe4, 0xad, 0x7a, 0x73, 0x1e, 0x81, 0x97, 0x44, 0xc3, 0xd4, 0x76, 0xce, - 0xe3, 0x72, 0xa2, 0x64, 0x44, 0x05, 0xf9, 0xbb, 0x0a, 0xc1, 0xe3, 0x8a, 0x43, 0x5a, 0x4d, 0x21, - 0x3d, 0xee, 0x42, 0xf0, 0x32, 0x96, 0xc4, 0x29, 0xa4, 0x4a, 0x0f, 0xb8, 0xfe, 0x15, 0xe3, 0xee, - 0xe6, 0xe1, 0xb6, 0x0b, 0xfe, 0x43, 0x52, 0xad, 0x3a, 0x87, 0x9c, 0x47, 0xe8, 0xfe, 0x4e, 0xb5, - 0x94, 0x39, 0x5d, 0xc4, 0x9a, 0x98, 0x56, 0x41, 0x71, 0x7c, 0x2b, 0xc9, 0xba, 0xba, 0x83, 0x62, - 0xbf, 0xe1, 0xaf, 0x33, 0x3f, 0x72, 0x48, 0xe3, 0x7b, 0xa3, 0x7b, 0x9a, 0xf3, 0x45, 0x97, 0xdf, - 0x01, 0x00, 0x00, 0xff, 0xff, 0xef, 0x27, 0x99, 0xf7, 0x17, 0x02, 0x00, 0x00, -} diff --git a/vendor/github.com/containerd/continuity/proto/manifest.proto b/vendor/github.com/containerd/continuity/proto/manifest.proto deleted file mode 100644 index 66ef80f054ed7..0000000000000 --- a/vendor/github.com/containerd/continuity/proto/manifest.proto +++ /dev/null @@ -1,97 +0,0 @@ -syntax = "proto3"; - -package proto; - -// Manifest specifies the entries in a container bundle, keyed and sorted by -// path. -message Manifest { - repeated Resource resource = 1; -} - -message Resource { - // Path specifies the path from the bundle root. If more than one - // path is present, the entry may represent a hardlink, rather than using - // a link target. The path format is operating system specific. - repeated string path = 1; - - // NOTE(stevvooe): Need to define clear precedence for user/group/uid/gid precedence. - - // Uid specifies the user id for the resource. - int64 uid = 2; - - // Gid specifies the group id for the resource. - int64 gid = 3; - - // user and group are not currently used but their field numbers have been - // reserved for future use. As such, they are marked as deprecated. - string user = 4 [deprecated=true]; // "deprecated" stands for "reserved" here - string group = 5 [deprecated=true]; // "deprecated" stands for "reserved" here - - // Mode defines the file mode and permissions. We've used the same - // bit-packing from Go's os package, - // http://golang.org/pkg/os/#FileMode, since they've done the work of - // creating a cross-platform layout. - uint32 mode = 6; - - // NOTE(stevvooe): Beyond here, we start defining type specific fields. - - // Size specifies the size in bytes of the resource. This is only valid - // for regular files. - uint64 size = 7; - - // Digest specifies the content digest of the target file. Only valid for - // regular files. The strings are formatted in OCI style, i.e. :. - // For detailed information about the format, please refer to OCI Image Spec: - // https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests-and-verification - // The digests are sorted in lexical order and implementations may choose - // which algorithms they prefer. - repeated string digest = 8; - - // Target defines the target of a hard or soft link. Absolute links start - // with a slash and specify the resource relative to the bundle root. - // Relative links do not start with a slash and are relative to the - // resource path. - string target = 9; - - // Major specifies the major device number for character and block devices. - uint64 major = 10; - - // Minor specifies the minor device number for character and block devices. - uint64 minor = 11; - - // Xattr provides storage for extended attributes for the target resource. - repeated XAttr xattr = 12; - - // Ads stores one or more alternate data streams for the target resource. - repeated ADSEntry ads = 13; - -} - -// XAttr encodes extended attributes for a resource. -message XAttr { - // Name specifies the attribute name. - string name = 1; - - // Data specifies the associated data for the attribute. - bytes data = 2; -} - -// ADSEntry encodes information for a Windows Alternate Data Stream. -message ADSEntry { - // Name specifices the stream name. - string name = 1; - - // Data specifies the stream data. - // See also the description about the digest below. - bytes data = 2; - - // Digest is a CAS representation of the stream data. - // - // At least one of data or digest MUST be specified, and either one of them - // SHOULD be specified. - // - // How to access the actual data using the digest is implementation-specific, - // and implementations can choose not to implement digest. - // So, digest SHOULD be used only when the stream data is large. - string digest = 3; -} diff --git a/vendor/github.com/containerd/continuity/resource.go b/vendor/github.com/containerd/continuity/resource.go deleted file mode 100644 index d2f52bd31a6e5..0000000000000 --- a/vendor/github.com/containerd/continuity/resource.go +++ /dev/null @@ -1,590 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import ( - "errors" - "fmt" - "os" - "reflect" - "sort" - - pb "github.com/containerd/continuity/proto" - "github.com/opencontainers/go-digest" -) - -// TODO(stevvooe): A record based model, somewhat sketched out at the bottom -// of this file, will be more flexible. Another possibly is to tie the package -// interface directly to the protobuf type. This will have efficiency -// advantages at the cost coupling the nasty codegen types to the exported -// interface. - -type Resource interface { - // Path provides the primary resource path relative to the bundle root. In - // cases where resources have more than one path, such as with hard links, - // this will return the primary path, which is often just the first entry. - Path() string - - // Mode returns the - Mode() os.FileMode - - UID() int64 - GID() int64 -} - -// ByPath provides the canonical sort order for a set of resources. Use with -// sort.Stable for deterministic sorting. -type ByPath []Resource - -func (bp ByPath) Len() int { return len(bp) } -func (bp ByPath) Swap(i, j int) { bp[i], bp[j] = bp[j], bp[i] } -func (bp ByPath) Less(i, j int) bool { return bp[i].Path() < bp[j].Path() } - -type XAttrer interface { - XAttrs() map[string][]byte -} - -// Hardlinkable is an interface that a resource type satisfies if it can be a -// hardlink target. -type Hardlinkable interface { - // Paths returns all paths of the resource, including the primary path - // returned by Resource.Path. If len(Paths()) > 1, the resource is a hard - // link. - Paths() []string -} - -type RegularFile interface { - Resource - XAttrer - Hardlinkable - - Size() int64 - Digests() []digest.Digest -} - -// Merge two or more Resources into new file. Typically, this should be -// used to merge regular files as hardlinks. If the files are not identical, -// other than Paths and Digests, the merge will fail and an error will be -// returned. -func Merge(fs ...Resource) (Resource, error) { - if len(fs) < 1 { - return nil, fmt.Errorf("please provide a resource to merge") - } - - if len(fs) == 1 { - return fs[0], nil - } - - var paths []string - var digests []digest.Digest - bypath := map[string][]Resource{} - - // The attributes are all compared against the first to make sure they - // agree before adding to the above collections. If any of these don't - // correctly validate, the merge fails. - prototype := fs[0] - xattrs := make(map[string][]byte) - - // initialize xattrs for use below. All files must have same xattrs. - if prototypeXAttrer, ok := prototype.(XAttrer); ok { - for attr, value := range prototypeXAttrer.XAttrs() { - xattrs[attr] = value - } - } - - for _, f := range fs { - h, isHardlinkable := f.(Hardlinkable) - if !isHardlinkable { - return nil, errNotAHardLink - } - - if f.Mode() != prototype.Mode() { - return nil, fmt.Errorf("modes do not match: %v != %v", f.Mode(), prototype.Mode()) - } - - if f.UID() != prototype.UID() { - return nil, fmt.Errorf("uid does not match: %v != %v", f.UID(), prototype.UID()) - } - - if f.GID() != prototype.GID() { - return nil, fmt.Errorf("gid does not match: %v != %v", f.GID(), prototype.GID()) - } - - if xattrer, ok := f.(XAttrer); ok { - fxattrs := xattrer.XAttrs() - if !reflect.DeepEqual(fxattrs, xattrs) { - return nil, fmt.Errorf("resource %q xattrs do not match: %v != %v", f, fxattrs, xattrs) - } - } - - for _, p := range h.Paths() { - pfs, ok := bypath[p] - if !ok { - // ensure paths are unique by only appending on a new path. - paths = append(paths, p) - } - - bypath[p] = append(pfs, f) - } - - if regFile, isRegFile := f.(RegularFile); isRegFile { - prototypeRegFile, prototypeIsRegFile := prototype.(RegularFile) - if !prototypeIsRegFile { - return nil, errors.New("prototype is not a regular file") - } - - if regFile.Size() != prototypeRegFile.Size() { - return nil, fmt.Errorf("size does not match: %v != %v", regFile.Size(), prototypeRegFile.Size()) - } - - digests = append(digests, regFile.Digests()...) - } else if device, isDevice := f.(Device); isDevice { - prototypeDevice, prototypeIsDevice := prototype.(Device) - if !prototypeIsDevice { - return nil, errors.New("prototype is not a device") - } - - if device.Major() != prototypeDevice.Major() { - return nil, fmt.Errorf("major number does not match: %v != %v", device.Major(), prototypeDevice.Major()) - } - if device.Minor() != prototypeDevice.Minor() { - return nil, fmt.Errorf("minor number does not match: %v != %v", device.Minor(), prototypeDevice.Minor()) - } - } else if _, isNamedPipe := f.(NamedPipe); isNamedPipe { - _, prototypeIsNamedPipe := prototype.(NamedPipe) - if !prototypeIsNamedPipe { - return nil, errors.New("prototype is not a named pipe") - } - } else { - return nil, errNotAHardLink - } - } - - sort.Stable(sort.StringSlice(paths)) - - // Choose a "canonical" file. Really, it is just the first file to sort - // against. We also effectively select the very first digest as the - // "canonical" one for this file. - first := bypath[paths[0]][0] - - resource := resource{ - paths: paths, - mode: first.Mode(), - uid: first.UID(), - gid: first.GID(), - xattrs: xattrs, - } - - switch typedF := first.(type) { - case RegularFile: - var err error - digests, err = uniqifyDigests(digests...) - if err != nil { - return nil, err - } - - return ®ularFile{ - resource: resource, - size: typedF.Size(), - digests: digests, - }, nil - case Device: - return &device{ - resource: resource, - major: typedF.Major(), - minor: typedF.Minor(), - }, nil - - case NamedPipe: - return &namedPipe{ - resource: resource, - }, nil - - default: - return nil, errNotAHardLink - } -} - -type Directory interface { - Resource - XAttrer - - // Directory is a no-op method to identify directory objects by interface. - Directory() -} - -type SymLink interface { - Resource - - // Target returns the target of the symlink contained in the . - Target() string -} - -type NamedPipe interface { - Resource - Hardlinkable - XAttrer - - // Pipe is a no-op method to allow consistent resolution of NamedPipe - // interface. - Pipe() -} - -type Device interface { - Resource - Hardlinkable - XAttrer - - Major() uint64 - Minor() uint64 -} - -type resource struct { - paths []string - mode os.FileMode - uid, gid int64 - xattrs map[string][]byte -} - -var _ Resource = &resource{} - -func (r *resource) Path() string { - if len(r.paths) < 1 { - return "" - } - - return r.paths[0] -} - -func (r *resource) Mode() os.FileMode { - return r.mode -} - -func (r *resource) UID() int64 { - return r.uid -} - -func (r *resource) GID() int64 { - return r.gid -} - -type regularFile struct { - resource - size int64 - digests []digest.Digest -} - -var _ RegularFile = ®ularFile{} - -// newRegularFile returns the RegularFile, using the populated base resource -// and one or more digests of the content. -func newRegularFile(base resource, paths []string, size int64, dgsts ...digest.Digest) (RegularFile, error) { - if !base.Mode().IsRegular() { - return nil, fmt.Errorf("not a regular file") - } - - base.paths = make([]string, len(paths)) - copy(base.paths, paths) - - // make our own copy of digests - ds := make([]digest.Digest, len(dgsts)) - copy(ds, dgsts) - - return ®ularFile{ - resource: base, - size: size, - digests: ds, - }, nil -} - -func (rf *regularFile) Paths() []string { - paths := make([]string, len(rf.paths)) - copy(paths, rf.paths) - return paths -} - -func (rf *regularFile) Size() int64 { - return rf.size -} - -func (rf *regularFile) Digests() []digest.Digest { - digests := make([]digest.Digest, len(rf.digests)) - copy(digests, rf.digests) - return digests -} - -func (rf *regularFile) XAttrs() map[string][]byte { - xattrs := make(map[string][]byte, len(rf.xattrs)) - - for attr, value := range rf.xattrs { - xattrs[attr] = append(xattrs[attr], value...) - } - - return xattrs -} - -type directory struct { - resource -} - -var _ Directory = &directory{} - -func newDirectory(base resource) (Directory, error) { - if !base.Mode().IsDir() { - return nil, fmt.Errorf("not a directory") - } - - return &directory{ - resource: base, - }, nil -} - -func (d *directory) Directory() {} - -func (d *directory) XAttrs() map[string][]byte { - xattrs := make(map[string][]byte, len(d.xattrs)) - - for attr, value := range d.xattrs { - xattrs[attr] = append(xattrs[attr], value...) - } - - return xattrs -} - -type symLink struct { - resource - target string -} - -var _ SymLink = &symLink{} - -func newSymLink(base resource, target string) (SymLink, error) { - if base.Mode()&os.ModeSymlink == 0 { - return nil, fmt.Errorf("not a symlink") - } - - return &symLink{ - resource: base, - target: target, - }, nil -} - -func (l *symLink) Target() string { - return l.target -} - -type namedPipe struct { - resource -} - -var _ NamedPipe = &namedPipe{} - -func newNamedPipe(base resource, paths []string) (NamedPipe, error) { - if base.Mode()&os.ModeNamedPipe == 0 { - return nil, fmt.Errorf("not a namedpipe") - } - - base.paths = make([]string, len(paths)) - copy(base.paths, paths) - - return &namedPipe{ - resource: base, - }, nil -} - -func (np *namedPipe) Pipe() {} - -func (np *namedPipe) Paths() []string { - paths := make([]string, len(np.paths)) - copy(paths, np.paths) - return paths -} - -func (np *namedPipe) XAttrs() map[string][]byte { - xattrs := make(map[string][]byte, len(np.xattrs)) - - for attr, value := range np.xattrs { - xattrs[attr] = append(xattrs[attr], value...) - } - - return xattrs -} - -type device struct { - resource - major, minor uint64 -} - -var _ Device = &device{} - -func newDevice(base resource, paths []string, major, minor uint64) (Device, error) { - if base.Mode()&os.ModeDevice == 0 { - return nil, fmt.Errorf("not a device") - } - - base.paths = make([]string, len(paths)) - copy(base.paths, paths) - - return &device{ - resource: base, - major: major, - minor: minor, - }, nil -} - -func (d *device) Paths() []string { - paths := make([]string, len(d.paths)) - copy(paths, d.paths) - return paths -} - -func (d *device) XAttrs() map[string][]byte { - xattrs := make(map[string][]byte, len(d.xattrs)) - - for attr, value := range d.xattrs { - xattrs[attr] = append(xattrs[attr], value...) - } - - return xattrs -} - -func (d device) Major() uint64 { - return d.major -} - -func (d device) Minor() uint64 { - return d.minor -} - -// toProto converts a resource to a protobuf record. We'd like to push this -// the individual types but we want to keep this all together during -// prototyping. -func toProto(resource Resource) *pb.Resource { - b := &pb.Resource{ - Path: []string{resource.Path()}, - Mode: uint32(resource.Mode()), - Uid: resource.UID(), - Gid: resource.GID(), - } - - if xattrer, ok := resource.(XAttrer); ok { - // Sorts the XAttrs by name for consistent ordering. - keys := []string{} - xattrs := xattrer.XAttrs() - for k := range xattrs { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - b.Xattr = append(b.Xattr, &pb.XAttr{Name: k, Data: xattrs[k]}) - } - } - - switch r := resource.(type) { - case RegularFile: - b.Path = r.Paths() - b.Size = uint64(r.Size()) - - for _, dgst := range r.Digests() { - b.Digest = append(b.Digest, dgst.String()) - } - case SymLink: - b.Target = r.Target() - case Device: - b.Major, b.Minor = r.Major(), r.Minor() - b.Path = r.Paths() - case NamedPipe: - b.Path = r.Paths() - } - - // enforce a few stability guarantees that may not be provided by the - // resource implementation. - sort.Strings(b.Path) - - return b -} - -// fromProto converts from a protobuf Resource to a Resource interface. -func fromProto(b *pb.Resource) (Resource, error) { - base := &resource{ - paths: b.Path, - mode: os.FileMode(b.Mode), - uid: b.Uid, - gid: b.Gid, - } - - base.xattrs = make(map[string][]byte, len(b.Xattr)) - - for _, attr := range b.Xattr { - base.xattrs[attr.Name] = attr.Data - } - - switch { - case base.Mode().IsRegular(): - dgsts := make([]digest.Digest, len(b.Digest)) - for i, dgst := range b.Digest { - // TODO(stevvooe): Should we be validating at this point? - dgsts[i] = digest.Digest(dgst) - } - - return newRegularFile(*base, b.Path, int64(b.Size), dgsts...) - case base.Mode().IsDir(): - return newDirectory(*base) - case base.Mode()&os.ModeSymlink != 0: - return newSymLink(*base, b.Target) - case base.Mode()&os.ModeNamedPipe != 0: - return newNamedPipe(*base, b.Path) - case base.Mode()&os.ModeDevice != 0: - return newDevice(*base, b.Path, b.Major, b.Minor) - } - - return nil, fmt.Errorf("unknown resource record (%#v): %s", b, base.Mode()) -} - -// NOTE(stevvooe): An alternative model that supports inline declaration. -// Convenient for unit testing where inline declarations may be desirable but -// creates an awkward API for the standard use case. - -// type ResourceKind int - -// const ( -// ResourceRegularFile = iota + 1 -// ResourceDirectory -// ResourceSymLink -// Resource -// ) - -// type Resource struct { -// Kind ResourceKind -// Paths []string -// Mode os.FileMode -// UID string -// GID string -// Size int64 -// Digests []digest.Digest -// Target string -// Major, Minor int -// XAttrs map[string][]byte -// } - -// type RegularFile struct { -// Paths []string -// Size int64 -// Digests []digest.Digest -// Perm os.FileMode // os.ModePerm + sticky, setuid, setgid -// } diff --git a/vendor/github.com/containerd/continuity/resource_unix.go b/vendor/github.com/containerd/continuity/resource_unix.go deleted file mode 100644 index 0e103ccc5c7a0..0000000000000 --- a/vendor/github.com/containerd/continuity/resource_unix.go +++ /dev/null @@ -1,53 +0,0 @@ -// +build linux darwin freebsd solaris - -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import ( - "fmt" - "os" - "syscall" -) - -// newBaseResource returns a *resource, populated with data from p and fi, -// where p will be populated directly. -func newBaseResource(p string, fi os.FileInfo) (*resource, error) { - // TODO(stevvooe): This need to be resolved for the container's root, - // where here we are really getting the host OS's value. We need to allow - // this be passed in and fixed up to make these uid/gid mappings portable. - // Either this can be part of the driver or we can achieve it through some - // other mechanism. - sys, ok := fi.Sys().(*syscall.Stat_t) - if !ok { - // TODO(stevvooe): This may not be a hard error for all platforms. We - // may want to move this to the driver. - return nil, fmt.Errorf("unable to resolve syscall.Stat_t from (os.FileInfo).Sys(): %#v", fi) - } - - return &resource{ - paths: []string{p}, - mode: fi.Mode(), - - uid: int64(sys.Uid), - gid: int64(sys.Gid), - - // NOTE(stevvooe): Population of shared xattrs field is deferred to - // the resource types that populate it. Since they are a property of - // the context, they must set there. - }, nil -} diff --git a/vendor/github.com/containerd/continuity/resource_windows.go b/vendor/github.com/containerd/continuity/resource_windows.go deleted file mode 100644 index f9801801cfc9b..0000000000000 --- a/vendor/github.com/containerd/continuity/resource_windows.go +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package continuity - -import "os" - -// newBaseResource returns a *resource, populated with data from p and fi, -// where p will be populated directly. -func newBaseResource(p string, fi os.FileInfo) (*resource, error) { - return &resource{ - paths: []string{p}, - mode: fi.Mode(), - }, nil -} diff --git a/vendor/github.com/containerd/continuity/syscallx/syscall_unix.go b/vendor/github.com/containerd/continuity/syscallx/syscall_unix.go deleted file mode 100644 index 0bfa6a0409d0e..0000000000000 --- a/vendor/github.com/containerd/continuity/syscallx/syscall_unix.go +++ /dev/null @@ -1,26 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package syscallx - -import "syscall" - -// Readlink returns the destination of the named symbolic link. -func Readlink(path string, buf []byte) (n int, err error) { - return syscall.Readlink(path, buf) -} diff --git a/vendor/github.com/containerd/continuity/syscallx/syscall_windows.go b/vendor/github.com/containerd/continuity/syscallx/syscall_windows.go deleted file mode 100644 index 2ba8149905197..0000000000000 --- a/vendor/github.com/containerd/continuity/syscallx/syscall_windows.go +++ /dev/null @@ -1,112 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package syscallx - -import ( - "syscall" - "unsafe" -) - -type reparseDataBuffer struct { - ReparseTag uint32 - ReparseDataLength uint16 - Reserved uint16 - - // GenericReparseBuffer - reparseBuffer byte -} - -type mountPointReparseBuffer struct { - SubstituteNameOffset uint16 - SubstituteNameLength uint16 - PrintNameOffset uint16 - PrintNameLength uint16 - PathBuffer [1]uint16 -} - -type symbolicLinkReparseBuffer struct { - SubstituteNameOffset uint16 - SubstituteNameLength uint16 - PrintNameOffset uint16 - PrintNameLength uint16 - Flags uint32 - PathBuffer [1]uint16 -} - -const ( - _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 - _SYMLINK_FLAG_RELATIVE = 1 -) - -// Readlink returns the destination of the named symbolic link. -func Readlink(path string, buf []byte) (n int, err error) { - fd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING, - syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) - if err != nil { - return -1, err - } - defer syscall.CloseHandle(fd) - - rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE) - var bytesReturned uint32 - err = syscall.DeviceIoControl(fd, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) - if err != nil { - return -1, err - } - - rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0])) - var s string - switch rdb.ReparseTag { - case syscall.IO_REPARSE_TAG_SYMLINK: - data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer)) - p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0])) - s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2]) - if data.Flags&_SYMLINK_FLAG_RELATIVE == 0 { - if len(s) >= 4 && s[:4] == `\??\` { - s = s[4:] - switch { - case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar - // do nothing - case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar - s = `\\` + s[4:] - default: - // unexpected; do nothing - } - } else { - // unexpected; do nothing - } - } - case _IO_REPARSE_TAG_MOUNT_POINT: - data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer)) - p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0])) - s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2]) - if len(s) >= 4 && s[:4] == `\??\` { // \??\C:\foo\bar - if len(s) < 48 || s[:11] != `\??\Volume{` { - s = s[4:] - } - } else { - // unexpected; do nothing - } - default: - // the path is not a symlink or junction but another type of reparse - // point - return -1, syscall.ENOENT - } - n = copy(buf, []byte(s)) - - return n, nil -} diff --git a/vendor/github.com/containerd/continuity/sysx/file_posix.go b/vendor/github.com/containerd/continuity/sysx/file_posix.go deleted file mode 100644 index e28f3a1b574c4..0000000000000 --- a/vendor/github.com/containerd/continuity/sysx/file_posix.go +++ /dev/null @@ -1,128 +0,0 @@ -/* - Copyright The containerd Authors. - - 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. -*/ - -package sysx - -import ( - "os" - "path/filepath" - - "github.com/containerd/continuity/syscallx" -) - -// Readlink returns the destination of the named symbolic link. -// If there is an error, it will be of type *PathError. -func Readlink(name string) (string, error) { - for len := 128; ; len *= 2 { - b := make([]byte, len) - n, e := fixCount(syscallx.Readlink(fixLongPath(name), b)) - if e != nil { - return "", &os.PathError{Op: "readlink", Path: name, Err: e} - } - if n < len { - return string(b[0:n]), nil - } - } -} - -// Many functions in package syscall return a count of -1 instead of 0. -// Using fixCount(call()) instead of call() corrects the count. -func fixCount(n int, err error) (int, error) { - if n < 0 { - n = 0 - } - return n, err -} - -// fixLongPath returns the extended-length (\\?\-prefixed) form of -// path when needed, in order to avoid the default 260 character file -// path limit imposed by Windows. If path is not easily converted to -// the extended-length form (for example, if path is a relative path -// or contains .. elements), or is short enough, fixLongPath returns -// path unmodified. -// -// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath -func fixLongPath(path string) string { - // Do nothing (and don't allocate) if the path is "short". - // Empirically (at least on the Windows Server 2013 builder), - // the kernel is arbitrarily okay with < 248 bytes. That - // matches what the docs above say: - // "When using an API to create a directory, the specified - // path cannot be so long that you cannot append an 8.3 file - // name (that is, the directory name cannot exceed MAX_PATH - // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248. - // - // The MSDN docs appear to say that a normal path that is 248 bytes long - // will work; empirically the path must be less then 248 bytes long. - if len(path) < 248 { - // Don't fix. (This is how Go 1.7 and earlier worked, - // not automatically generating the \\?\ form) - return path - } - - // The extended form begins with \\?\, as in - // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt. - // The extended form disables evaluation of . and .. path - // elements and disables the interpretation of / as equivalent - // to \. The conversion here rewrites / to \ and elides - // . elements as well as trailing or duplicate separators. For - // simplicity it avoids the conversion entirely for relative - // paths or paths containing .. elements. For now, - // \\server\share paths are not converted to - // \\?\UNC\server\share paths because the rules for doing so - // are less well-specified. - if len(path) >= 2 && path[:2] == `\\` { - // Don't canonicalize UNC paths. - return path - } - if !filepath.IsAbs(path) { - // Relative path - return path - } - - const prefix = `\\?` - - pathbuf := make([]byte, len(prefix)+len(path)+len(`\`)) - copy(pathbuf, prefix) - n := len(path) - r, w := 0, len(prefix) - for r < n { - switch { - case os.IsPathSeparator(path[r]): - // empty block - r++ - case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): - // /./ - r++ - case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): - // /../ is currently unhandled - return path - default: - pathbuf[w] = '\\' - w++ - for ; r < n && !os.IsPathSeparator(path[r]); r++ { - pathbuf[w] = path[r] - w++ - } - } - } - // A drive's root directory needs a trailing \ - if w == len(`\\?\c:`) { - pathbuf[w] = '\\' - w++ - } - return string(pathbuf[:w]) -} diff --git a/vendor/github.com/containerd/continuity/sysx/nodata_unix.go b/vendor/github.com/containerd/continuity/sysx/nodata_unix.go index b26f5b3d03949..de4b3d50cedad 100644 --- a/vendor/github.com/containerd/continuity/sysx/nodata_unix.go +++ b/vendor/github.com/containerd/continuity/sysx/nodata_unix.go @@ -1,4 +1,4 @@ -// +build darwin freebsd +// +build darwin freebsd openbsd /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/continuity/sysx/xattr.go b/vendor/github.com/containerd/continuity/sysx/xattr.go index 9e4326dcfe3fd..db6fe70fe927c 100644 --- a/vendor/github.com/containerd/continuity/sysx/xattr.go +++ b/vendor/github.com/containerd/continuity/sysx/xattr.go @@ -20,7 +20,6 @@ package sysx import ( "bytes" - "syscall" "golang.org/x/sys/unix" ) @@ -66,60 +65,53 @@ func LGetxattr(path, attr string) ([]byte, error) { return getxattrAll(path, attr, unix.Lgetxattr) } -const defaultXattrBufferSize = 5 +const defaultXattrBufferSize = 128 type listxattrFunc func(path string, dest []byte) (int, error) func listxattrAll(path string, listFunc listxattrFunc) ([]string, error) { - var p []byte // nil on first execution - - for { - n, err := listFunc(path, p) // first call gets buffer size. + buf := make([]byte, defaultXattrBufferSize) + n, err := listFunc(path, buf) + for err == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size + n, err = listFunc(path, []byte{}) if err != nil { return nil, err } + buf = make([]byte, n) + n, err = listFunc(path, buf) + } + if err != nil { + return nil, err + } - if n > len(p) { - p = make([]byte, n) - continue - } - - p = p[:n] - - ps := bytes.Split(bytes.TrimSuffix(p, []byte{0}), []byte{0}) - var entries []string - for _, p := range ps { - s := string(p) - if s != "" { - entries = append(entries, s) - } + ps := bytes.Split(bytes.TrimSuffix(buf[:n], []byte{0}), []byte{0}) + var entries []string + for _, p := range ps { + if len(p) > 0 { + entries = append(entries, string(p)) } - - return entries, nil } + + return entries, nil } type getxattrFunc func(string, string, []byte) (int, error) func getxattrAll(path, attr string, getFunc getxattrFunc) ([]byte, error) { - p := make([]byte, defaultXattrBufferSize) - for { - n, err := getFunc(path, attr, p) + buf := make([]byte, defaultXattrBufferSize) + n, err := getFunc(path, attr, buf) + for err == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size + n, err = getFunc(path, attr, []byte{}) if err != nil { - if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERANGE { - p = make([]byte, len(p)*2) // this can't be ideal. - continue // try again! - } - return nil, err } - - // realloc to correct size and repeat - if n > len(p) { - p = make([]byte, n) - continue - } - - return p[:n], nil + buf = make([]byte, n) + n, err = getFunc(path, attr, buf) + } + if err != nil { + return nil, err } + return buf[:n], nil } diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go b/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go index c9ef3a1d251f3..f8fa8c63fbae5 100644 --- a/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go +++ b/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go @@ -23,7 +23,7 @@ import ( "runtime" ) -var unsupported = errors.New("extended attributes unsupported on " + runtime.GOOS) +var errUnsupported = errors.New("extended attributes unsupported on " + runtime.GOOS) // Listxattr calls syscall listxattr and reads all content // and returns a string array @@ -33,17 +33,17 @@ func Listxattr(path string) ([]string, error) { // Removexattr calls syscall removexattr func Removexattr(path string, attr string) (err error) { - return unsupported + return errUnsupported } // Setxattr calls syscall setxattr func Setxattr(path string, attr string, data []byte, flags int) (err error) { - return unsupported + return errUnsupported } // Getxattr calls syscall getxattr func Getxattr(path, attr string) ([]byte, error) { - return []byte{}, unsupported + return []byte{}, errUnsupported } // LListxattr lists xattrs, not following symlinks @@ -53,12 +53,12 @@ func LListxattr(path string) ([]string, error) { // LRemovexattr removes an xattr, not following symlinks func LRemovexattr(path string, attr string) (err error) { - return unsupported + return errUnsupported } // LSetxattr sets an xattr, not following symlinks func LSetxattr(path string, attr string, data []byte, flags int) (err error) { - return unsupported + return errUnsupported } // LGetxattr gets an xattr, not following symlinks diff --git a/vendor/github.com/containerd/continuity/vendor.conf b/vendor/github.com/containerd/continuity/vendor.conf deleted file mode 100644 index 5bd88d5fd76df..0000000000000 --- a/vendor/github.com/containerd/continuity/vendor.conf +++ /dev/null @@ -1,13 +0,0 @@ -bazil.org/fuse 371fbbdaa8987b715bdd21d6adc4c9b20155f748 -github.com/dustin/go-humanize bb3d318650d48840a39aa21a027c6630e198e626 -github.com/golang/protobuf 1e59b77b52bf8e4b449a57e6f79f21226d571845 -github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 -github.com/opencontainers/go-digest 279bed98673dd5bef374d3b6e4b09e2af76183bf -github.com/pkg/errors f15c970de5b76fac0b59abb32d62c17cc7bed265 -github.com/sirupsen/logrus 89742aefa4b206dcf400792f3bd35b542998eb3b -github.com/spf13/cobra 2da4a54c5ceefcee7ca5dd0eea1e18a3b6366489 -github.com/spf13/pflag 4c012f6dcd9546820e378d0bdda4d8fc772cdfea -golang.org/x/crypto 9f005a07e0d31d45e6656d241bb5c0f2efd4bc94 -golang.org/x/net a337091b0525af65de94df2eb7e98bd9962dcbe2 -golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c -golang.org/x/sys 77b0e4315053a57ed2962443614bdb28db152054 diff --git a/vendor/github.com/containerd/cri/LICENSE b/vendor/github.com/containerd/cri/LICENSE deleted file mode 100644 index 8dada3edaf50d..0000000000000 --- a/vendor/github.com/containerd/cri/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - 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. diff --git a/vendor/github.com/containerd/cri/README.md b/vendor/github.com/containerd/cri/README.md deleted file mode 100644 index a97c2fe3da60c..0000000000000 --- a/vendor/github.com/containerd/cri/README.md +++ /dev/null @@ -1,176 +0,0 @@ -# cri -

- - -

- -*Note: The standalone `cri-containerd` binary is end-of-life. `cri-containerd` is -transitioning from a standalone binary that talks to containerd to a plugin within -containerd. This github branch is for the `cri` plugin. See -[standalone-cri-containerd branch](https://github.com/containerd/cri/tree/standalone-cri-containerd) -for information about the standalone version of `cri-containerd`.* - -*Note: You need to [drain your node](https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/) before upgrading from standalone `cri-containerd` to containerd with `cri` plugin.* - -[![Build Status](https://api.travis-ci.org/containerd/cri.svg?style=flat-square)](https://travis-ci.org/containerd/cri) -[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/cri)](https://goreportcard.com/report/github.com/containerd/cri) - -`cri` is a [containerd](https://containerd.io/) plugin implementation of Kubernetes [container runtime interface (CRI)](https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/apis/cri/runtime/v1alpha2/api.proto). - -With it, you could run Kubernetes using containerd as the container runtime. -![cri](./docs/cri.png) -## Current Status -`cri` is a native plugin of containerd 1.1 and above. It is built into containerd and enabled by default. - -`cri` is in GA: -* It is feature complete. -* It (the GA version) works with Kubernetes 1.10 and above. -* It has passed all [CRI validation tests](https://github.com/kubernetes/community/blob/master/contributors/devel/cri-validation.md). -* It has passed all [node e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-node-tests.md). -* It has passed all [e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-tests.md). - -See [test dashboard](https://k8s-testgrid.appspot.com/sig-node-containerd) -## Support Metrics -| CRI-Containerd Version | Containerd Version | Kubernetes Version | CRI Version | -|:----------------------:|:------------------:|:------------------:|:-----------:| -| v1.0.0-alpha.x | | 1.7, 1.8 | v1alpha1 | -| v1.0.0-beta.x | | 1.9 | v1alpha1 | -| End-Of-Life | v1.1 | 1.10+ | v1alpha2 | -| | HEAD | 1.10+ | v1alpha2 | - -## Production Quality Cluster on GCE -For a production quality cluster on GCE brought up with `kube-up.sh` refer [here](docs/kube-up.md). -## Installing with Ansible and Kubeadm -For a multi node cluster installer and bring up steps using ansible and kubeadm refer [here](contrib/ansible/README.md). -## Custom Installation -For non ansible users, you can download the `cri-containerd` release tarball and deploy -kubernetes cluster using kubeadm as described [here](docs/installation.md). -## Getting Started for Developers -### Binary Dependencies and Specifications -The current release of the `cri` plugin has the following dependencies: -* [containerd](https://github.com/containerd/containerd) -* [runc](https://github.com/opencontainers/runc) -* [CNI](https://github.com/containernetworking/cni) - -See [versions](./vendor.conf) of these dependencies `cri` is tested with. - -As containerd and runc move to their respective general availability releases, -we will do our best to rebase/retest `cri` with these releases on a -weekly/monthly basis. Similarly, given that `cri` uses the Open -Container Initiative (OCI) [image](https://github.com/opencontainers/image-spec) -and [runtime](https://github.com/opencontainers/runtime-spec) specifications, we -will also do our best to update `cri` to the latest releases of these -specifications as appropriate. -### Install Dependencies -1. Install development libraries: -* **libseccomp development library.** Required by `cri` and runc seccomp support. `libseccomp-dev` (Ubuntu, Debian) / `libseccomp-devel` -(Fedora, CentOS, RHEL). On releases of Ubuntu <=Trusty and Debian <=jessie a -backport version of `libseccomp-dev` is required. See [travis.yml](.travis.yml) for an example on trusty. -* **btrfs development library.** Required by containerd btrfs support. `btrfs-tools`(Ubuntu, Debian) / `btrfs-progs-devel`(Fedora, CentOS, RHEL) -2. Install **`socat`** (required by portforward). -2. Install and setup a go 1.10 development environment. -3. Make a local clone of this repository. -4. Install binary dependencies by running the following command from your cloned `cri/` project directory: -```bash -# Note: install.deps installs the above mentioned runc, containerd, and CNI -# binary dependencies. install.deps is only provided for general use and ease of -# testing. To customize `runc` and `containerd` build tags and/or to configure -# `cni`, please follow instructions in their documents. -make install.deps -``` -### Build and Install `cri` -To build and install a version of containerd with the `cri` plugin, enter the -following commands from your `cri` project directory: -```bash -make -sudo make install -``` -*NOTE: The version of containerd built and installed from the `Makefile` is only for -testing purposes. The version tag carries the suffix "-TEST".* -#### Build Tags -`cri` supports optional build tags for compiling support of various features. -To add build tags to the make option the `BUILD_TAGS` variable must be set. - -```bash -make BUILD_TAGS='seccomp apparmor' -``` - -| Build Tag | Feature | Dependency | -|-----------|------------------------------------|---------------------------------| -| seccomp | syscall filtering | libseccomp development library | -| selinux | selinux process and mount labeling | | -| apparmor | apparmor profile support | | -### Validate Your `cri` Setup -A Kubernetes incubator project called [cri-tools](https://github.com/kubernetes-sigs/cri-tools) -includes programs for exercising CRI implementations such as the `cri` plugin. -More importantly, cri-tools includes the program `critest` which is used for running -[CRI Validation Testing](https://github.com/kubernetes/community/blob/master/contributors/devel/cri-validation.md). - -Run the CRI Validation test to validate your installation of `containerd` with `cri` built in: -```bash -make test-cri -``` -### Running a Kubernetes local cluster -If you already have a working development environment for supported Kubernetes -version, you can try `cri` in a local cluster: - -1. Start the version of `containerd` with `cri` plugin that you built and installed -above as root in a first terminal: -```bash -sudo containerd -``` -2. From the Kubernetes project directory startup a local cluster using `containerd`: -```bash -CONTAINER_RUNTIME=remote CONTAINER_RUNTIME_ENDPOINT='unix:///run/containerd/containerd.sock' ./hack/local-up-cluster.sh -``` -### Test -See [here](./docs/testing.md) for information about test. -## Using crictl -See [here](./docs/crictl.md) for information about using `crictl` to debug -pods, containers, and images. -## Configurations -See [here](./docs/config.md) for information about how to configure cri plugins -and [here](https://github.com/containerd/containerd/blob/master/docs/man/containerd-config.1.md) -for information about how to configure containerd -## Documentation -See [here](./docs) for additional documentation. -## Contributing -Interested in contributing? Check out the [documentation](./CONTRIBUTING.md). - -## Communication -This project was originally established in April of 2017 in the Kubernetes -Incubator program. After reaching the Beta stage, In January of 2018, the -project was merged into [containerd](https://github.com/containerd/containerd). - -For async communication and long running discussions please use issues and pull -requests on this github repo. This will be the best place to discuss design and -implementation. - -For sync communication we have a community slack with a #containerd channel that -everyone is welcome to join and chat about development. - -**Slack:** https://dockr.ly/community - -## Other Communications -As this project is tightly coupled to CRI and CRI-Tools and they are Kubernetes -projects, some of our project communications take place in the Kubernetes' SIG: -`sig-node.` - -For more information about `sig-node`, `CRI`, and the `CRI-Tools` projects: -* [sig-node community site](https://github.com/kubernetes/community/tree/master/sig-node) -* Slack: `#sig-node` channel in Kubernetes (kubernetes.slack.com) -* Mailing List: https://groups.google.com/forum/#!forum/kubernetes-sig-node - -### Reporting Security Issues - -__If you are reporting a security issue, please reach out discreetly at security@containerd.io__. - -## Licenses -The containerd codebase is released under the [Apache 2.0 license](https://github.com/containerd/containerd/blob/master/LICENSE.code). -The README.md file, and files in the "docs" folder are licensed under the -Creative Commons Attribution 4.0 International License under the terms and -conditions set forth in the file "[LICENSE.docs](https://github.com/containerd/containerd/blob/master/LICENSE.docs)". You may obtain a duplicate -copy of the same license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/. - -## Code of Conduct -This project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). diff --git a/vendor/github.com/containerd/cri/pkg/util/deep_copy.go b/vendor/github.com/containerd/cri/pkg/util/deep_copy.go deleted file mode 100644 index 5fdee984b5288..0000000000000 --- a/vendor/github.com/containerd/cri/pkg/util/deep_copy.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -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. -*/ - -package util - -import ( - "encoding/json" - - "github.com/pkg/errors" -) - -// DeepCopy makes a deep copy from src into dst. -func DeepCopy(dst interface{}, src interface{}) error { - if dst == nil { - return errors.New("dst cannot be nil") - } - if src == nil { - return errors.New("src cannot be nil") - } - bytes, err := json.Marshal(src) - if err != nil { - return errors.Wrap(err, "unable to marshal src") - } - err = json.Unmarshal(bytes, dst) - if err != nil { - return errors.Wrap(err, "unable to unmarshal into dst") - } - return nil -} diff --git a/vendor/github.com/containerd/cri/pkg/util/id.go b/vendor/github.com/containerd/cri/pkg/util/id.go deleted file mode 100644 index 11b0a70a65c28..0000000000000 --- a/vendor/github.com/containerd/cri/pkg/util/id.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -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. -*/ - -package util - -import ( - "encoding/hex" - "math/rand" -) - -// GenerateID generates a random unique id. -func GenerateID() string { - b := make([]byte, 32) - rand.Read(b) - return hex.EncodeToString(b) -} diff --git a/vendor/github.com/containerd/cri/pkg/util/image.go b/vendor/github.com/containerd/cri/pkg/util/image.go deleted file mode 100644 index 0f471fc42243b..0000000000000 --- a/vendor/github.com/containerd/cri/pkg/util/image.go +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -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. -*/ - -package util - -import ( - "github.com/docker/distribution/reference" -) - -// NormalizeImageRef normalizes the image reference following the docker convention. This is added -// mainly for backward compatibility. -// The reference returned can only be either tagged or digested. For reference contains both tag -// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@ -// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as -// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa. -func NormalizeImageRef(ref string) (reference.Named, error) { - named, err := reference.ParseNormalizedNamed(ref) - if err != nil { - return nil, err - } - if _, ok := named.(reference.NamedTagged); ok { - if canonical, ok := named.(reference.Canonical); ok { - // The reference is both tagged and digested, only - // return digested. - newNamed, err := reference.WithName(canonical.Name()) - if err != nil { - return nil, err - } - newCanonical, err := reference.WithDigest(newNamed, canonical.Digest()) - if err != nil { - return nil, err - } - return newCanonical, nil - } - } - return reference.TagNameOnly(named), nil -} diff --git a/vendor/github.com/containerd/cri/pkg/util/strings.go b/vendor/github.com/containerd/cri/pkg/util/strings.go deleted file mode 100644 index d5cbc2e8ef30d..0000000000000 --- a/vendor/github.com/containerd/cri/pkg/util/strings.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -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. -*/ - -package util - -import "strings" - -// InStringSlice checks whether a string is inside a string slice. -// Comparison is case insensitive. -func InStringSlice(ss []string, str string) bool { - for _, s := range ss { - if strings.ToLower(s) == strings.ToLower(str) { - return true - } - } - return false -} - -// SubtractStringSlice subtracts string from string slice. -// Comparison is case insensitive. -func SubtractStringSlice(ss []string, str string) []string { - var res []string - for _, s := range ss { - if strings.ToLower(s) == strings.ToLower(str) { - continue - } - res = append(res, s) - } - return res -} - -// MergeStringSlices merges 2 string slices into one and remove duplicated elements. -func MergeStringSlices(a []string, b []string) []string { - set := map[string]struct{}{} - for _, s := range a { - set[s] = struct{}{} - } - for _, s := range b { - set[s] = struct{}{} - } - var ss []string - for s := range set { - ss = append(ss, s) - } - return ss -} diff --git a/vendor/github.com/containerd/cri/vendor.conf b/vendor/github.com/containerd/cri/vendor.conf deleted file mode 100644 index a9ffca873aee3..0000000000000 --- a/vendor/github.com/containerd/cri/vendor.conf +++ /dev/null @@ -1,78 +0,0 @@ -github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 -github.com/blang/semver v3.1.0 -github.com/boltdb/bolt v1.3.1 -github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 -github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 -github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 -github.com/containerd/containerd 1950f791d9225ffe061c77e74e292bcb3c428a04 -github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537 -github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c -github.com/containerd/go-cni 6d7b509a054a3cb1c35ed1865d4fde2f0cb547cd -github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 -github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d -github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 -github.com/containernetworking/cni v0.6.0 -github.com/containernetworking/plugins v0.7.0 -github.com/coreos/go-systemd v14 -github.com/davecgh/go-spew v1.1.0 -github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621 -github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00 -github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 -github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098 -github.com/docker/go-units v0.3.1 -github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528 -github.com/emicklei/go-restful v2.2.1 -github.com/ghodss/yaml v1.0.0 -github.com/godbus/dbus v3 -github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef -github.com/gogo/protobuf v1.0.0 -github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed -github.com/golang/protobuf v1.1.0 -github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c -github.com/grpc-ecosystem/go-grpc-prometheus v1.1 -github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 -github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f -github.com/json-iterator/go 1.1.5 -github.com/matttproud/golang_protobuf_extensions v1.0.0 -github.com/Microsoft/go-winio v0.4.10 -github.com/Microsoft/hcsshim 44c060121b68e8bdc40b411beba551f3b4ee9e55 -github.com/modern-go/concurrent 1.0.3 -github.com/modern-go/reflect2 1.0.1 -github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7 -github.com/opencontainers/image-spec v1.0.1 -github.com/opencontainers/runc 20aff4f0488c6d4b8df4d85b4f63f1f704c11abd -github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce -github.com/opencontainers/runtime-tools v0.6.0 -github.com/opencontainers/selinux b6fa367ed7f534f9ba25391cc2d467085dbb445a -github.com/pkg/errors v0.8.0 -github.com/pmezard/go-difflib v1.0.0 -github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823 -github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c -github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563 -github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd -github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 -github.com/sirupsen/logrus v1.0.0 -github.com/stretchr/testify v1.1.4 -github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 -github.com/tchap/go-patricia v2.2.6 -github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c -github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6 -github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b -github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874 -golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067 -golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac -golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4 -golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c -golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2 https://github.com/golang/sys -golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 -golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 -google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 -google.golang.org/grpc v1.12.0 -gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 -gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77 -k8s.io/api 012f271b5d41baad56190c5f1ae19bff16df0fd8 -k8s.io/apimachinery 6429050ef506887d121f3e7306e894f8900d8a63 -k8s.io/apiserver e9312c15296b6c2c923ebd5031ff5d1d5fd022d7 -k8s.io/client-go 37c3c02ec96533daec0dbda1f39a6b1d68505c79 -k8s.io/kubernetes v1.12.0-beta.1 -k8s.io/utils 982821ea41da7e7c15f3d3738921eb2e7e241ccd diff --git a/vendor/github.com/containerd/fifo/errors.go b/vendor/github.com/containerd/fifo/errors.go new file mode 100644 index 0000000000000..50f73b2dc01ba --- /dev/null +++ b/vendor/github.com/containerd/fifo/errors.go @@ -0,0 +1,28 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package fifo + +import "errors" + +var ( + ErrClosed = errors.New("fifo closed") + ErrCtrlClosed = errors.New("control of closed fifo") + ErrRdFrmWRONLY = errors.New("reading from write-only fifo") + ErrReadClosed = errors.New("reading from a closed fifo") + ErrWrToRDONLY = errors.New("writing to read-only fifo") + ErrWriteClosed = errors.New("writing to a closed fifo") +) diff --git a/vendor/github.com/containerd/fifo/fifo.go b/vendor/github.com/containerd/fifo/fifo.go index e79813da7db75..45a9b38402d43 100644 --- a/vendor/github.com/containerd/fifo/fifo.go +++ b/vendor/github.com/containerd/fifo/fifo.go @@ -1,3 +1,5 @@ +// +build !windows + /* Copyright The containerd Authors. @@ -17,6 +19,7 @@ package fifo import ( + "context" "io" "os" "runtime" @@ -24,7 +27,7 @@ import ( "syscall" "github.com/pkg/errors" - "golang.org/x/net/context" + "golang.org/x/sys/unix" ) type fifo struct { @@ -41,6 +44,21 @@ type fifo struct { var leakCheckWg *sync.WaitGroup +// OpenFifoDup2 is same as OpenFifo, but additionally creates a copy of the FIFO file descriptor with dup2 syscall. +func OpenFifoDup2(ctx context.Context, fn string, flag int, perm os.FileMode, fd int) (io.ReadWriteCloser, error) { + f, err := openFifo(ctx, fn, flag, perm) + if err != nil { + return nil, errors.Wrap(err, "fifo error") + } + + if err := unix.Dup2(int(f.file.Fd()), fd); err != nil { + _ = f.Close() + return nil, errors.Wrap(err, "dup2 error") + } + + return f, nil +} + // OpenFifo opens a fifo. Returns io.ReadWriteCloser. // Context can be used to cancel this function until open(2) has not returned. // Accepted flags: @@ -52,9 +70,13 @@ var leakCheckWg *sync.WaitGroup // fifo isn't open. read/write will be connected after the actual fifo is // open or after fifo is closed. func OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { + return openFifo(ctx, fn, flag, perm) +} + +func openFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (*fifo, error) { if _, err := os.Stat(fn); err != nil { if os.IsNotExist(err) && flag&syscall.O_CREAT != 0 { - if err := mkfifo(fn, uint32(perm&os.ModePerm)); err != nil && !os.IsExist(err) { + if err := syscall.Mkfifo(fn, uint32(perm&os.ModePerm)); err != nil && !os.IsExist(err) { return nil, errors.Wrapf(err, "error creating fifo %v", fn) } } else { @@ -147,7 +169,7 @@ func OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.Re // Read from a fifo to a byte array. func (f *fifo) Read(b []byte) (int, error) { if f.flag&syscall.O_WRONLY > 0 { - return 0, errors.New("reading from write-only fifo") + return 0, ErrRdFrmWRONLY } select { case <-f.opened: @@ -158,14 +180,14 @@ func (f *fifo) Read(b []byte) (int, error) { case <-f.opened: return f.file.Read(b) case <-f.closed: - return 0, errors.New("reading from a closed fifo") + return 0, ErrReadClosed } } // Write from byte array to a fifo. func (f *fifo) Write(b []byte) (int, error) { if f.flag&(syscall.O_WRONLY|syscall.O_RDWR) == 0 { - return 0, errors.New("writing to read-only fifo") + return 0, ErrWrToRDONLY } select { case <-f.opened: @@ -176,7 +198,7 @@ func (f *fifo) Write(b []byte) (int, error) { case <-f.opened: return f.file.Write(b) case <-f.closed: - return 0, errors.New("writing to a closed fifo") + return 0, ErrWriteClosed } } diff --git a/vendor/github.com/containerd/fifo/go.mod b/vendor/github.com/containerd/fifo/go.mod new file mode 100644 index 0000000000000..0c1c48fab7829 --- /dev/null +++ b/vendor/github.com/containerd/fifo/go.mod @@ -0,0 +1,9 @@ +module github.com/containerd/fifo + +go 1.13 + +require ( + github.com/pkg/errors v0.9.1 + github.com/stretchr/testify v1.6.1 + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c +) diff --git a/vendor/github.com/containerd/fifo/handle_linux.go b/vendor/github.com/containerd/fifo/handle_linux.go index 6ac89b6a4d9b4..0ee2c9feeba41 100644 --- a/vendor/github.com/containerd/fifo/handle_linux.go +++ b/vendor/github.com/containerd/fifo/handle_linux.go @@ -27,6 +27,7 @@ import ( "github.com/pkg/errors" ) +//nolint:golint const O_PATH = 010000000 type handle struct { @@ -56,9 +57,10 @@ func getHandle(fn string) (*handle, error) { h := &handle{ f: f, name: fn, - dev: uint64(stat.Dev), - ino: stat.Ino, - fd: fd, + //nolint:unconvert + dev: uint64(stat.Dev), + ino: stat.Ino, + fd: fd, } // check /proc just in case @@ -83,6 +85,7 @@ func (h *handle) Path() (string, error) { if err := syscall.Stat(h.procPath(), &stat); err != nil { return "", errors.Wrapf(err, "path %v could not be statted", h.procPath()) } + //nolint:unconvert if uint64(stat.Dev) != h.dev || stat.Ino != h.ino { return "", errors.Errorf("failed to verify handle %v/%v %v/%v", stat.Dev, h.dev, stat.Ino, h.ino) } diff --git a/vendor/github.com/containerd/fifo/handle_nolinux.go b/vendor/github.com/containerd/fifo/handle_nolinux.go index 4f2a282b2b363..81ca308fe50ea 100644 --- a/vendor/github.com/containerd/fifo/handle_nolinux.go +++ b/vendor/github.com/containerd/fifo/handle_nolinux.go @@ -1,4 +1,4 @@ -// +build !linux +// +build !linux,!windows /* Copyright The containerd Authors. @@ -38,8 +38,8 @@ func getHandle(fn string) (*handle, error) { h := &handle{ fn: fn, - dev: uint64(stat.Dev), - ino: uint64(stat.Ino), + dev: uint64(stat.Dev), //nolint: unconvert + ino: uint64(stat.Ino), //nolint: unconvert } return h, nil @@ -50,7 +50,7 @@ func (h *handle) Path() (string, error) { if err := syscall.Stat(h.fn, &stat); err != nil { return "", errors.Wrapf(err, "path %v could not be statted", h.fn) } - if uint64(stat.Dev) != h.dev || uint64(stat.Ino) != h.ino { + if uint64(stat.Dev) != h.dev || uint64(stat.Ino) != h.ino { //nolint: unconvert return "", errors.Errorf("failed to verify handle %v/%v %v/%v for %v", stat.Dev, h.dev, stat.Ino, h.ino, h.fn) } return h.fn, nil diff --git a/vendor/github.com/containerd/fifo/mkfifo_nosolaris.go b/vendor/github.com/containerd/fifo/mkfifo_nosolaris.go deleted file mode 100644 index 2799a06d10789..0000000000000 --- a/vendor/github.com/containerd/fifo/mkfifo_nosolaris.go +++ /dev/null @@ -1,25 +0,0 @@ -// +build !solaris - -/* - Copyright The containerd Authors. - - 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. -*/ - -package fifo - -import "syscall" - -func mkfifo(path string, mode uint32) (err error) { - return syscall.Mkfifo(path, mode) -} diff --git a/vendor/github.com/containerd/fifo/mkfifo_solaris.go b/vendor/github.com/containerd/fifo/mkfifo_solaris.go deleted file mode 100644 index 1ecd722ae2cb1..0000000000000 --- a/vendor/github.com/containerd/fifo/mkfifo_solaris.go +++ /dev/null @@ -1,27 +0,0 @@ -// +build solaris - -/* - Copyright The containerd Authors. - - 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. -*/ - -package fifo - -import ( - "golang.org/x/sys/unix" -) - -func mkfifo(path string, mode uint32) (err error) { - return unix.Mkfifo(path, mode) -} diff --git a/vendor/github.com/containerd/fifo/raw.go b/vendor/github.com/containerd/fifo/raw.go new file mode 100644 index 0000000000000..cead94ca27b0f --- /dev/null +++ b/vendor/github.com/containerd/fifo/raw.go @@ -0,0 +1,114 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package fifo + +import ( + "syscall" +) + +// SyscallConn provides raw access to the fifo's underlying filedescrptor. +// See syscall.Conn for guarantees provided by this interface. +func (f *fifo) SyscallConn() (syscall.RawConn, error) { + // deterministic check for closed + select { + case <-f.closed: + return nil, ErrClosed + default: + } + + select { + case <-f.closed: + return nil, ErrClosed + case <-f.opened: + return f.file.SyscallConn() + default: + } + + // Not opened and not closed, this means open is non-blocking AND it's not open yet + // Use rawConn to deal with non-blocking open. + rc := &rawConn{f: f, ready: make(chan struct{})} + go func() { + select { + case <-f.closed: + return + case <-f.opened: + rc.raw, rc.err = f.file.SyscallConn() + close(rc.ready) + } + }() + + return rc, nil +} + +type rawConn struct { + f *fifo + ready chan struct{} + raw syscall.RawConn + err error +} + +func (r *rawConn) Control(f func(fd uintptr)) error { + select { + case <-r.f.closed: + return ErrCtrlClosed + case <-r.ready: + } + + if r.err != nil { + return r.err + } + + return r.raw.Control(f) +} + +func (r *rawConn) Read(f func(fd uintptr) (done bool)) error { + if r.f.flag&syscall.O_WRONLY > 0 { + return ErrRdFrmWRONLY + } + + select { + case <-r.f.closed: + return ErrReadClosed + case <-r.ready: + } + + if r.err != nil { + return r.err + } + + return r.raw.Read(f) +} + +func (r *rawConn) Write(f func(fd uintptr) (done bool)) error { + if r.f.flag&(syscall.O_WRONLY|syscall.O_RDWR) == 0 { + return ErrWrToRDONLY + } + + select { + case <-r.f.closed: + return ErrWriteClosed + case <-r.ready: + } + + if r.err != nil { + return r.err + } + + return r.raw.Write(f) +} diff --git a/vendor/github.com/containerd/fifo/readme.md b/vendor/github.com/containerd/fifo/readme.md index 2b41b3b1ca376..ad4727dfe5dfe 100644 --- a/vendor/github.com/containerd/fifo/readme.md +++ b/vendor/github.com/containerd/fifo/readme.md @@ -1,6 +1,9 @@ ### fifo -[![Build Status](https://travis-ci.org/containerd/fifo.svg?branch=master)](https://travis-ci.org/containerd/fifo) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/containerd/fifo)](https://pkg.go.dev/github.com/containerd/fifo) +[![Build Status](https://github.com/containerd/fifo/workflows/CI/badge.svg)](https://github.com/containerd/fifo/actions?query=workflow%3ACI) +[![codecov](https://codecov.io/gh/containerd/fifo/branch/master/graph/badge.svg)](https://codecov.io/gh/containerd/fifo) +[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/fifo)](https://goreportcard.com/report/github.com/containerd/fifo) Go package for handling fifos in a sane way. @@ -30,3 +33,14 @@ func (f *fifo) Write(b []byte) (int, error) // before open(2) has returned and fifo was never opened. func (f *fifo) Close() error ``` + +## Project details + +The fifo is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/fifo/utils.go b/vendor/github.com/containerd/fifo/utils.go new file mode 100644 index 0000000000000..bbdf7901547f0 --- /dev/null +++ b/vendor/github.com/containerd/fifo/utils.go @@ -0,0 +1,35 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package fifo + +import "os" + +// IsFifo checks if a file is a (named pipe) fifo +// if the file does not exist then it returns false +func IsFifo(path string) (bool, error) { + stat, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + if stat.Mode()&os.ModeNamedPipe == os.ModeNamedPipe { + return true, nil + } + return false, nil +} diff --git a/vendor/github.com/containerd/go-runc/README.md b/vendor/github.com/containerd/go-runc/README.md index 239601f1e7ddc..c899bdd7ed873 100644 --- a/vendor/github.com/containerd/go-runc/README.md +++ b/vendor/github.com/containerd/go-runc/README.md @@ -1,7 +1,7 @@ # go-runc [![Build Status](https://travis-ci.org/containerd/go-runc.svg?branch=master)](https://travis-ci.org/containerd/go-runc) - +[![codecov](https://codecov.io/gh/containerd/go-runc/branch/master/graph/badge.svg)](https://codecov.io/gh/containerd/go-runc) This is a package for consuming the [runc](https://github.com/opencontainers/runc) binary in your Go applications. It tries to expose all the settings and features of the runc CLI. If there is something missing then add it, its opensource! @@ -12,3 +12,14 @@ or greater. ## Docs Docs can be found at [godoc.org](https://godoc.org/github.com/containerd/go-runc). + +## Project details + +The go-runc is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/go-runc/command_linux.go b/vendor/github.com/containerd/go-runc/command_linux.go index 71b52f9de4e82..8a30f679d08f3 100644 --- a/vendor/github.com/containerd/go-runc/command_linux.go +++ b/vendor/github.com/containerd/go-runc/command_linux.go @@ -20,6 +20,7 @@ import ( "context" "os" "os/exec" + "strings" "syscall" ) @@ -32,10 +33,24 @@ func (r *Runc) command(context context.Context, args ...string) *exec.Cmd { cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: r.Setpgid, } - cmd.Env = os.Environ() + cmd.Env = filterEnv(os.Environ(), "NOTIFY_SOCKET") // NOTIFY_SOCKET introduces a special behavior in runc but should only be set if invoked from systemd if r.PdeathSignal != 0 { cmd.SysProcAttr.Pdeathsig = r.PdeathSignal } return cmd } + +func filterEnv(in []string, names ...string) []string { + out := make([]string, 0, len(in)) +loop0: + for _, v := range in { + for _, k := range names { + if strings.HasPrefix(v, k+"=") { + continue loop0 + } + } + out = append(out, v) + } + return out +} diff --git a/vendor/github.com/containerd/go-runc/go.mod b/vendor/github.com/containerd/go-runc/go.mod new file mode 100644 index 0000000000000..f69c26fd68fb3 --- /dev/null +++ b/vendor/github.com/containerd/go-runc/go.mod @@ -0,0 +1,11 @@ +module github.com/containerd/go-runc + +go 1.13 + +require ( + github.com/containerd/console v1.0.1 + github.com/opencontainers/runtime-spec v1.0.2 + github.com/pkg/errors v0.9.1 + github.com/sirupsen/logrus v1.7.0 + golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f +) diff --git a/vendor/github.com/containerd/go-runc/io_unix.go b/vendor/github.com/containerd/go-runc/io_unix.go index 567cd072e5f88..ccf1dd490d9ec 100644 --- a/vendor/github.com/containerd/go-runc/io_unix.go +++ b/vendor/github.com/containerd/go-runc/io_unix.go @@ -20,7 +20,9 @@ package runc import ( "github.com/pkg/errors" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" + "runtime" ) // NewPipeIO creates pipe pairs to be used with runc @@ -47,7 +49,13 @@ func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) { } pipes = append(pipes, stdin) if err = unix.Fchown(int(stdin.r.Fd()), uid, gid); err != nil { - return nil, errors.Wrap(err, "failed to chown stdin") + // TODO: revert with proper darwin solution, skipping for now + // as darwin chown is returning EINVAL on anonymous pipe + if runtime.GOOS == "darwin" { + logrus.WithError(err).Debug("failed to chown stdin, ignored") + } else { + return nil, errors.Wrap(err, "failed to chown stdin") + } } } if option.OpenStdout { @@ -56,7 +64,13 @@ func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) { } pipes = append(pipes, stdout) if err = unix.Fchown(int(stdout.w.Fd()), uid, gid); err != nil { - return nil, errors.Wrap(err, "failed to chown stdout") + // TODO: revert with proper darwin solution, skipping for now + // as darwin chown is returning EINVAL on anonymous pipe + if runtime.GOOS == "darwin" { + logrus.WithError(err).Debug("failed to chown stdout, ignored") + } else { + return nil, errors.Wrap(err, "failed to chown stdout") + } } } if option.OpenStderr { @@ -65,7 +79,13 @@ func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) { } pipes = append(pipes, stderr) if err = unix.Fchown(int(stderr.w.Fd()), uid, gid); err != nil { - return nil, errors.Wrap(err, "failed to chown stderr") + // TODO: revert with proper darwin solution, skipping for now + // as darwin chown is returning EINVAL on anonymous pipe + if runtime.GOOS == "darwin" { + logrus.WithError(err).Debug("failed to chown stderr, ignored") + } else { + return nil, errors.Wrap(err, "failed to chown stderr") + } } } return &pipeIO{ diff --git a/vendor/github.com/containerd/go-runc/runc.go b/vendor/github.com/containerd/go-runc/runc.go index 96262afab3964..f5f03ae95eb4b 100644 --- a/vendor/github.com/containerd/go-runc/runc.go +++ b/vendor/github.com/containerd/go-runc/runc.go @@ -17,6 +17,7 @@ package runc import ( + "bytes" "context" "encoding/json" "errors" @@ -28,7 +29,6 @@ import ( "path/filepath" "strconv" "strings" - "syscall" "time" specs "github.com/opencontainers/runtime-spec/specs-go" @@ -54,29 +54,15 @@ const ( DefaultCommand = "runc" ) -// Runc is the client to the runc cli -type Runc struct { - //If command is empty, DefaultCommand is used - Command string - Root string - Debug bool - Log string - LogFormat Format - PdeathSignal syscall.Signal - Setpgid bool - Criu string - SystemdCgroup bool - Rootless *bool // nil stands for "auto" -} - // List returns all containers created inside the provided runc root directory func (r *Runc) List(context context.Context) ([]*Container, error) { - data, err := cmdOutput(r.command(context, "list", "--format=json"), false) + data, err := cmdOutput(r.command(context, "list", "--format=json"), false, nil) + defer putBuf(data) if err != nil { return nil, err } var out []*Container - if err := json.Unmarshal(data, &out); err != nil { + if err := json.Unmarshal(data.Bytes(), &out); err != nil { return nil, err } return out, nil @@ -84,12 +70,13 @@ func (r *Runc) List(context context.Context) ([]*Container, error) { // State returns the state for the container provided by id func (r *Runc) State(context context.Context, id string) (*Container, error) { - data, err := cmdOutput(r.command(context, "state", id), true) + data, err := cmdOutput(r.command(context, "state", id), true, nil) + defer putBuf(data) if err != nil { - return nil, fmt.Errorf("%s: %s", err, data) + return nil, fmt.Errorf("%s: %s", err, data.String()) } var c Container - if err := json.Unmarshal(data, &c); err != nil { + if err := json.Unmarshal(data.Bytes(), &c); err != nil { return nil, err } return &c, nil @@ -108,6 +95,7 @@ type CreateOpts struct { NoPivot bool NoNewKeyring bool ExtraFiles []*os.File + Started chan<- int } func (o *CreateOpts) args() (out []string, err error) { @@ -153,9 +141,10 @@ func (r *Runc) Create(context context.Context, id, bundle string, opts *CreateOp cmd.ExtraFiles = opts.ExtraFiles if cmd.Stdout == nil && cmd.Stderr == nil { - data, err := cmdOutput(cmd, true) + data, err := cmdOutput(cmd, true, nil) + defer putBuf(data) if err != nil { - return fmt.Errorf("%s: %s", err, data) + return fmt.Errorf("%s: %s", err, data.String()) } return nil } @@ -172,7 +161,7 @@ func (r *Runc) Create(context context.Context, id, bundle string, opts *CreateOp } status, err := Monitor.Wait(cmd, ec) if err == nil && status != 0 { - err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) + err = fmt.Errorf("%s did not terminate successfully: %w", cmd.Args[0], &ExitError{status}) } return err } @@ -187,6 +176,7 @@ type ExecOpts struct { PidFile string ConsoleSocket ConsoleSocket Detach bool + Started chan<- int } func (o *ExecOpts) args() (out []string, err error) { @@ -206,9 +196,12 @@ func (o *ExecOpts) args() (out []string, err error) { return out, nil } -// Exec executres and additional process inside the container based on a full +// Exec executes an additional process inside the container based on a full // OCI Process specification func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts *ExecOpts) error { + if opts.Started != nil { + defer close(opts.Started) + } f, err := ioutil.TempFile(os.Getenv("XDG_RUNTIME_DIR"), "runc-process") if err != nil { return err @@ -232,9 +225,10 @@ func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts opts.Set(cmd) } if cmd.Stdout == nil && cmd.Stderr == nil { - data, err := cmdOutput(cmd, true) + data, err := cmdOutput(cmd, true, opts.Started) + defer putBuf(data) if err != nil { - return fmt.Errorf("%s: %s", err, data) + return fmt.Errorf("%w: %s", err, data.String()) } return nil } @@ -242,6 +236,9 @@ func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts if err != nil { return err } + if opts.Started != nil { + opts.Started <- cmd.Process.Pid + } if opts != nil && opts.IO != nil { if c, ok := opts.IO.(StartCloser); ok { if err := c.CloseAfterStart(); err != nil { @@ -251,7 +248,7 @@ func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts } status, err := Monitor.Wait(cmd, ec) if err == nil && status != 0 { - err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) + err = fmt.Errorf("%s did not terminate successfully: %w", cmd.Args[0], &ExitError{status}) } return err } @@ -259,6 +256,9 @@ func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts // Run runs the create, start, delete lifecycle of the container // and returns its exit status after it has exited func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts) (int, error) { + if opts.Started != nil { + defer close(opts.Started) + } args := []string{"run", "--bundle", bundle} if opts != nil { oargs, err := opts.args() @@ -275,7 +275,14 @@ func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts) if err != nil { return -1, err } - return Monitor.Wait(cmd, ec) + if opts.Started != nil { + opts.Started <- cmd.Process.Pid + } + status, err := Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate successfully: %w", cmd.Args[0], &ExitError{status}) + } + return status, err } type DeleteOpts struct { @@ -394,12 +401,13 @@ func (r *Runc) Resume(context context.Context, id string) error { // Ps lists all the processes inside the container returning their pids func (r *Runc) Ps(context context.Context, id string) ([]int, error) { - data, err := cmdOutput(r.command(context, "ps", "--format", "json", id), true) + data, err := cmdOutput(r.command(context, "ps", "--format", "json", id), true, nil) + defer putBuf(data) if err != nil { - return nil, fmt.Errorf("%s: %s", err, data) + return nil, fmt.Errorf("%s: %s", err, data.String()) } var pids []int - if err := json.Unmarshal(data, &pids); err != nil { + if err := json.Unmarshal(data.Bytes(), &pids); err != nil { return nil, err } return pids, nil @@ -407,12 +415,13 @@ func (r *Runc) Ps(context context.Context, id string) ([]int, error) { // Top lists all the processes inside the container returning the full ps data func (r *Runc) Top(context context.Context, id string, psOptions string) (*TopResults, error) { - data, err := cmdOutput(r.command(context, "ps", "--format", "table", id, psOptions), true) + data, err := cmdOutput(r.command(context, "ps", "--format", "table", id, psOptions), true, nil) + defer putBuf(data) if err != nil { - return nil, fmt.Errorf("%s: %s", err, data) + return nil, fmt.Errorf("%s: %s", err, data.String()) } - topResults, err := ParsePSOutput(data) + topResults, err := ParsePSOutput(data.Bytes()) if err != nil { return nil, fmt.Errorf("%s: ", err) } @@ -441,6 +450,10 @@ type CheckpointOpts struct { // EmptyNamespaces creates a namespace for the container but does not save its properties // Provide the namespaces you wish to be checkpointed without their settings on restore EmptyNamespaces []string + // LazyPages uses userfaultfd to lazily restore memory pages + LazyPages bool + // StatusFile is the file criu writes \0 to once lazy-pages is ready + StatusFile *os.File } type CgroupMode string @@ -482,6 +495,9 @@ func (o *CheckpointOpts) args() (out []string) { for _, ns := range o.EmptyNamespaces { out = append(out, "--empty-ns", ns) } + if o.LazyPages { + out = append(out, "--lazy-pages") + } return out } @@ -500,13 +516,23 @@ func PreDump(args []string) []string { // Checkpoint allows you to checkpoint a container using criu func (r *Runc) Checkpoint(context context.Context, id string, opts *CheckpointOpts, actions ...CheckpointAction) error { args := []string{"checkpoint"} + extraFiles := []*os.File{} if opts != nil { args = append(args, opts.args()...) + if opts.StatusFile != nil { + // pass the status file to the child process + extraFiles = []*os.File{opts.StatusFile} + // set status-fd to 3 as this will be the file descriptor + // of the first file passed with cmd.ExtraFiles + args = append(args, "--status-fd", "3") + } } for _, a := range actions { args = a(args) } - return r.runOrError(r.command(context, append(args, id)...)) + cmd := r.command(context, append(args, id)...) + cmd.ExtraFiles = extraFiles + return r.runOrError(cmd) } type RestoreOpts struct { @@ -570,7 +596,11 @@ func (r *Runc) Restore(context context.Context, id, bundle string, opts *Restore } } } - return Monitor.Wait(cmd, ec) + status, err := Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate successfully: %w", cmd.Args[0], &ExitError{status}) + } + return status, err } // Update updates the current container with the provided resource spec @@ -597,42 +627,33 @@ type Version struct { // Version returns the runc and runtime-spec versions func (r *Runc) Version(context context.Context) (Version, error) { - data, err := cmdOutput(r.command(context, "--version"), false) + data, err := cmdOutput(r.command(context, "--version"), false, nil) + defer putBuf(data) if err != nil { return Version{}, err } - return parseVersion(data) + return parseVersion(data.Bytes()) } func parseVersion(data []byte) (Version, error) { var v Version parts := strings.Split(strings.TrimSpace(string(data)), "\n") - if len(parts) != 3 { - return v, nil - } - for i, p := range []struct { - dest *string - split string - }{ - { - dest: &v.Runc, - split: "version ", - }, - { - dest: &v.Commit, - split: ": ", - }, - { - dest: &v.Spec, - split: ": ", - }, - } { - p2 := strings.Split(parts[i], p.split) - if len(p2) != 2 { - return v, fmt.Errorf("unable to parse version line %q", parts[i]) + + if len(parts) > 0 { + if !strings.HasPrefix(parts[0], "runc version ") { + return v, nil + } + v.Runc = parts[0][13:] + + for _, part := range parts[1:] { + if strings.HasPrefix(part, "commit: ") { + v.Commit = part[8:] + } else if strings.HasPrefix(part, "spec: ") { + v.Spec = part[6:] + } } - *p.dest = p2[1] } + return v, nil } @@ -674,20 +695,22 @@ func (r *Runc) runOrError(cmd *exec.Cmd) error { } status, err := Monitor.Wait(cmd, ec) if err == nil && status != 0 { - err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) + err = fmt.Errorf("%s did not terminate successfully: %w", cmd.Args[0], &ExitError{status}) } return err } - data, err := cmdOutput(cmd, true) + data, err := cmdOutput(cmd, true, nil) + defer putBuf(data) if err != nil { - return fmt.Errorf("%s: %s", err, data) + return fmt.Errorf("%s: %s", err, data.String()) } return nil } -func cmdOutput(cmd *exec.Cmd, combined bool) ([]byte, error) { +// callers of cmdOutput are expected to call putBuf on the returned Buffer +// to ensure it is released back to the shared pool after use. +func cmdOutput(cmd *exec.Cmd, combined bool, started chan<- int) (*bytes.Buffer, error) { b := getBuf() - defer putBuf(b) cmd.Stdout = b if combined { @@ -697,11 +720,22 @@ func cmdOutput(cmd *exec.Cmd, combined bool) ([]byte, error) { if err != nil { return nil, err } + if started != nil { + started <- cmd.Process.Pid + } status, err := Monitor.Wait(cmd, ec) if err == nil && status != 0 { - err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) + err = fmt.Errorf("%s did not terminate successfully: %w", cmd.Args[0], &ExitError{status}) } - return b.Bytes(), err + return b, err +} + +type ExitError struct { + Status int +} + +func (e *ExitError) Error() string { + return fmt.Sprintf("exit status %d", e.Status) } diff --git a/vendor/github.com/containerd/go-runc/runc_unix.go b/vendor/github.com/containerd/go-runc/runc_unix.go new file mode 100644 index 0000000000000..548ffd6b90c64 --- /dev/null +++ b/vendor/github.com/containerd/go-runc/runc_unix.go @@ -0,0 +1,38 @@ +//+build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package runc + +import ( + "golang.org/x/sys/unix" +) + +// Runc is the client to the runc cli +type Runc struct { + //If command is empty, DefaultCommand is used + Command string + Root string + Debug bool + Log string + LogFormat Format + PdeathSignal unix.Signal + Setpgid bool + Criu string + SystemdCgroup bool + Rootless *bool // nil stands for "auto" +} diff --git a/vendor/github.com/containerd/go-runc/runc_windows.go b/vendor/github.com/containerd/go-runc/runc_windows.go new file mode 100644 index 0000000000000..c5873de8b6f7f --- /dev/null +++ b/vendor/github.com/containerd/go-runc/runc_windows.go @@ -0,0 +1,31 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package runc + +// Runc is the client to the runc cli +type Runc struct { + //If command is empty, DefaultCommand is used + Command string + Root string + Debug bool + Log string + LogFormat Format + Setpgid bool + Criu string + SystemdCgroup bool + Rootless *bool // nil stands for "auto" +} diff --git a/vendor/github.com/containerd/go-runc/utils.go b/vendor/github.com/containerd/go-runc/utils.go index 69ad6ead751b4..948b6336a7f02 100644 --- a/vendor/github.com/containerd/go-runc/utils.go +++ b/vendor/github.com/containerd/go-runc/utils.go @@ -57,6 +57,10 @@ func getBuf() *bytes.Buffer { } func putBuf(b *bytes.Buffer) { + if b == nil { + return + } + b.Reset() bytesBufferPool.Put(b) } diff --git a/vendor/github.com/containerd/ttrpc/README.md b/vendor/github.com/containerd/ttrpc/README.md index d1eed6b120c6f..547a1297df7de 100644 --- a/vendor/github.com/containerd/ttrpc/README.md +++ b/vendor/github.com/containerd/ttrpc/README.md @@ -1,6 +1,7 @@ # ttrpc -[![Build Status](https://travis-ci.org/containerd/ttrpc.svg?branch=master)](https://travis-ci.org/containerd/ttrpc) +[![Build Status](https://github.com/containerd/ttrpc/workflows/CI/badge.svg)](https://github.com/containerd/ttrpc/actions?query=workflow%3ACI) +[![codecov](https://codecov.io/gh/containerd/ttrpc/branch/main/graph/badge.svg)](https://codecov.io/gh/containerd/ttrpc) GRPC for low-memory environments. @@ -40,13 +41,18 @@ directly, if required. # Status -Very new. YMMV. - TODO: -- [X] Plumb error codes and GRPC status -- [X] Remove use of any type and dependency on typeurl package -- [X] Ensure that protocol can support streaming in the future - [ ] Document protocol layout - [ ] Add testing under concurrent load to ensure - [ ] Verify connection error handling + +# Project details + +ttrpc is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/ttrpc/channel.go b/vendor/github.com/containerd/ttrpc/channel.go index 22f5496b4b952..81116a5e23fc4 100644 --- a/vendor/github.com/containerd/ttrpc/channel.go +++ b/vendor/github.com/containerd/ttrpc/channel.go @@ -18,13 +18,12 @@ package ttrpc import ( "bufio" - "context" "encoding/binary" + "fmt" "io" "net" "sync" - "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -98,7 +97,7 @@ func newChannel(conn net.Conn) *channel { // returned will be valid and caller should send that along to // the correct consumer. The bytes on the underlying channel // will be discarded. -func (ch *channel) recv(ctx context.Context) (messageHeader, []byte, error) { +func (ch *channel) recv() (messageHeader, []byte, error) { mh, err := readMessageHeader(ch.hrbuf[:], ch.br) if err != nil { return messageHeader{}, nil, err @@ -106,7 +105,7 @@ func (ch *channel) recv(ctx context.Context) (messageHeader, []byte, error) { if mh.Length > uint32(messageLengthMax) { if _, err := ch.br.Discard(int(mh.Length)); err != nil { - return mh, nil, errors.Wrapf(err, "failed to discard after receiving oversized message") + return mh, nil, fmt.Errorf("failed to discard after receiving oversized message: %w", err) } return mh, nil, status.Errorf(codes.ResourceExhausted, "message length %v exceed maximum message size of %v", mh.Length, messageLengthMax) @@ -114,13 +113,13 @@ func (ch *channel) recv(ctx context.Context) (messageHeader, []byte, error) { p := ch.getmbuf(int(mh.Length)) if _, err := io.ReadFull(ch.br, p); err != nil { - return messageHeader{}, nil, errors.Wrapf(err, "failed reading message") + return messageHeader{}, nil, fmt.Errorf("failed reading message: %w", err) } return mh, p, nil } -func (ch *channel) send(ctx context.Context, streamID uint32, t messageType, p []byte) error { +func (ch *channel) send(streamID uint32, t messageType, p []byte) error { if err := writeMessageHeader(ch.bw, ch.hwbuf[:], messageHeader{Length: uint32(len(p)), StreamID: streamID, Type: t}); err != nil { return err } diff --git a/vendor/github.com/containerd/ttrpc/client.go b/vendor/github.com/containerd/ttrpc/client.go index e40592dd743eb..26c3dd2a98192 100644 --- a/vendor/github.com/containerd/ttrpc/client.go +++ b/vendor/github.com/containerd/ttrpc/client.go @@ -18,16 +18,18 @@ package ttrpc import ( "context" + "errors" "io" "net" "os" "strings" "sync" "syscall" + "time" "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -35,28 +37,58 @@ import ( // closed. var ErrClosed = errors.New("ttrpc: closed") +// Client for a ttrpc server type Client struct { codec codec conn net.Conn channel *channel calls chan *callRequest - closed chan struct{} - closeOnce sync.Once - closeFunc func() - done chan struct{} - err error + ctx context.Context + closed func() + + closeOnce sync.Once + userCloseFunc func() + userCloseWaitCh chan struct{} + + errOnce sync.Once + err error + interceptor UnaryClientInterceptor +} + +// ClientOpts configures a client +type ClientOpts func(c *Client) + +// WithOnClose sets the close func whenever the client's Close() method is called +func WithOnClose(onClose func()) ClientOpts { + return func(c *Client) { + c.userCloseFunc = onClose + } } -func NewClient(conn net.Conn) *Client { +// WithUnaryClientInterceptor sets the provided client interceptor +func WithUnaryClientInterceptor(i UnaryClientInterceptor) ClientOpts { + return func(c *Client) { + c.interceptor = i + } +} + +func NewClient(conn net.Conn, opts ...ClientOpts) *Client { + ctx, cancel := context.WithCancel(context.Background()) c := &Client{ - codec: codec{}, - conn: conn, - channel: newChannel(conn), - calls: make(chan *callRequest), - closed: make(chan struct{}), - done: make(chan struct{}), - closeFunc: func() {}, + codec: codec{}, + conn: conn, + channel: newChannel(conn), + calls: make(chan *callRequest), + closed: cancel, + ctx: ctx, + userCloseFunc: func() {}, + userCloseWaitCh: make(chan struct{}), + interceptor: defaultClientInterceptor, + } + + for _, o := range opts { + o(c) } go c.run() @@ -86,7 +118,18 @@ func (c *Client) Call(ctx context.Context, service, method string, req, resp int cresp = &Response{} ) - if err := c.dispatch(ctx, creq, cresp); err != nil { + if metadata, ok := GetMetadata(ctx); ok { + metadata.setRequest(creq) + } + + if dl, ok := ctx.Deadline(); ok { + creq.TimeoutNano = dl.Sub(time.Now()).Nanoseconds() + } + + info := &UnaryClientInfo{ + FullMethod: fullPath(service, method), + } + if err := c.interceptor(ctx, creq, cresp, info, c.dispatch); err != nil { return err } @@ -94,16 +137,16 @@ func (c *Client) Call(ctx context.Context, service, method string, req, resp int return err } - if cresp.Status == nil { - return errors.New("no status provided on response") + if cresp.Status != nil && cresp.Status.Code != int32(codes.OK) { + return status.ErrorProto(cresp.Status) } - - return status.ErrorProto(cresp.Status) + return nil } func (c *Client) dispatch(ctx context.Context, req *Request, resp *Response) error { errs := make(chan error, 1) call := &callRequest{ + ctx: ctx, req: req, resp: resp, errs: errs, @@ -113,8 +156,8 @@ func (c *Client) dispatch(ctx context.Context, req *Request, resp *Response) err case <-ctx.Done(): return ctx.Err() case c.calls <- call: - case <-c.done: - return c.err + case <-c.ctx.Done(): + return c.error() } select { @@ -122,22 +165,27 @@ func (c *Client) dispatch(ctx context.Context, req *Request, resp *Response) err return ctx.Err() case err := <-errs: return filterCloseErr(err) - case <-c.done: - return c.err + case <-c.ctx.Done(): + return c.error() } } func (c *Client) Close() error { c.closeOnce.Do(func() { - close(c.closed) + c.closed() }) - return nil } -// OnClose allows a close func to be called when the server is closed -func (c *Client) OnClose(closer func()) { - c.closeFunc = closer +// UserOnCloseWait is used to blocks untils the user's on-close callback +// finishes. +func (c *Client) UserOnCloseWait(ctx context.Context) error { + select { + case <-c.userCloseWaitCh: + return nil + case <-ctx.Done(): + return ctx.Err() + } } type message struct { @@ -146,101 +194,175 @@ type message struct { err error } +// callMap provides access to a map of active calls, guarded by a mutex. +type callMap struct { + m sync.Mutex + activeCalls map[uint32]*callRequest + closeErr error +} + +// newCallMap returns a new callMap with an empty set of active calls. +func newCallMap() *callMap { + return &callMap{ + activeCalls: make(map[uint32]*callRequest), + } +} + +// set adds a call entry to the map with the given streamID key. +func (cm *callMap) set(streamID uint32, cr *callRequest) error { + cm.m.Lock() + defer cm.m.Unlock() + if cm.closeErr != nil { + return cm.closeErr + } + cm.activeCalls[streamID] = cr + return nil +} + +// get looks up the call entry for the given streamID key, then removes it +// from the map and returns it. +func (cm *callMap) get(streamID uint32) (cr *callRequest, ok bool, err error) { + cm.m.Lock() + defer cm.m.Unlock() + if cm.closeErr != nil { + return nil, false, cm.closeErr + } + cr, ok = cm.activeCalls[streamID] + if ok { + delete(cm.activeCalls, streamID) + } + return +} + +// abort sends the given error to each active call, and clears the map. +// Once abort has been called, any subsequent calls to the callMap will return the error passed to abort. +func (cm *callMap) abort(err error) error { + cm.m.Lock() + defer cm.m.Unlock() + if cm.closeErr != nil { + return cm.closeErr + } + for streamID, call := range cm.activeCalls { + call.errs <- err + delete(cm.activeCalls, streamID) + } + cm.closeErr = err + return nil +} + func (c *Client) run() { var ( - streamID uint32 = 1 - waiters = make(map[uint32]*callRequest) - calls = c.calls - incoming = make(chan *message) - shutdown = make(chan struct{}) - shutdownErr error + waiters = newCallMap() + receiverDone = make(chan struct{}) ) + // Sender goroutine + // Receives calls from dispatch, adds them to the set of active calls, and sends them + // to the server. go func() { - defer close(shutdown) - - // start one more goroutine to recv messages without blocking. + var streamID uint32 = 1 for { - mh, p, err := c.channel.recv(context.TODO()) - if err != nil { - _, ok := status.FromError(err) - if !ok { - // treat all errors that are not an rpc status as terminal. - // all others poison the connection. - shutdownErr = err - return + select { + case <-c.ctx.Done(): + return + case call := <-c.calls: + id := streamID + streamID += 2 // enforce odd client initiated request ids + if err := waiters.set(id, call); err != nil { + call.errs <- err // errs is buffered so should not block. + continue + } + if err := c.send(id, messageTypeRequest, call.req); err != nil { + call.errs <- err // errs is buffered so should not block. + waiters.get(id) // remove from waiters set } } + } + }() + + // Receiver goroutine + // Receives responses from the server, looks up the call info in the set of active calls, + // and notifies the caller of the response. + go func() { + defer close(receiverDone) + for { select { - case incoming <- &message{ - messageHeader: mh, - p: p[:mh.Length], - err: err, - }: - case <-c.done: + case <-c.ctx.Done(): + c.setError(c.ctx.Err()) return + default: + mh, p, err := c.channel.recv() + if err != nil { + _, ok := status.FromError(err) + if !ok { + // treat all errors that are not an rpc status as terminal. + // all others poison the connection. + c.setError(filterCloseErr(err)) + return + } + } + msg := &message{ + messageHeader: mh, + p: p[:mh.Length], + err: err, + } + call, ok, err := waiters.get(mh.StreamID) + if err != nil { + logrus.Errorf("ttrpc: failed to look up active call: %s", err) + continue + } + if !ok { + logrus.Errorf("ttrpc: received message for unknown channel %v", mh.StreamID) + continue + } + call.errs <- c.recv(call.resp, msg) } } }() - defer c.conn.Close() - defer close(c.done) - defer c.closeFunc() + defer func() { + c.conn.Close() + c.userCloseFunc() + close(c.userCloseWaitCh) + }() for { select { - case call := <-calls: - if err := c.send(call.ctx, streamID, messageTypeRequest, call.req); err != nil { - call.errs <- err - continue - } - - waiters[streamID] = call - streamID += 2 // enforce odd client initiated request ids - case msg := <-incoming: - call, ok := waiters[msg.StreamID] - if !ok { - logrus.Errorf("ttrpc: received message for unknown channel %v", msg.StreamID) - continue - } - - call.errs <- c.recv(call.resp, msg) - delete(waiters, msg.StreamID) - case <-shutdown: - if shutdownErr != nil { - shutdownErr = filterCloseErr(shutdownErr) - } else { - shutdownErr = ErrClosed - } - - shutdownErr = errors.Wrapf(shutdownErr, "ttrpc: client shutting down") - - c.err = shutdownErr - for _, waiter := range waiters { - waiter.errs <- shutdownErr - } + case <-receiverDone: + // The receiver has exited. + // don't return out, let the close of the context trigger the abort of waiters c.Close() - return - case <-c.closed: - if c.err == nil { - c.err = ErrClosed - } - // broadcast the shutdown error to the remaining waiters. - for _, waiter := range waiters { - waiter.errs <- c.err - } + case <-c.ctx.Done(): + // Abort all active calls. This will also prevent any new calls from being added + // to waiters. + waiters.abort(c.error()) return } } } -func (c *Client) send(ctx context.Context, streamID uint32, mtype messageType, msg interface{}) error { +func (c *Client) error() error { + c.errOnce.Do(func() { + if c.err == nil { + c.err = ErrClosed + } + }) + return c.err +} + +func (c *Client) setError(err error) { + c.errOnce.Do(func() { + c.err = err + }) +} + +func (c *Client) send(streamID uint32, mtype messageType, msg interface{}) error { p, err := c.codec.Marshal(msg) if err != nil { return err } - return c.channel.send(ctx, streamID, mtype, p) + return c.channel.send(streamID, mtype, p) } func (c *Client) recv(resp *Response, msg *message) error { @@ -249,7 +371,7 @@ func (c *Client) recv(resp *Response, msg *message) error { } if msg.Type != messageTypeResponse { - return errors.New("unkown message type received") + return errors.New("unknown message type received") } defer c.channel.putmbuf(msg.p) @@ -261,22 +383,25 @@ func (c *Client) recv(resp *Response, msg *message) error { // // This purposely ignores errors with a wrapped cause. func filterCloseErr(err error) error { - if err == nil { + switch { + case err == nil: return nil - } - - if err == io.EOF { + case err == io.EOF: return ErrClosed - } - - if strings.Contains(err.Error(), "use of closed network connection") { + case errors.Is(err, io.EOF): return ErrClosed - } - - // if we have an epipe on a write, we cast to errclosed - if oerr, ok := err.(*net.OpError); ok && oerr.Op == "write" { - if serr, ok := oerr.Err.(*os.SyscallError); ok && serr.Err == syscall.EPIPE { - return ErrClosed + case strings.Contains(err.Error(), "use of closed network connection"): + return ErrClosed + default: + // if we have an epipe on a write or econnreset on a read , we cast to errclosed + var oerr *net.OpError + if errors.As(err, &oerr) && (oerr.Op == "write" || oerr.Op == "read") { + serr, sok := oerr.Err.(*os.SyscallError) + if sok && ((serr.Err == syscall.EPIPE && oerr.Op == "write") || + (serr.Err == syscall.ECONNRESET && oerr.Op == "read")) { + + return ErrClosed + } } } diff --git a/vendor/github.com/containerd/ttrpc/codec.go b/vendor/github.com/containerd/ttrpc/codec.go index b4aac2fac0388..880634c27e39d 100644 --- a/vendor/github.com/containerd/ttrpc/codec.go +++ b/vendor/github.com/containerd/ttrpc/codec.go @@ -17,8 +17,9 @@ package ttrpc import ( + "fmt" + "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" ) type codec struct{} @@ -28,7 +29,7 @@ func (c codec) Marshal(msg interface{}) ([]byte, error) { case proto.Message: return proto.Marshal(v) default: - return nil, errors.Errorf("ttrpc: cannot marshal unknown type: %T", msg) + return nil, fmt.Errorf("ttrpc: cannot marshal unknown type: %T", msg) } } @@ -37,6 +38,6 @@ func (c codec) Unmarshal(p []byte, msg interface{}) error { case proto.Message: return proto.Unmarshal(p, v) default: - return errors.Errorf("ttrpc: cannot unmarshal into unknown type: %T", msg) + return fmt.Errorf("ttrpc: cannot unmarshal into unknown type: %T", msg) } } diff --git a/vendor/github.com/containerd/ttrpc/config.go b/vendor/github.com/containerd/ttrpc/config.go index 019b7a09dd85d..097419635c6ab 100644 --- a/vendor/github.com/containerd/ttrpc/config.go +++ b/vendor/github.com/containerd/ttrpc/config.go @@ -16,12 +16,14 @@ package ttrpc -import "github.com/pkg/errors" +import "errors" type serverConfig struct { - handshaker Handshaker + handshaker Handshaker + interceptor UnaryServerInterceptor } +// ServerOpt for configuring a ttrpc server type ServerOpt func(*serverConfig) error // WithServerHandshaker can be passed to NewServer to ensure that the @@ -37,3 +39,14 @@ func WithServerHandshaker(handshaker Handshaker) ServerOpt { return nil } } + +// WithUnaryServerInterceptor sets the provided interceptor on the server +func WithUnaryServerInterceptor(i UnaryServerInterceptor) ServerOpt { + return func(c *serverConfig) error { + if c.interceptor != nil { + return errors.New("only one interceptor allowed per server") + } + c.interceptor = i + return nil + } +} diff --git a/vendor/github.com/containerd/ttrpc/go.mod b/vendor/github.com/containerd/ttrpc/go.mod new file mode 100644 index 0000000000000..efc00860c6aee --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/go.mod @@ -0,0 +1,13 @@ +module github.com/containerd/ttrpc + +go 1.13 + +require ( + github.com/gogo/protobuf v1.3.2 + github.com/prometheus/procfs v0.6.0 + github.com/sirupsen/logrus v1.8.1 + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c + google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 + google.golang.org/grpc v1.27.1 + google.golang.org/protobuf v1.27.1 +) diff --git a/vendor/github.com/containerd/ttrpc/interceptor.go b/vendor/github.com/containerd/ttrpc/interceptor.go new file mode 100644 index 0000000000000..c1219dac65f6f --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/interceptor.go @@ -0,0 +1,50 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package ttrpc + +import "context" + +// UnaryServerInfo provides information about the server request +type UnaryServerInfo struct { + FullMethod string +} + +// UnaryClientInfo provides information about the client request +type UnaryClientInfo struct { + FullMethod string +} + +// Unmarshaler contains the server request data and allows it to be unmarshaled +// into a concrete type +type Unmarshaler func(interface{}) error + +// Invoker invokes the client's request and response from the ttrpc server +type Invoker func(context.Context, *Request, *Response) error + +// UnaryServerInterceptor specifies the interceptor function for server request/response +type UnaryServerInterceptor func(context.Context, Unmarshaler, *UnaryServerInfo, Method) (interface{}, error) + +// UnaryClientInterceptor specifies the interceptor function for client request/response +type UnaryClientInterceptor func(context.Context, *Request, *Response, *UnaryClientInfo, Invoker) error + +func defaultServerInterceptor(ctx context.Context, unmarshal Unmarshaler, info *UnaryServerInfo, method Method) (interface{}, error) { + return method(ctx, unmarshal) +} + +func defaultClientInterceptor(ctx context.Context, req *Request, resp *Response, _ *UnaryClientInfo, invoker Invoker) error { + return invoker(ctx, req, resp) +} diff --git a/vendor/github.com/containerd/ttrpc/metadata.go b/vendor/github.com/containerd/ttrpc/metadata.go new file mode 100644 index 0000000000000..ce8c0d13c41b5 --- /dev/null +++ b/vendor/github.com/containerd/ttrpc/metadata.go @@ -0,0 +1,107 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package ttrpc + +import ( + "context" + "strings" +) + +// MD is the user type for ttrpc metadata +type MD map[string][]string + +// Get returns the metadata for a given key when they exist. +// If there is no metadata, a nil slice and false are returned. +func (m MD) Get(key string) ([]string, bool) { + key = strings.ToLower(key) + list, ok := m[key] + if !ok || len(list) == 0 { + return nil, false + } + + return list, true +} + +// Set sets the provided values for a given key. +// The values will overwrite any existing values. +// If no values provided, a key will be deleted. +func (m MD) Set(key string, values ...string) { + key = strings.ToLower(key) + if len(values) == 0 { + delete(m, key) + return + } + m[key] = values +} + +// Append appends additional values to the given key. +func (m MD) Append(key string, values ...string) { + key = strings.ToLower(key) + if len(values) == 0 { + return + } + current, ok := m[key] + if ok { + m.Set(key, append(current, values...)...) + } else { + m.Set(key, values...) + } +} + +func (m MD) setRequest(r *Request) { + for k, values := range m { + for _, v := range values { + r.Metadata = append(r.Metadata, &KeyValue{ + Key: k, + Value: v, + }) + } + } +} + +func (m MD) fromRequest(r *Request) { + for _, kv := range r.Metadata { + m[kv.Key] = append(m[kv.Key], kv.Value) + } +} + +type metadataKey struct{} + +// GetMetadata retrieves metadata from context.Context (previously attached with WithMetadata) +func GetMetadata(ctx context.Context) (MD, bool) { + metadata, ok := ctx.Value(metadataKey{}).(MD) + return metadata, ok +} + +// GetMetadataValue gets a specific metadata value by name from context.Context +func GetMetadataValue(ctx context.Context, name string) (string, bool) { + metadata, ok := GetMetadata(ctx) + if !ok { + return "", false + } + + if list, ok := metadata.Get(name); ok { + return list[0], true + } + + return "", false +} + +// WithMetadata attaches metadata map to a context.Context +func WithMetadata(ctx context.Context, md MD) context.Context { + return context.WithValue(ctx, metadataKey{}, md) +} diff --git a/vendor/github.com/containerd/ttrpc/server.go b/vendor/github.com/containerd/ttrpc/server.go index 263cb4583b1c5..b0e48073e4d9f 100644 --- a/vendor/github.com/containerd/ttrpc/server.go +++ b/vendor/github.com/containerd/ttrpc/server.go @@ -18,6 +18,7 @@ package ttrpc import ( "context" + "errors" "io" "math/rand" "net" @@ -25,7 +26,6 @@ import ( "sync/atomic" "time" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -53,10 +53,13 @@ func NewServer(opts ...ServerOpt) (*Server, error) { return nil, err } } + if config.interceptor == nil { + config.interceptor = defaultServerInterceptor + } return &Server{ config: config, - services: newServiceSet(), + services: newServiceSet(config.interceptor), done: make(chan struct{}), listeners: make(map[net.Listener]struct{}), connections: make(map[*serverConn]struct{}), @@ -206,6 +209,20 @@ func (s *Server) addConnection(c *serverConn) { s.connections[c] = struct{}{} } +func (s *Server) delConnection(c *serverConn) { + s.mu.Lock() + defer s.mu.Unlock() + + delete(s.connections, c) +} + +func (s *Server) countConnection() int { + s.mu.Lock() + defer s.mu.Unlock() + + return len(s.connections) +} + func (s *Server) closeIdleConns() bool { s.mu.Lock() defer s.mu.Unlock() @@ -310,6 +327,7 @@ func (c *serverConn) run(sctx context.Context) { defer c.conn.Close() defer cancel() defer close(done) + defer c.server.delConnection(c) go func(recvErr chan error) { defer close(recvErr) @@ -341,7 +359,7 @@ func (c *serverConn) run(sctx context.Context) { default: // proceed } - mh, p, err := ch.recv(ctx) + mh, p, err := ch.recv() if err != nil { status, ok := status.FromError(err) if !ok { @@ -414,6 +432,9 @@ func (c *serverConn) run(sctx context.Context) { case request := <-requests: active++ go func(id uint32) { + ctx, cancel := getRequestContext(ctx, request.req) + defer cancel() + p, status := c.server.services.call(ctx, request.req.Service, request.req.Method, request.req.Payload) resp := &Response{ Status: status.Proto(), @@ -435,7 +456,7 @@ func (c *serverConn) run(sctx context.Context) { return } - if err := ch.send(ctx, response.id, messageTypeResponse, p); err != nil { + if err := ch.send(response.id, messageTypeResponse, p); err != nil { logrus.WithError(err).Error("failed sending message on channel") return } @@ -446,7 +467,12 @@ func (c *serverConn) run(sctx context.Context) { // branch. Basically, it means that we are no longer receiving // requests due to a terminal error. recvErr = nil // connection is now "closing" - if err != nil && err != io.EOF { + if err == io.EOF || err == io.ErrUnexpectedEOF { + // The client went away and we should stop processing + // requests, so that the client connection is closed + return + } + if err != nil { logrus.WithError(err).Error("error receiving message") } case <-shutdown: @@ -454,3 +480,21 @@ func (c *serverConn) run(sctx context.Context) { } } } + +var noopFunc = func() {} + +func getRequestContext(ctx context.Context, req *Request) (retCtx context.Context, cancel func()) { + if len(req.Metadata) > 0 { + md := MD{} + md.fromRequest(req) + ctx = WithMetadata(ctx, md) + } + + cancel = noopFunc + if req.TimeoutNano == 0 { + return ctx, cancel + } + + ctx, cancel = context.WithTimeout(ctx, time.Duration(req.TimeoutNano)) + return ctx, cancel +} diff --git a/vendor/github.com/containerd/ttrpc/services.go b/vendor/github.com/containerd/ttrpc/services.go index e90963825912a..f359e9611f9e5 100644 --- a/vendor/github.com/containerd/ttrpc/services.go +++ b/vendor/github.com/containerd/ttrpc/services.go @@ -18,12 +18,14 @@ package ttrpc import ( "context" + "errors" + "fmt" "io" "os" "path" + "unsafe" "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -37,18 +39,20 @@ type ServiceDesc struct { } type serviceSet struct { - services map[string]ServiceDesc + services map[string]ServiceDesc + interceptor UnaryServerInterceptor } -func newServiceSet() *serviceSet { +func newServiceSet(interceptor UnaryServerInterceptor) *serviceSet { return &serviceSet{ - services: make(map[string]ServiceDesc), + services: make(map[string]ServiceDesc), + interceptor: interceptor, } } func (s *serviceSet) register(name string, methods map[string]Method) { if _, ok := s.services[name]; ok { - panic(errors.Errorf("duplicate service %v registered", name)) + panic(fmt.Errorf("duplicate service %v registered", name)) } s.services[name] = ServiceDesc{ @@ -76,7 +80,7 @@ func (s *serviceSet) dispatch(ctx context.Context, serviceName, methodName strin switch v := obj.(type) { case proto.Message: if err := proto.Unmarshal(p, v); err != nil { - return status.Errorf(codes.Internal, "ttrpc: error unmarshaling payload: %v", err.Error()) + return status.Errorf(codes.Internal, "ttrpc: error unmarshalling payload: %v", err.Error()) } default: return status.Errorf(codes.Internal, "ttrpc: error unsupported request type: %T", v) @@ -84,11 +88,19 @@ func (s *serviceSet) dispatch(ctx context.Context, serviceName, methodName strin return nil } - resp, err := method(ctx, unmarshal) + info := &UnaryServerInfo{ + FullMethod: fullPath(serviceName, methodName), + } + + resp, err := s.interceptor(ctx, unmarshal, info, method) if err != nil { return nil, err } + if isNil(resp) { + return nil, errors.New("ttrpc: marshal called with nil") + } + switch v := resp.(type) { case proto.Message: r, err := proto.Marshal(v) @@ -105,12 +117,12 @@ func (s *serviceSet) dispatch(ctx context.Context, serviceName, methodName strin func (s *serviceSet) resolve(service, method string) (Method, error) { srv, ok := s.services[service] if !ok { - return nil, status.Errorf(codes.NotFound, "service %v", service) + return nil, status.Errorf(codes.Unimplemented, "service %v", service) } mthd, ok := srv.Methods[method] if !ok { - return nil, status.Errorf(codes.NotFound, "method %v", method) + return nil, status.Errorf(codes.Unimplemented, "method %v", method) } return mthd, nil @@ -146,5 +158,9 @@ func convertCode(err error) codes.Code { } func fullPath(service, method string) string { - return "/" + path.Join("/", service, method) + return "/" + path.Join(service, method) +} + +func isNil(resp interface{}) bool { + return (*[2]uintptr)(unsafe.Pointer(&resp))[1] == 0 } diff --git a/vendor/github.com/containerd/ttrpc/types.go b/vendor/github.com/containerd/ttrpc/types.go index 1f7969e5cf46d..9a1c19a7238de 100644 --- a/vendor/github.com/containerd/ttrpc/types.go +++ b/vendor/github.com/containerd/ttrpc/types.go @@ -23,9 +23,11 @@ import ( ) type Request struct { - Service string `protobuf:"bytes,1,opt,name=service,proto3"` - Method string `protobuf:"bytes,2,opt,name=method,proto3"` - Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3"` + Service string `protobuf:"bytes,1,opt,name=service,proto3"` + Method string `protobuf:"bytes,2,opt,name=method,proto3"` + Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3"` + TimeoutNano int64 `protobuf:"varint,4,opt,name=timeout_nano,proto3"` + Metadata []*KeyValue `protobuf:"bytes,5,rep,name=metadata,proto3"` } func (r *Request) Reset() { *r = Request{} } @@ -40,3 +42,22 @@ type Response struct { func (r *Response) Reset() { *r = Response{} } func (r *Response) String() string { return fmt.Sprintf("%+#v", r) } func (r *Response) ProtoMessage() {} + +type StringList struct { + List []string `protobuf:"bytes,1,rep,name=list,proto3"` +} + +func (r *StringList) Reset() { *r = StringList{} } +func (r *StringList) String() string { return fmt.Sprintf("%+#v", r) } +func (r *StringList) ProtoMessage() {} + +func makeStringList(item ...string) StringList { return StringList{List: item} } + +type KeyValue struct { + Key string `protobuf:"bytes,1,opt,name=key,proto3"` + Value string `protobuf:"bytes,2,opt,name=value,proto3"` +} + +func (m *KeyValue) Reset() { *m = KeyValue{} } +func (*KeyValue) ProtoMessage() {} +func (m *KeyValue) String() string { return fmt.Sprintf("%+#v", m) } diff --git a/vendor/github.com/containerd/ttrpc/unixcreds_linux.go b/vendor/github.com/containerd/ttrpc/unixcreds_linux.go index a471bd365ca95..a59dad60cd55e 100644 --- a/vendor/github.com/containerd/ttrpc/unixcreds_linux.go +++ b/vendor/github.com/containerd/ttrpc/unixcreds_linux.go @@ -18,11 +18,12 @@ package ttrpc import ( "context" + "errors" + "fmt" "net" "os" "syscall" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -31,12 +32,12 @@ type UnixCredentialsFunc func(*unix.Ucred) error func (fn UnixCredentialsFunc) Handshake(ctx context.Context, conn net.Conn) (net.Conn, interface{}, error) { uc, err := requireUnixSocket(conn) if err != nil { - return nil, nil, errors.Wrap(err, "ttrpc.UnixCredentialsFunc: require unix socket") + return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: require unix socket: %w", err) } rs, err := uc.SyscallConn() if err != nil { - return nil, nil, errors.Wrap(err, "ttrpc.UnixCredentialsFunc: (net.UnixConn).SyscallConn failed") + return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: (net.UnixConn).SyscallConn failed: %w", err) } var ( ucred *unix.Ucred @@ -45,15 +46,15 @@ func (fn UnixCredentialsFunc) Handshake(ctx context.Context, conn net.Conn) (net if err := rs.Control(func(fd uintptr) { ucred, ucredErr = unix.GetsockoptUcred(int(fd), unix.SOL_SOCKET, unix.SO_PEERCRED) }); err != nil { - return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: (*syscall.RawConn).Control failed") + return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: (*syscall.RawConn).Control failed: %w", err) } if ucredErr != nil { - return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: failed to retrieve socket peer credentials") + return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: failed to retrieve socket peer credentials: %w", err) } if err := fn(ucred); err != nil { - return nil, nil, errors.Wrapf(err, "ttrpc.UnixCredentialsFunc: credential check failed") + return nil, nil, fmt.Errorf("ttrpc.UnixCredentialsFunc: credential check failed: %w", err) } return uc, ucred, nil @@ -93,7 +94,7 @@ func requireRoot(ucred *unix.Ucred) error { func requireUidGid(ucred *unix.Ucred, uid, gid int) error { if (uid != -1 && uint32(uid) != ucred.Uid) || (gid != -1 && uint32(gid) != ucred.Gid) { - return errors.Wrap(syscall.EPERM, "ttrpc: invalid credentials") + return fmt.Errorf("ttrpc: invalid credentials: %v", syscall.EPERM) } return nil } diff --git a/vendor/github.com/containerd/typeurl/LICENSE b/vendor/github.com/containerd/typeurl/LICENSE index 261eeb9e9f8b2..584149b6ee28c 100644 --- a/vendor/github.com/containerd/typeurl/LICENSE +++ b/vendor/github.com/containerd/typeurl/LICENSE @@ -1,6 +1,7 @@ + Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -175,24 +176,13 @@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] + Copyright The containerd Authors 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 + https://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, diff --git a/vendor/github.com/containerd/typeurl/README.md b/vendor/github.com/containerd/typeurl/README.md index e0787743c5dad..d021e96724972 100644 --- a/vendor/github.com/containerd/typeurl/README.md +++ b/vendor/github.com/containerd/typeurl/README.md @@ -1,9 +1,20 @@ # typeurl -[![Build Status](https://travis-ci.org/containerd/typeurl.svg?branch=master)](https://travis-ci.org/containerd/typeurl) - +[![PkgGoDev](https://pkg.go.dev/badge/github.com/containerd/typeurl)](https://pkg.go.dev/github.com/containerd/typeurl) +[![Build Status](https://github.com/containerd/typeurl/workflows/CI/badge.svg)](https://github.com/containerd/typeurl/actions?query=workflow%3ACI) [![codecov](https://codecov.io/gh/containerd/typeurl/branch/master/graph/badge.svg)](https://codecov.io/gh/containerd/typeurl) +[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/typeurl)](https://goreportcard.com/report/github.com/containerd/typeurl) A Go package for managing the registration, marshaling, and unmarshaling of encoded types. -This package helps when types are sent over a GRPC API and marshaled as a [protobuf.Any](). +This package helps when types are sent over a GRPC API and marshaled as a [protobuf.Any](https://github.com/gogo/protobuf/blob/master/protobuf/google/protobuf/any.proto). + +## Project details + +**typeurl** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/typeurl/doc.go b/vendor/github.com/containerd/typeurl/doc.go new file mode 100644 index 0000000000000..c0d0fd2053339 --- /dev/null +++ b/vendor/github.com/containerd/typeurl/doc.go @@ -0,0 +1,83 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package typeurl + +// Package typeurl assists with managing the registration, marshaling, and +// unmarshaling of types encoded as protobuf.Any. +// +// A protobuf.Any is a proto message that can contain any arbitrary data. It +// consists of two components, a TypeUrl and a Value, and its proto definition +// looks like this: +// +// message Any { +// string type_url = 1; +// bytes value = 2; +// } +// +// The TypeUrl is used to distinguish the contents from other proto.Any +// messages. This typeurl library manages these URLs to enable automagic +// marshaling and unmarshaling of the contents. +// +// For example, consider this go struct: +// +// type Foo struct { +// Field1 string +// Field2 string +// } +// +// To use typeurl, types must first be registered. This is typically done in +// the init function +// +// func init() { +// typeurl.Register(&Foo{}, "Foo") +// } +// +// This will register the type Foo with the url path "Foo". The arguments to +// Register are variadic, and are used to construct a url path. Consider this +// example, from the github.com/containerd/containerd/client package: +// +// func init() { +// const prefix = "types.containerd.io" +// // register TypeUrls for commonly marshaled external types +// major := strconv.Itoa(specs.VersionMajor) +// typeurl.Register(&specs.Spec{}, prefix, "opencontainers/runtime-spec", major, "Spec") +// // this function has more Register calls, which are elided. +// } +// +// This registers several types under a more complex url, which ends up mapping +// to `types.containerd.io/opencontainers/runtime-spec/1/Spec` (or some other +// value for major). +// +// Once a type is registered, it can be marshaled to a proto.Any message simply +// by calling `MarshalAny`, like this: +// +// foo := &Foo{Field1: "value1", Field2: "value2"} +// anyFoo, err := typeurl.MarshalAny(foo) +// +// MarshalAny will resolve the correct URL for the type. If the type in +// question implements the proto.Message interface, then it will be marshaled +// as a proto message. Otherwise, it will be marshaled as json. This means that +// typeurl will work on any arbitrary data, whether or not it has a proto +// definition, as long as it can be serialized to json. +// +// To unmarshal, the process is simply inverse: +// +// iface, err := typeurl.UnmarshalAny(anyFoo) +// foo := iface.(*Foo) +// +// The correct type is automatically chosen from the type registry, and the +// returned interface can be cast straight to that type. diff --git a/vendor/github.com/containerd/typeurl/go.mod b/vendor/github.com/containerd/typeurl/go.mod new file mode 100644 index 0000000000000..77e171e57bcb4 --- /dev/null +++ b/vendor/github.com/containerd/typeurl/go.mod @@ -0,0 +1,8 @@ +module github.com/containerd/typeurl + +go 1.13 + +require ( + github.com/gogo/protobuf v1.3.2 + github.com/pkg/errors v0.9.1 +) diff --git a/vendor/github.com/containerd/typeurl/types.go b/vendor/github.com/containerd/typeurl/types.go index 153c488d0aa62..647d419a293d9 100644 --- a/vendor/github.com/containerd/typeurl/types.go +++ b/vendor/github.com/containerd/typeurl/types.go @@ -28,13 +28,25 @@ import ( ) var ( - mu sync.Mutex + mu sync.RWMutex registry = make(map[reflect.Type]string) ) -var ErrNotFound = errors.New("not found") +// Definitions of common error types used throughout typeurl. +// +// These error types are used with errors.Wrap and errors.Wrapf to add context +// to an error. +// +// To detect an error class, use errors.Is() functions to tell whether an +// error is of this type. +var ( + ErrNotFound = errors.New("not found") +) -// Register a type with the base url of the type +// Register a type with a base URL for JSON marshaling. When the MarshalAny and +// UnmarshalAny functions are called they will treat the Any type value as JSON. +// To use protocol buffers for handling the Any value the proto.Register +// function should be used instead of this function. func Register(v interface{}, args ...string) { var ( t = tryDereference(v) @@ -44,18 +56,18 @@ func Register(v interface{}, args ...string) { defer mu.Unlock() if et, ok := registry[t]; ok { if et != p { - panic(errors.Errorf("type registred with alternate path %q != %q", et, p)) + panic(errors.Errorf("type registered with alternate path %q != %q", et, p)) } return } registry[t] = p } -// TypeURL returns the type url for a registred type +// TypeURL returns the type url for a registered type. func TypeURL(v interface{}) (string, error) { - mu.Lock() + mu.RLock() u, ok := registry[tryDereference(v)] - mu.Unlock() + mu.RUnlock() if !ok { // fallback to the proto registry if it is a proto message pb, ok := v.(proto.Message) @@ -67,7 +79,7 @@ func TypeURL(v interface{}) (string, error) { return u, nil } -// Is returns true if the type of the Any is the same as v +// Is returns true if the type of the Any is the same as v. func Is(any *types.Any, v interface{}) bool { // call to check that v is a pointer tryDereference(v) @@ -78,7 +90,10 @@ func Is(any *types.Any, v interface{}) bool { return any.TypeUrl == url } -// MarshalAny marshals the value v into an any with the correct TypeUrl +// MarshalAny marshals the value v into an any with the correct TypeUrl. +// If the provided object is already a proto.Any message, then it will be +// returned verbatim. If it is of type proto.Message, it will be marshaled as a +// protocol buffer. Otherwise, the object will be marshaled to json. func MarshalAny(v interface{}) (*types.Any, error) { var marshal func(v interface{}) ([]byte, error) switch t := v.(type) { @@ -108,18 +123,56 @@ func MarshalAny(v interface{}) (*types.Any, error) { }, nil } -// UnmarshalAny unmarshals the any type into a concrete type +// UnmarshalAny unmarshals the any type into a concrete type. func UnmarshalAny(any *types.Any) (interface{}, error) { - t, err := getTypeByUrl(any.TypeUrl) + return UnmarshalByTypeURL(any.TypeUrl, any.Value) +} + +// UnmarshalByTypeURL unmarshals the given type and value to into a concrete type. +func UnmarshalByTypeURL(typeURL string, value []byte) (interface{}, error) { + return unmarshal(typeURL, value, nil) +} + +// UnmarshalTo unmarshals the any type into a concrete type passed in the out +// argument. It is identical to UnmarshalAny, but lets clients provide a +// destination type through the out argument. +func UnmarshalTo(any *types.Any, out interface{}) error { + return UnmarshalToByTypeURL(any.TypeUrl, any.Value, out) +} + +// UnmarshalTo unmarshals the given type and value into a concrete type passed +// in the out argument. It is identical to UnmarshalByTypeURL, but lets clients +// provide a destination type through the out argument. +func UnmarshalToByTypeURL(typeURL string, value []byte, out interface{}) error { + _, err := unmarshal(typeURL, value, out) + return err +} + +func unmarshal(typeURL string, value []byte, v interface{}) (interface{}, error) { + t, err := getTypeByUrl(typeURL) if err != nil { return nil, err } - v := reflect.New(t.t).Interface() + + if v == nil { + v = reflect.New(t.t).Interface() + } else { + // Validate interface type provided by client + vURL, err := TypeURL(v) + if err != nil { + return nil, err + } + if typeURL != vURL { + return nil, errors.Errorf("can't unmarshal type %q to output %q", typeURL, vURL) + } + } + if t.isProto { - err = proto.Unmarshal(any.Value, v.(proto.Message)) + err = proto.Unmarshal(value, v.(proto.Message)) } else { - err = json.Unmarshal(any.Value, v) + err = json.Unmarshal(value, v) } + return v, err } @@ -129,13 +182,16 @@ type urlType struct { } func getTypeByUrl(url string) (urlType, error) { + mu.RLock() for t, u := range registry { if u == url { + mu.RUnlock() return urlType{ t: t, }, nil } } + mu.RUnlock() // fallback to proto registry t := proto.MessageType(url) if t != nil { diff --git a/vendor/github.com/coreos/etcd/client/json.go b/vendor/github.com/coreos/etcd/client/json.go new file mode 100644 index 0000000000000..97cdbcd7cfa57 --- /dev/null +++ b/vendor/github.com/coreos/etcd/client/json.go @@ -0,0 +1,72 @@ +// Copyright 2019 The etcd Authors +// +// 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. + +package client + +import ( + "github.com/json-iterator/go" + "github.com/modern-go/reflect2" + "strconv" + "unsafe" +) + +type customNumberExtension struct { + jsoniter.DummyExtension +} + +func (cne *customNumberExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder { + if typ.String() == "interface {}" { + return customNumberDecoder{} + } + return nil +} + +type customNumberDecoder struct { +} + +func (customNumberDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { + switch iter.WhatIsNext() { + case jsoniter.NumberValue: + var number jsoniter.Number + iter.ReadVal(&number) + i64, err := strconv.ParseInt(string(number), 10, 64) + if err == nil { + *(*interface{})(ptr) = i64 + return + } + f64, err := strconv.ParseFloat(string(number), 64) + if err == nil { + *(*interface{})(ptr) = f64 + return + } + iter.ReportError("DecodeNumber", err.Error()) + default: + *(*interface{})(ptr) = iter.Read() + } +} + +// caseSensitiveJsonIterator returns a jsoniterator API that's configured to be +// case-sensitive when unmarshalling, and otherwise compatible with +// the encoding/json standard library. +func caseSensitiveJsonIterator() jsoniter.API { + config := jsoniter.Config{ + EscapeHTML: true, + SortMapKeys: true, + ValidateJsonRawMessage: true, + CaseSensitive: true, + }.Froze() + // Force jsoniter to decode number to interface{} via int64/float64, if possible. + config.RegisterExtension(&customNumberExtension{}) + return config +} diff --git a/vendor/github.com/coreos/etcd/client/keys.generated.go b/vendor/github.com/coreos/etcd/client/keys.generated.go deleted file mode 100644 index 237fdbe8ffd17..0000000000000 --- a/vendor/github.com/coreos/etcd/client/keys.generated.go +++ /dev/null @@ -1,5218 +0,0 @@ -// ************************************************************ -// DO NOT EDIT. -// THIS FILE IS AUTO-GENERATED BY codecgen. -// ************************************************************ - -package client - -import ( - "errors" - "fmt" - "reflect" - "runtime" - time "time" - - codec1978 "github.com/ugorji/go/codec" -) - -const ( - // ----- content types ---- - codecSelferC_UTF87612 = 1 - codecSelferC_RAW7612 = 0 - // ----- value types used ---- - codecSelferValueTypeArray7612 = 10 - codecSelferValueTypeMap7612 = 9 - // ----- containerStateValues ---- - codecSelfer_containerMapKey7612 = 2 - codecSelfer_containerMapValue7612 = 3 - codecSelfer_containerMapEnd7612 = 4 - codecSelfer_containerArrayElem7612 = 6 - codecSelfer_containerArrayEnd7612 = 7 -) - -var ( - codecSelferBitsize7612 = uint8(reflect.TypeOf(uint(0)).Bits()) - codecSelferOnlyMapOrArrayEncodeToStructErr7612 = errors.New(`only encoded map or array can be decoded into a struct`) -) - -type codecSelfer7612 struct{} - -func init() { - if codec1978.GenVersion != 8 { - _, file, _, _ := runtime.Caller(0) - err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v", - 8, codec1978.GenVersion, file) - panic(err) - } - if false { // reference the types, but skip this branch at build/run time - var v0 time.Duration - _ = v0 - } -} - -func (x *Error) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(4) - } else { - r.WriteMapStart(4) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeInt(int64(x.Code)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("errorCode")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeInt(int64(x.Code)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Message)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("message")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Message)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Cause)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("cause")) - r.WriteMapElemValue() - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Cause)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym13 := z.EncBinary() - _ = yym13 - if false { - } else { - r.EncodeUint(uint64(x.Index)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("index")) - r.WriteMapElemValue() - yym14 := z.EncBinary() - _ = yym14 - if false { - } else { - r.EncodeUint(uint64(x.Index)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *Error) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *Error) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "errorCode": - if r.TryDecodeAsNil() { - x.Code = 0 - } else { - yyv4 := &x.Code - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*int)(yyv4)) = int(r.DecodeInt(codecSelferBitsize7612)) - } - } - case "message": - if r.TryDecodeAsNil() { - x.Message = "" - } else { - yyv6 := &x.Message - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*string)(yyv6)) = r.DecodeString() - } - } - case "cause": - if r.TryDecodeAsNil() { - x.Cause = "" - } else { - yyv8 := &x.Cause - yym9 := z.DecBinary() - _ = yym9 - if false { - } else { - *((*string)(yyv8)) = r.DecodeString() - } - } - case "index": - if r.TryDecodeAsNil() { - x.Index = 0 - } else { - yyv10 := &x.Index - yym11 := z.DecBinary() - _ = yym11 - if false { - } else { - *((*uint64)(yyv10)) = uint64(r.DecodeUint(64)) - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *Error) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj12 int - var yyb12 bool - var yyhl12 bool = l >= 0 - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Code = 0 - } else { - yyv13 := &x.Code - yym14 := z.DecBinary() - _ = yym14 - if false { - } else { - *((*int)(yyv13)) = int(r.DecodeInt(codecSelferBitsize7612)) - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Message = "" - } else { - yyv15 := &x.Message - yym16 := z.DecBinary() - _ = yym16 - if false { - } else { - *((*string)(yyv15)) = r.DecodeString() - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Cause = "" - } else { - yyv17 := &x.Cause - yym18 := z.DecBinary() - _ = yym18 - if false { - } else { - *((*string)(yyv17)) = r.DecodeString() - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Index = 0 - } else { - yyv19 := &x.Index - yym20 := z.DecBinary() - _ = yym20 - if false { - } else { - *((*uint64)(yyv19)) = uint64(r.DecodeUint(64)) - } - } - for { - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj12-1, "") - } - r.ReadArrayEnd() -} - -func (x PrevExistType) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x)) - } -} - -func (x *PrevExistType) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - *((*string)(x)) = r.DecodeString() - } -} - -func (x *WatcherOptions) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(2) - } else { - r.WriteMapStart(2) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeUint(uint64(x.AfterIndex)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("AfterIndex")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeUint(uint64(x.AfterIndex)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Recursive")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *WatcherOptions) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *WatcherOptions) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "AfterIndex": - if r.TryDecodeAsNil() { - x.AfterIndex = 0 - } else { - yyv4 := &x.AfterIndex - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*uint64)(yyv4)) = uint64(r.DecodeUint(64)) - } - } - case "Recursive": - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv6 := &x.Recursive - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*bool)(yyv6)) = r.DecodeBool() - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *WatcherOptions) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj8 int - var yyb8 bool - var yyhl8 bool = l >= 0 - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.AfterIndex = 0 - } else { - yyv9 := &x.AfterIndex - yym10 := z.DecBinary() - _ = yym10 - if false { - } else { - *((*uint64)(yyv9)) = uint64(r.DecodeUint(64)) - } - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv11 := &x.Recursive - yym12 := z.DecBinary() - _ = yym12 - if false { - } else { - *((*bool)(yyv11)) = r.DecodeBool() - } - } - for { - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj8-1, "") - } - r.ReadArrayEnd() -} - -func (x *CreateInOrderOptions) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(1) - } else { - r.WriteMapStart(1) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else if z.HasExtensions() && z.EncExt(x.TTL) { - } else { - r.EncodeInt(int64(x.TTL)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("TTL")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else if z.HasExtensions() && z.EncExt(x.TTL) { - } else { - r.EncodeInt(int64(x.TTL)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *CreateInOrderOptions) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *CreateInOrderOptions) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "TTL": - if r.TryDecodeAsNil() { - x.TTL = 0 - } else { - yyv4 := &x.TTL - yym5 := z.DecBinary() - _ = yym5 - if false { - } else if z.HasExtensions() && z.DecExt(yyv4) { - } else { - *((*int64)(yyv4)) = int64(r.DecodeInt(64)) - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *CreateInOrderOptions) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj6 int - var yyb6 bool - var yyhl6 bool = l >= 0 - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l - } else { - yyb6 = r.CheckBreak() - } - if yyb6 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.TTL = 0 - } else { - yyv7 := &x.TTL - yym8 := z.DecBinary() - _ = yym8 - if false { - } else if z.HasExtensions() && z.DecExt(yyv7) { - } else { - *((*int64)(yyv7)) = int64(r.DecodeInt(64)) - } - } - for { - yyj6++ - if yyhl6 { - yyb6 = yyj6 > l - } else { - yyb6 = r.CheckBreak() - } - if yyb6 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj6-1, "") - } - r.ReadArrayEnd() -} - -func (x *SetOptions) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(7) - } else { - r.WriteMapStart(7) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.PrevValue)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("PrevValue")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.PrevValue)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeUint(uint64(x.PrevIndex)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("PrevIndex")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeUint(uint64(x.PrevIndex)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - x.PrevExist.CodecEncodeSelf(e) - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("PrevExist")) - r.WriteMapElemValue() - x.PrevExist.CodecEncodeSelf(e) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym13 := z.EncBinary() - _ = yym13 - if false { - } else if z.HasExtensions() && z.EncExt(x.TTL) { - } else { - r.EncodeInt(int64(x.TTL)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("TTL")) - r.WriteMapElemValue() - yym14 := z.EncBinary() - _ = yym14 - if false { - } else if z.HasExtensions() && z.EncExt(x.TTL) { - } else { - r.EncodeInt(int64(x.TTL)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym16 := z.EncBinary() - _ = yym16 - if false { - } else { - r.EncodeBool(bool(x.Refresh)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Refresh")) - r.WriteMapElemValue() - yym17 := z.EncBinary() - _ = yym17 - if false { - } else { - r.EncodeBool(bool(x.Refresh)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym19 := z.EncBinary() - _ = yym19 - if false { - } else { - r.EncodeBool(bool(x.Dir)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Dir")) - r.WriteMapElemValue() - yym20 := z.EncBinary() - _ = yym20 - if false { - } else { - r.EncodeBool(bool(x.Dir)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym22 := z.EncBinary() - _ = yym22 - if false { - } else { - r.EncodeBool(bool(x.NoValueOnSuccess)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("NoValueOnSuccess")) - r.WriteMapElemValue() - yym23 := z.EncBinary() - _ = yym23 - if false { - } else { - r.EncodeBool(bool(x.NoValueOnSuccess)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *SetOptions) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *SetOptions) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "PrevValue": - if r.TryDecodeAsNil() { - x.PrevValue = "" - } else { - yyv4 := &x.PrevValue - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*string)(yyv4)) = r.DecodeString() - } - } - case "PrevIndex": - if r.TryDecodeAsNil() { - x.PrevIndex = 0 - } else { - yyv6 := &x.PrevIndex - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*uint64)(yyv6)) = uint64(r.DecodeUint(64)) - } - } - case "PrevExist": - if r.TryDecodeAsNil() { - x.PrevExist = "" - } else { - yyv8 := &x.PrevExist - yyv8.CodecDecodeSelf(d) - } - case "TTL": - if r.TryDecodeAsNil() { - x.TTL = 0 - } else { - yyv9 := &x.TTL - yym10 := z.DecBinary() - _ = yym10 - if false { - } else if z.HasExtensions() && z.DecExt(yyv9) { - } else { - *((*int64)(yyv9)) = int64(r.DecodeInt(64)) - } - } - case "Refresh": - if r.TryDecodeAsNil() { - x.Refresh = false - } else { - yyv11 := &x.Refresh - yym12 := z.DecBinary() - _ = yym12 - if false { - } else { - *((*bool)(yyv11)) = r.DecodeBool() - } - } - case "Dir": - if r.TryDecodeAsNil() { - x.Dir = false - } else { - yyv13 := &x.Dir - yym14 := z.DecBinary() - _ = yym14 - if false { - } else { - *((*bool)(yyv13)) = r.DecodeBool() - } - } - case "NoValueOnSuccess": - if r.TryDecodeAsNil() { - x.NoValueOnSuccess = false - } else { - yyv15 := &x.NoValueOnSuccess - yym16 := z.DecBinary() - _ = yym16 - if false { - } else { - *((*bool)(yyv15)) = r.DecodeBool() - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *SetOptions) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj17 int - var yyb17 bool - var yyhl17 bool = l >= 0 - yyj17++ - if yyhl17 { - yyb17 = yyj17 > l - } else { - yyb17 = r.CheckBreak() - } - if yyb17 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.PrevValue = "" - } else { - yyv18 := &x.PrevValue - yym19 := z.DecBinary() - _ = yym19 - if false { - } else { - *((*string)(yyv18)) = r.DecodeString() - } - } - yyj17++ - if yyhl17 { - yyb17 = yyj17 > l - } else { - yyb17 = r.CheckBreak() - } - if yyb17 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.PrevIndex = 0 - } else { - yyv20 := &x.PrevIndex - yym21 := z.DecBinary() - _ = yym21 - if false { - } else { - *((*uint64)(yyv20)) = uint64(r.DecodeUint(64)) - } - } - yyj17++ - if yyhl17 { - yyb17 = yyj17 > l - } else { - yyb17 = r.CheckBreak() - } - if yyb17 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.PrevExist = "" - } else { - yyv22 := &x.PrevExist - yyv22.CodecDecodeSelf(d) - } - yyj17++ - if yyhl17 { - yyb17 = yyj17 > l - } else { - yyb17 = r.CheckBreak() - } - if yyb17 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.TTL = 0 - } else { - yyv23 := &x.TTL - yym24 := z.DecBinary() - _ = yym24 - if false { - } else if z.HasExtensions() && z.DecExt(yyv23) { - } else { - *((*int64)(yyv23)) = int64(r.DecodeInt(64)) - } - } - yyj17++ - if yyhl17 { - yyb17 = yyj17 > l - } else { - yyb17 = r.CheckBreak() - } - if yyb17 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Refresh = false - } else { - yyv25 := &x.Refresh - yym26 := z.DecBinary() - _ = yym26 - if false { - } else { - *((*bool)(yyv25)) = r.DecodeBool() - } - } - yyj17++ - if yyhl17 { - yyb17 = yyj17 > l - } else { - yyb17 = r.CheckBreak() - } - if yyb17 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Dir = false - } else { - yyv27 := &x.Dir - yym28 := z.DecBinary() - _ = yym28 - if false { - } else { - *((*bool)(yyv27)) = r.DecodeBool() - } - } - yyj17++ - if yyhl17 { - yyb17 = yyj17 > l - } else { - yyb17 = r.CheckBreak() - } - if yyb17 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.NoValueOnSuccess = false - } else { - yyv29 := &x.NoValueOnSuccess - yym30 := z.DecBinary() - _ = yym30 - if false { - } else { - *((*bool)(yyv29)) = r.DecodeBool() - } - } - for { - yyj17++ - if yyhl17 { - yyb17 = yyj17 > l - } else { - yyb17 = r.CheckBreak() - } - if yyb17 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj17-1, "") - } - r.ReadArrayEnd() -} - -func (x *GetOptions) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(3) - } else { - r.WriteMapStart(3) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Recursive")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeBool(bool(x.Sort)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Sort")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeBool(bool(x.Sort)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - r.EncodeBool(bool(x.Quorum)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Quorum")) - r.WriteMapElemValue() - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - r.EncodeBool(bool(x.Quorum)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *GetOptions) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *GetOptions) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "Recursive": - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv4 := &x.Recursive - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*bool)(yyv4)) = r.DecodeBool() - } - } - case "Sort": - if r.TryDecodeAsNil() { - x.Sort = false - } else { - yyv6 := &x.Sort - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*bool)(yyv6)) = r.DecodeBool() - } - } - case "Quorum": - if r.TryDecodeAsNil() { - x.Quorum = false - } else { - yyv8 := &x.Quorum - yym9 := z.DecBinary() - _ = yym9 - if false { - } else { - *((*bool)(yyv8)) = r.DecodeBool() - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *GetOptions) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj10 int - var yyb10 bool - var yyhl10 bool = l >= 0 - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv11 := &x.Recursive - yym12 := z.DecBinary() - _ = yym12 - if false { - } else { - *((*bool)(yyv11)) = r.DecodeBool() - } - } - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Sort = false - } else { - yyv13 := &x.Sort - yym14 := z.DecBinary() - _ = yym14 - if false { - } else { - *((*bool)(yyv13)) = r.DecodeBool() - } - } - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Quorum = false - } else { - yyv15 := &x.Quorum - yym16 := z.DecBinary() - _ = yym16 - if false { - } else { - *((*bool)(yyv15)) = r.DecodeBool() - } - } - for { - yyj10++ - if yyhl10 { - yyb10 = yyj10 > l - } else { - yyb10 = r.CheckBreak() - } - if yyb10 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj10-1, "") - } - r.ReadArrayEnd() -} - -func (x *DeleteOptions) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(4) - } else { - r.WriteMapStart(4) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.PrevValue)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("PrevValue")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.PrevValue)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeUint(uint64(x.PrevIndex)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("PrevIndex")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeUint(uint64(x.PrevIndex)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Recursive")) - r.WriteMapElemValue() - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym13 := z.EncBinary() - _ = yym13 - if false { - } else { - r.EncodeBool(bool(x.Dir)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Dir")) - r.WriteMapElemValue() - yym14 := z.EncBinary() - _ = yym14 - if false { - } else { - r.EncodeBool(bool(x.Dir)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *DeleteOptions) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *DeleteOptions) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "PrevValue": - if r.TryDecodeAsNil() { - x.PrevValue = "" - } else { - yyv4 := &x.PrevValue - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*string)(yyv4)) = r.DecodeString() - } - } - case "PrevIndex": - if r.TryDecodeAsNil() { - x.PrevIndex = 0 - } else { - yyv6 := &x.PrevIndex - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*uint64)(yyv6)) = uint64(r.DecodeUint(64)) - } - } - case "Recursive": - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv8 := &x.Recursive - yym9 := z.DecBinary() - _ = yym9 - if false { - } else { - *((*bool)(yyv8)) = r.DecodeBool() - } - } - case "Dir": - if r.TryDecodeAsNil() { - x.Dir = false - } else { - yyv10 := &x.Dir - yym11 := z.DecBinary() - _ = yym11 - if false { - } else { - *((*bool)(yyv10)) = r.DecodeBool() - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *DeleteOptions) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj12 int - var yyb12 bool - var yyhl12 bool = l >= 0 - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.PrevValue = "" - } else { - yyv13 := &x.PrevValue - yym14 := z.DecBinary() - _ = yym14 - if false { - } else { - *((*string)(yyv13)) = r.DecodeString() - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.PrevIndex = 0 - } else { - yyv15 := &x.PrevIndex - yym16 := z.DecBinary() - _ = yym16 - if false { - } else { - *((*uint64)(yyv15)) = uint64(r.DecodeUint(64)) - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv17 := &x.Recursive - yym18 := z.DecBinary() - _ = yym18 - if false { - } else { - *((*bool)(yyv17)) = r.DecodeBool() - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Dir = false - } else { - yyv19 := &x.Dir - yym20 := z.DecBinary() - _ = yym20 - if false { - } else { - *((*bool)(yyv19)) = r.DecodeBool() - } - } - for { - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj12-1, "") - } - r.ReadArrayEnd() -} - -func (x *Response) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(3) - } else { - r.WriteMapStart(3) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Action)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("action")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Action)) - } - } - var yyn6 bool - if x.Node == nil { - yyn6 = true - goto LABEL6 - } - LABEL6: - if yyr2 || yy2arr2 { - if yyn6 { - r.WriteArrayElem() - r.EncodeNil() - } else { - r.WriteArrayElem() - if x.Node == nil { - r.EncodeNil() - } else { - x.Node.CodecEncodeSelf(e) - } - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("node")) - r.WriteMapElemValue() - if yyn6 { - r.EncodeNil() - } else { - if x.Node == nil { - r.EncodeNil() - } else { - x.Node.CodecEncodeSelf(e) - } - } - } - var yyn9 bool - if x.PrevNode == nil { - yyn9 = true - goto LABEL9 - } - LABEL9: - if yyr2 || yy2arr2 { - if yyn9 { - r.WriteArrayElem() - r.EncodeNil() - } else { - r.WriteArrayElem() - if x.PrevNode == nil { - r.EncodeNil() - } else { - x.PrevNode.CodecEncodeSelf(e) - } - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("prevNode")) - r.WriteMapElemValue() - if yyn9 { - r.EncodeNil() - } else { - if x.PrevNode == nil { - r.EncodeNil() - } else { - x.PrevNode.CodecEncodeSelf(e) - } - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *Response) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *Response) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "action": - if r.TryDecodeAsNil() { - x.Action = "" - } else { - yyv4 := &x.Action - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*string)(yyv4)) = r.DecodeString() - } - } - case "node": - if x.Node == nil { - x.Node = new(Node) - } - if r.TryDecodeAsNil() { - if x.Node != nil { - x.Node = nil - } - } else { - if x.Node == nil { - x.Node = new(Node) - } - x.Node.CodecDecodeSelf(d) - } - case "prevNode": - if x.PrevNode == nil { - x.PrevNode = new(Node) - } - if r.TryDecodeAsNil() { - if x.PrevNode != nil { - x.PrevNode = nil - } - } else { - if x.PrevNode == nil { - x.PrevNode = new(Node) - } - x.PrevNode.CodecDecodeSelf(d) - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *Response) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj8 int - var yyb8 bool - var yyhl8 bool = l >= 0 - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Action = "" - } else { - yyv9 := &x.Action - yym10 := z.DecBinary() - _ = yym10 - if false { - } else { - *((*string)(yyv9)) = r.DecodeString() - } - } - if x.Node == nil { - x.Node = new(Node) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - if x.Node != nil { - x.Node = nil - } - } else { - if x.Node == nil { - x.Node = new(Node) - } - x.Node.CodecDecodeSelf(d) - } - if x.PrevNode == nil { - x.PrevNode = new(Node) - } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - if x.PrevNode != nil { - x.PrevNode = nil - } - } else { - if x.PrevNode == nil { - x.PrevNode = new(Node) - } - x.PrevNode.CodecDecodeSelf(d) - } - for { - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l - } else { - yyb8 = r.CheckBreak() - } - if yyb8 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj8-1, "") - } - r.ReadArrayEnd() -} - -func (x *Node) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [8]bool - _ = yyq2 - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - yyq2[1] = x.Dir != false - yyq2[6] = x.Expiration != nil - yyq2[7] = x.TTL != 0 - if yyr2 || yy2arr2 { - r.WriteArrayStart(8) - } else { - var yynn2 = 5 - for _, b := range yyq2 { - if b { - yynn2++ - } - } - r.WriteMapStart(yynn2) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Key)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("key")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Key)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - if yyq2[1] { - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeBool(bool(x.Dir)) - } - } else { - r.EncodeBool(false) - } - } else { - if yyq2[1] { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("dir")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeBool(bool(x.Dir)) - } - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Value)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("value")) - r.WriteMapElemValue() - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Value)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - if x.Nodes == nil { - r.EncodeNil() - } else { - x.Nodes.CodecEncodeSelf(e) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("nodes")) - r.WriteMapElemValue() - if x.Nodes == nil { - r.EncodeNil() - } else { - x.Nodes.CodecEncodeSelf(e) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym16 := z.EncBinary() - _ = yym16 - if false { - } else { - r.EncodeUint(uint64(x.CreatedIndex)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("createdIndex")) - r.WriteMapElemValue() - yym17 := z.EncBinary() - _ = yym17 - if false { - } else { - r.EncodeUint(uint64(x.CreatedIndex)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym19 := z.EncBinary() - _ = yym19 - if false { - } else { - r.EncodeUint(uint64(x.ModifiedIndex)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("modifiedIndex")) - r.WriteMapElemValue() - yym20 := z.EncBinary() - _ = yym20 - if false { - } else { - r.EncodeUint(uint64(x.ModifiedIndex)) - } - } - var yyn21 bool - if x.Expiration == nil { - yyn21 = true - goto LABEL21 - } - LABEL21: - if yyr2 || yy2arr2 { - if yyn21 { - r.WriteArrayElem() - r.EncodeNil() - } else { - r.WriteArrayElem() - if yyq2[6] { - if x.Expiration == nil { - r.EncodeNil() - } else { - yym22 := z.EncBinary() - _ = yym22 - if false { - } else if yym23 := z.TimeRtidIfBinc(); yym23 != 0 { - r.EncodeBuiltin(yym23, x.Expiration) - } else if z.HasExtensions() && z.EncExt(x.Expiration) { - } else if yym22 { - z.EncBinaryMarshal(x.Expiration) - } else if !yym22 && z.IsJSONHandle() { - z.EncJSONMarshal(x.Expiration) - } else { - z.EncFallback(x.Expiration) - } - } - } else { - r.EncodeNil() - } - } - } else { - if yyq2[6] { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("expiration")) - r.WriteMapElemValue() - if yyn21 { - r.EncodeNil() - } else { - if x.Expiration == nil { - r.EncodeNil() - } else { - yym24 := z.EncBinary() - _ = yym24 - if false { - } else if yym25 := z.TimeRtidIfBinc(); yym25 != 0 { - r.EncodeBuiltin(yym25, x.Expiration) - } else if z.HasExtensions() && z.EncExt(x.Expiration) { - } else if yym24 { - z.EncBinaryMarshal(x.Expiration) - } else if !yym24 && z.IsJSONHandle() { - z.EncJSONMarshal(x.Expiration) - } else { - z.EncFallback(x.Expiration) - } - } - } - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - if yyq2[7] { - yym27 := z.EncBinary() - _ = yym27 - if false { - } else { - r.EncodeInt(int64(x.TTL)) - } - } else { - r.EncodeInt(0) - } - } else { - if yyq2[7] { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("ttl")) - r.WriteMapElemValue() - yym28 := z.EncBinary() - _ = yym28 - if false { - } else { - r.EncodeInt(int64(x.TTL)) - } - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *Node) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *Node) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "key": - if r.TryDecodeAsNil() { - x.Key = "" - } else { - yyv4 := &x.Key - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*string)(yyv4)) = r.DecodeString() - } - } - case "dir": - if r.TryDecodeAsNil() { - x.Dir = false - } else { - yyv6 := &x.Dir - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*bool)(yyv6)) = r.DecodeBool() - } - } - case "value": - if r.TryDecodeAsNil() { - x.Value = "" - } else { - yyv8 := &x.Value - yym9 := z.DecBinary() - _ = yym9 - if false { - } else { - *((*string)(yyv8)) = r.DecodeString() - } - } - case "nodes": - if r.TryDecodeAsNil() { - x.Nodes = nil - } else { - yyv10 := &x.Nodes - yyv10.CodecDecodeSelf(d) - } - case "createdIndex": - if r.TryDecodeAsNil() { - x.CreatedIndex = 0 - } else { - yyv11 := &x.CreatedIndex - yym12 := z.DecBinary() - _ = yym12 - if false { - } else { - *((*uint64)(yyv11)) = uint64(r.DecodeUint(64)) - } - } - case "modifiedIndex": - if r.TryDecodeAsNil() { - x.ModifiedIndex = 0 - } else { - yyv13 := &x.ModifiedIndex - yym14 := z.DecBinary() - _ = yym14 - if false { - } else { - *((*uint64)(yyv13)) = uint64(r.DecodeUint(64)) - } - } - case "expiration": - if x.Expiration == nil { - x.Expiration = new(time.Time) - } - if r.TryDecodeAsNil() { - if x.Expiration != nil { - x.Expiration = nil - } - } else { - if x.Expiration == nil { - x.Expiration = new(time.Time) - } - yym16 := z.DecBinary() - _ = yym16 - if false { - } else if yym17 := z.TimeRtidIfBinc(); yym17 != 0 { - r.DecodeBuiltin(yym17, x.Expiration) - } else if z.HasExtensions() && z.DecExt(x.Expiration) { - } else if yym16 { - z.DecBinaryUnmarshal(x.Expiration) - } else if !yym16 && z.IsJSONHandle() { - z.DecJSONUnmarshal(x.Expiration) - } else { - z.DecFallback(x.Expiration, false) - } - } - case "ttl": - if r.TryDecodeAsNil() { - x.TTL = 0 - } else { - yyv18 := &x.TTL - yym19 := z.DecBinary() - _ = yym19 - if false { - } else { - *((*int64)(yyv18)) = int64(r.DecodeInt(64)) - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *Node) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj20 int - var yyb20 bool - var yyhl20 bool = l >= 0 - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Key = "" - } else { - yyv21 := &x.Key - yym22 := z.DecBinary() - _ = yym22 - if false { - } else { - *((*string)(yyv21)) = r.DecodeString() - } - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Dir = false - } else { - yyv23 := &x.Dir - yym24 := z.DecBinary() - _ = yym24 - if false { - } else { - *((*bool)(yyv23)) = r.DecodeBool() - } - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Value = "" - } else { - yyv25 := &x.Value - yym26 := z.DecBinary() - _ = yym26 - if false { - } else { - *((*string)(yyv25)) = r.DecodeString() - } - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Nodes = nil - } else { - yyv27 := &x.Nodes - yyv27.CodecDecodeSelf(d) - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.CreatedIndex = 0 - } else { - yyv28 := &x.CreatedIndex - yym29 := z.DecBinary() - _ = yym29 - if false { - } else { - *((*uint64)(yyv28)) = uint64(r.DecodeUint(64)) - } - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.ModifiedIndex = 0 - } else { - yyv30 := &x.ModifiedIndex - yym31 := z.DecBinary() - _ = yym31 - if false { - } else { - *((*uint64)(yyv30)) = uint64(r.DecodeUint(64)) - } - } - if x.Expiration == nil { - x.Expiration = new(time.Time) - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - if x.Expiration != nil { - x.Expiration = nil - } - } else { - if x.Expiration == nil { - x.Expiration = new(time.Time) - } - yym33 := z.DecBinary() - _ = yym33 - if false { - } else if yym34 := z.TimeRtidIfBinc(); yym34 != 0 { - r.DecodeBuiltin(yym34, x.Expiration) - } else if z.HasExtensions() && z.DecExt(x.Expiration) { - } else if yym33 { - z.DecBinaryUnmarshal(x.Expiration) - } else if !yym33 && z.IsJSONHandle() { - z.DecJSONUnmarshal(x.Expiration) - } else { - z.DecFallback(x.Expiration, false) - } - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.TTL = 0 - } else { - yyv35 := &x.TTL - yym36 := z.DecBinary() - _ = yym36 - if false { - } else { - *((*int64)(yyv35)) = int64(r.DecodeInt(64)) - } - } - for { - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj20-1, "") - } - r.ReadArrayEnd() -} - -func (x Nodes) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - h.encNodes((Nodes)(x), e) - } - } -} - -func (x *Nodes) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - h.decNodes((*Nodes)(x), d) - } -} - -func (x *httpKeysAPI) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(0) - } else { - r.WriteMapStart(0) - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *httpKeysAPI) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *httpKeysAPI) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *httpKeysAPI) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj4 int - var yyb4 bool - var yyhl4 bool = l >= 0 - for { - yyj4++ - if yyhl4 { - yyb4 = yyj4 > l - } else { - yyb4 = r.CheckBreak() - } - if yyb4 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj4-1, "") - } - r.ReadArrayEnd() -} - -func (x *httpWatcher) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(0) - } else { - r.WriteMapStart(0) - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *httpWatcher) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *httpWatcher) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *httpWatcher) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj4 int - var yyb4 bool - var yyhl4 bool = l >= 0 - for { - yyj4++ - if yyhl4 { - yyb4 = yyj4 > l - } else { - yyb4 = r.CheckBreak() - } - if yyb4 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj4-1, "") - } - r.ReadArrayEnd() -} - -func (x *getAction) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(5) - } else { - r.WriteMapStart(5) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Prefix)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Prefix")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Prefix)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Key)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Key")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Key)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Recursive")) - r.WriteMapElemValue() - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym13 := z.EncBinary() - _ = yym13 - if false { - } else { - r.EncodeBool(bool(x.Sorted)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Sorted")) - r.WriteMapElemValue() - yym14 := z.EncBinary() - _ = yym14 - if false { - } else { - r.EncodeBool(bool(x.Sorted)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym16 := z.EncBinary() - _ = yym16 - if false { - } else { - r.EncodeBool(bool(x.Quorum)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Quorum")) - r.WriteMapElemValue() - yym17 := z.EncBinary() - _ = yym17 - if false { - } else { - r.EncodeBool(bool(x.Quorum)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *getAction) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *getAction) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "Prefix": - if r.TryDecodeAsNil() { - x.Prefix = "" - } else { - yyv4 := &x.Prefix - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*string)(yyv4)) = r.DecodeString() - } - } - case "Key": - if r.TryDecodeAsNil() { - x.Key = "" - } else { - yyv6 := &x.Key - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*string)(yyv6)) = r.DecodeString() - } - } - case "Recursive": - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv8 := &x.Recursive - yym9 := z.DecBinary() - _ = yym9 - if false { - } else { - *((*bool)(yyv8)) = r.DecodeBool() - } - } - case "Sorted": - if r.TryDecodeAsNil() { - x.Sorted = false - } else { - yyv10 := &x.Sorted - yym11 := z.DecBinary() - _ = yym11 - if false { - } else { - *((*bool)(yyv10)) = r.DecodeBool() - } - } - case "Quorum": - if r.TryDecodeAsNil() { - x.Quorum = false - } else { - yyv12 := &x.Quorum - yym13 := z.DecBinary() - _ = yym13 - if false { - } else { - *((*bool)(yyv12)) = r.DecodeBool() - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *getAction) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj14 int - var yyb14 bool - var yyhl14 bool = l >= 0 - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Prefix = "" - } else { - yyv15 := &x.Prefix - yym16 := z.DecBinary() - _ = yym16 - if false { - } else { - *((*string)(yyv15)) = r.DecodeString() - } - } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Key = "" - } else { - yyv17 := &x.Key - yym18 := z.DecBinary() - _ = yym18 - if false { - } else { - *((*string)(yyv17)) = r.DecodeString() - } - } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv19 := &x.Recursive - yym20 := z.DecBinary() - _ = yym20 - if false { - } else { - *((*bool)(yyv19)) = r.DecodeBool() - } - } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Sorted = false - } else { - yyv21 := &x.Sorted - yym22 := z.DecBinary() - _ = yym22 - if false { - } else { - *((*bool)(yyv21)) = r.DecodeBool() - } - } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Quorum = false - } else { - yyv23 := &x.Quorum - yym24 := z.DecBinary() - _ = yym24 - if false { - } else { - *((*bool)(yyv23)) = r.DecodeBool() - } - } - for { - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l - } else { - yyb14 = r.CheckBreak() - } - if yyb14 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj14-1, "") - } - r.ReadArrayEnd() -} - -func (x *waitAction) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(4) - } else { - r.WriteMapStart(4) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Prefix)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Prefix")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Prefix)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Key)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Key")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Key)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - r.EncodeUint(uint64(x.WaitIndex)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("WaitIndex")) - r.WriteMapElemValue() - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - r.EncodeUint(uint64(x.WaitIndex)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym13 := z.EncBinary() - _ = yym13 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Recursive")) - r.WriteMapElemValue() - yym14 := z.EncBinary() - _ = yym14 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *waitAction) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *waitAction) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "Prefix": - if r.TryDecodeAsNil() { - x.Prefix = "" - } else { - yyv4 := &x.Prefix - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*string)(yyv4)) = r.DecodeString() - } - } - case "Key": - if r.TryDecodeAsNil() { - x.Key = "" - } else { - yyv6 := &x.Key - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*string)(yyv6)) = r.DecodeString() - } - } - case "WaitIndex": - if r.TryDecodeAsNil() { - x.WaitIndex = 0 - } else { - yyv8 := &x.WaitIndex - yym9 := z.DecBinary() - _ = yym9 - if false { - } else { - *((*uint64)(yyv8)) = uint64(r.DecodeUint(64)) - } - } - case "Recursive": - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv10 := &x.Recursive - yym11 := z.DecBinary() - _ = yym11 - if false { - } else { - *((*bool)(yyv10)) = r.DecodeBool() - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *waitAction) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj12 int - var yyb12 bool - var yyhl12 bool = l >= 0 - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Prefix = "" - } else { - yyv13 := &x.Prefix - yym14 := z.DecBinary() - _ = yym14 - if false { - } else { - *((*string)(yyv13)) = r.DecodeString() - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Key = "" - } else { - yyv15 := &x.Key - yym16 := z.DecBinary() - _ = yym16 - if false { - } else { - *((*string)(yyv15)) = r.DecodeString() - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.WaitIndex = 0 - } else { - yyv17 := &x.WaitIndex - yym18 := z.DecBinary() - _ = yym18 - if false { - } else { - *((*uint64)(yyv17)) = uint64(r.DecodeUint(64)) - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv19 := &x.Recursive - yym20 := z.DecBinary() - _ = yym20 - if false { - } else { - *((*bool)(yyv19)) = r.DecodeBool() - } - } - for { - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj12-1, "") - } - r.ReadArrayEnd() -} - -func (x *setAction) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(10) - } else { - r.WriteMapStart(10) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Prefix)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Prefix")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Prefix)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Key)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Key")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Key)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Value)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Value")) - r.WriteMapElemValue() - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Value)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym13 := z.EncBinary() - _ = yym13 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.PrevValue)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("PrevValue")) - r.WriteMapElemValue() - yym14 := z.EncBinary() - _ = yym14 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.PrevValue)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym16 := z.EncBinary() - _ = yym16 - if false { - } else { - r.EncodeUint(uint64(x.PrevIndex)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("PrevIndex")) - r.WriteMapElemValue() - yym17 := z.EncBinary() - _ = yym17 - if false { - } else { - r.EncodeUint(uint64(x.PrevIndex)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - x.PrevExist.CodecEncodeSelf(e) - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("PrevExist")) - r.WriteMapElemValue() - x.PrevExist.CodecEncodeSelf(e) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym22 := z.EncBinary() - _ = yym22 - if false { - } else if z.HasExtensions() && z.EncExt(x.TTL) { - } else { - r.EncodeInt(int64(x.TTL)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("TTL")) - r.WriteMapElemValue() - yym23 := z.EncBinary() - _ = yym23 - if false { - } else if z.HasExtensions() && z.EncExt(x.TTL) { - } else { - r.EncodeInt(int64(x.TTL)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym25 := z.EncBinary() - _ = yym25 - if false { - } else { - r.EncodeBool(bool(x.Refresh)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Refresh")) - r.WriteMapElemValue() - yym26 := z.EncBinary() - _ = yym26 - if false { - } else { - r.EncodeBool(bool(x.Refresh)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym28 := z.EncBinary() - _ = yym28 - if false { - } else { - r.EncodeBool(bool(x.Dir)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Dir")) - r.WriteMapElemValue() - yym29 := z.EncBinary() - _ = yym29 - if false { - } else { - r.EncodeBool(bool(x.Dir)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym31 := z.EncBinary() - _ = yym31 - if false { - } else { - r.EncodeBool(bool(x.NoValueOnSuccess)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("NoValueOnSuccess")) - r.WriteMapElemValue() - yym32 := z.EncBinary() - _ = yym32 - if false { - } else { - r.EncodeBool(bool(x.NoValueOnSuccess)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *setAction) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *setAction) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "Prefix": - if r.TryDecodeAsNil() { - x.Prefix = "" - } else { - yyv4 := &x.Prefix - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*string)(yyv4)) = r.DecodeString() - } - } - case "Key": - if r.TryDecodeAsNil() { - x.Key = "" - } else { - yyv6 := &x.Key - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*string)(yyv6)) = r.DecodeString() - } - } - case "Value": - if r.TryDecodeAsNil() { - x.Value = "" - } else { - yyv8 := &x.Value - yym9 := z.DecBinary() - _ = yym9 - if false { - } else { - *((*string)(yyv8)) = r.DecodeString() - } - } - case "PrevValue": - if r.TryDecodeAsNil() { - x.PrevValue = "" - } else { - yyv10 := &x.PrevValue - yym11 := z.DecBinary() - _ = yym11 - if false { - } else { - *((*string)(yyv10)) = r.DecodeString() - } - } - case "PrevIndex": - if r.TryDecodeAsNil() { - x.PrevIndex = 0 - } else { - yyv12 := &x.PrevIndex - yym13 := z.DecBinary() - _ = yym13 - if false { - } else { - *((*uint64)(yyv12)) = uint64(r.DecodeUint(64)) - } - } - case "PrevExist": - if r.TryDecodeAsNil() { - x.PrevExist = "" - } else { - yyv14 := &x.PrevExist - yyv14.CodecDecodeSelf(d) - } - case "TTL": - if r.TryDecodeAsNil() { - x.TTL = 0 - } else { - yyv15 := &x.TTL - yym16 := z.DecBinary() - _ = yym16 - if false { - } else if z.HasExtensions() && z.DecExt(yyv15) { - } else { - *((*int64)(yyv15)) = int64(r.DecodeInt(64)) - } - } - case "Refresh": - if r.TryDecodeAsNil() { - x.Refresh = false - } else { - yyv17 := &x.Refresh - yym18 := z.DecBinary() - _ = yym18 - if false { - } else { - *((*bool)(yyv17)) = r.DecodeBool() - } - } - case "Dir": - if r.TryDecodeAsNil() { - x.Dir = false - } else { - yyv19 := &x.Dir - yym20 := z.DecBinary() - _ = yym20 - if false { - } else { - *((*bool)(yyv19)) = r.DecodeBool() - } - } - case "NoValueOnSuccess": - if r.TryDecodeAsNil() { - x.NoValueOnSuccess = false - } else { - yyv21 := &x.NoValueOnSuccess - yym22 := z.DecBinary() - _ = yym22 - if false { - } else { - *((*bool)(yyv21)) = r.DecodeBool() - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *setAction) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj23 int - var yyb23 bool - var yyhl23 bool = l >= 0 - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Prefix = "" - } else { - yyv24 := &x.Prefix - yym25 := z.DecBinary() - _ = yym25 - if false { - } else { - *((*string)(yyv24)) = r.DecodeString() - } - } - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Key = "" - } else { - yyv26 := &x.Key - yym27 := z.DecBinary() - _ = yym27 - if false { - } else { - *((*string)(yyv26)) = r.DecodeString() - } - } - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Value = "" - } else { - yyv28 := &x.Value - yym29 := z.DecBinary() - _ = yym29 - if false { - } else { - *((*string)(yyv28)) = r.DecodeString() - } - } - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.PrevValue = "" - } else { - yyv30 := &x.PrevValue - yym31 := z.DecBinary() - _ = yym31 - if false { - } else { - *((*string)(yyv30)) = r.DecodeString() - } - } - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.PrevIndex = 0 - } else { - yyv32 := &x.PrevIndex - yym33 := z.DecBinary() - _ = yym33 - if false { - } else { - *((*uint64)(yyv32)) = uint64(r.DecodeUint(64)) - } - } - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.PrevExist = "" - } else { - yyv34 := &x.PrevExist - yyv34.CodecDecodeSelf(d) - } - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.TTL = 0 - } else { - yyv35 := &x.TTL - yym36 := z.DecBinary() - _ = yym36 - if false { - } else if z.HasExtensions() && z.DecExt(yyv35) { - } else { - *((*int64)(yyv35)) = int64(r.DecodeInt(64)) - } - } - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Refresh = false - } else { - yyv37 := &x.Refresh - yym38 := z.DecBinary() - _ = yym38 - if false { - } else { - *((*bool)(yyv37)) = r.DecodeBool() - } - } - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Dir = false - } else { - yyv39 := &x.Dir - yym40 := z.DecBinary() - _ = yym40 - if false { - } else { - *((*bool)(yyv39)) = r.DecodeBool() - } - } - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.NoValueOnSuccess = false - } else { - yyv41 := &x.NoValueOnSuccess - yym42 := z.DecBinary() - _ = yym42 - if false { - } else { - *((*bool)(yyv41)) = r.DecodeBool() - } - } - for { - yyj23++ - if yyhl23 { - yyb23 = yyj23 > l - } else { - yyb23 = r.CheckBreak() - } - if yyb23 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj23-1, "") - } - r.ReadArrayEnd() -} - -func (x *deleteAction) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(6) - } else { - r.WriteMapStart(6) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Prefix)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Prefix")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Prefix)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Key)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Key")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Key)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.PrevValue)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("PrevValue")) - r.WriteMapElemValue() - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.PrevValue)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym13 := z.EncBinary() - _ = yym13 - if false { - } else { - r.EncodeUint(uint64(x.PrevIndex)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("PrevIndex")) - r.WriteMapElemValue() - yym14 := z.EncBinary() - _ = yym14 - if false { - } else { - r.EncodeUint(uint64(x.PrevIndex)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym16 := z.EncBinary() - _ = yym16 - if false { - } else { - r.EncodeBool(bool(x.Dir)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Dir")) - r.WriteMapElemValue() - yym17 := z.EncBinary() - _ = yym17 - if false { - } else { - r.EncodeBool(bool(x.Dir)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym19 := z.EncBinary() - _ = yym19 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Recursive")) - r.WriteMapElemValue() - yym20 := z.EncBinary() - _ = yym20 - if false { - } else { - r.EncodeBool(bool(x.Recursive)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *deleteAction) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *deleteAction) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "Prefix": - if r.TryDecodeAsNil() { - x.Prefix = "" - } else { - yyv4 := &x.Prefix - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*string)(yyv4)) = r.DecodeString() - } - } - case "Key": - if r.TryDecodeAsNil() { - x.Key = "" - } else { - yyv6 := &x.Key - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*string)(yyv6)) = r.DecodeString() - } - } - case "PrevValue": - if r.TryDecodeAsNil() { - x.PrevValue = "" - } else { - yyv8 := &x.PrevValue - yym9 := z.DecBinary() - _ = yym9 - if false { - } else { - *((*string)(yyv8)) = r.DecodeString() - } - } - case "PrevIndex": - if r.TryDecodeAsNil() { - x.PrevIndex = 0 - } else { - yyv10 := &x.PrevIndex - yym11 := z.DecBinary() - _ = yym11 - if false { - } else { - *((*uint64)(yyv10)) = uint64(r.DecodeUint(64)) - } - } - case "Dir": - if r.TryDecodeAsNil() { - x.Dir = false - } else { - yyv12 := &x.Dir - yym13 := z.DecBinary() - _ = yym13 - if false { - } else { - *((*bool)(yyv12)) = r.DecodeBool() - } - } - case "Recursive": - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv14 := &x.Recursive - yym15 := z.DecBinary() - _ = yym15 - if false { - } else { - *((*bool)(yyv14)) = r.DecodeBool() - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *deleteAction) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj16 int - var yyb16 bool - var yyhl16 bool = l >= 0 - yyj16++ - if yyhl16 { - yyb16 = yyj16 > l - } else { - yyb16 = r.CheckBreak() - } - if yyb16 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Prefix = "" - } else { - yyv17 := &x.Prefix - yym18 := z.DecBinary() - _ = yym18 - if false { - } else { - *((*string)(yyv17)) = r.DecodeString() - } - } - yyj16++ - if yyhl16 { - yyb16 = yyj16 > l - } else { - yyb16 = r.CheckBreak() - } - if yyb16 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Key = "" - } else { - yyv19 := &x.Key - yym20 := z.DecBinary() - _ = yym20 - if false { - } else { - *((*string)(yyv19)) = r.DecodeString() - } - } - yyj16++ - if yyhl16 { - yyb16 = yyj16 > l - } else { - yyb16 = r.CheckBreak() - } - if yyb16 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.PrevValue = "" - } else { - yyv21 := &x.PrevValue - yym22 := z.DecBinary() - _ = yym22 - if false { - } else { - *((*string)(yyv21)) = r.DecodeString() - } - } - yyj16++ - if yyhl16 { - yyb16 = yyj16 > l - } else { - yyb16 = r.CheckBreak() - } - if yyb16 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.PrevIndex = 0 - } else { - yyv23 := &x.PrevIndex - yym24 := z.DecBinary() - _ = yym24 - if false { - } else { - *((*uint64)(yyv23)) = uint64(r.DecodeUint(64)) - } - } - yyj16++ - if yyhl16 { - yyb16 = yyj16 > l - } else { - yyb16 = r.CheckBreak() - } - if yyb16 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Dir = false - } else { - yyv25 := &x.Dir - yym26 := z.DecBinary() - _ = yym26 - if false { - } else { - *((*bool)(yyv25)) = r.DecodeBool() - } - } - yyj16++ - if yyhl16 { - yyb16 = yyj16 > l - } else { - yyb16 = r.CheckBreak() - } - if yyb16 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Recursive = false - } else { - yyv27 := &x.Recursive - yym28 := z.DecBinary() - _ = yym28 - if false { - } else { - *((*bool)(yyv27)) = r.DecodeBool() - } - } - for { - yyj16++ - if yyhl16 { - yyb16 = yyj16 > l - } else { - yyb16 = r.CheckBreak() - } - if yyb16 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj16-1, "") - } - r.ReadArrayEnd() -} - -func (x *createInOrderAction) CodecEncodeSelf(e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - if x == nil { - r.EncodeNil() - } else { - yym1 := z.EncBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.EncExt(x) { - } else { - yysep2 := !z.EncBinary() - yy2arr2 := z.EncBasicHandle().StructToArray - _, _ = yysep2, yy2arr2 - const yyr2 bool = false - if yyr2 || yy2arr2 { - r.WriteArrayStart(4) - } else { - r.WriteMapStart(4) - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym4 := z.EncBinary() - _ = yym4 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Prefix)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Prefix")) - r.WriteMapElemValue() - yym5 := z.EncBinary() - _ = yym5 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Prefix)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym7 := z.EncBinary() - _ = yym7 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Dir)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Dir")) - r.WriteMapElemValue() - yym8 := z.EncBinary() - _ = yym8 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Dir)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym10 := z.EncBinary() - _ = yym10 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Value)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("Value")) - r.WriteMapElemValue() - yym11 := z.EncBinary() - _ = yym11 - if false { - } else { - r.EncodeString(codecSelferC_UTF87612, string(x.Value)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayElem() - yym13 := z.EncBinary() - _ = yym13 - if false { - } else if z.HasExtensions() && z.EncExt(x.TTL) { - } else { - r.EncodeInt(int64(x.TTL)) - } - } else { - r.WriteMapElemKey() - r.EncodeString(codecSelferC_UTF87612, string("TTL")) - r.WriteMapElemValue() - yym14 := z.EncBinary() - _ = yym14 - if false { - } else if z.HasExtensions() && z.EncExt(x.TTL) { - } else { - r.EncodeInt(int64(x.TTL)) - } - } - if yyr2 || yy2arr2 { - r.WriteArrayEnd() - } else { - r.WriteMapEnd() - } - } - } -} - -func (x *createInOrderAction) CodecDecodeSelf(d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - yym1 := z.DecBinary() - _ = yym1 - if false { - } else if z.HasExtensions() && z.DecExt(x) { - } else { - yyct2 := r.ContainerType() - if yyct2 == codecSelferValueTypeMap7612 { - yyl2 := r.ReadMapStart() - if yyl2 == 0 { - r.ReadMapEnd() - } else { - x.codecDecodeSelfFromMap(yyl2, d) - } - } else if yyct2 == codecSelferValueTypeArray7612 { - yyl2 := r.ReadArrayStart() - if yyl2 == 0 { - r.ReadArrayEnd() - } else { - x.codecDecodeSelfFromArray(yyl2, d) - } - } else { - panic(codecSelferOnlyMapOrArrayEncodeToStructErr7612) - } - } -} - -func (x *createInOrderAction) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yys3Slc = z.DecScratchBuffer() // default slice to decode into - _ = yys3Slc - var yyhl3 bool = l >= 0 - for yyj3 := 0; ; yyj3++ { - if yyhl3 { - if yyj3 >= l { - break - } - } else { - if r.CheckBreak() { - break - } - } - r.ReadMapElemKey() - yys3Slc = r.DecodeStringAsBytes() - yys3 := string(yys3Slc) - r.ReadMapElemValue() - switch yys3 { - case "Prefix": - if r.TryDecodeAsNil() { - x.Prefix = "" - } else { - yyv4 := &x.Prefix - yym5 := z.DecBinary() - _ = yym5 - if false { - } else { - *((*string)(yyv4)) = r.DecodeString() - } - } - case "Dir": - if r.TryDecodeAsNil() { - x.Dir = "" - } else { - yyv6 := &x.Dir - yym7 := z.DecBinary() - _ = yym7 - if false { - } else { - *((*string)(yyv6)) = r.DecodeString() - } - } - case "Value": - if r.TryDecodeAsNil() { - x.Value = "" - } else { - yyv8 := &x.Value - yym9 := z.DecBinary() - _ = yym9 - if false { - } else { - *((*string)(yyv8)) = r.DecodeString() - } - } - case "TTL": - if r.TryDecodeAsNil() { - x.TTL = 0 - } else { - yyv10 := &x.TTL - yym11 := z.DecBinary() - _ = yym11 - if false { - } else if z.HasExtensions() && z.DecExt(yyv10) { - } else { - *((*int64)(yyv10)) = int64(r.DecodeInt(64)) - } - } - default: - z.DecStructFieldNotFound(-1, yys3) - } // end switch yys3 - } // end for yyj3 - r.ReadMapEnd() -} - -func (x *createInOrderAction) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - var yyj12 int - var yyb12 bool - var yyhl12 bool = l >= 0 - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Prefix = "" - } else { - yyv13 := &x.Prefix - yym14 := z.DecBinary() - _ = yym14 - if false { - } else { - *((*string)(yyv13)) = r.DecodeString() - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Dir = "" - } else { - yyv15 := &x.Dir - yym16 := z.DecBinary() - _ = yym16 - if false { - } else { - *((*string)(yyv15)) = r.DecodeString() - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.Value = "" - } else { - yyv17 := &x.Value - yym18 := z.DecBinary() - _ = yym18 - if false { - } else { - *((*string)(yyv17)) = r.DecodeString() - } - } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - r.ReadArrayEnd() - return - } - r.ReadArrayElem() - if r.TryDecodeAsNil() { - x.TTL = 0 - } else { - yyv19 := &x.TTL - yym20 := z.DecBinary() - _ = yym20 - if false { - } else if z.HasExtensions() && z.DecExt(yyv19) { - } else { - *((*int64)(yyv19)) = int64(r.DecodeInt(64)) - } - } - for { - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l - } else { - yyb12 = r.CheckBreak() - } - if yyb12 { - break - } - r.ReadArrayElem() - z.DecStructFieldNotFound(yyj12-1, "") - } - r.ReadArrayEnd() -} - -func (x codecSelfer7612) encNodes(v Nodes, e *codec1978.Encoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperEncoder(e) - _, _, _ = h, z, r - r.WriteArrayStart(len(v)) - for _, yyv1 := range v { - r.WriteArrayElem() - if yyv1 == nil { - r.EncodeNil() - } else { - yyv1.CodecEncodeSelf(e) - } - } - r.WriteArrayEnd() -} - -func (x codecSelfer7612) decNodes(v *Nodes, d *codec1978.Decoder) { - var h codecSelfer7612 - z, r := codec1978.GenHelperDecoder(d) - _, _, _ = h, z, r - - yyv1 := *v - yyh1, yyl1 := z.DecSliceHelperStart() - var yyc1 bool - _ = yyc1 - if yyl1 == 0 { - if yyv1 == nil { - yyv1 = []*Node{} - yyc1 = true - } else if len(yyv1) != 0 { - yyv1 = yyv1[:0] - yyc1 = true - } - } else { - yyhl1 := yyl1 > 0 - var yyrl1 int - _ = yyrl1 - if yyhl1 { - if yyl1 > cap(yyv1) { - yyrl1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 8) - if yyrl1 <= cap(yyv1) { - yyv1 = yyv1[:yyrl1] - } else { - yyv1 = make([]*Node, yyrl1) - } - yyc1 = true - } else if yyl1 != len(yyv1) { - yyv1 = yyv1[:yyl1] - yyc1 = true - } - } - var yyj1 int - // var yydn1 bool - for ; (yyhl1 && yyj1 < yyl1) || !(yyhl1 || r.CheckBreak()); yyj1++ { - if yyj1 == 0 && len(yyv1) == 0 { - if yyhl1 { - yyrl1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 8) - } else { - yyrl1 = 8 - } - yyv1 = make([]*Node, yyrl1) - yyc1 = true - } - yyh1.ElemContainerState(yyj1) - // yydn1 = r.TryDecodeAsNil() - - // if indefinite, etc, then expand the slice if necessary - var yydb1 bool - if yyj1 >= len(yyv1) { - yyv1 = append(yyv1, nil) - yyc1 = true - - } - if yydb1 { - z.DecSwallow() - } else { - if r.TryDecodeAsNil() { - if yyv1[yyj1] != nil { - *yyv1[yyj1] = Node{} - } - } else { - if yyv1[yyj1] == nil { - yyv1[yyj1] = new(Node) - } - yyw2 := yyv1[yyj1] - yyw2.CodecDecodeSelf(d) - } - - } - - } - if yyj1 < len(yyv1) { - yyv1 = yyv1[:yyj1] - yyc1 = true - } else if yyj1 == 0 && yyv1 == nil { - yyv1 = make([]*Node, 0) - yyc1 = true - } - } - yyh1.End() - if yyc1 { - *v = yyv1 - } - -} diff --git a/vendor/github.com/coreos/etcd/client/keys.go b/vendor/github.com/coreos/etcd/client/keys.go index 8b9fd3f87a496..f8f2c7b186c22 100644 --- a/vendor/github.com/coreos/etcd/client/keys.go +++ b/vendor/github.com/coreos/etcd/client/keys.go @@ -14,8 +14,6 @@ package client -//go:generate codecgen -d 1819 -r "Node|Response|Nodes" -o keys.generated.go keys.go - import ( "context" "encoding/json" @@ -28,7 +26,6 @@ import ( "time" "github.com/coreos/etcd/pkg/pathutil" - "github.com/ugorji/go/codec" ) const ( @@ -656,9 +653,11 @@ func unmarshalHTTPResponse(code int, header http.Header, body []byte) (res *Resp return res, err } +var jsonIterator = caseSensitiveJsonIterator() + func unmarshalSuccessfulKeysResponse(header http.Header, body []byte) (*Response, error) { var res Response - err := codec.NewDecoderBytes(body, new(codec.JsonHandle)).Decode(&res) + err := jsonIterator.Unmarshal(body, &res) if err != nil { return nil, ErrInvalidJSON } diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/dir_unix.go b/vendor/github.com/coreos/etcd/pkg/fileutil/dir_unix.go index 58a77dfc1a99d..4ce15dc6bcf1f 100644 --- a/vendor/github.com/coreos/etcd/pkg/fileutil/dir_unix.go +++ b/vendor/github.com/coreos/etcd/pkg/fileutil/dir_unix.go @@ -18,5 +18,10 @@ package fileutil import "os" +const ( + // PrivateDirMode grants owner to make/remove files inside the directory. + PrivateDirMode = 0700 +) + // OpenDir opens a directory for syncing. func OpenDir(path string) (*os.File, error) { return os.Open(path) } diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/dir_windows.go b/vendor/github.com/coreos/etcd/pkg/fileutil/dir_windows.go index c123395c0040e..a10a90583c798 100644 --- a/vendor/github.com/coreos/etcd/pkg/fileutil/dir_windows.go +++ b/vendor/github.com/coreos/etcd/pkg/fileutil/dir_windows.go @@ -21,6 +21,11 @@ import ( "syscall" ) +const ( + // PrivateDirMode grants owner to make/remove files inside the directory. + PrivateDirMode = 0777 +) + // OpenDir opens a directory in windows with write access for syncing. func OpenDir(path string) (*os.File, error) { fd, err := openDir(path) diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/fileutil.go b/vendor/github.com/coreos/etcd/pkg/fileutil/fileutil.go index fce5126c6956d..3c73916a1e1c6 100644 --- a/vendor/github.com/coreos/etcd/pkg/fileutil/fileutil.go +++ b/vendor/github.com/coreos/etcd/pkg/fileutil/fileutil.go @@ -29,8 +29,6 @@ import ( const ( // PrivateFileMode grants owner to read/write a file. PrivateFileMode = 0600 - // PrivateDirMode grants owner to make/remove files inside the directory. - PrivateDirMode = 0700 ) var ( @@ -65,14 +63,22 @@ func ReadDir(dirpath string) ([]string, error) { // TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory // does not exists. TouchDirAll also ensures the given directory is writable. func TouchDirAll(dir string) error { - // If path is already a directory, MkdirAll does nothing - // and returns nil. - err := os.MkdirAll(dir, PrivateDirMode) - if err != nil { - // if mkdirAll("a/text") and "text" is not - // a directory, this will return syscall.ENOTDIR - return err + // If path is already a directory, MkdirAll does nothing and returns nil, so, + // first check if dir exist with an expected permission mode. + if Exist(dir) { + err := CheckDirPermission(dir, PrivateDirMode) + if err != nil { + plog.Warningf("check file permission: %v", err) + } + } else { + err := os.MkdirAll(dir, PrivateDirMode) + if err != nil { + // if mkdirAll("a/text") and "text" is not + // a directory, this will return syscall.ENOTDIR + return err + } } + return IsDirWriteable(dir) } @@ -120,3 +126,22 @@ func ZeroToEnd(f *os.File) error { _, err = f.Seek(off, io.SeekStart) return err } + +// CheckDirPermission checks permission on an existing dir. +// Returns error if dir is empty or exist with a different permission than specified. +func CheckDirPermission(dir string, perm os.FileMode) error { + if !Exist(dir) { + return fmt.Errorf("directory %q empty, cannot check permission.", dir) + } + //check the existing permission on the directory + dirInfo, err := os.Stat(dir) + if err != nil { + return err + } + dirMode := dirInfo.Mode().Perm() + if dirMode != perm { + err = fmt.Errorf("directory %q exist, but the permission is %q. The recommended permission is %q to prevent possible unprivileged access to the data.", dir, dirInfo.Mode(), os.FileMode(PrivateDirMode)) + return err + } + return nil +} diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/purge.go b/vendor/github.com/coreos/etcd/pkg/fileutil/purge.go index 92fceab017f31..c97368069198f 100644 --- a/vendor/github.com/coreos/etcd/pkg/fileutil/purge.go +++ b/vendor/github.com/coreos/etcd/pkg/fileutil/purge.go @@ -23,13 +23,23 @@ import ( ) func PurgeFile(dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) <-chan error { - return purgeFile(dirname, suffix, max, interval, stop, nil) + return purgeFile(dirname, suffix, max, interval, stop, nil, nil) +} + +func PurgeFileWithDoneNotify(dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) (<-chan struct{}, <-chan error) { + doneC := make(chan struct{}) + errC := purgeFile(dirname, suffix, max, interval, stop, nil, doneC) + return doneC, errC } // purgeFile is the internal implementation for PurgeFile which can post purged files to purgec if non-nil. -func purgeFile(dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}, purgec chan<- string) <-chan error { +// if donec is non-nil, the function closes it to notify its exit. +func purgeFile(dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}, purgec chan<- string, donec chan<- struct{}) <-chan error { errC := make(chan error, 1) go func() { + if donec != nil { + defer close(donec) + } for { fnames, err := ReadDir(dirname) if err != nil { diff --git a/vendor/github.com/coreos/etcd/pkg/ioutil/pagewriter.go b/vendor/github.com/coreos/etcd/pkg/ioutil/pagewriter.go index 72de1593d3adf..cf9a8dc664dcd 100644 --- a/vendor/github.com/coreos/etcd/pkg/ioutil/pagewriter.go +++ b/vendor/github.com/coreos/etcd/pkg/ioutil/pagewriter.go @@ -95,12 +95,23 @@ func (pw *PageWriter) Write(p []byte) (n int, err error) { return n, werr } +// Flush flushes buffered data. func (pw *PageWriter) Flush() error { + _, err := pw.flush() + return err +} + +// FlushN flushes buffered data and returns the number of written bytes. +func (pw *PageWriter) FlushN() (int, error) { + return pw.flush() +} + +func (pw *PageWriter) flush() (int, error) { if pw.bufferedBytes == 0 { - return nil + return 0, nil } - _, err := pw.w.Write(pw.buf[:pw.bufferedBytes]) + n, err := pw.w.Write(pw.buf[:pw.bufferedBytes]) pw.pageOffset = (pw.pageOffset + pw.bufferedBytes) % pw.pageBytes pw.bufferedBytes = 0 - return err + return n, err } diff --git a/vendor/github.com/coreos/etcd/raft/logger.go b/vendor/github.com/coreos/etcd/raft/logger.go index 92e55b373e1d4..426a77d344548 100644 --- a/vendor/github.com/coreos/etcd/raft/logger.go +++ b/vendor/github.com/coreos/etcd/raft/logger.go @@ -114,7 +114,7 @@ func (l *DefaultLogger) Fatalf(format string, v ...interface{}) { } func (l *DefaultLogger) Panic(v ...interface{}) { - l.Logger.Panic(v) + l.Logger.Panic(v...) } func (l *DefaultLogger) Panicf(format string, v ...interface{}) { diff --git a/vendor/github.com/coreos/etcd/raft/raft.go b/vendor/github.com/coreos/etcd/raft/raft.go index b4c0f0248ca28..22ff138e9c495 100644 --- a/vendor/github.com/coreos/etcd/raft/raft.go +++ b/vendor/github.com/coreos/etcd/raft/raft.go @@ -663,6 +663,7 @@ func (r *raft) becomePreCandidate() { r.step = stepCandidate r.votes = make(map[uint64]bool) r.tick = r.tickElection + r.lead = None r.state = StatePreCandidate r.logger.Infof("%x became pre-candidate at term %d", r.id, r.Term) } diff --git a/vendor/github.com/coreos/etcd/raft/raftpb/raft.pb.go b/vendor/github.com/coreos/etcd/raft/raftpb/raft.pb.go index fd9ee3729ecb6..753bd84ac6217 100644 --- a/vendor/github.com/coreos/etcd/raft/raftpb/raft.pb.go +++ b/vendor/github.com/coreos/etcd/raft/raftpb/raft.pb.go @@ -1,33 +1,16 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: raft.proto -/* - Package raftpb is a generated protocol buffer package. - - It is generated from these files: - raft.proto - - It has these top-level messages: - Entry - SnapshotMetadata - Snapshot - Message - HardState - ConfState - ConfChange -*/ package raftpb import ( - "fmt" - - proto "github.com/golang/protobuf/proto" - + fmt "fmt" + io "io" math "math" + math_bits "math/bits" _ "github.com/gogo/protobuf/gogoproto" - - io "io" + proto "github.com/golang/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. @@ -52,6 +35,7 @@ var EntryType_name = map[int32]string{ 0: "EntryNormal", 1: "EntryConfChange", } + var EntryType_value = map[string]int32{ "EntryNormal": 0, "EntryConfChange": 1, @@ -62,9 +46,11 @@ func (x EntryType) Enum() *EntryType { *p = x return p } + func (x EntryType) String() string { return proto.EnumName(EntryType_name, int32(x)) } + func (x *EntryType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(EntryType_value, data, "EntryType") if err != nil { @@ -73,7 +59,10 @@ func (x *EntryType) UnmarshalJSON(data []byte) error { *x = EntryType(value) return nil } -func (EntryType) EnumDescriptor() ([]byte, []int) { return fileDescriptorRaft, []int{0} } + +func (EntryType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_b042552c306ae59b, []int{0} +} type MessageType int32 @@ -120,6 +109,7 @@ var MessageType_name = map[int32]string{ 17: "MsgPreVote", 18: "MsgPreVoteResp", } + var MessageType_value = map[string]int32{ "MsgHup": 0, "MsgBeat": 1, @@ -147,9 +137,11 @@ func (x MessageType) Enum() *MessageType { *p = x return p } + func (x MessageType) String() string { return proto.EnumName(MessageType_name, int32(x)) } + func (x *MessageType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(MessageType_value, data, "MessageType") if err != nil { @@ -158,7 +150,10 @@ func (x *MessageType) UnmarshalJSON(data []byte) error { *x = MessageType(value) return nil } -func (MessageType) EnumDescriptor() ([]byte, []int) { return fileDescriptorRaft, []int{1} } + +func (MessageType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_b042552c306ae59b, []int{1} +} type ConfChangeType int32 @@ -175,6 +170,7 @@ var ConfChangeType_name = map[int32]string{ 2: "ConfChangeUpdateNode", 3: "ConfChangeAddLearnerNode", } + var ConfChangeType_value = map[string]int32{ "ConfChangeAddNode": 0, "ConfChangeRemoveNode": 1, @@ -187,9 +183,11 @@ func (x ConfChangeType) Enum() *ConfChangeType { *p = x return p } + func (x ConfChangeType) String() string { return proto.EnumName(ConfChangeType_name, int32(x)) } + func (x *ConfChangeType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(ConfChangeType_value, data, "ConfChangeType") if err != nil { @@ -198,102 +196,318 @@ func (x *ConfChangeType) UnmarshalJSON(data []byte) error { *x = ConfChangeType(value) return nil } -func (ConfChangeType) EnumDescriptor() ([]byte, []int) { return fileDescriptorRaft, []int{2} } + +func (ConfChangeType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_b042552c306ae59b, []int{2} +} type Entry struct { - Term uint64 `protobuf:"varint,2,opt,name=Term" json:"Term"` - Index uint64 `protobuf:"varint,3,opt,name=Index" json:"Index"` - Type EntryType `protobuf:"varint,1,opt,name=Type,enum=raftpb.EntryType" json:"Type"` - Data []byte `protobuf:"bytes,4,opt,name=Data" json:"Data,omitempty"` - XXX_unrecognized []byte `json:"-"` + Term uint64 `protobuf:"varint,2,opt,name=Term" json:"Term"` + Index uint64 `protobuf:"varint,3,opt,name=Index" json:"Index"` + Type EntryType `protobuf:"varint,1,opt,name=Type,enum=raftpb.EntryType" json:"Type"` + Data []byte `protobuf:"bytes,4,opt,name=Data" json:"Data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *Entry) Reset() { *m = Entry{} } -func (m *Entry) String() string { return proto.CompactTextString(m) } -func (*Entry) ProtoMessage() {} -func (*Entry) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{0} } +func (m *Entry) Reset() { *m = Entry{} } +func (m *Entry) String() string { return proto.CompactTextString(m) } +func (*Entry) ProtoMessage() {} +func (*Entry) Descriptor() ([]byte, []int) { + return fileDescriptor_b042552c306ae59b, []int{0} +} +func (m *Entry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Entry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Entry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Entry) XXX_Merge(src proto.Message) { + xxx_messageInfo_Entry.Merge(m, src) +} +func (m *Entry) XXX_Size() int { + return m.Size() +} +func (m *Entry) XXX_DiscardUnknown() { + xxx_messageInfo_Entry.DiscardUnknown(m) +} + +var xxx_messageInfo_Entry proto.InternalMessageInfo type SnapshotMetadata struct { - ConfState ConfState `protobuf:"bytes,1,opt,name=conf_state,json=confState" json:"conf_state"` - Index uint64 `protobuf:"varint,2,opt,name=index" json:"index"` - Term uint64 `protobuf:"varint,3,opt,name=term" json:"term"` - XXX_unrecognized []byte `json:"-"` + ConfState ConfState `protobuf:"bytes,1,opt,name=conf_state,json=confState" json:"conf_state"` + Index uint64 `protobuf:"varint,2,opt,name=index" json:"index"` + Term uint64 `protobuf:"varint,3,opt,name=term" json:"term"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *SnapshotMetadata) Reset() { *m = SnapshotMetadata{} } -func (m *SnapshotMetadata) String() string { return proto.CompactTextString(m) } -func (*SnapshotMetadata) ProtoMessage() {} -func (*SnapshotMetadata) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{1} } +func (m *SnapshotMetadata) Reset() { *m = SnapshotMetadata{} } +func (m *SnapshotMetadata) String() string { return proto.CompactTextString(m) } +func (*SnapshotMetadata) ProtoMessage() {} +func (*SnapshotMetadata) Descriptor() ([]byte, []int) { + return fileDescriptor_b042552c306ae59b, []int{1} +} +func (m *SnapshotMetadata) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SnapshotMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SnapshotMetadata.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SnapshotMetadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_SnapshotMetadata.Merge(m, src) +} +func (m *SnapshotMetadata) XXX_Size() int { + return m.Size() +} +func (m *SnapshotMetadata) XXX_DiscardUnknown() { + xxx_messageInfo_SnapshotMetadata.DiscardUnknown(m) +} + +var xxx_messageInfo_SnapshotMetadata proto.InternalMessageInfo type Snapshot struct { - Data []byte `protobuf:"bytes,1,opt,name=data" json:"data,omitempty"` - Metadata SnapshotMetadata `protobuf:"bytes,2,opt,name=metadata" json:"metadata"` - XXX_unrecognized []byte `json:"-"` + Data []byte `protobuf:"bytes,1,opt,name=data" json:"data,omitempty"` + Metadata SnapshotMetadata `protobuf:"bytes,2,opt,name=metadata" json:"metadata"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *Snapshot) Reset() { *m = Snapshot{} } -func (m *Snapshot) String() string { return proto.CompactTextString(m) } -func (*Snapshot) ProtoMessage() {} -func (*Snapshot) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{2} } +func (m *Snapshot) Reset() { *m = Snapshot{} } +func (m *Snapshot) String() string { return proto.CompactTextString(m) } +func (*Snapshot) ProtoMessage() {} +func (*Snapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_b042552c306ae59b, []int{2} +} +func (m *Snapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Snapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Snapshot.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Snapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_Snapshot.Merge(m, src) +} +func (m *Snapshot) XXX_Size() int { + return m.Size() +} +func (m *Snapshot) XXX_DiscardUnknown() { + xxx_messageInfo_Snapshot.DiscardUnknown(m) +} + +var xxx_messageInfo_Snapshot proto.InternalMessageInfo type Message struct { - Type MessageType `protobuf:"varint,1,opt,name=type,enum=raftpb.MessageType" json:"type"` - To uint64 `protobuf:"varint,2,opt,name=to" json:"to"` - From uint64 `protobuf:"varint,3,opt,name=from" json:"from"` - Term uint64 `protobuf:"varint,4,opt,name=term" json:"term"` - LogTerm uint64 `protobuf:"varint,5,opt,name=logTerm" json:"logTerm"` - Index uint64 `protobuf:"varint,6,opt,name=index" json:"index"` - Entries []Entry `protobuf:"bytes,7,rep,name=entries" json:"entries"` - Commit uint64 `protobuf:"varint,8,opt,name=commit" json:"commit"` - Snapshot Snapshot `protobuf:"bytes,9,opt,name=snapshot" json:"snapshot"` - Reject bool `protobuf:"varint,10,opt,name=reject" json:"reject"` - RejectHint uint64 `protobuf:"varint,11,opt,name=rejectHint" json:"rejectHint"` - Context []byte `protobuf:"bytes,12,opt,name=context" json:"context,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *Message) Reset() { *m = Message{} } -func (m *Message) String() string { return proto.CompactTextString(m) } -func (*Message) ProtoMessage() {} -func (*Message) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{3} } + Type MessageType `protobuf:"varint,1,opt,name=type,enum=raftpb.MessageType" json:"type"` + To uint64 `protobuf:"varint,2,opt,name=to" json:"to"` + From uint64 `protobuf:"varint,3,opt,name=from" json:"from"` + Term uint64 `protobuf:"varint,4,opt,name=term" json:"term"` + LogTerm uint64 `protobuf:"varint,5,opt,name=logTerm" json:"logTerm"` + Index uint64 `protobuf:"varint,6,opt,name=index" json:"index"` + Entries []Entry `protobuf:"bytes,7,rep,name=entries" json:"entries"` + Commit uint64 `protobuf:"varint,8,opt,name=commit" json:"commit"` + Snapshot Snapshot `protobuf:"bytes,9,opt,name=snapshot" json:"snapshot"` + Reject bool `protobuf:"varint,10,opt,name=reject" json:"reject"` + RejectHint uint64 `protobuf:"varint,11,opt,name=rejectHint" json:"rejectHint"` + Context []byte `protobuf:"bytes,12,opt,name=context" json:"context,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Message) Reset() { *m = Message{} } +func (m *Message) String() string { return proto.CompactTextString(m) } +func (*Message) ProtoMessage() {} +func (*Message) Descriptor() ([]byte, []int) { + return fileDescriptor_b042552c306ae59b, []int{3} +} +func (m *Message) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Message.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Message) XXX_Merge(src proto.Message) { + xxx_messageInfo_Message.Merge(m, src) +} +func (m *Message) XXX_Size() int { + return m.Size() +} +func (m *Message) XXX_DiscardUnknown() { + xxx_messageInfo_Message.DiscardUnknown(m) +} + +var xxx_messageInfo_Message proto.InternalMessageInfo type HardState struct { - Term uint64 `protobuf:"varint,1,opt,name=term" json:"term"` - Vote uint64 `protobuf:"varint,2,opt,name=vote" json:"vote"` - Commit uint64 `protobuf:"varint,3,opt,name=commit" json:"commit"` - XXX_unrecognized []byte `json:"-"` + Term uint64 `protobuf:"varint,1,opt,name=term" json:"term"` + Vote uint64 `protobuf:"varint,2,opt,name=vote" json:"vote"` + Commit uint64 `protobuf:"varint,3,opt,name=commit" json:"commit"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *HardState) Reset() { *m = HardState{} } -func (m *HardState) String() string { return proto.CompactTextString(m) } -func (*HardState) ProtoMessage() {} -func (*HardState) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{4} } +func (m *HardState) Reset() { *m = HardState{} } +func (m *HardState) String() string { return proto.CompactTextString(m) } +func (*HardState) ProtoMessage() {} +func (*HardState) Descriptor() ([]byte, []int) { + return fileDescriptor_b042552c306ae59b, []int{4} +} +func (m *HardState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HardState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HardState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HardState) XXX_Merge(src proto.Message) { + xxx_messageInfo_HardState.Merge(m, src) +} +func (m *HardState) XXX_Size() int { + return m.Size() +} +func (m *HardState) XXX_DiscardUnknown() { + xxx_messageInfo_HardState.DiscardUnknown(m) +} + +var xxx_messageInfo_HardState proto.InternalMessageInfo type ConfState struct { - Nodes []uint64 `protobuf:"varint,1,rep,name=nodes" json:"nodes,omitempty"` - Learners []uint64 `protobuf:"varint,2,rep,name=learners" json:"learners,omitempty"` - XXX_unrecognized []byte `json:"-"` + Nodes []uint64 `protobuf:"varint,1,rep,name=nodes" json:"nodes,omitempty"` + Learners []uint64 `protobuf:"varint,2,rep,name=learners" json:"learners,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ConfState) Reset() { *m = ConfState{} } -func (m *ConfState) String() string { return proto.CompactTextString(m) } -func (*ConfState) ProtoMessage() {} -func (*ConfState) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{5} } +func (m *ConfState) Reset() { *m = ConfState{} } +func (m *ConfState) String() string { return proto.CompactTextString(m) } +func (*ConfState) ProtoMessage() {} +func (*ConfState) Descriptor() ([]byte, []int) { + return fileDescriptor_b042552c306ae59b, []int{5} +} +func (m *ConfState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConfState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConfState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConfState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConfState.Merge(m, src) +} +func (m *ConfState) XXX_Size() int { + return m.Size() +} +func (m *ConfState) XXX_DiscardUnknown() { + xxx_messageInfo_ConfState.DiscardUnknown(m) +} + +var xxx_messageInfo_ConfState proto.InternalMessageInfo type ConfChange struct { - ID uint64 `protobuf:"varint,1,opt,name=ID" json:"ID"` - Type ConfChangeType `protobuf:"varint,2,opt,name=Type,enum=raftpb.ConfChangeType" json:"Type"` - NodeID uint64 `protobuf:"varint,3,opt,name=NodeID" json:"NodeID"` - Context []byte `protobuf:"bytes,4,opt,name=Context" json:"Context,omitempty"` - XXX_unrecognized []byte `json:"-"` + ID uint64 `protobuf:"varint,1,opt,name=ID" json:"ID"` + Type ConfChangeType `protobuf:"varint,2,opt,name=Type,enum=raftpb.ConfChangeType" json:"Type"` + NodeID uint64 `protobuf:"varint,3,opt,name=NodeID" json:"NodeID"` + Context []byte `protobuf:"bytes,4,opt,name=Context" json:"Context,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ConfChange) Reset() { *m = ConfChange{} } +func (m *ConfChange) String() string { return proto.CompactTextString(m) } +func (*ConfChange) ProtoMessage() {} +func (*ConfChange) Descriptor() ([]byte, []int) { + return fileDescriptor_b042552c306ae59b, []int{6} +} +func (m *ConfChange) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConfChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConfChange.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConfChange) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConfChange.Merge(m, src) +} +func (m *ConfChange) XXX_Size() int { + return m.Size() +} +func (m *ConfChange) XXX_DiscardUnknown() { + xxx_messageInfo_ConfChange.DiscardUnknown(m) } -func (m *ConfChange) Reset() { *m = ConfChange{} } -func (m *ConfChange) String() string { return proto.CompactTextString(m) } -func (*ConfChange) ProtoMessage() {} -func (*ConfChange) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{6} } +var xxx_messageInfo_ConfChange proto.InternalMessageInfo func init() { + proto.RegisterEnum("raftpb.EntryType", EntryType_name, EntryType_value) + proto.RegisterEnum("raftpb.MessageType", MessageType_name, MessageType_value) + proto.RegisterEnum("raftpb.ConfChangeType", ConfChangeType_name, ConfChangeType_value) proto.RegisterType((*Entry)(nil), "raftpb.Entry") proto.RegisterType((*SnapshotMetadata)(nil), "raftpb.SnapshotMetadata") proto.RegisterType((*Snapshot)(nil), "raftpb.Snapshot") @@ -301,14 +515,69 @@ func init() { proto.RegisterType((*HardState)(nil), "raftpb.HardState") proto.RegisterType((*ConfState)(nil), "raftpb.ConfState") proto.RegisterType((*ConfChange)(nil), "raftpb.ConfChange") - proto.RegisterEnum("raftpb.EntryType", EntryType_name, EntryType_value) - proto.RegisterEnum("raftpb.MessageType", MessageType_name, MessageType_value) - proto.RegisterEnum("raftpb.ConfChangeType", ConfChangeType_name, ConfChangeType_value) } + +func init() { proto.RegisterFile("raft.proto", fileDescriptor_b042552c306ae59b) } + +var fileDescriptor_b042552c306ae59b = []byte{ + // 816 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x54, 0xcd, 0x6e, 0x23, 0x45, + 0x10, 0xf6, 0x8c, 0xc7, 0x7f, 0x35, 0x8e, 0xd3, 0xa9, 0x35, 0xa8, 0x15, 0x45, 0xc6, 0xb2, 0x38, + 0x58, 0x41, 0x1b, 0x20, 0x07, 0x0e, 0x48, 0x1c, 0x36, 0x09, 0x52, 0x22, 0xad, 0xa3, 0xc5, 0x9b, + 0xe5, 0x80, 0x84, 0x50, 0xc7, 0x53, 0x9e, 0x18, 0x32, 0xd3, 0xa3, 0x9e, 0xf6, 0xb2, 0xb9, 0x20, + 0x1e, 0x80, 0x07, 0xe0, 0xc2, 0xfb, 0xe4, 0xb8, 0x12, 0x77, 0xc4, 0x86, 0x17, 0x41, 0xdd, 0xd3, + 0x63, 0xcf, 0x24, 0xb7, 0xae, 0xaf, 0x6a, 0xbe, 0xfa, 0xbe, 0xea, 0xea, 0x01, 0x50, 0x62, 0xa9, + 0x8f, 0x32, 0x25, 0xb5, 0xc4, 0xb6, 0x39, 0x67, 0xd7, 0xfb, 0xc3, 0x58, 0xc6, 0xd2, 0x42, 0x9f, + 0x9b, 0x53, 0x91, 0x9d, 0xfc, 0x06, 0xad, 0x6f, 0x53, 0xad, 0xee, 0x90, 0x43, 0x70, 0x45, 0x2a, + 0xe1, 0xfe, 0xd8, 0x9b, 0x06, 0x27, 0xc1, 0xfd, 0x3f, 0x9f, 0x34, 0xe6, 0x16, 0xc1, 0x7d, 0x68, + 0x5d, 0xa4, 0x11, 0xbd, 0xe3, 0xcd, 0x4a, 0xaa, 0x80, 0xf0, 0x33, 0x08, 0xae, 0xee, 0x32, 0xe2, + 0xde, 0xd8, 0x9b, 0x0e, 0x8e, 0xf7, 0x8e, 0x8a, 0x5e, 0x47, 0x96, 0xd2, 0x24, 0x36, 0x44, 0x77, + 0x19, 0x21, 0x42, 0x70, 0x26, 0xb4, 0xe0, 0xc1, 0xd8, 0x9b, 0xf6, 0xe7, 0xf6, 0x3c, 0xf9, 0xdd, + 0x03, 0xf6, 0x3a, 0x15, 0x59, 0x7e, 0x23, 0xf5, 0x8c, 0xb4, 0x88, 0x84, 0x16, 0xf8, 0x15, 0xc0, + 0x42, 0xa6, 0xcb, 0x9f, 0x72, 0x2d, 0x74, 0xc1, 0x1d, 0x6e, 0xb9, 0x4f, 0x65, 0xba, 0x7c, 0x6d, + 0x12, 0x8e, 0xbb, 0xb7, 0x28, 0x01, 0xa3, 0x74, 0x65, 0x95, 0x56, 0x4d, 0x14, 0x90, 0xf1, 0xa7, + 0x8d, 0xbf, 0xaa, 0x09, 0x8b, 0x4c, 0x7e, 0x80, 0x6e, 0xa9, 0xc0, 0x48, 0x34, 0x0a, 0x6c, 0xcf, + 0xfe, 0xdc, 0x9e, 0xf1, 0x6b, 0xe8, 0x26, 0x4e, 0x99, 0x25, 0x0e, 0x8f, 0x79, 0xa9, 0xe5, 0xb1, + 0x72, 0xc7, 0xbb, 0xa9, 0x9f, 0xfc, 0xd5, 0x84, 0xce, 0x8c, 0xf2, 0x5c, 0xc4, 0x84, 0xcf, 0x21, + 0xd0, 0xdb, 0x59, 0x3d, 0x2b, 0x39, 0x5c, 0xba, 0x3a, 0x2d, 0x53, 0x86, 0x43, 0xf0, 0xb5, 0xac, + 0x39, 0xf1, 0xb5, 0x34, 0x36, 0x96, 0x4a, 0x3e, 0xb2, 0x61, 0x90, 0x8d, 0xc1, 0xe0, 0xb1, 0x41, + 0x1c, 0x41, 0xe7, 0x56, 0xc6, 0xf6, 0x76, 0x5b, 0x95, 0x64, 0x09, 0x6e, 0xc7, 0xd6, 0x7e, 0x3a, + 0xb6, 0xe7, 0xd0, 0xa1, 0x54, 0xab, 0x15, 0xe5, 0xbc, 0x33, 0x6e, 0x4e, 0xc3, 0xe3, 0x9d, 0xda, + 0x1d, 0x97, 0x54, 0xae, 0x06, 0x0f, 0xa0, 0xbd, 0x90, 0x49, 0xb2, 0xd2, 0xbc, 0x5b, 0xe1, 0x72, + 0x18, 0x1e, 0x43, 0x37, 0x77, 0x13, 0xe3, 0x3d, 0x3b, 0x49, 0xf6, 0x78, 0x92, 0xe5, 0x04, 0xcb, + 0x3a, 0xc3, 0xa8, 0xe8, 0x67, 0x5a, 0x68, 0x0e, 0x63, 0x6f, 0xda, 0x2d, 0x19, 0x0b, 0x0c, 0x3f, + 0x05, 0x28, 0x4e, 0xe7, 0xab, 0x54, 0xf3, 0xb0, 0xd2, 0xb3, 0x82, 0x23, 0x87, 0xce, 0x42, 0xa6, + 0x9a, 0xde, 0x69, 0xde, 0xb7, 0x17, 0x5b, 0x86, 0x93, 0x1f, 0xa1, 0x77, 0x2e, 0x54, 0x54, 0xac, + 0x4f, 0x39, 0x41, 0xef, 0xc9, 0x04, 0x39, 0x04, 0x6f, 0xa5, 0xa6, 0xfa, 0xe3, 0x30, 0x48, 0xc5, + 0x70, 0xf3, 0xa9, 0xe1, 0xc9, 0x37, 0xd0, 0xdb, 0xac, 0x2b, 0x0e, 0xa1, 0x95, 0xca, 0x88, 0x72, + 0xee, 0x8d, 0x9b, 0xd3, 0x60, 0x5e, 0x04, 0xb8, 0x0f, 0xdd, 0x5b, 0x12, 0x2a, 0x25, 0x95, 0x73, + 0xdf, 0x26, 0x36, 0xf1, 0xe4, 0x0f, 0x0f, 0xc0, 0x7c, 0x7f, 0x7a, 0x23, 0xd2, 0xd8, 0x6e, 0xc4, + 0xc5, 0x59, 0x4d, 0x9d, 0x7f, 0x71, 0x86, 0x5f, 0xb8, 0x27, 0xe8, 0xdb, 0xb5, 0xfa, 0xb8, 0xfa, + 0x4c, 0x8a, 0xef, 0x9e, 0xbc, 0xc3, 0x03, 0x68, 0x5f, 0xca, 0x88, 0x2e, 0xce, 0xea, 0x9a, 0x0b, + 0xcc, 0x0c, 0xeb, 0xd4, 0x0d, 0xab, 0x78, 0xa8, 0x65, 0x78, 0xf8, 0x25, 0xf4, 0x36, 0x0f, 0x1b, + 0x77, 0x21, 0xb4, 0xc1, 0xa5, 0x54, 0x89, 0xb8, 0x65, 0x0d, 0x7c, 0x06, 0xbb, 0x16, 0xd8, 0x36, + 0x66, 0xde, 0xe1, 0xdf, 0x3e, 0x84, 0x95, 0x05, 0x47, 0x80, 0xf6, 0x2c, 0x8f, 0xcf, 0xd7, 0x19, + 0x6b, 0x60, 0x08, 0x9d, 0x59, 0x1e, 0x9f, 0x90, 0xd0, 0xcc, 0x73, 0xc1, 0x2b, 0x25, 0x33, 0xe6, + 0xbb, 0xaa, 0x17, 0x59, 0xc6, 0x9a, 0x38, 0x00, 0x28, 0xce, 0x73, 0xca, 0x33, 0x16, 0xb8, 0xc2, + 0xef, 0xa5, 0x26, 0xd6, 0x32, 0x22, 0x5c, 0x60, 0xb3, 0x6d, 0x97, 0x35, 0xcb, 0xc4, 0x3a, 0xc8, + 0xa0, 0x6f, 0x9a, 0x91, 0x50, 0xfa, 0xda, 0x74, 0xe9, 0xe2, 0x10, 0x58, 0x15, 0xb1, 0x1f, 0xf5, + 0x10, 0x61, 0x30, 0xcb, 0xe3, 0x37, 0xa9, 0x22, 0xb1, 0xb8, 0x11, 0xd7, 0xb7, 0xc4, 0x00, 0xf7, + 0x60, 0xc7, 0x11, 0x99, 0xcb, 0x5b, 0xe7, 0x2c, 0x74, 0x65, 0xa7, 0x37, 0xb4, 0xf8, 0xe5, 0xbb, + 0xb5, 0x54, 0xeb, 0x84, 0xf5, 0xf1, 0x23, 0xd8, 0x9b, 0xe5, 0xf1, 0x95, 0x12, 0x69, 0xbe, 0x24, + 0xf5, 0x92, 0x44, 0x44, 0x8a, 0xed, 0xb8, 0xaf, 0xaf, 0x56, 0x09, 0xc9, 0xb5, 0xbe, 0x94, 0xbf, + 0xb2, 0x81, 0x13, 0x33, 0x27, 0x11, 0xd9, 0x3f, 0x27, 0xdb, 0x75, 0x62, 0x36, 0x88, 0x15, 0xc3, + 0x9c, 0xdf, 0x57, 0x8a, 0xac, 0xc5, 0x3d, 0xd7, 0xd5, 0xc5, 0xb6, 0x06, 0x0f, 0xef, 0x60, 0x50, + 0xbf, 0x5e, 0xa3, 0x63, 0x8b, 0xbc, 0x88, 0x22, 0x73, 0x97, 0xac, 0x81, 0x1c, 0x86, 0x5b, 0x78, + 0x4e, 0x89, 0x7c, 0x4b, 0x36, 0xe3, 0xd5, 0x33, 0x6f, 0xb2, 0x48, 0xe8, 0x22, 0xe3, 0xe3, 0x01, + 0xf0, 0x1a, 0xd5, 0xcb, 0x62, 0x1b, 0x6d, 0xb6, 0x79, 0xc2, 0xef, 0x3f, 0x8c, 0x1a, 0xef, 0x3f, + 0x8c, 0x1a, 0xf7, 0x0f, 0x23, 0xef, 0xfd, 0xc3, 0xc8, 0xfb, 0xf7, 0x61, 0xe4, 0xfd, 0xf9, 0xdf, + 0xa8, 0xf1, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x30, 0xe1, 0x02, 0x69, 0x74, 0x06, 0x00, 0x00, +} + func (m *Entry) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -316,35 +585,42 @@ func (m *Entry) Marshal() (dAtA []byte, err error) { } func (m *Entry) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Entry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0x8 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Type)) - dAtA[i] = 0x10 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Term)) - dAtA[i] = 0x18 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Index)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Data != nil { - dAtA[i] = 0x22 - i++ + i -= len(m.Data) + copy(dAtA[i:], m.Data) i = encodeVarintRaft(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i-- + dAtA[i] = 0x22 } - return i, nil + i = encodeVarintRaft(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x18 + i = encodeVarintRaft(dAtA, i, uint64(m.Term)) + i-- + dAtA[i] = 0x10 + i = encodeVarintRaft(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + return len(dAtA) - i, nil } func (m *SnapshotMetadata) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -352,34 +628,42 @@ func (m *SnapshotMetadata) Marshal() (dAtA []byte, err error) { } func (m *SnapshotMetadata) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SnapshotMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.ConfState.Size())) - n1, err := m.ConfState.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - i += n1 - dAtA[i] = 0x10 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Index)) - dAtA[i] = 0x18 - i++ i = encodeVarintRaft(dAtA, i, uint64(m.Term)) - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i-- + dAtA[i] = 0x18 + i = encodeVarintRaft(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x10 + { + size, err := m.ConfState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *Snapshot) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -387,34 +671,43 @@ func (m *Snapshot) Marshal() (dAtA []byte, err error) { } func (m *Snapshot) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Snapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Data != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintRaft(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - dAtA[i] = 0x12 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Metadata.Size())) - n2, err := m.Metadata.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n2 - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i-- + dAtA[i] = 0x12 + if m.Data != nil { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintRaft(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Message) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -422,78 +715,89 @@ func (m *Message) Marshal() (dAtA []byte, err error) { } func (m *Message) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Message) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0x8 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Type)) - dAtA[i] = 0x10 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.To)) - dAtA[i] = 0x18 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.From)) - dAtA[i] = 0x20 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Term)) - dAtA[i] = 0x28 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.LogTerm)) - dAtA[i] = 0x30 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Index)) - if len(m.Entries) > 0 { - for _, msg := range m.Entries { - dAtA[i] = 0x3a - i++ - i = encodeVarintRaft(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - dAtA[i] = 0x40 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Commit)) - dAtA[i] = 0x4a - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Snapshot.Size())) - n3, err := m.Snapshot.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Context != nil { + i -= len(m.Context) + copy(dAtA[i:], m.Context) + i = encodeVarintRaft(dAtA, i, uint64(len(m.Context))) + i-- + dAtA[i] = 0x62 } - i += n3 - dAtA[i] = 0x50 - i++ + i = encodeVarintRaft(dAtA, i, uint64(m.RejectHint)) + i-- + dAtA[i] = 0x58 + i-- if m.Reject { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ - dAtA[i] = 0x58 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.RejectHint)) - if m.Context != nil { - dAtA[i] = 0x62 - i++ - i = encodeVarintRaft(dAtA, i, uint64(len(m.Context))) - i += copy(dAtA[i:], m.Context) + i-- + dAtA[i] = 0x50 + { + size, err := m.Snapshot.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i-- + dAtA[i] = 0x4a + i = encodeVarintRaft(dAtA, i, uint64(m.Commit)) + i-- + dAtA[i] = 0x40 + if len(m.Entries) > 0 { + for iNdEx := len(m.Entries) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Entries[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } } - return i, nil + i = encodeVarintRaft(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x30 + i = encodeVarintRaft(dAtA, i, uint64(m.LogTerm)) + i-- + dAtA[i] = 0x28 + i = encodeVarintRaft(dAtA, i, uint64(m.Term)) + i-- + dAtA[i] = 0x20 + i = encodeVarintRaft(dAtA, i, uint64(m.From)) + i-- + dAtA[i] = 0x18 + i = encodeVarintRaft(dAtA, i, uint64(m.To)) + i-- + dAtA[i] = 0x10 + i = encodeVarintRaft(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + return len(dAtA) - i, nil } func (m *HardState) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -501,29 +805,35 @@ func (m *HardState) Marshal() (dAtA []byte, err error) { } func (m *HardState) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HardState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0x8 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Term)) - dAtA[i] = 0x10 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Vote)) - dAtA[i] = 0x18 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Commit)) if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - return i, nil + i = encodeVarintRaft(dAtA, i, uint64(m.Commit)) + i-- + dAtA[i] = 0x18 + i = encodeVarintRaft(dAtA, i, uint64(m.Vote)) + i-- + dAtA[i] = 0x10 + i = encodeVarintRaft(dAtA, i, uint64(m.Term)) + i-- + dAtA[i] = 0x8 + return len(dAtA) - i, nil } func (m *ConfState) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -531,34 +841,40 @@ func (m *ConfState) Marshal() (dAtA []byte, err error) { } func (m *ConfState) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConfState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Nodes) > 0 { - for _, num := range m.Nodes { - dAtA[i] = 0x8 - i++ - i = encodeVarintRaft(dAtA, i, uint64(num)) - } + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Learners) > 0 { - for _, num := range m.Learners { + for iNdEx := len(m.Learners) - 1; iNdEx >= 0; iNdEx-- { + i = encodeVarintRaft(dAtA, i, uint64(m.Learners[iNdEx])) + i-- dAtA[i] = 0x10 - i++ - i = encodeVarintRaft(dAtA, i, uint64(num)) } } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + if len(m.Nodes) > 0 { + for iNdEx := len(m.Nodes) - 1; iNdEx >= 0; iNdEx-- { + i = encodeVarintRaft(dAtA, i, uint64(m.Nodes[iNdEx])) + i-- + dAtA[i] = 0x8 + } } - return i, nil + return len(dAtA) - i, nil } func (m *ConfChange) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -566,41 +882,53 @@ func (m *ConfChange) Marshal() (dAtA []byte, err error) { } func (m *ConfChange) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConfChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0x8 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.ID)) - dAtA[i] = 0x10 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Type)) - dAtA[i] = 0x18 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.NodeID)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Context != nil { - dAtA[i] = 0x22 - i++ + i -= len(m.Context) + copy(dAtA[i:], m.Context) i = encodeVarintRaft(dAtA, i, uint64(len(m.Context))) - i += copy(dAtA[i:], m.Context) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i-- + dAtA[i] = 0x22 } - return i, nil + i = encodeVarintRaft(dAtA, i, uint64(m.NodeID)) + i-- + dAtA[i] = 0x18 + i = encodeVarintRaft(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x10 + i = encodeVarintRaft(dAtA, i, uint64(m.ID)) + i-- + dAtA[i] = 0x8 + return len(dAtA) - i, nil } func encodeVarintRaft(dAtA []byte, offset int, v uint64) int { + offset -= sovRaft(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Entry) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovRaft(uint64(m.Type)) @@ -617,6 +945,9 @@ func (m *Entry) Size() (n int) { } func (m *SnapshotMetadata) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.ConfState.Size() @@ -630,6 +961,9 @@ func (m *SnapshotMetadata) Size() (n int) { } func (m *Snapshot) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Data != nil { @@ -645,6 +979,9 @@ func (m *Snapshot) Size() (n int) { } func (m *Message) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovRaft(uint64(m.Type)) @@ -675,6 +1012,9 @@ func (m *Message) Size() (n int) { } func (m *HardState) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovRaft(uint64(m.Term)) @@ -687,6 +1027,9 @@ func (m *HardState) Size() (n int) { } func (m *ConfState) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Nodes) > 0 { @@ -706,6 +1049,9 @@ func (m *ConfState) Size() (n int) { } func (m *ConfChange) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovRaft(uint64(m.ID)) @@ -722,14 +1068,7 @@ func (m *ConfChange) Size() (n int) { } func sovRaft(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozRaft(x uint64) (n int) { return sovRaft(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -749,7 +1088,7 @@ func (m *Entry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -777,7 +1116,7 @@ func (m *Entry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Type |= (EntryType(b) & 0x7F) << shift + m.Type |= EntryType(b&0x7F) << shift if b < 0x80 { break } @@ -796,7 +1135,7 @@ func (m *Entry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Term |= (uint64(b) & 0x7F) << shift + m.Term |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -815,7 +1154,7 @@ func (m *Entry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Index |= (uint64(b) & 0x7F) << shift + m.Index |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -834,7 +1173,7 @@ func (m *Entry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -843,6 +1182,9 @@ func (m *Entry) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -860,6 +1202,9 @@ func (m *Entry) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthRaft } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRaft + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -888,7 +1233,7 @@ func (m *SnapshotMetadata) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -916,7 +1261,7 @@ func (m *SnapshotMetadata) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -925,6 +1270,9 @@ func (m *SnapshotMetadata) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -946,7 +1294,7 @@ func (m *SnapshotMetadata) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Index |= (uint64(b) & 0x7F) << shift + m.Index |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -965,7 +1313,7 @@ func (m *SnapshotMetadata) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Term |= (uint64(b) & 0x7F) << shift + m.Term |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -979,6 +1327,9 @@ func (m *SnapshotMetadata) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthRaft } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRaft + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -1007,7 +1358,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1035,7 +1386,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1044,6 +1395,9 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1066,7 +1420,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1075,6 +1429,9 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1091,6 +1448,9 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthRaft } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRaft + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -1119,7 +1479,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1147,7 +1507,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Type |= (MessageType(b) & 0x7F) << shift + m.Type |= MessageType(b&0x7F) << shift if b < 0x80 { break } @@ -1166,7 +1526,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.To |= (uint64(b) & 0x7F) << shift + m.To |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1185,7 +1545,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.From |= (uint64(b) & 0x7F) << shift + m.From |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1204,7 +1564,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Term |= (uint64(b) & 0x7F) << shift + m.Term |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1223,7 +1583,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.LogTerm |= (uint64(b) & 0x7F) << shift + m.LogTerm |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1242,7 +1602,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Index |= (uint64(b) & 0x7F) << shift + m.Index |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1261,7 +1621,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1270,6 +1630,9 @@ func (m *Message) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1292,7 +1655,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Commit |= (uint64(b) & 0x7F) << shift + m.Commit |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1311,7 +1674,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1320,6 +1683,9 @@ func (m *Message) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1341,7 +1707,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1361,7 +1727,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.RejectHint |= (uint64(b) & 0x7F) << shift + m.RejectHint |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1380,7 +1746,7 @@ func (m *Message) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1389,6 +1755,9 @@ func (m *Message) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1406,6 +1775,9 @@ func (m *Message) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthRaft } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRaft + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -1434,7 +1806,7 @@ func (m *HardState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1462,7 +1834,7 @@ func (m *HardState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Term |= (uint64(b) & 0x7F) << shift + m.Term |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1481,7 +1853,7 @@ func (m *HardState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Vote |= (uint64(b) & 0x7F) << shift + m.Vote |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1500,7 +1872,7 @@ func (m *HardState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Commit |= (uint64(b) & 0x7F) << shift + m.Commit |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1514,6 +1886,9 @@ func (m *HardState) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthRaft } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRaft + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -1542,7 +1917,7 @@ func (m *ConfState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1568,7 +1943,7 @@ func (m *ConfState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (uint64(b) & 0x7F) << shift + v |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1585,7 +1960,7 @@ func (m *ConfState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - packedLen |= (int(b) & 0x7F) << shift + packedLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1594,9 +1969,23 @@ func (m *ConfState) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.Nodes) == 0 { + m.Nodes = make([]uint64, 0, elementCount) + } for iNdEx < postIndex { var v uint64 for shift := uint(0); ; shift += 7 { @@ -1608,7 +1997,7 @@ func (m *ConfState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (uint64(b) & 0x7F) << shift + v |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1630,7 +2019,7 @@ func (m *ConfState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (uint64(b) & 0x7F) << shift + v |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1647,7 +2036,7 @@ func (m *ConfState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - packedLen |= (int(b) & 0x7F) << shift + packedLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1656,9 +2045,23 @@ func (m *ConfState) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.Learners) == 0 { + m.Learners = make([]uint64, 0, elementCount) + } for iNdEx < postIndex { var v uint64 for shift := uint(0); ; shift += 7 { @@ -1670,7 +2073,7 @@ func (m *ConfState) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (uint64(b) & 0x7F) << shift + v |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1689,6 +2092,9 @@ func (m *ConfState) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthRaft } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRaft + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -1717,7 +2123,7 @@ func (m *ConfChange) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1745,7 +2151,7 @@ func (m *ConfChange) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ID |= (uint64(b) & 0x7F) << shift + m.ID |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1764,7 +2170,7 @@ func (m *ConfChange) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Type |= (ConfChangeType(b) & 0x7F) << shift + m.Type |= ConfChangeType(b&0x7F) << shift if b < 0x80 { break } @@ -1783,7 +2189,7 @@ func (m *ConfChange) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.NodeID |= (uint64(b) & 0x7F) << shift + m.NodeID |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1802,7 +2208,7 @@ func (m *ConfChange) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1811,6 +2217,9 @@ func (m *ConfChange) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1828,6 +2237,9 @@ func (m *ConfChange) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthRaft } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRaft + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -1895,10 +2307,13 @@ func skipRaft(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthRaft } + iNdEx += length + if iNdEx < 0 { + return 0, ErrInvalidLengthRaft + } return iNdEx, nil case 3: for { @@ -1927,6 +2342,9 @@ func skipRaft(dAtA []byte) (n int, err error) { return 0, err } iNdEx = start + next + if iNdEx < 0 { + return 0, ErrInvalidLengthRaft + } } return iNdEx, nil case 4: @@ -1945,60 +2363,3 @@ var ( ErrInvalidLengthRaft = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowRaft = fmt.Errorf("proto: integer overflow") ) - -func init() { proto.RegisterFile("raft.proto", fileDescriptorRaft) } - -var fileDescriptorRaft = []byte{ - // 815 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x54, 0xcd, 0x6e, 0x23, 0x45, - 0x10, 0xf6, 0x8c, 0xc7, 0x7f, 0x35, 0x8e, 0xd3, 0xa9, 0x35, 0xa8, 0x15, 0x45, 0xc6, 0xb2, 0x38, - 0x58, 0x41, 0x1b, 0x20, 0x07, 0x0e, 0x48, 0x1c, 0x36, 0x09, 0x52, 0x22, 0xad, 0xa3, 0xc5, 0x9b, - 0xe5, 0x80, 0x84, 0x50, 0xc7, 0x53, 0x9e, 0x18, 0x32, 0xd3, 0xa3, 0x9e, 0xf6, 0xb2, 0xb9, 0x20, - 0x1e, 0x80, 0x07, 0xe0, 0xc2, 0xfb, 0xe4, 0xb8, 0x12, 0x77, 0xc4, 0x86, 0x17, 0x41, 0xdd, 0xd3, - 0x63, 0xcf, 0x24, 0xb7, 0xae, 0xef, 0xab, 0xae, 0xfa, 0xea, 0xeb, 0x9a, 0x01, 0x50, 0x62, 0xa9, - 0x8f, 0x32, 0x25, 0xb5, 0xc4, 0xb6, 0x39, 0x67, 0xd7, 0xfb, 0xc3, 0x58, 0xc6, 0xd2, 0x42, 0x9f, - 0x9b, 0x53, 0xc1, 0x4e, 0x7e, 0x83, 0xd6, 0xb7, 0xa9, 0x56, 0x77, 0xf8, 0x19, 0x04, 0x57, 0x77, - 0x19, 0x71, 0x6f, 0xec, 0x4d, 0x07, 0xc7, 0x7b, 0x47, 0xc5, 0xad, 0x23, 0x4b, 0x1a, 0xe2, 0x24, - 0xb8, 0xff, 0xe7, 0x93, 0xc6, 0xdc, 0x26, 0x21, 0x87, 0xe0, 0x8a, 0x54, 0xc2, 0xfd, 0xb1, 0x37, - 0x0d, 0x36, 0x0c, 0xa9, 0x04, 0xf7, 0xa1, 0x75, 0x91, 0x46, 0xf4, 0x8e, 0x37, 0x2b, 0x54, 0x01, - 0x21, 0x42, 0x70, 0x26, 0xb4, 0xe0, 0xc1, 0xd8, 0x9b, 0xf6, 0xe7, 0xf6, 0x3c, 0xf9, 0xdd, 0x03, - 0xf6, 0x3a, 0x15, 0x59, 0x7e, 0x23, 0xf5, 0x8c, 0xb4, 0x88, 0x84, 0x16, 0xf8, 0x15, 0xc0, 0x42, - 0xa6, 0xcb, 0x9f, 0x72, 0x2d, 0x74, 0xa1, 0x28, 0xdc, 0x2a, 0x3a, 0x95, 0xe9, 0xf2, 0xb5, 0x21, - 0x5c, 0xf1, 0xde, 0xa2, 0x04, 0x4c, 0xf3, 0x95, 0x6d, 0x5e, 0xd5, 0x55, 0x40, 0x46, 0xb2, 0x36, - 0x92, 0xab, 0xba, 0x2c, 0x32, 0xf9, 0x01, 0xba, 0xa5, 0x02, 0x23, 0xd1, 0x28, 0xb0, 0x3d, 0xfb, - 0x73, 0x7b, 0xc6, 0xaf, 0xa1, 0x9b, 0x38, 0x65, 0xb6, 0x70, 0x78, 0xcc, 0x4b, 0x2d, 0x8f, 0x95, - 0xbb, 0xba, 0x9b, 0xfc, 0xc9, 0x5f, 0x4d, 0xe8, 0xcc, 0x28, 0xcf, 0x45, 0x4c, 0xf8, 0x1c, 0x02, - 0xbd, 0x75, 0xf8, 0x59, 0x59, 0xc3, 0xd1, 0x55, 0x8f, 0x4d, 0x1a, 0x0e, 0xc1, 0xd7, 0xb2, 0x36, - 0x89, 0xaf, 0xa5, 0x19, 0x63, 0xa9, 0xe4, 0xa3, 0x31, 0x0c, 0xb2, 0x19, 0x30, 0x78, 0x3c, 0x20, - 0x8e, 0xa0, 0x73, 0x2b, 0x63, 0xfb, 0x60, 0xad, 0x0a, 0x59, 0x82, 0x5b, 0xdb, 0xda, 0x4f, 0x6d, - 0x7b, 0x0e, 0x1d, 0x4a, 0xb5, 0x5a, 0x51, 0xce, 0x3b, 0xe3, 0xe6, 0x34, 0x3c, 0xde, 0xa9, 0x6d, - 0x46, 0x59, 0xca, 0xe5, 0xe0, 0x01, 0xb4, 0x17, 0x32, 0x49, 0x56, 0x9a, 0x77, 0x2b, 0xb5, 0x1c, - 0x86, 0xc7, 0xd0, 0xcd, 0x9d, 0x63, 0xbc, 0x67, 0x9d, 0x64, 0x8f, 0x9d, 0x2c, 0x1d, 0x2c, 0xf3, - 0x4c, 0x45, 0x45, 0x3f, 0xd3, 0x42, 0x73, 0x18, 0x7b, 0xd3, 0x6e, 0x59, 0xb1, 0xc0, 0xf0, 0x53, - 0x80, 0xe2, 0x74, 0xbe, 0x4a, 0x35, 0x0f, 0x2b, 0x3d, 0x2b, 0x38, 0x72, 0xe8, 0x2c, 0x64, 0xaa, - 0xe9, 0x9d, 0xe6, 0x7d, 0xfb, 0xb0, 0x65, 0x38, 0xf9, 0x11, 0x7a, 0xe7, 0x42, 0x45, 0xc5, 0xfa, - 0x94, 0x0e, 0x7a, 0x4f, 0x1c, 0xe4, 0x10, 0xbc, 0x95, 0x9a, 0xea, 0xfb, 0x6e, 0x90, 0xca, 0xc0, - 0xcd, 0xa7, 0x03, 0x4f, 0xbe, 0x81, 0xde, 0x66, 0x5d, 0x71, 0x08, 0xad, 0x54, 0x46, 0x94, 0x73, - 0x6f, 0xdc, 0x9c, 0x06, 0xf3, 0x22, 0xc0, 0x7d, 0xe8, 0xde, 0x92, 0x50, 0x29, 0xa9, 0x9c, 0xfb, - 0x96, 0xd8, 0xc4, 0x93, 0x3f, 0x3c, 0x00, 0x73, 0xff, 0xf4, 0x46, 0xa4, 0xb1, 0xdd, 0x88, 0x8b, - 0xb3, 0x9a, 0x3a, 0xff, 0xe2, 0x0c, 0xbf, 0x70, 0x1f, 0xae, 0x6f, 0xd7, 0xea, 0xe3, 0xea, 0x67, - 0x52, 0xdc, 0x7b, 0xf2, 0xf5, 0x1e, 0x40, 0xfb, 0x52, 0x46, 0x74, 0x71, 0x56, 0xd7, 0x5c, 0x60, - 0xc6, 0xac, 0x53, 0x67, 0x56, 0xf1, 0xa1, 0x96, 0xe1, 0xe1, 0x97, 0xd0, 0xdb, 0xfc, 0x0e, 0x70, - 0x17, 0x42, 0x1b, 0x5c, 0x4a, 0x95, 0x88, 0x5b, 0xd6, 0xc0, 0x67, 0xb0, 0x6b, 0x81, 0x6d, 0x63, - 0xe6, 0x1d, 0xfe, 0xed, 0x43, 0x58, 0x59, 0x70, 0x04, 0x68, 0xcf, 0xf2, 0xf8, 0x7c, 0x9d, 0xb1, - 0x06, 0x86, 0xd0, 0x99, 0xe5, 0xf1, 0x09, 0x09, 0xcd, 0x3c, 0x17, 0xbc, 0x52, 0x32, 0x63, 0xbe, - 0xcb, 0x7a, 0x91, 0x65, 0xac, 0x89, 0x03, 0x80, 0xe2, 0x3c, 0xa7, 0x3c, 0x63, 0x81, 0x4b, 0xfc, - 0x5e, 0x6a, 0x62, 0x2d, 0x23, 0xc2, 0x05, 0x96, 0x6d, 0x3b, 0xd6, 0x2c, 0x13, 0xeb, 0x20, 0x83, - 0xbe, 0x69, 0x46, 0x42, 0xe9, 0x6b, 0xd3, 0xa5, 0x8b, 0x43, 0x60, 0x55, 0xc4, 0x5e, 0xea, 0x21, - 0xc2, 0x60, 0x96, 0xc7, 0x6f, 0x52, 0x45, 0x62, 0x71, 0x23, 0xae, 0x6f, 0x89, 0x01, 0xee, 0xc1, - 0x8e, 0x2b, 0x64, 0x1e, 0x6f, 0x9d, 0xb3, 0xd0, 0xa5, 0x9d, 0xde, 0xd0, 0xe2, 0x97, 0xef, 0xd6, - 0x52, 0xad, 0x13, 0xd6, 0xc7, 0x8f, 0x60, 0x6f, 0x96, 0xc7, 0x57, 0x4a, 0xa4, 0xf9, 0x92, 0xd4, - 0x4b, 0x12, 0x11, 0x29, 0xb6, 0xe3, 0x6e, 0x5f, 0xad, 0x12, 0x92, 0x6b, 0x7d, 0x29, 0x7f, 0x65, - 0x03, 0x27, 0x66, 0x4e, 0x22, 0xb2, 0x3f, 0x43, 0xb6, 0xeb, 0xc4, 0x6c, 0x10, 0x2b, 0x86, 0xb9, - 0x79, 0x5f, 0x29, 0xb2, 0x23, 0xee, 0xb9, 0xae, 0x2e, 0xb6, 0x39, 0x78, 0x78, 0x07, 0x83, 0xfa, - 0xf3, 0x1a, 0x1d, 0x5b, 0xe4, 0x45, 0x14, 0x99, 0xb7, 0x64, 0x0d, 0xe4, 0x30, 0xdc, 0xc2, 0x73, - 0x4a, 0xe4, 0x5b, 0xb2, 0x8c, 0x57, 0x67, 0xde, 0x64, 0x91, 0xd0, 0x05, 0xe3, 0xe3, 0x01, 0xf0, - 0x5a, 0xa9, 0x97, 0xc5, 0x36, 0x5a, 0xb6, 0x79, 0xc2, 0xef, 0x3f, 0x8c, 0x1a, 0xef, 0x3f, 0x8c, - 0x1a, 0xf7, 0x0f, 0x23, 0xef, 0xfd, 0xc3, 0xc8, 0xfb, 0xf7, 0x61, 0xe4, 0xfd, 0xf9, 0xdf, 0xa8, - 0xf1, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x86, 0x52, 0x5b, 0xe0, 0x74, 0x06, 0x00, 0x00, -} diff --git a/vendor/github.com/coreos/etcd/snap/db.go b/vendor/github.com/coreos/etcd/snap/db.go index 01d897ae86116..dcbd3bd6710ca 100644 --- a/vendor/github.com/coreos/etcd/snap/db.go +++ b/vendor/github.com/coreos/etcd/snap/db.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "os" "path/filepath" + "time" "github.com/coreos/etcd/pkg/fileutil" ) @@ -30,6 +31,8 @@ var ErrNoDBSnapshot = errors.New("snap: snapshot file doesn't exist") // SaveDBFrom saves snapshot of the database from the given reader. It // guarantees the save operation is atomic. func (s *Snapshotter) SaveDBFrom(r io.Reader, id uint64) (int64, error) { + start := time.Now() + f, err := ioutil.TempFile(s.dir, "tmp") if err != nil { return 0, err @@ -37,7 +40,9 @@ func (s *Snapshotter) SaveDBFrom(r io.Reader, id uint64) (int64, error) { var n int64 n, err = io.Copy(f, r) if err == nil { + fsyncStart := time.Now() err = fileutil.Fsync(f) + snapDBFsyncSec.Observe(time.Since(fsyncStart).Seconds()) } f.Close() if err != nil { @@ -57,6 +62,7 @@ func (s *Snapshotter) SaveDBFrom(r io.Reader, id uint64) (int64, error) { plog.Infof("saved database snapshot to disk [total bytes: %d]", n) + snapDBSaveSec.Observe(time.Since(start).Seconds()) return n, nil } diff --git a/vendor/github.com/coreos/etcd/snap/metrics.go b/vendor/github.com/coreos/etcd/snap/metrics.go index 433ef09d4ba81..0d3b7e63e5e85 100644 --- a/vendor/github.com/coreos/etcd/snap/metrics.go +++ b/vendor/github.com/coreos/etcd/snap/metrics.go @@ -33,9 +33,33 @@ var ( Help: "The marshalling cost distributions of save called by snapshot.", Buckets: prometheus.ExponentialBuckets(0.001, 2, 14), }) + + snapDBSaveSec = prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: "etcd", + Subsystem: "snap_db", + Name: "save_total_duration_seconds", + Help: "The total latency distributions of v3 snapshot save", + + // lowest bucket start of upper bound 0.1 sec (100 ms) with factor 2 + // highest bucket start of 0.1 sec * 2^9 == 51.2 sec + Buckets: prometheus.ExponentialBuckets(0.1, 2, 10), + }) + + snapDBFsyncSec = prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: "etcd", + Subsystem: "snap_db", + Name: "fsync_duration_seconds", + Help: "The latency distributions of fsyncing .snap.db file", + + // lowest bucket start of upper bound 0.001 sec (1 ms) with factor 2 + // highest bucket start of 0.001 sec * 2^13 == 8.192 sec + Buckets: prometheus.ExponentialBuckets(0.001, 2, 14), + }) ) func init() { prometheus.MustRegister(saveDurations) prometheus.MustRegister(marshallingDurations) + prometheus.MustRegister(snapDBSaveSec) + prometheus.MustRegister(snapDBFsyncSec) } diff --git a/vendor/github.com/coreos/etcd/snap/snappb/snap.pb.go b/vendor/github.com/coreos/etcd/snap/snappb/snap.pb.go index e72b577f5b8d7..46897b45e16c2 100644 --- a/vendor/github.com/coreos/etcd/snap/snappb/snap.pb.go +++ b/vendor/github.com/coreos/etcd/snap/snappb/snap.pb.go @@ -1,27 +1,16 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: snap.proto -/* - Package snappb is a generated protocol buffer package. - - It is generated from these files: - snap.proto - - It has these top-level messages: - Snapshot -*/ package snappb import ( - "fmt" - - proto "github.com/golang/protobuf/proto" - + fmt "fmt" + io "io" math "math" + math_bits "math/bits" _ "github.com/gogo/protobuf/gogoproto" - - io "io" + proto "github.com/golang/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. @@ -36,23 +25,68 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type Snapshot struct { - Crc uint32 `protobuf:"varint,1,opt,name=crc" json:"crc"` - Data []byte `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"` - XXX_unrecognized []byte `json:"-"` + Crc uint32 `protobuf:"varint,1,opt,name=crc" json:"crc"` + Data []byte `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Snapshot) Reset() { *m = Snapshot{} } +func (m *Snapshot) String() string { return proto.CompactTextString(m) } +func (*Snapshot) ProtoMessage() {} +func (*Snapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e3c045ebf84d00, []int{0} +} +func (m *Snapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Snapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Snapshot.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Snapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_Snapshot.Merge(m, src) +} +func (m *Snapshot) XXX_Size() int { + return m.Size() +} +func (m *Snapshot) XXX_DiscardUnknown() { + xxx_messageInfo_Snapshot.DiscardUnknown(m) } -func (m *Snapshot) Reset() { *m = Snapshot{} } -func (m *Snapshot) String() string { return proto.CompactTextString(m) } -func (*Snapshot) ProtoMessage() {} -func (*Snapshot) Descriptor() ([]byte, []int) { return fileDescriptorSnap, []int{0} } +var xxx_messageInfo_Snapshot proto.InternalMessageInfo func init() { proto.RegisterType((*Snapshot)(nil), "snappb.snapshot") } + +func init() { proto.RegisterFile("snap.proto", fileDescriptor_f2e3c045ebf84d00) } + +var fileDescriptor_f2e3c045ebf84d00 = []byte{ + // 126 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0xce, 0x4b, 0x2c, + 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x03, 0xb1, 0x0b, 0x92, 0xa4, 0x44, 0xd2, 0xf3, + 0xd3, 0xf3, 0xc1, 0x42, 0xfa, 0x20, 0x16, 0x44, 0x56, 0xc9, 0x8c, 0x8b, 0x03, 0x24, 0x5f, 0x9c, + 0x91, 0x5f, 0x22, 0x24, 0xc6, 0xc5, 0x9c, 0x5c, 0x94, 0x2c, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0xeb, + 0xc4, 0x72, 0xe2, 0x9e, 0x3c, 0x43, 0x10, 0x48, 0x40, 0x48, 0x88, 0x8b, 0x25, 0x25, 0xb1, 0x24, + 0x51, 0x82, 0x49, 0x81, 0x51, 0x83, 0x27, 0x08, 0xcc, 0x76, 0x12, 0x39, 0xf1, 0x50, 0x8e, 0xe1, + 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf1, 0x58, 0x8e, + 0x01, 0x10, 0x00, 0x00, 0xff, 0xff, 0xd8, 0x0f, 0x32, 0xb2, 0x78, 0x00, 0x00, 0x00, +} + func (m *Snapshot) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -60,35 +94,47 @@ func (m *Snapshot) Marshal() (dAtA []byte, err error) { } func (m *Snapshot) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Snapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0x8 - i++ - i = encodeVarintSnap(dAtA, i, uint64(m.Crc)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Data != nil { - dAtA[i] = 0x12 - i++ + i -= len(m.Data) + copy(dAtA[i:], m.Data) i = encodeVarintSnap(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i-- + dAtA[i] = 0x12 } - return i, nil + i = encodeVarintSnap(dAtA, i, uint64(m.Crc)) + i-- + dAtA[i] = 0x8 + return len(dAtA) - i, nil } func encodeVarintSnap(dAtA []byte, offset int, v uint64) int { + offset -= sovSnap(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Snapshot) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovSnap(uint64(m.Crc)) @@ -103,14 +149,7 @@ func (m *Snapshot) Size() (n int) { } func sovSnap(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozSnap(x uint64) (n int) { return sovSnap(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -130,7 +169,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -158,7 +197,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Crc |= (uint32(b) & 0x7F) << shift + m.Crc |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -177,7 +216,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -186,6 +225,9 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnap } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSnap + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -203,6 +245,9 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthSnap } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthSnap + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -270,10 +315,13 @@ func skipSnap(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthSnap } + iNdEx += length + if iNdEx < 0 { + return 0, ErrInvalidLengthSnap + } return iNdEx, nil case 3: for { @@ -302,6 +350,9 @@ func skipSnap(dAtA []byte) (n int, err error) { return 0, err } iNdEx = start + next + if iNdEx < 0 { + return 0, ErrInvalidLengthSnap + } } return iNdEx, nil case 4: @@ -320,17 +371,3 @@ var ( ErrInvalidLengthSnap = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowSnap = fmt.Errorf("proto: integer overflow") ) - -func init() { proto.RegisterFile("snap.proto", fileDescriptorSnap) } - -var fileDescriptorSnap = []byte{ - // 126 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0xce, 0x4b, 0x2c, - 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x03, 0xb1, 0x0b, 0x92, 0xa4, 0x44, 0xd2, 0xf3, - 0xd3, 0xf3, 0xc1, 0x42, 0xfa, 0x20, 0x16, 0x44, 0x56, 0xc9, 0x8c, 0x8b, 0x03, 0x24, 0x5f, 0x9c, - 0x91, 0x5f, 0x22, 0x24, 0xc6, 0xc5, 0x9c, 0x5c, 0x94, 0x2c, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0xeb, - 0xc4, 0x72, 0xe2, 0x9e, 0x3c, 0x43, 0x10, 0x48, 0x40, 0x48, 0x88, 0x8b, 0x25, 0x25, 0xb1, 0x24, - 0x51, 0x82, 0x49, 0x81, 0x51, 0x83, 0x27, 0x08, 0xcc, 0x76, 0x12, 0x39, 0xf1, 0x50, 0x8e, 0xe1, - 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf1, 0x58, 0x8e, - 0x01, 0x10, 0x00, 0x00, 0xff, 0xff, 0xd8, 0x0f, 0x32, 0xb2, 0x78, 0x00, 0x00, 0x00, -} diff --git a/vendor/github.com/coreos/etcd/snap/snapshotter.go b/vendor/github.com/coreos/etcd/snap/snapshotter.go index 0075559212954..1d73a1c2a2717 100644 --- a/vendor/github.com/coreos/etcd/snap/snapshotter.go +++ b/vendor/github.com/coreos/etcd/snap/snapshotter.go @@ -23,6 +23,7 @@ import ( "os" "path/filepath" "sort" + "strconv" "strings" "time" @@ -31,7 +32,7 @@ import ( "github.com/coreos/etcd/raft" "github.com/coreos/etcd/raft/raftpb" "github.com/coreos/etcd/snap/snappb" - + "github.com/coreos/etcd/wal/walpb" "github.com/coreos/pkg/capnslog" ) @@ -80,9 +81,8 @@ func (s *Snapshotter) save(snapshot *raftpb.Snapshot) error { d, err := snap.Marshal() if err != nil { return err - } else { - marshallingDurations.Observe(float64(time.Since(start)) / float64(time.Second)) } + marshallingDurations.Observe(float64(time.Since(start)) / float64(time.Second)) err = pioutil.WriteAndSyncFile(filepath.Join(s.dir, fname), d, 0666) if err == nil { @@ -97,20 +97,35 @@ func (s *Snapshotter) save(snapshot *raftpb.Snapshot) error { } func (s *Snapshotter) Load() (*raftpb.Snapshot, error) { + return s.loadMatching(func(*raftpb.Snapshot) bool { return true }) +} + +// LoadNewestAvailable loads the newest snapshot available that is in walSnaps. +func (s *Snapshotter) LoadNewestAvailable(walSnaps []walpb.Snapshot) (*raftpb.Snapshot, error) { + return s.loadMatching(func(snapshot *raftpb.Snapshot) bool { + m := snapshot.Metadata + for i := len(walSnaps) - 1; i >= 0; i-- { + if m.Term == walSnaps[i].Term && m.Index == walSnaps[i].Index { + return true + } + } + return false + }) +} + +// loadMatching returns the newest snapshot where matchFn returns true. +func (s *Snapshotter) loadMatching(matchFn func(*raftpb.Snapshot) bool) (*raftpb.Snapshot, error) { names, err := s.snapNames() if err != nil { return nil, err } var snap *raftpb.Snapshot for _, name := range names { - if snap, err = loadSnap(s.dir, name); err == nil { - break + if snap, err = loadSnap(s.dir, name); err == nil && matchFn(snap) { + return snap, nil } } - if err != nil { - return nil, ErrNoSnapshot - } - return snap, nil + return nil, ErrNoSnapshot } func loadSnap(dir, name string) (*raftpb.Snapshot, error) { @@ -172,6 +187,10 @@ func (s *Snapshotter) snapNames() ([]string, error) { if err != nil { return nil, err } + names, err = s.cleanupSnapdir(names) + if err != nil { + return nil, err + } snaps := checkSuffix(names) if len(snaps) == 0 { return nil, ErrNoSnapshot @@ -202,3 +221,48 @@ func renameBroken(path string) { plog.Warningf("cannot rename broken snapshot file %v to %v: %v", path, brokenPath, err) } } + +// cleanupSnapdir removes any files that should not be in the snapshot directory: +// - db.tmp prefixed files that can be orphaned by defragmentation +func (s *Snapshotter) cleanupSnapdir(filenames []string) (names []string, err error) { + for _, filename := range filenames { + if strings.HasPrefix(filename, "db.tmp") { + plog.Infof("found orphaned defragmentation file; deleting: %s", filename) + if rmErr := os.Remove(filepath.Join(s.dir, filename)); rmErr != nil && !os.IsNotExist(rmErr) { + return nil, fmt.Errorf("failed to remove orphaned defragmentation file %s: %v", filename, rmErr) + } + continue + } + names = append(names, filename) + } + return names, nil +} + +func (s *Snapshotter) ReleaseSnapDBs(snap raftpb.Snapshot) error { + dir, err := os.Open(s.dir) + if err != nil { + return err + } + defer dir.Close() + filenames, err := dir.Readdirnames(-1) + if err != nil { + return err + } + for _, filename := range filenames { + if strings.HasSuffix(filename, ".snap.db") { + hexIndex := strings.TrimSuffix(filepath.Base(filename), ".snap.db") + index, err := strconv.ParseUint(hexIndex, 16, 64) + if err != nil { + plog.Warningf("failed to parse index from filename: %s (%v)", filename, err) + continue + } + if index < snap.Metadata.Index { + plog.Infof("found orphaned .snap.db file; deleting %q", filename) + if rmErr := os.Remove(filepath.Join(s.dir, filename)); rmErr != nil && !os.IsNotExist(rmErr) { + plog.Warningf("failed to remove orphaned .snap.db file: %s (%v)", filename, rmErr) + } + } + } + } + return nil +} diff --git a/vendor/github.com/coreos/etcd/version/version.go b/vendor/github.com/coreos/etcd/version/version.go index 156e0f11af95f..aa96ffad492fa 100644 --- a/vendor/github.com/coreos/etcd/version/version.go +++ b/vendor/github.com/coreos/etcd/version/version.go @@ -26,7 +26,7 @@ import ( var ( // MinClusterVersion is the min cluster version this etcd binary is compatible with. MinClusterVersion = "3.0.0" - Version = "3.3.9" + Version = "3.3.25" APIVersion = "unknown" // Git SHA Value will be set during build diff --git a/vendor/github.com/coreos/etcd/wal/decoder.go b/vendor/github.com/coreos/etcd/wal/decoder.go index 6a217f897b01f..f2f0b26fd076f 100644 --- a/vendor/github.com/coreos/etcd/wal/decoder.go +++ b/vendor/github.com/coreos/etcd/wal/decoder.go @@ -59,6 +59,11 @@ func (d *decoder) decode(rec *walpb.Record) error { return d.decodeRecord(rec) } +// raft max message size is set to 1 MB in etcd server +// assume projects set reasonable message size limit, +// thus entry size should never exceed 10 MB +const maxWALEntrySizeLimit = int64(10 * 1024 * 1024) + func (d *decoder) decodeRecord(rec *walpb.Record) error { if len(d.brs) == 0 { return io.EOF @@ -79,6 +84,9 @@ func (d *decoder) decodeRecord(rec *walpb.Record) error { } recBytes, padBytes := decodeFrameSize(l) + if recBytes >= maxWALEntrySizeLimit-padBytes { + return ErrMaxWALEntrySizeLimitExceeded + } data := make([]byte, recBytes+padBytes) if _, err = io.ReadFull(d.brs[0], data); err != nil { diff --git a/vendor/github.com/coreos/etcd/wal/encoder.go b/vendor/github.com/coreos/etcd/wal/encoder.go index e8040b8dff138..e8890d88a9b8b 100644 --- a/vendor/github.com/coreos/etcd/wal/encoder.go +++ b/vendor/github.com/coreos/etcd/wal/encoder.go @@ -92,7 +92,8 @@ func (e *encoder) encode(rec *walpb.Record) error { if padBytes != 0 { data = append(data, make([]byte, padBytes)...) } - _, err = e.bw.Write(data) + n, err = e.bw.Write(data) + walWriteBytes.Add(float64(n)) return err } @@ -108,13 +109,16 @@ func encodeFrameSize(dataBytes int) (lenField uint64, padBytes int) { func (e *encoder) flush() error { e.mu.Lock() - defer e.mu.Unlock() - return e.bw.Flush() + n, err := e.bw.FlushN() + e.mu.Unlock() + walWriteBytes.Add(float64(n)) + return err } func writeUint64(w io.Writer, n uint64, buf []byte) error { // http://golang.org/src/encoding/binary/binary.go binary.LittleEndian.PutUint64(buf, n) - _, err := w.Write(buf) + nv, err := w.Write(buf) + walWriteBytes.Add(float64(nv)) return err } diff --git a/vendor/github.com/coreos/etcd/wal/metrics.go b/vendor/github.com/coreos/etcd/wal/metrics.go index 9e089d380f9b0..7261544165ac1 100644 --- a/vendor/github.com/coreos/etcd/wal/metrics.go +++ b/vendor/github.com/coreos/etcd/wal/metrics.go @@ -24,8 +24,15 @@ var ( Help: "The latency distributions of fsync called by wal.", Buckets: prometheus.ExponentialBuckets(0.001, 2, 14), }) + walWriteBytes = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "etcd", + Subsystem: "disk", + Name: "wal_write_bytes_total", + Help: "Total number of bytes written in WAL.", + }) ) func init() { prometheus.MustRegister(syncDurations) + prometheus.MustRegister(walWriteBytes) } diff --git a/vendor/github.com/coreos/etcd/wal/repair.go b/vendor/github.com/coreos/etcd/wal/repair.go index 091036b57b9ae..f1e507683c3c4 100644 --- a/vendor/github.com/coreos/etcd/wal/repair.go +++ b/vendor/github.com/coreos/etcd/wal/repair.go @@ -18,6 +18,7 @@ import ( "io" "os" "path/filepath" + "time" "github.com/coreos/etcd/pkg/fileutil" "github.com/coreos/etcd/wal/walpb" @@ -76,10 +77,14 @@ func Repair(dirpath string) bool { plog.Errorf("could not repair %v, failed to truncate file", f.Name()) return false } + + start := time.Now() if err = fileutil.Fsync(f.File); err != nil { plog.Errorf("could not repair %v, failed to sync file", f.Name()) return false } + syncDurations.Observe(time.Since(start).Seconds()) + return true default: plog.Errorf("could not repair error (%v)", err) diff --git a/vendor/github.com/coreos/etcd/wal/wal.go b/vendor/github.com/coreos/etcd/wal/wal.go index 96d01a23af69f..f1ffc4326b652 100644 --- a/vendor/github.com/coreos/etcd/wal/wal.go +++ b/vendor/github.com/coreos/etcd/wal/wal.go @@ -55,12 +55,15 @@ var ( plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "wal") - ErrMetadataConflict = errors.New("wal: conflicting metadata found") - ErrFileNotFound = errors.New("wal: file not found") - ErrCRCMismatch = errors.New("wal: crc mismatch") - ErrSnapshotMismatch = errors.New("wal: snapshot mismatch") - ErrSnapshotNotFound = errors.New("wal: snapshot not found") - crcTable = crc32.MakeTable(crc32.Castagnoli) + ErrMetadataConflict = errors.New("wal: conflicting metadata found") + ErrFileNotFound = errors.New("wal: file not found") + ErrCRCMismatch = errors.New("wal: crc mismatch") + ErrSnapshotMismatch = errors.New("wal: snapshot mismatch") + ErrSnapshotNotFound = errors.New("wal: snapshot not found") + ErrSliceOutOfRange = errors.New("wal: slice bounds out of range") + ErrMaxWALEntrySizeLimitExceeded = errors.New("wal: max entry size limit exceeded") + ErrDecoderNotFound = errors.New("wal: decoder not found") + crcTable = crc32.MakeTable(crc32.Castagnoli) ) // WAL is a logical representation of the stable storage. @@ -90,7 +93,8 @@ type WAL struct { } // Create creates a WAL ready for appending records. The given metadata is -// recorded at the head of each WAL file, and can be retrieved with ReadAll. +// recorded at the head of each WAL file, and can be retrieved with ReadAll +// after the file is Open. func Create(dirpath string, metadata []byte) (*WAL, error) { if Exist(dirpath) { return nil, os.ErrExist @@ -147,9 +151,13 @@ func Create(dirpath string, metadata []byte) (*WAL, error) { if perr != nil { return nil, perr } + + start := time.Now() if perr = fileutil.Fsync(pdir); perr != nil { return nil, perr } + syncDurations.Observe(time.Since(start).Seconds()) + if perr = pdir.Close(); err != nil { return nil, perr } @@ -223,17 +231,55 @@ func OpenForRead(dirpath string, snap walpb.Snapshot) (*WAL, error) { } func openAtIndex(dirpath string, snap walpb.Snapshot, write bool) (*WAL, error) { - names, err := readWalNames(dirpath) + names, nameIndex, err := selectWALFiles(dirpath, snap) + if err != nil { + return nil, err + } + + rs, ls, closer, err := openWALFiles(dirpath, names, nameIndex, write) if err != nil { return nil, err } + // create a WAL ready for reading + w := &WAL{ + dir: dirpath, + start: snap, + decoder: newDecoder(rs...), + readClose: closer, + locks: ls, + } + + if write { + // write reuses the file descriptors from read; don't close so + // WAL can append without dropping the file lock + w.readClose = nil + if _, _, err := parseWalName(filepath.Base(w.tail().Name())); err != nil { + closer() + return nil, err + } + w.fp = newFilePipeline(w.dir, SegmentSizeBytes) + } + + return w, nil +} + +func selectWALFiles(dirpath string, snap walpb.Snapshot) ([]string, int, error) { + names, err := readWalNames(dirpath) + if err != nil { + return nil, -1, err + } + nameIndex, ok := searchIndex(names, snap.Index) if !ok || !isValidSeq(names[nameIndex:]) { - return nil, ErrFileNotFound + err = ErrFileNotFound + return nil, -1, err } - // open the wal files + return names, nameIndex, nil +} + +func openWALFiles(dirpath string, names []string, nameIndex int, write bool) ([]io.Reader, []*fileutil.LockedFile, func() error, error) { rcs := make([]io.ReadCloser, 0) rs := make([]io.Reader, 0) ls := make([]*fileutil.LockedFile, 0) @@ -243,7 +289,7 @@ func openAtIndex(dirpath string, snap walpb.Snapshot, write bool) (*WAL, error) l, err := fileutil.TryLockFile(p, os.O_RDWR, fileutil.PrivateFileMode) if err != nil { closeAll(rcs...) - return nil, err + return nil, nil, nil, err } ls = append(ls, l) rcs = append(rcs, l) @@ -251,7 +297,7 @@ func openAtIndex(dirpath string, snap walpb.Snapshot, write bool) (*WAL, error) rf, err := os.OpenFile(p, os.O_RDONLY, fileutil.PrivateFileMode) if err != nil { closeAll(rcs...) - return nil, err + return nil, nil, nil, err } ls = append(ls, nil) rcs = append(rcs, rf) @@ -261,27 +307,7 @@ func openAtIndex(dirpath string, snap walpb.Snapshot, write bool) (*WAL, error) closer := func() error { return closeAll(rcs...) } - // create a WAL ready for reading - w := &WAL{ - dir: dirpath, - start: snap, - decoder: newDecoder(rs...), - readClose: closer, - locks: ls, - } - - if write { - // write reuses the file descriptors from read; don't close so - // WAL can append without dropping the file lock - w.readClose = nil - if _, _, err := parseWalName(filepath.Base(w.tail().Name())); err != nil { - closer() - return nil, err - } - w.fp = newFilePipeline(w.dir, SegmentSizeBytes) - } - - return w, nil + return rs, ls, closer, nil } // ReadAll reads out records of the current WAL. @@ -299,6 +325,10 @@ func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb. defer w.mu.Unlock() rec := &walpb.Record{} + + if w.decoder == nil { + return nil, state, nil, ErrDecoderNotFound + } decoder := w.decoder var match bool @@ -306,8 +336,15 @@ func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb. switch rec.Type { case entryType: e := mustUnmarshalEntry(rec.Data) + // 0 <= e.Index-w.start.Index - 1 < len(ents) if e.Index > w.start.Index { - ents = append(ents[:e.Index-w.start.Index-1], e) + // prevent "panic: runtime error: slice bounds out of range [:13038096702221461992] with capacity 0" + up := e.Index - w.start.Index - 1 + if up > uint64(len(ents)) { + // return error before append call causes runtime panic + return nil, state, nil, ErrSliceOutOfRange + } + ents = append(ents[:up], e) } w.enti = e.Index case stateType: @@ -398,6 +435,150 @@ func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb. return metadata, state, ents, err } +// ValidSnapshotEntries returns all the valid snapshot entries in the wal logs in the given directory. +// Snapshot entries are valid if their index is less than or equal to the most recent committed hardstate. +func ValidSnapshotEntries(walDir string) ([]walpb.Snapshot, error) { + var snaps []walpb.Snapshot + var state raftpb.HardState + var err error + + rec := &walpb.Record{} + names, err := readWalNames(walDir) + if err != nil { + return nil, err + } + + // open wal files in read mode, so that there is no conflict + // when the same WAL is opened elsewhere in write mode + rs, _, closer, err := openWALFiles(walDir, names, 0, false) + if err != nil { + return nil, err + } + defer func() { + if closer != nil { + closer() + } + }() + + // create a new decoder from the readers on the WAL files + decoder := newDecoder(rs...) + + for err = decoder.decode(rec); err == nil; err = decoder.decode(rec) { + switch rec.Type { + case snapshotType: + var loadedSnap walpb.Snapshot + pbutil.MustUnmarshal(&loadedSnap, rec.Data) + snaps = append(snaps, loadedSnap) + case stateType: + state = mustUnmarshalState(rec.Data) + case crcType: + crc := decoder.crc.Sum32() + // current crc of decoder must match the crc of the record. + // do no need to match 0 crc, since the decoder is a new one at this case. + if crc != 0 && rec.Validate(crc) != nil { + return nil, ErrCRCMismatch + } + decoder.updateCRC(rec.Crc) + } + } + // We do not have to read out all the WAL entries + // as the decoder is opened in read mode. + if err != io.EOF && err != io.ErrUnexpectedEOF { + return nil, err + } + + // filter out any snaps that are newer than the committed hardstate + n := 0 + for _, s := range snaps { + if s.Index <= state.Commit { + snaps[n] = s + n++ + } + } + snaps = snaps[:n:n] + + return snaps, nil +} + +// Verify reads through the given WAL and verifies that it is not corrupted. +// It creates a new decoder to read through the records of the given WAL. +// It does not conflict with any open WAL, but it is recommended not to +// call this function after opening the WAL for writing. +// If it cannot read out the expected snap, it will return ErrSnapshotNotFound. +// If the loaded snap doesn't match with the expected one, it will +// return error ErrSnapshotMismatch. +func Verify(walDir string, snap walpb.Snapshot) error { + var metadata []byte + var err error + var match bool + + rec := &walpb.Record{} + + names, nameIndex, err := selectWALFiles(walDir, snap) + if err != nil { + return err + } + + // open wal files in read mode, so that there is no conflict + // when the same WAL is opened elsewhere in write mode + rs, _, closer, err := openWALFiles(walDir, names, nameIndex, false) + if err != nil { + return err + } + + // create a new decoder from the readers on the WAL files + decoder := newDecoder(rs...) + + for err = decoder.decode(rec); err == nil; err = decoder.decode(rec) { + switch rec.Type { + case metadataType: + if metadata != nil && !bytes.Equal(metadata, rec.Data) { + return ErrMetadataConflict + } + metadata = rec.Data + case crcType: + crc := decoder.crc.Sum32() + // Current crc of decoder must match the crc of the record. + // We need not match 0 crc, since the decoder is a new one at this point. + if crc != 0 && rec.Validate(crc) != nil { + return ErrCRCMismatch + } + decoder.updateCRC(rec.Crc) + case snapshotType: + var loadedSnap walpb.Snapshot + pbutil.MustUnmarshal(&loadedSnap, rec.Data) + if loadedSnap.Index == snap.Index { + if loadedSnap.Term != snap.Term { + return ErrSnapshotMismatch + } + match = true + } + // We ignore all entry and state type records as these + // are not necessary for validating the WAL contents + case entryType: + case stateType: + default: + return fmt.Errorf("unexpected block type %d", rec.Type) + } + } + + if closer != nil { + closer() + } + + // We do not have to read out all the WAL entries + // as the decoder is opened in read mode. + if err != io.EOF && err != io.ErrUnexpectedEOF { + return err + } + + if !match { + return ErrSnapshotNotFound + } + + return nil +} + // cut closes current file written and creates a new one ready to append. // cut first creates a temp wal file and writes necessary headers into it. // Then cut atomically rename temp wal file to a wal file. @@ -451,9 +632,12 @@ func (w *WAL) cut() error { if err = os.Rename(newTail.Name(), fpath); err != nil { return err } + + start := time.Now() if err = fileutil.Fsync(w.dirFile); err != nil { return err } + syncDurations.Observe(time.Since(start).Seconds()) // reopen newTail with its new path so calls to Name() match the wal filename format newTail.Close() @@ -495,6 +679,10 @@ func (w *WAL) sync() error { return err } +func (w *WAL) Sync() error { + return w.sync() +} + // ReleaseLockTo releases the locks, which has smaller index than the given index // except the largest one among them. // For example, if WAL is holding lock 1,2,3,4,5,6, ReleaseLockTo(4) will release diff --git a/vendor/github.com/coreos/etcd/wal/walpb/record.pb.go b/vendor/github.com/coreos/etcd/wal/walpb/record.pb.go index 3ce63ddc2eb6a..10ee41702efe1 100644 --- a/vendor/github.com/coreos/etcd/wal/walpb/record.pb.go +++ b/vendor/github.com/coreos/etcd/wal/walpb/record.pb.go @@ -1,28 +1,16 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: record.proto -/* - Package walpb is a generated protocol buffer package. - - It is generated from these files: - record.proto - - It has these top-level messages: - Record - Snapshot -*/ package walpb import ( - "fmt" - - proto "github.com/golang/protobuf/proto" - + fmt "fmt" + io "io" math "math" + math_bits "math/bits" _ "github.com/gogo/protobuf/gogoproto" - - io "io" + proto "github.com/golang/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. @@ -37,36 +25,115 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type Record struct { - Type int64 `protobuf:"varint,1,opt,name=type" json:"type"` - Crc uint32 `protobuf:"varint,2,opt,name=crc" json:"crc"` - Data []byte `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"` - XXX_unrecognized []byte `json:"-"` + Type int64 `protobuf:"varint,1,opt,name=type" json:"type"` + Crc uint32 `protobuf:"varint,2,opt,name=crc" json:"crc"` + Data []byte `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *Record) Reset() { *m = Record{} } -func (m *Record) String() string { return proto.CompactTextString(m) } -func (*Record) ProtoMessage() {} -func (*Record) Descriptor() ([]byte, []int) { return fileDescriptorRecord, []int{0} } +func (m *Record) Reset() { *m = Record{} } +func (m *Record) String() string { return proto.CompactTextString(m) } +func (*Record) ProtoMessage() {} +func (*Record) Descriptor() ([]byte, []int) { + return fileDescriptor_bf94fd919e302a1d, []int{0} +} +func (m *Record) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Record) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Record.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Record) XXX_Merge(src proto.Message) { + xxx_messageInfo_Record.Merge(m, src) +} +func (m *Record) XXX_Size() int { + return m.Size() +} +func (m *Record) XXX_DiscardUnknown() { + xxx_messageInfo_Record.DiscardUnknown(m) +} + +var xxx_messageInfo_Record proto.InternalMessageInfo type Snapshot struct { - Index uint64 `protobuf:"varint,1,opt,name=index" json:"index"` - Term uint64 `protobuf:"varint,2,opt,name=term" json:"term"` - XXX_unrecognized []byte `json:"-"` + Index uint64 `protobuf:"varint,1,opt,name=index" json:"index"` + Term uint64 `protobuf:"varint,2,opt,name=term" json:"term"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Snapshot) Reset() { *m = Snapshot{} } +func (m *Snapshot) String() string { return proto.CompactTextString(m) } +func (*Snapshot) ProtoMessage() {} +func (*Snapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_bf94fd919e302a1d, []int{1} +} +func (m *Snapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Snapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Snapshot.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Snapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_Snapshot.Merge(m, src) +} +func (m *Snapshot) XXX_Size() int { + return m.Size() +} +func (m *Snapshot) XXX_DiscardUnknown() { + xxx_messageInfo_Snapshot.DiscardUnknown(m) } -func (m *Snapshot) Reset() { *m = Snapshot{} } -func (m *Snapshot) String() string { return proto.CompactTextString(m) } -func (*Snapshot) ProtoMessage() {} -func (*Snapshot) Descriptor() ([]byte, []int) { return fileDescriptorRecord, []int{1} } +var xxx_messageInfo_Snapshot proto.InternalMessageInfo func init() { proto.RegisterType((*Record)(nil), "walpb.Record") proto.RegisterType((*Snapshot)(nil), "walpb.Snapshot") } + +func init() { proto.RegisterFile("record.proto", fileDescriptor_bf94fd919e302a1d) } + +var fileDescriptor_bf94fd919e302a1d = []byte{ + // 186 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x4a, 0x4d, 0xce, + 0x2f, 0x4a, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2d, 0x4f, 0xcc, 0x29, 0x48, 0x92, + 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x8b, 0xe8, 0x83, 0x58, 0x10, 0x49, 0x25, 0x3f, 0x2e, 0xb6, + 0x20, 0xb0, 0x62, 0x21, 0x09, 0x2e, 0x96, 0x92, 0xca, 0x82, 0x54, 0x09, 0x46, 0x05, 0x46, 0x0d, + 0x66, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xc0, 0x22, 0x42, 0x62, 0x5c, 0xcc, 0xc9, 0x45, + 0xc9, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xbc, 0x50, 0x09, 0x90, 0x80, 0x90, 0x10, 0x17, 0x4b, 0x4a, + 0x62, 0x49, 0xa2, 0x04, 0xb3, 0x02, 0xa3, 0x06, 0x4f, 0x10, 0x98, 0xad, 0xe4, 0xc0, 0xc5, 0x11, + 0x9c, 0x97, 0x58, 0x50, 0x9c, 0x91, 0x5f, 0x22, 0x24, 0xc5, 0xc5, 0x9a, 0x99, 0x97, 0x92, 0x5a, + 0x01, 0x36, 0x92, 0x05, 0xaa, 0x13, 0x22, 0x04, 0xb6, 0x2d, 0xb5, 0x28, 0x17, 0x6c, 0x28, 0x0b, + 0xdc, 0xb6, 0xd4, 0xa2, 0x5c, 0x27, 0x91, 0x13, 0x0f, 0xe5, 0x18, 0x4e, 0x3c, 0x92, 0x63, 0xbc, + 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x19, 0x8f, 0xe5, 0x18, 0x00, 0x01, 0x00, 0x00, + 0xff, 0xff, 0x7f, 0x5e, 0x5c, 0x46, 0xd3, 0x00, 0x00, 0x00, +} + func (m *Record) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -74,32 +141,39 @@ func (m *Record) Marshal() (dAtA []byte, err error) { } func (m *Record) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Record) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0x8 - i++ - i = encodeVarintRecord(dAtA, i, uint64(m.Type)) - dAtA[i] = 0x10 - i++ - i = encodeVarintRecord(dAtA, i, uint64(m.Crc)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Data != nil { - dAtA[i] = 0x1a - i++ + i -= len(m.Data) + copy(dAtA[i:], m.Data) i = encodeVarintRecord(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i-- + dAtA[i] = 0x1a } - return i, nil + i = encodeVarintRecord(dAtA, i, uint64(m.Crc)) + i-- + dAtA[i] = 0x10 + i = encodeVarintRecord(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + return len(dAtA) - i, nil } func (m *Snapshot) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -107,32 +181,43 @@ func (m *Snapshot) Marshal() (dAtA []byte, err error) { } func (m *Snapshot) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Snapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0x8 - i++ - i = encodeVarintRecord(dAtA, i, uint64(m.Index)) - dAtA[i] = 0x10 - i++ - i = encodeVarintRecord(dAtA, i, uint64(m.Term)) if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - return i, nil + i = encodeVarintRecord(dAtA, i, uint64(m.Term)) + i-- + dAtA[i] = 0x10 + i = encodeVarintRecord(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 + return len(dAtA) - i, nil } func encodeVarintRecord(dAtA []byte, offset int, v uint64) int { + offset -= sovRecord(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Record) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovRecord(uint64(m.Type)) @@ -148,6 +233,9 @@ func (m *Record) Size() (n int) { } func (m *Snapshot) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovRecord(uint64(m.Index)) @@ -159,14 +247,7 @@ func (m *Snapshot) Size() (n int) { } func sovRecord(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozRecord(x uint64) (n int) { return sovRecord(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -186,7 +267,7 @@ func (m *Record) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -214,7 +295,7 @@ func (m *Record) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Type |= (int64(b) & 0x7F) << shift + m.Type |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -233,7 +314,7 @@ func (m *Record) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Crc |= (uint32(b) & 0x7F) << shift + m.Crc |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -252,7 +333,7 @@ func (m *Record) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -261,6 +342,9 @@ func (m *Record) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRecord } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthRecord + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -278,6 +362,9 @@ func (m *Record) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthRecord } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRecord + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -306,7 +393,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -334,7 +421,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Index |= (uint64(b) & 0x7F) << shift + m.Index |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -353,7 +440,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Term |= (uint64(b) & 0x7F) << shift + m.Term |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -367,6 +454,9 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthRecord } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthRecord + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } @@ -434,10 +524,13 @@ func skipRecord(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthRecord } + iNdEx += length + if iNdEx < 0 { + return 0, ErrInvalidLengthRecord + } return iNdEx, nil case 3: for { @@ -466,6 +559,9 @@ func skipRecord(dAtA []byte) (n int, err error) { return 0, err } iNdEx = start + next + if iNdEx < 0 { + return 0, ErrInvalidLengthRecord + } } return iNdEx, nil case 4: @@ -484,21 +580,3 @@ var ( ErrInvalidLengthRecord = fmt.Errorf("proto: negative length found during unmarshaling") ErrIntOverflowRecord = fmt.Errorf("proto: integer overflow") ) - -func init() { proto.RegisterFile("record.proto", fileDescriptorRecord) } - -var fileDescriptorRecord = []byte{ - // 186 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x4a, 0x4d, 0xce, - 0x2f, 0x4a, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2d, 0x4f, 0xcc, 0x29, 0x48, 0x92, - 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x8b, 0xe8, 0x83, 0x58, 0x10, 0x49, 0x25, 0x3f, 0x2e, 0xb6, - 0x20, 0xb0, 0x62, 0x21, 0x09, 0x2e, 0x96, 0x92, 0xca, 0x82, 0x54, 0x09, 0x46, 0x05, 0x46, 0x0d, - 0x66, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xc0, 0x22, 0x42, 0x62, 0x5c, 0xcc, 0xc9, 0x45, - 0xc9, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xbc, 0x50, 0x09, 0x90, 0x80, 0x90, 0x10, 0x17, 0x4b, 0x4a, - 0x62, 0x49, 0xa2, 0x04, 0xb3, 0x02, 0xa3, 0x06, 0x4f, 0x10, 0x98, 0xad, 0xe4, 0xc0, 0xc5, 0x11, - 0x9c, 0x97, 0x58, 0x50, 0x9c, 0x91, 0x5f, 0x22, 0x24, 0xc5, 0xc5, 0x9a, 0x99, 0x97, 0x92, 0x5a, - 0x01, 0x36, 0x92, 0x05, 0xaa, 0x13, 0x22, 0x04, 0xb6, 0x2d, 0xb5, 0x28, 0x17, 0x6c, 0x28, 0x0b, - 0xdc, 0xb6, 0xd4, 0xa2, 0x5c, 0x27, 0x91, 0x13, 0x0f, 0xe5, 0x18, 0x4e, 0x3c, 0x92, 0x63, 0xbc, - 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x19, 0x8f, 0xe5, 0x18, 0x00, 0x01, 0x00, 0x00, - 0xff, 0xff, 0x7f, 0x5e, 0x5c, 0x46, 0xd3, 0x00, 0x00, 0x00, -} diff --git a/vendor/github.com/coreos/go-systemd/activation/files.go b/vendor/github.com/coreos/go-systemd/activation/files.go deleted file mode 100644 index 29dd18defad84..0000000000000 --- a/vendor/github.com/coreos/go-systemd/activation/files.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// 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. - -// Package activation implements primitives for systemd socket activation. -package activation - -import ( - "os" - "strconv" - "strings" - "syscall" -) - -const ( - // listenFdsStart corresponds to `SD_LISTEN_FDS_START`. - listenFdsStart = 3 -) - -// Files returns a slice containing a `os.File` object for each -// file descriptor passed to this process via systemd fd-passing protocol. -// -// The order of the file descriptors is preserved in the returned slice. -// `unsetEnv` is typically set to `true` in order to avoid clashes in -// fd usage and to avoid leaking environment flags to child processes. -func Files(unsetEnv bool) []*os.File { - if unsetEnv { - defer os.Unsetenv("LISTEN_PID") - defer os.Unsetenv("LISTEN_FDS") - defer os.Unsetenv("LISTEN_FDNAMES") - } - - pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) - if err != nil || pid != os.Getpid() { - return nil - } - - nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS")) - if err != nil || nfds == 0 { - return nil - } - - names := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":") - - files := make([]*os.File, 0, nfds) - for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ { - syscall.CloseOnExec(fd) - name := "LISTEN_FD_" + strconv.Itoa(fd) - offset := fd - listenFdsStart - if offset < len(names) && len(names[offset]) > 0 { - name = names[offset] - } - files = append(files, os.NewFile(uintptr(fd), name)) - } - - return files -} diff --git a/vendor/github.com/coreos/go-systemd/dbus/dbus.go b/vendor/github.com/coreos/go-systemd/dbus/dbus.go deleted file mode 100644 index 1d54810aff4e9..0000000000000 --- a/vendor/github.com/coreos/go-systemd/dbus/dbus.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// 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. - -// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/ -package dbus - -import ( - "encoding/hex" - "fmt" - "os" - "strconv" - "strings" - "sync" - - "github.com/godbus/dbus" -) - -const ( - alpha = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ` - num = `0123456789` - alphanum = alpha + num - signalBuffer = 100 -) - -// needsEscape checks whether a byte in a potential dbus ObjectPath needs to be escaped -func needsEscape(i int, b byte) bool { - // Escape everything that is not a-z-A-Z-0-9 - // Also escape 0-9 if it's the first character - return strings.IndexByte(alphanum, b) == -1 || - (i == 0 && strings.IndexByte(num, b) != -1) -} - -// PathBusEscape sanitizes a constituent string of a dbus ObjectPath using the -// rules that systemd uses for serializing special characters. -func PathBusEscape(path string) string { - // Special case the empty string - if len(path) == 0 { - return "_" - } - n := []byte{} - for i := 0; i < len(path); i++ { - c := path[i] - if needsEscape(i, c) { - e := fmt.Sprintf("_%x", c) - n = append(n, []byte(e)...) - } else { - n = append(n, c) - } - } - return string(n) -} - -// pathBusUnescape is the inverse of PathBusEscape. -func pathBusUnescape(path string) string { - if path == "_" { - return "" - } - n := []byte{} - for i := 0; i < len(path); i++ { - c := path[i] - if c == '_' && i+2 < len(path) { - res, err := hex.DecodeString(path[i+1 : i+3]) - if err == nil { - n = append(n, res...) - } - i += 2 - } else { - n = append(n, c) - } - } - return string(n) -} - -// Conn is a connection to systemd's dbus endpoint. -type Conn struct { - // sysconn/sysobj are only used to call dbus methods - sysconn *dbus.Conn - sysobj dbus.BusObject - - // sigconn/sigobj are only used to receive dbus signals - sigconn *dbus.Conn - sigobj dbus.BusObject - - jobListener struct { - jobs map[dbus.ObjectPath]chan<- string - sync.Mutex - } - subStateSubscriber struct { - updateCh chan<- *SubStateUpdate - errCh chan<- error - sync.Mutex - ignore map[dbus.ObjectPath]int64 - cleanIgnore int64 - } - propertiesSubscriber struct { - updateCh chan<- *PropertiesUpdate - errCh chan<- error - sync.Mutex - } -} - -// New establishes a connection to any available bus and authenticates. -// Callers should call Close() when done with the connection. -func New() (*Conn, error) { - conn, err := NewSystemConnection() - if err != nil && os.Geteuid() == 0 { - return NewSystemdConnection() - } - return conn, err -} - -// NewSystemConnection establishes a connection to the system bus and authenticates. -// Callers should call Close() when done with the connection -func NewSystemConnection() (*Conn, error) { - return NewConnection(func() (*dbus.Conn, error) { - return dbusAuthHelloConnection(dbus.SystemBusPrivate) - }) -} - -// NewUserConnection establishes a connection to the session bus and -// authenticates. This can be used to connect to systemd user instances. -// Callers should call Close() when done with the connection. -func NewUserConnection() (*Conn, error) { - return NewConnection(func() (*dbus.Conn, error) { - return dbusAuthHelloConnection(dbus.SessionBusPrivate) - }) -} - -// NewSystemdConnection establishes a private, direct connection to systemd. -// This can be used for communicating with systemd without a dbus daemon. -// Callers should call Close() when done with the connection. -func NewSystemdConnection() (*Conn, error) { - return NewConnection(func() (*dbus.Conn, error) { - // We skip Hello when talking directly to systemd. - return dbusAuthConnection(func() (*dbus.Conn, error) { - return dbus.Dial("unix:path=/run/systemd/private") - }) - }) -} - -// Close closes an established connection -func (c *Conn) Close() { - c.sysconn.Close() - c.sigconn.Close() -} - -// NewConnection establishes a connection to a bus using a caller-supplied function. -// This allows connecting to remote buses through a user-supplied mechanism. -// The supplied function may be called multiple times, and should return independent connections. -// The returned connection must be fully initialised: the org.freedesktop.DBus.Hello call must have succeeded, -// and any authentication should be handled by the function. -func NewConnection(dialBus func() (*dbus.Conn, error)) (*Conn, error) { - sysconn, err := dialBus() - if err != nil { - return nil, err - } - - sigconn, err := dialBus() - if err != nil { - sysconn.Close() - return nil, err - } - - c := &Conn{ - sysconn: sysconn, - sysobj: systemdObject(sysconn), - sigconn: sigconn, - sigobj: systemdObject(sigconn), - } - - c.subStateSubscriber.ignore = make(map[dbus.ObjectPath]int64) - c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string) - - // Setup the listeners on jobs so that we can get completions - c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, - "type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'") - - c.dispatch() - return c, nil -} - -// GetManagerProperty returns the value of a property on the org.freedesktop.systemd1.Manager -// interface. The value is returned in its string representation, as defined at -// https://developer.gnome.org/glib/unstable/gvariant-text.html -func (c *Conn) GetManagerProperty(prop string) (string, error) { - variant, err := c.sysobj.GetProperty("org.freedesktop.systemd1.Manager." + prop) - if err != nil { - return "", err - } - return variant.String(), nil -} - -func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) { - conn, err := createBus() - if err != nil { - return nil, err - } - - // Only use EXTERNAL method, and hardcode the uid (not username) - // to avoid a username lookup (which requires a dynamically linked - // libc) - methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))} - - err = conn.Auth(methods) - if err != nil { - conn.Close() - return nil, err - } - - return conn, nil -} - -func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) { - conn, err := dbusAuthConnection(createBus) - if err != nil { - return nil, err - } - - if err = conn.Hello(); err != nil { - conn.Close() - return nil, err - } - - return conn, nil -} - -func systemdObject(conn *dbus.Conn) dbus.BusObject { - return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1")) -} diff --git a/vendor/github.com/coreos/go-systemd/dbus/methods.go b/vendor/github.com/coreos/go-systemd/dbus/methods.go deleted file mode 100644 index 0b4207229f775..0000000000000 --- a/vendor/github.com/coreos/go-systemd/dbus/methods.go +++ /dev/null @@ -1,592 +0,0 @@ -// Copyright 2015, 2018 CoreOS, Inc. -// -// 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. - -package dbus - -import ( - "errors" - "fmt" - "path" - "strconv" - - "github.com/godbus/dbus" -) - -func (c *Conn) jobComplete(signal *dbus.Signal) { - var id uint32 - var job dbus.ObjectPath - var unit string - var result string - dbus.Store(signal.Body, &id, &job, &unit, &result) - c.jobListener.Lock() - out, ok := c.jobListener.jobs[job] - if ok { - out <- result - delete(c.jobListener.jobs, job) - } - c.jobListener.Unlock() -} - -func (c *Conn) startJob(ch chan<- string, job string, args ...interface{}) (int, error) { - if ch != nil { - c.jobListener.Lock() - defer c.jobListener.Unlock() - } - - var p dbus.ObjectPath - err := c.sysobj.Call(job, 0, args...).Store(&p) - if err != nil { - return 0, err - } - - if ch != nil { - c.jobListener.jobs[p] = ch - } - - // ignore error since 0 is fine if conversion fails - jobID, _ := strconv.Atoi(path.Base(string(p))) - - return jobID, nil -} - -// StartUnit enqueues a start job and depending jobs, if any (unless otherwise -// specified by the mode string). -// -// Takes the unit to activate, plus a mode string. The mode needs to be one of -// replace, fail, isolate, ignore-dependencies, ignore-requirements. If -// "replace" the call will start the unit and its dependencies, possibly -// replacing already queued jobs that conflict with this. If "fail" the call -// will start the unit and its dependencies, but will fail if this would change -// an already queued job. If "isolate" the call will start the unit in question -// and terminate all units that aren't dependencies of it. If -// "ignore-dependencies" it will start a unit but ignore all its dependencies. -// If "ignore-requirements" it will start a unit but only ignore the -// requirement dependencies. It is not recommended to make use of the latter -// two options. -// -// If the provided channel is non-nil, a result string will be sent to it upon -// job completion: one of done, canceled, timeout, failed, dependency, skipped. -// done indicates successful execution of a job. canceled indicates that a job -// has been canceled before it finished execution. timeout indicates that the -// job timeout was reached. failed indicates that the job failed. dependency -// indicates that a job this job has been depending on failed and the job hence -// has been removed too. skipped indicates that a job was skipped because it -// didn't apply to the units current state. -// -// If no error occurs, the ID of the underlying systemd job will be returned. There -// does exist the possibility for no error to be returned, but for the returned job -// ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint -// should not be considered authoritative. -// -// If an error does occur, it will be returned to the user alongside a job ID of 0. -func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode) -} - -// StopUnit is similar to StartUnit but stops the specified unit rather -// than starting it. -func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode) -} - -// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise. -func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode) -} - -// RestartUnit restarts a service. If a service is restarted that isn't -// running it will be started. -func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode) -} - -// TryRestartUnit is like RestartUnit, except that a service that isn't running -// is not affected by the restart. -func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode) -} - -// ReloadOrRestart attempts a reload if the unit supports it and use a restart -// otherwise. -func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode) -} - -// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try" -// flavored restart otherwise. -func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode) -} - -// StartTransientUnit() may be used to create and start a transient unit, which -// will be released as soon as it is not running or referenced anymore or the -// system is rebooted. name is the unit name including suffix, and must be -// unique. mode is the same as in StartUnit(), properties contains properties -// of the unit. -func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) { - return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0)) -} - -// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's -// processes are killed. -func (c *Conn) KillUnit(name string, signal int32) { - c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store() -} - -// ResetFailedUnit resets the "failed" state of a specific unit. -func (c *Conn) ResetFailedUnit(name string) error { - return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store() -} - -// SystemState returns the systemd state. Equivalent to `systemctl is-system-running`. -func (c *Conn) SystemState() (*Property, error) { - var err error - var prop dbus.Variant - - obj := c.sysconn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") - err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.systemd1.Manager", "SystemState").Store(&prop) - if err != nil { - return nil, err - } - - return &Property{Name: "SystemState", Value: prop}, nil -} - -// getProperties takes the unit path and returns all of its dbus object properties, for the given dbus interface -func (c *Conn) getProperties(path dbus.ObjectPath, dbusInterface string) (map[string]interface{}, error) { - var err error - var props map[string]dbus.Variant - - if !path.IsValid() { - return nil, fmt.Errorf("invalid unit name: %v", path) - } - - obj := c.sysconn.Object("org.freedesktop.systemd1", path) - err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props) - if err != nil { - return nil, err - } - - out := make(map[string]interface{}, len(props)) - for k, v := range props { - out[k] = v.Value() - } - - return out, nil -} - -// GetUnitProperties takes the (unescaped) unit name and returns all of its dbus object properties. -func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) { - path := unitPath(unit) - return c.getProperties(path, "org.freedesktop.systemd1.Unit") -} - -// GetUnitProperties takes the (escaped) unit path and returns all of its dbus object properties. -func (c *Conn) GetUnitPathProperties(path dbus.ObjectPath) (map[string]interface{}, error) { - return c.getProperties(path, "org.freedesktop.systemd1.Unit") -} - -func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) { - var err error - var prop dbus.Variant - - path := unitPath(unit) - if !path.IsValid() { - return nil, errors.New("invalid unit name: " + unit) - } - - obj := c.sysconn.Object("org.freedesktop.systemd1", path) - err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop) - if err != nil { - return nil, err - } - - return &Property{Name: propertyName, Value: prop}, nil -} - -func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) { - return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName) -} - -// GetServiceProperty returns property for given service name and property name -func (c *Conn) GetServiceProperty(service string, propertyName string) (*Property, error) { - return c.getProperty(service, "org.freedesktop.systemd1.Service", propertyName) -} - -// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type. -// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope -// return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit -func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) { - path := unitPath(unit) - return c.getProperties(path, "org.freedesktop.systemd1."+unitType) -} - -// SetUnitProperties() may be used to modify certain unit properties at runtime. -// Not all properties may be changed at runtime, but many resource management -// settings (primarily those in systemd.cgroup(5)) may. The changes are applied -// instantly, and stored on disk for future boots, unless runtime is true, in which -// case the settings only apply until the next reboot. name is the name of the unit -// to modify. properties are the settings to set, encoded as an array of property -// name and value pairs. -func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error { - return c.sysobj.Call("org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store() -} - -func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) { - return c.getProperty(unit, "org.freedesktop.systemd1."+unitType, propertyName) -} - -type UnitStatus struct { - Name string // The primary unit name as string - Description string // The human readable description string - LoadState string // The load state (i.e. whether the unit file has been loaded successfully) - ActiveState string // The active state (i.e. whether the unit is currently started or not) - SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not) - Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string. - Path dbus.ObjectPath // The unit object path - JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise - JobType string // The job type as string - JobPath dbus.ObjectPath // The job object path -} - -type storeFunc func(retvalues ...interface{}) error - -func (c *Conn) listUnitsInternal(f storeFunc) ([]UnitStatus, error) { - result := make([][]interface{}, 0) - err := f(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - status := make([]UnitStatus, len(result)) - statusInterface := make([]interface{}, len(status)) - for i := range status { - statusInterface[i] = &status[i] - } - - err = dbus.Store(resultInterface, statusInterface...) - if err != nil { - return nil, err - } - - return status, nil -} - -// ListUnits returns an array with all currently loaded units. Note that -// units may be known by multiple names at the same time, and hence there might -// be more unit names loaded than actual units behind them. -func (c *Conn) ListUnits() ([]UnitStatus, error) { - return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store) -} - -// ListUnitsFiltered returns an array with units filtered by state. -// It takes a list of units' statuses to filter. -func (c *Conn) ListUnitsFiltered(states []string) ([]UnitStatus, error) { - return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsFiltered", 0, states).Store) -} - -// ListUnitsByPatterns returns an array with units. -// It takes a list of units' statuses and names to filter. -// Note that units may be known by multiple names at the same time, -// and hence there might be more unit names loaded than actual units behind them. -func (c *Conn) ListUnitsByPatterns(states []string, patterns []string) ([]UnitStatus, error) { - return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByPatterns", 0, states, patterns).Store) -} - -// ListUnitsByNames returns an array with units. It takes a list of units' -// names and returns an UnitStatus array. Comparing to ListUnitsByPatterns -// method, this method returns statuses even for inactive or non-existing -// units. Input array should contain exact unit names, but not patterns. -// Note: Requires systemd v230 or higher -func (c *Conn) ListUnitsByNames(units []string) ([]UnitStatus, error) { - return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByNames", 0, units).Store) -} - -type UnitFile struct { - Path string - Type string -} - -func (c *Conn) listUnitFilesInternal(f storeFunc) ([]UnitFile, error) { - result := make([][]interface{}, 0) - err := f(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - files := make([]UnitFile, len(result)) - fileInterface := make([]interface{}, len(files)) - for i := range files { - fileInterface[i] = &files[i] - } - - err = dbus.Store(resultInterface, fileInterface...) - if err != nil { - return nil, err - } - - return files, nil -} - -// ListUnitFiles returns an array of all available units on disk. -func (c *Conn) ListUnitFiles() ([]UnitFile, error) { - return c.listUnitFilesInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFiles", 0).Store) -} - -// ListUnitFilesByPatterns returns an array of all available units on disk matched the patterns. -func (c *Conn) ListUnitFilesByPatterns(states []string, patterns []string) ([]UnitFile, error) { - return c.listUnitFilesInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns", 0, states, patterns).Store) -} - -type LinkUnitFileChange EnableUnitFileChange - -// LinkUnitFiles() links unit files (that are located outside of the -// usual unit search paths) into the unit search path. -// -// It takes a list of absolute paths to unit files to link and two -// booleans. The first boolean controls whether the unit shall be -// enabled for runtime only (true, /run), or persistently (false, -// /etc). -// The second controls whether symlinks pointing to other units shall -// be replaced if necessary. -// -// This call returns a list of the changes made. The list consists of -// structures with three strings: the type of the change (one of symlink -// or unlink), the file name of the symlink and the destination of the -// symlink. -func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) { - result := make([][]interface{}, 0) - err := c.sysobj.Call("org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - changes := make([]LinkUnitFileChange, len(result)) - changesInterface := make([]interface{}, len(changes)) - for i := range changes { - changesInterface[i] = &changes[i] - } - - err = dbus.Store(resultInterface, changesInterface...) - if err != nil { - return nil, err - } - - return changes, nil -} - -// EnableUnitFiles() may be used to enable one or more units in the system (by -// creating symlinks to them in /etc or /run). -// -// It takes a list of unit files to enable (either just file names or full -// absolute paths if the unit files are residing outside the usual unit -// search paths), and two booleans: the first controls whether the unit shall -// be enabled for runtime only (true, /run), or persistently (false, /etc). -// The second one controls whether symlinks pointing to other units shall -// be replaced if necessary. -// -// This call returns one boolean and an array with the changes made. The -// boolean signals whether the unit files contained any enablement -// information (i.e. an [Install]) section. The changes list consists of -// structures with three strings: the type of the change (one of symlink -// or unlink), the file name of the symlink and the destination of the -// symlink. -func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) { - var carries_install_info bool - - result := make([][]interface{}, 0) - err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result) - if err != nil { - return false, nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - changes := make([]EnableUnitFileChange, len(result)) - changesInterface := make([]interface{}, len(changes)) - for i := range changes { - changesInterface[i] = &changes[i] - } - - err = dbus.Store(resultInterface, changesInterface...) - if err != nil { - return false, nil, err - } - - return carries_install_info, changes, nil -} - -type EnableUnitFileChange struct { - Type string // Type of the change (one of symlink or unlink) - Filename string // File name of the symlink - Destination string // Destination of the symlink -} - -// DisableUnitFiles() may be used to disable one or more units in the system (by -// removing symlinks to them from /etc or /run). -// -// It takes a list of unit files to disable (either just file names or full -// absolute paths if the unit files are residing outside the usual unit -// search paths), and one boolean: whether the unit was enabled for runtime -// only (true, /run), or persistently (false, /etc). -// -// This call returns an array with the changes made. The changes list -// consists of structures with three strings: the type of the change (one of -// symlink or unlink), the file name of the symlink and the destination of the -// symlink. -func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) { - result := make([][]interface{}, 0) - err := c.sysobj.Call("org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - changes := make([]DisableUnitFileChange, len(result)) - changesInterface := make([]interface{}, len(changes)) - for i := range changes { - changesInterface[i] = &changes[i] - } - - err = dbus.Store(resultInterface, changesInterface...) - if err != nil { - return nil, err - } - - return changes, nil -} - -type DisableUnitFileChange struct { - Type string // Type of the change (one of symlink or unlink) - Filename string // File name of the symlink - Destination string // Destination of the symlink -} - -// MaskUnitFiles masks one or more units in the system -// -// It takes three arguments: -// * list of units to mask (either just file names or full -// absolute paths if the unit files are residing outside -// the usual unit search paths) -// * runtime to specify whether the unit was enabled for runtime -// only (true, /run/systemd/..), or persistently (false, /etc/systemd/..) -// * force flag -func (c *Conn) MaskUnitFiles(files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) { - result := make([][]interface{}, 0) - err := c.sysobj.Call("org.freedesktop.systemd1.Manager.MaskUnitFiles", 0, files, runtime, force).Store(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - changes := make([]MaskUnitFileChange, len(result)) - changesInterface := make([]interface{}, len(changes)) - for i := range changes { - changesInterface[i] = &changes[i] - } - - err = dbus.Store(resultInterface, changesInterface...) - if err != nil { - return nil, err - } - - return changes, nil -} - -type MaskUnitFileChange struct { - Type string // Type of the change (one of symlink or unlink) - Filename string // File name of the symlink - Destination string // Destination of the symlink -} - -// UnmaskUnitFiles unmasks one or more units in the system -// -// It takes two arguments: -// * list of unit files to mask (either just file names or full -// absolute paths if the unit files are residing outside -// the usual unit search paths) -// * runtime to specify whether the unit was enabled for runtime -// only (true, /run/systemd/..), or persistently (false, /etc/systemd/..) -func (c *Conn) UnmaskUnitFiles(files []string, runtime bool) ([]UnmaskUnitFileChange, error) { - result := make([][]interface{}, 0) - err := c.sysobj.Call("org.freedesktop.systemd1.Manager.UnmaskUnitFiles", 0, files, runtime).Store(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - changes := make([]UnmaskUnitFileChange, len(result)) - changesInterface := make([]interface{}, len(changes)) - for i := range changes { - changesInterface[i] = &changes[i] - } - - err = dbus.Store(resultInterface, changesInterface...) - if err != nil { - return nil, err - } - - return changes, nil -} - -type UnmaskUnitFileChange struct { - Type string // Type of the change (one of symlink or unlink) - Filename string // File name of the symlink - Destination string // Destination of the symlink -} - -// Reload instructs systemd to scan for and reload unit files. This is -// equivalent to a 'systemctl daemon-reload'. -func (c *Conn) Reload() error { - return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store() -} - -func unitPath(name string) dbus.ObjectPath { - return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name)) -} - -// unitName returns the unescaped base element of the supplied escaped path -func unitName(dpath dbus.ObjectPath) string { - return pathBusUnescape(path.Base(string(dpath))) -} diff --git a/vendor/github.com/coreos/go-systemd/dbus/properties.go b/vendor/github.com/coreos/go-systemd/dbus/properties.go deleted file mode 100644 index 6c81895876365..0000000000000 --- a/vendor/github.com/coreos/go-systemd/dbus/properties.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// 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. - -package dbus - -import ( - "github.com/godbus/dbus" -) - -// From the systemd docs: -// -// The properties array of StartTransientUnit() may take many of the settings -// that may also be configured in unit files. Not all parameters are currently -// accepted though, but we plan to cover more properties with future release. -// Currently you may set the Description, Slice and all dependency types of -// units, as well as RemainAfterExit, ExecStart for service units, -// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares, -// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth, -// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit, -// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map -// directly to their counterparts in unit files and as normal D-Bus object -// properties. The exception here is the PIDs field of scope units which is -// used for construction of the scope only and specifies the initial PIDs to -// add to the scope object. - -type Property struct { - Name string - Value dbus.Variant -} - -type PropertyCollection struct { - Name string - Properties []Property -} - -type execStart struct { - Path string // the binary path to execute - Args []string // an array with all arguments to pass to the executed command, starting with argument 0 - UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly -} - -// PropExecStart sets the ExecStart service property. The first argument is a -// slice with the binary path to execute followed by the arguments to pass to -// the executed command. See -// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= -func PropExecStart(command []string, uncleanIsFailure bool) Property { - execStarts := []execStart{ - execStart{ - Path: command[0], - Args: command, - UncleanIsFailure: uncleanIsFailure, - }, - } - - return Property{ - Name: "ExecStart", - Value: dbus.MakeVariant(execStarts), - } -} - -// PropRemainAfterExit sets the RemainAfterExit service property. See -// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit= -func PropRemainAfterExit(b bool) Property { - return Property{ - Name: "RemainAfterExit", - Value: dbus.MakeVariant(b), - } -} - -// PropType sets the Type service property. See -// http://www.freedesktop.org/software/systemd/man/systemd.service.html#Type= -func PropType(t string) Property { - return Property{ - Name: "Type", - Value: dbus.MakeVariant(t), - } -} - -// PropDescription sets the Description unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description= -func PropDescription(desc string) Property { - return Property{ - Name: "Description", - Value: dbus.MakeVariant(desc), - } -} - -func propDependency(name string, units []string) Property { - return Property{ - Name: name, - Value: dbus.MakeVariant(units), - } -} - -// PropRequires sets the Requires unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires= -func PropRequires(units ...string) Property { - return propDependency("Requires", units) -} - -// PropRequiresOverridable sets the RequiresOverridable unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable= -func PropRequiresOverridable(units ...string) Property { - return propDependency("RequiresOverridable", units) -} - -// PropRequisite sets the Requisite unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite= -func PropRequisite(units ...string) Property { - return propDependency("Requisite", units) -} - -// PropRequisiteOverridable sets the RequisiteOverridable unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable= -func PropRequisiteOverridable(units ...string) Property { - return propDependency("RequisiteOverridable", units) -} - -// PropWants sets the Wants unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants= -func PropWants(units ...string) Property { - return propDependency("Wants", units) -} - -// PropBindsTo sets the BindsTo unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo= -func PropBindsTo(units ...string) Property { - return propDependency("BindsTo", units) -} - -// PropRequiredBy sets the RequiredBy unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy= -func PropRequiredBy(units ...string) Property { - return propDependency("RequiredBy", units) -} - -// PropRequiredByOverridable sets the RequiredByOverridable unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable= -func PropRequiredByOverridable(units ...string) Property { - return propDependency("RequiredByOverridable", units) -} - -// PropWantedBy sets the WantedBy unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy= -func PropWantedBy(units ...string) Property { - return propDependency("WantedBy", units) -} - -// PropBoundBy sets the BoundBy unit property. See -// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy= -func PropBoundBy(units ...string) Property { - return propDependency("BoundBy", units) -} - -// PropConflicts sets the Conflicts unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts= -func PropConflicts(units ...string) Property { - return propDependency("Conflicts", units) -} - -// PropConflictedBy sets the ConflictedBy unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy= -func PropConflictedBy(units ...string) Property { - return propDependency("ConflictedBy", units) -} - -// PropBefore sets the Before unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before= -func PropBefore(units ...string) Property { - return propDependency("Before", units) -} - -// PropAfter sets the After unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After= -func PropAfter(units ...string) Property { - return propDependency("After", units) -} - -// PropOnFailure sets the OnFailure unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure= -func PropOnFailure(units ...string) Property { - return propDependency("OnFailure", units) -} - -// PropTriggers sets the Triggers unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers= -func PropTriggers(units ...string) Property { - return propDependency("Triggers", units) -} - -// PropTriggeredBy sets the TriggeredBy unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy= -func PropTriggeredBy(units ...string) Property { - return propDependency("TriggeredBy", units) -} - -// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo= -func PropPropagatesReloadTo(units ...string) Property { - return propDependency("PropagatesReloadTo", units) -} - -// PropRequiresMountsFor sets the RequiresMountsFor unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor= -func PropRequiresMountsFor(units ...string) Property { - return propDependency("RequiresMountsFor", units) -} - -// PropSlice sets the Slice unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice= -func PropSlice(slice string) Property { - return Property{ - Name: "Slice", - Value: dbus.MakeVariant(slice), - } -} - -// PropPids sets the PIDs field of scope units used in the initial construction -// of the scope only and specifies the initial PIDs to add to the scope object. -// See https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/#properties -func PropPids(pids ...uint32) Property { - return Property{ - Name: "PIDs", - Value: dbus.MakeVariant(pids), - } -} diff --git a/vendor/github.com/go-ini/ini/LICENSE b/vendor/github.com/coreos/go-systemd/v22/LICENSE similarity index 100% rename from vendor/github.com/go-ini/ini/LICENSE rename to vendor/github.com/coreos/go-systemd/v22/LICENSE diff --git a/vendor/github.com/coreos/go-systemd/v22/NOTICE b/vendor/github.com/coreos/go-systemd/v22/NOTICE new file mode 100644 index 0000000000000..23a0ada2fbb56 --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/NOTICE @@ -0,0 +1,5 @@ +CoreOS Project +Copyright 2018 CoreOS, Inc + +This product includes software developed at CoreOS, Inc. +(http://www.coreos.com/). diff --git a/vendor/github.com/coreos/go-systemd/v22/README.md b/vendor/github.com/coreos/go-systemd/v22/README.md new file mode 100644 index 0000000000000..9fac51300abb0 --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/README.md @@ -0,0 +1,73 @@ +# go-systemd + +[![Build Status](https://travis-ci.org/coreos/go-systemd.png?branch=master)](https://travis-ci.org/coreos/go-systemd) +[![godoc](https://img.shields.io/badge/godoc-reference-5272B4)](https://pkg.go.dev/mod/github.com/coreos/go-systemd/v22/?tab=packages) +![minimum golang 1.12](https://img.shields.io/badge/golang-1.12%2B-orange.svg) + + +Go bindings to systemd. The project has several packages: + +- `activation` - for writing and using socket activation from Go +- `daemon` - for notifying systemd of service status changes +- `dbus` - for starting/stopping/inspecting running services and units +- `journal` - for writing to systemd's logging service, journald +- `sdjournal` - for reading from journald by wrapping its C API +- `login1` - for integration with the systemd logind API +- `machine1` - for registering machines/containers with systemd +- `unit` - for (de)serialization and comparison of unit files + +## Socket Activation + +An example HTTP server using socket activation can be quickly set up by following this README on a Linux machine running systemd: + +https://github.com/coreos/go-systemd/tree/master/examples/activation/httpserver + +## systemd Service Notification + +The `daemon` package is an implementation of the [sd_notify protocol](https://www.freedesktop.org/software/systemd/man/sd_notify.html#Description). +It can be used to inform systemd of service start-up completion, watchdog events, and other status changes. + +## D-Bus + +The `dbus` package connects to the [systemd D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/dbus/) and lets you start, stop and introspect systemd units. +[API documentation][dbus-doc] is available online. + +[dbus-doc]: https://pkg.go.dev/github.com/coreos/go-systemd/v22/dbus?tab=doc + +### Debugging + +Create `/etc/dbus-1/system-local.conf` that looks like this: + +``` + + + + + + + +``` + +## Journal + +### Writing to the Journal + +Using the pure-Go `journal` package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry. + +### Reading from the Journal + +The `sdjournal` package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available. + +## logind + +The `login1` package provides functions to integrate with the [systemd logind API](http://www.freedesktop.org/wiki/Software/systemd/logind/). + +## machined + +The `machine1` package allows interaction with the [systemd machined D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/machined/). + +## Units + +The `unit` package provides various functions for working with [systemd unit files](http://www.freedesktop.org/software/systemd/man/systemd.unit.html). diff --git a/vendor/github.com/coreos/go-systemd/v22/activation/files_unix.go b/vendor/github.com/coreos/go-systemd/v22/activation/files_unix.go new file mode 100644 index 0000000000000..fc7db98fb41b6 --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/activation/files_unix.go @@ -0,0 +1,69 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +// +build !windows + +// Package activation implements primitives for systemd socket activation. +package activation + +import ( + "os" + "strconv" + "strings" + "syscall" +) + +const ( + // listenFdsStart corresponds to `SD_LISTEN_FDS_START`. + listenFdsStart = 3 +) + +// Files returns a slice containing a `os.File` object for each +// file descriptor passed to this process via systemd fd-passing protocol. +// +// The order of the file descriptors is preserved in the returned slice. +// `unsetEnv` is typically set to `true` in order to avoid clashes in +// fd usage and to avoid leaking environment flags to child processes. +func Files(unsetEnv bool) []*os.File { + if unsetEnv { + defer os.Unsetenv("LISTEN_PID") + defer os.Unsetenv("LISTEN_FDS") + defer os.Unsetenv("LISTEN_FDNAMES") + } + + pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) + if err != nil || pid != os.Getpid() { + return nil + } + + nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS")) + if err != nil || nfds == 0 { + return nil + } + + names := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":") + + files := make([]*os.File, 0, nfds) + for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ { + syscall.CloseOnExec(fd) + name := "LISTEN_FD_" + strconv.Itoa(fd) + offset := fd - listenFdsStart + if offset < len(names) && len(names[offset]) > 0 { + name = names[offset] + } + files = append(files, os.NewFile(uintptr(fd), name)) + } + + return files +} diff --git a/vendor/github.com/coreos/go-systemd/v22/activation/files_windows.go b/vendor/github.com/coreos/go-systemd/v22/activation/files_windows.go new file mode 100644 index 0000000000000..d391bf00c5eee --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/activation/files_windows.go @@ -0,0 +1,21 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +package activation + +import "os" + +func Files(unsetEnv bool) []*os.File { + return nil +} diff --git a/vendor/github.com/coreos/go-systemd/activation/listeners.go b/vendor/github.com/coreos/go-systemd/v22/activation/listeners.go similarity index 97% rename from vendor/github.com/coreos/go-systemd/activation/listeners.go rename to vendor/github.com/coreos/go-systemd/v22/activation/listeners.go index bb5cc2311e2cd..3dbe2b0877601 100644 --- a/vendor/github.com/coreos/go-systemd/activation/listeners.go +++ b/vendor/github.com/coreos/go-systemd/v22/activation/listeners.go @@ -67,7 +67,7 @@ func TLSListeners(tlsConfig *tls.Config) ([]net.Listener, error) { return nil, err } - if tlsConfig != nil && err == nil { + if tlsConfig != nil { for i, l := range listeners { // Activate TLS only for TCP sockets if l.Addr().Network() == "tcp" { @@ -88,7 +88,7 @@ func TLSListenersWithNames(tlsConfig *tls.Config) (map[string][]net.Listener, er return nil, err } - if tlsConfig != nil && err == nil { + if tlsConfig != nil { for _, ll := range listeners { // Activate TLS only for TCP sockets for i, l := range ll { diff --git a/vendor/github.com/coreos/go-systemd/activation/packetconns.go b/vendor/github.com/coreos/go-systemd/v22/activation/packetconns.go similarity index 100% rename from vendor/github.com/coreos/go-systemd/activation/packetconns.go rename to vendor/github.com/coreos/go-systemd/v22/activation/packetconns.go diff --git a/vendor/github.com/coreos/go-systemd/daemon/sdnotify.go b/vendor/github.com/coreos/go-systemd/v22/daemon/sdnotify.go similarity index 100% rename from vendor/github.com/coreos/go-systemd/daemon/sdnotify.go rename to vendor/github.com/coreos/go-systemd/v22/daemon/sdnotify.go diff --git a/vendor/github.com/coreos/go-systemd/daemon/watchdog.go b/vendor/github.com/coreos/go-systemd/v22/daemon/watchdog.go similarity index 100% rename from vendor/github.com/coreos/go-systemd/daemon/watchdog.go rename to vendor/github.com/coreos/go-systemd/v22/daemon/watchdog.go diff --git a/vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go b/vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go new file mode 100644 index 0000000000000..cff5af1a64c3e --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go @@ -0,0 +1,261 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/ +package dbus + +import ( + "context" + "encoding/hex" + "fmt" + "os" + "strconv" + "strings" + "sync" + + "github.com/godbus/dbus/v5" +) + +const ( + alpha = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ` + num = `0123456789` + alphanum = alpha + num + signalBuffer = 100 +) + +// needsEscape checks whether a byte in a potential dbus ObjectPath needs to be escaped +func needsEscape(i int, b byte) bool { + // Escape everything that is not a-z-A-Z-0-9 + // Also escape 0-9 if it's the first character + return strings.IndexByte(alphanum, b) == -1 || + (i == 0 && strings.IndexByte(num, b) != -1) +} + +// PathBusEscape sanitizes a constituent string of a dbus ObjectPath using the +// rules that systemd uses for serializing special characters. +func PathBusEscape(path string) string { + // Special case the empty string + if len(path) == 0 { + return "_" + } + n := []byte{} + for i := 0; i < len(path); i++ { + c := path[i] + if needsEscape(i, c) { + e := fmt.Sprintf("_%x", c) + n = append(n, []byte(e)...) + } else { + n = append(n, c) + } + } + return string(n) +} + +// pathBusUnescape is the inverse of PathBusEscape. +func pathBusUnescape(path string) string { + if path == "_" { + return "" + } + n := []byte{} + for i := 0; i < len(path); i++ { + c := path[i] + if c == '_' && i+2 < len(path) { + res, err := hex.DecodeString(path[i+1 : i+3]) + if err == nil { + n = append(n, res...) + } + i += 2 + } else { + n = append(n, c) + } + } + return string(n) +} + +// Conn is a connection to systemd's dbus endpoint. +type Conn struct { + // sysconn/sysobj are only used to call dbus methods + sysconn *dbus.Conn + sysobj dbus.BusObject + + // sigconn/sigobj are only used to receive dbus signals + sigconn *dbus.Conn + sigobj dbus.BusObject + + jobListener struct { + jobs map[dbus.ObjectPath]chan<- string + sync.Mutex + } + subStateSubscriber struct { + updateCh chan<- *SubStateUpdate + errCh chan<- error + sync.Mutex + ignore map[dbus.ObjectPath]int64 + cleanIgnore int64 + } + propertiesSubscriber struct { + updateCh chan<- *PropertiesUpdate + errCh chan<- error + sync.Mutex + } +} + +// Deprecated: use NewWithContext instead. +func New() (*Conn, error) { + return NewWithContext(context.Background()) +} + +// NewWithContext establishes a connection to any available bus and authenticates. +// Callers should call Close() when done with the connection. +func NewWithContext(ctx context.Context) (*Conn, error) { + conn, err := NewSystemConnectionContext(ctx) + if err != nil && os.Geteuid() == 0 { + return NewSystemdConnectionContext(ctx) + } + return conn, err +} + +// Deprecated: use NewSystemConnectionContext instead. +func NewSystemConnection() (*Conn, error) { + return NewSystemConnectionContext(context.Background()) +} + +// NewSystemConnectionContext establishes a connection to the system bus and authenticates. +// Callers should call Close() when done with the connection. +func NewSystemConnectionContext(ctx context.Context) (*Conn, error) { + return NewConnection(func() (*dbus.Conn, error) { + return dbusAuthHelloConnection(ctx, dbus.SystemBusPrivate) + }) +} + +// Deprecated: use NewUserConnectionContext instead. +func NewUserConnection() (*Conn, error) { + return NewUserConnectionContext(context.Background()) +} + +// NewUserConnectionContext establishes a connection to the session bus and +// authenticates. This can be used to connect to systemd user instances. +// Callers should call Close() when done with the connection. +func NewUserConnectionContext(ctx context.Context) (*Conn, error) { + return NewConnection(func() (*dbus.Conn, error) { + return dbusAuthHelloConnection(ctx, dbus.SessionBusPrivate) + }) +} + +// Deprecated: use NewSystemdConnectionContext instead. +func NewSystemdConnection() (*Conn, error) { + return NewSystemdConnectionContext(context.Background()) +} + +// NewSystemdConnectionContext establishes a private, direct connection to systemd. +// This can be used for communicating with systemd without a dbus daemon. +// Callers should call Close() when done with the connection. +func NewSystemdConnectionContext(ctx context.Context) (*Conn, error) { + return NewConnection(func() (*dbus.Conn, error) { + // We skip Hello when talking directly to systemd. + return dbusAuthConnection(ctx, func(opts ...dbus.ConnOption) (*dbus.Conn, error) { + return dbus.Dial("unix:path=/run/systemd/private", opts...) + }) + }) +} + +// Close closes an established connection. +func (c *Conn) Close() { + c.sysconn.Close() + c.sigconn.Close() +} + +// NewConnection establishes a connection to a bus using a caller-supplied function. +// This allows connecting to remote buses through a user-supplied mechanism. +// The supplied function may be called multiple times, and should return independent connections. +// The returned connection must be fully initialised: the org.freedesktop.DBus.Hello call must have succeeded, +// and any authentication should be handled by the function. +func NewConnection(dialBus func() (*dbus.Conn, error)) (*Conn, error) { + sysconn, err := dialBus() + if err != nil { + return nil, err + } + + sigconn, err := dialBus() + if err != nil { + sysconn.Close() + return nil, err + } + + c := &Conn{ + sysconn: sysconn, + sysobj: systemdObject(sysconn), + sigconn: sigconn, + sigobj: systemdObject(sigconn), + } + + c.subStateSubscriber.ignore = make(map[dbus.ObjectPath]int64) + c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string) + + // Setup the listeners on jobs so that we can get completions + c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, + "type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'") + + c.dispatch() + return c, nil +} + +// GetManagerProperty returns the value of a property on the org.freedesktop.systemd1.Manager +// interface. The value is returned in its string representation, as defined at +// https://developer.gnome.org/glib/unstable/gvariant-text.html. +func (c *Conn) GetManagerProperty(prop string) (string, error) { + variant, err := c.sysobj.GetProperty("org.freedesktop.systemd1.Manager." + prop) + if err != nil { + return "", err + } + return variant.String(), nil +} + +func dbusAuthConnection(ctx context.Context, createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) { + conn, err := createBus(dbus.WithContext(ctx)) + if err != nil { + return nil, err + } + + // Only use EXTERNAL method, and hardcode the uid (not username) + // to avoid a username lookup (which requires a dynamically linked + // libc) + methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))} + + err = conn.Auth(methods) + if err != nil { + conn.Close() + return nil, err + } + + return conn, nil +} + +func dbusAuthHelloConnection(ctx context.Context, createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) { + conn, err := dbusAuthConnection(ctx, createBus) + if err != nil { + return nil, err + } + + if err = conn.Hello(); err != nil { + conn.Close() + return nil, err + } + + return conn, nil +} + +func systemdObject(conn *dbus.Conn) dbus.BusObject { + return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1")) +} diff --git a/vendor/github.com/coreos/go-systemd/v22/dbus/methods.go b/vendor/github.com/coreos/go-systemd/v22/dbus/methods.go new file mode 100644 index 0000000000000..fa04afc708e7c --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/dbus/methods.go @@ -0,0 +1,830 @@ +// Copyright 2015, 2018 CoreOS, Inc. +// +// 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. + +package dbus + +import ( + "context" + "errors" + "fmt" + "path" + "strconv" + + "github.com/godbus/dbus/v5" +) + +// Who can be used to specify which process to kill in the unit via the KillUnitWithTarget API +type Who string + +const ( + // All sends the signal to all processes in the unit + All Who = "all" + // Main sends the signal to the main process of the unit + Main Who = "main" + // Control sends the signal to the control process of the unit + Control Who = "control" +) + +func (c *Conn) jobComplete(signal *dbus.Signal) { + var id uint32 + var job dbus.ObjectPath + var unit string + var result string + dbus.Store(signal.Body, &id, &job, &unit, &result) + c.jobListener.Lock() + out, ok := c.jobListener.jobs[job] + if ok { + out <- result + delete(c.jobListener.jobs, job) + } + c.jobListener.Unlock() +} + +func (c *Conn) startJob(ctx context.Context, ch chan<- string, job string, args ...interface{}) (int, error) { + if ch != nil { + c.jobListener.Lock() + defer c.jobListener.Unlock() + } + + var p dbus.ObjectPath + err := c.sysobj.CallWithContext(ctx, job, 0, args...).Store(&p) + if err != nil { + return 0, err + } + + if ch != nil { + c.jobListener.jobs[p] = ch + } + + // ignore error since 0 is fine if conversion fails + jobID, _ := strconv.Atoi(path.Base(string(p))) + + return jobID, nil +} + +// Deprecated: use StartUnitContext instead. +func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) { + return c.StartUnitContext(context.Background(), name, mode, ch) +} + +// StartUnitContext enqueues a start job and depending jobs, if any (unless otherwise +// specified by the mode string). +// +// Takes the unit to activate, plus a mode string. The mode needs to be one of +// replace, fail, isolate, ignore-dependencies, ignore-requirements. If +// "replace" the call will start the unit and its dependencies, possibly +// replacing already queued jobs that conflict with this. If "fail" the call +// will start the unit and its dependencies, but will fail if this would change +// an already queued job. If "isolate" the call will start the unit in question +// and terminate all units that aren't dependencies of it. If +// "ignore-dependencies" it will start a unit but ignore all its dependencies. +// If "ignore-requirements" it will start a unit but only ignore the +// requirement dependencies. It is not recommended to make use of the latter +// two options. +// +// If the provided channel is non-nil, a result string will be sent to it upon +// job completion: one of done, canceled, timeout, failed, dependency, skipped. +// done indicates successful execution of a job. canceled indicates that a job +// has been canceled before it finished execution. timeout indicates that the +// job timeout was reached. failed indicates that the job failed. dependency +// indicates that a job this job has been depending on failed and the job hence +// has been removed too. skipped indicates that a job was skipped because it +// didn't apply to the units current state. +// +// If no error occurs, the ID of the underlying systemd job will be returned. There +// does exist the possibility for no error to be returned, but for the returned job +// ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint +// should not be considered authoritative. +// +// If an error does occur, it will be returned to the user alongside a job ID of 0. +func (c *Conn) StartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode) +} + +// Deprecated: use StopUnitContext instead. +func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) { + return c.StopUnitContext(context.Background(), name, mode, ch) +} + +// StopUnitContext is similar to StartUnitContext, but stops the specified unit +// rather than starting it. +func (c *Conn) StopUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode) +} + +// Deprecated: use ReloadUnitContext instead. +func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) { + return c.ReloadUnitContext(context.Background(), name, mode, ch) +} + +// ReloadUnitContext reloads a unit. Reloading is done only if the unit +// is already running, and fails otherwise. +func (c *Conn) ReloadUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode) +} + +// Deprecated: use RestartUnitContext instead. +func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) { + return c.RestartUnitContext(context.Background(), name, mode, ch) +} + +// RestartUnitContext restarts a service. If a service is restarted that isn't +// running it will be started. +func (c *Conn) RestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode) +} + +// Deprecated: use TryRestartUnitContext instead. +func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) { + return c.TryRestartUnitContext(context.Background(), name, mode, ch) +} + +// TryRestartUnitContext is like RestartUnitContext, except that a service that +// isn't running is not affected by the restart. +func (c *Conn) TryRestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode) +} + +// Deprecated: use ReloadOrRestartUnitContext instead. +func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) { + return c.ReloadOrRestartUnitContext(context.Background(), name, mode, ch) +} + +// ReloadOrRestartUnitContext attempts a reload if the unit supports it and use +// a restart otherwise. +func (c *Conn) ReloadOrRestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode) +} + +// Deprecated: use ReloadOrTryRestartUnitContext instead. +func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) { + return c.ReloadOrTryRestartUnitContext(context.Background(), name, mode, ch) +} + +// ReloadOrTryRestartUnitContext attempts a reload if the unit supports it, +// and use a "Try" flavored restart otherwise. +func (c *Conn) ReloadOrTryRestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { + return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode) +} + +// Deprecated: use StartTransientUnitContext instead. +func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) { + return c.StartTransientUnitContext(context.Background(), name, mode, properties, ch) +} + +// StartTransientUnitContext may be used to create and start a transient unit, which +// will be released as soon as it is not running or referenced anymore or the +// system is rebooted. name is the unit name including suffix, and must be +// unique. mode is the same as in StartUnitContext, properties contains properties +// of the unit. +func (c *Conn) StartTransientUnitContext(ctx context.Context, name string, mode string, properties []Property, ch chan<- string) (int, error) { + return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0)) +} + +// Deprecated: use KillUnitContext instead. +func (c *Conn) KillUnit(name string, signal int32) { + c.KillUnitContext(context.Background(), name, signal) +} + +// KillUnitContext takes the unit name and a UNIX signal number to send. +// All of the unit's processes are killed. +func (c *Conn) KillUnitContext(ctx context.Context, name string, signal int32) { + c.KillUnitWithTarget(ctx, name, All, signal) +} + +// KillUnitWithTarget is like KillUnitContext, but allows you to specify which +// process in the unit to send the signal to. +func (c *Conn) KillUnitWithTarget(ctx context.Context, name string, target Who, signal int32) error { + return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.KillUnit", 0, name, string(target), signal).Store() +} + +// Deprecated: use ResetFailedUnitContext instead. +func (c *Conn) ResetFailedUnit(name string) error { + return c.ResetFailedUnitContext(context.Background(), name) +} + +// ResetFailedUnitContext resets the "failed" state of a specific unit. +func (c *Conn) ResetFailedUnitContext(ctx context.Context, name string) error { + return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store() +} + +// Deprecated: use SystemStateContext instead. +func (c *Conn) SystemState() (*Property, error) { + return c.SystemStateContext(context.Background()) +} + +// SystemStateContext returns the systemd state. Equivalent to +// systemctl is-system-running. +func (c *Conn) SystemStateContext(ctx context.Context) (*Property, error) { + var err error + var prop dbus.Variant + + obj := c.sysconn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") + err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.systemd1.Manager", "SystemState").Store(&prop) + if err != nil { + return nil, err + } + + return &Property{Name: "SystemState", Value: prop}, nil +} + +// getProperties takes the unit path and returns all of its dbus object properties, for the given dbus interface. +func (c *Conn) getProperties(ctx context.Context, path dbus.ObjectPath, dbusInterface string) (map[string]interface{}, error) { + var err error + var props map[string]dbus.Variant + + if !path.IsValid() { + return nil, fmt.Errorf("invalid unit name: %v", path) + } + + obj := c.sysconn.Object("org.freedesktop.systemd1", path) + err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props) + if err != nil { + return nil, err + } + + out := make(map[string]interface{}, len(props)) + for k, v := range props { + out[k] = v.Value() + } + + return out, nil +} + +// Deprecated: use GetUnitPropertiesContext instead. +func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) { + return c.GetUnitPropertiesContext(context.Background(), unit) +} + +// GetUnitPropertiesContext takes the (unescaped) unit name and returns all of +// its dbus object properties. +func (c *Conn) GetUnitPropertiesContext(ctx context.Context, unit string) (map[string]interface{}, error) { + path := unitPath(unit) + return c.getProperties(ctx, path, "org.freedesktop.systemd1.Unit") +} + +// Deprecated: use GetUnitPathPropertiesContext instead. +func (c *Conn) GetUnitPathProperties(path dbus.ObjectPath) (map[string]interface{}, error) { + return c.GetUnitPathPropertiesContext(context.Background(), path) +} + +// GetUnitPathPropertiesContext takes the (escaped) unit path and returns all +// of its dbus object properties. +func (c *Conn) GetUnitPathPropertiesContext(ctx context.Context, path dbus.ObjectPath) (map[string]interface{}, error) { + return c.getProperties(ctx, path, "org.freedesktop.systemd1.Unit") +} + +// Deprecated: use GetAllPropertiesContext instead. +func (c *Conn) GetAllProperties(unit string) (map[string]interface{}, error) { + return c.GetAllPropertiesContext(context.Background(), unit) +} + +// GetAllPropertiesContext takes the (unescaped) unit name and returns all of +// its dbus object properties. +func (c *Conn) GetAllPropertiesContext(ctx context.Context, unit string) (map[string]interface{}, error) { + path := unitPath(unit) + return c.getProperties(ctx, path, "") +} + +func (c *Conn) getProperty(ctx context.Context, unit string, dbusInterface string, propertyName string) (*Property, error) { + var err error + var prop dbus.Variant + + path := unitPath(unit) + if !path.IsValid() { + return nil, errors.New("invalid unit name: " + unit) + } + + obj := c.sysconn.Object("org.freedesktop.systemd1", path) + err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop) + if err != nil { + return nil, err + } + + return &Property{Name: propertyName, Value: prop}, nil +} + +// Deprecated: use GetUnitPropertyContext instead. +func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) { + return c.GetUnitPropertyContext(context.Background(), unit, propertyName) +} + +// GetUnitPropertyContext takes an (unescaped) unit name, and a property name, +// and returns the property value. +func (c *Conn) GetUnitPropertyContext(ctx context.Context, unit string, propertyName string) (*Property, error) { + return c.getProperty(ctx, unit, "org.freedesktop.systemd1.Unit", propertyName) +} + +// Deprecated: use GetServicePropertyContext instead. +func (c *Conn) GetServiceProperty(service string, propertyName string) (*Property, error) { + return c.GetServicePropertyContext(context.Background(), service, propertyName) +} + +// GetServiceProperty returns property for given service name and property name. +func (c *Conn) GetServicePropertyContext(ctx context.Context, service string, propertyName string) (*Property, error) { + return c.getProperty(ctx, service, "org.freedesktop.systemd1.Service", propertyName) +} + +// Deprecated: use GetUnitTypePropertiesContext instead. +func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) { + return c.GetUnitTypePropertiesContext(context.Background(), unit, unitType) +} + +// GetUnitTypePropertiesContext returns the extra properties for a unit, specific to the unit type. +// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope. +// Returns "dbus.Error: Unknown interface" error if the unitType is not the correct type of the unit. +func (c *Conn) GetUnitTypePropertiesContext(ctx context.Context, unit string, unitType string) (map[string]interface{}, error) { + path := unitPath(unit) + return c.getProperties(ctx, path, "org.freedesktop.systemd1."+unitType) +} + +// Deprecated: use SetUnitPropertiesContext instead. +func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error { + return c.SetUnitPropertiesContext(context.Background(), name, runtime, properties...) +} + +// SetUnitPropertiesContext may be used to modify certain unit properties at runtime. +// Not all properties may be changed at runtime, but many resource management +// settings (primarily those in systemd.cgroup(5)) may. The changes are applied +// instantly, and stored on disk for future boots, unless runtime is true, in which +// case the settings only apply until the next reboot. name is the name of the unit +// to modify. properties are the settings to set, encoded as an array of property +// name and value pairs. +func (c *Conn) SetUnitPropertiesContext(ctx context.Context, name string, runtime bool, properties ...Property) error { + return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store() +} + +// Deprecated: use GetUnitTypePropertyContext instead. +func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) { + return c.GetUnitTypePropertyContext(context.Background(), unit, unitType, propertyName) +} + +// GetUnitTypePropertyContext takes a property name, a unit name, and a unit type, +// and returns a property value. For valid values of unitType, see GetUnitTypePropertiesContext. +func (c *Conn) GetUnitTypePropertyContext(ctx context.Context, unit string, unitType string, propertyName string) (*Property, error) { + return c.getProperty(ctx, unit, "org.freedesktop.systemd1."+unitType, propertyName) +} + +type UnitStatus struct { + Name string // The primary unit name as string + Description string // The human readable description string + LoadState string // The load state (i.e. whether the unit file has been loaded successfully) + ActiveState string // The active state (i.e. whether the unit is currently started or not) + SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not) + Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string. + Path dbus.ObjectPath // The unit object path + JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise + JobType string // The job type as string + JobPath dbus.ObjectPath // The job object path +} + +type storeFunc func(retvalues ...interface{}) error + +func (c *Conn) listUnitsInternal(f storeFunc) ([]UnitStatus, error) { + result := make([][]interface{}, 0) + err := f(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + status := make([]UnitStatus, len(result)) + statusInterface := make([]interface{}, len(status)) + for i := range status { + statusInterface[i] = &status[i] + } + + err = dbus.Store(resultInterface, statusInterface...) + if err != nil { + return nil, err + } + + return status, nil +} + +// Deprecated: use ListUnitsContext instead. +func (c *Conn) ListUnits() ([]UnitStatus, error) { + return c.ListUnitsContext(context.Background()) +} + +// ListUnitsContext returns an array with all currently loaded units. Note that +// units may be known by multiple names at the same time, and hence there might +// be more unit names loaded than actual units behind them. +// Also note that a unit is only loaded if it is active and/or enabled. +// Units that are both disabled and inactive will thus not be returned. +func (c *Conn) ListUnitsContext(ctx context.Context) ([]UnitStatus, error) { + return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnits", 0).Store) +} + +// Deprecated: use ListUnitsFilteredContext instead. +func (c *Conn) ListUnitsFiltered(states []string) ([]UnitStatus, error) { + return c.ListUnitsFilteredContext(context.Background(), states) +} + +// ListUnitsFilteredContext returns an array with units filtered by state. +// It takes a list of units' statuses to filter. +func (c *Conn) ListUnitsFilteredContext(ctx context.Context, states []string) ([]UnitStatus, error) { + return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitsFiltered", 0, states).Store) +} + +// Deprecated: use ListUnitsByPatternsContext instead. +func (c *Conn) ListUnitsByPatterns(states []string, patterns []string) ([]UnitStatus, error) { + return c.ListUnitsByPatternsContext(context.Background(), states, patterns) +} + +// ListUnitsByPatternsContext returns an array with units. +// It takes a list of units' statuses and names to filter. +// Note that units may be known by multiple names at the same time, +// and hence there might be more unit names loaded than actual units behind them. +func (c *Conn) ListUnitsByPatternsContext(ctx context.Context, states []string, patterns []string) ([]UnitStatus, error) { + return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitsByPatterns", 0, states, patterns).Store) +} + +// Deprecated: use ListUnitsByNamesContext instead. +func (c *Conn) ListUnitsByNames(units []string) ([]UnitStatus, error) { + return c.ListUnitsByNamesContext(context.Background(), units) +} + +// ListUnitsByNamesContext returns an array with units. It takes a list of units' +// names and returns an UnitStatus array. Comparing to ListUnitsByPatternsContext +// method, this method returns statuses even for inactive or non-existing +// units. Input array should contain exact unit names, but not patterns. +// +// Requires systemd v230 or higher. +func (c *Conn) ListUnitsByNamesContext(ctx context.Context, units []string) ([]UnitStatus, error) { + return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitsByNames", 0, units).Store) +} + +type UnitFile struct { + Path string + Type string +} + +func (c *Conn) listUnitFilesInternal(f storeFunc) ([]UnitFile, error) { + result := make([][]interface{}, 0) + err := f(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + files := make([]UnitFile, len(result)) + fileInterface := make([]interface{}, len(files)) + for i := range files { + fileInterface[i] = &files[i] + } + + err = dbus.Store(resultInterface, fileInterface...) + if err != nil { + return nil, err + } + + return files, nil +} + +// Deprecated: use ListUnitFilesContext instead. +func (c *Conn) ListUnitFiles() ([]UnitFile, error) { + return c.ListUnitFilesContext(context.Background()) +} + +// ListUnitFiles returns an array of all available units on disk. +func (c *Conn) ListUnitFilesContext(ctx context.Context) ([]UnitFile, error) { + return c.listUnitFilesInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitFiles", 0).Store) +} + +// Deprecated: use ListUnitFilesByPatternsContext instead. +func (c *Conn) ListUnitFilesByPatterns(states []string, patterns []string) ([]UnitFile, error) { + return c.ListUnitFilesByPatternsContext(context.Background(), states, patterns) +} + +// ListUnitFilesByPatternsContext returns an array of all available units on disk matched the patterns. +func (c *Conn) ListUnitFilesByPatternsContext(ctx context.Context, states []string, patterns []string) ([]UnitFile, error) { + return c.listUnitFilesInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns", 0, states, patterns).Store) +} + +type LinkUnitFileChange EnableUnitFileChange + +// Deprecated: use LinkUnitFilesContext instead. +func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) { + return c.LinkUnitFilesContext(context.Background(), files, runtime, force) +} + +// LinkUnitFilesContext links unit files (that are located outside of the +// usual unit search paths) into the unit search path. +// +// It takes a list of absolute paths to unit files to link and two +// booleans. +// +// The first boolean controls whether the unit shall be +// enabled for runtime only (true, /run), or persistently (false, +// /etc). +// +// The second controls whether symlinks pointing to other units shall +// be replaced if necessary. +// +// This call returns a list of the changes made. The list consists of +// structures with three strings: the type of the change (one of symlink +// or unlink), the file name of the symlink and the destination of the +// symlink. +func (c *Conn) LinkUnitFilesContext(ctx context.Context, files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) { + result := make([][]interface{}, 0) + err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]LinkUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return nil, err + } + + return changes, nil +} + +// Deprecated: use EnableUnitFilesContext instead. +func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) { + return c.EnableUnitFilesContext(context.Background(), files, runtime, force) +} + +// EnableUnitFilesContext may be used to enable one or more units in the system +// (by creating symlinks to them in /etc or /run). +// +// It takes a list of unit files to enable (either just file names or full +// absolute paths if the unit files are residing outside the usual unit +// search paths), and two booleans: the first controls whether the unit shall +// be enabled for runtime only (true, /run), or persistently (false, /etc). +// The second one controls whether symlinks pointing to other units shall +// be replaced if necessary. +// +// This call returns one boolean and an array with the changes made. The +// boolean signals whether the unit files contained any enablement +// information (i.e. an [Install]) section. The changes list consists of +// structures with three strings: the type of the change (one of symlink +// or unlink), the file name of the symlink and the destination of the +// symlink. +func (c *Conn) EnableUnitFilesContext(ctx context.Context, files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) { + var carries_install_info bool + + result := make([][]interface{}, 0) + err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result) + if err != nil { + return false, nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]EnableUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return false, nil, err + } + + return carries_install_info, changes, nil +} + +type EnableUnitFileChange struct { + Type string // Type of the change (one of symlink or unlink) + Filename string // File name of the symlink + Destination string // Destination of the symlink +} + +// Deprecated: use DisableUnitFilesContext instead. +func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) { + return c.DisableUnitFilesContext(context.Background(), files, runtime) +} + +// DisableUnitFilesContext may be used to disable one or more units in the +// system (by removing symlinks to them from /etc or /run). +// +// It takes a list of unit files to disable (either just file names or full +// absolute paths if the unit files are residing outside the usual unit +// search paths), and one boolean: whether the unit was enabled for runtime +// only (true, /run), or persistently (false, /etc). +// +// This call returns an array with the changes made. The changes list +// consists of structures with three strings: the type of the change (one of +// symlink or unlink), the file name of the symlink and the destination of the +// symlink. +func (c *Conn) DisableUnitFilesContext(ctx context.Context, files []string, runtime bool) ([]DisableUnitFileChange, error) { + result := make([][]interface{}, 0) + err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]DisableUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return nil, err + } + + return changes, nil +} + +type DisableUnitFileChange struct { + Type string // Type of the change (one of symlink or unlink) + Filename string // File name of the symlink + Destination string // Destination of the symlink +} + +// Deprecated: use MaskUnitFilesContext instead. +func (c *Conn) MaskUnitFiles(files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) { + return c.MaskUnitFilesContext(context.Background(), files, runtime, force) +} + +// MaskUnitFilesContext masks one or more units in the system. +// +// The files argument contains a list of units to mask (either just file names +// or full absolute paths if the unit files are residing outside the usual unit +// search paths). +// +// The runtime argument is used to specify whether the unit was enabled for +// runtime only (true, /run/systemd/..), or persistently (false, +// /etc/systemd/..). +func (c *Conn) MaskUnitFilesContext(ctx context.Context, files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) { + result := make([][]interface{}, 0) + err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.MaskUnitFiles", 0, files, runtime, force).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]MaskUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return nil, err + } + + return changes, nil +} + +type MaskUnitFileChange struct { + Type string // Type of the change (one of symlink or unlink) + Filename string // File name of the symlink + Destination string // Destination of the symlink +} + +// Deprecated: use UnmaskUnitFilesContext instead. +func (c *Conn) UnmaskUnitFiles(files []string, runtime bool) ([]UnmaskUnitFileChange, error) { + return c.UnmaskUnitFilesContext(context.Background(), files, runtime) +} + +// UnmaskUnitFilesContext unmasks one or more units in the system. +// +// It takes the list of unit files to mask (either just file names or full +// absolute paths if the unit files are residing outside the usual unit search +// paths), and a boolean runtime flag to specify whether the unit was enabled +// for runtime only (true, /run/systemd/..), or persistently (false, +// /etc/systemd/..). +func (c *Conn) UnmaskUnitFilesContext(ctx context.Context, files []string, runtime bool) ([]UnmaskUnitFileChange, error) { + result := make([][]interface{}, 0) + err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.UnmaskUnitFiles", 0, files, runtime).Store(&result) + if err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + changes := make([]UnmaskUnitFileChange, len(result)) + changesInterface := make([]interface{}, len(changes)) + for i := range changes { + changesInterface[i] = &changes[i] + } + + err = dbus.Store(resultInterface, changesInterface...) + if err != nil { + return nil, err + } + + return changes, nil +} + +type UnmaskUnitFileChange struct { + Type string // Type of the change (one of symlink or unlink) + Filename string // File name of the symlink + Destination string // Destination of the symlink +} + +// Deprecated: use ReloadContext instead. +func (c *Conn) Reload() error { + return c.ReloadContext(context.Background()) +} + +// ReloadContext instructs systemd to scan for and reload unit files. This is +// an equivalent to systemctl daemon-reload. +func (c *Conn) ReloadContext(ctx context.Context) error { + return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.Reload", 0).Store() +} + +func unitPath(name string) dbus.ObjectPath { + return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name)) +} + +// unitName returns the unescaped base element of the supplied escaped path. +func unitName(dpath dbus.ObjectPath) string { + return pathBusUnescape(path.Base(string(dpath))) +} + +// JobStatus holds a currently queued job definition. +type JobStatus struct { + Id uint32 // The numeric job id + Unit string // The primary unit name for this job + JobType string // The job type as string + Status string // The job state as string + JobPath dbus.ObjectPath // The job object path + UnitPath dbus.ObjectPath // The unit object path +} + +// Deprecated: use ListJobsContext instead. +func (c *Conn) ListJobs() ([]JobStatus, error) { + return c.ListJobsContext(context.Background()) +} + +// ListJobsContext returns an array with all currently queued jobs. +func (c *Conn) ListJobsContext(ctx context.Context) ([]JobStatus, error) { + return c.listJobsInternal(ctx) +} + +func (c *Conn) listJobsInternal(ctx context.Context) ([]JobStatus, error) { + result := make([][]interface{}, 0) + if err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListJobs", 0).Store(&result); err != nil { + return nil, err + } + + resultInterface := make([]interface{}, len(result)) + for i := range result { + resultInterface[i] = result[i] + } + + status := make([]JobStatus, len(result)) + statusInterface := make([]interface{}, len(status)) + for i := range status { + statusInterface[i] = &status[i] + } + + if err := dbus.Store(resultInterface, statusInterface...); err != nil { + return nil, err + } + + return status, nil +} diff --git a/vendor/github.com/coreos/go-systemd/v22/dbus/properties.go b/vendor/github.com/coreos/go-systemd/v22/dbus/properties.go new file mode 100644 index 0000000000000..fb42b6273386c --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/dbus/properties.go @@ -0,0 +1,237 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +package dbus + +import ( + "github.com/godbus/dbus/v5" +) + +// From the systemd docs: +// +// The properties array of StartTransientUnit() may take many of the settings +// that may also be configured in unit files. Not all parameters are currently +// accepted though, but we plan to cover more properties with future release. +// Currently you may set the Description, Slice and all dependency types of +// units, as well as RemainAfterExit, ExecStart for service units, +// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares, +// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth, +// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit, +// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map +// directly to their counterparts in unit files and as normal D-Bus object +// properties. The exception here is the PIDs field of scope units which is +// used for construction of the scope only and specifies the initial PIDs to +// add to the scope object. + +type Property struct { + Name string + Value dbus.Variant +} + +type PropertyCollection struct { + Name string + Properties []Property +} + +type execStart struct { + Path string // the binary path to execute + Args []string // an array with all arguments to pass to the executed command, starting with argument 0 + UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly +} + +// PropExecStart sets the ExecStart service property. The first argument is a +// slice with the binary path to execute followed by the arguments to pass to +// the executed command. See +// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= +func PropExecStart(command []string, uncleanIsFailure bool) Property { + execStarts := []execStart{ + { + Path: command[0], + Args: command, + UncleanIsFailure: uncleanIsFailure, + }, + } + + return Property{ + Name: "ExecStart", + Value: dbus.MakeVariant(execStarts), + } +} + +// PropRemainAfterExit sets the RemainAfterExit service property. See +// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit= +func PropRemainAfterExit(b bool) Property { + return Property{ + Name: "RemainAfterExit", + Value: dbus.MakeVariant(b), + } +} + +// PropType sets the Type service property. See +// http://www.freedesktop.org/software/systemd/man/systemd.service.html#Type= +func PropType(t string) Property { + return Property{ + Name: "Type", + Value: dbus.MakeVariant(t), + } +} + +// PropDescription sets the Description unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description= +func PropDescription(desc string) Property { + return Property{ + Name: "Description", + Value: dbus.MakeVariant(desc), + } +} + +func propDependency(name string, units []string) Property { + return Property{ + Name: name, + Value: dbus.MakeVariant(units), + } +} + +// PropRequires sets the Requires unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires= +func PropRequires(units ...string) Property { + return propDependency("Requires", units) +} + +// PropRequiresOverridable sets the RequiresOverridable unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable= +func PropRequiresOverridable(units ...string) Property { + return propDependency("RequiresOverridable", units) +} + +// PropRequisite sets the Requisite unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite= +func PropRequisite(units ...string) Property { + return propDependency("Requisite", units) +} + +// PropRequisiteOverridable sets the RequisiteOverridable unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable= +func PropRequisiteOverridable(units ...string) Property { + return propDependency("RequisiteOverridable", units) +} + +// PropWants sets the Wants unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants= +func PropWants(units ...string) Property { + return propDependency("Wants", units) +} + +// PropBindsTo sets the BindsTo unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo= +func PropBindsTo(units ...string) Property { + return propDependency("BindsTo", units) +} + +// PropRequiredBy sets the RequiredBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy= +func PropRequiredBy(units ...string) Property { + return propDependency("RequiredBy", units) +} + +// PropRequiredByOverridable sets the RequiredByOverridable unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable= +func PropRequiredByOverridable(units ...string) Property { + return propDependency("RequiredByOverridable", units) +} + +// PropWantedBy sets the WantedBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy= +func PropWantedBy(units ...string) Property { + return propDependency("WantedBy", units) +} + +// PropBoundBy sets the BoundBy unit property. See +// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy= +func PropBoundBy(units ...string) Property { + return propDependency("BoundBy", units) +} + +// PropConflicts sets the Conflicts unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts= +func PropConflicts(units ...string) Property { + return propDependency("Conflicts", units) +} + +// PropConflictedBy sets the ConflictedBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy= +func PropConflictedBy(units ...string) Property { + return propDependency("ConflictedBy", units) +} + +// PropBefore sets the Before unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before= +func PropBefore(units ...string) Property { + return propDependency("Before", units) +} + +// PropAfter sets the After unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After= +func PropAfter(units ...string) Property { + return propDependency("After", units) +} + +// PropOnFailure sets the OnFailure unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure= +func PropOnFailure(units ...string) Property { + return propDependency("OnFailure", units) +} + +// PropTriggers sets the Triggers unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers= +func PropTriggers(units ...string) Property { + return propDependency("Triggers", units) +} + +// PropTriggeredBy sets the TriggeredBy unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy= +func PropTriggeredBy(units ...string) Property { + return propDependency("TriggeredBy", units) +} + +// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo= +func PropPropagatesReloadTo(units ...string) Property { + return propDependency("PropagatesReloadTo", units) +} + +// PropRequiresMountsFor sets the RequiresMountsFor unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor= +func PropRequiresMountsFor(units ...string) Property { + return propDependency("RequiresMountsFor", units) +} + +// PropSlice sets the Slice unit property. See +// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice= +func PropSlice(slice string) Property { + return Property{ + Name: "Slice", + Value: dbus.MakeVariant(slice), + } +} + +// PropPids sets the PIDs field of scope units used in the initial construction +// of the scope only and specifies the initial PIDs to add to the scope object. +// See https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/#properties +func PropPids(pids ...uint32) Property { + return Property{ + Name: "PIDs", + Value: dbus.MakeVariant(pids), + } +} diff --git a/vendor/github.com/coreos/go-systemd/dbus/set.go b/vendor/github.com/coreos/go-systemd/v22/dbus/set.go similarity index 100% rename from vendor/github.com/coreos/go-systemd/dbus/set.go rename to vendor/github.com/coreos/go-systemd/v22/dbus/set.go diff --git a/vendor/github.com/coreos/go-systemd/dbus/subscription.go b/vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go similarity index 98% rename from vendor/github.com/coreos/go-systemd/dbus/subscription.go rename to vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go index 70e63a6f16e15..7e370fea2121a 100644 --- a/vendor/github.com/coreos/go-systemd/dbus/subscription.go +++ b/vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go @@ -19,7 +19,7 @@ import ( "log" "time" - "github.com/godbus/dbus" + "github.com/godbus/dbus/v5" ) const ( @@ -94,7 +94,7 @@ func (c *Conn) dispatch() { }() } -// Returns two unbuffered channels which will receive all changed units every +// SubscribeUnits returns two unbuffered channels which will receive all changed units every // interval. Deleted units are sent as nil. func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) { return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil) diff --git a/vendor/github.com/coreos/go-systemd/dbus/subscription_set.go b/vendor/github.com/coreos/go-systemd/v22/dbus/subscription_set.go similarity index 100% rename from vendor/github.com/coreos/go-systemd/dbus/subscription_set.go rename to vendor/github.com/coreos/go-systemd/v22/dbus/subscription_set.go diff --git a/vendor/github.com/coreos/go-systemd/v22/go.mod b/vendor/github.com/coreos/go-systemd/v22/go.mod new file mode 100644 index 0000000000000..d285cf367e664 --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/go.mod @@ -0,0 +1,5 @@ +module github.com/coreos/go-systemd/v22 + +go 1.12 + +require github.com/godbus/dbus/v5 v5.0.4 diff --git a/vendor/github.com/coreos/go-systemd/v22/journal/journal.go b/vendor/github.com/coreos/go-systemd/v22/journal/journal.go new file mode 100644 index 0000000000000..ac24c7767d352 --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/journal/journal.go @@ -0,0 +1,46 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +// Package journal provides write bindings to the local systemd journal. +// It is implemented in pure Go and connects to the journal directly over its +// unix socket. +// +// To read from the journal, see the "sdjournal" package, which wraps the +// sd-journal a C API. +// +// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html +package journal + +import ( + "fmt" +) + +// Priority of a journal message +type Priority int + +const ( + PriEmerg Priority = iota + PriAlert + PriCrit + PriErr + PriWarning + PriNotice + PriInfo + PriDebug +) + +// Print prints a message to the local systemd journal using Send(). +func Print(priority Priority, format string, a ...interface{}) error { + return Send(fmt.Sprintf(format, a...), priority, nil) +} diff --git a/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go b/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go new file mode 100644 index 0000000000000..8d58ca0fbca04 --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go @@ -0,0 +1,210 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +// +build !windows + +// Package journal provides write bindings to the local systemd journal. +// It is implemented in pure Go and connects to the journal directly over its +// unix socket. +// +// To read from the journal, see the "sdjournal" package, which wraps the +// sd-journal a C API. +// +// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html +package journal + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "os" + "strconv" + "strings" + "sync" + "sync/atomic" + "syscall" + "unsafe" +) + +var ( + // This can be overridden at build-time: + // https://github.com/golang/go/wiki/GcToolchainTricks#including-build-information-in-the-executable + journalSocket = "/run/systemd/journal/socket" + + // unixConnPtr atomically holds the local unconnected Unix-domain socket. + // Concrete safe pointer type: *net.UnixConn + unixConnPtr unsafe.Pointer + // onceConn ensures that unixConnPtr is initialized exactly once. + onceConn sync.Once +) + +func init() { + onceConn.Do(initConn) +} + +// Enabled checks whether the local systemd journal is available for logging. +func Enabled() bool { + onceConn.Do(initConn) + + if (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) == nil { + return false + } + + conn, err := net.Dial("unixgram", journalSocket) + if err != nil { + return false + } + defer conn.Close() + + return true +} + +// Send a message to the local systemd journal. vars is a map of journald +// fields to values. Fields must be composed of uppercase letters, numbers, +// and underscores, but must not start with an underscore. Within these +// restrictions, any arbitrary field name may be used. Some names have special +// significance: see the journalctl documentation +// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html) +// for more details. vars may be nil. +func Send(message string, priority Priority, vars map[string]string) error { + conn := (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) + if conn == nil { + return errors.New("could not initialize socket to journald") + } + + socketAddr := &net.UnixAddr{ + Name: journalSocket, + Net: "unixgram", + } + + data := new(bytes.Buffer) + appendVariable(data, "PRIORITY", strconv.Itoa(int(priority))) + appendVariable(data, "MESSAGE", message) + for k, v := range vars { + appendVariable(data, k, v) + } + + _, _, err := conn.WriteMsgUnix(data.Bytes(), nil, socketAddr) + if err == nil { + return nil + } + if !isSocketSpaceError(err) { + return err + } + + // Large log entry, send it via tempfile and ancillary-fd. + file, err := tempFd() + if err != nil { + return err + } + defer file.Close() + _, err = io.Copy(file, data) + if err != nil { + return err + } + rights := syscall.UnixRights(int(file.Fd())) + _, _, err = conn.WriteMsgUnix([]byte{}, rights, socketAddr) + if err != nil { + return err + } + + return nil +} + +func appendVariable(w io.Writer, name, value string) { + if err := validVarName(name); err != nil { + fmt.Fprintf(os.Stderr, "variable name %s contains invalid character, ignoring\n", name) + } + if strings.ContainsRune(value, '\n') { + /* When the value contains a newline, we write: + * - the variable name, followed by a newline + * - the size (in 64bit little endian format) + * - the data, followed by a newline + */ + fmt.Fprintln(w, name) + binary.Write(w, binary.LittleEndian, uint64(len(value))) + fmt.Fprintln(w, value) + } else { + /* just write the variable and value all on one line */ + fmt.Fprintf(w, "%s=%s\n", name, value) + } +} + +// validVarName validates a variable name to make sure journald will accept it. +// The variable name must be in uppercase and consist only of characters, +// numbers and underscores, and may not begin with an underscore: +// https://www.freedesktop.org/software/systemd/man/sd_journal_print.html +func validVarName(name string) error { + if name == "" { + return errors.New("Empty variable name") + } else if name[0] == '_' { + return errors.New("Variable name begins with an underscore") + } + + for _, c := range name { + if !(('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_') { + return errors.New("Variable name contains invalid characters") + } + } + return nil +} + +// isSocketSpaceError checks whether the error is signaling +// an "overlarge message" condition. +func isSocketSpaceError(err error) bool { + opErr, ok := err.(*net.OpError) + if !ok || opErr == nil { + return false + } + + sysErr, ok := opErr.Err.(*os.SyscallError) + if !ok || sysErr == nil { + return false + } + + return sysErr.Err == syscall.EMSGSIZE || sysErr.Err == syscall.ENOBUFS +} + +// tempFd creates a temporary, unlinked file under `/dev/shm`. +func tempFd() (*os.File, error) { + file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX") + if err != nil { + return nil, err + } + err = syscall.Unlink(file.Name()) + if err != nil { + return nil, err + } + return file, nil +} + +// initConn initializes the global `unixConnPtr` socket. +// It is meant to be called exactly once, at program startup. +func initConn() { + autobind, err := net.ResolveUnixAddr("unixgram", "") + if err != nil { + return + } + + sock, err := net.ListenUnixgram("unixgram", autobind) + if err != nil { + return + } + + atomic.StorePointer(&unixConnPtr, unsafe.Pointer(sock)) +} diff --git a/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go b/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go new file mode 100644 index 0000000000000..677aca68ed20a --- /dev/null +++ b/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go @@ -0,0 +1,35 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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. + +// Package journal provides write bindings to the local systemd journal. +// It is implemented in pure Go and connects to the journal directly over its +// unix socket. +// +// To read from the journal, see the "sdjournal" package, which wraps the +// sd-journal a C API. +// +// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html +package journal + +import ( + "errors" +) + +func Enabled() bool { + return false +} + +func Send(message string, priority Priority, vars map[string]string) error { + return errors.New("could not initialize socket to journald") +} diff --git a/vendor/github.com/coreos/pkg/capnslog/README.md b/vendor/github.com/coreos/pkg/capnslog/README.md index 81efb1fb6a7bd..f79dbfca5cbe5 100644 --- a/vendor/github.com/coreos/pkg/capnslog/README.md +++ b/vendor/github.com/coreos/pkg/capnslog/README.md @@ -21,7 +21,7 @@ Still the job of `main` to expose these configurations. `main` may delegate this Splitting streams is probably not the job of your program, but rather, your log aggregation framework. If you must split output streams, again, `main` configures this and you can write a very simple two-output struct that satisfies io.Writer. -Fancy colorful formatting and JSON output are beyond the scope of a basic logging framework -- they're application/log-collector dependant. These are, at best, provided as options, but more likely, provided by your application. +Fancy colorful formatting and JSON output are beyond the scope of a basic logging framework -- they're application/log-collector dependent. These are, at best, provided as options, but more likely, provided by your application. ##### Log objects are an interface diff --git a/vendor/github.com/coreos/pkg/capnslog/logmap.go b/vendor/github.com/coreos/pkg/capnslog/logmap.go index 84954488308db..226b60c22534e 100644 --- a/vendor/github.com/coreos/pkg/capnslog/logmap.go +++ b/vendor/github.com/coreos/pkg/capnslog/logmap.go @@ -95,6 +95,11 @@ func (l *LogLevel) Set(s string) error { return nil } +// Returns an empty string, only here to fulfill the pflag.Value interface. +func (l *LogLevel) Type() string { + return "" +} + // ParseLevel translates some potential loglevel strings into their corresponding levels. func ParseLevel(s string) (LogLevel, error) { switch s { diff --git a/vendor/github.com/coreos/pkg/capnslog/pkg_logger.go b/vendor/github.com/coreos/pkg/capnslog/pkg_logger.go index 612d55c66c802..00ff37149aeb5 100644 --- a/vendor/github.com/coreos/pkg/capnslog/pkg_logger.go +++ b/vendor/github.com/coreos/pkg/capnslog/pkg_logger.go @@ -37,6 +37,14 @@ func (p *PackageLogger) internalLog(depth int, inLevel LogLevel, entries ...inte } } +// SetLevel allows users to change the current logging level. +func (p *PackageLogger) SetLevel(l LogLevel) { + logger.Lock() + defer logger.Unlock() + p.level = l +} + +// LevelAt checks if the given log level will be outputted under current setting. func (p *PackageLogger) LevelAt(l LogLevel) bool { logger.Lock() defer logger.Unlock() @@ -81,6 +89,12 @@ func (p *PackageLogger) Panic(args ...interface{}) { panic(s) } +func (p *PackageLogger) Panicln(args ...interface{}) { + s := fmt.Sprintln(args...) + p.internalLog(calldepth, CRITICAL, s) + panic(s) +} + func (p *PackageLogger) Fatalf(format string, args ...interface{}) { p.Logf(CRITICAL, format, args...) os.Exit(1) diff --git a/vendor/github.com/kr/pty/License b/vendor/github.com/creack/pty/LICENSE similarity index 100% rename from vendor/github.com/kr/pty/License rename to vendor/github.com/creack/pty/LICENSE diff --git a/vendor/github.com/creack/pty/README.md b/vendor/github.com/creack/pty/README.md new file mode 100644 index 0000000000000..5275014a7a71e --- /dev/null +++ b/vendor/github.com/creack/pty/README.md @@ -0,0 +1,100 @@ +# pty + +Pty is a Go package for using unix pseudo-terminals. + +## Install + + go get github.com/creack/pty + +## Example + +### Command + +```go +package main + +import ( + "github.com/creack/pty" + "io" + "os" + "os/exec" +) + +func main() { + c := exec.Command("grep", "--color=auto", "bar") + f, err := pty.Start(c) + if err != nil { + panic(err) + } + + go func() { + f.Write([]byte("foo\n")) + f.Write([]byte("bar\n")) + f.Write([]byte("baz\n")) + f.Write([]byte{4}) // EOT + }() + io.Copy(os.Stdout, f) +} +``` + +### Shell + +```go +package main + +import ( + "io" + "log" + "os" + "os/exec" + "os/signal" + "syscall" + + "github.com/creack/pty" + "golang.org/x/crypto/ssh/terminal" +) + +func test() error { + // Create arbitrary command. + c := exec.Command("bash") + + // Start the command with a pty. + ptmx, err := pty.Start(c) + if err != nil { + return err + } + // Make sure to close the pty at the end. + defer func() { _ = ptmx.Close() }() // Best effort. + + // Handle pty size. + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGWINCH) + go func() { + for range ch { + if err := pty.InheritSize(os.Stdin, ptmx); err != nil { + log.Printf("error resizing pty: %s", err) + } + } + }() + ch <- syscall.SIGWINCH // Initial resize. + + // Set stdin in raw mode. + oldState, err := terminal.MakeRaw(int(os.Stdin.Fd())) + if err != nil { + panic(err) + } + defer func() { _ = terminal.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort. + + // Copy stdin to the pty and the pty to stdout. + go func() { _, _ = io.Copy(ptmx, os.Stdin) }() + _, _ = io.Copy(os.Stdout, ptmx) + + return nil +} + +func main() { + if err := test(); err != nil { + log.Fatal(err) + } +} +``` diff --git a/vendor/github.com/kr/pty/doc.go b/vendor/github.com/creack/pty/doc.go similarity index 100% rename from vendor/github.com/kr/pty/doc.go rename to vendor/github.com/creack/pty/doc.go diff --git a/vendor/github.com/creack/pty/go.mod b/vendor/github.com/creack/pty/go.mod new file mode 100644 index 0000000000000..e48decaf469a5 --- /dev/null +++ b/vendor/github.com/creack/pty/go.mod @@ -0,0 +1,4 @@ +module github.com/creack/pty + +go 1.13 + diff --git a/vendor/github.com/creack/pty/ioctl.go b/vendor/github.com/creack/pty/ioctl.go new file mode 100644 index 0000000000000..c85cdcd14a014 --- /dev/null +++ b/vendor/github.com/creack/pty/ioctl.go @@ -0,0 +1,13 @@ +// +build !windows,!solaris + +package pty + +import "syscall" + +func ioctl(fd, cmd, ptr uintptr) error { + _, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr) + if e != 0 { + return e + } + return nil +} diff --git a/vendor/github.com/kr/pty/ioctl_bsd.go b/vendor/github.com/creack/pty/ioctl_bsd.go similarity index 100% rename from vendor/github.com/kr/pty/ioctl_bsd.go rename to vendor/github.com/creack/pty/ioctl_bsd.go diff --git a/vendor/github.com/creack/pty/ioctl_solaris.go b/vendor/github.com/creack/pty/ioctl_solaris.go new file mode 100644 index 0000000000000..f63985f34ce36 --- /dev/null +++ b/vendor/github.com/creack/pty/ioctl_solaris.go @@ -0,0 +1,30 @@ +package pty + +import ( + "golang.org/x/sys/unix" + "unsafe" +) + +const ( + // see /usr/include/sys/stropts.h + I_PUSH = uintptr((int32('S')<<8 | 002)) + I_STR = uintptr((int32('S')<<8 | 010)) + I_FIND = uintptr((int32('S')<<8 | 013)) + // see /usr/include/sys/ptms.h + ISPTM = (int32('P') << 8) | 1 + UNLKPT = (int32('P') << 8) | 2 + PTSSTTY = (int32('P') << 8) | 3 + ZONEPT = (int32('P') << 8) | 4 + OWNERPT = (int32('P') << 8) | 5 +) + +type strioctl struct { + ic_cmd int32 + ic_timout int32 + ic_len int32 + ic_dp unsafe.Pointer +} + +func ioctl(fd, cmd, ptr uintptr) error { + return unix.IoctlSetInt(int(fd), uint(cmd), int(ptr)) +} diff --git a/vendor/github.com/creack/pty/pty_darwin.go b/vendor/github.com/creack/pty/pty_darwin.go new file mode 100644 index 0000000000000..6344b6b0efb64 --- /dev/null +++ b/vendor/github.com/creack/pty/pty_darwin.go @@ -0,0 +1,65 @@ +package pty + +import ( + "errors" + "os" + "syscall" + "unsafe" +) + +func open() (pty, tty *os.File, err error) { + pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0) + if err != nil { + return nil, nil, err + } + p := os.NewFile(uintptr(pFD), "/dev/ptmx") + // In case of error after this point, make sure we close the ptmx fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + if err := grantpt(p); err != nil { + return nil, nil, err + } + + if err := unlockpt(p); err != nil { + return nil, nil, err + } + + t, err := os.OpenFile(sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func ptsname(f *os.File) (string, error) { + n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME)) + + err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0]))) + if err != nil { + return "", err + } + + for i, c := range n { + if c == 0 { + return string(n[:i]), nil + } + } + return "", errors.New("TIOCPTYGNAME string not NUL-terminated") +} + +func grantpt(f *os.File) error { + return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0) +} + +func unlockpt(f *os.File) error { + return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0) +} diff --git a/vendor/github.com/creack/pty/pty_dragonfly.go b/vendor/github.com/creack/pty/pty_dragonfly.go new file mode 100644 index 0000000000000..b7d1f20f29e52 --- /dev/null +++ b/vendor/github.com/creack/pty/pty_dragonfly.go @@ -0,0 +1,80 @@ +package pty + +import ( + "errors" + "os" + "strings" + "syscall" + "unsafe" +) + +// same code as pty_darwin.go +func open() (pty, tty *os.File, err error) { + p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + // In case of error after this point, make sure we close the ptmx fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + if err := grantpt(p); err != nil { + return nil, nil, err + } + + if err := unlockpt(p); err != nil { + return nil, nil, err + } + + t, err := os.OpenFile(sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func grantpt(f *os.File) error { + _, err := isptmaster(f.Fd()) + return err +} + +func unlockpt(f *os.File) error { + _, err := isptmaster(f.Fd()) + return err +} + +func isptmaster(fd uintptr) (bool, error) { + err := ioctl(fd, syscall.TIOCISPTMASTER, 0) + return err == nil, err +} + +var ( + emptyFiodgnameArg fiodgnameArg + ioctl_FIODNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg)) +) + +func ptsname(f *os.File) (string, error) { + name := make([]byte, _C_SPECNAMELEN) + fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}} + + err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa))) + if err != nil { + return "", err + } + + for i, c := range name { + if c == 0 { + s := "/dev/" + string(name[:i]) + return strings.Replace(s, "ptm", "pts", -1), nil + } + } + return "", errors.New("TIOCPTYGNAME string not NUL-terminated") +} diff --git a/vendor/github.com/creack/pty/pty_freebsd.go b/vendor/github.com/creack/pty/pty_freebsd.go new file mode 100644 index 0000000000000..63b6d91337ae3 --- /dev/null +++ b/vendor/github.com/creack/pty/pty_freebsd.go @@ -0,0 +1,78 @@ +package pty + +import ( + "errors" + "os" + "syscall" + "unsafe" +) + +func posixOpenpt(oflag int) (fd int, err error) { + r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return fd, err +} + +func open() (pty, tty *os.File, err error) { + fd, err := posixOpenpt(syscall.O_RDWR | syscall.O_CLOEXEC) + if err != nil { + return nil, nil, err + } + p := os.NewFile(uintptr(fd), "/dev/pts") + // In case of error after this point, make sure we close the pts fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0) + if err != nil { + return nil, nil, err + } + return p, t, nil +} + +func isptmaster(fd uintptr) (bool, error) { + err := ioctl(fd, syscall.TIOCPTMASTER, 0) + return err == nil, err +} + +var ( + emptyFiodgnameArg fiodgnameArg + ioctlFIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg)) +) + +func ptsname(f *os.File) (string, error) { + master, err := isptmaster(f.Fd()) + if err != nil { + return "", err + } + if !master { + return "", syscall.EINVAL + } + + const n = _C_SPECNAMELEN + 1 + var ( + buf = make([]byte, n) + arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))} + ) + if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil { + return "", err + } + + for i, c := range buf { + if c == 0 { + return string(buf[:i]), nil + } + } + return "", errors.New("FIODGNAME string not NUL-terminated") +} diff --git a/vendor/github.com/kr/pty/pty_linux.go b/vendor/github.com/creack/pty/pty_linux.go similarity index 75% rename from vendor/github.com/kr/pty/pty_linux.go rename to vendor/github.com/creack/pty/pty_linux.go index cb901a21e0063..4a833de184997 100644 --- a/vendor/github.com/kr/pty/pty_linux.go +++ b/vendor/github.com/creack/pty/pty_linux.go @@ -12,14 +12,19 @@ func open() (pty, tty *os.File, err error) { if err != nil { return nil, nil, err } + // In case of error after this point, make sure we close the ptmx fd. + defer func() { + if err != nil { + _ = p.Close() // Best effort. + } + }() sname, err := ptsname(p) if err != nil { return nil, nil, err } - err = unlockpt(p) - if err != nil { + if err := unlockpt(p); err != nil { return nil, nil, err } @@ -41,6 +46,6 @@ func ptsname(f *os.File) (string, error) { func unlockpt(f *os.File) error { var u _C_int - // use TIOCSPTLCK with a zero valued arg to clear the slave pty lock + // use TIOCSPTLCK with a pointer to zero to clear the lock return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) } diff --git a/vendor/github.com/creack/pty/pty_openbsd.go b/vendor/github.com/creack/pty/pty_openbsd.go new file mode 100644 index 0000000000000..a6a35d1e677e8 --- /dev/null +++ b/vendor/github.com/creack/pty/pty_openbsd.go @@ -0,0 +1,33 @@ +package pty + +import ( + "os" + "syscall" + "unsafe" +) + +func open() (pty, tty *os.File, err error) { + /* + * from ptm(4): + * The PTMGET command allocates a free pseudo terminal, changes its + * ownership to the caller, revokes the access privileges for all previous + * users, opens the file descriptors for the pty and tty devices and + * returns them to the caller in struct ptmget. + */ + + p, err := os.OpenFile("/dev/ptm", os.O_RDWR|syscall.O_CLOEXEC, 0) + if err != nil { + return nil, nil, err + } + defer p.Close() + + var ptm ptmget + if err := ioctl(p.Fd(), uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil { + return nil, nil, err + } + + pty = os.NewFile(uintptr(ptm.Cfd), "/dev/ptm") + tty = os.NewFile(uintptr(ptm.Sfd), "/dev/ptm") + + return pty, tty, nil +} diff --git a/vendor/github.com/creack/pty/pty_solaris.go b/vendor/github.com/creack/pty/pty_solaris.go new file mode 100644 index 0000000000000..09ec1b7978a60 --- /dev/null +++ b/vendor/github.com/creack/pty/pty_solaris.go @@ -0,0 +1,139 @@ +package pty + +/* based on: +http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/pt.c +*/ + +import ( + "errors" + "golang.org/x/sys/unix" + "os" + "strconv" + "syscall" + "unsafe" +) + +const NODEV = ^uint64(0) + +func open() (pty, tty *os.File, err error) { + masterfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|unix.O_NOCTTY, 0) + //masterfd, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC|unix.O_NOCTTY, 0) + if err != nil { + return nil, nil, err + } + p := os.NewFile(uintptr(masterfd), "/dev/ptmx") + + sname, err := ptsname(p) + if err != nil { + return nil, nil, err + } + + err = grantpt(p) + if err != nil { + return nil, nil, err + } + + err = unlockpt(p) + if err != nil { + return nil, nil, err + } + + slavefd, err := syscall.Open(sname, os.O_RDWR|unix.O_NOCTTY, 0) + if err != nil { + return nil, nil, err + } + t := os.NewFile(uintptr(slavefd), sname) + + // pushing terminal driver STREAMS modules as per pts(7) + for _, mod := range([]string{"ptem", "ldterm", "ttcompat"}) { + err = streams_push(t, mod) + if err != nil { + return nil, nil, err + } + } + + return p, t, nil +} + +func minor(x uint64) uint64 { + return x & 0377 +} + +func ptsdev(fd uintptr) uint64 { + istr := strioctl{ISPTM, 0, 0, nil} + err := ioctl(fd, I_STR, uintptr(unsafe.Pointer(&istr))) + if err != nil { + return NODEV + } + var status unix.Stat_t + err = unix.Fstat(int(fd), &status) + if err != nil { + return NODEV + } + return uint64(minor(status.Rdev)) +} + +func ptsname(f *os.File) (string, error) { + dev := ptsdev(f.Fd()) + if dev == NODEV { + return "", errors.New("not a master pty") + } + fn := "/dev/pts/" + strconv.FormatInt(int64(dev), 10) + // access(2) creates the slave device (if the pty exists) + // F_OK == 0 (unistd.h) + err := unix.Access(fn, 0) + if err != nil { + return "", err + } + return fn, nil +} + +type pt_own struct { + pto_ruid int32 + pto_rgid int32 +} + +func grantpt(f *os.File) error { + if ptsdev(f.Fd()) == NODEV { + return errors.New("not a master pty") + } + var pto pt_own + pto.pto_ruid = int32(os.Getuid()) + // XXX should first attempt to get gid of DEFAULT_TTY_GROUP="tty" + pto.pto_rgid = int32(os.Getgid()) + var istr strioctl + istr.ic_cmd = OWNERPT + istr.ic_timout = 0 + istr.ic_len = int32(unsafe.Sizeof(istr)) + istr.ic_dp = unsafe.Pointer(&pto) + err := ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr))) + if err != nil { + return errors.New("access denied") + } + return nil +} + +func unlockpt(f *os.File) error { + istr := strioctl{UNLKPT, 0, 0, nil} + return ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr))) +} + +// push STREAMS modules if not already done so +func streams_push(f *os.File, mod string) error { + var err error + buf := []byte(mod) + // XXX I_FIND is not returning an error when the module + // is already pushed even though truss reports a return + // value of 1. A bug in the Go Solaris syscall interface? + // XXX without this we are at risk of the issue + // https://www.illumos.org/issues/9042 + // but since we are not using libc or XPG4.2, we should not be + // double-pushing modules + + err = ioctl(f.Fd(), I_FIND, uintptr(unsafe.Pointer(&buf[0]))) + if err != nil { + return nil + } + err = ioctl(f.Fd(), I_PUSH, uintptr(unsafe.Pointer(&buf[0]))) + return err +} diff --git a/vendor/github.com/creack/pty/pty_unsupported.go b/vendor/github.com/creack/pty/pty_unsupported.go new file mode 100644 index 0000000000000..ceb425b19c982 --- /dev/null +++ b/vendor/github.com/creack/pty/pty_unsupported.go @@ -0,0 +1,11 @@ +// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd,!solaris + +package pty + +import ( + "os" +) + +func open() (pty, tty *os.File, err error) { + return nil, nil, ErrUnsupported +} diff --git a/vendor/github.com/creack/pty/run.go b/vendor/github.com/creack/pty/run.go new file mode 100644 index 0000000000000..b07942514d6ad --- /dev/null +++ b/vendor/github.com/creack/pty/run.go @@ -0,0 +1,74 @@ +// +build !windows + +package pty + +import ( + "os" + "os/exec" + "syscall" +) + +// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout, +// and c.Stderr, calls c.Start, and returns the File of the tty's +// corresponding pty. +// +// Starts the process in a new session and sets the controlling terminal. +func Start(c *exec.Cmd) (pty *os.File, err error) { + return StartWithSize(c, nil) +} + +// StartWithSize assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout, +// and c.Stderr, calls c.Start, and returns the File of the tty's +// corresponding pty. +// +// This will resize the pty to the specified size before starting the command. +// Starts the process in a new session and sets the controlling terminal. +func StartWithSize(c *exec.Cmd, sz *Winsize) (pty *os.File, err error) { + if c.SysProcAttr == nil { + c.SysProcAttr = &syscall.SysProcAttr{} + } + c.SysProcAttr.Setsid = true + c.SysProcAttr.Setctty = true + return StartWithAttrs(c, sz, c.SysProcAttr) +} + +// StartWithAttrs assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout, +// and c.Stderr, calls c.Start, and returns the File of the tty's +// corresponding pty. +// +// This will resize the pty to the specified size before starting the command if a size is provided. +// The `attrs` parameter overrides the one set in c.SysProcAttr. +// +// This should generally not be needed. Used in some edge cases where it is needed to create a pty +// without a controlling terminal. +func StartWithAttrs(c *exec.Cmd, sz *Winsize, attrs *syscall.SysProcAttr) (pty *os.File, err error) { + pty, tty, err := Open() + if err != nil { + return nil, err + } + defer tty.Close() + + if sz != nil { + if err := Setsize(pty, sz); err != nil { + pty.Close() + return nil, err + } + } + if c.Stdout == nil { + c.Stdout = tty + } + if c.Stderr == nil { + c.Stderr = tty + } + if c.Stdin == nil { + c.Stdin = tty + } + + c.SysProcAttr = attrs + + if err := c.Start(); err != nil { + _ = pty.Close() + return nil, err + } + return pty, err +} diff --git a/vendor/github.com/creack/pty/util.go b/vendor/github.com/creack/pty/util.go new file mode 100644 index 0000000000000..8fdde0bab988c --- /dev/null +++ b/vendor/github.com/creack/pty/util.go @@ -0,0 +1,64 @@ +// +build !windows,!solaris + +package pty + +import ( + "os" + "syscall" + "unsafe" +) + +// InheritSize applies the terminal size of pty to tty. This should be run +// in a signal handler for syscall.SIGWINCH to automatically resize the tty when +// the pty receives a window size change notification. +func InheritSize(pty, tty *os.File) error { + size, err := GetsizeFull(pty) + if err != nil { + return err + } + err = Setsize(tty, size) + if err != nil { + return err + } + return nil +} + +// Setsize resizes t to s. +func Setsize(t *os.File, ws *Winsize) error { + return windowRectCall(ws, t.Fd(), syscall.TIOCSWINSZ) +} + +// GetsizeFull returns the full terminal size description. +func GetsizeFull(t *os.File) (size *Winsize, err error) { + var ws Winsize + err = windowRectCall(&ws, t.Fd(), syscall.TIOCGWINSZ) + return &ws, err +} + +// Getsize returns the number of rows (lines) and cols (positions +// in each line) in terminal t. +func Getsize(t *os.File) (rows, cols int, err error) { + ws, err := GetsizeFull(t) + return int(ws.Rows), int(ws.Cols), err +} + +// Winsize describes the terminal size. +type Winsize struct { + Rows uint16 // ws_row: Number of rows (in cells) + Cols uint16 // ws_col: Number of columns (in cells) + X uint16 // ws_xpixel: Width in pixels + Y uint16 // ws_ypixel: Height in pixels +} + +func windowRectCall(ws *Winsize, fd, a2 uintptr) error { + _, _, errno := syscall.Syscall( + syscall.SYS_IOCTL, + fd, + a2, + uintptr(unsafe.Pointer(ws)), + ) + if errno != 0 { + return syscall.Errno(errno) + } + return nil +} diff --git a/vendor/github.com/creack/pty/util_solaris.go b/vendor/github.com/creack/pty/util_solaris.go new file mode 100644 index 0000000000000..e8896924824b3 --- /dev/null +++ b/vendor/github.com/creack/pty/util_solaris.go @@ -0,0 +1,51 @@ +// + +package pty + +import ( + "os" + "golang.org/x/sys/unix" +) + +const ( + TIOCGWINSZ = 21608 // 'T' << 8 | 104 + TIOCSWINSZ = 21607 // 'T' << 8 | 103 +) + +// Winsize describes the terminal size. +type Winsize struct { + Rows uint16 // ws_row: Number of rows (in cells) + Cols uint16 // ws_col: Number of columns (in cells) + X uint16 // ws_xpixel: Width in pixels + Y uint16 // ws_ypixel: Height in pixels +} + +// GetsizeFull returns the full terminal size description. +func GetsizeFull(t *os.File) (size *Winsize, err error) { + var wsz *unix.Winsize + wsz, err = unix.IoctlGetWinsize(int(t.Fd()), TIOCGWINSZ) + + if err != nil { + return nil, err + } else { + return &Winsize{wsz.Row, wsz.Col, wsz.Xpixel, wsz.Ypixel}, nil + } +} + +// Get Windows Size +func Getsize(t *os.File) (rows, cols int, err error) { + var wsz *unix.Winsize + wsz, err = unix.IoctlGetWinsize(int(t.Fd()), TIOCGWINSZ) + + if err != nil { + return 80, 25, err + } else { + return int(wsz.Row), int(wsz.Col), nil + } +} + +// Setsize resizes t to s. +func Setsize(t *os.File, ws *Winsize) error { + wsz := unix.Winsize{ws.Rows, ws.Cols, ws.X, ws.Y} + return unix.IoctlSetWinsize(int(t.Fd()), TIOCSWINSZ, &wsz) +} diff --git a/vendor/github.com/kr/pty/ztypes_386.go b/vendor/github.com/creack/pty/ztypes_386.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_386.go rename to vendor/github.com/creack/pty/ztypes_386.go diff --git a/vendor/github.com/kr/pty/ztypes_amd64.go b/vendor/github.com/creack/pty/ztypes_amd64.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_amd64.go rename to vendor/github.com/creack/pty/ztypes_amd64.go diff --git a/vendor/github.com/kr/pty/ztypes_arm.go b/vendor/github.com/creack/pty/ztypes_arm.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_arm.go rename to vendor/github.com/creack/pty/ztypes_arm.go diff --git a/vendor/github.com/kr/pty/ztypes_arm64.go b/vendor/github.com/creack/pty/ztypes_arm64.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_arm64.go rename to vendor/github.com/creack/pty/ztypes_arm64.go diff --git a/vendor/github.com/creack/pty/ztypes_dragonfly_amd64.go b/vendor/github.com/creack/pty/ztypes_dragonfly_amd64.go new file mode 100644 index 0000000000000..6b0ba037f897c --- /dev/null +++ b/vendor/github.com/creack/pty/ztypes_dragonfly_amd64.go @@ -0,0 +1,14 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_dragonfly.go + +package pty + +const ( + _C_SPECNAMELEN = 0x3f +) + +type fiodgnameArg struct { + Name *byte + Len uint32 + Pad_cgo_0 [4]byte +} diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_386.go b/vendor/github.com/creack/pty/ztypes_freebsd_386.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_freebsd_386.go rename to vendor/github.com/creack/pty/ztypes_freebsd_386.go diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_amd64.go b/vendor/github.com/creack/pty/ztypes_freebsd_amd64.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_freebsd_amd64.go rename to vendor/github.com/creack/pty/ztypes_freebsd_amd64.go diff --git a/vendor/github.com/kr/pty/ztypes_freebsd_arm.go b/vendor/github.com/creack/pty/ztypes_freebsd_arm.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_freebsd_arm.go rename to vendor/github.com/creack/pty/ztypes_freebsd_arm.go diff --git a/vendor/github.com/creack/pty/ztypes_freebsd_arm64.go b/vendor/github.com/creack/pty/ztypes_freebsd_arm64.go new file mode 100644 index 0000000000000..4418139b26fdc --- /dev/null +++ b/vendor/github.com/creack/pty/ztypes_freebsd_arm64.go @@ -0,0 +1,13 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs types_freebsd.go + +package pty + +const ( + _C_SPECNAMELEN = 0xff +) + +type fiodgnameArg struct { + Len int32 + Buf *byte +} diff --git a/vendor/github.com/creack/pty/ztypes_mipsx.go b/vendor/github.com/creack/pty/ztypes_mipsx.go new file mode 100644 index 0000000000000..f0ce74086aef7 --- /dev/null +++ b/vendor/github.com/creack/pty/ztypes_mipsx.go @@ -0,0 +1,12 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types.go + +// +build linux +// +build mips mipsle mips64 mips64le + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/creack/pty/ztypes_openbsd_32bit_int.go b/vendor/github.com/creack/pty/ztypes_openbsd_32bit_int.go new file mode 100644 index 0000000000000..d7cab4a2abecc --- /dev/null +++ b/vendor/github.com/creack/pty/ztypes_openbsd_32bit_int.go @@ -0,0 +1,13 @@ +// +build openbsd +// +build 386 amd64 arm arm64 + +package pty + +type ptmget struct { + Cfd int32 + Sfd int32 + Cn [16]int8 + Sn [16]int8 +} + +var ioctl_PTMGET = 0x40287401 diff --git a/vendor/github.com/kr/pty/ztypes_ppc64.go b/vendor/github.com/creack/pty/ztypes_ppc64.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_ppc64.go rename to vendor/github.com/creack/pty/ztypes_ppc64.go diff --git a/vendor/github.com/kr/pty/ztypes_ppc64le.go b/vendor/github.com/creack/pty/ztypes_ppc64le.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_ppc64le.go rename to vendor/github.com/creack/pty/ztypes_ppc64le.go diff --git a/vendor/github.com/creack/pty/ztypes_riscvx.go b/vendor/github.com/creack/pty/ztypes_riscvx.go new file mode 100644 index 0000000000000..99eec8ecbe021 --- /dev/null +++ b/vendor/github.com/creack/pty/ztypes_riscvx.go @@ -0,0 +1,11 @@ +// Code generated by cmd/cgo -godefs; DO NOT EDIT. +// cgo -godefs types.go + +// +build riscv riscv64 + +package pty + +type ( + _C_int int32 + _C_uint uint32 +) diff --git a/vendor/github.com/kr/pty/ztypes_s390x.go b/vendor/github.com/creack/pty/ztypes_s390x.go similarity index 100% rename from vendor/github.com/kr/pty/ztypes_s390x.go rename to vendor/github.com/creack/pty/ztypes_s390x.go diff --git a/vendor/github.com/cyphar/filepath-securejoin/LICENSE b/vendor/github.com/cyphar/filepath-securejoin/LICENSE new file mode 100644 index 0000000000000..bec842f294f77 --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/LICENSE @@ -0,0 +1,28 @@ +Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. +Copyright (C) 2017 SUSE LLC. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/cyphar/filepath-securejoin/README.md b/vendor/github.com/cyphar/filepath-securejoin/README.md new file mode 100644 index 0000000000000..49b2baa9f35cb --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/README.md @@ -0,0 +1,65 @@ +## `filepath-securejoin` ## + +[![Build Status](https://travis-ci.org/cyphar/filepath-securejoin.svg?branch=master)](https://travis-ci.org/cyphar/filepath-securejoin) + +An implementation of `SecureJoin`, a [candidate for inclusion in the Go +standard library][go#20126]. The purpose of this function is to be a "secure" +alternative to `filepath.Join`, and in particular it provides certain +guarantees that are not provided by `filepath.Join`. + +This is the function prototype: + +```go +func SecureJoin(root, unsafePath string) (string, error) +``` + +This library **guarantees** the following: + +* If no error is set, the resulting string **must** be a child path of + `SecureJoin` and will not contain any symlink path components (they will all + be expanded). + +* When expanding symlinks, all symlink path components **must** be resolved + relative to the provided root. In particular, this can be considered a + userspace implementation of how `chroot(2)` operates on file paths. Note that + these symlinks will **not** be expanded lexically (`filepath.Clean` is not + called on the input before processing). + +* Non-existant path components are unaffected by `SecureJoin` (similar to + `filepath.EvalSymlinks`'s semantics). + +* The returned path will always be `filepath.Clean`ed and thus not contain any + `..` components. + +A (trivial) implementation of this function on GNU/Linux systems could be done +with the following (note that this requires root privileges and is far more +opaque than the implementation in this library, and also requires that +`readlink` is inside the `root` path): + +```go +package securejoin + +import ( + "os/exec" + "path/filepath" +) + +func SecureJoin(root, unsafePath string) (string, error) { + unsafePath = string(filepath.Separator) + unsafePath + cmd := exec.Command("chroot", root, + "readlink", "--canonicalize-missing", "--no-newline", unsafePath) + output, err := cmd.CombinedOutput() + if err != nil { + return "", err + } + expanded := string(output) + return filepath.Join(root, expanded), nil +} +``` + +[go#20126]: https://github.com/golang/go/issues/20126 + +### License ### + +The license of this project is the same as Go, which is a BSD 3-clause license +available in the `LICENSE` file. diff --git a/vendor/github.com/cyphar/filepath-securejoin/join.go b/vendor/github.com/cyphar/filepath-securejoin/join.go new file mode 100644 index 0000000000000..c4ca3d7130057 --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/join.go @@ -0,0 +1,134 @@ +// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved. +// Copyright (C) 2017 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package securejoin is an implementation of the hopefully-soon-to-be-included +// SecureJoin helper that is meant to be part of the "path/filepath" package. +// The purpose of this project is to provide a PoC implementation to make the +// SecureJoin proposal (https://github.com/golang/go/issues/20126) more +// tangible. +package securejoin + +import ( + "bytes" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/pkg/errors" +) + +// ErrSymlinkLoop is returned by SecureJoinVFS when too many symlinks have been +// evaluated in attempting to securely join the two given paths. +var ErrSymlinkLoop = errors.Wrap(syscall.ELOOP, "secure join") + +// IsNotExist tells you if err is an error that implies that either the path +// accessed does not exist (or path components don't exist). This is +// effectively a more broad version of os.IsNotExist. +func IsNotExist(err error) bool { + // If it's a bone-fide ENOENT just bail. + if os.IsNotExist(errors.Cause(err)) { + return true + } + + // Check that it's not actually an ENOTDIR, which in some cases is a more + // convoluted case of ENOENT (usually involving weird paths). + var errno error + switch err := errors.Cause(err).(type) { + case *os.PathError: + errno = err.Err + case *os.LinkError: + errno = err.Err + case *os.SyscallError: + errno = err.Err + } + return errno == syscall.ENOTDIR || errno == syscall.ENOENT +} + +// SecureJoinVFS joins the two given path components (similar to Join) except +// that the returned path is guaranteed to be scoped inside the provided root +// path (when evaluated). Any symbolic links in the path are evaluated with the +// given root treated as the root of the filesystem, similar to a chroot. The +// filesystem state is evaluated through the given VFS interface (if nil, the +// standard os.* family of functions are used). +// +// Note that the guarantees provided by this function only apply if the path +// components in the returned string are not modified (in other words are not +// replaced with symlinks on the filesystem) after this function has returned. +// Such a symlink race is necessarily out-of-scope of SecureJoin. +func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) { + // Use the os.* VFS implementation if none was specified. + if vfs == nil { + vfs = osVFS{} + } + + var path bytes.Buffer + n := 0 + for unsafePath != "" { + if n > 255 { + return "", ErrSymlinkLoop + } + + // Next path component, p. + i := strings.IndexRune(unsafePath, filepath.Separator) + var p string + if i == -1 { + p, unsafePath = unsafePath, "" + } else { + p, unsafePath = unsafePath[:i], unsafePath[i+1:] + } + + // Create a cleaned path, using the lexical semantics of /../a, to + // create a "scoped" path component which can safely be joined to fullP + // for evaluation. At this point, path.String() doesn't contain any + // symlink components. + cleanP := filepath.Clean(string(filepath.Separator) + path.String() + p) + if cleanP == string(filepath.Separator) { + path.Reset() + continue + } + fullP := filepath.Clean(root + cleanP) + + // Figure out whether the path is a symlink. + fi, err := vfs.Lstat(fullP) + if err != nil && !IsNotExist(err) { + return "", err + } + // Treat non-existent path components the same as non-symlinks (we + // can't do any better here). + if IsNotExist(err) || fi.Mode()&os.ModeSymlink == 0 { + path.WriteString(p) + path.WriteRune(filepath.Separator) + continue + } + + // Only increment when we actually dereference a link. + n++ + + // It's a symlink, expand it by prepending it to the yet-unparsed path. + dest, err := vfs.Readlink(fullP) + if err != nil { + return "", err + } + // Absolute symlinks reset any work we've already done. + if filepath.IsAbs(dest) { + path.Reset() + } + unsafePath = dest + string(filepath.Separator) + unsafePath + } + + // We have to clean path.String() here because it may contain '..' + // components that are entirely lexical, but would be misleading otherwise. + // And finally do a final clean to ensure that root is also lexically + // clean. + fullP := filepath.Clean(string(filepath.Separator) + path.String()) + return filepath.Clean(root + fullP), nil +} + +// SecureJoin is a wrapper around SecureJoinVFS that just uses the os.* library +// of functions as the VFS. If in doubt, use this function over SecureJoinVFS. +func SecureJoin(root, unsafePath string) (string, error) { + return SecureJoinVFS(root, unsafePath, nil) +} diff --git a/vendor/github.com/cyphar/filepath-securejoin/vendor.conf b/vendor/github.com/cyphar/filepath-securejoin/vendor.conf new file mode 100644 index 0000000000000..66bb574b955bc --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/vendor.conf @@ -0,0 +1 @@ +github.com/pkg/errors v0.8.0 diff --git a/vendor/github.com/cyphar/filepath-securejoin/vfs.go b/vendor/github.com/cyphar/filepath-securejoin/vfs.go new file mode 100644 index 0000000000000..a82a5eae11eb2 --- /dev/null +++ b/vendor/github.com/cyphar/filepath-securejoin/vfs.go @@ -0,0 +1,41 @@ +// Copyright (C) 2017 SUSE LLC. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securejoin + +import "os" + +// In future this should be moved into a separate package, because now there +// are several projects (umoci and go-mtree) that are using this sort of +// interface. + +// VFS is the minimal interface necessary to use SecureJoinVFS. A nil VFS is +// equivalent to using the standard os.* family of functions. This is mainly +// used for the purposes of mock testing, but also can be used to otherwise use +// SecureJoin with VFS-like system. +type VFS interface { + // Lstat returns a FileInfo describing the named file. If the file is a + // symbolic link, the returned FileInfo describes the symbolic link. Lstat + // makes no attempt to follow the link. These semantics are identical to + // os.Lstat. + Lstat(name string) (os.FileInfo, error) + + // Readlink returns the destination of the named symbolic link. These + // semantics are identical to os.Readlink. + Readlink(name string) (string, error) +} + +// osVFS is the "nil" VFS, in that it just passes everything through to the os +// module. +type osVFS struct{} + +// Lstat returns a FileInfo describing the named file. If the file is a +// symbolic link, the returned FileInfo describes the symbolic link. Lstat +// makes no attempt to follow the link. These semantics are identical to +// os.Lstat. +func (o osVFS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) } + +// Readlink returns the destination of the named symbolic link. These +// semantics are identical to os.Readlink. +func (o osVFS) Readlink(name string) (string, error) { return os.Readlink(name) } diff --git a/vendor/github.com/docker/distribution/blobs.go b/vendor/github.com/docker/distribution/blobs.go index 145b078532b8e..c0e9261be9321 100644 --- a/vendor/github.com/docker/distribution/blobs.go +++ b/vendor/github.com/docker/distribution/blobs.go @@ -10,6 +10,7 @@ import ( "github.com/docker/distribution/reference" "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/specs-go/v1" ) var ( @@ -66,12 +67,19 @@ type Descriptor struct { Size int64 `json:"size,omitempty"` // Digest uniquely identifies the content. A byte stream can be verified - // against against this digest. + // against this digest. Digest digest.Digest `json:"digest,omitempty"` // URLs contains the source URLs of this content. URLs []string `json:"urls,omitempty"` + // Annotations contains arbitrary metadata relating to the targeted content. + Annotations map[string]string `json:"annotations,omitempty"` + + // Platform describes the platform which the image in the manifest runs on. + // This should only be used when referring to a manifest. + Platform *v1.Platform `json:"platform,omitempty"` + // NOTE: Before adding a field here, please ensure that all // other options have been exhausted. Much of the type relationships // depend on the simplicity of this type. diff --git a/vendor/github.com/docker/distribution/errors.go b/vendor/github.com/docker/distribution/errors.go index 020d33258b916..8e0b788d6c5cb 100644 --- a/vendor/github.com/docker/distribution/errors.go +++ b/vendor/github.com/docker/distribution/errors.go @@ -20,6 +20,10 @@ var ErrManifestNotModified = errors.New("manifest not modified") // performed var ErrUnsupported = errors.New("operation unsupported") +// ErrSchemaV1Unsupported is returned when a client tries to upload a schema v1 +// manifest but the registry is configured to reject it +var ErrSchemaV1Unsupported = errors.New("manifest schema v1 unsupported") + // ErrTagUnknown is returned if the given tag is not known by the tag service type ErrTagUnknown struct { Tag string diff --git a/vendor/github.com/docker/distribution/manifest/manifestlist/manifestlist.go b/vendor/github.com/docker/distribution/manifest/manifestlist/manifestlist.go index 3aa0662d9fd06..54c8f3c94c394 100644 --- a/vendor/github.com/docker/distribution/manifest/manifestlist/manifestlist.go +++ b/vendor/github.com/docker/distribution/manifest/manifestlist/manifestlist.go @@ -8,10 +8,13 @@ import ( "github.com/docker/distribution" "github.com/docker/distribution/manifest" "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/specs-go/v1" ) -// MediaTypeManifestList specifies the mediaType for manifest lists. -const MediaTypeManifestList = "application/vnd.docker.distribution.manifest.list.v2+json" +const ( + // MediaTypeManifestList specifies the mediaType for manifest lists. + MediaTypeManifestList = "application/vnd.docker.distribution.manifest.list.v2+json" +) // SchemaVersion provides a pre-initialized version structure for this // packages version of the manifest. @@ -20,6 +23,13 @@ var SchemaVersion = manifest.Versioned{ MediaType: MediaTypeManifestList, } +// OCISchemaVersion provides a pre-initialized version structure for this +// packages OCIschema version of the manifest. +var OCISchemaVersion = manifest.Versioned{ + SchemaVersion: 2, + MediaType: v1.MediaTypeImageIndex, +} + func init() { manifestListFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) { m := new(DeserializedManifestList) @@ -28,6 +38,13 @@ func init() { return nil, distribution.Descriptor{}, err } + if m.MediaType != MediaTypeManifestList { + err = fmt.Errorf("mediaType in manifest list should be '%s' not '%s'", + MediaTypeManifestList, m.MediaType) + + return nil, distribution.Descriptor{}, err + } + dgst := digest.FromBytes(b) return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifestList}, err } @@ -35,6 +52,28 @@ func init() { if err != nil { panic(fmt.Sprintf("Unable to register manifest: %s", err)) } + + imageIndexFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) { + m := new(DeserializedManifestList) + err := m.UnmarshalJSON(b) + if err != nil { + return nil, distribution.Descriptor{}, err + } + + if m.MediaType != "" && m.MediaType != v1.MediaTypeImageIndex { + err = fmt.Errorf("if present, mediaType in image index should be '%s' not '%s'", + v1.MediaTypeImageIndex, m.MediaType) + + return nil, distribution.Descriptor{}, err + } + + dgst := digest.FromBytes(b) + return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeImageIndex}, err + } + err = distribution.RegisterManifestSchema(v1.MediaTypeImageIndex, imageIndexFunc) + if err != nil { + panic(fmt.Sprintf("Unable to register OCI Image Index: %s", err)) + } } // PlatformSpec specifies a platform where a particular image manifest is @@ -105,11 +144,26 @@ type DeserializedManifestList struct { // DeserializedManifestList which contains the resulting manifest list // and its JSON representation. func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestList, error) { + var mediaType string + if len(descriptors) > 0 && descriptors[0].Descriptor.MediaType == v1.MediaTypeImageManifest { + mediaType = v1.MediaTypeImageIndex + } else { + mediaType = MediaTypeManifestList + } + + return FromDescriptorsWithMediaType(descriptors, mediaType) +} + +// FromDescriptorsWithMediaType is for testing purposes, it's useful to be able to specify the media type explicitly +func FromDescriptorsWithMediaType(descriptors []ManifestDescriptor, mediaType string) (*DeserializedManifestList, error) { m := ManifestList{ - Versioned: SchemaVersion, + Versioned: manifest.Versioned{ + SchemaVersion: 2, + MediaType: mediaType, + }, } - m.Manifests = make([]ManifestDescriptor, len(descriptors), len(descriptors)) + m.Manifests = make([]ManifestDescriptor, len(descriptors)) copy(m.Manifests, descriptors) deserialized := DeserializedManifestList{ @@ -123,7 +177,7 @@ func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestLis // UnmarshalJSON populates a new ManifestList struct from JSON data. func (m *DeserializedManifestList) UnmarshalJSON(b []byte) error { - m.canonical = make([]byte, len(b), len(b)) + m.canonical = make([]byte, len(b)) // store manifest list in canonical copy(m.canonical, b) @@ -151,5 +205,12 @@ func (m *DeserializedManifestList) MarshalJSON() ([]byte, error) { // Payload returns the raw content of the manifest list. The contents can be // used to calculate the content identifier. func (m DeserializedManifestList) Payload() (string, []byte, error) { - return m.MediaType, m.canonical, nil + var mediaType string + if m.MediaType == "" { + mediaType = v1.MediaTypeImageIndex + } else { + mediaType = m.MediaType + } + + return mediaType, m.canonical, nil } diff --git a/vendor/github.com/docker/distribution/manifest/ocischema/builder.go b/vendor/github.com/docker/distribution/manifest/ocischema/builder.go new file mode 100644 index 0000000000000..d90453bcf8671 --- /dev/null +++ b/vendor/github.com/docker/distribution/manifest/ocischema/builder.go @@ -0,0 +1,107 @@ +package ocischema + +import ( + "context" + "errors" + + "github.com/docker/distribution" + "github.com/docker/distribution/manifest" + "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/specs-go/v1" +) + +// Builder is a type for constructing manifests. +type Builder struct { + // bs is a BlobService used to publish the configuration blob. + bs distribution.BlobService + + // configJSON references + configJSON []byte + + // layers is a list of layer descriptors that gets built by successive + // calls to AppendReference. + layers []distribution.Descriptor + + // Annotations contains arbitrary metadata relating to the targeted content. + annotations map[string]string + + // For testing purposes + mediaType string +} + +// NewManifestBuilder is used to build new manifests for the current schema +// version. It takes a BlobService so it can publish the configuration blob +// as part of the Build process, and annotations. +func NewManifestBuilder(bs distribution.BlobService, configJSON []byte, annotations map[string]string) distribution.ManifestBuilder { + mb := &Builder{ + bs: bs, + configJSON: make([]byte, len(configJSON)), + annotations: annotations, + mediaType: v1.MediaTypeImageManifest, + } + copy(mb.configJSON, configJSON) + + return mb +} + +// SetMediaType assigns the passed mediatype or error if the mediatype is not a +// valid media type for oci image manifests currently: "" or "application/vnd.oci.image.manifest.v1+json" +func (mb *Builder) SetMediaType(mediaType string) error { + if mediaType != "" && mediaType != v1.MediaTypeImageManifest { + return errors.New("invalid media type for OCI image manifest") + } + + mb.mediaType = mediaType + return nil +} + +// Build produces a final manifest from the given references. +func (mb *Builder) Build(ctx context.Context) (distribution.Manifest, error) { + m := Manifest{ + Versioned: manifest.Versioned{ + SchemaVersion: 2, + MediaType: mb.mediaType, + }, + Layers: make([]distribution.Descriptor, len(mb.layers)), + Annotations: mb.annotations, + } + copy(m.Layers, mb.layers) + + configDigest := digest.FromBytes(mb.configJSON) + + var err error + m.Config, err = mb.bs.Stat(ctx, configDigest) + switch err { + case nil: + // Override MediaType, since Put always replaces the specified media + // type with application/octet-stream in the descriptor it returns. + m.Config.MediaType = v1.MediaTypeImageConfig + return FromStruct(m) + case distribution.ErrBlobUnknown: + // nop + default: + return nil, err + } + + // Add config to the blob store + m.Config, err = mb.bs.Put(ctx, v1.MediaTypeImageConfig, mb.configJSON) + // Override MediaType, since Put always replaces the specified media + // type with application/octet-stream in the descriptor it returns. + m.Config.MediaType = v1.MediaTypeImageConfig + if err != nil { + return nil, err + } + + return FromStruct(m) +} + +// AppendReference adds a reference to the current ManifestBuilder. +func (mb *Builder) AppendReference(d distribution.Describable) error { + mb.layers = append(mb.layers, d.Descriptor()) + return nil +} + +// References returns the current references added to this builder. +func (mb *Builder) References() []distribution.Descriptor { + return mb.layers +} diff --git a/vendor/github.com/docker/distribution/manifest/ocischema/manifest.go b/vendor/github.com/docker/distribution/manifest/ocischema/manifest.go new file mode 100644 index 0000000000000..b8c4bab547094 --- /dev/null +++ b/vendor/github.com/docker/distribution/manifest/ocischema/manifest.go @@ -0,0 +1,124 @@ +package ocischema + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/docker/distribution" + "github.com/docker/distribution/manifest" + "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/specs-go/v1" +) + +var ( + // SchemaVersion provides a pre-initialized version structure for this + // packages version of the manifest. + SchemaVersion = manifest.Versioned{ + SchemaVersion: 2, // historical value here.. does not pertain to OCI or docker version + MediaType: v1.MediaTypeImageManifest, + } +) + +func init() { + ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) { + m := new(DeserializedManifest) + err := m.UnmarshalJSON(b) + if err != nil { + return nil, distribution.Descriptor{}, err + } + + dgst := digest.FromBytes(b) + return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeImageManifest}, err + } + err := distribution.RegisterManifestSchema(v1.MediaTypeImageManifest, ocischemaFunc) + if err != nil { + panic(fmt.Sprintf("Unable to register manifest: %s", err)) + } +} + +// Manifest defines a ocischema manifest. +type Manifest struct { + manifest.Versioned + + // Config references the image configuration as a blob. + Config distribution.Descriptor `json:"config"` + + // Layers lists descriptors for the layers referenced by the + // configuration. + Layers []distribution.Descriptor `json:"layers"` + + // Annotations contains arbitrary metadata for the image manifest. + Annotations map[string]string `json:"annotations,omitempty"` +} + +// References returns the descriptors of this manifests references. +func (m Manifest) References() []distribution.Descriptor { + references := make([]distribution.Descriptor, 0, 1+len(m.Layers)) + references = append(references, m.Config) + references = append(references, m.Layers...) + return references +} + +// Target returns the target of this manifest. +func (m Manifest) Target() distribution.Descriptor { + return m.Config +} + +// DeserializedManifest wraps Manifest with a copy of the original JSON. +// It satisfies the distribution.Manifest interface. +type DeserializedManifest struct { + Manifest + + // canonical is the canonical byte representation of the Manifest. + canonical []byte +} + +// FromStruct takes a Manifest structure, marshals it to JSON, and returns a +// DeserializedManifest which contains the manifest and its JSON representation. +func FromStruct(m Manifest) (*DeserializedManifest, error) { + var deserialized DeserializedManifest + deserialized.Manifest = m + + var err error + deserialized.canonical, err = json.MarshalIndent(&m, "", " ") + return &deserialized, err +} + +// UnmarshalJSON populates a new Manifest struct from JSON data. +func (m *DeserializedManifest) UnmarshalJSON(b []byte) error { + m.canonical = make([]byte, len(b)) + // store manifest in canonical + copy(m.canonical, b) + + // Unmarshal canonical JSON into Manifest object + var manifest Manifest + if err := json.Unmarshal(m.canonical, &manifest); err != nil { + return err + } + + if manifest.MediaType != "" && manifest.MediaType != v1.MediaTypeImageManifest { + return fmt.Errorf("if present, mediaType in manifest should be '%s' not '%s'", + v1.MediaTypeImageManifest, manifest.MediaType) + } + + m.Manifest = manifest + + return nil +} + +// MarshalJSON returns the contents of canonical. If canonical is empty, +// marshals the inner contents. +func (m *DeserializedManifest) MarshalJSON() ([]byte, error) { + if len(m.canonical) > 0 { + return m.canonical, nil + } + + return nil, errors.New("JSON representation not initialized in DeserializedManifest") +} + +// Payload returns the raw content of the manifest. The contents can be used to +// calculate the content identifier. +func (m DeserializedManifest) Payload() (string, []byte, error) { + return v1.MediaTypeImageManifest, m.canonical, nil +} diff --git a/vendor/github.com/docker/distribution/manifest/schema1/manifest.go b/vendor/github.com/docker/distribution/manifest/schema1/manifest.go index 65042a75fc962..9fef4dc7eb772 100644 --- a/vendor/github.com/docker/distribution/manifest/schema1/manifest.go +++ b/vendor/github.com/docker/distribution/manifest/schema1/manifest.go @@ -108,7 +108,7 @@ type SignedManifest struct { // UnmarshalJSON populates a new SignedManifest struct from JSON data. func (sm *SignedManifest) UnmarshalJSON(b []byte) error { - sm.all = make([]byte, len(b), len(b)) + sm.all = make([]byte, len(b)) // store manifest and signatures in all copy(sm.all, b) @@ -124,7 +124,7 @@ func (sm *SignedManifest) UnmarshalJSON(b []byte) error { } // sm.Canonical stores the canonical manifest JSON - sm.Canonical = make([]byte, len(bytes), len(bytes)) + sm.Canonical = make([]byte, len(bytes)) copy(sm.Canonical, bytes) // Unmarshal canonical JSON into Manifest object @@ -138,7 +138,7 @@ func (sm *SignedManifest) UnmarshalJSON(b []byte) error { return nil } -// References returnes the descriptors of this manifests references +// References returns the descriptors of this manifests references func (sm SignedManifest) References() []distribution.Descriptor { dependencies := make([]distribution.Descriptor, len(sm.FSLayers)) for i, fsLayer := range sm.FSLayers { diff --git a/vendor/github.com/docker/distribution/manifest/schema1/reference_builder.go b/vendor/github.com/docker/distribution/manifest/schema1/reference_builder.go index a4f6032cd1e77..0f1d386aab478 100644 --- a/vendor/github.com/docker/distribution/manifest/schema1/reference_builder.go +++ b/vendor/github.com/docker/distribution/manifest/schema1/reference_builder.go @@ -58,7 +58,7 @@ func (mb *referenceManifestBuilder) Build(ctx context.Context) (distribution.Man func (mb *referenceManifestBuilder) AppendReference(d distribution.Describable) error { r, ok := d.(Reference) if !ok { - return fmt.Errorf("Unable to add non-reference type to v1 builder") + return fmt.Errorf("unable to add non-reference type to v1 builder") } // Entries need to be prepended diff --git a/vendor/github.com/docker/distribution/manifest/schema2/manifest.go b/vendor/github.com/docker/distribution/manifest/schema2/manifest.go index a2708c75098e0..41f480292019a 100644 --- a/vendor/github.com/docker/distribution/manifest/schema2/manifest.go +++ b/vendor/github.com/docker/distribution/manifest/schema2/manifest.go @@ -71,7 +71,7 @@ type Manifest struct { Layers []distribution.Descriptor `json:"layers"` } -// References returnes the descriptors of this manifests references. +// References returns the descriptors of this manifests references. func (m Manifest) References() []distribution.Descriptor { references := make([]distribution.Descriptor, 0, 1+len(m.Layers)) references = append(references, m.Config) @@ -79,7 +79,7 @@ func (m Manifest) References() []distribution.Descriptor { return references } -// Target returns the target of this signed manifest. +// Target returns the target of this manifest. func (m Manifest) Target() distribution.Descriptor { return m.Config } @@ -106,7 +106,7 @@ func FromStruct(m Manifest) (*DeserializedManifest, error) { // UnmarshalJSON populates a new Manifest struct from JSON data. func (m *DeserializedManifest) UnmarshalJSON(b []byte) error { - m.canonical = make([]byte, len(b), len(b)) + m.canonical = make([]byte, len(b)) // store manifest in canonical copy(m.canonical, b) @@ -116,6 +116,12 @@ func (m *DeserializedManifest) UnmarshalJSON(b []byte) error { return err } + if manifest.MediaType != MediaTypeManifest { + return fmt.Errorf("mediaType in manifest should be '%s' not '%s'", + MediaTypeManifest, manifest.MediaType) + + } + m.Manifest = manifest return nil diff --git a/vendor/github.com/docker/distribution/manifests.go b/vendor/github.com/docker/distribution/manifests.go index 1816baea1d653..8f84a220a97bb 100644 --- a/vendor/github.com/docker/distribution/manifests.go +++ b/vendor/github.com/docker/distribution/manifests.go @@ -87,7 +87,7 @@ func ManifestMediaTypes() (mediaTypes []string) { // UnmarshalFunc implements manifest unmarshalling a given MediaType type UnmarshalFunc func([]byte) (Manifest, Descriptor, error) -var mappings = make(map[string]UnmarshalFunc, 0) +var mappings = make(map[string]UnmarshalFunc) // UnmarshalManifest looks up manifest unmarshal functions based on // MediaType diff --git a/vendor/github.com/docker/distribution/reference/normalize.go b/vendor/github.com/docker/distribution/reference/normalize.go index 2d71fc5e9ffd9..b3dfb7a6d7e12 100644 --- a/vendor/github.com/docker/distribution/reference/normalize.go +++ b/vendor/github.com/docker/distribution/reference/normalize.go @@ -56,6 +56,35 @@ func ParseNormalizedNamed(s string) (Named, error) { return named, nil } +// ParseDockerRef normalizes the image reference following the docker convention. This is added +// mainly for backward compatibility. +// The reference returned can only be either tagged or digested. For reference contains both tag +// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@ +// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as +// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa. +func ParseDockerRef(ref string) (Named, error) { + named, err := ParseNormalizedNamed(ref) + if err != nil { + return nil, err + } + if _, ok := named.(NamedTagged); ok { + if canonical, ok := named.(Canonical); ok { + // The reference is both tagged and digested, only + // return digested. + newNamed, err := WithName(canonical.Name()) + if err != nil { + return nil, err + } + newCanonical, err := WithDigest(newNamed, canonical.Digest()) + if err != nil { + return nil, err + } + return newCanonical, nil + } + } + return TagNameOnly(named), nil +} + // splitDockerDomain splits a repository name to domain and remotename string. // If no valid domain is found, the default domain is used. Repository name // needs to be already validated before. diff --git a/vendor/github.com/docker/distribution/reference/reference.go b/vendor/github.com/docker/distribution/reference/reference.go index 2f66cca87a3c1..8c0c23b2fe1b7 100644 --- a/vendor/github.com/docker/distribution/reference/reference.go +++ b/vendor/github.com/docker/distribution/reference/reference.go @@ -205,7 +205,7 @@ func Parse(s string) (Reference, error) { var repo repository nameMatch := anchoredNameRegexp.FindStringSubmatch(matches[1]) - if nameMatch != nil && len(nameMatch) == 3 { + if len(nameMatch) == 3 { repo.domain = nameMatch[1] repo.path = nameMatch[2] } else { diff --git a/vendor/github.com/docker/distribution/registry.go b/vendor/github.com/docker/distribution/registry.go index a3a80ab884c20..6c32109894d5e 100644 --- a/vendor/github.com/docker/distribution/registry.go +++ b/vendor/github.com/docker/distribution/registry.go @@ -54,6 +54,11 @@ type RepositoryEnumerator interface { Enumerate(ctx context.Context, ingester func(string) error) error } +// RepositoryRemover removes given repository +type RepositoryRemover interface { + Remove(ctx context.Context, name reference.Named) error +} + // ManifestServiceOption is a function argument for Manifest Service methods type ManifestServiceOption interface { Apply(ManifestService) error diff --git a/vendor/github.com/docker/distribution/registry/api/errcode/errors.go b/vendor/github.com/docker/distribution/registry/api/errcode/errors.go index 6d9bb4b62afba..4c35b879afd8f 100644 --- a/vendor/github.com/docker/distribution/registry/api/errcode/errors.go +++ b/vendor/github.com/docker/distribution/registry/api/errcode/errors.go @@ -207,11 +207,11 @@ func (errs Errors) MarshalJSON() ([]byte, error) { for _, daErr := range errs { var err Error - switch daErr.(type) { + switch daErr := daErr.(type) { case ErrorCode: - err = daErr.(ErrorCode).WithDetail(nil) + err = daErr.WithDetail(nil) case Error: - err = daErr.(Error) + err = daErr default: err = ErrorCodeUnknown.WithDetail(daErr) diff --git a/vendor/github.com/docker/distribution/registry/api/v2/routes.go b/vendor/github.com/docker/distribution/registry/api/v2/routes.go index 5b80d5be76a5f..9612ac2e5a502 100644 --- a/vendor/github.com/docker/distribution/registry/api/v2/routes.go +++ b/vendor/github.com/docker/distribution/registry/api/v2/routes.go @@ -14,15 +14,6 @@ const ( RouteNameCatalog = "catalog" ) -var allEndpoints = []string{ - RouteNameManifest, - RouteNameCatalog, - RouteNameTags, - RouteNameBlob, - RouteNameBlobUpload, - RouteNameBlobUploadChunk, -} - // Router builds a gorilla router with named routes for the various API // methods. This can be used directly by both server implementations and // clients. diff --git a/vendor/github.com/docker/distribution/registry/api/v2/urls.go b/vendor/github.com/docker/distribution/registry/api/v2/urls.go index 1337bdb1276da..3c3ec9893306d 100644 --- a/vendor/github.com/docker/distribution/registry/api/v2/urls.go +++ b/vendor/github.com/docker/distribution/registry/api/v2/urls.go @@ -252,15 +252,3 @@ func appendValuesURL(u *url.URL, values ...url.Values) *url.URL { u.RawQuery = merged.Encode() return u } - -// appendValues appends the parameters to the url. Panics if the string is not -// a url. -func appendValues(u string, values ...url.Values) string { - up, err := url.Parse(u) - - if err != nil { - panic(err) // should never happen - } - - return appendValuesURL(up, values...).String() -} diff --git a/vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go b/vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go index 6e3f1ccc410df..fe238210cd7dc 100644 --- a/vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go +++ b/vendor/github.com/docker/distribution/registry/client/auth/challenge/authchallenge.go @@ -117,8 +117,8 @@ func init() { var t octetType isCtl := c <= 31 || c == 127 isChar := 0 <= c && c <= 127 - isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 - if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { + isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) + if strings.ContainsRune(" \t\r\n", rune(c)) { t |= isSpace } if isChar && !isCtl && !isSeparator { diff --git a/vendor/github.com/docker/distribution/registry/client/auth/session.go b/vendor/github.com/docker/distribution/registry/client/auth/session.go index db86c9b067c3d..aad8a0e6f5c6e 100644 --- a/vendor/github.com/docker/distribution/registry/client/auth/session.go +++ b/vendor/github.com/docker/distribution/registry/client/auth/session.go @@ -68,7 +68,6 @@ func NewAuthorizer(manager challenge.Manager, handlers ...AuthenticationHandler) type endpointAuthorizer struct { challenges challenge.Manager handlers []AuthenticationHandler - transport http.RoundTripper } func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error { @@ -121,7 +120,6 @@ type clock interface { } type tokenHandler struct { - header http.Header creds CredentialStore transport http.RoundTripper clock clock diff --git a/vendor/github.com/docker/distribution/registry/client/repository.go b/vendor/github.com/docker/distribution/registry/client/repository.go index d8e2c795d9536..aa442e654064c 100644 --- a/vendor/github.com/docker/distribution/registry/client/repository.go +++ b/vendor/github.com/docker/distribution/registry/client/repository.go @@ -81,9 +81,8 @@ func NewRegistry(baseURL string, transport http.RoundTripper) (Registry, error) } type registry struct { - client *http.Client - ub *v2.URLBuilder - context context.Context + client *http.Client + ub *v2.URLBuilder } // Repositories returns a lexigraphically sorted catalog given a base URL. The 'entries' slice will be filled up to the size @@ -152,10 +151,9 @@ func NewRepository(name reference.Named, baseURL string, transport http.RoundTri } type repository struct { - client *http.Client - ub *v2.URLBuilder - context context.Context - name reference.Named + client *http.Client + ub *v2.URLBuilder + name reference.Named } func (r *repository) Named() reference.Named { diff --git a/vendor/github.com/docker/distribution/registry/client/transport/http_reader.go b/vendor/github.com/docker/distribution/registry/client/transport/http_reader.go index e5ff09d756420..1d0b382fb511b 100644 --- a/vendor/github.com/docker/distribution/registry/client/transport/http_reader.go +++ b/vendor/github.com/docker/distribution/registry/client/transport/http_reader.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "net/http" - "os" "regexp" "strconv" ) @@ -97,7 +96,7 @@ func (hrs *httpReadSeeker) Seek(offset int64, whence int) (int64, error) { lastReaderOffset := hrs.readerOffset - if whence == os.SEEK_SET && hrs.rc == nil { + if whence == io.SeekStart && hrs.rc == nil { // If no request has been made yet, and we are seeking to an // absolute position, set the read offset as well to avoid an // unnecessary request. @@ -113,14 +112,14 @@ func (hrs *httpReadSeeker) Seek(offset int64, whence int) (int64, error) { newOffset := hrs.seekOffset switch whence { - case os.SEEK_CUR: + case io.SeekCurrent: newOffset += offset - case os.SEEK_END: + case io.SeekEnd: if hrs.size < 0 { return 0, errors.New("content length not known") } newOffset = hrs.size + offset - case os.SEEK_SET: + case io.SeekStart: newOffset = offset } diff --git a/vendor/github.com/docker/distribution/uuid/uuid.go b/vendor/github.com/docker/distribution/uuid/uuid.go deleted file mode 100644 index d433ccaf512d7..0000000000000 --- a/vendor/github.com/docker/distribution/uuid/uuid.go +++ /dev/null @@ -1,126 +0,0 @@ -// Package uuid provides simple UUID generation. Only version 4 style UUIDs -// can be generated. -// -// Please see http://tools.ietf.org/html/rfc4122 for details on UUIDs. -package uuid - -import ( - "crypto/rand" - "fmt" - "io" - "os" - "syscall" - "time" -) - -const ( - // Bits is the number of bits in a UUID - Bits = 128 - - // Size is the number of bytes in a UUID - Size = Bits / 8 - - format = "%08x-%04x-%04x-%04x-%012x" -) - -var ( - // ErrUUIDInvalid indicates a parsed string is not a valid uuid. - ErrUUIDInvalid = fmt.Errorf("invalid uuid") - - // Loggerf can be used to override the default logging destination. Such - // log messages in this library should be logged at warning or higher. - Loggerf = func(format string, args ...interface{}) {} -) - -// UUID represents a UUID value. UUIDs can be compared and set to other values -// and accessed by byte. -type UUID [Size]byte - -// Generate creates a new, version 4 uuid. -func Generate() (u UUID) { - const ( - // ensures we backoff for less than 450ms total. Use the following to - // select new value, in units of 10ms: - // n*(n+1)/2 = d -> n^2 + n - 2d -> n = (sqrt(8d + 1) - 1)/2 - maxretries = 9 - backoff = time.Millisecond * 10 - ) - - var ( - totalBackoff time.Duration - count int - retries int - ) - - for { - // This should never block but the read may fail. Because of this, - // we just try to read the random number generator until we get - // something. This is a very rare condition but may happen. - b := time.Duration(retries) * backoff - time.Sleep(b) - totalBackoff += b - - n, err := io.ReadFull(rand.Reader, u[count:]) - if err != nil { - if retryOnError(err) && retries < maxretries { - count += n - retries++ - Loggerf("error generating version 4 uuid, retrying: %v", err) - continue - } - - // Any other errors represent a system problem. What did someone - // do to /dev/urandom? - panic(fmt.Errorf("error reading random number generator, retried for %v: %v", totalBackoff.String(), err)) - } - - break - } - - u[6] = (u[6] & 0x0f) | 0x40 // set version byte - u[8] = (u[8] & 0x3f) | 0x80 // set high order byte 0b10{8,9,a,b} - - return u -} - -// Parse attempts to extract a uuid from the string or returns an error. -func Parse(s string) (u UUID, err error) { - if len(s) != 36 { - return UUID{}, ErrUUIDInvalid - } - - // create stack addresses for each section of the uuid. - p := make([][]byte, 5) - - if _, err := fmt.Sscanf(s, format, &p[0], &p[1], &p[2], &p[3], &p[4]); err != nil { - return u, err - } - - copy(u[0:4], p[0]) - copy(u[4:6], p[1]) - copy(u[6:8], p[2]) - copy(u[8:10], p[3]) - copy(u[10:16], p[4]) - - return -} - -func (u UUID) String() string { - return fmt.Sprintf(format, u[:4], u[4:6], u[6:8], u[8:10], u[10:]) -} - -// retryOnError tries to detect whether or not retrying would be fruitful. -func retryOnError(err error) bool { - switch err := err.(type) { - case *os.PathError: - return retryOnError(err.Err) // unpack the target error - case syscall.Errno: - if err == syscall.EPERM { - // EPERM represents an entropy pool exhaustion, a condition under - // which we backoff and retry. - return true - } - } - - return false -} diff --git a/vendor/github.com/docker/distribution/vendor.conf b/vendor/github.com/docker/distribution/vendor.conf index d0ebadf8b9b69..12f71672f3964 100644 --- a/vendor/github.com/docker/distribution/vendor.conf +++ b/vendor/github.com/docker/distribution/vendor.conf @@ -1,16 +1,15 @@ -github.com/Azure/azure-sdk-for-go 088007b3b08cc02b27f2eadfdcd870958460ce7e -github.com/Azure/go-autorest ec5f4903f77ed9927ac95b19ab8e44ada64c1356 +github.com/Azure/azure-sdk-for-go 4650843026a7fdec254a8d9cf893693a254edd0b +github.com/Azure/go-autorest eaa7994b2278094c904d31993d26f56324db3052 github.com/sirupsen/logrus 3d4380f53a34dcdc95f0c1db702615992b38d9a4 -github.com/aws/aws-sdk-go 5bcc0a238d880469f949fc7cd24e35f32ab80cbd +github.com/aws/aws-sdk-go f831d5a0822a1ad72420ab18c6269bca1ddaf490 github.com/bshuster-repo/logrus-logstash-hook d2c0ecc1836d91814e15e23bb5dc309c3ef51f4a github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 github.com/bugsnag/bugsnag-go b1d153021fcd90ca3f080db36bec96dc690fb274 github.com/bugsnag/osext 0dd3f918b21bec95ace9dc86c7e70266cfc5c702 github.com/bugsnag/panicwrap e2c28503fcd0675329da73bf48b33404db873782 -github.com/denverdino/aliyungo afedced274aa9a7fcdd47ac97018f0f8db4e5de2 +github.com/denverdino/aliyungo 6df11717a253d9c7d4141f9af4deaa7c580cd531 github.com/dgrijalva/jwt-go a601269ab70c205d26370c16f7c81e9017c14e04 github.com/docker/go-metrics 399ea8c73916000c64c2c76e8da00ca82f8387ab -github.com/docker/goamz f0a21f5b2e12f83a505ecf79b633bb2035cf6f85 github.com/docker/libtrust fa567046d9b14f6aa788882a950d69651d230b21 github.com/garyburd/redigo 535138d7bcd717d6531c701ef5933d98b1866257 github.com/go-ini/ini 2ba15ac2dc9cdf88c110ec2dc0ced7fa45f5678c @@ -19,17 +18,19 @@ github.com/gorilla/handlers 60c7bfde3e33c201519a200a4507a158cc03a17b github.com/gorilla/mux 599cba5e7b6137d46ddf58fb1765f5d928e69604 github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d +github.com/marstr/guid 8bd9a64bf37eb297b492a4101fb28e80ac0b290f +github.com/satori/go.uuid f58768cc1a7a7e77a3bd49e98cdd21419399b6a3 github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c github.com/miekg/dns 271c58e0c14f552178ea321a545ff9af38930f39 github.com/mitchellh/mapstructure 482a9fd5fa83e8c4e7817413b80f3eb8feec03ef -github.com/ncw/swift b964f2ca856aac39885e258ad25aec08d5f64ee6 +github.com/ncw/swift a0320860b16212c2b59b4912bb6508cda1d7cee6 github.com/prometheus/client_golang c332b6f63c0658a65eca15c0e5247ded801cf564 github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563 github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd +github.com/Shopify/logrus-bugsnag 577dee27f20dd8f1a529f82210094af593be12bd github.com/spf13/cobra 312092086bed4968099259622145a0c9ae280064 github.com/spf13/pflag 5644820622454e71517561946e3d94b9f9db6842 -github.com/stevvooe/resumable 2aaf90b2ceea5072cb503ef2a620b08ff3119870 github.com/xenolf/lego a9d8cec0e6563575e5868a005359ac97911b5985 github.com/yvasiyarov/go-metrics 57bccd1ccd43f94bb17fdd8bf3007059b802f85e github.com/yvasiyarov/gorelic a9bba5b9ab508a086f9a12b8c51fab68478e2128 @@ -44,6 +45,7 @@ google.golang.org/cloud 975617b05ea8a58727e6c1a06b6161ff4185a9f2 google.golang.org/grpc d3ddb4469d5a1b949fc7a7da7c1d6a0d1b6de994 gopkg.in/check.v1 64131543e7896d5bcc6bd5a76287eb75ea96c673 gopkg.in/square/go-jose.v1 40d457b439244b546f023d056628e5184136899b -gopkg.in/yaml.v2 bef53efd0c76e49e6de55ead051f886bea7e9420 +gopkg.in/yaml.v2 v2.2.1 rsc.io/letsencrypt e770c10b0f1a64775ae91d240407ce00d1a5bdeb https://github.com/dmcgowan/letsencrypt.git github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb +github.com/opencontainers/image-spec ab7389ef9f50030c9b245bc16b981c7ddf192882 diff --git a/vendor/github.com/docker/go-events/retry.go b/vendor/github.com/docker/go-events/retry.go index 2df55d21607bf..b7f0a54225287 100644 --- a/vendor/github.com/docker/go-events/retry.go +++ b/vendor/github.com/docker/go-events/retry.go @@ -203,8 +203,8 @@ type ExponentialBackoffConfig struct { // ExponentialBackoff implements random backoff with exponentially increasing // bounds as the number consecutive failures increase. type ExponentialBackoff struct { + failures uint64 // consecutive failure counter (needs to be 64-bit aligned) config ExponentialBackoffConfig - failures uint64 // consecutive failure counter. } // NewExponentialBackoff returns an exponential backoff strategy with the diff --git a/vendor/github.com/docker/go-metrics/LICENSE.code b/vendor/github.com/docker/go-metrics/LICENSE similarity index 100% rename from vendor/github.com/docker/go-metrics/LICENSE.code rename to vendor/github.com/docker/go-metrics/LICENSE diff --git a/vendor/github.com/docker/go-metrics/README.md b/vendor/github.com/docker/go-metrics/README.md index fdf7fb746fe67..a9e947cb566b2 100644 --- a/vendor/github.com/docker/go-metrics/README.md +++ b/vendor/github.com/docker/go-metrics/README.md @@ -68,9 +68,21 @@ If you need to use a unit but it is not defined in the package please open a PR Package documentation can be found [here](https://godoc.org/github.com/docker/go-metrics). +## HTTP Metrics + +To instrument a http handler, you can wrap the code like this: + +```go +namespace := metrics.NewNamespace("docker_distribution", "http", metrics.Labels{"handler": "your_http_handler_name"}) +httpMetrics := namespace.NewDefaultHttpMetrics() +metrics.Register(namespace) +instrumentedHandler = metrics.InstrumentHandler(httpMetrics, unInstrumentedHandler) +``` +Note: The `handler` label must be provided when a new namespace is created. + ## Additional Metrics -Additional metrics are also defined here that are not avaliable in the prometheus client. +Additional metrics are also defined here that are not available in the prometheus client. If you need a custom metrics and it is generic enough to be used by multiple projects, define it here. diff --git a/vendor/github.com/docker/go-metrics/go.mod b/vendor/github.com/docker/go-metrics/go.mod new file mode 100644 index 0000000000000..7e328f0cffe3b --- /dev/null +++ b/vendor/github.com/docker/go-metrics/go.mod @@ -0,0 +1,5 @@ +module github.com/docker/go-metrics + +go 1.11 + +require github.com/prometheus/client_golang v1.1.0 diff --git a/vendor/github.com/docker/go-metrics/handler.go b/vendor/github.com/docker/go-metrics/handler.go index bb3be41d364d8..05601e9ecd289 100644 --- a/vendor/github.com/docker/go-metrics/handler.go +++ b/vendor/github.com/docker/go-metrics/handler.go @@ -4,10 +4,71 @@ import ( "net/http" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +// HTTPHandlerOpts describes a set of configurable options of http metrics +type HTTPHandlerOpts struct { + DurationBuckets []float64 + RequestSizeBuckets []float64 + ResponseSizeBuckets []float64 +} + +const ( + InstrumentHandlerResponseSize = iota + InstrumentHandlerRequestSize + InstrumentHandlerDuration + InstrumentHandlerCounter + InstrumentHandlerInFlight +) + +type HTTPMetric struct { + prometheus.Collector + handlerType int +} + +var ( + defaultDurationBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10, 25, 60} + defaultRequestSizeBuckets = prometheus.ExponentialBuckets(1024, 2, 22) //1K to 4G + defaultResponseSizeBuckets = defaultRequestSizeBuckets ) // Handler returns the global http.Handler that provides the prometheus -// metrics format on GET requests +// metrics format on GET requests. This handler is no longer instrumented. func Handler() http.Handler { - return prometheus.Handler() + return promhttp.Handler() +} + +func InstrumentHandler(metrics []*HTTPMetric, handler http.Handler) http.HandlerFunc { + return InstrumentHandlerFunc(metrics, handler.ServeHTTP) +} + +func InstrumentHandlerFunc(metrics []*HTTPMetric, handlerFunc http.HandlerFunc) http.HandlerFunc { + var handler http.Handler + handler = http.HandlerFunc(handlerFunc) + for _, metric := range metrics { + switch metric.handlerType { + case InstrumentHandlerResponseSize: + if collector, ok := metric.Collector.(prometheus.ObserverVec); ok { + handler = promhttp.InstrumentHandlerResponseSize(collector, handler) + } + case InstrumentHandlerRequestSize: + if collector, ok := metric.Collector.(prometheus.ObserverVec); ok { + handler = promhttp.InstrumentHandlerRequestSize(collector, handler) + } + case InstrumentHandlerDuration: + if collector, ok := metric.Collector.(prometheus.ObserverVec); ok { + handler = promhttp.InstrumentHandlerDuration(collector, handler) + } + case InstrumentHandlerCounter: + if collector, ok := metric.Collector.(*prometheus.CounterVec); ok { + handler = promhttp.InstrumentHandlerCounter(collector, handler) + } + case InstrumentHandlerInFlight: + if collector, ok := metric.Collector.(prometheus.Gauge); ok { + handler = promhttp.InstrumentHandlerInFlight(collector, handler) + } + } + } + return handler.ServeHTTP } diff --git a/vendor/github.com/docker/go-metrics/namespace.go b/vendor/github.com/docker/go-metrics/namespace.go index 7734c294595f3..798315451a7d6 100644 --- a/vendor/github.com/docker/go-metrics/namespace.go +++ b/vendor/github.com/docker/go-metrics/namespace.go @@ -179,3 +179,137 @@ func makeName(name string, unit Unit) string { return fmt.Sprintf("%s_%s", name, unit) } + +func (n *Namespace) NewDefaultHttpMetrics(handlerName string) []*HTTPMetric { + return n.NewHttpMetricsWithOpts(handlerName, HTTPHandlerOpts{ + DurationBuckets: defaultDurationBuckets, + RequestSizeBuckets: defaultResponseSizeBuckets, + ResponseSizeBuckets: defaultResponseSizeBuckets, + }) +} + +func (n *Namespace) NewHttpMetrics(handlerName string, durationBuckets, requestSizeBuckets, responseSizeBuckets []float64) []*HTTPMetric { + return n.NewHttpMetricsWithOpts(handlerName, HTTPHandlerOpts{ + DurationBuckets: durationBuckets, + RequestSizeBuckets: requestSizeBuckets, + ResponseSizeBuckets: responseSizeBuckets, + }) +} + +func (n *Namespace) NewHttpMetricsWithOpts(handlerName string, opts HTTPHandlerOpts) []*HTTPMetric { + var httpMetrics []*HTTPMetric + inFlightMetric := n.NewInFlightGaugeMetric(handlerName) + requestTotalMetric := n.NewRequestTotalMetric(handlerName) + requestDurationMetric := n.NewRequestDurationMetric(handlerName, opts.DurationBuckets) + requestSizeMetric := n.NewRequestSizeMetric(handlerName, opts.RequestSizeBuckets) + responseSizeMetric := n.NewResponseSizeMetric(handlerName, opts.ResponseSizeBuckets) + httpMetrics = append(httpMetrics, inFlightMetric, requestDurationMetric, requestTotalMetric, requestSizeMetric, responseSizeMetric) + return httpMetrics +} + +func (n *Namespace) NewInFlightGaugeMetric(handlerName string) *HTTPMetric { + labels := prometheus.Labels(n.labels) + labels["handler"] = handlerName + metric := prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: n.name, + Subsystem: n.subsystem, + Name: "in_flight_requests", + Help: "The in-flight HTTP requests", + ConstLabels: prometheus.Labels(labels), + }) + httpMetric := &HTTPMetric{ + Collector: metric, + handlerType: InstrumentHandlerInFlight, + } + n.Add(httpMetric) + return httpMetric +} + +func (n *Namespace) NewRequestTotalMetric(handlerName string) *HTTPMetric { + labels := prometheus.Labels(n.labels) + labels["handler"] = handlerName + metric := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: n.name, + Subsystem: n.subsystem, + Name: "requests_total", + Help: "Total number of HTTP requests made.", + ConstLabels: prometheus.Labels(labels), + }, + []string{"code", "method"}, + ) + httpMetric := &HTTPMetric{ + Collector: metric, + handlerType: InstrumentHandlerCounter, + } + n.Add(httpMetric) + return httpMetric +} +func (n *Namespace) NewRequestDurationMetric(handlerName string, buckets []float64) *HTTPMetric { + if len(buckets) == 0 { + panic("DurationBuckets must be provided") + } + labels := prometheus.Labels(n.labels) + labels["handler"] = handlerName + opts := prometheus.HistogramOpts{ + Namespace: n.name, + Subsystem: n.subsystem, + Name: "request_duration_seconds", + Help: "The HTTP request latencies in seconds.", + Buckets: buckets, + ConstLabels: prometheus.Labels(labels), + } + metric := prometheus.NewHistogramVec(opts, []string{"method"}) + httpMetric := &HTTPMetric{ + Collector: metric, + handlerType: InstrumentHandlerDuration, + } + n.Add(httpMetric) + return httpMetric +} + +func (n *Namespace) NewRequestSizeMetric(handlerName string, buckets []float64) *HTTPMetric { + if len(buckets) == 0 { + panic("RequestSizeBuckets must be provided") + } + labels := prometheus.Labels(n.labels) + labels["handler"] = handlerName + opts := prometheus.HistogramOpts{ + Namespace: n.name, + Subsystem: n.subsystem, + Name: "request_size_bytes", + Help: "The HTTP request sizes in bytes.", + Buckets: buckets, + ConstLabels: prometheus.Labels(labels), + } + metric := prometheus.NewHistogramVec(opts, []string{}) + httpMetric := &HTTPMetric{ + Collector: metric, + handlerType: InstrumentHandlerRequestSize, + } + n.Add(httpMetric) + return httpMetric +} + +func (n *Namespace) NewResponseSizeMetric(handlerName string, buckets []float64) *HTTPMetric { + if len(buckets) == 0 { + panic("ResponseSizeBuckets must be provided") + } + labels := prometheus.Labels(n.labels) + labels["handler"] = handlerName + opts := prometheus.HistogramOpts{ + Namespace: n.name, + Subsystem: n.subsystem, + Name: "response_size_bytes", + Help: "The HTTP response sizes in bytes.", + Buckets: buckets, + ConstLabels: prometheus.Labels(labels), + } + metrics := prometheus.NewHistogramVec(opts, []string{}) + httpMetric := &HTTPMetric{ + Collector: metrics, + handlerType: InstrumentHandlerResponseSize, + } + n.Add(httpMetric) + return httpMetric +} diff --git a/vendor/github.com/docker/go-metrics/timer.go b/vendor/github.com/docker/go-metrics/timer.go index e91eca76bd985..824c98739cf5d 100644 --- a/vendor/github.com/docker/go-metrics/timer.go +++ b/vendor/github.com/docker/go-metrics/timer.go @@ -28,15 +28,27 @@ type Timer interface { // LabeledTimer is a timer that must have label values populated before use. type LabeledTimer interface { - WithValues(labels ...string) Timer + WithValues(labels ...string) *labeledTimerObserver } type labeledTimer struct { m *prometheus.HistogramVec } -func (lt *labeledTimer) WithValues(labels ...string) Timer { - return &timer{m: lt.m.WithLabelValues(labels...)} +type labeledTimerObserver struct { + m prometheus.Observer +} + +func (lbo *labeledTimerObserver) Update(duration time.Duration) { + lbo.m.Observe(duration.Seconds()) +} + +func (lbo *labeledTimerObserver) UpdateSince(since time.Time) { + lbo.m.Observe(time.Since(since).Seconds()) +} + +func (lt *labeledTimer) WithValues(labels ...string) *labeledTimerObserver { + return &labeledTimerObserver{m: lt.m.WithLabelValues(labels...)} } func (lt *labeledTimer) Describe(c chan<- *prometheus.Desc) { @@ -48,7 +60,7 @@ func (lt *labeledTimer) Collect(c chan<- prometheus.Metric) { } type timer struct { - m prometheus.Histogram + m prometheus.Observer } func (t *timer) Update(duration time.Duration) { @@ -60,9 +72,14 @@ func (t *timer) UpdateSince(since time.Time) { } func (t *timer) Describe(c chan<- *prometheus.Desc) { - t.m.Describe(c) + c <- t.m.(prometheus.Metric).Desc() } func (t *timer) Collect(c chan<- prometheus.Metric) { - t.m.Collect(c) + // Are there any observers that don't implement Collector? It is really + // unclear what the point of the upstream change was, but we'll let this + // panic if we get an observer that doesn't implement collector. In this + // case, we should almost always see metricVec objects, so this should + // never panic. + t.m.(prometheus.Collector).Collect(c) } diff --git a/vendor/github.com/docker/go-units/duration.go b/vendor/github.com/docker/go-units/duration.go index ba02af26dc5ce..48dd8744d4331 100644 --- a/vendor/github.com/docker/go-units/duration.go +++ b/vendor/github.com/docker/go-units/duration.go @@ -18,7 +18,7 @@ func HumanDuration(d time.Duration) string { return fmt.Sprintf("%d seconds", seconds) } else if minutes := int(d.Minutes()); minutes == 1 { return "About a minute" - } else if minutes < 46 { + } else if minutes < 60 { return fmt.Sprintf("%d minutes", minutes) } else if hours := int(d.Hours() + 0.5); hours == 1 { return "About an hour" diff --git a/vendor/github.com/docker/go-units/ulimit.go b/vendor/github.com/docker/go-units/ulimit.go index 5ac7fd825fcee..fca0400cc82bb 100644 --- a/vendor/github.com/docker/go-units/ulimit.go +++ b/vendor/github.com/docker/go-units/ulimit.go @@ -96,8 +96,13 @@ func ParseUlimit(val string) (*Ulimit, error) { return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1]) } - if soft > *hard { - return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard) + if *hard != -1 { + if soft == -1 { + return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: soft: -1 (unlimited), hard: %d", *hard) + } + if soft > *hard { + return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard) + } } return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil diff --git a/vendor/github.com/docker/libnetwork/README.md b/vendor/github.com/docker/libnetwork/README.md deleted file mode 100644 index a9020381abe8f..0000000000000 --- a/vendor/github.com/docker/libnetwork/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# libnetwork - networking for containers - -[![Circle CI](https://circleci.com/gh/docker/libnetwork/tree/master.svg?style=svg)](https://circleci.com/gh/docker/libnetwork/tree/master) [![Coverage Status](https://coveralls.io/repos/docker/libnetwork/badge.svg)](https://coveralls.io/r/docker/libnetwork) [![GoDoc](https://godoc.org/github.com/docker/libnetwork?status.svg)](https://godoc.org/github.com/docker/libnetwork) [![Go Report Card](https://goreportcard.com/badge/github.com/docker/libnetwork)](https://goreportcard.com/report/github.com/docker/libnetwork) - -Libnetwork provides a native Go implementation for connecting containers - -The goal of libnetwork is to deliver a robust Container Network Model that provides a consistent programming interface and the required network abstractions for applications. - -#### Design -Please refer to the [design](docs/design.md) for more information. - -#### Using libnetwork - -There are many networking solutions available to suit a broad range of use-cases. libnetwork uses a driver / plugin model to support all of these solutions while abstracting the complexity of the driver implementations by exposing a simple and consistent Network Model to users. - - -```go -import ( - "fmt" - "log" - - "github.com/docker/docker/pkg/reexec" - "github.com/docker/libnetwork" - "github.com/docker/libnetwork/config" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/options" -) - -func main() { - if reexec.Init() { - return - } - - // Select and configure the network driver - networkType := "bridge" - - // Create a new controller instance - driverOptions := options.Generic{} - genericOption := make(map[string]interface{}) - genericOption[netlabel.GenericData] = driverOptions - controller, err := libnetwork.New(config.OptionDriverConfig(networkType, genericOption)) - if err != nil { - log.Fatalf("libnetwork.New: %s", err) - } - - // Create a network for containers to join. - // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can use. - network, err := controller.NewNetwork(networkType, "network1", "") - if err != nil { - log.Fatalf("controller.NewNetwork: %s", err) - } - - // For each new container: allocate IP and interfaces. The returned network - // settings will be used for container infos (inspect and such), as well as - // iptables rules for port publishing. This info is contained or accessible - // from the returned endpoint. - ep, err := network.CreateEndpoint("Endpoint1") - if err != nil { - log.Fatalf("network.CreateEndpoint: %s", err) - } - - // Create the sandbox for the container. - // NewSandbox accepts Variadic optional arguments which libnetwork can use. - sbx, err := controller.NewSandbox("container1", - libnetwork.OptionHostname("test"), - libnetwork.OptionDomainname("docker.io")) - if err != nil { - log.Fatalf("controller.NewSandbox: %s", err) - } - - // A sandbox can join the endpoint via the join api. - err = ep.Join(sbx) - if err != nil { - log.Fatalf("ep.Join: %s", err) - } - - // libnetwork client can check the endpoint's operational data via the Info() API - epInfo, err := ep.DriverInfo() - if err != nil { - log.Fatalf("ep.DriverInfo: %s", err) - } - - macAddress, ok := epInfo[netlabel.MacAddress] - if !ok { - log.Fatalf("failed to get mac address from endpoint info") - } - - fmt.Printf("Joined endpoint %s (%s) to sandbox %s (%s)\n", ep.Name(), macAddress, sbx.ContainerID(), sbx.Key()) -} -``` - -## Future -Please refer to [roadmap](ROADMAP.md) for more information. - -## Contributing - -Want to hack on libnetwork? [Docker's contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) apply. - -## Copyright and license -Code and documentation copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons. diff --git a/vendor/github.com/docker/libnetwork/bitseq/sequence.go b/vendor/github.com/docker/libnetwork/bitseq/sequence.go deleted file mode 100644 index e10b2eedc09b3..0000000000000 --- a/vendor/github.com/docker/libnetwork/bitseq/sequence.go +++ /dev/null @@ -1,736 +0,0 @@ -// Package bitseq provides a structure and utilities for representing long bitmask -// as sequence of run-length encoded blocks. It operates directly on the encoded -// representation, it does not decode/encode. -package bitseq - -import ( - "encoding/binary" - "encoding/json" - "errors" - "fmt" - "sync" - - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/types" - "github.com/sirupsen/logrus" -) - -// block sequence constants -// If needed we can think of making these configurable -const ( - blockLen = uint32(32) - blockBytes = uint64(blockLen / 8) - blockMAX = uint32(1<%s", s.block, s.count, nextBlock) -} - -// GetAvailableBit returns the position of the first unset bit in the bitmask represented by this sequence -func (s *sequence) getAvailableBit(from uint64) (uint64, uint64, error) { - if s.block == blockMAX || s.count == 0 { - return invalidPos, invalidPos, ErrNoBitAvailable - } - bits := from - bitSel := blockFirstBit >> from - for bitSel > 0 && s.block&bitSel != 0 { - bitSel >>= 1 - bits++ - } - // Check if the loop exited because it could not - // find any available bit int block starting from - // "from". Return invalid pos in that case. - if bitSel == 0 { - return invalidPos, invalidPos, ErrNoBitAvailable - } - return bits / 8, bits % 8, nil -} - -// GetCopy returns a copy of the linked list rooted at this node -func (s *sequence) getCopy() *sequence { - n := &sequence{block: s.block, count: s.count} - pn := n - ps := s.next - for ps != nil { - pn.next = &sequence{block: ps.block, count: ps.count} - pn = pn.next - ps = ps.next - } - return n -} - -// Equal checks if this sequence is equal to the passed one -func (s *sequence) equal(o *sequence) bool { - this := s - other := o - for this != nil { - if other == nil { - return false - } - if this.block != other.block || this.count != other.count { - return false - } - this = this.next - other = other.next - } - // Check if other is longer than this - if other != nil { - return false - } - return true -} - -// ToByteArray converts the sequence into a byte array -func (s *sequence) toByteArray() ([]byte, error) { - var bb []byte - - p := s - for p != nil { - b := make([]byte, 12) - binary.BigEndian.PutUint32(b[0:], p.block) - binary.BigEndian.PutUint64(b[4:], p.count) - bb = append(bb, b...) - p = p.next - } - - return bb, nil -} - -// fromByteArray construct the sequence from the byte array -func (s *sequence) fromByteArray(data []byte) error { - l := len(data) - if l%12 != 0 { - return fmt.Errorf("cannot deserialize byte sequence of length %d (%v)", l, data) - } - - p := s - i := 0 - for { - p.block = binary.BigEndian.Uint32(data[i : i+4]) - p.count = binary.BigEndian.Uint64(data[i+4 : i+12]) - i += 12 - if i == l { - break - } - p.next = &sequence{} - p = p.next - } - - return nil -} - -func (h *Handle) getCopy() *Handle { - return &Handle{ - bits: h.bits, - unselected: h.unselected, - head: h.head.getCopy(), - app: h.app, - id: h.id, - dbIndex: h.dbIndex, - dbExists: h.dbExists, - store: h.store, - curr: h.curr, - } -} - -// SetAnyInRange atomically sets the first unset bit in the specified range in the sequence and returns the corresponding ordinal -func (h *Handle) SetAnyInRange(start, end uint64, serial bool) (uint64, error) { - if end < start || end >= h.bits { - return invalidPos, fmt.Errorf("invalid bit range [%d, %d]", start, end) - } - if h.Unselected() == 0 { - return invalidPos, ErrNoBitAvailable - } - return h.set(0, start, end, true, false, serial) -} - -// SetAny atomically sets the first unset bit in the sequence and returns the corresponding ordinal -func (h *Handle) SetAny(serial bool) (uint64, error) { - if h.Unselected() == 0 { - return invalidPos, ErrNoBitAvailable - } - return h.set(0, 0, h.bits-1, true, false, serial) -} - -// Set atomically sets the corresponding bit in the sequence -func (h *Handle) Set(ordinal uint64) error { - if err := h.validateOrdinal(ordinal); err != nil { - return err - } - _, err := h.set(ordinal, 0, 0, false, false, false) - return err -} - -// Unset atomically unsets the corresponding bit in the sequence -func (h *Handle) Unset(ordinal uint64) error { - if err := h.validateOrdinal(ordinal); err != nil { - return err - } - _, err := h.set(ordinal, 0, 0, false, true, false) - return err -} - -// IsSet atomically checks if the ordinal bit is set. In case ordinal -// is outside of the bit sequence limits, false is returned. -func (h *Handle) IsSet(ordinal uint64) bool { - if err := h.validateOrdinal(ordinal); err != nil { - return false - } - h.Lock() - _, _, err := checkIfAvailable(h.head, ordinal) - h.Unlock() - return err != nil -} - -func (h *Handle) runConsistencyCheck() bool { - corrupted := false - for p, c := h.head, h.head.next; c != nil; c = c.next { - if c.count == 0 { - corrupted = true - p.next = c.next - continue // keep same p - } - p = c - } - return corrupted -} - -// CheckConsistency checks if the bit sequence is in an inconsistent state and attempts to fix it. -// It looks for a corruption signature that may happen in docker 1.9.0 and 1.9.1. -func (h *Handle) CheckConsistency() error { - for { - h.Lock() - store := h.store - h.Unlock() - - if store != nil { - if err := store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound { - return err - } - } - - h.Lock() - nh := h.getCopy() - h.Unlock() - - if !nh.runConsistencyCheck() { - return nil - } - - if err := nh.writeToStore(); err != nil { - if _, ok := err.(types.RetryError); !ok { - return fmt.Errorf("internal failure while fixing inconsistent bitsequence: %v", err) - } - continue - } - - logrus.Infof("Fixed inconsistent bit sequence in datastore:\n%s\n%s", h, nh) - - h.Lock() - h.head = nh.head - h.Unlock() - - return nil - } -} - -// set/reset the bit -func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial bool) (uint64, error) { - var ( - bitPos uint64 - bytePos uint64 - ret uint64 - err error - ) - - for { - var store datastore.DataStore - curr := uint64(0) - h.Lock() - store = h.store - if store != nil { - h.Unlock() // The lock is acquired in the GetObject - if err := store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound { - return ret, err - } - h.Lock() // Acquire the lock back - } - if serial { - curr = h.curr - } - // Get position if available - if release { - bytePos, bitPos = ordinalToPos(ordinal) - } else { - if any { - bytePos, bitPos, err = getAvailableFromCurrent(h.head, start, curr, end) - ret = posToOrdinal(bytePos, bitPos) - if err == nil { - h.curr = ret + 1 - } - } else { - bytePos, bitPos, err = checkIfAvailable(h.head, ordinal) - ret = ordinal - } - } - if err != nil { - h.Unlock() - return ret, err - } - - // Create a private copy of h and work on it - nh := h.getCopy() - - nh.head = pushReservation(bytePos, bitPos, nh.head, release) - if release { - nh.unselected++ - } else { - nh.unselected-- - } - - if h.store != nil { - h.Unlock() - // Attempt to write private copy to store - if err := nh.writeToStore(); err != nil { - if _, ok := err.(types.RetryError); !ok { - return ret, fmt.Errorf("internal failure while setting the bit: %v", err) - } - // Retry - continue - } - h.Lock() - } - - // Previous atomic push was successful. Save private copy to local copy - h.unselected = nh.unselected - h.head = nh.head - h.dbExists = nh.dbExists - h.dbIndex = nh.dbIndex - h.Unlock() - return ret, nil - } -} - -// checks is needed because to cover the case where the number of bits is not a multiple of blockLen -func (h *Handle) validateOrdinal(ordinal uint64) error { - h.Lock() - defer h.Unlock() - if ordinal >= h.bits { - return errors.New("bit does not belong to the sequence") - } - return nil -} - -// Destroy removes from the datastore the data belonging to this handle -func (h *Handle) Destroy() error { - for { - if err := h.deleteFromStore(); err != nil { - if _, ok := err.(types.RetryError); !ok { - return fmt.Errorf("internal failure while destroying the sequence: %v", err) - } - // Fetch latest - if err := h.store.GetObject(datastore.Key(h.Key()...), h); err != nil { - if err == datastore.ErrKeyNotFound { // already removed - return nil - } - return fmt.Errorf("failed to fetch from store when destroying the sequence: %v", err) - } - continue - } - return nil - } -} - -// ToByteArray converts this handle's data into a byte array -func (h *Handle) ToByteArray() ([]byte, error) { - - h.Lock() - defer h.Unlock() - ba := make([]byte, 16) - binary.BigEndian.PutUint64(ba[0:], h.bits) - binary.BigEndian.PutUint64(ba[8:], h.unselected) - bm, err := h.head.toByteArray() - if err != nil { - return nil, fmt.Errorf("failed to serialize head: %s", err.Error()) - } - ba = append(ba, bm...) - - return ba, nil -} - -// FromByteArray reads his handle's data from a byte array -func (h *Handle) FromByteArray(ba []byte) error { - if ba == nil { - return errors.New("nil byte array") - } - - nh := &sequence{} - err := nh.fromByteArray(ba[16:]) - if err != nil { - return fmt.Errorf("failed to deserialize head: %s", err.Error()) - } - - h.Lock() - h.head = nh - h.bits = binary.BigEndian.Uint64(ba[0:8]) - h.unselected = binary.BigEndian.Uint64(ba[8:16]) - h.Unlock() - - return nil -} - -// Bits returns the length of the bit sequence -func (h *Handle) Bits() uint64 { - return h.bits -} - -// Unselected returns the number of bits which are not selected -func (h *Handle) Unselected() uint64 { - h.Lock() - defer h.Unlock() - return h.unselected -} - -func (h *Handle) String() string { - h.Lock() - defer h.Unlock() - return fmt.Sprintf("App: %s, ID: %s, DBIndex: 0x%x, Bits: %d, Unselected: %d, Sequence: %s Curr:%d", - h.app, h.id, h.dbIndex, h.bits, h.unselected, h.head.toString(), h.curr) -} - -// MarshalJSON encodes Handle into json message -func (h *Handle) MarshalJSON() ([]byte, error) { - m := map[string]interface{}{ - "id": h.id, - } - - b, err := h.ToByteArray() - if err != nil { - return nil, err - } - m["sequence"] = b - return json.Marshal(m) -} - -// UnmarshalJSON decodes json message into Handle -func (h *Handle) UnmarshalJSON(data []byte) error { - var ( - m map[string]interface{} - b []byte - err error - ) - if err = json.Unmarshal(data, &m); err != nil { - return err - } - h.id = m["id"].(string) - bi, _ := json.Marshal(m["sequence"]) - if err := json.Unmarshal(bi, &b); err != nil { - return err - } - return h.FromByteArray(b) -} - -// getFirstAvailable looks for the first unset bit in passed mask starting from start -func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) { - // Find sequence which contains the start bit - byteStart, bitStart := ordinalToPos(start) - current, _, precBlocks, inBlockBytePos := findSequence(head, byteStart) - // Derive the this sequence offsets - byteOffset := byteStart - inBlockBytePos - bitOffset := inBlockBytePos*8 + bitStart - for current != nil { - if current.block != blockMAX { - // If the current block is not full, check if there is any bit - // from the current bit in the current block. If not, before proceeding to the - // next block node, make sure we check for available bit in the next - // instance of the same block. Due to RLE same block signature will be - // compressed. - retry: - bytePos, bitPos, err := current.getAvailableBit(bitOffset) - if err != nil && precBlocks == current.count-1 { - // This is the last instance in the same block node, - // so move to the next block. - goto next - } - if err != nil { - // There are some more instances of the same block, so add the offset - // and be optimistic that you will find the available bit in the next - // instance of the same block. - bitOffset = 0 - byteOffset += blockBytes - precBlocks++ - goto retry - } - return byteOffset + bytePos, bitPos, err - } - // Moving to next block: Reset bit offset. - next: - bitOffset = 0 - byteOffset += (current.count * blockBytes) - (precBlocks * blockBytes) - precBlocks = 0 - current = current.next - } - return invalidPos, invalidPos, ErrNoBitAvailable -} - -// getAvailableFromCurrent will look for available ordinal from the current ordinal. -// If none found then it will loop back to the start to check of the available bit. -// This can be further optimized to check from start till curr in case of a rollover -func getAvailableFromCurrent(head *sequence, start, curr, end uint64) (uint64, uint64, error) { - var bytePos, bitPos uint64 - var err error - if curr != 0 && curr > start { - bytePos, bitPos, err = getFirstAvailable(head, curr) - ret := posToOrdinal(bytePos, bitPos) - if end < ret || err != nil { - goto begin - } - return bytePos, bitPos, nil - } - -begin: - bytePos, bitPos, err = getFirstAvailable(head, start) - ret := posToOrdinal(bytePos, bitPos) - if end < ret || err != nil { - return invalidPos, invalidPos, ErrNoBitAvailable - } - return bytePos, bitPos, nil -} - -// checkIfAvailable checks if the bit correspondent to the specified ordinal is unset -// If the ordinal is beyond the sequence limits, a negative response is returned -func checkIfAvailable(head *sequence, ordinal uint64) (uint64, uint64, error) { - bytePos, bitPos := ordinalToPos(ordinal) - - // Find the sequence containing this byte - current, _, _, inBlockBytePos := findSequence(head, bytePos) - if current != nil { - // Check whether the bit corresponding to the ordinal address is unset - bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos) - if current.block&bitSel == 0 { - return bytePos, bitPos, nil - } - } - - return invalidPos, invalidPos, ErrBitAllocated -} - -// Given the byte position and the sequences list head, return the pointer to the -// sequence containing the byte (current), the pointer to the previous sequence, -// the number of blocks preceding the block containing the byte inside the current sequence. -// If bytePos is outside of the list, function will return (nil, nil, 0, invalidPos) -func findSequence(head *sequence, bytePos uint64) (*sequence, *sequence, uint64, uint64) { - // Find the sequence containing this byte - previous := head - current := head - n := bytePos - for current.next != nil && n >= (current.count*blockBytes) { // Nil check for less than 32 addresses masks - n -= (current.count * blockBytes) - previous = current - current = current.next - } - - // If byte is outside of the list, let caller know - if n >= (current.count * blockBytes) { - return nil, nil, 0, invalidPos - } - - // Find the byte position inside the block and the number of blocks - // preceding the block containing the byte inside this sequence - precBlocks := n / blockBytes - inBlockBytePos := bytePos % blockBytes - - return current, previous, precBlocks, inBlockBytePos -} - -// PushReservation pushes the bit reservation inside the bitmask. -// Given byte and bit positions, identify the sequence (current) which holds the block containing the affected bit. -// Create a new block with the modified bit according to the operation (allocate/release). -// Create a new sequence containing the new block and insert it in the proper position. -// Remove current sequence if empty. -// Check if new sequence can be merged with neighbour (previous/next) sequences. -// -// -// Identify "current" sequence containing block: -// [prev seq] [current seq] [next seq] -// -// Based on block position, resulting list of sequences can be any of three forms: -// -// block position Resulting list of sequences -// A) block is first in current: [prev seq] [new] [modified current seq] [next seq] -// B) block is last in current: [prev seq] [modified current seq] [new] [next seq] -// C) block is in the middle of current: [prev seq] [curr pre] [new] [curr post] [next seq] -func pushReservation(bytePos, bitPos uint64, head *sequence, release bool) *sequence { - // Store list's head - newHead := head - - // Find the sequence containing this byte - current, previous, precBlocks, inBlockBytePos := findSequence(head, bytePos) - if current == nil { - return newHead - } - - // Construct updated block - bitSel := blockFirstBit >> (inBlockBytePos*8 + bitPos) - newBlock := current.block - if release { - newBlock &^= bitSel - } else { - newBlock |= bitSel - } - - // Quit if it was a redundant request - if current.block == newBlock { - return newHead - } - - // Current sequence inevitably looses one block, upadate count - current.count-- - - // Create new sequence - newSequence := &sequence{block: newBlock, count: 1} - - // Insert the new sequence in the list based on block position - if precBlocks == 0 { // First in sequence (A) - newSequence.next = current - if current == head { - newHead = newSequence - previous = newHead - } else { - previous.next = newSequence - } - removeCurrentIfEmpty(&newHead, newSequence, current) - mergeSequences(previous) - } else if precBlocks == current.count { // Last in sequence (B) - newSequence.next = current.next - current.next = newSequence - mergeSequences(current) - } else { // In between the sequence (C) - currPre := &sequence{block: current.block, count: precBlocks, next: newSequence} - currPost := current - currPost.count -= precBlocks - newSequence.next = currPost - if currPost == head { - newHead = currPre - } else { - previous.next = currPre - } - // No merging or empty current possible here - } - - return newHead -} - -// Removes the current sequence from the list if empty, adjusting the head pointer if needed -func removeCurrentIfEmpty(head **sequence, previous, current *sequence) { - if current.count == 0 { - if current == *head { - *head = current.next - } else { - previous.next = current.next - current = current.next - } - } -} - -// Given a pointer to a sequence, it checks if it can be merged with any following sequences -// It stops when no more merging is possible. -// TODO: Optimization: only attempt merge from start to end sequence, no need to scan till the end of the list -func mergeSequences(seq *sequence) { - if seq != nil { - // Merge all what possible from seq - for seq.next != nil && seq.block == seq.next.block { - seq.count += seq.next.count - seq.next = seq.next.next - } - // Move to next - mergeSequences(seq.next) - } -} - -func getNumBlocks(numBits uint64) uint64 { - numBlocks := numBits / uint64(blockLen) - if numBits%uint64(blockLen) != 0 { - numBlocks++ - } - return numBlocks -} - -func ordinalToPos(ordinal uint64) (uint64, uint64) { - return ordinal / 8, ordinal % 8 -} - -func posToOrdinal(bytePos, bitPos uint64) uint64 { - return bytePos*8 + bitPos -} diff --git a/vendor/github.com/docker/libnetwork/bitseq/store.go b/vendor/github.com/docker/libnetwork/bitseq/store.go deleted file mode 100644 index cdb7f04264e75..0000000000000 --- a/vendor/github.com/docker/libnetwork/bitseq/store.go +++ /dev/null @@ -1,142 +0,0 @@ -package bitseq - -import ( - "encoding/json" - "fmt" - - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/types" -) - -// Key provides the Key to be used in KV Store -func (h *Handle) Key() []string { - h.Lock() - defer h.Unlock() - return []string{h.app, h.id} -} - -// KeyPrefix returns the immediate parent key that can be used for tree walk -func (h *Handle) KeyPrefix() []string { - h.Lock() - defer h.Unlock() - return []string{h.app} -} - -// Value marshals the data to be stored in the KV store -func (h *Handle) Value() []byte { - b, err := json.Marshal(h) - if err != nil { - return nil - } - return b -} - -// SetValue unmarshals the data from the KV store -func (h *Handle) SetValue(value []byte) error { - return json.Unmarshal(value, h) -} - -// Index returns the latest DB Index as seen by this object -func (h *Handle) Index() uint64 { - h.Lock() - defer h.Unlock() - return h.dbIndex -} - -// SetIndex method allows the datastore to store the latest DB Index into this object -func (h *Handle) SetIndex(index uint64) { - h.Lock() - h.dbIndex = index - h.dbExists = true - h.Unlock() -} - -// Exists method is true if this object has been stored in the DB. -func (h *Handle) Exists() bool { - h.Lock() - defer h.Unlock() - return h.dbExists -} - -// New method returns a handle based on the receiver handle -func (h *Handle) New() datastore.KVObject { - h.Lock() - defer h.Unlock() - - return &Handle{ - app: h.app, - store: h.store, - } -} - -// CopyTo deep copies the handle into the passed destination object -func (h *Handle) CopyTo(o datastore.KVObject) error { - h.Lock() - defer h.Unlock() - - dstH := o.(*Handle) - if h == dstH { - return nil - } - dstH.Lock() - dstH.bits = h.bits - dstH.unselected = h.unselected - dstH.head = h.head.getCopy() - dstH.app = h.app - dstH.id = h.id - dstH.dbIndex = h.dbIndex - dstH.dbExists = h.dbExists - dstH.store = h.store - dstH.curr = h.curr - dstH.Unlock() - - return nil -} - -// Skip provides a way for a KV Object to avoid persisting it in the KV Store -func (h *Handle) Skip() bool { - return false -} - -// DataScope method returns the storage scope of the datastore -func (h *Handle) DataScope() string { - h.Lock() - defer h.Unlock() - - return h.store.Scope() -} - -func (h *Handle) fromDsValue(value []byte) error { - var ba []byte - if err := json.Unmarshal(value, &ba); err != nil { - return fmt.Errorf("failed to decode json: %s", err.Error()) - } - if err := h.FromByteArray(ba); err != nil { - return fmt.Errorf("failed to decode handle: %s", err.Error()) - } - return nil -} - -func (h *Handle) writeToStore() error { - h.Lock() - store := h.store - h.Unlock() - if store == nil { - return nil - } - err := store.PutObjectAtomic(h) - if err == datastore.ErrKeyModified { - return types.RetryErrorf("failed to perform atomic write (%v). Retry might fix the error", err) - } - return err -} - -func (h *Handle) deleteFromStore() error { - h.Lock() - store := h.store - h.Unlock() - if store == nil { - return nil - } - return store.DeleteObjectAtomic(h) -} diff --git a/vendor/github.com/docker/libnetwork/config/config.go b/vendor/github.com/docker/libnetwork/config/config.go deleted file mode 100644 index b7c66e9884e50..0000000000000 --- a/vendor/github.com/docker/libnetwork/config/config.go +++ /dev/null @@ -1,309 +0,0 @@ -package config - -import ( - "strings" - - "github.com/BurntSushi/toml" - "github.com/docker/docker/pkg/discovery" - "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/go-connections/tlsconfig" - "github.com/docker/libkv/store" - "github.com/docker/libnetwork/cluster" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/ipamutils" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/osl" - "github.com/sirupsen/logrus" -) - -const ( - warningThNetworkControlPlaneMTU = 1500 - minimumNetworkControlPlaneMTU = 500 -) - -// Config encapsulates configurations of various Libnetwork components -type Config struct { - Daemon DaemonCfg - Cluster ClusterCfg - Scopes map[string]*datastore.ScopeCfg - ActiveSandboxes map[string]interface{} - PluginGetter plugingetter.PluginGetter -} - -// DaemonCfg represents libnetwork core configuration -type DaemonCfg struct { - Debug bool - Experimental bool - DataDir string - ExecRoot string - DefaultNetwork string - DefaultDriver string - Labels []string - DriverCfg map[string]interface{} - ClusterProvider cluster.Provider - NetworkControlPlaneMTU int - DefaultAddressPool []*ipamutils.NetworkToSplit -} - -// ClusterCfg represents cluster configuration -type ClusterCfg struct { - Watcher discovery.Watcher - Address string - Discovery string - Heartbeat uint64 -} - -// LoadDefaultScopes loads default scope configs for scopes which -// doesn't have explicit user specified configs. -func (c *Config) LoadDefaultScopes(dataDir string) { - for k, v := range datastore.DefaultScopes(dataDir) { - if _, ok := c.Scopes[k]; !ok { - c.Scopes[k] = v - } - } -} - -// ParseConfig parses the libnetwork configuration file -func ParseConfig(tomlCfgFile string) (*Config, error) { - cfg := &Config{ - Scopes: map[string]*datastore.ScopeCfg{}, - } - - if _, err := toml.DecodeFile(tomlCfgFile, cfg); err != nil { - return nil, err - } - - cfg.LoadDefaultScopes(cfg.Daemon.DataDir) - return cfg, nil -} - -// ParseConfigOptions parses the configuration options and returns -// a reference to the corresponding Config structure -func ParseConfigOptions(cfgOptions ...Option) *Config { - cfg := &Config{ - Daemon: DaemonCfg{ - DriverCfg: make(map[string]interface{}), - }, - Scopes: make(map[string]*datastore.ScopeCfg), - } - - cfg.ProcessOptions(cfgOptions...) - cfg.LoadDefaultScopes(cfg.Daemon.DataDir) - - return cfg -} - -// Option is an option setter function type used to pass various configurations -// to the controller -type Option func(c *Config) - -// OptionDefaultNetwork function returns an option setter for a default network -func OptionDefaultNetwork(dn string) Option { - return func(c *Config) { - logrus.Debugf("Option DefaultNetwork: %s", dn) - c.Daemon.DefaultNetwork = strings.TrimSpace(dn) - } -} - -// OptionDefaultDriver function returns an option setter for default driver -func OptionDefaultDriver(dd string) Option { - return func(c *Config) { - logrus.Debugf("Option DefaultDriver: %s", dd) - c.Daemon.DefaultDriver = strings.TrimSpace(dd) - } -} - -// OptionDefaultAddressPoolConfig function returns an option setter for default address pool -func OptionDefaultAddressPoolConfig(addressPool []*ipamutils.NetworkToSplit) Option { - return func(c *Config) { - c.Daemon.DefaultAddressPool = addressPool - } -} - -// OptionDriverConfig returns an option setter for driver configuration. -func OptionDriverConfig(networkType string, config map[string]interface{}) Option { - return func(c *Config) { - c.Daemon.DriverCfg[networkType] = config - } -} - -// OptionLabels function returns an option setter for labels -func OptionLabels(labels []string) Option { - return func(c *Config) { - for _, label := range labels { - if strings.HasPrefix(label, netlabel.Prefix) { - c.Daemon.Labels = append(c.Daemon.Labels, label) - } - } - } -} - -// OptionKVProvider function returns an option setter for kvstore provider -func OptionKVProvider(provider string) Option { - return func(c *Config) { - logrus.Debugf("Option OptionKVProvider: %s", provider) - if _, ok := c.Scopes[datastore.GlobalScope]; !ok { - c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{} - } - c.Scopes[datastore.GlobalScope].Client.Provider = strings.TrimSpace(provider) - } -} - -// OptionKVProviderURL function returns an option setter for kvstore url -func OptionKVProviderURL(url string) Option { - return func(c *Config) { - logrus.Debugf("Option OptionKVProviderURL: %s", url) - if _, ok := c.Scopes[datastore.GlobalScope]; !ok { - c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{} - } - c.Scopes[datastore.GlobalScope].Client.Address = strings.TrimSpace(url) - } -} - -// OptionKVOpts function returns an option setter for kvstore options -func OptionKVOpts(opts map[string]string) Option { - return func(c *Config) { - if opts["kv.cacertfile"] != "" && opts["kv.certfile"] != "" && opts["kv.keyfile"] != "" { - logrus.Info("Option Initializing KV with TLS") - tlsConfig, err := tlsconfig.Client(tlsconfig.Options{ - CAFile: opts["kv.cacertfile"], - CertFile: opts["kv.certfile"], - KeyFile: opts["kv.keyfile"], - }) - if err != nil { - logrus.Errorf("Unable to set up TLS: %s", err) - return - } - if _, ok := c.Scopes[datastore.GlobalScope]; !ok { - c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{} - } - if c.Scopes[datastore.GlobalScope].Client.Config == nil { - c.Scopes[datastore.GlobalScope].Client.Config = &store.Config{TLS: tlsConfig} - } else { - c.Scopes[datastore.GlobalScope].Client.Config.TLS = tlsConfig - } - // Workaround libkv/etcd bug for https - c.Scopes[datastore.GlobalScope].Client.Config.ClientTLS = &store.ClientTLSConfig{ - CACertFile: opts["kv.cacertfile"], - CertFile: opts["kv.certfile"], - KeyFile: opts["kv.keyfile"], - } - } else { - logrus.Info("Option Initializing KV without TLS") - } - } -} - -// OptionDiscoveryWatcher function returns an option setter for discovery watcher -func OptionDiscoveryWatcher(watcher discovery.Watcher) Option { - return func(c *Config) { - c.Cluster.Watcher = watcher - } -} - -// OptionDiscoveryAddress function returns an option setter for self discovery address -func OptionDiscoveryAddress(address string) Option { - return func(c *Config) { - c.Cluster.Address = address - } -} - -// OptionDataDir function returns an option setter for data folder -func OptionDataDir(dataDir string) Option { - return func(c *Config) { - c.Daemon.DataDir = dataDir - } -} - -// OptionExecRoot function returns an option setter for exec root folder -func OptionExecRoot(execRoot string) Option { - return func(c *Config) { - c.Daemon.ExecRoot = execRoot - osl.SetBasePath(execRoot) - } -} - -// OptionPluginGetter returns a plugingetter for remote drivers. -func OptionPluginGetter(pg plugingetter.PluginGetter) Option { - return func(c *Config) { - c.PluginGetter = pg - } -} - -// OptionExperimental function returns an option setter for experimental daemon -func OptionExperimental(exp bool) Option { - return func(c *Config) { - logrus.Debugf("Option Experimental: %v", exp) - c.Daemon.Experimental = exp - } -} - -// OptionNetworkControlPlaneMTU function returns an option setter for control plane MTU -func OptionNetworkControlPlaneMTU(exp int) Option { - return func(c *Config) { - logrus.Debugf("Network Control Plane MTU: %d", exp) - if exp < warningThNetworkControlPlaneMTU { - logrus.Warnf("Received a MTU of %d, this value is very low, the network control plane can misbehave,"+ - " defaulting to minimum value (%d)", exp, minimumNetworkControlPlaneMTU) - if exp < minimumNetworkControlPlaneMTU { - exp = minimumNetworkControlPlaneMTU - } - } - c.Daemon.NetworkControlPlaneMTU = exp - } -} - -// ProcessOptions processes options and stores it in config -func (c *Config) ProcessOptions(options ...Option) { - for _, opt := range options { - if opt != nil { - opt(c) - } - } -} - -// IsValidName validates configuration objects supported by libnetwork -func IsValidName(name string) bool { - return strings.TrimSpace(name) != "" -} - -// OptionLocalKVProvider function returns an option setter for kvstore provider -func OptionLocalKVProvider(provider string) Option { - return func(c *Config) { - logrus.Debugf("Option OptionLocalKVProvider: %s", provider) - if _, ok := c.Scopes[datastore.LocalScope]; !ok { - c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{} - } - c.Scopes[datastore.LocalScope].Client.Provider = strings.TrimSpace(provider) - } -} - -// OptionLocalKVProviderURL function returns an option setter for kvstore url -func OptionLocalKVProviderURL(url string) Option { - return func(c *Config) { - logrus.Debugf("Option OptionLocalKVProviderURL: %s", url) - if _, ok := c.Scopes[datastore.LocalScope]; !ok { - c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{} - } - c.Scopes[datastore.LocalScope].Client.Address = strings.TrimSpace(url) - } -} - -// OptionLocalKVProviderConfig function returns an option setter for kvstore config -func OptionLocalKVProviderConfig(config *store.Config) Option { - return func(c *Config) { - logrus.Debugf("Option OptionLocalKVProviderConfig: %v", config) - if _, ok := c.Scopes[datastore.LocalScope]; !ok { - c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{} - } - c.Scopes[datastore.LocalScope].Client.Config = config - } -} - -// OptionActiveSandboxes function returns an option setter for passing the sandboxes -// which were active during previous daemon life -func OptionActiveSandboxes(sandboxes map[string]interface{}) Option { - return func(c *Config) { - c.ActiveSandboxes = sandboxes - } -} diff --git a/vendor/github.com/docker/libnetwork/default_gateway_windows.go b/vendor/github.com/docker/libnetwork/default_gateway_windows.go deleted file mode 100644 index f4ba198e57576..0000000000000 --- a/vendor/github.com/docker/libnetwork/default_gateway_windows.go +++ /dev/null @@ -1,22 +0,0 @@ -package libnetwork - -import ( - windriver "github.com/docker/libnetwork/drivers/windows" - "github.com/docker/libnetwork/options" - "github.com/docker/libnetwork/types" -) - -const libnGWNetwork = "nat" - -func getPlatformOption() EndpointOption { - - epOption := options.Generic{ - windriver.DisableICC: true, - windriver.DisableDNS: true, - } - return EndpointOptionGeneric(epOption) -} - -func (c *controller) createGWNetwork() (Network, error) { - return nil, types.NotImplementedErrorf("default gateway functionality is not implemented in windows") -} diff --git a/vendor/github.com/docker/libnetwork/diagnostic/server.go b/vendor/github.com/docker/libnetwork/diagnostic/server.go deleted file mode 100644 index 6c9372d682560..0000000000000 --- a/vendor/github.com/docker/libnetwork/diagnostic/server.go +++ /dev/null @@ -1,227 +0,0 @@ -package diagnostic - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "sync" - "sync/atomic" - - stackdump "github.com/docker/docker/pkg/signal" - "github.com/docker/libnetwork/internal/caller" - "github.com/sirupsen/logrus" -) - -// HTTPHandlerFunc TODO -type HTTPHandlerFunc func(interface{}, http.ResponseWriter, *http.Request) - -type httpHandlerCustom struct { - ctx interface{} - F func(interface{}, http.ResponseWriter, *http.Request) -} - -// ServeHTTP TODO -func (h httpHandlerCustom) ServeHTTP(w http.ResponseWriter, r *http.Request) { - h.F(h.ctx, w, r) -} - -var diagPaths2Func = map[string]HTTPHandlerFunc{ - "/": notImplemented, - "/help": help, - "/ready": ready, - "/stackdump": stackTrace, -} - -// Server when the debug is enabled exposes a -// This data structure is protected by the Agent mutex so does not require and additional mutex here -type Server struct { - enable int32 - srv *http.Server - port int - mux *http.ServeMux - registeredHanders map[string]bool - sync.Mutex -} - -// New creates a new diagnostic server -func New() *Server { - return &Server{ - registeredHanders: make(map[string]bool), - } -} - -// Init initialize the mux for the http handling and register the base hooks -func (s *Server) Init() { - s.mux = http.NewServeMux() - - // Register local handlers - s.RegisterHandler(s, diagPaths2Func) -} - -// RegisterHandler allows to register new handlers to the mux and to a specific path -func (s *Server) RegisterHandler(ctx interface{}, hdlrs map[string]HTTPHandlerFunc) { - s.Lock() - defer s.Unlock() - for path, fun := range hdlrs { - if _, ok := s.registeredHanders[path]; ok { - continue - } - s.mux.Handle(path, httpHandlerCustom{ctx, fun}) - s.registeredHanders[path] = true - } -} - -// ServeHTTP this is the method called bu the ListenAndServe, and is needed to allow us to -// use our custom mux -func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - s.mux.ServeHTTP(w, r) -} - -// EnableDiagnostic opens a TCP socket to debug the passed network DB -func (s *Server) EnableDiagnostic(ip string, port int) { - s.Lock() - defer s.Unlock() - - s.port = port - - if s.enable == 1 { - logrus.Info("The server is already up and running") - return - } - - logrus.Infof("Starting the diagnostic server listening on %d for commands", port) - srv := &http.Server{Addr: fmt.Sprintf("%s:%d", ip, port), Handler: s} - s.srv = srv - s.enable = 1 - go func(n *Server) { - // Ignore ErrServerClosed that is returned on the Shutdown call - if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - logrus.Errorf("ListenAndServe error: %s", err) - atomic.SwapInt32(&n.enable, 0) - } - }(s) -} - -// DisableDiagnostic stop the dubug and closes the tcp socket -func (s *Server) DisableDiagnostic() { - s.Lock() - defer s.Unlock() - - s.srv.Shutdown(context.Background()) - s.srv = nil - s.enable = 0 - logrus.Info("Disabling the diagnostic server") -} - -// IsDiagnosticEnabled returns true when the debug is enabled -func (s *Server) IsDiagnosticEnabled() bool { - s.Lock() - defer s.Unlock() - return s.enable == 1 -} - -func notImplemented(ctx interface{}, w http.ResponseWriter, r *http.Request) { - r.ParseForm() - _, json := ParseHTTPFormOptions(r) - rsp := WrongCommand("not implemented", fmt.Sprintf("URL path: %s no method implemented check /help\n", r.URL.Path)) - - // audit logs - log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()}) - log.Info("command not implemented done") - - HTTPReply(w, rsp, json) -} - -func help(ctx interface{}, w http.ResponseWriter, r *http.Request) { - r.ParseForm() - _, json := ParseHTTPFormOptions(r) - - // audit logs - log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()}) - log.Info("help done") - - n, ok := ctx.(*Server) - var result string - if ok { - for path := range n.registeredHanders { - result += fmt.Sprintf("%s\n", path) - } - HTTPReply(w, CommandSucceed(&StringCmd{Info: result}), json) - } -} - -func ready(ctx interface{}, w http.ResponseWriter, r *http.Request) { - r.ParseForm() - _, json := ParseHTTPFormOptions(r) - - // audit logs - log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()}) - log.Info("ready done") - HTTPReply(w, CommandSucceed(&StringCmd{Info: "OK"}), json) -} - -func stackTrace(ctx interface{}, w http.ResponseWriter, r *http.Request) { - r.ParseForm() - _, json := ParseHTTPFormOptions(r) - - // audit logs - log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()}) - log.Info("stack trace") - - path, err := stackdump.DumpStacks("/tmp/") - if err != nil { - log.WithError(err).Error("failed to write goroutines dump") - HTTPReply(w, FailCommand(err), json) - } else { - log.Info("stack trace done") - HTTPReply(w, CommandSucceed(&StringCmd{Info: fmt.Sprintf("goroutine stacks written to %s", path)}), json) - } -} - -// DebugHTTPForm helper to print the form url parameters -func DebugHTTPForm(r *http.Request) { - for k, v := range r.Form { - logrus.Debugf("Form[%q] = %q\n", k, v) - } -} - -// JSONOutput contains details on JSON output printing -type JSONOutput struct { - enable bool - prettyPrint bool -} - -// ParseHTTPFormOptions easily parse the JSON printing options -func ParseHTTPFormOptions(r *http.Request) (bool, *JSONOutput) { - _, unsafe := r.Form["unsafe"] - v, json := r.Form["json"] - var pretty bool - if len(v) > 0 { - pretty = v[0] == "pretty" - } - return unsafe, &JSONOutput{enable: json, prettyPrint: pretty} -} - -// HTTPReply helper function that takes care of sending the message out -func HTTPReply(w http.ResponseWriter, r *HTTPResult, j *JSONOutput) (int, error) { - var response []byte - if j.enable { - w.Header().Set("Content-Type", "application/json") - var err error - if j.prettyPrint { - response, err = json.MarshalIndent(r, "", " ") - if err != nil { - response, _ = json.MarshalIndent(FailCommand(err), "", " ") - } - } else { - response, err = json.Marshal(r) - if err != nil { - response, _ = json.Marshal(FailCommand(err)) - } - } - } else { - response = []byte(r.String()) - } - return fmt.Fprint(w, string(response)) -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/errors.go b/vendor/github.com/docker/libnetwork/drivers/bridge/errors.go deleted file mode 100644 index 93960794cb18a..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/errors.go +++ /dev/null @@ -1,341 +0,0 @@ -package bridge - -import ( - "fmt" - "net" -) - -// ErrConfigExists error is returned when driver already has a config applied. -type ErrConfigExists struct{} - -func (ece *ErrConfigExists) Error() string { - return "configuration already exists, bridge configuration can be applied only once" -} - -// Forbidden denotes the type of this error -func (ece *ErrConfigExists) Forbidden() {} - -// ErrInvalidDriverConfig error is returned when Bridge Driver is passed an invalid config -type ErrInvalidDriverConfig struct{} - -func (eidc *ErrInvalidDriverConfig) Error() string { - return "Invalid configuration passed to Bridge Driver" -} - -// BadRequest denotes the type of this error -func (eidc *ErrInvalidDriverConfig) BadRequest() {} - -// ErrInvalidNetworkConfig error is returned when a network is created on a driver without valid config. -type ErrInvalidNetworkConfig struct{} - -func (einc *ErrInvalidNetworkConfig) Error() string { - return "trying to create a network on a driver without valid config" -} - -// Forbidden denotes the type of this error -func (einc *ErrInvalidNetworkConfig) Forbidden() {} - -// ErrInvalidContainerConfig error is returned when an endpoint create is attempted with an invalid configuration. -type ErrInvalidContainerConfig struct{} - -func (eicc *ErrInvalidContainerConfig) Error() string { - return "Error in joining a container due to invalid configuration" -} - -// BadRequest denotes the type of this error -func (eicc *ErrInvalidContainerConfig) BadRequest() {} - -// ErrInvalidEndpointConfig error is returned when an endpoint create is attempted with an invalid endpoint configuration. -type ErrInvalidEndpointConfig struct{} - -func (eiec *ErrInvalidEndpointConfig) Error() string { - return "trying to create an endpoint with an invalid endpoint configuration" -} - -// BadRequest denotes the type of this error -func (eiec *ErrInvalidEndpointConfig) BadRequest() {} - -// ErrNetworkExists error is returned when a network already exists and another network is created. -type ErrNetworkExists struct{} - -func (ene *ErrNetworkExists) Error() string { - return "network already exists, bridge can only have one network" -} - -// Forbidden denotes the type of this error -func (ene *ErrNetworkExists) Forbidden() {} - -// ErrIfaceName error is returned when a new name could not be generated. -type ErrIfaceName struct{} - -func (ein *ErrIfaceName) Error() string { - return "failed to find name for new interface" -} - -// InternalError denotes the type of this error -func (ein *ErrIfaceName) InternalError() {} - -// ErrNoIPAddr error is returned when bridge has no IPv4 address configured. -type ErrNoIPAddr struct{} - -func (enip *ErrNoIPAddr) Error() string { - return "bridge has no IPv4 address configured" -} - -// InternalError denotes the type of this error -func (enip *ErrNoIPAddr) InternalError() {} - -// ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid. -type ErrInvalidGateway struct{} - -func (eig *ErrInvalidGateway) Error() string { - return "default gateway ip must be part of the network" -} - -// BadRequest denotes the type of this error -func (eig *ErrInvalidGateway) BadRequest() {} - -// ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid. -type ErrInvalidContainerSubnet struct{} - -func (eis *ErrInvalidContainerSubnet) Error() string { - return "container subnet must be a subset of bridge network" -} - -// BadRequest denotes the type of this error -func (eis *ErrInvalidContainerSubnet) BadRequest() {} - -// ErrInvalidMtu is returned when the user provided MTU is not valid. -type ErrInvalidMtu int - -func (eim ErrInvalidMtu) Error() string { - return fmt.Sprintf("invalid MTU number: %d", int(eim)) -} - -// BadRequest denotes the type of this error -func (eim ErrInvalidMtu) BadRequest() {} - -// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid. -type ErrInvalidPort string - -func (ip ErrInvalidPort) Error() string { - return fmt.Sprintf("invalid transport port: %s", string(ip)) -} - -// BadRequest denotes the type of this error -func (ip ErrInvalidPort) BadRequest() {} - -// ErrUnsupportedAddressType is returned when the specified address type is not supported. -type ErrUnsupportedAddressType string - -func (uat ErrUnsupportedAddressType) Error() string { - return fmt.Sprintf("unsupported address type: %s", string(uat)) -} - -// BadRequest denotes the type of this error -func (uat ErrUnsupportedAddressType) BadRequest() {} - -// ErrInvalidAddressBinding is returned when the host address specified in the port binding is not valid. -type ErrInvalidAddressBinding string - -func (iab ErrInvalidAddressBinding) Error() string { - return fmt.Sprintf("invalid host address in port binding: %s", string(iab)) -} - -// BadRequest denotes the type of this error -func (iab ErrInvalidAddressBinding) BadRequest() {} - -// ActiveEndpointsError is returned when there are -// still active endpoints in the network being deleted. -type ActiveEndpointsError string - -func (aee ActiveEndpointsError) Error() string { - return fmt.Sprintf("network %s has active endpoint", string(aee)) -} - -// Forbidden denotes the type of this error -func (aee ActiveEndpointsError) Forbidden() {} - -// InvalidNetworkIDError is returned when the passed -// network id for an existing network is not a known id. -type InvalidNetworkIDError string - -func (inie InvalidNetworkIDError) Error() string { - return fmt.Sprintf("invalid network id %s", string(inie)) -} - -// NotFound denotes the type of this error -func (inie InvalidNetworkIDError) NotFound() {} - -// InvalidEndpointIDError is returned when the passed -// endpoint id is not valid. -type InvalidEndpointIDError string - -func (ieie InvalidEndpointIDError) Error() string { - return fmt.Sprintf("invalid endpoint id: %s", string(ieie)) -} - -// BadRequest denotes the type of this error -func (ieie InvalidEndpointIDError) BadRequest() {} - -// InvalidSandboxIDError is returned when the passed -// sandbox id is not valid. -type InvalidSandboxIDError string - -func (isie InvalidSandboxIDError) Error() string { - return fmt.Sprintf("invalid sandbox id: %s", string(isie)) -} - -// BadRequest denotes the type of this error -func (isie InvalidSandboxIDError) BadRequest() {} - -// EndpointNotFoundError is returned when the no endpoint -// with the passed endpoint id is found. -type EndpointNotFoundError string - -func (enfe EndpointNotFoundError) Error() string { - return fmt.Sprintf("endpoint not found: %s", string(enfe)) -} - -// NotFound denotes the type of this error -func (enfe EndpointNotFoundError) NotFound() {} - -// NonDefaultBridgeExistError is returned when a non-default -// bridge config is passed but it does not already exist. -type NonDefaultBridgeExistError string - -func (ndbee NonDefaultBridgeExistError) Error() string { - return fmt.Sprintf("bridge device with non default name %s must be created manually", string(ndbee)) -} - -// Forbidden denotes the type of this error -func (ndbee NonDefaultBridgeExistError) Forbidden() {} - -// NonDefaultBridgeNeedsIPError is returned when a non-default -// bridge config is passed but it has no ip configured -type NonDefaultBridgeNeedsIPError string - -func (ndbee NonDefaultBridgeNeedsIPError) Error() string { - return fmt.Sprintf("bridge device with non default name %s must have a valid IP address", string(ndbee)) -} - -// Forbidden denotes the type of this error -func (ndbee NonDefaultBridgeNeedsIPError) Forbidden() {} - -// FixedCIDRv4Error is returned when fixed-cidrv4 configuration -// failed. -type FixedCIDRv4Error struct { - Net *net.IPNet - Subnet *net.IPNet - Err error -} - -func (fcv4 *FixedCIDRv4Error) Error() string { - return fmt.Sprintf("setup FixedCIDRv4 failed for subnet %s in %s: %v", fcv4.Subnet, fcv4.Net, fcv4.Err) -} - -// InternalError denotes the type of this error -func (fcv4 *FixedCIDRv4Error) InternalError() {} - -// FixedCIDRv6Error is returned when fixed-cidrv6 configuration -// failed. -type FixedCIDRv6Error struct { - Net *net.IPNet - Err error -} - -func (fcv6 *FixedCIDRv6Error) Error() string { - return fmt.Sprintf("setup FixedCIDRv6 failed for subnet %s in %s: %v", fcv6.Net, fcv6.Net, fcv6.Err) -} - -// InternalError denotes the type of this error -func (fcv6 *FixedCIDRv6Error) InternalError() {} - -// IPTableCfgError is returned when an unexpected ip tables configuration is entered -type IPTableCfgError string - -func (name IPTableCfgError) Error() string { - return fmt.Sprintf("unexpected request to set IP tables for interface: %s", string(name)) -} - -// BadRequest denotes the type of this error -func (name IPTableCfgError) BadRequest() {} - -// InvalidIPTablesCfgError is returned when an invalid ip tables configuration is entered -type InvalidIPTablesCfgError string - -func (action InvalidIPTablesCfgError) Error() string { - return fmt.Sprintf("Invalid IPTables action '%s'", string(action)) -} - -// BadRequest denotes the type of this error -func (action InvalidIPTablesCfgError) BadRequest() {} - -// IPv4AddrRangeError is returned when a valid IP address range couldn't be found. -type IPv4AddrRangeError string - -func (name IPv4AddrRangeError) Error() string { - return fmt.Sprintf("can't find an address range for interface %q", string(name)) -} - -// BadRequest denotes the type of this error -func (name IPv4AddrRangeError) BadRequest() {} - -// IPv4AddrAddError is returned when IPv4 address could not be added to the bridge. -type IPv4AddrAddError struct { - IP *net.IPNet - Err error -} - -func (ipv4 *IPv4AddrAddError) Error() string { - return fmt.Sprintf("failed to add IPv4 address %s to bridge: %v", ipv4.IP, ipv4.Err) -} - -// InternalError denotes the type of this error -func (ipv4 *IPv4AddrAddError) InternalError() {} - -// IPv6AddrAddError is returned when IPv6 address could not be added to the bridge. -type IPv6AddrAddError struct { - IP *net.IPNet - Err error -} - -func (ipv6 *IPv6AddrAddError) Error() string { - return fmt.Sprintf("failed to add IPv6 address %s to bridge: %v", ipv6.IP, ipv6.Err) -} - -// InternalError denotes the type of this error -func (ipv6 *IPv6AddrAddError) InternalError() {} - -// IPv4AddrNoMatchError is returned when the bridge's IPv4 address does not match configured. -type IPv4AddrNoMatchError struct { - IP net.IP - CfgIP net.IP -} - -func (ipv4 *IPv4AddrNoMatchError) Error() string { - return fmt.Sprintf("bridge IPv4 (%s) does not match requested configuration %s", ipv4.IP, ipv4.CfgIP) -} - -// BadRequest denotes the type of this error -func (ipv4 *IPv4AddrNoMatchError) BadRequest() {} - -// IPv6AddrNoMatchError is returned when the bridge's IPv6 address does not match configured. -type IPv6AddrNoMatchError net.IPNet - -func (ipv6 *IPv6AddrNoMatchError) Error() string { - return fmt.Sprintf("bridge IPv6 addresses do not match the expected bridge configuration %s", (*net.IPNet)(ipv6).String()) -} - -// BadRequest denotes the type of this error -func (ipv6 *IPv6AddrNoMatchError) BadRequest() {} - -// InvalidLinkIPAddrError is returned when a link is configured to a container with an invalid ip address -type InvalidLinkIPAddrError string - -func (address InvalidLinkIPAddrError) Error() string { - return fmt.Sprintf("Cannot link to a container with Invalid IP Address '%s'", string(address)) -} - -// BadRequest denotes the type of this error -func (address InvalidLinkIPAddrError) BadRequest() {} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/interface.go b/vendor/github.com/docker/libnetwork/drivers/bridge/interface.go deleted file mode 100644 index c9f3e8dfb7474..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/interface.go +++ /dev/null @@ -1,86 +0,0 @@ -package bridge - -import ( - "fmt" - "net" - - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -const ( - // DefaultBridgeName is the default name for the bridge interface managed - // by the driver when unspecified by the caller. - DefaultBridgeName = "docker0" -) - -// Interface models the bridge network device. -type bridgeInterface struct { - Link netlink.Link - bridgeIPv4 *net.IPNet - bridgeIPv6 *net.IPNet - gatewayIPv4 net.IP - gatewayIPv6 net.IP - nlh *netlink.Handle -} - -// newInterface creates a new bridge interface structure. It attempts to find -// an already existing device identified by the configuration BridgeName field, -// or the default bridge name when unspecified, but doesn't attempt to create -// one when missing -func newInterface(nlh *netlink.Handle, config *networkConfiguration) (*bridgeInterface, error) { - var err error - i := &bridgeInterface{nlh: nlh} - - // Initialize the bridge name to the default if unspecified. - if config.BridgeName == "" { - config.BridgeName = DefaultBridgeName - } - - // Attempt to find an existing bridge named with the specified name. - i.Link, err = nlh.LinkByName(config.BridgeName) - if err != nil { - logrus.Debugf("Did not find any interface with name %s: %v", config.BridgeName, err) - } else if _, ok := i.Link.(*netlink.Bridge); !ok { - return nil, fmt.Errorf("existing interface %s is not a bridge", i.Link.Attrs().Name) - } - return i, nil -} - -// exists indicates if the existing bridge interface exists on the system. -func (i *bridgeInterface) exists() bool { - return i.Link != nil -} - -// addresses returns all IPv4 addresses and all IPv6 addresses for the bridge interface. -func (i *bridgeInterface) addresses() ([]netlink.Addr, []netlink.Addr, error) { - v4addr, err := i.nlh.AddrList(i.Link, netlink.FAMILY_V4) - if err != nil { - return nil, nil, fmt.Errorf("Failed to retrieve V4 addresses: %v", err) - } - - v6addr, err := i.nlh.AddrList(i.Link, netlink.FAMILY_V6) - if err != nil { - return nil, nil, fmt.Errorf("Failed to retrieve V6 addresses: %v", err) - } - - if len(v4addr) == 0 { - return nil, v6addr, nil - } - return v4addr, v6addr, nil -} - -func (i *bridgeInterface) programIPv6Address() error { - _, nlAddressList, err := i.addresses() - if err != nil { - return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: fmt.Errorf("failed to retrieve address list: %v", err)} - } - nlAddr := netlink.Addr{IPNet: i.bridgeIPv6} - if findIPv6Address(nlAddr, nlAddressList) { - return nil - } - if err := i.nlh.AddrAdd(i.Link, &nlAddr); err != nil { - return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: err} - } - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/labels.go b/vendor/github.com/docker/libnetwork/drivers/bridge/labels.go deleted file mode 100644 index 7447bd3f939de..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/labels.go +++ /dev/null @@ -1,18 +0,0 @@ -package bridge - -const ( - // BridgeName label for bridge driver - BridgeName = "com.docker.network.bridge.name" - - // EnableIPMasquerade label for bridge driver - EnableIPMasquerade = "com.docker.network.bridge.enable_ip_masquerade" - - // EnableICC label - EnableICC = "com.docker.network.bridge.enable_icc" - - // DefaultBindingIP label - DefaultBindingIP = "com.docker.network.bridge.host_binding_ipv4" - - // DefaultBridge label - DefaultBridge = "com.docker.network.bridge.default_bridge" -) diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/link.go b/vendor/github.com/docker/libnetwork/drivers/bridge/link.go deleted file mode 100644 index d364516f1ae25..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/link.go +++ /dev/null @@ -1,85 +0,0 @@ -package bridge - -import ( - "fmt" - "net" - - "github.com/docker/libnetwork/iptables" - "github.com/docker/libnetwork/types" - "github.com/sirupsen/logrus" -) - -type link struct { - parentIP string - childIP string - ports []types.TransportPort - bridge string -} - -func (l *link) String() string { - return fmt.Sprintf("%s <-> %s [%v] on %s", l.parentIP, l.childIP, l.ports, l.bridge) -} - -func newLink(parentIP, childIP string, ports []types.TransportPort, bridge string) *link { - return &link{ - childIP: childIP, - parentIP: parentIP, - ports: ports, - bridge: bridge, - } - -} - -func (l *link) Enable() error { - // -A == iptables append flag - linkFunction := func() error { - return linkContainers("-A", l.parentIP, l.childIP, l.ports, l.bridge, false) - } - - iptables.OnReloaded(func() { linkFunction() }) - return linkFunction() -} - -func (l *link) Disable() { - // -D == iptables delete flag - err := linkContainers("-D", l.parentIP, l.childIP, l.ports, l.bridge, true) - if err != nil { - logrus.Errorf("Error removing IPTables rules for a link %s due to %s", l.String(), err.Error()) - } - // Return proper error once we move to use a proper iptables package - // that returns typed errors -} - -func linkContainers(action, parentIP, childIP string, ports []types.TransportPort, bridge string, - ignoreErrors bool) error { - var nfAction iptables.Action - - switch action { - case "-A": - nfAction = iptables.Append - case "-I": - nfAction = iptables.Insert - case "-D": - nfAction = iptables.Delete - default: - return InvalidIPTablesCfgError(action) - } - - ip1 := net.ParseIP(parentIP) - if ip1 == nil { - return InvalidLinkIPAddrError(parentIP) - } - ip2 := net.ParseIP(childIP) - if ip2 == nil { - return InvalidLinkIPAddrError(childIP) - } - - chain := iptables.ChainInfo{Name: DockerChain} - for _, port := range ports { - err := chain.Link(nfAction, ip1, ip2, int(port.Port), port.Proto.String(), bridge) - if !ignoreErrors && err != nil { - return err - } - } - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_linux_armppc64.go b/vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_linux_armppc64.go deleted file mode 100644 index 739d9c6ba3329..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_linux_armppc64.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build arm ppc64 ppc64le - -package bridge - -func ifrDataByte(b byte) uint8 { - return uint8(b) -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_linux_notarm.go b/vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_linux_notarm.go deleted file mode 100644 index df526952f7876..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/netlink_deprecated_linux_notarm.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build !arm,!ppc64,!ppc64le - -package bridge - -func ifrDataByte(b byte) int8 { - return int8(b) -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go b/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go deleted file mode 100644 index 853129fc2715f..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/port_mapping.go +++ /dev/null @@ -1,132 +0,0 @@ -package bridge - -import ( - "bytes" - "errors" - "fmt" - "net" - - "github.com/docker/libnetwork/types" - "github.com/ishidawataru/sctp" - "github.com/sirupsen/logrus" -) - -var ( - defaultBindingIP = net.IPv4(0, 0, 0, 0) -) - -func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { - if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil { - return nil, nil - } - - defHostIP := defaultBindingIP - if reqDefBindIP != nil { - defHostIP = reqDefBindIP - } - - return n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled) -} - -func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { - bs := make([]types.PortBinding, 0, len(bindings)) - for _, c := range bindings { - b := c.GetCopy() - if err := n.allocatePort(&b, containerIP, defHostIP, ulPxyEnabled); err != nil { - // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message - if cuErr := n.releasePortsInternal(bs); cuErr != nil { - logrus.Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr) - } - return nil, err - } - bs = append(bs, b) - } - return bs, nil -} - -func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) error { - var ( - host net.Addr - err error - ) - - // Store the container interface address in the operational binding - bnd.IP = containerIP - - // Adjust the host address in the operational binding - if len(bnd.HostIP) == 0 { - bnd.HostIP = defHostIP - } - - // Adjust HostPortEnd if this is not a range. - if bnd.HostPortEnd == 0 { - bnd.HostPortEnd = bnd.HostPort - } - - // Construct the container side transport address - container, err := bnd.ContainerAddr() - if err != nil { - return err - } - - // Try up to maxAllocatePortAttempts times to get a port that's not already allocated. - for i := 0; i < maxAllocatePortAttempts; i++ { - if host, err = n.portMapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), ulPxyEnabled); err == nil { - break - } - // There is no point in immediately retrying to map an explicitly chosen port. - if bnd.HostPort != 0 { - logrus.Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err) - break - } - logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1) - } - if err != nil { - return err - } - - // Save the host port (regardless it was or not specified in the binding) - switch netAddr := host.(type) { - case *net.TCPAddr: - bnd.HostPort = uint16(host.(*net.TCPAddr).Port) - return nil - case *net.UDPAddr: - bnd.HostPort = uint16(host.(*net.UDPAddr).Port) - return nil - case *sctp.SCTPAddr: - bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port) - return nil - default: - // For completeness - return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr)) - } -} - -func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error { - return n.releasePortsInternal(ep.portMapping) -} - -func (n *bridgeNetwork) releasePortsInternal(bindings []types.PortBinding) error { - var errorBuf bytes.Buffer - - // Attempt to release all port bindings, do not stop on failure - for _, m := range bindings { - if err := n.releasePort(m); err != nil { - errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err)) - } - } - - if errorBuf.Len() != 0 { - return errors.New(errorBuf.String()) - } - return nil -} - -func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error { - // Construct the host side transport address - host, err := bnd.HostAddr() - if err != nil { - return err - } - return n.portMapper.Unmap(host) -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup.go deleted file mode 100644 index eeb3611b78e56..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup.go +++ /dev/null @@ -1,26 +0,0 @@ -package bridge - -type setupStep func(*networkConfiguration, *bridgeInterface) error - -type bridgeSetup struct { - config *networkConfiguration - bridge *bridgeInterface - steps []setupStep -} - -func newBridgeSetup(c *networkConfiguration, i *bridgeInterface) *bridgeSetup { - return &bridgeSetup{config: c, bridge: i} -} - -func (b *bridgeSetup) apply() error { - for _, fn := range b.steps { - if err := fn(b.config, b.bridge); err != nil { - return err - } - } - return nil -} - -func (b *bridgeSetup) queueStep(step setupStep) { - b.steps = append(b.steps, step) -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_device.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_device.go deleted file mode 100644 index a9dfd06771f8f..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_device.go +++ /dev/null @@ -1,68 +0,0 @@ -package bridge - -import ( - "fmt" - - "github.com/docker/docker/pkg/parsers/kernel" - "github.com/docker/libnetwork/netutils" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -// SetupDevice create a new bridge interface/ -func setupDevice(config *networkConfiguration, i *bridgeInterface) error { - var setMac bool - - // We only attempt to create the bridge when the requested device name is - // the default one. - if config.BridgeName != DefaultBridgeName && config.DefaultBridge { - return NonDefaultBridgeExistError(config.BridgeName) - } - - // Set the bridgeInterface netlink.Bridge. - i.Link = &netlink.Bridge{ - LinkAttrs: netlink.LinkAttrs{ - Name: config.BridgeName, - }, - } - - // Only set the bridge's MAC address if the kernel version is > 3.3, as it - // was not supported before that. - kv, err := kernel.GetKernelVersion() - if err != nil { - logrus.Errorf("Failed to check kernel versions: %v. Will not assign a MAC address to the bridge interface", err) - } else { - setMac = kv.Kernel > 3 || (kv.Kernel == 3 && kv.Major >= 3) - } - - if err = i.nlh.LinkAdd(i.Link); err != nil { - logrus.Debugf("Failed to create bridge %s via netlink. Trying ioctl", config.BridgeName) - return ioctlCreateBridge(config.BridgeName, setMac) - } - - if setMac { - hwAddr := netutils.GenerateRandomMAC() - if err = i.nlh.LinkSetHardwareAddr(i.Link, hwAddr); err != nil { - return fmt.Errorf("failed to set bridge mac-address %s : %s", hwAddr, err.Error()) - } - logrus.Debugf("Setting bridge mac address to %s", hwAddr) - } - return err -} - -// SetupDeviceUp ups the given bridge interface. -func setupDeviceUp(config *networkConfiguration, i *bridgeInterface) error { - err := i.nlh.LinkSetUp(i.Link) - if err != nil { - return fmt.Errorf("Failed to set link up for %s: %v", config.BridgeName, err) - } - - // Attempt to update the bridge interface to refresh the flags status, - // ignoring any failure to do so. - if lnk, err := i.nlh.LinkByName(config.BridgeName); err == nil { - i.Link = lnk - } else { - logrus.Warnf("Failed to retrieve link for interface (%s): %v", config.BridgeName, err) - } - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go deleted file mode 100644 index 50cbdb1ddcef4..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_firewalld.go +++ /dev/null @@ -1,20 +0,0 @@ -package bridge - -import "github.com/docker/libnetwork/iptables" - -func (n *bridgeNetwork) setupFirewalld(config *networkConfiguration, i *bridgeInterface) error { - d := n.driver - d.Lock() - driverConfig := d.config - d.Unlock() - - // Sanity check. - if !driverConfig.EnableIPTables { - return IPTableCfgError(config.BridgeName) - } - - iptables.OnReloaded(func() { n.setupIPTables(config, i) }) - iptables.OnReloaded(n.portMapper.ReMapAll) - - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_forwarding.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_forwarding.go deleted file mode 100644 index 355a14d996991..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_forwarding.go +++ /dev/null @@ -1,56 +0,0 @@ -package bridge - -import ( - "fmt" - "io/ioutil" - - "github.com/docker/libnetwork/iptables" - "github.com/sirupsen/logrus" -) - -const ( - ipv4ForwardConf = "/proc/sys/net/ipv4/ip_forward" - ipv4ForwardConfPerm = 0644 -) - -func configureIPForwarding(enable bool) error { - var val byte - if enable { - val = '1' - } - return ioutil.WriteFile(ipv4ForwardConf, []byte{val, '\n'}, ipv4ForwardConfPerm) -} - -func setupIPForwarding(enableIPTables bool) error { - // Get current IPv4 forward setup - ipv4ForwardData, err := ioutil.ReadFile(ipv4ForwardConf) - if err != nil { - return fmt.Errorf("Cannot read IP forwarding setup: %v", err) - } - - // Enable IPv4 forwarding only if it is not already enabled - if ipv4ForwardData[0] != '1' { - // Enable IPv4 forwarding - if err := configureIPForwarding(true); err != nil { - return fmt.Errorf("Enabling IP forwarding failed: %v", err) - } - // When enabling ip_forward set the default policy on forward chain to - // drop only if the daemon option iptables is not set to false. - if !enableIPTables { - return nil - } - if err := iptables.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil { - if err := configureIPForwarding(false); err != nil { - logrus.Errorf("Disabling IP forwarding failed, %v", err) - } - return err - } - iptables.OnReloaded(func() { - logrus.Debug("Setting the default DROP policy on firewall reload") - if err := iptables.SetDefaultPolicy(iptables.Filter, "FORWARD", iptables.Drop); err != nil { - logrus.Warnf("Settig the default DROP policy on firewall reload failed, %v", err) - } - }) - } - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go deleted file mode 100644 index 5865a18f18d25..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go +++ /dev/null @@ -1,363 +0,0 @@ -package bridge - -import ( - "errors" - "fmt" - "net" - - "github.com/docker/libnetwork/iptables" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -// DockerChain: DOCKER iptable chain name -const ( - DockerChain = "DOCKER" - // Isolation between bridge networks is achieved in two stages by means - // of the following two chains in the filter table. The first chain matches - // on the source interface being a bridge network's bridge and the - // destination being a different interface. A positive match leads to the - // second isolation chain. No match returns to the parent chain. The second - // isolation chain matches on destination interface being a bridge network's - // bridge. A positive match identifies a packet originated from one bridge - // network's bridge destined to another bridge network's bridge and will - // result in the packet being dropped. No match returns to the parent chain. - IsolationChain1 = "DOCKER-ISOLATION-STAGE-1" - IsolationChain2 = "DOCKER-ISOLATION-STAGE-2" -) - -func setupIPChains(config *configuration) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) { - // Sanity check. - if config.EnableIPTables == false { - return nil, nil, nil, nil, errors.New("cannot create new chains, EnableIPTable is disabled") - } - - hairpinMode := !config.EnableUserlandProxy - - natChain, err := iptables.NewChain(DockerChain, iptables.Nat, hairpinMode) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to create NAT chain %s: %v", DockerChain, err) - } - defer func() { - if err != nil { - if err := iptables.RemoveExistingChain(DockerChain, iptables.Nat); err != nil { - logrus.Warnf("failed on removing iptables NAT chain %s on cleanup: %v", DockerChain, err) - } - } - }() - - filterChain, err := iptables.NewChain(DockerChain, iptables.Filter, false) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER chain %s: %v", DockerChain, err) - } - defer func() { - if err != nil { - if err := iptables.RemoveExistingChain(DockerChain, iptables.Filter); err != nil { - logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", DockerChain, err) - } - } - }() - - isolationChain1, err := iptables.NewChain(IsolationChain1, iptables.Filter, false) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err) - } - defer func() { - if err != nil { - if err := iptables.RemoveExistingChain(IsolationChain1, iptables.Filter); err != nil { - logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain1, err) - } - } - }() - - isolationChain2, err := iptables.NewChain(IsolationChain2, iptables.Filter, false) - if err != nil { - return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err) - } - defer func() { - if err != nil { - if err := iptables.RemoveExistingChain(IsolationChain2, iptables.Filter); err != nil { - logrus.Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain2, err) - } - } - }() - - if err := iptables.AddReturnRule(IsolationChain1); err != nil { - return nil, nil, nil, nil, err - } - - if err := iptables.AddReturnRule(IsolationChain2); err != nil { - return nil, nil, nil, nil, err - } - - return natChain, filterChain, isolationChain1, isolationChain2, nil -} - -func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInterface) error { - var err error - - d := n.driver - d.Lock() - driverConfig := d.config - d.Unlock() - - // Sanity check. - if driverConfig.EnableIPTables == false { - return errors.New("Cannot program chains, EnableIPTable is disabled") - } - - // Pickup this configuration option from driver - hairpinMode := !driverConfig.EnableUserlandProxy - - maskedAddrv4 := &net.IPNet{ - IP: i.bridgeIPv4.IP.Mask(i.bridgeIPv4.Mask), - Mask: i.bridgeIPv4.Mask, - } - if config.Internal { - if err = setupInternalNetworkRules(config.BridgeName, maskedAddrv4, config.EnableICC, true); err != nil { - return fmt.Errorf("Failed to Setup IP tables: %s", err.Error()) - } - n.registerIptCleanFunc(func() error { - return setupInternalNetworkRules(config.BridgeName, maskedAddrv4, config.EnableICC, false) - }) - } else { - if err = setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil { - return fmt.Errorf("Failed to Setup IP tables: %s", err.Error()) - } - n.registerIptCleanFunc(func() error { - return setupIPTablesInternal(config.BridgeName, maskedAddrv4, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false) - }) - natChain, filterChain, _, _, err := n.getDriverChains() - if err != nil { - return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error()) - } - - err = iptables.ProgramChain(natChain, config.BridgeName, hairpinMode, true) - if err != nil { - return fmt.Errorf("Failed to program NAT chain: %s", err.Error()) - } - - err = iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, true) - if err != nil { - return fmt.Errorf("Failed to program FILTER chain: %s", err.Error()) - } - - n.registerIptCleanFunc(func() error { - return iptables.ProgramChain(filterChain, config.BridgeName, hairpinMode, false) - }) - - n.portMapper.SetIptablesChain(natChain, n.getNetworkBridgeName()) - } - - d.Lock() - err = iptables.EnsureJumpRule("FORWARD", IsolationChain1) - d.Unlock() - if err != nil { - return err - } - - return nil -} - -type iptRule struct { - table iptables.Table - chain string - preArgs []string - args []string -} - -func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, hairpin, enable bool) error { - - var ( - address = addr.String() - natRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}} - hpNatRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}} - skipDNAT = iptRule{table: iptables.Nat, chain: DockerChain, preArgs: []string{"-t", "nat"}, args: []string{"-i", bridgeIface, "-j", "RETURN"}} - outRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}} - ) - - // Set NAT. - if ipmasq { - if err := programChainRule(natRule, "NAT", enable); err != nil { - return err - } - } - - if ipmasq && !hairpin { - if err := programChainRule(skipDNAT, "SKIP DNAT", enable); err != nil { - return err - } - } - - // In hairpin mode, masquerade traffic from localhost - if hairpin { - if err := programChainRule(hpNatRule, "MASQ LOCAL HOST", enable); err != nil { - return err - } - } - - // Set Inter Container Communication. - if err := setIcc(bridgeIface, icc, enable); err != nil { - return err - } - - // Set Accept on all non-intercontainer outgoing packets. - return programChainRule(outRule, "ACCEPT NON_ICC OUTGOING", enable) -} - -func programChainRule(rule iptRule, ruleDescr string, insert bool) error { - var ( - prefix []string - operation string - condition bool - doesExist = iptables.Exists(rule.table, rule.chain, rule.args...) - ) - - if insert { - condition = !doesExist - prefix = []string{"-I", rule.chain} - operation = "enable" - } else { - condition = doesExist - prefix = []string{"-D", rule.chain} - operation = "disable" - } - if rule.preArgs != nil { - prefix = append(rule.preArgs, prefix...) - } - - if condition { - if err := iptables.RawCombinedOutput(append(prefix, rule.args...)...); err != nil { - return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error()) - } - } - - return nil -} - -func setIcc(bridgeIface string, iccEnable, insert bool) error { - var ( - table = iptables.Filter - chain = "FORWARD" - args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"} - acceptArgs = append(args, "ACCEPT") - dropArgs = append(args, "DROP") - ) - - if insert { - if !iccEnable { - iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...) - - if !iptables.Exists(table, chain, dropArgs...) { - if err := iptables.RawCombinedOutput(append([]string{"-A", chain}, dropArgs...)...); err != nil { - return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error()) - } - } - } else { - iptables.Raw(append([]string{"-D", chain}, dropArgs...)...) - - if !iptables.Exists(table, chain, acceptArgs...) { - if err := iptables.RawCombinedOutput(append([]string{"-I", chain}, acceptArgs...)...); err != nil { - return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error()) - } - } - } - } else { - // Remove any ICC rule. - if !iccEnable { - if iptables.Exists(table, chain, dropArgs...) { - iptables.Raw(append([]string{"-D", chain}, dropArgs...)...) - } - } else { - if iptables.Exists(table, chain, acceptArgs...) { - iptables.Raw(append([]string{"-D", chain}, acceptArgs...)...) - } - } - } - - return nil -} - -// Control Inter Network Communication. Install[Remove] only if it is [not] present. -func setINC(iface string, enable bool) error { - var ( - action = iptables.Insert - actionMsg = "add" - chains = []string{IsolationChain1, IsolationChain2} - rules = [][]string{ - {"-i", iface, "!", "-o", iface, "-j", IsolationChain2}, - {"-o", iface, "-j", "DROP"}, - } - ) - - if !enable { - action = iptables.Delete - actionMsg = "remove" - } - - for i, chain := range chains { - if err := iptables.ProgramRule(iptables.Filter, chain, action, rules[i]); err != nil { - msg := fmt.Sprintf("unable to %s inter-network communication rule: %v", actionMsg, err) - if enable { - if i == 1 { - // Rollback the rule installed on first chain - if err2 := iptables.ProgramRule(iptables.Filter, chains[0], iptables.Delete, rules[0]); err2 != nil { - logrus.Warn("Failed to rollback iptables rule after failure (%v): %v", err, err2) - } - } - return fmt.Errorf(msg) - } - logrus.Warn(msg) - } - } - - return nil -} - -// Obsolete chain from previous docker versions -const oldIsolationChain = "DOCKER-ISOLATION" - -func removeIPChains() { - // Remove obsolete rules from default chains - iptables.ProgramRule(iptables.Filter, "FORWARD", iptables.Delete, []string{"-j", oldIsolationChain}) - - // Remove chains - for _, chainInfo := range []iptables.ChainInfo{ - {Name: DockerChain, Table: iptables.Nat}, - {Name: DockerChain, Table: iptables.Filter}, - {Name: IsolationChain1, Table: iptables.Filter}, - {Name: IsolationChain2, Table: iptables.Filter}, - {Name: oldIsolationChain, Table: iptables.Filter}, - } { - if err := chainInfo.Remove(); err != nil { - logrus.Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err) - } - } -} - -func setupInternalNetworkRules(bridgeIface string, addr net.Addr, icc, insert bool) error { - var ( - inDropRule = iptRule{table: iptables.Filter, chain: IsolationChain1, args: []string{"-i", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"}} - outDropRule = iptRule{table: iptables.Filter, chain: IsolationChain1, args: []string{"-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"}} - ) - if err := programChainRule(inDropRule, "DROP INCOMING", insert); err != nil { - return err - } - if err := programChainRule(outDropRule, "DROP OUTGOING", insert); err != nil { - return err - } - // Set Inter Container Communication. - return setIcc(bridgeIface, icc, insert) -} - -func clearEndpointConnections(nlh *netlink.Handle, ep *bridgeEndpoint) { - var ipv4List []net.IP - var ipv6List []net.IP - if ep.addr != nil { - ipv4List = append(ipv4List, ep.addr.IP) - } - if ep.addrv6 != nil { - ipv6List = append(ipv6List, ep.addrv6.IP) - } - iptables.DeleteConntrackEntries(nlh, ipv4List, ipv6List) -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go deleted file mode 100644 index 983669f324b84..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go +++ /dev/null @@ -1,80 +0,0 @@ -package bridge - -import ( - "errors" - "fmt" - "io/ioutil" - "net" - "path/filepath" - - "github.com/docker/libnetwork/types" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -func selectIPv4Address(addresses []netlink.Addr, selector *net.IPNet) (netlink.Addr, error) { - if len(addresses) == 0 { - return netlink.Addr{}, errors.New("unable to select an address as the address pool is empty") - } - if selector != nil { - for _, addr := range addresses { - if selector.Contains(addr.IP) { - return addr, nil - } - } - } - return addresses[0], nil -} - -func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error { - addrv4List, _, err := i.addresses() - if err != nil { - return fmt.Errorf("failed to retrieve bridge interface addresses: %v", err) - } - - addrv4, _ := selectIPv4Address(addrv4List, config.AddressIPv4) - - if !types.CompareIPNet(addrv4.IPNet, config.AddressIPv4) { - if addrv4.IPNet != nil { - if err := i.nlh.AddrDel(i.Link, &addrv4); err != nil { - return fmt.Errorf("failed to remove current ip address from bridge: %v", err) - } - } - logrus.Debugf("Assigning address to bridge interface %s: %s", config.BridgeName, config.AddressIPv4) - if err := i.nlh.AddrAdd(i.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil { - return &IPv4AddrAddError{IP: config.AddressIPv4, Err: err} - } - } - - // Store bridge network and default gateway - i.bridgeIPv4 = config.AddressIPv4 - i.gatewayIPv4 = config.AddressIPv4.IP - - return nil -} - -func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error { - if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) { - return &ErrInvalidGateway{} - } - - // Store requested default gateway - i.gatewayIPv4 = config.DefaultGatewayIPv4 - - return nil -} - -func setupLoopbackAddressesRouting(config *networkConfiguration, i *bridgeInterface) error { - sysPath := filepath.Join("/proc/sys/net/ipv4/conf", config.BridgeName, "route_localnet") - ipv4LoRoutingData, err := ioutil.ReadFile(sysPath) - if err != nil { - return fmt.Errorf("Cannot read IPv4 local routing setup: %v", err) - } - // Enable loopback addresses routing only if it isn't already enabled - if ipv4LoRoutingData[0] != '1' { - if err := ioutil.WriteFile(sysPath, []byte{'1', '\n'}, 0644); err != nil { - return fmt.Errorf("Unable to enable local routing for hairpin mode: %v", err) - } - } - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go b/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go deleted file mode 100644 index b944be081e3dd..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go +++ /dev/null @@ -1,119 +0,0 @@ -package bridge - -import ( - "fmt" - "io/ioutil" - "net" - "os" - - "github.com/docker/libnetwork/types" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -var bridgeIPv6 *net.IPNet - -const ( - bridgeIPv6Str = "fe80::1/64" - ipv6ForwardConfPerm = 0644 - ipv6ForwardConfDefault = "/proc/sys/net/ipv6/conf/default/forwarding" - ipv6ForwardConfAll = "/proc/sys/net/ipv6/conf/all/forwarding" -) - -func init() { - // We allow ourselves to panic in this special case because we indicate a - // failure to parse a compile-time define constant. - var err error - if bridgeIPv6, err = types.ParseCIDR(bridgeIPv6Str); err != nil { - panic(fmt.Sprintf("Cannot parse default bridge IPv6 address %q: %v", bridgeIPv6Str, err)) - } -} - -func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error { - procFile := "/proc/sys/net/ipv6/conf/" + config.BridgeName + "/disable_ipv6" - ipv6BridgeData, err := ioutil.ReadFile(procFile) - if err != nil { - return fmt.Errorf("Cannot read IPv6 setup for bridge %v: %v", config.BridgeName, err) - } - // Enable IPv6 on the bridge only if it isn't already enabled - if ipv6BridgeData[0] != '0' { - if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, ipv6ForwardConfPerm); err != nil { - return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err) - } - } - - // Store bridge network and default gateway - i.bridgeIPv6 = bridgeIPv6 - i.gatewayIPv6 = i.bridgeIPv6.IP - - if err := i.programIPv6Address(); err != nil { - return err - } - - if config.AddressIPv6 == nil { - return nil - } - - // Store the user specified bridge network and network gateway and program it - i.bridgeIPv6 = config.AddressIPv6 - i.gatewayIPv6 = config.AddressIPv6.IP - - if err := i.programIPv6Address(); err != nil { - return err - } - - // Setting route to global IPv6 subnet - logrus.Debugf("Adding route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName) - err = i.nlh.RouteAdd(&netlink.Route{ - Scope: netlink.SCOPE_UNIVERSE, - LinkIndex: i.Link.Attrs().Index, - Dst: config.AddressIPv6, - }) - if err != nil && !os.IsExist(err) { - logrus.Errorf("Could not add route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName) - } - - return nil -} - -func setupGatewayIPv6(config *networkConfiguration, i *bridgeInterface) error { - if config.AddressIPv6 == nil { - return &ErrInvalidContainerSubnet{} - } - if !config.AddressIPv6.Contains(config.DefaultGatewayIPv6) { - return &ErrInvalidGateway{} - } - - // Store requested default gateway - i.gatewayIPv6 = config.DefaultGatewayIPv6 - - return nil -} - -func setupIPv6Forwarding(config *networkConfiguration, i *bridgeInterface) error { - // Get current IPv6 default forwarding setup - ipv6ForwardDataDefault, err := ioutil.ReadFile(ipv6ForwardConfDefault) - if err != nil { - return fmt.Errorf("Cannot read IPv6 default forwarding setup: %v", err) - } - // Enable IPv6 default forwarding only if it is not already enabled - if ipv6ForwardDataDefault[0] != '1' { - if err := ioutil.WriteFile(ipv6ForwardConfDefault, []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil { - logrus.Warnf("Unable to enable IPv6 default forwarding: %v", err) - } - } - - // Get current IPv6 all forwarding setup - ipv6ForwardDataAll, err := ioutil.ReadFile(ipv6ForwardConfAll) - if err != nil { - return fmt.Errorf("Cannot read IPv6 all forwarding setup: %v", err) - } - // Enable IPv6 all forwarding only if it is not already enabled - if ipv6ForwardDataAll[0] != '1' { - if err := ioutil.WriteFile(ipv6ForwardConfAll, []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil { - logrus.Warnf("Unable to enable IPv6 all forwarding: %v", err) - } - } - - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/host/host.go b/vendor/github.com/docker/libnetwork/drivers/host/host.go deleted file mode 100644 index a71d461380f8e..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/host/host.go +++ /dev/null @@ -1,106 +0,0 @@ -package host - -import ( - "sync" - - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/types" -) - -const networkType = "host" - -type driver struct { - network string - sync.Mutex -} - -// Init registers a new instance of host driver -func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { - c := driverapi.Capability{ - DataScope: datastore.LocalScope, - ConnectivityScope: datastore.LocalScope, - } - return dc.RegisterDriver(networkType, &driver{}, c) -} - -func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { - return nil, types.NotImplementedErrorf("not implemented") -} - -func (d *driver) NetworkFree(id string) error { - return types.NotImplementedErrorf("not implemented") -} - -func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { -} - -func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { - return "", nil -} - -func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { - d.Lock() - defer d.Unlock() - - if d.network != "" { - return types.ForbiddenErrorf("only one instance of \"%s\" network is allowed", networkType) - } - - d.network = id - - return nil -} - -func (d *driver) DeleteNetwork(nid string) error { - return types.ForbiddenErrorf("network of type \"%s\" cannot be deleted", networkType) -} - -func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { - return nil -} - -func (d *driver) DeleteEndpoint(nid, eid string) error { - return nil -} - -func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { - return make(map[string]interface{}, 0), nil -} - -// Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { - return nil -} - -// Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid string) error { - return nil -} - -func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { - return nil -} - -func (d *driver) RevokeExternalConnectivity(nid, eid string) error { - return nil -} - -func (d *driver) Type() string { - return networkType -} - -func (d *driver) IsBuiltIn() bool { - return true -} - -// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster -func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { - return nil -} - -// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster -func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_joinleave.go b/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_joinleave.go deleted file mode 100644 index fc56bce5a6681..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan_joinleave.go +++ /dev/null @@ -1,199 +0,0 @@ -package ipvlan - -import ( - "fmt" - "net" - - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" - "github.com/sirupsen/logrus" -) - -type staticRoute struct { - Destination *net.IPNet - RouteType int - NextHop net.IP -} - -const ( - defaultV4RouteCidr = "0.0.0.0/0" - defaultV6RouteCidr = "::/0" -) - -// Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { - defer osl.InitOSContext()() - n, err := d.getNetwork(nid) - if err != nil { - return err - } - endpoint := n.endpoint(eid) - if endpoint == nil { - return fmt.Errorf("could not find endpoint with id %s", eid) - } - // generate a name for the iface that will be renamed to eth0 in the sbox - containerIfName, err := netutils.GenerateIfaceName(ns.NlHandle(), vethPrefix, vethLen) - if err != nil { - return fmt.Errorf("error generating an interface name: %v", err) - } - // create the netlink ipvlan interface - vethName, err := createIPVlan(containerIfName, n.config.Parent, n.config.IpvlanMode) - if err != nil { - return err - } - // bind the generated iface name to the endpoint - endpoint.srcName = vethName - ep := n.endpoint(eid) - if ep == nil { - return fmt.Errorf("could not find endpoint with id %s", eid) - } - if n.config.IpvlanMode == modeL3 { - // disable gateway services to add a default gw using dev eth0 only - jinfo.DisableGatewayService() - defaultRoute, err := ifaceGateway(defaultV4RouteCidr) - if err != nil { - return err - } - if err := jinfo.AddStaticRoute(defaultRoute.Destination, defaultRoute.RouteType, defaultRoute.NextHop); err != nil { - return fmt.Errorf("failed to set an ipvlan l3 mode ipv4 default gateway: %v", err) - } - logrus.Debugf("Ipvlan Endpoint Joined with IPv4_Addr: %s, Ipvlan_Mode: %s, Parent: %s", - ep.addr.IP.String(), n.config.IpvlanMode, n.config.Parent) - // If the endpoint has a v6 address, set a v6 default route - if ep.addrv6 != nil { - default6Route, err := ifaceGateway(defaultV6RouteCidr) - if err != nil { - return err - } - if err = jinfo.AddStaticRoute(default6Route.Destination, default6Route.RouteType, default6Route.NextHop); err != nil { - return fmt.Errorf("failed to set an ipvlan l3 mode ipv6 default gateway: %v", err) - } - logrus.Debugf("Ipvlan Endpoint Joined with IPv6_Addr: %s, Ipvlan_Mode: %s, Parent: %s", - ep.addrv6.IP.String(), n.config.IpvlanMode, n.config.Parent) - } - } - if n.config.IpvlanMode == modeL2 { - // parse and correlate the endpoint v4 address with the available v4 subnets - if len(n.config.Ipv4Subnets) > 0 { - s := n.getSubnetforIPv4(ep.addr) - if s == nil { - return fmt.Errorf("could not find a valid ipv4 subnet for endpoint %s", eid) - } - v4gw, _, err := net.ParseCIDR(s.GwIP) - if err != nil { - return fmt.Errorf("gateway %s is not a valid ipv4 address: %v", s.GwIP, err) - } - err = jinfo.SetGateway(v4gw) - if err != nil { - return err - } - logrus.Debugf("Ipvlan Endpoint Joined with IPv4_Addr: %s, Gateway: %s, Ipvlan_Mode: %s, Parent: %s", - ep.addr.IP.String(), v4gw.String(), n.config.IpvlanMode, n.config.Parent) - } - // parse and correlate the endpoint v6 address with the available v6 subnets - if len(n.config.Ipv6Subnets) > 0 { - s := n.getSubnetforIPv6(ep.addrv6) - if s == nil { - return fmt.Errorf("could not find a valid ipv6 subnet for endpoint %s", eid) - } - v6gw, _, err := net.ParseCIDR(s.GwIP) - if err != nil { - return fmt.Errorf("gateway %s is not a valid ipv6 address: %v", s.GwIP, err) - } - err = jinfo.SetGatewayIPv6(v6gw) - if err != nil { - return err - } - logrus.Debugf("Ipvlan Endpoint Joined with IPv6_Addr: %s, Gateway: %s, Ipvlan_Mode: %s, Parent: %s", - ep.addrv6.IP.String(), v6gw.String(), n.config.IpvlanMode, n.config.Parent) - } - } - iNames := jinfo.InterfaceName() - err = iNames.SetNames(vethName, containerVethPrefix) - if err != nil { - return err - } - if err = d.storeUpdate(ep); err != nil { - return fmt.Errorf("failed to save ipvlan endpoint %.7s to store: %v", ep.id, err) - } - - return nil -} - -// Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid string) error { - defer osl.InitOSContext()() - network, err := d.getNetwork(nid) - if err != nil { - return err - } - endpoint, err := network.getEndpoint(eid) - if err != nil { - return err - } - if endpoint == nil { - return fmt.Errorf("could not find endpoint with id %s", eid) - } - - return nil -} - -// ifaceGateway returns a static route for either v4/v6 to be set to the container eth0 -func ifaceGateway(dfNet string) (*staticRoute, error) { - nh, dst, err := net.ParseCIDR(dfNet) - if err != nil { - return nil, fmt.Errorf("unable to parse default route %v", err) - } - defaultRoute := &staticRoute{ - Destination: dst, - RouteType: types.CONNECTED, - NextHop: nh, - } - - return defaultRoute, nil -} - -// getSubnetforIPv4 returns the ipv4 subnet to which the given IP belongs -func (n *network) getSubnetforIPv4(ip *net.IPNet) *ipv4Subnet { - for _, s := range n.config.Ipv4Subnets { - _, snet, err := net.ParseCIDR(s.SubnetIP) - if err != nil { - return nil - } - // first check if the mask lengths are the same - i, _ := snet.Mask.Size() - j, _ := ip.Mask.Size() - if i != j { - continue - } - if snet.Contains(ip.IP) { - return s - } - } - - return nil -} - -// getSubnetforIPv6 returns the ipv6 subnet to which the given IP belongs -func (n *network) getSubnetforIPv6(ip *net.IPNet) *ipv6Subnet { - for _, s := range n.config.Ipv6Subnets { - _, snet, err := net.ParseCIDR(s.SubnetIP) - if err != nil { - return nil - } - // first check if the mask lengths are the same - i, _ := snet.Mask.Size() - j, _ := ip.Mask.Size() - if i != j { - continue - } - if snet.Contains(ip.IP) { - return s - } - } - - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_joinleave.go b/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_joinleave.go deleted file mode 100644 index 72d5c24ddc4e5..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan_joinleave.go +++ /dev/null @@ -1,144 +0,0 @@ -package macvlan - -import ( - "fmt" - "net" - - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/osl" - "github.com/sirupsen/logrus" -) - -// Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { - defer osl.InitOSContext()() - n, err := d.getNetwork(nid) - if err != nil { - return err - } - endpoint := n.endpoint(eid) - if endpoint == nil { - return fmt.Errorf("could not find endpoint with id %s", eid) - } - // generate a name for the iface that will be renamed to eth0 in the sbox - containerIfName, err := netutils.GenerateIfaceName(ns.NlHandle(), vethPrefix, vethLen) - if err != nil { - return fmt.Errorf("error generating an interface name: %s", err) - } - // create the netlink macvlan interface - vethName, err := createMacVlan(containerIfName, n.config.Parent, n.config.MacvlanMode) - if err != nil { - return err - } - // bind the generated iface name to the endpoint - endpoint.srcName = vethName - ep := n.endpoint(eid) - if ep == nil { - return fmt.Errorf("could not find endpoint with id %s", eid) - } - // parse and match the endpoint address with the available v4 subnets - if len(n.config.Ipv4Subnets) > 0 { - s := n.getSubnetforIPv4(ep.addr) - if s == nil { - return fmt.Errorf("could not find a valid ipv4 subnet for endpoint %s", eid) - } - v4gw, _, err := net.ParseCIDR(s.GwIP) - if err != nil { - return fmt.Errorf("gateway %s is not a valid ipv4 address: %v", s.GwIP, err) - } - err = jinfo.SetGateway(v4gw) - if err != nil { - return err - } - logrus.Debugf("Macvlan Endpoint Joined with IPv4_Addr: %s, Gateway: %s, MacVlan_Mode: %s, Parent: %s", - ep.addr.IP.String(), v4gw.String(), n.config.MacvlanMode, n.config.Parent) - } - // parse and match the endpoint address with the available v6 subnets - if len(n.config.Ipv6Subnets) > 0 { - s := n.getSubnetforIPv6(ep.addrv6) - if s == nil { - return fmt.Errorf("could not find a valid ipv6 subnet for endpoint %s", eid) - } - v6gw, _, err := net.ParseCIDR(s.GwIP) - if err != nil { - return fmt.Errorf("gateway %s is not a valid ipv6 address: %v", s.GwIP, err) - } - err = jinfo.SetGatewayIPv6(v6gw) - if err != nil { - return err - } - logrus.Debugf("Macvlan Endpoint Joined with IPv6_Addr: %s Gateway: %s MacVlan_Mode: %s, Parent: %s", - ep.addrv6.IP.String(), v6gw.String(), n.config.MacvlanMode, n.config.Parent) - } - iNames := jinfo.InterfaceName() - err = iNames.SetNames(vethName, containerVethPrefix) - if err != nil { - return err - } - if err := d.storeUpdate(ep); err != nil { - return fmt.Errorf("failed to save macvlan endpoint %.7s to store: %v", ep.id, err) - } - return nil -} - -// Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid string) error { - defer osl.InitOSContext()() - network, err := d.getNetwork(nid) - if err != nil { - return err - } - endpoint, err := network.getEndpoint(eid) - if err != nil { - return err - } - if endpoint == nil { - return fmt.Errorf("could not find endpoint with id %s", eid) - } - - return nil -} - -// getSubnetforIP returns the ipv4 subnet to which the given IP belongs -func (n *network) getSubnetforIPv4(ip *net.IPNet) *ipv4Subnet { - for _, s := range n.config.Ipv4Subnets { - _, snet, err := net.ParseCIDR(s.SubnetIP) - if err != nil { - return nil - } - // first check if the mask lengths are the same - i, _ := snet.Mask.Size() - j, _ := ip.Mask.Size() - if i != j { - continue - } - if snet.Contains(ip.IP) { - return s - } - } - - return nil -} - -// getSubnetforIPv6 returns the ipv6 subnet to which the given IP belongs -func (n *network) getSubnetforIPv6(ip *net.IPNet) *ipv6Subnet { - for _, s := range n.config.Ipv6Subnets { - _, snet, err := net.ParseCIDR(s.SubnetIP) - if err != nil { - return nil - } - // first check if the mask lengths are the same - i, _ := snet.Mask.Size() - j, _ := ip.Mask.Size() - if i != j { - continue - } - if snet.Contains(ip.IP) { - return s - } - } - - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/null/null.go b/vendor/github.com/docker/libnetwork/drivers/null/null.go deleted file mode 100644 index 7f2a5e32f7d82..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/null/null.go +++ /dev/null @@ -1,105 +0,0 @@ -package null - -import ( - "sync" - - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/types" -) - -const networkType = "null" - -type driver struct { - network string - sync.Mutex -} - -// Init registers a new instance of null driver -func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { - c := driverapi.Capability{ - DataScope: datastore.LocalScope, - } - return dc.RegisterDriver(networkType, &driver{}, c) -} - -func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { - return nil, types.NotImplementedErrorf("not implemented") -} - -func (d *driver) NetworkFree(id string) error { - return types.NotImplementedErrorf("not implemented") -} - -func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { -} - -func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { - return "", nil -} - -func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { - d.Lock() - defer d.Unlock() - - if d.network != "" { - return types.ForbiddenErrorf("only one instance of \"%s\" network is allowed", networkType) - } - - d.network = id - - return nil -} - -func (d *driver) DeleteNetwork(nid string) error { - return types.ForbiddenErrorf("network of type \"%s\" cannot be deleted", networkType) -} - -func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { - return nil -} - -func (d *driver) DeleteEndpoint(nid, eid string) error { - return nil -} - -func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { - return make(map[string]interface{}, 0), nil -} - -// Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { - return nil -} - -// Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid string) error { - return nil -} - -func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { - return nil -} - -func (d *driver) RevokeExternalConnectivity(nid, eid string) error { - return nil -} - -func (d *driver) Type() string { - return networkType -} - -func (d *driver) IsBuiltIn() bool { - return true -} - -// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster -func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { - return nil -} - -// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster -func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/filter.go b/vendor/github.com/docker/libnetwork/drivers/overlay/filter.go deleted file mode 100644 index 1601803aa0c40..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/filter.go +++ /dev/null @@ -1,142 +0,0 @@ -package overlay - -import ( - "fmt" - "sync" - - "github.com/docker/libnetwork/iptables" - "github.com/sirupsen/logrus" -) - -const globalChain = "DOCKER-OVERLAY" - -var filterOnce sync.Once - -var filterChan = make(chan struct{}, 1) - -func filterWait() func() { - filterChan <- struct{}{} - return func() { <-filterChan } -} - -func chainExists(cname string) bool { - if _, err := iptables.Raw("-L", cname); err != nil { - return false - } - - return true -} - -func setupGlobalChain() { - // Because of an ungraceful shutdown, chain could already be present - if !chainExists(globalChain) { - if err := iptables.RawCombinedOutput("-N", globalChain); err != nil { - logrus.Errorf("could not create global overlay chain: %v", err) - return - } - } - - if !iptables.Exists(iptables.Filter, globalChain, "-j", "RETURN") { - if err := iptables.RawCombinedOutput("-A", globalChain, "-j", "RETURN"); err != nil { - logrus.Errorf("could not install default return chain in the overlay global chain: %v", err) - } - } -} - -func setNetworkChain(cname string, remove bool) error { - // Initialize the onetime global overlay chain - filterOnce.Do(setupGlobalChain) - - exists := chainExists(cname) - - opt := "-N" - // In case of remove, make sure to flush the rules in the chain - if remove && exists { - if err := iptables.RawCombinedOutput("-F", cname); err != nil { - return fmt.Errorf("failed to flush overlay network chain %s rules: %v", cname, err) - } - opt = "-X" - } - - if (!remove && !exists) || (remove && exists) { - if err := iptables.RawCombinedOutput(opt, cname); err != nil { - return fmt.Errorf("failed network chain operation %q for chain %s: %v", opt, cname, err) - } - } - - if !remove { - if !iptables.Exists(iptables.Filter, cname, "-j", "DROP") { - if err := iptables.RawCombinedOutput("-A", cname, "-j", "DROP"); err != nil { - return fmt.Errorf("failed adding default drop rule to overlay network chain %s: %v", cname, err) - } - } - } - - return nil -} - -func addNetworkChain(cname string) error { - defer filterWait()() - - return setNetworkChain(cname, false) -} - -func removeNetworkChain(cname string) error { - defer filterWait()() - - return setNetworkChain(cname, true) -} - -func setFilters(cname, brName string, remove bool) error { - opt := "-I" - if remove { - opt = "-D" - } - - // Every time we set filters for a new subnet make sure to move the global overlay hook to the top of the both the OUTPUT and forward chains - if !remove { - for _, chain := range []string{"OUTPUT", "FORWARD"} { - exists := iptables.Exists(iptables.Filter, chain, "-j", globalChain) - if exists { - if err := iptables.RawCombinedOutput("-D", chain, "-j", globalChain); err != nil { - return fmt.Errorf("failed to delete overlay hook in chain %s while moving the hook: %v", chain, err) - } - } - - if err := iptables.RawCombinedOutput("-I", chain, "-j", globalChain); err != nil { - return fmt.Errorf("failed to insert overlay hook in chain %s: %v", chain, err) - } - } - } - - // Insert/Delete the rule to jump to per-bridge chain - exists := iptables.Exists(iptables.Filter, globalChain, "-o", brName, "-j", cname) - if (!remove && !exists) || (remove && exists) { - if err := iptables.RawCombinedOutput(opt, globalChain, "-o", brName, "-j", cname); err != nil { - return fmt.Errorf("failed to add per-bridge filter rule for bridge %s, network chain %s: %v", brName, cname, err) - } - } - - exists = iptables.Exists(iptables.Filter, cname, "-i", brName, "-j", "ACCEPT") - if (!remove && exists) || (remove && !exists) { - return nil - } - - if err := iptables.RawCombinedOutput(opt, cname, "-i", brName, "-j", "ACCEPT"); err != nil { - return fmt.Errorf("failed to add overlay filter rile for network chain %s, bridge %s: %v", cname, brName, err) - } - - return nil -} - -func addFilters(cname, brName string) error { - defer filterWait()() - - return setFilters(cname, brName, false) -} - -func removeFilters(cname, brName string) error { - defer filterWait()() - - return setFilters(cname, brName, true) -} diff --git a/vendor/github.com/docker/libnetwork/drivers/overlay/ostweaks_linux.go b/vendor/github.com/docker/libnetwork/drivers/overlay/ostweaks_linux.go deleted file mode 100644 index de946a58463cf..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/overlay/ostweaks_linux.go +++ /dev/null @@ -1,23 +0,0 @@ -package overlay - -import ( - "strconv" - - "github.com/docker/libnetwork/osl/kernel" -) - -var ovConfig = map[string]*kernel.OSValue{ - "net.ipv4.neigh.default.gc_thresh1": {"8192", checkHigher}, - "net.ipv4.neigh.default.gc_thresh2": {"49152", checkHigher}, - "net.ipv4.neigh.default.gc_thresh3": {"65536", checkHigher}, -} - -func checkHigher(val1, val2 string) bool { - val1Int, _ := strconv.ParseInt(val1, 10, 32) - val2Int, _ := strconv.ParseInt(val2, 10, 32) - return val1Int < val2Int -} - -func applyOStweaks() { - kernel.ApplyOSTweaks(ovConfig) -} diff --git a/vendor/github.com/docker/libnetwork/drivers/remote/api/api.go b/vendor/github.com/docker/libnetwork/drivers/remote/api/api.go deleted file mode 100644 index fb35da59288bc..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/remote/api/api.go +++ /dev/null @@ -1,221 +0,0 @@ -/* -Package api represents all requests and responses suitable for conversation -with a remote driver. -*/ -package api - -import ( - "net" - - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" -) - -// Response is the basic response structure used in all responses. -type Response struct { - Err string -} - -// GetError returns the error from the response, if any. -func (r *Response) GetError() string { - return r.Err -} - -// GetCapabilityResponse is the response of GetCapability request -type GetCapabilityResponse struct { - Response - Scope string - ConnectivityScope string -} - -// AllocateNetworkRequest requests allocation of new network by manager -type AllocateNetworkRequest struct { - // A network ID that remote plugins are expected to store for future - // reference. - NetworkID string - - // A free form map->object interface for communication of options. - Options map[string]string - - // IPAMData contains the address pool information for this network - IPv4Data, IPv6Data []driverapi.IPAMData -} - -// AllocateNetworkResponse is the response to the AllocateNetworkRequest. -type AllocateNetworkResponse struct { - Response - // A free form plugin specific string->string object to be sent in - // CreateNetworkRequest call in the libnetwork agents - Options map[string]string -} - -// FreeNetworkRequest is the request to free allocated network in the manager -type FreeNetworkRequest struct { - // The ID of the network to be freed. - NetworkID string -} - -// FreeNetworkResponse is the response to a request for freeing a network. -type FreeNetworkResponse struct { - Response -} - -// CreateNetworkRequest requests a new network. -type CreateNetworkRequest struct { - // A network ID that remote plugins are expected to store for future - // reference. - NetworkID string - - // A free form map->object interface for communication of options. - Options map[string]interface{} - - // IPAMData contains the address pool information for this network - IPv4Data, IPv6Data []driverapi.IPAMData -} - -// CreateNetworkResponse is the response to the CreateNetworkRequest. -type CreateNetworkResponse struct { - Response -} - -// DeleteNetworkRequest is the request to delete an existing network. -type DeleteNetworkRequest struct { - // The ID of the network to delete. - NetworkID string -} - -// DeleteNetworkResponse is the response to a request for deleting a network. -type DeleteNetworkResponse struct { - Response -} - -// CreateEndpointRequest is the request to create an endpoint within a network. -type CreateEndpointRequest struct { - // Provided at create time, this will be the network id referenced. - NetworkID string - // The ID of the endpoint for later reference. - EndpointID string - Interface *EndpointInterface - Options map[string]interface{} -} - -// EndpointInterface represents an interface endpoint. -type EndpointInterface struct { - Address string - AddressIPv6 string - MacAddress string -} - -// CreateEndpointResponse is the response to the CreateEndpoint action. -type CreateEndpointResponse struct { - Response - Interface *EndpointInterface -} - -// Interface is the representation of a linux interface. -type Interface struct { - Address *net.IPNet - AddressIPv6 *net.IPNet - MacAddress net.HardwareAddr -} - -// DeleteEndpointRequest describes the API for deleting an endpoint. -type DeleteEndpointRequest struct { - NetworkID string - EndpointID string -} - -// DeleteEndpointResponse is the response to the DeleteEndpoint action. -type DeleteEndpointResponse struct { - Response -} - -// EndpointInfoRequest retrieves information about the endpoint from the network driver. -type EndpointInfoRequest struct { - NetworkID string - EndpointID string -} - -// EndpointInfoResponse is the response to an EndpointInfoRequest. -type EndpointInfoResponse struct { - Response - Value map[string]interface{} -} - -// JoinRequest describes the API for joining an endpoint to a sandbox. -type JoinRequest struct { - NetworkID string - EndpointID string - SandboxKey string - Options map[string]interface{} -} - -// InterfaceName is the struct representation of a pair of devices with source -// and destination, for the purposes of putting an endpoint into a container. -type InterfaceName struct { - SrcName string - DstName string - DstPrefix string -} - -// StaticRoute is the plain JSON representation of a static route. -type StaticRoute struct { - Destination string - RouteType int - NextHop string -} - -// JoinResponse is the response to a JoinRequest. -type JoinResponse struct { - Response - InterfaceName *InterfaceName - Gateway string - GatewayIPv6 string - StaticRoutes []StaticRoute - DisableGatewayService bool -} - -// LeaveRequest describes the API for detaching an endpoint from a sandbox. -type LeaveRequest struct { - NetworkID string - EndpointID string -} - -// LeaveResponse is the answer to LeaveRequest. -type LeaveResponse struct { - Response -} - -// ProgramExternalConnectivityRequest describes the API for programming the external connectivity for the given endpoint. -type ProgramExternalConnectivityRequest struct { - NetworkID string - EndpointID string - Options map[string]interface{} -} - -// ProgramExternalConnectivityResponse is the answer to ProgramExternalConnectivityRequest. -type ProgramExternalConnectivityResponse struct { - Response -} - -// RevokeExternalConnectivityRequest describes the API for revoking the external connectivity for the given endpoint. -type RevokeExternalConnectivityRequest struct { - NetworkID string - EndpointID string -} - -// RevokeExternalConnectivityResponse is the answer to RevokeExternalConnectivityRequest. -type RevokeExternalConnectivityResponse struct { - Response -} - -// DiscoveryNotification represents a discovery notification -type DiscoveryNotification struct { - DiscoveryType discoverapi.DiscoveryType - DiscoveryData interface{} -} - -// DiscoveryResponse is used by libnetwork to log any plugin error processing the discovery notifications -type DiscoveryResponse struct { - Response -} diff --git a/vendor/github.com/docker/libnetwork/drivers/remote/driver.go b/vendor/github.com/docker/libnetwork/drivers/remote/driver.go deleted file mode 100644 index 9786d9e74689c..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/remote/driver.go +++ /dev/null @@ -1,436 +0,0 @@ -package remote - -import ( - "fmt" - "net" - - "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/docker/pkg/plugins" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/drivers/remote/api" - "github.com/docker/libnetwork/types" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -type driver struct { - endpoint *plugins.Client - networkType string -} - -type maybeError interface { - GetError() string -} - -func newDriver(name string, client *plugins.Client) driverapi.Driver { - return &driver{networkType: name, endpoint: client} -} - -// Init makes sure a remote driver is registered when a network driver -// plugin is activated. -func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { - newPluginHandler := func(name string, client *plugins.Client) { - // negotiate driver capability with client - d := newDriver(name, client) - c, err := d.(*driver).getCapabilities() - if err != nil { - logrus.Errorf("error getting capability for %s due to %v", name, err) - return - } - if err = dc.RegisterDriver(name, d, *c); err != nil { - logrus.Errorf("error registering driver for %s due to %v", name, err) - } - } - - // Unit test code is unaware of a true PluginStore. So we fall back to v1 plugins. - handleFunc := plugins.Handle - if pg := dc.GetPluginGetter(); pg != nil { - handleFunc = pg.Handle - activePlugins := pg.GetAllManagedPluginsByCap(driverapi.NetworkPluginEndpointType) - for _, ap := range activePlugins { - client, err := getPluginClient(ap) - if err != nil { - return err - } - newPluginHandler(ap.Name(), client) - } - } - handleFunc(driverapi.NetworkPluginEndpointType, newPluginHandler) - - return nil -} - -func getPluginClient(p plugingetter.CompatPlugin) (*plugins.Client, error) { - if v1, ok := p.(plugingetter.PluginWithV1Client); ok { - return v1.Client(), nil - } - - pa, ok := p.(plugingetter.PluginAddr) - if !ok { - return nil, errors.Errorf("unknown plugin type %T", p) - } - - if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 { - return nil, errors.Errorf("unsupported plugin protocol %s", pa.Protocol()) - } - - addr := pa.Addr() - client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout()) - if err != nil { - return nil, errors.Wrap(err, "error creating plugin client") - } - return client, nil -} - -// Get capability from client -func (d *driver) getCapabilities() (*driverapi.Capability, error) { - var capResp api.GetCapabilityResponse - if err := d.call("GetCapabilities", nil, &capResp); err != nil { - return nil, err - } - - c := &driverapi.Capability{} - switch capResp.Scope { - case "global": - c.DataScope = datastore.GlobalScope - case "local": - c.DataScope = datastore.LocalScope - default: - return nil, fmt.Errorf("invalid capability: expecting 'local' or 'global', got %s", capResp.Scope) - } - - switch capResp.ConnectivityScope { - case "global": - c.ConnectivityScope = datastore.GlobalScope - case "local": - c.ConnectivityScope = datastore.LocalScope - case "": - c.ConnectivityScope = c.DataScope - default: - return nil, fmt.Errorf("invalid capability: expecting 'local' or 'global', got %s", capResp.Scope) - } - - return c, nil -} - -// Config is not implemented for remote drivers, since it is assumed -// to be supplied to the remote process out-of-band (e.g., as command -// line arguments). -func (d *driver) Config(option map[string]interface{}) error { - return &driverapi.ErrNotImplemented{} -} - -func (d *driver) call(methodName string, arg interface{}, retVal maybeError) error { - method := driverapi.NetworkPluginEndpointType + "." + methodName - err := d.endpoint.Call(method, arg, retVal) - if err != nil { - return err - } - if e := retVal.GetError(); e != "" { - return fmt.Errorf("remote: %s", e) - } - return nil -} - -func (d *driver) NetworkAllocate(id string, options map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { - create := &api.AllocateNetworkRequest{ - NetworkID: id, - Options: options, - IPv4Data: ipV4Data, - IPv6Data: ipV6Data, - } - retVal := api.AllocateNetworkResponse{} - err := d.call("AllocateNetwork", create, &retVal) - return retVal.Options, err -} - -func (d *driver) NetworkFree(id string) error { - fr := &api.FreeNetworkRequest{NetworkID: id} - return d.call("FreeNetwork", fr, &api.FreeNetworkResponse{}) -} - -func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { -} - -func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { - return "", nil -} - -func (d *driver) CreateNetwork(id string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { - create := &api.CreateNetworkRequest{ - NetworkID: id, - Options: options, - IPv4Data: ipV4Data, - IPv6Data: ipV6Data, - } - return d.call("CreateNetwork", create, &api.CreateNetworkResponse{}) -} - -func (d *driver) DeleteNetwork(nid string) error { - delete := &api.DeleteNetworkRequest{NetworkID: nid} - return d.call("DeleteNetwork", delete, &api.DeleteNetworkResponse{}) -} - -func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { - if ifInfo == nil { - return errors.New("must not be called with nil InterfaceInfo") - } - - reqIface := &api.EndpointInterface{} - if ifInfo.Address() != nil { - reqIface.Address = ifInfo.Address().String() - } - if ifInfo.AddressIPv6() != nil { - reqIface.AddressIPv6 = ifInfo.AddressIPv6().String() - } - if ifInfo.MacAddress() != nil { - reqIface.MacAddress = ifInfo.MacAddress().String() - } - - create := &api.CreateEndpointRequest{ - NetworkID: nid, - EndpointID: eid, - Interface: reqIface, - Options: epOptions, - } - var res api.CreateEndpointResponse - if err := d.call("CreateEndpoint", create, &res); err != nil { - return err - } - - inIface, err := parseInterface(res) - if err != nil { - return err - } - if inIface == nil { - // Remote driver did not set any field - return nil - } - - if inIface.MacAddress != nil { - if err := ifInfo.SetMacAddress(inIface.MacAddress); err != nil { - return errorWithRollback(fmt.Sprintf("driver modified interface MAC address: %v", err), d.DeleteEndpoint(nid, eid)) - } - } - if inIface.Address != nil { - if err := ifInfo.SetIPAddress(inIface.Address); err != nil { - return errorWithRollback(fmt.Sprintf("driver modified interface address: %v", err), d.DeleteEndpoint(nid, eid)) - } - } - if inIface.AddressIPv6 != nil { - if err := ifInfo.SetIPAddress(inIface.AddressIPv6); err != nil { - return errorWithRollback(fmt.Sprintf("driver modified interface address: %v", err), d.DeleteEndpoint(nid, eid)) - } - } - - return nil -} - -func errorWithRollback(msg string, err error) error { - rollback := "rolled back" - if err != nil { - rollback = "failed to roll back: " + err.Error() - } - return fmt.Errorf("%s; %s", msg, rollback) -} - -func (d *driver) DeleteEndpoint(nid, eid string) error { - delete := &api.DeleteEndpointRequest{ - NetworkID: nid, - EndpointID: eid, - } - return d.call("DeleteEndpoint", delete, &api.DeleteEndpointResponse{}) -} - -func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { - info := &api.EndpointInfoRequest{ - NetworkID: nid, - EndpointID: eid, - } - var res api.EndpointInfoResponse - if err := d.call("EndpointOperInfo", info, &res); err != nil { - return nil, err - } - return res.Value, nil -} - -// Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { - join := &api.JoinRequest{ - NetworkID: nid, - EndpointID: eid, - SandboxKey: sboxKey, - Options: options, - } - var ( - res api.JoinResponse - err error - ) - if err = d.call("Join", join, &res); err != nil { - return err - } - - ifaceName := res.InterfaceName - if iface := jinfo.InterfaceName(); iface != nil && ifaceName != nil { - if err := iface.SetNames(ifaceName.SrcName, ifaceName.DstPrefix); err != nil { - return errorWithRollback(fmt.Sprintf("failed to set interface name: %s", err), d.Leave(nid, eid)) - } - } - - var addr net.IP - if res.Gateway != "" { - if addr = net.ParseIP(res.Gateway); addr == nil { - return fmt.Errorf(`unable to parse Gateway "%s"`, res.Gateway) - } - if jinfo.SetGateway(addr) != nil { - return errorWithRollback(fmt.Sprintf("failed to set gateway: %v", addr), d.Leave(nid, eid)) - } - } - if res.GatewayIPv6 != "" { - if addr = net.ParseIP(res.GatewayIPv6); addr == nil { - return fmt.Errorf(`unable to parse GatewayIPv6 "%s"`, res.GatewayIPv6) - } - if jinfo.SetGatewayIPv6(addr) != nil { - return errorWithRollback(fmt.Sprintf("failed to set gateway IPv6: %v", addr), d.Leave(nid, eid)) - } - } - if len(res.StaticRoutes) > 0 { - routes, err := parseStaticRoutes(res) - if err != nil { - return err - } - for _, route := range routes { - if jinfo.AddStaticRoute(route.Destination, route.RouteType, route.NextHop) != nil { - return errorWithRollback(fmt.Sprintf("failed to set static route: %v", route), d.Leave(nid, eid)) - } - } - } - if res.DisableGatewayService { - jinfo.DisableGatewayService() - } - return nil -} - -// Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid string) error { - leave := &api.LeaveRequest{ - NetworkID: nid, - EndpointID: eid, - } - return d.call("Leave", leave, &api.LeaveResponse{}) -} - -// ProgramExternalConnectivity is invoked to program the rules to allow external connectivity for the endpoint. -func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { - data := &api.ProgramExternalConnectivityRequest{ - NetworkID: nid, - EndpointID: eid, - Options: options, - } - err := d.call("ProgramExternalConnectivity", data, &api.ProgramExternalConnectivityResponse{}) - if err != nil && plugins.IsNotFound(err) { - // It is not mandatory yet to support this method - return nil - } - return err -} - -// RevokeExternalConnectivity method is invoked to remove any external connectivity programming related to the endpoint. -func (d *driver) RevokeExternalConnectivity(nid, eid string) error { - data := &api.RevokeExternalConnectivityRequest{ - NetworkID: nid, - EndpointID: eid, - } - err := d.call("RevokeExternalConnectivity", data, &api.RevokeExternalConnectivityResponse{}) - if err != nil && plugins.IsNotFound(err) { - // It is not mandatory yet to support this method - return nil - } - return err -} - -func (d *driver) Type() string { - return d.networkType -} - -func (d *driver) IsBuiltIn() bool { - return false -} - -// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster -func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { - if dType != discoverapi.NodeDiscovery { - return nil - } - notif := &api.DiscoveryNotification{ - DiscoveryType: dType, - DiscoveryData: data, - } - return d.call("DiscoverNew", notif, &api.DiscoveryResponse{}) -} - -// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster -func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { - if dType != discoverapi.NodeDiscovery { - return nil - } - notif := &api.DiscoveryNotification{ - DiscoveryType: dType, - DiscoveryData: data, - } - return d.call("DiscoverDelete", notif, &api.DiscoveryResponse{}) -} - -func parseStaticRoutes(r api.JoinResponse) ([]*types.StaticRoute, error) { - var routes = make([]*types.StaticRoute, len(r.StaticRoutes)) - for i, inRoute := range r.StaticRoutes { - var err error - outRoute := &types.StaticRoute{RouteType: inRoute.RouteType} - - if inRoute.Destination != "" { - if outRoute.Destination, err = types.ParseCIDR(inRoute.Destination); err != nil { - return nil, err - } - } - - if inRoute.NextHop != "" { - outRoute.NextHop = net.ParseIP(inRoute.NextHop) - if outRoute.NextHop == nil { - return nil, fmt.Errorf("failed to parse nexthop IP %s", inRoute.NextHop) - } - } - - routes[i] = outRoute - } - return routes, nil -} - -// parseInterfaces validates all the parameters of an Interface and returns them. -func parseInterface(r api.CreateEndpointResponse) (*api.Interface, error) { - var outIf *api.Interface - - inIf := r.Interface - if inIf != nil { - var err error - outIf = &api.Interface{} - if inIf.Address != "" { - if outIf.Address, err = types.ParseCIDR(inIf.Address); err != nil { - return nil, err - } - } - if inIf.AddressIPv6 != "" { - if outIf.AddressIPv6, err = types.ParseCIDR(inIf.AddressIPv6); err != nil { - return nil, err - } - } - if inIf.MacAddress != "" { - if outIf.MacAddress, err = net.ParseMAC(inIf.MacAddress); err != nil { - return nil, err - } - } - } - - return outIf, nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers/windows/windows.go b/vendor/github.com/docker/libnetwork/drivers/windows/windows.go deleted file mode 100644 index c1cc61aa352e2..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers/windows/windows.go +++ /dev/null @@ -1,867 +0,0 @@ -// +build windows - -// Shim for the Host Network Service (HNS) to manage networking for -// Windows Server containers and Hyper-V containers. This module -// is a basic libnetwork driver that passes all the calls to HNS -// It implements the 4 networking modes supported by HNS L2Bridge, -// L2Tunnel, NAT and Transparent(DHCP) -// -// The network are stored in memory and docker daemon ensures discovering -// and loading these networks on startup - -package windows - -import ( - "encoding/json" - "fmt" - "net" - "strconv" - "strings" - "sync" - - "github.com/Microsoft/hcsshim" - "github.com/docker/docker/pkg/system" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" - "github.com/sirupsen/logrus" -) - -// networkConfiguration for network specific configuration -type networkConfiguration struct { - ID string - Type string - Name string - HnsID string - RDID string - VLAN uint - VSID uint - DNSServers string - MacPools []hcsshim.MacPool - DNSSuffix string - SourceMac string - NetworkAdapterName string - dbIndex uint64 - dbExists bool - DisableGatewayDNS bool - EnableOutboundNat bool - OutboundNatExceptions []string -} - -// endpointConfiguration represents the user specified configuration for the sandbox endpoint -type endpointOption struct { - MacAddress net.HardwareAddr - QosPolicies []types.QosPolicy - DNSServers []string - DisableDNS bool - DisableICC bool -} - -// EndpointConnectivity stores the port bindings and exposed ports that the user has specified in epOptions. -type EndpointConnectivity struct { - PortBindings []types.PortBinding - ExposedPorts []types.TransportPort -} - -type hnsEndpoint struct { - id string - nid string - profileID string - Type string - //Note: Currently, the sandboxID is the same as the containerID since windows does - //not expose the sandboxID. - //In the future, windows will support a proper sandboxID that is different - //than the containerID. - //Therefore, we are using sandboxID now, so that we won't have to change this code - //when windows properly supports a sandboxID. - sandboxID string - macAddress net.HardwareAddr - epOption *endpointOption // User specified parameters - epConnectivity *EndpointConnectivity // User specified parameters - portMapping []types.PortBinding // Operation port bindings - addr *net.IPNet - gateway net.IP - dbIndex uint64 - dbExists bool -} - -type hnsNetwork struct { - id string - created bool - config *networkConfiguration - endpoints map[string]*hnsEndpoint // key: endpoint id - driver *driver // The network's driver - sync.Mutex -} - -type driver struct { - name string - networks map[string]*hnsNetwork - store datastore.DataStore - sync.Mutex -} - -const ( - errNotFound = "HNS failed with error : The object identifier does not represent a valid object. " -) - -// IsBuiltinLocalDriver validates if network-type is a builtin local-scoped driver -func IsBuiltinLocalDriver(networkType string) bool { - if "l2bridge" == networkType || "l2tunnel" == networkType || "nat" == networkType || "ics" == networkType || "transparent" == networkType { - return true - } - - return false -} - -// New constructs a new bridge driver -func newDriver(networkType string) *driver { - return &driver{name: networkType, networks: map[string]*hnsNetwork{}} -} - -// GetInit returns an initializer for the given network type -func GetInit(networkType string) func(dc driverapi.DriverCallback, config map[string]interface{}) error { - return func(dc driverapi.DriverCallback, config map[string]interface{}) error { - if !IsBuiltinLocalDriver(networkType) { - return types.BadRequestErrorf("Network type not supported: %s", networkType) - } - - d := newDriver(networkType) - - err := d.initStore(config) - if err != nil { - return err - } - - return dc.RegisterDriver(networkType, d, driverapi.Capability{ - DataScope: datastore.LocalScope, - ConnectivityScope: datastore.LocalScope, - }) - } -} - -func (d *driver) getNetwork(id string) (*hnsNetwork, error) { - d.Lock() - defer d.Unlock() - - if nw, ok := d.networks[id]; ok { - return nw, nil - } - - return nil, types.NotFoundErrorf("network not found: %s", id) -} - -func (n *hnsNetwork) getEndpoint(eid string) (*hnsEndpoint, error) { - n.Lock() - defer n.Unlock() - - if ep, ok := n.endpoints[eid]; ok { - return ep, nil - } - - return nil, types.NotFoundErrorf("Endpoint not found: %s", eid) -} - -func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string) (*networkConfiguration, error) { - config := &networkConfiguration{Type: d.name} - - for label, value := range genericOptions { - switch label { - case NetworkName: - config.Name = value - case HNSID: - config.HnsID = value - case RoutingDomain: - config.RDID = value - case Interface: - config.NetworkAdapterName = value - case DNSSuffix: - config.DNSSuffix = value - case DNSServers: - config.DNSServers = value - case DisableGatewayDNS: - b, err := strconv.ParseBool(value) - if err != nil { - return nil, err - } - config.DisableGatewayDNS = b - case MacPool: - config.MacPools = make([]hcsshim.MacPool, 0) - s := strings.Split(value, ",") - if len(s)%2 != 0 { - return nil, types.BadRequestErrorf("Invalid mac pool. You must specify both a start range and an end range") - } - for i := 0; i < len(s)-1; i += 2 { - config.MacPools = append(config.MacPools, hcsshim.MacPool{ - StartMacAddress: s[i], - EndMacAddress: s[i+1], - }) - } - case VLAN: - vlan, err := strconv.ParseUint(value, 10, 32) - if err != nil { - return nil, err - } - config.VLAN = uint(vlan) - case VSID: - vsid, err := strconv.ParseUint(value, 10, 32) - if err != nil { - return nil, err - } - config.VSID = uint(vsid) - case EnableOutboundNat: - if system.GetOSVersion().Build <= 16236 { - return nil, fmt.Errorf("Invalid network option. OutboundNat is not supported on this OS version") - } - b, err := strconv.ParseBool(value) - if err != nil { - return nil, err - } - config.EnableOutboundNat = b - case OutboundNatExceptions: - s := strings.Split(value, ",") - config.OutboundNatExceptions = s - } - } - - config.ID = id - config.Type = d.name - return config, nil -} - -func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error { - if len(ipamV6Data) > 0 { - return types.ForbiddenErrorf("windowsshim driver doesn't support v6 subnets") - } - - if len(ipamV4Data) == 0 { - return types.BadRequestErrorf("network %s requires ipv4 configuration", id) - } - - return nil -} - -func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) { -} - -func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) { - return "", nil -} - -func (d *driver) createNetwork(config *networkConfiguration) error { - network := &hnsNetwork{ - id: config.ID, - endpoints: make(map[string]*hnsEndpoint), - config: config, - driver: d, - } - - d.Lock() - d.networks[config.ID] = network - d.Unlock() - - return nil -} - -// Create a new network -func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error { - if _, err := d.getNetwork(id); err == nil { - return types.ForbiddenErrorf("network %s exists", id) - } - - genData, ok := option[netlabel.GenericData].(map[string]string) - if !ok { - return fmt.Errorf("Unknown generic data option") - } - - // Parse and validate the config. It should not conflict with existing networks' config - config, err := d.parseNetworkOptions(id, genData) - if err != nil { - return err - } - - err = config.processIPAM(id, ipV4Data, ipV6Data) - if err != nil { - return err - } - - err = d.createNetwork(config) - - if err != nil { - return err - } - - // A non blank hnsid indicates that the network was discovered - // from HNS. No need to call HNS if this network was discovered - // from HNS - if config.HnsID == "" { - subnets := []hcsshim.Subnet{} - - for _, ipData := range ipV4Data { - subnet := hcsshim.Subnet{ - AddressPrefix: ipData.Pool.String(), - } - - if ipData.Gateway != nil { - subnet.GatewayAddress = ipData.Gateway.IP.String() - } - - subnets = append(subnets, subnet) - } - - network := &hcsshim.HNSNetwork{ - Name: config.Name, - Type: d.name, - Subnets: subnets, - DNSServerList: config.DNSServers, - DNSSuffix: config.DNSSuffix, - MacPools: config.MacPools, - SourceMac: config.SourceMac, - NetworkAdapterName: config.NetworkAdapterName, - } - - if config.VLAN != 0 { - vlanPolicy, err := json.Marshal(hcsshim.VlanPolicy{ - Type: "VLAN", - VLAN: config.VLAN, - }) - - if err != nil { - return err - } - network.Policies = append(network.Policies, vlanPolicy) - } - - if config.VSID != 0 { - vsidPolicy, err := json.Marshal(hcsshim.VsidPolicy{ - Type: "VSID", - VSID: config.VSID, - }) - - if err != nil { - return err - } - network.Policies = append(network.Policies, vsidPolicy) - } - - if network.Name == "" { - network.Name = id - } - - configurationb, err := json.Marshal(network) - if err != nil { - return err - } - - configuration := string(configurationb) - logrus.Debugf("HNSNetwork Request =%v Address Space=%v", configuration, subnets) - - hnsresponse, err := hcsshim.HNSNetworkRequest("POST", "", configuration) - if err != nil { - return err - } - - config.HnsID = hnsresponse.Id - genData[HNSID] = config.HnsID - - } else { - // Delete any stale HNS endpoints for this network. - if endpoints, err := hcsshim.HNSListEndpointRequest(); err == nil { - for _, ep := range endpoints { - if ep.VirtualNetwork == config.HnsID { - logrus.Infof("Removing stale HNS endpoint %s", ep.Id) - _, err = hcsshim.HNSEndpointRequest("DELETE", ep.Id, "") - if err != nil { - logrus.Warnf("Error removing HNS endpoint %s", ep.Id) - } - } - } - } else { - logrus.Warnf("Error listing HNS endpoints for network %s", config.HnsID) - } - } - - n, err := d.getNetwork(id) - if err != nil { - return err - } - n.created = true - return d.storeUpdate(config) -} - -func (d *driver) DeleteNetwork(nid string) error { - n, err := d.getNetwork(nid) - if err != nil { - return types.InternalMaskableErrorf("%s", err) - } - - n.Lock() - config := n.config - n.Unlock() - - if n.created { - _, err = hcsshim.HNSNetworkRequest("DELETE", config.HnsID, "") - if err != nil && err.Error() != errNotFound { - return types.ForbiddenErrorf(err.Error()) - } - } - - d.Lock() - delete(d.networks, nid) - d.Unlock() - - // delele endpoints belong to this network - for _, ep := range n.endpoints { - if err := d.storeDelete(ep); err != nil { - logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err) - } - } - - return d.storeDelete(config) -} - -func convertQosPolicies(qosPolicies []types.QosPolicy) ([]json.RawMessage, error) { - var qps []json.RawMessage - - // Enumerate through the qos policies specified by the user and convert - // them into the internal structure matching the JSON blob that can be - // understood by the HCS. - for _, elem := range qosPolicies { - encodedPolicy, err := json.Marshal(hcsshim.QosPolicy{ - Type: "QOS", - MaximumOutgoingBandwidthInBytes: elem.MaxEgressBandwidth, - }) - - if err != nil { - return nil, err - } - qps = append(qps, encodedPolicy) - } - return qps, nil -} - -// ConvertPortBindings converts PortBindings to JSON for HNS request -func ConvertPortBindings(portBindings []types.PortBinding) ([]json.RawMessage, error) { - var pbs []json.RawMessage - - // Enumerate through the port bindings specified by the user and convert - // them into the internal structure matching the JSON blob that can be - // understood by the HCS. - for _, elem := range portBindings { - proto := strings.ToUpper(elem.Proto.String()) - if proto != "TCP" && proto != "UDP" { - return nil, fmt.Errorf("invalid protocol %s", elem.Proto.String()) - } - - if elem.HostPort != elem.HostPortEnd { - return nil, fmt.Errorf("Windows does not support more than one host port in NAT settings") - } - - if len(elem.HostIP) != 0 { - return nil, fmt.Errorf("Windows does not support host IP addresses in NAT settings") - } - - encodedPolicy, err := json.Marshal(hcsshim.NatPolicy{ - Type: "NAT", - ExternalPort: elem.HostPort, - InternalPort: elem.Port, - Protocol: elem.Proto.String(), - }) - - if err != nil { - return nil, err - } - pbs = append(pbs, encodedPolicy) - } - return pbs, nil -} - -// ParsePortBindingPolicies parses HNS endpoint response message to PortBindings -func ParsePortBindingPolicies(policies []json.RawMessage) ([]types.PortBinding, error) { - var bindings []types.PortBinding - hcsPolicy := &hcsshim.NatPolicy{} - - for _, elem := range policies { - - if err := json.Unmarshal([]byte(elem), &hcsPolicy); err != nil || hcsPolicy.Type != "NAT" { - continue - } - - binding := types.PortBinding{ - HostPort: hcsPolicy.ExternalPort, - HostPortEnd: hcsPolicy.ExternalPort, - Port: hcsPolicy.InternalPort, - Proto: types.ParseProtocol(hcsPolicy.Protocol), - HostIP: net.IPv4(0, 0, 0, 0), - } - - bindings = append(bindings, binding) - } - - return bindings, nil -} - -func parseEndpointOptions(epOptions map[string]interface{}) (*endpointOption, error) { - if epOptions == nil { - return nil, nil - } - - ec := &endpointOption{} - - if opt, ok := epOptions[netlabel.MacAddress]; ok { - if mac, ok := opt.(net.HardwareAddr); ok { - ec.MacAddress = mac - } else { - return nil, fmt.Errorf("Invalid endpoint configuration") - } - } - - if opt, ok := epOptions[QosPolicies]; ok { - if policies, ok := opt.([]types.QosPolicy); ok { - ec.QosPolicies = policies - } else { - return nil, fmt.Errorf("Invalid endpoint configuration") - } - } - - if opt, ok := epOptions[netlabel.DNSServers]; ok { - if dns, ok := opt.([]string); ok { - ec.DNSServers = dns - } else { - return nil, fmt.Errorf("Invalid endpoint configuration") - } - } - - if opt, ok := epOptions[DisableICC]; ok { - if disableICC, ok := opt.(bool); ok { - ec.DisableICC = disableICC - } else { - return nil, fmt.Errorf("Invalid endpoint configuration") - } - } - - if opt, ok := epOptions[DisableDNS]; ok { - if disableDNS, ok := opt.(bool); ok { - ec.DisableDNS = disableDNS - } else { - return nil, fmt.Errorf("Invalid endpoint configuration") - } - } - - return ec, nil -} - -// ParseEndpointConnectivity parses options passed to CreateEndpoint, specifically port bindings, and store in a endpointConnectivity object. -func ParseEndpointConnectivity(epOptions map[string]interface{}) (*EndpointConnectivity, error) { - if epOptions == nil { - return nil, nil - } - - ec := &EndpointConnectivity{} - - if opt, ok := epOptions[netlabel.PortMap]; ok { - if bs, ok := opt.([]types.PortBinding); ok { - ec.PortBindings = bs - } else { - return nil, fmt.Errorf("Invalid endpoint configuration") - } - } - - if opt, ok := epOptions[netlabel.ExposedPorts]; ok { - if ports, ok := opt.([]types.TransportPort); ok { - ec.ExposedPorts = ports - } else { - return nil, fmt.Errorf("Invalid endpoint configuration") - } - } - return ec, nil -} - -func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { - n, err := d.getNetwork(nid) - if err != nil { - return err - } - - // Check if endpoint id is good and retrieve corresponding endpoint - ep, err := n.getEndpoint(eid) - if err == nil && ep != nil { - return driverapi.ErrEndpointExists(eid) - } - - endpointStruct := &hcsshim.HNSEndpoint{ - VirtualNetwork: n.config.HnsID, - } - - epOption, err := parseEndpointOptions(epOptions) - if err != nil { - return err - } - epConnectivity, err := ParseEndpointConnectivity(epOptions) - if err != nil { - return err - } - - macAddress := ifInfo.MacAddress() - // Use the macaddress if it was provided - if macAddress != nil { - endpointStruct.MacAddress = strings.Replace(macAddress.String(), ":", "-", -1) - } - - endpointStruct.Policies, err = ConvertPortBindings(epConnectivity.PortBindings) - if err != nil { - return err - } - - qosPolicies, err := convertQosPolicies(epOption.QosPolicies) - if err != nil { - return err - } - endpointStruct.Policies = append(endpointStruct.Policies, qosPolicies...) - - if ifInfo.Address() != nil { - endpointStruct.IPAddress = ifInfo.Address().IP - } - - endpointStruct.DNSServerList = strings.Join(epOption.DNSServers, ",") - - // overwrite the ep DisableDNS option if DisableGatewayDNS was set to true during the network creation option - if n.config.DisableGatewayDNS { - logrus.Debugf("n.config.DisableGatewayDNS[%v] overwrites epOption.DisableDNS[%v]", n.config.DisableGatewayDNS, epOption.DisableDNS) - epOption.DisableDNS = n.config.DisableGatewayDNS - } - - if n.driver.name == "nat" && !epOption.DisableDNS { - logrus.Debugf("endpointStruct.EnableInternalDNS =[%v]", endpointStruct.EnableInternalDNS) - endpointStruct.EnableInternalDNS = true - } - - endpointStruct.DisableICC = epOption.DisableICC - - // Inherit OutboundNat policy from the network - if n.config.EnableOutboundNat { - outboundNatPolicy, err := json.Marshal(hcsshim.OutboundNatPolicy{ - Policy: hcsshim.Policy{Type: hcsshim.OutboundNat}, - Exceptions: n.config.OutboundNatExceptions, - }) - - if err != nil { - return err - } - endpointStruct.Policies = append(endpointStruct.Policies, outboundNatPolicy) - } - - configurationb, err := json.Marshal(endpointStruct) - if err != nil { - return err - } - - hnsresponse, err := hcsshim.HNSEndpointRequest("POST", "", string(configurationb)) - if err != nil { - return err - } - - mac, err := net.ParseMAC(hnsresponse.MacAddress) - if err != nil { - return err - } - - // TODO For now the ip mask is not in the info generated by HNS - endpoint := &hnsEndpoint{ - id: eid, - nid: n.id, - Type: d.name, - addr: &net.IPNet{IP: hnsresponse.IPAddress, Mask: hnsresponse.IPAddress.DefaultMask()}, - macAddress: mac, - } - - if hnsresponse.GatewayAddress != "" { - endpoint.gateway = net.ParseIP(hnsresponse.GatewayAddress) - } - - endpoint.profileID = hnsresponse.Id - endpoint.epConnectivity = epConnectivity - endpoint.epOption = epOption - endpoint.portMapping, err = ParsePortBindingPolicies(hnsresponse.Policies) - - if err != nil { - hcsshim.HNSEndpointRequest("DELETE", hnsresponse.Id, "") - return err - } - - n.Lock() - n.endpoints[eid] = endpoint - n.Unlock() - - if ifInfo.Address() == nil { - ifInfo.SetIPAddress(endpoint.addr) - } - - if macAddress == nil { - ifInfo.SetMacAddress(endpoint.macAddress) - } - - if err = d.storeUpdate(endpoint); err != nil { - logrus.Errorf("Failed to save endpoint %.7s to store: %v", endpoint.id, err) - } - - return nil -} - -func (d *driver) DeleteEndpoint(nid, eid string) error { - n, err := d.getNetwork(nid) - if err != nil { - return types.InternalMaskableErrorf("%s", err) - } - - ep, err := n.getEndpoint(eid) - if err != nil { - return err - } - - n.Lock() - delete(n.endpoints, eid) - n.Unlock() - - _, err = hcsshim.HNSEndpointRequest("DELETE", ep.profileID, "") - if err != nil && err.Error() != errNotFound { - return err - } - - if err := d.storeDelete(ep); err != nil { - logrus.Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err) - } - return nil -} - -func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { - network, err := d.getNetwork(nid) - if err != nil { - return nil, err - } - - ep, err := network.getEndpoint(eid) - if err != nil { - return nil, err - } - - data := make(map[string]interface{}, 1) - if network.driver.name == "nat" { - data["AllowUnqualifiedDNSQuery"] = true - } - - data["hnsid"] = ep.profileID - if ep.epConnectivity.ExposedPorts != nil { - // Return a copy of the config data - epc := make([]types.TransportPort, 0, len(ep.epConnectivity.ExposedPorts)) - for _, tp := range ep.epConnectivity.ExposedPorts { - epc = append(epc, tp.GetCopy()) - } - data[netlabel.ExposedPorts] = epc - } - - if ep.portMapping != nil { - // Return a copy of the operational data - pmc := make([]types.PortBinding, 0, len(ep.portMapping)) - for _, pm := range ep.portMapping { - pmc = append(pmc, pm.GetCopy()) - } - data[netlabel.PortMap] = pmc - } - - if len(ep.macAddress) != 0 { - data[netlabel.MacAddress] = ep.macAddress - } - return data, nil -} - -// Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { - network, err := d.getNetwork(nid) - if err != nil { - return err - } - - // Ensure that the endpoint exists - endpoint, err := network.getEndpoint(eid) - if err != nil { - return err - } - - err = jinfo.SetGateway(endpoint.gateway) - if err != nil { - return err - } - - endpoint.sandboxID = sboxKey - - err = hcsshim.HotAttachEndpoint(endpoint.sandboxID, endpoint.profileID) - if err != nil { - // If container doesn't exists in hcs, do not throw error for hot add/remove - if err != hcsshim.ErrComputeSystemDoesNotExist { - return err - } - } - - jinfo.DisableGatewayService() - return nil -} - -// Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid string) error { - network, err := d.getNetwork(nid) - if err != nil { - return types.InternalMaskableErrorf("%s", err) - } - - // Ensure that the endpoint exists - endpoint, err := network.getEndpoint(eid) - if err != nil { - return err - } - - err = hcsshim.HotDetachEndpoint(endpoint.sandboxID, endpoint.profileID) - if err != nil { - // If container doesn't exists in hcs, do not throw error for hot add/remove - if err != hcsshim.ErrComputeSystemDoesNotExist { - return err - } - } - return nil -} - -func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { - return nil -} - -func (d *driver) RevokeExternalConnectivity(nid, eid string) error { - return nil -} - -func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) { - return nil, types.NotImplementedErrorf("not implemented") -} - -func (d *driver) NetworkFree(id string) error { - return types.NotImplementedErrorf("not implemented") -} - -func (d *driver) Type() string { - return d.name -} - -func (d *driver) IsBuiltIn() bool { - return true -} - -// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster -func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { - return nil -} - -// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster -func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers_experimental_linux.go b/vendor/github.com/docker/libnetwork/drivers_experimental_linux.go deleted file mode 100644 index 4f540d4a87ee7..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers_experimental_linux.go +++ /dev/null @@ -1,9 +0,0 @@ -package libnetwork - -import "github.com/docker/libnetwork/drivers/ipvlan" - -func additionalDrivers() []initializer { - return []initializer{ - {ipvlan.Init, "ipvlan"}, - } -} diff --git a/vendor/github.com/docker/libnetwork/drivers_freebsd.go b/vendor/github.com/docker/libnetwork/drivers_freebsd.go deleted file mode 100644 index d117c25780c23..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers_freebsd.go +++ /dev/null @@ -1,13 +0,0 @@ -package libnetwork - -import ( - "github.com/docker/libnetwork/drivers/null" - "github.com/docker/libnetwork/drivers/remote" -) - -func getInitializers(experimental bool) []initializer { - return []initializer{ - {null.Init, "null"}, - {remote.Init, "remote"}, - } -} diff --git a/vendor/github.com/docker/libnetwork/drivers_ipam.go b/vendor/github.com/docker/libnetwork/drivers_ipam.go deleted file mode 100644 index f47c01c71480c..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers_ipam.go +++ /dev/null @@ -1,25 +0,0 @@ -package libnetwork - -import ( - "github.com/docker/libnetwork/drvregistry" - "github.com/docker/libnetwork/ipamapi" - builtinIpam "github.com/docker/libnetwork/ipams/builtin" - nullIpam "github.com/docker/libnetwork/ipams/null" - remoteIpam "github.com/docker/libnetwork/ipams/remote" - "github.com/docker/libnetwork/ipamutils" -) - -func initIPAMDrivers(r *drvregistry.DrvRegistry, lDs, gDs interface{}, addressPool []*ipamutils.NetworkToSplit) error { - builtinIpam.SetDefaultIPAddressPool(addressPool) - for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){ - builtinIpam.Init, - remoteIpam.Init, - nullIpam.Init, - } { - if err := fn(r, lDs, gDs); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/github.com/docker/libnetwork/drivers_linux.go b/vendor/github.com/docker/libnetwork/drivers_linux.go deleted file mode 100644 index c53d516fa6d52..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers_linux.go +++ /dev/null @@ -1,26 +0,0 @@ -package libnetwork - -import ( - "github.com/docker/libnetwork/drivers/bridge" - "github.com/docker/libnetwork/drivers/host" - "github.com/docker/libnetwork/drivers/macvlan" - "github.com/docker/libnetwork/drivers/null" - "github.com/docker/libnetwork/drivers/overlay" - "github.com/docker/libnetwork/drivers/remote" -) - -func getInitializers(experimental bool) []initializer { - in := []initializer{ - {bridge.Init, "bridge"}, - {host.Init, "host"}, - {macvlan.Init, "macvlan"}, - {null.Init, "null"}, - {remote.Init, "remote"}, - {overlay.Init, "overlay"}, - } - - if experimental { - in = append(in, additionalDrivers()...) - } - return in -} diff --git a/vendor/github.com/docker/libnetwork/drivers_windows.go b/vendor/github.com/docker/libnetwork/drivers_windows.go deleted file mode 100644 index a037c16efb438..0000000000000 --- a/vendor/github.com/docker/libnetwork/drivers_windows.go +++ /dev/null @@ -1,21 +0,0 @@ -package libnetwork - -import ( - "github.com/docker/libnetwork/drivers/null" - "github.com/docker/libnetwork/drivers/remote" - "github.com/docker/libnetwork/drivers/windows" - "github.com/docker/libnetwork/drivers/windows/overlay" -) - -func getInitializers(experimental bool) []initializer { - return []initializer{ - {null.Init, "null"}, - {overlay.Init, "overlay"}, - {remote.Init, "remote"}, - {windows.GetInit("transparent"), "transparent"}, - {windows.GetInit("l2bridge"), "l2bridge"}, - {windows.GetInit("l2tunnel"), "l2tunnel"}, - {windows.GetInit("nat"), "nat"}, - {windows.GetInit("ics"), "ics"}, - } -} diff --git a/vendor/github.com/docker/libnetwork/firewall_linux.go b/vendor/github.com/docker/libnetwork/firewall_linux.go deleted file mode 100644 index 54f9621f8131c..0000000000000 --- a/vendor/github.com/docker/libnetwork/firewall_linux.go +++ /dev/null @@ -1,40 +0,0 @@ -package libnetwork - -import ( - "github.com/docker/libnetwork/iptables" - "github.com/sirupsen/logrus" -) - -const userChain = "DOCKER-USER" - -func (c *controller) arrangeUserFilterRule() { - c.Lock() - arrangeUserFilterRule() - c.Unlock() - iptables.OnReloaded(func() { - c.Lock() - arrangeUserFilterRule() - c.Unlock() - }) -} - -// This chain allow users to configure firewall policies in a way that persists -// docker operations/restarts. Docker will not delete or modify any pre-existing -// rules from the DOCKER-USER filter chain. -func arrangeUserFilterRule() { - _, err := iptables.NewChain(userChain, iptables.Filter, false) - if err != nil { - logrus.Warnf("Failed to create %s chain: %v", userChain, err) - return - } - - if err = iptables.AddReturnRule(userChain); err != nil { - logrus.Warnf("Failed to add the RETURN rule for %s: %v", userChain, err) - return - } - - err = iptables.EnsureJumpRule("FORWARD", userChain) - if err != nil { - logrus.Warnf("Failed to ensure the jump rule for %s: %v", userChain, err) - } -} diff --git a/vendor/github.com/docker/libnetwork/firewall_others.go b/vendor/github.com/docker/libnetwork/firewall_others.go deleted file mode 100644 index 901f568fed41c..0000000000000 --- a/vendor/github.com/docker/libnetwork/firewall_others.go +++ /dev/null @@ -1,6 +0,0 @@ -// +build !linux - -package libnetwork - -func (c *controller) arrangeUserFilterRule() { -} diff --git a/vendor/github.com/docker/libnetwork/ipam/store.go b/vendor/github.com/docker/libnetwork/ipam/store.go deleted file mode 100644 index 124d585518b4c..0000000000000 --- a/vendor/github.com/docker/libnetwork/ipam/store.go +++ /dev/null @@ -1,136 +0,0 @@ -package ipam - -import ( - "encoding/json" - - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/types" - "github.com/sirupsen/logrus" -) - -// Key provides the Key to be used in KV Store -func (aSpace *addrSpace) Key() []string { - aSpace.Lock() - defer aSpace.Unlock() - return []string{aSpace.id} -} - -// KeyPrefix returns the immediate parent key that can be used for tree walk -func (aSpace *addrSpace) KeyPrefix() []string { - aSpace.Lock() - defer aSpace.Unlock() - return []string{dsConfigKey} -} - -// Value marshals the data to be stored in the KV store -func (aSpace *addrSpace) Value() []byte { - b, err := json.Marshal(aSpace) - if err != nil { - logrus.Warnf("Failed to marshal ipam configured pools: %v", err) - return nil - } - return b -} - -// SetValue unmarshalls the data from the KV store. -func (aSpace *addrSpace) SetValue(value []byte) error { - rc := &addrSpace{subnets: make(map[SubnetKey]*PoolData)} - if err := json.Unmarshal(value, rc); err != nil { - return err - } - aSpace.subnets = rc.subnets - return nil -} - -// Index returns the latest DB Index as seen by this object -func (aSpace *addrSpace) Index() uint64 { - aSpace.Lock() - defer aSpace.Unlock() - return aSpace.dbIndex -} - -// SetIndex method allows the datastore to store the latest DB Index into this object -func (aSpace *addrSpace) SetIndex(index uint64) { - aSpace.Lock() - aSpace.dbIndex = index - aSpace.dbExists = true - aSpace.Unlock() -} - -// Exists method is true if this object has been stored in the DB. -func (aSpace *addrSpace) Exists() bool { - aSpace.Lock() - defer aSpace.Unlock() - return aSpace.dbExists -} - -// Skip provides a way for a KV Object to avoid persisting it in the KV Store -func (aSpace *addrSpace) Skip() bool { - return false -} - -func (a *Allocator) getStore(as string) datastore.DataStore { - a.Lock() - defer a.Unlock() - - if aSpace, ok := a.addrSpaces[as]; ok { - return aSpace.ds - } - - return nil -} - -func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) { - store := a.getStore(as) - - // IPAM may not have a valid store. In such cases it is just in-memory state. - if store == nil { - return nil, nil - } - - pc := &addrSpace{id: dsConfigKey + "/" + as, ds: store, alloc: a} - if err := store.GetObject(datastore.Key(pc.Key()...), pc); err != nil { - if err == datastore.ErrKeyNotFound { - return nil, nil - } - - return nil, types.InternalErrorf("could not get pools config from store: %v", err) - } - - return pc, nil -} - -func (a *Allocator) writeToStore(aSpace *addrSpace) error { - store := aSpace.store() - - // IPAM may not have a valid store. In such cases it is just in-memory state. - if store == nil { - return nil - } - - err := store.PutObjectAtomic(aSpace) - if err == datastore.ErrKeyModified { - return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err) - } - - return err -} - -func (a *Allocator) deleteFromStore(aSpace *addrSpace) error { - store := aSpace.store() - - // IPAM may not have a valid store. In such cases it is just in-memory state. - if store == nil { - return nil - } - - return store.DeleteObjectAtomic(aSpace) -} - -// DataScope method returns the storage scope of the datastore -func (aSpace *addrSpace) DataScope() string { - aSpace.Lock() - defer aSpace.Unlock() - - return aSpace.scope -} diff --git a/vendor/github.com/docker/libnetwork/ipam/utils.go b/vendor/github.com/docker/libnetwork/ipam/utils.go deleted file mode 100644 index 5117c55cc7ca3..0000000000000 --- a/vendor/github.com/docker/libnetwork/ipam/utils.go +++ /dev/null @@ -1,81 +0,0 @@ -package ipam - -import ( - "fmt" - "net" - - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/types" -) - -type ipVersion int - -const ( - v4 = 4 - v6 = 6 -) - -func getAddressRange(pool string, masterNw *net.IPNet) (*AddressRange, error) { - ip, nw, err := net.ParseCIDR(pool) - if err != nil { - return nil, ipamapi.ErrInvalidSubPool - } - lIP, e := types.GetHostPartIP(nw.IP, masterNw.Mask) - if e != nil { - return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e) - } - bIP, e := types.GetBroadcastIP(nw.IP, nw.Mask) - if e != nil { - return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e) - } - hIP, e := types.GetHostPartIP(bIP, masterNw.Mask) - if e != nil { - return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e) - } - nw.IP = ip - return &AddressRange{nw, ipToUint64(types.GetMinimalIP(lIP)), ipToUint64(types.GetMinimalIP(hIP))}, nil -} - -// It generates the ip address in the passed subnet specified by -// the passed host address ordinal -func generateAddress(ordinal uint64, network *net.IPNet) net.IP { - var address [16]byte - - // Get network portion of IP - if getAddressVersion(network.IP) == v4 { - copy(address[:], network.IP.To4()) - } else { - copy(address[:], network.IP) - } - - end := len(network.Mask) - addIntToIP(address[:end], ordinal) - - return net.IP(address[:end]) -} - -func getAddressVersion(ip net.IP) ipVersion { - if ip.To4() == nil { - return v6 - } - return v4 -} - -// Adds the ordinal IP to the current array -// 192.168.0.0 + 53 => 192.168.0.53 -func addIntToIP(array []byte, ordinal uint64) { - for i := len(array) - 1; i >= 0; i-- { - array[i] |= (byte)(ordinal & 0xff) - ordinal >>= 8 - } -} - -// Convert an ordinal to the respective IP address -func ipToUint64(ip []byte) (value uint64) { - cip := types.GetMinimalIP(ip) - for i := 0; i < len(cip); i++ { - j := len(cip) - 1 - i - value += uint64(cip[i]) << uint(j*8) - } - return value -} diff --git a/vendor/github.com/docker/libnetwork/ipams/null/null.go b/vendor/github.com/docker/libnetwork/ipams/null/null.go deleted file mode 100644 index 339b5308d11b5..0000000000000 --- a/vendor/github.com/docker/libnetwork/ipams/null/null.go +++ /dev/null @@ -1,75 +0,0 @@ -// Package null implements the null ipam driver. Null ipam driver satisfies ipamapi contract, -// but does not effectively reserve/allocate any address pool or address -package null - -import ( - "fmt" - "net" - - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/types" -) - -var ( - defaultAS = "null" - defaultPool, _ = types.ParseCIDR("0.0.0.0/0") - defaultPoolID = fmt.Sprintf("%s/%s", defaultAS, defaultPool.String()) -) - -type allocator struct{} - -func (a *allocator) GetDefaultAddressSpaces() (string, string, error) { - return defaultAS, defaultAS, nil -} - -func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { - if addressSpace != defaultAS { - return "", nil, nil, types.BadRequestErrorf("unknown address space: %s", addressSpace) - } - if pool != "" { - return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle specific address pool requests") - } - if subPool != "" { - return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle specific address subpool requests") - } - if v6 { - return "", nil, nil, types.BadRequestErrorf("null ipam driver does not handle IPv6 address pool pool requests") - } - return defaultPoolID, defaultPool, nil, nil -} - -func (a *allocator) ReleasePool(poolID string) error { - return nil -} - -func (a *allocator) RequestAddress(poolID string, ip net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) { - if poolID != defaultPoolID { - return nil, nil, types.BadRequestErrorf("unknown pool id: %s", poolID) - } - return nil, nil, nil -} - -func (a *allocator) ReleaseAddress(poolID string, ip net.IP) error { - if poolID != defaultPoolID { - return types.BadRequestErrorf("unknown pool id: %s", poolID) - } - return nil -} - -func (a *allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { - return nil -} - -func (a *allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { - return nil -} - -func (a *allocator) IsBuiltIn() bool { - return true -} - -// Init registers a remote ipam when its plugin is activated -func Init(ic ipamapi.Callback, l, g interface{}) error { - return ic.RegisterIpamDriver(ipamapi.NullIPAM, &allocator{}) -} diff --git a/vendor/github.com/docker/libnetwork/ipams/remote/api/api.go b/vendor/github.com/docker/libnetwork/ipams/remote/api/api.go deleted file mode 100644 index 543c99bb008e9..0000000000000 --- a/vendor/github.com/docker/libnetwork/ipams/remote/api/api.go +++ /dev/null @@ -1,94 +0,0 @@ -// Package api defines the data structure to be used in the request/response -// messages between libnetwork and the remote ipam plugin -package api - -import "github.com/docker/libnetwork/ipamapi" - -// Response is the basic response structure used in all responses -type Response struct { - Error string -} - -// IsSuccess returns whether the plugin response is successful -func (r *Response) IsSuccess() bool { - return r.Error == "" -} - -// GetError returns the error from the response, if any. -func (r *Response) GetError() string { - return r.Error -} - -// GetCapabilityResponse is the response of GetCapability request -type GetCapabilityResponse struct { - Response - RequiresMACAddress bool - RequiresRequestReplay bool -} - -// ToCapability converts the capability response into the internal ipam driver capability structure -func (capRes GetCapabilityResponse) ToCapability() *ipamapi.Capability { - return &ipamapi.Capability{ - RequiresMACAddress: capRes.RequiresMACAddress, - RequiresRequestReplay: capRes.RequiresRequestReplay, - } -} - -// GetAddressSpacesResponse is the response to the ``get default address spaces`` request message -type GetAddressSpacesResponse struct { - Response - LocalDefaultAddressSpace string - GlobalDefaultAddressSpace string -} - -// RequestPoolRequest represents the expected data in a ``request address pool`` request message -type RequestPoolRequest struct { - AddressSpace string - Pool string - SubPool string - Options map[string]string - V6 bool -} - -// RequestPoolResponse represents the response message to a ``request address pool`` request -type RequestPoolResponse struct { - Response - PoolID string - Pool string // CIDR format - Data map[string]string -} - -// ReleasePoolRequest represents the expected data in a ``release address pool`` request message -type ReleasePoolRequest struct { - PoolID string -} - -// ReleasePoolResponse represents the response message to a ``release address pool`` request -type ReleasePoolResponse struct { - Response -} - -// RequestAddressRequest represents the expected data in a ``request address`` request message -type RequestAddressRequest struct { - PoolID string - Address string - Options map[string]string -} - -// RequestAddressResponse represents the expected data in the response message to a ``request address`` request -type RequestAddressResponse struct { - Response - Address string // in CIDR format - Data map[string]string -} - -// ReleaseAddressRequest represents the expected data in a ``release address`` request message -type ReleaseAddressRequest struct { - PoolID string - Address string -} - -// ReleaseAddressResponse represents the response message to a ``release address`` request -type ReleaseAddressResponse struct { - Response -} diff --git a/vendor/github.com/docker/libnetwork/ipams/remote/remote.go b/vendor/github.com/docker/libnetwork/ipams/remote/remote.go deleted file mode 100644 index 6f2173f3085a2..0000000000000 --- a/vendor/github.com/docker/libnetwork/ipams/remote/remote.go +++ /dev/null @@ -1,183 +0,0 @@ -package remote - -import ( - "fmt" - "net" - - "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/docker/pkg/plugins" - "github.com/docker/libnetwork/discoverapi" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/ipams/remote/api" - "github.com/docker/libnetwork/types" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -type allocator struct { - endpoint *plugins.Client - name string -} - -// PluginResponse is the interface for the plugin request responses -type PluginResponse interface { - IsSuccess() bool - GetError() string -} - -func newAllocator(name string, client *plugins.Client) ipamapi.Ipam { - a := &allocator{name: name, endpoint: client} - return a -} - -// Init registers a remote ipam when its plugin is activated -func Init(cb ipamapi.Callback, l, g interface{}) error { - - newPluginHandler := func(name string, client *plugins.Client) { - a := newAllocator(name, client) - if cps, err := a.(*allocator).getCapabilities(); err == nil { - if err := cb.RegisterIpamDriverWithCapabilities(name, a, cps); err != nil { - logrus.Errorf("error registering remote ipam driver %s due to %v", name, err) - } - } else { - logrus.Infof("remote ipam driver %s does not support capabilities", name) - logrus.Debug(err) - if err := cb.RegisterIpamDriver(name, a); err != nil { - logrus.Errorf("error registering remote ipam driver %s due to %v", name, err) - } - } - } - - // Unit test code is unaware of a true PluginStore. So we fall back to v1 plugins. - handleFunc := plugins.Handle - if pg := cb.GetPluginGetter(); pg != nil { - handleFunc = pg.Handle - activePlugins := pg.GetAllManagedPluginsByCap(ipamapi.PluginEndpointType) - for _, ap := range activePlugins { - client, err := getPluginClient(ap) - if err != nil { - return err - } - newPluginHandler(ap.Name(), client) - } - } - handleFunc(ipamapi.PluginEndpointType, newPluginHandler) - return nil -} - -func getPluginClient(p plugingetter.CompatPlugin) (*plugins.Client, error) { - if v1, ok := p.(plugingetter.PluginWithV1Client); ok { - return v1.Client(), nil - } - - pa, ok := p.(plugingetter.PluginAddr) - if !ok { - return nil, errors.Errorf("unknown plugin type %T", p) - } - - if pa.Protocol() != plugins.ProtocolSchemeHTTPV1 { - return nil, errors.Errorf("unsupported plugin protocol %s", pa.Protocol()) - } - - addr := pa.Addr() - client, err := plugins.NewClientWithTimeout(addr.Network()+"://"+addr.String(), nil, pa.Timeout()) - if err != nil { - return nil, errors.Wrap(err, "error creating plugin client") - } - return client, nil -} - -func (a *allocator) call(methodName string, arg interface{}, retVal PluginResponse) error { - method := ipamapi.PluginEndpointType + "." + methodName - err := a.endpoint.Call(method, arg, retVal) - if err != nil { - return err - } - if !retVal.IsSuccess() { - return fmt.Errorf("remote: %s", retVal.GetError()) - } - return nil -} - -func (a *allocator) getCapabilities() (*ipamapi.Capability, error) { - var res api.GetCapabilityResponse - if err := a.call("GetCapabilities", nil, &res); err != nil { - return nil, err - } - return res.ToCapability(), nil -} - -// GetDefaultAddressSpaces returns the local and global default address spaces -func (a *allocator) GetDefaultAddressSpaces() (string, string, error) { - res := &api.GetAddressSpacesResponse{} - if err := a.call("GetDefaultAddressSpaces", nil, res); err != nil { - return "", "", err - } - return res.LocalDefaultAddressSpace, res.GlobalDefaultAddressSpace, nil -} - -// RequestPool requests an address pool in the specified address space -func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { - req := &api.RequestPoolRequest{AddressSpace: addressSpace, Pool: pool, SubPool: subPool, Options: options, V6: v6} - res := &api.RequestPoolResponse{} - if err := a.call("RequestPool", req, res); err != nil { - return "", nil, nil, err - } - retPool, err := types.ParseCIDR(res.Pool) - return res.PoolID, retPool, res.Data, err -} - -// ReleasePool removes an address pool from the specified address space -func (a *allocator) ReleasePool(poolID string) error { - req := &api.ReleasePoolRequest{PoolID: poolID} - res := &api.ReleasePoolResponse{} - return a.call("ReleasePool", req, res) -} - -// RequestAddress requests an address from the address pool -func (a *allocator) RequestAddress(poolID string, address net.IP, options map[string]string) (*net.IPNet, map[string]string, error) { - var ( - prefAddress string - retAddress *net.IPNet - err error - ) - if address != nil { - prefAddress = address.String() - } - req := &api.RequestAddressRequest{PoolID: poolID, Address: prefAddress, Options: options} - res := &api.RequestAddressResponse{} - if err := a.call("RequestAddress", req, res); err != nil { - return nil, nil, err - } - if res.Address != "" { - retAddress, err = types.ParseCIDR(res.Address) - } else { - return nil, nil, ipamapi.ErrNoIPReturned - } - return retAddress, res.Data, err -} - -// ReleaseAddress releases the address from the specified address pool -func (a *allocator) ReleaseAddress(poolID string, address net.IP) error { - var relAddress string - if address != nil { - relAddress = address.String() - } - req := &api.ReleaseAddressRequest{PoolID: poolID, Address: relAddress} - res := &api.ReleaseAddressResponse{} - return a.call("ReleaseAddress", req, res) -} - -// DiscoverNew is a notification for a new discovery event, such as a new global datastore -func (a *allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error { - return nil -} - -// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster -func (a *allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error { - return nil -} - -func (a *allocator) IsBuiltIn() bool { - return false -} diff --git a/vendor/github.com/docker/libnetwork/iptables/firewalld.go b/vendor/github.com/docker/libnetwork/iptables/firewalld.go deleted file mode 100644 index 8f13c8644852b..0000000000000 --- a/vendor/github.com/docker/libnetwork/iptables/firewalld.go +++ /dev/null @@ -1,167 +0,0 @@ -package iptables - -import ( - "fmt" - "strings" - - "github.com/godbus/dbus" - "github.com/sirupsen/logrus" -) - -// IPV defines the table string -type IPV string - -const ( - // Iptables point ipv4 table - Iptables IPV = "ipv4" - // IP6Tables point to ipv6 table - IP6Tables IPV = "ipv6" - // Ebtables point to bridge table - Ebtables IPV = "eb" -) -const ( - dbusInterface = "org.fedoraproject.FirewallD1" - dbusPath = "/org/fedoraproject/FirewallD1" -) - -// Conn is a connection to firewalld dbus endpoint. -type Conn struct { - sysconn *dbus.Conn - sysobj dbus.BusObject - signal chan *dbus.Signal -} - -var ( - connection *Conn - firewalldRunning bool // is Firewalld service running - onReloaded []*func() // callbacks when Firewalld has been reloaded -) - -// FirewalldInit initializes firewalld management code. -func FirewalldInit() error { - var err error - - if connection, err = newConnection(); err != nil { - return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err) - } - firewalldRunning = checkRunning() - if !firewalldRunning { - connection.sysconn.Close() - connection = nil - } - if connection != nil { - go signalHandler() - } - - return nil -} - -// New() establishes a connection to the system bus. -func newConnection() (*Conn, error) { - c := new(Conn) - if err := c.initConnection(); err != nil { - return nil, err - } - - return c, nil -} - -// Initialize D-Bus connection. -func (c *Conn) initConnection() error { - var err error - - c.sysconn, err = dbus.SystemBus() - if err != nil { - return err - } - - // This never fails, even if the service is not running atm. - c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath)) - - rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'", - dbusPath, dbusInterface, dbusInterface) - c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) - - rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", - dbusInterface) - c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) - - c.signal = make(chan *dbus.Signal, 10) - c.sysconn.Signal(c.signal) - - return nil -} - -func signalHandler() { - for signal := range connection.signal { - if strings.Contains(signal.Name, "NameOwnerChanged") { - firewalldRunning = checkRunning() - dbusConnectionChanged(signal.Body) - } else if strings.Contains(signal.Name, "Reloaded") { - reloaded() - } - } -} - -func dbusConnectionChanged(args []interface{}) { - name := args[0].(string) - oldOwner := args[1].(string) - newOwner := args[2].(string) - - if name != dbusInterface { - return - } - - if len(newOwner) > 0 { - connectionEstablished() - } else if len(oldOwner) > 0 { - connectionLost() - } -} - -func connectionEstablished() { - reloaded() -} - -func connectionLost() { - // Doesn't do anything for now. Libvirt also doesn't react to this. -} - -// call all callbacks -func reloaded() { - for _, pf := range onReloaded { - (*pf)() - } -} - -// OnReloaded add callback -func OnReloaded(callback func()) { - for _, pf := range onReloaded { - if pf == &callback { - return - } - } - onReloaded = append(onReloaded, &callback) -} - -// Call some remote method to see whether the service is actually running. -func checkRunning() bool { - var zone string - var err error - - if connection != nil { - err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone) - return err == nil - } - return false -} - -// Passthrough method simply passes args through to iptables/ip6tables -func Passthrough(ipv IPV, args ...string) ([]byte, error) { - var output string - logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args) - if err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output); err != nil { - return nil, err - } - return []byte(output), nil -} diff --git a/vendor/github.com/docker/libnetwork/iptables/iptables.go b/vendor/github.com/docker/libnetwork/iptables/iptables.go deleted file mode 100644 index 4b8d8832e9035..0000000000000 --- a/vendor/github.com/docker/libnetwork/iptables/iptables.go +++ /dev/null @@ -1,575 +0,0 @@ -package iptables - -import ( - "errors" - "fmt" - "net" - "os/exec" - "regexp" - "strconv" - "strings" - "sync" - "time" - - "github.com/sirupsen/logrus" -) - -// Action signifies the iptable action. -type Action string - -// Policy is the default iptable policies -type Policy string - -// Table refers to Nat, Filter or Mangle. -type Table string - -const ( - // Append appends the rule at the end of the chain. - Append Action = "-A" - // Delete deletes the rule from the chain. - Delete Action = "-D" - // Insert inserts the rule at the top of the chain. - Insert Action = "-I" - // Nat table is used for nat translation rules. - Nat Table = "nat" - // Filter table is used for filter rules. - Filter Table = "filter" - // Mangle table is used for mangling the packet. - Mangle Table = "mangle" - // Drop is the default iptables DROP policy - Drop Policy = "DROP" - // Accept is the default iptables ACCEPT policy - Accept Policy = "ACCEPT" -) - -var ( - iptablesPath string - supportsXlock = false - supportsCOpt = false - xLockWaitMsg = "Another app is currently holding the xtables lock" - // used to lock iptables commands if xtables lock is not supported - bestEffortLock sync.Mutex - // ErrIptablesNotFound is returned when the rule is not found. - ErrIptablesNotFound = errors.New("Iptables not found") - initOnce sync.Once -) - -// ChainInfo defines the iptables chain. -type ChainInfo struct { - Name string - Table Table - HairpinMode bool -} - -// ChainError is returned to represent errors during ip table operation. -type ChainError struct { - Chain string - Output []byte -} - -func (e ChainError) Error() string { - return fmt.Sprintf("Error iptables %s: %s", e.Chain, string(e.Output)) -} - -func probe() { - if out, err := exec.Command("modprobe", "-va", "nf_nat").CombinedOutput(); err != nil { - logrus.Warnf("Running modprobe nf_nat failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err) - } - if out, err := exec.Command("modprobe", "-va", "xt_conntrack").CombinedOutput(); err != nil { - logrus.Warnf("Running modprobe xt_conntrack failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err) - } -} - -func initFirewalld() { - if err := FirewalldInit(); err != nil { - logrus.Debugf("Fail to initialize firewalld: %v, using raw iptables instead", err) - } -} - -func detectIptables() { - path, err := exec.LookPath("iptables") - if err != nil { - return - } - iptablesPath = path - supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil - mj, mn, mc, err := GetVersion() - if err != nil { - logrus.Warnf("Failed to read iptables version: %v", err) - return - } - supportsCOpt = supportsCOption(mj, mn, mc) -} - -func initDependencies() { - probe() - initFirewalld() - detectIptables() -} - -func initCheck() error { - initOnce.Do(initDependencies) - - if iptablesPath == "" { - return ErrIptablesNotFound - } - return nil -} - -// NewChain adds a new chain to ip table. -func NewChain(name string, table Table, hairpinMode bool) (*ChainInfo, error) { - c := &ChainInfo{ - Name: name, - Table: table, - HairpinMode: hairpinMode, - } - if string(c.Table) == "" { - c.Table = Filter - } - - // Add chain if it doesn't exist - if _, err := Raw("-t", string(c.Table), "-n", "-L", c.Name); err != nil { - if output, err := Raw("-t", string(c.Table), "-N", c.Name); err != nil { - return nil, err - } else if len(output) != 0 { - return nil, fmt.Errorf("Could not create %s/%s chain: %s", c.Table, c.Name, output) - } - } - return c, nil -} - -// ProgramChain is used to add rules to a chain -func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) error { - if c.Name == "" { - return errors.New("Could not program chain, missing chain name") - } - - switch c.Table { - case Nat: - preroute := []string{ - "-m", "addrtype", - "--dst-type", "LOCAL", - "-j", c.Name} - if !Exists(Nat, "PREROUTING", preroute...) && enable { - if err := c.Prerouting(Append, preroute...); err != nil { - return fmt.Errorf("Failed to inject %s in PREROUTING chain: %s", c.Name, err) - } - } else if Exists(Nat, "PREROUTING", preroute...) && !enable { - if err := c.Prerouting(Delete, preroute...); err != nil { - return fmt.Errorf("Failed to remove %s in PREROUTING chain: %s", c.Name, err) - } - } - output := []string{ - "-m", "addrtype", - "--dst-type", "LOCAL", - "-j", c.Name} - if !hairpinMode { - output = append(output, "!", "--dst", "127.0.0.0/8") - } - if !Exists(Nat, "OUTPUT", output...) && enable { - if err := c.Output(Append, output...); err != nil { - return fmt.Errorf("Failed to inject %s in OUTPUT chain: %s", c.Name, err) - } - } else if Exists(Nat, "OUTPUT", output...) && !enable { - if err := c.Output(Delete, output...); err != nil { - return fmt.Errorf("Failed to inject %s in OUTPUT chain: %s", c.Name, err) - } - } - case Filter: - if bridgeName == "" { - return fmt.Errorf("Could not program chain %s/%s, missing bridge name", - c.Table, c.Name) - } - link := []string{ - "-o", bridgeName, - "-j", c.Name} - if !Exists(Filter, "FORWARD", link...) && enable { - insert := append([]string{string(Insert), "FORWARD"}, link...) - if output, err := Raw(insert...); err != nil { - return err - } else if len(output) != 0 { - return fmt.Errorf("Could not create linking rule to %s/%s: %s", c.Table, c.Name, output) - } - } else if Exists(Filter, "FORWARD", link...) && !enable { - del := append([]string{string(Delete), "FORWARD"}, link...) - if output, err := Raw(del...); err != nil { - return err - } else if len(output) != 0 { - return fmt.Errorf("Could not delete linking rule from %s/%s: %s", c.Table, c.Name, output) - } - - } - establish := []string{ - "-o", bridgeName, - "-m", "conntrack", - "--ctstate", "RELATED,ESTABLISHED", - "-j", "ACCEPT"} - if !Exists(Filter, "FORWARD", establish...) && enable { - insert := append([]string{string(Insert), "FORWARD"}, establish...) - if output, err := Raw(insert...); err != nil { - return err - } else if len(output) != 0 { - return fmt.Errorf("Could not create establish rule to %s: %s", c.Table, output) - } - } else if Exists(Filter, "FORWARD", establish...) && !enable { - del := append([]string{string(Delete), "FORWARD"}, establish...) - if output, err := Raw(del...); err != nil { - return err - } else if len(output) != 0 { - return fmt.Errorf("Could not delete establish rule from %s: %s", c.Table, output) - } - } - } - return nil -} - -// RemoveExistingChain removes existing chain from the table. -func RemoveExistingChain(name string, table Table) error { - c := &ChainInfo{ - Name: name, - Table: table, - } - if string(c.Table) == "" { - c.Table = Filter - } - return c.Remove() -} - -// Forward adds forwarding rule to 'filter' table and corresponding nat rule to 'nat' table. -func (c *ChainInfo) Forward(action Action, ip net.IP, port int, proto, destAddr string, destPort int, bridgeName string) error { - daddr := ip.String() - if ip.IsUnspecified() { - // iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we - // want "0.0.0.0/0". "0/0" is correctly interpreted as "any - // value" by both iptables and ip6tables. - daddr = "0/0" - } - - args := []string{ - "-p", proto, - "-d", daddr, - "--dport", strconv.Itoa(port), - "-j", "DNAT", - "--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))} - if !c.HairpinMode { - args = append(args, "!", "-i", bridgeName) - } - if err := ProgramRule(Nat, c.Name, action, args); err != nil { - return err - } - - args = []string{ - "!", "-i", bridgeName, - "-o", bridgeName, - "-p", proto, - "-d", destAddr, - "--dport", strconv.Itoa(destPort), - "-j", "ACCEPT", - } - if err := ProgramRule(Filter, c.Name, action, args); err != nil { - return err - } - - args = []string{ - "-p", proto, - "-s", destAddr, - "-d", destAddr, - "--dport", strconv.Itoa(destPort), - "-j", "MASQUERADE", - } - - if err := ProgramRule(Nat, "POSTROUTING", action, args); err != nil { - return err - } - - if proto == "sctp" { - // Linux kernel v4.9 and below enables NETIF_F_SCTP_CRC for veth by - // the following commit. - // This introduces a problem when conbined with a physical NIC without - // NETIF_F_SCTP_CRC. As for a workaround, here we add an iptables entry - // to fill the checksum. - // - // https://github.com/torvalds/linux/commit/c80fafbbb59ef9924962f83aac85531039395b18 - args = []string{ - "-p", proto, - "--sport", strconv.Itoa(destPort), - "-j", "CHECKSUM", - "--checksum-fill", - } - if err := ProgramRule(Mangle, "POSTROUTING", action, args); err != nil { - return err - } - } - - return nil -} - -// Link adds reciprocal ACCEPT rule for two supplied IP addresses. -// Traffic is allowed from ip1 to ip2 and vice-versa -func (c *ChainInfo) Link(action Action, ip1, ip2 net.IP, port int, proto string, bridgeName string) error { - // forward - args := []string{ - "-i", bridgeName, "-o", bridgeName, - "-p", proto, - "-s", ip1.String(), - "-d", ip2.String(), - "--dport", strconv.Itoa(port), - "-j", "ACCEPT", - } - if err := ProgramRule(Filter, c.Name, action, args); err != nil { - return err - } - // reverse - args[7], args[9] = args[9], args[7] - args[10] = "--sport" - return ProgramRule(Filter, c.Name, action, args) -} - -// ProgramRule adds the rule specified by args only if the -// rule is not already present in the chain. Reciprocally, -// it removes the rule only if present. -func ProgramRule(table Table, chain string, action Action, args []string) error { - if Exists(table, chain, args...) != (action == Delete) { - return nil - } - return RawCombinedOutput(append([]string{"-t", string(table), string(action), chain}, args...)...) -} - -// Prerouting adds linking rule to nat/PREROUTING chain. -func (c *ChainInfo) Prerouting(action Action, args ...string) error { - a := []string{"-t", string(Nat), string(action), "PREROUTING"} - if len(args) > 0 { - a = append(a, args...) - } - if output, err := Raw(a...); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "PREROUTING", Output: output} - } - return nil -} - -// Output adds linking rule to an OUTPUT chain. -func (c *ChainInfo) Output(action Action, args ...string) error { - a := []string{"-t", string(c.Table), string(action), "OUTPUT"} - if len(args) > 0 { - a = append(a, args...) - } - if output, err := Raw(a...); err != nil { - return err - } else if len(output) != 0 { - return ChainError{Chain: "OUTPUT", Output: output} - } - return nil -} - -// Remove removes the chain. -func (c *ChainInfo) Remove() error { - // Ignore errors - This could mean the chains were never set up - if c.Table == Nat { - c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name) - c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", c.Name) - c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name) // Created in versions <= 0.1.6 - - c.Prerouting(Delete) - c.Output(Delete) - } - Raw("-t", string(c.Table), "-F", c.Name) - Raw("-t", string(c.Table), "-X", c.Name) - return nil -} - -// Exists checks if a rule exists -func Exists(table Table, chain string, rule ...string) bool { - return exists(false, table, chain, rule...) -} - -// ExistsNative behaves as Exists with the difference it -// will always invoke `iptables` binary. -func ExistsNative(table Table, chain string, rule ...string) bool { - return exists(true, table, chain, rule...) -} - -func exists(native bool, table Table, chain string, rule ...string) bool { - f := Raw - if native { - f = raw - } - - if string(table) == "" { - table = Filter - } - - if err := initCheck(); err != nil { - // The exists() signature does not allow us to return an error, but at least - // we can skip the (likely invalid) exec invocation. - return false - } - - if supportsCOpt { - // if exit status is 0 then return true, the rule exists - _, err := f(append([]string{"-t", string(table), "-C", chain}, rule...)...) - return err == nil - } - - // parse "iptables -S" for the rule (it checks rules in a specific chain - // in a specific table and it is very unreliable) - return existsRaw(table, chain, rule...) -} - -func existsRaw(table Table, chain string, rule ...string) bool { - ruleString := fmt.Sprintf("%s %s\n", chain, strings.Join(rule, " ")) - existingRules, _ := exec.Command(iptablesPath, "-t", string(table), "-S", chain).Output() - - return strings.Contains(string(existingRules), ruleString) -} - -// Maximum duration that an iptables operation can take -// before flagging a warning. -const opWarnTime = 2 * time.Second - -func filterOutput(start time.Time, output []byte, args ...string) []byte { - // Flag operations that have taken a long time to complete - opTime := time.Since(start) - if opTime > opWarnTime { - logrus.Warnf("xtables contention detected while running [%s]: Waited for %.2f seconds and received %q", strings.Join(args, " "), float64(opTime)/float64(time.Second), string(output)) - } - // ignore iptables' message about xtables lock: - // it is a warning, not an error. - if strings.Contains(string(output), xLockWaitMsg) { - output = []byte("") - } - // Put further filters here if desired - return output -} - -// Raw calls 'iptables' system command, passing supplied arguments. -func Raw(args ...string) ([]byte, error) { - if firewalldRunning { - startTime := time.Now() - output, err := Passthrough(Iptables, args...) - if err == nil || !strings.Contains(err.Error(), "was not provided by any .service files") { - return filterOutput(startTime, output, args...), err - } - } - return raw(args...) -} - -func raw(args ...string) ([]byte, error) { - if err := initCheck(); err != nil { - return nil, err - } - if supportsXlock { - args = append([]string{"--wait"}, args...) - } else { - bestEffortLock.Lock() - defer bestEffortLock.Unlock() - } - - logrus.Debugf("%s, %v", iptablesPath, args) - - startTime := time.Now() - output, err := exec.Command(iptablesPath, args...).CombinedOutput() - if err != nil { - return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err) - } - - return filterOutput(startTime, output, args...), err -} - -// RawCombinedOutput internally calls the Raw function and returns a non nil -// error if Raw returned a non nil error or a non empty output -func RawCombinedOutput(args ...string) error { - if output, err := Raw(args...); err != nil || len(output) != 0 { - return fmt.Errorf("%s (%v)", string(output), err) - } - return nil -} - -// RawCombinedOutputNative behave as RawCombinedOutput with the difference it -// will always invoke `iptables` binary -func RawCombinedOutputNative(args ...string) error { - if output, err := raw(args...); err != nil || len(output) != 0 { - return fmt.Errorf("%s (%v)", string(output), err) - } - return nil -} - -// ExistChain checks if a chain exists -func ExistChain(chain string, table Table) bool { - if _, err := Raw("-t", string(table), "-nL", chain); err == nil { - return true - } - return false -} - -// GetVersion reads the iptables version numbers during initialization -func GetVersion() (major, minor, micro int, err error) { - out, err := exec.Command(iptablesPath, "--version").CombinedOutput() - if err == nil { - major, minor, micro = parseVersionNumbers(string(out)) - } - return -} - -// SetDefaultPolicy sets the passed default policy for the table/chain -func SetDefaultPolicy(table Table, chain string, policy Policy) error { - if err := RawCombinedOutput("-t", string(table), "-P", chain, string(policy)); err != nil { - return fmt.Errorf("setting default policy to %v in %v chain failed: %v", policy, chain, err) - } - return nil -} - -func parseVersionNumbers(input string) (major, minor, micro int) { - re := regexp.MustCompile(`v\d*.\d*.\d*`) - line := re.FindString(input) - fmt.Sscanf(line, "v%d.%d.%d", &major, &minor, µ) - return -} - -// iptables -C, --check option was added in v.1.4.11 -// http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt -func supportsCOption(mj, mn, mc int) bool { - return mj > 1 || (mj == 1 && (mn > 4 || (mn == 4 && mc >= 11))) -} - -// AddReturnRule adds a return rule for the chain in the filter table -func AddReturnRule(chain string) error { - var ( - table = Filter - args = []string{"-j", "RETURN"} - ) - - if Exists(table, chain, args...) { - return nil - } - - err := RawCombinedOutput(append([]string{"-A", chain}, args...)...) - if err != nil { - return fmt.Errorf("unable to add return rule in %s chain: %s", chain, err.Error()) - } - - return nil -} - -// EnsureJumpRule ensures the jump rule is on top -func EnsureJumpRule(fromChain, toChain string) error { - var ( - table = Filter - args = []string{"-j", toChain} - ) - - if Exists(table, fromChain, args...) { - err := RawCombinedOutput(append([]string{"-D", fromChain}, args...)...) - if err != nil { - return fmt.Errorf("unable to remove jump to %s rule in %s chain: %s", toChain, fromChain, err.Error()) - } - } - - err := RawCombinedOutput(append([]string{"-I", fromChain}, args...)...) - if err != nil { - return fmt.Errorf("unable to insert jump to %s rule in %s chain: %s", toChain, fromChain, err.Error()) - } - - return nil -} diff --git a/vendor/github.com/docker/libnetwork/netlabel/labels.go b/vendor/github.com/docker/libnetwork/netlabel/labels.go deleted file mode 100644 index 1594556ec78cc..0000000000000 --- a/vendor/github.com/docker/libnetwork/netlabel/labels.go +++ /dev/null @@ -1,129 +0,0 @@ -package netlabel - -import ( - "strings" -) - -const ( - // Prefix constant marks the reserved label space for libnetwork - Prefix = "com.docker.network" - - // DriverPrefix constant marks the reserved label space for libnetwork drivers - DriverPrefix = Prefix + ".driver" - - // DriverPrivatePrefix constant marks the reserved label space - // for internal libnetwork drivers - DriverPrivatePrefix = DriverPrefix + ".private" - - // GenericData constant that helps to identify an option as a Generic constant - GenericData = Prefix + ".generic" - - // PortMap constant represents Port Mapping - PortMap = Prefix + ".portmap" - - // MacAddress constant represents Mac Address config of a Container - MacAddress = Prefix + ".endpoint.macaddress" - - // ExposedPorts constant represents the container's Exposed Ports - ExposedPorts = Prefix + ".endpoint.exposedports" - - // DNSServers A list of DNS servers associated with the endpoint - DNSServers = Prefix + ".endpoint.dnsservers" - - //EnableIPv6 constant represents enabling IPV6 at network level - EnableIPv6 = Prefix + ".enable_ipv6" - - // DriverMTU constant represents the MTU size for the network driver - DriverMTU = DriverPrefix + ".mtu" - - // OverlayBindInterface constant represents overlay driver bind interface - OverlayBindInterface = DriverPrefix + ".overlay.bind_interface" - - // OverlayNeighborIP constant represents overlay driver neighbor IP - OverlayNeighborIP = DriverPrefix + ".overlay.neighbor_ip" - - // OverlayVxlanIDList constant represents a list of VXLAN Ids as csv - OverlayVxlanIDList = DriverPrefix + ".overlay.vxlanid_list" - - // Gateway represents the gateway for the network - Gateway = Prefix + ".gateway" - - // Internal constant represents that the network is internal which disables default gateway service - Internal = Prefix + ".internal" - - // ContainerIfacePrefix can be used to override the interface prefix used inside the container - ContainerIfacePrefix = Prefix + ".container_iface_prefix" -) - -var ( - // GlobalKVProvider constant represents the KV provider backend - GlobalKVProvider = MakeKVProvider("global") - - // GlobalKVProviderURL constant represents the KV provider URL - GlobalKVProviderURL = MakeKVProviderURL("global") - - // GlobalKVProviderConfig constant represents the KV provider Config - GlobalKVProviderConfig = MakeKVProviderConfig("global") - - // GlobalKVClient constants represents the global kv store client - GlobalKVClient = MakeKVClient("global") - - // LocalKVProvider constant represents the KV provider backend - LocalKVProvider = MakeKVProvider("local") - - // LocalKVProviderURL constant represents the KV provider URL - LocalKVProviderURL = MakeKVProviderURL("local") - - // LocalKVProviderConfig constant represents the KV provider Config - LocalKVProviderConfig = MakeKVProviderConfig("local") - - // LocalKVClient constants represents the local kv store client - LocalKVClient = MakeKVClient("local") -) - -// MakeKVProvider returns the kvprovider label for the scope -func MakeKVProvider(scope string) string { - return DriverPrivatePrefix + scope + "kv_provider" -} - -// MakeKVProviderURL returns the kvprovider url label for the scope -func MakeKVProviderURL(scope string) string { - return DriverPrivatePrefix + scope + "kv_provider_url" -} - -// MakeKVProviderConfig returns the kvprovider config label for the scope -func MakeKVProviderConfig(scope string) string { - return DriverPrivatePrefix + scope + "kv_provider_config" -} - -// MakeKVClient returns the kv client label for the scope -func MakeKVClient(scope string) string { - return DriverPrivatePrefix + scope + "kv_client" -} - -// Key extracts the key portion of the label -func Key(label string) (key string) { - if kv := strings.SplitN(label, "=", 2); len(kv) > 0 { - key = kv[0] - } - return -} - -// Value extracts the value portion of the label -func Value(label string) (value string) { - if kv := strings.SplitN(label, "=", 2); len(kv) > 1 { - value = kv[1] - } - return -} - -// KeyValue decomposes the label in the (key,value) pair -func KeyValue(label string) (key string, value string) { - if kv := strings.SplitN(label, "=", 2); len(kv) > 0 { - key = kv[0] - if len(kv) > 1 { - value = kv[1] - } - } - return -} diff --git a/vendor/github.com/docker/libnetwork/netutils/utils.go b/vendor/github.com/docker/libnetwork/netutils/utils.go deleted file mode 100644 index 7de98f6b07a17..0000000000000 --- a/vendor/github.com/docker/libnetwork/netutils/utils.go +++ /dev/null @@ -1,194 +0,0 @@ -// Network utility functions. - -package netutils - -import ( - "crypto/rand" - "encoding/hex" - "errors" - "fmt" - "io" - "net" - "strings" - - "github.com/docker/libnetwork/types" -) - -var ( - // ErrNetworkOverlapsWithNameservers preformatted error - ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver") - // ErrNetworkOverlaps preformatted error - ErrNetworkOverlaps = errors.New("requested network overlaps with existing network") - // ErrNoDefaultRoute preformatted error - ErrNoDefaultRoute = errors.New("no default route") -) - -// CheckNameserverOverlaps checks whether the passed network overlaps with any of the nameservers -func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error { - if len(nameservers) > 0 { - for _, ns := range nameservers { - _, nsNetwork, err := net.ParseCIDR(ns) - if err != nil { - return err - } - if NetworkOverlaps(toCheck, nsNetwork) { - return ErrNetworkOverlapsWithNameservers - } - } - } - return nil -} - -// NetworkOverlaps detects overlap between one IPNet and another -func NetworkOverlaps(netX *net.IPNet, netY *net.IPNet) bool { - return netX.Contains(netY.IP) || netY.Contains(netX.IP) -} - -// NetworkRange calculates the first and last IP addresses in an IPNet -func NetworkRange(network *net.IPNet) (net.IP, net.IP) { - if network == nil { - return nil, nil - } - - firstIP := network.IP.Mask(network.Mask) - lastIP := types.GetIPCopy(firstIP) - for i := 0; i < len(firstIP); i++ { - lastIP[i] = firstIP[i] | ^network.Mask[i] - } - - if network.IP.To4() != nil { - firstIP = firstIP.To4() - lastIP = lastIP.To4() - } - - return firstIP, lastIP -} - -// GetIfaceAddr returns the first IPv4 address and slice of IPv6 addresses for the specified network interface -func GetIfaceAddr(name string) (net.Addr, []net.Addr, error) { - iface, err := net.InterfaceByName(name) - if err != nil { - return nil, nil, err - } - addrs, err := iface.Addrs() - if err != nil { - return nil, nil, err - } - var addrs4 []net.Addr - var addrs6 []net.Addr - for _, addr := range addrs { - ip := (addr.(*net.IPNet)).IP - if ip4 := ip.To4(); ip4 != nil { - addrs4 = append(addrs4, addr) - } else if ip6 := ip.To16(); len(ip6) == net.IPv6len { - addrs6 = append(addrs6, addr) - } - } - switch { - case len(addrs4) == 0: - return nil, nil, fmt.Errorf("Interface %v has no IPv4 addresses", name) - case len(addrs4) > 1: - fmt.Printf("Interface %v has more than 1 IPv4 address. Defaulting to using %v\n", - name, (addrs4[0].(*net.IPNet)).IP) - } - return addrs4[0], addrs6, nil -} - -func genMAC(ip net.IP) net.HardwareAddr { - hw := make(net.HardwareAddr, 6) - // The first byte of the MAC address has to comply with these rules: - // 1. Unicast: Set the least-significant bit to 0. - // 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1. - hw[0] = 0x02 - // The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI). - // Since this address is locally administered, we can do whatever we want as long as - // it doesn't conflict with other addresses. - hw[1] = 0x42 - // Fill the remaining 4 bytes based on the input - if ip == nil { - rand.Read(hw[2:]) - } else { - copy(hw[2:], ip.To4()) - } - return hw -} - -// GenerateRandomMAC returns a new 6-byte(48-bit) hardware address (MAC) -func GenerateRandomMAC() net.HardwareAddr { - return genMAC(nil) -} - -// GenerateMACFromIP returns a locally administered MAC address where the 4 least -// significant bytes are derived from the IPv4 address. -func GenerateMACFromIP(ip net.IP) net.HardwareAddr { - return genMAC(ip) -} - -// GenerateRandomName returns a new name joined with a prefix. This size -// specified is used to truncate the randomly generated value -func GenerateRandomName(prefix string, size int) (string, error) { - id := make([]byte, 32) - if _, err := io.ReadFull(rand.Reader, id); err != nil { - return "", err - } - return prefix + hex.EncodeToString(id)[:size], nil -} - -// ReverseIP accepts a V4 or V6 IP string in the canonical form and returns a reversed IP in -// the dotted decimal form . This is used to setup the IP to service name mapping in the optimal -// way for the DNS PTR queries. -func ReverseIP(IP string) string { - var reverseIP []string - - if net.ParseIP(IP).To4() != nil { - reverseIP = strings.Split(IP, ".") - l := len(reverseIP) - for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 { - reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i] - } - } else { - reverseIP = strings.Split(IP, ":") - - // Reversed IPv6 is represented in dotted decimal instead of the typical - // colon hex notation - for key := range reverseIP { - if len(reverseIP[key]) == 0 { // expand the compressed 0s - reverseIP[key] = strings.Repeat("0000", 8-strings.Count(IP, ":")) - } else if len(reverseIP[key]) < 4 { // 0-padding needed - reverseIP[key] = strings.Repeat("0", 4-len(reverseIP[key])) + reverseIP[key] - } - } - - reverseIP = strings.Split(strings.Join(reverseIP, ""), "") - - l := len(reverseIP) - for i, j := 0, l-1; i < l/2; i, j = i+1, j-1 { - reverseIP[i], reverseIP[j] = reverseIP[j], reverseIP[i] - } - } - - return strings.Join(reverseIP, ".") -} - -// ParseAlias parses and validates the specified string as an alias format (name:alias) -func ParseAlias(val string) (string, string, error) { - if val == "" { - return "", "", errors.New("empty string specified for alias") - } - arr := strings.Split(val, ":") - if len(arr) > 2 { - return "", "", fmt.Errorf("bad format for alias: %s", val) - } - if len(arr) == 1 { - return val, val, nil - } - return arr[0], arr[1], nil -} - -// ValidateAlias validates that the specified string has a valid alias format (containerName:alias). -func ValidateAlias(val string) (string, error) { - if _, _, err := ParseAlias(val); err != nil { - return val, err - } - return val, nil -} diff --git a/vendor/github.com/docker/libnetwork/netutils/utils_linux.go b/vendor/github.com/docker/libnetwork/netutils/utils_linux.go deleted file mode 100644 index 10a5e109ee04a..0000000000000 --- a/vendor/github.com/docker/libnetwork/netutils/utils_linux.go +++ /dev/null @@ -1,126 +0,0 @@ -// +build linux -// Network utility functions. - -package netutils - -import ( - "fmt" - "net" - "strings" - - "github.com/docker/libnetwork/ipamutils" - "github.com/docker/libnetwork/ns" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/resolvconf" - "github.com/docker/libnetwork/types" - "github.com/vishvananda/netlink" -) - -var ( - networkGetRoutesFct func(netlink.Link, int) ([]netlink.Route, error) -) - -// CheckRouteOverlaps checks whether the passed network overlaps with any existing routes -func CheckRouteOverlaps(toCheck *net.IPNet) error { - if networkGetRoutesFct == nil { - networkGetRoutesFct = ns.NlHandle().RouteList - } - networks, err := networkGetRoutesFct(nil, netlink.FAMILY_V4) - if err != nil { - return err - } - for _, network := range networks { - if network.Dst != nil && NetworkOverlaps(toCheck, network.Dst) { - return ErrNetworkOverlaps - } - } - return nil -} - -// GenerateIfaceName returns an interface name using the passed in -// prefix and the length of random bytes. The api ensures that the -// there are is no interface which exists with that name. -func GenerateIfaceName(nlh *netlink.Handle, prefix string, len int) (string, error) { - linkByName := netlink.LinkByName - if nlh != nil { - linkByName = nlh.LinkByName - } - for i := 0; i < 3; i++ { - name, err := GenerateRandomName(prefix, len) - if err != nil { - continue - } - _, err = linkByName(name) - if err != nil { - if strings.Contains(err.Error(), "not found") { - return name, nil - } - return "", err - } - } - return "", types.InternalErrorf("could not generate interface name") -} - -// ElectInterfaceAddresses looks for an interface on the OS with the -// specified name and returns returns all its IPv4 and IPv6 addresses in CIDR notation. -// If a failure in retrieving the addresses or no IPv4 address is found, an error is returned. -// If the interface does not exist, it chooses from a predefined -// list the first IPv4 address which does not conflict with other -// interfaces on the system. -func ElectInterfaceAddresses(name string) ([]*net.IPNet, []*net.IPNet, error) { - var ( - v4Nets []*net.IPNet - v6Nets []*net.IPNet - ) - - defer osl.InitOSContext()() - - link, _ := ns.NlHandle().LinkByName(name) - if link != nil { - v4addr, err := ns.NlHandle().AddrList(link, netlink.FAMILY_V4) - if err != nil { - return nil, nil, err - } - v6addr, err := ns.NlHandle().AddrList(link, netlink.FAMILY_V6) - if err != nil { - return nil, nil, err - } - for _, nlAddr := range v4addr { - v4Nets = append(v4Nets, nlAddr.IPNet) - } - for _, nlAddr := range v6addr { - v6Nets = append(v6Nets, nlAddr.IPNet) - } - } - - if link == nil || len(v4Nets) == 0 { - // Choose from predefined local scope networks - v4Net, err := FindAvailableNetwork(ipamutils.PredefinedLocalScopeDefaultNetworks) - if err != nil { - return nil, nil, err - } - v4Nets = append(v4Nets, v4Net) - } - - return v4Nets, v6Nets, nil -} - -// FindAvailableNetwork returns a network from the passed list which does not -// overlap with existing interfaces in the system -func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) { - // We don't check for an error here, because we don't really care if we - // can't read /etc/resolv.conf. So instead we skip the append if resolvConf - // is nil. It either doesn't exist, or we can't read it for some reason. - var nameservers []string - if rc, err := resolvconf.Get(); err == nil { - nameservers = resolvconf.GetNameserversAsCIDR(rc.Content) - } - for _, nw := range list { - if err := CheckNameserverOverlaps(nameservers, nw); err == nil { - if err := CheckRouteOverlaps(nw); err == nil { - return nw, nil - } - } - } - return nil, fmt.Errorf("no available network") -} diff --git a/vendor/github.com/docker/libnetwork/netutils/utils_windows.go b/vendor/github.com/docker/libnetwork/netutils/utils_windows.go deleted file mode 100644 index 73af44ec71160..0000000000000 --- a/vendor/github.com/docker/libnetwork/netutils/utils_windows.go +++ /dev/null @@ -1,25 +0,0 @@ -package netutils - -import ( - "net" - - "github.com/docker/libnetwork/types" -) - -// ElectInterfaceAddresses looks for an interface on the OS with the specified name -// and returns returns all its IPv4 and IPv6 addresses in CIDR notation. -// If a failure in retrieving the addresses or no IPv4 address is found, an error is returned. -// If the interface does not exist, it chooses from a predefined -// list the first IPv4 address which does not conflict with other -// interfaces on the system. -func ElectInterfaceAddresses(name string) ([]*net.IPNet, []*net.IPNet, error) { - return nil, nil, types.NotImplementedErrorf("not supported on windows") -} - -// FindAvailableNetwork returns a network from the passed list which does not -// overlap with existing interfaces in the system - -// TODO : Use appropriate windows APIs to identify non-overlapping subnets -func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) { - return nil, nil -} diff --git a/vendor/github.com/docker/libnetwork/network_windows.go b/vendor/github.com/docker/libnetwork/network_windows.go deleted file mode 100644 index e7819e1c3e9a7..0000000000000 --- a/vendor/github.com/docker/libnetwork/network_windows.go +++ /dev/null @@ -1,75 +0,0 @@ -// +build windows - -package libnetwork - -import ( - "runtime" - "time" - - "github.com/Microsoft/hcsshim" - "github.com/docker/libnetwork/drivers/windows" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/ipams/windowsipam" - "github.com/sirupsen/logrus" -) - -func executeInCompartment(compartmentID uint32, x func()) { - runtime.LockOSThread() - - if err := hcsshim.SetCurrentThreadCompartmentId(compartmentID); err != nil { - logrus.Error(err) - } - defer func() { - hcsshim.SetCurrentThreadCompartmentId(0) - runtime.UnlockOSThread() - }() - - x() -} - -func (n *network) startResolver() { - if n.networkType == "ics" { - return - } - n.resolverOnce.Do(func() { - logrus.Debugf("Launching DNS server for network %q", n.Name()) - options := n.Info().DriverOptions() - hnsid := options[windows.HNSID] - - if hnsid == "" { - return - } - - hnsresponse, err := hcsshim.HNSNetworkRequest("GET", hnsid, "") - if err != nil { - logrus.Errorf("Resolver Setup/Start failed for container %s, %q", n.Name(), err) - return - } - - for _, subnet := range hnsresponse.Subnets { - if subnet.GatewayAddress != "" { - for i := 0; i < 3; i++ { - resolver := NewResolver(subnet.GatewayAddress, false, "", n) - logrus.Debugf("Binding a resolver on network %s gateway %s", n.Name(), subnet.GatewayAddress) - executeInCompartment(hnsresponse.DNSServerCompartment, resolver.SetupFunc(53)) - - if err = resolver.Start(); err != nil { - logrus.Errorf("Resolver Setup/Start failed for container %s, %q", n.Name(), err) - time.Sleep(1 * time.Second) - } else { - logrus.Debugf("Resolver bound successfully for network %s", n.Name()) - n.resolver = append(n.resolver, resolver) - break - } - } - } - } - }) -} - -func defaultIpamForNetworkType(networkType string) string { - if windows.IsBuiltinLocalDriver(networkType) { - return windowsipam.DefaultIPAM - } - return ipamapi.DefaultIPAM -} diff --git a/vendor/github.com/docker/libnetwork/osl/sandbox.go b/vendor/github.com/docker/libnetwork/osl/sandbox.go deleted file mode 100644 index 198cf641a1dd3..0000000000000 --- a/vendor/github.com/docker/libnetwork/osl/sandbox.go +++ /dev/null @@ -1,191 +0,0 @@ -// Package osl describes structures and interfaces which abstract os entities -package osl - -import ( - "net" - - "github.com/docker/libnetwork/types" -) - -// SandboxType specify the time of the sandbox, this can be used to apply special configs -type SandboxType int - -const ( - // SandboxTypeIngress indicates that the sandbox is for the ingress - SandboxTypeIngress = iota - // SandboxTypeLoadBalancer indicates that the sandbox is a load balancer - SandboxTypeLoadBalancer = iota -) - -// Sandbox represents a network sandbox, identified by a specific key. It -// holds a list of Interfaces, routes etc, and more can be added dynamically. -type Sandbox interface { - // The path where the network namespace is mounted. - Key() string - - // Add an existing Interface to this sandbox. The operation will rename - // from the Interface SrcName to DstName as it moves, and reconfigure the - // interface according to the specified settings. The caller is expected - // to only provide a prefix for DstName. The AddInterface api will auto-generate - // an appropriate suffix for the DstName to disambiguate. - AddInterface(SrcName string, DstPrefix string, options ...IfaceOption) error - - // Set default IPv4 gateway for the sandbox - SetGateway(gw net.IP) error - - // Set default IPv6 gateway for the sandbox - SetGatewayIPv6(gw net.IP) error - - // Unset the previously set default IPv4 gateway in the sandbox - UnsetGateway() error - - // Unset the previously set default IPv6 gateway in the sandbox - UnsetGatewayIPv6() error - - // GetLoopbackIfaceName returns the name of the loopback interface - GetLoopbackIfaceName() string - - // AddAliasIP adds the passed IP address to the named interface - AddAliasIP(ifName string, ip *net.IPNet) error - - // RemoveAliasIP removes the passed IP address from the named interface - RemoveAliasIP(ifName string, ip *net.IPNet) error - - // DisableARPForVIP disables ARP replies and requests for VIP addresses - // on a particular interface - DisableARPForVIP(ifName string) error - - // Add a static route to the sandbox. - AddStaticRoute(*types.StaticRoute) error - - // Remove a static route from the sandbox. - RemoveStaticRoute(*types.StaticRoute) error - - // AddNeighbor adds a neighbor entry into the sandbox. - AddNeighbor(dstIP net.IP, dstMac net.HardwareAddr, force bool, option ...NeighOption) error - - // DeleteNeighbor deletes neighbor entry from the sandbox. - DeleteNeighbor(dstIP net.IP, dstMac net.HardwareAddr, osDelete bool) error - - // Returns an interface with methods to set neighbor options. - NeighborOptions() NeighborOptionSetter - - // Returns an interface with methods to set interface options. - InterfaceOptions() IfaceOptionSetter - - //Invoke - InvokeFunc(func()) error - - // Returns an interface with methods to get sandbox state. - Info() Info - - // Destroy the sandbox - Destroy() error - - // restore sandbox - Restore(ifsopt map[string][]IfaceOption, routes []*types.StaticRoute, gw net.IP, gw6 net.IP) error - - // ApplyOSTweaks applies operating system specific knobs on the sandbox - ApplyOSTweaks([]SandboxType) -} - -// NeighborOptionSetter interface defines the option setter methods for interface options -type NeighborOptionSetter interface { - // LinkName returns an option setter to set the srcName of the link that should - // be used in the neighbor entry - LinkName(string) NeighOption - - // Family returns an option setter to set the address family for the neighbor - // entry. eg. AF_BRIDGE - Family(int) NeighOption -} - -// IfaceOptionSetter interface defines the option setter methods for interface options. -type IfaceOptionSetter interface { - // Bridge returns an option setter to set if the interface is a bridge. - Bridge(bool) IfaceOption - - // MacAddress returns an option setter to set the MAC address. - MacAddress(net.HardwareAddr) IfaceOption - - // Address returns an option setter to set IPv4 address. - Address(*net.IPNet) IfaceOption - - // Address returns an option setter to set IPv6 address. - AddressIPv6(*net.IPNet) IfaceOption - - // LinkLocalAddresses returns an option setter to set the link-local IP addresses. - LinkLocalAddresses([]*net.IPNet) IfaceOption - - // Master returns an option setter to set the master interface if any for this - // interface. The master interface name should refer to the srcname of a - // previously added interface of type bridge. - Master(string) IfaceOption - - // Address returns an option setter to set interface routes. - Routes([]*net.IPNet) IfaceOption -} - -// Info represents all possible information that -// the driver wants to place in the sandbox which includes -// interfaces, routes and gateway -type Info interface { - // The collection of Interface previously added with the AddInterface - // method. Note that this doesn't include network interfaces added in any - // other way (such as the default loopback interface which is automatically - // created on creation of a sandbox). - Interfaces() []Interface - - // IPv4 gateway for the sandbox. - Gateway() net.IP - - // IPv6 gateway for the sandbox. - GatewayIPv6() net.IP - - // Additional static routes for the sandbox. (Note that directly - // connected routes are stored on the particular interface they refer to.) - StaticRoutes() []*types.StaticRoute - - // TODO: Add ip tables etc. -} - -// Interface represents the settings and identity of a network device. It is -// used as a return type for Network.Link, and it is common practice for the -// caller to use this information when moving interface SrcName from host -// namespace to DstName in a different net namespace with the appropriate -// network settings. -type Interface interface { - // The name of the interface in the origin network namespace. - SrcName() string - - // The name that will be assigned to the interface once moves inside a - // network namespace. When the caller passes in a DstName, it is only - // expected to pass a prefix. The name will modified with an appropriately - // auto-generated suffix. - DstName() string - - // IPv4 address for the interface. - Address() *net.IPNet - - // IPv6 address for the interface. - AddressIPv6() *net.IPNet - - // LinkLocalAddresses returns the link-local IP addresses assigned to the interface. - LinkLocalAddresses() []*net.IPNet - - // IP routes for the interface. - Routes() []*net.IPNet - - // Bridge returns true if the interface is a bridge - Bridge() bool - - // Master returns the srcname of the master interface for this interface. - Master() string - - // Remove an interface from the sandbox by renaming to original name - // and moving it out of the sandbox. - Remove() error - - // Statistics returns the statistics for this interface - Statistics() (*types.InterfaceStatistics, error) -} diff --git a/vendor/github.com/docker/libnetwork/portallocator/portallocator_freebsd.go b/vendor/github.com/docker/libnetwork/portallocator/portallocator_freebsd.go deleted file mode 100644 index 97d7fbb49d8c3..0000000000000 --- a/vendor/github.com/docker/libnetwork/portallocator/portallocator_freebsd.go +++ /dev/null @@ -1,42 +0,0 @@ -package portallocator - -import ( - "bytes" - "fmt" - "os/exec" -) - -func getDynamicPortRange() (start int, end int, err error) { - portRangeKernelSysctl := []string{"net.inet.ip.portrange.hifirst", "net.ip.portrange.hilast"} - portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", DefaultPortRangeStart, DefaultPortRangeEnd) - portRangeLowCmd := exec.Command("/sbin/sysctl", portRangeKernelSysctl[0]) - var portRangeLowOut bytes.Buffer - portRangeLowCmd.Stdout = &portRangeLowOut - cmdErr := portRangeLowCmd.Run() - if cmdErr != nil { - return 0, 0, fmt.Errorf("port allocator - sysctl net.inet.ip.portrange.hifirst failed - %s: %v", portRangeFallback, err) - } - n, err := fmt.Sscanf(portRangeLowOut.String(), "%d", &start) - if n != 1 || err != nil { - if err == nil { - err = fmt.Errorf("unexpected count of parsed numbers (%d)", n) - } - return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range start from %s - %s: %v", portRangeLowOut.String(), portRangeFallback, err) - } - - portRangeHighCmd := exec.Command("/sbin/sysctl", portRangeKernelSysctl[1]) - var portRangeHighOut bytes.Buffer - portRangeHighCmd.Stdout = &portRangeHighOut - cmdErr = portRangeHighCmd.Run() - if cmdErr != nil { - return 0, 0, fmt.Errorf("port allocator - sysctl net.inet.ip.portrange.hilast failed - %s: %v", portRangeFallback, err) - } - n, err = fmt.Sscanf(portRangeHighOut.String(), "%d", &end) - if n != 1 || err != nil { - if err == nil { - err = fmt.Errorf("unexpected count of parsed numbers (%d)", n) - } - return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range end from %s - %s: %v", portRangeHighOut.String(), portRangeFallback, err) - } - return start, end, nil -} diff --git a/vendor/github.com/docker/libnetwork/portallocator/portallocator_linux.go b/vendor/github.com/docker/libnetwork/portallocator/portallocator_linux.go deleted file mode 100644 index 687f6dabb7ac1..0000000000000 --- a/vendor/github.com/docker/libnetwork/portallocator/portallocator_linux.go +++ /dev/null @@ -1,27 +0,0 @@ -package portallocator - -import ( - "bufio" - "fmt" - "os" -) - -func getDynamicPortRange() (start int, end int, err error) { - const portRangeKernelParam = "/proc/sys/net/ipv4/ip_local_port_range" - portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", DefaultPortRangeStart, DefaultPortRangeEnd) - file, err := os.Open(portRangeKernelParam) - if err != nil { - return 0, 0, fmt.Errorf("port allocator - %s due to error: %v", portRangeFallback, err) - } - - defer file.Close() - - n, err := fmt.Fscanf(bufio.NewReader(file), "%d\t%d", &start, &end) - if n != 2 || err != nil { - if err == nil { - err = fmt.Errorf("unexpected count of parsed numbers (%d)", n) - } - return 0, 0, fmt.Errorf("port allocator - failed to parse system ephemeral port range from %s - %s: %v", portRangeKernelParam, portRangeFallback, err) - } - return start, end, nil -} diff --git a/vendor/github.com/docker/libnetwork/portallocator/portallocator_windows.go b/vendor/github.com/docker/libnetwork/portallocator/portallocator_windows.go deleted file mode 100644 index f07ae884f82a5..0000000000000 --- a/vendor/github.com/docker/libnetwork/portallocator/portallocator_windows.go +++ /dev/null @@ -1 +0,0 @@ -package portallocator diff --git a/vendor/github.com/docker/libnetwork/portmapper/mapper.go b/vendor/github.com/docker/libnetwork/portmapper/mapper.go deleted file mode 100644 index 7fa37b1fb61ae..0000000000000 --- a/vendor/github.com/docker/libnetwork/portmapper/mapper.go +++ /dev/null @@ -1,294 +0,0 @@ -package portmapper - -import ( - "errors" - "fmt" - "net" - "sync" - - "github.com/docker/libnetwork/iptables" - "github.com/docker/libnetwork/portallocator" - "github.com/ishidawataru/sctp" - "github.com/sirupsen/logrus" -) - -type mapping struct { - proto string - userlandProxy userlandProxy - host net.Addr - container net.Addr -} - -var newProxy = newProxyCommand - -var ( - // ErrUnknownBackendAddressType refers to an unknown container or unsupported address type - ErrUnknownBackendAddressType = errors.New("unknown container address type not supported") - // ErrPortMappedForIP refers to a port already mapped to an ip address - ErrPortMappedForIP = errors.New("port is already mapped to ip") - // ErrPortNotMapped refers to an unmapped port - ErrPortNotMapped = errors.New("port is not mapped") - // ErrSCTPAddrNoIP refers to a SCTP address without IP address. - ErrSCTPAddrNoIP = errors.New("sctp address does not contain any IP address") -) - -// PortMapper manages the network address translation -type PortMapper struct { - chain *iptables.ChainInfo - bridgeName string - - // udp:ip:port - currentMappings map[string]*mapping - lock sync.Mutex - - proxyPath string - - Allocator *portallocator.PortAllocator -} - -// New returns a new instance of PortMapper -func New(proxyPath string) *PortMapper { - return NewWithPortAllocator(portallocator.Get(), proxyPath) -} - -// NewWithPortAllocator returns a new instance of PortMapper which will use the specified PortAllocator -func NewWithPortAllocator(allocator *portallocator.PortAllocator, proxyPath string) *PortMapper { - return &PortMapper{ - currentMappings: make(map[string]*mapping), - Allocator: allocator, - proxyPath: proxyPath, - } -} - -// SetIptablesChain sets the specified chain into portmapper -func (pm *PortMapper) SetIptablesChain(c *iptables.ChainInfo, bridgeName string) { - pm.chain = c - pm.bridgeName = bridgeName -} - -// Map maps the specified container transport address to the host's network address and transport port -func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, useProxy bool) (host net.Addr, err error) { - return pm.MapRange(container, hostIP, hostPort, hostPort, useProxy) -} - -// MapRange maps the specified container transport address to the host's network address and transport port range -func (pm *PortMapper) MapRange(container net.Addr, hostIP net.IP, hostPortStart, hostPortEnd int, useProxy bool) (host net.Addr, err error) { - pm.lock.Lock() - defer pm.lock.Unlock() - - var ( - m *mapping - proto string - allocatedHostPort int - ) - - switch container.(type) { - case *net.TCPAddr: - proto = "tcp" - if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { - return nil, err - } - - m = &mapping{ - proto: proto, - host: &net.TCPAddr{IP: hostIP, Port: allocatedHostPort}, - container: container, - } - - if useProxy { - m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port, pm.proxyPath) - if err != nil { - return nil, err - } - } else { - m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort) - if err != nil { - return nil, err - } - } - case *net.UDPAddr: - proto = "udp" - if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { - return nil, err - } - - m = &mapping{ - proto: proto, - host: &net.UDPAddr{IP: hostIP, Port: allocatedHostPort}, - container: container, - } - - if useProxy { - m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port, pm.proxyPath) - if err != nil { - return nil, err - } - } else { - m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort) - if err != nil { - return nil, err - } - } - case *sctp.SCTPAddr: - proto = "sctp" - if allocatedHostPort, err = pm.Allocator.RequestPortInRange(hostIP, proto, hostPortStart, hostPortEnd); err != nil { - return nil, err - } - - m = &mapping{ - proto: proto, - host: &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: allocatedHostPort}, - container: container, - } - - if useProxy { - sctpAddr := container.(*sctp.SCTPAddr) - if len(sctpAddr.IP) == 0 { - return nil, ErrSCTPAddrNoIP - } - m.userlandProxy, err = newProxy(proto, hostIP, allocatedHostPort, sctpAddr.IP[0], sctpAddr.Port, pm.proxyPath) - if err != nil { - return nil, err - } - } else { - m.userlandProxy, err = newDummyProxy(proto, hostIP, allocatedHostPort) - if err != nil { - return nil, err - } - } - default: - return nil, ErrUnknownBackendAddressType - } - - // release the allocated port on any further error during return. - defer func() { - if err != nil { - pm.Allocator.ReleasePort(hostIP, proto, allocatedHostPort) - } - }() - - key := getKey(m.host) - if _, exists := pm.currentMappings[key]; exists { - return nil, ErrPortMappedForIP - } - - containerIP, containerPort := getIPAndPort(m.container) - if hostIP.To4() != nil { - if err := pm.forward(iptables.Append, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort); err != nil { - return nil, err - } - } - - cleanup := func() error { - // need to undo the iptables rules before we return - m.userlandProxy.Stop() - if hostIP.To4() != nil { - pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort) - if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil { - return err - } - } - - return nil - } - - if err := m.userlandProxy.Start(); err != nil { - if err := cleanup(); err != nil { - return nil, fmt.Errorf("Error during port allocation cleanup: %v", err) - } - return nil, err - } - - pm.currentMappings[key] = m - return m.host, nil -} - -// Unmap removes stored mapping for the specified host transport address -func (pm *PortMapper) Unmap(host net.Addr) error { - pm.lock.Lock() - defer pm.lock.Unlock() - - key := getKey(host) - data, exists := pm.currentMappings[key] - if !exists { - return ErrPortNotMapped - } - - if data.userlandProxy != nil { - data.userlandProxy.Stop() - } - - delete(pm.currentMappings, key) - - containerIP, containerPort := getIPAndPort(data.container) - hostIP, hostPort := getIPAndPort(data.host) - if err := pm.forward(iptables.Delete, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { - logrus.Errorf("Error on iptables delete: %s", err) - } - - switch a := host.(type) { - case *net.TCPAddr: - return pm.Allocator.ReleasePort(a.IP, "tcp", a.Port) - case *net.UDPAddr: - return pm.Allocator.ReleasePort(a.IP, "udp", a.Port) - case *sctp.SCTPAddr: - if len(a.IP) == 0 { - return ErrSCTPAddrNoIP - } - return pm.Allocator.ReleasePort(a.IP[0], "sctp", a.Port) - } - return ErrUnknownBackendAddressType -} - -//ReMapAll will re-apply all port mappings -func (pm *PortMapper) ReMapAll() { - pm.lock.Lock() - defer pm.lock.Unlock() - logrus.Debugln("Re-applying all port mappings.") - for _, data := range pm.currentMappings { - containerIP, containerPort := getIPAndPort(data.container) - hostIP, hostPort := getIPAndPort(data.host) - if err := pm.forward(iptables.Append, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil { - logrus.Errorf("Error on iptables add: %s", err) - } - } -} - -func getKey(a net.Addr) string { - switch t := a.(type) { - case *net.TCPAddr: - return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "tcp") - case *net.UDPAddr: - return fmt.Sprintf("%s:%d/%s", t.IP.String(), t.Port, "udp") - case *sctp.SCTPAddr: - if len(t.IP) == 0 { - logrus.Error(ErrSCTPAddrNoIP) - return "" - } - return fmt.Sprintf("%s:%d/%s", t.IP[0].String(), t.Port, "sctp") - } - return "" -} - -func getIPAndPort(a net.Addr) (net.IP, int) { - switch t := a.(type) { - case *net.TCPAddr: - return t.IP, t.Port - case *net.UDPAddr: - return t.IP, t.Port - case *sctp.SCTPAddr: - if len(t.IP) == 0 { - logrus.Error(ErrSCTPAddrNoIP) - return nil, 0 - } - return t.IP[0], t.Port - } - return nil, 0 -} - -func (pm *PortMapper) forward(action iptables.Action, proto string, sourceIP net.IP, sourcePort int, containerIP string, containerPort int) error { - if pm.chain == nil { - return nil - } - return pm.chain.Forward(action, sourceIP, sourcePort, proto, containerIP, containerPort, pm.bridgeName) -} diff --git a/vendor/github.com/docker/libnetwork/portmapper/proxy.go b/vendor/github.com/docker/libnetwork/portmapper/proxy.go deleted file mode 100644 index 1183c33a7eb0e..0000000000000 --- a/vendor/github.com/docker/libnetwork/portmapper/proxy.go +++ /dev/null @@ -1,131 +0,0 @@ -package portmapper - -import ( - "fmt" - "io" - "io/ioutil" - "net" - "os" - "os/exec" - "time" - - "github.com/ishidawataru/sctp" -) - -var userlandProxyCommandName = "docker-proxy" - -type userlandProxy interface { - Start() error - Stop() error -} - -// proxyCommand wraps an exec.Cmd to run the userland TCP and UDP -// proxies as separate processes. -type proxyCommand struct { - cmd *exec.Cmd -} - -func (p *proxyCommand) Start() error { - r, w, err := os.Pipe() - if err != nil { - return fmt.Errorf("proxy unable to open os.Pipe %s", err) - } - defer r.Close() - p.cmd.ExtraFiles = []*os.File{w} - if err := p.cmd.Start(); err != nil { - return err - } - w.Close() - - errchan := make(chan error, 1) - go func() { - buf := make([]byte, 2) - r.Read(buf) - - if string(buf) != "0\n" { - errStr, err := ioutil.ReadAll(r) - if err != nil { - errchan <- fmt.Errorf("Error reading exit status from userland proxy: %v", err) - return - } - - errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr) - return - } - errchan <- nil - }() - - select { - case err := <-errchan: - return err - case <-time.After(16 * time.Second): - return fmt.Errorf("Timed out proxy starting the userland proxy") - } -} - -func (p *proxyCommand) Stop() error { - if p.cmd.Process != nil { - if err := p.cmd.Process.Signal(os.Interrupt); err != nil { - return err - } - return p.cmd.Wait() - } - return nil -} - -// dummyProxy just listen on some port, it is needed to prevent accidental -// port allocations on bound port, because without userland proxy we using -// iptables rules and not net.Listen -type dummyProxy struct { - listener io.Closer - addr net.Addr -} - -func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, error) { - switch proto { - case "tcp": - addr := &net.TCPAddr{IP: hostIP, Port: hostPort} - return &dummyProxy{addr: addr}, nil - case "udp": - addr := &net.UDPAddr{IP: hostIP, Port: hostPort} - return &dummyProxy{addr: addr}, nil - case "sctp": - addr := &sctp.SCTPAddr{IP: []net.IP{hostIP}, Port: hostPort} - return &dummyProxy{addr: addr}, nil - default: - return nil, fmt.Errorf("Unknown addr type: %s", proto) - } -} - -func (p *dummyProxy) Start() error { - switch addr := p.addr.(type) { - case *net.TCPAddr: - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return err - } - p.listener = l - case *net.UDPAddr: - l, err := net.ListenUDP("udp", addr) - if err != nil { - return err - } - p.listener = l - case *sctp.SCTPAddr: - l, err := sctp.ListenSCTP("sctp", addr) - if err != nil { - return err - } - p.listener = l - default: - return fmt.Errorf("Unknown addr type: %T", p.addr) - } - return nil -} - -func (p *dummyProxy) Stop() error { - if p.listener != nil { - return p.listener.Close() - } - return nil -} diff --git a/vendor/github.com/docker/libnetwork/portmapper/proxy_linux.go b/vendor/github.com/docker/libnetwork/portmapper/proxy_linux.go deleted file mode 100644 index 947cd0ba4b3f5..0000000000000 --- a/vendor/github.com/docker/libnetwork/portmapper/proxy_linux.go +++ /dev/null @@ -1,38 +0,0 @@ -package portmapper - -import ( - "net" - "os/exec" - "strconv" - "syscall" -) - -func newProxyCommand(proto string, hostIP net.IP, hostPort int, containerIP net.IP, containerPort int, proxyPath string) (userlandProxy, error) { - path := proxyPath - if proxyPath == "" { - cmd, err := exec.LookPath(userlandProxyCommandName) - if err != nil { - return nil, err - } - path = cmd - } - - args := []string{ - path, - "-proto", proto, - "-host-ip", hostIP.String(), - "-host-port", strconv.Itoa(hostPort), - "-container-ip", containerIP.String(), - "-container-port", strconv.Itoa(containerPort), - } - - return &proxyCommand{ - cmd: &exec.Cmd{ - Path: path, - Args: args, - SysProcAttr: &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGTERM, // send a sigterm to the proxy if the daemon process dies - }, - }, - }, nil -} diff --git a/vendor/github.com/docker/libnetwork/resolvconf/README.md b/vendor/github.com/docker/libnetwork/resolvconf/README.md deleted file mode 100644 index cdda554ba5728..0000000000000 --- a/vendor/github.com/docker/libnetwork/resolvconf/README.md +++ /dev/null @@ -1 +0,0 @@ -Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf diff --git a/vendor/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go b/vendor/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go deleted file mode 100644 index e348bc57f56b8..0000000000000 --- a/vendor/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go +++ /dev/null @@ -1,26 +0,0 @@ -package dns - -import ( - "regexp" -) - -// IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range. -const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)` - -// IPv4Localhost is a regex pattern for IPv4 localhost address range. -const IPv4Localhost = `(127\.([0-9]{1,3}\.){2}[0-9]{1,3})` - -var localhostIPRegexp = regexp.MustCompile(IPLocalhost) -var localhostIPv4Regexp = regexp.MustCompile(IPv4Localhost) - -// IsLocalhost returns true if ip matches the localhost IP regular expression. -// Used for determining if nameserver settings are being passed which are -// localhost addresses -func IsLocalhost(ip string) bool { - return localhostIPRegexp.MatchString(ip) -} - -// IsIPv4Localhost returns true if ip matches the IPv4 localhost regular expression. -func IsIPv4Localhost(ip string) bool { - return localhostIPv4Regexp.MatchString(ip) -} diff --git a/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go b/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go deleted file mode 100644 index 23caf7f120832..0000000000000 --- a/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go +++ /dev/null @@ -1,251 +0,0 @@ -// Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf -package resolvconf - -import ( - "bytes" - "io/ioutil" - "regexp" - "strings" - "sync" - - "github.com/docker/docker/pkg/ioutils" - "github.com/docker/libnetwork/resolvconf/dns" - "github.com/docker/libnetwork/types" - "github.com/sirupsen/logrus" -) - -const ( - // DefaultResolvConf points to the default file used for dns configuration on a linux machine - DefaultResolvConf = "/etc/resolv.conf" -) - -var ( - // Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS - defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"} - defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"} - ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` - ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock - // This is not an IPv6 address verifier as it will accept a super-set of IPv6, and also - // will *not match* IPv4-Embedded IPv6 Addresses (RFC6052), but that and other variants - // -- e.g. other link-local types -- either won't work in containers or are unnecessary. - // For readability and sufficiency for Docker purposes this seemed more reasonable than a - // 1000+ character regexp with exact and complete IPv6 validation - ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})(%\w+)?` - - localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IPLocalhost + `\s*\n*`) - nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`) - nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) - nsIPv6Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv6Address + `))\s*$`) - nsIPv4Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `))\s*$`) - searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) - optionsRegexp = regexp.MustCompile(`^\s*options\s*(([^\s]+\s*)*)$`) -) - -var lastModified struct { - sync.Mutex - sha256 string - contents []byte -} - -// File contains the resolv.conf content and its hash -type File struct { - Content []byte - Hash string -} - -// Get returns the contents of /etc/resolv.conf and its hash -func Get() (*File, error) { - return GetSpecific(DefaultResolvConf) -} - -// GetSpecific returns the contents of the user specified resolv.conf file and its hash -func GetSpecific(path string) (*File, error) { - resolv, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - hash, err := ioutils.HashData(bytes.NewReader(resolv)) - if err != nil { - return nil, err - } - return &File{Content: resolv, Hash: hash}, nil -} - -// GetIfChanged retrieves the host /etc/resolv.conf file, checks against the last hash -// and, if modified since last check, returns the bytes and new hash. -// This feature is used by the resolv.conf updater for containers -func GetIfChanged() (*File, error) { - lastModified.Lock() - defer lastModified.Unlock() - - resolv, err := ioutil.ReadFile("/etc/resolv.conf") - if err != nil { - return nil, err - } - newHash, err := ioutils.HashData(bytes.NewReader(resolv)) - if err != nil { - return nil, err - } - if lastModified.sha256 != newHash { - lastModified.sha256 = newHash - lastModified.contents = resolv - return &File{Content: resolv, Hash: newHash}, nil - } - // nothing changed, so return no data - return nil, nil -} - -// GetLastModified retrieves the last used contents and hash of the host resolv.conf. -// Used by containers updating on restart -func GetLastModified() *File { - lastModified.Lock() - defer lastModified.Unlock() - - return &File{Content: lastModified.contents, Hash: lastModified.sha256} -} - -// FilterResolvDNS cleans up the config in resolvConf. It has two main jobs: -// 1. It looks for localhost (127.*|::1) entries in the provided -// resolv.conf, removing local nameserver entries, and, if the resulting -// cleaned config has no defined nameservers left, adds default DNS entries -// 2. Given the caller provides the enable/disable state of IPv6, the filter -// code will remove all IPv6 nameservers if it is not enabled for containers -// -func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) { - cleanedResolvConf := localhostNSRegexp.ReplaceAll(resolvConf, []byte{}) - // if IPv6 is not enabled, also clean out any IPv6 address nameserver - if !ipv6Enabled { - cleanedResolvConf = nsIPv6Regexp.ReplaceAll(cleanedResolvConf, []byte{}) - } - // if the resulting resolvConf has no more nameservers defined, add appropriate - // default DNS servers for IPv4 and (optionally) IPv6 - if len(GetNameservers(cleanedResolvConf, types.IP)) == 0 { - logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers: %v", defaultIPv4Dns) - dns := defaultIPv4Dns - if ipv6Enabled { - logrus.Infof("IPv6 enabled; Adding default IPv6 external servers: %v", defaultIPv6Dns) - dns = append(dns, defaultIPv6Dns...) - } - cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...) - } - hash, err := ioutils.HashData(bytes.NewReader(cleanedResolvConf)) - if err != nil { - return nil, err - } - return &File{Content: cleanedResolvConf, Hash: hash}, nil -} - -// getLines parses input into lines and strips away comments. -func getLines(input []byte, commentMarker []byte) [][]byte { - lines := bytes.Split(input, []byte("\n")) - var output [][]byte - for _, currentLine := range lines { - var commentIndex = bytes.Index(currentLine, commentMarker) - if commentIndex == -1 { - output = append(output, currentLine) - } else { - output = append(output, currentLine[:commentIndex]) - } - } - return output -} - -// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf -func GetNameservers(resolvConf []byte, kind int) []string { - nameservers := []string{} - for _, line := range getLines(resolvConf, []byte("#")) { - var ns [][]byte - if kind == types.IP { - ns = nsRegexp.FindSubmatch(line) - } else if kind == types.IPv4 { - ns = nsIPv4Regexpmatch.FindSubmatch(line) - } else if kind == types.IPv6 { - ns = nsIPv6Regexpmatch.FindSubmatch(line) - } - if len(ns) > 0 { - nameservers = append(nameservers, string(ns[1])) - } - } - return nameservers -} - -// GetNameserversAsCIDR returns nameservers (if any) listed in -// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") -// This function's output is intended for net.ParseCIDR -func GetNameserversAsCIDR(resolvConf []byte) []string { - nameservers := []string{} - for _, nameserver := range GetNameservers(resolvConf, types.IP) { - var address string - // If IPv6, strip zone if present - if strings.Contains(nameserver, ":") { - address = strings.Split(nameserver, "%")[0] + "/128" - } else { - address = nameserver + "/32" - } - nameservers = append(nameservers, address) - } - return nameservers -} - -// GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf -// If more than one search line is encountered, only the contents of the last -// one is returned. -func GetSearchDomains(resolvConf []byte) []string { - domains := []string{} - for _, line := range getLines(resolvConf, []byte("#")) { - match := searchRegexp.FindSubmatch(line) - if match == nil { - continue - } - domains = strings.Fields(string(match[1])) - } - return domains -} - -// GetOptions returns options (if any) listed in /etc/resolv.conf -// If more than one options line is encountered, only the contents of the last -// one is returned. -func GetOptions(resolvConf []byte) []string { - options := []string{} - for _, line := range getLines(resolvConf, []byte("#")) { - match := optionsRegexp.FindSubmatch(line) - if match == nil { - continue - } - options = strings.Fields(string(match[1])) - } - return options -} - -// Build writes a configuration file to path containing a "nameserver" entry -// for every element in dns, a "search" entry for every element in -// dnsSearch, and an "options" entry for every element in dnsOptions. -func Build(path string, dns, dnsSearch, dnsOptions []string) (*File, error) { - content := bytes.NewBuffer(nil) - if len(dnsSearch) > 0 { - if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." { - if _, err := content.WriteString("search " + searchString + "\n"); err != nil { - return nil, err - } - } - } - for _, dns := range dns { - if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil { - return nil, err - } - } - if len(dnsOptions) > 0 { - if optsString := strings.Join(dnsOptions, " "); strings.Trim(optsString, " ") != "" { - if _, err := content.WriteString("options " + optsString + "\n"); err != nil { - return nil, err - } - } - } - - hash, err := ioutils.HashData(bytes.NewReader(content.Bytes())) - if err != nil { - return nil, err - } - - return &File{Content: content.Bytes(), Hash: hash}, ioutil.WriteFile(path, content.Bytes(), 0644) -} diff --git a/vendor/github.com/docker/libnetwork/resolver_unix.go b/vendor/github.com/docker/libnetwork/resolver_unix.go deleted file mode 100644 index f4e4ad6184840..0000000000000 --- a/vendor/github.com/docker/libnetwork/resolver_unix.go +++ /dev/null @@ -1,101 +0,0 @@ -// +build !windows - -package libnetwork - -import ( - "fmt" - "net" - "os" - "os/exec" - "runtime" - - "github.com/docker/docker/pkg/reexec" - "github.com/docker/libnetwork/iptables" - "github.com/sirupsen/logrus" - "github.com/vishvananda/netns" -) - -func init() { - reexec.Register("setup-resolver", reexecSetupResolver) -} - -const ( - // outputChain used for docker embed dns - outputChain = "DOCKER_OUTPUT" - //postroutingchain used for docker embed dns - postroutingchain = "DOCKER_POSTROUTING" -) - -func reexecSetupResolver() { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - if len(os.Args) < 4 { - logrus.Error("invalid number of arguments..") - os.Exit(1) - } - - resolverIP, ipPort, _ := net.SplitHostPort(os.Args[2]) - _, tcpPort, _ := net.SplitHostPort(os.Args[3]) - rules := [][]string{ - {"-t", "nat", "-I", outputChain, "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", os.Args[2]}, - {"-t", "nat", "-I", postroutingchain, "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort}, - {"-t", "nat", "-I", outputChain, "-d", resolverIP, "-p", "tcp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", os.Args[3]}, - {"-t", "nat", "-I", postroutingchain, "-s", resolverIP, "-p", "tcp", "--sport", tcpPort, "-j", "SNAT", "--to-source", ":" + dnsPort}, - } - - f, err := os.OpenFile(os.Args[1], os.O_RDONLY, 0) - if err != nil { - logrus.Errorf("failed get network namespace %q: %v", os.Args[1], err) - os.Exit(2) - } - defer f.Close() - - nsFD := f.Fd() - if err = netns.Set(netns.NsHandle(nsFD)); err != nil { - logrus.Errorf("setting into container net ns %v failed, %v", os.Args[1], err) - os.Exit(3) - } - - // insert outputChain and postroutingchain - err = iptables.RawCombinedOutputNative("-t", "nat", "-C", "OUTPUT", "-d", resolverIP, "-j", outputChain) - if err == nil { - iptables.RawCombinedOutputNative("-t", "nat", "-F", outputChain) - } else { - iptables.RawCombinedOutputNative("-t", "nat", "-N", outputChain) - iptables.RawCombinedOutputNative("-t", "nat", "-I", "OUTPUT", "-d", resolverIP, "-j", outputChain) - } - - err = iptables.RawCombinedOutputNative("-t", "nat", "-C", "POSTROUTING", "-d", resolverIP, "-j", postroutingchain) - if err == nil { - iptables.RawCombinedOutputNative("-t", "nat", "-F", postroutingchain) - } else { - iptables.RawCombinedOutputNative("-t", "nat", "-N", postroutingchain) - iptables.RawCombinedOutputNative("-t", "nat", "-I", "POSTROUTING", "-d", resolverIP, "-j", postroutingchain) - } - - for _, rule := range rules { - if iptables.RawCombinedOutputNative(rule...) != nil { - logrus.Errorf("set up rule failed, %v", rule) - } - } -} - -func (r *resolver) setupIPTable() error { - if r.err != nil { - return r.err - } - laddr := r.conn.LocalAddr().String() - ltcpaddr := r.tcpListen.Addr().String() - - cmd := &exec.Cmd{ - Path: reexec.Self(), - Args: append([]string{"setup-resolver"}, r.resolverKey, laddr, ltcpaddr), - Stdout: os.Stdout, - Stderr: os.Stderr, - } - if err := cmd.Run(); err != nil { - return fmt.Errorf("reexec failed: %v", err) - } - return nil -} diff --git a/vendor/github.com/docker/libnetwork/sandbox.go b/vendor/github.com/docker/libnetwork/sandbox.go deleted file mode 100644 index 03c9215786ae7..0000000000000 --- a/vendor/github.com/docker/libnetwork/sandbox.go +++ /dev/null @@ -1,1269 +0,0 @@ -package libnetwork - -import ( - "encoding/json" - "fmt" - "net" - "sort" - "strings" - "sync" - "time" - - "github.com/docker/libnetwork/etchosts" - "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/osl" - "github.com/docker/libnetwork/types" - "github.com/sirupsen/logrus" -) - -// Sandbox provides the control over the network container entity. It is a one to one mapping with the container. -type Sandbox interface { - // ID returns the ID of the sandbox - ID() string - // Key returns the sandbox's key - Key() string - // ContainerID returns the container id associated to this sandbox - ContainerID() string - // Labels returns the sandbox's labels - Labels() map[string]interface{} - // Statistics retrieves the interfaces' statistics for the sandbox - Statistics() (map[string]*types.InterfaceStatistics, error) - // Refresh leaves all the endpoints, resets and re-applies the options, - // re-joins all the endpoints without destroying the osl sandbox - Refresh(options ...SandboxOption) error - // SetKey updates the Sandbox Key - SetKey(key string) error - // Rename changes the name of all attached Endpoints - Rename(name string) error - // Delete destroys this container after detaching it from all connected endpoints. - Delete() error - // Endpoints returns all the endpoints connected to the sandbox - Endpoints() []Endpoint - // ResolveService returns all the backend details about the containers or hosts - // backing a service. Its purpose is to satisfy an SRV query - ResolveService(name string) ([]*net.SRV, []net.IP) - // EnableService makes a managed container's service available by adding the - // endpoint to the service load balancer and service discovery - EnableService() error - // DisableService removes a managed container's endpoints from the load balancer - // and service discovery - DisableService() error -} - -// SandboxOption is an option setter function type used to pass various options to -// NewNetContainer method. The various setter functions of type SandboxOption are -// provided by libnetwork, they look like ContainerOptionXXXX(...) -type SandboxOption func(sb *sandbox) - -func (sb *sandbox) processOptions(options ...SandboxOption) { - for _, opt := range options { - if opt != nil { - opt(sb) - } - } -} - -type sandbox struct { - id string - containerID string - config containerConfig - extDNS []extDNSEntry - osSbox osl.Sandbox - controller *controller - resolver Resolver - resolverOnce sync.Once - refCnt int - endpoints []*endpoint - epPriority map[string]int - populatedEndpoints map[string]struct{} - joinLeaveDone chan struct{} - dbIndex uint64 - dbExists bool - isStub bool - inDelete bool - ingress bool - ndotsSet bool - oslTypes []osl.SandboxType // slice of properties of this sandbox - loadBalancerNID string // NID that this SB is a load balancer for - sync.Mutex - // This mutex is used to serialize service related operation for an endpoint - // The lock is here because the endpoint is saved into the store so is not unique - Service sync.Mutex -} - -// These are the container configs used to customize container /etc/hosts file. -type hostsPathConfig struct { - hostName string - domainName string - hostsPath string - originHostsPath string - extraHosts []extraHost - parentUpdates []parentUpdate -} - -type parentUpdate struct { - cid string - name string - ip string -} - -type extraHost struct { - name string - IP string -} - -// These are the container configs used to customize container /etc/resolv.conf file. -type resolvConfPathConfig struct { - resolvConfPath string - originResolvConfPath string - resolvConfHashFile string - dnsList []string - dnsSearchList []string - dnsOptionsList []string -} - -type containerConfig struct { - hostsPathConfig - resolvConfPathConfig - generic map[string]interface{} - useDefaultSandBox bool - useExternalKey bool - prio int // higher the value, more the priority - exposedPorts []types.TransportPort -} - -const ( - resolverIPSandbox = "127.0.0.11" -) - -func (sb *sandbox) ID() string { - return sb.id -} - -func (sb *sandbox) ContainerID() string { - return sb.containerID -} - -func (sb *sandbox) Key() string { - if sb.config.useDefaultSandBox { - return osl.GenerateKey("default") - } - return osl.GenerateKey(sb.id) -} - -func (sb *sandbox) Labels() map[string]interface{} { - sb.Lock() - defer sb.Unlock() - opts := make(map[string]interface{}, len(sb.config.generic)) - for k, v := range sb.config.generic { - opts[k] = v - } - return opts -} - -func (sb *sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) { - m := make(map[string]*types.InterfaceStatistics) - - sb.Lock() - osb := sb.osSbox - sb.Unlock() - if osb == nil { - return m, nil - } - - var err error - for _, i := range osb.Info().Interfaces() { - if m[i.DstName()], err = i.Statistics(); err != nil { - return m, err - } - } - - return m, nil -} - -func (sb *sandbox) Delete() error { - return sb.delete(false) -} - -func (sb *sandbox) delete(force bool) error { - sb.Lock() - if sb.inDelete { - sb.Unlock() - return types.ForbiddenErrorf("another sandbox delete in progress") - } - // Set the inDelete flag. This will ensure that we don't - // update the store until we have completed all the endpoint - // leaves and deletes. And when endpoint leaves and deletes - // are completed then we can finally delete the sandbox object - // altogether from the data store. If the daemon exits - // ungracefully in the middle of a sandbox delete this way we - // will have all the references to the endpoints in the - // sandbox so that we can clean them up when we restart - sb.inDelete = true - sb.Unlock() - - c := sb.controller - - // Detach from all endpoints - retain := false - for _, ep := range sb.getConnectedEndpoints() { - // gw network endpoint detach and removal are automatic - if ep.endpointInGWNetwork() && !force { - continue - } - // Retain the sanbdox if we can't obtain the network from store. - if _, err := c.getNetworkFromStore(ep.getNetwork().ID()); err != nil { - if c.isDistributedControl() { - retain = true - } - logrus.Warnf("Failed getting network for ep %s during sandbox %s delete: %v", ep.ID(), sb.ID(), err) - continue - } - - if !force { - if err := ep.Leave(sb); err != nil { - logrus.Warnf("Failed detaching sandbox %s from endpoint %s: %v\n", sb.ID(), ep.ID(), err) - } - } - - if err := ep.Delete(force); err != nil { - logrus.Warnf("Failed deleting endpoint %s: %v\n", ep.ID(), err) - } - } - - if retain { - sb.Lock() - sb.inDelete = false - sb.Unlock() - return fmt.Errorf("could not cleanup all the endpoints in container %s / sandbox %s", sb.containerID, sb.id) - } - // Container is going away. Path cache in etchosts is most - // likely not required any more. Drop it. - etchosts.Drop(sb.config.hostsPath) - - if sb.resolver != nil { - sb.resolver.Stop() - } - - if sb.osSbox != nil && !sb.config.useDefaultSandBox { - sb.osSbox.Destroy() - } - - if err := sb.storeDelete(); err != nil { - logrus.Warnf("Failed to delete sandbox %s from store: %v", sb.ID(), err) - } - - c.Lock() - if sb.ingress { - c.ingressSandbox = nil - } - delete(c.sandboxes, sb.ID()) - c.Unlock() - - return nil -} - -func (sb *sandbox) Rename(name string) error { - var err error - - for _, ep := range sb.getConnectedEndpoints() { - if ep.endpointInGWNetwork() { - continue - } - - oldName := ep.Name() - lEp := ep - if err = ep.rename(name); err != nil { - break - } - - defer func() { - if err != nil { - lEp.rename(oldName) - } - }() - } - - return err -} - -func (sb *sandbox) Refresh(options ...SandboxOption) error { - // Store connected endpoints - epList := sb.getConnectedEndpoints() - - // Detach from all endpoints - for _, ep := range epList { - if err := ep.Leave(sb); err != nil { - logrus.Warnf("Failed detaching sandbox %s from endpoint %s: %v\n", sb.ID(), ep.ID(), err) - } - } - - // Re-apply options - sb.config = containerConfig{} - sb.processOptions(options...) - - // Setup discovery files - if err := sb.setupResolutionFiles(); err != nil { - return err - } - - // Re-connect to all endpoints - for _, ep := range epList { - if err := ep.Join(sb); err != nil { - logrus.Warnf("Failed attach sandbox %s to endpoint %s: %v\n", sb.ID(), ep.ID(), err) - } - } - - return nil -} - -func (sb *sandbox) MarshalJSON() ([]byte, error) { - sb.Lock() - defer sb.Unlock() - - // We are just interested in the container ID. This can be expanded to include all of containerInfo if there is a need - return json.Marshal(sb.id) -} - -func (sb *sandbox) UnmarshalJSON(b []byte) (err error) { - sb.Lock() - defer sb.Unlock() - - var id string - if err := json.Unmarshal(b, &id); err != nil { - return err - } - sb.id = id - return nil -} - -func (sb *sandbox) Endpoints() []Endpoint { - sb.Lock() - defer sb.Unlock() - - endpoints := make([]Endpoint, len(sb.endpoints)) - for i, ep := range sb.endpoints { - endpoints[i] = ep - } - return endpoints -} - -func (sb *sandbox) getConnectedEndpoints() []*endpoint { - sb.Lock() - defer sb.Unlock() - - eps := make([]*endpoint, len(sb.endpoints)) - copy(eps, sb.endpoints) - - return eps -} - -func (sb *sandbox) addEndpoint(ep *endpoint) { - sb.Lock() - defer sb.Unlock() - - l := len(sb.endpoints) - i := sort.Search(l, func(j int) bool { - return ep.Less(sb.endpoints[j]) - }) - - sb.endpoints = append(sb.endpoints, nil) - copy(sb.endpoints[i+1:], sb.endpoints[i:]) - sb.endpoints[i] = ep -} - -func (sb *sandbox) removeEndpoint(ep *endpoint) { - sb.Lock() - defer sb.Unlock() - - sb.removeEndpointRaw(ep) -} - -func (sb *sandbox) removeEndpointRaw(ep *endpoint) { - for i, e := range sb.endpoints { - if e == ep { - sb.endpoints = append(sb.endpoints[:i], sb.endpoints[i+1:]...) - return - } - } -} - -func (sb *sandbox) getEndpoint(id string) *endpoint { - sb.Lock() - defer sb.Unlock() - - for _, ep := range sb.endpoints { - if ep.id == id { - return ep - } - } - - return nil -} - -func (sb *sandbox) updateGateway(ep *endpoint) error { - sb.Lock() - osSbox := sb.osSbox - sb.Unlock() - if osSbox == nil { - return nil - } - osSbox.UnsetGateway() - osSbox.UnsetGatewayIPv6() - - if ep == nil { - return nil - } - - ep.Lock() - joinInfo := ep.joinInfo - ep.Unlock() - - if err := osSbox.SetGateway(joinInfo.gw); err != nil { - return fmt.Errorf("failed to set gateway while updating gateway: %v", err) - } - - if err := osSbox.SetGatewayIPv6(joinInfo.gw6); err != nil { - return fmt.Errorf("failed to set IPv6 gateway while updating gateway: %v", err) - } - - return nil -} - -func (sb *sandbox) HandleQueryResp(name string, ip net.IP) { - for _, ep := range sb.getConnectedEndpoints() { - n := ep.getNetwork() - n.HandleQueryResp(name, ip) - } -} - -func (sb *sandbox) ResolveIP(ip string) string { - var svc string - logrus.Debugf("IP To resolve %v", ip) - - for _, ep := range sb.getConnectedEndpoints() { - n := ep.getNetwork() - svc = n.ResolveIP(ip) - if len(svc) != 0 { - return svc - } - } - - return svc -} - -func (sb *sandbox) ExecFunc(f func()) error { - sb.Lock() - osSbox := sb.osSbox - sb.Unlock() - if osSbox != nil { - return osSbox.InvokeFunc(f) - } - return fmt.Errorf("osl sandbox unavailable in ExecFunc for %v", sb.ContainerID()) -} - -func (sb *sandbox) ResolveService(name string) ([]*net.SRV, []net.IP) { - srv := []*net.SRV{} - ip := []net.IP{} - - logrus.Debugf("Service name To resolve: %v", name) - - // There are DNS implementations that allow SRV queries for names not in - // the format defined by RFC 2782. Hence specific validations checks are - // not done - parts := strings.Split(name, ".") - if len(parts) < 3 { - return nil, nil - } - - for _, ep := range sb.getConnectedEndpoints() { - n := ep.getNetwork() - - srv, ip = n.ResolveService(name) - if len(srv) > 0 { - break - } - } - return srv, ip -} - -func getDynamicNwEndpoints(epList []*endpoint) []*endpoint { - eps := []*endpoint{} - for _, ep := range epList { - n := ep.getNetwork() - if n.dynamic && !n.ingress { - eps = append(eps, ep) - } - } - return eps -} - -func getIngressNwEndpoint(epList []*endpoint) *endpoint { - for _, ep := range epList { - n := ep.getNetwork() - if n.ingress { - return ep - } - } - return nil -} - -func getLocalNwEndpoints(epList []*endpoint) []*endpoint { - eps := []*endpoint{} - for _, ep := range epList { - n := ep.getNetwork() - if !n.dynamic && !n.ingress { - eps = append(eps, ep) - } - } - return eps -} - -func (sb *sandbox) ResolveName(name string, ipType int) ([]net.IP, bool) { - // Embedded server owns the docker network domain. Resolution should work - // for both container_name and container_name.network_name - // We allow '.' in service name and network name. For a name a.b.c.d the - // following have to tried; - // {a.b.c.d in the networks container is connected to} - // {a.b.c in network d}, - // {a.b in network c.d}, - // {a in network b.c.d}, - - logrus.Debugf("Name To resolve: %v", name) - name = strings.TrimSuffix(name, ".") - reqName := []string{name} - networkName := []string{""} - - if strings.Contains(name, ".") { - var i int - dup := name - for { - if i = strings.LastIndex(dup, "."); i == -1 { - break - } - networkName = append(networkName, name[i+1:]) - reqName = append(reqName, name[:i]) - - dup = dup[:i] - } - } - - epList := sb.getConnectedEndpoints() - - // In swarm mode services with exposed ports are connected to user overlay - // network, ingress network and docker_gwbridge network. Name resolution - // should prioritize returning the VIP/IPs on user overlay network. - newList := []*endpoint{} - if !sb.controller.isDistributedControl() { - newList = append(newList, getDynamicNwEndpoints(epList)...) - ingressEP := getIngressNwEndpoint(epList) - if ingressEP != nil { - newList = append(newList, ingressEP) - } - newList = append(newList, getLocalNwEndpoints(epList)...) - epList = newList - } - - for i := 0; i < len(reqName); i++ { - - // First check for local container alias - ip, ipv6Miss := sb.resolveName(reqName[i], networkName[i], epList, true, ipType) - if ip != nil { - return ip, false - } - if ipv6Miss { - return ip, ipv6Miss - } - - // Resolve the actual container name - ip, ipv6Miss = sb.resolveName(reqName[i], networkName[i], epList, false, ipType) - if ip != nil { - return ip, false - } - if ipv6Miss { - return ip, ipv6Miss - } - } - return nil, false -} - -func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoint, alias bool, ipType int) ([]net.IP, bool) { - var ipv6Miss bool - - for _, ep := range epList { - name := req - n := ep.getNetwork() - - if networkName != "" && networkName != n.Name() { - continue - } - - if alias { - if ep.aliases == nil { - continue - } - - var ok bool - ep.Lock() - name, ok = ep.aliases[req] - ep.Unlock() - if !ok { - continue - } - } else { - // If it is a regular lookup and if the requested name is an alias - // don't perform a svc lookup for this endpoint. - ep.Lock() - if _, ok := ep.aliases[req]; ok { - ep.Unlock() - continue - } - ep.Unlock() - } - - ip, miss := n.ResolveName(name, ipType) - - if ip != nil { - return ip, false - } - - if miss { - ipv6Miss = miss - } - } - return nil, ipv6Miss -} - -func (sb *sandbox) SetKey(basePath string) error { - start := time.Now() - defer func() { - logrus.Debugf("sandbox set key processing took %s for container %s", time.Since(start), sb.ContainerID()) - }() - - if basePath == "" { - return types.BadRequestErrorf("invalid sandbox key") - } - - sb.Lock() - if sb.inDelete { - sb.Unlock() - return types.ForbiddenErrorf("failed to SetKey: sandbox %q delete in progress", sb.id) - } - oldosSbox := sb.osSbox - sb.Unlock() - - if oldosSbox != nil { - // If we already have an OS sandbox, release the network resources from that - // and destroy the OS snab. We are moving into a new home further down. Note that none - // of the network resources gets destroyed during the move. - sb.releaseOSSbox() - } - - osSbox, err := osl.GetSandboxForExternalKey(basePath, sb.Key()) - if err != nil { - return err - } - - sb.Lock() - sb.osSbox = osSbox - sb.Unlock() - - // If the resolver was setup before stop it and set it up in the - // new osl sandbox. - if oldosSbox != nil && sb.resolver != nil { - sb.resolver.Stop() - - if err := sb.osSbox.InvokeFunc(sb.resolver.SetupFunc(0)); err == nil { - if err := sb.resolver.Start(); err != nil { - logrus.Errorf("Resolver Start failed for container %s, %q", sb.ContainerID(), err) - } - } else { - logrus.Errorf("Resolver Setup Function failed for container %s, %q", sb.ContainerID(), err) - } - } - - for _, ep := range sb.getConnectedEndpoints() { - if err = sb.populateNetworkResources(ep); err != nil { - return err - } - } - return nil -} - -func (sb *sandbox) EnableService() (err error) { - logrus.Debugf("EnableService %s START", sb.containerID) - defer func() { - if err != nil { - sb.DisableService() - } - }() - for _, ep := range sb.getConnectedEndpoints() { - if !ep.isServiceEnabled() { - if err := ep.addServiceInfoToCluster(sb); err != nil { - return fmt.Errorf("could not update state for endpoint %s into cluster: %v", ep.Name(), err) - } - ep.enableService() - } - } - logrus.Debugf("EnableService %s DONE", sb.containerID) - return nil -} - -func (sb *sandbox) DisableService() (err error) { - logrus.Debugf("DisableService %s START", sb.containerID) - failedEps := []string{} - defer func() { - if len(failedEps) > 0 { - err = fmt.Errorf("failed to disable service on sandbox:%s, for endpoints %s", sb.ID(), strings.Join(failedEps, ",")) - } - }() - for _, ep := range sb.getConnectedEndpoints() { - if ep.isServiceEnabled() { - if err := ep.deleteServiceInfoFromCluster(sb, false, "DisableService"); err != nil { - failedEps = append(failedEps, ep.Name()) - logrus.Warnf("failed update state for endpoint %s into cluster: %v", ep.Name(), err) - } - ep.disableService() - } - } - logrus.Debugf("DisableService %s DONE", sb.containerID) - return nil -} - -func releaseOSSboxResources(osSbox osl.Sandbox, ep *endpoint) { - for _, i := range osSbox.Info().Interfaces() { - // Only remove the interfaces owned by this endpoint from the sandbox. - if ep.hasInterface(i.SrcName()) { - if err := i.Remove(); err != nil { - logrus.Debugf("Remove interface %s failed: %v", i.SrcName(), err) - } - } - } - - ep.Lock() - joinInfo := ep.joinInfo - vip := ep.virtualIP - lbModeIsDSR := ep.network.loadBalancerMode == loadBalancerModeDSR - ep.Unlock() - - if len(vip) > 0 && lbModeIsDSR { - ipNet := &net.IPNet{IP: vip, Mask: net.CIDRMask(32, 32)} - if err := osSbox.RemoveAliasIP(osSbox.GetLoopbackIfaceName(), ipNet); err != nil { - logrus.WithError(err).Debugf("failed to remove virtual ip %v to loopback", ipNet) - } - } - - if joinInfo == nil { - return - } - - // Remove non-interface routes. - for _, r := range joinInfo.StaticRoutes { - if err := osSbox.RemoveStaticRoute(r); err != nil { - logrus.Debugf("Remove route failed: %v", err) - } - } -} - -func (sb *sandbox) releaseOSSbox() { - sb.Lock() - osSbox := sb.osSbox - sb.osSbox = nil - sb.Unlock() - - if osSbox == nil { - return - } - - for _, ep := range sb.getConnectedEndpoints() { - releaseOSSboxResources(osSbox, ep) - } - - osSbox.Destroy() -} - -func (sb *sandbox) restoreOslSandbox() error { - var routes []*types.StaticRoute - - // restore osl sandbox - Ifaces := make(map[string][]osl.IfaceOption) - for _, ep := range sb.endpoints { - var ifaceOptions []osl.IfaceOption - ep.Lock() - joinInfo := ep.joinInfo - i := ep.iface - ep.Unlock() - - if i == nil { - logrus.Errorf("error restoring endpoint %s for container %s", ep.Name(), sb.ContainerID()) - continue - } - - ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes)) - if i.addrv6 != nil && i.addrv6.IP.To16() != nil { - ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(i.addrv6)) - } - if i.mac != nil { - ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().MacAddress(i.mac)) - } - if len(i.llAddrs) != 0 { - ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().LinkLocalAddresses(i.llAddrs)) - } - Ifaces[fmt.Sprintf("%s+%s", i.srcName, i.dstPrefix)] = ifaceOptions - if joinInfo != nil { - routes = append(routes, joinInfo.StaticRoutes...) - } - if ep.needResolver() { - sb.startResolver(true) - } - } - - gwep := sb.getGatewayEndpoint() - if gwep == nil { - return nil - } - - // restore osl sandbox - err := sb.osSbox.Restore(Ifaces, routes, gwep.joinInfo.gw, gwep.joinInfo.gw6) - return err -} - -func (sb *sandbox) populateNetworkResources(ep *endpoint) error { - sb.Lock() - if sb.osSbox == nil { - sb.Unlock() - return nil - } - inDelete := sb.inDelete - sb.Unlock() - - ep.Lock() - joinInfo := ep.joinInfo - i := ep.iface - lbModeIsDSR := ep.network.loadBalancerMode == loadBalancerModeDSR - ep.Unlock() - - if ep.needResolver() { - sb.startResolver(false) - } - - if i != nil && i.srcName != "" { - var ifaceOptions []osl.IfaceOption - - ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes)) - if i.addrv6 != nil && i.addrv6.IP.To16() != nil { - ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(i.addrv6)) - } - if len(i.llAddrs) != 0 { - ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().LinkLocalAddresses(i.llAddrs)) - } - if i.mac != nil { - ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().MacAddress(i.mac)) - } - - if err := sb.osSbox.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil { - return fmt.Errorf("failed to add interface %s to sandbox: %v", i.srcName, err) - } - - if len(ep.virtualIP) > 0 && lbModeIsDSR { - if sb.loadBalancerNID == "" { - if err := sb.osSbox.DisableARPForVIP(i.srcName); err != nil { - return fmt.Errorf("failed disable ARP for VIP: %v", err) - } - } - ipNet := &net.IPNet{IP: ep.virtualIP, Mask: net.CIDRMask(32, 32)} - if err := sb.osSbox.AddAliasIP(sb.osSbox.GetLoopbackIfaceName(), ipNet); err != nil { - return fmt.Errorf("failed to add virtual ip %v to loopback: %v", ipNet, err) - } - } - } - - if joinInfo != nil { - // Set up non-interface routes. - for _, r := range joinInfo.StaticRoutes { - if err := sb.osSbox.AddStaticRoute(r); err != nil { - return fmt.Errorf("failed to add static route %s: %v", r.Destination.String(), err) - } - } - } - - if ep == sb.getGatewayEndpoint() { - if err := sb.updateGateway(ep); err != nil { - return err - } - } - - // Make sure to add the endpoint to the populated endpoint set - // before populating loadbalancers. - sb.Lock() - sb.populatedEndpoints[ep.ID()] = struct{}{} - sb.Unlock() - - // Populate load balancer only after updating all the other - // information including gateway and other routes so that - // loadbalancers are populated all the network state is in - // place in the sandbox. - sb.populateLoadBalancers(ep) - - // Only update the store if we did not come here as part of - // sandbox delete. If we came here as part of delete then do - // not bother updating the store. The sandbox object will be - // deleted anyway - if !inDelete { - return sb.storeUpdate() - } - - return nil -} - -func (sb *sandbox) clearNetworkResources(origEp *endpoint) error { - ep := sb.getEndpoint(origEp.id) - if ep == nil { - return fmt.Errorf("could not find the sandbox endpoint data for endpoint %s", - origEp.id) - } - - sb.Lock() - osSbox := sb.osSbox - inDelete := sb.inDelete - sb.Unlock() - if osSbox != nil { - releaseOSSboxResources(osSbox, ep) - } - - sb.Lock() - delete(sb.populatedEndpoints, ep.ID()) - - if len(sb.endpoints) == 0 { - // sb.endpoints should never be empty and this is unexpected error condition - // We log an error message to note this down for debugging purposes. - logrus.Errorf("No endpoints in sandbox while trying to remove endpoint %s", ep.Name()) - sb.Unlock() - return nil - } - - var ( - gwepBefore, gwepAfter *endpoint - index = -1 - ) - for i, e := range sb.endpoints { - if e == ep { - index = i - } - if len(e.Gateway()) > 0 && gwepBefore == nil { - gwepBefore = e - } - if index != -1 && gwepBefore != nil { - break - } - } - - if index == -1 { - logrus.Warnf("Endpoint %s has already been deleted", ep.Name()) - sb.Unlock() - return nil - } - - sb.removeEndpointRaw(ep) - for _, e := range sb.endpoints { - if len(e.Gateway()) > 0 { - gwepAfter = e - break - } - } - delete(sb.epPriority, ep.ID()) - sb.Unlock() - - if gwepAfter != nil && gwepBefore != gwepAfter { - sb.updateGateway(gwepAfter) - } - - // Only update the store if we did not come here as part of - // sandbox delete. If we came here as part of delete then do - // not bother updating the store. The sandbox object will be - // deleted anyway - if !inDelete { - return sb.storeUpdate() - } - - return nil -} - -func (sb *sandbox) isEndpointPopulated(ep *endpoint) bool { - sb.Lock() - _, ok := sb.populatedEndpoints[ep.ID()] - sb.Unlock() - return ok -} - -// joinLeaveStart waits to ensure there are no joins or leaves in progress and -// marks this join/leave in progress without race -func (sb *sandbox) joinLeaveStart() { - sb.Lock() - defer sb.Unlock() - - for sb.joinLeaveDone != nil { - joinLeaveDone := sb.joinLeaveDone - sb.Unlock() - - <-joinLeaveDone - - sb.Lock() - } - - sb.joinLeaveDone = make(chan struct{}) -} - -// joinLeaveEnd marks the end of this join/leave operation and -// signals the same without race to other join and leave waiters -func (sb *sandbox) joinLeaveEnd() { - sb.Lock() - defer sb.Unlock() - - if sb.joinLeaveDone != nil { - close(sb.joinLeaveDone) - sb.joinLeaveDone = nil - } -} - -func (sb *sandbox) hasPortConfigs() bool { - opts := sb.Labels() - _, hasExpPorts := opts[netlabel.ExposedPorts] - _, hasPortMaps := opts[netlabel.PortMap] - return hasExpPorts || hasPortMaps -} - -// OptionHostname function returns an option setter for hostname option to -// be passed to NewSandbox method. -func OptionHostname(name string) SandboxOption { - return func(sb *sandbox) { - sb.config.hostName = name - } -} - -// OptionDomainname function returns an option setter for domainname option to -// be passed to NewSandbox method. -func OptionDomainname(name string) SandboxOption { - return func(sb *sandbox) { - sb.config.domainName = name - } -} - -// OptionHostsPath function returns an option setter for hostspath option to -// be passed to NewSandbox method. -func OptionHostsPath(path string) SandboxOption { - return func(sb *sandbox) { - sb.config.hostsPath = path - } -} - -// OptionOriginHostsPath function returns an option setter for origin hosts file path -// to be passed to NewSandbox method. -func OptionOriginHostsPath(path string) SandboxOption { - return func(sb *sandbox) { - sb.config.originHostsPath = path - } -} - -// OptionExtraHost function returns an option setter for extra /etc/hosts options -// which is a name and IP as strings. -func OptionExtraHost(name string, IP string) SandboxOption { - return func(sb *sandbox) { - sb.config.extraHosts = append(sb.config.extraHosts, extraHost{name: name, IP: IP}) - } -} - -// OptionParentUpdate function returns an option setter for parent container -// which needs to update the IP address for the linked container. -func OptionParentUpdate(cid string, name, ip string) SandboxOption { - return func(sb *sandbox) { - sb.config.parentUpdates = append(sb.config.parentUpdates, parentUpdate{cid: cid, name: name, ip: ip}) - } -} - -// OptionResolvConfPath function returns an option setter for resolvconfpath option to -// be passed to net container methods. -func OptionResolvConfPath(path string) SandboxOption { - return func(sb *sandbox) { - sb.config.resolvConfPath = path - } -} - -// OptionOriginResolvConfPath function returns an option setter to set the path to the -// origin resolv.conf file to be passed to net container methods. -func OptionOriginResolvConfPath(path string) SandboxOption { - return func(sb *sandbox) { - sb.config.originResolvConfPath = path - } -} - -// OptionDNS function returns an option setter for dns entry option to -// be passed to container Create method. -func OptionDNS(dns string) SandboxOption { - return func(sb *sandbox) { - sb.config.dnsList = append(sb.config.dnsList, dns) - } -} - -// OptionDNSSearch function returns an option setter for dns search entry option to -// be passed to container Create method. -func OptionDNSSearch(search string) SandboxOption { - return func(sb *sandbox) { - sb.config.dnsSearchList = append(sb.config.dnsSearchList, search) - } -} - -// OptionDNSOptions function returns an option setter for dns options entry option to -// be passed to container Create method. -func OptionDNSOptions(options string) SandboxOption { - return func(sb *sandbox) { - sb.config.dnsOptionsList = append(sb.config.dnsOptionsList, options) - } -} - -// OptionUseDefaultSandbox function returns an option setter for using default sandbox -// (host namespace) to be passed to container Create method. -func OptionUseDefaultSandbox() SandboxOption { - return func(sb *sandbox) { - sb.config.useDefaultSandBox = true - } -} - -// OptionUseExternalKey function returns an option setter for using provided namespace -// instead of creating one. -func OptionUseExternalKey() SandboxOption { - return func(sb *sandbox) { - sb.config.useExternalKey = true - } -} - -// OptionGeneric function returns an option setter for Generic configuration -// that is not managed by libNetwork but can be used by the Drivers during the call to -// net container creation method. Container Labels are a good example. -func OptionGeneric(generic map[string]interface{}) SandboxOption { - return func(sb *sandbox) { - if sb.config.generic == nil { - sb.config.generic = make(map[string]interface{}, len(generic)) - } - for k, v := range generic { - sb.config.generic[k] = v - } - } -} - -// OptionExposedPorts function returns an option setter for the container exposed -// ports option to be passed to container Create method. -func OptionExposedPorts(exposedPorts []types.TransportPort) SandboxOption { - return func(sb *sandbox) { - if sb.config.generic == nil { - sb.config.generic = make(map[string]interface{}) - } - // Defensive copy - eps := make([]types.TransportPort, len(exposedPorts)) - copy(eps, exposedPorts) - // Store endpoint label and in generic because driver needs it - sb.config.exposedPorts = eps - sb.config.generic[netlabel.ExposedPorts] = eps - } -} - -// OptionPortMapping function returns an option setter for the mapping -// ports option to be passed to container Create method. -func OptionPortMapping(portBindings []types.PortBinding) SandboxOption { - return func(sb *sandbox) { - if sb.config.generic == nil { - sb.config.generic = make(map[string]interface{}) - } - // Store a copy of the bindings as generic data to pass to the driver - pbs := make([]types.PortBinding, len(portBindings)) - copy(pbs, portBindings) - sb.config.generic[netlabel.PortMap] = pbs - } -} - -// OptionIngress function returns an option setter for marking a -// sandbox as the controller's ingress sandbox. -func OptionIngress() SandboxOption { - return func(sb *sandbox) { - sb.ingress = true - sb.oslTypes = append(sb.oslTypes, osl.SandboxTypeIngress) - } -} - -// OptionLoadBalancer function returns an option setter for marking a -// sandbox as a load balancer sandbox. -func OptionLoadBalancer(nid string) SandboxOption { - return func(sb *sandbox) { - sb.loadBalancerNID = nid - sb.oslTypes = append(sb.oslTypes, osl.SandboxTypeLoadBalancer) - } -} - -// <=> Returns true if a < b, false if a > b and advances to next level if a == b -// epi.prio <=> epj.prio # 2 < 1 -// epi.gw <=> epj.gw # non-gw < gw -// epi.internal <=> epj.internal # non-internal < internal -// epi.joininfo <=> epj.joininfo # ipv6 < ipv4 -// epi.name <=> epj.name # bar < foo -func (epi *endpoint) Less(epj *endpoint) bool { - var ( - prioi, prioj int - ) - - sbi, _ := epi.getSandbox() - sbj, _ := epj.getSandbox() - - // Prio defaults to 0 - if sbi != nil { - prioi = sbi.epPriority[epi.ID()] - } - if sbj != nil { - prioj = sbj.epPriority[epj.ID()] - } - - if prioi != prioj { - return prioi > prioj - } - - gwi := epi.endpointInGWNetwork() - gwj := epj.endpointInGWNetwork() - if gwi != gwj { - return gwj - } - - inti := epi.getNetwork().Internal() - intj := epj.getNetwork().Internal() - if inti != intj { - return intj - } - - jii := 0 - if epi.joinInfo != nil { - if epi.joinInfo.gw != nil { - jii = jii + 1 - } - if epi.joinInfo.gw6 != nil { - jii = jii + 2 - } - } - - jij := 0 - if epj.joinInfo != nil { - if epj.joinInfo.gw != nil { - jij = jij + 1 - } - if epj.joinInfo.gw6 != nil { - jij = jij + 2 - } - } - - if jii != jij { - return jii > jij - } - - return epi.network.Name() < epj.network.Name() -} - -func (sb *sandbox) NdotsSet() bool { - return sb.ndotsSet -} diff --git a/vendor/github.com/docker/libnetwork/sandbox_dns_windows.go b/vendor/github.com/docker/libnetwork/sandbox_dns_windows.go deleted file mode 100644 index e1ca73edefa43..0000000000000 --- a/vendor/github.com/docker/libnetwork/sandbox_dns_windows.go +++ /dev/null @@ -1,35 +0,0 @@ -// +build windows - -package libnetwork - -import ( - "github.com/docker/libnetwork/etchosts" -) - -// Stub implementations for DNS related functions - -func (sb *sandbox) startResolver(bool) { -} - -func (sb *sandbox) setupResolutionFiles() error { - return nil -} - -func (sb *sandbox) restorePath() { -} - -func (sb *sandbox) updateHostsFile(ifaceIP string) error { - return nil -} - -func (sb *sandbox) addHostsEntries(recs []etchosts.Record) { - -} - -func (sb *sandbox) deleteHostsEntries(recs []etchosts.Record) { - -} - -func (sb *sandbox) updateDNS(ipv6Enabled bool) error { - return nil -} diff --git a/vendor/github.com/docker/libnetwork/service.go b/vendor/github.com/docker/libnetwork/service.go deleted file mode 100644 index 5ed11959a10f9..0000000000000 --- a/vendor/github.com/docker/libnetwork/service.go +++ /dev/null @@ -1,98 +0,0 @@ -package libnetwork - -import ( - "fmt" - "net" - "sync" - - "github.com/docker/libnetwork/internal/setmatrix" -) - -var ( - // A global monotonic counter to assign firewall marks to - // services. - fwMarkCtr uint32 = 256 - fwMarkCtrMu sync.Mutex -) - -type portConfigs []*PortConfig - -func (p portConfigs) String() string { - if len(p) == 0 { - return "" - } - - pc := p[0] - str := fmt.Sprintf("%d:%d/%s", pc.PublishedPort, pc.TargetPort, PortConfig_Protocol_name[int32(pc.Protocol)]) - for _, pc := range p[1:] { - str = str + fmt.Sprintf(",%d:%d/%s", pc.PublishedPort, pc.TargetPort, PortConfig_Protocol_name[int32(pc.Protocol)]) - } - - return str -} - -type serviceKey struct { - id string - ports string -} - -type service struct { - name string // Service Name - id string // Service ID - - // Map of loadbalancers for the service one-per attached - // network. It is keyed with network ID. - loadBalancers map[string]*loadBalancer - - // List of ingress ports exposed by the service - ingressPorts portConfigs - - // Service aliases - aliases []string - - // This maps tracks for each IP address the list of endpoints ID - // associated with it. At stable state the endpoint ID expected is 1 - // but during transition and service change it is possible to have - // temporary more than 1 - ipToEndpoint setmatrix.SetMatrix - - deleted bool - - sync.Mutex -} - -// assignIPToEndpoint inserts the mapping between the IP and the endpoint identifier -// returns true if the mapping was not present, false otherwise -// returns also the number of endpoints associated to the IP -func (s *service) assignIPToEndpoint(ip, eID string) (bool, int) { - return s.ipToEndpoint.Insert(ip, eID) -} - -// removeIPToEndpoint removes the mapping between the IP and the endpoint identifier -// returns true if the mapping was deleted, false otherwise -// returns also the number of endpoints associated to the IP -func (s *service) removeIPToEndpoint(ip, eID string) (bool, int) { - return s.ipToEndpoint.Remove(ip, eID) -} - -func (s *service) printIPToEndpoint(ip string) (string, bool) { - return s.ipToEndpoint.String(ip) -} - -type lbBackend struct { - ip net.IP - disabled bool -} - -type loadBalancer struct { - vip net.IP - fwMark uint32 - - // Map of backend IPs backing this loadbalancer on this - // network. It is keyed with endpoint ID. - backEnds map[string]*lbBackend - - // Back pointer to service to which the loadbalancer belongs. - service *service - sync.Mutex -} diff --git a/vendor/github.com/docker/libnetwork/store.go b/vendor/github.com/docker/libnetwork/store.go deleted file mode 100644 index 0a7c5754d31de..0000000000000 --- a/vendor/github.com/docker/libnetwork/store.go +++ /dev/null @@ -1,496 +0,0 @@ -package libnetwork - -import ( - "fmt" - "strings" - - "github.com/docker/libkv/store/boltdb" - "github.com/docker/libkv/store/consul" - "github.com/docker/libkv/store/etcd" - "github.com/docker/libkv/store/zookeeper" - "github.com/docker/libnetwork/datastore" - "github.com/sirupsen/logrus" -) - -func registerKVStores() { - consul.Register() - zookeeper.Register() - etcd.Register() - boltdb.Register() -} - -func (c *controller) initScopedStore(scope string, scfg *datastore.ScopeCfg) error { - store, err := datastore.NewDataStore(scope, scfg) - if err != nil { - return err - } - c.Lock() - c.stores = append(c.stores, store) - c.Unlock() - - return nil -} - -func (c *controller) initStores() error { - registerKVStores() - - c.Lock() - if c.cfg == nil { - c.Unlock() - return nil - } - scopeConfigs := c.cfg.Scopes - c.stores = nil - c.Unlock() - - for scope, scfg := range scopeConfigs { - if err := c.initScopedStore(scope, scfg); err != nil { - return err - } - } - - c.startWatch() - return nil -} - -func (c *controller) closeStores() { - for _, store := range c.getStores() { - store.Close() - } -} - -func (c *controller) getStore(scope string) datastore.DataStore { - c.Lock() - defer c.Unlock() - - for _, store := range c.stores { - if store.Scope() == scope { - return store - } - } - - return nil -} - -func (c *controller) getStores() []datastore.DataStore { - c.Lock() - defer c.Unlock() - - return c.stores -} - -func (c *controller) getNetworkFromStore(nid string) (*network, error) { - for _, store := range c.getStores() { - n := &network{id: nid, ctrlr: c} - err := store.GetObject(datastore.Key(n.Key()...), n) - // Continue searching in the next store if the key is not found in this store - if err != nil { - if err != datastore.ErrKeyNotFound { - logrus.Debugf("could not find network %s: %v", nid, err) - } - continue - } - - ec := &endpointCnt{n: n} - err = store.GetObject(datastore.Key(ec.Key()...), ec) - if err != nil && !n.inDelete { - return nil, fmt.Errorf("could not find endpoint count for network %s: %v", n.Name(), err) - } - - n.epCnt = ec - if n.scope == "" { - n.scope = store.Scope() - } - return n, nil - } - - return nil, fmt.Errorf("network %s not found", nid) -} - -func (c *controller) getNetworksForScope(scope string) ([]*network, error) { - var nl []*network - - store := c.getStore(scope) - if store == nil { - return nil, nil - } - - kvol, err := store.List(datastore.Key(datastore.NetworkKeyPrefix), - &network{ctrlr: c}) - if err != nil && err != datastore.ErrKeyNotFound { - return nil, fmt.Errorf("failed to get networks for scope %s: %v", - scope, err) - } - - for _, kvo := range kvol { - n := kvo.(*network) - n.ctrlr = c - - ec := &endpointCnt{n: n} - err = store.GetObject(datastore.Key(ec.Key()...), ec) - if err != nil && !n.inDelete { - logrus.Warnf("Could not find endpoint count key %s for network %s while listing: %v", datastore.Key(ec.Key()...), n.Name(), err) - continue - } - - n.epCnt = ec - if n.scope == "" { - n.scope = scope - } - nl = append(nl, n) - } - - return nl, nil -} - -func (c *controller) getNetworksFromStore() ([]*network, error) { - var nl []*network - - for _, store := range c.getStores() { - kvol, err := store.List(datastore.Key(datastore.NetworkKeyPrefix), - &network{ctrlr: c}) - // Continue searching in the next store if no keys found in this store - if err != nil { - if err != datastore.ErrKeyNotFound { - logrus.Debugf("failed to get networks for scope %s: %v", store.Scope(), err) - } - continue - } - - kvep, err := store.Map(datastore.Key(epCntKeyPrefix), &endpointCnt{}) - if err != nil { - if err != datastore.ErrKeyNotFound { - logrus.Warnf("failed to get endpoint_count map for scope %s: %v", store.Scope(), err) - } - } - - for _, kvo := range kvol { - n := kvo.(*network) - n.Lock() - n.ctrlr = c - ec := &endpointCnt{n: n} - // Trim the leading & trailing "/" to make it consistent across all stores - if val, ok := kvep[strings.Trim(datastore.Key(ec.Key()...), "/")]; ok { - ec = val.(*endpointCnt) - ec.n = n - n.epCnt = ec - } - if n.scope == "" { - n.scope = store.Scope() - } - n.Unlock() - nl = append(nl, n) - } - } - - return nl, nil -} - -func (n *network) getEndpointFromStore(eid string) (*endpoint, error) { - var errors []string - for _, store := range n.ctrlr.getStores() { - ep := &endpoint{id: eid, network: n} - err := store.GetObject(datastore.Key(ep.Key()...), ep) - // Continue searching in the next store if the key is not found in this store - if err != nil { - if err != datastore.ErrKeyNotFound { - errors = append(errors, fmt.Sprintf("{%s:%v}, ", store.Scope(), err)) - logrus.Debugf("could not find endpoint %s in %s: %v", eid, store.Scope(), err) - } - continue - } - return ep, nil - } - return nil, fmt.Errorf("could not find endpoint %s: %v", eid, errors) -} - -func (n *network) getEndpointsFromStore() ([]*endpoint, error) { - var epl []*endpoint - - tmp := endpoint{network: n} - for _, store := range n.getController().getStores() { - kvol, err := store.List(datastore.Key(tmp.KeyPrefix()...), &endpoint{network: n}) - // Continue searching in the next store if no keys found in this store - if err != nil { - if err != datastore.ErrKeyNotFound { - logrus.Debugf("failed to get endpoints for network %s scope %s: %v", - n.Name(), store.Scope(), err) - } - continue - } - - for _, kvo := range kvol { - ep := kvo.(*endpoint) - epl = append(epl, ep) - } - } - - return epl, nil -} - -func (c *controller) updateToStore(kvObject datastore.KVObject) error { - cs := c.getStore(kvObject.DataScope()) - if cs == nil { - return ErrDataStoreNotInitialized(kvObject.DataScope()) - } - - if err := cs.PutObjectAtomic(kvObject); err != nil { - if err == datastore.ErrKeyModified { - return err - } - return fmt.Errorf("failed to update store for object type %T: %v", kvObject, err) - } - - return nil -} - -func (c *controller) deleteFromStore(kvObject datastore.KVObject) error { - cs := c.getStore(kvObject.DataScope()) - if cs == nil { - return ErrDataStoreNotInitialized(kvObject.DataScope()) - } - -retry: - if err := cs.DeleteObjectAtomic(kvObject); err != nil { - if err == datastore.ErrKeyModified { - if err := cs.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil { - return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err) - } - logrus.Warnf("Error (%v) deleting object %v, retrying....", err, kvObject.Key()) - goto retry - } - return err - } - - return nil -} - -type netWatch struct { - localEps map[string]*endpoint - remoteEps map[string]*endpoint - stopCh chan struct{} -} - -func (c *controller) getLocalEps(nw *netWatch) []*endpoint { - c.Lock() - defer c.Unlock() - - var epl []*endpoint - for _, ep := range nw.localEps { - epl = append(epl, ep) - } - - return epl -} - -func (c *controller) watchSvcRecord(ep *endpoint) { - c.watchCh <- ep -} - -func (c *controller) unWatchSvcRecord(ep *endpoint) { - c.unWatchCh <- ep -} - -func (c *controller) networkWatchLoop(nw *netWatch, ep *endpoint, ecCh <-chan datastore.KVObject) { - for { - select { - case <-nw.stopCh: - return - case o := <-ecCh: - ec := o.(*endpointCnt) - - epl, err := ec.n.getEndpointsFromStore() - if err != nil { - break - } - - c.Lock() - var addEp []*endpoint - - delEpMap := make(map[string]*endpoint) - renameEpMap := make(map[string]bool) - for k, v := range nw.remoteEps { - delEpMap[k] = v - } - - for _, lEp := range epl { - if _, ok := nw.localEps[lEp.ID()]; ok { - continue - } - - if ep, ok := nw.remoteEps[lEp.ID()]; ok { - // On a container rename EP ID will remain - // the same but the name will change. service - // records should reflect the change. - // Keep old EP entry in the delEpMap and add - // EP from the store (which has the new name) - // into the new list - if lEp.name == ep.name { - delete(delEpMap, lEp.ID()) - continue - } - renameEpMap[lEp.ID()] = true - } - nw.remoteEps[lEp.ID()] = lEp - addEp = append(addEp, lEp) - } - - // EPs whose name are to be deleted from the svc records - // should also be removed from nw's remote EP list, except - // the ones that are getting renamed. - for _, lEp := range delEpMap { - if !renameEpMap[lEp.ID()] { - delete(nw.remoteEps, lEp.ID()) - } - } - c.Unlock() - - for _, lEp := range delEpMap { - ep.getNetwork().updateSvcRecord(lEp, c.getLocalEps(nw), false) - - } - for _, lEp := range addEp { - ep.getNetwork().updateSvcRecord(lEp, c.getLocalEps(nw), true) - } - } - } -} - -func (c *controller) processEndpointCreate(nmap map[string]*netWatch, ep *endpoint) { - n := ep.getNetwork() - if !c.isDistributedControl() && n.Scope() == datastore.SwarmScope && n.driverIsMultihost() { - return - } - - c.Lock() - nw, ok := nmap[n.ID()] - c.Unlock() - - if ok { - // Update the svc db for the local endpoint join right away - n.updateSvcRecord(ep, c.getLocalEps(nw), true) - - c.Lock() - nw.localEps[ep.ID()] = ep - - // If we had learned that from the kv store remove it - // from remote ep list now that we know that this is - // indeed a local endpoint - delete(nw.remoteEps, ep.ID()) - c.Unlock() - return - } - - nw = &netWatch{ - localEps: make(map[string]*endpoint), - remoteEps: make(map[string]*endpoint), - } - - // Update the svc db for the local endpoint join right away - // Do this before adding this ep to localEps so that we don't - // try to update this ep's container's svc records - n.updateSvcRecord(ep, c.getLocalEps(nw), true) - - c.Lock() - nw.localEps[ep.ID()] = ep - nmap[n.ID()] = nw - nw.stopCh = make(chan struct{}) - c.Unlock() - - store := c.getStore(n.DataScope()) - if store == nil { - return - } - - if !store.Watchable() { - return - } - - ch, err := store.Watch(n.getEpCnt(), nw.stopCh) - if err != nil { - logrus.Warnf("Error creating watch for network: %v", err) - return - } - - go c.networkWatchLoop(nw, ep, ch) -} - -func (c *controller) processEndpointDelete(nmap map[string]*netWatch, ep *endpoint) { - n := ep.getNetwork() - if !c.isDistributedControl() && n.Scope() == datastore.SwarmScope && n.driverIsMultihost() { - return - } - - c.Lock() - nw, ok := nmap[n.ID()] - - if ok { - delete(nw.localEps, ep.ID()) - c.Unlock() - - // Update the svc db about local endpoint leave right away - // Do this after we remove this ep from localEps so that we - // don't try to remove this svc record from this ep's container. - n.updateSvcRecord(ep, c.getLocalEps(nw), false) - - c.Lock() - if len(nw.localEps) == 0 { - close(nw.stopCh) - - // This is the last container going away for the network. Destroy - // this network's svc db entry - delete(c.svcRecords, n.ID()) - - delete(nmap, n.ID()) - } - } - c.Unlock() -} - -func (c *controller) watchLoop() { - for { - select { - case ep := <-c.watchCh: - c.processEndpointCreate(c.nmap, ep) - case ep := <-c.unWatchCh: - c.processEndpointDelete(c.nmap, ep) - } - } -} - -func (c *controller) startWatch() { - if c.watchCh != nil { - return - } - c.watchCh = make(chan *endpoint) - c.unWatchCh = make(chan *endpoint) - c.nmap = make(map[string]*netWatch) - - go c.watchLoop() -} - -func (c *controller) networkCleanup() { - networks, err := c.getNetworksFromStore() - if err != nil { - logrus.Warnf("Could not retrieve networks from store(s) during network cleanup: %v", err) - return - } - - for _, n := range networks { - if n.inDelete { - logrus.Infof("Removing stale network %s (%s)", n.Name(), n.ID()) - if err := n.delete(true, true); err != nil { - logrus.Debugf("Error while removing stale network: %v", err) - } - } - } -} - -var populateSpecial NetworkWalker = func(nw Network) bool { - if n := nw.(*network); n.hasSpecialDriver() && !n.ConfigOnly() { - if err := n.getController().addNetwork(n); err != nil { - logrus.Warnf("Failed to populate network %q with driver %q", nw.Name(), nw.Type()) - } - } - return false -} diff --git a/vendor/github.com/docker/libnetwork/types/types.go b/vendor/github.com/docker/libnetwork/types/types.go deleted file mode 100644 index b102ba4c390be..0000000000000 --- a/vendor/github.com/docker/libnetwork/types/types.go +++ /dev/null @@ -1,653 +0,0 @@ -// Package types contains types that are common across libnetwork project -package types - -import ( - "bytes" - "fmt" - "net" - "strconv" - "strings" - - "github.com/ishidawataru/sctp" -) - -// constants for the IP address type -const ( - IP = iota // IPv4 and IPv6 - IPv4 - IPv6 -) - -// EncryptionKey is the libnetwork representation of the key distributed by the lead -// manager. -type EncryptionKey struct { - Subsystem string - Algorithm int32 - Key []byte - LamportTime uint64 -} - -// UUID represents a globally unique ID of various resources like network and endpoint -type UUID string - -// QosPolicy represents a quality of service policy on an endpoint -type QosPolicy struct { - MaxEgressBandwidth uint64 -} - -// TransportPort represents a local Layer 4 endpoint -type TransportPort struct { - Proto Protocol - Port uint16 -} - -// Equal checks if this instance of Transportport is equal to the passed one -func (t *TransportPort) Equal(o *TransportPort) bool { - if t == o { - return true - } - - if o == nil { - return false - } - - if t.Proto != o.Proto || t.Port != o.Port { - return false - } - - return true -} - -// GetCopy returns a copy of this TransportPort structure instance -func (t *TransportPort) GetCopy() TransportPort { - return TransportPort{Proto: t.Proto, Port: t.Port} -} - -// String returns the TransportPort structure in string form -func (t *TransportPort) String() string { - return fmt.Sprintf("%s/%d", t.Proto.String(), t.Port) -} - -// FromString reads the TransportPort structure from string -func (t *TransportPort) FromString(s string) error { - ps := strings.Split(s, "/") - if len(ps) == 2 { - t.Proto = ParseProtocol(ps[0]) - if p, err := strconv.ParseUint(ps[1], 10, 16); err == nil { - t.Port = uint16(p) - return nil - } - } - return BadRequestErrorf("invalid format for transport port: %s", s) -} - -// PortBinding represents a port binding between the container and the host -type PortBinding struct { - Proto Protocol - IP net.IP - Port uint16 - HostIP net.IP - HostPort uint16 - HostPortEnd uint16 -} - -// HostAddr returns the host side transport address -func (p PortBinding) HostAddr() (net.Addr, error) { - switch p.Proto { - case UDP: - return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil - case TCP: - return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil - case SCTP: - return &sctp.SCTPAddr{IP: []net.IP{p.HostIP}, Port: int(p.HostPort)}, nil - default: - return nil, ErrInvalidProtocolBinding(p.Proto.String()) - } -} - -// ContainerAddr returns the container side transport address -func (p PortBinding) ContainerAddr() (net.Addr, error) { - switch p.Proto { - case UDP: - return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil - case TCP: - return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil - case SCTP: - return &sctp.SCTPAddr{IP: []net.IP{p.IP}, Port: int(p.Port)}, nil - default: - return nil, ErrInvalidProtocolBinding(p.Proto.String()) - } -} - -// GetCopy returns a copy of this PortBinding structure instance -func (p *PortBinding) GetCopy() PortBinding { - return PortBinding{ - Proto: p.Proto, - IP: GetIPCopy(p.IP), - Port: p.Port, - HostIP: GetIPCopy(p.HostIP), - HostPort: p.HostPort, - HostPortEnd: p.HostPortEnd, - } -} - -// String returns the PortBinding structure in string form -func (p *PortBinding) String() string { - ret := fmt.Sprintf("%s/", p.Proto) - if p.IP != nil { - ret += p.IP.String() - } - ret = fmt.Sprintf("%s:%d/", ret, p.Port) - if p.HostIP != nil { - ret += p.HostIP.String() - } - ret = fmt.Sprintf("%s:%d", ret, p.HostPort) - return ret -} - -// FromString reads the PortBinding structure from string s. -// String s is a triple of "protocol/containerIP:port/hostIP:port" -// containerIP and hostIP can be in dotted decimal ("192.0.2.1") or IPv6 ("2001:db8::68") form. -// Zoned addresses ("169.254.0.23%eth0" or "fe80::1ff:fe23:4567:890a%eth0") are not supported. -// If string s is incorrectly formatted or the IP addresses or ports cannot be parsed, FromString -// returns an error. -func (p *PortBinding) FromString(s string) error { - ps := strings.Split(s, "/") - if len(ps) != 3 { - return BadRequestErrorf("invalid format for port binding: %s", s) - } - - p.Proto = ParseProtocol(ps[0]) - - var err error - if p.IP, p.Port, err = parseIPPort(ps[1]); err != nil { - return BadRequestErrorf("failed to parse Container IP/Port in port binding: %s", err.Error()) - } - - if p.HostIP, p.HostPort, err = parseIPPort(ps[2]); err != nil { - return BadRequestErrorf("failed to parse Host IP/Port in port binding: %s", err.Error()) - } - - return nil -} - -func parseIPPort(s string) (net.IP, uint16, error) { - hoststr, portstr, err := net.SplitHostPort(s) - if err != nil { - return nil, 0, err - } - - ip := net.ParseIP(hoststr) - if ip == nil { - return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr) - } - - port, err := strconv.ParseUint(portstr, 10, 16) - if err != nil { - return nil, 0, BadRequestErrorf("invalid port: %s", portstr) - } - - return ip, uint16(port), nil -} - -// Equal checks if this instance of PortBinding is equal to the passed one -func (p *PortBinding) Equal(o *PortBinding) bool { - if p == o { - return true - } - - if o == nil { - return false - } - - if p.Proto != o.Proto || p.Port != o.Port || - p.HostPort != o.HostPort || p.HostPortEnd != o.HostPortEnd { - return false - } - - if p.IP != nil { - if !p.IP.Equal(o.IP) { - return false - } - } else { - if o.IP != nil { - return false - } - } - - if p.HostIP != nil { - if !p.HostIP.Equal(o.HostIP) { - return false - } - } else { - if o.HostIP != nil { - return false - } - } - - return true -} - -// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid. -type ErrInvalidProtocolBinding string - -func (ipb ErrInvalidProtocolBinding) Error() string { - return fmt.Sprintf("invalid transport protocol: %s", string(ipb)) -} - -const ( - // ICMP is for the ICMP ip protocol - ICMP = 1 - // TCP is for the TCP ip protocol - TCP = 6 - // UDP is for the UDP ip protocol - UDP = 17 - // SCTP is for the SCTP ip protocol - SCTP = 132 -) - -// Protocol represents an IP protocol number -type Protocol uint8 - -func (p Protocol) String() string { - switch p { - case ICMP: - return "icmp" - case TCP: - return "tcp" - case UDP: - return "udp" - case SCTP: - return "sctp" - default: - return fmt.Sprintf("%d", p) - } -} - -// ParseProtocol returns the respective Protocol type for the passed string -func ParseProtocol(s string) Protocol { - switch strings.ToLower(s) { - case "icmp": - return ICMP - case "udp": - return UDP - case "tcp": - return TCP - case "sctp": - return SCTP - default: - return 0 - } -} - -// GetMacCopy returns a copy of the passed MAC address -func GetMacCopy(from net.HardwareAddr) net.HardwareAddr { - if from == nil { - return nil - } - to := make(net.HardwareAddr, len(from)) - copy(to, from) - return to -} - -// GetIPCopy returns a copy of the passed IP address -func GetIPCopy(from net.IP) net.IP { - if from == nil { - return nil - } - to := make(net.IP, len(from)) - copy(to, from) - return to -} - -// GetIPNetCopy returns a copy of the passed IP Network -func GetIPNetCopy(from *net.IPNet) *net.IPNet { - if from == nil { - return nil - } - bm := make(net.IPMask, len(from.Mask)) - copy(bm, from.Mask) - return &net.IPNet{IP: GetIPCopy(from.IP), Mask: bm} -} - -// GetIPNetCanonical returns the canonical form for the passed network -func GetIPNetCanonical(nw *net.IPNet) *net.IPNet { - if nw == nil { - return nil - } - c := GetIPNetCopy(nw) - c.IP = c.IP.Mask(nw.Mask) - return c -} - -// CompareIPNet returns equal if the two IP Networks are equal -func CompareIPNet(a, b *net.IPNet) bool { - if a == b { - return true - } - if a == nil || b == nil { - return false - } - return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask) -} - -// GetMinimalIP returns the address in its shortest form -// If ip contains an IPv4-mapped IPv6 address, the 4-octet form of the IPv4 address will be returned. -// Otherwise ip is returned unchanged. -func GetMinimalIP(ip net.IP) net.IP { - if ip != nil && ip.To4() != nil { - return ip.To4() - } - return ip -} - -// GetMinimalIPNet returns a copy of the passed IP Network with congruent ip and mask notation -func GetMinimalIPNet(nw *net.IPNet) *net.IPNet { - if nw == nil { - return nil - } - if len(nw.IP) == 16 && nw.IP.To4() != nil { - m := nw.Mask - if len(m) == 16 { - m = m[12:16] - } - return &net.IPNet{IP: nw.IP.To4(), Mask: m} - } - return nw -} - -// IsIPNetValid returns true if the ipnet is a valid network/mask -// combination. Otherwise returns false. -func IsIPNetValid(nw *net.IPNet) bool { - return nw.String() != "0.0.0.0/0" -} - -var v4inV6MaskPrefix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - -// compareIPMask checks if the passed ip and mask are semantically compatible. -// It returns the byte indexes for the address and mask so that caller can -// do bitwise operations without modifying address representation. -func compareIPMask(ip net.IP, mask net.IPMask) (is int, ms int, err error) { - // Find the effective starting of address and mask - if len(ip) == net.IPv6len && ip.To4() != nil { - is = 12 - } - if len(ip[is:]) == net.IPv4len && len(mask) == net.IPv6len && bytes.Equal(mask[:12], v4inV6MaskPrefix) { - ms = 12 - } - // Check if address and mask are semantically compatible - if len(ip[is:]) != len(mask[ms:]) { - err = fmt.Errorf("ip and mask are not compatible: (%#v, %#v)", ip, mask) - } - return -} - -// GetHostPartIP returns the host portion of the ip address identified by the mask. -// IP address representation is not modified. If address and mask are not compatible -// an error is returned. -func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) { - // Find the effective starting of address and mask - is, ms, err := compareIPMask(ip, mask) - if err != nil { - return nil, fmt.Errorf("cannot compute host portion ip address because %s", err) - } - - // Compute host portion - out := GetIPCopy(ip) - for i := 0; i < len(mask[ms:]); i++ { - out[is+i] &= ^mask[ms+i] - } - - return out, nil -} - -// GetBroadcastIP returns the broadcast ip address for the passed network (ip and mask). -// IP address representation is not modified. If address and mask are not compatible -// an error is returned. -func GetBroadcastIP(ip net.IP, mask net.IPMask) (net.IP, error) { - // Find the effective starting of address and mask - is, ms, err := compareIPMask(ip, mask) - if err != nil { - return nil, fmt.Errorf("cannot compute broadcast ip address because %s", err) - } - - // Compute broadcast address - out := GetIPCopy(ip) - for i := 0; i < len(mask[ms:]); i++ { - out[is+i] |= ^mask[ms+i] - } - - return out, nil -} - -// ParseCIDR returns the *net.IPNet represented by the passed CIDR notation -func ParseCIDR(cidr string) (n *net.IPNet, e error) { - var i net.IP - if i, n, e = net.ParseCIDR(cidr); e == nil { - n.IP = i - } - return -} - -const ( - // NEXTHOP indicates a StaticRoute with an IP next hop. - NEXTHOP = iota - - // CONNECTED indicates a StaticRoute with an interface for directly connected peers. - CONNECTED -) - -// StaticRoute is a statically-provisioned IP route. -type StaticRoute struct { - Destination *net.IPNet - - RouteType int // NEXT_HOP or CONNECTED - - // NextHop will be resolved by the kernel (i.e. as a loose hop). - NextHop net.IP -} - -// GetCopy returns a copy of this StaticRoute structure -func (r *StaticRoute) GetCopy() *StaticRoute { - d := GetIPNetCopy(r.Destination) - nh := GetIPCopy(r.NextHop) - return &StaticRoute{Destination: d, - RouteType: r.RouteType, - NextHop: nh, - } -} - -// InterfaceStatistics represents the interface's statistics -type InterfaceStatistics struct { - RxBytes uint64 - RxPackets uint64 - RxErrors uint64 - RxDropped uint64 - TxBytes uint64 - TxPackets uint64 - TxErrors uint64 - TxDropped uint64 -} - -func (is *InterfaceStatistics) String() string { - return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d", - is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped) -} - -/****************************** - * Well-known Error Interfaces - ******************************/ - -// MaskableError is an interface for errors which can be ignored by caller -type MaskableError interface { - // Maskable makes implementer into MaskableError type - Maskable() -} - -// RetryError is an interface for errors which might get resolved through retry -type RetryError interface { - // Retry makes implementer into RetryError type - Retry() -} - -// BadRequestError is an interface for errors originated by a bad request -type BadRequestError interface { - // BadRequest makes implementer into BadRequestError type - BadRequest() -} - -// NotFoundError is an interface for errors raised because a needed resource is not available -type NotFoundError interface { - // NotFound makes implementer into NotFoundError type - NotFound() -} - -// ForbiddenError is an interface for errors which denote a valid request that cannot be honored -type ForbiddenError interface { - // Forbidden makes implementer into ForbiddenError type - Forbidden() -} - -// NoServiceError is an interface for errors returned when the required service is not available -type NoServiceError interface { - // NoService makes implementer into NoServiceError type - NoService() -} - -// TimeoutError is an interface for errors raised because of timeout -type TimeoutError interface { - // Timeout makes implementer into TimeoutError type - Timeout() -} - -// NotImplementedError is an interface for errors raised because of requested functionality is not yet implemented -type NotImplementedError interface { - // NotImplemented makes implementer into NotImplementedError type - NotImplemented() -} - -// InternalError is an interface for errors raised because of an internal error -type InternalError interface { - // Internal makes implementer into InternalError type - Internal() -} - -/****************************** - * Well-known Error Formatters - ******************************/ - -// BadRequestErrorf creates an instance of BadRequestError -func BadRequestErrorf(format string, params ...interface{}) error { - return badRequest(fmt.Sprintf(format, params...)) -} - -// NotFoundErrorf creates an instance of NotFoundError -func NotFoundErrorf(format string, params ...interface{}) error { - return notFound(fmt.Sprintf(format, params...)) -} - -// ForbiddenErrorf creates an instance of ForbiddenError -func ForbiddenErrorf(format string, params ...interface{}) error { - return forbidden(fmt.Sprintf(format, params...)) -} - -// NoServiceErrorf creates an instance of NoServiceError -func NoServiceErrorf(format string, params ...interface{}) error { - return noService(fmt.Sprintf(format, params...)) -} - -// NotImplementedErrorf creates an instance of NotImplementedError -func NotImplementedErrorf(format string, params ...interface{}) error { - return notImpl(fmt.Sprintf(format, params...)) -} - -// TimeoutErrorf creates an instance of TimeoutError -func TimeoutErrorf(format string, params ...interface{}) error { - return timeout(fmt.Sprintf(format, params...)) -} - -// InternalErrorf creates an instance of InternalError -func InternalErrorf(format string, params ...interface{}) error { - return internal(fmt.Sprintf(format, params...)) -} - -// InternalMaskableErrorf creates an instance of InternalError and MaskableError -func InternalMaskableErrorf(format string, params ...interface{}) error { - return maskInternal(fmt.Sprintf(format, params...)) -} - -// RetryErrorf creates an instance of RetryError -func RetryErrorf(format string, params ...interface{}) error { - return retry(fmt.Sprintf(format, params...)) -} - -/*********************** - * Internal Error Types - ***********************/ -type badRequest string - -func (br badRequest) Error() string { - return string(br) -} -func (br badRequest) BadRequest() {} - -type maskBadRequest string - -type notFound string - -func (nf notFound) Error() string { - return string(nf) -} -func (nf notFound) NotFound() {} - -type forbidden string - -func (frb forbidden) Error() string { - return string(frb) -} -func (frb forbidden) Forbidden() {} - -type noService string - -func (ns noService) Error() string { - return string(ns) -} -func (ns noService) NoService() {} - -type maskNoService string - -type timeout string - -func (to timeout) Error() string { - return string(to) -} -func (to timeout) Timeout() {} - -type notImpl string - -func (ni notImpl) Error() string { - return string(ni) -} -func (ni notImpl) NotImplemented() {} - -type internal string - -func (nt internal) Error() string { - return string(nt) -} -func (nt internal) Internal() {} - -type maskInternal string - -func (mnt maskInternal) Error() string { - return string(mnt) -} -func (mnt maskInternal) Internal() {} -func (mnt maskInternal) Maskable() {} - -type retry string - -func (r retry) Error() string { - return string(r) -} -func (r retry) Retry() {} diff --git a/vendor/github.com/docker/libnetwork/vendor.conf b/vendor/github.com/docker/libnetwork/vendor.conf deleted file mode 100644 index f2bb55223e3fe..0000000000000 --- a/vendor/github.com/docker/libnetwork/vendor.conf +++ /dev/null @@ -1,50 +0,0 @@ -github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 -github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 -github.com/Microsoft/go-winio v0.4.11 -github.com/Microsoft/hcsshim v0.7.3 -github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec -github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 -github.com/codegangsta/cli a65b733b303f0055f8d324d805f393cd3e7a7904 -github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b -github.com/coreos/etcd v3.2.1 -github.com/coreos/go-semver v0.2.0 -github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d -go.etcd.io/bbolt v1.3.1-etcd.8 - -github.com/docker/docker 162ba6016def672690ee4a1f3978368853a1e149 -github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6 -github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 -github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1 -github.com/docker/libkv 458977154600b9f23984d9f4b82e79570b5ae12b - -github.com/godbus/dbus v4.0.0 -github.com/gogo/protobuf v1.0.0 -github.com/gorilla/context v1.1 -github.com/gorilla/mux v1.1 -github.com/hashicorp/consul v0.5.2 -github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b -github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e -github.com/hashicorp/memberlist 3d8438da9589e7b608a83ffac1ef8211486bcb7c -github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372 -github.com/hashicorp/go-sockaddr 6d291a969b86c4b633730bfc6b8b9d64c3aafed9 -github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870 -github.com/mattn/go-shellwords v1.0.3 -github.com/miekg/dns v1.0.7 -github.com/opencontainers/go-digest v1.0.0-rc1 -github.com/opencontainers/image-spec v1.0.1 -github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340 -github.com/opencontainers/runtime-spec v1.0.1 -github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374 -github.com/sirupsen/logrus v1.0.3 -github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065 -github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e -github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25 -golang.org/x/crypto 1a580b3eff7814fc9b40602fd35256c63b50f491 -golang.org/x/net 0ed95abb35c445290478a5348a7b38bb154135fd -golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd -golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5 -github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9 -github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb - -gotest.tools v2.1.0 -github.com/google/go-cmp v0.2.0 diff --git a/vendor/github.com/docker/swarmkit/agent/agent.go b/vendor/github.com/docker/swarmkit/agent/agent.go index 4d8f99cfcde07..8f45f183cc379 100644 --- a/vendor/github.com/docker/swarmkit/agent/agent.go +++ b/vendor/github.com/docker/swarmkit/agent/agent.go @@ -2,6 +2,7 @@ package agent import ( "bytes" + "context" "math/rand" "reflect" "sync" @@ -11,7 +12,6 @@ import ( "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/log" "github.com/pkg/errors" - "golang.org/x/net/context" ) const ( @@ -205,6 +205,11 @@ func (a *Agent) run(ctx context.Context) { sessionq chan sessionOperation leaving = a.leaving subscriptions = map[string]context.CancelFunc{} + // subscriptionDone is a channel that allows us to notify ourselves + // that a lot subscription should be finished. this channel is + // unbuffered, because it is only written to in a goroutine, and + // therefore cannot block the main execution path. + subscriptionDone = make(chan string) ) defer func() { session.close() @@ -310,8 +315,26 @@ func (a *Agent) run(ctx context.Context) { subCtx, subCancel := context.WithCancel(ctx) subscriptions[sub.ID] = subCancel - // TODO(dperny) we're tossing the error here, that seems wrong - go a.worker.Subscribe(subCtx, sub) + // NOTE(dperny): for like 3 years, there has been a to do saying + // "we're tossing the error here, that seems wrong". this is not a + // to do anymore. 9/10 of these errors are going to be "context + // deadline exceeded", and the remaining 1/10 obviously doesn't + // matter or we'd have missed it by now. + go func() { + a.worker.Subscribe(subCtx, sub) + // when the worker finishes the subscription, we should notify + // ourselves that this has occurred. We cannot rely on getting + // a Close message from the manager, as any number of things + // could go wrong (see github.com/moby/moby/issues/39916). + subscriptionDone <- sub.ID + }() + case subID := <-subscriptionDone: + // subscription may already have been removed. If so, no need to + // take any action. + if cancel, ok := subscriptions[subID]; ok { + cancel() + delete(subscriptions, subID) + } case <-registered: log.G(ctx).Debugln("agent: registered") if ready != nil { @@ -548,8 +571,9 @@ func (a *Agent) Publisher(ctx context.Context, subscriptionID string) (exec.LogP SubscriptionID: subscriptionID, Close: true, }) - // close the stream forreal - publisher.CloseSend() + // close the stream forreal. ignore the return value and the error, + // because we don't care. + publisher.CloseAndRecv() } return exec.LogPublisherFunc(func(ctx context.Context, message api.LogMessage) error { @@ -575,7 +599,7 @@ func (a *Agent) nodeDescriptionWithHostname(ctx context.Context, tlsInfo *api.No // Override hostname and TLS info if desc != nil { - if a.config.Hostname != "" && desc != nil { + if a.config.Hostname != "" { desc.Hostname = a.config.Hostname } desc.TLSInfo = tlsInfo diff --git a/vendor/github.com/docker/swarmkit/agent/errors.go b/vendor/github.com/docker/swarmkit/agent/errors.go index 29f8ff1c9f913..f5514d8311145 100644 --- a/vendor/github.com/docker/swarmkit/agent/errors.go +++ b/vendor/github.com/docker/swarmkit/agent/errors.go @@ -13,10 +13,5 @@ var ( errAgentStarted = errors.New("agent: already started") errAgentNotStarted = errors.New("agent: not started") - errTaskNoController = errors.New("agent: no task controller") - errTaskNotAssigned = errors.New("agent: task not assigned") - errTaskStatusUpdateNoChange = errors.New("agent: no change in task status") - errTaskUnknown = errors.New("agent: task unknown") - - errTaskInvalid = errors.New("task: invalid") + errTaskUnknown = errors.New("agent: task unknown") ) diff --git a/vendor/github.com/docker/swarmkit/agent/exec/controller.go b/vendor/github.com/docker/swarmkit/agent/exec/controller.go index c9e9343fd7f1e..b617d37ca35b1 100644 --- a/vendor/github.com/docker/swarmkit/agent/exec/controller.go +++ b/vendor/github.com/docker/swarmkit/agent/exec/controller.go @@ -1,6 +1,7 @@ package exec import ( + "context" "fmt" "time" @@ -10,7 +11,6 @@ import ( "github.com/docker/swarmkit/protobuf/ptypes" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "golang.org/x/net/context" ) // Controller controls execution of a task. diff --git a/vendor/github.com/docker/swarmkit/agent/exec/controller_stub.go b/vendor/github.com/docker/swarmkit/agent/exec/controller_stub.go index 076955ff80663..dd16ce457d1df 100644 --- a/vendor/github.com/docker/swarmkit/agent/exec/controller_stub.go +++ b/vendor/github.com/docker/swarmkit/agent/exec/controller_stub.go @@ -1,10 +1,11 @@ package exec import ( - "github.com/docker/swarmkit/api" - "golang.org/x/net/context" + "context" "runtime" "strings" + + "github.com/docker/swarmkit/api" ) // StubController implements the Controller interface, diff --git a/vendor/github.com/docker/swarmkit/agent/exec/executor.go b/vendor/github.com/docker/swarmkit/agent/exec/executor.go index 8c3fd035064a6..26c1bfcba7005 100644 --- a/vendor/github.com/docker/swarmkit/agent/exec/executor.go +++ b/vendor/github.com/docker/swarmkit/agent/exec/executor.go @@ -1,8 +1,9 @@ package exec import ( + "context" + "github.com/docker/swarmkit/api" - "golang.org/x/net/context" ) // Executor provides controllers for tasks. diff --git a/vendor/github.com/docker/swarmkit/agent/helpers.go b/vendor/github.com/docker/swarmkit/agent/helpers.go index 5fdf1660594cd..5e95d93297a71 100644 --- a/vendor/github.com/docker/swarmkit/agent/helpers.go +++ b/vendor/github.com/docker/swarmkit/agent/helpers.go @@ -1,6 +1,6 @@ package agent -import "golang.org/x/net/context" +import "context" // runctx blocks until the function exits, closed is closed, or the context is // cancelled. Call as part of go statement. diff --git a/vendor/github.com/docker/swarmkit/agent/reporter.go b/vendor/github.com/docker/swarmkit/agent/reporter.go index 73e6ab3fd921b..2afb75795fe80 100644 --- a/vendor/github.com/docker/swarmkit/agent/reporter.go +++ b/vendor/github.com/docker/swarmkit/agent/reporter.go @@ -1,12 +1,12 @@ package agent import ( + "context" "reflect" "sync" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/log" - "golang.org/x/net/context" ) // StatusReporter receives updates to task status. Method may be called diff --git a/vendor/github.com/docker/swarmkit/agent/resource.go b/vendor/github.com/docker/swarmkit/agent/resource.go index 8e88d2cd656eb..32be069c115cd 100644 --- a/vendor/github.com/docker/swarmkit/agent/resource.go +++ b/vendor/github.com/docker/swarmkit/agent/resource.go @@ -1,8 +1,9 @@ package agent import ( + "context" + "github.com/docker/swarmkit/api" - "golang.org/x/net/context" ) type resourceAllocator struct { diff --git a/vendor/github.com/docker/swarmkit/agent/secrets/secrets.go b/vendor/github.com/docker/swarmkit/agent/secrets/secrets.go index 233101d0fb423..fb0ee1d3b9d9e 100644 --- a/vendor/github.com/docker/swarmkit/agent/secrets/secrets.go +++ b/vendor/github.com/docker/swarmkit/agent/secrets/secrets.go @@ -6,6 +6,7 @@ import ( "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/identity" ) // secrets is a map that keeps all the currently available secrets to the agent @@ -62,6 +63,7 @@ func (s *secrets) Reset() { type taskRestrictedSecretsProvider struct { secrets exec.SecretGetter secretIDs map[string]struct{} // allow list of secret ids + taskID string // ID of the task the provider restricts for } func (sp *taskRestrictedSecretsProvider) Get(secretID string) (*api.Secret, error) { @@ -69,7 +71,18 @@ func (sp *taskRestrictedSecretsProvider) Get(secretID string) (*api.Secret, erro return nil, fmt.Errorf("task not authorized to access secret %s", secretID) } - return sp.secrets.Get(secretID) + // First check if the secret is available with the task specific ID, which is the concatenation + // of the original secret ID and the task ID with a dot in between. + // That is the case when a secret driver has returned DoNotReuse == true for a secret value. + taskSpecificID := identity.CombineTwoIDs(secretID, sp.taskID) + secret, err := sp.secrets.Get(taskSpecificID) + if err != nil { + // Otherwise, which is the default case, the secret is retrieved by its original ID. + return sp.secrets.Get(secretID) + } + // For all intents and purposes, the rest of the flow should deal with the original secret ID. + secret.ID = secretID + return secret, err } // Restrict provides a getter that only allows access to the secrets @@ -84,5 +97,5 @@ func Restrict(secrets exec.SecretGetter, t *api.Task) exec.SecretGetter { } } - return &taskRestrictedSecretsProvider{secrets: secrets, secretIDs: sids} + return &taskRestrictedSecretsProvider{secrets: secrets, secretIDs: sids, taskID: t.ID} } diff --git a/vendor/github.com/docker/swarmkit/agent/session.go b/vendor/github.com/docker/swarmkit/agent/session.go index 8c01d08fdd6d7..2e7f1b6a37081 100644 --- a/vendor/github.com/docker/swarmkit/agent/session.go +++ b/vendor/github.com/docker/swarmkit/agent/session.go @@ -1,7 +1,9 @@ package agent import ( + "context" "errors" + "math" "sync" "time" @@ -9,7 +11,6 @@ import ( "github.com/docker/swarmkit/connectionbroker" "github.com/docker/swarmkit/log" "github.com/sirupsen/logrus" - "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -17,7 +18,6 @@ import ( var ( dispatcherRPCTimeout = 5 * time.Second - errSessionDisconnect = errors.New("agent: session disconnect") // instructed to disconnect errSessionClosed = errors.New("agent: session closed") ) @@ -65,6 +65,7 @@ func newSession(ctx context.Context, agent *Agent, delay time.Duration, sessionI cc, err := agent.config.ConnBroker.Select( grpc.WithTransportCredentials(agent.config.Credentials), grpc.WithTimeout(dispatcherRPCTimeout), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32)), ) if err != nil { @@ -137,7 +138,7 @@ func (s *session) start(ctx context.Context, description *api.NodeDescription) e // `ctx` is done and hence fail to propagate the timeout error to the agent. // If the error is not propogated to the agent, the agent will not close // the session or rebuild a new session. - sessionCtx, cancelSession := context.WithCancel(ctx) + sessionCtx, cancelSession := context.WithCancel(ctx) //nolint:govet // Need to run Session in a goroutine since there's no way to set a // timeout for an individual Recv call in a stream. @@ -160,7 +161,7 @@ func (s *session) start(ctx context.Context, description *api.NodeDescription) e select { case err := <-errChan: if err != nil { - return err + return err //nolint:govet } case <-time.After(dispatcherRPCTimeout): cancelSession() diff --git a/vendor/github.com/docker/swarmkit/agent/storage.go b/vendor/github.com/docker/swarmkit/agent/storage.go index 519880197f2a6..8d32ebf1ba8ac 100644 --- a/vendor/github.com/docker/swarmkit/agent/storage.go +++ b/vendor/github.com/docker/swarmkit/agent/storage.go @@ -131,7 +131,9 @@ func PutTask(tx *bolt.Tx, task *api.Task) error { // PutTaskStatus updates the status for the task with id. func PutTaskStatus(tx *bolt.Tx, id string, status *api.TaskStatus) error { - return withCreateTaskBucketIfNotExists(tx, id, func(bkt *bolt.Bucket) error { + // this used to be withCreateTaskBucketIfNotExists, but that could lead + // to weird race conditions, and was not necessary. + return withTaskBucket(tx, id, func(bkt *bolt.Bucket) error { p, err := proto.Marshal(status) if err != nil { return err diff --git a/vendor/github.com/docker/swarmkit/agent/task.go b/vendor/github.com/docker/swarmkit/agent/task.go index 95fe93179bfd6..17c713c092603 100644 --- a/vendor/github.com/docker/swarmkit/agent/task.go +++ b/vendor/github.com/docker/swarmkit/agent/task.go @@ -1,6 +1,7 @@ package agent import ( + "context" "sync" "time" @@ -8,7 +9,6 @@ import ( "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/api/equality" "github.com/docker/swarmkit/log" - "golang.org/x/net/context" ) // taskManager manages all aspects of task execution and reporting for an agent diff --git a/vendor/github.com/docker/swarmkit/agent/worker.go b/vendor/github.com/docker/swarmkit/agent/worker.go index 8125145d213c6..53d88dfe1fd10 100644 --- a/vendor/github.com/docker/swarmkit/agent/worker.go +++ b/vendor/github.com/docker/swarmkit/agent/worker.go @@ -1,6 +1,7 @@ package agent import ( + "context" "sync" "github.com/docker/swarmkit/agent/exec" @@ -9,7 +10,6 @@ import ( "github.com/docker/swarmkit/watch" "github.com/sirupsen/logrus" bolt "go.etcd.io/bbolt" - "golang.org/x/net/context" ) // Worker implements the core task management logic and persistence. It @@ -278,10 +278,15 @@ func reconcileTaskState(ctx context.Context, w *worker, assignments []*api.Assig removeTaskAssignment := func(taskID string) error { ctx := log.WithLogger(ctx, log.G(ctx).WithField("task.id", taskID)) - if err := SetTaskAssignment(tx, taskID, false); err != nil { - log.G(ctx).WithError(err).Error("error setting task assignment in database") + // if a task is no longer assigned, then we do not have to keep track + // of it. a task will only be unassigned when it is deleted on the + // manager. instead of SetTaskAssginment to true, we'll just remove the + // task now. + if err := DeleteTask(tx, taskID); err != nil { + log.G(ctx).WithError(err).Error("error removing de-assigned task") + return err } - return err + return nil } // If this was a complete set of assignments, we're going to remove all the remaining @@ -500,6 +505,21 @@ func (w *worker) newTaskManager(ctx context.Context, tx *bolt.Tx, task *api.Task // updateTaskStatus reports statuses to listeners, read lock must be held. func (w *worker) updateTaskStatus(ctx context.Context, tx *bolt.Tx, taskID string, status *api.TaskStatus) error { if err := PutTaskStatus(tx, taskID, status); err != nil { + // we shouldn't fail to put a task status. however, there exists the + // possibility of a race in which we try to put a task status after the + // task has been deleted. because this whole contraption is a careful + // dance of too-tightly-coupled concurrent parts, fixing tht race is + // fraught with hazards. instead, we'll recognize that it can occur, + // log the error, and then ignore it. + if err == errTaskUnknown { + // log at info level. debug logging in docker is already really + // verbose, so many people disable it. the race that causes this + // behavior should be very rare, but if it occurs, we should know + // about it, because if there is some case where it is _not_ rare, + // then knowing about it will go a long way toward debugging. + log.G(ctx).Info("attempted to update status for a task that has been removed") + return nil + } log.G(ctx).WithError(err).Error("failed writing status to disk") return err } diff --git a/vendor/github.com/docker/swarmkit/api/ca.pb.go b/vendor/github.com/docker/swarmkit/api/ca.pb.go index 23d375f95ec93..e37dbc97a2bd1 100644 --- a/vendor/github.com/docker/swarmkit/api/ca.pb.go +++ b/vendor/github.com/docker/swarmkit/api/ca.pb.go @@ -1,248 +1,28 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/docker/swarmkit/api/ca.proto -/* - Package api is a generated protocol buffer package. - - It is generated from these files: - github.com/docker/swarmkit/api/ca.proto - github.com/docker/swarmkit/api/control.proto - github.com/docker/swarmkit/api/dispatcher.proto - github.com/docker/swarmkit/api/health.proto - github.com/docker/swarmkit/api/logbroker.proto - github.com/docker/swarmkit/api/objects.proto - github.com/docker/swarmkit/api/raft.proto - github.com/docker/swarmkit/api/resource.proto - github.com/docker/swarmkit/api/snapshot.proto - github.com/docker/swarmkit/api/specs.proto - github.com/docker/swarmkit/api/types.proto - github.com/docker/swarmkit/api/watch.proto - - It has these top-level messages: - NodeCertificateStatusRequest - NodeCertificateStatusResponse - IssueNodeCertificateRequest - IssueNodeCertificateResponse - GetRootCACertificateRequest - GetRootCACertificateResponse - GetUnlockKeyRequest - GetUnlockKeyResponse - GetNodeRequest - GetNodeResponse - ListNodesRequest - ListNodesResponse - UpdateNodeRequest - UpdateNodeResponse - RemoveNodeRequest - RemoveNodeResponse - GetTaskRequest - GetTaskResponse - RemoveTaskRequest - RemoveTaskResponse - ListTasksRequest - ListTasksResponse - CreateServiceRequest - CreateServiceResponse - GetServiceRequest - GetServiceResponse - UpdateServiceRequest - UpdateServiceResponse - RemoveServiceRequest - RemoveServiceResponse - ListServicesRequest - ListServicesResponse - CreateNetworkRequest - CreateNetworkResponse - GetNetworkRequest - GetNetworkResponse - RemoveNetworkRequest - RemoveNetworkResponse - ListNetworksRequest - ListNetworksResponse - GetClusterRequest - GetClusterResponse - ListClustersRequest - ListClustersResponse - KeyRotation - UpdateClusterRequest - UpdateClusterResponse - GetSecretRequest - GetSecretResponse - UpdateSecretRequest - UpdateSecretResponse - ListSecretsRequest - ListSecretsResponse - CreateSecretRequest - CreateSecretResponse - RemoveSecretRequest - RemoveSecretResponse - GetConfigRequest - GetConfigResponse - UpdateConfigRequest - UpdateConfigResponse - ListConfigsRequest - ListConfigsResponse - CreateConfigRequest - CreateConfigResponse - RemoveConfigRequest - RemoveConfigResponse - SessionRequest - SessionMessage - HeartbeatRequest - HeartbeatResponse - UpdateTaskStatusRequest - UpdateTaskStatusResponse - TasksRequest - TasksMessage - AssignmentsRequest - Assignment - AssignmentChange - AssignmentsMessage - HealthCheckRequest - HealthCheckResponse - LogSubscriptionOptions - LogSelector - LogContext - LogAttr - LogMessage - SubscribeLogsRequest - SubscribeLogsMessage - ListenSubscriptionsRequest - SubscriptionMessage - PublishLogsMessage - PublishLogsResponse - Meta - Node - Service - Endpoint - Task - NetworkAttachment - Network - Cluster - Secret - Config - Resource - Extension - RaftMember - JoinRequest - JoinResponse - LeaveRequest - LeaveResponse - ProcessRaftMessageRequest - ProcessRaftMessageResponse - StreamRaftMessageRequest - StreamRaftMessageResponse - ResolveAddressRequest - ResolveAddressResponse - InternalRaftRequest - StoreAction - AttachNetworkRequest - AttachNetworkResponse - DetachNetworkRequest - DetachNetworkResponse - StoreSnapshot - ClusterSnapshot - Snapshot - NodeSpec - ServiceSpec - ReplicatedService - GlobalService - TaskSpec - ResourceReference - GenericRuntimeSpec - NetworkAttachmentSpec - ContainerSpec - EndpointSpec - NetworkSpec - ClusterSpec - SecretSpec - ConfigSpec - Version - IndexEntry - Annotations - NamedGenericResource - DiscreteGenericResource - GenericResource - Resources - ResourceRequirements - Platform - PluginDescription - EngineDescription - NodeDescription - NodeTLSInfo - RaftMemberStatus - NodeStatus - Image - Mount - RestartPolicy - UpdateConfig - UpdateStatus - ContainerStatus - PortStatus - TaskStatus - NetworkAttachmentConfig - IPAMConfig - PortConfig - Driver - IPAMOptions - Peer - WeightedPeer - IssuanceStatus - AcceptancePolicy - ExternalCA - CAConfig - OrchestrationConfig - TaskDefaults - DispatcherConfig - RaftConfig - EncryptionConfig - SpreadOver - PlacementPreference - Placement - JoinTokens - RootCA - Certificate - EncryptionKey - ManagerStatus - FileTarget - SecretReference - ConfigReference - BlacklistedCertificate - HealthConfig - MaybeEncryptedRecord - RootRotation - Privileges - Object - SelectBySlot - SelectByCustom - SelectBy - WatchRequest - WatchMessage -*/ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" -import _ "github.com/docker/swarmkit/protobuf/plugin" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import raftselector "github.com/docker/swarmkit/manager/raftselector" -import codes "google.golang.org/grpc/codes" -import status "google.golang.org/grpc/status" -import metadata "google.golang.org/grpc/metadata" -import peer "google.golang.org/grpc/peer" -import rafttime "time" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + context "context" + fmt "fmt" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + raftselector "github.com/docker/swarmkit/manager/raftselector" + _ "github.com/docker/swarmkit/protobuf/plugin" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + metadata "google.golang.org/grpc/metadata" + peer "google.golang.org/grpc/peer" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + rafttime "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -253,28 +33,84 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type NodeCertificateStatusRequest struct { NodeID string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` } -func (m *NodeCertificateStatusRequest) Reset() { *m = NodeCertificateStatusRequest{} } -func (*NodeCertificateStatusRequest) ProtoMessage() {} -func (*NodeCertificateStatusRequest) Descriptor() ([]byte, []int) { return fileDescriptorCa, []int{0} } +func (m *NodeCertificateStatusRequest) Reset() { *m = NodeCertificateStatusRequest{} } +func (*NodeCertificateStatusRequest) ProtoMessage() {} +func (*NodeCertificateStatusRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_64a8db18191af608, []int{0} +} +func (m *NodeCertificateStatusRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NodeCertificateStatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NodeCertificateStatusRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NodeCertificateStatusRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_NodeCertificateStatusRequest.Merge(m, src) +} +func (m *NodeCertificateStatusRequest) XXX_Size() int { + return m.Size() +} +func (m *NodeCertificateStatusRequest) XXX_DiscardUnknown() { + xxx_messageInfo_NodeCertificateStatusRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_NodeCertificateStatusRequest proto.InternalMessageInfo type NodeCertificateStatusResponse struct { - Status *IssuanceStatus `protobuf:"bytes,1,opt,name=status" json:"status,omitempty"` - Certificate *Certificate `protobuf:"bytes,2,opt,name=certificate" json:"certificate,omitempty"` + Status *IssuanceStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Certificate *Certificate `protobuf:"bytes,2,opt,name=certificate,proto3" json:"certificate,omitempty"` } -func (m *NodeCertificateStatusResponse) Reset() { *m = NodeCertificateStatusResponse{} } -func (*NodeCertificateStatusResponse) ProtoMessage() {} -func (*NodeCertificateStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptorCa, []int{1} } +func (m *NodeCertificateStatusResponse) Reset() { *m = NodeCertificateStatusResponse{} } +func (*NodeCertificateStatusResponse) ProtoMessage() {} +func (*NodeCertificateStatusResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_64a8db18191af608, []int{1} +} +func (m *NodeCertificateStatusResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NodeCertificateStatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NodeCertificateStatusResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NodeCertificateStatusResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_NodeCertificateStatusResponse.Merge(m, src) +} +func (m *NodeCertificateStatusResponse) XXX_Size() int { + return m.Size() +} +func (m *NodeCertificateStatusResponse) XXX_DiscardUnknown() { + xxx_messageInfo_NodeCertificateStatusResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_NodeCertificateStatusResponse proto.InternalMessageInfo type IssueNodeCertificateRequest struct { // DEPRECATED: Role is now selected based on which secret is matched. - Role NodeRole `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"` + Role NodeRole `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"` // Deprecated: Do not use. // CSR is the certificate signing request. CSR []byte `protobuf:"bytes,2,opt,name=csr,proto3" json:"csr,omitempty"` // Token represents a user-provided string that is necessary for new @@ -284,49 +120,217 @@ type IssueNodeCertificateRequest struct { Availability NodeSpec_Availability `protobuf:"varint,4,opt,name=availability,proto3,enum=docker.swarmkit.v1.NodeSpec_Availability" json:"availability,omitempty"` } -func (m *IssueNodeCertificateRequest) Reset() { *m = IssueNodeCertificateRequest{} } -func (*IssueNodeCertificateRequest) ProtoMessage() {} -func (*IssueNodeCertificateRequest) Descriptor() ([]byte, []int) { return fileDescriptorCa, []int{2} } +func (m *IssueNodeCertificateRequest) Reset() { *m = IssueNodeCertificateRequest{} } +func (*IssueNodeCertificateRequest) ProtoMessage() {} +func (*IssueNodeCertificateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_64a8db18191af608, []int{2} +} +func (m *IssueNodeCertificateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IssueNodeCertificateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IssueNodeCertificateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *IssueNodeCertificateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_IssueNodeCertificateRequest.Merge(m, src) +} +func (m *IssueNodeCertificateRequest) XXX_Size() int { + return m.Size() +} +func (m *IssueNodeCertificateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_IssueNodeCertificateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_IssueNodeCertificateRequest proto.InternalMessageInfo type IssueNodeCertificateResponse struct { NodeID string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` NodeMembership NodeSpec_Membership `protobuf:"varint,2,opt,name=node_membership,json=nodeMembership,proto3,enum=docker.swarmkit.v1.NodeSpec_Membership" json:"node_membership,omitempty"` } -func (m *IssueNodeCertificateResponse) Reset() { *m = IssueNodeCertificateResponse{} } -func (*IssueNodeCertificateResponse) ProtoMessage() {} -func (*IssueNodeCertificateResponse) Descriptor() ([]byte, []int) { return fileDescriptorCa, []int{3} } +func (m *IssueNodeCertificateResponse) Reset() { *m = IssueNodeCertificateResponse{} } +func (*IssueNodeCertificateResponse) ProtoMessage() {} +func (*IssueNodeCertificateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_64a8db18191af608, []int{3} +} +func (m *IssueNodeCertificateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IssueNodeCertificateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IssueNodeCertificateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *IssueNodeCertificateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_IssueNodeCertificateResponse.Merge(m, src) +} +func (m *IssueNodeCertificateResponse) XXX_Size() int { + return m.Size() +} +func (m *IssueNodeCertificateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_IssueNodeCertificateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_IssueNodeCertificateResponse proto.InternalMessageInfo type GetRootCACertificateRequest struct { } -func (m *GetRootCACertificateRequest) Reset() { *m = GetRootCACertificateRequest{} } -func (*GetRootCACertificateRequest) ProtoMessage() {} -func (*GetRootCACertificateRequest) Descriptor() ([]byte, []int) { return fileDescriptorCa, []int{4} } +func (m *GetRootCACertificateRequest) Reset() { *m = GetRootCACertificateRequest{} } +func (*GetRootCACertificateRequest) ProtoMessage() {} +func (*GetRootCACertificateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_64a8db18191af608, []int{4} +} +func (m *GetRootCACertificateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetRootCACertificateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetRootCACertificateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetRootCACertificateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetRootCACertificateRequest.Merge(m, src) +} +func (m *GetRootCACertificateRequest) XXX_Size() int { + return m.Size() +} +func (m *GetRootCACertificateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetRootCACertificateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetRootCACertificateRequest proto.InternalMessageInfo type GetRootCACertificateResponse struct { Certificate []byte `protobuf:"bytes,1,opt,name=certificate,proto3" json:"certificate,omitempty"` } -func (m *GetRootCACertificateResponse) Reset() { *m = GetRootCACertificateResponse{} } -func (*GetRootCACertificateResponse) ProtoMessage() {} -func (*GetRootCACertificateResponse) Descriptor() ([]byte, []int) { return fileDescriptorCa, []int{5} } +func (m *GetRootCACertificateResponse) Reset() { *m = GetRootCACertificateResponse{} } +func (*GetRootCACertificateResponse) ProtoMessage() {} +func (*GetRootCACertificateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_64a8db18191af608, []int{5} +} +func (m *GetRootCACertificateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetRootCACertificateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetRootCACertificateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetRootCACertificateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetRootCACertificateResponse.Merge(m, src) +} +func (m *GetRootCACertificateResponse) XXX_Size() int { + return m.Size() +} +func (m *GetRootCACertificateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetRootCACertificateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetRootCACertificateResponse proto.InternalMessageInfo type GetUnlockKeyRequest struct { } -func (m *GetUnlockKeyRequest) Reset() { *m = GetUnlockKeyRequest{} } -func (*GetUnlockKeyRequest) ProtoMessage() {} -func (*GetUnlockKeyRequest) Descriptor() ([]byte, []int) { return fileDescriptorCa, []int{6} } +func (m *GetUnlockKeyRequest) Reset() { *m = GetUnlockKeyRequest{} } +func (*GetUnlockKeyRequest) ProtoMessage() {} +func (*GetUnlockKeyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_64a8db18191af608, []int{6} +} +func (m *GetUnlockKeyRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetUnlockKeyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetUnlockKeyRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetUnlockKeyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetUnlockKeyRequest.Merge(m, src) +} +func (m *GetUnlockKeyRequest) XXX_Size() int { + return m.Size() +} +func (m *GetUnlockKeyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetUnlockKeyRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetUnlockKeyRequest proto.InternalMessageInfo type GetUnlockKeyResponse struct { UnlockKey []byte `protobuf:"bytes,1,opt,name=unlock_key,json=unlockKey,proto3" json:"unlock_key,omitempty"` - Version Version `protobuf:"bytes,2,opt,name=version" json:"version"` + Version Version `protobuf:"bytes,2,opt,name=version,proto3" json:"version"` } -func (m *GetUnlockKeyResponse) Reset() { *m = GetUnlockKeyResponse{} } -func (*GetUnlockKeyResponse) ProtoMessage() {} -func (*GetUnlockKeyResponse) Descriptor() ([]byte, []int) { return fileDescriptorCa, []int{7} } +func (m *GetUnlockKeyResponse) Reset() { *m = GetUnlockKeyResponse{} } +func (*GetUnlockKeyResponse) ProtoMessage() {} +func (*GetUnlockKeyResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_64a8db18191af608, []int{7} +} +func (m *GetUnlockKeyResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetUnlockKeyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetUnlockKeyResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetUnlockKeyResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetUnlockKeyResponse.Merge(m, src) +} +func (m *GetUnlockKeyResponse) XXX_Size() int { + return m.Size() +} +func (m *GetUnlockKeyResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetUnlockKeyResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetUnlockKeyResponse proto.InternalMessageInfo func init() { proto.RegisterType((*NodeCertificateStatusRequest)(nil), "docker.swarmkit.v1.NodeCertificateStatusRequest") @@ -339,6 +343,55 @@ func init() { proto.RegisterType((*GetUnlockKeyResponse)(nil), "docker.swarmkit.v1.GetUnlockKeyResponse") } +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/ca.proto", fileDescriptor_64a8db18191af608) +} + +var fileDescriptor_64a8db18191af608 = []byte{ + // 651 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xc1, 0x4f, 0x13, 0x4f, + 0x14, 0xee, 0x2c, 0xfc, 0xca, 0x8f, 0x47, 0x05, 0x33, 0x42, 0x52, 0x4b, 0xd9, 0x92, 0xd5, 0x04, + 0x34, 0x71, 0x17, 0xaa, 0x89, 0x89, 0x5e, 0x6c, 0x6b, 0x42, 0x88, 0xc1, 0x98, 0x21, 0x7a, 0x25, + 0xdb, 0xed, 0x50, 0x26, 0x6d, 0x77, 0xd6, 0x9d, 0x59, 0xb4, 0x37, 0x13, 0x8d, 0x67, 0x2f, 0x46, + 0x4f, 0xfe, 0x09, 0xfe, 0x1d, 0xc4, 0x13, 0x89, 0x17, 0x4e, 0x44, 0x96, 0xbb, 0x7f, 0x83, 0xd9, + 0xd9, 0x5d, 0x69, 0x61, 0x5b, 0xf0, 0xd4, 0x9d, 0xf7, 0xbe, 0xef, 0x7b, 0xef, 0x7d, 0xf3, 0x3a, + 0xb0, 0xd2, 0x66, 0x72, 0x2f, 0x68, 0x9a, 0x0e, 0xef, 0x59, 0x2d, 0xee, 0x74, 0xa8, 0x6f, 0x89, + 0x37, 0xb6, 0xdf, 0xeb, 0x30, 0x69, 0xd9, 0x1e, 0xb3, 0x1c, 0xdb, 0xf4, 0x7c, 0x2e, 0x39, 0xc6, + 0x71, 0xd6, 0x4c, 0xb3, 0xe6, 0xfe, 0x7a, 0xe9, 0xee, 0x25, 0x64, 0xd9, 0xf7, 0xa8, 0x88, 0xf9, + 0x97, 0x62, 0x85, 0x47, 0x9d, 0x14, 0x3b, 0xdf, 0xe6, 0x6d, 0xae, 0x3e, 0xad, 0xe8, 0x2b, 0x89, + 0x3e, 0x1c, 0xa3, 0xa0, 0x10, 0xcd, 0x60, 0xd7, 0xf2, 0xba, 0x41, 0x9b, 0xb9, 0xc9, 0x4f, 0x4c, + 0x34, 0x1a, 0x50, 0x7e, 0xce, 0x5b, 0xb4, 0x41, 0x7d, 0xc9, 0x76, 0x99, 0x63, 0x4b, 0xba, 0x2d, + 0x6d, 0x19, 0x08, 0x42, 0x5f, 0x07, 0x54, 0x48, 0x7c, 0x0b, 0xa6, 0x5c, 0xde, 0xa2, 0x3b, 0xac, + 0x55, 0x44, 0xcb, 0x68, 0x75, 0xba, 0x0e, 0xe1, 0x71, 0x25, 0x1f, 0x51, 0x36, 0x9f, 0x92, 0x7c, + 0x94, 0xda, 0x6c, 0x19, 0xdf, 0x10, 0x2c, 0x8d, 0x50, 0x11, 0x1e, 0x77, 0x05, 0xc5, 0x8f, 0x20, + 0x2f, 0x54, 0x44, 0xa9, 0xcc, 0x54, 0x0d, 0xf3, 0xa2, 0x65, 0xe6, 0xa6, 0x10, 0x81, 0xed, 0x3a, + 0x29, 0x37, 0x61, 0xe0, 0x1a, 0xcc, 0x38, 0x67, 0xc2, 0x45, 0x4d, 0x09, 0x54, 0xb2, 0x04, 0x06, + 0xea, 0x93, 0x41, 0x8e, 0xf1, 0x13, 0xc1, 0x62, 0xa4, 0x4e, 0xcf, 0x75, 0x99, 0x4e, 0xf9, 0x00, + 0x26, 0x7d, 0xde, 0xa5, 0xaa, 0xb9, 0xd9, 0x6a, 0x39, 0x4b, 0x3b, 0x62, 0x12, 0xde, 0xa5, 0x75, + 0xad, 0x88, 0x88, 0x42, 0xe3, 0x9b, 0x30, 0xe1, 0x08, 0x5f, 0x35, 0x54, 0xa8, 0x4f, 0x85, 0xc7, + 0x95, 0x89, 0xc6, 0x36, 0x21, 0x51, 0x0c, 0xcf, 0xc3, 0x7f, 0x92, 0x77, 0xa8, 0x5b, 0x9c, 0x88, + 0x4c, 0x23, 0xf1, 0x01, 0x6f, 0x41, 0xc1, 0xde, 0xb7, 0x59, 0xd7, 0x6e, 0xb2, 0x2e, 0x93, 0xfd, + 0xe2, 0xa4, 0x2a, 0x77, 0x67, 0x54, 0xb9, 0x6d, 0x8f, 0x3a, 0x66, 0x6d, 0x80, 0x40, 0x86, 0xe8, + 0xc6, 0x67, 0x04, 0xe5, 0xec, 0xa9, 0x12, 0xd7, 0xaf, 0x72, 0x79, 0xf8, 0x05, 0xcc, 0x29, 0x50, + 0x8f, 0xf6, 0x9a, 0xd4, 0x17, 0x7b, 0xcc, 0x53, 0x13, 0xcd, 0x56, 0x57, 0xc6, 0xf6, 0xb5, 0xf5, + 0x17, 0x4e, 0x66, 0x23, 0xfe, 0xd9, 0xd9, 0x58, 0x82, 0xc5, 0x0d, 0x2a, 0x09, 0xe7, 0xb2, 0x51, + 0xbb, 0x68, 0xb6, 0xf1, 0x04, 0xca, 0xd9, 0xe9, 0xa4, 0xeb, 0xe5, 0xe1, 0xfb, 0x8e, 0x3a, 0x2f, + 0x0c, 0x5f, 0xe7, 0x02, 0xdc, 0xd8, 0xa0, 0xf2, 0xa5, 0xdb, 0xe5, 0x4e, 0xe7, 0x19, 0xed, 0xa7, + 0xc2, 0x3e, 0xcc, 0x0f, 0x87, 0x13, 0xc1, 0x25, 0x80, 0x40, 0x05, 0x77, 0x3a, 0xb4, 0x9f, 0xe8, + 0x4d, 0x07, 0x29, 0x0c, 0x3f, 0x86, 0xa9, 0x7d, 0xea, 0x0b, 0xc6, 0xdd, 0x64, 0xb7, 0x16, 0xb3, + 0x06, 0x7f, 0x15, 0x43, 0xea, 0x93, 0x07, 0xc7, 0x95, 0x1c, 0x49, 0x19, 0xd5, 0x0f, 0x1a, 0x68, + 0x8d, 0x1a, 0x7e, 0x8f, 0x54, 0xed, 0x0b, 0x43, 0x61, 0x2b, 0x4b, 0x6b, 0x8c, 0x3b, 0xa5, 0xb5, + 0xab, 0x13, 0xe2, 0xf1, 0x8c, 0xff, 0x7f, 0x7c, 0xff, 0xfd, 0x55, 0xd3, 0xae, 0x23, 0xfc, 0x16, + 0x0a, 0x83, 0x06, 0xe0, 0x95, 0x11, 0x5a, 0xe7, 0x9d, 0x2b, 0xad, 0x5e, 0x0e, 0x4c, 0x8a, 0x2d, + 0xa8, 0x62, 0x73, 0x70, 0x4d, 0x21, 0xef, 0xf5, 0x6c, 0xd7, 0x6e, 0x53, 0xbf, 0xfa, 0x45, 0x03, + 0xb5, 0x57, 0x89, 0x15, 0x59, 0x5b, 0x99, 0x6d, 0xc5, 0x98, 0x7f, 0x65, 0xb6, 0x15, 0xe3, 0x16, + 0x7e, 0xc0, 0x8a, 0x8f, 0x08, 0x16, 0x32, 0x9f, 0x24, 0xbc, 0x36, 0x6a, 0xad, 0x47, 0xbd, 0x81, + 0xa5, 0xf5, 0x7f, 0x60, 0x9c, 0x6f, 0xa4, 0x7e, 0xfb, 0xe0, 0x44, 0xcf, 0x1d, 0x9d, 0xe8, 0xb9, + 0x77, 0xa1, 0x8e, 0x0e, 0x42, 0x1d, 0x1d, 0x86, 0x3a, 0xfa, 0x15, 0xea, 0xe8, 0xd3, 0xa9, 0x9e, + 0x3b, 0x3c, 0xd5, 0x73, 0x47, 0xa7, 0x7a, 0xae, 0x99, 0x57, 0xaf, 0xf1, 0xfd, 0x3f, 0x01, 0x00, + 0x00, 0xff, 0xff, 0xd2, 0x82, 0x20, 0x1b, 0x73, 0x06, 0x00, 0x00, +} + type authenticatedWrapperCAServer struct { local CAServer authorize func(context.Context, []string) error @@ -416,11 +469,11 @@ func (m *NodeCertificateStatusResponse) CopyFrom(src interface{}) { *m = *o if o.Status != nil { m.Status = &IssuanceStatus{} - deepcopy.Copy(m.Status, o.Status) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Status, o.Status) } if o.Certificate != nil { m.Certificate = &Certificate{} - deepcopy.Copy(m.Certificate, o.Certificate) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Certificate, o.Certificate) } } @@ -514,7 +567,7 @@ func (m *GetUnlockKeyResponse) CopyFrom(src interface{}) { m.UnlockKey = make([]byte, len(o.UnlockKey)) copy(m.UnlockKey, o.UnlockKey) } - deepcopy.Copy(&m.Version, &o.Version) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Version, &o.Version) } // Reference imports to suppress errors if they are not otherwise used. @@ -525,8 +578,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for CA service - +// CAClient is the client API for CA service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type CAClient interface { GetRootCACertificate(ctx context.Context, in *GetRootCACertificateRequest, opts ...grpc.CallOption) (*GetRootCACertificateResponse, error) // GetUnlockKey returns the current unlock key for the cluster for the role of the client @@ -544,7 +598,7 @@ func NewCAClient(cc *grpc.ClientConn) CAClient { func (c *cAClient) GetRootCACertificate(ctx context.Context, in *GetRootCACertificateRequest, opts ...grpc.CallOption) (*GetRootCACertificateResponse, error) { out := new(GetRootCACertificateResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.CA/GetRootCACertificate", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.CA/GetRootCACertificate", in, out, opts...) if err != nil { return nil, err } @@ -553,15 +607,14 @@ func (c *cAClient) GetRootCACertificate(ctx context.Context, in *GetRootCACertif func (c *cAClient) GetUnlockKey(ctx context.Context, in *GetUnlockKeyRequest, opts ...grpc.CallOption) (*GetUnlockKeyResponse, error) { out := new(GetUnlockKeyResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.CA/GetUnlockKey", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.CA/GetUnlockKey", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for CA service - +// CAServer is the server API for CA service. type CAServer interface { GetRootCACertificate(context.Context, *GetRootCACertificateRequest) (*GetRootCACertificateResponse, error) // GetUnlockKey returns the current unlock key for the cluster for the role of the client @@ -569,6 +622,17 @@ type CAServer interface { GetUnlockKey(context.Context, *GetUnlockKeyRequest) (*GetUnlockKeyResponse, error) } +// UnimplementedCAServer can be embedded to have forward compatible implementations. +type UnimplementedCAServer struct { +} + +func (*UnimplementedCAServer) GetRootCACertificate(ctx context.Context, req *GetRootCACertificateRequest) (*GetRootCACertificateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetRootCACertificate not implemented") +} +func (*UnimplementedCAServer) GetUnlockKey(ctx context.Context, req *GetUnlockKeyRequest) (*GetUnlockKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUnlockKey not implemented") +} + func RegisterCAServer(s *grpc.Server, srv CAServer) { s.RegisterService(&_CA_serviceDesc, srv) } @@ -626,8 +690,9 @@ var _CA_serviceDesc = grpc.ServiceDesc{ Metadata: "github.com/docker/swarmkit/api/ca.proto", } -// Client API for NodeCA service - +// NodeCAClient is the client API for NodeCA service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type NodeCAClient interface { IssueNodeCertificate(ctx context.Context, in *IssueNodeCertificateRequest, opts ...grpc.CallOption) (*IssueNodeCertificateResponse, error) NodeCertificateStatus(ctx context.Context, in *NodeCertificateStatusRequest, opts ...grpc.CallOption) (*NodeCertificateStatusResponse, error) @@ -643,7 +708,7 @@ func NewNodeCAClient(cc *grpc.ClientConn) NodeCAClient { func (c *nodeCAClient) IssueNodeCertificate(ctx context.Context, in *IssueNodeCertificateRequest, opts ...grpc.CallOption) (*IssueNodeCertificateResponse, error) { out := new(IssueNodeCertificateResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.NodeCA/IssueNodeCertificate", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.NodeCA/IssueNodeCertificate", in, out, opts...) if err != nil { return nil, err } @@ -652,20 +717,30 @@ func (c *nodeCAClient) IssueNodeCertificate(ctx context.Context, in *IssueNodeCe func (c *nodeCAClient) NodeCertificateStatus(ctx context.Context, in *NodeCertificateStatusRequest, opts ...grpc.CallOption) (*NodeCertificateStatusResponse, error) { out := new(NodeCertificateStatusResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.NodeCA/NodeCertificateStatus", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.NodeCA/NodeCertificateStatus", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for NodeCA service - +// NodeCAServer is the server API for NodeCA service. type NodeCAServer interface { IssueNodeCertificate(context.Context, *IssueNodeCertificateRequest) (*IssueNodeCertificateResponse, error) NodeCertificateStatus(context.Context, *NodeCertificateStatusRequest) (*NodeCertificateStatusResponse, error) } +// UnimplementedNodeCAServer can be embedded to have forward compatible implementations. +type UnimplementedNodeCAServer struct { +} + +func (*UnimplementedNodeCAServer) IssueNodeCertificate(ctx context.Context, req *IssueNodeCertificateRequest) (*IssueNodeCertificateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method IssueNodeCertificate not implemented") +} +func (*UnimplementedNodeCAServer) NodeCertificateStatus(ctx context.Context, req *NodeCertificateStatusRequest) (*NodeCertificateStatusResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NodeCertificateStatus not implemented") +} + func RegisterNodeCAServer(s *grpc.Server, srv NodeCAServer) { s.RegisterService(&_NodeCA_serviceDesc, srv) } @@ -726,7 +801,7 @@ var _NodeCA_serviceDesc = grpc.ServiceDesc{ func (m *NodeCertificateStatusRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -734,23 +809,29 @@ func (m *NodeCertificateStatusRequest) Marshal() (dAtA []byte, err error) { } func (m *NodeCertificateStatusRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NodeCertificateStatusRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.NodeID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) i = encodeVarintCa(dAtA, i, uint64(len(m.NodeID))) - i += copy(dAtA[i:], m.NodeID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *NodeCertificateStatusResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -758,37 +839,46 @@ func (m *NodeCertificateStatusResponse) Marshal() (dAtA []byte, err error) { } func (m *NodeCertificateStatusResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NodeCertificateStatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Status != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintCa(dAtA, i, uint64(m.Status.Size())) - n1, err := m.Status.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 - } if m.Certificate != nil { + { + size, err := m.Certificate.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCa(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintCa(dAtA, i, uint64(m.Certificate.Size())) - n2, err := m.Certificate.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + if m.Status != nil { + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCa(dAtA, i, uint64(size)) } - i += n2 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *IssueNodeCertificateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -796,39 +886,46 @@ func (m *IssueNodeCertificateRequest) Marshal() (dAtA []byte, err error) { } func (m *IssueNodeCertificateRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IssueNodeCertificateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Role != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintCa(dAtA, i, uint64(m.Role)) - } - if len(m.CSR) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintCa(dAtA, i, uint64(len(m.CSR))) - i += copy(dAtA[i:], m.CSR) + if m.Availability != 0 { + i = encodeVarintCa(dAtA, i, uint64(m.Availability)) + i-- + dAtA[i] = 0x20 } if len(m.Token) > 0 { - dAtA[i] = 0x1a - i++ + i -= len(m.Token) + copy(dAtA[i:], m.Token) i = encodeVarintCa(dAtA, i, uint64(len(m.Token))) - i += copy(dAtA[i:], m.Token) + i-- + dAtA[i] = 0x1a } - if m.Availability != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintCa(dAtA, i, uint64(m.Availability)) + if len(m.CSR) > 0 { + i -= len(m.CSR) + copy(dAtA[i:], m.CSR) + i = encodeVarintCa(dAtA, i, uint64(len(m.CSR))) + i-- + dAtA[i] = 0x12 + } + if m.Role != 0 { + i = encodeVarintCa(dAtA, i, uint64(m.Role)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *IssueNodeCertificateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -836,28 +933,34 @@ func (m *IssueNodeCertificateResponse) Marshal() (dAtA []byte, err error) { } func (m *IssueNodeCertificateResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IssueNodeCertificateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.NodeID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintCa(dAtA, i, uint64(len(m.NodeID))) - i += copy(dAtA[i:], m.NodeID) - } if m.NodeMembership != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintCa(dAtA, i, uint64(m.NodeMembership)) + i-- + dAtA[i] = 0x10 } - return i, nil + if len(m.NodeID) > 0 { + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) + i = encodeVarintCa(dAtA, i, uint64(len(m.NodeID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *GetRootCACertificateRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -865,17 +968,22 @@ func (m *GetRootCACertificateRequest) Marshal() (dAtA []byte, err error) { } func (m *GetRootCACertificateRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetRootCACertificateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + return len(dAtA) - i, nil } func (m *GetRootCACertificateResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -883,23 +991,29 @@ func (m *GetRootCACertificateResponse) Marshal() (dAtA []byte, err error) { } func (m *GetRootCACertificateResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetRootCACertificateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.Certificate) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Certificate) + copy(dAtA[i:], m.Certificate) i = encodeVarintCa(dAtA, i, uint64(len(m.Certificate))) - i += copy(dAtA[i:], m.Certificate) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *GetUnlockKeyRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -907,17 +1021,22 @@ func (m *GetUnlockKeyRequest) Marshal() (dAtA []byte, err error) { } func (m *GetUnlockKeyRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetUnlockKeyRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + return len(dAtA) - i, nil } func (m *GetUnlockKeyResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -925,35 +1044,45 @@ func (m *GetUnlockKeyResponse) Marshal() (dAtA []byte, err error) { } func (m *GetUnlockKeyResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetUnlockKeyResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.UnlockKey) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintCa(dAtA, i, uint64(len(m.UnlockKey))) - i += copy(dAtA[i:], m.UnlockKey) + { + size, err := m.Version.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCa(dAtA, i, uint64(size)) } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintCa(dAtA, i, uint64(m.Version.Size())) - n3, err := m.Version.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.UnlockKey) > 0 { + i -= len(m.UnlockKey) + copy(dAtA[i:], m.UnlockKey) + i = encodeVarintCa(dAtA, i, uint64(len(m.UnlockKey))) + i-- + dAtA[i] = 0xa } - i += n3 - return i, nil + return len(dAtA) - i, nil } func encodeVarintCa(dAtA []byte, offset int, v uint64) int { + offset -= sovCa(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } type raftProxyCAServer struct { @@ -1241,6 +1370,9 @@ func (p *raftProxyNodeCAServer) NodeCertificateStatus(ctx context.Context, r *No } func (m *NodeCertificateStatusRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.NodeID) @@ -1251,6 +1383,9 @@ func (m *NodeCertificateStatusRequest) Size() (n int) { } func (m *NodeCertificateStatusResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Status != nil { @@ -1265,6 +1400,9 @@ func (m *NodeCertificateStatusResponse) Size() (n int) { } func (m *IssueNodeCertificateRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Role != 0 { @@ -1285,6 +1423,9 @@ func (m *IssueNodeCertificateRequest) Size() (n int) { } func (m *IssueNodeCertificateResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.NodeID) @@ -1298,12 +1439,18 @@ func (m *IssueNodeCertificateResponse) Size() (n int) { } func (m *GetRootCACertificateRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l return n } func (m *GetRootCACertificateResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Certificate) @@ -1314,12 +1461,18 @@ func (m *GetRootCACertificateResponse) Size() (n int) { } func (m *GetUnlockKeyRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l return n } func (m *GetUnlockKeyResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.UnlockKey) @@ -1332,14 +1485,7 @@ func (m *GetUnlockKeyResponse) Size() (n int) { } func sovCa(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozCa(x uint64) (n int) { return sovCa(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -1423,7 +1569,7 @@ func (this *GetUnlockKeyResponse) String() string { } s := strings.Join([]string{`&GetUnlockKeyResponse{`, `UnlockKey:` + fmt.Sprintf("%v", this.UnlockKey) + `,`, - `Version:` + strings.Replace(strings.Replace(this.Version.String(), "Version", "Version", 1), `&`, ``, 1) + `,`, + `Version:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Version), "Version", "Version", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -1451,7 +1597,7 @@ func (m *NodeCertificateStatusRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1479,7 +1625,7 @@ func (m *NodeCertificateStatusRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1489,6 +1635,9 @@ func (m *NodeCertificateStatusRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthCa } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCa + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1500,7 +1649,7 @@ func (m *NodeCertificateStatusRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCa } if (iNdEx + skippy) > l { @@ -1530,7 +1679,7 @@ func (m *NodeCertificateStatusResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1558,7 +1707,7 @@ func (m *NodeCertificateStatusResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1567,6 +1716,9 @@ func (m *NodeCertificateStatusResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthCa } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCa + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1591,7 +1743,7 @@ func (m *NodeCertificateStatusResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1600,6 +1752,9 @@ func (m *NodeCertificateStatusResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthCa } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCa + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1616,7 +1771,7 @@ func (m *NodeCertificateStatusResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCa } if (iNdEx + skippy) > l { @@ -1646,7 +1801,7 @@ func (m *IssueNodeCertificateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1674,7 +1829,7 @@ func (m *IssueNodeCertificateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Role |= (NodeRole(b) & 0x7F) << shift + m.Role |= NodeRole(b&0x7F) << shift if b < 0x80 { break } @@ -1693,7 +1848,7 @@ func (m *IssueNodeCertificateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1702,6 +1857,9 @@ func (m *IssueNodeCertificateRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthCa } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCa + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1724,7 +1882,7 @@ func (m *IssueNodeCertificateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1734,6 +1892,9 @@ func (m *IssueNodeCertificateRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthCa } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCa + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1753,7 +1914,7 @@ func (m *IssueNodeCertificateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Availability |= (NodeSpec_Availability(b) & 0x7F) << shift + m.Availability |= NodeSpec_Availability(b&0x7F) << shift if b < 0x80 { break } @@ -1764,7 +1925,7 @@ func (m *IssueNodeCertificateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCa } if (iNdEx + skippy) > l { @@ -1794,7 +1955,7 @@ func (m *IssueNodeCertificateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1822,7 +1983,7 @@ func (m *IssueNodeCertificateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1832,6 +1993,9 @@ func (m *IssueNodeCertificateResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthCa } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCa + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1851,7 +2015,7 @@ func (m *IssueNodeCertificateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.NodeMembership |= (NodeSpec_Membership(b) & 0x7F) << shift + m.NodeMembership |= NodeSpec_Membership(b&0x7F) << shift if b < 0x80 { break } @@ -1862,7 +2026,7 @@ func (m *IssueNodeCertificateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCa } if (iNdEx + skippy) > l { @@ -1892,7 +2056,7 @@ func (m *GetRootCACertificateRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1912,7 +2076,7 @@ func (m *GetRootCACertificateRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCa } if (iNdEx + skippy) > l { @@ -1942,7 +2106,7 @@ func (m *GetRootCACertificateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1970,7 +2134,7 @@ func (m *GetRootCACertificateResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1979,6 +2143,9 @@ func (m *GetRootCACertificateResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthCa } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCa + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1993,7 +2160,7 @@ func (m *GetRootCACertificateResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCa } if (iNdEx + skippy) > l { @@ -2023,7 +2190,7 @@ func (m *GetUnlockKeyRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2043,7 +2210,7 @@ func (m *GetUnlockKeyRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCa } if (iNdEx + skippy) > l { @@ -2073,7 +2240,7 @@ func (m *GetUnlockKeyResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2101,7 +2268,7 @@ func (m *GetUnlockKeyResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2110,6 +2277,9 @@ func (m *GetUnlockKeyResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthCa } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCa + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2132,7 +2302,7 @@ func (m *GetUnlockKeyResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2141,6 +2311,9 @@ func (m *GetUnlockKeyResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthCa } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCa + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2154,7 +2327,7 @@ func (m *GetUnlockKeyResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthCa } if (iNdEx + skippy) > l { @@ -2172,6 +2345,7 @@ func (m *GetUnlockKeyResponse) Unmarshal(dAtA []byte) error { func skipCa(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -2203,10 +2377,8 @@ func skipCa(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -2223,99 +2395,34 @@ func skipCa(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthCa } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowCa - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipCa(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCa + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthCa + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthCa = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowCa = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthCa = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCa = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCa = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("github.com/docker/swarmkit/api/ca.proto", fileDescriptorCa) } - -var fileDescriptorCa = []byte{ - // 638 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xc1, 0x6e, 0xd3, 0x4c, - 0x10, 0xee, 0xba, 0xfd, 0xd3, 0xbf, 0xd3, 0xd0, 0xa2, 0xa5, 0x95, 0x4c, 0x9a, 0x3a, 0x95, 0x39, - 0xb4, 0x20, 0x61, 0xb7, 0x01, 0x09, 0x09, 0x2e, 0x24, 0x41, 0xaa, 0x2a, 0x54, 0x84, 0xb6, 0x82, - 0x6b, 0xe5, 0x38, 0xdb, 0x74, 0x15, 0xc7, 0x6b, 0xbc, 0xeb, 0x42, 0x6e, 0x48, 0x20, 0xde, 0x00, - 0xc1, 0x89, 0x47, 0xe0, 0x39, 0x2a, 0x4e, 0x48, 0x5c, 0x38, 0x55, 0xd4, 0x0f, 0xc0, 0x33, 0x20, - 0xaf, 0x6d, 0x9a, 0xb4, 0x4e, 0x5a, 0x4e, 0xf1, 0xce, 0x7c, 0xdf, 0x37, 0x33, 0xdf, 0x4e, 0x16, - 0xd6, 0xbb, 0x4c, 0x1e, 0x46, 0x6d, 0xcb, 0xe5, 0x7d, 0xbb, 0xc3, 0xdd, 0x1e, 0x0d, 0x6d, 0xf1, - 0xda, 0x09, 0xfb, 0x3d, 0x26, 0x6d, 0x27, 0x60, 0xb6, 0xeb, 0x58, 0x41, 0xc8, 0x25, 0xc7, 0x38, - 0xcd, 0x5a, 0x79, 0xd6, 0x3a, 0xda, 0xaa, 0xdc, 0xb9, 0x84, 0x2c, 0x07, 0x01, 0x15, 0x29, 0xff, - 0x52, 0xac, 0x08, 0xa8, 0x9b, 0x63, 0x97, 0xba, 0xbc, 0xcb, 0xd5, 0xa7, 0x9d, 0x7c, 0x65, 0xd1, - 0x07, 0x13, 0x14, 0x14, 0xa2, 0x1d, 0x1d, 0xd8, 0x81, 0x17, 0x75, 0x99, 0x9f, 0xfd, 0xa4, 0x44, - 0xb3, 0x05, 0xd5, 0x67, 0xbc, 0x43, 0x5b, 0x34, 0x94, 0xec, 0x80, 0xb9, 0x8e, 0xa4, 0x7b, 0xd2, - 0x91, 0x91, 0x20, 0xf4, 0x55, 0x44, 0x85, 0xc4, 0xb7, 0x60, 0xd6, 0xe7, 0x1d, 0xba, 0xcf, 0x3a, - 0x3a, 0x5a, 0x43, 0x1b, 0x73, 0x4d, 0x88, 0x4f, 0x6a, 0xa5, 0x84, 0xb2, 0xf3, 0x84, 0x94, 0x92, - 0xd4, 0x4e, 0xc7, 0xfc, 0x82, 0x60, 0x75, 0x8c, 0x8a, 0x08, 0xb8, 0x2f, 0x28, 0x7e, 0x08, 0x25, - 0xa1, 0x22, 0x4a, 0x65, 0xbe, 0x6e, 0x5a, 0x17, 0x2d, 0xb3, 0x76, 0x84, 0x88, 0x1c, 0xdf, 0xcd, - 0xb9, 0x19, 0x03, 0x37, 0x60, 0xde, 0x3d, 0x13, 0xd6, 0x35, 0x25, 0x50, 0x2b, 0x12, 0x18, 0xaa, - 0x4f, 0x86, 0x39, 0xe6, 0x0f, 0x04, 0x2b, 0x89, 0x3a, 0x3d, 0xd7, 0x65, 0x3e, 0xe5, 0x7d, 0x98, - 0x09, 0xb9, 0x47, 0x55, 0x73, 0x0b, 0xf5, 0x6a, 0x91, 0x76, 0xc2, 0x24, 0xdc, 0xa3, 0x4d, 0x4d, - 0x47, 0x44, 0xa1, 0xf1, 0x4d, 0x98, 0x76, 0x45, 0xa8, 0x1a, 0x2a, 0x37, 0x67, 0xe3, 0x93, 0xda, - 0x74, 0x6b, 0x8f, 0x90, 0x24, 0x86, 0x97, 0xe0, 0x3f, 0xc9, 0x7b, 0xd4, 0xd7, 0xa7, 0x13, 0xd3, - 0x48, 0x7a, 0xc0, 0xbb, 0x50, 0x76, 0x8e, 0x1c, 0xe6, 0x39, 0x6d, 0xe6, 0x31, 0x39, 0xd0, 0x67, - 0x54, 0xb9, 0xdb, 0xe3, 0xca, 0xed, 0x05, 0xd4, 0xb5, 0x1a, 0x43, 0x04, 0x32, 0x42, 0x37, 0x3f, - 0x22, 0xa8, 0x16, 0x4f, 0x95, 0xb9, 0x7e, 0x95, 0xcb, 0xc3, 0xcf, 0x61, 0x51, 0x81, 0xfa, 0xb4, - 0xdf, 0xa6, 0xa1, 0x38, 0x64, 0x81, 0x9a, 0x68, 0xa1, 0xbe, 0x3e, 0xb1, 0xaf, 0xdd, 0xbf, 0x70, - 0xb2, 0x90, 0xf0, 0xcf, 0xce, 0xe6, 0x2a, 0xac, 0x6c, 0x53, 0x49, 0x38, 0x97, 0xad, 0xc6, 0x45, - 0xb3, 0xcd, 0xc7, 0x50, 0x2d, 0x4e, 0x67, 0x5d, 0xaf, 0x8d, 0xde, 0x77, 0xd2, 0x79, 0x79, 0xf4, - 0x3a, 0x97, 0xe1, 0xc6, 0x36, 0x95, 0x2f, 0x7c, 0x8f, 0xbb, 0xbd, 0xa7, 0x74, 0x90, 0x0b, 0x87, - 0xb0, 0x34, 0x1a, 0xce, 0x04, 0x57, 0x01, 0x22, 0x15, 0xdc, 0xef, 0xd1, 0x41, 0xa6, 0x37, 0x17, - 0xe5, 0x30, 0xfc, 0x08, 0x66, 0x8f, 0x68, 0x28, 0x18, 0xf7, 0xb3, 0xdd, 0x5a, 0x29, 0x1a, 0xfc, - 0x65, 0x0a, 0x69, 0xce, 0x1c, 0x9f, 0xd4, 0xa6, 0x48, 0xce, 0xa8, 0xbf, 0xd7, 0x40, 0x6b, 0x35, - 0xf0, 0x3b, 0xa4, 0x6a, 0x5f, 0x18, 0x0a, 0xdb, 0x45, 0x5a, 0x13, 0xdc, 0xa9, 0x6c, 0x5e, 0x9d, - 0x90, 0x8e, 0x67, 0xfe, 0xff, 0xed, 0xeb, 0xef, 0xcf, 0x9a, 0x76, 0x1d, 0xe1, 0x37, 0x50, 0x1e, - 0x36, 0x00, 0xaf, 0x8f, 0xd1, 0x3a, 0xef, 0x5c, 0x65, 0xe3, 0x72, 0x60, 0x56, 0x6c, 0x59, 0x15, - 0x5b, 0x84, 0x6b, 0x0a, 0x79, 0xb7, 0xef, 0xf8, 0x4e, 0x97, 0x86, 0xf5, 0x4f, 0x1a, 0xa8, 0xbd, - 0xca, 0xac, 0x28, 0xda, 0xca, 0x62, 0x2b, 0x26, 0xfc, 0x2b, 0x8b, 0xad, 0x98, 0xb4, 0xf0, 0x43, - 0x56, 0x7c, 0x40, 0xb0, 0x5c, 0xf8, 0x24, 0xe1, 0xcd, 0x71, 0x6b, 0x3d, 0xee, 0x0d, 0xac, 0x6c, - 0xfd, 0x03, 0xe3, 0x7c, 0x23, 0x4d, 0xfd, 0xf8, 0xd4, 0x98, 0xfa, 0x79, 0x6a, 0x4c, 0xbd, 0x8d, - 0x0d, 0x74, 0x1c, 0x1b, 0xe8, 0x7b, 0x6c, 0xa0, 0x5f, 0xb1, 0x81, 0xda, 0x25, 0xf5, 0x02, 0xdf, - 0xfb, 0x13, 0x00, 0x00, 0xff, 0xff, 0xe1, 0xda, 0xca, 0xba, 0x67, 0x06, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/control.pb.go b/vendor/github.com/docker/swarmkit/api/control.pb.go index 25f0c854cf31a..9268b44c2b4b4 100644 --- a/vendor/github.com/docker/swarmkit/api/control.pb.go +++ b/vendor/github.com/docker/swarmkit/api/control.pb.go @@ -3,35 +3,40 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" -import _ "github.com/docker/swarmkit/protobuf/plugin" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import raftselector "github.com/docker/swarmkit/manager/raftselector" -import codes "google.golang.org/grpc/codes" -import status "google.golang.org/grpc/status" -import metadata "google.golang.org/grpc/metadata" -import peer "google.golang.org/grpc/peer" -import rafttime "time" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + context "context" + fmt "fmt" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + raftselector "github.com/docker/swarmkit/manager/raftselector" + _ "github.com/docker/swarmkit/protobuf/plugin" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + types "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + metadata "google.golang.org/grpc/metadata" + peer "google.golang.org/grpc/peer" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + rafttime "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type UpdateServiceRequest_Rollback int32 const ( @@ -47,6 +52,7 @@ var UpdateServiceRequest_Rollback_name = map[int32]string{ 0: "NONE", 1: "PREVIOUS", } + var UpdateServiceRequest_Rollback_value = map[string]int32{ "NONE": 0, "PREVIOUS": 1, @@ -55,82 +61,277 @@ var UpdateServiceRequest_Rollback_value = map[string]int32{ func (x UpdateServiceRequest_Rollback) String() string { return proto.EnumName(UpdateServiceRequest_Rollback_name, int32(x)) } + func (UpdateServiceRequest_Rollback) EnumDescriptor() ([]byte, []int) { - return fileDescriptorControl, []int{18, 0} + return fileDescriptor_b37401dd08bf8930, []int{18, 0} } type GetNodeRequest struct { NodeID string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` } -func (m *GetNodeRequest) Reset() { *m = GetNodeRequest{} } -func (*GetNodeRequest) ProtoMessage() {} -func (*GetNodeRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{0} } +func (m *GetNodeRequest) Reset() { *m = GetNodeRequest{} } +func (*GetNodeRequest) ProtoMessage() {} +func (*GetNodeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{0} +} +func (m *GetNodeRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetNodeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetNodeRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetNodeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetNodeRequest.Merge(m, src) +} +func (m *GetNodeRequest) XXX_Size() int { + return m.Size() +} +func (m *GetNodeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetNodeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetNodeRequest proto.InternalMessageInfo type GetNodeResponse struct { - Node *Node `protobuf:"bytes,1,opt,name=node" json:"node,omitempty"` + Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` +} + +func (m *GetNodeResponse) Reset() { *m = GetNodeResponse{} } +func (*GetNodeResponse) ProtoMessage() {} +func (*GetNodeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{1} +} +func (m *GetNodeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetNodeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetNodeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetNodeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetNodeResponse.Merge(m, src) +} +func (m *GetNodeResponse) XXX_Size() int { + return m.Size() +} +func (m *GetNodeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetNodeResponse.DiscardUnknown(m) } -func (m *GetNodeResponse) Reset() { *m = GetNodeResponse{} } -func (*GetNodeResponse) ProtoMessage() {} -func (*GetNodeResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{1} } +var xxx_messageInfo_GetNodeResponse proto.InternalMessageInfo type ListNodesRequest struct { - Filters *ListNodesRequest_Filters `protobuf:"bytes,1,opt,name=filters" json:"filters,omitempty"` + Filters *ListNodesRequest_Filters `protobuf:"bytes,1,opt,name=filters,proto3" json:"filters,omitempty"` } -func (m *ListNodesRequest) Reset() { *m = ListNodesRequest{} } -func (*ListNodesRequest) ProtoMessage() {} -func (*ListNodesRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{2} } +func (m *ListNodesRequest) Reset() { *m = ListNodesRequest{} } +func (*ListNodesRequest) ProtoMessage() {} +func (*ListNodesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{2} +} +func (m *ListNodesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListNodesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListNodesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListNodesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListNodesRequest.Merge(m, src) +} +func (m *ListNodesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListNodesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListNodesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListNodesRequest proto.InternalMessageInfo type ListNodesRequest_Filters struct { - Names []string `protobuf:"bytes,1,rep,name=names" json:"names,omitempty"` - IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes" json:"id_prefixes,omitempty"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes,proto3" json:"id_prefixes,omitempty"` // Labels refers to engine labels, which are labels set by the user on the // node and reported back to the managers - Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // NodeLabels are labels set on the node object on the managers. - NodeLabels map[string]string `protobuf:"bytes,7,rep,name=node_labels,json=nodeLabels" json:"node_labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Memberships []NodeSpec_Membership `protobuf:"varint,4,rep,name=memberships,enum=docker.swarmkit.v1.NodeSpec_Membership" json:"memberships,omitempty"` - Roles []NodeRole `protobuf:"varint,5,rep,name=roles,enum=docker.swarmkit.v1.NodeRole" json:"roles,omitempty"` + NodeLabels map[string]string `protobuf:"bytes,7,rep,name=node_labels,json=nodeLabels,proto3" json:"node_labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Memberships []NodeSpec_Membership `protobuf:"varint,4,rep,name=memberships,proto3,enum=docker.swarmkit.v1.NodeSpec_Membership" json:"memberships,omitempty"` + Roles []NodeRole `protobuf:"varint,5,rep,name=roles,proto3,enum=docker.swarmkit.v1.NodeRole" json:"roles,omitempty"` // NamePrefixes matches all objects with the given prefixes - NamePrefixes []string `protobuf:"bytes,6,rep,name=name_prefixes,json=namePrefixes" json:"name_prefixes,omitempty"` + NamePrefixes []string `protobuf:"bytes,6,rep,name=name_prefixes,json=namePrefixes,proto3" json:"name_prefixes,omitempty"` } func (m *ListNodesRequest_Filters) Reset() { *m = ListNodesRequest_Filters{} } func (*ListNodesRequest_Filters) ProtoMessage() {} func (*ListNodesRequest_Filters) Descriptor() ([]byte, []int) { - return fileDescriptorControl, []int{2, 0} + return fileDescriptor_b37401dd08bf8930, []int{2, 0} +} +func (m *ListNodesRequest_Filters) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListNodesRequest_Filters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListNodesRequest_Filters.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListNodesRequest_Filters) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListNodesRequest_Filters.Merge(m, src) +} +func (m *ListNodesRequest_Filters) XXX_Size() int { + return m.Size() +} +func (m *ListNodesRequest_Filters) XXX_DiscardUnknown() { + xxx_messageInfo_ListNodesRequest_Filters.DiscardUnknown(m) } +var xxx_messageInfo_ListNodesRequest_Filters proto.InternalMessageInfo + type ListNodesResponse struct { - Nodes []*Node `protobuf:"bytes,1,rep,name=nodes" json:"nodes,omitempty"` + Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"` +} + +func (m *ListNodesResponse) Reset() { *m = ListNodesResponse{} } +func (*ListNodesResponse) ProtoMessage() {} +func (*ListNodesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{3} +} +func (m *ListNodesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListNodesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListNodesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListNodesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListNodesResponse.Merge(m, src) +} +func (m *ListNodesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListNodesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListNodesResponse.DiscardUnknown(m) } -func (m *ListNodesResponse) Reset() { *m = ListNodesResponse{} } -func (*ListNodesResponse) ProtoMessage() {} -func (*ListNodesResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{3} } +var xxx_messageInfo_ListNodesResponse proto.InternalMessageInfo // UpdateNodeRequest requests an update to the specified node. This may be used // to request a new availability for a node, such as PAUSE. Invalid updates // will be denied and cause an error. type UpdateNodeRequest struct { NodeID string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` - NodeVersion *Version `protobuf:"bytes,2,opt,name=node_version,json=nodeVersion" json:"node_version,omitempty"` - Spec *NodeSpec `protobuf:"bytes,3,opt,name=spec" json:"spec,omitempty"` + NodeVersion *Version `protobuf:"bytes,2,opt,name=node_version,json=nodeVersion,proto3" json:"node_version,omitempty"` + Spec *NodeSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` } -func (m *UpdateNodeRequest) Reset() { *m = UpdateNodeRequest{} } -func (*UpdateNodeRequest) ProtoMessage() {} -func (*UpdateNodeRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{4} } +func (m *UpdateNodeRequest) Reset() { *m = UpdateNodeRequest{} } +func (*UpdateNodeRequest) ProtoMessage() {} +func (*UpdateNodeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{4} +} +func (m *UpdateNodeRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateNodeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateNodeRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateNodeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateNodeRequest.Merge(m, src) +} +func (m *UpdateNodeRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateNodeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateNodeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateNodeRequest proto.InternalMessageInfo type UpdateNodeResponse struct { - Node *Node `protobuf:"bytes,1,opt,name=node" json:"node,omitempty"` + Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` +} + +func (m *UpdateNodeResponse) Reset() { *m = UpdateNodeResponse{} } +func (*UpdateNodeResponse) ProtoMessage() {} +func (*UpdateNodeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{5} +} +func (m *UpdateNodeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateNodeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateNodeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateNodeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateNodeResponse.Merge(m, src) +} +func (m *UpdateNodeResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateNodeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateNodeResponse.DiscardUnknown(m) } -func (m *UpdateNodeResponse) Reset() { *m = UpdateNodeResponse{} } -func (*UpdateNodeResponse) ProtoMessage() {} -func (*UpdateNodeResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{5} } +var xxx_messageInfo_UpdateNodeResponse proto.InternalMessageInfo // RemoveNodeRequest requests to delete the specified node from store. type RemoveNodeRequest struct { @@ -138,66 +339,262 @@ type RemoveNodeRequest struct { Force bool `protobuf:"varint,2,opt,name=force,proto3" json:"force,omitempty"` } -func (m *RemoveNodeRequest) Reset() { *m = RemoveNodeRequest{} } -func (*RemoveNodeRequest) ProtoMessage() {} -func (*RemoveNodeRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{6} } +func (m *RemoveNodeRequest) Reset() { *m = RemoveNodeRequest{} } +func (*RemoveNodeRequest) ProtoMessage() {} +func (*RemoveNodeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{6} +} +func (m *RemoveNodeRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveNodeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveNodeRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveNodeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveNodeRequest.Merge(m, src) +} +func (m *RemoveNodeRequest) XXX_Size() int { + return m.Size() +} +func (m *RemoveNodeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveNodeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveNodeRequest proto.InternalMessageInfo type RemoveNodeResponse struct { } -func (m *RemoveNodeResponse) Reset() { *m = RemoveNodeResponse{} } -func (*RemoveNodeResponse) ProtoMessage() {} -func (*RemoveNodeResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{7} } +func (m *RemoveNodeResponse) Reset() { *m = RemoveNodeResponse{} } +func (*RemoveNodeResponse) ProtoMessage() {} +func (*RemoveNodeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{7} +} +func (m *RemoveNodeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveNodeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveNodeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveNodeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveNodeResponse.Merge(m, src) +} +func (m *RemoveNodeResponse) XXX_Size() int { + return m.Size() +} +func (m *RemoveNodeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveNodeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveNodeResponse proto.InternalMessageInfo type GetTaskRequest struct { TaskID string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` } -func (m *GetTaskRequest) Reset() { *m = GetTaskRequest{} } -func (*GetTaskRequest) ProtoMessage() {} -func (*GetTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{8} } +func (m *GetTaskRequest) Reset() { *m = GetTaskRequest{} } +func (*GetTaskRequest) ProtoMessage() {} +func (*GetTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{8} +} +func (m *GetTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTaskRequest.Merge(m, src) +} +func (m *GetTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *GetTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTaskRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTaskRequest proto.InternalMessageInfo type GetTaskResponse struct { - Task *Task `protobuf:"bytes,1,opt,name=task" json:"task,omitempty"` + Task *Task `protobuf:"bytes,1,opt,name=task,proto3" json:"task,omitempty"` +} + +func (m *GetTaskResponse) Reset() { *m = GetTaskResponse{} } +func (*GetTaskResponse) ProtoMessage() {} +func (*GetTaskResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{9} +} +func (m *GetTaskResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetTaskResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetTaskResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTaskResponse.Merge(m, src) +} +func (m *GetTaskResponse) XXX_Size() int { + return m.Size() +} +func (m *GetTaskResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetTaskResponse.DiscardUnknown(m) } -func (m *GetTaskResponse) Reset() { *m = GetTaskResponse{} } -func (*GetTaskResponse) ProtoMessage() {} -func (*GetTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{9} } +var xxx_messageInfo_GetTaskResponse proto.InternalMessageInfo type RemoveTaskRequest struct { TaskID string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` } -func (m *RemoveTaskRequest) Reset() { *m = RemoveTaskRequest{} } -func (*RemoveTaskRequest) ProtoMessage() {} -func (*RemoveTaskRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{10} } +func (m *RemoveTaskRequest) Reset() { *m = RemoveTaskRequest{} } +func (*RemoveTaskRequest) ProtoMessage() {} +func (*RemoveTaskRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{10} +} +func (m *RemoveTaskRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveTaskRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveTaskRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveTaskRequest.Merge(m, src) +} +func (m *RemoveTaskRequest) XXX_Size() int { + return m.Size() +} +func (m *RemoveTaskRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveTaskRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveTaskRequest proto.InternalMessageInfo type RemoveTaskResponse struct { } -func (m *RemoveTaskResponse) Reset() { *m = RemoveTaskResponse{} } -func (*RemoveTaskResponse) ProtoMessage() {} -func (*RemoveTaskResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{11} } +func (m *RemoveTaskResponse) Reset() { *m = RemoveTaskResponse{} } +func (*RemoveTaskResponse) ProtoMessage() {} +func (*RemoveTaskResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{11} +} +func (m *RemoveTaskResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveTaskResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveTaskResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveTaskResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveTaskResponse.Merge(m, src) +} +func (m *RemoveTaskResponse) XXX_Size() int { + return m.Size() +} +func (m *RemoveTaskResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveTaskResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveTaskResponse proto.InternalMessageInfo type ListTasksRequest struct { - Filters *ListTasksRequest_Filters `protobuf:"bytes,1,opt,name=filters" json:"filters,omitempty"` + Filters *ListTasksRequest_Filters `protobuf:"bytes,1,opt,name=filters,proto3" json:"filters,omitempty"` +} + +func (m *ListTasksRequest) Reset() { *m = ListTasksRequest{} } +func (*ListTasksRequest) ProtoMessage() {} +func (*ListTasksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{12} +} +func (m *ListTasksRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListTasksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListTasksRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListTasksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListTasksRequest.Merge(m, src) +} +func (m *ListTasksRequest) XXX_Size() int { + return m.Size() +} +func (m *ListTasksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListTasksRequest.DiscardUnknown(m) } -func (m *ListTasksRequest) Reset() { *m = ListTasksRequest{} } -func (*ListTasksRequest) ProtoMessage() {} -func (*ListTasksRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{12} } +var xxx_messageInfo_ListTasksRequest proto.InternalMessageInfo type ListTasksRequest_Filters struct { - Names []string `protobuf:"bytes,1,rep,name=names" json:"names,omitempty"` - IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes" json:"id_prefixes,omitempty"` - Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ServiceIDs []string `protobuf:"bytes,4,rep,name=service_ids,json=serviceIds" json:"service_ids,omitempty"` - NodeIDs []string `protobuf:"bytes,5,rep,name=node_ids,json=nodeIds" json:"node_ids,omitempty"` - DesiredStates []TaskState `protobuf:"varint,6,rep,name=desired_states,json=desiredStates,enum=docker.swarmkit.v1.TaskState" json:"desired_states,omitempty"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes,proto3" json:"id_prefixes,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + ServiceIDs []string `protobuf:"bytes,4,rep,name=service_ids,json=serviceIds,proto3" json:"service_ids,omitempty"` + NodeIDs []string `protobuf:"bytes,5,rep,name=node_ids,json=nodeIds,proto3" json:"node_ids,omitempty"` + DesiredStates []TaskState `protobuf:"varint,6,rep,name=desired_states,json=desiredStates,proto3,enum=docker.swarmkit.v1.TaskState" json:"desired_states,omitempty"` // NamePrefixes matches all objects with the given prefixes - NamePrefixes []string `protobuf:"bytes,7,rep,name=name_prefixes,json=namePrefixes" json:"name_prefixes,omitempty"` - Runtimes []string `protobuf:"bytes,9,rep,name=runtimes" json:"runtimes,omitempty"` + NamePrefixes []string `protobuf:"bytes,7,rep,name=name_prefixes,json=namePrefixes,proto3" json:"name_prefixes,omitempty"` + Runtimes []string `protobuf:"bytes,9,rep,name=runtimes,proto3" json:"runtimes,omitempty"` // UpToDate matches tasks that are consistent with the current // service definition. // Note: this is intended for internal status reporting rather @@ -208,242 +605,1123 @@ type ListTasksRequest_Filters struct { func (m *ListTasksRequest_Filters) Reset() { *m = ListTasksRequest_Filters{} } func (*ListTasksRequest_Filters) ProtoMessage() {} func (*ListTasksRequest_Filters) Descriptor() ([]byte, []int) { - return fileDescriptorControl, []int{12, 0} + return fileDescriptor_b37401dd08bf8930, []int{12, 0} +} +func (m *ListTasksRequest_Filters) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListTasksRequest_Filters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListTasksRequest_Filters.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListTasksRequest_Filters) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListTasksRequest_Filters.Merge(m, src) +} +func (m *ListTasksRequest_Filters) XXX_Size() int { + return m.Size() +} +func (m *ListTasksRequest_Filters) XXX_DiscardUnknown() { + xxx_messageInfo_ListTasksRequest_Filters.DiscardUnknown(m) } +var xxx_messageInfo_ListTasksRequest_Filters proto.InternalMessageInfo + type ListTasksResponse struct { - Tasks []*Task `protobuf:"bytes,1,rep,name=tasks" json:"tasks,omitempty"` + Tasks []*Task `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"` +} + +func (m *ListTasksResponse) Reset() { *m = ListTasksResponse{} } +func (*ListTasksResponse) ProtoMessage() {} +func (*ListTasksResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{13} +} +func (m *ListTasksResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListTasksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListTasksResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListTasksResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListTasksResponse.Merge(m, src) +} +func (m *ListTasksResponse) XXX_Size() int { + return m.Size() +} +func (m *ListTasksResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListTasksResponse.DiscardUnknown(m) } -func (m *ListTasksResponse) Reset() { *m = ListTasksResponse{} } -func (*ListTasksResponse) ProtoMessage() {} -func (*ListTasksResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{13} } +var xxx_messageInfo_ListTasksResponse proto.InternalMessageInfo type CreateServiceRequest struct { - Spec *ServiceSpec `protobuf:"bytes,1,opt,name=spec" json:"spec,omitempty"` + Spec *ServiceSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` +} + +func (m *CreateServiceRequest) Reset() { *m = CreateServiceRequest{} } +func (*CreateServiceRequest) ProtoMessage() {} +func (*CreateServiceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{14} +} +func (m *CreateServiceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateServiceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateServiceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateServiceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateServiceRequest.Merge(m, src) +} +func (m *CreateServiceRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateServiceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateServiceRequest.DiscardUnknown(m) } -func (m *CreateServiceRequest) Reset() { *m = CreateServiceRequest{} } -func (*CreateServiceRequest) ProtoMessage() {} -func (*CreateServiceRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{14} } +var xxx_messageInfo_CreateServiceRequest proto.InternalMessageInfo type CreateServiceResponse struct { - Service *Service `protobuf:"bytes,1,opt,name=service" json:"service,omitempty"` + Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` +} + +func (m *CreateServiceResponse) Reset() { *m = CreateServiceResponse{} } +func (*CreateServiceResponse) ProtoMessage() {} +func (*CreateServiceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{15} +} +func (m *CreateServiceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateServiceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateServiceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateServiceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateServiceResponse.Merge(m, src) +} +func (m *CreateServiceResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateServiceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateServiceResponse.DiscardUnknown(m) } -func (m *CreateServiceResponse) Reset() { *m = CreateServiceResponse{} } -func (*CreateServiceResponse) ProtoMessage() {} -func (*CreateServiceResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{15} } +var xxx_messageInfo_CreateServiceResponse proto.InternalMessageInfo type GetServiceRequest struct { ServiceID string `protobuf:"bytes,1,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` InsertDefaults bool `protobuf:"varint,2,opt,name=insert_defaults,json=insertDefaults,proto3" json:"insert_defaults,omitempty"` } -func (m *GetServiceRequest) Reset() { *m = GetServiceRequest{} } -func (*GetServiceRequest) ProtoMessage() {} -func (*GetServiceRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{16} } +func (m *GetServiceRequest) Reset() { *m = GetServiceRequest{} } +func (*GetServiceRequest) ProtoMessage() {} +func (*GetServiceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{16} +} +func (m *GetServiceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetServiceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetServiceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetServiceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetServiceRequest.Merge(m, src) +} +func (m *GetServiceRequest) XXX_Size() int { + return m.Size() +} +func (m *GetServiceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetServiceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetServiceRequest proto.InternalMessageInfo type GetServiceResponse struct { - Service *Service `protobuf:"bytes,1,opt,name=service" json:"service,omitempty"` + Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` +} + +func (m *GetServiceResponse) Reset() { *m = GetServiceResponse{} } +func (*GetServiceResponse) ProtoMessage() {} +func (*GetServiceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{17} +} +func (m *GetServiceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetServiceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetServiceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetServiceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetServiceResponse.Merge(m, src) +} +func (m *GetServiceResponse) XXX_Size() int { + return m.Size() +} +func (m *GetServiceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetServiceResponse.DiscardUnknown(m) } -func (m *GetServiceResponse) Reset() { *m = GetServiceResponse{} } -func (*GetServiceResponse) ProtoMessage() {} -func (*GetServiceResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{17} } +var xxx_messageInfo_GetServiceResponse proto.InternalMessageInfo type UpdateServiceRequest struct { ServiceID string `protobuf:"bytes,1,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` - ServiceVersion *Version `protobuf:"bytes,2,opt,name=service_version,json=serviceVersion" json:"service_version,omitempty"` - Spec *ServiceSpec `protobuf:"bytes,3,opt,name=spec" json:"spec,omitempty"` + ServiceVersion *Version `protobuf:"bytes,2,opt,name=service_version,json=serviceVersion,proto3" json:"service_version,omitempty"` + Spec *ServiceSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` // Rollback may be set to PREVIOUS to request a rollback (the service's // spec will be set to the value of its previous_spec field). In this // case, the spec field of this request is ignored. Rollback UpdateServiceRequest_Rollback `protobuf:"varint,4,opt,name=rollback,proto3,enum=docker.swarmkit.v1.UpdateServiceRequest_Rollback" json:"rollback,omitempty"` } -func (m *UpdateServiceRequest) Reset() { *m = UpdateServiceRequest{} } -func (*UpdateServiceRequest) ProtoMessage() {} -func (*UpdateServiceRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{18} } +func (m *UpdateServiceRequest) Reset() { *m = UpdateServiceRequest{} } +func (*UpdateServiceRequest) ProtoMessage() {} +func (*UpdateServiceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{18} +} +func (m *UpdateServiceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateServiceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateServiceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateServiceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateServiceRequest.Merge(m, src) +} +func (m *UpdateServiceRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateServiceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateServiceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateServiceRequest proto.InternalMessageInfo type UpdateServiceResponse struct { - Service *Service `protobuf:"bytes,1,opt,name=service" json:"service,omitempty"` + Service *Service `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` +} + +func (m *UpdateServiceResponse) Reset() { *m = UpdateServiceResponse{} } +func (*UpdateServiceResponse) ProtoMessage() {} +func (*UpdateServiceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{19} +} +func (m *UpdateServiceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateServiceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateServiceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateServiceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateServiceResponse.Merge(m, src) +} +func (m *UpdateServiceResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateServiceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateServiceResponse.DiscardUnknown(m) } -func (m *UpdateServiceResponse) Reset() { *m = UpdateServiceResponse{} } -func (*UpdateServiceResponse) ProtoMessage() {} -func (*UpdateServiceResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{19} } +var xxx_messageInfo_UpdateServiceResponse proto.InternalMessageInfo type RemoveServiceRequest struct { ServiceID string `protobuf:"bytes,1,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` } -func (m *RemoveServiceRequest) Reset() { *m = RemoveServiceRequest{} } -func (*RemoveServiceRequest) ProtoMessage() {} -func (*RemoveServiceRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{20} } - -type RemoveServiceResponse struct { +func (m *RemoveServiceRequest) Reset() { *m = RemoveServiceRequest{} } +func (*RemoveServiceRequest) ProtoMessage() {} +func (*RemoveServiceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{20} } - -func (m *RemoveServiceResponse) Reset() { *m = RemoveServiceResponse{} } -func (*RemoveServiceResponse) ProtoMessage() {} -func (*RemoveServiceResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{21} } - -type ListServicesRequest struct { - Filters *ListServicesRequest_Filters `protobuf:"bytes,1,opt,name=filters" json:"filters,omitempty"` +func (m *RemoveServiceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveServiceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveServiceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveServiceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveServiceRequest.Merge(m, src) +} +func (m *RemoveServiceRequest) XXX_Size() int { + return m.Size() +} +func (m *RemoveServiceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveServiceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveServiceRequest proto.InternalMessageInfo + +type RemoveServiceResponse struct { +} + +func (m *RemoveServiceResponse) Reset() { *m = RemoveServiceResponse{} } +func (*RemoveServiceResponse) ProtoMessage() {} +func (*RemoveServiceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{21} +} +func (m *RemoveServiceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveServiceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveServiceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveServiceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveServiceResponse.Merge(m, src) +} +func (m *RemoveServiceResponse) XXX_Size() int { + return m.Size() +} +func (m *RemoveServiceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveServiceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveServiceResponse proto.InternalMessageInfo + +type ListServicesRequest struct { + Filters *ListServicesRequest_Filters `protobuf:"bytes,1,opt,name=filters,proto3" json:"filters,omitempty"` +} + +func (m *ListServicesRequest) Reset() { *m = ListServicesRequest{} } +func (*ListServicesRequest) ProtoMessage() {} +func (*ListServicesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{22} +} +func (m *ListServicesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListServicesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListServicesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListServicesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListServicesRequest.Merge(m, src) +} +func (m *ListServicesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListServicesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListServicesRequest.DiscardUnknown(m) } -func (m *ListServicesRequest) Reset() { *m = ListServicesRequest{} } -func (*ListServicesRequest) ProtoMessage() {} -func (*ListServicesRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{22} } - +var xxx_messageInfo_ListServicesRequest proto.InternalMessageInfo + type ListServicesRequest_Filters struct { - Names []string `protobuf:"bytes,1,rep,name=names" json:"names,omitempty"` - IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes" json:"id_prefixes,omitempty"` - Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes,proto3" json:"id_prefixes,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // NamePrefixes matches all objects with the given prefixes - NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes" json:"name_prefixes,omitempty"` - Runtimes []string `protobuf:"bytes,5,rep,name=runtimes" json:"runtimes,omitempty"` + NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes,proto3" json:"name_prefixes,omitempty"` + Runtimes []string `protobuf:"bytes,5,rep,name=runtimes,proto3" json:"runtimes,omitempty"` } func (m *ListServicesRequest_Filters) Reset() { *m = ListServicesRequest_Filters{} } func (*ListServicesRequest_Filters) ProtoMessage() {} func (*ListServicesRequest_Filters) Descriptor() ([]byte, []int) { - return fileDescriptorControl, []int{22, 0} + return fileDescriptor_b37401dd08bf8930, []int{22, 0} +} +func (m *ListServicesRequest_Filters) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListServicesRequest_Filters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListServicesRequest_Filters.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListServicesRequest_Filters) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListServicesRequest_Filters.Merge(m, src) +} +func (m *ListServicesRequest_Filters) XXX_Size() int { + return m.Size() +} +func (m *ListServicesRequest_Filters) XXX_DiscardUnknown() { + xxx_messageInfo_ListServicesRequest_Filters.DiscardUnknown(m) } +var xxx_messageInfo_ListServicesRequest_Filters proto.InternalMessageInfo + type ListServicesResponse struct { - Services []*Service `protobuf:"bytes,1,rep,name=services" json:"services,omitempty"` + Services []*Service `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"` +} + +func (m *ListServicesResponse) Reset() { *m = ListServicesResponse{} } +func (*ListServicesResponse) ProtoMessage() {} +func (*ListServicesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{23} +} +func (m *ListServicesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListServicesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListServicesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListServicesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListServicesResponse.Merge(m, src) +} +func (m *ListServicesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListServicesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListServicesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListServicesResponse proto.InternalMessageInfo + +// ListServiceStatusesRequest is a request to get the aggregate status of a +// service by computing the number of running vs desired tasks. It includes +// only a service ID. +type ListServiceStatusesRequest struct { + // Services is a list of service IDs to get statuses for. + Services []string `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"` +} + +func (m *ListServiceStatusesRequest) Reset() { *m = ListServiceStatusesRequest{} } +func (*ListServiceStatusesRequest) ProtoMessage() {} +func (*ListServiceStatusesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{24} +} +func (m *ListServiceStatusesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListServiceStatusesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListServiceStatusesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListServiceStatusesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListServiceStatusesRequest.Merge(m, src) +} +func (m *ListServiceStatusesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListServiceStatusesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListServiceStatusesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListServiceStatusesRequest proto.InternalMessageInfo + +// ListServiceStatusesResponse is a response containing the aggregate status of +// a service, formed by computing the number of running vs desired tasks. The +// values returned are only valid for the point in time at which the request is +// made. +type ListServiceStatusesResponse struct { + Statuses []*ListServiceStatusesResponse_ServiceStatus `protobuf:"bytes,1,rep,name=statuses,proto3" json:"statuses,omitempty"` +} + +func (m *ListServiceStatusesResponse) Reset() { *m = ListServiceStatusesResponse{} } +func (*ListServiceStatusesResponse) ProtoMessage() {} +func (*ListServiceStatusesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{25} +} +func (m *ListServiceStatusesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListServiceStatusesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListServiceStatusesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListServiceStatusesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListServiceStatusesResponse.Merge(m, src) +} +func (m *ListServiceStatusesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListServiceStatusesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListServiceStatusesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListServiceStatusesResponse proto.InternalMessageInfo + +type ListServiceStatusesResponse_ServiceStatus struct { + // ServiceID is the ID of the service this status describes + ServiceID string `protobuf:"bytes,1,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` + // DesiredTasks is the number of tasks desired to be running according to the + // service definition at request time. It is a uint64 because that is what + // the replicas field on the service spec is + DesiredTasks uint64 `protobuf:"varint,2,opt,name=desired_tasks,json=desiredTasks,proto3" json:"desired_tasks,omitempty"` + // RunningTasks is the number of tasks currently in the Running state at + // request time. This may be larger than desired tasks if, for example, a + // service has been scaled down. + RunningTasks uint64 `protobuf:"varint,3,opt,name=running_tasks,json=runningTasks,proto3" json:"running_tasks,omitempty"` + // CompletedTasks is the number of tasks in state Completed, if this + // service is in mode ReplicatedJob or GlobalJob. This must be + // cross-referenced with the service type, because the default value of 0 + // may mean that a service is not in a Job mode, or it may mean the Job has + // yet to complete any Tasks. + CompletedTasks uint64 `protobuf:"varint,4,opt,name=completed_tasks,json=completedTasks,proto3" json:"completed_tasks,omitempty"` +} + +func (m *ListServiceStatusesResponse_ServiceStatus) Reset() { + *m = ListServiceStatusesResponse_ServiceStatus{} +} +func (*ListServiceStatusesResponse_ServiceStatus) ProtoMessage() {} +func (*ListServiceStatusesResponse_ServiceStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{25, 0} +} +func (m *ListServiceStatusesResponse_ServiceStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListServiceStatusesResponse_ServiceStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListServiceStatusesResponse_ServiceStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListServiceStatusesResponse_ServiceStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListServiceStatusesResponse_ServiceStatus.Merge(m, src) +} +func (m *ListServiceStatusesResponse_ServiceStatus) XXX_Size() int { + return m.Size() +} +func (m *ListServiceStatusesResponse_ServiceStatus) XXX_DiscardUnknown() { + xxx_messageInfo_ListServiceStatusesResponse_ServiceStatus.DiscardUnknown(m) } -func (m *ListServicesResponse) Reset() { *m = ListServicesResponse{} } -func (*ListServicesResponse) ProtoMessage() {} -func (*ListServicesResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{23} } +var xxx_messageInfo_ListServiceStatusesResponse_ServiceStatus proto.InternalMessageInfo type CreateNetworkRequest struct { - Spec *NetworkSpec `protobuf:"bytes,1,opt,name=spec" json:"spec,omitempty"` + Spec *NetworkSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` +} + +func (m *CreateNetworkRequest) Reset() { *m = CreateNetworkRequest{} } +func (*CreateNetworkRequest) ProtoMessage() {} +func (*CreateNetworkRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{26} +} +func (m *CreateNetworkRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateNetworkRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateNetworkRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateNetworkRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateNetworkRequest.Merge(m, src) +} +func (m *CreateNetworkRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateNetworkRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateNetworkRequest.DiscardUnknown(m) } -func (m *CreateNetworkRequest) Reset() { *m = CreateNetworkRequest{} } -func (*CreateNetworkRequest) ProtoMessage() {} -func (*CreateNetworkRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{24} } +var xxx_messageInfo_CreateNetworkRequest proto.InternalMessageInfo type CreateNetworkResponse struct { - Network *Network `protobuf:"bytes,1,opt,name=network" json:"network,omitempty"` + Network *Network `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` +} + +func (m *CreateNetworkResponse) Reset() { *m = CreateNetworkResponse{} } +func (*CreateNetworkResponse) ProtoMessage() {} +func (*CreateNetworkResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{27} +} +func (m *CreateNetworkResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateNetworkResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateNetworkResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateNetworkResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateNetworkResponse.Merge(m, src) +} +func (m *CreateNetworkResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateNetworkResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateNetworkResponse.DiscardUnknown(m) } -func (m *CreateNetworkResponse) Reset() { *m = CreateNetworkResponse{} } -func (*CreateNetworkResponse) ProtoMessage() {} -func (*CreateNetworkResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{25} } +var xxx_messageInfo_CreateNetworkResponse proto.InternalMessageInfo type GetNetworkRequest struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` NetworkID string `protobuf:"bytes,2,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` } -func (m *GetNetworkRequest) Reset() { *m = GetNetworkRequest{} } -func (*GetNetworkRequest) ProtoMessage() {} -func (*GetNetworkRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{26} } +func (m *GetNetworkRequest) Reset() { *m = GetNetworkRequest{} } +func (*GetNetworkRequest) ProtoMessage() {} +func (*GetNetworkRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{28} +} +func (m *GetNetworkRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetNetworkRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetNetworkRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetNetworkRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetNetworkRequest.Merge(m, src) +} +func (m *GetNetworkRequest) XXX_Size() int { + return m.Size() +} +func (m *GetNetworkRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetNetworkRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetNetworkRequest proto.InternalMessageInfo type GetNetworkResponse struct { - Network *Network `protobuf:"bytes,1,opt,name=network" json:"network,omitempty"` + Network *Network `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` +} + +func (m *GetNetworkResponse) Reset() { *m = GetNetworkResponse{} } +func (*GetNetworkResponse) ProtoMessage() {} +func (*GetNetworkResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{29} +} +func (m *GetNetworkResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetNetworkResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetNetworkResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetNetworkResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetNetworkResponse.Merge(m, src) +} +func (m *GetNetworkResponse) XXX_Size() int { + return m.Size() +} +func (m *GetNetworkResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetNetworkResponse.DiscardUnknown(m) } -func (m *GetNetworkResponse) Reset() { *m = GetNetworkResponse{} } -func (*GetNetworkResponse) ProtoMessage() {} -func (*GetNetworkResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{27} } +var xxx_messageInfo_GetNetworkResponse proto.InternalMessageInfo type RemoveNetworkRequest struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` NetworkID string `protobuf:"bytes,2,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` } -func (m *RemoveNetworkRequest) Reset() { *m = RemoveNetworkRequest{} } -func (*RemoveNetworkRequest) ProtoMessage() {} -func (*RemoveNetworkRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{28} } +func (m *RemoveNetworkRequest) Reset() { *m = RemoveNetworkRequest{} } +func (*RemoveNetworkRequest) ProtoMessage() {} +func (*RemoveNetworkRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{30} +} +func (m *RemoveNetworkRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveNetworkRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveNetworkRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveNetworkRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveNetworkRequest.Merge(m, src) +} +func (m *RemoveNetworkRequest) XXX_Size() int { + return m.Size() +} +func (m *RemoveNetworkRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveNetworkRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveNetworkRequest proto.InternalMessageInfo type RemoveNetworkResponse struct { } -func (m *RemoveNetworkResponse) Reset() { *m = RemoveNetworkResponse{} } -func (*RemoveNetworkResponse) ProtoMessage() {} -func (*RemoveNetworkResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{29} } +func (m *RemoveNetworkResponse) Reset() { *m = RemoveNetworkResponse{} } +func (*RemoveNetworkResponse) ProtoMessage() {} +func (*RemoveNetworkResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{31} +} +func (m *RemoveNetworkResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveNetworkResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveNetworkResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveNetworkResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveNetworkResponse.Merge(m, src) +} +func (m *RemoveNetworkResponse) XXX_Size() int { + return m.Size() +} +func (m *RemoveNetworkResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveNetworkResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveNetworkResponse proto.InternalMessageInfo type ListNetworksRequest struct { - Filters *ListNetworksRequest_Filters `protobuf:"bytes,1,opt,name=filters" json:"filters,omitempty"` + Filters *ListNetworksRequest_Filters `protobuf:"bytes,1,opt,name=filters,proto3" json:"filters,omitempty"` +} + +func (m *ListNetworksRequest) Reset() { *m = ListNetworksRequest{} } +func (*ListNetworksRequest) ProtoMessage() {} +func (*ListNetworksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{32} +} +func (m *ListNetworksRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListNetworksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListNetworksRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListNetworksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListNetworksRequest.Merge(m, src) +} +func (m *ListNetworksRequest) XXX_Size() int { + return m.Size() +} +func (m *ListNetworksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListNetworksRequest.DiscardUnknown(m) } -func (m *ListNetworksRequest) Reset() { *m = ListNetworksRequest{} } -func (*ListNetworksRequest) ProtoMessage() {} -func (*ListNetworksRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{30} } +var xxx_messageInfo_ListNetworksRequest proto.InternalMessageInfo type ListNetworksRequest_Filters struct { - Names []string `protobuf:"bytes,1,rep,name=names" json:"names,omitempty"` - IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes" json:"id_prefixes,omitempty"` - Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes,proto3" json:"id_prefixes,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // NamePrefixes matches all objects with the given prefixes - NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes" json:"name_prefixes,omitempty"` + NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes,proto3" json:"name_prefixes,omitempty"` } func (m *ListNetworksRequest_Filters) Reset() { *m = ListNetworksRequest_Filters{} } func (*ListNetworksRequest_Filters) ProtoMessage() {} func (*ListNetworksRequest_Filters) Descriptor() ([]byte, []int) { - return fileDescriptorControl, []int{30, 0} + return fileDescriptor_b37401dd08bf8930, []int{32, 0} } +func (m *ListNetworksRequest_Filters) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListNetworksRequest_Filters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListNetworksRequest_Filters.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListNetworksRequest_Filters) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListNetworksRequest_Filters.Merge(m, src) +} +func (m *ListNetworksRequest_Filters) XXX_Size() int { + return m.Size() +} +func (m *ListNetworksRequest_Filters) XXX_DiscardUnknown() { + xxx_messageInfo_ListNetworksRequest_Filters.DiscardUnknown(m) +} + +var xxx_messageInfo_ListNetworksRequest_Filters proto.InternalMessageInfo type ListNetworksResponse struct { - Networks []*Network `protobuf:"bytes,1,rep,name=networks" json:"networks,omitempty"` + Networks []*Network `protobuf:"bytes,1,rep,name=networks,proto3" json:"networks,omitempty"` +} + +func (m *ListNetworksResponse) Reset() { *m = ListNetworksResponse{} } +func (*ListNetworksResponse) ProtoMessage() {} +func (*ListNetworksResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{33} +} +func (m *ListNetworksResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListNetworksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListNetworksResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListNetworksResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListNetworksResponse.Merge(m, src) +} +func (m *ListNetworksResponse) XXX_Size() int { + return m.Size() +} +func (m *ListNetworksResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListNetworksResponse.DiscardUnknown(m) } -func (m *ListNetworksResponse) Reset() { *m = ListNetworksResponse{} } -func (*ListNetworksResponse) ProtoMessage() {} -func (*ListNetworksResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{31} } +var xxx_messageInfo_ListNetworksResponse proto.InternalMessageInfo type GetClusterRequest struct { ClusterID string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` } -func (m *GetClusterRequest) Reset() { *m = GetClusterRequest{} } -func (*GetClusterRequest) ProtoMessage() {} -func (*GetClusterRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{32} } +func (m *GetClusterRequest) Reset() { *m = GetClusterRequest{} } +func (*GetClusterRequest) ProtoMessage() {} +func (*GetClusterRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{34} +} +func (m *GetClusterRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetClusterRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetClusterRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetClusterRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetClusterRequest.Merge(m, src) +} +func (m *GetClusterRequest) XXX_Size() int { + return m.Size() +} +func (m *GetClusterRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetClusterRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetClusterRequest proto.InternalMessageInfo type GetClusterResponse struct { - Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster" json:"cluster,omitempty"` + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` } -func (m *GetClusterResponse) Reset() { *m = GetClusterResponse{} } -func (*GetClusterResponse) ProtoMessage() {} -func (*GetClusterResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{33} } +func (m *GetClusterResponse) Reset() { *m = GetClusterResponse{} } +func (*GetClusterResponse) ProtoMessage() {} +func (*GetClusterResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{35} +} +func (m *GetClusterResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetClusterResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetClusterResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetClusterResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetClusterResponse.Merge(m, src) +} +func (m *GetClusterResponse) XXX_Size() int { + return m.Size() +} +func (m *GetClusterResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetClusterResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetClusterResponse proto.InternalMessageInfo type ListClustersRequest struct { - Filters *ListClustersRequest_Filters `protobuf:"bytes,1,opt,name=filters" json:"filters,omitempty"` + Filters *ListClustersRequest_Filters `protobuf:"bytes,1,opt,name=filters,proto3" json:"filters,omitempty"` +} + +func (m *ListClustersRequest) Reset() { *m = ListClustersRequest{} } +func (*ListClustersRequest) ProtoMessage() {} +func (*ListClustersRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{36} +} +func (m *ListClustersRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListClustersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListClustersRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListClustersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListClustersRequest.Merge(m, src) +} +func (m *ListClustersRequest) XXX_Size() int { + return m.Size() +} +func (m *ListClustersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListClustersRequest.DiscardUnknown(m) } -func (m *ListClustersRequest) Reset() { *m = ListClustersRequest{} } -func (*ListClustersRequest) ProtoMessage() {} -func (*ListClustersRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{34} } +var xxx_messageInfo_ListClustersRequest proto.InternalMessageInfo type ListClustersRequest_Filters struct { - Names []string `protobuf:"bytes,1,rep,name=names" json:"names,omitempty"` - IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes" json:"id_prefixes,omitempty"` - Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes,proto3" json:"id_prefixes,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // NamePrefixes matches all objects with the given prefixes - NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes" json:"name_prefixes,omitempty"` + NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes,proto3" json:"name_prefixes,omitempty"` } func (m *ListClustersRequest_Filters) Reset() { *m = ListClustersRequest_Filters{} } func (*ListClustersRequest_Filters) ProtoMessage() {} func (*ListClustersRequest_Filters) Descriptor() ([]byte, []int) { - return fileDescriptorControl, []int{34, 0} + return fileDescriptor_b37401dd08bf8930, []int{36, 0} +} +func (m *ListClustersRequest_Filters) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListClustersRequest_Filters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListClustersRequest_Filters.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } } +func (m *ListClustersRequest_Filters) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListClustersRequest_Filters.Merge(m, src) +} +func (m *ListClustersRequest_Filters) XXX_Size() int { + return m.Size() +} +func (m *ListClustersRequest_Filters) XXX_DiscardUnknown() { + xxx_messageInfo_ListClustersRequest_Filters.DiscardUnknown(m) +} + +var xxx_messageInfo_ListClustersRequest_Filters proto.InternalMessageInfo type ListClustersResponse struct { - Clusters []*Cluster `protobuf:"bytes,1,rep,name=clusters" json:"clusters,omitempty"` + Clusters []*Cluster `protobuf:"bytes,1,rep,name=clusters,proto3" json:"clusters,omitempty"` } -func (m *ListClustersResponse) Reset() { *m = ListClustersResponse{} } -func (*ListClustersResponse) ProtoMessage() {} -func (*ListClustersResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{35} } +func (m *ListClustersResponse) Reset() { *m = ListClustersResponse{} } +func (*ListClustersResponse) ProtoMessage() {} +func (*ListClustersResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{37} +} +func (m *ListClustersResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListClustersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListClustersResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListClustersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListClustersResponse.Merge(m, src) +} +func (m *ListClustersResponse) XXX_Size() int { + return m.Size() +} +func (m *ListClustersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListClustersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListClustersResponse proto.InternalMessageInfo // KeyRotation tells UpdateCluster what items to rotate type KeyRotation struct { @@ -455,130 +1733,464 @@ type KeyRotation struct { ManagerUnlockKey bool `protobuf:"varint,3,opt,name=manager_unlock_key,json=managerUnlockKey,proto3" json:"manager_unlock_key,omitempty"` } -func (m *KeyRotation) Reset() { *m = KeyRotation{} } -func (*KeyRotation) ProtoMessage() {} -func (*KeyRotation) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{36} } - -type UpdateClusterRequest struct { - // ClusterID is the cluster ID to update. - ClusterID string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` - // ClusterVersion is the version of the cluster being updated. - ClusterVersion *Version `protobuf:"bytes,2,opt,name=cluster_version,json=clusterVersion" json:"cluster_version,omitempty"` +func (m *KeyRotation) Reset() { *m = KeyRotation{} } +func (*KeyRotation) ProtoMessage() {} +func (*KeyRotation) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{38} +} +func (m *KeyRotation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KeyRotation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_KeyRotation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *KeyRotation) XXX_Merge(src proto.Message) { + xxx_messageInfo_KeyRotation.Merge(m, src) +} +func (m *KeyRotation) XXX_Size() int { + return m.Size() +} +func (m *KeyRotation) XXX_DiscardUnknown() { + xxx_messageInfo_KeyRotation.DiscardUnknown(m) +} + +var xxx_messageInfo_KeyRotation proto.InternalMessageInfo + +type UpdateClusterRequest struct { + // ClusterID is the cluster ID to update. + ClusterID string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + // ClusterVersion is the version of the cluster being updated. + ClusterVersion *Version `protobuf:"bytes,2,opt,name=cluster_version,json=clusterVersion,proto3" json:"cluster_version,omitempty"` // Spec is the new spec to apply to the cluster. - Spec *ClusterSpec `protobuf:"bytes,3,opt,name=spec" json:"spec,omitempty"` + Spec *ClusterSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` // Rotation contains flags for join token and unlock key rotation - Rotation KeyRotation `protobuf:"bytes,4,opt,name=rotation" json:"rotation"` + Rotation KeyRotation `protobuf:"bytes,4,opt,name=rotation,proto3" json:"rotation"` } -func (m *UpdateClusterRequest) Reset() { *m = UpdateClusterRequest{} } -func (*UpdateClusterRequest) ProtoMessage() {} -func (*UpdateClusterRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{37} } +func (m *UpdateClusterRequest) Reset() { *m = UpdateClusterRequest{} } +func (*UpdateClusterRequest) ProtoMessage() {} +func (*UpdateClusterRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{39} +} +func (m *UpdateClusterRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateClusterRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateClusterRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateClusterRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateClusterRequest.Merge(m, src) +} +func (m *UpdateClusterRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateClusterRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateClusterRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateClusterRequest proto.InternalMessageInfo type UpdateClusterResponse struct { - Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster" json:"cluster,omitempty"` + Cluster *Cluster `protobuf:"bytes,1,opt,name=cluster,proto3" json:"cluster,omitempty"` +} + +func (m *UpdateClusterResponse) Reset() { *m = UpdateClusterResponse{} } +func (*UpdateClusterResponse) ProtoMessage() {} +func (*UpdateClusterResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{40} +} +func (m *UpdateClusterResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateClusterResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateClusterResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateClusterResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateClusterResponse.Merge(m, src) +} +func (m *UpdateClusterResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateClusterResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateClusterResponse.DiscardUnknown(m) } -func (m *UpdateClusterResponse) Reset() { *m = UpdateClusterResponse{} } -func (*UpdateClusterResponse) ProtoMessage() {} -func (*UpdateClusterResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{38} } +var xxx_messageInfo_UpdateClusterResponse proto.InternalMessageInfo // GetSecretRequest is the request to get a `Secret` object given a secret id. type GetSecretRequest struct { SecretID string `protobuf:"bytes,1,opt,name=secret_id,json=secretId,proto3" json:"secret_id,omitempty"` } -func (m *GetSecretRequest) Reset() { *m = GetSecretRequest{} } -func (*GetSecretRequest) ProtoMessage() {} -func (*GetSecretRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{39} } +func (m *GetSecretRequest) Reset() { *m = GetSecretRequest{} } +func (*GetSecretRequest) ProtoMessage() {} +func (*GetSecretRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{41} +} +func (m *GetSecretRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetSecretRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetSecretRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetSecretRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSecretRequest.Merge(m, src) +} +func (m *GetSecretRequest) XXX_Size() int { + return m.Size() +} +func (m *GetSecretRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetSecretRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSecretRequest proto.InternalMessageInfo // GetSecretResponse contains the Secret corresponding to the id in // `GetSecretRequest`, but the `Secret.Spec.Data` field in each `Secret` // object should be nil instead of actually containing the secret bytes. type GetSecretResponse struct { - Secret *Secret `protobuf:"bytes,1,opt,name=secret" json:"secret,omitempty"` + Secret *Secret `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` +} + +func (m *GetSecretResponse) Reset() { *m = GetSecretResponse{} } +func (*GetSecretResponse) ProtoMessage() {} +func (*GetSecretResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{42} +} +func (m *GetSecretResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetSecretResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetSecretResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetSecretResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSecretResponse.Merge(m, src) +} +func (m *GetSecretResponse) XXX_Size() int { + return m.Size() +} +func (m *GetSecretResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetSecretResponse.DiscardUnknown(m) } -func (m *GetSecretResponse) Reset() { *m = GetSecretResponse{} } -func (*GetSecretResponse) ProtoMessage() {} -func (*GetSecretResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{40} } +var xxx_messageInfo_GetSecretResponse proto.InternalMessageInfo type UpdateSecretRequest struct { // SecretID is the secret ID to update. SecretID string `protobuf:"bytes,1,opt,name=secret_id,json=secretId,proto3" json:"secret_id,omitempty"` // SecretVersion is the version of the secret being updated. - SecretVersion *Version `protobuf:"bytes,2,opt,name=secret_version,json=secretVersion" json:"secret_version,omitempty"` + SecretVersion *Version `protobuf:"bytes,2,opt,name=secret_version,json=secretVersion,proto3" json:"secret_version,omitempty"` // Spec is the new spec to apply to the Secret // Only some fields are allowed to be updated. - Spec *SecretSpec `protobuf:"bytes,3,opt,name=spec" json:"spec,omitempty"` + Spec *SecretSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` +} + +func (m *UpdateSecretRequest) Reset() { *m = UpdateSecretRequest{} } +func (*UpdateSecretRequest) ProtoMessage() {} +func (*UpdateSecretRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{43} +} +func (m *UpdateSecretRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateSecretRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateSecretRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateSecretRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateSecretRequest.Merge(m, src) +} +func (m *UpdateSecretRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateSecretRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateSecretRequest.DiscardUnknown(m) } -func (m *UpdateSecretRequest) Reset() { *m = UpdateSecretRequest{} } -func (*UpdateSecretRequest) ProtoMessage() {} -func (*UpdateSecretRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{41} } +var xxx_messageInfo_UpdateSecretRequest proto.InternalMessageInfo type UpdateSecretResponse struct { - Secret *Secret `protobuf:"bytes,1,opt,name=secret" json:"secret,omitempty"` + Secret *Secret `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` +} + +func (m *UpdateSecretResponse) Reset() { *m = UpdateSecretResponse{} } +func (*UpdateSecretResponse) ProtoMessage() {} +func (*UpdateSecretResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{44} +} +func (m *UpdateSecretResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateSecretResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateSecretResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateSecretResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateSecretResponse.Merge(m, src) +} +func (m *UpdateSecretResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateSecretResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateSecretResponse.DiscardUnknown(m) } -func (m *UpdateSecretResponse) Reset() { *m = UpdateSecretResponse{} } -func (*UpdateSecretResponse) ProtoMessage() {} -func (*UpdateSecretResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{42} } +var xxx_messageInfo_UpdateSecretResponse proto.InternalMessageInfo // ListSecretRequest is the request to list all non-internal secrets in the secret store, // or all secrets filtered by (name or name prefix or id prefix) and labels. type ListSecretsRequest struct { - Filters *ListSecretsRequest_Filters `protobuf:"bytes,1,opt,name=filters" json:"filters,omitempty"` + Filters *ListSecretsRequest_Filters `protobuf:"bytes,1,opt,name=filters,proto3" json:"filters,omitempty"` +} + +func (m *ListSecretsRequest) Reset() { *m = ListSecretsRequest{} } +func (*ListSecretsRequest) ProtoMessage() {} +func (*ListSecretsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{45} +} +func (m *ListSecretsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListSecretsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListSecretsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListSecretsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListSecretsRequest.Merge(m, src) +} +func (m *ListSecretsRequest) XXX_Size() int { + return m.Size() +} +func (m *ListSecretsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListSecretsRequest.DiscardUnknown(m) } -func (m *ListSecretsRequest) Reset() { *m = ListSecretsRequest{} } -func (*ListSecretsRequest) ProtoMessage() {} -func (*ListSecretsRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{43} } +var xxx_messageInfo_ListSecretsRequest proto.InternalMessageInfo type ListSecretsRequest_Filters struct { - Names []string `protobuf:"bytes,1,rep,name=names" json:"names,omitempty"` - IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes" json:"id_prefixes,omitempty"` - Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes" json:"name_prefixes,omitempty"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes,proto3" json:"id_prefixes,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes,proto3" json:"name_prefixes,omitempty"` } func (m *ListSecretsRequest_Filters) Reset() { *m = ListSecretsRequest_Filters{} } func (*ListSecretsRequest_Filters) ProtoMessage() {} func (*ListSecretsRequest_Filters) Descriptor() ([]byte, []int) { - return fileDescriptorControl, []int{43, 0} + return fileDescriptor_b37401dd08bf8930, []int{45, 0} +} +func (m *ListSecretsRequest_Filters) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListSecretsRequest_Filters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListSecretsRequest_Filters.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListSecretsRequest_Filters) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListSecretsRequest_Filters.Merge(m, src) +} +func (m *ListSecretsRequest_Filters) XXX_Size() int { + return m.Size() +} +func (m *ListSecretsRequest_Filters) XXX_DiscardUnknown() { + xxx_messageInfo_ListSecretsRequest_Filters.DiscardUnknown(m) } +var xxx_messageInfo_ListSecretsRequest_Filters proto.InternalMessageInfo + // ListSecretResponse contains a list of all the secrets that match the name or // name prefix filters provided in `ListSecretRequest`. The `Secret.Spec.Data` // field in each `Secret` object should be nil instead of actually containing // the secret bytes. type ListSecretsResponse struct { - Secrets []*Secret `protobuf:"bytes,1,rep,name=secrets" json:"secrets,omitempty"` + Secrets []*Secret `protobuf:"bytes,1,rep,name=secrets,proto3" json:"secrets,omitempty"` +} + +func (m *ListSecretsResponse) Reset() { *m = ListSecretsResponse{} } +func (*ListSecretsResponse) ProtoMessage() {} +func (*ListSecretsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{46} +} +func (m *ListSecretsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListSecretsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListSecretsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListSecretsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListSecretsResponse.Merge(m, src) +} +func (m *ListSecretsResponse) XXX_Size() int { + return m.Size() +} +func (m *ListSecretsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListSecretsResponse.DiscardUnknown(m) } -func (m *ListSecretsResponse) Reset() { *m = ListSecretsResponse{} } -func (*ListSecretsResponse) ProtoMessage() {} -func (*ListSecretsResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{44} } +var xxx_messageInfo_ListSecretsResponse proto.InternalMessageInfo // CreateSecretRequest specifies a new secret (it will not update an existing // secret) to create. type CreateSecretRequest struct { - Spec *SecretSpec `protobuf:"bytes,1,opt,name=spec" json:"spec,omitempty"` + Spec *SecretSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` +} + +func (m *CreateSecretRequest) Reset() { *m = CreateSecretRequest{} } +func (*CreateSecretRequest) ProtoMessage() {} +func (*CreateSecretRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{47} +} +func (m *CreateSecretRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateSecretRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateSecretRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateSecretRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateSecretRequest.Merge(m, src) +} +func (m *CreateSecretRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateSecretRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateSecretRequest.DiscardUnknown(m) } -func (m *CreateSecretRequest) Reset() { *m = CreateSecretRequest{} } -func (*CreateSecretRequest) ProtoMessage() {} -func (*CreateSecretRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{45} } +var xxx_messageInfo_CreateSecretRequest proto.InternalMessageInfo // CreateSecretResponse contains the newly created `Secret` corresponding to the // name in `CreateSecretRequest`. The `Secret.Spec.Data` field should be nil instead // of actually containing the secret bytes. type CreateSecretResponse struct { - Secret *Secret `protobuf:"bytes,1,opt,name=secret" json:"secret,omitempty"` + Secret *Secret `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"` +} + +func (m *CreateSecretResponse) Reset() { *m = CreateSecretResponse{} } +func (*CreateSecretResponse) ProtoMessage() {} +func (*CreateSecretResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{48} +} +func (m *CreateSecretResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateSecretResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateSecretResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateSecretResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateSecretResponse.Merge(m, src) +} +func (m *CreateSecretResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateSecretResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateSecretResponse.DiscardUnknown(m) } -func (m *CreateSecretResponse) Reset() { *m = CreateSecretResponse{} } -func (*CreateSecretResponse) ProtoMessage() {} -func (*CreateSecretResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{46} } +var xxx_messageInfo_CreateSecretResponse proto.InternalMessageInfo // RemoveSecretRequest contains the ID of the secret that should be removed. This // removes all versions of the secret. @@ -586,112 +2198,418 @@ type RemoveSecretRequest struct { SecretID string `protobuf:"bytes,1,opt,name=secret_id,json=secretId,proto3" json:"secret_id,omitempty"` } -func (m *RemoveSecretRequest) Reset() { *m = RemoveSecretRequest{} } -func (*RemoveSecretRequest) ProtoMessage() {} -func (*RemoveSecretRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{47} } +func (m *RemoveSecretRequest) Reset() { *m = RemoveSecretRequest{} } +func (*RemoveSecretRequest) ProtoMessage() {} +func (*RemoveSecretRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{49} +} +func (m *RemoveSecretRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveSecretRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveSecretRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveSecretRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveSecretRequest.Merge(m, src) +} +func (m *RemoveSecretRequest) XXX_Size() int { + return m.Size() +} +func (m *RemoveSecretRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveSecretRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveSecretRequest proto.InternalMessageInfo // RemoveSecretResponse is an empty object indicating the successful removal of // a secret. type RemoveSecretResponse struct { } -func (m *RemoveSecretResponse) Reset() { *m = RemoveSecretResponse{} } -func (*RemoveSecretResponse) ProtoMessage() {} -func (*RemoveSecretResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{48} } +func (m *RemoveSecretResponse) Reset() { *m = RemoveSecretResponse{} } +func (*RemoveSecretResponse) ProtoMessage() {} +func (*RemoveSecretResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{50} +} +func (m *RemoveSecretResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveSecretResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveSecretResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveSecretResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveSecretResponse.Merge(m, src) +} +func (m *RemoveSecretResponse) XXX_Size() int { + return m.Size() +} +func (m *RemoveSecretResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveSecretResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveSecretResponse proto.InternalMessageInfo // GetConfigRequest is the request to get a `Config` object given a config id. type GetConfigRequest struct { ConfigID string `protobuf:"bytes,1,opt,name=config_id,json=configId,proto3" json:"config_id,omitempty"` } -func (m *GetConfigRequest) Reset() { *m = GetConfigRequest{} } -func (*GetConfigRequest) ProtoMessage() {} -func (*GetConfigRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{49} } +func (m *GetConfigRequest) Reset() { *m = GetConfigRequest{} } +func (*GetConfigRequest) ProtoMessage() {} +func (*GetConfigRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{51} +} +func (m *GetConfigRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetConfigRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetConfigRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetConfigRequest.Merge(m, src) +} +func (m *GetConfigRequest) XXX_Size() int { + return m.Size() +} +func (m *GetConfigRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetConfigRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetConfigRequest proto.InternalMessageInfo // GetConfigResponse contains the Config corresponding to the id in // `GetConfigRequest`. type GetConfigResponse struct { - Config *Config `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + Config *Config `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` +} + +func (m *GetConfigResponse) Reset() { *m = GetConfigResponse{} } +func (*GetConfigResponse) ProtoMessage() {} +func (*GetConfigResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{52} +} +func (m *GetConfigResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetConfigResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetConfigResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetConfigResponse.Merge(m, src) +} +func (m *GetConfigResponse) XXX_Size() int { + return m.Size() +} +func (m *GetConfigResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetConfigResponse.DiscardUnknown(m) } -func (m *GetConfigResponse) Reset() { *m = GetConfigResponse{} } -func (*GetConfigResponse) ProtoMessage() {} -func (*GetConfigResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{50} } +var xxx_messageInfo_GetConfigResponse proto.InternalMessageInfo type UpdateConfigRequest struct { // ConfigID is the config ID to update. ConfigID string `protobuf:"bytes,1,opt,name=config_id,json=configId,proto3" json:"config_id,omitempty"` // ConfigVersion is the version of the config being updated. - ConfigVersion *Version `protobuf:"bytes,2,opt,name=config_version,json=configVersion" json:"config_version,omitempty"` + ConfigVersion *Version `protobuf:"bytes,2,opt,name=config_version,json=configVersion,proto3" json:"config_version,omitempty"` // Spec is the new spec to apply to the Config // Only some fields are allowed to be updated. - Spec *ConfigSpec `protobuf:"bytes,3,opt,name=spec" json:"spec,omitempty"` + Spec *ConfigSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec,omitempty"` +} + +func (m *UpdateConfigRequest) Reset() { *m = UpdateConfigRequest{} } +func (*UpdateConfigRequest) ProtoMessage() {} +func (*UpdateConfigRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{53} +} +func (m *UpdateConfigRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateConfigRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateConfigRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateConfigRequest.Merge(m, src) +} +func (m *UpdateConfigRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateConfigRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateConfigRequest.DiscardUnknown(m) } -func (m *UpdateConfigRequest) Reset() { *m = UpdateConfigRequest{} } -func (*UpdateConfigRequest) ProtoMessage() {} -func (*UpdateConfigRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{51} } +var xxx_messageInfo_UpdateConfigRequest proto.InternalMessageInfo type UpdateConfigResponse struct { - Config *Config `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + Config *Config `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` +} + +func (m *UpdateConfigResponse) Reset() { *m = UpdateConfigResponse{} } +func (*UpdateConfigResponse) ProtoMessage() {} +func (*UpdateConfigResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{54} +} +func (m *UpdateConfigResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateConfigResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateConfigResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateConfigResponse.Merge(m, src) +} +func (m *UpdateConfigResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateConfigResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateConfigResponse.DiscardUnknown(m) } -func (m *UpdateConfigResponse) Reset() { *m = UpdateConfigResponse{} } -func (*UpdateConfigResponse) ProtoMessage() {} -func (*UpdateConfigResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{52} } +var xxx_messageInfo_UpdateConfigResponse proto.InternalMessageInfo // ListConfigRequest is the request to list all configs in the config store, // or all configs filtered by (name or name prefix or id prefix) and labels. type ListConfigsRequest struct { - Filters *ListConfigsRequest_Filters `protobuf:"bytes,1,opt,name=filters" json:"filters,omitempty"` + Filters *ListConfigsRequest_Filters `protobuf:"bytes,1,opt,name=filters,proto3" json:"filters,omitempty"` +} + +func (m *ListConfigsRequest) Reset() { *m = ListConfigsRequest{} } +func (*ListConfigsRequest) ProtoMessage() {} +func (*ListConfigsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{55} +} +func (m *ListConfigsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListConfigsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListConfigsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListConfigsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListConfigsRequest.Merge(m, src) +} +func (m *ListConfigsRequest) XXX_Size() int { + return m.Size() +} +func (m *ListConfigsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListConfigsRequest.DiscardUnknown(m) } -func (m *ListConfigsRequest) Reset() { *m = ListConfigsRequest{} } -func (*ListConfigsRequest) ProtoMessage() {} -func (*ListConfigsRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{53} } +var xxx_messageInfo_ListConfigsRequest proto.InternalMessageInfo type ListConfigsRequest_Filters struct { - Names []string `protobuf:"bytes,1,rep,name=names" json:"names,omitempty"` - IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes" json:"id_prefixes,omitempty"` - Labels map[string]string `protobuf:"bytes,3,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes" json:"name_prefixes,omitempty"` + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes,proto3" json:"id_prefixes,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes,proto3" json:"name_prefixes,omitempty"` } func (m *ListConfigsRequest_Filters) Reset() { *m = ListConfigsRequest_Filters{} } func (*ListConfigsRequest_Filters) ProtoMessage() {} func (*ListConfigsRequest_Filters) Descriptor() ([]byte, []int) { - return fileDescriptorControl, []int{53, 0} + return fileDescriptor_b37401dd08bf8930, []int{55, 0} +} +func (m *ListConfigsRequest_Filters) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListConfigsRequest_Filters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListConfigsRequest_Filters.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListConfigsRequest_Filters) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListConfigsRequest_Filters.Merge(m, src) +} +func (m *ListConfigsRequest_Filters) XXX_Size() int { + return m.Size() +} +func (m *ListConfigsRequest_Filters) XXX_DiscardUnknown() { + xxx_messageInfo_ListConfigsRequest_Filters.DiscardUnknown(m) } +var xxx_messageInfo_ListConfigsRequest_Filters proto.InternalMessageInfo + // ListConfigResponse contains a list of all the configs that match the name or // name prefix filters provided in `ListConfigRequest`. type ListConfigsResponse struct { - Configs []*Config `protobuf:"bytes,1,rep,name=configs" json:"configs,omitempty"` + Configs []*Config `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty"` +} + +func (m *ListConfigsResponse) Reset() { *m = ListConfigsResponse{} } +func (*ListConfigsResponse) ProtoMessage() {} +func (*ListConfigsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{56} +} +func (m *ListConfigsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListConfigsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListConfigsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListConfigsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListConfigsResponse.Merge(m, src) +} +func (m *ListConfigsResponse) XXX_Size() int { + return m.Size() +} +func (m *ListConfigsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListConfigsResponse.DiscardUnknown(m) } -func (m *ListConfigsResponse) Reset() { *m = ListConfigsResponse{} } -func (*ListConfigsResponse) ProtoMessage() {} -func (*ListConfigsResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{54} } +var xxx_messageInfo_ListConfigsResponse proto.InternalMessageInfo // CreateConfigRequest specifies a new config (it will not update an existing // config) to create. type CreateConfigRequest struct { - Spec *ConfigSpec `protobuf:"bytes,1,opt,name=spec" json:"spec,omitempty"` + Spec *ConfigSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` +} + +func (m *CreateConfigRequest) Reset() { *m = CreateConfigRequest{} } +func (*CreateConfigRequest) ProtoMessage() {} +func (*CreateConfigRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{57} +} +func (m *CreateConfigRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateConfigRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateConfigRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateConfigRequest.Merge(m, src) +} +func (m *CreateConfigRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateConfigRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateConfigRequest.DiscardUnknown(m) } -func (m *CreateConfigRequest) Reset() { *m = CreateConfigRequest{} } -func (*CreateConfigRequest) ProtoMessage() {} -func (*CreateConfigRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{55} } +var xxx_messageInfo_CreateConfigRequest proto.InternalMessageInfo // CreateConfigResponse contains the newly created `Config` corresponding to the // name in `CreateConfigRequest`. type CreateConfigResponse struct { - Config *Config `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + Config *Config `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` +} + +func (m *CreateConfigResponse) Reset() { *m = CreateConfigResponse{} } +func (*CreateConfigResponse) ProtoMessage() {} +func (*CreateConfigResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{58} +} +func (m *CreateConfigResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateConfigResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CreateConfigResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateConfigResponse.Merge(m, src) +} +func (m *CreateConfigResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateConfigResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateConfigResponse.DiscardUnknown(m) } -func (m *CreateConfigResponse) Reset() { *m = CreateConfigResponse{} } -func (*CreateConfigResponse) ProtoMessage() {} -func (*CreateConfigResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{56} } +var xxx_messageInfo_CreateConfigResponse proto.InternalMessageInfo // RemoveConfigRequest contains the ID of the config that should be removed. This // removes all versions of the config. @@ -699,858 +2617,1389 @@ type RemoveConfigRequest struct { ConfigID string `protobuf:"bytes,1,opt,name=config_id,json=configId,proto3" json:"config_id,omitempty"` } -func (m *RemoveConfigRequest) Reset() { *m = RemoveConfigRequest{} } -func (*RemoveConfigRequest) ProtoMessage() {} -func (*RemoveConfigRequest) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{57} } +func (m *RemoveConfigRequest) Reset() { *m = RemoveConfigRequest{} } +func (*RemoveConfigRequest) ProtoMessage() {} +func (*RemoveConfigRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{59} +} +func (m *RemoveConfigRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveConfigRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveConfigRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveConfigRequest.Merge(m, src) +} +func (m *RemoveConfigRequest) XXX_Size() int { + return m.Size() +} +func (m *RemoveConfigRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveConfigRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveConfigRequest proto.InternalMessageInfo // RemoveConfigResponse is an empty object indicating the successful removal of // a config. type RemoveConfigResponse struct { } -func (m *RemoveConfigResponse) Reset() { *m = RemoveConfigResponse{} } -func (*RemoveConfigResponse) ProtoMessage() {} -func (*RemoveConfigResponse) Descriptor() ([]byte, []int) { return fileDescriptorControl, []int{58} } - -func init() { - proto.RegisterType((*GetNodeRequest)(nil), "docker.swarmkit.v1.GetNodeRequest") - proto.RegisterType((*GetNodeResponse)(nil), "docker.swarmkit.v1.GetNodeResponse") - proto.RegisterType((*ListNodesRequest)(nil), "docker.swarmkit.v1.ListNodesRequest") - proto.RegisterType((*ListNodesRequest_Filters)(nil), "docker.swarmkit.v1.ListNodesRequest.Filters") - proto.RegisterType((*ListNodesResponse)(nil), "docker.swarmkit.v1.ListNodesResponse") - proto.RegisterType((*UpdateNodeRequest)(nil), "docker.swarmkit.v1.UpdateNodeRequest") - proto.RegisterType((*UpdateNodeResponse)(nil), "docker.swarmkit.v1.UpdateNodeResponse") - proto.RegisterType((*RemoveNodeRequest)(nil), "docker.swarmkit.v1.RemoveNodeRequest") - proto.RegisterType((*RemoveNodeResponse)(nil), "docker.swarmkit.v1.RemoveNodeResponse") - proto.RegisterType((*GetTaskRequest)(nil), "docker.swarmkit.v1.GetTaskRequest") - proto.RegisterType((*GetTaskResponse)(nil), "docker.swarmkit.v1.GetTaskResponse") - proto.RegisterType((*RemoveTaskRequest)(nil), "docker.swarmkit.v1.RemoveTaskRequest") - proto.RegisterType((*RemoveTaskResponse)(nil), "docker.swarmkit.v1.RemoveTaskResponse") - proto.RegisterType((*ListTasksRequest)(nil), "docker.swarmkit.v1.ListTasksRequest") - proto.RegisterType((*ListTasksRequest_Filters)(nil), "docker.swarmkit.v1.ListTasksRequest.Filters") - proto.RegisterType((*ListTasksResponse)(nil), "docker.swarmkit.v1.ListTasksResponse") - proto.RegisterType((*CreateServiceRequest)(nil), "docker.swarmkit.v1.CreateServiceRequest") - proto.RegisterType((*CreateServiceResponse)(nil), "docker.swarmkit.v1.CreateServiceResponse") - proto.RegisterType((*GetServiceRequest)(nil), "docker.swarmkit.v1.GetServiceRequest") - proto.RegisterType((*GetServiceResponse)(nil), "docker.swarmkit.v1.GetServiceResponse") - proto.RegisterType((*UpdateServiceRequest)(nil), "docker.swarmkit.v1.UpdateServiceRequest") - proto.RegisterType((*UpdateServiceResponse)(nil), "docker.swarmkit.v1.UpdateServiceResponse") - proto.RegisterType((*RemoveServiceRequest)(nil), "docker.swarmkit.v1.RemoveServiceRequest") - proto.RegisterType((*RemoveServiceResponse)(nil), "docker.swarmkit.v1.RemoveServiceResponse") - proto.RegisterType((*ListServicesRequest)(nil), "docker.swarmkit.v1.ListServicesRequest") - proto.RegisterType((*ListServicesRequest_Filters)(nil), "docker.swarmkit.v1.ListServicesRequest.Filters") - proto.RegisterType((*ListServicesResponse)(nil), "docker.swarmkit.v1.ListServicesResponse") - proto.RegisterType((*CreateNetworkRequest)(nil), "docker.swarmkit.v1.CreateNetworkRequest") - proto.RegisterType((*CreateNetworkResponse)(nil), "docker.swarmkit.v1.CreateNetworkResponse") - proto.RegisterType((*GetNetworkRequest)(nil), "docker.swarmkit.v1.GetNetworkRequest") - proto.RegisterType((*GetNetworkResponse)(nil), "docker.swarmkit.v1.GetNetworkResponse") - proto.RegisterType((*RemoveNetworkRequest)(nil), "docker.swarmkit.v1.RemoveNetworkRequest") - proto.RegisterType((*RemoveNetworkResponse)(nil), "docker.swarmkit.v1.RemoveNetworkResponse") - proto.RegisterType((*ListNetworksRequest)(nil), "docker.swarmkit.v1.ListNetworksRequest") - proto.RegisterType((*ListNetworksRequest_Filters)(nil), "docker.swarmkit.v1.ListNetworksRequest.Filters") - proto.RegisterType((*ListNetworksResponse)(nil), "docker.swarmkit.v1.ListNetworksResponse") - proto.RegisterType((*GetClusterRequest)(nil), "docker.swarmkit.v1.GetClusterRequest") - proto.RegisterType((*GetClusterResponse)(nil), "docker.swarmkit.v1.GetClusterResponse") - proto.RegisterType((*ListClustersRequest)(nil), "docker.swarmkit.v1.ListClustersRequest") - proto.RegisterType((*ListClustersRequest_Filters)(nil), "docker.swarmkit.v1.ListClustersRequest.Filters") - proto.RegisterType((*ListClustersResponse)(nil), "docker.swarmkit.v1.ListClustersResponse") - proto.RegisterType((*KeyRotation)(nil), "docker.swarmkit.v1.KeyRotation") - proto.RegisterType((*UpdateClusterRequest)(nil), "docker.swarmkit.v1.UpdateClusterRequest") - proto.RegisterType((*UpdateClusterResponse)(nil), "docker.swarmkit.v1.UpdateClusterResponse") - proto.RegisterType((*GetSecretRequest)(nil), "docker.swarmkit.v1.GetSecretRequest") - proto.RegisterType((*GetSecretResponse)(nil), "docker.swarmkit.v1.GetSecretResponse") - proto.RegisterType((*UpdateSecretRequest)(nil), "docker.swarmkit.v1.UpdateSecretRequest") - proto.RegisterType((*UpdateSecretResponse)(nil), "docker.swarmkit.v1.UpdateSecretResponse") - proto.RegisterType((*ListSecretsRequest)(nil), "docker.swarmkit.v1.ListSecretsRequest") - proto.RegisterType((*ListSecretsRequest_Filters)(nil), "docker.swarmkit.v1.ListSecretsRequest.Filters") - proto.RegisterType((*ListSecretsResponse)(nil), "docker.swarmkit.v1.ListSecretsResponse") - proto.RegisterType((*CreateSecretRequest)(nil), "docker.swarmkit.v1.CreateSecretRequest") - proto.RegisterType((*CreateSecretResponse)(nil), "docker.swarmkit.v1.CreateSecretResponse") - proto.RegisterType((*RemoveSecretRequest)(nil), "docker.swarmkit.v1.RemoveSecretRequest") - proto.RegisterType((*RemoveSecretResponse)(nil), "docker.swarmkit.v1.RemoveSecretResponse") - proto.RegisterType((*GetConfigRequest)(nil), "docker.swarmkit.v1.GetConfigRequest") - proto.RegisterType((*GetConfigResponse)(nil), "docker.swarmkit.v1.GetConfigResponse") - proto.RegisterType((*UpdateConfigRequest)(nil), "docker.swarmkit.v1.UpdateConfigRequest") - proto.RegisterType((*UpdateConfigResponse)(nil), "docker.swarmkit.v1.UpdateConfigResponse") - proto.RegisterType((*ListConfigsRequest)(nil), "docker.swarmkit.v1.ListConfigsRequest") - proto.RegisterType((*ListConfigsRequest_Filters)(nil), "docker.swarmkit.v1.ListConfigsRequest.Filters") - proto.RegisterType((*ListConfigsResponse)(nil), "docker.swarmkit.v1.ListConfigsResponse") - proto.RegisterType((*CreateConfigRequest)(nil), "docker.swarmkit.v1.CreateConfigRequest") - proto.RegisterType((*CreateConfigResponse)(nil), "docker.swarmkit.v1.CreateConfigResponse") - proto.RegisterType((*RemoveConfigRequest)(nil), "docker.swarmkit.v1.RemoveConfigRequest") - proto.RegisterType((*RemoveConfigResponse)(nil), "docker.swarmkit.v1.RemoveConfigResponse") - proto.RegisterEnum("docker.swarmkit.v1.UpdateServiceRequest_Rollback", UpdateServiceRequest_Rollback_name, UpdateServiceRequest_Rollback_value) +func (m *RemoveConfigResponse) Reset() { *m = RemoveConfigResponse{} } +func (*RemoveConfigResponse) ProtoMessage() {} +func (*RemoveConfigResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{60} } - -type authenticatedWrapperControlServer struct { - local ControlServer - authorize func(context.Context, []string) error +func (m *RemoveConfigResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) } - -func NewAuthenticatedWrapperControlServer(local ControlServer, authorize func(context.Context, []string) error) ControlServer { - return &authenticatedWrapperControlServer{ - local: local, - authorize: authorize, +func (m *RemoveConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveConfigResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } } - -func (p *authenticatedWrapperControlServer) GetNode(ctx context.Context, r *GetNodeRequest) (*GetNodeResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.GetNode(ctx, r) +func (m *RemoveConfigResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveConfigResponse.Merge(m, src) } - -func (p *authenticatedWrapperControlServer) ListNodes(ctx context.Context, r *ListNodesRequest) (*ListNodesResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.ListNodes(ctx, r) +func (m *RemoveConfigResponse) XXX_Size() int { + return m.Size() +} +func (m *RemoveConfigResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveConfigResponse.DiscardUnknown(m) } -func (p *authenticatedWrapperControlServer) UpdateNode(ctx context.Context, r *UpdateNodeRequest) (*UpdateNodeResponse, error) { +var xxx_messageInfo_RemoveConfigResponse proto.InternalMessageInfo - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.UpdateNode(ctx, r) +// CreateExtensionRequest creates a new extension as specified by the provided +// parameters +type CreateExtensionRequest struct { + Annotations *Annotations `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` } -func (p *authenticatedWrapperControlServer) RemoveNode(ctx context.Context, r *RemoveNodeRequest) (*RemoveNodeResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err +func (m *CreateExtensionRequest) Reset() { *m = CreateExtensionRequest{} } +func (*CreateExtensionRequest) ProtoMessage() {} +func (*CreateExtensionRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{61} +} +func (m *CreateExtensionRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateExtensionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateExtensionRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return p.local.RemoveNode(ctx, r) +} +func (m *CreateExtensionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateExtensionRequest.Merge(m, src) +} +func (m *CreateExtensionRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateExtensionRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateExtensionRequest.DiscardUnknown(m) } -func (p *authenticatedWrapperControlServer) GetTask(ctx context.Context, r *GetTaskRequest) (*GetTaskResponse, error) { +var xxx_messageInfo_CreateExtensionRequest proto.InternalMessageInfo - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.GetTask(ctx, r) +// CreateExtensionResponse contains the newly created `Extension` corresponding +// to the parameters in the CreateExtensionRequest. +type CreateExtensionResponse struct { + Extension *Extension `protobuf:"bytes,1,opt,name=extension,proto3" json:"extension,omitempty"` } -func (p *authenticatedWrapperControlServer) ListTasks(ctx context.Context, r *ListTasksRequest) (*ListTasksResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err +func (m *CreateExtensionResponse) Reset() { *m = CreateExtensionResponse{} } +func (*CreateExtensionResponse) ProtoMessage() {} +func (*CreateExtensionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{62} +} +func (m *CreateExtensionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateExtensionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateExtensionResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return p.local.ListTasks(ctx, r) +} +func (m *CreateExtensionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateExtensionResponse.Merge(m, src) +} +func (m *CreateExtensionResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateExtensionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateExtensionResponse.DiscardUnknown(m) } -func (p *authenticatedWrapperControlServer) RemoveTask(ctx context.Context, r *RemoveTaskRequest) (*RemoveTaskResponse, error) { +var xxx_messageInfo_CreateExtensionResponse proto.InternalMessageInfo - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.RemoveTask(ctx, r) +// RemoveExtensionRequest contains the ID of the extension that should be removed. This +// removes all versions of the extension. +type RemoveExtensionRequest struct { + ExtensionID string `protobuf:"bytes,1,opt,name=extension_id,json=extensionId,proto3" json:"extension_id,omitempty"` } -func (p *authenticatedWrapperControlServer) GetService(ctx context.Context, r *GetServiceRequest) (*GetServiceResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err +func (m *RemoveExtensionRequest) Reset() { *m = RemoveExtensionRequest{} } +func (*RemoveExtensionRequest) ProtoMessage() {} +func (*RemoveExtensionRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{63} +} +func (m *RemoveExtensionRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveExtensionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveExtensionRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return p.local.GetService(ctx, r) +} +func (m *RemoveExtensionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveExtensionRequest.Merge(m, src) +} +func (m *RemoveExtensionRequest) XXX_Size() int { + return m.Size() +} +func (m *RemoveExtensionRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveExtensionRequest.DiscardUnknown(m) } -func (p *authenticatedWrapperControlServer) ListServices(ctx context.Context, r *ListServicesRequest) (*ListServicesResponse, error) { +var xxx_messageInfo_RemoveExtensionRequest proto.InternalMessageInfo - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.ListServices(ctx, r) +// RemoveExtensionResponse is an empty object indicating the successful removal +// of an extension. +type RemoveExtensionResponse struct { } -func (p *authenticatedWrapperControlServer) CreateService(ctx context.Context, r *CreateServiceRequest) (*CreateServiceResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err +func (m *RemoveExtensionResponse) Reset() { *m = RemoveExtensionResponse{} } +func (*RemoveExtensionResponse) ProtoMessage() {} +func (*RemoveExtensionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{64} +} +func (m *RemoveExtensionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveExtensionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveExtensionResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return p.local.CreateService(ctx, r) +} +func (m *RemoveExtensionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveExtensionResponse.Merge(m, src) +} +func (m *RemoveExtensionResponse) XXX_Size() int { + return m.Size() +} +func (m *RemoveExtensionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveExtensionResponse.DiscardUnknown(m) } -func (p *authenticatedWrapperControlServer) UpdateService(ctx context.Context, r *UpdateServiceRequest) (*UpdateServiceResponse, error) { +var xxx_messageInfo_RemoveExtensionResponse proto.InternalMessageInfo - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.UpdateService(ctx, r) +// GetResourceRequest is the request to get a Extension object given a extension id. +type GetExtensionRequest struct { + ExtensionID string `protobuf:"bytes,1,opt,name=extension_id,json=extensionId,proto3" json:"extension_id,omitempty"` } -func (p *authenticatedWrapperControlServer) RemoveService(ctx context.Context, r *RemoveServiceRequest) (*RemoveServiceResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err +func (m *GetExtensionRequest) Reset() { *m = GetExtensionRequest{} } +func (*GetExtensionRequest) ProtoMessage() {} +func (*GetExtensionRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{65} +} +func (m *GetExtensionRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetExtensionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetExtensionRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return p.local.RemoveService(ctx, r) +} +func (m *GetExtensionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExtensionRequest.Merge(m, src) +} +func (m *GetExtensionRequest) XXX_Size() int { + return m.Size() +} +func (m *GetExtensionRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetExtensionRequest.DiscardUnknown(m) } -func (p *authenticatedWrapperControlServer) GetNetwork(ctx context.Context, r *GetNetworkRequest) (*GetNetworkResponse, error) { +var xxx_messageInfo_GetExtensionRequest proto.InternalMessageInfo - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.GetNetwork(ctx, r) +// GetExtensionResponse contains the Extension corresponding to the id in +// `GetExtensionRequest`. +type GetExtensionResponse struct { + Extension *Extension `protobuf:"bytes,1,opt,name=extension,proto3" json:"extension,omitempty"` } -func (p *authenticatedWrapperControlServer) ListNetworks(ctx context.Context, r *ListNetworksRequest) (*ListNetworksResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err +func (m *GetExtensionResponse) Reset() { *m = GetExtensionResponse{} } +func (*GetExtensionResponse) ProtoMessage() {} +func (*GetExtensionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{66} +} +func (m *GetExtensionResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetExtensionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetExtensionResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return p.local.ListNetworks(ctx, r) +} +func (m *GetExtensionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExtensionResponse.Merge(m, src) +} +func (m *GetExtensionResponse) XXX_Size() int { + return m.Size() +} +func (m *GetExtensionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetExtensionResponse.DiscardUnknown(m) } -func (p *authenticatedWrapperControlServer) CreateNetwork(ctx context.Context, r *CreateNetworkRequest) (*CreateNetworkResponse, error) { +var xxx_messageInfo_GetExtensionResponse proto.InternalMessageInfo - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.CreateNetwork(ctx, r) +// CreateResourceRequest creates a new resource specified by the included +// resource object. An existing resource will not be updated. +type CreateResourceRequest struct { + Annotations *Annotations `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations,omitempty"` + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` + Payload *types.Any `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` } -func (p *authenticatedWrapperControlServer) RemoveNetwork(ctx context.Context, r *RemoveNetworkRequest) (*RemoveNetworkResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err +func (m *CreateResourceRequest) Reset() { *m = CreateResourceRequest{} } +func (*CreateResourceRequest) ProtoMessage() {} +func (*CreateResourceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{67} +} +func (m *CreateResourceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateResourceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateResourceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return p.local.RemoveNetwork(ctx, r) +} +func (m *CreateResourceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateResourceRequest.Merge(m, src) +} +func (m *CreateResourceRequest) XXX_Size() int { + return m.Size() +} +func (m *CreateResourceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CreateResourceRequest.DiscardUnknown(m) } -func (p *authenticatedWrapperControlServer) GetCluster(ctx context.Context, r *GetClusterRequest) (*GetClusterResponse, error) { +var xxx_messageInfo_CreateResourceRequest proto.InternalMessageInfo - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.GetCluster(ctx, r) +// CreateResourceResponse contains the newly created `Resource` corresponding +// to the resource in the CreateResourceRequest. +type CreateResourceResponse struct { + Resource *Resource `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` } -func (p *authenticatedWrapperControlServer) ListClusters(ctx context.Context, r *ListClustersRequest) (*ListClustersResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err +func (m *CreateResourceResponse) Reset() { *m = CreateResourceResponse{} } +func (*CreateResourceResponse) ProtoMessage() {} +func (*CreateResourceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{68} +} +func (m *CreateResourceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CreateResourceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CreateResourceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return p.local.ListClusters(ctx, r) +} +func (m *CreateResourceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CreateResourceResponse.Merge(m, src) +} +func (m *CreateResourceResponse) XXX_Size() int { + return m.Size() +} +func (m *CreateResourceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CreateResourceResponse.DiscardUnknown(m) } -func (p *authenticatedWrapperControlServer) UpdateCluster(ctx context.Context, r *UpdateClusterRequest) (*UpdateClusterResponse, error) { +var xxx_messageInfo_CreateResourceResponse proto.InternalMessageInfo - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.UpdateCluster(ctx, r) +// RemoveResourceRequest contains the ID of the resource that should be removed. This +// removes all versions of the resource. +type RemoveResourceRequest struct { + ResourceID string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` } -func (p *authenticatedWrapperControlServer) GetSecret(ctx context.Context, r *GetSecretRequest) (*GetSecretResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.GetSecret(ctx, r) +func (m *RemoveResourceRequest) Reset() { *m = RemoveResourceRequest{} } +func (*RemoveResourceRequest) ProtoMessage() {} +func (*RemoveResourceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{69} } - -func (p *authenticatedWrapperControlServer) UpdateSecret(ctx context.Context, r *UpdateSecretRequest) (*UpdateSecretResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.UpdateSecret(ctx, r) +func (m *RemoveResourceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) } - -func (p *authenticatedWrapperControlServer) ListSecrets(ctx context.Context, r *ListSecretsRequest) (*ListSecretsResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err +func (m *RemoveResourceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveResourceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return p.local.ListSecrets(ctx, r) } - -func (p *authenticatedWrapperControlServer) CreateSecret(ctx context.Context, r *CreateSecretRequest) (*CreateSecretResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.CreateSecret(ctx, r) +func (m *RemoveResourceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveResourceRequest.Merge(m, src) } - -func (p *authenticatedWrapperControlServer) RemoveSecret(ctx context.Context, r *RemoveSecretRequest) (*RemoveSecretResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.RemoveSecret(ctx, r) +func (m *RemoveResourceRequest) XXX_Size() int { + return m.Size() } - -func (p *authenticatedWrapperControlServer) GetConfig(ctx context.Context, r *GetConfigRequest) (*GetConfigResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.GetConfig(ctx, r) +func (m *RemoveResourceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveResourceRequest.DiscardUnknown(m) } -func (p *authenticatedWrapperControlServer) UpdateConfig(ctx context.Context, r *UpdateConfigRequest) (*UpdateConfigResponse, error) { +var xxx_messageInfo_RemoveResourceRequest proto.InternalMessageInfo - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.UpdateConfig(ctx, r) +// RemoveResourceResponse is an empty object indicating the successful removal +// of a resource. +type RemoveResourceResponse struct { } -func (p *authenticatedWrapperControlServer) ListConfigs(ctx context.Context, r *ListConfigsRequest) (*ListConfigsResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.ListConfigs(ctx, r) +func (m *RemoveResourceResponse) Reset() { *m = RemoveResourceResponse{} } +func (*RemoveResourceResponse) ProtoMessage() {} +func (*RemoveResourceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{70} } - -func (p *authenticatedWrapperControlServer) CreateConfig(ctx context.Context, r *CreateConfigRequest) (*CreateConfigResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err - } - return p.local.CreateConfig(ctx, r) +func (m *RemoveResourceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) } - -func (p *authenticatedWrapperControlServer) RemoveConfig(ctx context.Context, r *RemoveConfigRequest) (*RemoveConfigResponse, error) { - - if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { - return nil, err +func (m *RemoveResourceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveResourceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return p.local.RemoveConfig(ctx, r) } - -func (m *GetNodeRequest) Copy() *GetNodeRequest { - if m == nil { - return nil - } - o := &GetNodeRequest{} - o.CopyFrom(m) - return o +func (m *RemoveResourceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveResourceResponse.Merge(m, src) } - -func (m *GetNodeRequest) CopyFrom(src interface{}) { - - o := src.(*GetNodeRequest) - *m = *o +func (m *RemoveResourceResponse) XXX_Size() int { + return m.Size() } - -func (m *GetNodeResponse) Copy() *GetNodeResponse { - if m == nil { - return nil - } - o := &GetNodeResponse{} - o.CopyFrom(m) - return o +func (m *RemoveResourceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveResourceResponse.DiscardUnknown(m) } -func (m *GetNodeResponse) CopyFrom(src interface{}) { +var xxx_messageInfo_RemoveResourceResponse proto.InternalMessageInfo - o := src.(*GetNodeResponse) - *m = *o - if o.Node != nil { - m.Node = &Node{} - deepcopy.Copy(m.Node, o.Node) - } +// UpdateResourceRequest updates the resource specified by the given resource object. +type UpdateResourceRequest struct { + ResourceID string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + ResourceVersion *Version `protobuf:"bytes,2,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` + // Annotations describes the annotations to update. If the Annotations should + // be unchanged, then this field should be left empty. Note that the name of + // a Resource cannot be changed, only its labels. + Annotations *Annotations `protobuf:"bytes,3,opt,name=annotations,proto3" json:"annotations,omitempty"` + // Payload describes the new payload of the resource. If the Payload should + // be unchanged, then this field should be left empty. + Payload *types.Any `protobuf:"bytes,4,opt,name=payload,proto3" json:"payload,omitempty"` } -func (m *ListNodesRequest) Copy() *ListNodesRequest { - if m == nil { - return nil +func (m *UpdateResourceRequest) Reset() { *m = UpdateResourceRequest{} } +func (*UpdateResourceRequest) ProtoMessage() {} +func (*UpdateResourceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{71} +} +func (m *UpdateResourceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateResourceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateResourceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - o := &ListNodesRequest{} - o.CopyFrom(m) - return o +} +func (m *UpdateResourceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateResourceRequest.Merge(m, src) +} +func (m *UpdateResourceRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateResourceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateResourceRequest.DiscardUnknown(m) } -func (m *ListNodesRequest) CopyFrom(src interface{}) { +var xxx_messageInfo_UpdateResourceRequest proto.InternalMessageInfo - o := src.(*ListNodesRequest) - *m = *o - if o.Filters != nil { - m.Filters = &ListNodesRequest_Filters{} - deepcopy.Copy(m.Filters, o.Filters) - } +type UpdateResourceResponse struct { + Resource *Resource `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` } -func (m *ListNodesRequest_Filters) Copy() *ListNodesRequest_Filters { - if m == nil { - return nil +func (m *UpdateResourceResponse) Reset() { *m = UpdateResourceResponse{} } +func (*UpdateResourceResponse) ProtoMessage() {} +func (*UpdateResourceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{72} +} +func (m *UpdateResourceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateResourceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateResourceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - o := &ListNodesRequest_Filters{} - o.CopyFrom(m) - return o +} +func (m *UpdateResourceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateResourceResponse.Merge(m, src) +} +func (m *UpdateResourceResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateResourceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateResourceResponse.DiscardUnknown(m) } -func (m *ListNodesRequest_Filters) CopyFrom(src interface{}) { - - o := src.(*ListNodesRequest_Filters) - *m = *o - if o.Names != nil { - m.Names = make([]string, len(o.Names)) - copy(m.Names, o.Names) - } +var xxx_messageInfo_UpdateResourceResponse proto.InternalMessageInfo - if o.IDPrefixes != nil { - m.IDPrefixes = make([]string, len(o.IDPrefixes)) - copy(m.IDPrefixes, o.IDPrefixes) - } +// GetResourceRequest is the request to get a Resource object given a resource id. +type GetResourceRequest struct { + ResourceID string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` +} - if o.Labels != nil { - m.Labels = make(map[string]string, len(o.Labels)) - for k, v := range o.Labels { - m.Labels[k] = v +func (m *GetResourceRequest) Reset() { *m = GetResourceRequest{} } +func (*GetResourceRequest) ProtoMessage() {} +func (*GetResourceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{73} +} +func (m *GetResourceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetResourceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetResourceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err } + return b[:n], nil } +} +func (m *GetResourceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetResourceRequest.Merge(m, src) +} +func (m *GetResourceRequest) XXX_Size() int { + return m.Size() +} +func (m *GetResourceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetResourceRequest.DiscardUnknown(m) +} - if o.NodeLabels != nil { - m.NodeLabels = make(map[string]string, len(o.NodeLabels)) - for k, v := range o.NodeLabels { - m.NodeLabels[k] = v - } - } +var xxx_messageInfo_GetResourceRequest proto.InternalMessageInfo - if o.Memberships != nil { - m.Memberships = make([]NodeSpec_Membership, len(o.Memberships)) - copy(m.Memberships, o.Memberships) - } +// GetResourceResponse contains the Resource corresponding to the id in +// `GetResourceRequest`. +type GetResourceResponse struct { + Resource *Resource `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` +} - if o.Roles != nil { - m.Roles = make([]NodeRole, len(o.Roles)) - copy(m.Roles, o.Roles) +func (m *GetResourceResponse) Reset() { *m = GetResourceResponse{} } +func (*GetResourceResponse) ProtoMessage() {} +func (*GetResourceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{74} +} +func (m *GetResourceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetResourceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetResourceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } +} +func (m *GetResourceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetResourceResponse.Merge(m, src) +} +func (m *GetResourceResponse) XXX_Size() int { + return m.Size() +} +func (m *GetResourceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetResourceResponse.DiscardUnknown(m) +} - if o.NamePrefixes != nil { - m.NamePrefixes = make([]string, len(o.NamePrefixes)) - copy(m.NamePrefixes, o.NamePrefixes) - } +var xxx_messageInfo_GetResourceResponse proto.InternalMessageInfo +// ListResourcesRequest is the request to list all resources in the raft store, +// or all resources filtered by (name or name prefix or id prefix), labels and extension. +type ListResourcesRequest struct { + Filters *ListResourcesRequest_Filters `protobuf:"bytes,1,opt,name=filters,proto3" json:"filters,omitempty"` } -func (m *ListNodesResponse) Copy() *ListNodesResponse { - if m == nil { - return nil - } - o := &ListNodesResponse{} - o.CopyFrom(m) - return o +func (m *ListResourcesRequest) Reset() { *m = ListResourcesRequest{} } +func (*ListResourcesRequest) ProtoMessage() {} +func (*ListResourcesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{75} } - -func (m *ListNodesResponse) CopyFrom(src interface{}) { - - o := src.(*ListNodesResponse) - *m = *o - if o.Nodes != nil { - m.Nodes = make([]*Node, len(o.Nodes)) - for i := range m.Nodes { - m.Nodes[i] = &Node{} - deepcopy.Copy(m.Nodes[i], o.Nodes[i]) +func (m *ListResourcesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListResourcesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListResourcesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err } + return b[:n], nil } - } - -func (m *UpdateNodeRequest) Copy() *UpdateNodeRequest { - if m == nil { - return nil - } - o := &UpdateNodeRequest{} - o.CopyFrom(m) - return o +func (m *ListResourcesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListResourcesRequest.Merge(m, src) +} +func (m *ListResourcesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListResourcesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListResourcesRequest.DiscardUnknown(m) } -func (m *UpdateNodeRequest) CopyFrom(src interface{}) { +var xxx_messageInfo_ListResourcesRequest proto.InternalMessageInfo - o := src.(*UpdateNodeRequest) - *m = *o - if o.NodeVersion != nil { - m.NodeVersion = &Version{} - deepcopy.Copy(m.NodeVersion, o.NodeVersion) - } - if o.Spec != nil { - m.Spec = &NodeSpec{} - deepcopy.Copy(m.Spec, o.Spec) - } +type ListResourcesRequest_Filters struct { + Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"` + IDPrefixes []string `protobuf:"bytes,2,rep,name=id_prefixes,json=idPrefixes,proto3" json:"id_prefixes,omitempty"` + Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + NamePrefixes []string `protobuf:"bytes,4,rep,name=name_prefixes,json=namePrefixes,proto3" json:"name_prefixes,omitempty"` + Kind string `protobuf:"bytes,5,opt,name=kind,proto3" json:"kind,omitempty"` } -func (m *UpdateNodeResponse) Copy() *UpdateNodeResponse { - if m == nil { - return nil +func (m *ListResourcesRequest_Filters) Reset() { *m = ListResourcesRequest_Filters{} } +func (*ListResourcesRequest_Filters) ProtoMessage() {} +func (*ListResourcesRequest_Filters) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{75, 0} +} +func (m *ListResourcesRequest_Filters) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListResourcesRequest_Filters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListResourcesRequest_Filters.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - o := &UpdateNodeResponse{} - o.CopyFrom(m) - return o +} +func (m *ListResourcesRequest_Filters) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListResourcesRequest_Filters.Merge(m, src) +} +func (m *ListResourcesRequest_Filters) XXX_Size() int { + return m.Size() +} +func (m *ListResourcesRequest_Filters) XXX_DiscardUnknown() { + xxx_messageInfo_ListResourcesRequest_Filters.DiscardUnknown(m) } -func (m *UpdateNodeResponse) CopyFrom(src interface{}) { +var xxx_messageInfo_ListResourcesRequest_Filters proto.InternalMessageInfo - o := src.(*UpdateNodeResponse) - *m = *o - if o.Node != nil { - m.Node = &Node{} - deepcopy.Copy(m.Node, o.Node) - } +// ListResourcesResponse contains a list of all the resources that match the name or +// name prefix filters provided in `ListResourcesRequest`. +type ListResourcesResponse struct { + Resources []*Resource `protobuf:"bytes,1,rep,name=resources,proto3" json:"resources,omitempty"` } -func (m *RemoveNodeRequest) Copy() *RemoveNodeRequest { - if m == nil { - return nil +func (m *ListResourcesResponse) Reset() { *m = ListResourcesResponse{} } +func (*ListResourcesResponse) ProtoMessage() {} +func (*ListResourcesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b37401dd08bf8930, []int{76} +} +func (m *ListResourcesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListResourcesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListResourcesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - o := &RemoveNodeRequest{} - o.CopyFrom(m) - return o +} +func (m *ListResourcesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListResourcesResponse.Merge(m, src) +} +func (m *ListResourcesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListResourcesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListResourcesResponse.DiscardUnknown(m) } -func (m *RemoveNodeRequest) CopyFrom(src interface{}) { +var xxx_messageInfo_ListResourcesResponse proto.InternalMessageInfo - o := src.(*RemoveNodeRequest) - *m = *o +func init() { + proto.RegisterEnum("docker.swarmkit.v1.UpdateServiceRequest_Rollback", UpdateServiceRequest_Rollback_name, UpdateServiceRequest_Rollback_value) + proto.RegisterType((*GetNodeRequest)(nil), "docker.swarmkit.v1.GetNodeRequest") + proto.RegisterType((*GetNodeResponse)(nil), "docker.swarmkit.v1.GetNodeResponse") + proto.RegisterType((*ListNodesRequest)(nil), "docker.swarmkit.v1.ListNodesRequest") + proto.RegisterType((*ListNodesRequest_Filters)(nil), "docker.swarmkit.v1.ListNodesRequest.Filters") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ListNodesRequest.Filters.LabelsEntry") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ListNodesRequest.Filters.NodeLabelsEntry") + proto.RegisterType((*ListNodesResponse)(nil), "docker.swarmkit.v1.ListNodesResponse") + proto.RegisterType((*UpdateNodeRequest)(nil), "docker.swarmkit.v1.UpdateNodeRequest") + proto.RegisterType((*UpdateNodeResponse)(nil), "docker.swarmkit.v1.UpdateNodeResponse") + proto.RegisterType((*RemoveNodeRequest)(nil), "docker.swarmkit.v1.RemoveNodeRequest") + proto.RegisterType((*RemoveNodeResponse)(nil), "docker.swarmkit.v1.RemoveNodeResponse") + proto.RegisterType((*GetTaskRequest)(nil), "docker.swarmkit.v1.GetTaskRequest") + proto.RegisterType((*GetTaskResponse)(nil), "docker.swarmkit.v1.GetTaskResponse") + proto.RegisterType((*RemoveTaskRequest)(nil), "docker.swarmkit.v1.RemoveTaskRequest") + proto.RegisterType((*RemoveTaskResponse)(nil), "docker.swarmkit.v1.RemoveTaskResponse") + proto.RegisterType((*ListTasksRequest)(nil), "docker.swarmkit.v1.ListTasksRequest") + proto.RegisterType((*ListTasksRequest_Filters)(nil), "docker.swarmkit.v1.ListTasksRequest.Filters") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ListTasksRequest.Filters.LabelsEntry") + proto.RegisterType((*ListTasksResponse)(nil), "docker.swarmkit.v1.ListTasksResponse") + proto.RegisterType((*CreateServiceRequest)(nil), "docker.swarmkit.v1.CreateServiceRequest") + proto.RegisterType((*CreateServiceResponse)(nil), "docker.swarmkit.v1.CreateServiceResponse") + proto.RegisterType((*GetServiceRequest)(nil), "docker.swarmkit.v1.GetServiceRequest") + proto.RegisterType((*GetServiceResponse)(nil), "docker.swarmkit.v1.GetServiceResponse") + proto.RegisterType((*UpdateServiceRequest)(nil), "docker.swarmkit.v1.UpdateServiceRequest") + proto.RegisterType((*UpdateServiceResponse)(nil), "docker.swarmkit.v1.UpdateServiceResponse") + proto.RegisterType((*RemoveServiceRequest)(nil), "docker.swarmkit.v1.RemoveServiceRequest") + proto.RegisterType((*RemoveServiceResponse)(nil), "docker.swarmkit.v1.RemoveServiceResponse") + proto.RegisterType((*ListServicesRequest)(nil), "docker.swarmkit.v1.ListServicesRequest") + proto.RegisterType((*ListServicesRequest_Filters)(nil), "docker.swarmkit.v1.ListServicesRequest.Filters") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ListServicesRequest.Filters.LabelsEntry") + proto.RegisterType((*ListServicesResponse)(nil), "docker.swarmkit.v1.ListServicesResponse") + proto.RegisterType((*ListServiceStatusesRequest)(nil), "docker.swarmkit.v1.ListServiceStatusesRequest") + proto.RegisterType((*ListServiceStatusesResponse)(nil), "docker.swarmkit.v1.ListServiceStatusesResponse") + proto.RegisterType((*ListServiceStatusesResponse_ServiceStatus)(nil), "docker.swarmkit.v1.ListServiceStatusesResponse.ServiceStatus") + proto.RegisterType((*CreateNetworkRequest)(nil), "docker.swarmkit.v1.CreateNetworkRequest") + proto.RegisterType((*CreateNetworkResponse)(nil), "docker.swarmkit.v1.CreateNetworkResponse") + proto.RegisterType((*GetNetworkRequest)(nil), "docker.swarmkit.v1.GetNetworkRequest") + proto.RegisterType((*GetNetworkResponse)(nil), "docker.swarmkit.v1.GetNetworkResponse") + proto.RegisterType((*RemoveNetworkRequest)(nil), "docker.swarmkit.v1.RemoveNetworkRequest") + proto.RegisterType((*RemoveNetworkResponse)(nil), "docker.swarmkit.v1.RemoveNetworkResponse") + proto.RegisterType((*ListNetworksRequest)(nil), "docker.swarmkit.v1.ListNetworksRequest") + proto.RegisterType((*ListNetworksRequest_Filters)(nil), "docker.swarmkit.v1.ListNetworksRequest.Filters") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ListNetworksRequest.Filters.LabelsEntry") + proto.RegisterType((*ListNetworksResponse)(nil), "docker.swarmkit.v1.ListNetworksResponse") + proto.RegisterType((*GetClusterRequest)(nil), "docker.swarmkit.v1.GetClusterRequest") + proto.RegisterType((*GetClusterResponse)(nil), "docker.swarmkit.v1.GetClusterResponse") + proto.RegisterType((*ListClustersRequest)(nil), "docker.swarmkit.v1.ListClustersRequest") + proto.RegisterType((*ListClustersRequest_Filters)(nil), "docker.swarmkit.v1.ListClustersRequest.Filters") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ListClustersRequest.Filters.LabelsEntry") + proto.RegisterType((*ListClustersResponse)(nil), "docker.swarmkit.v1.ListClustersResponse") + proto.RegisterType((*KeyRotation)(nil), "docker.swarmkit.v1.KeyRotation") + proto.RegisterType((*UpdateClusterRequest)(nil), "docker.swarmkit.v1.UpdateClusterRequest") + proto.RegisterType((*UpdateClusterResponse)(nil), "docker.swarmkit.v1.UpdateClusterResponse") + proto.RegisterType((*GetSecretRequest)(nil), "docker.swarmkit.v1.GetSecretRequest") + proto.RegisterType((*GetSecretResponse)(nil), "docker.swarmkit.v1.GetSecretResponse") + proto.RegisterType((*UpdateSecretRequest)(nil), "docker.swarmkit.v1.UpdateSecretRequest") + proto.RegisterType((*UpdateSecretResponse)(nil), "docker.swarmkit.v1.UpdateSecretResponse") + proto.RegisterType((*ListSecretsRequest)(nil), "docker.swarmkit.v1.ListSecretsRequest") + proto.RegisterType((*ListSecretsRequest_Filters)(nil), "docker.swarmkit.v1.ListSecretsRequest.Filters") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ListSecretsRequest.Filters.LabelsEntry") + proto.RegisterType((*ListSecretsResponse)(nil), "docker.swarmkit.v1.ListSecretsResponse") + proto.RegisterType((*CreateSecretRequest)(nil), "docker.swarmkit.v1.CreateSecretRequest") + proto.RegisterType((*CreateSecretResponse)(nil), "docker.swarmkit.v1.CreateSecretResponse") + proto.RegisterType((*RemoveSecretRequest)(nil), "docker.swarmkit.v1.RemoveSecretRequest") + proto.RegisterType((*RemoveSecretResponse)(nil), "docker.swarmkit.v1.RemoveSecretResponse") + proto.RegisterType((*GetConfigRequest)(nil), "docker.swarmkit.v1.GetConfigRequest") + proto.RegisterType((*GetConfigResponse)(nil), "docker.swarmkit.v1.GetConfigResponse") + proto.RegisterType((*UpdateConfigRequest)(nil), "docker.swarmkit.v1.UpdateConfigRequest") + proto.RegisterType((*UpdateConfigResponse)(nil), "docker.swarmkit.v1.UpdateConfigResponse") + proto.RegisterType((*ListConfigsRequest)(nil), "docker.swarmkit.v1.ListConfigsRequest") + proto.RegisterType((*ListConfigsRequest_Filters)(nil), "docker.swarmkit.v1.ListConfigsRequest.Filters") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ListConfigsRequest.Filters.LabelsEntry") + proto.RegisterType((*ListConfigsResponse)(nil), "docker.swarmkit.v1.ListConfigsResponse") + proto.RegisterType((*CreateConfigRequest)(nil), "docker.swarmkit.v1.CreateConfigRequest") + proto.RegisterType((*CreateConfigResponse)(nil), "docker.swarmkit.v1.CreateConfigResponse") + proto.RegisterType((*RemoveConfigRequest)(nil), "docker.swarmkit.v1.RemoveConfigRequest") + proto.RegisterType((*RemoveConfigResponse)(nil), "docker.swarmkit.v1.RemoveConfigResponse") + proto.RegisterType((*CreateExtensionRequest)(nil), "docker.swarmkit.v1.CreateExtensionRequest") + proto.RegisterType((*CreateExtensionResponse)(nil), "docker.swarmkit.v1.CreateExtensionResponse") + proto.RegisterType((*RemoveExtensionRequest)(nil), "docker.swarmkit.v1.RemoveExtensionRequest") + proto.RegisterType((*RemoveExtensionResponse)(nil), "docker.swarmkit.v1.RemoveExtensionResponse") + proto.RegisterType((*GetExtensionRequest)(nil), "docker.swarmkit.v1.GetExtensionRequest") + proto.RegisterType((*GetExtensionResponse)(nil), "docker.swarmkit.v1.GetExtensionResponse") + proto.RegisterType((*CreateResourceRequest)(nil), "docker.swarmkit.v1.CreateResourceRequest") + proto.RegisterType((*CreateResourceResponse)(nil), "docker.swarmkit.v1.CreateResourceResponse") + proto.RegisterType((*RemoveResourceRequest)(nil), "docker.swarmkit.v1.RemoveResourceRequest") + proto.RegisterType((*RemoveResourceResponse)(nil), "docker.swarmkit.v1.RemoveResourceResponse") + proto.RegisterType((*UpdateResourceRequest)(nil), "docker.swarmkit.v1.UpdateResourceRequest") + proto.RegisterType((*UpdateResourceResponse)(nil), "docker.swarmkit.v1.UpdateResourceResponse") + proto.RegisterType((*GetResourceRequest)(nil), "docker.swarmkit.v1.GetResourceRequest") + proto.RegisterType((*GetResourceResponse)(nil), "docker.swarmkit.v1.GetResourceResponse") + proto.RegisterType((*ListResourcesRequest)(nil), "docker.swarmkit.v1.ListResourcesRequest") + proto.RegisterType((*ListResourcesRequest_Filters)(nil), "docker.swarmkit.v1.ListResourcesRequest.Filters") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ListResourcesRequest.Filters.LabelsEntry") + proto.RegisterType((*ListResourcesResponse)(nil), "docker.swarmkit.v1.ListResourcesResponse") } -func (m *RemoveNodeResponse) Copy() *RemoveNodeResponse { - if m == nil { - return nil - } - o := &RemoveNodeResponse{} - o.CopyFrom(m) - return o +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/control.proto", fileDescriptor_b37401dd08bf8930) +} + +var fileDescriptor_b37401dd08bf8930 = []byte{ + // 2778 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xdb, 0x6f, 0x1b, 0x59, + 0x19, 0x8f, 0x1d, 0x27, 0xb6, 0x3f, 0x27, 0x4e, 0x7a, 0x9a, 0xb6, 0x5e, 0xb7, 0x24, 0xd5, 0xf4, + 0x12, 0xb7, 0x14, 0x7b, 0xd7, 0x65, 0xd9, 0xd2, 0xdd, 0x02, 0x4d, 0xd3, 0x8b, 0x7b, 0x49, 0xab, + 0x49, 0x5a, 0xc1, 0x03, 0x8a, 0x1c, 0xfb, 0x24, 0x3b, 0xb5, 0x33, 0x63, 0x66, 0xc6, 0xdd, 0x46, + 0x08, 0xc4, 0xa5, 0x88, 0x27, 0x24, 0x5e, 0x90, 0x10, 0x0f, 0x48, 0x3c, 0x21, 0x81, 0x84, 0x10, + 0x4f, 0x3c, 0xf0, 0x07, 0x54, 0x3c, 0xed, 0xe3, 0x3e, 0x05, 0x36, 0x15, 0x12, 0x4f, 0xfc, 0x0b, + 0xac, 0xce, 0x6d, 0x6e, 0x3e, 0x73, 0xf1, 0x45, 0xea, 0x3e, 0x25, 0x3e, 0xf3, 0xfb, 0x2e, 0xe7, + 0xfb, 0xbe, 0xf9, 0x9d, 0x33, 0xdf, 0x39, 0x70, 0x65, 0x4f, 0xb3, 0x3f, 0xee, 0xef, 0x54, 0x5b, + 0xc6, 0x7e, 0xad, 0x6d, 0xb4, 0x3a, 0xd8, 0xac, 0x59, 0x9f, 0x34, 0xcd, 0xfd, 0x8e, 0x66, 0xd7, + 0x9a, 0x3d, 0xad, 0xd6, 0x32, 0x74, 0xdb, 0x34, 0xba, 0xd5, 0x9e, 0x69, 0xd8, 0x06, 0x42, 0x0c, + 0x52, 0x15, 0x90, 0xea, 0x8b, 0xf7, 0xca, 0x97, 0x63, 0x34, 0x58, 0x3d, 0xdc, 0xb2, 0x98, 0x7c, + 0x39, 0xce, 0x9a, 0xb1, 0xf3, 0x1c, 0xb7, 0x6c, 0x81, 0x8e, 0xd3, 0x6c, 0x1f, 0xf4, 0xb0, 0xc0, + 0x2e, 0xed, 0x19, 0x7b, 0x06, 0xfd, 0xb7, 0x46, 0xfe, 0xe3, 0xa3, 0xef, 0xec, 0x19, 0xc6, 0x5e, + 0x17, 0xd7, 0xe8, 0xaf, 0x9d, 0xfe, 0x6e, 0xad, 0xa9, 0x1f, 0xf0, 0x47, 0x1f, 0x44, 0x28, 0x77, + 0xe0, 0xbd, 0x6e, 0x7f, 0x4f, 0xd3, 0xf9, 0x1f, 0x26, 0xa8, 0xbc, 0x0f, 0xc5, 0xbb, 0xd8, 0xde, + 0x30, 0xda, 0x58, 0xc5, 0x3f, 0xe8, 0x63, 0xcb, 0x46, 0xe7, 0x20, 0xab, 0x1b, 0x6d, 0xbc, 0xad, + 0xb5, 0x4b, 0xa9, 0xb3, 0xa9, 0x4a, 0x7e, 0x0d, 0x8e, 0x0e, 0x57, 0x66, 0x09, 0xa2, 0xb1, 0xae, + 0xce, 0x92, 0x47, 0x8d, 0xb6, 0xf2, 0x6d, 0x58, 0x70, 0xc4, 0xac, 0x9e, 0xa1, 0x5b, 0x18, 0x5d, + 0x81, 0x0c, 0x79, 0x48, 0x85, 0x0a, 0xf5, 0x52, 0x75, 0x30, 0xb8, 0x55, 0x8a, 0xa7, 0x28, 0xe5, + 0xd5, 0x0c, 0x2c, 0x3e, 0xd4, 0x2c, 0xaa, 0xc2, 0x12, 0xa6, 0xef, 0x40, 0x76, 0x57, 0xeb, 0xda, + 0xd8, 0xb4, 0xb8, 0x96, 0x2b, 0x32, 0x2d, 0x41, 0xb1, 0xea, 0x1d, 0x26, 0xa3, 0x0a, 0xe1, 0xf2, + 0x1f, 0x33, 0x90, 0xe5, 0x83, 0x68, 0x09, 0x66, 0xf4, 0xe6, 0x3e, 0x26, 0x1a, 0xa7, 0x2b, 0x79, + 0x95, 0xfd, 0x40, 0x35, 0x28, 0x68, 0xed, 0xed, 0x9e, 0x89, 0x77, 0xb5, 0x97, 0xd8, 0x2a, 0xa5, + 0xc9, 0xb3, 0xb5, 0xe2, 0xd1, 0xe1, 0x0a, 0x34, 0xd6, 0x9f, 0xf0, 0x51, 0x15, 0xb4, 0xb6, 0xf8, + 0x1f, 0x3d, 0x81, 0xd9, 0x6e, 0x73, 0x07, 0x77, 0xad, 0xd2, 0xf4, 0xd9, 0xe9, 0x4a, 0xa1, 0x7e, + 0x6d, 0x18, 0xcf, 0xaa, 0x0f, 0xa9, 0xe8, 0x6d, 0xdd, 0x36, 0x0f, 0x54, 0xae, 0x07, 0x7d, 0x1f, + 0x0a, 0x34, 0xce, 0x5c, 0x6d, 0x96, 0xaa, 0xfd, 0x68, 0x28, 0xb5, 0x64, 0xd0, 0xab, 0x1a, 0x74, + 0x67, 0x00, 0x3d, 0x82, 0xc2, 0x3e, 0xde, 0xdf, 0xc1, 0xa6, 0xf5, 0xb1, 0xd6, 0xb3, 0x4a, 0x99, + 0xb3, 0xd3, 0x95, 0x62, 0x7d, 0x35, 0x2c, 0x2b, 0x9b, 0x3d, 0xdc, 0xaa, 0x3e, 0x72, 0xf0, 0x6b, + 0xe9, 0xc5, 0x29, 0xd5, 0x2b, 0x8f, 0xbe, 0x01, 0x33, 0xa6, 0xd1, 0xc5, 0x56, 0x69, 0x86, 0x2a, + 0x3a, 0x13, 0x9a, 0x5e, 0xa3, 0x8b, 0xa9, 0x34, 0x83, 0xa3, 0x73, 0x30, 0x4f, 0x22, 0xee, 0x86, + 0x7a, 0x96, 0xa6, 0x61, 0x8e, 0x0c, 0x8a, 0xe0, 0x96, 0xbf, 0x09, 0x05, 0xcf, 0x34, 0xd0, 0x22, + 0x4c, 0x77, 0xf0, 0x01, 0xab, 0x3e, 0x95, 0xfc, 0x4b, 0x92, 0xf8, 0xa2, 0xd9, 0xed, 0xe3, 0x52, + 0x9a, 0x8e, 0xb1, 0x1f, 0xd7, 0xd3, 0xd7, 0x52, 0xe5, 0x1b, 0xb0, 0x10, 0x88, 0xc2, 0x30, 0xe2, + 0xca, 0x2d, 0x38, 0xe6, 0x89, 0x2e, 0xaf, 0xe4, 0x2a, 0xcc, 0x90, 0x40, 0xb2, 0x92, 0x89, 0x2a, + 0x65, 0x06, 0x53, 0xfe, 0x94, 0x82, 0x63, 0x4f, 0x7b, 0xed, 0xa6, 0x8d, 0x87, 0x7d, 0x8f, 0xd0, + 0xb7, 0x60, 0x8e, 0x82, 0x5e, 0x60, 0xd3, 0xd2, 0x0c, 0x9d, 0x3a, 0x58, 0xa8, 0x9f, 0x96, 0x59, + 0x7c, 0xc6, 0x20, 0x2a, 0xad, 0x1a, 0xfe, 0x03, 0xbd, 0x0b, 0x19, 0xc2, 0x48, 0xa5, 0x69, 0x2a, + 0x77, 0x26, 0x2a, 0xbd, 0x2a, 0x45, 0x2a, 0x6b, 0x80, 0xbc, 0xbe, 0x8e, 0xf4, 0xf2, 0x6e, 0xc0, + 0x31, 0x15, 0xef, 0x1b, 0x2f, 0x86, 0x9f, 0xef, 0x12, 0xcc, 0xec, 0x1a, 0x66, 0x8b, 0x65, 0x22, + 0xa7, 0xb2, 0x1f, 0xca, 0x12, 0x20, 0xaf, 0x3e, 0xe6, 0x13, 0xa7, 0xa6, 0xad, 0xa6, 0xd5, 0xf1, + 0x98, 0xb0, 0x9b, 0x56, 0x27, 0x60, 0x82, 0x20, 0x88, 0x09, 0xf2, 0xc8, 0xa1, 0x26, 0x26, 0xe6, + 0xce, 0x8e, 0x3c, 0x8c, 0x9a, 0x1d, 0xc5, 0x53, 0x94, 0x72, 0x4d, 0xcc, 0x6e, 0x68, 0xd3, 0xce, + 0x3c, 0xbc, 0xd6, 0x95, 0x7f, 0x64, 0x18, 0xd5, 0x91, 0xc1, 0x11, 0xa8, 0xce, 0x2b, 0x36, 0x48, + 0x75, 0xff, 0x9a, 0x7e, 0x7b, 0x54, 0x27, 0xf3, 0x4c, 0x4a, 0x75, 0x35, 0x28, 0x58, 0xd8, 0x7c, + 0xa1, 0xb5, 0x48, 0x75, 0x30, 0x2e, 0xe2, 0x2e, 0x6c, 0xb2, 0xe1, 0xc6, 0xba, 0xa5, 0x02, 0x87, + 0x34, 0xda, 0x16, 0xba, 0x08, 0x39, 0x5e, 0x4b, 0x8c, 0x70, 0xf2, 0x6b, 0x85, 0xa3, 0xc3, 0x95, + 0x2c, 0x2b, 0x26, 0x4b, 0xcd, 0xb2, 0x6a, 0xb2, 0xd0, 0x3d, 0x28, 0xb6, 0xb1, 0xa5, 0x99, 0xb8, + 0xbd, 0x6d, 0xd9, 0x4d, 0x9b, 0xd3, 0x4b, 0xb1, 0xfe, 0x95, 0xb0, 0x14, 0x6f, 0x12, 0x14, 0xe5, + 0xa7, 0x79, 0x2e, 0x48, 0x47, 0x24, 0x3c, 0x95, 0x1d, 0xe4, 0x29, 0x54, 0x86, 0x9c, 0xd9, 0xd7, + 0x6d, 0x8d, 0xc4, 0x38, 0x4f, 0x9f, 0x3b, 0xbf, 0xd1, 0x19, 0x80, 0x7e, 0x6f, 0xdb, 0x36, 0xb6, + 0xc9, 0xbb, 0x55, 0xca, 0xd1, 0xf2, 0xce, 0xf5, 0x7b, 0x5b, 0xc6, 0x7a, 0xd3, 0xc6, 0x63, 0x30, + 0x9c, 0xa0, 0x28, 0x1e, 0x6c, 0x97, 0xa2, 0x48, 0xcd, 0x45, 0x52, 0x14, 0x2d, 0x42, 0x06, 0x53, + 0x1e, 0xc0, 0xd2, 0x2d, 0x13, 0x37, 0x6d, 0xcc, 0x03, 0x2e, 0xca, 0xf0, 0x2a, 0xe7, 0x0f, 0x56, + 0x83, 0x2b, 0x32, 0x35, 0x5c, 0xc2, 0x43, 0x21, 0x1b, 0x70, 0x22, 0xa0, 0x8c, 0x7b, 0xf5, 0x3e, + 0x64, 0x79, 0x12, 0xb9, 0xc2, 0xd3, 0x11, 0x0a, 0x55, 0x81, 0x55, 0x9e, 0xc3, 0xb1, 0xbb, 0xd8, + 0x0e, 0x78, 0x76, 0x05, 0xc0, 0xad, 0x19, 0xfe, 0xce, 0xcd, 0x1f, 0x1d, 0xae, 0xe4, 0x9d, 0x92, + 0x51, 0xf3, 0x4e, 0xc5, 0xa0, 0x55, 0x58, 0xd0, 0x74, 0x0b, 0x9b, 0xf6, 0x76, 0x1b, 0xef, 0x36, + 0xfb, 0x5d, 0xdb, 0xe2, 0x0c, 0x53, 0x64, 0xc3, 0xeb, 0x7c, 0x54, 0x79, 0x00, 0xc8, 0x6b, 0x6b, + 0x3c, 0xc7, 0xff, 0x96, 0x86, 0x25, 0x46, 0xa6, 0x63, 0x39, 0xbf, 0x0e, 0x0b, 0x02, 0x3d, 0xc4, + 0x3a, 0x50, 0xe4, 0x32, 0x62, 0x29, 0xb8, 0xea, 0x5b, 0x0a, 0x92, 0xa5, 0x12, 0x3d, 0x82, 0x9c, + 0x69, 0x74, 0xbb, 0x3b, 0xcd, 0x56, 0xa7, 0x94, 0x39, 0x9b, 0xaa, 0x14, 0xeb, 0xef, 0xc9, 0x04, + 0x65, 0x93, 0xac, 0xaa, 0x5c, 0x50, 0x75, 0x54, 0x28, 0x0a, 0xe4, 0xc4, 0x28, 0xca, 0x41, 0x66, + 0xe3, 0xf1, 0xc6, 0xed, 0xc5, 0x29, 0x34, 0x07, 0xb9, 0x27, 0xea, 0xed, 0x67, 0x8d, 0xc7, 0x4f, + 0x37, 0x17, 0x53, 0xa4, 0x7a, 0x02, 0xea, 0xc6, 0x4b, 0xc2, 0x3a, 0x2c, 0x31, 0xd2, 0x1d, 0x27, + 0x07, 0xca, 0x29, 0x38, 0x11, 0xd0, 0xc2, 0xd9, 0xfb, 0xd5, 0x34, 0x1c, 0x27, 0xef, 0x1f, 0x1f, + 0x77, 0x08, 0xbc, 0x11, 0x24, 0xf0, 0x5a, 0x18, 0x4d, 0x06, 0x24, 0x07, 0x39, 0xfc, 0x0f, 0xe9, + 0x89, 0x73, 0xf8, 0x66, 0x80, 0xc3, 0x3f, 0x1c, 0xd2, 0x39, 0x29, 0x8d, 0x0f, 0x70, 0x64, 0x26, + 0x86, 0x23, 0x67, 0xfc, 0x1c, 0x39, 0x0e, 0x0b, 0x3e, 0x86, 0x25, 0xbf, 0xbb, 0xbc, 0x68, 0x3e, + 0x80, 0x1c, 0x4f, 0xa2, 0xe0, 0xc2, 0xc8, 0xaa, 0x71, 0xc0, 0xca, 0x35, 0x28, 0x7b, 0x14, 0x92, + 0x55, 0xa0, 0x6f, 0xb9, 0xd9, 0x2d, 0x07, 0xd4, 0xe6, 0x3d, 0x92, 0x7f, 0x4e, 0xc3, 0x69, 0xa9, + 0x28, 0x77, 0xe9, 0x7b, 0x90, 0xb3, 0xf8, 0x18, 0x77, 0xe9, 0x46, 0x4c, 0xf4, 0x83, 0x2a, 0xaa, + 0xbe, 0x71, 0xd5, 0x51, 0x57, 0xfe, 0x6b, 0x0a, 0xe6, 0x7d, 0xcf, 0x86, 0x64, 0x9a, 0x73, 0x20, + 0x96, 0xbd, 0x6d, 0xb6, 0x7c, 0x90, 0x38, 0x67, 0xd4, 0x39, 0x3e, 0x48, 0xd7, 0x18, 0x02, 0x32, + 0xfb, 0xba, 0xae, 0xe9, 0x7b, 0x1c, 0x34, 0xcd, 0x40, 0x7c, 0x90, 0x81, 0x56, 0x61, 0xa1, 0x65, + 0xec, 0xf7, 0xba, 0xd8, 0x76, 0x74, 0x65, 0x28, 0xac, 0xe8, 0x0c, 0x6f, 0xf9, 0x57, 0x9e, 0x0d, + 0x6c, 0x7f, 0x62, 0x98, 0x9d, 0x21, 0x56, 0x1e, 0x2e, 0x21, 0x5b, 0x79, 0x1c, 0x65, 0x2e, 0x77, + 0xe8, 0x6c, 0x28, 0x8a, 0x3b, 0x84, 0x94, 0xc0, 0x2a, 0x4f, 0xe9, 0xca, 0x13, 0xf0, 0x0c, 0x41, + 0x86, 0x54, 0x34, 0xaf, 0x4b, 0xfa, 0x3f, 0x09, 0x33, 0x97, 0x21, 0x61, 0x4e, 0xbb, 0x61, 0xe6, + 0xb2, 0x24, 0xcc, 0x1c, 0xd0, 0x68, 0xf3, 0x45, 0x66, 0x42, 0x3e, 0x7e, 0x57, 0xf0, 0xdb, 0xc4, + 0xdd, 0x74, 0x38, 0x2f, 0xe0, 0xa9, 0xf2, 0xdf, 0x34, 0xe3, 0x3c, 0x3e, 0x3e, 0x02, 0xe7, 0x05, + 0x24, 0x07, 0x39, 0xef, 0x17, 0x6f, 0x91, 0xf3, 0x42, 0x9c, 0x1b, 0x99, 0xf3, 0x26, 0xc0, 0x6b, + 0xae, 0x4b, 0x2e, 0xaf, 0xf1, 0x44, 0x45, 0xf2, 0x9a, 0xc8, 0x9c, 0x03, 0x56, 0x6e, 0xd2, 0x92, + 0xbe, 0xd5, 0xed, 0x5b, 0x36, 0x36, 0x3d, 0x6b, 0x61, 0x8b, 0x8d, 0x04, 0x58, 0x82, 0xe3, 0x48, + 0x5d, 0x70, 0x80, 0x53, 0xbe, 0x8e, 0x0a, 0xb7, 0x7c, 0x39, 0x24, 0xaa, 0x7c, 0x85, 0x94, 0xc0, + 0x3a, 0xb5, 0xc4, 0x1f, 0x8c, 0x50, 0x4b, 0x01, 0xc9, 0x2f, 0x57, 0x2d, 0x85, 0x38, 0xf7, 0x36, + 0x6b, 0xc9, 0x75, 0xc9, 0xad, 0x25, 0x9e, 0x8d, 0xc8, 0x5a, 0x12, 0xa9, 0x73, 0xc0, 0xca, 0x6f, + 0x52, 0x50, 0x78, 0x80, 0x0f, 0x54, 0xc3, 0x6e, 0xda, 0x64, 0x8b, 0x79, 0x19, 0x8e, 0x91, 0x22, + 0xc3, 0xe6, 0xf6, 0x73, 0x43, 0xd3, 0xb7, 0x6d, 0xa3, 0x83, 0x75, 0xea, 0x5a, 0x4e, 0x5d, 0x60, + 0x0f, 0xee, 0x1b, 0x9a, 0xbe, 0x45, 0x86, 0xd1, 0x15, 0x40, 0xfb, 0x4d, 0xbd, 0xb9, 0xe7, 0x07, + 0xb3, 0x4d, 0xf9, 0x22, 0x7f, 0x22, 0x45, 0xf7, 0xf5, 0xae, 0xd1, 0xea, 0x6c, 0x93, 0x59, 0x4f, + 0xfb, 0xd0, 0x4f, 0xe9, 0x83, 0x07, 0xf8, 0x40, 0xf9, 0x99, 0xb3, 0xef, 0x1e, 0xa7, 0xce, 0xc9, + 0xbe, 0x5b, 0xa0, 0x87, 0xd9, 0x77, 0x73, 0x99, 0x21, 0xf6, 0xdd, 0xdc, 0xba, 0x67, 0xdf, 0x7d, + 0x93, 0xec, 0xbb, 0x59, 0x54, 0xe9, 0xba, 0x19, 0x22, 0xe8, 0x09, 0xfe, 0x5a, 0xe6, 0xf5, 0xe1, + 0xca, 0x94, 0xea, 0x88, 0xb9, 0xfb, 0xe8, 0x09, 0xbd, 0xa8, 0x37, 0x60, 0x91, 0x7e, 0x19, 0xb5, + 0x4c, 0x6c, 0x8b, 0x78, 0x5e, 0x82, 0xbc, 0x45, 0x07, 0xdc, 0x70, 0xce, 0x1d, 0x1d, 0xae, 0xe4, + 0x18, 0xaa, 0xb1, 0x4e, 0x76, 0x45, 0xf4, 0xbf, 0xb6, 0x72, 0x97, 0x7f, 0xc4, 0x31, 0x71, 0xee, + 0x4a, 0x1d, 0x66, 0x19, 0x80, 0x7b, 0x52, 0x96, 0xef, 0xcd, 0xa8, 0x0c, 0x47, 0x2a, 0x7f, 0x4f, + 0xc1, 0x71, 0xf1, 0x81, 0x30, 0x9a, 0x2f, 0x68, 0x0d, 0x8a, 0x1c, 0x3a, 0x44, 0x5e, 0xe7, 0x99, + 0x88, 0x48, 0x6b, 0xdd, 0x97, 0xd6, 0xe5, 0x70, 0xc7, 0x3d, 0xdb, 0x93, 0xfb, 0xee, 0xe7, 0xe0, + 0xd8, 0x61, 0xf8, 0x4f, 0x1a, 0x10, 0xdb, 0x22, 0x92, 0x9f, 0x0e, 0x6d, 0xde, 0x0b, 0xd2, 0x66, + 0x35, 0x7c, 0x6f, 0xe9, 0x15, 0x1c, 0x64, 0xcd, 0x57, 0x93, 0x67, 0x4d, 0x35, 0xc0, 0x9a, 0xd7, + 0x87, 0xf3, 0xed, 0xad, 0x90, 0xe6, 0x03, 0xf1, 0x79, 0xc7, 0x3d, 0xe2, 0x29, 0xfb, 0x3a, 0xf9, + 0x18, 0xa5, 0x43, 0x9c, 0x32, 0xa3, 0x72, 0x26, 0xa0, 0x4a, 0x03, 0x8e, 0x8b, 0xce, 0x88, 0xb7, + 0x74, 0xeb, 0xbe, 0xbd, 0x6e, 0xe2, 0x5a, 0xf2, 0xab, 0x1a, 0xa3, 0x96, 0xbe, 0x03, 0xc7, 0xc5, + 0xc7, 0xed, 0x88, 0x6f, 0xf7, 0x49, 0xf7, 0x23, 0xdb, 0xeb, 0x0d, 0x27, 0x8d, 0x5b, 0x86, 0xbe, + 0xab, 0xed, 0x79, 0xd4, 0xb6, 0xe8, 0x40, 0x40, 0x2d, 0x43, 0x11, 0xb5, 0xec, 0xb1, 0x43, 0x1a, + 0x42, 0xdc, 0x9d, 0x21, 0x03, 0x44, 0xcd, 0x90, 0xcb, 0x70, 0xa4, 0x87, 0x34, 0x46, 0xf5, 0x85, + 0x90, 0x06, 0x87, 0x0e, 0x43, 0x1a, 0x4c, 0x64, 0x08, 0xd2, 0x60, 0x96, 0x65, 0xa4, 0x31, 0x81, + 0x30, 0x08, 0xd2, 0x60, 0xc3, 0x23, 0x90, 0x86, 0x5f, 0xf0, 0xcb, 0x45, 0x1a, 0x72, 0xdf, 0xde, + 0x26, 0x69, 0x38, 0x1e, 0xb9, 0xa4, 0xc1, 0x12, 0x11, 0x49, 0x1a, 0x3c, 0x67, 0x02, 0xea, 0x92, + 0x86, 0xbf, 0x74, 0x13, 0x90, 0x86, 0xac, 0x96, 0xfc, 0xaa, 0xc6, 0xa8, 0x25, 0x87, 0x34, 0x46, + 0x7e, 0xbb, 0x1d, 0xd2, 0xf0, 0x7b, 0xa3, 0xfc, 0x08, 0x4e, 0x32, 0x2f, 0x6f, 0xbf, 0xb4, 0xb1, + 0x4e, 0xdf, 0x23, 0xae, 0xfc, 0x26, 0x14, 0x9a, 0xba, 0xce, 0x77, 0x38, 0x56, 0x54, 0x6f, 0xe0, + 0xa6, 0x0b, 0x53, 0xbd, 0x32, 0xe8, 0x2c, 0x14, 0xda, 0xd8, 0x6a, 0x99, 0x5a, 0xcf, 0x16, 0xef, + 0x70, 0x5e, 0xf5, 0x0e, 0x29, 0xcf, 0xe0, 0xd4, 0x80, 0x79, 0x1e, 0xa7, 0x0f, 0x21, 0x8f, 0xc5, + 0x20, 0xb7, 0x2e, 0x3d, 0x4a, 0x70, 0x25, 0x5d, 0xbc, 0xf2, 0x10, 0x4e, 0xb2, 0xe9, 0x0e, 0x4c, + 0xab, 0x0e, 0x73, 0x0e, 0xcc, 0x0d, 0xdb, 0xc2, 0xd1, 0xe1, 0x4a, 0xc1, 0xc1, 0x36, 0xd6, 0xd5, + 0x82, 0x03, 0x6a, 0xb4, 0x95, 0x77, 0xe0, 0xd4, 0x80, 0x36, 0x1e, 0xbf, 0x06, 0x1c, 0xbf, 0x8b, + 0xed, 0x89, 0x58, 0xd9, 0x84, 0x25, 0xbf, 0xaa, 0x49, 0x04, 0xe2, 0xf7, 0x29, 0xd1, 0xa6, 0x51, + 0xb1, 0x65, 0xf4, 0x4d, 0xb7, 0x27, 0x3b, 0x81, 0xfc, 0x22, 0xc8, 0x74, 0x34, 0x9d, 0x37, 0x37, + 0x54, 0xfa, 0x3f, 0xaa, 0x42, 0xb6, 0xd7, 0x3c, 0xe8, 0x1a, 0xcd, 0x36, 0x67, 0xde, 0xa5, 0x2a, + 0xbb, 0x2a, 0x51, 0x15, 0x77, 0x1f, 0xaa, 0x37, 0xf5, 0x03, 0x55, 0x80, 0x14, 0x55, 0x14, 0xa0, + 0xeb, 0x1f, 0x9f, 0xf7, 0x35, 0xc8, 0x99, 0x7c, 0x8c, 0x7b, 0x27, 0x3d, 0x53, 0x75, 0xe4, 0x1c, + 0xb4, 0x72, 0x4f, 0x34, 0x53, 0x82, 0x73, 0xae, 0x41, 0x41, 0x80, 0xdc, 0xac, 0x50, 0x42, 0x14, + 0xc8, 0xc6, 0xba, 0x0a, 0x02, 0xd2, 0x68, 0x2b, 0x25, 0x51, 0x47, 0x41, 0xef, 0x94, 0x9f, 0xa7, + 0xc5, 0x9e, 0x7f, 0x5c, 0x23, 0xe8, 0x0e, 0x2c, 0x3a, 0x02, 0x43, 0xac, 0x77, 0x0b, 0x42, 0x48, + 0xac, 0x78, 0x81, 0x8c, 0x4e, 0x8f, 0x90, 0x51, 0x4f, 0xf6, 0x32, 0x09, 0xb3, 0x17, 0x0c, 0xc2, + 0xd8, 0xd9, 0xbb, 0x4d, 0x5b, 0x1e, 0x63, 0xa7, 0xee, 0x31, 0x7d, 0x33, 0x27, 0xe8, 0xd7, 0xff, + 0xd3, 0xec, 0x9b, 0x5e, 0x3c, 0x72, 0x96, 0xf4, 0xfb, 0xc1, 0x25, 0xfd, 0xdd, 0xb0, 0x65, 0x33, + 0x28, 0x3a, 0xb8, 0xa8, 0xff, 0x6e, 0xf2, 0x8b, 0xfa, 0x56, 0x60, 0x51, 0xff, 0x68, 0x58, 0xef, + 0x46, 0x3f, 0x80, 0x10, 0x04, 0x31, 0xe3, 0x12, 0xc4, 0x38, 0x4b, 0xfd, 0x26, 0x9c, 0x08, 0xf8, + 0xc9, 0x93, 0x7a, 0x1d, 0xf2, 0x22, 0x4d, 0x62, 0xb9, 0x8f, 0xce, 0xaa, 0x0b, 0xaf, 0xff, 0xf2, + 0x3c, 0x64, 0x6f, 0xb1, 0xbb, 0x68, 0x48, 0x83, 0x2c, 0xbf, 0x4a, 0x85, 0x14, 0x99, 0xbc, 0xff, + 0x7a, 0x56, 0xf9, 0x5c, 0x24, 0x86, 0x13, 0xc5, 0x89, 0x7f, 0xfe, 0xe5, 0x7f, 0xbf, 0x4d, 0x2f, + 0xc0, 0x3c, 0x05, 0x7d, 0x8d, 0x37, 0x50, 0x90, 0x01, 0x79, 0xe7, 0xb6, 0x0b, 0x3a, 0x9f, 0xe4, + 0xaa, 0x51, 0xf9, 0x42, 0x0c, 0x2a, 0xda, 0xa0, 0x09, 0xe0, 0x5e, 0x36, 0x41, 0x17, 0xc2, 0x8f, + 0x16, 0xbd, 0x33, 0xbc, 0x18, 0x07, 0x8b, 0xb5, 0xe9, 0x5e, 0x26, 0x91, 0xdb, 0x1c, 0xb8, 0xbc, + 0x22, 0xb7, 0x29, 0xb9, 0x93, 0x12, 0x62, 0x93, 0xe5, 0x70, 0xab, 0x69, 0x75, 0x42, 0x73, 0xe8, + 0xb9, 0x4c, 0x12, 0x9a, 0x43, 0xdf, 0xb5, 0x91, 0xe8, 0x1c, 0xb2, 0x53, 0x98, 0xf3, 0x49, 0xae, + 0x66, 0x84, 0xe7, 0xd0, 0x77, 0xa7, 0x20, 0x36, 0x9e, 0x74, 0x7a, 0x11, 0xf1, 0xf4, 0xce, 0xf0, + 0x62, 0x1c, 0x2c, 0xd6, 0xa6, 0x7b, 0x4a, 0x2f, 0xb7, 0x39, 0x70, 0x63, 0x40, 0x6e, 0x73, 0xf0, + 0xb0, 0x3f, 0xcc, 0xe6, 0x4b, 0x98, 0xf3, 0x9e, 0x30, 0xa2, 0xd5, 0x84, 0x47, 0xa6, 0xe5, 0x4a, + 0x3c, 0x30, 0xda, 0xf2, 0x0f, 0x61, 0xde, 0x77, 0x9f, 0x02, 0x49, 0x35, 0xca, 0xee, 0x6f, 0x94, + 0x2f, 0x25, 0x40, 0xc6, 0x1a, 0xf7, 0x1d, 0xc7, 0xcb, 0x8d, 0xcb, 0x2e, 0x00, 0xc8, 0x8d, 0x4b, + 0xcf, 0xf6, 0x23, 0x8c, 0xfb, 0x4e, 0xdd, 0xe5, 0xc6, 0x65, 0xc7, 0xfb, 0x72, 0xe3, 0xf2, 0x23, + 0xfc, 0x10, 0xe3, 0xbf, 0x4a, 0xf9, 0x4e, 0xf6, 0xc5, 0x21, 0x2c, 0xaa, 0x26, 0x3e, 0xad, 0x65, + 0x9e, 0xd4, 0x86, 0x3c, 0xdd, 0x8d, 0x2e, 0x7a, 0x7e, 0xa2, 0x13, 0x5a, 0xf4, 0xfe, 0x53, 0xc0, + 0xd0, 0xa2, 0x0f, 0x1e, 0xe9, 0x45, 0x17, 0xbd, 0x38, 0x7e, 0x0a, 0x2f, 0xfa, 0xc0, 0x99, 0x59, + 0x78, 0xd1, 0x07, 0x4f, 0xb2, 0x62, 0x8b, 0x5e, 0x4c, 0x38, 0xa2, 0xe8, 0x03, 0x73, 0xbe, 0x94, + 0x00, 0x99, 0xb0, 0xee, 0x22, 0x8d, 0xcb, 0x8e, 0x5d, 0xa3, 0xea, 0x2e, 0xa1, 0x71, 0x96, 0x67, + 0xde, 0x7f, 0x0f, 0xcd, 0xb3, 0xff, 0x64, 0x23, 0x34, 0xcf, 0x81, 0xe6, 0x7f, 0x4c, 0x9e, 0xc5, + 0xd1, 0x50, 0x78, 0x9e, 0x03, 0xe7, 0x59, 0xe1, 0x79, 0x0e, 0x9e, 0x32, 0xc5, 0xf2, 0x8b, 0x98, + 0x70, 0x04, 0xbf, 0x04, 0xe6, 0x7c, 0x29, 0x01, 0x32, 0x76, 0xb1, 0x74, 0x0e, 0x25, 0xe4, 0x8b, + 0x65, 0xf0, 0xc8, 0xa3, 0x7c, 0x21, 0x06, 0x15, 0x1b, 0x67, 0xef, 0x09, 0x80, 0x3c, 0xce, 0x92, + 0xd3, 0x8d, 0x72, 0x25, 0x1e, 0x18, 0x6d, 0xb9, 0x0f, 0x05, 0x4f, 0x1f, 0x1b, 0x5d, 0x4c, 0xd6, + 0x7a, 0x2f, 0xaf, 0xc6, 0xe2, 0x62, 0x27, 0xec, 0x6d, 0x53, 0xcb, 0x27, 0x2c, 0xe9, 0x89, 0x97, + 0x2b, 0xf1, 0xc0, 0x58, 0xcb, 0xde, 0x96, 0xb4, 0xdc, 0xb2, 0xa4, 0xed, 0x5d, 0xae, 0xc4, 0x03, + 0x93, 0x54, 0x15, 0x6b, 0x6a, 0x85, 0x56, 0x95, 0xaf, 0x6b, 0x16, 0x5a, 0x55, 0x81, 0xce, 0x58, + 0x5c, 0x55, 0x71, 0x9b, 0x11, 0x55, 0xe5, 0x37, 0x5b, 0x89, 0x07, 0x26, 0xaa, 0x2a, 0xde, 0xe8, + 0x0c, 0xaf, 0x2a, 0x7f, 0x6f, 0x36, 0xbc, 0xaa, 0x02, 0x1d, 0xd3, 0xd8, 0xaa, 0x8a, 0x9a, 0xb0, + 0xa4, 0x69, 0x1a, 0x55, 0x55, 0x89, 0x43, 0xed, 0xed, 0x59, 0x46, 0x55, 0x55, 0x02, 0xcb, 0xd2, + 0xf6, 0x67, 0xb8, 0x65, 0x6f, 0x2b, 0x4e, 0x6e, 0x59, 0xd2, 0xf7, 0x93, 0x5b, 0x96, 0x75, 0xf5, + 0xc2, 0x2c, 0xff, 0x34, 0x05, 0x0b, 0x81, 0x8e, 0x28, 0xba, 0x1c, 0x1e, 0xc8, 0x01, 0x07, 0xbe, + 0x9a, 0x08, 0x1b, 0xef, 0x43, 0xa0, 0xdf, 0x29, 0xf7, 0x41, 0xde, 0x62, 0x95, 0xfb, 0x10, 0xd6, + 0x40, 0x0d, 0x2f, 0x76, 0x4f, 0xf7, 0x06, 0x85, 0x2d, 0xb9, 0x81, 0x2e, 0x51, 0x79, 0x35, 0x16, + 0x17, 0x6d, 0xf6, 0xc7, 0x50, 0xf4, 0xf7, 0xb3, 0x50, 0xc4, 0xc2, 0x17, 0x34, 0x7e, 0x39, 0x09, + 0x34, 0x76, 0x85, 0xf6, 0x75, 0x38, 0x50, 0x25, 0x69, 0xb3, 0x46, 0xbe, 0x42, 0x4b, 0xdb, 0x25, + 0x11, 0x93, 0xf7, 0xb7, 0x62, 0x51, 0xc4, 0xee, 0x2e, 0xd1, 0xe4, 0xe5, 0x9d, 0xdd, 0x08, 0xfb, + 0xfe, 0x66, 0x2b, 0x8a, 0xd8, 0xe0, 0x25, 0xb2, 0x1f, 0xd2, 0xbb, 0x95, 0xdb, 0x5f, 0x3b, 0xff, + 0xfa, 0xf3, 0xe5, 0xa9, 0xcf, 0x3e, 0x5f, 0x9e, 0xfa, 0xc9, 0xd1, 0x72, 0xea, 0xf5, 0xd1, 0x72, + 0xea, 0xd3, 0xa3, 0xe5, 0xd4, 0xbf, 0x8f, 0x96, 0x53, 0xbf, 0x7e, 0xb3, 0x3c, 0xf5, 0xe9, 0x9b, + 0xe5, 0xa9, 0xcf, 0xde, 0x2c, 0x4f, 0xed, 0xcc, 0xd2, 0x4e, 0xe8, 0xd5, 0x2f, 0x02, 0x00, 0x00, + 0xff, 0xff, 0xf1, 0x75, 0x17, 0x2e, 0xe0, 0x38, 0x00, 0x00, } -func (m *RemoveNodeResponse) CopyFrom(src interface{}) {} -func (m *GetTaskRequest) Copy() *GetTaskRequest { - if m == nil { - return nil +type authenticatedWrapperControlServer struct { + local ControlServer + authorize func(context.Context, []string) error +} + +func NewAuthenticatedWrapperControlServer(local ControlServer, authorize func(context.Context, []string) error) ControlServer { + return &authenticatedWrapperControlServer{ + local: local, + authorize: authorize, } - o := &GetTaskRequest{} - o.CopyFrom(m) - return o } -func (m *GetTaskRequest) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) GetNode(ctx context.Context, r *GetNodeRequest) (*GetNodeResponse, error) { - o := src.(*GetTaskRequest) - *m = *o + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.GetNode(ctx, r) } -func (m *GetTaskResponse) Copy() *GetTaskResponse { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) ListNodes(ctx context.Context, r *ListNodesRequest) (*ListNodesResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &GetTaskResponse{} - o.CopyFrom(m) - return o + return p.local.ListNodes(ctx, r) } -func (m *GetTaskResponse) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) UpdateNode(ctx context.Context, r *UpdateNodeRequest) (*UpdateNodeResponse, error) { - o := src.(*GetTaskResponse) - *m = *o - if o.Task != nil { - m.Task = &Task{} - deepcopy.Copy(m.Task, o.Task) + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.UpdateNode(ctx, r) } -func (m *RemoveTaskRequest) Copy() *RemoveTaskRequest { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) RemoveNode(ctx context.Context, r *RemoveNodeRequest) (*RemoveNodeResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &RemoveTaskRequest{} - o.CopyFrom(m) - return o + return p.local.RemoveNode(ctx, r) } -func (m *RemoveTaskRequest) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) GetTask(ctx context.Context, r *GetTaskRequest) (*GetTaskResponse, error) { - o := src.(*RemoveTaskRequest) - *m = *o + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.GetTask(ctx, r) } -func (m *RemoveTaskResponse) Copy() *RemoveTaskResponse { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) ListTasks(ctx context.Context, r *ListTasksRequest) (*ListTasksResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &RemoveTaskResponse{} - o.CopyFrom(m) - return o + return p.local.ListTasks(ctx, r) } -func (m *RemoveTaskResponse) CopyFrom(src interface{}) {} -func (m *ListTasksRequest) Copy() *ListTasksRequest { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) RemoveTask(ctx context.Context, r *RemoveTaskRequest) (*RemoveTaskResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &ListTasksRequest{} - o.CopyFrom(m) - return o + return p.local.RemoveTask(ctx, r) } -func (m *ListTasksRequest) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) GetService(ctx context.Context, r *GetServiceRequest) (*GetServiceResponse, error) { - o := src.(*ListTasksRequest) - *m = *o - if o.Filters != nil { - m.Filters = &ListTasksRequest_Filters{} - deepcopy.Copy(m.Filters, o.Filters) + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.GetService(ctx, r) } -func (m *ListTasksRequest_Filters) Copy() *ListTasksRequest_Filters { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) ListServices(ctx context.Context, r *ListServicesRequest) (*ListServicesResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &ListTasksRequest_Filters{} - o.CopyFrom(m) - return o + return p.local.ListServices(ctx, r) } -func (m *ListTasksRequest_Filters) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) CreateService(ctx context.Context, r *CreateServiceRequest) (*CreateServiceResponse, error) { - o := src.(*ListTasksRequest_Filters) - *m = *o - if o.Names != nil { - m.Names = make([]string, len(o.Names)) - copy(m.Names, o.Names) + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.CreateService(ctx, r) +} - if o.IDPrefixes != nil { - m.IDPrefixes = make([]string, len(o.IDPrefixes)) - copy(m.IDPrefixes, o.IDPrefixes) - } +func (p *authenticatedWrapperControlServer) UpdateService(ctx context.Context, r *UpdateServiceRequest) (*UpdateServiceResponse, error) { - if o.Labels != nil { - m.Labels = make(map[string]string, len(o.Labels)) - for k, v := range o.Labels { - m.Labels[k] = v - } + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.UpdateService(ctx, r) +} - if o.ServiceIDs != nil { - m.ServiceIDs = make([]string, len(o.ServiceIDs)) - copy(m.ServiceIDs, o.ServiceIDs) - } +func (p *authenticatedWrapperControlServer) RemoveService(ctx context.Context, r *RemoveServiceRequest) (*RemoveServiceResponse, error) { - if o.NodeIDs != nil { - m.NodeIDs = make([]string, len(o.NodeIDs)) - copy(m.NodeIDs, o.NodeIDs) + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.RemoveService(ctx, r) +} - if o.DesiredStates != nil { - m.DesiredStates = make([]TaskState, len(o.DesiredStates)) - copy(m.DesiredStates, o.DesiredStates) - } +func (p *authenticatedWrapperControlServer) ListServiceStatuses(ctx context.Context, r *ListServiceStatusesRequest) (*ListServiceStatusesResponse, error) { - if o.NamePrefixes != nil { - m.NamePrefixes = make([]string, len(o.NamePrefixes)) - copy(m.NamePrefixes, o.NamePrefixes) + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.ListServiceStatuses(ctx, r) +} - if o.Runtimes != nil { - m.Runtimes = make([]string, len(o.Runtimes)) - copy(m.Runtimes, o.Runtimes) - } +func (p *authenticatedWrapperControlServer) GetNetwork(ctx context.Context, r *GetNetworkRequest) (*GetNetworkResponse, error) { + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.GetNetwork(ctx, r) } -func (m *ListTasksResponse) Copy() *ListTasksResponse { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) ListNetworks(ctx context.Context, r *ListNetworksRequest) (*ListNetworksResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &ListTasksResponse{} - o.CopyFrom(m) - return o + return p.local.ListNetworks(ctx, r) } -func (m *ListTasksResponse) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) CreateNetwork(ctx context.Context, r *CreateNetworkRequest) (*CreateNetworkResponse, error) { - o := src.(*ListTasksResponse) - *m = *o - if o.Tasks != nil { - m.Tasks = make([]*Task, len(o.Tasks)) - for i := range m.Tasks { - m.Tasks[i] = &Task{} - deepcopy.Copy(m.Tasks[i], o.Tasks[i]) - } + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.CreateNetwork(ctx, r) +} + +func (p *authenticatedWrapperControlServer) RemoveNetwork(ctx context.Context, r *RemoveNetworkRequest) (*RemoveNetworkResponse, error) { + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.RemoveNetwork(ctx, r) } -func (m *CreateServiceRequest) Copy() *CreateServiceRequest { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) GetCluster(ctx context.Context, r *GetClusterRequest) (*GetClusterResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &CreateServiceRequest{} - o.CopyFrom(m) - return o + return p.local.GetCluster(ctx, r) } -func (m *CreateServiceRequest) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) ListClusters(ctx context.Context, r *ListClustersRequest) (*ListClustersResponse, error) { - o := src.(*CreateServiceRequest) - *m = *o - if o.Spec != nil { - m.Spec = &ServiceSpec{} - deepcopy.Copy(m.Spec, o.Spec) + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.ListClusters(ctx, r) } -func (m *CreateServiceResponse) Copy() *CreateServiceResponse { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) UpdateCluster(ctx context.Context, r *UpdateClusterRequest) (*UpdateClusterResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &CreateServiceResponse{} - o.CopyFrom(m) - return o + return p.local.UpdateCluster(ctx, r) } -func (m *CreateServiceResponse) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) GetSecret(ctx context.Context, r *GetSecretRequest) (*GetSecretResponse, error) { - o := src.(*CreateServiceResponse) - *m = *o - if o.Service != nil { - m.Service = &Service{} - deepcopy.Copy(m.Service, o.Service) + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.GetSecret(ctx, r) } -func (m *GetServiceRequest) Copy() *GetServiceRequest { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) UpdateSecret(ctx context.Context, r *UpdateSecretRequest) (*UpdateSecretResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &GetServiceRequest{} - o.CopyFrom(m) - return o + return p.local.UpdateSecret(ctx, r) } -func (m *GetServiceRequest) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) ListSecrets(ctx context.Context, r *ListSecretsRequest) (*ListSecretsResponse, error) { - o := src.(*GetServiceRequest) - *m = *o + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.ListSecrets(ctx, r) } -func (m *GetServiceResponse) Copy() *GetServiceResponse { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) CreateSecret(ctx context.Context, r *CreateSecretRequest) (*CreateSecretResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &GetServiceResponse{} - o.CopyFrom(m) - return o + return p.local.CreateSecret(ctx, r) } -func (m *GetServiceResponse) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) RemoveSecret(ctx context.Context, r *RemoveSecretRequest) (*RemoveSecretResponse, error) { - o := src.(*GetServiceResponse) - *m = *o - if o.Service != nil { - m.Service = &Service{} - deepcopy.Copy(m.Service, o.Service) + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.RemoveSecret(ctx, r) } -func (m *UpdateServiceRequest) Copy() *UpdateServiceRequest { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) GetConfig(ctx context.Context, r *GetConfigRequest) (*GetConfigResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &UpdateServiceRequest{} - o.CopyFrom(m) - return o + return p.local.GetConfig(ctx, r) } -func (m *UpdateServiceRequest) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) UpdateConfig(ctx context.Context, r *UpdateConfigRequest) (*UpdateConfigResponse, error) { - o := src.(*UpdateServiceRequest) - *m = *o - if o.ServiceVersion != nil { - m.ServiceVersion = &Version{} - deepcopy.Copy(m.ServiceVersion, o.ServiceVersion) + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - if o.Spec != nil { - m.Spec = &ServiceSpec{} - deepcopy.Copy(m.Spec, o.Spec) + return p.local.UpdateConfig(ctx, r) +} + +func (p *authenticatedWrapperControlServer) ListConfigs(ctx context.Context, r *ListConfigsRequest) (*ListConfigsResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.ListConfigs(ctx, r) } -func (m *UpdateServiceResponse) Copy() *UpdateServiceResponse { - if m == nil { - return nil +func (p *authenticatedWrapperControlServer) CreateConfig(ctx context.Context, r *CreateConfigRequest) (*CreateConfigResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } - o := &UpdateServiceResponse{} - o.CopyFrom(m) - return o + return p.local.CreateConfig(ctx, r) } -func (m *UpdateServiceResponse) CopyFrom(src interface{}) { +func (p *authenticatedWrapperControlServer) RemoveConfig(ctx context.Context, r *RemoveConfigRequest) (*RemoveConfigResponse, error) { - o := src.(*UpdateServiceResponse) - *m = *o - if o.Service != nil { - m.Service = &Service{} - deepcopy.Copy(m.Service, o.Service) + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err } + return p.local.RemoveConfig(ctx, r) } -func (m *RemoveServiceRequest) Copy() *RemoveServiceRequest { +func (p *authenticatedWrapperControlServer) GetExtension(ctx context.Context, r *GetExtensionRequest) (*GetExtensionResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.GetExtension(ctx, r) +} + +func (p *authenticatedWrapperControlServer) CreateExtension(ctx context.Context, r *CreateExtensionRequest) (*CreateExtensionResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.CreateExtension(ctx, r) +} + +func (p *authenticatedWrapperControlServer) RemoveExtension(ctx context.Context, r *RemoveExtensionRequest) (*RemoveExtensionResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.RemoveExtension(ctx, r) +} + +func (p *authenticatedWrapperControlServer) GetResource(ctx context.Context, r *GetResourceRequest) (*GetResourceResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.GetResource(ctx, r) +} + +func (p *authenticatedWrapperControlServer) UpdateResource(ctx context.Context, r *UpdateResourceRequest) (*UpdateResourceResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.UpdateResource(ctx, r) +} + +func (p *authenticatedWrapperControlServer) ListResources(ctx context.Context, r *ListResourcesRequest) (*ListResourcesResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.ListResources(ctx, r) +} + +func (p *authenticatedWrapperControlServer) CreateResource(ctx context.Context, r *CreateResourceRequest) (*CreateResourceResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.CreateResource(ctx, r) +} + +func (p *authenticatedWrapperControlServer) RemoveResource(ctx context.Context, r *RemoveResourceRequest) (*RemoveResourceResponse, error) { + + if err := p.authorize(ctx, []string{"swarm-manager"}); err != nil { + return nil, err + } + return p.local.RemoveResource(ctx, r) +} + +func (m *GetNodeRequest) Copy() *GetNodeRequest { if m == nil { return nil } - o := &RemoveServiceRequest{} + o := &GetNodeRequest{} o.CopyFrom(m) return o } -func (m *RemoveServiceRequest) CopyFrom(src interface{}) { +func (m *GetNodeRequest) CopyFrom(src interface{}) { - o := src.(*RemoveServiceRequest) + o := src.(*GetNodeRequest) *m = *o } -func (m *RemoveServiceResponse) Copy() *RemoveServiceResponse { +func (m *GetNodeResponse) Copy() *GetNodeResponse { if m == nil { return nil } - o := &RemoveServiceResponse{} + o := &GetNodeResponse{} o.CopyFrom(m) return o } -func (m *RemoveServiceResponse) CopyFrom(src interface{}) {} -func (m *ListServicesRequest) Copy() *ListServicesRequest { +func (m *GetNodeResponse) CopyFrom(src interface{}) { + + o := src.(*GetNodeResponse) + *m = *o + if o.Node != nil { + m.Node = &Node{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Node, o.Node) + } +} + +func (m *ListNodesRequest) Copy() *ListNodesRequest { if m == nil { return nil } - o := &ListServicesRequest{} + o := &ListNodesRequest{} o.CopyFrom(m) return o } -func (m *ListServicesRequest) CopyFrom(src interface{}) { +func (m *ListNodesRequest) CopyFrom(src interface{}) { - o := src.(*ListServicesRequest) + o := src.(*ListNodesRequest) *m = *o if o.Filters != nil { - m.Filters = &ListServicesRequest_Filters{} - deepcopy.Copy(m.Filters, o.Filters) + m.Filters = &ListNodesRequest_Filters{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Filters, o.Filters) } } -func (m *ListServicesRequest_Filters) Copy() *ListServicesRequest_Filters { +func (m *ListNodesRequest_Filters) Copy() *ListNodesRequest_Filters { if m == nil { return nil } - o := &ListServicesRequest_Filters{} + o := &ListNodesRequest_Filters{} o.CopyFrom(m) return o } -func (m *ListServicesRequest_Filters) CopyFrom(src interface{}) { +func (m *ListNodesRequest_Filters) CopyFrom(src interface{}) { - o := src.(*ListServicesRequest_Filters) + o := src.(*ListNodesRequest_Filters) *m = *o if o.Names != nil { m.Names = make([]string, len(o.Names)) @@ -1569,169 +4018,210 @@ func (m *ListServicesRequest_Filters) CopyFrom(src interface{}) { } } + if o.NodeLabels != nil { + m.NodeLabels = make(map[string]string, len(o.NodeLabels)) + for k, v := range o.NodeLabels { + m.NodeLabels[k] = v + } + } + + if o.Memberships != nil { + m.Memberships = make([]NodeSpec_Membership, len(o.Memberships)) + copy(m.Memberships, o.Memberships) + } + + if o.Roles != nil { + m.Roles = make([]NodeRole, len(o.Roles)) + copy(m.Roles, o.Roles) + } + if o.NamePrefixes != nil { m.NamePrefixes = make([]string, len(o.NamePrefixes)) copy(m.NamePrefixes, o.NamePrefixes) } - if o.Runtimes != nil { - m.Runtimes = make([]string, len(o.Runtimes)) - copy(m.Runtimes, o.Runtimes) - } - } -func (m *ListServicesResponse) Copy() *ListServicesResponse { +func (m *ListNodesResponse) Copy() *ListNodesResponse { if m == nil { return nil } - o := &ListServicesResponse{} + o := &ListNodesResponse{} o.CopyFrom(m) return o } -func (m *ListServicesResponse) CopyFrom(src interface{}) { +func (m *ListNodesResponse) CopyFrom(src interface{}) { - o := src.(*ListServicesResponse) + o := src.(*ListNodesResponse) *m = *o - if o.Services != nil { - m.Services = make([]*Service, len(o.Services)) - for i := range m.Services { - m.Services[i] = &Service{} - deepcopy.Copy(m.Services[i], o.Services[i]) + if o.Nodes != nil { + m.Nodes = make([]*Node, len(o.Nodes)) + for i := range m.Nodes { + m.Nodes[i] = &Node{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Nodes[i], o.Nodes[i]) } } } -func (m *CreateNetworkRequest) Copy() *CreateNetworkRequest { +func (m *UpdateNodeRequest) Copy() *UpdateNodeRequest { if m == nil { return nil } - o := &CreateNetworkRequest{} + o := &UpdateNodeRequest{} o.CopyFrom(m) return o } -func (m *CreateNetworkRequest) CopyFrom(src interface{}) { +func (m *UpdateNodeRequest) CopyFrom(src interface{}) { - o := src.(*CreateNetworkRequest) + o := src.(*UpdateNodeRequest) *m = *o + if o.NodeVersion != nil { + m.NodeVersion = &Version{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.NodeVersion, o.NodeVersion) + } if o.Spec != nil { - m.Spec = &NetworkSpec{} - deepcopy.Copy(m.Spec, o.Spec) + m.Spec = &NodeSpec{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Spec, o.Spec) } } -func (m *CreateNetworkResponse) Copy() *CreateNetworkResponse { +func (m *UpdateNodeResponse) Copy() *UpdateNodeResponse { if m == nil { return nil } - o := &CreateNetworkResponse{} + o := &UpdateNodeResponse{} o.CopyFrom(m) return o } -func (m *CreateNetworkResponse) CopyFrom(src interface{}) { +func (m *UpdateNodeResponse) CopyFrom(src interface{}) { - o := src.(*CreateNetworkResponse) + o := src.(*UpdateNodeResponse) *m = *o - if o.Network != nil { - m.Network = &Network{} - deepcopy.Copy(m.Network, o.Network) + if o.Node != nil { + m.Node = &Node{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Node, o.Node) } } -func (m *GetNetworkRequest) Copy() *GetNetworkRequest { +func (m *RemoveNodeRequest) Copy() *RemoveNodeRequest { if m == nil { return nil } - o := &GetNetworkRequest{} + o := &RemoveNodeRequest{} o.CopyFrom(m) return o } -func (m *GetNetworkRequest) CopyFrom(src interface{}) { +func (m *RemoveNodeRequest) CopyFrom(src interface{}) { - o := src.(*GetNetworkRequest) + o := src.(*RemoveNodeRequest) *m = *o } -func (m *GetNetworkResponse) Copy() *GetNetworkResponse { +func (m *RemoveNodeResponse) Copy() *RemoveNodeResponse { if m == nil { return nil } - o := &GetNetworkResponse{} + o := &RemoveNodeResponse{} o.CopyFrom(m) return o } -func (m *GetNetworkResponse) CopyFrom(src interface{}) { +func (m *RemoveNodeResponse) CopyFrom(src interface{}) {} +func (m *GetTaskRequest) Copy() *GetTaskRequest { + if m == nil { + return nil + } + o := &GetTaskRequest{} + o.CopyFrom(m) + return o +} - o := src.(*GetNetworkResponse) +func (m *GetTaskRequest) CopyFrom(src interface{}) { + + o := src.(*GetTaskRequest) *m = *o - if o.Network != nil { - m.Network = &Network{} - deepcopy.Copy(m.Network, o.Network) +} + +func (m *GetTaskResponse) Copy() *GetTaskResponse { + if m == nil { + return nil } + o := &GetTaskResponse{} + o.CopyFrom(m) + return o } -func (m *RemoveNetworkRequest) Copy() *RemoveNetworkRequest { +func (m *GetTaskResponse) CopyFrom(src interface{}) { + + o := src.(*GetTaskResponse) + *m = *o + if o.Task != nil { + m.Task = &Task{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Task, o.Task) + } +} + +func (m *RemoveTaskRequest) Copy() *RemoveTaskRequest { if m == nil { return nil } - o := &RemoveNetworkRequest{} + o := &RemoveTaskRequest{} o.CopyFrom(m) return o } -func (m *RemoveNetworkRequest) CopyFrom(src interface{}) { +func (m *RemoveTaskRequest) CopyFrom(src interface{}) { - o := src.(*RemoveNetworkRequest) + o := src.(*RemoveTaskRequest) *m = *o } -func (m *RemoveNetworkResponse) Copy() *RemoveNetworkResponse { +func (m *RemoveTaskResponse) Copy() *RemoveTaskResponse { if m == nil { return nil } - o := &RemoveNetworkResponse{} + o := &RemoveTaskResponse{} o.CopyFrom(m) return o } -func (m *RemoveNetworkResponse) CopyFrom(src interface{}) {} -func (m *ListNetworksRequest) Copy() *ListNetworksRequest { +func (m *RemoveTaskResponse) CopyFrom(src interface{}) {} +func (m *ListTasksRequest) Copy() *ListTasksRequest { if m == nil { return nil } - o := &ListNetworksRequest{} + o := &ListTasksRequest{} o.CopyFrom(m) return o } -func (m *ListNetworksRequest) CopyFrom(src interface{}) { +func (m *ListTasksRequest) CopyFrom(src interface{}) { - o := src.(*ListNetworksRequest) + o := src.(*ListTasksRequest) *m = *o if o.Filters != nil { - m.Filters = &ListNetworksRequest_Filters{} - deepcopy.Copy(m.Filters, o.Filters) + m.Filters = &ListTasksRequest_Filters{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Filters, o.Filters) } } -func (m *ListNetworksRequest_Filters) Copy() *ListNetworksRequest_Filters { +func (m *ListTasksRequest_Filters) Copy() *ListTasksRequest_Filters { if m == nil { return nil } - o := &ListNetworksRequest_Filters{} + o := &ListTasksRequest_Filters{} o.CopyFrom(m) return o } -func (m *ListNetworksRequest_Filters) CopyFrom(src interface{}) { +func (m *ListTasksRequest_Filters) CopyFrom(src interface{}) { - o := src.(*ListNetworksRequest_Filters) + o := src.(*ListTasksRequest_Filters) *m = *o if o.Names != nil { m.Names = make([]string, len(o.Names)) @@ -1750,314 +4240,226 @@ func (m *ListNetworksRequest_Filters) CopyFrom(src interface{}) { } } + if o.ServiceIDs != nil { + m.ServiceIDs = make([]string, len(o.ServiceIDs)) + copy(m.ServiceIDs, o.ServiceIDs) + } + + if o.NodeIDs != nil { + m.NodeIDs = make([]string, len(o.NodeIDs)) + copy(m.NodeIDs, o.NodeIDs) + } + + if o.DesiredStates != nil { + m.DesiredStates = make([]TaskState, len(o.DesiredStates)) + copy(m.DesiredStates, o.DesiredStates) + } + if o.NamePrefixes != nil { m.NamePrefixes = make([]string, len(o.NamePrefixes)) copy(m.NamePrefixes, o.NamePrefixes) } + if o.Runtimes != nil { + m.Runtimes = make([]string, len(o.Runtimes)) + copy(m.Runtimes, o.Runtimes) + } + } -func (m *ListNetworksResponse) Copy() *ListNetworksResponse { +func (m *ListTasksResponse) Copy() *ListTasksResponse { if m == nil { return nil } - o := &ListNetworksResponse{} + o := &ListTasksResponse{} o.CopyFrom(m) return o } -func (m *ListNetworksResponse) CopyFrom(src interface{}) { +func (m *ListTasksResponse) CopyFrom(src interface{}) { - o := src.(*ListNetworksResponse) + o := src.(*ListTasksResponse) *m = *o - if o.Networks != nil { - m.Networks = make([]*Network, len(o.Networks)) - for i := range m.Networks { - m.Networks[i] = &Network{} - deepcopy.Copy(m.Networks[i], o.Networks[i]) + if o.Tasks != nil { + m.Tasks = make([]*Task, len(o.Tasks)) + for i := range m.Tasks { + m.Tasks[i] = &Task{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Tasks[i], o.Tasks[i]) } } } -func (m *GetClusterRequest) Copy() *GetClusterRequest { +func (m *CreateServiceRequest) Copy() *CreateServiceRequest { if m == nil { return nil } - o := &GetClusterRequest{} + o := &CreateServiceRequest{} o.CopyFrom(m) return o } -func (m *GetClusterRequest) CopyFrom(src interface{}) { +func (m *CreateServiceRequest) CopyFrom(src interface{}) { - o := src.(*GetClusterRequest) + o := src.(*CreateServiceRequest) *m = *o + if o.Spec != nil { + m.Spec = &ServiceSpec{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Spec, o.Spec) + } } -func (m *GetClusterResponse) Copy() *GetClusterResponse { +func (m *CreateServiceResponse) Copy() *CreateServiceResponse { if m == nil { return nil } - o := &GetClusterResponse{} + o := &CreateServiceResponse{} o.CopyFrom(m) return o } -func (m *GetClusterResponse) CopyFrom(src interface{}) { +func (m *CreateServiceResponse) CopyFrom(src interface{}) { - o := src.(*GetClusterResponse) + o := src.(*CreateServiceResponse) *m = *o - if o.Cluster != nil { - m.Cluster = &Cluster{} - deepcopy.Copy(m.Cluster, o.Cluster) + if o.Service != nil { + m.Service = &Service{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Service, o.Service) } } -func (m *ListClustersRequest) Copy() *ListClustersRequest { +func (m *GetServiceRequest) Copy() *GetServiceRequest { if m == nil { return nil } - o := &ListClustersRequest{} + o := &GetServiceRequest{} o.CopyFrom(m) return o } -func (m *ListClustersRequest) CopyFrom(src interface{}) { +func (m *GetServiceRequest) CopyFrom(src interface{}) { - o := src.(*ListClustersRequest) + o := src.(*GetServiceRequest) *m = *o - if o.Filters != nil { - m.Filters = &ListClustersRequest_Filters{} - deepcopy.Copy(m.Filters, o.Filters) - } } -func (m *ListClustersRequest_Filters) Copy() *ListClustersRequest_Filters { +func (m *GetServiceResponse) Copy() *GetServiceResponse { if m == nil { return nil } - o := &ListClustersRequest_Filters{} + o := &GetServiceResponse{} o.CopyFrom(m) return o } -func (m *ListClustersRequest_Filters) CopyFrom(src interface{}) { +func (m *GetServiceResponse) CopyFrom(src interface{}) { - o := src.(*ListClustersRequest_Filters) + o := src.(*GetServiceResponse) *m = *o - if o.Names != nil { - m.Names = make([]string, len(o.Names)) - copy(m.Names, o.Names) + if o.Service != nil { + m.Service = &Service{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Service, o.Service) } +} - if o.IDPrefixes != nil { - m.IDPrefixes = make([]string, len(o.IDPrefixes)) - copy(m.IDPrefixes, o.IDPrefixes) +func (m *UpdateServiceRequest) Copy() *UpdateServiceRequest { + if m == nil { + return nil } + o := &UpdateServiceRequest{} + o.CopyFrom(m) + return o +} - if o.Labels != nil { - m.Labels = make(map[string]string, len(o.Labels)) - for k, v := range o.Labels { - m.Labels[k] = v - } - } +func (m *UpdateServiceRequest) CopyFrom(src interface{}) { - if o.NamePrefixes != nil { - m.NamePrefixes = make([]string, len(o.NamePrefixes)) - copy(m.NamePrefixes, o.NamePrefixes) + o := src.(*UpdateServiceRequest) + *m = *o + if o.ServiceVersion != nil { + m.ServiceVersion = &Version{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.ServiceVersion, o.ServiceVersion) + } + if o.Spec != nil { + m.Spec = &ServiceSpec{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Spec, o.Spec) } - } -func (m *ListClustersResponse) Copy() *ListClustersResponse { +func (m *UpdateServiceResponse) Copy() *UpdateServiceResponse { if m == nil { return nil } - o := &ListClustersResponse{} + o := &UpdateServiceResponse{} o.CopyFrom(m) return o } -func (m *ListClustersResponse) CopyFrom(src interface{}) { +func (m *UpdateServiceResponse) CopyFrom(src interface{}) { - o := src.(*ListClustersResponse) + o := src.(*UpdateServiceResponse) *m = *o - if o.Clusters != nil { - m.Clusters = make([]*Cluster, len(o.Clusters)) - for i := range m.Clusters { - m.Clusters[i] = &Cluster{} - deepcopy.Copy(m.Clusters[i], o.Clusters[i]) - } + if o.Service != nil { + m.Service = &Service{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Service, o.Service) } - } -func (m *KeyRotation) Copy() *KeyRotation { +func (m *RemoveServiceRequest) Copy() *RemoveServiceRequest { if m == nil { return nil } - o := &KeyRotation{} + o := &RemoveServiceRequest{} o.CopyFrom(m) return o } -func (m *KeyRotation) CopyFrom(src interface{}) { +func (m *RemoveServiceRequest) CopyFrom(src interface{}) { - o := src.(*KeyRotation) + o := src.(*RemoveServiceRequest) *m = *o } -func (m *UpdateClusterRequest) Copy() *UpdateClusterRequest { +func (m *RemoveServiceResponse) Copy() *RemoveServiceResponse { if m == nil { return nil } - o := &UpdateClusterRequest{} + o := &RemoveServiceResponse{} o.CopyFrom(m) return o } -func (m *UpdateClusterRequest) CopyFrom(src interface{}) { - - o := src.(*UpdateClusterRequest) - *m = *o - if o.ClusterVersion != nil { - m.ClusterVersion = &Version{} - deepcopy.Copy(m.ClusterVersion, o.ClusterVersion) - } - if o.Spec != nil { - m.Spec = &ClusterSpec{} - deepcopy.Copy(m.Spec, o.Spec) - } - deepcopy.Copy(&m.Rotation, &o.Rotation) -} - -func (m *UpdateClusterResponse) Copy() *UpdateClusterResponse { - if m == nil { - return nil - } - o := &UpdateClusterResponse{} - o.CopyFrom(m) - return o -} - -func (m *UpdateClusterResponse) CopyFrom(src interface{}) { - - o := src.(*UpdateClusterResponse) - *m = *o - if o.Cluster != nil { - m.Cluster = &Cluster{} - deepcopy.Copy(m.Cluster, o.Cluster) - } -} - -func (m *GetSecretRequest) Copy() *GetSecretRequest { - if m == nil { - return nil - } - o := &GetSecretRequest{} - o.CopyFrom(m) - return o -} - -func (m *GetSecretRequest) CopyFrom(src interface{}) { - - o := src.(*GetSecretRequest) - *m = *o -} - -func (m *GetSecretResponse) Copy() *GetSecretResponse { - if m == nil { - return nil - } - o := &GetSecretResponse{} - o.CopyFrom(m) - return o -} - -func (m *GetSecretResponse) CopyFrom(src interface{}) { - - o := src.(*GetSecretResponse) - *m = *o - if o.Secret != nil { - m.Secret = &Secret{} - deepcopy.Copy(m.Secret, o.Secret) - } -} - -func (m *UpdateSecretRequest) Copy() *UpdateSecretRequest { - if m == nil { - return nil - } - o := &UpdateSecretRequest{} - o.CopyFrom(m) - return o -} - -func (m *UpdateSecretRequest) CopyFrom(src interface{}) { - - o := src.(*UpdateSecretRequest) - *m = *o - if o.SecretVersion != nil { - m.SecretVersion = &Version{} - deepcopy.Copy(m.SecretVersion, o.SecretVersion) - } - if o.Spec != nil { - m.Spec = &SecretSpec{} - deepcopy.Copy(m.Spec, o.Spec) - } -} - -func (m *UpdateSecretResponse) Copy() *UpdateSecretResponse { - if m == nil { - return nil - } - o := &UpdateSecretResponse{} - o.CopyFrom(m) - return o -} - -func (m *UpdateSecretResponse) CopyFrom(src interface{}) { - - o := src.(*UpdateSecretResponse) - *m = *o - if o.Secret != nil { - m.Secret = &Secret{} - deepcopy.Copy(m.Secret, o.Secret) - } -} - -func (m *ListSecretsRequest) Copy() *ListSecretsRequest { +func (m *RemoveServiceResponse) CopyFrom(src interface{}) {} +func (m *ListServicesRequest) Copy() *ListServicesRequest { if m == nil { return nil } - o := &ListSecretsRequest{} + o := &ListServicesRequest{} o.CopyFrom(m) return o } -func (m *ListSecretsRequest) CopyFrom(src interface{}) { +func (m *ListServicesRequest) CopyFrom(src interface{}) { - o := src.(*ListSecretsRequest) + o := src.(*ListServicesRequest) *m = *o if o.Filters != nil { - m.Filters = &ListSecretsRequest_Filters{} - deepcopy.Copy(m.Filters, o.Filters) + m.Filters = &ListServicesRequest_Filters{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Filters, o.Filters) } } -func (m *ListSecretsRequest_Filters) Copy() *ListSecretsRequest_Filters { +func (m *ListServicesRequest_Filters) Copy() *ListServicesRequest_Filters { if m == nil { return nil } - o := &ListSecretsRequest_Filters{} + o := &ListServicesRequest_Filters{} o.CopyFrom(m) return o } -func (m *ListSecretsRequest_Filters) CopyFrom(src interface{}) { +func (m *ListServicesRequest_Filters) CopyFrom(src interface{}) { - o := src.(*ListSecretsRequest_Filters) + o := src.(*ListServicesRequest_Filters) *m = *o if o.Names != nil { m.Names = make([]string, len(o.Names)) @@ -2081,201 +4483,222 @@ func (m *ListSecretsRequest_Filters) CopyFrom(src interface{}) { copy(m.NamePrefixes, o.NamePrefixes) } + if o.Runtimes != nil { + m.Runtimes = make([]string, len(o.Runtimes)) + copy(m.Runtimes, o.Runtimes) + } + } -func (m *ListSecretsResponse) Copy() *ListSecretsResponse { +func (m *ListServicesResponse) Copy() *ListServicesResponse { if m == nil { return nil } - o := &ListSecretsResponse{} + o := &ListServicesResponse{} o.CopyFrom(m) return o } -func (m *ListSecretsResponse) CopyFrom(src interface{}) { +func (m *ListServicesResponse) CopyFrom(src interface{}) { - o := src.(*ListSecretsResponse) + o := src.(*ListServicesResponse) *m = *o - if o.Secrets != nil { - m.Secrets = make([]*Secret, len(o.Secrets)) - for i := range m.Secrets { - m.Secrets[i] = &Secret{} - deepcopy.Copy(m.Secrets[i], o.Secrets[i]) + if o.Services != nil { + m.Services = make([]*Service, len(o.Services)) + for i := range m.Services { + m.Services[i] = &Service{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Services[i], o.Services[i]) } } } -func (m *CreateSecretRequest) Copy() *CreateSecretRequest { +func (m *ListServiceStatusesRequest) Copy() *ListServiceStatusesRequest { if m == nil { return nil } - o := &CreateSecretRequest{} + o := &ListServiceStatusesRequest{} o.CopyFrom(m) return o } -func (m *CreateSecretRequest) CopyFrom(src interface{}) { +func (m *ListServiceStatusesRequest) CopyFrom(src interface{}) { - o := src.(*CreateSecretRequest) + o := src.(*ListServiceStatusesRequest) *m = *o - if o.Spec != nil { - m.Spec = &SecretSpec{} - deepcopy.Copy(m.Spec, o.Spec) + if o.Services != nil { + m.Services = make([]string, len(o.Services)) + copy(m.Services, o.Services) } + } -func (m *CreateSecretResponse) Copy() *CreateSecretResponse { +func (m *ListServiceStatusesResponse) Copy() *ListServiceStatusesResponse { if m == nil { return nil } - o := &CreateSecretResponse{} + o := &ListServiceStatusesResponse{} o.CopyFrom(m) return o } -func (m *CreateSecretResponse) CopyFrom(src interface{}) { +func (m *ListServiceStatusesResponse) CopyFrom(src interface{}) { - o := src.(*CreateSecretResponse) + o := src.(*ListServiceStatusesResponse) *m = *o - if o.Secret != nil { - m.Secret = &Secret{} - deepcopy.Copy(m.Secret, o.Secret) + if o.Statuses != nil { + m.Statuses = make([]*ListServiceStatusesResponse_ServiceStatus, len(o.Statuses)) + for i := range m.Statuses { + m.Statuses[i] = &ListServiceStatusesResponse_ServiceStatus{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Statuses[i], o.Statuses[i]) + } } + } -func (m *RemoveSecretRequest) Copy() *RemoveSecretRequest { +func (m *ListServiceStatusesResponse_ServiceStatus) Copy() *ListServiceStatusesResponse_ServiceStatus { if m == nil { return nil } - o := &RemoveSecretRequest{} + o := &ListServiceStatusesResponse_ServiceStatus{} o.CopyFrom(m) return o } -func (m *RemoveSecretRequest) CopyFrom(src interface{}) { +func (m *ListServiceStatusesResponse_ServiceStatus) CopyFrom(src interface{}) { - o := src.(*RemoveSecretRequest) + o := src.(*ListServiceStatusesResponse_ServiceStatus) *m = *o } -func (m *RemoveSecretResponse) Copy() *RemoveSecretResponse { +func (m *CreateNetworkRequest) Copy() *CreateNetworkRequest { if m == nil { return nil } - o := &RemoveSecretResponse{} + o := &CreateNetworkRequest{} o.CopyFrom(m) return o } -func (m *RemoveSecretResponse) CopyFrom(src interface{}) {} -func (m *GetConfigRequest) Copy() *GetConfigRequest { +func (m *CreateNetworkRequest) CopyFrom(src interface{}) { + + o := src.(*CreateNetworkRequest) + *m = *o + if o.Spec != nil { + m.Spec = &NetworkSpec{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Spec, o.Spec) + } +} + +func (m *CreateNetworkResponse) Copy() *CreateNetworkResponse { if m == nil { return nil } - o := &GetConfigRequest{} + o := &CreateNetworkResponse{} o.CopyFrom(m) return o } -func (m *GetConfigRequest) CopyFrom(src interface{}) { +func (m *CreateNetworkResponse) CopyFrom(src interface{}) { - o := src.(*GetConfigRequest) + o := src.(*CreateNetworkResponse) *m = *o + if o.Network != nil { + m.Network = &Network{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Network, o.Network) + } } -func (m *GetConfigResponse) Copy() *GetConfigResponse { +func (m *GetNetworkRequest) Copy() *GetNetworkRequest { if m == nil { return nil } - o := &GetConfigResponse{} + o := &GetNetworkRequest{} o.CopyFrom(m) return o } -func (m *GetConfigResponse) CopyFrom(src interface{}) { +func (m *GetNetworkRequest) CopyFrom(src interface{}) { - o := src.(*GetConfigResponse) + o := src.(*GetNetworkRequest) *m = *o - if o.Config != nil { - m.Config = &Config{} - deepcopy.Copy(m.Config, o.Config) - } } -func (m *UpdateConfigRequest) Copy() *UpdateConfigRequest { +func (m *GetNetworkResponse) Copy() *GetNetworkResponse { if m == nil { return nil } - o := &UpdateConfigRequest{} + o := &GetNetworkResponse{} o.CopyFrom(m) return o } -func (m *UpdateConfigRequest) CopyFrom(src interface{}) { +func (m *GetNetworkResponse) CopyFrom(src interface{}) { - o := src.(*UpdateConfigRequest) + o := src.(*GetNetworkResponse) *m = *o - if o.ConfigVersion != nil { - m.ConfigVersion = &Version{} - deepcopy.Copy(m.ConfigVersion, o.ConfigVersion) - } - if o.Spec != nil { - m.Spec = &ConfigSpec{} - deepcopy.Copy(m.Spec, o.Spec) + if o.Network != nil { + m.Network = &Network{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Network, o.Network) } } -func (m *UpdateConfigResponse) Copy() *UpdateConfigResponse { +func (m *RemoveNetworkRequest) Copy() *RemoveNetworkRequest { if m == nil { return nil } - o := &UpdateConfigResponse{} + o := &RemoveNetworkRequest{} o.CopyFrom(m) return o } -func (m *UpdateConfigResponse) CopyFrom(src interface{}) { +func (m *RemoveNetworkRequest) CopyFrom(src interface{}) { - o := src.(*UpdateConfigResponse) + o := src.(*RemoveNetworkRequest) *m = *o - if o.Config != nil { - m.Config = &Config{} - deepcopy.Copy(m.Config, o.Config) +} + +func (m *RemoveNetworkResponse) Copy() *RemoveNetworkResponse { + if m == nil { + return nil } + o := &RemoveNetworkResponse{} + o.CopyFrom(m) + return o } -func (m *ListConfigsRequest) Copy() *ListConfigsRequest { +func (m *RemoveNetworkResponse) CopyFrom(src interface{}) {} +func (m *ListNetworksRequest) Copy() *ListNetworksRequest { if m == nil { return nil } - o := &ListConfigsRequest{} + o := &ListNetworksRequest{} o.CopyFrom(m) return o } -func (m *ListConfigsRequest) CopyFrom(src interface{}) { +func (m *ListNetworksRequest) CopyFrom(src interface{}) { - o := src.(*ListConfigsRequest) + o := src.(*ListNetworksRequest) *m = *o if o.Filters != nil { - m.Filters = &ListConfigsRequest_Filters{} - deepcopy.Copy(m.Filters, o.Filters) + m.Filters = &ListNetworksRequest_Filters{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Filters, o.Filters) } } -func (m *ListConfigsRequest_Filters) Copy() *ListConfigsRequest_Filters { +func (m *ListNetworksRequest_Filters) Copy() *ListNetworksRequest_Filters { if m == nil { return nil } - o := &ListConfigsRequest_Filters{} + o := &ListNetworksRequest_Filters{} o.CopyFrom(m) return o } -func (m *ListConfigsRequest_Filters) CopyFrom(src interface{}) { +func (m *ListNetworksRequest_Filters) CopyFrom(src interface{}) { - o := src.(*ListConfigsRequest_Filters) + o := src.(*ListNetworksRequest_Filters) *m = *o if o.Names != nil { m.Names = make([]string, len(o.Names)) @@ -2301,6371 +4724,12632 @@ func (m *ListConfigsRequest_Filters) CopyFrom(src interface{}) { } -func (m *ListConfigsResponse) Copy() *ListConfigsResponse { +func (m *ListNetworksResponse) Copy() *ListNetworksResponse { if m == nil { return nil } - o := &ListConfigsResponse{} + o := &ListNetworksResponse{} o.CopyFrom(m) return o } -func (m *ListConfigsResponse) CopyFrom(src interface{}) { +func (m *ListNetworksResponse) CopyFrom(src interface{}) { - o := src.(*ListConfigsResponse) + o := src.(*ListNetworksResponse) *m = *o - if o.Configs != nil { - m.Configs = make([]*Config, len(o.Configs)) - for i := range m.Configs { - m.Configs[i] = &Config{} - deepcopy.Copy(m.Configs[i], o.Configs[i]) + if o.Networks != nil { + m.Networks = make([]*Network, len(o.Networks)) + for i := range m.Networks { + m.Networks[i] = &Network{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Networks[i], o.Networks[i]) } } } -func (m *CreateConfigRequest) Copy() *CreateConfigRequest { +func (m *GetClusterRequest) Copy() *GetClusterRequest { if m == nil { return nil } - o := &CreateConfigRequest{} + o := &GetClusterRequest{} o.CopyFrom(m) return o } -func (m *CreateConfigRequest) CopyFrom(src interface{}) { +func (m *GetClusterRequest) CopyFrom(src interface{}) { - o := src.(*CreateConfigRequest) + o := src.(*GetClusterRequest) *m = *o - if o.Spec != nil { - m.Spec = &ConfigSpec{} - deepcopy.Copy(m.Spec, o.Spec) - } } -func (m *CreateConfigResponse) Copy() *CreateConfigResponse { +func (m *GetClusterResponse) Copy() *GetClusterResponse { if m == nil { return nil } - o := &CreateConfigResponse{} + o := &GetClusterResponse{} o.CopyFrom(m) return o } -func (m *CreateConfigResponse) CopyFrom(src interface{}) { +func (m *GetClusterResponse) CopyFrom(src interface{}) { - o := src.(*CreateConfigResponse) + o := src.(*GetClusterResponse) *m = *o - if o.Config != nil { - m.Config = &Config{} - deepcopy.Copy(m.Config, o.Config) + if o.Cluster != nil { + m.Cluster = &Cluster{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Cluster, o.Cluster) } } -func (m *RemoveConfigRequest) Copy() *RemoveConfigRequest { +func (m *ListClustersRequest) Copy() *ListClustersRequest { if m == nil { return nil } - o := &RemoveConfigRequest{} + o := &ListClustersRequest{} o.CopyFrom(m) return o } -func (m *RemoveConfigRequest) CopyFrom(src interface{}) { +func (m *ListClustersRequest) CopyFrom(src interface{}) { - o := src.(*RemoveConfigRequest) + o := src.(*ListClustersRequest) *m = *o + if o.Filters != nil { + m.Filters = &ListClustersRequest_Filters{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Filters, o.Filters) + } } -func (m *RemoveConfigResponse) Copy() *RemoveConfigResponse { +func (m *ListClustersRequest_Filters) Copy() *ListClustersRequest_Filters { if m == nil { return nil } - o := &RemoveConfigResponse{} + o := &ListClustersRequest_Filters{} o.CopyFrom(m) return o } -func (m *RemoveConfigResponse) CopyFrom(src interface{}) {} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn +func (m *ListClustersRequest_Filters) CopyFrom(src interface{}) { -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 + o := src.(*ListClustersRequest_Filters) + *m = *o + if o.Names != nil { + m.Names = make([]string, len(o.Names)) + copy(m.Names, o.Names) + } -// Client API for Control service + if o.IDPrefixes != nil { + m.IDPrefixes = make([]string, len(o.IDPrefixes)) + copy(m.IDPrefixes, o.IDPrefixes) + } -type ControlClient interface { - GetNode(ctx context.Context, in *GetNodeRequest, opts ...grpc.CallOption) (*GetNodeResponse, error) - ListNodes(ctx context.Context, in *ListNodesRequest, opts ...grpc.CallOption) (*ListNodesResponse, error) - UpdateNode(ctx context.Context, in *UpdateNodeRequest, opts ...grpc.CallOption) (*UpdateNodeResponse, error) - RemoveNode(ctx context.Context, in *RemoveNodeRequest, opts ...grpc.CallOption) (*RemoveNodeResponse, error) - GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskResponse, error) - ListTasks(ctx context.Context, in *ListTasksRequest, opts ...grpc.CallOption) (*ListTasksResponse, error) - RemoveTask(ctx context.Context, in *RemoveTaskRequest, opts ...grpc.CallOption) (*RemoveTaskResponse, error) - GetService(ctx context.Context, in *GetServiceRequest, opts ...grpc.CallOption) (*GetServiceResponse, error) - ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) - CreateService(ctx context.Context, in *CreateServiceRequest, opts ...grpc.CallOption) (*CreateServiceResponse, error) - UpdateService(ctx context.Context, in *UpdateServiceRequest, opts ...grpc.CallOption) (*UpdateServiceResponse, error) - RemoveService(ctx context.Context, in *RemoveServiceRequest, opts ...grpc.CallOption) (*RemoveServiceResponse, error) - GetNetwork(ctx context.Context, in *GetNetworkRequest, opts ...grpc.CallOption) (*GetNetworkResponse, error) - ListNetworks(ctx context.Context, in *ListNetworksRequest, opts ...grpc.CallOption) (*ListNetworksResponse, error) - CreateNetwork(ctx context.Context, in *CreateNetworkRequest, opts ...grpc.CallOption) (*CreateNetworkResponse, error) - RemoveNetwork(ctx context.Context, in *RemoveNetworkRequest, opts ...grpc.CallOption) (*RemoveNetworkResponse, error) - GetCluster(ctx context.Context, in *GetClusterRequest, opts ...grpc.CallOption) (*GetClusterResponse, error) - ListClusters(ctx context.Context, in *ListClustersRequest, opts ...grpc.CallOption) (*ListClustersResponse, error) - UpdateCluster(ctx context.Context, in *UpdateClusterRequest, opts ...grpc.CallOption) (*UpdateClusterResponse, error) - // GetSecret returns a `GetSecretResponse` with a `Secret` with the same - // id as `GetSecretRequest.SecretID` - // - Returns `NotFound` if the Secret with the given id is not found. - // - Returns `InvalidArgument` if the `GetSecretRequest.SecretID` is empty. - // - Returns an error if getting fails. - GetSecret(ctx context.Context, in *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error) - // UpdateSecret returns a `UpdateSecretResponse` with a `Secret` with the same - // id as `GetSecretRequest.SecretID` - // - Returns `NotFound` if the Secret with the given id is not found. - // - Returns `InvalidArgument` if the `GetSecretRequest.SecretID` is empty. - // - Returns an error if updating fails. - UpdateSecret(ctx context.Context, in *UpdateSecretRequest, opts ...grpc.CallOption) (*UpdateSecretResponse, error) - // ListSecrets returns a `ListSecretResponse` with a list of all non-internal `Secret`s being - // managed, or all secrets matching any name in `ListSecretsRequest.Names`, any - // name prefix in `ListSecretsRequest.NamePrefixes`, any id in - // `ListSecretsRequest.SecretIDs`, or any id prefix in `ListSecretsRequest.IDPrefixes`. - // - Returns an error if listing fails. - ListSecrets(ctx context.Context, in *ListSecretsRequest, opts ...grpc.CallOption) (*ListSecretsResponse, error) - // CreateSecret creates and return a `CreateSecretResponse` with a `Secret` based - // on the provided `CreateSecretRequest.SecretSpec`. - // - Returns `InvalidArgument` if the `CreateSecretRequest.SecretSpec` is malformed, - // or if the secret data is too long or contains invalid characters. - // - Returns an error if the creation fails. - CreateSecret(ctx context.Context, in *CreateSecretRequest, opts ...grpc.CallOption) (*CreateSecretResponse, error) - // RemoveSecret removes the secret referenced by `RemoveSecretRequest.ID`. - // - Returns `InvalidArgument` if `RemoveSecretRequest.ID` is empty. - // - Returns `NotFound` if the a secret named `RemoveSecretRequest.ID` is not found. - // - Returns an error if the deletion fails. - RemoveSecret(ctx context.Context, in *RemoveSecretRequest, opts ...grpc.CallOption) (*RemoveSecretResponse, error) - // GetConfig returns a `GetConfigResponse` with a `Config` with the same - // id as `GetConfigRequest.ConfigID` - // - Returns `NotFound` if the Config with the given id is not found. - // - Returns `InvalidArgument` if the `GetConfigRequest.ConfigID` is empty. - // - Returns an error if getting fails. - GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error) - // UpdateConfig returns a `UpdateConfigResponse` with a `Config` with the same - // id as `GetConfigRequest.ConfigID` - // - Returns `NotFound` if the Config with the given id is not found. - // - Returns `InvalidArgument` if the `GetConfigRequest.ConfigID` is empty. - // - Returns an error if updating fails. - UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*UpdateConfigResponse, error) - // ListConfigs returns a `ListConfigResponse` with a list of `Config`s being - // managed, or all configs matching any name in `ListConfigsRequest.Names`, any - // name prefix in `ListConfigsRequest.NamePrefixes`, any id in - // `ListConfigsRequest.ConfigIDs`, or any id prefix in `ListConfigsRequest.IDPrefixes`. - // - Returns an error if listing fails. - ListConfigs(ctx context.Context, in *ListConfigsRequest, opts ...grpc.CallOption) (*ListConfigsResponse, error) - // CreateConfig creates and return a `CreateConfigResponse` with a `Config` based - // on the provided `CreateConfigRequest.ConfigSpec`. - // - Returns `InvalidArgument` if the `CreateConfigRequest.ConfigSpec` is malformed, - // or if the config data is too long or contains invalid characters. - // - Returns an error if the creation fails. - CreateConfig(ctx context.Context, in *CreateConfigRequest, opts ...grpc.CallOption) (*CreateConfigResponse, error) - // RemoveConfig removes the config referenced by `RemoveConfigRequest.ID`. - // - Returns `InvalidArgument` if `RemoveConfigRequest.ID` is empty. - // - Returns `NotFound` if the a config named `RemoveConfigRequest.ID` is not found. - // - Returns an error if the deletion fails. - RemoveConfig(ctx context.Context, in *RemoveConfigRequest, opts ...grpc.CallOption) (*RemoveConfigResponse, error) -} + if o.Labels != nil { + m.Labels = make(map[string]string, len(o.Labels)) + for k, v := range o.Labels { + m.Labels[k] = v + } + } -type controlClient struct { - cc *grpc.ClientConn -} + if o.NamePrefixes != nil { + m.NamePrefixes = make([]string, len(o.NamePrefixes)) + copy(m.NamePrefixes, o.NamePrefixes) + } -func NewControlClient(cc *grpc.ClientConn) ControlClient { - return &controlClient{cc} } -func (c *controlClient) GetNode(ctx context.Context, in *GetNodeRequest, opts ...grpc.CallOption) (*GetNodeResponse, error) { - out := new(GetNodeResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetNode", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *ListClustersResponse) Copy() *ListClustersResponse { + if m == nil { + return nil } - return out, nil + o := &ListClustersResponse{} + o.CopyFrom(m) + return o } -func (c *controlClient) ListNodes(ctx context.Context, in *ListNodesRequest, opts ...grpc.CallOption) (*ListNodesResponse, error) { - out := new(ListNodesResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListNodes", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *ListClustersResponse) CopyFrom(src interface{}) { + + o := src.(*ListClustersResponse) + *m = *o + if o.Clusters != nil { + m.Clusters = make([]*Cluster, len(o.Clusters)) + for i := range m.Clusters { + m.Clusters[i] = &Cluster{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Clusters[i], o.Clusters[i]) + } } - return out, nil + } -func (c *controlClient) UpdateNode(ctx context.Context, in *UpdateNodeRequest, opts ...grpc.CallOption) (*UpdateNodeResponse, error) { - out := new(UpdateNodeResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateNode", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *KeyRotation) Copy() *KeyRotation { + if m == nil { + return nil } - return out, nil + o := &KeyRotation{} + o.CopyFrom(m) + return o } -func (c *controlClient) RemoveNode(ctx context.Context, in *RemoveNodeRequest, opts ...grpc.CallOption) (*RemoveNodeResponse, error) { - out := new(RemoveNodeResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveNode", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil +func (m *KeyRotation) CopyFrom(src interface{}) { + + o := src.(*KeyRotation) + *m = *o } -func (c *controlClient) GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskResponse, error) { - out := new(GetTaskResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetTask", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *UpdateClusterRequest) Copy() *UpdateClusterRequest { + if m == nil { + return nil } - return out, nil + o := &UpdateClusterRequest{} + o.CopyFrom(m) + return o } -func (c *controlClient) ListTasks(ctx context.Context, in *ListTasksRequest, opts ...grpc.CallOption) (*ListTasksResponse, error) { - out := new(ListTasksResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListTasks", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *UpdateClusterRequest) CopyFrom(src interface{}) { + + o := src.(*UpdateClusterRequest) + *m = *o + if o.ClusterVersion != nil { + m.ClusterVersion = &Version{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.ClusterVersion, o.ClusterVersion) } - return out, nil + if o.Spec != nil { + m.Spec = &ClusterSpec{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Spec, o.Spec) + } + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Rotation, &o.Rotation) } -func (c *controlClient) RemoveTask(ctx context.Context, in *RemoveTaskRequest, opts ...grpc.CallOption) (*RemoveTaskResponse, error) { - out := new(RemoveTaskResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveTask", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *UpdateClusterResponse) Copy() *UpdateClusterResponse { + if m == nil { + return nil } - return out, nil + o := &UpdateClusterResponse{} + o.CopyFrom(m) + return o } -func (c *controlClient) GetService(ctx context.Context, in *GetServiceRequest, opts ...grpc.CallOption) (*GetServiceResponse, error) { - out := new(GetServiceResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetService", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *UpdateClusterResponse) CopyFrom(src interface{}) { + + o := src.(*UpdateClusterResponse) + *m = *o + if o.Cluster != nil { + m.Cluster = &Cluster{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Cluster, o.Cluster) } - return out, nil } -func (c *controlClient) ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) { - out := new(ListServicesResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListServices", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *GetSecretRequest) Copy() *GetSecretRequest { + if m == nil { + return nil } - return out, nil + o := &GetSecretRequest{} + o.CopyFrom(m) + return o } -func (c *controlClient) CreateService(ctx context.Context, in *CreateServiceRequest, opts ...grpc.CallOption) (*CreateServiceResponse, error) { - out := new(CreateServiceResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/CreateService", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil +func (m *GetSecretRequest) CopyFrom(src interface{}) { + + o := src.(*GetSecretRequest) + *m = *o } -func (c *controlClient) UpdateService(ctx context.Context, in *UpdateServiceRequest, opts ...grpc.CallOption) (*UpdateServiceResponse, error) { - out := new(UpdateServiceResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateService", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *GetSecretResponse) Copy() *GetSecretResponse { + if m == nil { + return nil } - return out, nil + o := &GetSecretResponse{} + o.CopyFrom(m) + return o } -func (c *controlClient) RemoveService(ctx context.Context, in *RemoveServiceRequest, opts ...grpc.CallOption) (*RemoveServiceResponse, error) { - out := new(RemoveServiceResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveService", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *GetSecretResponse) CopyFrom(src interface{}) { + + o := src.(*GetSecretResponse) + *m = *o + if o.Secret != nil { + m.Secret = &Secret{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Secret, o.Secret) } - return out, nil } -func (c *controlClient) GetNetwork(ctx context.Context, in *GetNetworkRequest, opts ...grpc.CallOption) (*GetNetworkResponse, error) { - out := new(GetNetworkResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetNetwork", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *UpdateSecretRequest) Copy() *UpdateSecretRequest { + if m == nil { + return nil } - return out, nil + o := &UpdateSecretRequest{} + o.CopyFrom(m) + return o } -func (c *controlClient) ListNetworks(ctx context.Context, in *ListNetworksRequest, opts ...grpc.CallOption) (*ListNetworksResponse, error) { - out := new(ListNetworksResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListNetworks", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *UpdateSecretRequest) CopyFrom(src interface{}) { + + o := src.(*UpdateSecretRequest) + *m = *o + if o.SecretVersion != nil { + m.SecretVersion = &Version{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.SecretVersion, o.SecretVersion) + } + if o.Spec != nil { + m.Spec = &SecretSpec{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Spec, o.Spec) } - return out, nil } -func (c *controlClient) CreateNetwork(ctx context.Context, in *CreateNetworkRequest, opts ...grpc.CallOption) (*CreateNetworkResponse, error) { - out := new(CreateNetworkResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/CreateNetwork", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *UpdateSecretResponse) Copy() *UpdateSecretResponse { + if m == nil { + return nil } - return out, nil + o := &UpdateSecretResponse{} + o.CopyFrom(m) + return o } -func (c *controlClient) RemoveNetwork(ctx context.Context, in *RemoveNetworkRequest, opts ...grpc.CallOption) (*RemoveNetworkResponse, error) { - out := new(RemoveNetworkResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveNetwork", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *UpdateSecretResponse) CopyFrom(src interface{}) { + + o := src.(*UpdateSecretResponse) + *m = *o + if o.Secret != nil { + m.Secret = &Secret{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Secret, o.Secret) } - return out, nil } -func (c *controlClient) GetCluster(ctx context.Context, in *GetClusterRequest, opts ...grpc.CallOption) (*GetClusterResponse, error) { - out := new(GetClusterResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetCluster", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *ListSecretsRequest) Copy() *ListSecretsRequest { + if m == nil { + return nil } - return out, nil + o := &ListSecretsRequest{} + o.CopyFrom(m) + return o } -func (c *controlClient) ListClusters(ctx context.Context, in *ListClustersRequest, opts ...grpc.CallOption) (*ListClustersResponse, error) { - out := new(ListClustersResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListClusters", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *ListSecretsRequest) CopyFrom(src interface{}) { + + o := src.(*ListSecretsRequest) + *m = *o + if o.Filters != nil { + m.Filters = &ListSecretsRequest_Filters{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Filters, o.Filters) } - return out, nil } -func (c *controlClient) UpdateCluster(ctx context.Context, in *UpdateClusterRequest, opts ...grpc.CallOption) (*UpdateClusterResponse, error) { - out := new(UpdateClusterResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateCluster", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *ListSecretsRequest_Filters) Copy() *ListSecretsRequest_Filters { + if m == nil { + return nil } - return out, nil + o := &ListSecretsRequest_Filters{} + o.CopyFrom(m) + return o } -func (c *controlClient) GetSecret(ctx context.Context, in *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error) { - out := new(GetSecretResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetSecret", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *ListSecretsRequest_Filters) CopyFrom(src interface{}) { + + o := src.(*ListSecretsRequest_Filters) + *m = *o + if o.Names != nil { + m.Names = make([]string, len(o.Names)) + copy(m.Names, o.Names) } - return out, nil -} -func (c *controlClient) UpdateSecret(ctx context.Context, in *UpdateSecretRequest, opts ...grpc.CallOption) (*UpdateSecretResponse, error) { - out := new(UpdateSecretResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateSecret", in, out, c.cc, opts...) - if err != nil { - return nil, err + if o.IDPrefixes != nil { + m.IDPrefixes = make([]string, len(o.IDPrefixes)) + copy(m.IDPrefixes, o.IDPrefixes) } - return out, nil -} -func (c *controlClient) ListSecrets(ctx context.Context, in *ListSecretsRequest, opts ...grpc.CallOption) (*ListSecretsResponse, error) { - out := new(ListSecretsResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListSecrets", in, out, c.cc, opts...) - if err != nil { - return nil, err + if o.Labels != nil { + m.Labels = make(map[string]string, len(o.Labels)) + for k, v := range o.Labels { + m.Labels[k] = v + } } - return out, nil + + if o.NamePrefixes != nil { + m.NamePrefixes = make([]string, len(o.NamePrefixes)) + copy(m.NamePrefixes, o.NamePrefixes) + } + } -func (c *controlClient) CreateSecret(ctx context.Context, in *CreateSecretRequest, opts ...grpc.CallOption) (*CreateSecretResponse, error) { - out := new(CreateSecretResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/CreateSecret", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *ListSecretsResponse) Copy() *ListSecretsResponse { + if m == nil { + return nil } - return out, nil + o := &ListSecretsResponse{} + o.CopyFrom(m) + return o } -func (c *controlClient) RemoveSecret(ctx context.Context, in *RemoveSecretRequest, opts ...grpc.CallOption) (*RemoveSecretResponse, error) { - out := new(RemoveSecretResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveSecret", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *ListSecretsResponse) CopyFrom(src interface{}) { + + o := src.(*ListSecretsResponse) + *m = *o + if o.Secrets != nil { + m.Secrets = make([]*Secret, len(o.Secrets)) + for i := range m.Secrets { + m.Secrets[i] = &Secret{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Secrets[i], o.Secrets[i]) + } } - return out, nil + } -func (c *controlClient) GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error) { - out := new(GetConfigResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetConfig", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *CreateSecretRequest) Copy() *CreateSecretRequest { + if m == nil { + return nil } - return out, nil + o := &CreateSecretRequest{} + o.CopyFrom(m) + return o } -func (c *controlClient) UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*UpdateConfigResponse, error) { - out := new(UpdateConfigResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateConfig", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *CreateSecretRequest) CopyFrom(src interface{}) { + + o := src.(*CreateSecretRequest) + *m = *o + if o.Spec != nil { + m.Spec = &SecretSpec{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Spec, o.Spec) } - return out, nil } -func (c *controlClient) ListConfigs(ctx context.Context, in *ListConfigsRequest, opts ...grpc.CallOption) (*ListConfigsResponse, error) { - out := new(ListConfigsResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListConfigs", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *CreateSecretResponse) Copy() *CreateSecretResponse { + if m == nil { + return nil } - return out, nil + o := &CreateSecretResponse{} + o.CopyFrom(m) + return o } -func (c *controlClient) CreateConfig(ctx context.Context, in *CreateConfigRequest, opts ...grpc.CallOption) (*CreateConfigResponse, error) { - out := new(CreateConfigResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/CreateConfig", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *CreateSecretResponse) CopyFrom(src interface{}) { + + o := src.(*CreateSecretResponse) + *m = *o + if o.Secret != nil { + m.Secret = &Secret{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Secret, o.Secret) } - return out, nil } -func (c *controlClient) RemoveConfig(ctx context.Context, in *RemoveConfigRequest, opts ...grpc.CallOption) (*RemoveConfigResponse, error) { - out := new(RemoveConfigResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveConfig", in, out, c.cc, opts...) - if err != nil { - return nil, err +func (m *RemoveSecretRequest) Copy() *RemoveSecretRequest { + if m == nil { + return nil } - return out, nil + o := &RemoveSecretRequest{} + o.CopyFrom(m) + return o } -// Server API for Control service +func (m *RemoveSecretRequest) CopyFrom(src interface{}) { -type ControlServer interface { - GetNode(context.Context, *GetNodeRequest) (*GetNodeResponse, error) - ListNodes(context.Context, *ListNodesRequest) (*ListNodesResponse, error) - UpdateNode(context.Context, *UpdateNodeRequest) (*UpdateNodeResponse, error) - RemoveNode(context.Context, *RemoveNodeRequest) (*RemoveNodeResponse, error) - GetTask(context.Context, *GetTaskRequest) (*GetTaskResponse, error) - ListTasks(context.Context, *ListTasksRequest) (*ListTasksResponse, error) - RemoveTask(context.Context, *RemoveTaskRequest) (*RemoveTaskResponse, error) - GetService(context.Context, *GetServiceRequest) (*GetServiceResponse, error) - ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error) - CreateService(context.Context, *CreateServiceRequest) (*CreateServiceResponse, error) - UpdateService(context.Context, *UpdateServiceRequest) (*UpdateServiceResponse, error) - RemoveService(context.Context, *RemoveServiceRequest) (*RemoveServiceResponse, error) - GetNetwork(context.Context, *GetNetworkRequest) (*GetNetworkResponse, error) - ListNetworks(context.Context, *ListNetworksRequest) (*ListNetworksResponse, error) - CreateNetwork(context.Context, *CreateNetworkRequest) (*CreateNetworkResponse, error) - RemoveNetwork(context.Context, *RemoveNetworkRequest) (*RemoveNetworkResponse, error) - GetCluster(context.Context, *GetClusterRequest) (*GetClusterResponse, error) - ListClusters(context.Context, *ListClustersRequest) (*ListClustersResponse, error) - UpdateCluster(context.Context, *UpdateClusterRequest) (*UpdateClusterResponse, error) - // GetSecret returns a `GetSecretResponse` with a `Secret` with the same - // id as `GetSecretRequest.SecretID` - // - Returns `NotFound` if the Secret with the given id is not found. - // - Returns `InvalidArgument` if the `GetSecretRequest.SecretID` is empty. - // - Returns an error if getting fails. - GetSecret(context.Context, *GetSecretRequest) (*GetSecretResponse, error) - // UpdateSecret returns a `UpdateSecretResponse` with a `Secret` with the same - // id as `GetSecretRequest.SecretID` - // - Returns `NotFound` if the Secret with the given id is not found. - // - Returns `InvalidArgument` if the `GetSecretRequest.SecretID` is empty. - // - Returns an error if updating fails. - UpdateSecret(context.Context, *UpdateSecretRequest) (*UpdateSecretResponse, error) - // ListSecrets returns a `ListSecretResponse` with a list of all non-internal `Secret`s being - // managed, or all secrets matching any name in `ListSecretsRequest.Names`, any - // name prefix in `ListSecretsRequest.NamePrefixes`, any id in - // `ListSecretsRequest.SecretIDs`, or any id prefix in `ListSecretsRequest.IDPrefixes`. - // - Returns an error if listing fails. - ListSecrets(context.Context, *ListSecretsRequest) (*ListSecretsResponse, error) - // CreateSecret creates and return a `CreateSecretResponse` with a `Secret` based - // on the provided `CreateSecretRequest.SecretSpec`. - // - Returns `InvalidArgument` if the `CreateSecretRequest.SecretSpec` is malformed, - // or if the secret data is too long or contains invalid characters. - // - Returns an error if the creation fails. - CreateSecret(context.Context, *CreateSecretRequest) (*CreateSecretResponse, error) - // RemoveSecret removes the secret referenced by `RemoveSecretRequest.ID`. - // - Returns `InvalidArgument` if `RemoveSecretRequest.ID` is empty. - // - Returns `NotFound` if the a secret named `RemoveSecretRequest.ID` is not found. - // - Returns an error if the deletion fails. - RemoveSecret(context.Context, *RemoveSecretRequest) (*RemoveSecretResponse, error) - // GetConfig returns a `GetConfigResponse` with a `Config` with the same - // id as `GetConfigRequest.ConfigID` - // - Returns `NotFound` if the Config with the given id is not found. - // - Returns `InvalidArgument` if the `GetConfigRequest.ConfigID` is empty. - // - Returns an error if getting fails. - GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error) - // UpdateConfig returns a `UpdateConfigResponse` with a `Config` with the same - // id as `GetConfigRequest.ConfigID` - // - Returns `NotFound` if the Config with the given id is not found. - // - Returns `InvalidArgument` if the `GetConfigRequest.ConfigID` is empty. - // - Returns an error if updating fails. - UpdateConfig(context.Context, *UpdateConfigRequest) (*UpdateConfigResponse, error) - // ListConfigs returns a `ListConfigResponse` with a list of `Config`s being - // managed, or all configs matching any name in `ListConfigsRequest.Names`, any - // name prefix in `ListConfigsRequest.NamePrefixes`, any id in - // `ListConfigsRequest.ConfigIDs`, or any id prefix in `ListConfigsRequest.IDPrefixes`. - // - Returns an error if listing fails. - ListConfigs(context.Context, *ListConfigsRequest) (*ListConfigsResponse, error) - // CreateConfig creates and return a `CreateConfigResponse` with a `Config` based - // on the provided `CreateConfigRequest.ConfigSpec`. - // - Returns `InvalidArgument` if the `CreateConfigRequest.ConfigSpec` is malformed, - // or if the config data is too long or contains invalid characters. - // - Returns an error if the creation fails. - CreateConfig(context.Context, *CreateConfigRequest) (*CreateConfigResponse, error) - // RemoveConfig removes the config referenced by `RemoveConfigRequest.ID`. - // - Returns `InvalidArgument` if `RemoveConfigRequest.ID` is empty. - // - Returns `NotFound` if the a config named `RemoveConfigRequest.ID` is not found. - // - Returns an error if the deletion fails. - RemoveConfig(context.Context, *RemoveConfigRequest) (*RemoveConfigResponse, error) + o := src.(*RemoveSecretRequest) + *m = *o } -func RegisterControlServer(s *grpc.Server, srv ControlServer) { - s.RegisterService(&_Control_serviceDesc, srv) +func (m *RemoveSecretResponse) Copy() *RemoveSecretResponse { + if m == nil { + return nil + } + o := &RemoveSecretResponse{} + o.CopyFrom(m) + return o } -func _Control_GetNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetNodeRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetNode(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/GetNode", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetNode(ctx, req.(*GetNodeRequest)) +func (m *RemoveSecretResponse) CopyFrom(src interface{}) {} +func (m *GetConfigRequest) Copy() *GetConfigRequest { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &GetConfigRequest{} + o.CopyFrom(m) + return o } -func _Control_ListNodes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListNodesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ListNodes(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/ListNodes", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ListNodes(ctx, req.(*ListNodesRequest)) - } - return interceptor(ctx, in, info, handler) +func (m *GetConfigRequest) CopyFrom(src interface{}) { + + o := src.(*GetConfigRequest) + *m = *o } -func _Control_UpdateNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateNodeRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).UpdateNode(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/UpdateNode", +func (m *GetConfigResponse) Copy() *GetConfigResponse { + if m == nil { + return nil } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).UpdateNode(ctx, req.(*UpdateNodeRequest)) + o := &GetConfigResponse{} + o.CopyFrom(m) + return o +} + +func (m *GetConfigResponse) CopyFrom(src interface{}) { + + o := src.(*GetConfigResponse) + *m = *o + if o.Config != nil { + m.Config = &Config{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Config, o.Config) } - return interceptor(ctx, in, info, handler) } -func _Control_RemoveNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveNodeRequest) - if err := dec(in); err != nil { - return nil, err +func (m *UpdateConfigRequest) Copy() *UpdateConfigRequest { + if m == nil { + return nil } - if interceptor == nil { - return srv.(ControlServer).RemoveNode(ctx, in) + o := &UpdateConfigRequest{} + o.CopyFrom(m) + return o +} + +func (m *UpdateConfigRequest) CopyFrom(src interface{}) { + + o := src.(*UpdateConfigRequest) + *m = *o + if o.ConfigVersion != nil { + m.ConfigVersion = &Version{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.ConfigVersion, o.ConfigVersion) } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/RemoveNode", + if o.Spec != nil { + m.Spec = &ConfigSpec{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Spec, o.Spec) } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).RemoveNode(ctx, req.(*RemoveNodeRequest)) +} + +func (m *UpdateConfigResponse) Copy() *UpdateConfigResponse { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &UpdateConfigResponse{} + o.CopyFrom(m) + return o } -func _Control_GetTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetTaskRequest) - if err := dec(in); err != nil { - return nil, err +func (m *UpdateConfigResponse) CopyFrom(src interface{}) { + + o := src.(*UpdateConfigResponse) + *m = *o + if o.Config != nil { + m.Config = &Config{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Config, o.Config) } - if interceptor == nil { - return srv.(ControlServer).GetTask(ctx, in) +} + +func (m *ListConfigsRequest) Copy() *ListConfigsRequest { + if m == nil { + return nil } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/GetTask", + o := &ListConfigsRequest{} + o.CopyFrom(m) + return o +} + +func (m *ListConfigsRequest) CopyFrom(src interface{}) { + + o := src.(*ListConfigsRequest) + *m = *o + if o.Filters != nil { + m.Filters = &ListConfigsRequest_Filters{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Filters, o.Filters) } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetTask(ctx, req.(*GetTaskRequest)) +} + +func (m *ListConfigsRequest_Filters) Copy() *ListConfigsRequest_Filters { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &ListConfigsRequest_Filters{} + o.CopyFrom(m) + return o } -func _Control_ListTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListTasksRequest) - if err := dec(in); err != nil { - return nil, err +func (m *ListConfigsRequest_Filters) CopyFrom(src interface{}) { + + o := src.(*ListConfigsRequest_Filters) + *m = *o + if o.Names != nil { + m.Names = make([]string, len(o.Names)) + copy(m.Names, o.Names) } - if interceptor == nil { - return srv.(ControlServer).ListTasks(ctx, in) + + if o.IDPrefixes != nil { + m.IDPrefixes = make([]string, len(o.IDPrefixes)) + copy(m.IDPrefixes, o.IDPrefixes) } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/ListTasks", + + if o.Labels != nil { + m.Labels = make(map[string]string, len(o.Labels)) + for k, v := range o.Labels { + m.Labels[k] = v + } } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ListTasks(ctx, req.(*ListTasksRequest)) + + if o.NamePrefixes != nil { + m.NamePrefixes = make([]string, len(o.NamePrefixes)) + copy(m.NamePrefixes, o.NamePrefixes) } - return interceptor(ctx, in, info, handler) + } -func _Control_RemoveTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveTaskRequest) - if err := dec(in); err != nil { - return nil, err +func (m *ListConfigsResponse) Copy() *ListConfigsResponse { + if m == nil { + return nil } - if interceptor == nil { - return srv.(ControlServer).RemoveTask(ctx, in) + o := &ListConfigsResponse{} + o.CopyFrom(m) + return o +} + +func (m *ListConfigsResponse) CopyFrom(src interface{}) { + + o := src.(*ListConfigsResponse) + *m = *o + if o.Configs != nil { + m.Configs = make([]*Config, len(o.Configs)) + for i := range m.Configs { + m.Configs[i] = &Config{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Configs[i], o.Configs[i]) + } } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/RemoveTask", + +} + +func (m *CreateConfigRequest) Copy() *CreateConfigRequest { + if m == nil { + return nil } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).RemoveTask(ctx, req.(*RemoveTaskRequest)) + o := &CreateConfigRequest{} + o.CopyFrom(m) + return o +} + +func (m *CreateConfigRequest) CopyFrom(src interface{}) { + + o := src.(*CreateConfigRequest) + *m = *o + if o.Spec != nil { + m.Spec = &ConfigSpec{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Spec, o.Spec) } - return interceptor(ctx, in, info, handler) } -func _Control_GetService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetServiceRequest) - if err := dec(in); err != nil { - return nil, err +func (m *CreateConfigResponse) Copy() *CreateConfigResponse { + if m == nil { + return nil } - if interceptor == nil { - return srv.(ControlServer).GetService(ctx, in) + o := &CreateConfigResponse{} + o.CopyFrom(m) + return o +} + +func (m *CreateConfigResponse) CopyFrom(src interface{}) { + + o := src.(*CreateConfigResponse) + *m = *o + if o.Config != nil { + m.Config = &Config{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Config, o.Config) } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/GetService", +} + +func (m *RemoveConfigRequest) Copy() *RemoveConfigRequest { + if m == nil { + return nil } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetService(ctx, req.(*GetServiceRequest)) + o := &RemoveConfigRequest{} + o.CopyFrom(m) + return o +} + +func (m *RemoveConfigRequest) CopyFrom(src interface{}) { + + o := src.(*RemoveConfigRequest) + *m = *o +} + +func (m *RemoveConfigResponse) Copy() *RemoveConfigResponse { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &RemoveConfigResponse{} + o.CopyFrom(m) + return o } -func _Control_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListServicesRequest) - if err := dec(in); err != nil { - return nil, err +func (m *RemoveConfigResponse) CopyFrom(src interface{}) {} +func (m *CreateExtensionRequest) Copy() *CreateExtensionRequest { + if m == nil { + return nil } - if interceptor == nil { - return srv.(ControlServer).ListServices(ctx, in) + o := &CreateExtensionRequest{} + o.CopyFrom(m) + return o +} + +func (m *CreateExtensionRequest) CopyFrom(src interface{}) { + + o := src.(*CreateExtensionRequest) + *m = *o + if o.Annotations != nil { + m.Annotations = &Annotations{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Annotations, o.Annotations) } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/ListServices", +} + +func (m *CreateExtensionResponse) Copy() *CreateExtensionResponse { + if m == nil { + return nil } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ListServices(ctx, req.(*ListServicesRequest)) + o := &CreateExtensionResponse{} + o.CopyFrom(m) + return o +} + +func (m *CreateExtensionResponse) CopyFrom(src interface{}) { + + o := src.(*CreateExtensionResponse) + *m = *o + if o.Extension != nil { + m.Extension = &Extension{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Extension, o.Extension) } - return interceptor(ctx, in, info, handler) } -func _Control_CreateService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateServiceRequest) - if err := dec(in); err != nil { - return nil, err +func (m *RemoveExtensionRequest) Copy() *RemoveExtensionRequest { + if m == nil { + return nil } - if interceptor == nil { - return srv.(ControlServer).CreateService(ctx, in) + o := &RemoveExtensionRequest{} + o.CopyFrom(m) + return o +} + +func (m *RemoveExtensionRequest) CopyFrom(src interface{}) { + + o := src.(*RemoveExtensionRequest) + *m = *o +} + +func (m *RemoveExtensionResponse) Copy() *RemoveExtensionResponse { + if m == nil { + return nil } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/CreateService", + o := &RemoveExtensionResponse{} + o.CopyFrom(m) + return o +} + +func (m *RemoveExtensionResponse) CopyFrom(src interface{}) {} +func (m *GetExtensionRequest) Copy() *GetExtensionRequest { + if m == nil { + return nil } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).CreateService(ctx, req.(*CreateServiceRequest)) + o := &GetExtensionRequest{} + o.CopyFrom(m) + return o +} + +func (m *GetExtensionRequest) CopyFrom(src interface{}) { + + o := src.(*GetExtensionRequest) + *m = *o +} + +func (m *GetExtensionResponse) Copy() *GetExtensionResponse { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &GetExtensionResponse{} + o.CopyFrom(m) + return o } -func _Control_UpdateService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateServiceRequest) - if err := dec(in); err != nil { - return nil, err +func (m *GetExtensionResponse) CopyFrom(src interface{}) { + + o := src.(*GetExtensionResponse) + *m = *o + if o.Extension != nil { + m.Extension = &Extension{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Extension, o.Extension) } - if interceptor == nil { - return srv.(ControlServer).UpdateService(ctx, in) +} + +func (m *CreateResourceRequest) Copy() *CreateResourceRequest { + if m == nil { + return nil } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/UpdateService", + o := &CreateResourceRequest{} + o.CopyFrom(m) + return o +} + +func (m *CreateResourceRequest) CopyFrom(src interface{}) { + + o := src.(*CreateResourceRequest) + *m = *o + if o.Annotations != nil { + m.Annotations = &Annotations{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Annotations, o.Annotations) } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).UpdateService(ctx, req.(*UpdateServiceRequest)) + if o.Payload != nil { + m.Payload = &types.Any{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Payload, o.Payload) } - return interceptor(ctx, in, info, handler) } -func _Control_RemoveService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveServiceRequest) - if err := dec(in); err != nil { - return nil, err +func (m *CreateResourceResponse) Copy() *CreateResourceResponse { + if m == nil { + return nil } - if interceptor == nil { - return srv.(ControlServer).RemoveService(ctx, in) + o := &CreateResourceResponse{} + o.CopyFrom(m) + return o +} + +func (m *CreateResourceResponse) CopyFrom(src interface{}) { + + o := src.(*CreateResourceResponse) + *m = *o + if o.Resource != nil { + m.Resource = &Resource{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Resource, o.Resource) } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/RemoveService", +} + +func (m *RemoveResourceRequest) Copy() *RemoveResourceRequest { + if m == nil { + return nil } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).RemoveService(ctx, req.(*RemoveServiceRequest)) + o := &RemoveResourceRequest{} + o.CopyFrom(m) + return o +} + +func (m *RemoveResourceRequest) CopyFrom(src interface{}) { + + o := src.(*RemoveResourceRequest) + *m = *o +} + +func (m *RemoveResourceResponse) Copy() *RemoveResourceResponse { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &RemoveResourceResponse{} + o.CopyFrom(m) + return o } -func _Control_GetNetwork_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetNetworkRequest) - if err := dec(in); err != nil { - return nil, err +func (m *RemoveResourceResponse) CopyFrom(src interface{}) {} +func (m *UpdateResourceRequest) Copy() *UpdateResourceRequest { + if m == nil { + return nil } - if interceptor == nil { - return srv.(ControlServer).GetNetwork(ctx, in) + o := &UpdateResourceRequest{} + o.CopyFrom(m) + return o +} + +func (m *UpdateResourceRequest) CopyFrom(src interface{}) { + + o := src.(*UpdateResourceRequest) + *m = *o + if o.ResourceVersion != nil { + m.ResourceVersion = &Version{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.ResourceVersion, o.ResourceVersion) } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/GetNetwork", + if o.Annotations != nil { + m.Annotations = &Annotations{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Annotations, o.Annotations) } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetNetwork(ctx, req.(*GetNetworkRequest)) + if o.Payload != nil { + m.Payload = &types.Any{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Payload, o.Payload) } - return interceptor(ctx, in, info, handler) } -func _Control_ListNetworks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListNetworksRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ListNetworks(ctx, in) +func (m *UpdateResourceResponse) Copy() *UpdateResourceResponse { + if m == nil { + return nil } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/ListNetworks", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ListNetworks(ctx, req.(*ListNetworksRequest)) - } - return interceptor(ctx, in, info, handler) + o := &UpdateResourceResponse{} + o.CopyFrom(m) + return o } -func _Control_CreateNetwork_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateNetworkRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).CreateNetwork(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/CreateNetwork", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).CreateNetwork(ctx, req.(*CreateNetworkRequest)) - } - return interceptor(ctx, in, info, handler) -} +func (m *UpdateResourceResponse) CopyFrom(src interface{}) { -func _Control_RemoveNetwork_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveNetworkRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).RemoveNetwork(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/RemoveNetwork", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).RemoveNetwork(ctx, req.(*RemoveNetworkRequest)) + o := src.(*UpdateResourceResponse) + *m = *o + if o.Resource != nil { + m.Resource = &Resource{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Resource, o.Resource) } - return interceptor(ctx, in, info, handler) } -func _Control_GetCluster_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetClusterRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetCluster(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/GetCluster", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetCluster(ctx, req.(*GetClusterRequest)) +func (m *GetResourceRequest) Copy() *GetResourceRequest { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &GetResourceRequest{} + o.CopyFrom(m) + return o } -func _Control_ListClusters_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListClustersRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ListClusters(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/ListClusters", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ListClusters(ctx, req.(*ListClustersRequest)) - } - return interceptor(ctx, in, info, handler) +func (m *GetResourceRequest) CopyFrom(src interface{}) { + + o := src.(*GetResourceRequest) + *m = *o } -func _Control_UpdateCluster_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateClusterRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).UpdateCluster(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/UpdateCluster", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).UpdateCluster(ctx, req.(*UpdateClusterRequest)) +func (m *GetResourceResponse) Copy() *GetResourceResponse { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &GetResourceResponse{} + o.CopyFrom(m) + return o } -func _Control_GetSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSecretRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetSecret(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/GetSecret", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetSecret(ctx, req.(*GetSecretRequest)) +func (m *GetResourceResponse) CopyFrom(src interface{}) { + + o := src.(*GetResourceResponse) + *m = *o + if o.Resource != nil { + m.Resource = &Resource{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Resource, o.Resource) } - return interceptor(ctx, in, info, handler) } -func _Control_UpdateSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateSecretRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).UpdateSecret(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/UpdateSecret", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).UpdateSecret(ctx, req.(*UpdateSecretRequest)) +func (m *ListResourcesRequest) Copy() *ListResourcesRequest { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &ListResourcesRequest{} + o.CopyFrom(m) + return o } -func _Control_ListSecrets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSecretsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ListSecrets(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/ListSecrets", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ListSecrets(ctx, req.(*ListSecretsRequest)) +func (m *ListResourcesRequest) CopyFrom(src interface{}) { + + o := src.(*ListResourcesRequest) + *m = *o + if o.Filters != nil { + m.Filters = &ListResourcesRequest_Filters{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Filters, o.Filters) } - return interceptor(ctx, in, info, handler) } -func _Control_CreateSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateSecretRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).CreateSecret(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/CreateSecret", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).CreateSecret(ctx, req.(*CreateSecretRequest)) +func (m *ListResourcesRequest_Filters) Copy() *ListResourcesRequest_Filters { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &ListResourcesRequest_Filters{} + o.CopyFrom(m) + return o } -func _Control_RemoveSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveSecretRequest) - if err := dec(in); err != nil { - return nil, err +func (m *ListResourcesRequest_Filters) CopyFrom(src interface{}) { + + o := src.(*ListResourcesRequest_Filters) + *m = *o + if o.Names != nil { + m.Names = make([]string, len(o.Names)) + copy(m.Names, o.Names) } - if interceptor == nil { - return srv.(ControlServer).RemoveSecret(ctx, in) + + if o.IDPrefixes != nil { + m.IDPrefixes = make([]string, len(o.IDPrefixes)) + copy(m.IDPrefixes, o.IDPrefixes) } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/RemoveSecret", + + if o.Labels != nil { + m.Labels = make(map[string]string, len(o.Labels)) + for k, v := range o.Labels { + m.Labels[k] = v + } } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).RemoveSecret(ctx, req.(*RemoveSecretRequest)) + + if o.NamePrefixes != nil { + m.NamePrefixes = make([]string, len(o.NamePrefixes)) + copy(m.NamePrefixes, o.NamePrefixes) } - return interceptor(ctx, in, info, handler) + } -func _Control_GetConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetConfigRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).GetConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/GetConfig", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).GetConfig(ctx, req.(*GetConfigRequest)) +func (m *ListResourcesResponse) Copy() *ListResourcesResponse { + if m == nil { + return nil } - return interceptor(ctx, in, info, handler) + o := &ListResourcesResponse{} + o.CopyFrom(m) + return o } -func _Control_UpdateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateConfigRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).UpdateConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/UpdateConfig", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).UpdateConfig(ctx, req.(*UpdateConfigRequest)) +func (m *ListResourcesResponse) CopyFrom(src interface{}) { + + o := src.(*ListResourcesResponse) + *m = *o + if o.Resources != nil { + m.Resources = make([]*Resource, len(o.Resources)) + for i := range m.Resources { + m.Resources[i] = &Resource{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Resources[i], o.Resources[i]) + } } - return interceptor(ctx, in, info, handler) + } -func _Control_ListConfigs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListConfigsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).ListConfigs(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/ListConfigs", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).ListConfigs(ctx, req.(*ListConfigsRequest)) +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// ControlClient is the client API for Control service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ControlClient interface { + GetNode(ctx context.Context, in *GetNodeRequest, opts ...grpc.CallOption) (*GetNodeResponse, error) + ListNodes(ctx context.Context, in *ListNodesRequest, opts ...grpc.CallOption) (*ListNodesResponse, error) + UpdateNode(ctx context.Context, in *UpdateNodeRequest, opts ...grpc.CallOption) (*UpdateNodeResponse, error) + RemoveNode(ctx context.Context, in *RemoveNodeRequest, opts ...grpc.CallOption) (*RemoveNodeResponse, error) + GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskResponse, error) + ListTasks(ctx context.Context, in *ListTasksRequest, opts ...grpc.CallOption) (*ListTasksResponse, error) + RemoveTask(ctx context.Context, in *RemoveTaskRequest, opts ...grpc.CallOption) (*RemoveTaskResponse, error) + GetService(ctx context.Context, in *GetServiceRequest, opts ...grpc.CallOption) (*GetServiceResponse, error) + ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) + CreateService(ctx context.Context, in *CreateServiceRequest, opts ...grpc.CallOption) (*CreateServiceResponse, error) + UpdateService(ctx context.Context, in *UpdateServiceRequest, opts ...grpc.CallOption) (*UpdateServiceResponse, error) + RemoveService(ctx context.Context, in *RemoveServiceRequest, opts ...grpc.CallOption) (*RemoveServiceResponse, error) + // ListServiceStatuses returns a `ListServiceStatusesResponse` with the + // status of the requested services, formed by computing the number of + // running vs desired tasks. It is provided as a shortcut or helper method, + // which allows a client to avoid having to calculate this value by listing + // all Tasks. If any service requested does not exist, it will be returned + // but with empty status values. + ListServiceStatuses(ctx context.Context, in *ListServiceStatusesRequest, opts ...grpc.CallOption) (*ListServiceStatusesResponse, error) + GetNetwork(ctx context.Context, in *GetNetworkRequest, opts ...grpc.CallOption) (*GetNetworkResponse, error) + ListNetworks(ctx context.Context, in *ListNetworksRequest, opts ...grpc.CallOption) (*ListNetworksResponse, error) + CreateNetwork(ctx context.Context, in *CreateNetworkRequest, opts ...grpc.CallOption) (*CreateNetworkResponse, error) + RemoveNetwork(ctx context.Context, in *RemoveNetworkRequest, opts ...grpc.CallOption) (*RemoveNetworkResponse, error) + GetCluster(ctx context.Context, in *GetClusterRequest, opts ...grpc.CallOption) (*GetClusterResponse, error) + ListClusters(ctx context.Context, in *ListClustersRequest, opts ...grpc.CallOption) (*ListClustersResponse, error) + UpdateCluster(ctx context.Context, in *UpdateClusterRequest, opts ...grpc.CallOption) (*UpdateClusterResponse, error) + // GetSecret returns a `GetSecretResponse` with a `Secret` with the same + // id as `GetSecretRequest.SecretID` + // - Returns `NotFound` if the Secret with the given id is not found. + // - Returns `InvalidArgument` if the `GetSecretRequest.SecretID` is empty. + // - Returns an error if getting fails. + GetSecret(ctx context.Context, in *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error) + // UpdateSecret returns a `UpdateSecretResponse` with a `Secret` with the same + // id as `GetSecretRequest.SecretID` + // - Returns `NotFound` if the Secret with the given id is not found. + // - Returns `InvalidArgument` if the `GetSecretRequest.SecretID` is empty. + // - Returns an error if updating fails. + UpdateSecret(ctx context.Context, in *UpdateSecretRequest, opts ...grpc.CallOption) (*UpdateSecretResponse, error) + // ListSecrets returns a `ListSecretResponse` with a list of all non-internal `Secret`s being + // managed, or all secrets matching any name in `ListSecretsRequest.Names`, any + // name prefix in `ListSecretsRequest.NamePrefixes`, any id in + // `ListSecretsRequest.SecretIDs`, or any id prefix in `ListSecretsRequest.IDPrefixes`. + // - Returns an error if listing fails. + ListSecrets(ctx context.Context, in *ListSecretsRequest, opts ...grpc.CallOption) (*ListSecretsResponse, error) + // CreateSecret creates and return a `CreateSecretResponse` with a `Secret` based + // on the provided `CreateSecretRequest.SecretSpec`. + // - Returns `InvalidArgument` if the `CreateSecretRequest.SecretSpec` is malformed, + // or if the secret data is too long or contains invalid characters. + // - Returns an error if the creation fails. + CreateSecret(ctx context.Context, in *CreateSecretRequest, opts ...grpc.CallOption) (*CreateSecretResponse, error) + // RemoveSecret removes the secret referenced by `RemoveSecretRequest.ID`. + // - Returns `InvalidArgument` if `RemoveSecretRequest.ID` is empty. + // - Returns `NotFound` if the a secret named `RemoveSecretRequest.ID` is not found. + // - Returns an error if the deletion fails. + RemoveSecret(ctx context.Context, in *RemoveSecretRequest, opts ...grpc.CallOption) (*RemoveSecretResponse, error) + // GetConfig returns a `GetConfigResponse` with a `Config` with the same + // id as `GetConfigRequest.ConfigID` + // - Returns `NotFound` if the Config with the given id is not found. + // - Returns `InvalidArgument` if the `GetConfigRequest.ConfigID` is empty. + // - Returns an error if getting fails. + GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error) + // UpdateConfig returns a `UpdateConfigResponse` with a `Config` with the same + // id as `GetConfigRequest.ConfigID` + // - Returns `NotFound` if the Config with the given id is not found. + // - Returns `InvalidArgument` if the `GetConfigRequest.ConfigID` is empty. + // - Returns an error if updating fails. + UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*UpdateConfigResponse, error) + // ListConfigs returns a `ListConfigResponse` with a list of `Config`s being + // managed, or all configs matching any name in `ListConfigsRequest.Names`, any + // name prefix in `ListConfigsRequest.NamePrefixes`, any id in + // `ListConfigsRequest.ConfigIDs`, or any id prefix in `ListConfigsRequest.IDPrefixes`. + // - Returns an error if listing fails. + ListConfigs(ctx context.Context, in *ListConfigsRequest, opts ...grpc.CallOption) (*ListConfigsResponse, error) + // CreateConfig creates and return a `CreateConfigResponse` with a `Config` based + // on the provided `CreateConfigRequest.ConfigSpec`. + // - Returns `InvalidArgument` if the `CreateConfigRequest.ConfigSpec` is malformed, + // or if the config data is too long or contains invalid characters. + // - Returns an error if the creation fails. + CreateConfig(ctx context.Context, in *CreateConfigRequest, opts ...grpc.CallOption) (*CreateConfigResponse, error) + // RemoveConfig removes the config referenced by `RemoveConfigRequest.ID`. + // - Returns `InvalidArgument` if `RemoveConfigRequest.ID` is empty. + // - Returns `NotFound` if the a config named `RemoveConfigRequest.ID` is not found. + // - Returns an error if the deletion fails. + RemoveConfig(ctx context.Context, in *RemoveConfigRequest, opts ...grpc.CallOption) (*RemoveConfigResponse, error) + // GetExtension returns a `GetExtensionResponse` with a `Extension` with the same + // id as `GetExtensionRequest.ExtensionId` + // - Returns `NotFound` if the Extension with the given id is not found. + // - Returns `InvalidArgument` if the `GetExtensionRequest.ExtensionId` is empty. + // - Returns an error if the get fails. + GetExtension(ctx context.Context, in *GetExtensionRequest, opts ...grpc.CallOption) (*GetExtensionResponse, error) + // CreateExtension creates an `Extension` based on the provided `CreateExtensionRequest.Extension` + // and returns a `CreateExtensionResponse`. + // - Returns `InvalidArgument` if the `CreateExtensionRequest.Extension` is malformed, + // or fails validation. + // - Returns an error if the creation fails. + CreateExtension(ctx context.Context, in *CreateExtensionRequest, opts ...grpc.CallOption) (*CreateExtensionResponse, error) + // RemoveExtension removes the extension referenced by `RemoveExtensionRequest.ID`. + // - Returns `InvalidArgument` if `RemoveExtensionRequest.ExtensionId` is empty. + // - Returns `NotFound` if the an extension named `RemoveExtensionRequest.ExtensionId` is not found. + // - Returns an error if the deletion fails. + RemoveExtension(ctx context.Context, in *RemoveExtensionRequest, opts ...grpc.CallOption) (*RemoveExtensionResponse, error) + // GetResource returns a `GetResourceResponse` with a `Resource` with the same + // id as `GetResourceRequest.Resource` + // - Returns `NotFound` if the Resource with the given id is not found. + // - Returns `InvalidArgument` if the `GetResourceRequest.Resource` is empty. + // - Returns an error if getting fails. + GetResource(ctx context.Context, in *GetResourceRequest, opts ...grpc.CallOption) (*GetResourceResponse, error) + // UpdateResource updates the resource with the given `UpdateResourceRequest.Resource.Id` using the given `UpdateResourceRequest.Resource` and returns a `UpdateResourceResponse`. + // - Returns `NotFound` if the Resource with the given `UpdateResourceRequest.Resource.Id` is not found. + // - Returns `InvalidArgument` if the UpdateResourceRequest.Resource.Id` is empty. + // - Returns an error if updating fails. + UpdateResource(ctx context.Context, in *UpdateResourceRequest, opts ...grpc.CallOption) (*UpdateResourceResponse, error) + // ListResources returns a `ListResourcesResponse` with a list of `Resource`s stored in the raft store, + // or all resources matching any name in `ListConfigsRequest.Names`, any + // name prefix in `ListResourcesRequest.NamePrefixes`, any id in + // `ListResourcesRequest.ResourceIDs`, or any id prefix in `ListResourcesRequest.IDPrefixes`, + // extension name equal to `ListResourcesRequest.Extension`. + // - Returns an error if listing fails. + ListResources(ctx context.Context, in *ListResourcesRequest, opts ...grpc.CallOption) (*ListResourcesResponse, error) + // CreateResource returns a `CreateResourceResponse` after creating a `Resource` based + // on the provided `CreateResourceRequest.Resource`. + // - Returns `InvalidArgument` if the `CreateResourceRequest.Resource` is malformed, + // or if the config data is too long or contains invalid characters. + // - Returns an error if the creation fails. + CreateResource(ctx context.Context, in *CreateResourceRequest, opts ...grpc.CallOption) (*CreateResourceResponse, error) + // RemoveResource removes the `Resource` referenced by `RemoveResourceRequest.ResourceID`. + // - Returns `InvalidArgument` if `RemoveResourceRequest.ResourceID` is empty. + // - Returns `NotFound` if the a resource named `RemoveResourceRequest.ResourceID` is not found. + // - Returns an error if the deletion fails. + RemoveResource(ctx context.Context, in *RemoveResourceRequest, opts ...grpc.CallOption) (*RemoveResourceResponse, error) +} + +type controlClient struct { + cc *grpc.ClientConn +} + +func NewControlClient(cc *grpc.ClientConn) ControlClient { + return &controlClient{cc} +} + +func (c *controlClient) GetNode(ctx context.Context, in *GetNodeRequest, opts ...grpc.CallOption) (*GetNodeResponse, error) { + out := new(GetNodeResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetNode", in, out, opts...) + if err != nil { + return nil, err } - return interceptor(ctx, in, info, handler) + return out, nil } -func _Control_CreateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateConfigRequest) - if err := dec(in); err != nil { +func (c *controlClient) ListNodes(ctx context.Context, in *ListNodesRequest, opts ...grpc.CallOption) (*ListNodesResponse, error) { + out := new(ListNodesResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListNodes", in, out, opts...) + if err != nil { return nil, err } - if interceptor == nil { - return srv.(ControlServer).CreateConfig(ctx, in) + return out, nil +} + +func (c *controlClient) UpdateNode(ctx context.Context, in *UpdateNodeRequest, opts ...grpc.CallOption) (*UpdateNodeResponse, error) { + out := new(UpdateNodeResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateNode", in, out, opts...) + if err != nil { + return nil, err } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/CreateConfig", + return out, nil +} + +func (c *controlClient) RemoveNode(ctx context.Context, in *RemoveNodeRequest, opts ...grpc.CallOption) (*RemoveNodeResponse, error) { + out := new(RemoveNodeResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveNode", in, out, opts...) + if err != nil { + return nil, err } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).CreateConfig(ctx, req.(*CreateConfigRequest)) + return out, nil +} + +func (c *controlClient) GetTask(ctx context.Context, in *GetTaskRequest, opts ...grpc.CallOption) (*GetTaskResponse, error) { + out := new(GetTaskResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetTask", in, out, opts...) + if err != nil { + return nil, err } - return interceptor(ctx, in, info, handler) + return out, nil } -func _Control_RemoveConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RemoveConfigRequest) - if err := dec(in); err != nil { +func (c *controlClient) ListTasks(ctx context.Context, in *ListTasksRequest, opts ...grpc.CallOption) (*ListTasksResponse, error) { + out := new(ListTasksResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListTasks", in, out, opts...) + if err != nil { return nil, err } - if interceptor == nil { - return srv.(ControlServer).RemoveConfig(ctx, in) + return out, nil +} + +func (c *controlClient) RemoveTask(ctx context.Context, in *RemoveTaskRequest, opts ...grpc.CallOption) (*RemoveTaskResponse, error) { + out := new(RemoveTaskResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveTask", in, out, opts...) + if err != nil { + return nil, err } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/docker.swarmkit.v1.Control/RemoveConfig", + return out, nil +} + +func (c *controlClient) GetService(ctx context.Context, in *GetServiceRequest, opts ...grpc.CallOption) (*GetServiceResponse, error) { + out := new(GetServiceResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetService", in, out, opts...) + if err != nil { + return nil, err } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).RemoveConfig(ctx, req.(*RemoveConfigRequest)) + return out, nil +} + +func (c *controlClient) ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) { + out := new(ListServicesResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListServices", in, out, opts...) + if err != nil { + return nil, err } - return interceptor(ctx, in, info, handler) + return out, nil } -var _Control_serviceDesc = grpc.ServiceDesc{ - ServiceName: "docker.swarmkit.v1.Control", - HandlerType: (*ControlServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "GetNode", - Handler: _Control_GetNode_Handler, - }, - { - MethodName: "ListNodes", - Handler: _Control_ListNodes_Handler, - }, - { - MethodName: "UpdateNode", - Handler: _Control_UpdateNode_Handler, - }, - { - MethodName: "RemoveNode", - Handler: _Control_RemoveNode_Handler, - }, - { - MethodName: "GetTask", - Handler: _Control_GetTask_Handler, - }, - { - MethodName: "ListTasks", - Handler: _Control_ListTasks_Handler, - }, - { - MethodName: "RemoveTask", - Handler: _Control_RemoveTask_Handler, - }, - { - MethodName: "GetService", - Handler: _Control_GetService_Handler, - }, - { - MethodName: "ListServices", - Handler: _Control_ListServices_Handler, - }, - { - MethodName: "CreateService", - Handler: _Control_CreateService_Handler, - }, - { - MethodName: "UpdateService", - Handler: _Control_UpdateService_Handler, - }, - { - MethodName: "RemoveService", - Handler: _Control_RemoveService_Handler, - }, - { - MethodName: "GetNetwork", - Handler: _Control_GetNetwork_Handler, - }, - { - MethodName: "ListNetworks", - Handler: _Control_ListNetworks_Handler, - }, - { - MethodName: "CreateNetwork", - Handler: _Control_CreateNetwork_Handler, - }, - { - MethodName: "RemoveNetwork", - Handler: _Control_RemoveNetwork_Handler, - }, - { - MethodName: "GetCluster", - Handler: _Control_GetCluster_Handler, - }, - { - MethodName: "ListClusters", - Handler: _Control_ListClusters_Handler, - }, - { - MethodName: "UpdateCluster", - Handler: _Control_UpdateCluster_Handler, - }, - { - MethodName: "GetSecret", - Handler: _Control_GetSecret_Handler, - }, - { - MethodName: "UpdateSecret", - Handler: _Control_UpdateSecret_Handler, - }, - { - MethodName: "ListSecrets", - Handler: _Control_ListSecrets_Handler, - }, - { - MethodName: "CreateSecret", - Handler: _Control_CreateSecret_Handler, - }, - { - MethodName: "RemoveSecret", - Handler: _Control_RemoveSecret_Handler, - }, - { - MethodName: "GetConfig", - Handler: _Control_GetConfig_Handler, - }, - { - MethodName: "UpdateConfig", - Handler: _Control_UpdateConfig_Handler, - }, - { - MethodName: "ListConfigs", - Handler: _Control_ListConfigs_Handler, - }, - { - MethodName: "CreateConfig", - Handler: _Control_CreateConfig_Handler, - }, - { - MethodName: "RemoveConfig", - Handler: _Control_RemoveConfig_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "github.com/docker/swarmkit/api/control.proto", -} - -func (m *GetNodeRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) CreateService(ctx context.Context, in *CreateServiceRequest, opts ...grpc.CallOption) (*CreateServiceResponse, error) { + out := new(CreateServiceResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/CreateService", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *GetNodeRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.NodeID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.NodeID))) - i += copy(dAtA[i:], m.NodeID) +func (c *controlClient) UpdateService(ctx context.Context, in *UpdateServiceRequest, opts ...grpc.CallOption) (*UpdateServiceResponse, error) { + out := new(UpdateServiceResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateService", in, out, opts...) + if err != nil { + return nil, err } - return i, nil + return out, nil } -func (m *GetNodeResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) RemoveService(ctx context.Context, in *RemoveServiceRequest, opts ...grpc.CallOption) (*RemoveServiceResponse, error) { + out := new(RemoveServiceResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveService", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *GetNodeResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Node != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Node.Size())) - n1, err := m.Node.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 +func (c *controlClient) ListServiceStatuses(ctx context.Context, in *ListServiceStatusesRequest, opts ...grpc.CallOption) (*ListServiceStatusesResponse, error) { + out := new(ListServiceStatusesResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListServiceStatuses", in, out, opts...) + if err != nil { + return nil, err } - return i, nil + return out, nil } -func (m *ListNodesRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) GetNetwork(ctx context.Context, in *GetNetworkRequest, opts ...grpc.CallOption) (*GetNetworkResponse, error) { + out := new(GetNetworkResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetNetwork", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *ListNodesRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Filters != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Filters.Size())) - n2, err := m.Filters.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 +func (c *controlClient) ListNetworks(ctx context.Context, in *ListNetworksRequest, opts ...grpc.CallOption) (*ListNetworksResponse, error) { + out := new(ListNetworksResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListNetworks", in, out, opts...) + if err != nil { + return nil, err } - return i, nil + return out, nil } -func (m *ListNodesRequest_Filters) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) CreateNetwork(ctx context.Context, in *CreateNetworkRequest, opts ...grpc.CallOption) (*CreateNetworkResponse, error) { + out := new(CreateNetworkResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/CreateNetwork", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *ListNodesRequest_Filters) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x1a - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - i = encodeVarintControl(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) - } - } - if len(m.Memberships) > 0 { - for _, num := range m.Memberships { - dAtA[i] = 0x20 - i++ - i = encodeVarintControl(dAtA, i, uint64(num)) - } - } - if len(m.Roles) > 0 { - for _, num := range m.Roles { - dAtA[i] = 0x28 - i++ - i = encodeVarintControl(dAtA, i, uint64(num)) - } - } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - dAtA[i] = 0x32 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.NodeLabels) > 0 { - for k, _ := range m.NodeLabels { - dAtA[i] = 0x3a - i++ - v := m.NodeLabels[k] - mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - i = encodeVarintControl(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) - } +func (c *controlClient) RemoveNetwork(ctx context.Context, in *RemoveNetworkRequest, opts ...grpc.CallOption) (*RemoveNetworkResponse, error) { + out := new(RemoveNetworkResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveNetwork", in, out, opts...) + if err != nil { + return nil, err } - return i, nil + return out, nil } -func (m *ListNodesResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) GetCluster(ctx context.Context, in *GetClusterRequest, opts ...grpc.CallOption) (*GetClusterResponse, error) { + out := new(GetClusterResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetCluster", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *ListNodesResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Nodes) > 0 { - for _, msg := range m.Nodes { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (c *controlClient) ListClusters(ctx context.Context, in *ListClustersRequest, opts ...grpc.CallOption) (*ListClustersResponse, error) { + out := new(ListClustersResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListClusters", in, out, opts...) + if err != nil { + return nil, err } - return i, nil + return out, nil } -func (m *UpdateNodeRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) UpdateCluster(ctx context.Context, in *UpdateClusterRequest, opts ...grpc.CallOption) (*UpdateClusterResponse, error) { + out := new(UpdateClusterResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateCluster", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *UpdateNodeRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.NodeID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.NodeID))) - i += copy(dAtA[i:], m.NodeID) - } - if m.NodeVersion != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(m.NodeVersion.Size())) - n3, err := m.NodeVersion.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n3 - } - if m.Spec != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Spec.Size())) - n4, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n4 +func (c *controlClient) GetSecret(ctx context.Context, in *GetSecretRequest, opts ...grpc.CallOption) (*GetSecretResponse, error) { + out := new(GetSecretResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetSecret", in, out, opts...) + if err != nil { + return nil, err } - return i, nil + return out, nil } -func (m *UpdateNodeResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) UpdateSecret(ctx context.Context, in *UpdateSecretRequest, opts ...grpc.CallOption) (*UpdateSecretResponse, error) { + out := new(UpdateSecretResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateSecret", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *UpdateNodeResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Node != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Node.Size())) - n5, err := m.Node.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n5 +func (c *controlClient) ListSecrets(ctx context.Context, in *ListSecretsRequest, opts ...grpc.CallOption) (*ListSecretsResponse, error) { + out := new(ListSecretsResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListSecrets", in, out, opts...) + if err != nil { + return nil, err } - return i, nil + return out, nil } -func (m *RemoveNodeRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) CreateSecret(ctx context.Context, in *CreateSecretRequest, opts ...grpc.CallOption) (*CreateSecretResponse, error) { + out := new(CreateSecretResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/CreateSecret", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *RemoveNodeRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.NodeID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.NodeID))) - i += copy(dAtA[i:], m.NodeID) - } - if m.Force { - dAtA[i] = 0x10 - i++ - if m.Force { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - return i, nil -} - -func (m *RemoveNodeResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) RemoveSecret(ctx context.Context, in *RemoveSecretRequest, opts ...grpc.CallOption) (*RemoveSecretResponse, error) { + out := new(RemoveSecretResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveSecret", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *RemoveNodeResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - return i, nil + return out, nil } -func (m *GetTaskRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error) { + out := new(GetConfigResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetConfig", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *GetTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.TaskID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.TaskID))) - i += copy(dAtA[i:], m.TaskID) - } - return i, nil + return out, nil } -func (m *GetTaskResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*UpdateConfigResponse, error) { + out := new(UpdateConfigResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateConfig", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *GetTaskResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Task != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Task.Size())) - n6, err := m.Task.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n6 - } - return i, nil + return out, nil } -func (m *RemoveTaskRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) ListConfigs(ctx context.Context, in *ListConfigsRequest, opts ...grpc.CallOption) (*ListConfigsResponse, error) { + out := new(ListConfigsResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListConfigs", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *RemoveTaskRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.TaskID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.TaskID))) - i += copy(dAtA[i:], m.TaskID) - } - return i, nil + return out, nil } -func (m *RemoveTaskResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) CreateConfig(ctx context.Context, in *CreateConfigRequest, opts ...grpc.CallOption) (*CreateConfigResponse, error) { + out := new(CreateConfigResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/CreateConfig", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *RemoveTaskResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - return i, nil + return out, nil } -func (m *ListTasksRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) RemoveConfig(ctx context.Context, in *RemoveConfigRequest, opts ...grpc.CallOption) (*RemoveConfigResponse, error) { + out := new(RemoveConfigResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveConfig", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *ListTasksRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Filters != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Filters.Size())) - n7, err := m.Filters.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n7 - } - return i, nil + return out, nil } -func (m *ListTasksRequest_Filters) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) GetExtension(ctx context.Context, in *GetExtensionRequest, opts ...grpc.CallOption) (*GetExtensionResponse, error) { + out := new(GetExtensionResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetExtension", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *ListTasksRequest_Filters) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x1a - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - i = encodeVarintControl(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) - } - } - if len(m.ServiceIDs) > 0 { - for _, s := range m.ServiceIDs { - dAtA[i] = 0x22 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.NodeIDs) > 0 { - for _, s := range m.NodeIDs { - dAtA[i] = 0x2a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.DesiredStates) > 0 { - for _, num := range m.DesiredStates { - dAtA[i] = 0x30 - i++ - i = encodeVarintControl(dAtA, i, uint64(num)) - } - } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - dAtA[i] = 0x3a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if m.UpToDate { - dAtA[i] = 0x40 - i++ - if m.UpToDate { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - if len(m.Runtimes) > 0 { - for _, s := range m.Runtimes { - dAtA[i] = 0x4a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - return i, nil + return out, nil } -func (m *ListTasksResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) CreateExtension(ctx context.Context, in *CreateExtensionRequest, opts ...grpc.CallOption) (*CreateExtensionResponse, error) { + out := new(CreateExtensionResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/CreateExtension", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *ListTasksResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Tasks) > 0 { - for _, msg := range m.Tasks { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } +func (c *controlClient) RemoveExtension(ctx context.Context, in *RemoveExtensionRequest, opts ...grpc.CallOption) (*RemoveExtensionResponse, error) { + out := new(RemoveExtensionResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveExtension", in, out, opts...) + if err != nil { + return nil, err } - return i, nil + return out, nil } -func (m *CreateServiceRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) GetResource(ctx context.Context, in *GetResourceRequest, opts ...grpc.CallOption) (*GetResourceResponse, error) { + out := new(GetResourceResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/GetResource", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *CreateServiceRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Spec != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Spec.Size())) - n8, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n8 +func (c *controlClient) UpdateResource(ctx context.Context, in *UpdateResourceRequest, opts ...grpc.CallOption) (*UpdateResourceResponse, error) { + out := new(UpdateResourceResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/UpdateResource", in, out, opts...) + if err != nil { + return nil, err } - return i, nil + return out, nil } -func (m *CreateServiceResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) ListResources(ctx context.Context, in *ListResourcesRequest, opts ...grpc.CallOption) (*ListResourcesResponse, error) { + out := new(ListResourcesResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/ListResources", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *CreateServiceResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Service != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Service.Size())) - n9, err := m.Service.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n9 +func (c *controlClient) CreateResource(ctx context.Context, in *CreateResourceRequest, opts ...grpc.CallOption) (*CreateResourceResponse, error) { + out := new(CreateResourceResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/CreateResource", in, out, opts...) + if err != nil { + return nil, err } - return i, nil + return out, nil } -func (m *GetServiceRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) +func (c *controlClient) RemoveResource(ctx context.Context, in *RemoveResourceRequest, opts ...grpc.CallOption) (*RemoveResourceResponse, error) { + out := new(RemoveResourceResponse) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Control/RemoveResource", in, out, opts...) if err != nil { return nil, err } - return dAtA[:n], nil + return out, nil } -func (m *GetServiceRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ServiceID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.ServiceID))) - i += copy(dAtA[i:], m.ServiceID) - } - if m.InsertDefaults { - dAtA[i] = 0x10 - i++ - if m.InsertDefaults { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ - } - return i, nil +// ControlServer is the server API for Control service. +type ControlServer interface { + GetNode(context.Context, *GetNodeRequest) (*GetNodeResponse, error) + ListNodes(context.Context, *ListNodesRequest) (*ListNodesResponse, error) + UpdateNode(context.Context, *UpdateNodeRequest) (*UpdateNodeResponse, error) + RemoveNode(context.Context, *RemoveNodeRequest) (*RemoveNodeResponse, error) + GetTask(context.Context, *GetTaskRequest) (*GetTaskResponse, error) + ListTasks(context.Context, *ListTasksRequest) (*ListTasksResponse, error) + RemoveTask(context.Context, *RemoveTaskRequest) (*RemoveTaskResponse, error) + GetService(context.Context, *GetServiceRequest) (*GetServiceResponse, error) + ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error) + CreateService(context.Context, *CreateServiceRequest) (*CreateServiceResponse, error) + UpdateService(context.Context, *UpdateServiceRequest) (*UpdateServiceResponse, error) + RemoveService(context.Context, *RemoveServiceRequest) (*RemoveServiceResponse, error) + // ListServiceStatuses returns a `ListServiceStatusesResponse` with the + // status of the requested services, formed by computing the number of + // running vs desired tasks. It is provided as a shortcut or helper method, + // which allows a client to avoid having to calculate this value by listing + // all Tasks. If any service requested does not exist, it will be returned + // but with empty status values. + ListServiceStatuses(context.Context, *ListServiceStatusesRequest) (*ListServiceStatusesResponse, error) + GetNetwork(context.Context, *GetNetworkRequest) (*GetNetworkResponse, error) + ListNetworks(context.Context, *ListNetworksRequest) (*ListNetworksResponse, error) + CreateNetwork(context.Context, *CreateNetworkRequest) (*CreateNetworkResponse, error) + RemoveNetwork(context.Context, *RemoveNetworkRequest) (*RemoveNetworkResponse, error) + GetCluster(context.Context, *GetClusterRequest) (*GetClusterResponse, error) + ListClusters(context.Context, *ListClustersRequest) (*ListClustersResponse, error) + UpdateCluster(context.Context, *UpdateClusterRequest) (*UpdateClusterResponse, error) + // GetSecret returns a `GetSecretResponse` with a `Secret` with the same + // id as `GetSecretRequest.SecretID` + // - Returns `NotFound` if the Secret with the given id is not found. + // - Returns `InvalidArgument` if the `GetSecretRequest.SecretID` is empty. + // - Returns an error if getting fails. + GetSecret(context.Context, *GetSecretRequest) (*GetSecretResponse, error) + // UpdateSecret returns a `UpdateSecretResponse` with a `Secret` with the same + // id as `GetSecretRequest.SecretID` + // - Returns `NotFound` if the Secret with the given id is not found. + // - Returns `InvalidArgument` if the `GetSecretRequest.SecretID` is empty. + // - Returns an error if updating fails. + UpdateSecret(context.Context, *UpdateSecretRequest) (*UpdateSecretResponse, error) + // ListSecrets returns a `ListSecretResponse` with a list of all non-internal `Secret`s being + // managed, or all secrets matching any name in `ListSecretsRequest.Names`, any + // name prefix in `ListSecretsRequest.NamePrefixes`, any id in + // `ListSecretsRequest.SecretIDs`, or any id prefix in `ListSecretsRequest.IDPrefixes`. + // - Returns an error if listing fails. + ListSecrets(context.Context, *ListSecretsRequest) (*ListSecretsResponse, error) + // CreateSecret creates and return a `CreateSecretResponse` with a `Secret` based + // on the provided `CreateSecretRequest.SecretSpec`. + // - Returns `InvalidArgument` if the `CreateSecretRequest.SecretSpec` is malformed, + // or if the secret data is too long or contains invalid characters. + // - Returns an error if the creation fails. + CreateSecret(context.Context, *CreateSecretRequest) (*CreateSecretResponse, error) + // RemoveSecret removes the secret referenced by `RemoveSecretRequest.ID`. + // - Returns `InvalidArgument` if `RemoveSecretRequest.ID` is empty. + // - Returns `NotFound` if the a secret named `RemoveSecretRequest.ID` is not found. + // - Returns an error if the deletion fails. + RemoveSecret(context.Context, *RemoveSecretRequest) (*RemoveSecretResponse, error) + // GetConfig returns a `GetConfigResponse` with a `Config` with the same + // id as `GetConfigRequest.ConfigID` + // - Returns `NotFound` if the Config with the given id is not found. + // - Returns `InvalidArgument` if the `GetConfigRequest.ConfigID` is empty. + // - Returns an error if getting fails. + GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error) + // UpdateConfig returns a `UpdateConfigResponse` with a `Config` with the same + // id as `GetConfigRequest.ConfigID` + // - Returns `NotFound` if the Config with the given id is not found. + // - Returns `InvalidArgument` if the `GetConfigRequest.ConfigID` is empty. + // - Returns an error if updating fails. + UpdateConfig(context.Context, *UpdateConfigRequest) (*UpdateConfigResponse, error) + // ListConfigs returns a `ListConfigResponse` with a list of `Config`s being + // managed, or all configs matching any name in `ListConfigsRequest.Names`, any + // name prefix in `ListConfigsRequest.NamePrefixes`, any id in + // `ListConfigsRequest.ConfigIDs`, or any id prefix in `ListConfigsRequest.IDPrefixes`. + // - Returns an error if listing fails. + ListConfigs(context.Context, *ListConfigsRequest) (*ListConfigsResponse, error) + // CreateConfig creates and return a `CreateConfigResponse` with a `Config` based + // on the provided `CreateConfigRequest.ConfigSpec`. + // - Returns `InvalidArgument` if the `CreateConfigRequest.ConfigSpec` is malformed, + // or if the config data is too long or contains invalid characters. + // - Returns an error if the creation fails. + CreateConfig(context.Context, *CreateConfigRequest) (*CreateConfigResponse, error) + // RemoveConfig removes the config referenced by `RemoveConfigRequest.ID`. + // - Returns `InvalidArgument` if `RemoveConfigRequest.ID` is empty. + // - Returns `NotFound` if the a config named `RemoveConfigRequest.ID` is not found. + // - Returns an error if the deletion fails. + RemoveConfig(context.Context, *RemoveConfigRequest) (*RemoveConfigResponse, error) + // GetExtension returns a `GetExtensionResponse` with a `Extension` with the same + // id as `GetExtensionRequest.ExtensionId` + // - Returns `NotFound` if the Extension with the given id is not found. + // - Returns `InvalidArgument` if the `GetExtensionRequest.ExtensionId` is empty. + // - Returns an error if the get fails. + GetExtension(context.Context, *GetExtensionRequest) (*GetExtensionResponse, error) + // CreateExtension creates an `Extension` based on the provided `CreateExtensionRequest.Extension` + // and returns a `CreateExtensionResponse`. + // - Returns `InvalidArgument` if the `CreateExtensionRequest.Extension` is malformed, + // or fails validation. + // - Returns an error if the creation fails. + CreateExtension(context.Context, *CreateExtensionRequest) (*CreateExtensionResponse, error) + // RemoveExtension removes the extension referenced by `RemoveExtensionRequest.ID`. + // - Returns `InvalidArgument` if `RemoveExtensionRequest.ExtensionId` is empty. + // - Returns `NotFound` if the an extension named `RemoveExtensionRequest.ExtensionId` is not found. + // - Returns an error if the deletion fails. + RemoveExtension(context.Context, *RemoveExtensionRequest) (*RemoveExtensionResponse, error) + // GetResource returns a `GetResourceResponse` with a `Resource` with the same + // id as `GetResourceRequest.Resource` + // - Returns `NotFound` if the Resource with the given id is not found. + // - Returns `InvalidArgument` if the `GetResourceRequest.Resource` is empty. + // - Returns an error if getting fails. + GetResource(context.Context, *GetResourceRequest) (*GetResourceResponse, error) + // UpdateResource updates the resource with the given `UpdateResourceRequest.Resource.Id` using the given `UpdateResourceRequest.Resource` and returns a `UpdateResourceResponse`. + // - Returns `NotFound` if the Resource with the given `UpdateResourceRequest.Resource.Id` is not found. + // - Returns `InvalidArgument` if the UpdateResourceRequest.Resource.Id` is empty. + // - Returns an error if updating fails. + UpdateResource(context.Context, *UpdateResourceRequest) (*UpdateResourceResponse, error) + // ListResources returns a `ListResourcesResponse` with a list of `Resource`s stored in the raft store, + // or all resources matching any name in `ListConfigsRequest.Names`, any + // name prefix in `ListResourcesRequest.NamePrefixes`, any id in + // `ListResourcesRequest.ResourceIDs`, or any id prefix in `ListResourcesRequest.IDPrefixes`, + // extension name equal to `ListResourcesRequest.Extension`. + // - Returns an error if listing fails. + ListResources(context.Context, *ListResourcesRequest) (*ListResourcesResponse, error) + // CreateResource returns a `CreateResourceResponse` after creating a `Resource` based + // on the provided `CreateResourceRequest.Resource`. + // - Returns `InvalidArgument` if the `CreateResourceRequest.Resource` is malformed, + // or if the config data is too long or contains invalid characters. + // - Returns an error if the creation fails. + CreateResource(context.Context, *CreateResourceRequest) (*CreateResourceResponse, error) + // RemoveResource removes the `Resource` referenced by `RemoveResourceRequest.ResourceID`. + // - Returns `InvalidArgument` if `RemoveResourceRequest.ResourceID` is empty. + // - Returns `NotFound` if the a resource named `RemoveResourceRequest.ResourceID` is not found. + // - Returns an error if the deletion fails. + RemoveResource(context.Context, *RemoveResourceRequest) (*RemoveResourceResponse, error) } -func (m *GetServiceResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +// UnimplementedControlServer can be embedded to have forward compatible implementations. +type UnimplementedControlServer struct { } -func (m *GetServiceResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Service != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Service.Size())) - n10, err := m.Service.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n10 - } - return i, nil +func (*UnimplementedControlServer) GetNode(ctx context.Context, req *GetNodeRequest) (*GetNodeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetNode not implemented") +} +func (*UnimplementedControlServer) ListNodes(ctx context.Context, req *ListNodesRequest) (*ListNodesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListNodes not implemented") +} +func (*UnimplementedControlServer) UpdateNode(ctx context.Context, req *UpdateNodeRequest) (*UpdateNodeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateNode not implemented") +} +func (*UnimplementedControlServer) RemoveNode(ctx context.Context, req *RemoveNodeRequest) (*RemoveNodeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveNode not implemented") +} +func (*UnimplementedControlServer) GetTask(ctx context.Context, req *GetTaskRequest) (*GetTaskResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTask not implemented") +} +func (*UnimplementedControlServer) ListTasks(ctx context.Context, req *ListTasksRequest) (*ListTasksResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListTasks not implemented") +} +func (*UnimplementedControlServer) RemoveTask(ctx context.Context, req *RemoveTaskRequest) (*RemoveTaskResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveTask not implemented") +} +func (*UnimplementedControlServer) GetService(ctx context.Context, req *GetServiceRequest) (*GetServiceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetService not implemented") +} +func (*UnimplementedControlServer) ListServices(ctx context.Context, req *ListServicesRequest) (*ListServicesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListServices not implemented") +} +func (*UnimplementedControlServer) CreateService(ctx context.Context, req *CreateServiceRequest) (*CreateServiceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateService not implemented") +} +func (*UnimplementedControlServer) UpdateService(ctx context.Context, req *UpdateServiceRequest) (*UpdateServiceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateService not implemented") +} +func (*UnimplementedControlServer) RemoveService(ctx context.Context, req *RemoveServiceRequest) (*RemoveServiceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveService not implemented") +} +func (*UnimplementedControlServer) ListServiceStatuses(ctx context.Context, req *ListServiceStatusesRequest) (*ListServiceStatusesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListServiceStatuses not implemented") +} +func (*UnimplementedControlServer) GetNetwork(ctx context.Context, req *GetNetworkRequest) (*GetNetworkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetNetwork not implemented") +} +func (*UnimplementedControlServer) ListNetworks(ctx context.Context, req *ListNetworksRequest) (*ListNetworksResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListNetworks not implemented") +} +func (*UnimplementedControlServer) CreateNetwork(ctx context.Context, req *CreateNetworkRequest) (*CreateNetworkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateNetwork not implemented") +} +func (*UnimplementedControlServer) RemoveNetwork(ctx context.Context, req *RemoveNetworkRequest) (*RemoveNetworkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveNetwork not implemented") +} +func (*UnimplementedControlServer) GetCluster(ctx context.Context, req *GetClusterRequest) (*GetClusterResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCluster not implemented") +} +func (*UnimplementedControlServer) ListClusters(ctx context.Context, req *ListClustersRequest) (*ListClustersResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListClusters not implemented") +} +func (*UnimplementedControlServer) UpdateCluster(ctx context.Context, req *UpdateClusterRequest) (*UpdateClusterResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateCluster not implemented") +} +func (*UnimplementedControlServer) GetSecret(ctx context.Context, req *GetSecretRequest) (*GetSecretResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSecret not implemented") +} +func (*UnimplementedControlServer) UpdateSecret(ctx context.Context, req *UpdateSecretRequest) (*UpdateSecretResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateSecret not implemented") +} +func (*UnimplementedControlServer) ListSecrets(ctx context.Context, req *ListSecretsRequest) (*ListSecretsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListSecrets not implemented") +} +func (*UnimplementedControlServer) CreateSecret(ctx context.Context, req *CreateSecretRequest) (*CreateSecretResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateSecret not implemented") +} +func (*UnimplementedControlServer) RemoveSecret(ctx context.Context, req *RemoveSecretRequest) (*RemoveSecretResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveSecret not implemented") +} +func (*UnimplementedControlServer) GetConfig(ctx context.Context, req *GetConfigRequest) (*GetConfigResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetConfig not implemented") +} +func (*UnimplementedControlServer) UpdateConfig(ctx context.Context, req *UpdateConfigRequest) (*UpdateConfigResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateConfig not implemented") +} +func (*UnimplementedControlServer) ListConfigs(ctx context.Context, req *ListConfigsRequest) (*ListConfigsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListConfigs not implemented") +} +func (*UnimplementedControlServer) CreateConfig(ctx context.Context, req *CreateConfigRequest) (*CreateConfigResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateConfig not implemented") +} +func (*UnimplementedControlServer) RemoveConfig(ctx context.Context, req *RemoveConfigRequest) (*RemoveConfigResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveConfig not implemented") +} +func (*UnimplementedControlServer) GetExtension(ctx context.Context, req *GetExtensionRequest) (*GetExtensionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetExtension not implemented") +} +func (*UnimplementedControlServer) CreateExtension(ctx context.Context, req *CreateExtensionRequest) (*CreateExtensionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateExtension not implemented") +} +func (*UnimplementedControlServer) RemoveExtension(ctx context.Context, req *RemoveExtensionRequest) (*RemoveExtensionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveExtension not implemented") +} +func (*UnimplementedControlServer) GetResource(ctx context.Context, req *GetResourceRequest) (*GetResourceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetResource not implemented") +} +func (*UnimplementedControlServer) UpdateResource(ctx context.Context, req *UpdateResourceRequest) (*UpdateResourceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateResource not implemented") +} +func (*UnimplementedControlServer) ListResources(ctx context.Context, req *ListResourcesRequest) (*ListResourcesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListResources not implemented") +} +func (*UnimplementedControlServer) CreateResource(ctx context.Context, req *CreateResourceRequest) (*CreateResourceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateResource not implemented") +} +func (*UnimplementedControlServer) RemoveResource(ctx context.Context, req *RemoveResourceRequest) (*RemoveResourceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveResource not implemented") } -func (m *UpdateServiceRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil +func RegisterControlServer(s *grpc.Server, srv ControlServer) { + s.RegisterService(&_Control_serviceDesc, srv) } -func (m *UpdateServiceRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ServiceID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.ServiceID))) - i += copy(dAtA[i:], m.ServiceID) +func _Control_GetNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetNodeRequest) + if err := dec(in); err != nil { + return nil, err } - if m.ServiceVersion != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(m.ServiceVersion.Size())) - n11, err := m.ServiceVersion.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n11 + if interceptor == nil { + return srv.(ControlServer).GetNode(ctx, in) } - if m.Spec != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Spec.Size())) - n12, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n12 + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/GetNode", } - if m.Rollback != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Rollback)) + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetNode(ctx, req.(*GetNodeRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *UpdateServiceResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_ListNodes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListNodesRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *UpdateServiceResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Service != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Service.Size())) - n13, err := m.Service.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n13 + if interceptor == nil { + return srv.(ControlServer).ListNodes(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/ListNodes", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListNodes(ctx, req.(*ListNodesRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *RemoveServiceRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_UpdateNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateNodeRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *RemoveServiceRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ServiceID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.ServiceID))) - i += copy(dAtA[i:], m.ServiceID) + if interceptor == nil { + return srv.(ControlServer).UpdateNode(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/UpdateNode", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).UpdateNode(ctx, req.(*UpdateNodeRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *RemoveServiceResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_RemoveNode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveNodeRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *RemoveServiceResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - return i, nil -} - -func (m *ListServicesRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err + if interceptor == nil { + return srv.(ControlServer).RemoveNode(ctx, in) } - return dAtA[:n], nil -} - -func (m *ListServicesRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Filters != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Filters.Size())) - n14, err := m.Filters.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n14 + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/RemoveNode", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RemoveNode(ctx, req.(*RemoveNodeRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *ListServicesRequest_Filters) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_GetTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTaskRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).GetTask(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/GetTask", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetTask(ctx, req.(*GetTaskRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *ListServicesRequest_Filters) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } +func _Control_ListTasks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListTasksRequest) + if err := dec(in); err != nil { + return nil, err } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x1a - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - i = encodeVarintControl(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) - } + if interceptor == nil { + return srv.(ControlServer).ListTasks(ctx, in) } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - dAtA[i] = 0x22 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/ListTasks", } - if len(m.Runtimes) > 0 { - for _, s := range m.Runtimes { - dAtA[i] = 0x2a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListTasks(ctx, req.(*ListTasksRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *ListServicesResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_RemoveTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveTaskRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *ListServicesResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Services) > 0 { - for _, msg := range m.Services { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if interceptor == nil { + return srv.(ControlServer).RemoveTask(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/RemoveTask", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RemoveTask(ctx, req.(*RemoveTaskRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *CreateNetworkRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_GetService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetServiceRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *CreateNetworkRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Spec != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Spec.Size())) - n15, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n15 + if interceptor == nil { + return srv.(ControlServer).GetService(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/GetService", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetService(ctx, req.(*GetServiceRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *CreateNetworkResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListServicesRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).ListServices(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/ListServices", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListServices(ctx, req.(*ListServicesRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *CreateNetworkResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Network != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Network.Size())) - n16, err := m.Network.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n16 +func _Control_CreateService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateServiceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).CreateService(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/CreateService", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).CreateService(ctx, req.(*CreateServiceRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *GetNetworkRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_UpdateService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateServiceRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).UpdateService(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/UpdateService", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).UpdateService(ctx, req.(*UpdateServiceRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *GetNetworkRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) +func _Control_RemoveService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveServiceRequest) + if err := dec(in); err != nil { + return nil, err } - if len(m.NetworkID) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.NetworkID))) - i += copy(dAtA[i:], m.NetworkID) + if interceptor == nil { + return srv.(ControlServer).RemoveService(ctx, in) } - return i, nil + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/RemoveService", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RemoveService(ctx, req.(*RemoveServiceRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *GetNetworkResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_ListServiceStatuses_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListServiceStatusesRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).ListServiceStatuses(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/ListServiceStatuses", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListServiceStatuses(ctx, req.(*ListServiceStatusesRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *GetNetworkResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Network != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Network.Size())) - n17, err := m.Network.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n17 +func _Control_GetNetwork_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetNetworkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetNetwork(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/GetNetwork", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetNetwork(ctx, req.(*GetNetworkRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *RemoveNetworkRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_ListNetworks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListNetworksRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *RemoveNetworkRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if interceptor == nil { + return srv.(ControlServer).ListNetworks(ctx, in) } - if len(m.NetworkID) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.NetworkID))) - i += copy(dAtA[i:], m.NetworkID) + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/ListNetworks", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListNetworks(ctx, req.(*ListNetworksRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *RemoveNetworkResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_CreateNetwork_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateNetworkRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *RemoveNetworkResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - return i, nil + if interceptor == nil { + return srv.(ControlServer).CreateNetwork(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/CreateNetwork", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).CreateNetwork(ctx, req.(*CreateNetworkRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *ListNetworksRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_RemoveNetwork_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveNetworkRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *ListNetworksRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Filters != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Filters.Size())) - n18, err := m.Filters.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n18 + if interceptor == nil { + return srv.(ControlServer).RemoveNetwork(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/RemoveNetwork", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RemoveNetwork(ctx, req.(*RemoveNetworkRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *ListNetworksRequest_Filters) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_GetCluster_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetClusterRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).GetCluster(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/GetCluster", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetCluster(ctx, req.(*GetClusterRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *ListNetworksRequest_Filters) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } +func _Control_ListClusters_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListClustersRequest) + if err := dec(in); err != nil { + return nil, err } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } + if interceptor == nil { + return srv.(ControlServer).ListClusters(ctx, in) } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x1a - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - i = encodeVarintControl(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) - } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/ListClusters", } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - dAtA[i] = 0x22 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListClusters(ctx, req.(*ListClustersRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *ListNetworksResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_UpdateCluster_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateClusterRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *ListNetworksResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Networks) > 0 { - for _, msg := range m.Networks { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if interceptor == nil { + return srv.(ControlServer).UpdateCluster(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/UpdateCluster", } - return i, nil + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).UpdateCluster(ctx, req.(*UpdateClusterRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *GetClusterRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_GetSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSecretRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).GetSecret(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/GetSecret", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetSecret(ctx, req.(*GetSecretRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *GetClusterRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ClusterID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.ClusterID))) - i += copy(dAtA[i:], m.ClusterID) +func _Control_UpdateSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateSecretRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).UpdateSecret(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/UpdateSecret", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).UpdateSecret(ctx, req.(*UpdateSecretRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *GetClusterResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_ListSecrets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListSecretsRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).ListSecrets(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/ListSecrets", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListSecrets(ctx, req.(*ListSecretsRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *GetClusterResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Cluster != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Cluster.Size())) - n19, err := m.Cluster.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n19 +func _Control_CreateSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateSecretRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).CreateSecret(ctx, in) } - return i, nil + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/CreateSecret", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).CreateSecret(ctx, req.(*CreateSecretRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *ListClustersRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_RemoveSecret_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveSecretRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *ListClustersRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Filters != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Filters.Size())) - n20, err := m.Filters.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n20 + if interceptor == nil { + return srv.(ControlServer).RemoveSecret(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/RemoveSecret", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RemoveSecret(ctx, req.(*RemoveSecretRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *ListClustersRequest_Filters) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_GetConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetConfigRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).GetConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/GetConfig", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetConfig(ctx, req.(*GetConfigRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *ListClustersRequest_Filters) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } +func _Control_UpdateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateConfigRequest) + if err := dec(in); err != nil { + return nil, err } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } + if interceptor == nil { + return srv.(ControlServer).UpdateConfig(ctx, in) } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x1a - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - i = encodeVarintControl(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) - } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/UpdateConfig", } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - dAtA[i] = 0x22 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).UpdateConfig(ctx, req.(*UpdateConfigRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *ListClustersResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_ListConfigs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListConfigsRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil -} - -func (m *ListClustersResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.Clusters) > 0 { - for _, msg := range m.Clusters { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if interceptor == nil { + return srv.(ControlServer).ListConfigs(ctx, in) } - return i, nil + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/ListConfigs", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListConfigs(ctx, req.(*ListConfigsRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *KeyRotation) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_CreateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateConfigRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).CreateConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/CreateConfig", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).CreateConfig(ctx, req.(*CreateConfigRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *KeyRotation) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.WorkerJoinToken { - dAtA[i] = 0x8 - i++ - if m.WorkerJoinToken { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ +func _Control_RemoveConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveConfigRequest) + if err := dec(in); err != nil { + return nil, err } - if m.ManagerJoinToken { - dAtA[i] = 0x10 - i++ - if m.ManagerJoinToken { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ + if interceptor == nil { + return srv.(ControlServer).RemoveConfig(ctx, in) } - if m.ManagerUnlockKey { - dAtA[i] = 0x18 - i++ - if m.ManagerUnlockKey { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i++ + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/RemoveConfig", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RemoveConfig(ctx, req.(*RemoveConfigRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *UpdateClusterRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_GetExtension_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetExtensionRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).GetExtension(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/GetExtension", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetExtension(ctx, req.(*GetExtensionRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *UpdateClusterRequest) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if len(m.ClusterID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.ClusterID))) - i += copy(dAtA[i:], m.ClusterID) +func _Control_CreateExtension_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateExtensionRequest) + if err := dec(in); err != nil { + return nil, err } - if m.ClusterVersion != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(m.ClusterVersion.Size())) - n21, err := m.ClusterVersion.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n21 + if interceptor == nil { + return srv.(ControlServer).CreateExtension(ctx, in) } - if m.Spec != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Spec.Size())) - n22, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n22 + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/CreateExtension", } - dAtA[i] = 0x22 - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Rotation.Size())) - n23, err := m.Rotation.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).CreateExtension(ctx, req.(*CreateExtensionRequest)) } - i += n23 - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *UpdateClusterResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { +func _Control_RemoveExtension_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveExtensionRequest) + if err := dec(in); err != nil { return nil, err } - return dAtA[:n], nil + if interceptor == nil { + return srv.(ControlServer).RemoveExtension(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/RemoveExtension", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RemoveExtension(ctx, req.(*RemoveExtensionRequest)) + } + return interceptor(ctx, in, info, handler) } -func (m *UpdateClusterResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - if m.Cluster != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Cluster.Size())) - n24, err := m.Cluster.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n24 +func _Control_GetResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/GetResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetResource(ctx, req.(*GetResourceRequest)) } - return i, nil + return interceptor(ctx, in, info, handler) } -func (m *GetSecretRequest) Marshal() (dAtA []byte, err error) { +func _Control_UpdateResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).UpdateResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/UpdateResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).UpdateResource(ctx, req.(*UpdateResourceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_ListResources_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListResourcesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).ListResources(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/ListResources", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListResources(ctx, req.(*ListResourcesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_CreateResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).CreateResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/CreateResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).CreateResource(ctx, req.(*CreateResourceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_RemoveResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).RemoveResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/docker.swarmkit.v1.Control/RemoveResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).RemoveResource(ctx, req.(*RemoveResourceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Control_serviceDesc = grpc.ServiceDesc{ + ServiceName: "docker.swarmkit.v1.Control", + HandlerType: (*ControlServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetNode", + Handler: _Control_GetNode_Handler, + }, + { + MethodName: "ListNodes", + Handler: _Control_ListNodes_Handler, + }, + { + MethodName: "UpdateNode", + Handler: _Control_UpdateNode_Handler, + }, + { + MethodName: "RemoveNode", + Handler: _Control_RemoveNode_Handler, + }, + { + MethodName: "GetTask", + Handler: _Control_GetTask_Handler, + }, + { + MethodName: "ListTasks", + Handler: _Control_ListTasks_Handler, + }, + { + MethodName: "RemoveTask", + Handler: _Control_RemoveTask_Handler, + }, + { + MethodName: "GetService", + Handler: _Control_GetService_Handler, + }, + { + MethodName: "ListServices", + Handler: _Control_ListServices_Handler, + }, + { + MethodName: "CreateService", + Handler: _Control_CreateService_Handler, + }, + { + MethodName: "UpdateService", + Handler: _Control_UpdateService_Handler, + }, + { + MethodName: "RemoveService", + Handler: _Control_RemoveService_Handler, + }, + { + MethodName: "ListServiceStatuses", + Handler: _Control_ListServiceStatuses_Handler, + }, + { + MethodName: "GetNetwork", + Handler: _Control_GetNetwork_Handler, + }, + { + MethodName: "ListNetworks", + Handler: _Control_ListNetworks_Handler, + }, + { + MethodName: "CreateNetwork", + Handler: _Control_CreateNetwork_Handler, + }, + { + MethodName: "RemoveNetwork", + Handler: _Control_RemoveNetwork_Handler, + }, + { + MethodName: "GetCluster", + Handler: _Control_GetCluster_Handler, + }, + { + MethodName: "ListClusters", + Handler: _Control_ListClusters_Handler, + }, + { + MethodName: "UpdateCluster", + Handler: _Control_UpdateCluster_Handler, + }, + { + MethodName: "GetSecret", + Handler: _Control_GetSecret_Handler, + }, + { + MethodName: "UpdateSecret", + Handler: _Control_UpdateSecret_Handler, + }, + { + MethodName: "ListSecrets", + Handler: _Control_ListSecrets_Handler, + }, + { + MethodName: "CreateSecret", + Handler: _Control_CreateSecret_Handler, + }, + { + MethodName: "RemoveSecret", + Handler: _Control_RemoveSecret_Handler, + }, + { + MethodName: "GetConfig", + Handler: _Control_GetConfig_Handler, + }, + { + MethodName: "UpdateConfig", + Handler: _Control_UpdateConfig_Handler, + }, + { + MethodName: "ListConfigs", + Handler: _Control_ListConfigs_Handler, + }, + { + MethodName: "CreateConfig", + Handler: _Control_CreateConfig_Handler, + }, + { + MethodName: "RemoveConfig", + Handler: _Control_RemoveConfig_Handler, + }, + { + MethodName: "GetExtension", + Handler: _Control_GetExtension_Handler, + }, + { + MethodName: "CreateExtension", + Handler: _Control_CreateExtension_Handler, + }, + { + MethodName: "RemoveExtension", + Handler: _Control_RemoveExtension_Handler, + }, + { + MethodName: "GetResource", + Handler: _Control_GetResource_Handler, + }, + { + MethodName: "UpdateResource", + Handler: _Control_UpdateResource_Handler, + }, + { + MethodName: "ListResources", + Handler: _Control_ListResources_Handler, + }, + { + MethodName: "CreateResource", + Handler: _Control_CreateResource_Handler, + }, + { + MethodName: "RemoveResource", + Handler: _Control_RemoveResource_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "github.com/docker/swarmkit/api/control.proto", +} + +func (m *GetNodeRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *GetSecretRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *GetNodeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetNodeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.SecretID) > 0 { + if len(m.NodeID) > 0 { + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) + i = encodeVarintControl(dAtA, i, uint64(len(m.NodeID))) + i-- dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.SecretID))) - i += copy(dAtA[i:], m.SecretID) } - return i, nil + return len(dAtA) - i, nil } -func (m *GetSecretResponse) Marshal() (dAtA []byte, err error) { +func (m *GetNodeResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *GetSecretResponse) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *GetNodeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetNodeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Secret != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Secret.Size())) - n25, err := m.Secret.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Node != nil { + { + size, err := m.Node.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - i += n25 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } -func (m *UpdateSecretRequest) Marshal() (dAtA []byte, err error) { +func (m *ListNodesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *UpdateSecretRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *ListNodesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListNodesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.SecretID) > 0 { + if m.Filters != nil { + { + size, err := m.Filters.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.SecretID))) - i += copy(dAtA[i:], m.SecretID) } - if m.SecretVersion != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(m.SecretVersion.Size())) - n26, err := m.SecretVersion.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n26 - } - if m.Spec != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Spec.Size())) - n27, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n27 - } - return i, nil + return len(dAtA) - i, nil } -func (m *UpdateSecretResponse) Marshal() (dAtA []byte, err error) { +func (m *ListNodesRequest_Filters) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *UpdateSecretResponse) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *ListNodesRequest_Filters) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListNodesRequest_Filters) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Secret != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Secret.Size())) - n28, err := m.Secret.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.NodeLabels) > 0 { + for k := range m.NodeLabels { + v := m.NodeLabels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintControl(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x3a + } + } + if len(m.NamePrefixes) > 0 { + for iNdEx := len(m.NamePrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NamePrefixes[iNdEx]) + copy(dAtA[i:], m.NamePrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.NamePrefixes[iNdEx]))) + i-- + dAtA[i] = 0x32 + } + } + if len(m.Roles) > 0 { + for iNdEx := len(m.Roles) - 1; iNdEx >= 0; iNdEx-- { + i = encodeVarintControl(dAtA, i, uint64(m.Roles[iNdEx])) + i-- + dAtA[i] = 0x28 + } + } + if len(m.Memberships) > 0 { + for iNdEx := len(m.Memberships) - 1; iNdEx >= 0; iNdEx-- { + i = encodeVarintControl(dAtA, i, uint64(m.Memberships[iNdEx])) + i-- + dAtA[i] = 0x20 + } + } + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintControl(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.IDPrefixes) > 0 { + for iNdEx := len(m.IDPrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IDPrefixes[iNdEx]) + copy(dAtA[i:], m.IDPrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.IDPrefixes[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Names) > 0 { + for iNdEx := len(m.Names) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Names[iNdEx]) + copy(dAtA[i:], m.Names[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Names[iNdEx]))) + i-- + dAtA[i] = 0xa } - i += n28 } - return i, nil + return len(dAtA) - i, nil } -func (m *ListSecretsRequest) Marshal() (dAtA []byte, err error) { +func (m *ListNodesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *ListSecretsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *ListNodesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListNodesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Filters != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Filters.Size())) - n29, err := m.Filters.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Nodes) > 0 { + for iNdEx := len(m.Nodes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Nodes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - i += n29 } - return i, nil + return len(dAtA) - i, nil } -func (m *ListSecretsRequest_Filters) Marshal() (dAtA []byte, err error) { +func (m *UpdateNodeRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *ListSecretsRequest_Filters) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *UpdateNodeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateNodeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x1a } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ + if m.NodeVersion != nil { + { + size, err := m.NodeVersion.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x1a - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - i = encodeVarintControl(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x12 } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - dAtA[i] = 0x22 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } + if len(m.NodeID) > 0 { + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) + i = encodeVarintControl(dAtA, i, uint64(len(m.NodeID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } -func (m *ListSecretsResponse) Marshal() (dAtA []byte, err error) { +func (m *UpdateNodeResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *ListSecretsResponse) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *UpdateNodeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateNodeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Secrets) > 0 { - for _, msg := range m.Secrets { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Node != nil { + { + size, err := m.Node.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } -func (m *CreateSecretRequest) Marshal() (dAtA []byte, err error) { +func (m *RemoveNodeRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *CreateSecretRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *RemoveNodeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveNodeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Spec != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Spec.Size())) - n30, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Force { + i-- + if m.Force { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } - i += n30 + i-- + dAtA[i] = 0x10 + } + if len(m.NodeID) > 0 { + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) + i = encodeVarintControl(dAtA, i, uint64(len(m.NodeID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } -func (m *CreateSecretResponse) Marshal() (dAtA []byte, err error) { +func (m *RemoveNodeResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *CreateSecretResponse) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *RemoveNodeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveNodeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Secret != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Secret.Size())) - n31, err := m.Secret.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n31 - } - return i, nil + return len(dAtA) - i, nil } -func (m *RemoveSecretRequest) Marshal() (dAtA []byte, err error) { +func (m *GetTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *RemoveSecretRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *GetTaskRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.SecretID) > 0 { + if len(m.TaskID) > 0 { + i -= len(m.TaskID) + copy(dAtA[i:], m.TaskID) + i = encodeVarintControl(dAtA, i, uint64(len(m.TaskID))) + i-- dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.SecretID))) - i += copy(dAtA[i:], m.SecretID) } - return i, nil + return len(dAtA) - i, nil } -func (m *RemoveSecretResponse) Marshal() (dAtA []byte, err error) { +func (m *GetTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *RemoveSecretResponse) MarshalTo(dAtA []byte) (int, error) { - var i int - _ = i - var l int - _ = l - return i, nil -} - -func (m *GetConfigRequest) Marshal() (dAtA []byte, err error) { +func (m *GetTaskResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) - if err != nil { - return nil, err - } - return dAtA[:n], nil + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *GetConfigRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *GetTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ConfigID) > 0 { + if m.Task != nil { + { + size, err := m.Task.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.ConfigID))) - i += copy(dAtA[i:], m.ConfigID) } - return i, nil + return len(dAtA) - i, nil } -func (m *GetConfigResponse) Marshal() (dAtA []byte, err error) { +func (m *RemoveTaskRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *GetConfigResponse) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *RemoveTaskRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveTaskRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Config != nil { + if len(m.TaskID) > 0 { + i -= len(m.TaskID) + copy(dAtA[i:], m.TaskID) + i = encodeVarintControl(dAtA, i, uint64(len(m.TaskID))) + i-- dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Config.Size())) - n32, err := m.Config.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n32 } - return i, nil + return len(dAtA) - i, nil } -func (m *UpdateConfigRequest) Marshal() (dAtA []byte, err error) { +func (m *RemoveTaskResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *UpdateConfigRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *RemoveTaskResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveTaskResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ConfigID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.ConfigID))) - i += copy(dAtA[i:], m.ConfigID) - } - if m.ConfigVersion != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(m.ConfigVersion.Size())) - n33, err := m.ConfigVersion.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n33 - } - if m.Spec != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Spec.Size())) - n34, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n34 - } - return i, nil + return len(dAtA) - i, nil } -func (m *UpdateConfigResponse) Marshal() (dAtA []byte, err error) { +func (m *ListTasksRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *UpdateConfigResponse) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *ListTasksRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListTasksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Config != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Config.Size())) - n35, err := m.Config.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Filters != nil { + { + size, err := m.Filters.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - i += n35 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } -func (m *ListConfigsRequest) Marshal() (dAtA []byte, err error) { +func (m *ListTasksRequest_Filters) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *ListConfigsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *ListTasksRequest_Filters) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListTasksRequest_Filters) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Filters != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Filters.Size())) - n36, err := m.Filters.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Runtimes) > 0 { + for iNdEx := len(m.Runtimes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Runtimes[iNdEx]) + copy(dAtA[i:], m.Runtimes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Runtimes[iNdEx]))) + i-- + dAtA[i] = 0x4a + } + } + if m.UpToDate { + i-- + if m.UpToDate { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if len(m.NamePrefixes) > 0 { + for iNdEx := len(m.NamePrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NamePrefixes[iNdEx]) + copy(dAtA[i:], m.NamePrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.NamePrefixes[iNdEx]))) + i-- + dAtA[i] = 0x3a + } + } + if len(m.DesiredStates) > 0 { + for iNdEx := len(m.DesiredStates) - 1; iNdEx >= 0; iNdEx-- { + i = encodeVarintControl(dAtA, i, uint64(m.DesiredStates[iNdEx])) + i-- + dAtA[i] = 0x30 + } + } + if len(m.NodeIDs) > 0 { + for iNdEx := len(m.NodeIDs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NodeIDs[iNdEx]) + copy(dAtA[i:], m.NodeIDs[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.NodeIDs[iNdEx]))) + i-- + dAtA[i] = 0x2a + } + } + if len(m.ServiceIDs) > 0 { + for iNdEx := len(m.ServiceIDs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ServiceIDs[iNdEx]) + copy(dAtA[i:], m.ServiceIDs[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.ServiceIDs[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintControl(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } + if len(m.IDPrefixes) > 0 { + for iNdEx := len(m.IDPrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IDPrefixes[iNdEx]) + copy(dAtA[i:], m.IDPrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.IDPrefixes[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Names) > 0 { + for iNdEx := len(m.Names) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Names[iNdEx]) + copy(dAtA[i:], m.Names[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Names[iNdEx]))) + i-- + dAtA[i] = 0xa } - i += n36 } - return i, nil + return len(dAtA) - i, nil } -func (m *ListConfigsRequest_Filters) Marshal() (dAtA []byte, err error) { +func (m *ListTasksResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *ListConfigsRequest_Filters) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *ListTasksResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListTasksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ + if len(m.Tasks) > 0 { + for iNdEx := len(m.Tasks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Tasks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x1a - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - i = encodeVarintControl(dAtA, i, uint64(mapSize)) + i-- dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintControl(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) - } - } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - dAtA[i] = 0x22 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - return i, nil + return len(dAtA) - i, nil } -func (m *ListConfigsResponse) Marshal() (dAtA []byte, err error) { +func (m *CreateServiceRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *ListConfigsResponse) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *CreateServiceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateServiceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Configs) > 0 { - for _, msg := range m.Configs { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } -func (m *CreateConfigRequest) Marshal() (dAtA []byte, err error) { +func (m *CreateServiceResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *CreateConfigRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *CreateServiceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateServiceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Spec != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Spec.Size())) - n37, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Service != nil { + { + size, err := m.Service.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - i += n37 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } -func (m *CreateConfigResponse) Marshal() (dAtA []byte, err error) { +func (m *GetServiceRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *CreateConfigResponse) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *GetServiceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetServiceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Config != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(m.Config.Size())) - n38, err := m.Config.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.InsertDefaults { + i-- + if m.InsertDefaults { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } - i += n38 + i-- + dAtA[i] = 0x10 + } + if len(m.ServiceID) > 0 { + i -= len(m.ServiceID) + copy(dAtA[i:], m.ServiceID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ServiceID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } -func (m *RemoveConfigRequest) Marshal() (dAtA []byte, err error) { +func (m *GetServiceResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *RemoveConfigRequest) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *GetServiceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetServiceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ConfigID) > 0 { + if m.Service != nil { + { + size, err := m.Service.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0xa - i++ - i = encodeVarintControl(dAtA, i, uint64(len(m.ConfigID))) - i += copy(dAtA[i:], m.ConfigID) } - return i, nil + return len(dAtA) - i, nil } -func (m *RemoveConfigResponse) Marshal() (dAtA []byte, err error) { +func (m *UpdateServiceRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } -func (m *RemoveConfigResponse) MarshalTo(dAtA []byte) (int, error) { - var i int +func (m *UpdateServiceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateServiceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + if m.Rollback != 0 { + i = encodeVarintControl(dAtA, i, uint64(m.Rollback)) + i-- + dAtA[i] = 0x20 + } + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.ServiceVersion != nil { + { + size, err := m.ServiceVersion.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ServiceID) > 0 { + i -= len(m.ServiceID) + copy(dAtA[i:], m.ServiceID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ServiceID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } -func encodeVarintControl(dAtA []byte, offset int, v uint64) int { - for v >= 1<<7 { - dAtA[offset] = uint8(v&0x7f | 0x80) - v >>= 7 - offset++ +func (m *UpdateServiceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - dAtA[offset] = uint8(v) - return offset + 1 + return dAtA[:n], nil } -type raftProxyControlServer struct { - local ControlServer - connSelector raftselector.ConnProvider - localCtxMods, remoteCtxMods []func(context.Context) (context.Context, error) +func (m *UpdateServiceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func NewRaftProxyControlServer(local ControlServer, connSelector raftselector.ConnProvider, localCtxMod, remoteCtxMod func(context.Context) (context.Context, error)) ControlServer { - redirectChecker := func(ctx context.Context) (context.Context, error) { - p, ok := peer.FromContext(ctx) - if !ok { - return ctx, status.Errorf(codes.InvalidArgument, "remote addr is not found in context") - } - addr := p.Addr.String() - md, ok := metadata.FromIncomingContext(ctx) - if ok && len(md["redirect"]) != 0 { - return ctx, status.Errorf(codes.ResourceExhausted, "more than one redirect to leader from: %s", md["redirect"]) - } - if !ok { - md = metadata.New(map[string]string{}) +func (m *UpdateServiceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Service != nil { + { + size, err := m.Service.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - md["redirect"] = append(md["redirect"], addr) - return metadata.NewOutgoingContext(ctx, md), nil + i-- + dAtA[i] = 0xa } - remoteMods := []func(context.Context) (context.Context, error){redirectChecker} - remoteMods = append(remoteMods, remoteCtxMod) + return len(dAtA) - i, nil +} - var localMods []func(context.Context) (context.Context, error) - if localCtxMod != nil { - localMods = []func(context.Context) (context.Context, error){localCtxMod} +func (m *RemoveServiceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } + return dAtA[:n], nil +} - return &raftProxyControlServer{ - local: local, - connSelector: connSelector, - localCtxMods: localMods, - remoteCtxMods: remoteMods, +func (m *RemoveServiceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveServiceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ServiceID) > 0 { + i -= len(m.ServiceID) + copy(dAtA[i:], m.ServiceID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ServiceID))) + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil } -func (p *raftProxyControlServer) runCtxMods(ctx context.Context, ctxMods []func(context.Context) (context.Context, error)) (context.Context, error) { - var err error - for _, mod := range ctxMods { - ctx, err = mod(ctx) - if err != nil { - return ctx, err - } + +func (m *RemoveServiceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return ctx, nil + return dAtA[:n], nil } -func (p *raftProxyControlServer) pollNewLeaderConn(ctx context.Context) (*grpc.ClientConn, error) { - ticker := rafttime.NewTicker(500 * rafttime.Millisecond) - defer ticker.Stop() - for { - select { - case <-ticker.C: - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - return nil, err - } - client := NewHealthClient(conn) +func (m *RemoveServiceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - resp, err := client.Check(ctx, &HealthCheckRequest{Service: "Raft"}) - if err != nil || resp.Status != HealthCheckResponse_SERVING { - continue - } - return conn, nil - case <-ctx.Done(): - return nil, ctx.Err() - } +func (m *RemoveServiceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *ListServicesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } + return dAtA[:n], nil } -func (p *raftProxyControlServer) GetNode(ctx context.Context, r *GetNodeRequest) (*GetNodeResponse, error) { +func (m *ListServicesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) +func (m *ListServicesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Filters != nil { + { + size, err := m.Filters.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.GetNode(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err - } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).GetNode(modCtx, r) +func (m *ListServicesRequest_Filters) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.GetNode(ctx, r) - } - return nil, err - } - return NewControlClient(conn).GetNode(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) ListNodes(ctx context.Context, r *ListNodesRequest) (*ListNodesResponse, error) { +func (m *ListServicesRequest_Filters) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.ListNodes(ctx, r) +func (m *ListServicesRequest_Filters) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Runtimes) > 0 { + for iNdEx := len(m.Runtimes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Runtimes[iNdEx]) + copy(dAtA[i:], m.Runtimes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Runtimes[iNdEx]))) + i-- + dAtA[i] = 0x2a } - return nil, err } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + if len(m.NamePrefixes) > 0 { + for iNdEx := len(m.NamePrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NamePrefixes[iNdEx]) + copy(dAtA[i:], m.NamePrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.NamePrefixes[iNdEx]))) + i-- + dAtA[i] = 0x22 + } } - - resp, err := NewControlClient(conn).ListNodes(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintControl(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.ListNodes(ctx, r) - } - return nil, err + } + if len(m.IDPrefixes) > 0 { + for iNdEx := len(m.IDPrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IDPrefixes[iNdEx]) + copy(dAtA[i:], m.IDPrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.IDPrefixes[iNdEx]))) + i-- + dAtA[i] = 0x12 } - return NewControlClient(conn).ListNodes(modCtx, r) } - return resp, err -} - -func (p *raftProxyControlServer) UpdateNode(ctx context.Context, r *UpdateNodeRequest) (*UpdateNodeResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.UpdateNode(ctx, r) + if len(m.Names) > 0 { + for iNdEx := len(m.Names) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Names[iNdEx]) + copy(dAtA[i:], m.Names[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Names[iNdEx]))) + i-- + dAtA[i] = 0xa } - return nil, err } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + return len(dAtA) - i, nil +} + +func (m *ListServicesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).UpdateNode(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.UpdateNode(ctx, r) +func (m *ListServicesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListServicesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Services) > 0 { + for iNdEx := len(m.Services) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Services[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err + i-- + dAtA[i] = 0xa } - return NewControlClient(conn).UpdateNode(modCtx, r) } - return resp, err + return len(dAtA) - i, nil } -func (p *raftProxyControlServer) RemoveNode(ctx context.Context, r *RemoveNodeRequest) (*RemoveNodeResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) +func (m *ListServiceStatusesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.RemoveNode(ctx, r) - } return nil, err } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + return dAtA[:n], nil +} + +func (m *ListServiceStatusesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListServiceStatusesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Services) > 0 { + for iNdEx := len(m.Services) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Services[iNdEx]) + copy(dAtA[i:], m.Services[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Services[iNdEx]))) + i-- + dAtA[i] = 0xa + } } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).RemoveNode(modCtx, r) +func (m *ListServiceStatusesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.RemoveNode(ctx, r) - } - return nil, err - } - return NewControlClient(conn).RemoveNode(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) GetTask(ctx context.Context, r *GetTaskRequest) (*GetTaskResponse, error) { +func (m *ListServiceStatusesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err +func (m *ListServiceStatusesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Statuses) > 0 { + for iNdEx := len(m.Statuses) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Statuses[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return p.local.GetTask(ctx, r) + i-- + dAtA[i] = 0xa } - return nil, err } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + return len(dAtA) - i, nil +} + +func (m *ListServiceStatusesResponse_ServiceStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).GetTask(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.GetTask(ctx, r) - } - return nil, err - } - return NewControlClient(conn).GetTask(modCtx, r) - } - return resp, err +func (m *ListServiceStatusesResponse_ServiceStatus) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (p *raftProxyControlServer) ListTasks(ctx context.Context, r *ListTasksRequest) (*ListTasksResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.ListTasks(ctx, r) - } - return nil, err +func (m *ListServiceStatusesResponse_ServiceStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CompletedTasks != 0 { + i = encodeVarintControl(dAtA, i, uint64(m.CompletedTasks)) + i-- + dAtA[i] = 0x20 } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + if m.RunningTasks != 0 { + i = encodeVarintControl(dAtA, i, uint64(m.RunningTasks)) + i-- + dAtA[i] = 0x18 + } + if m.DesiredTasks != 0 { + i = encodeVarintControl(dAtA, i, uint64(m.DesiredTasks)) + i-- + dAtA[i] = 0x10 + } + if len(m.ServiceID) > 0 { + i -= len(m.ServiceID) + copy(dAtA[i:], m.ServiceID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ServiceID))) + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).ListTasks(modCtx, r) +func (m *CreateNetworkRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.ListTasks(ctx, r) - } - return nil, err - } - return NewControlClient(conn).ListTasks(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) RemoveTask(ctx context.Context, r *RemoveTaskRequest) (*RemoveTaskResponse, error) { +func (m *CreateNetworkRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) +func (m *CreateNetworkRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.RemoveTask(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err - } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).RemoveTask(modCtx, r) +func (m *CreateNetworkResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.RemoveTask(ctx, r) - } - return nil, err - } - return NewControlClient(conn).RemoveTask(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) GetService(ctx context.Context, r *GetServiceRequest) (*GetServiceResponse, error) { +func (m *CreateNetworkResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) +func (m *CreateNetworkResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Network != nil { + { + size, err := m.Network.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.GetService(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err - } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).GetService(modCtx, r) +func (m *GetNetworkRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.GetService(ctx, r) - } - return nil, err - } - return NewControlClient(conn).GetService(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) ListServices(ctx context.Context, r *ListServicesRequest) (*ListServicesResponse, error) { +func (m *GetNetworkRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.ListServices(ctx, r) - } - return nil, err +func (m *GetNetworkRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NetworkID) > 0 { + i -= len(m.NetworkID) + copy(dAtA[i:], m.NetworkID) + i = encodeVarintControl(dAtA, i, uint64(len(m.NetworkID))) + i-- + dAtA[i] = 0x12 } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintControl(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).ListServices(modCtx, r) +func (m *GetNetworkResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.ListServices(ctx, r) - } - return nil, err - } - return NewControlClient(conn).ListServices(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) CreateService(ctx context.Context, r *CreateServiceRequest) (*CreateServiceResponse, error) { +func (m *GetNetworkResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) +func (m *GetNetworkResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Network != nil { + { + size, err := m.Network.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.CreateService(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err - } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).CreateService(modCtx, r) +func (m *RemoveNetworkRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.CreateService(ctx, r) - } - return nil, err - } - return NewControlClient(conn).CreateService(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) UpdateService(ctx context.Context, r *UpdateServiceRequest) (*UpdateServiceResponse, error) { +func (m *RemoveNetworkRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.UpdateService(ctx, r) - } - return nil, err +func (m *RemoveNetworkRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NetworkID) > 0 { + i -= len(m.NetworkID) + copy(dAtA[i:], m.NetworkID) + i = encodeVarintControl(dAtA, i, uint64(len(m.NetworkID))) + i-- + dAtA[i] = 0x12 } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintControl(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RemoveNetworkResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).UpdateService(modCtx, r) +func (m *RemoveNetworkResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveNetworkResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *ListNetworksRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.UpdateService(ctx, r) - } - return nil, err - } - return NewControlClient(conn).UpdateService(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) RemoveService(ctx context.Context, r *RemoveServiceRequest) (*RemoveServiceResponse, error) { +func (m *ListNetworksRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) +func (m *ListNetworksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Filters != nil { + { + size, err := m.Filters.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.RemoveService(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err - } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).RemoveService(modCtx, r) +func (m *ListNetworksRequest_Filters) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.RemoveService(ctx, r) - } - return nil, err - } - return NewControlClient(conn).RemoveService(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) GetNetwork(ctx context.Context, r *GetNetworkRequest) (*GetNetworkResponse, error) { +func (m *ListNetworksRequest_Filters) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.GetNetwork(ctx, r) +func (m *ListNetworksRequest_Filters) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NamePrefixes) > 0 { + for iNdEx := len(m.NamePrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NamePrefixes[iNdEx]) + copy(dAtA[i:], m.NamePrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.NamePrefixes[iNdEx]))) + i-- + dAtA[i] = 0x22 } - return nil, err } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintControl(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } } - - resp, err := NewControlClient(conn).GetNetwork(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err + if len(m.IDPrefixes) > 0 { + for iNdEx := len(m.IDPrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IDPrefixes[iNdEx]) + copy(dAtA[i:], m.IDPrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.IDPrefixes[iNdEx]))) + i-- + dAtA[i] = 0x12 } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.GetNetwork(ctx, r) - } - return nil, err + } + if len(m.Names) > 0 { + for iNdEx := len(m.Names) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Names[iNdEx]) + copy(dAtA[i:], m.Names[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Names[iNdEx]))) + i-- + dAtA[i] = 0xa } - return NewControlClient(conn).GetNetwork(modCtx, r) } - return resp, err + return len(dAtA) - i, nil } -func (p *raftProxyControlServer) ListNetworks(ctx context.Context, r *ListNetworksRequest) (*ListNetworksResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.ListNetworks(ctx, r) - } - return nil, err - } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) +func (m *ListNetworksResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).ListNetworks(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.ListNetworks(ctx, r) +func (m *ListNetworksResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListNetworksResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Networks) > 0 { + for iNdEx := len(m.Networks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Networks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err + i-- + dAtA[i] = 0xa } - return NewControlClient(conn).ListNetworks(modCtx, r) } - return resp, err + return len(dAtA) - i, nil } -func (p *raftProxyControlServer) CreateNetwork(ctx context.Context, r *CreateNetworkRequest) (*CreateNetworkResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) +func (m *GetClusterRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.CreateNetwork(ctx, r) - } return nil, err } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + return dAtA[:n], nil +} + +func (m *GetClusterRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetClusterRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ClusterID) > 0 { + i -= len(m.ClusterID) + copy(dAtA[i:], m.ClusterID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ClusterID))) + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).CreateNetwork(modCtx, r) +func (m *GetClusterResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.CreateNetwork(ctx, r) - } - return nil, err - } - return NewControlClient(conn).CreateNetwork(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) RemoveNetwork(ctx context.Context, r *RemoveNetworkRequest) (*RemoveNetworkResponse, error) { +func (m *GetClusterResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) +func (m *GetClusterResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Cluster != nil { + { + size, err := m.Cluster.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.RemoveNetwork(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err + i-- + dAtA[i] = 0xa } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + return len(dAtA) - i, nil +} + +func (m *ListClustersRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).RemoveNetwork(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.RemoveNetwork(ctx, r) +func (m *ListClustersRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListClustersRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Filters != nil { + { + size, err := m.Filters.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - return nil, err + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return NewControlClient(conn).RemoveNetwork(modCtx, r) + i-- + dAtA[i] = 0xa } - return resp, err + return len(dAtA) - i, nil } -func (p *raftProxyControlServer) GetCluster(ctx context.Context, r *GetClusterRequest) (*GetClusterResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.GetCluster(ctx, r) - } - return nil, err - } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) +func (m *ListClustersRequest_Filters) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).GetCluster(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err +func (m *ListClustersRequest_Filters) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListClustersRequest_Filters) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NamePrefixes) > 0 { + for iNdEx := len(m.NamePrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NamePrefixes[iNdEx]) + copy(dAtA[i:], m.NamePrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.NamePrefixes[iNdEx]))) + i-- + dAtA[i] = 0x22 } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.GetCluster(ctx, r) - } - return nil, err + } + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintControl(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a } - return NewControlClient(conn).GetCluster(modCtx, r) } - return resp, err -} - -func (p *raftProxyControlServer) ListClusters(ctx context.Context, r *ListClustersRequest) (*ListClustersResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.ListClusters(ctx, r) + if len(m.IDPrefixes) > 0 { + for iNdEx := len(m.IDPrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IDPrefixes[iNdEx]) + copy(dAtA[i:], m.IDPrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.IDPrefixes[iNdEx]))) + i-- + dAtA[i] = 0x12 } - return nil, err } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + if len(m.Names) > 0 { + for iNdEx := len(m.Names) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Names[iNdEx]) + copy(dAtA[i:], m.Names[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Names[iNdEx]))) + i-- + dAtA[i] = 0xa + } } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).ListClusters(modCtx, r) +func (m *ListClustersResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.ListClusters(ctx, r) - } - return nil, err - } - return NewControlClient(conn).ListClusters(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) UpdateCluster(ctx context.Context, r *UpdateClusterRequest) (*UpdateClusterResponse, error) { +func (m *ListClustersResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err +func (m *ListClustersResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Clusters) > 0 { + for iNdEx := len(m.Clusters) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Clusters[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return p.local.UpdateCluster(ctx, r) + i-- + dAtA[i] = 0xa } - return nil, err } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + return len(dAtA) - i, nil +} + +func (m *KeyRotation) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).UpdateCluster(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err +func (m *KeyRotation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KeyRotation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ManagerUnlockKey { + i-- + if m.ManagerUnlockKey { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.UpdateCluster(ctx, r) - } - return nil, err + i-- + dAtA[i] = 0x18 + } + if m.ManagerJoinToken { + i-- + if m.ManagerJoinToken { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } - return NewControlClient(conn).UpdateCluster(modCtx, r) + i-- + dAtA[i] = 0x10 } - return resp, err -} - -func (p *raftProxyControlServer) GetSecret(ctx context.Context, r *GetSecretRequest) (*GetSecretResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.GetSecret(ctx, r) + if m.WorkerJoinToken { + i-- + if m.WorkerJoinToken { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } - return nil, err + i-- + dAtA[i] = 0x8 } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + return len(dAtA) - i, nil +} + +func (m *UpdateClusterRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).GetSecret(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) +func (m *UpdateClusterRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateClusterRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Rotation.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.GetSecret(ctx, r) + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - return nil, err + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return NewControlClient(conn).GetSecret(modCtx, r) + i-- + dAtA[i] = 0x1a } - return resp, err -} - -func (p *raftProxyControlServer) UpdateSecret(ctx context.Context, r *UpdateSecretRequest) (*UpdateSecretResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if m.ClusterVersion != nil { + { + size, err := m.ClusterVersion.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.UpdateSecret(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err + i-- + dAtA[i] = 0x12 } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if len(m.ClusterID) > 0 { + i -= len(m.ClusterID) + copy(dAtA[i:], m.ClusterID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ClusterID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UpdateClusterResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).UpdateSecret(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.UpdateSecret(ctx, r) +func (m *UpdateClusterResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateClusterResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Cluster != nil { + { + size, err := m.Cluster.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - return nil, err + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return NewControlClient(conn).UpdateSecret(modCtx, r) + i-- + dAtA[i] = 0xa } - return resp, err + return len(dAtA) - i, nil } -func (p *raftProxyControlServer) ListSecrets(ctx context.Context, r *ListSecretsRequest) (*ListSecretsResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) +func (m *GetSecretRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.ListSecrets(ctx, r) - } return nil, err } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + return dAtA[:n], nil +} + +func (m *GetSecretRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetSecretRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.SecretID) > 0 { + i -= len(m.SecretID) + copy(dAtA[i:], m.SecretID) + i = encodeVarintControl(dAtA, i, uint64(len(m.SecretID))) + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).ListSecrets(modCtx, r) +func (m *GetSecretResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.ListSecrets(ctx, r) - } - return nil, err - } - return NewControlClient(conn).ListSecrets(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) CreateSecret(ctx context.Context, r *CreateSecretRequest) (*CreateSecretResponse, error) { +func (m *GetSecretResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) +func (m *GetSecretResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Secret != nil { + { + size, err := m.Secret.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.CreateSecret(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err + i-- + dAtA[i] = 0xa } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + return len(dAtA) - i, nil +} + +func (m *UpdateSecretRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).CreateSecret(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.CreateSecret(ctx, r) +func (m *UpdateSecretRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateSecretRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - return nil, err + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return NewControlClient(conn).CreateSecret(modCtx, r) + i-- + dAtA[i] = 0x1a } - return resp, err -} - -func (p *raftProxyControlServer) RemoveSecret(ctx context.Context, r *RemoveSecretRequest) (*RemoveSecretResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if m.SecretVersion != nil { + { + size, err := m.SecretVersion.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.RemoveSecret(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err + i-- + dAtA[i] = 0x12 } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + if len(m.SecretID) > 0 { + i -= len(m.SecretID) + copy(dAtA[i:], m.SecretID) + i = encodeVarintControl(dAtA, i, uint64(len(m.SecretID))) + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).RemoveSecret(modCtx, r) +func (m *UpdateSecretResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.RemoveSecret(ctx, r) - } - return nil, err - } - return NewControlClient(conn).RemoveSecret(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) GetConfig(ctx context.Context, r *GetConfigRequest) (*GetConfigResponse, error) { +func (m *UpdateSecretResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) +func (m *UpdateSecretResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Secret != nil { + { + size, err := m.Secret.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.GetConfig(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err - } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).GetConfig(modCtx, r) +func (m *ListSecretsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.GetConfig(ctx, r) - } - return nil, err - } - return NewControlClient(conn).GetConfig(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) UpdateConfig(ctx context.Context, r *UpdateConfigRequest) (*UpdateConfigResponse, error) { +func (m *ListSecretsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) +func (m *ListSecretsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Filters != nil { + { + size, err := m.Filters.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.UpdateConfig(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err + i-- + dAtA[i] = 0xa } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + return len(dAtA) - i, nil +} + +func (m *ListSecretsRequest_Filters) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).UpdateConfig(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err +func (m *ListSecretsRequest_Filters) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListSecretsRequest_Filters) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.NamePrefixes) > 0 { + for iNdEx := len(m.NamePrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NamePrefixes[iNdEx]) + copy(dAtA[i:], m.NamePrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.NamePrefixes[iNdEx]))) + i-- + dAtA[i] = 0x22 } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.UpdateConfig(ctx, r) - } - return nil, err + } + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintControl(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a } - return NewControlClient(conn).UpdateConfig(modCtx, r) } - return resp, err -} - -func (p *raftProxyControlServer) ListConfigs(ctx context.Context, r *ListConfigsRequest) (*ListConfigsResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) - if err != nil { - return nil, err - } - return p.local.ListConfigs(ctx, r) + if len(m.IDPrefixes) > 0 { + for iNdEx := len(m.IDPrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IDPrefixes[iNdEx]) + copy(dAtA[i:], m.IDPrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.IDPrefixes[iNdEx]))) + i-- + dAtA[i] = 0x12 } - return nil, err } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if len(m.Names) > 0 { + for iNdEx := len(m.Names) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Names[iNdEx]) + copy(dAtA[i:], m.Names[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Names[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ListSecretsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).ListConfigs(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.ListConfigs(ctx, r) +func (m *ListSecretsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListSecretsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Secrets) > 0 { + for iNdEx := len(m.Secrets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Secrets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err + i-- + dAtA[i] = 0xa } - return NewControlClient(conn).ListConfigs(modCtx, r) } - return resp, err + return len(dAtA) - i, nil } -func (p *raftProxyControlServer) CreateConfig(ctx context.Context, r *CreateConfigRequest) (*CreateConfigResponse, error) { - - conn, err := p.connSelector.LeaderConn(ctx) +func (m *CreateSecretRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) + return nil, err + } + return dAtA[:n], nil +} + +func (m *CreateSecretRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateSecretRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.CreateConfig(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err - } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) - if err != nil { - return nil, err + i-- + dAtA[i] = 0xa } + return len(dAtA) - i, nil +} - resp, err := NewControlClient(conn).CreateConfig(modCtx, r) +func (m *CreateSecretResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.CreateConfig(ctx, r) - } - return nil, err - } - return NewControlClient(conn).CreateConfig(modCtx, r) + return nil, err } - return resp, err + return dAtA[:n], nil } -func (p *raftProxyControlServer) RemoveConfig(ctx context.Context, r *RemoveConfigRequest) (*RemoveConfigResponse, error) { +func (m *CreateSecretResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} - conn, err := p.connSelector.LeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - ctx, err = p.runCtxMods(ctx, p.localCtxMods) +func (m *CreateSecretResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Secret != nil { + { + size, err := m.Secret.MarshalToSizedBuffer(dAtA[:i]) if err != nil { - return nil, err + return 0, err } - return p.local.RemoveConfig(ctx, r) + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } - return nil, err + i-- + dAtA[i] = 0xa } - modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + return len(dAtA) - i, nil +} + +func (m *RemoveSecretRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } + return dAtA[:n], nil +} - resp, err := NewControlClient(conn).RemoveConfig(modCtx, r) - if err != nil { - if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { - return resp, err - } - conn, err := p.pollNewLeaderConn(ctx) - if err != nil { - if err == raftselector.ErrIsLeader { - return p.local.RemoveConfig(ctx, r) - } - return nil, err - } - return NewControlClient(conn).RemoveConfig(modCtx, r) - } - return resp, err +func (m *RemoveSecretRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *GetNodeRequest) Size() (n int) { +func (m *RemoveSecretRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.NodeID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) + if len(m.SecretID) > 0 { + i -= len(m.SecretID) + copy(dAtA[i:], m.SecretID) + i = encodeVarintControl(dAtA, i, uint64(len(m.SecretID))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *GetNodeResponse) Size() (n int) { - var l int - _ = l - if m.Node != nil { - l = m.Node.Size() - n += 1 + l + sovControl(uint64(l)) +func (m *RemoveSecretResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *ListNodesRequest) Size() (n int) { - var l int - _ = l - if m.Filters != nil { - l = m.Filters.Size() - n += 1 + l + sovControl(uint64(l)) - } - return n +func (m *RemoveSecretResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ListNodesRequest_Filters) Size() (n int) { +func (m *RemoveSecretResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } - } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } - } - if len(m.Labels) > 0 { - for k, v := range m.Labels { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) - } - } - if len(m.Memberships) > 0 { - for _, e := range m.Memberships { - n += 1 + sovControl(uint64(e)) - } - } - if len(m.Roles) > 0 { - for _, e := range m.Roles { - n += 1 + sovControl(uint64(e)) - } - } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } - } - if len(m.NodeLabels) > 0 { - for k, v := range m.NodeLabels { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) - } - } - return n + return len(dAtA) - i, nil } -func (m *ListNodesResponse) Size() (n int) { - var l int - _ = l - if len(m.Nodes) > 0 { - for _, e := range m.Nodes { - l = e.Size() - n += 1 + l + sovControl(uint64(l)) - } +func (m *GetConfigRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *UpdateNodeRequest) Size() (n int) { - var l int - _ = l - l = len(m.NodeID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) - } - if m.NodeVersion != nil { - l = m.NodeVersion.Size() - n += 1 + l + sovControl(uint64(l)) - } - if m.Spec != nil { - l = m.Spec.Size() - n += 1 + l + sovControl(uint64(l)) - } - return n +func (m *GetConfigRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *UpdateNodeResponse) Size() (n int) { +func (m *GetConfigRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Node != nil { - l = m.Node.Size() - n += 1 + l + sovControl(uint64(l)) + if len(m.ConfigID) > 0 { + i -= len(m.ConfigID) + copy(dAtA[i:], m.ConfigID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ConfigID))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *RemoveNodeRequest) Size() (n int) { - var l int - _ = l - l = len(m.NodeID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) - } - if m.Force { - n += 2 +func (m *GetConfigResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *RemoveNodeResponse) Size() (n int) { - var l int - _ = l - return n +func (m *GetConfigResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *GetTaskRequest) Size() (n int) { +func (m *GetConfigResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.TaskID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) + if m.Config != nil { + { + size, err := m.Config.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *GetTaskResponse) Size() (n int) { - var l int - _ = l - if m.Task != nil { - l = m.Task.Size() - n += 1 + l + sovControl(uint64(l)) +func (m *UpdateConfigRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *RemoveTaskRequest) Size() (n int) { +func (m *UpdateConfigRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateConfigRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.TaskID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } - return n + if m.ConfigVersion != nil { + { + size, err := m.ConfigVersion.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ConfigID) > 0 { + i -= len(m.ConfigID) + copy(dAtA[i:], m.ConfigID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ConfigID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } -func (m *RemoveTaskResponse) Size() (n int) { +func (m *UpdateConfigResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UpdateConfigResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateConfigResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - return n + if m.Config != nil { + { + size, err := m.Config.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } -func (m *ListTasksRequest) Size() (n int) { +func (m *ListConfigsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListConfigsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListConfigsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l if m.Filters != nil { - l = m.Filters.Size() - n += 1 + l + sovControl(uint64(l)) + { + size, err := m.Filters.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *ListTasksRequest_Filters) Size() (n int) { +func (m *ListConfigsRequest_Filters) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListConfigsRequest_Filters) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListConfigsRequest_Filters) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } - } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) + if len(m.NamePrefixes) > 0 { + for iNdEx := len(m.NamePrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NamePrefixes[iNdEx]) + copy(dAtA[i:], m.NamePrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.NamePrefixes[iNdEx]))) + i-- + dAtA[i] = 0x22 } } if len(m.Labels) > 0 { - for k, v := range m.Labels { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) - } - } - if len(m.ServiceIDs) > 0 { - for _, s := range m.ServiceIDs { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } - } - if len(m.NodeIDs) > 0 { - for _, s := range m.NodeIDs { - l = len(s) - n += 1 + l + sovControl(uint64(l)) + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintControl(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a } } - if len(m.DesiredStates) > 0 { - for _, e := range m.DesiredStates { - n += 1 + sovControl(uint64(e)) + if len(m.IDPrefixes) > 0 { + for iNdEx := len(m.IDPrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IDPrefixes[iNdEx]) + copy(dAtA[i:], m.IDPrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.IDPrefixes[iNdEx]))) + i-- + dAtA[i] = 0x12 } } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) + if len(m.Names) > 0 { + for iNdEx := len(m.Names) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Names[iNdEx]) + copy(dAtA[i:], m.Names[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Names[iNdEx]))) + i-- + dAtA[i] = 0xa } } - if m.UpToDate { - n += 2 - } - if len(m.Runtimes) > 0 { - for _, s := range m.Runtimes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } + return len(dAtA) - i, nil +} + +func (m *ListConfigsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *ListTasksResponse) Size() (n int) { +func (m *ListConfigsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListConfigsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.Tasks) > 0 { - for _, e := range m.Tasks { - l = e.Size() - n += 1 + l + sovControl(uint64(l)) + if len(m.Configs) > 0 { + for iNdEx := len(m.Configs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Configs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } } - return n + return len(dAtA) - i, nil } -func (m *CreateServiceRequest) Size() (n int) { - var l int - _ = l - if m.Spec != nil { - l = m.Spec.Size() - n += 1 + l + sovControl(uint64(l)) +func (m *CreateConfigRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *CreateServiceResponse) Size() (n int) { - var l int - _ = l - if m.Service != nil { - l = m.Service.Size() - n += 1 + l + sovControl(uint64(l)) - } - return n +func (m *CreateConfigRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *GetServiceRequest) Size() (n int) { +func (m *CreateConfigRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.ServiceID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) - } - if m.InsertDefaults { - n += 2 + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *GetServiceResponse) Size() (n int) { - var l int - _ = l - if m.Service != nil { - l = m.Service.Size() - n += 1 + l + sovControl(uint64(l)) +func (m *CreateConfigResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *UpdateServiceRequest) Size() (n int) { +func (m *CreateConfigResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateConfigResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.ServiceID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) - } - if m.ServiceVersion != nil { - l = m.ServiceVersion.Size() - n += 1 + l + sovControl(uint64(l)) - } - if m.Spec != nil { - l = m.Spec.Size() - n += 1 + l + sovControl(uint64(l)) + if m.Config != nil { + { + size, err := m.Config.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - if m.Rollback != 0 { - n += 1 + sovControl(uint64(m.Rollback)) + return len(dAtA) - i, nil +} + +func (m *RemoveConfigRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *UpdateServiceResponse) Size() (n int) { +func (m *RemoveConfigRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveConfigRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Service != nil { - l = m.Service.Size() - n += 1 + l + sovControl(uint64(l)) + if len(m.ConfigID) > 0 { + i -= len(m.ConfigID) + copy(dAtA[i:], m.ConfigID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ConfigID))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *RemoveServiceRequest) Size() (n int) { - var l int - _ = l - l = len(m.ServiceID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) +func (m *RemoveConfigResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *RemoveServiceResponse) Size() (n int) { - var l int - _ = l - return n +func (m *RemoveConfigResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ListServicesRequest) Size() (n int) { +func (m *RemoveConfigResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Filters != nil { - l = m.Filters.Size() - n += 1 + l + sovControl(uint64(l)) + return len(dAtA) - i, nil +} + +func (m *CreateExtensionRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *ListServicesRequest_Filters) Size() (n int) { +func (m *CreateExtensionRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateExtensionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } - } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } - } - if len(m.Labels) > 0 { - for k, v := range m.Labels { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) - } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintControl(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x12 } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) + if m.Annotations != nil { + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - if len(m.Runtimes) > 0 { - for _, s := range m.Runtimes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } + return len(dAtA) - i, nil +} + +func (m *CreateExtensionResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *ListServicesResponse) Size() (n int) { +func (m *CreateExtensionResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateExtensionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.Services) > 0 { - for _, e := range m.Services { - l = e.Size() - n += 1 + l + sovControl(uint64(l)) + if m.Extension != nil { + { + size, err := m.Extension.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *CreateNetworkRequest) Size() (n int) { - var l int - _ = l - if m.Spec != nil { - l = m.Spec.Size() - n += 1 + l + sovControl(uint64(l)) +func (m *RemoveExtensionRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *CreateNetworkResponse) Size() (n int) { - var l int - _ = l - if m.Network != nil { - l = m.Network.Size() - n += 1 + l + sovControl(uint64(l)) - } - return n +func (m *RemoveExtensionRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *GetNetworkRequest) Size() (n int) { +func (m *RemoveExtensionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) + if len(m.ExtensionID) > 0 { + i -= len(m.ExtensionID) + copy(dAtA[i:], m.ExtensionID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ExtensionID))) + i-- + dAtA[i] = 0xa } - l = len(m.NetworkID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) + return len(dAtA) - i, nil +} + +func (m *RemoveExtensionResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *GetNetworkResponse) Size() (n int) { +func (m *RemoveExtensionResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveExtensionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Network != nil { - l = m.Network.Size() - n += 1 + l + sovControl(uint64(l)) + return len(dAtA) - i, nil +} + +func (m *GetExtensionRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *RemoveNetworkRequest) Size() (n int) { +func (m *GetExtensionRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetExtensionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.Name) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) + if len(m.ExtensionID) > 0 { + i -= len(m.ExtensionID) + copy(dAtA[i:], m.ExtensionID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ExtensionID))) + i-- + dAtA[i] = 0xa } - l = len(m.NetworkID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) + return len(dAtA) - i, nil +} + +func (m *GetExtensionResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *RemoveNetworkResponse) Size() (n int) { - var l int - _ = l - return n +func (m *GetExtensionResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ListNetworksRequest) Size() (n int) { +func (m *GetExtensionResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Filters != nil { - l = m.Filters.Size() - n += 1 + l + sovControl(uint64(l)) + if m.Extension != nil { + { + size, err := m.Extension.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *ListNetworksRequest_Filters) Size() (n int) { +func (m *CreateResourceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CreateResourceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - l = len(s) - n += 1 + l + sovControl(uint64(l)) + if m.Payload != nil { + { + size, err := m.Payload.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x1a } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } + if len(m.Kind) > 0 { + i -= len(m.Kind) + copy(dAtA[i:], m.Kind) + i = encodeVarintControl(dAtA, i, uint64(len(m.Kind))) + i-- + dAtA[i] = 0x12 } - if len(m.Labels) > 0 { - for k, v := range m.Labels { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + if m.Annotations != nil { + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } + return len(dAtA) - i, nil +} + +func (m *CreateResourceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *ListNetworksResponse) Size() (n int) { +func (m *CreateResourceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateResourceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.Networks) > 0 { - for _, e := range m.Networks { - l = e.Size() - n += 1 + l + sovControl(uint64(l)) + if m.Resource != nil { + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *GetClusterRequest) Size() (n int) { - var l int - _ = l - l = len(m.ClusterID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) +func (m *RemoveResourceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *GetClusterResponse) Size() (n int) { +func (m *RemoveResourceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Cluster != nil { - l = m.Cluster.Size() - n += 1 + l + sovControl(uint64(l)) + if len(m.ResourceID) > 0 { + i -= len(m.ResourceID) + copy(dAtA[i:], m.ResourceID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ResourceID))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *ListClustersRequest) Size() (n int) { +func (m *RemoveResourceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RemoveResourceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveResourceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if m.Filters != nil { - l = m.Filters.Size() - n += 1 + l + sovControl(uint64(l)) + return len(dAtA) - i, nil +} + +func (m *UpdateResourceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *ListClustersRequest_Filters) Size() (n int) { +func (m *UpdateResourceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - l = len(s) - n += 1 + l + sovControl(uint64(l)) + if m.Payload != nil { + { + size, err := m.Payload.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x22 } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) + if m.Annotations != nil { + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x1a } - if len(m.Labels) > 0 { - for k, v := range m.Labels { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + if m.ResourceVersion != nil { + { + size, err := m.ResourceVersion.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x12 } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } + if len(m.ResourceID) > 0 { + i -= len(m.ResourceID) + copy(dAtA[i:], m.ResourceID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ResourceID))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *ListClustersResponse) Size() (n int) { - var l int - _ = l - if len(m.Clusters) > 0 { - for _, e := range m.Clusters { - l = e.Size() - n += 1 + l + sovControl(uint64(l)) - } +func (m *UpdateResourceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *KeyRotation) Size() (n int) { - var l int - _ = l - if m.WorkerJoinToken { - n += 2 - } - if m.ManagerJoinToken { - n += 2 - } - if m.ManagerUnlockKey { - n += 2 - } - return n +func (m *UpdateResourceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *UpdateClusterRequest) Size() (n int) { +func (m *UpdateResourceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.ClusterID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) - } - if m.ClusterVersion != nil { - l = m.ClusterVersion.Size() - n += 1 + l + sovControl(uint64(l)) - } - if m.Spec != nil { - l = m.Spec.Size() - n += 1 + l + sovControl(uint64(l)) + if m.Resource != nil { + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - l = m.Rotation.Size() - n += 1 + l + sovControl(uint64(l)) - return n + return len(dAtA) - i, nil } -func (m *UpdateClusterResponse) Size() (n int) { - var l int - _ = l - if m.Cluster != nil { - l = m.Cluster.Size() - n += 1 + l + sovControl(uint64(l)) +func (m *GetResourceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *GetSecretRequest) Size() (n int) { +func (m *GetResourceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.SecretID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) + if len(m.ResourceID) > 0 { + i -= len(m.ResourceID) + copy(dAtA[i:], m.ResourceID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ResourceID))) + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *GetSecretResponse) Size() (n int) { - var l int - _ = l - if m.Secret != nil { - l = m.Secret.Size() - n += 1 + l + sovControl(uint64(l)) +func (m *GetResourceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *UpdateSecretRequest) Size() (n int) { +func (m *GetResourceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetResourceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.SecretID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) - } - if m.SecretVersion != nil { - l = m.SecretVersion.Size() - n += 1 + l + sovControl(uint64(l)) - } - if m.Spec != nil { - l = m.Spec.Size() - n += 1 + l + sovControl(uint64(l)) + if m.Resource != nil { + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *UpdateSecretResponse) Size() (n int) { - var l int - _ = l - if m.Secret != nil { - l = m.Secret.Size() - n += 1 + l + sovControl(uint64(l)) +func (m *ListResourcesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *ListSecretsRequest) Size() (n int) { +func (m *ListResourcesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListResourcesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l if m.Filters != nil { - l = m.Filters.Size() - n += 1 + l + sovControl(uint64(l)) + { + size, err := m.Filters.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - return n + return len(dAtA) - i, nil } -func (m *ListSecretsRequest_Filters) Size() (n int) { +func (m *ListResourcesRequest_Filters) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListResourcesRequest_Filters) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListResourcesRequest_Filters) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } + if len(m.Kind) > 0 { + i -= len(m.Kind) + copy(dAtA[i:], m.Kind) + i = encodeVarintControl(dAtA, i, uint64(len(m.Kind))) + i-- + dAtA[i] = 0x2a } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) + if len(m.NamePrefixes) > 0 { + for iNdEx := len(m.NamePrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NamePrefixes[iNdEx]) + copy(dAtA[i:], m.NamePrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.NamePrefixes[iNdEx]))) + i-- + dAtA[i] = 0x22 } } if len(m.Labels) > 0 { - for k, v := range m.Labels { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintControl(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintControl(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a } } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) + if len(m.IDPrefixes) > 0 { + for iNdEx := len(m.IDPrefixes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IDPrefixes[iNdEx]) + copy(dAtA[i:], m.IDPrefixes[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.IDPrefixes[iNdEx]))) + i-- + dAtA[i] = 0x12 } } - return n -} - -func (m *ListSecretsResponse) Size() (n int) { - var l int - _ = l - if len(m.Secrets) > 0 { - for _, e := range m.Secrets { - l = e.Size() - n += 1 + l + sovControl(uint64(l)) + if len(m.Names) > 0 { + for iNdEx := len(m.Names) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Names[iNdEx]) + copy(dAtA[i:], m.Names[iNdEx]) + i = encodeVarintControl(dAtA, i, uint64(len(m.Names[iNdEx]))) + i-- + dAtA[i] = 0xa } } - return n + return len(dAtA) - i, nil } -func (m *CreateSecretRequest) Size() (n int) { - var l int - _ = l - if m.Spec != nil { - l = m.Spec.Size() - n += 1 + l + sovControl(uint64(l)) +func (m *ListResourcesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return n + return dAtA[:n], nil } -func (m *CreateSecretResponse) Size() (n int) { - var l int - _ = l - if m.Secret != nil { - l = m.Secret.Size() - n += 1 + l + sovControl(uint64(l)) - } - return n +func (m *ListResourcesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *RemoveSecretRequest) Size() (n int) { +func (m *ListResourcesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i var l int _ = l - l = len(m.SecretID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) + if len(m.Resources) > 0 { + for iNdEx := len(m.Resources) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Resources[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } } - return n -} - -func (m *RemoveSecretResponse) Size() (n int) { - var l int - _ = l - return n + return len(dAtA) - i, nil } -func (m *GetConfigRequest) Size() (n int) { - var l int - _ = l - l = len(m.ConfigID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) +func encodeVarintControl(dAtA []byte, offset int, v uint64) int { + offset -= sovControl(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ } - return n + dAtA[offset] = uint8(v) + return base } -func (m *GetConfigResponse) Size() (n int) { - var l int - _ = l - if m.Config != nil { - l = m.Config.Size() - n += 1 + l + sovControl(uint64(l)) - } - return n +type raftProxyControlServer struct { + local ControlServer + connSelector raftselector.ConnProvider + localCtxMods, remoteCtxMods []func(context.Context) (context.Context, error) } -func (m *UpdateConfigRequest) Size() (n int) { - var l int - _ = l - l = len(m.ConfigID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) - } - if m.ConfigVersion != nil { - l = m.ConfigVersion.Size() - n += 1 + l + sovControl(uint64(l)) - } - if m.Spec != nil { - l = m.Spec.Size() - n += 1 + l + sovControl(uint64(l)) +func NewRaftProxyControlServer(local ControlServer, connSelector raftselector.ConnProvider, localCtxMod, remoteCtxMod func(context.Context) (context.Context, error)) ControlServer { + redirectChecker := func(ctx context.Context) (context.Context, error) { + p, ok := peer.FromContext(ctx) + if !ok { + return ctx, status.Errorf(codes.InvalidArgument, "remote addr is not found in context") + } + addr := p.Addr.String() + md, ok := metadata.FromIncomingContext(ctx) + if ok && len(md["redirect"]) != 0 { + return ctx, status.Errorf(codes.ResourceExhausted, "more than one redirect to leader from: %s", md["redirect"]) + } + if !ok { + md = metadata.New(map[string]string{}) + } + md["redirect"] = append(md["redirect"], addr) + return metadata.NewOutgoingContext(ctx, md), nil } - return n -} + remoteMods := []func(context.Context) (context.Context, error){redirectChecker} + remoteMods = append(remoteMods, remoteCtxMod) -func (m *UpdateConfigResponse) Size() (n int) { - var l int - _ = l - if m.Config != nil { - l = m.Config.Size() - n += 1 + l + sovControl(uint64(l)) + var localMods []func(context.Context) (context.Context, error) + if localCtxMod != nil { + localMods = []func(context.Context) (context.Context, error){localCtxMod} } - return n -} -func (m *ListConfigsRequest) Size() (n int) { - var l int - _ = l - if m.Filters != nil { - l = m.Filters.Size() - n += 1 + l + sovControl(uint64(l)) + return &raftProxyControlServer{ + local: local, + connSelector: connSelector, + localCtxMods: localMods, + remoteCtxMods: remoteMods, } - return n } - -func (m *ListConfigsRequest_Filters) Size() (n int) { - var l int - _ = l - if len(m.Names) > 0 { - for _, s := range m.Names { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } - } - if len(m.IDPrefixes) > 0 { - for _, s := range m.IDPrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) - } - } - if len(m.Labels) > 0 { - for k, v := range m.Labels { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) - n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) - } - } - if len(m.NamePrefixes) > 0 { - for _, s := range m.NamePrefixes { - l = len(s) - n += 1 + l + sovControl(uint64(l)) +func (p *raftProxyControlServer) runCtxMods(ctx context.Context, ctxMods []func(context.Context) (context.Context, error)) (context.Context, error) { + var err error + for _, mod := range ctxMods { + ctx, err = mod(ctx) + if err != nil { + return ctx, err } } - return n + return ctx, nil } +func (p *raftProxyControlServer) pollNewLeaderConn(ctx context.Context) (*grpc.ClientConn, error) { + ticker := rafttime.NewTicker(500 * rafttime.Millisecond) + defer ticker.Stop() + for { + select { + case <-ticker.C: + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + return nil, err + } -func (m *ListConfigsResponse) Size() (n int) { - var l int - _ = l - if len(m.Configs) > 0 { - for _, e := range m.Configs { - l = e.Size() - n += 1 + l + sovControl(uint64(l)) + client := NewHealthClient(conn) + + resp, err := client.Check(ctx, &HealthCheckRequest{Service: "Raft"}) + if err != nil || resp.Status != HealthCheckResponse_SERVING { + continue + } + return conn, nil + case <-ctx.Done(): + return nil, ctx.Err() } } - return n } -func (m *CreateConfigRequest) Size() (n int) { - var l int - _ = l - if m.Spec != nil { - l = m.Spec.Size() - n += 1 + l + sovControl(uint64(l)) - } - return n -} +func (p *raftProxyControlServer) GetNode(ctx context.Context, r *GetNodeRequest) (*GetNodeResponse, error) { -func (m *CreateConfigResponse) Size() (n int) { - var l int - _ = l - if m.Config != nil { - l = m.Config.Size() - n += 1 + l + sovControl(uint64(l)) + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.GetNode(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - return n -} -func (m *RemoveConfigRequest) Size() (n int) { - var l int - _ = l - l = len(m.ConfigID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) + resp, err := NewControlClient(conn).GetNode(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.GetNode(ctx, r) + } + return nil, err + } + return NewControlClient(conn).GetNode(modCtx, r) } - return n + return resp, err } -func (m *RemoveConfigResponse) Size() (n int) { - var l int - _ = l - return n -} +func (p *raftProxyControlServer) ListNodes(ctx context.Context, r *ListNodesRequest) (*ListNodesResponse, error) { -func sovControl(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.ListNodes(ctx, r) } + return nil, err } - return n -} -func sozControl(x uint64) (n int) { - return sovControl(uint64((x << 1) ^ uint64((int64(x) >> 63)))) -} -func (this *GetNodeRequest) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&GetNodeRequest{`, - `NodeID:` + fmt.Sprintf("%v", this.NodeID) + `,`, - `}`, - }, "") - return s -} -func (this *GetNodeResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).ListNodes(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.ListNodes(ctx, r) + } + return nil, err + } + return NewControlClient(conn).ListNodes(modCtx, r) } - s := strings.Join([]string{`&GetNodeResponse{`, - `Node:` + strings.Replace(fmt.Sprintf("%v", this.Node), "Node", "Node", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListNodesRequest) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) UpdateNode(ctx context.Context, r *UpdateNodeRequest) (*UpdateNodeResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.UpdateNode(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&ListNodesRequest{`, - `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListNodesRequest_Filters", "ListNodesRequest_Filters", 1) + `,`, - `}`, - }, "") - return s -} -func (this *ListNodesRequest_Filters) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - keysForLabels := make([]string, 0, len(this.Labels)) - for k, _ := range this.Labels { - keysForLabels = append(keysForLabels, k) + + resp, err := NewControlClient(conn).UpdateNode(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.UpdateNode(ctx, r) + } + return nil, err + } + return NewControlClient(conn).UpdateNode(modCtx, r) } - sortkeys.Strings(keysForLabels) - mapStringForLabels := "map[string]string{" - for _, k := range keysForLabels { - mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + return resp, err +} + +func (p *raftProxyControlServer) RemoveNode(ctx context.Context, r *RemoveNodeRequest) (*RemoveNodeResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.RemoveNode(ctx, r) + } + return nil, err } - mapStringForLabels += "}" - keysForNodeLabels := make([]string, 0, len(this.NodeLabels)) - for k, _ := range this.NodeLabels { - keysForNodeLabels = append(keysForNodeLabels, k) + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - sortkeys.Strings(keysForNodeLabels) - mapStringForNodeLabels := "map[string]string{" - for _, k := range keysForNodeLabels { - mapStringForNodeLabels += fmt.Sprintf("%v: %v,", k, this.NodeLabels[k]) + + resp, err := NewControlClient(conn).RemoveNode(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.RemoveNode(ctx, r) + } + return nil, err + } + return NewControlClient(conn).RemoveNode(modCtx, r) } - mapStringForNodeLabels += "}" - s := strings.Join([]string{`&ListNodesRequest_Filters{`, - `Names:` + fmt.Sprintf("%v", this.Names) + `,`, - `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, - `Labels:` + mapStringForLabels + `,`, - `Memberships:` + fmt.Sprintf("%v", this.Memberships) + `,`, - `Roles:` + fmt.Sprintf("%v", this.Roles) + `,`, - `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, - `NodeLabels:` + mapStringForNodeLabels + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListNodesResponse) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) GetTask(ctx context.Context, r *GetTaskRequest) (*GetTaskResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.GetTask(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&ListNodesResponse{`, - `Nodes:` + strings.Replace(fmt.Sprintf("%v", this.Nodes), "Node", "Node", 1) + `,`, - `}`, - }, "") - return s -} -func (this *UpdateNodeRequest) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&UpdateNodeRequest{`, - `NodeID:` + fmt.Sprintf("%v", this.NodeID) + `,`, - `NodeVersion:` + strings.Replace(fmt.Sprintf("%v", this.NodeVersion), "Version", "Version", 1) + `,`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "NodeSpec", "NodeSpec", 1) + `,`, - `}`, - }, "") - return s -} -func (this *UpdateNodeResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).GetTask(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.GetTask(ctx, r) + } + return nil, err + } + return NewControlClient(conn).GetTask(modCtx, r) } - s := strings.Join([]string{`&UpdateNodeResponse{`, - `Node:` + strings.Replace(fmt.Sprintf("%v", this.Node), "Node", "Node", 1) + `,`, - `}`, - }, "") - return s -} -func (this *RemoveNodeRequest) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&RemoveNodeRequest{`, - `NodeID:` + fmt.Sprintf("%v", this.NodeID) + `,`, - `Force:` + fmt.Sprintf("%v", this.Force) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *RemoveNodeResponse) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) ListTasks(ctx context.Context, r *ListTasksRequest) (*ListTasksResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.ListTasks(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&RemoveNodeResponse{`, - `}`, - }, "") - return s -} -func (this *GetTaskRequest) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&GetTaskRequest{`, - `TaskID:` + fmt.Sprintf("%v", this.TaskID) + `,`, - `}`, - }, "") - return s -} -func (this *GetTaskResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).ListTasks(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.ListTasks(ctx, r) + } + return nil, err + } + return NewControlClient(conn).ListTasks(modCtx, r) } - s := strings.Join([]string{`&GetTaskResponse{`, - `Task:` + strings.Replace(fmt.Sprintf("%v", this.Task), "Task", "Task", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *RemoveTaskRequest) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) RemoveTask(ctx context.Context, r *RemoveTaskRequest) (*RemoveTaskResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.RemoveTask(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&RemoveTaskRequest{`, - `TaskID:` + fmt.Sprintf("%v", this.TaskID) + `,`, - `}`, - }, "") - return s -} -func (this *RemoveTaskResponse) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&RemoveTaskResponse{`, - `}`, - }, "") - return s -} -func (this *ListTasksRequest) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).RemoveTask(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.RemoveTask(ctx, r) + } + return nil, err + } + return NewControlClient(conn).RemoveTask(modCtx, r) } - s := strings.Join([]string{`&ListTasksRequest{`, - `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListTasksRequest_Filters", "ListTasksRequest_Filters", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListTasksRequest_Filters) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) GetService(ctx context.Context, r *GetServiceRequest) (*GetServiceResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.GetService(ctx, r) + } + return nil, err } - keysForLabels := make([]string, 0, len(this.Labels)) - for k, _ := range this.Labels { - keysForLabels = append(keysForLabels, k) + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - sortkeys.Strings(keysForLabels) - mapStringForLabels := "map[string]string{" - for _, k := range keysForLabels { - mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + + resp, err := NewControlClient(conn).GetService(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.GetService(ctx, r) + } + return nil, err + } + return NewControlClient(conn).GetService(modCtx, r) } - mapStringForLabels += "}" - s := strings.Join([]string{`&ListTasksRequest_Filters{`, - `Names:` + fmt.Sprintf("%v", this.Names) + `,`, - `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, - `Labels:` + mapStringForLabels + `,`, - `ServiceIDs:` + fmt.Sprintf("%v", this.ServiceIDs) + `,`, - `NodeIDs:` + fmt.Sprintf("%v", this.NodeIDs) + `,`, - `DesiredStates:` + fmt.Sprintf("%v", this.DesiredStates) + `,`, - `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, - `UpToDate:` + fmt.Sprintf("%v", this.UpToDate) + `,`, - `Runtimes:` + fmt.Sprintf("%v", this.Runtimes) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListTasksResponse) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) ListServices(ctx context.Context, r *ListServicesRequest) (*ListServicesResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.ListServices(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&ListTasksResponse{`, - `Tasks:` + strings.Replace(fmt.Sprintf("%v", this.Tasks), "Task", "Task", 1) + `,`, - `}`, - }, "") - return s -} -func (this *CreateServiceRequest) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&CreateServiceRequest{`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "ServiceSpec", "ServiceSpec", 1) + `,`, - `}`, - }, "") - return s -} -func (this *CreateServiceResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).ListServices(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.ListServices(ctx, r) + } + return nil, err + } + return NewControlClient(conn).ListServices(modCtx, r) } - s := strings.Join([]string{`&CreateServiceResponse{`, - `Service:` + strings.Replace(fmt.Sprintf("%v", this.Service), "Service", "Service", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *GetServiceRequest) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) CreateService(ctx context.Context, r *CreateServiceRequest) (*CreateServiceResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.CreateService(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&GetServiceRequest{`, - `ServiceID:` + fmt.Sprintf("%v", this.ServiceID) + `,`, - `InsertDefaults:` + fmt.Sprintf("%v", this.InsertDefaults) + `,`, - `}`, - }, "") - return s -} -func (this *GetServiceResponse) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&GetServiceResponse{`, - `Service:` + strings.Replace(fmt.Sprintf("%v", this.Service), "Service", "Service", 1) + `,`, - `}`, - }, "") - return s -} -func (this *UpdateServiceRequest) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&UpdateServiceRequest{`, - `ServiceID:` + fmt.Sprintf("%v", this.ServiceID) + `,`, - `ServiceVersion:` + strings.Replace(fmt.Sprintf("%v", this.ServiceVersion), "Version", "Version", 1) + `,`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "ServiceSpec", "ServiceSpec", 1) + `,`, - `Rollback:` + fmt.Sprintf("%v", this.Rollback) + `,`, - `}`, - }, "") - return s -} -func (this *UpdateServiceResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).CreateService(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.CreateService(ctx, r) + } + return nil, err + } + return NewControlClient(conn).CreateService(modCtx, r) } - s := strings.Join([]string{`&UpdateServiceResponse{`, - `Service:` + strings.Replace(fmt.Sprintf("%v", this.Service), "Service", "Service", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *RemoveServiceRequest) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) UpdateService(ctx context.Context, r *UpdateServiceRequest) (*UpdateServiceResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.UpdateService(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&RemoveServiceRequest{`, - `ServiceID:` + fmt.Sprintf("%v", this.ServiceID) + `,`, - `}`, - }, "") - return s -} -func (this *RemoveServiceResponse) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&RemoveServiceResponse{`, - `}`, - }, "") - return s -} -func (this *ListServicesRequest) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).UpdateService(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.UpdateService(ctx, r) + } + return nil, err + } + return NewControlClient(conn).UpdateService(modCtx, r) } - s := strings.Join([]string{`&ListServicesRequest{`, - `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListServicesRequest_Filters", "ListServicesRequest_Filters", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListServicesRequest_Filters) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) RemoveService(ctx context.Context, r *RemoveServiceRequest) (*RemoveServiceResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.RemoveService(ctx, r) + } + return nil, err } - keysForLabels := make([]string, 0, len(this.Labels)) - for k, _ := range this.Labels { - keysForLabels = append(keysForLabels, k) + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - sortkeys.Strings(keysForLabels) - mapStringForLabels := "map[string]string{" - for _, k := range keysForLabels { - mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + + resp, err := NewControlClient(conn).RemoveService(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.RemoveService(ctx, r) + } + return nil, err + } + return NewControlClient(conn).RemoveService(modCtx, r) } - mapStringForLabels += "}" - s := strings.Join([]string{`&ListServicesRequest_Filters{`, - `Names:` + fmt.Sprintf("%v", this.Names) + `,`, - `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, - `Labels:` + mapStringForLabels + `,`, - `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, - `Runtimes:` + fmt.Sprintf("%v", this.Runtimes) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListServicesResponse) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) ListServiceStatuses(ctx context.Context, r *ListServiceStatusesRequest) (*ListServiceStatusesResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.ListServiceStatuses(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&ListServicesResponse{`, - `Services:` + strings.Replace(fmt.Sprintf("%v", this.Services), "Service", "Service", 1) + `,`, - `}`, - }, "") - return s -} -func (this *CreateNetworkRequest) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&CreateNetworkRequest{`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "NetworkSpec", "NetworkSpec", 1) + `,`, - `}`, - }, "") - return s -} -func (this *CreateNetworkResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).ListServiceStatuses(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.ListServiceStatuses(ctx, r) + } + return nil, err + } + return NewControlClient(conn).ListServiceStatuses(modCtx, r) } - s := strings.Join([]string{`&CreateNetworkResponse{`, - `Network:` + strings.Replace(fmt.Sprintf("%v", this.Network), "Network", "Network", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *GetNetworkRequest) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) GetNetwork(ctx context.Context, r *GetNetworkRequest) (*GetNetworkResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.GetNetwork(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&GetNetworkRequest{`, - `Name:` + fmt.Sprintf("%v", this.Name) + `,`, - `NetworkID:` + fmt.Sprintf("%v", this.NetworkID) + `,`, - `}`, - }, "") - return s -} -func (this *GetNetworkResponse) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&GetNetworkResponse{`, - `Network:` + strings.Replace(fmt.Sprintf("%v", this.Network), "Network", "Network", 1) + `,`, - `}`, - }, "") - return s + + resp, err := NewControlClient(conn).GetNetwork(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.GetNetwork(ctx, r) + } + return nil, err + } + return NewControlClient(conn).GetNetwork(modCtx, r) + } + return resp, err } -func (this *RemoveNetworkRequest) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) ListNetworks(ctx context.Context, r *ListNetworksRequest) (*ListNetworksResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.ListNetworks(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&RemoveNetworkRequest{`, - `Name:` + fmt.Sprintf("%v", this.Name) + `,`, - `NetworkID:` + fmt.Sprintf("%v", this.NetworkID) + `,`, - `}`, - }, "") - return s -} -func (this *RemoveNetworkResponse) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&RemoveNetworkResponse{`, - `}`, - }, "") - return s -} -func (this *ListNetworksRequest) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).ListNetworks(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.ListNetworks(ctx, r) + } + return nil, err + } + return NewControlClient(conn).ListNetworks(modCtx, r) } - s := strings.Join([]string{`&ListNetworksRequest{`, - `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListNetworksRequest_Filters", "ListNetworksRequest_Filters", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListNetworksRequest_Filters) String() string { - if this == nil { - return "nil" - } - keysForLabels := make([]string, 0, len(this.Labels)) - for k, _ := range this.Labels { - keysForLabels = append(keysForLabels, k) + +func (p *raftProxyControlServer) CreateNetwork(ctx context.Context, r *CreateNetworkRequest) (*CreateNetworkResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.CreateNetwork(ctx, r) + } + return nil, err } - sortkeys.Strings(keysForLabels) - mapStringForLabels := "map[string]string{" - for _, k := range keysForLabels { - mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - mapStringForLabels += "}" - s := strings.Join([]string{`&ListNetworksRequest_Filters{`, - `Names:` + fmt.Sprintf("%v", this.Names) + `,`, - `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, - `Labels:` + mapStringForLabels + `,`, - `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, - `}`, - }, "") - return s -} -func (this *ListNetworksResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).CreateNetwork(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.CreateNetwork(ctx, r) + } + return nil, err + } + return NewControlClient(conn).CreateNetwork(modCtx, r) } - s := strings.Join([]string{`&ListNetworksResponse{`, - `Networks:` + strings.Replace(fmt.Sprintf("%v", this.Networks), "Network", "Network", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *GetClusterRequest) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) RemoveNetwork(ctx context.Context, r *RemoveNetworkRequest) (*RemoveNetworkResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.RemoveNetwork(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&GetClusterRequest{`, - `ClusterID:` + fmt.Sprintf("%v", this.ClusterID) + `,`, - `}`, - }, "") - return s -} -func (this *GetClusterResponse) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&GetClusterResponse{`, - `Cluster:` + strings.Replace(fmt.Sprintf("%v", this.Cluster), "Cluster", "Cluster", 1) + `,`, - `}`, - }, "") - return s -} -func (this *ListClustersRequest) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).RemoveNetwork(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.RemoveNetwork(ctx, r) + } + return nil, err + } + return NewControlClient(conn).RemoveNetwork(modCtx, r) } - s := strings.Join([]string{`&ListClustersRequest{`, - `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListClustersRequest_Filters", "ListClustersRequest_Filters", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListClustersRequest_Filters) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) GetCluster(ctx context.Context, r *GetClusterRequest) (*GetClusterResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.GetCluster(ctx, r) + } + return nil, err } - keysForLabels := make([]string, 0, len(this.Labels)) - for k, _ := range this.Labels { - keysForLabels = append(keysForLabels, k) + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - sortkeys.Strings(keysForLabels) - mapStringForLabels := "map[string]string{" - for _, k := range keysForLabels { - mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + + resp, err := NewControlClient(conn).GetCluster(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.GetCluster(ctx, r) + } + return nil, err + } + return NewControlClient(conn).GetCluster(modCtx, r) } - mapStringForLabels += "}" - s := strings.Join([]string{`&ListClustersRequest_Filters{`, - `Names:` + fmt.Sprintf("%v", this.Names) + `,`, - `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, - `Labels:` + mapStringForLabels + `,`, - `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListClustersResponse) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) ListClusters(ctx context.Context, r *ListClustersRequest) (*ListClustersResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.ListClusters(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&ListClustersResponse{`, - `Clusters:` + strings.Replace(fmt.Sprintf("%v", this.Clusters), "Cluster", "Cluster", 1) + `,`, - `}`, - }, "") - return s -} -func (this *KeyRotation) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&KeyRotation{`, - `WorkerJoinToken:` + fmt.Sprintf("%v", this.WorkerJoinToken) + `,`, - `ManagerJoinToken:` + fmt.Sprintf("%v", this.ManagerJoinToken) + `,`, - `ManagerUnlockKey:` + fmt.Sprintf("%v", this.ManagerUnlockKey) + `,`, - `}`, - }, "") - return s -} -func (this *UpdateClusterRequest) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).ListClusters(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.ListClusters(ctx, r) + } + return nil, err + } + return NewControlClient(conn).ListClusters(modCtx, r) } - s := strings.Join([]string{`&UpdateClusterRequest{`, - `ClusterID:` + fmt.Sprintf("%v", this.ClusterID) + `,`, - `ClusterVersion:` + strings.Replace(fmt.Sprintf("%v", this.ClusterVersion), "Version", "Version", 1) + `,`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "ClusterSpec", "ClusterSpec", 1) + `,`, - `Rotation:` + strings.Replace(strings.Replace(this.Rotation.String(), "KeyRotation", "KeyRotation", 1), `&`, ``, 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *UpdateClusterResponse) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) UpdateCluster(ctx context.Context, r *UpdateClusterRequest) (*UpdateClusterResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.UpdateCluster(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&UpdateClusterResponse{`, - `Cluster:` + strings.Replace(fmt.Sprintf("%v", this.Cluster), "Cluster", "Cluster", 1) + `,`, - `}`, - }, "") - return s -} -func (this *GetSecretRequest) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&GetSecretRequest{`, - `SecretID:` + fmt.Sprintf("%v", this.SecretID) + `,`, - `}`, - }, "") - return s -} -func (this *GetSecretResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).UpdateCluster(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.UpdateCluster(ctx, r) + } + return nil, err + } + return NewControlClient(conn).UpdateCluster(modCtx, r) } - s := strings.Join([]string{`&GetSecretResponse{`, - `Secret:` + strings.Replace(fmt.Sprintf("%v", this.Secret), "Secret", "Secret", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *UpdateSecretRequest) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) GetSecret(ctx context.Context, r *GetSecretRequest) (*GetSecretResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.GetSecret(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&UpdateSecretRequest{`, - `SecretID:` + fmt.Sprintf("%v", this.SecretID) + `,`, - `SecretVersion:` + strings.Replace(fmt.Sprintf("%v", this.SecretVersion), "Version", "Version", 1) + `,`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "SecretSpec", "SecretSpec", 1) + `,`, - `}`, - }, "") - return s -} -func (this *UpdateSecretResponse) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&UpdateSecretResponse{`, - `Secret:` + strings.Replace(fmt.Sprintf("%v", this.Secret), "Secret", "Secret", 1) + `,`, - `}`, - }, "") - return s -} -func (this *ListSecretsRequest) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).GetSecret(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.GetSecret(ctx, r) + } + return nil, err + } + return NewControlClient(conn).GetSecret(modCtx, r) } - s := strings.Join([]string{`&ListSecretsRequest{`, - `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListSecretsRequest_Filters", "ListSecretsRequest_Filters", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListSecretsRequest_Filters) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) UpdateSecret(ctx context.Context, r *UpdateSecretRequest) (*UpdateSecretResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.UpdateSecret(ctx, r) + } + return nil, err } - keysForLabels := make([]string, 0, len(this.Labels)) - for k, _ := range this.Labels { - keysForLabels = append(keysForLabels, k) + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - sortkeys.Strings(keysForLabels) - mapStringForLabels := "map[string]string{" - for _, k := range keysForLabels { - mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + + resp, err := NewControlClient(conn).UpdateSecret(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.UpdateSecret(ctx, r) + } + return nil, err + } + return NewControlClient(conn).UpdateSecret(modCtx, r) } - mapStringForLabels += "}" - s := strings.Join([]string{`&ListSecretsRequest_Filters{`, - `Names:` + fmt.Sprintf("%v", this.Names) + `,`, - `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, - `Labels:` + mapStringForLabels + `,`, - `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListSecretsResponse) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) ListSecrets(ctx context.Context, r *ListSecretsRequest) (*ListSecretsResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.ListSecrets(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&ListSecretsResponse{`, - `Secrets:` + strings.Replace(fmt.Sprintf("%v", this.Secrets), "Secret", "Secret", 1) + `,`, - `}`, - }, "") - return s -} -func (this *CreateSecretRequest) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&CreateSecretRequest{`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "SecretSpec", "SecretSpec", 1) + `,`, - `}`, - }, "") - return s -} -func (this *CreateSecretResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).ListSecrets(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.ListSecrets(ctx, r) + } + return nil, err + } + return NewControlClient(conn).ListSecrets(modCtx, r) } - s := strings.Join([]string{`&CreateSecretResponse{`, - `Secret:` + strings.Replace(fmt.Sprintf("%v", this.Secret), "Secret", "Secret", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *RemoveSecretRequest) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&RemoveSecretRequest{`, - `SecretID:` + fmt.Sprintf("%v", this.SecretID) + `,`, - `}`, - }, "") - return s -} -func (this *RemoveSecretResponse) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) CreateSecret(ctx context.Context, r *CreateSecretRequest) (*CreateSecretResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.CreateSecret(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&RemoveSecretResponse{`, - `}`, - }, "") - return s -} -func (this *GetConfigRequest) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&GetConfigRequest{`, - `ConfigID:` + fmt.Sprintf("%v", this.ConfigID) + `,`, - `}`, - }, "") - return s -} -func (this *GetConfigResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).CreateSecret(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.CreateSecret(ctx, r) + } + return nil, err + } + return NewControlClient(conn).CreateSecret(modCtx, r) } - s := strings.Join([]string{`&GetConfigResponse{`, - `Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "Config", "Config", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *UpdateConfigRequest) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) RemoveSecret(ctx context.Context, r *RemoveSecretRequest) (*RemoveSecretResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.RemoveSecret(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&UpdateConfigRequest{`, - `ConfigID:` + fmt.Sprintf("%v", this.ConfigID) + `,`, - `ConfigVersion:` + strings.Replace(fmt.Sprintf("%v", this.ConfigVersion), "Version", "Version", 1) + `,`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "ConfigSpec", "ConfigSpec", 1) + `,`, - `}`, - }, "") - return s -} -func (this *UpdateConfigResponse) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&UpdateConfigResponse{`, - `Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "Config", "Config", 1) + `,`, - `}`, - }, "") - return s -} -func (this *ListConfigsRequest) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).RemoveSecret(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.RemoveSecret(ctx, r) + } + return nil, err + } + return NewControlClient(conn).RemoveSecret(modCtx, r) } - s := strings.Join([]string{`&ListConfigsRequest{`, - `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListConfigsRequest_Filters", "ListConfigsRequest_Filters", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListConfigsRequest_Filters) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) GetConfig(ctx context.Context, r *GetConfigRequest) (*GetConfigResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.GetConfig(ctx, r) + } + return nil, err } - keysForLabels := make([]string, 0, len(this.Labels)) - for k, _ := range this.Labels { - keysForLabels = append(keysForLabels, k) + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - sortkeys.Strings(keysForLabels) - mapStringForLabels := "map[string]string{" - for _, k := range keysForLabels { - mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + + resp, err := NewControlClient(conn).GetConfig(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.GetConfig(ctx, r) + } + return nil, err + } + return NewControlClient(conn).GetConfig(modCtx, r) } - mapStringForLabels += "}" - s := strings.Join([]string{`&ListConfigsRequest_Filters{`, - `Names:` + fmt.Sprintf("%v", this.Names) + `,`, - `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, - `Labels:` + mapStringForLabels + `,`, - `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *ListConfigsResponse) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) UpdateConfig(ctx context.Context, r *UpdateConfigRequest) (*UpdateConfigResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.UpdateConfig(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&ListConfigsResponse{`, - `Configs:` + strings.Replace(fmt.Sprintf("%v", this.Configs), "Config", "Config", 1) + `,`, - `}`, - }, "") - return s -} -func (this *CreateConfigRequest) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&CreateConfigRequest{`, - `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "ConfigSpec", "ConfigSpec", 1) + `,`, - `}`, - }, "") - return s -} -func (this *CreateConfigResponse) String() string { - if this == nil { - return "nil" + + resp, err := NewControlClient(conn).UpdateConfig(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.UpdateConfig(ctx, r) + } + return nil, err + } + return NewControlClient(conn).UpdateConfig(modCtx, r) } - s := strings.Join([]string{`&CreateConfigResponse{`, - `Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "Config", "Config", 1) + `,`, - `}`, - }, "") - return s + return resp, err } -func (this *RemoveConfigRequest) String() string { - if this == nil { - return "nil" + +func (p *raftProxyControlServer) ListConfigs(ctx context.Context, r *ListConfigsRequest) (*ListConfigsResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.ListConfigs(ctx, r) + } + return nil, err } - s := strings.Join([]string{`&RemoveConfigRequest{`, - `ConfigID:` + fmt.Sprintf("%v", this.ConfigID) + `,`, - `}`, - }, "") - return s -} -func (this *RemoveConfigResponse) String() string { - if this == nil { - return "nil" + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err } - s := strings.Join([]string{`&RemoveConfigResponse{`, - `}`, - }, "") - return s -} -func valueToStringControl(v interface{}) string { - rv := reflect.ValueOf(v) + + resp, err := NewControlClient(conn).ListConfigs(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.ListConfigs(ctx, r) + } + return nil, err + } + return NewControlClient(conn).ListConfigs(modCtx, r) + } + return resp, err +} + +func (p *raftProxyControlServer) CreateConfig(ctx context.Context, r *CreateConfigRequest) (*CreateConfigResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.CreateConfig(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err + } + + resp, err := NewControlClient(conn).CreateConfig(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.CreateConfig(ctx, r) + } + return nil, err + } + return NewControlClient(conn).CreateConfig(modCtx, r) + } + return resp, err +} + +func (p *raftProxyControlServer) RemoveConfig(ctx context.Context, r *RemoveConfigRequest) (*RemoveConfigResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.RemoveConfig(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err + } + + resp, err := NewControlClient(conn).RemoveConfig(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.RemoveConfig(ctx, r) + } + return nil, err + } + return NewControlClient(conn).RemoveConfig(modCtx, r) + } + return resp, err +} + +func (p *raftProxyControlServer) GetExtension(ctx context.Context, r *GetExtensionRequest) (*GetExtensionResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.GetExtension(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err + } + + resp, err := NewControlClient(conn).GetExtension(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.GetExtension(ctx, r) + } + return nil, err + } + return NewControlClient(conn).GetExtension(modCtx, r) + } + return resp, err +} + +func (p *raftProxyControlServer) CreateExtension(ctx context.Context, r *CreateExtensionRequest) (*CreateExtensionResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.CreateExtension(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err + } + + resp, err := NewControlClient(conn).CreateExtension(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.CreateExtension(ctx, r) + } + return nil, err + } + return NewControlClient(conn).CreateExtension(modCtx, r) + } + return resp, err +} + +func (p *raftProxyControlServer) RemoveExtension(ctx context.Context, r *RemoveExtensionRequest) (*RemoveExtensionResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.RemoveExtension(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err + } + + resp, err := NewControlClient(conn).RemoveExtension(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.RemoveExtension(ctx, r) + } + return nil, err + } + return NewControlClient(conn).RemoveExtension(modCtx, r) + } + return resp, err +} + +func (p *raftProxyControlServer) GetResource(ctx context.Context, r *GetResourceRequest) (*GetResourceResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.GetResource(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err + } + + resp, err := NewControlClient(conn).GetResource(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.GetResource(ctx, r) + } + return nil, err + } + return NewControlClient(conn).GetResource(modCtx, r) + } + return resp, err +} + +func (p *raftProxyControlServer) UpdateResource(ctx context.Context, r *UpdateResourceRequest) (*UpdateResourceResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.UpdateResource(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err + } + + resp, err := NewControlClient(conn).UpdateResource(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.UpdateResource(ctx, r) + } + return nil, err + } + return NewControlClient(conn).UpdateResource(modCtx, r) + } + return resp, err +} + +func (p *raftProxyControlServer) ListResources(ctx context.Context, r *ListResourcesRequest) (*ListResourcesResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.ListResources(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err + } + + resp, err := NewControlClient(conn).ListResources(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.ListResources(ctx, r) + } + return nil, err + } + return NewControlClient(conn).ListResources(modCtx, r) + } + return resp, err +} + +func (p *raftProxyControlServer) CreateResource(ctx context.Context, r *CreateResourceRequest) (*CreateResourceResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.CreateResource(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err + } + + resp, err := NewControlClient(conn).CreateResource(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.CreateResource(ctx, r) + } + return nil, err + } + return NewControlClient(conn).CreateResource(modCtx, r) + } + return resp, err +} + +func (p *raftProxyControlServer) RemoveResource(ctx context.Context, r *RemoveResourceRequest) (*RemoveResourceResponse, error) { + + conn, err := p.connSelector.LeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + ctx, err = p.runCtxMods(ctx, p.localCtxMods) + if err != nil { + return nil, err + } + return p.local.RemoveResource(ctx, r) + } + return nil, err + } + modCtx, err := p.runCtxMods(ctx, p.remoteCtxMods) + if err != nil { + return nil, err + } + + resp, err := NewControlClient(conn).RemoveResource(modCtx, r) + if err != nil { + if !strings.Contains(err.Error(), "is closing") && !strings.Contains(err.Error(), "the connection is unavailable") && !strings.Contains(err.Error(), "connection error") { + return resp, err + } + conn, err := p.pollNewLeaderConn(ctx) + if err != nil { + if err == raftselector.ErrIsLeader { + return p.local.RemoveResource(ctx, r) + } + return nil, err + } + return NewControlClient(conn).RemoveResource(modCtx, r) + } + return resp, err +} + +func (m *GetNodeRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.NodeID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetNodeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Node != nil { + l = m.Node.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListNodesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Filters != nil { + l = m.Filters.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListNodesRequest_Filters) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Names) > 0 { + for _, s := range m.Names { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.IDPrefixes) > 0 { + for _, s := range m.IDPrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + if len(m.Memberships) > 0 { + for _, e := range m.Memberships { + n += 1 + sovControl(uint64(e)) + } + } + if len(m.Roles) > 0 { + for _, e := range m.Roles { + n += 1 + sovControl(uint64(e)) + } + } + if len(m.NamePrefixes) > 0 { + for _, s := range m.NamePrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.NodeLabels) > 0 { + for k, v := range m.NodeLabels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + return n +} + +func (m *ListNodesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Nodes) > 0 { + for _, e := range m.Nodes { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *UpdateNodeRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.NodeID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.NodeVersion != nil { + l = m.NodeVersion.Size() + n += 1 + l + sovControl(uint64(l)) + } + if m.Spec != nil { + l = m.Spec.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *UpdateNodeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Node != nil { + l = m.Node.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveNodeRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.NodeID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.Force { + n += 2 + } + return n +} + +func (m *RemoveNodeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *GetTaskRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.TaskID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetTaskResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Task != nil { + l = m.Task.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveTaskRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.TaskID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveTaskResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *ListTasksRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Filters != nil { + l = m.Filters.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListTasksRequest_Filters) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Names) > 0 { + for _, s := range m.Names { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.IDPrefixes) > 0 { + for _, s := range m.IDPrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + if len(m.ServiceIDs) > 0 { + for _, s := range m.ServiceIDs { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.NodeIDs) > 0 { + for _, s := range m.NodeIDs { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.DesiredStates) > 0 { + for _, e := range m.DesiredStates { + n += 1 + sovControl(uint64(e)) + } + } + if len(m.NamePrefixes) > 0 { + for _, s := range m.NamePrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if m.UpToDate { + n += 2 + } + if len(m.Runtimes) > 0 { + for _, s := range m.Runtimes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *ListTasksResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Tasks) > 0 { + for _, e := range m.Tasks { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *CreateServiceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Spec != nil { + l = m.Spec.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *CreateServiceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Service != nil { + l = m.Service.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetServiceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ServiceID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.InsertDefaults { + n += 2 + } + return n +} + +func (m *GetServiceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Service != nil { + l = m.Service.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *UpdateServiceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ServiceID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.ServiceVersion != nil { + l = m.ServiceVersion.Size() + n += 1 + l + sovControl(uint64(l)) + } + if m.Spec != nil { + l = m.Spec.Size() + n += 1 + l + sovControl(uint64(l)) + } + if m.Rollback != 0 { + n += 1 + sovControl(uint64(m.Rollback)) + } + return n +} + +func (m *UpdateServiceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Service != nil { + l = m.Service.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveServiceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ServiceID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveServiceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *ListServicesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Filters != nil { + l = m.Filters.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListServicesRequest_Filters) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Names) > 0 { + for _, s := range m.Names { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.IDPrefixes) > 0 { + for _, s := range m.IDPrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + if len(m.NamePrefixes) > 0 { + for _, s := range m.NamePrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Runtimes) > 0 { + for _, s := range m.Runtimes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *ListServicesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Services) > 0 { + for _, e := range m.Services { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *ListServiceStatusesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Services) > 0 { + for _, s := range m.Services { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *ListServiceStatusesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Statuses) > 0 { + for _, e := range m.Statuses { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *ListServiceStatusesResponse_ServiceStatus) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ServiceID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.DesiredTasks != 0 { + n += 1 + sovControl(uint64(m.DesiredTasks)) + } + if m.RunningTasks != 0 { + n += 1 + sovControl(uint64(m.RunningTasks)) + } + if m.CompletedTasks != 0 { + n += 1 + sovControl(uint64(m.CompletedTasks)) + } + return n +} + +func (m *CreateNetworkRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Spec != nil { + l = m.Spec.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *CreateNetworkResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Network != nil { + l = m.Network.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetNetworkRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + l = len(m.NetworkID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetNetworkResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Network != nil { + l = m.Network.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveNetworkRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + l = len(m.NetworkID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveNetworkResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *ListNetworksRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Filters != nil { + l = m.Filters.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListNetworksRequest_Filters) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Names) > 0 { + for _, s := range m.Names { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.IDPrefixes) > 0 { + for _, s := range m.IDPrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + if len(m.NamePrefixes) > 0 { + for _, s := range m.NamePrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *ListNetworksResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Networks) > 0 { + for _, e := range m.Networks { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *GetClusterRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClusterID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetClusterResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Cluster != nil { + l = m.Cluster.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListClustersRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Filters != nil { + l = m.Filters.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListClustersRequest_Filters) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Names) > 0 { + for _, s := range m.Names { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.IDPrefixes) > 0 { + for _, s := range m.IDPrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + if len(m.NamePrefixes) > 0 { + for _, s := range m.NamePrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *ListClustersResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Clusters) > 0 { + for _, e := range m.Clusters { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *KeyRotation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.WorkerJoinToken { + n += 2 + } + if m.ManagerJoinToken { + n += 2 + } + if m.ManagerUnlockKey { + n += 2 + } + return n +} + +func (m *UpdateClusterRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ClusterID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.ClusterVersion != nil { + l = m.ClusterVersion.Size() + n += 1 + l + sovControl(uint64(l)) + } + if m.Spec != nil { + l = m.Spec.Size() + n += 1 + l + sovControl(uint64(l)) + } + l = m.Rotation.Size() + n += 1 + l + sovControl(uint64(l)) + return n +} + +func (m *UpdateClusterResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Cluster != nil { + l = m.Cluster.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetSecretRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SecretID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetSecretResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Secret != nil { + l = m.Secret.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *UpdateSecretRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SecretID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.SecretVersion != nil { + l = m.SecretVersion.Size() + n += 1 + l + sovControl(uint64(l)) + } + if m.Spec != nil { + l = m.Spec.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *UpdateSecretResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Secret != nil { + l = m.Secret.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListSecretsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Filters != nil { + l = m.Filters.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListSecretsRequest_Filters) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Names) > 0 { + for _, s := range m.Names { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.IDPrefixes) > 0 { + for _, s := range m.IDPrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + if len(m.NamePrefixes) > 0 { + for _, s := range m.NamePrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *ListSecretsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Secrets) > 0 { + for _, e := range m.Secrets { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *CreateSecretRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Spec != nil { + l = m.Spec.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *CreateSecretResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Secret != nil { + l = m.Secret.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveSecretRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SecretID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveSecretResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *GetConfigRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConfigID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetConfigResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Config != nil { + l = m.Config.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *UpdateConfigRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConfigID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.ConfigVersion != nil { + l = m.ConfigVersion.Size() + n += 1 + l + sovControl(uint64(l)) + } + if m.Spec != nil { + l = m.Spec.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *UpdateConfigResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Config != nil { + l = m.Config.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListConfigsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Filters != nil { + l = m.Filters.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListConfigsRequest_Filters) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Names) > 0 { + for _, s := range m.Names { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.IDPrefixes) > 0 { + for _, s := range m.IDPrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + if len(m.NamePrefixes) > 0 { + for _, s := range m.NamePrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *ListConfigsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Configs) > 0 { + for _, e := range m.Configs { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func (m *CreateConfigRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Spec != nil { + l = m.Spec.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *CreateConfigResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Config != nil { + l = m.Config.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveConfigRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConfigID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveConfigResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *CreateExtensionRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Annotations != nil { + l = m.Annotations.Size() + n += 1 + l + sovControl(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *CreateExtensionResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Extension != nil { + l = m.Extension.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveExtensionRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ExtensionID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveExtensionResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *GetExtensionRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ExtensionID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetExtensionResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Extension != nil { + l = m.Extension.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *CreateResourceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Annotations != nil { + l = m.Annotations.Size() + n += 1 + l + sovControl(uint64(l)) + } + l = len(m.Kind) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.Payload != nil { + l = m.Payload.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *CreateResourceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Resource != nil { + l = m.Resource.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveResourceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ResourceID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *RemoveResourceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *UpdateResourceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ResourceID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + if m.ResourceVersion != nil { + l = m.ResourceVersion.Size() + n += 1 + l + sovControl(uint64(l)) + } + if m.Annotations != nil { + l = m.Annotations.Size() + n += 1 + l + sovControl(uint64(l)) + } + if m.Payload != nil { + l = m.Payload.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *UpdateResourceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Resource != nil { + l = m.Resource.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetResourceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ResourceID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *GetResourceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Resource != nil { + l = m.Resource.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListResourcesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Filters != nil { + l = m.Filters.Size() + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListResourcesRequest_Filters) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Names) > 0 { + for _, s := range m.Names { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.IDPrefixes) > 0 { + for _, s := range m.IDPrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + if len(m.Labels) > 0 { + for k, v := range m.Labels { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } + if len(m.NamePrefixes) > 0 { + for _, s := range m.NamePrefixes { + l = len(s) + n += 1 + l + sovControl(uint64(l)) + } + } + l = len(m.Kind) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } + return n +} + +func (m *ListResourcesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Resources) > 0 { + for _, e := range m.Resources { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } + return n +} + +func sovControl(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozControl(x uint64) (n int) { + return sovControl(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *GetNodeRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetNodeRequest{`, + `NodeID:` + fmt.Sprintf("%v", this.NodeID) + `,`, + `}`, + }, "") + return s +} +func (this *GetNodeResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetNodeResponse{`, + `Node:` + strings.Replace(fmt.Sprintf("%v", this.Node), "Node", "Node", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListNodesRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListNodesRequest{`, + `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListNodesRequest_Filters", "ListNodesRequest_Filters", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListNodesRequest_Filters) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + keysForNodeLabels := make([]string, 0, len(this.NodeLabels)) + for k, _ := range this.NodeLabels { + keysForNodeLabels = append(keysForNodeLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForNodeLabels) + mapStringForNodeLabels := "map[string]string{" + for _, k := range keysForNodeLabels { + mapStringForNodeLabels += fmt.Sprintf("%v: %v,", k, this.NodeLabels[k]) + } + mapStringForNodeLabels += "}" + s := strings.Join([]string{`&ListNodesRequest_Filters{`, + `Names:` + fmt.Sprintf("%v", this.Names) + `,`, + `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, + `Labels:` + mapStringForLabels + `,`, + `Memberships:` + fmt.Sprintf("%v", this.Memberships) + `,`, + `Roles:` + fmt.Sprintf("%v", this.Roles) + `,`, + `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, + `NodeLabels:` + mapStringForNodeLabels + `,`, + `}`, + }, "") + return s +} +func (this *ListNodesResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForNodes := "[]*Node{" + for _, f := range this.Nodes { + repeatedStringForNodes += strings.Replace(fmt.Sprintf("%v", f), "Node", "Node", 1) + "," + } + repeatedStringForNodes += "}" + s := strings.Join([]string{`&ListNodesResponse{`, + `Nodes:` + repeatedStringForNodes + `,`, + `}`, + }, "") + return s +} +func (this *UpdateNodeRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateNodeRequest{`, + `NodeID:` + fmt.Sprintf("%v", this.NodeID) + `,`, + `NodeVersion:` + strings.Replace(fmt.Sprintf("%v", this.NodeVersion), "Version", "Version", 1) + `,`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "NodeSpec", "NodeSpec", 1) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateNodeResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateNodeResponse{`, + `Node:` + strings.Replace(fmt.Sprintf("%v", this.Node), "Node", "Node", 1) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveNodeRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveNodeRequest{`, + `NodeID:` + fmt.Sprintf("%v", this.NodeID) + `,`, + `Force:` + fmt.Sprintf("%v", this.Force) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveNodeResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveNodeResponse{`, + `}`, + }, "") + return s +} +func (this *GetTaskRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetTaskRequest{`, + `TaskID:` + fmt.Sprintf("%v", this.TaskID) + `,`, + `}`, + }, "") + return s +} +func (this *GetTaskResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetTaskResponse{`, + `Task:` + strings.Replace(fmt.Sprintf("%v", this.Task), "Task", "Task", 1) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveTaskRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveTaskRequest{`, + `TaskID:` + fmt.Sprintf("%v", this.TaskID) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveTaskResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveTaskResponse{`, + `}`, + }, "") + return s +} +func (this *ListTasksRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListTasksRequest{`, + `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListTasksRequest_Filters", "ListTasksRequest_Filters", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListTasksRequest_Filters) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&ListTasksRequest_Filters{`, + `Names:` + fmt.Sprintf("%v", this.Names) + `,`, + `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, + `Labels:` + mapStringForLabels + `,`, + `ServiceIDs:` + fmt.Sprintf("%v", this.ServiceIDs) + `,`, + `NodeIDs:` + fmt.Sprintf("%v", this.NodeIDs) + `,`, + `DesiredStates:` + fmt.Sprintf("%v", this.DesiredStates) + `,`, + `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, + `UpToDate:` + fmt.Sprintf("%v", this.UpToDate) + `,`, + `Runtimes:` + fmt.Sprintf("%v", this.Runtimes) + `,`, + `}`, + }, "") + return s +} +func (this *ListTasksResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForTasks := "[]*Task{" + for _, f := range this.Tasks { + repeatedStringForTasks += strings.Replace(fmt.Sprintf("%v", f), "Task", "Task", 1) + "," + } + repeatedStringForTasks += "}" + s := strings.Join([]string{`&ListTasksResponse{`, + `Tasks:` + repeatedStringForTasks + `,`, + `}`, + }, "") + return s +} +func (this *CreateServiceRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateServiceRequest{`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "ServiceSpec", "ServiceSpec", 1) + `,`, + `}`, + }, "") + return s +} +func (this *CreateServiceResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateServiceResponse{`, + `Service:` + strings.Replace(fmt.Sprintf("%v", this.Service), "Service", "Service", 1) + `,`, + `}`, + }, "") + return s +} +func (this *GetServiceRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetServiceRequest{`, + `ServiceID:` + fmt.Sprintf("%v", this.ServiceID) + `,`, + `InsertDefaults:` + fmt.Sprintf("%v", this.InsertDefaults) + `,`, + `}`, + }, "") + return s +} +func (this *GetServiceResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetServiceResponse{`, + `Service:` + strings.Replace(fmt.Sprintf("%v", this.Service), "Service", "Service", 1) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateServiceRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateServiceRequest{`, + `ServiceID:` + fmt.Sprintf("%v", this.ServiceID) + `,`, + `ServiceVersion:` + strings.Replace(fmt.Sprintf("%v", this.ServiceVersion), "Version", "Version", 1) + `,`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "ServiceSpec", "ServiceSpec", 1) + `,`, + `Rollback:` + fmt.Sprintf("%v", this.Rollback) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateServiceResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateServiceResponse{`, + `Service:` + strings.Replace(fmt.Sprintf("%v", this.Service), "Service", "Service", 1) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveServiceRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveServiceRequest{`, + `ServiceID:` + fmt.Sprintf("%v", this.ServiceID) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveServiceResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveServiceResponse{`, + `}`, + }, "") + return s +} +func (this *ListServicesRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListServicesRequest{`, + `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListServicesRequest_Filters", "ListServicesRequest_Filters", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListServicesRequest_Filters) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&ListServicesRequest_Filters{`, + `Names:` + fmt.Sprintf("%v", this.Names) + `,`, + `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, + `Labels:` + mapStringForLabels + `,`, + `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, + `Runtimes:` + fmt.Sprintf("%v", this.Runtimes) + `,`, + `}`, + }, "") + return s +} +func (this *ListServicesResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForServices := "[]*Service{" + for _, f := range this.Services { + repeatedStringForServices += strings.Replace(fmt.Sprintf("%v", f), "Service", "Service", 1) + "," + } + repeatedStringForServices += "}" + s := strings.Join([]string{`&ListServicesResponse{`, + `Services:` + repeatedStringForServices + `,`, + `}`, + }, "") + return s +} +func (this *ListServiceStatusesRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListServiceStatusesRequest{`, + `Services:` + fmt.Sprintf("%v", this.Services) + `,`, + `}`, + }, "") + return s +} +func (this *ListServiceStatusesResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForStatuses := "[]*ListServiceStatusesResponse_ServiceStatus{" + for _, f := range this.Statuses { + repeatedStringForStatuses += strings.Replace(fmt.Sprintf("%v", f), "ListServiceStatusesResponse_ServiceStatus", "ListServiceStatusesResponse_ServiceStatus", 1) + "," + } + repeatedStringForStatuses += "}" + s := strings.Join([]string{`&ListServiceStatusesResponse{`, + `Statuses:` + repeatedStringForStatuses + `,`, + `}`, + }, "") + return s +} +func (this *ListServiceStatusesResponse_ServiceStatus) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListServiceStatusesResponse_ServiceStatus{`, + `ServiceID:` + fmt.Sprintf("%v", this.ServiceID) + `,`, + `DesiredTasks:` + fmt.Sprintf("%v", this.DesiredTasks) + `,`, + `RunningTasks:` + fmt.Sprintf("%v", this.RunningTasks) + `,`, + `CompletedTasks:` + fmt.Sprintf("%v", this.CompletedTasks) + `,`, + `}`, + }, "") + return s +} +func (this *CreateNetworkRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateNetworkRequest{`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "NetworkSpec", "NetworkSpec", 1) + `,`, + `}`, + }, "") + return s +} +func (this *CreateNetworkResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateNetworkResponse{`, + `Network:` + strings.Replace(fmt.Sprintf("%v", this.Network), "Network", "Network", 1) + `,`, + `}`, + }, "") + return s +} +func (this *GetNetworkRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetNetworkRequest{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `NetworkID:` + fmt.Sprintf("%v", this.NetworkID) + `,`, + `}`, + }, "") + return s +} +func (this *GetNetworkResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetNetworkResponse{`, + `Network:` + strings.Replace(fmt.Sprintf("%v", this.Network), "Network", "Network", 1) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveNetworkRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveNetworkRequest{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `NetworkID:` + fmt.Sprintf("%v", this.NetworkID) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveNetworkResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveNetworkResponse{`, + `}`, + }, "") + return s +} +func (this *ListNetworksRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListNetworksRequest{`, + `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListNetworksRequest_Filters", "ListNetworksRequest_Filters", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListNetworksRequest_Filters) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&ListNetworksRequest_Filters{`, + `Names:` + fmt.Sprintf("%v", this.Names) + `,`, + `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, + `Labels:` + mapStringForLabels + `,`, + `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, + `}`, + }, "") + return s +} +func (this *ListNetworksResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForNetworks := "[]*Network{" + for _, f := range this.Networks { + repeatedStringForNetworks += strings.Replace(fmt.Sprintf("%v", f), "Network", "Network", 1) + "," + } + repeatedStringForNetworks += "}" + s := strings.Join([]string{`&ListNetworksResponse{`, + `Networks:` + repeatedStringForNetworks + `,`, + `}`, + }, "") + return s +} +func (this *GetClusterRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetClusterRequest{`, + `ClusterID:` + fmt.Sprintf("%v", this.ClusterID) + `,`, + `}`, + }, "") + return s +} +func (this *GetClusterResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetClusterResponse{`, + `Cluster:` + strings.Replace(fmt.Sprintf("%v", this.Cluster), "Cluster", "Cluster", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListClustersRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListClustersRequest{`, + `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListClustersRequest_Filters", "ListClustersRequest_Filters", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListClustersRequest_Filters) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&ListClustersRequest_Filters{`, + `Names:` + fmt.Sprintf("%v", this.Names) + `,`, + `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, + `Labels:` + mapStringForLabels + `,`, + `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, + `}`, + }, "") + return s +} +func (this *ListClustersResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForClusters := "[]*Cluster{" + for _, f := range this.Clusters { + repeatedStringForClusters += strings.Replace(fmt.Sprintf("%v", f), "Cluster", "Cluster", 1) + "," + } + repeatedStringForClusters += "}" + s := strings.Join([]string{`&ListClustersResponse{`, + `Clusters:` + repeatedStringForClusters + `,`, + `}`, + }, "") + return s +} +func (this *KeyRotation) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&KeyRotation{`, + `WorkerJoinToken:` + fmt.Sprintf("%v", this.WorkerJoinToken) + `,`, + `ManagerJoinToken:` + fmt.Sprintf("%v", this.ManagerJoinToken) + `,`, + `ManagerUnlockKey:` + fmt.Sprintf("%v", this.ManagerUnlockKey) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateClusterRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateClusterRequest{`, + `ClusterID:` + fmt.Sprintf("%v", this.ClusterID) + `,`, + `ClusterVersion:` + strings.Replace(fmt.Sprintf("%v", this.ClusterVersion), "Version", "Version", 1) + `,`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "ClusterSpec", "ClusterSpec", 1) + `,`, + `Rotation:` + strings.Replace(strings.Replace(this.Rotation.String(), "KeyRotation", "KeyRotation", 1), `&`, ``, 1) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateClusterResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateClusterResponse{`, + `Cluster:` + strings.Replace(fmt.Sprintf("%v", this.Cluster), "Cluster", "Cluster", 1) + `,`, + `}`, + }, "") + return s +} +func (this *GetSecretRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetSecretRequest{`, + `SecretID:` + fmt.Sprintf("%v", this.SecretID) + `,`, + `}`, + }, "") + return s +} +func (this *GetSecretResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetSecretResponse{`, + `Secret:` + strings.Replace(fmt.Sprintf("%v", this.Secret), "Secret", "Secret", 1) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateSecretRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateSecretRequest{`, + `SecretID:` + fmt.Sprintf("%v", this.SecretID) + `,`, + `SecretVersion:` + strings.Replace(fmt.Sprintf("%v", this.SecretVersion), "Version", "Version", 1) + `,`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "SecretSpec", "SecretSpec", 1) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateSecretResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateSecretResponse{`, + `Secret:` + strings.Replace(fmt.Sprintf("%v", this.Secret), "Secret", "Secret", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListSecretsRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListSecretsRequest{`, + `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListSecretsRequest_Filters", "ListSecretsRequest_Filters", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListSecretsRequest_Filters) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&ListSecretsRequest_Filters{`, + `Names:` + fmt.Sprintf("%v", this.Names) + `,`, + `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, + `Labels:` + mapStringForLabels + `,`, + `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, + `}`, + }, "") + return s +} +func (this *ListSecretsResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForSecrets := "[]*Secret{" + for _, f := range this.Secrets { + repeatedStringForSecrets += strings.Replace(fmt.Sprintf("%v", f), "Secret", "Secret", 1) + "," + } + repeatedStringForSecrets += "}" + s := strings.Join([]string{`&ListSecretsResponse{`, + `Secrets:` + repeatedStringForSecrets + `,`, + `}`, + }, "") + return s +} +func (this *CreateSecretRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateSecretRequest{`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "SecretSpec", "SecretSpec", 1) + `,`, + `}`, + }, "") + return s +} +func (this *CreateSecretResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateSecretResponse{`, + `Secret:` + strings.Replace(fmt.Sprintf("%v", this.Secret), "Secret", "Secret", 1) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveSecretRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveSecretRequest{`, + `SecretID:` + fmt.Sprintf("%v", this.SecretID) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveSecretResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveSecretResponse{`, + `}`, + }, "") + return s +} +func (this *GetConfigRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetConfigRequest{`, + `ConfigID:` + fmt.Sprintf("%v", this.ConfigID) + `,`, + `}`, + }, "") + return s +} +func (this *GetConfigResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetConfigResponse{`, + `Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "Config", "Config", 1) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateConfigRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateConfigRequest{`, + `ConfigID:` + fmt.Sprintf("%v", this.ConfigID) + `,`, + `ConfigVersion:` + strings.Replace(fmt.Sprintf("%v", this.ConfigVersion), "Version", "Version", 1) + `,`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "ConfigSpec", "ConfigSpec", 1) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateConfigResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateConfigResponse{`, + `Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "Config", "Config", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListConfigsRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListConfigsRequest{`, + `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListConfigsRequest_Filters", "ListConfigsRequest_Filters", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListConfigsRequest_Filters) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&ListConfigsRequest_Filters{`, + `Names:` + fmt.Sprintf("%v", this.Names) + `,`, + `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, + `Labels:` + mapStringForLabels + `,`, + `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, + `}`, + }, "") + return s +} +func (this *ListConfigsResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForConfigs := "[]*Config{" + for _, f := range this.Configs { + repeatedStringForConfigs += strings.Replace(fmt.Sprintf("%v", f), "Config", "Config", 1) + "," + } + repeatedStringForConfigs += "}" + s := strings.Join([]string{`&ListConfigsResponse{`, + `Configs:` + repeatedStringForConfigs + `,`, + `}`, + }, "") + return s +} +func (this *CreateConfigRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateConfigRequest{`, + `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "ConfigSpec", "ConfigSpec", 1) + `,`, + `}`, + }, "") + return s +} +func (this *CreateConfigResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateConfigResponse{`, + `Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "Config", "Config", 1) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveConfigRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveConfigRequest{`, + `ConfigID:` + fmt.Sprintf("%v", this.ConfigID) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveConfigResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveConfigResponse{`, + `}`, + }, "") + return s +} +func (this *CreateExtensionRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateExtensionRequest{`, + `Annotations:` + strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1) + `,`, + `Description:` + fmt.Sprintf("%v", this.Description) + `,`, + `}`, + }, "") + return s +} +func (this *CreateExtensionResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateExtensionResponse{`, + `Extension:` + strings.Replace(fmt.Sprintf("%v", this.Extension), "Extension", "Extension", 1) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveExtensionRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveExtensionRequest{`, + `ExtensionID:` + fmt.Sprintf("%v", this.ExtensionID) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveExtensionResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveExtensionResponse{`, + `}`, + }, "") + return s +} +func (this *GetExtensionRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetExtensionRequest{`, + `ExtensionID:` + fmt.Sprintf("%v", this.ExtensionID) + `,`, + `}`, + }, "") + return s +} +func (this *GetExtensionResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetExtensionResponse{`, + `Extension:` + strings.Replace(fmt.Sprintf("%v", this.Extension), "Extension", "Extension", 1) + `,`, + `}`, + }, "") + return s +} +func (this *CreateResourceRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateResourceRequest{`, + `Annotations:` + strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1) + `,`, + `Kind:` + fmt.Sprintf("%v", this.Kind) + `,`, + `Payload:` + strings.Replace(fmt.Sprintf("%v", this.Payload), "Any", "types.Any", 1) + `,`, + `}`, + }, "") + return s +} +func (this *CreateResourceResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&CreateResourceResponse{`, + `Resource:` + strings.Replace(fmt.Sprintf("%v", this.Resource), "Resource", "Resource", 1) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveResourceRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveResourceRequest{`, + `ResourceID:` + fmt.Sprintf("%v", this.ResourceID) + `,`, + `}`, + }, "") + return s +} +func (this *RemoveResourceResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RemoveResourceResponse{`, + `}`, + }, "") + return s +} +func (this *UpdateResourceRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateResourceRequest{`, + `ResourceID:` + fmt.Sprintf("%v", this.ResourceID) + `,`, + `ResourceVersion:` + strings.Replace(fmt.Sprintf("%v", this.ResourceVersion), "Version", "Version", 1) + `,`, + `Annotations:` + strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1) + `,`, + `Payload:` + strings.Replace(fmt.Sprintf("%v", this.Payload), "Any", "types.Any", 1) + `,`, + `}`, + }, "") + return s +} +func (this *UpdateResourceResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&UpdateResourceResponse{`, + `Resource:` + strings.Replace(fmt.Sprintf("%v", this.Resource), "Resource", "Resource", 1) + `,`, + `}`, + }, "") + return s +} +func (this *GetResourceRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetResourceRequest{`, + `ResourceID:` + fmt.Sprintf("%v", this.ResourceID) + `,`, + `}`, + }, "") + return s +} +func (this *GetResourceResponse) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GetResourceResponse{`, + `Resource:` + strings.Replace(fmt.Sprintf("%v", this.Resource), "Resource", "Resource", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListResourcesRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListResourcesRequest{`, + `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "ListResourcesRequest_Filters", "ListResourcesRequest_Filters", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListResourcesRequest_Filters) String() string { + if this == nil { + return "nil" + } + keysForLabels := make([]string, 0, len(this.Labels)) + for k, _ := range this.Labels { + keysForLabels = append(keysForLabels, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) + mapStringForLabels := "map[string]string{" + for _, k := range keysForLabels { + mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) + } + mapStringForLabels += "}" + s := strings.Join([]string{`&ListResourcesRequest_Filters{`, + `Names:` + fmt.Sprintf("%v", this.Names) + `,`, + `IDPrefixes:` + fmt.Sprintf("%v", this.IDPrefixes) + `,`, + `Labels:` + mapStringForLabels + `,`, + `NamePrefixes:` + fmt.Sprintf("%v", this.NamePrefixes) + `,`, + `Kind:` + fmt.Sprintf("%v", this.Kind) + `,`, + `}`, + }, "") + return s +} +func (this *ListResourcesResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForResources := "[]*Resource{" + for _, f := range this.Resources { + repeatedStringForResources += strings.Replace(fmt.Sprintf("%v", f), "Resource", "Resource", 1) + "," + } + repeatedStringForResources += "}" + s := strings.Join([]string{`&ListResourcesResponse{`, + `Resources:` + repeatedStringForResources + `,`, + `}`, + }, "") + return s +} +func valueToStringControl(v interface{}) string { + rv := reflect.ValueOf(v) if rv.IsNil() { return "nil" } - pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("*%v", pv) + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *GetNodeRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetNodeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetNodeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NodeID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetNodeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetNodeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetNodeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Node", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Node == nil { + m.Node = &Node{} + } + if err := m.Node.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListNodesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListNodesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListNodesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Filters == nil { + m.Filters = &ListNodesRequest_Filters{} + } + if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Filters: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Filters: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Names", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Names = append(m.Names, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IDPrefixes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IDPrefixes = append(m.IDPrefixes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType == 0 { + var v NodeSpec_Membership + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= NodeSpec_Membership(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Memberships = append(m.Memberships, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + if elementCount != 0 && len(m.Memberships) == 0 { + m.Memberships = make([]NodeSpec_Membership, 0, elementCount) + } + for iNdEx < postIndex { + var v NodeSpec_Membership + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= NodeSpec_Membership(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Memberships = append(m.Memberships, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field Memberships", wireType) + } + case 5: + if wireType == 0 { + var v NodeRole + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= NodeRole(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Roles = append(m.Roles, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + if elementCount != 0 && len(m.Roles) == 0 { + m.Roles = make([]NodeRole, 0, elementCount) + } + for iNdEx < postIndex { + var v NodeRole + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= NodeRole(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Roles = append(m.Roles, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType) + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeLabels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NodeLabels == nil { + m.NodeLabels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.NodeLabels[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListNodesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListNodesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListNodesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nodes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Nodes = append(m.Nodes, &Node{}) + if err := m.Nodes[len(m.Nodes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateNodeRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateNodeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateNodeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NodeID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeVersion", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.NodeVersion == nil { + m.NodeVersion = &Version{} + } + if err := m.NodeVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Spec == nil { + m.Spec = &NodeSpec{} + } + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateNodeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateNodeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateNodeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Node", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Node == nil { + m.Node = &Node{} + } + if err := m.Node.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RemoveNodeRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RemoveNodeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RemoveNodeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NodeID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Force", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Force = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RemoveNodeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RemoveNodeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RemoveNodeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetTaskRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetTaskRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TaskID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetTaskResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetTaskResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Task", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Task == nil { + m.Task = &Task{} + } + if err := m.Task.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RemoveTaskRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RemoveTaskRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RemoveTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TaskID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TaskID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RemoveTaskResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RemoveTaskResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RemoveTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListTasksRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListTasksRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Filters == nil { + m.Filters = &ListTasksRequest_Filters{} + } + if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Filters: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Filters: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Names", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Names = append(m.Names, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IDPrefixes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IDPrefixes = append(m.IDPrefixes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ServiceIDs", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ServiceIDs = append(m.ServiceIDs, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeIDs", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NodeIDs = append(m.NodeIDs, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 6: + if wireType == 0 { + var v TaskState + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= TaskState(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DesiredStates = append(m.DesiredStates, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + if elementCount != 0 && len(m.DesiredStates) == 0 { + m.DesiredStates = make([]TaskState, 0, elementCount) + } + for iNdEx < postIndex { + var v TaskState + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= TaskState(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DesiredStates = append(m.DesiredStates, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field DesiredStates", wireType) + } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UpToDate", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.UpToDate = bool(v != 0) + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Runtimes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Runtimes = append(m.Runtimes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListTasksResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListTasksResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListTasksResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tasks = append(m.Tasks, &Task{}) + if err := m.Tasks[len(m.Tasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CreateServiceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CreateServiceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Spec == nil { + m.Spec = &ServiceSpec{} + } + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CreateServiceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CreateServiceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateServiceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Service == nil { + m.Service = &Service{} + } + if err := m.Service.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetServiceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetServiceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ServiceID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ServiceID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InsertDefaults", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.InsertDefaults = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetServiceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetServiceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetServiceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Service == nil { + m.Service = &Service{} + } + if err := m.Service.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateServiceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ServiceID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ServiceID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ServiceVersion", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ServiceVersion == nil { + m.ServiceVersion = &Version{} + } + if err := m.ServiceVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Spec == nil { + m.Spec = &ServiceSpec{} + } + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Rollback", wireType) + } + m.Rollback = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Rollback |= UpdateServiceRequest_Rollback(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil } -func (m *GetNodeRequest) Unmarshal(dAtA []byte) error { +func (m *UpdateServiceResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -8680,7 +17364,7 @@ func (m *GetNodeRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8688,17 +17372,17 @@ func (m *GetNodeRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetNodeRequest: wiretype end group for non-group") + return fmt.Errorf("proto: UpdateServiceResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetNodeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: UpdateServiceResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -8708,20 +17392,27 @@ func (m *GetNodeRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.NodeID = string(dAtA[iNdEx:postIndex]) + if m.Service == nil { + m.Service = &Service{} + } + if err := m.Service.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -8729,7 +17420,7 @@ func (m *GetNodeRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -8744,7 +17435,7 @@ func (m *GetNodeRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *GetNodeResponse) Unmarshal(dAtA []byte) error { +func (m *RemoveServiceRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -8759,7 +17450,7 @@ func (m *GetNodeResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8767,17 +17458,17 @@ func (m *GetNodeResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetNodeResponse: wiretype end group for non-group") + return fmt.Errorf("proto: RemoveServiceRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetNodeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RemoveServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Node", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ServiceID", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -8787,32 +17478,81 @@ func (m *GetNodeResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Node == nil { - m.Node = &Node{} - } - if err := m.Node.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.ServiceID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { return err } - iNdEx = postIndex + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RemoveServiceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RemoveServiceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RemoveServiceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -8827,7 +17567,7 @@ func (m *GetNodeResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListNodesRequest) Unmarshal(dAtA []byte) error { +func (m *ListServicesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -8842,7 +17582,7 @@ func (m *ListNodesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8850,10 +17590,10 @@ func (m *ListNodesRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListNodesRequest: wiretype end group for non-group") + return fmt.Errorf("proto: ListServicesRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListNodesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListServicesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -8870,7 +17610,7 @@ func (m *ListNodesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -8879,11 +17619,14 @@ func (m *ListNodesRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Filters == nil { - m.Filters = &ListNodesRequest_Filters{} + m.Filters = &ListServicesRequest_Filters{} } if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -8895,7 +17638,7 @@ func (m *ListNodesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -8910,7 +17653,7 @@ func (m *ListNodesRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { +func (m *ListServicesRequest_Filters) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -8925,7 +17668,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8953,7 +17696,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8963,6 +17706,9 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8982,7 +17728,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8992,6 +17738,9 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9011,7 +17760,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9020,6 +17769,9 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9040,7 +17792,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9057,7 +17809,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9067,6 +17819,9 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthControl + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -9083,7 +17838,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9093,6 +17848,9 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthControl + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -9104,7 +17862,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > postIndex { @@ -9116,132 +17874,40 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { m.Labels[mapkey] = mapvalue iNdEx = postIndex case 4: - if wireType == 0 { - var v NodeSpec_Membership - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (NodeSpec_Membership(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - m.Memberships = append(m.Memberships, v) - } else if wireType == 2 { - var packedLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - packedLen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if packedLen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + packedLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - for iNdEx < postIndex { - var v NodeSpec_Membership - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (NodeSpec_Membership(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - m.Memberships = append(m.Memberships, v) - } - } else { - return fmt.Errorf("proto: wrong wireType = %d for field Memberships", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) } - case 5: - if wireType == 0 { - var v NodeRole - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (NodeRole(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - m.Roles = append(m.Roles, v) - } else if wireType == 2 { - var packedLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - packedLen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if packedLen < 0 { - return ErrInvalidLengthControl + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl } - postIndex := iNdEx + packedLen - if postIndex > l { + if iNdEx >= l { return io.ErrUnexpectedEOF } - for iNdEx < postIndex { - var v NodeRole - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (NodeRole(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - m.Roles = append(m.Roles, v) + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break } - } else { - return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType) } - case 6: + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Runtimes", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -9253,7 +17919,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9263,14 +17929,67 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) + m.Runtimes = append(m.Runtimes, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 7: + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListServicesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListServicesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListServicesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NodeLabels", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Services", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -9282,7 +18001,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9291,100 +18010,98 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.NodeLabels == nil { - m.NodeLabels = make(map[string]string) + m.Services = append(m.Services, &Service{}) + if err := m.Services[len(m.Services)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - var mapkey string - var mapvalue string - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListServiceStatusesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListServiceStatusesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListServiceStatusesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Services", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthControl - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var stringLenmapvalue uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthControl - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue - } else { - iNdEx = entryPreIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break } } - m.NodeLabels[mapkey] = mapvalue + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Services = append(m.Services, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex @@ -9392,7 +18109,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -9407,7 +18124,7 @@ func (m *ListNodesRequest_Filters) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListNodesResponse) Unmarshal(dAtA []byte) error { +func (m *ListServiceStatusesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -9422,7 +18139,7 @@ func (m *ListNodesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9430,15 +18147,15 @@ func (m *ListNodesResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListNodesResponse: wiretype end group for non-group") + return fmt.Errorf("proto: ListServiceStatusesResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListNodesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListServiceStatusesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Nodes", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Statuses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -9450,7 +18167,7 @@ func (m *ListNodesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9459,11 +18176,14 @@ func (m *ListNodesResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Nodes = append(m.Nodes, &Node{}) - if err := m.Nodes[len(m.Nodes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Statuses = append(m.Statuses, &ListServiceStatusesResponse_ServiceStatus{}) + if err := m.Statuses[len(m.Statuses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -9473,7 +18193,7 @@ func (m *ListNodesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -9488,7 +18208,7 @@ func (m *ListNodesResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateNodeRequest) Unmarshal(dAtA []byte) error { +func (m *ListServiceStatusesResponse_ServiceStatus) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -9503,7 +18223,7 @@ func (m *UpdateNodeRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9511,15 +18231,15 @@ func (m *UpdateNodeRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateNodeRequest: wiretype end group for non-group") + return fmt.Errorf("proto: ServiceStatus: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateNodeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ServiceStatus: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ServiceID", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -9531,7 +18251,7 @@ func (m *UpdateNodeRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9541,16 +18261,38 @@ func (m *UpdateNodeRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.NodeID = string(dAtA[iNdEx:postIndex]) + m.ServiceID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NodeVersion", wireType) + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DesiredTasks", wireType) + } + m.DesiredTasks = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DesiredTasks |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RunningTasks", wireType) } - var msglen int + m.RunningTasks = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -9560,30 +18302,16 @@ func (m *UpdateNodeRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + m.RunningTasks |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.NodeVersion == nil { - m.NodeVersion = &Version{} - } - if err := m.NodeVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CompletedTasks", wireType) } - var msglen int + m.CompletedTasks = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -9593,32 +18321,18 @@ func (m *UpdateNodeRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + m.CompletedTasks |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Spec == nil { - m.Spec = &NodeSpec{} - } - if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -9633,7 +18347,7 @@ func (m *UpdateNodeRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateNodeResponse) Unmarshal(dAtA []byte) error { +func (m *CreateNetworkRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -9648,7 +18362,7 @@ func (m *UpdateNodeResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9656,15 +18370,15 @@ func (m *UpdateNodeResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateNodeResponse: wiretype end group for non-group") + return fmt.Errorf("proto: CreateNetworkRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateNodeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateNetworkRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Node", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -9676,7 +18390,7 @@ func (m *UpdateNodeResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9685,13 +18399,16 @@ func (m *UpdateNodeResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Node == nil { - m.Node = &Node{} + if m.Spec == nil { + m.Spec = &NetworkSpec{} } - if err := m.Node.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -9701,7 +18418,7 @@ func (m *UpdateNodeResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -9716,7 +18433,7 @@ func (m *UpdateNodeResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *RemoveNodeRequest) Unmarshal(dAtA []byte) error { +func (m *CreateNetworkResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -9731,7 +18448,7 @@ func (m *RemoveNodeRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9739,17 +18456,17 @@ func (m *RemoveNodeRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RemoveNodeRequest: wiretype end group for non-group") + return fmt.Errorf("proto: CreateNetworkResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveNodeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateNetworkResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Network", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -9759,48 +18476,35 @@ func (m *RemoveNodeRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.NodeID = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Force", wireType) + if m.Network == nil { + m.Network = &Network{} } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } + if err := m.Network.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - m.Force = bool(v != 0) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -9815,7 +18519,7 @@ func (m *RemoveNodeRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *RemoveNodeResponse) Unmarshal(dAtA []byte) error { +func (m *GetNetworkRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -9830,7 +18534,7 @@ func (m *RemoveNodeResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9838,65 +18542,47 @@ func (m *RemoveNodeResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RemoveNodeResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetNetworkRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveNodeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetNetworkRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } - if skippy < 0 { - return ErrInvalidLengthControl + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetTaskRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl } - if iNdEx >= l { + if postIndex > l { return io.ErrUnexpectedEOF } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetTaskRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field NetworkID", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -9908,7 +18594,7 @@ func (m *GetTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9918,10 +18604,13 @@ func (m *GetTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.TaskID = string(dAtA[iNdEx:postIndex]) + m.NetworkID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -9929,7 +18618,7 @@ func (m *GetTaskRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -9944,7 +18633,7 @@ func (m *GetTaskRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *GetTaskResponse) Unmarshal(dAtA []byte) error { +func (m *GetNetworkResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -9959,7 +18648,7 @@ func (m *GetTaskResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9967,15 +18656,15 @@ func (m *GetTaskResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetTaskResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetNetworkResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetNetworkResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Task", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Network", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -9987,7 +18676,7 @@ func (m *GetTaskResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9996,13 +18685,16 @@ func (m *GetTaskResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Task == nil { - m.Task = &Task{} + if m.Network == nil { + m.Network = &Network{} } - if err := m.Task.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Network.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -10012,7 +18704,7 @@ func (m *GetTaskResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -10027,7 +18719,7 @@ func (m *GetTaskResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *RemoveTaskRequest) Unmarshal(dAtA []byte) error { +func (m *RemoveNetworkRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -10042,7 +18734,7 @@ func (m *RemoveTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10050,15 +18742,15 @@ func (m *RemoveTaskRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RemoveTaskRequest: wiretype end group for non-group") + return fmt.Errorf("proto: RemoveNetworkRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveTaskRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RemoveNetworkRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TaskID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -10070,7 +18762,7 @@ func (m *RemoveTaskRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10080,10 +18772,45 @@ func (m *RemoveTaskRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.TaskID = string(dAtA[iNdEx:postIndex]) + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NetworkID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NetworkID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -10091,7 +18818,7 @@ func (m *RemoveTaskRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -10106,7 +18833,7 @@ func (m *RemoveTaskRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *RemoveTaskResponse) Unmarshal(dAtA []byte) error { +func (m *RemoveNetworkResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -10121,7 +18848,7 @@ func (m *RemoveTaskResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10129,10 +18856,10 @@ func (m *RemoveTaskResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RemoveTaskResponse: wiretype end group for non-group") + return fmt.Errorf("proto: RemoveNetworkResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveTaskResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RemoveNetworkResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -10141,7 +18868,7 @@ func (m *RemoveTaskResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -10156,7 +18883,7 @@ func (m *RemoveTaskResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { +func (m *ListNetworksRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -10171,7 +18898,7 @@ func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10179,10 +18906,10 @@ func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListTasksRequest: wiretype end group for non-group") + return fmt.Errorf("proto: ListNetworksRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListTasksRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListNetworksRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -10199,7 +18926,7 @@ func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10208,11 +18935,14 @@ func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Filters == nil { - m.Filters = &ListTasksRequest_Filters{} + m.Filters = &ListNetworksRequest_Filters{} } if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -10224,7 +18954,7 @@ func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -10239,7 +18969,7 @@ func (m *ListTasksRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { +func (m *ListNetworksRequest_Filters) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -10254,7 +18984,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10282,7 +19012,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10292,6 +19022,9 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10311,7 +19044,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10321,6 +19054,9 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10340,7 +19076,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10349,6 +19085,9 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10369,7 +19108,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10386,7 +19125,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10396,6 +19135,9 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthControl + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -10412,7 +19154,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10422,6 +19164,9 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthControl + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -10433,7 +19178,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > postIndex { @@ -10446,7 +19191,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ServiceIDs", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -10458,7 +19203,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10468,16 +19213,69 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.ServiceIDs = append(m.ServiceIDs, string(dAtA[iNdEx:postIndex])) + m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex - case 5: + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListNetworksResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListNetworksResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListNetworksResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NodeIDs", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Networks", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -10487,88 +19285,163 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.NodeIDs = append(m.NodeIDs, string(dAtA[iNdEx:postIndex])) + m.Networks = append(m.Networks, &Network{}) + if err := m.Networks[len(m.Networks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 6: - if wireType == 0 { - var v TaskState - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (TaskState(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - m.DesiredStates = append(m.DesiredStates, v) - } else if wireType == 2 { - var packedLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - packedLen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if packedLen < 0 { - return ErrInvalidLengthControl + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetClusterRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetClusterRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetClusterRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ClusterID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl } - postIndex := iNdEx + packedLen - if postIndex > l { + if iNdEx >= l { return io.ErrUnexpectedEOF } - for iNdEx < postIndex { - var v TaskState - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (TaskState(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - m.DesiredStates = append(m.DesiredStates, v) + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break } - } else { - return fmt.Errorf("proto: wrong wireType = %d for field DesiredStates", wireType) } - case 7: + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ClusterID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetClusterResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetClusterResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetClusterResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -10578,46 +19451,83 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) + if m.Cluster == nil { + m.Cluster = &Cluster{} + } + if err := m.Cluster.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 8: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field UpToDate", wireType) + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl } - m.UpToDate = bool(v != 0) - case 9: + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListClustersRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListClustersRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListClustersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Runtimes", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -10627,20 +19537,27 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.Runtimes = append(m.Runtimes, string(dAtA[iNdEx:postIndex])) + if m.Filters == nil { + m.Filters = &ListClustersRequest_Filters{} + } + if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -10648,7 +19565,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -10663,7 +19580,7 @@ func (m *ListTasksRequest_Filters) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListTasksResponse) Unmarshal(dAtA []byte) error { +func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -10678,7 +19595,7 @@ func (m *ListTasksResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10686,15 +19603,79 @@ func (m *ListTasksResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListTasksResponse: wiretype end group for non-group") + return fmt.Errorf("proto: Filters: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListTasksResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: Filters: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tasks", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Names", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Names = append(m.Names, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IDPrefixes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IDPrefixes = append(m.IDPrefixes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -10706,78 +19687,124 @@ func (m *ListTasksResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Tasks = append(m.Tasks, &Task{}) - if err := m.Tasks[len(m.Tasks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CreateServiceRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CreateServiceRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CreateServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -10787,24 +19814,23 @@ func (m *CreateServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Spec == nil { - m.Spec = &ServiceSpec{} - } - if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex @@ -10812,7 +19838,7 @@ func (m *CreateServiceRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -10827,7 +19853,7 @@ func (m *CreateServiceRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *CreateServiceResponse) Unmarshal(dAtA []byte) error { +func (m *ListClustersResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -10842,7 +19868,7 @@ func (m *CreateServiceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10850,15 +19876,15 @@ func (m *CreateServiceResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateServiceResponse: wiretype end group for non-group") + return fmt.Errorf("proto: ListClustersResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateServiceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListClustersResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Clusters", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -10870,7 +19896,7 @@ func (m *CreateServiceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10879,13 +19905,14 @@ func (m *CreateServiceResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Service == nil { - m.Service = &Service{} - } - if err := m.Service.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Clusters = append(m.Clusters, &Cluster{}) + if err := m.Clusters[len(m.Clusters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -10895,7 +19922,7 @@ func (m *CreateServiceResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -10910,7 +19937,7 @@ func (m *CreateServiceResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *GetServiceRequest) Unmarshal(dAtA []byte) error { +func (m *KeyRotation) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -10925,7 +19952,7 @@ func (m *GetServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10933,17 +19960,17 @@ func (m *GetServiceRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetServiceRequest: wiretype end group for non-group") + return fmt.Errorf("proto: KeyRotation: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: KeyRotation: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ServiceID", wireType) + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WorkerJoinToken", wireType) } - var stringLen uint64 + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -10953,24 +19980,15 @@ func (m *GetServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ServiceID = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex + m.WorkerJoinToken = bool(v != 0) case 2: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field InsertDefaults", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ManagerJoinToken", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -10982,67 +20000,17 @@ func (m *GetServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - m.InsertDefaults = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetServiceResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetServiceResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetServiceResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) + m.ManagerJoinToken = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ManagerUnlockKey", wireType) } - var msglen int + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -11052,32 +20020,19 @@ func (m *GetServiceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Service == nil { - m.Service = &Service{} - } - if err := m.Service.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex + m.ManagerUnlockKey = bool(v != 0) default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -11092,7 +20047,7 @@ func (m *GetServiceResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { +func (m *UpdateClusterRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -11107,7 +20062,7 @@ func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11115,15 +20070,15 @@ func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateServiceRequest: wiretype end group for non-group") + return fmt.Errorf("proto: UpdateClusterRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: UpdateClusterRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ServiceID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterID", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -11135,7 +20090,7 @@ func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11145,14 +20100,17 @@ func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.ServiceID = string(dAtA[iNdEx:postIndex]) + m.ClusterID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ServiceVersion", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClusterVersion", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -11164,7 +20122,7 @@ func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11173,13 +20131,16 @@ func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.ServiceVersion == nil { - m.ServiceVersion = &Version{} + if m.ClusterVersion == nil { + m.ClusterVersion = &Version{} } - if err := m.ServiceVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.ClusterVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -11197,7 +20158,7 @@ func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11206,21 +20167,24 @@ func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Spec == nil { - m.Spec = &ServiceSpec{} + m.Spec = &ClusterSpec{} } if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Rollback", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Rotation", wireType) } - m.Rollback = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -11230,18 +20194,32 @@ func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Rollback |= (UpdateServiceRequest_Rollback(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Rotation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -11256,7 +20234,7 @@ func (m *UpdateServiceRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateServiceResponse) Unmarshal(dAtA []byte) error { +func (m *UpdateClusterResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -11271,7 +20249,7 @@ func (m *UpdateServiceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11279,15 +20257,15 @@ func (m *UpdateServiceResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateServiceResponse: wiretype end group for non-group") + return fmt.Errorf("proto: UpdateClusterResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateServiceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: UpdateClusterResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -11299,7 +20277,7 @@ func (m *UpdateServiceResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11308,13 +20286,16 @@ func (m *UpdateServiceResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Service == nil { - m.Service = &Service{} + if m.Cluster == nil { + m.Cluster = &Cluster{} } - if err := m.Service.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Cluster.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -11324,7 +20305,7 @@ func (m *UpdateServiceResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -11339,7 +20320,7 @@ func (m *UpdateServiceResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *RemoveServiceRequest) Unmarshal(dAtA []byte) error { +func (m *GetSecretRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -11354,7 +20335,7 @@ func (m *RemoveServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11362,15 +20343,15 @@ func (m *RemoveServiceRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RemoveServiceRequest: wiretype end group for non-group") + return fmt.Errorf("proto: GetSecretRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveServiceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetSecretRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ServiceID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SecretID", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -11382,7 +20363,7 @@ func (m *RemoveServiceRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11392,68 +20373,21 @@ func (m *RemoveServiceRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ServiceID = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { + if postIndex < 0 { return ErrInvalidLengthControl } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *RemoveServiceResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { + if postIndex > l { return io.ErrUnexpectedEOF } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RemoveServiceResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveServiceResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { + m.SecretID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -11468,7 +20402,7 @@ func (m *RemoveServiceResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListServicesRequest) Unmarshal(dAtA []byte) error { +func (m *GetSecretResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -11483,7 +20417,7 @@ func (m *ListServicesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11491,15 +20425,15 @@ func (m *ListServicesRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListServicesRequest: wiretype end group for non-group") + return fmt.Errorf("proto: GetSecretResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListServicesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetSecretResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Secret", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -11511,7 +20445,7 @@ func (m *ListServicesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11520,13 +20454,16 @@ func (m *ListServicesRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Filters == nil { - m.Filters = &ListServicesRequest_Filters{} + if m.Secret == nil { + m.Secret = &Secret{} } - if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Secret.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -11536,7 +20473,7 @@ func (m *ListServicesRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -11551,7 +20488,7 @@ func (m *ListServicesRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListServicesRequest_Filters) Unmarshal(dAtA []byte) error { +func (m *UpdateSecretRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -11566,7 +20503,7 @@ func (m *ListServicesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11574,44 +20511,15 @@ func (m *ListServicesRequest_Filters) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: Filters: wiretype end group for non-group") + return fmt.Errorf("proto: UpdateSecretRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: Filters: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: UpdateSecretRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Names", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Names = append(m.Names, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field IDPrefixes", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SecretID", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -11623,7 +20531,7 @@ func (m *ListServicesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11633,134 +20541,19 @@ func (m *ListServicesRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.IDPrefixes = append(m.IDPrefixes, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Labels == nil { - m.Labels = make(map[string]string) - } - var mapkey string - var mapvalue string - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthControl - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var stringLenmapvalue uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthControl - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue - } else { - iNdEx = entryPreIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.Labels[mapkey] = mapvalue + m.SecretID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SecretVersion", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -11770,26 +20563,33 @@ func (m *ListServicesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) + if m.SecretVersion == nil { + m.SecretVersion = &Version{} + } + if err := m.SecretVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex - case 5: + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Runtimes", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -11799,20 +20599,27 @@ func (m *ListServicesRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.Runtimes = append(m.Runtimes, string(dAtA[iNdEx:postIndex])) + if m.Spec == nil { + m.Spec = &SecretSpec{} + } + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -11820,7 +20627,7 @@ func (m *ListServicesRequest_Filters) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -11835,7 +20642,7 @@ func (m *ListServicesRequest_Filters) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListServicesResponse) Unmarshal(dAtA []byte) error { +func (m *UpdateSecretResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -11850,7 +20657,7 @@ func (m *ListServicesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11858,15 +20665,15 @@ func (m *ListServicesResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListServicesResponse: wiretype end group for non-group") + return fmt.Errorf("proto: UpdateSecretResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListServicesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: UpdateSecretResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Services", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Secret", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -11878,7 +20685,7 @@ func (m *ListServicesResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11887,11 +20694,16 @@ func (m *ListServicesResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Services = append(m.Services, &Service{}) - if err := m.Services[len(m.Services)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if m.Secret == nil { + m.Secret = &Secret{} + } + if err := m.Secret.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -11901,7 +20713,7 @@ func (m *ListServicesResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -11916,7 +20728,7 @@ func (m *ListServicesResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *CreateNetworkRequest) Unmarshal(dAtA []byte) error { +func (m *ListSecretsRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -11931,7 +20743,7 @@ func (m *CreateNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11939,15 +20751,15 @@ func (m *CreateNetworkRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateNetworkRequest: wiretype end group for non-group") + return fmt.Errorf("proto: ListSecretsRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateNetworkRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListSecretsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -11959,7 +20771,7 @@ func (m *CreateNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11968,13 +20780,16 @@ func (m *CreateNetworkRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Spec == nil { - m.Spec = &NetworkSpec{} + if m.Filters == nil { + m.Filters = &ListSecretsRequest_Filters{} } - if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -11984,7 +20799,7 @@ func (m *CreateNetworkRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -11999,7 +20814,7 @@ func (m *CreateNetworkRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *CreateNetworkResponse) Unmarshal(dAtA []byte) error { +func (m *ListSecretsRequest_Filters) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -12014,7 +20829,7 @@ func (m *CreateNetworkResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12022,17 +20837,208 @@ func (m *CreateNetworkResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateNetworkResponse: wiretype end group for non-group") + return fmt.Errorf("proto: Filters: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateNetworkResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: Filters: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Network", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Names", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Names = append(m.Names, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IDPrefixes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IDPrefixes = append(m.IDPrefixes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Labels == nil { + m.Labels = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -12042,24 +21048,23 @@ func (m *CreateNetworkResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Network == nil { - m.Network = &Network{} - } - if err := m.Network.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex @@ -12067,7 +21072,7 @@ func (m *CreateNetworkResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -12082,7 +21087,7 @@ func (m *CreateNetworkResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *GetNetworkRequest) Unmarshal(dAtA []byte) error { +func (m *ListSecretsResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -12097,7 +21102,7 @@ func (m *GetNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12105,17 +21110,17 @@ func (m *GetNetworkRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetNetworkRequest: wiretype end group for non-group") + return fmt.Errorf("proto: ListSecretsResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetNetworkRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListSecretsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Secrets", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -12125,49 +21130,25 @@ func (m *GetNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NetworkID", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.NetworkID = string(dAtA[iNdEx:postIndex]) + m.Secrets = append(m.Secrets, &Secret{}) + if err := m.Secrets[len(m.Secrets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -12175,7 +21156,7 @@ func (m *GetNetworkRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -12190,7 +21171,7 @@ func (m *GetNetworkRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *GetNetworkResponse) Unmarshal(dAtA []byte) error { +func (m *CreateSecretRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -12205,7 +21186,7 @@ func (m *GetNetworkResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12213,15 +21194,15 @@ func (m *GetNetworkResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetNetworkResponse: wiretype end group for non-group") + return fmt.Errorf("proto: CreateSecretRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetNetworkResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateSecretRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Network", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -12233,7 +21214,7 @@ func (m *GetNetworkResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -12242,13 +21223,16 @@ func (m *GetNetworkResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Network == nil { - m.Network = &Network{} + if m.Spec == nil { + m.Spec = &SecretSpec{} } - if err := m.Network.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -12258,7 +21242,7 @@ func (m *GetNetworkResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -12273,7 +21257,7 @@ func (m *GetNetworkResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *RemoveNetworkRequest) Unmarshal(dAtA []byte) error { +func (m *CreateSecretResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -12288,7 +21272,7 @@ func (m *RemoveNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12296,17 +21280,17 @@ func (m *RemoveNetworkRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RemoveNetworkRequest: wiretype end group for non-group") + return fmt.Errorf("proto: CreateSecretResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveNetworkRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateSecretResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Secret", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -12316,107 +21300,35 @@ func (m *RemoveNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Name = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NetworkID", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.NetworkID = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *RemoveNetworkResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF + if m.Secret == nil { + m.Secret = &Secret{} } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break + if err := m.Secret.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RemoveNetworkResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveNetworkResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -12431,7 +21343,7 @@ func (m *RemoveNetworkResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListNetworksRequest) Unmarshal(dAtA []byte) error { +func (m *RemoveSecretRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -12446,7 +21358,7 @@ func (m *ListNetworksRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12454,17 +21366,17 @@ func (m *ListNetworksRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListNetworksRequest: wiretype end group for non-group") + return fmt.Errorf("proto: RemoveSecretRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListNetworksRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RemoveSecretRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SecretID", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -12474,24 +21386,23 @@ func (m *ListNetworksRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Filters == nil { - m.Filters = &ListNetworksRequest_Filters{} - } - if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.SecretID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -12499,7 +21410,7 @@ func (m *ListNetworksRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -12514,7 +21425,7 @@ func (m *ListNetworksRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListNetworksRequest_Filters) Unmarshal(dAtA []byte) error { +func (m *RemoveSecretResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -12529,7 +21440,7 @@ func (m *ListNetworksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12537,191 +21448,65 @@ func (m *ListNetworksRequest_Filters) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: Filters: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Filters: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Names", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Names = append(m.Names, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field IDPrefixes", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } + return fmt.Errorf("proto: RemoveSecretResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RemoveSecretResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err } - intStringLen := int(stringLen) - if intStringLen < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen - if postIndex > l { + if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - m.IDPrefixes = append(m.IDPrefixes, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthControl + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetConfigRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl } - postIndex := iNdEx + msglen - if postIndex > l { + if iNdEx >= l { return io.ErrUnexpectedEOF } - if m.Labels == nil { - m.Labels = make(map[string]string) - } - var mapkey string - var mapvalue string - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthControl - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var stringLenmapvalue uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthControl - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue - } else { - iNdEx = entryPreIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break } - m.Labels[mapkey] = mapvalue - iNdEx = postIndex - case 4: + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetConfigRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ConfigID", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -12733,7 +21518,7 @@ func (m *ListNetworksRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12743,10 +21528,13 @@ func (m *ListNetworksRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) + m.ConfigID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -12754,7 +21542,7 @@ func (m *ListNetworksRequest_Filters) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -12769,7 +21557,7 @@ func (m *ListNetworksRequest_Filters) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListNetworksResponse) Unmarshal(dAtA []byte) error { +func (m *GetConfigResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -12784,7 +21572,7 @@ func (m *ListNetworksResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12792,15 +21580,15 @@ func (m *ListNetworksResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListNetworksResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetConfigResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListNetworksResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Networks", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -12812,7 +21600,7 @@ func (m *ListNetworksResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -12821,11 +21609,16 @@ func (m *ListNetworksResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Networks = append(m.Networks, &Network{}) - if err := m.Networks[len(m.Networks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if m.Config == nil { + m.Config = &Config{} + } + if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -12835,7 +21628,7 @@ func (m *ListNetworksResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -12850,7 +21643,7 @@ func (m *ListNetworksResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *GetClusterRequest) Unmarshal(dAtA []byte) error { +func (m *UpdateConfigRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -12865,25 +21658,93 @@ func (m *GetClusterRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetClusterRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetClusterRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateConfigRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConfigID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConfigID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConfigVersion", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ConfigVersion == nil { + m.ConfigVersion = &Version{} + } + if err := m.ConfigVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -12893,20 +21754,27 @@ func (m *GetClusterRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.ClusterID = string(dAtA[iNdEx:postIndex]) + if m.Spec == nil { + m.Spec = &ConfigSpec{} + } + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -12914,7 +21782,7 @@ func (m *GetClusterRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -12929,7 +21797,7 @@ func (m *GetClusterRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *GetClusterResponse) Unmarshal(dAtA []byte) error { +func (m *UpdateConfigResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -12944,7 +21812,7 @@ func (m *GetClusterResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12952,15 +21820,15 @@ func (m *GetClusterResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetClusterResponse: wiretype end group for non-group") + return fmt.Errorf("proto: UpdateConfigResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetClusterResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: UpdateConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -12972,7 +21840,7 @@ func (m *GetClusterResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -12981,13 +21849,16 @@ func (m *GetClusterResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Cluster == nil { - m.Cluster = &Cluster{} + if m.Config == nil { + m.Config = &Config{} } - if err := m.Cluster.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -12997,7 +21868,7 @@ func (m *GetClusterResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -13012,7 +21883,7 @@ func (m *GetClusterResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListClustersRequest) Unmarshal(dAtA []byte) error { +func (m *ListConfigsRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13027,7 +21898,7 @@ func (m *ListClustersRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13035,10 +21906,10 @@ func (m *ListClustersRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListClustersRequest: wiretype end group for non-group") + return fmt.Errorf("proto: ListConfigsRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListClustersRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListConfigsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -13055,7 +21926,7 @@ func (m *ListClustersRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13064,11 +21935,14 @@ func (m *ListClustersRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Filters == nil { - m.Filters = &ListClustersRequest_Filters{} + m.Filters = &ListConfigsRequest_Filters{} } if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -13080,7 +21954,7 @@ func (m *ListClustersRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -13095,7 +21969,7 @@ func (m *ListClustersRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { +func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13110,7 +21984,7 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13138,7 +22012,7 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13148,6 +22022,9 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13167,7 +22044,7 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13177,6 +22054,9 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13196,7 +22076,7 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13205,6 +22085,9 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13225,7 +22108,7 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13242,7 +22125,7 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13252,6 +22135,9 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthControl + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -13268,7 +22154,7 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13278,6 +22164,9 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthControl + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -13289,202 +22178,22 @@ func (m *ListClustersRequest_Filters) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.Labels[mapkey] = mapvalue - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ListClustersResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ListClustersResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ListClustersResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Clusters", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Clusters = append(m.Clusters, &Cluster{}) - if err := m.Clusters[len(m.Clusters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *KeyRotation) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: KeyRotation: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: KeyRotation: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field WorkerJoinToken", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (int(b) & 0x7F) << shift - if b < 0x80 { - break + return io.ErrUnexpectedEOF + } + iNdEx += skippy } } - m.WorkerJoinToken = bool(v != 0) - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ManagerJoinToken", wireType) + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) } - var v int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -13494,39 +22203,31 @@ func (m *KeyRotation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - m.ManagerJoinToken = bool(v != 0) - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ManagerUnlockKey", wireType) + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl } - m.ManagerUnlockKey = bool(v != 0) + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -13541,7 +22242,7 @@ func (m *KeyRotation) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateClusterRequest) Unmarshal(dAtA []byte) error { +func (m *ListConfigsResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13556,7 +22257,7 @@ func (m *UpdateClusterRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13564,77 +22265,15 @@ func (m *UpdateClusterRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateClusterRequest: wiretype end group for non-group") + return fmt.Errorf("proto: ListConfigsResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateClusterRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListConfigsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterID", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ClusterID = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ClusterVersion", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ClusterVersion == nil { - m.ClusterVersion = &Version{} - } - if err := m.ClusterVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Configs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13646,7 +22285,7 @@ func (m *UpdateClusterRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13655,43 +22294,14 @@ func (m *UpdateClusterRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Spec == nil { - m.Spec = &ClusterSpec{} - } - if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Rotation", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Rotation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Configs = append(m.Configs, &Config{}) + if err := m.Configs[len(m.Configs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13701,7 +22311,7 @@ func (m *UpdateClusterRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -13716,7 +22326,7 @@ func (m *UpdateClusterRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateClusterResponse) Unmarshal(dAtA []byte) error { +func (m *CreateConfigRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13731,7 +22341,7 @@ func (m *UpdateClusterResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13739,15 +22349,15 @@ func (m *UpdateClusterResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateClusterResponse: wiretype end group for non-group") + return fmt.Errorf("proto: CreateConfigRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateClusterResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Cluster", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13759,7 +22369,7 @@ func (m *UpdateClusterResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13768,13 +22378,16 @@ func (m *UpdateClusterResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Cluster == nil { - m.Cluster = &Cluster{} + if m.Spec == nil { + m.Spec = &ConfigSpec{} } - if err := m.Cluster.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13784,7 +22397,7 @@ func (m *UpdateClusterResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -13799,7 +22412,7 @@ func (m *UpdateClusterResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *GetSecretRequest) Unmarshal(dAtA []byte) error { +func (m *CreateConfigResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13814,7 +22427,7 @@ func (m *GetSecretRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13822,17 +22435,17 @@ func (m *GetSecretRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetSecretRequest: wiretype end group for non-group") + return fmt.Errorf("proto: CreateConfigResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetSecretRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SecretID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -13842,20 +22455,27 @@ func (m *GetSecretRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.SecretID = string(dAtA[iNdEx:postIndex]) + if m.Config == nil { + m.Config = &Config{} + } + if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -13863,7 +22483,7 @@ func (m *GetSecretRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -13878,7 +22498,7 @@ func (m *GetSecretRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *GetSecretResponse) Unmarshal(dAtA []byte) error { +func (m *RemoveConfigRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13893,7 +22513,7 @@ func (m *GetSecretResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13901,17 +22521,17 @@ func (m *GetSecretResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetSecretResponse: wiretype end group for non-group") + return fmt.Errorf("proto: RemoveConfigRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetSecretResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RemoveConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Secret", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ConfigID", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -13921,24 +22541,23 @@ func (m *GetSecretResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Secret == nil { - m.Secret = &Secret{} - } - if err := m.Secret.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.ConfigID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -13946,7 +22565,7 @@ func (m *GetSecretResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -13961,7 +22580,7 @@ func (m *GetSecretResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateSecretRequest) Unmarshal(dAtA []byte) error { +func (m *RemoveConfigResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13976,7 +22595,7 @@ func (m *UpdateSecretRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13984,44 +22603,65 @@ func (m *UpdateSecretRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateSecretRequest: wiretype end group for non-group") + return fmt.Errorf("proto: RemoveConfigResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateSecretRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RemoveConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SecretID", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err } - intStringLen := int(stringLen) - if intStringLen < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen - if postIndex > l { + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CreateExtensionRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { return io.ErrUnexpectedEOF } - m.SecretID = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CreateExtensionRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CreateExtensionRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SecretVersion", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -14033,7 +22673,7 @@ func (m *UpdateSecretRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14042,21 +22682,24 @@ func (m *UpdateSecretRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.SecretVersion == nil { - m.SecretVersion = &Version{} + if m.Annotations == nil { + m.Annotations = &Annotations{} } - if err := m.SecretVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Annotations.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 3: + case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -14066,24 +22709,23 @@ func (m *UpdateSecretRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Spec == nil { - m.Spec = &SecretSpec{} - } - if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Description = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -14091,7 +22733,7 @@ func (m *UpdateSecretRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -14106,7 +22748,7 @@ func (m *UpdateSecretRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateSecretResponse) Unmarshal(dAtA []byte) error { +func (m *CreateExtensionResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14121,7 +22763,7 @@ func (m *UpdateSecretResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14129,15 +22771,15 @@ func (m *UpdateSecretResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateSecretResponse: wiretype end group for non-group") + return fmt.Errorf("proto: CreateExtensionResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateSecretResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateExtensionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Secret", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Extension", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -14149,7 +22791,7 @@ func (m *UpdateSecretResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14158,13 +22800,16 @@ func (m *UpdateSecretResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Secret == nil { - m.Secret = &Secret{} + if m.Extension == nil { + m.Extension = &Extension{} } - if err := m.Secret.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Extension.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -14174,7 +22819,7 @@ func (m *UpdateSecretResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -14189,7 +22834,7 @@ func (m *UpdateSecretResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListSecretsRequest) Unmarshal(dAtA []byte) error { +func (m *RemoveExtensionRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14204,7 +22849,7 @@ func (m *ListSecretsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14212,17 +22857,17 @@ func (m *ListSecretsRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListSecretsRequest: wiretype end group for non-group") + return fmt.Errorf("proto: RemoveExtensionRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListSecretsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RemoveExtensionRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Filters", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ExtensionID", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -14232,24 +22877,23 @@ func (m *ListSecretsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Filters == nil { - m.Filters = &ListSecretsRequest_Filters{} - } - if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.ExtensionID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -14257,7 +22901,7 @@ func (m *ListSecretsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -14272,7 +22916,7 @@ func (m *ListSecretsRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListSecretsRequest_Filters) Unmarshal(dAtA []byte) error { +func (m *RemoveExtensionResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14287,7 +22931,7 @@ func (m *ListSecretsRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14295,191 +22939,65 @@ func (m *ListSecretsRequest_Filters) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: Filters: wiretype end group for non-group") + return fmt.Errorf("proto: RemoveExtensionResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: Filters: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RemoveExtensionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Names", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Names = append(m.Names, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field IDPrefixes", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.IDPrefixes = append(m.IDPrefixes, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Labels == nil { - m.Labels = make(map[string]string) - } - var mapkey string - var mapvalue string - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthControl - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var stringLenmapvalue uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthControl - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue - } else { - iNdEx = entryPreIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } + default: + iNdEx = preIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err } - m.Labels[mapkey] = mapvalue - iNdEx = postIndex - case 4: + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetExtensionRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetExtensionRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetExtensionRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ExtensionID", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -14491,7 +23009,7 @@ func (m *ListSecretsRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14501,10 +23019,13 @@ func (m *ListSecretsRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) + m.ExtensionID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -14512,7 +23033,7 @@ func (m *ListSecretsRequest_Filters) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -14527,7 +23048,7 @@ func (m *ListSecretsRequest_Filters) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListSecretsResponse) Unmarshal(dAtA []byte) error { +func (m *GetExtensionResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14542,7 +23063,7 @@ func (m *ListSecretsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14550,15 +23071,15 @@ func (m *ListSecretsResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListSecretsResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetExtensionResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListSecretsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetExtensionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Secrets", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Extension", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -14570,7 +23091,7 @@ func (m *ListSecretsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14579,11 +23100,16 @@ func (m *ListSecretsResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Secrets = append(m.Secrets, &Secret{}) - if err := m.Secrets[len(m.Secrets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if m.Extension == nil { + m.Extension = &Extension{} + } + if err := m.Extension.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -14593,7 +23119,7 @@ func (m *ListSecretsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -14608,7 +23134,7 @@ func (m *ListSecretsResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *CreateSecretRequest) Unmarshal(dAtA []byte) error { +func (m *CreateResourceRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14623,7 +23149,7 @@ func (m *CreateSecretRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14631,15 +23157,83 @@ func (m *CreateSecretRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateSecretRequest: wiretype end group for non-group") + return fmt.Errorf("proto: CreateResourceRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateSecretRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateResourceRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Annotations == nil { + m.Annotations = &Annotations{} + } + if err := m.Annotations.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Kind = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Payload", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -14651,7 +23245,7 @@ func (m *CreateSecretRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14660,13 +23254,16 @@ func (m *CreateSecretRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Spec == nil { - m.Spec = &SecretSpec{} + if m.Payload == nil { + m.Payload = &types.Any{} } - if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Payload.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -14676,7 +23273,7 @@ func (m *CreateSecretRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -14691,7 +23288,7 @@ func (m *CreateSecretRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *CreateSecretResponse) Unmarshal(dAtA []byte) error { +func (m *CreateResourceResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14706,7 +23303,7 @@ func (m *CreateSecretResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14714,15 +23311,15 @@ func (m *CreateSecretResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateSecretResponse: wiretype end group for non-group") + return fmt.Errorf("proto: CreateResourceResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateSecretResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: CreateResourceResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Secret", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -14734,7 +23331,7 @@ func (m *CreateSecretResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14743,13 +23340,16 @@ func (m *CreateSecretResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Secret == nil { - m.Secret = &Secret{} + if m.Resource == nil { + m.Resource = &Resource{} } - if err := m.Secret.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Resource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -14759,7 +23359,7 @@ func (m *CreateSecretResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -14774,7 +23374,7 @@ func (m *CreateSecretResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *RemoveSecretRequest) Unmarshal(dAtA []byte) error { +func (m *RemoveResourceRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14789,7 +23389,7 @@ func (m *RemoveSecretRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14797,15 +23397,15 @@ func (m *RemoveSecretRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RemoveSecretRequest: wiretype end group for non-group") + return fmt.Errorf("proto: RemoveResourceRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveSecretRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RemoveResourceRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SecretID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ResourceID", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -14817,7 +23417,7 @@ func (m *RemoveSecretRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14827,10 +23427,13 @@ func (m *RemoveSecretRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - m.SecretID = string(dAtA[iNdEx:postIndex]) + m.ResourceID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -14838,7 +23441,7 @@ func (m *RemoveSecretRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -14853,7 +23456,7 @@ func (m *RemoveSecretRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *RemoveSecretResponse) Unmarshal(dAtA []byte) error { +func (m *RemoveResourceResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14868,7 +23471,7 @@ func (m *RemoveSecretResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14876,10 +23479,10 @@ func (m *RemoveSecretResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: RemoveSecretResponse: wiretype end group for non-group") + return fmt.Errorf("proto: RemoveResourceResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveSecretResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: RemoveResourceResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -14888,55 +23491,159 @@ func (m *RemoveSecretResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateResourceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateResourceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateResourceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceVersion", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ResourceVersion == nil { + m.ResourceVersion = &Version{} + } + if err := m.ResourceVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { return ErrInvalidLengthControl } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *GetConfigRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl } - if iNdEx >= l { + if postIndex > l { return io.ErrUnexpectedEOF } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break + if m.Annotations == nil { + m.Annotations = &Annotations{} } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: GetConfigRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: GetConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + if err := m.Annotations.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConfigID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Payload", wireType) } - var stringLen uint64 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -14946,20 +23653,27 @@ func (m *GetConfigRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.ConfigID = string(dAtA[iNdEx:postIndex]) + if m.Payload == nil { + m.Payload = &types.Any{} + } + if err := m.Payload.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } iNdEx = postIndex default: iNdEx = preIndex @@ -14967,7 +23681,7 @@ func (m *GetConfigRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -14982,7 +23696,7 @@ func (m *GetConfigRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *GetConfigResponse) Unmarshal(dAtA []byte) error { +func (m *UpdateResourceResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -14997,7 +23711,7 @@ func (m *GetConfigResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15005,15 +23719,15 @@ func (m *GetConfigResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GetConfigResponse: wiretype end group for non-group") + return fmt.Errorf("proto: UpdateResourceResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GetConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: UpdateResourceResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -15025,7 +23739,7 @@ func (m *GetConfigResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15034,13 +23748,16 @@ func (m *GetConfigResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Config == nil { - m.Config = &Config{} + if m.Resource == nil { + m.Resource = &Resource{} } - if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Resource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -15050,7 +23767,7 @@ func (m *GetConfigResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -15065,7 +23782,7 @@ func (m *GetConfigResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateConfigRequest) Unmarshal(dAtA []byte) error { +func (m *GetResourceRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15080,7 +23797,7 @@ func (m *UpdateConfigRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15088,15 +23805,15 @@ func (m *UpdateConfigRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateConfigRequest: wiretype end group for non-group") + return fmt.Errorf("proto: GetResourceRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetResourceRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConfigID", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ResourceID", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -15108,7 +23825,7 @@ func (m *UpdateConfigRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15118,76 +23835,13 @@ func (m *UpdateConfigRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ConfigID = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConfigVersion", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.ConfigVersion == nil { - m.ConfigVersion = &Version{} - } - if err := m.ConfigVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= (int(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Spec == nil { - m.Spec = &ConfigSpec{} - } - if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.ResourceID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -15195,7 +23849,7 @@ func (m *UpdateConfigRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -15210,7 +23864,7 @@ func (m *UpdateConfigRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdateConfigResponse) Unmarshal(dAtA []byte) error { +func (m *GetResourceResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15225,7 +23879,7 @@ func (m *UpdateConfigResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15233,15 +23887,15 @@ func (m *UpdateConfigResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: UpdateConfigResponse: wiretype end group for non-group") + return fmt.Errorf("proto: GetResourceResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: UpdateConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GetResourceResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -15253,7 +23907,7 @@ func (m *UpdateConfigResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15262,13 +23916,16 @@ func (m *UpdateConfigResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Config == nil { - m.Config = &Config{} + if m.Resource == nil { + m.Resource = &Resource{} } - if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Resource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -15278,7 +23935,7 @@ func (m *UpdateConfigResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -15293,7 +23950,7 @@ func (m *UpdateConfigResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListConfigsRequest) Unmarshal(dAtA []byte) error { +func (m *ListResourcesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15308,7 +23965,7 @@ func (m *ListConfigsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15316,10 +23973,10 @@ func (m *ListConfigsRequest) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ListConfigsRequest: wiretype end group for non-group") + return fmt.Errorf("proto: ListResourcesRequest: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ListConfigsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListResourcesRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -15336,7 +23993,7 @@ func (m *ListConfigsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15345,11 +24002,14 @@ func (m *ListConfigsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Filters == nil { - m.Filters = &ListConfigsRequest_Filters{} + m.Filters = &ListResourcesRequest_Filters{} } if err := m.Filters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -15361,7 +24021,7 @@ func (m *ListConfigsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -15376,7 +24036,7 @@ func (m *ListConfigsRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { +func (m *ListResourcesRequest_Filters) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15391,7 +24051,7 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15419,7 +24079,7 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15429,6 +24089,9 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15448,7 +24111,7 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15458,6 +24121,9 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15477,7 +24143,7 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15486,6 +24152,9 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15506,7 +24175,7 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15523,7 +24192,7 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15533,6 +24202,9 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthControl + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -15549,7 +24221,7 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15559,6 +24231,9 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthControl + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -15570,101 +24245,22 @@ func (m *ListConfigsRequest_Filters) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - m.Labels[mapkey] = mapvalue - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthControl - } - postIndex := iNdEx + intStringLen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *ListConfigsResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: ListConfigsResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: ListConfigsResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + } + iNdEx += skippy + } + } + m.Labels[mapkey] = mapvalue + iNdEx = postIndex + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Configs", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field NamePrefixes", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -15674,78 +24270,29 @@ func (m *ListConfigsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Configs = append(m.Configs, &Config{}) - if err := m.Configs[len(m.Configs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthControl } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CreateConfigRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { + if postIndex > l { return io.ErrUnexpectedEOF } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: CreateConfigRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CreateConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: + m.NamePrefixes = append(m.NamePrefixes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) } - var msglen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -15755,24 +24302,23 @@ func (m *CreateConfigRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + msglen if postIndex > l { return io.ErrUnexpectedEOF } - if m.Spec == nil { - m.Spec = &ConfigSpec{} - } - if err := m.Spec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.Kind = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -15780,7 +24326,7 @@ func (m *CreateConfigRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -15795,7 +24341,7 @@ func (m *CreateConfigRequest) Unmarshal(dAtA []byte) error { } return nil } -func (m *CreateConfigResponse) Unmarshal(dAtA []byte) error { +func (m *ListResourcesResponse) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -15810,7 +24356,7 @@ func (m *CreateConfigResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15818,15 +24364,15 @@ func (m *CreateConfigResponse) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: CreateConfigResponse: wiretype end group for non-group") + return fmt.Errorf("proto: ListResourcesResponse: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: CreateConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ListResourcesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Resources", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -15838,7 +24384,7 @@ func (m *CreateConfigResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15847,152 +24393,24 @@ func (m *CreateConfigResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthControl } postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Config == nil { - m.Config = &Config{} - } - if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *RemoveConfigRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RemoveConfigRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveConfigRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ConfigID", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { + if postIndex < 0 { return ErrInvalidLengthControl } - postIndex := iNdEx + intStringLen if postIndex > l { return io.ErrUnexpectedEOF } - m.ConfigID = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipControl(dAtA[iNdEx:]) - if err != nil { + m.Resources = append(m.Resources, &Resource{}) + if err := m.Resources[len(m.Resources)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - if skippy < 0 { - return ErrInvalidLengthControl - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *RemoveConfigResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowControl - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: RemoveConfigResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: RemoveConfigResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthControl } if (iNdEx + skippy) > l { @@ -16010,6 +24428,7 @@ func (m *RemoveConfigResponse) Unmarshal(dAtA []byte) error { func skipControl(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -16041,10 +24460,8 @@ func skipControl(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -16061,195 +24478,34 @@ func skipControl(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthControl } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowControl - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipControl(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupControl + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthControl + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthControl = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowControl = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthControl = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowControl = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupControl = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("github.com/docker/swarmkit/api/control.proto", fileDescriptorControl) } - -var fileDescriptorControl = []byte{ - // 2167 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0x4d, 0x73, 0x1b, 0x49, - 0x19, 0xb6, 0x3e, 0x6c, 0xc9, 0xaf, 0x6c, 0xd9, 0xee, 0x78, 0x41, 0xa5, 0x04, 0x3b, 0x35, 0x21, - 0x89, 0xb2, 0x65, 0x24, 0x56, 0x61, 0xd9, 0xb0, 0xb0, 0xc0, 0xda, 0xce, 0x66, 0xb5, 0xde, 0x38, - 0xa9, 0x71, 0xb2, 0xc5, 0x85, 0x52, 0xc9, 0x52, 0xdb, 0x3b, 0x91, 0xac, 0x11, 0x33, 0x23, 0xef, - 0xba, 0xb8, 0x00, 0x15, 0x7e, 0x02, 0x55, 0x5c, 0x39, 0x51, 0xc5, 0x81, 0x03, 0x27, 0x0e, 0xfc, - 0x80, 0x14, 0x27, 0x8e, 0x9c, 0x0c, 0xab, 0x2a, 0xaa, 0x38, 0xf1, 0x1b, 0xa8, 0xee, 0x7e, 0x7b, - 0xbe, 0xd4, 0x33, 0xa3, 0x0f, 0x57, 0x79, 0x4f, 0x96, 0x7a, 0x9e, 0xf7, 0xa3, 0xfb, 0x7d, 0xfa, - 0x51, 0xf7, 0x3b, 0x86, 0x9d, 0x53, 0xc3, 0xf9, 0x7c, 0x78, 0x5c, 0x6d, 0x9b, 0x67, 0xb5, 0x8e, - 0xd9, 0xee, 0x52, 0xab, 0x66, 0x7f, 0xd1, 0xb2, 0xce, 0xba, 0x86, 0x53, 0x6b, 0x0d, 0x8c, 0x5a, - 0xdb, 0xec, 0x3b, 0x96, 0xd9, 0xab, 0x0e, 0x2c, 0xd3, 0x31, 0x09, 0x11, 0x90, 0xaa, 0x84, 0x54, - 0xcf, 0xdf, 0x29, 0xbf, 0x9d, 0xe0, 0xc1, 0x1e, 0xd0, 0xb6, 0x2d, 0xec, 0xcb, 0x49, 0xd1, 0xcc, - 0xe3, 0x57, 0xb4, 0xed, 0x48, 0x74, 0x92, 0x67, 0xe7, 0x62, 0x40, 0x25, 0x76, 0xf3, 0xd4, 0x3c, - 0x35, 0xf9, 0xc7, 0x1a, 0xfb, 0x84, 0xa3, 0xef, 0xc5, 0x78, 0xe0, 0x88, 0xe3, 0xe1, 0x49, 0x6d, - 0xd0, 0x1b, 0x9e, 0x1a, 0x7d, 0xfc, 0x23, 0x0c, 0xb5, 0x77, 0xa1, 0xf8, 0x84, 0x3a, 0x87, 0x66, - 0x87, 0xea, 0xf4, 0x17, 0x43, 0x6a, 0x3b, 0xe4, 0x0e, 0xe4, 0xfa, 0x66, 0x87, 0x36, 0x8d, 0x4e, - 0x29, 0x75, 0x3b, 0x55, 0x59, 0xde, 0x85, 0xd1, 0xe5, 0xf6, 0x12, 0x43, 0x34, 0xf6, 0xf5, 0x25, - 0xf6, 0xa8, 0xd1, 0xd1, 0x7e, 0x02, 0x6b, 0xae, 0x99, 0x3d, 0x30, 0xfb, 0x36, 0x25, 0x3b, 0x90, - 0x65, 0x0f, 0xb9, 0x51, 0xa1, 0x5e, 0xaa, 0x8e, 0xaf, 0x60, 0x95, 0xe3, 0x39, 0x4a, 0x7b, 0xbd, - 0x08, 0xeb, 0x9f, 0x1a, 0x36, 0x77, 0x61, 0xcb, 0xd0, 0x1f, 0x41, 0xee, 0xc4, 0xe8, 0x39, 0xd4, - 0xb2, 0xd1, 0xcb, 0x8e, 0xca, 0x4b, 0xd8, 0xac, 0xfa, 0x91, 0xb0, 0xd1, 0xa5, 0x71, 0xf9, 0x8f, - 0x59, 0xc8, 0xe1, 0x20, 0xd9, 0x84, 0xc5, 0x7e, 0xeb, 0x8c, 0x32, 0x8f, 0x99, 0xca, 0xb2, 0x2e, - 0xbe, 0x90, 0x1a, 0x14, 0x8c, 0x4e, 0x73, 0x60, 0xd1, 0x13, 0xe3, 0x4b, 0x6a, 0x97, 0xd2, 0xec, - 0xd9, 0x6e, 0x71, 0x74, 0xb9, 0x0d, 0x8d, 0xfd, 0xe7, 0x38, 0xaa, 0x83, 0xd1, 0x91, 0x9f, 0xc9, - 0x73, 0x58, 0xea, 0xb5, 0x8e, 0x69, 0xcf, 0x2e, 0x65, 0x6e, 0x67, 0x2a, 0x85, 0xfa, 0xa3, 0x69, - 0x32, 0xab, 0x7e, 0xca, 0x4d, 0x1f, 0xf7, 0x1d, 0xeb, 0x42, 0x47, 0x3f, 0xe4, 0x29, 0x14, 0xce, - 0xe8, 0xd9, 0x31, 0xb5, 0xec, 0xcf, 0x8d, 0x81, 0x5d, 0xca, 0xde, 0xce, 0x54, 0x8a, 0xf5, 0xfb, - 0x51, 0xcb, 0x76, 0x34, 0xa0, 0xed, 0xea, 0x53, 0x17, 0xbf, 0x9b, 0x5e, 0x5f, 0xd0, 0xfd, 0xf6, - 0xe4, 0xfb, 0xb0, 0x68, 0x99, 0x3d, 0x6a, 0x97, 0x16, 0xb9, 0xa3, 0x5b, 0x91, 0xeb, 0x6f, 0xf6, - 0x28, 0xb7, 0x16, 0x70, 0x72, 0x07, 0x56, 0xd9, 0x92, 0x78, 0x6b, 0xb1, 0xc4, 0xd7, 0x69, 0x85, - 0x0d, 0xba, 0xb3, 0xff, 0x39, 0x14, 0x38, 0x27, 0x70, 0x09, 0x72, 0x7c, 0x09, 0x7e, 0x34, 0xd5, - 0x12, 0xb0, 0x41, 0xff, 0x32, 0x40, 0xdf, 0x1d, 0x28, 0xff, 0x00, 0x0a, 0xbe, 0x47, 0x64, 0x1d, - 0x32, 0x5d, 0x7a, 0x21, 0xd8, 0xa7, 0xb3, 0x8f, 0xac, 0x88, 0xe7, 0xad, 0xde, 0x90, 0x96, 0xd2, - 0x7c, 0x4c, 0x7c, 0x79, 0x3f, 0xfd, 0x28, 0x55, 0xfe, 0x00, 0xd6, 0x42, 0x9e, 0xa7, 0x31, 0xd7, - 0xf6, 0x60, 0xc3, 0x97, 0x31, 0x32, 0xb9, 0x0a, 0x8b, 0x2c, 0x39, 0x41, 0x99, 0x38, 0x2a, 0x0b, - 0x98, 0xf6, 0xa7, 0x14, 0x6c, 0xbc, 0x1c, 0x74, 0x5a, 0x0e, 0x9d, 0x76, 0x1f, 0x91, 0x1f, 0xc3, - 0x0a, 0x07, 0x9d, 0x53, 0xcb, 0x36, 0xcc, 0x3e, 0x4f, 0xb0, 0x50, 0xbf, 0xa9, 0x8a, 0xf8, 0x99, - 0x80, 0xe8, 0xbc, 0x12, 0xf8, 0x85, 0x7c, 0x17, 0xb2, 0x4c, 0x76, 0x4a, 0x19, 0x6e, 0x77, 0x2b, - 0x8e, 0x3d, 0x3a, 0x47, 0x6a, 0xbb, 0x40, 0xfc, 0xb9, 0xce, 0xb4, 0x79, 0x0f, 0x61, 0x43, 0xa7, - 0x67, 0xe6, 0xf9, 0xf4, 0xf3, 0xdd, 0x84, 0xc5, 0x13, 0xd3, 0x6a, 0x8b, 0x4a, 0xe4, 0x75, 0xf1, - 0x45, 0xdb, 0x04, 0xe2, 0xf7, 0x27, 0x72, 0x42, 0x69, 0x7a, 0xd1, 0xb2, 0xbb, 0xbe, 0x10, 0x4e, - 0xcb, 0xee, 0x86, 0x42, 0x30, 0x04, 0x0b, 0xc1, 0x1e, 0xb9, 0xd2, 0x24, 0xcc, 0xbc, 0xd9, 0xb1, - 0x87, 0x71, 0xb3, 0xe3, 0x78, 0x8e, 0xd2, 0x1e, 0xc9, 0xd9, 0x4d, 0x1d, 0xda, 0x9d, 0x87, 0x3f, - 0xba, 0xf6, 0xb7, 0xac, 0x90, 0x3a, 0x36, 0x38, 0x83, 0xd4, 0xf9, 0xcd, 0xc6, 0xa5, 0xee, 0x5f, - 0x99, 0xeb, 0x93, 0x3a, 0x55, 0x66, 0x4a, 0xa9, 0xab, 0x41, 0xc1, 0xa6, 0xd6, 0xb9, 0xd1, 0x66, - 0xec, 0x10, 0x52, 0x87, 0x29, 0x1c, 0x89, 0xe1, 0xc6, 0xbe, 0xad, 0x03, 0x42, 0x1a, 0x1d, 0x9b, - 0xdc, 0x83, 0x3c, 0x72, 0x49, 0xe8, 0xd9, 0xf2, 0x6e, 0x61, 0x74, 0xb9, 0x9d, 0x13, 0x64, 0xb2, - 0xf5, 0x9c, 0x60, 0x93, 0x4d, 0x3e, 0x86, 0x62, 0x87, 0xda, 0x86, 0x45, 0x3b, 0x4d, 0xdb, 0x69, - 0x39, 0xa8, 0x5e, 0xc5, 0xfa, 0xb7, 0xa2, 0x4a, 0x7c, 0xc4, 0x50, 0x5c, 0xfe, 0x56, 0xd1, 0x90, - 0x8f, 0x28, 0x64, 0x30, 0xa7, 0x90, 0xc1, 0x5b, 0x00, 0xc3, 0x41, 0xd3, 0x31, 0x9b, 0x6c, 0xff, - 0x94, 0xf2, 0x9c, 0xc2, 0xf9, 0xe1, 0xe0, 0x85, 0xb9, 0xdf, 0x72, 0x28, 0x29, 0x43, 0xde, 0x1a, - 0xf6, 0x1d, 0x83, 0x55, 0x60, 0x99, 0x5b, 0xbb, 0xdf, 0xe7, 0x50, 0x38, 0x29, 0x51, 0xb8, 0xd8, - 0x9e, 0x44, 0x31, 0xce, 0xc5, 0x4a, 0x14, 0x27, 0xa1, 0x80, 0x69, 0x07, 0xb0, 0xb9, 0x67, 0xd1, - 0x96, 0x43, 0x71, 0xc1, 0x25, 0x0d, 0x1f, 0xa2, 0x7e, 0x08, 0x0e, 0x6e, 0xab, 0xdc, 0xa0, 0x85, - 0x4f, 0x42, 0x0e, 0xe1, 0xad, 0x90, 0x33, 0xcc, 0xea, 0x5d, 0xc8, 0x61, 0x11, 0xd1, 0xe1, 0xcd, - 0x18, 0x87, 0xba, 0xc4, 0x6a, 0xaf, 0x60, 0xe3, 0x09, 0x75, 0x42, 0x99, 0xed, 0x00, 0x78, 0x9c, - 0xc1, 0x3d, 0xb7, 0x3a, 0xba, 0xdc, 0x5e, 0x76, 0x29, 0xa3, 0x2f, 0xbb, 0x8c, 0x21, 0xf7, 0x61, - 0xcd, 0xe8, 0xdb, 0xd4, 0x72, 0x9a, 0x1d, 0x7a, 0xd2, 0x1a, 0xf6, 0x1c, 0x1b, 0x15, 0xa6, 0x28, - 0x86, 0xf7, 0x71, 0x54, 0x3b, 0x00, 0xe2, 0x8f, 0x35, 0x5f, 0xe2, 0x7f, 0x49, 0xc3, 0xa6, 0x10, - 0xd3, 0xb9, 0x92, 0xdf, 0x87, 0x35, 0x89, 0x9e, 0xe2, 0x77, 0xa0, 0x88, 0x36, 0xf2, 0xa7, 0xe0, - 0x61, 0xe0, 0xa7, 0x60, 0xb2, 0x52, 0x92, 0xa7, 0x90, 0xb7, 0xcc, 0x5e, 0xef, 0xb8, 0xd5, 0xee, - 0x96, 0xb2, 0xb7, 0x53, 0x95, 0x62, 0xfd, 0x1d, 0x95, 0xa1, 0x6a, 0x92, 0x55, 0x1d, 0x0d, 0x75, - 0xd7, 0x85, 0xa6, 0x41, 0x5e, 0x8e, 0x92, 0x3c, 0x64, 0x0f, 0x9f, 0x1d, 0x3e, 0x5e, 0x5f, 0x20, - 0x2b, 0x90, 0x7f, 0xae, 0x3f, 0xfe, 0xac, 0xf1, 0xec, 0xe5, 0xd1, 0x7a, 0x8a, 0xb1, 0x27, 0xe4, - 0x6e, 0xbe, 0x22, 0xec, 0xc3, 0xa6, 0x10, 0xdd, 0x79, 0x6a, 0xa0, 0x7d, 0x13, 0xde, 0x0a, 0x79, - 0x41, 0xf5, 0x7e, 0x9d, 0x81, 0x1b, 0x6c, 0xff, 0xe1, 0xb8, 0x2b, 0xe0, 0x8d, 0xb0, 0x80, 0xd7, - 0xa2, 0x64, 0x32, 0x64, 0x39, 0xae, 0xe1, 0x7f, 0x48, 0x5f, 0xb9, 0x86, 0x1f, 0x85, 0x34, 0xfc, - 0x87, 0x53, 0x26, 0xa7, 0x94, 0xf1, 0x31, 0x8d, 0xcc, 0x2a, 0x34, 0xd2, 0xaf, 0x82, 0x8b, 0x57, - 0xa7, 0x82, 0xcf, 0x60, 0x33, 0x98, 0x2e, 0x92, 0xe6, 0x3d, 0xc8, 0x63, 0x11, 0xa5, 0x16, 0xc6, - 0xb2, 0xc6, 0x05, 0x7b, 0x8a, 0x78, 0x48, 0x9d, 0x2f, 0x4c, 0xab, 0x3b, 0x85, 0x22, 0xa2, 0x85, - 0x4a, 0x11, 0x5d, 0x67, 0x1e, 0xa7, 0xfb, 0x62, 0x28, 0x8e, 0xd3, 0xd2, 0x4a, 0x62, 0xb5, 0x97, - 0x5c, 0x11, 0x43, 0x99, 0x11, 0xc8, 0xb2, 0x95, 0xc6, 0xf5, 0xe2, 0x9f, 0x19, 0xc9, 0xd1, 0x86, - 0x91, 0x3c, 0xed, 0x91, 0x1c, 0x6d, 0x19, 0xc9, 0x11, 0xd0, 0xe8, 0xa0, 0xf8, 0x5d, 0x51, 0x8e, - 0x3f, 0x93, 0xfb, 0xee, 0xca, 0xd3, 0x74, 0xf7, 0x62, 0x28, 0x53, 0xed, 0xbf, 0x69, 0xb1, 0x17, - 0x71, 0x7c, 0x86, 0xbd, 0x18, 0xb2, 0x1c, 0xdf, 0x8b, 0xbf, 0xbd, 0xc6, 0xbd, 0x18, 0x91, 0xdc, - 0xcc, 0x7b, 0xf1, 0x0a, 0xf6, 0x9b, 0x97, 0x92, 0xb7, 0xdf, 0xb0, 0x50, 0xb1, 0xfb, 0x4d, 0x56, - 0xce, 0x05, 0x6b, 0x1f, 0x72, 0x4a, 0xef, 0xf5, 0x86, 0xb6, 0x43, 0x2d, 0x9f, 0x46, 0xb7, 0xc5, - 0x48, 0x48, 0xa3, 0x11, 0xc7, 0x78, 0x81, 0x00, 0x97, 0xbe, 0xae, 0x0b, 0x8f, 0xbe, 0x08, 0x89, - 0xa3, 0xaf, 0xb4, 0x92, 0x58, 0x97, 0x4b, 0xf8, 0x60, 0x06, 0x2e, 0x85, 0x2c, 0xbf, 0x5e, 0x5c, - 0x8a, 0x48, 0xee, 0x3a, 0xb9, 0xe4, 0xa5, 0xe4, 0x71, 0x09, 0xab, 0x11, 0xcb, 0x25, 0x59, 0x3a, - 0x17, 0xac, 0xfd, 0x2e, 0x05, 0x85, 0x03, 0x7a, 0xa1, 0x9b, 0x4e, 0xcb, 0x61, 0x47, 0x9f, 0xb7, - 0x61, 0x83, 0x91, 0x8c, 0x5a, 0xcd, 0x57, 0xa6, 0xd1, 0x6f, 0x3a, 0x66, 0x97, 0xf6, 0x79, 0x6a, - 0x79, 0x7d, 0x4d, 0x3c, 0xf8, 0xc4, 0x34, 0xfa, 0x2f, 0xd8, 0x30, 0xd9, 0x01, 0x72, 0xd6, 0xea, - 0xb7, 0x4e, 0x83, 0x60, 0x71, 0x58, 0x5c, 0xc7, 0x27, 0x4a, 0xf4, 0xb0, 0xdf, 0x33, 0xdb, 0xdd, - 0x26, 0x9b, 0x75, 0x26, 0x80, 0x7e, 0xc9, 0x1f, 0x1c, 0xd0, 0x0b, 0xed, 0x37, 0xee, 0x79, 0x70, - 0x1e, 0x9e, 0xb3, 0xf3, 0xa0, 0x44, 0x4f, 0x73, 0x1e, 0x44, 0x9b, 0x29, 0xce, 0x83, 0x18, 0xdd, - 0x77, 0x1e, 0xfc, 0x90, 0x9d, 0x07, 0xc5, 0xaa, 0xf2, 0xf3, 0x60, 0x84, 0xa1, 0x6f, 0xf1, 0x77, - 0xb3, 0x6f, 0x2e, 0xb7, 0x17, 0x74, 0xd7, 0xcc, 0x3b, 0xdf, 0x5d, 0xd1, 0x46, 0xfd, 0x00, 0xd6, - 0xf9, 0x89, 0xbd, 0x6d, 0x51, 0x47, 0xae, 0xe7, 0x03, 0x58, 0xb6, 0xf9, 0x80, 0xb7, 0x9c, 0x2b, - 0xa3, 0xcb, 0xed, 0xbc, 0x40, 0x35, 0xf6, 0xd9, 0xef, 0x3c, 0xff, 0xd4, 0xd1, 0x9e, 0xe0, 0xe5, - 0x42, 0x98, 0x63, 0x2a, 0x75, 0x58, 0x12, 0x00, 0xcc, 0xa4, 0xac, 0x3e, 0x33, 0x70, 0x1b, 0x44, - 0x6a, 0x7f, 0x4d, 0xc1, 0x0d, 0x79, 0x70, 0x9d, 0x2d, 0x17, 0xb2, 0x0b, 0x45, 0x84, 0x4e, 0x51, - 0xd7, 0x55, 0x61, 0x22, 0xcb, 0x5a, 0x0f, 0x94, 0x75, 0x2b, 0x3a, 0x71, 0xdf, 0xf1, 0xe4, 0x13, - 0xef, 0x9a, 0x32, 0xf7, 0x32, 0xfc, 0x27, 0x0d, 0x44, 0x9c, 0xc4, 0xd8, 0x57, 0x57, 0x36, 0x3f, - 0x0e, 0xcb, 0x66, 0x35, 0xfa, 0xc4, 0xe9, 0x37, 0x1c, 0x57, 0xcd, 0xd7, 0x57, 0xaf, 0x9a, 0x7a, - 0x48, 0x35, 0xdf, 0x9f, 0x2e, 0xb7, 0x6b, 0x11, 0xcd, 0x03, 0x79, 0xed, 0xc0, 0x8c, 0xb0, 0x64, - 0xdf, 0x63, 0x97, 0x24, 0x3e, 0x84, 0x92, 0x19, 0x57, 0x33, 0x09, 0xd5, 0x1a, 0x70, 0x43, 0xde, - 0xd8, 0xfd, 0xd4, 0xad, 0x07, 0xce, 0xba, 0x13, 0x73, 0x29, 0xe8, 0x6a, 0x0e, 0x2e, 0xfd, 0x14, - 0x6e, 0xc8, 0x4b, 0xd7, 0x8c, 0xbb, 0xfb, 0x1b, 0xde, 0xe5, 0xcf, 0x9f, 0x0d, 0x8a, 0xc6, 0x9e, - 0xd9, 0x3f, 0x31, 0x4e, 0x7d, 0x6e, 0xdb, 0x7c, 0x20, 0xe4, 0x56, 0xa0, 0x98, 0x5b, 0xf1, 0xd8, - 0x15, 0x0d, 0x69, 0xee, 0xcd, 0x50, 0x00, 0xe2, 0x66, 0x88, 0x36, 0x88, 0xf4, 0x89, 0xc6, 0xac, - 0xb9, 0x30, 0xd1, 0x40, 0xe8, 0x34, 0xa2, 0x21, 0x4c, 0xa6, 0x10, 0x0d, 0x11, 0x59, 0x25, 0x1a, - 0x57, 0xb0, 0x0c, 0x52, 0x34, 0xc4, 0xf0, 0x0c, 0xa2, 0x11, 0x34, 0xfc, 0x7a, 0x89, 0x86, 0x3a, - 0xb7, 0xeb, 0x14, 0x0d, 0x37, 0x23, 0x4f, 0x34, 0x44, 0x21, 0x62, 0x45, 0x03, 0x6b, 0x26, 0xa1, - 0x9e, 0x68, 0x04, 0xa9, 0x3b, 0x81, 0x68, 0xa8, 0xb8, 0x14, 0x74, 0x35, 0x07, 0x97, 0x5c, 0xd1, - 0x98, 0x79, 0x77, 0xbb, 0xa2, 0x11, 0xcc, 0xa6, 0xfe, 0xeb, 0x9b, 0x90, 0xdb, 0x13, 0xaf, 0x81, - 0x89, 0x01, 0x39, 0x7c, 0xc1, 0x49, 0x34, 0x55, 0x52, 0xc1, 0x97, 0xa6, 0xe5, 0x3b, 0xb1, 0x18, - 0x14, 0xa5, 0xb7, 0xfe, 0xfe, 0xe7, 0xff, 0xfd, 0x3e, 0xbd, 0x06, 0xab, 0x1c, 0xf4, 0x1d, 0x3c, - 0x3e, 0x12, 0x13, 0x96, 0xdd, 0x77, 0x50, 0xe4, 0xdb, 0x93, 0xbc, 0x54, 0x2b, 0xdf, 0x4d, 0x40, - 0xc5, 0x07, 0xb4, 0x00, 0xbc, 0x57, 0x40, 0xe4, 0x6e, 0x74, 0xc3, 0xcf, 0x3f, 0xc3, 0x7b, 0x49, - 0xb0, 0xc4, 0x98, 0xde, 0x2b, 0x1e, 0x75, 0xcc, 0xb1, 0x57, 0x4a, 0xea, 0x98, 0x8a, 0x37, 0x45, - 0x11, 0x31, 0x45, 0x0d, 0x5f, 0xb4, 0xec, 0x6e, 0x64, 0x0d, 0x7d, 0xaf, 0x78, 0x22, 0x6b, 0x18, - 0x78, 0x99, 0x13, 0x5f, 0x43, 0xde, 0xa4, 0x8f, 0xae, 0xa1, 0xff, 0x85, 0x49, 0x74, 0x0d, 0x03, - 0x9d, 0xfe, 0xc4, 0xf5, 0xe4, 0xd3, 0x8b, 0x59, 0x4f, 0xff, 0x0c, 0xef, 0x25, 0xc1, 0x12, 0x63, - 0x7a, 0xbd, 0x73, 0x75, 0xcc, 0xb1, 0x3e, 0xbe, 0x3a, 0xe6, 0x78, 0x0b, 0x3e, 0x2a, 0xe6, 0x97, - 0xb0, 0xe2, 0xef, 0xfb, 0x91, 0xfb, 0x13, 0x36, 0x32, 0xcb, 0x95, 0x64, 0x60, 0x7c, 0xe4, 0x5f, - 0xc2, 0x6a, 0xe0, 0x2d, 0x07, 0x51, 0x7a, 0x54, 0xbd, 0x55, 0x29, 0x3f, 0x98, 0x00, 0x99, 0x18, - 0x3c, 0xd0, 0x24, 0x57, 0x07, 0x57, 0xb5, 0xe5, 0xd5, 0xc1, 0x95, 0x1d, 0xf7, 0x98, 0xe0, 0x81, - 0x5e, 0xb8, 0x3a, 0xb8, 0xaa, 0xe9, 0xae, 0x0e, 0xae, 0x6e, 0xac, 0xc7, 0x92, 0x0c, 0xfb, 0x47, - 0x91, 0x24, 0x0b, 0xf6, 0x1c, 0x23, 0x49, 0x16, 0x6e, 0x20, 0xc6, 0x93, 0x4c, 0x36, 0xbb, 0xa2, - 0x49, 0x16, 0xea, 0xd0, 0x45, 0x93, 0x2c, 0xdc, 0x37, 0x4b, 0x24, 0x99, 0x9c, 0x70, 0x0c, 0xc9, - 0x42, 0x73, 0x7e, 0x30, 0x01, 0x72, 0xc2, 0x3a, 0xc7, 0x06, 0x57, 0x35, 0x79, 0xe3, 0xea, 0x3c, - 0x61, 0x70, 0x51, 0x67, 0xbc, 0xed, 0x47, 0xd6, 0x39, 0xd8, 0x47, 0x89, 0xac, 0x73, 0xa8, 0xd5, - 0x90, 0x50, 0x67, 0xd9, 0x88, 0x8a, 0xae, 0x73, 0xa8, 0x7b, 0x16, 0x5d, 0xe7, 0x70, 0x4f, 0x2b, - 0x71, 0x3f, 0xcb, 0x09, 0xc7, 0xec, 0xe7, 0xd0, 0x9c, 0x1f, 0x4c, 0x80, 0x4c, 0xfc, 0x71, 0x72, - 0x5b, 0x20, 0xea, 0x1f, 0xa7, 0x70, 0x83, 0xa5, 0x7c, 0x37, 0x01, 0x95, 0xb8, 0xce, 0xfe, 0x7e, - 0x83, 0x7a, 0x9d, 0x15, 0xbd, 0x94, 0x72, 0x25, 0x19, 0x18, 0x1f, 0x79, 0x08, 0x05, 0xdf, 0xad, - 0x99, 0xdc, 0x9b, 0xec, 0xa2, 0x5f, 0xbe, 0x9f, 0x88, 0x4b, 0x9c, 0xb0, 0xff, 0x52, 0xac, 0x9e, - 0xb0, 0xe2, 0x06, 0x5e, 0xae, 0x24, 0x03, 0x13, 0x23, 0xfb, 0x2f, 0xc0, 0xea, 0xc8, 0x8a, 0x4b, - 0x76, 0xb9, 0x92, 0x0c, 0x9c, 0x84, 0x55, 0xe2, 0x08, 0x1d, 0xc9, 0xaa, 0xc0, 0x19, 0x3d, 0x92, - 0x55, 0xc1, 0x73, 0x78, 0x22, 0xab, 0x30, 0x66, 0x0c, 0xab, 0x82, 0x61, 0x2b, 0xc9, 0xc0, 0x89, - 0x58, 0x85, 0xd7, 0xaa, 0x68, 0x56, 0x05, 0x6f, 0x82, 0xd1, 0xac, 0x0a, 0xdd, 0xcf, 0x12, 0x59, - 0x15, 0x37, 0x61, 0xc5, 0x15, 0x2d, 0x8e, 0x55, 0x13, 0x2f, 0xb5, 0xff, 0x86, 0x14, 0xc7, 0xaa, - 0x09, 0x22, 0xab, 0x2e, 0x5b, 0x11, 0x91, 0x77, 0x4b, 0x6f, 0xbe, 0xda, 0x5a, 0xf8, 0xe7, 0x57, - 0x5b, 0x0b, 0xbf, 0x1a, 0x6d, 0xa5, 0xde, 0x8c, 0xb6, 0x52, 0xff, 0x18, 0x6d, 0xa5, 0xfe, 0x3d, - 0xda, 0x4a, 0x1d, 0x2f, 0xf1, 0x7f, 0x58, 0x7d, 0xf8, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x96, - 0x0e, 0xd9, 0x9f, 0xc9, 0x2b, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/control.proto b/vendor/github.com/docker/swarmkit/api/control.proto index 66180b8a784e5..c90b78d9b83f2 100644 --- a/vendor/github.com/docker/swarmkit/api/control.proto +++ b/vendor/github.com/docker/swarmkit/api/control.proto @@ -6,6 +6,7 @@ import "github.com/docker/swarmkit/api/specs.proto"; import "github.com/docker/swarmkit/api/objects.proto"; import "github.com/docker/swarmkit/api/types.proto"; import "gogoproto/gogo.proto"; +import "google/protobuf/any.proto"; import "github.com/docker/swarmkit/protobuf/plugin/plugin.proto"; // Control defines the RPC methods for controlling a cluster. @@ -49,6 +50,16 @@ service Control { option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; }; + // ListServiceStatuses returns a `ListServiceStatusesResponse` with the + // status of the requested services, formed by computing the number of + // running vs desired tasks. It is provided as a shortcut or helper method, + // which allows a client to avoid having to calculate this value by listing + // all Tasks. If any service requested does not exist, it will be returned + // but with empty status values. + rpc ListServiceStatuses(ListServiceStatusesRequest) returns (ListServiceStatusesResponse) { + option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; + }; + rpc GetNetwork(GetNetworkRequest) returns (GetNetworkResponse) { option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; }; @@ -161,6 +172,80 @@ service Control { rpc RemoveConfig(RemoveConfigRequest) returns (RemoveConfigResponse) { option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; } + + // --- extension APIs --- + + // GetExtension returns a `GetExtensionResponse` with a `Extension` with the same + // id as `GetExtensionRequest.ExtensionId` + // - Returns `NotFound` if the Extension with the given id is not found. + // - Returns `InvalidArgument` if the `GetExtensionRequest.ExtensionId` is empty. + // - Returns an error if the get fails. + rpc GetExtension(GetExtensionRequest) returns (GetExtensionResponse) { + option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; + } + + // CreateExtension creates an `Extension` based on the provided `CreateExtensionRequest.Extension` + // and returns a `CreateExtensionResponse`. + // - Returns `InvalidArgument` if the `CreateExtensionRequest.Extension` is malformed, + // or fails validation. + // - Returns an error if the creation fails. + rpc CreateExtension(CreateExtensionRequest) returns (CreateExtensionResponse) { + option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; + } + + // RemoveExtension removes the extension referenced by `RemoveExtensionRequest.ID`. + // - Returns `InvalidArgument` if `RemoveExtensionRequest.ExtensionId` is empty. + // - Returns `NotFound` if the an extension named `RemoveExtensionRequest.ExtensionId` is not found. + // - Returns an error if the deletion fails. + rpc RemoveExtension(RemoveExtensionRequest) returns (RemoveExtensionResponse) { + option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; + } + + // --- resource APIs --- + + // GetResource returns a `GetResourceResponse` with a `Resource` with the same + // id as `GetResourceRequest.Resource` + // - Returns `NotFound` if the Resource with the given id is not found. + // - Returns `InvalidArgument` if the `GetResourceRequest.Resource` is empty. + // - Returns an error if getting fails. + rpc GetResource(GetResourceRequest) returns (GetResourceResponse) { + option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; + } + + // UpdateResource updates the resource with the given `UpdateResourceRequest.Resource.Id` using the given `UpdateResourceRequest.Resource` and returns a `UpdateResourceResponse`. + // - Returns `NotFound` if the Resource with the given `UpdateResourceRequest.Resource.Id` is not found. + // - Returns `InvalidArgument` if the UpdateResourceRequest.Resource.Id` is empty. + // - Returns an error if updating fails. + rpc UpdateResource(UpdateResourceRequest) returns (UpdateResourceResponse) { + option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; + } + + // ListResources returns a `ListResourcesResponse` with a list of `Resource`s stored in the raft store, + // or all resources matching any name in `ListConfigsRequest.Names`, any + // name prefix in `ListResourcesRequest.NamePrefixes`, any id in + // `ListResourcesRequest.ResourceIDs`, or any id prefix in `ListResourcesRequest.IDPrefixes`, + // extension name equal to `ListResourcesRequest.Extension`. + // - Returns an error if listing fails. + rpc ListResources(ListResourcesRequest) returns (ListResourcesResponse) { + option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; + } + + // CreateResource returns a `CreateResourceResponse` after creating a `Resource` based + // on the provided `CreateResourceRequest.Resource`. + // - Returns `InvalidArgument` if the `CreateResourceRequest.Resource` is malformed, + // or if the config data is too long or contains invalid characters. + // - Returns an error if the creation fails. + rpc CreateResource(CreateResourceRequest) returns (CreateResourceResponse) { + option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; + } + + // RemoveResource removes the `Resource` referenced by `RemoveResourceRequest.ResourceID`. + // - Returns `InvalidArgument` if `RemoveResourceRequest.ResourceID` is empty. + // - Returns `NotFound` if the a resource named `RemoveResourceRequest.ResourceID` is not found. + // - Returns an error if the deletion fails. + rpc RemoveResource(RemoveResourceRequest) returns (RemoveResourceResponse) { + option (docker.protobuf.plugin.tls_authorization) = { roles: "swarm-manager" }; + } } message GetNodeRequest { @@ -322,6 +407,44 @@ message ListServicesResponse { repeated Service services = 1; } +// ListServiceStatusesRequest is a request to get the aggregate status of a +// service by computing the number of running vs desired tasks. It includes +// only a service ID. +message ListServiceStatusesRequest { + // Services is a list of service IDs to get statuses for. + repeated string services = 1; +} + +// ListServiceStatusesResponse is a response containing the aggregate status of +// a service, formed by computing the number of running vs desired tasks. The +// values returned are only valid for the point in time at which the request is +// made. +message ListServiceStatusesResponse { + message ServiceStatus { + // ServiceID is the ID of the service this status describes + string service_id = 1; + + // DesiredTasks is the number of tasks desired to be running according to the + // service definition at request time. It is a uint64 because that is what + // the replicas field on the service spec is + uint64 desired_tasks = 2; + + // RunningTasks is the number of tasks currently in the Running state at + // request time. This may be larger than desired tasks if, for example, a + // service has been scaled down. + uint64 running_tasks = 3; + + // CompletedTasks is the number of tasks in state Completed, if this + // service is in mode ReplicatedJob or GlobalJob. This must be + // cross-referenced with the service type, because the default value of 0 + // may mean that a service is not in a Job mode, or it may mean the Job has + // yet to complete any Tasks. + uint64 completed_tasks = 4; + } + + repeated ServiceStatus statuses = 1; +} + message CreateNetworkRequest { NetworkSpec spec = 1; } @@ -556,3 +679,111 @@ message RemoveConfigRequest { // RemoveConfigResponse is an empty object indicating the successful removal of // a config. message RemoveConfigResponse {} + +// CreateExtensionRequest creates a new extension as specified by the provided +// parameters +message CreateExtensionRequest { + Annotations annotations = 1; + string description = 2; +} + +// CreateExtensionResponse contains the newly created `Extension` corresponding +// to the parameters in the CreateExtensionRequest. +message CreateExtensionResponse { + Extension extension = 1; +} + +// RemoveExtensionRequest contains the ID of the extension that should be removed. This +// removes all versions of the extension. +message RemoveExtensionRequest { + string extension_id = 1; +} + +// RemoveExtensionResponse is an empty object indicating the successful removal +// of an extension. +message RemoveExtensionResponse { +} + +// GetResourceRequest is the request to get a Extension object given a extension id. +message GetExtensionRequest { + string extension_id = 1; +} + +// GetExtensionResponse contains the Extension corresponding to the id in +// `GetExtensionRequest`. +message GetExtensionResponse { + Extension extension = 1; +} + +// CreateResourceRequest creates a new resource specified by the included +// resource object. An existing resource will not be updated. +message CreateResourceRequest { + Annotations annotations = 1; + string kind = 2; + google.protobuf.Any payload = 3; +} + +// CreateResourceResponse contains the newly created `Resource` corresponding +// to the resource in the CreateResourceRequest. +message CreateResourceResponse { + Resource resource = 1; +} + +// RemoveResourceRequest contains the ID of the resource that should be removed. This +// removes all versions of the resource. +message RemoveResourceRequest { + string resource_id = 1; +} + +// RemoveResourceResponse is an empty object indicating the successful removal +// of a resource. +message RemoveResourceResponse { +} + +// UpdateResourceRequest updates the resource specified by the given resource object. +message UpdateResourceRequest { + string resource_id = 1; + Version resource_version = 2; + // Annotations describes the annotations to update. If the Annotations should + // be unchanged, then this field should be left empty. Note that the name of + // a Resource cannot be changed, only its labels. + Annotations annotations = 3; + // Payload describes the new payload of the resource. If the Payload should + // be unchanged, then this field should be left empty. + google.protobuf.Any payload = 4; +} + +message UpdateResourceResponse { + Resource resource = 1; +} + +// GetResourceRequest is the request to get a Resource object given a resource id. +message GetResourceRequest { + string resource_id = 1; +} + +// GetResourceResponse contains the Resource corresponding to the id in +// `GetResourceRequest`. +message GetResourceResponse { + Resource resource = 1; +} + +// ListResourcesRequest is the request to list all resources in the raft store, +// or all resources filtered by (name or name prefix or id prefix), labels and extension. +message ListResourcesRequest { + message Filters { + repeated string names = 1; + repeated string id_prefixes = 2; + map labels = 3; + repeated string name_prefixes = 4; + string kind = 5; + } + + Filters filters = 1; +} + +// ListResourcesResponse contains a list of all the resources that match the name or +// name prefix filters provided in `ListResourcesRequest`. +message ListResourcesResponse { + repeated Resource resources = 1; +} diff --git a/vendor/github.com/docker/swarmkit/api/dispatcher.pb.go b/vendor/github.com/docker/swarmkit/api/dispatcher.pb.go index f72d3d9995b5c..96b13a1218a30 100644 --- a/vendor/github.com/docker/swarmkit/api/dispatcher.pb.go +++ b/vendor/github.com/docker/swarmkit/api/dispatcher.pb.go @@ -3,33 +3,29 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" -import _ "github.com/docker/swarmkit/protobuf/plugin" -import _ "github.com/gogo/protobuf/types" - -import time "time" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import types "github.com/gogo/protobuf/types" - -import raftselector "github.com/docker/swarmkit/manager/raftselector" -import codes "google.golang.org/grpc/codes" -import status "google.golang.org/grpc/status" -import metadata "google.golang.org/grpc/metadata" -import peer "google.golang.org/grpc/peer" -import rafttime "time" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + context "context" + fmt "fmt" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + raftselector "github.com/docker/swarmkit/manager/raftselector" + _ "github.com/docker/swarmkit/protobuf/plugin" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "github.com/gogo/protobuf/types" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + metadata "google.golang.org/grpc/metadata" + peer "google.golang.org/grpc/peer" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + rafttime "time" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -37,6 +33,12 @@ var _ = fmt.Errorf var _ = math.Inf var _ = time.Kitchen +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type AssignmentChange_AssignmentAction int32 const ( @@ -48,6 +50,7 @@ var AssignmentChange_AssignmentAction_name = map[int32]string{ 0: "UPDATE", 1: "REMOVE", } + var AssignmentChange_AssignmentAction_value = map[string]int32{ "UPDATE": 0, "REMOVE": 1, @@ -56,8 +59,9 @@ var AssignmentChange_AssignmentAction_value = map[string]int32{ func (x AssignmentChange_AssignmentAction) String() string { return proto.EnumName(AssignmentChange_AssignmentAction_name, int32(x)) } + func (AssignmentChange_AssignmentAction) EnumDescriptor() ([]byte, []int) { - return fileDescriptorDispatcher, []int{10, 0} + return fileDescriptor_71002346457e55a8, []int{10, 0} } // AssignmentType specifies whether this assignment message carries @@ -73,6 +77,7 @@ var AssignmentsMessage_Type_name = map[int32]string{ 0: "COMPLETE", 1: "INCREMENTAL", } + var AssignmentsMessage_Type_value = map[string]int32{ "COMPLETE": 0, "INCREMENTAL": 1, @@ -81,13 +86,14 @@ var AssignmentsMessage_Type_value = map[string]int32{ func (x AssignmentsMessage_Type) String() string { return proto.EnumName(AssignmentsMessage_Type_name, int32(x)) } + func (AssignmentsMessage_Type) EnumDescriptor() ([]byte, []int) { - return fileDescriptorDispatcher, []int{11, 0} + return fileDescriptor_71002346457e55a8, []int{11, 0} } // SessionRequest starts a session. type SessionRequest struct { - Description *NodeDescription `protobuf:"bytes,1,opt,name=description" json:"description,omitempty"` + Description *NodeDescription `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` // SessionID can be provided to attempt resuming an existing session. If the // SessionID is empty or invalid, a new SessionID will be assigned. // @@ -95,9 +101,37 @@ type SessionRequest struct { SessionID string `protobuf:"bytes,2,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` } -func (m *SessionRequest) Reset() { *m = SessionRequest{} } -func (*SessionRequest) ProtoMessage() {} -func (*SessionRequest) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{0} } +func (m *SessionRequest) Reset() { *m = SessionRequest{} } +func (*SessionRequest) ProtoMessage() {} +func (*SessionRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_71002346457e55a8, []int{0} +} +func (m *SessionRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SessionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SessionRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SessionRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SessionRequest.Merge(m, src) +} +func (m *SessionRequest) XXX_Size() int { + return m.Size() +} +func (m *SessionRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SessionRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SessionRequest proto.InternalMessageInfo // SessionMessage instructs an agent on various actions as part of the current // session. An agent should act immediately on the contents. @@ -147,55 +181,165 @@ type SessionMessage struct { // directly in the RPC message set. SessionID string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` // Node identifies the registering node. - Node *Node `protobuf:"bytes,2,opt,name=node" json:"node,omitempty"` + Node *Node `protobuf:"bytes,2,opt,name=node,proto3" json:"node,omitempty"` // Managers provides a weight list of alternative dispatchers - Managers []*WeightedPeer `protobuf:"bytes,3,rep,name=managers" json:"managers,omitempty"` + Managers []*WeightedPeer `protobuf:"bytes,3,rep,name=managers,proto3" json:"managers,omitempty"` // Symmetric encryption key distributed by the lead manager. Used by agents // for securing network bootstrapping and communication. - NetworkBootstrapKeys []*EncryptionKey `protobuf:"bytes,4,rep,name=network_bootstrap_keys,json=networkBootstrapKeys" json:"network_bootstrap_keys,omitempty"` + NetworkBootstrapKeys []*EncryptionKey `protobuf:"bytes,4,rep,name=network_bootstrap_keys,json=networkBootstrapKeys,proto3" json:"network_bootstrap_keys,omitempty"` // Which root certificates to trust RootCA []byte `protobuf:"bytes,5,opt,name=RootCA,proto3" json:"RootCA,omitempty"` } -func (m *SessionMessage) Reset() { *m = SessionMessage{} } -func (*SessionMessage) ProtoMessage() {} -func (*SessionMessage) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{1} } +func (m *SessionMessage) Reset() { *m = SessionMessage{} } +func (*SessionMessage) ProtoMessage() {} +func (*SessionMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_71002346457e55a8, []int{1} +} +func (m *SessionMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SessionMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SessionMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SessionMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_SessionMessage.Merge(m, src) +} +func (m *SessionMessage) XXX_Size() int { + return m.Size() +} +func (m *SessionMessage) XXX_DiscardUnknown() { + xxx_messageInfo_SessionMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_SessionMessage proto.InternalMessageInfo // HeartbeatRequest provides identifying properties for a single heartbeat. type HeartbeatRequest struct { SessionID string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` } -func (m *HeartbeatRequest) Reset() { *m = HeartbeatRequest{} } -func (*HeartbeatRequest) ProtoMessage() {} -func (*HeartbeatRequest) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{2} } +func (m *HeartbeatRequest) Reset() { *m = HeartbeatRequest{} } +func (*HeartbeatRequest) ProtoMessage() {} +func (*HeartbeatRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_71002346457e55a8, []int{2} +} +func (m *HeartbeatRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HeartbeatRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HeartbeatRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HeartbeatRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_HeartbeatRequest.Merge(m, src) +} +func (m *HeartbeatRequest) XXX_Size() int { + return m.Size() +} +func (m *HeartbeatRequest) XXX_DiscardUnknown() { + xxx_messageInfo_HeartbeatRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_HeartbeatRequest proto.InternalMessageInfo type HeartbeatResponse struct { // Period is the duration to wait before sending the next heartbeat. // Well-behaved agents should update this on every heartbeat round trip. - Period time.Duration `protobuf:"bytes,1,opt,name=period,stdduration" json:"period"` + Period time.Duration `protobuf:"bytes,1,opt,name=period,proto3,stdduration" json:"period"` +} + +func (m *HeartbeatResponse) Reset() { *m = HeartbeatResponse{} } +func (*HeartbeatResponse) ProtoMessage() {} +func (*HeartbeatResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_71002346457e55a8, []int{3} +} +func (m *HeartbeatResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HeartbeatResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HeartbeatResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HeartbeatResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_HeartbeatResponse.Merge(m, src) +} +func (m *HeartbeatResponse) XXX_Size() int { + return m.Size() +} +func (m *HeartbeatResponse) XXX_DiscardUnknown() { + xxx_messageInfo_HeartbeatResponse.DiscardUnknown(m) } -func (m *HeartbeatResponse) Reset() { *m = HeartbeatResponse{} } -func (*HeartbeatResponse) ProtoMessage() {} -func (*HeartbeatResponse) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{3} } +var xxx_messageInfo_HeartbeatResponse proto.InternalMessageInfo type UpdateTaskStatusRequest struct { // Tasks should contain all statuses for running tasks. Only the status // field must be set. The spec is not required. SessionID string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` - Updates []*UpdateTaskStatusRequest_TaskStatusUpdate `protobuf:"bytes,3,rep,name=updates" json:"updates,omitempty"` + Updates []*UpdateTaskStatusRequest_TaskStatusUpdate `protobuf:"bytes,3,rep,name=updates,proto3" json:"updates,omitempty"` } func (m *UpdateTaskStatusRequest) Reset() { *m = UpdateTaskStatusRequest{} } func (*UpdateTaskStatusRequest) ProtoMessage() {} func (*UpdateTaskStatusRequest) Descriptor() ([]byte, []int) { - return fileDescriptorDispatcher, []int{4} + return fileDescriptor_71002346457e55a8, []int{4} +} +func (m *UpdateTaskStatusRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateTaskStatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateTaskStatusRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateTaskStatusRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateTaskStatusRequest.Merge(m, src) +} +func (m *UpdateTaskStatusRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateTaskStatusRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateTaskStatusRequest.DiscardUnknown(m) } +var xxx_messageInfo_UpdateTaskStatusRequest proto.InternalMessageInfo + type UpdateTaskStatusRequest_TaskStatusUpdate struct { TaskID string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` - Status *TaskStatus `protobuf:"bytes,2,opt,name=status" json:"status,omitempty"` + Status *TaskStatus `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` } func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Reset() { @@ -203,43 +347,179 @@ func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Reset() { } func (*UpdateTaskStatusRequest_TaskStatusUpdate) ProtoMessage() {} func (*UpdateTaskStatusRequest_TaskStatusUpdate) Descriptor() ([]byte, []int) { - return fileDescriptorDispatcher, []int{4, 0} + return fileDescriptor_71002346457e55a8, []int{4, 0} +} +func (m *UpdateTaskStatusRequest_TaskStatusUpdate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateTaskStatusRequest_TaskStatusUpdate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateTaskStatusRequest_TaskStatusUpdate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateTaskStatusRequest_TaskStatusUpdate) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateTaskStatusRequest_TaskStatusUpdate.Merge(m, src) +} +func (m *UpdateTaskStatusRequest_TaskStatusUpdate) XXX_Size() int { + return m.Size() +} +func (m *UpdateTaskStatusRequest_TaskStatusUpdate) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateTaskStatusRequest_TaskStatusUpdate.DiscardUnknown(m) } +var xxx_messageInfo_UpdateTaskStatusRequest_TaskStatusUpdate proto.InternalMessageInfo + type UpdateTaskStatusResponse struct { } func (m *UpdateTaskStatusResponse) Reset() { *m = UpdateTaskStatusResponse{} } func (*UpdateTaskStatusResponse) ProtoMessage() {} func (*UpdateTaskStatusResponse) Descriptor() ([]byte, []int) { - return fileDescriptorDispatcher, []int{5} + return fileDescriptor_71002346457e55a8, []int{5} +} +func (m *UpdateTaskStatusResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateTaskStatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateTaskStatusResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateTaskStatusResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateTaskStatusResponse.Merge(m, src) +} +func (m *UpdateTaskStatusResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateTaskStatusResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateTaskStatusResponse.DiscardUnknown(m) } +var xxx_messageInfo_UpdateTaskStatusResponse proto.InternalMessageInfo + type TasksRequest struct { SessionID string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` } -func (m *TasksRequest) Reset() { *m = TasksRequest{} } -func (*TasksRequest) ProtoMessage() {} -func (*TasksRequest) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{6} } +func (m *TasksRequest) Reset() { *m = TasksRequest{} } +func (*TasksRequest) ProtoMessage() {} +func (*TasksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_71002346457e55a8, []int{6} +} +func (m *TasksRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TasksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TasksRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TasksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_TasksRequest.Merge(m, src) +} +func (m *TasksRequest) XXX_Size() int { + return m.Size() +} +func (m *TasksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_TasksRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_TasksRequest proto.InternalMessageInfo type TasksMessage struct { // Tasks is the set of tasks that should be running on the node. // Tasks outside of this set running on the node should be terminated. - Tasks []*Task `protobuf:"bytes,1,rep,name=tasks" json:"tasks,omitempty"` + Tasks []*Task `protobuf:"bytes,1,rep,name=tasks,proto3" json:"tasks,omitempty"` +} + +func (m *TasksMessage) Reset() { *m = TasksMessage{} } +func (*TasksMessage) ProtoMessage() {} +func (*TasksMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_71002346457e55a8, []int{7} +} +func (m *TasksMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TasksMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TasksMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TasksMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_TasksMessage.Merge(m, src) +} +func (m *TasksMessage) XXX_Size() int { + return m.Size() +} +func (m *TasksMessage) XXX_DiscardUnknown() { + xxx_messageInfo_TasksMessage.DiscardUnknown(m) } -func (m *TasksMessage) Reset() { *m = TasksMessage{} } -func (*TasksMessage) ProtoMessage() {} -func (*TasksMessage) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{7} } +var xxx_messageInfo_TasksMessage proto.InternalMessageInfo type AssignmentsRequest struct { SessionID string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` } -func (m *AssignmentsRequest) Reset() { *m = AssignmentsRequest{} } -func (*AssignmentsRequest) ProtoMessage() {} -func (*AssignmentsRequest) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{8} } +func (m *AssignmentsRequest) Reset() { *m = AssignmentsRequest{} } +func (*AssignmentsRequest) ProtoMessage() {} +func (*AssignmentsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_71002346457e55a8, []int{8} +} +func (m *AssignmentsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AssignmentsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AssignmentsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AssignmentsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AssignmentsRequest.Merge(m, src) +} +func (m *AssignmentsRequest) XXX_Size() int { + return m.Size() +} +func (m *AssignmentsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AssignmentsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AssignmentsRequest proto.InternalMessageInfo type Assignment struct { // Types that are valid to be assigned to Item: @@ -249,9 +529,37 @@ type Assignment struct { Item isAssignment_Item `protobuf_oneof:"item"` } -func (m *Assignment) Reset() { *m = Assignment{} } -func (*Assignment) ProtoMessage() {} -func (*Assignment) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{9} } +func (m *Assignment) Reset() { *m = Assignment{} } +func (*Assignment) ProtoMessage() {} +func (*Assignment) Descriptor() ([]byte, []int) { + return fileDescriptor_71002346457e55a8, []int{9} +} +func (m *Assignment) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Assignment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Assignment.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Assignment) XXX_Merge(src proto.Message) { + xxx_messageInfo_Assignment.Merge(m, src) +} +func (m *Assignment) XXX_Size() int { + return m.Size() +} +func (m *Assignment) XXX_DiscardUnknown() { + xxx_messageInfo_Assignment.DiscardUnknown(m) +} + +var xxx_messageInfo_Assignment proto.InternalMessageInfo type isAssignment_Item interface { isAssignment_Item() @@ -260,13 +568,13 @@ type isAssignment_Item interface { } type Assignment_Task struct { - Task *Task `protobuf:"bytes,1,opt,name=task,oneof"` + Task *Task `protobuf:"bytes,1,opt,name=task,proto3,oneof" json:"task,omitempty"` } type Assignment_Secret struct { - Secret *Secret `protobuf:"bytes,2,opt,name=secret,oneof"` + Secret *Secret `protobuf:"bytes,2,opt,name=secret,proto3,oneof" json:"secret,omitempty"` } type Assignment_Config struct { - Config *Config `protobuf:"bytes,3,opt,name=config,oneof"` + Config *Config `protobuf:"bytes,3,opt,name=config,proto3,oneof" json:"config,omitempty"` } func (*Assignment_Task) isAssignment_Item() {} @@ -301,107 +609,51 @@ func (m *Assignment) GetConfig() *Config { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*Assignment) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _Assignment_OneofMarshaler, _Assignment_OneofUnmarshaler, _Assignment_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Assignment) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*Assignment_Task)(nil), (*Assignment_Secret)(nil), (*Assignment_Config)(nil), } } -func _Assignment_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*Assignment) - // item - switch x := m.Item.(type) { - case *Assignment_Task: - _ = b.EncodeVarint(1<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Task); err != nil { - return err - } - case *Assignment_Secret: - _ = b.EncodeVarint(2<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Secret); err != nil { - return err - } - case *Assignment_Config: - _ = b.EncodeVarint(3<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Config); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("Assignment.Item has unexpected type %T", x) - } - return nil +type AssignmentChange struct { + Assignment *Assignment `protobuf:"bytes,1,opt,name=assignment,proto3" json:"assignment,omitempty"` + Action AssignmentChange_AssignmentAction `protobuf:"varint,2,opt,name=action,proto3,enum=docker.swarmkit.v1.AssignmentChange_AssignmentAction" json:"action,omitempty"` } -func _Assignment_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*Assignment) - switch tag { - case 1: // item.task - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Task) - err := b.DecodeMessage(msg) - m.Item = &Assignment_Task{msg} - return true, err - case 2: // item.secret - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Secret) - err := b.DecodeMessage(msg) - m.Item = &Assignment_Secret{msg} - return true, err - case 3: // item.config - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Config) - err := b.DecodeMessage(msg) - m.Item = &Assignment_Config{msg} - return true, err - default: - return false, nil - } -} - -func _Assignment_OneofSizer(msg proto.Message) (n int) { - m := msg.(*Assignment) - // item - switch x := m.Item.(type) { - case *Assignment_Task: - s := proto.Size(x.Task) - n += proto.SizeVarint(1<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Assignment_Secret: - s := proto.Size(x.Secret) - n += proto.SizeVarint(2<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Assignment_Config: - s := proto.Size(x.Config) - n += proto.SizeVarint(3<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) +func (m *AssignmentChange) Reset() { *m = AssignmentChange{} } +func (*AssignmentChange) ProtoMessage() {} +func (*AssignmentChange) Descriptor() ([]byte, []int) { + return fileDescriptor_71002346457e55a8, []int{10} +} +func (m *AssignmentChange) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AssignmentChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AssignmentChange.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return n } - -type AssignmentChange struct { - Assignment *Assignment `protobuf:"bytes,1,opt,name=assignment" json:"assignment,omitempty"` - Action AssignmentChange_AssignmentAction `protobuf:"varint,2,opt,name=action,proto3,enum=docker.swarmkit.v1.AssignmentChange_AssignmentAction" json:"action,omitempty"` +func (m *AssignmentChange) XXX_Merge(src proto.Message) { + xxx_messageInfo_AssignmentChange.Merge(m, src) +} +func (m *AssignmentChange) XXX_Size() int { + return m.Size() +} +func (m *AssignmentChange) XXX_DiscardUnknown() { + xxx_messageInfo_AssignmentChange.DiscardUnknown(m) } -func (m *AssignmentChange) Reset() { *m = AssignmentChange{} } -func (*AssignmentChange) ProtoMessage() {} -func (*AssignmentChange) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{10} } +var xxx_messageInfo_AssignmentChange proto.InternalMessageInfo type AssignmentsMessage struct { Type AssignmentsMessage_Type `protobuf:"varint,1,opt,name=type,proto3,enum=docker.swarmkit.v1.AssignmentsMessage_Type" json:"type,omitempty"` @@ -416,14 +668,44 @@ type AssignmentsMessage struct { // against missed messages. ResultsIn string `protobuf:"bytes,3,opt,name=results_in,json=resultsIn,proto3" json:"results_in,omitempty"` // AssignmentChange is a set of changes to apply on this node. - Changes []*AssignmentChange `protobuf:"bytes,4,rep,name=changes" json:"changes,omitempty"` + Changes []*AssignmentChange `protobuf:"bytes,4,rep,name=changes,proto3" json:"changes,omitempty"` } -func (m *AssignmentsMessage) Reset() { *m = AssignmentsMessage{} } -func (*AssignmentsMessage) ProtoMessage() {} -func (*AssignmentsMessage) Descriptor() ([]byte, []int) { return fileDescriptorDispatcher, []int{11} } +func (m *AssignmentsMessage) Reset() { *m = AssignmentsMessage{} } +func (*AssignmentsMessage) ProtoMessage() {} +func (*AssignmentsMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_71002346457e55a8, []int{11} +} +func (m *AssignmentsMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AssignmentsMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AssignmentsMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AssignmentsMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_AssignmentsMessage.Merge(m, src) +} +func (m *AssignmentsMessage) XXX_Size() int { + return m.Size() +} +func (m *AssignmentsMessage) XXX_DiscardUnknown() { + xxx_messageInfo_AssignmentsMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_AssignmentsMessage proto.InternalMessageInfo func init() { + proto.RegisterEnum("docker.swarmkit.v1.AssignmentChange_AssignmentAction", AssignmentChange_AssignmentAction_name, AssignmentChange_AssignmentAction_value) + proto.RegisterEnum("docker.swarmkit.v1.AssignmentsMessage_Type", AssignmentsMessage_Type_name, AssignmentsMessage_Type_value) proto.RegisterType((*SessionRequest)(nil), "docker.swarmkit.v1.SessionRequest") proto.RegisterType((*SessionMessage)(nil), "docker.swarmkit.v1.SessionMessage") proto.RegisterType((*HeartbeatRequest)(nil), "docker.swarmkit.v1.HeartbeatRequest") @@ -437,8 +719,78 @@ func init() { proto.RegisterType((*Assignment)(nil), "docker.swarmkit.v1.Assignment") proto.RegisterType((*AssignmentChange)(nil), "docker.swarmkit.v1.AssignmentChange") proto.RegisterType((*AssignmentsMessage)(nil), "docker.swarmkit.v1.AssignmentsMessage") - proto.RegisterEnum("docker.swarmkit.v1.AssignmentChange_AssignmentAction", AssignmentChange_AssignmentAction_name, AssignmentChange_AssignmentAction_value) - proto.RegisterEnum("docker.swarmkit.v1.AssignmentsMessage_Type", AssignmentsMessage_Type_name, AssignmentsMessage_Type_value) +} + +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/dispatcher.proto", fileDescriptor_71002346457e55a8) +} + +var fileDescriptor_71002346457e55a8 = []byte{ + // 1019 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcf, 0x4f, 0x1b, 0x47, + 0x18, 0xf5, 0x18, 0xb3, 0xe0, 0xcf, 0x84, 0xba, 0xa3, 0x88, 0xba, 0x96, 0xb2, 0xb8, 0x4b, 0x82, + 0x50, 0x43, 0xd7, 0xa9, 0xfb, 0xeb, 0x50, 0x44, 0x8b, 0xb1, 0x25, 0xac, 0x04, 0x82, 0x06, 0x92, + 0x1c, 0xd1, 0x7a, 0x77, 0xb2, 0x6c, 0x8d, 0x77, 0xb6, 0x3b, 0xe3, 0xa4, 0x3e, 0x54, 0xea, 0xa1, + 0x91, 0xaa, 0x9e, 0xa2, 0x9e, 0x90, 0xaa, 0xfe, 0x0b, 0x55, 0xff, 0x0c, 0xd4, 0x53, 0x8e, 0x39, + 0xd1, 0xc6, 0xdc, 0xfb, 0x07, 0xf4, 0x54, 0xed, 0xec, 0xac, 0xed, 0x12, 0x1b, 0x0c, 0x27, 0x7b, + 0x67, 0xde, 0x7b, 0xf3, 0xf6, 0x7d, 0xdf, 0x7e, 0x03, 0x65, 0xd7, 0x13, 0x87, 0x9d, 0xa6, 0x69, + 0xb3, 0x76, 0xd9, 0x61, 0x76, 0x8b, 0x86, 0x65, 0xfe, 0xdc, 0x0a, 0xdb, 0x2d, 0x4f, 0x94, 0xad, + 0xc0, 0x2b, 0x3b, 0x1e, 0x0f, 0x2c, 0x61, 0x1f, 0xd2, 0xd0, 0x0c, 0x42, 0x26, 0x18, 0xc6, 0x31, + 0xca, 0x4c, 0x50, 0xe6, 0xb3, 0x8f, 0x8b, 0x1f, 0x5e, 0x22, 0x22, 0xba, 0x01, 0xe5, 0x31, 0xbf, + 0xb8, 0x7a, 0x09, 0x96, 0x35, 0xbf, 0xa1, 0xb6, 0x48, 0xd0, 0x37, 0x5d, 0xe6, 0x32, 0xf9, 0xb7, + 0x1c, 0xfd, 0x53, 0xab, 0x5f, 0x5c, 0xa0, 0x21, 0x11, 0xcd, 0xce, 0xd3, 0x72, 0x70, 0xd4, 0x71, + 0x3d, 0x5f, 0xfd, 0x28, 0xa2, 0xee, 0x32, 0xe6, 0x1e, 0xd1, 0x01, 0xc8, 0xe9, 0x84, 0x96, 0xf0, + 0x98, 0xda, 0x37, 0x5e, 0x20, 0x98, 0xdf, 0xa3, 0x9c, 0x7b, 0xcc, 0x27, 0xf4, 0xdb, 0x0e, 0xe5, + 0x02, 0xd7, 0x21, 0xe7, 0x50, 0x6e, 0x87, 0x5e, 0x10, 0xe1, 0x0a, 0xa8, 0x84, 0x56, 0x72, 0x95, + 0x25, 0xf3, 0xed, 0x14, 0xcc, 0x1d, 0xe6, 0xd0, 0xda, 0x00, 0x4a, 0x86, 0x79, 0x78, 0x15, 0x80, + 0xc7, 0xc2, 0x07, 0x9e, 0x53, 0x48, 0x97, 0xd0, 0x4a, 0xb6, 0x7a, 0xa3, 0x77, 0xba, 0x98, 0x55, + 0xc7, 0x35, 0x6a, 0x24, 0xab, 0x00, 0x0d, 0xc7, 0xf8, 0x35, 0xdd, 0xf7, 0xb1, 0x4d, 0x39, 0xb7, + 0x5c, 0x7a, 0x4e, 0x00, 0x5d, 0x2c, 0x80, 0x57, 0x21, 0xe3, 0x33, 0x87, 0xca, 0x83, 0x72, 0x95, + 0xc2, 0x38, 0xbb, 0x44, 0xa2, 0xf0, 0x1a, 0xcc, 0xb6, 0x2d, 0xdf, 0x72, 0x69, 0xc8, 0x0b, 0x53, + 0xa5, 0xa9, 0x95, 0x5c, 0xa5, 0x34, 0x8a, 0xf1, 0x84, 0x7a, 0xee, 0xa1, 0xa0, 0xce, 0x2e, 0xa5, + 0x21, 0xe9, 0x33, 0xf0, 0x13, 0x58, 0xf0, 0xa9, 0x78, 0xce, 0xc2, 0xd6, 0x41, 0x93, 0x31, 0xc1, + 0x45, 0x68, 0x05, 0x07, 0x2d, 0xda, 0xe5, 0x85, 0x8c, 0xd4, 0xfa, 0x60, 0x94, 0x56, 0xdd, 0xb7, + 0xc3, 0xae, 0x8c, 0xe6, 0x3e, 0xed, 0x92, 0x9b, 0x4a, 0xa0, 0x9a, 0xf0, 0xef, 0xd3, 0x2e, 0xc7, + 0x0b, 0xa0, 0x11, 0xc6, 0xc4, 0xe6, 0x46, 0x61, 0xba, 0x84, 0x56, 0xe6, 0x88, 0x7a, 0x32, 0xbe, + 0x86, 0xfc, 0x16, 0xb5, 0x42, 0xd1, 0xa4, 0x96, 0x48, 0xca, 0x74, 0xa5, 0x78, 0x8c, 0x5d, 0x78, + 0x77, 0x48, 0x81, 0x07, 0xcc, 0xe7, 0x14, 0x7f, 0x09, 0x5a, 0x40, 0x43, 0x8f, 0x39, 0xaa, 0xc8, + 0xef, 0x9b, 0x71, 0xb7, 0x98, 0x49, 0xb7, 0x98, 0x35, 0xd5, 0x2d, 0xd5, 0xd9, 0x93, 0xd3, 0xc5, + 0xd4, 0xf1, 0x5f, 0x8b, 0x88, 0x28, 0x8a, 0xf1, 0x32, 0x0d, 0xef, 0x3d, 0x0a, 0x1c, 0x4b, 0xd0, + 0x7d, 0x8b, 0xb7, 0xf6, 0x84, 0x25, 0x3a, 0xfc, 0x5a, 0xde, 0xf0, 0x63, 0x98, 0xe9, 0x48, 0xa1, + 0xa4, 0x16, 0x6b, 0xa3, 0xf2, 0x1b, 0x73, 0x96, 0x39, 0x58, 0x89, 0x11, 0x24, 0x11, 0x2b, 0x32, + 0xc8, 0x9f, 0xdf, 0xc4, 0x4b, 0x30, 0x23, 0x2c, 0xde, 0x1a, 0xd8, 0x82, 0xde, 0xe9, 0xa2, 0x16, + 0xc1, 0x1a, 0x35, 0xa2, 0x45, 0x5b, 0x0d, 0x07, 0x7f, 0x0e, 0x1a, 0x97, 0x24, 0xd5, 0x4d, 0xfa, + 0x28, 0x3f, 0x43, 0x4e, 0x14, 0xda, 0x28, 0x42, 0xe1, 0x6d, 0x97, 0x71, 0xd6, 0xc6, 0x1a, 0xcc, + 0x45, 0xab, 0xd7, 0x8b, 0xc8, 0x58, 0x57, 0xec, 0xe4, 0xdb, 0x30, 0x61, 0x3a, 0xf2, 0xca, 0x0b, + 0x48, 0x06, 0x56, 0x18, 0x67, 0x90, 0xc4, 0x30, 0xa3, 0x0a, 0x78, 0x83, 0x73, 0xcf, 0xf5, 0xdb, + 0xd4, 0x17, 0xd7, 0xf4, 0xf0, 0x07, 0x02, 0x18, 0x88, 0x60, 0x13, 0x32, 0x91, 0xb6, 0x6a, 0x9d, + 0xb1, 0x0e, 0xb6, 0x52, 0x44, 0xe2, 0xf0, 0xa7, 0xa0, 0x71, 0x6a, 0x87, 0x54, 0xa8, 0x50, 0x8b, + 0xa3, 0x18, 0x7b, 0x12, 0xb1, 0x95, 0x22, 0x0a, 0x1b, 0xb1, 0x6c, 0xe6, 0x3f, 0xf5, 0xdc, 0xc2, + 0xd4, 0x78, 0xd6, 0xa6, 0x44, 0x44, 0xac, 0x18, 0x5b, 0xd5, 0x20, 0xe3, 0x09, 0xda, 0x36, 0x5e, + 0xa4, 0x21, 0x3f, 0xb0, 0xbc, 0x79, 0x68, 0xf9, 0x2e, 0xc5, 0xeb, 0x00, 0x56, 0x7f, 0x4d, 0xd9, + 0x1f, 0x59, 0xe1, 0x01, 0x93, 0x0c, 0x31, 0xf0, 0x36, 0x68, 0x96, 0x2d, 0x47, 0x63, 0xf4, 0x22, + 0xf3, 0x95, 0xcf, 0x2e, 0xe6, 0xc6, 0xa7, 0x0e, 0x2d, 0x6c, 0x48, 0x32, 0x51, 0x22, 0x46, 0x73, + 0xd8, 0x62, 0xbc, 0x87, 0x97, 0x41, 0x7b, 0xb4, 0x5b, 0xdb, 0xd8, 0xaf, 0xe7, 0x53, 0xc5, 0xe2, + 0xcf, 0xbf, 0x95, 0x16, 0xce, 0x23, 0x54, 0x37, 0x2f, 0x83, 0x46, 0xea, 0xdb, 0x0f, 0x1f, 0xd7, + 0xf3, 0x68, 0x34, 0x8e, 0xd0, 0x36, 0x7b, 0x46, 0x8d, 0x7f, 0xd1, 0xff, 0xea, 0x9f, 0x74, 0xd1, + 0x57, 0x90, 0x89, 0x2e, 0x2a, 0x99, 0xc1, 0x7c, 0xe5, 0xee, 0xc5, 0xef, 0x91, 0xb0, 0xcc, 0xfd, + 0x6e, 0x40, 0x89, 0x24, 0xe2, 0x5b, 0x00, 0x56, 0x10, 0x1c, 0x79, 0x94, 0x1f, 0x08, 0x16, 0xcf, + 0x78, 0x92, 0x55, 0x2b, 0xfb, 0x2c, 0xda, 0x0e, 0x29, 0xef, 0x1c, 0x09, 0x7e, 0xe0, 0xf9, 0xb2, + 0x80, 0x59, 0x92, 0x55, 0x2b, 0x0d, 0x1f, 0xaf, 0xc3, 0x8c, 0x2d, 0xc3, 0x49, 0xe6, 0xe6, 0xed, + 0x49, 0x92, 0x24, 0x09, 0xc9, 0xb8, 0x03, 0x99, 0xc8, 0x0b, 0x9e, 0x83, 0xd9, 0xcd, 0x87, 0xdb, + 0xbb, 0x0f, 0xea, 0x51, 0x5e, 0xf8, 0x1d, 0xc8, 0x35, 0x76, 0x36, 0x49, 0x7d, 0xbb, 0xbe, 0xb3, + 0xbf, 0xf1, 0x20, 0x8f, 0x2a, 0xc7, 0xd3, 0x00, 0xb5, 0xfe, 0xa5, 0x8e, 0xbf, 0x83, 0x19, 0xd5, + 0xde, 0xd8, 0x18, 0xdd, 0x82, 0xc3, 0xb7, 0x61, 0xf1, 0x22, 0x8c, 0x4a, 0xc4, 0x58, 0xfa, 0xf3, + 0xf7, 0x7f, 0x8e, 0xd3, 0xb7, 0x60, 0x4e, 0x62, 0x3e, 0x8a, 0xe6, 0x3a, 0x0d, 0xe1, 0x46, 0xfc, + 0xa4, 0x6e, 0x8d, 0x7b, 0x08, 0x7f, 0x0f, 0xd9, 0xfe, 0x0c, 0xc6, 0x23, 0xdf, 0xf5, 0xfc, 0x90, + 0x2f, 0xde, 0xb9, 0x04, 0xa5, 0x86, 0xcb, 0x24, 0x06, 0xf0, 0x2f, 0x08, 0xf2, 0xe7, 0xc7, 0x13, + 0xbe, 0x7b, 0x85, 0x51, 0x5b, 0x5c, 0x9d, 0x0c, 0x7c, 0x15, 0x53, 0x1d, 0x98, 0x96, 0x83, 0x0d, + 0x97, 0xc6, 0x0d, 0x90, 0xfe, 0xe9, 0xe3, 0x11, 0x49, 0x1d, 0x96, 0x27, 0x38, 0xf1, 0xa7, 0x34, + 0xba, 0x87, 0xf0, 0x8f, 0x08, 0x72, 0x43, 0xad, 0x8d, 0x97, 0x2f, 0xe9, 0xfd, 0xc4, 0xc3, 0xf2, + 0x64, 0xdf, 0xc8, 0x84, 0x1d, 0x51, 0xbd, 0x7d, 0xf2, 0x46, 0x4f, 0xbd, 0x7e, 0xa3, 0xa7, 0x7e, + 0xe8, 0xe9, 0xe8, 0xa4, 0xa7, 0xa3, 0x57, 0x3d, 0x1d, 0xfd, 0xdd, 0xd3, 0xd1, 0xcb, 0x33, 0x3d, + 0xf5, 0xea, 0x4c, 0x4f, 0xbd, 0x3e, 0xd3, 0x53, 0x4d, 0x4d, 0x5e, 0xc7, 0x9f, 0xfc, 0x17, 0x00, + 0x00, 0xff, 0xff, 0xa9, 0xc4, 0x36, 0xaa, 0xba, 0x0a, 0x00, 0x00, } type authenticatedWrapperDispatcherServer struct { @@ -508,7 +860,7 @@ func (m *SessionRequest) CopyFrom(src interface{}) { *m = *o if o.Description != nil { m.Description = &NodeDescription{} - deepcopy.Copy(m.Description, o.Description) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Description, o.Description) } } @@ -527,13 +879,13 @@ func (m *SessionMessage) CopyFrom(src interface{}) { *m = *o if o.Node != nil { m.Node = &Node{} - deepcopy.Copy(m.Node, o.Node) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Node, o.Node) } if o.Managers != nil { m.Managers = make([]*WeightedPeer, len(o.Managers)) for i := range m.Managers { m.Managers[i] = &WeightedPeer{} - deepcopy.Copy(m.Managers[i], o.Managers[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Managers[i], o.Managers[i]) } } @@ -541,7 +893,7 @@ func (m *SessionMessage) CopyFrom(src interface{}) { m.NetworkBootstrapKeys = make([]*EncryptionKey, len(o.NetworkBootstrapKeys)) for i := range m.NetworkBootstrapKeys { m.NetworkBootstrapKeys[i] = &EncryptionKey{} - deepcopy.Copy(m.NetworkBootstrapKeys[i], o.NetworkBootstrapKeys[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.NetworkBootstrapKeys[i], o.NetworkBootstrapKeys[i]) } } @@ -579,7 +931,7 @@ func (m *HeartbeatResponse) CopyFrom(src interface{}) { o := src.(*HeartbeatResponse) *m = *o - deepcopy.Copy(&m.Period, &o.Period) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Period, &o.Period) } func (m *UpdateTaskStatusRequest) Copy() *UpdateTaskStatusRequest { @@ -599,7 +951,7 @@ func (m *UpdateTaskStatusRequest) CopyFrom(src interface{}) { m.Updates = make([]*UpdateTaskStatusRequest_TaskStatusUpdate, len(o.Updates)) for i := range m.Updates { m.Updates[i] = &UpdateTaskStatusRequest_TaskStatusUpdate{} - deepcopy.Copy(m.Updates[i], o.Updates[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Updates[i], o.Updates[i]) } } @@ -620,7 +972,7 @@ func (m *UpdateTaskStatusRequest_TaskStatusUpdate) CopyFrom(src interface{}) { *m = *o if o.Status != nil { m.Status = &TaskStatus{} - deepcopy.Copy(m.Status, o.Status) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Status, o.Status) } } @@ -666,7 +1018,7 @@ func (m *TasksMessage) CopyFrom(src interface{}) { m.Tasks = make([]*Task, len(o.Tasks)) for i := range m.Tasks { m.Tasks[i] = &Task{} - deepcopy.Copy(m.Tasks[i], o.Tasks[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Tasks[i], o.Tasks[i]) } } @@ -706,19 +1058,19 @@ func (m *Assignment) CopyFrom(src interface{}) { v := Assignment_Task{ Task: &Task{}, } - deepcopy.Copy(v.Task, o.GetTask()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Task, o.GetTask()) m.Item = &v case *Assignment_Secret: v := Assignment_Secret{ Secret: &Secret{}, } - deepcopy.Copy(v.Secret, o.GetSecret()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Secret, o.GetSecret()) m.Item = &v case *Assignment_Config: v := Assignment_Config{ Config: &Config{}, } - deepcopy.Copy(v.Config, o.GetConfig()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Config, o.GetConfig()) m.Item = &v } } @@ -740,7 +1092,7 @@ func (m *AssignmentChange) CopyFrom(src interface{}) { *m = *o if o.Assignment != nil { m.Assignment = &Assignment{} - deepcopy.Copy(m.Assignment, o.Assignment) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Assignment, o.Assignment) } } @@ -761,7 +1113,7 @@ func (m *AssignmentsMessage) CopyFrom(src interface{}) { m.Changes = make([]*AssignmentChange, len(o.Changes)) for i := range m.Changes { m.Changes[i] = &AssignmentChange{} - deepcopy.Copy(m.Changes[i], o.Changes[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Changes[i], o.Changes[i]) } } @@ -775,8 +1127,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Dispatcher service - +// DispatcherClient is the client API for Dispatcher service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type DispatcherClient interface { // Session starts an agent session with the dispatcher. The session is // started after the first SessionMessage is received. @@ -818,7 +1171,7 @@ func NewDispatcherClient(cc *grpc.ClientConn) DispatcherClient { } func (c *dispatcherClient) Session(ctx context.Context, in *SessionRequest, opts ...grpc.CallOption) (Dispatcher_SessionClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Dispatcher_serviceDesc.Streams[0], c.cc, "/docker.swarmkit.v1.Dispatcher/Session", opts...) + stream, err := c.cc.NewStream(ctx, &_Dispatcher_serviceDesc.Streams[0], "/docker.swarmkit.v1.Dispatcher/Session", opts...) if err != nil { return nil, err } @@ -851,7 +1204,7 @@ func (x *dispatcherSessionClient) Recv() (*SessionMessage, error) { func (c *dispatcherClient) Heartbeat(ctx context.Context, in *HeartbeatRequest, opts ...grpc.CallOption) (*HeartbeatResponse, error) { out := new(HeartbeatResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Dispatcher/Heartbeat", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Dispatcher/Heartbeat", in, out, opts...) if err != nil { return nil, err } @@ -860,15 +1213,16 @@ func (c *dispatcherClient) Heartbeat(ctx context.Context, in *HeartbeatRequest, func (c *dispatcherClient) UpdateTaskStatus(ctx context.Context, in *UpdateTaskStatusRequest, opts ...grpc.CallOption) (*UpdateTaskStatusResponse, error) { out := new(UpdateTaskStatusResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Dispatcher/UpdateTaskStatus", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Dispatcher/UpdateTaskStatus", in, out, opts...) if err != nil { return nil, err } return out, nil } +// Deprecated: Do not use. func (c *dispatcherClient) Tasks(ctx context.Context, in *TasksRequest, opts ...grpc.CallOption) (Dispatcher_TasksClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Dispatcher_serviceDesc.Streams[1], c.cc, "/docker.swarmkit.v1.Dispatcher/Tasks", opts...) + stream, err := c.cc.NewStream(ctx, &_Dispatcher_serviceDesc.Streams[1], "/docker.swarmkit.v1.Dispatcher/Tasks", opts...) if err != nil { return nil, err } @@ -900,7 +1254,7 @@ func (x *dispatcherTasksClient) Recv() (*TasksMessage, error) { } func (c *dispatcherClient) Assignments(ctx context.Context, in *AssignmentsRequest, opts ...grpc.CallOption) (Dispatcher_AssignmentsClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Dispatcher_serviceDesc.Streams[2], c.cc, "/docker.swarmkit.v1.Dispatcher/Assignments", opts...) + stream, err := c.cc.NewStream(ctx, &_Dispatcher_serviceDesc.Streams[2], "/docker.swarmkit.v1.Dispatcher/Assignments", opts...) if err != nil { return nil, err } @@ -931,8 +1285,7 @@ func (x *dispatcherAssignmentsClient) Recv() (*AssignmentsMessage, error) { return m, nil } -// Server API for Dispatcher service - +// DispatcherServer is the server API for Dispatcher service. type DispatcherServer interface { // Session starts an agent session with the dispatcher. The session is // started after the first SessionMessage is received. @@ -965,6 +1318,26 @@ type DispatcherServer interface { Assignments(*AssignmentsRequest, Dispatcher_AssignmentsServer) error } +// UnimplementedDispatcherServer can be embedded to have forward compatible implementations. +type UnimplementedDispatcherServer struct { +} + +func (*UnimplementedDispatcherServer) Session(req *SessionRequest, srv Dispatcher_SessionServer) error { + return status.Errorf(codes.Unimplemented, "method Session not implemented") +} +func (*UnimplementedDispatcherServer) Heartbeat(ctx context.Context, req *HeartbeatRequest) (*HeartbeatResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Heartbeat not implemented") +} +func (*UnimplementedDispatcherServer) UpdateTaskStatus(ctx context.Context, req *UpdateTaskStatusRequest) (*UpdateTaskStatusResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateTaskStatus not implemented") +} +func (*UnimplementedDispatcherServer) Tasks(req *TasksRequest, srv Dispatcher_TasksServer) error { + return status.Errorf(codes.Unimplemented, "method Tasks not implemented") +} +func (*UnimplementedDispatcherServer) Assignments(req *AssignmentsRequest, srv Dispatcher_AssignmentsServer) error { + return status.Errorf(codes.Unimplemented, "method Assignments not implemented") +} + func RegisterDispatcherServer(s *grpc.Server, srv DispatcherServer) { s.RegisterService(&_Dispatcher_serviceDesc, srv) } @@ -1104,7 +1477,7 @@ var _Dispatcher_serviceDesc = grpc.ServiceDesc{ func (m *SessionRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1112,33 +1485,41 @@ func (m *SessionRequest) Marshal() (dAtA []byte, err error) { } func (m *SessionRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SessionRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Description != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(m.Description.Size())) - n1, err := m.Description.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 - } if len(m.SessionID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.SessionID) + copy(dAtA[i:], m.SessionID) i = encodeVarintDispatcher(dAtA, i, uint64(len(m.SessionID))) - i += copy(dAtA[i:], m.SessionID) + i-- + dAtA[i] = 0x12 } - return i, nil + if m.Description != nil { + { + size, err := m.Description.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *SessionMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1146,63 +1527,76 @@ func (m *SessionMessage) Marshal() (dAtA []byte, err error) { } func (m *SessionMessage) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SessionMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.SessionID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(len(m.SessionID))) - i += copy(dAtA[i:], m.SessionID) + if len(m.RootCA) > 0 { + i -= len(m.RootCA) + copy(dAtA[i:], m.RootCA) + i = encodeVarintDispatcher(dAtA, i, uint64(len(m.RootCA))) + i-- + dAtA[i] = 0x2a } - if m.Node != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(m.Node.Size())) - n2, err := m.Node.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.NetworkBootstrapKeys) > 0 { + for iNdEx := len(m.NetworkBootstrapKeys) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.NetworkBootstrapKeys[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 } - i += n2 } if len(m.Managers) > 0 { - for _, msg := range m.Managers { - dAtA[i] = 0x1a - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Managers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Managers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x1a } } - if len(m.NetworkBootstrapKeys) > 0 { - for _, msg := range m.NetworkBootstrapKeys { - dAtA[i] = 0x22 - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Node != nil { + { + size, err := m.Node.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x12 } - if len(m.RootCA) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(len(m.RootCA))) - i += copy(dAtA[i:], m.RootCA) + if len(m.SessionID) > 0 { + i -= len(m.SessionID) + copy(dAtA[i:], m.SessionID) + i = encodeVarintDispatcher(dAtA, i, uint64(len(m.SessionID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *HeartbeatRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1210,23 +1604,29 @@ func (m *HeartbeatRequest) Marshal() (dAtA []byte, err error) { } func (m *HeartbeatRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HeartbeatRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.SessionID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.SessionID) + copy(dAtA[i:], m.SessionID) i = encodeVarintDispatcher(dAtA, i, uint64(len(m.SessionID))) - i += copy(dAtA[i:], m.SessionID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *HeartbeatResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1234,25 +1634,30 @@ func (m *HeartbeatResponse) Marshal() (dAtA []byte, err error) { } func (m *HeartbeatResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HeartbeatResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(types.SizeOfStdDuration(m.Period))) - n3, err := types.StdDurationMarshalTo(m.Period, dAtA[i:]) - if err != nil { - return 0, err + n3, err3 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.Period, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.Period):]) + if err3 != nil { + return 0, err3 } - i += n3 - return i, nil + i -= n3 + i = encodeVarintDispatcher(dAtA, i, uint64(n3)) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *UpdateTaskStatusRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1260,35 +1665,43 @@ func (m *UpdateTaskStatusRequest) Marshal() (dAtA []byte, err error) { } func (m *UpdateTaskStatusRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateTaskStatusRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.SessionID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(len(m.SessionID))) - i += copy(dAtA[i:], m.SessionID) - } if len(m.Updates) > 0 { - for _, msg := range m.Updates { - dAtA[i] = 0x1a - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Updates) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Updates[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x1a } } - return i, nil + if len(m.SessionID) > 0 { + i -= len(m.SessionID) + copy(dAtA[i:], m.SessionID) + i = encodeVarintDispatcher(dAtA, i, uint64(len(m.SessionID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1296,33 +1709,41 @@ func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Marshal() (dAtA []byte, err e } func (m *UpdateTaskStatusRequest_TaskStatusUpdate) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateTaskStatusRequest_TaskStatusUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.TaskID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(len(m.TaskID))) - i += copy(dAtA[i:], m.TaskID) - } if m.Status != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(m.Status.Size())) - n4, err := m.Status.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) } - i += n4 + i-- + dAtA[i] = 0x12 + } + if len(m.TaskID) > 0 { + i -= len(m.TaskID) + copy(dAtA[i:], m.TaskID) + i = encodeVarintDispatcher(dAtA, i, uint64(len(m.TaskID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *UpdateTaskStatusResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1330,17 +1751,22 @@ func (m *UpdateTaskStatusResponse) Marshal() (dAtA []byte, err error) { } func (m *UpdateTaskStatusResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateTaskStatusResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + return len(dAtA) - i, nil } func (m *TasksRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1348,23 +1774,29 @@ func (m *TasksRequest) Marshal() (dAtA []byte, err error) { } func (m *TasksRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TasksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.SessionID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.SessionID) + copy(dAtA[i:], m.SessionID) i = encodeVarintDispatcher(dAtA, i, uint64(len(m.SessionID))) - i += copy(dAtA[i:], m.SessionID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *TasksMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1372,29 +1804,36 @@ func (m *TasksMessage) Marshal() (dAtA []byte, err error) { } func (m *TasksMessage) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TasksMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.Tasks) > 0 { - for _, msg := range m.Tasks { - dAtA[i] = 0xa - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Tasks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Tasks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *AssignmentsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1402,23 +1841,29 @@ func (m *AssignmentsRequest) Marshal() (dAtA []byte, err error) { } func (m *AssignmentsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AssignmentsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.SessionID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.SessionID) + copy(dAtA[i:], m.SessionID) i = encodeVarintDispatcher(dAtA, i, uint64(len(m.SessionID))) - i += copy(dAtA[i:], m.SessionID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Assignment) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1426,66 +1871,94 @@ func (m *Assignment) Marshal() (dAtA []byte, err error) { } func (m *Assignment) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Assignment) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Item != nil { - nn5, err := m.Item.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size := m.Item.Size() + i -= size + if _, err := m.Item.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += nn5 } - return i, nil + return len(dAtA) - i, nil } func (m *Assignment_Task) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Assignment_Task) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Task != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(m.Task.Size())) - n6, err := m.Task.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Task.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) } - i += n6 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Assignment_Secret) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Assignment_Secret) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Secret != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(m.Secret.Size())) - n7, err := m.Secret.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Secret.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) } - i += n7 + i-- + dAtA[i] = 0x12 } - return i, nil + return len(dAtA) - i, nil } func (m *Assignment_Config) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Assignment_Config) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Config != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(m.Config.Size())) - n8, err := m.Config.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Config.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) } - i += n8 + i-- + dAtA[i] = 0x1a } - return i, nil + return len(dAtA) - i, nil } func (m *AssignmentChange) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1493,32 +1966,39 @@ func (m *AssignmentChange) Marshal() (dAtA []byte, err error) { } func (m *AssignmentChange) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AssignmentChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Assignment != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(m.Assignment.Size())) - n9, err := m.Assignment.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n9 - } if m.Action != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintDispatcher(dAtA, i, uint64(m.Action)) + i-- + dAtA[i] = 0x10 + } + if m.Assignment != nil { + { + size, err := m.Assignment.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *AssignmentsMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1526,50 +2006,61 @@ func (m *AssignmentsMessage) Marshal() (dAtA []byte, err error) { } func (m *AssignmentsMessage) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AssignmentsMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Type != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(m.Type)) - } - if len(m.AppliesTo) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(len(m.AppliesTo))) - i += copy(dAtA[i:], m.AppliesTo) + if len(m.Changes) > 0 { + for iNdEx := len(m.Changes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Changes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDispatcher(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } } if len(m.ResultsIn) > 0 { - dAtA[i] = 0x1a - i++ + i -= len(m.ResultsIn) + copy(dAtA[i:], m.ResultsIn) i = encodeVarintDispatcher(dAtA, i, uint64(len(m.ResultsIn))) - i += copy(dAtA[i:], m.ResultsIn) + i-- + dAtA[i] = 0x1a } - if len(m.Changes) > 0 { - for _, msg := range m.Changes { - dAtA[i] = 0x22 - i++ - i = encodeVarintDispatcher(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if len(m.AppliesTo) > 0 { + i -= len(m.AppliesTo) + copy(dAtA[i:], m.AppliesTo) + i = encodeVarintDispatcher(dAtA, i, uint64(len(m.AppliesTo))) + i-- + dAtA[i] = 0x12 + } + if m.Type != 0 { + i = encodeVarintDispatcher(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func encodeVarintDispatcher(dAtA []byte, offset int, v uint64) int { + offset -= sovDispatcher(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } type raftProxyDispatcherServer struct { @@ -1868,6 +2359,9 @@ func (p *raftProxyDispatcherServer) Assignments(r *AssignmentsRequest, stream Di } func (m *SessionRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Description != nil { @@ -1882,6 +2376,9 @@ func (m *SessionRequest) Size() (n int) { } func (m *SessionMessage) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.SessionID) @@ -1912,6 +2409,9 @@ func (m *SessionMessage) Size() (n int) { } func (m *HeartbeatRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.SessionID) @@ -1922,14 +2422,20 @@ func (m *HeartbeatRequest) Size() (n int) { } func (m *HeartbeatResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l - l = types.SizeOfStdDuration(m.Period) + l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.Period) n += 1 + l + sovDispatcher(uint64(l)) return n } func (m *UpdateTaskStatusRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.SessionID) @@ -1946,6 +2452,9 @@ func (m *UpdateTaskStatusRequest) Size() (n int) { } func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.TaskID) @@ -1960,12 +2469,18 @@ func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Size() (n int) { } func (m *UpdateTaskStatusResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l return n } func (m *TasksRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.SessionID) @@ -1976,6 +2491,9 @@ func (m *TasksRequest) Size() (n int) { } func (m *TasksMessage) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Tasks) > 0 { @@ -1988,6 +2506,9 @@ func (m *TasksMessage) Size() (n int) { } func (m *AssignmentsRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.SessionID) @@ -1998,6 +2519,9 @@ func (m *AssignmentsRequest) Size() (n int) { } func (m *Assignment) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Item != nil { @@ -2007,6 +2531,9 @@ func (m *Assignment) Size() (n int) { } func (m *Assignment_Task) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Task != nil { @@ -2016,6 +2543,9 @@ func (m *Assignment_Task) Size() (n int) { return n } func (m *Assignment_Secret) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Secret != nil { @@ -2025,6 +2555,9 @@ func (m *Assignment_Secret) Size() (n int) { return n } func (m *Assignment_Config) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Config != nil { @@ -2034,6 +2567,9 @@ func (m *Assignment_Config) Size() (n int) { return n } func (m *AssignmentChange) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Assignment != nil { @@ -2047,6 +2583,9 @@ func (m *AssignmentChange) Size() (n int) { } func (m *AssignmentsMessage) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Type != 0 { @@ -2070,14 +2609,7 @@ func (m *AssignmentsMessage) Size() (n int) { } func sovDispatcher(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozDispatcher(x uint64) (n int) { return sovDispatcher(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -2097,11 +2629,21 @@ func (this *SessionMessage) String() string { if this == nil { return "nil" } + repeatedStringForManagers := "[]*WeightedPeer{" + for _, f := range this.Managers { + repeatedStringForManagers += strings.Replace(fmt.Sprintf("%v", f), "WeightedPeer", "WeightedPeer", 1) + "," + } + repeatedStringForManagers += "}" + repeatedStringForNetworkBootstrapKeys := "[]*EncryptionKey{" + for _, f := range this.NetworkBootstrapKeys { + repeatedStringForNetworkBootstrapKeys += strings.Replace(fmt.Sprintf("%v", f), "EncryptionKey", "EncryptionKey", 1) + "," + } + repeatedStringForNetworkBootstrapKeys += "}" s := strings.Join([]string{`&SessionMessage{`, `SessionID:` + fmt.Sprintf("%v", this.SessionID) + `,`, `Node:` + strings.Replace(fmt.Sprintf("%v", this.Node), "Node", "Node", 1) + `,`, - `Managers:` + strings.Replace(fmt.Sprintf("%v", this.Managers), "WeightedPeer", "WeightedPeer", 1) + `,`, - `NetworkBootstrapKeys:` + strings.Replace(fmt.Sprintf("%v", this.NetworkBootstrapKeys), "EncryptionKey", "EncryptionKey", 1) + `,`, + `Managers:` + repeatedStringForManagers + `,`, + `NetworkBootstrapKeys:` + repeatedStringForNetworkBootstrapKeys + `,`, `RootCA:` + fmt.Sprintf("%v", this.RootCA) + `,`, `}`, }, "") @@ -2122,7 +2664,7 @@ func (this *HeartbeatResponse) String() string { return "nil" } s := strings.Join([]string{`&HeartbeatResponse{`, - `Period:` + strings.Replace(strings.Replace(this.Period.String(), "Duration", "google_protobuf1.Duration", 1), `&`, ``, 1) + `,`, + `Period:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Period), "Duration", "types.Duration", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -2131,9 +2673,14 @@ func (this *UpdateTaskStatusRequest) String() string { if this == nil { return "nil" } + repeatedStringForUpdates := "[]*UpdateTaskStatusRequest_TaskStatusUpdate{" + for _, f := range this.Updates { + repeatedStringForUpdates += strings.Replace(fmt.Sprintf("%v", f), "UpdateTaskStatusRequest_TaskStatusUpdate", "UpdateTaskStatusRequest_TaskStatusUpdate", 1) + "," + } + repeatedStringForUpdates += "}" s := strings.Join([]string{`&UpdateTaskStatusRequest{`, `SessionID:` + fmt.Sprintf("%v", this.SessionID) + `,`, - `Updates:` + strings.Replace(fmt.Sprintf("%v", this.Updates), "UpdateTaskStatusRequest_TaskStatusUpdate", "UpdateTaskStatusRequest_TaskStatusUpdate", 1) + `,`, + `Updates:` + repeatedStringForUpdates + `,`, `}`, }, "") return s @@ -2172,8 +2719,13 @@ func (this *TasksMessage) String() string { if this == nil { return "nil" } + repeatedStringForTasks := "[]*Task{" + for _, f := range this.Tasks { + repeatedStringForTasks += strings.Replace(fmt.Sprintf("%v", f), "Task", "Task", 1) + "," + } + repeatedStringForTasks += "}" s := strings.Join([]string{`&TasksMessage{`, - `Tasks:` + strings.Replace(fmt.Sprintf("%v", this.Tasks), "Task", "Task", 1) + `,`, + `Tasks:` + repeatedStringForTasks + `,`, `}`, }, "") return s @@ -2233,7 +2785,7 @@ func (this *AssignmentChange) String() string { return "nil" } s := strings.Join([]string{`&AssignmentChange{`, - `Assignment:` + strings.Replace(fmt.Sprintf("%v", this.Assignment), "Assignment", "Assignment", 1) + `,`, + `Assignment:` + strings.Replace(this.Assignment.String(), "Assignment", "Assignment", 1) + `,`, `Action:` + fmt.Sprintf("%v", this.Action) + `,`, `}`, }, "") @@ -2243,11 +2795,16 @@ func (this *AssignmentsMessage) String() string { if this == nil { return "nil" } + repeatedStringForChanges := "[]*AssignmentChange{" + for _, f := range this.Changes { + repeatedStringForChanges += strings.Replace(f.String(), "AssignmentChange", "AssignmentChange", 1) + "," + } + repeatedStringForChanges += "}" s := strings.Join([]string{`&AssignmentsMessage{`, `Type:` + fmt.Sprintf("%v", this.Type) + `,`, `AppliesTo:` + fmt.Sprintf("%v", this.AppliesTo) + `,`, `ResultsIn:` + fmt.Sprintf("%v", this.ResultsIn) + `,`, - `Changes:` + strings.Replace(fmt.Sprintf("%v", this.Changes), "AssignmentChange", "AssignmentChange", 1) + `,`, + `Changes:` + repeatedStringForChanges + `,`, `}`, }, "") return s @@ -2275,7 +2832,7 @@ func (m *SessionRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2303,7 +2860,7 @@ func (m *SessionRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2312,6 +2869,9 @@ func (m *SessionRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2336,7 +2896,7 @@ func (m *SessionRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2346,6 +2906,9 @@ func (m *SessionRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2357,7 +2920,7 @@ func (m *SessionRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -2387,7 +2950,7 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2415,7 +2978,7 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2425,6 +2988,9 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2444,7 +3010,7 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2453,6 +3019,9 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2477,7 +3046,7 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2486,6 +3055,9 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2508,7 +3080,7 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2517,6 +3089,9 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2539,7 +3114,7 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2548,6 +3123,9 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2562,7 +3140,7 @@ func (m *SessionMessage) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -2592,7 +3170,7 @@ func (m *HeartbeatRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2620,7 +3198,7 @@ func (m *HeartbeatRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2630,6 +3208,9 @@ func (m *HeartbeatRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2641,7 +3222,7 @@ func (m *HeartbeatRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -2671,7 +3252,7 @@ func (m *HeartbeatResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2699,7 +3280,7 @@ func (m *HeartbeatResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2708,10 +3289,13 @@ func (m *HeartbeatResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdDurationUnmarshal(&m.Period, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.Period, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -2721,7 +3305,7 @@ func (m *HeartbeatResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -2751,7 +3335,7 @@ func (m *UpdateTaskStatusRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2779,7 +3363,7 @@ func (m *UpdateTaskStatusRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2789,6 +3373,9 @@ func (m *UpdateTaskStatusRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2808,7 +3395,7 @@ func (m *UpdateTaskStatusRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2817,6 +3404,9 @@ func (m *UpdateTaskStatusRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2831,7 +3421,7 @@ func (m *UpdateTaskStatusRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -2861,7 +3451,7 @@ func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Unmarshal(dAtA []byte) error } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2889,7 +3479,7 @@ func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Unmarshal(dAtA []byte) error } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2899,6 +3489,9 @@ func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Unmarshal(dAtA []byte) error return ErrInvalidLengthDispatcher } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2918,7 +3511,7 @@ func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Unmarshal(dAtA []byte) error } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2927,6 +3520,9 @@ func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Unmarshal(dAtA []byte) error return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2943,7 +3539,7 @@ func (m *UpdateTaskStatusRequest_TaskStatusUpdate) Unmarshal(dAtA []byte) error if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -2973,7 +3569,7 @@ func (m *UpdateTaskStatusResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2993,7 +3589,7 @@ func (m *UpdateTaskStatusResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -3023,7 +3619,7 @@ func (m *TasksRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3051,7 +3647,7 @@ func (m *TasksRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3061,6 +3657,9 @@ func (m *TasksRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3072,7 +3671,7 @@ func (m *TasksRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -3102,7 +3701,7 @@ func (m *TasksMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3130,7 +3729,7 @@ func (m *TasksMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3139,6 +3738,9 @@ func (m *TasksMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3153,7 +3755,7 @@ func (m *TasksMessage) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -3183,7 +3785,7 @@ func (m *AssignmentsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3211,7 +3813,7 @@ func (m *AssignmentsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3221,6 +3823,9 @@ func (m *AssignmentsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3232,7 +3837,7 @@ func (m *AssignmentsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -3262,7 +3867,7 @@ func (m *Assignment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3290,7 +3895,7 @@ func (m *Assignment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3299,6 +3904,9 @@ func (m *Assignment) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3322,7 +3930,7 @@ func (m *Assignment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3331,6 +3939,9 @@ func (m *Assignment) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3354,7 +3965,7 @@ func (m *Assignment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3363,6 +3974,9 @@ func (m *Assignment) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3378,7 +3992,7 @@ func (m *Assignment) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -3408,7 +4022,7 @@ func (m *AssignmentChange) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3436,7 +4050,7 @@ func (m *AssignmentChange) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3445,6 +4059,9 @@ func (m *AssignmentChange) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3469,7 +4086,7 @@ func (m *AssignmentChange) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Action |= (AssignmentChange_AssignmentAction(b) & 0x7F) << shift + m.Action |= AssignmentChange_AssignmentAction(b&0x7F) << shift if b < 0x80 { break } @@ -3480,7 +4097,7 @@ func (m *AssignmentChange) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -3510,7 +4127,7 @@ func (m *AssignmentsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3538,7 +4155,7 @@ func (m *AssignmentsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Type |= (AssignmentsMessage_Type(b) & 0x7F) << shift + m.Type |= AssignmentsMessage_Type(b&0x7F) << shift if b < 0x80 { break } @@ -3557,7 +4174,7 @@ func (m *AssignmentsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3567,6 +4184,9 @@ func (m *AssignmentsMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3586,7 +4206,7 @@ func (m *AssignmentsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3596,6 +4216,9 @@ func (m *AssignmentsMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3615,7 +4238,7 @@ func (m *AssignmentsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3624,6 +4247,9 @@ func (m *AssignmentsMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthDispatcher } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDispatcher + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3638,7 +4264,7 @@ func (m *AssignmentsMessage) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDispatcher } if (iNdEx + skippy) > l { @@ -3656,6 +4282,7 @@ func (m *AssignmentsMessage) Unmarshal(dAtA []byte) error { func skipDispatcher(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -3687,10 +4314,8 @@ func skipDispatcher(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -3707,124 +4332,34 @@ func skipDispatcher(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthDispatcher } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowDispatcher - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipDispatcher(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupDispatcher + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthDispatcher + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthDispatcher = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowDispatcher = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthDispatcher = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDispatcher = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupDispatcher = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/docker/swarmkit/api/dispatcher.proto", fileDescriptorDispatcher) -} - -var fileDescriptorDispatcher = []byte{ - // 1007 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0x4f, 0x6f, 0xe3, 0x44, - 0x1c, 0xcd, 0xa4, 0xa9, 0xdb, 0xfc, 0xd2, 0x2d, 0x61, 0xb4, 0x2a, 0xc6, 0xd2, 0xa6, 0xc1, 0x65, - 0xab, 0x8a, 0x2d, 0xce, 0x12, 0xfe, 0x1d, 0xa8, 0x0a, 0x4d, 0x13, 0xa9, 0xd1, 0x6e, 0xbb, 0xd5, - 0xb4, 0xbb, 0x7b, 0xac, 0x1c, 0x7b, 0xd6, 0x35, 0x69, 0x3c, 0xc6, 0x33, 0xd9, 0x25, 0x07, 0x24, - 0x0e, 0xac, 0x84, 0x38, 0x21, 0x4e, 0x95, 0x10, 0x5f, 0x01, 0xf1, 0x31, 0x2a, 0x4e, 0x1c, 0x39, - 0x15, 0x36, 0x1f, 0x80, 0x0f, 0xc0, 0x09, 0x79, 0x3c, 0x4e, 0x42, 0x37, 0x69, 0xd3, 0x9e, 0x12, - 0xcf, 0xbc, 0xf7, 0xe6, 0xf9, 0xfd, 0x7e, 0xfe, 0x0d, 0x54, 0x3c, 0x5f, 0x1c, 0x77, 0x5b, 0x96, - 0xc3, 0x3a, 0x15, 0x97, 0x39, 0x6d, 0x1a, 0x55, 0xf8, 0x0b, 0x3b, 0xea, 0xb4, 0x7d, 0x51, 0xb1, - 0x43, 0xbf, 0xe2, 0xfa, 0x3c, 0xb4, 0x85, 0x73, 0x4c, 0x23, 0x2b, 0x8c, 0x98, 0x60, 0x18, 0x27, - 0x28, 0x2b, 0x45, 0x59, 0xcf, 0x3f, 0x30, 0xde, 0xbb, 0x42, 0x44, 0xf4, 0x42, 0xca, 0x13, 0xbe, - 0xb1, 0x7e, 0x05, 0x96, 0xb5, 0xbe, 0xa4, 0x8e, 0x48, 0xd1, 0xb7, 0x3d, 0xe6, 0x31, 0xf9, 0xb7, - 0x12, 0xff, 0x53, 0xab, 0x9f, 0x5e, 0xa2, 0x21, 0x11, 0xad, 0xee, 0xb3, 0x4a, 0x78, 0xd2, 0xf5, - 0xfc, 0x40, 0xfd, 0x28, 0x62, 0xc9, 0x63, 0xcc, 0x3b, 0xa1, 0x43, 0x90, 0xdb, 0x8d, 0x6c, 0xe1, - 0x33, 0xb5, 0x6f, 0xbe, 0x44, 0xb0, 0x78, 0x40, 0x39, 0xf7, 0x59, 0x40, 0xe8, 0x57, 0x5d, 0xca, - 0x05, 0x6e, 0x40, 0xc1, 0xa5, 0xdc, 0x89, 0xfc, 0x30, 0xc6, 0xe9, 0xa8, 0x8c, 0xd6, 0x0a, 0xd5, - 0x15, 0xeb, 0xf5, 0x14, 0xac, 0x3d, 0xe6, 0xd2, 0xfa, 0x10, 0x4a, 0x46, 0x79, 0x78, 0x1d, 0x80, - 0x27, 0xc2, 0x47, 0xbe, 0xab, 0x67, 0xcb, 0x68, 0x2d, 0x5f, 0xbb, 0xd5, 0x3f, 0x5f, 0xce, 0xab, - 0xe3, 0x9a, 0x75, 0x92, 0x57, 0x80, 0xa6, 0x6b, 0xfe, 0x9c, 0x1d, 0xf8, 0xd8, 0xa5, 0x9c, 0xdb, - 0x1e, 0xbd, 0x20, 0x80, 0x2e, 0x17, 0xc0, 0xeb, 0x90, 0x0b, 0x98, 0x4b, 0xe5, 0x41, 0x85, 0xaa, - 0x3e, 0xc9, 0x2e, 0x91, 0x28, 0xbc, 0x01, 0xf3, 0x1d, 0x3b, 0xb0, 0x3d, 0x1a, 0x71, 0x7d, 0xa6, - 0x3c, 0xb3, 0x56, 0xa8, 0x96, 0xc7, 0x31, 0x9e, 0x52, 0xdf, 0x3b, 0x16, 0xd4, 0xdd, 0xa7, 0x34, - 0x22, 0x03, 0x06, 0x7e, 0x0a, 0x4b, 0x01, 0x15, 0x2f, 0x58, 0xd4, 0x3e, 0x6a, 0x31, 0x26, 0xb8, - 0x88, 0xec, 0xf0, 0xa8, 0x4d, 0x7b, 0x5c, 0xcf, 0x49, 0xad, 0x77, 0xc6, 0x69, 0x35, 0x02, 0x27, - 0xea, 0xc9, 0x68, 0x1e, 0xd0, 0x1e, 0xb9, 0xad, 0x04, 0x6a, 0x29, 0xff, 0x01, 0xed, 0x71, 0xbc, - 0x04, 0x1a, 0x61, 0x4c, 0x6c, 0x6f, 0xe9, 0xb3, 0x65, 0xb4, 0xb6, 0x40, 0xd4, 0x93, 0xf9, 0x05, - 0x14, 0x77, 0xa8, 0x1d, 0x89, 0x16, 0xb5, 0x45, 0x5a, 0xa6, 0x6b, 0xc5, 0x63, 0xee, 0xc3, 0x9b, - 0x23, 0x0a, 0x3c, 0x64, 0x01, 0xa7, 0xf8, 0x33, 0xd0, 0x42, 0x1a, 0xf9, 0xcc, 0x55, 0x45, 0x7e, - 0xdb, 0x4a, 0xba, 0xc5, 0x4a, 0xbb, 0xc5, 0xaa, 0xab, 0x6e, 0xa9, 0xcd, 0x9f, 0x9d, 0x2f, 0x67, - 0x4e, 0xff, 0x5a, 0x46, 0x44, 0x51, 0xcc, 0x1f, 0xb3, 0xf0, 0xd6, 0xe3, 0xd0, 0xb5, 0x05, 0x3d, - 0xb4, 0x79, 0xfb, 0x40, 0xd8, 0xa2, 0xcb, 0x6f, 0xe4, 0x0d, 0x3f, 0x81, 0xb9, 0xae, 0x14, 0x4a, - 0x6b, 0xb1, 0x31, 0x2e, 0xbf, 0x09, 0x67, 0x59, 0xc3, 0x95, 0x04, 0x41, 0x52, 0x31, 0x83, 0x41, - 0xf1, 0xe2, 0x26, 0x5e, 0x81, 0x39, 0x61, 0xf3, 0xf6, 0xd0, 0x16, 0xf4, 0xcf, 0x97, 0xb5, 0x18, - 0xd6, 0xac, 0x13, 0x2d, 0xde, 0x6a, 0xba, 0xf8, 0x13, 0xd0, 0xb8, 0x24, 0xa9, 0x6e, 0x2a, 0x8d, - 0xf3, 0x33, 0xe2, 0x44, 0xa1, 0x4d, 0x03, 0xf4, 0xd7, 0x5d, 0x26, 0x59, 0x9b, 0x1b, 0xb0, 0x10, - 0xaf, 0xde, 0x2c, 0x22, 0x73, 0x53, 0xb1, 0xd3, 0x6f, 0xc3, 0x82, 0xd9, 0xd8, 0x2b, 0xd7, 0x91, - 0x0c, 0x4c, 0x9f, 0x64, 0x90, 0x24, 0x30, 0xb3, 0x06, 0x78, 0x8b, 0x73, 0xdf, 0x0b, 0x3a, 0x34, - 0x10, 0x37, 0xf4, 0xf0, 0x1b, 0x02, 0x18, 0x8a, 0x60, 0x0b, 0x72, 0xb1, 0xb6, 0x6a, 0x9d, 0x89, - 0x0e, 0x76, 0x32, 0x44, 0xe2, 0xf0, 0x47, 0xa0, 0x71, 0xea, 0x44, 0x54, 0xa8, 0x50, 0x8d, 0x71, - 0x8c, 0x03, 0x89, 0xd8, 0xc9, 0x10, 0x85, 0x8d, 0x59, 0x0e, 0x0b, 0x9e, 0xf9, 0x9e, 0x3e, 0x33, - 0x99, 0xb5, 0x2d, 0x11, 0x31, 0x2b, 0xc1, 0xd6, 0x34, 0xc8, 0xf9, 0x82, 0x76, 0xcc, 0x97, 0x59, - 0x28, 0x0e, 0x2d, 0x6f, 0x1f, 0xdb, 0x81, 0x47, 0xf1, 0x26, 0x80, 0x3d, 0x58, 0x53, 0xf6, 0xc7, - 0x56, 0x78, 0xc8, 0x24, 0x23, 0x0c, 0xbc, 0x0b, 0x9a, 0xed, 0xc8, 0xd1, 0x18, 0xbf, 0xc8, 0x62, - 0xf5, 0xe3, 0xcb, 0xb9, 0xc9, 0xa9, 0x23, 0x0b, 0x5b, 0x92, 0x4c, 0x94, 0x88, 0xd9, 0x1a, 0xb5, - 0x98, 0xec, 0xe1, 0x55, 0xd0, 0x1e, 0xef, 0xd7, 0xb7, 0x0e, 0x1b, 0xc5, 0x8c, 0x61, 0xfc, 0xf0, - 0x4b, 0x79, 0xe9, 0x22, 0x42, 0x75, 0xf3, 0x2a, 0x68, 0xa4, 0xb1, 0xfb, 0xe8, 0x49, 0xa3, 0x88, - 0xc6, 0xe3, 0x08, 0xed, 0xb0, 0xe7, 0xd4, 0xfc, 0x17, 0xfd, 0xaf, 0xfe, 0x69, 0x17, 0x7d, 0x0e, - 0xb9, 0xf8, 0xa2, 0x92, 0x19, 0x2c, 0x56, 0xef, 0x5d, 0xfe, 0x1e, 0x29, 0xcb, 0x3a, 0xec, 0x85, - 0x94, 0x48, 0x22, 0xbe, 0x03, 0x60, 0x87, 0xe1, 0x89, 0x4f, 0xf9, 0x91, 0x60, 0xc9, 0x8c, 0x27, - 0x79, 0xb5, 0x72, 0xc8, 0xe2, 0xed, 0x88, 0xf2, 0xee, 0x89, 0xe0, 0x47, 0x7e, 0x20, 0x0b, 0x98, - 0x27, 0x79, 0xb5, 0xd2, 0x0c, 0xf0, 0x26, 0xcc, 0x39, 0x32, 0x9c, 0x74, 0x6e, 0xbe, 0x3b, 0x4d, - 0x92, 0x24, 0x25, 0x99, 0x77, 0x21, 0x17, 0x7b, 0xc1, 0x0b, 0x30, 0xbf, 0xfd, 0x68, 0x77, 0xff, - 0x61, 0x23, 0xce, 0x0b, 0xbf, 0x01, 0x85, 0xe6, 0xde, 0x36, 0x69, 0xec, 0x36, 0xf6, 0x0e, 0xb7, - 0x1e, 0x16, 0x51, 0xf5, 0x74, 0x16, 0xa0, 0x3e, 0xb8, 0xd4, 0xf1, 0xd7, 0x30, 0xa7, 0xda, 0x1b, - 0x9b, 0xe3, 0x5b, 0x70, 0xf4, 0x36, 0x34, 0x2e, 0xc3, 0xa8, 0x44, 0xcc, 0x95, 0xdf, 0x7f, 0xfd, - 0xe7, 0x34, 0x7b, 0x07, 0x16, 0x24, 0xe6, 0xfd, 0x78, 0xae, 0xd3, 0x08, 0x6e, 0x25, 0x4f, 0xea, - 0xd6, 0xb8, 0x8f, 0xf0, 0x37, 0x90, 0x1f, 0xcc, 0x60, 0x3c, 0xf6, 0x5d, 0x2f, 0x0e, 0x79, 0xe3, - 0xee, 0x15, 0x28, 0x35, 0x5c, 0xa6, 0x31, 0x80, 0x7f, 0x42, 0x50, 0xbc, 0x38, 0x9e, 0xf0, 0xbd, - 0x6b, 0x8c, 0x5a, 0x63, 0x7d, 0x3a, 0xf0, 0x75, 0x4c, 0x75, 0x61, 0x56, 0x0e, 0x36, 0x5c, 0x9e, - 0x34, 0x40, 0x06, 0xa7, 0x4f, 0x46, 0xa4, 0x75, 0x58, 0x9d, 0xe2, 0xc4, 0xef, 0xb3, 0xe8, 0x3e, - 0xc2, 0xdf, 0x21, 0x28, 0x8c, 0xb4, 0x36, 0x5e, 0xbd, 0xa2, 0xf7, 0x53, 0x0f, 0xab, 0xd3, 0x7d, - 0x23, 0x53, 0x76, 0x44, 0x4d, 0x3f, 0x7b, 0x55, 0xca, 0xfc, 0xf9, 0xaa, 0x94, 0xf9, 0xb6, 0x5f, - 0x42, 0x67, 0xfd, 0x12, 0xfa, 0xa3, 0x5f, 0x42, 0x7f, 0xf7, 0x4b, 0xa8, 0xa5, 0xc9, 0x2b, 0xf8, - 0xc3, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0xe0, 0xf0, 0x6a, 0xcb, 0xae, 0x0a, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/genericresource/resource_management.go b/vendor/github.com/docker/swarmkit/api/genericresource/resource_management.go index a89a118d62dc0..506257ab9754f 100644 --- a/vendor/github.com/docker/swarmkit/api/genericresource/resource_management.go +++ b/vendor/github.com/docker/swarmkit/api/genericresource/resource_management.go @@ -2,6 +2,7 @@ package genericresource import ( "fmt" + "github.com/docker/swarmkit/api" ) diff --git a/vendor/github.com/docker/swarmkit/api/genericresource/validate.go b/vendor/github.com/docker/swarmkit/api/genericresource/validate.go index eee3706c7416e..0ad49ff75feec 100644 --- a/vendor/github.com/docker/swarmkit/api/genericresource/validate.go +++ b/vendor/github.com/docker/swarmkit/api/genericresource/validate.go @@ -2,6 +2,7 @@ package genericresource import ( "fmt" + "github.com/docker/swarmkit/api" ) diff --git a/vendor/github.com/docker/swarmkit/api/health.pb.go b/vendor/github.com/docker/swarmkit/api/health.pb.go index 453e01fc3f65c..3ca506a031089 100644 --- a/vendor/github.com/docker/swarmkit/api/health.pb.go +++ b/vendor/github.com/docker/swarmkit/api/health.pb.go @@ -3,32 +3,37 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" -import _ "github.com/docker/swarmkit/protobuf/plugin" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import raftselector "github.com/docker/swarmkit/manager/raftselector" -import codes "google.golang.org/grpc/codes" -import status "google.golang.org/grpc/status" -import metadata "google.golang.org/grpc/metadata" -import peer "google.golang.org/grpc/peer" -import rafttime "time" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + context "context" + fmt "fmt" + raftselector "github.com/docker/swarmkit/manager/raftselector" + _ "github.com/docker/swarmkit/protobuf/plugin" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + metadata "google.golang.org/grpc/metadata" + peer "google.golang.org/grpc/peer" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + rafttime "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type HealthCheckResponse_ServingStatus int32 const ( @@ -42,6 +47,7 @@ var HealthCheckResponse_ServingStatus_name = map[int32]string{ 1: "SERVING", 2: "NOT_SERVING", } + var HealthCheckResponse_ServingStatus_value = map[string]int32{ "UNKNOWN": 0, "SERVING": 1, @@ -51,30 +57,116 @@ var HealthCheckResponse_ServingStatus_value = map[string]int32{ func (x HealthCheckResponse_ServingStatus) String() string { return proto.EnumName(HealthCheckResponse_ServingStatus_name, int32(x)) } + func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) { - return fileDescriptorHealth, []int{1, 0} + return fileDescriptor_288522a148aed5ad, []int{1, 0} } type HealthCheckRequest struct { Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` } -func (m *HealthCheckRequest) Reset() { *m = HealthCheckRequest{} } -func (*HealthCheckRequest) ProtoMessage() {} -func (*HealthCheckRequest) Descriptor() ([]byte, []int) { return fileDescriptorHealth, []int{0} } +func (m *HealthCheckRequest) Reset() { *m = HealthCheckRequest{} } +func (*HealthCheckRequest) ProtoMessage() {} +func (*HealthCheckRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_288522a148aed5ad, []int{0} +} +func (m *HealthCheckRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HealthCheckRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HealthCheckRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HealthCheckRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_HealthCheckRequest.Merge(m, src) +} +func (m *HealthCheckRequest) XXX_Size() int { + return m.Size() +} +func (m *HealthCheckRequest) XXX_DiscardUnknown() { + xxx_messageInfo_HealthCheckRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_HealthCheckRequest proto.InternalMessageInfo type HealthCheckResponse struct { Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=docker.swarmkit.v1.HealthCheckResponse_ServingStatus" json:"status,omitempty"` } -func (m *HealthCheckResponse) Reset() { *m = HealthCheckResponse{} } -func (*HealthCheckResponse) ProtoMessage() {} -func (*HealthCheckResponse) Descriptor() ([]byte, []int) { return fileDescriptorHealth, []int{1} } +func (m *HealthCheckResponse) Reset() { *m = HealthCheckResponse{} } +func (*HealthCheckResponse) ProtoMessage() {} +func (*HealthCheckResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_288522a148aed5ad, []int{1} +} +func (m *HealthCheckResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HealthCheckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HealthCheckResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HealthCheckResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_HealthCheckResponse.Merge(m, src) +} +func (m *HealthCheckResponse) XXX_Size() int { + return m.Size() +} +func (m *HealthCheckResponse) XXX_DiscardUnknown() { + xxx_messageInfo_HealthCheckResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_HealthCheckResponse proto.InternalMessageInfo func init() { + proto.RegisterEnum("docker.swarmkit.v1.HealthCheckResponse_ServingStatus", HealthCheckResponse_ServingStatus_name, HealthCheckResponse_ServingStatus_value) proto.RegisterType((*HealthCheckRequest)(nil), "docker.swarmkit.v1.HealthCheckRequest") proto.RegisterType((*HealthCheckResponse)(nil), "docker.swarmkit.v1.HealthCheckResponse") - proto.RegisterEnum("docker.swarmkit.v1.HealthCheckResponse_ServingStatus", HealthCheckResponse_ServingStatus_name, HealthCheckResponse_ServingStatus_value) +} + +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/health.proto", fileDescriptor_288522a148aed5ad) +} + +var fileDescriptor_288522a148aed5ad = []byte{ + // 328 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4e, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xc9, 0x4f, 0xce, 0x4e, 0x2d, 0xd2, 0x2f, 0x2e, + 0x4f, 0x2c, 0xca, 0xcd, 0xce, 0x2c, 0xd1, 0x4f, 0x2c, 0xc8, 0xd4, 0xcf, 0x48, 0x4d, 0xcc, 0x29, + 0xc9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x82, 0xa8, 0xd0, 0x83, 0xa9, 0xd0, 0x2b, + 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x4b, 0xeb, 0x83, 0x58, 0x10, 0x95, 0x52, 0xe6, + 0x78, 0x8c, 0x05, 0xab, 0x48, 0x2a, 0x4d, 0xd3, 0x2f, 0xc8, 0x29, 0x4d, 0xcf, 0xcc, 0x83, 0x52, + 0x10, 0x8d, 0x4a, 0x7a, 0x5c, 0x42, 0x1e, 0x60, 0x2b, 0x9d, 0x33, 0x52, 0x93, 0xb3, 0x83, 0x52, + 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x24, 0xb8, 0xd8, 0x8b, 0x53, 0x8b, 0xca, 0x32, 0x93, 0x53, + 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0x60, 0x5c, 0xa5, 0x05, 0x8c, 0x5c, 0xc2, 0x28, 0x1a, + 0x8a, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x85, 0x7c, 0xb9, 0xd8, 0x8a, 0x4b, 0x12, 0x4b, 0x4a, 0x8b, + 0xc1, 0x1a, 0xf8, 0x8c, 0x4c, 0xf5, 0x30, 0xdd, 0xae, 0x87, 0x45, 0xa3, 0x5e, 0x30, 0xc8, 0xe0, + 0xbc, 0xf4, 0x60, 0xb0, 0xe6, 0x20, 0xa8, 0x21, 0x4a, 0x56, 0x5c, 0xbc, 0x28, 0x12, 0x42, 0xdc, + 0x5c, 0xec, 0xa1, 0x7e, 0xde, 0x7e, 0xfe, 0xe1, 0x7e, 0x02, 0x0c, 0x20, 0x4e, 0xb0, 0x6b, 0x50, + 0x98, 0xa7, 0x9f, 0xbb, 0x00, 0xa3, 0x10, 0x3f, 0x17, 0xb7, 0x9f, 0x7f, 0x48, 0x3c, 0x4c, 0x80, + 0xc9, 0xa8, 0x92, 0x8b, 0x0d, 0x62, 0x91, 0x50, 0x3e, 0x17, 0x2b, 0xd8, 0x32, 0x21, 0x35, 0x82, + 0xae, 0x01, 0xfb, 0x5b, 0x4a, 0x9d, 0x48, 0x57, 0x2b, 0x89, 0x9e, 0x5a, 0xf7, 0x6e, 0x06, 0x13, + 0x3f, 0x17, 0x2f, 0x58, 0xa1, 0x6e, 0x6e, 0x62, 0x5e, 0x62, 0x7a, 0x6a, 0x91, 0x93, 0xca, 0x89, + 0x87, 0x72, 0x0c, 0x37, 0x1e, 0xca, 0x31, 0x34, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, + 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, + 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x24, 0x36, 0x70, 0xd0, 0x1b, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, + 0xc9, 0xe8, 0xd3, 0x02, 0x0c, 0x02, 0x00, 0x00, } type authenticatedWrapperHealthServer struct { @@ -135,8 +227,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Health service - +// HealthClient is the client API for Health service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type HealthClient interface { Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) } @@ -151,19 +244,26 @@ func NewHealthClient(cc *grpc.ClientConn) HealthClient { func (c *healthClient) Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) { out := new(HealthCheckResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Health/Check", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Health/Check", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Health service - +// HealthServer is the server API for Health service. type HealthServer interface { Check(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) } +// UnimplementedHealthServer can be embedded to have forward compatible implementations. +type UnimplementedHealthServer struct { +} + +func (*UnimplementedHealthServer) Check(ctx context.Context, req *HealthCheckRequest) (*HealthCheckResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Check not implemented") +} + func RegisterHealthServer(s *grpc.Server, srv HealthServer) { s.RegisterService(&_Health_serviceDesc, srv) } @@ -202,7 +302,7 @@ var _Health_serviceDesc = grpc.ServiceDesc{ func (m *HealthCheckRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -210,23 +310,29 @@ func (m *HealthCheckRequest) Marshal() (dAtA []byte, err error) { } func (m *HealthCheckRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HealthCheckRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.Service) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Service) + copy(dAtA[i:], m.Service) i = encodeVarintHealth(dAtA, i, uint64(len(m.Service))) - i += copy(dAtA[i:], m.Service) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *HealthCheckResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -234,26 +340,33 @@ func (m *HealthCheckResponse) Marshal() (dAtA []byte, err error) { } func (m *HealthCheckResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HealthCheckResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Status != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintHealth(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func encodeVarintHealth(dAtA []byte, offset int, v uint64) int { + offset -= sovHealth(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } type raftProxyHealthServer struct { @@ -364,6 +477,9 @@ func (p *raftProxyHealthServer) Check(ctx context.Context, r *HealthCheckRequest } func (m *HealthCheckRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Service) @@ -374,6 +490,9 @@ func (m *HealthCheckRequest) Size() (n int) { } func (m *HealthCheckResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Status != 0 { @@ -383,14 +502,7 @@ func (m *HealthCheckResponse) Size() (n int) { } func sovHealth(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozHealth(x uint64) (n int) { return sovHealth(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -438,7 +550,7 @@ func (m *HealthCheckRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -466,7 +578,7 @@ func (m *HealthCheckRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -476,6 +588,9 @@ func (m *HealthCheckRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthHealth } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthHealth + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -487,7 +602,7 @@ func (m *HealthCheckRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthHealth } if (iNdEx + skippy) > l { @@ -517,7 +632,7 @@ func (m *HealthCheckResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -545,7 +660,7 @@ func (m *HealthCheckResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Status |= (HealthCheckResponse_ServingStatus(b) & 0x7F) << shift + m.Status |= HealthCheckResponse_ServingStatus(b&0x7F) << shift if b < 0x80 { break } @@ -556,7 +671,7 @@ func (m *HealthCheckResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthHealth } if (iNdEx + skippy) > l { @@ -574,6 +689,7 @@ func (m *HealthCheckResponse) Unmarshal(dAtA []byte) error { func skipHealth(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -605,10 +721,8 @@ func skipHealth(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -625,79 +739,34 @@ func skipHealth(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthHealth } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowHealth - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipHealth(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupHealth + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthHealth + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthHealth = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowHealth = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthHealth = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowHealth = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupHealth = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("github.com/docker/swarmkit/api/health.proto", fileDescriptorHealth) } - -var fileDescriptorHealth = []byte{ - // 315 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4e, 0xcf, 0x2c, 0xc9, - 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xc9, 0x4f, 0xce, 0x4e, 0x2d, 0xd2, 0x2f, 0x2e, - 0x4f, 0x2c, 0xca, 0xcd, 0xce, 0x2c, 0xd1, 0x4f, 0x2c, 0xc8, 0xd4, 0xcf, 0x48, 0x4d, 0xcc, 0x29, - 0xc9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x82, 0xa8, 0xd0, 0x83, 0xa9, 0xd0, 0x2b, - 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x4b, 0xeb, 0x83, 0x58, 0x10, 0x95, 0x52, 0xe6, - 0x78, 0x8c, 0x05, 0xab, 0x48, 0x2a, 0x4d, 0xd3, 0x2f, 0xc8, 0x29, 0x4d, 0xcf, 0xcc, 0x83, 0x52, - 0x10, 0x8d, 0x4a, 0x7a, 0x5c, 0x42, 0x1e, 0x60, 0x2b, 0x9d, 0x33, 0x52, 0x93, 0xb3, 0x83, 0x52, - 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x24, 0xb8, 0xd8, 0x8b, 0x53, 0x8b, 0xca, 0x32, 0x93, 0x53, - 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0x60, 0x5c, 0xa5, 0x05, 0x8c, 0x5c, 0xc2, 0x28, 0x1a, - 0x8a, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x85, 0x7c, 0xb9, 0xd8, 0x8a, 0x4b, 0x12, 0x4b, 0x4a, 0x8b, - 0xc1, 0x1a, 0xf8, 0x8c, 0x4c, 0xf5, 0x30, 0xdd, 0xae, 0x87, 0x45, 0xa3, 0x5e, 0x30, 0xc8, 0xe0, - 0xbc, 0xf4, 0x60, 0xb0, 0xe6, 0x20, 0xa8, 0x21, 0x4a, 0x56, 0x5c, 0xbc, 0x28, 0x12, 0x42, 0xdc, - 0x5c, 0xec, 0xa1, 0x7e, 0xde, 0x7e, 0xfe, 0xe1, 0x7e, 0x02, 0x0c, 0x20, 0x4e, 0xb0, 0x6b, 0x50, - 0x98, 0xa7, 0x9f, 0xbb, 0x00, 0xa3, 0x10, 0x3f, 0x17, 0xb7, 0x9f, 0x7f, 0x48, 0x3c, 0x4c, 0x80, - 0xc9, 0xa8, 0x92, 0x8b, 0x0d, 0x62, 0x91, 0x50, 0x3e, 0x17, 0x2b, 0xd8, 0x32, 0x21, 0x35, 0x82, - 0xae, 0x01, 0xfb, 0x5b, 0x4a, 0x9d, 0x48, 0x57, 0x2b, 0x89, 0x9e, 0x5a, 0xf7, 0x6e, 0x06, 0x13, - 0x3f, 0x17, 0x2f, 0x58, 0xa1, 0x6e, 0x6e, 0x62, 0x5e, 0x62, 0x7a, 0x6a, 0x91, 0x93, 0xc4, 0x89, - 0x87, 0x72, 0x0c, 0x37, 0x1e, 0xca, 0x31, 0x34, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, - 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x93, 0xd8, 0xc0, 0xc1, 0x6d, 0x0c, 0x08, 0x00, - 0x00, 0xff, 0xff, 0x7b, 0xf2, 0xdd, 0x23, 0x00, 0x02, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/logbroker.pb.go b/vendor/github.com/docker/swarmkit/api/logbroker.pb.go index 5456c85816dc3..1b95b226c7ef0 100644 --- a/vendor/github.com/docker/swarmkit/api/logbroker.pb.go +++ b/vendor/github.com/docker/swarmkit/api/logbroker.pb.go @@ -3,35 +3,39 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" -import google_protobuf "github.com/gogo/protobuf/types" -import _ "github.com/docker/swarmkit/protobuf/plugin" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import raftselector "github.com/docker/swarmkit/manager/raftselector" -import codes "google.golang.org/grpc/codes" -import status "google.golang.org/grpc/status" -import metadata "google.golang.org/grpc/metadata" -import peer "google.golang.org/grpc/peer" -import rafttime "time" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + context "context" + fmt "fmt" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + raftselector "github.com/docker/swarmkit/manager/raftselector" + _ "github.com/docker/swarmkit/protobuf/plugin" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + types "github.com/gogo/protobuf/types" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + metadata "google.golang.org/grpc/metadata" + peer "google.golang.org/grpc/peer" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + rafttime "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + // LogStream defines the stream from which the log message came. type LogStream int32 @@ -46,6 +50,7 @@ var LogStream_name = map[int32]string{ 1: "LOG_STREAM_STDOUT", 2: "LOG_STREAM_STDERR", } + var LogStream_value = map[string]int32{ "LOG_STREAM_UNKNOWN": 0, "LOG_STREAM_STDOUT": 1, @@ -55,12 +60,15 @@ var LogStream_value = map[string]int32{ func (x LogStream) String() string { return proto.EnumName(LogStream_name, int32(x)) } -func (LogStream) EnumDescriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{0} } + +func (LogStream) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{0} +} type LogSubscriptionOptions struct { // Streams defines which log streams should be sent from the task source. // Empty means send all the messages. - Streams []LogStream `protobuf:"varint,1,rep,name=streams,enum=docker.swarmkit.v1.LogStream" json:"streams,omitempty"` + Streams []LogStream `protobuf:"varint,1,rep,name=streams,proto3,enum=docker.swarmkit.v1.LogStream" json:"streams,omitempty"` // Follow instructs the publisher to continue sending log messages as they // are produced, after satisfying the initial query. Follow bool `protobuf:"varint,2,opt,name=follow,proto3" json:"follow,omitempty"` @@ -83,12 +91,40 @@ type LogSubscriptionOptions struct { // Since indicates that only log messages produced after this timestamp // should be sent. // Note: can't use stdtime because this field is nullable. - Since *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=since" json:"since,omitempty"` + Since *types.Timestamp `protobuf:"bytes,4,opt,name=since,proto3" json:"since,omitempty"` } -func (m *LogSubscriptionOptions) Reset() { *m = LogSubscriptionOptions{} } -func (*LogSubscriptionOptions) ProtoMessage() {} -func (*LogSubscriptionOptions) Descriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{0} } +func (m *LogSubscriptionOptions) Reset() { *m = LogSubscriptionOptions{} } +func (*LogSubscriptionOptions) ProtoMessage() {} +func (*LogSubscriptionOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{0} +} +func (m *LogSubscriptionOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogSubscriptionOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogSubscriptionOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LogSubscriptionOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogSubscriptionOptions.Merge(m, src) +} +func (m *LogSubscriptionOptions) XXX_Size() int { + return m.Size() +} +func (m *LogSubscriptionOptions) XXX_DiscardUnknown() { + xxx_messageInfo_LogSubscriptionOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_LogSubscriptionOptions proto.InternalMessageInfo // LogSelector will match logs from ANY of the defined parameters. // @@ -96,14 +132,42 @@ func (*LogSubscriptionOptions) Descriptor() ([]byte, []int) { return fileDescrip // possible. For example, if they want to listen to all the tasks of a service, // they should use the service id, rather than specifying the individual tasks. type LogSelector struct { - ServiceIDs []string `protobuf:"bytes,1,rep,name=service_ids,json=serviceIds" json:"service_ids,omitempty"` - NodeIDs []string `protobuf:"bytes,2,rep,name=node_ids,json=nodeIds" json:"node_ids,omitempty"` - TaskIDs []string `protobuf:"bytes,3,rep,name=task_ids,json=taskIds" json:"task_ids,omitempty"` + ServiceIDs []string `protobuf:"bytes,1,rep,name=service_ids,json=serviceIds,proto3" json:"service_ids,omitempty"` + NodeIDs []string `protobuf:"bytes,2,rep,name=node_ids,json=nodeIds,proto3" json:"node_ids,omitempty"` + TaskIDs []string `protobuf:"bytes,3,rep,name=task_ids,json=taskIds,proto3" json:"task_ids,omitempty"` +} + +func (m *LogSelector) Reset() { *m = LogSelector{} } +func (*LogSelector) ProtoMessage() {} +func (*LogSelector) Descriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{1} +} +func (m *LogSelector) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogSelector) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogSelector.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LogSelector) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogSelector.Merge(m, src) +} +func (m *LogSelector) XXX_Size() int { + return m.Size() +} +func (m *LogSelector) XXX_DiscardUnknown() { + xxx_messageInfo_LogSelector.DiscardUnknown(m) } -func (m *LogSelector) Reset() { *m = LogSelector{} } -func (*LogSelector) ProtoMessage() {} -func (*LogSelector) Descriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{1} } +var xxx_messageInfo_LogSelector proto.InternalMessageInfo // LogContext marks the context from which a log message was generated. type LogContext struct { @@ -112,9 +176,37 @@ type LogContext struct { TaskID string `protobuf:"bytes,3,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` } -func (m *LogContext) Reset() { *m = LogContext{} } -func (*LogContext) ProtoMessage() {} -func (*LogContext) Descriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{2} } +func (m *LogContext) Reset() { *m = LogContext{} } +func (*LogContext) ProtoMessage() {} +func (*LogContext) Descriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{2} +} +func (m *LogContext) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogContext) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogContext.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LogContext) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogContext.Merge(m, src) +} +func (m *LogContext) XXX_Size() int { + return m.Size() +} +func (m *LogContext) XXX_DiscardUnknown() { + xxx_messageInfo_LogContext.DiscardUnknown(m) +} + +var xxx_messageInfo_LogContext proto.InternalMessageInfo // LogAttr is an extra key/value pair that may be have been set by users type LogAttr struct { @@ -122,47 +214,159 @@ type LogAttr struct { Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } -func (m *LogAttr) Reset() { *m = LogAttr{} } -func (*LogAttr) ProtoMessage() {} -func (*LogAttr) Descriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{3} } +func (m *LogAttr) Reset() { *m = LogAttr{} } +func (*LogAttr) ProtoMessage() {} +func (*LogAttr) Descriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{3} +} +func (m *LogAttr) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogAttr) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogAttr.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LogAttr) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogAttr.Merge(m, src) +} +func (m *LogAttr) XXX_Size() int { + return m.Size() +} +func (m *LogAttr) XXX_DiscardUnknown() { + xxx_messageInfo_LogAttr.DiscardUnknown(m) +} + +var xxx_messageInfo_LogAttr proto.InternalMessageInfo // LogMessage type LogMessage struct { // Context identifies the source of the log message. - Context LogContext `protobuf:"bytes,1,opt,name=context" json:"context"` + Context LogContext `protobuf:"bytes,1,opt,name=context,proto3" json:"context"` // Timestamp is the time at which the message was generated. // Note: can't use stdtime because this field is nullable. - Timestamp *google_protobuf.Timestamp `protobuf:"bytes,2,opt,name=timestamp" json:"timestamp,omitempty"` + Timestamp *types.Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Stream identifies the stream of the log message, stdout or stderr. Stream LogStream `protobuf:"varint,3,opt,name=stream,proto3,enum=docker.swarmkit.v1.LogStream" json:"stream,omitempty"` // Data is the raw log message, as generated by the application. Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` // Attrs is a list of key value pairs representing additional log details // that may have been returned from the logger - Attrs []LogAttr `protobuf:"bytes,5,rep,name=attrs" json:"attrs"` + Attrs []LogAttr `protobuf:"bytes,5,rep,name=attrs,proto3" json:"attrs"` +} + +func (m *LogMessage) Reset() { *m = LogMessage{} } +func (*LogMessage) ProtoMessage() {} +func (*LogMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{4} +} +func (m *LogMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LogMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LogMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LogMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_LogMessage.Merge(m, src) +} +func (m *LogMessage) XXX_Size() int { + return m.Size() +} +func (m *LogMessage) XXX_DiscardUnknown() { + xxx_messageInfo_LogMessage.DiscardUnknown(m) } -func (m *LogMessage) Reset() { *m = LogMessage{} } -func (*LogMessage) ProtoMessage() {} -func (*LogMessage) Descriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{4} } +var xxx_messageInfo_LogMessage proto.InternalMessageInfo type SubscribeLogsRequest struct { // LogSelector describes the logs to which the subscriber is - Selector *LogSelector `protobuf:"bytes,1,opt,name=selector" json:"selector,omitempty"` - Options *LogSubscriptionOptions `protobuf:"bytes,2,opt,name=options" json:"options,omitempty"` + Selector *LogSelector `protobuf:"bytes,1,opt,name=selector,proto3" json:"selector,omitempty"` + Options *LogSubscriptionOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` } -func (m *SubscribeLogsRequest) Reset() { *m = SubscribeLogsRequest{} } -func (*SubscribeLogsRequest) ProtoMessage() {} -func (*SubscribeLogsRequest) Descriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{5} } +func (m *SubscribeLogsRequest) Reset() { *m = SubscribeLogsRequest{} } +func (*SubscribeLogsRequest) ProtoMessage() {} +func (*SubscribeLogsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{5} +} +func (m *SubscribeLogsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubscribeLogsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubscribeLogsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SubscribeLogsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubscribeLogsRequest.Merge(m, src) +} +func (m *SubscribeLogsRequest) XXX_Size() int { + return m.Size() +} +func (m *SubscribeLogsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SubscribeLogsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SubscribeLogsRequest proto.InternalMessageInfo type SubscribeLogsMessage struct { - Messages []LogMessage `protobuf:"bytes,1,rep,name=messages" json:"messages"` + Messages []LogMessage `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages"` } -func (m *SubscribeLogsMessage) Reset() { *m = SubscribeLogsMessage{} } -func (*SubscribeLogsMessage) ProtoMessage() {} -func (*SubscribeLogsMessage) Descriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{6} } +func (m *SubscribeLogsMessage) Reset() { *m = SubscribeLogsMessage{} } +func (*SubscribeLogsMessage) ProtoMessage() {} +func (*SubscribeLogsMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{6} +} +func (m *SubscribeLogsMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubscribeLogsMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubscribeLogsMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SubscribeLogsMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubscribeLogsMessage.Merge(m, src) +} +func (m *SubscribeLogsMessage) XXX_Size() int { + return m.Size() +} +func (m *SubscribeLogsMessage) XXX_DiscardUnknown() { + xxx_messageInfo_SubscribeLogsMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_SubscribeLogsMessage proto.InternalMessageInfo // ListenSubscriptionsRequest is a placeholder to begin listening for // subscriptions. @@ -172,9 +376,35 @@ type ListenSubscriptionsRequest struct { func (m *ListenSubscriptionsRequest) Reset() { *m = ListenSubscriptionsRequest{} } func (*ListenSubscriptionsRequest) ProtoMessage() {} func (*ListenSubscriptionsRequest) Descriptor() ([]byte, []int) { - return fileDescriptorLogbroker, []int{7} + return fileDescriptor_d5aa8d24ac30376c, []int{7} +} +func (m *ListenSubscriptionsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListenSubscriptionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListenSubscriptionsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListenSubscriptionsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListenSubscriptionsRequest.Merge(m, src) +} +func (m *ListenSubscriptionsRequest) XXX_Size() int { + return m.Size() +} +func (m *ListenSubscriptionsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListenSubscriptionsRequest.DiscardUnknown(m) } +var xxx_messageInfo_ListenSubscriptionsRequest proto.InternalMessageInfo + // SubscriptionMessage instructs the listener to start publishing messages for // the stream or end a subscription. // @@ -183,24 +413,52 @@ type SubscriptionMessage struct { // ID identifies the subscription. ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // Selector defines which sources should be sent for the subscription. - Selector *LogSelector `protobuf:"bytes,2,opt,name=selector" json:"selector,omitempty"` + Selector *LogSelector `protobuf:"bytes,2,opt,name=selector,proto3" json:"selector,omitempty"` // Options specify how the subscription should be satisfied. - Options *LogSubscriptionOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` + Options *LogSubscriptionOptions `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"` // Close will be true if the node should shutdown the subscription with the // provided identifier. Close bool `protobuf:"varint,4,opt,name=close,proto3" json:"close,omitempty"` } -func (m *SubscriptionMessage) Reset() { *m = SubscriptionMessage{} } -func (*SubscriptionMessage) ProtoMessage() {} -func (*SubscriptionMessage) Descriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{8} } +func (m *SubscriptionMessage) Reset() { *m = SubscriptionMessage{} } +func (*SubscriptionMessage) ProtoMessage() {} +func (*SubscriptionMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{8} +} +func (m *SubscriptionMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubscriptionMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubscriptionMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SubscriptionMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubscriptionMessage.Merge(m, src) +} +func (m *SubscriptionMessage) XXX_Size() int { + return m.Size() +} +func (m *SubscriptionMessage) XXX_DiscardUnknown() { + xxx_messageInfo_SubscriptionMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_SubscriptionMessage proto.InternalMessageInfo type PublishLogsMessage struct { // SubscriptionID identifies which subscription the set of messages should // be sent to. We can think of this as a "mail box" for the subscription. SubscriptionID string `protobuf:"bytes,1,opt,name=subscription_id,json=subscriptionId,proto3" json:"subscription_id,omitempty"` // Messages is the log message for publishing. - Messages []LogMessage `protobuf:"bytes,2,rep,name=messages" json:"messages"` + Messages []LogMessage `protobuf:"bytes,2,rep,name=messages,proto3" json:"messages"` // Close is a boolean for whether or not the client has completed its log // stream. When close is called, the manager can hang up the subscription. // Any further logs from this subscription are an error condition. Any @@ -208,18 +466,75 @@ type PublishLogsMessage struct { Close bool `protobuf:"varint,3,opt,name=close,proto3" json:"close,omitempty"` } -func (m *PublishLogsMessage) Reset() { *m = PublishLogsMessage{} } -func (*PublishLogsMessage) ProtoMessage() {} -func (*PublishLogsMessage) Descriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{9} } +func (m *PublishLogsMessage) Reset() { *m = PublishLogsMessage{} } +func (*PublishLogsMessage) ProtoMessage() {} +func (*PublishLogsMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{9} +} +func (m *PublishLogsMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PublishLogsMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PublishLogsMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PublishLogsMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_PublishLogsMessage.Merge(m, src) +} +func (m *PublishLogsMessage) XXX_Size() int { + return m.Size() +} +func (m *PublishLogsMessage) XXX_DiscardUnknown() { + xxx_messageInfo_PublishLogsMessage.DiscardUnknown(m) +} + +var xxx_messageInfo_PublishLogsMessage proto.InternalMessageInfo type PublishLogsResponse struct { } -func (m *PublishLogsResponse) Reset() { *m = PublishLogsResponse{} } -func (*PublishLogsResponse) ProtoMessage() {} -func (*PublishLogsResponse) Descriptor() ([]byte, []int) { return fileDescriptorLogbroker, []int{10} } +func (m *PublishLogsResponse) Reset() { *m = PublishLogsResponse{} } +func (*PublishLogsResponse) ProtoMessage() {} +func (*PublishLogsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d5aa8d24ac30376c, []int{10} +} +func (m *PublishLogsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PublishLogsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PublishLogsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PublishLogsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PublishLogsResponse.Merge(m, src) +} +func (m *PublishLogsResponse) XXX_Size() int { + return m.Size() +} +func (m *PublishLogsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PublishLogsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PublishLogsResponse proto.InternalMessageInfo func init() { + proto.RegisterEnum("docker.swarmkit.v1.LogStream", LogStream_name, LogStream_value) proto.RegisterType((*LogSubscriptionOptions)(nil), "docker.swarmkit.v1.LogSubscriptionOptions") proto.RegisterType((*LogSelector)(nil), "docker.swarmkit.v1.LogSelector") proto.RegisterType((*LogContext)(nil), "docker.swarmkit.v1.LogContext") @@ -231,7 +546,76 @@ func init() { proto.RegisterType((*SubscriptionMessage)(nil), "docker.swarmkit.v1.SubscriptionMessage") proto.RegisterType((*PublishLogsMessage)(nil), "docker.swarmkit.v1.PublishLogsMessage") proto.RegisterType((*PublishLogsResponse)(nil), "docker.swarmkit.v1.PublishLogsResponse") - proto.RegisterEnum("docker.swarmkit.v1.LogStream", LogStream_name, LogStream_value) +} + +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/logbroker.proto", fileDescriptor_d5aa8d24ac30376c) +} + +var fileDescriptor_d5aa8d24ac30376c = []byte{ + // 979 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x95, 0x41, 0x6f, 0x1b, 0x45, + 0x14, 0xc7, 0x3d, 0xeb, 0xc4, 0x8e, 0x9f, 0x9b, 0xc4, 0x9d, 0xa4, 0x91, 0x65, 0xe8, 0xda, 0xda, + 0xa2, 0x62, 0x45, 0x65, 0xdd, 0x1a, 0xa1, 0x22, 0x45, 0x42, 0xd4, 0xa4, 0x42, 0x16, 0x6e, 0x82, + 0x26, 0x8e, 0xe0, 0x16, 0xad, 0xed, 0xe9, 0x76, 0xe5, 0xf5, 0x8e, 0xd9, 0x19, 0x27, 0x20, 0x71, + 0xe0, 0x50, 0x24, 0x94, 0x03, 0xe2, 0x82, 0x04, 0x87, 0x9e, 0xe8, 0x05, 0x21, 0x71, 0xe1, 0xc6, + 0x07, 0x40, 0x11, 0xa7, 0x1e, 0x7b, 0xb2, 0xe8, 0xe6, 0xce, 0x67, 0x40, 0x3b, 0xb3, 0x5e, 0x6f, + 0xb0, 0x9d, 0xa2, 0x72, 0xb1, 0x67, 0x3c, 0xbf, 0xb7, 0xef, 0xff, 0xfe, 0xf3, 0xde, 0x1a, 0x4c, + 0xdb, 0x11, 0x8f, 0x46, 0x1d, 0xb3, 0xcb, 0x06, 0xb5, 0x1e, 0xeb, 0xf6, 0xa9, 0x5f, 0xe3, 0x27, + 0x96, 0x3f, 0xe8, 0x3b, 0xa2, 0x66, 0x0d, 0x9d, 0x9a, 0xcb, 0xec, 0x8e, 0xcf, 0xfa, 0xd4, 0x37, + 0x87, 0x3e, 0x13, 0x0c, 0x63, 0x05, 0x99, 0x13, 0xc8, 0x3c, 0xbe, 0x53, 0xda, 0xb4, 0x99, 0xcd, + 0xe4, 0x71, 0x2d, 0x5c, 0x29, 0xb2, 0x54, 0xb6, 0x19, 0xb3, 0x5d, 0x5a, 0x93, 0xbb, 0xce, 0xe8, + 0x61, 0x4d, 0x38, 0x03, 0xca, 0x85, 0x35, 0x18, 0x46, 0xc0, 0xdd, 0x4b, 0x52, 0xc7, 0x41, 0x43, + 0x77, 0x64, 0x3b, 0x5e, 0xf4, 0xa5, 0x02, 0x8d, 0xdf, 0x10, 0x6c, 0xb5, 0x98, 0x7d, 0x30, 0xea, + 0xf0, 0xae, 0xef, 0x0c, 0x85, 0xc3, 0xbc, 0x7d, 0xf9, 0xc9, 0xf1, 0x0e, 0x64, 0xb9, 0xf0, 0xa9, + 0x35, 0xe0, 0x45, 0x54, 0x49, 0x57, 0xd7, 0xea, 0xd7, 0xcd, 0x59, 0xc1, 0x66, 0x18, 0x2c, 0xa9, + 0x86, 0x56, 0x48, 0x91, 0x49, 0x04, 0xde, 0x82, 0xcc, 0x43, 0xe6, 0xba, 0xec, 0xa4, 0xa8, 0x55, + 0x50, 0x75, 0x85, 0x44, 0x3b, 0x8c, 0x61, 0x49, 0x58, 0x8e, 0x5b, 0x4c, 0x57, 0x50, 0x35, 0x4d, + 0xe4, 0x1a, 0xdf, 0x86, 0x65, 0xee, 0x78, 0x5d, 0x5a, 0x5c, 0xaa, 0xa0, 0x6a, 0xbe, 0x5e, 0x32, + 0x55, 0xb5, 0xe6, 0x44, 0xb8, 0xd9, 0x9e, 0x54, 0x4b, 0x14, 0x68, 0x7c, 0x8b, 0x20, 0x1f, 0x26, + 0xa6, 0x2e, 0xed, 0x0a, 0xe6, 0xe3, 0x1a, 0xe4, 0x39, 0xf5, 0x8f, 0x9d, 0x2e, 0x3d, 0x72, 0x7a, + 0x4a, 0x6e, 0xae, 0xb1, 0x16, 0x8c, 0xcb, 0x70, 0xa0, 0x7e, 0x6e, 0xee, 0x72, 0x02, 0x11, 0xd2, + 0xec, 0x71, 0x7c, 0x13, 0x56, 0x3c, 0xd6, 0x53, 0xb4, 0x26, 0xe9, 0x7c, 0x30, 0x2e, 0x67, 0xf7, + 0x58, 0x4f, 0xa2, 0xd9, 0xf0, 0x30, 0xe2, 0x84, 0xc5, 0xfb, 0x92, 0x4b, 0x4f, 0xb9, 0xb6, 0xc5, + 0xfb, 0x92, 0x0b, 0x0f, 0x9b, 0x3d, 0x6e, 0x3c, 0x46, 0x00, 0x2d, 0x66, 0x7f, 0xc0, 0x3c, 0x41, + 0x3f, 0x17, 0xf8, 0x16, 0xc0, 0x54, 0x4f, 0x11, 0x55, 0x50, 0x35, 0xd7, 0x58, 0x0d, 0xc6, 0xe5, + 0x5c, 0x2c, 0x87, 0xe4, 0x62, 0x35, 0xf8, 0x06, 0x64, 0x23, 0x31, 0xd2, 0xac, 0x5c, 0x03, 0x82, + 0x71, 0x39, 0xa3, 0xb4, 0x90, 0x8c, 0x92, 0x12, 0x42, 0x91, 0x12, 0xe9, 0x5d, 0x04, 0x29, 0x21, + 0x24, 0xa3, 0x74, 0x18, 0x77, 0x20, 0xdb, 0x62, 0xf6, 0x3d, 0x21, 0x7c, 0x5c, 0x80, 0x74, 0x9f, + 0x7e, 0xa1, 0x72, 0x93, 0x70, 0x89, 0x37, 0x61, 0xf9, 0xd8, 0x72, 0x47, 0x54, 0x25, 0x21, 0x6a, + 0x63, 0x9c, 0x6a, 0x52, 0xf9, 0x03, 0xca, 0xb9, 0x65, 0x53, 0xfc, 0x1e, 0x64, 0xbb, 0xaa, 0x08, + 0x19, 0x9a, 0xaf, 0xeb, 0x0b, 0x2e, 0x3d, 0x2a, 0xb5, 0xb1, 0x74, 0x36, 0x2e, 0xa7, 0xc8, 0x24, + 0x08, 0xbf, 0x0b, 0xb9, 0xb8, 0x37, 0x65, 0xa2, 0xcb, 0xef, 0x73, 0x0a, 0xe3, 0x77, 0x20, 0xa3, + 0x9a, 0x47, 0xd6, 0xf7, 0xb2, 0x6e, 0x23, 0x11, 0x1c, 0x36, 0x54, 0xcf, 0x12, 0x96, 0xec, 0x9d, + 0x2b, 0x44, 0xae, 0xf1, 0x5d, 0x58, 0xb6, 0x84, 0xf0, 0x79, 0x71, 0xb9, 0x92, 0xae, 0xe6, 0xeb, + 0xaf, 0x2d, 0x78, 0x52, 0xe8, 0x53, 0xa4, 0x5f, 0xf1, 0xc6, 0x8f, 0x08, 0x36, 0xa3, 0x51, 0xe8, + 0xd0, 0x16, 0xb3, 0x39, 0xa1, 0x9f, 0x8d, 0x28, 0x17, 0x78, 0x07, 0x56, 0x78, 0xd4, 0x6c, 0x91, + 0x2f, 0xe5, 0x45, 0xf2, 0x22, 0x8c, 0xc4, 0x01, 0x78, 0x17, 0xb2, 0x4c, 0xcd, 0x54, 0xe4, 0xc8, + 0xf6, 0xa2, 0xd8, 0xd9, 0x29, 0x24, 0x93, 0x50, 0xe3, 0xd3, 0x7f, 0x49, 0x9b, 0xdc, 0xd8, 0xfb, + 0xb0, 0x32, 0x50, 0x4b, 0xd5, 0xf8, 0x8b, 0xaf, 0x2c, 0x8a, 0x88, 0x4a, 0x8e, 0xa3, 0x8c, 0xd7, + 0xa1, 0xd4, 0x72, 0xb8, 0xa0, 0x5e, 0x32, 0xff, 0xa4, 0x74, 0xe3, 0x0f, 0x04, 0x1b, 0xc9, 0x83, + 0x49, 0xde, 0x2d, 0xd0, 0xe2, 0xde, 0xce, 0x04, 0xe3, 0xb2, 0xd6, 0xdc, 0x25, 0x9a, 0xd3, 0xbb, + 0x60, 0x95, 0xf6, 0x3f, 0xac, 0x4a, 0xbf, 0xb2, 0x55, 0x61, 0xa7, 0x77, 0x5d, 0xc6, 0xd5, 0x0b, + 0x65, 0x85, 0xa8, 0x8d, 0xf1, 0x33, 0x02, 0xfc, 0xf1, 0xa8, 0xe3, 0x3a, 0xfc, 0x51, 0xd2, 0xbf, + 0x1d, 0x58, 0xe7, 0x89, 0x87, 0x4d, 0x07, 0x16, 0x07, 0xe3, 0xf2, 0x5a, 0x32, 0x4f, 0x73, 0x97, + 0xac, 0x25, 0xd1, 0x66, 0xef, 0x82, 0xf9, 0xda, 0xab, 0x98, 0x3f, 0xd5, 0x9a, 0x4e, 0x6a, 0xbd, + 0x06, 0x1b, 0x09, 0xa9, 0x84, 0xf2, 0x21, 0xf3, 0x38, 0xdd, 0x7e, 0x8a, 0x20, 0x17, 0x8f, 0x00, + 0xbe, 0x05, 0xb8, 0xb5, 0xff, 0xe1, 0xd1, 0x41, 0x9b, 0xdc, 0xbf, 0xf7, 0xe0, 0xe8, 0x70, 0xef, + 0xa3, 0xbd, 0xfd, 0x4f, 0xf6, 0x0a, 0xa9, 0xd2, 0xe6, 0xe9, 0x93, 0x4a, 0x21, 0xc6, 0x0e, 0xbd, + 0xbe, 0xc7, 0x4e, 0x3c, 0xbc, 0x0d, 0x57, 0x13, 0xf4, 0x41, 0x7b, 0x77, 0xff, 0xb0, 0x5d, 0x40, + 0xa5, 0x8d, 0xd3, 0x27, 0x95, 0xf5, 0x18, 0x3e, 0x10, 0x3d, 0x36, 0x12, 0xb3, 0xec, 0x7d, 0x42, + 0x0a, 0xda, 0x2c, 0x4b, 0x7d, 0xbf, 0x74, 0xf5, 0x9b, 0x9f, 0xf4, 0xd4, 0xef, 0x4f, 0xf5, 0xa9, + 0xb0, 0xfa, 0x63, 0x04, 0x4b, 0xa1, 0x6e, 0xfc, 0x25, 0xac, 0x5e, 0xe8, 0x59, 0x5c, 0x9d, 0xe7, + 0xce, 0xbc, 0x89, 0x2b, 0xbd, 0x9c, 0x8c, 0x1c, 0x35, 0xae, 0xfd, 0xf9, 0xeb, 0xdf, 0x3f, 0x68, + 0xeb, 0xb0, 0x2a, 0xc9, 0xb7, 0x06, 0x96, 0x67, 0xd9, 0xd4, 0xbf, 0x8d, 0xea, 0xbf, 0x68, 0xd2, + 0xad, 0x86, 0xfc, 0xcf, 0xc5, 0xdf, 0x23, 0xd8, 0x98, 0xd3, 0xe6, 0xd8, 0x9c, 0x7b, 0x61, 0x0b, + 0xe7, 0xa1, 0xf4, 0xe6, 0x25, 0xc2, 0x92, 0x03, 0x62, 0xdc, 0x90, 0xba, 0xae, 0xc3, 0x15, 0xa5, + 0xeb, 0x84, 0xf9, 0x7d, 0xea, 0xcf, 0xa8, 0xc4, 0x5f, 0x23, 0xc8, 0x27, 0xee, 0x1a, 0xdf, 0x9c, + 0xf7, 0xfc, 0xd9, 0xbe, 0x9d, 0xaf, 0x63, 0x4e, 0xd3, 0xfc, 0x27, 0x1d, 0x55, 0xd4, 0x78, 0xe3, + 0xec, 0x85, 0x9e, 0x7a, 0xfe, 0x42, 0x4f, 0x7d, 0x15, 0xe8, 0xe8, 0x2c, 0xd0, 0xd1, 0xb3, 0x40, + 0x47, 0x7f, 0x05, 0x3a, 0xfa, 0xee, 0x5c, 0x4f, 0x3d, 0x3b, 0xd7, 0x53, 0xcf, 0xcf, 0xf5, 0x54, + 0x27, 0x23, 0x5f, 0xe2, 0x6f, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0x00, 0xba, 0x6b, 0x91, 0xec, + 0x08, 0x00, 0x00, } type authenticatedWrapperLogsServer struct { @@ -301,8 +685,8 @@ func (m *LogSubscriptionOptions) CopyFrom(src interface{}) { } if o.Since != nil { - m.Since = &google_protobuf.Timestamp{} - deepcopy.Copy(m.Since, o.Since) + m.Since = &types.Timestamp{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Since, o.Since) } } @@ -379,10 +763,10 @@ func (m *LogMessage) CopyFrom(src interface{}) { o := src.(*LogMessage) *m = *o - deepcopy.Copy(&m.Context, &o.Context) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Context, &o.Context) if o.Timestamp != nil { - m.Timestamp = &google_protobuf.Timestamp{} - deepcopy.Copy(m.Timestamp, o.Timestamp) + m.Timestamp = &types.Timestamp{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Timestamp, o.Timestamp) } if o.Data != nil { m.Data = make([]byte, len(o.Data)) @@ -391,7 +775,7 @@ func (m *LogMessage) CopyFrom(src interface{}) { if o.Attrs != nil { m.Attrs = make([]LogAttr, len(o.Attrs)) for i := range m.Attrs { - deepcopy.Copy(&m.Attrs[i], &o.Attrs[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Attrs[i], &o.Attrs[i]) } } @@ -412,11 +796,11 @@ func (m *SubscribeLogsRequest) CopyFrom(src interface{}) { *m = *o if o.Selector != nil { m.Selector = &LogSelector{} - deepcopy.Copy(m.Selector, o.Selector) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Selector, o.Selector) } if o.Options != nil { m.Options = &LogSubscriptionOptions{} - deepcopy.Copy(m.Options, o.Options) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Options, o.Options) } } @@ -436,7 +820,7 @@ func (m *SubscribeLogsMessage) CopyFrom(src interface{}) { if o.Messages != nil { m.Messages = make([]LogMessage, len(o.Messages)) for i := range m.Messages { - deepcopy.Copy(&m.Messages[i], &o.Messages[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Messages[i], &o.Messages[i]) } } @@ -467,11 +851,11 @@ func (m *SubscriptionMessage) CopyFrom(src interface{}) { *m = *o if o.Selector != nil { m.Selector = &LogSelector{} - deepcopy.Copy(m.Selector, o.Selector) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Selector, o.Selector) } if o.Options != nil { m.Options = &LogSubscriptionOptions{} - deepcopy.Copy(m.Options, o.Options) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Options, o.Options) } } @@ -491,7 +875,7 @@ func (m *PublishLogsMessage) CopyFrom(src interface{}) { if o.Messages != nil { m.Messages = make([]LogMessage, len(o.Messages)) for i := range m.Messages { - deepcopy.Copy(&m.Messages[i], &o.Messages[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Messages[i], &o.Messages[i]) } } @@ -516,8 +900,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Logs service - +// LogsClient is the client API for Logs service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type LogsClient interface { // SubscribeLogs starts a subscription with the specified selector and options. // @@ -537,7 +922,7 @@ func NewLogsClient(cc *grpc.ClientConn) LogsClient { } func (c *logsClient) SubscribeLogs(ctx context.Context, in *SubscribeLogsRequest, opts ...grpc.CallOption) (Logs_SubscribeLogsClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Logs_serviceDesc.Streams[0], c.cc, "/docker.swarmkit.v1.Logs/SubscribeLogs", opts...) + stream, err := c.cc.NewStream(ctx, &_Logs_serviceDesc.Streams[0], "/docker.swarmkit.v1.Logs/SubscribeLogs", opts...) if err != nil { return nil, err } @@ -568,8 +953,7 @@ func (x *logsSubscribeLogsClient) Recv() (*SubscribeLogsMessage, error) { return m, nil } -// Server API for Logs service - +// LogsServer is the server API for Logs service. type LogsServer interface { // SubscribeLogs starts a subscription with the specified selector and options. // @@ -580,6 +964,14 @@ type LogsServer interface { SubscribeLogs(*SubscribeLogsRequest, Logs_SubscribeLogsServer) error } +// UnimplementedLogsServer can be embedded to have forward compatible implementations. +type UnimplementedLogsServer struct { +} + +func (*UnimplementedLogsServer) SubscribeLogs(req *SubscribeLogsRequest, srv Logs_SubscribeLogsServer) error { + return status.Errorf(codes.Unimplemented, "method SubscribeLogs not implemented") +} + func RegisterLogsServer(s *grpc.Server, srv LogsServer) { s.RegisterService(&_Logs_serviceDesc, srv) } @@ -619,8 +1011,9 @@ var _Logs_serviceDesc = grpc.ServiceDesc{ Metadata: "github.com/docker/swarmkit/api/logbroker.proto", } -// Client API for LogBroker service - +// LogBrokerClient is the client API for LogBroker service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type LogBrokerClient interface { // ListenSubscriptions starts a subscription stream for the node. For each // message received, the node should attempt to satisfy the subscription. @@ -642,7 +1035,7 @@ func NewLogBrokerClient(cc *grpc.ClientConn) LogBrokerClient { } func (c *logBrokerClient) ListenSubscriptions(ctx context.Context, in *ListenSubscriptionsRequest, opts ...grpc.CallOption) (LogBroker_ListenSubscriptionsClient, error) { - stream, err := grpc.NewClientStream(ctx, &_LogBroker_serviceDesc.Streams[0], c.cc, "/docker.swarmkit.v1.LogBroker/ListenSubscriptions", opts...) + stream, err := c.cc.NewStream(ctx, &_LogBroker_serviceDesc.Streams[0], "/docker.swarmkit.v1.LogBroker/ListenSubscriptions", opts...) if err != nil { return nil, err } @@ -674,7 +1067,7 @@ func (x *logBrokerListenSubscriptionsClient) Recv() (*SubscriptionMessage, error } func (c *logBrokerClient) PublishLogs(ctx context.Context, opts ...grpc.CallOption) (LogBroker_PublishLogsClient, error) { - stream, err := grpc.NewClientStream(ctx, &_LogBroker_serviceDesc.Streams[1], c.cc, "/docker.swarmkit.v1.LogBroker/PublishLogs", opts...) + stream, err := c.cc.NewStream(ctx, &_LogBroker_serviceDesc.Streams[1], "/docker.swarmkit.v1.LogBroker/PublishLogs", opts...) if err != nil { return nil, err } @@ -707,8 +1100,7 @@ func (x *logBrokerPublishLogsClient) CloseAndRecv() (*PublishLogsResponse, error return m, nil } -// Server API for LogBroker service - +// LogBrokerServer is the server API for LogBroker service. type LogBrokerServer interface { // ListenSubscriptions starts a subscription stream for the node. For each // message received, the node should attempt to satisfy the subscription. @@ -721,6 +1113,17 @@ type LogBrokerServer interface { PublishLogs(LogBroker_PublishLogsServer) error } +// UnimplementedLogBrokerServer can be embedded to have forward compatible implementations. +type UnimplementedLogBrokerServer struct { +} + +func (*UnimplementedLogBrokerServer) ListenSubscriptions(req *ListenSubscriptionsRequest, srv LogBroker_ListenSubscriptionsServer) error { + return status.Errorf(codes.Unimplemented, "method ListenSubscriptions not implemented") +} +func (*UnimplementedLogBrokerServer) PublishLogs(srv LogBroker_PublishLogsServer) error { + return status.Errorf(codes.Unimplemented, "method PublishLogs not implemented") +} + func RegisterLogBrokerServer(s *grpc.Server, srv LogBrokerServer) { s.RegisterService(&_LogBroker_serviceDesc, srv) } @@ -794,7 +1197,7 @@ var _LogBroker_serviceDesc = grpc.ServiceDesc{ func (m *LogSubscriptionOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -802,49 +1205,56 @@ func (m *LogSubscriptionOptions) Marshal() (dAtA []byte, err error) { } func (m *LogSubscriptionOptions) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogSubscriptionOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Streams) > 0 { - for _, num := range m.Streams { - dAtA[i] = 0x8 - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(num)) + if m.Since != nil { + { + size, err := m.Since.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogbroker(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x22 + } + if m.Tail != 0 { + i = encodeVarintLogbroker(dAtA, i, uint64(m.Tail)) + i-- + dAtA[i] = 0x18 } if m.Follow { - dAtA[i] = 0x10 - i++ + i-- if m.Follow { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ - } - if m.Tail != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(m.Tail)) + i-- + dAtA[i] = 0x10 } - if m.Since != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(m.Since.Size())) - n1, err := m.Since.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Streams) > 0 { + for iNdEx := len(m.Streams) - 1; iNdEx >= 0; iNdEx-- { + i = encodeVarintLogbroker(dAtA, i, uint64(m.Streams[iNdEx])) + i-- + dAtA[i] = 0x8 } - i += n1 } - return i, nil + return len(dAtA) - i, nil } func (m *LogSelector) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -852,62 +1262,49 @@ func (m *LogSelector) Marshal() (dAtA []byte, err error) { } func (m *LogSelector) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogSelector) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ServiceIDs) > 0 { - for _, s := range m.ServiceIDs { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + if len(m.TaskIDs) > 0 { + for iNdEx := len(m.TaskIDs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.TaskIDs[iNdEx]) + copy(dAtA[i:], m.TaskIDs[iNdEx]) + i = encodeVarintLogbroker(dAtA, i, uint64(len(m.TaskIDs[iNdEx]))) + i-- + dAtA[i] = 0x1a } } if len(m.NodeIDs) > 0 { - for _, s := range m.NodeIDs { + for iNdEx := len(m.NodeIDs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.NodeIDs[iNdEx]) + copy(dAtA[i:], m.NodeIDs[iNdEx]) + i = encodeVarintLogbroker(dAtA, i, uint64(len(m.NodeIDs[iNdEx]))) + i-- dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - if len(m.TaskIDs) > 0 { - for _, s := range m.TaskIDs { - dAtA[i] = 0x1a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + if len(m.ServiceIDs) > 0 { + for iNdEx := len(m.ServiceIDs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ServiceIDs[iNdEx]) + copy(dAtA[i:], m.ServiceIDs[iNdEx]) + i = encodeVarintLogbroker(dAtA, i, uint64(len(m.ServiceIDs[iNdEx]))) + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *LogContext) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -915,35 +1312,43 @@ func (m *LogContext) Marshal() (dAtA []byte, err error) { } func (m *LogContext) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogContext) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ServiceID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(len(m.ServiceID))) - i += copy(dAtA[i:], m.ServiceID) + if len(m.TaskID) > 0 { + i -= len(m.TaskID) + copy(dAtA[i:], m.TaskID) + i = encodeVarintLogbroker(dAtA, i, uint64(len(m.TaskID))) + i-- + dAtA[i] = 0x1a } if len(m.NodeID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) i = encodeVarintLogbroker(dAtA, i, uint64(len(m.NodeID))) - i += copy(dAtA[i:], m.NodeID) + i-- + dAtA[i] = 0x12 } - if len(m.TaskID) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(len(m.TaskID))) - i += copy(dAtA[i:], m.TaskID) + if len(m.ServiceID) > 0 { + i -= len(m.ServiceID) + copy(dAtA[i:], m.ServiceID) + i = encodeVarintLogbroker(dAtA, i, uint64(len(m.ServiceID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *LogAttr) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -951,29 +1356,36 @@ func (m *LogAttr) Marshal() (dAtA []byte, err error) { } func (m *LogAttr) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogAttr) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Key) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) - } if len(m.Value) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Value) + copy(dAtA[i:], m.Value) i = encodeVarintLogbroker(dAtA, i, uint64(len(m.Value))) - i += copy(dAtA[i:], m.Value) + i-- + dAtA[i] = 0x12 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintLogbroker(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *LogMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -981,58 +1393,70 @@ func (m *LogMessage) Marshal() (dAtA []byte, err error) { } func (m *LogMessage) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LogMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(m.Context.Size())) - n2, err := m.Context.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 - if m.Timestamp != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(m.Timestamp.Size())) - n3, err := m.Timestamp.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Attrs) > 0 { + for iNdEx := len(m.Attrs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Attrs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogbroker(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a } - i += n3 - } - if m.Stream != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(m.Stream)) } if len(m.Data) > 0 { - dAtA[i] = 0x22 - i++ + i -= len(m.Data) + copy(dAtA[i:], m.Data) i = encodeVarintLogbroker(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) + i-- + dAtA[i] = 0x22 } - if len(m.Attrs) > 0 { - for _, msg := range m.Attrs { - dAtA[i] = 0x2a - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Stream != 0 { + i = encodeVarintLogbroker(dAtA, i, uint64(m.Stream)) + i-- + dAtA[i] = 0x18 + } + if m.Timestamp != nil { + { + size, err := m.Timestamp.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintLogbroker(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Context.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarintLogbroker(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *SubscribeLogsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1040,37 +1464,46 @@ func (m *SubscribeLogsRequest) Marshal() (dAtA []byte, err error) { } func (m *SubscribeLogsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeLogsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Selector != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(m.Selector.Size())) - n4, err := m.Selector.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n4 - } if m.Options != nil { + { + size, err := m.Options.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogbroker(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(m.Options.Size())) - n5, err := m.Options.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + if m.Selector != nil { + { + size, err := m.Selector.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogbroker(dAtA, i, uint64(size)) } - i += n5 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *SubscribeLogsMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1078,29 +1511,36 @@ func (m *SubscribeLogsMessage) Marshal() (dAtA []byte, err error) { } func (m *SubscribeLogsMessage) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeLogsMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.Messages) > 0 { - for _, msg := range m.Messages { - dAtA[i] = 0xa - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Messages[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogbroker(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *ListenSubscriptionsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1108,17 +1548,22 @@ func (m *ListenSubscriptionsRequest) Marshal() (dAtA []byte, err error) { } func (m *ListenSubscriptionsRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListenSubscriptionsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + return len(dAtA) - i, nil } func (m *SubscriptionMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1126,53 +1571,63 @@ func (m *SubscriptionMessage) Marshal() (dAtA []byte, err error) { } func (m *SubscriptionMessage) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscriptionMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - } - if m.Selector != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(m.Selector.Size())) - n6, err := m.Selector.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n6 - } - if m.Options != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(m.Options.Size())) - n7, err := m.Options.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n7 - } if m.Close { - dAtA[i] = 0x20 - i++ + i-- if m.Close { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x20 + } + if m.Options != nil { + { + size, err := m.Options.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogbroker(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } - return i, nil + if m.Selector != nil { + { + size, err := m.Selector.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogbroker(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintLogbroker(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *PublishLogsMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1180,45 +1635,53 @@ func (m *PublishLogsMessage) Marshal() (dAtA []byte, err error) { } func (m *PublishLogsMessage) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PublishLogsMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.SubscriptionID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(len(m.SubscriptionID))) - i += copy(dAtA[i:], m.SubscriptionID) - } - if len(m.Messages) > 0 { - for _, msg := range m.Messages { - dAtA[i] = 0x12 - i++ - i = encodeVarintLogbroker(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } if m.Close { - dAtA[i] = 0x18 - i++ + i-- if m.Close { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x18 + } + if len(m.Messages) > 0 { + for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Messages[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogbroker(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } } - return i, nil + if len(m.SubscriptionID) > 0 { + i -= len(m.SubscriptionID) + copy(dAtA[i:], m.SubscriptionID) + i = encodeVarintLogbroker(dAtA, i, uint64(len(m.SubscriptionID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *PublishLogsResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1226,21 +1689,28 @@ func (m *PublishLogsResponse) Marshal() (dAtA []byte, err error) { } func (m *PublishLogsResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PublishLogsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + return len(dAtA) - i, nil } func encodeVarintLogbroker(dAtA []byte, offset int, v uint64) int { + offset -= sovLogbroker(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } type raftProxyLogsServer struct { @@ -1547,6 +2017,9 @@ func (p *raftProxyLogBrokerServer) PublishLogs(stream LogBroker_PublishLogsServe } func (m *LogSubscriptionOptions) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Streams) > 0 { @@ -1568,6 +2041,9 @@ func (m *LogSubscriptionOptions) Size() (n int) { } func (m *LogSelector) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.ServiceIDs) > 0 { @@ -1592,6 +2068,9 @@ func (m *LogSelector) Size() (n int) { } func (m *LogContext) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ServiceID) @@ -1610,6 +2089,9 @@ func (m *LogContext) Size() (n int) { } func (m *LogAttr) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Key) @@ -1624,6 +2106,9 @@ func (m *LogAttr) Size() (n int) { } func (m *LogMessage) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Context.Size() @@ -1649,6 +2134,9 @@ func (m *LogMessage) Size() (n int) { } func (m *SubscribeLogsRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Selector != nil { @@ -1663,6 +2151,9 @@ func (m *SubscribeLogsRequest) Size() (n int) { } func (m *SubscribeLogsMessage) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Messages) > 0 { @@ -1675,12 +2166,18 @@ func (m *SubscribeLogsMessage) Size() (n int) { } func (m *ListenSubscriptionsRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l return n } func (m *SubscriptionMessage) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1702,6 +2199,9 @@ func (m *SubscriptionMessage) Size() (n int) { } func (m *PublishLogsMessage) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.SubscriptionID) @@ -1721,20 +2221,16 @@ func (m *PublishLogsMessage) Size() (n int) { } func (m *PublishLogsResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l return n } func sovLogbroker(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozLogbroker(x uint64) (n int) { return sovLogbroker(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -1747,7 +2243,7 @@ func (this *LogSubscriptionOptions) String() string { `Streams:` + fmt.Sprintf("%v", this.Streams) + `,`, `Follow:` + fmt.Sprintf("%v", this.Follow) + `,`, `Tail:` + fmt.Sprintf("%v", this.Tail) + `,`, - `Since:` + strings.Replace(fmt.Sprintf("%v", this.Since), "Timestamp", "google_protobuf.Timestamp", 1) + `,`, + `Since:` + strings.Replace(fmt.Sprintf("%v", this.Since), "Timestamp", "types.Timestamp", 1) + `,`, `}`, }, "") return s @@ -1791,12 +2287,17 @@ func (this *LogMessage) String() string { if this == nil { return "nil" } + repeatedStringForAttrs := "[]LogAttr{" + for _, f := range this.Attrs { + repeatedStringForAttrs += strings.Replace(strings.Replace(f.String(), "LogAttr", "LogAttr", 1), `&`, ``, 1) + "," + } + repeatedStringForAttrs += "}" s := strings.Join([]string{`&LogMessage{`, `Context:` + strings.Replace(strings.Replace(this.Context.String(), "LogContext", "LogContext", 1), `&`, ``, 1) + `,`, - `Timestamp:` + strings.Replace(fmt.Sprintf("%v", this.Timestamp), "Timestamp", "google_protobuf.Timestamp", 1) + `,`, + `Timestamp:` + strings.Replace(fmt.Sprintf("%v", this.Timestamp), "Timestamp", "types.Timestamp", 1) + `,`, `Stream:` + fmt.Sprintf("%v", this.Stream) + `,`, `Data:` + fmt.Sprintf("%v", this.Data) + `,`, - `Attrs:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Attrs), "LogAttr", "LogAttr", 1), `&`, ``, 1) + `,`, + `Attrs:` + repeatedStringForAttrs + `,`, `}`, }, "") return s @@ -1806,8 +2307,8 @@ func (this *SubscribeLogsRequest) String() string { return "nil" } s := strings.Join([]string{`&SubscribeLogsRequest{`, - `Selector:` + strings.Replace(fmt.Sprintf("%v", this.Selector), "LogSelector", "LogSelector", 1) + `,`, - `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "LogSubscriptionOptions", "LogSubscriptionOptions", 1) + `,`, + `Selector:` + strings.Replace(this.Selector.String(), "LogSelector", "LogSelector", 1) + `,`, + `Options:` + strings.Replace(this.Options.String(), "LogSubscriptionOptions", "LogSubscriptionOptions", 1) + `,`, `}`, }, "") return s @@ -1816,8 +2317,13 @@ func (this *SubscribeLogsMessage) String() string { if this == nil { return "nil" } + repeatedStringForMessages := "[]LogMessage{" + for _, f := range this.Messages { + repeatedStringForMessages += strings.Replace(strings.Replace(f.String(), "LogMessage", "LogMessage", 1), `&`, ``, 1) + "," + } + repeatedStringForMessages += "}" s := strings.Join([]string{`&SubscribeLogsMessage{`, - `Messages:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Messages), "LogMessage", "LogMessage", 1), `&`, ``, 1) + `,`, + `Messages:` + repeatedStringForMessages + `,`, `}`, }, "") return s @@ -1837,8 +2343,8 @@ func (this *SubscriptionMessage) String() string { } s := strings.Join([]string{`&SubscriptionMessage{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, - `Selector:` + strings.Replace(fmt.Sprintf("%v", this.Selector), "LogSelector", "LogSelector", 1) + `,`, - `Options:` + strings.Replace(fmt.Sprintf("%v", this.Options), "LogSubscriptionOptions", "LogSubscriptionOptions", 1) + `,`, + `Selector:` + strings.Replace(this.Selector.String(), "LogSelector", "LogSelector", 1) + `,`, + `Options:` + strings.Replace(this.Options.String(), "LogSubscriptionOptions", "LogSubscriptionOptions", 1) + `,`, `Close:` + fmt.Sprintf("%v", this.Close) + `,`, `}`, }, "") @@ -1848,9 +2354,14 @@ func (this *PublishLogsMessage) String() string { if this == nil { return "nil" } + repeatedStringForMessages := "[]LogMessage{" + for _, f := range this.Messages { + repeatedStringForMessages += strings.Replace(strings.Replace(f.String(), "LogMessage", "LogMessage", 1), `&`, ``, 1) + "," + } + repeatedStringForMessages += "}" s := strings.Join([]string{`&PublishLogsMessage{`, `SubscriptionID:` + fmt.Sprintf("%v", this.SubscriptionID) + `,`, - `Messages:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Messages), "LogMessage", "LogMessage", 1), `&`, ``, 1) + `,`, + `Messages:` + repeatedStringForMessages + `,`, `Close:` + fmt.Sprintf("%v", this.Close) + `,`, `}`, }, "") @@ -1888,7 +2399,7 @@ func (m *LogSubscriptionOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1914,7 +2425,7 @@ func (m *LogSubscriptionOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (LogStream(b) & 0x7F) << shift + v |= LogStream(b&0x7F) << shift if b < 0x80 { break } @@ -1931,7 +2442,7 @@ func (m *LogSubscriptionOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - packedLen |= (int(b) & 0x7F) << shift + packedLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1940,9 +2451,16 @@ func (m *LogSubscriptionOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } + var elementCount int + if elementCount != 0 && len(m.Streams) == 0 { + m.Streams = make([]LogStream, 0, elementCount) + } for iNdEx < postIndex { var v LogStream for shift := uint(0); ; shift += 7 { @@ -1954,7 +2472,7 @@ func (m *LogSubscriptionOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (LogStream(b) & 0x7F) << shift + v |= LogStream(b&0x7F) << shift if b < 0x80 { break } @@ -1978,7 +2496,7 @@ func (m *LogSubscriptionOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1998,7 +2516,7 @@ func (m *LogSubscriptionOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Tail |= (int64(b) & 0x7F) << shift + m.Tail |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -2017,7 +2535,7 @@ func (m *LogSubscriptionOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2026,11 +2544,14 @@ func (m *LogSubscriptionOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Since == nil { - m.Since = &google_protobuf.Timestamp{} + m.Since = &types.Timestamp{} } if err := m.Since.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -2042,7 +2563,7 @@ func (m *LogSubscriptionOptions) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -2072,7 +2593,7 @@ func (m *LogSelector) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2100,7 +2621,7 @@ func (m *LogSelector) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2110,6 +2631,9 @@ func (m *LogSelector) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2129,7 +2653,7 @@ func (m *LogSelector) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2139,6 +2663,9 @@ func (m *LogSelector) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2158,7 +2685,7 @@ func (m *LogSelector) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2168,6 +2695,9 @@ func (m *LogSelector) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2179,7 +2709,7 @@ func (m *LogSelector) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -2209,7 +2739,7 @@ func (m *LogContext) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2237,7 +2767,7 @@ func (m *LogContext) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2247,6 +2777,9 @@ func (m *LogContext) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2266,7 +2799,7 @@ func (m *LogContext) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2276,6 +2809,9 @@ func (m *LogContext) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2295,7 +2831,7 @@ func (m *LogContext) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2305,6 +2841,9 @@ func (m *LogContext) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2316,7 +2855,7 @@ func (m *LogContext) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -2346,7 +2885,7 @@ func (m *LogAttr) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2374,7 +2913,7 @@ func (m *LogAttr) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2384,6 +2923,9 @@ func (m *LogAttr) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2403,7 +2945,7 @@ func (m *LogAttr) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2413,6 +2955,9 @@ func (m *LogAttr) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2424,7 +2969,7 @@ func (m *LogAttr) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -2454,7 +2999,7 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2482,7 +3027,7 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2491,6 +3036,9 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2512,7 +3060,7 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2521,11 +3069,14 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Timestamp == nil { - m.Timestamp = &google_protobuf.Timestamp{} + m.Timestamp = &types.Timestamp{} } if err := m.Timestamp.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -2545,7 +3096,7 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Stream |= (LogStream(b) & 0x7F) << shift + m.Stream |= LogStream(b&0x7F) << shift if b < 0x80 { break } @@ -2564,7 +3115,7 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2573,6 +3124,9 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2595,7 +3149,7 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2604,6 +3158,9 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2618,7 +3175,7 @@ func (m *LogMessage) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -2648,7 +3205,7 @@ func (m *SubscribeLogsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2676,7 +3233,7 @@ func (m *SubscribeLogsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2685,6 +3242,9 @@ func (m *SubscribeLogsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2709,7 +3269,7 @@ func (m *SubscribeLogsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2718,6 +3278,9 @@ func (m *SubscribeLogsRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2734,7 +3297,7 @@ func (m *SubscribeLogsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -2764,7 +3327,7 @@ func (m *SubscribeLogsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2792,7 +3355,7 @@ func (m *SubscribeLogsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2801,6 +3364,9 @@ func (m *SubscribeLogsMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2815,7 +3381,7 @@ func (m *SubscribeLogsMessage) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -2845,7 +3411,7 @@ func (m *ListenSubscriptionsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2865,7 +3431,7 @@ func (m *ListenSubscriptionsRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -2895,7 +3461,7 @@ func (m *SubscriptionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2923,7 +3489,7 @@ func (m *SubscriptionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2933,6 +3499,9 @@ func (m *SubscriptionMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2952,7 +3521,7 @@ func (m *SubscriptionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2961,6 +3530,9 @@ func (m *SubscriptionMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2985,7 +3557,7 @@ func (m *SubscriptionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2994,6 +3566,9 @@ func (m *SubscriptionMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3018,7 +3593,7 @@ func (m *SubscriptionMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3030,7 +3605,7 @@ func (m *SubscriptionMessage) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -3060,7 +3635,7 @@ func (m *PublishLogsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3088,7 +3663,7 @@ func (m *PublishLogsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3098,6 +3673,9 @@ func (m *PublishLogsMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3117,7 +3695,7 @@ func (m *PublishLogsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3126,6 +3704,9 @@ func (m *PublishLogsMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthLogbroker } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogbroker + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3148,7 +3729,7 @@ func (m *PublishLogsMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3160,7 +3741,7 @@ func (m *PublishLogsMessage) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -3190,7 +3771,7 @@ func (m *PublishLogsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3210,7 +3791,7 @@ func (m *PublishLogsResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthLogbroker } if (iNdEx + skippy) > l { @@ -3228,6 +3809,7 @@ func (m *PublishLogsResponse) Unmarshal(dAtA []byte) error { func skipLogbroker(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -3259,10 +3841,8 @@ func skipLogbroker(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -3279,122 +3859,34 @@ func skipLogbroker(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthLogbroker } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowLogbroker - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipLogbroker(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupLogbroker + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthLogbroker + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthLogbroker = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowLogbroker = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthLogbroker = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowLogbroker = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupLogbroker = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/docker/swarmkit/api/logbroker.proto", fileDescriptorLogbroker) -} - -var fileDescriptorLogbroker = []byte{ - // 966 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x95, 0x41, 0x6f, 0x1b, 0x45, - 0x14, 0xc7, 0x3d, 0xeb, 0xc4, 0x8e, 0x9f, 0x9b, 0xc4, 0x9d, 0xa4, 0x91, 0x65, 0xa8, 0x6d, 0x6d, - 0xa5, 0x62, 0x45, 0x65, 0xdd, 0x1a, 0xa1, 0x22, 0x45, 0x42, 0xd4, 0xb8, 0x42, 0x16, 0x6e, 0x82, - 0xc6, 0x8e, 0xe0, 0x16, 0xad, 0xbd, 0xd3, 0xed, 0xca, 0xeb, 0x1d, 0xb3, 0x33, 0x4e, 0x40, 0xe2, - 0xc0, 0xa1, 0x48, 0x28, 0x07, 0x6e, 0x48, 0x70, 0xe8, 0x89, 0x5e, 0x10, 0x12, 0x17, 0x6e, 0x7c, - 0x00, 0x14, 0x71, 0xe2, 0xc8, 0xc9, 0xa2, 0xfb, 0x01, 0xf8, 0x0c, 0x68, 0x67, 0xd6, 0xeb, 0x0d, - 0xb6, 0x53, 0x54, 0x2e, 0xf6, 0x8c, 0xe7, 0xf7, 0xf6, 0xfd, 0xdf, 0x7f, 0xde, 0x5b, 0x83, 0x61, - 0x3b, 0xe2, 0xc9, 0xa4, 0x6f, 0x0c, 0xd8, 0xa8, 0x6e, 0xb1, 0xc1, 0x90, 0xfa, 0x75, 0x7e, 0x66, - 0xfa, 0xa3, 0xa1, 0x23, 0xea, 0xe6, 0xd8, 0xa9, 0xbb, 0xcc, 0xee, 0xfb, 0x6c, 0x48, 0x7d, 0x63, - 0xec, 0x33, 0xc1, 0x30, 0x56, 0x90, 0x31, 0x83, 0x8c, 0xd3, 0x7b, 0xa5, 0x5d, 0x9b, 0xd9, 0x4c, - 0x1e, 0xd7, 0xc3, 0x95, 0x22, 0x4b, 0x15, 0x9b, 0x31, 0xdb, 0xa5, 0x75, 0xb9, 0xeb, 0x4f, 0x1e, - 0xd7, 0x85, 0x33, 0xa2, 0x5c, 0x98, 0xa3, 0x71, 0x04, 0xdc, 0xbf, 0x22, 0x75, 0x1c, 0x34, 0x76, - 0x27, 0xb6, 0xe3, 0x45, 0x5f, 0x2a, 0x50, 0xff, 0x05, 0xc1, 0x5e, 0x87, 0xd9, 0xdd, 0x49, 0x9f, - 0x0f, 0x7c, 0x67, 0x2c, 0x1c, 0xe6, 0x1d, 0xc9, 0x4f, 0x8e, 0x0f, 0x20, 0xcb, 0x85, 0x4f, 0xcd, - 0x11, 0x2f, 0xa2, 0x6a, 0xba, 0xb6, 0xd5, 0xb8, 0x69, 0x2c, 0x0a, 0x36, 0xc2, 0x60, 0x49, 0x35, - 0xb5, 0x42, 0x8a, 0xcc, 0x22, 0xf0, 0x1e, 0x64, 0x1e, 0x33, 0xd7, 0x65, 0x67, 0x45, 0xad, 0x8a, - 0x6a, 0x1b, 0x24, 0xda, 0x61, 0x0c, 0x6b, 0xc2, 0x74, 0xdc, 0x62, 0xba, 0x8a, 0x6a, 0x69, 0x22, - 0xd7, 0xf8, 0x2e, 0xac, 0x73, 0xc7, 0x1b, 0xd0, 0xe2, 0x5a, 0x15, 0xd5, 0xf2, 0x8d, 0x92, 0xa1, - 0xaa, 0x35, 0x66, 0xc2, 0x8d, 0xde, 0xac, 0x5a, 0xa2, 0x40, 0xfd, 0x1b, 0x04, 0xf9, 0x30, 0x31, - 0x75, 0xe9, 0x40, 0x30, 0x1f, 0xd7, 0x21, 0xcf, 0xa9, 0x7f, 0xea, 0x0c, 0xe8, 0x89, 0x63, 0x29, - 0xb9, 0xb9, 0xe6, 0x56, 0x30, 0xad, 0x40, 0x57, 0xfd, 0xdc, 0x6e, 0x71, 0x02, 0x11, 0xd2, 0xb6, - 0x38, 0xbe, 0x0d, 0x1b, 0x1e, 0xb3, 0x14, 0xad, 0x49, 0x3a, 0x1f, 0x4c, 0x2b, 0xd9, 0x43, 0x66, - 0x49, 0x34, 0x1b, 0x1e, 0x46, 0x9c, 0x30, 0xf9, 0x50, 0x72, 0xe9, 0x39, 0xd7, 0x33, 0xf9, 0x50, - 0x72, 0xe1, 0x61, 0xdb, 0xe2, 0xfa, 0x53, 0x04, 0xd0, 0x61, 0xf6, 0xfb, 0xcc, 0x13, 0xf4, 0x33, - 0x81, 0xef, 0x00, 0xcc, 0xf5, 0x14, 0x51, 0x15, 0xd5, 0x72, 0xcd, 0xcd, 0x60, 0x5a, 0xc9, 0xc5, - 0x72, 0x48, 0x2e, 0x56, 0x83, 0x6f, 0x41, 0x36, 0x12, 0x23, 0xcd, 0xca, 0x35, 0x21, 0x98, 0x56, - 0x32, 0x4a, 0x0b, 0xc9, 0x28, 0x29, 0x21, 0x14, 0x29, 0x91, 0xde, 0x45, 0x90, 0x12, 0x42, 0x32, - 0x4a, 0x87, 0x7e, 0x0f, 0xb2, 0x1d, 0x66, 0x3f, 0x10, 0xc2, 0xc7, 0x05, 0x48, 0x0f, 0xe9, 0xe7, - 0x2a, 0x37, 0x09, 0x97, 0x78, 0x17, 0xd6, 0x4f, 0x4d, 0x77, 0x42, 0x55, 0x12, 0xa2, 0x36, 0xfa, - 0xb9, 0x26, 0x95, 0x3f, 0xa2, 0x9c, 0x9b, 0x36, 0xc5, 0xef, 0x42, 0x76, 0xa0, 0x8a, 0x90, 0xa1, - 0xf9, 0x46, 0x79, 0xc5, 0xa5, 0x47, 0xa5, 0x36, 0xd7, 0x2e, 0xa6, 0x95, 0x14, 0x99, 0x05, 0xe1, - 0x77, 0x20, 0x17, 0xf7, 0xa6, 0x4c, 0x74, 0xf5, 0x7d, 0xce, 0x61, 0xfc, 0x36, 0x64, 0x54, 0xf3, - 0xc8, 0xfa, 0x5e, 0xd6, 0x6d, 0x24, 0x82, 0xc3, 0x86, 0xb2, 0x4c, 0x61, 0xca, 0xde, 0xb9, 0x46, - 0xe4, 0x1a, 0xdf, 0x87, 0x75, 0x53, 0x08, 0x9f, 0x17, 0xd7, 0xab, 0xe9, 0x5a, 0xbe, 0xf1, 0xda, - 0x8a, 0x27, 0x85, 0x3e, 0x45, 0xfa, 0x15, 0xaf, 0x7f, 0x8f, 0x60, 0x37, 0x1a, 0x85, 0x3e, 0xed, - 0x30, 0x9b, 0x13, 0xfa, 0xe9, 0x84, 0x72, 0x81, 0x0f, 0x60, 0x83, 0x47, 0xcd, 0x16, 0xf9, 0x52, - 0x59, 0x25, 0x2f, 0xc2, 0x48, 0x1c, 0x80, 0x5b, 0x90, 0x65, 0x6a, 0xa6, 0x22, 0x47, 0xf6, 0x57, - 0xc5, 0x2e, 0x4e, 0x21, 0x99, 0x85, 0xea, 0x9f, 0xfc, 0x4b, 0xda, 0xec, 0xc6, 0xde, 0x83, 0x8d, - 0x91, 0x5a, 0xaa, 0xc6, 0x5f, 0x7d, 0x65, 0x51, 0x44, 0x54, 0x72, 0x1c, 0xa5, 0xbf, 0x0e, 0xa5, - 0x8e, 0xc3, 0x05, 0xf5, 0x92, 0xf9, 0x67, 0xa5, 0xeb, 0xbf, 0x21, 0xd8, 0x49, 0x1e, 0xcc, 0xf2, - 0xee, 0x81, 0x16, 0xf7, 0x76, 0x26, 0x98, 0x56, 0xb4, 0x76, 0x8b, 0x68, 0x8e, 0x75, 0xc9, 0x2a, - 0xed, 0x7f, 0x58, 0x95, 0x7e, 0x65, 0xab, 0xc2, 0x4e, 0x1f, 0xb8, 0x8c, 0xab, 0x17, 0xca, 0x06, - 0x51, 0x1b, 0xfd, 0x47, 0x04, 0xf8, 0xa3, 0x49, 0xdf, 0x75, 0xf8, 0x93, 0xa4, 0x7f, 0x07, 0xb0, - 0xcd, 0x13, 0x0f, 0x9b, 0x0f, 0x2c, 0x0e, 0xa6, 0x95, 0xad, 0x64, 0x9e, 0x76, 0x8b, 0x6c, 0x25, - 0xd1, 0xb6, 0x75, 0xc9, 0x7c, 0xed, 0x55, 0xcc, 0x9f, 0x6b, 0x4d, 0x27, 0xb5, 0xde, 0x80, 0x9d, - 0x84, 0x54, 0x42, 0xf9, 0x98, 0x79, 0x9c, 0xee, 0x3f, 0x47, 0x90, 0x8b, 0x47, 0x00, 0xdf, 0x01, - 0xdc, 0x39, 0xfa, 0xe0, 0xa4, 0xdb, 0x23, 0x0f, 0x1f, 0x3c, 0x3a, 0x39, 0x3e, 0xfc, 0xf0, 0xf0, - 0xe8, 0xe3, 0xc3, 0x42, 0xaa, 0xb4, 0x7b, 0xfe, 0xac, 0x5a, 0x88, 0xb1, 0x63, 0x6f, 0xe8, 0xb1, - 0x33, 0x0f, 0xef, 0xc3, 0xf5, 0x04, 0xdd, 0xed, 0xb5, 0x8e, 0x8e, 0x7b, 0x05, 0x54, 0xda, 0x39, - 0x7f, 0x56, 0xdd, 0x8e, 0xe1, 0xae, 0xb0, 0xd8, 0x44, 0x2c, 0xb2, 0x0f, 0x09, 0x29, 0x68, 0x8b, - 0x2c, 0xf5, 0xfd, 0xd2, 0xf5, 0xaf, 0x7f, 0x28, 0xa7, 0x7e, 0x7d, 0x5e, 0x9e, 0x0b, 0x6b, 0x3c, - 0x45, 0xb0, 0x16, 0xea, 0xc6, 0x5f, 0xc0, 0xe6, 0xa5, 0x9e, 0xc5, 0xb5, 0x65, 0xee, 0x2c, 0x9b, - 0xb8, 0xd2, 0xcb, 0xc9, 0xc8, 0x51, 0xfd, 0xc6, 0xef, 0x3f, 0xff, 0xfd, 0x9d, 0xb6, 0x0d, 0x9b, - 0x92, 0x7c, 0x73, 0x64, 0x7a, 0xa6, 0x4d, 0xfd, 0xbb, 0xa8, 0xf1, 0x93, 0x26, 0xdd, 0x6a, 0xca, - 0xff, 0x5c, 0xfc, 0x2d, 0x82, 0x9d, 0x25, 0x6d, 0x8e, 0x8d, 0xa5, 0x17, 0xb6, 0x72, 0x1e, 0x4a, - 0x6f, 0x5c, 0x21, 0x2c, 0x39, 0x20, 0xfa, 0x2d, 0xa9, 0xeb, 0x26, 0x5c, 0x53, 0xba, 0xce, 0x98, - 0x3f, 0xa4, 0xfe, 0x82, 0x4a, 0xfc, 0x15, 0x82, 0x7c, 0xe2, 0xae, 0xf1, 0xed, 0x65, 0xcf, 0x5f, - 0xec, 0xdb, 0xe5, 0x3a, 0x96, 0x34, 0xcd, 0x7f, 0xd2, 0x51, 0x43, 0xcd, 0xe2, 0xc5, 0x8b, 0x72, - 0xea, 0xcf, 0x17, 0xe5, 0xd4, 0x97, 0x41, 0x19, 0x5d, 0x04, 0x65, 0xf4, 0x47, 0x50, 0x46, 0x7f, - 0x05, 0x65, 0xd4, 0xcf, 0xc8, 0x17, 0xf7, 0x5b, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x95, 0x7b, - 0x3c, 0x04, 0xe0, 0x08, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/objects.pb.go b/vendor/github.com/docker/swarmkit/api/objects.pb.go index 4200eda49b519..743e6f9003fc6 100644 --- a/vendor/github.com/docker/swarmkit/api/objects.pb.go +++ b/vendor/github.com/docker/swarmkit/api/objects.pb.go @@ -3,64 +3,96 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/gogo/protobuf/types" -import _ "github.com/gogo/protobuf/gogoproto" -import google_protobuf3 "github.com/gogo/protobuf/types" -import _ "github.com/docker/swarmkit/protobuf/plugin" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import go_events "github.com/docker/go-events" -import strings "strings" - -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + fmt "fmt" + github_com_docker_go_events "github.com/docker/go-events" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + _ "github.com/docker/swarmkit/protobuf/plugin" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + types "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + // Meta contains metadata about objects. Every object contains a meta field. type Meta struct { // Version tracks the current version of the object. - Version Version `protobuf:"bytes,1,opt,name=version" json:"version"` + Version Version `protobuf:"bytes,1,opt,name=version,proto3" json:"version"` // Object timestamps. // Note: can't use stdtime because these fields are nullable. - CreatedAt *google_protobuf.Timestamp `protobuf:"bytes,2,opt,name=created_at,json=createdAt" json:"created_at,omitempty"` - UpdatedAt *google_protobuf.Timestamp `protobuf:"bytes,3,opt,name=updated_at,json=updatedAt" json:"updated_at,omitempty"` + CreatedAt *types.Timestamp `protobuf:"bytes,2,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *types.Timestamp `protobuf:"bytes,3,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` +} + +func (m *Meta) Reset() { *m = Meta{} } +func (*Meta) ProtoMessage() {} +func (*Meta) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{0} +} +func (m *Meta) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Meta) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Meta.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Meta) XXX_Merge(src proto.Message) { + xxx_messageInfo_Meta.Merge(m, src) +} +func (m *Meta) XXX_Size() int { + return m.Size() +} +func (m *Meta) XXX_DiscardUnknown() { + xxx_messageInfo_Meta.DiscardUnknown(m) } -func (m *Meta) Reset() { *m = Meta{} } -func (*Meta) ProtoMessage() {} -func (*Meta) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{0} } +var xxx_messageInfo_Meta proto.InternalMessageInfo // Node provides the internal node state as seen by the cluster. type Node struct { // ID specifies the identity of the node. ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Meta Meta `protobuf:"bytes,2,opt,name=meta" json:"meta"` + Meta Meta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta"` // Spec defines the desired state of the node as specified by the user. // The system will honor this and will *never* modify it. - Spec NodeSpec `protobuf:"bytes,3,opt,name=spec" json:"spec"` + Spec NodeSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec"` // Description encapsulated the properties of the Node as reported by the // agent. - Description *NodeDescription `protobuf:"bytes,4,opt,name=description" json:"description,omitempty"` + Description *NodeDescription `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` // Status provides the current status of the node, as seen by the manager. - Status NodeStatus `protobuf:"bytes,5,opt,name=status" json:"status"` + Status NodeStatus `protobuf:"bytes,5,opt,name=status,proto3" json:"status"` // ManagerStatus provides the current status of the node's manager // component, if the node is a manager. - ManagerStatus *ManagerStatus `protobuf:"bytes,6,opt,name=manager_status,json=managerStatus" json:"manager_status,omitempty"` + ManagerStatus *ManagerStatus `protobuf:"bytes,6,opt,name=manager_status,json=managerStatus,proto3" json:"manager_status,omitempty"` // DEPRECATED: Use Attachments to find the ingress network // The node attachment to the ingress network. - Attachment *NetworkAttachment `protobuf:"bytes,7,opt,name=attachment" json:"attachment,omitempty"` + Attachment *NetworkAttachment `protobuf:"bytes,7,opt,name=attachment,proto3" json:"attachment,omitempty"` // Deprecated: Do not use. // Certificate is the TLS certificate issued for the node, if any. - Certificate Certificate `protobuf:"bytes,8,opt,name=certificate" json:"certificate"` + Certificate Certificate `protobuf:"bytes,8,opt,name=certificate,proto3" json:"certificate"` // Role is the *observed* role for this node. It differs from the // desired role set in Node.Spec.Role because the role here is only // updated after the Raft member list has been reconciled with the @@ -74,56 +106,155 @@ type Node struct { // Attachments enumerates the network attachments for the node to set up an // endpoint on the node to be used for load balancing. Each overlay // network, including ingress network, will have an NetworkAttachment. - Attachments []*NetworkAttachment `protobuf:"bytes,10,rep,name=attachments" json:"attachments,omitempty"` + Attachments []*NetworkAttachment `protobuf:"bytes,10,rep,name=attachments,proto3" json:"attachments,omitempty"` + // VXLANUDPPort specifies the UDP port for VXLAN traffic. + // This information is passed from cluster object to individual nodes. + VXLANUDPPort uint32 `protobuf:"varint,11,opt,name=VXLANUDPPort,proto3" json:"VXLANUDPPort,omitempty"` +} + +func (m *Node) Reset() { *m = Node{} } +func (*Node) ProtoMessage() {} +func (*Node) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{1} +} +func (m *Node) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Node) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Node.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Node) XXX_Merge(src proto.Message) { + xxx_messageInfo_Node.Merge(m, src) +} +func (m *Node) XXX_Size() int { + return m.Size() +} +func (m *Node) XXX_DiscardUnknown() { + xxx_messageInfo_Node.DiscardUnknown(m) } -func (m *Node) Reset() { *m = Node{} } -func (*Node) ProtoMessage() {} -func (*Node) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{1} } +var xxx_messageInfo_Node proto.InternalMessageInfo type Service struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Meta Meta `protobuf:"bytes,2,opt,name=meta" json:"meta"` - Spec ServiceSpec `protobuf:"bytes,3,opt,name=spec" json:"spec"` + Meta Meta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta"` + Spec ServiceSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec"` // SpecVersion versions Spec, to identify changes in the spec. Note that // this is not directly comparable to the service's Version. - SpecVersion *Version `protobuf:"bytes,10,opt,name=spec_version,json=specVersion" json:"spec_version,omitempty"` + SpecVersion *Version `protobuf:"bytes,10,opt,name=spec_version,json=specVersion,proto3" json:"spec_version,omitempty"` // PreviousSpec is the previous service spec that was in place before // "Spec". - PreviousSpec *ServiceSpec `protobuf:"bytes,6,opt,name=previous_spec,json=previousSpec" json:"previous_spec,omitempty"` + PreviousSpec *ServiceSpec `protobuf:"bytes,6,opt,name=previous_spec,json=previousSpec,proto3" json:"previous_spec,omitempty"` // PreviousSpecVersion versions PreviousSpec. Note that this is not // directly comparable to the service's Version. - PreviousSpecVersion *Version `protobuf:"bytes,11,opt,name=previous_spec_version,json=previousSpecVersion" json:"previous_spec_version,omitempty"` + PreviousSpecVersion *Version `protobuf:"bytes,11,opt,name=previous_spec_version,json=previousSpecVersion,proto3" json:"previous_spec_version,omitempty"` // Runtime state of service endpoint. This may be different // from the spec version because the user may not have entered // the optional fields like node_port or virtual_ip and it // could be auto allocated by the system. - Endpoint *Endpoint `protobuf:"bytes,4,opt,name=endpoint" json:"endpoint,omitempty"` + Endpoint *Endpoint `protobuf:"bytes,4,opt,name=endpoint,proto3" json:"endpoint,omitempty"` // UpdateStatus contains the status of an update, if one is in // progress. - UpdateStatus *UpdateStatus `protobuf:"bytes,5,opt,name=update_status,json=updateStatus" json:"update_status,omitempty"` + UpdateStatus *UpdateStatus `protobuf:"bytes,5,opt,name=update_status,json=updateStatus,proto3" json:"update_status,omitempty"` + // JobStatus contains the status of a Service that is in one of the Job + // modes. It is absent on Replicated and Global services. + JobStatus *JobStatus `protobuf:"bytes,12,opt,name=job_status,json=jobStatus,proto3" json:"job_status,omitempty"` + // PendingDelete indicates that this service's deletion has been requested. + // Services, as well as all service-level resources, can only be deleted + // after all of the service's containers have properly shut down. + // When a user requests a deletion, we just flip this flag + // the deallocator will take it from there - it will start monitoring + // this service's tasks, and proceed to delete the service itself (and + // potentially its associated resources also marked for deletion) when + // all of its tasks are gone + PendingDelete bool `protobuf:"varint,7,opt,name=pending_delete,json=pendingDelete,proto3" json:"pending_delete,omitempty"` +} + +func (m *Service) Reset() { *m = Service{} } +func (*Service) ProtoMessage() {} +func (*Service) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{2} +} +func (m *Service) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Service) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Service.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Service) XXX_Merge(src proto.Message) { + xxx_messageInfo_Service.Merge(m, src) +} +func (m *Service) XXX_Size() int { + return m.Size() +} +func (m *Service) XXX_DiscardUnknown() { + xxx_messageInfo_Service.DiscardUnknown(m) } -func (m *Service) Reset() { *m = Service{} } -func (*Service) ProtoMessage() {} -func (*Service) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{2} } +var xxx_messageInfo_Service proto.InternalMessageInfo // Endpoint specified all the network parameters required to // correctly discover and load balance a service type Endpoint struct { - Spec *EndpointSpec `protobuf:"bytes,1,opt,name=spec" json:"spec,omitempty"` + Spec *EndpointSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` // Runtime state of the exposed ports which may carry // auto-allocated swarm ports in addition to the user // configured information. - Ports []*PortConfig `protobuf:"bytes,2,rep,name=ports" json:"ports,omitempty"` + Ports []*PortConfig `protobuf:"bytes,2,rep,name=ports,proto3" json:"ports,omitempty"` // VirtualIPs specifies the IP addresses under which this endpoint will be // made available. - VirtualIPs []*Endpoint_VirtualIP `protobuf:"bytes,3,rep,name=virtual_ips,json=virtualIps" json:"virtual_ips,omitempty"` + VirtualIPs []*Endpoint_VirtualIP `protobuf:"bytes,3,rep,name=virtual_ips,json=virtualIps,proto3" json:"virtual_ips,omitempty"` +} + +func (m *Endpoint) Reset() { *m = Endpoint{} } +func (*Endpoint) ProtoMessage() {} +func (*Endpoint) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{3} +} +func (m *Endpoint) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Endpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Endpoint.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Endpoint) XXX_Merge(src proto.Message) { + xxx_messageInfo_Endpoint.Merge(m, src) +} +func (m *Endpoint) XXX_Size() int { + return m.Size() +} +func (m *Endpoint) XXX_DiscardUnknown() { + xxx_messageInfo_Endpoint.DiscardUnknown(m) } -func (m *Endpoint) Reset() { *m = Endpoint{} } -func (*Endpoint) ProtoMessage() {} -func (*Endpoint) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{3} } +var xxx_messageInfo_Endpoint proto.InternalMessageInfo // VirtualIP specifies a set of networks this endpoint will be attached to // and the IP addresses the target service will be made available under. @@ -141,23 +272,51 @@ type Endpoint_VirtualIP struct { Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` } -func (m *Endpoint_VirtualIP) Reset() { *m = Endpoint_VirtualIP{} } -func (*Endpoint_VirtualIP) ProtoMessage() {} -func (*Endpoint_VirtualIP) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{3, 0} } +func (m *Endpoint_VirtualIP) Reset() { *m = Endpoint_VirtualIP{} } +func (*Endpoint_VirtualIP) ProtoMessage() {} +func (*Endpoint_VirtualIP) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{3, 0} +} +func (m *Endpoint_VirtualIP) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Endpoint_VirtualIP) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Endpoint_VirtualIP.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Endpoint_VirtualIP) XXX_Merge(src proto.Message) { + xxx_messageInfo_Endpoint_VirtualIP.Merge(m, src) +} +func (m *Endpoint_VirtualIP) XXX_Size() int { + return m.Size() +} +func (m *Endpoint_VirtualIP) XXX_DiscardUnknown() { + xxx_messageInfo_Endpoint_VirtualIP.DiscardUnknown(m) +} + +var xxx_messageInfo_Endpoint_VirtualIP proto.InternalMessageInfo // Task specifies the parameters for implementing a Spec. A task is effectively // immutable and idempotent. Once it is dispatched to a node, it will not be // dispatched to another node. type Task struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Meta Meta `protobuf:"bytes,2,opt,name=meta" json:"meta"` + Meta Meta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta"` // Spec defines the desired state of the task as specified by the user. // The system will honor this and will *never* modify it. - Spec TaskSpec `protobuf:"bytes,3,opt,name=spec" json:"spec"` + Spec TaskSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec"` // SpecVersion is copied from Service, to identify which version of the // spec this task has. Note that this is not directly comparable to the // service's Version. - SpecVersion *Version `protobuf:"bytes,14,opt,name=spec_version,json=specVersion" json:"spec_version,omitempty"` + SpecVersion *Version `protobuf:"bytes,14,opt,name=spec_version,json=specVersion,proto3" json:"spec_version,omitempty"` // ServiceID indicates the service under which this task is orchestrated. This // should almost always be set. ServiceID string `protobuf:"bytes,4,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` @@ -176,24 +335,24 @@ type Task struct { // // NOTE(stevvooe): The preserves the ability for us to making naming // decisions for tasks in orchestrator, albeit, this is left empty for now. - Annotations Annotations `protobuf:"bytes,7,opt,name=annotations" json:"annotations"` + Annotations Annotations `protobuf:"bytes,7,opt,name=annotations,proto3" json:"annotations"` // ServiceAnnotations is a direct copy of the service name and labels when // this task is created. // // Labels set here will *not* be propagated to the runtime target, such as a // container. Use labels on the runtime target for that purpose. - ServiceAnnotations Annotations `protobuf:"bytes,8,opt,name=service_annotations,json=serviceAnnotations" json:"service_annotations"` - Status TaskStatus `protobuf:"bytes,9,opt,name=status" json:"status"` + ServiceAnnotations Annotations `protobuf:"bytes,8,opt,name=service_annotations,json=serviceAnnotations,proto3" json:"service_annotations"` + Status TaskStatus `protobuf:"bytes,9,opt,name=status,proto3" json:"status"` // DesiredState is the target state for the task. It is set to // TaskStateRunning when a task is first created, and changed to // TaskStateShutdown if the manager wants to terminate the task. This field // is only written by the manager. DesiredState TaskState `protobuf:"varint,10,opt,name=desired_state,json=desiredState,proto3,enum=docker.swarmkit.v1.TaskState" json:"desired_state,omitempty"` // List of network attachments by the task. - Networks []*NetworkAttachment `protobuf:"bytes,11,rep,name=networks" json:"networks,omitempty"` + Networks []*NetworkAttachment `protobuf:"bytes,11,rep,name=networks,proto3" json:"networks,omitempty"` // A copy of runtime state of service endpoint from Service // object to be distributed to agents as part of the task. - Endpoint *Endpoint `protobuf:"bytes,12,opt,name=endpoint" json:"endpoint,omitempty"` + Endpoint *Endpoint `protobuf:"bytes,12,opt,name=endpoint,proto3" json:"endpoint,omitempty"` // LogDriver specifies the selected log driver to use for the task. Agent // processes should always favor the value in this field. // @@ -202,13 +361,44 @@ type Task struct { // such a cluster default or policy-based value. // // If not present, the daemon's default will be used. - LogDriver *Driver `protobuf:"bytes,13,opt,name=log_driver,json=logDriver" json:"log_driver,omitempty"` - AssignedGenericResources []*GenericResource `protobuf:"bytes,15,rep,name=assigned_generic_resources,json=assignedGenericResources" json:"assigned_generic_resources,omitempty"` + LogDriver *Driver `protobuf:"bytes,13,opt,name=log_driver,json=logDriver,proto3" json:"log_driver,omitempty"` + AssignedGenericResources []*GenericResource `protobuf:"bytes,15,rep,name=assigned_generic_resources,json=assignedGenericResources,proto3" json:"assigned_generic_resources,omitempty"` + // JobIteration is the iteration number of the Job-mode Service that this + // task belongs to. + JobIteration *Version `protobuf:"bytes,16,opt,name=job_iteration,json=jobIteration,proto3" json:"job_iteration,omitempty"` +} + +func (m *Task) Reset() { *m = Task{} } +func (*Task) ProtoMessage() {} +func (*Task) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{4} +} +func (m *Task) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Task) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Task.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Task) XXX_Merge(src proto.Message) { + xxx_messageInfo_Task.Merge(m, src) +} +func (m *Task) XXX_Size() int { + return m.Size() +} +func (m *Task) XXX_DiscardUnknown() { + xxx_messageInfo_Task.DiscardUnknown(m) } -func (m *Task) Reset() { *m = Task{} } -func (*Task) ProtoMessage() {} -func (*Task) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{4} } +var xxx_messageInfo_Task proto.InternalMessageInfo // NetworkAttachment specifies the network parameters of attachment to // a single network by an object such as task or node. @@ -216,45 +406,114 @@ type NetworkAttachment struct { // Network state as a whole becomes part of the object so that // it always is available for use in agents so that agents // don't have any other dependency during execution. - Network *Network `protobuf:"bytes,1,opt,name=network" json:"network,omitempty"` + Network *Network `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` // List of IPv4/IPv6 addresses that are assigned to the object // as part of getting attached to this network. - Addresses []string `protobuf:"bytes,2,rep,name=addresses" json:"addresses,omitempty"` + Addresses []string `protobuf:"bytes,2,rep,name=addresses,proto3" json:"addresses,omitempty"` // List of aliases by which a task is resolved in a network - Aliases []string `protobuf:"bytes,3,rep,name=aliases" json:"aliases,omitempty"` + Aliases []string `protobuf:"bytes,3,rep,name=aliases,proto3" json:"aliases,omitempty"` // Map of all the driver attachment options for this network - DriverAttachmentOpts map[string]string `protobuf:"bytes,4,rep,name=driver_attachment_opts,json=driverAttachmentOpts" json:"driver_attachment_opts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + DriverAttachmentOpts map[string]string `protobuf:"bytes,4,rep,name=driver_attachment_opts,json=driverAttachmentOpts,proto3" json:"driver_attachment_opts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *NetworkAttachment) Reset() { *m = NetworkAttachment{} } +func (*NetworkAttachment) ProtoMessage() {} +func (*NetworkAttachment) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{5} +} +func (m *NetworkAttachment) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NetworkAttachment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NetworkAttachment.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NetworkAttachment) XXX_Merge(src proto.Message) { + xxx_messageInfo_NetworkAttachment.Merge(m, src) +} +func (m *NetworkAttachment) XXX_Size() int { + return m.Size() +} +func (m *NetworkAttachment) XXX_DiscardUnknown() { + xxx_messageInfo_NetworkAttachment.DiscardUnknown(m) } -func (m *NetworkAttachment) Reset() { *m = NetworkAttachment{} } -func (*NetworkAttachment) ProtoMessage() {} -func (*NetworkAttachment) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{5} } +var xxx_messageInfo_NetworkAttachment proto.InternalMessageInfo type Network struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Meta Meta `protobuf:"bytes,2,opt,name=meta" json:"meta"` - Spec NetworkSpec `protobuf:"bytes,3,opt,name=spec" json:"spec"` + Meta Meta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta"` + Spec NetworkSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec"` // Driver specific operational state provided by the network driver. - DriverState *Driver `protobuf:"bytes,4,opt,name=driver_state,json=driverState" json:"driver_state,omitempty"` + DriverState *Driver `protobuf:"bytes,4,opt,name=driver_state,json=driverState,proto3" json:"driver_state,omitempty"` // Runtime state of IPAM options. This may not reflect the // ipam options from NetworkSpec. - IPAM *IPAMOptions `protobuf:"bytes,5,opt,name=ipam" json:"ipam,omitempty"` + IPAM *IPAMOptions `protobuf:"bytes,5,opt,name=ipam,proto3" json:"ipam,omitempty"` + // PendingDelete indicates that this network's deletion has been requested. + // Services, as well as all service-level resources, can only be deleted + // after all the service's containers have properly shut down + // when a user requests a deletion, we just flip this flag + // the deallocator will take it from there + // PendingDelete indicates that this network's deletion has been requested. + // Services, as well as all service-level resources, can only be deleted + // after all of the service's containers have properly shut down. + // When a user requests a deletion of this network, we just flip this flag + // the deallocator will take it from there - it will start monitoring + // the services that still use this service, and proceed to delete + // this network when all of these services are gone + PendingDelete bool `protobuf:"varint,6,opt,name=pending_delete,json=pendingDelete,proto3" json:"pending_delete,omitempty"` +} + +func (m *Network) Reset() { *m = Network{} } +func (*Network) ProtoMessage() {} +func (*Network) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{6} +} +func (m *Network) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Network) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Network.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Network) XXX_Merge(src proto.Message) { + xxx_messageInfo_Network.Merge(m, src) +} +func (m *Network) XXX_Size() int { + return m.Size() +} +func (m *Network) XXX_DiscardUnknown() { + xxx_messageInfo_Network.DiscardUnknown(m) } -func (m *Network) Reset() { *m = Network{} } -func (*Network) ProtoMessage() {} -func (*Network) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{6} } +var xxx_messageInfo_Network proto.InternalMessageInfo // Cluster provides global cluster settings. type Cluster struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Meta Meta `protobuf:"bytes,2,opt,name=meta" json:"meta"` - Spec ClusterSpec `protobuf:"bytes,3,opt,name=spec" json:"spec"` + Meta Meta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta"` + Spec ClusterSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec"` // RootCA contains key material for the root CA. - RootCA RootCA `protobuf:"bytes,4,opt,name=root_ca,json=rootCa" json:"root_ca"` + RootCA RootCA `protobuf:"bytes,4,opt,name=root_ca,json=rootCa,proto3" json:"root_ca"` // Symmetric encryption key distributed by the lead manager. Used by agents // for securing network bootstrapping and communication. - NetworkBootstrapKeys []*EncryptionKey `protobuf:"bytes,5,rep,name=network_bootstrap_keys,json=networkBootstrapKeys" json:"network_bootstrap_keys,omitempty"` + NetworkBootstrapKeys []*EncryptionKey `protobuf:"bytes,5,rep,name=network_bootstrap_keys,json=networkBootstrapKeys,proto3" json:"network_bootstrap_keys,omitempty"` // Logical clock used to timestamp every key. It allows other managers // and agents to unambiguously identify the older key to be deleted when // a new key is allocated on key rotation. @@ -262,13 +521,13 @@ type Cluster struct { // BlacklistedCertificates tracks certificates that should no longer // be honored. It's a mapping from CN -> BlacklistedCertificate. // swarm. Their certificates should effectively be blacklisted. - BlacklistedCertificates map[string]*BlacklistedCertificate `protobuf:"bytes,8,rep,name=blacklisted_certificates,json=blacklistedCertificates" json:"blacklisted_certificates,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` + BlacklistedCertificates map[string]*BlacklistedCertificate `protobuf:"bytes,8,rep,name=blacklisted_certificates,json=blacklistedCertificates,proto3" json:"blacklisted_certificates,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // UnlockKeys defines the keys that lock node data at rest. For example, // this would contain the key encrypting key (KEK) that will encrypt the // manager TLS keys at rest and the raft encryption keys at rest. // If the key is empty, the node will be unlocked (will not require a key // to start up from a shut down state). - UnlockKeys []*EncryptionKey `protobuf:"bytes,9,rep,name=unlock_keys,json=unlockKeys" json:"unlock_keys,omitempty"` + UnlockKeys []*EncryptionKey `protobuf:"bytes,9,rep,name=unlock_keys,json=unlockKeys,proto3" json:"unlock_keys,omitempty"` // FIPS specifies whether this cluster should be in FIPS mode. This changes // the format of the join tokens, and nodes that are not FIPS-enabled should // reject joining the cluster. Nodes that report themselves to be non-FIPS @@ -277,78 +536,220 @@ type Cluster struct { // This field specifies default subnet pools for global scope networks. If // unspecified, Docker will use the predefined subnets as it works on older releases. // Format Example : {"20.20.0.0/16",""20.20.0.0/16"} - DefaultAddressPool []string `protobuf:"bytes,11,rep,name=defaultAddressPool" json:"defaultAddressPool,omitempty"` + DefaultAddressPool []string `protobuf:"bytes,11,rep,name=defaultAddressPool,proto3" json:"defaultAddressPool,omitempty"` // This flag specifies the default subnet size of global scope networks by giving // the length of the subnet masks for every such network SubnetSize uint32 `protobuf:"varint,12,opt,name=subnetSize,proto3" json:"subnetSize,omitempty"` + // VXLANUDPPort specifies the UDP port for VXLAN traffic. + VXLANUDPPort uint32 `protobuf:"varint,13,opt,name=VXLANUDPPort,proto3" json:"VXLANUDPPort,omitempty"` +} + +func (m *Cluster) Reset() { *m = Cluster{} } +func (*Cluster) ProtoMessage() {} +func (*Cluster) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{7} +} +func (m *Cluster) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Cluster) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Cluster.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Cluster) XXX_Merge(src proto.Message) { + xxx_messageInfo_Cluster.Merge(m, src) +} +func (m *Cluster) XXX_Size() int { + return m.Size() +} +func (m *Cluster) XXX_DiscardUnknown() { + xxx_messageInfo_Cluster.DiscardUnknown(m) } -func (m *Cluster) Reset() { *m = Cluster{} } -func (*Cluster) ProtoMessage() {} -func (*Cluster) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{7} } +var xxx_messageInfo_Cluster proto.InternalMessageInfo // Secret represents a secret that should be passed to a container or a node, // and is immutable. type Secret struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Meta Meta `protobuf:"bytes,2,opt,name=meta" json:"meta"` + Meta Meta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta"` // Spec contains the actual secret data, as well as any context around the // secret data that the user provides. - Spec SecretSpec `protobuf:"bytes,3,opt,name=spec" json:"spec"` + Spec SecretSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec"` // Whether the secret is an internal secret (not set by a user) or not. Internal bool `protobuf:"varint,4,opt,name=internal,proto3" json:"internal,omitempty"` } -func (m *Secret) Reset() { *m = Secret{} } -func (*Secret) ProtoMessage() {} -func (*Secret) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{8} } +func (m *Secret) Reset() { *m = Secret{} } +func (*Secret) ProtoMessage() {} +func (*Secret) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{8} +} +func (m *Secret) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Secret) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Secret.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Secret) XXX_Merge(src proto.Message) { + xxx_messageInfo_Secret.Merge(m, src) +} +func (m *Secret) XXX_Size() int { + return m.Size() +} +func (m *Secret) XXX_DiscardUnknown() { + xxx_messageInfo_Secret.DiscardUnknown(m) +} + +var xxx_messageInfo_Secret proto.InternalMessageInfo // Config represents a set of configuration files that should be passed to a // container. type Config struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Meta Meta `protobuf:"bytes,2,opt,name=meta" json:"meta"` + Meta Meta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta"` // Spec contains the actual config data, as well as any context around the // config data that the user provides. - Spec ConfigSpec `protobuf:"bytes,3,opt,name=spec" json:"spec"` + Spec ConfigSpec `protobuf:"bytes,3,opt,name=spec,proto3" json:"spec"` +} + +func (m *Config) Reset() { *m = Config{} } +func (*Config) ProtoMessage() {} +func (*Config) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{9} +} +func (m *Config) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Config) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Config.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Config) XXX_Merge(src proto.Message) { + xxx_messageInfo_Config.Merge(m, src) +} +func (m *Config) XXX_Size() int { + return m.Size() +} +func (m *Config) XXX_DiscardUnknown() { + xxx_messageInfo_Config.DiscardUnknown(m) } -func (m *Config) Reset() { *m = Config{} } -func (*Config) ProtoMessage() {} -func (*Config) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{9} } +var xxx_messageInfo_Config proto.InternalMessageInfo // Resource is a top-level object with externally defined content and indexing. // SwarmKit can serve as a store for these objects without understanding their // meanings. type Resource struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Meta Meta `protobuf:"bytes,2,opt,name=meta" json:"meta"` - Annotations Annotations `protobuf:"bytes,3,opt,name=annotations" json:"annotations"` + Meta Meta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta"` + Annotations Annotations `protobuf:"bytes,3,opt,name=annotations,proto3" json:"annotations"` // Kind identifies this class of object. It is essentially a namespace // to keep IDs or indices from colliding between unrelated Resource // objects. This must correspond to the name of an Extension. Kind string `protobuf:"bytes,4,opt,name=kind,proto3" json:"kind,omitempty"` // Payload bytes. This data is not interpreted in any way by SwarmKit. // By convention, it should be a marshalled protocol buffers message. - Payload *google_protobuf3.Any `protobuf:"bytes,5,opt,name=payload" json:"payload,omitempty"` + Payload *types.Any `protobuf:"bytes,5,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (m *Resource) Reset() { *m = Resource{} } +func (*Resource) ProtoMessage() {} +func (*Resource) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{10} +} +func (m *Resource) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Resource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Resource.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Resource) XXX_Merge(src proto.Message) { + xxx_messageInfo_Resource.Merge(m, src) +} +func (m *Resource) XXX_Size() int { + return m.Size() +} +func (m *Resource) XXX_DiscardUnknown() { + xxx_messageInfo_Resource.DiscardUnknown(m) } -func (m *Resource) Reset() { *m = Resource{} } -func (*Resource) ProtoMessage() {} -func (*Resource) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{10} } +var xxx_messageInfo_Resource proto.InternalMessageInfo // Extension declares a type of "resource" object. This message provides some // metadata about the objects. type Extension struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Meta Meta `protobuf:"bytes,2,opt,name=meta" json:"meta"` - Annotations Annotations `protobuf:"bytes,3,opt,name=annotations" json:"annotations"` + Meta Meta `protobuf:"bytes,2,opt,name=meta,proto3" json:"meta"` + Annotations Annotations `protobuf:"bytes,3,opt,name=annotations,proto3" json:"annotations"` Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` } -func (m *Extension) Reset() { *m = Extension{} } -func (*Extension) ProtoMessage() {} -func (*Extension) Descriptor() ([]byte, []int) { return fileDescriptorObjects, []int{11} } +func (m *Extension) Reset() { *m = Extension{} } +func (*Extension) ProtoMessage() {} +func (*Extension) Descriptor() ([]byte, []int) { + return fileDescriptor_6218a23329ef342d, []int{11} +} +func (m *Extension) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Extension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Extension.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Extension) XXX_Merge(src proto.Message) { + xxx_messageInfo_Extension.Merge(m, src) +} +func (m *Extension) XXX_Size() int { + return m.Size() +} +func (m *Extension) XXX_DiscardUnknown() { + xxx_messageInfo_Extension.DiscardUnknown(m) +} + +var xxx_messageInfo_Extension proto.InternalMessageInfo func init() { proto.RegisterType((*Meta)(nil), "docker.swarmkit.v1.Meta") @@ -358,14 +759,131 @@ func init() { proto.RegisterType((*Endpoint_VirtualIP)(nil), "docker.swarmkit.v1.Endpoint.VirtualIP") proto.RegisterType((*Task)(nil), "docker.swarmkit.v1.Task") proto.RegisterType((*NetworkAttachment)(nil), "docker.swarmkit.v1.NetworkAttachment") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.NetworkAttachment.DriverAttachmentOptsEntry") proto.RegisterType((*Network)(nil), "docker.swarmkit.v1.Network") proto.RegisterType((*Cluster)(nil), "docker.swarmkit.v1.Cluster") + proto.RegisterMapType((map[string]*BlacklistedCertificate)(nil), "docker.swarmkit.v1.Cluster.BlacklistedCertificatesEntry") proto.RegisterType((*Secret)(nil), "docker.swarmkit.v1.Secret") proto.RegisterType((*Config)(nil), "docker.swarmkit.v1.Config") proto.RegisterType((*Resource)(nil), "docker.swarmkit.v1.Resource") proto.RegisterType((*Extension)(nil), "docker.swarmkit.v1.Extension") } +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/objects.proto", fileDescriptor_6218a23329ef342d) +} + +var fileDescriptor_6218a23329ef342d = []byte{ + // 1700 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcd, 0x73, 0x1b, 0x49, + 0x15, 0xf7, 0xc8, 0x13, 0x49, 0xf3, 0xf4, 0x81, 0xe9, 0x35, 0x66, 0x62, 0x8c, 0x6c, 0xb4, 0x2c, + 0x95, 0xda, 0x4a, 0xc9, 0x4b, 0x58, 0xc0, 0x31, 0xbb, 0x6c, 0x24, 0xdb, 0x64, 0xc5, 0x6e, 0x36, + 0xae, 0xf6, 0x6e, 0x76, 0x6f, 0x43, 0x6b, 0xa6, 0xad, 0x4c, 0x34, 0x9a, 0x9e, 0x9a, 0x6e, 0x69, + 0x11, 0x27, 0xce, 0x39, 0x71, 0xcb, 0x8d, 0x03, 0xc5, 0x3f, 0xc1, 0x85, 0x03, 0x07, 0x2a, 0x1c, + 0xa8, 0xda, 0x13, 0x95, 0x93, 0x8b, 0x28, 0x7f, 0x05, 0x37, 0xaa, 0x7b, 0x7a, 0xa4, 0x71, 0x34, + 0xb6, 0x1c, 0x2a, 0x95, 0xe2, 0xa4, 0xfe, 0xf8, 0xfd, 0x5e, 0xbf, 0xf7, 0xe6, 0x7d, 0x74, 0x0b, + 0x6e, 0xf6, 0x7d, 0xf1, 0x70, 0xd4, 0x6b, 0xb9, 0x6c, 0xb8, 0xeb, 0x31, 0x77, 0x40, 0xe3, 0x5d, + 0xfe, 0x35, 0x89, 0x87, 0x03, 0x5f, 0xec, 0x92, 0xc8, 0xdf, 0x65, 0xbd, 0x47, 0xd4, 0x15, 0xbc, + 0x15, 0xc5, 0x4c, 0x30, 0x84, 0x12, 0x48, 0x2b, 0x85, 0xb4, 0xc6, 0x3f, 0xde, 0x7c, 0x77, 0x89, + 0x04, 0x31, 0x89, 0xa8, 0xe6, 0x2f, 0xc5, 0xf2, 0x88, 0xba, 0x29, 0x76, 0xbb, 0xcf, 0x58, 0x3f, + 0xa0, 0xbb, 0x6a, 0xd6, 0x1b, 0x9d, 0xee, 0x0a, 0x7f, 0x48, 0xb9, 0x20, 0xc3, 0x48, 0x03, 0xd6, + 0xfb, 0xac, 0xcf, 0xd4, 0x70, 0x57, 0x8e, 0xf4, 0xea, 0xf5, 0x97, 0x69, 0x24, 0x9c, 0xe8, 0xad, + 0x9f, 0x5f, 0x72, 0xfa, 0x0c, 0x1e, 0x05, 0xa3, 0xbe, 0x1f, 0xea, 0x9f, 0x84, 0xd8, 0xfc, 0x8b, + 0x01, 0xe6, 0x3d, 0x2a, 0x08, 0xfa, 0x05, 0x94, 0xc6, 0x34, 0xe6, 0x3e, 0x0b, 0x6d, 0x63, 0xc7, + 0xb8, 0x51, 0xb9, 0xf5, 0xbd, 0xd6, 0xa2, 0x47, 0x5a, 0x0f, 0x12, 0x48, 0xc7, 0x7c, 0x7a, 0xb6, + 0xbd, 0x82, 0x53, 0x06, 0xba, 0x0d, 0xe0, 0xc6, 0x94, 0x08, 0xea, 0x39, 0x44, 0xd8, 0x05, 0xc5, + 0xdf, 0x6c, 0x25, 0xea, 0xb6, 0xd2, 0xf3, 0x5b, 0x9f, 0xa7, 0x56, 0x62, 0x4b, 0xa3, 0xdb, 0x42, + 0x52, 0x47, 0x91, 0x97, 0x52, 0x57, 0x97, 0x53, 0x35, 0xba, 0x2d, 0x9a, 0x7f, 0xbe, 0x06, 0xe6, + 0x67, 0xcc, 0xa3, 0x68, 0x03, 0x0a, 0xbe, 0xa7, 0xd4, 0xb6, 0x3a, 0xc5, 0xe9, 0xd9, 0x76, 0xa1, + 0x7b, 0x88, 0x0b, 0xbe, 0x87, 0x6e, 0x81, 0x39, 0xa4, 0x82, 0x68, 0x85, 0xec, 0x3c, 0x83, 0xa4, + 0xed, 0xda, 0x1a, 0x85, 0x45, 0x3f, 0x03, 0x53, 0x7e, 0x2a, 0xad, 0xc9, 0x56, 0x1e, 0x47, 0x9e, + 0x79, 0x12, 0x51, 0x37, 0xe5, 0x49, 0x3c, 0x3a, 0x82, 0x8a, 0x47, 0xb9, 0x1b, 0xfb, 0x91, 0x90, + 0x3e, 0x34, 0x15, 0xfd, 0xed, 0x8b, 0xe8, 0x87, 0x73, 0x28, 0xce, 0xf2, 0xd0, 0x07, 0x50, 0xe4, + 0x82, 0x88, 0x11, 0xb7, 0xaf, 0x29, 0x09, 0x8d, 0x0b, 0x15, 0x50, 0x28, 0xad, 0x82, 0xe6, 0xa0, + 0x8f, 0xa1, 0x3e, 0x24, 0x21, 0xe9, 0xd3, 0xd8, 0xd1, 0x52, 0x8a, 0x4a, 0xca, 0x0f, 0x72, 0x4d, + 0x4f, 0x90, 0x89, 0x20, 0x5c, 0x1b, 0x66, 0xa7, 0xa8, 0x0b, 0x40, 0x84, 0x20, 0xee, 0xc3, 0x21, + 0x0d, 0x85, 0x5d, 0x52, 0x52, 0xde, 0xc9, 0xd5, 0x85, 0x8a, 0xaf, 0x59, 0x3c, 0x68, 0xcf, 0xc0, + 0x9d, 0x82, 0x6d, 0xe0, 0x0c, 0x19, 0xdd, 0x85, 0x8a, 0x4b, 0x63, 0xe1, 0x9f, 0xfa, 0x2e, 0x11, + 0xd4, 0x2e, 0x2b, 0x59, 0xdb, 0x79, 0xb2, 0x0e, 0xe6, 0x30, 0x6d, 0x58, 0x96, 0x89, 0xde, 0x03, + 0x33, 0x66, 0x01, 0xb5, 0xad, 0x1d, 0xe3, 0x46, 0xfd, 0xe2, 0x4f, 0x83, 0x59, 0x40, 0xb1, 0x42, + 0xca, 0xa3, 0xe7, 0x8a, 0x70, 0x1b, 0x76, 0x56, 0xaf, 0x6c, 0x06, 0xce, 0x32, 0x51, 0x13, 0xaa, + 0x0f, 0xbe, 0xfa, 0xb4, 0xfd, 0xd9, 0x17, 0x87, 0xc7, 0xc7, 0x2c, 0x16, 0x76, 0x65, 0xc7, 0xb8, + 0x51, 0xc3, 0xe7, 0xd6, 0xf6, 0x37, 0x1e, 0x3f, 0x69, 0x22, 0x58, 0x2b, 0x1b, 0x6b, 0x86, 0x8a, + 0x45, 0xe3, 0x3d, 0xe3, 0x2b, 0xe3, 0x37, 0x46, 0xf3, 0xb9, 0x09, 0xa5, 0x13, 0x1a, 0x8f, 0x7d, + 0xf7, 0xf5, 0x46, 0xea, 0xed, 0x73, 0x91, 0x9a, 0xeb, 0x50, 0x7d, 0xec, 0x42, 0xb0, 0xfe, 0x12, + 0xaa, 0xf2, 0xd7, 0x49, 0x33, 0x1e, 0x96, 0x66, 0x3c, 0xae, 0x48, 0x82, 0x9e, 0xa0, 0x43, 0xa8, + 0x45, 0x31, 0x1d, 0xfb, 0x6c, 0xc4, 0x1d, 0xa5, 0x43, 0xf1, 0x4a, 0x3a, 0xe0, 0x6a, 0xca, 0x92, + 0x33, 0x74, 0x1f, 0xbe, 0x73, 0x4e, 0xca, 0x4c, 0x9d, 0xca, 0x72, 0x75, 0xde, 0xca, 0x4a, 0x4a, + 0xd5, 0xda, 0x83, 0x32, 0x0d, 0xbd, 0x88, 0xf9, 0xa1, 0xd0, 0x09, 0x98, 0x1b, 0x24, 0x47, 0x1a, + 0x83, 0x67, 0x68, 0x74, 0x04, 0xb5, 0xa4, 0xae, 0x38, 0xe7, 0xb2, 0x6f, 0x27, 0x8f, 0xfe, 0x85, + 0x02, 0xea, 0xb4, 0xa9, 0x8e, 0x32, 0x33, 0xf4, 0x01, 0xc0, 0x23, 0xd6, 0x4b, 0x65, 0x54, 0x95, + 0x8c, 0xef, 0xe7, 0xc9, 0xf8, 0x35, 0xeb, 0x69, 0x01, 0xd6, 0xa3, 0x74, 0x88, 0xde, 0x81, 0x7a, + 0x44, 0x43, 0xcf, 0x0f, 0xfb, 0x8e, 0x47, 0x03, 0x2a, 0xa8, 0xca, 0xbb, 0x32, 0xae, 0xe9, 0xd5, + 0x43, 0xb5, 0xb8, 0x8f, 0x1e, 0x3f, 0x69, 0xd6, 0xa1, 0x9a, 0x8d, 0xb3, 0xe6, 0x1f, 0x0b, 0x50, + 0x4e, 0xcd, 0x42, 0xef, 0xeb, 0xc0, 0x30, 0x2e, 0xb6, 0x21, 0xc5, 0xaa, 0xaf, 0x92, 0xc4, 0xc4, + 0xfb, 0x70, 0x2d, 0x62, 0xb1, 0xe0, 0x76, 0x41, 0x65, 0x49, 0x6e, 0xe1, 0x91, 0x71, 0x7e, 0xc0, + 0xc2, 0x53, 0xbf, 0x8f, 0x13, 0x30, 0xfa, 0x12, 0x2a, 0x63, 0x3f, 0x16, 0x23, 0x12, 0x38, 0x7e, + 0xc4, 0xed, 0x55, 0xc5, 0xfd, 0xd1, 0x65, 0x47, 0xb6, 0x1e, 0x24, 0xf8, 0xee, 0x71, 0xa7, 0x3e, + 0x3d, 0xdb, 0x86, 0xd9, 0x94, 0x63, 0xd0, 0xa2, 0xba, 0x11, 0xdf, 0xbc, 0x07, 0xd6, 0x6c, 0x07, + 0xdd, 0x04, 0x08, 0x93, 0x04, 0x75, 0x66, 0xe9, 0x53, 0x9b, 0x9e, 0x6d, 0x5b, 0x3a, 0x6d, 0xbb, + 0x87, 0xd8, 0xd2, 0x80, 0xae, 0x87, 0x10, 0x98, 0xc4, 0xf3, 0x62, 0x95, 0x4c, 0x16, 0x56, 0xe3, + 0xe6, 0x3f, 0x4b, 0x60, 0x7e, 0x4e, 0xf8, 0xe0, 0x4d, 0xf7, 0x0a, 0x79, 0xe6, 0xd2, 0xf4, 0xab, + 0xbf, 0x62, 0xfa, 0xdd, 0x04, 0xe0, 0x49, 0x56, 0x49, 0x77, 0x98, 0x73, 0x77, 0xe8, 0x5c, 0x93, + 0xee, 0xd0, 0x80, 0xc4, 0x1d, 0x3c, 0x60, 0x42, 0x85, 0xb4, 0x89, 0xd5, 0x18, 0xbd, 0x0d, 0xa5, + 0x90, 0x79, 0x8a, 0x5e, 0x54, 0x74, 0x98, 0x9e, 0x6d, 0x17, 0x65, 0xf5, 0xec, 0x1e, 0xe2, 0xa2, + 0xdc, 0xea, 0x7a, 0xaa, 0x7a, 0x86, 0x21, 0x13, 0x44, 0x76, 0x26, 0xae, 0x9b, 0x40, 0x6e, 0x8e, + 0xb7, 0xe7, 0xb0, 0xb4, 0x70, 0x67, 0x98, 0xe8, 0x01, 0xbc, 0x95, 0xea, 0x9b, 0x15, 0x58, 0x7e, + 0x15, 0x81, 0x48, 0x4b, 0xc8, 0xec, 0x64, 0x9a, 0xa5, 0x75, 0x71, 0xb3, 0x54, 0x5f, 0x20, 0xaf, + 0x59, 0x76, 0xa0, 0xe6, 0x51, 0xee, 0xc7, 0xd4, 0x53, 0x09, 0x4b, 0x55, 0x15, 0xac, 0xe7, 0xe7, + 0x6b, 0x2a, 0x84, 0xe2, 0xaa, 0xe6, 0xa8, 0x19, 0x6a, 0x43, 0x59, 0xc7, 0x1d, 0xb7, 0x2b, 0xaf, + 0xd2, 0x5d, 0x66, 0xb4, 0x73, 0x45, 0xab, 0xfa, 0x4a, 0x45, 0xeb, 0x36, 0x40, 0xc0, 0xfa, 0x8e, + 0x17, 0xfb, 0x63, 0x1a, 0xdb, 0x35, 0x7d, 0x75, 0xca, 0xe1, 0x1e, 0x2a, 0x04, 0xb6, 0x02, 0xd6, + 0x4f, 0x86, 0x88, 0xc0, 0x26, 0xe1, 0xdc, 0xef, 0x87, 0xd4, 0x73, 0xfa, 0x34, 0xa4, 0xb1, 0xef, + 0x3a, 0x31, 0xe5, 0x6c, 0x14, 0xbb, 0x94, 0xdb, 0xdf, 0x52, 0x96, 0xe4, 0x5e, 0x5e, 0xee, 0x26, + 0x60, 0xac, 0xb1, 0xd8, 0x4e, 0xc5, 0xbc, 0xb4, 0xc1, 0xd1, 0x1d, 0xa8, 0xc9, 0x5a, 0xe8, 0x0b, + 0x1a, 0xab, 0xcf, 0x65, 0xaf, 0x2d, 0x8f, 0xf2, 0xea, 0x23, 0xd6, 0xeb, 0xa6, 0x84, 0xfd, 0xcd, + 0xc7, 0x4f, 0x9a, 0x1b, 0xb0, 0x9e, 0x2d, 0x74, 0x7b, 0xc6, 0x1d, 0xe3, 0x63, 0xe3, 0xd8, 0x68, + 0xfe, 0xad, 0x00, 0xdf, 0x5e, 0xf0, 0x2a, 0xfa, 0x29, 0x94, 0xb4, 0x5f, 0x2f, 0xbb, 0xc4, 0x6a, + 0x1e, 0x4e, 0xb1, 0x68, 0x0b, 0x2c, 0x59, 0x24, 0x28, 0xe7, 0x34, 0x29, 0x7f, 0x16, 0x9e, 0x2f, + 0x20, 0x1b, 0x4a, 0x24, 0xf0, 0x89, 0xdc, 0x5b, 0x55, 0x7b, 0xe9, 0x14, 0x8d, 0x60, 0x23, 0x71, + 0xbe, 0x33, 0xbf, 0x2b, 0x38, 0x2c, 0x12, 0xdc, 0x36, 0x95, 0x07, 0x3f, 0xba, 0x52, 0x2c, 0xe8, + 0xcf, 0x33, 0x5f, 0xb8, 0x1f, 0x09, 0x7e, 0x14, 0x8a, 0x78, 0x82, 0xd7, 0xbd, 0x9c, 0xad, 0xcd, + 0xbb, 0x70, 0xfd, 0x42, 0x0a, 0x5a, 0x83, 0xd5, 0x01, 0x9d, 0x24, 0x05, 0x0e, 0xcb, 0x21, 0x5a, + 0x87, 0x6b, 0x63, 0x12, 0x8c, 0xa8, 0xae, 0x87, 0xc9, 0x64, 0xbf, 0xb0, 0x67, 0x34, 0xff, 0x51, + 0x80, 0x92, 0x56, 0xe7, 0x4d, 0xdf, 0x4c, 0xf4, 0xb1, 0x0b, 0xa5, 0xf1, 0x43, 0xa8, 0x6a, 0x97, + 0x26, 0x39, 0x69, 0x2e, 0x8d, 0xea, 0x4a, 0x82, 0x4f, 0xf2, 0xf1, 0x43, 0x30, 0xfd, 0x88, 0x0c, + 0x75, 0xfb, 0xce, 0x3d, 0xb9, 0x7b, 0xdc, 0xbe, 0x77, 0x3f, 0x4a, 0x4a, 0x4b, 0x79, 0x7a, 0xb6, + 0x6d, 0xca, 0x05, 0xac, 0x68, 0x39, 0x1d, 0xb8, 0x78, 0xd5, 0x0e, 0xfc, 0xf7, 0x22, 0x94, 0x0e, + 0x82, 0x11, 0x17, 0x34, 0x7e, 0xd3, 0xbe, 0xd4, 0xc7, 0x2e, 0xf8, 0xf2, 0x00, 0x4a, 0x31, 0x63, + 0xc2, 0x71, 0xc9, 0x65, 0x6e, 0xc4, 0x8c, 0x89, 0x83, 0x76, 0xa7, 0x2e, 0x89, 0xb2, 0x09, 0x24, + 0x73, 0x5c, 0x94, 0xd4, 0x03, 0x82, 0xbe, 0x84, 0x8d, 0xb4, 0xf5, 0xf6, 0x18, 0x13, 0x5c, 0xc4, + 0x24, 0x72, 0x06, 0x74, 0x22, 0xaf, 0x48, 0xab, 0x17, 0x3d, 0x2d, 0x8e, 0x42, 0x37, 0x9e, 0x28, + 0x1f, 0x7f, 0x42, 0x27, 0x78, 0x5d, 0x0b, 0xe8, 0xa4, 0xfc, 0x4f, 0xe8, 0x84, 0xa3, 0x8f, 0x60, + 0x8b, 0xce, 0x60, 0x52, 0xa2, 0x13, 0x90, 0xa1, 0xbc, 0x54, 0x38, 0x6e, 0xc0, 0xdc, 0x81, 0xf2, + 0xbc, 0x89, 0xaf, 0xd3, 0xac, 0xa8, 0x4f, 0x13, 0xc4, 0x81, 0x04, 0x20, 0x0e, 0x76, 0x2f, 0x20, + 0xee, 0x20, 0xf0, 0xb9, 0x7c, 0x3d, 0x66, 0x5e, 0x0a, 0xb2, 0xb5, 0x48, 0xdd, 0xf6, 0x2e, 0xf1, + 0x56, 0xab, 0x33, 0xe7, 0x66, 0xde, 0x1d, 0x3a, 0xf1, 0xbe, 0xdb, 0xcb, 0xdf, 0x45, 0x1d, 0xa8, + 0x8c, 0x42, 0x79, 0x7c, 0xe2, 0x03, 0xeb, 0xaa, 0x3e, 0x80, 0x84, 0xa5, 0x2c, 0xdf, 0x02, 0xf3, + 0x54, 0x5e, 0x96, 0x64, 0xbf, 0x29, 0x27, 0x31, 0xf8, 0xab, 0xee, 0xf1, 0x09, 0x56, 0xab, 0xa8, + 0x05, 0xc8, 0xa3, 0xa7, 0x64, 0x14, 0x88, 0x76, 0x52, 0x82, 0x8e, 0x19, 0x0b, 0x54, 0x73, 0xb1, + 0x70, 0xce, 0x0e, 0x6a, 0x00, 0xf0, 0x51, 0x2f, 0xa4, 0xe2, 0xc4, 0xff, 0x1d, 0x55, 0x1d, 0xa4, + 0x86, 0x33, 0x2b, 0x0b, 0x4f, 0x97, 0xda, 0xe2, 0xd3, 0x65, 0x73, 0x0c, 0x5b, 0x97, 0xb9, 0x23, + 0xa7, 0xa8, 0xdc, 0xc9, 0x16, 0x95, 0xca, 0xad, 0x77, 0xf3, 0x3c, 0x90, 0x2f, 0x32, 0x53, 0x80, + 0x72, 0x13, 0xe9, 0xaf, 0x06, 0x14, 0x4f, 0xa8, 0x1b, 0x53, 0xf1, 0x5a, 0xf3, 0x68, 0xef, 0x5c, + 0x1e, 0x35, 0xf2, 0x5f, 0x2a, 0xf2, 0xd4, 0x85, 0x34, 0xda, 0x84, 0xb2, 0x1f, 0x0a, 0x1a, 0x87, + 0x24, 0x50, 0x79, 0x54, 0xc6, 0xb3, 0x79, 0xae, 0x01, 0x7f, 0x32, 0xa0, 0x98, 0x5c, 0x92, 0xdf, + 0xb4, 0x01, 0xc9, 0xa9, 0x2f, 0x1b, 0x90, 0xab, 0xe4, 0x7f, 0x0c, 0x28, 0xa7, 0xbd, 0xfa, 0xb5, + 0xaa, 0xf9, 0xd2, 0xa5, 0x71, 0xf5, 0x7f, 0xbe, 0x34, 0x22, 0x30, 0x07, 0x7e, 0xa8, 0xaf, 0xb7, + 0x58, 0x8d, 0x51, 0x0b, 0x4a, 0x11, 0x99, 0x04, 0x8c, 0x78, 0xba, 0xc2, 0xaf, 0x2f, 0xfc, 0x53, + 0xd4, 0x0e, 0x27, 0x38, 0x05, 0xed, 0xaf, 0x3f, 0x7e, 0xd2, 0x5c, 0x83, 0x7a, 0xd6, 0xf2, 0x87, + 0x46, 0xf3, 0x5f, 0x06, 0x58, 0x47, 0xbf, 0x15, 0x34, 0x54, 0x97, 0xe9, 0xff, 0x4b, 0xe3, 0x77, + 0x16, 0xff, 0x4d, 0xb2, 0xce, 0xfd, 0x51, 0x94, 0xf7, 0x51, 0x3b, 0x3f, 0x7c, 0xfa, 0xbc, 0xb1, + 0xf2, 0xec, 0x79, 0x63, 0xe5, 0xf7, 0xd3, 0x86, 0xf1, 0x74, 0xda, 0x30, 0xbe, 0x99, 0x36, 0x8c, + 0x7f, 0x4f, 0x1b, 0xc6, 0x1f, 0x5e, 0x34, 0x56, 0xbe, 0x79, 0xd1, 0x58, 0x79, 0xf6, 0xa2, 0xb1, + 0xd2, 0x2b, 0x2a, 0x5f, 0xfd, 0xe4, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x9e, 0xda, 0xee, 0xd3, + 0x20, 0x15, 0x00, 0x00, +} + func (m *Meta) Copy() *Meta { if m == nil { return nil @@ -379,14 +897,14 @@ func (m *Meta) CopyFrom(src interface{}) { o := src.(*Meta) *m = *o - deepcopy.Copy(&m.Version, &o.Version) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Version, &o.Version) if o.CreatedAt != nil { - m.CreatedAt = &google_protobuf.Timestamp{} - deepcopy.Copy(m.CreatedAt, o.CreatedAt) + m.CreatedAt = &types.Timestamp{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.CreatedAt, o.CreatedAt) } if o.UpdatedAt != nil { - m.UpdatedAt = &google_protobuf.Timestamp{} - deepcopy.Copy(m.UpdatedAt, o.UpdatedAt) + m.UpdatedAt = &types.Timestamp{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.UpdatedAt, o.UpdatedAt) } } @@ -403,27 +921,27 @@ func (m *Node) CopyFrom(src interface{}) { o := src.(*Node) *m = *o - deepcopy.Copy(&m.Meta, &o.Meta) - deepcopy.Copy(&m.Spec, &o.Spec) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Meta, &o.Meta) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Spec, &o.Spec) if o.Description != nil { m.Description = &NodeDescription{} - deepcopy.Copy(m.Description, o.Description) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Description, o.Description) } - deepcopy.Copy(&m.Status, &o.Status) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Status, &o.Status) if o.ManagerStatus != nil { m.ManagerStatus = &ManagerStatus{} - deepcopy.Copy(m.ManagerStatus, o.ManagerStatus) + github_com_docker_swarmkit_api_deepcopy.Copy(m.ManagerStatus, o.ManagerStatus) } if o.Attachment != nil { m.Attachment = &NetworkAttachment{} - deepcopy.Copy(m.Attachment, o.Attachment) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Attachment, o.Attachment) } - deepcopy.Copy(&m.Certificate, &o.Certificate) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Certificate, &o.Certificate) if o.Attachments != nil { m.Attachments = make([]*NetworkAttachment, len(o.Attachments)) for i := range m.Attachments { m.Attachments[i] = &NetworkAttachment{} - deepcopy.Copy(m.Attachments[i], o.Attachments[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Attachments[i], o.Attachments[i]) } } @@ -442,27 +960,31 @@ func (m *Service) CopyFrom(src interface{}) { o := src.(*Service) *m = *o - deepcopy.Copy(&m.Meta, &o.Meta) - deepcopy.Copy(&m.Spec, &o.Spec) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Meta, &o.Meta) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Spec, &o.Spec) if o.SpecVersion != nil { m.SpecVersion = &Version{} - deepcopy.Copy(m.SpecVersion, o.SpecVersion) + github_com_docker_swarmkit_api_deepcopy.Copy(m.SpecVersion, o.SpecVersion) } if o.PreviousSpec != nil { m.PreviousSpec = &ServiceSpec{} - deepcopy.Copy(m.PreviousSpec, o.PreviousSpec) + github_com_docker_swarmkit_api_deepcopy.Copy(m.PreviousSpec, o.PreviousSpec) } if o.PreviousSpecVersion != nil { m.PreviousSpecVersion = &Version{} - deepcopy.Copy(m.PreviousSpecVersion, o.PreviousSpecVersion) + github_com_docker_swarmkit_api_deepcopy.Copy(m.PreviousSpecVersion, o.PreviousSpecVersion) } if o.Endpoint != nil { m.Endpoint = &Endpoint{} - deepcopy.Copy(m.Endpoint, o.Endpoint) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Endpoint, o.Endpoint) } if o.UpdateStatus != nil { m.UpdateStatus = &UpdateStatus{} - deepcopy.Copy(m.UpdateStatus, o.UpdateStatus) + github_com_docker_swarmkit_api_deepcopy.Copy(m.UpdateStatus, o.UpdateStatus) + } + if o.JobStatus != nil { + m.JobStatus = &JobStatus{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.JobStatus, o.JobStatus) } } @@ -481,13 +1003,13 @@ func (m *Endpoint) CopyFrom(src interface{}) { *m = *o if o.Spec != nil { m.Spec = &EndpointSpec{} - deepcopy.Copy(m.Spec, o.Spec) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Spec, o.Spec) } if o.Ports != nil { m.Ports = make([]*PortConfig, len(o.Ports)) for i := range m.Ports { m.Ports[i] = &PortConfig{} - deepcopy.Copy(m.Ports[i], o.Ports[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Ports[i], o.Ports[i]) } } @@ -495,7 +1017,7 @@ func (m *Endpoint) CopyFrom(src interface{}) { m.VirtualIPs = make([]*Endpoint_VirtualIP, len(o.VirtualIPs)) for i := range m.VirtualIPs { m.VirtualIPs[i] = &Endpoint_VirtualIP{} - deepcopy.Copy(m.VirtualIPs[i], o.VirtualIPs[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.VirtualIPs[i], o.VirtualIPs[i]) } } @@ -529,39 +1051,43 @@ func (m *Task) CopyFrom(src interface{}) { o := src.(*Task) *m = *o - deepcopy.Copy(&m.Meta, &o.Meta) - deepcopy.Copy(&m.Spec, &o.Spec) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Meta, &o.Meta) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Spec, &o.Spec) if o.SpecVersion != nil { m.SpecVersion = &Version{} - deepcopy.Copy(m.SpecVersion, o.SpecVersion) + github_com_docker_swarmkit_api_deepcopy.Copy(m.SpecVersion, o.SpecVersion) } - deepcopy.Copy(&m.Annotations, &o.Annotations) - deepcopy.Copy(&m.ServiceAnnotations, &o.ServiceAnnotations) - deepcopy.Copy(&m.Status, &o.Status) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Annotations, &o.Annotations) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.ServiceAnnotations, &o.ServiceAnnotations) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Status, &o.Status) if o.Networks != nil { m.Networks = make([]*NetworkAttachment, len(o.Networks)) for i := range m.Networks { m.Networks[i] = &NetworkAttachment{} - deepcopy.Copy(m.Networks[i], o.Networks[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Networks[i], o.Networks[i]) } } if o.Endpoint != nil { m.Endpoint = &Endpoint{} - deepcopy.Copy(m.Endpoint, o.Endpoint) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Endpoint, o.Endpoint) } if o.LogDriver != nil { m.LogDriver = &Driver{} - deepcopy.Copy(m.LogDriver, o.LogDriver) + github_com_docker_swarmkit_api_deepcopy.Copy(m.LogDriver, o.LogDriver) } if o.AssignedGenericResources != nil { m.AssignedGenericResources = make([]*GenericResource, len(o.AssignedGenericResources)) for i := range m.AssignedGenericResources { m.AssignedGenericResources[i] = &GenericResource{} - deepcopy.Copy(m.AssignedGenericResources[i], o.AssignedGenericResources[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.AssignedGenericResources[i], o.AssignedGenericResources[i]) } } + if o.JobIteration != nil { + m.JobIteration = &Version{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.JobIteration, o.JobIteration) + } } func (m *NetworkAttachment) Copy() *NetworkAttachment { @@ -579,7 +1105,7 @@ func (m *NetworkAttachment) CopyFrom(src interface{}) { *m = *o if o.Network != nil { m.Network = &Network{} - deepcopy.Copy(m.Network, o.Network) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Network, o.Network) } if o.Addresses != nil { m.Addresses = make([]string, len(o.Addresses)) @@ -613,15 +1139,15 @@ func (m *Network) CopyFrom(src interface{}) { o := src.(*Network) *m = *o - deepcopy.Copy(&m.Meta, &o.Meta) - deepcopy.Copy(&m.Spec, &o.Spec) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Meta, &o.Meta) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Spec, &o.Spec) if o.DriverState != nil { m.DriverState = &Driver{} - deepcopy.Copy(m.DriverState, o.DriverState) + github_com_docker_swarmkit_api_deepcopy.Copy(m.DriverState, o.DriverState) } if o.IPAM != nil { m.IPAM = &IPAMOptions{} - deepcopy.Copy(m.IPAM, o.IPAM) + github_com_docker_swarmkit_api_deepcopy.Copy(m.IPAM, o.IPAM) } } @@ -638,14 +1164,14 @@ func (m *Cluster) CopyFrom(src interface{}) { o := src.(*Cluster) *m = *o - deepcopy.Copy(&m.Meta, &o.Meta) - deepcopy.Copy(&m.Spec, &o.Spec) - deepcopy.Copy(&m.RootCA, &o.RootCA) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Meta, &o.Meta) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Spec, &o.Spec) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.RootCA, &o.RootCA) if o.NetworkBootstrapKeys != nil { m.NetworkBootstrapKeys = make([]*EncryptionKey, len(o.NetworkBootstrapKeys)) for i := range m.NetworkBootstrapKeys { m.NetworkBootstrapKeys[i] = &EncryptionKey{} - deepcopy.Copy(m.NetworkBootstrapKeys[i], o.NetworkBootstrapKeys[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.NetworkBootstrapKeys[i], o.NetworkBootstrapKeys[i]) } } @@ -653,7 +1179,7 @@ func (m *Cluster) CopyFrom(src interface{}) { m.BlacklistedCertificates = make(map[string]*BlacklistedCertificate, len(o.BlacklistedCertificates)) for k, v := range o.BlacklistedCertificates { m.BlacklistedCertificates[k] = &BlacklistedCertificate{} - deepcopy.Copy(m.BlacklistedCertificates[k], v) + github_com_docker_swarmkit_api_deepcopy.Copy(m.BlacklistedCertificates[k], v) } } @@ -661,7 +1187,7 @@ func (m *Cluster) CopyFrom(src interface{}) { m.UnlockKeys = make([]*EncryptionKey, len(o.UnlockKeys)) for i := range m.UnlockKeys { m.UnlockKeys[i] = &EncryptionKey{} - deepcopy.Copy(m.UnlockKeys[i], o.UnlockKeys[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.UnlockKeys[i], o.UnlockKeys[i]) } } @@ -685,8 +1211,8 @@ func (m *Secret) CopyFrom(src interface{}) { o := src.(*Secret) *m = *o - deepcopy.Copy(&m.Meta, &o.Meta) - deepcopy.Copy(&m.Spec, &o.Spec) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Meta, &o.Meta) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Spec, &o.Spec) } func (m *Config) Copy() *Config { @@ -702,8 +1228,8 @@ func (m *Config) CopyFrom(src interface{}) { o := src.(*Config) *m = *o - deepcopy.Copy(&m.Meta, &o.Meta) - deepcopy.Copy(&m.Spec, &o.Spec) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Meta, &o.Meta) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Spec, &o.Spec) } func (m *Resource) Copy() *Resource { @@ -719,11 +1245,11 @@ func (m *Resource) CopyFrom(src interface{}) { o := src.(*Resource) *m = *o - deepcopy.Copy(&m.Meta, &o.Meta) - deepcopy.Copy(&m.Annotations, &o.Annotations) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Meta, &o.Meta) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Annotations, &o.Annotations) if o.Payload != nil { - m.Payload = &google_protobuf3.Any{} - deepcopy.Copy(m.Payload, o.Payload) + m.Payload = &types.Any{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Payload, o.Payload) } } @@ -740,14 +1266,14 @@ func (m *Extension) CopyFrom(src interface{}) { o := src.(*Extension) *m = *o - deepcopy.Copy(&m.Meta, &o.Meta) - deepcopy.Copy(&m.Annotations, &o.Annotations) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Meta, &o.Meta) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Annotations, &o.Annotations) } func (m *Meta) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -755,45 +1281,56 @@ func (m *Meta) Marshal() (dAtA []byte, err error) { } func (m *Meta) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Meta) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Version.Size())) - n1, err := m.Version.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.UpdatedAt != nil { + { + size, err := m.UpdatedAt.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } - i += n1 if m.CreatedAt != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.CreatedAt.Size())) - n2, err := m.CreatedAt.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.CreatedAt.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n2 + i-- + dAtA[i] = 0x12 } - if m.UpdatedAt != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.UpdatedAt.Size())) - n3, err := m.UpdatedAt.MarshalTo(dAtA[i:]) + { + size, err := m.Version.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n3 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *Node) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -801,102 +1338,129 @@ func (m *Node) Marshal() (dAtA []byte, err error) { } func (m *Node) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.VXLANUDPPort != 0 { + i = encodeVarintObjects(dAtA, i, uint64(m.VXLANUDPPort)) + i-- + dAtA[i] = 0x58 } - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Meta.Size())) - n4, err := m.Meta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Attachments) > 0 { + for iNdEx := len(m.Attachments) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Attachments[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } } - i += n4 - dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Spec.Size())) - n5, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Role != 0 { + i = encodeVarintObjects(dAtA, i, uint64(m.Role)) + i-- + dAtA[i] = 0x48 } - i += n5 - if m.Description != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Description.Size())) - n6, err := m.Description.MarshalTo(dAtA[i:]) + { + size, err := m.Certificate.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n6 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - dAtA[i] = 0x2a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Status.Size())) - n7, err := m.Status.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + i-- + dAtA[i] = 0x42 + if m.Attachment != nil { + { + size, err := m.Attachment.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a } - i += n7 if m.ManagerStatus != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.ManagerStatus.Size())) - n8, err := m.ManagerStatus.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.ManagerStatus.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n8 + i-- + dAtA[i] = 0x32 } - if m.Attachment != nil { - dAtA[i] = 0x3a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Attachment.Size())) - n9, err := m.Attachment.MarshalTo(dAtA[i:]) + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n9 - } - dAtA[i] = 0x42 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Certificate.Size())) - n10, err := m.Certificate.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n10 - if m.Role != 0 { - dAtA[i] = 0x48 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Role)) + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - if len(m.Attachments) > 0 { - for _, msg := range m.Attachments { - dAtA[i] = 0x52 - i++ - i = encodeVarintObjects(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + i-- + dAtA[i] = 0x2a + if m.Description != nil { + { + size, err := m.Description.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.Meta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Service) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -904,89 +1468,131 @@ func (m *Service) Marshal() (dAtA []byte, err error) { } func (m *Service) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Service) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - } - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Meta.Size())) - n11, err := m.Meta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n11 - dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Spec.Size())) - n12, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.JobStatus != nil { + { + size, err := m.JobStatus.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 } - i += n12 - if m.Endpoint != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Endpoint.Size())) - n13, err := m.Endpoint.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.PreviousSpecVersion != nil { + { + size, err := m.PreviousSpecVersion.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n13 + i-- + dAtA[i] = 0x5a } - if m.UpdateStatus != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.UpdateStatus.Size())) - n14, err := m.UpdateStatus.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.SpecVersion != nil { + { + size, err := m.SpecVersion.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n14 + i-- + dAtA[i] = 0x52 + } + if m.PendingDelete { + i-- + if m.PendingDelete { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 } if m.PreviousSpec != nil { + { + size, err := m.PreviousSpec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x32 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.PreviousSpec.Size())) - n15, err := m.PreviousSpec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + if m.UpdateStatus != nil { + { + size, err := m.UpdateStatus.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n15 + i-- + dAtA[i] = 0x2a } - if m.SpecVersion != nil { - dAtA[i] = 0x52 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.SpecVersion.Size())) - n16, err := m.SpecVersion.MarshalTo(dAtA[i:]) + if m.Endpoint != nil { + { + size, err := m.Endpoint.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n16 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - if m.PreviousSpecVersion != nil { - dAtA[i] = 0x5a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.PreviousSpecVersion.Size())) - n17, err := m.PreviousSpecVersion.MarshalTo(dAtA[i:]) + i-- + dAtA[i] = 0x1a + { + size, err := m.Meta.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n17 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *Endpoint) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -994,51 +1600,62 @@ func (m *Endpoint) Marshal() (dAtA []byte, err error) { } func (m *Endpoint) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Endpoint) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Spec != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Spec.Size())) - n18, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.VirtualIPs) > 0 { + for iNdEx := len(m.VirtualIPs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.VirtualIPs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } - i += n18 } if len(m.Ports) > 0 { - for _, msg := range m.Ports { - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Ports) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Ports[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x12 } } - if len(m.VirtualIPs) > 0 { - for _, msg := range m.VirtualIPs { - dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Spec != nil { + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Endpoint_VirtualIP) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1046,29 +1663,36 @@ func (m *Endpoint_VirtualIP) Marshal() (dAtA []byte, err error) { } func (m *Endpoint_VirtualIP) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Endpoint_VirtualIP) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.NetworkID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.NetworkID))) - i += copy(dAtA[i:], m.NetworkID) - } if len(m.Addr) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Addr) + copy(dAtA[i:], m.Addr) i = encodeVarintObjects(dAtA, i, uint64(len(m.Addr))) - i += copy(dAtA[i:], m.Addr) + i-- + dAtA[i] = 0x12 + } + if len(m.NetworkID) > 0 { + i -= len(m.NetworkID) + copy(dAtA[i:], m.NetworkID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.NetworkID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Task) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1076,139 +1700,181 @@ func (m *Task) Marshal() (dAtA []byte, err error) { } func (m *Task) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Task) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - } - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Meta.Size())) - n19, err := m.Meta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n19 - dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Spec.Size())) - n20, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n20 - if len(m.ServiceID) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.ServiceID))) - i += copy(dAtA[i:], m.ServiceID) + if m.JobIteration != nil { + { + size, err := m.JobIteration.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x82 } - if m.Slot != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Slot)) + if len(m.AssignedGenericResources) > 0 { + for iNdEx := len(m.AssignedGenericResources) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AssignedGenericResources[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x7a + } } - if len(m.NodeID) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.NodeID))) - i += copy(dAtA[i:], m.NodeID) + if m.SpecVersion != nil { + { + size, err := m.SpecVersion.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x72 } - dAtA[i] = 0x3a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Annotations.Size())) - n21, err := m.Annotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.LogDriver != nil { + { + size, err := m.LogDriver.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6a } - i += n21 - dAtA[i] = 0x42 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.ServiceAnnotations.Size())) - n22, err := m.ServiceAnnotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Endpoint != nil { + { + size, err := m.Endpoint.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 } - i += n22 - dAtA[i] = 0x4a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Status.Size())) - n23, err := m.Status.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Networks) > 0 { + for iNdEx := len(m.Networks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Networks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } } - i += n23 if m.DesiredState != 0 { - dAtA[i] = 0x50 - i++ i = encodeVarintObjects(dAtA, i, uint64(m.DesiredState)) + i-- + dAtA[i] = 0x50 } - if len(m.Networks) > 0 { - for _, msg := range m.Networks { - dAtA[i] = 0x5a - i++ - i = encodeVarintObjects(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - if m.Endpoint != nil { - dAtA[i] = 0x62 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Endpoint.Size())) - n24, err := m.Endpoint.MarshalTo(dAtA[i:]) + i-- + dAtA[i] = 0x4a + { + size, err := m.ServiceAnnotations.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n24 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - if m.LogDriver != nil { - dAtA[i] = 0x6a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.LogDriver.Size())) - n25, err := m.LogDriver.MarshalTo(dAtA[i:]) + i-- + dAtA[i] = 0x42 + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n25 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - if m.SpecVersion != nil { - dAtA[i] = 0x72 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.SpecVersion.Size())) - n26, err := m.SpecVersion.MarshalTo(dAtA[i:]) + i-- + dAtA[i] = 0x3a + if len(m.NodeID) > 0 { + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.NodeID))) + i-- + dAtA[i] = 0x32 + } + if m.Slot != 0 { + i = encodeVarintObjects(dAtA, i, uint64(m.Slot)) + i-- + dAtA[i] = 0x28 + } + if len(m.ServiceID) > 0 { + i -= len(m.ServiceID) + copy(dAtA[i:], m.ServiceID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.ServiceID))) + i-- + dAtA[i] = 0x22 + } + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n26 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - if len(m.AssignedGenericResources) > 0 { - for _, msg := range m.AssignedGenericResources { - dAtA[i] = 0x7a - i++ - i = encodeVarintObjects(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n + i-- + dAtA[i] = 0x1a + { + size, err := m.Meta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *NetworkAttachment) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1216,74 +1882,71 @@ func (m *NetworkAttachment) Marshal() (dAtA []byte, err error) { } func (m *NetworkAttachment) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NetworkAttachment) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Network != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Network.Size())) - n27, err := m.Network.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n27 - } - if len(m.Addresses) > 0 { - for _, s := range m.Addresses { + if len(m.DriverAttachmentOpts) > 0 { + for k := range m.DriverAttachmentOpts { + v := m.DriverAttachmentOpts[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintObjects(dAtA, i, uint64(len(v))) + i-- dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintObjects(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintObjects(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 } } if len(m.Aliases) > 0 { - for _, s := range m.Aliases { + for iNdEx := len(m.Aliases) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Aliases[iNdEx]) + copy(dAtA[i:], m.Aliases[iNdEx]) + i = encodeVarintObjects(dAtA, i, uint64(len(m.Aliases[iNdEx]))) + i-- dAtA[i] = 0x1a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - if len(m.DriverAttachmentOpts) > 0 { - for k, _ := range m.DriverAttachmentOpts { - dAtA[i] = 0x22 - i++ - v := m.DriverAttachmentOpts[k] - mapSize := 1 + len(k) + sovObjects(uint64(len(k))) + 1 + len(v) + sovObjects(uint64(len(v))) - i = encodeVarintObjects(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + if len(m.Addresses) > 0 { + for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Addresses[iNdEx]) + copy(dAtA[i:], m.Addresses[iNdEx]) + i = encodeVarintObjects(dAtA, i, uint64(len(m.Addresses[iNdEx]))) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - return i, nil + if m.Network != nil { + { + size, err := m.Network.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *Network) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1291,59 +1954,83 @@ func (m *Network) Marshal() (dAtA []byte, err error) { } func (m *Network) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Network) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - } - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Meta.Size())) - n28, err := m.Meta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.PendingDelete { + i-- + if m.PendingDelete { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 } - i += n28 - dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Spec.Size())) - n29, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.IPAM != nil { + { + size, err := m.IPAM.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a } - i += n29 if m.DriverState != nil { + { + size, err := m.DriverState.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x22 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.DriverState.Size())) - n30, err := m.DriverState.MarshalTo(dAtA[i:]) + } + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n30 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - if m.IPAM != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.IPAM.Size())) - n31, err := m.IPAM.MarshalTo(dAtA[i:]) + i-- + dAtA[i] = 0x1a + { + size, err := m.Meta.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n31 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *Cluster) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1351,134 +2038,147 @@ func (m *Cluster) Marshal() (dAtA []byte, err error) { } func (m *Cluster) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Cluster) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if m.VXLANUDPPort != 0 { + i = encodeVarintObjects(dAtA, i, uint64(m.VXLANUDPPort)) + i-- + dAtA[i] = 0x68 } - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Meta.Size())) - n32, err := m.Meta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.SubnetSize != 0 { + i = encodeVarintObjects(dAtA, i, uint64(m.SubnetSize)) + i-- + dAtA[i] = 0x60 } - i += n32 - dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Spec.Size())) - n33, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.DefaultAddressPool) > 0 { + for iNdEx := len(m.DefaultAddressPool) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DefaultAddressPool[iNdEx]) + copy(dAtA[i:], m.DefaultAddressPool[iNdEx]) + i = encodeVarintObjects(dAtA, i, uint64(len(m.DefaultAddressPool[iNdEx]))) + i-- + dAtA[i] = 0x5a + } } - i += n33 - dAtA[i] = 0x22 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.RootCA.Size())) - n34, err := m.RootCA.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.FIPS { + i-- + if m.FIPS { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x50 } - i += n34 - if len(m.NetworkBootstrapKeys) > 0 { - for _, msg := range m.NetworkBootstrapKeys { - dAtA[i] = 0x2a - i++ - i = encodeVarintObjects(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.UnlockKeys) > 0 { + for iNdEx := len(m.UnlockKeys) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.UnlockKeys[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x4a } } - if m.EncryptionKeyLamportClock != 0 { - dAtA[i] = 0x30 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.EncryptionKeyLamportClock)) - } if len(m.BlacklistedCertificates) > 0 { - for k, _ := range m.BlacklistedCertificates { - dAtA[i] = 0x42 - i++ + for k := range m.BlacklistedCertificates { v := m.BlacklistedCertificates[k] - msgSize := 0 + baseI := i if v != nil { - msgSize = v.Size() - msgSize += 1 + sovObjects(uint64(msgSize)) + { + size, err := v.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 } - mapSize := 1 + len(k) + sovObjects(uint64(len(k))) + msgSize - i = encodeVarintObjects(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintObjects(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - if v != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(v.Size())) - n35, err := v.MarshalTo(dAtA[i:]) + i-- + dAtA[i] = 0xa + i = encodeVarintObjects(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x42 + } + } + if m.EncryptionKeyLamportClock != 0 { + i = encodeVarintObjects(dAtA, i, uint64(m.EncryptionKeyLamportClock)) + i-- + dAtA[i] = 0x30 + } + if len(m.NetworkBootstrapKeys) > 0 { + for iNdEx := len(m.NetworkBootstrapKeys) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.NetworkBootstrapKeys[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n35 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x2a } } - if len(m.UnlockKeys) > 0 { - for _, msg := range m.UnlockKeys { - dAtA[i] = 0x4a - i++ - i = encodeVarintObjects(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n + { + size, err := m.RootCA.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - if m.FIPS { - dAtA[i] = 0x50 - i++ - if m.FIPS { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + i-- + dAtA[i] = 0x22 + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - i++ + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - if len(m.DefaultAddressPool) > 0 { - for _, s := range m.DefaultAddressPool { - dAtA[i] = 0x5a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + i-- + dAtA[i] = 0x1a + { + size, err := m.Meta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - if m.SubnetSize != 0 { - dAtA[i] = 0x60 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.SubnetSize)) + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Secret) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1486,49 +2186,59 @@ func (m *Secret) Marshal() (dAtA []byte, err error) { } func (m *Secret) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Secret) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - } - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Meta.Size())) - n36, err := m.Meta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Internal { + i-- + if m.Internal { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 } - i += n36 - dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Spec.Size())) - n37, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n37 - if m.Internal { - dAtA[i] = 0x20 - i++ - if m.Internal { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + i-- + dAtA[i] = 0x1a + { + size, err := m.Meta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - i++ + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Config) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1536,39 +2246,49 @@ func (m *Config) Marshal() (dAtA []byte, err error) { } func (m *Config) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Config) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - } - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Meta.Size())) - n38, err := m.Meta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n38 + i-- dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Spec.Size())) - n39, err := m.Spec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Meta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - i += n39 - return i, nil + return len(dAtA) - i, nil } func (m *Resource) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1576,55 +2296,68 @@ func (m *Resource) Marshal() (dAtA []byte, err error) { } func (m *Resource) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Resource) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - } - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Meta.Size())) - n40, err := m.Meta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n40 - dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Annotations.Size())) - n41, err := m.Annotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Payload != nil { + { + size, err := m.Payload.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a } - i += n41 if len(m.Kind) > 0 { - dAtA[i] = 0x22 - i++ + i -= len(m.Kind) + copy(dAtA[i:], m.Kind) i = encodeVarintObjects(dAtA, i, uint64(len(m.Kind))) - i += copy(dAtA[i:], m.Kind) + i-- + dAtA[i] = 0x22 } - if m.Payload != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Payload.Size())) - n42, err := m.Payload.MarshalTo(dAtA[i:]) + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size, err := m.Meta.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n42 + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Extension) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1632,52 +2365,67 @@ func (m *Extension) Marshal() (dAtA []byte, err error) { } func (m *Extension) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Extension) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintObjects(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x22 } - dAtA[i] = 0x12 - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Meta.Size())) - n43, err := m.Meta.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n43 + i-- dAtA[i] = 0x1a - i++ - i = encodeVarintObjects(dAtA, i, uint64(m.Annotations.Size())) - n44, err := m.Annotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Meta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintObjects(dAtA, i, uint64(size)) } - i += n44 - if len(m.Description) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintObjects(dAtA, i, uint64(len(m.Description))) - i += copy(dAtA[i:], m.Description) + i-- + dAtA[i] = 0x12 + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintObjects(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintObjects(dAtA []byte, offset int, v uint64) int { + offset -= sovObjects(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } - func (m *Meta) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Version.Size() @@ -1694,6 +2442,9 @@ func (m *Meta) Size() (n int) { } func (m *Node) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1729,10 +2480,16 @@ func (m *Node) Size() (n int) { n += 1 + l + sovObjects(uint64(l)) } } + if m.VXLANUDPPort != 0 { + n += 1 + sovObjects(uint64(m.VXLANUDPPort)) + } return n } func (m *Service) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1755,6 +2512,9 @@ func (m *Service) Size() (n int) { l = m.PreviousSpec.Size() n += 1 + l + sovObjects(uint64(l)) } + if m.PendingDelete { + n += 2 + } if m.SpecVersion != nil { l = m.SpecVersion.Size() n += 1 + l + sovObjects(uint64(l)) @@ -1763,10 +2523,17 @@ func (m *Service) Size() (n int) { l = m.PreviousSpecVersion.Size() n += 1 + l + sovObjects(uint64(l)) } + if m.JobStatus != nil { + l = m.JobStatus.Size() + n += 1 + l + sovObjects(uint64(l)) + } return n } func (m *Endpoint) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Spec != nil { @@ -1789,6 +2556,9 @@ func (m *Endpoint) Size() (n int) { } func (m *Endpoint_VirtualIP) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.NetworkID) @@ -1803,6 +2573,9 @@ func (m *Endpoint_VirtualIP) Size() (n int) { } func (m *Task) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1857,10 +2630,17 @@ func (m *Task) Size() (n int) { n += 1 + l + sovObjects(uint64(l)) } } + if m.JobIteration != nil { + l = m.JobIteration.Size() + n += 2 + l + sovObjects(uint64(l)) + } return n } func (m *NetworkAttachment) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Network != nil { @@ -1891,6 +2671,9 @@ func (m *NetworkAttachment) Size() (n int) { } func (m *Network) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1909,10 +2692,16 @@ func (m *Network) Size() (n int) { l = m.IPAM.Size() n += 1 + l + sovObjects(uint64(l)) } + if m.PendingDelete { + n += 2 + } return n } func (m *Cluster) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1965,10 +2754,16 @@ func (m *Cluster) Size() (n int) { if m.SubnetSize != 0 { n += 1 + sovObjects(uint64(m.SubnetSize)) } + if m.VXLANUDPPort != 0 { + n += 1 + sovObjects(uint64(m.VXLANUDPPort)) + } return n } func (m *Secret) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -1986,6 +2781,9 @@ func (m *Secret) Size() (n int) { } func (m *Config) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -2000,6 +2798,9 @@ func (m *Config) Size() (n int) { } func (m *Resource) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -2022,6 +2823,9 @@ func (m *Resource) Size() (n int) { } func (m *Extension) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -2040,14 +2844,7 @@ func (m *Extension) Size() (n int) { } func sovObjects(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozObjects(x uint64) (n int) { return sovObjects(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -2064,7 +2861,7 @@ type EventCreateNode struct { Checks []NodeCheckFunc } -func (e EventCreateNode) Matches(apiEvent go_events.Event) bool { +func (e EventCreateNode) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventCreateNode) if !ok { return false @@ -2092,7 +2889,7 @@ type EventUpdateNode struct { Checks []NodeCheckFunc } -func (e EventUpdateNode) Matches(apiEvent go_events.Event) bool { +func (e EventUpdateNode) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventUpdateNode) if !ok { return false @@ -2119,7 +2916,7 @@ type EventDeleteNode struct { Checks []NodeCheckFunc } -func (e EventDeleteNode) Matches(apiEvent go_events.Event) bool { +func (e EventDeleteNode) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventDeleteNode) if !ok { return false @@ -2340,7 +3137,7 @@ type EventCreateService struct { Checks []ServiceCheckFunc } -func (e EventCreateService) Matches(apiEvent go_events.Event) bool { +func (e EventCreateService) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventCreateService) if !ok { return false @@ -2368,7 +3165,7 @@ type EventUpdateService struct { Checks []ServiceCheckFunc } -func (e EventUpdateService) Matches(apiEvent go_events.Event) bool { +func (e EventUpdateService) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventUpdateService) if !ok { return false @@ -2395,7 +3192,7 @@ type EventDeleteService struct { Checks []ServiceCheckFunc } -func (e EventDeleteService) Matches(apiEvent go_events.Event) bool { +func (e EventDeleteService) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventDeleteService) if !ok { return false @@ -2586,7 +3383,7 @@ type EventCreateTask struct { Checks []TaskCheckFunc } -func (e EventCreateTask) Matches(apiEvent go_events.Event) bool { +func (e EventCreateTask) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventCreateTask) if !ok { return false @@ -2614,7 +3411,7 @@ type EventUpdateTask struct { Checks []TaskCheckFunc } -func (e EventUpdateTask) Matches(apiEvent go_events.Event) bool { +func (e EventUpdateTask) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventUpdateTask) if !ok { return false @@ -2641,7 +3438,7 @@ type EventDeleteTask struct { Checks []TaskCheckFunc } -func (e EventDeleteTask) Matches(apiEvent go_events.Event) bool { +func (e EventDeleteTask) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventDeleteTask) if !ok { return false @@ -2875,7 +3672,7 @@ type EventCreateNetwork struct { Checks []NetworkCheckFunc } -func (e EventCreateNetwork) Matches(apiEvent go_events.Event) bool { +func (e EventCreateNetwork) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventCreateNetwork) if !ok { return false @@ -2903,7 +3700,7 @@ type EventUpdateNetwork struct { Checks []NetworkCheckFunc } -func (e EventUpdateNetwork) Matches(apiEvent go_events.Event) bool { +func (e EventUpdateNetwork) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventUpdateNetwork) if !ok { return false @@ -2930,7 +3727,7 @@ type EventDeleteNetwork struct { Checks []NetworkCheckFunc } -func (e EventDeleteNetwork) Matches(apiEvent go_events.Event) bool { +func (e EventDeleteNetwork) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventDeleteNetwork) if !ok { return false @@ -3121,7 +3918,7 @@ type EventCreateCluster struct { Checks []ClusterCheckFunc } -func (e EventCreateCluster) Matches(apiEvent go_events.Event) bool { +func (e EventCreateCluster) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventCreateCluster) if !ok { return false @@ -3149,7 +3946,7 @@ type EventUpdateCluster struct { Checks []ClusterCheckFunc } -func (e EventUpdateCluster) Matches(apiEvent go_events.Event) bool { +func (e EventUpdateCluster) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventUpdateCluster) if !ok { return false @@ -3176,7 +3973,7 @@ type EventDeleteCluster struct { Checks []ClusterCheckFunc } -func (e EventDeleteCluster) Matches(apiEvent go_events.Event) bool { +func (e EventDeleteCluster) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventDeleteCluster) if !ok { return false @@ -3367,7 +4164,7 @@ type EventCreateSecret struct { Checks []SecretCheckFunc } -func (e EventCreateSecret) Matches(apiEvent go_events.Event) bool { +func (e EventCreateSecret) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventCreateSecret) if !ok { return false @@ -3395,7 +4192,7 @@ type EventUpdateSecret struct { Checks []SecretCheckFunc } -func (e EventUpdateSecret) Matches(apiEvent go_events.Event) bool { +func (e EventUpdateSecret) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventUpdateSecret) if !ok { return false @@ -3422,7 +4219,7 @@ type EventDeleteSecret struct { Checks []SecretCheckFunc } -func (e EventDeleteSecret) Matches(apiEvent go_events.Event) bool { +func (e EventDeleteSecret) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventDeleteSecret) if !ok { return false @@ -3613,7 +4410,7 @@ type EventCreateConfig struct { Checks []ConfigCheckFunc } -func (e EventCreateConfig) Matches(apiEvent go_events.Event) bool { +func (e EventCreateConfig) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventCreateConfig) if !ok { return false @@ -3641,7 +4438,7 @@ type EventUpdateConfig struct { Checks []ConfigCheckFunc } -func (e EventUpdateConfig) Matches(apiEvent go_events.Event) bool { +func (e EventUpdateConfig) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventUpdateConfig) if !ok { return false @@ -3668,7 +4465,7 @@ type EventDeleteConfig struct { Checks []ConfigCheckFunc } -func (e EventDeleteConfig) Matches(apiEvent go_events.Event) bool { +func (e EventDeleteConfig) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventDeleteConfig) if !ok { return false @@ -3859,7 +4656,7 @@ type EventCreateResource struct { Checks []ResourceCheckFunc } -func (e EventCreateResource) Matches(apiEvent go_events.Event) bool { +func (e EventCreateResource) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventCreateResource) if !ok { return false @@ -3887,7 +4684,7 @@ type EventUpdateResource struct { Checks []ResourceCheckFunc } -func (e EventUpdateResource) Matches(apiEvent go_events.Event) bool { +func (e EventUpdateResource) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventUpdateResource) if !ok { return false @@ -3914,7 +4711,7 @@ type EventDeleteResource struct { Checks []ResourceCheckFunc } -func (e EventDeleteResource) Matches(apiEvent go_events.Event) bool { +func (e EventDeleteResource) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventDeleteResource) if !ok { return false @@ -4111,7 +4908,7 @@ type EventCreateExtension struct { Checks []ExtensionCheckFunc } -func (e EventCreateExtension) Matches(apiEvent go_events.Event) bool { +func (e EventCreateExtension) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventCreateExtension) if !ok { return false @@ -4139,7 +4936,7 @@ type EventUpdateExtension struct { Checks []ExtensionCheckFunc } -func (e EventUpdateExtension) Matches(apiEvent go_events.Event) bool { +func (e EventUpdateExtension) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventUpdateExtension) if !ok { return false @@ -4166,7 +4963,7 @@ type EventDeleteExtension struct { Checks []ExtensionCheckFunc } -func (e EventDeleteExtension) Matches(apiEvent go_events.Event) bool { +func (e EventDeleteExtension) Matches(apiEvent github_com_docker_go_events.Event) bool { typedEvent, ok := apiEvent.(EventDeleteExtension) if !ok { return false @@ -4694,9 +5491,9 @@ func (this *Meta) String() string { return "nil" } s := strings.Join([]string{`&Meta{`, - `Version:` + strings.Replace(strings.Replace(this.Version.String(), "Version", "Version", 1), `&`, ``, 1) + `,`, - `CreatedAt:` + strings.Replace(fmt.Sprintf("%v", this.CreatedAt), "Timestamp", "google_protobuf.Timestamp", 1) + `,`, - `UpdatedAt:` + strings.Replace(fmt.Sprintf("%v", this.UpdatedAt), "Timestamp", "google_protobuf.Timestamp", 1) + `,`, + `Version:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Version), "Version", "Version", 1), `&`, ``, 1) + `,`, + `CreatedAt:` + strings.Replace(fmt.Sprintf("%v", this.CreatedAt), "Timestamp", "types.Timestamp", 1) + `,`, + `UpdatedAt:` + strings.Replace(fmt.Sprintf("%v", this.UpdatedAt), "Timestamp", "types.Timestamp", 1) + `,`, `}`, }, "") return s @@ -4705,17 +5502,23 @@ func (this *Node) String() string { if this == nil { return "nil" } + repeatedStringForAttachments := "[]*NetworkAttachment{" + for _, f := range this.Attachments { + repeatedStringForAttachments += strings.Replace(f.String(), "NetworkAttachment", "NetworkAttachment", 1) + "," + } + repeatedStringForAttachments += "}" s := strings.Join([]string{`&Node{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Meta:` + strings.Replace(strings.Replace(this.Meta.String(), "Meta", "Meta", 1), `&`, ``, 1) + `,`, - `Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "NodeSpec", "NodeSpec", 1), `&`, ``, 1) + `,`, + `Spec:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Spec), "NodeSpec", "NodeSpec", 1), `&`, ``, 1) + `,`, `Description:` + strings.Replace(fmt.Sprintf("%v", this.Description), "NodeDescription", "NodeDescription", 1) + `,`, - `Status:` + strings.Replace(strings.Replace(this.Status.String(), "NodeStatus", "NodeStatus", 1), `&`, ``, 1) + `,`, + `Status:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Status), "NodeStatus", "NodeStatus", 1), `&`, ``, 1) + `,`, `ManagerStatus:` + strings.Replace(fmt.Sprintf("%v", this.ManagerStatus), "ManagerStatus", "ManagerStatus", 1) + `,`, - `Attachment:` + strings.Replace(fmt.Sprintf("%v", this.Attachment), "NetworkAttachment", "NetworkAttachment", 1) + `,`, - `Certificate:` + strings.Replace(strings.Replace(this.Certificate.String(), "Certificate", "Certificate", 1), `&`, ``, 1) + `,`, + `Attachment:` + strings.Replace(this.Attachment.String(), "NetworkAttachment", "NetworkAttachment", 1) + `,`, + `Certificate:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Certificate), "Certificate", "Certificate", 1), `&`, ``, 1) + `,`, `Role:` + fmt.Sprintf("%v", this.Role) + `,`, - `Attachments:` + strings.Replace(fmt.Sprintf("%v", this.Attachments), "NetworkAttachment", "NetworkAttachment", 1) + `,`, + `Attachments:` + repeatedStringForAttachments + `,`, + `VXLANUDPPort:` + fmt.Sprintf("%v", this.VXLANUDPPort) + `,`, `}`, }, "") return s @@ -4727,12 +5530,14 @@ func (this *Service) String() string { s := strings.Join([]string{`&Service{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Meta:` + strings.Replace(strings.Replace(this.Meta.String(), "Meta", "Meta", 1), `&`, ``, 1) + `,`, - `Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "ServiceSpec", "ServiceSpec", 1), `&`, ``, 1) + `,`, - `Endpoint:` + strings.Replace(fmt.Sprintf("%v", this.Endpoint), "Endpoint", "Endpoint", 1) + `,`, + `Spec:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Spec), "ServiceSpec", "ServiceSpec", 1), `&`, ``, 1) + `,`, + `Endpoint:` + strings.Replace(this.Endpoint.String(), "Endpoint", "Endpoint", 1) + `,`, `UpdateStatus:` + strings.Replace(fmt.Sprintf("%v", this.UpdateStatus), "UpdateStatus", "UpdateStatus", 1) + `,`, `PreviousSpec:` + strings.Replace(fmt.Sprintf("%v", this.PreviousSpec), "ServiceSpec", "ServiceSpec", 1) + `,`, + `PendingDelete:` + fmt.Sprintf("%v", this.PendingDelete) + `,`, `SpecVersion:` + strings.Replace(fmt.Sprintf("%v", this.SpecVersion), "Version", "Version", 1) + `,`, `PreviousSpecVersion:` + strings.Replace(fmt.Sprintf("%v", this.PreviousSpecVersion), "Version", "Version", 1) + `,`, + `JobStatus:` + strings.Replace(fmt.Sprintf("%v", this.JobStatus), "JobStatus", "JobStatus", 1) + `,`, `}`, }, "") return s @@ -4741,10 +5546,20 @@ func (this *Endpoint) String() string { if this == nil { return "nil" } + repeatedStringForPorts := "[]*PortConfig{" + for _, f := range this.Ports { + repeatedStringForPorts += strings.Replace(fmt.Sprintf("%v", f), "PortConfig", "PortConfig", 1) + "," + } + repeatedStringForPorts += "}" + repeatedStringForVirtualIPs := "[]*Endpoint_VirtualIP{" + for _, f := range this.VirtualIPs { + repeatedStringForVirtualIPs += strings.Replace(fmt.Sprintf("%v", f), "Endpoint_VirtualIP", "Endpoint_VirtualIP", 1) + "," + } + repeatedStringForVirtualIPs += "}" s := strings.Join([]string{`&Endpoint{`, `Spec:` + strings.Replace(fmt.Sprintf("%v", this.Spec), "EndpointSpec", "EndpointSpec", 1) + `,`, - `Ports:` + strings.Replace(fmt.Sprintf("%v", this.Ports), "PortConfig", "PortConfig", 1) + `,`, - `VirtualIPs:` + strings.Replace(fmt.Sprintf("%v", this.VirtualIPs), "Endpoint_VirtualIP", "Endpoint_VirtualIP", 1) + `,`, + `Ports:` + repeatedStringForPorts + `,`, + `VirtualIPs:` + repeatedStringForVirtualIPs + `,`, `}`, }, "") return s @@ -4764,22 +5579,33 @@ func (this *Task) String() string { if this == nil { return "nil" } + repeatedStringForNetworks := "[]*NetworkAttachment{" + for _, f := range this.Networks { + repeatedStringForNetworks += strings.Replace(f.String(), "NetworkAttachment", "NetworkAttachment", 1) + "," + } + repeatedStringForNetworks += "}" + repeatedStringForAssignedGenericResources := "[]*GenericResource{" + for _, f := range this.AssignedGenericResources { + repeatedStringForAssignedGenericResources += strings.Replace(fmt.Sprintf("%v", f), "GenericResource", "GenericResource", 1) + "," + } + repeatedStringForAssignedGenericResources += "}" s := strings.Join([]string{`&Task{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Meta:` + strings.Replace(strings.Replace(this.Meta.String(), "Meta", "Meta", 1), `&`, ``, 1) + `,`, - `Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "TaskSpec", "TaskSpec", 1), `&`, ``, 1) + `,`, + `Spec:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Spec), "TaskSpec", "TaskSpec", 1), `&`, ``, 1) + `,`, `ServiceID:` + fmt.Sprintf("%v", this.ServiceID) + `,`, `Slot:` + fmt.Sprintf("%v", this.Slot) + `,`, `NodeID:` + fmt.Sprintf("%v", this.NodeID) + `,`, - `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, - `ServiceAnnotations:` + strings.Replace(strings.Replace(this.ServiceAnnotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, - `Status:` + strings.Replace(strings.Replace(this.Status.String(), "TaskStatus", "TaskStatus", 1), `&`, ``, 1) + `,`, + `Annotations:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, + `ServiceAnnotations:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ServiceAnnotations), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, + `Status:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Status), "TaskStatus", "TaskStatus", 1), `&`, ``, 1) + `,`, `DesiredState:` + fmt.Sprintf("%v", this.DesiredState) + `,`, - `Networks:` + strings.Replace(fmt.Sprintf("%v", this.Networks), "NetworkAttachment", "NetworkAttachment", 1) + `,`, - `Endpoint:` + strings.Replace(fmt.Sprintf("%v", this.Endpoint), "Endpoint", "Endpoint", 1) + `,`, + `Networks:` + repeatedStringForNetworks + `,`, + `Endpoint:` + strings.Replace(this.Endpoint.String(), "Endpoint", "Endpoint", 1) + `,`, `LogDriver:` + strings.Replace(fmt.Sprintf("%v", this.LogDriver), "Driver", "Driver", 1) + `,`, `SpecVersion:` + strings.Replace(fmt.Sprintf("%v", this.SpecVersion), "Version", "Version", 1) + `,`, - `AssignedGenericResources:` + strings.Replace(fmt.Sprintf("%v", this.AssignedGenericResources), "GenericResource", "GenericResource", 1) + `,`, + `AssignedGenericResources:` + repeatedStringForAssignedGenericResources + `,`, + `JobIteration:` + strings.Replace(fmt.Sprintf("%v", this.JobIteration), "Version", "Version", 1) + `,`, `}`, }, "") return s @@ -4792,14 +5618,14 @@ func (this *NetworkAttachment) String() string { for k, _ := range this.DriverAttachmentOpts { keysForDriverAttachmentOpts = append(keysForDriverAttachmentOpts, k) } - sortkeys.Strings(keysForDriverAttachmentOpts) + github_com_gogo_protobuf_sortkeys.Strings(keysForDriverAttachmentOpts) mapStringForDriverAttachmentOpts := "map[string]string{" for _, k := range keysForDriverAttachmentOpts { mapStringForDriverAttachmentOpts += fmt.Sprintf("%v: %v,", k, this.DriverAttachmentOpts[k]) } mapStringForDriverAttachmentOpts += "}" s := strings.Join([]string{`&NetworkAttachment{`, - `Network:` + strings.Replace(fmt.Sprintf("%v", this.Network), "Network", "Network", 1) + `,`, + `Network:` + strings.Replace(this.Network.String(), "Network", "Network", 1) + `,`, `Addresses:` + fmt.Sprintf("%v", this.Addresses) + `,`, `Aliases:` + fmt.Sprintf("%v", this.Aliases) + `,`, `DriverAttachmentOpts:` + mapStringForDriverAttachmentOpts + `,`, @@ -4814,9 +5640,10 @@ func (this *Network) String() string { s := strings.Join([]string{`&Network{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Meta:` + strings.Replace(strings.Replace(this.Meta.String(), "Meta", "Meta", 1), `&`, ``, 1) + `,`, - `Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "NetworkSpec", "NetworkSpec", 1), `&`, ``, 1) + `,`, + `Spec:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Spec), "NetworkSpec", "NetworkSpec", 1), `&`, ``, 1) + `,`, `DriverState:` + strings.Replace(fmt.Sprintf("%v", this.DriverState), "Driver", "Driver", 1) + `,`, `IPAM:` + strings.Replace(fmt.Sprintf("%v", this.IPAM), "IPAMOptions", "IPAMOptions", 1) + `,`, + `PendingDelete:` + fmt.Sprintf("%v", this.PendingDelete) + `,`, `}`, }, "") return s @@ -4825,11 +5652,21 @@ func (this *Cluster) String() string { if this == nil { return "nil" } + repeatedStringForNetworkBootstrapKeys := "[]*EncryptionKey{" + for _, f := range this.NetworkBootstrapKeys { + repeatedStringForNetworkBootstrapKeys += strings.Replace(fmt.Sprintf("%v", f), "EncryptionKey", "EncryptionKey", 1) + "," + } + repeatedStringForNetworkBootstrapKeys += "}" + repeatedStringForUnlockKeys := "[]*EncryptionKey{" + for _, f := range this.UnlockKeys { + repeatedStringForUnlockKeys += strings.Replace(fmt.Sprintf("%v", f), "EncryptionKey", "EncryptionKey", 1) + "," + } + repeatedStringForUnlockKeys += "}" keysForBlacklistedCertificates := make([]string, 0, len(this.BlacklistedCertificates)) for k, _ := range this.BlacklistedCertificates { keysForBlacklistedCertificates = append(keysForBlacklistedCertificates, k) } - sortkeys.Strings(keysForBlacklistedCertificates) + github_com_gogo_protobuf_sortkeys.Strings(keysForBlacklistedCertificates) mapStringForBlacklistedCertificates := "map[string]*BlacklistedCertificate{" for _, k := range keysForBlacklistedCertificates { mapStringForBlacklistedCertificates += fmt.Sprintf("%v: %v,", k, this.BlacklistedCertificates[k]) @@ -4838,15 +5675,16 @@ func (this *Cluster) String() string { s := strings.Join([]string{`&Cluster{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Meta:` + strings.Replace(strings.Replace(this.Meta.String(), "Meta", "Meta", 1), `&`, ``, 1) + `,`, - `Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "ClusterSpec", "ClusterSpec", 1), `&`, ``, 1) + `,`, - `RootCA:` + strings.Replace(strings.Replace(this.RootCA.String(), "RootCA", "RootCA", 1), `&`, ``, 1) + `,`, - `NetworkBootstrapKeys:` + strings.Replace(fmt.Sprintf("%v", this.NetworkBootstrapKeys), "EncryptionKey", "EncryptionKey", 1) + `,`, + `Spec:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Spec), "ClusterSpec", "ClusterSpec", 1), `&`, ``, 1) + `,`, + `RootCA:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.RootCA), "RootCA", "RootCA", 1), `&`, ``, 1) + `,`, + `NetworkBootstrapKeys:` + repeatedStringForNetworkBootstrapKeys + `,`, `EncryptionKeyLamportClock:` + fmt.Sprintf("%v", this.EncryptionKeyLamportClock) + `,`, `BlacklistedCertificates:` + mapStringForBlacklistedCertificates + `,`, - `UnlockKeys:` + strings.Replace(fmt.Sprintf("%v", this.UnlockKeys), "EncryptionKey", "EncryptionKey", 1) + `,`, + `UnlockKeys:` + repeatedStringForUnlockKeys + `,`, `FIPS:` + fmt.Sprintf("%v", this.FIPS) + `,`, `DefaultAddressPool:` + fmt.Sprintf("%v", this.DefaultAddressPool) + `,`, `SubnetSize:` + fmt.Sprintf("%v", this.SubnetSize) + `,`, + `VXLANUDPPort:` + fmt.Sprintf("%v", this.VXLANUDPPort) + `,`, `}`, }, "") return s @@ -4858,7 +5696,7 @@ func (this *Secret) String() string { s := strings.Join([]string{`&Secret{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Meta:` + strings.Replace(strings.Replace(this.Meta.String(), "Meta", "Meta", 1), `&`, ``, 1) + `,`, - `Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "SecretSpec", "SecretSpec", 1), `&`, ``, 1) + `,`, + `Spec:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Spec), "SecretSpec", "SecretSpec", 1), `&`, ``, 1) + `,`, `Internal:` + fmt.Sprintf("%v", this.Internal) + `,`, `}`, }, "") @@ -4871,7 +5709,7 @@ func (this *Config) String() string { s := strings.Join([]string{`&Config{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Meta:` + strings.Replace(strings.Replace(this.Meta.String(), "Meta", "Meta", 1), `&`, ``, 1) + `,`, - `Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "ConfigSpec", "ConfigSpec", 1), `&`, ``, 1) + `,`, + `Spec:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Spec), "ConfigSpec", "ConfigSpec", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -4883,9 +5721,9 @@ func (this *Resource) String() string { s := strings.Join([]string{`&Resource{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Meta:` + strings.Replace(strings.Replace(this.Meta.String(), "Meta", "Meta", 1), `&`, ``, 1) + `,`, - `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, + `Annotations:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `Kind:` + fmt.Sprintf("%v", this.Kind) + `,`, - `Payload:` + strings.Replace(fmt.Sprintf("%v", this.Payload), "Any", "google_protobuf3.Any", 1) + `,`, + `Payload:` + strings.Replace(fmt.Sprintf("%v", this.Payload), "Any", "types.Any", 1) + `,`, `}`, }, "") return s @@ -4897,7 +5735,7 @@ func (this *Extension) String() string { s := strings.Join([]string{`&Extension{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `Meta:` + strings.Replace(strings.Replace(this.Meta.String(), "Meta", "Meta", 1), `&`, ``, 1) + `,`, - `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, + `Annotations:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `Description:` + fmt.Sprintf("%v", this.Description) + `,`, `}`, }, "") @@ -4926,7 +5764,7 @@ func (m *Meta) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4954,7 +5792,7 @@ func (m *Meta) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4963,6 +5801,9 @@ func (m *Meta) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4984,7 +5825,7 @@ func (m *Meta) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4993,11 +5834,14 @@ func (m *Meta) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } if m.CreatedAt == nil { - m.CreatedAt = &google_protobuf.Timestamp{} + m.CreatedAt = &types.Timestamp{} } if err := m.CreatedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -5017,7 +5861,7 @@ func (m *Meta) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5026,11 +5870,14 @@ func (m *Meta) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } if m.UpdatedAt == nil { - m.UpdatedAt = &google_protobuf.Timestamp{} + m.UpdatedAt = &types.Timestamp{} } if err := m.UpdatedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -5042,7 +5889,7 @@ func (m *Meta) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -5072,7 +5919,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5100,7 +5947,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5110,6 +5957,9 @@ func (m *Node) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5129,7 +5979,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5138,6 +5988,9 @@ func (m *Node) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5159,7 +6012,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5168,6 +6021,9 @@ func (m *Node) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5189,7 +6045,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5198,6 +6054,9 @@ func (m *Node) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5222,7 +6081,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5231,6 +6090,9 @@ func (m *Node) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5252,7 +6114,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5261,6 +6123,9 @@ func (m *Node) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5285,7 +6150,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5294,6 +6159,9 @@ func (m *Node) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5318,7 +6186,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5327,6 +6195,9 @@ func (m *Node) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5348,7 +6219,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Role |= (NodeRole(b) & 0x7F) << shift + m.Role |= NodeRole(b&0x7F) << shift if b < 0x80 { break } @@ -5367,7 +6238,7 @@ func (m *Node) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5376,6 +6247,9 @@ func (m *Node) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5384,13 +6258,32 @@ func (m *Node) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VXLANUDPPort", wireType) + } + m.VXLANUDPPort = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowObjects + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.VXLANUDPPort |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipObjects(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -5420,7 +6313,7 @@ func (m *Service) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5448,7 +6341,7 @@ func (m *Service) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5458,6 +6351,9 @@ func (m *Service) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5477,7 +6373,7 @@ func (m *Service) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5486,6 +6382,9 @@ func (m *Service) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5507,7 +6406,7 @@ func (m *Service) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5516,6 +6415,9 @@ func (m *Service) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5537,7 +6439,7 @@ func (m *Service) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5546,6 +6448,9 @@ func (m *Service) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5570,28 +6475,87 @@ func (m *Service) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthObjects + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.UpdateStatus == nil { + m.UpdateStatus = &UpdateStatus{} + } + if err := m.UpdateStatus.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PreviousSpec", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowObjects + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthObjects + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PreviousSpec == nil { + m.PreviousSpec = &ServiceSpec{} + } + if err := m.PreviousSpec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PendingDelete", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowObjects + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { - return ErrInvalidLengthObjects - } - postIndex := iNdEx + msglen - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.UpdateStatus == nil { - m.UpdateStatus = &UpdateStatus{} - } - if err := m.UpdateStatus.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 6: + m.PendingDelete = bool(v != 0) + case 10: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PreviousSpec", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field SpecVersion", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -5603,7 +6567,7 @@ func (m *Service) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5612,19 +6576,22 @@ func (m *Service) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.PreviousSpec == nil { - m.PreviousSpec = &ServiceSpec{} + if m.SpecVersion == nil { + m.SpecVersion = &Version{} } - if err := m.PreviousSpec.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.SpecVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 10: + case 11: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field SpecVersion", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PreviousSpecVersion", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -5636,7 +6603,7 @@ func (m *Service) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5645,19 +6612,22 @@ func (m *Service) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.SpecVersion == nil { - m.SpecVersion = &Version{} + if m.PreviousSpecVersion == nil { + m.PreviousSpecVersion = &Version{} } - if err := m.SpecVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.PreviousSpecVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 11: + case 12: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PreviousSpecVersion", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field JobStatus", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -5669,7 +6639,7 @@ func (m *Service) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5678,13 +6648,16 @@ func (m *Service) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.PreviousSpecVersion == nil { - m.PreviousSpecVersion = &Version{} + if m.JobStatus == nil { + m.JobStatus = &JobStatus{} } - if err := m.PreviousSpecVersion.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.JobStatus.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -5694,7 +6667,7 @@ func (m *Service) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -5724,7 +6697,7 @@ func (m *Endpoint) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5752,7 +6725,7 @@ func (m *Endpoint) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5761,6 +6734,9 @@ func (m *Endpoint) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5785,7 +6761,7 @@ func (m *Endpoint) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5794,6 +6770,9 @@ func (m *Endpoint) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5816,7 +6795,7 @@ func (m *Endpoint) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5825,6 +6804,9 @@ func (m *Endpoint) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5839,7 +6821,7 @@ func (m *Endpoint) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -5869,7 +6851,7 @@ func (m *Endpoint_VirtualIP) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5897,7 +6879,7 @@ func (m *Endpoint_VirtualIP) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5907,6 +6889,9 @@ func (m *Endpoint_VirtualIP) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5926,7 +6911,7 @@ func (m *Endpoint_VirtualIP) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5936,6 +6921,9 @@ func (m *Endpoint_VirtualIP) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5947,7 +6935,7 @@ func (m *Endpoint_VirtualIP) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -5977,7 +6965,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6005,7 +6993,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6015,6 +7003,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6034,7 +7025,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6043,6 +7034,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6064,7 +7058,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6073,6 +7067,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6094,7 +7091,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6104,6 +7101,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6123,7 +7123,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Slot |= (uint64(b) & 0x7F) << shift + m.Slot |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6142,7 +7142,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6152,6 +7152,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6171,7 +7174,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6180,6 +7183,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6201,7 +7207,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6210,6 +7216,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6231,7 +7240,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6240,6 +7249,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6261,7 +7273,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.DesiredState |= (TaskState(b) & 0x7F) << shift + m.DesiredState |= TaskState(b&0x7F) << shift if b < 0x80 { break } @@ -6280,7 +7292,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6289,6 +7301,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6311,7 +7326,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6320,6 +7335,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6344,7 +7362,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6353,6 +7371,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6377,7 +7398,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6386,6 +7407,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6410,7 +7434,7 @@ func (m *Task) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6419,6 +7443,9 @@ func (m *Task) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6427,13 +7454,49 @@ func (m *Task) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 16: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JobIteration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowObjects + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthObjects + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.JobIteration == nil { + m.JobIteration = &Version{} + } + if err := m.JobIteration.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipObjects(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -6463,7 +7526,7 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6491,7 +7554,7 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6500,6 +7563,9 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6524,7 +7590,7 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6534,6 +7600,9 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6553,7 +7622,7 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6563,6 +7632,9 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6582,7 +7654,7 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6591,6 +7663,9 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6611,7 +7686,7 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6628,7 +7703,7 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6638,6 +7713,9 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthObjects + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -6654,7 +7732,7 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6664,6 +7742,9 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthObjects + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -6675,7 +7756,7 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > postIndex { @@ -6692,7 +7773,7 @@ func (m *NetworkAttachment) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -6722,7 +7803,7 @@ func (m *Network) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6750,7 +7831,7 @@ func (m *Network) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6760,6 +7841,9 @@ func (m *Network) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6779,7 +7863,7 @@ func (m *Network) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6788,6 +7872,9 @@ func (m *Network) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6809,7 +7896,7 @@ func (m *Network) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6818,6 +7905,9 @@ func (m *Network) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6839,7 +7929,7 @@ func (m *Network) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6848,6 +7938,9 @@ func (m *Network) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6872,7 +7965,7 @@ func (m *Network) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6881,6 +7974,9 @@ func (m *Network) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6891,13 +7987,33 @@ func (m *Network) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PendingDelete", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowObjects + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.PendingDelete = bool(v != 0) default: iNdEx = preIndex skippy, err := skipObjects(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -6927,7 +8043,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6955,7 +8071,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6965,6 +8081,9 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6984,7 +8103,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6993,6 +8112,9 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7014,7 +8136,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7023,6 +8145,9 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7044,7 +8169,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7053,6 +8178,9 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7074,7 +8202,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7083,6 +8211,9 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7105,7 +8236,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.EncryptionKeyLamportClock |= (uint64(b) & 0x7F) << shift + m.EncryptionKeyLamportClock |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7124,7 +8255,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7133,6 +8264,9 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7153,7 +8287,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7170,7 +8304,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7180,6 +8314,9 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthObjects + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -7196,7 +8333,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - mapmsglen |= (int(b) & 0x7F) << shift + mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7205,7 +8342,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postmsgIndex := iNdEx + mapmsglen - if mapmsglen < 0 { + if postmsgIndex < 0 { return ErrInvalidLengthObjects } if postmsgIndex > l { @@ -7222,7 +8359,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > postIndex { @@ -7247,7 +8384,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7256,6 +8393,9 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7278,7 +8418,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7298,7 +8438,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7308,6 +8448,9 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7327,7 +8470,26 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.SubnetSize |= (uint32(b) & 0x7F) << shift + m.SubnetSize |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field VXLANUDPPort", wireType) + } + m.VXLANUDPPort = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowObjects + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.VXLANUDPPort |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -7338,7 +8500,7 @@ func (m *Cluster) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -7368,7 +8530,7 @@ func (m *Secret) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7396,7 +8558,7 @@ func (m *Secret) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7406,6 +8568,9 @@ func (m *Secret) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7425,7 +8590,7 @@ func (m *Secret) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7434,6 +8599,9 @@ func (m *Secret) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7455,7 +8623,7 @@ func (m *Secret) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7464,6 +8632,9 @@ func (m *Secret) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7485,7 +8656,7 @@ func (m *Secret) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7497,7 +8668,7 @@ func (m *Secret) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -7527,7 +8698,7 @@ func (m *Config) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7555,7 +8726,7 @@ func (m *Config) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7565,6 +8736,9 @@ func (m *Config) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7584,7 +8758,7 @@ func (m *Config) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7593,6 +8767,9 @@ func (m *Config) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7614,7 +8791,7 @@ func (m *Config) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7623,6 +8800,9 @@ func (m *Config) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7636,7 +8816,7 @@ func (m *Config) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -7666,7 +8846,7 @@ func (m *Resource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7694,7 +8874,7 @@ func (m *Resource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7704,6 +8884,9 @@ func (m *Resource) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7723,7 +8906,7 @@ func (m *Resource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7732,6 +8915,9 @@ func (m *Resource) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7753,7 +8939,7 @@ func (m *Resource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7762,6 +8948,9 @@ func (m *Resource) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7783,7 +8972,7 @@ func (m *Resource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7793,6 +8982,9 @@ func (m *Resource) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7812,7 +9004,7 @@ func (m *Resource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7821,11 +9013,14 @@ func (m *Resource) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Payload == nil { - m.Payload = &google_protobuf3.Any{} + m.Payload = &types.Any{} } if err := m.Payload.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -7837,7 +9032,7 @@ func (m *Resource) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -7867,7 +9062,7 @@ func (m *Extension) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7895,7 +9090,7 @@ func (m *Extension) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7905,6 +9100,9 @@ func (m *Extension) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7924,7 +9122,7 @@ func (m *Extension) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7933,6 +9131,9 @@ func (m *Extension) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7954,7 +9155,7 @@ func (m *Extension) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -7963,6 +9164,9 @@ func (m *Extension) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -7984,7 +9188,7 @@ func (m *Extension) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -7994,6 +9198,9 @@ func (m *Extension) Unmarshal(dAtA []byte) error { return ErrInvalidLengthObjects } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthObjects + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8005,7 +9212,7 @@ func (m *Extension) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthObjects } if (iNdEx + skippy) > l { @@ -8023,6 +9230,7 @@ func (m *Extension) Unmarshal(dAtA []byte) error { func skipObjects(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -8054,10 +9262,8 @@ func skipObjects(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -8074,158 +9280,34 @@ func skipObjects(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthObjects } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowObjects - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipObjects(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupObjects + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthObjects + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthObjects = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowObjects = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthObjects = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowObjects = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupObjects = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("github.com/docker/swarmkit/api/objects.proto", fileDescriptorObjects) } - -var fileDescriptorObjects = []byte{ - // 1581 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4b, 0x73, 0x1b, 0x4b, - 0x15, 0xce, 0x48, 0x63, 0x3d, 0x8e, 0x6c, 0x61, 0xfa, 0x1a, 0x33, 0x11, 0x46, 0x32, 0xba, 0x05, - 0x75, 0xeb, 0x56, 0x4a, 0xbe, 0x98, 0x0b, 0x38, 0x86, 0xcb, 0x8d, 0x64, 0x9b, 0x44, 0x15, 0x42, - 0x5c, 0xed, 0x90, 0xb0, 0x1b, 0x5a, 0x33, 0x6d, 0x65, 0xd0, 0x68, 0x7a, 0x6a, 0xba, 0xa5, 0x20, - 0x56, 0x59, 0x9b, 0x1f, 0xe0, 0x1d, 0x8b, 0xfc, 0x0b, 0x36, 0x2c, 0x58, 0x65, 0xc9, 0x8a, 0x62, - 0xe5, 0x22, 0xfa, 0x17, 0x54, 0xb1, 0xa0, 0xba, 0xa7, 0x47, 0x1a, 0x5b, 0xe3, 0x17, 0x95, 0x72, - 0xb1, 0x72, 0x3f, 0xbe, 0xef, 0xf4, 0x39, 0x67, 0xce, 0xcb, 0x82, 0x07, 0x7d, 0x4f, 0xbc, 0x1e, - 0xf5, 0x5a, 0x0e, 0x1b, 0x6e, 0xb9, 0xcc, 0x19, 0xd0, 0x68, 0x8b, 0xbf, 0x21, 0xd1, 0x70, 0xe0, - 0x89, 0x2d, 0x12, 0x7a, 0x5b, 0xac, 0xf7, 0x7b, 0xea, 0x08, 0xde, 0x0a, 0x23, 0x26, 0x18, 0x42, - 0x31, 0xa4, 0x95, 0x40, 0x5a, 0xe3, 0x1f, 0xd6, 0x3e, 0xbf, 0x46, 0x82, 0x98, 0x84, 0x54, 0xf3, - 0xaf, 0xc5, 0xf2, 0x90, 0x3a, 0x09, 0xb6, 0xd1, 0x67, 0xac, 0xef, 0xd3, 0x2d, 0xb5, 0xeb, 0x8d, - 0x8e, 0xb7, 0x84, 0x37, 0xa4, 0x5c, 0x90, 0x61, 0xa8, 0x01, 0x6b, 0x7d, 0xd6, 0x67, 0x6a, 0xb9, - 0x25, 0x57, 0xfa, 0xf4, 0xfe, 0x45, 0x1a, 0x09, 0x26, 0xfa, 0xea, 0xa7, 0x57, 0xbc, 0x3e, 0x83, - 0x87, 0xfe, 0xa8, 0xef, 0x05, 0xfa, 0x4f, 0x4c, 0x6c, 0xfe, 0xc5, 0x00, 0xf3, 0x19, 0x15, 0x04, - 0xfd, 0x0c, 0x8a, 0x63, 0x1a, 0x71, 0x8f, 0x05, 0x96, 0xb1, 0x69, 0x7c, 0x56, 0xd9, 0xfe, 0x4e, - 0x6b, 0xd1, 0x23, 0xad, 0x97, 0x31, 0xa4, 0x63, 0xbe, 0x3f, 0x6b, 0xdc, 0xc3, 0x09, 0x03, 0x3d, - 0x04, 0x70, 0x22, 0x4a, 0x04, 0x75, 0x6d, 0x22, 0xac, 0x9c, 0xe2, 0xd7, 0x5a, 0xb1, 0xba, 0xad, - 0xe4, 0xfd, 0xd6, 0x8b, 0xc4, 0x4a, 0x5c, 0xd6, 0xe8, 0xb6, 0x90, 0xd4, 0x51, 0xe8, 0x26, 0xd4, - 0xfc, 0xf5, 0x54, 0x8d, 0x6e, 0x8b, 0xe6, 0xdb, 0x25, 0x30, 0x7f, 0xcd, 0x5c, 0x8a, 0xd6, 0x21, - 0xe7, 0xb9, 0x4a, 0xed, 0x72, 0xa7, 0x30, 0x3d, 0x6b, 0xe4, 0xba, 0xfb, 0x38, 0xe7, 0xb9, 0x68, - 0x1b, 0xcc, 0x21, 0x15, 0x44, 0x2b, 0x64, 0x65, 0x19, 0x24, 0x6d, 0xd7, 0xd6, 0x28, 0x2c, 0xfa, - 0x09, 0x98, 0xf2, 0x53, 0x69, 0x4d, 0x36, 0xb2, 0x38, 0xf2, 0xcd, 0xa3, 0x90, 0x3a, 0x09, 0x4f, - 0xe2, 0xd1, 0x01, 0x54, 0x5c, 0xca, 0x9d, 0xc8, 0x0b, 0x85, 0xf4, 0xa1, 0xa9, 0xe8, 0x9f, 0x5e, - 0x46, 0xdf, 0x9f, 0x43, 0x71, 0x9a, 0x87, 0x7e, 0x0e, 0x05, 0x2e, 0x88, 0x18, 0x71, 0x6b, 0x49, - 0x49, 0xa8, 0x5f, 0xaa, 0x80, 0x42, 0x69, 0x15, 0x34, 0x07, 0x3d, 0x81, 0xea, 0x90, 0x04, 0xa4, - 0x4f, 0x23, 0x5b, 0x4b, 0x29, 0x28, 0x29, 0xdf, 0xcb, 0x34, 0x3d, 0x46, 0xc6, 0x82, 0xf0, 0xca, - 0x30, 0xbd, 0x45, 0x5d, 0x00, 0x22, 0x04, 0x71, 0x5e, 0x0f, 0x69, 0x20, 0xac, 0xa2, 0x92, 0xf2, - 0xfd, 0x4c, 0x5d, 0xa8, 0x78, 0xc3, 0xa2, 0x41, 0x7b, 0x06, 0xee, 0xe4, 0x2c, 0x03, 0xa7, 0xc8, - 0xe8, 0x31, 0x54, 0x1c, 0x1a, 0x09, 0xef, 0xd8, 0x73, 0x88, 0xa0, 0x56, 0x49, 0xc9, 0x6a, 0x64, - 0xc9, 0xda, 0x9b, 0xc3, 0xb4, 0x61, 0x69, 0x26, 0xfa, 0x02, 0xcc, 0x88, 0xf9, 0xd4, 0x2a, 0x6f, - 0x1a, 0x9f, 0x55, 0x2f, 0xff, 0x34, 0x98, 0xf9, 0x14, 0x2b, 0xa4, 0x7c, 0x7a, 0xae, 0x08, 0xb7, - 0x60, 0x33, 0x7f, 0x63, 0x33, 0x70, 0x9a, 0xb9, 0xbb, 0x7e, 0x72, 0xda, 0x44, 0xb0, 0x5a, 0x32, - 0x56, 0x0d, 0x15, 0x67, 0xc6, 0x17, 0xc6, 0x6f, 0x8d, 0xdf, 0x19, 0xcd, 0xff, 0xe4, 0xa1, 0x78, - 0x44, 0xa3, 0xb1, 0xe7, 0x7c, 0xdc, 0x28, 0x7c, 0x78, 0x2e, 0x0a, 0x33, 0x9d, 0xa5, 0x9f, 0x5d, - 0x08, 0xc4, 0x1d, 0x28, 0xd1, 0xc0, 0x0d, 0x99, 0x17, 0x08, 0x1d, 0x85, 0x99, 0x9e, 0x3a, 0xd0, - 0x18, 0x3c, 0x43, 0xa3, 0x03, 0x58, 0x89, 0x93, 0xcb, 0x3e, 0x17, 0x82, 0x9b, 0x59, 0xf4, 0xdf, - 0x28, 0xa0, 0x8e, 0x9d, 0xe5, 0x51, 0x6a, 0x87, 0xf6, 0x61, 0x25, 0x8c, 0xe8, 0xd8, 0x63, 0x23, - 0x6e, 0x2b, 0x23, 0x0a, 0x37, 0x32, 0x02, 0x2f, 0x27, 0x2c, 0xb9, 0x43, 0xbf, 0x80, 0x65, 0x49, - 0xb6, 0x93, 0xa2, 0x04, 0xd7, 0x16, 0x25, 0x5c, 0x91, 0x04, 0xbd, 0x41, 0xcf, 0xe1, 0x5b, 0xe7, - 0xb4, 0x98, 0x09, 0xaa, 0x5c, 0x2f, 0xe8, 0x93, 0xb4, 0x26, 0xfa, 0x70, 0x17, 0x9d, 0x9c, 0x36, - 0xab, 0xb0, 0x9c, 0x0e, 0x81, 0xe6, 0x9f, 0x73, 0x50, 0x4a, 0x1c, 0x89, 0xbe, 0xd4, 0xdf, 0xcc, - 0xb8, 0xdc, 0x6b, 0x09, 0x56, 0xd9, 0x1b, 0x7f, 0xae, 0x2f, 0x61, 0x29, 0x64, 0x91, 0xe0, 0x56, - 0x4e, 0x05, 0x67, 0x66, 0xbe, 0x1f, 0xb2, 0x48, 0xec, 0xb1, 0xe0, 0xd8, 0xeb, 0xe3, 0x18, 0x8c, - 0x5e, 0x41, 0x65, 0xec, 0x45, 0x62, 0x44, 0x7c, 0xdb, 0x0b, 0xb9, 0x95, 0x57, 0xdc, 0x1f, 0x5c, - 0xf5, 0x64, 0xeb, 0x65, 0x8c, 0xef, 0x1e, 0x76, 0xaa, 0xd3, 0xb3, 0x06, 0xcc, 0xb6, 0x1c, 0x83, - 0x16, 0xd5, 0x0d, 0x79, 0xed, 0x19, 0x94, 0x67, 0x37, 0xe8, 0x01, 0x40, 0x10, 0xe7, 0x85, 0x3d, - 0x8b, 0xec, 0x95, 0xe9, 0x59, 0xa3, 0xac, 0xb3, 0xa5, 0xbb, 0x8f, 0xcb, 0x1a, 0xd0, 0x75, 0x11, - 0x02, 0x93, 0xb8, 0x6e, 0xa4, 0xe2, 0xbc, 0x8c, 0xd5, 0xba, 0xf9, 0xa7, 0x22, 0x98, 0x2f, 0x08, - 0x1f, 0xdc, 0x75, 0x89, 0x96, 0x6f, 0x2e, 0x64, 0xc6, 0x03, 0x00, 0x1e, 0xc7, 0x9b, 0x34, 0xc7, - 0x9c, 0x9b, 0xa3, 0xa3, 0x50, 0x9a, 0xa3, 0x01, 0xb1, 0x39, 0xdc, 0x67, 0x42, 0x25, 0x81, 0x89, - 0xd5, 0x1a, 0x7d, 0x0a, 0xc5, 0x80, 0xb9, 0x8a, 0x5e, 0x50, 0x74, 0x98, 0x9e, 0x35, 0x0a, 0xb2, - 0xe8, 0x74, 0xf7, 0x71, 0x41, 0x5e, 0x75, 0x5d, 0x55, 0x74, 0x82, 0x80, 0x09, 0x22, 0x0b, 0x3a, - 0xd7, 0xb5, 0x33, 0x33, 0xfa, 0xdb, 0x73, 0x58, 0x52, 0xef, 0x52, 0x4c, 0xf4, 0x12, 0x3e, 0x49, - 0xf4, 0x4d, 0x0b, 0x2c, 0xdd, 0x46, 0x20, 0xd2, 0x12, 0x52, 0x37, 0xa9, 0x1e, 0x53, 0xbe, 0xbc, - 0xc7, 0x28, 0x0f, 0x66, 0xf5, 0x98, 0x0e, 0xac, 0xb8, 0x94, 0x7b, 0x11, 0x75, 0x55, 0x99, 0xa0, - 0x2a, 0x33, 0xab, 0xdb, 0xdf, 0xbd, 0x4a, 0x08, 0xc5, 0xcb, 0x9a, 0xa3, 0x76, 0xa8, 0x0d, 0x25, - 0x1d, 0x37, 0xdc, 0xaa, 0xdc, 0xa6, 0x28, 0xcf, 0x68, 0xe7, 0xca, 0xdc, 0xf2, 0xad, 0xca, 0xdc, - 0x43, 0x00, 0x9f, 0xf5, 0x6d, 0x37, 0xf2, 0xc6, 0x34, 0xb2, 0x56, 0xf4, 0xc4, 0x91, 0xc1, 0xdd, - 0x57, 0x08, 0x5c, 0xf6, 0x59, 0x3f, 0x5e, 0x2e, 0x14, 0xa5, 0xea, 0x2d, 0x8b, 0x12, 0x81, 0x1a, - 0xe1, 0xdc, 0xeb, 0x07, 0xd4, 0xb5, 0xfb, 0x34, 0xa0, 0x91, 0xe7, 0xd8, 0x11, 0xe5, 0x6c, 0x14, - 0x39, 0x94, 0x5b, 0xdf, 0x50, 0x9e, 0xc8, 0x9c, 0x19, 0x1e, 0xc7, 0x60, 0xac, 0xb1, 0xd8, 0x4a, - 0xc4, 0x5c, 0xb8, 0xe0, 0xbb, 0xb5, 0x93, 0xd3, 0xe6, 0x3a, 0xac, 0xa5, 0xcb, 0xd4, 0x8e, 0xf1, - 0xc8, 0x78, 0x62, 0x1c, 0x1a, 0xcd, 0xbf, 0xe5, 0xe0, 0x9b, 0x0b, 0x3e, 0x45, 0x3f, 0x86, 0xa2, - 0xf6, 0xea, 0x55, 0x93, 0x9f, 0xe6, 0xe1, 0x04, 0x8b, 0x36, 0xa0, 0x2c, 0x53, 0x9c, 0x72, 0x4e, - 0xe3, 0xe2, 0x55, 0xc6, 0xf3, 0x03, 0x64, 0x41, 0x91, 0xf8, 0x1e, 0x91, 0x77, 0x79, 0x75, 0x97, - 0x6c, 0xd1, 0x08, 0xd6, 0x63, 0xd7, 0xdb, 0xf3, 0x06, 0x6b, 0xb3, 0x50, 0x70, 0xcb, 0x54, 0xf6, - 0x7f, 0x7d, 0xa3, 0x48, 0xd0, 0x1f, 0x67, 0x7e, 0xf0, 0x3c, 0x14, 0xfc, 0x20, 0x10, 0xd1, 0x04, - 0xaf, 0xb9, 0x19, 0x57, 0xb5, 0xc7, 0x70, 0xff, 0x52, 0x0a, 0x5a, 0x85, 0xfc, 0x80, 0x4e, 0xe2, - 0xf2, 0x84, 0xe5, 0x12, 0xad, 0xc1, 0xd2, 0x98, 0xf8, 0x23, 0xaa, 0xab, 0x59, 0xbc, 0xd9, 0xcd, - 0xed, 0x18, 0xcd, 0x77, 0x39, 0x28, 0x6a, 0x75, 0xee, 0xba, 0xe5, 0xeb, 0x67, 0x17, 0x0a, 0xdb, - 0x57, 0xb0, 0xac, 0x5d, 0x1a, 0x67, 0xa4, 0x79, 0x6d, 0x4c, 0x57, 0x62, 0x7c, 0x9c, 0x8d, 0x5f, - 0x81, 0xe9, 0x85, 0x64, 0xa8, 0xdb, 0x7d, 0xe6, 0xcb, 0xdd, 0xc3, 0xf6, 0xb3, 0xe7, 0x61, 0x5c, - 0x58, 0x4a, 0xd3, 0xb3, 0x86, 0x29, 0x0f, 0xb0, 0xa2, 0x65, 0x36, 0xc6, 0x77, 0x05, 0x28, 0xee, - 0xf9, 0x23, 0x2e, 0x68, 0x74, 0xd7, 0x4e, 0xd2, 0xcf, 0x2e, 0x38, 0x69, 0x0f, 0x8a, 0x11, 0x63, - 0xc2, 0x76, 0xc8, 0x55, 0xfe, 0xc1, 0x8c, 0x89, 0xbd, 0x76, 0xa7, 0x2a, 0x89, 0xb2, 0xb6, 0xc7, - 0x7b, 0x5c, 0x90, 0xd4, 0x3d, 0x82, 0x5e, 0xc1, 0x7a, 0xd2, 0x11, 0x7b, 0x8c, 0x09, 0x2e, 0x22, - 0x12, 0xda, 0x03, 0x3a, 0x91, 0xb3, 0x52, 0xfe, 0xb2, 0x41, 0xfb, 0x20, 0x70, 0xa2, 0x89, 0x72, - 0xde, 0x53, 0x3a, 0xc1, 0x6b, 0x5a, 0x40, 0x27, 0xe1, 0x3f, 0xa5, 0x13, 0x8e, 0xbe, 0x86, 0x0d, - 0x3a, 0x83, 0x49, 0x89, 0xb6, 0x4f, 0x86, 0xb2, 0xd7, 0xdb, 0x8e, 0xcf, 0x9c, 0x81, 0x6a, 0x37, - 0x26, 0xbe, 0x4f, 0xd3, 0xa2, 0x7e, 0x15, 0x23, 0xf6, 0x24, 0x00, 0x71, 0xb0, 0x7a, 0x3e, 0x71, - 0x06, 0xbe, 0xc7, 0xe5, 0xff, 0x52, 0xa9, 0xb9, 0x59, 0x76, 0x0c, 0xa9, 0xdb, 0xce, 0x15, 0xde, - 0x6a, 0x75, 0xe6, 0xdc, 0xd4, 0x14, 0xae, 0x33, 0xea, 0xdb, 0xbd, 0xec, 0x5b, 0xd4, 0x81, 0xca, - 0x28, 0x90, 0xcf, 0xc7, 0x3e, 0x28, 0xdf, 0xd4, 0x07, 0x10, 0xb3, 0x94, 0xe5, 0x1b, 0x60, 0x1e, - 0xcb, 0x19, 0x46, 0xb6, 0x91, 0x52, 0x1c, 0x5c, 0xbf, 0xec, 0x1e, 0x1e, 0x61, 0x75, 0x8a, 0x5a, - 0x80, 0x5c, 0x7a, 0x4c, 0x46, 0xbe, 0x68, 0xc7, 0xb5, 0xe5, 0x90, 0x31, 0x5f, 0xf5, 0x8c, 0x32, - 0xce, 0xb8, 0x41, 0x75, 0x00, 0x3e, 0xea, 0x05, 0x54, 0x1c, 0x79, 0x7f, 0xa4, 0xaa, 0x31, 0xac, - 0xe0, 0xd4, 0x49, 0x6d, 0x0c, 0x1b, 0x57, 0x99, 0x9a, 0x51, 0x09, 0x1e, 0xa5, 0x2b, 0x41, 0x65, - 0xfb, 0xf3, 0x2c, 0xeb, 0xb2, 0x45, 0xa6, 0xaa, 0x46, 0x66, 0x92, 0xfc, 0xd5, 0x80, 0xc2, 0x11, - 0x75, 0x22, 0x2a, 0x3e, 0x6a, 0x8e, 0xec, 0x9c, 0xcb, 0x91, 0x7a, 0xf6, 0xd8, 0x2d, 0x5f, 0x5d, - 0x48, 0x91, 0x1a, 0x94, 0xbc, 0x40, 0xd0, 0x28, 0x20, 0xbe, 0xca, 0x91, 0x12, 0x9e, 0xed, 0xb3, - 0xb3, 0xdc, 0x80, 0x42, 0x3c, 0x97, 0xde, 0xb5, 0x01, 0xf1, 0xab, 0x17, 0x0d, 0xc8, 0x54, 0xf2, - 0xdf, 0x06, 0x94, 0x92, 0xf6, 0xf8, 0x51, 0xd5, 0xbc, 0x30, 0xe7, 0xe5, 0xff, 0xe7, 0x39, 0x0f, - 0x81, 0x39, 0xf0, 0x02, 0x3d, 0x91, 0x62, 0xb5, 0x46, 0x2d, 0x28, 0x86, 0x64, 0xe2, 0x33, 0xe2, - 0xea, 0xb2, 0xbc, 0xb6, 0xf0, 0x9b, 0x48, 0x3b, 0x98, 0xe0, 0x04, 0xb4, 0xbb, 0x76, 0x72, 0xda, - 0x5c, 0x85, 0x6a, 0xda, 0xf2, 0xd7, 0x46, 0xf3, 0x1f, 0x06, 0x94, 0x0f, 0xfe, 0x20, 0x68, 0xa0, - 0xa6, 0x8f, 0xff, 0x4b, 0xe3, 0x37, 0x17, 0x7f, 0x37, 0x29, 0x9f, 0xfb, 0x49, 0x24, 0xeb, 0xa3, - 0x76, 0xac, 0xf7, 0x1f, 0xea, 0xf7, 0xfe, 0xf9, 0xa1, 0x7e, 0xef, 0xed, 0xb4, 0x6e, 0xbc, 0x9f, - 0xd6, 0x8d, 0xbf, 0x4f, 0xeb, 0xc6, 0xbf, 0xa6, 0x75, 0xa3, 0x57, 0x50, 0xfe, 0xf9, 0xd1, 0x7f, - 0x03, 0x00, 0x00, 0xff, 0xff, 0xbc, 0x6b, 0x9b, 0xd8, 0xfe, 0x13, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/objects.proto b/vendor/github.com/docker/swarmkit/api/objects.proto index 2211395bf5e92..4baf58af0a192 100644 --- a/vendor/github.com/docker/swarmkit/api/objects.proto +++ b/vendor/github.com/docker/swarmkit/api/objects.proto @@ -81,6 +81,10 @@ message Node { // endpoint on the node to be used for load balancing. Each overlay // network, including ingress network, will have an NetworkAttachment. repeated NetworkAttachment attachments = 10; + + // VXLANUDPPort specifies the UDP port for VXLAN traffic. + // This information is passed from cluster object to individual nodes. + uint32 VXLANUDPPort = 11; } message Service { @@ -122,6 +126,20 @@ message Service { // UpdateStatus contains the status of an update, if one is in // progress. UpdateStatus update_status = 5; + + // JobStatus contains the status of a Service that is in one of the Job + // modes. It is absent on Replicated and Global services. + JobStatus job_status = 12; + + // PendingDelete indicates that this service's deletion has been requested. + // Services, as well as all service-level resources, can only be deleted + // after all of the service's containers have properly shut down. + // When a user requests a deletion, we just flip this flag + // the deallocator will take it from there - it will start monitoring + // this service's tasks, and proceed to delete the service itself (and + // potentially its associated resources also marked for deletion) when + // all of its tasks are gone + bool pending_delete = 7; } // Endpoint specified all the network parameters required to @@ -247,6 +265,10 @@ message Task { Driver log_driver = 13; repeated GenericResource assigned_generic_resources = 15; + + // JobIteration is the iteration number of the Job-mode Service that this + // task belongs to. + Version job_iteration = 16; } // NetworkAttachment specifies the network parameters of attachment to @@ -292,6 +314,20 @@ message Network { // Runtime state of IPAM options. This may not reflect the // ipam options from NetworkSpec. IPAMOptions ipam = 5 [(gogoproto.customname) = "IPAM"]; + + // PendingDelete indicates that this network's deletion has been requested. + // Services, as well as all service-level resources, can only be deleted + // after all the service's containers have properly shut down + // when a user requests a deletion, we just flip this flag + // the deallocator will take it from there + // PendingDelete indicates that this network's deletion has been requested. + // Services, as well as all service-level resources, can only be deleted + // after all of the service's containers have properly shut down. + // When a user requests a deletion of this network, we just flip this flag + // the deallocator will take it from there - it will start monitoring + // the services that still use this service, and proceed to delete + // this network when all of these services are gone + bool pending_delete = 6; } // Cluster provides global cluster settings. @@ -351,6 +387,9 @@ message Cluster { // This flag specifies the default subnet size of global scope networks by giving // the length of the subnet masks for every such network uint32 subnetSize = 12; + + // VXLANUDPPort specifies the UDP port for VXLAN traffic. + uint32 VXLANUDPPort = 13; } // Secret represents a secret that should be passed to a container or a node, diff --git a/vendor/github.com/docker/swarmkit/api/raft.pb.go b/vendor/github.com/docker/swarmkit/api/raft.pb.go index a32a6001b92fd..1c2537f5288e9 100644 --- a/vendor/github.com/docker/swarmkit/api/raft.pb.go +++ b/vendor/github.com/docker/swarmkit/api/raft.pb.go @@ -3,36 +3,37 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import raftpb "github.com/coreos/etcd/raft/raftpb" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" -// skipping weak import docker_protobuf_plugin "github.com/docker/swarmkit/protobuf/plugin" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import raftselector "github.com/docker/swarmkit/manager/raftselector" -import codes "google.golang.org/grpc/codes" -import status "google.golang.org/grpc/status" -import metadata "google.golang.org/grpc/metadata" -import peer "google.golang.org/grpc/peer" -import rafttime "time" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + context "context" + fmt "fmt" + raftpb "github.com/coreos/etcd/raft/raftpb" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + raftselector "github.com/docker/swarmkit/manager/raftselector" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + metadata "google.golang.org/grpc/metadata" + peer "google.golang.org/grpc/peer" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + rafttime "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + // StoreActionKind defines the operation to take on the store for the target of // a storage action. type StoreActionKind int32 @@ -50,6 +51,7 @@ var StoreActionKind_name = map[int32]string{ 2: "STORE_ACTION_UPDATE", 3: "STORE_ACTION_REMOVE", } + var StoreActionKind_value = map[string]int32{ "UNKNOWN": 0, "STORE_ACTION_CREATE": 1, @@ -60,7 +62,10 @@ var StoreActionKind_value = map[string]int32{ func (x StoreActionKind) String() string { return proto.EnumName(StoreActionKind_name, int32(x)) } -func (StoreActionKind) EnumDescriptor() ([]byte, []int) { return fileDescriptorRaft, []int{0} } + +func (StoreActionKind) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{0} +} type RaftMember struct { // RaftID specifies the internal ID used by the manager in a raft context, it can never be modified @@ -71,112 +76,448 @@ type RaftMember struct { // Addr specifies the address of the member Addr string `protobuf:"bytes,3,opt,name=addr,proto3" json:"addr,omitempty"` // Status provides the current status of the manager from the perspective of another manager. - Status RaftMemberStatus `protobuf:"bytes,4,opt,name=status" json:"status"` + Status RaftMemberStatus `protobuf:"bytes,4,opt,name=status,proto3" json:"status"` +} + +func (m *RaftMember) Reset() { *m = RaftMember{} } +func (*RaftMember) ProtoMessage() {} +func (*RaftMember) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{0} +} +func (m *RaftMember) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RaftMember) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RaftMember.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RaftMember) XXX_Merge(src proto.Message) { + xxx_messageInfo_RaftMember.Merge(m, src) +} +func (m *RaftMember) XXX_Size() int { + return m.Size() +} +func (m *RaftMember) XXX_DiscardUnknown() { + xxx_messageInfo_RaftMember.DiscardUnknown(m) } -func (m *RaftMember) Reset() { *m = RaftMember{} } -func (*RaftMember) ProtoMessage() {} -func (*RaftMember) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{0} } +var xxx_messageInfo_RaftMember proto.InternalMessageInfo type JoinRequest struct { // Addr specifies the address of the member Addr string `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"` } -func (m *JoinRequest) Reset() { *m = JoinRequest{} } -func (*JoinRequest) ProtoMessage() {} -func (*JoinRequest) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{1} } +func (m *JoinRequest) Reset() { *m = JoinRequest{} } +func (*JoinRequest) ProtoMessage() {} +func (*JoinRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{1} +} +func (m *JoinRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *JoinRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_JoinRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *JoinRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_JoinRequest.Merge(m, src) +} +func (m *JoinRequest) XXX_Size() int { + return m.Size() +} +func (m *JoinRequest) XXX_DiscardUnknown() { + xxx_messageInfo_JoinRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_JoinRequest proto.InternalMessageInfo type JoinResponse struct { // RaftID is the ID assigned to the new member. RaftID uint64 `protobuf:"varint,1,opt,name=raft_id,json=raftId,proto3" json:"raft_id,omitempty"` // Members is the membership set of the cluster. - Members []*RaftMember `protobuf:"bytes,2,rep,name=members" json:"members,omitempty"` + Members []*RaftMember `protobuf:"bytes,2,rep,name=members,proto3" json:"members,omitempty"` // RemovedMembers is a list of members that have been removed from // the cluster, so the new node can avoid communicating with them. - RemovedMembers []uint64 `protobuf:"varint,3,rep,name=removed_members,json=removedMembers" json:"removed_members,omitempty"` + RemovedMembers []uint64 `protobuf:"varint,3,rep,name=removed_members,json=removedMembers,proto3" json:"removed_members,omitempty"` +} + +func (m *JoinResponse) Reset() { *m = JoinResponse{} } +func (*JoinResponse) ProtoMessage() {} +func (*JoinResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{2} +} +func (m *JoinResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *JoinResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_JoinResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *JoinResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_JoinResponse.Merge(m, src) +} +func (m *JoinResponse) XXX_Size() int { + return m.Size() +} +func (m *JoinResponse) XXX_DiscardUnknown() { + xxx_messageInfo_JoinResponse.DiscardUnknown(m) } -func (m *JoinResponse) Reset() { *m = JoinResponse{} } -func (*JoinResponse) ProtoMessage() {} -func (*JoinResponse) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{2} } +var xxx_messageInfo_JoinResponse proto.InternalMessageInfo type LeaveRequest struct { - Node *RaftMember `protobuf:"bytes,1,opt,name=node" json:"node,omitempty"` + Node *RaftMember `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` +} + +func (m *LeaveRequest) Reset() { *m = LeaveRequest{} } +func (*LeaveRequest) ProtoMessage() {} +func (*LeaveRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{3} +} +func (m *LeaveRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LeaveRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LeaveRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LeaveRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_LeaveRequest.Merge(m, src) +} +func (m *LeaveRequest) XXX_Size() int { + return m.Size() +} +func (m *LeaveRequest) XXX_DiscardUnknown() { + xxx_messageInfo_LeaveRequest.DiscardUnknown(m) } -func (m *LeaveRequest) Reset() { *m = LeaveRequest{} } -func (*LeaveRequest) ProtoMessage() {} -func (*LeaveRequest) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{3} } +var xxx_messageInfo_LeaveRequest proto.InternalMessageInfo type LeaveResponse struct { } -func (m *LeaveResponse) Reset() { *m = LeaveResponse{} } -func (*LeaveResponse) ProtoMessage() {} -func (*LeaveResponse) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{4} } +func (m *LeaveResponse) Reset() { *m = LeaveResponse{} } +func (*LeaveResponse) ProtoMessage() {} +func (*LeaveResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{4} +} +func (m *LeaveResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LeaveResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LeaveResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LeaveResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_LeaveResponse.Merge(m, src) +} +func (m *LeaveResponse) XXX_Size() int { + return m.Size() +} +func (m *LeaveResponse) XXX_DiscardUnknown() { + xxx_messageInfo_LeaveResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_LeaveResponse proto.InternalMessageInfo type ProcessRaftMessageRequest struct { - Message *raftpb.Message `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` + Message *raftpb.Message `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (m *ProcessRaftMessageRequest) Reset() { *m = ProcessRaftMessageRequest{} } +func (*ProcessRaftMessageRequest) ProtoMessage() {} +func (*ProcessRaftMessageRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{5} +} +func (m *ProcessRaftMessageRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProcessRaftMessageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProcessRaftMessageRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ProcessRaftMessageRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProcessRaftMessageRequest.Merge(m, src) +} +func (m *ProcessRaftMessageRequest) XXX_Size() int { + return m.Size() +} +func (m *ProcessRaftMessageRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ProcessRaftMessageRequest.DiscardUnknown(m) } -func (m *ProcessRaftMessageRequest) Reset() { *m = ProcessRaftMessageRequest{} } -func (*ProcessRaftMessageRequest) ProtoMessage() {} -func (*ProcessRaftMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{5} } +var xxx_messageInfo_ProcessRaftMessageRequest proto.InternalMessageInfo type ProcessRaftMessageResponse struct { } -func (m *ProcessRaftMessageResponse) Reset() { *m = ProcessRaftMessageResponse{} } -func (*ProcessRaftMessageResponse) ProtoMessage() {} -func (*ProcessRaftMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{6} } +func (m *ProcessRaftMessageResponse) Reset() { *m = ProcessRaftMessageResponse{} } +func (*ProcessRaftMessageResponse) ProtoMessage() {} +func (*ProcessRaftMessageResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{6} +} +func (m *ProcessRaftMessageResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProcessRaftMessageResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ProcessRaftMessageResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ProcessRaftMessageResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProcessRaftMessageResponse.Merge(m, src) +} +func (m *ProcessRaftMessageResponse) XXX_Size() int { + return m.Size() +} +func (m *ProcessRaftMessageResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ProcessRaftMessageResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ProcessRaftMessageResponse proto.InternalMessageInfo // Raft message streaming request. type StreamRaftMessageRequest struct { - Message *raftpb.Message `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` + Message *raftpb.Message `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (m *StreamRaftMessageRequest) Reset() { *m = StreamRaftMessageRequest{} } +func (*StreamRaftMessageRequest) ProtoMessage() {} +func (*StreamRaftMessageRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{7} +} +func (m *StreamRaftMessageRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StreamRaftMessageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StreamRaftMessageRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StreamRaftMessageRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_StreamRaftMessageRequest.Merge(m, src) +} +func (m *StreamRaftMessageRequest) XXX_Size() int { + return m.Size() +} +func (m *StreamRaftMessageRequest) XXX_DiscardUnknown() { + xxx_messageInfo_StreamRaftMessageRequest.DiscardUnknown(m) } -func (m *StreamRaftMessageRequest) Reset() { *m = StreamRaftMessageRequest{} } -func (*StreamRaftMessageRequest) ProtoMessage() {} -func (*StreamRaftMessageRequest) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{7} } +var xxx_messageInfo_StreamRaftMessageRequest proto.InternalMessageInfo // Raft message streaming response. type StreamRaftMessageResponse struct { } -func (m *StreamRaftMessageResponse) Reset() { *m = StreamRaftMessageResponse{} } -func (*StreamRaftMessageResponse) ProtoMessage() {} -func (*StreamRaftMessageResponse) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{8} } +func (m *StreamRaftMessageResponse) Reset() { *m = StreamRaftMessageResponse{} } +func (*StreamRaftMessageResponse) ProtoMessage() {} +func (*StreamRaftMessageResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{8} +} +func (m *StreamRaftMessageResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StreamRaftMessageResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StreamRaftMessageResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StreamRaftMessageResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_StreamRaftMessageResponse.Merge(m, src) +} +func (m *StreamRaftMessageResponse) XXX_Size() int { + return m.Size() +} +func (m *StreamRaftMessageResponse) XXX_DiscardUnknown() { + xxx_messageInfo_StreamRaftMessageResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_StreamRaftMessageResponse proto.InternalMessageInfo type ResolveAddressRequest struct { // raft_id is the ID to resolve to an address. RaftID uint64 `protobuf:"varint,1,opt,name=raft_id,json=raftId,proto3" json:"raft_id,omitempty"` } -func (m *ResolveAddressRequest) Reset() { *m = ResolveAddressRequest{} } -func (*ResolveAddressRequest) ProtoMessage() {} -func (*ResolveAddressRequest) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{9} } +func (m *ResolveAddressRequest) Reset() { *m = ResolveAddressRequest{} } +func (*ResolveAddressRequest) ProtoMessage() {} +func (*ResolveAddressRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{9} +} +func (m *ResolveAddressRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResolveAddressRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResolveAddressRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResolveAddressRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResolveAddressRequest.Merge(m, src) +} +func (m *ResolveAddressRequest) XXX_Size() int { + return m.Size() +} +func (m *ResolveAddressRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ResolveAddressRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ResolveAddressRequest proto.InternalMessageInfo type ResolveAddressResponse struct { // Addr specifies the address of the member Addr string `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"` } -func (m *ResolveAddressResponse) Reset() { *m = ResolveAddressResponse{} } -func (*ResolveAddressResponse) ProtoMessage() {} -func (*ResolveAddressResponse) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{10} } +func (m *ResolveAddressResponse) Reset() { *m = ResolveAddressResponse{} } +func (*ResolveAddressResponse) ProtoMessage() {} +func (*ResolveAddressResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{10} +} +func (m *ResolveAddressResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResolveAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResolveAddressResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResolveAddressResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResolveAddressResponse.Merge(m, src) +} +func (m *ResolveAddressResponse) XXX_Size() int { + return m.Size() +} +func (m *ResolveAddressResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ResolveAddressResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ResolveAddressResponse proto.InternalMessageInfo // Contains one of many protobuf encoded objects to replicate // over the raft backend with a request ID to track when the // action is effectively applied type InternalRaftRequest struct { ID uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Action []StoreAction `protobuf:"bytes,2,rep,name=action" json:"action"` + Action []StoreAction `protobuf:"bytes,2,rep,name=action,proto3" json:"action"` +} + +func (m *InternalRaftRequest) Reset() { *m = InternalRaftRequest{} } +func (*InternalRaftRequest) ProtoMessage() {} +func (*InternalRaftRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{11} +} +func (m *InternalRaftRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *InternalRaftRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_InternalRaftRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *InternalRaftRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_InternalRaftRequest.Merge(m, src) +} +func (m *InternalRaftRequest) XXX_Size() int { + return m.Size() +} +func (m *InternalRaftRequest) XXX_DiscardUnknown() { + xxx_messageInfo_InternalRaftRequest.DiscardUnknown(m) } -func (m *InternalRaftRequest) Reset() { *m = InternalRaftRequest{} } -func (*InternalRaftRequest) ProtoMessage() {} -func (*InternalRaftRequest) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{11} } +var xxx_messageInfo_InternalRaftRequest proto.InternalMessageInfo // StoreAction defines a target and operation to apply on the storage system. type StoreAction struct { @@ -194,9 +535,37 @@ type StoreAction struct { Target isStoreAction_Target `protobuf_oneof:"target"` } -func (m *StoreAction) Reset() { *m = StoreAction{} } -func (*StoreAction) ProtoMessage() {} -func (*StoreAction) Descriptor() ([]byte, []int) { return fileDescriptorRaft, []int{12} } +func (m *StoreAction) Reset() { *m = StoreAction{} } +func (*StoreAction) ProtoMessage() {} +func (*StoreAction) Descriptor() ([]byte, []int) { + return fileDescriptor_d2c32e1e3c930c15, []int{12} +} +func (m *StoreAction) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StoreAction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StoreAction.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StoreAction) XXX_Merge(src proto.Message) { + xxx_messageInfo_StoreAction.Merge(m, src) +} +func (m *StoreAction) XXX_Size() int { + return m.Size() +} +func (m *StoreAction) XXX_DiscardUnknown() { + xxx_messageInfo_StoreAction.DiscardUnknown(m) +} + +var xxx_messageInfo_StoreAction proto.InternalMessageInfo type isStoreAction_Target interface { isStoreAction_Target() @@ -205,31 +574,31 @@ type isStoreAction_Target interface { } type StoreAction_Node struct { - Node *Node `protobuf:"bytes,2,opt,name=node,oneof"` + Node *Node `protobuf:"bytes,2,opt,name=node,proto3,oneof" json:"node,omitempty"` } type StoreAction_Service struct { - Service *Service `protobuf:"bytes,3,opt,name=service,oneof"` + Service *Service `protobuf:"bytes,3,opt,name=service,proto3,oneof" json:"service,omitempty"` } type StoreAction_Task struct { - Task *Task `protobuf:"bytes,4,opt,name=task,oneof"` + Task *Task `protobuf:"bytes,4,opt,name=task,proto3,oneof" json:"task,omitempty"` } type StoreAction_Network struct { - Network *Network `protobuf:"bytes,5,opt,name=network,oneof"` + Network *Network `protobuf:"bytes,5,opt,name=network,proto3,oneof" json:"network,omitempty"` } type StoreAction_Cluster struct { - Cluster *Cluster `protobuf:"bytes,6,opt,name=cluster,oneof"` + Cluster *Cluster `protobuf:"bytes,6,opt,name=cluster,proto3,oneof" json:"cluster,omitempty"` } type StoreAction_Secret struct { - Secret *Secret `protobuf:"bytes,7,opt,name=secret,oneof"` + Secret *Secret `protobuf:"bytes,7,opt,name=secret,proto3,oneof" json:"secret,omitempty"` } type StoreAction_Resource struct { - Resource *Resource `protobuf:"bytes,8,opt,name=resource,oneof"` + Resource *Resource `protobuf:"bytes,8,opt,name=resource,proto3,oneof" json:"resource,omitempty"` } type StoreAction_Extension struct { - Extension *Extension `protobuf:"bytes,9,opt,name=extension,oneof"` + Extension *Extension `protobuf:"bytes,9,opt,name=extension,proto3,oneof" json:"extension,omitempty"` } type StoreAction_Config struct { - Config *Config `protobuf:"bytes,10,opt,name=config,oneof"` + Config *Config `protobuf:"bytes,10,opt,name=config,proto3,oneof" json:"config,omitempty"` } func (*StoreAction_Node) isStoreAction_Target() {} @@ -312,9 +681,9 @@ func (m *StoreAction) GetConfig() *Config { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*StoreAction) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _StoreAction_OneofMarshaler, _StoreAction_OneofUnmarshaler, _StoreAction_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*StoreAction) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*StoreAction_Node)(nil), (*StoreAction_Service)(nil), (*StoreAction_Task)(nil), @@ -327,199 +696,8 @@ func (*StoreAction) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) e } } -func _StoreAction_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*StoreAction) - // target - switch x := m.Target.(type) { - case *StoreAction_Node: - _ = b.EncodeVarint(2<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Node); err != nil { - return err - } - case *StoreAction_Service: - _ = b.EncodeVarint(3<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Service); err != nil { - return err - } - case *StoreAction_Task: - _ = b.EncodeVarint(4<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Task); err != nil { - return err - } - case *StoreAction_Network: - _ = b.EncodeVarint(5<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Network); err != nil { - return err - } - case *StoreAction_Cluster: - _ = b.EncodeVarint(6<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Cluster); err != nil { - return err - } - case *StoreAction_Secret: - _ = b.EncodeVarint(7<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Secret); err != nil { - return err - } - case *StoreAction_Resource: - _ = b.EncodeVarint(8<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Resource); err != nil { - return err - } - case *StoreAction_Extension: - _ = b.EncodeVarint(9<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Extension); err != nil { - return err - } - case *StoreAction_Config: - _ = b.EncodeVarint(10<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Config); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("StoreAction.Target has unexpected type %T", x) - } - return nil -} - -func _StoreAction_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*StoreAction) - switch tag { - case 2: // target.node - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Node) - err := b.DecodeMessage(msg) - m.Target = &StoreAction_Node{msg} - return true, err - case 3: // target.service - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Service) - err := b.DecodeMessage(msg) - m.Target = &StoreAction_Service{msg} - return true, err - case 4: // target.task - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Task) - err := b.DecodeMessage(msg) - m.Target = &StoreAction_Task{msg} - return true, err - case 5: // target.network - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Network) - err := b.DecodeMessage(msg) - m.Target = &StoreAction_Network{msg} - return true, err - case 6: // target.cluster - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Cluster) - err := b.DecodeMessage(msg) - m.Target = &StoreAction_Cluster{msg} - return true, err - case 7: // target.secret - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Secret) - err := b.DecodeMessage(msg) - m.Target = &StoreAction_Secret{msg} - return true, err - case 8: // target.resource - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Resource) - err := b.DecodeMessage(msg) - m.Target = &StoreAction_Resource{msg} - return true, err - case 9: // target.extension - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Extension) - err := b.DecodeMessage(msg) - m.Target = &StoreAction_Extension{msg} - return true, err - case 10: // target.config - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Config) - err := b.DecodeMessage(msg) - m.Target = &StoreAction_Config{msg} - return true, err - default: - return false, nil - } -} - -func _StoreAction_OneofSizer(msg proto.Message) (n int) { - m := msg.(*StoreAction) - // target - switch x := m.Target.(type) { - case *StoreAction_Node: - s := proto.Size(x.Node) - n += proto.SizeVarint(2<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *StoreAction_Service: - s := proto.Size(x.Service) - n += proto.SizeVarint(3<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *StoreAction_Task: - s := proto.Size(x.Task) - n += proto.SizeVarint(4<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *StoreAction_Network: - s := proto.Size(x.Network) - n += proto.SizeVarint(5<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *StoreAction_Cluster: - s := proto.Size(x.Cluster) - n += proto.SizeVarint(6<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *StoreAction_Secret: - s := proto.Size(x.Secret) - n += proto.SizeVarint(7<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *StoreAction_Resource: - s := proto.Size(x.Resource) - n += proto.SizeVarint(8<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *StoreAction_Extension: - s := proto.Size(x.Extension) - n += proto.SizeVarint(9<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *StoreAction_Config: - s := proto.Size(x.Config) - n += proto.SizeVarint(10<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - func init() { + proto.RegisterEnum("docker.swarmkit.v1.StoreActionKind", StoreActionKind_name, StoreActionKind_value) proto.RegisterType((*RaftMember)(nil), "docker.swarmkit.v1.RaftMember") proto.RegisterType((*JoinRequest)(nil), "docker.swarmkit.v1.JoinRequest") proto.RegisterType((*JoinResponse)(nil), "docker.swarmkit.v1.JoinResponse") @@ -533,7 +711,79 @@ func init() { proto.RegisterType((*ResolveAddressResponse)(nil), "docker.swarmkit.v1.ResolveAddressResponse") proto.RegisterType((*InternalRaftRequest)(nil), "docker.swarmkit.v1.InternalRaftRequest") proto.RegisterType((*StoreAction)(nil), "docker.swarmkit.v1.StoreAction") - proto.RegisterEnum("docker.swarmkit.v1.StoreActionKind", StoreActionKind_name, StoreActionKind_value) +} + +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/raft.proto", fileDescriptor_d2c32e1e3c930c15) +} + +var fileDescriptor_d2c32e1e3c930c15 = []byte{ + // 1028 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0x41, 0x73, 0xdb, 0x44, + 0x14, 0xc7, 0x25, 0x5b, 0x75, 0x9a, 0x97, 0x36, 0x09, 0x5b, 0x12, 0x14, 0xb5, 0x28, 0xae, 0xda, + 0x19, 0x9c, 0x90, 0xc8, 0x83, 0x61, 0xa6, 0x4c, 0xa1, 0x87, 0x38, 0xf1, 0x4c, 0x4c, 0x5a, 0xa7, + 0xa3, 0x24, 0xd0, 0x5b, 0x90, 0xa5, 0x8d, 0x2b, 0x1c, 0x6b, 0xcd, 0xee, 0x3a, 0x81, 0x0b, 0xd3, + 0x23, 0xe4, 0xc4, 0x0d, 0x86, 0x99, 0x0e, 0x07, 0x38, 0xf7, 0x03, 0xf0, 0x01, 0x98, 0x0c, 0xa7, + 0xde, 0xe8, 0x29, 0x43, 0x9d, 0x3b, 0x7c, 0x05, 0x66, 0x57, 0x52, 0x12, 0x6c, 0xd9, 0xf1, 0x81, + 0x4b, 0xb2, 0xa3, 0xfd, 0xfd, 0xdf, 0xff, 0xed, 0xee, 0xdb, 0xb7, 0x86, 0x85, 0x46, 0xc0, 0x9f, + 0x76, 0xea, 0xb6, 0x47, 0x5a, 0x45, 0x9f, 0x78, 0x4d, 0x4c, 0x8b, 0xec, 0xd0, 0xa5, 0xad, 0x66, + 0xc0, 0x8b, 0x6e, 0x3b, 0x28, 0x52, 0x77, 0x8f, 0xdb, 0x6d, 0x4a, 0x38, 0x41, 0x28, 0x9a, 0xb7, + 0x93, 0x79, 0xfb, 0xe0, 0x3d, 0x63, 0xe9, 0x12, 0x39, 0xa9, 0x7f, 0x81, 0x3d, 0xce, 0xa2, 0x08, + 0xc6, 0xe2, 0x25, 0x34, 0xff, 0xba, 0x8d, 0x13, 0x76, 0xf9, 0x02, 0xeb, 0x11, 0x8a, 0x09, 0x2b, + 0x62, 0xee, 0xf9, 0x32, 0x21, 0xf9, 0xa7, 0x5d, 0xbf, 0x90, 0x9c, 0xf1, 0x66, 0x83, 0x34, 0x88, + 0x1c, 0x16, 0xc5, 0x28, 0xfe, 0x7a, 0x6f, 0x88, 0xa1, 0x24, 0xea, 0x9d, 0xbd, 0x62, 0x7b, 0xbf, + 0xd3, 0x08, 0xc2, 0xf8, 0x5f, 0x24, 0xb4, 0x5e, 0xa8, 0x00, 0x8e, 0xbb, 0xc7, 0x1f, 0xe1, 0x56, + 0x1d, 0x53, 0x74, 0x07, 0xc6, 0x84, 0xd7, 0x6e, 0xe0, 0xeb, 0x6a, 0x5e, 0x2d, 0x68, 0x65, 0xe8, + 0x9e, 0xcc, 0xe7, 0x04, 0x50, 0x5d, 0x73, 0x72, 0x62, 0xaa, 0xea, 0x0b, 0x28, 0x24, 0x3e, 0x16, + 0x50, 0x26, 0xaf, 0x16, 0xc6, 0x23, 0xa8, 0x46, 0x7c, 0x2c, 0x20, 0x31, 0x55, 0xf5, 0x11, 0x02, + 0xcd, 0xf5, 0x7d, 0xaa, 0x67, 0x05, 0xe1, 0xc8, 0x31, 0x2a, 0x43, 0x8e, 0x71, 0x97, 0x77, 0x98, + 0xae, 0xe5, 0xd5, 0xc2, 0x44, 0xe9, 0xae, 0xdd, 0xbf, 0xd3, 0xf6, 0x79, 0x36, 0x5b, 0x92, 0x2d, + 0x6b, 0xc7, 0x27, 0xf3, 0x8a, 0x13, 0x2b, 0xad, 0xdb, 0x30, 0xf1, 0x09, 0x09, 0x42, 0x07, 0x7f, + 0xd9, 0xc1, 0x8c, 0x9f, 0xd9, 0xa8, 0xe7, 0x36, 0xd6, 0x4f, 0x2a, 0x5c, 0x8b, 0x18, 0xd6, 0x26, + 0x21, 0xc3, 0xa3, 0xad, 0xea, 0x43, 0x18, 0x6b, 0x49, 0x5b, 0xa6, 0x67, 0xf2, 0xd9, 0xc2, 0x44, + 0xc9, 0x1c, 0x9e, 0x9d, 0x93, 0xe0, 0xe8, 0x5d, 0x98, 0xa2, 0xb8, 0x45, 0x0e, 0xb0, 0xbf, 0x9b, + 0x44, 0xc8, 0xe6, 0xb3, 0x05, 0xad, 0x9c, 0x99, 0x56, 0x9c, 0xc9, 0x78, 0x2a, 0x12, 0x31, 0xab, + 0x0c, 0xd7, 0x1e, 0x62, 0xf7, 0x00, 0x27, 0x0b, 0x28, 0x81, 0x26, 0x76, 0x4c, 0x26, 0x76, 0xb9, + 0xa7, 0x64, 0xad, 0x29, 0xb8, 0x1e, 0xc7, 0x88, 0x16, 0x68, 0x3d, 0x84, 0xb9, 0xc7, 0x94, 0x78, + 0x98, 0xb1, 0x88, 0x65, 0xcc, 0x6d, 0x9c, 0x39, 0x2c, 0x88, 0x85, 0xc9, 0x2f, 0xb1, 0xc9, 0x94, + 0x1d, 0x95, 0x95, 0x9d, 0x80, 0xc9, 0xfc, 0x7d, 0xed, 0xd9, 0x0f, 0x96, 0x62, 0xdd, 0x02, 0x23, + 0x2d, 0x5a, 0xec, 0xb5, 0x01, 0xfa, 0x16, 0xa7, 0xd8, 0x6d, 0xfd, 0x1f, 0x56, 0x37, 0x61, 0x2e, + 0x25, 0x58, 0xec, 0xf4, 0x31, 0xcc, 0x38, 0x98, 0x91, 0xfd, 0x03, 0xbc, 0xe2, 0xfb, 0x54, 0xa4, + 0x13, 0xdb, 0x8c, 0x72, 0x9e, 0xd6, 0x12, 0xcc, 0xf6, 0xaa, 0xe3, 0x72, 0x48, 0xab, 0x99, 0x7d, + 0xb8, 0x51, 0x0d, 0x39, 0xa6, 0xa1, 0xbb, 0x2f, 0xe2, 0x24, 0x4e, 0xb3, 0x90, 0x39, 0x33, 0xc9, + 0x75, 0x4f, 0xe6, 0x33, 0xd5, 0x35, 0x27, 0x13, 0xf8, 0xe8, 0x01, 0xe4, 0x5c, 0x8f, 0x07, 0x24, + 0x8c, 0x6b, 0x65, 0x3e, 0xed, 0xdc, 0xb6, 0x38, 0xa1, 0x78, 0x45, 0x62, 0x49, 0x11, 0x47, 0x22, + 0xeb, 0x77, 0x0d, 0x26, 0x2e, 0xcc, 0xa2, 0x8f, 0xce, 0xc2, 0x09, 0xab, 0xc9, 0xd2, 0x9d, 0x4b, + 0xc2, 0x6d, 0x04, 0xa1, 0x9f, 0x04, 0x43, 0x76, 0x5c, 0x41, 0x19, 0xb9, 0xe3, 0x7a, 0x9a, 0x54, + 0xdc, 0xcd, 0x75, 0x25, 0xaa, 0x1e, 0x74, 0x0f, 0xc6, 0x18, 0xa6, 0x07, 0x81, 0x87, 0xe5, 0xe5, + 0x9c, 0x28, 0xdd, 0x4c, 0x75, 0x8b, 0x90, 0x75, 0xc5, 0x49, 0x68, 0x61, 0xc4, 0x5d, 0xd6, 0x8c, + 0x2f, 0x6f, 0xaa, 0xd1, 0xb6, 0xcb, 0x9a, 0xc2, 0x48, 0x70, 0xc2, 0x28, 0xc4, 0xfc, 0x90, 0xd0, + 0xa6, 0x7e, 0x65, 0xb0, 0x51, 0x2d, 0x42, 0x84, 0x51, 0x4c, 0x0b, 0xa1, 0xb7, 0xdf, 0x61, 0x1c, + 0x53, 0x3d, 0x37, 0x58, 0xb8, 0x1a, 0x21, 0x42, 0x18, 0xd3, 0xe8, 0x03, 0xc8, 0x31, 0xec, 0x51, + 0xcc, 0xf5, 0x31, 0xa9, 0x33, 0xd2, 0x57, 0x26, 0x88, 0x75, 0xd1, 0x52, 0xe4, 0x08, 0xdd, 0x87, + 0xab, 0x14, 0x33, 0xd2, 0xa1, 0x1e, 0xd6, 0xaf, 0x4a, 0xdd, 0xad, 0xd4, 0x6b, 0x18, 0x33, 0xeb, + 0x8a, 0x73, 0xc6, 0xa3, 0x07, 0x30, 0x8e, 0xbf, 0xe2, 0x38, 0x64, 0xe2, 0xf0, 0xc6, 0xa5, 0xf8, + 0xed, 0x34, 0x71, 0x25, 0x81, 0xd6, 0x15, 0xe7, 0x5c, 0x21, 0x12, 0xf6, 0x48, 0xb8, 0x17, 0x34, + 0x74, 0x18, 0x9c, 0xf0, 0xaa, 0x24, 0x44, 0xc2, 0x11, 0x5b, 0xbe, 0x0a, 0x39, 0xee, 0xd2, 0x06, + 0xe6, 0x8b, 0xff, 0xa8, 0x30, 0xd5, 0x53, 0x17, 0xe8, 0x1d, 0x18, 0xdb, 0xa9, 0x6d, 0xd4, 0x36, + 0x3f, 0xab, 0x4d, 0x2b, 0x86, 0x71, 0xf4, 0x3c, 0x3f, 0xdb, 0x43, 0xec, 0x84, 0xcd, 0x90, 0x1c, + 0x86, 0xa8, 0x04, 0x37, 0xb6, 0xb6, 0x37, 0x9d, 0xca, 0xee, 0xca, 0xea, 0x76, 0x75, 0xb3, 0xb6, + 0xbb, 0xea, 0x54, 0x56, 0xb6, 0x2b, 0xd3, 0xaa, 0x31, 0x77, 0xf4, 0x3c, 0x3f, 0xd3, 0x23, 0x5a, + 0xa5, 0xd8, 0xe5, 0xb8, 0x4f, 0xb3, 0xf3, 0x78, 0x4d, 0x68, 0x32, 0xa9, 0x9a, 0x9d, 0xb6, 0x9f, + 0xa6, 0x71, 0x2a, 0x8f, 0x36, 0x3f, 0xad, 0x4c, 0x67, 0x53, 0x35, 0x8e, 0x6c, 0x97, 0xc6, 0x5b, + 0xdf, 0xfe, 0x62, 0x2a, 0xbf, 0xfd, 0x6a, 0xf6, 0xae, 0xae, 0xf4, 0x73, 0x16, 0x34, 0x71, 0x43, + 0xd1, 0x91, 0x0a, 0xa8, 0xbf, 0x4d, 0xa1, 0xe5, 0xb4, 0x1d, 0x1c, 0xd8, 0x1c, 0x0d, 0x7b, 0x54, + 0x3c, 0xee, 0x49, 0x33, 0x7f, 0xbc, 0xf8, 0xfb, 0xc7, 0xcc, 0x14, 0x5c, 0x97, 0xfc, 0x72, 0xcb, + 0x0d, 0xdd, 0x06, 0xa6, 0xe8, 0x3b, 0x15, 0xde, 0xe8, 0x6b, 0x64, 0x68, 0x29, 0xfd, 0x1a, 0xa7, + 0x37, 0x4f, 0x63, 0x79, 0x44, 0x7a, 0x68, 0x26, 0x05, 0x15, 0x7d, 0x03, 0x93, 0xff, 0x6d, 0x7c, + 0x68, 0x61, 0x50, 0x39, 0xf7, 0xb5, 0x56, 0x63, 0x71, 0x14, 0x74, 0x68, 0x06, 0xa5, 0x3f, 0x55, + 0x98, 0x3c, 0x7f, 0xb2, 0xd8, 0xd3, 0xa0, 0x8d, 0x3e, 0x07, 0x4d, 0x3c, 0xc8, 0x28, 0xb5, 0x4d, + 0x5e, 0x78, 0xce, 0x8d, 0xfc, 0x60, 0x60, 0xf8, 0x01, 0x78, 0x70, 0x45, 0x3e, 0x89, 0x28, 0x35, + 0xc2, 0xc5, 0x17, 0xd7, 0xb8, 0x3d, 0x84, 0x18, 0x6a, 0x52, 0xbe, 0x7b, 0xfc, 0xda, 0x54, 0x5e, + 0xbd, 0x36, 0x95, 0x67, 0x5d, 0x53, 0x3d, 0xee, 0x9a, 0xea, 0xcb, 0xae, 0xa9, 0xfe, 0xd5, 0x35, + 0xd5, 0xef, 0x4f, 0x4d, 0xe5, 0xe5, 0xa9, 0xa9, 0xbc, 0x3a, 0x35, 0x95, 0x27, 0xd9, 0x27, 0x5a, + 0x3d, 0x27, 0x7f, 0x5d, 0xbd, 0xff, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc9, 0x42, 0x09, 0xd0, + 0x76, 0x0a, 0x00, 0x00, } type authenticatedWrapperRaftServer struct { @@ -613,7 +863,7 @@ func (m *RaftMember) CopyFrom(src interface{}) { o := src.(*RaftMember) *m = *o - deepcopy.Copy(&m.Status, &o.Status) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Status, &o.Status) } func (m *JoinRequest) Copy() *JoinRequest { @@ -648,7 +898,7 @@ func (m *JoinResponse) CopyFrom(src interface{}) { m.Members = make([]*RaftMember, len(o.Members)) for i := range m.Members { m.Members[i] = &RaftMember{} - deepcopy.Copy(m.Members[i], o.Members[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Members[i], o.Members[i]) } } @@ -674,7 +924,7 @@ func (m *LeaveRequest) CopyFrom(src interface{}) { *m = *o if o.Node != nil { m.Node = &RaftMember{} - deepcopy.Copy(m.Node, o.Node) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Node, o.Node) } } @@ -754,7 +1004,7 @@ func (m *InternalRaftRequest) CopyFrom(src interface{}) { if o.Action != nil { m.Action = make([]StoreAction, len(o.Action)) for i := range m.Action { - deepcopy.Copy(&m.Action[i], &o.Action[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Action[i], &o.Action[i]) } } @@ -779,55 +1029,55 @@ func (m *StoreAction) CopyFrom(src interface{}) { v := StoreAction_Node{ Node: &Node{}, } - deepcopy.Copy(v.Node, o.GetNode()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Node, o.GetNode()) m.Target = &v case *StoreAction_Service: v := StoreAction_Service{ Service: &Service{}, } - deepcopy.Copy(v.Service, o.GetService()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Service, o.GetService()) m.Target = &v case *StoreAction_Task: v := StoreAction_Task{ Task: &Task{}, } - deepcopy.Copy(v.Task, o.GetTask()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Task, o.GetTask()) m.Target = &v case *StoreAction_Network: v := StoreAction_Network{ Network: &Network{}, } - deepcopy.Copy(v.Network, o.GetNetwork()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Network, o.GetNetwork()) m.Target = &v case *StoreAction_Cluster: v := StoreAction_Cluster{ Cluster: &Cluster{}, } - deepcopy.Copy(v.Cluster, o.GetCluster()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Cluster, o.GetCluster()) m.Target = &v case *StoreAction_Secret: v := StoreAction_Secret{ Secret: &Secret{}, } - deepcopy.Copy(v.Secret, o.GetSecret()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Secret, o.GetSecret()) m.Target = &v case *StoreAction_Resource: v := StoreAction_Resource{ Resource: &Resource{}, } - deepcopy.Copy(v.Resource, o.GetResource()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Resource, o.GetResource()) m.Target = &v case *StoreAction_Extension: v := StoreAction_Extension{ Extension: &Extension{}, } - deepcopy.Copy(v.Extension, o.GetExtension()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Extension, o.GetExtension()) m.Target = &v case *StoreAction_Config: v := StoreAction_Config{ Config: &Config{}, } - deepcopy.Copy(v.Config, o.GetConfig()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Config, o.GetConfig()) m.Target = &v } } @@ -842,8 +1092,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Raft service - +// RaftClient is the client API for Raft service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type RaftClient interface { // ProcessRaftMessage sends a raft message to be processed on a raft member, it is // called from the RaftMember willing to send a message to its destination ('To' field) @@ -868,7 +1119,7 @@ func NewRaftClient(cc *grpc.ClientConn) RaftClient { func (c *raftClient) ProcessRaftMessage(ctx context.Context, in *ProcessRaftMessageRequest, opts ...grpc.CallOption) (*ProcessRaftMessageResponse, error) { out := new(ProcessRaftMessageResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Raft/ProcessRaftMessage", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Raft/ProcessRaftMessage", in, out, opts...) if err != nil { return nil, err } @@ -876,7 +1127,7 @@ func (c *raftClient) ProcessRaftMessage(ctx context.Context, in *ProcessRaftMess } func (c *raftClient) StreamRaftMessage(ctx context.Context, opts ...grpc.CallOption) (Raft_StreamRaftMessageClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Raft_serviceDesc.Streams[0], c.cc, "/docker.swarmkit.v1.Raft/StreamRaftMessage", opts...) + stream, err := c.cc.NewStream(ctx, &_Raft_serviceDesc.Streams[0], "/docker.swarmkit.v1.Raft/StreamRaftMessage", opts...) if err != nil { return nil, err } @@ -911,15 +1162,14 @@ func (x *raftStreamRaftMessageClient) CloseAndRecv() (*StreamRaftMessageResponse func (c *raftClient) ResolveAddress(ctx context.Context, in *ResolveAddressRequest, opts ...grpc.CallOption) (*ResolveAddressResponse, error) { out := new(ResolveAddressResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.Raft/ResolveAddress", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.Raft/ResolveAddress", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for Raft service - +// RaftServer is the server API for Raft service. type RaftServer interface { // ProcessRaftMessage sends a raft message to be processed on a raft member, it is // called from the RaftMember willing to send a message to its destination ('To' field) @@ -934,6 +1184,20 @@ type RaftServer interface { ResolveAddress(context.Context, *ResolveAddressRequest) (*ResolveAddressResponse, error) } +// UnimplementedRaftServer can be embedded to have forward compatible implementations. +type UnimplementedRaftServer struct { +} + +func (*UnimplementedRaftServer) ProcessRaftMessage(ctx context.Context, req *ProcessRaftMessageRequest) (*ProcessRaftMessageResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ProcessRaftMessage not implemented") +} +func (*UnimplementedRaftServer) StreamRaftMessage(srv Raft_StreamRaftMessageServer) error { + return status.Errorf(codes.Unimplemented, "method StreamRaftMessage not implemented") +} +func (*UnimplementedRaftServer) ResolveAddress(ctx context.Context, req *ResolveAddressRequest) (*ResolveAddressResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ResolveAddress not implemented") +} + func RegisterRaftServer(s *grpc.Server, srv RaftServer) { s.RegisterService(&_Raft_serviceDesc, srv) } @@ -1023,8 +1287,9 @@ var _Raft_serviceDesc = grpc.ServiceDesc{ Metadata: "github.com/docker/swarmkit/api/raft.proto", } -// Client API for RaftMembership service - +// RaftMembershipClient is the client API for RaftMembership service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type RaftMembershipClient interface { // Join adds a RaftMember to the raft cluster. Join(ctx context.Context, in *JoinRequest, opts ...grpc.CallOption) (*JoinResponse, error) @@ -1042,7 +1307,7 @@ func NewRaftMembershipClient(cc *grpc.ClientConn) RaftMembershipClient { func (c *raftMembershipClient) Join(ctx context.Context, in *JoinRequest, opts ...grpc.CallOption) (*JoinResponse, error) { out := new(JoinResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.RaftMembership/Join", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.RaftMembership/Join", in, out, opts...) if err != nil { return nil, err } @@ -1051,15 +1316,14 @@ func (c *raftMembershipClient) Join(ctx context.Context, in *JoinRequest, opts . func (c *raftMembershipClient) Leave(ctx context.Context, in *LeaveRequest, opts ...grpc.CallOption) (*LeaveResponse, error) { out := new(LeaveResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.RaftMembership/Leave", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.RaftMembership/Leave", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for RaftMembership service - +// RaftMembershipServer is the server API for RaftMembership service. type RaftMembershipServer interface { // Join adds a RaftMember to the raft cluster. Join(context.Context, *JoinRequest) (*JoinResponse, error) @@ -1067,6 +1331,17 @@ type RaftMembershipServer interface { Leave(context.Context, *LeaveRequest) (*LeaveResponse, error) } +// UnimplementedRaftMembershipServer can be embedded to have forward compatible implementations. +type UnimplementedRaftMembershipServer struct { +} + +func (*UnimplementedRaftMembershipServer) Join(ctx context.Context, req *JoinRequest) (*JoinResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Join not implemented") +} +func (*UnimplementedRaftMembershipServer) Leave(ctx context.Context, req *LeaveRequest) (*LeaveResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Leave not implemented") +} + func RegisterRaftMembershipServer(s *grpc.Server, srv RaftMembershipServer) { s.RegisterService(&_RaftMembership_serviceDesc, srv) } @@ -1127,7 +1402,7 @@ var _RaftMembership_serviceDesc = grpc.ServiceDesc{ func (m *RaftMember) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1135,42 +1410,51 @@ func (m *RaftMember) Marshal() (dAtA []byte, err error) { } func (m *RaftMember) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RaftMember) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.RaftID != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.RaftID)) - } - if len(m.NodeID) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintRaft(dAtA, i, uint64(len(m.NodeID))) - i += copy(dAtA[i:], m.NodeID) + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x22 if len(m.Addr) > 0 { - dAtA[i] = 0x1a - i++ + i -= len(m.Addr) + copy(dAtA[i:], m.Addr) i = encodeVarintRaft(dAtA, i, uint64(len(m.Addr))) - i += copy(dAtA[i:], m.Addr) + i-- + dAtA[i] = 0x1a } - dAtA[i] = 0x22 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Status.Size())) - n1, err := m.Status.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.NodeID) > 0 { + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) + i = encodeVarintRaft(dAtA, i, uint64(len(m.NodeID))) + i-- + dAtA[i] = 0x12 + } + if m.RaftID != 0 { + i = encodeVarintRaft(dAtA, i, uint64(m.RaftID)) + i-- + dAtA[i] = 0x8 } - i += n1 - return i, nil + return len(dAtA) - i, nil } func (m *JoinRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1178,23 +1462,29 @@ func (m *JoinRequest) Marshal() (dAtA []byte, err error) { } func (m *JoinRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *JoinRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.Addr) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Addr) + copy(dAtA[i:], m.Addr) i = encodeVarintRaft(dAtA, i, uint64(len(m.Addr))) - i += copy(dAtA[i:], m.Addr) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *JoinResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1202,41 +1492,48 @@ func (m *JoinResponse) Marshal() (dAtA []byte, err error) { } func (m *JoinResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *JoinResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.RaftID != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.RaftID)) + if len(m.RemovedMembers) > 0 { + for iNdEx := len(m.RemovedMembers) - 1; iNdEx >= 0; iNdEx-- { + i = encodeVarintRaft(dAtA, i, uint64(m.RemovedMembers[iNdEx])) + i-- + dAtA[i] = 0x18 + } } if len(m.Members) > 0 { - for _, msg := range m.Members { - dAtA[i] = 0x12 - i++ - i = encodeVarintRaft(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x12 } } - if len(m.RemovedMembers) > 0 { - for _, num := range m.RemovedMembers { - dAtA[i] = 0x18 - i++ - i = encodeVarintRaft(dAtA, i, uint64(num)) - } + if m.RaftID != 0 { + i = encodeVarintRaft(dAtA, i, uint64(m.RaftID)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *LeaveRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1244,27 +1541,34 @@ func (m *LeaveRequest) Marshal() (dAtA []byte, err error) { } func (m *LeaveRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LeaveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Node != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Node.Size())) - n2, err := m.Node.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Node.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n2 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *LeaveResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1272,17 +1576,22 @@ func (m *LeaveResponse) Marshal() (dAtA []byte, err error) { } func (m *LeaveResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LeaveResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + return len(dAtA) - i, nil } func (m *ProcessRaftMessageRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1290,27 +1599,34 @@ func (m *ProcessRaftMessageRequest) Marshal() (dAtA []byte, err error) { } func (m *ProcessRaftMessageRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProcessRaftMessageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Message != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Message.Size())) - n3, err := m.Message.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Message.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n3 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ProcessRaftMessageResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1318,17 +1634,22 @@ func (m *ProcessRaftMessageResponse) Marshal() (dAtA []byte, err error) { } func (m *ProcessRaftMessageResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProcessRaftMessageResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + return len(dAtA) - i, nil } func (m *StreamRaftMessageRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1336,27 +1657,34 @@ func (m *StreamRaftMessageRequest) Marshal() (dAtA []byte, err error) { } func (m *StreamRaftMessageRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StreamRaftMessageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Message != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Message.Size())) - n4, err := m.Message.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Message.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n4 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *StreamRaftMessageResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1364,17 +1692,22 @@ func (m *StreamRaftMessageResponse) Marshal() (dAtA []byte, err error) { } func (m *StreamRaftMessageResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StreamRaftMessageResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + return len(dAtA) - i, nil } func (m *ResolveAddressRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1382,22 +1715,27 @@ func (m *ResolveAddressRequest) Marshal() (dAtA []byte, err error) { } func (m *ResolveAddressRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResolveAddressRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.RaftID != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintRaft(dAtA, i, uint64(m.RaftID)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *ResolveAddressResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1405,23 +1743,29 @@ func (m *ResolveAddressResponse) Marshal() (dAtA []byte, err error) { } func (m *ResolveAddressResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResolveAddressResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.Addr) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Addr) + copy(dAtA[i:], m.Addr) i = encodeVarintRaft(dAtA, i, uint64(len(m.Addr))) - i += copy(dAtA[i:], m.Addr) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *InternalRaftRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1429,34 +1773,41 @@ func (m *InternalRaftRequest) Marshal() (dAtA []byte, err error) { } func (m *InternalRaftRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *InternalRaftRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.ID != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.ID)) - } if len(m.Action) > 0 { - for _, msg := range m.Action { - dAtA[i] = 0x12 - i++ - i = encodeVarintRaft(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Action) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Action[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x12 } } - return i, nil + if m.ID != 0 { + i = encodeVarintRaft(dAtA, i, uint64(m.ID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func (m *StoreAction) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1464,159 +1815,231 @@ func (m *StoreAction) Marshal() (dAtA []byte, err error) { } func (m *StoreAction) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreAction) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Action != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Action)) - } if m.Target != nil { - nn5, err := m.Target.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size := m.Target.Size() + i -= size + if _, err := m.Target.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += nn5 } - return i, nil + if m.Action != 0 { + i = encodeVarintRaft(dAtA, i, uint64(m.Action)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func (m *StoreAction_Node) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreAction_Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Node != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Node.Size())) - n6, err := m.Node.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Node.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n6 + i-- + dAtA[i] = 0x12 } - return i, nil + return len(dAtA) - i, nil } func (m *StoreAction_Service) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreAction_Service) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Service != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Service.Size())) - n7, err := m.Service.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Service.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n7 + i-- + dAtA[i] = 0x1a } - return i, nil + return len(dAtA) - i, nil } func (m *StoreAction_Task) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreAction_Task) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Task != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Task.Size())) - n8, err := m.Task.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Task.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n8 + i-- + dAtA[i] = 0x22 } - return i, nil + return len(dAtA) - i, nil } func (m *StoreAction_Network) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreAction_Network) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Network != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Network.Size())) - n9, err := m.Network.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Network.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n9 + i-- + dAtA[i] = 0x2a } - return i, nil + return len(dAtA) - i, nil } func (m *StoreAction_Cluster) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreAction_Cluster) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Cluster != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Cluster.Size())) - n10, err := m.Cluster.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Cluster.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n10 + i-- + dAtA[i] = 0x32 } - return i, nil + return len(dAtA) - i, nil } func (m *StoreAction_Secret) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreAction_Secret) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Secret != nil { - dAtA[i] = 0x3a - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Secret.Size())) - n11, err := m.Secret.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Secret.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n11 + i-- + dAtA[i] = 0x3a } - return i, nil + return len(dAtA) - i, nil } func (m *StoreAction_Resource) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreAction_Resource) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Resource != nil { - dAtA[i] = 0x42 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Resource.Size())) - n12, err := m.Resource.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n12 + i-- + dAtA[i] = 0x42 } - return i, nil + return len(dAtA) - i, nil } func (m *StoreAction_Extension) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreAction_Extension) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Extension != nil { - dAtA[i] = 0x4a - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Extension.Size())) - n13, err := m.Extension.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Extension.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n13 + i-- + dAtA[i] = 0x4a } - return i, nil + return len(dAtA) - i, nil } func (m *StoreAction_Config) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreAction_Config) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Config != nil { - dAtA[i] = 0x52 - i++ - i = encodeVarintRaft(dAtA, i, uint64(m.Config.Size())) - n14, err := m.Config.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Config.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintRaft(dAtA, i, uint64(size)) } - i += n14 + i-- + dAtA[i] = 0x52 } - return i, nil + return len(dAtA) - i, nil } func encodeVarintRaft(dAtA []byte, offset int, v uint64) int { + offset -= sovRaft(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } type raftProxyRaftServer struct { @@ -1961,6 +2384,9 @@ func (p *raftProxyRaftMembershipServer) Leave(ctx context.Context, r *LeaveReque } func (m *RaftMember) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.RaftID != 0 { @@ -1980,6 +2406,9 @@ func (m *RaftMember) Size() (n int) { } func (m *JoinRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Addr) @@ -1990,6 +2419,9 @@ func (m *JoinRequest) Size() (n int) { } func (m *JoinResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.RaftID != 0 { @@ -2010,6 +2442,9 @@ func (m *JoinResponse) Size() (n int) { } func (m *LeaveRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Node != nil { @@ -2020,12 +2455,18 @@ func (m *LeaveRequest) Size() (n int) { } func (m *LeaveResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l return n } func (m *ProcessRaftMessageRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Message != nil { @@ -2036,12 +2477,18 @@ func (m *ProcessRaftMessageRequest) Size() (n int) { } func (m *ProcessRaftMessageResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l return n } func (m *StreamRaftMessageRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Message != nil { @@ -2052,12 +2499,18 @@ func (m *StreamRaftMessageRequest) Size() (n int) { } func (m *StreamRaftMessageResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l return n } func (m *ResolveAddressRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.RaftID != 0 { @@ -2067,6 +2520,9 @@ func (m *ResolveAddressRequest) Size() (n int) { } func (m *ResolveAddressResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Addr) @@ -2077,6 +2533,9 @@ func (m *ResolveAddressResponse) Size() (n int) { } func (m *InternalRaftRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.ID != 0 { @@ -2092,6 +2551,9 @@ func (m *InternalRaftRequest) Size() (n int) { } func (m *StoreAction) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Action != 0 { @@ -2104,6 +2566,9 @@ func (m *StoreAction) Size() (n int) { } func (m *StoreAction_Node) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Node != nil { @@ -2113,6 +2578,9 @@ func (m *StoreAction_Node) Size() (n int) { return n } func (m *StoreAction_Service) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Service != nil { @@ -2122,6 +2590,9 @@ func (m *StoreAction_Service) Size() (n int) { return n } func (m *StoreAction_Task) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Task != nil { @@ -2131,6 +2602,9 @@ func (m *StoreAction_Task) Size() (n int) { return n } func (m *StoreAction_Network) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Network != nil { @@ -2140,6 +2614,9 @@ func (m *StoreAction_Network) Size() (n int) { return n } func (m *StoreAction_Cluster) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Cluster != nil { @@ -2149,6 +2626,9 @@ func (m *StoreAction_Cluster) Size() (n int) { return n } func (m *StoreAction_Secret) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Secret != nil { @@ -2158,6 +2638,9 @@ func (m *StoreAction_Secret) Size() (n int) { return n } func (m *StoreAction_Resource) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Resource != nil { @@ -2167,6 +2650,9 @@ func (m *StoreAction_Resource) Size() (n int) { return n } func (m *StoreAction_Extension) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Extension != nil { @@ -2176,6 +2662,9 @@ func (m *StoreAction_Extension) Size() (n int) { return n } func (m *StoreAction_Config) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Config != nil { @@ -2186,14 +2675,7 @@ func (m *StoreAction_Config) Size() (n int) { } func sovRaft(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozRaft(x uint64) (n int) { return sovRaft(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -2206,7 +2688,7 @@ func (this *RaftMember) String() string { `RaftID:` + fmt.Sprintf("%v", this.RaftID) + `,`, `NodeID:` + fmt.Sprintf("%v", this.NodeID) + `,`, `Addr:` + fmt.Sprintf("%v", this.Addr) + `,`, - `Status:` + strings.Replace(strings.Replace(this.Status.String(), "RaftMemberStatus", "RaftMemberStatus", 1), `&`, ``, 1) + `,`, + `Status:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Status), "RaftMemberStatus", "RaftMemberStatus", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -2225,9 +2707,14 @@ func (this *JoinResponse) String() string { if this == nil { return "nil" } + repeatedStringForMembers := "[]*RaftMember{" + for _, f := range this.Members { + repeatedStringForMembers += strings.Replace(f.String(), "RaftMember", "RaftMember", 1) + "," + } + repeatedStringForMembers += "}" s := strings.Join([]string{`&JoinResponse{`, `RaftID:` + fmt.Sprintf("%v", this.RaftID) + `,`, - `Members:` + strings.Replace(fmt.Sprintf("%v", this.Members), "RaftMember", "RaftMember", 1) + `,`, + `Members:` + repeatedStringForMembers + `,`, `RemovedMembers:` + fmt.Sprintf("%v", this.RemovedMembers) + `,`, `}`, }, "") @@ -2238,7 +2725,7 @@ func (this *LeaveRequest) String() string { return "nil" } s := strings.Join([]string{`&LeaveRequest{`, - `Node:` + strings.Replace(fmt.Sprintf("%v", this.Node), "RaftMember", "RaftMember", 1) + `,`, + `Node:` + strings.Replace(this.Node.String(), "RaftMember", "RaftMember", 1) + `,`, `}`, }, "") return s @@ -2314,9 +2801,14 @@ func (this *InternalRaftRequest) String() string { if this == nil { return "nil" } + repeatedStringForAction := "[]StoreAction{" + for _, f := range this.Action { + repeatedStringForAction += strings.Replace(strings.Replace(f.String(), "StoreAction", "StoreAction", 1), `&`, ``, 1) + "," + } + repeatedStringForAction += "}" s := strings.Join([]string{`&InternalRaftRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, - `Action:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Action), "StoreAction", "StoreAction", 1), `&`, ``, 1) + `,`, + `Action:` + repeatedStringForAction + `,`, `}`, }, "") return s @@ -2445,7 +2937,7 @@ func (m *RaftMember) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2473,7 +2965,7 @@ func (m *RaftMember) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.RaftID |= (uint64(b) & 0x7F) << shift + m.RaftID |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2492,7 +2984,7 @@ func (m *RaftMember) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2502,6 +2994,9 @@ func (m *RaftMember) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2521,7 +3016,7 @@ func (m *RaftMember) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2531,6 +3026,9 @@ func (m *RaftMember) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2550,7 +3048,7 @@ func (m *RaftMember) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2559,6 +3057,9 @@ func (m *RaftMember) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2572,7 +3073,7 @@ func (m *RaftMember) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -2602,7 +3103,7 @@ func (m *JoinRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2630,7 +3131,7 @@ func (m *JoinRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2640,6 +3141,9 @@ func (m *JoinRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2651,7 +3155,7 @@ func (m *JoinRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -2681,7 +3185,7 @@ func (m *JoinResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2709,7 +3213,7 @@ func (m *JoinResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.RaftID |= (uint64(b) & 0x7F) << shift + m.RaftID |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2728,7 +3232,7 @@ func (m *JoinResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2737,6 +3241,9 @@ func (m *JoinResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2757,7 +3264,7 @@ func (m *JoinResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (uint64(b) & 0x7F) << shift + v |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2774,7 +3281,7 @@ func (m *JoinResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - packedLen |= (int(b) & 0x7F) << shift + packedLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2783,9 +3290,23 @@ func (m *JoinResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.RemovedMembers) == 0 { + m.RemovedMembers = make([]uint64, 0, elementCount) + } for iNdEx < postIndex { var v uint64 for shift := uint(0); ; shift += 7 { @@ -2797,7 +3318,7 @@ func (m *JoinResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (uint64(b) & 0x7F) << shift + v |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2813,7 +3334,7 @@ func (m *JoinResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -2843,7 +3364,7 @@ func (m *LeaveRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2871,7 +3392,7 @@ func (m *LeaveRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2880,6 +3401,9 @@ func (m *LeaveRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2896,7 +3420,7 @@ func (m *LeaveRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -2926,7 +3450,7 @@ func (m *LeaveResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2946,7 +3470,7 @@ func (m *LeaveResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -2976,7 +3500,7 @@ func (m *ProcessRaftMessageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3004,7 +3528,7 @@ func (m *ProcessRaftMessageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3013,6 +3537,9 @@ func (m *ProcessRaftMessageRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3029,7 +3556,7 @@ func (m *ProcessRaftMessageRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -3059,7 +3586,7 @@ func (m *ProcessRaftMessageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3079,7 +3606,7 @@ func (m *ProcessRaftMessageResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -3109,7 +3636,7 @@ func (m *StreamRaftMessageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3137,7 +3664,7 @@ func (m *StreamRaftMessageRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3146,6 +3673,9 @@ func (m *StreamRaftMessageRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3162,7 +3692,7 @@ func (m *StreamRaftMessageRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -3192,7 +3722,7 @@ func (m *StreamRaftMessageResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3212,7 +3742,7 @@ func (m *StreamRaftMessageResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -3242,7 +3772,7 @@ func (m *ResolveAddressRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3270,7 +3800,7 @@ func (m *ResolveAddressRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.RaftID |= (uint64(b) & 0x7F) << shift + m.RaftID |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3281,7 +3811,7 @@ func (m *ResolveAddressRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -3311,7 +3841,7 @@ func (m *ResolveAddressResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3339,7 +3869,7 @@ func (m *ResolveAddressResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3349,6 +3879,9 @@ func (m *ResolveAddressResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3360,7 +3893,7 @@ func (m *ResolveAddressResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -3390,7 +3923,7 @@ func (m *InternalRaftRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3418,7 +3951,7 @@ func (m *InternalRaftRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ID |= (uint64(b) & 0x7F) << shift + m.ID |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3437,7 +3970,7 @@ func (m *InternalRaftRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3446,6 +3979,9 @@ func (m *InternalRaftRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3460,7 +3996,7 @@ func (m *InternalRaftRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -3490,7 +4026,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3518,7 +4054,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Action |= (StoreActionKind(b) & 0x7F) << shift + m.Action |= StoreActionKind(b&0x7F) << shift if b < 0x80 { break } @@ -3537,7 +4073,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3546,6 +4082,9 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3569,7 +4108,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3578,6 +4117,9 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3601,7 +4143,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3610,6 +4152,9 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3633,7 +4178,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3642,6 +4187,9 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3665,7 +4213,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3674,6 +4222,9 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3697,7 +4248,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3706,6 +4257,9 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3729,7 +4283,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3738,6 +4292,9 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3761,7 +4318,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3770,6 +4327,9 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3793,7 +4353,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3802,6 +4362,9 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { return ErrInvalidLengthRaft } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthRaft + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3817,7 +4380,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthRaft } if (iNdEx + skippy) > l { @@ -3835,6 +4398,7 @@ func (m *StoreAction) Unmarshal(dAtA []byte) error { func skipRaft(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -3866,10 +4430,8 @@ func skipRaft(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -3886,123 +4448,34 @@ func skipRaft(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthRaft } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowRaft - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipRaft(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupRaft + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthRaft + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthRaft = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowRaft = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthRaft = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowRaft = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupRaft = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("github.com/docker/swarmkit/api/raft.proto", fileDescriptorRaft) } - -var fileDescriptorRaft = []byte{ - // 1015 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0xc1, 0x6e, 0x1b, 0x45, - 0x18, 0xc7, 0x77, 0xed, 0xad, 0xd3, 0x7c, 0x69, 0x93, 0x30, 0x25, 0x61, 0xb3, 0x2d, 0x8e, 0xbb, - 0x45, 0xc2, 0x09, 0xc9, 0x5a, 0x18, 0xa4, 0xa2, 0x42, 0x0f, 0x71, 0x62, 0x29, 0x26, 0xad, 0x53, - 0x6d, 0x12, 0xe8, 0x2d, 0xac, 0x77, 0x27, 0xee, 0x62, 0x7b, 0xc7, 0xcc, 0x8c, 0x1d, 0xb8, 0xa0, - 0x1e, 0x21, 0x2f, 0x00, 0x42, 0xaa, 0x38, 0xc0, 0xb9, 0x0f, 0xc0, 0x03, 0xa0, 0x88, 0x13, 0x37, - 0x38, 0x45, 0xd4, 0x0f, 0x00, 0xaf, 0x80, 0x66, 0x76, 0xd7, 0x31, 0xf6, 0xda, 0xf1, 0x81, 0x4b, - 0x32, 0xda, 0xf9, 0xfd, 0xbf, 0xff, 0x37, 0x33, 0xdf, 0x7c, 0x63, 0x58, 0xab, 0xfb, 0xfc, 0x59, - 0xa7, 0x66, 0xb9, 0xa4, 0x55, 0xf0, 0x88, 0xdb, 0xc0, 0xb4, 0xc0, 0x4e, 0x1d, 0xda, 0x6a, 0xf8, - 0xbc, 0xe0, 0xb4, 0xfd, 0x02, 0x75, 0x4e, 0xb8, 0xd5, 0xa6, 0x84, 0x13, 0x84, 0xc2, 0x79, 0x2b, - 0x9e, 0xb7, 0xba, 0xef, 0x1a, 0x1b, 0x57, 0xc8, 0x49, 0xed, 0x73, 0xec, 0x72, 0x16, 0x46, 0x30, - 0xd6, 0xaf, 0xa0, 0xf9, 0x57, 0x6d, 0x1c, 0xb3, 0x9b, 0x03, 0xac, 0x4b, 0x28, 0x26, 0xac, 0x80, - 0xb9, 0xeb, 0xc9, 0x84, 0xe4, 0x9f, 0x76, 0x6d, 0x20, 0x39, 0xe3, 0xf5, 0x3a, 0xa9, 0x13, 0x39, - 0x2c, 0x88, 0x51, 0xf4, 0xf5, 0xfe, 0x04, 0x43, 0x49, 0xd4, 0x3a, 0x27, 0x85, 0x76, 0xb3, 0x53, - 0xf7, 0x83, 0xe8, 0x5f, 0x28, 0x34, 0x5f, 0xaa, 0x00, 0xb6, 0x73, 0xc2, 0x1f, 0xe3, 0x56, 0x0d, - 0x53, 0x74, 0x0f, 0x66, 0x84, 0xd7, 0xb1, 0xef, 0xe9, 0x6a, 0x4e, 0xcd, 0x6b, 0x25, 0xe8, 0x5d, - 0xac, 0x66, 0x04, 0x50, 0xd9, 0xb1, 0x33, 0x62, 0xaa, 0xe2, 0x09, 0x28, 0x20, 0x1e, 0x16, 0x50, - 0x2a, 0xa7, 0xe6, 0x67, 0x43, 0xa8, 0x4a, 0x3c, 0x2c, 0x20, 0x31, 0x55, 0xf1, 0x10, 0x02, 0xcd, - 0xf1, 0x3c, 0xaa, 0xa7, 0x05, 0x61, 0xcb, 0x31, 0x2a, 0x41, 0x86, 0x71, 0x87, 0x77, 0x98, 0xae, - 0xe5, 0xd4, 0xfc, 0x5c, 0xf1, 0x2d, 0x6b, 0x74, 0xa7, 0xad, 0xcb, 0x6c, 0x0e, 0x24, 0x5b, 0xd2, - 0xce, 0x2f, 0x56, 0x15, 0x3b, 0x52, 0x9a, 0x77, 0x61, 0xee, 0x63, 0xe2, 0x07, 0x36, 0xfe, 0xa2, - 0x83, 0x19, 0xef, 0xdb, 0xa8, 0x97, 0x36, 0xe6, 0x0f, 0x2a, 0xdc, 0x08, 0x19, 0xd6, 0x26, 0x01, - 0xc3, 0xd3, 0xad, 0xea, 0x03, 0x98, 0x69, 0x49, 0x5b, 0xa6, 0xa7, 0x72, 0xe9, 0xfc, 0x5c, 0x31, - 0x3b, 0x39, 0x3b, 0x3b, 0xc6, 0xd1, 0x3b, 0xb0, 0x40, 0x71, 0x8b, 0x74, 0xb1, 0x77, 0x1c, 0x47, - 0x48, 0xe7, 0xd2, 0x79, 0xad, 0x94, 0x5a, 0x54, 0xec, 0xf9, 0x68, 0x2a, 0x14, 0x31, 0xb3, 0x04, - 0x37, 0x1e, 0x61, 0xa7, 0x8b, 0xe3, 0x05, 0x14, 0x41, 0x13, 0x3b, 0x26, 0x13, 0xbb, 0xda, 0x53, - 0xb2, 0xe6, 0x02, 0xdc, 0x8c, 0x62, 0x84, 0x0b, 0x34, 0x1f, 0xc1, 0xca, 0x13, 0x4a, 0x5c, 0xcc, - 0x58, 0xc8, 0x32, 0xe6, 0xd4, 0xfb, 0x0e, 0x6b, 0x62, 0x61, 0xf2, 0x4b, 0x64, 0xb2, 0x60, 0x85, - 0x65, 0x65, 0xc5, 0x60, 0x3c, 0xff, 0x40, 0x7b, 0xfe, 0x9d, 0xa9, 0x98, 0x77, 0xc0, 0x48, 0x8a, - 0x16, 0x79, 0xed, 0x81, 0x7e, 0xc0, 0x29, 0x76, 0x5a, 0xff, 0x87, 0xd5, 0x6d, 0x58, 0x49, 0x08, - 0x16, 0x39, 0x7d, 0x04, 0x4b, 0x36, 0x66, 0xa4, 0xd9, 0xc5, 0x5b, 0x9e, 0x47, 0x45, 0x3a, 0x91, - 0xcd, 0x34, 0xe7, 0x69, 0x6e, 0xc0, 0xf2, 0xb0, 0x3a, 0x2a, 0x87, 0xa4, 0x9a, 0x69, 0xc2, 0xad, - 0x4a, 0xc0, 0x31, 0x0d, 0x9c, 0xa6, 0x88, 0x13, 0x3b, 0x2d, 0x43, 0xaa, 0x6f, 0x92, 0xe9, 0x5d, - 0xac, 0xa6, 0x2a, 0x3b, 0x76, 0xca, 0xf7, 0xd0, 0x43, 0xc8, 0x38, 0x2e, 0xf7, 0x49, 0x10, 0xd5, - 0xca, 0x6a, 0xd2, 0xb9, 0x1d, 0x70, 0x42, 0xf1, 0x96, 0xc4, 0xe2, 0x22, 0x0e, 0x45, 0xe6, 0xaf, - 0x1a, 0xcc, 0x0d, 0xcc, 0xa2, 0x0f, 0xfb, 0xe1, 0x84, 0xd5, 0x7c, 0xf1, 0xde, 0x15, 0xe1, 0xf6, - 0xfc, 0xc0, 0x8b, 0x83, 0x21, 0x2b, 0xaa, 0xa0, 0x94, 0xdc, 0x71, 0x3d, 0x49, 0x2a, 0xee, 0xe6, - 0xae, 0x12, 0x56, 0x0f, 0xba, 0x0f, 0x33, 0x0c, 0xd3, 0xae, 0xef, 0x62, 0x79, 0x39, 0xe7, 0x8a, - 0xb7, 0x13, 0xdd, 0x42, 0x64, 0x57, 0xb1, 0x63, 0x5a, 0x18, 0x71, 0x87, 0x35, 0xa2, 0xcb, 0x9b, - 0x68, 0x74, 0xe8, 0xb0, 0x86, 0x30, 0x12, 0x9c, 0x30, 0x0a, 0x30, 0x3f, 0x25, 0xb4, 0xa1, 0x5f, - 0x1b, 0x6f, 0x54, 0x0d, 0x11, 0x61, 0x14, 0xd1, 0x42, 0xe8, 0x36, 0x3b, 0x8c, 0x63, 0xaa, 0x67, - 0xc6, 0x0b, 0xb7, 0x43, 0x44, 0x08, 0x23, 0x1a, 0xbd, 0x0f, 0x19, 0x86, 0x5d, 0x8a, 0xb9, 0x3e, - 0x23, 0x75, 0x46, 0xf2, 0xca, 0x04, 0xb1, 0x2b, 0x5a, 0x8a, 0x1c, 0xa1, 0x07, 0x70, 0x9d, 0x62, - 0x46, 0x3a, 0xd4, 0xc5, 0xfa, 0x75, 0xa9, 0xbb, 0x93, 0x78, 0x0d, 0x23, 0x66, 0x57, 0xb1, 0xfb, - 0x3c, 0x7a, 0x08, 0xb3, 0xf8, 0x4b, 0x8e, 0x03, 0x26, 0x0e, 0x6f, 0x56, 0x8a, 0xdf, 0x4c, 0x12, - 0x97, 0x63, 0x68, 0x57, 0xb1, 0x2f, 0x15, 0x22, 0x61, 0x97, 0x04, 0x27, 0x7e, 0x5d, 0x87, 0xf1, - 0x09, 0x6f, 0x4b, 0x42, 0x24, 0x1c, 0xb2, 0xa5, 0xeb, 0x90, 0xe1, 0x0e, 0xad, 0x63, 0xbe, 0xfe, - 0x8f, 0x0a, 0x0b, 0x43, 0x75, 0x81, 0xde, 0x86, 0x99, 0xa3, 0xea, 0x5e, 0x75, 0xff, 0xd3, 0xea, - 0xa2, 0x62, 0x18, 0x67, 0x2f, 0x72, 0xcb, 0x43, 0xc4, 0x51, 0xd0, 0x08, 0xc8, 0x69, 0x80, 0x8a, - 0x70, 0xeb, 0xe0, 0x70, 0xdf, 0x2e, 0x1f, 0x6f, 0x6d, 0x1f, 0x56, 0xf6, 0xab, 0xc7, 0xdb, 0x76, - 0x79, 0xeb, 0xb0, 0xbc, 0xa8, 0x1a, 0x2b, 0x67, 0x2f, 0x72, 0x4b, 0x43, 0xa2, 0x6d, 0x8a, 0x1d, - 0x8e, 0x47, 0x34, 0x47, 0x4f, 0x76, 0x84, 0x26, 0x95, 0xa8, 0x39, 0x6a, 0x7b, 0x49, 0x1a, 0xbb, - 0xfc, 0x78, 0xff, 0x93, 0xf2, 0x62, 0x3a, 0x51, 0x63, 0xcb, 0x76, 0x69, 0xbc, 0xf1, 0xcd, 0x4f, - 0x59, 0xe5, 0x97, 0x9f, 0xb3, 0xc3, 0xab, 0x2b, 0xfe, 0x98, 0x06, 0x4d, 0xdc, 0x50, 0x74, 0xa6, - 0x02, 0x1a, 0x6d, 0x53, 0x68, 0x33, 0x69, 0x07, 0xc7, 0x36, 0x47, 0xc3, 0x9a, 0x16, 0x8f, 0x7a, - 0xd2, 0xd2, 0x6f, 0x2f, 0xff, 0xfe, 0x3e, 0xb5, 0x00, 0x37, 0x25, 0xbf, 0xd9, 0x72, 0x02, 0xa7, - 0x8e, 0x29, 0xfa, 0x56, 0x85, 0xd7, 0x46, 0x1a, 0x19, 0xda, 0x48, 0xbe, 0xc6, 0xc9, 0xcd, 0xd3, - 0xd8, 0x9c, 0x92, 0x9e, 0x98, 0x49, 0x5e, 0x45, 0x5f, 0xc3, 0xfc, 0x7f, 0x1b, 0x1f, 0x5a, 0x1b, - 0x57, 0xce, 0x23, 0xad, 0xd5, 0x58, 0x9f, 0x06, 0x9d, 0x98, 0x41, 0xf1, 0x0f, 0x15, 0xe6, 0x2f, - 0x9f, 0x2c, 0xf6, 0xcc, 0x6f, 0xa3, 0xcf, 0x40, 0x13, 0x0f, 0x32, 0x4a, 0x6c, 0x93, 0x03, 0xcf, - 0xb9, 0x91, 0x1b, 0x0f, 0x4c, 0x3e, 0x00, 0x17, 0xae, 0xc9, 0x27, 0x11, 0x25, 0x46, 0x18, 0x7c, - 0x71, 0x8d, 0xbb, 0x13, 0x88, 0x89, 0x26, 0x25, 0xfd, 0xfc, 0x55, 0x56, 0xf9, 0xf3, 0x55, 0x56, - 0x79, 0xde, 0xcb, 0xaa, 0xe7, 0xbd, 0xac, 0xfa, 0x7b, 0x2f, 0xab, 0xfe, 0xd5, 0xcb, 0xaa, 0x4f, - 0xd3, 0x4f, 0xb5, 0x5a, 0x46, 0xfe, 0xa2, 0x7a, 0xef, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3e, - 0x7a, 0x8b, 0xe7, 0x6a, 0x0a, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/resource.pb.go b/vendor/github.com/docker/swarmkit/api/resource.pb.go index 2d4741993e9fa..8c9cf71e26184 100644 --- a/vendor/github.com/docker/swarmkit/api/resource.pb.go +++ b/vendor/github.com/docker/swarmkit/api/resource.pb.go @@ -3,65 +3,181 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" -import _ "github.com/docker/swarmkit/protobuf/plugin" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import raftselector "github.com/docker/swarmkit/manager/raftselector" -import codes "google.golang.org/grpc/codes" -import status "google.golang.org/grpc/status" -import metadata "google.golang.org/grpc/metadata" -import peer "google.golang.org/grpc/peer" -import rafttime "time" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + context "context" + fmt "fmt" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + raftselector "github.com/docker/swarmkit/manager/raftselector" + _ "github.com/docker/swarmkit/protobuf/plugin" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + metadata "google.golang.org/grpc/metadata" + peer "google.golang.org/grpc/peer" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + rafttime "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type AttachNetworkRequest struct { - Config *NetworkAttachmentConfig `protobuf:"bytes,1,opt,name=config" json:"config,omitempty"` + Config *NetworkAttachmentConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` ContainerID string `protobuf:"bytes,2,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` } -func (m *AttachNetworkRequest) Reset() { *m = AttachNetworkRequest{} } -func (*AttachNetworkRequest) ProtoMessage() {} -func (*AttachNetworkRequest) Descriptor() ([]byte, []int) { return fileDescriptorResource, []int{0} } +func (m *AttachNetworkRequest) Reset() { *m = AttachNetworkRequest{} } +func (*AttachNetworkRequest) ProtoMessage() {} +func (*AttachNetworkRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_909455b1b868ddb9, []int{0} +} +func (m *AttachNetworkRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AttachNetworkRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AttachNetworkRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AttachNetworkRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AttachNetworkRequest.Merge(m, src) +} +func (m *AttachNetworkRequest) XXX_Size() int { + return m.Size() +} +func (m *AttachNetworkRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AttachNetworkRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AttachNetworkRequest proto.InternalMessageInfo type AttachNetworkResponse struct { AttachmentID string `protobuf:"bytes,1,opt,name=attachment_id,json=attachmentId,proto3" json:"attachment_id,omitempty"` } -func (m *AttachNetworkResponse) Reset() { *m = AttachNetworkResponse{} } -func (*AttachNetworkResponse) ProtoMessage() {} -func (*AttachNetworkResponse) Descriptor() ([]byte, []int) { return fileDescriptorResource, []int{1} } +func (m *AttachNetworkResponse) Reset() { *m = AttachNetworkResponse{} } +func (*AttachNetworkResponse) ProtoMessage() {} +func (*AttachNetworkResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_909455b1b868ddb9, []int{1} +} +func (m *AttachNetworkResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AttachNetworkResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AttachNetworkResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AttachNetworkResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_AttachNetworkResponse.Merge(m, src) +} +func (m *AttachNetworkResponse) XXX_Size() int { + return m.Size() +} +func (m *AttachNetworkResponse) XXX_DiscardUnknown() { + xxx_messageInfo_AttachNetworkResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_AttachNetworkResponse proto.InternalMessageInfo type DetachNetworkRequest struct { AttachmentID string `protobuf:"bytes,1,opt,name=attachment_id,json=attachmentId,proto3" json:"attachment_id,omitempty"` } -func (m *DetachNetworkRequest) Reset() { *m = DetachNetworkRequest{} } -func (*DetachNetworkRequest) ProtoMessage() {} -func (*DetachNetworkRequest) Descriptor() ([]byte, []int) { return fileDescriptorResource, []int{2} } +func (m *DetachNetworkRequest) Reset() { *m = DetachNetworkRequest{} } +func (*DetachNetworkRequest) ProtoMessage() {} +func (*DetachNetworkRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_909455b1b868ddb9, []int{2} +} +func (m *DetachNetworkRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DetachNetworkRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DetachNetworkRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DetachNetworkRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DetachNetworkRequest.Merge(m, src) +} +func (m *DetachNetworkRequest) XXX_Size() int { + return m.Size() +} +func (m *DetachNetworkRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DetachNetworkRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DetachNetworkRequest proto.InternalMessageInfo type DetachNetworkResponse struct { } -func (m *DetachNetworkResponse) Reset() { *m = DetachNetworkResponse{} } -func (*DetachNetworkResponse) ProtoMessage() {} -func (*DetachNetworkResponse) Descriptor() ([]byte, []int) { return fileDescriptorResource, []int{3} } +func (m *DetachNetworkResponse) Reset() { *m = DetachNetworkResponse{} } +func (*DetachNetworkResponse) ProtoMessage() {} +func (*DetachNetworkResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_909455b1b868ddb9, []int{3} +} +func (m *DetachNetworkResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DetachNetworkResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DetachNetworkResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DetachNetworkResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_DetachNetworkResponse.Merge(m, src) +} +func (m *DetachNetworkResponse) XXX_Size() int { + return m.Size() +} +func (m *DetachNetworkResponse) XXX_DiscardUnknown() { + xxx_messageInfo_DetachNetworkResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_DetachNetworkResponse proto.InternalMessageInfo func init() { proto.RegisterType((*AttachNetworkRequest)(nil), "docker.swarmkit.v1.AttachNetworkRequest") @@ -70,6 +186,40 @@ func init() { proto.RegisterType((*DetachNetworkResponse)(nil), "docker.swarmkit.v1.DetachNetworkResponse") } +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/resource.proto", fileDescriptor_909455b1b868ddb9) +} + +var fileDescriptor_909455b1b868ddb9 = []byte{ + // 411 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0x3f, 0x6f, 0xda, 0x40, + 0x18, 0xc6, 0x7d, 0x0c, 0x48, 0x3d, 0x8c, 0xda, 0x5a, 0xa0, 0x22, 0xa4, 0x1e, 0xc8, 0xed, 0x40, + 0x5b, 0x61, 0xab, 0x54, 0x55, 0x67, 0xfe, 0x2c, 0x1e, 0xca, 0xe0, 0x2f, 0x50, 0x1d, 0xf6, 0x61, + 0x2c, 0xb0, 0xcf, 0x3d, 0x9f, 0x83, 0xb2, 0x65, 0xcd, 0x94, 0x8c, 0xf9, 0x0e, 0x91, 0xf2, 0x39, + 0x50, 0x26, 0x46, 0x26, 0x14, 0xcc, 0x9e, 0xcf, 0x10, 0xe1, 0x33, 0x20, 0x88, 0x95, 0xa0, 0x4c, + 0x3e, 0x9f, 0x9f, 0xe7, 0x79, 0x7f, 0xef, 0xfb, 0x1a, 0x36, 0x1d, 0x97, 0x8f, 0xa2, 0x81, 0x66, + 0x51, 0x4f, 0xb7, 0xa9, 0x35, 0x26, 0x4c, 0x0f, 0xa7, 0x98, 0x79, 0x63, 0x97, 0xeb, 0x38, 0x70, + 0x75, 0x46, 0x42, 0x1a, 0x31, 0x8b, 0x68, 0x01, 0xa3, 0x9c, 0x2a, 0x8a, 0xd0, 0x68, 0x5b, 0x8d, + 0x76, 0xf6, 0xb3, 0xfa, 0xfd, 0x95, 0x08, 0x7e, 0x1e, 0x90, 0x50, 0xf8, 0xab, 0x25, 0x87, 0x3a, + 0x34, 0x39, 0xea, 0x9b, 0x53, 0x7a, 0xfb, 0xe7, 0x85, 0x84, 0x44, 0x31, 0x88, 0x86, 0x7a, 0x30, + 0x89, 0x1c, 0xd7, 0x4f, 0x1f, 0xc2, 0xa8, 0x5e, 0x01, 0x58, 0x6a, 0x73, 0x8e, 0xad, 0x51, 0x9f, + 0xf0, 0x29, 0x65, 0x63, 0x93, 0xfc, 0x8f, 0x48, 0xc8, 0x95, 0x2e, 0xcc, 0x5b, 0xd4, 0x1f, 0xba, + 0x4e, 0x05, 0xd4, 0x41, 0xa3, 0xd0, 0xfa, 0xa1, 0x3d, 0x07, 0xd7, 0x52, 0x8f, 0x08, 0xf0, 0x88, + 0xcf, 0xbb, 0x89, 0xc5, 0x4c, 0xad, 0x4a, 0x0b, 0xca, 0x16, 0xf5, 0x39, 0x76, 0x7d, 0xc2, 0xfe, + 0xb9, 0x76, 0x25, 0x57, 0x07, 0x8d, 0x77, 0x9d, 0xf7, 0xf1, 0xb2, 0x56, 0xe8, 0x6e, 0xef, 0x8d, + 0x9e, 0x59, 0xd8, 0x89, 0x0c, 0x5b, 0xed, 0xc3, 0xf2, 0x11, 0x50, 0x18, 0x50, 0x3f, 0x24, 0xca, + 0x6f, 0x58, 0xc4, 0xbb, 0x42, 0x9b, 0x34, 0x90, 0xa4, 0x7d, 0x88, 0x97, 0x35, 0x79, 0x4f, 0x60, + 0xf4, 0x4c, 0x79, 0x2f, 0x33, 0x6c, 0xf5, 0x2f, 0x2c, 0xf5, 0x48, 0x46, 0x83, 0x6f, 0x8c, 0xfb, + 0x04, 0xcb, 0x47, 0x71, 0x02, 0xaf, 0x75, 0x9b, 0x83, 0x1f, 0xcd, 0x74, 0xd7, 0xed, 0xc9, 0x84, + 0x5a, 0x98, 0x53, 0xa6, 0x5c, 0x02, 0x58, 0x3c, 0x68, 0x47, 0x69, 0x64, 0x0d, 0x32, 0x6b, 0x05, + 0xd5, 0x6f, 0x27, 0x28, 0x45, 0x71, 0xf5, 0xcb, 0xfd, 0xdd, 0xe3, 0x4d, 0xee, 0x33, 0x94, 0x13, + 0x69, 0x73, 0xf3, 0x8d, 0x30, 0x58, 0x14, 0x6f, 0x1e, 0xf6, 0xb1, 0x43, 0x04, 0xcb, 0x01, 0x7b, + 0x36, 0x4b, 0xd6, 0xb4, 0xb2, 0x59, 0x32, 0x07, 0x71, 0x12, 0x4b, 0xe7, 0xeb, 0x6c, 0x85, 0xa4, + 0xc5, 0x0a, 0x49, 0x17, 0x31, 0x02, 0xb3, 0x18, 0x81, 0x79, 0x8c, 0xc0, 0x43, 0x8c, 0xc0, 0xf5, + 0x1a, 0x49, 0xf3, 0x35, 0x92, 0x16, 0x6b, 0x24, 0x0d, 0xf2, 0xc9, 0x4f, 0xfa, 0xeb, 0x29, 0x00, + 0x00, 0xff, 0xff, 0x9d, 0x2f, 0x31, 0x83, 0x64, 0x03, 0x00, 0x00, +} + type authenticatedWrapperResourceAllocatorServer struct { local ResourceAllocatorServer authorize func(context.Context, []string) error @@ -113,7 +263,7 @@ func (m *AttachNetworkRequest) CopyFrom(src interface{}) { *m = *o if o.Config != nil { m.Config = &NetworkAttachmentConfig{} - deepcopy.Copy(m.Config, o.Config) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Config, o.Config) } } @@ -166,8 +316,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for ResourceAllocator service - +// ResourceAllocatorClient is the client API for ResourceAllocator service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ResourceAllocatorClient interface { AttachNetwork(ctx context.Context, in *AttachNetworkRequest, opts ...grpc.CallOption) (*AttachNetworkResponse, error) DetachNetwork(ctx context.Context, in *DetachNetworkRequest, opts ...grpc.CallOption) (*DetachNetworkResponse, error) @@ -183,7 +334,7 @@ func NewResourceAllocatorClient(cc *grpc.ClientConn) ResourceAllocatorClient { func (c *resourceAllocatorClient) AttachNetwork(ctx context.Context, in *AttachNetworkRequest, opts ...grpc.CallOption) (*AttachNetworkResponse, error) { out := new(AttachNetworkResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.ResourceAllocator/AttachNetwork", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.ResourceAllocator/AttachNetwork", in, out, opts...) if err != nil { return nil, err } @@ -192,20 +343,30 @@ func (c *resourceAllocatorClient) AttachNetwork(ctx context.Context, in *AttachN func (c *resourceAllocatorClient) DetachNetwork(ctx context.Context, in *DetachNetworkRequest, opts ...grpc.CallOption) (*DetachNetworkResponse, error) { out := new(DetachNetworkResponse) - err := grpc.Invoke(ctx, "/docker.swarmkit.v1.ResourceAllocator/DetachNetwork", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/docker.swarmkit.v1.ResourceAllocator/DetachNetwork", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for ResourceAllocator service - +// ResourceAllocatorServer is the server API for ResourceAllocator service. type ResourceAllocatorServer interface { AttachNetwork(context.Context, *AttachNetworkRequest) (*AttachNetworkResponse, error) DetachNetwork(context.Context, *DetachNetworkRequest) (*DetachNetworkResponse, error) } +// UnimplementedResourceAllocatorServer can be embedded to have forward compatible implementations. +type UnimplementedResourceAllocatorServer struct { +} + +func (*UnimplementedResourceAllocatorServer) AttachNetwork(ctx context.Context, req *AttachNetworkRequest) (*AttachNetworkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AttachNetwork not implemented") +} +func (*UnimplementedResourceAllocatorServer) DetachNetwork(ctx context.Context, req *DetachNetworkRequest) (*DetachNetworkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DetachNetwork not implemented") +} + func RegisterResourceAllocatorServer(s *grpc.Server, srv ResourceAllocatorServer) { s.RegisterService(&_ResourceAllocator_serviceDesc, srv) } @@ -266,7 +427,7 @@ var _ResourceAllocator_serviceDesc = grpc.ServiceDesc{ func (m *AttachNetworkRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -274,33 +435,41 @@ func (m *AttachNetworkRequest) Marshal() (dAtA []byte, err error) { } func (m *AttachNetworkRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AttachNetworkRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Config != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintResource(dAtA, i, uint64(m.Config.Size())) - n1, err := m.Config.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 - } if len(m.ContainerID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) i = encodeVarintResource(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + i-- + dAtA[i] = 0x12 + } + if m.Config != nil { + { + size, err := m.Config.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintResource(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *AttachNetworkResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -308,23 +477,29 @@ func (m *AttachNetworkResponse) Marshal() (dAtA []byte, err error) { } func (m *AttachNetworkResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AttachNetworkResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.AttachmentID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.AttachmentID) + copy(dAtA[i:], m.AttachmentID) i = encodeVarintResource(dAtA, i, uint64(len(m.AttachmentID))) - i += copy(dAtA[i:], m.AttachmentID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *DetachNetworkRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -332,23 +507,29 @@ func (m *DetachNetworkRequest) Marshal() (dAtA []byte, err error) { } func (m *DetachNetworkRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DetachNetworkRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.AttachmentID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.AttachmentID) + copy(dAtA[i:], m.AttachmentID) i = encodeVarintResource(dAtA, i, uint64(len(m.AttachmentID))) - i += copy(dAtA[i:], m.AttachmentID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *DetachNetworkResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -356,21 +537,28 @@ func (m *DetachNetworkResponse) Marshal() (dAtA []byte, err error) { } func (m *DetachNetworkResponse) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DetachNetworkResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + return len(dAtA) - i, nil } func encodeVarintResource(dAtA []byte, offset int, v uint64) int { + offset -= sovResource(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } type raftProxyResourceAllocatorServer struct { @@ -516,6 +704,9 @@ func (p *raftProxyResourceAllocatorServer) DetachNetwork(ctx context.Context, r } func (m *AttachNetworkRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Config != nil { @@ -530,6 +721,9 @@ func (m *AttachNetworkRequest) Size() (n int) { } func (m *AttachNetworkResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.AttachmentID) @@ -540,6 +734,9 @@ func (m *AttachNetworkResponse) Size() (n int) { } func (m *DetachNetworkRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.AttachmentID) @@ -550,20 +747,16 @@ func (m *DetachNetworkRequest) Size() (n int) { } func (m *DetachNetworkResponse) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l return n } func sovResource(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozResource(x uint64) (n int) { return sovResource(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -631,7 +824,7 @@ func (m *AttachNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -659,7 +852,7 @@ func (m *AttachNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -668,6 +861,9 @@ func (m *AttachNetworkRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthResource } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthResource + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -692,7 +888,7 @@ func (m *AttachNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -702,6 +898,9 @@ func (m *AttachNetworkRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthResource } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResource + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -713,7 +912,7 @@ func (m *AttachNetworkRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthResource } if (iNdEx + skippy) > l { @@ -743,7 +942,7 @@ func (m *AttachNetworkResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -771,7 +970,7 @@ func (m *AttachNetworkResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -781,6 +980,9 @@ func (m *AttachNetworkResponse) Unmarshal(dAtA []byte) error { return ErrInvalidLengthResource } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResource + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -792,7 +994,7 @@ func (m *AttachNetworkResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthResource } if (iNdEx + skippy) > l { @@ -822,7 +1024,7 @@ func (m *DetachNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -850,7 +1052,7 @@ func (m *DetachNetworkRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -860,6 +1062,9 @@ func (m *DetachNetworkRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthResource } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResource + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -871,7 +1076,7 @@ func (m *DetachNetworkRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthResource } if (iNdEx + skippy) > l { @@ -901,7 +1106,7 @@ func (m *DetachNetworkResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -921,7 +1126,7 @@ func (m *DetachNetworkResponse) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthResource } if (iNdEx + skippy) > l { @@ -939,6 +1144,7 @@ func (m *DetachNetworkResponse) Unmarshal(dAtA []byte) error { func skipResource(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -970,10 +1176,8 @@ func skipResource(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -990,86 +1194,34 @@ func skipResource(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthResource } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowResource - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipResource(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupResource + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthResource + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthResource = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowResource = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthResource = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowResource = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupResource = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/docker/swarmkit/api/resource.proto", fileDescriptorResource) -} - -var fileDescriptorResource = []byte{ - // 397 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0xcf, 0x4e, 0xf2, 0x40, - 0x14, 0xc5, 0x19, 0x16, 0x24, 0xdf, 0x50, 0xf2, 0x69, 0x03, 0x91, 0x90, 0x58, 0x48, 0xdd, 0xa0, - 0x86, 0x36, 0x62, 0x8c, 0x6b, 0xfe, 0x6c, 0xba, 0x90, 0x45, 0x5f, 0xc0, 0x0c, 0xed, 0x50, 0x1a, - 0x68, 0xa7, 0x4e, 0xa7, 0x12, 0x77, 0x6e, 0x5d, 0xb9, 0xf5, 0x1d, 0x4c, 0x7c, 0x0e, 0xe2, 0xca, - 0xa5, 0x2b, 0x22, 0x7d, 0x00, 0x9f, 0xc1, 0xd0, 0x29, 0x10, 0x70, 0xa2, 0xc4, 0x55, 0xa7, 0xd3, - 0x73, 0xce, 0xfd, 0xdd, 0x7b, 0x0b, 0x1b, 0x8e, 0xcb, 0x86, 0x51, 0x5f, 0xb3, 0x88, 0xa7, 0xdb, - 0xc4, 0x1a, 0x61, 0xaa, 0x87, 0x13, 0x44, 0xbd, 0x91, 0xcb, 0x74, 0x14, 0xb8, 0x3a, 0xc5, 0x21, - 0x89, 0xa8, 0x85, 0xb5, 0x80, 0x12, 0x46, 0x64, 0x99, 0x6b, 0xb4, 0xa5, 0x46, 0xbb, 0x3d, 0xab, - 0x9c, 0xfc, 0x12, 0xc1, 0xee, 0x02, 0x1c, 0x72, 0x7f, 0xa5, 0xe8, 0x10, 0x87, 0x24, 0x47, 0x7d, - 0x71, 0x4a, 0x6f, 0x2f, 0x7f, 0x48, 0x48, 0x14, 0xfd, 0x68, 0xa0, 0x07, 0xe3, 0xc8, 0x71, 0xfd, - 0xf4, 0xc1, 0x8d, 0xea, 0x23, 0x80, 0xc5, 0x16, 0x63, 0xc8, 0x1a, 0xf6, 0x30, 0x9b, 0x10, 0x3a, - 0x32, 0xf1, 0x4d, 0x84, 0x43, 0x26, 0x77, 0x60, 0xce, 0x22, 0xfe, 0xc0, 0x75, 0xca, 0xa0, 0x06, - 0xea, 0xf9, 0xe6, 0xa9, 0xf6, 0x1d, 0x5c, 0x4b, 0x3d, 0x3c, 0xc0, 0xc3, 0x3e, 0xeb, 0x24, 0x16, - 0x33, 0xb5, 0xca, 0x4d, 0x28, 0x59, 0xc4, 0x67, 0xc8, 0xf5, 0x31, 0xbd, 0x76, 0xed, 0x72, 0xb6, - 0x06, 0xea, 0xff, 0xda, 0xff, 0xe3, 0x59, 0x35, 0xdf, 0x59, 0xde, 0x1b, 0x5d, 0x33, 0xbf, 0x12, - 0x19, 0xb6, 0xda, 0x83, 0xa5, 0x2d, 0xa0, 0x30, 0x20, 0x7e, 0x88, 0xe5, 0x0b, 0x58, 0x40, 0xab, - 0x42, 0x8b, 0x34, 0x90, 0xa4, 0xed, 0xc5, 0xb3, 0xaa, 0xb4, 0x26, 0x30, 0xba, 0xa6, 0xb4, 0x96, - 0x19, 0xb6, 0x7a, 0x05, 0x8b, 0x5d, 0x2c, 0x68, 0xf0, 0x8f, 0x71, 0x07, 0xb0, 0xb4, 0x15, 0xc7, - 0xf1, 0x9a, 0xcf, 0x59, 0xb8, 0x6f, 0xa6, 0xbb, 0x6e, 0x8d, 0xc7, 0xc4, 0x42, 0x8c, 0x50, 0xf9, - 0x01, 0xc0, 0xc2, 0x46, 0x3b, 0x72, 0x5d, 0x34, 0x48, 0xd1, 0x0a, 0x2a, 0xc7, 0x3b, 0x28, 0x79, - 0x71, 0xf5, 0xe8, 0xf5, 0xe5, 0xf3, 0x29, 0x7b, 0x08, 0xa5, 0x44, 0xda, 0x58, 0x7c, 0xc3, 0x14, - 0x16, 0xf8, 0x9b, 0x87, 0x7c, 0xe4, 0x60, 0xce, 0xb2, 0xc1, 0x2e, 0x66, 0x11, 0x4d, 0x4b, 0xcc, - 0x22, 0x1c, 0xc4, 0x4e, 0x2c, 0xed, 0xf2, 0x74, 0xae, 0x64, 0xde, 0xe7, 0x4a, 0xe6, 0x3e, 0x56, - 0xc0, 0x34, 0x56, 0xc0, 0x5b, 0xac, 0x80, 0x8f, 0x58, 0x01, 0xfd, 0x5c, 0xf2, 0x63, 0x9e, 0x7f, - 0x05, 0x00, 0x00, 0xff, 0xff, 0xc1, 0x7a, 0x29, 0xfc, 0x58, 0x03, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/snapshot.pb.go b/vendor/github.com/docker/swarmkit/api/snapshot.pb.go index 4d6893a90450d..e7f1c35e44554 100644 --- a/vendor/github.com/docker/swarmkit/api/snapshot.pb.go +++ b/vendor/github.com/docker/swarmkit/api/snapshot.pb.go @@ -3,24 +3,28 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type Snapshot_Version int32 const ( @@ -31,6 +35,7 @@ const ( var Snapshot_Version_name = map[int32]string{ 0: "V0", } + var Snapshot_Version_value = map[string]int32{ "V0": 0, } @@ -38,50 +43,177 @@ var Snapshot_Version_value = map[string]int32{ func (x Snapshot_Version) String() string { return proto.EnumName(Snapshot_Version_name, int32(x)) } -func (Snapshot_Version) EnumDescriptor() ([]byte, []int) { return fileDescriptorSnapshot, []int{2, 0} } + +func (Snapshot_Version) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_c5cad0b62cecd9af, []int{2, 0} +} // StoreSnapshot is used to store snapshots of the store. type StoreSnapshot struct { - Nodes []*Node `protobuf:"bytes,1,rep,name=nodes" json:"nodes,omitempty"` - Services []*Service `protobuf:"bytes,2,rep,name=services" json:"services,omitempty"` - Networks []*Network `protobuf:"bytes,3,rep,name=networks" json:"networks,omitempty"` - Tasks []*Task `protobuf:"bytes,4,rep,name=tasks" json:"tasks,omitempty"` - Clusters []*Cluster `protobuf:"bytes,5,rep,name=clusters" json:"clusters,omitempty"` - Secrets []*Secret `protobuf:"bytes,6,rep,name=secrets" json:"secrets,omitempty"` - Resources []*Resource `protobuf:"bytes,7,rep,name=resources" json:"resources,omitempty"` - Extensions []*Extension `protobuf:"bytes,8,rep,name=extensions" json:"extensions,omitempty"` - Configs []*Config `protobuf:"bytes,9,rep,name=configs" json:"configs,omitempty"` + Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"` + Services []*Service `protobuf:"bytes,2,rep,name=services,proto3" json:"services,omitempty"` + Networks []*Network `protobuf:"bytes,3,rep,name=networks,proto3" json:"networks,omitempty"` + Tasks []*Task `protobuf:"bytes,4,rep,name=tasks,proto3" json:"tasks,omitempty"` + Clusters []*Cluster `protobuf:"bytes,5,rep,name=clusters,proto3" json:"clusters,omitempty"` + Secrets []*Secret `protobuf:"bytes,6,rep,name=secrets,proto3" json:"secrets,omitempty"` + Resources []*Resource `protobuf:"bytes,7,rep,name=resources,proto3" json:"resources,omitempty"` + Extensions []*Extension `protobuf:"bytes,8,rep,name=extensions,proto3" json:"extensions,omitempty"` + Configs []*Config `protobuf:"bytes,9,rep,name=configs,proto3" json:"configs,omitempty"` +} + +func (m *StoreSnapshot) Reset() { *m = StoreSnapshot{} } +func (*StoreSnapshot) ProtoMessage() {} +func (*StoreSnapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_c5cad0b62cecd9af, []int{0} +} +func (m *StoreSnapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StoreSnapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StoreSnapshot.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StoreSnapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_StoreSnapshot.Merge(m, src) +} +func (m *StoreSnapshot) XXX_Size() int { + return m.Size() +} +func (m *StoreSnapshot) XXX_DiscardUnknown() { + xxx_messageInfo_StoreSnapshot.DiscardUnknown(m) } -func (m *StoreSnapshot) Reset() { *m = StoreSnapshot{} } -func (*StoreSnapshot) ProtoMessage() {} -func (*StoreSnapshot) Descriptor() ([]byte, []int) { return fileDescriptorSnapshot, []int{0} } +var xxx_messageInfo_StoreSnapshot proto.InternalMessageInfo // ClusterSnapshot stores cluster membership information in snapshots. type ClusterSnapshot struct { - Members []*RaftMember `protobuf:"bytes,1,rep,name=members" json:"members,omitempty"` - Removed []uint64 `protobuf:"varint,2,rep,name=removed" json:"removed,omitempty"` + Members []*RaftMember `protobuf:"bytes,1,rep,name=members,proto3" json:"members,omitempty"` + Removed []uint64 `protobuf:"varint,2,rep,name=removed,proto3" json:"removed,omitempty"` } -func (m *ClusterSnapshot) Reset() { *m = ClusterSnapshot{} } -func (*ClusterSnapshot) ProtoMessage() {} -func (*ClusterSnapshot) Descriptor() ([]byte, []int) { return fileDescriptorSnapshot, []int{1} } +func (m *ClusterSnapshot) Reset() { *m = ClusterSnapshot{} } +func (*ClusterSnapshot) ProtoMessage() {} +func (*ClusterSnapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_c5cad0b62cecd9af, []int{1} +} +func (m *ClusterSnapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClusterSnapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClusterSnapshot.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ClusterSnapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClusterSnapshot.Merge(m, src) +} +func (m *ClusterSnapshot) XXX_Size() int { + return m.Size() +} +func (m *ClusterSnapshot) XXX_DiscardUnknown() { + xxx_messageInfo_ClusterSnapshot.DiscardUnknown(m) +} + +var xxx_messageInfo_ClusterSnapshot proto.InternalMessageInfo type Snapshot struct { Version Snapshot_Version `protobuf:"varint,1,opt,name=version,proto3,enum=docker.swarmkit.v1.Snapshot_Version" json:"version,omitempty"` - Membership ClusterSnapshot `protobuf:"bytes,2,opt,name=membership" json:"membership"` - Store StoreSnapshot `protobuf:"bytes,3,opt,name=store" json:"store"` + Membership ClusterSnapshot `protobuf:"bytes,2,opt,name=membership,proto3" json:"membership"` + Store StoreSnapshot `protobuf:"bytes,3,opt,name=store,proto3" json:"store"` } -func (m *Snapshot) Reset() { *m = Snapshot{} } -func (*Snapshot) ProtoMessage() {} -func (*Snapshot) Descriptor() ([]byte, []int) { return fileDescriptorSnapshot, []int{2} } +func (m *Snapshot) Reset() { *m = Snapshot{} } +func (*Snapshot) ProtoMessage() {} +func (*Snapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_c5cad0b62cecd9af, []int{2} +} +func (m *Snapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Snapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Snapshot.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Snapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_Snapshot.Merge(m, src) +} +func (m *Snapshot) XXX_Size() int { + return m.Size() +} +func (m *Snapshot) XXX_DiscardUnknown() { + xxx_messageInfo_Snapshot.DiscardUnknown(m) +} + +var xxx_messageInfo_Snapshot proto.InternalMessageInfo func init() { + proto.RegisterEnum("docker.swarmkit.v1.Snapshot_Version", Snapshot_Version_name, Snapshot_Version_value) proto.RegisterType((*StoreSnapshot)(nil), "docker.swarmkit.v1.StoreSnapshot") proto.RegisterType((*ClusterSnapshot)(nil), "docker.swarmkit.v1.ClusterSnapshot") proto.RegisterType((*Snapshot)(nil), "docker.swarmkit.v1.Snapshot") - proto.RegisterEnum("docker.swarmkit.v1.Snapshot_Version", Snapshot_Version_name, Snapshot_Version_value) +} + +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/snapshot.proto", fileDescriptor_c5cad0b62cecd9af) +} + +var fileDescriptor_c5cad0b62cecd9af = []byte{ + // 507 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x93, 0xcf, 0x6f, 0xd3, 0x30, + 0x14, 0xc7, 0x93, 0xfe, 0x4a, 0xe7, 0x09, 0x18, 0x16, 0x07, 0xab, 0x0c, 0x53, 0xca, 0x0e, 0x45, + 0x82, 0x14, 0x0a, 0x12, 0x08, 0x69, 0x1c, 0x3a, 0x71, 0xe0, 0xc0, 0x0e, 0x2e, 0x9a, 0xb8, 0xa6, + 0xa9, 0xdb, 0x86, 0x90, 0xb8, 0xf2, 0x73, 0x3b, 0x8e, 0xf0, 0x1f, 0xf0, 0x67, 0xf5, 0xb8, 0xe3, + 0x4e, 0x88, 0xb5, 0x07, 0xfe, 0x0d, 0x64, 0x3b, 0x09, 0x95, 0x48, 0xb7, 0x5b, 0x64, 0x7d, 0x3e, + 0xef, 0x7d, 0xed, 0xbc, 0x87, 0x9e, 0x4d, 0x23, 0x35, 0x5b, 0x8c, 0xfc, 0x50, 0x24, 0xbd, 0xb1, + 0x08, 0x63, 0x2e, 0x7b, 0x70, 0x1e, 0xc8, 0x24, 0x8e, 0x54, 0x2f, 0x98, 0x47, 0x3d, 0x48, 0x83, + 0x39, 0xcc, 0x84, 0xf2, 0xe7, 0x52, 0x28, 0x81, 0xb1, 0x65, 0xfc, 0x9c, 0xf1, 0x97, 0x2f, 0x5a, + 0x4f, 0x6f, 0x28, 0x21, 0x46, 0x5f, 0x78, 0xa8, 0xc0, 0x56, 0x68, 0x3d, 0xb9, 0x81, 0x96, 0xc1, + 0x24, 0x6b, 0xd6, 0xba, 0x37, 0x15, 0x53, 0x61, 0x3e, 0x7b, 0xfa, 0xcb, 0x9e, 0x76, 0x7e, 0xd4, + 0xd0, 0xad, 0xa1, 0x12, 0x92, 0x0f, 0xb3, 0x68, 0xd8, 0x47, 0xf5, 0x54, 0x8c, 0x39, 0x10, 0xb7, + 0x5d, 0xed, 0xee, 0xf7, 0x89, 0xff, 0x7f, 0x48, 0xff, 0x54, 0x8c, 0x39, 0xb3, 0x18, 0x7e, 0x8d, + 0x9a, 0xc0, 0xe5, 0x32, 0x0a, 0x39, 0x90, 0x8a, 0x51, 0xee, 0x97, 0x29, 0x43, 0xcb, 0xb0, 0x02, + 0xd6, 0x62, 0xca, 0xd5, 0xb9, 0x90, 0x31, 0x90, 0xea, 0x6e, 0xf1, 0xd4, 0x32, 0xac, 0x80, 0x75, + 0x42, 0x15, 0x40, 0x0c, 0xa4, 0xb6, 0x3b, 0xe1, 0xa7, 0x00, 0x62, 0x66, 0x31, 0xdd, 0x28, 0xfc, + 0xba, 0x00, 0xc5, 0x25, 0x90, 0xfa, 0xee, 0x46, 0x27, 0x96, 0x61, 0x05, 0x8c, 0x5f, 0x21, 0x0f, + 0x78, 0x28, 0xb9, 0x02, 0xd2, 0x30, 0x5e, 0xab, 0xfc, 0x66, 0x1a, 0x61, 0x39, 0x8a, 0xdf, 0xa2, + 0x3d, 0xc9, 0x41, 0x2c, 0xa4, 0x7e, 0x11, 0xcf, 0x78, 0x87, 0x65, 0x1e, 0xcb, 0x20, 0xf6, 0x0f, + 0xc7, 0xc7, 0x08, 0xf1, 0x6f, 0x8a, 0xa7, 0x10, 0x89, 0x14, 0x48, 0xd3, 0xc8, 0x0f, 0xca, 0xe4, + 0xf7, 0x39, 0xc5, 0xb6, 0x04, 0x1d, 0x38, 0x14, 0xe9, 0x24, 0x9a, 0x02, 0xd9, 0xdb, 0x1d, 0xf8, + 0xc4, 0x20, 0x2c, 0x47, 0x3b, 0x11, 0xba, 0x93, 0xdd, 0xbd, 0x18, 0x82, 0x37, 0xc8, 0x4b, 0x78, + 0x32, 0xd2, 0x2f, 0x66, 0xc7, 0x80, 0x96, 0xde, 0x20, 0x98, 0xa8, 0x8f, 0x06, 0x63, 0x39, 0x8e, + 0x0f, 0x91, 0x27, 0x79, 0x22, 0x96, 0x7c, 0x6c, 0xa6, 0xa1, 0x36, 0xa8, 0x1c, 0x38, 0x2c, 0x3f, + 0xea, 0xfc, 0x71, 0x51, 0xb3, 0x68, 0xf2, 0x0e, 0x79, 0x4b, 0x2e, 0x75, 0x72, 0xe2, 0xb6, 0xdd, + 0xee, 0xed, 0xfe, 0x51, 0xe9, 0xf3, 0xe6, 0x3b, 0x73, 0x66, 0x59, 0x96, 0x4b, 0xf8, 0x03, 0x42, + 0x59, 0xd7, 0x59, 0x34, 0x27, 0x95, 0xb6, 0xdb, 0xdd, 0xef, 0x3f, 0xbe, 0xe6, 0xcf, 0xe6, 0x95, + 0x06, 0xb5, 0xd5, 0xaf, 0x87, 0x0e, 0xdb, 0x92, 0xf1, 0x31, 0xaa, 0x83, 0xde, 0x02, 0x52, 0x35, + 0x55, 0x1e, 0x95, 0x06, 0xd9, 0x5e, 0x93, 0xac, 0x86, 0xb5, 0x3a, 0x77, 0x91, 0x97, 0xa5, 0xc3, + 0x0d, 0x54, 0x39, 0x7b, 0x7e, 0xe0, 0x0c, 0x8e, 0x56, 0x57, 0xd4, 0xb9, 0xbc, 0xa2, 0xce, 0xf7, + 0x35, 0x75, 0x57, 0x6b, 0xea, 0x5e, 0xac, 0xa9, 0xfb, 0x7b, 0x4d, 0xdd, 0x9f, 0x1b, 0xea, 0x5c, + 0x6c, 0xa8, 0x73, 0xb9, 0xa1, 0xce, 0xe7, 0xca, 0xa8, 0x61, 0xf6, 0xf0, 0xe5, 0xdf, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x97, 0x4e, 0xfd, 0x2a, 0x3b, 0x04, 0x00, 0x00, } func (m *StoreSnapshot) Copy() *StoreSnapshot { @@ -101,7 +233,7 @@ func (m *StoreSnapshot) CopyFrom(src interface{}) { m.Nodes = make([]*Node, len(o.Nodes)) for i := range m.Nodes { m.Nodes[i] = &Node{} - deepcopy.Copy(m.Nodes[i], o.Nodes[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Nodes[i], o.Nodes[i]) } } @@ -109,7 +241,7 @@ func (m *StoreSnapshot) CopyFrom(src interface{}) { m.Services = make([]*Service, len(o.Services)) for i := range m.Services { m.Services[i] = &Service{} - deepcopy.Copy(m.Services[i], o.Services[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Services[i], o.Services[i]) } } @@ -117,7 +249,7 @@ func (m *StoreSnapshot) CopyFrom(src interface{}) { m.Networks = make([]*Network, len(o.Networks)) for i := range m.Networks { m.Networks[i] = &Network{} - deepcopy.Copy(m.Networks[i], o.Networks[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Networks[i], o.Networks[i]) } } @@ -125,7 +257,7 @@ func (m *StoreSnapshot) CopyFrom(src interface{}) { m.Tasks = make([]*Task, len(o.Tasks)) for i := range m.Tasks { m.Tasks[i] = &Task{} - deepcopy.Copy(m.Tasks[i], o.Tasks[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Tasks[i], o.Tasks[i]) } } @@ -133,7 +265,7 @@ func (m *StoreSnapshot) CopyFrom(src interface{}) { m.Clusters = make([]*Cluster, len(o.Clusters)) for i := range m.Clusters { m.Clusters[i] = &Cluster{} - deepcopy.Copy(m.Clusters[i], o.Clusters[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Clusters[i], o.Clusters[i]) } } @@ -141,7 +273,7 @@ func (m *StoreSnapshot) CopyFrom(src interface{}) { m.Secrets = make([]*Secret, len(o.Secrets)) for i := range m.Secrets { m.Secrets[i] = &Secret{} - deepcopy.Copy(m.Secrets[i], o.Secrets[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Secrets[i], o.Secrets[i]) } } @@ -149,7 +281,7 @@ func (m *StoreSnapshot) CopyFrom(src interface{}) { m.Resources = make([]*Resource, len(o.Resources)) for i := range m.Resources { m.Resources[i] = &Resource{} - deepcopy.Copy(m.Resources[i], o.Resources[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Resources[i], o.Resources[i]) } } @@ -157,7 +289,7 @@ func (m *StoreSnapshot) CopyFrom(src interface{}) { m.Extensions = make([]*Extension, len(o.Extensions)) for i := range m.Extensions { m.Extensions[i] = &Extension{} - deepcopy.Copy(m.Extensions[i], o.Extensions[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Extensions[i], o.Extensions[i]) } } @@ -165,7 +297,7 @@ func (m *StoreSnapshot) CopyFrom(src interface{}) { m.Configs = make([]*Config, len(o.Configs)) for i := range m.Configs { m.Configs[i] = &Config{} - deepcopy.Copy(m.Configs[i], o.Configs[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Configs[i], o.Configs[i]) } } @@ -188,7 +320,7 @@ func (m *ClusterSnapshot) CopyFrom(src interface{}) { m.Members = make([]*RaftMember, len(o.Members)) for i := range m.Members { m.Members[i] = &RaftMember{} - deepcopy.Copy(m.Members[i], o.Members[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Members[i], o.Members[i]) } } @@ -212,14 +344,14 @@ func (m *Snapshot) CopyFrom(src interface{}) { o := src.(*Snapshot) *m = *o - deepcopy.Copy(&m.Membership, &o.Membership) - deepcopy.Copy(&m.Store, &o.Store) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Membership, &o.Membership) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Store, &o.Store) } func (m *StoreSnapshot) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -227,125 +359,148 @@ func (m *StoreSnapshot) Marshal() (dAtA []byte, err error) { } func (m *StoreSnapshot) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Nodes) > 0 { - for _, msg := range m.Nodes { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Configs) > 0 { + for iNdEx := len(m.Configs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Configs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x4a } } - if len(m.Services) > 0 { - for _, msg := range m.Services { - dAtA[i] = 0x12 - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Extensions) > 0 { + for iNdEx := len(m.Extensions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Extensions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x42 } } - if len(m.Networks) > 0 { - for _, msg := range m.Networks { - dAtA[i] = 0x1a - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Resources) > 0 { + for iNdEx := len(m.Resources) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Resources[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x3a } } - if len(m.Tasks) > 0 { - for _, msg := range m.Tasks { - dAtA[i] = 0x22 - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Secrets) > 0 { + for iNdEx := len(m.Secrets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Secrets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x32 } } if len(m.Clusters) > 0 { - for _, msg := range m.Clusters { - dAtA[i] = 0x2a - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Clusters) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Clusters[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x2a } } - if len(m.Secrets) > 0 { - for _, msg := range m.Secrets { - dAtA[i] = 0x32 - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Tasks) > 0 { + for iNdEx := len(m.Tasks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Tasks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x22 } } - if len(m.Resources) > 0 { - for _, msg := range m.Resources { - dAtA[i] = 0x3a - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Networks) > 0 { + for iNdEx := len(m.Networks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Networks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x1a } } - if len(m.Extensions) > 0 { - for _, msg := range m.Extensions { - dAtA[i] = 0x42 - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Services) > 0 { + for iNdEx := len(m.Services) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Services[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x12 } } - if len(m.Configs) > 0 { - for _, msg := range m.Configs { - dAtA[i] = 0x4a - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Nodes) > 0 { + for iNdEx := len(m.Nodes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Nodes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *ClusterSnapshot) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -353,36 +508,43 @@ func (m *ClusterSnapshot) Marshal() (dAtA []byte, err error) { } func (m *ClusterSnapshot) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClusterSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Members) > 0 { - for _, msg := range m.Members { - dAtA[i] = 0xa - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } - } if len(m.Removed) > 0 { - for _, num := range m.Removed { + for iNdEx := len(m.Removed) - 1; iNdEx >= 0; iNdEx-- { + i = encodeVarintSnapshot(dAtA, i, uint64(m.Removed[iNdEx])) + i-- dAtA[i] = 0x10 - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(num)) } } - return i, nil + if len(m.Members) > 0 { + for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil } func (m *Snapshot) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -390,45 +552,58 @@ func (m *Snapshot) Marshal() (dAtA []byte, err error) { } func (m *Snapshot) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Snapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Version != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(m.Version)) - } - dAtA[i] = 0x12 - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(m.Membership.Size())) - n1, err := m.Membership.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Store.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n1 + i-- dAtA[i] = 0x1a - i++ - i = encodeVarintSnapshot(dAtA, i, uint64(m.Store.Size())) - n2, err := m.Store.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Membership.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSnapshot(dAtA, i, uint64(size)) } - i += n2 - return i, nil + i-- + dAtA[i] = 0x12 + if m.Version != 0 { + i = encodeVarintSnapshot(dAtA, i, uint64(m.Version)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func encodeVarintSnapshot(dAtA []byte, offset int, v uint64) int { + offset -= sovSnapshot(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } - func (m *StoreSnapshot) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Nodes) > 0 { @@ -489,6 +664,9 @@ func (m *StoreSnapshot) Size() (n int) { } func (m *ClusterSnapshot) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Members) > 0 { @@ -506,6 +684,9 @@ func (m *ClusterSnapshot) Size() (n int) { } func (m *Snapshot) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Version != 0 { @@ -519,14 +700,7 @@ func (m *Snapshot) Size() (n int) { } func sovSnapshot(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozSnapshot(x uint64) (n int) { return sovSnapshot(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -535,16 +709,61 @@ func (this *StoreSnapshot) String() string { if this == nil { return "nil" } + repeatedStringForNodes := "[]*Node{" + for _, f := range this.Nodes { + repeatedStringForNodes += strings.Replace(fmt.Sprintf("%v", f), "Node", "Node", 1) + "," + } + repeatedStringForNodes += "}" + repeatedStringForServices := "[]*Service{" + for _, f := range this.Services { + repeatedStringForServices += strings.Replace(fmt.Sprintf("%v", f), "Service", "Service", 1) + "," + } + repeatedStringForServices += "}" + repeatedStringForNetworks := "[]*Network{" + for _, f := range this.Networks { + repeatedStringForNetworks += strings.Replace(fmt.Sprintf("%v", f), "Network", "Network", 1) + "," + } + repeatedStringForNetworks += "}" + repeatedStringForTasks := "[]*Task{" + for _, f := range this.Tasks { + repeatedStringForTasks += strings.Replace(fmt.Sprintf("%v", f), "Task", "Task", 1) + "," + } + repeatedStringForTasks += "}" + repeatedStringForClusters := "[]*Cluster{" + for _, f := range this.Clusters { + repeatedStringForClusters += strings.Replace(fmt.Sprintf("%v", f), "Cluster", "Cluster", 1) + "," + } + repeatedStringForClusters += "}" + repeatedStringForSecrets := "[]*Secret{" + for _, f := range this.Secrets { + repeatedStringForSecrets += strings.Replace(fmt.Sprintf("%v", f), "Secret", "Secret", 1) + "," + } + repeatedStringForSecrets += "}" + repeatedStringForResources := "[]*Resource{" + for _, f := range this.Resources { + repeatedStringForResources += strings.Replace(fmt.Sprintf("%v", f), "Resource", "Resource", 1) + "," + } + repeatedStringForResources += "}" + repeatedStringForExtensions := "[]*Extension{" + for _, f := range this.Extensions { + repeatedStringForExtensions += strings.Replace(fmt.Sprintf("%v", f), "Extension", "Extension", 1) + "," + } + repeatedStringForExtensions += "}" + repeatedStringForConfigs := "[]*Config{" + for _, f := range this.Configs { + repeatedStringForConfigs += strings.Replace(fmt.Sprintf("%v", f), "Config", "Config", 1) + "," + } + repeatedStringForConfigs += "}" s := strings.Join([]string{`&StoreSnapshot{`, - `Nodes:` + strings.Replace(fmt.Sprintf("%v", this.Nodes), "Node", "Node", 1) + `,`, - `Services:` + strings.Replace(fmt.Sprintf("%v", this.Services), "Service", "Service", 1) + `,`, - `Networks:` + strings.Replace(fmt.Sprintf("%v", this.Networks), "Network", "Network", 1) + `,`, - `Tasks:` + strings.Replace(fmt.Sprintf("%v", this.Tasks), "Task", "Task", 1) + `,`, - `Clusters:` + strings.Replace(fmt.Sprintf("%v", this.Clusters), "Cluster", "Cluster", 1) + `,`, - `Secrets:` + strings.Replace(fmt.Sprintf("%v", this.Secrets), "Secret", "Secret", 1) + `,`, - `Resources:` + strings.Replace(fmt.Sprintf("%v", this.Resources), "Resource", "Resource", 1) + `,`, - `Extensions:` + strings.Replace(fmt.Sprintf("%v", this.Extensions), "Extension", "Extension", 1) + `,`, - `Configs:` + strings.Replace(fmt.Sprintf("%v", this.Configs), "Config", "Config", 1) + `,`, + `Nodes:` + repeatedStringForNodes + `,`, + `Services:` + repeatedStringForServices + `,`, + `Networks:` + repeatedStringForNetworks + `,`, + `Tasks:` + repeatedStringForTasks + `,`, + `Clusters:` + repeatedStringForClusters + `,`, + `Secrets:` + repeatedStringForSecrets + `,`, + `Resources:` + repeatedStringForResources + `,`, + `Extensions:` + repeatedStringForExtensions + `,`, + `Configs:` + repeatedStringForConfigs + `,`, `}`, }, "") return s @@ -553,8 +772,13 @@ func (this *ClusterSnapshot) String() string { if this == nil { return "nil" } + repeatedStringForMembers := "[]*RaftMember{" + for _, f := range this.Members { + repeatedStringForMembers += strings.Replace(fmt.Sprintf("%v", f), "RaftMember", "RaftMember", 1) + "," + } + repeatedStringForMembers += "}" s := strings.Join([]string{`&ClusterSnapshot{`, - `Members:` + strings.Replace(fmt.Sprintf("%v", this.Members), "RaftMember", "RaftMember", 1) + `,`, + `Members:` + repeatedStringForMembers + `,`, `Removed:` + fmt.Sprintf("%v", this.Removed) + `,`, `}`, }, "") @@ -595,7 +819,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -623,7 +847,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -632,6 +856,9 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -654,7 +881,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -663,6 +890,9 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -685,7 +915,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -694,6 +924,9 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -716,7 +949,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -725,6 +958,9 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -747,7 +983,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -756,6 +992,9 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -778,7 +1017,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -787,6 +1026,9 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -809,7 +1051,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -818,6 +1060,9 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -840,7 +1085,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -849,6 +1094,9 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -871,7 +1119,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -880,6 +1128,9 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -894,7 +1145,7 @@ func (m *StoreSnapshot) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { @@ -924,7 +1175,7 @@ func (m *ClusterSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -952,7 +1203,7 @@ func (m *ClusterSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -961,6 +1212,9 @@ func (m *ClusterSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -981,7 +1235,7 @@ func (m *ClusterSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (uint64(b) & 0x7F) << shift + v |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -998,7 +1252,7 @@ func (m *ClusterSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - packedLen |= (int(b) & 0x7F) << shift + packedLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1007,9 +1261,23 @@ func (m *ClusterSnapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.Removed) == 0 { + m.Removed = make([]uint64, 0, elementCount) + } for iNdEx < postIndex { var v uint64 for shift := uint(0); ; shift += 7 { @@ -1021,7 +1289,7 @@ func (m *ClusterSnapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (uint64(b) & 0x7F) << shift + v |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1037,7 +1305,7 @@ func (m *ClusterSnapshot) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { @@ -1067,7 +1335,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1095,7 +1363,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Version |= (Snapshot_Version(b) & 0x7F) << shift + m.Version |= Snapshot_Version(b&0x7F) << shift if b < 0x80 { break } @@ -1114,7 +1382,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1123,6 +1391,9 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1144,7 +1415,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1153,6 +1424,9 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSnapshot } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSnapshot + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1166,7 +1440,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSnapshot } if (iNdEx + skippy) > l { @@ -1184,6 +1458,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { func skipSnapshot(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1215,10 +1490,8 @@ func skipSnapshot(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1235,92 +1508,34 @@ func skipSnapshot(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthSnapshot } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowSnapshot - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipSnapshot(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSnapshot + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthSnapshot + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthSnapshot = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowSnapshot = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthSnapshot = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSnapshot = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSnapshot = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/docker/swarmkit/api/snapshot.proto", fileDescriptorSnapshot) -} - -var fileDescriptorSnapshot = []byte{ - // 493 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x93, 0xbf, 0x8f, 0xd3, 0x30, - 0x14, 0xc7, 0x93, 0xf4, 0x47, 0x7a, 0x3e, 0x01, 0x87, 0xc5, 0x60, 0x95, 0x23, 0x94, 0xc2, 0x50, - 0x24, 0x48, 0xa0, 0x20, 0x81, 0x90, 0x8e, 0xa1, 0x27, 0x06, 0x06, 0x6e, 0x70, 0xd1, 0x89, 0x35, - 0x4d, 0xdd, 0x36, 0x84, 0xc4, 0x95, 0x9f, 0xdb, 0x63, 0x84, 0xff, 0xae, 0x23, 0x23, 0x13, 0xe2, - 0xba, 0xf0, 0x6f, 0x20, 0xdb, 0x71, 0xa8, 0x44, 0x7a, 0xb7, 0x45, 0xd6, 0xe7, 0xf3, 0xde, 0xd7, - 0xce, 0x7b, 0xe8, 0xe9, 0x3c, 0x95, 0x8b, 0xd5, 0x24, 0x4c, 0x78, 0x1e, 0x4d, 0x79, 0x92, 0x31, - 0x11, 0xc1, 0x45, 0x2c, 0xf2, 0x2c, 0x95, 0x51, 0xbc, 0x4c, 0x23, 0x28, 0xe2, 0x25, 0x2c, 0xb8, - 0x0c, 0x97, 0x82, 0x4b, 0x8e, 0xb1, 0x61, 0x42, 0xcb, 0x84, 0xeb, 0xe7, 0xdd, 0x27, 0xd7, 0x94, - 0xe0, 0x93, 0xcf, 0x2c, 0x91, 0x60, 0x2a, 0x74, 0x1f, 0x5f, 0x43, 0x8b, 0x78, 0x56, 0x36, 0xeb, - 0xde, 0x99, 0xf3, 0x39, 0xd7, 0x9f, 0x91, 0xfa, 0x32, 0xa7, 0xfd, 0xef, 0x4d, 0x74, 0x63, 0x2c, - 0xb9, 0x60, 0xe3, 0x32, 0x1a, 0x0e, 0x51, 0xab, 0xe0, 0x53, 0x06, 0xc4, 0xed, 0x35, 0x06, 0x87, - 0x43, 0x12, 0xfe, 0x1f, 0x32, 0x3c, 0xe3, 0x53, 0x46, 0x0d, 0x86, 0x5f, 0xa1, 0x0e, 0x30, 0xb1, - 0x4e, 0x13, 0x06, 0xc4, 0xd3, 0xca, 0xdd, 0x3a, 0x65, 0x6c, 0x18, 0x5a, 0xc1, 0x4a, 0x2c, 0x98, - 0xbc, 0xe0, 0x22, 0x03, 0xd2, 0xd8, 0x2f, 0x9e, 0x19, 0x86, 0x56, 0xb0, 0x4a, 0x28, 0x63, 0xc8, - 0x80, 0x34, 0xf7, 0x27, 0xfc, 0x18, 0x43, 0x46, 0x0d, 0xa6, 0x1a, 0x25, 0x5f, 0x56, 0x20, 0x99, - 0x00, 0xd2, 0xda, 0xdf, 0xe8, 0xd4, 0x30, 0xb4, 0x82, 0xf1, 0x4b, 0xe4, 0x03, 0x4b, 0x04, 0x93, - 0x40, 0xda, 0xda, 0xeb, 0xd6, 0xdf, 0x4c, 0x21, 0xd4, 0xa2, 0xf8, 0x0d, 0x3a, 0x10, 0x0c, 0xf8, - 0x4a, 0xa8, 0x17, 0xf1, 0xb5, 0x77, 0x5c, 0xe7, 0xd1, 0x12, 0xa2, 0xff, 0x70, 0x7c, 0x82, 0x10, - 0xfb, 0x2a, 0x59, 0x01, 0x29, 0x2f, 0x80, 0x74, 0xb4, 0x7c, 0xaf, 0x4e, 0x7e, 0x67, 0x29, 0xba, - 0x23, 0xa8, 0xc0, 0x09, 0x2f, 0x66, 0xe9, 0x1c, 0xc8, 0xc1, 0xfe, 0xc0, 0xa7, 0x1a, 0xa1, 0x16, - 0xed, 0xa7, 0xe8, 0x56, 0x79, 0xf7, 0x6a, 0x08, 0x5e, 0x23, 0x3f, 0x67, 0xf9, 0x44, 0xbd, 0x98, - 0x19, 0x83, 0xa0, 0xf6, 0x06, 0xf1, 0x4c, 0x7e, 0xd0, 0x18, 0xb5, 0x38, 0x3e, 0x46, 0xbe, 0x60, - 0x39, 0x5f, 0xb3, 0xa9, 0x9e, 0x86, 0xe6, 0xc8, 0x3b, 0x72, 0xa8, 0x3d, 0xea, 0xff, 0x71, 0x51, - 0xa7, 0x6a, 0xf2, 0x16, 0xf9, 0x6b, 0x26, 0x54, 0x72, 0xe2, 0xf6, 0xdc, 0xc1, 0xcd, 0xe1, 0xa3, - 0xda, 0xe7, 0xb5, 0x3b, 0x73, 0x6e, 0x58, 0x6a, 0x25, 0xfc, 0x1e, 0xa1, 0xb2, 0xeb, 0x22, 0x5d, - 0x12, 0xaf, 0xe7, 0x0e, 0x0e, 0x87, 0x0f, 0xaf, 0xf8, 0xb3, 0xb6, 0xd2, 0xa8, 0xb9, 0xf9, 0x75, - 0xdf, 0xa1, 0x3b, 0x32, 0x3e, 0x41, 0x2d, 0x50, 0x5b, 0x40, 0x1a, 0xba, 0xca, 0x83, 0xda, 0x20, - 0xbb, 0x6b, 0x52, 0xd6, 0x30, 0x56, 0xff, 0x36, 0xf2, 0xcb, 0x74, 0xb8, 0x8d, 0xbc, 0xf3, 0x67, - 0x47, 0xce, 0x88, 0x6c, 0x2e, 0x03, 0xe7, 0xe7, 0x65, 0xe0, 0x7c, 0xdb, 0x06, 0xee, 0x66, 0x1b, - 0xb8, 0x3f, 0xb6, 0x81, 0xfb, 0x7b, 0x1b, 0xb8, 0x9f, 0xbc, 0x49, 0x5b, 0xef, 0xde, 0x8b, 0xbf, - 0x01, 0x00, 0x00, 0xff, 0xff, 0xfd, 0xbe, 0x47, 0xec, 0x2f, 0x04, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/specs.pb.go b/vendor/github.com/docker/swarmkit/api/specs.pb.go index 3adb9129b0939..c04c13a88e177 100644 --- a/vendor/github.com/docker/swarmkit/api/specs.pb.go +++ b/vendor/github.com/docker/swarmkit/api/specs.pb.go @@ -3,27 +3,31 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" -import google_protobuf1 "github.com/gogo/protobuf/types" -import google_protobuf3 "github.com/gogo/protobuf/types" -import google_protobuf4 "github.com/gogo/protobuf/types" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + fmt "fmt" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + types "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type NodeSpec_Membership int32 const ( @@ -35,6 +39,7 @@ var NodeSpec_Membership_name = map[int32]string{ 0: "PENDING", 1: "ACCEPTED", } + var NodeSpec_Membership_value = map[string]int32{ "PENDING": 0, "ACCEPTED": 1, @@ -43,7 +48,10 @@ var NodeSpec_Membership_value = map[string]int32{ func (x NodeSpec_Membership) String() string { return proto.EnumName(NodeSpec_Membership_name, int32(x)) } -func (NodeSpec_Membership) EnumDescriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{0, 0} } + +func (NodeSpec_Membership) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{0, 0} +} type NodeSpec_Availability int32 @@ -63,6 +71,7 @@ var NodeSpec_Availability_name = map[int32]string{ 1: "PAUSE", 2: "DRAIN", } + var NodeSpec_Availability_value = map[string]int32{ "ACTIVE": 0, "PAUSE": 1, @@ -72,7 +81,10 @@ var NodeSpec_Availability_value = map[string]int32{ func (x NodeSpec_Availability) String() string { return proto.EnumName(NodeSpec_Availability_name, int32(x)) } -func (NodeSpec_Availability) EnumDescriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{0, 1} } + +func (NodeSpec_Availability) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{0, 1} +} type ContainerSpec_Isolation int32 @@ -90,6 +102,7 @@ var ContainerSpec_Isolation_name = map[int32]string{ 1: "ISOLATION_PROCESS", 2: "ISOLATION_HYPERV", } + var ContainerSpec_Isolation_value = map[string]int32{ "ISOLATION_DEFAULT": 0, "ISOLATION_PROCESS": 1, @@ -99,8 +112,9 @@ var ContainerSpec_Isolation_value = map[string]int32{ func (x ContainerSpec_Isolation) String() string { return proto.EnumName(ContainerSpec_Isolation_name, int32(x)) } + func (ContainerSpec_Isolation) EnumDescriptor() ([]byte, []int) { - return fileDescriptorSpecs, []int{8, 0} + return fileDescriptor_6589acc608f7d4fd, []int{10, 0} } // ResolutionMode specifies the mode of resolution to use for @@ -127,6 +141,7 @@ var EndpointSpec_ResolutionMode_name = map[int32]string{ 0: "VIP", 1: "DNSRR", } + var EndpointSpec_ResolutionMode_value = map[string]int32{ "VIP": 0, "DNSRR": 1, @@ -135,12 +150,13 @@ var EndpointSpec_ResolutionMode_value = map[string]int32{ func (x EndpointSpec_ResolutionMode) String() string { return proto.EnumName(EndpointSpec_ResolutionMode_name, int32(x)) } + func (EndpointSpec_ResolutionMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptorSpecs, []int{9, 0} + return fileDescriptor_6589acc608f7d4fd, []int{11, 0} } type NodeSpec struct { - Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"` + Annotations Annotations `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations"` // DesiredRole defines the role the node should have. DesiredRole NodeRole `protobuf:"varint,2,opt,name=desired_role,json=desiredRole,proto3,enum=docker.swarmkit.v1.NodeRole" json:"desired_role,omitempty"` // Membership controls the admission of the node into the cluster. @@ -150,9 +166,37 @@ type NodeSpec struct { Availability NodeSpec_Availability `protobuf:"varint,4,opt,name=availability,proto3,enum=docker.swarmkit.v1.NodeSpec_Availability" json:"availability,omitempty"` } -func (m *NodeSpec) Reset() { *m = NodeSpec{} } -func (*NodeSpec) ProtoMessage() {} -func (*NodeSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{0} } +func (m *NodeSpec) Reset() { *m = NodeSpec{} } +func (*NodeSpec) ProtoMessage() {} +func (*NodeSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{0} +} +func (m *NodeSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NodeSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NodeSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NodeSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_NodeSpec.Merge(m, src) +} +func (m *NodeSpec) XXX_Size() int { + return m.Size() +} +func (m *NodeSpec) XXX_DiscardUnknown() { + xxx_messageInfo_NodeSpec.DiscardUnknown(m) +} + +var xxx_messageInfo_NodeSpec proto.InternalMessageInfo // ServiceSpec defines the properties of a service. // @@ -161,31 +205,61 @@ func (*NodeSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []in // strategy and restart policy, a number of application-level behaviors can be // defined. type ServiceSpec struct { - Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"` + Annotations Annotations `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations"` // Task defines the task template this service will spawn. - Task TaskSpec `protobuf:"bytes,2,opt,name=task" json:"task"` + Task TaskSpec `protobuf:"bytes,2,opt,name=task,proto3" json:"task"` // Types that are valid to be assigned to Mode: // *ServiceSpec_Replicated // *ServiceSpec_Global + // *ServiceSpec_ReplicatedJob + // *ServiceSpec_GlobalJob Mode isServiceSpec_Mode `protobuf_oneof:"mode"` // Update contains settings which affect updates. - Update *UpdateConfig `protobuf:"bytes,6,opt,name=update" json:"update,omitempty"` + Update *UpdateConfig `protobuf:"bytes,6,opt,name=update,proto3" json:"update,omitempty"` // Rollback contains settings which affect rollbacks of updates. - Rollback *UpdateConfig `protobuf:"bytes,9,opt,name=rollback" json:"rollback,omitempty"` + Rollback *UpdateConfig `protobuf:"bytes,9,opt,name=rollback,proto3" json:"rollback,omitempty"` // ServiceSpec.Networks has been deprecated and is replaced by // Networks field in Task (TaskSpec.Networks). // This field (ServiceSpec.Networks) is kept for compatibility. // In case TaskSpec.Networks does not exist, ServiceSpec.Networks // is still honored if it exists. - Networks []*NetworkAttachmentConfig `protobuf:"bytes,7,rep,name=networks" json:"networks,omitempty"` + Networks []*NetworkAttachmentConfig `protobuf:"bytes,7,rep,name=networks,proto3" json:"networks,omitempty"` // Deprecated: Do not use. // Service endpoint specifies the user provided configuration // to properly discover and load balance a service. - Endpoint *EndpointSpec `protobuf:"bytes,8,opt,name=endpoint" json:"endpoint,omitempty"` + Endpoint *EndpointSpec `protobuf:"bytes,8,opt,name=endpoint,proto3" json:"endpoint,omitempty"` +} + +func (m *ServiceSpec) Reset() { *m = ServiceSpec{} } +func (*ServiceSpec) ProtoMessage() {} +func (*ServiceSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{1} +} +func (m *ServiceSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ServiceSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ServiceSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ServiceSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServiceSpec.Merge(m, src) +} +func (m *ServiceSpec) XXX_Size() int { + return m.Size() +} +func (m *ServiceSpec) XXX_DiscardUnknown() { + xxx_messageInfo_ServiceSpec.DiscardUnknown(m) } -func (m *ServiceSpec) Reset() { *m = ServiceSpec{} } -func (*ServiceSpec) ProtoMessage() {} -func (*ServiceSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{1} } +var xxx_messageInfo_ServiceSpec proto.InternalMessageInfo type isServiceSpec_Mode interface { isServiceSpec_Mode() @@ -194,14 +268,22 @@ type isServiceSpec_Mode interface { } type ServiceSpec_Replicated struct { - Replicated *ReplicatedService `protobuf:"bytes,3,opt,name=replicated,oneof"` + Replicated *ReplicatedService `protobuf:"bytes,3,opt,name=replicated,proto3,oneof" json:"replicated,omitempty"` } type ServiceSpec_Global struct { - Global *GlobalService `protobuf:"bytes,4,opt,name=global,oneof"` + Global *GlobalService `protobuf:"bytes,4,opt,name=global,proto3,oneof" json:"global,omitempty"` +} +type ServiceSpec_ReplicatedJob struct { + ReplicatedJob *ReplicatedJob `protobuf:"bytes,10,opt,name=replicated_job,json=replicatedJob,proto3,oneof" json:"replicated_job,omitempty"` +} +type ServiceSpec_GlobalJob struct { + GlobalJob *GlobalJob `protobuf:"bytes,11,opt,name=global_job,json=globalJob,proto3,oneof" json:"global_job,omitempty"` } -func (*ServiceSpec_Replicated) isServiceSpec_Mode() {} -func (*ServiceSpec_Global) isServiceSpec_Mode() {} +func (*ServiceSpec_Replicated) isServiceSpec_Mode() {} +func (*ServiceSpec_Global) isServiceSpec_Mode() {} +func (*ServiceSpec_ReplicatedJob) isServiceSpec_Mode() {} +func (*ServiceSpec_GlobalJob) isServiceSpec_Mode() {} func (m *ServiceSpec) GetMode() isServiceSpec_Mode { if m != nil { @@ -224,78 +306,28 @@ func (m *ServiceSpec) GetGlobal() *GlobalService { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*ServiceSpec) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _ServiceSpec_OneofMarshaler, _ServiceSpec_OneofUnmarshaler, _ServiceSpec_OneofSizer, []interface{}{ - (*ServiceSpec_Replicated)(nil), - (*ServiceSpec_Global)(nil), +func (m *ServiceSpec) GetReplicatedJob() *ReplicatedJob { + if x, ok := m.GetMode().(*ServiceSpec_ReplicatedJob); ok { + return x.ReplicatedJob } + return nil } -func _ServiceSpec_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*ServiceSpec) - // mode - switch x := m.Mode.(type) { - case *ServiceSpec_Replicated: - _ = b.EncodeVarint(3<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Replicated); err != nil { - return err - } - case *ServiceSpec_Global: - _ = b.EncodeVarint(4<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Global); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("ServiceSpec.Mode has unexpected type %T", x) +func (m *ServiceSpec) GetGlobalJob() *GlobalJob { + if x, ok := m.GetMode().(*ServiceSpec_GlobalJob); ok { + return x.GlobalJob } return nil } -func _ServiceSpec_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*ServiceSpec) - switch tag { - case 3: // mode.replicated - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(ReplicatedService) - err := b.DecodeMessage(msg) - m.Mode = &ServiceSpec_Replicated{msg} - return true, err - case 4: // mode.global - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(GlobalService) - err := b.DecodeMessage(msg) - m.Mode = &ServiceSpec_Global{msg} - return true, err - default: - return false, nil - } -} - -func _ServiceSpec_OneofSizer(msg proto.Message) (n int) { - m := msg.(*ServiceSpec) - // mode - switch x := m.Mode.(type) { - case *ServiceSpec_Replicated: - s := proto.Size(x.Replicated) - n += proto.SizeVarint(3<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *ServiceSpec_Global: - s := proto.Size(x.Global) - n += proto.SizeVarint(4<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) +// XXX_OneofWrappers is for the internal use of the proto package. +func (*ServiceSpec) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*ServiceSpec_Replicated)(nil), + (*ServiceSpec_Global)(nil), + (*ServiceSpec_ReplicatedJob)(nil), + (*ServiceSpec_GlobalJob)(nil), } - return n } // ReplicatedService sets the reconciliation target to certain number of replicas. @@ -303,17 +335,155 @@ type ReplicatedService struct { Replicas uint64 `protobuf:"varint,1,opt,name=replicas,proto3" json:"replicas,omitempty"` } -func (m *ReplicatedService) Reset() { *m = ReplicatedService{} } -func (*ReplicatedService) ProtoMessage() {} -func (*ReplicatedService) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{2} } +func (m *ReplicatedService) Reset() { *m = ReplicatedService{} } +func (*ReplicatedService) ProtoMessage() {} +func (*ReplicatedService) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{2} +} +func (m *ReplicatedService) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReplicatedService) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReplicatedService.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ReplicatedService) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReplicatedService.Merge(m, src) +} +func (m *ReplicatedService) XXX_Size() int { + return m.Size() +} +func (m *ReplicatedService) XXX_DiscardUnknown() { + xxx_messageInfo_ReplicatedService.DiscardUnknown(m) +} + +var xxx_messageInfo_ReplicatedService proto.InternalMessageInfo // GlobalService represents global service. type GlobalService struct { } -func (m *GlobalService) Reset() { *m = GlobalService{} } -func (*GlobalService) ProtoMessage() {} -func (*GlobalService) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{3} } +func (m *GlobalService) Reset() { *m = GlobalService{} } +func (*GlobalService) ProtoMessage() {} +func (*GlobalService) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{3} +} +func (m *GlobalService) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GlobalService) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GlobalService.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GlobalService) XXX_Merge(src proto.Message) { + xxx_messageInfo_GlobalService.Merge(m, src) +} +func (m *GlobalService) XXX_Size() int { + return m.Size() +} +func (m *GlobalService) XXX_DiscardUnknown() { + xxx_messageInfo_GlobalService.DiscardUnknown(m) +} + +var xxx_messageInfo_GlobalService proto.InternalMessageInfo + +// ReplicatedJob is a certain type of one-off job which executes many Tasks in +// parallel until the specified number of Tasks have succeeded. +type ReplicatedJob struct { + // MaxConcurrent indicates the maximum number of Tasks that should be + // executing simultaneously at any given time. + MaxConcurrent uint64 `protobuf:"varint,1,opt,name=max_concurrent,json=maxConcurrent,proto3" json:"max_concurrent,omitempty"` + // TotalCompletions sets the total number of Tasks desired to run to + // completion. This is also the absolute maximum number of Tasks that will + // be executed in parallel. That is, if this number is smaller than + // MaxConcurrent, only this many replicas will run. + TotalCompletions uint64 `protobuf:"varint,2,opt,name=total_completions,json=totalCompletions,proto3" json:"total_completions,omitempty"` +} + +func (m *ReplicatedJob) Reset() { *m = ReplicatedJob{} } +func (*ReplicatedJob) ProtoMessage() {} +func (*ReplicatedJob) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{4} +} +func (m *ReplicatedJob) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ReplicatedJob) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ReplicatedJob.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ReplicatedJob) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReplicatedJob.Merge(m, src) +} +func (m *ReplicatedJob) XXX_Size() int { + return m.Size() +} +func (m *ReplicatedJob) XXX_DiscardUnknown() { + xxx_messageInfo_ReplicatedJob.DiscardUnknown(m) +} + +var xxx_messageInfo_ReplicatedJob proto.InternalMessageInfo + +// GlobalJob is a type of one-off job which executes one Task on every node +// matching the service's placement constraints. +type GlobalJob struct { +} + +func (m *GlobalJob) Reset() { *m = GlobalJob{} } +func (*GlobalJob) ProtoMessage() {} +func (*GlobalJob) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{5} +} +func (m *GlobalJob) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GlobalJob) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GlobalJob.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GlobalJob) XXX_Merge(src proto.Message) { + xxx_messageInfo_GlobalJob.Merge(m, src) +} +func (m *GlobalJob) XXX_Size() int { + return m.Size() +} +func (m *GlobalJob) XXX_DiscardUnknown() { + xxx_messageInfo_GlobalJob.DiscardUnknown(m) +} + +var xxx_messageInfo_GlobalJob proto.InternalMessageInfo type TaskSpec struct { // Types that are valid to be assigned to Runtime: @@ -322,18 +492,18 @@ type TaskSpec struct { // *TaskSpec_Generic Runtime isTaskSpec_Runtime `protobuf_oneof:"runtime"` // Resource requirements for the container. - Resources *ResourceRequirements `protobuf:"bytes,2,opt,name=resources" json:"resources,omitempty"` + Resources *ResourceRequirements `protobuf:"bytes,2,opt,name=resources,proto3" json:"resources,omitempty"` // RestartPolicy specifies what to do when a task fails or finishes. - Restart *RestartPolicy `protobuf:"bytes,4,opt,name=restart" json:"restart,omitempty"` + Restart *RestartPolicy `protobuf:"bytes,4,opt,name=restart,proto3" json:"restart,omitempty"` // Placement specifies node selection constraints - Placement *Placement `protobuf:"bytes,5,opt,name=placement" json:"placement,omitempty"` + Placement *Placement `protobuf:"bytes,5,opt,name=placement,proto3" json:"placement,omitempty"` // LogDriver specifies the log driver to use for the task. Any runtime will // direct logs into the specified driver for the duration of the task. - LogDriver *Driver `protobuf:"bytes,6,opt,name=log_driver,json=logDriver" json:"log_driver,omitempty"` + LogDriver *Driver `protobuf:"bytes,6,opt,name=log_driver,json=logDriver,proto3" json:"log_driver,omitempty"` // Networks specifies the list of network attachment // configurations (which specify the network and per-network // aliases) that this task spec is bound to. - Networks []*NetworkAttachmentConfig `protobuf:"bytes,7,rep,name=networks" json:"networks,omitempty"` + Networks []*NetworkAttachmentConfig `protobuf:"bytes,7,rep,name=networks,proto3" json:"networks,omitempty"` // ForceUpdate is a counter that triggers an update even if no relevant // parameters have been changed. We do this to allow forced restarts // using the same reconciliation-based mechanism that performs rolling @@ -347,12 +517,40 @@ type TaskSpec struct { // dispatcher to send the related objects. // // ResourceReferences is a list of ResourceReferences used by the task. - ResourceReferences []ResourceReference `protobuf:"bytes,11,rep,name=resource_references,json=resourceReferences" json:"resource_references"` + ResourceReferences []ResourceReference `protobuf:"bytes,11,rep,name=resource_references,json=resourceReferences,proto3" json:"resource_references"` +} + +func (m *TaskSpec) Reset() { *m = TaskSpec{} } +func (*TaskSpec) ProtoMessage() {} +func (*TaskSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{6} +} +func (m *TaskSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskSpec.Merge(m, src) +} +func (m *TaskSpec) XXX_Size() int { + return m.Size() +} +func (m *TaskSpec) XXX_DiscardUnknown() { + xxx_messageInfo_TaskSpec.DiscardUnknown(m) } -func (m *TaskSpec) Reset() { *m = TaskSpec{} } -func (*TaskSpec) ProtoMessage() {} -func (*TaskSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{4} } +var xxx_messageInfo_TaskSpec proto.InternalMessageInfo type isTaskSpec_Runtime interface { isTaskSpec_Runtime() @@ -361,13 +559,13 @@ type isTaskSpec_Runtime interface { } type TaskSpec_Attachment struct { - Attachment *NetworkAttachmentSpec `protobuf:"bytes,8,opt,name=attachment,oneof"` + Attachment *NetworkAttachmentSpec `protobuf:"bytes,8,opt,name=attachment,proto3,oneof" json:"attachment,omitempty"` } type TaskSpec_Container struct { - Container *ContainerSpec `protobuf:"bytes,1,opt,name=container,oneof"` + Container *ContainerSpec `protobuf:"bytes,1,opt,name=container,proto3,oneof" json:"container,omitempty"` } type TaskSpec_Generic struct { - Generic *GenericRuntimeSpec `protobuf:"bytes,10,opt,name=generic,oneof"` + Generic *GenericRuntimeSpec `protobuf:"bytes,10,opt,name=generic,proto3,oneof" json:"generic,omitempty"` } func (*TaskSpec_Attachment) isTaskSpec_Runtime() {} @@ -402,116 +600,88 @@ func (m *TaskSpec) GetGeneric() *GenericRuntimeSpec { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*TaskSpec) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _TaskSpec_OneofMarshaler, _TaskSpec_OneofUnmarshaler, _TaskSpec_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TaskSpec) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*TaskSpec_Attachment)(nil), (*TaskSpec_Container)(nil), (*TaskSpec_Generic)(nil), } } -func _TaskSpec_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*TaskSpec) - // runtime - switch x := m.Runtime.(type) { - case *TaskSpec_Attachment: - _ = b.EncodeVarint(8<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Attachment); err != nil { - return err - } - case *TaskSpec_Container: - _ = b.EncodeVarint(1<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Container); err != nil { - return err - } - case *TaskSpec_Generic: - _ = b.EncodeVarint(10<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Generic); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("TaskSpec.Runtime has unexpected type %T", x) - } - return nil +type ResourceReference struct { + ResourceID string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + ResourceType ResourceType `protobuf:"varint,2,opt,name=resource_type,json=resourceType,proto3,enum=docker.swarmkit.v1.ResourceType" json:"resource_type,omitempty"` } -func _TaskSpec_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*TaskSpec) - switch tag { - case 8: // runtime.attachment - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(NetworkAttachmentSpec) - err := b.DecodeMessage(msg) - m.Runtime = &TaskSpec_Attachment{msg} - return true, err - case 1: // runtime.container - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(ContainerSpec) - err := b.DecodeMessage(msg) - m.Runtime = &TaskSpec_Container{msg} - return true, err - case 10: // runtime.generic - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(GenericRuntimeSpec) - err := b.DecodeMessage(msg) - m.Runtime = &TaskSpec_Generic{msg} - return true, err - default: - return false, nil - } -} - -func _TaskSpec_OneofSizer(msg proto.Message) (n int) { - m := msg.(*TaskSpec) - // runtime - switch x := m.Runtime.(type) { - case *TaskSpec_Attachment: - s := proto.Size(x.Attachment) - n += proto.SizeVarint(8<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *TaskSpec_Container: - s := proto.Size(x.Container) - n += proto.SizeVarint(1<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *TaskSpec_Generic: - s := proto.Size(x.Generic) - n += proto.SizeVarint(10<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) +func (m *ResourceReference) Reset() { *m = ResourceReference{} } +func (*ResourceReference) ProtoMessage() {} +func (*ResourceReference) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{7} +} +func (m *ResourceReference) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceReference) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceReference.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil } - return n } - -type ResourceReference struct { - ResourceID string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` - ResourceType ResourceType `protobuf:"varint,2,opt,name=resource_type,json=resourceType,proto3,enum=docker.swarmkit.v1.ResourceType" json:"resource_type,omitempty"` +func (m *ResourceReference) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceReference.Merge(m, src) +} +func (m *ResourceReference) XXX_Size() int { + return m.Size() +} +func (m *ResourceReference) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceReference.DiscardUnknown(m) } -func (m *ResourceReference) Reset() { *m = ResourceReference{} } -func (*ResourceReference) ProtoMessage() {} -func (*ResourceReference) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{5} } +var xxx_messageInfo_ResourceReference proto.InternalMessageInfo type GenericRuntimeSpec struct { - Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` - Payload *google_protobuf3.Any `protobuf:"bytes,2,opt,name=payload" json:"payload,omitempty"` + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` + Payload *types.Any `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (m *GenericRuntimeSpec) Reset() { *m = GenericRuntimeSpec{} } +func (*GenericRuntimeSpec) ProtoMessage() {} +func (*GenericRuntimeSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{8} +} +func (m *GenericRuntimeSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenericRuntimeSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenericRuntimeSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenericRuntimeSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenericRuntimeSpec.Merge(m, src) +} +func (m *GenericRuntimeSpec) XXX_Size() int { + return m.Size() +} +func (m *GenericRuntimeSpec) XXX_DiscardUnknown() { + xxx_messageInfo_GenericRuntimeSpec.DiscardUnknown(m) } -func (m *GenericRuntimeSpec) Reset() { *m = GenericRuntimeSpec{} } -func (*GenericRuntimeSpec) ProtoMessage() {} -func (*GenericRuntimeSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{6} } +var xxx_messageInfo_GenericRuntimeSpec proto.InternalMessageInfo // NetworkAttachmentSpec specifies runtime parameters required to attach // a container to a network. @@ -521,9 +691,37 @@ type NetworkAttachmentSpec struct { ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` } -func (m *NetworkAttachmentSpec) Reset() { *m = NetworkAttachmentSpec{} } -func (*NetworkAttachmentSpec) ProtoMessage() {} -func (*NetworkAttachmentSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{7} } +func (m *NetworkAttachmentSpec) Reset() { *m = NetworkAttachmentSpec{} } +func (*NetworkAttachmentSpec) ProtoMessage() {} +func (*NetworkAttachmentSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{9} +} +func (m *NetworkAttachmentSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NetworkAttachmentSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NetworkAttachmentSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NetworkAttachmentSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_NetworkAttachmentSpec.Merge(m, src) +} +func (m *NetworkAttachmentSpec) XXX_Size() int { + return m.Size() +} +func (m *NetworkAttachmentSpec) XXX_DiscardUnknown() { + xxx_messageInfo_NetworkAttachmentSpec.DiscardUnknown(m) +} + +var xxx_messageInfo_NetworkAttachmentSpec proto.InternalMessageInfo // Container specifies runtime parameters for a container. type ContainerSpec struct { @@ -540,25 +738,25 @@ type ContainerSpec struct { // // This field *must* remain compatible with the Labels field of // Annotations. - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Command to run the the container. The first element is a path to the // executable and the following elements are treated as arguments. // // If command is empty, execution will fall back to the image's entrypoint. // // Command should only be used when overriding entrypoint. - Command []string `protobuf:"bytes,3,rep,name=command" json:"command,omitempty"` + Command []string `protobuf:"bytes,3,rep,name=command,proto3" json:"command,omitempty"` // Args specifies arguments provided to the image's entrypoint. // // If Command and Args are provided, Args will be appended to Command. - Args []string `protobuf:"bytes,4,rep,name=args" json:"args,omitempty"` + Args []string `protobuf:"bytes,4,rep,name=args,proto3" json:"args,omitempty"` // Hostname specifies the hostname that will be set on containers created by docker swarm. // All containers for a given service will have the same hostname Hostname string `protobuf:"bytes,14,opt,name=hostname,proto3" json:"hostname,omitempty"` // Env specifies the environment variables for the container in NAME=VALUE // format. These must be compliant with [IEEE Std // 1003.1-2001](http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html). - Env []string `protobuf:"bytes,5,rep,name=env" json:"env,omitempty"` + Env []string `protobuf:"bytes,5,rep,name=env,proto3" json:"env,omitempty"` // Dir defines the working directory to set for the container process. Dir string `protobuf:"bytes,6,opt,name=dir,proto3" json:"dir,omitempty"` // User specifies the user that should be employed to run the container. @@ -568,11 +766,11 @@ type ContainerSpec struct { // `:`. User string `protobuf:"bytes,7,opt,name=user,proto3" json:"user,omitempty"` // Groups specifies supplementary groups available to the user. - Groups []string `protobuf:"bytes,11,rep,name=groups" json:"groups,omitempty"` + Groups []string `protobuf:"bytes,11,rep,name=groups,proto3" json:"groups,omitempty"` // Privileges specifies security configuration/permissions. - Privileges *Privileges `protobuf:"bytes,22,opt,name=privileges" json:"privileges,omitempty"` + Privileges *Privileges `protobuf:"bytes,22,opt,name=privileges,proto3" json:"privileges,omitempty"` // Init declares that a custom init will be running inside the container, if null, use the daemon's configured settings - Init *google_protobuf4.BoolValue `protobuf:"bytes,23,opt,name=init" json:"init,omitempty"` + Init *types.BoolValue `protobuf:"bytes,23,opt,name=init,proto3" json:"init,omitempty"` // TTY declares that a TTY should be attached to the standard streams, // including stdin if it is still open. TTY bool `protobuf:"varint,13,opt,name=tty,proto3" json:"tty,omitempty"` @@ -585,19 +783,19 @@ type ContainerSpec struct { ReadOnly bool `protobuf:"varint,19,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` // StopSignal defines the signal to stop the container. StopSignal string `protobuf:"bytes,20,opt,name=stop_signal,json=stopSignal,proto3" json:"stop_signal,omitempty"` - Mounts []Mount `protobuf:"bytes,8,rep,name=mounts" json:"mounts"` + Mounts []Mount `protobuf:"bytes,8,rep,name=mounts,proto3" json:"mounts"` // StopGracePeriod the grace period for stopping the container before // forcefully killing the container. // Note: Can't use stdduration here because this needs to be nullable. - StopGracePeriod *google_protobuf1.Duration `protobuf:"bytes,9,opt,name=stop_grace_period,json=stopGracePeriod" json:"stop_grace_period,omitempty"` + StopGracePeriod *types.Duration `protobuf:"bytes,9,opt,name=stop_grace_period,json=stopGracePeriod,proto3" json:"stop_grace_period,omitempty"` // PullOptions parameterize the behavior of image pulls. - PullOptions *ContainerSpec_PullOptions `protobuf:"bytes,10,opt,name=pull_options,json=pullOptions" json:"pull_options,omitempty"` + PullOptions *ContainerSpec_PullOptions `protobuf:"bytes,10,opt,name=pull_options,json=pullOptions,proto3" json:"pull_options,omitempty"` // SecretReference contains references to zero or more secrets that // will be exposed to the container. - Secrets []*SecretReference `protobuf:"bytes,12,rep,name=secrets" json:"secrets,omitempty"` + Secrets []*SecretReference `protobuf:"bytes,12,rep,name=secrets,proto3" json:"secrets,omitempty"` // ConfigReference contains references to zero or more configs that // will be exposed to the container. - Configs []*ConfigReference `protobuf:"bytes,21,rep,name=configs" json:"configs,omitempty"` + Configs []*ConfigReference `protobuf:"bytes,21,rep,name=configs,proto3" json:"configs,omitempty"` // Hosts allow additional entries to be specified in /etc/hosts // that associates IP addresses with hostnames. // Detailed documentation is available in: @@ -608,14 +806,14 @@ type ContainerSpec struct { // above. // This is different from `docker run --add-host :` // where format is `:` - Hosts []string `protobuf:"bytes,17,rep,name=hosts" json:"hosts,omitempty"` + Hosts []string `protobuf:"bytes,17,rep,name=hosts,proto3" json:"hosts,omitempty"` // DNSConfig allows one to specify DNS related configuration in resolv.conf - DNSConfig *ContainerSpec_DNSConfig `protobuf:"bytes,15,opt,name=dns_config,json=dnsConfig" json:"dns_config,omitempty"` + DNSConfig *ContainerSpec_DNSConfig `protobuf:"bytes,15,opt,name=dns_config,json=dnsConfig,proto3" json:"dns_config,omitempty"` // Healthcheck describes how to check the container is healthy. If the // container is considered unhealthy, it will be destroyed, its creating // task will exit and a new task will be rescheduled elsewhere. A container // is considered unhealthy after `Retries` number of consecutive failures. - Healthcheck *HealthConfig `protobuf:"bytes,16,opt,name=healthcheck" json:"healthcheck,omitempty"` + Healthcheck *HealthConfig `protobuf:"bytes,16,opt,name=healthcheck,proto3" json:"healthcheck,omitempty"` // Isolation defines the isolation level for windows containers (default, process, hyperv). // Runtimes that don't support it ignore that field Isolation ContainerSpec_Isolation `protobuf:"varint,24,opt,name=isolation,proto3,enum=docker.swarmkit.v1.ContainerSpec_Isolation" json:"isolation,omitempty"` @@ -635,18 +833,47 @@ type ContainerSpec struct { // documentation at: // // https://docs.docker.com/engine/reference/commandline/run/#configure-namespaced-kernel-parameters-sysctls-at-runtime - Sysctls map[string]string `protobuf:"bytes,26,rep,name=sysctls" json:"sysctls,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - // Swap limit equal to memory plus swap: '-1' to enable unlimited swap - MemorySwap int64 `protobuf:"varint,27,opt,name=memory_swap,json=memorySwap,proto3" json:"memory_swap,omitempty"` - // Tune container memory swappiness (0 to 100) - if not specified, defaults - // to the container OS's default - generally 60, or the value predefined in - // the image; set to -1 to unset a previously set value - MemorySwappiness *google_protobuf4.Int64Value `protobuf:"bytes,28,opt,name=memory_swappiness,json=memorySwappiness" json:"memory_swappiness,omitempty"` + Sysctls map[string]string `protobuf:"bytes,26,rep,name=sysctls,proto3" json:"sysctls,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // CapabilityAdd sets the list of capabilities to add to the default capability list + CapabilityAdd []string `protobuf:"bytes,27,rep,name=capability_add,json=capabilityAdd,proto3" json:"capability_add,omitempty"` + // CapabilityDrop sets the list of capabilities to drop from the default capability list + CapabilityDrop []string `protobuf:"bytes,28,rep,name=capability_drop,json=capabilityDrop,proto3" json:"capability_drop,omitempty"` + // Ulimits defines the list of ulimits to set in the container. This option + // is equivalent to passing --ulimit to docker run. + Ulimits []*ContainerSpec_Ulimit `protobuf:"bytes,29,rep,name=ulimits,proto3" json:"ulimits,omitempty"` +} + +func (m *ContainerSpec) Reset() { *m = ContainerSpec{} } +func (*ContainerSpec) ProtoMessage() {} +func (*ContainerSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{10} +} +func (m *ContainerSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerSpec.Merge(m, src) +} +func (m *ContainerSpec) XXX_Size() int { + return m.Size() +} +func (m *ContainerSpec) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerSpec.DiscardUnknown(m) } -func (m *ContainerSpec) Reset() { *m = ContainerSpec{} } -func (*ContainerSpec) ProtoMessage() {} -func (*ContainerSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{8} } +var xxx_messageInfo_ContainerSpec proto.InternalMessageInfo // PullOptions allows one to parameterize an image pull. type ContainerSpec_PullOptions struct { @@ -660,25 +887,117 @@ type ContainerSpec_PullOptions struct { func (m *ContainerSpec_PullOptions) Reset() { *m = ContainerSpec_PullOptions{} } func (*ContainerSpec_PullOptions) ProtoMessage() {} func (*ContainerSpec_PullOptions) Descriptor() ([]byte, []int) { - return fileDescriptorSpecs, []int{8, 1} + return fileDescriptor_6589acc608f7d4fd, []int{10, 1} +} +func (m *ContainerSpec_PullOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerSpec_PullOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerSpec_PullOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerSpec_PullOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerSpec_PullOptions.Merge(m, src) +} +func (m *ContainerSpec_PullOptions) XXX_Size() int { + return m.Size() +} +func (m *ContainerSpec_PullOptions) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerSpec_PullOptions.DiscardUnknown(m) } +var xxx_messageInfo_ContainerSpec_PullOptions proto.InternalMessageInfo + // DNSConfig specifies DNS related configurations in resolver configuration file (resolv.conf) // Detailed documentation is available in: // http://man7.org/linux/man-pages/man5/resolv.conf.5.html // TODO: domain is not supported yet type ContainerSpec_DNSConfig struct { // Nameservers specifies the IP addresses of the name servers - Nameservers []string `protobuf:"bytes,1,rep,name=nameservers" json:"nameservers,omitempty"` + Nameservers []string `protobuf:"bytes,1,rep,name=nameservers,proto3" json:"nameservers,omitempty"` // Search specifies the search list for host-name lookup - Search []string `protobuf:"bytes,2,rep,name=search" json:"search,omitempty"` + Search []string `protobuf:"bytes,2,rep,name=search,proto3" json:"search,omitempty"` // Options allows certain internal resolver variables to be modified - Options []string `protobuf:"bytes,3,rep,name=options" json:"options,omitempty"` + Options []string `protobuf:"bytes,3,rep,name=options,proto3" json:"options,omitempty"` +} + +func (m *ContainerSpec_DNSConfig) Reset() { *m = ContainerSpec_DNSConfig{} } +func (*ContainerSpec_DNSConfig) ProtoMessage() {} +func (*ContainerSpec_DNSConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{10, 2} +} +func (m *ContainerSpec_DNSConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerSpec_DNSConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerSpec_DNSConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerSpec_DNSConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerSpec_DNSConfig.Merge(m, src) +} +func (m *ContainerSpec_DNSConfig) XXX_Size() int { + return m.Size() +} +func (m *ContainerSpec_DNSConfig) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerSpec_DNSConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_ContainerSpec_DNSConfig proto.InternalMessageInfo + +type ContainerSpec_Ulimit struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Soft int64 `protobuf:"varint,2,opt,name=soft,proto3" json:"soft,omitempty"` + Hard int64 `protobuf:"varint,3,opt,name=hard,proto3" json:"hard,omitempty"` +} + +func (m *ContainerSpec_Ulimit) Reset() { *m = ContainerSpec_Ulimit{} } +func (*ContainerSpec_Ulimit) ProtoMessage() {} +func (*ContainerSpec_Ulimit) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{10, 4} +} +func (m *ContainerSpec_Ulimit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerSpec_Ulimit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerSpec_Ulimit.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerSpec_Ulimit) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerSpec_Ulimit.Merge(m, src) +} +func (m *ContainerSpec_Ulimit) XXX_Size() int { + return m.Size() +} +func (m *ContainerSpec_Ulimit) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerSpec_Ulimit.DiscardUnknown(m) } -func (m *ContainerSpec_DNSConfig) Reset() { *m = ContainerSpec_DNSConfig{} } -func (*ContainerSpec_DNSConfig) ProtoMessage() {} -func (*ContainerSpec_DNSConfig) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{8, 2} } +var xxx_messageInfo_ContainerSpec_Ulimit proto.InternalMessageInfo // EndpointSpec defines the properties that can be configured to // access and loadbalance the service. @@ -686,24 +1005,52 @@ type EndpointSpec struct { Mode EndpointSpec_ResolutionMode `protobuf:"varint,1,opt,name=mode,proto3,enum=docker.swarmkit.v1.EndpointSpec_ResolutionMode" json:"mode,omitempty"` // List of exposed ports that this service is accessible from // external to the cluster. - Ports []*PortConfig `protobuf:"bytes,2,rep,name=ports" json:"ports,omitempty"` + Ports []*PortConfig `protobuf:"bytes,2,rep,name=ports,proto3" json:"ports,omitempty"` +} + +func (m *EndpointSpec) Reset() { *m = EndpointSpec{} } +func (*EndpointSpec) ProtoMessage() {} +func (*EndpointSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{11} +} +func (m *EndpointSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EndpointSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EndpointSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EndpointSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_EndpointSpec.Merge(m, src) +} +func (m *EndpointSpec) XXX_Size() int { + return m.Size() +} +func (m *EndpointSpec) XXX_DiscardUnknown() { + xxx_messageInfo_EndpointSpec.DiscardUnknown(m) } -func (m *EndpointSpec) Reset() { *m = EndpointSpec{} } -func (*EndpointSpec) ProtoMessage() {} -func (*EndpointSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{9} } +var xxx_messageInfo_EndpointSpec proto.InternalMessageInfo // NetworkSpec specifies user defined network parameters. type NetworkSpec struct { - Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"` + Annotations Annotations `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations"` // DriverConfig specific configuration consumed by the network driver. - DriverConfig *Driver `protobuf:"bytes,2,opt,name=driver_config,json=driverConfig" json:"driver_config,omitempty"` + DriverConfig *Driver `protobuf:"bytes,2,opt,name=driver_config,json=driverConfig,proto3" json:"driver_config,omitempty"` // IPv6Enabled enables support for IPv6 on the network. Ipv6Enabled bool `protobuf:"varint,3,opt,name=ipv6_enabled,json=ipv6Enabled,proto3" json:"ipv6_enabled,omitempty"` // internal restricts external access to the network. This may be // accomplished by disabling the default gateway or through other means. Internal bool `protobuf:"varint,4,opt,name=internal,proto3" json:"internal,omitempty"` - IPAM *IPAMOptions `protobuf:"bytes,5,opt,name=ipam" json:"ipam,omitempty"` + IPAM *IPAMOptions `protobuf:"bytes,5,opt,name=ipam,proto3" json:"ipam,omitempty"` // Attachable allows external(to swarm) entities to manually // attach to this network. With this flag enabled, external // entities such as containers running in an worker node in @@ -724,9 +1071,37 @@ type NetworkSpec struct { ConfigFrom isNetworkSpec_ConfigFrom `protobuf_oneof:"config_from"` } -func (m *NetworkSpec) Reset() { *m = NetworkSpec{} } -func (*NetworkSpec) ProtoMessage() {} -func (*NetworkSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{10} } +func (m *NetworkSpec) Reset() { *m = NetworkSpec{} } +func (*NetworkSpec) ProtoMessage() {} +func (*NetworkSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{12} +} +func (m *NetworkSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NetworkSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NetworkSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NetworkSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_NetworkSpec.Merge(m, src) +} +func (m *NetworkSpec) XXX_Size() int { + return m.Size() +} +func (m *NetworkSpec) XXX_DiscardUnknown() { + xxx_messageInfo_NetworkSpec.DiscardUnknown(m) +} + +var xxx_messageInfo_NetworkSpec proto.InternalMessageInfo type isNetworkSpec_ConfigFrom interface { isNetworkSpec_ConfigFrom() @@ -735,7 +1110,7 @@ type isNetworkSpec_ConfigFrom interface { } type NetworkSpec_Network struct { - Network string `protobuf:"bytes,8,opt,name=network,proto3,oneof"` + Network string `protobuf:"bytes,8,opt,name=network,proto3,oneof" json:"network,omitempty"` } func (*NetworkSpec_Network) isNetworkSpec_ConfigFrom() {} @@ -754,85 +1129,69 @@ func (m *NetworkSpec) GetNetwork() string { return "" } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*NetworkSpec) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _NetworkSpec_OneofMarshaler, _NetworkSpec_OneofUnmarshaler, _NetworkSpec_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*NetworkSpec) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*NetworkSpec_Network)(nil), } } -func _NetworkSpec_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*NetworkSpec) - // config_from - switch x := m.ConfigFrom.(type) { - case *NetworkSpec_Network: - _ = b.EncodeVarint(8<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.Network) - case nil: - default: - return fmt.Errorf("NetworkSpec.ConfigFrom has unexpected type %T", x) - } - return nil -} - -func _NetworkSpec_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*NetworkSpec) - switch tag { - case 8: // config_from.network - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.ConfigFrom = &NetworkSpec_Network{x} - return true, err - default: - return false, nil - } -} - -func _NetworkSpec_OneofSizer(msg proto.Message) (n int) { - m := msg.(*NetworkSpec) - // config_from - switch x := m.ConfigFrom.(type) { - case *NetworkSpec_Network: - n += proto.SizeVarint(8<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.Network))) - n += len(x.Network) - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - // ClusterSpec specifies global cluster settings. type ClusterSpec struct { - Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"` + Annotations Annotations `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations"` // DEPRECATED: AcceptancePolicy defines the certificate issuance policy. // Acceptance policy is no longer customizable, and secrets have been // replaced with join tokens. - AcceptancePolicy AcceptancePolicy `protobuf:"bytes,2,opt,name=acceptance_policy,json=acceptancePolicy" json:"acceptance_policy"` + AcceptancePolicy AcceptancePolicy `protobuf:"bytes,2,opt,name=acceptance_policy,json=acceptancePolicy,proto3" json:"acceptance_policy"` // Deprecated: Do not use. // Orchestration defines cluster-level orchestration settings. - Orchestration OrchestrationConfig `protobuf:"bytes,3,opt,name=orchestration" json:"orchestration"` + Orchestration OrchestrationConfig `protobuf:"bytes,3,opt,name=orchestration,proto3" json:"orchestration"` // Raft defines the cluster's raft settings. - Raft RaftConfig `protobuf:"bytes,4,opt,name=raft" json:"raft"` + Raft RaftConfig `protobuf:"bytes,4,opt,name=raft,proto3" json:"raft"` // Dispatcher defines cluster-level dispatcher settings. - Dispatcher DispatcherConfig `protobuf:"bytes,5,opt,name=dispatcher" json:"dispatcher"` + Dispatcher DispatcherConfig `protobuf:"bytes,5,opt,name=dispatcher,proto3" json:"dispatcher"` // CAConfig defines cluster-level certificate authority settings. - CAConfig CAConfig `protobuf:"bytes,6,opt,name=ca_config,json=caConfig" json:"ca_config"` + CAConfig CAConfig `protobuf:"bytes,6,opt,name=ca_config,json=caConfig,proto3" json:"ca_config"` // TaskDefaults specifies the default values to use for task creation. - TaskDefaults TaskDefaults `protobuf:"bytes,7,opt,name=task_defaults,json=taskDefaults" json:"task_defaults"` + TaskDefaults TaskDefaults `protobuf:"bytes,7,opt,name=task_defaults,json=taskDefaults,proto3" json:"task_defaults"` // EncryptionConfig defines the cluster's encryption settings. - EncryptionConfig EncryptionConfig `protobuf:"bytes,8,opt,name=encryption_config,json=encryptionConfig" json:"encryption_config"` + EncryptionConfig EncryptionConfig `protobuf:"bytes,8,opt,name=encryption_config,json=encryptionConfig,proto3" json:"encryption_config"` +} + +func (m *ClusterSpec) Reset() { *m = ClusterSpec{} } +func (*ClusterSpec) ProtoMessage() {} +func (*ClusterSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{13} +} +func (m *ClusterSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ClusterSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ClusterSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ClusterSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClusterSpec.Merge(m, src) +} +func (m *ClusterSpec) XXX_Size() int { + return m.Size() +} +func (m *ClusterSpec) XXX_DiscardUnknown() { + xxx_messageInfo_ClusterSpec.DiscardUnknown(m) } -func (m *ClusterSpec) Reset() { *m = ClusterSpec{} } -func (*ClusterSpec) ProtoMessage() {} -func (*ClusterSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{11} } +var xxx_messageInfo_ClusterSpec proto.InternalMessageInfo // SecretSpec specifies a user-provided secret. type SecretSpec struct { - Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"` + Annotations Annotations `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations"` // Data is the secret payload - the maximum size is 500KB (that is, 500*1024 bytes) Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` // Templating controls whether and how to evaluate the secret payload as @@ -840,18 +1199,46 @@ type SecretSpec struct { // // The currently recognized values are: // - golang: Go templating - Templating *Driver `protobuf:"bytes,3,opt,name=templating" json:"templating,omitempty"` + Templating *Driver `protobuf:"bytes,3,opt,name=templating,proto3" json:"templating,omitempty"` // Driver is the the secret driver that is used to store the specified secret - Driver *Driver `protobuf:"bytes,4,opt,name=driver" json:"driver,omitempty"` + Driver *Driver `protobuf:"bytes,4,opt,name=driver,proto3" json:"driver,omitempty"` +} + +func (m *SecretSpec) Reset() { *m = SecretSpec{} } +func (*SecretSpec) ProtoMessage() {} +func (*SecretSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{14} +} +func (m *SecretSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SecretSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SecretSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SecretSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_SecretSpec.Merge(m, src) +} +func (m *SecretSpec) XXX_Size() int { + return m.Size() +} +func (m *SecretSpec) XXX_DiscardUnknown() { + xxx_messageInfo_SecretSpec.DiscardUnknown(m) } -func (m *SecretSpec) Reset() { *m = SecretSpec{} } -func (*SecretSpec) ProtoMessage() {} -func (*SecretSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{12} } +var xxx_messageInfo_SecretSpec proto.InternalMessageInfo // ConfigSpec specifies user-provided configuration files. type ConfigSpec struct { - Annotations Annotations `protobuf:"bytes,1,opt,name=annotations" json:"annotations"` + Annotations Annotations `protobuf:"bytes,1,opt,name=annotations,proto3" json:"annotations"` // Data is the config payload - the maximum size is 500KB (that is, 500*1024 bytes) // TODO(aaronl): Do we want to revise this to include multiple payloads in a single // ConfigSpec? Define this to be a tar? etc... @@ -861,34 +1248,223 @@ type ConfigSpec struct { // // The currently recognized values are: // - golang: Go templating - Templating *Driver `protobuf:"bytes,3,opt,name=templating" json:"templating,omitempty"` + Templating *Driver `protobuf:"bytes,3,opt,name=templating,proto3" json:"templating,omitempty"` +} + +func (m *ConfigSpec) Reset() { *m = ConfigSpec{} } +func (*ConfigSpec) ProtoMessage() {} +func (*ConfigSpec) Descriptor() ([]byte, []int) { + return fileDescriptor_6589acc608f7d4fd, []int{15} +} +func (m *ConfigSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConfigSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConfigSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConfigSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConfigSpec.Merge(m, src) +} +func (m *ConfigSpec) XXX_Size() int { + return m.Size() +} +func (m *ConfigSpec) XXX_DiscardUnknown() { + xxx_messageInfo_ConfigSpec.DiscardUnknown(m) } -func (m *ConfigSpec) Reset() { *m = ConfigSpec{} } -func (*ConfigSpec) ProtoMessage() {} -func (*ConfigSpec) Descriptor() ([]byte, []int) { return fileDescriptorSpecs, []int{13} } +var xxx_messageInfo_ConfigSpec proto.InternalMessageInfo func init() { + proto.RegisterEnum("docker.swarmkit.v1.NodeSpec_Membership", NodeSpec_Membership_name, NodeSpec_Membership_value) + proto.RegisterEnum("docker.swarmkit.v1.NodeSpec_Availability", NodeSpec_Availability_name, NodeSpec_Availability_value) + proto.RegisterEnum("docker.swarmkit.v1.ContainerSpec_Isolation", ContainerSpec_Isolation_name, ContainerSpec_Isolation_value) + proto.RegisterEnum("docker.swarmkit.v1.EndpointSpec_ResolutionMode", EndpointSpec_ResolutionMode_name, EndpointSpec_ResolutionMode_value) proto.RegisterType((*NodeSpec)(nil), "docker.swarmkit.v1.NodeSpec") proto.RegisterType((*ServiceSpec)(nil), "docker.swarmkit.v1.ServiceSpec") proto.RegisterType((*ReplicatedService)(nil), "docker.swarmkit.v1.ReplicatedService") proto.RegisterType((*GlobalService)(nil), "docker.swarmkit.v1.GlobalService") + proto.RegisterType((*ReplicatedJob)(nil), "docker.swarmkit.v1.ReplicatedJob") + proto.RegisterType((*GlobalJob)(nil), "docker.swarmkit.v1.GlobalJob") proto.RegisterType((*TaskSpec)(nil), "docker.swarmkit.v1.TaskSpec") proto.RegisterType((*ResourceReference)(nil), "docker.swarmkit.v1.ResourceReference") proto.RegisterType((*GenericRuntimeSpec)(nil), "docker.swarmkit.v1.GenericRuntimeSpec") proto.RegisterType((*NetworkAttachmentSpec)(nil), "docker.swarmkit.v1.NetworkAttachmentSpec") proto.RegisterType((*ContainerSpec)(nil), "docker.swarmkit.v1.ContainerSpec") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ContainerSpec.LabelsEntry") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ContainerSpec.SysctlsEntry") proto.RegisterType((*ContainerSpec_PullOptions)(nil), "docker.swarmkit.v1.ContainerSpec.PullOptions") proto.RegisterType((*ContainerSpec_DNSConfig)(nil), "docker.swarmkit.v1.ContainerSpec.DNSConfig") + proto.RegisterType((*ContainerSpec_Ulimit)(nil), "docker.swarmkit.v1.ContainerSpec.Ulimit") proto.RegisterType((*EndpointSpec)(nil), "docker.swarmkit.v1.EndpointSpec") proto.RegisterType((*NetworkSpec)(nil), "docker.swarmkit.v1.NetworkSpec") proto.RegisterType((*ClusterSpec)(nil), "docker.swarmkit.v1.ClusterSpec") proto.RegisterType((*SecretSpec)(nil), "docker.swarmkit.v1.SecretSpec") proto.RegisterType((*ConfigSpec)(nil), "docker.swarmkit.v1.ConfigSpec") - proto.RegisterEnum("docker.swarmkit.v1.NodeSpec_Membership", NodeSpec_Membership_name, NodeSpec_Membership_value) - proto.RegisterEnum("docker.swarmkit.v1.NodeSpec_Availability", NodeSpec_Availability_name, NodeSpec_Availability_value) - proto.RegisterEnum("docker.swarmkit.v1.ContainerSpec_Isolation", ContainerSpec_Isolation_name, ContainerSpec_Isolation_value) - proto.RegisterEnum("docker.swarmkit.v1.EndpointSpec_ResolutionMode", EndpointSpec_ResolutionMode_name, EndpointSpec_ResolutionMode_value) +} + +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/specs.proto", fileDescriptor_6589acc608f7d4fd) +} + +var fileDescriptor_6589acc608f7d4fd = []byte{ + // 2363 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4d, 0x73, 0x1b, 0xc7, + 0xd1, 0x06, 0x48, 0x10, 0x1f, 0xbd, 0x00, 0x05, 0x8e, 0x65, 0x7b, 0x09, 0x49, 0x20, 0x0c, 0xcb, + 0x36, 0x6d, 0xd7, 0x0b, 0xd6, 0xcb, 0xb8, 0x1c, 0x7f, 0xc4, 0x49, 0x00, 0x02, 0x96, 0x60, 0x49, + 0x14, 0x6a, 0x40, 0x29, 0x51, 0x55, 0xaa, 0x50, 0x83, 0xdd, 0x21, 0xb0, 0xe1, 0x62, 0x67, 0x33, + 0x3b, 0xa0, 0x8d, 0x5b, 0x8e, 0x2e, 0xe5, 0x92, 0x3f, 0xc0, 0x53, 0x2a, 0xa7, 0x5c, 0x92, 0x7f, + 0x90, 0xa3, 0x8f, 0x3e, 0x3a, 0x17, 0x56, 0x4c, 0xff, 0x84, 0xdc, 0x72, 0x49, 0x6a, 0x66, 0x67, + 0x17, 0x0b, 0x0a, 0x10, 0x95, 0x8a, 0x0e, 0xb9, 0xcd, 0xf4, 0x3e, 0x4f, 0x4f, 0xcf, 0x4c, 0x77, + 0x4f, 0xf7, 0xc2, 0x7b, 0x23, 0x47, 0x8c, 0xa7, 0xc3, 0x86, 0xc5, 0x26, 0x7b, 0x36, 0xb3, 0x4e, + 0x28, 0xdf, 0x0b, 0xbe, 0x24, 0x7c, 0x72, 0xe2, 0x88, 0x3d, 0xe2, 0x3b, 0x7b, 0x81, 0x4f, 0xad, + 0xa0, 0xe1, 0x73, 0x26, 0x18, 0x42, 0x21, 0xa0, 0x11, 0x01, 0x1a, 0xa7, 0xff, 0x5f, 0xb9, 0x8a, + 0x2f, 0x66, 0x3e, 0xd5, 0xfc, 0xca, 0xf5, 0x11, 0x1b, 0x31, 0x35, 0xdc, 0x93, 0x23, 0x2d, 0xad, + 0x8e, 0x18, 0x1b, 0xb9, 0x74, 0x4f, 0xcd, 0x86, 0xd3, 0xe3, 0x3d, 0x7b, 0xca, 0x89, 0x70, 0x98, + 0xa7, 0xbf, 0x6f, 0x5f, 0xfe, 0x4e, 0xbc, 0xd9, 0x2a, 0xea, 0x97, 0x9c, 0xf8, 0x3e, 0xe5, 0x7a, + 0xc1, 0xfa, 0x59, 0x06, 0xf2, 0x87, 0xcc, 0xa6, 0x7d, 0x9f, 0x5a, 0xe8, 0x0e, 0x18, 0xc4, 0xf3, + 0x98, 0x50, 0xba, 0x03, 0x33, 0x5d, 0x4b, 0xef, 0x1a, 0xfb, 0x3b, 0x8d, 0x67, 0xf7, 0xd4, 0x68, + 0xce, 0x61, 0xad, 0xcc, 0x37, 0xe7, 0x3b, 0x29, 0x9c, 0x64, 0xa2, 0x9f, 0x41, 0xd1, 0xa6, 0x81, + 0xc3, 0xa9, 0x3d, 0xe0, 0xcc, 0xa5, 0xe6, 0x5a, 0x2d, 0xbd, 0xbb, 0xb9, 0x7f, 0x73, 0x99, 0x26, + 0xb9, 0x38, 0x66, 0x2e, 0xc5, 0x86, 0x66, 0xc8, 0x09, 0xba, 0x03, 0x30, 0xa1, 0x93, 0x21, 0xe5, + 0xc1, 0xd8, 0xf1, 0xcd, 0x75, 0x45, 0x7f, 0x67, 0x15, 0x5d, 0xda, 0xde, 0x78, 0x10, 0xc3, 0x71, + 0x82, 0x8a, 0x1e, 0x40, 0x91, 0x9c, 0x12, 0xc7, 0x25, 0x43, 0xc7, 0x75, 0xc4, 0xcc, 0xcc, 0x28, + 0x55, 0xef, 0x3e, 0x57, 0x55, 0x33, 0x41, 0xc0, 0x0b, 0xf4, 0xba, 0x0d, 0x30, 0x5f, 0x08, 0xbd, + 0x0d, 0xb9, 0x5e, 0xe7, 0xb0, 0xdd, 0x3d, 0xbc, 0x53, 0x4e, 0x55, 0xb6, 0x9f, 0x9e, 0xd5, 0x5e, + 0x95, 0x3a, 0xe6, 0x80, 0x1e, 0xf5, 0x6c, 0xc7, 0x1b, 0xa1, 0x5d, 0xc8, 0x37, 0x0f, 0x0e, 0x3a, + 0xbd, 0xa3, 0x4e, 0xbb, 0x9c, 0xae, 0x54, 0x9e, 0x9e, 0xd5, 0x5e, 0x5b, 0x04, 0x36, 0x2d, 0x8b, + 0xfa, 0x82, 0xda, 0x95, 0xcc, 0xd7, 0x7f, 0xa8, 0xa6, 0xea, 0x5f, 0xa7, 0xa1, 0x98, 0x34, 0x02, + 0xbd, 0x0d, 0xd9, 0xe6, 0xc1, 0x51, 0xf7, 0x71, 0xa7, 0x9c, 0x9a, 0xd3, 0x93, 0x88, 0xa6, 0x25, + 0x9c, 0x53, 0x8a, 0x6e, 0xc3, 0x46, 0xaf, 0xf9, 0xa8, 0xdf, 0x29, 0xa7, 0xe7, 0xe6, 0x24, 0x61, + 0x3d, 0x32, 0x0d, 0x14, 0xaa, 0x8d, 0x9b, 0xdd, 0xc3, 0xf2, 0xda, 0x72, 0x54, 0x9b, 0x13, 0xc7, + 0xd3, 0xa6, 0xfc, 0x69, 0x03, 0x8c, 0x3e, 0xe5, 0xa7, 0x8e, 0xf5, 0x92, 0x5d, 0xe4, 0x43, 0xc8, + 0x08, 0x12, 0x9c, 0x28, 0xd7, 0x30, 0x96, 0xbb, 0xc6, 0x11, 0x09, 0x4e, 0xe4, 0xa2, 0x9a, 0xae, + 0xf0, 0xd2, 0x33, 0x38, 0xf5, 0x5d, 0xc7, 0x22, 0x82, 0xda, 0xca, 0x33, 0x8c, 0xfd, 0xb7, 0x96, + 0xb1, 0x71, 0x8c, 0xd2, 0xf6, 0xdf, 0x4d, 0xe1, 0x04, 0x15, 0x7d, 0x0a, 0xd9, 0x91, 0xcb, 0x86, + 0xc4, 0x55, 0x3e, 0x61, 0xec, 0xbf, 0xb1, 0x4c, 0xc9, 0x1d, 0x85, 0x98, 0x2b, 0xd0, 0x14, 0xf4, + 0x05, 0x6c, 0xce, 0x55, 0x0d, 0x7e, 0xcd, 0x86, 0x26, 0xac, 0x56, 0x32, 0xb7, 0xe4, 0x0b, 0x36, + 0xbc, 0x9b, 0xc2, 0x25, 0x9e, 0x14, 0xa0, 0x9f, 0x02, 0x84, 0x5a, 0x95, 0x1e, 0x43, 0xe9, 0xb9, + 0xb5, 0xda, 0x98, 0x50, 0x47, 0x61, 0x14, 0x4d, 0xd0, 0x47, 0x90, 0x9d, 0xfa, 0x36, 0x11, 0xd4, + 0xcc, 0x2a, 0x6e, 0x6d, 0x19, 0xf7, 0x91, 0x42, 0x1c, 0x30, 0xef, 0xd8, 0x19, 0x61, 0x8d, 0x47, + 0x3f, 0x81, 0x3c, 0x67, 0xae, 0x3b, 0x24, 0xd6, 0x89, 0x59, 0x78, 0x41, 0x6e, 0xcc, 0x40, 0xf7, + 0x20, 0xef, 0x51, 0xf1, 0x25, 0xe3, 0x27, 0x81, 0x99, 0xab, 0xad, 0xef, 0x1a, 0xfb, 0xef, 0x2f, + 0x0d, 0xab, 0x10, 0xd3, 0x14, 0x82, 0x58, 0xe3, 0x09, 0xf5, 0x44, 0xa8, 0xa8, 0xb5, 0x66, 0xa6, + 0x71, 0xac, 0x40, 0x9a, 0x42, 0x3d, 0xdb, 0x67, 0x8e, 0x27, 0xcc, 0xfc, 0x6a, 0x53, 0x3a, 0x1a, + 0x23, 0xdd, 0x02, 0xc7, 0x8c, 0x56, 0x16, 0x32, 0x13, 0x66, 0xd3, 0xfa, 0x1e, 0x6c, 0x3d, 0x73, + 0xed, 0xa8, 0x02, 0x79, 0x7d, 0xe0, 0xa1, 0xbf, 0x66, 0x70, 0x3c, 0xaf, 0x5f, 0x83, 0xd2, 0xc2, + 0x15, 0xd7, 0x2d, 0x28, 0x2d, 0x5c, 0x17, 0x7a, 0x0b, 0x36, 0x27, 0xe4, 0xab, 0x81, 0xc5, 0x3c, + 0x6b, 0xca, 0x39, 0xf5, 0x84, 0xd6, 0x51, 0x9a, 0x90, 0xaf, 0x0e, 0x62, 0x21, 0x7a, 0x1f, 0xb6, + 0x04, 0x13, 0xc4, 0x1d, 0x58, 0x6c, 0xe2, 0xbb, 0x34, 0x8c, 0x8e, 0x35, 0x85, 0x2c, 0xab, 0x0f, + 0x07, 0x73, 0x79, 0xdd, 0x80, 0x42, 0x7c, 0x97, 0xf5, 0x3f, 0x6f, 0x40, 0x3e, 0xf2, 0x74, 0x74, + 0x0f, 0x80, 0xc4, 0x07, 0xa5, 0x0f, 0xe2, 0xdd, 0x17, 0x3a, 0x55, 0x49, 0x97, 0x1e, 0x3e, 0xa7, + 0xa3, 0x26, 0x14, 0x2c, 0xe6, 0x09, 0xe2, 0x78, 0x94, 0xeb, 0x48, 0x5d, 0xea, 0x9f, 0x07, 0x11, + 0x48, 0xeb, 0x98, 0xb3, 0x50, 0x0b, 0x72, 0x23, 0xea, 0x51, 0xee, 0x58, 0xda, 0xc1, 0xdf, 0x5e, + 0xea, 0x98, 0x21, 0x04, 0x4f, 0x3d, 0xe1, 0x4c, 0xa8, 0xd6, 0x12, 0x11, 0xd1, 0xe7, 0x50, 0xe0, + 0x34, 0x60, 0x53, 0x6e, 0xd1, 0x40, 0x87, 0xfb, 0xee, 0xf2, 0x30, 0x09, 0x41, 0x98, 0xfe, 0x66, + 0xea, 0x70, 0x2a, 0xb7, 0x10, 0xe0, 0x39, 0x15, 0x7d, 0x0a, 0x39, 0x4e, 0x03, 0x41, 0xb8, 0x78, + 0x5e, 0xc4, 0xe2, 0x10, 0xd2, 0x63, 0xae, 0x63, 0xcd, 0x70, 0xc4, 0x40, 0x9f, 0x42, 0xc1, 0x77, + 0x89, 0xa5, 0xb4, 0x9a, 0x1b, 0xab, 0x63, 0xac, 0x17, 0x81, 0xf0, 0x1c, 0x8f, 0x3e, 0x06, 0x70, + 0xd9, 0x68, 0x60, 0x73, 0xe7, 0x94, 0x72, 0x1d, 0x65, 0x95, 0x65, 0xec, 0xb6, 0x42, 0xe0, 0x82, + 0xcb, 0x46, 0xe1, 0x10, 0xdd, 0xf9, 0xaf, 0x82, 0x24, 0x11, 0x20, 0x6f, 0x40, 0xf1, 0x98, 0x71, + 0x8b, 0x0e, 0x74, 0xac, 0x17, 0x94, 0x6f, 0x19, 0x4a, 0x16, 0x06, 0x28, 0xfa, 0x15, 0xbc, 0x12, + 0x9d, 0xd6, 0x80, 0xd3, 0x63, 0xca, 0xa9, 0x27, 0x8f, 0xdc, 0x50, 0xcb, 0xbe, 0xf5, 0xfc, 0x23, + 0xd7, 0x68, 0x9d, 0x6a, 0x11, 0xbf, 0xfc, 0x21, 0x68, 0x15, 0x20, 0xc7, 0xc3, 0x0b, 0xae, 0xff, + 0x2e, 0x2d, 0xe3, 0xec, 0x12, 0x02, 0xed, 0x81, 0x11, 0x2f, 0xef, 0xd8, 0xca, 0xe1, 0x0a, 0xad, + 0xcd, 0x8b, 0xf3, 0x1d, 0x88, 0xb0, 0xdd, 0xb6, 0xcc, 0xc0, 0x7a, 0x6c, 0xa3, 0x0e, 0x94, 0x62, + 0x82, 0x2c, 0x82, 0x74, 0x99, 0x50, 0x7b, 0x9e, 0xa5, 0x47, 0x33, 0x9f, 0xe2, 0x22, 0x4f, 0xcc, + 0xea, 0xbf, 0x04, 0xf4, 0xac, 0x03, 0x22, 0x04, 0x99, 0x13, 0xc7, 0xd3, 0x66, 0x60, 0x35, 0x46, + 0x0d, 0xc8, 0xf9, 0x64, 0xe6, 0x32, 0x62, 0x6b, 0x3f, 0xbc, 0xde, 0x08, 0xcb, 0xa3, 0x46, 0x54, + 0x1e, 0x35, 0x9a, 0xde, 0x0c, 0x47, 0xa0, 0xfa, 0x3d, 0x78, 0x75, 0x69, 0x9c, 0xa1, 0x7d, 0x28, + 0xc6, 0x31, 0x32, 0xdf, 0xeb, 0xb5, 0x8b, 0xf3, 0x1d, 0x23, 0x0e, 0xa6, 0x6e, 0x1b, 0x1b, 0x31, + 0xa8, 0x6b, 0xd7, 0xff, 0xba, 0x09, 0xa5, 0x85, 0x48, 0x43, 0xd7, 0x61, 0xc3, 0x99, 0x90, 0x11, + 0xd5, 0x36, 0x86, 0x13, 0xd4, 0x81, 0xac, 0x4b, 0x86, 0xd4, 0x95, 0xb1, 0x22, 0x2f, 0xee, 0xff, + 0xae, 0x0c, 0xd9, 0xc6, 0x7d, 0x85, 0xef, 0x78, 0x82, 0xcf, 0xb0, 0x26, 0x23, 0x13, 0x72, 0x16, + 0x9b, 0x4c, 0x88, 0x27, 0x1f, 0xc9, 0xf5, 0xdd, 0x02, 0x8e, 0xa6, 0xf2, 0x64, 0x08, 0x1f, 0x05, + 0x66, 0x46, 0x89, 0xd5, 0x58, 0xe6, 0xc8, 0x31, 0x0b, 0x84, 0x47, 0x26, 0xd4, 0xdc, 0x54, 0xd6, + 0xc4, 0x73, 0x54, 0x86, 0x75, 0xea, 0x9d, 0x9a, 0x1b, 0x0a, 0x2e, 0x87, 0x52, 0x62, 0x3b, 0x61, + 0x20, 0x14, 0xb0, 0x1c, 0x4a, 0x9d, 0xd3, 0x80, 0x72, 0x33, 0x17, 0x9e, 0xb6, 0x1c, 0xa3, 0xd7, + 0x20, 0x3b, 0xe2, 0x6c, 0xea, 0x87, 0x1e, 0x58, 0xc0, 0x7a, 0x26, 0xdf, 0x3b, 0x9f, 0x3b, 0xa7, + 0x8e, 0x4b, 0x47, 0x34, 0x30, 0x5f, 0x53, 0x17, 0x51, 0x5d, 0x1a, 0x8b, 0x31, 0x0a, 0x27, 0x18, + 0xa8, 0x01, 0x19, 0xc7, 0x73, 0x84, 0xf9, 0xba, 0x8e, 0xc3, 0xcb, 0x57, 0xd8, 0x62, 0xcc, 0x7d, + 0x4c, 0xdc, 0x29, 0xc5, 0x0a, 0x87, 0xb6, 0x61, 0x5d, 0x88, 0x99, 0x59, 0xaa, 0xa5, 0x77, 0xf3, + 0xad, 0xdc, 0xc5, 0xf9, 0xce, 0xfa, 0xd1, 0xd1, 0x13, 0x2c, 0x65, 0xe8, 0x16, 0x00, 0xf3, 0xa9, + 0x37, 0x08, 0x84, 0xed, 0x78, 0x26, 0x92, 0x08, 0x5c, 0x90, 0x92, 0xbe, 0x14, 0xa0, 0x1b, 0x32, + 0x73, 0x11, 0x7b, 0xc0, 0x3c, 0x77, 0x66, 0xbe, 0xa2, 0xbe, 0xe6, 0xa5, 0xe0, 0xa1, 0xe7, 0xce, + 0xd0, 0x0e, 0x18, 0x81, 0x60, 0xfe, 0x20, 0x70, 0x46, 0x1e, 0x71, 0xcd, 0xeb, 0x6a, 0xe7, 0x20, + 0x45, 0x7d, 0x25, 0x41, 0x3f, 0x86, 0xec, 0x84, 0x4d, 0x3d, 0x11, 0x98, 0x79, 0x75, 0x91, 0xdb, + 0xcb, 0xf6, 0xf8, 0x40, 0x22, 0x74, 0xd4, 0x69, 0x38, 0xea, 0xc0, 0x96, 0xd2, 0x3c, 0xe2, 0xc4, + 0xa2, 0x03, 0x9f, 0x72, 0x87, 0xd9, 0xfa, 0x7d, 0xde, 0x7e, 0x66, 0xb7, 0x6d, 0xdd, 0x0a, 0xe0, + 0x6b, 0x92, 0x73, 0x47, 0x52, 0x7a, 0x8a, 0x81, 0x7a, 0x50, 0xf4, 0xa7, 0xae, 0x3b, 0x60, 0x7e, + 0xf8, 0x1a, 0x85, 0x09, 0xfc, 0x05, 0xdc, 0xa9, 0x37, 0x75, 0xdd, 0x87, 0x21, 0x09, 0x1b, 0xfe, + 0x7c, 0x82, 0x3e, 0x83, 0x5c, 0x40, 0x2d, 0x4e, 0x45, 0x60, 0x16, 0xd5, 0x96, 0xde, 0x5c, 0xa6, + 0xac, 0xaf, 0x20, 0x71, 0x5e, 0xc0, 0x11, 0x47, 0xd2, 0x2d, 0x95, 0xd6, 0x02, 0xf3, 0xd5, 0xd5, + 0x74, 0x9d, 0xf9, 0xe6, 0x74, 0xcd, 0x91, 0xe1, 0x22, 0x7d, 0x32, 0x30, 0xb7, 0x94, 0x3b, 0x85, + 0x13, 0xf4, 0x04, 0xc0, 0xf6, 0x82, 0x41, 0x08, 0x32, 0xaf, 0xa9, 0x3d, 0xbe, 0x7f, 0xf5, 0x1e, + 0xdb, 0x87, 0x7d, 0x5d, 0x87, 0x94, 0x2e, 0xce, 0x77, 0x0a, 0xf1, 0x14, 0x17, 0x6c, 0x2f, 0x08, + 0x87, 0xa8, 0x05, 0xc6, 0x98, 0x12, 0x57, 0x8c, 0xad, 0x31, 0xb5, 0x4e, 0xcc, 0xf2, 0xea, 0xb2, + 0xe4, 0xae, 0x82, 0x69, 0x0d, 0x49, 0x12, 0xea, 0x42, 0xc1, 0x09, 0x98, 0xab, 0xae, 0xc8, 0x34, + 0x55, 0x7e, 0x7b, 0x01, 0xeb, 0xba, 0x11, 0x05, 0xcf, 0xd9, 0xe8, 0x26, 0x14, 0x7c, 0xc7, 0x0e, + 0xee, 0x3b, 0x13, 0x47, 0x98, 0xdb, 0xb5, 0xf4, 0xee, 0x3a, 0x9e, 0x0b, 0xd0, 0x5d, 0xc8, 0x05, + 0xb3, 0xc0, 0x12, 0x6e, 0x60, 0x56, 0xd4, 0xe1, 0x36, 0xae, 0x5e, 0xa6, 0x1f, 0x12, 0xc2, 0xc4, + 0x11, 0xd1, 0x65, 0xc5, 0x63, 0x11, 0x5f, 0xf7, 0x02, 0x03, 0x62, 0xdb, 0xe6, 0x0d, 0x75, 0xe0, + 0xa5, 0xb9, 0xb4, 0x69, 0xdb, 0xe8, 0x1d, 0xb8, 0x96, 0x80, 0xd9, 0x9c, 0xf9, 0xe6, 0x4d, 0x85, + 0x4b, 0xb0, 0xdb, 0x9c, 0xf9, 0xb2, 0x86, 0x98, 0xba, 0xd2, 0xc6, 0xc0, 0xbc, 0xa5, 0x2c, 0xdb, + 0xbd, 0xda, 0xb2, 0x47, 0x8a, 0x80, 0x23, 0x62, 0xe5, 0x63, 0x30, 0x12, 0x49, 0x4e, 0x26, 0xa0, + 0x13, 0x3a, 0xd3, 0x79, 0x53, 0x0e, 0xa5, 0x73, 0x9c, 0xca, 0x98, 0x57, 0x89, 0xbd, 0x80, 0xc3, + 0xc9, 0x27, 0x6b, 0x1f, 0xa5, 0x2b, 0xfb, 0x60, 0x24, 0x1c, 0x1a, 0xbd, 0x29, 0x1f, 0x9d, 0x91, + 0x13, 0x08, 0x3e, 0x1b, 0x90, 0xa9, 0x18, 0x9b, 0x3f, 0x57, 0x84, 0x62, 0x24, 0x6c, 0x4e, 0xc5, + 0xb8, 0x32, 0x80, 0xb9, 0x47, 0xa0, 0x1a, 0x18, 0x32, 0x0f, 0x06, 0x94, 0x9f, 0x52, 0x2e, 0x4b, + 0x48, 0xb9, 0xc9, 0xa4, 0x48, 0x66, 0xba, 0x80, 0x12, 0x6e, 0x8d, 0x55, 0xca, 0x2e, 0x60, 0x3d, + 0x93, 0x39, 0x38, 0x0a, 0x3e, 0x9d, 0x83, 0xf5, 0xb4, 0xf2, 0x09, 0x14, 0x93, 0x87, 0xff, 0x1f, + 0x6d, 0xa8, 0x0d, 0xd9, 0xf0, 0x78, 0x64, 0xd6, 0x55, 0x19, 0x5b, 0xbf, 0x71, 0x2a, 0x5b, 0x23, + 0xc8, 0x04, 0xec, 0x58, 0x28, 0xda, 0x3a, 0x56, 0x63, 0x29, 0x1b, 0x13, 0x1e, 0x76, 0x4b, 0xeb, + 0x58, 0x8d, 0xeb, 0x7f, 0x49, 0x43, 0x21, 0x76, 0x33, 0xf4, 0x01, 0x6c, 0x75, 0xfb, 0x0f, 0xef, + 0x37, 0x8f, 0xba, 0x0f, 0x0f, 0x07, 0xed, 0xce, 0xe7, 0xcd, 0x47, 0xf7, 0x8f, 0xca, 0xa9, 0xca, + 0xad, 0xa7, 0x67, 0xb5, 0xed, 0xf9, 0x8b, 0x16, 0xc1, 0xdb, 0xf4, 0x98, 0x4c, 0x5d, 0xb1, 0xc8, + 0xea, 0xe1, 0x87, 0x07, 0x9d, 0x7e, 0xbf, 0x9c, 0x5e, 0xc5, 0xea, 0x71, 0x66, 0xd1, 0x20, 0x40, + 0xfb, 0x50, 0x9e, 0xb3, 0xee, 0x3e, 0xe9, 0x75, 0xf0, 0xe3, 0xf2, 0x5a, 0xe5, 0xe6, 0xd3, 0xb3, + 0x9a, 0xf9, 0x2c, 0xe9, 0xee, 0xcc, 0xa7, 0xfc, 0xb1, 0x6e, 0x46, 0xff, 0x91, 0x86, 0x62, 0xb2, + 0x03, 0x40, 0x07, 0x61, 0xdd, 0xaf, 0x0e, 0x60, 0x73, 0x7f, 0xef, 0xaa, 0x8e, 0x41, 0x55, 0x11, + 0xee, 0x54, 0xea, 0x7d, 0xc0, 0x6c, 0x8a, 0x15, 0x19, 0x7d, 0x00, 0x1b, 0x3e, 0xe3, 0x22, 0x7a, + 0x6f, 0x97, 0x3f, 0x45, 0x8c, 0x47, 0x25, 0x59, 0x08, 0xae, 0x8f, 0x61, 0x73, 0x51, 0x1b, 0xba, + 0x0d, 0xeb, 0x8f, 0xbb, 0xbd, 0x72, 0xaa, 0x72, 0xe3, 0xe9, 0x59, 0xed, 0xf5, 0xc5, 0x8f, 0x8f, + 0x1d, 0x2e, 0xa6, 0xc4, 0xed, 0xf6, 0xd0, 0x7b, 0xb0, 0xd1, 0x3e, 0xec, 0x63, 0x5c, 0x4e, 0x57, + 0x76, 0x9e, 0x9e, 0xd5, 0x6e, 0x2c, 0xe2, 0xe4, 0x27, 0x36, 0xf5, 0x6c, 0xcc, 0x86, 0x71, 0x0b, + 0xfe, 0xcf, 0x35, 0x30, 0x74, 0x19, 0xf2, 0xb2, 0xff, 0xd2, 0x94, 0xc2, 0x92, 0x36, 0xca, 0x9e, + 0x6b, 0x57, 0x56, 0xb6, 0xc5, 0x90, 0xa0, 0x23, 0xe3, 0x0d, 0x28, 0x3a, 0xfe, 0xe9, 0x87, 0x03, + 0xea, 0x91, 0xa1, 0xab, 0xbb, 0xf1, 0x3c, 0x36, 0xa4, 0xac, 0x13, 0x8a, 0x64, 0x61, 0xe1, 0x78, + 0x82, 0x72, 0x4f, 0xf7, 0xd9, 0x79, 0x1c, 0xcf, 0xd1, 0x67, 0x90, 0x71, 0x7c, 0x32, 0xd1, 0xe5, + 0xf8, 0xd2, 0x1d, 0x74, 0x7b, 0xcd, 0x07, 0x3a, 0x72, 0x5b, 0xf9, 0x8b, 0xf3, 0x9d, 0x8c, 0x14, + 0x60, 0x45, 0x43, 0xd5, 0xa8, 0x57, 0x92, 0x2b, 0xa9, 0x62, 0x24, 0x8f, 0x13, 0x12, 0x19, 0x7d, + 0x8e, 0x37, 0xe2, 0x34, 0x08, 0x54, 0x59, 0x92, 0xc7, 0xd1, 0x14, 0x55, 0x20, 0xa7, 0xeb, 0x6a, + 0xd5, 0x62, 0x15, 0x64, 0xb7, 0xa2, 0x05, 0xad, 0x12, 0x18, 0xe1, 0x69, 0x0c, 0x8e, 0x39, 0x9b, + 0xd4, 0xff, 0x95, 0x01, 0xe3, 0xc0, 0x9d, 0x06, 0x42, 0xd7, 0x6c, 0x2f, 0xed, 0xf0, 0x9f, 0xc0, + 0x16, 0x51, 0x7f, 0x7d, 0x88, 0x27, 0x1f, 0x79, 0xd5, 0xae, 0xe8, 0x0b, 0xb8, 0xbd, 0x54, 0x5d, + 0x0c, 0x0e, 0x5b, 0x9b, 0x56, 0x56, 0xea, 0x34, 0xd3, 0xb8, 0x4c, 0x2e, 0x7d, 0x41, 0x7d, 0x28, + 0x31, 0x6e, 0x8d, 0x69, 0x20, 0xc2, 0xd2, 0x40, 0xff, 0x25, 0x59, 0xfa, 0xff, 0xec, 0x61, 0x12, + 0xa8, 0x5f, 0xc4, 0xd0, 0xda, 0x45, 0x1d, 0xe8, 0x23, 0xc8, 0x70, 0x72, 0x1c, 0xb5, 0x5e, 0x4b, + 0x83, 0x04, 0x93, 0x63, 0xb1, 0xa0, 0x42, 0x31, 0xd0, 0x17, 0x00, 0xb6, 0x13, 0xf8, 0x44, 0x58, + 0x63, 0xca, 0xf5, 0x65, 0x2f, 0xdd, 0x62, 0x3b, 0x46, 0x2d, 0x68, 0x49, 0xb0, 0xd1, 0x3d, 0x28, + 0x58, 0x24, 0x72, 0xd7, 0xec, 0xea, 0x5f, 0x47, 0x07, 0x4d, 0xad, 0xa2, 0x2c, 0x55, 0x5c, 0x9c, + 0xef, 0xe4, 0x23, 0x09, 0xce, 0x5b, 0x44, 0xbb, 0xef, 0x3d, 0x28, 0x09, 0x12, 0x9c, 0x0c, 0xec, + 0x30, 0x9d, 0x85, 0x6e, 0xb2, 0xe2, 0x85, 0x97, 0x1d, 0xba, 0x4e, 0x7b, 0xd1, 0x75, 0x16, 0x45, + 0x42, 0x86, 0x7e, 0x01, 0x5b, 0xd4, 0xb3, 0xf8, 0x4c, 0x39, 0x6b, 0x64, 0x61, 0x7e, 0xf5, 0x66, + 0x3b, 0x31, 0x78, 0x61, 0xb3, 0x65, 0x7a, 0x49, 0x5e, 0xff, 0x5b, 0x1a, 0x20, 0x2c, 0xa9, 0x5e, + 0xae, 0x03, 0x22, 0xc8, 0xd8, 0x44, 0x10, 0xe5, 0x73, 0x45, 0xac, 0xc6, 0xe8, 0x13, 0x00, 0x41, + 0x27, 0xbe, 0x4c, 0xbd, 0xde, 0x48, 0xbb, 0xcd, 0xf3, 0xd2, 0x41, 0x02, 0x8d, 0xf6, 0x21, 0xab, + 0x1b, 0xe4, 0xcc, 0x95, 0x3c, 0x8d, 0xac, 0xff, 0x31, 0x0d, 0x10, 0x6e, 0xf3, 0x7f, 0x7a, 0x6f, + 0xad, 0xdb, 0xdf, 0x7c, 0x5f, 0x4d, 0x7d, 0xf7, 0x7d, 0x35, 0xf5, 0xdb, 0x8b, 0x6a, 0xfa, 0x9b, + 0x8b, 0x6a, 0xfa, 0xdb, 0x8b, 0x6a, 0xfa, 0xef, 0x17, 0xd5, 0xf4, 0xef, 0x7f, 0xa8, 0xa6, 0xbe, + 0xfd, 0xa1, 0x9a, 0xfa, 0xee, 0x87, 0x6a, 0x6a, 0x98, 0x55, 0x45, 0xf9, 0x8f, 0xfe, 0x1d, 0x00, + 0x00, 0xff, 0xff, 0x74, 0x9e, 0x83, 0x44, 0x31, 0x18, 0x00, 0x00, } func (m *NodeSpec) Copy() *NodeSpec { @@ -904,7 +1480,7 @@ func (m *NodeSpec) CopyFrom(src interface{}) { o := src.(*NodeSpec) *m = *o - deepcopy.Copy(&m.Annotations, &o.Annotations) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Annotations, &o.Annotations) } func (m *ServiceSpec) Copy() *ServiceSpec { @@ -920,27 +1496,27 @@ func (m *ServiceSpec) CopyFrom(src interface{}) { o := src.(*ServiceSpec) *m = *o - deepcopy.Copy(&m.Annotations, &o.Annotations) - deepcopy.Copy(&m.Task, &o.Task) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Annotations, &o.Annotations) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Task, &o.Task) if o.Update != nil { m.Update = &UpdateConfig{} - deepcopy.Copy(m.Update, o.Update) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Update, o.Update) } if o.Rollback != nil { m.Rollback = &UpdateConfig{} - deepcopy.Copy(m.Rollback, o.Rollback) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Rollback, o.Rollback) } if o.Networks != nil { m.Networks = make([]*NetworkAttachmentConfig, len(o.Networks)) for i := range m.Networks { m.Networks[i] = &NetworkAttachmentConfig{} - deepcopy.Copy(m.Networks[i], o.Networks[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Networks[i], o.Networks[i]) } } if o.Endpoint != nil { m.Endpoint = &EndpointSpec{} - deepcopy.Copy(m.Endpoint, o.Endpoint) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Endpoint, o.Endpoint) } if o.Mode != nil { switch o.Mode.(type) { @@ -948,13 +1524,25 @@ func (m *ServiceSpec) CopyFrom(src interface{}) { v := ServiceSpec_Replicated{ Replicated: &ReplicatedService{}, } - deepcopy.Copy(v.Replicated, o.GetReplicated()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Replicated, o.GetReplicated()) m.Mode = &v case *ServiceSpec_Global: v := ServiceSpec_Global{ Global: &GlobalService{}, } - deepcopy.Copy(v.Global, o.GetGlobal()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Global, o.GetGlobal()) + m.Mode = &v + case *ServiceSpec_ReplicatedJob: + v := ServiceSpec_ReplicatedJob{ + ReplicatedJob: &ReplicatedJob{}, + } + github_com_docker_swarmkit_api_deepcopy.Copy(v.ReplicatedJob, o.GetReplicatedJob()) + m.Mode = &v + case *ServiceSpec_GlobalJob: + v := ServiceSpec_GlobalJob{ + GlobalJob: &GlobalJob{}, + } + github_com_docker_swarmkit_api_deepcopy.Copy(v.GlobalJob, o.GetGlobalJob()) m.Mode = &v } } @@ -986,7 +1574,32 @@ func (m *GlobalService) Copy() *GlobalService { } func (m *GlobalService) CopyFrom(src interface{}) {} -func (m *TaskSpec) Copy() *TaskSpec { +func (m *ReplicatedJob) Copy() *ReplicatedJob { + if m == nil { + return nil + } + o := &ReplicatedJob{} + o.CopyFrom(m) + return o +} + +func (m *ReplicatedJob) CopyFrom(src interface{}) { + + o := src.(*ReplicatedJob) + *m = *o +} + +func (m *GlobalJob) Copy() *GlobalJob { + if m == nil { + return nil + } + o := &GlobalJob{} + o.CopyFrom(m) + return o +} + +func (m *GlobalJob) CopyFrom(src interface{}) {} +func (m *TaskSpec) Copy() *TaskSpec { if m == nil { return nil } @@ -1001,32 +1614,32 @@ func (m *TaskSpec) CopyFrom(src interface{}) { *m = *o if o.Resources != nil { m.Resources = &ResourceRequirements{} - deepcopy.Copy(m.Resources, o.Resources) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Resources, o.Resources) } if o.Restart != nil { m.Restart = &RestartPolicy{} - deepcopy.Copy(m.Restart, o.Restart) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Restart, o.Restart) } if o.Placement != nil { m.Placement = &Placement{} - deepcopy.Copy(m.Placement, o.Placement) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Placement, o.Placement) } if o.LogDriver != nil { m.LogDriver = &Driver{} - deepcopy.Copy(m.LogDriver, o.LogDriver) + github_com_docker_swarmkit_api_deepcopy.Copy(m.LogDriver, o.LogDriver) } if o.Networks != nil { m.Networks = make([]*NetworkAttachmentConfig, len(o.Networks)) for i := range m.Networks { m.Networks[i] = &NetworkAttachmentConfig{} - deepcopy.Copy(m.Networks[i], o.Networks[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Networks[i], o.Networks[i]) } } if o.ResourceReferences != nil { m.ResourceReferences = make([]ResourceReference, len(o.ResourceReferences)) for i := range m.ResourceReferences { - deepcopy.Copy(&m.ResourceReferences[i], &o.ResourceReferences[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.ResourceReferences[i], &o.ResourceReferences[i]) } } @@ -1036,19 +1649,19 @@ func (m *TaskSpec) CopyFrom(src interface{}) { v := TaskSpec_Attachment{ Attachment: &NetworkAttachmentSpec{}, } - deepcopy.Copy(v.Attachment, o.GetAttachment()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Attachment, o.GetAttachment()) m.Runtime = &v case *TaskSpec_Container: v := TaskSpec_Container{ Container: &ContainerSpec{}, } - deepcopy.Copy(v.Container, o.GetContainer()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Container, o.GetContainer()) m.Runtime = &v case *TaskSpec_Generic: v := TaskSpec_Generic{ Generic: &GenericRuntimeSpec{}, } - deepcopy.Copy(v.Generic, o.GetGeneric()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Generic, o.GetGeneric()) m.Runtime = &v } } @@ -1084,8 +1697,8 @@ func (m *GenericRuntimeSpec) CopyFrom(src interface{}) { o := src.(*GenericRuntimeSpec) *m = *o if o.Payload != nil { - m.Payload = &google_protobuf3.Any{} - deepcopy.Copy(m.Payload, o.Payload) + m.Payload = &types.Any{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Payload, o.Payload) } } @@ -1146,32 +1759,32 @@ func (m *ContainerSpec) CopyFrom(src interface{}) { if o.Privileges != nil { m.Privileges = &Privileges{} - deepcopy.Copy(m.Privileges, o.Privileges) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Privileges, o.Privileges) } if o.Init != nil { - m.Init = &google_protobuf4.BoolValue{} - deepcopy.Copy(m.Init, o.Init) + m.Init = &types.BoolValue{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Init, o.Init) } if o.Mounts != nil { m.Mounts = make([]Mount, len(o.Mounts)) for i := range m.Mounts { - deepcopy.Copy(&m.Mounts[i], &o.Mounts[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Mounts[i], &o.Mounts[i]) } } if o.StopGracePeriod != nil { - m.StopGracePeriod = &google_protobuf1.Duration{} - deepcopy.Copy(m.StopGracePeriod, o.StopGracePeriod) + m.StopGracePeriod = &types.Duration{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.StopGracePeriod, o.StopGracePeriod) } if o.PullOptions != nil { m.PullOptions = &ContainerSpec_PullOptions{} - deepcopy.Copy(m.PullOptions, o.PullOptions) + github_com_docker_swarmkit_api_deepcopy.Copy(m.PullOptions, o.PullOptions) } if o.Secrets != nil { m.Secrets = make([]*SecretReference, len(o.Secrets)) for i := range m.Secrets { m.Secrets[i] = &SecretReference{} - deepcopy.Copy(m.Secrets[i], o.Secrets[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Secrets[i], o.Secrets[i]) } } @@ -1179,7 +1792,7 @@ func (m *ContainerSpec) CopyFrom(src interface{}) { m.Configs = make([]*ConfigReference, len(o.Configs)) for i := range m.Configs { m.Configs[i] = &ConfigReference{} - deepcopy.Copy(m.Configs[i], o.Configs[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Configs[i], o.Configs[i]) } } @@ -1190,11 +1803,11 @@ func (m *ContainerSpec) CopyFrom(src interface{}) { if o.DNSConfig != nil { m.DNSConfig = &ContainerSpec_DNSConfig{} - deepcopy.Copy(m.DNSConfig, o.DNSConfig) + github_com_docker_swarmkit_api_deepcopy.Copy(m.DNSConfig, o.DNSConfig) } if o.Healthcheck != nil { m.Healthcheck = &HealthConfig{} - deepcopy.Copy(m.Healthcheck, o.Healthcheck) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Healthcheck, o.Healthcheck) } if o.Sysctls != nil { m.Sysctls = make(map[string]string, len(o.Sysctls)) @@ -1203,10 +1816,24 @@ func (m *ContainerSpec) CopyFrom(src interface{}) { } } - if o.MemorySwappiness != nil { - m.MemorySwappiness = &google_protobuf4.Int64Value{} - deepcopy.Copy(m.MemorySwappiness, o.MemorySwappiness) + if o.CapabilityAdd != nil { + m.CapabilityAdd = make([]string, len(o.CapabilityAdd)) + copy(m.CapabilityAdd, o.CapabilityAdd) + } + + if o.CapabilityDrop != nil { + m.CapabilityDrop = make([]string, len(o.CapabilityDrop)) + copy(m.CapabilityDrop, o.CapabilityDrop) + } + + if o.Ulimits != nil { + m.Ulimits = make([]*ContainerSpec_Ulimit, len(o.Ulimits)) + for i := range m.Ulimits { + m.Ulimits[i] = &ContainerSpec_Ulimit{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Ulimits[i], o.Ulimits[i]) + } } + } func (m *ContainerSpec_PullOptions) Copy() *ContainerSpec_PullOptions { @@ -1254,6 +1881,21 @@ func (m *ContainerSpec_DNSConfig) CopyFrom(src interface{}) { } +func (m *ContainerSpec_Ulimit) Copy() *ContainerSpec_Ulimit { + if m == nil { + return nil + } + o := &ContainerSpec_Ulimit{} + o.CopyFrom(m) + return o +} + +func (m *ContainerSpec_Ulimit) CopyFrom(src interface{}) { + + o := src.(*ContainerSpec_Ulimit) + *m = *o +} + func (m *EndpointSpec) Copy() *EndpointSpec { if m == nil { return nil @@ -1271,7 +1913,7 @@ func (m *EndpointSpec) CopyFrom(src interface{}) { m.Ports = make([]*PortConfig, len(o.Ports)) for i := range m.Ports { m.Ports[i] = &PortConfig{} - deepcopy.Copy(m.Ports[i], o.Ports[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Ports[i], o.Ports[i]) } } @@ -1290,14 +1932,14 @@ func (m *NetworkSpec) CopyFrom(src interface{}) { o := src.(*NetworkSpec) *m = *o - deepcopy.Copy(&m.Annotations, &o.Annotations) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Annotations, &o.Annotations) if o.DriverConfig != nil { m.DriverConfig = &Driver{} - deepcopy.Copy(m.DriverConfig, o.DriverConfig) + github_com_docker_swarmkit_api_deepcopy.Copy(m.DriverConfig, o.DriverConfig) } if o.IPAM != nil { m.IPAM = &IPAMOptions{} - deepcopy.Copy(m.IPAM, o.IPAM) + github_com_docker_swarmkit_api_deepcopy.Copy(m.IPAM, o.IPAM) } if o.ConfigFrom != nil { switch o.ConfigFrom.(type) { @@ -1324,14 +1966,14 @@ func (m *ClusterSpec) CopyFrom(src interface{}) { o := src.(*ClusterSpec) *m = *o - deepcopy.Copy(&m.Annotations, &o.Annotations) - deepcopy.Copy(&m.AcceptancePolicy, &o.AcceptancePolicy) - deepcopy.Copy(&m.Orchestration, &o.Orchestration) - deepcopy.Copy(&m.Raft, &o.Raft) - deepcopy.Copy(&m.Dispatcher, &o.Dispatcher) - deepcopy.Copy(&m.CAConfig, &o.CAConfig) - deepcopy.Copy(&m.TaskDefaults, &o.TaskDefaults) - deepcopy.Copy(&m.EncryptionConfig, &o.EncryptionConfig) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Annotations, &o.Annotations) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.AcceptancePolicy, &o.AcceptancePolicy) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Orchestration, &o.Orchestration) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Raft, &o.Raft) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Dispatcher, &o.Dispatcher) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.CAConfig, &o.CAConfig) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.TaskDefaults, &o.TaskDefaults) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.EncryptionConfig, &o.EncryptionConfig) } func (m *SecretSpec) Copy() *SecretSpec { @@ -1347,18 +1989,18 @@ func (m *SecretSpec) CopyFrom(src interface{}) { o := src.(*SecretSpec) *m = *o - deepcopy.Copy(&m.Annotations, &o.Annotations) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Annotations, &o.Annotations) if o.Data != nil { m.Data = make([]byte, len(o.Data)) copy(m.Data, o.Data) } if o.Templating != nil { m.Templating = &Driver{} - deepcopy.Copy(m.Templating, o.Templating) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Templating, o.Templating) } if o.Driver != nil { m.Driver = &Driver{} - deepcopy.Copy(m.Driver, o.Driver) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Driver, o.Driver) } } @@ -1375,21 +2017,21 @@ func (m *ConfigSpec) CopyFrom(src interface{}) { o := src.(*ConfigSpec) *m = *o - deepcopy.Copy(&m.Annotations, &o.Annotations) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Annotations, &o.Annotations) if o.Data != nil { m.Data = make([]byte, len(o.Data)) copy(m.Data, o.Data) } if o.Templating != nil { m.Templating = &Driver{} - deepcopy.Copy(m.Templating, o.Templating) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Templating, o.Templating) } } func (m *NodeSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1397,40 +2039,47 @@ func (m *NodeSpec) Marshal() (dAtA []byte, err error) { } func (m *NodeSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NodeSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n1, err := m.Annotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n1 - if m.DesiredRole != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.DesiredRole)) + if m.Availability != 0 { + i = encodeVarintSpecs(dAtA, i, uint64(m.Availability)) + i-- + dAtA[i] = 0x20 } if m.Membership != 0 { - dAtA[i] = 0x18 - i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Membership)) + i-- + dAtA[i] = 0x18 } - if m.Availability != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Availability)) + if m.DesiredRole != 0 { + i = encodeVarintSpecs(dAtA, i, uint64(m.DesiredRole)) + i-- + dAtA[i] = 0x10 } - return i, nil + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *ServiceSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1438,110 +2087,185 @@ func (m *ServiceSpec) Marshal() (dAtA []byte, err error) { } func (m *ServiceSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ServiceSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n2, err := m.Annotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n2 - dAtA[i] = 0x12 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Task.Size())) - n3, err := m.Task.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n3 if m.Mode != nil { - nn4, err := m.Mode.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size := m.Mode.Size() + i -= size + if _, err := m.Mode.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += nn4 } - if m.Update != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Update.Size())) - n5, err := m.Update.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Rollback != nil { + { + size, err := m.Rollback.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + if m.Endpoint != nil { + { + size, err := m.Endpoint.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n5 + i-- + dAtA[i] = 0x42 } if len(m.Networks) > 0 { - for _, msg := range m.Networks { + for iNdEx := len(m.Networks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Networks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x3a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + } + } + if m.Update != nil { + { + size, err := m.Update.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x32 } - if m.Endpoint != nil { - dAtA[i] = 0x42 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Endpoint.Size())) - n6, err := m.Endpoint.MarshalTo(dAtA[i:]) + { + size, err := m.Task.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n6 + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - if m.Rollback != nil { - dAtA[i] = 0x4a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Rollback.Size())) - n7, err := m.Rollback.MarshalTo(dAtA[i:]) + i-- + dAtA[i] = 0x12 + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n7 + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *ServiceSpec_Replicated) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ServiceSpec_Replicated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Replicated != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Replicated.Size())) - n8, err := m.Replicated.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Replicated.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n8 + i-- + dAtA[i] = 0x1a } - return i, nil + return len(dAtA) - i, nil } func (m *ServiceSpec_Global) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ServiceSpec_Global) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Global != nil { + { + size, err := m.Global.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x22 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Global.Size())) - n9, err := m.Global.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + return len(dAtA) - i, nil +} +func (m *ServiceSpec_ReplicatedJob) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ServiceSpec_ReplicatedJob) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ReplicatedJob != nil { + { + size, err := m.ReplicatedJob.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + return len(dAtA) - i, nil +} +func (m *ServiceSpec_GlobalJob) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ServiceSpec_GlobalJob) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.GlobalJob != nil { + { + size, err := m.GlobalJob.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n9 + i-- + dAtA[i] = 0x5a } - return i, nil + return len(dAtA) - i, nil } func (m *ReplicatedService) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1549,22 +2273,27 @@ func (m *ReplicatedService) Marshal() (dAtA []byte, err error) { } func (m *ReplicatedService) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReplicatedService) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Replicas != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintSpecs(dAtA, i, uint64(m.Replicas)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *GlobalService) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1572,17 +2301,78 @@ func (m *GlobalService) Marshal() (dAtA []byte, err error) { } func (m *GlobalService) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GlobalService) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *ReplicatedJob) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ReplicatedJob) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ReplicatedJob) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.TotalCompletions != 0 { + i = encodeVarintSpecs(dAtA, i, uint64(m.TotalCompletions)) + i-- + dAtA[i] = 0x10 + } + if m.MaxConcurrent != 0 { + i = encodeVarintSpecs(dAtA, i, uint64(m.MaxConcurrent)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *GlobalJob) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GlobalJob) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GlobalJob) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + return len(dAtA) - i, nil } func (m *TaskSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1590,135 +2380,175 @@ func (m *TaskSpec) Marshal() (dAtA []byte, err error) { } func (m *TaskSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Runtime != nil { - nn10, err := m.Runtime.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.ResourceReferences) > 0 { + for iNdEx := len(m.ResourceReferences) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ResourceReferences[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a } - i += nn10 } - if m.Resources != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Resources.Size())) - n11, err := m.Resources.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Runtime != nil { + { + size := m.Runtime.Size() + i -= size + if _, err := m.Runtime.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += n11 } - if m.Restart != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Restart.Size())) - n12, err := m.Restart.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n12 + if m.ForceUpdate != 0 { + i = encodeVarintSpecs(dAtA, i, uint64(m.ForceUpdate)) + i-- + dAtA[i] = 0x48 } - if m.Placement != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Placement.Size())) - n13, err := m.Placement.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Networks) > 0 { + for iNdEx := len(m.Networks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Networks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a } - i += n13 } if m.LogDriver != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.LogDriver.Size())) - n14, err := m.LogDriver.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.LogDriver.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n14 + i-- + dAtA[i] = 0x32 } - if len(m.Networks) > 0 { - for _, msg := range m.Networks { - dAtA[i] = 0x3a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Placement != nil { + { + size, err := m.Placement.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x2a } - if m.ForceUpdate != 0 { - dAtA[i] = 0x48 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.ForceUpdate)) + if m.Restart != nil { + { + size, err := m.Restart.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 } - if len(m.ResourceReferences) > 0 { - for _, msg := range m.ResourceReferences { - dAtA[i] = 0x5a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Resources != nil { + { + size, err := m.Resources.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x12 } - return i, nil + return len(dAtA) - i, nil } func (m *TaskSpec_Container) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskSpec_Container) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Container != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Container.Size())) - n15, err := m.Container.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Container.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n15 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *TaskSpec_Attachment) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskSpec_Attachment) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Attachment != nil { - dAtA[i] = 0x42 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Attachment.Size())) - n16, err := m.Attachment.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Attachment.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n16 + i-- + dAtA[i] = 0x42 } - return i, nil + return len(dAtA) - i, nil } func (m *TaskSpec_Generic) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskSpec_Generic) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Generic != nil { - dAtA[i] = 0x52 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Generic.Size())) - n17, err := m.Generic.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Generic.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n17 + i-- + dAtA[i] = 0x52 } - return i, nil + return len(dAtA) - i, nil } func (m *ResourceReference) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1726,28 +2556,34 @@ func (m *ResourceReference) Marshal() (dAtA []byte, err error) { } func (m *ResourceReference) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceReference) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ResourceID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(m.ResourceID))) - i += copy(dAtA[i:], m.ResourceID) - } if m.ResourceType != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintSpecs(dAtA, i, uint64(m.ResourceType)) + i-- + dAtA[i] = 0x10 + } + if len(m.ResourceID) > 0 { + i -= len(m.ResourceID) + copy(dAtA[i:], m.ResourceID) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.ResourceID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *GenericRuntimeSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1755,33 +2591,41 @@ func (m *GenericRuntimeSpec) Marshal() (dAtA []byte, err error) { } func (m *GenericRuntimeSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenericRuntimeSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Kind) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(m.Kind))) - i += copy(dAtA[i:], m.Kind) - } if m.Payload != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Payload.Size())) - n18, err := m.Payload.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Payload.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n18 + i-- + dAtA[i] = 0x12 + } + if len(m.Kind) > 0 { + i -= len(m.Kind) + copy(dAtA[i:], m.Kind) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Kind))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *NetworkAttachmentSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1789,23 +2633,29 @@ func (m *NetworkAttachmentSpec) Marshal() (dAtA []byte, err error) { } func (m *NetworkAttachmentSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NetworkAttachmentSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) i = encodeVarintSpecs(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ContainerSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1813,333 +2663,354 @@ func (m *ContainerSpec) Marshal() (dAtA []byte, err error) { } func (m *ContainerSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Image) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(m.Image))) - i += copy(dAtA[i:], m.Image) - } - if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x12 - i++ - v := m.Labels[k] - mapSize := 1 + len(k) + sovSpecs(uint64(len(k))) + 1 + len(v) + sovSpecs(uint64(len(v))) - i = encodeVarintSpecs(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + if len(m.Ulimits) > 0 { + for iNdEx := len(m.Ulimits) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Ulimits[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xea } } - if len(m.Command) > 0 { - for _, s := range m.Command { - dAtA[i] = 0x1a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + if len(m.CapabilityDrop) > 0 { + for iNdEx := len(m.CapabilityDrop) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.CapabilityDrop[iNdEx]) + copy(dAtA[i:], m.CapabilityDrop[iNdEx]) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.CapabilityDrop[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe2 } } - if len(m.Args) > 0 { - for _, s := range m.Args { - dAtA[i] = 0x22 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + if len(m.CapabilityAdd) > 0 { + for iNdEx := len(m.CapabilityAdd) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.CapabilityAdd[iNdEx]) + copy(dAtA[i:], m.CapabilityAdd[iNdEx]) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.CapabilityAdd[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xda } } - if len(m.Env) > 0 { - for _, s := range m.Env { - dAtA[i] = 0x2a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + if len(m.Sysctls) > 0 { + for k := range m.Sysctls { + v := m.Sysctls[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintSpecs(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintSpecs(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintSpecs(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xd2 } } - if len(m.Dir) > 0 { - dAtA[i] = 0x32 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(m.Dir))) - i += copy(dAtA[i:], m.Dir) + if m.PidsLimit != 0 { + i = encodeVarintSpecs(dAtA, i, uint64(m.PidsLimit)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc8 } - if len(m.User) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(m.User))) - i += copy(dAtA[i:], m.User) + if m.Isolation != 0 { + i = encodeVarintSpecs(dAtA, i, uint64(m.Isolation)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc0 } - if len(m.Mounts) > 0 { - for _, msg := range m.Mounts { - dAtA[i] = 0x42 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Init != nil { + { + size, err := m.Init.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n - } - } - if m.StopGracePeriod != nil { - dAtA[i] = 0x4a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.StopGracePeriod.Size())) - n19, err := m.StopGracePeriod.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n19 - } - if m.PullOptions != nil { - dAtA[i] = 0x52 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.PullOptions.Size())) - n20, err := m.PullOptions.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n20 + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xba } - if len(m.Groups) > 0 { - for _, s := range m.Groups { - dAtA[i] = 0x5a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ + if m.Privileges != nil { + { + size, err := m.Privileges.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb2 } - if len(m.Secrets) > 0 { - for _, msg := range m.Secrets { - dAtA[i] = 0x62 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Configs) > 0 { + for iNdEx := len(m.Configs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Configs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xaa } } - if m.TTY { - dAtA[i] = 0x68 - i++ - if m.TTY { + if len(m.StopSignal) > 0 { + i -= len(m.StopSignal) + copy(dAtA[i:], m.StopSignal) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.StopSignal))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa2 + } + if m.ReadOnly { + i-- + if m.ReadOnly { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 } - if len(m.Hostname) > 0 { - dAtA[i] = 0x72 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(m.Hostname))) - i += copy(dAtA[i:], m.Hostname) + if m.OpenStdin { + i-- + if m.OpenStdin { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x90 } - if m.DNSConfig != nil { - dAtA[i] = 0x7a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.DNSConfig.Size())) - n21, err := m.DNSConfig.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Hosts) > 0 { + for iNdEx := len(m.Hosts) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Hosts[iNdEx]) + copy(dAtA[i:], m.Hosts[iNdEx]) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Hosts[iNdEx]))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x8a } - i += n21 } if m.Healthcheck != nil { - dAtA[i] = 0x82 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Healthcheck.Size())) - n22, err := m.Healthcheck.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Healthcheck.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n22 + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x82 } - if len(m.Hosts) > 0 { - for _, s := range m.Hosts { - dAtA[i] = 0x8a - i++ - dAtA[i] = 0x1 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ + if m.DNSConfig != nil { + { + size, err := m.DNSConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x7a } - if m.OpenStdin { - dAtA[i] = 0x90 - i++ - dAtA[i] = 0x1 - i++ - if m.OpenStdin { + if len(m.Hostname) > 0 { + i -= len(m.Hostname) + copy(dAtA[i:], m.Hostname) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Hostname))) + i-- + dAtA[i] = 0x72 + } + if m.TTY { + i-- + if m.TTY { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x68 } - if m.ReadOnly { - dAtA[i] = 0x98 - i++ - dAtA[i] = 0x1 - i++ - if m.ReadOnly { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + if len(m.Secrets) > 0 { + for iNdEx := len(m.Secrets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Secrets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 } - i++ } - if len(m.StopSignal) > 0 { - dAtA[i] = 0xa2 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(m.StopSignal))) - i += copy(dAtA[i:], m.StopSignal) + if len(m.Groups) > 0 { + for iNdEx := len(m.Groups) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Groups[iNdEx]) + copy(dAtA[i:], m.Groups[iNdEx]) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Groups[iNdEx]))) + i-- + dAtA[i] = 0x5a + } } - if len(m.Configs) > 0 { - for _, msg := range m.Configs { - dAtA[i] = 0xaa - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.PullOptions != nil { + { + size, err := m.PullOptions.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x52 } - if m.Privileges != nil { - dAtA[i] = 0xb2 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Privileges.Size())) - n23, err := m.Privileges.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.StopGracePeriod != nil { + { + size, err := m.StopGracePeriod.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n23 + i-- + dAtA[i] = 0x4a } - if m.Init != nil { - dAtA[i] = 0xba - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Init.Size())) - n24, err := m.Init.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Mounts) > 0 { + for iNdEx := len(m.Mounts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Mounts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 } - i += n24 } - if m.Isolation != 0 { - dAtA[i] = 0xc0 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Isolation)) + if len(m.User) > 0 { + i -= len(m.User) + copy(dAtA[i:], m.User) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.User))) + i-- + dAtA[i] = 0x3a } - if m.PidsLimit != 0 { - dAtA[i] = 0xc8 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.PidsLimit)) + if len(m.Dir) > 0 { + i -= len(m.Dir) + copy(dAtA[i:], m.Dir) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Dir))) + i-- + dAtA[i] = 0x32 } - if len(m.Sysctls) > 0 { - for k, _ := range m.Sysctls { - dAtA[i] = 0xd2 - i++ - dAtA[i] = 0x1 - i++ - v := m.Sysctls[k] - mapSize := 1 + len(k) + sovSpecs(uint64(len(k))) + 1 + len(v) + sovSpecs(uint64(len(v))) - i = encodeVarintSpecs(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + if len(m.Env) > 0 { + for iNdEx := len(m.Env) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Env[iNdEx]) + copy(dAtA[i:], m.Env[iNdEx]) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Env[iNdEx]))) + i-- + dAtA[i] = 0x2a } } - if m.MemorySwap != 0 { - dAtA[i] = 0xd8 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.MemorySwap)) + if len(m.Args) > 0 { + for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Args[iNdEx]) + copy(dAtA[i:], m.Args[iNdEx]) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Args[iNdEx]))) + i-- + dAtA[i] = 0x22 + } } - if m.MemorySwappiness != nil { - dAtA[i] = 0xe2 - i++ - dAtA[i] = 0x1 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.MemorySwappiness.Size())) - n25, err := m.MemorySwappiness.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Command) > 0 { + for iNdEx := len(m.Command) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Command[iNdEx]) + copy(dAtA[i:], m.Command[iNdEx]) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Command[iNdEx]))) + i-- + dAtA[i] = 0x1a } - i += n25 } - return i, nil + if len(m.Labels) > 0 { + for k := range m.Labels { + v := m.Labels[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintSpecs(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintSpecs(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintSpecs(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Image) > 0 { + i -= len(m.Image) + copy(dAtA[i:], m.Image) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Image))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *ContainerSpec_PullOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -2147,25 +3018,31 @@ func (m *ContainerSpec_PullOptions) Marshal() (dAtA []byte, err error) { } func (m *ContainerSpec_PullOptions) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerSpec_PullOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.RegistryAuth) > 0 { - dAtA[i] = 0x82 - i++ - dAtA[i] = 0x4 - i++ + i -= len(m.RegistryAuth) + copy(dAtA[i:], m.RegistryAuth) i = encodeVarintSpecs(dAtA, i, uint64(len(m.RegistryAuth))) - i += copy(dAtA[i:], m.RegistryAuth) + i-- + dAtA[i] = 0x4 + i-- + dAtA[i] = 0x82 } - return i, nil + return len(dAtA) - i, nil } func (m *ContainerSpec_DNSConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -2173,62 +3050,89 @@ func (m *ContainerSpec_DNSConfig) Marshal() (dAtA []byte, err error) { } func (m *ContainerSpec_DNSConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerSpec_DNSConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Nameservers) > 0 { - for _, s := range m.Nameservers { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + if len(m.Options) > 0 { + for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Options[iNdEx]) + copy(dAtA[i:], m.Options[iNdEx]) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Options[iNdEx]))) + i-- + dAtA[i] = 0x1a } } if len(m.Search) > 0 { - for _, s := range m.Search { + for iNdEx := len(m.Search) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Search[iNdEx]) + copy(dAtA[i:], m.Search[iNdEx]) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Search[iNdEx]))) + i-- dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - if len(m.Options) > 0 { - for _, s := range m.Options { - dAtA[i] = 0x1a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + if len(m.Nameservers) > 0 { + for iNdEx := len(m.Nameservers) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Nameservers[iNdEx]) + copy(dAtA[i:], m.Nameservers[iNdEx]) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Nameservers[iNdEx]))) + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil +} + +func (m *ContainerSpec_Ulimit) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ContainerSpec_Ulimit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerSpec_Ulimit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Hard != 0 { + i = encodeVarintSpecs(dAtA, i, uint64(m.Hard)) + i-- + dAtA[i] = 0x18 + } + if m.Soft != 0 { + i = encodeVarintSpecs(dAtA, i, uint64(m.Soft)) + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *EndpointSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -2236,34 +3140,41 @@ func (m *EndpointSpec) Marshal() (dAtA []byte, err error) { } func (m *EndpointSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EndpointSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Mode != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Mode)) - } if len(m.Ports) > 0 { - for _, msg := range m.Ports { - dAtA[i] = 0x12 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Ports) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Ports[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x12 } } - return i, nil + if m.Mode != 0 { + i = encodeVarintSpecs(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func (m *NetworkSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -2271,100 +3182,119 @@ func (m *NetworkSpec) Marshal() (dAtA []byte, err error) { } func (m *NetworkSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NetworkSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n26, err := m.Annotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n26 - if m.DriverConfig != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.DriverConfig.Size())) - n27, err := m.DriverConfig.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.ConfigFrom != nil { + { + size := m.ConfigFrom.Size() + i -= size + if _, err := m.ConfigFrom.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += n27 } - if m.Ipv6Enabled { - dAtA[i] = 0x18 - i++ - if m.Ipv6Enabled { + if m.Ingress { + i-- + if m.Ingress { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x38 } - if m.Internal { - dAtA[i] = 0x20 - i++ - if m.Internal { + if m.Attachable { + i-- + if m.Attachable { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x30 } if m.IPAM != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.IPAM.Size())) - n28, err := m.IPAM.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.IPAM.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n28 + i-- + dAtA[i] = 0x2a } - if m.Attachable { - dAtA[i] = 0x30 - i++ - if m.Attachable { + if m.Internal { + i-- + if m.Internal { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x20 } - if m.Ingress { - dAtA[i] = 0x38 - i++ - if m.Ingress { + if m.Ipv6Enabled { + i-- + if m.Ipv6Enabled { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x18 } - if m.ConfigFrom != nil { - nn29, err := m.ConfigFrom.MarshalTo(dAtA[i:]) + if m.DriverConfig != nil { + { + size, err := m.DriverConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += nn29 + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *NetworkSpec_Network) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x42 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NetworkSpec_Network) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.Network) + copy(dAtA[i:], m.Network) i = encodeVarintSpecs(dAtA, i, uint64(len(m.Network))) - i += copy(dAtA[i:], m.Network) - return i, nil + i-- + dAtA[i] = 0x42 + return len(dAtA) - i, nil } func (m *ClusterSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -2372,81 +3302,102 @@ func (m *ClusterSpec) Marshal() (dAtA []byte, err error) { } func (m *ClusterSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ClusterSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n30, err := m.Annotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.EncryptionConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n30 - dAtA[i] = 0x12 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.AcceptancePolicy.Size())) - n31, err := m.AcceptancePolicy.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + i-- + dAtA[i] = 0x42 + { + size, err := m.TaskDefaults.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n31 - dAtA[i] = 0x1a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Orchestration.Size())) - n32, err := m.Orchestration.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + i-- + dAtA[i] = 0x3a + { + size, err := m.CAConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n32 - dAtA[i] = 0x22 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Raft.Size())) - n33, err := m.Raft.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + i-- + dAtA[i] = 0x32 + { + size, err := m.Dispatcher.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n33 + i-- dAtA[i] = 0x2a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Dispatcher.Size())) - n34, err := m.Dispatcher.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Raft.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n34 - dAtA[i] = 0x32 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.CAConfig.Size())) - n35, err := m.CAConfig.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + i-- + dAtA[i] = 0x22 + { + size, err := m.Orchestration.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n35 - dAtA[i] = 0x3a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.TaskDefaults.Size())) - n36, err := m.TaskDefaults.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + i-- + dAtA[i] = 0x1a + { + size, err := m.AcceptancePolicy.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n36 - dAtA[i] = 0x42 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.EncryptionConfig.Size())) - n37, err := m.EncryptionConfig.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + i-- + dAtA[i] = 0x12 + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n37 - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *SecretSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -2454,51 +3405,63 @@ func (m *SecretSpec) Marshal() (dAtA []byte, err error) { } func (m *SecretSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SecretSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n38, err := m.Annotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n38 - if len(m.Data) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) + if m.Driver != nil { + { + size, err := m.Driver.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 } if m.Templating != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Templating.Size())) - n39, err := m.Templating.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Templating.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - i += n39 + i-- + dAtA[i] = 0x1a } - if m.Driver != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Driver.Size())) - n40, err := m.Driver.MarshalTo(dAtA[i:]) + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x12 + } + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n40 + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *ConfigSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -2506,48 +3469,62 @@ func (m *ConfigSpec) Marshal() (dAtA []byte, err error) { } func (m *ConfigSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConfigSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - dAtA[i] = 0xa - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Annotations.Size())) - n41, err := m.Annotations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Templating != nil { + { + size, err := m.Templating.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } - i += n41 if len(m.Data) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Data) + copy(dAtA[i:], m.Data) i = encodeVarintSpecs(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) + i-- + dAtA[i] = 0x12 } - if m.Templating != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintSpecs(dAtA, i, uint64(m.Templating.Size())) - n42, err := m.Templating.MarshalTo(dAtA[i:]) + { + size, err := m.Annotations.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n42 + i -= size + i = encodeVarintSpecs(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func encodeVarintSpecs(dAtA []byte, offset int, v uint64) int { + offset -= sovSpecs(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } - func (m *NodeSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Annotations.Size() @@ -2565,6 +3542,9 @@ func (m *NodeSpec) Size() (n int) { } func (m *ServiceSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Annotations.Size() @@ -2596,6 +3576,9 @@ func (m *ServiceSpec) Size() (n int) { } func (m *ServiceSpec_Replicated) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Replicated != nil { @@ -2605,6 +3588,9 @@ func (m *ServiceSpec_Replicated) Size() (n int) { return n } func (m *ServiceSpec_Global) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Global != nil { @@ -2613,7 +3599,34 @@ func (m *ServiceSpec_Global) Size() (n int) { } return n } +func (m *ServiceSpec_ReplicatedJob) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ReplicatedJob != nil { + l = m.ReplicatedJob.Size() + n += 1 + l + sovSpecs(uint64(l)) + } + return n +} +func (m *ServiceSpec_GlobalJob) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.GlobalJob != nil { + l = m.GlobalJob.Size() + n += 1 + l + sovSpecs(uint64(l)) + } + return n +} func (m *ReplicatedService) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Replicas != 0 { @@ -2623,12 +3636,42 @@ func (m *ReplicatedService) Size() (n int) { } func (m *GlobalService) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *ReplicatedJob) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.MaxConcurrent != 0 { + n += 1 + sovSpecs(uint64(m.MaxConcurrent)) + } + if m.TotalCompletions != 0 { + n += 1 + sovSpecs(uint64(m.TotalCompletions)) + } + return n +} + +func (m *GlobalJob) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l return n } func (m *TaskSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Runtime != nil { @@ -2669,6 +3712,9 @@ func (m *TaskSpec) Size() (n int) { } func (m *TaskSpec_Container) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Container != nil { @@ -2678,6 +3724,9 @@ func (m *TaskSpec_Container) Size() (n int) { return n } func (m *TaskSpec_Attachment) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Attachment != nil { @@ -2687,6 +3736,9 @@ func (m *TaskSpec_Attachment) Size() (n int) { return n } func (m *TaskSpec_Generic) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Generic != nil { @@ -2696,6 +3748,9 @@ func (m *TaskSpec_Generic) Size() (n int) { return n } func (m *ResourceReference) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ResourceID) @@ -2709,6 +3764,9 @@ func (m *ResourceReference) Size() (n int) { } func (m *GenericRuntimeSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Kind) @@ -2723,6 +3781,9 @@ func (m *GenericRuntimeSpec) Size() (n int) { } func (m *NetworkAttachmentSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -2733,6 +3794,9 @@ func (m *NetworkAttachmentSpec) Size() (n int) { } func (m *ContainerSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Image) @@ -2858,17 +3922,31 @@ func (m *ContainerSpec) Size() (n int) { n += mapEntrySize + 2 + sovSpecs(uint64(mapEntrySize)) } } - if m.MemorySwap != 0 { - n += 2 + sovSpecs(uint64(m.MemorySwap)) + if len(m.CapabilityAdd) > 0 { + for _, s := range m.CapabilityAdd { + l = len(s) + n += 2 + l + sovSpecs(uint64(l)) + } + } + if len(m.CapabilityDrop) > 0 { + for _, s := range m.CapabilityDrop { + l = len(s) + n += 2 + l + sovSpecs(uint64(l)) + } } - if m.MemorySwappiness != nil { - l = m.MemorySwappiness.Size() - n += 2 + l + sovSpecs(uint64(l)) + if len(m.Ulimits) > 0 { + for _, e := range m.Ulimits { + l = e.Size() + n += 2 + l + sovSpecs(uint64(l)) + } } return n } func (m *ContainerSpec_PullOptions) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.RegistryAuth) @@ -2879,6 +3957,9 @@ func (m *ContainerSpec_PullOptions) Size() (n int) { } func (m *ContainerSpec_DNSConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Nameservers) > 0 { @@ -2902,7 +3983,29 @@ func (m *ContainerSpec_DNSConfig) Size() (n int) { return n } +func (m *ContainerSpec_Ulimit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovSpecs(uint64(l)) + } + if m.Soft != 0 { + n += 1 + sovSpecs(uint64(m.Soft)) + } + if m.Hard != 0 { + n += 1 + sovSpecs(uint64(m.Hard)) + } + return n +} + func (m *EndpointSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Mode != 0 { @@ -2918,6 +4021,9 @@ func (m *EndpointSpec) Size() (n int) { } func (m *NetworkSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Annotations.Size() @@ -2949,6 +4055,9 @@ func (m *NetworkSpec) Size() (n int) { } func (m *NetworkSpec_Network) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Network) @@ -2956,6 +4065,9 @@ func (m *NetworkSpec_Network) Size() (n int) { return n } func (m *ClusterSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Annotations.Size() @@ -2978,6 +4090,9 @@ func (m *ClusterSpec) Size() (n int) { } func (m *SecretSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Annotations.Size() @@ -2998,6 +4113,9 @@ func (m *SecretSpec) Size() (n int) { } func (m *ConfigSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Annotations.Size() @@ -3014,14 +4132,7 @@ func (m *ConfigSpec) Size() (n int) { } func sovSpecs(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozSpecs(x uint64) (n int) { return sovSpecs(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -3031,7 +4142,7 @@ func (this *NodeSpec) String() string { return "nil" } s := strings.Join([]string{`&NodeSpec{`, - `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, + `Annotations:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `DesiredRole:` + fmt.Sprintf("%v", this.DesiredRole) + `,`, `Membership:` + fmt.Sprintf("%v", this.Membership) + `,`, `Availability:` + fmt.Sprintf("%v", this.Availability) + `,`, @@ -3043,13 +4154,18 @@ func (this *ServiceSpec) String() string { if this == nil { return "nil" } + repeatedStringForNetworks := "[]*NetworkAttachmentConfig{" + for _, f := range this.Networks { + repeatedStringForNetworks += strings.Replace(fmt.Sprintf("%v", f), "NetworkAttachmentConfig", "NetworkAttachmentConfig", 1) + "," + } + repeatedStringForNetworks += "}" s := strings.Join([]string{`&ServiceSpec{`, - `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, + `Annotations:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `Task:` + strings.Replace(strings.Replace(this.Task.String(), "TaskSpec", "TaskSpec", 1), `&`, ``, 1) + `,`, `Mode:` + fmt.Sprintf("%v", this.Mode) + `,`, `Update:` + strings.Replace(fmt.Sprintf("%v", this.Update), "UpdateConfig", "UpdateConfig", 1) + `,`, - `Networks:` + strings.Replace(fmt.Sprintf("%v", this.Networks), "NetworkAttachmentConfig", "NetworkAttachmentConfig", 1) + `,`, - `Endpoint:` + strings.Replace(fmt.Sprintf("%v", this.Endpoint), "EndpointSpec", "EndpointSpec", 1) + `,`, + `Networks:` + repeatedStringForNetworks + `,`, + `Endpoint:` + strings.Replace(this.Endpoint.String(), "EndpointSpec", "EndpointSpec", 1) + `,`, `Rollback:` + strings.Replace(fmt.Sprintf("%v", this.Rollback), "UpdateConfig", "UpdateConfig", 1) + `,`, `}`, }, "") @@ -3075,6 +4191,26 @@ func (this *ServiceSpec_Global) String() string { }, "") return s } +func (this *ServiceSpec_ReplicatedJob) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ServiceSpec_ReplicatedJob{`, + `ReplicatedJob:` + strings.Replace(fmt.Sprintf("%v", this.ReplicatedJob), "ReplicatedJob", "ReplicatedJob", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ServiceSpec_GlobalJob) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ServiceSpec_GlobalJob{`, + `GlobalJob:` + strings.Replace(fmt.Sprintf("%v", this.GlobalJob), "GlobalJob", "GlobalJob", 1) + `,`, + `}`, + }, "") + return s +} func (this *ReplicatedService) String() string { if this == nil { return "nil" @@ -3094,19 +4230,49 @@ func (this *GlobalService) String() string { }, "") return s } +func (this *ReplicatedJob) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ReplicatedJob{`, + `MaxConcurrent:` + fmt.Sprintf("%v", this.MaxConcurrent) + `,`, + `TotalCompletions:` + fmt.Sprintf("%v", this.TotalCompletions) + `,`, + `}`, + }, "") + return s +} +func (this *GlobalJob) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&GlobalJob{`, + `}`, + }, "") + return s +} func (this *TaskSpec) String() string { if this == nil { return "nil" } + repeatedStringForNetworks := "[]*NetworkAttachmentConfig{" + for _, f := range this.Networks { + repeatedStringForNetworks += strings.Replace(fmt.Sprintf("%v", f), "NetworkAttachmentConfig", "NetworkAttachmentConfig", 1) + "," + } + repeatedStringForNetworks += "}" + repeatedStringForResourceReferences := "[]ResourceReference{" + for _, f := range this.ResourceReferences { + repeatedStringForResourceReferences += strings.Replace(strings.Replace(f.String(), "ResourceReference", "ResourceReference", 1), `&`, ``, 1) + "," + } + repeatedStringForResourceReferences += "}" s := strings.Join([]string{`&TaskSpec{`, `Runtime:` + fmt.Sprintf("%v", this.Runtime) + `,`, `Resources:` + strings.Replace(fmt.Sprintf("%v", this.Resources), "ResourceRequirements", "ResourceRequirements", 1) + `,`, `Restart:` + strings.Replace(fmt.Sprintf("%v", this.Restart), "RestartPolicy", "RestartPolicy", 1) + `,`, `Placement:` + strings.Replace(fmt.Sprintf("%v", this.Placement), "Placement", "Placement", 1) + `,`, `LogDriver:` + strings.Replace(fmt.Sprintf("%v", this.LogDriver), "Driver", "Driver", 1) + `,`, - `Networks:` + strings.Replace(fmt.Sprintf("%v", this.Networks), "NetworkAttachmentConfig", "NetworkAttachmentConfig", 1) + `,`, + `Networks:` + repeatedStringForNetworks + `,`, `ForceUpdate:` + fmt.Sprintf("%v", this.ForceUpdate) + `,`, - `ResourceReferences:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ResourceReferences), "ResourceReference", "ResourceReference", 1), `&`, ``, 1) + `,`, + `ResourceReferences:` + repeatedStringForResourceReferences + `,`, `}`, }, "") return s @@ -3158,7 +4324,7 @@ func (this *GenericRuntimeSpec) String() string { } s := strings.Join([]string{`&GenericRuntimeSpec{`, `Kind:` + fmt.Sprintf("%v", this.Kind) + `,`, - `Payload:` + strings.Replace(fmt.Sprintf("%v", this.Payload), "Any", "google_protobuf3.Any", 1) + `,`, + `Payload:` + strings.Replace(fmt.Sprintf("%v", this.Payload), "Any", "types.Any", 1) + `,`, `}`, }, "") return s @@ -3177,11 +4343,31 @@ func (this *ContainerSpec) String() string { if this == nil { return "nil" } + repeatedStringForMounts := "[]Mount{" + for _, f := range this.Mounts { + repeatedStringForMounts += fmt.Sprintf("%v", f) + "," + } + repeatedStringForMounts += "}" + repeatedStringForSecrets := "[]*SecretReference{" + for _, f := range this.Secrets { + repeatedStringForSecrets += strings.Replace(fmt.Sprintf("%v", f), "SecretReference", "SecretReference", 1) + "," + } + repeatedStringForSecrets += "}" + repeatedStringForConfigs := "[]*ConfigReference{" + for _, f := range this.Configs { + repeatedStringForConfigs += strings.Replace(fmt.Sprintf("%v", f), "ConfigReference", "ConfigReference", 1) + "," + } + repeatedStringForConfigs += "}" + repeatedStringForUlimits := "[]*ContainerSpec_Ulimit{" + for _, f := range this.Ulimits { + repeatedStringForUlimits += strings.Replace(fmt.Sprintf("%v", f), "ContainerSpec_Ulimit", "ContainerSpec_Ulimit", 1) + "," + } + repeatedStringForUlimits += "}" keysForLabels := make([]string, 0, len(this.Labels)) for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -3191,7 +4377,7 @@ func (this *ContainerSpec) String() string { for k, _ := range this.Sysctls { keysForSysctls = append(keysForSysctls, k) } - sortkeys.Strings(keysForSysctls) + github_com_gogo_protobuf_sortkeys.Strings(keysForSysctls) mapStringForSysctls := "map[string]string{" for _, k := range keysForSysctls { mapStringForSysctls += fmt.Sprintf("%v: %v,", k, this.Sysctls[k]) @@ -3205,11 +4391,11 @@ func (this *ContainerSpec) String() string { `Env:` + fmt.Sprintf("%v", this.Env) + `,`, `Dir:` + fmt.Sprintf("%v", this.Dir) + `,`, `User:` + fmt.Sprintf("%v", this.User) + `,`, - `Mounts:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Mounts), "Mount", "Mount", 1), `&`, ``, 1) + `,`, - `StopGracePeriod:` + strings.Replace(fmt.Sprintf("%v", this.StopGracePeriod), "Duration", "google_protobuf1.Duration", 1) + `,`, + `Mounts:` + repeatedStringForMounts + `,`, + `StopGracePeriod:` + strings.Replace(fmt.Sprintf("%v", this.StopGracePeriod), "Duration", "types.Duration", 1) + `,`, `PullOptions:` + strings.Replace(fmt.Sprintf("%v", this.PullOptions), "ContainerSpec_PullOptions", "ContainerSpec_PullOptions", 1) + `,`, `Groups:` + fmt.Sprintf("%v", this.Groups) + `,`, - `Secrets:` + strings.Replace(fmt.Sprintf("%v", this.Secrets), "SecretReference", "SecretReference", 1) + `,`, + `Secrets:` + repeatedStringForSecrets + `,`, `TTY:` + fmt.Sprintf("%v", this.TTY) + `,`, `Hostname:` + fmt.Sprintf("%v", this.Hostname) + `,`, `DNSConfig:` + strings.Replace(fmt.Sprintf("%v", this.DNSConfig), "ContainerSpec_DNSConfig", "ContainerSpec_DNSConfig", 1) + `,`, @@ -3218,14 +4404,15 @@ func (this *ContainerSpec) String() string { `OpenStdin:` + fmt.Sprintf("%v", this.OpenStdin) + `,`, `ReadOnly:` + fmt.Sprintf("%v", this.ReadOnly) + `,`, `StopSignal:` + fmt.Sprintf("%v", this.StopSignal) + `,`, - `Configs:` + strings.Replace(fmt.Sprintf("%v", this.Configs), "ConfigReference", "ConfigReference", 1) + `,`, + `Configs:` + repeatedStringForConfigs + `,`, `Privileges:` + strings.Replace(fmt.Sprintf("%v", this.Privileges), "Privileges", "Privileges", 1) + `,`, - `Init:` + strings.Replace(fmt.Sprintf("%v", this.Init), "BoolValue", "google_protobuf4.BoolValue", 1) + `,`, + `Init:` + strings.Replace(fmt.Sprintf("%v", this.Init), "BoolValue", "types.BoolValue", 1) + `,`, `Isolation:` + fmt.Sprintf("%v", this.Isolation) + `,`, `PidsLimit:` + fmt.Sprintf("%v", this.PidsLimit) + `,`, `Sysctls:` + mapStringForSysctls + `,`, - `MemorySwap:` + fmt.Sprintf("%v", this.MemorySwap) + `,`, - `MemorySwappiness:` + strings.Replace(fmt.Sprintf("%v", this.MemorySwappiness), "Int64Value", "google_protobuf4.Int64Value", 1) + `,`, + `CapabilityAdd:` + fmt.Sprintf("%v", this.CapabilityAdd) + `,`, + `CapabilityDrop:` + fmt.Sprintf("%v", this.CapabilityDrop) + `,`, + `Ulimits:` + repeatedStringForUlimits + `,`, `}`, }, "") return s @@ -3252,13 +4439,30 @@ func (this *ContainerSpec_DNSConfig) String() string { }, "") return s } +func (this *ContainerSpec_Ulimit) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ContainerSpec_Ulimit{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Soft:` + fmt.Sprintf("%v", this.Soft) + `,`, + `Hard:` + fmt.Sprintf("%v", this.Hard) + `,`, + `}`, + }, "") + return s +} func (this *EndpointSpec) String() string { if this == nil { return "nil" } + repeatedStringForPorts := "[]*PortConfig{" + for _, f := range this.Ports { + repeatedStringForPorts += strings.Replace(fmt.Sprintf("%v", f), "PortConfig", "PortConfig", 1) + "," + } + repeatedStringForPorts += "}" s := strings.Join([]string{`&EndpointSpec{`, `Mode:` + fmt.Sprintf("%v", this.Mode) + `,`, - `Ports:` + strings.Replace(fmt.Sprintf("%v", this.Ports), "PortConfig", "PortConfig", 1) + `,`, + `Ports:` + repeatedStringForPorts + `,`, `}`, }, "") return s @@ -3268,7 +4472,7 @@ func (this *NetworkSpec) String() string { return "nil" } s := strings.Join([]string{`&NetworkSpec{`, - `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, + `Annotations:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `DriverConfig:` + strings.Replace(fmt.Sprintf("%v", this.DriverConfig), "Driver", "Driver", 1) + `,`, `Ipv6Enabled:` + fmt.Sprintf("%v", this.Ipv6Enabled) + `,`, `Internal:` + fmt.Sprintf("%v", this.Internal) + `,`, @@ -3295,14 +4499,14 @@ func (this *ClusterSpec) String() string { return "nil" } s := strings.Join([]string{`&ClusterSpec{`, - `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, - `AcceptancePolicy:` + strings.Replace(strings.Replace(this.AcceptancePolicy.String(), "AcceptancePolicy", "AcceptancePolicy", 1), `&`, ``, 1) + `,`, - `Orchestration:` + strings.Replace(strings.Replace(this.Orchestration.String(), "OrchestrationConfig", "OrchestrationConfig", 1), `&`, ``, 1) + `,`, - `Raft:` + strings.Replace(strings.Replace(this.Raft.String(), "RaftConfig", "RaftConfig", 1), `&`, ``, 1) + `,`, - `Dispatcher:` + strings.Replace(strings.Replace(this.Dispatcher.String(), "DispatcherConfig", "DispatcherConfig", 1), `&`, ``, 1) + `,`, - `CAConfig:` + strings.Replace(strings.Replace(this.CAConfig.String(), "CAConfig", "CAConfig", 1), `&`, ``, 1) + `,`, - `TaskDefaults:` + strings.Replace(strings.Replace(this.TaskDefaults.String(), "TaskDefaults", "TaskDefaults", 1), `&`, ``, 1) + `,`, - `EncryptionConfig:` + strings.Replace(strings.Replace(this.EncryptionConfig.String(), "EncryptionConfig", "EncryptionConfig", 1), `&`, ``, 1) + `,`, + `Annotations:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, + `AcceptancePolicy:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.AcceptancePolicy), "AcceptancePolicy", "AcceptancePolicy", 1), `&`, ``, 1) + `,`, + `Orchestration:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Orchestration), "OrchestrationConfig", "OrchestrationConfig", 1), `&`, ``, 1) + `,`, + `Raft:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Raft), "RaftConfig", "RaftConfig", 1), `&`, ``, 1) + `,`, + `Dispatcher:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Dispatcher), "DispatcherConfig", "DispatcherConfig", 1), `&`, ``, 1) + `,`, + `CAConfig:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.CAConfig), "CAConfig", "CAConfig", 1), `&`, ``, 1) + `,`, + `TaskDefaults:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.TaskDefaults), "TaskDefaults", "TaskDefaults", 1), `&`, ``, 1) + `,`, + `EncryptionConfig:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.EncryptionConfig), "EncryptionConfig", "EncryptionConfig", 1), `&`, ``, 1) + `,`, `}`, }, "") return s @@ -3312,7 +4516,7 @@ func (this *SecretSpec) String() string { return "nil" } s := strings.Join([]string{`&SecretSpec{`, - `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, + `Annotations:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `Data:` + fmt.Sprintf("%v", this.Data) + `,`, `Templating:` + strings.Replace(fmt.Sprintf("%v", this.Templating), "Driver", "Driver", 1) + `,`, `Driver:` + strings.Replace(fmt.Sprintf("%v", this.Driver), "Driver", "Driver", 1) + `,`, @@ -3325,7 +4529,7 @@ func (this *ConfigSpec) String() string { return "nil" } s := strings.Join([]string{`&ConfigSpec{`, - `Annotations:` + strings.Replace(strings.Replace(this.Annotations.String(), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, + `Annotations:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Annotations), "Annotations", "Annotations", 1), `&`, ``, 1) + `,`, `Data:` + fmt.Sprintf("%v", this.Data) + `,`, `Templating:` + strings.Replace(fmt.Sprintf("%v", this.Templating), "Driver", "Driver", 1) + `,`, `}`, @@ -3355,7 +4559,7 @@ func (m *NodeSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3383,7 +4587,7 @@ func (m *NodeSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3392,6 +4596,9 @@ func (m *NodeSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3413,7 +4620,7 @@ func (m *NodeSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.DesiredRole |= (NodeRole(b) & 0x7F) << shift + m.DesiredRole |= NodeRole(b&0x7F) << shift if b < 0x80 { break } @@ -3432,7 +4639,7 @@ func (m *NodeSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Membership |= (NodeSpec_Membership(b) & 0x7F) << shift + m.Membership |= NodeSpec_Membership(b&0x7F) << shift if b < 0x80 { break } @@ -3451,7 +4658,7 @@ func (m *NodeSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Availability |= (NodeSpec_Availability(b) & 0x7F) << shift + m.Availability |= NodeSpec_Availability(b&0x7F) << shift if b < 0x80 { break } @@ -3462,7 +4669,7 @@ func (m *NodeSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -3492,7 +4699,7 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3520,7 +4727,7 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3529,6 +4736,9 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3550,7 +4760,110 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSpecs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Task.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Replicated", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSpecs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ReplicatedService{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Mode = &ServiceSpec_Replicated{v} + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Global", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthSpecs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &GlobalService{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Mode = &ServiceSpec_Global{v} + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Update", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3559,16 +4872,22 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Task.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if m.Update == nil { + m.Update = &UpdateConfig{} + } + if err := m.Update.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 3: + case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Replicated", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Networks", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3580,7 +4899,7 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3589,18 +4908,20 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } - v := &ReplicatedService{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Networks = append(m.Networks, &NetworkAttachmentConfig{}) + if err := m.Networks[len(m.Networks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - m.Mode = &ServiceSpec_Replicated{v} iNdEx = postIndex - case 4: + case 8: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Global", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3612,7 +4933,7 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3621,18 +4942,22 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } - v := &GlobalService{} - if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if m.Endpoint == nil { + m.Endpoint = &EndpointSpec{} + } + if err := m.Endpoint.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } - m.Mode = &ServiceSpec_Global{v} iNdEx = postIndex - case 6: + case 9: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Update", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Rollback", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3644,7 +4969,7 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3653,19 +4978,22 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Update == nil { - m.Update = &UpdateConfig{} + if m.Rollback == nil { + m.Rollback = &UpdateConfig{} } - if err := m.Update.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Rollback.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex - case 7: + case 10: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Networks", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ReplicatedJob", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3677,7 +5005,7 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3686,17 +5014,21 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Networks = append(m.Networks, &NetworkAttachmentConfig{}) - if err := m.Networks[len(m.Networks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + v := &ReplicatedJob{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } + m.Mode = &ServiceSpec_ReplicatedJob{v} iNdEx = postIndex - case 8: + case 11: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field GlobalJob", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -3708,7 +5040,7 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3717,21 +5049,73 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Endpoint == nil { - m.Endpoint = &EndpointSpec{} - } - if err := m.Endpoint.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + v := &GlobalJob{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } + m.Mode = &ServiceSpec_GlobalJob{v} iNdEx = postIndex - case 9: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Rollback", wireType) + default: + iNdEx = preIndex + skippy, err := skipSpecs(dAtA[iNdEx:]) + if err != nil { + return err } - var msglen int + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSpecs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ReplicatedService) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ReplicatedService: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ReplicatedService: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Replicas", wireType) + } + m.Replicas = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSpecs @@ -3741,32 +5125,68 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + m.Replicas |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + default: + iNdEx = preIndex + skippy, err := skipSpecs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } - postIndex := iNdEx + msglen - if postIndex > l { + if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - if m.Rollback == nil { - m.Rollback = &UpdateConfig{} + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GlobalService) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs } - if err := m.Rollback.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + if iNdEx >= l { + return io.ErrUnexpectedEOF } - iNdEx = postIndex + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GlobalService: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GlobalService: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { default: iNdEx = preIndex skippy, err := skipSpecs(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -3781,7 +5201,7 @@ func (m *ServiceSpec) Unmarshal(dAtA []byte) error { } return nil } -func (m *ReplicatedService) Unmarshal(dAtA []byte) error { +func (m *ReplicatedJob) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3796,7 +5216,7 @@ func (m *ReplicatedService) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3804,17 +5224,36 @@ func (m *ReplicatedService) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: ReplicatedService: wiretype end group for non-group") + return fmt.Errorf("proto: ReplicatedJob: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: ReplicatedService: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: ReplicatedJob: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Replicas", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field MaxConcurrent", wireType) } - m.Replicas = 0 + m.MaxConcurrent = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxConcurrent |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TotalCompletions", wireType) + } + m.TotalCompletions = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSpecs @@ -3824,7 +5263,7 @@ func (m *ReplicatedService) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Replicas |= (uint64(b) & 0x7F) << shift + m.TotalCompletions |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3835,7 +5274,7 @@ func (m *ReplicatedService) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -3850,7 +5289,7 @@ func (m *ReplicatedService) Unmarshal(dAtA []byte) error { } return nil } -func (m *GlobalService) Unmarshal(dAtA []byte) error { +func (m *GlobalJob) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -3865,7 +5304,7 @@ func (m *GlobalService) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3873,10 +5312,10 @@ func (m *GlobalService) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: GlobalService: wiretype end group for non-group") + return fmt.Errorf("proto: GlobalJob: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: GlobalService: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: GlobalJob: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: @@ -3885,7 +5324,7 @@ func (m *GlobalService) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -3915,7 +5354,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3943,7 +5382,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3952,6 +5391,9 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3975,7 +5417,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3984,6 +5426,9 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4008,7 +5453,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4017,6 +5462,9 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4041,7 +5489,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4050,6 +5498,9 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4074,7 +5525,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4083,6 +5534,9 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4107,7 +5561,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4116,6 +5570,9 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4138,7 +5595,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4147,6 +5604,9 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4170,7 +5630,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ForceUpdate |= (uint64(b) & 0x7F) << shift + m.ForceUpdate |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4189,7 +5649,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4198,6 +5658,9 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4221,7 +5684,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4230,6 +5693,9 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4244,7 +5710,7 @@ func (m *TaskSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -4274,7 +5740,7 @@ func (m *ResourceReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4302,7 +5768,7 @@ func (m *ResourceReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4312,6 +5778,9 @@ func (m *ResourceReference) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4331,7 +5800,7 @@ func (m *ResourceReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ResourceType |= (ResourceType(b) & 0x7F) << shift + m.ResourceType |= ResourceType(b&0x7F) << shift if b < 0x80 { break } @@ -4342,7 +5811,7 @@ func (m *ResourceReference) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -4372,7 +5841,7 @@ func (m *GenericRuntimeSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4400,7 +5869,7 @@ func (m *GenericRuntimeSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4410,6 +5879,9 @@ func (m *GenericRuntimeSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4429,7 +5901,7 @@ func (m *GenericRuntimeSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4438,11 +5910,14 @@ func (m *GenericRuntimeSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Payload == nil { - m.Payload = &google_protobuf3.Any{} + m.Payload = &types.Any{} } if err := m.Payload.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -4454,7 +5929,7 @@ func (m *GenericRuntimeSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -4484,7 +5959,7 @@ func (m *NetworkAttachmentSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4512,7 +5987,7 @@ func (m *NetworkAttachmentSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4522,6 +5997,9 @@ func (m *NetworkAttachmentSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4533,7 +6011,7 @@ func (m *NetworkAttachmentSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -4563,7 +6041,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4591,7 +6069,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4601,6 +6079,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4620,7 +6101,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4629,6 +6110,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4649,7 +6133,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4666,7 +6150,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4676,6 +6160,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthSpecs + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -4692,7 +6179,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4702,6 +6189,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthSpecs + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -4713,7 +6203,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > postIndex { @@ -4738,7 +6228,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4748,6 +6238,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4767,7 +6260,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4777,6 +6270,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4796,7 +6292,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4806,6 +6302,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4825,7 +6324,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4835,6 +6334,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4854,7 +6356,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4864,6 +6366,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4883,7 +6388,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4892,6 +6397,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4914,7 +6422,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4923,11 +6431,14 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } if m.StopGracePeriod == nil { - m.StopGracePeriod = &google_protobuf1.Duration{} + m.StopGracePeriod = &types.Duration{} } if err := m.StopGracePeriod.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -4947,7 +6458,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4956,6 +6467,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4980,7 +6494,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4990,6 +6504,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5009,7 +6526,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5018,6 +6535,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5040,7 +6560,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5060,7 +6580,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5069,7 +6589,10 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { if intStringLen < 0 { return ErrInvalidLengthSpecs } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5089,7 +6612,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5098,6 +6621,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5122,7 +6648,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5131,6 +6657,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5155,7 +6684,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5165,6 +6694,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5184,7 +6716,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5204,7 +6736,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5224,7 +6756,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5234,6 +6766,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5253,7 +6788,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5262,6 +6797,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5284,7 +6822,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5293,6 +6831,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5317,7 +6858,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5326,11 +6867,14 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Init == nil { - m.Init = &google_protobuf4.BoolValue{} + m.Init = &types.BoolValue{} } if err := m.Init.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -5350,7 +6894,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Isolation |= (ContainerSpec_Isolation(b) & 0x7F) << shift + m.Isolation |= ContainerSpec_Isolation(b&0x7F) << shift if b < 0x80 { break } @@ -5369,7 +6913,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.PidsLimit |= (int64(b) & 0x7F) << shift + m.PidsLimit |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -5388,7 +6932,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5397,6 +6941,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5417,7 +6964,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5434,7 +6981,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5444,6 +6991,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthSpecs + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -5460,7 +7010,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5470,6 +7020,9 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthSpecs + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -5481,7 +7034,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > postIndex { @@ -5493,10 +7046,10 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { m.Sysctls[mapkey] = mapvalue iNdEx = postIndex case 27: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field MemorySwap", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CapabilityAdd", wireType) } - m.MemorySwap = 0 + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowSpecs @@ -5506,14 +7059,59 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.MemorySwap |= (int64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSpecs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CapabilityAdd = append(m.CapabilityAdd, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex case 28: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field MemorySwappiness", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field CapabilityDrop", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSpecs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CapabilityDrop = append(m.CapabilityDrop, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 29: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Ulimits", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -5525,7 +7123,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5534,13 +7132,14 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } - if m.MemorySwappiness == nil { - m.MemorySwappiness = &google_protobuf4.Int64Value{} - } - if err := m.MemorySwappiness.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Ulimits = append(m.Ulimits, &ContainerSpec_Ulimit{}) + if err := m.Ulimits[len(m.Ulimits)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -5550,7 +7149,7 @@ func (m *ContainerSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -5580,7 +7179,7 @@ func (m *ContainerSpec_PullOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5608,7 +7207,7 @@ func (m *ContainerSpec_PullOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5618,6 +7217,9 @@ func (m *ContainerSpec_PullOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5629,7 +7231,7 @@ func (m *ContainerSpec_PullOptions) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -5659,7 +7261,7 @@ func (m *ContainerSpec_DNSConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5687,7 +7289,7 @@ func (m *ContainerSpec_DNSConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5697,6 +7299,9 @@ func (m *ContainerSpec_DNSConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5716,7 +7321,7 @@ func (m *ContainerSpec_DNSConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5726,6 +7331,9 @@ func (m *ContainerSpec_DNSConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5745,7 +7353,7 @@ func (m *ContainerSpec_DNSConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5755,6 +7363,9 @@ func (m *ContainerSpec_DNSConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5766,7 +7377,127 @@ func (m *ContainerSpec_DNSConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSpecs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ContainerSpec_Ulimit) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Ulimit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Ulimit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSpecs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Soft", wireType) + } + m.Soft = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Soft |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Hard", wireType) + } + m.Hard = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSpecs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Hard |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipSpecs(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -5796,7 +7527,7 @@ func (m *EndpointSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5824,7 +7555,7 @@ func (m *EndpointSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Mode |= (EndpointSpec_ResolutionMode(b) & 0x7F) << shift + m.Mode |= EndpointSpec_ResolutionMode(b&0x7F) << shift if b < 0x80 { break } @@ -5843,7 +7574,7 @@ func (m *EndpointSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5852,6 +7583,9 @@ func (m *EndpointSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5866,7 +7600,7 @@ func (m *EndpointSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -5896,7 +7630,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -5924,7 +7658,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5933,6 +7667,9 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5954,7 +7691,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -5963,6 +7700,9 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -5987,7 +7727,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6007,7 +7747,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6027,7 +7767,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6036,6 +7776,9 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6060,7 +7803,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6080,7 +7823,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6100,7 +7843,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6110,6 +7853,9 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6121,7 +7867,7 @@ func (m *NetworkSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -6151,7 +7897,7 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6179,7 +7925,7 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6188,6 +7934,9 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6209,7 +7958,7 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6218,6 +7967,9 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6239,7 +7991,7 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6248,6 +8000,9 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6269,7 +8024,7 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6278,6 +8033,9 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6299,7 +8057,7 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6308,6 +8066,9 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6329,7 +8090,7 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6338,6 +8099,9 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6359,7 +8123,7 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6368,6 +8132,9 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6389,7 +8156,7 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6398,6 +8165,9 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6411,7 +8181,7 @@ func (m *ClusterSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -6441,7 +8211,7 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6469,7 +8239,7 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6478,6 +8248,9 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6499,7 +8272,7 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6508,6 +8281,9 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6530,7 +8306,7 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6539,6 +8315,9 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6563,7 +8342,7 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6572,6 +8351,9 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6588,7 +8370,7 @@ func (m *SecretSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -6618,7 +8400,7 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -6646,7 +8428,7 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6655,6 +8437,9 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6676,7 +8461,7 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6685,6 +8470,9 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6707,7 +8495,7 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -6716,6 +8504,9 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthSpecs } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthSpecs + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -6732,7 +8523,7 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthSpecs } if (iNdEx + skippy) > l { @@ -6750,6 +8541,7 @@ func (m *ConfigSpec) Unmarshal(dAtA []byte) error { func skipSpecs(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -6781,10 +8573,8 @@ func skipSpecs(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -6801,198 +8591,34 @@ func skipSpecs(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthSpecs } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowSpecs - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipSpecs(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSpecs + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthSpecs + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthSpecs = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowSpecs = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthSpecs = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSpecs = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSpecs = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("github.com/docker/swarmkit/api/specs.proto", fileDescriptorSpecs) } - -var fileDescriptorSpecs = []byte{ - // 2213 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0xcf, 0x6f, 0x1b, 0xb9, - 0xf5, 0xb7, 0x6c, 0x59, 0x3f, 0xde, 0xc8, 0x89, 0xcc, 0xcd, 0xee, 0x8e, 0x95, 0xac, 0xad, 0xd5, - 0x66, 0xf3, 0xf5, 0xee, 0xe2, 0x2b, 0xa3, 0x6e, 0x90, 0x66, 0x93, 0x6e, 0x5b, 0xc9, 0xd2, 0xda, - 0x6a, 0x12, 0x5b, 0xa0, 0x1c, 0xb7, 0x01, 0x0a, 0x08, 0xf4, 0x0c, 0x2d, 0x0d, 0x3c, 0x1a, 0x4e, - 0x49, 0xca, 0x81, 0x6e, 0x3d, 0x2e, 0xdc, 0xbf, 0xc1, 0xe8, 0xa1, 0xe8, 0xbd, 0xfd, 0x2f, 0x72, - 0x6c, 0x6f, 0xed, 0xc5, 0xe8, 0xfa, 0x5f, 0xe8, 0xad, 0x97, 0x16, 0xe4, 0x70, 0xa4, 0x91, 0x2d, - 0xc7, 0x29, 0x9a, 0x43, 0x6f, 0xe4, 0x9b, 0xcf, 0xe7, 0x91, 0x7c, 0xfc, 0xf0, 0xf1, 0x71, 0xe0, - 0xcb, 0x9e, 0x27, 0xfb, 0xc3, 0xc3, 0xaa, 0xc3, 0x06, 0x1b, 0x2e, 0x73, 0x8e, 0x29, 0xdf, 0x10, - 0xaf, 0x09, 0x1f, 0x1c, 0x7b, 0x72, 0x83, 0x84, 0xde, 0x86, 0x08, 0xa9, 0x23, 0xaa, 0x21, 0x67, - 0x92, 0x21, 0x14, 0x01, 0xaa, 0x31, 0xa0, 0x7a, 0xf2, 0x83, 0xd2, 0x4d, 0x7c, 0x39, 0x0a, 0xa9, - 0xe1, 0x97, 0xee, 0xf4, 0x58, 0x8f, 0xe9, 0xe6, 0x86, 0x6a, 0x19, 0xeb, 0x6a, 0x8f, 0xb1, 0x9e, - 0x4f, 0x37, 0x74, 0xef, 0x70, 0x78, 0xb4, 0xe1, 0x0e, 0x39, 0x91, 0x1e, 0x0b, 0xcc, 0xf7, 0x95, - 0xcb, 0xdf, 0x49, 0x30, 0xba, 0x8e, 0xfa, 0x9a, 0x93, 0x30, 0xa4, 0xdc, 0x0c, 0x58, 0x39, 0x4b, - 0x43, 0x6e, 0x97, 0xb9, 0xb4, 0x13, 0x52, 0x07, 0x6d, 0x83, 0x45, 0x82, 0x80, 0x49, 0xed, 0x5b, - 0xd8, 0xa9, 0x72, 0x6a, 0xdd, 0xda, 0x5c, 0xab, 0x5e, 0x5d, 0x53, 0xb5, 0x36, 0x81, 0xd5, 0xd3, - 0x6f, 0xce, 0xd7, 0xe6, 0x70, 0x92, 0x89, 0x7e, 0x0a, 0x05, 0x97, 0x0a, 0x8f, 0x53, 0xb7, 0xcb, - 0x99, 0x4f, 0xed, 0xf9, 0x72, 0x6a, 0xfd, 0xd6, 0xe6, 0xbd, 0x59, 0x9e, 0xd4, 0xe0, 0x98, 0xf9, - 0x14, 0x5b, 0x86, 0xa1, 0x3a, 0x68, 0x1b, 0x60, 0x40, 0x07, 0x87, 0x94, 0x8b, 0xbe, 0x17, 0xda, - 0x0b, 0x9a, 0xfe, 0x7f, 0xd7, 0xd1, 0xd5, 0xdc, 0xab, 0x2f, 0xc6, 0x70, 0x9c, 0xa0, 0xa2, 0x17, - 0x50, 0x20, 0x27, 0xc4, 0xf3, 0xc9, 0xa1, 0xe7, 0x7b, 0x72, 0x64, 0xa7, 0xb5, 0xab, 0x2f, 0xde, - 0xea, 0xaa, 0x96, 0x20, 0xe0, 0x29, 0x7a, 0xc5, 0x05, 0x98, 0x0c, 0x84, 0x1e, 0x40, 0xb6, 0xdd, - 0xdc, 0x6d, 0xb4, 0x76, 0xb7, 0x8b, 0x73, 0xa5, 0x95, 0xd3, 0xb3, 0xf2, 0x87, 0xca, 0xc7, 0x04, - 0xd0, 0xa6, 0x81, 0xeb, 0x05, 0x3d, 0xb4, 0x0e, 0xb9, 0xda, 0xd6, 0x56, 0xb3, 0xbd, 0xdf, 0x6c, - 0x14, 0x53, 0xa5, 0xd2, 0xe9, 0x59, 0xf9, 0xa3, 0x69, 0x60, 0xcd, 0x71, 0x68, 0x28, 0xa9, 0x5b, - 0x4a, 0x7f, 0xf7, 0xfb, 0xd5, 0xb9, 0xca, 0x77, 0x29, 0x28, 0x24, 0x27, 0x81, 0x1e, 0x40, 0xa6, - 0xb6, 0xb5, 0xdf, 0x3a, 0x68, 0x16, 0xe7, 0x26, 0xf4, 0x24, 0xa2, 0xe6, 0x48, 0xef, 0x84, 0xa2, - 0xfb, 0xb0, 0xd8, 0xae, 0xbd, 0xec, 0x34, 0x8b, 0xa9, 0xc9, 0x74, 0x92, 0xb0, 0x36, 0x19, 0x0a, - 0x8d, 0x6a, 0xe0, 0x5a, 0x6b, 0xb7, 0x38, 0x3f, 0x1b, 0xd5, 0xe0, 0xc4, 0x0b, 0xcc, 0x54, 0x7e, - 0x97, 0x06, 0xab, 0x43, 0xf9, 0x89, 0xe7, 0xbc, 0x67, 0x89, 0x3c, 0x82, 0xb4, 0x24, 0xe2, 0x58, - 0x4b, 0xc3, 0x9a, 0x2d, 0x8d, 0x7d, 0x22, 0x8e, 0xd5, 0xa0, 0x86, 0xae, 0xf1, 0x4a, 0x19, 0x9c, - 0x86, 0xbe, 0xe7, 0x10, 0x49, 0x5d, 0xad, 0x0c, 0x6b, 0xf3, 0xf3, 0x59, 0x6c, 0x3c, 0x46, 0x99, - 0xf9, 0xef, 0xcc, 0xe1, 0x04, 0x15, 0x3d, 0x85, 0x4c, 0xcf, 0x67, 0x87, 0xc4, 0xd7, 0x9a, 0xb0, - 0x36, 0x3f, 0x9d, 0xe5, 0x64, 0x5b, 0x23, 0x26, 0x0e, 0x0c, 0x05, 0x3d, 0x86, 0xcc, 0x30, 0x74, - 0x89, 0xa4, 0x76, 0x46, 0x93, 0xcb, 0xb3, 0xc8, 0x2f, 0x35, 0x62, 0x8b, 0x05, 0x47, 0x5e, 0x0f, - 0x1b, 0x3c, 0x7a, 0x06, 0xb9, 0x80, 0xca, 0xd7, 0x8c, 0x1f, 0x0b, 0x3b, 0x5b, 0x5e, 0x58, 0xb7, - 0x36, 0xbf, 0x9a, 0x29, 0xc6, 0x08, 0x53, 0x93, 0x92, 0x38, 0xfd, 0x01, 0x0d, 0x64, 0xe4, 0xa6, - 0x3e, 0x6f, 0xa7, 0xf0, 0xd8, 0x01, 0xfa, 0x31, 0xe4, 0x68, 0xe0, 0x86, 0xcc, 0x0b, 0xa4, 0x9d, - 0xbb, 0x7e, 0x22, 0x4d, 0x83, 0x51, 0xc1, 0xc4, 0x63, 0x86, 0x62, 0x73, 0xe6, 0xfb, 0x87, 0xc4, - 0x39, 0xb6, 0xf3, 0xef, 0xb8, 0x8c, 0x31, 0xa3, 0x9e, 0x81, 0xf4, 0x80, 0xb9, 0xb4, 0xb2, 0x01, - 0xcb, 0x57, 0x42, 0x8d, 0x4a, 0x90, 0x33, 0xa1, 0x8e, 0x34, 0x92, 0xc6, 0xe3, 0x7e, 0xe5, 0x36, - 0x2c, 0x4d, 0x85, 0xb5, 0xf2, 0xc7, 0x45, 0xc8, 0xc5, 0x7b, 0x8d, 0x6a, 0x90, 0x77, 0x58, 0x20, - 0x89, 0x17, 0x50, 0x6e, 0xe4, 0x35, 0x73, 0x67, 0xb6, 0x62, 0x90, 0x62, 0xed, 0xcc, 0xe1, 0x09, - 0x0b, 0x7d, 0x0b, 0x79, 0x4e, 0x05, 0x1b, 0x72, 0x87, 0x0a, 0xa3, 0xaf, 0xf5, 0xd9, 0x0a, 0x89, - 0x40, 0x98, 0xfe, 0x7a, 0xe8, 0x71, 0xaa, 0xa2, 0x2c, 0xf0, 0x84, 0x8a, 0x9e, 0x42, 0x96, 0x53, - 0x21, 0x09, 0x97, 0x6f, 0x93, 0x08, 0x8e, 0x20, 0x6d, 0xe6, 0x7b, 0xce, 0x08, 0xc7, 0x0c, 0xf4, - 0x14, 0xf2, 0xa1, 0x4f, 0x1c, 0xed, 0xd5, 0x5e, 0xd4, 0xf4, 0x4f, 0x66, 0xd1, 0xdb, 0x31, 0x08, - 0x4f, 0xf0, 0xe8, 0x6b, 0x00, 0x9f, 0xf5, 0xba, 0x2e, 0xf7, 0x4e, 0x28, 0x37, 0x12, 0x2b, 0xcd, - 0x62, 0x37, 0x34, 0x02, 0xe7, 0x7d, 0xd6, 0x8b, 0x9a, 0x68, 0xfb, 0xbf, 0xd2, 0x57, 0x42, 0x5b, - 0xcf, 0x00, 0xc8, 0xf8, 0xab, 0x51, 0xd7, 0x17, 0xef, 0xe4, 0xca, 0xec, 0x48, 0x82, 0x8e, 0x3e, - 0x85, 0xc2, 0x11, 0xe3, 0x0e, 0xed, 0x9a, 0x53, 0x93, 0xd7, 0x9a, 0xb0, 0xb4, 0x2d, 0xd2, 0x17, - 0xaa, 0x43, 0xb6, 0x47, 0x03, 0xca, 0x3d, 0xc7, 0x06, 0x3d, 0xd8, 0x83, 0x99, 0x07, 0x32, 0x82, - 0xe0, 0x61, 0x20, 0xbd, 0x01, 0x35, 0x23, 0xc5, 0x44, 0xf4, 0x2b, 0xf8, 0x20, 0xde, 0xbe, 0x2e, - 0xa7, 0x47, 0x94, 0xd3, 0x40, 0x69, 0xc0, 0xd2, 0x71, 0xf8, 0xfc, 0xed, 0x1a, 0x30, 0x68, 0x93, - 0x6c, 0x10, 0xbf, 0xfc, 0x41, 0xd4, 0xf3, 0x90, 0xe5, 0xd1, 0xb8, 0x95, 0xdf, 0xa6, 0x94, 0xea, - 0x2f, 0x21, 0xd0, 0x06, 0x58, 0xe3, 0xe1, 0x3d, 0x57, 0xab, 0x37, 0x5f, 0xbf, 0x75, 0x71, 0xbe, - 0x06, 0x31, 0xb6, 0xd5, 0x50, 0x39, 0xc8, 0xb4, 0x5d, 0xd4, 0x84, 0xa5, 0x31, 0x41, 0x95, 0x01, - 0xe6, 0xa2, 0x2c, 0xbf, 0x6d, 0xa6, 0xfb, 0xa3, 0x90, 0xe2, 0x02, 0x4f, 0xf4, 0x2a, 0xbf, 0x04, - 0x74, 0x35, 0x2e, 0x08, 0x41, 0xfa, 0xd8, 0x0b, 0xcc, 0x34, 0xb0, 0x6e, 0xa3, 0x2a, 0x64, 0x43, - 0x32, 0xf2, 0x19, 0x71, 0xcd, 0xc1, 0xb8, 0x53, 0x8d, 0x0a, 0x84, 0x6a, 0x5c, 0x20, 0x54, 0x6b, - 0xc1, 0x08, 0xc7, 0xa0, 0xca, 0x33, 0xf8, 0x70, 0xe6, 0xf6, 0xa2, 0x4d, 0x28, 0x8c, 0x0f, 0xdc, - 0x64, 0xad, 0xb7, 0x2f, 0xce, 0xd7, 0xac, 0xf1, 0xc9, 0x6c, 0x35, 0xb0, 0x35, 0x06, 0xb5, 0xdc, - 0xca, 0x5f, 0x96, 0x60, 0x69, 0xea, 0xd8, 0xa2, 0x3b, 0xb0, 0xe8, 0x0d, 0x48, 0x8f, 0x9a, 0x39, - 0x46, 0x1d, 0xd4, 0x84, 0x8c, 0x4f, 0x0e, 0xa9, 0xaf, 0x0e, 0xaf, 0xda, 0xb8, 0xff, 0xbf, 0xf1, - 0xfc, 0x57, 0x9f, 0x6b, 0x7c, 0x33, 0x90, 0x7c, 0x84, 0x0d, 0x19, 0xd9, 0x90, 0x75, 0xd8, 0x60, - 0x40, 0x02, 0x75, 0x4d, 0x2c, 0xac, 0xe7, 0x71, 0xdc, 0x55, 0x91, 0x21, 0xbc, 0x27, 0xec, 0xb4, - 0x36, 0xeb, 0x36, 0x2a, 0xc2, 0x02, 0x0d, 0x4e, 0xec, 0x45, 0x6d, 0x52, 0x4d, 0x65, 0x71, 0xbd, - 0xe8, 0xf4, 0xe5, 0xb1, 0x6a, 0x2a, 0xde, 0x50, 0x50, 0x6e, 0x67, 0xa3, 0x88, 0xaa, 0x36, 0xfa, - 0x11, 0x64, 0x06, 0x6c, 0x18, 0x48, 0x61, 0xe7, 0xf4, 0x64, 0x57, 0x66, 0x4d, 0xf6, 0x85, 0x42, - 0x18, 0x65, 0x19, 0x38, 0x6a, 0xc2, 0xb2, 0x90, 0x2c, 0xec, 0xf6, 0x38, 0x71, 0x68, 0x37, 0xa4, - 0xdc, 0x63, 0xae, 0x49, 0xc3, 0x2b, 0x57, 0x36, 0xa5, 0x61, 0x0a, 0x3e, 0x7c, 0x5b, 0x71, 0xb6, - 0x15, 0xa5, 0xad, 0x19, 0xa8, 0x0d, 0x85, 0x70, 0xe8, 0xfb, 0x5d, 0x16, 0x46, 0x37, 0x72, 0x74, - 0x76, 0xde, 0x21, 0x64, 0xed, 0xa1, 0xef, 0xef, 0x45, 0x24, 0x6c, 0x85, 0x93, 0x0e, 0xfa, 0x08, - 0x32, 0x3d, 0xce, 0x86, 0x61, 0x74, 0x6e, 0xf2, 0xd8, 0xf4, 0xd0, 0x37, 0x90, 0x15, 0xd4, 0xe1, - 0x54, 0x0a, 0xbb, 0xa0, 0x97, 0xfa, 0xd9, 0xac, 0x41, 0x3a, 0x1a, 0x32, 0x3e, 0x13, 0x38, 0xe6, - 0xa0, 0x15, 0x58, 0x90, 0x72, 0x64, 0x2f, 0x95, 0x53, 0xeb, 0xb9, 0x7a, 0xf6, 0xe2, 0x7c, 0x6d, - 0x61, 0x7f, 0xff, 0x15, 0x56, 0x36, 0x75, 0x5b, 0xf4, 0x99, 0x90, 0x01, 0x19, 0x50, 0xfb, 0x96, - 0x8e, 0xed, 0xb8, 0x8f, 0x5e, 0x01, 0xb8, 0x81, 0xe8, 0x3a, 0x3a, 0x3d, 0xd9, 0xb7, 0xf5, 0xea, - 0xbe, 0xba, 0x79, 0x75, 0x8d, 0xdd, 0x8e, 0xb9, 0x31, 0x97, 0x2e, 0xce, 0xd7, 0xf2, 0xe3, 0x2e, - 0xce, 0xbb, 0x81, 0x88, 0x9a, 0xa8, 0x0e, 0x56, 0x9f, 0x12, 0x5f, 0xf6, 0x9d, 0x3e, 0x75, 0x8e, - 0xed, 0xe2, 0xf5, 0x57, 0xe0, 0x8e, 0x86, 0x19, 0x0f, 0x49, 0x92, 0x52, 0xb0, 0x9a, 0xaa, 0xb0, - 0x97, 0x75, 0xac, 0xa2, 0x0e, 0xfa, 0x04, 0x80, 0x85, 0x34, 0xe8, 0x0a, 0xe9, 0x7a, 0x81, 0x8d, - 0xd4, 0x92, 0x71, 0x5e, 0x59, 0x3a, 0xca, 0x80, 0xee, 0xaa, 0x0b, 0x8a, 0xb8, 0x5d, 0x16, 0xf8, - 0x23, 0xfb, 0x03, 0xfd, 0x35, 0xa7, 0x0c, 0x7b, 0x81, 0x3f, 0x42, 0x6b, 0x60, 0x69, 0x5d, 0x08, - 0xaf, 0x17, 0x10, 0xdf, 0xbe, 0xa3, 0xe3, 0x01, 0xca, 0xd4, 0xd1, 0x16, 0xb5, 0x0f, 0x51, 0x34, - 0x84, 0xfd, 0xe1, 0xf5, 0xfb, 0x60, 0x26, 0x3b, 0xd9, 0x07, 0xc3, 0x41, 0x3f, 0x01, 0x08, 0xb9, - 0x77, 0xe2, 0xf9, 0xb4, 0x47, 0x85, 0xfd, 0x91, 0x5e, 0xf4, 0xea, 0xcc, 0x9b, 0x69, 0x8c, 0xc2, - 0x09, 0x06, 0xaa, 0x42, 0xda, 0x0b, 0x3c, 0x69, 0x7f, 0x6c, 0x6e, 0xa5, 0xcb, 0x52, 0xad, 0x33, - 0xe6, 0x1f, 0x10, 0x7f, 0x48, 0xb1, 0xc6, 0xa1, 0x16, 0xe4, 0x3d, 0xc1, 0x7c, 0x2d, 0x5f, 0xdb, - 0xd6, 0xf9, 0xed, 0x1d, 0xf6, 0xaf, 0x15, 0x53, 0xf0, 0x84, 0x8d, 0xee, 0x41, 0x3e, 0xf4, 0x5c, - 0xf1, 0xdc, 0x1b, 0x78, 0xd2, 0x5e, 0x29, 0xa7, 0xd6, 0x17, 0xf0, 0xc4, 0x80, 0x76, 0x20, 0x2b, - 0x46, 0xc2, 0x91, 0xbe, 0xb0, 0x4b, 0x3a, 0x2e, 0xd5, 0x9b, 0x87, 0xe9, 0x44, 0x84, 0x28, 0x71, - 0xc4, 0x74, 0xb5, 0x05, 0x03, 0x3a, 0x60, 0x7c, 0xd4, 0x15, 0xaf, 0x49, 0x68, 0xdf, 0xd5, 0x23, - 0x41, 0x64, 0xea, 0xbc, 0x26, 0x21, 0xda, 0x81, 0xe5, 0x04, 0x20, 0xf4, 0x02, 0x2a, 0x84, 0x7d, - 0x4f, 0x07, 0xe4, 0xee, 0x95, 0x80, 0xb4, 0x02, 0xf9, 0xe8, 0x61, 0x14, 0x91, 0xe2, 0xc4, 0x47, - 0x44, 0x2a, 0x7d, 0x0d, 0x56, 0x22, 0x77, 0xa9, 0x9c, 0x73, 0x4c, 0x47, 0x26, 0x1d, 0xaa, 0xa6, - 0x12, 0xd8, 0x89, 0xe2, 0xea, 0x7c, 0x9d, 0xc7, 0x51, 0xe7, 0xc9, 0xfc, 0xe3, 0x54, 0x69, 0x13, - 0xac, 0xc4, 0x19, 0x46, 0x9f, 0xa9, 0xbb, 0xa4, 0xe7, 0x09, 0xc9, 0x47, 0x5d, 0x32, 0x94, 0x7d, - 0xfb, 0x67, 0x9a, 0x50, 0x88, 0x8d, 0xb5, 0xa1, 0xec, 0x97, 0xba, 0x30, 0x39, 0x0a, 0xa8, 0x0c, - 0x96, 0x3a, 0x62, 0x82, 0xf2, 0x13, 0xca, 0x55, 0x9d, 0xa6, 0x14, 0x9c, 0x34, 0xa9, 0x54, 0x20, - 0x28, 0xe1, 0x4e, 0x5f, 0x67, 0xe2, 0x3c, 0x36, 0x3d, 0x95, 0x5a, 0xe3, 0x7c, 0x63, 0x52, 0xab, - 0xe9, 0x96, 0x9e, 0x40, 0x21, 0x19, 0xd3, 0xff, 0x64, 0x41, 0x95, 0x3f, 0xa5, 0x20, 0x3f, 0xde, - 0x77, 0xf4, 0x10, 0x96, 0x5b, 0x9d, 0xbd, 0xe7, 0xb5, 0xfd, 0xd6, 0xde, 0x6e, 0xb7, 0xd1, 0xfc, - 0xb6, 0xf6, 0xf2, 0xf9, 0x7e, 0x71, 0xae, 0xf4, 0xc9, 0xe9, 0x59, 0x79, 0x65, 0x72, 0xc5, 0xc4, - 0xf0, 0x06, 0x3d, 0x22, 0x43, 0x5f, 0x4e, 0xb3, 0xda, 0x78, 0x6f, 0xab, 0xd9, 0xe9, 0x14, 0x53, - 0xd7, 0xb1, 0xda, 0x9c, 0x39, 0x54, 0x08, 0xb4, 0x09, 0xc5, 0x09, 0x6b, 0xe7, 0x55, 0xbb, 0x89, - 0x0f, 0x8a, 0xf3, 0xa5, 0x7b, 0xa7, 0x67, 0x65, 0xfb, 0x2a, 0x69, 0x67, 0x14, 0x52, 0x7e, 0x60, - 0xde, 0x47, 0xff, 0x48, 0x41, 0x21, 0x59, 0x5e, 0xa3, 0xad, 0xa8, 0x2c, 0xd6, 0x2b, 0xbe, 0xb5, - 0xb9, 0x71, 0x53, 0x39, 0xae, 0xaf, 0x75, 0x7f, 0xa8, 0xfc, 0xbe, 0x50, 0x2f, 0x61, 0x4d, 0x46, - 0x0f, 0x61, 0x31, 0x64, 0x5c, 0xc6, 0x17, 0xe0, 0xec, 0xe3, 0xc9, 0x78, 0x5c, 0xb4, 0x45, 0xe0, - 0x4a, 0x1f, 0x6e, 0x4d, 0x7b, 0x43, 0xf7, 0x61, 0xe1, 0xa0, 0xd5, 0x2e, 0xce, 0x95, 0xee, 0x9e, - 0x9e, 0x95, 0x3f, 0x9e, 0xfe, 0x78, 0xe0, 0x71, 0x39, 0x24, 0x7e, 0xab, 0x8d, 0xbe, 0x84, 0xc5, - 0xc6, 0x6e, 0x07, 0xe3, 0x62, 0xaa, 0xb4, 0x76, 0x7a, 0x56, 0xbe, 0x3b, 0x8d, 0x53, 0x9f, 0xd8, - 0x30, 0x70, 0x31, 0x3b, 0x1c, 0xbf, 0x0a, 0xff, 0x39, 0x0f, 0x96, 0xa9, 0x0b, 0xde, 0xf7, 0x8f, - 0x83, 0xa5, 0xa8, 0xe8, 0x8d, 0x13, 0xfe, 0xfc, 0x8d, 0xb5, 0x6f, 0x21, 0x22, 0x18, 0x4d, 0x7f, - 0x0a, 0x05, 0x2f, 0x3c, 0x79, 0xd4, 0xa5, 0x01, 0x39, 0xf4, 0xcd, 0x03, 0x31, 0x87, 0x2d, 0x65, - 0x6b, 0x46, 0x26, 0x75, 0xdb, 0x78, 0x81, 0xa4, 0x3c, 0x30, 0x4f, 0xbf, 0x1c, 0x1e, 0xf7, 0xd1, - 0x37, 0x90, 0xf6, 0x42, 0x32, 0x30, 0x05, 0xfb, 0xcc, 0x15, 0xb4, 0xda, 0xb5, 0x17, 0xe6, 0xcc, - 0xd5, 0x73, 0x17, 0xe7, 0x6b, 0x69, 0x65, 0xc0, 0x9a, 0x86, 0x56, 0xe3, 0x9a, 0x59, 0x8d, 0xa4, - 0x2b, 0x87, 0x1c, 0x4e, 0x58, 0xd4, 0xb9, 0xf1, 0x82, 0x1e, 0x57, 0xd9, 0x22, 0xab, 0x3f, 0xc6, - 0x5d, 0x54, 0x82, 0xac, 0xa9, 0xbc, 0x75, 0xa9, 0x9d, 0x57, 0x55, 0xad, 0x31, 0xd4, 0x97, 0xc0, - 0x8a, 0xa2, 0xd1, 0x3d, 0xe2, 0x6c, 0x50, 0xf9, 0x57, 0x1a, 0xac, 0x2d, 0x7f, 0x28, 0xa4, 0x29, - 0xa2, 0xde, 0x5b, 0xf0, 0x5f, 0xc1, 0x32, 0xd1, 0x3f, 0x22, 0x48, 0xa0, 0x2a, 0x12, 0xfd, 0xa0, - 0x31, 0x1b, 0x70, 0x7f, 0xa6, 0xbb, 0x31, 0x38, 0x7a, 0xfc, 0xd4, 0x33, 0xca, 0xa7, 0x9d, 0xc2, - 0x45, 0x72, 0xe9, 0x0b, 0xea, 0xc0, 0x12, 0xe3, 0x4e, 0x9f, 0x0a, 0x19, 0xd5, 0x31, 0xe6, 0xe1, - 0x3e, 0xf3, 0x97, 0xce, 0x5e, 0x12, 0x68, 0x2e, 0xf1, 0x68, 0xb6, 0xd3, 0x3e, 0xd0, 0x63, 0x48, - 0x73, 0x72, 0x14, 0x3f, 0xce, 0x66, 0x1e, 0x12, 0x4c, 0x8e, 0xe4, 0x94, 0x0b, 0xcd, 0x40, 0x3f, - 0x07, 0x70, 0x3d, 0x11, 0x12, 0xe9, 0xf4, 0x29, 0x37, 0x9b, 0x3d, 0x73, 0x89, 0x8d, 0x31, 0x6a, - 0xca, 0x4b, 0x82, 0x8d, 0x9e, 0x41, 0xde, 0x21, 0xb1, 0x5c, 0x33, 0xd7, 0xff, 0xcd, 0xd8, 0xaa, - 0x19, 0x17, 0x45, 0xe5, 0xe2, 0xe2, 0x7c, 0x2d, 0x17, 0x5b, 0x70, 0xce, 0x21, 0x46, 0xbe, 0xcf, - 0x60, 0x49, 0x12, 0x71, 0xdc, 0x75, 0xa3, 0x74, 0x16, 0xc9, 0xe4, 0x9a, 0xa2, 0x44, 0x3d, 0x99, - 0x4d, 0xda, 0x8b, 0xb7, 0xb3, 0x20, 0x13, 0x36, 0xf4, 0x0b, 0x58, 0xa6, 0x81, 0xc3, 0x47, 0x5a, - 0xac, 0xf1, 0x0c, 0x73, 0xd7, 0x2f, 0xb6, 0x39, 0x06, 0x4f, 0x2d, 0xb6, 0x48, 0x2f, 0xd9, 0x2b, - 0x7f, 0x4b, 0x01, 0x44, 0x75, 0xde, 0xfb, 0x15, 0x20, 0x82, 0xb4, 0x4b, 0x24, 0xd1, 0x9a, 0x2b, - 0x60, 0xdd, 0x46, 0x4f, 0x00, 0x24, 0x1d, 0x84, 0x2a, 0xf5, 0x06, 0x3d, 0x23, 0x9b, 0xb7, 0xa5, - 0x83, 0x04, 0x1a, 0x6d, 0x42, 0xc6, 0x3c, 0xa1, 0xd3, 0x37, 0xf2, 0x0c, 0xb2, 0xf2, 0x87, 0x14, - 0x40, 0xb4, 0xcc, 0xff, 0xe9, 0xb5, 0xd5, 0xed, 0x37, 0xdf, 0xaf, 0xce, 0xfd, 0xf5, 0xfb, 0xd5, - 0xb9, 0xdf, 0x5c, 0xac, 0xa6, 0xde, 0x5c, 0xac, 0xa6, 0xfe, 0x7c, 0xb1, 0x9a, 0xfa, 0xfb, 0xc5, - 0x6a, 0xea, 0x30, 0xa3, 0x2b, 0x8f, 0x1f, 0xfe, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x67, 0x32, 0x8c, - 0x8f, 0xb8, 0x16, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/specs.proto b/vendor/github.com/docker/swarmkit/api/specs.proto index 069419c8d4c9e..80e0e523784ad 100644 --- a/vendor/github.com/docker/swarmkit/api/specs.proto +++ b/vendor/github.com/docker/swarmkit/api/specs.proto @@ -69,6 +69,8 @@ message ServiceSpec { oneof mode { ReplicatedService replicated = 3; GlobalService global = 4; + ReplicatedJob replicated_job = 10; + GlobalJob global_job = 11; } // Update contains settings which affect updates. @@ -99,6 +101,26 @@ message GlobalService { // Empty message for now. } +// ReplicatedJob is a certain type of one-off job which executes many Tasks in +// parallel until the specified number of Tasks have succeeded. +message ReplicatedJob { + // MaxConcurrent indicates the maximum number of Tasks that should be + // executing simultaneously at any given time. + uint64 max_concurrent = 1; + + // TotalCompletions sets the total number of Tasks desired to run to + // completion. This is also the absolute maximum number of Tasks that will + // be executed in parallel. That is, if this number is smaller than + // MaxConcurrent, only this many replicas will run. + uint64 total_completions = 2; +} + +// GlobalJob is a type of one-off job which executes one Task on every node +// matching the service's placement constraints. +message GlobalJob { + // Empty message for now. +} + message TaskSpec { oneof runtime { NetworkAttachmentSpec attachment = 8; @@ -334,13 +356,20 @@ message ContainerSpec { // https://docs.docker.com/engine/reference/commandline/run/#configure-namespaced-kernel-parameters-sysctls-at-runtime map sysctls = 26; - // Swap limit equal to memory plus swap: '-1' to enable unlimited swap - int64 memory_swap = 27; + // CapabilityAdd sets the list of capabilities to add to the default capability list + repeated string capability_add = 27; + // CapabilityDrop sets the list of capabilities to drop from the default capability list + repeated string capability_drop = 28; + + message Ulimit { + string name = 1; + int64 soft = 2; + int64 hard = 3; + } - // Tune container memory swappiness (0 to 100) - if not specified, defaults - // to the container OS's default - generally 60, or the value predefined in - // the image; set to -1 to unset a previously set value - google.protobuf.Int64Value memory_swappiness = 28; + // Ulimits defines the list of ulimits to set in the container. This option + // is equivalent to passing --ulimit to docker run. + repeated Ulimit ulimits = 29; } // EndpointSpec defines the properties that can be configured to diff --git a/vendor/github.com/docker/swarmkit/api/types.pb.go b/vendor/github.com/docker/swarmkit/api/types.pb.go index f5843467cb405..d941916ff62a6 100644 --- a/vendor/github.com/docker/swarmkit/api/types.pb.go +++ b/vendor/github.com/docker/swarmkit/api/types.pb.go @@ -3,26 +3,23 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/gogo/protobuf/types" -import google_protobuf1 "github.com/gogo/protobuf/types" -import _ "github.com/gogo/protobuf/gogoproto" - -import os "os" -import time "time" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import binary "encoding/binary" -import types "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import io "io" +import ( + encoding_binary "encoding/binary" + fmt "fmt" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + types "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" + os "os" + reflect "reflect" + strings "strings" + time "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -30,6 +27,12 @@ var _ = fmt.Errorf var _ = math.Inf var _ = time.Kitchen +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + type ResourceType int32 const ( @@ -43,6 +46,7 @@ var ResourceType_name = map[int32]string{ 1: "SECRET", 2: "CONFIG", } + var ResourceType_value = map[string]int32{ "TASK": 0, "SECRET": 1, @@ -52,7 +56,10 @@ var ResourceType_value = map[string]int32{ func (x ResourceType) String() string { return proto.EnumName(ResourceType_name, int32(x)) } -func (ResourceType) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{0} } + +func (ResourceType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{0} +} // Only the manager create a NEW task, and move the task to PENDING and ASSIGNED. // Afterward, the manager must rely on the agent to update the task status @@ -104,6 +111,7 @@ var TaskState_name = map[int32]string{ 800: "REMOVE", 832: "ORPHANED", } + var TaskState_value = map[string]int32{ "NEW": 0, "PENDING": 64, @@ -124,7 +132,10 @@ var TaskState_value = map[string]int32{ func (x TaskState) String() string { return proto.EnumName(TaskState_name, int32(x)) } -func (TaskState) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{1} } + +func (TaskState) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{1} +} type NodeRole int32 @@ -137,6 +148,7 @@ var NodeRole_name = map[int32]string{ 0: "WORKER", 1: "MANAGER", } + var NodeRole_value = map[string]int32{ "WORKER": 0, "MANAGER": 1, @@ -145,7 +157,10 @@ var NodeRole_value = map[string]int32{ func (x NodeRole) String() string { return proto.EnumName(NodeRole_name, int32(x)) } -func (NodeRole) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{2} } + +func (NodeRole) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{2} +} type RaftMemberStatus_Reachability int32 @@ -165,6 +180,7 @@ var RaftMemberStatus_Reachability_name = map[int32]string{ 1: "UNREACHABLE", 2: "REACHABLE", } + var RaftMemberStatus_Reachability_value = map[string]int32{ "UNKNOWN": 0, "UNREACHABLE": 1, @@ -174,8 +190,9 @@ var RaftMemberStatus_Reachability_value = map[string]int32{ func (x RaftMemberStatus_Reachability) String() string { return proto.EnumName(RaftMemberStatus_Reachability_name, int32(x)) } + func (RaftMemberStatus_Reachability) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{13, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{13, 0} } // TODO(aluzzardi) These should be using `gogoproto.enumvalue_customname`. @@ -198,6 +215,7 @@ var NodeStatus_State_name = map[int32]string{ 2: "READY", 3: "DISCONNECTED", } + var NodeStatus_State_value = map[string]int32{ "UNKNOWN": 0, "DOWN": 1, @@ -208,7 +226,10 @@ var NodeStatus_State_value = map[string]int32{ func (x NodeStatus_State) String() string { return proto.EnumName(NodeStatus_State_name, int32(x)) } -func (NodeStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{14, 0} } + +func (NodeStatus_State) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{14, 0} +} type Mount_MountType int32 @@ -225,6 +246,7 @@ var Mount_MountType_name = map[int32]string{ 2: "TMPFS", 3: "NPIPE", } + var Mount_MountType_value = map[string]int32{ "BIND": 0, "VOLUME": 1, @@ -235,7 +257,10 @@ var Mount_MountType_value = map[string]int32{ func (x Mount_MountType) String() string { return proto.EnumName(Mount_MountType_name, int32(x)) } -func (Mount_MountType) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16, 0} } + +func (Mount_MountType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{16, 0} +} // Consistency indicates the tolerable level of file system consistency type Mount_MountConsistency int32 @@ -253,6 +278,7 @@ var Mount_MountConsistency_name = map[int32]string{ 2: "CACHED", 3: "DELEGATED", } + var Mount_MountConsistency_value = map[string]int32{ "DEFAULT": 0, "CONSISTENT": 1, @@ -263,8 +289,9 @@ var Mount_MountConsistency_value = map[string]int32{ func (x Mount_MountConsistency) String() string { return proto.EnumName(Mount_MountConsistency_name, int32(x)) } + func (Mount_MountConsistency) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{16, 1} + return fileDescriptor_0b5eafd0404ded3d, []int{16, 1} } type Mount_BindOptions_MountPropagation int32 @@ -286,6 +313,7 @@ var Mount_BindOptions_MountPropagation_name = map[int32]string{ 4: "RSLAVE", 5: "SLAVE", } + var Mount_BindOptions_MountPropagation_value = map[string]int32{ "RPRIVATE": 0, "PRIVATE": 1, @@ -298,8 +326,9 @@ var Mount_BindOptions_MountPropagation_value = map[string]int32{ func (x Mount_BindOptions_MountPropagation) String() string { return proto.EnumName(Mount_BindOptions_MountPropagation_name, int32(x)) } + func (Mount_BindOptions_MountPropagation) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{16, 0, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{16, 0, 0} } type RestartPolicy_RestartCondition int32 @@ -315,6 +344,7 @@ var RestartPolicy_RestartCondition_name = map[int32]string{ 1: "ON_FAILURE", 2: "ANY", } + var RestartPolicy_RestartCondition_value = map[string]int32{ "NONE": 0, "ON_FAILURE": 1, @@ -324,8 +354,9 @@ var RestartPolicy_RestartCondition_value = map[string]int32{ func (x RestartPolicy_RestartCondition) String() string { return proto.EnumName(RestartPolicy_RestartCondition_name, int32(x)) } + func (RestartPolicy_RestartCondition) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{17, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{17, 0} } type UpdateConfig_FailureAction int32 @@ -341,6 +372,7 @@ var UpdateConfig_FailureAction_name = map[int32]string{ 1: "CONTINUE", 2: "ROLLBACK", } + var UpdateConfig_FailureAction_value = map[string]int32{ "PAUSE": 0, "CONTINUE": 1, @@ -350,8 +382,9 @@ var UpdateConfig_FailureAction_value = map[string]int32{ func (x UpdateConfig_FailureAction) String() string { return proto.EnumName(UpdateConfig_FailureAction_name, int32(x)) } + func (UpdateConfig_FailureAction) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{18, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{18, 0} } // UpdateOrder controls the order of operations when rolling out an @@ -369,6 +402,7 @@ var UpdateConfig_UpdateOrder_name = map[int32]string{ 0: "STOP_FIRST", 1: "START_FIRST", } + var UpdateConfig_UpdateOrder_value = map[string]int32{ "STOP_FIRST": 0, "START_FIRST": 1, @@ -377,8 +411,9 @@ var UpdateConfig_UpdateOrder_value = map[string]int32{ func (x UpdateConfig_UpdateOrder) String() string { return proto.EnumName(UpdateConfig_UpdateOrder_name, int32(x)) } + func (UpdateConfig_UpdateOrder) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{18, 1} + return fileDescriptor_0b5eafd0404ded3d, []int{18, 1} } type UpdateStatus_UpdateState int32 @@ -402,6 +437,7 @@ var UpdateStatus_UpdateState_name = map[int32]string{ 5: "ROLLBACK_PAUSED", 6: "ROLLBACK_COMPLETED", } + var UpdateStatus_UpdateState_value = map[string]int32{ "UNKNOWN": 0, "UPDATING": 1, @@ -415,8 +451,9 @@ var UpdateStatus_UpdateState_value = map[string]int32{ func (x UpdateStatus_UpdateState) String() string { return proto.EnumName(UpdateStatus_UpdateState_name, int32(x)) } + func (UpdateStatus_UpdateState) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{19, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{19, 0} } // AddressFamily specifies the network address family that @@ -434,6 +471,7 @@ var IPAMConfig_AddressFamily_name = map[int32]string{ 4: "IPV4", 6: "IPV6", } + var IPAMConfig_AddressFamily_value = map[string]int32{ "UNKNOWN": 0, "IPV4": 4, @@ -443,8 +481,9 @@ var IPAMConfig_AddressFamily_value = map[string]int32{ func (x IPAMConfig_AddressFamily) String() string { return proto.EnumName(IPAMConfig_AddressFamily_name, int32(x)) } + func (IPAMConfig_AddressFamily) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{24, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{24, 0} } type PortConfig_Protocol int32 @@ -460,6 +499,7 @@ var PortConfig_Protocol_name = map[int32]string{ 1: "UDP", 2: "SCTP", } + var PortConfig_Protocol_value = map[string]int32{ "TCP": 0, "UDP": 1, @@ -469,7 +509,10 @@ var PortConfig_Protocol_value = map[string]int32{ func (x PortConfig_Protocol) String() string { return proto.EnumName(PortConfig_Protocol_name, int32(x)) } -func (PortConfig_Protocol) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25, 0} } + +func (PortConfig_Protocol) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{25, 0} +} // PublishMode controls how ports are published on the swarm. type PortConfig_PublishMode int32 @@ -488,6 +531,7 @@ var PortConfig_PublishMode_name = map[int32]string{ 0: "INGRESS", 1: "HOST", } + var PortConfig_PublishMode_value = map[string]int32{ "INGRESS": 0, "HOST": 1, @@ -496,8 +540,9 @@ var PortConfig_PublishMode_value = map[string]int32{ func (x PortConfig_PublishMode) String() string { return proto.EnumName(PortConfig_PublishMode_name, int32(x)) } + func (PortConfig_PublishMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{25, 1} + return fileDescriptor_0b5eafd0404ded3d, []int{25, 1} } type IssuanceStatus_State int32 @@ -525,6 +570,7 @@ var IssuanceStatus_State_name = map[int32]string{ 4: "FAILED", 5: "ROTATE", } + var IssuanceStatus_State_value = map[string]int32{ "UNKNOWN": 0, "RENEW": 1, @@ -537,7 +583,10 @@ var IssuanceStatus_State_value = map[string]int32{ func (x IssuanceStatus_State) String() string { return proto.EnumName(IssuanceStatus_State_name, int32(x)) } -func (IssuanceStatus_State) EnumDescriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30, 0} } + +func (IssuanceStatus_State) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{30, 0} +} type ExternalCA_CAProtocol int32 @@ -548,6 +597,7 @@ const ( var ExternalCA_CAProtocol_name = map[int32]string{ 0: "CFSSL", } + var ExternalCA_CAProtocol_value = map[string]int32{ "CFSSL": 0, } @@ -555,8 +605,9 @@ var ExternalCA_CAProtocol_value = map[string]int32{ func (x ExternalCA_CAProtocol) String() string { return proto.EnumName(ExternalCA_CAProtocol_name, int32(x)) } + func (ExternalCA_CAProtocol) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{32, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{32, 0} } // Encryption algorithm that can implemented using this key @@ -569,6 +620,7 @@ const ( var EncryptionKey_Algorithm_name = map[int32]string{ 0: "AES_128_GCM", } + var EncryptionKey_Algorithm_value = map[string]int32{ "AES_128_GCM": 0, } @@ -576,8 +628,9 @@ var EncryptionKey_Algorithm_value = map[string]int32{ func (x EncryptionKey_Algorithm) String() string { return proto.EnumName(EncryptionKey_Algorithm_name, int32(x)) } + func (EncryptionKey_Algorithm) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{45, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{45, 0} } type MaybeEncryptedRecord_Algorithm int32 @@ -593,6 +646,7 @@ var MaybeEncryptedRecord_Algorithm_name = map[int32]string{ 1: "SECRETBOX_SALSA20_POLY1305", 2: "FERNET_AES_128_CBC", } + var MaybeEncryptedRecord_Algorithm_value = map[string]int32{ "NONE": 0, "SECRETBOX_SALSA20_POLY1305": 1, @@ -602,8 +656,9 @@ var MaybeEncryptedRecord_Algorithm_value = map[string]int32{ func (x MaybeEncryptedRecord_Algorithm) String() string { return proto.EnumName(MaybeEncryptedRecord_Algorithm_name, int32(x)) } + func (MaybeEncryptedRecord_Algorithm) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{52, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{53, 0} } // Version tracks the last time an object in the store was updated. @@ -611,32 +666,116 @@ type Version struct { Index uint64 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` } -func (m *Version) Reset() { *m = Version{} } -func (*Version) ProtoMessage() {} -func (*Version) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{0} } +func (m *Version) Reset() { *m = Version{} } +func (*Version) ProtoMessage() {} +func (*Version) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{0} +} +func (m *Version) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Version.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Version) XXX_Merge(src proto.Message) { + xxx_messageInfo_Version.Merge(m, src) +} +func (m *Version) XXX_Size() int { + return m.Size() +} +func (m *Version) XXX_DiscardUnknown() { + xxx_messageInfo_Version.DiscardUnknown(m) +} + +var xxx_messageInfo_Version proto.InternalMessageInfo type IndexEntry struct { Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Val string `protobuf:"bytes,2,opt,name=val,proto3" json:"val,omitempty"` } -func (m *IndexEntry) Reset() { *m = IndexEntry{} } -func (*IndexEntry) ProtoMessage() {} -func (*IndexEntry) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{1} } +func (m *IndexEntry) Reset() { *m = IndexEntry{} } +func (*IndexEntry) ProtoMessage() {} +func (*IndexEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{1} +} +func (m *IndexEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IndexEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IndexEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *IndexEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_IndexEntry.Merge(m, src) +} +func (m *IndexEntry) XXX_Size() int { + return m.Size() +} +func (m *IndexEntry) XXX_DiscardUnknown() { + xxx_messageInfo_IndexEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_IndexEntry proto.InternalMessageInfo // Annotations provide useful information to identify API objects. They are // common to all API specs. type Annotations struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Indices provides keys and values for indexing this object. // A single key may have multiple values. - Indices []IndexEntry `protobuf:"bytes,4,rep,name=indices" json:"indices"` + Indices []IndexEntry `protobuf:"bytes,4,rep,name=indices,proto3" json:"indices"` +} + +func (m *Annotations) Reset() { *m = Annotations{} } +func (*Annotations) ProtoMessage() {} +func (*Annotations) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{2} +} +func (m *Annotations) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Annotations) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Annotations.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Annotations) XXX_Merge(src proto.Message) { + xxx_messageInfo_Annotations.Merge(m, src) +} +func (m *Annotations) XXX_Size() int { + return m.Size() +} +func (m *Annotations) XXX_DiscardUnknown() { + xxx_messageInfo_Annotations.DiscardUnknown(m) } -func (m *Annotations) Reset() { *m = Annotations{} } -func (*Annotations) ProtoMessage() {} -func (*Annotations) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{2} } +var xxx_messageInfo_Annotations proto.InternalMessageInfo // NamedGenericResource represents a "user defined" resource which is defined // as a string. @@ -647,9 +786,37 @@ type NamedGenericResource struct { Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } -func (m *NamedGenericResource) Reset() { *m = NamedGenericResource{} } -func (*NamedGenericResource) ProtoMessage() {} -func (*NamedGenericResource) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{3} } +func (m *NamedGenericResource) Reset() { *m = NamedGenericResource{} } +func (*NamedGenericResource) ProtoMessage() {} +func (*NamedGenericResource) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{3} +} +func (m *NamedGenericResource) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NamedGenericResource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NamedGenericResource.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NamedGenericResource) XXX_Merge(src proto.Message) { + xxx_messageInfo_NamedGenericResource.Merge(m, src) +} +func (m *NamedGenericResource) XXX_Size() int { + return m.Size() +} +func (m *NamedGenericResource) XXX_DiscardUnknown() { + xxx_messageInfo_NamedGenericResource.DiscardUnknown(m) +} + +var xxx_messageInfo_NamedGenericResource proto.InternalMessageInfo // DiscreteGenericResource represents a "user defined" resource which is defined // as an integer @@ -660,9 +827,37 @@ type DiscreteGenericResource struct { Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` } -func (m *DiscreteGenericResource) Reset() { *m = DiscreteGenericResource{} } -func (*DiscreteGenericResource) ProtoMessage() {} -func (*DiscreteGenericResource) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{4} } +func (m *DiscreteGenericResource) Reset() { *m = DiscreteGenericResource{} } +func (*DiscreteGenericResource) ProtoMessage() {} +func (*DiscreteGenericResource) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{4} +} +func (m *DiscreteGenericResource) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DiscreteGenericResource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DiscreteGenericResource.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DiscreteGenericResource) XXX_Merge(src proto.Message) { + xxx_messageInfo_DiscreteGenericResource.Merge(m, src) +} +func (m *DiscreteGenericResource) XXX_Size() int { + return m.Size() +} +func (m *DiscreteGenericResource) XXX_DiscardUnknown() { + xxx_messageInfo_DiscreteGenericResource.DiscardUnknown(m) +} + +var xxx_messageInfo_DiscreteGenericResource proto.InternalMessageInfo // GenericResource represents a "user defined" resource which can // be either an integer (e.g: SSD=3) or a string (e.g: SSD=sda1) @@ -673,9 +868,37 @@ type GenericResource struct { Resource isGenericResource_Resource `protobuf_oneof:"resource"` } -func (m *GenericResource) Reset() { *m = GenericResource{} } -func (*GenericResource) ProtoMessage() {} -func (*GenericResource) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{5} } +func (m *GenericResource) Reset() { *m = GenericResource{} } +func (*GenericResource) ProtoMessage() {} +func (*GenericResource) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{5} +} +func (m *GenericResource) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenericResource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenericResource.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenericResource) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenericResource.Merge(m, src) +} +func (m *GenericResource) XXX_Size() int { + return m.Size() +} +func (m *GenericResource) XXX_DiscardUnknown() { + xxx_messageInfo_GenericResource.DiscardUnknown(m) +} + +var xxx_messageInfo_GenericResource proto.InternalMessageInfo type isGenericResource_Resource interface { isGenericResource_Resource() @@ -684,10 +907,10 @@ type isGenericResource_Resource interface { } type GenericResource_NamedResourceSpec struct { - NamedResourceSpec *NamedGenericResource `protobuf:"bytes,1,opt,name=named_resource_spec,json=namedResourceSpec,oneof"` + NamedResourceSpec *NamedGenericResource `protobuf:"bytes,1,opt,name=named_resource_spec,json=namedResourceSpec,proto3,oneof" json:"named_resource_spec,omitempty"` } type GenericResource_DiscreteResourceSpec struct { - DiscreteResourceSpec *DiscreteGenericResource `protobuf:"bytes,2,opt,name=discrete_resource_spec,json=discreteResourceSpec,oneof"` + DiscreteResourceSpec *DiscreteGenericResource `protobuf:"bytes,2,opt,name=discrete_resource_spec,json=discreteResourceSpec,proto3,oneof" json:"discrete_resource_spec,omitempty"` } func (*GenericResource_NamedResourceSpec) isGenericResource_Resource() {} @@ -714,101 +937,100 @@ func (m *GenericResource) GetDiscreteResourceSpec() *DiscreteGenericResource { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*GenericResource) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _GenericResource_OneofMarshaler, _GenericResource_OneofUnmarshaler, _GenericResource_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*GenericResource) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*GenericResource_NamedResourceSpec)(nil), (*GenericResource_DiscreteResourceSpec)(nil), } } -func _GenericResource_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*GenericResource) - // resource - switch x := m.Resource.(type) { - case *GenericResource_NamedResourceSpec: - _ = b.EncodeVarint(1<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.NamedResourceSpec); err != nil { - return err - } - case *GenericResource_DiscreteResourceSpec: - _ = b.EncodeVarint(2<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.DiscreteResourceSpec); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("GenericResource.Resource has unexpected type %T", x) - } - return nil -} - -func _GenericResource_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*GenericResource) - switch tag { - case 1: // resource.named_resource_spec - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(NamedGenericResource) - err := b.DecodeMessage(msg) - m.Resource = &GenericResource_NamedResourceSpec{msg} - return true, err - case 2: // resource.discrete_resource_spec - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(DiscreteGenericResource) - err := b.DecodeMessage(msg) - m.Resource = &GenericResource_DiscreteResourceSpec{msg} - return true, err - default: - return false, nil - } -} - -func _GenericResource_OneofSizer(msg proto.Message) (n int) { - m := msg.(*GenericResource) - // resource - switch x := m.Resource.(type) { - case *GenericResource_NamedResourceSpec: - s := proto.Size(x.NamedResourceSpec) - n += proto.SizeVarint(1<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *GenericResource_DiscreteResourceSpec: - s := proto.Size(x.DiscreteResourceSpec) - n += proto.SizeVarint(2<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - type Resources struct { // Amount of CPUs (e.g. 2000000000 = 2 CPU cores) NanoCPUs int64 `protobuf:"varint,1,opt,name=nano_cpus,json=nanoCpus,proto3" json:"nano_cpus,omitempty"` // Amount of memory in bytes. MemoryBytes int64 `protobuf:"varint,2,opt,name=memory_bytes,json=memoryBytes,proto3" json:"memory_bytes,omitempty"` // User specified resource (e.g: bananas=2;apple={red,yellow,green}) - Generic []*GenericResource `protobuf:"bytes,3,rep,name=generic" json:"generic,omitempty"` + Generic []*GenericResource `protobuf:"bytes,3,rep,name=generic,proto3" json:"generic,omitempty"` +} + +func (m *Resources) Reset() { *m = Resources{} } +func (*Resources) ProtoMessage() {} +func (*Resources) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{6} +} +func (m *Resources) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Resources) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Resources.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Resources) XXX_Merge(src proto.Message) { + xxx_messageInfo_Resources.Merge(m, src) +} +func (m *Resources) XXX_Size() int { + return m.Size() +} +func (m *Resources) XXX_DiscardUnknown() { + xxx_messageInfo_Resources.DiscardUnknown(m) } -func (m *Resources) Reset() { *m = Resources{} } -func (*Resources) ProtoMessage() {} -func (*Resources) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{6} } +var xxx_messageInfo_Resources proto.InternalMessageInfo type ResourceRequirements struct { - Limits *Resources `protobuf:"bytes,1,opt,name=limits" json:"limits,omitempty"` - Reservations *Resources `protobuf:"bytes,2,opt,name=reservations" json:"reservations,omitempty"` + Limits *Resources `protobuf:"bytes,1,opt,name=limits,proto3" json:"limits,omitempty"` + Reservations *Resources `protobuf:"bytes,2,opt,name=reservations,proto3" json:"reservations,omitempty"` + // Amount of swap in bytes - can only be used together with a memory limit + // -1 means unlimited + // a null pointer indicates that the default behaviour of granting twice + // the memory is maintained + SwapBytes *types.Int64Value `protobuf:"bytes,3,opt,name=swap_bytes,json=swapBytes,proto3" json:"swap_bytes,omitempty"` + // Tune container memory swappiness (0 to 100) - if not specified, defaults + // to the container OS's default - generally 60, or the value predefined in + // the image; set to -1 to unset a previously set value + MemorySwappiness *types.Int64Value `protobuf:"bytes,4,opt,name=memory_swappiness,json=memorySwappiness,proto3" json:"memory_swappiness,omitempty"` +} + +func (m *ResourceRequirements) Reset() { *m = ResourceRequirements{} } +func (*ResourceRequirements) ProtoMessage() {} +func (*ResourceRequirements) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{7} +} +func (m *ResourceRequirements) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceRequirements) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceRequirements.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceRequirements) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceRequirements.Merge(m, src) +} +func (m *ResourceRequirements) XXX_Size() int { + return m.Size() +} +func (m *ResourceRequirements) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceRequirements.DiscardUnknown(m) } -func (m *ResourceRequirements) Reset() { *m = ResourceRequirements{} } -func (*ResourceRequirements) ProtoMessage() {} -func (*ResourceRequirements) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{7} } +var xxx_messageInfo_ResourceRequirements proto.InternalMessageInfo type Platform struct { // Architecture (e.g. x86_64) @@ -817,9 +1039,37 @@ type Platform struct { OS string `protobuf:"bytes,2,opt,name=os,proto3" json:"os,omitempty"` } -func (m *Platform) Reset() { *m = Platform{} } -func (*Platform) ProtoMessage() {} -func (*Platform) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{8} } +func (m *Platform) Reset() { *m = Platform{} } +func (*Platform) ProtoMessage() {} +func (*Platform) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{8} +} +func (m *Platform) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Platform) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Platform.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Platform) XXX_Merge(src proto.Message) { + xxx_messageInfo_Platform.Merge(m, src) +} +func (m *Platform) XXX_Size() int { + return m.Size() +} +func (m *Platform) XXX_DiscardUnknown() { + xxx_messageInfo_Platform.DiscardUnknown(m) +} + +var xxx_messageInfo_Platform proto.InternalMessageInfo // PluginDescription describes an engine plugin. type PluginDescription struct { @@ -831,42 +1081,126 @@ type PluginDescription struct { Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` } -func (m *PluginDescription) Reset() { *m = PluginDescription{} } -func (*PluginDescription) ProtoMessage() {} -func (*PluginDescription) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{9} } +func (m *PluginDescription) Reset() { *m = PluginDescription{} } +func (*PluginDescription) ProtoMessage() {} +func (*PluginDescription) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{9} +} +func (m *PluginDescription) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PluginDescription) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PluginDescription.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PluginDescription) XXX_Merge(src proto.Message) { + xxx_messageInfo_PluginDescription.Merge(m, src) +} +func (m *PluginDescription) XXX_Size() int { + return m.Size() +} +func (m *PluginDescription) XXX_DiscardUnknown() { + xxx_messageInfo_PluginDescription.DiscardUnknown(m) +} + +var xxx_messageInfo_PluginDescription proto.InternalMessageInfo type EngineDescription struct { // Docker daemon version running on the node. EngineVersion string `protobuf:"bytes,1,opt,name=engine_version,json=engineVersion,proto3" json:"engine_version,omitempty"` // Labels attached to the engine. - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Volume, Network, and Auth plugins - Plugins []PluginDescription `protobuf:"bytes,3,rep,name=plugins" json:"plugins"` + Plugins []PluginDescription `protobuf:"bytes,3,rep,name=plugins,proto3" json:"plugins"` +} + +func (m *EngineDescription) Reset() { *m = EngineDescription{} } +func (*EngineDescription) ProtoMessage() {} +func (*EngineDescription) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{10} +} +func (m *EngineDescription) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EngineDescription) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EngineDescription.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EngineDescription) XXX_Merge(src proto.Message) { + xxx_messageInfo_EngineDescription.Merge(m, src) +} +func (m *EngineDescription) XXX_Size() int { + return m.Size() +} +func (m *EngineDescription) XXX_DiscardUnknown() { + xxx_messageInfo_EngineDescription.DiscardUnknown(m) } -func (m *EngineDescription) Reset() { *m = EngineDescription{} } -func (*EngineDescription) ProtoMessage() {} -func (*EngineDescription) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{10} } +var xxx_messageInfo_EngineDescription proto.InternalMessageInfo type NodeDescription struct { // Hostname of the node as reported by the agent. // This is different from spec.meta.name which is user-defined. Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` // Platform of the node. - Platform *Platform `protobuf:"bytes,2,opt,name=platform" json:"platform,omitempty"` + Platform *Platform `protobuf:"bytes,2,opt,name=platform,proto3" json:"platform,omitempty"` // Total resources on the node. - Resources *Resources `protobuf:"bytes,3,opt,name=resources" json:"resources,omitempty"` + Resources *Resources `protobuf:"bytes,3,opt,name=resources,proto3" json:"resources,omitempty"` // Information about the Docker Engine on the node. - Engine *EngineDescription `protobuf:"bytes,4,opt,name=engine" json:"engine,omitempty"` + Engine *EngineDescription `protobuf:"bytes,4,opt,name=engine,proto3" json:"engine,omitempty"` // Information on the node's TLS setup - TLSInfo *NodeTLSInfo `protobuf:"bytes,5,opt,name=tls_info,json=tlsInfo" json:"tls_info,omitempty"` + TLSInfo *NodeTLSInfo `protobuf:"bytes,5,opt,name=tls_info,json=tlsInfo,proto3" json:"tls_info,omitempty"` // FIPS indicates whether the node has FIPS-enabled FIPS bool `protobuf:"varint,6,opt,name=fips,proto3" json:"fips,omitempty"` } -func (m *NodeDescription) Reset() { *m = NodeDescription{} } -func (*NodeDescription) ProtoMessage() {} -func (*NodeDescription) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{11} } +func (m *NodeDescription) Reset() { *m = NodeDescription{} } +func (*NodeDescription) ProtoMessage() {} +func (*NodeDescription) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{11} +} +func (m *NodeDescription) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NodeDescription) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NodeDescription.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NodeDescription) XXX_Merge(src proto.Message) { + xxx_messageInfo_NodeDescription.Merge(m, src) +} +func (m *NodeDescription) XXX_Size() int { + return m.Size() +} +func (m *NodeDescription) XXX_DiscardUnknown() { + xxx_messageInfo_NodeDescription.DiscardUnknown(m) +} + +var xxx_messageInfo_NodeDescription proto.InternalMessageInfo type NodeTLSInfo struct { // Information about which root certs the node trusts @@ -876,9 +1210,37 @@ type NodeTLSInfo struct { CertIssuerPublicKey []byte `protobuf:"bytes,3,opt,name=cert_issuer_public_key,json=certIssuerPublicKey,proto3" json:"cert_issuer_public_key,omitempty"` } -func (m *NodeTLSInfo) Reset() { *m = NodeTLSInfo{} } -func (*NodeTLSInfo) ProtoMessage() {} -func (*NodeTLSInfo) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{12} } +func (m *NodeTLSInfo) Reset() { *m = NodeTLSInfo{} } +func (*NodeTLSInfo) ProtoMessage() {} +func (*NodeTLSInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{12} +} +func (m *NodeTLSInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NodeTLSInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NodeTLSInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NodeTLSInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_NodeTLSInfo.Merge(m, src) +} +func (m *NodeTLSInfo) XXX_Size() int { + return m.Size() +} +func (m *NodeTLSInfo) XXX_DiscardUnknown() { + xxx_messageInfo_NodeTLSInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_NodeTLSInfo proto.InternalMessageInfo type RaftMemberStatus struct { Leader bool `protobuf:"varint,1,opt,name=leader,proto3" json:"leader,omitempty"` @@ -886,9 +1248,37 @@ type RaftMemberStatus struct { Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` } -func (m *RaftMemberStatus) Reset() { *m = RaftMemberStatus{} } -func (*RaftMemberStatus) ProtoMessage() {} -func (*RaftMemberStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{13} } +func (m *RaftMemberStatus) Reset() { *m = RaftMemberStatus{} } +func (*RaftMemberStatus) ProtoMessage() {} +func (*RaftMemberStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{13} +} +func (m *RaftMemberStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RaftMemberStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RaftMemberStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RaftMemberStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_RaftMemberStatus.Merge(m, src) +} +func (m *RaftMemberStatus) XXX_Size() int { + return m.Size() +} +func (m *RaftMemberStatus) XXX_DiscardUnknown() { + xxx_messageInfo_RaftMemberStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_RaftMemberStatus proto.InternalMessageInfo type NodeStatus struct { State NodeStatus_State `protobuf:"varint,1,opt,name=state,proto3,enum=docker.swarmkit.v1.NodeStatus_State" json:"state,omitempty"` @@ -897,9 +1287,37 @@ type NodeStatus struct { Addr string `protobuf:"bytes,3,opt,name=addr,proto3" json:"addr,omitempty"` } -func (m *NodeStatus) Reset() { *m = NodeStatus{} } -func (*NodeStatus) ProtoMessage() {} -func (*NodeStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{14} } +func (m *NodeStatus) Reset() { *m = NodeStatus{} } +func (*NodeStatus) ProtoMessage() {} +func (*NodeStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{14} +} +func (m *NodeStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NodeStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NodeStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NodeStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_NodeStatus.Merge(m, src) +} +func (m *NodeStatus) XXX_Size() int { + return m.Size() +} +func (m *NodeStatus) XXX_DiscardUnknown() { + xxx_messageInfo_NodeStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_NodeStatus proto.InternalMessageInfo type Image struct { // reference is a docker image reference. This can include a rpository, tag @@ -908,9 +1326,37 @@ type Image struct { Reference string `protobuf:"bytes,1,opt,name=reference,proto3" json:"reference,omitempty"` } -func (m *Image) Reset() { *m = Image{} } -func (*Image) ProtoMessage() {} -func (*Image) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{15} } +func (m *Image) Reset() { *m = Image{} } +func (*Image) ProtoMessage() {} +func (*Image) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{15} +} +func (m *Image) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Image) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Image.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Image) XXX_Merge(src proto.Message) { + xxx_messageInfo_Image.Merge(m, src) +} +func (m *Image) XXX_Size() int { + return m.Size() +} +func (m *Image) XXX_DiscardUnknown() { + xxx_messageInfo_Image.DiscardUnknown(m) +} + +var xxx_messageInfo_Image proto.InternalMessageInfo // Mount describes volume mounts for a container. // @@ -932,48 +1378,134 @@ type Mount struct { // BindOptions configures properties of a bind mount type. // // For mounts of type bind, the source must be an absolute host path. - BindOptions *Mount_BindOptions `protobuf:"bytes,5,opt,name=bind_options,json=bindOptions" json:"bind_options,omitempty"` + BindOptions *Mount_BindOptions `protobuf:"bytes,5,opt,name=bind_options,json=bindOptions,proto3" json:"bind_options,omitempty"` // VolumeOptions configures the properties specific to a volume mount type. // // For mounts of type volume, the source will be used as the volume name. - VolumeOptions *Mount_VolumeOptions `protobuf:"bytes,6,opt,name=volume_options,json=volumeOptions" json:"volume_options,omitempty"` + VolumeOptions *Mount_VolumeOptions `protobuf:"bytes,6,opt,name=volume_options,json=volumeOptions,proto3" json:"volume_options,omitempty"` // TmpfsOptions allows one to set options for mounting a temporary // filesystem. // // The source field will be ignored when using mounts of type tmpfs. - TmpfsOptions *Mount_TmpfsOptions `protobuf:"bytes,7,opt,name=tmpfs_options,json=tmpfsOptions" json:"tmpfs_options,omitempty"` + TmpfsOptions *Mount_TmpfsOptions `protobuf:"bytes,7,opt,name=tmpfs_options,json=tmpfsOptions,proto3" json:"tmpfs_options,omitempty"` +} + +func (m *Mount) Reset() { *m = Mount{} } +func (*Mount) ProtoMessage() {} +func (*Mount) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{16} +} +func (m *Mount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Mount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Mount.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Mount) XXX_Merge(src proto.Message) { + xxx_messageInfo_Mount.Merge(m, src) +} +func (m *Mount) XXX_Size() int { + return m.Size() +} +func (m *Mount) XXX_DiscardUnknown() { + xxx_messageInfo_Mount.DiscardUnknown(m) } -func (m *Mount) Reset() { *m = Mount{} } -func (*Mount) ProtoMessage() {} -func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16} } +var xxx_messageInfo_Mount proto.InternalMessageInfo // BindOptions specifies options that are specific to a bind mount. type Mount_BindOptions struct { // Propagation mode of mount. Propagation Mount_BindOptions_MountPropagation `protobuf:"varint,1,opt,name=propagation,proto3,enum=docker.swarmkit.v1.Mount_BindOptions_MountPropagation" json:"propagation,omitempty"` + // allows non-recursive bind-mount, i.e. mount(2) with "bind" rather than "rbind". + NonRecursive bool `protobuf:"varint,2,opt,name=nonrecursive,proto3" json:"nonrecursive,omitempty"` +} + +func (m *Mount_BindOptions) Reset() { *m = Mount_BindOptions{} } +func (*Mount_BindOptions) ProtoMessage() {} +func (*Mount_BindOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{16, 0} +} +func (m *Mount_BindOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Mount_BindOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Mount_BindOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Mount_BindOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_Mount_BindOptions.Merge(m, src) +} +func (m *Mount_BindOptions) XXX_Size() int { + return m.Size() +} +func (m *Mount_BindOptions) XXX_DiscardUnknown() { + xxx_messageInfo_Mount_BindOptions.DiscardUnknown(m) } -func (m *Mount_BindOptions) Reset() { *m = Mount_BindOptions{} } -func (*Mount_BindOptions) ProtoMessage() {} -func (*Mount_BindOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16, 0} } +var xxx_messageInfo_Mount_BindOptions proto.InternalMessageInfo // VolumeOptions contains parameters for mounting the volume. type Mount_VolumeOptions struct { // nocopy prevents automatic copying of data to the volume with data from target NoCopy bool `protobuf:"varint,1,opt,name=nocopy,proto3" json:"nocopy,omitempty"` // labels to apply to the volume if creating - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Labels map[string]string `protobuf:"bytes,2,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // DriverConfig specifies the options that may be passed to the driver // if the volume is created. // // If this is empty, no volume will be created if the volume is missing. - DriverConfig *Driver `protobuf:"bytes,3,opt,name=driver_config,json=driverConfig" json:"driver_config,omitempty"` + DriverConfig *Driver `protobuf:"bytes,3,opt,name=driver_config,json=driverConfig,proto3" json:"driver_config,omitempty"` +} + +func (m *Mount_VolumeOptions) Reset() { *m = Mount_VolumeOptions{} } +func (*Mount_VolumeOptions) ProtoMessage() {} +func (*Mount_VolumeOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{16, 1} +} +func (m *Mount_VolumeOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Mount_VolumeOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Mount_VolumeOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Mount_VolumeOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_Mount_VolumeOptions.Merge(m, src) +} +func (m *Mount_VolumeOptions) XXX_Size() int { + return m.Size() +} +func (m *Mount_VolumeOptions) XXX_DiscardUnknown() { + xxx_messageInfo_Mount_VolumeOptions.DiscardUnknown(m) } -func (m *Mount_VolumeOptions) Reset() { *m = Mount_VolumeOptions{} } -func (*Mount_VolumeOptions) ProtoMessage() {} -func (*Mount_VolumeOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16, 1} } +var xxx_messageInfo_Mount_VolumeOptions proto.InternalMessageInfo type Mount_TmpfsOptions struct { // Size sets the size of the tmpfs, in bytes. @@ -987,29 +1519,87 @@ type Mount_TmpfsOptions struct { SizeBytes int64 `protobuf:"varint,1,opt,name=size_bytes,json=sizeBytes,proto3" json:"size_bytes,omitempty"` // Mode of the tmpfs upon creation Mode os.FileMode `protobuf:"varint,2,opt,name=mode,proto3,customtype=os.FileMode" json:"mode"` + // Options passed to tmpfs mount + Options string `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"` } -func (m *Mount_TmpfsOptions) Reset() { *m = Mount_TmpfsOptions{} } -func (*Mount_TmpfsOptions) ProtoMessage() {} -func (*Mount_TmpfsOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{16, 2} } - -type RestartPolicy struct { - Condition RestartPolicy_RestartCondition `protobuf:"varint,1,opt,name=condition,proto3,enum=docker.swarmkit.v1.RestartPolicy_RestartCondition" json:"condition,omitempty"` - // Delay between restart attempts - // Note: can't use stdduration because this field needs to be nullable. - Delay *google_protobuf1.Duration `protobuf:"bytes,2,opt,name=delay" json:"delay,omitempty"` - // MaxAttempts is the maximum number of restarts to attempt on an - // instance before giving up. Ignored if 0. - MaxAttempts uint64 `protobuf:"varint,3,opt,name=max_attempts,json=maxAttempts,proto3" json:"max_attempts,omitempty"` +func (m *Mount_TmpfsOptions) Reset() { *m = Mount_TmpfsOptions{} } +func (*Mount_TmpfsOptions) ProtoMessage() {} +func (*Mount_TmpfsOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{16, 2} +} +func (m *Mount_TmpfsOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Mount_TmpfsOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Mount_TmpfsOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Mount_TmpfsOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_Mount_TmpfsOptions.Merge(m, src) +} +func (m *Mount_TmpfsOptions) XXX_Size() int { + return m.Size() +} +func (m *Mount_TmpfsOptions) XXX_DiscardUnknown() { + xxx_messageInfo_Mount_TmpfsOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_Mount_TmpfsOptions proto.InternalMessageInfo + +type RestartPolicy struct { + Condition RestartPolicy_RestartCondition `protobuf:"varint,1,opt,name=condition,proto3,enum=docker.swarmkit.v1.RestartPolicy_RestartCondition" json:"condition,omitempty"` + // Delay between restart attempts + // Note: can't use stdduration because this field needs to be nullable. + Delay *types.Duration `protobuf:"bytes,2,opt,name=delay,proto3" json:"delay,omitempty"` + // MaxAttempts is the maximum number of restarts to attempt on an + // instance before giving up. Ignored if 0. + MaxAttempts uint64 `protobuf:"varint,3,opt,name=max_attempts,json=maxAttempts,proto3" json:"max_attempts,omitempty"` // Window is the time window used to evaluate the restart policy. // The time window is unbounded if this is 0. // Note: can't use stdduration because this field needs to be nullable. - Window *google_protobuf1.Duration `protobuf:"bytes,4,opt,name=window" json:"window,omitempty"` + Window *types.Duration `protobuf:"bytes,4,opt,name=window,proto3" json:"window,omitempty"` +} + +func (m *RestartPolicy) Reset() { *m = RestartPolicy{} } +func (*RestartPolicy) ProtoMessage() {} +func (*RestartPolicy) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{17} +} +func (m *RestartPolicy) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RestartPolicy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RestartPolicy.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RestartPolicy) XXX_Merge(src proto.Message) { + xxx_messageInfo_RestartPolicy.Merge(m, src) +} +func (m *RestartPolicy) XXX_Size() int { + return m.Size() +} +func (m *RestartPolicy) XXX_DiscardUnknown() { + xxx_messageInfo_RestartPolicy.DiscardUnknown(m) } -func (m *RestartPolicy) Reset() { *m = RestartPolicy{} } -func (*RestartPolicy) ProtoMessage() {} -func (*RestartPolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{17} } +var xxx_messageInfo_RestartPolicy proto.InternalMessageInfo // UpdateConfig specifies the rate and policy of updates. // TODO(aluzzardi): Consider making this a oneof with RollingStrategy and LockstepStrategy. @@ -1018,7 +1608,7 @@ type UpdateConfig struct { // 0 means unlimited parallelism. Parallelism uint64 `protobuf:"varint,1,opt,name=parallelism,proto3" json:"parallelism,omitempty"` // Amount of time between updates. - Delay time.Duration `protobuf:"bytes,2,opt,name=delay,stdduration" json:"delay"` + Delay time.Duration `protobuf:"bytes,2,opt,name=delay,proto3,stdduration" json:"delay"` // FailureAction is the action to take when an update failures. FailureAction UpdateConfig_FailureAction `protobuf:"varint,3,opt,name=failure_action,json=failureAction,proto3,enum=docker.swarmkit.v1.UpdateConfig_FailureAction" json:"failure_action,omitempty"` // Monitor indicates how long to monitor a task for failure after it is @@ -1028,7 +1618,7 @@ type UpdateConfig struct { // count as a failure. If Monitor is unspecified, a default value will // be used. // Note: can't use stdduration because this field needs to be nullable. - Monitor *google_protobuf1.Duration `protobuf:"bytes,4,opt,name=monitor" json:"monitor,omitempty"` + Monitor *types.Duration `protobuf:"bytes,4,opt,name=monitor,proto3" json:"monitor,omitempty"` // MaxFailureRatio is the fraction of tasks that may fail during // an update before the failure action is invoked. Any task created by // the current update which ends up in one of the states REJECTED, @@ -1047,9 +1637,37 @@ type UpdateConfig struct { Order UpdateConfig_UpdateOrder `protobuf:"varint,6,opt,name=order,proto3,enum=docker.swarmkit.v1.UpdateConfig_UpdateOrder" json:"order,omitempty"` } -func (m *UpdateConfig) Reset() { *m = UpdateConfig{} } -func (*UpdateConfig) ProtoMessage() {} -func (*UpdateConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{18} } +func (m *UpdateConfig) Reset() { *m = UpdateConfig{} } +func (*UpdateConfig) ProtoMessage() {} +func (*UpdateConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{18} +} +func (m *UpdateConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateConfig.Merge(m, src) +} +func (m *UpdateConfig) XXX_Size() int { + return m.Size() +} +func (m *UpdateConfig) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateConfig proto.InternalMessageInfo // UpdateStatus is the status of an update in progress. type UpdateStatus struct { @@ -1059,11 +1677,11 @@ type UpdateStatus struct { State UpdateStatus_UpdateState `protobuf:"varint,1,opt,name=state,proto3,enum=docker.swarmkit.v1.UpdateStatus_UpdateState" json:"state,omitempty"` // StartedAt is the time at which the update was started. // Note: can't use stdtime because this field is nullable. - StartedAt *google_protobuf.Timestamp `protobuf:"bytes,2,opt,name=started_at,json=startedAt" json:"started_at,omitempty"` + StartedAt *types.Timestamp `protobuf:"bytes,2,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` // CompletedAt is the time at which the update completed successfully, // paused, or finished rolling back. // Note: can't use stdtime because this field is nullable. - CompletedAt *google_protobuf.Timestamp `protobuf:"bytes,3,opt,name=completed_at,json=completedAt" json:"completed_at,omitempty"` + CompletedAt *types.Timestamp `protobuf:"bytes,3,opt,name=completed_at,json=completedAt,proto3" json:"completed_at,omitempty"` // Message explains how the update got into its current state. For // example, if the update is paused, it will explain what is preventing // the update from proceeding (typically the failure of a task to start up @@ -1071,9 +1689,37 @@ type UpdateStatus struct { Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` } -func (m *UpdateStatus) Reset() { *m = UpdateStatus{} } -func (*UpdateStatus) ProtoMessage() {} -func (*UpdateStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{19} } +func (m *UpdateStatus) Reset() { *m = UpdateStatus{} } +func (*UpdateStatus) ProtoMessage() {} +func (*UpdateStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{19} +} +func (m *UpdateStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateStatus.Merge(m, src) +} +func (m *UpdateStatus) XXX_Size() int { + return m.Size() +} +func (m *UpdateStatus) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateStatus proto.InternalMessageInfo // Container specific status. type ContainerStatus struct { @@ -1082,23 +1728,79 @@ type ContainerStatus struct { ExitCode int32 `protobuf:"varint,3,opt,name=exit_code,json=exitCode,proto3" json:"exit_code,omitempty"` } -func (m *ContainerStatus) Reset() { *m = ContainerStatus{} } -func (*ContainerStatus) ProtoMessage() {} -func (*ContainerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{20} } +func (m *ContainerStatus) Reset() { *m = ContainerStatus{} } +func (*ContainerStatus) ProtoMessage() {} +func (*ContainerStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{20} +} +func (m *ContainerStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerStatus.Merge(m, src) +} +func (m *ContainerStatus) XXX_Size() int { + return m.Size() +} +func (m *ContainerStatus) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_ContainerStatus proto.InternalMessageInfo // PortStatus specifies the actual allocated runtime state of a list // of port configs. type PortStatus struct { - Ports []*PortConfig `protobuf:"bytes,1,rep,name=ports" json:"ports,omitempty"` + Ports []*PortConfig `protobuf:"bytes,1,rep,name=ports,proto3" json:"ports,omitempty"` +} + +func (m *PortStatus) Reset() { *m = PortStatus{} } +func (*PortStatus) ProtoMessage() {} +func (*PortStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{21} +} +func (m *PortStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PortStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PortStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PortStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_PortStatus.Merge(m, src) +} +func (m *PortStatus) XXX_Size() int { + return m.Size() +} +func (m *PortStatus) XXX_DiscardUnknown() { + xxx_messageInfo_PortStatus.DiscardUnknown(m) } -func (m *PortStatus) Reset() { *m = PortStatus{} } -func (*PortStatus) ProtoMessage() {} -func (*PortStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{21} } +var xxx_messageInfo_PortStatus proto.InternalMessageInfo type TaskStatus struct { // Note: can't use stdtime because this field is nullable. - Timestamp *google_protobuf.Timestamp `protobuf:"bytes,1,opt,name=timestamp" json:"timestamp,omitempty"` + Timestamp *types.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // State expresses the current state of the task. State TaskState `protobuf:"varint,2,opt,name=state,proto3,enum=docker.swarmkit.v1.TaskState" json:"state,omitempty"` // Message reports a message for the task status. This should provide a @@ -1117,7 +1819,7 @@ type TaskStatus struct { // // The following states should report a companion error: // - // FAILED, REJECTED + // FAILED, REJECTED // // In general, messages that should be surfaced to users belong in the // Err field, and notes on routine state transitions belong in Message. @@ -1131,19 +1833,47 @@ type TaskStatus struct { RuntimeStatus isTaskStatus_RuntimeStatus `protobuf_oneof:"runtime_status"` // HostPorts provides a list of ports allocated at the host // level. - PortStatus *PortStatus `protobuf:"bytes,6,opt,name=port_status,json=portStatus" json:"port_status,omitempty"` + PortStatus *PortStatus `protobuf:"bytes,6,opt,name=port_status,json=portStatus,proto3" json:"port_status,omitempty"` // AppliedBy gives the node ID of the manager that applied this task // status update to the Task object. AppliedBy string `protobuf:"bytes,7,opt,name=applied_by,json=appliedBy,proto3" json:"applied_by,omitempty"` // AppliedAt gives a timestamp of when this status update was applied to // the Task object. // Note: can't use stdtime because this field is nullable. - AppliedAt *google_protobuf.Timestamp `protobuf:"bytes,8,opt,name=applied_at,json=appliedAt" json:"applied_at,omitempty"` + AppliedAt *types.Timestamp `protobuf:"bytes,8,opt,name=applied_at,json=appliedAt,proto3" json:"applied_at,omitempty"` +} + +func (m *TaskStatus) Reset() { *m = TaskStatus{} } +func (*TaskStatus) ProtoMessage() {} +func (*TaskStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{22} +} +func (m *TaskStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskStatus.Merge(m, src) +} +func (m *TaskStatus) XXX_Size() int { + return m.Size() +} +func (m *TaskStatus) XXX_DiscardUnknown() { + xxx_messageInfo_TaskStatus.DiscardUnknown(m) } -func (m *TaskStatus) Reset() { *m = TaskStatus{} } -func (*TaskStatus) ProtoMessage() {} -func (*TaskStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{22} } +var xxx_messageInfo_TaskStatus proto.InternalMessageInfo type isTaskStatus_RuntimeStatus interface { isTaskStatus_RuntimeStatus() @@ -1152,7 +1882,7 @@ type isTaskStatus_RuntimeStatus interface { } type TaskStatus_Container struct { - Container *ContainerStatus `protobuf:"bytes,5,opt,name=container,oneof"` + Container *ContainerStatus `protobuf:"bytes,5,opt,name=container,proto3,oneof" json:"container,omitempty"` } func (*TaskStatus_Container) isTaskStatus_RuntimeStatus() {} @@ -1171,61 +1901,13 @@ func (m *TaskStatus) GetContainer() *ContainerStatus { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*TaskStatus) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _TaskStatus_OneofMarshaler, _TaskStatus_OneofUnmarshaler, _TaskStatus_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*TaskStatus) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*TaskStatus_Container)(nil), } } -func _TaskStatus_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*TaskStatus) - // runtime_status - switch x := m.RuntimeStatus.(type) { - case *TaskStatus_Container: - _ = b.EncodeVarint(5<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Container); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("TaskStatus.RuntimeStatus has unexpected type %T", x) - } - return nil -} - -func _TaskStatus_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*TaskStatus) - switch tag { - case 5: // runtime_status.container - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(ContainerStatus) - err := b.DecodeMessage(msg) - m.RuntimeStatus = &TaskStatus_Container{msg} - return true, err - default: - return false, nil - } -} - -func _TaskStatus_OneofSizer(msg proto.Message) (n int) { - m := msg.(*TaskStatus) - // runtime_status - switch x := m.RuntimeStatus.(type) { - case *TaskStatus_Container: - s := proto.Size(x.Container) - n += proto.SizeVarint(5<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - // NetworkAttachmentConfig specifies how a service should be attached to a particular network. // // For now, this is a simple struct, but this can include future information @@ -1236,18 +1918,46 @@ type NetworkAttachmentConfig struct { // network ID. Target string `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"` // Aliases specifies a list of discoverable alternate names for the service on this Target. - Aliases []string `protobuf:"bytes,2,rep,name=aliases" json:"aliases,omitempty"` + Aliases []string `protobuf:"bytes,2,rep,name=aliases,proto3" json:"aliases,omitempty"` // Addresses specifies a list of ipv4 and ipv6 addresses // preferred. If these addresses are not available then the // attachment might fail. - Addresses []string `protobuf:"bytes,3,rep,name=addresses" json:"addresses,omitempty"` + Addresses []string `protobuf:"bytes,3,rep,name=addresses,proto3" json:"addresses,omitempty"` // DriverAttachmentOpts is a map of driver attachment options for the network target - DriverAttachmentOpts map[string]string `protobuf:"bytes,4,rep,name=driver_attachment_opts,json=driverAttachmentOpts" json:"driver_attachment_opts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + DriverAttachmentOpts map[string]string `protobuf:"bytes,4,rep,name=driver_attachment_opts,json=driverAttachmentOpts,proto3" json:"driver_attachment_opts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *NetworkAttachmentConfig) Reset() { *m = NetworkAttachmentConfig{} } +func (*NetworkAttachmentConfig) ProtoMessage() {} +func (*NetworkAttachmentConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{23} +} +func (m *NetworkAttachmentConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NetworkAttachmentConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NetworkAttachmentConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NetworkAttachmentConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_NetworkAttachmentConfig.Merge(m, src) +} +func (m *NetworkAttachmentConfig) XXX_Size() int { + return m.Size() +} +func (m *NetworkAttachmentConfig) XXX_DiscardUnknown() { + xxx_messageInfo_NetworkAttachmentConfig.DiscardUnknown(m) } -func (m *NetworkAttachmentConfig) Reset() { *m = NetworkAttachmentConfig{} } -func (*NetworkAttachmentConfig) ProtoMessage() {} -func (*NetworkAttachmentConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{23} } +var xxx_messageInfo_NetworkAttachmentConfig proto.InternalMessageInfo // IPAMConfig specifies parameters for IP Address Management. type IPAMConfig struct { @@ -1263,12 +1973,40 @@ type IPAMConfig struct { // Reserved is a list of address from the master pool that should *not* be // allocated. These addresses may have already been allocated or may be // reserved for another allocation manager. - Reserved map[string]string `protobuf:"bytes,5,rep,name=reserved" json:"reserved,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Reserved map[string]string `protobuf:"bytes,5,rep,name=reserved,proto3" json:"reserved,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *IPAMConfig) Reset() { *m = IPAMConfig{} } +func (*IPAMConfig) ProtoMessage() {} +func (*IPAMConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{24} +} +func (m *IPAMConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IPAMConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IPAMConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *IPAMConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_IPAMConfig.Merge(m, src) +} +func (m *IPAMConfig) XXX_Size() int { + return m.Size() +} +func (m *IPAMConfig) XXX_DiscardUnknown() { + xxx_messageInfo_IPAMConfig.DiscardUnknown(m) } -func (m *IPAMConfig) Reset() { *m = IPAMConfig{} } -func (*IPAMConfig) ProtoMessage() {} -func (*IPAMConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{24} } +var xxx_messageInfo_IPAMConfig proto.InternalMessageInfo // PortConfig specifies an exposed port which can be // addressed using the given name. This can be later queried @@ -1292,9 +2030,37 @@ type PortConfig struct { PublishMode PortConfig_PublishMode `protobuf:"varint,5,opt,name=publish_mode,json=publishMode,proto3,enum=docker.swarmkit.v1.PortConfig_PublishMode" json:"publish_mode,omitempty"` } -func (m *PortConfig) Reset() { *m = PortConfig{} } -func (*PortConfig) ProtoMessage() {} -func (*PortConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{25} } +func (m *PortConfig) Reset() { *m = PortConfig{} } +func (*PortConfig) ProtoMessage() {} +func (*PortConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{25} +} +func (m *PortConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PortConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PortConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PortConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_PortConfig.Merge(m, src) +} +func (m *PortConfig) XXX_Size() int { + return m.Size() +} +func (m *PortConfig) XXX_DiscardUnknown() { + xxx_messageInfo_PortConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_PortConfig proto.InternalMessageInfo // Driver is a generic driver type to be used throughout the API. For now, a // driver is simply a name and set of options. The field contents depend on the @@ -1302,21 +2068,77 @@ func (*PortConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, [] // have different rules than a volume driver. type Driver struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Options map[string]string `protobuf:"bytes,2,rep,name=options" json:"options,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Options map[string]string `protobuf:"bytes,2,rep,name=options,proto3" json:"options,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (m *Driver) Reset() { *m = Driver{} } +func (*Driver) ProtoMessage() {} +func (*Driver) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{26} +} +func (m *Driver) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Driver) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Driver.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Driver) XXX_Merge(src proto.Message) { + xxx_messageInfo_Driver.Merge(m, src) +} +func (m *Driver) XXX_Size() int { + return m.Size() +} +func (m *Driver) XXX_DiscardUnknown() { + xxx_messageInfo_Driver.DiscardUnknown(m) } -func (m *Driver) Reset() { *m = Driver{} } -func (*Driver) ProtoMessage() {} -func (*Driver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{26} } +var xxx_messageInfo_Driver proto.InternalMessageInfo type IPAMOptions struct { - Driver *Driver `protobuf:"bytes,1,opt,name=driver" json:"driver,omitempty"` - Configs []*IPAMConfig `protobuf:"bytes,3,rep,name=configs" json:"configs,omitempty"` + Driver *Driver `protobuf:"bytes,1,opt,name=driver,proto3" json:"driver,omitempty"` + Configs []*IPAMConfig `protobuf:"bytes,3,rep,name=configs,proto3" json:"configs,omitempty"` +} + +func (m *IPAMOptions) Reset() { *m = IPAMOptions{} } +func (*IPAMOptions) ProtoMessage() {} +func (*IPAMOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{27} +} +func (m *IPAMOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IPAMOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IPAMOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *IPAMOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_IPAMOptions.Merge(m, src) +} +func (m *IPAMOptions) XXX_Size() int { + return m.Size() +} +func (m *IPAMOptions) XXX_DiscardUnknown() { + xxx_messageInfo_IPAMOptions.DiscardUnknown(m) } -func (m *IPAMOptions) Reset() { *m = IPAMOptions{} } -func (*IPAMOptions) ProtoMessage() {} -func (*IPAMOptions) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{27} } +var xxx_messageInfo_IPAMOptions proto.InternalMessageInfo // Peer should be used anywhere where we are describing a remote peer. type Peer struct { @@ -1324,40 +2146,152 @@ type Peer struct { Addr string `protobuf:"bytes,2,opt,name=addr,proto3" json:"addr,omitempty"` } -func (m *Peer) Reset() { *m = Peer{} } -func (*Peer) ProtoMessage() {} -func (*Peer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{28} } +func (m *Peer) Reset() { *m = Peer{} } +func (*Peer) ProtoMessage() {} +func (*Peer) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{28} +} +func (m *Peer) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Peer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Peer.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Peer) XXX_Merge(src proto.Message) { + xxx_messageInfo_Peer.Merge(m, src) +} +func (m *Peer) XXX_Size() int { + return m.Size() +} +func (m *Peer) XXX_DiscardUnknown() { + xxx_messageInfo_Peer.DiscardUnknown(m) +} + +var xxx_messageInfo_Peer proto.InternalMessageInfo // WeightedPeer should be used anywhere where we are describing a remote peer // with a weight. type WeightedPeer struct { - Peer *Peer `protobuf:"bytes,1,opt,name=peer" json:"peer,omitempty"` + Peer *Peer `protobuf:"bytes,1,opt,name=peer,proto3" json:"peer,omitempty"` Weight int64 `protobuf:"varint,2,opt,name=weight,proto3" json:"weight,omitempty"` } -func (m *WeightedPeer) Reset() { *m = WeightedPeer{} } -func (*WeightedPeer) ProtoMessage() {} -func (*WeightedPeer) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{29} } +func (m *WeightedPeer) Reset() { *m = WeightedPeer{} } +func (*WeightedPeer) ProtoMessage() {} +func (*WeightedPeer) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{29} +} +func (m *WeightedPeer) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WeightedPeer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WeightedPeer.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WeightedPeer) XXX_Merge(src proto.Message) { + xxx_messageInfo_WeightedPeer.Merge(m, src) +} +func (m *WeightedPeer) XXX_Size() int { + return m.Size() +} +func (m *WeightedPeer) XXX_DiscardUnknown() { + xxx_messageInfo_WeightedPeer.DiscardUnknown(m) +} + +var xxx_messageInfo_WeightedPeer proto.InternalMessageInfo type IssuanceStatus struct { State IssuanceStatus_State `protobuf:"varint,1,opt,name=state,proto3,enum=docker.swarmkit.v1.IssuanceStatus_State" json:"state,omitempty"` // Err is set if the Certificate Issuance is in an error state. // The following states should report a companion error: - // FAILED + // FAILED Err string `protobuf:"bytes,2,opt,name=err,proto3" json:"err,omitempty"` } -func (m *IssuanceStatus) Reset() { *m = IssuanceStatus{} } -func (*IssuanceStatus) ProtoMessage() {} -func (*IssuanceStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{30} } +func (m *IssuanceStatus) Reset() { *m = IssuanceStatus{} } +func (*IssuanceStatus) ProtoMessage() {} +func (*IssuanceStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{30} +} +func (m *IssuanceStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *IssuanceStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_IssuanceStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *IssuanceStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_IssuanceStatus.Merge(m, src) +} +func (m *IssuanceStatus) XXX_Size() int { + return m.Size() +} +func (m *IssuanceStatus) XXX_DiscardUnknown() { + xxx_messageInfo_IssuanceStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_IssuanceStatus proto.InternalMessageInfo type AcceptancePolicy struct { - Policies []*AcceptancePolicy_RoleAdmissionPolicy `protobuf:"bytes,1,rep,name=policies" json:"policies,omitempty"` + Policies []*AcceptancePolicy_RoleAdmissionPolicy `protobuf:"bytes,1,rep,name=policies,proto3" json:"policies,omitempty"` +} + +func (m *AcceptancePolicy) Reset() { *m = AcceptancePolicy{} } +func (*AcceptancePolicy) ProtoMessage() {} +func (*AcceptancePolicy) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{31} +} +func (m *AcceptancePolicy) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AcceptancePolicy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AcceptancePolicy.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AcceptancePolicy) XXX_Merge(src proto.Message) { + xxx_messageInfo_AcceptancePolicy.Merge(m, src) +} +func (m *AcceptancePolicy) XXX_Size() int { + return m.Size() +} +func (m *AcceptancePolicy) XXX_DiscardUnknown() { + xxx_messageInfo_AcceptancePolicy.DiscardUnknown(m) } -func (m *AcceptancePolicy) Reset() { *m = AcceptancePolicy{} } -func (*AcceptancePolicy) ProtoMessage() {} -func (*AcceptancePolicy) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{31} } +var xxx_messageInfo_AcceptancePolicy proto.InternalMessageInfo type AcceptancePolicy_RoleAdmissionPolicy struct { Role NodeRole `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"` @@ -1366,14 +2300,40 @@ type AcceptancePolicy_RoleAdmissionPolicy struct { Autoaccept bool `protobuf:"varint,2,opt,name=autoaccept,proto3" json:"autoaccept,omitempty"` // Secret represents a user-provided string that is necessary for new // nodes to join the cluster - Secret *AcceptancePolicy_RoleAdmissionPolicy_Secret `protobuf:"bytes,3,opt,name=secret" json:"secret,omitempty"` + Secret *AcceptancePolicy_RoleAdmissionPolicy_Secret `protobuf:"bytes,3,opt,name=secret,proto3" json:"secret,omitempty"` } func (m *AcceptancePolicy_RoleAdmissionPolicy) Reset() { *m = AcceptancePolicy_RoleAdmissionPolicy{} } func (*AcceptancePolicy_RoleAdmissionPolicy) ProtoMessage() {} func (*AcceptancePolicy_RoleAdmissionPolicy) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{31, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{31, 0} +} +func (m *AcceptancePolicy_RoleAdmissionPolicy) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AcceptancePolicy_RoleAdmissionPolicy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AcceptancePolicy_RoleAdmissionPolicy.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AcceptancePolicy_RoleAdmissionPolicy) XXX_Merge(src proto.Message) { + xxx_messageInfo_AcceptancePolicy_RoleAdmissionPolicy.Merge(m, src) +} +func (m *AcceptancePolicy_RoleAdmissionPolicy) XXX_Size() int { + return m.Size() } +func (m *AcceptancePolicy_RoleAdmissionPolicy) XXX_DiscardUnknown() { + xxx_messageInfo_AcceptancePolicy_RoleAdmissionPolicy.DiscardUnknown(m) +} + +var xxx_messageInfo_AcceptancePolicy_RoleAdmissionPolicy proto.InternalMessageInfo type AcceptancePolicy_RoleAdmissionPolicy_Secret struct { // The actual content (possibly hashed) @@ -1387,9 +2347,35 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Reset() { } func (*AcceptancePolicy_RoleAdmissionPolicy_Secret) ProtoMessage() {} func (*AcceptancePolicy_RoleAdmissionPolicy_Secret) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{31, 0, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{31, 0, 0} +} +func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AcceptancePolicy_RoleAdmissionPolicy_Secret.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) XXX_Merge(src proto.Message) { + xxx_messageInfo_AcceptancePolicy_RoleAdmissionPolicy_Secret.Merge(m, src) +} +func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) XXX_Size() int { + return m.Size() +} +func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) XXX_DiscardUnknown() { + xxx_messageInfo_AcceptancePolicy_RoleAdmissionPolicy_Secret.DiscardUnknown(m) } +var xxx_messageInfo_AcceptancePolicy_RoleAdmissionPolicy_Secret proto.InternalMessageInfo + type ExternalCA struct { // Protocol is the protocol used by this external CA. Protocol ExternalCA_CAProtocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=docker.swarmkit.v1.ExternalCA_CAProtocol" json:"protocol,omitempty"` @@ -1397,22 +2383,50 @@ type ExternalCA struct { URL string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` // Options is a set of additional key/value pairs whose interpretation // depends on the specified CA type. - Options map[string]string `protobuf:"bytes,3,rep,name=options" json:"options,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Options map[string]string `protobuf:"bytes,3,rep,name=options,proto3" json:"options,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // CACert specifies which root CA is used by this external CA CACert []byte `protobuf:"bytes,4,opt,name=ca_cert,json=caCert,proto3" json:"ca_cert,omitempty"` } -func (m *ExternalCA) Reset() { *m = ExternalCA{} } -func (*ExternalCA) ProtoMessage() {} -func (*ExternalCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{32} } +func (m *ExternalCA) Reset() { *m = ExternalCA{} } +func (*ExternalCA) ProtoMessage() {} +func (*ExternalCA) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{32} +} +func (m *ExternalCA) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ExternalCA) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ExternalCA.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ExternalCA) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExternalCA.Merge(m, src) +} +func (m *ExternalCA) XXX_Size() int { + return m.Size() +} +func (m *ExternalCA) XXX_DiscardUnknown() { + xxx_messageInfo_ExternalCA.DiscardUnknown(m) +} + +var xxx_messageInfo_ExternalCA proto.InternalMessageInfo type CAConfig struct { // NodeCertExpiry is the duration certificates should be issued for // Note: can't use stdduration because this field needs to be nullable. - NodeCertExpiry *google_protobuf1.Duration `protobuf:"bytes,1,opt,name=node_cert_expiry,json=nodeCertExpiry" json:"node_cert_expiry,omitempty"` + NodeCertExpiry *types.Duration `protobuf:"bytes,1,opt,name=node_cert_expiry,json=nodeCertExpiry,proto3" json:"node_cert_expiry,omitempty"` // ExternalCAs is a list of CAs to which a manager node will make // certificate signing requests for node certificates. - ExternalCAs []*ExternalCA `protobuf:"bytes,2,rep,name=external_cas,json=externalCas" json:"external_cas,omitempty"` + ExternalCAs []*ExternalCA `protobuf:"bytes,2,rep,name=external_cas,json=externalCas,proto3" json:"external_cas,omitempty"` // SigningCACert is the desired CA certificate to be used as the root and // signing CA for the swarm. If not provided, indicates that we are either happy // with the current configuration, or (together with a bump in the ForceRotate value) @@ -1427,9 +2441,37 @@ type CAConfig struct { ForceRotate uint64 `protobuf:"varint,5,opt,name=force_rotate,json=forceRotate,proto3" json:"force_rotate,omitempty"` } -func (m *CAConfig) Reset() { *m = CAConfig{} } -func (*CAConfig) ProtoMessage() {} -func (*CAConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{33} } +func (m *CAConfig) Reset() { *m = CAConfig{} } +func (*CAConfig) ProtoMessage() {} +func (*CAConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{33} +} +func (m *CAConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CAConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CAConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CAConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_CAConfig.Merge(m, src) +} +func (m *CAConfig) XXX_Size() int { + return m.Size() +} +func (m *CAConfig) XXX_DiscardUnknown() { + xxx_messageInfo_CAConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_CAConfig proto.InternalMessageInfo // OrchestrationConfig defines cluster-level orchestration settings. type OrchestrationConfig struct { @@ -1438,9 +2480,37 @@ type OrchestrationConfig struct { TaskHistoryRetentionLimit int64 `protobuf:"varint,1,opt,name=task_history_retention_limit,json=taskHistoryRetentionLimit,proto3" json:"task_history_retention_limit,omitempty"` } -func (m *OrchestrationConfig) Reset() { *m = OrchestrationConfig{} } -func (*OrchestrationConfig) ProtoMessage() {} -func (*OrchestrationConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{34} } +func (m *OrchestrationConfig) Reset() { *m = OrchestrationConfig{} } +func (*OrchestrationConfig) ProtoMessage() {} +func (*OrchestrationConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{34} +} +func (m *OrchestrationConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *OrchestrationConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_OrchestrationConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *OrchestrationConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_OrchestrationConfig.Merge(m, src) +} +func (m *OrchestrationConfig) XXX_Size() int { + return m.Size() +} +func (m *OrchestrationConfig) XXX_DiscardUnknown() { + xxx_messageInfo_OrchestrationConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_OrchestrationConfig proto.InternalMessageInfo // TaskDefaults specifies default values for task creation. type TaskDefaults struct { @@ -1449,24 +2519,80 @@ type TaskDefaults struct { // // If this is changed, only new tasks will pick up the new log driver. // Existing tasks will continue to use the previous default until rescheduled. - LogDriver *Driver `protobuf:"bytes,1,opt,name=log_driver,json=logDriver" json:"log_driver,omitempty"` + LogDriver *Driver `protobuf:"bytes,1,opt,name=log_driver,json=logDriver,proto3" json:"log_driver,omitempty"` +} + +func (m *TaskDefaults) Reset() { *m = TaskDefaults{} } +func (*TaskDefaults) ProtoMessage() {} +func (*TaskDefaults) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{35} +} +func (m *TaskDefaults) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TaskDefaults) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TaskDefaults.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TaskDefaults) XXX_Merge(src proto.Message) { + xxx_messageInfo_TaskDefaults.Merge(m, src) +} +func (m *TaskDefaults) XXX_Size() int { + return m.Size() +} +func (m *TaskDefaults) XXX_DiscardUnknown() { + xxx_messageInfo_TaskDefaults.DiscardUnknown(m) } -func (m *TaskDefaults) Reset() { *m = TaskDefaults{} } -func (*TaskDefaults) ProtoMessage() {} -func (*TaskDefaults) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{35} } +var xxx_messageInfo_TaskDefaults proto.InternalMessageInfo // DispatcherConfig defines cluster-level dispatcher settings. type DispatcherConfig struct { // HeartbeatPeriod defines how often agent should send heartbeats to // dispatcher. // Note: can't use stdduration because this field needs to be nullable. - HeartbeatPeriod *google_protobuf1.Duration `protobuf:"bytes,1,opt,name=heartbeat_period,json=heartbeatPeriod" json:"heartbeat_period,omitempty"` + HeartbeatPeriod *types.Duration `protobuf:"bytes,1,opt,name=heartbeat_period,json=heartbeatPeriod,proto3" json:"heartbeat_period,omitempty"` +} + +func (m *DispatcherConfig) Reset() { *m = DispatcherConfig{} } +func (*DispatcherConfig) ProtoMessage() {} +func (*DispatcherConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{36} +} +func (m *DispatcherConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DispatcherConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DispatcherConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DispatcherConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_DispatcherConfig.Merge(m, src) +} +func (m *DispatcherConfig) XXX_Size() int { + return m.Size() +} +func (m *DispatcherConfig) XXX_DiscardUnknown() { + xxx_messageInfo_DispatcherConfig.DiscardUnknown(m) } -func (m *DispatcherConfig) Reset() { *m = DispatcherConfig{} } -func (*DispatcherConfig) ProtoMessage() {} -func (*DispatcherConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{36} } +var xxx_messageInfo_DispatcherConfig proto.InternalMessageInfo // RaftConfig defines raft settings for the cluster. type RaftConfig struct { @@ -1486,28 +2612,112 @@ type RaftConfig struct { ElectionTick uint32 `protobuf:"varint,5,opt,name=election_tick,json=electionTick,proto3" json:"election_tick,omitempty"` } -func (m *RaftConfig) Reset() { *m = RaftConfig{} } -func (*RaftConfig) ProtoMessage() {} -func (*RaftConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{37} } - -type EncryptionConfig struct { - // AutoLockManagers specifies whether or not managers TLS keys and raft data - // should be encrypted at rest in such a way that they must be unlocked - // before the manager node starts up again. - AutoLockManagers bool `protobuf:"varint,1,opt,name=auto_lock_managers,json=autoLockManagers,proto3" json:"auto_lock_managers,omitempty"` +func (m *RaftConfig) Reset() { *m = RaftConfig{} } +func (*RaftConfig) ProtoMessage() {} +func (*RaftConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{37} +} +func (m *RaftConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RaftConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RaftConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RaftConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_RaftConfig.Merge(m, src) +} +func (m *RaftConfig) XXX_Size() int { + return m.Size() +} +func (m *RaftConfig) XXX_DiscardUnknown() { + xxx_messageInfo_RaftConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_RaftConfig proto.InternalMessageInfo + +type EncryptionConfig struct { + // AutoLockManagers specifies whether or not managers TLS keys and raft data + // should be encrypted at rest in such a way that they must be unlocked + // before the manager node starts up again. + AutoLockManagers bool `protobuf:"varint,1,opt,name=auto_lock_managers,json=autoLockManagers,proto3" json:"auto_lock_managers,omitempty"` } -func (m *EncryptionConfig) Reset() { *m = EncryptionConfig{} } -func (*EncryptionConfig) ProtoMessage() {} -func (*EncryptionConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{38} } - +func (m *EncryptionConfig) Reset() { *m = EncryptionConfig{} } +func (*EncryptionConfig) ProtoMessage() {} +func (*EncryptionConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{38} +} +func (m *EncryptionConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EncryptionConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EncryptionConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EncryptionConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_EncryptionConfig.Merge(m, src) +} +func (m *EncryptionConfig) XXX_Size() int { + return m.Size() +} +func (m *EncryptionConfig) XXX_DiscardUnknown() { + xxx_messageInfo_EncryptionConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_EncryptionConfig proto.InternalMessageInfo + type SpreadOver struct { SpreadDescriptor string `protobuf:"bytes,1,opt,name=spread_descriptor,json=spreadDescriptor,proto3" json:"spread_descriptor,omitempty"` } -func (m *SpreadOver) Reset() { *m = SpreadOver{} } -func (*SpreadOver) ProtoMessage() {} -func (*SpreadOver) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{39} } +func (m *SpreadOver) Reset() { *m = SpreadOver{} } +func (*SpreadOver) ProtoMessage() {} +func (*SpreadOver) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{39} +} +func (m *SpreadOver) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SpreadOver) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SpreadOver.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SpreadOver) XXX_Merge(src proto.Message) { + xxx_messageInfo_SpreadOver.Merge(m, src) +} +func (m *SpreadOver) XXX_Size() int { + return m.Size() +} +func (m *SpreadOver) XXX_DiscardUnknown() { + xxx_messageInfo_SpreadOver.DiscardUnknown(m) +} + +var xxx_messageInfo_SpreadOver proto.InternalMessageInfo type PlacementPreference struct { // Types that are valid to be assigned to Preference: @@ -1515,9 +2725,37 @@ type PlacementPreference struct { Preference isPlacementPreference_Preference `protobuf_oneof:"Preference"` } -func (m *PlacementPreference) Reset() { *m = PlacementPreference{} } -func (*PlacementPreference) ProtoMessage() {} -func (*PlacementPreference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{40} } +func (m *PlacementPreference) Reset() { *m = PlacementPreference{} } +func (*PlacementPreference) ProtoMessage() {} +func (*PlacementPreference) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{40} +} +func (m *PlacementPreference) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PlacementPreference) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PlacementPreference.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PlacementPreference) XXX_Merge(src proto.Message) { + xxx_messageInfo_PlacementPreference.Merge(m, src) +} +func (m *PlacementPreference) XXX_Size() int { + return m.Size() +} +func (m *PlacementPreference) XXX_DiscardUnknown() { + xxx_messageInfo_PlacementPreference.DiscardUnknown(m) +} + +var xxx_messageInfo_PlacementPreference proto.InternalMessageInfo type isPlacementPreference_Preference interface { isPlacementPreference_Preference() @@ -1526,7 +2764,7 @@ type isPlacementPreference_Preference interface { } type PlacementPreference_Spread struct { - Spread *SpreadOver `protobuf:"bytes,1,opt,name=spread,oneof"` + Spread *SpreadOver `protobuf:"bytes,1,opt,name=spread,proto3,oneof" json:"spread,omitempty"` } func (*PlacementPreference_Spread) isPlacementPreference_Preference() {} @@ -1545,78 +2783,60 @@ func (m *PlacementPreference) GetSpread() *SpreadOver { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*PlacementPreference) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _PlacementPreference_OneofMarshaler, _PlacementPreference_OneofUnmarshaler, _PlacementPreference_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*PlacementPreference) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*PlacementPreference_Spread)(nil), } } -func _PlacementPreference_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*PlacementPreference) - // Preference - switch x := m.Preference.(type) { - case *PlacementPreference_Spread: - _ = b.EncodeVarint(1<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Spread); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("PlacementPreference.Preference has unexpected type %T", x) - } - return nil -} - -func _PlacementPreference_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*PlacementPreference) - switch tag { - case 1: // Preference.spread - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(SpreadOver) - err := b.DecodeMessage(msg) - m.Preference = &PlacementPreference_Spread{msg} - return true, err - default: - return false, nil - } -} - -func _PlacementPreference_OneofSizer(msg proto.Message) (n int) { - m := msg.(*PlacementPreference) - // Preference - switch x := m.Preference.(type) { - case *PlacementPreference_Spread: - s := proto.Size(x.Spread) - n += proto.SizeVarint(1<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - // Placement specifies task distribution constraints. type Placement struct { // Constraints specifies a set of requirements a node should meet for a task. - Constraints []string `protobuf:"bytes,1,rep,name=constraints" json:"constraints,omitempty"` + Constraints []string `protobuf:"bytes,1,rep,name=constraints,proto3" json:"constraints,omitempty"` // Preferences provide a way to make the scheduler aware of factors // such as topology. They are provided in order from highest to lowest // precedence. - Preferences []*PlacementPreference `protobuf:"bytes,2,rep,name=preferences" json:"preferences,omitempty"` + Preferences []*PlacementPreference `protobuf:"bytes,2,rep,name=preferences,proto3" json:"preferences,omitempty"` // Platforms stores all the platforms that the image can run on. // This field is used in the platform filter for scheduling. If empty, // then the platform filter is off, meaning there are no scheduling restrictions. - Platforms []*Platform `protobuf:"bytes,3,rep,name=platforms" json:"platforms,omitempty"` + Platforms []*Platform `protobuf:"bytes,3,rep,name=platforms,proto3" json:"platforms,omitempty"` + // MaxReplicas specifies the limit for maximum number of replicas running on one node. + MaxReplicas uint64 `protobuf:"varint,4,opt,name=max_replicas,json=maxReplicas,proto3" json:"max_replicas,omitempty"` +} + +func (m *Placement) Reset() { *m = Placement{} } +func (*Placement) ProtoMessage() {} +func (*Placement) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{41} +} +func (m *Placement) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Placement) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Placement.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Placement) XXX_Merge(src proto.Message) { + xxx_messageInfo_Placement.Merge(m, src) +} +func (m *Placement) XXX_Size() int { + return m.Size() +} +func (m *Placement) XXX_DiscardUnknown() { + xxx_messageInfo_Placement.DiscardUnknown(m) } -func (m *Placement) Reset() { *m = Placement{} } -func (*Placement) ProtoMessage() {} -func (*Placement) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{41} } +var xxx_messageInfo_Placement proto.InternalMessageInfo // JoinToken contains the join tokens for workers and managers. type JoinTokens struct { @@ -1626,9 +2846,37 @@ type JoinTokens struct { Manager string `protobuf:"bytes,2,opt,name=manager,proto3" json:"manager,omitempty"` } -func (m *JoinTokens) Reset() { *m = JoinTokens{} } -func (*JoinTokens) ProtoMessage() {} -func (*JoinTokens) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{42} } +func (m *JoinTokens) Reset() { *m = JoinTokens{} } +func (*JoinTokens) ProtoMessage() {} +func (*JoinTokens) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{42} +} +func (m *JoinTokens) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *JoinTokens) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_JoinTokens.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *JoinTokens) XXX_Merge(src proto.Message) { + xxx_messageInfo_JoinTokens.Merge(m, src) +} +func (m *JoinTokens) XXX_Size() int { + return m.Size() +} +func (m *JoinTokens) XXX_DiscardUnknown() { + xxx_messageInfo_JoinTokens.DiscardUnknown(m) +} + +var xxx_messageInfo_JoinTokens proto.InternalMessageInfo type RootCA struct { // CAKey is the root CA private key. @@ -1638,31 +2886,87 @@ type RootCA struct { // CACertHash is the digest of the CA Certificate. CACertHash string `protobuf:"bytes,3,opt,name=ca_cert_hash,json=caCertHash,proto3" json:"ca_cert_hash,omitempty"` // JoinTokens contains the join tokens for workers and managers. - JoinTokens JoinTokens `protobuf:"bytes,4,opt,name=join_tokens,json=joinTokens" json:"join_tokens"` + JoinTokens JoinTokens `protobuf:"bytes,4,opt,name=join_tokens,json=joinTokens,proto3" json:"join_tokens"` // RootRotation contains the new root cert and key we want to rotate to - if this is nil, we are not in the // middle of a root rotation - RootRotation *RootRotation `protobuf:"bytes,5,opt,name=root_rotation,json=rootRotation" json:"root_rotation,omitempty"` + RootRotation *RootRotation `protobuf:"bytes,5,opt,name=root_rotation,json=rootRotation,proto3" json:"root_rotation,omitempty"` // LastForcedRotation matches the Cluster Spec's CAConfig's ForceRotation counter. // It indicates when the current CA cert and key were generated (or updated). LastForcedRotation uint64 `protobuf:"varint,6,opt,name=last_forced_rotation,json=lastForcedRotation,proto3" json:"last_forced_rotation,omitempty"` } -func (m *RootCA) Reset() { *m = RootCA{} } -func (*RootCA) ProtoMessage() {} -func (*RootCA) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{43} } +func (m *RootCA) Reset() { *m = RootCA{} } +func (*RootCA) ProtoMessage() {} +func (*RootCA) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{43} +} +func (m *RootCA) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RootCA) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RootCA.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RootCA) XXX_Merge(src proto.Message) { + xxx_messageInfo_RootCA.Merge(m, src) +} +func (m *RootCA) XXX_Size() int { + return m.Size() +} +func (m *RootCA) XXX_DiscardUnknown() { + xxx_messageInfo_RootCA.DiscardUnknown(m) +} + +var xxx_messageInfo_RootCA proto.InternalMessageInfo type Certificate struct { Role NodeRole `protobuf:"varint,1,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole" json:"role,omitempty"` CSR []byte `protobuf:"bytes,2,opt,name=csr,proto3" json:"csr,omitempty"` - Status IssuanceStatus `protobuf:"bytes,3,opt,name=status" json:"status"` + Status IssuanceStatus `protobuf:"bytes,3,opt,name=status,proto3" json:"status"` Certificate []byte `protobuf:"bytes,4,opt,name=certificate,proto3" json:"certificate,omitempty"` // CN represents the node ID. CN string `protobuf:"bytes,5,opt,name=cn,proto3" json:"cn,omitempty"` } -func (m *Certificate) Reset() { *m = Certificate{} } -func (*Certificate) ProtoMessage() {} -func (*Certificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{44} } +func (m *Certificate) Reset() { *m = Certificate{} } +func (*Certificate) ProtoMessage() {} +func (*Certificate) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{44} +} +func (m *Certificate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Certificate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Certificate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Certificate) XXX_Merge(src proto.Message) { + xxx_messageInfo_Certificate.Merge(m, src) +} +func (m *Certificate) XXX_Size() int { + return m.Size() +} +func (m *Certificate) XXX_DiscardUnknown() { + xxx_messageInfo_Certificate.DiscardUnknown(m) +} + +var xxx_messageInfo_Certificate proto.InternalMessageInfo // Symmetric keys to encrypt inter-agent communication. type EncryptionKey struct { @@ -1676,9 +2980,37 @@ type EncryptionKey struct { LamportTime uint64 `protobuf:"varint,4,opt,name=lamport_time,json=lamportTime,proto3" json:"lamport_time,omitempty"` } -func (m *EncryptionKey) Reset() { *m = EncryptionKey{} } -func (*EncryptionKey) ProtoMessage() {} -func (*EncryptionKey) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{45} } +func (m *EncryptionKey) Reset() { *m = EncryptionKey{} } +func (*EncryptionKey) ProtoMessage() {} +func (*EncryptionKey) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{45} +} +func (m *EncryptionKey) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EncryptionKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EncryptionKey.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EncryptionKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_EncryptionKey.Merge(m, src) +} +func (m *EncryptionKey) XXX_Size() int { + return m.Size() +} +func (m *EncryptionKey) XXX_DiscardUnknown() { + xxx_messageInfo_EncryptionKey.DiscardUnknown(m) +} + +var xxx_messageInfo_EncryptionKey proto.InternalMessageInfo // ManagerStatus provides informations about the state of a manager in the cluster. type ManagerStatus struct { @@ -1693,9 +3025,37 @@ type ManagerStatus struct { Reachability RaftMemberStatus_Reachability `protobuf:"varint,4,opt,name=reachability,proto3,enum=docker.swarmkit.v1.RaftMemberStatus_Reachability" json:"reachability,omitempty"` } -func (m *ManagerStatus) Reset() { *m = ManagerStatus{} } -func (*ManagerStatus) ProtoMessage() {} -func (*ManagerStatus) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{46} } +func (m *ManagerStatus) Reset() { *m = ManagerStatus{} } +func (*ManagerStatus) ProtoMessage() {} +func (*ManagerStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{46} +} +func (m *ManagerStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ManagerStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ManagerStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ManagerStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_ManagerStatus.Merge(m, src) +} +func (m *ManagerStatus) XXX_Size() int { + return m.Size() +} +func (m *ManagerStatus) XXX_DiscardUnknown() { + xxx_messageInfo_ManagerStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_ManagerStatus proto.InternalMessageInfo // FileTarget represents a specific target that is backed by a file type FileTarget struct { @@ -1709,9 +3069,76 @@ type FileTarget struct { Mode os.FileMode `protobuf:"varint,4,opt,name=mode,proto3,customtype=os.FileMode" json:"mode"` } -func (m *FileTarget) Reset() { *m = FileTarget{} } -func (*FileTarget) ProtoMessage() {} -func (*FileTarget) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{47} } +func (m *FileTarget) Reset() { *m = FileTarget{} } +func (*FileTarget) ProtoMessage() {} +func (*FileTarget) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{47} +} +func (m *FileTarget) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FileTarget) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FileTarget.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *FileTarget) XXX_Merge(src proto.Message) { + xxx_messageInfo_FileTarget.Merge(m, src) +} +func (m *FileTarget) XXX_Size() int { + return m.Size() +} +func (m *FileTarget) XXX_DiscardUnknown() { + xxx_messageInfo_FileTarget.DiscardUnknown(m) +} + +var xxx_messageInfo_FileTarget proto.InternalMessageInfo + +// RuntimeTarget represents that this secret is _not_ mounted into the +// container, but is used for some other purpose by the container runtime. +// +// Currently, RuntimeTarget has no fields; it's just a placeholder. +type RuntimeTarget struct { +} + +func (m *RuntimeTarget) Reset() { *m = RuntimeTarget{} } +func (*RuntimeTarget) ProtoMessage() {} +func (*RuntimeTarget) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{48} +} +func (m *RuntimeTarget) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RuntimeTarget) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RuntimeTarget.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RuntimeTarget) XXX_Merge(src proto.Message) { + xxx_messageInfo_RuntimeTarget.Merge(m, src) +} +func (m *RuntimeTarget) XXX_Size() int { + return m.Size() +} +func (m *RuntimeTarget) XXX_DiscardUnknown() { + xxx_messageInfo_RuntimeTarget.DiscardUnknown(m) +} + +var xxx_messageInfo_RuntimeTarget proto.InternalMessageInfo // SecretReference is the linkage between a service and a secret that it uses. type SecretReference struct { @@ -1729,9 +3156,37 @@ type SecretReference struct { Target isSecretReference_Target `protobuf_oneof:"target"` } -func (m *SecretReference) Reset() { *m = SecretReference{} } -func (*SecretReference) ProtoMessage() {} -func (*SecretReference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{48} } +func (m *SecretReference) Reset() { *m = SecretReference{} } +func (*SecretReference) ProtoMessage() {} +func (*SecretReference) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{49} +} +func (m *SecretReference) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SecretReference) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SecretReference.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SecretReference) XXX_Merge(src proto.Message) { + xxx_messageInfo_SecretReference.Merge(m, src) +} +func (m *SecretReference) XXX_Size() int { + return m.Size() +} +func (m *SecretReference) XXX_DiscardUnknown() { + xxx_messageInfo_SecretReference.DiscardUnknown(m) +} + +var xxx_messageInfo_SecretReference proto.InternalMessageInfo type isSecretReference_Target interface { isSecretReference_Target() @@ -1740,7 +3195,7 @@ type isSecretReference_Target interface { } type SecretReference_File struct { - File *FileTarget `protobuf:"bytes,3,opt,name=file,oneof"` + File *FileTarget `protobuf:"bytes,3,opt,name=file,proto3,oneof" json:"file,omitempty"` } func (*SecretReference_File) isSecretReference_Target() {} @@ -1759,61 +3214,13 @@ func (m *SecretReference) GetFile() *FileTarget { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*SecretReference) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _SecretReference_OneofMarshaler, _SecretReference_OneofUnmarshaler, _SecretReference_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*SecretReference) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*SecretReference_File)(nil), } } -func _SecretReference_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*SecretReference) - // target - switch x := m.Target.(type) { - case *SecretReference_File: - _ = b.EncodeVarint(3<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.File); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("SecretReference.Target has unexpected type %T", x) - } - return nil -} - -func _SecretReference_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*SecretReference) - switch tag { - case 3: // target.file - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(FileTarget) - err := b.DecodeMessage(msg) - m.Target = &SecretReference_File{msg} - return true, err - default: - return false, nil - } -} - -func _SecretReference_OneofSizer(msg proto.Message) (n int) { - m := msg.(*SecretReference) - // target - switch x := m.Target.(type) { - case *SecretReference_File: - s := proto.Size(x.File) - n += proto.SizeVarint(3<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - // ConfigReference is the linkage between a service and a config that it uses. type ConfigReference struct { // ConfigID represents the ID of the specific Config that we're @@ -1822,16 +3229,45 @@ type ConfigReference struct { // ConfigName is the name of the config that this references, but this is just provided for // lookup/display purposes. The config in the reference will be identified by its ID. ConfigName string `protobuf:"bytes,2,opt,name=config_name,json=configName,proto3" json:"config_name,omitempty"` - // Target specifies how this secret should be exposed to the task. + // Target specifies how this config should be exposed to the task. // // Types that are valid to be assigned to Target: // *ConfigReference_File + // *ConfigReference_Runtime Target isConfigReference_Target `protobuf_oneof:"target"` } -func (m *ConfigReference) Reset() { *m = ConfigReference{} } -func (*ConfigReference) ProtoMessage() {} -func (*ConfigReference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{49} } +func (m *ConfigReference) Reset() { *m = ConfigReference{} } +func (*ConfigReference) ProtoMessage() {} +func (*ConfigReference) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{50} +} +func (m *ConfigReference) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConfigReference) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConfigReference.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConfigReference) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConfigReference.Merge(m, src) +} +func (m *ConfigReference) XXX_Size() int { + return m.Size() +} +func (m *ConfigReference) XXX_DiscardUnknown() { + xxx_messageInfo_ConfigReference.DiscardUnknown(m) +} + +var xxx_messageInfo_ConfigReference proto.InternalMessageInfo type isConfigReference_Target interface { isConfigReference_Target() @@ -1840,10 +3276,14 @@ type isConfigReference_Target interface { } type ConfigReference_File struct { - File *FileTarget `protobuf:"bytes,3,opt,name=file,oneof"` + File *FileTarget `protobuf:"bytes,3,opt,name=file,proto3,oneof" json:"file,omitempty"` +} +type ConfigReference_Runtime struct { + Runtime *RuntimeTarget `protobuf:"bytes,4,opt,name=runtime,proto3,oneof" json:"runtime,omitempty"` } -func (*ConfigReference_File) isConfigReference_Target() {} +func (*ConfigReference_File) isConfigReference_Target() {} +func (*ConfigReference_Runtime) isConfigReference_Target() {} func (m *ConfigReference) GetTarget() isConfigReference_Target { if m != nil { @@ -1859,59 +3299,19 @@ func (m *ConfigReference) GetFile() *FileTarget { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*ConfigReference) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _ConfigReference_OneofMarshaler, _ConfigReference_OneofUnmarshaler, _ConfigReference_OneofSizer, []interface{}{ - (*ConfigReference_File)(nil), - } -} - -func _ConfigReference_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*ConfigReference) - // target - switch x := m.Target.(type) { - case *ConfigReference_File: - _ = b.EncodeVarint(3<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.File); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("ConfigReference.Target has unexpected type %T", x) +func (m *ConfigReference) GetRuntime() *RuntimeTarget { + if x, ok := m.GetTarget().(*ConfigReference_Runtime); ok { + return x.Runtime } return nil } -func _ConfigReference_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*ConfigReference) - switch tag { - case 3: // target.file - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(FileTarget) - err := b.DecodeMessage(msg) - m.Target = &ConfigReference_File{msg} - return true, err - default: - return false, nil - } -} - -func _ConfigReference_OneofSizer(msg proto.Message) (n int) { - m := msg.(*ConfigReference) - // target - switch x := m.Target.(type) { - case *ConfigReference_File: - s := proto.Size(x.File) - n += proto.SizeVarint(3<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) +// XXX_OneofWrappers is for the internal use of the proto package. +func (*ConfigReference) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*ConfigReference_File)(nil), + (*ConfigReference_Runtime)(nil), } - return n } // BlacklistedCertificate is a record for a blacklisted certificate. It does not @@ -1920,12 +3320,40 @@ type BlacklistedCertificate struct { // Expiry is the latest known expiration time of a certificate that // was issued for the given CN. // Note: can't use stdtime because this field is nullable. - Expiry *google_protobuf.Timestamp `protobuf:"bytes,1,opt,name=expiry" json:"expiry,omitempty"` + Expiry *types.Timestamp `protobuf:"bytes,1,opt,name=expiry,proto3" json:"expiry,omitempty"` +} + +func (m *BlacklistedCertificate) Reset() { *m = BlacklistedCertificate{} } +func (*BlacklistedCertificate) ProtoMessage() {} +func (*BlacklistedCertificate) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{51} +} +func (m *BlacklistedCertificate) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BlacklistedCertificate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BlacklistedCertificate.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BlacklistedCertificate) XXX_Merge(src proto.Message) { + xxx_messageInfo_BlacklistedCertificate.Merge(m, src) +} +func (m *BlacklistedCertificate) XXX_Size() int { + return m.Size() +} +func (m *BlacklistedCertificate) XXX_DiscardUnknown() { + xxx_messageInfo_BlacklistedCertificate.DiscardUnknown(m) } -func (m *BlacklistedCertificate) Reset() { *m = BlacklistedCertificate{} } -func (*BlacklistedCertificate) ProtoMessage() {} -func (*BlacklistedCertificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{50} } +var xxx_messageInfo_BlacklistedCertificate proto.InternalMessageInfo // HealthConfig holds configuration settings for the HEALTHCHECK feature. type HealthConfig struct { @@ -1936,26 +3364,54 @@ type HealthConfig struct { // {"NONE"} : disable healthcheck // {"CMD", args...} : exec arguments directly // {"CMD-SHELL", command} : run command with system's default shell - Test []string `protobuf:"bytes,1,rep,name=test" json:"test,omitempty"` + Test []string `protobuf:"bytes,1,rep,name=test,proto3" json:"test,omitempty"` // Interval is the time to wait between checks. Zero means inherit. // Note: can't use stdduration because this field needs to be nullable. - Interval *google_protobuf1.Duration `protobuf:"bytes,2,opt,name=interval" json:"interval,omitempty"` + Interval *types.Duration `protobuf:"bytes,2,opt,name=interval,proto3" json:"interval,omitempty"` // Timeout is the time to wait before considering the check to have hung. // Zero means inherit. // Note: can't use stdduration because this field needs to be nullable. - Timeout *google_protobuf1.Duration `protobuf:"bytes,3,opt,name=timeout" json:"timeout,omitempty"` + Timeout *types.Duration `protobuf:"bytes,3,opt,name=timeout,proto3" json:"timeout,omitempty"` // Retries is the number of consecutive failures needed to consider a // container as unhealthy. Zero means inherit. Retries int32 `protobuf:"varint,4,opt,name=retries,proto3" json:"retries,omitempty"` // Start period is the period for container initialization during // which health check failures will note count towards the maximum // number of retries. - StartPeriod *google_protobuf1.Duration `protobuf:"bytes,5,opt,name=start_period,json=startPeriod" json:"start_period,omitempty"` + StartPeriod *types.Duration `protobuf:"bytes,5,opt,name=start_period,json=startPeriod,proto3" json:"start_period,omitempty"` +} + +func (m *HealthConfig) Reset() { *m = HealthConfig{} } +func (*HealthConfig) ProtoMessage() {} +func (*HealthConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{52} +} +func (m *HealthConfig) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HealthConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HealthConfig.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HealthConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_HealthConfig.Merge(m, src) +} +func (m *HealthConfig) XXX_Size() int { + return m.Size() +} +func (m *HealthConfig) XXX_DiscardUnknown() { + xxx_messageInfo_HealthConfig.DiscardUnknown(m) } -func (m *HealthConfig) Reset() { *m = HealthConfig{} } -func (*HealthConfig) ProtoMessage() {} -func (*HealthConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{51} } +var xxx_messageInfo_HealthConfig proto.InternalMessageInfo type MaybeEncryptedRecord struct { Algorithm MaybeEncryptedRecord_Algorithm `protobuf:"varint,1,opt,name=algorithm,proto3,enum=docker.swarmkit.v1.MaybeEncryptedRecord_Algorithm" json:"algorithm,omitempty"` @@ -1963,9 +3419,37 @@ type MaybeEncryptedRecord struct { Nonce []byte `protobuf:"bytes,3,opt,name=nonce,proto3" json:"nonce,omitempty"` } -func (m *MaybeEncryptedRecord) Reset() { *m = MaybeEncryptedRecord{} } -func (*MaybeEncryptedRecord) ProtoMessage() {} -func (*MaybeEncryptedRecord) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{52} } +func (m *MaybeEncryptedRecord) Reset() { *m = MaybeEncryptedRecord{} } +func (*MaybeEncryptedRecord) ProtoMessage() {} +func (*MaybeEncryptedRecord) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{53} +} +func (m *MaybeEncryptedRecord) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MaybeEncryptedRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MaybeEncryptedRecord.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MaybeEncryptedRecord) XXX_Merge(src proto.Message) { + xxx_messageInfo_MaybeEncryptedRecord.Merge(m, src) +} +func (m *MaybeEncryptedRecord) XXX_Size() int { + return m.Size() +} +func (m *MaybeEncryptedRecord) XXX_DiscardUnknown() { + xxx_messageInfo_MaybeEncryptedRecord.DiscardUnknown(m) +} + +var xxx_messageInfo_MaybeEncryptedRecord proto.InternalMessageInfo type RootRotation struct { CACert []byte `protobuf:"bytes,1,opt,name=ca_cert,json=caCert,proto3" json:"ca_cert,omitempty"` @@ -1974,34 +3458,117 @@ type RootRotation struct { CrossSignedCACert []byte `protobuf:"bytes,3,opt,name=cross_signed_ca_cert,json=crossSignedCaCert,proto3" json:"cross_signed_ca_cert,omitempty"` } -func (m *RootRotation) Reset() { *m = RootRotation{} } -func (*RootRotation) ProtoMessage() {} -func (*RootRotation) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{53} } +func (m *RootRotation) Reset() { *m = RootRotation{} } +func (*RootRotation) ProtoMessage() {} +func (*RootRotation) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{54} +} +func (m *RootRotation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RootRotation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RootRotation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RootRotation) XXX_Merge(src proto.Message) { + xxx_messageInfo_RootRotation.Merge(m, src) +} +func (m *RootRotation) XXX_Size() int { + return m.Size() +} +func (m *RootRotation) XXX_DiscardUnknown() { + xxx_messageInfo_RootRotation.DiscardUnknown(m) +} + +var xxx_messageInfo_RootRotation proto.InternalMessageInfo // Privileges specifies security configuration/permissions. type Privileges struct { - CredentialSpec *Privileges_CredentialSpec `protobuf:"bytes,1,opt,name=credential_spec,json=credentialSpec" json:"credential_spec,omitempty"` - SELinuxContext *Privileges_SELinuxContext `protobuf:"bytes,2,opt,name=selinux_context,json=selinuxContext" json:"selinux_context,omitempty"` + CredentialSpec *Privileges_CredentialSpec `protobuf:"bytes,1,opt,name=credential_spec,json=credentialSpec,proto3" json:"credential_spec,omitempty"` + SELinuxContext *Privileges_SELinuxContext `protobuf:"bytes,2,opt,name=selinux_context,json=selinuxContext,proto3" json:"selinux_context,omitempty"` +} + +func (m *Privileges) Reset() { *m = Privileges{} } +func (*Privileges) ProtoMessage() {} +func (*Privileges) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{55} +} +func (m *Privileges) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Privileges) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Privileges.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Privileges) XXX_Merge(src proto.Message) { + xxx_messageInfo_Privileges.Merge(m, src) +} +func (m *Privileges) XXX_Size() int { + return m.Size() +} +func (m *Privileges) XXX_DiscardUnknown() { + xxx_messageInfo_Privileges.DiscardUnknown(m) } -func (m *Privileges) Reset() { *m = Privileges{} } -func (*Privileges) ProtoMessage() {} -func (*Privileges) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{54} } +var xxx_messageInfo_Privileges proto.InternalMessageInfo // CredentialSpec for managed service account (Windows only). type Privileges_CredentialSpec struct { // Types that are valid to be assigned to Source: // *Privileges_CredentialSpec_File // *Privileges_CredentialSpec_Registry + // *Privileges_CredentialSpec_Config Source isPrivileges_CredentialSpec_Source `protobuf_oneof:"source"` } func (m *Privileges_CredentialSpec) Reset() { *m = Privileges_CredentialSpec{} } func (*Privileges_CredentialSpec) ProtoMessage() {} func (*Privileges_CredentialSpec) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{54, 0} + return fileDescriptor_0b5eafd0404ded3d, []int{55, 0} +} +func (m *Privileges_CredentialSpec) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Privileges_CredentialSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Privileges_CredentialSpec.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Privileges_CredentialSpec) XXX_Merge(src proto.Message) { + xxx_messageInfo_Privileges_CredentialSpec.Merge(m, src) +} +func (m *Privileges_CredentialSpec) XXX_Size() int { + return m.Size() +} +func (m *Privileges_CredentialSpec) XXX_DiscardUnknown() { + xxx_messageInfo_Privileges_CredentialSpec.DiscardUnknown(m) } +var xxx_messageInfo_Privileges_CredentialSpec proto.InternalMessageInfo + type isPrivileges_CredentialSpec_Source interface { isPrivileges_CredentialSpec_Source() MarshalTo([]byte) (int, error) @@ -2009,14 +3576,18 @@ type isPrivileges_CredentialSpec_Source interface { } type Privileges_CredentialSpec_File struct { - File string `protobuf:"bytes,1,opt,name=file,proto3,oneof"` + File string `protobuf:"bytes,1,opt,name=file,proto3,oneof" json:"file,omitempty"` } type Privileges_CredentialSpec_Registry struct { - Registry string `protobuf:"bytes,2,opt,name=registry,proto3,oneof"` + Registry string `protobuf:"bytes,2,opt,name=registry,proto3,oneof" json:"registry,omitempty"` +} +type Privileges_CredentialSpec_Config struct { + Config string `protobuf:"bytes,3,opt,name=config,proto3,oneof" json:"config,omitempty"` } func (*Privileges_CredentialSpec_File) isPrivileges_CredentialSpec_Source() {} func (*Privileges_CredentialSpec_Registry) isPrivileges_CredentialSpec_Source() {} +func (*Privileges_CredentialSpec_Config) isPrivileges_CredentialSpec_Source() {} func (m *Privileges_CredentialSpec) GetSource() isPrivileges_CredentialSpec_Source { if m != nil { @@ -2039,70 +3610,20 @@ func (m *Privileges_CredentialSpec) GetRegistry() string { return "" } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*Privileges_CredentialSpec) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _Privileges_CredentialSpec_OneofMarshaler, _Privileges_CredentialSpec_OneofUnmarshaler, _Privileges_CredentialSpec_OneofSizer, []interface{}{ - (*Privileges_CredentialSpec_File)(nil), - (*Privileges_CredentialSpec_Registry)(nil), +func (m *Privileges_CredentialSpec) GetConfig() string { + if x, ok := m.GetSource().(*Privileges_CredentialSpec_Config); ok { + return x.Config } + return "" } -func _Privileges_CredentialSpec_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*Privileges_CredentialSpec) - // source - switch x := m.Source.(type) { - case *Privileges_CredentialSpec_File: - _ = b.EncodeVarint(1<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.File) - case *Privileges_CredentialSpec_Registry: - _ = b.EncodeVarint(2<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.Registry) - case nil: - default: - return fmt.Errorf("Privileges_CredentialSpec.Source has unexpected type %T", x) +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Privileges_CredentialSpec) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*Privileges_CredentialSpec_File)(nil), + (*Privileges_CredentialSpec_Registry)(nil), + (*Privileges_CredentialSpec_Config)(nil), } - return nil -} - -func _Privileges_CredentialSpec_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*Privileges_CredentialSpec) - switch tag { - case 1: // source.file - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.Source = &Privileges_CredentialSpec_File{x} - return true, err - case 2: // source.registry - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.Source = &Privileges_CredentialSpec_Registry{x} - return true, err - default: - return false, nil - } -} - -func _Privileges_CredentialSpec_OneofSizer(msg proto.Message) (n int) { - m := msg.(*Privileges_CredentialSpec) - // source - switch x := m.Source.(type) { - case *Privileges_CredentialSpec_File: - n += proto.SizeVarint(1<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.File))) - n += len(x.File) - case *Privileges_CredentialSpec_Registry: - n += proto.SizeVarint(2<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.Registry))) - n += len(x.Registry) - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n } // SELinuxContext contains the SELinux labels for the container. @@ -2117,13 +3638,106 @@ type Privileges_SELinuxContext struct { func (m *Privileges_SELinuxContext) Reset() { *m = Privileges_SELinuxContext{} } func (*Privileges_SELinuxContext) ProtoMessage() {} func (*Privileges_SELinuxContext) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{54, 1} + return fileDescriptor_0b5eafd0404ded3d, []int{55, 1} +} +func (m *Privileges_SELinuxContext) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Privileges_SELinuxContext) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Privileges_SELinuxContext.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Privileges_SELinuxContext) XXX_Merge(src proto.Message) { + xxx_messageInfo_Privileges_SELinuxContext.Merge(m, src) +} +func (m *Privileges_SELinuxContext) XXX_Size() int { + return m.Size() +} +func (m *Privileges_SELinuxContext) XXX_DiscardUnknown() { + xxx_messageInfo_Privileges_SELinuxContext.DiscardUnknown(m) +} + +var xxx_messageInfo_Privileges_SELinuxContext proto.InternalMessageInfo + +// JobStatus indicates the status of a Service that is in one of the Job modes. +type JobStatus struct { + // JobIteration is the count of how many times the Job has been excecuted, + // successfully or otherwise. "Executed" refers to the job as a whole being + // started, not to the individual Tasks being launched. This is used to + // disambiguate which Tasks belong to which iteration of a Job. + JobIteration Version `protobuf:"bytes,1,opt,name=job_iteration,json=jobIteration,proto3" json:"job_iteration"` + // LastExecution is the time that the job was last executed. This is set by + // the orchestrator in the same transaction that JobIteration is incremented. + // While time is a fungible concept in distributed systems like Swarmkit, + // this value gives us a best-effort attempt to prevent weird behavior like + // newly added nodes executing long-forgotten jobs. + LastExecution *types.Timestamp `protobuf:"bytes,2,opt,name=last_execution,json=lastExecution,proto3" json:"last_execution,omitempty"` +} + +func (m *JobStatus) Reset() { *m = JobStatus{} } +func (*JobStatus) ProtoMessage() {} +func (*JobStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_0b5eafd0404ded3d, []int{56} +} +func (m *JobStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *JobStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_JobStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *JobStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_JobStatus.Merge(m, src) +} +func (m *JobStatus) XXX_Size() int { + return m.Size() } +func (m *JobStatus) XXX_DiscardUnknown() { + xxx_messageInfo_JobStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_JobStatus proto.InternalMessageInfo func init() { + proto.RegisterEnum("docker.swarmkit.v1.ResourceType", ResourceType_name, ResourceType_value) + proto.RegisterEnum("docker.swarmkit.v1.TaskState", TaskState_name, TaskState_value) + proto.RegisterEnum("docker.swarmkit.v1.NodeRole", NodeRole_name, NodeRole_value) + proto.RegisterEnum("docker.swarmkit.v1.RaftMemberStatus_Reachability", RaftMemberStatus_Reachability_name, RaftMemberStatus_Reachability_value) + proto.RegisterEnum("docker.swarmkit.v1.NodeStatus_State", NodeStatus_State_name, NodeStatus_State_value) + proto.RegisterEnum("docker.swarmkit.v1.Mount_MountType", Mount_MountType_name, Mount_MountType_value) + proto.RegisterEnum("docker.swarmkit.v1.Mount_MountConsistency", Mount_MountConsistency_name, Mount_MountConsistency_value) + proto.RegisterEnum("docker.swarmkit.v1.Mount_BindOptions_MountPropagation", Mount_BindOptions_MountPropagation_name, Mount_BindOptions_MountPropagation_value) + proto.RegisterEnum("docker.swarmkit.v1.RestartPolicy_RestartCondition", RestartPolicy_RestartCondition_name, RestartPolicy_RestartCondition_value) + proto.RegisterEnum("docker.swarmkit.v1.UpdateConfig_FailureAction", UpdateConfig_FailureAction_name, UpdateConfig_FailureAction_value) + proto.RegisterEnum("docker.swarmkit.v1.UpdateConfig_UpdateOrder", UpdateConfig_UpdateOrder_name, UpdateConfig_UpdateOrder_value) + proto.RegisterEnum("docker.swarmkit.v1.UpdateStatus_UpdateState", UpdateStatus_UpdateState_name, UpdateStatus_UpdateState_value) + proto.RegisterEnum("docker.swarmkit.v1.IPAMConfig_AddressFamily", IPAMConfig_AddressFamily_name, IPAMConfig_AddressFamily_value) + proto.RegisterEnum("docker.swarmkit.v1.PortConfig_Protocol", PortConfig_Protocol_name, PortConfig_Protocol_value) + proto.RegisterEnum("docker.swarmkit.v1.PortConfig_PublishMode", PortConfig_PublishMode_name, PortConfig_PublishMode_value) + proto.RegisterEnum("docker.swarmkit.v1.IssuanceStatus_State", IssuanceStatus_State_name, IssuanceStatus_State_value) + proto.RegisterEnum("docker.swarmkit.v1.ExternalCA_CAProtocol", ExternalCA_CAProtocol_name, ExternalCA_CAProtocol_value) + proto.RegisterEnum("docker.swarmkit.v1.EncryptionKey_Algorithm", EncryptionKey_Algorithm_name, EncryptionKey_Algorithm_value) + proto.RegisterEnum("docker.swarmkit.v1.MaybeEncryptedRecord_Algorithm", MaybeEncryptedRecord_Algorithm_name, MaybeEncryptedRecord_Algorithm_value) proto.RegisterType((*Version)(nil), "docker.swarmkit.v1.Version") proto.RegisterType((*IndexEntry)(nil), "docker.swarmkit.v1.IndexEntry") proto.RegisterType((*Annotations)(nil), "docker.swarmkit.v1.Annotations") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.Annotations.LabelsEntry") proto.RegisterType((*NamedGenericResource)(nil), "docker.swarmkit.v1.NamedGenericResource") proto.RegisterType((*DiscreteGenericResource)(nil), "docker.swarmkit.v1.DiscreteGenericResource") proto.RegisterType((*GenericResource)(nil), "docker.swarmkit.v1.GenericResource") @@ -2132,6 +3746,7 @@ func init() { proto.RegisterType((*Platform)(nil), "docker.swarmkit.v1.Platform") proto.RegisterType((*PluginDescription)(nil), "docker.swarmkit.v1.PluginDescription") proto.RegisterType((*EngineDescription)(nil), "docker.swarmkit.v1.EngineDescription") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.EngineDescription.LabelsEntry") proto.RegisterType((*NodeDescription)(nil), "docker.swarmkit.v1.NodeDescription") proto.RegisterType((*NodeTLSInfo)(nil), "docker.swarmkit.v1.NodeTLSInfo") proto.RegisterType((*RaftMemberStatus)(nil), "docker.swarmkit.v1.RaftMemberStatus") @@ -2140,6 +3755,7 @@ func init() { proto.RegisterType((*Mount)(nil), "docker.swarmkit.v1.Mount") proto.RegisterType((*Mount_BindOptions)(nil), "docker.swarmkit.v1.Mount.BindOptions") proto.RegisterType((*Mount_VolumeOptions)(nil), "docker.swarmkit.v1.Mount.VolumeOptions") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.Mount.VolumeOptions.LabelsEntry") proto.RegisterType((*Mount_TmpfsOptions)(nil), "docker.swarmkit.v1.Mount.TmpfsOptions") proto.RegisterType((*RestartPolicy)(nil), "docker.swarmkit.v1.RestartPolicy") proto.RegisterType((*UpdateConfig)(nil), "docker.swarmkit.v1.UpdateConfig") @@ -2148,9 +3764,12 @@ func init() { proto.RegisterType((*PortStatus)(nil), "docker.swarmkit.v1.PortStatus") proto.RegisterType((*TaskStatus)(nil), "docker.swarmkit.v1.TaskStatus") proto.RegisterType((*NetworkAttachmentConfig)(nil), "docker.swarmkit.v1.NetworkAttachmentConfig") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.NetworkAttachmentConfig.DriverAttachmentOptsEntry") proto.RegisterType((*IPAMConfig)(nil), "docker.swarmkit.v1.IPAMConfig") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.IPAMConfig.ReservedEntry") proto.RegisterType((*PortConfig)(nil), "docker.swarmkit.v1.PortConfig") proto.RegisterType((*Driver)(nil), "docker.swarmkit.v1.Driver") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.Driver.OptionsEntry") proto.RegisterType((*IPAMOptions)(nil), "docker.swarmkit.v1.IPAMOptions") proto.RegisterType((*Peer)(nil), "docker.swarmkit.v1.Peer") proto.RegisterType((*WeightedPeer)(nil), "docker.swarmkit.v1.WeightedPeer") @@ -2159,6 +3778,7 @@ func init() { proto.RegisterType((*AcceptancePolicy_RoleAdmissionPolicy)(nil), "docker.swarmkit.v1.AcceptancePolicy.RoleAdmissionPolicy") proto.RegisterType((*AcceptancePolicy_RoleAdmissionPolicy_Secret)(nil), "docker.swarmkit.v1.AcceptancePolicy.RoleAdmissionPolicy.Secret") proto.RegisterType((*ExternalCA)(nil), "docker.swarmkit.v1.ExternalCA") + proto.RegisterMapType((map[string]string)(nil), "docker.swarmkit.v1.ExternalCA.OptionsEntry") proto.RegisterType((*CAConfig)(nil), "docker.swarmkit.v1.CAConfig") proto.RegisterType((*OrchestrationConfig)(nil), "docker.swarmkit.v1.OrchestrationConfig") proto.RegisterType((*TaskDefaults)(nil), "docker.swarmkit.v1.TaskDefaults") @@ -2174,6 +3794,7 @@ func init() { proto.RegisterType((*EncryptionKey)(nil), "docker.swarmkit.v1.EncryptionKey") proto.RegisterType((*ManagerStatus)(nil), "docker.swarmkit.v1.ManagerStatus") proto.RegisterType((*FileTarget)(nil), "docker.swarmkit.v1.FileTarget") + proto.RegisterType((*RuntimeTarget)(nil), "docker.swarmkit.v1.RuntimeTarget") proto.RegisterType((*SecretReference)(nil), "docker.swarmkit.v1.SecretReference") proto.RegisterType((*ConfigReference)(nil), "docker.swarmkit.v1.ConfigReference") proto.RegisterType((*BlacklistedCertificate)(nil), "docker.swarmkit.v1.BlacklistedCertificate") @@ -2183,25 +3804,350 @@ func init() { proto.RegisterType((*Privileges)(nil), "docker.swarmkit.v1.Privileges") proto.RegisterType((*Privileges_CredentialSpec)(nil), "docker.swarmkit.v1.Privileges.CredentialSpec") proto.RegisterType((*Privileges_SELinuxContext)(nil), "docker.swarmkit.v1.Privileges.SELinuxContext") - proto.RegisterEnum("docker.swarmkit.v1.ResourceType", ResourceType_name, ResourceType_value) - proto.RegisterEnum("docker.swarmkit.v1.TaskState", TaskState_name, TaskState_value) - proto.RegisterEnum("docker.swarmkit.v1.NodeRole", NodeRole_name, NodeRole_value) - proto.RegisterEnum("docker.swarmkit.v1.RaftMemberStatus_Reachability", RaftMemberStatus_Reachability_name, RaftMemberStatus_Reachability_value) - proto.RegisterEnum("docker.swarmkit.v1.NodeStatus_State", NodeStatus_State_name, NodeStatus_State_value) - proto.RegisterEnum("docker.swarmkit.v1.Mount_MountType", Mount_MountType_name, Mount_MountType_value) - proto.RegisterEnum("docker.swarmkit.v1.Mount_MountConsistency", Mount_MountConsistency_name, Mount_MountConsistency_value) - proto.RegisterEnum("docker.swarmkit.v1.Mount_BindOptions_MountPropagation", Mount_BindOptions_MountPropagation_name, Mount_BindOptions_MountPropagation_value) - proto.RegisterEnum("docker.swarmkit.v1.RestartPolicy_RestartCondition", RestartPolicy_RestartCondition_name, RestartPolicy_RestartCondition_value) - proto.RegisterEnum("docker.swarmkit.v1.UpdateConfig_FailureAction", UpdateConfig_FailureAction_name, UpdateConfig_FailureAction_value) - proto.RegisterEnum("docker.swarmkit.v1.UpdateConfig_UpdateOrder", UpdateConfig_UpdateOrder_name, UpdateConfig_UpdateOrder_value) - proto.RegisterEnum("docker.swarmkit.v1.UpdateStatus_UpdateState", UpdateStatus_UpdateState_name, UpdateStatus_UpdateState_value) - proto.RegisterEnum("docker.swarmkit.v1.IPAMConfig_AddressFamily", IPAMConfig_AddressFamily_name, IPAMConfig_AddressFamily_value) - proto.RegisterEnum("docker.swarmkit.v1.PortConfig_Protocol", PortConfig_Protocol_name, PortConfig_Protocol_value) - proto.RegisterEnum("docker.swarmkit.v1.PortConfig_PublishMode", PortConfig_PublishMode_name, PortConfig_PublishMode_value) - proto.RegisterEnum("docker.swarmkit.v1.IssuanceStatus_State", IssuanceStatus_State_name, IssuanceStatus_State_value) - proto.RegisterEnum("docker.swarmkit.v1.ExternalCA_CAProtocol", ExternalCA_CAProtocol_name, ExternalCA_CAProtocol_value) - proto.RegisterEnum("docker.swarmkit.v1.EncryptionKey_Algorithm", EncryptionKey_Algorithm_name, EncryptionKey_Algorithm_value) - proto.RegisterEnum("docker.swarmkit.v1.MaybeEncryptedRecord_Algorithm", MaybeEncryptedRecord_Algorithm_name, MaybeEncryptedRecord_Algorithm_value) + proto.RegisterType((*JobStatus)(nil), "docker.swarmkit.v1.JobStatus") +} + +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/types.proto", fileDescriptor_0b5eafd0404ded3d) +} + +var fileDescriptor_0b5eafd0404ded3d = []byte{ + // 5345 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x3a, 0x4b, 0x6c, 0x24, 0x49, + 0x56, 0xae, 0x72, 0x55, 0xb9, 0xea, 0x55, 0xd9, 0xce, 0x8e, 0xf6, 0xf6, 0xb8, 0x6b, 0x7a, 0x6c, + 0x4f, 0xce, 0xf4, 0xce, 0xec, 0xec, 0xe0, 0xfe, 0xcd, 0x8e, 0x7a, 0x66, 0x98, 0x9d, 0xa9, 0x4f, + 0xba, 0x5d, 0xdb, 0x76, 0x55, 0x29, 0xaa, 0xdc, 0xbd, 0x8b, 0x04, 0x49, 0x3a, 0x33, 0x5c, 0xce, + 0x76, 0x56, 0x46, 0x92, 0x99, 0x65, 0x77, 0xb1, 0x20, 0xe6, 0x04, 0xc8, 0x27, 0xb8, 0x2c, 0xbb, + 0x42, 0x96, 0x90, 0xe0, 0xc6, 0x81, 0x03, 0x07, 0x16, 0x4e, 0x83, 0x84, 0xd0, 0x8a, 0x0b, 0xbb, + 0x20, 0xc1, 0x0a, 0x90, 0x61, 0xbc, 0x12, 0x37, 0x04, 0x17, 0xc4, 0x85, 0x03, 0x8a, 0x4f, 0x66, + 0xa5, 0xab, 0xd3, 0xf6, 0xcc, 0x2e, 0x17, 0xbb, 0xe2, 0xfd, 0xf2, 0xc5, 0x8b, 0x17, 0x2f, 0xde, + 0x7b, 0x11, 0xf0, 0xd6, 0xc0, 0x0e, 0xf7, 0x47, 0xbb, 0xeb, 0x26, 0x1d, 0xde, 0xb1, 0xa8, 0x79, + 0x40, 0xfc, 0x3b, 0xc1, 0x91, 0xe1, 0x0f, 0x0f, 0xec, 0xf0, 0x8e, 0xe1, 0xd9, 0x77, 0xc2, 0xb1, + 0x47, 0x82, 0x75, 0xcf, 0xa7, 0x21, 0x45, 0x48, 0x10, 0xac, 0x47, 0x04, 0xeb, 0x87, 0xf7, 0xaa, + 0xab, 0x03, 0x4a, 0x07, 0x0e, 0xb9, 0xc3, 0x29, 0x76, 0x47, 0x7b, 0x77, 0x42, 0x7b, 0x48, 0x82, + 0xd0, 0x18, 0x7a, 0x82, 0xa9, 0xba, 0x32, 0x4d, 0x60, 0x8d, 0x7c, 0x23, 0xb4, 0xa9, 0x7b, 0x11, + 0xfe, 0xc8, 0x37, 0x3c, 0x8f, 0xf8, 0xf2, 0xa3, 0xd5, 0xa5, 0x01, 0x1d, 0x50, 0xfe, 0xf3, 0x0e, + 0xfb, 0x25, 0xa0, 0xea, 0x2a, 0xcc, 0x3d, 0x21, 0x7e, 0x60, 0x53, 0x17, 0x2d, 0x41, 0xde, 0x76, + 0x2d, 0xf2, 0x7c, 0x39, 0xb3, 0x96, 0x79, 0x33, 0x87, 0xc5, 0x40, 0xbd, 0x0b, 0xd0, 0x62, 0x3f, + 0x34, 0x37, 0xf4, 0xc7, 0x48, 0x81, 0xd9, 0x03, 0x32, 0xe6, 0x14, 0x25, 0xcc, 0x7e, 0x32, 0xc8, + 0xa1, 0xe1, 0x2c, 0x67, 0x05, 0xe4, 0xd0, 0x70, 0xd4, 0xcf, 0x32, 0x50, 0xae, 0xb9, 0x2e, 0x0d, + 0xb9, 0x76, 0x01, 0x42, 0x90, 0x73, 0x8d, 0x21, 0x91, 0x4c, 0xfc, 0x37, 0x6a, 0x40, 0xc1, 0x31, + 0x76, 0x89, 0x13, 0x2c, 0x67, 0xd7, 0x66, 0xdf, 0x2c, 0xdf, 0xff, 0xea, 0xfa, 0x8b, 0x26, 0x59, + 0x4f, 0x08, 0x59, 0xdf, 0xe2, 0xd4, 0x5c, 0x09, 0x2c, 0x59, 0xd1, 0xd7, 0x61, 0xce, 0x76, 0x2d, + 0xdb, 0x24, 0xc1, 0x72, 0x8e, 0x4b, 0x59, 0x49, 0x93, 0x32, 0xd1, 0xbe, 0x9e, 0xfb, 0xc1, 0xe9, + 0xea, 0x0c, 0x8e, 0x98, 0xaa, 0xef, 0x41, 0x39, 0x21, 0x36, 0x65, 0x6e, 0x4b, 0x90, 0x3f, 0x34, + 0x9c, 0x11, 0x91, 0xb3, 0x13, 0x83, 0xf7, 0xb3, 0x0f, 0x33, 0xea, 0xc7, 0xb0, 0xd4, 0x36, 0x86, + 0xc4, 0x7a, 0x44, 0x5c, 0xe2, 0xdb, 0x26, 0x26, 0x01, 0x1d, 0xf9, 0x26, 0x61, 0x73, 0x3d, 0xb0, + 0x5d, 0x2b, 0x9a, 0x2b, 0xfb, 0x9d, 0x2e, 0x45, 0x6d, 0xc0, 0x4b, 0x4d, 0x3b, 0x30, 0x7d, 0x12, + 0x92, 0x2f, 0x2c, 0x64, 0x36, 0x12, 0x72, 0x9a, 0x81, 0xc5, 0x69, 0xee, 0x5f, 0x80, 0xeb, 0xcc, + 0xc4, 0x96, 0xee, 0x4b, 0x88, 0x1e, 0x78, 0xc4, 0xe4, 0xc2, 0xca, 0xf7, 0xdf, 0x4c, 0xb3, 0x50, + 0xda, 0x4c, 0x36, 0x67, 0xf0, 0x35, 0x2e, 0x26, 0x02, 0xf4, 0x3c, 0x62, 0x22, 0x13, 0x6e, 0x58, + 0x52, 0xe9, 0x29, 0xf1, 0x59, 0x2e, 0x3e, 0x75, 0x19, 0x2f, 0x98, 0xe6, 0xe6, 0x0c, 0x5e, 0x8a, + 0x84, 0x25, 0x3f, 0x52, 0x07, 0x28, 0x46, 0xb2, 0xd5, 0xef, 0x66, 0xa0, 0x14, 0x21, 0x03, 0xf4, + 0x15, 0x28, 0xb9, 0x86, 0x4b, 0x75, 0xd3, 0x1b, 0x05, 0x7c, 0x42, 0xb3, 0xf5, 0xca, 0xd9, 0xe9, + 0x6a, 0xb1, 0x6d, 0xb8, 0xb4, 0xd1, 0xdd, 0x09, 0x70, 0x91, 0xa1, 0x1b, 0xde, 0x28, 0x40, 0xaf, + 0x42, 0x65, 0x48, 0x86, 0xd4, 0x1f, 0xeb, 0xbb, 0xe3, 0x90, 0x04, 0xd2, 0x6c, 0x65, 0x01, 0xab, + 0x33, 0x10, 0xfa, 0x10, 0xe6, 0x06, 0x42, 0xa5, 0xe5, 0x59, 0xee, 0x3e, 0xaf, 0xa5, 0x69, 0x3f, + 0xa5, 0x35, 0x8e, 0x78, 0xd4, 0xef, 0x64, 0x61, 0x29, 0x86, 0x92, 0x5f, 0x19, 0xd9, 0x3e, 0x19, + 0x12, 0x37, 0x0c, 0xd0, 0xd7, 0xa0, 0xe0, 0xd8, 0x43, 0x3b, 0x0c, 0xa4, 0xcd, 0x5f, 0x49, 0x13, + 0x1b, 0x4f, 0x0a, 0x4b, 0x62, 0x54, 0x83, 0x8a, 0x4f, 0x02, 0xe2, 0x1f, 0x0a, 0x8f, 0x97, 0x16, + 0xbd, 0x82, 0xf9, 0x1c, 0x0b, 0x7a, 0x1f, 0x20, 0x38, 0x32, 0x3c, 0x39, 0xe5, 0x59, 0x2e, 0xe0, + 0xe5, 0x75, 0x11, 0x17, 0xd6, 0xa3, 0xb8, 0xb0, 0xde, 0x72, 0xc3, 0x77, 0xdf, 0x79, 0xc2, 0xfc, + 0x07, 0x97, 0x18, 0xb9, 0xb0, 0xc6, 0x26, 0x5c, 0x93, 0x06, 0x63, 0x30, 0xcf, 0x76, 0x49, 0xc0, + 0xb6, 0xd5, 0x95, 0x22, 0x14, 0xc1, 0xd5, 0x8b, 0x99, 0xd4, 0x0d, 0x28, 0x76, 0x1d, 0x23, 0xdc, + 0xa3, 0xfe, 0x10, 0xa9, 0x50, 0x31, 0x7c, 0x73, 0xdf, 0x0e, 0x89, 0x19, 0x8e, 0xfc, 0x28, 0x06, + 0x9c, 0x83, 0xa1, 0x1b, 0x90, 0xa5, 0x62, 0xba, 0xa5, 0x7a, 0xe1, 0xec, 0x74, 0x35, 0xdb, 0xe9, + 0xe1, 0x2c, 0x0d, 0xd4, 0x0f, 0xe0, 0x5a, 0xd7, 0x19, 0x0d, 0x6c, 0xb7, 0x49, 0x02, 0xd3, 0xb7, + 0x3d, 0x36, 0x47, 0xb6, 0x37, 0x58, 0x24, 0x8d, 0xf6, 0x06, 0xfb, 0x1d, 0x07, 0x98, 0xec, 0x24, + 0xc0, 0xa8, 0xbf, 0x95, 0x85, 0x6b, 0x9a, 0x3b, 0xb0, 0x5d, 0x92, 0xe4, 0xbe, 0x0d, 0x0b, 0x84, + 0x03, 0xf5, 0x43, 0x11, 0xf4, 0xa4, 0x9c, 0x79, 0x01, 0x8d, 0x22, 0x61, 0x6b, 0x2a, 0x3a, 0xdd, + 0x4b, 0x5b, 0x84, 0x17, 0xa4, 0xa7, 0xc6, 0x28, 0x0d, 0xe6, 0x3c, 0x3e, 0x89, 0x40, 0x3a, 0xd9, + 0xed, 0x34, 0x59, 0x2f, 0xcc, 0x33, 0x0a, 0x55, 0x92, 0xf7, 0x67, 0x09, 0x55, 0x7f, 0x93, 0x85, + 0xc5, 0x36, 0xb5, 0xce, 0xd9, 0xa1, 0x0a, 0xc5, 0x7d, 0x1a, 0x84, 0x89, 0xb0, 0x1c, 0x8f, 0xd1, + 0x43, 0x28, 0x7a, 0x72, 0xf9, 0xa4, 0x0f, 0xde, 0x4a, 0x57, 0x59, 0xd0, 0xe0, 0x98, 0x1a, 0x7d, + 0x00, 0xa5, 0x68, 0xe3, 0x46, 0xde, 0x77, 0x85, 0xfb, 0x4e, 0xe8, 0xd1, 0x87, 0x50, 0x10, 0x8b, + 0x20, 0x9d, 0xee, 0xf6, 0xe7, 0xb2, 0x39, 0x96, 0x4c, 0xe8, 0x11, 0x14, 0x43, 0x27, 0xd0, 0x6d, + 0x77, 0x8f, 0x2e, 0xe7, 0xb9, 0x80, 0xd5, 0xd4, 0x50, 0x47, 0x2d, 0xd2, 0xdf, 0xea, 0xb5, 0xdc, + 0x3d, 0x5a, 0x2f, 0x9f, 0x9d, 0xae, 0xce, 0xc9, 0x01, 0x9e, 0x0b, 0x9d, 0x80, 0xfd, 0x40, 0xb7, + 0x20, 0xb7, 0x67, 0x7b, 0xc1, 0x72, 0x61, 0x2d, 0xf3, 0x66, 0xb1, 0x5e, 0x3c, 0x3b, 0x5d, 0xcd, + 0x6d, 0xb4, 0xba, 0x3d, 0xcc, 0xa1, 0xea, 0xef, 0x66, 0xa0, 0x9c, 0x90, 0x81, 0x5e, 0x01, 0x08, + 0xfd, 0x51, 0x10, 0xea, 0x3e, 0xa5, 0x21, 0x37, 0x65, 0x05, 0x97, 0x38, 0x04, 0x53, 0x1a, 0xa2, + 0x75, 0xb8, 0x6e, 0x12, 0x3f, 0xd4, 0xed, 0x20, 0x18, 0x11, 0x5f, 0x0f, 0x46, 0xbb, 0xcf, 0x88, + 0x19, 0x72, 0xb3, 0x56, 0xf0, 0x35, 0x86, 0x6a, 0x71, 0x4c, 0x4f, 0x20, 0xd0, 0x03, 0xb8, 0x91, + 0xa4, 0xf7, 0x46, 0xbb, 0x8e, 0x6d, 0xea, 0x6c, 0xa9, 0x67, 0x39, 0xcb, 0xf5, 0x09, 0x4b, 0x97, + 0xe3, 0x1e, 0x93, 0xb1, 0xfa, 0xe3, 0x0c, 0x28, 0xd8, 0xd8, 0x0b, 0xb7, 0xc9, 0x70, 0x97, 0xf8, + 0xbd, 0xd0, 0x08, 0x47, 0x01, 0xba, 0x01, 0x05, 0x87, 0x18, 0x16, 0xf1, 0xb9, 0x52, 0x45, 0x2c, + 0x47, 0x68, 0x87, 0x45, 0x19, 0xc3, 0xdc, 0x37, 0x76, 0x6d, 0xc7, 0x0e, 0xc7, 0x5c, 0x95, 0x85, + 0x74, 0x07, 0x9f, 0x96, 0xb9, 0x8e, 0x13, 0x8c, 0xf8, 0x9c, 0x18, 0xb4, 0x0c, 0x73, 0x43, 0x12, + 0x04, 0xc6, 0x80, 0x70, 0x4d, 0x4b, 0x38, 0x1a, 0xaa, 0x1f, 0x40, 0x25, 0xc9, 0x87, 0xca, 0x30, + 0xb7, 0xd3, 0x7e, 0xdc, 0xee, 0x3c, 0x6d, 0x2b, 0x33, 0x68, 0x11, 0xca, 0x3b, 0x6d, 0xac, 0xd5, + 0x1a, 0x9b, 0xb5, 0xfa, 0x96, 0xa6, 0x64, 0xd0, 0x3c, 0x94, 0x26, 0xc3, 0xac, 0xfa, 0xa7, 0x19, + 0x00, 0x66, 0x6e, 0x39, 0xa9, 0xf7, 0x21, 0x1f, 0x84, 0x46, 0x28, 0x7c, 0x76, 0xe1, 0xfe, 0xeb, + 0x17, 0xad, 0xb0, 0xd4, 0x97, 0xfd, 0x23, 0x58, 0xb0, 0x24, 0x35, 0xcc, 0x9e, 0xd3, 0x90, 0x85, + 0x0f, 0xc3, 0xb2, 0x7c, 0xa9, 0x38, 0xff, 0xad, 0x7e, 0x00, 0x79, 0xce, 0x7d, 0x5e, 0xdd, 0x22, + 0xe4, 0x9a, 0xec, 0x57, 0x06, 0x95, 0x20, 0x8f, 0xb5, 0x5a, 0xf3, 0x5b, 0x4a, 0x16, 0x29, 0x50, + 0x69, 0xb6, 0x7a, 0x8d, 0x4e, 0xbb, 0xad, 0x35, 0xfa, 0x5a, 0x53, 0x99, 0x55, 0x6f, 0x43, 0xbe, + 0x35, 0x64, 0x92, 0x6f, 0xb1, 0x0d, 0xb1, 0x47, 0x7c, 0xe2, 0x9a, 0xd1, 0x3e, 0x9b, 0x00, 0xd4, + 0xcf, 0x2a, 0x90, 0xdf, 0xa6, 0x23, 0x37, 0x44, 0xf7, 0x13, 0x41, 0x6d, 0x21, 0x3d, 0x8b, 0xe1, + 0x84, 0xeb, 0xfd, 0xb1, 0x47, 0x64, 0xd0, 0xbb, 0x01, 0x05, 0xb1, 0x75, 0xe4, 0x74, 0xe4, 0x88, + 0xc1, 0x43, 0xc3, 0x1f, 0x90, 0x50, 0xce, 0x47, 0x8e, 0xd0, 0x9b, 0xec, 0x54, 0x35, 0x2c, 0xea, + 0x3a, 0x63, 0xbe, 0xc3, 0x8a, 0xe2, 0xe8, 0xc4, 0xc4, 0xb0, 0x3a, 0xae, 0x33, 0xc6, 0x31, 0x16, + 0x3d, 0x82, 0xb2, 0x49, 0xdd, 0xc0, 0x0e, 0x42, 0xe2, 0x9a, 0xe3, 0xe5, 0x22, 0x57, 0xea, 0xf6, + 0xc5, 0x4a, 0x35, 0x26, 0xc4, 0x38, 0xc9, 0x89, 0x36, 0xa1, 0xb2, 0x6b, 0xbb, 0x96, 0x4e, 0x3d, + 0x71, 0xa2, 0xe5, 0x2f, 0xde, 0xd8, 0x42, 0x52, 0xdd, 0x76, 0xad, 0x8e, 0x20, 0xc6, 0xe5, 0xdd, + 0xc9, 0x00, 0xb5, 0x61, 0xe1, 0x90, 0x3a, 0xa3, 0x21, 0x89, 0x65, 0x15, 0xb8, 0xac, 0x37, 0x2e, + 0x96, 0xf5, 0x84, 0xd3, 0x47, 0xd2, 0xe6, 0x0f, 0x93, 0x43, 0xf4, 0x18, 0xe6, 0xc3, 0xa1, 0xb7, + 0x17, 0xc4, 0xe2, 0xe6, 0xb8, 0xb8, 0x2f, 0x5f, 0x62, 0x79, 0x46, 0x1e, 0x49, 0xab, 0x84, 0x89, + 0x51, 0xf5, 0xfb, 0xb3, 0x50, 0x4e, 0x68, 0x8e, 0x7a, 0x50, 0xf6, 0x7c, 0xea, 0x19, 0x03, 0x7e, + 0x2a, 0xcb, 0x45, 0xbd, 0xf7, 0xb9, 0x66, 0xbd, 0xde, 0x9d, 0x30, 0xe2, 0xa4, 0x14, 0xf4, 0x0e, + 0x54, 0x5c, 0xea, 0xfa, 0xc4, 0x1c, 0xf9, 0x81, 0x7d, 0x28, 0x16, 0xbd, 0x58, 0x57, 0xce, 0x4e, + 0x57, 0x2b, 0x6d, 0xea, 0xe2, 0x08, 0x8e, 0xcf, 0x51, 0xa9, 0x27, 0x59, 0x28, 0x27, 0x44, 0xa2, + 0xb7, 0xa0, 0x88, 0xbb, 0xb8, 0xf5, 0xa4, 0xd6, 0xd7, 0x94, 0x99, 0xea, 0xad, 0xe3, 0x93, 0xb5, + 0x65, 0xae, 0x43, 0xf2, 0xb3, 0x5d, 0xdf, 0x3e, 0x64, 0x9e, 0xff, 0x26, 0xcc, 0x45, 0xa4, 0x99, + 0xea, 0xcb, 0xc7, 0x27, 0x6b, 0x2f, 0x4d, 0x93, 0x26, 0x28, 0x71, 0x6f, 0xb3, 0x86, 0xb5, 0xa6, + 0x92, 0x4d, 0xa7, 0xc4, 0xbd, 0x7d, 0xc3, 0x27, 0x16, 0xfa, 0x32, 0x14, 0x24, 0xe1, 0x6c, 0xb5, + 0x7a, 0x7c, 0xb2, 0x76, 0x63, 0x9a, 0x70, 0x42, 0x87, 0x7b, 0x5b, 0xb5, 0x27, 0x9a, 0x92, 0x4b, + 0xa7, 0xc3, 0x3d, 0xc7, 0x38, 0x24, 0xe8, 0x75, 0xc8, 0x0b, 0xb2, 0x7c, 0xf5, 0xe6, 0xf1, 0xc9, + 0xda, 0x97, 0x5e, 0x10, 0xc7, 0xa8, 0xaa, 0xcb, 0xbf, 0xfd, 0x87, 0x2b, 0x33, 0x7f, 0xf1, 0x47, + 0x2b, 0xca, 0x34, 0xba, 0xfa, 0xbf, 0x19, 0x98, 0x3f, 0xe7, 0x28, 0x48, 0x85, 0x82, 0x4b, 0x4d, + 0xea, 0x89, 0xc3, 0xb5, 0x58, 0x87, 0xb3, 0xd3, 0xd5, 0x42, 0x9b, 0x36, 0xa8, 0x37, 0xc6, 0x12, + 0x83, 0x1e, 0x4f, 0xa5, 0x07, 0x0f, 0x3e, 0xa7, 0x17, 0xa6, 0x26, 0x08, 0x1f, 0xc1, 0xbc, 0xe5, + 0xdb, 0x87, 0xc4, 0xd7, 0x4d, 0xea, 0xee, 0xd9, 0x03, 0x79, 0x70, 0x56, 0x53, 0x33, 0x69, 0x4e, + 0x88, 0x2b, 0x82, 0xa1, 0xc1, 0xe9, 0x7f, 0x86, 0xd4, 0xa0, 0xea, 0x41, 0x25, 0xe9, 0xd7, 0xec, + 0x34, 0x0b, 0xec, 0x5f, 0x25, 0x32, 0x7f, 0xe4, 0x09, 0x36, 0x2e, 0x31, 0x88, 0x48, 0x11, 0xdf, + 0x80, 0xdc, 0x90, 0x5a, 0x42, 0xce, 0x7c, 0xfd, 0x3a, 0xcb, 0x50, 0xfe, 0xe9, 0x74, 0xb5, 0x4c, + 0x83, 0xf5, 0x0d, 0xdb, 0x21, 0xdb, 0xd4, 0x22, 0x98, 0x13, 0xb0, 0x58, 0x1b, 0x6d, 0x2c, 0x79, + 0x1a, 0xc8, 0xa1, 0xfa, 0xbd, 0x0c, 0xe4, 0x58, 0x10, 0x43, 0x2f, 0x43, 0xae, 0xde, 0x6a, 0x37, + 0x95, 0x99, 0xea, 0xb5, 0xe3, 0x93, 0xb5, 0x79, 0x6e, 0x2d, 0x86, 0x60, 0x9b, 0x01, 0xad, 0x42, + 0xe1, 0x49, 0x67, 0x6b, 0x67, 0x9b, 0x79, 0xde, 0xf5, 0xe3, 0x93, 0xb5, 0xc5, 0x18, 0x2d, 0xec, + 0x89, 0x5e, 0x81, 0x7c, 0x7f, 0xbb, 0xbb, 0xd1, 0x53, 0xb2, 0x55, 0x74, 0x7c, 0xb2, 0xb6, 0x10, + 0xe3, 0xf9, 0x74, 0xd0, 0xab, 0x90, 0x6f, 0x77, 0x5b, 0x5d, 0x4d, 0x99, 0xad, 0xde, 0x38, 0x3e, + 0x59, 0x43, 0x31, 0x9a, 0x57, 0x3a, 0x5d, 0xdb, 0x23, 0xd5, 0x6b, 0xd2, 0x27, 0x4a, 0x31, 0x4e, + 0xfd, 0x51, 0x06, 0xca, 0x89, 0x58, 0xc6, 0xdc, 0xba, 0xa9, 0x6d, 0xd4, 0x76, 0xb6, 0xfa, 0xca, + 0x4c, 0xc2, 0xad, 0x13, 0x24, 0x4d, 0xb2, 0x67, 0x8c, 0x1c, 0x16, 0x5b, 0xa1, 0xd1, 0x69, 0xf7, + 0x5a, 0xbd, 0xbe, 0xd6, 0xee, 0x2b, 0x99, 0xea, 0xf2, 0xf1, 0xc9, 0xda, 0xd2, 0x34, 0xf1, 0xc6, + 0xc8, 0x71, 0x98, 0x63, 0x37, 0x6a, 0x8d, 0x4d, 0xbe, 0x53, 0x26, 0x8e, 0x9d, 0xa0, 0x6a, 0x18, + 0xe6, 0x3e, 0xb1, 0xd0, 0xdb, 0x50, 0x6a, 0x6a, 0x5b, 0xda, 0xa3, 0x1a, 0x3f, 0x51, 0xaa, 0xaf, + 0x1c, 0x9f, 0xac, 0xdd, 0x7c, 0xf1, 0xeb, 0x0e, 0x19, 0x18, 0x21, 0xb1, 0xa6, 0x1c, 0x3c, 0x41, + 0xa2, 0xfe, 0x77, 0x16, 0xe6, 0x31, 0x09, 0x42, 0xc3, 0x0f, 0xbb, 0xd4, 0xb1, 0xcd, 0x31, 0xea, + 0x42, 0xc9, 0xa4, 0xae, 0x65, 0x27, 0x62, 0xd3, 0xfd, 0x0b, 0x92, 0xb4, 0x09, 0x57, 0x34, 0x6a, + 0x44, 0x9c, 0x78, 0x22, 0x04, 0xdd, 0x81, 0xbc, 0x45, 0x1c, 0x63, 0x2c, 0xb3, 0xc5, 0x9b, 0x2f, + 0x54, 0x0b, 0x4d, 0xd9, 0xa8, 0xc0, 0x82, 0x8e, 0xd7, 0x66, 0xc6, 0x73, 0xdd, 0x08, 0x43, 0x32, + 0xf4, 0x42, 0xe1, 0x23, 0x39, 0x5c, 0x1e, 0x1a, 0xcf, 0x6b, 0x12, 0x84, 0xee, 0x41, 0xe1, 0xc8, + 0x76, 0x2d, 0x7a, 0x24, 0xb3, 0xc1, 0x4b, 0x84, 0x4a, 0x42, 0xf5, 0x98, 0xa5, 0x41, 0x53, 0x6a, + 0x32, 0x37, 0x6b, 0x77, 0xda, 0x5a, 0xe4, 0x66, 0x12, 0xdf, 0x71, 0xdb, 0xd4, 0x65, 0xd1, 0x03, + 0x3a, 0x6d, 0x7d, 0xa3, 0xd6, 0xda, 0xda, 0xc1, 0xcc, 0xd5, 0x96, 0x8e, 0x4f, 0xd6, 0x94, 0x98, + 0x64, 0xc3, 0xb0, 0x1d, 0x56, 0x9e, 0xdc, 0x84, 0xd9, 0x5a, 0xfb, 0x5b, 0x4a, 0xb6, 0xaa, 0x1c, + 0x9f, 0xac, 0x55, 0x62, 0x74, 0xcd, 0x1d, 0x4f, 0xec, 0x3e, 0xfd, 0x5d, 0xf5, 0x6f, 0x67, 0xa1, + 0xb2, 0xe3, 0x59, 0x46, 0x48, 0xc4, 0x2e, 0x45, 0x6b, 0x50, 0xf6, 0x0c, 0xdf, 0x70, 0x1c, 0xe2, + 0xd8, 0xc1, 0x50, 0xb6, 0x58, 0x92, 0x20, 0xf4, 0xde, 0xe7, 0x35, 0x63, 0xbd, 0xc8, 0x76, 0xde, + 0x77, 0xff, 0x75, 0x35, 0x13, 0x19, 0x74, 0x07, 0x16, 0xf6, 0x84, 0xb6, 0xba, 0x61, 0xf2, 0x85, + 0x9d, 0xe5, 0x0b, 0xbb, 0x9e, 0xb6, 0xb0, 0x49, 0xb5, 0xd6, 0xe5, 0x24, 0x6b, 0x9c, 0x0b, 0xcf, + 0xef, 0x25, 0x87, 0xe8, 0x01, 0xcc, 0x0d, 0xa9, 0x6b, 0x87, 0xd4, 0xbf, 0x7a, 0x15, 0x22, 0x4a, + 0xf4, 0x16, 0x5c, 0x63, 0x8b, 0x1b, 0xe9, 0xc3, 0xd1, 0xfc, 0xe4, 0xcf, 0xe2, 0xc5, 0xa1, 0xf1, + 0x5c, 0x7e, 0x10, 0x33, 0x30, 0xaa, 0x43, 0x9e, 0xfa, 0x2c, 0x47, 0x2d, 0x70, 0x75, 0xdf, 0xbe, + 0x52, 0x5d, 0x31, 0xe8, 0x30, 0x1e, 0x2c, 0x58, 0xd5, 0x77, 0x61, 0xfe, 0xdc, 0x24, 0x58, 0x6a, + 0xd6, 0xad, 0xed, 0xf4, 0x34, 0x65, 0x06, 0x55, 0xa0, 0xd8, 0xe8, 0xb4, 0xfb, 0xad, 0xf6, 0x0e, + 0xcb, 0x2d, 0x2b, 0x50, 0xc4, 0x9d, 0xad, 0xad, 0x7a, 0xad, 0xf1, 0x58, 0xc9, 0xaa, 0xeb, 0x50, + 0x4e, 0x48, 0x43, 0x0b, 0x00, 0xbd, 0x7e, 0xa7, 0xab, 0x6f, 0xb4, 0x70, 0xaf, 0x2f, 0x32, 0xd3, + 0x5e, 0xbf, 0x86, 0xfb, 0x12, 0x90, 0x51, 0xff, 0x33, 0x1b, 0xad, 0xa8, 0x4c, 0x46, 0xeb, 0xe7, + 0x93, 0xd1, 0x4b, 0x94, 0x97, 0xe9, 0xe8, 0x64, 0x10, 0x27, 0xa5, 0xef, 0x01, 0x70, 0xc7, 0x21, + 0x96, 0x6e, 0x84, 0x72, 0xe1, 0xab, 0x2f, 0x18, 0xb9, 0x1f, 0x75, 0x02, 0x71, 0x49, 0x52, 0xd7, + 0x42, 0xf4, 0x21, 0x54, 0x4c, 0x3a, 0xf4, 0x1c, 0x22, 0x99, 0x67, 0xaf, 0x64, 0x2e, 0xc7, 0xf4, + 0xb5, 0x30, 0x99, 0x0e, 0xe7, 0xce, 0x27, 0xec, 0xbf, 0x99, 0x89, 0x2c, 0x93, 0x92, 0x01, 0x57, + 0xa0, 0xb8, 0xd3, 0x6d, 0xd6, 0xfa, 0xad, 0xf6, 0x23, 0x25, 0x83, 0x00, 0x0a, 0xdc, 0xd4, 0x4d, + 0x25, 0xcb, 0x32, 0xf7, 0x46, 0x67, 0xbb, 0xbb, 0xa5, 0xf1, 0x88, 0x85, 0x96, 0x40, 0x89, 0x8c, + 0xad, 0x73, 0x43, 0x6a, 0x4d, 0x25, 0x87, 0xae, 0xc3, 0x62, 0x0c, 0x95, 0x9c, 0x79, 0x74, 0x03, + 0x50, 0x0c, 0x9c, 0x88, 0x28, 0xa8, 0xbf, 0x0e, 0x8b, 0x0d, 0xea, 0x86, 0x86, 0xed, 0xc6, 0x55, + 0xcd, 0x7d, 0x36, 0x69, 0x09, 0xd2, 0x6d, 0xd9, 0x21, 0xab, 0x2f, 0x9e, 0x9d, 0xae, 0x96, 0x63, + 0xd2, 0x56, 0x93, 0x67, 0xa1, 0x72, 0x60, 0xb1, 0xfd, 0xeb, 0xd9, 0x16, 0x37, 0x6e, 0xbe, 0x3e, + 0x77, 0x76, 0xba, 0x3a, 0xdb, 0x6d, 0x35, 0x31, 0x83, 0xa1, 0x97, 0xa1, 0x44, 0x9e, 0xdb, 0xa1, + 0x6e, 0xb2, 0x53, 0x8d, 0x19, 0x30, 0x8f, 0x8b, 0x0c, 0xd0, 0xa0, 0x16, 0x51, 0xeb, 0x00, 0x5d, + 0xea, 0x87, 0xf2, 0xcb, 0xef, 0x40, 0xde, 0xa3, 0x3e, 0xef, 0xe9, 0x5c, 0xd8, 0x69, 0x64, 0xe4, + 0xc2, 0x51, 0xb1, 0x20, 0x56, 0xbf, 0x37, 0x0b, 0xd0, 0x37, 0x82, 0x03, 0x29, 0xe4, 0x21, 0x94, + 0xe2, 0xae, 0xae, 0x6c, 0x0e, 0x5d, 0xba, 0xda, 0x31, 0x31, 0x7a, 0x10, 0x39, 0x9b, 0xa8, 0xd7, + 0x52, 0xcb, 0xea, 0xe8, 0x43, 0x69, 0x25, 0xcf, 0xf9, 0xa2, 0x8c, 0x25, 0x09, 0xc4, 0xf7, 0xe5, + 0xca, 0xb3, 0x9f, 0xa8, 0xc1, 0x8f, 0x05, 0x61, 0x34, 0x99, 0xa8, 0xa7, 0xb6, 0xc3, 0xa6, 0x56, + 0x64, 0x73, 0x06, 0x4f, 0xf8, 0xd0, 0x47, 0x50, 0x66, 0xf3, 0xd6, 0x03, 0x8e, 0x93, 0x39, 0xfa, + 0x85, 0xa6, 0x12, 0x12, 0x30, 0x78, 0x13, 0x2b, 0xbf, 0x02, 0x60, 0x78, 0x9e, 0x63, 0x13, 0x4b, + 0xdf, 0x1d, 0xf3, 0xa4, 0xbc, 0x84, 0x4b, 0x12, 0x52, 0x1f, 0xb3, 0xed, 0x12, 0xa1, 0x8d, 0x90, + 0x17, 0x26, 0x57, 0x18, 0x50, 0x52, 0xd7, 0xc2, 0xba, 0x02, 0x0b, 0xfe, 0xc8, 0x65, 0x06, 0x95, + 0xda, 0xa9, 0x7f, 0x92, 0x85, 0x97, 0xda, 0x24, 0x3c, 0xa2, 0xfe, 0x41, 0x2d, 0x0c, 0x0d, 0x73, + 0x7f, 0x48, 0x5c, 0xb9, 0x7c, 0x89, 0x22, 0x2a, 0x73, 0xae, 0x88, 0x5a, 0x86, 0x39, 0xc3, 0xb1, + 0x8d, 0x80, 0x88, 0xd4, 0xaf, 0x84, 0xa3, 0x21, 0x2b, 0xf5, 0x58, 0xe1, 0x48, 0x82, 0x80, 0x88, + 0x4e, 0x0f, 0x53, 0x3c, 0x02, 0xa0, 0x6f, 0xc3, 0x0d, 0x99, 0xe4, 0x19, 0xf1, 0xa7, 0x58, 0xed, + 0x11, 0x35, 0xae, 0xb5, 0xd4, 0x4a, 0x36, 0x5d, 0x39, 0x99, 0x05, 0x4e, 0xc0, 0x1d, 0x2f, 0x94, + 0x39, 0xe5, 0x92, 0x95, 0x82, 0xaa, 0x3e, 0x82, 0x9b, 0x17, 0xb2, 0x7c, 0xa1, 0x4e, 0xd2, 0xdf, + 0x67, 0x01, 0x5a, 0xdd, 0xda, 0xb6, 0x34, 0x52, 0x13, 0x0a, 0x7b, 0xc6, 0xd0, 0x76, 0xc6, 0x97, + 0x45, 0xc0, 0x09, 0xfd, 0x7a, 0x4d, 0x98, 0x63, 0x83, 0xf3, 0x60, 0xc9, 0xcb, 0xeb, 0xd8, 0xd1, + 0xae, 0x4b, 0xc2, 0xb8, 0x8e, 0xe5, 0x23, 0xa6, 0x86, 0x6f, 0xb8, 0xb1, 0xeb, 0x8a, 0x01, 0x5b, + 0x00, 0x96, 0xf2, 0x1c, 0x19, 0xe3, 0x28, 0x6c, 0xc9, 0x21, 0xda, 0xe4, 0x5d, 0x63, 0xe2, 0x1f, + 0x12, 0x6b, 0x39, 0xcf, 0x8d, 0x7a, 0x95, 0x3e, 0x58, 0x92, 0x0b, 0xdb, 0xc5, 0xdc, 0xd5, 0x0f, + 0x78, 0xca, 0x34, 0x41, 0x7d, 0x21, 0x1b, 0xdd, 0x85, 0xf9, 0x73, 0xf3, 0x7c, 0xa1, 0x81, 0xd0, + 0xea, 0x3e, 0x79, 0x47, 0xc9, 0xc9, 0x5f, 0xef, 0x2a, 0x05, 0xf5, 0xaf, 0x67, 0x45, 0xa0, 0x91, + 0x56, 0x4d, 0xbf, 0x2d, 0x29, 0x72, 0xef, 0x36, 0xa9, 0x23, 0x03, 0xc0, 0x1b, 0x97, 0xc7, 0x1f, + 0x56, 0x47, 0x72, 0x72, 0x1c, 0x33, 0xa2, 0x55, 0x28, 0x0b, 0x2f, 0xd6, 0xd9, 0x86, 0xe3, 0x66, + 0x9d, 0xc7, 0x20, 0x40, 0x8c, 0x13, 0xdd, 0x86, 0x05, 0xde, 0x70, 0x0a, 0xf6, 0x89, 0x25, 0x68, + 0x72, 0x9c, 0x66, 0x3e, 0x86, 0x72, 0xb2, 0x6d, 0xa8, 0x48, 0x80, 0xce, 0xab, 0x81, 0x3c, 0x57, + 0xe8, 0xad, 0xab, 0x14, 0x12, 0x2c, 0xbc, 0x48, 0x28, 0x7b, 0x93, 0x81, 0xfa, 0xcb, 0x50, 0x8c, + 0x94, 0x45, 0xcb, 0x30, 0xdb, 0x6f, 0x74, 0x95, 0x99, 0xea, 0xe2, 0xf1, 0xc9, 0x5a, 0x39, 0x02, + 0xf7, 0x1b, 0x5d, 0x86, 0xd9, 0x69, 0x76, 0x95, 0xcc, 0x79, 0xcc, 0x4e, 0xb3, 0x8b, 0xaa, 0x90, + 0xeb, 0x35, 0xfa, 0xdd, 0x28, 0x3f, 0x8b, 0x50, 0x0c, 0x56, 0xcd, 0xb1, 0xfc, 0x4c, 0xdd, 0x83, + 0x72, 0xe2, 0xeb, 0xe8, 0x35, 0x98, 0x6b, 0xb5, 0x1f, 0x61, 0xad, 0xd7, 0x53, 0x66, 0x44, 0x79, + 0x90, 0xc0, 0xb6, 0xdc, 0x01, 0x5b, 0x3b, 0xf4, 0x0a, 0xe4, 0x36, 0x3b, 0xec, 0xdc, 0x17, 0xf5, + 0x47, 0x82, 0x62, 0x93, 0x06, 0x61, 0xf5, 0xba, 0x4c, 0xfc, 0x92, 0x82, 0xd5, 0xdf, 0xcf, 0x40, + 0x41, 0x6c, 0xb4, 0xd4, 0x45, 0xac, 0x4d, 0x8a, 0x22, 0x51, 0x36, 0xbe, 0x71, 0x71, 0x89, 0xb7, + 0x2e, 0x2b, 0x32, 0xe1, 0x9a, 0x11, 0x5f, 0xf5, 0x7d, 0xa8, 0x24, 0x11, 0x5f, 0xc8, 0x31, 0xbf, + 0x0d, 0x65, 0xe6, 0xfb, 0x51, 0xa9, 0x77, 0x1f, 0x0a, 0x22, 0x58, 0xc4, 0xe7, 0xd0, 0xc5, 0xf5, + 0xa6, 0xa4, 0x44, 0x0f, 0x61, 0x4e, 0xd4, 0xa8, 0x51, 0x2f, 0x7b, 0xe5, 0xf2, 0x1d, 0x86, 0x23, + 0x72, 0xf5, 0x23, 0xc8, 0x75, 0x09, 0xf1, 0x99, 0xed, 0x5d, 0x6a, 0x91, 0xc9, 0xd1, 0x2d, 0xcb, + 0x6b, 0x8b, 0xb4, 0x9a, 0xac, 0xbc, 0xb6, 0x48, 0xcb, 0x8a, 0xfb, 0x71, 0xd9, 0x44, 0x3f, 0xae, + 0x0f, 0x95, 0xa7, 0xc4, 0x1e, 0xec, 0x87, 0xc4, 0xe2, 0x82, 0xde, 0x86, 0x9c, 0x47, 0x62, 0xe5, + 0x97, 0x53, 0x9d, 0x8f, 0x10, 0x1f, 0x73, 0x2a, 0x16, 0x63, 0x8e, 0x38, 0xb7, 0xbc, 0x06, 0x92, + 0x23, 0xf5, 0xef, 0xb2, 0xb0, 0xd0, 0x0a, 0x82, 0x91, 0xe1, 0x9a, 0x51, 0x56, 0xf7, 0xf5, 0xf3, + 0x59, 0x5d, 0xea, 0x7d, 0xd9, 0x79, 0x96, 0xf3, 0x6d, 0x46, 0x79, 0xb2, 0x66, 0xe3, 0x93, 0x55, + 0xfd, 0x8f, 0x4c, 0xd4, 0x4b, 0xbc, 0x9d, 0x08, 0x05, 0xa2, 0x46, 0x4c, 0x4a, 0x22, 0x3b, 0xee, + 0x81, 0x4b, 0x8f, 0x5c, 0x56, 0xbd, 0x62, 0xad, 0xad, 0x3d, 0x55, 0x32, 0xc2, 0x3d, 0xcf, 0x11, + 0x61, 0xe2, 0x92, 0x23, 0x26, 0xa9, 0xab, 0xb5, 0x9b, 0x2c, 0x0b, 0xcb, 0xa6, 0x48, 0xea, 0x12, + 0xd7, 0xb2, 0xdd, 0x01, 0x7a, 0x0d, 0x0a, 0xad, 0x5e, 0x6f, 0x87, 0x97, 0x90, 0x2f, 0x1d, 0x9f, + 0xac, 0x5d, 0x3f, 0x47, 0xc5, 0xfb, 0xc8, 0x16, 0x23, 0x62, 0x25, 0x10, 0xcb, 0xcf, 0x52, 0x88, + 0x58, 0x6e, 0x2d, 0x88, 0x70, 0xa7, 0x5f, 0xeb, 0x6b, 0x4a, 0x3e, 0x85, 0x08, 0x53, 0xf6, 0x57, + 0x6e, 0xb7, 0x7f, 0xce, 0x82, 0x52, 0x33, 0x4d, 0xe2, 0x85, 0x0c, 0x2f, 0xab, 0xce, 0x3e, 0x14, + 0x3d, 0xf6, 0xcb, 0x26, 0x51, 0x06, 0xf5, 0x30, 0xf5, 0xc6, 0x77, 0x8a, 0x6f, 0x1d, 0x53, 0x87, + 0xd4, 0xac, 0xa1, 0x1d, 0x04, 0x36, 0x75, 0x05, 0x0c, 0xc7, 0x92, 0xaa, 0xff, 0x95, 0x81, 0xeb, + 0x29, 0x14, 0xe8, 0x2e, 0xe4, 0x7c, 0xea, 0x44, 0x6b, 0x78, 0xeb, 0xa2, 0x36, 0x31, 0x63, 0xc5, + 0x9c, 0x12, 0xad, 0x00, 0x18, 0xa3, 0x90, 0x1a, 0xfc, 0xfb, 0xa2, 0xb9, 0x86, 0x13, 0x10, 0xf4, + 0x14, 0x0a, 0x01, 0x31, 0x7d, 0x12, 0xe5, 0xd9, 0x1f, 0xfd, 0xb4, 0xda, 0xaf, 0xf7, 0xb8, 0x18, + 0x2c, 0xc5, 0x55, 0xd7, 0xa1, 0x20, 0x20, 0xcc, 0xed, 0x2d, 0x23, 0x34, 0xe4, 0x25, 0x02, 0xff, + 0xcd, 0xbc, 0xc9, 0x70, 0x06, 0x91, 0x37, 0x19, 0xce, 0x40, 0xfd, 0xab, 0x2c, 0x80, 0xf6, 0x3c, + 0x24, 0xbe, 0x6b, 0x38, 0x8d, 0x1a, 0xd2, 0x12, 0x27, 0x83, 0x98, 0xed, 0x57, 0x52, 0xef, 0x4d, + 0x62, 0x8e, 0xf5, 0x46, 0x2d, 0xe5, 0x6c, 0xb8, 0x09, 0xb3, 0x23, 0x5f, 0x5e, 0xe2, 0x8b, 0x1c, + 0x79, 0x07, 0x6f, 0x61, 0x06, 0x43, 0x5a, 0xb2, 0x97, 0x73, 0xe1, 0x55, 0x7d, 0xe2, 0x03, 0xa9, + 0xa1, 0x8b, 0xed, 0x7c, 0xd3, 0xd0, 0x4d, 0x22, 0x4f, 0x95, 0x8a, 0xd8, 0xf9, 0x8d, 0x5a, 0x83, + 0xf8, 0x21, 0x2e, 0x98, 0x06, 0xfb, 0xff, 0x33, 0xc5, 0xb7, 0xb7, 0x01, 0x26, 0x53, 0x43, 0x2b, + 0x90, 0x6f, 0x6c, 0xf4, 0x7a, 0x5b, 0xca, 0x8c, 0x08, 0xe0, 0x13, 0x14, 0x07, 0xab, 0x7f, 0x9e, + 0x85, 0x62, 0xa3, 0x26, 0x8f, 0xdc, 0x06, 0x28, 0x3c, 0x2a, 0xf1, 0xab, 0x17, 0xf2, 0xdc, 0xb3, + 0xfd, 0xb1, 0x0c, 0x2c, 0x97, 0x14, 0xbc, 0x0b, 0x8c, 0x85, 0x69, 0xad, 0x71, 0x06, 0x84, 0xa1, + 0x42, 0xa4, 0x11, 0x74, 0xd3, 0x88, 0x62, 0xfc, 0xca, 0xe5, 0xc6, 0x12, 0xa5, 0xcb, 0x64, 0x1c, + 0xe0, 0x72, 0x24, 0xa4, 0x61, 0x04, 0xe8, 0x3d, 0x58, 0x0c, 0xec, 0x81, 0x6b, 0xbb, 0x03, 0x3d, + 0x32, 0x1e, 0xbf, 0x07, 0xaa, 0x5f, 0x3b, 0x3b, 0x5d, 0x9d, 0xef, 0x09, 0x94, 0xb4, 0xe1, 0xbc, + 0xa4, 0x6c, 0x70, 0x53, 0xa2, 0x77, 0x61, 0x21, 0xc1, 0xca, 0xac, 0x28, 0xcc, 0xce, 0x3b, 0xc6, + 0x31, 0xe7, 0x63, 0x32, 0xc6, 0x95, 0x98, 0xf1, 0x31, 0xe1, 0xbd, 0x99, 0x3d, 0xea, 0x9b, 0x44, + 0xf7, 0xf9, 0x9e, 0xe6, 0xa7, 0x7b, 0x0e, 0x97, 0x39, 0x4c, 0x6c, 0x73, 0xf5, 0x09, 0x5c, 0xef, + 0xf8, 0xe6, 0x3e, 0x09, 0x42, 0x61, 0x0a, 0x69, 0xc5, 0x8f, 0xe0, 0x56, 0x68, 0x04, 0x07, 0xfa, + 0xbe, 0x1d, 0x84, 0xd4, 0x1f, 0xeb, 0x3e, 0x09, 0x89, 0xcb, 0xf0, 0x3a, 0xbf, 0xe0, 0x96, 0xed, + 0xc4, 0x9b, 0x8c, 0x66, 0x53, 0x90, 0xe0, 0x88, 0x62, 0x8b, 0x11, 0xa8, 0x2d, 0xa8, 0xb0, 0x12, + 0x46, 0x36, 0xd5, 0xd8, 0xec, 0xc1, 0xa1, 0x03, 0xfd, 0x73, 0x1f, 0x53, 0x25, 0x87, 0x0e, 0xc4, + 0x4f, 0xf5, 0x9b, 0xa0, 0x34, 0xed, 0xc0, 0x33, 0x42, 0x73, 0x3f, 0xea, 0x93, 0xa2, 0x26, 0x28, + 0xfb, 0xc4, 0xf0, 0xc3, 0x5d, 0x62, 0x84, 0xba, 0x47, 0x7c, 0x9b, 0x5a, 0x57, 0xaf, 0xf2, 0x62, + 0xcc, 0xd2, 0xe5, 0x1c, 0xea, 0xff, 0x64, 0x00, 0xb0, 0xb1, 0x17, 0x65, 0x6b, 0x5f, 0x85, 0x6b, + 0x81, 0x6b, 0x78, 0xc1, 0x3e, 0x0d, 0x75, 0xdb, 0x0d, 0x89, 0x7f, 0x68, 0x38, 0xb2, 0xb9, 0xa3, + 0x44, 0x88, 0x96, 0x84, 0xa3, 0xb7, 0x01, 0x1d, 0x10, 0xe2, 0xe9, 0xd4, 0xb1, 0xf4, 0x08, 0x29, + 0x2e, 0xbe, 0x73, 0x58, 0x61, 0x98, 0x8e, 0x63, 0xf5, 0x22, 0x38, 0xaa, 0xc3, 0x0a, 0x9b, 0x3e, + 0x71, 0x43, 0xdf, 0x26, 0x81, 0xbe, 0x47, 0x7d, 0x3d, 0x70, 0xe8, 0x91, 0xbe, 0x47, 0x1d, 0x87, + 0x1e, 0x11, 0x3f, 0xea, 0x9b, 0x55, 0x1d, 0x3a, 0xd0, 0x04, 0xd1, 0x06, 0xf5, 0x7b, 0x0e, 0x3d, + 0xda, 0x88, 0x28, 0x58, 0x4a, 0x37, 0x99, 0x73, 0x68, 0x9b, 0x07, 0x51, 0x4a, 0x17, 0x43, 0xfb, + 0xb6, 0x79, 0x80, 0x5e, 0x83, 0x79, 0xe2, 0x10, 0xde, 0x3e, 0x11, 0x54, 0x79, 0x4e, 0x55, 0x89, + 0x80, 0x8c, 0x48, 0xfd, 0x18, 0x14, 0xcd, 0x35, 0xfd, 0xb1, 0x97, 0x58, 0xf3, 0xb7, 0x01, 0xb1, + 0x20, 0xa9, 0x3b, 0xd4, 0x3c, 0xd0, 0x87, 0x86, 0x6b, 0x0c, 0x98, 0x5e, 0xe2, 0xc6, 0x51, 0x61, + 0x98, 0x2d, 0x6a, 0x1e, 0x6c, 0x4b, 0xb8, 0xfa, 0x1e, 0x40, 0xcf, 0xf3, 0x89, 0x61, 0x75, 0x58, + 0x36, 0xc1, 0x4c, 0xc7, 0x47, 0xba, 0x25, 0xef, 0x73, 0xa9, 0x2f, 0xb7, 0xba, 0x22, 0x10, 0xcd, + 0x18, 0xae, 0xfe, 0x22, 0x5c, 0xef, 0x3a, 0x86, 0xc9, 0x5f, 0x58, 0x74, 0xe3, 0x2b, 0x34, 0xf4, + 0x10, 0x0a, 0x82, 0x54, 0xae, 0x64, 0xea, 0x76, 0x9b, 0x7c, 0x73, 0x73, 0x06, 0x4b, 0xfa, 0x7a, + 0x05, 0x60, 0x22, 0x47, 0xfd, 0xc7, 0x0c, 0x94, 0x62, 0xf9, 0x68, 0x4d, 0x5c, 0x80, 0x85, 0xbe, + 0x61, 0xbb, 0xb2, 0xe2, 0x2f, 0xe1, 0x24, 0x08, 0xb5, 0xa0, 0xec, 0xc5, 0xdc, 0x97, 0xe6, 0x73, + 0x29, 0x5a, 0xe3, 0x24, 0x2f, 0x7a, 0x1f, 0x4a, 0xd1, 0x05, 0x7a, 0x14, 0x61, 0x2f, 0xbf, 0x6f, + 0x9f, 0x90, 0x47, 0x8d, 0x54, 0x9f, 0x78, 0x8e, 0xcd, 0x62, 0x4e, 0x2e, 0x6e, 0xa4, 0x62, 0x09, + 0x52, 0xbf, 0x0e, 0xf0, 0x0d, 0x6a, 0xbb, 0x7d, 0x7a, 0x40, 0x5c, 0x7e, 0x2b, 0xcc, 0x4a, 0x4a, + 0x12, 0x19, 0x5a, 0x8e, 0x78, 0xa7, 0x40, 0xac, 0x52, 0x7c, 0x39, 0x2a, 0x86, 0xea, 0x5f, 0x66, + 0xa1, 0x80, 0x29, 0x0d, 0x1b, 0x35, 0xb4, 0x06, 0x05, 0x19, 0x4a, 0xf8, 0x11, 0x55, 0x2f, 0x9d, + 0x9d, 0xae, 0xe6, 0x45, 0x0c, 0xc9, 0x9b, 0x3c, 0x78, 0x24, 0x82, 0x7c, 0xf6, 0xa2, 0x20, 0x8f, + 0xee, 0x42, 0x45, 0x12, 0xe9, 0xfb, 0x46, 0xb0, 0x2f, 0xea, 0xbb, 0xfa, 0xc2, 0xd9, 0xe9, 0x2a, + 0x08, 0xca, 0x4d, 0x23, 0xd8, 0xc7, 0x20, 0xa8, 0xd9, 0x6f, 0xa4, 0x41, 0xf9, 0x19, 0xb5, 0x5d, + 0x3d, 0xe4, 0x93, 0x90, 0xbd, 0xc8, 0xd4, 0xa5, 0x9e, 0x4c, 0x55, 0x3e, 0xa0, 0x80, 0x67, 0x93, + 0xc9, 0x6b, 0x30, 0xef, 0x53, 0x1a, 0x8a, 0xc8, 0x66, 0x53, 0x57, 0xb6, 0x39, 0xd6, 0x52, 0xbb, + 0xdf, 0x94, 0x86, 0x58, 0xd2, 0xe1, 0x8a, 0x9f, 0x18, 0xa1, 0xbb, 0xb0, 0xe4, 0x18, 0x41, 0xa8, + 0xf3, 0x90, 0x68, 0x4d, 0xa4, 0x15, 0xb8, 0xf1, 0x11, 0xc3, 0x6d, 0x70, 0x54, 0xc4, 0xa1, 0xfe, + 0x43, 0x06, 0xca, 0x6c, 0x32, 0xf6, 0x9e, 0x6d, 0xb2, 0x3c, 0xf0, 0x8b, 0xa7, 0x27, 0x37, 0x61, + 0xd6, 0x0c, 0x7c, 0x69, 0x54, 0x7e, 0x3e, 0x37, 0x7a, 0x18, 0x33, 0x18, 0xfa, 0x18, 0x0a, 0xb2, + 0xdd, 0x22, 0x32, 0x13, 0xf5, 0xea, 0x8c, 0x55, 0xda, 0x46, 0xf2, 0x71, 0x77, 0x9f, 0x68, 0x27, + 0xce, 0x09, 0x9c, 0x04, 0xa1, 0x1b, 0x90, 0x35, 0x85, 0xb9, 0xe4, 0x0b, 0x9d, 0x46, 0x1b, 0x67, + 0x4d, 0x57, 0xfd, 0x51, 0x06, 0xe6, 0x27, 0x31, 0x81, 0x79, 0xc0, 0x2d, 0x28, 0x05, 0xa3, 0xdd, + 0x60, 0x1c, 0x84, 0x64, 0x18, 0xdd, 0x78, 0xc7, 0x00, 0xd4, 0x82, 0x92, 0xe1, 0x0c, 0xa8, 0x6f, + 0x87, 0xfb, 0x43, 0x59, 0xc8, 0xa6, 0x67, 0x13, 0x49, 0x99, 0xeb, 0xb5, 0x88, 0x05, 0x4f, 0xb8, + 0xa3, 0xd4, 0x40, 0x3c, 0x8b, 0xe0, 0xa9, 0xc1, 0xab, 0x50, 0x71, 0x8c, 0x21, 0xef, 0x3f, 0x85, + 0xf6, 0x90, 0x44, 0x9b, 0x41, 0xc2, 0xfa, 0xf6, 0x90, 0xa8, 0x2a, 0x94, 0x62, 0x61, 0x68, 0x11, + 0xca, 0x35, 0xad, 0xa7, 0xdf, 0xbb, 0xff, 0x50, 0x7f, 0xd4, 0xd8, 0x56, 0x66, 0x64, 0xfa, 0xfa, + 0x67, 0x19, 0x98, 0x97, 0x11, 0x4b, 0x96, 0x04, 0xaf, 0xc1, 0x9c, 0x6f, 0xec, 0x85, 0x51, 0xd1, + 0x92, 0x13, 0x5e, 0xcd, 0x0e, 0x01, 0x56, 0xb4, 0x30, 0x54, 0x7a, 0xd1, 0x92, 0x78, 0x83, 0x31, + 0x7b, 0xe9, 0x1b, 0x8c, 0xdc, 0xff, 0xcb, 0x1b, 0x0c, 0xf5, 0x37, 0x00, 0x36, 0x6c, 0x87, 0xf4, + 0x45, 0xab, 0x2a, 0xad, 0x04, 0x65, 0x69, 0x9e, 0x6c, 0x85, 0x46, 0x69, 0x5e, 0xab, 0x89, 0x19, + 0x8c, 0xa1, 0x06, 0xb6, 0x25, 0x37, 0x23, 0x47, 0x3d, 0x62, 0xa8, 0x81, 0x6d, 0xc5, 0xd7, 0x7e, + 0xb9, 0x2b, 0xae, 0xfd, 0xd4, 0x45, 0x98, 0xc7, 0xa2, 0xc7, 0x26, 0x74, 0x50, 0x4f, 0x32, 0xb0, + 0x28, 0xf3, 0xdd, 0x38, 0x64, 0x7f, 0x05, 0x4a, 0x22, 0xf5, 0x9d, 0x14, 0x81, 0xfc, 0x21, 0x82, + 0xa0, 0x6b, 0x35, 0x71, 0x51, 0xa0, 0x5b, 0x16, 0x5a, 0x85, 0xb2, 0x24, 0x4d, 0x3c, 0xef, 0x02, + 0x01, 0x6a, 0xb3, 0xf9, 0xbc, 0x03, 0xb9, 0x3d, 0xdb, 0x21, 0xd2, 0xf3, 0x53, 0x23, 0xc2, 0xc4, + 0x22, 0x9b, 0x33, 0x98, 0x53, 0xd7, 0x8b, 0x51, 0x73, 0x4f, 0xfd, 0x97, 0x0c, 0x6f, 0x31, 0xb3, + 0x52, 0x35, 0xa9, 0x9f, 0xa8, 0x5a, 0xa7, 0xf4, 0x13, 0x74, 0x4c, 0x3f, 0x81, 0x16, 0xfa, 0x49, + 0xd2, 0xa4, 0x7e, 0x02, 0xf4, 0xd3, 0xeb, 0x87, 0x3e, 0x84, 0x39, 0xd9, 0xaa, 0x94, 0xa1, 0xee, + 0xd5, 0x54, 0xcf, 0x48, 0x5a, 0x7a, 0x73, 0x06, 0x47, 0x3c, 0x89, 0xe9, 0x6d, 0xc1, 0x8d, 0xba, + 0x63, 0x98, 0x07, 0x8e, 0x1d, 0x84, 0xc4, 0x4a, 0x46, 0xa0, 0xfb, 0x50, 0x38, 0x97, 0xe7, 0x5e, + 0xd6, 0x44, 0x95, 0x94, 0xea, 0xbf, 0x67, 0xa0, 0xb2, 0x49, 0x0c, 0x27, 0xdc, 0x9f, 0x74, 0xaa, + 0x42, 0x12, 0x84, 0xf2, 0x7c, 0xe4, 0xbf, 0xd1, 0xd7, 0xa0, 0x18, 0xa7, 0x41, 0x57, 0x5e, 0x07, + 0xc6, 0xa4, 0xe8, 0x01, 0xcc, 0x31, 0xdd, 0xe9, 0x28, 0xaa, 0xaf, 0x2e, 0xbb, 0x69, 0x92, 0x94, + 0xec, 0xd0, 0xf2, 0x09, 0xcf, 0x7b, 0xb8, 0x9d, 0xf2, 0x38, 0x1a, 0xa2, 0x9f, 0x87, 0x0a, 0xbf, + 0x28, 0x89, 0xd2, 0xbc, 0xfc, 0x55, 0x32, 0xcb, 0xe2, 0xae, 0x53, 0xa4, 0x78, 0x7f, 0x9c, 0x85, + 0xa5, 0x6d, 0x63, 0xbc, 0x4b, 0x64, 0x18, 0x22, 0x16, 0x26, 0x26, 0xf5, 0x2d, 0xd4, 0x4d, 0x86, + 0xaf, 0x4b, 0xae, 0x4e, 0xd3, 0x98, 0xd3, 0xa3, 0x58, 0x54, 0xf3, 0x65, 0x13, 0x35, 0xdf, 0x12, + 0xe4, 0x5d, 0xea, 0x9a, 0x44, 0xc6, 0x36, 0x31, 0x50, 0xbf, 0x93, 0x49, 0xc6, 0xae, 0x6a, 0x7c, + 0xad, 0xc9, 0x9b, 0x5e, 0x6d, 0x1a, 0xc6, 0x9f, 0x43, 0x1f, 0x43, 0xb5, 0xa7, 0x35, 0xb0, 0xd6, + 0xaf, 0x77, 0xbe, 0xa9, 0xf7, 0x6a, 0x5b, 0xbd, 0xda, 0xfd, 0xbb, 0x7a, 0xb7, 0xb3, 0xf5, 0xad, + 0x7b, 0x0f, 0xee, 0x7e, 0x4d, 0xc9, 0x54, 0xd7, 0x8e, 0x4f, 0xd6, 0x6e, 0xb5, 0x6b, 0x8d, 0x2d, + 0xb1, 0xe3, 0x76, 0xe9, 0xf3, 0x9e, 0xe1, 0x04, 0xc6, 0xfd, 0xbb, 0x5d, 0xea, 0x8c, 0x19, 0x0d, + 0xfa, 0x2a, 0xa0, 0x0d, 0x0d, 0xb7, 0xb5, 0xbe, 0x1e, 0x05, 0xc8, 0x46, 0xbd, 0xa1, 0x64, 0x45, + 0x25, 0xb5, 0x41, 0x7c, 0x97, 0x84, 0x35, 0xad, 0x77, 0xef, 0xfe, 0xc3, 0x46, 0xbd, 0xc1, 0xf6, + 0x78, 0x25, 0x79, 0x5a, 0x26, 0x93, 0x80, 0xcc, 0x85, 0x49, 0xc0, 0x24, 0x97, 0xc8, 0x5e, 0x90, + 0x4b, 0x6c, 0xc0, 0x92, 0xe9, 0xd3, 0x20, 0xd0, 0x59, 0x79, 0x42, 0xac, 0xa9, 0x02, 0xe8, 0x4b, + 0x67, 0xa7, 0xab, 0xd7, 0x1a, 0x0c, 0xdf, 0xe3, 0x68, 0x29, 0xfe, 0x9a, 0x99, 0x00, 0xf1, 0x2f, + 0xa9, 0xdf, 0x9f, 0x65, 0x99, 0x9e, 0x7d, 0x68, 0x3b, 0x64, 0x40, 0x02, 0xf4, 0x04, 0x16, 0x4d, + 0x9f, 0x58, 0xac, 0xee, 0x30, 0x9c, 0xe4, 0xcb, 0xe8, 0x9f, 0x4b, 0x4d, 0xba, 0x62, 0xc6, 0xf5, + 0x46, 0xcc, 0xd5, 0xf3, 0x88, 0x89, 0x17, 0xcc, 0x73, 0x63, 0xf4, 0x0c, 0x16, 0x03, 0xe2, 0xd8, + 0xee, 0xe8, 0xb9, 0x6e, 0x52, 0x37, 0x24, 0xcf, 0xa3, 0xeb, 0xbc, 0xab, 0xe4, 0xf6, 0xb4, 0x2d, + 0xc6, 0xd5, 0x10, 0x4c, 0x75, 0x74, 0x76, 0xba, 0xba, 0x70, 0x1e, 0x86, 0x17, 0xa4, 0x64, 0x39, + 0xae, 0xee, 0xc3, 0xc2, 0x79, 0x6d, 0xd0, 0x92, 0x0c, 0x34, 0x3c, 0x5e, 0xc5, 0x81, 0xe4, 0x16, + 0x14, 0x7d, 0x32, 0xb0, 0x83, 0xd0, 0x17, 0x66, 0x66, 0x98, 0x18, 0x82, 0x96, 0xa1, 0x90, 0x78, + 0x71, 0xc2, 0x70, 0x72, 0xcc, 0x22, 0x88, 0x78, 0x4c, 0x56, 0xfd, 0x35, 0x98, 0xd2, 0x85, 0x6d, + 0x3a, 0xcb, 0x0e, 0x8c, 0x5d, 0xf9, 0xb1, 0x22, 0x8e, 0x86, 0xcc, 0x97, 0x47, 0x41, 0x9c, 0x40, + 0xf2, 0xdf, 0x0c, 0xc6, 0x33, 0x1d, 0xf9, 0xb4, 0x8e, 0xe7, 0x32, 0xd1, 0x0b, 0xde, 0x5c, 0xe2, + 0x05, 0xef, 0x12, 0xe4, 0x1d, 0x72, 0x48, 0x1c, 0x91, 0x63, 0x60, 0x31, 0xe0, 0x3e, 0xff, 0x0d, + 0xba, 0x2b, 0x8f, 0xe1, 0x0d, 0x98, 0x7f, 0x46, 0x77, 0x75, 0x3b, 0x24, 0xfe, 0xe4, 0x61, 0x55, + 0xf9, 0xfe, 0xcb, 0x69, 0xf6, 0x95, 0x0f, 0x79, 0x65, 0xa2, 0x53, 0x79, 0x46, 0x77, 0x5b, 0x11, + 0x1b, 0xaa, 0xc1, 0x02, 0xcf, 0xdf, 0xc8, 0x73, 0x62, 0x8e, 0xb8, 0xa0, 0xab, 0xef, 0x5d, 0xe7, + 0x19, 0x87, 0x16, 0x31, 0xbc, 0x75, 0x17, 0x2a, 0xd1, 0x1b, 0x56, 0xfe, 0x98, 0xa5, 0x08, 0xb9, + 0x7e, 0xad, 0xf7, 0x58, 0x99, 0x41, 0x00, 0x05, 0xb1, 0xf9, 0xc4, 0xed, 0x68, 0xa3, 0xd3, 0xde, + 0x68, 0x3d, 0x52, 0xb2, 0x6f, 0xfd, 0x5e, 0x0e, 0x4a, 0xf1, 0xfd, 0x1c, 0x3b, 0x6c, 0xdb, 0xda, + 0xd3, 0x68, 0xf7, 0xc6, 0xf0, 0x36, 0x39, 0x42, 0xaf, 0x4e, 0x3a, 0x7b, 0x1f, 0x8b, 0x07, 0x09, + 0x31, 0x3a, 0xea, 0xea, 0xbd, 0x0e, 0xc5, 0x5a, 0xaf, 0xd7, 0x7a, 0xd4, 0xd6, 0x9a, 0xca, 0xa7, + 0x99, 0xea, 0x97, 0x8e, 0x4f, 0xd6, 0xae, 0xc5, 0x44, 0xb5, 0x40, 0xec, 0x17, 0x4e, 0xd5, 0x68, + 0x68, 0xdd, 0xbe, 0xd6, 0x54, 0x3e, 0xc9, 0x4e, 0x53, 0xf1, 0x4e, 0x15, 0x7f, 0x68, 0x55, 0xea, + 0x62, 0xad, 0x5b, 0xc3, 0xec, 0x83, 0x9f, 0x66, 0x45, 0xc3, 0x71, 0xf2, 0x45, 0x9f, 0x78, 0x86, + 0xcf, 0xbe, 0xb9, 0x12, 0xbd, 0x77, 0xfc, 0x64, 0x56, 0xbc, 0xb8, 0x99, 0x5c, 0x36, 0x12, 0xc3, + 0x1a, 0xb3, 0xaf, 0xf1, 0x5b, 0x5e, 0x2e, 0x66, 0x76, 0xea, 0x6b, 0x3d, 0x16, 0x5c, 0x99, 0x14, + 0x15, 0xe6, 0xf0, 0x4e, 0xbb, 0xcd, 0x88, 0x3e, 0xc9, 0x4d, 0xcd, 0x0e, 0x8f, 0x5c, 0x97, 0xd1, + 0xdc, 0x86, 0x62, 0x74, 0x09, 0xac, 0x7c, 0x9a, 0x9b, 0x52, 0xa8, 0x11, 0xdd, 0x60, 0xf3, 0x0f, + 0x6e, 0xee, 0xf4, 0xf9, 0x73, 0xcc, 0x4f, 0xf2, 0xd3, 0x1f, 0xdc, 0x1f, 0x85, 0x16, 0x3d, 0x72, + 0x59, 0x98, 0x91, 0xbd, 0xcd, 0x4f, 0xf3, 0x22, 0x7c, 0xc5, 0x34, 0xb2, 0xb1, 0xf9, 0x3a, 0x14, + 0xb1, 0xf6, 0x0d, 0xf1, 0x72, 0xf3, 0x93, 0xc2, 0x94, 0x1c, 0x4c, 0x9e, 0x11, 0x93, 0x7d, 0x6d, + 0x0d, 0x0a, 0x58, 0xdb, 0xee, 0x3c, 0xd1, 0x94, 0x3f, 0x28, 0x4c, 0xc9, 0xc1, 0x64, 0x48, 0xf9, + 0x4b, 0xb4, 0x62, 0x07, 0x77, 0x37, 0x6b, 0x7c, 0x51, 0xa6, 0xe5, 0x74, 0x7c, 0x6f, 0xdf, 0x70, + 0x89, 0x35, 0x79, 0x75, 0x14, 0xa3, 0xde, 0xfa, 0x25, 0x28, 0x46, 0xc9, 0x3e, 0x5a, 0x81, 0xc2, + 0xd3, 0x0e, 0x7e, 0xac, 0x61, 0x65, 0x46, 0x58, 0x39, 0xc2, 0x3c, 0x15, 0x65, 0xda, 0x1a, 0xcc, + 0x6d, 0xd7, 0xda, 0xb5, 0x47, 0x1a, 0x8e, 0x2e, 0x26, 0x22, 0x02, 0x99, 0xb1, 0x56, 0x15, 0xf9, + 0x81, 0x58, 0x66, 0xfd, 0xf5, 0x1f, 0x7c, 0xb6, 0x32, 0xf3, 0xe3, 0xcf, 0x56, 0x66, 0x3e, 0x39, + 0x5b, 0xc9, 0xfc, 0xe0, 0x6c, 0x25, 0xf3, 0xc3, 0xb3, 0x95, 0xcc, 0xbf, 0x9d, 0xad, 0x64, 0x7e, + 0xe7, 0x27, 0x2b, 0x33, 0x3f, 0xfc, 0xc9, 0xca, 0xcc, 0x8f, 0x7f, 0xb2, 0x32, 0xb3, 0x5b, 0xe0, + 0x4e, 0xff, 0xe0, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0xde, 0x15, 0x00, 0xb1, 0xc6, 0x34, 0x00, + 0x00, } func (m *Version) Copy() *Version { @@ -2257,7 +4203,7 @@ func (m *Annotations) CopyFrom(src interface{}) { if o.Indices != nil { m.Indices = make([]IndexEntry, len(o.Indices)) for i := range m.Indices { - deepcopy.Copy(&m.Indices[i], &o.Indices[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Indices[i], &o.Indices[i]) } } @@ -2312,13 +4258,13 @@ func (m *GenericResource) CopyFrom(src interface{}) { v := GenericResource_NamedResourceSpec{ NamedResourceSpec: &NamedGenericResource{}, } - deepcopy.Copy(v.NamedResourceSpec, o.GetNamedResourceSpec()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.NamedResourceSpec, o.GetNamedResourceSpec()) m.Resource = &v case *GenericResource_DiscreteResourceSpec: v := GenericResource_DiscreteResourceSpec{ DiscreteResourceSpec: &DiscreteGenericResource{}, } - deepcopy.Copy(v.DiscreteResourceSpec, o.GetDiscreteResourceSpec()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.DiscreteResourceSpec, o.GetDiscreteResourceSpec()) m.Resource = &v } } @@ -2342,7 +4288,7 @@ func (m *Resources) CopyFrom(src interface{}) { m.Generic = make([]*GenericResource, len(o.Generic)) for i := range m.Generic { m.Generic[i] = &GenericResource{} - deepcopy.Copy(m.Generic[i], o.Generic[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Generic[i], o.Generic[i]) } } @@ -2363,11 +4309,19 @@ func (m *ResourceRequirements) CopyFrom(src interface{}) { *m = *o if o.Limits != nil { m.Limits = &Resources{} - deepcopy.Copy(m.Limits, o.Limits) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Limits, o.Limits) } if o.Reservations != nil { m.Reservations = &Resources{} - deepcopy.Copy(m.Reservations, o.Reservations) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Reservations, o.Reservations) + } + if o.SwapBytes != nil { + m.SwapBytes = &types.Int64Value{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.SwapBytes, o.SwapBytes) + } + if o.MemorySwappiness != nil { + m.MemorySwappiness = &types.Int64Value{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.MemorySwappiness, o.MemorySwappiness) } } @@ -2424,7 +4378,7 @@ func (m *EngineDescription) CopyFrom(src interface{}) { if o.Plugins != nil { m.Plugins = make([]PluginDescription, len(o.Plugins)) for i := range m.Plugins { - deepcopy.Copy(&m.Plugins[i], &o.Plugins[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Plugins[i], &o.Plugins[i]) } } @@ -2445,19 +4399,19 @@ func (m *NodeDescription) CopyFrom(src interface{}) { *m = *o if o.Platform != nil { m.Platform = &Platform{} - deepcopy.Copy(m.Platform, o.Platform) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Platform, o.Platform) } if o.Resources != nil { m.Resources = &Resources{} - deepcopy.Copy(m.Resources, o.Resources) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Resources, o.Resources) } if o.Engine != nil { m.Engine = &EngineDescription{} - deepcopy.Copy(m.Engine, o.Engine) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Engine, o.Engine) } if o.TLSInfo != nil { m.TLSInfo = &NodeTLSInfo{} - deepcopy.Copy(m.TLSInfo, o.TLSInfo) + github_com_docker_swarmkit_api_deepcopy.Copy(m.TLSInfo, o.TLSInfo) } } @@ -2548,15 +4502,15 @@ func (m *Mount) CopyFrom(src interface{}) { *m = *o if o.BindOptions != nil { m.BindOptions = &Mount_BindOptions{} - deepcopy.Copy(m.BindOptions, o.BindOptions) + github_com_docker_swarmkit_api_deepcopy.Copy(m.BindOptions, o.BindOptions) } if o.VolumeOptions != nil { m.VolumeOptions = &Mount_VolumeOptions{} - deepcopy.Copy(m.VolumeOptions, o.VolumeOptions) + github_com_docker_swarmkit_api_deepcopy.Copy(m.VolumeOptions, o.VolumeOptions) } if o.TmpfsOptions != nil { m.TmpfsOptions = &Mount_TmpfsOptions{} - deepcopy.Copy(m.TmpfsOptions, o.TmpfsOptions) + github_com_docker_swarmkit_api_deepcopy.Copy(m.TmpfsOptions, o.TmpfsOptions) } } @@ -2597,7 +4551,7 @@ func (m *Mount_VolumeOptions) CopyFrom(src interface{}) { if o.DriverConfig != nil { m.DriverConfig = &Driver{} - deepcopy.Copy(m.DriverConfig, o.DriverConfig) + github_com_docker_swarmkit_api_deepcopy.Copy(m.DriverConfig, o.DriverConfig) } } @@ -2630,12 +4584,12 @@ func (m *RestartPolicy) CopyFrom(src interface{}) { o := src.(*RestartPolicy) *m = *o if o.Delay != nil { - m.Delay = &google_protobuf1.Duration{} - deepcopy.Copy(m.Delay, o.Delay) + m.Delay = &types.Duration{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Delay, o.Delay) } if o.Window != nil { - m.Window = &google_protobuf1.Duration{} - deepcopy.Copy(m.Window, o.Window) + m.Window = &types.Duration{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Window, o.Window) } } @@ -2652,10 +4606,10 @@ func (m *UpdateConfig) CopyFrom(src interface{}) { o := src.(*UpdateConfig) *m = *o - deepcopy.Copy(&m.Delay, &o.Delay) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Delay, &o.Delay) if o.Monitor != nil { - m.Monitor = &google_protobuf1.Duration{} - deepcopy.Copy(m.Monitor, o.Monitor) + m.Monitor = &types.Duration{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Monitor, o.Monitor) } } @@ -2673,12 +4627,12 @@ func (m *UpdateStatus) CopyFrom(src interface{}) { o := src.(*UpdateStatus) *m = *o if o.StartedAt != nil { - m.StartedAt = &google_protobuf.Timestamp{} - deepcopy.Copy(m.StartedAt, o.StartedAt) + m.StartedAt = &types.Timestamp{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.StartedAt, o.StartedAt) } if o.CompletedAt != nil { - m.CompletedAt = &google_protobuf.Timestamp{} - deepcopy.Copy(m.CompletedAt, o.CompletedAt) + m.CompletedAt = &types.Timestamp{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.CompletedAt, o.CompletedAt) } } @@ -2714,7 +4668,7 @@ func (m *PortStatus) CopyFrom(src interface{}) { m.Ports = make([]*PortConfig, len(o.Ports)) for i := range m.Ports { m.Ports[i] = &PortConfig{} - deepcopy.Copy(m.Ports[i], o.Ports[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Ports[i], o.Ports[i]) } } @@ -2734,16 +4688,16 @@ func (m *TaskStatus) CopyFrom(src interface{}) { o := src.(*TaskStatus) *m = *o if o.Timestamp != nil { - m.Timestamp = &google_protobuf.Timestamp{} - deepcopy.Copy(m.Timestamp, o.Timestamp) + m.Timestamp = &types.Timestamp{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Timestamp, o.Timestamp) } if o.PortStatus != nil { m.PortStatus = &PortStatus{} - deepcopy.Copy(m.PortStatus, o.PortStatus) + github_com_docker_swarmkit_api_deepcopy.Copy(m.PortStatus, o.PortStatus) } if o.AppliedAt != nil { - m.AppliedAt = &google_protobuf.Timestamp{} - deepcopy.Copy(m.AppliedAt, o.AppliedAt) + m.AppliedAt = &types.Timestamp{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.AppliedAt, o.AppliedAt) } if o.RuntimeStatus != nil { switch o.RuntimeStatus.(type) { @@ -2751,7 +4705,7 @@ func (m *TaskStatus) CopyFrom(src interface{}) { v := TaskStatus_Container{ Container: &ContainerStatus{}, } - deepcopy.Copy(v.Container, o.GetContainer()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Container, o.GetContainer()) m.RuntimeStatus = &v } } @@ -2864,13 +4818,13 @@ func (m *IPAMOptions) CopyFrom(src interface{}) { *m = *o if o.Driver != nil { m.Driver = &Driver{} - deepcopy.Copy(m.Driver, o.Driver) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Driver, o.Driver) } if o.Configs != nil { m.Configs = make([]*IPAMConfig, len(o.Configs)) for i := range m.Configs { m.Configs[i] = &IPAMConfig{} - deepcopy.Copy(m.Configs[i], o.Configs[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Configs[i], o.Configs[i]) } } @@ -2906,7 +4860,7 @@ func (m *WeightedPeer) CopyFrom(src interface{}) { *m = *o if o.Peer != nil { m.Peer = &Peer{} - deepcopy.Copy(m.Peer, o.Peer) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Peer, o.Peer) } } @@ -2942,7 +4896,7 @@ func (m *AcceptancePolicy) CopyFrom(src interface{}) { m.Policies = make([]*AcceptancePolicy_RoleAdmissionPolicy, len(o.Policies)) for i := range m.Policies { m.Policies[i] = &AcceptancePolicy_RoleAdmissionPolicy{} - deepcopy.Copy(m.Policies[i], o.Policies[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Policies[i], o.Policies[i]) } } @@ -2963,7 +4917,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) CopyFrom(src interface{}) { *m = *o if o.Secret != nil { m.Secret = &AcceptancePolicy_RoleAdmissionPolicy_Secret{} - deepcopy.Copy(m.Secret, o.Secret) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Secret, o.Secret) } } @@ -3026,14 +4980,14 @@ func (m *CAConfig) CopyFrom(src interface{}) { o := src.(*CAConfig) *m = *o if o.NodeCertExpiry != nil { - m.NodeCertExpiry = &google_protobuf1.Duration{} - deepcopy.Copy(m.NodeCertExpiry, o.NodeCertExpiry) + m.NodeCertExpiry = &types.Duration{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.NodeCertExpiry, o.NodeCertExpiry) } if o.ExternalCAs != nil { m.ExternalCAs = make([]*ExternalCA, len(o.ExternalCAs)) for i := range m.ExternalCAs { m.ExternalCAs[i] = &ExternalCA{} - deepcopy.Copy(m.ExternalCAs[i], o.ExternalCAs[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.ExternalCAs[i], o.ExternalCAs[i]) } } @@ -3077,7 +5031,7 @@ func (m *TaskDefaults) CopyFrom(src interface{}) { *m = *o if o.LogDriver != nil { m.LogDriver = &Driver{} - deepcopy.Copy(m.LogDriver, o.LogDriver) + github_com_docker_swarmkit_api_deepcopy.Copy(m.LogDriver, o.LogDriver) } } @@ -3095,8 +5049,8 @@ func (m *DispatcherConfig) CopyFrom(src interface{}) { o := src.(*DispatcherConfig) *m = *o if o.HeartbeatPeriod != nil { - m.HeartbeatPeriod = &google_protobuf1.Duration{} - deepcopy.Copy(m.HeartbeatPeriod, o.HeartbeatPeriod) + m.HeartbeatPeriod = &types.Duration{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.HeartbeatPeriod, o.HeartbeatPeriod) } } @@ -3164,7 +5118,7 @@ func (m *PlacementPreference) CopyFrom(src interface{}) { v := PlacementPreference_Spread{ Spread: &SpreadOver{}, } - deepcopy.Copy(v.Spread, o.GetSpread()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Spread, o.GetSpread()) m.Preference = &v } } @@ -3193,7 +5147,7 @@ func (m *Placement) CopyFrom(src interface{}) { m.Preferences = make([]*PlacementPreference, len(o.Preferences)) for i := range m.Preferences { m.Preferences[i] = &PlacementPreference{} - deepcopy.Copy(m.Preferences[i], o.Preferences[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Preferences[i], o.Preferences[i]) } } @@ -3201,7 +5155,7 @@ func (m *Placement) CopyFrom(src interface{}) { m.Platforms = make([]*Platform, len(o.Platforms)) for i := range m.Platforms { m.Platforms[i] = &Platform{} - deepcopy.Copy(m.Platforms[i], o.Platforms[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Platforms[i], o.Platforms[i]) } } @@ -3243,10 +5197,10 @@ func (m *RootCA) CopyFrom(src interface{}) { m.CACert = make([]byte, len(o.CACert)) copy(m.CACert, o.CACert) } - deepcopy.Copy(&m.JoinTokens, &o.JoinTokens) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.JoinTokens, &o.JoinTokens) if o.RootRotation != nil { m.RootRotation = &RootRotation{} - deepcopy.Copy(m.RootRotation, o.RootRotation) + github_com_docker_swarmkit_api_deepcopy.Copy(m.RootRotation, o.RootRotation) } } @@ -3267,7 +5221,7 @@ func (m *Certificate) CopyFrom(src interface{}) { m.CSR = make([]byte, len(o.CSR)) copy(m.CSR, o.CSR) } - deepcopy.Copy(&m.Status, &o.Status) + github_com_docker_swarmkit_api_deepcopy.Copy(&m.Status, &o.Status) if o.Certificate != nil { m.Certificate = make([]byte, len(o.Certificate)) copy(m.Certificate, o.Certificate) @@ -3323,6 +5277,16 @@ func (m *FileTarget) CopyFrom(src interface{}) { *m = *o } +func (m *RuntimeTarget) Copy() *RuntimeTarget { + if m == nil { + return nil + } + o := &RuntimeTarget{} + o.CopyFrom(m) + return o +} + +func (m *RuntimeTarget) CopyFrom(src interface{}) {} func (m *SecretReference) Copy() *SecretReference { if m == nil { return nil @@ -3342,7 +5306,7 @@ func (m *SecretReference) CopyFrom(src interface{}) { v := SecretReference_File{ File: &FileTarget{}, } - deepcopy.Copy(v.File, o.GetFile()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.File, o.GetFile()) m.Target = &v } } @@ -3368,7 +5332,13 @@ func (m *ConfigReference) CopyFrom(src interface{}) { v := ConfigReference_File{ File: &FileTarget{}, } - deepcopy.Copy(v.File, o.GetFile()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.File, o.GetFile()) + m.Target = &v + case *ConfigReference_Runtime: + v := ConfigReference_Runtime{ + Runtime: &RuntimeTarget{}, + } + github_com_docker_swarmkit_api_deepcopy.Copy(v.Runtime, o.GetRuntime()) m.Target = &v } } @@ -3389,8 +5359,8 @@ func (m *BlacklistedCertificate) CopyFrom(src interface{}) { o := src.(*BlacklistedCertificate) *m = *o if o.Expiry != nil { - m.Expiry = &google_protobuf.Timestamp{} - deepcopy.Copy(m.Expiry, o.Expiry) + m.Expiry = &types.Timestamp{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Expiry, o.Expiry) } } @@ -3413,16 +5383,16 @@ func (m *HealthConfig) CopyFrom(src interface{}) { } if o.Interval != nil { - m.Interval = &google_protobuf1.Duration{} - deepcopy.Copy(m.Interval, o.Interval) + m.Interval = &types.Duration{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Interval, o.Interval) } if o.Timeout != nil { - m.Timeout = &google_protobuf1.Duration{} - deepcopy.Copy(m.Timeout, o.Timeout) + m.Timeout = &types.Duration{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.Timeout, o.Timeout) } if o.StartPeriod != nil { - m.StartPeriod = &google_protobuf1.Duration{} - deepcopy.Copy(m.StartPeriod, o.StartPeriod) + m.StartPeriod = &types.Duration{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.StartPeriod, o.StartPeriod) } } @@ -3491,11 +5461,11 @@ func (m *Privileges) CopyFrom(src interface{}) { *m = *o if o.CredentialSpec != nil { m.CredentialSpec = &Privileges_CredentialSpec{} - deepcopy.Copy(m.CredentialSpec, o.CredentialSpec) + github_com_docker_swarmkit_api_deepcopy.Copy(m.CredentialSpec, o.CredentialSpec) } if o.SELinuxContext != nil { m.SELinuxContext = &Privileges_SELinuxContext{} - deepcopy.Copy(m.SELinuxContext, o.SELinuxContext) + github_com_docker_swarmkit_api_deepcopy.Copy(m.SELinuxContext, o.SELinuxContext) } } @@ -3524,6 +5494,11 @@ func (m *Privileges_CredentialSpec) CopyFrom(src interface{}) { Registry: o.GetRegistry(), } m.Source = &v + case *Privileges_CredentialSpec_Config: + v := Privileges_CredentialSpec_Config{ + Config: o.GetConfig(), + } + m.Source = &v } } @@ -3544,10 +5519,30 @@ func (m *Privileges_SELinuxContext) CopyFrom(src interface{}) { *m = *o } +func (m *JobStatus) Copy() *JobStatus { + if m == nil { + return nil + } + o := &JobStatus{} + o.CopyFrom(m) + return o +} + +func (m *JobStatus) CopyFrom(src interface{}) { + + o := src.(*JobStatus) + *m = *o + github_com_docker_swarmkit_api_deepcopy.Copy(&m.JobIteration, &o.JobIteration) + if o.LastExecution != nil { + m.LastExecution = &types.Timestamp{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.LastExecution, o.LastExecution) + } +} + func (m *Version) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3555,22 +5550,27 @@ func (m *Version) Marshal() (dAtA []byte, err error) { } func (m *Version) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Version) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Index != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *IndexEntry) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3578,29 +5578,36 @@ func (m *IndexEntry) Marshal() (dAtA []byte, err error) { } func (m *IndexEntry) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IndexEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Key) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) - } if len(m.Val) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Val) + copy(dAtA[i:], m.Val) i = encodeVarintTypes(dAtA, i, uint64(len(m.Val))) - i += copy(dAtA[i:], m.Val) + i-- + dAtA[i] = 0x12 + } + if len(m.Key) > 0 { + i -= len(m.Key) + copy(dAtA[i:], m.Key) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Key))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Annotations) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3608,52 +5615,62 @@ func (m *Annotations) Marshal() (dAtA []byte, err error) { } func (m *Annotations) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Annotations) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if len(m.Indices) > 0 { + for iNdEx := len(m.Indices) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Indices[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x12 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovTypes(uint64(len(k))) + 1 + len(v) + sovTypes(uint64(len(v))) - i = encodeVarintTypes(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintTypes(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintTypes(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintTypes(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - if len(m.Indices) > 0 { - for _, msg := range m.Indices { - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *NamedGenericResource) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3661,29 +5678,36 @@ func (m *NamedGenericResource) Marshal() (dAtA []byte, err error) { } func (m *NamedGenericResource) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NamedGenericResource) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Kind) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Kind))) - i += copy(dAtA[i:], m.Kind) - } if len(m.Value) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Value) + copy(dAtA[i:], m.Value) i = encodeVarintTypes(dAtA, i, uint64(len(m.Value))) - i += copy(dAtA[i:], m.Value) + i-- + dAtA[i] = 0x12 + } + if len(m.Kind) > 0 { + i -= len(m.Kind) + copy(dAtA[i:], m.Kind) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Kind))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *DiscreteGenericResource) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3691,28 +5715,34 @@ func (m *DiscreteGenericResource) Marshal() (dAtA []byte, err error) { } func (m *DiscreteGenericResource) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DiscreteGenericResource) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Kind) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Kind))) - i += copy(dAtA[i:], m.Kind) - } if m.Value != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.Value)) + i-- + dAtA[i] = 0x10 } - return i, nil + if len(m.Kind) > 0 { + i -= len(m.Kind) + copy(dAtA[i:], m.Kind) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Kind))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *GenericResource) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3720,52 +5750,73 @@ func (m *GenericResource) Marshal() (dAtA []byte, err error) { } func (m *GenericResource) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenericResource) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Resource != nil { - nn1, err := m.Resource.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size := m.Resource.Size() + i -= size + if _, err := m.Resource.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += nn1 } - return i, nil + return len(dAtA) - i, nil } func (m *GenericResource_NamedResourceSpec) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenericResource_NamedResourceSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.NamedResourceSpec != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.NamedResourceSpec.Size())) - n2, err := m.NamedResourceSpec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.NamedResourceSpec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n2 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *GenericResource_DiscreteResourceSpec) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenericResource_DiscreteResourceSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.DiscreteResourceSpec != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.DiscreteResourceSpec.Size())) - n3, err := m.DiscreteResourceSpec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.DiscreteResourceSpec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n3 + i-- + dAtA[i] = 0x12 } - return i, nil + return len(dAtA) - i, nil } func (m *Resources) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3773,39 +5824,46 @@ func (m *Resources) Marshal() (dAtA []byte, err error) { } func (m *Resources) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Resources) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.NanoCPUs != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.NanoCPUs)) + if len(m.Generic) > 0 { + for iNdEx := len(m.Generic) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Generic[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } } if m.MemoryBytes != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.MemoryBytes)) + i-- + dAtA[i] = 0x10 } - if len(m.Generic) > 0 { - for _, msg := range m.Generic { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if m.NanoCPUs != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.NanoCPUs)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *ResourceRequirements) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3813,37 +5871,70 @@ func (m *ResourceRequirements) Marshal() (dAtA []byte, err error) { } func (m *ResourceRequirements) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceRequirements) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Limits != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Limits.Size())) - n4, err := m.Limits.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.MemorySwappiness != nil { + { + size, err := m.MemorySwappiness.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.SwapBytes != nil { + { + size, err := m.SwapBytes.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n4 + i-- + dAtA[i] = 0x1a } if m.Reservations != nil { + { + size, err := m.Reservations.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Reservations.Size())) - n5, err := m.Reservations.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + if m.Limits != nil { + { + size, err := m.Limits.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n5 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Platform) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3851,29 +5942,36 @@ func (m *Platform) Marshal() (dAtA []byte, err error) { } func (m *Platform) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Platform) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Architecture) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Architecture))) - i += copy(dAtA[i:], m.Architecture) - } if len(m.OS) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.OS) + copy(dAtA[i:], m.OS) i = encodeVarintTypes(dAtA, i, uint64(len(m.OS))) - i += copy(dAtA[i:], m.OS) + i-- + dAtA[i] = 0x12 } - return i, nil -} - -func (m *PluginDescription) Marshal() (dAtA []byte, err error) { + if len(m.Architecture) > 0 { + i -= len(m.Architecture) + copy(dAtA[i:], m.Architecture) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Architecture))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PluginDescription) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3881,29 +5979,36 @@ func (m *PluginDescription) Marshal() (dAtA []byte, err error) { } func (m *PluginDescription) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PluginDescription) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Type) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Type))) - i += copy(dAtA[i:], m.Type) - } if len(m.Name) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Name) + copy(dAtA[i:], m.Name) i = encodeVarintTypes(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *EngineDescription) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3911,52 +6016,62 @@ func (m *EngineDescription) Marshal() (dAtA []byte, err error) { } func (m *EngineDescription) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EngineDescription) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.EngineVersion) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.EngineVersion))) - i += copy(dAtA[i:], m.EngineVersion) + if len(m.Plugins) > 0 { + for iNdEx := len(m.Plugins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Plugins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x12 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovTypes(uint64(len(k))) + 1 + len(v) + sovTypes(uint64(len(v))) - i = encodeVarintTypes(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintTypes(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintTypes(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintTypes(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - if len(m.Plugins) > 0 { - for _, msg := range m.Plugins { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if len(m.EngineVersion) > 0 { + i -= len(m.EngineVersion) + copy(dAtA[i:], m.EngineVersion) + i = encodeVarintTypes(dAtA, i, uint64(len(m.EngineVersion))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *NodeDescription) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -3964,73 +6079,87 @@ func (m *NodeDescription) Marshal() (dAtA []byte, err error) { } func (m *NodeDescription) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NodeDescription) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Hostname) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Hostname))) - i += copy(dAtA[i:], m.Hostname) - } - if m.Platform != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Platform.Size())) - n6, err := m.Platform.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.FIPS { + i-- + if m.FIPS { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } - i += n6 + i-- + dAtA[i] = 0x30 } - if m.Resources != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Resources.Size())) - n7, err := m.Resources.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.TLSInfo != nil { + { + size, err := m.TLSInfo.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n7 + i-- + dAtA[i] = 0x2a } if m.Engine != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Engine.Size())) - n8, err := m.Engine.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Engine.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n8 + i-- + dAtA[i] = 0x22 } - if m.TLSInfo != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.TLSInfo.Size())) - n9, err := m.TLSInfo.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Resources != nil { + { + size, err := m.Resources.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n9 + i-- + dAtA[i] = 0x1a } - if m.FIPS { - dAtA[i] = 0x30 - i++ - if m.FIPS { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + if m.Platform != nil { + { + size, err := m.Platform.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i++ + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.Hostname) > 0 { + i -= len(m.Hostname) + copy(dAtA[i:], m.Hostname) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Hostname))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *NodeTLSInfo) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4038,35 +6167,43 @@ func (m *NodeTLSInfo) Marshal() (dAtA []byte, err error) { } func (m *NodeTLSInfo) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NodeTLSInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.TrustRoot) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.TrustRoot))) - i += copy(dAtA[i:], m.TrustRoot) + if len(m.CertIssuerPublicKey) > 0 { + i -= len(m.CertIssuerPublicKey) + copy(dAtA[i:], m.CertIssuerPublicKey) + i = encodeVarintTypes(dAtA, i, uint64(len(m.CertIssuerPublicKey))) + i-- + dAtA[i] = 0x1a } if len(m.CertIssuerSubject) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.CertIssuerSubject) + copy(dAtA[i:], m.CertIssuerSubject) i = encodeVarintTypes(dAtA, i, uint64(len(m.CertIssuerSubject))) - i += copy(dAtA[i:], m.CertIssuerSubject) + i-- + dAtA[i] = 0x12 } - if len(m.CertIssuerPublicKey) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.CertIssuerPublicKey))) - i += copy(dAtA[i:], m.CertIssuerPublicKey) + if len(m.TrustRoot) > 0 { + i -= len(m.TrustRoot) + copy(dAtA[i:], m.TrustRoot) + i = encodeVarintTypes(dAtA, i, uint64(len(m.TrustRoot))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *RaftMemberStatus) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4074,38 +6211,44 @@ func (m *RaftMemberStatus) Marshal() (dAtA []byte, err error) { } func (m *RaftMemberStatus) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RaftMemberStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x1a + } + if m.Reachability != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Reachability)) + i-- + dAtA[i] = 0x10 + } if m.Leader { - dAtA[i] = 0x8 - i++ + i-- if m.Leader { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ - } - if m.Reachability != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Reachability)) - } - if len(m.Message) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Message))) - i += copy(dAtA[i:], m.Message) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *NodeStatus) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4113,34 +6256,41 @@ func (m *NodeStatus) Marshal() (dAtA []byte, err error) { } func (m *NodeStatus) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NodeStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.State != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.State)) + if len(m.Addr) > 0 { + i -= len(m.Addr) + copy(dAtA[i:], m.Addr) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Addr))) + i-- + dAtA[i] = 0x1a } if len(m.Message) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Message) + copy(dAtA[i:], m.Message) i = encodeVarintTypes(dAtA, i, uint64(len(m.Message))) - i += copy(dAtA[i:], m.Message) + i-- + dAtA[i] = 0x12 } - if len(m.Addr) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Addr))) - i += copy(dAtA[i:], m.Addr) + if m.State != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.State)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *Image) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4148,23 +6298,29 @@ func (m *Image) Marshal() (dAtA []byte, err error) { } func (m *Image) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Image) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.Reference) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Reference) + copy(dAtA[i:], m.Reference) i = encodeVarintTypes(dAtA, i, uint64(len(m.Reference))) - i += copy(dAtA[i:], m.Reference) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Mount) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4172,79 +6328,92 @@ func (m *Mount) Marshal() (dAtA []byte, err error) { } func (m *Mount) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Mount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Type != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Type)) + if m.Consistency != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Consistency)) + i-- + dAtA[i] = 0x40 } - if len(m.Source) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Source))) - i += copy(dAtA[i:], m.Source) + if m.TmpfsOptions != nil { + { + size, err := m.TmpfsOptions.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a } - if len(m.Target) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Target))) - i += copy(dAtA[i:], m.Target) + if m.VolumeOptions != nil { + { + size, err := m.VolumeOptions.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.BindOptions != nil { + { + size, err := m.BindOptions.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a } if m.ReadOnly { - dAtA[i] = 0x20 - i++ + i-- if m.ReadOnly { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ - } - if m.BindOptions != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.BindOptions.Size())) - n10, err := m.BindOptions.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n10 + i-- + dAtA[i] = 0x20 } - if m.VolumeOptions != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.VolumeOptions.Size())) - n11, err := m.VolumeOptions.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n11 + if len(m.Target) > 0 { + i -= len(m.Target) + copy(dAtA[i:], m.Target) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Target))) + i-- + dAtA[i] = 0x1a } - if m.TmpfsOptions != nil { - dAtA[i] = 0x3a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.TmpfsOptions.Size())) - n12, err := m.TmpfsOptions.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n12 + if len(m.Source) > 0 { + i -= len(m.Source) + copy(dAtA[i:], m.Source) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Source))) + i-- + dAtA[i] = 0x12 } - if m.Consistency != 0 { - dAtA[i] = 0x40 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Consistency)) + if m.Type != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *Mount_BindOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4252,22 +6421,37 @@ func (m *Mount_BindOptions) Marshal() (dAtA []byte, err error) { } func (m *Mount_BindOptions) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Mount_BindOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.NonRecursive { + i-- + if m.NonRecursive { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } if m.Propagation != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.Propagation)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *Mount_VolumeOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4275,54 +6459,63 @@ func (m *Mount_VolumeOptions) Marshal() (dAtA []byte, err error) { } func (m *Mount_VolumeOptions) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Mount_VolumeOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.NoCopy { - dAtA[i] = 0x8 - i++ - if m.NoCopy { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + if m.DriverConfig != nil { + { + size, err := m.DriverConfig.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i++ + i-- + dAtA[i] = 0x1a } if len(m.Labels) > 0 { - for k, _ := range m.Labels { - dAtA[i] = 0x12 - i++ + for k := range m.Labels { v := m.Labels[k] - mapSize := 1 + len(k) + sovTypes(uint64(len(k))) + 1 + len(v) + sovTypes(uint64(len(v))) - i = encodeVarintTypes(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintTypes(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintTypes(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintTypes(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - if m.DriverConfig != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.DriverConfig.Size())) - n13, err := m.DriverConfig.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.NoCopy { + i-- + if m.NoCopy { + dAtA[i] = 1 + } else { + dAtA[i] = 0 } - i += n13 + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *Mount_TmpfsOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4330,27 +6523,39 @@ func (m *Mount_TmpfsOptions) Marshal() (dAtA []byte, err error) { } func (m *Mount_TmpfsOptions) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Mount_TmpfsOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.SizeBytes != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.SizeBytes)) + if len(m.Options) > 0 { + i -= len(m.Options) + copy(dAtA[i:], m.Options) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Options))) + i-- + dAtA[i] = 0x1a } if m.Mode != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x10 + } + if m.SizeBytes != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.SizeBytes)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *RestartPolicy) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4358,47 +6563,56 @@ func (m *RestartPolicy) Marshal() (dAtA []byte, err error) { } func (m *RestartPolicy) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RestartPolicy) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Condition != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Condition)) - } - if m.Delay != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Delay.Size())) - n14, err := m.Delay.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Window != nil { + { + size, err := m.Window.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n14 + i-- + dAtA[i] = 0x22 } if m.MaxAttempts != 0 { - dAtA[i] = 0x18 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.MaxAttempts)) + i-- + dAtA[i] = 0x18 } - if m.Window != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Window.Size())) - n15, err := m.Window.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.Delay != nil { + { + size, err := m.Delay.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n15 + i-- + dAtA[i] = 0x12 } - return i, nil + if m.Condition != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Condition)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func (m *UpdateConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4406,56 +6620,63 @@ func (m *UpdateConfig) Marshal() (dAtA []byte, err error) { } func (m *UpdateConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Parallelism != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Parallelism)) - } - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(types.SizeOfStdDuration(m.Delay))) - n16, err := types.StdDurationMarshalTo(m.Delay, dAtA[i:]) - if err != nil { - return 0, err + if m.Order != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Order)) + i-- + dAtA[i] = 0x30 } - i += n16 - if m.FailureAction != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.FailureAction)) + if m.MaxFailureRatio != 0 { + i -= 4 + encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(math.Float32bits(float32(m.MaxFailureRatio)))) + i-- + dAtA[i] = 0x2d } if m.Monitor != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Monitor.Size())) - n17, err := m.Monitor.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Monitor.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n17 + i-- + dAtA[i] = 0x22 } - if m.MaxFailureRatio != 0 { - dAtA[i] = 0x2d - i++ - binary.LittleEndian.PutUint32(dAtA[i:], uint32(math.Float32bits(float32(m.MaxFailureRatio)))) - i += 4 + if m.FailureAction != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.FailureAction)) + i-- + dAtA[i] = 0x18 } - if m.Order != 0 { - dAtA[i] = 0x30 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Order)) + n18, err18 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.Delay, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.Delay):]) + if err18 != nil { + return 0, err18 + } + i -= n18 + i = encodeVarintTypes(dAtA, i, uint64(n18)) + i-- + dAtA[i] = 0x12 + if m.Parallelism != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Parallelism)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *UpdateStatus) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4463,48 +6684,58 @@ func (m *UpdateStatus) Marshal() (dAtA []byte, err error) { } func (m *UpdateStatus) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.State != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.State)) - } - if m.StartedAt != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.StartedAt.Size())) - n18, err := m.StartedAt.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n18 + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x22 } if m.CompletedAt != nil { + { + size, err := m.CompletedAt.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.CompletedAt.Size())) - n19, err := m.CompletedAt.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + if m.StartedAt != nil { + { + size, err := m.StartedAt.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n19 + i-- + dAtA[i] = 0x12 } - if len(m.Message) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Message))) - i += copy(dAtA[i:], m.Message) + if m.State != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.State)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *ContainerStatus) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4512,33 +6743,39 @@ func (m *ContainerStatus) Marshal() (dAtA []byte, err error) { } func (m *ContainerStatus) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ContainerID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.ContainerID))) - i += copy(dAtA[i:], m.ContainerID) + if m.ExitCode != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.ExitCode)) + i-- + dAtA[i] = 0x18 } if m.PID != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.PID)) + i-- + dAtA[i] = 0x10 } - if m.ExitCode != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.ExitCode)) + if len(m.ContainerID) > 0 { + i -= len(m.ContainerID) + copy(dAtA[i:], m.ContainerID) + i = encodeVarintTypes(dAtA, i, uint64(len(m.ContainerID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *PortStatus) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4546,29 +6783,36 @@ func (m *PortStatus) Marshal() (dAtA []byte, err error) { } func (m *PortStatus) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PortStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.Ports) > 0 { - for _, msg := range m.Ports { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Ports) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Ports[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *TaskStatus) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4576,91 +6820,114 @@ func (m *TaskStatus) Marshal() (dAtA []byte, err error) { } func (m *TaskStatus) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Timestamp != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Timestamp.Size())) - n20, err := m.Timestamp.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.AppliedAt != nil { + { + size, err := m.AppliedAt.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n20 - } - if m.State != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.State)) + i-- + dAtA[i] = 0x42 } - if len(m.Message) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Message))) - i += copy(dAtA[i:], m.Message) + if len(m.AppliedBy) > 0 { + i -= len(m.AppliedBy) + copy(dAtA[i:], m.AppliedBy) + i = encodeVarintTypes(dAtA, i, uint64(len(m.AppliedBy))) + i-- + dAtA[i] = 0x3a } - if len(m.Err) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Err))) - i += copy(dAtA[i:], m.Err) + if m.PortStatus != nil { + { + size, err := m.PortStatus.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 } if m.RuntimeStatus != nil { - nn21, err := m.RuntimeStatus.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size := m.RuntimeStatus.Size() + i -= size + if _, err := m.RuntimeStatus.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += nn21 } - if m.PortStatus != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.PortStatus.Size())) - n22, err := m.PortStatus.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n22 + if len(m.Err) > 0 { + i -= len(m.Err) + copy(dAtA[i:], m.Err) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Err))) + i-- + dAtA[i] = 0x22 } - if len(m.AppliedBy) > 0 { - dAtA[i] = 0x3a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.AppliedBy))) - i += copy(dAtA[i:], m.AppliedBy) + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x1a } - if m.AppliedAt != nil { - dAtA[i] = 0x42 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.AppliedAt.Size())) - n23, err := m.AppliedAt.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.State != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.State)) + i-- + dAtA[i] = 0x10 + } + if m.Timestamp != nil { + { + size, err := m.Timestamp.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n23 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *TaskStatus_Container) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskStatus_Container) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Container != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Container.Size())) - n24, err := m.Container.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Container.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n24 + i-- + dAtA[i] = 0x2a } - return i, nil + return len(dAtA) - i, nil } func (m *NetworkAttachmentConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4668,70 +6935,66 @@ func (m *NetworkAttachmentConfig) Marshal() (dAtA []byte, err error) { } func (m *NetworkAttachmentConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NetworkAttachmentConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Target) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Target))) - i += copy(dAtA[i:], m.Target) - } - if len(m.Aliases) > 0 { - for _, s := range m.Aliases { + if len(m.DriverAttachmentOpts) > 0 { + for k := range m.DriverAttachmentOpts { + v := m.DriverAttachmentOpts[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintTypes(dAtA, i, uint64(len(v))) + i-- dAtA[i] = 0x12 - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintTypes(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintTypes(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x22 } } if len(m.Addresses) > 0 { - for _, s := range m.Addresses { + for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Addresses[iNdEx]) + copy(dAtA[i:], m.Addresses[iNdEx]) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Addresses[iNdEx]))) + i-- dAtA[i] = 0x1a - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - if len(m.DriverAttachmentOpts) > 0 { - for k, _ := range m.DriverAttachmentOpts { - dAtA[i] = 0x22 - i++ - v := m.DriverAttachmentOpts[k] - mapSize := 1 + len(k) + sovTypes(uint64(len(k))) + 1 + len(v) + sovTypes(uint64(len(v))) - i = encodeVarintTypes(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + if len(m.Aliases) > 0 { + for iNdEx := len(m.Aliases) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Aliases[iNdEx]) + copy(dAtA[i:], m.Aliases[iNdEx]) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Aliases[iNdEx]))) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - return i, nil + if len(m.Target) > 0 { + i -= len(m.Target) + copy(dAtA[i:], m.Target) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Target))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *IPAMConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4739,57 +7002,67 @@ func (m *IPAMConfig) Marshal() (dAtA []byte, err error) { } func (m *IPAMConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IPAMConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Family != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Family)) + if len(m.Reserved) > 0 { + for k := range m.Reserved { + v := m.Reserved[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintTypes(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintTypes(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintTypes(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x2a + } } - if len(m.Subnet) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Subnet))) - i += copy(dAtA[i:], m.Subnet) + if len(m.Gateway) > 0 { + i -= len(m.Gateway) + copy(dAtA[i:], m.Gateway) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Gateway))) + i-- + dAtA[i] = 0x22 } if len(m.Range) > 0 { - dAtA[i] = 0x1a - i++ + i -= len(m.Range) + copy(dAtA[i:], m.Range) i = encodeVarintTypes(dAtA, i, uint64(len(m.Range))) - i += copy(dAtA[i:], m.Range) + i-- + dAtA[i] = 0x1a } - if len(m.Gateway) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Gateway))) - i += copy(dAtA[i:], m.Gateway) + if len(m.Subnet) > 0 { + i -= len(m.Subnet) + copy(dAtA[i:], m.Subnet) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Subnet))) + i-- + dAtA[i] = 0x12 } - if len(m.Reserved) > 0 { - for k, _ := range m.Reserved { - dAtA[i] = 0x2a - i++ - v := m.Reserved[k] - mapSize := 1 + len(k) + sovTypes(uint64(len(k))) + 1 + len(v) + sovTypes(uint64(len(v))) - i = encodeVarintTypes(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) - } + if m.Family != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Family)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *PortConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4797,43 +7070,49 @@ func (m *PortConfig) Marshal() (dAtA []byte, err error) { } func (m *PortConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PortConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.PublishMode != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.PublishMode)) + i-- + dAtA[i] = 0x28 } - if m.Protocol != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Protocol)) + if m.PublishedPort != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.PublishedPort)) + i-- + dAtA[i] = 0x20 } if m.TargetPort != 0 { - dAtA[i] = 0x18 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.TargetPort)) + i-- + dAtA[i] = 0x18 } - if m.PublishedPort != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.PublishedPort)) + if m.Protocol != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Protocol)) + i-- + dAtA[i] = 0x10 } - if m.PublishMode != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.PublishMode)) + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Driver) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4841,40 +7120,48 @@ func (m *Driver) Marshal() (dAtA []byte, err error) { } func (m *Driver) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Driver) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - } if len(m.Options) > 0 { - for k, _ := range m.Options { - dAtA[i] = 0x12 - i++ + for k := range m.Options { v := m.Options[k] - mapSize := 1 + len(k) + sovTypes(uint64(len(k))) + 1 + len(v) + sovTypes(uint64(len(v))) - i = encodeVarintTypes(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintTypes(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) i = encodeVarintTypes(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + i-- + dAtA[i] = 0xa + i = encodeVarintTypes(dAtA, i, uint64(baseI-i)) + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) } } - return i, nil + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *IPAMOptions) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4882,39 +7169,48 @@ func (m *IPAMOptions) Marshal() (dAtA []byte, err error) { } func (m *IPAMOptions) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IPAMOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Driver != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Driver.Size())) - n25, err := m.Driver.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n25 - } if len(m.Configs) > 0 { - for _, msg := range m.Configs { + for iNdEx := len(m.Configs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Configs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + } + } + if m.Driver != nil { + { + size, err := m.Driver.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Peer) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4922,29 +7218,36 @@ func (m *Peer) Marshal() (dAtA []byte, err error) { } func (m *Peer) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Peer) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.NodeID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.NodeID))) - i += copy(dAtA[i:], m.NodeID) - } if len(m.Addr) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Addr) + copy(dAtA[i:], m.Addr) i = encodeVarintTypes(dAtA, i, uint64(len(m.Addr))) - i += copy(dAtA[i:], m.Addr) + i-- + dAtA[i] = 0x12 + } + if len(m.NodeID) > 0 { + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) + i = encodeVarintTypes(dAtA, i, uint64(len(m.NodeID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *WeightedPeer) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4952,32 +7255,39 @@ func (m *WeightedPeer) Marshal() (dAtA []byte, err error) { } func (m *WeightedPeer) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WeightedPeer) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Peer != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Peer.Size())) - n26, err := m.Peer.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n26 - } if m.Weight != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.Weight)) + i-- + dAtA[i] = 0x10 + } + if m.Peer != nil { + { + size, err := m.Peer.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *IssuanceStatus) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -4985,28 +7295,34 @@ func (m *IssuanceStatus) Marshal() (dAtA []byte, err error) { } func (m *IssuanceStatus) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *IssuanceStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.State != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.State)) - } if len(m.Err) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Err) + copy(dAtA[i:], m.Err) i = encodeVarintTypes(dAtA, i, uint64(len(m.Err))) - i += copy(dAtA[i:], m.Err) + i-- + dAtA[i] = 0x12 + } + if m.State != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.State)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *AcceptancePolicy) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5014,29 +7330,36 @@ func (m *AcceptancePolicy) Marshal() (dAtA []byte, err error) { } func (m *AcceptancePolicy) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AcceptancePolicy) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.Policies) > 0 { - for _, msg := range m.Policies { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Policies) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Policies[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *AcceptancePolicy_RoleAdmissionPolicy) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5044,42 +7367,49 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) Marshal() (dAtA []byte, err error } func (m *AcceptancePolicy_RoleAdmissionPolicy) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AcceptancePolicy_RoleAdmissionPolicy) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Role != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Role)) + if m.Secret != nil { + { + size, err := m.Secret.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } if m.Autoaccept { - dAtA[i] = 0x10 - i++ + i-- if m.Autoaccept { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x10 } - if m.Secret != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Secret.Size())) - n27, err := m.Secret.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n27 + if m.Role != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Role)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5087,29 +7417,36 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Marshal() (dAtA []byte, er } func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Data) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) - } if len(m.Alg) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Alg) + copy(dAtA[i:], m.Alg) i = encodeVarintTypes(dAtA, i, uint64(len(m.Alg))) - i += copy(dAtA[i:], m.Alg) + i-- + dAtA[i] = 0x12 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ExternalCA) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5117,51 +7454,60 @@ func (m *ExternalCA) Marshal() (dAtA []byte, err error) { } func (m *ExternalCA) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExternalCA) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int - _ = l - if m.Protocol != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Protocol)) - } - if len(m.URL) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.URL))) - i += copy(dAtA[i:], m.URL) + _ = l + if len(m.CACert) > 0 { + i -= len(m.CACert) + copy(dAtA[i:], m.CACert) + i = encodeVarintTypes(dAtA, i, uint64(len(m.CACert))) + i-- + dAtA[i] = 0x22 } if len(m.Options) > 0 { - for k, _ := range m.Options { - dAtA[i] = 0x1a - i++ + for k := range m.Options { v := m.Options[k] - mapSize := 1 + len(k) + sovTypes(uint64(len(k))) + 1 + len(v) + sovTypes(uint64(len(v))) - i = encodeVarintTypes(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) - dAtA[i] = 0x12 - i++ + baseI := i + i -= len(v) + copy(dAtA[i:], v) i = encodeVarintTypes(dAtA, i, uint64(len(v))) - i += copy(dAtA[i:], v) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintTypes(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintTypes(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a } } - if len(m.CACert) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.CACert))) - i += copy(dAtA[i:], m.CACert) + if len(m.URL) > 0 { + i -= len(m.URL) + copy(dAtA[i:], m.URL) + i = encodeVarintTypes(dAtA, i, uint64(len(m.URL))) + i-- + dAtA[i] = 0x12 + } + if m.Protocol != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Protocol)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *CAConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5169,56 +7515,67 @@ func (m *CAConfig) Marshal() (dAtA []byte, err error) { } func (m *CAConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CAConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.NodeCertExpiry != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.NodeCertExpiry.Size())) - n28, err := m.NodeCertExpiry.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n28 + if m.ForceRotate != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.ForceRotate)) + i-- + dAtA[i] = 0x28 + } + if len(m.SigningCAKey) > 0 { + i -= len(m.SigningCAKey) + copy(dAtA[i:], m.SigningCAKey) + i = encodeVarintTypes(dAtA, i, uint64(len(m.SigningCAKey))) + i-- + dAtA[i] = 0x22 + } + if len(m.SigningCACert) > 0 { + i -= len(m.SigningCACert) + copy(dAtA[i:], m.SigningCACert) + i = encodeVarintTypes(dAtA, i, uint64(len(m.SigningCACert))) + i-- + dAtA[i] = 0x1a } if len(m.ExternalCAs) > 0 { - for _, msg := range m.ExternalCAs { + for iNdEx := len(m.ExternalCAs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ExternalCAs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + } + } + if m.NodeCertExpiry != nil { + { + size, err := m.NodeCertExpiry.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0xa } - if len(m.SigningCACert) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.SigningCACert))) - i += copy(dAtA[i:], m.SigningCACert) - } - if len(m.SigningCAKey) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.SigningCAKey))) - i += copy(dAtA[i:], m.SigningCAKey) - } - if m.ForceRotate != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.ForceRotate)) - } - return i, nil + return len(dAtA) - i, nil } func (m *OrchestrationConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5226,22 +7583,27 @@ func (m *OrchestrationConfig) Marshal() (dAtA []byte, err error) { } func (m *OrchestrationConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *OrchestrationConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.TaskHistoryRetentionLimit != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.TaskHistoryRetentionLimit)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *TaskDefaults) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5249,27 +7611,34 @@ func (m *TaskDefaults) Marshal() (dAtA []byte, err error) { } func (m *TaskDefaults) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TaskDefaults) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.LogDriver != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.LogDriver.Size())) - n29, err := m.LogDriver.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.LogDriver.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n29 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *DispatcherConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5277,27 +7646,34 @@ func (m *DispatcherConfig) Marshal() (dAtA []byte, err error) { } func (m *DispatcherConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DispatcherConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.HeartbeatPeriod != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.HeartbeatPeriod.Size())) - n30, err := m.HeartbeatPeriod.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.HeartbeatPeriod.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n30 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *RaftConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5305,42 +7681,47 @@ func (m *RaftConfig) Marshal() (dAtA []byte, err error) { } func (m *RaftConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RaftConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.SnapshotInterval != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.SnapshotInterval)) + if m.ElectionTick != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.ElectionTick)) + i-- + dAtA[i] = 0x28 } - if m.KeepOldSnapshots != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.KeepOldSnapshots)) + if m.HeartbeatTick != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.HeartbeatTick)) + i-- + dAtA[i] = 0x20 } if m.LogEntriesForSlowFollowers != 0 { - dAtA[i] = 0x18 - i++ i = encodeVarintTypes(dAtA, i, uint64(m.LogEntriesForSlowFollowers)) + i-- + dAtA[i] = 0x18 } - if m.HeartbeatTick != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.HeartbeatTick)) + if m.KeepOldSnapshots != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.KeepOldSnapshots)) + i-- + dAtA[i] = 0x10 } - if m.ElectionTick != 0 { - dAtA[i] = 0x28 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.ElectionTick)) + if m.SnapshotInterval != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.SnapshotInterval)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *EncryptionConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5348,27 +7729,32 @@ func (m *EncryptionConfig) Marshal() (dAtA []byte, err error) { } func (m *EncryptionConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EncryptionConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.AutoLockManagers { - dAtA[i] = 0x8 - i++ + i-- if m.AutoLockManagers { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *SpreadOver) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5376,23 +7762,29 @@ func (m *SpreadOver) Marshal() (dAtA []byte, err error) { } func (m *SpreadOver) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SpreadOver) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if len(m.SpreadDescriptor) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.SpreadDescriptor) + copy(dAtA[i:], m.SpreadDescriptor) i = encodeVarintTypes(dAtA, i, uint64(len(m.SpreadDescriptor))) - i += copy(dAtA[i:], m.SpreadDescriptor) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *PlacementPreference) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5400,38 +7792,52 @@ func (m *PlacementPreference) Marshal() (dAtA []byte, err error) { } func (m *PlacementPreference) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PlacementPreference) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Preference != nil { - nn31, err := m.Preference.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size := m.Preference.Size() + i -= size + if _, err := m.Preference.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += nn31 } - return i, nil + return len(dAtA) - i, nil } func (m *PlacementPreference_Spread) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PlacementPreference_Spread) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Spread != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Spread.Size())) - n32, err := m.Spread.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Spread.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n32 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Placement) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5439,56 +7845,64 @@ func (m *Placement) Marshal() (dAtA []byte, err error) { } func (m *Placement) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Placement) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Constraints) > 0 { - for _, s := range m.Constraints { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ + if m.MaxReplicas != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.MaxReplicas)) + i-- + dAtA[i] = 0x20 + } + if len(m.Platforms) > 0 { + for iNdEx := len(m.Platforms) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Platforms[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + i-- + dAtA[i] = 0x1a } } if len(m.Preferences) > 0 { - for _, msg := range m.Preferences { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Preferences) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Preferences[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x12 } } - if len(m.Platforms) > 0 { - for _, msg := range m.Platforms { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n + if len(m.Constraints) > 0 { + for iNdEx := len(m.Constraints) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Constraints[iNdEx]) + copy(dAtA[i:], m.Constraints[iNdEx]) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Constraints[iNdEx]))) + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *JoinTokens) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5496,29 +7910,36 @@ func (m *JoinTokens) Marshal() (dAtA []byte, err error) { } func (m *JoinTokens) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *JoinTokens) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Worker) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Worker))) - i += copy(dAtA[i:], m.Worker) - } if len(m.Manager) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Manager) + copy(dAtA[i:], m.Manager) i = encodeVarintTypes(dAtA, i, uint64(len(m.Manager))) - i += copy(dAtA[i:], m.Manager) + i-- + dAtA[i] = 0x12 + } + if len(m.Worker) > 0 { + i -= len(m.Worker) + copy(dAtA[i:], m.Worker) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Worker))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *RootCA) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5526,58 +7947,70 @@ func (m *RootCA) Marshal() (dAtA []byte, err error) { } func (m *RootCA) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RootCA) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.CAKey) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.CAKey))) - i += copy(dAtA[i:], m.CAKey) - } - if len(m.CACert) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.CACert))) - i += copy(dAtA[i:], m.CACert) - } - if len(m.CACertHash) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.CACertHash))) - i += copy(dAtA[i:], m.CACertHash) - } - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.JoinTokens.Size())) - n33, err := m.JoinTokens.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if m.LastForcedRotation != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.LastForcedRotation)) + i-- + dAtA[i] = 0x30 } - i += n33 if m.RootRotation != nil { + { + size, err := m.RootRotation.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x2a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.RootRotation.Size())) - n34, err := m.RootRotation.MarshalTo(dAtA[i:]) + } + { + size, err := m.JoinTokens.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n34 + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - if m.LastForcedRotation != 0 { - dAtA[i] = 0x30 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.LastForcedRotation)) + i-- + dAtA[i] = 0x22 + if len(m.CACertHash) > 0 { + i -= len(m.CACertHash) + copy(dAtA[i:], m.CACertHash) + i = encodeVarintTypes(dAtA, i, uint64(len(m.CACertHash))) + i-- + dAtA[i] = 0x1a + } + if len(m.CACert) > 0 { + i -= len(m.CACert) + copy(dAtA[i:], m.CACert) + i = encodeVarintTypes(dAtA, i, uint64(len(m.CACert))) + i-- + dAtA[i] = 0x12 + } + if len(m.CAKey) > 0 { + i -= len(m.CAKey) + copy(dAtA[i:], m.CAKey) + i = encodeVarintTypes(dAtA, i, uint64(len(m.CAKey))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Certificate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5585,48 +8018,58 @@ func (m *Certificate) Marshal() (dAtA []byte, err error) { } func (m *Certificate) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Certificate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Role != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Role)) - } - if len(m.CSR) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.CSR))) - i += copy(dAtA[i:], m.CSR) - } - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Status.Size())) - n35, err := m.Status.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.CN) > 0 { + i -= len(m.CN) + copy(dAtA[i:], m.CN) + i = encodeVarintTypes(dAtA, i, uint64(len(m.CN))) + i-- + dAtA[i] = 0x2a } - i += n35 if len(m.Certificate) > 0 { - dAtA[i] = 0x22 - i++ + i -= len(m.Certificate) + copy(dAtA[i:], m.Certificate) i = encodeVarintTypes(dAtA, i, uint64(len(m.Certificate))) - i += copy(dAtA[i:], m.Certificate) + i-- + dAtA[i] = 0x22 } - if len(m.CN) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.CN))) - i += copy(dAtA[i:], m.CN) + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.CSR) > 0 { + i -= len(m.CSR) + copy(dAtA[i:], m.CSR) + i = encodeVarintTypes(dAtA, i, uint64(len(m.CSR))) + i-- + dAtA[i] = 0x12 + } + if m.Role != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Role)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *EncryptionKey) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5634,39 +8077,46 @@ func (m *EncryptionKey) Marshal() (dAtA []byte, err error) { } func (m *EncryptionKey) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EncryptionKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Subsystem) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Subsystem))) - i += copy(dAtA[i:], m.Subsystem) - } - if m.Algorithm != 0 { - dAtA[i] = 0x10 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Algorithm)) + if m.LamportTime != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.LamportTime)) + i-- + dAtA[i] = 0x20 } if len(m.Key) > 0 { - dAtA[i] = 0x1a - i++ + i -= len(m.Key) + copy(dAtA[i:], m.Key) i = encodeVarintTypes(dAtA, i, uint64(len(m.Key))) - i += copy(dAtA[i:], m.Key) + i-- + dAtA[i] = 0x1a } - if m.LamportTime != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.LamportTime)) + if m.Algorithm != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Algorithm)) + i-- + dAtA[i] = 0x10 + } + if len(m.Subsystem) > 0 { + i -= len(m.Subsystem) + copy(dAtA[i:], m.Subsystem) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Subsystem))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ManagerStatus) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5674,43 +8124,49 @@ func (m *ManagerStatus) Marshal() (dAtA []byte, err error) { } func (m *ManagerStatus) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ManagerStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.RaftID != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.RaftID)) - } - if len(m.Addr) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Addr))) - i += copy(dAtA[i:], m.Addr) + if m.Reachability != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Reachability)) + i-- + dAtA[i] = 0x20 } if m.Leader { - dAtA[i] = 0x18 - i++ + i-- if m.Leader { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x18 } - if m.Reachability != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Reachability)) + if len(m.Addr) > 0 { + i -= len(m.Addr) + copy(dAtA[i:], m.Addr) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Addr))) + i-- + dAtA[i] = 0x12 + } + if m.RaftID != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.RaftID)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *FileTarget) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5718,40 +8174,71 @@ func (m *FileTarget) Marshal() (dAtA []byte, err error) { } func (m *FileTarget) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FileTarget) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Name) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) + if m.Mode != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Mode)) + i-- + dAtA[i] = 0x20 + } + if len(m.GID) > 0 { + i -= len(m.GID) + copy(dAtA[i:], m.GID) + i = encodeVarintTypes(dAtA, i, uint64(len(m.GID))) + i-- + dAtA[i] = 0x1a } if len(m.UID) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.UID) + copy(dAtA[i:], m.UID) i = encodeVarintTypes(dAtA, i, uint64(len(m.UID))) - i += copy(dAtA[i:], m.UID) + i-- + dAtA[i] = 0x12 } - if len(m.GID) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.GID))) - i += copy(dAtA[i:], m.GID) + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa } - if m.Mode != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Mode)) + return len(dAtA) - i, nil +} + +func (m *RuntimeTarget) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - return i, nil + return dAtA[:n], nil +} + +func (m *RuntimeTarget) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RuntimeTarget) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil } func (m *SecretReference) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5759,50 +8246,66 @@ func (m *SecretReference) Marshal() (dAtA []byte, err error) { } func (m *SecretReference) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SecretReference) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.SecretID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.SecretID))) - i += copy(dAtA[i:], m.SecretID) + if m.Target != nil { + { + size := m.Target.Size() + i -= size + if _, err := m.Target.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } } if len(m.SecretName) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.SecretName) + copy(dAtA[i:], m.SecretName) i = encodeVarintTypes(dAtA, i, uint64(len(m.SecretName))) - i += copy(dAtA[i:], m.SecretName) + i-- + dAtA[i] = 0x12 } - if m.Target != nil { - nn36, err := m.Target.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += nn36 + if len(m.SecretID) > 0 { + i -= len(m.SecretID) + copy(dAtA[i:], m.SecretID) + i = encodeVarintTypes(dAtA, i, uint64(len(m.SecretID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *SecretReference_File) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SecretReference_File) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.File != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.File.Size())) - n37, err := m.File.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.File.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n37 + i-- + dAtA[i] = 0x1a } - return i, nil + return len(dAtA) - i, nil } func (m *ConfigReference) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5810,50 +8313,87 @@ func (m *ConfigReference) Marshal() (dAtA []byte, err error) { } func (m *ConfigReference) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConfigReference) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ConfigID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.ConfigID))) - i += copy(dAtA[i:], m.ConfigID) + if m.Target != nil { + { + size := m.Target.Size() + i -= size + if _, err := m.Target.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } } if len(m.ConfigName) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ConfigName) + copy(dAtA[i:], m.ConfigName) i = encodeVarintTypes(dAtA, i, uint64(len(m.ConfigName))) - i += copy(dAtA[i:], m.ConfigName) + i-- + dAtA[i] = 0x12 } - if m.Target != nil { - nn38, err := m.Target.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += nn38 + if len(m.ConfigID) > 0 { + i -= len(m.ConfigID) + copy(dAtA[i:], m.ConfigID) + i = encodeVarintTypes(dAtA, i, uint64(len(m.ConfigID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ConfigReference_File) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConfigReference_File) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.File != nil { + { + size, err := m.File.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.File.Size())) - n39, err := m.File.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + return len(dAtA) - i, nil +} +func (m *ConfigReference_Runtime) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConfigReference_Runtime) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Runtime != nil { + { + size, err := m.Runtime.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n39 + i-- + dAtA[i] = 0x22 } - return i, nil + return len(dAtA) - i, nil } func (m *BlacklistedCertificate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5861,27 +8401,34 @@ func (m *BlacklistedCertificate) Marshal() (dAtA []byte, err error) { } func (m *BlacklistedCertificate) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BlacklistedCertificate) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Expiry != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Expiry.Size())) - n40, err := m.Expiry.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Expiry.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n40 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *HealthConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5889,67 +8436,72 @@ func (m *HealthConfig) Marshal() (dAtA []byte, err error) { } func (m *HealthConfig) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HealthConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Test) > 0 { - for _, s := range m.Test { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ + if m.StartPeriod != nil { + { + size, err := m.StartPeriod.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x2a } - if m.Interval != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Interval.Size())) - n41, err := m.Interval.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n41 + if m.Retries != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Retries)) + i-- + dAtA[i] = 0x20 } if m.Timeout != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Timeout.Size())) - n42, err := m.Timeout.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Timeout.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n42 + i-- + dAtA[i] = 0x1a } - if m.Retries != 0 { - dAtA[i] = 0x20 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Retries)) + if m.Interval != nil { + { + size, err := m.Interval.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 } - if m.StartPeriod != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.StartPeriod.Size())) - n43, err := m.StartPeriod.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + if len(m.Test) > 0 { + for iNdEx := len(m.Test) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Test[iNdEx]) + copy(dAtA[i:], m.Test[iNdEx]) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Test[iNdEx]))) + i-- + dAtA[i] = 0xa } - i += n43 } - return i, nil + return len(dAtA) - i, nil } func (m *MaybeEncryptedRecord) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5957,34 +8509,41 @@ func (m *MaybeEncryptedRecord) Marshal() (dAtA []byte, err error) { } func (m *MaybeEncryptedRecord) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MaybeEncryptedRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Algorithm != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.Algorithm)) + if len(m.Nonce) > 0 { + i -= len(m.Nonce) + copy(dAtA[i:], m.Nonce) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Nonce))) + i-- + dAtA[i] = 0x1a } if len(m.Data) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Data) + copy(dAtA[i:], m.Data) i = encodeVarintTypes(dAtA, i, uint64(len(m.Data))) - i += copy(dAtA[i:], m.Data) + i-- + dAtA[i] = 0x12 } - if len(m.Nonce) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Nonce))) - i += copy(dAtA[i:], m.Nonce) + if m.Algorithm != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Algorithm)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *RootRotation) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -5992,35 +8551,43 @@ func (m *RootRotation) Marshal() (dAtA []byte, err error) { } func (m *RootRotation) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RootRotation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.CACert) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.CACert))) - i += copy(dAtA[i:], m.CACert) + if len(m.CrossSignedCACert) > 0 { + i -= len(m.CrossSignedCACert) + copy(dAtA[i:], m.CrossSignedCACert) + i = encodeVarintTypes(dAtA, i, uint64(len(m.CrossSignedCACert))) + i-- + dAtA[i] = 0x1a } if len(m.CAKey) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.CAKey) + copy(dAtA[i:], m.CAKey) i = encodeVarintTypes(dAtA, i, uint64(len(m.CAKey))) - i += copy(dAtA[i:], m.CAKey) + i-- + dAtA[i] = 0x12 } - if len(m.CrossSignedCACert) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.CrossSignedCACert))) - i += copy(dAtA[i:], m.CrossSignedCACert) + if len(m.CACert) > 0 { + i -= len(m.CACert) + copy(dAtA[i:], m.CACert) + i = encodeVarintTypes(dAtA, i, uint64(len(m.CACert))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Privileges) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -6028,37 +8595,46 @@ func (m *Privileges) Marshal() (dAtA []byte, err error) { } func (m *Privileges) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Privileges) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.CredentialSpec != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.CredentialSpec.Size())) - n44, err := m.CredentialSpec.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n44 - } if m.SELinuxContext != nil { + { + size, err := m.SELinuxContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.SELinuxContext.Size())) - n45, err := m.SELinuxContext.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + if m.CredentialSpec != nil { + { + size, err := m.CredentialSpec.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - i += n45 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Privileges_CredentialSpec) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -6066,40 +8642,73 @@ func (m *Privileges_CredentialSpec) Marshal() (dAtA []byte, err error) { } func (m *Privileges_CredentialSpec) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Privileges_CredentialSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Source != nil { - nn46, err := m.Source.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size := m.Source.Size() + i -= size + if _, err := m.Source.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += nn46 } - return i, nil + return len(dAtA) - i, nil } func (m *Privileges_CredentialSpec_File) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0xa - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Privileges_CredentialSpec_File) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.File) + copy(dAtA[i:], m.File) i = encodeVarintTypes(dAtA, i, uint64(len(m.File))) - i += copy(dAtA[i:], m.File) - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *Privileges_CredentialSpec_Registry) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x12 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Privileges_CredentialSpec_Registry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.Registry) + copy(dAtA[i:], m.Registry) i = encodeVarintTypes(dAtA, i, uint64(len(m.Registry))) - i += copy(dAtA[i:], m.Registry) - return i, nil + i-- + dAtA[i] = 0x12 + return len(dAtA) - i, nil +} +func (m *Privileges_CredentialSpec_Config) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Privileges_CredentialSpec_Config) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.Config) + copy(dAtA[i:], m.Config) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Config))) + i-- + dAtA[i] = 0x1a + return len(dAtA) - i, nil } func (m *Privileges_SELinuxContext) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -6107,58 +8716,116 @@ func (m *Privileges_SELinuxContext) Marshal() (dAtA []byte, err error) { } func (m *Privileges_SELinuxContext) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Privileges_SELinuxContext) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if len(m.Level) > 0 { + i -= len(m.Level) + copy(dAtA[i:], m.Level) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Level))) + i-- + dAtA[i] = 0x2a + } + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0x22 + } + if len(m.Role) > 0 { + i -= len(m.Role) + copy(dAtA[i:], m.Role) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Role))) + i-- + dAtA[i] = 0x1a + } + if len(m.User) > 0 { + i -= len(m.User) + copy(dAtA[i:], m.User) + i = encodeVarintTypes(dAtA, i, uint64(len(m.User))) + i-- + dAtA[i] = 0x12 + } if m.Disable { - dAtA[i] = 0x8 - i++ + i-- if m.Disable { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ - } - if len(m.User) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.User))) - i += copy(dAtA[i:], m.User) + i-- + dAtA[i] = 0x8 } - if len(m.Role) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Role))) - i += copy(dAtA[i:], m.Role) + return len(dAtA) - i, nil +} + +func (m *JobStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err } - if len(m.Type) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Type))) - i += copy(dAtA[i:], m.Type) + return dAtA[:n], nil +} + +func (m *JobStatus) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *JobStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.LastExecution != nil { + { + size, err := m.LastExecution.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 } - if len(m.Level) > 0 { - dAtA[i] = 0x2a - i++ - i = encodeVarintTypes(dAtA, i, uint64(len(m.Level))) - i += copy(dAtA[i:], m.Level) + { + size, err := m.JobIteration.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) } - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { + offset -= sovTypes(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } - func (m *Version) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Index != 0 { @@ -6168,6 +8835,9 @@ func (m *Version) Size() (n int) { } func (m *IndexEntry) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Key) @@ -6182,6 +8852,9 @@ func (m *IndexEntry) Size() (n int) { } func (m *Annotations) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -6206,6 +8879,9 @@ func (m *Annotations) Size() (n int) { } func (m *NamedGenericResource) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Kind) @@ -6220,6 +8896,9 @@ func (m *NamedGenericResource) Size() (n int) { } func (m *DiscreteGenericResource) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Kind) @@ -6233,6 +8912,9 @@ func (m *DiscreteGenericResource) Size() (n int) { } func (m *GenericResource) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Resource != nil { @@ -6242,6 +8924,9 @@ func (m *GenericResource) Size() (n int) { } func (m *GenericResource_NamedResourceSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.NamedResourceSpec != nil { @@ -6251,6 +8936,9 @@ func (m *GenericResource_NamedResourceSpec) Size() (n int) { return n } func (m *GenericResource_DiscreteResourceSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.DiscreteResourceSpec != nil { @@ -6260,6 +8948,9 @@ func (m *GenericResource_DiscreteResourceSpec) Size() (n int) { return n } func (m *Resources) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.NanoCPUs != 0 { @@ -6278,6 +8969,9 @@ func (m *Resources) Size() (n int) { } func (m *ResourceRequirements) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Limits != nil { @@ -6288,10 +8982,21 @@ func (m *ResourceRequirements) Size() (n int) { l = m.Reservations.Size() n += 1 + l + sovTypes(uint64(l)) } + if m.SwapBytes != nil { + l = m.SwapBytes.Size() + n += 1 + l + sovTypes(uint64(l)) + } + if m.MemorySwappiness != nil { + l = m.MemorySwappiness.Size() + n += 1 + l + sovTypes(uint64(l)) + } return n } func (m *Platform) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Architecture) @@ -6306,6 +9011,9 @@ func (m *Platform) Size() (n int) { } func (m *PluginDescription) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Type) @@ -6320,6 +9028,9 @@ func (m *PluginDescription) Size() (n int) { } func (m *EngineDescription) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.EngineVersion) @@ -6344,6 +9055,9 @@ func (m *EngineDescription) Size() (n int) { } func (m *NodeDescription) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Hostname) @@ -6373,6 +9087,9 @@ func (m *NodeDescription) Size() (n int) { } func (m *NodeTLSInfo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.TrustRoot) @@ -6391,6 +9108,9 @@ func (m *NodeTLSInfo) Size() (n int) { } func (m *RaftMemberStatus) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Leader { @@ -6407,6 +9127,9 @@ func (m *RaftMemberStatus) Size() (n int) { } func (m *NodeStatus) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.State != 0 { @@ -6424,6 +9147,9 @@ func (m *NodeStatus) Size() (n int) { } func (m *Image) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Reference) @@ -6434,6 +9160,9 @@ func (m *Image) Size() (n int) { } func (m *Mount) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Type != 0 { @@ -6469,15 +9198,24 @@ func (m *Mount) Size() (n int) { } func (m *Mount_BindOptions) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Propagation != 0 { n += 1 + sovTypes(uint64(m.Propagation)) } + if m.NonRecursive { + n += 2 + } return n } func (m *Mount_VolumeOptions) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.NoCopy { @@ -6499,6 +9237,9 @@ func (m *Mount_VolumeOptions) Size() (n int) { } func (m *Mount_TmpfsOptions) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.SizeBytes != 0 { @@ -6507,10 +9248,17 @@ func (m *Mount_TmpfsOptions) Size() (n int) { if m.Mode != 0 { n += 1 + sovTypes(uint64(m.Mode)) } + l = len(m.Options) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } return n } func (m *RestartPolicy) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Condition != 0 { @@ -6531,12 +9279,15 @@ func (m *RestartPolicy) Size() (n int) { } func (m *UpdateConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Parallelism != 0 { n += 1 + sovTypes(uint64(m.Parallelism)) } - l = types.SizeOfStdDuration(m.Delay) + l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.Delay) n += 1 + l + sovTypes(uint64(l)) if m.FailureAction != 0 { n += 1 + sovTypes(uint64(m.FailureAction)) @@ -6555,6 +9306,9 @@ func (m *UpdateConfig) Size() (n int) { } func (m *UpdateStatus) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.State != 0 { @@ -6576,6 +9330,9 @@ func (m *UpdateStatus) Size() (n int) { } func (m *ContainerStatus) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ContainerID) @@ -6592,6 +9349,9 @@ func (m *ContainerStatus) Size() (n int) { } func (m *PortStatus) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Ports) > 0 { @@ -6604,6 +9364,9 @@ func (m *PortStatus) Size() (n int) { } func (m *TaskStatus) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Timestamp != nil { @@ -6640,6 +9403,9 @@ func (m *TaskStatus) Size() (n int) { } func (m *TaskStatus_Container) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Container != nil { @@ -6649,6 +9415,9 @@ func (m *TaskStatus_Container) Size() (n int) { return n } func (m *NetworkAttachmentConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Target) @@ -6679,6 +9448,9 @@ func (m *NetworkAttachmentConfig) Size() (n int) { } func (m *IPAMConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Family != 0 { @@ -6708,6 +9480,9 @@ func (m *IPAMConfig) Size() (n int) { } func (m *PortConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -6730,6 +9505,9 @@ func (m *PortConfig) Size() (n int) { } func (m *Driver) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -6748,6 +9526,9 @@ func (m *Driver) Size() (n int) { } func (m *IPAMOptions) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Driver != nil { @@ -6764,6 +9545,9 @@ func (m *IPAMOptions) Size() (n int) { } func (m *Peer) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.NodeID) @@ -6778,6 +9562,9 @@ func (m *Peer) Size() (n int) { } func (m *WeightedPeer) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Peer != nil { @@ -6791,6 +9578,9 @@ func (m *WeightedPeer) Size() (n int) { } func (m *IssuanceStatus) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.State != 0 { @@ -6804,6 +9594,9 @@ func (m *IssuanceStatus) Size() (n int) { } func (m *AcceptancePolicy) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Policies) > 0 { @@ -6816,6 +9609,9 @@ func (m *AcceptancePolicy) Size() (n int) { } func (m *AcceptancePolicy_RoleAdmissionPolicy) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Role != 0 { @@ -6832,6 +9628,9 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) Size() (n int) { } func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Data) @@ -6846,6 +9645,9 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Size() (n int) { } func (m *ExternalCA) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Protocol != 0 { @@ -6871,6 +9673,9 @@ func (m *ExternalCA) Size() (n int) { } func (m *CAConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.NodeCertExpiry != nil { @@ -6898,6 +9703,9 @@ func (m *CAConfig) Size() (n int) { } func (m *OrchestrationConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.TaskHistoryRetentionLimit != 0 { @@ -6907,6 +9715,9 @@ func (m *OrchestrationConfig) Size() (n int) { } func (m *TaskDefaults) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.LogDriver != nil { @@ -6917,6 +9728,9 @@ func (m *TaskDefaults) Size() (n int) { } func (m *DispatcherConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.HeartbeatPeriod != nil { @@ -6927,6 +9741,9 @@ func (m *DispatcherConfig) Size() (n int) { } func (m *RaftConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.SnapshotInterval != 0 { @@ -6948,6 +9765,9 @@ func (m *RaftConfig) Size() (n int) { } func (m *EncryptionConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.AutoLockManagers { @@ -6957,6 +9777,9 @@ func (m *EncryptionConfig) Size() (n int) { } func (m *SpreadOver) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.SpreadDescriptor) @@ -6967,6 +9790,9 @@ func (m *SpreadOver) Size() (n int) { } func (m *PlacementPreference) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Preference != nil { @@ -6976,6 +9802,9 @@ func (m *PlacementPreference) Size() (n int) { } func (m *PlacementPreference_Spread) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Spread != nil { @@ -6985,6 +9814,9 @@ func (m *PlacementPreference_Spread) Size() (n int) { return n } func (m *Placement) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Constraints) > 0 { @@ -7005,10 +9837,16 @@ func (m *Placement) Size() (n int) { n += 1 + l + sovTypes(uint64(l)) } } + if m.MaxReplicas != 0 { + n += 1 + sovTypes(uint64(m.MaxReplicas)) + } return n } func (m *JoinTokens) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Worker) @@ -7023,6 +9861,9 @@ func (m *JoinTokens) Size() (n int) { } func (m *RootCA) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.CAKey) @@ -7050,6 +9891,9 @@ func (m *RootCA) Size() (n int) { } func (m *Certificate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Role != 0 { @@ -7073,6 +9917,9 @@ func (m *Certificate) Size() (n int) { } func (m *EncryptionKey) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Subsystem) @@ -7093,6 +9940,9 @@ func (m *EncryptionKey) Size() (n int) { } func (m *ManagerStatus) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.RaftID != 0 { @@ -7112,6 +9962,9 @@ func (m *ManagerStatus) Size() (n int) { } func (m *FileTarget) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -7132,7 +9985,19 @@ func (m *FileTarget) Size() (n int) { return n } +func (m *RuntimeTarget) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *SecretReference) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.SecretID) @@ -7150,6 +10015,9 @@ func (m *SecretReference) Size() (n int) { } func (m *SecretReference_File) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.File != nil { @@ -7159,6 +10027,9 @@ func (m *SecretReference_File) Size() (n int) { return n } func (m *ConfigReference) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ConfigID) @@ -7176,6 +10047,9 @@ func (m *ConfigReference) Size() (n int) { } func (m *ConfigReference_File) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.File != nil { @@ -7184,7 +10058,22 @@ func (m *ConfigReference_File) Size() (n int) { } return n } +func (m *ConfigReference_Runtime) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Runtime != nil { + l = m.Runtime.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} func (m *BlacklistedCertificate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Expiry != nil { @@ -7195,6 +10084,9 @@ func (m *BlacklistedCertificate) Size() (n int) { } func (m *HealthConfig) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Test) > 0 { @@ -7222,6 +10114,9 @@ func (m *HealthConfig) Size() (n int) { } func (m *MaybeEncryptedRecord) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Algorithm != 0 { @@ -7239,6 +10134,9 @@ func (m *MaybeEncryptedRecord) Size() (n int) { } func (m *RootRotation) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.CACert) @@ -7257,6 +10155,9 @@ func (m *RootRotation) Size() (n int) { } func (m *Privileges) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.CredentialSpec != nil { @@ -7271,6 +10172,9 @@ func (m *Privileges) Size() (n int) { } func (m *Privileges_CredentialSpec) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Source != nil { @@ -7280,20 +10184,39 @@ func (m *Privileges_CredentialSpec) Size() (n int) { } func (m *Privileges_CredentialSpec_File) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.File) n += 1 + l + sovTypes(uint64(l)) return n } -func (m *Privileges_CredentialSpec_Registry) Size() (n int) { +func (m *Privileges_CredentialSpec_Registry) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Registry) + n += 1 + l + sovTypes(uint64(l)) + return n +} +func (m *Privileges_CredentialSpec_Config) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l - l = len(m.Registry) + l = len(m.Config) n += 1 + l + sovTypes(uint64(l)) return n } func (m *Privileges_SELinuxContext) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Disable { @@ -7318,16 +10241,24 @@ func (m *Privileges_SELinuxContext) Size() (n int) { return n } -func sovTypes(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } +func (m *JobStatus) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.JobIteration.Size() + n += 1 + l + sovTypes(uint64(l)) + if m.LastExecution != nil { + l = m.LastExecution.Size() + n += 1 + l + sovTypes(uint64(l)) } return n } + +func sovTypes(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} func sozTypes(x uint64) (n int) { return sovTypes(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } @@ -7356,11 +10287,16 @@ func (this *Annotations) String() string { if this == nil { return "nil" } + repeatedStringForIndices := "[]IndexEntry{" + for _, f := range this.Indices { + repeatedStringForIndices += strings.Replace(strings.Replace(f.String(), "IndexEntry", "IndexEntry", 1), `&`, ``, 1) + "," + } + repeatedStringForIndices += "}" keysForLabels := make([]string, 0, len(this.Labels)) for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -7369,7 +10305,7 @@ func (this *Annotations) String() string { s := strings.Join([]string{`&Annotations{`, `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `Labels:` + mapStringForLabels + `,`, - `Indices:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Indices), "IndexEntry", "IndexEntry", 1), `&`, ``, 1) + `,`, + `Indices:` + repeatedStringForIndices + `,`, `}`, }, "") return s @@ -7430,10 +10366,15 @@ func (this *Resources) String() string { if this == nil { return "nil" } + repeatedStringForGeneric := "[]*GenericResource{" + for _, f := range this.Generic { + repeatedStringForGeneric += strings.Replace(f.String(), "GenericResource", "GenericResource", 1) + "," + } + repeatedStringForGeneric += "}" s := strings.Join([]string{`&Resources{`, `NanoCPUs:` + fmt.Sprintf("%v", this.NanoCPUs) + `,`, `MemoryBytes:` + fmt.Sprintf("%v", this.MemoryBytes) + `,`, - `Generic:` + strings.Replace(fmt.Sprintf("%v", this.Generic), "GenericResource", "GenericResource", 1) + `,`, + `Generic:` + repeatedStringForGeneric + `,`, `}`, }, "") return s @@ -7443,8 +10384,10 @@ func (this *ResourceRequirements) String() string { return "nil" } s := strings.Join([]string{`&ResourceRequirements{`, - `Limits:` + strings.Replace(fmt.Sprintf("%v", this.Limits), "Resources", "Resources", 1) + `,`, - `Reservations:` + strings.Replace(fmt.Sprintf("%v", this.Reservations), "Resources", "Resources", 1) + `,`, + `Limits:` + strings.Replace(this.Limits.String(), "Resources", "Resources", 1) + `,`, + `Reservations:` + strings.Replace(this.Reservations.String(), "Resources", "Resources", 1) + `,`, + `SwapBytes:` + strings.Replace(fmt.Sprintf("%v", this.SwapBytes), "Int64Value", "types.Int64Value", 1) + `,`, + `MemorySwappiness:` + strings.Replace(fmt.Sprintf("%v", this.MemorySwappiness), "Int64Value", "types.Int64Value", 1) + `,`, `}`, }, "") return s @@ -7475,11 +10418,16 @@ func (this *EngineDescription) String() string { if this == nil { return "nil" } + repeatedStringForPlugins := "[]PluginDescription{" + for _, f := range this.Plugins { + repeatedStringForPlugins += strings.Replace(strings.Replace(f.String(), "PluginDescription", "PluginDescription", 1), `&`, ``, 1) + "," + } + repeatedStringForPlugins += "}" keysForLabels := make([]string, 0, len(this.Labels)) for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -7488,7 +10436,7 @@ func (this *EngineDescription) String() string { s := strings.Join([]string{`&EngineDescription{`, `EngineVersion:` + fmt.Sprintf("%v", this.EngineVersion) + `,`, `Labels:` + mapStringForLabels + `,`, - `Plugins:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Plugins), "PluginDescription", "PluginDescription", 1), `&`, ``, 1) + `,`, + `Plugins:` + repeatedStringForPlugins + `,`, `}`, }, "") return s @@ -7499,10 +10447,10 @@ func (this *NodeDescription) String() string { } s := strings.Join([]string{`&NodeDescription{`, `Hostname:` + fmt.Sprintf("%v", this.Hostname) + `,`, - `Platform:` + strings.Replace(fmt.Sprintf("%v", this.Platform), "Platform", "Platform", 1) + `,`, - `Resources:` + strings.Replace(fmt.Sprintf("%v", this.Resources), "Resources", "Resources", 1) + `,`, - `Engine:` + strings.Replace(fmt.Sprintf("%v", this.Engine), "EngineDescription", "EngineDescription", 1) + `,`, - `TLSInfo:` + strings.Replace(fmt.Sprintf("%v", this.TLSInfo), "NodeTLSInfo", "NodeTLSInfo", 1) + `,`, + `Platform:` + strings.Replace(this.Platform.String(), "Platform", "Platform", 1) + `,`, + `Resources:` + strings.Replace(this.Resources.String(), "Resources", "Resources", 1) + `,`, + `Engine:` + strings.Replace(this.Engine.String(), "EngineDescription", "EngineDescription", 1) + `,`, + `TLSInfo:` + strings.Replace(this.TLSInfo.String(), "NodeTLSInfo", "NodeTLSInfo", 1) + `,`, `FIPS:` + fmt.Sprintf("%v", this.FIPS) + `,`, `}`, }, "") @@ -7577,6 +10525,7 @@ func (this *Mount_BindOptions) String() string { } s := strings.Join([]string{`&Mount_BindOptions{`, `Propagation:` + fmt.Sprintf("%v", this.Propagation) + `,`, + `NonRecursive:` + fmt.Sprintf("%v", this.NonRecursive) + `,`, `}`, }, "") return s @@ -7589,7 +10538,7 @@ func (this *Mount_VolumeOptions) String() string { for k, _ := range this.Labels { keysForLabels = append(keysForLabels, k) } - sortkeys.Strings(keysForLabels) + github_com_gogo_protobuf_sortkeys.Strings(keysForLabels) mapStringForLabels := "map[string]string{" for _, k := range keysForLabels { mapStringForLabels += fmt.Sprintf("%v: %v,", k, this.Labels[k]) @@ -7598,7 +10547,7 @@ func (this *Mount_VolumeOptions) String() string { s := strings.Join([]string{`&Mount_VolumeOptions{`, `NoCopy:` + fmt.Sprintf("%v", this.NoCopy) + `,`, `Labels:` + mapStringForLabels + `,`, - `DriverConfig:` + strings.Replace(fmt.Sprintf("%v", this.DriverConfig), "Driver", "Driver", 1) + `,`, + `DriverConfig:` + strings.Replace(this.DriverConfig.String(), "Driver", "Driver", 1) + `,`, `}`, }, "") return s @@ -7610,6 +10559,7 @@ func (this *Mount_TmpfsOptions) String() string { s := strings.Join([]string{`&Mount_TmpfsOptions{`, `SizeBytes:` + fmt.Sprintf("%v", this.SizeBytes) + `,`, `Mode:` + fmt.Sprintf("%v", this.Mode) + `,`, + `Options:` + fmt.Sprintf("%v", this.Options) + `,`, `}`, }, "") return s @@ -7620,9 +10570,9 @@ func (this *RestartPolicy) String() string { } s := strings.Join([]string{`&RestartPolicy{`, `Condition:` + fmt.Sprintf("%v", this.Condition) + `,`, - `Delay:` + strings.Replace(fmt.Sprintf("%v", this.Delay), "Duration", "google_protobuf1.Duration", 1) + `,`, + `Delay:` + strings.Replace(fmt.Sprintf("%v", this.Delay), "Duration", "types.Duration", 1) + `,`, `MaxAttempts:` + fmt.Sprintf("%v", this.MaxAttempts) + `,`, - `Window:` + strings.Replace(fmt.Sprintf("%v", this.Window), "Duration", "google_protobuf1.Duration", 1) + `,`, + `Window:` + strings.Replace(fmt.Sprintf("%v", this.Window), "Duration", "types.Duration", 1) + `,`, `}`, }, "") return s @@ -7633,9 +10583,9 @@ func (this *UpdateConfig) String() string { } s := strings.Join([]string{`&UpdateConfig{`, `Parallelism:` + fmt.Sprintf("%v", this.Parallelism) + `,`, - `Delay:` + strings.Replace(strings.Replace(this.Delay.String(), "Duration", "google_protobuf1.Duration", 1), `&`, ``, 1) + `,`, + `Delay:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Delay), "Duration", "types.Duration", 1), `&`, ``, 1) + `,`, `FailureAction:` + fmt.Sprintf("%v", this.FailureAction) + `,`, - `Monitor:` + strings.Replace(fmt.Sprintf("%v", this.Monitor), "Duration", "google_protobuf1.Duration", 1) + `,`, + `Monitor:` + strings.Replace(fmt.Sprintf("%v", this.Monitor), "Duration", "types.Duration", 1) + `,`, `MaxFailureRatio:` + fmt.Sprintf("%v", this.MaxFailureRatio) + `,`, `Order:` + fmt.Sprintf("%v", this.Order) + `,`, `}`, @@ -7648,8 +10598,8 @@ func (this *UpdateStatus) String() string { } s := strings.Join([]string{`&UpdateStatus{`, `State:` + fmt.Sprintf("%v", this.State) + `,`, - `StartedAt:` + strings.Replace(fmt.Sprintf("%v", this.StartedAt), "Timestamp", "google_protobuf.Timestamp", 1) + `,`, - `CompletedAt:` + strings.Replace(fmt.Sprintf("%v", this.CompletedAt), "Timestamp", "google_protobuf.Timestamp", 1) + `,`, + `StartedAt:` + strings.Replace(fmt.Sprintf("%v", this.StartedAt), "Timestamp", "types.Timestamp", 1) + `,`, + `CompletedAt:` + strings.Replace(fmt.Sprintf("%v", this.CompletedAt), "Timestamp", "types.Timestamp", 1) + `,`, `Message:` + fmt.Sprintf("%v", this.Message) + `,`, `}`, }, "") @@ -7671,8 +10621,13 @@ func (this *PortStatus) String() string { if this == nil { return "nil" } + repeatedStringForPorts := "[]*PortConfig{" + for _, f := range this.Ports { + repeatedStringForPorts += strings.Replace(f.String(), "PortConfig", "PortConfig", 1) + "," + } + repeatedStringForPorts += "}" s := strings.Join([]string{`&PortStatus{`, - `Ports:` + strings.Replace(fmt.Sprintf("%v", this.Ports), "PortConfig", "PortConfig", 1) + `,`, + `Ports:` + repeatedStringForPorts + `,`, `}`, }, "") return s @@ -7682,14 +10637,14 @@ func (this *TaskStatus) String() string { return "nil" } s := strings.Join([]string{`&TaskStatus{`, - `Timestamp:` + strings.Replace(fmt.Sprintf("%v", this.Timestamp), "Timestamp", "google_protobuf.Timestamp", 1) + `,`, + `Timestamp:` + strings.Replace(fmt.Sprintf("%v", this.Timestamp), "Timestamp", "types.Timestamp", 1) + `,`, `State:` + fmt.Sprintf("%v", this.State) + `,`, `Message:` + fmt.Sprintf("%v", this.Message) + `,`, `Err:` + fmt.Sprintf("%v", this.Err) + `,`, `RuntimeStatus:` + fmt.Sprintf("%v", this.RuntimeStatus) + `,`, - `PortStatus:` + strings.Replace(fmt.Sprintf("%v", this.PortStatus), "PortStatus", "PortStatus", 1) + `,`, + `PortStatus:` + strings.Replace(this.PortStatus.String(), "PortStatus", "PortStatus", 1) + `,`, `AppliedBy:` + fmt.Sprintf("%v", this.AppliedBy) + `,`, - `AppliedAt:` + strings.Replace(fmt.Sprintf("%v", this.AppliedAt), "Timestamp", "google_protobuf.Timestamp", 1) + `,`, + `AppliedAt:` + strings.Replace(fmt.Sprintf("%v", this.AppliedAt), "Timestamp", "types.Timestamp", 1) + `,`, `}`, }, "") return s @@ -7712,7 +10667,7 @@ func (this *NetworkAttachmentConfig) String() string { for k, _ := range this.DriverAttachmentOpts { keysForDriverAttachmentOpts = append(keysForDriverAttachmentOpts, k) } - sortkeys.Strings(keysForDriverAttachmentOpts) + github_com_gogo_protobuf_sortkeys.Strings(keysForDriverAttachmentOpts) mapStringForDriverAttachmentOpts := "map[string]string{" for _, k := range keysForDriverAttachmentOpts { mapStringForDriverAttachmentOpts += fmt.Sprintf("%v: %v,", k, this.DriverAttachmentOpts[k]) @@ -7735,7 +10690,7 @@ func (this *IPAMConfig) String() string { for k, _ := range this.Reserved { keysForReserved = append(keysForReserved, k) } - sortkeys.Strings(keysForReserved) + github_com_gogo_protobuf_sortkeys.Strings(keysForReserved) mapStringForReserved := "map[string]string{" for _, k := range keysForReserved { mapStringForReserved += fmt.Sprintf("%v: %v,", k, this.Reserved[k]) @@ -7773,7 +10728,7 @@ func (this *Driver) String() string { for k, _ := range this.Options { keysForOptions = append(keysForOptions, k) } - sortkeys.Strings(keysForOptions) + github_com_gogo_protobuf_sortkeys.Strings(keysForOptions) mapStringForOptions := "map[string]string{" for _, k := range keysForOptions { mapStringForOptions += fmt.Sprintf("%v: %v,", k, this.Options[k]) @@ -7790,9 +10745,14 @@ func (this *IPAMOptions) String() string { if this == nil { return "nil" } + repeatedStringForConfigs := "[]*IPAMConfig{" + for _, f := range this.Configs { + repeatedStringForConfigs += strings.Replace(f.String(), "IPAMConfig", "IPAMConfig", 1) + "," + } + repeatedStringForConfigs += "}" s := strings.Join([]string{`&IPAMOptions{`, - `Driver:` + strings.Replace(fmt.Sprintf("%v", this.Driver), "Driver", "Driver", 1) + `,`, - `Configs:` + strings.Replace(fmt.Sprintf("%v", this.Configs), "IPAMConfig", "IPAMConfig", 1) + `,`, + `Driver:` + strings.Replace(this.Driver.String(), "Driver", "Driver", 1) + `,`, + `Configs:` + repeatedStringForConfigs + `,`, `}`, }, "") return s @@ -7813,7 +10773,7 @@ func (this *WeightedPeer) String() string { return "nil" } s := strings.Join([]string{`&WeightedPeer{`, - `Peer:` + strings.Replace(fmt.Sprintf("%v", this.Peer), "Peer", "Peer", 1) + `,`, + `Peer:` + strings.Replace(this.Peer.String(), "Peer", "Peer", 1) + `,`, `Weight:` + fmt.Sprintf("%v", this.Weight) + `,`, `}`, }, "") @@ -7834,8 +10794,13 @@ func (this *AcceptancePolicy) String() string { if this == nil { return "nil" } + repeatedStringForPolicies := "[]*AcceptancePolicy_RoleAdmissionPolicy{" + for _, f := range this.Policies { + repeatedStringForPolicies += strings.Replace(fmt.Sprintf("%v", f), "AcceptancePolicy_RoleAdmissionPolicy", "AcceptancePolicy_RoleAdmissionPolicy", 1) + "," + } + repeatedStringForPolicies += "}" s := strings.Join([]string{`&AcceptancePolicy{`, - `Policies:` + strings.Replace(fmt.Sprintf("%v", this.Policies), "AcceptancePolicy_RoleAdmissionPolicy", "AcceptancePolicy_RoleAdmissionPolicy", 1) + `,`, + `Policies:` + repeatedStringForPolicies + `,`, `}`, }, "") return s @@ -7871,7 +10836,7 @@ func (this *ExternalCA) String() string { for k, _ := range this.Options { keysForOptions = append(keysForOptions, k) } - sortkeys.Strings(keysForOptions) + github_com_gogo_protobuf_sortkeys.Strings(keysForOptions) mapStringForOptions := "map[string]string{" for _, k := range keysForOptions { mapStringForOptions += fmt.Sprintf("%v: %v,", k, this.Options[k]) @@ -7890,9 +10855,14 @@ func (this *CAConfig) String() string { if this == nil { return "nil" } + repeatedStringForExternalCAs := "[]*ExternalCA{" + for _, f := range this.ExternalCAs { + repeatedStringForExternalCAs += strings.Replace(f.String(), "ExternalCA", "ExternalCA", 1) + "," + } + repeatedStringForExternalCAs += "}" s := strings.Join([]string{`&CAConfig{`, - `NodeCertExpiry:` + strings.Replace(fmt.Sprintf("%v", this.NodeCertExpiry), "Duration", "google_protobuf1.Duration", 1) + `,`, - `ExternalCAs:` + strings.Replace(fmt.Sprintf("%v", this.ExternalCAs), "ExternalCA", "ExternalCA", 1) + `,`, + `NodeCertExpiry:` + strings.Replace(fmt.Sprintf("%v", this.NodeCertExpiry), "Duration", "types.Duration", 1) + `,`, + `ExternalCAs:` + repeatedStringForExternalCAs + `,`, `SigningCACert:` + fmt.Sprintf("%v", this.SigningCACert) + `,`, `SigningCAKey:` + fmt.Sprintf("%v", this.SigningCAKey) + `,`, `ForceRotate:` + fmt.Sprintf("%v", this.ForceRotate) + `,`, @@ -7915,7 +10885,7 @@ func (this *TaskDefaults) String() string { return "nil" } s := strings.Join([]string{`&TaskDefaults{`, - `LogDriver:` + strings.Replace(fmt.Sprintf("%v", this.LogDriver), "Driver", "Driver", 1) + `,`, + `LogDriver:` + strings.Replace(this.LogDriver.String(), "Driver", "Driver", 1) + `,`, `}`, }, "") return s @@ -7925,7 +10895,7 @@ func (this *DispatcherConfig) String() string { return "nil" } s := strings.Join([]string{`&DispatcherConfig{`, - `HeartbeatPeriod:` + strings.Replace(fmt.Sprintf("%v", this.HeartbeatPeriod), "Duration", "google_protobuf1.Duration", 1) + `,`, + `HeartbeatPeriod:` + strings.Replace(fmt.Sprintf("%v", this.HeartbeatPeriod), "Duration", "types.Duration", 1) + `,`, `}`, }, "") return s @@ -7988,10 +10958,21 @@ func (this *Placement) String() string { if this == nil { return "nil" } + repeatedStringForPreferences := "[]*PlacementPreference{" + for _, f := range this.Preferences { + repeatedStringForPreferences += strings.Replace(f.String(), "PlacementPreference", "PlacementPreference", 1) + "," + } + repeatedStringForPreferences += "}" + repeatedStringForPlatforms := "[]*Platform{" + for _, f := range this.Platforms { + repeatedStringForPlatforms += strings.Replace(f.String(), "Platform", "Platform", 1) + "," + } + repeatedStringForPlatforms += "}" s := strings.Join([]string{`&Placement{`, `Constraints:` + fmt.Sprintf("%v", this.Constraints) + `,`, - `Preferences:` + strings.Replace(fmt.Sprintf("%v", this.Preferences), "PlacementPreference", "PlacementPreference", 1) + `,`, - `Platforms:` + strings.Replace(fmt.Sprintf("%v", this.Platforms), "Platform", "Platform", 1) + `,`, + `Preferences:` + repeatedStringForPreferences + `,`, + `Platforms:` + repeatedStringForPlatforms + `,`, + `MaxReplicas:` + fmt.Sprintf("%v", this.MaxReplicas) + `,`, `}`, }, "") return s @@ -8016,7 +10997,7 @@ func (this *RootCA) String() string { `CACert:` + fmt.Sprintf("%v", this.CACert) + `,`, `CACertHash:` + fmt.Sprintf("%v", this.CACertHash) + `,`, `JoinTokens:` + strings.Replace(strings.Replace(this.JoinTokens.String(), "JoinTokens", "JoinTokens", 1), `&`, ``, 1) + `,`, - `RootRotation:` + strings.Replace(fmt.Sprintf("%v", this.RootRotation), "RootRotation", "RootRotation", 1) + `,`, + `RootRotation:` + strings.Replace(this.RootRotation.String(), "RootRotation", "RootRotation", 1) + `,`, `LastForcedRotation:` + fmt.Sprintf("%v", this.LastForcedRotation) + `,`, `}`, }, "") @@ -8075,6 +11056,15 @@ func (this *FileTarget) String() string { }, "") return s } +func (this *RuntimeTarget) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RuntimeTarget{`, + `}`, + }, "") + return s +} func (this *SecretReference) String() string { if this == nil { return "nil" @@ -8119,12 +11109,22 @@ func (this *ConfigReference_File) String() string { }, "") return s } +func (this *ConfigReference_Runtime) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ConfigReference_Runtime{`, + `Runtime:` + strings.Replace(fmt.Sprintf("%v", this.Runtime), "RuntimeTarget", "RuntimeTarget", 1) + `,`, + `}`, + }, "") + return s +} func (this *BlacklistedCertificate) String() string { if this == nil { return "nil" } s := strings.Join([]string{`&BlacklistedCertificate{`, - `Expiry:` + strings.Replace(fmt.Sprintf("%v", this.Expiry), "Timestamp", "google_protobuf.Timestamp", 1) + `,`, + `Expiry:` + strings.Replace(fmt.Sprintf("%v", this.Expiry), "Timestamp", "types.Timestamp", 1) + `,`, `}`, }, "") return s @@ -8135,10 +11135,10 @@ func (this *HealthConfig) String() string { } s := strings.Join([]string{`&HealthConfig{`, `Test:` + fmt.Sprintf("%v", this.Test) + `,`, - `Interval:` + strings.Replace(fmt.Sprintf("%v", this.Interval), "Duration", "google_protobuf1.Duration", 1) + `,`, - `Timeout:` + strings.Replace(fmt.Sprintf("%v", this.Timeout), "Duration", "google_protobuf1.Duration", 1) + `,`, + `Interval:` + strings.Replace(fmt.Sprintf("%v", this.Interval), "Duration", "types.Duration", 1) + `,`, + `Timeout:` + strings.Replace(fmt.Sprintf("%v", this.Timeout), "Duration", "types.Duration", 1) + `,`, `Retries:` + fmt.Sprintf("%v", this.Retries) + `,`, - `StartPeriod:` + strings.Replace(fmt.Sprintf("%v", this.StartPeriod), "Duration", "google_protobuf1.Duration", 1) + `,`, + `StartPeriod:` + strings.Replace(fmt.Sprintf("%v", this.StartPeriod), "Duration", "types.Duration", 1) + `,`, `}`, }, "") return s @@ -8208,6 +11208,16 @@ func (this *Privileges_CredentialSpec_Registry) String() string { }, "") return s } +func (this *Privileges_CredentialSpec_Config) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Privileges_CredentialSpec_Config{`, + `Config:` + fmt.Sprintf("%v", this.Config) + `,`, + `}`, + }, "") + return s +} func (this *Privileges_SELinuxContext) String() string { if this == nil { return "nil" @@ -8222,6 +11232,17 @@ func (this *Privileges_SELinuxContext) String() string { }, "") return s } +func (this *JobStatus) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&JobStatus{`, + `JobIteration:` + strings.Replace(strings.Replace(this.JobIteration.String(), "Version", "Version", 1), `&`, ``, 1) + `,`, + `LastExecution:` + strings.Replace(fmt.Sprintf("%v", this.LastExecution), "Timestamp", "types.Timestamp", 1) + `,`, + `}`, + }, "") + return s +} func valueToStringTypes(v interface{}) string { rv := reflect.ValueOf(v) if rv.IsNil() { @@ -8245,7 +11266,7 @@ func (m *Version) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8273,7 +11294,7 @@ func (m *Version) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Index |= (uint64(b) & 0x7F) << shift + m.Index |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8284,7 +11305,7 @@ func (m *Version) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -8314,7 +11335,7 @@ func (m *IndexEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8342,7 +11363,7 @@ func (m *IndexEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8352,6 +11373,9 @@ func (m *IndexEntry) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8371,7 +11395,7 @@ func (m *IndexEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8381,6 +11405,9 @@ func (m *IndexEntry) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8392,7 +11419,7 @@ func (m *IndexEntry) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -8422,7 +11449,7 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8450,7 +11477,7 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8460,6 +11487,9 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8479,7 +11509,7 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -8488,6 +11518,9 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8508,7 +11541,7 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8525,7 +11558,7 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8535,6 +11568,9 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -8551,7 +11587,7 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8561,6 +11597,9 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -8572,7 +11611,7 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > postIndex { @@ -8597,7 +11636,7 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -8606,6 +11645,9 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8620,7 +11662,7 @@ func (m *Annotations) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -8650,7 +11692,7 @@ func (m *NamedGenericResource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8678,7 +11720,7 @@ func (m *NamedGenericResource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8688,6 +11730,9 @@ func (m *NamedGenericResource) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8707,7 +11752,7 @@ func (m *NamedGenericResource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8717,6 +11762,9 @@ func (m *NamedGenericResource) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8728,7 +11776,7 @@ func (m *NamedGenericResource) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -8758,7 +11806,7 @@ func (m *DiscreteGenericResource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8786,7 +11834,7 @@ func (m *DiscreteGenericResource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8796,6 +11844,9 @@ func (m *DiscreteGenericResource) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8815,7 +11866,7 @@ func (m *DiscreteGenericResource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Value |= (int64(b) & 0x7F) << shift + m.Value |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -8826,7 +11877,7 @@ func (m *DiscreteGenericResource) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -8856,7 +11907,7 @@ func (m *GenericResource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8884,7 +11935,7 @@ func (m *GenericResource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -8893,6 +11944,9 @@ func (m *GenericResource) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8916,7 +11970,7 @@ func (m *GenericResource) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -8925,6 +11979,9 @@ func (m *GenericResource) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -8940,7 +11997,7 @@ func (m *GenericResource) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -8970,7 +12027,7 @@ func (m *Resources) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -8998,7 +12055,7 @@ func (m *Resources) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.NanoCPUs |= (int64(b) & 0x7F) << shift + m.NanoCPUs |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -9017,7 +12074,7 @@ func (m *Resources) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.MemoryBytes |= (int64(b) & 0x7F) << shift + m.MemoryBytes |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -9036,7 +12093,7 @@ func (m *Resources) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9045,6 +12102,9 @@ func (m *Resources) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9059,7 +12119,7 @@ func (m *Resources) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -9089,7 +12149,7 @@ func (m *ResourceRequirements) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9117,7 +12177,7 @@ func (m *ResourceRequirements) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9126,6 +12186,9 @@ func (m *ResourceRequirements) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9150,7 +12213,7 @@ func (m *ResourceRequirements) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9159,6 +12222,9 @@ func (m *ResourceRequirements) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9169,13 +12235,85 @@ func (m *ResourceRequirements) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SwapBytes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SwapBytes == nil { + m.SwapBytes = &types.Int64Value{} + } + if err := m.SwapBytes.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MemorySwappiness", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.MemorySwappiness == nil { + m.MemorySwappiness = &types.Int64Value{} + } + if err := m.MemorySwappiness.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -9205,7 +12343,7 @@ func (m *Platform) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9233,7 +12371,7 @@ func (m *Platform) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9243,6 +12381,9 @@ func (m *Platform) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9262,7 +12403,7 @@ func (m *Platform) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9272,6 +12413,9 @@ func (m *Platform) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9283,7 +12427,7 @@ func (m *Platform) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -9313,7 +12457,7 @@ func (m *PluginDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9341,7 +12485,7 @@ func (m *PluginDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9351,6 +12495,9 @@ func (m *PluginDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9370,7 +12517,7 @@ func (m *PluginDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9380,6 +12527,9 @@ func (m *PluginDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9391,7 +12541,7 @@ func (m *PluginDescription) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -9421,7 +12571,7 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9449,7 +12599,7 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9459,6 +12609,9 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9478,7 +12631,7 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9487,6 +12640,9 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9507,7 +12663,7 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9524,7 +12680,7 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9534,6 +12690,9 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -9550,7 +12709,7 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9560,6 +12719,9 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -9571,7 +12733,7 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > postIndex { @@ -9596,7 +12758,7 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9605,6 +12767,9 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9619,7 +12784,7 @@ func (m *EngineDescription) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -9649,7 +12814,7 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9677,7 +12842,7 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9687,6 +12852,9 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9706,7 +12874,7 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9715,6 +12883,9 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9739,7 +12910,7 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9748,6 +12919,9 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9772,7 +12946,7 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9781,6 +12955,9 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9805,7 +12982,7 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9814,6 +12991,9 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9838,7 +13018,7 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9850,7 +13030,7 @@ func (m *NodeDescription) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -9880,7 +13060,7 @@ func (m *NodeTLSInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -9908,7 +13088,7 @@ func (m *NodeTLSInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9917,6 +13097,9 @@ func (m *NodeTLSInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9939,7 +13122,7 @@ func (m *NodeTLSInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9948,6 +13131,9 @@ func (m *NodeTLSInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9970,7 +13156,7 @@ func (m *NodeTLSInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -9979,6 +13165,9 @@ func (m *NodeTLSInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -9993,7 +13182,7 @@ func (m *NodeTLSInfo) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -10023,7 +13212,7 @@ func (m *RaftMemberStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10051,7 +13240,7 @@ func (m *RaftMemberStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10071,7 +13260,7 @@ func (m *RaftMemberStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Reachability |= (RaftMemberStatus_Reachability(b) & 0x7F) << shift + m.Reachability |= RaftMemberStatus_Reachability(b&0x7F) << shift if b < 0x80 { break } @@ -10090,7 +13279,7 @@ func (m *RaftMemberStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10100,6 +13289,9 @@ func (m *RaftMemberStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10111,7 +13303,7 @@ func (m *RaftMemberStatus) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -10141,7 +13333,7 @@ func (m *NodeStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10169,7 +13361,7 @@ func (m *NodeStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.State |= (NodeStatus_State(b) & 0x7F) << shift + m.State |= NodeStatus_State(b&0x7F) << shift if b < 0x80 { break } @@ -10188,7 +13380,7 @@ func (m *NodeStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10198,6 +13390,9 @@ func (m *NodeStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10217,7 +13412,7 @@ func (m *NodeStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10227,6 +13422,9 @@ func (m *NodeStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10238,7 +13436,7 @@ func (m *NodeStatus) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -10268,7 +13466,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10296,7 +13494,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10306,6 +13504,9 @@ func (m *Image) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10317,7 +13518,7 @@ func (m *Image) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -10347,7 +13548,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10375,7 +13576,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Type |= (Mount_MountType(b) & 0x7F) << shift + m.Type |= Mount_MountType(b&0x7F) << shift if b < 0x80 { break } @@ -10394,7 +13595,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10404,6 +13605,9 @@ func (m *Mount) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10423,7 +13627,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10433,6 +13637,9 @@ func (m *Mount) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10452,7 +13659,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10472,7 +13679,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10481,6 +13688,9 @@ func (m *Mount) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10505,7 +13715,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10514,6 +13724,9 @@ func (m *Mount) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10538,7 +13751,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10547,6 +13760,9 @@ func (m *Mount) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10571,7 +13787,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Consistency |= (Mount_MountConsistency(b) & 0x7F) << shift + m.Consistency |= Mount_MountConsistency(b&0x7F) << shift if b < 0x80 { break } @@ -10582,7 +13798,7 @@ func (m *Mount) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -10612,7 +13828,7 @@ func (m *Mount_BindOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10640,18 +13856,38 @@ func (m *Mount_BindOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Propagation |= (Mount_BindOptions_MountPropagation(b) & 0x7F) << shift + m.Propagation |= Mount_BindOptions_MountPropagation(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NonRecursive", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift if b < 0x80 { break } } + m.NonRecursive = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -10681,7 +13917,7 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10709,7 +13945,7 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10729,7 +13965,7 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10738,6 +13974,9 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10758,7 +13997,7 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10775,7 +14014,7 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10785,6 +14024,9 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -10801,7 +14043,7 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10811,6 +14053,9 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -10822,7 +14067,7 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > postIndex { @@ -10847,7 +14092,7 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -10856,6 +14101,9 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -10872,7 +14120,7 @@ func (m *Mount_VolumeOptions) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -10902,7 +14150,7 @@ func (m *Mount_TmpfsOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -10930,7 +14178,7 @@ func (m *Mount_TmpfsOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.SizeBytes |= (int64(b) & 0x7F) << shift + m.SizeBytes |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -10939,7 +14187,26 @@ func (m *Mount_TmpfsOptions) Unmarshal(dAtA []byte) error { if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) } - m.Mode = 0 + m.Mode = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Mode |= os.FileMode(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTypes @@ -10949,18 +14216,31 @@ func (m *Mount_TmpfsOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Mode |= (os.FileMode(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Options = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -10990,7 +14270,7 @@ func (m *RestartPolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11018,7 +14298,7 @@ func (m *RestartPolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Condition |= (RestartPolicy_RestartCondition(b) & 0x7F) << shift + m.Condition |= RestartPolicy_RestartCondition(b&0x7F) << shift if b < 0x80 { break } @@ -11037,7 +14317,7 @@ func (m *RestartPolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11046,11 +14326,14 @@ func (m *RestartPolicy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Delay == nil { - m.Delay = &google_protobuf1.Duration{} + m.Delay = &types.Duration{} } if err := m.Delay.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -11070,7 +14353,7 @@ func (m *RestartPolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.MaxAttempts |= (uint64(b) & 0x7F) << shift + m.MaxAttempts |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11089,7 +14372,7 @@ func (m *RestartPolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11098,11 +14381,14 @@ func (m *RestartPolicy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Window == nil { - m.Window = &google_protobuf1.Duration{} + m.Window = &types.Duration{} } if err := m.Window.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -11114,7 +14400,7 @@ func (m *RestartPolicy) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -11144,7 +14430,7 @@ func (m *UpdateConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11172,7 +14458,7 @@ func (m *UpdateConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Parallelism |= (uint64(b) & 0x7F) << shift + m.Parallelism |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11191,7 +14477,7 @@ func (m *UpdateConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11200,10 +14486,13 @@ func (m *UpdateConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } - if err := types.StdDurationUnmarshal(&m.Delay, dAtA[iNdEx:postIndex]); err != nil { + if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.Delay, dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -11221,7 +14510,7 @@ func (m *UpdateConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.FailureAction |= (UpdateConfig_FailureAction(b) & 0x7F) << shift + m.FailureAction |= UpdateConfig_FailureAction(b&0x7F) << shift if b < 0x80 { break } @@ -11240,7 +14529,7 @@ func (m *UpdateConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11249,11 +14538,14 @@ func (m *UpdateConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Monitor == nil { - m.Monitor = &google_protobuf1.Duration{} + m.Monitor = &types.Duration{} } if err := m.Monitor.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -11267,7 +14559,7 @@ func (m *UpdateConfig) Unmarshal(dAtA []byte) error { if (iNdEx + 4) > l { return io.ErrUnexpectedEOF } - v = uint32(binary.LittleEndian.Uint32(dAtA[iNdEx:])) + v = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:])) iNdEx += 4 m.MaxFailureRatio = float32(math.Float32frombits(v)) case 6: @@ -11284,7 +14576,7 @@ func (m *UpdateConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Order |= (UpdateConfig_UpdateOrder(b) & 0x7F) << shift + m.Order |= UpdateConfig_UpdateOrder(b&0x7F) << shift if b < 0x80 { break } @@ -11295,7 +14587,7 @@ func (m *UpdateConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -11325,7 +14617,7 @@ func (m *UpdateStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11353,7 +14645,7 @@ func (m *UpdateStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.State |= (UpdateStatus_UpdateState(b) & 0x7F) << shift + m.State |= UpdateStatus_UpdateState(b&0x7F) << shift if b < 0x80 { break } @@ -11372,7 +14664,7 @@ func (m *UpdateStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11381,11 +14673,14 @@ func (m *UpdateStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartedAt == nil { - m.StartedAt = &google_protobuf.Timestamp{} + m.StartedAt = &types.Timestamp{} } if err := m.StartedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -11405,7 +14700,7 @@ func (m *UpdateStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11414,11 +14709,14 @@ func (m *UpdateStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.CompletedAt == nil { - m.CompletedAt = &google_protobuf.Timestamp{} + m.CompletedAt = &types.Timestamp{} } if err := m.CompletedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -11438,7 +14736,7 @@ func (m *UpdateStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11448,6 +14746,9 @@ func (m *UpdateStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -11459,7 +14760,7 @@ func (m *UpdateStatus) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -11489,7 +14790,7 @@ func (m *ContainerStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11517,7 +14818,7 @@ func (m *ContainerStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11527,6 +14828,9 @@ func (m *ContainerStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -11546,7 +14850,7 @@ func (m *ContainerStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.PID |= (int32(b) & 0x7F) << shift + m.PID |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -11565,7 +14869,7 @@ func (m *ContainerStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ExitCode |= (int32(b) & 0x7F) << shift + m.ExitCode |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -11576,7 +14880,7 @@ func (m *ContainerStatus) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -11606,7 +14910,7 @@ func (m *PortStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11634,7 +14938,7 @@ func (m *PortStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11643,6 +14947,9 @@ func (m *PortStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -11657,7 +14964,7 @@ func (m *PortStatus) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -11687,7 +14994,7 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11715,7 +15022,7 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11724,11 +15031,14 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Timestamp == nil { - m.Timestamp = &google_protobuf.Timestamp{} + m.Timestamp = &types.Timestamp{} } if err := m.Timestamp.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -11748,7 +15058,7 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.State |= (TaskState(b) & 0x7F) << shift + m.State |= TaskState(b&0x7F) << shift if b < 0x80 { break } @@ -11767,7 +15077,7 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11777,6 +15087,9 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -11796,7 +15109,7 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11806,6 +15119,9 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -11825,7 +15141,7 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11834,6 +15150,9 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -11857,7 +15176,7 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11866,6 +15185,9 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -11890,7 +15212,7 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -11900,6 +15222,9 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -11919,7 +15244,7 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -11928,11 +15253,14 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.AppliedAt == nil { - m.AppliedAt = &google_protobuf.Timestamp{} + m.AppliedAt = &types.Timestamp{} } if err := m.AppliedAt.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -11944,7 +15272,7 @@ func (m *TaskStatus) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -11974,7 +15302,7 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12002,7 +15330,7 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12012,6 +15340,9 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12031,7 +15362,7 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12041,6 +15372,9 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12060,7 +15394,7 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12070,6 +15404,9 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12089,7 +15426,7 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -12098,6 +15435,9 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12118,7 +15458,7 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12135,7 +15475,7 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12145,6 +15485,9 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -12161,7 +15504,7 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12171,6 +15514,9 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -12182,7 +15528,7 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > postIndex { @@ -12199,7 +15545,7 @@ func (m *NetworkAttachmentConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -12229,7 +15575,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12257,7 +15603,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Family |= (IPAMConfig_AddressFamily(b) & 0x7F) << shift + m.Family |= IPAMConfig_AddressFamily(b&0x7F) << shift if b < 0x80 { break } @@ -12276,7 +15622,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12286,6 +15632,9 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12305,7 +15654,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12315,6 +15664,9 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12334,7 +15686,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12344,6 +15696,9 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12363,7 +15718,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -12372,6 +15727,9 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12392,7 +15750,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12409,7 +15767,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12419,6 +15777,9 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -12435,7 +15796,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12445,6 +15806,9 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -12456,7 +15820,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > postIndex { @@ -12473,7 +15837,7 @@ func (m *IPAMConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -12503,7 +15867,7 @@ func (m *PortConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12531,7 +15895,7 @@ func (m *PortConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12541,6 +15905,9 @@ func (m *PortConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12560,7 +15927,7 @@ func (m *PortConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Protocol |= (PortConfig_Protocol(b) & 0x7F) << shift + m.Protocol |= PortConfig_Protocol(b&0x7F) << shift if b < 0x80 { break } @@ -12579,7 +15946,7 @@ func (m *PortConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.TargetPort |= (uint32(b) & 0x7F) << shift + m.TargetPort |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -12598,7 +15965,7 @@ func (m *PortConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.PublishedPort |= (uint32(b) & 0x7F) << shift + m.PublishedPort |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -12617,7 +15984,7 @@ func (m *PortConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.PublishMode |= (PortConfig_PublishMode(b) & 0x7F) << shift + m.PublishMode |= PortConfig_PublishMode(b&0x7F) << shift if b < 0x80 { break } @@ -12628,7 +15995,7 @@ func (m *PortConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -12658,7 +16025,7 @@ func (m *Driver) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12686,7 +16053,7 @@ func (m *Driver) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12696,6 +16063,9 @@ func (m *Driver) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12715,7 +16085,7 @@ func (m *Driver) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -12724,6 +16094,9 @@ func (m *Driver) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12744,7 +16117,7 @@ func (m *Driver) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12761,7 +16134,7 @@ func (m *Driver) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12771,6 +16144,9 @@ func (m *Driver) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -12787,7 +16163,7 @@ func (m *Driver) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12797,6 +16173,9 @@ func (m *Driver) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -12808,7 +16187,7 @@ func (m *Driver) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > postIndex { @@ -12825,7 +16204,7 @@ func (m *Driver) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -12855,7 +16234,7 @@ func (m *IPAMOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12883,7 +16262,7 @@ func (m *IPAMOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -12892,6 +16271,9 @@ func (m *IPAMOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12916,7 +16298,7 @@ func (m *IPAMOptions) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -12925,6 +16307,9 @@ func (m *IPAMOptions) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -12939,7 +16324,7 @@ func (m *IPAMOptions) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -12969,7 +16354,7 @@ func (m *Peer) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -12997,7 +16382,7 @@ func (m *Peer) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13007,6 +16392,9 @@ func (m *Peer) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13026,7 +16414,7 @@ func (m *Peer) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13036,6 +16424,9 @@ func (m *Peer) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13047,7 +16438,7 @@ func (m *Peer) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -13077,7 +16468,7 @@ func (m *WeightedPeer) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13105,7 +16496,7 @@ func (m *WeightedPeer) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13114,6 +16505,9 @@ func (m *WeightedPeer) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13138,7 +16532,7 @@ func (m *WeightedPeer) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Weight |= (int64(b) & 0x7F) << shift + m.Weight |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -13149,7 +16543,7 @@ func (m *WeightedPeer) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -13179,7 +16573,7 @@ func (m *IssuanceStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13207,7 +16601,7 @@ func (m *IssuanceStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.State |= (IssuanceStatus_State(b) & 0x7F) << shift + m.State |= IssuanceStatus_State(b&0x7F) << shift if b < 0x80 { break } @@ -13226,7 +16620,7 @@ func (m *IssuanceStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13236,6 +16630,9 @@ func (m *IssuanceStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13247,7 +16644,7 @@ func (m *IssuanceStatus) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -13277,7 +16674,7 @@ func (m *AcceptancePolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13305,7 +16702,7 @@ func (m *AcceptancePolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13314,6 +16711,9 @@ func (m *AcceptancePolicy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13328,7 +16728,7 @@ func (m *AcceptancePolicy) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -13358,7 +16758,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13386,7 +16786,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Role |= (NodeRole(b) & 0x7F) << shift + m.Role |= NodeRole(b&0x7F) << shift if b < 0x80 { break } @@ -13405,7 +16805,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13425,7 +16825,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13434,6 +16834,9 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13450,7 +16853,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -13480,7 +16883,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Unmarshal(dAtA []byte) err } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13508,7 +16911,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Unmarshal(dAtA []byte) err } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13517,6 +16920,9 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Unmarshal(dAtA []byte) err return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13539,7 +16945,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Unmarshal(dAtA []byte) err } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13549,6 +16955,9 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Unmarshal(dAtA []byte) err return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13560,7 +16969,7 @@ func (m *AcceptancePolicy_RoleAdmissionPolicy_Secret) Unmarshal(dAtA []byte) err if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -13590,7 +16999,7 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13618,7 +17027,7 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Protocol |= (ExternalCA_CAProtocol(b) & 0x7F) << shift + m.Protocol |= ExternalCA_CAProtocol(b&0x7F) << shift if b < 0x80 { break } @@ -13637,7 +17046,7 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13647,6 +17056,9 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13666,7 +17078,7 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13675,6 +17087,9 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13695,7 +17110,7 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13712,7 +17127,7 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13722,6 +17137,9 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -13738,7 +17156,7 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapvalue |= (uint64(b) & 0x7F) << shift + stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13748,6 +17166,9 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthTypes + } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } @@ -13759,7 +17180,7 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > postIndex { @@ -13784,7 +17205,7 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13793,6 +17214,9 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13807,7 +17231,7 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -13837,7 +17261,7 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -13865,7 +17289,7 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13874,11 +17298,14 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.NodeCertExpiry == nil { - m.NodeCertExpiry = &google_protobuf1.Duration{} + m.NodeCertExpiry = &types.Duration{} } if err := m.NodeCertExpiry.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -13898,7 +17325,7 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13907,6 +17334,9 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13929,7 +17359,7 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13938,6 +17368,9 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13960,7 +17393,7 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -13969,6 +17402,9 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -13991,7 +17427,7 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ForceRotate |= (uint64(b) & 0x7F) << shift + m.ForceRotate |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14002,7 +17438,7 @@ func (m *CAConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -14032,7 +17468,7 @@ func (m *OrchestrationConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14060,7 +17496,7 @@ func (m *OrchestrationConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.TaskHistoryRetentionLimit |= (int64(b) & 0x7F) << shift + m.TaskHistoryRetentionLimit |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -14071,7 +17507,7 @@ func (m *OrchestrationConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -14101,7 +17537,7 @@ func (m *TaskDefaults) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14129,7 +17565,7 @@ func (m *TaskDefaults) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14138,6 +17574,9 @@ func (m *TaskDefaults) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -14154,7 +17593,7 @@ func (m *TaskDefaults) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -14184,7 +17623,7 @@ func (m *DispatcherConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14212,7 +17651,7 @@ func (m *DispatcherConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14221,11 +17660,14 @@ func (m *DispatcherConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.HeartbeatPeriod == nil { - m.HeartbeatPeriod = &google_protobuf1.Duration{} + m.HeartbeatPeriod = &types.Duration{} } if err := m.HeartbeatPeriod.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -14237,7 +17679,7 @@ func (m *DispatcherConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -14267,7 +17709,7 @@ func (m *RaftConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14295,7 +17737,7 @@ func (m *RaftConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.SnapshotInterval |= (uint64(b) & 0x7F) << shift + m.SnapshotInterval |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14314,7 +17756,7 @@ func (m *RaftConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.KeepOldSnapshots |= (uint64(b) & 0x7F) << shift + m.KeepOldSnapshots |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14333,7 +17775,7 @@ func (m *RaftConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.LogEntriesForSlowFollowers |= (uint64(b) & 0x7F) << shift + m.LogEntriesForSlowFollowers |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14352,7 +17794,7 @@ func (m *RaftConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.HeartbeatTick |= (uint32(b) & 0x7F) << shift + m.HeartbeatTick |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -14371,7 +17813,7 @@ func (m *RaftConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ElectionTick |= (uint32(b) & 0x7F) << shift + m.ElectionTick |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -14382,7 +17824,7 @@ func (m *RaftConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -14412,7 +17854,7 @@ func (m *EncryptionConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14440,7 +17882,7 @@ func (m *EncryptionConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14452,7 +17894,7 @@ func (m *EncryptionConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -14482,7 +17924,7 @@ func (m *SpreadOver) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14510,7 +17952,7 @@ func (m *SpreadOver) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14520,6 +17962,9 @@ func (m *SpreadOver) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -14531,7 +17976,7 @@ func (m *SpreadOver) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -14561,7 +18006,7 @@ func (m *PlacementPreference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14589,7 +18034,7 @@ func (m *PlacementPreference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14598,6 +18043,9 @@ func (m *PlacementPreference) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -14613,7 +18061,7 @@ func (m *PlacementPreference) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -14643,7 +18091,7 @@ func (m *Placement) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14671,7 +18119,7 @@ func (m *Placement) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14681,6 +18129,9 @@ func (m *Placement) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -14700,7 +18151,7 @@ func (m *Placement) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14709,6 +18160,9 @@ func (m *Placement) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -14731,7 +18185,7 @@ func (m *Placement) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14740,6 +18194,9 @@ func (m *Placement) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -14748,13 +18205,32 @@ func (m *Placement) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxReplicas", wireType) + } + m.MaxReplicas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MaxReplicas |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -14784,7 +18260,7 @@ func (m *JoinTokens) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14812,7 +18288,7 @@ func (m *JoinTokens) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14822,6 +18298,9 @@ func (m *JoinTokens) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -14841,7 +18320,7 @@ func (m *JoinTokens) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14851,6 +18330,9 @@ func (m *JoinTokens) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -14862,7 +18344,7 @@ func (m *JoinTokens) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -14892,7 +18374,7 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14920,7 +18402,7 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14929,6 +18411,9 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -14951,7 +18436,7 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -14960,6 +18445,9 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -14982,7 +18470,7 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -14992,6 +18480,9 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15011,7 +18502,7 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15020,6 +18511,9 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15041,7 +18535,7 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15050,6 +18544,9 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15074,7 +18571,7 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.LastForcedRotation |= (uint64(b) & 0x7F) << shift + m.LastForcedRotation |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15085,7 +18582,7 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -15115,7 +18612,7 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15143,7 +18640,7 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Role |= (NodeRole(b) & 0x7F) << shift + m.Role |= NodeRole(b&0x7F) << shift if b < 0x80 { break } @@ -15162,7 +18659,7 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15171,6 +18668,9 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15193,7 +18693,7 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15202,6 +18702,9 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15223,7 +18726,7 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15232,6 +18735,9 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15254,7 +18760,7 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15264,6 +18770,9 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15275,7 +18784,7 @@ func (m *Certificate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -15305,7 +18814,7 @@ func (m *EncryptionKey) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15333,7 +18842,7 @@ func (m *EncryptionKey) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15343,6 +18852,9 @@ func (m *EncryptionKey) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15362,7 +18874,7 @@ func (m *EncryptionKey) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Algorithm |= (EncryptionKey_Algorithm(b) & 0x7F) << shift + m.Algorithm |= EncryptionKey_Algorithm(b&0x7F) << shift if b < 0x80 { break } @@ -15381,7 +18893,7 @@ func (m *EncryptionKey) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15390,6 +18902,9 @@ func (m *EncryptionKey) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15412,7 +18927,7 @@ func (m *EncryptionKey) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.LamportTime |= (uint64(b) & 0x7F) << shift + m.LamportTime |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15423,7 +18938,7 @@ func (m *EncryptionKey) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -15453,7 +18968,7 @@ func (m *ManagerStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15481,7 +18996,7 @@ func (m *ManagerStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.RaftID |= (uint64(b) & 0x7F) << shift + m.RaftID |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15500,7 +19015,7 @@ func (m *ManagerStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15510,6 +19025,9 @@ func (m *ManagerStatus) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15529,7 +19047,7 @@ func (m *ManagerStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15549,7 +19067,7 @@ func (m *ManagerStatus) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Reachability |= (RaftMemberStatus_Reachability(b) & 0x7F) << shift + m.Reachability |= RaftMemberStatus_Reachability(b&0x7F) << shift if b < 0x80 { break } @@ -15560,7 +19078,7 @@ func (m *ManagerStatus) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -15590,7 +19108,7 @@ func (m *FileTarget) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15618,7 +19136,7 @@ func (m *FileTarget) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15628,6 +19146,9 @@ func (m *FileTarget) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15647,7 +19168,7 @@ func (m *FileTarget) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15657,6 +19178,9 @@ func (m *FileTarget) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15676,7 +19200,7 @@ func (m *FileTarget) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15686,6 +19210,9 @@ func (m *FileTarget) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15705,7 +19232,7 @@ func (m *FileTarget) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Mode |= (os.FileMode(b) & 0x7F) << shift + m.Mode |= os.FileMode(b&0x7F) << shift if b < 0x80 { break } @@ -15716,7 +19243,57 @@ func (m *FileTarget) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RuntimeTarget) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RuntimeTarget: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RuntimeTarget: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -15746,7 +19323,7 @@ func (m *SecretReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15774,7 +19351,7 @@ func (m *SecretReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15784,6 +19361,9 @@ func (m *SecretReference) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15803,7 +19383,7 @@ func (m *SecretReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15813,6 +19393,9 @@ func (m *SecretReference) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15832,7 +19415,7 @@ func (m *SecretReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15841,6 +19424,9 @@ func (m *SecretReference) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15856,7 +19442,7 @@ func (m *SecretReference) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -15886,7 +19472,7 @@ func (m *ConfigReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15914,7 +19500,7 @@ func (m *ConfigReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15924,6 +19510,9 @@ func (m *ConfigReference) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15943,7 +19532,7 @@ func (m *ConfigReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -15953,6 +19542,9 @@ func (m *ConfigReference) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15972,7 +19564,7 @@ func (m *ConfigReference) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -15981,6 +19573,9 @@ func (m *ConfigReference) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -15990,13 +19585,48 @@ func (m *ConfigReference) Unmarshal(dAtA []byte) error { } m.Target = &ConfigReference_File{v} iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Runtime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &RuntimeTarget{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Target = &ConfigReference_Runtime{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -16026,7 +19656,7 @@ func (m *BlacklistedCertificate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16054,7 +19684,7 @@ func (m *BlacklistedCertificate) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16063,11 +19693,14 @@ func (m *BlacklistedCertificate) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Expiry == nil { - m.Expiry = &google_protobuf.Timestamp{} + m.Expiry = &types.Timestamp{} } if err := m.Expiry.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -16079,7 +19712,7 @@ func (m *BlacklistedCertificate) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -16109,7 +19742,7 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16137,7 +19770,7 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16147,6 +19780,9 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16166,7 +19802,7 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16175,11 +19811,14 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Interval == nil { - m.Interval = &google_protobuf1.Duration{} + m.Interval = &types.Duration{} } if err := m.Interval.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -16199,7 +19838,7 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16208,11 +19847,14 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.Timeout == nil { - m.Timeout = &google_protobuf1.Duration{} + m.Timeout = &types.Duration{} } if err := m.Timeout.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -16232,7 +19874,7 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Retries |= (int32(b) & 0x7F) << shift + m.Retries |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -16251,7 +19893,7 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16260,11 +19902,14 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } if m.StartPeriod == nil { - m.StartPeriod = &google_protobuf1.Duration{} + m.StartPeriod = &types.Duration{} } if err := m.StartPeriod.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -16276,7 +19921,7 @@ func (m *HealthConfig) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -16306,7 +19951,7 @@ func (m *MaybeEncryptedRecord) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16334,7 +19979,7 @@ func (m *MaybeEncryptedRecord) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Algorithm |= (MaybeEncryptedRecord_Algorithm(b) & 0x7F) << shift + m.Algorithm |= MaybeEncryptedRecord_Algorithm(b&0x7F) << shift if b < 0x80 { break } @@ -16353,7 +19998,7 @@ func (m *MaybeEncryptedRecord) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16362,6 +20007,9 @@ func (m *MaybeEncryptedRecord) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16384,7 +20032,7 @@ func (m *MaybeEncryptedRecord) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16393,6 +20041,9 @@ func (m *MaybeEncryptedRecord) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16407,7 +20058,7 @@ func (m *MaybeEncryptedRecord) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -16437,7 +20088,7 @@ func (m *RootRotation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16465,7 +20116,7 @@ func (m *RootRotation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16474,6 +20125,9 @@ func (m *RootRotation) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16496,7 +20150,7 @@ func (m *RootRotation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16505,6 +20159,9 @@ func (m *RootRotation) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16527,7 +20184,7 @@ func (m *RootRotation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16536,6 +20193,9 @@ func (m *RootRotation) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16550,7 +20210,7 @@ func (m *RootRotation) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -16580,7 +20240,7 @@ func (m *Privileges) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16608,7 +20268,7 @@ func (m *Privileges) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16617,6 +20277,9 @@ func (m *Privileges) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16641,7 +20304,7 @@ func (m *Privileges) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16650,6 +20313,9 @@ func (m *Privileges) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16666,7 +20332,7 @@ func (m *Privileges) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -16696,7 +20362,7 @@ func (m *Privileges_CredentialSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16724,7 +20390,7 @@ func (m *Privileges_CredentialSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16734,6 +20400,9 @@ func (m *Privileges_CredentialSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16753,7 +20422,7 @@ func (m *Privileges_CredentialSpec) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16763,18 +20432,53 @@ func (m *Privileges_CredentialSpec) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } m.Source = &Privileges_CredentialSpec_Registry{string(dAtA[iNdEx:postIndex])} iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Source = &Privileges_CredentialSpec_Config{string(dAtA[iNdEx:postIndex])} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -16804,7 +20508,7 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16832,7 +20536,7 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -16852,7 +20556,7 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16862,6 +20566,9 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16881,7 +20588,7 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16891,6 +20598,9 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16910,7 +20620,7 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16920,6 +20630,9 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16939,7 +20652,7 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -16949,6 +20662,9 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { return ErrInvalidLengthTypes } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -16960,7 +20676,126 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *JobStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: JobStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: JobStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JobIteration", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.JobIteration.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LastExecution", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.LastExecution == nil { + m.LastExecution = &types.Timestamp{} + } + if err := m.LastExecution.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTypes } if (iNdEx + skippy) > l { @@ -16978,6 +20813,7 @@ func (m *Privileges_SELinuxContext) Unmarshal(dAtA []byte) error { func skipTypes(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -17009,10 +20845,8 @@ func skipTypes(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -17029,380 +20863,34 @@ func skipTypes(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthTypes } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTypes - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipTypes(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTypes + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthTypes + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthTypes = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthTypes = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTypes = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("github.com/docker/swarmkit/api/types.proto", fileDescriptorTypes) } - -var fileDescriptorTypes = []byte{ - // 5135 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x7a, 0x4d, 0x6c, 0x24, 0x49, - 0x56, 0xbf, 0xeb, 0xd3, 0x55, 0xaf, 0xca, 0x76, 0x76, 0xb4, 0xb7, 0xc7, 0x5d, 0xdb, 0x63, 0xd7, - 0xe4, 0x4c, 0xef, 0xcc, 0xf6, 0xce, 0xbf, 0xfa, 0x6b, 0x77, 0xd5, 0x33, 0xf3, 0xdf, 0x9d, 0xa9, - 0x8f, 0x74, 0xbb, 0xb6, 0xed, 0xaa, 0x52, 0x54, 0xb9, 0x7b, 0xf7, 0x2f, 0xfd, 0x49, 0xd2, 0x99, - 0xe1, 0x72, 0x8e, 0xb3, 0x32, 0x8a, 0xcc, 0x2c, 0xbb, 0x8b, 0x05, 0x31, 0xe2, 0x00, 0xc8, 0x27, - 0xf6, 0x02, 0xbb, 0x42, 0x46, 0x48, 0x70, 0xe3, 0xc0, 0x01, 0x24, 0x04, 0xa7, 0x41, 0x42, 0x68, - 0xc5, 0x05, 0x16, 0x24, 0xb4, 0x02, 0xc9, 0xb0, 0x3e, 0x70, 0x43, 0x70, 0x41, 0x5c, 0x38, 0xa0, - 0xf8, 0xc8, 0xac, 0x74, 0x75, 0xda, 0x9e, 0x61, 0xf7, 0x62, 0x57, 0xbc, 0xf7, 0x7b, 0x2f, 0x22, - 0x5e, 0x44, 0xbc, 0x78, 0xef, 0x45, 0xc2, 0xbd, 0xa1, 0x1d, 0x1c, 0x4c, 0xf6, 0x6a, 0x26, 0x1d, - 0xdd, 0xb7, 0xa8, 0x79, 0x48, 0xbc, 0xfb, 0xfe, 0xb1, 0xe1, 0x8d, 0x0e, 0xed, 0xe0, 0xbe, 0x31, - 0xb6, 0xef, 0x07, 0xd3, 0x31, 0xf1, 0x6b, 0x63, 0x8f, 0x06, 0x14, 0x21, 0x01, 0xa8, 0x85, 0x80, - 0xda, 0xd1, 0xc3, 0xca, 0xc6, 0x90, 0xd2, 0xa1, 0x43, 0xee, 0x73, 0xc4, 0xde, 0x64, 0xff, 0x7e, - 0x60, 0x8f, 0x88, 0x1f, 0x18, 0xa3, 0xb1, 0x10, 0xaa, 0xac, 0xcf, 0x03, 0xac, 0x89, 0x67, 0x04, - 0x36, 0x75, 0x25, 0x7f, 0x75, 0x48, 0x87, 0x94, 0xff, 0xbc, 0xcf, 0x7e, 0x09, 0xaa, 0xba, 0x01, - 0x8b, 0xcf, 0x89, 0xe7, 0xdb, 0xd4, 0x45, 0xab, 0x90, 0xb3, 0x5d, 0x8b, 0xbc, 0x5c, 0x4b, 0x55, - 0x53, 0xef, 0x64, 0xb1, 0x68, 0xa8, 0x0f, 0x00, 0xda, 0xec, 0x87, 0xe6, 0x06, 0xde, 0x14, 0x29, - 0x90, 0x39, 0x24, 0x53, 0x8e, 0x28, 0x62, 0xf6, 0x93, 0x51, 0x8e, 0x0c, 0x67, 0x2d, 0x2d, 0x28, - 0x47, 0x86, 0xa3, 0xfe, 0x24, 0x05, 0xa5, 0xba, 0xeb, 0xd2, 0x80, 0xf7, 0xee, 0x23, 0x04, 0x59, - 0xd7, 0x18, 0x11, 0x29, 0xc4, 0x7f, 0xa3, 0x26, 0xe4, 0x1d, 0x63, 0x8f, 0x38, 0xfe, 0x5a, 0xba, - 0x9a, 0x79, 0xa7, 0xf4, 0xe8, 0x2b, 0xb5, 0x57, 0xa7, 0x5c, 0x8b, 0x29, 0xa9, 0x6d, 0x73, 0x34, - 0x1f, 0x04, 0x96, 0xa2, 0xe8, 0x9b, 0xb0, 0x68, 0xbb, 0x96, 0x6d, 0x12, 0x7f, 0x2d, 0xcb, 0xb5, - 0xac, 0x27, 0x69, 0x99, 0x8d, 0xbe, 0x91, 0xfd, 0xe1, 0xd9, 0xc6, 0x02, 0x0e, 0x85, 0x2a, 0xef, - 0x41, 0x29, 0xa6, 0x36, 0x61, 0x6e, 0xab, 0x90, 0x3b, 0x32, 0x9c, 0x09, 0x91, 0xb3, 0x13, 0x8d, - 0xf7, 0xd3, 0x4f, 0x52, 0xea, 0x47, 0xb0, 0xda, 0x31, 0x46, 0xc4, 0x7a, 0x4a, 0x5c, 0xe2, 0xd9, - 0x26, 0x26, 0x3e, 0x9d, 0x78, 0x26, 0x61, 0x73, 0x3d, 0xb4, 0x5d, 0x2b, 0x9c, 0x2b, 0xfb, 0x9d, - 0xac, 0x45, 0x6d, 0xc2, 0x6b, 0x2d, 0xdb, 0x37, 0x3d, 0x12, 0x90, 0xcf, 0xad, 0x24, 0x13, 0x2a, - 0x39, 0x4b, 0xc1, 0xca, 0xbc, 0xf4, 0xff, 0x83, 0x9b, 0xcc, 0xc4, 0x96, 0xee, 0x49, 0x8a, 0xee, - 0x8f, 0x89, 0xc9, 0x95, 0x95, 0x1e, 0xbd, 0x93, 0x64, 0xa1, 0xa4, 0x99, 0x6c, 0x2d, 0xe0, 0x1b, - 0x5c, 0x4d, 0x48, 0xe8, 0x8f, 0x89, 0x89, 0x4c, 0xb8, 0x65, 0xc9, 0x41, 0xcf, 0xa9, 0x4f, 0x73, - 0xf5, 0x89, 0xcb, 0x78, 0xc9, 0x34, 0xb7, 0x16, 0xf0, 0x6a, 0xa8, 0x2c, 0xde, 0x49, 0x03, 0xa0, - 0x10, 0xea, 0x56, 0xbf, 0x9f, 0x82, 0x62, 0xc8, 0xf4, 0xd1, 0x97, 0xa1, 0xe8, 0x1a, 0x2e, 0xd5, - 0xcd, 0xf1, 0xc4, 0xe7, 0x13, 0xca, 0x34, 0xca, 0xe7, 0x67, 0x1b, 0x85, 0x8e, 0xe1, 0xd2, 0x66, - 0x6f, 0xd7, 0xc7, 0x05, 0xc6, 0x6e, 0x8e, 0x27, 0x3e, 0x7a, 0x03, 0xca, 0x23, 0x32, 0xa2, 0xde, - 0x54, 0xdf, 0x9b, 0x06, 0xc4, 0x97, 0x66, 0x2b, 0x09, 0x5a, 0x83, 0x91, 0xd0, 0x37, 0x60, 0x71, - 0x28, 0x86, 0xb4, 0x96, 0xe1, 0xdb, 0xe7, 0xcd, 0xa4, 0xd1, 0xcf, 0x8d, 0x1a, 0x87, 0x32, 0xea, - 0x6f, 0xa6, 0x60, 0x35, 0xa2, 0x92, 0x5f, 0x98, 0xd8, 0x1e, 0x19, 0x11, 0x37, 0xf0, 0xd1, 0xd7, - 0x20, 0xef, 0xd8, 0x23, 0x3b, 0xf0, 0xa5, 0xcd, 0x5f, 0x4f, 0x52, 0x1b, 0x4d, 0x0a, 0x4b, 0x30, - 0xaa, 0x43, 0xd9, 0x23, 0x3e, 0xf1, 0x8e, 0xc4, 0x8e, 0x97, 0x16, 0xbd, 0x46, 0xf8, 0x82, 0x88, - 0xba, 0x09, 0x85, 0x9e, 0x63, 0x04, 0xfb, 0xd4, 0x1b, 0x21, 0x15, 0xca, 0x86, 0x67, 0x1e, 0xd8, - 0x01, 0x31, 0x83, 0x89, 0x17, 0x9e, 0xbe, 0x0b, 0x34, 0x74, 0x0b, 0xd2, 0x54, 0x74, 0x54, 0x6c, - 0xe4, 0xcf, 0xcf, 0x36, 0xd2, 0xdd, 0x3e, 0x4e, 0x53, 0x5f, 0xfd, 0x00, 0x6e, 0xf4, 0x9c, 0xc9, - 0xd0, 0x76, 0x5b, 0xc4, 0x37, 0x3d, 0x7b, 0xcc, 0xb4, 0xb3, 0x5d, 0xc9, 0x7c, 0x54, 0xb8, 0x2b, - 0xd9, 0xef, 0xe8, 0x68, 0xa7, 0x67, 0x47, 0x5b, 0xfd, 0xf5, 0x34, 0xdc, 0xd0, 0xdc, 0xa1, 0xed, - 0x92, 0xb8, 0xf4, 0x5d, 0x58, 0x26, 0x9c, 0xa8, 0x1f, 0x09, 0x77, 0x23, 0xf5, 0x2c, 0x09, 0x6a, - 0xe8, 0x83, 0xda, 0x73, 0x7e, 0xe1, 0x61, 0xd2, 0xf4, 0x5f, 0xd1, 0x9e, 0xe8, 0x1d, 0x34, 0x58, - 0x1c, 0xf3, 0x49, 0xf8, 0x72, 0x79, 0xef, 0x26, 0xe9, 0x7a, 0x65, 0x9e, 0xa1, 0x93, 0x90, 0xb2, - 0x3f, 0x8d, 0x93, 0xf8, 0xeb, 0x34, 0xac, 0x74, 0xa8, 0x75, 0xc1, 0x0e, 0x15, 0x28, 0x1c, 0x50, - 0x3f, 0x88, 0x39, 0xc4, 0xa8, 0x8d, 0x9e, 0x40, 0x61, 0x2c, 0x97, 0x4f, 0xae, 0xfe, 0x9d, 0xe4, - 0x21, 0x0b, 0x0c, 0x8e, 0xd0, 0xe8, 0x03, 0x28, 0x86, 0x47, 0x86, 0xcd, 0xf6, 0x33, 0x6c, 0x9c, - 0x19, 0x1e, 0x7d, 0x03, 0xf2, 0x62, 0x11, 0xd6, 0xb2, 0x5c, 0xf2, 0xee, 0x67, 0xb2, 0x39, 0x96, - 0x42, 0xe8, 0x29, 0x14, 0x02, 0xc7, 0xd7, 0x6d, 0x77, 0x9f, 0xae, 0xe5, 0xb8, 0x82, 0x8d, 0x44, - 0x27, 0x43, 0x2d, 0x32, 0xd8, 0xee, 0xb7, 0xdd, 0x7d, 0xda, 0x28, 0x9d, 0x9f, 0x6d, 0x2c, 0xca, - 0x06, 0x5e, 0x0c, 0x1c, 0x9f, 0xfd, 0x40, 0x77, 0x20, 0xbb, 0x6f, 0x8f, 0xfd, 0xb5, 0x7c, 0x35, - 0xf5, 0x4e, 0xa1, 0x51, 0x38, 0x3f, 0xdb, 0xc8, 0x6e, 0xb6, 0x7b, 0x7d, 0xcc, 0xa9, 0xea, 0xf7, - 0x52, 0x50, 0x8a, 0xe9, 0x40, 0xaf, 0x03, 0x04, 0xde, 0xc4, 0x0f, 0x74, 0x8f, 0xd2, 0x80, 0x9b, - 0xb2, 0x8c, 0x8b, 0x9c, 0x82, 0x29, 0x0d, 0x50, 0x0d, 0x6e, 0x9a, 0xc4, 0x0b, 0x74, 0xdb, 0xf7, - 0x27, 0xc4, 0xd3, 0xfd, 0xc9, 0xde, 0xc7, 0xc4, 0x0c, 0xb8, 0x59, 0xcb, 0xf8, 0x06, 0x63, 0xb5, - 0x39, 0xa7, 0x2f, 0x18, 0xe8, 0x31, 0xdc, 0x8a, 0xe3, 0xc7, 0x93, 0x3d, 0xc7, 0x36, 0x75, 0xb6, - 0xd4, 0x19, 0x2e, 0x72, 0x73, 0x26, 0xd2, 0xe3, 0xbc, 0x67, 0x64, 0xaa, 0xfe, 0x38, 0x05, 0x0a, - 0x36, 0xf6, 0x83, 0x1d, 0x32, 0xda, 0x23, 0x5e, 0x3f, 0x30, 0x82, 0x89, 0x8f, 0x6e, 0x41, 0xde, - 0x21, 0x86, 0x45, 0x3c, 0x3e, 0xa8, 0x02, 0x96, 0x2d, 0xb4, 0xcb, 0xce, 0xb7, 0x61, 0x1e, 0x18, - 0x7b, 0xb6, 0x63, 0x07, 0x53, 0x3e, 0x94, 0xe5, 0xe4, 0x0d, 0x3e, 0xaf, 0xb3, 0x86, 0x63, 0x82, - 0xf8, 0x82, 0x1a, 0xb4, 0x06, 0x8b, 0x23, 0xe2, 0xfb, 0xc6, 0x90, 0xf0, 0x91, 0x16, 0x71, 0xd8, - 0x54, 0x3f, 0x80, 0x72, 0x5c, 0x0e, 0x95, 0x60, 0x71, 0xb7, 0xf3, 0xac, 0xd3, 0x7d, 0xd1, 0x51, - 0x16, 0xd0, 0x0a, 0x94, 0x76, 0x3b, 0x58, 0xab, 0x37, 0xb7, 0xea, 0x8d, 0x6d, 0x4d, 0x49, 0xa1, - 0x25, 0x28, 0xce, 0x9a, 0x69, 0xf5, 0x8f, 0x53, 0x00, 0xcc, 0xdc, 0x72, 0x52, 0xef, 0x43, 0xce, - 0x0f, 0x8c, 0x40, 0xec, 0xd9, 0xe5, 0x47, 0x6f, 0x5d, 0xb6, 0xc2, 0x72, 0xbc, 0xec, 0x1f, 0xc1, - 0x42, 0x24, 0x3e, 0xc2, 0xf4, 0x85, 0x11, 0x32, 0xf7, 0x61, 0x58, 0x96, 0x27, 0x07, 0xce, 0x7f, - 0xab, 0x1f, 0x40, 0x8e, 0x4b, 0x5f, 0x1c, 0x6e, 0x01, 0xb2, 0x2d, 0xf6, 0x2b, 0x85, 0x8a, 0x90, - 0xc3, 0x5a, 0xbd, 0xf5, 0x1d, 0x25, 0x8d, 0x14, 0x28, 0xb7, 0xda, 0xfd, 0x66, 0xb7, 0xd3, 0xd1, - 0x9a, 0x03, 0xad, 0xa5, 0x64, 0xd4, 0xbb, 0x90, 0x6b, 0x8f, 0x98, 0xe6, 0x3b, 0xec, 0x40, 0xec, - 0x13, 0x8f, 0xb8, 0x66, 0x78, 0xce, 0x66, 0x04, 0xf5, 0x7b, 0x65, 0xc8, 0xed, 0xd0, 0x89, 0x1b, - 0xa0, 0x47, 0x31, 0xa7, 0xb6, 0x9c, 0x1c, 0x3f, 0x70, 0x60, 0x6d, 0x30, 0x1d, 0x13, 0xe9, 0xf4, - 0x6e, 0x41, 0x5e, 0x1c, 0x1d, 0x39, 0x1d, 0xd9, 0x62, 0xf4, 0xc0, 0xf0, 0x86, 0x24, 0x90, 0xf3, - 0x91, 0x2d, 0xf4, 0x0e, 0xbb, 0xcf, 0x0c, 0x8b, 0xba, 0xce, 0x94, 0x9f, 0xb0, 0x82, 0xb8, 0xb4, - 0x30, 0x31, 0xac, 0xae, 0xeb, 0x4c, 0x71, 0xc4, 0x45, 0x5b, 0x50, 0xde, 0xb3, 0x5d, 0x4b, 0xa7, - 0x63, 0x71, 0x05, 0xe4, 0x2e, 0x3f, 0x8f, 0x62, 0x54, 0x0d, 0xdb, 0xb5, 0xba, 0x02, 0x8c, 0x4b, - 0x7b, 0xb3, 0x06, 0xea, 0xc0, 0xf2, 0x11, 0x75, 0x26, 0x23, 0x12, 0xe9, 0xca, 0x73, 0x5d, 0x6f, - 0x5f, 0xae, 0xeb, 0x39, 0xc7, 0x87, 0xda, 0x96, 0x8e, 0xe2, 0x4d, 0xf4, 0x0c, 0x96, 0x82, 0xd1, - 0x78, 0xdf, 0x8f, 0xd4, 0x2d, 0x72, 0x75, 0x5f, 0xba, 0xc2, 0x60, 0x0c, 0x1e, 0x6a, 0x2b, 0x07, - 0xb1, 0x16, 0x7a, 0x0a, 0x25, 0x93, 0xba, 0xbe, 0xed, 0x07, 0xc4, 0x35, 0xa7, 0x6b, 0x05, 0x6e, - 0xfb, 0x2b, 0x66, 0xd9, 0x9c, 0x81, 0x71, 0x5c, 0xb2, 0xf2, 0xab, 0x19, 0x28, 0xc5, 0x4c, 0x80, - 0xfa, 0x50, 0x1a, 0x7b, 0x74, 0x6c, 0x0c, 0xf9, 0x7d, 0x28, 0x17, 0xf5, 0xe1, 0x67, 0x32, 0x5f, - 0xad, 0x37, 0x13, 0xc4, 0x71, 0x2d, 0xea, 0x69, 0x1a, 0x4a, 0x31, 0x26, 0xba, 0x07, 0x05, 0xdc, - 0xc3, 0xed, 0xe7, 0xf5, 0x81, 0xa6, 0x2c, 0x54, 0xee, 0x9c, 0x9c, 0x56, 0xd7, 0xb8, 0xb6, 0xb8, - 0x82, 0x9e, 0x67, 0x1f, 0xb1, 0x3d, 0xfc, 0x0e, 0x2c, 0x86, 0xd0, 0x54, 0xe5, 0x8b, 0x27, 0xa7, - 0xd5, 0xd7, 0xe6, 0xa1, 0x31, 0x24, 0xee, 0x6f, 0xd5, 0xb1, 0xd6, 0x52, 0xd2, 0xc9, 0x48, 0xdc, - 0x3f, 0x30, 0x3c, 0x62, 0xa1, 0x2f, 0x41, 0x5e, 0x02, 0x33, 0x95, 0xca, 0xc9, 0x69, 0xf5, 0xd6, - 0x3c, 0x70, 0x86, 0xc3, 0xfd, 0xed, 0xfa, 0x73, 0x4d, 0xc9, 0x26, 0xe3, 0x70, 0xdf, 0x31, 0x8e, - 0x08, 0x7a, 0x0b, 0x72, 0x02, 0x96, 0xab, 0xdc, 0x3e, 0x39, 0xad, 0x7e, 0xe1, 0x15, 0x75, 0x0c, - 0x55, 0x59, 0xfb, 0x8d, 0xdf, 0x5f, 0x5f, 0xf8, 0xf3, 0x3f, 0x58, 0x57, 0xe6, 0xd9, 0x95, 0xff, - 0x4e, 0xc1, 0xd2, 0x85, 0xbd, 0x83, 0x54, 0xc8, 0xbb, 0xd4, 0xa4, 0x63, 0x71, 0x4d, 0x16, 0x1a, - 0x70, 0x7e, 0xb6, 0x91, 0xef, 0xd0, 0x26, 0x1d, 0x4f, 0xb1, 0xe4, 0xa0, 0x67, 0x73, 0x17, 0xfd, - 0xe3, 0xcf, 0xb8, 0x31, 0x13, 0xaf, 0xfa, 0x0f, 0x61, 0xc9, 0xf2, 0xec, 0x23, 0xe2, 0xe9, 0x26, - 0x75, 0xf7, 0xed, 0xa1, 0xbc, 0x02, 0x2b, 0x89, 0xd1, 0x28, 0x07, 0xe2, 0xb2, 0x10, 0x68, 0x72, - 0xfc, 0x4f, 0x71, 0xc9, 0x57, 0x9e, 0x43, 0x39, 0xbe, 0xd5, 0xd9, 0xbd, 0xe4, 0xdb, 0xbf, 0x48, - 0x64, 0xd8, 0xc9, 0x83, 0x54, 0x5c, 0x64, 0x14, 0x11, 0x74, 0xbe, 0x0d, 0xd9, 0x11, 0xb5, 0x84, - 0x9e, 0xa5, 0xc6, 0x4d, 0x16, 0x6b, 0xfc, 0xe3, 0xd9, 0x46, 0x89, 0xfa, 0xb5, 0x4d, 0xdb, 0x21, - 0x3b, 0xd4, 0x22, 0x98, 0x03, 0xd4, 0x1f, 0xa4, 0x20, 0xcb, 0x9c, 0x0e, 0xfa, 0x22, 0x64, 0x1b, - 0xed, 0x4e, 0x4b, 0x59, 0xa8, 0xdc, 0x38, 0x39, 0xad, 0x2e, 0x71, 0x9b, 0x30, 0x06, 0xdb, 0xbc, - 0x68, 0x03, 0xf2, 0xcf, 0xbb, 0xdb, 0xbb, 0x3b, 0x6c, 0x7f, 0xdd, 0x3c, 0x39, 0xad, 0xae, 0x44, - 0x6c, 0x61, 0x35, 0xf4, 0x3a, 0xe4, 0x06, 0x3b, 0xbd, 0xcd, 0xbe, 0x92, 0xae, 0xa0, 0x93, 0xd3, - 0xea, 0x72, 0xc4, 0xe7, 0x83, 0x46, 0x6f, 0x40, 0xae, 0xd3, 0x6b, 0xf7, 0x34, 0x25, 0x53, 0xb9, - 0x75, 0x72, 0x5a, 0x45, 0x11, 0x9b, 0xe7, 0x04, 0x3d, 0x7b, 0x4c, 0x2a, 0x37, 0xe4, 0xca, 0x17, - 0x23, 0x9e, 0xfa, 0xa3, 0x14, 0x94, 0x62, 0x87, 0x92, 0x6d, 0xde, 0x96, 0xb6, 0x59, 0xdf, 0xdd, - 0x1e, 0x28, 0x0b, 0xb1, 0xcd, 0x1b, 0x83, 0xb4, 0xc8, 0xbe, 0x31, 0x71, 0x98, 0x2f, 0x84, 0x66, - 0xb7, 0xd3, 0x6f, 0xf7, 0x07, 0x5a, 0x67, 0xa0, 0xa4, 0x2a, 0x6b, 0x27, 0xa7, 0xd5, 0xd5, 0x79, - 0xf0, 0xe6, 0xc4, 0x71, 0xd8, 0xf6, 0x6d, 0xd6, 0x9b, 0x5b, 0xfc, 0x3c, 0xcc, 0xb6, 0x6f, 0x0c, - 0xd5, 0x34, 0xcc, 0x03, 0x62, 0xa1, 0x77, 0xa1, 0xd8, 0xd2, 0xb6, 0xb5, 0xa7, 0x75, 0x7e, 0x03, - 0x54, 0x5e, 0x3f, 0x39, 0xad, 0xde, 0x7e, 0xb5, 0x77, 0x87, 0x0c, 0x8d, 0x80, 0x58, 0x73, 0xdb, - 0x38, 0x06, 0x51, 0xff, 0x33, 0x0d, 0x4b, 0x98, 0x25, 0xd4, 0x5e, 0xd0, 0xa3, 0x8e, 0x6d, 0x4e, - 0x51, 0x0f, 0x8a, 0x26, 0x75, 0x2d, 0x3b, 0xe6, 0x4b, 0x1e, 0x5d, 0x12, 0x54, 0xcd, 0xa4, 0xc2, - 0x56, 0x33, 0x94, 0xc4, 0x33, 0x25, 0xe8, 0x3e, 0xe4, 0x2c, 0xe2, 0x18, 0x53, 0x19, 0xdd, 0xdd, - 0xae, 0x89, 0x94, 0xbd, 0x16, 0xa6, 0xec, 0xb5, 0x96, 0x4c, 0xd9, 0xb1, 0xc0, 0xf1, 0x2c, 0xc6, - 0x78, 0xa9, 0x1b, 0x41, 0x40, 0x46, 0xe3, 0x40, 0x84, 0x76, 0x59, 0x5c, 0x1a, 0x19, 0x2f, 0xeb, - 0x92, 0x84, 0x1e, 0x42, 0xfe, 0xd8, 0x76, 0x2d, 0x7a, 0x2c, 0xa3, 0xb7, 0x2b, 0x94, 0x4a, 0xa0, - 0x7a, 0xc2, 0xc2, 0x96, 0xb9, 0x61, 0xb2, 0x6d, 0xd6, 0xe9, 0x76, 0xb4, 0x70, 0x9b, 0x49, 0x7e, - 0xd7, 0xed, 0x50, 0x97, 0xf9, 0x08, 0xe8, 0x76, 0xf4, 0xcd, 0x7a, 0x7b, 0x7b, 0x17, 0xb3, 0xad, - 0xb6, 0x7a, 0x72, 0x5a, 0x55, 0x22, 0xc8, 0xa6, 0x61, 0x3b, 0x2c, 0x9d, 0xb8, 0x0d, 0x99, 0x7a, - 0xe7, 0x3b, 0x4a, 0xba, 0xa2, 0x9c, 0x9c, 0x56, 0xcb, 0x11, 0xbb, 0xee, 0x4e, 0x67, 0x76, 0x9f, - 0xef, 0x57, 0xfd, 0x9b, 0x0c, 0x94, 0x77, 0xc7, 0x96, 0x11, 0x10, 0x71, 0x16, 0x51, 0x15, 0x4a, - 0x63, 0xc3, 0x33, 0x1c, 0x87, 0x38, 0xb6, 0x3f, 0x92, 0xc5, 0x88, 0x38, 0x09, 0xbd, 0xf7, 0x59, - 0xcd, 0xd8, 0x28, 0xb0, 0xf3, 0xf5, 0xfd, 0x7f, 0xde, 0x48, 0x85, 0x06, 0xdd, 0x85, 0xe5, 0x7d, - 0x31, 0x5a, 0xdd, 0x30, 0xf9, 0xc2, 0x66, 0xf8, 0xc2, 0xd6, 0x92, 0x16, 0x36, 0x3e, 0xac, 0x9a, - 0x9c, 0x64, 0x9d, 0x4b, 0xe1, 0xa5, 0xfd, 0x78, 0x13, 0x3d, 0x86, 0xc5, 0x11, 0x75, 0xed, 0x80, - 0x7a, 0xd7, 0xaf, 0x42, 0x88, 0x44, 0xf7, 0xe0, 0x06, 0x5b, 0xdc, 0x70, 0x3c, 0x9c, 0xcd, 0xaf, - 0xfc, 0x34, 0x5e, 0x19, 0x19, 0x2f, 0x65, 0x87, 0x98, 0x91, 0x51, 0x03, 0x72, 0xd4, 0x63, 0x31, - 0x65, 0x9e, 0x0f, 0xf7, 0xdd, 0x6b, 0x87, 0x2b, 0x1a, 0x5d, 0x26, 0x83, 0x85, 0xa8, 0xfa, 0x75, - 0x58, 0xba, 0x30, 0x09, 0x16, 0x4a, 0xf5, 0xea, 0xbb, 0x7d, 0x4d, 0x59, 0x40, 0x65, 0x28, 0x34, - 0xbb, 0x9d, 0x41, 0xbb, 0xb3, 0xcb, 0x62, 0xc1, 0x32, 0x14, 0x70, 0x77, 0x7b, 0xbb, 0x51, 0x6f, - 0x3e, 0x53, 0xd2, 0x6a, 0x0d, 0x4a, 0x31, 0x6d, 0x68, 0x19, 0xa0, 0x3f, 0xe8, 0xf6, 0xf4, 0xcd, - 0x36, 0xee, 0x0f, 0x44, 0x24, 0xd9, 0x1f, 0xd4, 0xf1, 0x40, 0x12, 0x52, 0xea, 0xbf, 0xa7, 0xc3, - 0x15, 0x95, 0xc1, 0x63, 0xe3, 0x62, 0xf0, 0x78, 0xc5, 0xe0, 0x65, 0xf8, 0x38, 0x6b, 0x44, 0x41, - 0xe4, 0x7b, 0x00, 0x7c, 0xe3, 0x10, 0x4b, 0x37, 0x02, 0xb9, 0xf0, 0x95, 0x57, 0x8c, 0x3c, 0x08, - 0x6b, 0x62, 0xb8, 0x28, 0xd1, 0xf5, 0x00, 0x7d, 0x03, 0xca, 0x26, 0x1d, 0x8d, 0x1d, 0x22, 0x85, - 0x33, 0xd7, 0x0a, 0x97, 0x22, 0x7c, 0x3d, 0x88, 0x87, 0xaf, 0xd9, 0x8b, 0x01, 0xf6, 0xaf, 0xa5, - 0x42, 0xcb, 0x24, 0x44, 0xac, 0x65, 0x28, 0xec, 0xf6, 0x5a, 0xf5, 0x41, 0xbb, 0xf3, 0x54, 0x49, - 0x21, 0x80, 0x3c, 0x37, 0x75, 0x4b, 0x49, 0xb3, 0x48, 0xbb, 0xd9, 0xdd, 0xe9, 0x6d, 0x6b, 0xdc, - 0x63, 0xa1, 0x55, 0x50, 0x42, 0x63, 0xeb, 0xdc, 0x90, 0x5a, 0x4b, 0xc9, 0xa2, 0x9b, 0xb0, 0x12, - 0x51, 0xa5, 0x64, 0x0e, 0xdd, 0x02, 0x14, 0x11, 0x67, 0x2a, 0xf2, 0xea, 0x2f, 0xc3, 0x4a, 0x93, - 0xba, 0x81, 0x61, 0xbb, 0x51, 0x16, 0xf2, 0x88, 0x4d, 0x5a, 0x92, 0x74, 0x5b, 0xd6, 0x92, 0x1a, - 0x2b, 0xe7, 0x67, 0x1b, 0xa5, 0x08, 0xda, 0x6e, 0xf1, 0x70, 0x4a, 0x36, 0x2c, 0x76, 0x7e, 0xc7, - 0xb6, 0xc5, 0x8d, 0x9b, 0x6b, 0x2c, 0x9e, 0x9f, 0x6d, 0x64, 0x7a, 0xed, 0x16, 0x66, 0x34, 0xf4, - 0x45, 0x28, 0x92, 0x97, 0x76, 0xa0, 0x9b, 0xec, 0xee, 0x62, 0x06, 0xcc, 0xe1, 0x02, 0x23, 0x34, - 0xd9, 0x55, 0xd5, 0x00, 0xe8, 0x51, 0x2f, 0x90, 0x3d, 0x7f, 0x15, 0x72, 0x63, 0xea, 0xf1, 0xea, - 0xc7, 0xa5, 0x35, 0x39, 0x06, 0x17, 0x1b, 0x15, 0x0b, 0xb0, 0xfa, 0x83, 0x0c, 0xc0, 0xc0, 0xf0, - 0x0f, 0xa5, 0x92, 0x27, 0x50, 0x8c, 0xea, 0x9b, 0xb2, 0x8c, 0x72, 0xe5, 0x6a, 0x47, 0x60, 0xf4, - 0x38, 0xdc, 0x6c, 0x22, 0xbf, 0x4a, 0x4c, 0x83, 0xc3, 0x8e, 0x92, 0x52, 0x94, 0x8b, 0x49, 0x14, - 0x0b, 0x05, 0x88, 0xe7, 0xc9, 0x95, 0x67, 0x3f, 0x51, 0x93, 0x5f, 0x0b, 0xc2, 0x68, 0x32, 0x42, - 0x4f, 0x2c, 0x1c, 0xcd, 0xad, 0xc8, 0xd6, 0x02, 0x9e, 0xc9, 0xa1, 0x0f, 0xa1, 0xc4, 0xe6, 0xad, - 0xfb, 0x9c, 0x27, 0x83, 0xf3, 0x4b, 0x4d, 0x25, 0x34, 0x60, 0x18, 0xcf, 0xac, 0xfc, 0x3a, 0x80, - 0x31, 0x1e, 0x3b, 0x36, 0xb1, 0xf4, 0xbd, 0x29, 0x8f, 0xc6, 0x8b, 0xb8, 0x28, 0x29, 0x8d, 0x29, - 0x3b, 0x2e, 0x21, 0xdb, 0x08, 0x78, 0x84, 0x7d, 0x8d, 0x01, 0x25, 0xba, 0x1e, 0x34, 0x14, 0x58, - 0xf6, 0x26, 0x2e, 0x33, 0xa8, 0x1c, 0x9d, 0xfa, 0x47, 0x69, 0x78, 0xad, 0x43, 0x82, 0x63, 0xea, - 0x1d, 0xd6, 0x83, 0xc0, 0x30, 0x0f, 0x46, 0xc4, 0x95, 0xcb, 0x17, 0x4b, 0x7a, 0x52, 0x17, 0x92, - 0x9e, 0x35, 0x58, 0x34, 0x1c, 0xdb, 0xf0, 0x89, 0x08, 0xf0, 0x8a, 0x38, 0x6c, 0xb2, 0xd4, 0x8c, - 0x25, 0x7a, 0xc4, 0xf7, 0x89, 0xa8, 0xcc, 0xb0, 0x81, 0x87, 0x04, 0xf4, 0x5d, 0xb8, 0x25, 0x43, - 0x39, 0x23, 0xea, 0x8a, 0x25, 0x1d, 0x61, 0x89, 0x57, 0x4b, 0xcc, 0x3c, 0x93, 0x07, 0x27, 0x63, - 0xbd, 0x19, 0xb9, 0x3b, 0x0e, 0x64, 0xe4, 0xb8, 0x6a, 0x25, 0xb0, 0x2a, 0x4f, 0xe1, 0xf6, 0xa5, - 0x22, 0x9f, 0xab, 0xf2, 0xf3, 0xf7, 0x69, 0x80, 0x76, 0xaf, 0xbe, 0x23, 0x8d, 0xd4, 0x82, 0xfc, - 0xbe, 0x31, 0xb2, 0x9d, 0xe9, 0x55, 0x1e, 0x70, 0x86, 0xaf, 0xd5, 0x85, 0x39, 0x36, 0xb9, 0x0c, - 0x96, 0xb2, 0x3c, 0xef, 0x9c, 0xec, 0xb9, 0x24, 0x88, 0xf2, 0x4e, 0xde, 0x62, 0xc3, 0xf0, 0x0c, - 0x37, 0xda, 0xba, 0xa2, 0xc1, 0x16, 0x80, 0x85, 0x3c, 0xc7, 0xc6, 0x34, 0x74, 0x5b, 0xb2, 0x89, - 0xb6, 0x78, 0x7d, 0x95, 0x78, 0x47, 0xc4, 0x5a, 0xcb, 0x71, 0xa3, 0x5e, 0x37, 0x1e, 0x2c, 0xe1, - 0xc2, 0x76, 0x91, 0x74, 0xe5, 0x03, 0x1e, 0x32, 0xcd, 0x58, 0x9f, 0xcb, 0x46, 0x0f, 0x60, 0xe9, - 0xc2, 0x3c, 0x5f, 0x49, 0xf8, 0xdb, 0xbd, 0xe7, 0x5f, 0x55, 0xb2, 0xf2, 0xd7, 0xd7, 0x95, 0xbc, - 0xfa, 0x57, 0x19, 0xe1, 0x68, 0xa4, 0x55, 0x93, 0xdf, 0x15, 0x0a, 0x7c, 0x77, 0x9b, 0xd4, 0x91, - 0x0e, 0xe0, 0xed, 0xab, 0xfd, 0x0f, 0xcb, 0xfb, 0x38, 0x1c, 0x47, 0x82, 0x68, 0x03, 0x4a, 0x62, - 0x17, 0xeb, 0xec, 0xc0, 0x71, 0xb3, 0x2e, 0x61, 0x10, 0x24, 0x26, 0x89, 0xee, 0xc2, 0x32, 0x2f, - 0x10, 0xf9, 0x07, 0xc4, 0x12, 0x98, 0x2c, 0xc7, 0x2c, 0x45, 0x54, 0x0e, 0xdb, 0x81, 0xb2, 0x24, - 0xe8, 0x3c, 0xe6, 0xcf, 0xf1, 0x01, 0xdd, 0xbb, 0x6e, 0x40, 0x42, 0x84, 0xa7, 0x02, 0xa5, 0xf1, - 0xac, 0xa1, 0xfe, 0x3c, 0x14, 0xc2, 0xc1, 0xa2, 0x35, 0xc8, 0x0c, 0x9a, 0x3d, 0x65, 0xa1, 0xb2, - 0x72, 0x72, 0x5a, 0x2d, 0x85, 0xe4, 0x41, 0xb3, 0xc7, 0x38, 0xbb, 0xad, 0x9e, 0x92, 0xba, 0xc8, - 0xd9, 0x6d, 0xf5, 0x50, 0x05, 0xb2, 0xfd, 0xe6, 0xa0, 0x17, 0xc6, 0x67, 0x21, 0x8b, 0xd1, 0x2a, - 0x59, 0x16, 0x9f, 0xa9, 0xfb, 0x50, 0x8a, 0xf5, 0x8e, 0xde, 0x84, 0xc5, 0x76, 0xe7, 0x29, 0xd6, - 0xfa, 0x7d, 0x65, 0x41, 0xa4, 0x07, 0x31, 0x6e, 0xdb, 0x1d, 0xb2, 0xb5, 0x43, 0xaf, 0x43, 0x76, - 0xab, 0xcb, 0xee, 0x7d, 0x91, 0x7f, 0xc4, 0x10, 0x5b, 0xd4, 0x0f, 0x2a, 0x37, 0x65, 0xe0, 0x17, - 0x57, 0xac, 0xfe, 0x4e, 0x0a, 0xf2, 0xe2, 0xa0, 0x25, 0x2e, 0x62, 0x1d, 0x16, 0xc3, 0x32, 0x83, - 0x48, 0x0e, 0xdf, 0xbe, 0x3c, 0x91, 0xab, 0xc9, 0xbc, 0x4b, 0x6c, 0xcd, 0x50, 0xae, 0xf2, 0x3e, - 0x94, 0xe3, 0x8c, 0xcf, 0xb5, 0x31, 0xbf, 0x0b, 0x25, 0xb6, 0xf7, 0xc3, 0x84, 0xee, 0x11, 0xe4, - 0x85, 0xb3, 0x88, 0xee, 0xa1, 0xcb, 0xb3, 0x4a, 0x89, 0x44, 0x4f, 0x60, 0x51, 0x64, 0xa2, 0x61, - 0xed, 0x79, 0xfd, 0xea, 0x13, 0x86, 0x43, 0xb8, 0xfa, 0x21, 0x64, 0x7b, 0x84, 0x78, 0xcc, 0xf6, - 0x2e, 0xb5, 0xc8, 0xec, 0xea, 0x96, 0x49, 0xb4, 0x45, 0xda, 0x2d, 0x96, 0x44, 0x5b, 0xa4, 0x6d, - 0x45, 0xf5, 0xb3, 0x74, 0xac, 0x7e, 0x36, 0x80, 0xf2, 0x0b, 0x62, 0x0f, 0x0f, 0x02, 0x62, 0x71, - 0x45, 0xef, 0x42, 0x76, 0x4c, 0xa2, 0xc1, 0xaf, 0x25, 0x6e, 0x3e, 0x42, 0x3c, 0xcc, 0x51, 0xcc, - 0xc7, 0x1c, 0x73, 0x69, 0xf9, 0x60, 0x22, 0x5b, 0xea, 0xdf, 0xa5, 0x61, 0xb9, 0xed, 0xfb, 0x13, - 0xc3, 0x35, 0xc3, 0xa8, 0xee, 0x9b, 0x17, 0xa3, 0xba, 0xc4, 0x97, 0xa5, 0x8b, 0x22, 0x17, 0xcb, - 0x82, 0xf2, 0x66, 0x4d, 0x47, 0x37, 0xab, 0xfa, 0x6f, 0xa9, 0xb0, 0xf6, 0x77, 0x37, 0xe6, 0x0a, - 0x44, 0x8e, 0x18, 0xd7, 0x44, 0x76, 0xdd, 0x43, 0x97, 0x1e, 0xbb, 0x2c, 0x7b, 0xc5, 0x5a, 0x47, - 0x7b, 0xa1, 0xa4, 0xc4, 0xf6, 0xbc, 0x00, 0xc2, 0xc4, 0x25, 0xc7, 0x4c, 0x53, 0x4f, 0xeb, 0xb4, - 0x58, 0x14, 0x96, 0x4e, 0xd0, 0xd4, 0x23, 0xae, 0x65, 0xbb, 0x43, 0xf4, 0x26, 0xe4, 0xdb, 0xfd, - 0xfe, 0x2e, 0x4f, 0x21, 0x5f, 0x3b, 0x39, 0xad, 0xde, 0xbc, 0x80, 0xe2, 0x75, 0x5f, 0x8b, 0x81, - 0x58, 0x0a, 0xc4, 0xe2, 0xb3, 0x04, 0x10, 0x8b, 0xad, 0x05, 0x08, 0x77, 0x07, 0xf5, 0x81, 0xa6, - 0xe4, 0x12, 0x40, 0x98, 0xb2, 0xbf, 0xf2, 0xb8, 0xfd, 0x53, 0x1a, 0x94, 0xba, 0x69, 0x92, 0x71, - 0xc0, 0xf8, 0x32, 0xeb, 0x1c, 0x40, 0x61, 0xcc, 0x7e, 0xd9, 0x24, 0x8c, 0xa0, 0x9e, 0x24, 0xbe, - 0x8d, 0xce, 0xc9, 0xd5, 0x30, 0x75, 0x48, 0xdd, 0x1a, 0xd9, 0xbe, 0x6f, 0x53, 0x57, 0xd0, 0x70, - 0xa4, 0xa9, 0xf2, 0x1f, 0x29, 0xb8, 0x99, 0x80, 0x40, 0x0f, 0x20, 0xeb, 0x51, 0x27, 0x5c, 0xc3, - 0x3b, 0x97, 0x95, 0x75, 0x99, 0x28, 0xe6, 0x48, 0xb4, 0x0e, 0x60, 0x4c, 0x02, 0x6a, 0xf0, 0xfe, - 0xf9, 0xea, 0x15, 0x70, 0x8c, 0x82, 0x5e, 0x40, 0xde, 0x27, 0xa6, 0x47, 0xc2, 0x38, 0xfb, 0xc3, - 0xff, 0xed, 0xe8, 0x6b, 0x7d, 0xae, 0x06, 0x4b, 0x75, 0x95, 0x1a, 0xe4, 0x05, 0x85, 0x6d, 0x7b, - 0xcb, 0x08, 0x0c, 0x59, 0xf4, 0xe7, 0xbf, 0xd9, 0x6e, 0x32, 0x9c, 0x61, 0xb8, 0x9b, 0x0c, 0x67, - 0xa8, 0xfe, 0x65, 0x1a, 0x40, 0x7b, 0x19, 0x10, 0xcf, 0x35, 0x9c, 0x66, 0x1d, 0x69, 0xb1, 0x9b, - 0x41, 0xcc, 0xf6, 0xcb, 0x89, 0xef, 0x1c, 0x91, 0x44, 0xad, 0x59, 0x4f, 0xb8, 0x1b, 0x6e, 0x43, - 0x66, 0xe2, 0xc9, 0xe7, 0x6e, 0x11, 0x23, 0xef, 0xe2, 0x6d, 0xcc, 0x68, 0x48, 0x9b, 0xb9, 0xad, - 0xcc, 0xe5, 0x8f, 0xda, 0xb1, 0x0e, 0x12, 0x5d, 0x17, 0x3b, 0xf9, 0xa6, 0xa1, 0x9b, 0x44, 0xde, - 0x2a, 0x65, 0x71, 0xf2, 0x9b, 0xf5, 0x26, 0xf1, 0x02, 0x9c, 0x37, 0x0d, 0xf6, 0xff, 0xa7, 0xf2, - 0x6f, 0xef, 0x02, 0xcc, 0xa6, 0x86, 0xd6, 0x21, 0xd7, 0xdc, 0xec, 0xf7, 0xb7, 0x95, 0x05, 0xe1, - 0xc0, 0x67, 0x2c, 0x4e, 0x56, 0xff, 0x2c, 0x0d, 0x85, 0x66, 0x5d, 0x5e, 0xb9, 0x4d, 0x50, 0xb8, - 0x57, 0xe2, 0x4f, 0x25, 0xe4, 0xe5, 0xd8, 0xf6, 0xa6, 0xd2, 0xb1, 0x5c, 0x91, 0xf0, 0x2e, 0x33, - 0x11, 0x36, 0x6a, 0x8d, 0x0b, 0x20, 0x0c, 0x65, 0x22, 0x8d, 0xa0, 0x9b, 0x46, 0xe8, 0xe3, 0xd7, - 0xaf, 0x36, 0x96, 0x48, 0x5d, 0x66, 0x6d, 0x1f, 0x97, 0x42, 0x25, 0x4d, 0xc3, 0x47, 0xef, 0xc1, - 0x8a, 0x6f, 0x0f, 0x5d, 0xdb, 0x1d, 0xea, 0xa1, 0xf1, 0xf8, 0xbb, 0x4d, 0xe3, 0xc6, 0xf9, 0xd9, - 0xc6, 0x52, 0x5f, 0xb0, 0xa4, 0x0d, 0x97, 0x24, 0xb2, 0xc9, 0x4d, 0x89, 0xbe, 0x0e, 0xcb, 0x31, - 0x51, 0x66, 0x45, 0x61, 0x76, 0xe5, 0xfc, 0x6c, 0xa3, 0x1c, 0x49, 0x3e, 0x23, 0x53, 0x5c, 0x8e, - 0x04, 0x9f, 0x11, 0x5e, 0x9b, 0xd9, 0xa7, 0x9e, 0x49, 0x74, 0x8f, 0x9f, 0x69, 0x7e, 0xbb, 0x67, - 0x71, 0x89, 0xd3, 0xc4, 0x31, 0x57, 0x9f, 0xc3, 0xcd, 0xae, 0x67, 0x1e, 0x10, 0x3f, 0x10, 0xa6, - 0x90, 0x56, 0xfc, 0x10, 0xee, 0x04, 0x86, 0x7f, 0xa8, 0x1f, 0xd8, 0x7e, 0x40, 0xbd, 0xa9, 0xee, - 0x91, 0x80, 0xb8, 0x8c, 0xaf, 0xf3, 0xa7, 0x60, 0x59, 0x34, 0xbc, 0xcd, 0x30, 0x5b, 0x02, 0x82, - 0x43, 0xc4, 0x36, 0x03, 0xa8, 0x6d, 0x28, 0xb3, 0x14, 0x46, 0x16, 0xd5, 0xd8, 0xec, 0xc1, 0xa1, - 0x43, 0xfd, 0x33, 0x5f, 0x53, 0x45, 0x87, 0x0e, 0xc5, 0x4f, 0xf5, 0xdb, 0xa0, 0xb4, 0x6c, 0x7f, - 0x6c, 0x04, 0xe6, 0x41, 0x58, 0x0d, 0x45, 0x2d, 0x50, 0x0e, 0x88, 0xe1, 0x05, 0x7b, 0xc4, 0x08, - 0xf4, 0x31, 0xf1, 0x6c, 0x6a, 0x5d, 0xbf, 0xca, 0x2b, 0x91, 0x48, 0x8f, 0x4b, 0xa8, 0xff, 0x95, - 0x02, 0xc0, 0xc6, 0x7e, 0x18, 0xad, 0x7d, 0x05, 0x6e, 0xf8, 0xae, 0x31, 0xf6, 0x0f, 0x68, 0xa0, - 0xdb, 0x6e, 0x40, 0xbc, 0x23, 0xc3, 0x91, 0xc5, 0x1d, 0x25, 0x64, 0xb4, 0x25, 0x1d, 0xbd, 0x0b, - 0xe8, 0x90, 0x90, 0xb1, 0x4e, 0x1d, 0x4b, 0x0f, 0x99, 0xe2, 0xa1, 0x3a, 0x8b, 0x15, 0xc6, 0xe9, - 0x3a, 0x56, 0x3f, 0xa4, 0xa3, 0x06, 0xac, 0xb3, 0xe9, 0x13, 0x37, 0xf0, 0x6c, 0xe2, 0xeb, 0xfb, - 0xd4, 0xd3, 0x7d, 0x87, 0x1e, 0xeb, 0xfb, 0xd4, 0x71, 0xe8, 0x31, 0xf1, 0xc2, 0xba, 0x59, 0xc5, - 0xa1, 0x43, 0x4d, 0x80, 0x36, 0xa9, 0xd7, 0x77, 0xe8, 0xf1, 0x66, 0x88, 0x60, 0x21, 0xdd, 0x6c, - 0xce, 0x81, 0x6d, 0x1e, 0x86, 0x21, 0x5d, 0x44, 0x1d, 0xd8, 0xe6, 0x21, 0x7a, 0x13, 0x96, 0x88, - 0x43, 0x78, 0xf9, 0x44, 0xa0, 0x72, 0x1c, 0x55, 0x0e, 0x89, 0x0c, 0xa4, 0x7e, 0x04, 0x8a, 0xe6, - 0x9a, 0xde, 0x74, 0x1c, 0x5b, 0xf3, 0x77, 0x01, 0x31, 0x27, 0xa9, 0x3b, 0xd4, 0x3c, 0xd4, 0x47, - 0x86, 0x6b, 0x0c, 0xd9, 0xb8, 0xc4, 0x0b, 0xa1, 0xc2, 0x38, 0xdb, 0xd4, 0x3c, 0xdc, 0x91, 0x74, - 0xf5, 0x3d, 0x80, 0xfe, 0xd8, 0x23, 0x86, 0xd5, 0x65, 0xd1, 0x04, 0x33, 0x1d, 0x6f, 0xe9, 0x96, - 0x7c, 0x7f, 0xa5, 0x9e, 0x3c, 0xea, 0x8a, 0x60, 0xb4, 0x22, 0xba, 0xfa, 0xff, 0xe1, 0x66, 0xcf, - 0x31, 0x4c, 0xfe, 0x2d, 0x42, 0x2f, 0x7a, 0xf2, 0x42, 0x4f, 0x20, 0x2f, 0xa0, 0x72, 0x25, 0x13, - 0x8f, 0xdb, 0xac, 0xcf, 0xad, 0x05, 0x2c, 0xf1, 0x8d, 0x32, 0xc0, 0x4c, 0x8f, 0xfa, 0x27, 0x29, - 0x28, 0x46, 0xfa, 0x51, 0x55, 0xbc, 0xe4, 0x04, 0x9e, 0x61, 0xbb, 0x32, 0xe3, 0x2f, 0xe2, 0x38, - 0x09, 0xb5, 0xa1, 0x34, 0x8e, 0xa4, 0xaf, 0x8c, 0xe7, 0x12, 0x46, 0x8d, 0xe3, 0xb2, 0xe8, 0x7d, - 0x28, 0x86, 0x0f, 0xde, 0xa1, 0x87, 0xbd, 0xfa, 0x7d, 0x7c, 0x06, 0x57, 0xbf, 0x09, 0xf0, 0x2d, - 0x6a, 0xbb, 0x03, 0x7a, 0x48, 0x5c, 0xfe, 0x44, 0xcb, 0xf2, 0x45, 0x12, 0x5a, 0x51, 0xb6, 0x78, - 0x19, 0x40, 0x2c, 0x41, 0xf4, 0x52, 0x29, 0x9a, 0xea, 0x5f, 0xa4, 0x21, 0x8f, 0x29, 0x0d, 0x9a, - 0x75, 0x54, 0x85, 0xbc, 0xf4, 0x13, 0xfc, 0xfe, 0x69, 0x14, 0xcf, 0xcf, 0x36, 0x72, 0xc2, 0x41, - 0xe4, 0x4c, 0xee, 0x19, 0x62, 0x1e, 0x3c, 0x7d, 0x99, 0x07, 0x47, 0x0f, 0xa0, 0x2c, 0x41, 0xfa, - 0x81, 0xe1, 0x1f, 0x88, 0xe4, 0xad, 0xb1, 0x7c, 0x7e, 0xb6, 0x01, 0x02, 0xb9, 0x65, 0xf8, 0x07, - 0x18, 0x04, 0x9a, 0xfd, 0x46, 0x1a, 0x94, 0x3e, 0xa6, 0xb6, 0xab, 0x07, 0x7c, 0x12, 0xb2, 0xd0, - 0x98, 0xb8, 0x8e, 0xb3, 0xa9, 0xca, 0xaf, 0x19, 0xe0, 0xe3, 0xd9, 0xe4, 0x35, 0x58, 0xf2, 0x28, - 0x0d, 0x84, 0xdb, 0xb2, 0xa9, 0x2b, 0x6b, 0x18, 0xd5, 0xc4, 0xd2, 0x36, 0xa5, 0x01, 0x96, 0x38, - 0x5c, 0xf6, 0x62, 0x2d, 0xf4, 0x00, 0x56, 0x1d, 0xc3, 0x0f, 0x74, 0xee, 0xef, 0xac, 0x99, 0xb6, - 0x3c, 0x3f, 0x6a, 0x88, 0xf1, 0x36, 0x39, 0x2b, 0x94, 0x50, 0xff, 0x21, 0x05, 0x25, 0x36, 0x19, - 0x7b, 0xdf, 0x36, 0x59, 0x90, 0xf7, 0xf9, 0x63, 0x8f, 0xdb, 0x90, 0x31, 0x7d, 0x4f, 0x1a, 0x95, - 0x5f, 0xbe, 0xcd, 0x3e, 0xc6, 0x8c, 0x86, 0x3e, 0x82, 0xbc, 0xac, 0xa5, 0x88, 0xb0, 0x43, 0xbd, - 0x3e, 0x1c, 0x95, 0xb6, 0x91, 0x72, 0x7c, 0x2f, 0xcf, 0x46, 0x27, 0x2e, 0x01, 0x1c, 0x27, 0xa1, - 0x5b, 0x90, 0x36, 0x85, 0xb9, 0xe4, 0xe7, 0x32, 0xcd, 0x0e, 0x4e, 0x9b, 0xae, 0xfa, 0xa3, 0x14, - 0x2c, 0xcd, 0x0e, 0x3c, 0xdb, 0x01, 0x77, 0xa0, 0xe8, 0x4f, 0xf6, 0xfc, 0xa9, 0x1f, 0x90, 0x51, - 0xf8, 0xfc, 0x1c, 0x11, 0x50, 0x1b, 0x8a, 0x86, 0x33, 0xa4, 0x9e, 0x1d, 0x1c, 0x8c, 0x64, 0x96, - 0x9a, 0x1c, 0x2a, 0xc4, 0x75, 0xd6, 0xea, 0xa1, 0x08, 0x9e, 0x49, 0x87, 0xf7, 0xbe, 0xf8, 0x46, - 0x81, 0xdf, 0xfb, 0x6f, 0x40, 0xd9, 0x31, 0x46, 0xbc, 0xb8, 0x14, 0xd8, 0x23, 0x31, 0x8f, 0x2c, - 0x2e, 0x49, 0xda, 0xc0, 0x1e, 0x11, 0x55, 0x85, 0x62, 0xa4, 0x0c, 0xad, 0x40, 0xa9, 0xae, 0xf5, - 0xf5, 0x87, 0x8f, 0x9e, 0xe8, 0x4f, 0x9b, 0x3b, 0xca, 0x82, 0x8c, 0x4d, 0xff, 0x34, 0x05, 0x4b, - 0xd2, 0x1d, 0xc9, 0x78, 0xff, 0x4d, 0x58, 0xf4, 0x8c, 0xfd, 0x20, 0xcc, 0x48, 0xb2, 0x62, 0x57, - 0x33, 0x0f, 0xcf, 0x32, 0x12, 0xc6, 0x4a, 0xce, 0x48, 0x62, 0x1f, 0x44, 0x64, 0xae, 0xfc, 0x20, - 0x22, 0xfb, 0x33, 0xf9, 0x20, 0x42, 0xfd, 0x15, 0x80, 0x4d, 0xdb, 0x21, 0x03, 0x51, 0x87, 0x4a, - 0xca, 0x2f, 0x59, 0x0c, 0x27, 0xeb, 0x9c, 0x61, 0x0c, 0xd7, 0x6e, 0x61, 0x46, 0x63, 0xac, 0xa1, - 0x6d, 0xc9, 0xc3, 0xc8, 0x59, 0x4f, 0x19, 0x6b, 0x68, 0x5b, 0xd1, 0xcb, 0x5d, 0xf6, 0xba, 0x97, - 0xbb, 0xd3, 0x14, 0xac, 0xc8, 0xd8, 0x35, 0x72, 0xbf, 0x5f, 0x86, 0xa2, 0x08, 0x63, 0x67, 0x09, - 0x1d, 0xff, 0x08, 0x40, 0xe0, 0xda, 0x2d, 0x5c, 0x10, 0xec, 0xb6, 0x85, 0x36, 0xa0, 0x24, 0xa1, - 0xb1, 0x4f, 0xab, 0x40, 0x90, 0x3a, 0x6c, 0xf8, 0x5f, 0x85, 0xec, 0xbe, 0xed, 0x10, 0xb9, 0xd1, - 0x13, 0x1d, 0xc0, 0xcc, 0x00, 0x5b, 0x0b, 0x98, 0xa3, 0x1b, 0x85, 0xb0, 0x50, 0xc7, 0xc7, 0x27, - 0xd3, 0xce, 0xf8, 0xf8, 0x44, 0x06, 0x3a, 0x37, 0x3e, 0x81, 0x63, 0xe3, 0x13, 0x6c, 0x31, 0x3e, - 0x09, 0x8d, 0x8f, 0x4f, 0x90, 0x7e, 0x26, 0xe3, 0xdb, 0x86, 0x5b, 0x0d, 0xc7, 0x30, 0x0f, 0x1d, - 0xdb, 0x0f, 0x88, 0x15, 0xf7, 0x18, 0x8f, 0x20, 0x7f, 0x21, 0xe8, 0xbc, 0xaa, 0xa2, 0x29, 0x91, - 0xea, 0xbf, 0xa6, 0xa0, 0xbc, 0x45, 0x0c, 0x27, 0x38, 0x98, 0x95, 0x8d, 0x02, 0xe2, 0x07, 0xf2, - 0xb2, 0xe2, 0xbf, 0xd1, 0xd7, 0xa0, 0x10, 0xc5, 0x24, 0xd7, 0xbe, 0xcd, 0x45, 0x50, 0xf4, 0x18, - 0x16, 0xd9, 0x19, 0xa3, 0x93, 0x30, 0xd9, 0xb9, 0xea, 0xd9, 0x47, 0x22, 0xd9, 0x25, 0xe3, 0x11, - 0x1e, 0x84, 0xf0, 0xad, 0x94, 0xc3, 0x61, 0x13, 0xfd, 0x5f, 0x28, 0xf3, 0x57, 0x8b, 0x30, 0xe6, - 0xca, 0x5d, 0xa7, 0xb3, 0x24, 0x1e, 0x1e, 0x45, 0xbc, 0xf5, 0x87, 0x69, 0x58, 0xdd, 0x31, 0xa6, - 0x7b, 0x44, 0xba, 0x0d, 0x62, 0x61, 0x62, 0x52, 0xcf, 0x42, 0xbd, 0xb8, 0xbb, 0xb9, 0xe2, 0x1d, - 0x33, 0x49, 0x38, 0xd9, 0xeb, 0x84, 0x09, 0x58, 0x3a, 0x96, 0x80, 0xad, 0x42, 0xce, 0xa5, 0xae, - 0x49, 0xa4, 0x2f, 0x12, 0x0d, 0xf5, 0xb7, 0x52, 0x71, 0x5f, 0x53, 0x89, 0xde, 0x18, 0x79, 0x05, - 0xaa, 0x43, 0x83, 0xa8, 0x3b, 0xf4, 0x11, 0x54, 0xfa, 0x5a, 0x13, 0x6b, 0x83, 0x46, 0xf7, 0xdb, - 0x7a, 0xbf, 0xbe, 0xdd, 0xaf, 0x3f, 0x7a, 0xa0, 0xf7, 0xba, 0xdb, 0xdf, 0x79, 0xf8, 0xf8, 0xc1, - 0xd7, 0x94, 0x54, 0xa5, 0x7a, 0x72, 0x5a, 0xbd, 0xd3, 0xa9, 0x37, 0xb7, 0xc5, 0x91, 0xd9, 0xa3, - 0x2f, 0xfb, 0x86, 0xe3, 0x1b, 0x8f, 0x1e, 0xf4, 0xa8, 0x33, 0x65, 0x18, 0xf4, 0x15, 0x40, 0x9b, - 0x1a, 0xee, 0x68, 0x03, 0x3d, 0x74, 0x68, 0xcd, 0x46, 0x53, 0x49, 0x8b, 0xb4, 0x66, 0x93, 0x78, - 0x2e, 0x09, 0xea, 0x5a, 0xff, 0xe1, 0xa3, 0x27, 0xcd, 0x46, 0x93, 0x1d, 0x82, 0x72, 0xfc, 0x76, - 0x8b, 0x5f, 0xda, 0xa9, 0x4b, 0x2f, 0xed, 0xd9, 0xdd, 0x9f, 0xbe, 0xe4, 0xee, 0xdf, 0x84, 0x55, - 0xd3, 0xa3, 0xbe, 0xaf, 0xb3, 0x5c, 0x81, 0x58, 0x73, 0xd9, 0xc8, 0x17, 0xce, 0xcf, 0x36, 0x6e, - 0x34, 0x19, 0xbf, 0xcf, 0xd9, 0x52, 0xfd, 0x0d, 0x33, 0x46, 0xe2, 0x3d, 0xa9, 0xbf, 0x9b, 0x61, - 0x61, 0x97, 0x7d, 0x64, 0x3b, 0x64, 0x48, 0x7c, 0xf4, 0x1c, 0x56, 0x4c, 0x8f, 0x58, 0x2c, 0x09, - 0x30, 0x9c, 0xf8, 0x07, 0xbd, 0xff, 0x27, 0x31, 0x02, 0x8a, 0x04, 0x6b, 0xcd, 0x48, 0xaa, 0x3f, - 0x26, 0x26, 0x5e, 0x36, 0x2f, 0xb4, 0xd1, 0xc7, 0xb0, 0xe2, 0x13, 0xc7, 0x76, 0x27, 0x2f, 0x75, - 0x93, 0xba, 0x01, 0x79, 0x19, 0xbe, 0xad, 0x5d, 0xa7, 0xb7, 0xaf, 0x6d, 0x33, 0xa9, 0xa6, 0x10, - 0x6a, 0xa0, 0xf3, 0xb3, 0x8d, 0xe5, 0x8b, 0x34, 0xbc, 0x2c, 0x35, 0xcb, 0x76, 0xa5, 0x03, 0xcb, - 0x17, 0x47, 0x83, 0x56, 0xa5, 0xa7, 0xe0, 0x0e, 0x27, 0xf4, 0x04, 0xe8, 0x0e, 0x14, 0x3c, 0x32, - 0xb4, 0xfd, 0xc0, 0x13, 0x66, 0x66, 0x9c, 0x88, 0xc2, 0xfc, 0x84, 0xf8, 0xde, 0xaa, 0xf2, 0x4b, - 0x30, 0xd7, 0x23, 0x3b, 0x5a, 0x96, 0xed, 0x1b, 0x7b, 0x52, 0x65, 0x01, 0x87, 0x4d, 0xb6, 0x63, - 0x27, 0x7e, 0x14, 0xd6, 0xf1, 0xdf, 0x8c, 0xc6, 0xe3, 0x0f, 0xf9, 0xf5, 0x19, 0x8f, 0x30, 0xc2, - 0x8f, 0x5c, 0xb3, 0xb1, 0x8f, 0x5c, 0x57, 0x21, 0xe7, 0x90, 0x23, 0xe2, 0x88, 0x9b, 0x1f, 0x8b, - 0xc6, 0xbd, 0x07, 0x50, 0x0e, 0xbf, 0xa6, 0xe4, 0x9f, 0x69, 0x14, 0x20, 0x3b, 0xa8, 0xf7, 0x9f, - 0x29, 0x0b, 0x08, 0x20, 0x2f, 0x76, 0xb2, 0x78, 0xf7, 0x6b, 0x76, 0x3b, 0x9b, 0xed, 0xa7, 0x4a, - 0xfa, 0xde, 0x6f, 0x67, 0xa1, 0x18, 0xbd, 0x3c, 0xb1, 0x9b, 0xa6, 0xa3, 0xbd, 0x08, 0x8f, 0x42, - 0x44, 0xef, 0x90, 0x63, 0xf4, 0xc6, 0xac, 0x66, 0xf5, 0x91, 0x78, 0x6a, 0x8f, 0xd8, 0x61, 0xbd, - 0xea, 0x2d, 0x28, 0xd4, 0xfb, 0xfd, 0xf6, 0xd3, 0x8e, 0xd6, 0x52, 0x3e, 0x4d, 0x55, 0xbe, 0x70, - 0x72, 0x5a, 0xbd, 0x11, 0x81, 0xea, 0xbe, 0xd8, 0x7c, 0x1c, 0xd5, 0x6c, 0x6a, 0xbd, 0x81, 0xd6, - 0x52, 0x3e, 0x49, 0xcf, 0xa3, 0x78, 0x0d, 0x86, 0x7f, 0x28, 0x54, 0xec, 0x61, 0xad, 0x57, 0xc7, - 0xac, 0xc3, 0x4f, 0xd3, 0xa2, 0x94, 0x36, 0xeb, 0xd1, 0x23, 0x63, 0xc3, 0x63, 0x7d, 0xae, 0x87, - 0x5f, 0xde, 0x7d, 0x92, 0x11, 0xdf, 0x92, 0xcc, 0x9e, 0xd1, 0x88, 0x61, 0x4d, 0x59, 0x6f, 0xfc, - 0xfd, 0x92, 0xab, 0xc9, 0xcc, 0xf5, 0xd6, 0x67, 0x9e, 0x8a, 0x69, 0x51, 0x61, 0x11, 0xef, 0x76, - 0x3a, 0x0c, 0xf4, 0x49, 0x76, 0x6e, 0x76, 0x78, 0xe2, 0xb2, 0xfc, 0x1a, 0xdd, 0x85, 0x42, 0xf8, - 0xbc, 0xa9, 0x7c, 0x9a, 0x9d, 0x1b, 0x50, 0x33, 0x7c, 0x9b, 0xe5, 0x1d, 0x6e, 0xed, 0x0e, 0xf8, - 0x87, 0x81, 0x9f, 0xe4, 0xe6, 0x3b, 0x3c, 0x98, 0x04, 0x16, 0x3d, 0x76, 0xd9, 0x99, 0x95, 0x55, - 0xbb, 0x4f, 0x73, 0xc2, 0x17, 0x44, 0x18, 0x59, 0xb2, 0x7b, 0x0b, 0x0a, 0x58, 0xfb, 0x96, 0xf8, - 0x86, 0xf0, 0x93, 0xfc, 0x9c, 0x1e, 0x4c, 0x3e, 0x26, 0x26, 0xeb, 0xad, 0x0a, 0x79, 0xac, 0xed, - 0x74, 0x9f, 0x6b, 0xca, 0xef, 0xe5, 0xe7, 0xf4, 0x60, 0x32, 0xa2, 0xfc, 0x4b, 0xaa, 0x42, 0x17, - 0xf7, 0xb6, 0xea, 0x7c, 0x51, 0xe6, 0xf5, 0x74, 0xbd, 0xf1, 0x81, 0xe1, 0x12, 0x6b, 0xf6, 0x3d, - 0x4d, 0xc4, 0xba, 0xf7, 0x73, 0x50, 0x08, 0x23, 0x5d, 0xb4, 0x0e, 0xf9, 0x17, 0x5d, 0xfc, 0x4c, - 0xc3, 0xca, 0x82, 0xb0, 0x72, 0xc8, 0x79, 0x21, 0x72, 0x94, 0x2a, 0x2c, 0xee, 0xd4, 0x3b, 0xf5, - 0xa7, 0x1a, 0x0e, 0x4b, 0xee, 0x21, 0x40, 0x86, 0x6b, 0x15, 0x45, 0x76, 0x10, 0xe9, 0x6c, 0xac, - 0xfd, 0xf0, 0x27, 0xeb, 0x0b, 0x3f, 0xfe, 0xc9, 0xfa, 0xc2, 0x27, 0xe7, 0xeb, 0xa9, 0x1f, 0x9e, - 0xaf, 0xa7, 0xfe, 0xf6, 0x7c, 0x3d, 0xf5, 0x2f, 0xe7, 0xeb, 0xa9, 0xbd, 0x3c, 0xbf, 0x54, 0x1e, - 0xff, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xac, 0xef, 0xe8, 0x11, 0x9e, 0x32, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/api/types.proto b/vendor/github.com/docker/swarmkit/api/types.proto index e075534a07821..bdd1972ca9889 100644 --- a/vendor/github.com/docker/swarmkit/api/types.proto +++ b/vendor/github.com/docker/swarmkit/api/types.proto @@ -4,6 +4,7 @@ package docker.swarmkit.v1; import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; +import "google/protobuf/wrappers.proto"; import "gogoproto/gogo.proto"; // This file contains types that are common to objects and spec or that are not @@ -77,6 +78,17 @@ message Resources { message ResourceRequirements { Resources limits = 1; Resources reservations = 2; + + // Amount of swap in bytes - can only be used together with a memory limit + // -1 means unlimited + // a null pointer indicates that the default behaviour of granting twice + // the memory is maintained + google.protobuf.Int64Value swap_bytes = 3; + + // Tune container memory swappiness (0 to 100) - if not specified, defaults + // to the container OS's default - generally 60, or the value predefined in + // the image; set to -1 to unset a previously set value + google.protobuf.Int64Value memory_swappiness = 4; } message Platform { @@ -246,6 +258,8 @@ message Mount { // Propagation mode of mount. Propagation propagation = 1; + // allows non-recursive bind-mount, i.e. mount(2) with "bind" rather than "rbind". + bool nonrecursive = 2 [(gogoproto.customname) = "NonRecursive"]; } // VolumeOptions contains parameters for mounting the volume. @@ -277,6 +291,8 @@ message Mount { // Mode of the tmpfs upon creation uint32 mode = 2 [(gogoproto.customtype) = "os.FileMode", (gogoproto.nullable) = false]; + // Options passed to tmpfs mount + string options = 3; // TODO(stevvooe): There are several more tmpfs flags, specified in the // daemon, that are accepted. Only the most basic are added for now. // @@ -862,6 +878,9 @@ message Placement { // This field is used in the platform filter for scheduling. If empty, // then the platform filter is off, meaning there are no scheduling restrictions. repeated Platform platforms = 3; + + // MaxReplicas specifies the limit for maximum number of replicas running on one node. + uint64 max_replicas = 4; } // JoinToken contains the join tokens for workers and managers. @@ -971,6 +990,12 @@ message FileTarget { uint32 mode = 4 [(gogoproto.customtype) = "os.FileMode", (gogoproto.nullable) = false]; } +// RuntimeTarget represents that this secret is _not_ mounted into the +// container, but is used for some other purpose by the container runtime. +// +// Currently, RuntimeTarget has no fields; it's just a placeholder. +message RuntimeTarget {} + // SecretReference is the linkage between a service and a secret that it uses. message SecretReference { // SecretID represents the ID of the specific Secret that we're @@ -998,9 +1023,10 @@ message ConfigReference { // lookup/display purposes. The config in the reference will be identified by its ID. string config_name = 2; - // Target specifies how this secret should be exposed to the task. + // Target specifies how this config should be exposed to the task. oneof target { FileTarget file = 3; + RuntimeTarget runtime = 4; } } @@ -1070,6 +1096,10 @@ message Privileges { oneof source { string file = 1; string registry = 2; + + // Config represents a Config ID from which to get the CredentialSpec. + // The Config MUST be included in the SecretReferences with a RuntimeTarget + string config = 3; } } CredentialSpec credential_spec = 1; @@ -1085,3 +1115,19 @@ message Privileges { } SELinuxContext selinux_context = 2 [(gogoproto.customname) = "SELinuxContext"]; } + +// JobStatus indicates the status of a Service that is in one of the Job modes. +message JobStatus { + // JobIteration is the count of how many times the Job has been excecuted, + // successfully or otherwise. "Executed" refers to the job as a whole being + // started, not to the individual Tasks being launched. This is used to + // disambiguate which Tasks belong to which iteration of a Job. + Version job_iteration = 1 [(gogoproto.nullable) = false]; + + // LastExecution is the time that the job was last executed. This is set by + // the orchestrator in the same transaction that JobIteration is incremented. + // While time is a fungible concept in distributed systems like Swarmkit, + // this value gives us a best-effort attempt to prevent weird behavior like + // newly added nodes executing long-forgotten jobs. + google.protobuf.Timestamp last_execution = 2; +} diff --git a/vendor/github.com/docker/swarmkit/api/watch.pb.go b/vendor/github.com/docker/swarmkit/api/watch.pb.go index 9d152514cc3c1..cdae947d607d7 100644 --- a/vendor/github.com/docker/swarmkit/api/watch.pb.go +++ b/vendor/github.com/docker/swarmkit/api/watch.pb.go @@ -3,34 +3,38 @@ package api -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "github.com/gogo/protobuf/gogoproto" -import _ "github.com/docker/swarmkit/protobuf/plugin" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import context "golang.org/x/net/context" -import grpc "google.golang.org/grpc" - -import raftselector "github.com/docker/swarmkit/manager/raftselector" -import codes "google.golang.org/grpc/codes" -import status "google.golang.org/grpc/status" -import metadata "google.golang.org/grpc/metadata" -import peer "google.golang.org/grpc/peer" -import rafttime "time" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + context "context" + fmt "fmt" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + raftselector "github.com/docker/swarmkit/manager/raftselector" + _ "github.com/docker/swarmkit/protobuf/plugin" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + metadata "google.golang.org/grpc/metadata" + peer "google.golang.org/grpc/peer" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" + rafttime "time" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + // WatchActionKind distinguishes between creations, updates, and removals. It // is structured as a bitmap so multiple kinds of events can be requested with // a mask. @@ -49,6 +53,7 @@ var WatchActionKind_name = map[int32]string{ 2: "WATCH_ACTION_UPDATE", 4: "WATCH_ACTION_REMOVE", } + var WatchActionKind_value = map[string]int32{ "WATCH_ACTION_UNKNOWN": 0, "WATCH_ACTION_CREATE": 1, @@ -59,7 +64,10 @@ var WatchActionKind_value = map[string]int32{ func (x WatchActionKind) String() string { return proto.EnumName(WatchActionKind_name, int32(x)) } -func (WatchActionKind) EnumDescriptor() ([]byte, []int) { return fileDescriptorWatch, []int{0} } + +func (WatchActionKind) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_da25266013800cd9, []int{0} +} type Object struct { // Types that are valid to be assigned to Object: @@ -75,9 +83,37 @@ type Object struct { Object isObject_Object `protobuf_oneof:"Object"` } -func (m *Object) Reset() { *m = Object{} } -func (*Object) ProtoMessage() {} -func (*Object) Descriptor() ([]byte, []int) { return fileDescriptorWatch, []int{0} } +func (m *Object) Reset() { *m = Object{} } +func (*Object) ProtoMessage() {} +func (*Object) Descriptor() ([]byte, []int) { + return fileDescriptor_da25266013800cd9, []int{0} +} +func (m *Object) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Object) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Object.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Object) XXX_Merge(src proto.Message) { + xxx_messageInfo_Object.Merge(m, src) +} +func (m *Object) XXX_Size() int { + return m.Size() +} +func (m *Object) XXX_DiscardUnknown() { + xxx_messageInfo_Object.DiscardUnknown(m) +} + +var xxx_messageInfo_Object proto.InternalMessageInfo type isObject_Object interface { isObject_Object() @@ -86,31 +122,31 @@ type isObject_Object interface { } type Object_Node struct { - Node *Node `protobuf:"bytes,1,opt,name=node,oneof"` + Node *Node `protobuf:"bytes,1,opt,name=node,proto3,oneof" json:"node,omitempty"` } type Object_Service struct { - Service *Service `protobuf:"bytes,2,opt,name=service,oneof"` + Service *Service `protobuf:"bytes,2,opt,name=service,proto3,oneof" json:"service,omitempty"` } type Object_Network struct { - Network *Network `protobuf:"bytes,3,opt,name=network,oneof"` + Network *Network `protobuf:"bytes,3,opt,name=network,proto3,oneof" json:"network,omitempty"` } type Object_Task struct { - Task *Task `protobuf:"bytes,4,opt,name=task,oneof"` + Task *Task `protobuf:"bytes,4,opt,name=task,proto3,oneof" json:"task,omitempty"` } type Object_Cluster struct { - Cluster *Cluster `protobuf:"bytes,5,opt,name=cluster,oneof"` + Cluster *Cluster `protobuf:"bytes,5,opt,name=cluster,proto3,oneof" json:"cluster,omitempty"` } type Object_Secret struct { - Secret *Secret `protobuf:"bytes,6,opt,name=secret,oneof"` + Secret *Secret `protobuf:"bytes,6,opt,name=secret,proto3,oneof" json:"secret,omitempty"` } type Object_Resource struct { - Resource *Resource `protobuf:"bytes,7,opt,name=resource,oneof"` + Resource *Resource `protobuf:"bytes,7,opt,name=resource,proto3,oneof" json:"resource,omitempty"` } type Object_Extension struct { - Extension *Extension `protobuf:"bytes,8,opt,name=extension,oneof"` + Extension *Extension `protobuf:"bytes,8,opt,name=extension,proto3,oneof" json:"extension,omitempty"` } type Object_Config struct { - Config *Config `protobuf:"bytes,9,opt,name=config,oneof"` + Config *Config `protobuf:"bytes,9,opt,name=config,proto3,oneof" json:"config,omitempty"` } func (*Object_Node) isObject_Object() {} @@ -193,9 +229,9 @@ func (m *Object) GetConfig() *Config { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*Object) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _Object_OneofMarshaler, _Object_OneofUnmarshaler, _Object_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Object) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*Object_Node)(nil), (*Object_Service)(nil), (*Object_Network)(nil), @@ -208,198 +244,6 @@ func (*Object) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, } } -func _Object_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*Object) - // Object - switch x := m.Object.(type) { - case *Object_Node: - _ = b.EncodeVarint(1<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Node); err != nil { - return err - } - case *Object_Service: - _ = b.EncodeVarint(2<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Service); err != nil { - return err - } - case *Object_Network: - _ = b.EncodeVarint(3<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Network); err != nil { - return err - } - case *Object_Task: - _ = b.EncodeVarint(4<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Task); err != nil { - return err - } - case *Object_Cluster: - _ = b.EncodeVarint(5<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Cluster); err != nil { - return err - } - case *Object_Secret: - _ = b.EncodeVarint(6<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Secret); err != nil { - return err - } - case *Object_Resource: - _ = b.EncodeVarint(7<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Resource); err != nil { - return err - } - case *Object_Extension: - _ = b.EncodeVarint(8<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Extension); err != nil { - return err - } - case *Object_Config: - _ = b.EncodeVarint(9<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Config); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("Object.Object has unexpected type %T", x) - } - return nil -} - -func _Object_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*Object) - switch tag { - case 1: // Object.node - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Node) - err := b.DecodeMessage(msg) - m.Object = &Object_Node{msg} - return true, err - case 2: // Object.service - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Service) - err := b.DecodeMessage(msg) - m.Object = &Object_Service{msg} - return true, err - case 3: // Object.network - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Network) - err := b.DecodeMessage(msg) - m.Object = &Object_Network{msg} - return true, err - case 4: // Object.task - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Task) - err := b.DecodeMessage(msg) - m.Object = &Object_Task{msg} - return true, err - case 5: // Object.cluster - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Cluster) - err := b.DecodeMessage(msg) - m.Object = &Object_Cluster{msg} - return true, err - case 6: // Object.secret - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Secret) - err := b.DecodeMessage(msg) - m.Object = &Object_Secret{msg} - return true, err - case 7: // Object.resource - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Resource) - err := b.DecodeMessage(msg) - m.Object = &Object_Resource{msg} - return true, err - case 8: // Object.extension - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Extension) - err := b.DecodeMessage(msg) - m.Object = &Object_Extension{msg} - return true, err - case 9: // Object.config - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Config) - err := b.DecodeMessage(msg) - m.Object = &Object_Config{msg} - return true, err - default: - return false, nil - } -} - -func _Object_OneofSizer(msg proto.Message) (n int) { - m := msg.(*Object) - // Object - switch x := m.Object.(type) { - case *Object_Node: - s := proto.Size(x.Node) - n += proto.SizeVarint(1<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Object_Service: - s := proto.Size(x.Service) - n += proto.SizeVarint(2<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Object_Network: - s := proto.Size(x.Network) - n += proto.SizeVarint(3<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Object_Task: - s := proto.Size(x.Task) - n += proto.SizeVarint(4<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Object_Cluster: - s := proto.Size(x.Cluster) - n += proto.SizeVarint(5<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Object_Secret: - s := proto.Size(x.Secret) - n += proto.SizeVarint(6<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Object_Resource: - s := proto.Size(x.Resource) - n += proto.SizeVarint(7<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Object_Extension: - s := proto.Size(x.Extension) - n += proto.SizeVarint(8<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Object_Config: - s := proto.Size(x.Config) - n += proto.SizeVarint(9<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - // FIXME(aaronl): These messages should ideally be embedded in SelectBy, but // protoc generates bad code for that. type SelectBySlot struct { @@ -407,9 +251,37 @@ type SelectBySlot struct { Slot uint64 `protobuf:"varint,2,opt,name=slot,proto3" json:"slot,omitempty"` } -func (m *SelectBySlot) Reset() { *m = SelectBySlot{} } -func (*SelectBySlot) ProtoMessage() {} -func (*SelectBySlot) Descriptor() ([]byte, []int) { return fileDescriptorWatch, []int{1} } +func (m *SelectBySlot) Reset() { *m = SelectBySlot{} } +func (*SelectBySlot) ProtoMessage() {} +func (*SelectBySlot) Descriptor() ([]byte, []int) { + return fileDescriptor_da25266013800cd9, []int{1} +} +func (m *SelectBySlot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SelectBySlot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SelectBySlot.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SelectBySlot) XXX_Merge(src proto.Message) { + xxx_messageInfo_SelectBySlot.Merge(m, src) +} +func (m *SelectBySlot) XXX_Size() int { + return m.Size() +} +func (m *SelectBySlot) XXX_DiscardUnknown() { + xxx_messageInfo_SelectBySlot.DiscardUnknown(m) +} + +var xxx_messageInfo_SelectBySlot proto.InternalMessageInfo type SelectByCustom struct { Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` @@ -417,9 +289,37 @@ type SelectByCustom struct { Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` } -func (m *SelectByCustom) Reset() { *m = SelectByCustom{} } -func (*SelectByCustom) ProtoMessage() {} -func (*SelectByCustom) Descriptor() ([]byte, []int) { return fileDescriptorWatch, []int{2} } +func (m *SelectByCustom) Reset() { *m = SelectByCustom{} } +func (*SelectByCustom) ProtoMessage() {} +func (*SelectByCustom) Descriptor() ([]byte, []int) { + return fileDescriptor_da25266013800cd9, []int{2} +} +func (m *SelectByCustom) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SelectByCustom) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SelectByCustom.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SelectByCustom) XXX_Merge(src proto.Message) { + xxx_messageInfo_SelectByCustom.Merge(m, src) +} +func (m *SelectByCustom) XXX_Size() int { + return m.Size() +} +func (m *SelectByCustom) XXX_DiscardUnknown() { + xxx_messageInfo_SelectByCustom.DiscardUnknown(m) +} + +var xxx_messageInfo_SelectByCustom proto.InternalMessageInfo type SelectBy struct { // TODO(aaronl): Are all of these things we want to expose in @@ -446,9 +346,37 @@ type SelectBy struct { By isSelectBy_By `protobuf_oneof:"By"` } -func (m *SelectBy) Reset() { *m = SelectBy{} } -func (*SelectBy) ProtoMessage() {} -func (*SelectBy) Descriptor() ([]byte, []int) { return fileDescriptorWatch, []int{3} } +func (m *SelectBy) Reset() { *m = SelectBy{} } +func (*SelectBy) ProtoMessage() {} +func (*SelectBy) Descriptor() ([]byte, []int) { + return fileDescriptor_da25266013800cd9, []int{3} +} +func (m *SelectBy) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SelectBy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SelectBy.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SelectBy) XXX_Merge(src proto.Message) { + xxx_messageInfo_SelectBy.Merge(m, src) +} +func (m *SelectBy) XXX_Size() int { + return m.Size() +} +func (m *SelectBy) XXX_DiscardUnknown() { + xxx_messageInfo_SelectBy.DiscardUnknown(m) +} + +var xxx_messageInfo_SelectBy proto.InternalMessageInfo type isSelectBy_By interface { isSelectBy_By() @@ -457,52 +385,52 @@ type isSelectBy_By interface { } type SelectBy_ID struct { - ID string `protobuf:"bytes,1,opt,name=id,proto3,oneof"` + ID string `protobuf:"bytes,1,opt,name=id,proto3,oneof" json:"id,omitempty"` } type SelectBy_IDPrefix struct { - IDPrefix string `protobuf:"bytes,2,opt,name=id_prefix,json=idPrefix,proto3,oneof"` + IDPrefix string `protobuf:"bytes,2,opt,name=id_prefix,json=idPrefix,proto3,oneof" json:"id_prefix,omitempty"` } type SelectBy_Name struct { - Name string `protobuf:"bytes,3,opt,name=name,proto3,oneof"` + Name string `protobuf:"bytes,3,opt,name=name,proto3,oneof" json:"name,omitempty"` } type SelectBy_NamePrefix struct { - NamePrefix string `protobuf:"bytes,4,opt,name=name_prefix,json=namePrefix,proto3,oneof"` + NamePrefix string `protobuf:"bytes,4,opt,name=name_prefix,json=namePrefix,proto3,oneof" json:"name_prefix,omitempty"` } type SelectBy_Custom struct { - Custom *SelectByCustom `protobuf:"bytes,5,opt,name=custom,oneof"` + Custom *SelectByCustom `protobuf:"bytes,5,opt,name=custom,proto3,oneof" json:"custom,omitempty"` } type SelectBy_CustomPrefix struct { - CustomPrefix *SelectByCustom `protobuf:"bytes,6,opt,name=custom_prefix,json=customPrefix,oneof"` + CustomPrefix *SelectByCustom `protobuf:"bytes,6,opt,name=custom_prefix,json=customPrefix,proto3,oneof" json:"custom_prefix,omitempty"` } type SelectBy_ServiceID struct { - ServiceID string `protobuf:"bytes,7,opt,name=service_id,json=serviceId,proto3,oneof"` + ServiceID string `protobuf:"bytes,7,opt,name=service_id,json=serviceId,proto3,oneof" json:"service_id,omitempty"` } type SelectBy_NodeID struct { - NodeID string `protobuf:"bytes,8,opt,name=node_id,json=nodeId,proto3,oneof"` + NodeID string `protobuf:"bytes,8,opt,name=node_id,json=nodeId,proto3,oneof" json:"node_id,omitempty"` } type SelectBy_Slot struct { - Slot *SelectBySlot `protobuf:"bytes,9,opt,name=slot,oneof"` + Slot *SelectBySlot `protobuf:"bytes,9,opt,name=slot,proto3,oneof" json:"slot,omitempty"` } type SelectBy_DesiredState struct { - DesiredState TaskState `protobuf:"varint,10,opt,name=desired_state,json=desiredState,proto3,enum=docker.swarmkit.v1.TaskState,oneof"` + DesiredState TaskState `protobuf:"varint,10,opt,name=desired_state,json=desiredState,proto3,enum=docker.swarmkit.v1.TaskState,oneof" json:"desired_state,omitempty"` } type SelectBy_Role struct { - Role NodeRole `protobuf:"varint,11,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole,oneof"` + Role NodeRole `protobuf:"varint,11,opt,name=role,proto3,enum=docker.swarmkit.v1.NodeRole,oneof" json:"role,omitempty"` } type SelectBy_Membership struct { - Membership NodeSpec_Membership `protobuf:"varint,12,opt,name=membership,proto3,enum=docker.swarmkit.v1.NodeSpec_Membership,oneof"` + Membership NodeSpec_Membership `protobuf:"varint,12,opt,name=membership,proto3,enum=docker.swarmkit.v1.NodeSpec_Membership,oneof" json:"membership,omitempty"` } type SelectBy_ReferencedNetworkID struct { - ReferencedNetworkID string `protobuf:"bytes,13,opt,name=referenced_network_id,json=referencedNetworkId,proto3,oneof"` + ReferencedNetworkID string `protobuf:"bytes,13,opt,name=referenced_network_id,json=referencedNetworkId,proto3,oneof" json:"referenced_network_id,omitempty"` } type SelectBy_ReferencedSecretID struct { - ReferencedSecretID string `protobuf:"bytes,14,opt,name=referenced_secret_id,json=referencedSecretId,proto3,oneof"` + ReferencedSecretID string `protobuf:"bytes,14,opt,name=referenced_secret_id,json=referencedSecretId,proto3,oneof" json:"referenced_secret_id,omitempty"` } type SelectBy_ReferencedConfigID struct { - ReferencedConfigID string `protobuf:"bytes,16,opt,name=referenced_config_id,json=referencedConfigId,proto3,oneof"` + ReferencedConfigID string `protobuf:"bytes,16,opt,name=referenced_config_id,json=referencedConfigId,proto3,oneof" json:"referenced_config_id,omitempty"` } type SelectBy_Kind struct { - Kind string `protobuf:"bytes,15,opt,name=kind,proto3,oneof"` + Kind string `protobuf:"bytes,15,opt,name=kind,proto3,oneof" json:"kind,omitempty"` } func (*SelectBy_ID) isSelectBy_By() {} @@ -641,9 +569,9 @@ func (m *SelectBy) GetKind() string { return "" } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*SelectBy) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _SelectBy_OneofMarshaler, _SelectBy_OneofUnmarshaler, _SelectBy_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*SelectBy) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*SelectBy_ID)(nil), (*SelectBy_IDPrefix)(nil), (*SelectBy_Name)(nil), @@ -663,280 +591,17 @@ func (*SelectBy) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) erro } } -func _SelectBy_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*SelectBy) - // By - switch x := m.By.(type) { - case *SelectBy_ID: - _ = b.EncodeVarint(1<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.ID) - case *SelectBy_IDPrefix: - _ = b.EncodeVarint(2<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.IDPrefix) - case *SelectBy_Name: - _ = b.EncodeVarint(3<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.Name) - case *SelectBy_NamePrefix: - _ = b.EncodeVarint(4<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.NamePrefix) - case *SelectBy_Custom: - _ = b.EncodeVarint(5<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Custom); err != nil { - return err - } - case *SelectBy_CustomPrefix: - _ = b.EncodeVarint(6<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.CustomPrefix); err != nil { - return err - } - case *SelectBy_ServiceID: - _ = b.EncodeVarint(7<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.ServiceID) - case *SelectBy_NodeID: - _ = b.EncodeVarint(8<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.NodeID) - case *SelectBy_Slot: - _ = b.EncodeVarint(9<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Slot); err != nil { - return err - } - case *SelectBy_DesiredState: - _ = b.EncodeVarint(10<<3 | proto.WireVarint) - _ = b.EncodeVarint(uint64(x.DesiredState)) - case *SelectBy_Role: - _ = b.EncodeVarint(11<<3 | proto.WireVarint) - _ = b.EncodeVarint(uint64(x.Role)) - case *SelectBy_Membership: - _ = b.EncodeVarint(12<<3 | proto.WireVarint) - _ = b.EncodeVarint(uint64(x.Membership)) - case *SelectBy_ReferencedNetworkID: - _ = b.EncodeVarint(13<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.ReferencedNetworkID) - case *SelectBy_ReferencedSecretID: - _ = b.EncodeVarint(14<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.ReferencedSecretID) - case *SelectBy_ReferencedConfigID: - _ = b.EncodeVarint(16<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.ReferencedConfigID) - case *SelectBy_Kind: - _ = b.EncodeVarint(15<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.Kind) - case nil: - default: - return fmt.Errorf("SelectBy.By has unexpected type %T", x) - } - return nil -} - -func _SelectBy_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*SelectBy) - switch tag { - case 1: // By.id - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.By = &SelectBy_ID{x} - return true, err - case 2: // By.id_prefix - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.By = &SelectBy_IDPrefix{x} - return true, err - case 3: // By.name - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.By = &SelectBy_Name{x} - return true, err - case 4: // By.name_prefix - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.By = &SelectBy_NamePrefix{x} - return true, err - case 5: // By.custom - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(SelectByCustom) - err := b.DecodeMessage(msg) - m.By = &SelectBy_Custom{msg} - return true, err - case 6: // By.custom_prefix - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(SelectByCustom) - err := b.DecodeMessage(msg) - m.By = &SelectBy_CustomPrefix{msg} - return true, err - case 7: // By.service_id - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.By = &SelectBy_ServiceID{x} - return true, err - case 8: // By.node_id - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.By = &SelectBy_NodeID{x} - return true, err - case 9: // By.slot - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(SelectBySlot) - err := b.DecodeMessage(msg) - m.By = &SelectBy_Slot{msg} - return true, err - case 10: // By.desired_state - if wire != proto.WireVarint { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeVarint() - m.By = &SelectBy_DesiredState{TaskState(x)} - return true, err - case 11: // By.role - if wire != proto.WireVarint { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeVarint() - m.By = &SelectBy_Role{NodeRole(x)} - return true, err - case 12: // By.membership - if wire != proto.WireVarint { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeVarint() - m.By = &SelectBy_Membership{NodeSpec_Membership(x)} - return true, err - case 13: // By.referenced_network_id - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.By = &SelectBy_ReferencedNetworkID{x} - return true, err - case 14: // By.referenced_secret_id - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.By = &SelectBy_ReferencedSecretID{x} - return true, err - case 16: // By.referenced_config_id - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.By = &SelectBy_ReferencedConfigID{x} - return true, err - case 15: // By.kind - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.By = &SelectBy_Kind{x} - return true, err - default: - return false, nil - } -} - -func _SelectBy_OneofSizer(msg proto.Message) (n int) { - m := msg.(*SelectBy) - // By - switch x := m.By.(type) { - case *SelectBy_ID: - n += proto.SizeVarint(1<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.ID))) - n += len(x.ID) - case *SelectBy_IDPrefix: - n += proto.SizeVarint(2<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.IDPrefix))) - n += len(x.IDPrefix) - case *SelectBy_Name: - n += proto.SizeVarint(3<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.Name))) - n += len(x.Name) - case *SelectBy_NamePrefix: - n += proto.SizeVarint(4<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.NamePrefix))) - n += len(x.NamePrefix) - case *SelectBy_Custom: - s := proto.Size(x.Custom) - n += proto.SizeVarint(5<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *SelectBy_CustomPrefix: - s := proto.Size(x.CustomPrefix) - n += proto.SizeVarint(6<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *SelectBy_ServiceID: - n += proto.SizeVarint(7<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.ServiceID))) - n += len(x.ServiceID) - case *SelectBy_NodeID: - n += proto.SizeVarint(8<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.NodeID))) - n += len(x.NodeID) - case *SelectBy_Slot: - s := proto.Size(x.Slot) - n += proto.SizeVarint(9<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *SelectBy_DesiredState: - n += proto.SizeVarint(10<<3 | proto.WireVarint) - n += proto.SizeVarint(uint64(x.DesiredState)) - case *SelectBy_Role: - n += proto.SizeVarint(11<<3 | proto.WireVarint) - n += proto.SizeVarint(uint64(x.Role)) - case *SelectBy_Membership: - n += proto.SizeVarint(12<<3 | proto.WireVarint) - n += proto.SizeVarint(uint64(x.Membership)) - case *SelectBy_ReferencedNetworkID: - n += proto.SizeVarint(13<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.ReferencedNetworkID))) - n += len(x.ReferencedNetworkID) - case *SelectBy_ReferencedSecretID: - n += proto.SizeVarint(14<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.ReferencedSecretID))) - n += len(x.ReferencedSecretID) - case *SelectBy_ReferencedConfigID: - n += proto.SizeVarint(16<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.ReferencedConfigID))) - n += len(x.ReferencedConfigID) - case *SelectBy_Kind: - n += proto.SizeVarint(15<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.Kind))) - n += len(x.Kind) - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - type WatchRequest struct { // Multiple entries are combined using OR logic - i.e. if an event // matches all of the selectors specified in any single watch entry, // the event will be sent to the client. - Entries []*WatchRequest_WatchEntry `protobuf:"bytes,1,rep,name=entries" json:"entries,omitempty"` + Entries []*WatchRequest_WatchEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` // ResumeFrom provides an version to resume the watch from, if non-nil. // The watch will return changes since this version, and continue to // return new changes afterwards. Watch will return an error if the // server has compacted its log and no longer has complete history to // this point. - ResumeFrom *Version `protobuf:"bytes,2,opt,name=resume_from,json=resumeFrom" json:"resume_from,omitempty"` + ResumeFrom *Version `protobuf:"bytes,2,opt,name=resume_from,json=resumeFrom,proto3" json:"resume_from,omitempty"` // IncludeOldObject causes WatchMessages to include a copy of the // previous version of the object on updates. Note that only live // changes will include the old object (not historical changes @@ -944,9 +609,37 @@ type WatchRequest struct { IncludeOldObject bool `protobuf:"varint,3,opt,name=include_old_object,json=includeOldObject,proto3" json:"include_old_object,omitempty"` } -func (m *WatchRequest) Reset() { *m = WatchRequest{} } -func (*WatchRequest) ProtoMessage() {} -func (*WatchRequest) Descriptor() ([]byte, []int) { return fileDescriptorWatch, []int{4} } +func (m *WatchRequest) Reset() { *m = WatchRequest{} } +func (*WatchRequest) ProtoMessage() {} +func (*WatchRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_da25266013800cd9, []int{4} +} +func (m *WatchRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WatchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WatchRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WatchRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WatchRequest.Merge(m, src) +} +func (m *WatchRequest) XXX_Size() int { + return m.Size() +} +func (m *WatchRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WatchRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WatchRequest proto.InternalMessageInfo type WatchRequest_WatchEntry struct { // Kind can contain a builtin type such as "node", "secret", etc. or @@ -957,26 +650,82 @@ type WatchRequest_WatchEntry struct { Action WatchActionKind `protobuf:"varint,2,opt,name=action,proto3,enum=docker.swarmkit.v1.WatchActionKind" json:"action,omitempty"` // Filters are combined using AND logic - an event must match // all of them to pass the filter. - Filters []*SelectBy `protobuf:"bytes,3,rep,name=filters" json:"filters,omitempty"` + Filters []*SelectBy `protobuf:"bytes,3,rep,name=filters,proto3" json:"filters,omitempty"` +} + +func (m *WatchRequest_WatchEntry) Reset() { *m = WatchRequest_WatchEntry{} } +func (*WatchRequest_WatchEntry) ProtoMessage() {} +func (*WatchRequest_WatchEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_da25266013800cd9, []int{4, 0} +} +func (m *WatchRequest_WatchEntry) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WatchRequest_WatchEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WatchRequest_WatchEntry.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WatchRequest_WatchEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_WatchRequest_WatchEntry.Merge(m, src) +} +func (m *WatchRequest_WatchEntry) XXX_Size() int { + return m.Size() +} +func (m *WatchRequest_WatchEntry) XXX_DiscardUnknown() { + xxx_messageInfo_WatchRequest_WatchEntry.DiscardUnknown(m) } -func (m *WatchRequest_WatchEntry) Reset() { *m = WatchRequest_WatchEntry{} } -func (*WatchRequest_WatchEntry) ProtoMessage() {} -func (*WatchRequest_WatchEntry) Descriptor() ([]byte, []int) { return fileDescriptorWatch, []int{4, 0} } +var xxx_messageInfo_WatchRequest_WatchEntry proto.InternalMessageInfo // WatchMessage is the type of the stream that's returned to the client by // Watch. Note that the first item of this stream will always be a WatchMessage // with a nil Object, to signal that the stream has started. type WatchMessage struct { - Events []*WatchMessage_Event `protobuf:"bytes,1,rep,name=events" json:"events,omitempty"` + Events []*WatchMessage_Event `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` // Index versions this change to the data store. It can be used to // resume the watch from this point. - Version *Version `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"` + Version *Version `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` +} + +func (m *WatchMessage) Reset() { *m = WatchMessage{} } +func (*WatchMessage) ProtoMessage() {} +func (*WatchMessage) Descriptor() ([]byte, []int) { + return fileDescriptor_da25266013800cd9, []int{5} +} +func (m *WatchMessage) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WatchMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WatchMessage.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WatchMessage) XXX_Merge(src proto.Message) { + xxx_messageInfo_WatchMessage.Merge(m, src) +} +func (m *WatchMessage) XXX_Size() int { + return m.Size() +} +func (m *WatchMessage) XXX_DiscardUnknown() { + xxx_messageInfo_WatchMessage.DiscardUnknown(m) } -func (m *WatchMessage) Reset() { *m = WatchMessage{} } -func (*WatchMessage) ProtoMessage() {} -func (*WatchMessage) Descriptor() ([]byte, []int) { return fileDescriptorWatch, []int{5} } +var xxx_messageInfo_WatchMessage proto.InternalMessageInfo type WatchMessage_Event struct { // Action (create/update/delete) @@ -984,18 +733,47 @@ type WatchMessage_Event struct { // mark transaction boundaries. Action WatchActionKind `protobuf:"varint,1,opt,name=action,proto3,enum=docker.swarmkit.v1.WatchActionKind" json:"action,omitempty"` // Matched object - Object *Object `protobuf:"bytes,2,opt,name=object" json:"object,omitempty"` + Object *Object `protobuf:"bytes,2,opt,name=object,proto3" json:"object,omitempty"` // For updates, OldObject will optionally be included in the // watch message, containing the previous version of the // object, if IncludeOldObject was set in WatchRequest. - OldObject *Object `protobuf:"bytes,3,opt,name=old_object,json=oldObject" json:"old_object,omitempty"` + OldObject *Object `protobuf:"bytes,3,opt,name=old_object,json=oldObject,proto3" json:"old_object,omitempty"` +} + +func (m *WatchMessage_Event) Reset() { *m = WatchMessage_Event{} } +func (*WatchMessage_Event) ProtoMessage() {} +func (*WatchMessage_Event) Descriptor() ([]byte, []int) { + return fileDescriptor_da25266013800cd9, []int{5, 0} +} +func (m *WatchMessage_Event) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WatchMessage_Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WatchMessage_Event.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WatchMessage_Event) XXX_Merge(src proto.Message) { + xxx_messageInfo_WatchMessage_Event.Merge(m, src) +} +func (m *WatchMessage_Event) XXX_Size() int { + return m.Size() +} +func (m *WatchMessage_Event) XXX_DiscardUnknown() { + xxx_messageInfo_WatchMessage_Event.DiscardUnknown(m) } -func (m *WatchMessage_Event) Reset() { *m = WatchMessage_Event{} } -func (*WatchMessage_Event) ProtoMessage() {} -func (*WatchMessage_Event) Descriptor() ([]byte, []int) { return fileDescriptorWatch, []int{5, 0} } +var xxx_messageInfo_WatchMessage_Event proto.InternalMessageInfo func init() { + proto.RegisterEnum("docker.swarmkit.v1.WatchActionKind", WatchActionKind_name, WatchActionKind_value) proto.RegisterType((*Object)(nil), "docker.swarmkit.v1.Object") proto.RegisterType((*SelectBySlot)(nil), "docker.swarmkit.v1.SelectBySlot") proto.RegisterType((*SelectByCustom)(nil), "docker.swarmkit.v1.SelectByCustom") @@ -1004,7 +782,89 @@ func init() { proto.RegisterType((*WatchRequest_WatchEntry)(nil), "docker.swarmkit.v1.WatchRequest.WatchEntry") proto.RegisterType((*WatchMessage)(nil), "docker.swarmkit.v1.WatchMessage") proto.RegisterType((*WatchMessage_Event)(nil), "docker.swarmkit.v1.WatchMessage.Event") - proto.RegisterEnum("docker.swarmkit.v1.WatchActionKind", WatchActionKind_name, WatchActionKind_value) +} + +func init() { + proto.RegisterFile("github.com/docker/swarmkit/api/watch.proto", fileDescriptor_da25266013800cd9) +} + +var fileDescriptor_da25266013800cd9 = []byte{ + // 1199 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x96, 0xbd, 0x73, 0x1b, 0xc5, + 0x1b, 0xc7, 0xef, 0x14, 0xf9, 0x24, 0x3d, 0xb6, 0x13, 0xcf, 0xc6, 0x49, 0xee, 0xa7, 0x5f, 0x90, + 0x85, 0x78, 0xcb, 0x24, 0x41, 0x06, 0x13, 0x92, 0x01, 0x02, 0x33, 0x96, 0x2c, 0x46, 0x22, 0xe3, + 0x97, 0x59, 0xdb, 0x49, 0xa9, 0x39, 0xdf, 0x3d, 0x56, 0x0e, 0xdf, 0xdd, 0x8a, 0xbd, 0x93, 0x1d, + 0x77, 0x14, 0x14, 0x4c, 0x2a, 0x1a, 0x66, 0x68, 0x52, 0x41, 0x4d, 0x43, 0x07, 0xff, 0x40, 0x86, + 0x2a, 0x65, 0x68, 0x3c, 0x44, 0xe9, 0x28, 0xf8, 0x0b, 0x28, 0x98, 0x7d, 0x39, 0xdb, 0x51, 0x4e, + 0x36, 0xa9, 0xb4, 0xb7, 0xf7, 0xf9, 0x3e, 0xfb, 0xec, 0xf3, 0x76, 0x82, 0xab, 0x3d, 0x3f, 0xb9, + 0x3f, 0xd8, 0xaa, 0xbb, 0x2c, 0x9c, 0xf7, 0x98, 0xbb, 0x83, 0x7c, 0x3e, 0xde, 0x73, 0x78, 0xb8, + 0xe3, 0x27, 0xf3, 0x4e, 0xdf, 0x9f, 0xdf, 0x73, 0x12, 0xf7, 0x7e, 0xbd, 0xcf, 0x59, 0xc2, 0x08, + 0x51, 0x40, 0x3d, 0x05, 0xea, 0xbb, 0xef, 0x97, 0x4f, 0xd3, 0xc7, 0x7d, 0x74, 0x63, 0xa5, 0x2f, + 0x5f, 0x3f, 0x85, 0x65, 0x5b, 0x5f, 0xa2, 0x9b, 0xa4, 0xf4, 0x69, 0x96, 0x93, 0xfd, 0x3e, 0xa6, + 0xec, 0x6c, 0x8f, 0xf5, 0x98, 0x5c, 0xce, 0x8b, 0x95, 0xde, 0xbd, 0x75, 0x82, 0x05, 0x49, 0x6c, + 0x0d, 0xb6, 0xe7, 0xfb, 0xc1, 0xa0, 0xe7, 0x47, 0xfa, 0x47, 0x09, 0x6b, 0xdf, 0xe4, 0xc1, 0x5a, + 0x95, 0xce, 0x90, 0x3a, 0xe4, 0x23, 0xe6, 0xa1, 0x6d, 0x56, 0xcd, 0x2b, 0x93, 0x0b, 0x76, 0xfd, + 0xe5, 0x10, 0xd4, 0x57, 0x98, 0x87, 0x6d, 0x83, 0x4a, 0x8e, 0xdc, 0x82, 0x42, 0x8c, 0x7c, 0xd7, + 0x77, 0xd1, 0xce, 0x49, 0xc9, 0xff, 0xb3, 0x24, 0xeb, 0x0a, 0x69, 0x1b, 0x34, 0xa5, 0x85, 0x30, + 0xc2, 0x64, 0x8f, 0xf1, 0x1d, 0xfb, 0xcc, 0x78, 0xe1, 0x8a, 0x42, 0x84, 0x50, 0xd3, 0xc2, 0xc3, + 0xc4, 0x89, 0x77, 0xec, 0xfc, 0x78, 0x0f, 0x37, 0x9c, 0x58, 0x48, 0x24, 0x27, 0x0e, 0x72, 0x83, + 0x41, 0x9c, 0x20, 0xb7, 0x27, 0xc6, 0x1f, 0xd4, 0x54, 0x88, 0x38, 0x48, 0xd3, 0xe4, 0x06, 0x58, + 0x31, 0xba, 0x1c, 0x13, 0xdb, 0x92, 0xba, 0x72, 0xf6, 0xcd, 0x04, 0xd1, 0x36, 0xa8, 0x66, 0xc9, + 0xc7, 0x50, 0xe4, 0x18, 0xb3, 0x01, 0x77, 0xd1, 0x2e, 0x48, 0xdd, 0xe5, 0x2c, 0x1d, 0xd5, 0x4c, + 0xdb, 0xa0, 0x87, 0x3c, 0xf9, 0x14, 0x4a, 0xf8, 0x20, 0xc1, 0x28, 0xf6, 0x59, 0x64, 0x17, 0xa5, + 0xf8, 0xb5, 0x2c, 0x71, 0x2b, 0x85, 0xda, 0x06, 0x3d, 0x52, 0x08, 0x87, 0x5d, 0x16, 0x6d, 0xfb, + 0x3d, 0xbb, 0x34, 0xde, 0xe1, 0xa6, 0x24, 0x84, 0xc3, 0x8a, 0x6d, 0x14, 0xd3, 0xdc, 0xd7, 0xd6, + 0x60, 0x6a, 0x1d, 0x03, 0x74, 0x93, 0xc6, 0xfe, 0x7a, 0xc0, 0x12, 0x72, 0x1d, 0x40, 0x67, 0xab, + 0xeb, 0x7b, 0xb2, 0x22, 0x4a, 0x8d, 0xe9, 0xe1, 0xc1, 0x5c, 0x49, 0xa7, 0xb3, 0xb3, 0x44, 0x4b, + 0x1a, 0xe8, 0x78, 0x84, 0x40, 0x3e, 0x0e, 0x58, 0x22, 0xcb, 0x20, 0x4f, 0xe5, 0xba, 0xb6, 0x06, + 0x67, 0x53, 0x8b, 0xcd, 0x41, 0x9c, 0xb0, 0x50, 0x50, 0x3b, 0x7e, 0xa4, 0xad, 0x51, 0xb9, 0x26, + 0xb3, 0x30, 0xe1, 0x47, 0x1e, 0x3e, 0x90, 0xd2, 0x12, 0x55, 0x0f, 0x62, 0x77, 0xd7, 0x09, 0x06, + 0x28, 0xcb, 0xa3, 0x44, 0xd5, 0x43, 0xed, 0x2f, 0x0b, 0x8a, 0xa9, 0x49, 0x62, 0x43, 0xee, 0xd0, + 0x31, 0x6b, 0x78, 0x30, 0x97, 0xeb, 0x2c, 0xb5, 0x0d, 0x9a, 0xf3, 0x3d, 0x72, 0x0d, 0x4a, 0xbe, + 0xd7, 0xed, 0x73, 0xdc, 0xf6, 0xb5, 0xd9, 0xc6, 0xd4, 0xf0, 0x60, 0xae, 0xd8, 0x59, 0x5a, 0x93, + 0x7b, 0x22, 0xec, 0xbe, 0xa7, 0xd6, 0x64, 0x16, 0xf2, 0x91, 0x13, 0xea, 0x83, 0x64, 0x65, 0x3b, + 0x21, 0x92, 0xd7, 0x61, 0x52, 0xfc, 0xa6, 0x46, 0xf2, 0xfa, 0x25, 0x88, 0x4d, 0x2d, 0xbc, 0x0d, + 0x96, 0x2b, 0xaf, 0xa5, 0x2b, 0xab, 0x96, 0x5d, 0x21, 0xc7, 0x03, 0x20, 0x03, 0xaf, 0x42, 0xd1, + 0x81, 0x69, 0xb5, 0x4a, 0x8f, 0xb0, 0x5e, 0xc1, 0xc8, 0x94, 0x92, 0x6a, 0x47, 0xea, 0x2f, 0x64, + 0xaa, 0x90, 0x91, 0x29, 0x51, 0x29, 0x47, 0xb9, 0x7a, 0x0b, 0x0a, 0xa2, 0x7b, 0x05, 0x5c, 0x94, + 0x30, 0x0c, 0x0f, 0xe6, 0x2c, 0xd1, 0xd8, 0x92, 0xb4, 0xc4, 0xcb, 0x8e, 0x47, 0x6e, 0xea, 0x94, + 0xaa, 0x72, 0xaa, 0x9e, 0xe4, 0x98, 0x28, 0x18, 0x11, 0x3a, 0xc1, 0x93, 0x25, 0x98, 0xf6, 0x30, + 0xf6, 0x39, 0x7a, 0xdd, 0x38, 0x71, 0x12, 0xb4, 0xa1, 0x6a, 0x5e, 0x39, 0x9b, 0x5d, 0xcb, 0xa2, + 0x57, 0xd7, 0x05, 0x24, 0x2e, 0xa5, 0x55, 0xf2, 0x99, 0x2c, 0x40, 0x9e, 0xb3, 0x00, 0xed, 0x49, + 0x29, 0xbe, 0x3c, 0x6e, 0x14, 0x51, 0x16, 0xc8, 0x71, 0x24, 0x58, 0xd2, 0x01, 0x08, 0x31, 0xdc, + 0x42, 0x1e, 0xdf, 0xf7, 0xfb, 0xf6, 0x94, 0x54, 0xbe, 0x33, 0x4e, 0xb9, 0xde, 0x47, 0xb7, 0xbe, + 0x7c, 0x88, 0x8b, 0xe4, 0x1e, 0x89, 0xc9, 0x32, 0x5c, 0xe0, 0xb8, 0x8d, 0x1c, 0x23, 0x17, 0xbd, + 0xae, 0x9e, 0x3e, 0x22, 0x62, 0xd3, 0x32, 0x62, 0x97, 0x86, 0x07, 0x73, 0xe7, 0xe9, 0x21, 0xa0, + 0x07, 0x95, 0x0c, 0xdf, 0x79, 0xfe, 0xd2, 0xb6, 0x47, 0xbe, 0x80, 0xd9, 0x63, 0xe6, 0xd4, 0xb0, + 0x10, 0xd6, 0xce, 0x4a, 0x6b, 0x17, 0x87, 0x07, 0x73, 0xe4, 0xc8, 0x9a, 0x9a, 0x2a, 0xd2, 0x18, + 0xe1, 0xa3, 0xbb, 0xa3, 0xb6, 0x54, 0x1f, 0x0b, 0x5b, 0x33, 0x59, 0xb6, 0x54, 0xc3, 0x8f, 0xda, + 0xd2, 0xbb, 0xa2, 0xf9, 0x54, 0x43, 0x9e, 0x4b, 0x8b, 0x5f, 0x3c, 0x35, 0xf2, 0x90, 0x6b, 0xec, + 0xd7, 0xfe, 0xc8, 0xc1, 0xd4, 0x3d, 0xf1, 0x41, 0xa4, 0xf8, 0xd5, 0x00, 0xe3, 0x84, 0xb4, 0xa0, + 0x80, 0x51, 0xc2, 0x7d, 0x8c, 0x6d, 0xb3, 0x7a, 0xe6, 0xca, 0xe4, 0xc2, 0xb5, 0xac, 0xd8, 0x1e, + 0x97, 0xa8, 0x87, 0x56, 0x94, 0xf0, 0x7d, 0x9a, 0x6a, 0xc9, 0x6d, 0x98, 0xe4, 0x18, 0x0f, 0x42, + 0xec, 0x6e, 0x73, 0x16, 0x9e, 0xf4, 0xe1, 0xb8, 0x8b, 0x5c, 0x8c, 0x36, 0x0a, 0x8a, 0xff, 0x9c, + 0xb3, 0x90, 0x5c, 0x07, 0xe2, 0x47, 0x6e, 0x30, 0xf0, 0xb0, 0xcb, 0x02, 0xaf, 0xab, 0xbe, 0xa2, + 0xb2, 0x79, 0x8b, 0x74, 0x46, 0xbf, 0x59, 0x0d, 0x3c, 0x35, 0xd4, 0xca, 0xdf, 0x9b, 0x00, 0x47, + 0x3e, 0x64, 0xce, 0x9f, 0x4f, 0xc0, 0x72, 0xdc, 0x44, 0xcc, 0xdc, 0x9c, 0x2c, 0x98, 0x37, 0xc6, + 0x5e, 0x6a, 0x51, 0x62, 0x77, 0xfc, 0xc8, 0xa3, 0x5a, 0x42, 0x6e, 0x42, 0x61, 0xdb, 0x0f, 0x12, + 0xe4, 0xb1, 0x7d, 0x46, 0x86, 0xe4, 0xf2, 0x49, 0x6d, 0x42, 0x53, 0xb8, 0xf6, 0x5b, 0x1a, 0xdb, + 0x65, 0x8c, 0x63, 0xa7, 0x87, 0xe4, 0x33, 0xb0, 0x70, 0x17, 0xa3, 0x24, 0x0d, 0xed, 0xdb, 0x63, + 0xbd, 0xd0, 0x8a, 0x7a, 0x4b, 0xe0, 0x54, 0xab, 0xc8, 0x87, 0x50, 0xd8, 0x55, 0xd1, 0xfa, 0x2f, + 0x01, 0x4d, 0xd9, 0xf2, 0x2f, 0x26, 0x4c, 0x48, 0x43, 0xc7, 0xc2, 0x60, 0xbe, 0x7a, 0x18, 0x16, + 0xc0, 0xd2, 0x89, 0xc8, 0x8d, 0xff, 0xf6, 0xa8, 0x94, 0x50, 0x4d, 0x92, 0x8f, 0x00, 0x46, 0x12, + 0x78, 0xb2, 0xae, 0xc4, 0xd2, 0xac, 0x5e, 0xfd, 0xc7, 0x84, 0x73, 0x23, 0xae, 0x90, 0x1b, 0x30, + 0x7b, 0x6f, 0x71, 0xa3, 0xd9, 0xee, 0x2e, 0x36, 0x37, 0x3a, 0xab, 0x2b, 0xdd, 0xcd, 0x95, 0x3b, + 0x2b, 0xab, 0xf7, 0x56, 0x66, 0x8c, 0x72, 0xf9, 0xe1, 0xa3, 0xea, 0xc5, 0x11, 0x7c, 0x33, 0xda, + 0x89, 0xd8, 0x9e, 0x70, 0xfc, 0xfc, 0x0b, 0xaa, 0x26, 0x6d, 0x2d, 0x6e, 0xb4, 0x66, 0xcc, 0xf2, + 0xff, 0x1e, 0x3e, 0xaa, 0x5e, 0x18, 0x11, 0x35, 0x39, 0xaa, 0xc9, 0xf4, 0xa2, 0x66, 0x73, 0x6d, + 0x49, 0x68, 0x72, 0x99, 0x9a, 0xcd, 0xbe, 0x97, 0xa5, 0xa1, 0xad, 0xe5, 0xd5, 0xbb, 0xad, 0x99, + 0x7c, 0xa6, 0x86, 0x62, 0xc8, 0x76, 0xb1, 0x7c, 0xe9, 0xdb, 0x1f, 0x2b, 0xc6, 0xaf, 0x3f, 0x55, + 0x46, 0xaf, 0xba, 0x10, 0xc2, 0x84, 0xdc, 0x22, 0x5e, 0xba, 0xa8, 0x9e, 0xd6, 0x88, 0xe5, 0xea, + 0x69, 0xf5, 0x54, 0xbb, 0xf0, 0xfb, 0xcf, 0x7f, 0xff, 0x90, 0x3b, 0x07, 0xd3, 0x92, 0x78, 0x37, + 0x74, 0x22, 0xa7, 0x87, 0xfc, 0x3d, 0xb3, 0xf1, 0xe6, 0xe3, 0x67, 0x15, 0xe3, 0xe9, 0xb3, 0x8a, + 0xf1, 0xf5, 0xb0, 0x62, 0x3e, 0x1e, 0x56, 0xcc, 0x27, 0xc3, 0x8a, 0xf9, 0xe7, 0xb0, 0x62, 0x7e, + 0xf7, 0xbc, 0x62, 0x3c, 0x79, 0x5e, 0x31, 0x9e, 0x3e, 0xaf, 0x18, 0x5b, 0x96, 0xfc, 0x33, 0xf9, + 0xc1, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x96, 0x4e, 0x58, 0x61, 0x63, 0x0b, 0x00, 0x00, } type authenticatedWrapperWatchServer struct { @@ -1046,55 +906,55 @@ func (m *Object) CopyFrom(src interface{}) { v := Object_Node{ Node: &Node{}, } - deepcopy.Copy(v.Node, o.GetNode()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Node, o.GetNode()) m.Object = &v case *Object_Service: v := Object_Service{ Service: &Service{}, } - deepcopy.Copy(v.Service, o.GetService()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Service, o.GetService()) m.Object = &v case *Object_Network: v := Object_Network{ Network: &Network{}, } - deepcopy.Copy(v.Network, o.GetNetwork()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Network, o.GetNetwork()) m.Object = &v case *Object_Task: v := Object_Task{ Task: &Task{}, } - deepcopy.Copy(v.Task, o.GetTask()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Task, o.GetTask()) m.Object = &v case *Object_Cluster: v := Object_Cluster{ Cluster: &Cluster{}, } - deepcopy.Copy(v.Cluster, o.GetCluster()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Cluster, o.GetCluster()) m.Object = &v case *Object_Secret: v := Object_Secret{ Secret: &Secret{}, } - deepcopy.Copy(v.Secret, o.GetSecret()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Secret, o.GetSecret()) m.Object = &v case *Object_Resource: v := Object_Resource{ Resource: &Resource{}, } - deepcopy.Copy(v.Resource, o.GetResource()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Resource, o.GetResource()) m.Object = &v case *Object_Extension: v := Object_Extension{ Extension: &Extension{}, } - deepcopy.Copy(v.Extension, o.GetExtension()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Extension, o.GetExtension()) m.Object = &v case *Object_Config: v := Object_Config{ Config: &Config{}, } - deepcopy.Copy(v.Config, o.GetConfig()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Config, o.GetConfig()) m.Object = &v } } @@ -1170,13 +1030,13 @@ func (m *SelectBy) CopyFrom(src interface{}) { v := SelectBy_Custom{ Custom: &SelectByCustom{}, } - deepcopy.Copy(v.Custom, o.GetCustom()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Custom, o.GetCustom()) m.By = &v case *SelectBy_CustomPrefix: v := SelectBy_CustomPrefix{ CustomPrefix: &SelectByCustom{}, } - deepcopy.Copy(v.CustomPrefix, o.GetCustomPrefix()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.CustomPrefix, o.GetCustomPrefix()) m.By = &v case *SelectBy_ServiceID: v := SelectBy_ServiceID{ @@ -1192,7 +1052,7 @@ func (m *SelectBy) CopyFrom(src interface{}) { v := SelectBy_Slot{ Slot: &SelectBySlot{}, } - deepcopy.Copy(v.Slot, o.GetSlot()) + github_com_docker_swarmkit_api_deepcopy.Copy(v.Slot, o.GetSlot()) m.By = &v case *SelectBy_DesiredState: v := SelectBy_DesiredState{ @@ -1251,13 +1111,13 @@ func (m *WatchRequest) CopyFrom(src interface{}) { m.Entries = make([]*WatchRequest_WatchEntry, len(o.Entries)) for i := range m.Entries { m.Entries[i] = &WatchRequest_WatchEntry{} - deepcopy.Copy(m.Entries[i], o.Entries[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Entries[i], o.Entries[i]) } } if o.ResumeFrom != nil { m.ResumeFrom = &Version{} - deepcopy.Copy(m.ResumeFrom, o.ResumeFrom) + github_com_docker_swarmkit_api_deepcopy.Copy(m.ResumeFrom, o.ResumeFrom) } } @@ -1278,7 +1138,7 @@ func (m *WatchRequest_WatchEntry) CopyFrom(src interface{}) { m.Filters = make([]*SelectBy, len(o.Filters)) for i := range m.Filters { m.Filters[i] = &SelectBy{} - deepcopy.Copy(m.Filters[i], o.Filters[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Filters[i], o.Filters[i]) } } @@ -1301,13 +1161,13 @@ func (m *WatchMessage) CopyFrom(src interface{}) { m.Events = make([]*WatchMessage_Event, len(o.Events)) for i := range m.Events { m.Events[i] = &WatchMessage_Event{} - deepcopy.Copy(m.Events[i], o.Events[i]) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Events[i], o.Events[i]) } } if o.Version != nil { m.Version = &Version{} - deepcopy.Copy(m.Version, o.Version) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Version, o.Version) } } @@ -1326,11 +1186,11 @@ func (m *WatchMessage_Event) CopyFrom(src interface{}) { *m = *o if o.Object != nil { m.Object = &Object{} - deepcopy.Copy(m.Object, o.Object) + github_com_docker_swarmkit_api_deepcopy.Copy(m.Object, o.Object) } if o.OldObject != nil { m.OldObject = &Object{} - deepcopy.Copy(m.OldObject, o.OldObject) + github_com_docker_swarmkit_api_deepcopy.Copy(m.OldObject, o.OldObject) } } @@ -1342,8 +1202,9 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for Watch service - +// WatchClient is the client API for Watch service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type WatchClient interface { // Watch starts a stream that returns any changes to objects that match // the specified selectors. When the stream begins, it immediately sends @@ -1362,7 +1223,7 @@ func NewWatchClient(cc *grpc.ClientConn) WatchClient { } func (c *watchClient) Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Watch_WatchClient, error) { - stream, err := grpc.NewClientStream(ctx, &_Watch_serviceDesc.Streams[0], c.cc, "/docker.swarmkit.v1.Watch/Watch", opts...) + stream, err := c.cc.NewStream(ctx, &_Watch_serviceDesc.Streams[0], "/docker.swarmkit.v1.Watch/Watch", opts...) if err != nil { return nil, err } @@ -1393,8 +1254,7 @@ func (x *watchWatchClient) Recv() (*WatchMessage, error) { return m, nil } -// Server API for Watch service - +// WatchServer is the server API for Watch service. type WatchServer interface { // Watch starts a stream that returns any changes to objects that match // the specified selectors. When the stream begins, it immediately sends @@ -1404,6 +1264,14 @@ type WatchServer interface { Watch(*WatchRequest, Watch_WatchServer) error } +// UnimplementedWatchServer can be embedded to have forward compatible implementations. +type UnimplementedWatchServer struct { +} + +func (*UnimplementedWatchServer) Watch(req *WatchRequest, srv Watch_WatchServer) error { + return status.Errorf(codes.Unimplemented, "method Watch not implemented") +} + func RegisterWatchServer(s *grpc.Server, srv WatchServer) { s.RegisterService(&_Watch_serviceDesc, srv) } @@ -1446,7 +1314,7 @@ var _Watch_serviceDesc = grpc.ServiceDesc{ func (m *Object) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1454,150 +1322,220 @@ func (m *Object) Marshal() (dAtA []byte, err error) { } func (m *Object) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Object) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.Object != nil { - nn1, err := m.Object.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size := m.Object.Size() + i -= size + if _, err := m.Object.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += nn1 } - return i, nil + return len(dAtA) - i, nil } func (m *Object_Node) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Object_Node) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Node != nil { - dAtA[i] = 0xa - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Node.Size())) - n2, err := m.Node.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Node.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n2 + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Object_Service) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Object_Service) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Service != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Service.Size())) - n3, err := m.Service.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Service.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n3 + i-- + dAtA[i] = 0x12 } - return i, nil + return len(dAtA) - i, nil } func (m *Object_Network) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Object_Network) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Network != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Network.Size())) - n4, err := m.Network.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Network.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n4 + i-- + dAtA[i] = 0x1a } - return i, nil + return len(dAtA) - i, nil } func (m *Object_Task) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Object_Task) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Task != nil { - dAtA[i] = 0x22 - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Task.Size())) - n5, err := m.Task.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Task.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n5 + i-- + dAtA[i] = 0x22 } - return i, nil + return len(dAtA) - i, nil } func (m *Object_Cluster) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Object_Cluster) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Cluster != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Cluster.Size())) - n6, err := m.Cluster.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Cluster.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n6 + i-- + dAtA[i] = 0x2a } - return i, nil + return len(dAtA) - i, nil } func (m *Object_Secret) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Object_Secret) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Secret != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Secret.Size())) - n7, err := m.Secret.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Secret.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n7 + i-- + dAtA[i] = 0x32 } - return i, nil + return len(dAtA) - i, nil } func (m *Object_Resource) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Object_Resource) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Resource != nil { - dAtA[i] = 0x3a - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Resource.Size())) - n8, err := m.Resource.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n8 + i-- + dAtA[i] = 0x3a } - return i, nil + return len(dAtA) - i, nil } func (m *Object_Extension) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Object_Extension) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Extension != nil { - dAtA[i] = 0x42 - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Extension.Size())) - n9, err := m.Extension.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Extension.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n9 + i-- + dAtA[i] = 0x42 } - return i, nil + return len(dAtA) - i, nil } func (m *Object_Config) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Object_Config) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Config != nil { - dAtA[i] = 0x4a - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Config.Size())) - n10, err := m.Config.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Config.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n10 + i-- + dAtA[i] = 0x4a } - return i, nil + return len(dAtA) - i, nil } func (m *SelectBySlot) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1605,28 +1543,34 @@ func (m *SelectBySlot) Marshal() (dAtA []byte, err error) { } func (m *SelectBySlot) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBySlot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ServiceID) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintWatch(dAtA, i, uint64(len(m.ServiceID))) - i += copy(dAtA[i:], m.ServiceID) - } if m.Slot != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintWatch(dAtA, i, uint64(m.Slot)) + i-- + dAtA[i] = 0x10 + } + if len(m.ServiceID) > 0 { + i -= len(m.ServiceID) + copy(dAtA[i:], m.ServiceID) + i = encodeVarintWatch(dAtA, i, uint64(len(m.ServiceID))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *SelectByCustom) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1634,35 +1578,43 @@ func (m *SelectByCustom) Marshal() (dAtA []byte, err error) { } func (m *SelectByCustom) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectByCustom) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Kind) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintWatch(dAtA, i, uint64(len(m.Kind))) - i += copy(dAtA[i:], m.Kind) + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintWatch(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x1a } if len(m.Index) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Index) + copy(dAtA[i:], m.Index) i = encodeVarintWatch(dAtA, i, uint64(len(m.Index))) - i += copy(dAtA[i:], m.Index) + i-- + dAtA[i] = 0x12 } - if len(m.Value) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintWatch(dAtA, i, uint64(len(m.Value))) - i += copy(dAtA[i:], m.Value) + if len(m.Kind) > 0 { + i -= len(m.Kind) + copy(dAtA[i:], m.Kind) + i = encodeVarintWatch(dAtA, i, uint64(len(m.Kind))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *SelectBy) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1670,169 +1622,272 @@ func (m *SelectBy) Marshal() (dAtA []byte, err error) { } func (m *SelectBy) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.By != nil { - nn11, err := m.By.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size := m.By.Size() + i -= size + if _, err := m.By.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += nn11 } - return i, nil + return len(dAtA) - i, nil } func (m *SelectBy_ID) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0xa - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_ID) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.ID) + copy(dAtA[i:], m.ID) i = encodeVarintWatch(dAtA, i, uint64(len(m.ID))) - i += copy(dAtA[i:], m.ID) - return i, nil + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil } func (m *SelectBy_IDPrefix) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x12 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_IDPrefix) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.IDPrefix) + copy(dAtA[i:], m.IDPrefix) i = encodeVarintWatch(dAtA, i, uint64(len(m.IDPrefix))) - i += copy(dAtA[i:], m.IDPrefix) - return i, nil + i-- + dAtA[i] = 0x12 + return len(dAtA) - i, nil } func (m *SelectBy_Name) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x1a - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_Name) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.Name) + copy(dAtA[i:], m.Name) i = encodeVarintWatch(dAtA, i, uint64(len(m.Name))) - i += copy(dAtA[i:], m.Name) - return i, nil + i-- + dAtA[i] = 0x1a + return len(dAtA) - i, nil } func (m *SelectBy_NamePrefix) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x22 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_NamePrefix) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.NamePrefix) + copy(dAtA[i:], m.NamePrefix) i = encodeVarintWatch(dAtA, i, uint64(len(m.NamePrefix))) - i += copy(dAtA[i:], m.NamePrefix) - return i, nil + i-- + dAtA[i] = 0x22 + return len(dAtA) - i, nil } func (m *SelectBy_Custom) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_Custom) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Custom != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Custom.Size())) - n12, err := m.Custom.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Custom.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n12 + i-- + dAtA[i] = 0x2a } - return i, nil + return len(dAtA) - i, nil } func (m *SelectBy_CustomPrefix) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_CustomPrefix) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.CustomPrefix != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.CustomPrefix.Size())) - n13, err := m.CustomPrefix.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.CustomPrefix.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n13 + i-- + dAtA[i] = 0x32 } - return i, nil + return len(dAtA) - i, nil } func (m *SelectBy_ServiceID) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x3a - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_ServiceID) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.ServiceID) + copy(dAtA[i:], m.ServiceID) i = encodeVarintWatch(dAtA, i, uint64(len(m.ServiceID))) - i += copy(dAtA[i:], m.ServiceID) - return i, nil + i-- + dAtA[i] = 0x3a + return len(dAtA) - i, nil } func (m *SelectBy_NodeID) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x42 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_NodeID) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.NodeID) + copy(dAtA[i:], m.NodeID) i = encodeVarintWatch(dAtA, i, uint64(len(m.NodeID))) - i += copy(dAtA[i:], m.NodeID) - return i, nil + i-- + dAtA[i] = 0x42 + return len(dAtA) - i, nil } func (m *SelectBy_Slot) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_Slot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.Slot != nil { - dAtA[i] = 0x4a - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Slot.Size())) - n14, err := m.Slot.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Slot.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n14 + i-- + dAtA[i] = 0x4a } - return i, nil + return len(dAtA) - i, nil } func (m *SelectBy_DesiredState) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x50 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_DesiredState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) i = encodeVarintWatch(dAtA, i, uint64(m.DesiredState)) - return i, nil + i-- + dAtA[i] = 0x50 + return len(dAtA) - i, nil } func (m *SelectBy_Role) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x58 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_Role) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) i = encodeVarintWatch(dAtA, i, uint64(m.Role)) - return i, nil + i-- + dAtA[i] = 0x58 + return len(dAtA) - i, nil } func (m *SelectBy_Membership) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x60 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_Membership) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) i = encodeVarintWatch(dAtA, i, uint64(m.Membership)) - return i, nil + i-- + dAtA[i] = 0x60 + return len(dAtA) - i, nil } func (m *SelectBy_ReferencedNetworkID) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x6a - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_ReferencedNetworkID) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.ReferencedNetworkID) + copy(dAtA[i:], m.ReferencedNetworkID) i = encodeVarintWatch(dAtA, i, uint64(len(m.ReferencedNetworkID))) - i += copy(dAtA[i:], m.ReferencedNetworkID) - return i, nil + i-- + dAtA[i] = 0x6a + return len(dAtA) - i, nil } func (m *SelectBy_ReferencedSecretID) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x72 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_ReferencedSecretID) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.ReferencedSecretID) + copy(dAtA[i:], m.ReferencedSecretID) i = encodeVarintWatch(dAtA, i, uint64(len(m.ReferencedSecretID))) - i += copy(dAtA[i:], m.ReferencedSecretID) - return i, nil + i-- + dAtA[i] = 0x72 + return len(dAtA) - i, nil } func (m *SelectBy_Kind) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x7a - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_Kind) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.Kind) + copy(dAtA[i:], m.Kind) i = encodeVarintWatch(dAtA, i, uint64(len(m.Kind))) - i += copy(dAtA[i:], m.Kind) - return i, nil + i-- + dAtA[i] = 0x7a + return len(dAtA) - i, nil } func (m *SelectBy_ReferencedConfigID) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x82 - i++ - dAtA[i] = 0x1 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SelectBy_ReferencedConfigID) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.ReferencedConfigID) + copy(dAtA[i:], m.ReferencedConfigID) i = encodeVarintWatch(dAtA, i, uint64(len(m.ReferencedConfigID))) - i += copy(dAtA[i:], m.ReferencedConfigID) - return i, nil + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x82 + return len(dAtA) - i, nil } func (m *WatchRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1840,49 +1895,58 @@ func (m *WatchRequest) Marshal() (dAtA []byte, err error) { } func (m *WatchRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WatchRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Entries) > 0 { - for _, msg := range m.Entries { - dAtA[i] = 0xa - i++ - i = encodeVarintWatch(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.IncludeOldObject { + i-- + if m.IncludeOldObject { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.ResumeFrom != nil { + { + size, err := m.ResumeFrom.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - } - if m.ResumeFrom != nil { + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.ResumeFrom.Size())) - n15, err := m.ResumeFrom.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n15 } - if m.IncludeOldObject { - dAtA[i] = 0x18 - i++ - if m.IncludeOldObject { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + if len(m.Entries) > 0 { + for iNdEx := len(m.Entries) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Entries[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - i++ } - return i, nil + return len(dAtA) - i, nil } func (m *WatchRequest_WatchEntry) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1890,40 +1954,48 @@ func (m *WatchRequest_WatchEntry) Marshal() (dAtA []byte, err error) { } func (m *WatchRequest_WatchEntry) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WatchRequest_WatchEntry) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Kind) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintWatch(dAtA, i, uint64(len(m.Kind))) - i += copy(dAtA[i:], m.Kind) + if len(m.Filters) > 0 { + for iNdEx := len(m.Filters) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Filters[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } } if m.Action != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintWatch(dAtA, i, uint64(m.Action)) + i-- + dAtA[i] = 0x10 } - if len(m.Filters) > 0 { - for _, msg := range m.Filters { - dAtA[i] = 0x1a - i++ - i = encodeVarintWatch(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n - } + if len(m.Kind) > 0 { + i -= len(m.Kind) + copy(dAtA[i:], m.Kind) + i = encodeVarintWatch(dAtA, i, uint64(len(m.Kind))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *WatchMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1931,39 +2003,48 @@ func (m *WatchMessage) Marshal() (dAtA []byte, err error) { } func (m *WatchMessage) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WatchMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Events) > 0 { - for _, msg := range m.Events { - dAtA[i] = 0xa - i++ - i = encodeVarintWatch(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) + if m.Version != nil { + { + size, err := m.Version.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } - i += n + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - } - if m.Version != nil { + i-- dAtA[i] = 0x12 - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Version.Size())) - n16, err := m.Version.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + } + if len(m.Events) > 0 { + for iNdEx := len(m.Events) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Events[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - i += n16 } - return i, nil + return len(dAtA) - i, nil } func (m *WatchMessage_Event) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1971,46 +2052,57 @@ func (m *WatchMessage_Event) Marshal() (dAtA []byte, err error) { } func (m *WatchMessage_Event) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WatchMessage_Event) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Action != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Action)) + if m.OldObject != nil { + { + size, err := m.OldObject.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a } if m.Object != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.Object.Size())) - n17, err := m.Object.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.Object.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintWatch(dAtA, i, uint64(size)) } - i += n17 + i-- + dAtA[i] = 0x12 } - if m.OldObject != nil { - dAtA[i] = 0x1a - i++ - i = encodeVarintWatch(dAtA, i, uint64(m.OldObject.Size())) - n18, err := m.OldObject.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err - } - i += n18 + if m.Action != 0 { + i = encodeVarintWatch(dAtA, i, uint64(m.Action)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func encodeVarintWatch(dAtA []byte, offset int, v uint64) int { + offset -= sovWatch(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } type raftProxyWatchServer struct { @@ -2137,6 +2229,9 @@ func (p *raftProxyWatchServer) Watch(r *WatchRequest, stream Watch_WatchServer) } func (m *Object) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Object != nil { @@ -2146,6 +2241,9 @@ func (m *Object) Size() (n int) { } func (m *Object_Node) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Node != nil { @@ -2155,6 +2253,9 @@ func (m *Object_Node) Size() (n int) { return n } func (m *Object_Service) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Service != nil { @@ -2164,6 +2265,9 @@ func (m *Object_Service) Size() (n int) { return n } func (m *Object_Network) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Network != nil { @@ -2173,6 +2277,9 @@ func (m *Object_Network) Size() (n int) { return n } func (m *Object_Task) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Task != nil { @@ -2182,6 +2289,9 @@ func (m *Object_Task) Size() (n int) { return n } func (m *Object_Cluster) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Cluster != nil { @@ -2191,6 +2301,9 @@ func (m *Object_Cluster) Size() (n int) { return n } func (m *Object_Secret) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Secret != nil { @@ -2200,6 +2313,9 @@ func (m *Object_Secret) Size() (n int) { return n } func (m *Object_Resource) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Resource != nil { @@ -2209,6 +2325,9 @@ func (m *Object_Resource) Size() (n int) { return n } func (m *Object_Extension) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Extension != nil { @@ -2218,6 +2337,9 @@ func (m *Object_Extension) Size() (n int) { return n } func (m *Object_Config) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Config != nil { @@ -2227,6 +2349,9 @@ func (m *Object_Config) Size() (n int) { return n } func (m *SelectBySlot) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ServiceID) @@ -2240,6 +2365,9 @@ func (m *SelectBySlot) Size() (n int) { } func (m *SelectByCustom) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Kind) @@ -2258,6 +2386,9 @@ func (m *SelectByCustom) Size() (n int) { } func (m *SelectBy) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.By != nil { @@ -2267,6 +2398,9 @@ func (m *SelectBy) Size() (n int) { } func (m *SelectBy_ID) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ID) @@ -2274,6 +2408,9 @@ func (m *SelectBy_ID) Size() (n int) { return n } func (m *SelectBy_IDPrefix) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.IDPrefix) @@ -2281,6 +2418,9 @@ func (m *SelectBy_IDPrefix) Size() (n int) { return n } func (m *SelectBy_Name) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Name) @@ -2288,6 +2428,9 @@ func (m *SelectBy_Name) Size() (n int) { return n } func (m *SelectBy_NamePrefix) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.NamePrefix) @@ -2295,6 +2438,9 @@ func (m *SelectBy_NamePrefix) Size() (n int) { return n } func (m *SelectBy_Custom) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Custom != nil { @@ -2304,6 +2450,9 @@ func (m *SelectBy_Custom) Size() (n int) { return n } func (m *SelectBy_CustomPrefix) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.CustomPrefix != nil { @@ -2313,6 +2462,9 @@ func (m *SelectBy_CustomPrefix) Size() (n int) { return n } func (m *SelectBy_ServiceID) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ServiceID) @@ -2320,6 +2472,9 @@ func (m *SelectBy_ServiceID) Size() (n int) { return n } func (m *SelectBy_NodeID) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.NodeID) @@ -2327,6 +2482,9 @@ func (m *SelectBy_NodeID) Size() (n int) { return n } func (m *SelectBy_Slot) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Slot != nil { @@ -2336,24 +2494,36 @@ func (m *SelectBy_Slot) Size() (n int) { return n } func (m *SelectBy_DesiredState) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovWatch(uint64(m.DesiredState)) return n } func (m *SelectBy_Role) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovWatch(uint64(m.Role)) return n } func (m *SelectBy_Membership) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovWatch(uint64(m.Membership)) return n } func (m *SelectBy_ReferencedNetworkID) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ReferencedNetworkID) @@ -2361,6 +2531,9 @@ func (m *SelectBy_ReferencedNetworkID) Size() (n int) { return n } func (m *SelectBy_ReferencedSecretID) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ReferencedSecretID) @@ -2368,6 +2541,9 @@ func (m *SelectBy_ReferencedSecretID) Size() (n int) { return n } func (m *SelectBy_Kind) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Kind) @@ -2375,6 +2551,9 @@ func (m *SelectBy_Kind) Size() (n int) { return n } func (m *SelectBy_ReferencedConfigID) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ReferencedConfigID) @@ -2382,6 +2561,9 @@ func (m *SelectBy_ReferencedConfigID) Size() (n int) { return n } func (m *WatchRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Entries) > 0 { @@ -2401,6 +2583,9 @@ func (m *WatchRequest) Size() (n int) { } func (m *WatchRequest_WatchEntry) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Kind) @@ -2420,6 +2605,9 @@ func (m *WatchRequest_WatchEntry) Size() (n int) { } func (m *WatchMessage) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Events) > 0 { @@ -2436,6 +2624,9 @@ func (m *WatchMessage) Size() (n int) { } func (m *WatchMessage_Event) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Action != 0 { @@ -2453,14 +2644,7 @@ func (m *WatchMessage_Event) Size() (n int) { } func sovWatch(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozWatch(x uint64) (n int) { return sovWatch(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -2762,8 +2946,13 @@ func (this *WatchRequest) String() string { if this == nil { return "nil" } + repeatedStringForEntries := "[]*WatchRequest_WatchEntry{" + for _, f := range this.Entries { + repeatedStringForEntries += strings.Replace(fmt.Sprintf("%v", f), "WatchRequest_WatchEntry", "WatchRequest_WatchEntry", 1) + "," + } + repeatedStringForEntries += "}" s := strings.Join([]string{`&WatchRequest{`, - `Entries:` + strings.Replace(fmt.Sprintf("%v", this.Entries), "WatchRequest_WatchEntry", "WatchRequest_WatchEntry", 1) + `,`, + `Entries:` + repeatedStringForEntries + `,`, `ResumeFrom:` + strings.Replace(fmt.Sprintf("%v", this.ResumeFrom), "Version", "Version", 1) + `,`, `IncludeOldObject:` + fmt.Sprintf("%v", this.IncludeOldObject) + `,`, `}`, @@ -2774,10 +2963,15 @@ func (this *WatchRequest_WatchEntry) String() string { if this == nil { return "nil" } + repeatedStringForFilters := "[]*SelectBy{" + for _, f := range this.Filters { + repeatedStringForFilters += strings.Replace(f.String(), "SelectBy", "SelectBy", 1) + "," + } + repeatedStringForFilters += "}" s := strings.Join([]string{`&WatchRequest_WatchEntry{`, `Kind:` + fmt.Sprintf("%v", this.Kind) + `,`, `Action:` + fmt.Sprintf("%v", this.Action) + `,`, - `Filters:` + strings.Replace(fmt.Sprintf("%v", this.Filters), "SelectBy", "SelectBy", 1) + `,`, + `Filters:` + repeatedStringForFilters + `,`, `}`, }, "") return s @@ -2786,8 +2980,13 @@ func (this *WatchMessage) String() string { if this == nil { return "nil" } + repeatedStringForEvents := "[]*WatchMessage_Event{" + for _, f := range this.Events { + repeatedStringForEvents += strings.Replace(fmt.Sprintf("%v", f), "WatchMessage_Event", "WatchMessage_Event", 1) + "," + } + repeatedStringForEvents += "}" s := strings.Join([]string{`&WatchMessage{`, - `Events:` + strings.Replace(fmt.Sprintf("%v", this.Events), "WatchMessage_Event", "WatchMessage_Event", 1) + `,`, + `Events:` + repeatedStringForEvents + `,`, `Version:` + strings.Replace(fmt.Sprintf("%v", this.Version), "Version", "Version", 1) + `,`, `}`, }, "") @@ -2799,8 +2998,8 @@ func (this *WatchMessage_Event) String() string { } s := strings.Join([]string{`&WatchMessage_Event{`, `Action:` + fmt.Sprintf("%v", this.Action) + `,`, - `Object:` + strings.Replace(fmt.Sprintf("%v", this.Object), "Object", "Object", 1) + `,`, - `OldObject:` + strings.Replace(fmt.Sprintf("%v", this.OldObject), "Object", "Object", 1) + `,`, + `Object:` + strings.Replace(this.Object.String(), "Object", "Object", 1) + `,`, + `OldObject:` + strings.Replace(this.OldObject.String(), "Object", "Object", 1) + `,`, `}`, }, "") return s @@ -2828,7 +3027,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2856,7 +3055,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2865,6 +3064,9 @@ func (m *Object) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2888,7 +3090,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2897,6 +3099,9 @@ func (m *Object) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2920,7 +3125,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2929,6 +3134,9 @@ func (m *Object) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2952,7 +3160,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2961,6 +3169,9 @@ func (m *Object) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2984,7 +3195,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2993,6 +3204,9 @@ func (m *Object) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3016,7 +3230,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3025,6 +3239,9 @@ func (m *Object) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3048,7 +3265,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3057,6 +3274,9 @@ func (m *Object) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3080,7 +3300,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3089,6 +3309,9 @@ func (m *Object) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3112,7 +3335,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3121,6 +3344,9 @@ func (m *Object) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3136,7 +3362,7 @@ func (m *Object) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWatch } if (iNdEx + skippy) > l { @@ -3166,7 +3392,7 @@ func (m *SelectBySlot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3194,7 +3420,7 @@ func (m *SelectBySlot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3204,6 +3430,9 @@ func (m *SelectBySlot) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3223,7 +3452,7 @@ func (m *SelectBySlot) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Slot |= (uint64(b) & 0x7F) << shift + m.Slot |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3234,7 +3463,7 @@ func (m *SelectBySlot) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWatch } if (iNdEx + skippy) > l { @@ -3264,7 +3493,7 @@ func (m *SelectByCustom) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3292,7 +3521,7 @@ func (m *SelectByCustom) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3302,6 +3531,9 @@ func (m *SelectByCustom) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3321,7 +3553,7 @@ func (m *SelectByCustom) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3331,6 +3563,9 @@ func (m *SelectByCustom) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3350,7 +3585,7 @@ func (m *SelectByCustom) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3360,6 +3595,9 @@ func (m *SelectByCustom) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3371,7 +3609,7 @@ func (m *SelectByCustom) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWatch } if (iNdEx + skippy) > l { @@ -3401,7 +3639,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3429,7 +3667,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3439,6 +3677,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3458,7 +3699,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3468,6 +3709,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3487,7 +3731,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3497,6 +3741,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3516,7 +3763,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3526,6 +3773,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3545,7 +3795,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3554,6 +3804,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3577,7 +3830,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3586,6 +3839,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3609,7 +3865,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3619,6 +3875,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3638,7 +3897,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3648,6 +3907,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3667,7 +3929,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3676,6 +3938,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3699,7 +3964,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (TaskState(b) & 0x7F) << shift + v |= TaskState(b&0x7F) << shift if b < 0x80 { break } @@ -3719,7 +3984,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (NodeRole(b) & 0x7F) << shift + v |= NodeRole(b&0x7F) << shift if b < 0x80 { break } @@ -3739,7 +4004,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (NodeSpec_Membership(b) & 0x7F) << shift + v |= NodeSpec_Membership(b&0x7F) << shift if b < 0x80 { break } @@ -3759,7 +4024,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3769,6 +4034,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3788,7 +4056,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3798,6 +4066,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3817,7 +4088,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3827,6 +4098,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3846,7 +4120,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3856,6 +4130,9 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3867,7 +4144,7 @@ func (m *SelectBy) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWatch } if (iNdEx + skippy) > l { @@ -3897,7 +4174,7 @@ func (m *WatchRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3925,7 +4202,7 @@ func (m *WatchRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3934,6 +4211,9 @@ func (m *WatchRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3956,7 +4236,7 @@ func (m *WatchRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3965,6 +4245,9 @@ func (m *WatchRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3989,7 +4272,7 @@ func (m *WatchRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4001,7 +4284,7 @@ func (m *WatchRequest) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWatch } if (iNdEx + skippy) > l { @@ -4031,7 +4314,7 @@ func (m *WatchRequest_WatchEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4059,7 +4342,7 @@ func (m *WatchRequest_WatchEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4069,6 +4352,9 @@ func (m *WatchRequest_WatchEntry) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4088,7 +4374,7 @@ func (m *WatchRequest_WatchEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Action |= (WatchActionKind(b) & 0x7F) << shift + m.Action |= WatchActionKind(b&0x7F) << shift if b < 0x80 { break } @@ -4107,7 +4393,7 @@ func (m *WatchRequest_WatchEntry) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4116,6 +4402,9 @@ func (m *WatchRequest_WatchEntry) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4130,7 +4419,7 @@ func (m *WatchRequest_WatchEntry) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWatch } if (iNdEx + skippy) > l { @@ -4160,7 +4449,7 @@ func (m *WatchMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4188,7 +4477,7 @@ func (m *WatchMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4197,6 +4486,9 @@ func (m *WatchMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4219,7 +4511,7 @@ func (m *WatchMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4228,6 +4520,9 @@ func (m *WatchMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4244,7 +4539,7 @@ func (m *WatchMessage) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWatch } if (iNdEx + skippy) > l { @@ -4274,7 +4569,7 @@ func (m *WatchMessage_Event) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -4302,7 +4597,7 @@ func (m *WatchMessage_Event) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Action |= (WatchActionKind(b) & 0x7F) << shift + m.Action |= WatchActionKind(b&0x7F) << shift if b < 0x80 { break } @@ -4321,7 +4616,7 @@ func (m *WatchMessage_Event) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4330,6 +4625,9 @@ func (m *WatchMessage_Event) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4354,7 +4652,7 @@ func (m *WatchMessage_Event) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -4363,6 +4661,9 @@ func (m *WatchMessage_Event) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWatch } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWatch + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -4379,7 +4680,7 @@ func (m *WatchMessage_Event) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWatch } if (iNdEx + skippy) > l { @@ -4397,6 +4698,7 @@ func (m *WatchMessage_Event) Unmarshal(dAtA []byte) error { func skipWatch(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -4428,10 +4730,8 @@ func skipWatch(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -4448,134 +4748,34 @@ func skipWatch(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthWatch } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowWatch - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipWatch(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupWatch + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthWatch + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthWatch = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowWatch = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthWatch = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowWatch = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupWatch = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("github.com/docker/swarmkit/api/watch.proto", fileDescriptorWatch) } - -var fileDescriptorWatch = []byte{ - // 1186 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x96, 0xbd, 0x73, 0x1b, 0xc5, - 0x1b, 0xc7, 0x75, 0x8a, 0x7c, 0x92, 0x1e, 0xdb, 0x89, 0x67, 0xe3, 0x24, 0xf7, 0xd3, 0x2f, 0xc8, - 0x42, 0x0c, 0x90, 0x49, 0x82, 0x0c, 0x26, 0x24, 0x03, 0x04, 0x66, 0x2c, 0x59, 0x8c, 0x44, 0xc6, - 0x2f, 0xb3, 0xb6, 0x93, 0x52, 0x73, 0xbe, 0x7b, 0xac, 0x1c, 0xba, 0xbb, 0x15, 0x7b, 0x27, 0x39, - 0xee, 0x28, 0x28, 0x98, 0xf4, 0xcc, 0xd0, 0xa4, 0x82, 0x9a, 0x86, 0x0e, 0xfe, 0x81, 0x0c, 0x15, - 0x25, 0x34, 0x1a, 0xa2, 0x92, 0x82, 0xbf, 0x80, 0x82, 0xd9, 0x97, 0xf3, 0x8b, 0x72, 0xb2, 0x49, - 0xa5, 0xbd, 0xbd, 0xcf, 0xf7, 0xd9, 0x67, 0x9f, 0xb7, 0x13, 0xdc, 0xec, 0x7a, 0xf1, 0xe3, 0xc1, - 0x5e, 0xcd, 0x61, 0xc1, 0xb2, 0xcb, 0x9c, 0x1e, 0xf2, 0xe5, 0xe8, 0xc0, 0xe6, 0x41, 0xcf, 0x8b, - 0x97, 0xed, 0xbe, 0xb7, 0x7c, 0x60, 0xc7, 0xce, 0xe3, 0x5a, 0x9f, 0xb3, 0x98, 0x11, 0xa2, 0x80, - 0x5a, 0x02, 0xd4, 0x86, 0xef, 0x95, 0xce, 0xd3, 0x47, 0x7d, 0x74, 0x22, 0xa5, 0x2f, 0xdd, 0x3e, - 0x87, 0x65, 0x7b, 0x5f, 0xa0, 0x13, 0x27, 0xf4, 0x79, 0x96, 0xe3, 0xc3, 0x3e, 0x26, 0xec, 0x62, - 0x97, 0x75, 0x99, 0x5c, 0x2e, 0x8b, 0x95, 0xde, 0xbd, 0x77, 0x86, 0x05, 0x49, 0xec, 0x0d, 0xf6, - 0x97, 0xfb, 0xfe, 0xa0, 0xeb, 0x85, 0xfa, 0x47, 0x09, 0xab, 0x5f, 0xe7, 0xc0, 0xdc, 0x94, 0xce, - 0x90, 0x1a, 0xe4, 0x42, 0xe6, 0xa2, 0x65, 0x54, 0x8c, 0x1b, 0xb3, 0x2b, 0x56, 0xed, 0xe5, 0x10, - 0xd4, 0x36, 0x98, 0x8b, 0xad, 0x0c, 0x95, 0x1c, 0xb9, 0x07, 0xf9, 0x08, 0xf9, 0xd0, 0x73, 0xd0, - 0xca, 0x4a, 0xc9, 0xff, 0xd3, 0x24, 0xdb, 0x0a, 0x69, 0x65, 0x68, 0x42, 0x0b, 0x61, 0x88, 0xf1, - 0x01, 0xe3, 0x3d, 0xeb, 0xc2, 0x74, 0xe1, 0x86, 0x42, 0x84, 0x50, 0xd3, 0xc2, 0xc3, 0xd8, 0x8e, - 0x7a, 0x56, 0x6e, 0xba, 0x87, 0x3b, 0x76, 0x24, 0x24, 0x92, 0x13, 0x07, 0x39, 0xfe, 0x20, 0x8a, - 0x91, 0x5b, 0x33, 0xd3, 0x0f, 0x6a, 0x28, 0x44, 0x1c, 0xa4, 0x69, 0x72, 0x07, 0xcc, 0x08, 0x1d, - 0x8e, 0xb1, 0x65, 0x4a, 0x5d, 0x29, 0xfd, 0x66, 0x82, 0x68, 0x65, 0xa8, 0x66, 0xc9, 0x47, 0x50, - 0xe0, 0x18, 0xb1, 0x01, 0x77, 0xd0, 0xca, 0x4b, 0xdd, 0xf5, 0x34, 0x1d, 0xd5, 0x4c, 0x2b, 0x43, - 0x8f, 0x78, 0xf2, 0x09, 0x14, 0xf1, 0x49, 0x8c, 0x61, 0xe4, 0xb1, 0xd0, 0x2a, 0x48, 0xf1, 0x6b, - 0x69, 0xe2, 0x66, 0x02, 0xb5, 0x32, 0xf4, 0x58, 0x21, 0x1c, 0x76, 0x58, 0xb8, 0xef, 0x75, 0xad, - 0xe2, 0x74, 0x87, 0x1b, 0x92, 0x10, 0x0e, 0x2b, 0xb6, 0x5e, 0x48, 0x72, 0x5f, 0xdd, 0x82, 0xb9, - 0x6d, 0xf4, 0xd1, 0x89, 0xeb, 0x87, 0xdb, 0x3e, 0x8b, 0xc9, 0x6d, 0x00, 0x9d, 0xad, 0x8e, 0xe7, - 0xca, 0x8a, 0x28, 0xd6, 0xe7, 0xc7, 0xa3, 0xa5, 0xa2, 0x4e, 0x67, 0x7b, 0x8d, 0x16, 0x35, 0xd0, - 0x76, 0x09, 0x81, 0x5c, 0xe4, 0xb3, 0x58, 0x96, 0x41, 0x8e, 0xca, 0x75, 0x75, 0x0b, 0x2e, 0x26, - 0x16, 0x1b, 0x83, 0x28, 0x66, 0x81, 0xa0, 0x7a, 0x5e, 0xa8, 0xad, 0x51, 0xb9, 0x26, 0x8b, 0x30, - 0xe3, 0x85, 0x2e, 0x3e, 0x91, 0xd2, 0x22, 0x55, 0x0f, 0x62, 0x77, 0x68, 0xfb, 0x03, 0x94, 0xe5, - 0x51, 0xa4, 0xea, 0xa1, 0xfa, 0x97, 0x09, 0x85, 0xc4, 0x24, 0xb1, 0x20, 0x7b, 0xe4, 0x98, 0x39, - 0x1e, 0x2d, 0x65, 0xdb, 0x6b, 0xad, 0x0c, 0xcd, 0x7a, 0x2e, 0xb9, 0x05, 0x45, 0xcf, 0xed, 0xf4, - 0x39, 0xee, 0x7b, 0xda, 0x6c, 0x7d, 0x6e, 0x3c, 0x5a, 0x2a, 0xb4, 0xd7, 0xb6, 0xe4, 0x9e, 0x08, - 0xbb, 0xe7, 0xaa, 0x35, 0x59, 0x84, 0x5c, 0x68, 0x07, 0xfa, 0x20, 0x59, 0xd9, 0x76, 0x80, 0xe4, - 0x75, 0x98, 0x15, 0xbf, 0x89, 0x91, 0x9c, 0x7e, 0x09, 0x62, 0x53, 0x0b, 0xef, 0x83, 0xe9, 0xc8, - 0x6b, 0xe9, 0xca, 0xaa, 0xa6, 0x57, 0xc8, 0xc9, 0x00, 0xc8, 0xc0, 0xab, 0x50, 0xb4, 0x61, 0x5e, - 0xad, 0x92, 0x23, 0xcc, 0x57, 0x30, 0x32, 0xa7, 0xa4, 0xda, 0x91, 0xda, 0xa9, 0x4c, 0xe5, 0x53, - 0x32, 0x25, 0x2a, 0xe5, 0x38, 0x57, 0x6f, 0x42, 0x5e, 0x74, 0xaf, 0x80, 0x0b, 0x12, 0x86, 0xf1, - 0x68, 0xc9, 0x14, 0x8d, 0x2d, 0x49, 0x53, 0xbc, 0x6c, 0xbb, 0xe4, 0xae, 0x4e, 0xa9, 0x2a, 0xa7, - 0xca, 0x59, 0x8e, 0x89, 0x82, 0x11, 0xa1, 0x13, 0x3c, 0x59, 0x83, 0x79, 0x17, 0x23, 0x8f, 0xa3, - 0xdb, 0x89, 0x62, 0x3b, 0x46, 0x0b, 0x2a, 0xc6, 0x8d, 0x8b, 0xe9, 0xb5, 0x2c, 0x7a, 0x75, 0x5b, - 0x40, 0xe2, 0x52, 0x5a, 0x25, 0x9f, 0xc9, 0x0a, 0xe4, 0x38, 0xf3, 0xd1, 0x9a, 0x95, 0xe2, 0xeb, - 0xd3, 0x46, 0x11, 0x65, 0xbe, 0x1c, 0x47, 0x82, 0x25, 0x6d, 0x80, 0x00, 0x83, 0x3d, 0xe4, 0xd1, - 0x63, 0xaf, 0x6f, 0xcd, 0x49, 0xe5, 0xdb, 0xd3, 0x94, 0xdb, 0x7d, 0x74, 0x6a, 0xeb, 0x47, 0xb8, - 0x48, 0xee, 0xb1, 0x98, 0xac, 0xc3, 0x15, 0x8e, 0xfb, 0xc8, 0x31, 0x74, 0xd0, 0xed, 0xe8, 0xe9, - 0x23, 0x22, 0x36, 0x2f, 0x23, 0x76, 0x6d, 0x3c, 0x5a, 0xba, 0x4c, 0x8f, 0x00, 0x3d, 0xa8, 0x64, - 0xf8, 0x2e, 0xf3, 0x97, 0xb6, 0x5d, 0xf2, 0x39, 0x2c, 0x9e, 0x30, 0xa7, 0x86, 0x85, 0xb0, 0x76, - 0x51, 0x5a, 0xbb, 0x3a, 0x1e, 0x2d, 0x91, 0x63, 0x6b, 0x6a, 0xaa, 0x48, 0x63, 0x84, 0x4f, 0xee, - 0x8a, 0x86, 0x51, 0x4d, 0x74, 0x29, 0x29, 0x58, 0xd9, 0x46, 0xa7, 0x4f, 0x50, 0xdd, 0x2d, 0x4e, - 0x58, 0x48, 0x3b, 0x41, 0x8d, 0x81, 0xc9, 0x13, 0xf4, 0xae, 0x5b, 0xcf, 0x41, 0xb6, 0x7e, 0x58, - 0xfd, 0x23, 0x0b, 0x73, 0x8f, 0xc4, 0x07, 0x91, 0xe2, 0x97, 0x03, 0x8c, 0x62, 0xd2, 0x84, 0x3c, - 0x86, 0x31, 0xf7, 0x30, 0xb2, 0x8c, 0xca, 0x85, 0x1b, 0xb3, 0x2b, 0xb7, 0xd2, 0x62, 0x7b, 0x52, - 0xa2, 0x1e, 0x9a, 0x61, 0xcc, 0x0f, 0x69, 0xa2, 0x25, 0xf7, 0x61, 0x96, 0x63, 0x34, 0x08, 0xb0, - 0xb3, 0xcf, 0x59, 0x70, 0xd6, 0x87, 0xe3, 0x21, 0x72, 0x31, 0xda, 0x28, 0x28, 0xfe, 0x33, 0xce, - 0x02, 0x72, 0x1b, 0x88, 0x17, 0x3a, 0xfe, 0xc0, 0xc5, 0x0e, 0xf3, 0xdd, 0x8e, 0xfa, 0x8a, 0xca, - 0xe6, 0x2d, 0xd0, 0x05, 0xfd, 0x66, 0xd3, 0x77, 0xd5, 0x50, 0x2b, 0x7d, 0x6b, 0x00, 0x1c, 0xfb, - 0x90, 0x3a, 0x7f, 0x3e, 0x06, 0xd3, 0x76, 0x62, 0x31, 0x73, 0xb3, 0xb2, 0x60, 0xde, 0x98, 0x7a, - 0xa9, 0x55, 0x89, 0x3d, 0xf0, 0x42, 0x97, 0x6a, 0x09, 0xb9, 0x0b, 0xf9, 0x7d, 0xcf, 0x8f, 0x91, - 0x47, 0xd6, 0x05, 0x19, 0x92, 0xeb, 0x67, 0xb5, 0x09, 0x4d, 0xe0, 0xea, 0x2f, 0x49, 0x6c, 0xd7, - 0x31, 0x8a, 0xec, 0x2e, 0x92, 0x4f, 0xc1, 0xc4, 0x21, 0x86, 0x71, 0x12, 0xda, 0xb7, 0xa6, 0x7a, - 0xa1, 0x15, 0xb5, 0xa6, 0xc0, 0xa9, 0x56, 0x91, 0x0f, 0x20, 0x3f, 0x54, 0xd1, 0xfa, 0x2f, 0x01, - 0x4d, 0xd8, 0xd2, 0x4f, 0x06, 0xcc, 0x48, 0x43, 0x27, 0xc2, 0x60, 0xbc, 0x7a, 0x18, 0x56, 0xc0, - 0xd4, 0x89, 0xc8, 0x4e, 0xff, 0xf6, 0xa8, 0x94, 0x50, 0x4d, 0x92, 0x0f, 0x01, 0x26, 0x12, 0x78, - 0xb6, 0xae, 0xc8, 0x92, 0xac, 0xde, 0xfc, 0xc7, 0x80, 0x4b, 0x13, 0xae, 0x90, 0x3b, 0xb0, 0xf8, - 0x68, 0x75, 0xa7, 0xd1, 0xea, 0xac, 0x36, 0x76, 0xda, 0x9b, 0x1b, 0x9d, 0xdd, 0x8d, 0x07, 0x1b, - 0x9b, 0x8f, 0x36, 0x16, 0x32, 0xa5, 0xd2, 0xd3, 0x67, 0x95, 0xab, 0x13, 0xf8, 0x6e, 0xd8, 0x0b, - 0xd9, 0x81, 0x70, 0xfc, 0xf2, 0x29, 0x55, 0x83, 0x36, 0x57, 0x77, 0x9a, 0x0b, 0x46, 0xe9, 0x7f, - 0x4f, 0x9f, 0x55, 0xae, 0x4c, 0x88, 0x1a, 0x1c, 0xd5, 0x64, 0x3a, 0xad, 0xd9, 0xdd, 0x5a, 0x13, - 0x9a, 0x6c, 0xaa, 0x66, 0xb7, 0xef, 0xa6, 0x69, 0x68, 0x73, 0x7d, 0xf3, 0x61, 0x73, 0x21, 0x97, - 0xaa, 0xa1, 0x18, 0xb0, 0x21, 0x96, 0xae, 0x7d, 0xf3, 0x7d, 0x39, 0xf3, 0xf3, 0x0f, 0xe5, 0xc9, - 0xab, 0xae, 0x04, 0x30, 0x23, 0xb7, 0x88, 0x9b, 0x2c, 0x2a, 0xe7, 0x35, 0x62, 0xa9, 0x72, 0x5e, - 0x3d, 0x55, 0xaf, 0xfc, 0xfa, 0xe3, 0xdf, 0xdf, 0x65, 0x2f, 0xc1, 0xbc, 0x24, 0xde, 0x09, 0xec, - 0xd0, 0xee, 0x22, 0x7f, 0xd7, 0xa8, 0x5b, 0xcf, 0x5f, 0x94, 0x33, 0xbf, 0xbf, 0x28, 0x67, 0xbe, - 0x1a, 0x97, 0x8d, 0xe7, 0xe3, 0xb2, 0xf1, 0xdb, 0xb8, 0x6c, 0xfc, 0x39, 0x2e, 0x1b, 0x7b, 0xa6, - 0xfc, 0x03, 0xf9, 0xfe, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xe6, 0x76, 0x89, 0xef, 0x57, 0x0b, - 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/ca/auth.go b/vendor/github.com/docker/swarmkit/ca/auth.go index df4547fb13600..e0ff898c19bc8 100644 --- a/vendor/github.com/docker/swarmkit/ca/auth.go +++ b/vendor/github.com/docker/swarmkit/ca/auth.go @@ -1,6 +1,7 @@ package ca import ( + "context" "crypto/tls" "crypto/x509/pkix" "strings" @@ -9,7 +10,6 @@ import ( "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/log" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" "google.golang.org/grpc/peer" diff --git a/vendor/github.com/docker/swarmkit/ca/certificates.go b/vendor/github.com/docker/swarmkit/ca/certificates.go index ad2be2c571a24..dd0297ab4f41e 100644 --- a/vendor/github.com/docker/swarmkit/ca/certificates.go +++ b/vendor/github.com/docker/swarmkit/ca/certificates.go @@ -2,6 +2,7 @@ package ca import ( "bytes" + "context" "crypto" "crypto/ecdsa" "crypto/elliptic" @@ -31,7 +32,6 @@ import ( "github.com/docker/swarmkit/ioutils" "github.com/opencontainers/go-digest" "github.com/pkg/errors" - "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" diff --git a/vendor/github.com/docker/swarmkit/ca/config.go b/vendor/github.com/docker/swarmkit/ca/config.go index 4a7230ac2f27f..4befee5bccf3d 100644 --- a/vendor/github.com/docker/swarmkit/ca/config.go +++ b/vendor/github.com/docker/swarmkit/ca/config.go @@ -1,6 +1,7 @@ package ca import ( + "context" cryptorand "crypto/rand" "crypto/tls" "crypto/x509" @@ -23,8 +24,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "google.golang.org/grpc/credentials" - - "golang.org/x/net/context" ) const ( @@ -32,7 +31,6 @@ const ( rootCAKeyFilename = "swarm-root-ca.key" nodeTLSCertFilename = "swarm-node.crt" nodeTLSKeyFilename = "swarm-node.key" - nodeCSRFilename = "swarm-node.csr" // DefaultRootCN represents the root CN that we should create roots CAs with by default DefaultRootCN = "swarm-ca" @@ -626,10 +624,10 @@ func calculateRandomExpiry(validFrom, validUntil time.Time) time.Duration { if maxValidity-minValidity < 1 { randomExpiry = minValidity } else { - randomExpiry = rand.Intn(maxValidity-minValidity) + int(minValidity) + randomExpiry = rand.Intn(maxValidity-minValidity) + minValidity } - expiry := validFrom.Add(time.Duration(randomExpiry) * time.Minute).Sub(time.Now()) + expiry := time.Until(validFrom.Add(time.Duration(randomExpiry) * time.Minute)) if expiry < 0 { return 0 } diff --git a/vendor/github.com/docker/swarmkit/ca/external.go b/vendor/github.com/docker/swarmkit/ca/external.go index 789361eb61d9c..6b81204595dbf 100644 --- a/vendor/github.com/docker/swarmkit/ca/external.go +++ b/vendor/github.com/docker/swarmkit/ca/external.go @@ -2,6 +2,7 @@ package ca import ( "bytes" + "context" cryptorand "crypto/rand" "crypto/tls" "crypto/x509" @@ -21,7 +22,6 @@ import ( "github.com/docker/swarmkit/log" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "golang.org/x/net/context" "golang.org/x/net/context/ctxhttp" ) diff --git a/vendor/github.com/docker/swarmkit/ca/forward.go b/vendor/github.com/docker/swarmkit/ca/forward.go index a05cf53aaf65b..7ad7c7dd0b788 100644 --- a/vendor/github.com/docker/swarmkit/ca/forward.go +++ b/vendor/github.com/docker/swarmkit/ca/forward.go @@ -1,7 +1,8 @@ package ca import ( - "golang.org/x/net/context" + "context" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/peer" ) diff --git a/vendor/github.com/docker/swarmkit/ca/renewer.go b/vendor/github.com/docker/swarmkit/ca/renewer.go index 2a2fae7808c39..e5d165f6a8722 100644 --- a/vendor/github.com/docker/swarmkit/ca/renewer.go +++ b/vendor/github.com/docker/swarmkit/ca/renewer.go @@ -1,6 +1,7 @@ package ca import ( + "context" "sync" "time" @@ -9,7 +10,6 @@ import ( "github.com/docker/swarmkit/log" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "golang.org/x/net/context" ) // RenewTLSExponentialBackoff sets the exponential backoff when trying to renew TLS certificates that have expired diff --git a/vendor/github.com/docker/swarmkit/ca/server.go b/vendor/github.com/docker/swarmkit/ca/server.go index a456df7900810..01959972e994b 100644 --- a/vendor/github.com/docker/swarmkit/ca/server.go +++ b/vendor/github.com/docker/swarmkit/ca/server.go @@ -2,6 +2,7 @@ package ca import ( "bytes" + "context" "crypto/subtle" "crypto/x509" "sync" @@ -15,7 +16,6 @@ import ( gogotypes "github.com/gogo/protobuf/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -323,7 +323,7 @@ func (s *Server) IssueNodeCertificate(ctx context.Context, request *api.IssueNod Availability: request.Availability, }, } - + node.VXLANUDPPort = clusters[0].VXLANUDPPort return store.CreateNode(tx, node) }) if err == nil { diff --git a/vendor/github.com/docker/swarmkit/ca/transport.go b/vendor/github.com/docker/swarmkit/ca/transport.go index 35943afbcb8fd..69c4379b36a4e 100644 --- a/vendor/github.com/docker/swarmkit/ca/transport.go +++ b/vendor/github.com/docker/swarmkit/ca/transport.go @@ -1,6 +1,7 @@ package ca import ( + "context" "crypto/tls" "crypto/x509" "crypto/x509/pkix" @@ -9,7 +10,6 @@ import ( "sync" "github.com/pkg/errors" - "golang.org/x/net/context" "google.golang.org/grpc/credentials" ) @@ -18,12 +18,6 @@ var ( alpnProtoStr = []string{"h2"} ) -type timeoutError struct{} - -func (timeoutError) Error() string { return "mutablecredentials: Dial timed out" } -func (timeoutError) Timeout() bool { return true } -func (timeoutError) Temporary() bool { return true } - // MutableTLSCreds is the credentials required for authenticating a connection using TLS. type MutableTLSCreds struct { // Mutex for the tls config diff --git a/vendor/github.com/docker/swarmkit/identity/combined_id.go b/vendor/github.com/docker/swarmkit/identity/combined_id.go new file mode 100644 index 0000000000000..2c4a292761b2b --- /dev/null +++ b/vendor/github.com/docker/swarmkit/identity/combined_id.go @@ -0,0 +1,8 @@ +package identity + +import "fmt" + +// CombineTwoIDs combines the given IDs into a new ID, e.g. a secret and a task ID. +func CombineTwoIDs(id1, id2 string) string { + return fmt.Sprintf("%s.%s", id1, id2) +} diff --git a/vendor/github.com/docker/swarmkit/log/context.go b/vendor/github.com/docker/swarmkit/log/context.go index ac4f8488069ef..cc1d590f111e4 100644 --- a/vendor/github.com/docker/swarmkit/log/context.go +++ b/vendor/github.com/docker/swarmkit/log/context.go @@ -1,10 +1,10 @@ package log import ( + "context" "path" "github.com/sirupsen/logrus" - "golang.org/x/net/context" ) var ( diff --git a/vendor/github.com/docker/swarmkit/log/grpc.go b/vendor/github.com/docker/swarmkit/log/grpc.go index 0417c944c550b..bced5cfa1b9f6 100644 --- a/vendor/github.com/docker/swarmkit/log/grpc.go +++ b/vendor/github.com/docker/swarmkit/log/grpc.go @@ -1,8 +1,9 @@ package log import ( + "context" + "github.com/sirupsen/logrus" - "golang.org/x/net/context" "google.golang.org/grpc/grpclog" ) diff --git a/vendor/github.com/docker/swarmkit/manager/allocator/allocator.go b/vendor/github.com/docker/swarmkit/manager/allocator/allocator.go index e8b7e88c47368..b4cc1c9e86478 100644 --- a/vendor/github.com/docker/swarmkit/manager/allocator/allocator.go +++ b/vendor/github.com/docker/swarmkit/manager/allocator/allocator.go @@ -1,6 +1,7 @@ package allocator import ( + "context" "sync" "github.com/docker/docker/pkg/plugingetter" @@ -9,7 +10,6 @@ import ( "github.com/docker/swarmkit/manager/allocator/cnmallocator" "github.com/docker/swarmkit/manager/state" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" ) // Allocator controls how the allocation stage in the manager is handled. diff --git a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_darwin.go b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_darwin.go index 8cbedbd6b82f8..1767ee0f1c282 100644 --- a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_darwin.go +++ b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_darwin.go @@ -1,8 +1,8 @@ package cnmallocator import ( - "github.com/docker/libnetwork/drivers/overlay/ovmanager" - "github.com/docker/libnetwork/drivers/remote" + "github.com/docker/docker/libnetwork/drivers/overlay/ovmanager" + "github.com/docker/docker/libnetwork/drivers/remote" "github.com/docker/swarmkit/manager/allocator/networkallocator" ) diff --git a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_ipam.go b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_ipam.go index 39bce0615c70e..1b9617d31e08f 100644 --- a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_ipam.go +++ b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_ipam.go @@ -4,12 +4,12 @@ import ( "strconv" "strings" - "github.com/docker/libnetwork/drvregistry" - "github.com/docker/libnetwork/ipamapi" - builtinIpam "github.com/docker/libnetwork/ipams/builtin" - nullIpam "github.com/docker/libnetwork/ipams/null" - remoteIpam "github.com/docker/libnetwork/ipams/remote" - "github.com/docker/libnetwork/ipamutils" + "github.com/docker/docker/libnetwork/drvregistry" + "github.com/docker/docker/libnetwork/ipamapi" + builtinIpam "github.com/docker/docker/libnetwork/ipams/builtin" + nullIpam "github.com/docker/docker/libnetwork/ipams/null" + remoteIpam "github.com/docker/docker/libnetwork/ipams/remote" + "github.com/docker/docker/libnetwork/ipamutils" "github.com/sirupsen/logrus" ) @@ -31,6 +31,7 @@ func initIPAMDrivers(r *drvregistry.DrvRegistry, netConfig *NetworkConfig) error } str.WriteString(": Size ") str.WriteString(strconv.Itoa(int(netConfig.SubnetSize))) + } if err := ipamutils.ConfigGlobalScopeDefaultNetworks(addressPool); err != nil { return err diff --git a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_network_linux.go b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_network_linux.go index 5d6a0e74bf3b3..9d3b0e51cbdd2 100644 --- a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_network_linux.go +++ b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_network_linux.go @@ -1,12 +1,12 @@ package cnmallocator import ( - "github.com/docker/libnetwork/drivers/bridge/brmanager" - "github.com/docker/libnetwork/drivers/host" - "github.com/docker/libnetwork/drivers/ipvlan/ivmanager" - "github.com/docker/libnetwork/drivers/macvlan/mvmanager" - "github.com/docker/libnetwork/drivers/overlay/ovmanager" - "github.com/docker/libnetwork/drivers/remote" + "github.com/docker/docker/libnetwork/drivers/bridge/brmanager" + "github.com/docker/docker/libnetwork/drivers/host" + "github.com/docker/docker/libnetwork/drivers/ipvlan/ivmanager" + "github.com/docker/docker/libnetwork/drivers/macvlan/mvmanager" + "github.com/docker/docker/libnetwork/drivers/overlay/ovmanager" + "github.com/docker/docker/libnetwork/drivers/remote" "github.com/docker/swarmkit/manager/allocator/networkallocator" ) diff --git a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_network_windows.go b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_network_windows.go index 8cbedbd6b82f8..1767ee0f1c282 100644 --- a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_network_windows.go +++ b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/drivers_network_windows.go @@ -1,8 +1,8 @@ package cnmallocator import ( - "github.com/docker/libnetwork/drivers/overlay/ovmanager" - "github.com/docker/libnetwork/drivers/remote" + "github.com/docker/docker/libnetwork/drivers/overlay/ovmanager" + "github.com/docker/docker/libnetwork/drivers/remote" "github.com/docker/swarmkit/manager/allocator/networkallocator" ) diff --git a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/networkallocator.go b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/networkallocator.go index 7a786406e1494..a21ba1cfa636b 100644 --- a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/networkallocator.go +++ b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/networkallocator.go @@ -1,22 +1,22 @@ package cnmallocator import ( + "context" "fmt" "net" "strings" + "github.com/docker/docker/libnetwork/datastore" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/drvregistry" + "github.com/docker/docker/libnetwork/ipamapi" + "github.com/docker/docker/libnetwork/netlabel" "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/drvregistry" - "github.com/docker/libnetwork/ipamapi" - "github.com/docker/libnetwork/netlabel" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/allocator/networkallocator" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "golang.org/x/net/context" ) const ( @@ -94,6 +94,9 @@ type NetworkConfig struct { // SubnetSize specifies the subnet size of the networks created from // the default subnet pool SubnetSize uint32 + + // VXLANUDPPort specifies the UDP port number for VXLAN traffic + VXLANUDPPort uint32 } // New returns a new NetworkAllocator handle @@ -482,7 +485,7 @@ func (na *cnmNetworkAllocator) IsAttachmentAllocated(node *api.Node, networkAtta return false } - if networkAttachment == nil { + if networkAttachment == nil || networkAttachment.Network == nil { return false } @@ -815,8 +818,7 @@ func (na *cnmNetworkAllocator) resolveDriver(n *api.Network) (*networkDriver, er d, drvcap := na.drvRegistry.Driver(dName) if d == nil { - var err error - err = na.loadDriver(dName) + err := na.loadDriver(dName) if err != nil { return nil, err } diff --git a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/portallocator.go b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/portallocator.go index 113f9002422cc..f5e4b60bd1630 100644 --- a/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/portallocator.go +++ b/vendor/github.com/docker/swarmkit/manager/allocator/cnmallocator/portallocator.go @@ -3,7 +3,7 @@ package cnmallocator import ( "fmt" - "github.com/docker/libnetwork/idm" + "github.com/docker/docker/libnetwork/idm" "github.com/docker/swarmkit/api" ) @@ -407,12 +407,12 @@ func (ps *portSpace) allocate(p *api.PortConfig) (err error) { } defer func() { if err != nil { - ps.dynamicPortSpace.Release(uint64(swarmPort)) + ps.dynamicPortSpace.Release(swarmPort) } }() // Make sure we allocate the same port from the master space. - if err = ps.masterPortSpace.GetSpecificID(uint64(swarmPort)); err != nil { + if err = ps.masterPortSpace.GetSpecificID(swarmPort); err != nil { return } diff --git a/vendor/github.com/docker/swarmkit/manager/allocator/network.go b/vendor/github.com/docker/swarmkit/manager/allocator/network.go index e4d5b61827adb..fee3bf1d44b58 100644 --- a/vendor/github.com/docker/swarmkit/manager/allocator/network.go +++ b/vendor/github.com/docker/swarmkit/manager/allocator/network.go @@ -1,6 +1,7 @@ package allocator import ( + "context" "fmt" "time" @@ -13,7 +14,6 @@ import ( "github.com/docker/swarmkit/manager/state/store" "github.com/docker/swarmkit/protobuf/ptypes" "github.com/pkg/errors" - "golang.org/x/net/context" ) const ( @@ -69,10 +69,22 @@ type networkContext struct { func (a *Allocator) doNetworkInit(ctx context.Context) (err error) { var netConfig *cnmallocator.NetworkConfig - if a.networkConfig != nil && a.networkConfig.DefaultAddrPool != nil { - netConfig = &cnmallocator.NetworkConfig{ - DefaultAddrPool: a.networkConfig.DefaultAddrPool, - SubnetSize: a.networkConfig.SubnetSize, + // There are two ways user can invoke swarm init + // with default address pool & vxlan port or with only vxlan port + // hence we need two different way to construct netconfig + if a.networkConfig != nil { + if a.networkConfig.DefaultAddrPool != nil { + netConfig = &cnmallocator.NetworkConfig{ + DefaultAddrPool: a.networkConfig.DefaultAddrPool, + SubnetSize: a.networkConfig.SubnetSize, + VXLANUDPPort: a.networkConfig.VXLANUDPPort, + } + } else if a.networkConfig.VXLANUDPPort != 0 { + netConfig = &cnmallocator.NetworkConfig{ + DefaultAddrPool: nil, + SubnetSize: 0, + VXLANUDPPort: a.networkConfig.VXLANUDPPort, + } } } @@ -172,7 +184,7 @@ func (a *Allocator) doNetworkAlloc(ctx context.Context, ev events.Event) { } if IsIngressNetwork(n) && nc.ingressNetwork != nil { log.G(ctx).Errorf("Cannot allocate ingress network %s (%s) because another ingress network is already present: %s (%s)", - n.ID, n.Spec.Annotations.Name, nc.ingressNetwork.ID, nc.ingressNetwork.Spec.Annotations) + n.ID, n.Spec.Annotations.Name, nc.ingressNetwork.ID, nc.ingressNetwork.Spec.Annotations.Name) break } @@ -977,8 +989,11 @@ func (a *Allocator) allocateNode(ctx context.Context, node *api.Node, existingAd nc := a.netCtx + var nwIDs = make(map[string]struct{}, len(networks)) + // go through all of the networks we've passed in for _, network := range networks { + nwIDs[network.ID] = struct{}{} // for each one, create space for an attachment. then, search through // all of the attachments already on the node. if the attachment @@ -998,6 +1013,10 @@ func (a *Allocator) allocateNode(ctx context.Context, node *api.Node, existingAd } if lbAttachment == nil { + // if we're restoring state, we should not add an attachment here. + if existingAddressesOnly { + continue + } lbAttachment = &api.NetworkAttachment{} node.Attachments = append(node.Attachments, lbAttachment) } @@ -1033,17 +1052,8 @@ func (a *Allocator) allocateNode(ctx context.Context, node *api.Node, existingAd // https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating attachments := node.Attachments[:0] for _, attach := range node.Attachments { - // for every attachment, go through every network. if the attachment - // belongs to one of the networks, then go to the next attachment. if - // no network matches, then the the attachment should be removed. - attachmentBelongs := false - for _, network := range networks { - if network.ID == attach.Network.ID { - attachmentBelongs = true - break - } - } - if attachmentBelongs { + if _, ok := nwIDs[attach.Network.ID]; ok { + // attachment belongs to one of the networks, so keep it attachments = append(attachments, attach) } else { // free the attachment and remove it from the node's attachments by diff --git a/vendor/github.com/docker/swarmkit/manager/constraint/constraint.go b/vendor/github.com/docker/swarmkit/manager/constraint/constraint.go index 9f13217ae4b55..6c49c0772840b 100644 --- a/vendor/github.com/docker/swarmkit/manager/constraint/constraint.go +++ b/vendor/github.com/docker/swarmkit/manager/constraint/constraint.go @@ -56,7 +56,7 @@ func Parse(env []string) ([]Constraint, error) { part0 := strings.TrimSpace(parts[0]) // validate key matched := alphaNumeric.MatchString(part0) - if matched == false { + if !matched { return nil, fmt.Errorf("key '%s' is invalid", part0) } @@ -64,7 +64,7 @@ func Parse(env []string) ([]Constraint, error) { // validate Value matched = valuePattern.MatchString(part1) - if matched == false { + if !matched { return nil, fmt.Errorf("value '%s' is invalid", part1) } // TODO(dongluochen): revisit requirements to see if globing or regex are useful diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/ca_rotation.go b/vendor/github.com/docker/swarmkit/manager/controlapi/ca_rotation.go index d39c7d2b693a3..91869a8d0af46 100644 --- a/vendor/github.com/docker/swarmkit/manager/controlapi/ca_rotation.go +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/ca_rotation.go @@ -203,6 +203,24 @@ func validateCAConfig(ctx context.Context, securityConfig *ca.SecurityConfig, cl var oldCertExtCAs []*api.ExternalCA if !hasSigningKey(&cluster.RootCA) { + + // If we are going from external -> internal, but providing the external CA's signing key, + // then we don't need to validate any external CAs. We can in fact abort any outstanding root + // rotations if we are just adding a key. Because we have a key, we don't care if there are + // no external CAs matching the certificate. + if bytes.Equal(normalizedRootCA, newConfig.SigningCACert) && hasSigningKey(newConfig) { + // validate that the key and cert indeed match - if they don't then just fail now rather + // than go through all the external CA URLs, which is a more expensive operation + if _, err := ca.NewRootCA(newConfig.SigningCACert, newConfig.SigningCACert, newConfig.SigningCAKey, ca.DefaultNodeCertExpiration, nil); err != nil { + return nil, err + } + copied := cluster.RootCA.Copy() + copied.CAKey = newConfig.SigningCAKey + copied.RootRotation = nil + copied.LastForcedRotation = newConfig.ForceRotate + return copied, nil + } + oldCertExtCAs, err = validateHasAtLeastOneExternalCA(ctx, extCAs, securityConfig, normalizedRootCA, "current") if err != nil { return nil, err diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/cluster.go b/vendor/github.com/docker/swarmkit/manager/controlapi/cluster.go index ad248044844a0..73915f3eae583 100644 --- a/vendor/github.com/docker/swarmkit/manager/controlapi/cluster.go +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/cluster.go @@ -1,6 +1,7 @@ package controlapi import ( + "context" "strings" "time" @@ -10,7 +11,6 @@ import ( "github.com/docker/swarmkit/manager/encryption" "github.com/docker/swarmkit/manager/state/store" gogotypes "github.com/gogo/protobuf/types" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -21,6 +21,8 @@ const ( expiredCertGrace = 24 * time.Hour * 7 // inbuilt default subnet size inbuiltSubnetSize = 24 + // VXLAN default port + defaultVXLANPort = 4789 ) var ( @@ -274,6 +276,7 @@ func redactClusters(clusters []*api.Cluster) []*api.Cluster { BlacklistedCertificates: cluster.BlacklistedCertificates, DefaultAddressPool: cluster.DefaultAddressPool, SubnetSize: cluster.SubnetSize, + VXLANUDPPort: cluster.VXLANUDPPort, } if newCluster.DefaultAddressPool == nil { // This is just for CLI display. Set the inbuilt default pool for @@ -281,6 +284,9 @@ func redactClusters(clusters []*api.Cluster) []*api.Cluster { newCluster.DefaultAddressPool = inbuiltDefaultAddressPool newCluster.SubnetSize = inbuiltSubnetSize } + if newCluster.VXLANUDPPort == 0 { + newCluster.VXLANUDPPort = defaultVXLANPort + } redactedClusters = append(redactedClusters, newCluster) } diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/common.go b/vendor/github.com/docker/swarmkit/manager/controlapi/common.go index 9e521794648c9..6a4b92add4a4f 100644 --- a/vendor/github.com/docker/swarmkit/manager/controlapi/common.go +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/common.go @@ -4,9 +4,9 @@ import ( "regexp" "strings" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/ipamapi" "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/ipamapi" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/manager/allocator" "github.com/docker/swarmkit/manager/state/store" diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/config.go b/vendor/github.com/docker/swarmkit/manager/controlapi/config.go index ae08885b001d1..bc8726fb86862 100644 --- a/vendor/github.com/docker/swarmkit/manager/controlapi/config.go +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/config.go @@ -2,6 +2,7 @@ package controlapi import ( "bytes" + "context" "strings" "github.com/docker/swarmkit/api" @@ -9,7 +10,6 @@ import ( "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/state/store" "github.com/sirupsen/logrus" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/extension.go b/vendor/github.com/docker/swarmkit/manager/controlapi/extension.go new file mode 100644 index 0000000000000..321ec762d3829 --- /dev/null +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/extension.go @@ -0,0 +1,133 @@ +package controlapi + +import ( + "context" + "strings" + + "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/identity" + "github.com/docker/swarmkit/log" + "github.com/docker/swarmkit/manager/state/store" + "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// CreateExtension creates an `Extension` based on the provided `CreateExtensionRequest.Extension` +// and returns a `CreateExtensionResponse`. +// - Returns `InvalidArgument` if the `CreateExtensionRequest.Extension` is malformed, +// or fails validation. +// - Returns an error if the creation fails. +func (s *Server) CreateExtension(ctx context.Context, request *api.CreateExtensionRequest) (*api.CreateExtensionResponse, error) { + if request.Annotations == nil || request.Annotations.Name == "" { + return nil, status.Errorf(codes.InvalidArgument, "extension name must be provided") + } + + extension := &api.Extension{ + ID: identity.NewID(), + Annotations: *request.Annotations, + Description: request.Description, + } + + err := s.store.Update(func(tx store.Tx) error { + return store.CreateExtension(tx, extension) + }) + + switch err { + case store.ErrNameConflict: + return nil, status.Errorf(codes.AlreadyExists, "extension %s already exists", request.Annotations.Name) + case nil: + log.G(ctx).WithFields(logrus.Fields{ + "extension.Name": request.Annotations.Name, + "method": "CreateExtension", + }).Debugf("extension created") + + return &api.CreateExtensionResponse{Extension: extension}, nil + default: + return nil, status.Errorf(codes.Internal, "could not create extension: %v", err.Error()) + } +} + +// GetExtension returns a `GetExtensionResponse` with a `Extension` with the same +// id as `GetExtensionRequest.extension_id` +// - Returns `NotFound` if the Extension with the given id is not found. +// - Returns `InvalidArgument` if the `GetExtensionRequest.extension_id` is empty. +// - Returns an error if the get fails. +func (s *Server) GetExtension(ctx context.Context, request *api.GetExtensionRequest) (*api.GetExtensionResponse, error) { + if request.ExtensionID == "" { + return nil, status.Errorf(codes.InvalidArgument, "extension ID must be provided") + } + + var extension *api.Extension + s.store.View(func(tx store.ReadTx) { + extension = store.GetExtension(tx, request.ExtensionID) + }) + + if extension == nil { + return nil, status.Errorf(codes.NotFound, "extension %s not found", request.ExtensionID) + } + + return &api.GetExtensionResponse{Extension: extension}, nil +} + +// RemoveExtension removes the extension referenced by `RemoveExtensionRequest.ID`. +// - Returns `InvalidArgument` if `RemoveExtensionRequest.extension_id` is empty. +// - Returns `NotFound` if the an extension named `RemoveExtensionRequest.extension_id` is not found. +// - Returns an error if the deletion fails. +func (s *Server) RemoveExtension(ctx context.Context, request *api.RemoveExtensionRequest) (*api.RemoveExtensionResponse, error) { + if request.ExtensionID == "" { + return nil, status.Errorf(codes.InvalidArgument, "extension ID must be provided") + } + + err := s.store.Update(func(tx store.Tx) error { + // Check if the extension exists + extension := store.GetExtension(tx, request.ExtensionID) + if extension == nil { + return status.Errorf(codes.NotFound, "could not find extension %s", request.ExtensionID) + } + + // Check if any resources of this type present in the store, return error if so + resources, err := store.FindResources(tx, store.ByKind(request.ExtensionID)) + if err != nil { + return status.Errorf(codes.Internal, "could not find resources using extension %s: %v", request.ExtensionID, err) + } + + if len(resources) != 0 { + resourceNames := make([]string, 0, len(resources)) + // Number of resources for an extension could be quite large. + // Show a limited number of resources for debugging. + attachedResourceForDebug := 10 + for _, resource := range resources { + resourceNames = append(resourceNames, resource.Annotations.Name) + attachedResourceForDebug = attachedResourceForDebug - 1 + if attachedResourceForDebug == 0 { + break + } + } + + extensionName := extension.Annotations.Name + resourceNameStr := strings.Join(resourceNames, ", ") + resourceStr := "resources" + if len(resourceNames) == 1 { + resourceStr = "resource" + } + + return status.Errorf(codes.InvalidArgument, "extension '%s' is in use by the following %s: %v", extensionName, resourceStr, resourceNameStr) + } + + return store.DeleteExtension(tx, request.ExtensionID) + }) + switch err { + case store.ErrNotExist: + return nil, status.Errorf(codes.NotFound, "extension %s not found", request.ExtensionID) + case nil: + log.G(ctx).WithFields(logrus.Fields{ + "extension.ID": request.ExtensionID, + "method": "RemoveExtension", + }).Debugf("extension removed") + + return &api.RemoveExtensionResponse{}, nil + default: + return nil, err + } +} diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/network.go b/vendor/github.com/docker/swarmkit/manager/controlapi/network.go index 481b0cfe4b9ee..ccb885d01032f 100644 --- a/vendor/github.com/docker/swarmkit/manager/controlapi/network.go +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/network.go @@ -1,17 +1,17 @@ package controlapi import ( + "context" "net" + "github.com/docker/docker/libnetwork/driverapi" + "github.com/docker/docker/libnetwork/ipamapi" "github.com/docker/docker/pkg/plugingetter" - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/ipamapi" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/identity" "github.com/docker/swarmkit/manager/allocator" "github.com/docker/swarmkit/manager/allocator/networkallocator" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/node.go b/vendor/github.com/docker/swarmkit/manager/controlapi/node.go index b2d15df07a7e6..6e8bdba5bd6c3 100644 --- a/vendor/github.com/docker/swarmkit/manager/controlapi/node.go +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/node.go @@ -1,6 +1,7 @@ package controlapi import ( + "context" "crypto/x509" "encoding/pem" @@ -8,7 +9,6 @@ import ( "github.com/docker/swarmkit/manager/state/raft/membership" "github.com/docker/swarmkit/manager/state/store" gogotypes "github.com/gogo/protobuf/types" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -254,24 +254,33 @@ func (s *Server) UpdateNode(ctx context.Context, request *api.UpdateNodeRequest) }, nil } -func removeNodeAttachments(tx store.Tx, nodeID string) error { - // orphan the node's attached containers. if we don't do this, the - // network these attachments are connected to will never be removeable +func orphanNodeTasks(tx store.Tx, nodeID string) error { + // when a node is deleted, all of its tasks are irrecoverably removed. + // additionally, the Dispatcher can no longer be relied on to update the + // task status. Therefore, when the node is removed, we must additionally + // move all of its assigned tasks to the Orphaned state, so that their + // resources can be cleaned up. tasks, err := store.FindTasks(tx, store.ByNodeID(nodeID)) if err != nil { return err } for _, task := range tasks { - // if the task is an attachment, then we just delete it. the allocator - // will do the heavy lifting. basically, GetAttachment will return the - // attachment if that's the kind of runtime, or nil if it's not. - if task.Spec.GetAttachment() != nil { - // don't delete the task. instead, update it to `ORPHANED` so that - // the taskreaper will clean it up. - task.Status.State = api.TaskStateOrphaned - if err := store.UpdateTask(tx, task); err != nil { - return err + // this operation must occur within the same transaction boundary. If + // we cannot accomplish this task orphaning in the same transaction, we + // could crash or die between transactions and not get a chance to do + // this. however, in cases were there is an exceptionally large number + // of tasks for a node, this may cause the transaction to exceed the + // max message size. + // + // therefore, we restrict updating to only tasks in a non-terminal + // state. Tasks in a terminal state do not need to be updated. + if task.Status.State < api.TaskStateCompleted { + task.Status = api.TaskStatus{ + Timestamp: gogotypes.TimestampNow(), + State: api.TaskStateOrphaned, + Message: "Task belonged to a node that has been deleted", } + store.UpdateTask(tx, task) } } return nil @@ -342,7 +351,7 @@ func (s *Server) RemoveNode(ctx context.Context, request *api.RemoveNodeRequest) return err } - if err := removeNodeAttachments(tx, request.NodeID); err != nil { + if err := orphanNodeTasks(tx, request.NodeID); err != nil { return err } diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/resource.go b/vendor/github.com/docker/swarmkit/manager/controlapi/resource.go new file mode 100644 index 0000000000000..8ae5cb1375169 --- /dev/null +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/resource.go @@ -0,0 +1,226 @@ +package controlapi + +import ( + "context" + + "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/identity" + "github.com/docker/swarmkit/log" + "github.com/docker/swarmkit/manager/state/store" +) + +// CreateResource returns a `CreateResourceResponse` after creating a `Resource` based +// on the provided `CreateResourceRequest.Resource`. +// - Returns `InvalidArgument` if the `CreateResourceRequest.Resource` is malformed, +// or if the config data is too long or contains invalid characters. +// - Returns an error if the creation fails. +func (s *Server) CreateResource(ctx context.Context, request *api.CreateResourceRequest) (*api.CreateResourceResponse, error) { + if request.Annotations == nil || request.Annotations.Name == "" { + return nil, status.Errorf(codes.InvalidArgument, "Resource must have a name") + } + + // finally, validate that Kind is not an emptystring. We know that creating + // with Kind as empty string should fail at the store level, but to make + // errors clearer, special case this. + if request.Kind == "" { + return nil, status.Errorf(codes.InvalidArgument, "Resource must belong to an Extension") + } + r := &api.Resource{ + ID: identity.NewID(), + Annotations: *request.Annotations, + Kind: request.Kind, + Payload: request.Payload, + } + + err := s.store.Update(func(tx store.Tx) error { + return store.CreateResource(tx, r) + }) + + switch err { + case store.ErrNoKind: + return nil, status.Errorf(codes.InvalidArgument, "Kind %v is not registered", r.Kind) + case store.ErrNameConflict: + return nil, status.Errorf( + codes.AlreadyExists, + "A resource with name %v already exists", + r.Annotations.Name, + ) + case nil: + log.G(ctx).WithFields(logrus.Fields{ + "resource.Name": r.Annotations.Name, + "method": "CreateResource", + }).Debugf("resource created") + return &api.CreateResourceResponse{Resource: r}, nil + default: + return nil, err + } +} + +// GetResource returns a `GetResourceResponse` with a `Resource` with the same +// id as `GetResourceRequest.Resource` +// - Returns `NotFound` if the Resource with the given id is not found. +// - Returns `InvalidArgument` if the `GetResourceRequest.Resource` is empty. +// - Returns an error if getting fails. +func (s *Server) GetResource(ctx context.Context, request *api.GetResourceRequest) (*api.GetResourceResponse, error) { + if request.ResourceID == "" { + return nil, status.Errorf(codes.InvalidArgument, "resource ID must be present") + } + var resource *api.Resource + s.store.View(func(tx store.ReadTx) { + resource = store.GetResource(tx, request.ResourceID) + }) + + if resource == nil { + return nil, status.Errorf(codes.NotFound, "resource %s not found", request.ResourceID) + } + + return &api.GetResourceResponse{Resource: resource}, nil +} + +// RemoveResource removes the `Resource` referenced by `RemoveResourceRequest.ResourceID`. +// - Returns `InvalidArgument` if `RemoveResourceRequest.ResourceID` is empty. +// - Returns `NotFound` if the a resource named `RemoveResourceRequest.ResourceID` is not found. +// - Returns an error if the deletion fails. +func (s *Server) RemoveResource(ctx context.Context, request *api.RemoveResourceRequest) (*api.RemoveResourceResponse, error) { + if request.ResourceID == "" { + return nil, status.Errorf(codes.InvalidArgument, "resource ID must be present") + } + err := s.store.Update(func(tx store.Tx) error { + return store.DeleteResource(tx, request.ResourceID) + }) + switch err { + case store.ErrNotExist: + return nil, status.Errorf(codes.NotFound, "resource %s not found", request.ResourceID) + case nil: + return &api.RemoveResourceResponse{}, nil + default: + return nil, err + } +} + +// ListResources returns a `ListResourcesResponse` with a list of `Resource`s stored in the raft store, +// or all resources matching any name in `ListConfigsRequest.Names`, any +// name prefix in `ListResourcesRequest.NamePrefixes`, any id in +// `ListResourcesRequest.ResourceIDs`, or any id prefix in `ListResourcesRequest.IDPrefixes`. +// - Returns an error if listing fails. +func (s *Server) ListResources(ctx context.Context, request *api.ListResourcesRequest) (*api.ListResourcesResponse, error) { + var ( + resources []*api.Resource + respResources []*api.Resource + err error + byFilters []store.By + by store.By + labels map[string]string + ) + + // andKind is set to true if the Extension filter is not the only filter + // being used. If this is the case, we do not have to compare by strings, + // which could be slow. + var andKind bool + + if request.Filters != nil { + for _, name := range request.Filters.Names { + byFilters = append(byFilters, store.ByName(name)) + } + for _, prefix := range request.Filters.NamePrefixes { + byFilters = append(byFilters, store.ByNamePrefix(prefix)) + } + for _, prefix := range request.Filters.IDPrefixes { + byFilters = append(byFilters, store.ByIDPrefix(prefix)) + } + labels = request.Filters.Labels + if request.Filters.Kind != "" { + // if we're filtering on Extensions, then set this to true. If Kind is + // the _only_ kind of filter, we'll set this to false below. + andKind = true + } + } + + switch len(byFilters) { + case 0: + // NOTE(dperny): currently, filtering using store.ByKind would apply an + // Or operation, which means that filtering by kind would return a + // union. However, for Kind filters, we actually want the + // _intersection_; that is, _only_ objects of the specified kind. we + // could dig into the db code to figure out how to write and use an + // efficient And combinator, but I don't have the time nor expertise to + // do so at the moment. instead, we'll filter by kind after the fact. + // however, if there are no other kinds of filters, and we're ONLY + // listing by Kind, we can set that to be the only filter. + if andKind { + by = store.ByKind(request.Filters.Kind) + andKind = false + } else { + by = store.All + } + case 1: + by = byFilters[0] + default: + by = store.Or(byFilters...) + } + + s.store.View(func(tx store.ReadTx) { + resources, err = store.FindResources(tx, by) + }) + if err != nil { + return nil, err + } + + // filter by label and extension + for _, resource := range resources { + if !filterMatchLabels(resource.Annotations.Labels, labels) { + continue + } + if andKind && resource.Kind != request.Filters.Kind { + continue + } + respResources = append(respResources, resource) + } + + return &api.ListResourcesResponse{Resources: respResources}, nil +} + +// UpdateResource updates the resource with the given `UpdateResourceRequest.Resource.Id` using the given `UpdateResourceRequest.Resource` and returns a `UpdateResourceResponse`. +// - Returns `NotFound` if the Resource with the given `UpdateResourceRequest.Resource.Id` is not found. +// - Returns `InvalidArgument` if the UpdateResourceRequest.Resource.Id` is empty. +// - Returns an error if updating fails. +func (s *Server) UpdateResource(ctx context.Context, request *api.UpdateResourceRequest) (*api.UpdateResourceResponse, error) { + if request.ResourceID == "" || request.ResourceVersion == nil { + return nil, status.Errorf(codes.InvalidArgument, "must include ID and version") + } + var r *api.Resource + err := s.store.Update(func(tx store.Tx) error { + r = store.GetResource(tx, request.ResourceID) + if r == nil { + return status.Errorf(codes.NotFound, "resource %v not found", request.ResourceID) + } + + if request.Annotations != nil { + if r.Annotations.Name != request.Annotations.Name { + return status.Errorf(codes.InvalidArgument, "cannot change resource name") + } + r.Annotations = *request.Annotations + } + r.Meta.Version = *request.ResourceVersion + // only alter the payload if the + if request.Payload != nil { + r.Payload = request.Payload + } + + return store.UpdateResource(tx, r) + }) + switch err { + case store.ErrSequenceConflict: + return nil, status.Errorf(codes.InvalidArgument, "update out of sequence") + case nil: + return &api.UpdateResourceResponse{ + Resource: r, + }, nil + default: + return nil, err + } +} diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/secret.go b/vendor/github.com/docker/swarmkit/manager/controlapi/secret.go index fdcd2c412c686..f3d87d1a7cfca 100644 --- a/vendor/github.com/docker/swarmkit/manager/controlapi/secret.go +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/secret.go @@ -1,6 +1,7 @@ package controlapi import ( + "context" "crypto/subtle" "strings" @@ -10,7 +11,6 @@ import ( "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/state/store" "github.com/sirupsen/logrus" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/service.go b/vendor/github.com/docker/swarmkit/manager/controlapi/service.go index 3912052bf0004..e4db20b17f7a7 100644 --- a/vendor/github.com/docker/swarmkit/manager/controlapi/service.go +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/service.go @@ -1,6 +1,7 @@ package controlapi import ( + "context" "errors" "reflect" "strings" @@ -18,7 +19,6 @@ import ( "github.com/docker/swarmkit/protobuf/ptypes" "github.com/docker/swarmkit/template" gogotypes "github.com/gogo/protobuf/types" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -197,7 +197,7 @@ func validateHealthCheck(hc *api.HealthConfig) error { if err != nil { return err } - if interval != 0 && interval < time.Duration(minimumDuration) { + if interval != 0 && interval < minimumDuration { return status.Errorf(codes.InvalidArgument, "ContainerSpec: Interval in HealthConfig cannot be less than %s", minimumDuration) } } @@ -207,7 +207,7 @@ func validateHealthCheck(hc *api.HealthConfig) error { if err != nil { return err } - if timeout != 0 && timeout < time.Duration(minimumDuration) { + if timeout != 0 && timeout < minimumDuration { return status.Errorf(codes.InvalidArgument, "ContainerSpec: Timeout in HealthConfig cannot be less than %s", minimumDuration) } } @@ -217,7 +217,7 @@ func validateHealthCheck(hc *api.HealthConfig) error { if err != nil { return err } - if sp != 0 && sp < time.Duration(minimumDuration) { + if sp != 0 && sp < minimumDuration { return status.Errorf(codes.InvalidArgument, "ContainerSpec: StartPeriod in HealthConfig cannot be less than %s", minimumDuration) } } @@ -392,6 +392,21 @@ func validateConfigRefsSpec(spec api.TaskSpec) error { return nil } + // check if we're using a config as a CredentialSpec -- if so, we need to + // verify + var ( + credSpecConfig string + credSpecConfigFound bool + ) + if p := container.Privileges; p != nil { + if cs := p.CredentialSpec; cs != nil { + // if there is no config in the credspec, then this will just be + // assigned to emptystring anyway, so we don't need to check + // existence. + credSpecConfig = cs.GetConfig() + } + } + // Keep a map to track all the targets that will be exposed // The string returned is only used for logging. It could as well be struct{}{} existingTargets := make(map[string]string) @@ -421,6 +436,20 @@ func validateConfigRefsSpec(spec api.TaskSpec) error { existingTargets[fileName] = configRef.ConfigName } + + if configRef.GetRuntime() != nil { + if configRef.ConfigID == credSpecConfig { + credSpecConfigFound = true + } + } + } + + if credSpecConfig != "" && !credSpecConfigFound { + return status.Errorf( + codes.InvalidArgument, + "CredentialSpec references config '%s', but that config isn't in config references with RuntimeTarget", + credSpecConfig, + ) } return nil @@ -445,12 +474,32 @@ func (s *Server) validateNetworks(networks []*api.NetworkAttachmentConfig) error func validateMode(s *api.ServiceSpec) error { m := s.GetMode() - switch m.(type) { + switch mode := m.(type) { case *api.ServiceSpec_Replicated: - if int64(m.(*api.ServiceSpec_Replicated).Replicated.Replicas) < 0 { + if int64(mode.Replicated.Replicas) < 0 { return status.Errorf(codes.InvalidArgument, "Number of replicas must be non-negative") } case *api.ServiceSpec_Global: + case *api.ServiceSpec_ReplicatedJob: + // this check shouldn't be required as the point of uint64 is to + // constrain the possible values to positive numbers, but it almost + // certainly is required because there are almost certainly blind casts + // from int64 to uint64, and uint64(-1) is almost certain to crash the + // cluster because of how large it is. + if int64(mode.ReplicatedJob.MaxConcurrent) < 0 { + return status.Errorf( + codes.InvalidArgument, + "Maximum concurrent jobs must not be negative", + ) + } + + if int64(mode.ReplicatedJob.TotalCompletions) < 0 { + return status.Errorf( + codes.InvalidArgument, + "Total completed jobs must not be negative", + ) + } + case *api.ServiceSpec_GlobalJob: default: return status.Errorf(codes.InvalidArgument, "Unrecognized service mode") } @@ -458,6 +507,13 @@ func validateMode(s *api.ServiceSpec) error { return nil } +func validateJob(spec *api.ServiceSpec) error { + if spec.Update != nil { + return status.Errorf(codes.InvalidArgument, "Jobs may not have an update config") + } + return nil +} + func validateServiceSpec(spec *api.ServiceSpec) error { if spec == nil { return status.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) @@ -468,13 +524,32 @@ func validateServiceSpec(spec *api.ServiceSpec) error { if err := validateTaskSpec(spec.Task); err != nil { return err } - if err := validateUpdate(spec.Update); err != nil { + err := validateMode(spec) + if err != nil { return err } - if err := validateEndpointSpec(spec.Endpoint); err != nil { - return err + + // job-mode services are validated differently. most notably, they do not + // have UpdateConfigs, which is why this case statement skips update + // validation. + if isJobSpec(spec) { + if err := validateJob(spec); err != nil { + return err + } + } else { + if err := validateUpdate(spec.Update); err != nil { + return err + } } - return validateMode(spec) + + return validateEndpointSpec(spec.Endpoint) +} + +func isJobSpec(spec *api.ServiceSpec) bool { + mode := spec.GetMode() + _, isGlobalJob := mode.(*api.ServiceSpec_GlobalJob) + _, isReplicatedJob := mode.(*api.ServiceSpec_ReplicatedJob) + return isGlobalJob || isReplicatedJob } // checkPortConflicts does a best effort to find if the passed in spec has port @@ -660,6 +735,12 @@ func (s *Server) CreateService(ctx context.Context, request *api.CreateServiceRe SpecVersion: &api.Version{}, } + if isJobSpec(request.Spec) { + service.JobStatus = &api.JobStatus{ + LastExecution: gogotypes.TimestampNow(), + } + } + if allocator.IsIngressNetworkNeeded(service) { if _, err := allocator.GetIngressNetwork(s.store); err == allocator.ErrNoIngress { return nil, status.Errorf(codes.FailedPrecondition, "service needs ingress network, but no ingress network is present") @@ -680,13 +761,17 @@ func (s *Server) CreateService(ctx context.Context, request *api.CreateServiceRe return store.CreateService(tx, service) }) - if err != nil { + switch err { + case store.ErrNameConflict: + // Enhance the name-confict error to include the service name. The original + // `ErrNameConflict` error-message is included for backward-compatibility + // with older consumers of the API performing string-matching. + return nil, status.Errorf(codes.AlreadyExists, "%s: service %s already exists", err.Error(), request.Spec.Annotations.Name) + case nil: + return &api.CreateServiceResponse{Service: service}, nil + default: return nil, err } - - return &api.CreateServiceResponse{ - Service: service, - }, nil } // GetService returns a Service given a ServiceID. @@ -786,6 +871,13 @@ func (s *Server) UpdateService(ctx context.Context, request *api.UpdateServiceRe service.Meta.Version = *request.ServiceVersion + // if the service has a JobStatus, that means it must be a Job, and we + // should increment the JobIteration + if service.JobStatus != nil { + service.JobStatus.JobIteration.Index = service.JobStatus.JobIteration.Index + 1 + service.JobStatus.LastExecution = gogotypes.TimestampNow() + } + if request.Rollback == api.UpdateServiceRequest_PREVIOUS { if service.PreviousSpec == nil { return status.Errorf(codes.FailedPrecondition, "service %s does not have a previous spec", request.ServiceID) @@ -896,7 +988,12 @@ func (s *Server) ListServices(ctx context.Context, request *api.ListServicesRequ } }) if err != nil { - return nil, err + switch err { + case store.ErrInvalidFindBy: + return nil, status.Errorf(codes.InvalidArgument, err.Error()) + default: + return nil, err + } } if request.Filters != nil { @@ -930,3 +1027,100 @@ func (s *Server) ListServices(ctx context.Context, request *api.ListServicesRequ Services: services, }, nil } + +// ListServiceStatuses returns a `ListServiceStatusesResponse` with the status +// of the requested services, formed by computing the number of running vs +// desired tasks. It is provided as a shortcut or helper method, which allows a +// client to avoid having to calculate this value by listing all Tasks. If any +// service requested does not exist, it will be returned but with empty status +// values. +func (s *Server) ListServiceStatuses(ctx context.Context, req *api.ListServiceStatusesRequest) (*api.ListServiceStatusesResponse, error) { + resp := &api.ListServiceStatusesResponse{} + if req == nil { + return resp, nil + } + + s.store.View(func(tx store.ReadTx) { + for _, id := range req.Services { + status := &api.ListServiceStatusesResponse_ServiceStatus{ + ServiceID: id, + } + // no matter what, add this status to the list. + resp.Statuses = append(resp.Statuses, status) + + tasks, findErr := store.FindTasks(tx, store.ByServiceID(id)) + if findErr != nil { + // if there is another kind of error here (not sure what it + // could be) then still return 0/0 for this service. + continue + } + + // use a boolean to see global vs replicated. this avoids us having to + // iterate the task list twice. + global := false + // jobIteration is the iteration that the Job is currently + // operating on, to distinguish Tasks in old executions from tasks + // in the current one. if nil, service is not a Job + var jobIteration *api.Version + service := store.GetService(tx, id) + // a service might be deleted, but it may still have tasks. in that + // case, we will be using 0 as the desired task count. + if service != nil { + // figure out how many tasks the service requires. for replicated + // services, this is easy: we can just check the replicas field. for + // global services, this is a bit harder and we'll need to do some + // numbercrunchin + if replicated := service.Spec.GetReplicated(); replicated != nil { + status.DesiredTasks = replicated.Replicas + } else if replicatedJob := service.Spec.GetReplicatedJob(); replicatedJob != nil { + status.DesiredTasks = replicatedJob.MaxConcurrent + } else { + // global applies to both GlobalJob and regular Global + global = true + } + + if service.JobStatus != nil { + jobIteration = &service.JobStatus.JobIteration + } + } + + // now, figure out how many tasks are running. Pretty easy, and + // universal across both global and replicated services + for _, task := range tasks { + // if the service is a Job, jobIteration will be non-nil. This + // means we should check if the task belongs to the current job + // iteration. If not, skip accounting the task. + if jobIteration != nil { + if task.JobIteration == nil || task.JobIteration.Index != jobIteration.Index { + continue + } + + // additionally, since we've verified that the service is a + // job and the task belongs to this iteration, we should + // increment CompletedTasks + if task.Status.State == api.TaskStateCompleted { + status.CompletedTasks++ + } + } + if task.Status.State == api.TaskStateRunning { + status.RunningTasks++ + } + + // if the service is global, a shortcut for figuring out the + // number of tasks desired is to look at all tasks, and take a + // count of the ones whose desired state is not Shutdown. + if global && task.DesiredState == api.TaskStateRunning { + status.DesiredTasks++ + } + + // for jobs, this is any task with desired state Completed + // which is not actually in that state. + if global && task.Status.State != api.TaskStateCompleted && task.DesiredState == api.TaskStateCompleted { + status.DesiredTasks++ + } + } + } + }) + + return resp, nil +} diff --git a/vendor/github.com/docker/swarmkit/manager/controlapi/task.go b/vendor/github.com/docker/swarmkit/manager/controlapi/task.go index dc56e7410543c..5f85f9a93c1d2 100644 --- a/vendor/github.com/docker/swarmkit/manager/controlapi/task.go +++ b/vendor/github.com/docker/swarmkit/manager/controlapi/task.go @@ -1,11 +1,12 @@ package controlapi import ( + "context" + "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/api/naming" "github.com/docker/swarmkit/manager/orchestrator" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/vendor/github.com/docker/swarmkit/manager/dispatcher/assignments.go b/vendor/github.com/docker/swarmkit/manager/dispatcher/assignments.go index 5a56348053d72..101c449eddd38 100644 --- a/vendor/github.com/docker/swarmkit/manager/dispatcher/assignments.go +++ b/vendor/github.com/docker/swarmkit/manager/dispatcher/assignments.go @@ -6,6 +6,7 @@ import ( "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/api/equality" "github.com/docker/swarmkit/api/validation" + "github.com/docker/swarmkit/identity" "github.com/docker/swarmkit/manager/drivers" "github.com/docker/swarmkit/manager/state/store" "github.com/sirupsen/logrus" @@ -35,8 +36,10 @@ func newAssignmentSet(log *logrus.Entry, dp *drivers.DriverProvider) *assignment } func assignSecret(a *assignmentSet, readTx store.ReadTx, mapKey typeAndID, t *api.Task) { - a.tasksUsingDependency[mapKey] = make(map[string]struct{}) - secret, err := a.secret(readTx, t, mapKey.id) + if _, exists := a.tasksUsingDependency[mapKey]; !exists { + a.tasksUsingDependency[mapKey] = make(map[string]struct{}) + } + secret, doNotReuse, err := a.secret(readTx, t, mapKey.id) if err != nil { a.log.WithFields(logrus.Fields{ "resource.type": "secret", @@ -45,6 +48,19 @@ func assignSecret(a *assignmentSet, readTx store.ReadTx, mapKey typeAndID, t *ap }).Debug("failed to fetch secret") return } + // If the secret should not be reused for other tasks, give it a unique ID for the task to allow different values for different tasks. + if doNotReuse { + // Give the secret a new ID and mark it as internal + originalSecretID := secret.ID + taskSpecificID := identity.CombineTwoIDs(originalSecretID, t.ID) + secret.ID = taskSpecificID + secret.Internal = true + // Create a new mapKey with the new ID and insert it into the dependencies map for the task. + // This will make the changes map contain an entry with the new ID rather than the original one. + mapKey = typeAndID{objType: mapKey.objType, id: secret.ID} + a.tasksUsingDependency[mapKey] = make(map[string]struct{}) + a.tasksUsingDependency[mapKey][t.ID] = struct{}{} + } a.changes[mapKey] = &api.AssignmentChange{ Assignment: &api.Assignment{ Item: &api.Assignment_Secret{ @@ -104,7 +120,12 @@ func (a *assignmentSet) addTaskDependencies(readTx store.ReadTx, t *api.Task) { secretID := secretRef.SecretID mapKey := typeAndID{objType: api.ResourceType_SECRET, id: secretID} - if len(a.tasksUsingDependency[mapKey]) == 0 { + // This checks for the presence of each task in the dependency map for the + // secret. This is currently only done for secrets since the other types of + // dependencies do not support driver plugins. Arguably, the same task would + // not have the same secret as a dependency more than once, but this check + // makes sure the task only gets the secret assigned once. + if _, exists := a.tasksUsingDependency[mapKey][t.ID]; !exists { assignSecret(a, readTx, mapKey, t) } a.tasksUsingDependency[mapKey][t.ID] = struct{}{} @@ -290,27 +311,29 @@ func (a *assignmentSet) message() api.AssignmentsMessage { } // secret populates the secret value from raft store. For external secrets, the value is populated -// from the secret driver. -func (a *assignmentSet) secret(readTx store.ReadTx, task *api.Task, secretID string) (*api.Secret, error) { +// from the secret driver. The function returns: a secret object; an indication of whether the value +// is to be reused across tasks; and an error if the secret is not found in the store, if the secret +// driver responds with one or if the payload does not pass validation. +func (a *assignmentSet) secret(readTx store.ReadTx, task *api.Task, secretID string) (*api.Secret, bool, error) { secret := store.GetSecret(readTx, secretID) if secret == nil { - return nil, fmt.Errorf("secret not found") + return nil, false, fmt.Errorf("secret not found") } if secret.Spec.Driver == nil { - return secret, nil + return secret, false, nil } d, err := a.dp.NewSecretDriver(secret.Spec.Driver) if err != nil { - return nil, err + return nil, false, err } - value, err := d.Get(&secret.Spec, task) + value, doNotReuse, err := d.Get(&secret.Spec, task) if err != nil { - return nil, err + return nil, false, err } if err := validation.ValidateSecretPayload(value); err != nil { - return nil, err + return nil, false, err } // Assign the secret secret.Spec.Data = value - return secret, nil + return secret, doNotReuse, nil } diff --git a/vendor/github.com/docker/swarmkit/manager/dispatcher/dispatcher.go b/vendor/github.com/docker/swarmkit/manager/dispatcher/dispatcher.go index 569ab26375865..d1db2fdc83c16 100644 --- a/vendor/github.com/docker/swarmkit/manager/dispatcher/dispatcher.go +++ b/vendor/github.com/docker/swarmkit/manager/dispatcher/dispatcher.go @@ -1,6 +1,7 @@ package dispatcher import ( + "context" "fmt" "net" "strconv" @@ -21,7 +22,6 @@ import ( gogotypes "github.com/gogo/protobuf/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -238,7 +238,7 @@ func (d *Dispatcher) Run(ctx context.Context) error { if err != nil { return err } - if err == nil && len(clusters) == 1 { + if len(clusters) == 1 { heartbeatPeriod, err := gogotypes.DurationFromProto(clusters[0].Spec.Dispatcher.HeartbeatPeriod) if err == nil && heartbeatPeriod > 0 { d.config.HeartbeatPeriod = heartbeatPeriod @@ -1090,14 +1090,10 @@ func (d *Dispatcher) moveTasksToOrphaned(nodeID string) error { task.Status.State = api.TaskStateOrphaned } - if err := batch.Update(func(tx store.Tx) error { - err := store.UpdateTask(tx, task) - if err != nil { - return err - } - - return nil - }); err != nil { + err := batch.Update(func(tx store.Tx) error { + return store.UpdateTask(tx, task) + }) + if err != nil { return err } diff --git a/vendor/github.com/docker/swarmkit/manager/dispatcher/nodes.go b/vendor/github.com/docker/swarmkit/manager/dispatcher/nodes.go index cf35bb869aae8..fae6dc5f82c60 100644 --- a/vendor/github.com/docker/swarmkit/manager/dispatcher/nodes.go +++ b/vendor/github.com/docker/swarmkit/manager/dispatcher/nodes.go @@ -156,7 +156,7 @@ func (s *nodeStore) Heartbeat(id, sid string) (time.Duration, error) { return 0, err } period := s.periodChooser.Choose() // base period for node - grace := period * time.Duration(s.gracePeriodMultiplierNormal) + grace := period * s.gracePeriodMultiplierNormal rn.mu.Lock() rn.Heartbeat.Update(grace) rn.Heartbeat.Beat() diff --git a/vendor/github.com/docker/swarmkit/manager/drivers/provider.go b/vendor/github.com/docker/swarmkit/manager/drivers/provider.go index 0d9be6119d1a7..97c36fe73d96b 100644 --- a/vendor/github.com/docker/swarmkit/manager/drivers/provider.go +++ b/vendor/github.com/docker/swarmkit/manager/drivers/provider.go @@ -22,7 +22,7 @@ func (m *DriverProvider) NewSecretDriver(driver *api.Driver) (*SecretDriver, err if m.pluginGetter == nil { return nil, fmt.Errorf("plugin getter is nil") } - if driver == nil && driver.Name == "" { + if driver == nil || driver.Name == "" { return nil, fmt.Errorf("driver specification is nil") } // Search for the specified plugin diff --git a/vendor/github.com/docker/swarmkit/manager/drivers/secrets.go b/vendor/github.com/docker/swarmkit/manager/drivers/secrets.go index 2e7bc392b60a7..642d244690ea1 100644 --- a/vendor/github.com/docker/swarmkit/manager/drivers/secrets.go +++ b/vendor/github.com/docker/swarmkit/manager/drivers/secrets.go @@ -5,6 +5,7 @@ import ( "github.com/docker/docker/pkg/plugingetter" "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/api/naming" ) const ( @@ -25,20 +26,29 @@ func NewSecretDriver(plugin plugingetter.CompatPlugin) *SecretDriver { return &SecretDriver{plugin: plugin} } -// Get gets a secret from the secret provider -func (d *SecretDriver) Get(spec *api.SecretSpec, task *api.Task) ([]byte, error) { +// Get gets a secret from the secret provider. The function returns: the secret value; +// a bool indicating whether the value should be reused across different tasks (defaults to false); +// and an error if either the spec or task are nil, if calling the driver returns an error, or if +// the driver returns an error in the payload. +func (d *SecretDriver) Get(spec *api.SecretSpec, task *api.Task) ([]byte, bool, error) { if spec == nil { - return nil, fmt.Errorf("secret spec is nil") + return nil, false, fmt.Errorf("secret spec is nil") } if task == nil { - return nil, fmt.Errorf("task is nil") + return nil, false, fmt.Errorf("task is nil") } var secretResp SecretsProviderResponse secretReq := &SecretsProviderRequest{ SecretName: spec.Annotations.Name, + SecretLabels: spec.Annotations.Labels, + ServiceID: task.ServiceID, ServiceName: task.ServiceAnnotations.Name, ServiceLabels: task.ServiceAnnotations.Labels, + TaskID: task.ID, + TaskName: naming.Task(task), + TaskImage: task.Spec.GetContainer().Image, + NodeID: task.NodeID, } container := task.Spec.GetContainer() if container != nil { @@ -67,21 +77,27 @@ func (d *SecretDriver) Get(spec *api.SecretSpec, task *api.Task) ([]byte, error) err := d.plugin.Client().Call(SecretsProviderAPI, secretReq, &secretResp) if err != nil { - return nil, err + return nil, false, err } if secretResp.Err != "" { - return nil, fmt.Errorf(secretResp.Err) + return nil, secretResp.DoNotReuse, fmt.Errorf(secretResp.Err) } // Assign the secret value - return secretResp.Value, nil + return secretResp.Value, secretResp.DoNotReuse, nil } // SecretsProviderRequest is the secrets provider request. type SecretsProviderRequest struct { SecretName string `json:",omitempty"` // SecretName is the name of the secret to request from the plugin + SecretLabels map[string]string `json:",omitempty"` // SecretLabels capture environment names and other metadata pertaining to the secret ServiceHostname string `json:",omitempty"` // ServiceHostname is the hostname of the service, can be used for x509 certificate + ServiceID string `json:",omitempty"` // ServiceID is the name of the service that requested the secret ServiceName string `json:",omitempty"` // ServiceName is the name of the service that requested the secret - ServiceLabels map[string]string `json:",omitempty"` // ServiceLabels capture environment names and other metadata + ServiceLabels map[string]string `json:",omitempty"` // ServiceLabels capture environment names and other metadata pertaining to the service + TaskID string `json:",omitempty"` // TaskID is the ID of the task that the secret will be assigned to + TaskName string `json:",omitempty"` // TaskName is the name of the task that the secret will be assigned to + TaskImage string `json:",omitempty"` // TaskName is the image of the task that the secret will be assigned to + NodeID string `json:",omitempty"` // NodeID is the ID of the node that the task will be executed on ServiceEndpointSpec *EndpointSpec `json:",omitempty"` // ServiceEndpointSpec holds the specification for endpoints } @@ -89,6 +105,11 @@ type SecretsProviderRequest struct { type SecretsProviderResponse struct { Value []byte `json:",omitempty"` // Value is the value of the secret Err string `json:",omitempty"` // Err is the error response of the plugin + + // DoNotReuse indicates that the secret returned from this request should + // only be used for one task, and any further tasks should call the secret + // driver again. + DoNotReuse bool `json:",omitempty"` } // EndpointSpec represents the spec of an endpoint. diff --git a/vendor/github.com/docker/swarmkit/manager/health/health.go b/vendor/github.com/docker/swarmkit/manager/health/health.go index ef6658b09ddd7..d75cbf4b60234 100644 --- a/vendor/github.com/docker/swarmkit/manager/health/health.go +++ b/vendor/github.com/docker/swarmkit/manager/health/health.go @@ -8,10 +8,10 @@ package health import ( + "context" "sync" "github.com/docker/swarmkit/api" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/vendor/github.com/docker/swarmkit/manager/keymanager/keymanager.go b/vendor/github.com/docker/swarmkit/manager/keymanager/keymanager.go index e61979cb36179..7a5d7bf14d784 100644 --- a/vendor/github.com/docker/swarmkit/manager/keymanager/keymanager.go +++ b/vendor/github.com/docker/swarmkit/manager/keymanager/keymanager.go @@ -6,6 +6,7 @@ package keymanager // which is used to exchange service discovery and overlay network control // plane information. It can also be used to encrypt overlay data traffic. import ( + "context" cryptorand "crypto/rand" "encoding/binary" "sync" @@ -15,7 +16,6 @@ import ( "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/state/store" "github.com/pkg/errors" - "golang.org/x/net/context" ) const ( diff --git a/vendor/github.com/docker/swarmkit/manager/logbroker/broker.go b/vendor/github.com/docker/swarmkit/manager/logbroker/broker.go index dfc898e0702e8..c19438a27aa5f 100644 --- a/vendor/github.com/docker/swarmkit/manager/logbroker/broker.go +++ b/vendor/github.com/docker/swarmkit/manager/logbroker/broker.go @@ -1,6 +1,7 @@ package logbroker import ( + "context" "errors" "fmt" "io" @@ -14,7 +15,6 @@ import ( "github.com/docker/swarmkit/manager/state/store" "github.com/docker/swarmkit/watch" "github.com/sirupsen/logrus" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/vendor/github.com/docker/swarmkit/manager/logbroker/subscription.go b/vendor/github.com/docker/swarmkit/manager/logbroker/subscription.go index b9c9c7b403839..45295bb40aded 100644 --- a/vendor/github.com/docker/swarmkit/manager/logbroker/subscription.go +++ b/vendor/github.com/docker/swarmkit/manager/logbroker/subscription.go @@ -1,6 +1,7 @@ package logbroker import ( + "context" "fmt" "strings" "sync" @@ -11,7 +12,6 @@ import ( "github.com/docker/swarmkit/manager/state" "github.com/docker/swarmkit/manager/state/store" "github.com/docker/swarmkit/watch" - "golang.org/x/net/context" ) type subscription struct { @@ -204,20 +204,31 @@ func (s *subscription) watch(ch <-chan events.Event) error { } add := func(t *api.Task) { + // this mutex does not have a deferred unlock, because there is work + // we need to do after we release it. s.mu.Lock() - defer s.mu.Unlock() // Un-allocated task. if t.NodeID == "" { s.pendingTasks[t.ID] = struct{}{} + s.mu.Unlock() return } delete(s.pendingTasks, t.ID) if _, ok := s.nodes[t.NodeID]; !ok { s.nodes[t.NodeID] = struct{}{} + + s.mu.Unlock() + + // if we try to call Publish before we release the lock, we can end + // up in a situation where the receiver is trying to acquire a read + // lock on it. it's hard to explain. s.changed.Publish(s) + return } + + s.mu.Unlock() } for { diff --git a/vendor/github.com/docker/swarmkit/manager/manager.go b/vendor/github.com/docker/swarmkit/manager/manager.go index 106c8e2b585c2..2eaaeaa02554d 100644 --- a/vendor/github.com/docker/swarmkit/manager/manager.go +++ b/vendor/github.com/docker/swarmkit/manager/manager.go @@ -1,8 +1,10 @@ package manager import ( + "context" "crypto/tls" "fmt" + "math" "net" "os" "path/filepath" @@ -31,6 +33,7 @@ import ( "github.com/docker/swarmkit/manager/metrics" "github.com/docker/swarmkit/manager/orchestrator/constraintenforcer" "github.com/docker/swarmkit/manager/orchestrator/global" + "github.com/docker/swarmkit/manager/orchestrator/jobs" "github.com/docker/swarmkit/manager/orchestrator/replicated" "github.com/docker/swarmkit/manager/orchestrator/taskreaper" "github.com/docker/swarmkit/manager/resourceapi" @@ -45,7 +48,6 @@ import ( grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) @@ -145,6 +147,7 @@ type Manager struct { watchServer *watchapi.Server replicatedOrchestrator *replicated.Orchestrator globalOrchestrator *global.Orchestrator + jobsOrchestrator *jobs.Orchestrator taskReaper *taskreaper.TaskReaper constraintEnforcer *constraintenforcer.ConstraintEnforcer scheduler *scheduler.Scheduler @@ -680,6 +683,9 @@ func (m *Manager) Stop(ctx context.Context, clearData bool) { if m.globalOrchestrator != nil { m.globalOrchestrator.Stop() } + if m.jobsOrchestrator != nil { + m.jobsOrchestrator.Stop() + } if m.taskReaper != nil { m.taskReaper.Stop() } @@ -758,6 +764,7 @@ func (m *Manager) updateKEK(ctx context.Context, cluster *api.Cluster) error { func(addr string, timeout time.Duration) (net.Conn, error) { return xnet.DialTimeoutLocal(addr, timeout) }), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32)), ) if err != nil { logger.WithError(err).Error("failed to connect to local manager socket after locking the cluster") @@ -942,23 +949,30 @@ func (m *Manager) becomeLeader(ctx context.Context) { rootCA, m.config.FIPS, nil, + 0, 0) // If defaultAddrPool is valid we update cluster object with new value - if m.config.NetworkConfig != nil && m.config.NetworkConfig.DefaultAddrPool != nil { - clusterObj.DefaultAddressPool = m.config.NetworkConfig.DefaultAddrPool - clusterObj.SubnetSize = m.config.NetworkConfig.SubnetSize - } + // If VXLANUDPPort is not 0 then we call update cluster object with new value + if m.config.NetworkConfig != nil { + if m.config.NetworkConfig.DefaultAddrPool != nil { + clusterObj.DefaultAddressPool = m.config.NetworkConfig.DefaultAddrPool + clusterObj.SubnetSize = m.config.NetworkConfig.SubnetSize + } + if m.config.NetworkConfig.VXLANUDPPort != 0 { + clusterObj.VXLANUDPPort = m.config.NetworkConfig.VXLANUDPPort + } + } err := store.CreateCluster(tx, clusterObj) - if err != nil && err != store.ErrExist { + if err != nil && (err != store.ErrExist || err != store.ErrNameConflict) { log.G(ctx).WithError(err).Errorf("error creating cluster object") } // Add Node entry for ourself, if one // doesn't exist already. - freshCluster := nil == store.CreateNode(tx, managerNode(nodeID, m.config.Availability)) + freshCluster := nil == store.CreateNode(tx, managerNode(nodeID, m.config.Availability, clusterObj.VXLANUDPPort)) if freshCluster { // This is a fresh swarm cluster. Add to store now any initial @@ -985,28 +999,37 @@ func (m *Manager) becomeLeader(ctx context.Context) { m.replicatedOrchestrator = replicated.NewReplicatedOrchestrator(s) m.constraintEnforcer = constraintenforcer.New(s) m.globalOrchestrator = global.NewGlobalOrchestrator(s) + m.jobsOrchestrator = jobs.NewOrchestrator(s) m.taskReaper = taskreaper.New(s) m.scheduler = scheduler.New(s) m.keyManager = keymanager.New(s, keymanager.DefaultConfig()) m.roleManager = newRoleManager(s, m.raftNode) // TODO(stevvooe): Allocate a context that can be used to - // shutdown underlying manager processes when leadership is + // shutdown underlying manager processes when leadership isTestUpdaterRollback // lost. // If DefaultAddrPool is null, Read from store and check if // DefaultAddrPool info is stored in cluster object - if m.config.NetworkConfig == nil || m.config.NetworkConfig.DefaultAddrPool == nil { + // If VXLANUDPPort is 0, read it from the store - cluster object + if m.config.NetworkConfig == nil || m.config.NetworkConfig.DefaultAddrPool == nil || m.config.NetworkConfig.VXLANUDPPort == 0 { var cluster *api.Cluster s.View(func(tx store.ReadTx) { cluster = store.GetCluster(tx, clusterID) }) if cluster.DefaultAddressPool != nil { - for _, address := range cluster.DefaultAddressPool { - m.config.NetworkConfig.DefaultAddrPool = append(m.config.NetworkConfig.DefaultAddrPool, address) + if m.config.NetworkConfig == nil { + m.config.NetworkConfig = &cnmallocator.NetworkConfig{} } + m.config.NetworkConfig.DefaultAddrPool = append(m.config.NetworkConfig.DefaultAddrPool, cluster.DefaultAddressPool...) m.config.NetworkConfig.SubnetSize = cluster.SubnetSize } + if cluster.VXLANUDPPort != 0 { + if m.config.NetworkConfig == nil { + m.config.NetworkConfig = &cnmallocator.NetworkConfig{} + } + m.config.NetworkConfig.VXLANUDPPort = cluster.VXLANUDPPort + } } m.allocator, err = allocator.New(s, m.config.PluginGetter, m.config.NetworkConfig) @@ -1026,7 +1049,16 @@ func (m *Manager) becomeLeader(ctx context.Context) { go func(d *dispatcher.Dispatcher) { // Initialize the dispatcher. - d.Init(m.raftNode, dispatcher.DefaultConfig(), drivers.New(m.config.PluginGetter), m.config.SecurityConfig) + var cluster *api.Cluster + s.View(func(tx store.ReadTx) { + cluster = store.GetCluster(tx, clusterID) + }) + var defaultConfig = dispatcher.DefaultConfig() + heartbeatPeriod, err := gogotypes.DurationFromProto(cluster.Spec.Dispatcher.HeartbeatPeriod) + if err == nil { + defaultConfig.HeartbeatPeriod = heartbeatPeriod + } + d.Init(m.raftNode, defaultConfig, drivers.New(m.config.PluginGetter), m.config.SecurityConfig) if err := d.Run(ctx); err != nil { log.G(ctx).WithError(err).Error("Dispatcher exited with an error") } @@ -1073,6 +1105,11 @@ func (m *Manager) becomeLeader(ctx context.Context) { } }(m.replicatedOrchestrator) + go func(orchestrator *jobs.Orchestrator) { + // jobs orchestrator does not return errors. + orchestrator.Run(ctx) + }(m.jobsOrchestrator) + go func(globalOrchestrator *global.Orchestrator) { if err := globalOrchestrator.Run(ctx); err != nil { log.G(ctx).WithError(err).Error("global orchestrator exited with an error") @@ -1133,7 +1170,8 @@ func defaultClusterObject( rootCA *ca.RootCA, fips bool, defaultAddressPool []string, - subnetSize uint32) *api.Cluster { + subnetSize uint32, + vxlanUDPPort uint32) *api.Cluster { var caKey []byte if rcaSigner, err := rootCA.Signer(); err == nil { caKey = rcaSigner.Key @@ -1168,11 +1206,12 @@ func defaultClusterObject( FIPS: fips, DefaultAddressPool: defaultAddressPool, SubnetSize: subnetSize, + VXLANUDPPort: vxlanUDPPort, } } // managerNode creates a new node with NodeRoleManager role. -func managerNode(nodeID string, availability api.NodeSpec_Availability) *api.Node { +func managerNode(nodeID string, availability api.NodeSpec_Availability, vxlanPort uint32) *api.Node { return &api.Node{ ID: nodeID, Certificate: api.Certificate{ @@ -1187,6 +1226,7 @@ func managerNode(nodeID string, availability api.NodeSpec_Availability) *api.Nod Membership: api.NodeMembershipAccepted, Availability: availability, }, + VXLANUDPPort: vxlanPort, } } @@ -1204,12 +1244,8 @@ func newIngressNetwork() *api.Network { }, DriverConfig: &api.Driver{}, IPAM: &api.IPAMOptions{ - Driver: &api.Driver{}, - Configs: []*api.IPAMConfig{ - { - Subnet: "10.255.0.0/16", - }, - }, + Driver: &api.Driver{}, + Configs: []*api.IPAMConfig{}, }, }, } diff --git a/vendor/github.com/docker/swarmkit/manager/metrics/collector.go b/vendor/github.com/docker/swarmkit/manager/metrics/collector.go index 384743707d6fc..5539a898ca9ef 100644 --- a/vendor/github.com/docker/swarmkit/manager/metrics/collector.go +++ b/vendor/github.com/docker/swarmkit/manager/metrics/collector.go @@ -188,7 +188,6 @@ func (c *Collector) handleNodeEvent(event events.Event) { if newNode != nil { nodesMetric.WithValues(strings.ToLower(newNode.Status.State.String())).Inc(1) } - return } func (c *Collector) handleTaskEvent(event events.Event) { @@ -218,8 +217,6 @@ func (c *Collector) handleTaskEvent(event events.Event) { strings.ToLower(newTask.Status.State.String()), ).Inc(1) } - - return } func (c *Collector) handleServiceEvent(event events.Event) { diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/constraintenforcer/constraint_enforcer.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/constraintenforcer/constraint_enforcer.go index 7aa7651db726c..6b179de3f7078 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/constraintenforcer/constraint_enforcer.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/constraintenforcer/constraint_enforcer.go @@ -76,8 +76,22 @@ func (ce *ConstraintEnforcer) rejectNoncompliantTasks(node *api.Node) { err error ) + services := map[string]*api.Service{} ce.store.View(func(tx store.ReadTx) { tasks, err = store.FindTasks(tx, store.ByNodeID(node.ID)) + if err != nil { + return + } + + // Deduplicate service IDs using the services map. It's okay for the + // values to be nil for now, we will look them up from the store next. + for _, task := range tasks { + services[task.ServiceID] = nil + } + + for serviceID := range services { + services[serviceID] = store.GetService(tx, serviceID) + } }) if err != nil { @@ -101,14 +115,48 @@ func (ce *ConstraintEnforcer) rejectNoncompliantTasks(node *api.Node) { // to remove the most resource-intensive tasks. loop: for _, t := range tasks { - if t.DesiredState < api.TaskStateAssigned || t.DesiredState > api.TaskStateRunning { + if t.DesiredState < api.TaskStateAssigned || t.DesiredState > api.TaskStateCompleted { continue } - // Ensure that the task still meets scheduling - // constraints. - if t.Spec.Placement != nil && len(t.Spec.Placement.Constraints) != 0 { - constraints, _ := constraint.Parse(t.Spec.Placement.Constraints) + // Ensure that the node still satisfies placement constraints. + // NOTE: If the task is associacted with a service then we must use the + // constraints from the current service spec rather than the + // constraints from the task spec because they may be outdated. This + // will happen if the service was previously updated in a way which + // only changes the placement constraints and the node matched the + // placement constraints both before and after that update. In the case + // of such updates, the tasks are not considered "dirty" and are not + // restarted but it will mean that the task spec's placement + // constraints are outdated. Consider this example: + // - A service is created with no constraints and a task is scheduled + // to a node. + // - The node is updated to add a label, this doesn't affect the task + // on that node because it has no constraints. + // - The service is updated to add a node label constraint which + // matches the label which was just added to the node. The updater + // does not shut down the task because the only the constraints have + // changed and the node still matches the updated constraints. + // - The node is updated to remove the node label. The node no longer + // satisfies the placement constraints of the service, so the task + // should be shutdown. However, the task's spec still has the + // original and outdated constraints (that are still satisfied by + // the node). If we used those original constraints then the task + // would incorrectly not be removed. This is why the constraints + // from the service spec should be used instead. + var placement *api.Placement + if service := services[t.ServiceID]; service != nil { + // This task is associated with a service, so we use the service's + // current placement constraints. + placement = service.Spec.Task.Placement + } else { + // This task is not associated with a service (or the service no + // longer exists), so we use the placement constraints from the + // original task spec. + placement = t.Spec.Placement + } + if placement != nil && len(placement.Constraints) > 0 { + constraints, _ := constraint.Parse(placement.Constraints) if !constraint.NodeMatches(constraints, node) { removeTasks[t.ID] = t continue @@ -147,7 +195,7 @@ loop: for _, t := range removeTasks { err := batch.Update(func(tx store.Tx) error { t = store.GetTask(tx, t.ID) - if t == nil || t.DesiredState > api.TaskStateRunning { + if t == nil || t.DesiredState > api.TaskStateCompleted { return nil } diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/global/global.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/global/global.go index 2b20813ce2af3..715781e817118 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/global/global.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/global/global.go @@ -1,6 +1,8 @@ package global import ( + "context" + "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/constraint" @@ -9,7 +11,6 @@ import ( "github.com/docker/swarmkit/manager/orchestrator/taskinit" "github.com/docker/swarmkit/manager/orchestrator/update" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" ) type globalService struct { @@ -585,11 +586,3 @@ func (g *Orchestrator) SlotTuple(t *api.Task) orchestrator.SlotTuple { NodeID: t.NodeID, } } - -func isTaskCompleted(t *api.Task, restartPolicy api.RestartPolicy_RestartCondition) bool { - if t == nil || t.DesiredState <= api.TaskStateRunning { - return false - } - return restartPolicy == api.RestartOnNone || - (restartPolicy == api.RestartOnFailure && t.Status.State == api.TaskStateCompleted) -} diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/jobs/global/reconciler.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/jobs/global/reconciler.go new file mode 100644 index 0000000000000..00e95804c36cd --- /dev/null +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/jobs/global/reconciler.go @@ -0,0 +1,301 @@ +package global + +import ( + "context" + + "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/manager/constraint" + "github.com/docker/swarmkit/manager/orchestrator" + "github.com/docker/swarmkit/manager/state/store" +) + +// restartSupervisor is an interface representing the methods from the +// restart.SupervisorInterface that are actually needed by the reconciler. This +// more limited interface allows us to write a less ugly fake for unit testing. +type restartSupervisor interface { + Restart(context.Context, store.Tx, *api.Cluster, *api.Service, api.Task) error +} + +// Reconciler is an object that manages reconciliation of global jobs. It is +// blocking and non-asynchronous, for ease of testing. It implements the +// Reconciler interface from the orchestrator package above it, and the +// taskinit.InitHandler interface. +type Reconciler struct { + store *store.MemoryStore + + restart restartSupervisor +} + +// NewReconciler creates a new global job reconciler. +func NewReconciler(store *store.MemoryStore, restart restartSupervisor) *Reconciler { + return &Reconciler{ + store: store, + restart: restart, + } +} + +// ReconcileService reconciles one global job service. +func (r *Reconciler) ReconcileService(id string) error { + var ( + service *api.Service + cluster *api.Cluster + tasks []*api.Task + nodes []*api.Node + viewErr error + ) + + // we need to first get the latest iteration of the service, its tasks, and + // the nodes in the cluster. + r.store.View(func(tx store.ReadTx) { + service = store.GetService(tx, id) + if service == nil { + return + } + + // getting tasks with FindTasks should only return an error if we've + // made a mistake coding; there's no user-input or even reasonable + // system state that can cause it. If it returns an error, we'll just + // panic and crash. + tasks, viewErr = store.FindTasks(tx, store.ByServiceID(id)) + if viewErr != nil { + return + } + + // same as with FindTasks + nodes, viewErr = store.FindNodes(tx, store.All) + if viewErr != nil { + return + } + + clusters, _ := store.FindClusters(tx, store.All) + if len(clusters) == 1 { + cluster = clusters[0] + } else if len(clusters) > 1 { + panic("there should never be more than one cluster object") + } + }) + + if viewErr != nil { + return viewErr + } + + // the service may be nil if the service has been deleted before we entered + // the View. + if service == nil { + return nil + } + + if service.JobStatus == nil { + service.JobStatus = &api.JobStatus{} + } + + // we need to compute the constraints on the service so we know which nodes + // to schedule it on + var constraints []constraint.Constraint + if service.Spec.Task.Placement != nil && len(service.Spec.Task.Placement.Constraints) != 0 { + // constraint.Parse does return an error, but we don't need to check + // it, because it was already checked when the service was created or + // updated. + constraints, _ = constraint.Parse(service.Spec.Task.Placement.Constraints) + } + + var candidateNodes []string + var invalidNodes []string + for _, node := range nodes { + // instead of having a big ugly multi-line boolean expression in the + // if-statement, we'll have several if-statements, and bail out of + // this loop iteration with continue if the node is not acceptable + if !constraint.NodeMatches(constraints, node) { + continue + } + + // if a node is invalid, we should remove any tasks that might be on it + if orchestrator.InvalidNode(node) { + invalidNodes = append(invalidNodes, node.ID) + continue + } + + if node.Spec.Availability != api.NodeAvailabilityActive { + continue + } + if node.Status.State != api.NodeStatus_READY { + continue + } + // you can append to a nil slice and get a non-nil slice, which is + // pretty slick. + candidateNodes = append(candidateNodes, node.ID) + } + + // now, we have a list of all nodes that match constraints. it's time to + // match running tasks to the nodes. we need to identify all nodes that + // need new tasks, which is any node that doesn't have a task of this job + // iteration. trade some space for some time by building a node ID to task + // ID mapping, so that we're just doing 2x linear operation, instead of a + // quadratic operation. + nodeToTask := map[string]string{} + // additionally, while we're iterating through tasks, if any of those tasks + // are failed, we'll hand them to the restart supervisor to handle + restartTasks := []string{} + // and if there are any tasks belonging to old job iterations, set them to + // be removed + removeTasks := []string{} + for _, task := range tasks { + // match all tasks belonging to this job iteration which are in desired + // state completed, including failed tasks. We only want to create + // tasks for nodes on which there are no existing tasks. + if task.JobIteration != nil { + if task.JobIteration.Index == service.JobStatus.JobIteration.Index && + task.DesiredState <= api.TaskStateCompleted { + // we already know the task is desired to be executing (because its + // desired state is Completed). Check here to see if it's already + // failed, so we can restart it + if task.Status.State > api.TaskStateCompleted { + restartTasks = append(restartTasks, task.ID) + } + nodeToTask[task.NodeID] = task.ID + } + + if task.JobIteration.Index != service.JobStatus.JobIteration.Index { + if task.DesiredState != api.TaskStateRemove { + removeTasks = append(removeTasks, task.ID) + } + } + } + } + + return r.store.Batch(func(batch *store.Batch) error { + // first, create any new tasks required. + for _, node := range candidateNodes { + // check if there is a task for this node ID. If not, then we need + // to create one. + if _, ok := nodeToTask[node]; !ok { + if err := batch.Update(func(tx store.Tx) error { + // if the node does not already have a running or completed + // task, create a task for this node. + task := orchestrator.NewTask(cluster, service, 0, node) + task.JobIteration = &service.JobStatus.JobIteration + task.DesiredState = api.TaskStateCompleted + return store.CreateTask(tx, task) + }); err != nil { + return err + } + } + } + + // then, restart any tasks that are failed + for _, taskID := range restartTasks { + if err := batch.Update(func(tx store.Tx) error { + // get the latest version of the task for the restart + t := store.GetTask(tx, taskID) + // if it's deleted, nothing to do + if t == nil { + return nil + } + + // if it's not still desired to be running, then don't restart + // it. + if t.DesiredState > api.TaskStateCompleted { + return nil + } + + // Finally, restart it + // TODO(dperny): pass in context to ReconcileService, so we can + // pass it in here. + return r.restart.Restart(context.Background(), tx, cluster, service, *t) + }); err != nil { + // TODO(dperny): probably should log like in the other + // orchestrators instead of returning here. + return err + } + } + + // remove tasks that need to be removed + for _, taskID := range removeTasks { + if err := batch.Update(func(tx store.Tx) error { + t := store.GetTask(tx, taskID) + if t == nil { + return nil + } + + if t.DesiredState == api.TaskStateRemove { + return nil + } + + t.DesiredState = api.TaskStateRemove + return store.UpdateTask(tx, t) + }); err != nil { + return err + } + } + + // finally, shut down any tasks on invalid nodes + for _, nodeID := range invalidNodes { + if taskID, ok := nodeToTask[nodeID]; ok { + if err := batch.Update(func(tx store.Tx) error { + t := store.GetTask(tx, taskID) + if t == nil { + return nil + } + // if the task is still desired to be running, and is still + // actually, running, then it still needs to be shut down. + if t.DesiredState > api.TaskStateCompleted || t.Status.State <= api.TaskStateRunning { + t.DesiredState = api.TaskStateShutdown + return store.UpdateTask(tx, t) + } + return nil + }); err != nil { + return err + } + } + } + return nil + }) +} + +// IsRelatedService returns true if the task is a global job. This method +// fulfills the taskinit.InitHandler interface. Because it is just a wrapper +// around a well-tested function call, it has no tests of its own. +func (r *Reconciler) IsRelatedService(service *api.Service) bool { + return orchestrator.IsGlobalJob(service) +} + +// FixTask validates that a task is compliant with the rest of the cluster +// state, and fixes it if it's not. This covers some main scenarios: +// +// * The node that the task is running on is now paused or drained. we do not +// need to check if the node still meets constraints -- that is the purview +// of the constraint enforcer. +// * The task has failed and needs to be restarted. +// +// This implements the FixTask method of the taskinit.InitHandler interface. +func (r *Reconciler) FixTask(ctx context.Context, batch *store.Batch, t *api.Task) { + // tasks already desired to be shut down need no action. + if t.DesiredState > api.TaskStateCompleted { + return + } + + batch.Update(func(tx store.Tx) error { + node := store.GetNode(tx, t.NodeID) + // if the node is no longer a valid node for this task, we need to shut + // it down + if orchestrator.InvalidNode(node) { + task := store.GetTask(tx, t.ID) + if task != nil && task.DesiredState < api.TaskStateShutdown { + task.DesiredState = api.TaskStateShutdown + return store.UpdateTask(tx, task) + } + } + // we will reconcile all services after fixing the tasks, so we don't + // need to restart tasks right now; we'll do so after this. + return nil + }) +} + +// SlotTuple returns a slot tuple representing this task. It implements the +// taskinit.InitHandler interface. +func (r *Reconciler) SlotTuple(t *api.Task) orchestrator.SlotTuple { + return orchestrator.SlotTuple{ + ServiceID: t.ServiceID, + NodeID: t.NodeID, + } +} diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/jobs/orchestrator.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/jobs/orchestrator.go new file mode 100644 index 0000000000000..d244d3c65b1cd --- /dev/null +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/jobs/orchestrator.go @@ -0,0 +1,250 @@ +package jobs + +import ( + "context" + "sync" + + "github.com/docker/go-events" + + "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/log" + "github.com/docker/swarmkit/manager/orchestrator" + "github.com/docker/swarmkit/manager/orchestrator/jobs/global" + "github.com/docker/swarmkit/manager/orchestrator/jobs/replicated" + "github.com/docker/swarmkit/manager/orchestrator/restart" + "github.com/docker/swarmkit/manager/orchestrator/taskinit" + "github.com/docker/swarmkit/manager/state/store" +) + +// Reconciler is the type that holds the reconciliation logic for the +// orchestrator. It exists so that the logic of actually reconciling and +// writing to the store is separated from the orchestrator, to make the event +// handling logic in the orchestrator easier to test. +type Reconciler interface { + taskinit.InitHandler + + ReconcileService(id string) error +} + +// Orchestrator is the combined orchestrator controlling both Global and +// Replicated Jobs. Initially, these job types were two separate orchestrators, +// like the Replicated and Global orchestrators. However, it became apparent +// that because of the simplicity of Jobs as compared to Services, one combined +// orchestrator suffices for both job types. +type Orchestrator struct { + store *store.MemoryStore + + // two reconcilers, one for each service type + + replicatedReconciler Reconciler + globalReconciler Reconciler + + // startOnce is a function that stops the orchestrator from being started + // multiple times. + startOnce sync.Once + + // restartSupervisor is the component that handles restarting tasks + restartSupervisor restart.SupervisorInterface + + // stopChan is a channel that is closed to signal the orchestrator to stop + // running + stopChan chan struct{} + // stopOnce is used to ensure that stopChan can only be closed once, just + // in case some freak accident causes subsequent calls to Stop. + stopOnce sync.Once + // doneChan is closed when the orchestrator actually stops running + doneChan chan struct{} + + // checkTasksFunc is a variable that hold taskinit.CheckTasks, but allows + // swapping it out in testing. + checkTasksFunc func(context.Context, *store.MemoryStore, store.ReadTx, taskinit.InitHandler, restart.SupervisorInterface) error + + // the watchChan and watchCancel provide the event stream + watchChan chan events.Event + watchCancel func() +} + +func NewOrchestrator(store *store.MemoryStore) *Orchestrator { + return &Orchestrator{ + store: store, + stopChan: make(chan struct{}), + doneChan: make(chan struct{}), + } +} + +// Run runs the Orchestrator reconciliation loop. It takes a context as an +// argument, but canceling this context will not stop the routine; this context +// is only for passing in logging information. Call Stop to stop the +// Orchestrator +func (o *Orchestrator) Run(ctx context.Context) { + o.startOnce.Do(func() { o.run(ctx) }) +} + +// init runs the once-off initialization logic for the orchestrator. This +// includes initializing the sub-components, starting the channel watch, and +// running the initial reconciliation pass. this runs as part of the run +// method, but is broken out for the purpose of testing. +func (o *Orchestrator) init(ctx context.Context) { + var ( + services []*api.Service + ) + + // there are several components to the Orchestrator that are interfaces + // designed to be swapped out in testing. in production, these fields will + // all be unset, and be initialized here. in testing, we will set fakes, + // and this initialization will be skipped. + + if o.restartSupervisor == nil { + o.restartSupervisor = restart.NewSupervisor(o.store) + } + + if o.replicatedReconciler == nil { + // the cluster might be nil, but that doesn't matter. + o.replicatedReconciler = replicated.NewReconciler(o.store, o.restartSupervisor) + } + + if o.globalReconciler == nil { + o.globalReconciler = global.NewReconciler(o.store, o.restartSupervisor) + } + + if o.checkTasksFunc == nil { + o.checkTasksFunc = taskinit.CheckTasks + } + + o.watchChan, o.watchCancel, _ = store.ViewAndWatch(o.store, func(tx store.ReadTx) error { + services, _ = store.FindServices(tx, store.All) + return nil + }) + + // checkTasksFunc is used to resume any in-progress restarts that were + // interrupted by a leadership change. In other orchestrators, this + // additionally queues up some tasks to be restarted. However, the jobs + // orchestrator will make a reconciliation pass across all services + // immediately after this, and so does not need to restart any tasks; they + // will be restarted during this pass. + // + // we cannot call o.checkTasksFunc inside of store.ViewAndWatch above. + // despite taking a callback with a ReadTx, it actually performs an Update, + // which acquires a lock and will result in a deadlock. instead, do + // o.checkTasksFunc here. + o.store.View(func(tx store.ReadTx) { + o.checkTasksFunc(ctx, o.store, tx, o.replicatedReconciler, o.restartSupervisor) + o.checkTasksFunc(ctx, o.store, tx, o.globalReconciler, o.restartSupervisor) + }) + + for _, service := range services { + if orchestrator.IsReplicatedJob(service) { + if err := o.replicatedReconciler.ReconcileService(service.ID); err != nil { + log.G(ctx).WithField( + "service.id", service.ID, + ).WithError(err).Error("error reconciling replicated job") + } + } + + if orchestrator.IsGlobalJob(service) { + if err := o.globalReconciler.ReconcileService(service.ID); err != nil { + log.G(ctx).WithField( + "service.id", service.ID, + ).WithError(err).Error("error reconciling global job") + } + } + } +} + +// run provides the actual meat of the the run operation. The call to run is +// made inside of Run, and is enclosed in a sync.Once to stop this from being +// called multiple times +func (o *Orchestrator) run(ctx context.Context) { + ctx = log.WithModule(ctx, "orchestrator/jobs") + + // closing doneChan should be the absolute last thing that happens in this + // method, and so should be the absolute first thing we defer. + defer close(o.doneChan) + + o.init(ctx) + defer o.watchCancel() + + for { + // first, before taking any action, see if we should stop the + // orchestrator. if both the stop channel and the watch channel are + // available to read, the channel that gets read is picked at random, + // but we always want to stop if it's possible. + select { + case <-o.stopChan: + return + default: + } + + select { + case event := <-o.watchChan: + o.handleEvent(ctx, event) + case <-o.stopChan: + // we also need to check for stop in here, in case there are no + // updates to cause the loop to turn over. + return + } + } +} + +// handle event does the logic of handling one event message and calling the +// reconciler as needed. by handling the event logic in this function, we can +// make an end-run around the run-loop and avoid being at the mercy of the go +// scheduler when testing the orchestrator. +func (o *Orchestrator) handleEvent(ctx context.Context, event events.Event) { + var ( + service *api.Service + task *api.Task + ) + + switch ev := event.(type) { + case api.EventCreateService: + service = ev.Service + case api.EventUpdateService: + service = ev.Service + case api.EventUpdateTask: + task = ev.Task + } + + // if this is a task event, we should check if it means the service + // should be reconciled. + if task != nil { + // only bother with all this if the task has entered a terminal + // state and we don't want that to have happened. + if task.Status.State > api.TaskStateRunning && task.DesiredState <= api.TaskStateCompleted { + o.store.View(func(tx store.ReadTx) { + // if for any reason the service ID is invalid, then + // service will just be nil and nothing needs to be + // done + service = store.GetService(tx, task.ServiceID) + }) + } + } + + if orchestrator.IsReplicatedJob(service) { + if err := o.replicatedReconciler.ReconcileService(service.ID); err != nil { + log.G(ctx).WithField( + "service.id", service.ID, + ).WithError(err).Error("error reconciling replicated job") + } + } + + if orchestrator.IsGlobalJob(service) { + if err := o.globalReconciler.ReconcileService(service.ID); err != nil { + log.G(ctx).WithField( + "service.id", service.ID, + ).WithError(err).Error("error reconciling global job") + } + } +} + +// Stop stops the Orchestrator +func (o *Orchestrator) Stop() { + // close stopChan inside of the Once so that there can be no races + // involving multiple attempts to close stopChan. + o.stopOnce.Do(func() { + close(o.stopChan) + }) + // now, we wait for the Orchestrator to stop. this wait is unqualified; we + // will not return until Orchestrator has stopped successfully. + <-o.doneChan +} diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/jobs/replicated/reconciler.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/jobs/replicated/reconciler.go new file mode 100644 index 0000000000000..886ac8d91e1a9 --- /dev/null +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/jobs/replicated/reconciler.go @@ -0,0 +1,296 @@ +package replicated + +import ( + "context" + "fmt" + + "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/manager/orchestrator" + "github.com/docker/swarmkit/manager/state/store" +) + +// restartSupervisor is an interface representing the methods from the +// restart.SupervisorInterface that are actually needed by the reconciler. This +// more limited interface allows us to write a less ugly fake for unit testing. +type restartSupervisor interface { + Restart(context.Context, store.Tx, *api.Cluster, *api.Service, api.Task) error +} + +// Reconciler is an object that manages reconciliation of replicated jobs. It +// is blocking and non-asynchronous, for ease of testing. It implements two +// interfaces. The first is the Reconciler interface of the Orchestrator +// package above this one. The second is the taskinit.InitHandler interface. +type Reconciler struct { + // we need the store, of course, to do updates + store *store.MemoryStore + + restart restartSupervisor +} + +// newReconciler creates a new reconciler object +func NewReconciler(store *store.MemoryStore, restart restartSupervisor) *Reconciler { + return &Reconciler{ + store: store, + restart: restart, + } +} + +// ReconcileService reconciles the replicated job service with the given ID by +// checking to see if new replicas should be created. reconcileService returns +// an error if there is some case prevent it from correctly reconciling the +// service. +func (r *Reconciler) ReconcileService(id string) error { + var ( + service *api.Service + tasks []*api.Task + cluster *api.Cluster + viewErr error + ) + // first, get the service and all of its tasks + r.store.View(func(tx store.ReadTx) { + service = store.GetService(tx, id) + + tasks, viewErr = store.FindTasks(tx, store.ByServiceID(id)) + + // there should only ever be 1 cluster object, but for reasons + // forgotten by me, it needs to be retrieved in a rather roundabout way + // from the store + var clusters []*api.Cluster + clusters, viewErr = store.FindClusters(tx, store.All) + if len(clusters) == 1 { + cluster = clusters[0] + } else if len(clusters) > 1 { + // this should never happen, and indicates that the system is + // broken. + panic("there should never be more than one cluster object") + } + }) + + // errors during view should only happen in a few rather catastrophic + // cases, but here it's not unreasonable to just return an error anyway. + if viewErr != nil { + return viewErr + } + + // if the service has already been deleted, there's nothing to do here. + if service == nil { + return nil + } + + // if this is the first iteration of the service, it may not yet have a + // JobStatus, so we should create one if so. this won't actually be + // committed, though. + if service.JobStatus == nil { + service.JobStatus = &api.JobStatus{} + } + + // Jobs can be run in multiple iterations. The JobStatus of the service + // indicates which Version of iteration we're on. We should only be looking + // at tasks of the latest Version + + jobVersion := service.JobStatus.JobIteration.Index + + // now, check how many tasks we need and how many we have running. note + // that some of these Running tasks may complete before we even finish this + // code block, and so we might have to immediately re-enter reconciliation, + // so this number is 100% definitive, but it is accurate for this + // particular moment in time, and it won't result in us going OVER the + // needed task count + // + // importantly, we are computing only how many _new_ tasks are needed. Some + // tasks may need to be restarted as well, but we don't do this directly; + // restarting tasks is under the purview of the restartSupervisor. + // + // also also, for the math later, we need these values to be of type uint64. + runningTasks := uint64(0) + completeTasks := uint64(0) + restartTasks := []string{} + removeTasks := []string{} + + // for replicated jobs, each task will get a different slot number, so that + // when the job has completed, there will be one Completed task in every + // slot number [0, TotalCompletions-1]. + // + // By assigning each task to a unique slot, we simply handling of + // restarting failed tasks through the restart manager. + slots := map[uint64]bool{} + for _, task := range tasks { + // we only care about tasks from this job iteration. tasks from the + // previous job iteration are not important + if task.JobIteration != nil { + if task.JobIteration.Index == jobVersion { + if task.Status.State == api.TaskStateCompleted { + completeTasks++ + slots[task.Slot] = true + } + + // the Restart Manager may put a task in the desired state Ready, + // so we should match not only tasks in desired state Completed, + // but also those in any valid running state. + if task.Status.State != api.TaskStateCompleted && task.DesiredState <= api.TaskStateCompleted { + runningTasks++ + slots[task.Slot] = true + + // if the task is in a terminal state, we might need to restart + // it. throw it on the pile if so. this is still counted as a + // running task for the purpose of determining how many new + // tasks to create. + if task.Status.State > api.TaskStateCompleted { + restartTasks = append(restartTasks, task.ID) + } + } + } else { + // tasks belonging to a previous iteration of the job may + // exist. if any such tasks exist, they should have their task + // state set to Remove + if task.Status.State <= api.TaskStateRunning && task.DesiredState != api.TaskStateRemove { + removeTasks = append(removeTasks, task.ID) + } + } + } + } + + // now that we have our counts, we need to see how many new tasks to + // create. this number can never exceed MaxConcurrent, but also should not + // result in us exceeding TotalCompletions. first, get these numbers out of + // the service spec. + rj := service.Spec.GetReplicatedJob() + + // possibleNewTasks gives us the upper bound for how many tasks we'll + // create. also, ugh, subtracting uints. there's no way this can ever go + // wrong. + possibleNewTasks := rj.MaxConcurrent - runningTasks + + // allowedNewTasks is how many tasks we could create, if there were no + // restriction on maximum concurrency. This is the total number of tasks + // we want completed, minus the tasks that are already completed, minus + // the tasks that are in progress. + // + // seriously, ugh, subtracting unsigned ints. totally a fine and not at all + // risky operation, with no possibility for catastrophe + allowedNewTasks := rj.TotalCompletions - completeTasks - runningTasks + + // the lower number of allowedNewTasks and possibleNewTasks is how many we + // can create. we'll just use an if statement instead of some fancy floor + // function. + actualNewTasks := allowedNewTasks + if possibleNewTasks < allowedNewTasks { + actualNewTasks = possibleNewTasks + } + + // this check might seem odd, but it protects us from an underflow of the + // above subtractions, which, again, is a totally impossible thing that can + // never happen, ever, obviously. + if actualNewTasks > rj.TotalCompletions { + return fmt.Errorf( + "uint64 underflow, we're not going to create %v tasks", + actualNewTasks, + ) + } + + // finally, we can create these tasks. do this in a batch operation, to + // avoid exceeding transaction size limits + err := r.store.Batch(func(batch *store.Batch) error { + for i := uint64(0); i < actualNewTasks; i++ { + if err := batch.Update(func(tx store.Tx) error { + var slot uint64 + // each task will go into a unique slot, and at the end, there + // should be the same number of slots as there are desired + // total completions. We could simplify this logic by simply + // assuming that slots are filled in order, but it's a more + // robust solution to not assume that, and instead assure that + // the slot is unoccupied. + for s := uint64(0); s < rj.TotalCompletions; s++ { + // when we're iterating through, if the service has slots + // that haven't been used yet (for example, if this is the + // first time we're running this iteration), then doing + // a map lookup for the number will return the 0-value + // (false) even if the number doesn't exist in the map. + if !slots[s] { + slot = s + // once we've found a slot, mark it as occupied, so we + // don't double assign in subsequent iterations. + slots[slot] = true + break + } + } + + task := orchestrator.NewTask(cluster, service, slot, "") + // when we create the task, we also need to set the + // JobIteration. + task.JobIteration = &api.Version{Index: jobVersion} + task.DesiredState = api.TaskStateCompleted + + // finally, create the task in the store. + return store.CreateTask(tx, task) + }); err != nil { + return err + } + } + + for _, taskID := range restartTasks { + if err := batch.Update(func(tx store.Tx) error { + t := store.GetTask(tx, taskID) + if t == nil { + return nil + } + + if t.DesiredState > api.TaskStateCompleted { + return nil + } + + // TODO(dperny): pass in context from above + return r.restart.Restart(context.Background(), tx, cluster, service, *t) + }); err != nil { + return err + } + } + + for _, taskID := range removeTasks { + if err := batch.Update(func(tx store.Tx) error { + t := store.GetTask(tx, taskID) + if t == nil { + return nil + } + + // don't do unnecessary updates + if t.DesiredState == api.TaskStateRemove { + return nil + } + t.DesiredState = api.TaskStateRemove + return store.UpdateTask(tx, t) + }); err != nil { + return err + } + } + + return nil + }) + + return err +} + +// IsRelatedService returns true if the task is a replicated job. This method +// fulfills the taskinit.InitHandler interface. Because it is just a wrapper +// around a well-tested function call, it has no tests of its own. +func (r *Reconciler) IsRelatedService(service *api.Service) bool { + return orchestrator.IsReplicatedJob(service) +} + +// FixTask ostensibly validates that a task is compliant with the rest of the +// cluster state. However, in the replicated jobs case, the only action we +// can take with a noncompliant task is to restart it. Because the replicated +// jobs orchestrator reconciles the whole service at once, any tasks that +// need to be restarted will be done when we make the reconiliation pass over +// all services. Therefore, in this instance, FixTask does nothing except +// implement the FixTask method of the taskinit.InitHandler interface. +func (r *Reconciler) FixTask(_ context.Context, _ *store.Batch, _ *api.Task) {} + +// SlotTuple returns an orchestrator.SlotTuple object for this task. It +// implements the taskinit.InitHandler interface +func (r *Reconciler) SlotTuple(t *api.Task) orchestrator.SlotTuple { + return orchestrator.SlotTuple{ + ServiceID: t.ServiceID, + Slot: t.Slot, + } +} diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/replicated.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/replicated.go index 18b8e24abaf20..dc455286826c9 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/replicated.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/replicated.go @@ -1,12 +1,13 @@ package replicated import ( + "context" + "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/manager/orchestrator/restart" "github.com/docker/swarmkit/manager/orchestrator/update" "github.com/docker/swarmkit/manager/state" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" ) // An Orchestrator runs a reconciliation loop to create and destroy diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/services.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/services.go index 04aea8795a4ee..b5e6bb12e8192 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/services.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/services.go @@ -1,6 +1,7 @@ package replicated import ( + "context" "sort" "github.com/docker/go-events" @@ -8,7 +9,6 @@ import ( "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/orchestrator" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" ) // This file provices service-level orchestration. It observes changes to diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/slot.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/slot.go index cee9fe10a0fe1..1160d4c96c030 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/slot.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/slot.go @@ -1,10 +1,11 @@ package replicated import ( + "context" + "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/manager/orchestrator" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" ) type slotsByRunningState []orchestrator.Slot diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/tasks.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/tasks.go index 66000e5d86781..b6336aa88a0ac 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/tasks.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/replicated/tasks.go @@ -1,13 +1,14 @@ package replicated import ( + "context" + "github.com/docker/go-events" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/orchestrator" "github.com/docker/swarmkit/manager/orchestrator/taskinit" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" ) // This file provides task-level orchestration. It observes changes to task diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/restart/restart.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/restart/restart.go index 6af44b734c558..44e1309f6c042 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/restart/restart.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/restart/restart.go @@ -2,6 +2,7 @@ package restart import ( "container/list" + "context" "errors" "sync" "time" @@ -14,7 +15,6 @@ import ( "github.com/docker/swarmkit/manager/state" "github.com/docker/swarmkit/manager/state/store" gogotypes "github.com/gogo/protobuf/types" - "golang.org/x/net/context" ) const defaultOldTaskTimeout = time.Minute @@ -49,6 +49,20 @@ type delayedStart struct { waiter bool } +// SupervisorInterface is an interface implemented by the Supervisor. It exists +// to make testing easier, by allowing the restart supervisor to be mocked or +// faked where desired. +type SupervisorInterface interface { + Restart(context.Context, store.Tx, *api.Cluster, *api.Service, api.Task) error + UpdatableTasksInSlot(context.Context, orchestrator.Slot, *api.Service) orchestrator.Slot + RecordRestartHistory(orchestrator.SlotTuple, *api.Task) + DelayStart(context.Context, store.Tx, *api.Task, string, time.Duration, bool) <-chan struct{} + StartNow(store.Tx, string) error + Cancel(string) + CancelAll() + ClearServiceHistory(string) +} + // Supervisor initiates and manages restarts. It's responsible for // delaying restarts when applicable. type Supervisor struct { @@ -121,7 +135,7 @@ func (r *Supervisor) Restart(ctx context.Context, tx store.Tx, cluster *api.Clus // Sanity check: was the task shut down already by a separate call to // Restart? If so, we must avoid restarting it, because this will create // an extra task. This should never happen unless there is a bug. - if t.DesiredState > api.TaskStateRunning { + if t.DesiredState > api.TaskStateCompleted { return errors.New("Restart called on task that was already shut down") } @@ -138,15 +152,21 @@ func (r *Supervisor) Restart(ctx context.Context, tx store.Tx, cluster *api.Clus var restartTask *api.Task - if orchestrator.IsReplicatedService(service) { + if orchestrator.IsReplicatedService(service) || orchestrator.IsReplicatedJob(service) { restartTask = orchestrator.NewTask(cluster, service, t.Slot, "") - } else if orchestrator.IsGlobalService(service) { + } else if orchestrator.IsGlobalService(service) || orchestrator.IsGlobalJob(service) { restartTask = orchestrator.NewTask(cluster, service, 0, t.NodeID) } else { log.G(ctx).Error("service not supported by restart supervisor") return nil } + if orchestrator.IsReplicatedJob(service) || orchestrator.IsGlobalJob(service) { + restartTask.JobIteration = &api.Version{ + Index: service.JobStatus.JobIteration.Index, + } + } + n := store.GetNode(tx, t.NodeID) restartTask.DesiredState = api.TaskStateReady @@ -194,10 +214,28 @@ func (r *Supervisor) Restart(ctx context.Context, tx store.Tx, cluster *api.Clus // restart policy. func (r *Supervisor) shouldRestart(ctx context.Context, t *api.Task, service *api.Service) bool { // TODO(aluzzardi): This function should not depend on `service`. - condition := orchestrator.RestartCondition(t) - - if condition != api.RestartOnAny && - (condition != api.RestartOnFailure || t.Status.State == api.TaskStateCompleted) { + // There are 3 possible restart policies. + switch orchestrator.RestartCondition(t) { + case api.RestartOnAny: + // we will be restarting, we just need to do a few more checks. + // however, if the task belongs to a job, then we will treat + // RestartOnAny the same as RestartOnFailure, as it would be + // nonsensical to restart completed jobs. + if orchestrator.IsReplicatedJob(service) || orchestrator.IsGlobalJob(service) { + // it'd be nice to put a fallthrough here, but we can't fallthrough + // from inside of an if statement. + if t.Status.State == api.TaskStateCompleted { + return false + } + } + case api.RestartOnFailure: + // we won't restart if the task is in TaskStateCompleted, as this is a + // not a failed state -- it indicates that the task exited with 0 + if t.Status.State == api.TaskStateCompleted { + return false + } + case api.RestartOnNone: + // RestartOnNone means we just don't restart, ever return false } @@ -490,7 +528,15 @@ func (r *Supervisor) StartNow(tx store.Tx, taskID string) error { if t == nil || t.DesiredState >= api.TaskStateRunning { return nil } - t.DesiredState = api.TaskStateRunning + + // only tasks belonging to jobs will have a JobIteration, so this can be + // used to distinguish whether this is a job task without looking at the + // service. + if t.JobIteration != nil { + t.DesiredState = api.TaskStateCompleted + } else { + t.DesiredState = api.TaskStateRunning + } return store.UpdateTask(tx, t) } @@ -508,20 +554,13 @@ func (r *Supervisor) Cancel(taskID string) { <-delay.doneCh } -// CancelAll aborts all pending restarts and waits for any instances of -// StartNow that have already triggered to complete. +// CancelAll aborts all pending restarts func (r *Supervisor) CancelAll() { - var cancelled []delayedStart - r.mu.Lock() for _, delay := range r.delays { delay.cancel() } r.mu.Unlock() - - for _, delay := range cancelled { - <-delay.doneCh - } } // ClearServiceHistory forgets restart history related to a given service ID. diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/service.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/service.go index 7356c38cd533c..adc5181b5046c 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/service.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/service.go @@ -1,10 +1,11 @@ package orchestrator import ( + "context" + "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" ) // IsReplicatedService checks if a service is a replicated service. @@ -27,6 +28,26 @@ func IsGlobalService(service *api.Service) bool { return ok } +// IsReplicatedJob returns true if the service is a replicated job. +func IsReplicatedJob(service *api.Service) bool { + if service == nil { + return false + } + + _, ok := service.Spec.GetMode().(*api.ServiceSpec_ReplicatedJob) + return ok +} + +// IsGlobalJob returns true if the service is a global job. +func IsGlobalJob(service *api.Service) bool { + if service == nil { + return false + } + + _, ok := service.Spec.GetMode().(*api.ServiceSpec_GlobalJob) + return ok +} + // SetServiceTasksRemove sets the desired state of tasks associated with a service // to REMOVE, so that they can be properly shut down by the agent and later removed // by the task reaper. @@ -46,22 +67,27 @@ func SetServiceTasksRemove(ctx context.Context, s *store.MemoryStore, service *a err = s.Batch(func(batch *store.Batch) error { for _, t := range tasks { err := batch.Update(func(tx store.Tx) error { + // the task may have changed for some reason in the meantime + // since we read it out, so we need to get from the store again + // within the boundaries of a transaction + latestTask := store.GetTask(tx, t.ID) + // time travel is not allowed. if the current desired state is // above the one we're trying to go to we can't go backwards. // we have nothing to do and we should skip to the next task - if t.DesiredState > api.TaskStateRemove { + if latestTask.DesiredState > api.TaskStateRemove { // log a warning, though. we shouln't be trying to rewrite // a state to an earlier state log.G(ctx).Warnf( "cannot update task %v in desired state %v to an earlier desired state %v", - t.ID, t.DesiredState, api.TaskStateRemove, + latestTask.ID, latestTask.DesiredState, api.TaskStateRemove, ) return nil } // update desired state to REMOVE - t.DesiredState = api.TaskStateRemove + latestTask.DesiredState = api.TaskStateRemove - if err := store.UpdateTask(tx, t); err != nil { + if err := store.UpdateTask(tx, latestTask); err != nil { log.G(ctx).WithError(err).Errorf("failed transaction: update task desired state to REMOVE") } return nil diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/taskinit/init.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/taskinit/init.go index b893428d514be..5636c3c699a85 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/taskinit/init.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/taskinit/init.go @@ -1,6 +1,7 @@ package taskinit import ( + "context" "sort" "time" @@ -11,7 +12,6 @@ import ( "github.com/docker/swarmkit/manager/orchestrator/restart" "github.com/docker/swarmkit/manager/state/store" gogotypes "github.com/gogo/protobuf/types" - "golang.org/x/net/context" ) // InitHandler defines orchestrator's action to fix tasks at start. @@ -23,7 +23,7 @@ type InitHandler interface { // CheckTasks fixes tasks in the store before orchestrator runs. The previous leader might // not have finished processing their updates and left them in an inconsistent state. -func CheckTasks(ctx context.Context, s *store.MemoryStore, readTx store.ReadTx, initHandler InitHandler, startSupervisor *restart.Supervisor) error { +func CheckTasks(ctx context.Context, s *store.MemoryStore, readTx store.ReadTx, initHandler InitHandler, startSupervisor restart.SupervisorInterface) error { instances := make(map[orchestrator.SlotTuple][]*api.Task) err := s.Batch(func(batch *store.Batch) error { tasks, err := store.FindTasks(readTx, store.All) @@ -59,7 +59,7 @@ func CheckTasks(ctx context.Context, s *store.MemoryStore, readTx store.ReadTx, // desired state ready is a transient state that it should be started. // however previous leader may not have started it, retry start here - if t.DesiredState != api.TaskStateReady || t.Status.State > api.TaskStateRunning { + if t.DesiredState != api.TaskStateReady || t.Status.State > api.TaskStateCompleted { continue } restartDelay, _ := gogotypes.DurationFromProto(defaults.Service.Task.Restart.Delay) @@ -80,7 +80,7 @@ func CheckTasks(ctx context.Context, s *store.MemoryStore, readTx store.ReadTx, } if err == nil { restartTime := timestamp.Add(restartDelay) - calculatedRestartDelay := restartTime.Sub(time.Now()) + calculatedRestartDelay := time.Until(restartTime) if calculatedRestartDelay < restartDelay { restartDelay = calculatedRestartDelay } diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/taskreaper/task_reaper.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/taskreaper/task_reaper.go index cbeb321de9ff6..7fdb0a65d0cf6 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/taskreaper/task_reaper.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/taskreaper/task_reaper.go @@ -1,6 +1,7 @@ package taskreaper import ( + "context" "sort" "sync" "time" @@ -10,7 +11,6 @@ import ( "github.com/docker/swarmkit/manager/orchestrator" "github.com/docker/swarmkit/manager/state" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" ) const ( @@ -31,7 +31,7 @@ type TaskReaper struct { // taskHistory is the number of tasks to keep taskHistory int64 - // List of slot tubles to be inspected for task history cleanup. + // List of slot tuples to be inspected for task history cleanup. dirty map[orchestrator.SlotTuple]struct{} // List of tasks collected for cleanup, which includes two kinds of tasks @@ -61,7 +61,7 @@ func New(store *store.MemoryStore) *TaskReaper { // Run is the TaskReaper's watch loop which collects candidates for cleanup. // Task history is mainly used in task restarts but is also available for administrative purposes. // Note that the task history is stored per-slot-per-service for replicated services -// and per-node-per-service for global services. History does not apply to serviceless +// and per-node-per-service for global services. History does not apply to serviceless tasks // since they are not attached to a service. In addition, the TaskReaper watch loop is also // responsible for cleaning up tasks associated with slots that were removed as part of // service scale down or service removal. @@ -196,11 +196,9 @@ func (tr *TaskReaper) Run(ctx context.Context) { } isTimerStopped = true tr.tick() - } else { - if isTimerStopped { - timer.Reset(reaperBatchingInterval) - isTimerStopped = false - } + } else if isTimerStopped { + timer.Reset(reaperBatchingInterval) + isTimerStopped = false } case <-timer.C: // we can safely ignore draining off of the timer channel, because diff --git a/vendor/github.com/docker/swarmkit/manager/orchestrator/update/updater.go b/vendor/github.com/docker/swarmkit/manager/orchestrator/update/updater.go index 5a7d61231c940..e181a4ffa2915 100644 --- a/vendor/github.com/docker/swarmkit/manager/orchestrator/update/updater.go +++ b/vendor/github.com/docker/swarmkit/manager/orchestrator/update/updater.go @@ -1,14 +1,13 @@ package update import ( + "context" "errors" "fmt" "reflect" "sync" "time" - "golang.org/x/net/context" - "github.com/docker/go-events" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/api/defaults" @@ -281,6 +280,11 @@ slotsLoop: wg.Wait() if !stopped { + // if a delay is set we need to monitor for a period longer than the delay + // otherwise we will leave the monitorLoop before the task is done delaying + if updateConfig.Delay >= monitoringPeriod { + monitoringPeriod = updateConfig.Delay + 1*time.Second + } // Keep watching for task failures for one more monitoringPeriod, // before declaring the update complete. doneMonitoring := time.After(monitoringPeriod) @@ -502,7 +506,10 @@ func (u *Updater) removeOldTasks(ctx context.Context, batch *store.Batch, remove return fmt.Errorf("task %s not found while trying to shut it down", original.ID) } if t.DesiredState > api.TaskStateRunning { - return fmt.Errorf("task %s was already shut down when reached by updater", original.ID) + return fmt.Errorf( + "task %s was already shut down when reached by updater (state: %v)", + original.ID, t.DesiredState, + ) } t.DesiredState = api.TaskStateShutdown return store.UpdateTask(tx, t) diff --git a/vendor/github.com/docker/swarmkit/manager/raftselector/raftselector.go b/vendor/github.com/docker/swarmkit/manager/raftselector/raftselector.go index 89e7918a3d440..47adcf0fd9f6d 100644 --- a/vendor/github.com/docker/swarmkit/manager/raftselector/raftselector.go +++ b/vendor/github.com/docker/swarmkit/manager/raftselector/raftselector.go @@ -1,10 +1,9 @@ package raftselector import ( + "context" "errors" - "golang.org/x/net/context" - "google.golang.org/grpc" ) diff --git a/vendor/github.com/docker/swarmkit/manager/resourceapi/allocator.go b/vendor/github.com/docker/swarmkit/manager/resourceapi/allocator.go index ec19fba85047b..545e4ded99bc4 100644 --- a/vendor/github.com/docker/swarmkit/manager/resourceapi/allocator.go +++ b/vendor/github.com/docker/swarmkit/manager/resourceapi/allocator.go @@ -1,6 +1,7 @@ package resourceapi import ( + "context" "errors" "time" @@ -9,7 +10,6 @@ import ( "github.com/docker/swarmkit/identity" "github.com/docker/swarmkit/manager/state/store" "github.com/docker/swarmkit/protobuf/ptypes" - "golang.org/x/net/context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/vendor/github.com/docker/swarmkit/manager/role_manager.go b/vendor/github.com/docker/swarmkit/manager/role_manager.go index e5cf27b62fd82..e5901525b922c 100644 --- a/vendor/github.com/docker/swarmkit/manager/role_manager.go +++ b/vendor/github.com/docker/swarmkit/manager/role_manager.go @@ -1,15 +1,15 @@ package manager import ( + "context" "time" + "code.cloudfoundry.org/clock" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/state/raft" "github.com/docker/swarmkit/manager/state/raft/membership" "github.com/docker/swarmkit/manager/state/store" - "github.com/pivotal-golang/clock" - "golang.org/x/net/context" ) const ( diff --git a/vendor/github.com/docker/swarmkit/manager/scheduler/filter.go b/vendor/github.com/docker/swarmkit/manager/scheduler/filter.go index dab3c6695d4b2..df0ccbc2a65ed 100644 --- a/vendor/github.com/docker/swarmkit/manager/scheduler/filter.go +++ b/vendor/github.com/docker/swarmkit/manager/scheduler/filter.go @@ -359,3 +359,28 @@ func (f *HostPortFilter) Explain(nodes int) string { } return fmt.Sprintf("host-mode port already in use on %d nodes", nodes) } + +// MaxReplicasFilter selects only nodes that does not exceed max replicas per node. +type MaxReplicasFilter struct { + t *api.Task +} + +// SetTask returns true when max replicas per node filter > 0 for a given task. +func (f *MaxReplicasFilter) SetTask(t *api.Task) bool { + if t.Spec.Placement != nil && t.Spec.Placement.MaxReplicas > 0 { + f.t = t + return true + } + + return false +} + +// Check returns true if there is less active (assigned or pre-assigned) tasks for this service on current node than set to MaxReplicas limit +func (f *MaxReplicasFilter) Check(n *NodeInfo) bool { + return uint64(n.ActiveTasksCountByService[f.t.ServiceID]) < f.t.Spec.Placement.MaxReplicas +} + +// Explain returns an explanation of a failure. +func (f *MaxReplicasFilter) Explain(nodes int) string { + return "max replicas per node limit exceed" +} diff --git a/vendor/github.com/docker/swarmkit/manager/scheduler/nodeinfo.go b/vendor/github.com/docker/swarmkit/manager/scheduler/nodeinfo.go index 7f490586975bf..a854ab63e61cb 100644 --- a/vendor/github.com/docker/swarmkit/manager/scheduler/nodeinfo.go +++ b/vendor/github.com/docker/swarmkit/manager/scheduler/nodeinfo.go @@ -1,12 +1,12 @@ package scheduler import ( + "context" "time" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/api/genericresource" "github.com/docker/swarmkit/log" - "golang.org/x/net/context" ) // hostPortSpec specifies a used host port. @@ -70,7 +70,7 @@ func (nodeInfo *NodeInfo) removeTask(t *api.Task) bool { } delete(nodeInfo.Tasks, t.ID) - if oldTask.DesiredState <= api.TaskStateRunning { + if oldTask.DesiredState <= api.TaskStateCompleted { nodeInfo.ActiveTasksCount-- nodeInfo.ActiveTasksCountByService[t.ServiceID]-- } @@ -108,12 +108,12 @@ func (nodeInfo *NodeInfo) removeTask(t *api.Task) bool { func (nodeInfo *NodeInfo) addTask(t *api.Task) bool { oldTask, ok := nodeInfo.Tasks[t.ID] if ok { - if t.DesiredState <= api.TaskStateRunning && oldTask.DesiredState > api.TaskStateRunning { + if t.DesiredState <= api.TaskStateCompleted && oldTask.DesiredState > api.TaskStateCompleted { nodeInfo.Tasks[t.ID] = t nodeInfo.ActiveTasksCount++ nodeInfo.ActiveTasksCountByService[t.ServiceID]++ return true - } else if t.DesiredState > api.TaskStateRunning && oldTask.DesiredState <= api.TaskStateRunning { + } else if t.DesiredState > api.TaskStateCompleted && oldTask.DesiredState <= api.TaskStateCompleted { nodeInfo.Tasks[t.ID] = t nodeInfo.ActiveTasksCount-- nodeInfo.ActiveTasksCountByService[t.ServiceID]-- @@ -145,7 +145,7 @@ func (nodeInfo *NodeInfo) addTask(t *api.Task) bool { } } - if t.DesiredState <= api.TaskStateRunning { + if t.DesiredState <= api.TaskStateCompleted { nodeInfo.ActiveTasksCount++ nodeInfo.ActiveTasksCountByService[t.ServiceID]++ } diff --git a/vendor/github.com/docker/swarmkit/manager/scheduler/pipeline.go b/vendor/github.com/docker/swarmkit/manager/scheduler/pipeline.go index c577fbcd20ae9..a3dac4939b51f 100644 --- a/vendor/github.com/docker/swarmkit/manager/scheduler/pipeline.go +++ b/vendor/github.com/docker/swarmkit/manager/scheduler/pipeline.go @@ -15,6 +15,7 @@ var ( &ConstraintFilter{}, &PlatformFilter{}, &HostPortFilter{}, + &MaxReplicasFilter{}, } ) diff --git a/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go b/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go index 9e708ed1b6b09..78dd7dc3fed17 100644 --- a/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go +++ b/vendor/github.com/docker/swarmkit/manager/scheduler/scheduler.go @@ -1,6 +1,7 @@ package scheduler import ( + "context" "time" "github.com/docker/swarmkit/api" @@ -9,7 +10,6 @@ import ( "github.com/docker/swarmkit/manager/state" "github.com/docker/swarmkit/manager/state/store" "github.com/docker/swarmkit/protobuf/ptypes" - "golang.org/x/net/context" ) const ( @@ -74,9 +74,10 @@ func (s *Scheduler) setupTasksList(tx store.ReadTx) error { continue } - // Also ignore tasks that have not yet been assigned but desired state is beyond TaskStateRunning - // This can happen if you update, delete or scale down a service before its tasks were assigned. - if t.Status.State == api.TaskStatePending && t.DesiredState > api.TaskStateRunning { + // Also ignore tasks that have not yet been assigned but desired state + // is beyond TaskStateCompleted. This can happen if you update, delete + // or scale down a service before its tasks were assigned. + if t.Status.State == api.TaskStatePending && t.DesiredState > api.TaskStateCompleted { continue } @@ -720,15 +721,32 @@ func (s *Scheduler) noSuitableNode(ctx context.Context, taskGroup map[string]*ap newT := *t newT.Status.Timestamp = ptypes.MustTimestampProto(time.Now()) - if explanation != "" { - newT.Status.Err = "no suitable node (" + explanation + ")" + sv := service.SpecVersion + tv := newT.SpecVersion + if sv != nil && tv != nil && sv.Index > tv.Index { + log.G(ctx).WithField("task.id", t.ID).Debug( + "task belongs to old revision of service", + ) + if t.Status.State == api.TaskStatePending && t.DesiredState >= api.TaskStateShutdown { + log.G(ctx).WithField("task.id", t.ID).Debug( + "task is desired shutdown, scheduler will go ahead and do so", + ) + newT.Status.State = api.TaskStateShutdown + newT.Status.Err = "" + } } else { - newT.Status.Err = "no suitable node" + if explanation != "" { + newT.Status.Err = "no suitable node (" + explanation + ")" + } else { + newT.Status.Err = "no suitable node" + } + + // re-enqueue a task that should still be attempted + s.enqueue(&newT) } + s.allTasks[t.ID] = &newT schedulingDecisions[t.ID] = schedulingDecision{old: t, new: &newT} - - s.enqueue(&newT) } } diff --git a/vendor/github.com/docker/swarmkit/manager/state/proposer.go b/vendor/github.com/docker/swarmkit/manager/state/proposer.go index 4967f98a1ec3f..8d53f5772f0a0 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/proposer.go +++ b/vendor/github.com/docker/swarmkit/manager/state/proposer.go @@ -1,8 +1,9 @@ package state import ( + "context" + "github.com/docker/swarmkit/api" - "golang.org/x/net/context" ) // A Change includes a version number and a set of store actions from a diff --git a/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go b/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go index 38c61c7bd2f5d..9d744b35e1a98 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go +++ b/vendor/github.com/docker/swarmkit/manager/state/raft/raft.go @@ -1,19 +1,22 @@ package raft import ( + "context" "fmt" "io" "math" "math/rand" "net" + "os" + "runtime" "sync" "sync/atomic" "time" + "code.cloudfoundry.org/clock" "github.com/coreos/etcd/pkg/idutil" "github.com/coreos/etcd/raft" "github.com/coreos/etcd/raft/raftpb" - "github.com/docker/docker/pkg/signal" "github.com/docker/go-events" "github.com/docker/go-metrics" "github.com/docker/swarmkit/api" @@ -27,10 +30,8 @@ import ( "github.com/docker/swarmkit/manager/state/store" "github.com/docker/swarmkit/watch" "github.com/gogo/protobuf/proto" - "github.com/pivotal-golang/clock" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "golang.org/x/net/context" "golang.org/x/time/rate" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -594,7 +595,7 @@ func (n *Node) Run(ctx context.Context) error { transferLeadershipLimit.Allow() { log.G(ctx).Error("Attempting to transfer leadership") if !n.opts.DisableStackDump { - signal.DumpStacks("") + stackDump() } transferee, err := n.transport.LongestActive() if err != nil { @@ -1182,11 +1183,8 @@ func (n *Node) CanRemoveMember(id uint64) bool { } nquorum := (len(members)-1)/2 + 1 - if nreachable < nquorum { - return false - } - return true + return nreachable >= nquorum } func (n *Node) removeMember(ctx context.Context, id uint64) error { @@ -1591,10 +1589,7 @@ func (n *Node) ProposeValue(ctx context.Context, storeAction []api.StoreAction, defer cancel() _, err := n.processInternalRaftRequest(ctx, &api.InternalRaftRequest{Action: storeAction}, cb) - if err != nil { - return err - } - return nil + return err } // GetVersion returns the sequence information for the current raft round. @@ -2137,3 +2132,21 @@ func getIDs(snap *raftpb.Snapshot, ents []raftpb.Entry) []uint64 { func (n *Node) reqTimeout() time.Duration { return 5*time.Second + 2*time.Duration(n.Config.ElectionTick)*n.opts.TickInterval } + +// stackDump outputs the runtime stack to os.StdErr. +// +// It is based on Moby's stack.Dump(); https://github.com/moby/moby/blob/471fd27709777d2cce3251129887e14e8bb2e0c7/pkg/stack/stackdump.go#L41-L57 +func stackDump() { + var ( + buf []byte + stackSize int + ) + bufferLen := 16384 + for stackSize == len(buf) { + buf = make([]byte, bufferLen) + stackSize = runtime.Stack(buf, true) + bufferLen *= 2 + } + buf = buf[:stackSize] + _, _ = os.Stderr.Write(buf) +} diff --git a/vendor/github.com/docker/swarmkit/manager/state/raft/storage.go b/vendor/github.com/docker/swarmkit/manager/state/raft/storage.go index 547b775645672..915cc3f24148a 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/raft/storage.go +++ b/vendor/github.com/docker/swarmkit/manager/state/raft/storage.go @@ -1,6 +1,7 @@ package raft import ( + "context" "fmt" "github.com/coreos/etcd/raft" @@ -13,7 +14,6 @@ import ( "github.com/docker/swarmkit/manager/state/raft/storage" "github.com/docker/swarmkit/manager/state/store" "github.com/pkg/errors" - "golang.org/x/net/context" ) var ( diff --git a/vendor/github.com/docker/swarmkit/manager/state/raft/storage/storage.go b/vendor/github.com/docker/swarmkit/manager/state/raft/storage/storage.go index bbd262f37ce21..64d821894ef7c 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/raft/storage/storage.go +++ b/vendor/github.com/docker/swarmkit/manager/state/raft/storage/storage.go @@ -1,13 +1,12 @@ package storage import ( + "context" "fmt" "os" "path/filepath" "sync" - "golang.org/x/net/context" - "github.com/coreos/etcd/pkg/fileutil" "github.com/coreos/etcd/raft/raftpb" "github.com/coreos/etcd/snap" diff --git a/vendor/github.com/docker/swarmkit/manager/state/raft/storage/walwrap.go b/vendor/github.com/docker/swarmkit/manager/state/raft/storage/walwrap.go index 3c1208cdd0c97..d1155975bf924 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/raft/storage/walwrap.go +++ b/vendor/github.com/docker/swarmkit/manager/state/raft/storage/walwrap.go @@ -1,6 +1,7 @@ package storage import ( + "context" "io" "io/ioutil" "os" @@ -14,7 +15,6 @@ import ( "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/encryption" "github.com/pkg/errors" - "golang.org/x/net/context" ) // This package wraps the github.com/coreos/etcd/wal package, and encrypts diff --git a/vendor/github.com/docker/swarmkit/manager/state/raft/transport/peer.go b/vendor/github.com/docker/swarmkit/manager/state/raft/transport/peer.go index 644e295214e70..6fb39523cafb1 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/raft/transport/peer.go +++ b/vendor/github.com/docker/swarmkit/manager/state/raft/transport/peer.go @@ -1,12 +1,11 @@ package transport import ( + "context" "fmt" "sync" "time" - "golang.org/x/net/context" - "google.golang.org/grpc" "google.golang.org/grpc/codes" diff --git a/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go b/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go index b741c4aa67996..6bd0bc32ef811 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go +++ b/vendor/github.com/docker/swarmkit/manager/state/raft/transport/transport.go @@ -3,12 +3,12 @@ package transport import ( + "context" + "math" "net" "sync" "time" - "golang.org/x/net/context" - grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -355,6 +355,15 @@ func (t *Transport) dial(addr string) (*grpc.ClientConn, error) { return net.DialTimeout("tcp", addr, timeout) })) + // TODO(dperny): this changes the max received message size for outgoing + // client connections. this means if the server sends a message larger than + // this, we will still accept and unmarshal it. i'm unsure what the + // potential consequences are of setting this to be effectively unbounded, + // so after docker/swarmkit#2774 is fixed, we should remove this option + grpcOptions = append(grpcOptions, grpc.WithDefaultCallOptions( + grpc.MaxCallRecvMsgSize(math.MaxInt32), + )) + cc, err := grpc.Dial(addr, grpcOptions...) if err != nil { return nil, err diff --git a/vendor/github.com/docker/swarmkit/manager/state/raft/util.go b/vendor/github.com/docker/swarmkit/manager/state/raft/util.go index da44dca6a5a0e..1a49f76cb4bcb 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/raft/util.go +++ b/vendor/github.com/docker/swarmkit/manager/state/raft/util.go @@ -1,10 +1,10 @@ package raft import ( + "context" + "net" "time" - "golang.org/x/net/context" - "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/manager/state" "github.com/docker/swarmkit/manager/state/store" @@ -15,11 +15,15 @@ import ( // dial returns a grpc client connection func dial(addr string, protocol string, creds credentials.TransportCredentials, timeout time.Duration) (*grpc.ClientConn, error) { + // gRPC dialer connects to proxy first. Provide a custom dialer here avoid that. grpcOptions := []grpc.DialOption{ grpc.WithBackoffMaxDelay(2 * time.Second), grpc.WithTransportCredentials(creds), grpc.WithUnaryInterceptor(grpc_prometheus.UnaryClientInterceptor), grpc.WithStreamInterceptor(grpc_prometheus.StreamClientInterceptor), + grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout("tcp", addr, timeout) + }), } if timeout != 0 { diff --git a/vendor/github.com/docker/swarmkit/manager/state/store/memory.go b/vendor/github.com/docker/swarmkit/manager/state/store/memory.go index 423f04cad3647..d0319c7f65aa2 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/store/memory.go +++ b/vendor/github.com/docker/swarmkit/manager/state/store/memory.go @@ -1,6 +1,7 @@ package store import ( + "context" "errors" "fmt" "runtime" @@ -18,7 +19,6 @@ import ( "github.com/docker/swarmkit/watch" gogotypes "github.com/gogo/protobuf/types" memdb "github.com/hashicorp/go-memdb" - "golang.org/x/net/context" ) const ( @@ -689,7 +689,7 @@ func (tx readTx) findIterators(table string, by By, checkType func(By) error) ([ } return []memdb.ResultIterator{it}, nil case bySlot: - it, err := tx.memDBTx.Get(table, indexSlot, v.serviceID+"\x00"+strconv.FormatUint(uint64(v.slot), 10)) + it, err := tx.memDBTx.Get(table, indexSlot, v.serviceID+"\x00"+strconv.FormatUint(v.slot, 10)) if err != nil { return nil, err } diff --git a/vendor/github.com/docker/swarmkit/manager/state/store/networks.go b/vendor/github.com/docker/swarmkit/manager/state/store/networks.go index 3042def1bf65f..fa887b3b11674 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/store/networks.go +++ b/vendor/github.com/docker/swarmkit/manager/state/store/networks.go @@ -105,7 +105,7 @@ func GetNetwork(tx ReadTx, id string) *api.Network { func FindNetworks(tx ReadTx, by By) ([]*api.Network, error) { checkType := func(by By) error { switch by.(type) { - case byName, byNamePrefix, byIDPrefix, byCustom, byCustomPrefix: + case byName, byNamePrefix, byIDPrefix, byCustom, byCustomPrefix, byAll: return nil default: return ErrInvalidFindBy diff --git a/vendor/github.com/docker/swarmkit/manager/state/store/resources.go b/vendor/github.com/docker/swarmkit/manager/state/store/resources.go index 1f2c3904f1db0..9852f64fe043c 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/store/resources.go +++ b/vendor/github.com/docker/swarmkit/manager/state/store/resources.go @@ -1,6 +1,8 @@ package store import ( + "strings" + "github.com/docker/swarmkit/api" memdb "github.com/hashicorp/go-memdb" "github.com/pkg/errors" @@ -8,6 +10,12 @@ import ( const tableResource = "resource" +var ( + // ErrNoKind is returned by resource create operations if the provided Kind + // of the resource does not exist + ErrNoKind = errors.New("object kind is unregistered") +) + func init() { register(ObjectStoreConfig{ Table: &memdb.TableSchema{ @@ -87,17 +95,26 @@ func confirmExtension(tx Tx, r *api.Resource) error { return errors.Wrap(err, "failed to query extensions") } if len(extensions) == 0 { - return errors.Errorf("object kind %s is unregistered", r.Kind) + return ErrNoKind } return nil } // CreateResource adds a new resource object to the store. // Returns ErrExist if the ID is already taken. +// Returns ErrNameConflict if a Resource with this Name already exists +// Returns ErrNoKind if the specified Kind does not exist func CreateResource(tx Tx, r *api.Resource) error { if err := confirmExtension(tx, r); err != nil { return err } + // TODO(dperny): currently the "name" index is unique, which means only one + // Resource of _any_ Kind can exist with that name. This isn't a problem + // right now, but the ideal case would be for names to be namespaced to the + // kind. + if tx.lookup(tableResource, indexName, strings.ToLower(r.Annotations.Name)) != nil { + return ErrNameConflict + } return tx.create(tableResource, resourceEntry{r}) } @@ -130,7 +147,7 @@ func GetResource(tx ReadTx, id string) *api.Resource { func FindResources(tx ReadTx, by By) ([]*api.Resource, error) { checkType := func(by By) error { switch by.(type) { - case byIDPrefix, byName, byKind, byCustom, byCustomPrefix: + case byIDPrefix, byName, byNamePrefix, byKind, byCustom, byCustomPrefix: return nil default: return ErrInvalidFindBy diff --git a/vendor/github.com/docker/swarmkit/manager/state/store/services.go b/vendor/github.com/docker/swarmkit/manager/state/store/services.go index 1adbb87fe45de..a581737369b81 100644 --- a/vendor/github.com/docker/swarmkit/manager/state/store/services.go +++ b/vendor/github.com/docker/swarmkit/manager/state/store/services.go @@ -126,7 +126,7 @@ func GetService(tx ReadTx, id string) *api.Service { func FindServices(tx ReadTx, by By) ([]*api.Service, error) { checkType := func(by By) error { switch by.(type) { - case byName, byNamePrefix, byIDPrefix, byRuntime, byReferencedNetworkID, byReferencedSecretID, byReferencedConfigID, byCustom, byCustomPrefix: + case byName, byNamePrefix, byIDPrefix, byRuntime, byReferencedNetworkID, byReferencedSecretID, byReferencedConfigID, byCustom, byCustomPrefix, byAll: return nil default: return ErrInvalidFindBy diff --git a/vendor/github.com/docker/swarmkit/manager/watchapi/server.go b/vendor/github.com/docker/swarmkit/manager/watchapi/server.go index 6d49dca715d77..eb086b8ac0ff7 100644 --- a/vendor/github.com/docker/swarmkit/manager/watchapi/server.go +++ b/vendor/github.com/docker/swarmkit/manager/watchapi/server.go @@ -1,11 +1,11 @@ package watchapi import ( + "context" "errors" "sync" "github.com/docker/swarmkit/manager/state/store" - "golang.org/x/net/context" ) var ( diff --git a/vendor/github.com/docker/swarmkit/node/node.go b/vendor/github.com/docker/swarmkit/node/node.go index 6cbf265c5d3ea..c4ce906e34e2b 100644 --- a/vendor/github.com/docker/swarmkit/node/node.go +++ b/vendor/github.com/docker/swarmkit/node/node.go @@ -2,9 +2,11 @@ package node import ( "bytes" + "context" "crypto/tls" "encoding/json" "io/ioutil" + "math" "net" "os" "path/filepath" @@ -17,8 +19,9 @@ import ( "github.com/docker/swarmkit/ca/keyutils" "github.com/docker/swarmkit/identity" + "github.com/docker/docker/libnetwork/drivers/overlay/overlayutils" "github.com/docker/docker/pkg/plugingetter" - metrics "github.com/docker/go-metrics" + "github.com/docker/go-metrics" "github.com/docker/swarmkit/agent" "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" @@ -35,7 +38,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" bolt "go.etcd.io/bbolt" - "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/status" @@ -161,6 +163,7 @@ type Node struct { manager *manager.Manager notifyNodeChange chan *agent.NodeChanges // used by the agent to relay node updates from the dispatcher Session stream to (*Node).run unlockKey []byte + vxlanUDPPort uint32 } type lastSeenRole struct { @@ -269,6 +272,15 @@ func (n *Node) currentRole() api.NodeRole { return currentRole } +// configVXLANUDPPort sets vxlan port in libnetwork +func configVXLANUDPPort(ctx context.Context, vxlanUDPPort uint32) { + if err := overlayutils.ConfigVXLANUDPPort(vxlanUDPPort); err != nil { + log.G(ctx).WithError(err).Error("failed to configure VXLAN UDP port") + return + } + logrus.Infof("initialized VXLAN UDP port to %d ", vxlanUDPPort) +} + func (n *Node) run(ctx context.Context) (err error) { defer func() { n.err = err @@ -358,6 +370,10 @@ func (n *Node) run(ctx context.Context) (err error) { return case nodeChanges := <-n.notifyNodeChange: if nodeChanges.Node != nil { + if nodeChanges.Node.VXLANUDPPort != 0 { + n.vxlanUDPPort = nodeChanges.Node.VXLANUDPPort + configVXLANUDPPort(ctx, n.vxlanUDPPort) + } // This is a bit complex to be backward compatible with older CAs that // don't support the Node.Role field. They only use what's presently // called DesiredRole. @@ -896,6 +912,7 @@ func (n *Node) initManagerConnection(ctx context.Context, ready chan<- struct{}) opts := []grpc.DialOption{ grpc.WithUnaryInterceptor(grpc_prometheus.UnaryClientInterceptor), grpc.WithStreamInterceptor(grpc_prometheus.StreamClientInterceptor), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(math.MaxInt32)), } insecureCreds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}) opts = append(opts, grpc.WithTransportCredentials(insecureCreds)) @@ -1204,19 +1221,16 @@ func (s *persistentRemotes) Observe(peer api.Peer, weight int) { s.c.Broadcast() if err := s.save(); err != nil { logrus.Errorf("error writing cluster state file: %v", err) - return } - return } + func (s *persistentRemotes) Remove(peers ...api.Peer) { s.Lock() defer s.Unlock() s.Remotes.Remove(peers...) if err := s.save(); err != nil { logrus.Errorf("error writing cluster state file: %v", err) - return } - return } func (s *persistentRemotes) save() error { diff --git a/vendor/github.com/docker/swarmkit/protobuf/plugin/plugin.pb.go b/vendor/github.com/docker/swarmkit/protobuf/plugin/plugin.pb.go index 0d08eb6eb3179..1ff8cdc382d78 100644 --- a/vendor/github.com/docker/swarmkit/protobuf/plugin/plugin.pb.go +++ b/vendor/github.com/docker/swarmkit/protobuf/plugin/plugin.pb.go @@ -1,30 +1,20 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: github.com/docker/swarmkit/protobuf/plugin/plugin.proto -/* - Package plugin is a generated protocol buffer package. - - It is generated from these files: - github.com/docker/swarmkit/protobuf/plugin/plugin.proto - - It has these top-level messages: - WatchSelectors - StoreObject - TLSAuthorization -*/ package plugin -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" - -import deepcopy "github.com/docker/swarmkit/api/deepcopy" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + fmt "fmt" + github_com_docker_swarmkit_api_deepcopy "github.com/docker/swarmkit/api/deepcopy" + github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" + proto "github.com/gogo/protobuf/proto" + descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -35,7 +25,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type WatchSelectors struct { // supported by all object types @@ -54,22 +44,76 @@ type WatchSelectors struct { Role *bool `protobuf:"varint,11,opt,name=role" json:"role,omitempty"` Membership *bool `protobuf:"varint,12,opt,name=membership" json:"membership,omitempty"` // supported by: resource - Kind *bool `protobuf:"varint,13,opt,name=kind" json:"kind,omitempty"` - XXX_unrecognized []byte `json:"-"` + Kind *bool `protobuf:"varint,13,opt,name=kind" json:"kind,omitempty"` +} + +func (m *WatchSelectors) Reset() { *m = WatchSelectors{} } +func (*WatchSelectors) ProtoMessage() {} +func (*WatchSelectors) Descriptor() ([]byte, []int) { + return fileDescriptor_3708583e03e1c1e3, []int{0} +} +func (m *WatchSelectors) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *WatchSelectors) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_WatchSelectors.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *WatchSelectors) XXX_Merge(src proto.Message) { + xxx_messageInfo_WatchSelectors.Merge(m, src) +} +func (m *WatchSelectors) XXX_Size() int { + return m.Size() +} +func (m *WatchSelectors) XXX_DiscardUnknown() { + xxx_messageInfo_WatchSelectors.DiscardUnknown(m) } -func (m *WatchSelectors) Reset() { *m = WatchSelectors{} } -func (*WatchSelectors) ProtoMessage() {} -func (*WatchSelectors) Descriptor() ([]byte, []int) { return fileDescriptorPlugin, []int{0} } +var xxx_messageInfo_WatchSelectors proto.InternalMessageInfo type StoreObject struct { - WatchSelectors *WatchSelectors `protobuf:"bytes,1,req,name=watch_selectors,json=watchSelectors" json:"watch_selectors,omitempty"` - XXX_unrecognized []byte `json:"-"` + WatchSelectors *WatchSelectors `protobuf:"bytes,1,req,name=watch_selectors,json=watchSelectors" json:"watch_selectors,omitempty"` } -func (m *StoreObject) Reset() { *m = StoreObject{} } -func (*StoreObject) ProtoMessage() {} -func (*StoreObject) Descriptor() ([]byte, []int) { return fileDescriptorPlugin, []int{1} } +func (m *StoreObject) Reset() { *m = StoreObject{} } +func (*StoreObject) ProtoMessage() {} +func (*StoreObject) Descriptor() ([]byte, []int) { + return fileDescriptor_3708583e03e1c1e3, []int{1} +} +func (m *StoreObject) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StoreObject) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StoreObject.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StoreObject) XXX_Merge(src proto.Message) { + xxx_messageInfo_StoreObject.Merge(m, src) +} +func (m *StoreObject) XXX_Size() int { + return m.Size() +} +func (m *StoreObject) XXX_DiscardUnknown() { + xxx_messageInfo_StoreObject.DiscardUnknown(m) +} + +var xxx_messageInfo_StoreObject proto.InternalMessageInfo type TLSAuthorization struct { // Roles contains the acceptable TLS OU roles for the handler. @@ -77,16 +121,43 @@ type TLSAuthorization struct { // Insecure is set to true if this method does not require // authorization. NOTE: Specifying both "insecure" and a nonempty // list of roles is invalid. This would fail at codegen time. - Insecure *bool `protobuf:"varint,2,opt,name=insecure" json:"insecure,omitempty"` - XXX_unrecognized []byte `json:"-"` + Insecure *bool `protobuf:"varint,2,opt,name=insecure" json:"insecure,omitempty"` } -func (m *TLSAuthorization) Reset() { *m = TLSAuthorization{} } -func (*TLSAuthorization) ProtoMessage() {} -func (*TLSAuthorization) Descriptor() ([]byte, []int) { return fileDescriptorPlugin, []int{2} } +func (m *TLSAuthorization) Reset() { *m = TLSAuthorization{} } +func (*TLSAuthorization) ProtoMessage() {} +func (*TLSAuthorization) Descriptor() ([]byte, []int) { + return fileDescriptor_3708583e03e1c1e3, []int{2} +} +func (m *TLSAuthorization) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TLSAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TLSAuthorization.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TLSAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_TLSAuthorization.Merge(m, src) +} +func (m *TLSAuthorization) XXX_Size() int { + return m.Size() +} +func (m *TLSAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_TLSAuthorization.DiscardUnknown(m) +} + +var xxx_messageInfo_TLSAuthorization proto.InternalMessageInfo var E_Deepcopy = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 70000, Name: "docker.protobuf.plugin.deepcopy", @@ -95,20 +166,20 @@ var E_Deepcopy = &proto.ExtensionDesc{ } var E_StoreObject = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*StoreObject)(nil), Field: 70001, Name: "docker.protobuf.plugin.store_object", - Tag: "bytes,70001,opt,name=store_object,json=storeObject", + Tag: "bytes,70001,opt,name=store_object", Filename: "github.com/docker/swarmkit/protobuf/plugin/plugin.proto", } var E_TlsAuthorization = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MethodOptions)(nil), + ExtendedType: (*descriptor.MethodOptions)(nil), ExtensionType: (*TLSAuthorization)(nil), Field: 73626345, Name: "docker.protobuf.plugin.tls_authorization", - Tag: "bytes,73626345,opt,name=tls_authorization,json=tlsAuthorization", + Tag: "bytes,73626345,opt,name=tls_authorization", Filename: "github.com/docker/swarmkit/protobuf/plugin/plugin.proto", } @@ -121,6 +192,51 @@ func init() { proto.RegisterExtension(E_TlsAuthorization) } +func init() { + proto.RegisterFile("github.com/docker/swarmkit/protobuf/plugin/plugin.proto", fileDescriptor_3708583e03e1c1e3) +} + +var fileDescriptor_3708583e03e1c1e3 = []byte{ + // 588 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x52, 0x41, 0x6f, 0xd3, 0x4c, + 0x10, 0x8d, 0xd3, 0x36, 0x4d, 0x26, 0x69, 0xbf, 0x7e, 0x2b, 0x54, 0x59, 0x3d, 0x38, 0x55, 0x83, + 0x50, 0x90, 0x90, 0x23, 0xf5, 0x82, 0x94, 0x1b, 0x25, 0x97, 0x48, 0x40, 0x91, 0x83, 0xc4, 0x8d, + 0xc8, 0xf1, 0x4e, 0x93, 0xa5, 0x8e, 0xd7, 0xda, 0x5d, 0xd3, 0xc2, 0x89, 0x3f, 0x80, 0xc4, 0x95, + 0x2b, 0xbf, 0xa6, 0xc7, 0x1e, 0x7b, 0x8a, 0xa8, 0x23, 0x0e, 0xdc, 0xe0, 0x1f, 0xa0, 0xdd, 0x75, + 0x1a, 0x82, 0x5a, 0x71, 0xf2, 0xcc, 0x9b, 0xf7, 0x66, 0xe6, 0xed, 0x18, 0x1e, 0x8f, 0x99, 0x9a, + 0x64, 0x23, 0x3f, 0xe2, 0xd3, 0x0e, 0xe5, 0xd1, 0x29, 0x8a, 0x8e, 0x3c, 0x0b, 0xc5, 0xf4, 0x94, + 0xa9, 0x4e, 0x2a, 0xb8, 0xe2, 0xa3, 0xec, 0xa4, 0x93, 0xc6, 0xd9, 0x98, 0x25, 0xc5, 0xc7, 0x37, + 0x30, 0xd9, 0xb5, 0x6c, 0x7f, 0x41, 0xf2, 0x6d, 0x75, 0x6f, 0x7f, 0xcc, 0xf9, 0x38, 0xc6, 0xa5, + 0x98, 0xa2, 0x8c, 0x04, 0x4b, 0x15, 0x2f, 0xb8, 0x07, 0x5f, 0xd6, 0x60, 0xfb, 0x75, 0xa8, 0xa2, + 0xc9, 0x00, 0x63, 0x8c, 0x14, 0x17, 0x92, 0xec, 0x42, 0x99, 0x51, 0xd7, 0xd9, 0x77, 0xda, 0xd5, + 0xa3, 0x4a, 0x3e, 0x6b, 0x96, 0xfb, 0xbd, 0xa0, 0xcc, 0x28, 0x79, 0x08, 0x35, 0x46, 0x87, 0xa9, + 0xc0, 0x13, 0x76, 0xee, 0x96, 0x4d, 0xb9, 0x91, 0xcf, 0x9a, 0xd5, 0x7e, 0xef, 0xa5, 0xc1, 0x82, + 0x2a, 0xa3, 0x36, 0x22, 0x04, 0xd6, 0x93, 0x70, 0x8a, 0xee, 0x9a, 0x66, 0x05, 0x26, 0x26, 0x4d, + 0xa8, 0xeb, 0xef, 0xa2, 0xc1, 0xba, 0x29, 0x81, 0x86, 0x0a, 0xd1, 0x2e, 0x54, 0xa2, 0x4c, 0x2a, + 0x3e, 0x75, 0x37, 0x4c, 0xad, 0xc8, 0x48, 0x0b, 0xb6, 0x6c, 0xb4, 0x90, 0x56, 0x4c, 0xb9, 0x61, + 0xc1, 0x42, 0xfc, 0x08, 0x40, 0xa2, 0x78, 0xc7, 0x22, 0x1c, 0x32, 0xea, 0x6e, 0x9a, 0xed, 0xb6, + 0xf2, 0x59, 0xb3, 0x36, 0xb0, 0x68, 0xbf, 0x17, 0xd4, 0x0a, 0x42, 0x9f, 0x92, 0x16, 0x6c, 0x26, + 0x9c, 0x1a, 0x6a, 0xd5, 0x50, 0x21, 0x9f, 0x35, 0x2b, 0x2f, 0x38, 0xd5, 0xbc, 0x8a, 0x2e, 0xf5, + 0xa9, 0x36, 0x21, 0x63, 0xae, 0xdc, 0x9a, 0x35, 0xa1, 0x63, 0xbd, 0x0b, 0x45, 0xc9, 0x04, 0xd2, + 0xa1, 0x54, 0xa1, 0x42, 0x17, 0xec, 0x2e, 0x05, 0x38, 0xd0, 0x98, 0x16, 0x0a, 0x1e, 0xa3, 0x5b, + 0xb7, 0x42, 0x1d, 0x13, 0x0f, 0x60, 0x8a, 0xd3, 0x11, 0x0a, 0x39, 0x61, 0xa9, 0xdb, 0xb0, 0xe6, + 0x97, 0x88, 0xd6, 0x9c, 0xb2, 0x84, 0xba, 0x5b, 0x56, 0xa3, 0xe3, 0x83, 0x37, 0x50, 0x1f, 0x28, + 0x2e, 0xf0, 0x78, 0xf4, 0x16, 0x23, 0x45, 0x8e, 0xe1, 0xbf, 0x33, 0x7d, 0xa9, 0xa1, 0x5c, 0x9c, + 0xca, 0x75, 0xf6, 0xcb, 0xed, 0xfa, 0xe1, 0x03, 0xff, 0xf6, 0xf3, 0xfb, 0xab, 0x87, 0x0d, 0xb6, + 0xcf, 0x56, 0xf2, 0x83, 0x1e, 0xec, 0xbc, 0x7a, 0x36, 0x78, 0x92, 0xa9, 0x09, 0x17, 0xec, 0x43, + 0xa8, 0x18, 0x4f, 0xc8, 0x3d, 0xd8, 0xd0, 0xfb, 0xea, 0xd6, 0x6b, 0xed, 0x5a, 0x60, 0x13, 0xb2, + 0x07, 0x55, 0x96, 0x48, 0x8c, 0x32, 0x81, 0xf6, 0xf2, 0xc1, 0x4d, 0xde, 0x7d, 0x0a, 0x55, 0x8a, + 0x98, 0x46, 0x3c, 0x7d, 0x4f, 0x9a, 0xbe, 0xfd, 0xe1, 0x96, 0x9b, 0x3c, 0x47, 0x29, 0xc3, 0x31, + 0x1e, 0xa7, 0xba, 0xbb, 0x74, 0x7f, 0x7e, 0x35, 0x77, 0xef, 0xae, 0x2b, 0x91, 0x61, 0x70, 0x23, + 0xec, 0x32, 0x68, 0x48, 0x6d, 0x75, 0xc8, 0xad, 0xd7, 0x7f, 0x36, 0xfa, 0x65, 0x1a, 0xd5, 0x0f, + 0x5b, 0x77, 0x79, 0xff, 0xe3, 0xe5, 0x82, 0xba, 0x5c, 0x26, 0xdd, 0x73, 0xf8, 0x5f, 0xc5, 0x72, + 0x18, 0xae, 0xd8, 0xf6, 0x6e, 0x99, 0xa7, 0x26, 0x9c, 0x2e, 0xc6, 0xfd, 0xf8, 0xfe, 0xa9, 0x65, + 0xe6, 0xb5, 0xef, 0x9a, 0xf7, 0xf7, 0x4b, 0x06, 0x3b, 0x2a, 0x96, 0x2b, 0xc8, 0xd1, 0xfd, 0x8b, + 0x6b, 0xaf, 0x74, 0x75, 0xed, 0x95, 0x3e, 0xe6, 0x9e, 0x73, 0x91, 0x7b, 0xce, 0x65, 0xee, 0x39, + 0xdf, 0x72, 0xcf, 0xf9, 0x3c, 0xf7, 0x4a, 0x97, 0x73, 0xaf, 0x74, 0x35, 0xf7, 0x4a, 0xbf, 0x03, + 0x00, 0x00, 0xff, 0xff, 0x0f, 0x50, 0xb9, 0xa3, 0x05, 0x04, 0x00, 0x00, +} + func (m *WatchSelectors) Copy() *WatchSelectors { if m == nil { return nil @@ -151,7 +267,7 @@ func (m *StoreObject) CopyFrom(src interface{}) { *m = *o if o.WatchSelectors != nil { m.WatchSelectors = &WatchSelectors{} - deepcopy.Copy(m.WatchSelectors, o.WatchSelectors) + github_com_docker_swarmkit_api_deepcopy.Copy(m.WatchSelectors, o.WatchSelectors) } } @@ -178,7 +294,7 @@ func (m *TLSAuthorization) CopyFrom(src interface{}) { func (m *WatchSelectors) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -186,150 +302,152 @@ func (m *WatchSelectors) Marshal() (dAtA []byte, err error) { } func (m *WatchSelectors) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *WatchSelectors) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.ID != nil { - dAtA[i] = 0x8 - i++ - if *m.ID { + if m.Kind != nil { + i-- + if *m.Kind { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x68 } - if m.IDPrefix != nil { - dAtA[i] = 0x10 - i++ - if *m.IDPrefix { + if m.Membership != nil { + i-- + if *m.Membership { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x60 } - if m.Name != nil { - dAtA[i] = 0x18 - i++ - if *m.Name { + if m.Role != nil { + i-- + if *m.Role { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x58 } - if m.NamePrefix != nil { - dAtA[i] = 0x20 - i++ - if *m.NamePrefix { + if m.DesiredState != nil { + i-- + if *m.DesiredState { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x50 } - if m.Custom != nil { - dAtA[i] = 0x28 - i++ - if *m.Custom { + if m.Slot != nil { + i-- + if *m.Slot { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x48 } - if m.CustomPrefix != nil { - dAtA[i] = 0x30 - i++ - if *m.CustomPrefix { + if m.NodeID != nil { + i-- + if *m.NodeID { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x40 } if m.ServiceID != nil { - dAtA[i] = 0x38 - i++ + i-- if *m.ServiceID { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x38 } - if m.NodeID != nil { - dAtA[i] = 0x40 - i++ - if *m.NodeID { + if m.CustomPrefix != nil { + i-- + if *m.CustomPrefix { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x30 } - if m.Slot != nil { - dAtA[i] = 0x48 - i++ - if *m.Slot { + if m.Custom != nil { + i-- + if *m.Custom { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x28 } - if m.DesiredState != nil { - dAtA[i] = 0x50 - i++ - if *m.DesiredState { + if m.NamePrefix != nil { + i-- + if *m.NamePrefix { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x20 } - if m.Role != nil { - dAtA[i] = 0x58 - i++ - if *m.Role { + if m.Name != nil { + i-- + if *m.Name { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x18 } - if m.Membership != nil { - dAtA[i] = 0x60 - i++ - if *m.Membership { + if m.IDPrefix != nil { + i-- + if *m.IDPrefix { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x10 } - if m.Kind != nil { - dAtA[i] = 0x68 - i++ - if *m.Kind { + if m.ID != nil { + i-- + if *m.ID { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *StoreObject) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -337,32 +455,36 @@ func (m *StoreObject) Marshal() (dAtA []byte, err error) { } func (m *StoreObject) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StoreObject) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l if m.WatchSelectors == nil { - return 0, proto.NewRequiredNotSetError("watch_selectors") + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("watch_selectors") } else { - dAtA[i] = 0xa - i++ - i = encodeVarintPlugin(dAtA, i, uint64(m.WatchSelectors.Size())) - n1, err := m.WatchSelectors.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.WatchSelectors.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintPlugin(dAtA, i, uint64(size)) } - i += n1 - } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *TLSAuthorization) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -370,52 +492,52 @@ func (m *TLSAuthorization) Marshal() (dAtA []byte, err error) { } func (m *TLSAuthorization) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TLSAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Roles) > 0 { - for _, s := range m.Roles { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } - } if m.Insecure != nil { - dAtA[i] = 0x10 - i++ + i-- if *m.Insecure { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x10 } - if m.XXX_unrecognized != nil { - i += copy(dAtA[i:], m.XXX_unrecognized) + if len(m.Roles) > 0 { + for iNdEx := len(m.Roles) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Roles[iNdEx]) + copy(dAtA[i:], m.Roles[iNdEx]) + i = encodeVarintPlugin(dAtA, i, uint64(len(m.Roles[iNdEx]))) + i-- + dAtA[i] = 0xa + } } - return i, nil + return len(dAtA) - i, nil } func encodeVarintPlugin(dAtA []byte, offset int, v uint64) int { + offset -= sovPlugin(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } - func (m *WatchSelectors) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.ID != nil { @@ -457,26 +579,26 @@ func (m *WatchSelectors) Size() (n int) { if m.Kind != nil { n += 2 } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } return n } func (m *StoreObject) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.WatchSelectors != nil { l = m.WatchSelectors.Size() n += 1 + l + sovPlugin(uint64(l)) } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } return n } func (m *TLSAuthorization) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Roles) > 0 { @@ -488,21 +610,11 @@ func (m *TLSAuthorization) Size() (n int) { if m.Insecure != nil { n += 2 } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } return n } func sovPlugin(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozPlugin(x uint64) (n int) { return sovPlugin(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -525,7 +637,6 @@ func (this *WatchSelectors) String() string { `Role:` + valueToStringPlugin(this.Role) + `,`, `Membership:` + valueToStringPlugin(this.Membership) + `,`, `Kind:` + valueToStringPlugin(this.Kind) + `,`, - `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -535,8 +646,7 @@ func (this *StoreObject) String() string { return "nil" } s := strings.Join([]string{`&StoreObject{`, - `WatchSelectors:` + strings.Replace(fmt.Sprintf("%v", this.WatchSelectors), "WatchSelectors", "WatchSelectors", 1) + `,`, - `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `WatchSelectors:` + strings.Replace(this.WatchSelectors.String(), "WatchSelectors", "WatchSelectors", 1) + `,`, `}`, }, "") return s @@ -548,7 +658,6 @@ func (this *TLSAuthorization) String() string { s := strings.Join([]string{`&TLSAuthorization{`, `Roles:` + fmt.Sprintf("%v", this.Roles) + `,`, `Insecure:` + valueToStringPlugin(this.Insecure) + `,`, - `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -576,7 +685,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -604,7 +713,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -625,7 +734,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -646,7 +755,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -667,7 +776,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -688,7 +797,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -709,7 +818,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -730,7 +839,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -751,7 +860,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -772,7 +881,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -793,7 +902,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -814,7 +923,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -835,7 +944,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -856,7 +965,7 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -869,13 +978,12 @@ func (m *WatchSelectors) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthPlugin } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -901,7 +1009,7 @@ func (m *StoreObject) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -929,7 +1037,7 @@ func (m *StoreObject) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -938,6 +1046,9 @@ func (m *StoreObject) Unmarshal(dAtA []byte) error { return ErrInvalidLengthPlugin } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthPlugin + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -955,18 +1066,17 @@ func (m *StoreObject) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthPlugin } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if hasFields[0]&uint64(0x00000001) == 0 { - return proto.NewRequiredNotSetError("watch_selectors") + return github_com_gogo_protobuf_proto.NewRequiredNotSetError("watch_selectors") } if iNdEx > l { @@ -989,7 +1099,7 @@ func (m *TLSAuthorization) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1017,7 +1127,7 @@ func (m *TLSAuthorization) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1027,6 +1137,9 @@ func (m *TLSAuthorization) Unmarshal(dAtA []byte) error { return ErrInvalidLengthPlugin } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPlugin + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1046,7 +1159,7 @@ func (m *TLSAuthorization) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1059,13 +1172,12 @@ func (m *TLSAuthorization) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthPlugin } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1078,6 +1190,7 @@ func (m *TLSAuthorization) Unmarshal(dAtA []byte) error { func skipPlugin(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1109,10 +1222,8 @@ func skipPlugin(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1129,97 +1240,34 @@ func skipPlugin(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthPlugin } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowPlugin - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipPlugin(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupPlugin + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthPlugin + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthPlugin = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowPlugin = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthPlugin = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowPlugin = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupPlugin = fmt.Errorf("proto: unexpected end of group") ) - -func init() { - proto.RegisterFile("github.com/docker/swarmkit/protobuf/plugin/plugin.proto", fileDescriptorPlugin) -} - -var fileDescriptorPlugin = []byte{ - // 575 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x52, 0xc1, 0x6e, 0xd3, 0x4c, - 0x10, 0xae, 0xd3, 0x36, 0x4d, 0x26, 0x69, 0xff, 0xfe, 0x2b, 0x54, 0xad, 0x7a, 0x70, 0xaa, 0x46, - 0x42, 0x41, 0x42, 0x8e, 0xd4, 0x0b, 0x52, 0x6e, 0x94, 0x5c, 0x22, 0x01, 0x45, 0x0e, 0x12, 0x37, - 0x22, 0xc7, 0x3b, 0x4d, 0x96, 0x3a, 0x5e, 0x6b, 0x77, 0x4d, 0x0a, 0x27, 0x5e, 0x80, 0x07, 0xe0, - 0xca, 0xd3, 0xf4, 0xc8, 0x91, 0x53, 0x44, 0x2d, 0x71, 0xe0, 0x06, 0x6f, 0x80, 0x76, 0xd7, 0x69, - 0x08, 0x6a, 0xc5, 0xc9, 0x33, 0xdf, 0x7c, 0xdf, 0xcc, 0x7c, 0x3b, 0x86, 0x47, 0x13, 0xae, 0xa7, - 0xf9, 0x38, 0x88, 0xc5, 0xac, 0xcb, 0x44, 0x7c, 0x81, 0xb2, 0xab, 0xe6, 0x91, 0x9c, 0x5d, 0x70, - 0xdd, 0xcd, 0xa4, 0xd0, 0x62, 0x9c, 0x9f, 0x77, 0xb3, 0x24, 0x9f, 0xf0, 0xb4, 0xfc, 0x04, 0x16, - 0x26, 0x07, 0x8e, 0x1d, 0x2c, 0x49, 0x81, 0xab, 0x1e, 0x1e, 0x4d, 0x84, 0x98, 0x24, 0xb8, 0x12, - 0x33, 0x54, 0xb1, 0xe4, 0x99, 0x16, 0x25, 0xf7, 0xf8, 0xd3, 0x26, 0xec, 0xbd, 0x8a, 0x74, 0x3c, - 0x1d, 0x62, 0x82, 0xb1, 0x16, 0x52, 0x91, 0x03, 0xa8, 0x70, 0x46, 0xbd, 0x23, 0xaf, 0x53, 0x3b, - 0xad, 0x16, 0x8b, 0x56, 0x65, 0xd0, 0x0f, 0x2b, 0x9c, 0x91, 0x07, 0x50, 0xe7, 0x6c, 0x94, 0x49, - 0x3c, 0xe7, 0x97, 0xb4, 0x62, 0xcb, 0xcd, 0x62, 0xd1, 0xaa, 0x0d, 0xfa, 0x2f, 0x2c, 0x16, 0xd6, - 0x38, 0x73, 0x11, 0x21, 0xb0, 0x95, 0x46, 0x33, 0xa4, 0x9b, 0x86, 0x15, 0xda, 0x98, 0xb4, 0xa0, - 0x61, 0xbe, 0xcb, 0x06, 0x5b, 0xb6, 0x04, 0x06, 0x2a, 0x45, 0x07, 0x50, 0x8d, 0x73, 0xa5, 0xc5, - 0x8c, 0x6e, 0xdb, 0x5a, 0x99, 0x91, 0x36, 0xec, 0xba, 0x68, 0x29, 0xad, 0xda, 0x72, 0xd3, 0x81, - 0xa5, 0xf8, 0x21, 0x80, 0x42, 0xf9, 0x96, 0xc7, 0x38, 0xe2, 0x8c, 0xee, 0xd8, 0xed, 0x76, 0x8b, - 0x45, 0xab, 0x3e, 0x74, 0xe8, 0xa0, 0x1f, 0xd6, 0x4b, 0xc2, 0x80, 0x91, 0x36, 0xec, 0xa4, 0x82, - 0x59, 0x6a, 0xcd, 0x52, 0xa1, 0x58, 0xb4, 0xaa, 0xcf, 0x05, 0x33, 0xbc, 0xaa, 0x29, 0x0d, 0x98, - 0x31, 0xa1, 0x12, 0xa1, 0x69, 0xdd, 0x99, 0x30, 0xb1, 0xd9, 0x85, 0xa1, 0xe2, 0x12, 0xd9, 0x48, - 0xe9, 0x48, 0x23, 0x05, 0xb7, 0x4b, 0x09, 0x0e, 0x0d, 0x66, 0x84, 0x52, 0x24, 0x48, 0x1b, 0x4e, - 0x68, 0x62, 0xe2, 0x03, 0xcc, 0x70, 0x36, 0x46, 0xa9, 0xa6, 0x3c, 0xa3, 0x4d, 0x67, 0x7e, 0x85, - 0x18, 0xcd, 0x05, 0x4f, 0x19, 0xdd, 0x75, 0x1a, 0x13, 0x1f, 0xbf, 0x86, 0xc6, 0x50, 0x0b, 0x89, - 0x67, 0xe3, 0x37, 0x18, 0x6b, 0x72, 0x06, 0xff, 0xcd, 0xcd, 0xa5, 0x46, 0x6a, 0x79, 0x2a, 0xea, - 0x1d, 0x55, 0x3a, 0x8d, 0x93, 0xfb, 0xc1, 0xed, 0xe7, 0x0f, 0xd6, 0x0f, 0x1b, 0xee, 0xcd, 0xd7, - 0xf2, 0xe3, 0x3e, 0xec, 0xbf, 0x7c, 0x3a, 0x7c, 0x9c, 0xeb, 0xa9, 0x90, 0xfc, 0x7d, 0xa4, 0xb9, - 0x48, 0xc9, 0x3d, 0xd8, 0x36, 0xfb, 0x9a, 0xd6, 0x9b, 0x9d, 0x7a, 0xe8, 0x12, 0x72, 0x08, 0x35, - 0x9e, 0x2a, 0x8c, 0x73, 0x89, 0xee, 0xf2, 0xe1, 0x4d, 0xde, 0x7b, 0x02, 0x35, 0x86, 0x98, 0xc5, - 0x22, 0x7b, 0x47, 0x5a, 0x81, 0xfb, 0xe1, 0x56, 0x9b, 0x3c, 0x43, 0xa5, 0xa2, 0x09, 0x9e, 0x65, - 0xa6, 0xbb, 0xa2, 0x3f, 0x3f, 0xdb, 0xbb, 0xf7, 0xb6, 0xb4, 0xcc, 0x31, 0xbc, 0x11, 0xf6, 0x38, - 0x34, 0x95, 0xb1, 0x3a, 0x12, 0xce, 0xeb, 0x3f, 0x1b, 0xfd, 0xb2, 0x8d, 0x1a, 0x27, 0xed, 0xbb, - 0xbc, 0xff, 0xf1, 0x72, 0x61, 0x43, 0xad, 0x92, 0xde, 0x25, 0xfc, 0xaf, 0x13, 0x35, 0x8a, 0xd6, - 0x6c, 0xfb, 0xb7, 0xcc, 0xd3, 0x53, 0xc1, 0x96, 0xe3, 0x7e, 0x7c, 0xff, 0xd8, 0xb6, 0xf3, 0x3a, - 0x77, 0xcd, 0xfb, 0xfb, 0x25, 0xc3, 0x7d, 0x9d, 0xa8, 0x35, 0xe4, 0x94, 0x5e, 0x5d, 0xfb, 0x1b, - 0x5f, 0xaf, 0xfd, 0x8d, 0x0f, 0x85, 0xef, 0x5d, 0x15, 0xbe, 0xf7, 0xa5, 0xf0, 0xbd, 0x6f, 0x85, - 0xef, 0xfd, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xb3, 0x99, 0x7d, 0xfb, 0xf9, 0x03, 0x00, 0x00, -} diff --git a/vendor/github.com/docker/swarmkit/vendor.conf b/vendor/github.com/docker/swarmkit/vendor.conf index f91ea497e0af1..fe8a3cd498e6f 100644 --- a/vendor/github.com/docker/swarmkit/vendor.conf +++ b/vendor/github.com/docker/swarmkit/vendor.conf @@ -8,60 +8,72 @@ # In >=1.11, those errors were brought back but the string had changed again. # After updating GRPC, if integration test failures occur, verify that the # string matching there is correct. -google.golang.org/grpc v1.12.0 -github.com/gogo/protobuf v1.0.0 -github.com/golang/protobuf v1.1.0 -github.com/matttproud/golang_protobuf_extensions v1.0.0 -google.golang.org/genproto 694d95ba50e67b2e363f3483057db5d4910c18f9 +google.golang.org/grpc f495f5b15ae7ccda3b38c53a1bfcde4c1a58a2bc # v1.27.1 +github.com/gogo/protobuf b03c65ea87cdc3521ede29f62fe3ce239267c1bc # v1.3.2 +github.com/golang/protobuf 84668698ea25b64748563aa20726db66a6b8d299 # v1.3.5 +github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c # v1.0.1 +google.golang.org/genproto 3f1135a288c9a07e340ae8ba4cc6c7065a3160e8 # metrics -github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 -github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18 +github.com/grpc-ecosystem/go-grpc-prometheus c225b8c3b01faf2899099b768856a9e916e5087b # v1.2.0 +github.com/docker/go-metrics b619b3592b65de4f087d9f16863a7e6ff905973c # v0.0.1 # etcd/raft -github.com/coreos/etcd v3.2.1 -github.com/coreos/go-systemd v17 -github.com/coreos/pkg v3 -github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e -github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6 -github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8 -github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 +github.com/coreos/etcd 2c834459e1aab78a5d5219c7dfe42335fc4b617a # v3.3.25 -github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 -github.com/docker/docker b9bb3bae5161f931c1dede43c67948c599197f50 -github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6 -github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 -github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1 -github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef -github.com/docker/libnetwork a79d3687931697244b8e03485bf7b2042f8ec6b6 -github.com/opencontainers/runc ad0f5255060d36872be04de22f8731f38ef2d7b1 -github.com/opencontainers/go-digest v1.0.0-rc1 -github.com/opencontainers/image-spec v1.0.1 -github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb +# go-systemd v17 is required by github.com/coreos/pkg/capnslog/journald_formatter.go +github.com/coreos/go-systemd 39ca1b05acc7ad1220e09f133283b8859a8b71ab # v17 +github.com/coreos/pkg 97fdf19511ea361ae1c100dd393cc47f8dcfa1e1 # v4 +github.com/prometheus/client_golang 6edbbd9e560190e318cdc5b4d3e630b442858380 # v1.6.0 +github.com/prometheus/client_model 7bc5445566f0fe75b15de23e6b93886e982d7bf9 # v0.2.0 +github.com/prometheus/common d978bcb1309602d68bb4ba69cf3f8ed900e07308 # v0.9.1 +github.com/prometheus/procfs 46159f73e74d1cb8dc223deef9b2d049286f46b1 # v0.0.11 +github.com/cespare/xxhash/v2 d7df74196a9e781ede915320c11c378c1b2f3a1f # v2.1.1 -github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0 -github.com/Microsoft/go-winio v0.4.8 -github.com/sirupsen/logrus v1.0.3 -github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 -github.com/cloudflare/cfssl 1.3.2 -github.com/dustin/go-humanize 8929fe90cee4b2cb9deb468b51fb34eba64d1bf0 -github.com/fernet/fernet-go 1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2 -github.com/google/certificate-transparency-go v1.0.20 -github.com/hashicorp/go-immutable-radix 826af9ccf0feeee615d546d69b11f8e98da8c8f1 git://github.com/tonistiigi/go-immutable-radix.git -github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad -github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4 -github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 -github.com/phayes/permbits f7e3ac5e859d0b919c5068d581cc4c5d4f4f9bc5 -github.com/pivotal-golang/clock 3fd3c1944c59d9742e1cd333672181cd1a6f9fa0 -github.com/pkg/errors 645ef00459ed84a119197bfb8d8205042c6df63d -github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2 -github.com/rcrowley/go-metrics 51425a2415d21afadfd55cd93432c0bc69e9598d -github.com/spf13/cobra 8e91712f174ced10270cf66615e0a9127e7c4de5 -github.com/spf13/pflag 7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7 -github.com/stretchr/testify v1.1.4 -go.etcd.io/bbolt v1.3.1-etcd.8 -golang.org/x/crypto 1a580b3eff7814fc9b40602fd35256c63b50f491 -golang.org/x/net 0ed95abb35c445290478a5348a7b38bb154135fd -golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd -golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 -golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb +github.com/docker/distribution 0d3efadf0154c2b8a4e7b6621fff9809655cc580 +github.com/docker/docker 471fd27709777d2cce3251129887e14e8bb2e0c7 # master / v21.xx-dev +github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4.0 +github.com/docker/go-events e31b211e4f1cd09aa76fe4ac244571fab96ae47f +github.com/docker/go-units 519db1ee28dcc9fd2474ae59fca29a810482bfb1 # v0.4.0 +github.com/docker/libkv 458977154600b9f23984d9f4b82e79570b5ae12b +github.com/opencontainers/runc b9ee9c6314599f1b4a7f497e1f1f856fe433d3b7 # v1.0.0-rc95 +github.com/opencontainers/go-digest ea51bea511f75cfa3ef6098cc253c5c3609b037a # v1.0.0 +github.com/opencontainers/image-spec d60099175f88c47cd379c4738d158884749ed235 # v1.0.1 +github.com/ishidawataru/sctp f2269e66cdee387bd321445d5d300893449805be +github.com/containerd/containerd 36cc874494a56a253cd181a1a685b44b58a2e34a # v1.5.2 + +github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73 # v1.1.1 +github.com/Microsoft/go-winio 5b44b70ab3ab4d291a7c1d28afe7b4afeced0ed4 # v0.4.15 +github.com/sirupsen/logrus 6699a89a232f3db797f2e280639854bbc4b89725 # v1.7.0 +github.com/beorn7/perks 37c8de3658fcb183f997c4e13e8337516ab753e6 # v1.0.1 +github.com/cloudflare/cfssl 5d63dbd981b5c408effbb58c442d54761ff94fbd # 1.3.2 +github.com/dustin/go-humanize 9f541cc9db5d55bce703bd99987c9d5cb8eea45e # v1.0.0 +github.com/fernet/fernet-go 9eac43b88a5efb8651d24de9b68e87567e029736 +github.com/google/certificate-transparency-go 37a384cd035e722ea46e55029093e26687138edf # v1.0.20 +github.com/hashicorp/go-immutable-radix 826af9ccf0feeee615d546d69b11f8e98da8c8f1 git://github.com/tonistiigi/go-immutable-radix.git +github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad +github.com/hashicorp/golang-lru 7f827b33c0f158ec5dfbba01bb0b14a4541fd81d # v0.5.3 +github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 # v1.0.0 +github.com/phayes/permbits f7e3ac5e859d0b919c5068d581cc4c5d4f4f9bc5 +code.cloudfoundry.org/clock 02e53af36e6c978af692887ed449b74026d76fec # v1.0.0 +github.com/pkg/errors 614d223910a179a466c1767a985424175c39b465 # v0.9.1 +github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2 # v1.0.0 +github.com/rcrowley/go-metrics 51425a2415d21afadfd55cd93432c0bc69e9598d +github.com/spf13/cobra 8380ddd3132bdf8fd77731725b550c181dda0aa8 # v1.1.3 +github.com/spf13/pflag 2e9d26c8c37aae03e3f9d4e90b7116f5accb7cab # v1.0.5 +github.com/stretchr/testify ffdc059bfe9ce6a4e144ba849dbedead332c6053 # v1.3.0 +go.etcd.io/bbolt 232d8fc87f50244f9c808f4745759e08a304c029 # v1.3.5 +golang.org/x/crypto c1f2f97bffc9c53fc40a1a28a5b460094c0050d9 +golang.org/x/net 6772e930b67bb09bf22262c7378e7d2f67cf59d1 +golang.org/x/sys d19ff857e887eacb631721f188c7d365c2331456 +golang.org/x/text 23ae387dee1f90d29a23c0e87ee0b46038fbed0e # v0.3.3 +golang.org/x/time 555d28b269f0569763d25dbe1a237ae74c6bcc82 + +# ginkgo is used for testing in some places in the code. this is it and its +# sub-dependencies. +github.com/onsi/ginkgo eea6ad008b96acdaa524f5b409513bf062b500ad # v1.8.0 +github.com/onsi/gomega 90e289841c1ed79b7a598a7cd9959750cb5e89e2 # v1.5.0 +gopkg.in/yaml.v2 7649d4548cb53a614db133b2a8ac1f31859dda8c # v2.4.0 +github.com/hpcloud/tail a30252cb686a21eb2d0b98132633053ec2f7f1e5 # v1.0.0 +gopkg.in/fsnotify.v1 c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9 # v1.4.7 +gopkg.in/tomb.v1 dd632973f1e7218eb1089048e0798ec9ae7dceb8 # v1 diff --git a/vendor/github.com/fernet/fernet-go/fernet.go b/vendor/github.com/fernet/fernet-go/fernet.go index 8549e69c45e04..9e4bcce35ce14 100644 --- a/vendor/github.com/fernet/fernet-go/fernet.go +++ b/vendor/github.com/fernet/fernet-go/fernet.go @@ -67,7 +67,7 @@ func verify(msg, tok []byte, ttl time.Duration, now time.Time, k *Key) []byte { return nil } ts := time.Unix(int64(binary.BigEndian.Uint64(tok[1:])), 0) - if ttl >= 0 && (now.After(ts.Add(ttl)) || ts.After(now.Add(maxClockSkew))) { + if ttl > 0 && (now.After(ts.Add(ttl)) || ts.After(now.Add(maxClockSkew))) { return nil } n := len(tok) - sha256.Size diff --git a/vendor/github.com/fluent/fluent-logger-golang/README.md b/vendor/github.com/fluent/fluent-logger-golang/README.md index cbb8bdc5420f9..554619a31cd0e 100644 --- a/vendor/github.com/fluent/fluent-logger-golang/README.md +++ b/vendor/github.com/fluent/fluent-logger-golang/README.md @@ -1,7 +1,8 @@ fluent-logger-golang ==== -[![Build Status](https://travis-ci.org/fluent/fluent-logger-golang.png?branch=master)](https://travis-ci.org/fluent/fluent-logger-golang) +[![Build Status](https://github.com/fluent/fluent-logger-golang/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/fluent/fluent-logger-golang/actions) +[![GoDoc](https://godoc.org/github.com/fluent/fluent-logger-golang/fluent?status.svg)](https://godoc.org/github.com/fluent/fluent-logger-golang/fluent) ## A structured event logger for Fluentd (Golang) @@ -19,8 +20,6 @@ Install the package with `go get` and use `import` to include it in your project import "github.com/fluent/fluent-logger-golang/fluent" ``` -GoDoc: http://godoc.org/github.com/fluent/fluent-logger-golang/fluent - ## Example ```go @@ -29,7 +28,7 @@ package main import ( "github.com/fluent/fluent-logger-golang/fluent" "fmt" - "time" + //"time" ) func main() { @@ -59,12 +58,125 @@ func main() { f := fluent.New(fluent.Config{FluentPort: 80, FluentHost: "example.com"}) ``` +### FluentNetwork + +Specify the network protocol. The supported values are: + + * "tcp" (use `FluentHost` and `FluentPort`) + * "tls" (use`FluentHost` and `FluentPort`) + * "unix" (use `FluentSocketPath`) + +The default is "tcp". + +### FluentHost + +Specify a hostname or IP address as a string for the destination of the "tcp" protocol. +The default is "127.0.0.1". + +### FluentPort + +Specify the TCP port of the destination. The default is 24224. + +### FluentSocketPath + +Specify the unix socket path when `FluentNetwork` is "unix". + +### Timeout + +Set the timeout value of `time.Duration` to connect to the destination. +The default is 3 seconds. + ### WriteTimeout -Sets the timeout for Write call of logger.Post. +Sets the timeout value of `time.Duration` for Write call of `logger.Post`. Since the default is zero value, Write will not time out. +### BufferLimit + +Sets the number of events buffered on the memory. Records will be stored in memory up to this number. If the buffer is full, the call to record logs will fail. +The default is 8192. + +### RetryWait + +Set the duration of the initial wait for the first retry, in milliseconds. The actual retry wait will be `r * 1.5^(N-1)` (r: this value, N: the number of retries). +The default is 500. + +### MaxRetry + +Sets the maximum number of retries. If the number of retries become larger than this value, the write/send operation will fail. +The default is 13. + +### MaxRetryWait + +The maximum duration of wait between retries, in milliseconds. If the calculated retry wait is larger than this value, the actual retry wait will be this value. +The default is 60,000 (60 seconds). + +### TagPrefix + +Sets the prefix string of the tag. Prefix will be appended with a dot `.`, like `ppp.tag` (ppp: the value of this parameter, tag: the tag string specified in a call). +The default is blank. + +### Async + +Enable asynchronous I/O (connect and write) for sending events to Fluentd. +The default is false. + +### ForceStopAsyncSend + +When Async is enabled, immediately discard the event queue on close() and return (instead of trying MaxRetry times for each event in the queue before returning) +The default is false. + +### AsyncResultCallback + +When Async is enabled, if this is callback is provided, it will be called on every write to Fluentd. The callback function +takes two arguments - a `[]byte` of the message that was to be sent and an `error`. If the `error` is not nil this means the +delivery of the message was unsuccessful. + +### SubSecondPrecision + +Enable time encoding as EventTime, which contains sub-second precision values. The messages encoded with this option can be received only by Fluentd v0.14 or later. +The default is false. + +### MarshalAsJson + +Enable Json data marshaling to send messages using Json format (instead of the standard MessagePack). It is supported by Fluentd `in_forward` plugin. +The default is false. + +### RequestAck + +Sets whether to request acknowledgment from Fluentd to increase the reliability +of the connection. The default is false. + +### TlsInsecureSkipVerify + +Skip verifying the server certificate. Useful for development and testing. The default is false. + +## FAQ + +### Does this logger support the features of Fluentd Forward Protocol v1? + +"the features" includes heartbeat messages (for TCP keepalive), TLS transport and shared key authentication. + +This logger doesn't support those features. Patches are welcome! + +### Is it allowed to call `Fluent.Post()` after connection close? + +Before v1.8.0, the Fluent logger silently reopened connections whenever +`Fluent.Post()` was called. + +```go +logger, _ := fluent.New(fluent.Config{}) +logger.Post(tag, data) +logger.Close() +logger.Post(tag, data) /* reopen connection */ +``` + +However, this behavior was confusing, in particular when multiple goroutines +were involved. Starting v1.8.0, the logger no longer accepts `Fluent.Post()` +after `Fluent.Close()`, and instead returns a "Logger already closed" error. + ## Tests ``` + go test ``` diff --git a/vendor/github.com/fluent/fluent-logger-golang/fluent/fluent.go b/vendor/github.com/fluent/fluent-logger-golang/fluent/fluent.go index 4693c5c3b5898..c2e1b25955586 100644 --- a/vendor/github.com/fluent/fluent-logger-golang/fluent/fluent.go +++ b/vendor/github.com/fluent/fluent-logger-golang/fluent/fluent.go @@ -1,16 +1,24 @@ package fluent import ( + "context" + "crypto/tls" "encoding/json" "errors" "fmt" "math" + "math/rand" "net" + "os" "reflect" "strconv" "sync" "time" + "bytes" + "encoding/base64" + "encoding/binary" + "github.com/tinylib/msgp/msgp" ) @@ -21,47 +29,104 @@ const ( defaultPort = 24224 defaultTimeout = 3 * time.Second defaultWriteTimeout = time.Duration(0) // Write() will not time out - defaultBufferLimit = 8 * 1024 * 1024 + defaultBufferLimit = 8 * 1024 defaultRetryWait = 500 + defaultMaxRetryWait = 60000 defaultMaxRetry = 13 defaultReconnectWaitIncreRate = 1.5 // Default sub-second precision value to false since it is only compatible // with fluentd versions v0.14 and above. defaultSubSecondPrecision = false + + // Default value whether to skip checking insecure certs on TLS connections. + defaultTlsInsecureSkipVerify = false ) +// randomGenerator is used by getUniqueId to generate ack hashes. Its value is replaced +// during tests with a deterministic function. +var randomGenerator = rand.Uint64 + type Config struct { - FluentPort int `json:"fluent_port"` - FluentHost string `json:"fluent_host"` - FluentNetwork string `json:"fluent_network"` - FluentSocketPath string `json:"fluent_socket_path"` - Timeout time.Duration `json:"timeout"` - WriteTimeout time.Duration `json:"write_timeout"` - BufferLimit int `json:"buffer_limit"` - RetryWait int `json:"retry_wait"` - MaxRetry int `json:"max_retry"` - TagPrefix string `json:"tag_prefix"` - AsyncConnect bool `json:"async_connect"` - MarshalAsJSON bool `json:"marshal_as_json"` + FluentPort int `json:"fluent_port"` + FluentHost string `json:"fluent_host"` + FluentNetwork string `json:"fluent_network"` + FluentSocketPath string `json:"fluent_socket_path"` + Timeout time.Duration `json:"timeout"` + WriteTimeout time.Duration `json:"write_timeout"` + BufferLimit int `json:"buffer_limit"` + RetryWait int `json:"retry_wait"` + MaxRetry int `json:"max_retry"` + MaxRetryWait int `json:"max_retry_wait"` + TagPrefix string `json:"tag_prefix"` + Async bool `json:"async"` + ForceStopAsyncSend bool `json:"force_stop_async_send"` + AsyncResultCallback func(data []byte, err error) + // Deprecated: Use Async instead + AsyncConnect bool `json:"async_connect"` + MarshalAsJSON bool `json:"marshal_as_json"` // Sub-second precision timestamps are only possible for those using fluentd // v0.14+ and serializing their messages with msgpack. SubSecondPrecision bool `json:"sub_second_precision"` + + // RequestAck sends the chunk option with a unique ID. The server will + // respond with an acknowledgement. This option improves the reliability + // of the message transmission. + RequestAck bool `json:"request_ack"` + + // Flag to skip verifying insecure certs on TLS connections + TlsInsecureSkipVerify bool `json: "tls_insecure_skip_verify"` +} + +type ErrUnknownNetwork struct { + network string +} + +func (e *ErrUnknownNetwork) Error() string { + return "unknown network " + e.network +} + +func NewErrUnknownNetwork(network string) error { + return &ErrUnknownNetwork{network} +} + +type msgToSend struct { + data []byte + ack string } type Fluent struct { Config - mubuff sync.Mutex - pending []byte + dialer dialer + // stopRunning is used in async mode to signal to run() it should abort. + stopRunning chan struct{} + // cancelDialings is used by Close() to stop any in-progress dialing. + cancelDialings context.CancelFunc + pending chan *msgToSend + pendingMutex sync.RWMutex + closed bool + wg sync.WaitGroup + + muconn sync.RWMutex + conn net.Conn +} - muconn sync.Mutex - conn net.Conn - reconnecting bool +type dialer interface { + DialContext(ctx context.Context, network, address string) (net.Conn, error) } // New creates a new Logger. -func New(config Config) (f *Fluent, err error) { +func New(config Config) (*Fluent, error) { + if config.Timeout == 0 { + config.Timeout = defaultTimeout + } + return newWithDialer(config, &net.Dialer{ + Timeout: config.Timeout, + }) +} + +func newWithDialer(config Config, d dialer) (f *Fluent, err error) { if config.FluentNetwork == "" { config.FluentNetwork = defaultNetwork } @@ -74,9 +139,6 @@ func New(config Config) (f *Fluent, err error) { if config.FluentSocketPath == "" { config.FluentSocketPath = defaultSocketPath } - if config.Timeout == 0 { - config.Timeout = defaultTimeout - } if config.WriteTimeout == 0 { config.WriteTimeout = defaultWriteTimeout } @@ -89,12 +151,39 @@ func New(config Config) (f *Fluent, err error) { if config.MaxRetry == 0 { config.MaxRetry = defaultMaxRetry } + if config.MaxRetryWait == 0 { + config.MaxRetryWait = defaultMaxRetryWait + } + if !config.TlsInsecureSkipVerify { + config.TlsInsecureSkipVerify = defaultTlsInsecureSkipVerify + } if config.AsyncConnect { - f = &Fluent{Config: config, reconnecting: true} - go f.reconnect() + fmt.Fprintf(os.Stderr, "fluent#New: AsyncConnect is now deprecated, please use Async instead") + config.Async = config.Async || config.AsyncConnect + } + + if config.Async { + ctx, cancel := context.WithCancel(context.Background()) + + f = &Fluent{ + Config: config, + dialer: d, + stopRunning: make(chan struct{}), + cancelDialings: cancel, + pending: make(chan *msgToSend, config.BufferLimit), + pendingMutex: sync.RWMutex{}, + muconn: sync.RWMutex{}, + } + + f.wg.Add(1) + go f.run(ctx) } else { - f = &Fluent{Config: config, reconnecting: false} - err = f.connect() + f = &Fluent{ + Config: config, + dialer: d, + muconn: sync.RWMutex{}, + } + err = f.connect(context.Background()) } return } @@ -147,13 +236,18 @@ func (f *Fluent) PostWithTime(tag string, tm time.Time, message interface{}) err fields := msgtype.NumField() for i := 0; i < fields; i++ { field := msgtype.Field(i) + value := msg.FieldByIndex(field.Index) + // ignore unexported fields + if !value.CanInterface() { + continue + } name := field.Name if n1 := field.Tag.Get("msg"); n1 != "" { name = n1 } else if n2 := field.Tag.Get("codec"); n2 != "" { name = n2 } - kv[name] = msg.FieldByIndex(field.Index).Interface() + kv[name] = value.Interface() } return f.EncodeAndPostData(tag, tm, kv) } @@ -173,28 +267,29 @@ func (f *Fluent) PostWithTime(tag string, tm time.Time, message interface{}) err } func (f *Fluent) EncodeAndPostData(tag string, tm time.Time, message interface{}) error { - var data []byte + var msg *msgToSend var err error - if data, err = f.EncodeData(tag, tm, message); err != nil { + if msg, err = f.EncodeData(tag, tm, message); err != nil { return fmt.Errorf("fluent#EncodeAndPostData: can't convert '%#v' to msgpack:%v", message, err) } - return f.postRawData(data) + return f.postRawData(msg) } // Deprecated: Use EncodeAndPostData instead -func (f *Fluent) PostRawData(data []byte) { - f.postRawData(data) +func (f *Fluent) PostRawData(msg *msgToSend) { + f.postRawData(msg) } -func (f *Fluent) postRawData(data []byte) error { - if err := f.appendBuffer(data); err != nil { - return err +func (f *Fluent) postRawData(msg *msgToSend) error { + if f.Config.Async { + return f.appendBuffer(msg) } - if err := f.send(); err != nil { - f.close() - return err + + // Synchronous write + if f.closed { + return fmt.Errorf("fluent#postRawData: Logger already closed") } - return nil + return f.writeWithRetry(context.Background(), msg) } // For sending forward protocol adopted JSON @@ -207,126 +302,315 @@ type MessageChunk struct { // So, it should write JSON marshaler by hand. func (chunk *MessageChunk) MarshalJSON() ([]byte, error) { data, err := json.Marshal(chunk.message.Record) - return []byte(fmt.Sprintf("[\"%s\",%d,%s,null]", chunk.message.Tag, - chunk.message.Time, data)), err + if err != nil { + return nil, err + } + option, err := json.Marshal(chunk.message.Option) + if err != nil { + return nil, err + } + return []byte(fmt.Sprintf("[\"%s\",%d,%s,%s]", chunk.message.Tag, + chunk.message.Time, data, option)), err +} + +// getUniqueID returns a base64 encoded unique ID that can be used for chunk/ack +// mechanism, see +// https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1#option +func getUniqueID(timeUnix int64) (string, error) { + buf := bytes.NewBuffer(nil) + enc := base64.NewEncoder(base64.StdEncoding, buf) + if err := binary.Write(enc, binary.LittleEndian, timeUnix); err != nil { + enc.Close() + return "", err + } + if err := binary.Write(enc, binary.LittleEndian, randomGenerator()); err != nil { + enc.Close() + return "", err + } + // encoder needs to be closed before buf.String(), defer does not work + // here + enc.Close() + return buf.String(), nil } -func (f *Fluent) EncodeData(tag string, tm time.Time, message interface{}) (data []byte, err error) { +func (f *Fluent) EncodeData(tag string, tm time.Time, message interface{}) (msg *msgToSend, err error) { + option := make(map[string]string) + msg = &msgToSend{} timeUnix := tm.Unix() + if f.Config.RequestAck { + var err error + msg.ack, err = getUniqueID(timeUnix) + if err != nil { + return nil, err + } + option["chunk"] = msg.ack + } if f.Config.MarshalAsJSON { - msg := Message{Tag: tag, Time: timeUnix, Record: message} - chunk := &MessageChunk{message: msg} - data, err = json.Marshal(chunk) + m := Message{Tag: tag, Time: timeUnix, Record: message, Option: option} + chunk := &MessageChunk{message: m} + msg.data, err = json.Marshal(chunk) } else if f.Config.SubSecondPrecision { - msg := &MessageExt{Tag: tag, Time: EventTime(tm), Record: message} - data, err = msg.MarshalMsg(nil) + m := &MessageExt{Tag: tag, Time: EventTime(tm), Record: message, Option: option} + msg.data, err = m.MarshalMsg(nil) } else { - msg := &Message{Tag: tag, Time: timeUnix, Record: message} - data, err = msg.MarshalMsg(nil) + m := &Message{Tag: tag, Time: timeUnix, Record: message, Option: option} + msg.data, err = m.MarshalMsg(nil) } return } -// Close closes the connection. +// Close closes the connection, waiting for pending logs to be sent. If the client is +// running in async mode, the run() goroutine exits before Close() returns. func (f *Fluent) Close() (err error) { - if len(f.pending) > 0 { - err = f.send() + if f.Config.Async { + f.pendingMutex.Lock() + if f.closed { + f.pendingMutex.Unlock() + return nil + } + f.closed = true + f.pendingMutex.Unlock() + + if f.Config.ForceStopAsyncSend { + close(f.stopRunning) + f.cancelDialings() + } + + close(f.pending) + // If ForceStopAsyncSend is false, all logs in the channel have to be sent + // before closing the connection. At this point closed is true so no more + // logs are written to the channel and f.pending has been closed, so run() + // goroutine will exit as soon as all logs in the channel are sent. + if !f.Config.ForceStopAsyncSend { + f.wg.Wait() + } } + + f.muconn.Lock() f.close() + f.closed = true + f.muconn.Unlock() + + // If ForceStopAsyncSend is true, we shall close the connection before waiting for + // run() goroutine to exit to be sure we aren't waiting on ack message that might + // never come (eg. because fluentd server is down). However we want to be sure the + // run() goroutine stops before returning from Close(). + if f.Config.ForceStopAsyncSend { + f.wg.Wait() + } return } // appendBuffer appends data to buffer with lock. -func (f *Fluent) appendBuffer(data []byte) error { - f.mubuff.Lock() - defer f.mubuff.Unlock() - if len(f.pending)+len(data) > f.Config.BufferLimit { - return errors.New(fmt.Sprintf("fluent#appendBuffer: Buffer full, limit %v", f.Config.BufferLimit)) +func (f *Fluent) appendBuffer(msg *msgToSend) error { + f.pendingMutex.RLock() + defer f.pendingMutex.RUnlock() + if f.closed { + return fmt.Errorf("fluent#appendBuffer: Logger already closed") + } + select { + case f.pending <- msg: + default: + return fmt.Errorf("fluent#appendBuffer: Buffer full, limit %v", f.Config.BufferLimit) } - f.pending = append(f.pending, data...) return nil } -// close closes the connection. +// close closes the connection. Callers should take care of locking muconn first. func (f *Fluent) close() { - f.muconn.Lock() if f.conn != nil { f.conn.Close() f.conn = nil } - f.muconn.Unlock() } -// connect establishes a new connection using the specified transport. -func (f *Fluent) connect() (err error) { - f.muconn.Lock() - defer f.muconn.Unlock() - +// connect establishes a new connection using the specified transport. Caller should +// take care of locking muconn first. +func (f *Fluent) connect(ctx context.Context) (err error) { switch f.Config.FluentNetwork { case "tcp": - f.conn, err = net.DialTimeout(f.Config.FluentNetwork, f.Config.FluentHost+":"+strconv.Itoa(f.Config.FluentPort), f.Config.Timeout) + f.conn, err = f.dialer.DialContext(ctx, + f.Config.FluentNetwork, + f.Config.FluentHost+":"+strconv.Itoa(f.Config.FluentPort)) + case "tls": + tlsConfig := &tls.Config{InsecureSkipVerify: f.Config.TlsInsecureSkipVerify} + f.conn, err = tls.DialWithDialer( + &net.Dialer{Timeout: f.Config.Timeout}, + "tcp", + f.Config.FluentHost+":"+strconv.Itoa(f.Config.FluentPort), tlsConfig, + ) case "unix": - f.conn, err = net.DialTimeout(f.Config.FluentNetwork, f.Config.FluentSocketPath, f.Config.Timeout) + f.conn, err = f.dialer.DialContext(ctx, + f.Config.FluentNetwork, + f.Config.FluentSocketPath) default: - err = net.UnknownNetworkError(f.Config.FluentNetwork) + err = NewErrUnknownNetwork(f.Config.FluentNetwork) } - if err == nil { - f.reconnecting = false + return err +} + +var errIsClosing = errors.New("fluent logger is closing") + +// Caller should take care of locking muconn first. +func (f *Fluent) connectWithRetry(ctx context.Context) error { + // A Time channel is used instead of time.Sleep() to avoid blocking this + // goroutine during way too much time (because of the exponential back-off + // retry). + // time.NewTimer() is used instead of time.After() to avoid leaking the + // timer channel (cf. https://pkg.go.dev/time#After). + timeout := time.NewTimer(time.Duration(0)) + defer func() { + // timeout.Stop() is called in a function literal instead of being + // defered directly as it's re-assigned below when the retry loop spins. + timeout.Stop() + }() + + for i := 0; i < f.Config.MaxRetry; i++ { + select { + case <-timeout.C: + err := f.connect(ctx) + if err == nil { + return nil + } + + if _, ok := err.(*ErrUnknownNetwork); ok { + return err + } + if err == context.Canceled { + return errIsClosing + } + + waitTime := f.Config.RetryWait * e(defaultReconnectWaitIncreRate, float64(i-1)) + if waitTime > f.Config.MaxRetryWait { + waitTime = f.Config.MaxRetryWait + } + + timeout = time.NewTimer(time.Duration(waitTime) * time.Millisecond) + case <-ctx.Done(): + return errIsClosing + } + } + + return fmt.Errorf("could not connect to fluentd after %d retries", f.Config.MaxRetry) +} + +// run is the goroutine used to unqueue and write logs in async mode. That +// goroutine is meant to run during the whole life of the Fluent logger. +func (f *Fluent) run(ctx context.Context) { + for { + select { + case entry, ok := <-f.pending: + // f.stopRunning is closed before f.pending only when ForceStopAsyncSend + // is enabled. Otherwise, f.pending is closed when Close() is called. + if !ok { + f.wg.Done() + return + } + + err := f.writeWithRetry(ctx, entry) + if err != nil && err != errIsClosing { + fmt.Fprintf(os.Stderr, "[%s] Unable to send logs to fluentd, reconnecting...\n", time.Now().Format(time.RFC3339)) + } + if f.AsyncResultCallback != nil { + var data []byte + if entry != nil { + data = entry.data + } + f.AsyncResultCallback(data, err) + } + case <-f.stopRunning: + fmt.Fprintf(os.Stderr, "[%s] Discarding queued events...\n", time.Now().Format(time.RFC3339)) + + f.wg.Done() + return + } } - return } func e(x, y float64) int { return int(math.Pow(x, y)) } -func (f *Fluent) reconnect() { - for i := 0; ; i++ { - err := f.connect() - if err == nil { - f.send() - return +func (f *Fluent) writeWithRetry(ctx context.Context, msg *msgToSend) error { + for i := 0; i < f.Config.MaxRetry; i++ { + if retry, err := f.write(ctx, msg); !retry { + return err } - if i == f.Config.MaxRetry { - // TODO: What we can do when connection failed MaxRetry times? - panic("fluent#reconnect: failed to reconnect!") - } - waitTime := f.Config.RetryWait * e(defaultReconnectWaitIncreRate, float64(i-1)) - time.Sleep(time.Duration(waitTime) * time.Millisecond) } + + return fmt.Errorf("fluent#write: failed to write after %d attempts", f.Config.MaxRetry) } -func (f *Fluent) send() error { - f.muconn.Lock() - defer f.muconn.Unlock() +// write writes the provided msg to fluentd server. Its first return values is +// a bool indicating whether the write should be retried. +// This method relies on function literals to execute muconn.Unlock or +// muconn.RUnlock in deferred calls to ensure the mutex is unlocked even in +// the case of panic recovering. +func (f *Fluent) write(ctx context.Context, msg *msgToSend) (bool, error) { + closer := func() { + f.muconn.Lock() + defer f.muconn.Unlock() - if f.conn == nil { - if f.reconnecting == false { - f.reconnecting = true - go f.reconnect() + f.close() + } + + if err := func() (err error) { + f.muconn.Lock() + defer f.muconn.Unlock() + + if f.conn == nil { + err = f.connectWithRetry(ctx) } - return errors.New("fluent#send: can't send logs, client is reconnecting") + + return err + }(); err != nil { + // Here, we don't want to retry the write since connectWithRetry already + // retries Config.MaxRetry times to connect. + return false, fmt.Errorf("fluent#write: %v", err) } - f.mubuff.Lock() - defer f.mubuff.Unlock() + if err := func() (err error) { + f.muconn.RLock() + defer f.muconn.RUnlock() + + if f.conn == nil { + return fmt.Errorf("connection has been closed before writing to it") + } - var err error - if len(f.pending) > 0 { t := f.Config.WriteTimeout if time.Duration(0) < t { f.conn.SetWriteDeadline(time.Now().Add(t)) } else { f.conn.SetWriteDeadline(time.Time{}) } - _, err = f.conn.Write(f.pending) - if err != nil { - f.conn.Close() - f.conn = nil + + _, err = f.conn.Write(msg.data) + return err + }(); err != nil { + closer() + return true, fmt.Errorf("fluent#write: %v", err) + } + + // Acknowledgment check + if msg.ack != "" { + resp := &AckResp{} + var err error + if f.Config.MarshalAsJSON { + dec := json.NewDecoder(f.conn) + err = dec.Decode(resp) } else { - f.pending = f.pending[:0] + r := msgp.NewReader(f.conn) + err = resp.DecodeMsg(r) + } + + if err != nil || resp.Ack != msg.ack { + fmt.Fprintf(os.Stderr, "fluent#write: message ack (%s) doesn't match expected one (%s). Closing connection...", resp.Ack, msg.ack) + + closer() + return true, err } } - return err + + return false, nil } diff --git a/vendor/github.com/fluent/fluent-logger-golang/fluent/proto.go b/vendor/github.com/fluent/fluent-logger-golang/fluent/proto.go index 158e22da71cfd..95faa9624a77f 100644 --- a/vendor/github.com/fluent/fluent-logger-golang/fluent/proto.go +++ b/vendor/github.com/fluent/fluent-logger-golang/fluent/proto.go @@ -3,6 +3,7 @@ package fluent import ( + "fmt" "time" "github.com/tinylib/msgp/msgp" @@ -16,9 +17,9 @@ type Entry struct { //msgp:tuple Forward type Forward struct { - Tag string `msg:"tag"` - Entries []Entry `msg:"entries"` - Option interface{} `msg:"option"` + Tag string `msg:"tag"` + Entries []Entry `msg:"entries"` + Option map[string]string } //msgp:tuple Message @@ -26,7 +27,7 @@ type Message struct { Tag string `msg:"tag"` Time int64 `msg:"time"` Record interface{} `msg:"record"` - Option interface{} `msg:"option"` + Option map[string]string } //msgp:tuple MessageExt @@ -34,7 +35,11 @@ type MessageExt struct { Tag string `msg:"tag"` Time EventTime `msg:"time,extension"` Record interface{} `msg:"record"` - Option interface{} `msg:"option"` + Option map[string]string +} + +type AckResp struct { + Ack string `json:"ack" msg:"ack"` } // EventTime is an extension to the serialized time value. It builds in support @@ -89,8 +94,19 @@ func (t *EventTime) MarshalBinaryTo(b []byte) error { return nil } -// UnmarshalBinary is not implemented since decoding messages is not supported -// by this library. +// Although decoding messages is not officially supported by this library, +// UnmarshalBinary is implemented for testing and general completeness. func (t *EventTime) UnmarshalBinary(b []byte) error { + if len(b) != length { + return fmt.Errorf("Invalid EventTime byte length: %d", len(b)) + } + + sec := (int32(b[0]) << 24) | (int32(b[1]) << 16) + sec = sec | (int32(b[2]) << 8) | int32(b[3]) + + nsec := (int32(b[4]) << 24) | (int32(b[5]) << 16) + nsec = nsec | (int32(b[6]) << 8) | int32(b[7]) + + *t = EventTime(time.Unix(int64(sec), int64(nsec))) return nil } diff --git a/vendor/github.com/fluent/fluent-logger-golang/fluent/proto_gen.go b/vendor/github.com/fluent/fluent-logger-golang/fluent/proto_gen.go index 5b88a688f86b5..e808880c6ab03 100644 --- a/vendor/github.com/fluent/fluent-logger-golang/fluent/proto_gen.go +++ b/vendor/github.com/fluent/fluent-logger-golang/fluent/proto_gen.go @@ -1,30 +1,134 @@ package fluent -// NOTE: THIS FILE WAS PRODUCED BY THE -// MSGP CODE GENERATION TOOL (github.com/tinylib/msgp) -// DO NOT EDIT +// Code generated by github.com/tinylib/msgp DO NOT EDIT. import ( "github.com/tinylib/msgp/msgp" ) +// DecodeMsg implements msgp.Decodable +func (z *AckResp) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "ack": + z.Ack, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Ack") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z AckResp) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 1 + // write "ack" + err = en.Append(0x81, 0xa3, 0x61, 0x63, 0x6b) + if err != nil { + return + } + err = en.WriteString(z.Ack) + if err != nil { + err = msgp.WrapError(err, "Ack") + return + } + return +} + +// MarshalMsg implements msgp.Marshaler +func (z AckResp) MarshalMsg(b []byte) (o []byte, err error) { + o = msgp.Require(b, z.Msgsize()) + // map header, size 1 + // string "ack" + o = append(o, 0x81, 0xa3, 0x61, 0x63, 0x6b) + o = msgp.AppendString(o, z.Ack) + return +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *AckResp) UnmarshalMsg(bts []byte) (o []byte, err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "ack": + z.Ack, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Ack") + return + } + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + o = bts + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z AckResp) Msgsize() (s int) { + s = 1 + 4 + msgp.StringPrefixSize + len(z.Ack) + return +} + // DecodeMsg implements msgp.Decodable func (z *Entry) DecodeMsg(dc *msgp.Reader) (err error) { - var zxvk uint32 - zxvk, err = dc.ReadArrayHeader() + var zb0001 uint32 + zb0001, err = dc.ReadArrayHeader() if err != nil { + err = msgp.WrapError(err) return } - if zxvk != 2 { - err = msgp.ArrayError{Wanted: 2, Got: zxvk} + if zb0001 != 2 { + err = msgp.ArrayError{Wanted: 2, Got: zb0001} return } z.Time, err = dc.ReadInt64() if err != nil { + err = msgp.WrapError(err, "Time") return } z.Record, err = dc.ReadIntf() if err != nil { + err = msgp.WrapError(err, "Record") return } return @@ -35,14 +139,16 @@ func (z Entry) EncodeMsg(en *msgp.Writer) (err error) { // array header, size 2 err = en.Append(0x92) if err != nil { - return err + return } err = en.WriteInt64(z.Time) if err != nil { + err = msgp.WrapError(err, "Time") return } err = en.WriteIntf(z.Record) if err != nil { + err = msgp.WrapError(err, "Record") return } return @@ -56,6 +162,7 @@ func (z Entry) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.AppendInt64(o, z.Time) o, err = msgp.AppendIntf(o, z.Record) if err != nil { + err = msgp.WrapError(err, "Record") return } return @@ -63,21 +170,24 @@ func (z Entry) MarshalMsg(b []byte) (o []byte, err error) { // UnmarshalMsg implements msgp.Unmarshaler func (z *Entry) UnmarshalMsg(bts []byte) (o []byte, err error) { - var zbzg uint32 - zbzg, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0001 uint32 + zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { + err = msgp.WrapError(err) return } - if zbzg != 2 { - err = msgp.ArrayError{Wanted: 2, Got: zbzg} + if zb0001 != 2 { + err = msgp.ArrayError{Wanted: 2, Got: zb0001} return } z.Time, bts, err = msgp.ReadInt64Bytes(bts) if err != nil { + err = msgp.WrapError(err, "Time") return } z.Record, bts, err = msgp.ReadIntfBytes(bts) if err != nil { + err = msgp.WrapError(err, "Record") return } o = bts @@ -92,52 +202,83 @@ func (z Entry) Msgsize() (s int) { // DecodeMsg implements msgp.Decodable func (z *Forward) DecodeMsg(dc *msgp.Reader) (err error) { - var zcmr uint32 - zcmr, err = dc.ReadArrayHeader() + var zb0001 uint32 + zb0001, err = dc.ReadArrayHeader() if err != nil { + err = msgp.WrapError(err) return } - if zcmr != 3 { - err = msgp.ArrayError{Wanted: 3, Got: zcmr} + if zb0001 != 3 { + err = msgp.ArrayError{Wanted: 3, Got: zb0001} return } z.Tag, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Tag") return } - var zajw uint32 - zajw, err = dc.ReadArrayHeader() + var zb0002 uint32 + zb0002, err = dc.ReadArrayHeader() if err != nil { + err = msgp.WrapError(err, "Entries") return } - if cap(z.Entries) >= int(zajw) { - z.Entries = (z.Entries)[:zajw] + if cap(z.Entries) >= int(zb0002) { + z.Entries = (z.Entries)[:zb0002] } else { - z.Entries = make([]Entry, zajw) + z.Entries = make([]Entry, zb0002) } - for zbai := range z.Entries { - var zwht uint32 - zwht, err = dc.ReadArrayHeader() + for za0001 := range z.Entries { + var zb0003 uint32 + zb0003, err = dc.ReadArrayHeader() if err != nil { + err = msgp.WrapError(err, "Entries", za0001) return } - if zwht != 2 { - err = msgp.ArrayError{Wanted: 2, Got: zwht} + if zb0003 != 2 { + err = msgp.ArrayError{Wanted: 2, Got: zb0003} return } - z.Entries[zbai].Time, err = dc.ReadInt64() + z.Entries[za0001].Time, err = dc.ReadInt64() if err != nil { + err = msgp.WrapError(err, "Entries", za0001, "Time") return } - z.Entries[zbai].Record, err = dc.ReadIntf() + z.Entries[za0001].Record, err = dc.ReadIntf() if err != nil { + err = msgp.WrapError(err, "Entries", za0001, "Record") return } } - z.Option, err = dc.ReadIntf() + var zb0004 uint32 + zb0004, err = dc.ReadMapHeader() if err != nil { + err = msgp.WrapError(err, "Option") return } + if z.Option == nil { + z.Option = make(map[string]string, zb0004) + } else if len(z.Option) > 0 { + for key := range z.Option { + delete(z.Option, key) + } + } + for zb0004 > 0 { + zb0004-- + var za0002 string + var za0003 string + za0002, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Option") + return + } + za0003, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Option", za0002) + return + } + z.Option[za0002] = za0003 + } return } @@ -146,35 +287,52 @@ func (z *Forward) EncodeMsg(en *msgp.Writer) (err error) { // array header, size 3 err = en.Append(0x93) if err != nil { - return err + return } err = en.WriteString(z.Tag) if err != nil { + err = msgp.WrapError(err, "Tag") return } err = en.WriteArrayHeader(uint32(len(z.Entries))) if err != nil { + err = msgp.WrapError(err, "Entries") return } - for zbai := range z.Entries { + for za0001 := range z.Entries { // array header, size 2 err = en.Append(0x92) if err != nil { - return err + return } - err = en.WriteInt64(z.Entries[zbai].Time) + err = en.WriteInt64(z.Entries[za0001].Time) if err != nil { + err = msgp.WrapError(err, "Entries", za0001, "Time") return } - err = en.WriteIntf(z.Entries[zbai].Record) + err = en.WriteIntf(z.Entries[za0001].Record) if err != nil { + err = msgp.WrapError(err, "Entries", za0001, "Record") return } } - err = en.WriteIntf(z.Option) + err = en.WriteMapHeader(uint32(len(z.Option))) if err != nil { + err = msgp.WrapError(err, "Option") return } + for za0002, za0003 := range z.Option { + err = en.WriteString(za0002) + if err != nil { + err = msgp.WrapError(err, "Option") + return + } + err = en.WriteString(za0003) + if err != nil { + err = msgp.WrapError(err, "Option", za0002) + return + } + } return } @@ -185,70 +343,103 @@ func (z *Forward) MarshalMsg(b []byte) (o []byte, err error) { o = append(o, 0x93) o = msgp.AppendString(o, z.Tag) o = msgp.AppendArrayHeader(o, uint32(len(z.Entries))) - for zbai := range z.Entries { + for za0001 := range z.Entries { // array header, size 2 o = append(o, 0x92) - o = msgp.AppendInt64(o, z.Entries[zbai].Time) - o, err = msgp.AppendIntf(o, z.Entries[zbai].Record) + o = msgp.AppendInt64(o, z.Entries[za0001].Time) + o, err = msgp.AppendIntf(o, z.Entries[za0001].Record) if err != nil { + err = msgp.WrapError(err, "Entries", za0001, "Record") return } } - o, err = msgp.AppendIntf(o, z.Option) - if err != nil { - return + o = msgp.AppendMapHeader(o, uint32(len(z.Option))) + for za0002, za0003 := range z.Option { + o = msgp.AppendString(o, za0002) + o = msgp.AppendString(o, za0003) } return } // UnmarshalMsg implements msgp.Unmarshaler func (z *Forward) UnmarshalMsg(bts []byte) (o []byte, err error) { - var zhct uint32 - zhct, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0001 uint32 + zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { + err = msgp.WrapError(err) return } - if zhct != 3 { - err = msgp.ArrayError{Wanted: 3, Got: zhct} + if zb0001 != 3 { + err = msgp.ArrayError{Wanted: 3, Got: zb0001} return } z.Tag, bts, err = msgp.ReadStringBytes(bts) if err != nil { + err = msgp.WrapError(err, "Tag") return } - var zcua uint32 - zcua, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0002 uint32 + zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { + err = msgp.WrapError(err, "Entries") return } - if cap(z.Entries) >= int(zcua) { - z.Entries = (z.Entries)[:zcua] + if cap(z.Entries) >= int(zb0002) { + z.Entries = (z.Entries)[:zb0002] } else { - z.Entries = make([]Entry, zcua) + z.Entries = make([]Entry, zb0002) } - for zbai := range z.Entries { - var zxhx uint32 - zxhx, bts, err = msgp.ReadArrayHeaderBytes(bts) + for za0001 := range z.Entries { + var zb0003 uint32 + zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { + err = msgp.WrapError(err, "Entries", za0001) return } - if zxhx != 2 { - err = msgp.ArrayError{Wanted: 2, Got: zxhx} + if zb0003 != 2 { + err = msgp.ArrayError{Wanted: 2, Got: zb0003} return } - z.Entries[zbai].Time, bts, err = msgp.ReadInt64Bytes(bts) + z.Entries[za0001].Time, bts, err = msgp.ReadInt64Bytes(bts) if err != nil { + err = msgp.WrapError(err, "Entries", za0001, "Time") return } - z.Entries[zbai].Record, bts, err = msgp.ReadIntfBytes(bts) + z.Entries[za0001].Record, bts, err = msgp.ReadIntfBytes(bts) if err != nil { + err = msgp.WrapError(err, "Entries", za0001, "Record") return } } - z.Option, bts, err = msgp.ReadIntfBytes(bts) + var zb0004 uint32 + zb0004, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { + err = msgp.WrapError(err, "Option") return } + if z.Option == nil { + z.Option = make(map[string]string, zb0004) + } else if len(z.Option) > 0 { + for key := range z.Option { + delete(z.Option, key) + } + } + for zb0004 > 0 { + var za0002 string + var za0003 string + zb0004-- + za0002, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Option") + return + } + za0003, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Option", za0002) + return + } + z.Option[za0002] = za0003 + } o = bts return } @@ -256,40 +447,75 @@ func (z *Forward) UnmarshalMsg(bts []byte) (o []byte, err error) { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *Forward) Msgsize() (s int) { s = 1 + msgp.StringPrefixSize + len(z.Tag) + msgp.ArrayHeaderSize - for zbai := range z.Entries { - s += 1 + msgp.Int64Size + msgp.GuessSize(z.Entries[zbai].Record) + for za0001 := range z.Entries { + s += 1 + msgp.Int64Size + msgp.GuessSize(z.Entries[za0001].Record) + } + s += msgp.MapHeaderSize + if z.Option != nil { + for za0002, za0003 := range z.Option { + _ = za0003 + s += msgp.StringPrefixSize + len(za0002) + msgp.StringPrefixSize + len(za0003) + } } - s += msgp.GuessSize(z.Option) return } // DecodeMsg implements msgp.Decodable func (z *Message) DecodeMsg(dc *msgp.Reader) (err error) { - var zlqf uint32 - zlqf, err = dc.ReadArrayHeader() + var zb0001 uint32 + zb0001, err = dc.ReadArrayHeader() if err != nil { + err = msgp.WrapError(err) return } - if zlqf != 4 { - err = msgp.ArrayError{Wanted: 4, Got: zlqf} + if zb0001 != 4 { + err = msgp.ArrayError{Wanted: 4, Got: zb0001} return } z.Tag, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Tag") return } z.Time, err = dc.ReadInt64() if err != nil { + err = msgp.WrapError(err, "Time") return } z.Record, err = dc.ReadIntf() if err != nil { + err = msgp.WrapError(err, "Record") return } - z.Option, err = dc.ReadIntf() + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() if err != nil { + err = msgp.WrapError(err, "Option") return } + if z.Option == nil { + z.Option = make(map[string]string, zb0002) + } else if len(z.Option) > 0 { + for key := range z.Option { + delete(z.Option, key) + } + } + for zb0002 > 0 { + zb0002-- + var za0001 string + var za0002 string + za0001, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Option") + return + } + za0002, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Option", za0001) + return + } + z.Option[za0001] = za0002 + } return } @@ -298,24 +524,40 @@ func (z *Message) EncodeMsg(en *msgp.Writer) (err error) { // array header, size 4 err = en.Append(0x94) if err != nil { - return err + return } err = en.WriteString(z.Tag) if err != nil { + err = msgp.WrapError(err, "Tag") return } err = en.WriteInt64(z.Time) if err != nil { + err = msgp.WrapError(err, "Time") return } err = en.WriteIntf(z.Record) if err != nil { + err = msgp.WrapError(err, "Record") return } - err = en.WriteIntf(z.Option) + err = en.WriteMapHeader(uint32(len(z.Option))) if err != nil { + err = msgp.WrapError(err, "Option") return } + for za0001, za0002 := range z.Option { + err = en.WriteString(za0001) + if err != nil { + err = msgp.WrapError(err, "Option") + return + } + err = en.WriteString(za0002) + if err != nil { + err = msgp.WrapError(err, "Option", za0001) + return + } + } return } @@ -328,79 +570,145 @@ func (z *Message) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.AppendInt64(o, z.Time) o, err = msgp.AppendIntf(o, z.Record) if err != nil { + err = msgp.WrapError(err, "Record") return } - o, err = msgp.AppendIntf(o, z.Option) - if err != nil { - return + o = msgp.AppendMapHeader(o, uint32(len(z.Option))) + for za0001, za0002 := range z.Option { + o = msgp.AppendString(o, za0001) + o = msgp.AppendString(o, za0002) } return } // UnmarshalMsg implements msgp.Unmarshaler func (z *Message) UnmarshalMsg(bts []byte) (o []byte, err error) { - var zdaf uint32 - zdaf, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0001 uint32 + zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { + err = msgp.WrapError(err) return } - if zdaf != 4 { - err = msgp.ArrayError{Wanted: 4, Got: zdaf} + if zb0001 != 4 { + err = msgp.ArrayError{Wanted: 4, Got: zb0001} return } z.Tag, bts, err = msgp.ReadStringBytes(bts) if err != nil { + err = msgp.WrapError(err, "Tag") return } z.Time, bts, err = msgp.ReadInt64Bytes(bts) if err != nil { + err = msgp.WrapError(err, "Time") return } z.Record, bts, err = msgp.ReadIntfBytes(bts) if err != nil { + err = msgp.WrapError(err, "Record") return } - z.Option, bts, err = msgp.ReadIntfBytes(bts) + var zb0002 uint32 + zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { + err = msgp.WrapError(err, "Option") return } + if z.Option == nil { + z.Option = make(map[string]string, zb0002) + } else if len(z.Option) > 0 { + for key := range z.Option { + delete(z.Option, key) + } + } + for zb0002 > 0 { + var za0001 string + var za0002 string + zb0002-- + za0001, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Option") + return + } + za0002, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Option", za0001) + return + } + z.Option[za0001] = za0002 + } o = bts return } // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *Message) Msgsize() (s int) { - s = 1 + msgp.StringPrefixSize + len(z.Tag) + msgp.Int64Size + msgp.GuessSize(z.Record) + msgp.GuessSize(z.Option) + s = 1 + msgp.StringPrefixSize + len(z.Tag) + msgp.Int64Size + msgp.GuessSize(z.Record) + msgp.MapHeaderSize + if z.Option != nil { + for za0001, za0002 := range z.Option { + _ = za0002 + s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002) + } + } return } // DecodeMsg implements msgp.Decodable func (z *MessageExt) DecodeMsg(dc *msgp.Reader) (err error) { - var zpks uint32 - zpks, err = dc.ReadArrayHeader() + var zb0001 uint32 + zb0001, err = dc.ReadArrayHeader() if err != nil { + err = msgp.WrapError(err) return } - if zpks != 4 { - err = msgp.ArrayError{Wanted: 4, Got: zpks} + if zb0001 != 4 { + err = msgp.ArrayError{Wanted: 4, Got: zb0001} return } z.Tag, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Tag") return } err = dc.ReadExtension(&z.Time) if err != nil { + err = msgp.WrapError(err, "Time") return } z.Record, err = dc.ReadIntf() if err != nil { + err = msgp.WrapError(err, "Record") return } - z.Option, err = dc.ReadIntf() + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() if err != nil { + err = msgp.WrapError(err, "Option") return } + if z.Option == nil { + z.Option = make(map[string]string, zb0002) + } else if len(z.Option) > 0 { + for key := range z.Option { + delete(z.Option, key) + } + } + for zb0002 > 0 { + zb0002-- + var za0001 string + var za0002 string + za0001, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Option") + return + } + za0002, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Option", za0001) + return + } + z.Option[za0001] = za0002 + } return } @@ -409,24 +717,40 @@ func (z *MessageExt) EncodeMsg(en *msgp.Writer) (err error) { // array header, size 4 err = en.Append(0x94) if err != nil { - return err + return } err = en.WriteString(z.Tag) if err != nil { + err = msgp.WrapError(err, "Tag") return } err = en.WriteExtension(&z.Time) if err != nil { + err = msgp.WrapError(err, "Time") return } err = en.WriteIntf(z.Record) if err != nil { + err = msgp.WrapError(err, "Record") return } - err = en.WriteIntf(z.Option) + err = en.WriteMapHeader(uint32(len(z.Option))) if err != nil { + err = msgp.WrapError(err, "Option") return } + for za0001, za0002 := range z.Option { + err = en.WriteString(za0001) + if err != nil { + err = msgp.WrapError(err, "Option") + return + } + err = en.WriteString(za0002) + if err != nil { + err = msgp.WrapError(err, "Option", za0001) + return + } + } return } @@ -438,52 +762,90 @@ func (z *MessageExt) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.AppendString(o, z.Tag) o, err = msgp.AppendExtension(o, &z.Time) if err != nil { + err = msgp.WrapError(err, "Time") return } o, err = msgp.AppendIntf(o, z.Record) if err != nil { + err = msgp.WrapError(err, "Record") return } - o, err = msgp.AppendIntf(o, z.Option) - if err != nil { - return + o = msgp.AppendMapHeader(o, uint32(len(z.Option))) + for za0001, za0002 := range z.Option { + o = msgp.AppendString(o, za0001) + o = msgp.AppendString(o, za0002) } return } // UnmarshalMsg implements msgp.Unmarshaler func (z *MessageExt) UnmarshalMsg(bts []byte) (o []byte, err error) { - var zjfb uint32 - zjfb, bts, err = msgp.ReadArrayHeaderBytes(bts) + var zb0001 uint32 + zb0001, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { + err = msgp.WrapError(err) return } - if zjfb != 4 { - err = msgp.ArrayError{Wanted: 4, Got: zjfb} + if zb0001 != 4 { + err = msgp.ArrayError{Wanted: 4, Got: zb0001} return } z.Tag, bts, err = msgp.ReadStringBytes(bts) if err != nil { + err = msgp.WrapError(err, "Tag") return } bts, err = msgp.ReadExtensionBytes(bts, &z.Time) if err != nil { + err = msgp.WrapError(err, "Time") return } z.Record, bts, err = msgp.ReadIntfBytes(bts) if err != nil { + err = msgp.WrapError(err, "Record") return } - z.Option, bts, err = msgp.ReadIntfBytes(bts) + var zb0002 uint32 + zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { + err = msgp.WrapError(err, "Option") return } + if z.Option == nil { + z.Option = make(map[string]string, zb0002) + } else if len(z.Option) > 0 { + for key := range z.Option { + delete(z.Option, key) + } + } + for zb0002 > 0 { + var za0001 string + var za0002 string + zb0002-- + za0001, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Option") + return + } + za0002, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Option", za0001) + return + } + z.Option[za0001] = za0002 + } o = bts return } // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *MessageExt) Msgsize() (s int) { - s = 1 + msgp.StringPrefixSize + len(z.Tag) + msgp.ExtensionPrefixSize + z.Time.Len() + msgp.GuessSize(z.Record) + msgp.GuessSize(z.Option) + s = 1 + msgp.StringPrefixSize + len(z.Tag) + msgp.ExtensionPrefixSize + z.Time.Len() + msgp.GuessSize(z.Record) + msgp.MapHeaderSize + if z.Option != nil { + for za0001, za0002 := range z.Option { + _ = za0002 + s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002) + } + } return } diff --git a/vendor/github.com/fluent/fluent-logger-golang/fluent/test_message_gen.go b/vendor/github.com/fluent/fluent-logger-golang/fluent/test_message_gen.go index 17a45e22a34f5..591e1a97576ad 100644 --- a/vendor/github.com/fluent/fluent-logger-golang/fluent/test_message_gen.go +++ b/vendor/github.com/fluent/fluent-logger-golang/fluent/test_message_gen.go @@ -1,8 +1,6 @@ package fluent -// NOTE: THIS FILE WAS PRODUCED BY THE -// MSGP CODE GENERATION TOOL (github.com/tinylib/msgp) -// DO NOT EDIT +// Code generated by github.com/tinylib/msgp DO NOT EDIT. import ( "github.com/tinylib/msgp/msgp" @@ -12,31 +10,36 @@ import ( func (z *TestMessage) DecodeMsg(dc *msgp.Reader) (err error) { var field []byte _ = field - var zxvk uint32 - zxvk, err = dc.ReadMapHeader() + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() if err != nil { + err = msgp.WrapError(err) return } - for zxvk > 0 { - zxvk-- + for zb0001 > 0 { + zb0001-- field, err = dc.ReadMapKeyPtr() if err != nil { + err = msgp.WrapError(err) return } switch msgp.UnsafeString(field) { case "foo": z.Foo, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Foo") return } case "hoge": z.Hoge, err = dc.ReadString() if err != nil { + err = msgp.WrapError(err, "Hoge") return } default: err = dc.Skip() if err != nil { + err = msgp.WrapError(err) return } } @@ -50,19 +53,21 @@ func (z TestMessage) EncodeMsg(en *msgp.Writer) (err error) { // write "foo" err = en.Append(0x82, 0xa3, 0x66, 0x6f, 0x6f) if err != nil { - return err + return } err = en.WriteString(z.Foo) if err != nil { + err = msgp.WrapError(err, "Foo") return } // write "hoge" err = en.Append(0xa4, 0x68, 0x6f, 0x67, 0x65) if err != nil { - return err + return } err = en.WriteString(z.Hoge) if err != nil { + err = msgp.WrapError(err, "Hoge") return } return @@ -85,31 +90,36 @@ func (z TestMessage) MarshalMsg(b []byte) (o []byte, err error) { func (z *TestMessage) UnmarshalMsg(bts []byte) (o []byte, err error) { var field []byte _ = field - var zbzg uint32 - zbzg, bts, err = msgp.ReadMapHeaderBytes(bts) + var zb0001 uint32 + zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) if err != nil { + err = msgp.WrapError(err) return } - for zbzg > 0 { - zbzg-- + for zb0001 > 0 { + zb0001-- field, bts, err = msgp.ReadMapKeyZC(bts) if err != nil { + err = msgp.WrapError(err) return } switch msgp.UnsafeString(field) { case "foo": z.Foo, bts, err = msgp.ReadStringBytes(bts) if err != nil { + err = msgp.WrapError(err, "Foo") return } case "hoge": z.Hoge, bts, err = msgp.ReadStringBytes(bts) if err != nil { + err = msgp.WrapError(err, "Hoge") return } default: bts, err = msgp.Skip(bts) if err != nil { + err = msgp.WrapError(err) return } } diff --git a/vendor/github.com/fluent/fluent-logger-golang/fluent/version.go b/vendor/github.com/fluent/fluent-logger-golang/fluent/version.go index c6ec7e41e2473..83e8932492eab 100644 --- a/vendor/github.com/fluent/fluent-logger-golang/fluent/version.go +++ b/vendor/github.com/fluent/fluent-logger-golang/fluent/version.go @@ -1,3 +1,3 @@ package fluent -const Version = "1.3.0" +const Version = "1.4.0" diff --git a/vendor/github.com/fsnotify/fsnotify/LICENSE b/vendor/github.com/fsnotify/fsnotify/LICENSE index f21e54080090f..e180c8fb0599c 100644 --- a/vendor/github.com/fsnotify/fsnotify/LICENSE +++ b/vendor/github.com/fsnotify/fsnotify/LICENSE @@ -1,5 +1,5 @@ Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright (c) 2012 fsnotify Authors. All rights reserved. +Copyright (c) 2012-2019 fsnotify Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md index 3993207413a75..b2629e5229ca4 100644 --- a/vendor/github.com/fsnotify/fsnotify/README.md +++ b/vendor/github.com/fsnotify/fsnotify/README.md @@ -10,16 +10,16 @@ go get -u golang.org/x/sys/... Cross platform: Windows, Linux, BSD and macOS. -|Adapter |OS |Status | -|----------|----------|----------| -|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| -|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| -|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)| -|fanotify |Linux 2.6.37+ | | -|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)| -|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)| +| Adapter | OS | Status | +| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| inotify | Linux 2.6.27 or later, Android\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) | +| kqueue | BSD, macOS, iOS\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) | +| ReadDirectoryChangesW | Windows | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) | +| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) | +| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/issues/12) | +| fanotify | Linux 2.6.37+ | [Planned](https://github.com/fsnotify/fsnotify/issues/114) | +| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) | +| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) | \* Android and iOS are untested. @@ -33,6 +33,53 @@ All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based o Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. +## Usage + +```go +package main + +import ( + "log" + + "github.com/fsnotify/fsnotify" +) + +func main() { + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() + + done := make(chan bool) + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + log.Println("event:", event) + if event.Op&fsnotify.Write == fsnotify.Write { + log.Println("modified file:", event.Name) + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + log.Println("error:", err) + } + } + }() + + err = watcher.Add("/tmp/foo") + if err != nil { + log.Fatal(err) + } + <-done +} +``` + ## Contributing Please refer to [CONTRIBUTING][] before opening an issue or pull request. @@ -65,6 +112,10 @@ There are OS-specific limits as to how many watches can be created: * Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. * BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. +**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?** + +fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications. + [#62]: https://github.com/howeyc/fsnotify/issues/62 [#18]: https://github.com/fsnotify/fsnotify/issues/18 [#11]: https://github.com/fsnotify/fsnotify/issues/11 diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go index 190bf0de57562..89cab046d124b 100644 --- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go +++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go @@ -63,4 +63,6 @@ func (e Event) String() string { } // Common errors that can be reported by a watcher -var ErrEventOverflow = errors.New("fsnotify queue overflow") +var ( + ErrEventOverflow = errors.New("fsnotify queue overflow") +) diff --git a/vendor/github.com/fsnotify/fsnotify/go.mod b/vendor/github.com/fsnotify/fsnotify/go.mod new file mode 100644 index 0000000000000..ff11e13f22403 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/go.mod @@ -0,0 +1,5 @@ +module github.com/fsnotify/fsnotify + +go 1.13 + +require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go index cc7db4b22ef5b..b33f2b4d4b799 100644 --- a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go +++ b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go @@ -40,12 +40,12 @@ func newFdPoller(fd int) (*fdPoller, error) { poller.fd = fd // Create epoll fd - poller.epfd, errno = unix.EpollCreate1(0) + poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC) if poller.epfd == -1 { return nil, errno } // Create pipe; pipe[0] is the read end, pipe[1] the write end. - errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) + errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC) if errno != nil { return nil, errno } diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go index 7d8de14513ede..2306c4620bf6d 100644 --- a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go +++ b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go @@ -8,4 +8,4 @@ package fsnotify import "golang.org/x/sys/unix" -const openMode = unix.O_NONBLOCK | unix.O_RDONLY +const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go index 9139e17161bfb..870c4d6d18450 100644 --- a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go +++ b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go @@ -9,4 +9,4 @@ package fsnotify import "golang.org/x/sys/unix" // note: this constant is not defined on BSD -const openMode = unix.O_EVTONLY +const openMode = unix.O_EVTONLY | unix.O_CLOEXEC diff --git a/vendor/github.com/go-check/check/LICENSE b/vendor/github.com/go-check/check/LICENSE deleted file mode 100644 index 545cf2d3311b0..0000000000000 --- a/vendor/github.com/go-check/check/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Gocheck - A rich testing framework for Go - -Copyright (c) 2010-2013 Gustavo Niemeyer - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/go-check/check/README.md b/vendor/github.com/go-check/check/README.md deleted file mode 100644 index 0ca78d97e81a6..0000000000000 --- a/vendor/github.com/go-check/check/README.md +++ /dev/null @@ -1,10 +0,0 @@ -Go-check -======== - -This is a fork of https://github.com/go-check/check - -The intention of this fork is not to change any of the original behavior, but add -some specific behaviors needed for some of my projects already using this test suite. -For documentation on the main behavior of go-check see the aforementioned repo. - -The original branch is intact at `orig_v1` diff --git a/vendor/github.com/go-check/check/benchmark.go b/vendor/github.com/go-check/check/benchmark.go deleted file mode 100644 index 46ea9dc6dad69..0000000000000 --- a/vendor/github.com/go-check/check/benchmark.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) 2012 The Go Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package check - -import ( - "fmt" - "runtime" - "time" -) - -var memStats runtime.MemStats - -// testingB is a type passed to Benchmark functions to manage benchmark -// timing and to specify the number of iterations to run. -type timer struct { - start time.Time // Time test or benchmark started - duration time.Duration - N int - bytes int64 - timerOn bool - benchTime time.Duration - // The initial states of memStats.Mallocs and memStats.TotalAlloc. - startAllocs uint64 - startBytes uint64 - // The net total of this test after being run. - netAllocs uint64 - netBytes uint64 -} - -// StartTimer starts timing a test. This function is called automatically -// before a benchmark starts, but it can also used to resume timing after -// a call to StopTimer. -func (c *C) StartTimer() { - if !c.timerOn { - c.start = time.Now() - c.timerOn = true - - runtime.ReadMemStats(&memStats) - c.startAllocs = memStats.Mallocs - c.startBytes = memStats.TotalAlloc - } -} - -// StopTimer stops timing a test. This can be used to pause the timer -// while performing complex initialization that you don't -// want to measure. -func (c *C) StopTimer() { - if c.timerOn { - c.duration += time.Now().Sub(c.start) - c.timerOn = false - runtime.ReadMemStats(&memStats) - c.netAllocs += memStats.Mallocs - c.startAllocs - c.netBytes += memStats.TotalAlloc - c.startBytes - } -} - -// ResetTimer sets the elapsed benchmark time to zero. -// It does not affect whether the timer is running. -func (c *C) ResetTimer() { - if c.timerOn { - c.start = time.Now() - runtime.ReadMemStats(&memStats) - c.startAllocs = memStats.Mallocs - c.startBytes = memStats.TotalAlloc - } - c.duration = 0 - c.netAllocs = 0 - c.netBytes = 0 -} - -// SetBytes informs the number of bytes that the benchmark processes -// on each iteration. If this is called in a benchmark it will also -// report MB/s. -func (c *C) SetBytes(n int64) { - c.bytes = n -} - -func (c *C) nsPerOp() int64 { - if c.N <= 0 { - return 0 - } - return c.duration.Nanoseconds() / int64(c.N) -} - -func (c *C) mbPerSec() float64 { - if c.bytes <= 0 || c.duration <= 0 || c.N <= 0 { - return 0 - } - return (float64(c.bytes) * float64(c.N) / 1e6) / c.duration.Seconds() -} - -func (c *C) timerString() string { - if c.N <= 0 { - return fmt.Sprintf("%3.3fs", float64(c.duration.Nanoseconds())/1e9) - } - mbs := c.mbPerSec() - mb := "" - if mbs != 0 { - mb = fmt.Sprintf("\t%7.2f MB/s", mbs) - } - nsop := c.nsPerOp() - ns := fmt.Sprintf("%10d ns/op", nsop) - if c.N > 0 && nsop < 100 { - // The format specifiers here make sure that - // the ones digits line up for all three possible formats. - if nsop < 10 { - ns = fmt.Sprintf("%13.2f ns/op", float64(c.duration.Nanoseconds())/float64(c.N)) - } else { - ns = fmt.Sprintf("%12.1f ns/op", float64(c.duration.Nanoseconds())/float64(c.N)) - } - } - memStats := "" - if c.benchMem { - allocedBytes := fmt.Sprintf("%8d B/op", int64(c.netBytes)/int64(c.N)) - allocs := fmt.Sprintf("%8d allocs/op", int64(c.netAllocs)/int64(c.N)) - memStats = fmt.Sprintf("\t%s\t%s", allocedBytes, allocs) - } - return fmt.Sprintf("%8d\t%s%s%s", c.N, ns, mb, memStats) -} - -func min(x, y int) int { - if x > y { - return y - } - return x -} - -func max(x, y int) int { - if x < y { - return y - } - return x -} - -// roundDown10 rounds a number down to the nearest power of 10. -func roundDown10(n int) int { - var tens = 0 - // tens = floor(log_10(n)) - for n > 10 { - n = n / 10 - tens++ - } - // result = 10^tens - result := 1 - for i := 0; i < tens; i++ { - result *= 10 - } - return result -} - -// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX]. -func roundUp(n int) int { - base := roundDown10(n) - if n < (2 * base) { - return 2 * base - } - if n < (5 * base) { - return 5 * base - } - return 10 * base -} diff --git a/vendor/github.com/go-check/check/check.go b/vendor/github.com/go-check/check/check.go deleted file mode 100644 index a4e60903c2717..0000000000000 --- a/vendor/github.com/go-check/check/check.go +++ /dev/null @@ -1,939 +0,0 @@ -// Package check is a rich testing extension for Go's testing package. -// -// For details about the project, see: -// -// http://labix.org/gocheck -// -package check - -import ( - "bytes" - "errors" - "fmt" - "io" - "math/rand" - "os" - "path" - "path/filepath" - "reflect" - "regexp" - "runtime" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" -) - -// ----------------------------------------------------------------------- -// Internal type which deals with suite method calling. - -const ( - fixtureKd = iota - testKd -) - -type funcKind int - -const ( - succeededSt = iota - failedSt - skippedSt - panickedSt - fixturePanickedSt - missedSt -) - -type funcStatus uint32 - -// A method value can't reach its own Method structure. -type methodType struct { - reflect.Value - Info reflect.Method -} - -func newMethod(receiver reflect.Value, i int) *methodType { - return &methodType{receiver.Method(i), receiver.Type().Method(i)} -} - -func (method *methodType) PC() uintptr { - return method.Info.Func.Pointer() -} - -func (method *methodType) suiteName() string { - t := method.Info.Type.In(0) - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - return t.Name() -} - -func (method *methodType) String() string { - return method.suiteName() + "." + method.Info.Name -} - -func (method *methodType) matches(re *regexp.Regexp) bool { - return (re.MatchString(method.Info.Name) || - re.MatchString(method.suiteName()) || - re.MatchString(method.String())) -} - -type C struct { - method *methodType - kind funcKind - testName string - _status funcStatus - logb *logger - logw io.Writer - done chan *C - reason string - mustFail bool - tempDir *tempDir - benchMem bool - startTime time.Time - timer -} - -func (c *C) status() funcStatus { - return funcStatus(atomic.LoadUint32((*uint32)(&c._status))) -} - -func (c *C) setStatus(s funcStatus) { - atomic.StoreUint32((*uint32)(&c._status), uint32(s)) -} - -func (c *C) stopNow() { - runtime.Goexit() -} - -// logger is a concurrency safe byte.Buffer -type logger struct { - sync.Mutex - writer bytes.Buffer -} - -func (l *logger) Write(buf []byte) (int, error) { - l.Lock() - defer l.Unlock() - return l.writer.Write(buf) -} - -func (l *logger) WriteTo(w io.Writer) (int64, error) { - l.Lock() - defer l.Unlock() - return l.writer.WriteTo(w) -} - -func (l *logger) String() string { - l.Lock() - defer l.Unlock() - return l.writer.String() -} - -// ----------------------------------------------------------------------- -// Handling of temporary files and directories. - -type tempDir struct { - sync.Mutex - path string - counter int -} - -func (td *tempDir) newPath() string { - td.Lock() - defer td.Unlock() - if td.path == "" { - var err error - for i := 0; i != 100; i++ { - path := fmt.Sprintf("%s%ccheck-%d", os.TempDir(), os.PathSeparator, rand.Int()) - if err = os.Mkdir(path, 0700); err == nil { - td.path = path - break - } - } - if td.path == "" { - panic("Couldn't create temporary directory: " + err.Error()) - } - } - result := filepath.Join(td.path, strconv.Itoa(td.counter)) - td.counter += 1 - return result -} - -func (td *tempDir) removeAll() { - td.Lock() - defer td.Unlock() - if td.path != "" { - err := os.RemoveAll(td.path) - if err != nil { - fmt.Fprintf(os.Stderr, "WARNING: Error cleaning up temporaries: "+err.Error()) - } - } -} - -// Create a new temporary directory which is automatically removed after -// the suite finishes running. -func (c *C) MkDir() string { - path := c.tempDir.newPath() - if err := os.Mkdir(path, 0700); err != nil { - panic(fmt.Sprintf("Couldn't create temporary directory %s: %s", path, err.Error())) - } - return path -} - -// ----------------------------------------------------------------------- -// Low-level logging functions. - -func (c *C) log(args ...interface{}) { - c.writeLog([]byte(fmt.Sprint(args...) + "\n")) -} - -func (c *C) logf(format string, args ...interface{}) { - c.writeLog([]byte(fmt.Sprintf(format+"\n", args...))) -} - -func (c *C) logNewLine() { - c.writeLog([]byte{'\n'}) -} - -func (c *C) writeLog(buf []byte) { - c.logb.Write(buf) - if c.logw != nil { - c.logw.Write(buf) - } -} - -func hasStringOrError(x interface{}) (ok bool) { - _, ok = x.(fmt.Stringer) - if ok { - return - } - _, ok = x.(error) - return -} - -func (c *C) logValue(label string, value interface{}) { - if label == "" { - if hasStringOrError(value) { - c.logf("... %#v (%q)", value, value) - } else { - c.logf("... %#v", value) - } - } else if value == nil { - c.logf("... %s = nil", label) - } else { - if hasStringOrError(value) { - fv := fmt.Sprintf("%#v", value) - qv := fmt.Sprintf("%q", value) - if fv != qv { - c.logf("... %s %s = %s (%s)", label, reflect.TypeOf(value), fv, qv) - return - } - } - if s, ok := value.(string); ok && isMultiLine(s) { - c.logf(`... %s %s = "" +`, label, reflect.TypeOf(value)) - c.logMultiLine(s) - } else { - c.logf("... %s %s = %#v", label, reflect.TypeOf(value), value) - } - } -} - -func (c *C) logMultiLine(s string) { - b := make([]byte, 0, len(s)*2) - i := 0 - n := len(s) - for i < n { - j := i + 1 - for j < n && s[j-1] != '\n' { - j++ - } - b = append(b, "... "...) - b = strconv.AppendQuote(b, s[i:j]) - if j < n { - b = append(b, " +"...) - } - b = append(b, '\n') - i = j - } - c.writeLog(b) -} - -func isMultiLine(s string) bool { - for i := 0; i+1 < len(s); i++ { - if s[i] == '\n' { - return true - } - } - return false -} - -func (c *C) logString(issue string) { - c.log("... ", issue) -} - -func (c *C) logCaller(skip int) { - // This is a bit heavier than it ought to be. - skip += 1 // Our own frame. - pc, callerFile, callerLine, ok := runtime.Caller(skip) - if !ok { - return - } - var testFile string - var testLine int - testFunc := runtime.FuncForPC(c.method.PC()) - if runtime.FuncForPC(pc) != testFunc { - for { - skip += 1 - if pc, file, line, ok := runtime.Caller(skip); ok { - // Note that the test line may be different on - // distinct calls for the same test. Showing - // the "internal" line is helpful when debugging. - if runtime.FuncForPC(pc) == testFunc { - testFile, testLine = file, line - break - } - } else { - break - } - } - } - if testFile != "" && (testFile != callerFile || testLine != callerLine) { - c.logCode(testFile, testLine) - } - c.logCode(callerFile, callerLine) -} - -func (c *C) logCode(path string, line int) { - c.logf("%s:%d:", nicePath(path), line) - code, err := printLine(path, line) - if code == "" { - code = "..." // XXX Open the file and take the raw line. - if err != nil { - code += err.Error() - } - } - c.log(indent(code, " ")) -} - -var valueGo = filepath.Join("reflect", "value.go") -var asmGo = filepath.Join("runtime", "asm_") - -func (c *C) logPanic(skip int, value interface{}) { - skip++ // Our own frame. - initialSkip := skip - for ; ; skip++ { - if pc, file, line, ok := runtime.Caller(skip); ok { - if skip == initialSkip { - c.logf("... Panic: %s (PC=0x%X)\n", value, pc) - } - name := niceFuncName(pc) - path := nicePath(file) - if strings.Contains(path, "/gopkg.in/check.v") { - continue - } - if name == "Value.call" && strings.HasSuffix(path, valueGo) { - continue - } - if (name == "call16" || name == "call32") && strings.Contains(path, asmGo) { - continue - } - c.logf("%s:%d\n in %s", nicePath(file), line, name) - } else { - break - } - } -} - -func (c *C) logSoftPanic(issue string) { - c.log("... Panic: ", issue) -} - -func (c *C) logArgPanic(method *methodType, expectedType string) { - c.logf("... Panic: %s argument should be %s", - niceFuncName(method.PC()), expectedType) -} - -// ----------------------------------------------------------------------- -// Some simple formatting helpers. - -var initWD, initWDErr = os.Getwd() - -func init() { - if initWDErr == nil { - initWD = strings.Replace(initWD, "\\", "/", -1) + "/" - } -} - -func nicePath(path string) string { - if initWDErr == nil { - if strings.HasPrefix(path, initWD) { - return path[len(initWD):] - } - } - return path -} - -func niceFuncPath(pc uintptr) string { - function := runtime.FuncForPC(pc) - if function != nil { - filename, line := function.FileLine(pc) - return fmt.Sprintf("%s:%d", nicePath(filename), line) - } - return "" -} - -func niceFuncName(pc uintptr) string { - function := runtime.FuncForPC(pc) - if function != nil { - name := path.Base(function.Name()) - if i := strings.Index(name, "."); i > 0 { - name = name[i+1:] - } - if strings.HasPrefix(name, "(*") { - if i := strings.Index(name, ")"); i > 0 { - name = name[2:i] + name[i+1:] - } - } - if i := strings.LastIndex(name, ".*"); i != -1 { - name = name[:i] + "." + name[i+2:] - } - if i := strings.LastIndex(name, "·"); i != -1 { - name = name[:i] + "." + name[i+2:] - } - return name - } - return "" -} - -// ----------------------------------------------------------------------- -// Result tracker to aggregate call results. - -type Result struct { - Succeeded int - Failed int - Skipped int - Panicked int - FixturePanicked int - ExpectedFailures int - Missed int // Not even tried to run, related to a panic in the fixture. - RunError error // Houston, we've got a problem. - WorkDir string // If KeepWorkDir is true -} - -type resultTracker struct { - result Result - _lastWasProblem bool - _waiting int - _missed int - _expectChan chan *C - _doneChan chan *C - _stopChan chan bool -} - -func newResultTracker() *resultTracker { - return &resultTracker{_expectChan: make(chan *C), // Synchronous - _doneChan: make(chan *C, 32), // Asynchronous - _stopChan: make(chan bool)} // Synchronous -} - -func (tracker *resultTracker) start() { - go tracker._loopRoutine() -} - -func (tracker *resultTracker) waitAndStop() { - <-tracker._stopChan -} - -func (tracker *resultTracker) expectCall(c *C) { - tracker._expectChan <- c -} - -func (tracker *resultTracker) callDone(c *C) { - tracker._doneChan <- c -} - -func (tracker *resultTracker) _loopRoutine() { - for { - var c *C - if tracker._waiting > 0 { - // Calls still running. Can't stop. - select { - // XXX Reindent this (not now to make diff clear) - case c = <-tracker._expectChan: - tracker._waiting += 1 - case c = <-tracker._doneChan: - tracker._waiting -= 1 - switch c.status() { - case succeededSt: - if c.kind == testKd { - if c.mustFail { - tracker.result.ExpectedFailures++ - } else { - tracker.result.Succeeded++ - } - } - case failedSt: - tracker.result.Failed++ - case panickedSt: - if c.kind == fixtureKd { - tracker.result.FixturePanicked++ - } else { - tracker.result.Panicked++ - } - case fixturePanickedSt: - // Track it as missed, since the panic - // was on the fixture, not on the test. - tracker.result.Missed++ - case missedSt: - tracker.result.Missed++ - case skippedSt: - if c.kind == testKd { - tracker.result.Skipped++ - } - } - } - } else { - // No calls. Can stop, but no done calls here. - select { - case tracker._stopChan <- true: - return - case c = <-tracker._expectChan: - tracker._waiting += 1 - case c = <-tracker._doneChan: - panic("Tracker got an unexpected done call.") - } - } - } -} - -// ----------------------------------------------------------------------- -// The underlying suite runner. - -type suiteRunner struct { - suite interface{} - setUpSuite, tearDownSuite *methodType - setUpTest, tearDownTest *methodType - onTimeout *methodType - tests []*methodType - tracker *resultTracker - tempDir *tempDir - keepDir bool - output *outputWriter - reportedProblemLast bool - benchTime time.Duration - benchMem bool - checkTimeout time.Duration -} - -type RunConf struct { - Output io.Writer - Stream bool - Verbose bool - Filter string - Benchmark bool - BenchmarkTime time.Duration // Defaults to 1 second - BenchmarkMem bool - KeepWorkDir bool - CheckTimeout time.Duration -} - -// Create a new suiteRunner able to run all methods in the given suite. -func newSuiteRunner(suite interface{}, runConf *RunConf) *suiteRunner { - var conf RunConf - if runConf != nil { - conf = *runConf - } - if conf.Output == nil { - conf.Output = os.Stdout - } - if conf.Benchmark { - conf.Verbose = true - } - - suiteType := reflect.TypeOf(suite) - suiteNumMethods := suiteType.NumMethod() - suiteValue := reflect.ValueOf(suite) - - runner := &suiteRunner{ - suite: suite, - output: newOutputWriter(conf.Output, conf.Stream, conf.Verbose), - tracker: newResultTracker(), - benchTime: conf.BenchmarkTime, - benchMem: conf.BenchmarkMem, - tempDir: &tempDir{}, - keepDir: conf.KeepWorkDir, - tests: make([]*methodType, 0, suiteNumMethods), - checkTimeout: conf.CheckTimeout, - } - if runner.benchTime == 0 { - runner.benchTime = 1 * time.Second - } - - var filterRegexp *regexp.Regexp - if conf.Filter != "" { - if regexp, err := regexp.Compile(conf.Filter); err != nil { - msg := "Bad filter expression: " + err.Error() - runner.tracker.result.RunError = errors.New(msg) - return runner - } else { - filterRegexp = regexp - } - } - - for i := 0; i != suiteNumMethods; i++ { - method := newMethod(suiteValue, i) - switch method.Info.Name { - case "SetUpSuite": - runner.setUpSuite = method - case "TearDownSuite": - runner.tearDownSuite = method - case "SetUpTest": - runner.setUpTest = method - case "TearDownTest": - runner.tearDownTest = method - case "OnTimeout": - runner.onTimeout = method - default: - prefix := "Test" - if conf.Benchmark { - prefix = "Benchmark" - } - if !strings.HasPrefix(method.Info.Name, prefix) { - continue - } - if filterRegexp == nil || method.matches(filterRegexp) { - runner.tests = append(runner.tests, method) - } - } - } - return runner -} - -// Run all methods in the given suite. -func (runner *suiteRunner) run() *Result { - if runner.tracker.result.RunError == nil && len(runner.tests) > 0 { - runner.tracker.start() - if runner.checkFixtureArgs() { - c := runner.runFixture(runner.setUpSuite, "", nil) - if c == nil || c.status() == succeededSt { - for i := 0; i != len(runner.tests); i++ { - c := runner.runTest(runner.tests[i]) - if c.status() == fixturePanickedSt { - runner.skipTests(missedSt, runner.tests[i+1:]) - break - } - } - } else if c != nil && c.status() == skippedSt { - runner.skipTests(skippedSt, runner.tests) - } else { - runner.skipTests(missedSt, runner.tests) - } - runner.runFixture(runner.tearDownSuite, "", nil) - } else { - runner.skipTests(missedSt, runner.tests) - } - runner.tracker.waitAndStop() - if runner.keepDir { - runner.tracker.result.WorkDir = runner.tempDir.path - } else { - runner.tempDir.removeAll() - } - } - return &runner.tracker.result -} - -// Create a call object with the given suite method, and fork a -// goroutine with the provided dispatcher for running it. -func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C { - var logw io.Writer - if runner.output.Stream { - logw = runner.output - } - if logb == nil { - logb = new(logger) - } - c := &C{ - method: method, - kind: kind, - testName: testName, - logb: logb, - logw: logw, - tempDir: runner.tempDir, - done: make(chan *C, 1), - timer: timer{benchTime: runner.benchTime}, - startTime: time.Now(), - benchMem: runner.benchMem, - } - runner.tracker.expectCall(c) - go (func() { - runner.reportCallStarted(c) - defer runner.callDone(c) - dispatcher(c) - })() - return c -} - -type timeoutErr struct { - method *methodType - t time.Duration -} - -func (e timeoutErr) Error() string { - return fmt.Sprintf("%s test timed out after %v", e.method.String(), e.t) -} - -func isTimeout(e error) bool { - if e == nil { - return false - } - _, ok := e.(timeoutErr) - return ok -} - -// Same as forkCall(), but wait for call to finish before returning. -func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C { - var timeout <-chan time.Time - if runner.checkTimeout != 0 { - timeout = time.After(runner.checkTimeout) - } - c := runner.forkCall(method, kind, testName, logb, dispatcher) - select { - case <-c.done: - case <-timeout: - if runner.onTimeout != nil { - // run the OnTimeout callback, allowing the suite to collect any sort of debug information it can - // `runFixture` is syncronous, so run this in a separate goroutine with a timeout - cChan := make(chan *C) - go func() { - cChan <- runner.runFixture(runner.onTimeout, c.testName, c.logb) - }() - select { - case <-cChan: - case <-time.After(runner.checkTimeout): - } - } - panic(timeoutErr{method, runner.checkTimeout}) - } - return c -} - -// Handle a finished call. If there were any panics, update the call status -// accordingly. Then, mark the call as done and report to the tracker. -func (runner *suiteRunner) callDone(c *C) { - value := recover() - if value != nil { - switch v := value.(type) { - case *fixturePanic: - if v.status == skippedSt { - c.setStatus(skippedSt) - } else { - c.logSoftPanic("Fixture has panicked (see related PANIC)") - c.setStatus(fixturePanickedSt) - } - default: - c.logPanic(1, value) - c.setStatus(panickedSt) - } - } - if c.mustFail { - switch c.status() { - case failedSt: - c.setStatus(succeededSt) - case succeededSt: - c.setStatus(failedSt) - c.logString("Error: Test succeeded, but was expected to fail") - c.logString("Reason: " + c.reason) - } - } - - runner.reportCallDone(c) - c.done <- c -} - -// Runs a fixture call synchronously. The fixture will still be run in a -// goroutine like all suite methods, but this method will not return -// while the fixture goroutine is not done, because the fixture must be -// run in a desired order. -func (runner *suiteRunner) runFixture(method *methodType, testName string, logb *logger) *C { - if method != nil { - c := runner.runFunc(method, fixtureKd, testName, logb, func(c *C) { - c.ResetTimer() - c.StartTimer() - defer c.StopTimer() - c.method.Call([]reflect.Value{reflect.ValueOf(c)}) - }) - return c - } - return nil -} - -// Run the fixture method with runFixture(), but panic with a fixturePanic{} -// in case the fixture method panics. This makes it easier to track the -// fixture panic together with other call panics within forkTest(). -func (runner *suiteRunner) runFixtureWithPanic(method *methodType, testName string, logb *logger, skipped *bool) *C { - if skipped != nil && *skipped { - return nil - } - c := runner.runFixture(method, testName, logb) - if c != nil && c.status() != succeededSt { - if skipped != nil { - *skipped = c.status() == skippedSt - } - panic(&fixturePanic{c.status(), method}) - } - return c -} - -type fixturePanic struct { - status funcStatus - method *methodType -} - -// Run the suite test method, together with the test-specific fixture, -// asynchronously. -func (runner *suiteRunner) forkTest(method *methodType) *C { - testName := method.String() - return runner.forkCall(method, testKd, testName, nil, func(c *C) { - var skipped bool - defer runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, &skipped) - defer c.StopTimer() - benchN := 1 - for { - runner.runFixtureWithPanic(runner.setUpTest, testName, c.logb, &skipped) - mt := c.method.Type() - if mt.NumIn() != 1 || mt.In(0) != reflect.TypeOf(c) { - // Rather than a plain panic, provide a more helpful message when - // the argument type is incorrect. - c.setStatus(panickedSt) - c.logArgPanic(c.method, "*check.C") - return - } - - if strings.HasPrefix(c.method.Info.Name, "Test") { - c.ResetTimer() - c.StartTimer() - c.method.Call([]reflect.Value{reflect.ValueOf(c)}) - return - } - - if !strings.HasPrefix(c.method.Info.Name, "Benchmark") { - panic("unexpected method prefix: " + c.method.Info.Name) - } - - runtime.GC() - c.N = benchN - c.ResetTimer() - c.StartTimer() - - c.method.Call([]reflect.Value{reflect.ValueOf(c)}) - c.StopTimer() - if c.status() != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 { - return - } - perOpN := int(1e9) - if c.nsPerOp() != 0 { - perOpN = int(c.benchTime.Nanoseconds() / c.nsPerOp()) - } - - // Logic taken from the stock testing package: - // - Run more iterations than we think we'll need for a second (1.5x). - // - Don't grow too fast in case we had timing errors previously. - // - Be sure to run at least one more than last time. - benchN = max(min(perOpN+perOpN/2, 100*benchN), benchN+1) - benchN = roundUp(benchN) - - skipped = true // Don't run the deferred one if this panics. - runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, nil) - skipped = false - } - }) -} - -// Same as forkTest(), but wait for the test to finish before returning. -func (runner *suiteRunner) runTest(method *methodType) *C { - var timeout <-chan time.Time - if runner.checkTimeout != 0 { - timeout = time.After(runner.checkTimeout) - } - c := runner.forkTest(method) - select { - case <-c.done: - case <-timeout: - if runner.onTimeout != nil { - // run the OnTimeout callback, allowing the suite to collect any sort of debug information it can - // `runFixture` is syncronous, so run this in a separate goroutine with a timeout - cChan := make(chan *C) - go func() { - cChan <- runner.runFixture(runner.onTimeout, c.testName, c.logb) - }() - select { - case <-cChan: - case <-time.After(runner.checkTimeout): - } - } - panic(timeoutErr{method, runner.checkTimeout}) - } - return c -} - -// Helper to mark tests as skipped or missed. A bit heavy for what -// it does, but it enables homogeneous handling of tracking, including -// nice verbose output. -func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) { - for _, method := range methods { - runner.runFunc(method, testKd, "", nil, func(c *C) { - c.setStatus(status) - }) - } -} - -// Verify if the fixture arguments are *check.C. In case of errors, -// log the error as a panic in the fixture method call, and return false. -func (runner *suiteRunner) checkFixtureArgs() bool { - succeeded := true - argType := reflect.TypeOf(&C{}) - for _, method := range []*methodType{runner.setUpSuite, runner.tearDownSuite, runner.setUpTest, runner.tearDownTest, runner.onTimeout} { - if method != nil { - mt := method.Type() - if mt.NumIn() != 1 || mt.In(0) != argType { - succeeded = false - runner.runFunc(method, fixtureKd, "", nil, func(c *C) { - c.logArgPanic(method, "*check.C") - c.setStatus(panickedSt) - }) - } - } - } - return succeeded -} - -func (runner *suiteRunner) reportCallStarted(c *C) { - runner.output.WriteCallStarted("START", c) -} - -func (runner *suiteRunner) reportCallDone(c *C) { - runner.tracker.callDone(c) - switch c.status() { - case succeededSt: - if c.mustFail { - runner.output.WriteCallSuccess("FAIL EXPECTED", c) - } else { - runner.output.WriteCallSuccess("PASS", c) - } - case skippedSt: - runner.output.WriteCallSuccess("SKIP", c) - case failedSt: - runner.output.WriteCallProblem("FAIL", c) - case panickedSt: - runner.output.WriteCallProblem("PANIC", c) - case fixturePanickedSt: - // That's a testKd call reporting that its fixture - // has panicked. The fixture call which caused the - // panic itself was tracked above. We'll report to - // aid debugging. - runner.output.WriteCallProblem("PANIC", c) - case missedSt: - runner.output.WriteCallSuccess("MISS", c) - } -} diff --git a/vendor/github.com/go-check/check/checkers.go b/vendor/github.com/go-check/check/checkers.go deleted file mode 100644 index bac338729c887..0000000000000 --- a/vendor/github.com/go-check/check/checkers.go +++ /dev/null @@ -1,458 +0,0 @@ -package check - -import ( - "fmt" - "reflect" - "regexp" -) - -// ----------------------------------------------------------------------- -// CommentInterface and Commentf helper, to attach extra information to checks. - -type comment struct { - format string - args []interface{} -} - -// Commentf returns an infomational value to use with Assert or Check calls. -// If the checker test fails, the provided arguments will be passed to -// fmt.Sprintf, and will be presented next to the logged failure. -// -// For example: -// -// c.Assert(v, Equals, 42, Commentf("Iteration #%d failed.", i)) -// -// Note that if the comment is constant, a better option is to -// simply use a normal comment right above or next to the line, as -// it will also get printed with any errors: -// -// c.Assert(l, Equals, 8192) // Ensure buffer size is correct (bug #123) -// -func Commentf(format string, args ...interface{}) CommentInterface { - return &comment{format, args} -} - -// CommentInterface must be implemented by types that attach extra -// information to failed checks. See the Commentf function for details. -type CommentInterface interface { - CheckCommentString() string -} - -func (c *comment) CheckCommentString() string { - return fmt.Sprintf(c.format, c.args...) -} - -// ----------------------------------------------------------------------- -// The Checker interface. - -// The Checker interface must be provided by checkers used with -// the Assert and Check verification methods. -type Checker interface { - Info() *CheckerInfo - Check(params []interface{}, names []string) (result bool, error string) -} - -// See the Checker interface. -type CheckerInfo struct { - Name string - Params []string -} - -func (info *CheckerInfo) Info() *CheckerInfo { - return info -} - -// ----------------------------------------------------------------------- -// Not checker logic inverter. - -// The Not checker inverts the logic of the provided checker. The -// resulting checker will succeed where the original one failed, and -// vice-versa. -// -// For example: -// -// c.Assert(a, Not(Equals), b) -// -func Not(checker Checker) Checker { - return ¬Checker{checker} -} - -type notChecker struct { - sub Checker -} - -func (checker *notChecker) Info() *CheckerInfo { - info := *checker.sub.Info() - info.Name = "Not(" + info.Name + ")" - return &info -} - -func (checker *notChecker) Check(params []interface{}, names []string) (result bool, error string) { - result, error = checker.sub.Check(params, names) - result = !result - return -} - -// ----------------------------------------------------------------------- -// IsNil checker. - -type isNilChecker struct { - *CheckerInfo -} - -// The IsNil checker tests whether the obtained value is nil. -// -// For example: -// -// c.Assert(err, IsNil) -// -var IsNil Checker = &isNilChecker{ - &CheckerInfo{Name: "IsNil", Params: []string{"value"}}, -} - -func (checker *isNilChecker) Check(params []interface{}, names []string) (result bool, error string) { - return isNil(params[0]), "" -} - -func isNil(obtained interface{}) (result bool) { - if obtained == nil { - result = true - } else { - switch v := reflect.ValueOf(obtained); v.Kind() { - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: - return v.IsNil() - } - } - return -} - -// ----------------------------------------------------------------------- -// NotNil checker. Alias for Not(IsNil), since it's so common. - -type notNilChecker struct { - *CheckerInfo -} - -// The NotNil checker verifies that the obtained value is not nil. -// -// For example: -// -// c.Assert(iface, NotNil) -// -// This is an alias for Not(IsNil), made available since it's a -// fairly common check. -// -var NotNil Checker = ¬NilChecker{ - &CheckerInfo{Name: "NotNil", Params: []string{"value"}}, -} - -func (checker *notNilChecker) Check(params []interface{}, names []string) (result bool, error string) { - return !isNil(params[0]), "" -} - -// ----------------------------------------------------------------------- -// Equals checker. - -type equalsChecker struct { - *CheckerInfo -} - -// The Equals checker verifies that the obtained value is equal to -// the expected value, according to usual Go semantics for ==. -// -// For example: -// -// c.Assert(value, Equals, 42) -// -var Equals Checker = &equalsChecker{ - &CheckerInfo{Name: "Equals", Params: []string{"obtained", "expected"}}, -} - -func (checker *equalsChecker) Check(params []interface{}, names []string) (result bool, error string) { - defer func() { - if v := recover(); v != nil { - result = false - error = fmt.Sprint(v) - } - }() - return params[0] == params[1], "" -} - -// ----------------------------------------------------------------------- -// DeepEquals checker. - -type deepEqualsChecker struct { - *CheckerInfo -} - -// The DeepEquals checker verifies that the obtained value is deep-equal to -// the expected value. The check will work correctly even when facing -// slices, interfaces, and values of different types (which always fail -// the test). -// -// For example: -// -// c.Assert(value, DeepEquals, 42) -// c.Assert(array, DeepEquals, []string{"hi", "there"}) -// -var DeepEquals Checker = &deepEqualsChecker{ - &CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}}, -} - -func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) { - return reflect.DeepEqual(params[0], params[1]), "" -} - -// ----------------------------------------------------------------------- -// HasLen checker. - -type hasLenChecker struct { - *CheckerInfo -} - -// The HasLen checker verifies that the obtained value has the -// provided length. In many cases this is superior to using Equals -// in conjuction with the len function because in case the check -// fails the value itself will be printed, instead of its length, -// providing more details for figuring the problem. -// -// For example: -// -// c.Assert(list, HasLen, 5) -// -var HasLen Checker = &hasLenChecker{ - &CheckerInfo{Name: "HasLen", Params: []string{"obtained", "n"}}, -} - -func (checker *hasLenChecker) Check(params []interface{}, names []string) (result bool, error string) { - n, ok := params[1].(int) - if !ok { - return false, "n must be an int" - } - value := reflect.ValueOf(params[0]) - switch value.Kind() { - case reflect.Map, reflect.Array, reflect.Slice, reflect.Chan, reflect.String: - default: - return false, "obtained value type has no length" - } - return value.Len() == n, "" -} - -// ----------------------------------------------------------------------- -// ErrorMatches checker. - -type errorMatchesChecker struct { - *CheckerInfo -} - -// The ErrorMatches checker verifies that the error value -// is non nil and matches the regular expression provided. -// -// For example: -// -// c.Assert(err, ErrorMatches, "perm.*denied") -// -var ErrorMatches Checker = errorMatchesChecker{ - &CheckerInfo{Name: "ErrorMatches", Params: []string{"value", "regex"}}, -} - -func (checker errorMatchesChecker) Check(params []interface{}, names []string) (result bool, errStr string) { - if params[0] == nil { - return false, "Error value is nil" - } - err, ok := params[0].(error) - if !ok { - return false, "Value is not an error" - } - params[0] = err.Error() - names[0] = "error" - return matches(params[0], params[1]) -} - -// ----------------------------------------------------------------------- -// Matches checker. - -type matchesChecker struct { - *CheckerInfo -} - -// The Matches checker verifies that the string provided as the obtained -// value (or the string resulting from obtained.String()) matches the -// regular expression provided. -// -// For example: -// -// c.Assert(err, Matches, "perm.*denied") -// -var Matches Checker = &matchesChecker{ - &CheckerInfo{Name: "Matches", Params: []string{"value", "regex"}}, -} - -func (checker *matchesChecker) Check(params []interface{}, names []string) (result bool, error string) { - return matches(params[0], params[1]) -} - -func matches(value, regex interface{}) (result bool, error string) { - reStr, ok := regex.(string) - if !ok { - return false, "Regex must be a string" - } - valueStr, valueIsStr := value.(string) - if !valueIsStr { - if valueWithStr, valueHasStr := value.(fmt.Stringer); valueHasStr { - valueStr, valueIsStr = valueWithStr.String(), true - } - } - if valueIsStr { - matches, err := regexp.MatchString("^"+reStr+"$", valueStr) - if err != nil { - return false, "Can't compile regex: " + err.Error() - } - return matches, "" - } - return false, "Obtained value is not a string and has no .String()" -} - -// ----------------------------------------------------------------------- -// Panics checker. - -type panicsChecker struct { - *CheckerInfo -} - -// The Panics checker verifies that calling the provided zero-argument -// function will cause a panic which is deep-equal to the provided value. -// -// For example: -// -// c.Assert(func() { f(1, 2) }, Panics, &SomeErrorType{"BOOM"}). -// -// -var Panics Checker = &panicsChecker{ - &CheckerInfo{Name: "Panics", Params: []string{"function", "expected"}}, -} - -func (checker *panicsChecker) Check(params []interface{}, names []string) (result bool, error string) { - f := reflect.ValueOf(params[0]) - if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { - return false, "Function must take zero arguments" - } - defer func() { - // If the function has not panicked, then don't do the check. - if error != "" { - return - } - params[0] = recover() - names[0] = "panic" - result = reflect.DeepEqual(params[0], params[1]) - }() - f.Call(nil) - return false, "Function has not panicked" -} - -type panicMatchesChecker struct { - *CheckerInfo -} - -// The PanicMatches checker verifies that calling the provided zero-argument -// function will cause a panic with an error value matching -// the regular expression provided. -// -// For example: -// -// c.Assert(func() { f(1, 2) }, PanicMatches, `open.*: no such file or directory`). -// -// -var PanicMatches Checker = &panicMatchesChecker{ - &CheckerInfo{Name: "PanicMatches", Params: []string{"function", "expected"}}, -} - -func (checker *panicMatchesChecker) Check(params []interface{}, names []string) (result bool, errmsg string) { - f := reflect.ValueOf(params[0]) - if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { - return false, "Function must take zero arguments" - } - defer func() { - // If the function has not panicked, then don't do the check. - if errmsg != "" { - return - } - obtained := recover() - names[0] = "panic" - if e, ok := obtained.(error); ok { - params[0] = e.Error() - } else if _, ok := obtained.(string); ok { - params[0] = obtained - } else { - errmsg = "Panic value is not a string or an error" - return - } - result, errmsg = matches(params[0], params[1]) - }() - f.Call(nil) - return false, "Function has not panicked" -} - -// ----------------------------------------------------------------------- -// FitsTypeOf checker. - -type fitsTypeChecker struct { - *CheckerInfo -} - -// The FitsTypeOf checker verifies that the obtained value is -// assignable to a variable with the same type as the provided -// sample value. -// -// For example: -// -// c.Assert(value, FitsTypeOf, int64(0)) -// c.Assert(value, FitsTypeOf, os.Error(nil)) -// -var FitsTypeOf Checker = &fitsTypeChecker{ - &CheckerInfo{Name: "FitsTypeOf", Params: []string{"obtained", "sample"}}, -} - -func (checker *fitsTypeChecker) Check(params []interface{}, names []string) (result bool, error string) { - obtained := reflect.ValueOf(params[0]) - sample := reflect.ValueOf(params[1]) - if !obtained.IsValid() { - return false, "" - } - if !sample.IsValid() { - return false, "Invalid sample value" - } - return obtained.Type().AssignableTo(sample.Type()), "" -} - -// ----------------------------------------------------------------------- -// Implements checker. - -type implementsChecker struct { - *CheckerInfo -} - -// The Implements checker verifies that the obtained value -// implements the interface specified via a pointer to an interface -// variable. -// -// For example: -// -// var e os.Error -// c.Assert(err, Implements, &e) -// -var Implements Checker = &implementsChecker{ - &CheckerInfo{Name: "Implements", Params: []string{"obtained", "ifaceptr"}}, -} - -func (checker *implementsChecker) Check(params []interface{}, names []string) (result bool, error string) { - obtained := reflect.ValueOf(params[0]) - ifaceptr := reflect.ValueOf(params[1]) - if !obtained.IsValid() { - return false, "" - } - if !ifaceptr.IsValid() || ifaceptr.Kind() != reflect.Ptr || ifaceptr.Elem().Kind() != reflect.Interface { - return false, "ifaceptr should be a pointer to an interface variable" - } - return obtained.Type().Implements(ifaceptr.Elem().Type()), "" -} diff --git a/vendor/github.com/go-check/check/helpers.go b/vendor/github.com/go-check/check/helpers.go deleted file mode 100644 index 58a733b50f6dc..0000000000000 --- a/vendor/github.com/go-check/check/helpers.go +++ /dev/null @@ -1,231 +0,0 @@ -package check - -import ( - "fmt" - "strings" - "time" -) - -// TestName returns the current test name in the form "SuiteName.TestName" -func (c *C) TestName() string { - return c.testName -} - -// ----------------------------------------------------------------------- -// Basic succeeding/failing logic. - -// Failed returns whether the currently running test has already failed. -func (c *C) Failed() bool { - return c.status() == failedSt -} - -// Fail marks the currently running test as failed. -// -// Something ought to have been previously logged so the developer can tell -// what went wrong. The higher level helper functions will fail the test -// and do the logging properly. -func (c *C) Fail() { - c.setStatus(failedSt) -} - -// FailNow marks the currently running test as failed and stops running it. -// Something ought to have been previously logged so the developer can tell -// what went wrong. The higher level helper functions will fail the test -// and do the logging properly. -func (c *C) FailNow() { - c.Fail() - c.stopNow() -} - -// Succeed marks the currently running test as succeeded, undoing any -// previous failures. -func (c *C) Succeed() { - c.setStatus(succeededSt) -} - -// SucceedNow marks the currently running test as succeeded, undoing any -// previous failures, and stops running the test. -func (c *C) SucceedNow() { - c.Succeed() - c.stopNow() -} - -// ExpectFailure informs that the running test is knowingly broken for -// the provided reason. If the test does not fail, an error will be reported -// to raise attention to this fact. This method is useful to temporarily -// disable tests which cover well known problems until a better time to -// fix the problem is found, without forgetting about the fact that a -// failure still exists. -func (c *C) ExpectFailure(reason string) { - if reason == "" { - panic("Missing reason why the test is expected to fail") - } - c.mustFail = true - c.reason = reason -} - -// Skip skips the running test for the provided reason. If run from within -// SetUpTest, the individual test being set up will be skipped, and if run -// from within SetUpSuite, the whole suite is skipped. -func (c *C) Skip(reason string) { - if reason == "" { - panic("Missing reason why the test is being skipped") - } - c.reason = reason - c.setStatus(skippedSt) - c.stopNow() -} - -// ----------------------------------------------------------------------- -// Basic logging. - -// GetTestLog returns the current test error output. -func (c *C) GetTestLog() string { - return c.logb.String() -} - -// Log logs some information into the test error output. -// The provided arguments are assembled together into a string with fmt.Sprint. -func (c *C) Log(args ...interface{}) { - c.log(args...) -} - -// Log logs some information into the test error output. -// The provided arguments are assembled together into a string with fmt.Sprintf. -func (c *C) Logf(format string, args ...interface{}) { - c.logf(format, args...) -} - -// Output enables *C to be used as a logger in functions that require only -// the minimum interface of *log.Logger. -func (c *C) Output(calldepth int, s string) error { - d := time.Now().Sub(c.startTime) - msec := d / time.Millisecond - sec := d / time.Second - min := d / time.Minute - - c.Logf("[LOG] %d:%02d.%03d %s", min, sec%60, msec%1000, s) - return nil -} - -// Error logs an error into the test error output and marks the test as failed. -// The provided arguments are assembled together into a string with fmt.Sprint. -func (c *C) Error(args ...interface{}) { - c.logCaller(1) - c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...))) - c.logNewLine() - c.Fail() -} - -// Errorf logs an error into the test error output and marks the test as failed. -// The provided arguments are assembled together into a string with fmt.Sprintf. -func (c *C) Errorf(format string, args ...interface{}) { - c.logCaller(1) - c.logString(fmt.Sprintf("Error: "+format, args...)) - c.logNewLine() - c.Fail() -} - -// Fatal logs an error into the test error output, marks the test as failed, and -// stops the test execution. The provided arguments are assembled together into -// a string with fmt.Sprint. -func (c *C) Fatal(args ...interface{}) { - c.logCaller(1) - c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...))) - c.logNewLine() - c.FailNow() -} - -// Fatlaf logs an error into the test error output, marks the test as failed, and -// stops the test execution. The provided arguments are assembled together into -// a string with fmt.Sprintf. -func (c *C) Fatalf(format string, args ...interface{}) { - c.logCaller(1) - c.logString(fmt.Sprint("Error: ", fmt.Sprintf(format, args...))) - c.logNewLine() - c.FailNow() -} - -// ----------------------------------------------------------------------- -// Generic checks and assertions based on checkers. - -// Check verifies if the first value matches the expected value according -// to the provided checker. If they do not match, an error is logged, the -// test is marked as failed, and the test execution continues. -// -// Some checkers may not need the expected argument (e.g. IsNil). -// -// Extra arguments provided to the function are logged next to the reported -// problem when the matching fails. -func (c *C) Check(obtained interface{}, checker Checker, args ...interface{}) bool { - return c.internalCheck("Check", obtained, checker, args...) -} - -// Assert ensures that the first value matches the expected value according -// to the provided checker. If they do not match, an error is logged, the -// test is marked as failed, and the test execution stops. -// -// Some checkers may not need the expected argument (e.g. IsNil). -// -// Extra arguments provided to the function are logged next to the reported -// problem when the matching fails. -func (c *C) Assert(obtained interface{}, checker Checker, args ...interface{}) { - if !c.internalCheck("Assert", obtained, checker, args...) { - c.stopNow() - } -} - -func (c *C) internalCheck(funcName string, obtained interface{}, checker Checker, args ...interface{}) bool { - if checker == nil { - c.logCaller(2) - c.logString(fmt.Sprintf("%s(obtained, nil!?, ...):", funcName)) - c.logString("Oops.. you've provided a nil checker!") - c.logNewLine() - c.Fail() - return false - } - - // If the last argument is a bug info, extract it out. - var comment CommentInterface - if len(args) > 0 { - if c, ok := args[len(args)-1].(CommentInterface); ok { - comment = c - args = args[:len(args)-1] - } - } - - params := append([]interface{}{obtained}, args...) - info := checker.Info() - - if len(params) != len(info.Params) { - names := append([]string{info.Params[0], info.Name}, info.Params[1:]...) - c.logCaller(2) - c.logString(fmt.Sprintf("%s(%s):", funcName, strings.Join(names, ", "))) - c.logString(fmt.Sprintf("Wrong number of parameters for %s: want %d, got %d", info.Name, len(names), len(params)+1)) - c.logNewLine() - c.Fail() - return false - } - - // Copy since it may be mutated by Check. - names := append([]string{}, info.Params...) - - // Do the actual check. - result, error := checker.Check(params, names) - if !result || error != "" { - c.logCaller(2) - for i := 0; i != len(params); i++ { - c.logValue(names[i], params[i]) - } - if comment != nil { - c.logString(comment.CheckCommentString()) - } - if error != "" { - c.logString(error) - } - c.logNewLine() - c.Fail() - return false - } - return true -} diff --git a/vendor/github.com/go-check/check/printer.go b/vendor/github.com/go-check/check/printer.go deleted file mode 100644 index e0f7557b5cc76..0000000000000 --- a/vendor/github.com/go-check/check/printer.go +++ /dev/null @@ -1,168 +0,0 @@ -package check - -import ( - "bytes" - "go/ast" - "go/parser" - "go/printer" - "go/token" - "os" -) - -func indent(s, with string) (r string) { - eol := true - for i := 0; i != len(s); i++ { - c := s[i] - switch { - case eol && c == '\n' || c == '\r': - case c == '\n' || c == '\r': - eol = true - case eol: - eol = false - s = s[:i] + with + s[i:] - i += len(with) - } - } - return s -} - -func printLine(filename string, line int) (string, error) { - fset := token.NewFileSet() - file, err := os.Open(filename) - if err != nil { - return "", err - } - fnode, err := parser.ParseFile(fset, filename, file, parser.ParseComments) - if err != nil { - return "", err - } - config := &printer.Config{Mode: printer.UseSpaces, Tabwidth: 4} - lp := &linePrinter{fset: fset, fnode: fnode, line: line, config: config} - ast.Walk(lp, fnode) - result := lp.output.Bytes() - // Comments leave \n at the end. - n := len(result) - for n > 0 && result[n-1] == '\n' { - n-- - } - return string(result[:n]), nil -} - -type linePrinter struct { - config *printer.Config - fset *token.FileSet - fnode *ast.File - line int - output bytes.Buffer - stmt ast.Stmt -} - -func (lp *linePrinter) emit() bool { - if lp.stmt != nil { - lp.trim(lp.stmt) - lp.printWithComments(lp.stmt) - lp.stmt = nil - return true - } - return false -} - -func (lp *linePrinter) printWithComments(n ast.Node) { - nfirst := lp.fset.Position(n.Pos()).Line - nlast := lp.fset.Position(n.End()).Line - for _, g := range lp.fnode.Comments { - cfirst := lp.fset.Position(g.Pos()).Line - clast := lp.fset.Position(g.End()).Line - if clast == nfirst-1 && lp.fset.Position(n.Pos()).Column == lp.fset.Position(g.Pos()).Column { - for _, c := range g.List { - lp.output.WriteString(c.Text) - lp.output.WriteByte('\n') - } - } - if cfirst >= nfirst && cfirst <= nlast && n.End() <= g.List[0].Slash { - // The printer will not include the comment if it starts past - // the node itself. Trick it into printing by overlapping the - // slash with the end of the statement. - g.List[0].Slash = n.End() - 1 - } - } - node := &printer.CommentedNode{n, lp.fnode.Comments} - lp.config.Fprint(&lp.output, lp.fset, node) -} - -func (lp *linePrinter) Visit(n ast.Node) (w ast.Visitor) { - if n == nil { - if lp.output.Len() == 0 { - lp.emit() - } - return nil - } - first := lp.fset.Position(n.Pos()).Line - last := lp.fset.Position(n.End()).Line - if first <= lp.line && last >= lp.line { - // Print the innermost statement containing the line. - if stmt, ok := n.(ast.Stmt); ok { - if _, ok := n.(*ast.BlockStmt); !ok { - lp.stmt = stmt - } - } - if first == lp.line && lp.emit() { - return nil - } - return lp - } - return nil -} - -func (lp *linePrinter) trim(n ast.Node) bool { - stmt, ok := n.(ast.Stmt) - if !ok { - return true - } - line := lp.fset.Position(n.Pos()).Line - if line != lp.line { - return false - } - switch stmt := stmt.(type) { - case *ast.IfStmt: - stmt.Body = lp.trimBlock(stmt.Body) - case *ast.SwitchStmt: - stmt.Body = lp.trimBlock(stmt.Body) - case *ast.TypeSwitchStmt: - stmt.Body = lp.trimBlock(stmt.Body) - case *ast.CaseClause: - stmt.Body = lp.trimList(stmt.Body) - case *ast.CommClause: - stmt.Body = lp.trimList(stmt.Body) - case *ast.BlockStmt: - stmt.List = lp.trimList(stmt.List) - } - return true -} - -func (lp *linePrinter) trimBlock(stmt *ast.BlockStmt) *ast.BlockStmt { - if !lp.trim(stmt) { - return lp.emptyBlock(stmt) - } - stmt.Rbrace = stmt.Lbrace - return stmt -} - -func (lp *linePrinter) trimList(stmts []ast.Stmt) []ast.Stmt { - for i := 0; i != len(stmts); i++ { - if !lp.trim(stmts[i]) { - stmts[i] = lp.emptyStmt(stmts[i]) - break - } - } - return stmts -} - -func (lp *linePrinter) emptyStmt(n ast.Node) *ast.ExprStmt { - return &ast.ExprStmt{&ast.Ellipsis{n.Pos(), nil}} -} - -func (lp *linePrinter) emptyBlock(n ast.Node) *ast.BlockStmt { - p := n.Pos() - return &ast.BlockStmt{p, []ast.Stmt{lp.emptyStmt(n)}, p} -} diff --git a/vendor/github.com/go-check/check/reporter.go b/vendor/github.com/go-check/check/reporter.go deleted file mode 100644 index fb04f76f64d80..0000000000000 --- a/vendor/github.com/go-check/check/reporter.go +++ /dev/null @@ -1,88 +0,0 @@ -package check - -import ( - "fmt" - "io" - "sync" -) - -// ----------------------------------------------------------------------- -// Output writer manages atomic output writing according to settings. - -type outputWriter struct { - m sync.Mutex - writer io.Writer - wroteCallProblemLast bool - Stream bool - Verbose bool -} - -func newOutputWriter(writer io.Writer, stream, verbose bool) *outputWriter { - return &outputWriter{writer: writer, Stream: stream, Verbose: verbose} -} - -func (ow *outputWriter) Write(content []byte) (n int, err error) { - ow.m.Lock() - n, err = ow.writer.Write(content) - ow.m.Unlock() - return -} - -func (ow *outputWriter) WriteCallStarted(label string, c *C) { - if ow.Stream { - header := renderCallHeader(label, c, "", "\n") - ow.m.Lock() - ow.writer.Write([]byte(header)) - ow.m.Unlock() - } -} - -func (ow *outputWriter) WriteCallProblem(label string, c *C) { - var prefix string - if !ow.Stream { - prefix = "\n-----------------------------------" + - "-----------------------------------\n" - } - header := renderCallHeader(label, c, prefix, "\n\n") - ow.m.Lock() - ow.wroteCallProblemLast = true - ow.writer.Write([]byte(header)) - if !ow.Stream { - c.logb.WriteTo(ow.writer) - } - ow.m.Unlock() -} - -func (ow *outputWriter) WriteCallSuccess(label string, c *C) { - if ow.Stream || (ow.Verbose && c.kind == testKd) { - // TODO Use a buffer here. - var suffix string - if c.reason != "" { - suffix = " (" + c.reason + ")" - } - if c.status() == succeededSt { - suffix += "\t" + c.timerString() - } - suffix += "\n" - if ow.Stream { - suffix += "\n" - } - header := renderCallHeader(label, c, "", suffix) - ow.m.Lock() - // Resist temptation of using line as prefix above due to race. - if !ow.Stream && ow.wroteCallProblemLast { - header = "\n-----------------------------------" + - "-----------------------------------\n" + - header - } - ow.wroteCallProblemLast = false - ow.writer.Write([]byte(header)) - ow.m.Unlock() - } -} - -func renderCallHeader(label string, c *C, prefix, suffix string) string { - pc := c.method.PC() - return fmt.Sprintf("%s%s: %s: %s%s", prefix, label, niceFuncPath(pc), - niceFuncName(pc), suffix) -} diff --git a/vendor/github.com/go-check/check/run.go b/vendor/github.com/go-check/check/run.go deleted file mode 100644 index f72119656d0bb..0000000000000 --- a/vendor/github.com/go-check/check/run.go +++ /dev/null @@ -1,183 +0,0 @@ -package check - -import ( - "bufio" - "flag" - "fmt" - "os" - "testing" - "time" -) - -// ----------------------------------------------------------------------- -// Test suite registry. - -var allSuites []interface{} - -// Suite registers the given value as a test suite to be run. Any methods -// starting with the Test prefix in the given value will be considered as -// a test method. -func Suite(suite interface{}) interface{} { - allSuites = append(allSuites, suite) - return suite -} - -// ----------------------------------------------------------------------- -// Public running interface. - -var ( - oldFilterFlag = flag.String("gocheck.f", "", "Regular expression selecting which tests and/or suites to run") - oldVerboseFlag = flag.Bool("gocheck.v", false, "Verbose mode") - oldStreamFlag = flag.Bool("gocheck.vv", false, "Super verbose mode (disables output caching)") - oldBenchFlag = flag.Bool("gocheck.b", false, "Run benchmarks") - oldBenchTime = flag.Duration("gocheck.btime", 1*time.Second, "approximate run time for each benchmark") - oldListFlag = flag.Bool("gocheck.list", false, "List the names of all tests that will be run") - oldWorkFlag = flag.Bool("gocheck.work", false, "Display and do not remove the test working directory") - - newFilterFlag = flag.String("check.f", "", "Regular expression selecting which tests and/or suites to run") - newVerboseFlag = flag.Bool("check.v", false, "Verbose mode") - newStreamFlag = flag.Bool("check.vv", false, "Super verbose mode (disables output caching)") - newBenchFlag = flag.Bool("check.b", false, "Run benchmarks") - newBenchTime = flag.Duration("check.btime", 1*time.Second, "approximate run time for each benchmark") - newBenchMem = flag.Bool("check.bmem", false, "Report memory benchmarks") - newListFlag = flag.Bool("check.list", false, "List the names of all tests that will be run") - newWorkFlag = flag.Bool("check.work", false, "Display and do not remove the test working directory") - checkTimeout = flag.String("check.timeout", "", "Panic if test runs longer than specified duration") -) - -// TestingT runs all test suites registered with the Suite function, -// printing results to stdout, and reporting any failures back to -// the "testing" package. -func TestingT(testingT *testing.T) { - benchTime := *newBenchTime - if benchTime == 1*time.Second { - benchTime = *oldBenchTime - } - conf := &RunConf{ - Filter: *oldFilterFlag + *newFilterFlag, - Verbose: *oldVerboseFlag || *newVerboseFlag, - Stream: *oldStreamFlag || *newStreamFlag, - Benchmark: *oldBenchFlag || *newBenchFlag, - BenchmarkTime: benchTime, - BenchmarkMem: *newBenchMem, - KeepWorkDir: *oldWorkFlag || *newWorkFlag, - } - if *checkTimeout != "" { - timeout, err := time.ParseDuration(*checkTimeout) - if err != nil { - testingT.Fatalf("error parsing specified timeout flag: %v", err) - } - conf.CheckTimeout = timeout - } - if *oldListFlag || *newListFlag { - w := bufio.NewWriter(os.Stdout) - for _, name := range ListAll(conf) { - fmt.Fprintln(w, name) - } - w.Flush() - return - } - result := RunAll(conf) - println(result.String()) - if !result.Passed() { - testingT.Fail() - } -} - -// RunAll runs all test suites registered with the Suite function, using the -// provided run configuration. -func RunAll(runConf *RunConf) *Result { - result := Result{} - for _, suite := range allSuites { - result.Add(Run(suite, runConf)) - } - return &result -} - -// Run runs the provided test suite using the provided run configuration. -func Run(suite interface{}, runConf *RunConf) *Result { - runner := newSuiteRunner(suite, runConf) - return runner.run() -} - -// ListAll returns the names of all the test functions registered with the -// Suite function that will be run with the provided run configuration. -func ListAll(runConf *RunConf) []string { - var names []string - for _, suite := range allSuites { - names = append(names, List(suite, runConf)...) - } - return names -} - -// List returns the names of the test functions in the given -// suite that will be run with the provided run configuration. -func List(suite interface{}, runConf *RunConf) []string { - var names []string - runner := newSuiteRunner(suite, runConf) - for _, t := range runner.tests { - names = append(names, t.String()) - } - return names -} - -// ----------------------------------------------------------------------- -// Result methods. - -func (r *Result) Add(other *Result) { - r.Succeeded += other.Succeeded - r.Skipped += other.Skipped - r.Failed += other.Failed - r.Panicked += other.Panicked - r.FixturePanicked += other.FixturePanicked - r.ExpectedFailures += other.ExpectedFailures - r.Missed += other.Missed - if r.WorkDir != "" && other.WorkDir != "" { - r.WorkDir += ":" + other.WorkDir - } else if other.WorkDir != "" { - r.WorkDir = other.WorkDir - } -} - -func (r *Result) Passed() bool { - return (r.Failed == 0 && r.Panicked == 0 && - r.FixturePanicked == 0 && r.Missed == 0 && - r.RunError == nil) -} - -func (r *Result) String() string { - if r.RunError != nil { - return "ERROR: " + r.RunError.Error() - } - - var value string - if r.Failed == 0 && r.Panicked == 0 && r.FixturePanicked == 0 && - r.Missed == 0 { - value = "OK: " - } else { - value = "OOPS: " - } - value += fmt.Sprintf("%d passed", r.Succeeded) - if r.Skipped != 0 { - value += fmt.Sprintf(", %d skipped", r.Skipped) - } - if r.ExpectedFailures != 0 { - value += fmt.Sprintf(", %d expected failures", r.ExpectedFailures) - } - if r.Failed != 0 { - value += fmt.Sprintf(", %d FAILED", r.Failed) - } - if r.Panicked != 0 { - value += fmt.Sprintf(", %d PANICKED", r.Panicked) - } - if r.FixturePanicked != 0 { - value += fmt.Sprintf(", %d FIXTURE-PANICKED", r.FixturePanicked) - } - if r.Missed != 0 { - value += fmt.Sprintf(", %d MISSED", r.Missed) - } - if r.WorkDir != "" { - value += "\nWORK=" + r.WorkDir - } - return value -} diff --git a/vendor/github.com/go-ini/ini/README.md b/vendor/github.com/go-ini/ini/README.md deleted file mode 100644 index 85947422d70e7..0000000000000 --- a/vendor/github.com/go-ini/ini/README.md +++ /dev/null @@ -1,740 +0,0 @@ -INI [![Build Status](https://travis-ci.org/go-ini/ini.svg?branch=master)](https://travis-ci.org/go-ini/ini) [![Sourcegraph](https://sourcegraph.com/github.com/go-ini/ini/-/badge.svg)](https://sourcegraph.com/github.com/go-ini/ini?badge) -=== - -![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200) - -Package ini provides INI file read and write functionality in Go. - -[简体中文](README_ZH.md) - -## Feature - -- Load multiple data sources(`[]byte`, file and `io.ReadCloser`) with overwrites. -- Read with recursion values. -- Read with parent-child sections. -- Read with auto-increment key names. -- Read with multiple-line values. -- Read with tons of helper methods. -- Read and convert values to Go types. -- Read and **WRITE** comments of sections and keys. -- Manipulate sections, keys and comments with ease. -- Keep sections and keys in order as you parse and save. - -## Installation - -To use a tagged revision: - - go get gopkg.in/ini.v1 - -To use with latest changes: - - go get github.com/go-ini/ini - -Please add `-u` flag to update in the future. - -### Testing - -If you want to test on your machine, please apply `-t` flag: - - go get -t gopkg.in/ini.v1 - -Please add `-u` flag to update in the future. - -## Getting Started - -### Loading from data sources - -A **Data Source** is either raw data in type `[]byte`, a file name with type `string` or `io.ReadCloser`. You can load **as many data sources as you want**. Passing other types will simply return an error. - -```go -cfg, err := ini.Load([]byte("raw data"), "filename", ioutil.NopCloser(bytes.NewReader([]byte("some other data")))) -``` - -Or start with an empty object: - -```go -cfg := ini.Empty() -``` - -When you cannot decide how many data sources to load at the beginning, you will still be able to **Append()** them later. - -```go -err := cfg.Append("other file", []byte("other raw data")) -``` - -If you have a list of files with possibilities that some of them may not available at the time, and you don't know exactly which ones, you can use `LooseLoad` to ignore nonexistent files without returning error. - -```go -cfg, err := ini.LooseLoad("filename", "filename_404") -``` - -The cool thing is, whenever the file is available to load while you're calling `Reload` method, it will be counted as usual. - -#### Ignore cases of key name - -When you do not care about cases of section and key names, you can use `InsensitiveLoad` to force all names to be lowercased while parsing. - -```go -cfg, err := ini.InsensitiveLoad("filename") -//... - -// sec1 and sec2 are the exactly same section object -sec1, err := cfg.GetSection("Section") -sec2, err := cfg.GetSection("SecTIOn") - -// key1 and key2 are the exactly same key object -key1, err := cfg.GetKey("Key") -key2, err := cfg.GetKey("KeY") -``` - -#### MySQL-like boolean key - -MySQL's configuration allows a key without value as follows: - -```ini -[mysqld] -... -skip-host-cache -skip-name-resolve -``` - -By default, this is considered as missing value. But if you know you're going to deal with those cases, you can assign advanced load options: - -```go -cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf")) -``` - -The value of those keys are always `true`, and when you save to a file, it will keep in the same foramt as you read. - -To generate such keys in your program, you could use `NewBooleanKey`: - -```go -key, err := sec.NewBooleanKey("skip-host-cache") -``` - -#### Comment - -Take care that following format will be treated as comment: - -1. Line begins with `#` or `;` -2. Words after `#` or `;` -3. Words after section name (i.e words after `[some section name]`) - -If you want to save a value with `#` or `;`, please quote them with ``` ` ``` or ``` """ ```. - -### Working with sections - -To get a section, you would need to: - -```go -section, err := cfg.GetSection("section name") -``` - -For a shortcut for default section, just give an empty string as name: - -```go -section, err := cfg.GetSection("") -``` - -When you're pretty sure the section exists, following code could make your life easier: - -```go -section := cfg.Section("section name") -``` - -What happens when the section somehow does not exist? Don't panic, it automatically creates and returns a new section to you. - -To create a new section: - -```go -err := cfg.NewSection("new section") -``` - -To get a list of sections or section names: - -```go -sections := cfg.Sections() -names := cfg.SectionStrings() -``` - -### Working with keys - -To get a key under a section: - -```go -key, err := cfg.Section("").GetKey("key name") -``` - -Same rule applies to key operations: - -```go -key := cfg.Section("").Key("key name") -``` - -To check if a key exists: - -```go -yes := cfg.Section("").HasKey("key name") -``` - -To create a new key: - -```go -err := cfg.Section("").NewKey("name", "value") -``` - -To get a list of keys or key names: - -```go -keys := cfg.Section("").Keys() -names := cfg.Section("").KeyStrings() -``` - -To get a clone hash of keys and corresponding values: - -```go -hash := cfg.Section("").KeysHash() -``` - -### Working with values - -To get a string value: - -```go -val := cfg.Section("").Key("key name").String() -``` - -To validate key value on the fly: - -```go -val := cfg.Section("").Key("key name").Validate(func(in string) string { - if len(in) == 0 { - return "default" - } - return in -}) -``` - -If you do not want any auto-transformation (such as recursive read) for the values, you can get raw value directly (this way you get much better performance): - -```go -val := cfg.Section("").Key("key name").Value() -``` - -To check if raw value exists: - -```go -yes := cfg.Section("").HasValue("test value") -``` - -To get value with types: - -```go -// For boolean values: -// true when value is: 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On -// false when value is: 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off -v, err = cfg.Section("").Key("BOOL").Bool() -v, err = cfg.Section("").Key("FLOAT64").Float64() -v, err = cfg.Section("").Key("INT").Int() -v, err = cfg.Section("").Key("INT64").Int64() -v, err = cfg.Section("").Key("UINT").Uint() -v, err = cfg.Section("").Key("UINT64").Uint64() -v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339) -v, err = cfg.Section("").Key("TIME").Time() // RFC3339 - -v = cfg.Section("").Key("BOOL").MustBool() -v = cfg.Section("").Key("FLOAT64").MustFloat64() -v = cfg.Section("").Key("INT").MustInt() -v = cfg.Section("").Key("INT64").MustInt64() -v = cfg.Section("").Key("UINT").MustUint() -v = cfg.Section("").Key("UINT64").MustUint64() -v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339) -v = cfg.Section("").Key("TIME").MustTime() // RFC3339 - -// Methods start with Must also accept one argument for default value -// when key not found or fail to parse value to given type. -// Except method MustString, which you have to pass a default value. - -v = cfg.Section("").Key("String").MustString("default") -v = cfg.Section("").Key("BOOL").MustBool(true) -v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25) -v = cfg.Section("").Key("INT").MustInt(10) -v = cfg.Section("").Key("INT64").MustInt64(99) -v = cfg.Section("").Key("UINT").MustUint(3) -v = cfg.Section("").Key("UINT64").MustUint64(6) -v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now()) -v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339 -``` - -What if my value is three-line long? - -```ini -[advance] -ADDRESS = """404 road, -NotFound, State, 5000 -Earth""" -``` - -Not a problem! - -```go -cfg.Section("advance").Key("ADDRESS").String() - -/* --- start --- -404 road, -NotFound, State, 5000 -Earth ------- end --- */ -``` - -That's cool, how about continuation lines? - -```ini -[advance] -two_lines = how about \ - continuation lines? -lots_of_lines = 1 \ - 2 \ - 3 \ - 4 -``` - -Piece of cake! - -```go -cfg.Section("advance").Key("two_lines").String() // how about continuation lines? -cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4 -``` - -Well, I hate continuation lines, how do I disable that? - -```go -cfg, err := ini.LoadSources(ini.LoadOptions{ - IgnoreContinuation: true, -}, "filename") -``` - -Holy crap! - -Note that single quotes around values will be stripped: - -```ini -foo = "some value" // foo: some value -bar = 'some value' // bar: some value -``` - -That's all? Hmm, no. - -#### Helper methods of working with values - -To get value with given candidates: - -```go -v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"}) -v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75}) -v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30}) -v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30}) -v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9}) -v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9}) -v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3}) -v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339 -``` - -Default value will be presented if value of key is not in candidates you given, and default value does not need be one of candidates. - -To validate value in a given range: - -```go -vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2) -vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20) -vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20) -vals = cfg.Section("").Key("UINT").RangeUint(0, 3, 9) -vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9) -vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime) -vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339 -``` - -##### Auto-split values into a slice - -To use zero value of type for invalid inputs: - -```go -// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] -// Input: how, 2.2, are, you -> [0.0 2.2 0.0 0.0] -vals = cfg.Section("").Key("STRINGS").Strings(",") -vals = cfg.Section("").Key("FLOAT64S").Float64s(",") -vals = cfg.Section("").Key("INTS").Ints(",") -vals = cfg.Section("").Key("INT64S").Int64s(",") -vals = cfg.Section("").Key("UINTS").Uints(",") -vals = cfg.Section("").Key("UINT64S").Uint64s(",") -vals = cfg.Section("").Key("TIMES").Times(",") -``` - -To exclude invalid values out of result slice: - -```go -// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] -// Input: how, 2.2, are, you -> [2.2] -vals = cfg.Section("").Key("FLOAT64S").ValidFloat64s(",") -vals = cfg.Section("").Key("INTS").ValidInts(",") -vals = cfg.Section("").Key("INT64S").ValidInt64s(",") -vals = cfg.Section("").Key("UINTS").ValidUints(",") -vals = cfg.Section("").Key("UINT64S").ValidUint64s(",") -vals = cfg.Section("").Key("TIMES").ValidTimes(",") -``` - -Or to return nothing but error when have invalid inputs: - -```go -// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] -// Input: how, 2.2, are, you -> error -vals = cfg.Section("").Key("FLOAT64S").StrictFloat64s(",") -vals = cfg.Section("").Key("INTS").StrictInts(",") -vals = cfg.Section("").Key("INT64S").StrictInt64s(",") -vals = cfg.Section("").Key("UINTS").StrictUints(",") -vals = cfg.Section("").Key("UINT64S").StrictUint64s(",") -vals = cfg.Section("").Key("TIMES").StrictTimes(",") -``` - -### Save your configuration - -Finally, it's time to save your configuration to somewhere. - -A typical way to save configuration is writing it to a file: - -```go -// ... -err = cfg.SaveTo("my.ini") -err = cfg.SaveToIndent("my.ini", "\t") -``` - -Another way to save is writing to a `io.Writer` interface: - -```go -// ... -cfg.WriteTo(writer) -cfg.WriteToIndent(writer, "\t") -``` - -By default, spaces are used to align "=" sign between key and values, to disable that: - -```go -ini.PrettyFormat = false -``` - -## Advanced Usage - -### Recursive Values - -For all value of keys, there is a special syntax `%()s`, where `` is the key name in same section or default section, and `%()s` will be replaced by corresponding value(empty string if key not found). You can use this syntax at most 99 level of recursions. - -```ini -NAME = ini - -[author] -NAME = Unknwon -GITHUB = https://github.com/%(NAME)s - -[package] -FULL_NAME = github.com/go-ini/%(NAME)s -``` - -```go -cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon -cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini -``` - -### Parent-child Sections - -You can use `.` in section name to indicate parent-child relationship between two or more sections. If the key not found in the child section, library will try again on its parent section until there is no parent section. - -```ini -NAME = ini -VERSION = v1 -IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s - -[package] -CLONE_URL = https://%(IMPORT_PATH)s - -[package.sub] -``` - -```go -cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1 -``` - -#### Retrieve parent keys available to a child section - -```go -cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"] -``` - -### Unparseable Sections - -Sometimes, you have sections that do not contain key-value pairs but raw content, to handle such case, you can use `LoadOptions.UnparsableSections`: - -```go -cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] -<1> This slide has the fuel listed in the wrong units `)) - -body := cfg.Section("COMMENTS").Body() - -/* --- start --- -<1> This slide has the fuel listed in the wrong units ------- end --- */ -``` - -### Auto-increment Key Names - -If key name is `-` in data source, then it would be seen as special syntax for auto-increment key name start from 1, and every section is independent on counter. - -```ini -[features] --: Support read/write comments of keys and sections --: Support auto-increment of key names --: Support load multiple files to overwrite key values -``` - -```go -cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"} -``` - -### Map To Struct - -Want more objective way to play with INI? Cool. - -```ini -Name = Unknwon -age = 21 -Male = true -Born = 1993-01-01T20:17:05Z - -[Note] -Content = Hi is a good man! -Cities = HangZhou, Boston -``` - -```go -type Note struct { - Content string - Cities []string -} - -type Person struct { - Name string - Age int `ini:"age"` - Male bool - Born time.Time - Note - Created time.Time `ini:"-"` -} - -func main() { - cfg, err := ini.Load("path/to/ini") - // ... - p := new(Person) - err = cfg.MapTo(p) - // ... - - // Things can be simpler. - err = ini.MapTo(p, "path/to/ini") - // ... - - // Just map a section? Fine. - n := new(Note) - err = cfg.Section("Note").MapTo(n) - // ... -} -``` - -Can I have default value for field? Absolutely. - -Assign it before you map to struct. It will keep the value as it is if the key is not presented or got wrong type. - -```go -// ... -p := &Person{ - Name: "Joe", -} -// ... -``` - -It's really cool, but what's the point if you can't give me my file back from struct? - -### Reflect From Struct - -Why not? - -```go -type Embeded struct { - Dates []time.Time `delim:"|"` - Places []string `ini:"places,omitempty"` - None []int `ini:",omitempty"` -} - -type Author struct { - Name string `ini:"NAME"` - Male bool - Age int - GPA float64 - NeverMind string `ini:"-"` - *Embeded -} - -func main() { - a := &Author{"Unknwon", true, 21, 2.8, "", - &Embeded{ - []time.Time{time.Now(), time.Now()}, - []string{"HangZhou", "Boston"}, - []int{}, - }} - cfg := ini.Empty() - err = ini.ReflectFrom(cfg, a) - // ... -} -``` - -So, what do I get? - -```ini -NAME = Unknwon -Male = true -Age = 21 -GPA = 2.8 - -[Embeded] -Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00 -places = HangZhou,Boston -``` - -#### Name Mapper - -To save your time and make your code cleaner, this library supports [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) between struct field and actual section and key name. - -There are 2 built-in name mappers: - -- `AllCapsUnderscore`: it converts to format `ALL_CAPS_UNDERSCORE` then match section or key. -- `TitleUnderscore`: it converts to format `title_underscore` then match section or key. - -To use them: - -```go -type Info struct { - PackageName string -} - -func main() { - err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini")) - // ... - - cfg, err := ini.Load([]byte("PACKAGE_NAME=ini")) - // ... - info := new(Info) - cfg.NameMapper = ini.AllCapsUnderscore - err = cfg.MapTo(info) - // ... -} -``` - -Same rules of name mapper apply to `ini.ReflectFromWithMapper` function. - -#### Value Mapper - -To expand values (e.g. from environment variables), you can use the `ValueMapper` to transform values: - -```go -type Env struct { - Foo string `ini:"foo"` -} - -func main() { - cfg, err := ini.Load([]byte("[env]\nfoo = ${MY_VAR}\n") - cfg.ValueMapper = os.ExpandEnv - // ... - env := &Env{} - err = cfg.Section("env").MapTo(env) -} -``` - -This would set the value of `env.Foo` to the value of the environment variable `MY_VAR`. - -#### Other Notes On Map/Reflect - -Any embedded struct is treated as a section by default, and there is no automatic parent-child relations in map/reflect feature: - -```go -type Child struct { - Age string -} - -type Parent struct { - Name string - Child -} - -type Config struct { - City string - Parent -} -``` - -Example configuration: - -```ini -City = Boston - -[Parent] -Name = Unknwon - -[Child] -Age = 21 -``` - -What if, yes, I'm paranoid, I want embedded struct to be in the same section. Well, all roads lead to Rome. - -```go -type Child struct { - Age string -} - -type Parent struct { - Name string - Child `ini:"Parent"` -} - -type Config struct { - City string - Parent -} -``` - -Example configuration: - -```ini -City = Boston - -[Parent] -Name = Unknwon -Age = 21 -``` - -## Getting Help - -- [API Documentation](https://gowalker.org/gopkg.in/ini.v1) -- [File An Issue](https://github.com/go-ini/ini/issues/new) - -## FAQs - -### What does `BlockMode` field do? - -By default, library lets you read and write values so we need a locker to make sure your data is safe. But in cases that you are very sure about only reading data through the library, you can set `cfg.BlockMode = false` to speed up read operations about **50-70%** faster. - -### Why another INI library? - -Many people are using my another INI library [goconfig](https://github.com/Unknwon/goconfig), so the reason for this one is I would like to make more Go style code. Also when you set `cfg.BlockMode = false`, this one is about **10-30%** faster. - -To make those changes I have to confirm API broken, so it's safer to keep it in another place and start using `gopkg.in` to version my package at this time.(PS: shorter import path) - -## License - -This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text. diff --git a/vendor/github.com/go-ini/ini/README_ZH.md b/vendor/github.com/go-ini/ini/README_ZH.md deleted file mode 100644 index 163432db9affe..0000000000000 --- a/vendor/github.com/go-ini/ini/README_ZH.md +++ /dev/null @@ -1,727 +0,0 @@ -本包提供了 Go 语言中读写 INI 文件的功能。 - -## 功能特性 - -- 支持覆盖加载多个数据源(`[]byte`、文件和 `io.ReadCloser`) -- 支持递归读取键值 -- 支持读取父子分区 -- 支持读取自增键名 -- 支持读取多行的键值 -- 支持大量辅助方法 -- 支持在读取时直接转换为 Go 语言类型 -- 支持读取和 **写入** 分区和键的注释 -- 轻松操作分区、键值和注释 -- 在保存文件时分区和键值会保持原有的顺序 - -## 下载安装 - -使用一个特定版本: - - go get gopkg.in/ini.v1 - -使用最新版: - - go get github.com/go-ini/ini - -如需更新请添加 `-u` 选项。 - -### 测试安装 - -如果您想要在自己的机器上运行测试,请使用 `-t` 标记: - - go get -t gopkg.in/ini.v1 - -如需更新请添加 `-u` 选项。 - -## 开始使用 - -### 从数据源加载 - -一个 **数据源** 可以是 `[]byte` 类型的原始数据,`string` 类型的文件路径或 `io.ReadCloser`。您可以加载 **任意多个** 数据源。如果您传递其它类型的数据源,则会直接返回错误。 - -```go -cfg, err := ini.Load([]byte("raw data"), "filename", ioutil.NopCloser(bytes.NewReader([]byte("some other data")))) -``` - -或者从一个空白的文件开始: - -```go -cfg := ini.Empty() -``` - -当您在一开始无法决定需要加载哪些数据源时,仍可以使用 **Append()** 在需要的时候加载它们。 - -```go -err := cfg.Append("other file", []byte("other raw data")) -``` - -当您想要加载一系列文件,但是不能够确定其中哪些文件是不存在的,可以通过调用函数 `LooseLoad` 来忽略它们(`Load` 会因为文件不存在而返回错误): - -```go -cfg, err := ini.LooseLoad("filename", "filename_404") -``` - -更牛逼的是,当那些之前不存在的文件在重新调用 `Reload` 方法的时候突然出现了,那么它们会被正常加载。 - -#### 忽略键名的大小写 - -有时候分区和键的名称大小写混合非常烦人,这个时候就可以通过 `InsensitiveLoad` 将所有分区和键名在读取里强制转换为小写: - -```go -cfg, err := ini.InsensitiveLoad("filename") -//... - -// sec1 和 sec2 指向同一个分区对象 -sec1, err := cfg.GetSection("Section") -sec2, err := cfg.GetSection("SecTIOn") - -// key1 和 key2 指向同一个键对象 -key1, err := cfg.GetKey("Key") -key2, err := cfg.GetKey("KeY") -``` - -#### 类似 MySQL 配置中的布尔值键 - -MySQL 的配置文件中会出现没有具体值的布尔类型的键: - -```ini -[mysqld] -... -skip-host-cache -skip-name-resolve -``` - -默认情况下这被认为是缺失值而无法完成解析,但可以通过高级的加载选项对它们进行处理: - -```go -cfg, err := LoadSources(LoadOptions{AllowBooleanKeys: true}, "my.cnf")) -``` - -这些键的值永远为 `true`,且在保存到文件时也只会输出键名。 - -如果您想要通过程序来生成此类键,则可以使用 `NewBooleanKey`: - -```go -key, err := sec.NewBooleanKey("skip-host-cache") -``` - -#### 关于注释 - -下述几种情况的内容将被视为注释: - -1. 所有以 `#` 或 `;` 开头的行 -2. 所有在 `#` 或 `;` 之后的内容 -3. 分区标签后的文字 (即 `[分区名]` 之后的内容) - -如果你希望使用包含 `#` 或 `;` 的值,请使用 ``` ` ``` 或 ``` """ ``` 进行包覆。 - -### 操作分区(Section) - -获取指定分区: - -```go -section, err := cfg.GetSection("section name") -``` - -如果您想要获取默认分区,则可以用空字符串代替分区名: - -```go -section, err := cfg.GetSection("") -``` - -当您非常确定某个分区是存在的,可以使用以下简便方法: - -```go -section := cfg.Section("section name") -``` - -如果不小心判断错了,要获取的分区其实是不存在的,那会发生什么呢?没事的,它会自动创建并返回一个对应的分区对象给您。 - -创建一个分区: - -```go -err := cfg.NewSection("new section") -``` - -获取所有分区对象或名称: - -```go -sections := cfg.Sections() -names := cfg.SectionStrings() -``` - -### 操作键(Key) - -获取某个分区下的键: - -```go -key, err := cfg.Section("").GetKey("key name") -``` - -和分区一样,您也可以直接获取键而忽略错误处理: - -```go -key := cfg.Section("").Key("key name") -``` - -判断某个键是否存在: - -```go -yes := cfg.Section("").HasKey("key name") -``` - -创建一个新的键: - -```go -err := cfg.Section("").NewKey("name", "value") -``` - -获取分区下的所有键或键名: - -```go -keys := cfg.Section("").Keys() -names := cfg.Section("").KeyStrings() -``` - -获取分区下的所有键值对的克隆: - -```go -hash := cfg.Section("").KeysHash() -``` - -### 操作键值(Value) - -获取一个类型为字符串(string)的值: - -```go -val := cfg.Section("").Key("key name").String() -``` - -获取值的同时通过自定义函数进行处理验证: - -```go -val := cfg.Section("").Key("key name").Validate(func(in string) string { - if len(in) == 0 { - return "default" - } - return in -}) -``` - -如果您不需要任何对值的自动转变功能(例如递归读取),可以直接获取原值(这种方式性能最佳): - -```go -val := cfg.Section("").Key("key name").Value() -``` - -判断某个原值是否存在: - -```go -yes := cfg.Section("").HasValue("test value") -``` - -获取其它类型的值: - -```go -// 布尔值的规则: -// true 当值为:1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On -// false 当值为:0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off -v, err = cfg.Section("").Key("BOOL").Bool() -v, err = cfg.Section("").Key("FLOAT64").Float64() -v, err = cfg.Section("").Key("INT").Int() -v, err = cfg.Section("").Key("INT64").Int64() -v, err = cfg.Section("").Key("UINT").Uint() -v, err = cfg.Section("").Key("UINT64").Uint64() -v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339) -v, err = cfg.Section("").Key("TIME").Time() // RFC3339 - -v = cfg.Section("").Key("BOOL").MustBool() -v = cfg.Section("").Key("FLOAT64").MustFloat64() -v = cfg.Section("").Key("INT").MustInt() -v = cfg.Section("").Key("INT64").MustInt64() -v = cfg.Section("").Key("UINT").MustUint() -v = cfg.Section("").Key("UINT64").MustUint64() -v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339) -v = cfg.Section("").Key("TIME").MustTime() // RFC3339 - -// 由 Must 开头的方法名允许接收一个相同类型的参数来作为默认值, -// 当键不存在或者转换失败时,则会直接返回该默认值。 -// 但是,MustString 方法必须传递一个默认值。 - -v = cfg.Seciont("").Key("String").MustString("default") -v = cfg.Section("").Key("BOOL").MustBool(true) -v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25) -v = cfg.Section("").Key("INT").MustInt(10) -v = cfg.Section("").Key("INT64").MustInt64(99) -v = cfg.Section("").Key("UINT").MustUint(3) -v = cfg.Section("").Key("UINT64").MustUint64(6) -v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now()) -v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339 -``` - -如果我的值有好多行怎么办? - -```ini -[advance] -ADDRESS = """404 road, -NotFound, State, 5000 -Earth""" -``` - -嗯哼?小 case! - -```go -cfg.Section("advance").Key("ADDRESS").String() - -/* --- start --- -404 road, -NotFound, State, 5000 -Earth ------- end --- */ -``` - -赞爆了!那要是我属于一行的内容写不下想要写到第二行怎么办? - -```ini -[advance] -two_lines = how about \ - continuation lines? -lots_of_lines = 1 \ - 2 \ - 3 \ - 4 -``` - -简直是小菜一碟! - -```go -cfg.Section("advance").Key("two_lines").String() // how about continuation lines? -cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4 -``` - -可是我有时候觉得两行连在一起特别没劲,怎么才能不自动连接两行呢? - -```go -cfg, err := ini.LoadSources(ini.LoadOptions{ - IgnoreContinuation: true, -}, "filename") -``` - -哇靠给力啊! - -需要注意的是,值两侧的单引号会被自动剔除: - -```ini -foo = "some value" // foo: some value -bar = 'some value' // bar: some value -``` - -这就是全部了?哈哈,当然不是。 - -#### 操作键值的辅助方法 - -获取键值时设定候选值: - -```go -v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"}) -v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75}) -v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30}) -v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30}) -v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9}) -v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9}) -v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3}) -v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339 -``` - -如果获取到的值不是候选值的任意一个,则会返回默认值,而默认值不需要是候选值中的一员。 - -验证获取的值是否在指定范围内: - -```go -vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2) -vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20) -vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20) -vals = cfg.Section("").Key("UINT").RangeUint(0, 3, 9) -vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9) -vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime) -vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339 -``` - -##### 自动分割键值到切片(slice) - -当存在无效输入时,使用零值代替: - -```go -// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] -// Input: how, 2.2, are, you -> [0.0 2.2 0.0 0.0] -vals = cfg.Section("").Key("STRINGS").Strings(",") -vals = cfg.Section("").Key("FLOAT64S").Float64s(",") -vals = cfg.Section("").Key("INTS").Ints(",") -vals = cfg.Section("").Key("INT64S").Int64s(",") -vals = cfg.Section("").Key("UINTS").Uints(",") -vals = cfg.Section("").Key("UINT64S").Uint64s(",") -vals = cfg.Section("").Key("TIMES").Times(",") -``` - -从结果切片中剔除无效输入: - -```go -// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] -// Input: how, 2.2, are, you -> [2.2] -vals = cfg.Section("").Key("FLOAT64S").ValidFloat64s(",") -vals = cfg.Section("").Key("INTS").ValidInts(",") -vals = cfg.Section("").Key("INT64S").ValidInt64s(",") -vals = cfg.Section("").Key("UINTS").ValidUints(",") -vals = cfg.Section("").Key("UINT64S").ValidUint64s(",") -vals = cfg.Section("").Key("TIMES").ValidTimes(",") -``` - -当存在无效输入时,直接返回错误: - -```go -// Input: 1.1, 2.2, 3.3, 4.4 -> [1.1 2.2 3.3 4.4] -// Input: how, 2.2, are, you -> error -vals = cfg.Section("").Key("FLOAT64S").StrictFloat64s(",") -vals = cfg.Section("").Key("INTS").StrictInts(",") -vals = cfg.Section("").Key("INT64S").StrictInt64s(",") -vals = cfg.Section("").Key("UINTS").StrictUints(",") -vals = cfg.Section("").Key("UINT64S").StrictUint64s(",") -vals = cfg.Section("").Key("TIMES").StrictTimes(",") -``` - -### 保存配置 - -终于到了这个时刻,是时候保存一下配置了。 - -比较原始的做法是输出配置到某个文件: - -```go -// ... -err = cfg.SaveTo("my.ini") -err = cfg.SaveToIndent("my.ini", "\t") -``` - -另一个比较高级的做法是写入到任何实现 `io.Writer` 接口的对象中: - -```go -// ... -cfg.WriteTo(writer) -cfg.WriteToIndent(writer, "\t") -``` - -默认情况下,空格将被用于对齐键值之间的等号以美化输出结果,以下代码可以禁用该功能: - -```go -ini.PrettyFormat = false -``` - -## 高级用法 - -### 递归读取键值 - -在获取所有键值的过程中,特殊语法 `%()s` 会被应用,其中 `` 可以是相同分区或者默认分区下的键名。字符串 `%()s` 会被相应的键值所替代,如果指定的键不存在,则会用空字符串替代。您可以最多使用 99 层的递归嵌套。 - -```ini -NAME = ini - -[author] -NAME = Unknwon -GITHUB = https://github.com/%(NAME)s - -[package] -FULL_NAME = github.com/go-ini/%(NAME)s -``` - -```go -cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon -cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini -``` - -### 读取父子分区 - -您可以在分区名称中使用 `.` 来表示两个或多个分区之间的父子关系。如果某个键在子分区中不存在,则会去它的父分区中再次寻找,直到没有父分区为止。 - -```ini -NAME = ini -VERSION = v1 -IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s - -[package] -CLONE_URL = https://%(IMPORT_PATH)s - -[package.sub] -``` - -```go -cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1 -``` - -#### 获取上级父分区下的所有键名 - -```go -cfg.Section("package.sub").ParentKeys() // ["CLONE_URL"] -``` - -### 无法解析的分区 - -如果遇到一些比较特殊的分区,它们不包含常见的键值对,而是没有固定格式的纯文本,则可以使用 `LoadOptions.UnparsableSections` 进行处理: - -```go -cfg, err := LoadSources(LoadOptions{UnparseableSections: []string{"COMMENTS"}}, `[COMMENTS] -<1> This slide has the fuel listed in the wrong units `)) - -body := cfg.Section("COMMENTS").Body() - -/* --- start --- -<1> This slide has the fuel listed in the wrong units ------- end --- */ -``` - -### 读取自增键名 - -如果数据源中的键名为 `-`,则认为该键使用了自增键名的特殊语法。计数器从 1 开始,并且分区之间是相互独立的。 - -```ini -[features] --: Support read/write comments of keys and sections --: Support auto-increment of key names --: Support load multiple files to overwrite key values -``` - -```go -cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"} -``` - -### 映射到结构 - -想要使用更加面向对象的方式玩转 INI 吗?好主意。 - -```ini -Name = Unknwon -age = 21 -Male = true -Born = 1993-01-01T20:17:05Z - -[Note] -Content = Hi is a good man! -Cities = HangZhou, Boston -``` - -```go -type Note struct { - Content string - Cities []string -} - -type Person struct { - Name string - Age int `ini:"age"` - Male bool - Born time.Time - Note - Created time.Time `ini:"-"` -} - -func main() { - cfg, err := ini.Load("path/to/ini") - // ... - p := new(Person) - err = cfg.MapTo(p) - // ... - - // 一切竟可以如此的简单。 - err = ini.MapTo(p, "path/to/ini") - // ... - - // 嗯哼?只需要映射一个分区吗? - n := new(Note) - err = cfg.Section("Note").MapTo(n) - // ... -} -``` - -结构的字段怎么设置默认值呢?很简单,只要在映射之前对指定字段进行赋值就可以了。如果键未找到或者类型错误,该值不会发生改变。 - -```go -// ... -p := &Person{ - Name: "Joe", -} -// ... -``` - -这样玩 INI 真的好酷啊!然而,如果不能还给我原来的配置文件,有什么卵用? - -### 从结构反射 - -可是,我有说不能吗? - -```go -type Embeded struct { - Dates []time.Time `delim:"|"` - Places []string `ini:"places,omitempty"` - None []int `ini:",omitempty"` -} - -type Author struct { - Name string `ini:"NAME"` - Male bool - Age int - GPA float64 - NeverMind string `ini:"-"` - *Embeded -} - -func main() { - a := &Author{"Unknwon", true, 21, 2.8, "", - &Embeded{ - []time.Time{time.Now(), time.Now()}, - []string{"HangZhou", "Boston"}, - []int{}, - }} - cfg := ini.Empty() - err = ini.ReflectFrom(cfg, a) - // ... -} -``` - -瞧瞧,奇迹发生了。 - -```ini -NAME = Unknwon -Male = true -Age = 21 -GPA = 2.8 - -[Embeded] -Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00 -places = HangZhou,Boston -``` - -#### 名称映射器(Name Mapper) - -为了节省您的时间并简化代码,本库支持类型为 [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) 的名称映射器,该映射器负责结构字段名与分区名和键名之间的映射。 - -目前有 2 款内置的映射器: - -- `AllCapsUnderscore`:该映射器将字段名转换至格式 `ALL_CAPS_UNDERSCORE` 后再去匹配分区名和键名。 -- `TitleUnderscore`:该映射器将字段名转换至格式 `title_underscore` 后再去匹配分区名和键名。 - -使用方法: - -```go -type Info struct{ - PackageName string -} - -func main() { - err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("package_name=ini")) - // ... - - cfg, err := ini.Load([]byte("PACKAGE_NAME=ini")) - // ... - info := new(Info) - cfg.NameMapper = ini.AllCapsUnderscore - err = cfg.MapTo(info) - // ... -} -``` - -使用函数 `ini.ReflectFromWithMapper` 时也可应用相同的规则。 - -#### 值映射器(Value Mapper) - -值映射器允许使用一个自定义函数自动展开值的具体内容,例如:运行时获取环境变量: - -```go -type Env struct { - Foo string `ini:"foo"` -} - -func main() { - cfg, err := ini.Load([]byte("[env]\nfoo = ${MY_VAR}\n") - cfg.ValueMapper = os.ExpandEnv - // ... - env := &Env{} - err = cfg.Section("env").MapTo(env) -} -``` - -本例中,`env.Foo` 将会是运行时所获取到环境变量 `MY_VAR` 的值。 - -#### 映射/反射的其它说明 - -任何嵌入的结构都会被默认认作一个不同的分区,并且不会自动产生所谓的父子分区关联: - -```go -type Child struct { - Age string -} - -type Parent struct { - Name string - Child -} - -type Config struct { - City string - Parent -} -``` - -示例配置文件: - -```ini -City = Boston - -[Parent] -Name = Unknwon - -[Child] -Age = 21 -``` - -很好,但是,我就是要嵌入结构也在同一个分区。好吧,你爹是李刚! - -```go -type Child struct { - Age string -} - -type Parent struct { - Name string - Child `ini:"Parent"` -} - -type Config struct { - City string - Parent -} -``` - -示例配置文件: - -```ini -City = Boston - -[Parent] -Name = Unknwon -Age = 21 -``` - -## 获取帮助 - -- [API 文档](https://gowalker.org/gopkg.in/ini.v1) -- [创建工单](https://github.com/go-ini/ini/issues/new) - -## 常见问题 - -### 字段 `BlockMode` 是什么? - -默认情况下,本库会在您进行读写操作时采用锁机制来确保数据时间。但在某些情况下,您非常确定只进行读操作。此时,您可以通过设置 `cfg.BlockMode = false` 来将读操作提升大约 **50-70%** 的性能。 - -### 为什么要写另一个 INI 解析库? - -许多人都在使用我的 [goconfig](https://github.com/Unknwon/goconfig) 来完成对 INI 文件的操作,但我希望使用更加 Go 风格的代码。并且当您设置 `cfg.BlockMode = false` 时,会有大约 **10-30%** 的性能提升。 - -为了做出这些改变,我必须对 API 进行破坏,所以新开一个仓库是最安全的做法。除此之外,本库直接使用 `gopkg.in` 来进行版本化发布。(其实真相是导入路径更短了) diff --git a/vendor/github.com/go-ini/ini/error.go b/vendor/github.com/go-ini/ini/error.go deleted file mode 100644 index 80afe7431584a..0000000000000 --- a/vendor/github.com/go-ini/ini/error.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2016 Unknwon -// -// 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. - -package ini - -import ( - "fmt" -) - -type ErrDelimiterNotFound struct { - Line string -} - -func IsErrDelimiterNotFound(err error) bool { - _, ok := err.(ErrDelimiterNotFound) - return ok -} - -func (err ErrDelimiterNotFound) Error() string { - return fmt.Sprintf("key-value delimiter not found: %s", err.Line) -} diff --git a/vendor/github.com/go-ini/ini/ini.go b/vendor/github.com/go-ini/ini/ini.go deleted file mode 100644 index 68d73aa750c7f..0000000000000 --- a/vendor/github.com/go-ini/ini/ini.go +++ /dev/null @@ -1,549 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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. - -// Package ini provides INI file read and write functionality in Go. -package ini - -import ( - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "regexp" - "runtime" - "strconv" - "strings" - "sync" - "time" -) - -const ( - // Name for default section. You can use this constant or the string literal. - // In most of cases, an empty string is all you need to access the section. - DEFAULT_SECTION = "DEFAULT" - - // Maximum allowed depth when recursively substituing variable names. - _DEPTH_VALUES = 99 - _VERSION = "1.25.4" -) - -// Version returns current package version literal. -func Version() string { - return _VERSION -} - -var ( - // Delimiter to determine or compose a new line. - // This variable will be changed to "\r\n" automatically on Windows - // at package init time. - LineBreak = "\n" - - // Variable regexp pattern: %(variable)s - varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`) - - // Indicate whether to align "=" sign with spaces to produce pretty output - // or reduce all possible spaces for compact format. - PrettyFormat = true - - // Explicitly write DEFAULT section header - DefaultHeader = false -) - -func init() { - if runtime.GOOS == "windows" { - LineBreak = "\r\n" - } -} - -func inSlice(str string, s []string) bool { - for _, v := range s { - if str == v { - return true - } - } - return false -} - -// dataSource is an interface that returns object which can be read and closed. -type dataSource interface { - ReadCloser() (io.ReadCloser, error) -} - -// sourceFile represents an object that contains content on the local file system. -type sourceFile struct { - name string -} - -func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) { - return os.Open(s.name) -} - -type bytesReadCloser struct { - reader io.Reader -} - -func (rc *bytesReadCloser) Read(p []byte) (n int, err error) { - return rc.reader.Read(p) -} - -func (rc *bytesReadCloser) Close() error { - return nil -} - -// sourceData represents an object that contains content in memory. -type sourceData struct { - data []byte -} - -func (s *sourceData) ReadCloser() (io.ReadCloser, error) { - return ioutil.NopCloser(bytes.NewReader(s.data)), nil -} - -// sourceReadCloser represents an input stream with Close method. -type sourceReadCloser struct { - reader io.ReadCloser -} - -func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) { - return s.reader, nil -} - -// File represents a combination of a or more INI file(s) in memory. -type File struct { - // Should make things safe, but sometimes doesn't matter. - BlockMode bool - // Make sure data is safe in multiple goroutines. - lock sync.RWMutex - - // Allow combination of multiple data sources. - dataSources []dataSource - // Actual data is stored here. - sections map[string]*Section - - // To keep data in order. - sectionList []string - - options LoadOptions - - NameMapper - ValueMapper -} - -// newFile initializes File object with given data sources. -func newFile(dataSources []dataSource, opts LoadOptions) *File { - return &File{ - BlockMode: true, - dataSources: dataSources, - sections: make(map[string]*Section), - sectionList: make([]string, 0, 10), - options: opts, - } -} - -func parseDataSource(source interface{}) (dataSource, error) { - switch s := source.(type) { - case string: - return sourceFile{s}, nil - case []byte: - return &sourceData{s}, nil - case io.ReadCloser: - return &sourceReadCloser{s}, nil - default: - return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s) - } -} - -type LoadOptions struct { - // Loose indicates whether the parser should ignore nonexistent files or return error. - Loose bool - // Insensitive indicates whether the parser forces all section and key names to lowercase. - Insensitive bool - // IgnoreContinuation indicates whether to ignore continuation lines while parsing. - IgnoreContinuation bool - // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing. - // This type of keys are mostly used in my.cnf. - AllowBooleanKeys bool - // AllowShadows indicates whether to keep track of keys with same name under same section. - AllowShadows bool - // Some INI formats allow group blocks that store a block of raw content that doesn't otherwise - // conform to key/value pairs. Specify the names of those blocks here. - UnparseableSections []string -} - -func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) { - sources := make([]dataSource, len(others)+1) - sources[0], err = parseDataSource(source) - if err != nil { - return nil, err - } - for i := range others { - sources[i+1], err = parseDataSource(others[i]) - if err != nil { - return nil, err - } - } - f := newFile(sources, opts) - if err = f.Reload(); err != nil { - return nil, err - } - return f, nil -} - -// Load loads and parses from INI data sources. -// Arguments can be mixed of file name with string type, or raw data in []byte. -// It will return error if list contains nonexistent files. -func Load(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{}, source, others...) -} - -// LooseLoad has exactly same functionality as Load function -// except it ignores nonexistent files instead of returning error. -func LooseLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{Loose: true}, source, others...) -} - -// InsensitiveLoad has exactly same functionality as Load function -// except it forces all section and key names to be lowercased. -func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{Insensitive: true}, source, others...) -} - -// InsensitiveLoad has exactly same functionality as Load function -// except it allows have shadow keys. -func ShadowLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{AllowShadows: true}, source, others...) -} - -// Empty returns an empty file object. -func Empty() *File { - // Ignore error here, we sure our data is good. - f, _ := Load([]byte("")) - return f -} - -// NewSection creates a new section. -func (f *File) NewSection(name string) (*Section, error) { - if len(name) == 0 { - return nil, errors.New("error creating new section: empty section name") - } else if f.options.Insensitive && name != DEFAULT_SECTION { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if inSlice(name, f.sectionList) { - return f.sections[name], nil - } - - f.sectionList = append(f.sectionList, name) - f.sections[name] = newSection(f, name) - return f.sections[name], nil -} - -// NewRawSection creates a new section with an unparseable body. -func (f *File) NewRawSection(name, body string) (*Section, error) { - section, err := f.NewSection(name) - if err != nil { - return nil, err - } - - section.isRawSection = true - section.rawBody = body - return section, nil -} - -// NewSections creates a list of sections. -func (f *File) NewSections(names ...string) (err error) { - for _, name := range names { - if _, err = f.NewSection(name); err != nil { - return err - } - } - return nil -} - -// GetSection returns section by given name. -func (f *File) GetSection(name string) (*Section, error) { - if len(name) == 0 { - name = DEFAULT_SECTION - } else if f.options.Insensitive { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.RLock() - defer f.lock.RUnlock() - } - - sec := f.sections[name] - if sec == nil { - return nil, fmt.Errorf("section '%s' does not exist", name) - } - return sec, nil -} - -// Section assumes named section exists and returns a zero-value when not. -func (f *File) Section(name string) *Section { - sec, err := f.GetSection(name) - if err != nil { - // Note: It's OK here because the only possible error is empty section name, - // but if it's empty, this piece of code won't be executed. - sec, _ = f.NewSection(name) - return sec - } - return sec -} - -// Section returns list of Section. -func (f *File) Sections() []*Section { - sections := make([]*Section, len(f.sectionList)) - for i := range f.sectionList { - sections[i] = f.Section(f.sectionList[i]) - } - return sections -} - -// SectionStrings returns list of section names. -func (f *File) SectionStrings() []string { - list := make([]string, len(f.sectionList)) - copy(list, f.sectionList) - return list -} - -// DeleteSection deletes a section. -func (f *File) DeleteSection(name string) { - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if len(name) == 0 { - name = DEFAULT_SECTION - } - - for i, s := range f.sectionList { - if s == name { - f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...) - delete(f.sections, name) - return - } - } -} - -func (f *File) reload(s dataSource) error { - r, err := s.ReadCloser() - if err != nil { - return err - } - defer r.Close() - - return f.parse(r) -} - -// Reload reloads and parses all data sources. -func (f *File) Reload() (err error) { - for _, s := range f.dataSources { - if err = f.reload(s); err != nil { - // In loose mode, we create an empty default section for nonexistent files. - if os.IsNotExist(err) && f.options.Loose { - f.parse(bytes.NewBuffer(nil)) - continue - } - return err - } - } - return nil -} - -// Append appends one or more data sources and reloads automatically. -func (f *File) Append(source interface{}, others ...interface{}) error { - ds, err := parseDataSource(source) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - for _, s := range others { - ds, err = parseDataSource(s) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - } - return f.Reload() -} - -// WriteToIndent writes content into io.Writer with given indention. -// If PrettyFormat has been set to be true, -// it will align "=" sign with spaces under each section. -func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) { - equalSign := "=" - if PrettyFormat { - equalSign = " = " - } - - // Use buffer to make sure target is safe until finish encoding. - buf := bytes.NewBuffer(nil) - for i, sname := range f.sectionList { - sec := f.Section(sname) - if len(sec.Comment) > 0 { - if sec.Comment[0] != '#' && sec.Comment[0] != ';' { - sec.Comment = "; " + sec.Comment - } - if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil { - return 0, err - } - } - - if i > 0 || DefaultHeader { - if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil { - return 0, err - } - } else { - // Write nothing if default section is empty - if len(sec.keyList) == 0 { - continue - } - } - - if sec.isRawSection { - if _, err = buf.WriteString(sec.rawBody); err != nil { - return 0, err - } - continue - } - - // Count and generate alignment length and buffer spaces using the - // longest key. Keys may be modifed if they contain certain characters so - // we need to take that into account in our calculation. - alignLength := 0 - if PrettyFormat { - for _, kname := range sec.keyList { - keyLength := len(kname) - // First case will surround key by ` and second by """ - if strings.ContainsAny(kname, "\"=:") { - keyLength += 2 - } else if strings.Contains(kname, "`") { - keyLength += 6 - } - - if keyLength > alignLength { - alignLength = keyLength - } - } - } - alignSpaces := bytes.Repeat([]byte(" "), alignLength) - - KEY_LIST: - for _, kname := range sec.keyList { - key := sec.Key(kname) - if len(key.Comment) > 0 { - if len(indent) > 0 && sname != DEFAULT_SECTION { - buf.WriteString(indent) - } - if key.Comment[0] != '#' && key.Comment[0] != ';' { - key.Comment = "; " + key.Comment - } - if _, err = buf.WriteString(key.Comment + LineBreak); err != nil { - return 0, err - } - } - - if len(indent) > 0 && sname != DEFAULT_SECTION { - buf.WriteString(indent) - } - - switch { - case key.isAutoIncrement: - kname = "-" - case strings.ContainsAny(kname, "\"=:"): - kname = "`" + kname + "`" - case strings.Contains(kname, "`"): - kname = `"""` + kname + `"""` - } - - for _, val := range key.ValueWithShadows() { - if _, err = buf.WriteString(kname); err != nil { - return 0, err - } - - if key.isBooleanType { - if kname != sec.keyList[len(sec.keyList)-1] { - buf.WriteString(LineBreak) - } - continue KEY_LIST - } - - // Write out alignment spaces before "=" sign - if PrettyFormat { - buf.Write(alignSpaces[:alignLength-len(kname)]) - } - - // In case key value contains "\n", "`", "\"", "#" or ";" - if strings.ContainsAny(val, "\n`") { - val = `"""` + val + `"""` - } else if strings.ContainsAny(val, "#;") { - val = "`" + val + "`" - } - if _, err = buf.WriteString(equalSign + val + LineBreak); err != nil { - return 0, err - } - } - } - - // Put a line between sections - if _, err = buf.WriteString(LineBreak); err != nil { - return 0, err - } - } - - return buf.WriteTo(w) -} - -// WriteTo writes file content into io.Writer. -func (f *File) WriteTo(w io.Writer) (int64, error) { - return f.WriteToIndent(w, "") -} - -// SaveToIndent writes content to file system with given value indention. -func (f *File) SaveToIndent(filename, indent string) error { - // Note: Because we are truncating with os.Create, - // so it's safer to save to a temporary file location and rename afte done. - tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp" - defer os.Remove(tmpPath) - - fw, err := os.Create(tmpPath) - if err != nil { - return err - } - - if _, err = f.WriteToIndent(fw, indent); err != nil { - fw.Close() - return err - } - fw.Close() - - // Remove old file and rename the new one. - os.Remove(filename) - return os.Rename(tmpPath, filename) -} - -// SaveTo writes content to file system. -func (f *File) SaveTo(filename string) error { - return f.SaveToIndent(filename, "") -} diff --git a/vendor/github.com/go-ini/ini/key.go b/vendor/github.com/go-ini/ini/key.go deleted file mode 100644 index 852696f4c4fa6..0000000000000 --- a/vendor/github.com/go-ini/ini/key.go +++ /dev/null @@ -1,703 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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. - -package ini - -import ( - "errors" - "fmt" - "strconv" - "strings" - "time" -) - -// Key represents a key under a section. -type Key struct { - s *Section - name string - value string - isAutoIncrement bool - isBooleanType bool - - isShadow bool - shadows []*Key - - Comment string -} - -// newKey simply return a key object with given values. -func newKey(s *Section, name, val string) *Key { - return &Key{ - s: s, - name: name, - value: val, - } -} - -func (k *Key) addShadow(val string) error { - if k.isShadow { - return errors.New("cannot add shadow to another shadow key") - } else if k.isAutoIncrement || k.isBooleanType { - return errors.New("cannot add shadow to auto-increment or boolean key") - } - - shadow := newKey(k.s, k.name, val) - shadow.isShadow = true - k.shadows = append(k.shadows, shadow) - return nil -} - -// AddShadow adds a new shadow key to itself. -func (k *Key) AddShadow(val string) error { - if !k.s.f.options.AllowShadows { - return errors.New("shadow key is not allowed") - } - return k.addShadow(val) -} - -// ValueMapper represents a mapping function for values, e.g. os.ExpandEnv -type ValueMapper func(string) string - -// Name returns name of key. -func (k *Key) Name() string { - return k.name -} - -// Value returns raw value of key for performance purpose. -func (k *Key) Value() string { - return k.value -} - -// ValueWithShadows returns raw values of key and its shadows if any. -func (k *Key) ValueWithShadows() []string { - if len(k.shadows) == 0 { - return []string{k.value} - } - vals := make([]string, len(k.shadows)+1) - vals[0] = k.value - for i := range k.shadows { - vals[i+1] = k.shadows[i].value - } - return vals -} - -// transformValue takes a raw value and transforms to its final string. -func (k *Key) transformValue(val string) string { - if k.s.f.ValueMapper != nil { - val = k.s.f.ValueMapper(val) - } - - // Fail-fast if no indicate char found for recursive value - if !strings.Contains(val, "%") { - return val - } - for i := 0; i < _DEPTH_VALUES; i++ { - vr := varPattern.FindString(val) - if len(vr) == 0 { - break - } - - // Take off leading '%(' and trailing ')s'. - noption := strings.TrimLeft(vr, "%(") - noption = strings.TrimRight(noption, ")s") - - // Search in the same section. - nk, err := k.s.GetKey(noption) - if err != nil { - // Search again in default section. - nk, _ = k.s.f.Section("").GetKey(noption) - } - - // Substitute by new value and take off leading '%(' and trailing ')s'. - val = strings.Replace(val, vr, nk.value, -1) - } - return val -} - -// String returns string representation of value. -func (k *Key) String() string { - return k.transformValue(k.value) -} - -// Validate accepts a validate function which can -// return modifed result as key value. -func (k *Key) Validate(fn func(string) string) string { - return fn(k.String()) -} - -// parseBool returns the boolean value represented by the string. -// -// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On, -// 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off. -// Any other value returns an error. -func parseBool(str string) (value bool, err error) { - switch str { - case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "y", "ON", "on", "On": - return true, nil - case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "n", "OFF", "off", "Off": - return false, nil - } - return false, fmt.Errorf("parsing \"%s\": invalid syntax", str) -} - -// Bool returns bool type value. -func (k *Key) Bool() (bool, error) { - return parseBool(k.String()) -} - -// Float64 returns float64 type value. -func (k *Key) Float64() (float64, error) { - return strconv.ParseFloat(k.String(), 64) -} - -// Int returns int type value. -func (k *Key) Int() (int, error) { - return strconv.Atoi(k.String()) -} - -// Int64 returns int64 type value. -func (k *Key) Int64() (int64, error) { - return strconv.ParseInt(k.String(), 10, 64) -} - -// Uint returns uint type valued. -func (k *Key) Uint() (uint, error) { - u, e := strconv.ParseUint(k.String(), 10, 64) - return uint(u), e -} - -// Uint64 returns uint64 type value. -func (k *Key) Uint64() (uint64, error) { - return strconv.ParseUint(k.String(), 10, 64) -} - -// Duration returns time.Duration type value. -func (k *Key) Duration() (time.Duration, error) { - return time.ParseDuration(k.String()) -} - -// TimeFormat parses with given format and returns time.Time type value. -func (k *Key) TimeFormat(format string) (time.Time, error) { - return time.Parse(format, k.String()) -} - -// Time parses with RFC3339 format and returns time.Time type value. -func (k *Key) Time() (time.Time, error) { - return k.TimeFormat(time.RFC3339) -} - -// MustString returns default value if key value is empty. -func (k *Key) MustString(defaultVal string) string { - val := k.String() - if len(val) == 0 { - k.value = defaultVal - return defaultVal - } - return val -} - -// MustBool always returns value without error, -// it returns false if error occurs. -func (k *Key) MustBool(defaultVal ...bool) bool { - val, err := k.Bool() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatBool(defaultVal[0]) - return defaultVal[0] - } - return val -} - -// MustFloat64 always returns value without error, -// it returns 0.0 if error occurs. -func (k *Key) MustFloat64(defaultVal ...float64) float64 { - val, err := k.Float64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatFloat(defaultVal[0], 'f', -1, 64) - return defaultVal[0] - } - return val -} - -// MustInt always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt(defaultVal ...int) int { - val, err := k.Int() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatInt(int64(defaultVal[0]), 10) - return defaultVal[0] - } - return val -} - -// MustInt64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt64(defaultVal ...int64) int64 { - val, err := k.Int64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatInt(defaultVal[0], 10) - return defaultVal[0] - } - return val -} - -// MustUint always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustUint(defaultVal ...uint) uint { - val, err := k.Uint() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatUint(uint64(defaultVal[0]), 10) - return defaultVal[0] - } - return val -} - -// MustUint64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustUint64(defaultVal ...uint64) uint64 { - val, err := k.Uint64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatUint(defaultVal[0], 10) - return defaultVal[0] - } - return val -} - -// MustDuration always returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustDuration(defaultVal ...time.Duration) time.Duration { - val, err := k.Duration() - if len(defaultVal) > 0 && err != nil { - k.value = defaultVal[0].String() - return defaultVal[0] - } - return val -} - -// MustTimeFormat always parses with given format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time { - val, err := k.TimeFormat(format) - if len(defaultVal) > 0 && err != nil { - k.value = defaultVal[0].Format(format) - return defaultVal[0] - } - return val -} - -// MustTime always parses with RFC3339 format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTime(defaultVal ...time.Time) time.Time { - return k.MustTimeFormat(time.RFC3339, defaultVal...) -} - -// In always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) In(defaultVal string, candidates []string) string { - val := k.String() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InFloat64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 { - val := k.MustFloat64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt(defaultVal int, candidates []int) int { - val := k.MustInt() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 { - val := k.MustInt64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InUint always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InUint(defaultVal uint, candidates []uint) uint { - val := k.MustUint() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InUint64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InUint64(defaultVal uint64, candidates []uint64) uint64 { - val := k.MustUint64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTimeFormat always parses with given format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time { - val := k.MustTimeFormat(format) - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTime always parses with RFC3339 format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time { - return k.InTimeFormat(time.RFC3339, defaultVal, candidates) -} - -// RangeFloat64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeFloat64(defaultVal, min, max float64) float64 { - val := k.MustFloat64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt(defaultVal, min, max int) int { - val := k.MustInt() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt64(defaultVal, min, max int64) int64 { - val := k.MustInt64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeTimeFormat checks if value with given format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time { - val := k.MustTimeFormat(format) - if val.Unix() < min.Unix() || val.Unix() > max.Unix() { - return defaultVal - } - return val -} - -// RangeTime checks if value with RFC3339 format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time { - return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max) -} - -// Strings returns list of string divided by given delimiter. -func (k *Key) Strings(delim string) []string { - str := k.String() - if len(str) == 0 { - return []string{} - } - - vals := strings.Split(str, delim) - for i := range vals { - // vals[i] = k.transformValue(strings.TrimSpace(vals[i])) - vals[i] = strings.TrimSpace(vals[i]) - } - return vals -} - -// StringsWithShadows returns list of string divided by given delimiter. -// Shadows will also be appended if any. -func (k *Key) StringsWithShadows(delim string) []string { - vals := k.ValueWithShadows() - results := make([]string, 0, len(vals)*2) - for i := range vals { - if len(vals) == 0 { - continue - } - - results = append(results, strings.Split(vals[i], delim)...) - } - - for i := range results { - results[i] = k.transformValue(strings.TrimSpace(results[i])) - } - return results -} - -// Float64s returns list of float64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Float64s(delim string) []float64 { - vals, _ := k.getFloat64s(delim, true, false) - return vals -} - -// Ints returns list of int divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Ints(delim string) []int { - vals, _ := k.parseInts(k.Strings(delim), true, false) - return vals -} - -// Int64s returns list of int64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Int64s(delim string) []int64 { - vals, _ := k.parseInt64s(k.Strings(delim), true, false) - return vals -} - -// Uints returns list of uint divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Uints(delim string) []uint { - vals, _ := k.getUints(delim, true, false) - return vals -} - -// Uint64s returns list of uint64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Uint64s(delim string) []uint64 { - vals, _ := k.getUint64s(delim, true, false) - return vals -} - -// TimesFormat parses with given format and returns list of time.Time divided by given delimiter. -// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC). -func (k *Key) TimesFormat(format, delim string) []time.Time { - vals, _ := k.getTimesFormat(format, delim, true, false) - return vals -} - -// Times parses with RFC3339 format and returns list of time.Time divided by given delimiter. -// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC). -func (k *Key) Times(delim string) []time.Time { - return k.TimesFormat(time.RFC3339, delim) -} - -// ValidFloat64s returns list of float64 divided by given delimiter. If some value is not float, then -// it will not be included to result list. -func (k *Key) ValidFloat64s(delim string) []float64 { - vals, _ := k.getFloat64s(delim, false, false) - return vals -} - -// ValidInts returns list of int divided by given delimiter. If some value is not integer, then it will -// not be included to result list. -func (k *Key) ValidInts(delim string) []int { - vals, _ := k.parseInts(k.Strings(delim), false, false) - return vals -} - -// ValidInt64s returns list of int64 divided by given delimiter. If some value is not 64-bit integer, -// then it will not be included to result list. -func (k *Key) ValidInt64s(delim string) []int64 { - vals, _ := k.parseInt64s(k.Strings(delim), false, false) - return vals -} - -// ValidUints returns list of uint divided by given delimiter. If some value is not unsigned integer, -// then it will not be included to result list. -func (k *Key) ValidUints(delim string) []uint { - vals, _ := k.getUints(delim, false, false) - return vals -} - -// ValidUint64s returns list of uint64 divided by given delimiter. If some value is not 64-bit unsigned -// integer, then it will not be included to result list. -func (k *Key) ValidUint64s(delim string) []uint64 { - vals, _ := k.getUint64s(delim, false, false) - return vals -} - -// ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter. -func (k *Key) ValidTimesFormat(format, delim string) []time.Time { - vals, _ := k.getTimesFormat(format, delim, false, false) - return vals -} - -// ValidTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter. -func (k *Key) ValidTimes(delim string) []time.Time { - return k.ValidTimesFormat(time.RFC3339, delim) -} - -// StrictFloat64s returns list of float64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictFloat64s(delim string) ([]float64, error) { - return k.getFloat64s(delim, false, true) -} - -// StrictInts returns list of int divided by given delimiter or error on first invalid input. -func (k *Key) StrictInts(delim string) ([]int, error) { - return k.parseInts(k.Strings(delim), false, true) -} - -// StrictInt64s returns list of int64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictInt64s(delim string) ([]int64, error) { - return k.parseInt64s(k.Strings(delim), false, true) -} - -// StrictUints returns list of uint divided by given delimiter or error on first invalid input. -func (k *Key) StrictUints(delim string) ([]uint, error) { - return k.getUints(delim, false, true) -} - -// StrictUint64s returns list of uint64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictUint64s(delim string) ([]uint64, error) { - return k.getUint64s(delim, false, true) -} - -// StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter -// or error on first invalid input. -func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) { - return k.getTimesFormat(format, delim, false, true) -} - -// StrictTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter -// or error on first invalid input. -func (k *Key) StrictTimes(delim string) ([]time.Time, error) { - return k.StrictTimesFormat(time.RFC3339, delim) -} - -// getFloat64s returns list of float64 divided by given delimiter. -func (k *Key) getFloat64s(delim string, addInvalid, returnOnInvalid bool) ([]float64, error) { - strs := k.Strings(delim) - vals := make([]float64, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseFloat(str, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// parseInts transforms strings to ints. -func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) { - vals := make([]int, 0, len(strs)) - for _, str := range strs { - val, err := strconv.Atoi(str) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// parseInt64s transforms strings to int64s. -func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) { - vals := make([]int64, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseInt(str, 10, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// getUints returns list of uint divided by given delimiter. -func (k *Key) getUints(delim string, addInvalid, returnOnInvalid bool) ([]uint, error) { - strs := k.Strings(delim) - vals := make([]uint, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseUint(str, 10, 0) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, uint(val)) - } - } - return vals, nil -} - -// getUint64s returns list of uint64 divided by given delimiter. -func (k *Key) getUint64s(delim string, addInvalid, returnOnInvalid bool) ([]uint64, error) { - strs := k.Strings(delim) - vals := make([]uint64, 0, len(strs)) - for _, str := range strs { - val, err := strconv.ParseUint(str, 10, 64) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// getTimesFormat parses with given format and returns list of time.Time divided by given delimiter. -func (k *Key) getTimesFormat(format, delim string, addInvalid, returnOnInvalid bool) ([]time.Time, error) { - strs := k.Strings(delim) - vals := make([]time.Time, 0, len(strs)) - for _, str := range strs { - val, err := time.Parse(format, str) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// SetValue changes key value. -func (k *Key) SetValue(v string) { - if k.s.f.BlockMode { - k.s.f.lock.Lock() - defer k.s.f.lock.Unlock() - } - - k.value = v - k.s.keysHash[k.name] = v -} diff --git a/vendor/github.com/go-ini/ini/parser.go b/vendor/github.com/go-ini/ini/parser.go deleted file mode 100644 index 673ef80ca2674..0000000000000 --- a/vendor/github.com/go-ini/ini/parser.go +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2015 Unknwon -// -// 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. - -package ini - -import ( - "bufio" - "bytes" - "fmt" - "io" - "strconv" - "strings" - "unicode" -) - -type tokenType int - -const ( - _TOKEN_INVALID tokenType = iota - _TOKEN_COMMENT - _TOKEN_SECTION - _TOKEN_KEY -) - -type parser struct { - buf *bufio.Reader - isEOF bool - count int - comment *bytes.Buffer -} - -func newParser(r io.Reader) *parser { - return &parser{ - buf: bufio.NewReader(r), - count: 1, - comment: &bytes.Buffer{}, - } -} - -// BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format. -// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding -func (p *parser) BOM() error { - mask, err := p.buf.Peek(2) - if err != nil && err != io.EOF { - return err - } else if len(mask) < 2 { - return nil - } - - switch { - case mask[0] == 254 && mask[1] == 255: - fallthrough - case mask[0] == 255 && mask[1] == 254: - p.buf.Read(mask) - case mask[0] == 239 && mask[1] == 187: - mask, err := p.buf.Peek(3) - if err != nil && err != io.EOF { - return err - } else if len(mask) < 3 { - return nil - } - if mask[2] == 191 { - p.buf.Read(mask) - } - } - return nil -} - -func (p *parser) readUntil(delim byte) ([]byte, error) { - data, err := p.buf.ReadBytes(delim) - if err != nil { - if err == io.EOF { - p.isEOF = true - } else { - return nil, err - } - } - return data, nil -} - -func cleanComment(in []byte) ([]byte, bool) { - i := bytes.IndexAny(in, "#;") - if i == -1 { - return nil, false - } - return in[i:], true -} - -func readKeyName(in []byte) (string, int, error) { - line := string(in) - - // Check if key name surrounded by quotes. - var keyQuote string - if line[0] == '"' { - if len(line) > 6 && string(line[0:3]) == `"""` { - keyQuote = `"""` - } else { - keyQuote = `"` - } - } else if line[0] == '`' { - keyQuote = "`" - } - - // Get out key name - endIdx := -1 - if len(keyQuote) > 0 { - startIdx := len(keyQuote) - // FIXME: fail case -> """"""name"""=value - pos := strings.Index(line[startIdx:], keyQuote) - if pos == -1 { - return "", -1, fmt.Errorf("missing closing key quote: %s", line) - } - pos += startIdx - - // Find key-value delimiter - i := strings.IndexAny(line[pos+startIdx:], "=:") - if i < 0 { - return "", -1, ErrDelimiterNotFound{line} - } - endIdx = pos + i - return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil - } - - endIdx = strings.IndexAny(line, "=:") - if endIdx < 0 { - return "", -1, ErrDelimiterNotFound{line} - } - return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil -} - -func (p *parser) readMultilines(line, val, valQuote string) (string, error) { - for { - data, err := p.readUntil('\n') - if err != nil { - return "", err - } - next := string(data) - - pos := strings.LastIndex(next, valQuote) - if pos > -1 { - val += next[:pos] - - comment, has := cleanComment([]byte(next[pos:])) - if has { - p.comment.Write(bytes.TrimSpace(comment)) - } - break - } - val += next - if p.isEOF { - return "", fmt.Errorf("missing closing key quote from '%s' to '%s'", line, next) - } - } - return val, nil -} - -func (p *parser) readContinuationLines(val string) (string, error) { - for { - data, err := p.readUntil('\n') - if err != nil { - return "", err - } - next := strings.TrimSpace(string(data)) - - if len(next) == 0 { - break - } - val += next - if val[len(val)-1] != '\\' { - break - } - val = val[:len(val)-1] - } - return val, nil -} - -// hasSurroundedQuote check if and only if the first and last characters -// are quotes \" or \'. -// It returns false if any other parts also contain same kind of quotes. -func hasSurroundedQuote(in string, quote byte) bool { - return len(in) > 2 && in[0] == quote && in[len(in)-1] == quote && - strings.IndexByte(in[1:], quote) == len(in)-2 -} - -func (p *parser) readValue(in []byte, ignoreContinuation bool) (string, error) { - line := strings.TrimLeftFunc(string(in), unicode.IsSpace) - if len(line) == 0 { - return "", nil - } - - var valQuote string - if len(line) > 3 && string(line[0:3]) == `"""` { - valQuote = `"""` - } else if line[0] == '`' { - valQuote = "`" - } - - if len(valQuote) > 0 { - startIdx := len(valQuote) - pos := strings.LastIndex(line[startIdx:], valQuote) - // Check for multi-line value - if pos == -1 { - return p.readMultilines(line, line[startIdx:], valQuote) - } - - return line[startIdx : pos+startIdx], nil - } - - // Won't be able to reach here if value only contains whitespace. - line = strings.TrimSpace(line) - - // Check continuation lines when desired. - if !ignoreContinuation && line[len(line)-1] == '\\' { - return p.readContinuationLines(line[:len(line)-1]) - } - - i := strings.IndexAny(line, "#;") - if i > -1 { - p.comment.WriteString(line[i:]) - line = strings.TrimSpace(line[:i]) - } - - // Trim single quotes - if hasSurroundedQuote(line, '\'') || - hasSurroundedQuote(line, '"') { - line = line[1 : len(line)-1] - } - return line, nil -} - -// parse parses data through an io.Reader. -func (f *File) parse(reader io.Reader) (err error) { - p := newParser(reader) - if err = p.BOM(); err != nil { - return fmt.Errorf("BOM: %v", err) - } - - // Ignore error because default section name is never empty string. - section, _ := f.NewSection(DEFAULT_SECTION) - - var line []byte - var inUnparseableSection bool - for !p.isEOF { - line, err = p.readUntil('\n') - if err != nil { - return err - } - - line = bytes.TrimLeftFunc(line, unicode.IsSpace) - if len(line) == 0 { - continue - } - - // Comments - if line[0] == '#' || line[0] == ';' { - // Note: we do not care ending line break, - // it is needed for adding second line, - // so just clean it once at the end when set to value. - p.comment.Write(line) - continue - } - - // Section - if line[0] == '[' { - // Read to the next ']' (TODO: support quoted strings) - // TODO(unknwon): use LastIndexByte when stop supporting Go1.4 - closeIdx := bytes.LastIndex(line, []byte("]")) - if closeIdx == -1 { - return fmt.Errorf("unclosed section: %s", line) - } - - name := string(line[1:closeIdx]) - section, err = f.NewSection(name) - if err != nil { - return err - } - - comment, has := cleanComment(line[closeIdx+1:]) - if has { - p.comment.Write(comment) - } - - section.Comment = strings.TrimSpace(p.comment.String()) - - // Reset aotu-counter and comments - p.comment.Reset() - p.count = 1 - - inUnparseableSection = false - for i := range f.options.UnparseableSections { - if f.options.UnparseableSections[i] == name || - (f.options.Insensitive && strings.ToLower(f.options.UnparseableSections[i]) == strings.ToLower(name)) { - inUnparseableSection = true - continue - } - } - continue - } - - if inUnparseableSection { - section.isRawSection = true - section.rawBody += string(line) - continue - } - - kname, offset, err := readKeyName(line) - if err != nil { - // Treat as boolean key when desired, and whole line is key name. - if IsErrDelimiterNotFound(err) && f.options.AllowBooleanKeys { - kname, err := p.readValue(line, f.options.IgnoreContinuation) - if err != nil { - return err - } - key, err := section.NewBooleanKey(kname) - if err != nil { - return err - } - key.Comment = strings.TrimSpace(p.comment.String()) - p.comment.Reset() - continue - } - return err - } - - // Auto increment. - isAutoIncr := false - if kname == "-" { - isAutoIncr = true - kname = "#" + strconv.Itoa(p.count) - p.count++ - } - - value, err := p.readValue(line[offset:], f.options.IgnoreContinuation) - if err != nil { - return err - } - - key, err := section.NewKey(kname, value) - if err != nil { - return err - } - key.isAutoIncrement = isAutoIncr - key.Comment = strings.TrimSpace(p.comment.String()) - p.comment.Reset() - } - return nil -} diff --git a/vendor/github.com/go-ini/ini/section.go b/vendor/github.com/go-ini/ini/section.go deleted file mode 100644 index c9fa27e9ca7c6..0000000000000 --- a/vendor/github.com/go-ini/ini/section.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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. - -package ini - -import ( - "errors" - "fmt" - "strings" -) - -// Section represents a config section. -type Section struct { - f *File - Comment string - name string - keys map[string]*Key - keyList []string - keysHash map[string]string - - isRawSection bool - rawBody string -} - -func newSection(f *File, name string) *Section { - return &Section{ - f: f, - name: name, - keys: make(map[string]*Key), - keyList: make([]string, 0, 10), - keysHash: make(map[string]string), - } -} - -// Name returns name of Section. -func (s *Section) Name() string { - return s.name -} - -// Body returns rawBody of Section if the section was marked as unparseable. -// It still follows the other rules of the INI format surrounding leading/trailing whitespace. -func (s *Section) Body() string { - return strings.TrimSpace(s.rawBody) -} - -// NewKey creates a new key to given section. -func (s *Section) NewKey(name, val string) (*Key, error) { - if len(name) == 0 { - return nil, errors.New("error creating new key: empty key name") - } else if s.f.options.Insensitive { - name = strings.ToLower(name) - } - - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - if inSlice(name, s.keyList) { - if s.f.options.AllowShadows { - if err := s.keys[name].addShadow(val); err != nil { - return nil, err - } - } else { - s.keys[name].value = val - } - return s.keys[name], nil - } - - s.keyList = append(s.keyList, name) - s.keys[name] = newKey(s, name, val) - s.keysHash[name] = val - return s.keys[name], nil -} - -// NewBooleanKey creates a new boolean type key to given section. -func (s *Section) NewBooleanKey(name string) (*Key, error) { - key, err := s.NewKey(name, "true") - if err != nil { - return nil, err - } - - key.isBooleanType = true - return key, nil -} - -// GetKey returns key in section by given name. -func (s *Section) GetKey(name string) (*Key, error) { - // FIXME: change to section level lock? - if s.f.BlockMode { - s.f.lock.RLock() - } - if s.f.options.Insensitive { - name = strings.ToLower(name) - } - key := s.keys[name] - if s.f.BlockMode { - s.f.lock.RUnlock() - } - - if key == nil { - // Check if it is a child-section. - sname := s.name - for { - if i := strings.LastIndex(sname, "."); i > -1 { - sname = sname[:i] - sec, err := s.f.GetSection(sname) - if err != nil { - continue - } - return sec.GetKey(name) - } else { - break - } - } - return nil, fmt.Errorf("error when getting key of section '%s': key '%s' not exists", s.name, name) - } - return key, nil -} - -// HasKey returns true if section contains a key with given name. -func (s *Section) HasKey(name string) bool { - key, _ := s.GetKey(name) - return key != nil -} - -// Haskey is a backwards-compatible name for HasKey. -func (s *Section) Haskey(name string) bool { - return s.HasKey(name) -} - -// HasValue returns true if section contains given raw value. -func (s *Section) HasValue(value string) bool { - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - for _, k := range s.keys { - if value == k.value { - return true - } - } - return false -} - -// Key assumes named Key exists in section and returns a zero-value when not. -func (s *Section) Key(name string) *Key { - key, err := s.GetKey(name) - if err != nil { - // It's OK here because the only possible error is empty key name, - // but if it's empty, this piece of code won't be executed. - key, _ = s.NewKey(name, "") - return key - } - return key -} - -// Keys returns list of keys of section. -func (s *Section) Keys() []*Key { - keys := make([]*Key, len(s.keyList)) - for i := range s.keyList { - keys[i] = s.Key(s.keyList[i]) - } - return keys -} - -// ParentKeys returns list of keys of parent section. -func (s *Section) ParentKeys() []*Key { - var parentKeys []*Key - sname := s.name - for { - if i := strings.LastIndex(sname, "."); i > -1 { - sname = sname[:i] - sec, err := s.f.GetSection(sname) - if err != nil { - continue - } - parentKeys = append(parentKeys, sec.Keys()...) - } else { - break - } - - } - return parentKeys -} - -// KeyStrings returns list of key names of section. -func (s *Section) KeyStrings() []string { - list := make([]string, len(s.keyList)) - copy(list, s.keyList) - return list -} - -// KeysHash returns keys hash consisting of names and values. -func (s *Section) KeysHash() map[string]string { - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - hash := map[string]string{} - for key, value := range s.keysHash { - hash[key] = value - } - return hash -} - -// DeleteKey deletes a key from section. -func (s *Section) DeleteKey(name string) { - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - for i, k := range s.keyList { - if k == name { - s.keyList = append(s.keyList[:i], s.keyList[i+1:]...) - delete(s.keys, name) - return - } - } -} diff --git a/vendor/github.com/go-ini/ini/struct.go b/vendor/github.com/go-ini/ini/struct.go deleted file mode 100644 index 509c682fa6df4..0000000000000 --- a/vendor/github.com/go-ini/ini/struct.go +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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. - -package ini - -import ( - "bytes" - "errors" - "fmt" - "reflect" - "strings" - "time" - "unicode" -) - -// NameMapper represents a ini tag name mapper. -type NameMapper func(string) string - -// Built-in name getters. -var ( - // AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE. - AllCapsUnderscore NameMapper = func(raw string) string { - newstr := make([]rune, 0, len(raw)) - for i, chr := range raw { - if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { - if i > 0 { - newstr = append(newstr, '_') - } - } - newstr = append(newstr, unicode.ToUpper(chr)) - } - return string(newstr) - } - // TitleUnderscore converts to format title_underscore. - TitleUnderscore NameMapper = func(raw string) string { - newstr := make([]rune, 0, len(raw)) - for i, chr := range raw { - if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { - if i > 0 { - newstr = append(newstr, '_') - } - chr -= ('A' - 'a') - } - newstr = append(newstr, chr) - } - return string(newstr) - } -) - -func (s *Section) parseFieldName(raw, actual string) string { - if len(actual) > 0 { - return actual - } - if s.f.NameMapper != nil { - return s.f.NameMapper(raw) - } - return raw -} - -func parseDelim(actual string) string { - if len(actual) > 0 { - return actual - } - return "," -} - -var reflectTime = reflect.TypeOf(time.Now()).Kind() - -// setSliceWithProperType sets proper values to slice based on its type. -func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow bool) error { - var strs []string - if allowShadow { - strs = key.StringsWithShadows(delim) - } else { - strs = key.Strings(delim) - } - - numVals := len(strs) - if numVals == 0 { - return nil - } - - var vals interface{} - - sliceOf := field.Type().Elem().Kind() - switch sliceOf { - case reflect.String: - vals = strs - case reflect.Int: - vals, _ = key.parseInts(strs, true, false) - case reflect.Int64: - vals, _ = key.parseInt64s(strs, true, false) - case reflect.Uint: - vals = key.Uints(delim) - case reflect.Uint64: - vals = key.Uint64s(delim) - case reflect.Float64: - vals = key.Float64s(delim) - case reflectTime: - vals = key.Times(delim) - default: - return fmt.Errorf("unsupported type '[]%s'", sliceOf) - } - - slice := reflect.MakeSlice(field.Type(), numVals, numVals) - for i := 0; i < numVals; i++ { - switch sliceOf { - case reflect.String: - slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i])) - case reflect.Int: - slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i])) - case reflect.Int64: - slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i])) - case reflect.Uint: - slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i])) - case reflect.Uint64: - slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i])) - case reflect.Float64: - slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i])) - case reflectTime: - slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i])) - } - } - field.Set(slice) - return nil -} - -// setWithProperType sets proper value to field based on its type, -// but it does not return error for failing parsing, -// because we want to use default value that is already assigned to strcut. -func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow bool) error { - switch t.Kind() { - case reflect.String: - if len(key.String()) == 0 { - return nil - } - field.SetString(key.String()) - case reflect.Bool: - boolVal, err := key.Bool() - if err != nil { - return nil - } - field.SetBool(boolVal) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - durationVal, err := key.Duration() - // Skip zero value - if err == nil && int(durationVal) > 0 { - field.Set(reflect.ValueOf(durationVal)) - return nil - } - - intVal, err := key.Int64() - if err != nil || intVal == 0 { - return nil - } - field.SetInt(intVal) - // byte is an alias for uint8, so supporting uint8 breaks support for byte - case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: - durationVal, err := key.Duration() - // Skip zero value - if err == nil && int(durationVal) > 0 { - field.Set(reflect.ValueOf(durationVal)) - return nil - } - - uintVal, err := key.Uint64() - if err != nil { - return nil - } - field.SetUint(uintVal) - - case reflect.Float32, reflect.Float64: - floatVal, err := key.Float64() - if err != nil { - return nil - } - field.SetFloat(floatVal) - case reflectTime: - timeVal, err := key.Time() - if err != nil { - return nil - } - field.Set(reflect.ValueOf(timeVal)) - case reflect.Slice: - return setSliceWithProperType(key, field, delim, allowShadow) - default: - return fmt.Errorf("unsupported type '%s'", t) - } - return nil -} - -func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool) { - opts := strings.SplitN(tag, ",", 3) - rawName = opts[0] - if len(opts) > 1 { - omitEmpty = opts[1] == "omitempty" - } - if len(opts) > 2 { - allowShadow = opts[2] == "allowshadow" - } - return rawName, omitEmpty, allowShadow -} - -func (s *Section) mapTo(val reflect.Value) error { - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - typ := val.Type() - - for i := 0; i < typ.NumField(); i++ { - field := val.Field(i) - tpField := typ.Field(i) - - tag := tpField.Tag.Get("ini") - if tag == "-" { - continue - } - - rawName, _, allowShadow := parseTagOptions(tag) - fieldName := s.parseFieldName(tpField.Name, rawName) - if len(fieldName) == 0 || !field.CanSet() { - continue - } - - isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous - isStruct := tpField.Type.Kind() == reflect.Struct - if isAnonymous { - field.Set(reflect.New(tpField.Type.Elem())) - } - - if isAnonymous || isStruct { - if sec, err := s.f.GetSection(fieldName); err == nil { - if err = sec.mapTo(field); err != nil { - return fmt.Errorf("error mapping field(%s): %v", fieldName, err) - } - continue - } - } - - if key, err := s.GetKey(fieldName); err == nil { - delim := parseDelim(tpField.Tag.Get("delim")) - if err = setWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil { - return fmt.Errorf("error mapping field(%s): %v", fieldName, err) - } - } - } - return nil -} - -// MapTo maps section to given struct. -func (s *Section) MapTo(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("cannot map to non-pointer struct") - } - - return s.mapTo(val) -} - -// MapTo maps file to given struct. -func (f *File) MapTo(v interface{}) error { - return f.Section("").MapTo(v) -} - -// MapTo maps data sources to given struct with name mapper. -func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { - cfg, err := Load(source, others...) - if err != nil { - return err - } - cfg.NameMapper = mapper - return cfg.MapTo(v) -} - -// MapTo maps data sources to given struct. -func MapTo(v, source interface{}, others ...interface{}) error { - return MapToWithMapper(v, nil, source, others...) -} - -// reflectSliceWithProperType does the opposite thing as setSliceWithProperType. -func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error { - slice := field.Slice(0, field.Len()) - if field.Len() == 0 { - return nil - } - - var buf bytes.Buffer - sliceOf := field.Type().Elem().Kind() - for i := 0; i < field.Len(); i++ { - switch sliceOf { - case reflect.String: - buf.WriteString(slice.Index(i).String()) - case reflect.Int, reflect.Int64: - buf.WriteString(fmt.Sprint(slice.Index(i).Int())) - case reflect.Uint, reflect.Uint64: - buf.WriteString(fmt.Sprint(slice.Index(i).Uint())) - case reflect.Float64: - buf.WriteString(fmt.Sprint(slice.Index(i).Float())) - case reflectTime: - buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339)) - default: - return fmt.Errorf("unsupported type '[]%s'", sliceOf) - } - buf.WriteString(delim) - } - key.SetValue(buf.String()[:buf.Len()-1]) - return nil -} - -// reflectWithProperType does the opposite thing as setWithProperType. -func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error { - switch t.Kind() { - case reflect.String: - key.SetValue(field.String()) - case reflect.Bool: - key.SetValue(fmt.Sprint(field.Bool())) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - key.SetValue(fmt.Sprint(field.Int())) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - key.SetValue(fmt.Sprint(field.Uint())) - case reflect.Float32, reflect.Float64: - key.SetValue(fmt.Sprint(field.Float())) - case reflectTime: - key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339))) - case reflect.Slice: - return reflectSliceWithProperType(key, field, delim) - default: - return fmt.Errorf("unsupported type '%s'", t) - } - return nil -} - -// CR: copied from encoding/json/encode.go with modifications of time.Time support. -// TODO: add more test coverage. -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflectTime: - return v.Interface().(time.Time).IsZero() - case reflect.Interface, reflect.Ptr: - return v.IsNil() - } - return false -} - -func (s *Section) reflectFrom(val reflect.Value) error { - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - typ := val.Type() - - for i := 0; i < typ.NumField(); i++ { - field := val.Field(i) - tpField := typ.Field(i) - - tag := tpField.Tag.Get("ini") - if tag == "-" { - continue - } - - opts := strings.SplitN(tag, ",", 2) - if len(opts) == 2 && opts[1] == "omitempty" && isEmptyValue(field) { - continue - } - - fieldName := s.parseFieldName(tpField.Name, opts[0]) - if len(fieldName) == 0 || !field.CanSet() { - continue - } - - if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) || - (tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") { - // Note: The only error here is section doesn't exist. - sec, err := s.f.GetSection(fieldName) - if err != nil { - // Note: fieldName can never be empty here, ignore error. - sec, _ = s.f.NewSection(fieldName) - } - if err = sec.reflectFrom(field); err != nil { - return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) - } - continue - } - - // Note: Same reason as secion. - key, err := s.GetKey(fieldName) - if err != nil { - key, _ = s.NewKey(fieldName, "") - } - if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil { - return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) - } - - } - return nil -} - -// ReflectFrom reflects secion from given struct. -func (s *Section) ReflectFrom(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("cannot reflect from non-pointer struct") - } - - return s.reflectFrom(val) -} - -// ReflectFrom reflects file from given struct. -func (f *File) ReflectFrom(v interface{}) error { - return f.Section("").ReflectFrom(v) -} - -// ReflectFrom reflects data sources from given struct with name mapper. -func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error { - cfg.NameMapper = mapper - return cfg.ReflectFrom(v) -} - -// ReflectFrom reflects data sources from given struct. -func ReflectFrom(cfg *File, v interface{}) error { - return ReflectFromWithMapper(cfg, v, nil) -} diff --git a/vendor/github.com/godbus/dbus/README.markdown b/vendor/github.com/godbus/dbus/README.markdown deleted file mode 100644 index 0a6e7e5bfb096..0000000000000 --- a/vendor/github.com/godbus/dbus/README.markdown +++ /dev/null @@ -1,41 +0,0 @@ -dbus ----- - -dbus is a simple library that implements native Go client bindings for the -D-Bus message bus system. - -### Features - -* Complete native implementation of the D-Bus message protocol -* Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections) -* Subpackages that help with the introspection / property interfaces - -### Installation - -This packages requires Go 1.1. If you installed it and set up your GOPATH, just run: - -``` -go get github.com/godbus/dbus -``` - -If you want to use the subpackages, you can install them the same way. - -### Usage - -The complete package documentation and some simple examples are available at -[godoc.org](http://godoc.org/github.com/godbus/dbus). Also, the -[_examples](https://github.com/godbus/dbus/tree/master/_examples) directory -gives a short overview over the basic usage. - -#### Projects using godbus -- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library. - -Please note that the API is considered unstable for now and may change without -further notice. - -### License - -go.dbus is available under the Simplified BSD License; see LICENSE for the full -text. - -Nearly all of the credit for this library goes to github.com/guelfey/go.dbus. diff --git a/vendor/github.com/godbus/dbus/auth.go b/vendor/github.com/godbus/dbus/auth.go deleted file mode 100644 index 98017b693eec5..0000000000000 --- a/vendor/github.com/godbus/dbus/auth.go +++ /dev/null @@ -1,253 +0,0 @@ -package dbus - -import ( - "bufio" - "bytes" - "errors" - "io" - "os" - "strconv" -) - -// AuthStatus represents the Status of an authentication mechanism. -type AuthStatus byte - -const ( - // AuthOk signals that authentication is finished; the next command - // from the server should be an OK. - AuthOk AuthStatus = iota - - // AuthContinue signals that additional data is needed; the next command - // from the server should be a DATA. - AuthContinue - - // AuthError signals an error; the server sent invalid data or some - // other unexpected thing happened and the current authentication - // process should be aborted. - AuthError -) - -type authState byte - -const ( - waitingForData authState = iota - waitingForOk - waitingForReject -) - -// Auth defines the behaviour of an authentication mechanism. -type Auth interface { - // Return the name of the mechnism, the argument to the first AUTH command - // and the next status. - FirstData() (name, resp []byte, status AuthStatus) - - // Process the given DATA command, and return the argument to the DATA - // command and the next status. If len(resp) == 0, no DATA command is sent. - HandleData(data []byte) (resp []byte, status AuthStatus) -} - -// Auth authenticates the connection, trying the given list of authentication -// mechanisms (in that order). If nil is passed, the EXTERNAL and -// DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private -// connections, this method must be called before sending any messages to the -// bus. Auth must not be called on shared connections. -func (conn *Conn) Auth(methods []Auth) error { - if methods == nil { - uid := strconv.Itoa(os.Getuid()) - methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())} - } - in := bufio.NewReader(conn.transport) - err := conn.transport.SendNullByte() - if err != nil { - return err - } - err = authWriteLine(conn.transport, []byte("AUTH")) - if err != nil { - return err - } - s, err := authReadLine(in) - if err != nil { - return err - } - if len(s) < 2 || !bytes.Equal(s[0], []byte("REJECTED")) { - return errors.New("dbus: authentication protocol error") - } - s = s[1:] - for _, v := range s { - for _, m := range methods { - if name, data, status := m.FirstData(); bytes.Equal(v, name) { - var ok bool - err = authWriteLine(conn.transport, []byte("AUTH"), []byte(v), data) - if err != nil { - return err - } - switch status { - case AuthOk: - err, ok = conn.tryAuth(m, waitingForOk, in) - case AuthContinue: - err, ok = conn.tryAuth(m, waitingForData, in) - default: - panic("dbus: invalid authentication status") - } - if err != nil { - return err - } - if ok { - if conn.transport.SupportsUnixFDs() { - err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD")) - if err != nil { - return err - } - line, err := authReadLine(in) - if err != nil { - return err - } - switch { - case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")): - conn.EnableUnixFDs() - conn.unixFD = true - case bytes.Equal(line[0], []byte("ERROR")): - default: - return errors.New("dbus: authentication protocol error") - } - } - err = authWriteLine(conn.transport, []byte("BEGIN")) - if err != nil { - return err - } - go conn.inWorker() - go conn.outWorker() - return nil - } - } - } - } - return errors.New("dbus: authentication failed") -} - -// tryAuth tries to authenticate with m as the mechanism, using state as the -// initial authState and in for reading input. It returns (nil, true) on -// success, (nil, false) on a REJECTED and (someErr, false) if some other -// error occured. -func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) { - for { - s, err := authReadLine(in) - if err != nil { - return err, false - } - switch { - case state == waitingForData && string(s[0]) == "DATA": - if len(s) != 2 { - err = authWriteLine(conn.transport, []byte("ERROR")) - if err != nil { - return err, false - } - continue - } - data, status := m.HandleData(s[1]) - switch status { - case AuthOk, AuthContinue: - if len(data) != 0 { - err = authWriteLine(conn.transport, []byte("DATA"), data) - if err != nil { - return err, false - } - } - if status == AuthOk { - state = waitingForOk - } - case AuthError: - err = authWriteLine(conn.transport, []byte("ERROR")) - if err != nil { - return err, false - } - } - case state == waitingForData && string(s[0]) == "REJECTED": - return nil, false - case state == waitingForData && string(s[0]) == "ERROR": - err = authWriteLine(conn.transport, []byte("CANCEL")) - if err != nil { - return err, false - } - state = waitingForReject - case state == waitingForData && string(s[0]) == "OK": - if len(s) != 2 { - err = authWriteLine(conn.transport, []byte("CANCEL")) - if err != nil { - return err, false - } - state = waitingForReject - } - conn.uuid = string(s[1]) - return nil, true - case state == waitingForData: - err = authWriteLine(conn.transport, []byte("ERROR")) - if err != nil { - return err, false - } - case state == waitingForOk && string(s[0]) == "OK": - if len(s) != 2 { - err = authWriteLine(conn.transport, []byte("CANCEL")) - if err != nil { - return err, false - } - state = waitingForReject - } - conn.uuid = string(s[1]) - return nil, true - case state == waitingForOk && string(s[0]) == "REJECTED": - return nil, false - case state == waitingForOk && (string(s[0]) == "DATA" || - string(s[0]) == "ERROR"): - - err = authWriteLine(conn.transport, []byte("CANCEL")) - if err != nil { - return err, false - } - state = waitingForReject - case state == waitingForOk: - err = authWriteLine(conn.transport, []byte("ERROR")) - if err != nil { - return err, false - } - case state == waitingForReject && string(s[0]) == "REJECTED": - return nil, false - case state == waitingForReject: - return errors.New("dbus: authentication protocol error"), false - default: - panic("dbus: invalid auth state") - } - } -} - -// authReadLine reads a line and separates it into its fields. -func authReadLine(in *bufio.Reader) ([][]byte, error) { - data, err := in.ReadBytes('\n') - if err != nil { - return nil, err - } - data = bytes.TrimSuffix(data, []byte("\r\n")) - return bytes.Split(data, []byte{' '}), nil -} - -// authWriteLine writes the given line in the authentication protocol format -// (elements of data separated by a " " and terminated by "\r\n"). -func authWriteLine(out io.Writer, data ...[]byte) error { - buf := make([]byte, 0) - for i, v := range data { - buf = append(buf, v...) - if i != len(data)-1 { - buf = append(buf, ' ') - } - } - buf = append(buf, '\r') - buf = append(buf, '\n') - n, err := out.Write(buf) - if err != nil { - return err - } - if n != len(buf) { - return io.ErrUnexpectedEOF - } - return nil -} diff --git a/vendor/github.com/godbus/dbus/call.go b/vendor/github.com/godbus/dbus/call.go deleted file mode 100644 index ba6e73f607a9d..0000000000000 --- a/vendor/github.com/godbus/dbus/call.go +++ /dev/null @@ -1,36 +0,0 @@ -package dbus - -import ( - "errors" -) - -// Call represents a pending or completed method call. -type Call struct { - Destination string - Path ObjectPath - Method string - Args []interface{} - - // Strobes when the call is complete. - Done chan *Call - - // After completion, the error status. If this is non-nil, it may be an - // error message from the peer (with Error as its type) or some other error. - Err error - - // Holds the response once the call is done. - Body []interface{} -} - -var errSignature = errors.New("dbus: mismatched signature") - -// Store stores the body of the reply into the provided pointers. It returns -// an error if the signatures of the body and retvalues don't match, or if -// the error status is not nil. -func (c *Call) Store(retvalues ...interface{}) error { - if c.Err != nil { - return c.Err - } - - return Store(c.Body, retvalues...) -} diff --git a/vendor/github.com/godbus/dbus/conn.go b/vendor/github.com/godbus/dbus/conn.go deleted file mode 100644 index 9aa2e128099dd..0000000000000 --- a/vendor/github.com/godbus/dbus/conn.go +++ /dev/null @@ -1,634 +0,0 @@ -package dbus - -import ( - "errors" - "io" - "os" - "reflect" - "strings" - "sync" -) - -const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" - -var ( - systemBus *Conn - systemBusLck sync.Mutex - sessionBus *Conn - sessionBusLck sync.Mutex - sessionEnvLck sync.Mutex -) - -// ErrClosed is the error returned by calls on a closed connection. -var ErrClosed = errors.New("dbus: connection closed by user") - -// Conn represents a connection to a message bus (usually, the system or -// session bus). -// -// Connections are either shared or private. Shared connections -// are shared between calls to the functions that return them. As a result, -// the methods Close, Auth and Hello must not be called on them. -// -// Multiple goroutines may invoke methods on a connection simultaneously. -type Conn struct { - transport - - busObj BusObject - unixFD bool - uuid string - - names []string - namesLck sync.RWMutex - - serialLck sync.Mutex - nextSerial uint32 - serialUsed map[uint32]bool - - calls map[uint32]*Call - callsLck sync.RWMutex - - handlers map[ObjectPath]map[string]exportedObj - handlersLck sync.RWMutex - - out chan *Message - closed bool - outLck sync.RWMutex - - signals []chan<- *Signal - signalsLck sync.Mutex - - eavesdropped chan<- *Message - eavesdroppedLck sync.Mutex -} - -// SessionBus returns a shared connection to the session bus, connecting to it -// if not already done. -func SessionBus() (conn *Conn, err error) { - sessionBusLck.Lock() - defer sessionBusLck.Unlock() - if sessionBus != nil { - return sessionBus, nil - } - defer func() { - if conn != nil { - sessionBus = conn - } - }() - conn, err = SessionBusPrivate() - if err != nil { - return - } - if err = conn.Auth(nil); err != nil { - conn.Close() - conn = nil - return - } - if err = conn.Hello(); err != nil { - conn.Close() - conn = nil - } - return -} - -// SessionBusPrivate returns a new private connection to the session bus. -func SessionBusPrivate() (*Conn, error) { - sessionEnvLck.Lock() - defer sessionEnvLck.Unlock() - address := os.Getenv("DBUS_SESSION_BUS_ADDRESS") - if address != "" && address != "autolaunch:" { - return Dial(address) - } - - return sessionBusPlatform() -} - -// SystemBus returns a shared connection to the system bus, connecting to it if -// not already done. -func SystemBus() (conn *Conn, err error) { - systemBusLck.Lock() - defer systemBusLck.Unlock() - if systemBus != nil { - return systemBus, nil - } - defer func() { - if conn != nil { - systemBus = conn - } - }() - conn, err = SystemBusPrivate() - if err != nil { - return - } - if err = conn.Auth(nil); err != nil { - conn.Close() - conn = nil - return - } - if err = conn.Hello(); err != nil { - conn.Close() - conn = nil - } - return -} - -// SystemBusPrivate returns a new private connection to the system bus. -func SystemBusPrivate() (*Conn, error) { - address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") - if address != "" { - return Dial(address) - } - return Dial(defaultSystemBusAddress) -} - -// Dial establishes a new private connection to the message bus specified by address. -func Dial(address string) (*Conn, error) { - tr, err := getTransport(address) - if err != nil { - return nil, err - } - return newConn(tr) -} - -// NewConn creates a new private *Conn from an already established connection. -func NewConn(conn io.ReadWriteCloser) (*Conn, error) { - return newConn(genericTransport{conn}) -} - -// newConn creates a new *Conn from a transport. -func newConn(tr transport) (*Conn, error) { - conn := new(Conn) - conn.transport = tr - conn.calls = make(map[uint32]*Call) - conn.out = make(chan *Message, 10) - conn.handlers = make(map[ObjectPath]map[string]exportedObj) - conn.nextSerial = 1 - conn.serialUsed = map[uint32]bool{0: true} - conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") - return conn, nil -} - -// BusObject returns the object owned by the bus daemon which handles -// administrative requests. -func (conn *Conn) BusObject() BusObject { - return conn.busObj -} - -// Close closes the connection. Any blocked operations will return with errors -// and the channels passed to Eavesdrop and Signal are closed. This method must -// not be called on shared connections. -func (conn *Conn) Close() error { - conn.outLck.Lock() - if conn.closed { - // inWorker calls Close on read error, the read error may - // be caused by another caller calling Close to shutdown the - // dbus connection, a double-close scenario we prevent here. - conn.outLck.Unlock() - return nil - } - close(conn.out) - conn.closed = true - conn.outLck.Unlock() - conn.signalsLck.Lock() - for _, ch := range conn.signals { - close(ch) - } - conn.signalsLck.Unlock() - conn.eavesdroppedLck.Lock() - if conn.eavesdropped != nil { - close(conn.eavesdropped) - } - conn.eavesdroppedLck.Unlock() - return conn.transport.Close() -} - -// Eavesdrop causes conn to send all incoming messages to the given channel -// without further processing. Method replies, errors and signals will not be -// sent to the appropiate channels and method calls will not be handled. If nil -// is passed, the normal behaviour is restored. -// -// The caller has to make sure that ch is sufficiently buffered; -// if a message arrives when a write to ch is not possible, the message is -// discarded. -func (conn *Conn) Eavesdrop(ch chan<- *Message) { - conn.eavesdroppedLck.Lock() - conn.eavesdropped = ch - conn.eavesdroppedLck.Unlock() -} - -// getSerial returns an unused serial. -func (conn *Conn) getSerial() uint32 { - conn.serialLck.Lock() - defer conn.serialLck.Unlock() - n := conn.nextSerial - for conn.serialUsed[n] { - n++ - } - conn.serialUsed[n] = true - conn.nextSerial = n + 1 - return n -} - -// Hello sends the initial org.freedesktop.DBus.Hello call. This method must be -// called after authentication, but before sending any other messages to the -// bus. Hello must not be called for shared connections. -func (conn *Conn) Hello() error { - var s string - err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s) - if err != nil { - return err - } - conn.namesLck.Lock() - conn.names = make([]string, 1) - conn.names[0] = s - conn.namesLck.Unlock() - return nil -} - -// inWorker runs in an own goroutine, reading incoming messages from the -// transport and dispatching them appropiately. -func (conn *Conn) inWorker() { - for { - msg, err := conn.ReadMessage() - if err == nil { - conn.eavesdroppedLck.Lock() - if conn.eavesdropped != nil { - select { - case conn.eavesdropped <- msg: - default: - } - conn.eavesdroppedLck.Unlock() - continue - } - conn.eavesdroppedLck.Unlock() - dest, _ := msg.Headers[FieldDestination].value.(string) - found := false - if dest == "" { - found = true - } else { - conn.namesLck.RLock() - if len(conn.names) == 0 { - found = true - } - for _, v := range conn.names { - if dest == v { - found = true - break - } - } - conn.namesLck.RUnlock() - } - if !found { - // Eavesdropped a message, but no channel for it is registered. - // Ignore it. - continue - } - switch msg.Type { - case TypeMethodReply, TypeError: - serial := msg.Headers[FieldReplySerial].value.(uint32) - conn.callsLck.Lock() - if c, ok := conn.calls[serial]; ok { - if msg.Type == TypeError { - name, _ := msg.Headers[FieldErrorName].value.(string) - c.Err = Error{name, msg.Body} - } else { - c.Body = msg.Body - } - c.Done <- c - conn.serialLck.Lock() - delete(conn.serialUsed, serial) - conn.serialLck.Unlock() - delete(conn.calls, serial) - } - conn.callsLck.Unlock() - case TypeSignal: - iface := msg.Headers[FieldInterface].value.(string) - member := msg.Headers[FieldMember].value.(string) - // as per http://dbus.freedesktop.org/doc/dbus-specification.html , - // sender is optional for signals. - sender, _ := msg.Headers[FieldSender].value.(string) - if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" { - if member == "NameLost" { - // If we lost the name on the bus, remove it from our - // tracking list. - name, ok := msg.Body[0].(string) - if !ok { - panic("Unable to read the lost name") - } - conn.namesLck.Lock() - for i, v := range conn.names { - if v == name { - conn.names = append(conn.names[:i], - conn.names[i+1:]...) - } - } - conn.namesLck.Unlock() - } else if member == "NameAcquired" { - // If we acquired the name on the bus, add it to our - // tracking list. - name, ok := msg.Body[0].(string) - if !ok { - panic("Unable to read the acquired name") - } - conn.namesLck.Lock() - conn.names = append(conn.names, name) - conn.namesLck.Unlock() - } - } - signal := &Signal{ - Sender: sender, - Path: msg.Headers[FieldPath].value.(ObjectPath), - Name: iface + "." + member, - Body: msg.Body, - } - conn.signalsLck.Lock() - for _, ch := range conn.signals { - ch <- signal - } - conn.signalsLck.Unlock() - case TypeMethodCall: - go conn.handleCall(msg) - } - } else if _, ok := err.(InvalidMessageError); !ok { - // Some read error occured (usually EOF); we can't really do - // anything but to shut down all stuff and returns errors to all - // pending replies. - conn.Close() - conn.callsLck.RLock() - for _, v := range conn.calls { - v.Err = err - v.Done <- v - } - conn.callsLck.RUnlock() - return - } - // invalid messages are ignored - } -} - -// Names returns the list of all names that are currently owned by this -// connection. The slice is always at least one element long, the first element -// being the unique name of the connection. -func (conn *Conn) Names() []string { - conn.namesLck.RLock() - // copy the slice so it can't be modified - s := make([]string, len(conn.names)) - copy(s, conn.names) - conn.namesLck.RUnlock() - return s -} - -// Object returns the object identified by the given destination name and path. -func (conn *Conn) Object(dest string, path ObjectPath) BusObject { - return &Object{conn, dest, path} -} - -// outWorker runs in an own goroutine, encoding and sending messages that are -// sent to conn.out. -func (conn *Conn) outWorker() { - for msg := range conn.out { - err := conn.SendMessage(msg) - conn.callsLck.RLock() - if err != nil { - if c := conn.calls[msg.serial]; c != nil { - c.Err = err - c.Done <- c - } - conn.serialLck.Lock() - delete(conn.serialUsed, msg.serial) - conn.serialLck.Unlock() - } else if msg.Type != TypeMethodCall { - conn.serialLck.Lock() - delete(conn.serialUsed, msg.serial) - conn.serialLck.Unlock() - } - conn.callsLck.RUnlock() - } -} - -// Send sends the given message to the message bus. You usually don't need to -// use this; use the higher-level equivalents (Call / Go, Emit and Export) -// instead. If msg is a method call and NoReplyExpected is not set, a non-nil -// call is returned and the same value is sent to ch (which must be buffered) -// once the call is complete. Otherwise, ch is ignored and a Call structure is -// returned of which only the Err member is valid. -func (conn *Conn) Send(msg *Message, ch chan *Call) *Call { - var call *Call - - msg.serial = conn.getSerial() - if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 { - if ch == nil { - ch = make(chan *Call, 5) - } else if cap(ch) == 0 { - panic("dbus: unbuffered channel passed to (*Conn).Send") - } - call = new(Call) - call.Destination, _ = msg.Headers[FieldDestination].value.(string) - call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath) - iface, _ := msg.Headers[FieldInterface].value.(string) - member, _ := msg.Headers[FieldMember].value.(string) - call.Method = iface + "." + member - call.Args = msg.Body - call.Done = ch - conn.callsLck.Lock() - conn.calls[msg.serial] = call - conn.callsLck.Unlock() - conn.outLck.RLock() - if conn.closed { - call.Err = ErrClosed - call.Done <- call - } else { - conn.out <- msg - } - conn.outLck.RUnlock() - } else { - conn.outLck.RLock() - if conn.closed { - call = &Call{Err: ErrClosed} - } else { - conn.out <- msg - call = &Call{Err: nil} - } - conn.outLck.RUnlock() - } - return call -} - -// sendError creates an error message corresponding to the parameters and sends -// it to conn.out. -func (conn *Conn) sendError(e Error, dest string, serial uint32) { - msg := new(Message) - msg.Type = TypeError - msg.serial = conn.getSerial() - msg.Headers = make(map[HeaderField]Variant) - if dest != "" { - msg.Headers[FieldDestination] = MakeVariant(dest) - } - msg.Headers[FieldErrorName] = MakeVariant(e.Name) - msg.Headers[FieldReplySerial] = MakeVariant(serial) - msg.Body = e.Body - if len(e.Body) > 0 { - msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...)) - } - conn.outLck.RLock() - if !conn.closed { - conn.out <- msg - } - conn.outLck.RUnlock() -} - -// sendReply creates a method reply message corresponding to the parameters and -// sends it to conn.out. -func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) { - msg := new(Message) - msg.Type = TypeMethodReply - msg.serial = conn.getSerial() - msg.Headers = make(map[HeaderField]Variant) - if dest != "" { - msg.Headers[FieldDestination] = MakeVariant(dest) - } - msg.Headers[FieldReplySerial] = MakeVariant(serial) - msg.Body = values - if len(values) > 0 { - msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) - } - conn.outLck.RLock() - if !conn.closed { - conn.out <- msg - } - conn.outLck.RUnlock() -} - -// Signal registers the given channel to be passed all received signal messages. -// The caller has to make sure that ch is sufficiently buffered; if a message -// arrives when a write to c is not possible, it is discarded. -// -// Multiple of these channels can be registered at the same time. -// -// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a -// channel for eavesdropped messages, this channel receives all signals, and -// none of the channels passed to Signal will receive any signals. -func (conn *Conn) Signal(ch chan<- *Signal) { - conn.signalsLck.Lock() - conn.signals = append(conn.signals, ch) - conn.signalsLck.Unlock() -} - -// RemoveSignal removes the given channel from the list of the registered channels. -func (conn *Conn) RemoveSignal(ch chan<- *Signal) { - conn.signalsLck.Lock() - for i := len(conn.signals) - 1; i >= 0; i-- { - if ch == conn.signals[i] { - copy(conn.signals[i:], conn.signals[i+1:]) - conn.signals[len(conn.signals)-1] = nil - conn.signals = conn.signals[:len(conn.signals)-1] - } - } - conn.signalsLck.Unlock() -} - -// SupportsUnixFDs returns whether the underlying transport supports passing of -// unix file descriptors. If this is false, method calls containing unix file -// descriptors will return an error and emitted signals containing them will -// not be sent. -func (conn *Conn) SupportsUnixFDs() bool { - return conn.unixFD -} - -// Error represents a D-Bus message of type Error. -type Error struct { - Name string - Body []interface{} -} - -func NewError(name string, body []interface{}) *Error { - return &Error{name, body} -} - -func (e Error) Error() string { - if len(e.Body) >= 1 { - s, ok := e.Body[0].(string) - if ok { - return s - } - } - return e.Name -} - -// Signal represents a D-Bus message of type Signal. The name member is given in -// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost. -type Signal struct { - Sender string - Path ObjectPath - Name string - Body []interface{} -} - -// transport is a D-Bus transport. -type transport interface { - // Read and Write raw data (for example, for the authentication protocol). - io.ReadWriteCloser - - // Send the initial null byte used for the EXTERNAL mechanism. - SendNullByte() error - - // Returns whether this transport supports passing Unix FDs. - SupportsUnixFDs() bool - - // Signal the transport that Unix FD passing is enabled for this connection. - EnableUnixFDs() - - // Read / send a message, handling things like Unix FDs. - ReadMessage() (*Message, error) - SendMessage(*Message) error -} - -var ( - transports = make(map[string]func(string) (transport, error)) -) - -func getTransport(address string) (transport, error) { - var err error - var t transport - - addresses := strings.Split(address, ";") - for _, v := range addresses { - i := strings.IndexRune(v, ':') - if i == -1 { - err = errors.New("dbus: invalid bus address (no transport)") - continue - } - f := transports[v[:i]] - if f == nil { - err = errors.New("dbus: invalid bus address (invalid or unsupported transport)") - continue - } - t, err = f(v[i+1:]) - if err == nil { - return t, nil - } - } - return nil, err -} - -// dereferenceAll returns a slice that, assuming that vs is a slice of pointers -// of arbitrary types, containes the values that are obtained from dereferencing -// all elements in vs. -func dereferenceAll(vs []interface{}) []interface{} { - for i := range vs { - v := reflect.ValueOf(vs[i]) - v = v.Elem() - vs[i] = v.Interface() - } - return vs -} - -// getKey gets a key from a the list of keys. Returns "" on error / not found... -func getKey(s, key string) string { - for _, keyEqualsValue := range strings.Split(s, ",") { - keyValue := strings.SplitN(keyEqualsValue, "=", 2) - if len(keyValue) == 2 && keyValue[0] == key { - return keyValue[1] - } - } - return "" -} diff --git a/vendor/github.com/godbus/dbus/conn_darwin.go b/vendor/github.com/godbus/dbus/conn_darwin.go deleted file mode 100644 index b67bb1b81da68..0000000000000 --- a/vendor/github.com/godbus/dbus/conn_darwin.go +++ /dev/null @@ -1,21 +0,0 @@ -package dbus - -import ( - "errors" - "os/exec" -) - -func sessionBusPlatform() (*Conn, error) { - cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET") - b, err := cmd.CombinedOutput() - - if err != nil { - return nil, err - } - - if len(b) == 0 { - return nil, errors.New("dbus: couldn't determine address of session bus") - } - - return Dial("unix:path=" + string(b[:len(b)-1])) -} diff --git a/vendor/github.com/godbus/dbus/conn_other.go b/vendor/github.com/godbus/dbus/conn_other.go deleted file mode 100644 index 289e8c5d2be47..0000000000000 --- a/vendor/github.com/godbus/dbus/conn_other.go +++ /dev/null @@ -1,31 +0,0 @@ -// +build !darwin - -package dbus - -import ( - "bytes" - "errors" - "os" - "os/exec" -) - -func sessionBusPlatform() (*Conn, error) { - cmd := exec.Command("dbus-launch") - b, err := cmd.CombinedOutput() - - if err != nil { - return nil, err - } - - i := bytes.IndexByte(b, '=') - j := bytes.IndexByte(b, '\n') - - if i == -1 || j == -1 { - return nil, errors.New("dbus: couldn't determine address of session bus") - } - - env, addr := string(b[0:i]), string(b[i+1:j]) - os.Setenv(env, addr) - - return Dial(addr) -} diff --git a/vendor/github.com/godbus/dbus/dbus.go b/vendor/github.com/godbus/dbus/dbus.go deleted file mode 100644 index 2ce68735cdf92..0000000000000 --- a/vendor/github.com/godbus/dbus/dbus.go +++ /dev/null @@ -1,258 +0,0 @@ -package dbus - -import ( - "errors" - "reflect" - "strings" -) - -var ( - byteType = reflect.TypeOf(byte(0)) - boolType = reflect.TypeOf(false) - uint8Type = reflect.TypeOf(uint8(0)) - int16Type = reflect.TypeOf(int16(0)) - uint16Type = reflect.TypeOf(uint16(0)) - int32Type = reflect.TypeOf(int32(0)) - uint32Type = reflect.TypeOf(uint32(0)) - int64Type = reflect.TypeOf(int64(0)) - uint64Type = reflect.TypeOf(uint64(0)) - float64Type = reflect.TypeOf(float64(0)) - stringType = reflect.TypeOf("") - signatureType = reflect.TypeOf(Signature{""}) - objectPathType = reflect.TypeOf(ObjectPath("")) - variantType = reflect.TypeOf(Variant{Signature{""}, nil}) - interfacesType = reflect.TypeOf([]interface{}{}) - unixFDType = reflect.TypeOf(UnixFD(0)) - unixFDIndexType = reflect.TypeOf(UnixFDIndex(0)) -) - -// An InvalidTypeError signals that a value which cannot be represented in the -// D-Bus wire format was passed to a function. -type InvalidTypeError struct { - Type reflect.Type -} - -func (e InvalidTypeError) Error() string { - return "dbus: invalid type " + e.Type.String() -} - -// Store copies the values contained in src to dest, which must be a slice of -// pointers. It converts slices of interfaces from src to corresponding structs -// in dest. An error is returned if the lengths of src and dest or the types of -// their elements don't match. -func Store(src []interface{}, dest ...interface{}) error { - if len(src) != len(dest) { - return errors.New("dbus.Store: length mismatch") - } - - for i := range src { - if err := store(src[i], dest[i]); err != nil { - return err - } - } - return nil -} - -func store(src, dest interface{}) error { - if reflect.TypeOf(dest).Elem() == reflect.TypeOf(src) { - reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src)) - return nil - } else if hasStruct(dest) { - rv := reflect.ValueOf(dest).Elem() - switch rv.Kind() { - case reflect.Struct: - vs, ok := src.([]interface{}) - if !ok { - return errors.New("dbus.Store: type mismatch") - } - t := rv.Type() - ndest := make([]interface{}, 0, rv.NumField()) - for i := 0; i < rv.NumField(); i++ { - field := t.Field(i) - if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { - ndest = append(ndest, rv.Field(i).Addr().Interface()) - } - } - if len(vs) != len(ndest) { - return errors.New("dbus.Store: type mismatch") - } - err := Store(vs, ndest...) - if err != nil { - return errors.New("dbus.Store: type mismatch") - } - case reflect.Slice: - sv := reflect.ValueOf(src) - if sv.Kind() != reflect.Slice { - return errors.New("dbus.Store: type mismatch") - } - rv.Set(reflect.MakeSlice(rv.Type(), sv.Len(), sv.Len())) - for i := 0; i < sv.Len(); i++ { - if err := store(sv.Index(i).Interface(), rv.Index(i).Addr().Interface()); err != nil { - return err - } - } - case reflect.Map: - sv := reflect.ValueOf(src) - if sv.Kind() != reflect.Map { - return errors.New("dbus.Store: type mismatch") - } - keys := sv.MapKeys() - rv.Set(reflect.MakeMap(sv.Type())) - for _, key := range keys { - v := reflect.New(sv.Type().Elem()) - if err := store(v, sv.MapIndex(key).Interface()); err != nil { - return err - } - rv.SetMapIndex(key, v.Elem()) - } - default: - return errors.New("dbus.Store: type mismatch") - } - return nil - } else { - return errors.New("dbus.Store: type mismatch") - } -} - -func hasStruct(v interface{}) bool { - t := reflect.TypeOf(v) - for { - switch t.Kind() { - case reflect.Struct: - return true - case reflect.Slice, reflect.Ptr, reflect.Map: - t = t.Elem() - default: - return false - } - } -} - -// An ObjectPath is an object path as defined by the D-Bus spec. -type ObjectPath string - -// IsValid returns whether the object path is valid. -func (o ObjectPath) IsValid() bool { - s := string(o) - if len(s) == 0 { - return false - } - if s[0] != '/' { - return false - } - if s[len(s)-1] == '/' && len(s) != 1 { - return false - } - // probably not used, but technically possible - if s == "/" { - return true - } - split := strings.Split(s[1:], "/") - for _, v := range split { - if len(v) == 0 { - return false - } - for _, c := range v { - if !isMemberChar(c) { - return false - } - } - } - return true -} - -// A UnixFD is a Unix file descriptor sent over the wire. See the package-level -// documentation for more information about Unix file descriptor passsing. -type UnixFD int32 - -// A UnixFDIndex is the representation of a Unix file descriptor in a message. -type UnixFDIndex uint32 - -// alignment returns the alignment of values of type t. -func alignment(t reflect.Type) int { - switch t { - case variantType: - return 1 - case objectPathType: - return 4 - case signatureType: - return 1 - case interfacesType: // sometimes used for structs - return 8 - } - switch t.Kind() { - case reflect.Uint8: - return 1 - case reflect.Uint16, reflect.Int16: - return 2 - case reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map: - return 4 - case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct: - return 8 - case reflect.Ptr: - return alignment(t.Elem()) - } - return 1 -} - -// isKeyType returns whether t is a valid type for a D-Bus dict. -func isKeyType(t reflect.Type) bool { - switch t.Kind() { - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64, - reflect.String: - - return true - } - return false -} - -// isValidInterface returns whether s is a valid name for an interface. -func isValidInterface(s string) bool { - if len(s) == 0 || len(s) > 255 || s[0] == '.' { - return false - } - elem := strings.Split(s, ".") - if len(elem) < 2 { - return false - } - for _, v := range elem { - if len(v) == 0 { - return false - } - if v[0] >= '0' && v[0] <= '9' { - return false - } - for _, c := range v { - if !isMemberChar(c) { - return false - } - } - } - return true -} - -// isValidMember returns whether s is a valid name for a member. -func isValidMember(s string) bool { - if len(s) == 0 || len(s) > 255 { - return false - } - i := strings.Index(s, ".") - if i != -1 { - return false - } - if s[0] >= '0' && s[0] <= '9' { - return false - } - for _, c := range s { - if !isMemberChar(c) { - return false - } - } - return true -} - -func isMemberChar(c rune) bool { - return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || c == '_' -} diff --git a/vendor/github.com/godbus/dbus/decoder.go b/vendor/github.com/godbus/dbus/decoder.go deleted file mode 100644 index ef50dcab98d0d..0000000000000 --- a/vendor/github.com/godbus/dbus/decoder.go +++ /dev/null @@ -1,228 +0,0 @@ -package dbus - -import ( - "encoding/binary" - "io" - "reflect" -) - -type decoder struct { - in io.Reader - order binary.ByteOrder - pos int -} - -// newDecoder returns a new decoder that reads values from in. The input is -// expected to be in the given byte order. -func newDecoder(in io.Reader, order binary.ByteOrder) *decoder { - dec := new(decoder) - dec.in = in - dec.order = order - return dec -} - -// align aligns the input to the given boundary and panics on error. -func (dec *decoder) align(n int) { - if dec.pos%n != 0 { - newpos := (dec.pos + n - 1) & ^(n - 1) - empty := make([]byte, newpos-dec.pos) - if _, err := io.ReadFull(dec.in, empty); err != nil { - panic(err) - } - dec.pos = newpos - } -} - -// Calls binary.Read(dec.in, dec.order, v) and panics on read errors. -func (dec *decoder) binread(v interface{}) { - if err := binary.Read(dec.in, dec.order, v); err != nil { - panic(err) - } -} - -func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) { - defer func() { - var ok bool - v := recover() - if err, ok = v.(error); ok { - if err == io.EOF || err == io.ErrUnexpectedEOF { - err = FormatError("unexpected EOF") - } - } - }() - vs = make([]interface{}, 0) - s := sig.str - for s != "" { - err, rem := validSingle(s, 0) - if err != nil { - return nil, err - } - v := dec.decode(s[:len(s)-len(rem)], 0) - vs = append(vs, v) - s = rem - } - return vs, nil -} - -func (dec *decoder) decode(s string, depth int) interface{} { - dec.align(alignment(typeFor(s))) - switch s[0] { - case 'y': - var b [1]byte - if _, err := dec.in.Read(b[:]); err != nil { - panic(err) - } - dec.pos++ - return b[0] - case 'b': - i := dec.decode("u", depth).(uint32) - switch { - case i == 0: - return false - case i == 1: - return true - default: - panic(FormatError("invalid value for boolean")) - } - case 'n': - var i int16 - dec.binread(&i) - dec.pos += 2 - return i - case 'i': - var i int32 - dec.binread(&i) - dec.pos += 4 - return i - case 'x': - var i int64 - dec.binread(&i) - dec.pos += 8 - return i - case 'q': - var i uint16 - dec.binread(&i) - dec.pos += 2 - return i - case 'u': - var i uint32 - dec.binread(&i) - dec.pos += 4 - return i - case 't': - var i uint64 - dec.binread(&i) - dec.pos += 8 - return i - case 'd': - var f float64 - dec.binread(&f) - dec.pos += 8 - return f - case 's': - length := dec.decode("u", depth).(uint32) - b := make([]byte, int(length)+1) - if _, err := io.ReadFull(dec.in, b); err != nil { - panic(err) - } - dec.pos += int(length) + 1 - return string(b[:len(b)-1]) - case 'o': - return ObjectPath(dec.decode("s", depth).(string)) - case 'g': - length := dec.decode("y", depth).(byte) - b := make([]byte, int(length)+1) - if _, err := io.ReadFull(dec.in, b); err != nil { - panic(err) - } - dec.pos += int(length) + 1 - sig, err := ParseSignature(string(b[:len(b)-1])) - if err != nil { - panic(err) - } - return sig - case 'v': - if depth >= 64 { - panic(FormatError("input exceeds container depth limit")) - } - var variant Variant - sig := dec.decode("g", depth).(Signature) - if len(sig.str) == 0 { - panic(FormatError("variant signature is empty")) - } - err, rem := validSingle(sig.str, 0) - if err != nil { - panic(err) - } - if rem != "" { - panic(FormatError("variant signature has multiple types")) - } - variant.sig = sig - variant.value = dec.decode(sig.str, depth+1) - return variant - case 'h': - return UnixFDIndex(dec.decode("u", depth).(uint32)) - case 'a': - if len(s) > 1 && s[1] == '{' { - ksig := s[2:3] - vsig := s[3 : len(s)-1] - v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig))) - if depth >= 63 { - panic(FormatError("input exceeds container depth limit")) - } - length := dec.decode("u", depth).(uint32) - // Even for empty maps, the correct padding must be included - dec.align(8) - spos := dec.pos - for dec.pos < spos+int(length) { - dec.align(8) - if !isKeyType(v.Type().Key()) { - panic(InvalidTypeError{v.Type()}) - } - kv := dec.decode(ksig, depth+2) - vv := dec.decode(vsig, depth+2) - v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv)) - } - return v.Interface() - } - if depth >= 64 { - panic(FormatError("input exceeds container depth limit")) - } - length := dec.decode("u", depth).(uint32) - v := reflect.MakeSlice(reflect.SliceOf(typeFor(s[1:])), 0, int(length)) - // Even for empty arrays, the correct padding must be included - dec.align(alignment(typeFor(s[1:]))) - spos := dec.pos - for dec.pos < spos+int(length) { - ev := dec.decode(s[1:], depth+1) - v = reflect.Append(v, reflect.ValueOf(ev)) - } - return v.Interface() - case '(': - if depth >= 64 { - panic(FormatError("input exceeds container depth limit")) - } - dec.align(8) - v := make([]interface{}, 0) - s = s[1 : len(s)-1] - for s != "" { - err, rem := validSingle(s, 0) - if err != nil { - panic(err) - } - ev := dec.decode(s[:len(s)-len(rem)], depth+1) - v = append(v, ev) - s = rem - } - return v - default: - panic(SignatureError{Sig: s}) - } -} - -// A FormatError is an error in the wire format. -type FormatError string - -func (e FormatError) Error() string { - return "dbus: wire format error: " + string(e) -} diff --git a/vendor/github.com/godbus/dbus/doc.go b/vendor/github.com/godbus/dbus/doc.go deleted file mode 100644 index deff554a381ad..0000000000000 --- a/vendor/github.com/godbus/dbus/doc.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Package dbus implements bindings to the D-Bus message bus system. - -To use the message bus API, you first need to connect to a bus (usually the -session or system bus). The acquired connection then can be used to call methods -on remote objects and emit or receive signals. Using the Export method, you can -arrange D-Bus methods calls to be directly translated to method calls on a Go -value. - -Conversion Rules - -For outgoing messages, Go types are automatically converted to the -corresponding D-Bus types. The following types are directly encoded as their -respective D-Bus equivalents: - - Go type | D-Bus type - ------------+----------- - byte | BYTE - bool | BOOLEAN - int16 | INT16 - uint16 | UINT16 - int32 | INT32 - uint32 | UINT32 - int64 | INT64 - uint64 | UINT64 - float64 | DOUBLE - string | STRING - ObjectPath | OBJECT_PATH - Signature | SIGNATURE - Variant | VARIANT - UnixFDIndex | UNIX_FD - -Slices and arrays encode as ARRAYs of their element type. - -Maps encode as DICTs, provided that their key type can be used as a key for -a DICT. - -Structs other than Variant and Signature encode as a STRUCT containing their -exported fields. Fields whose tags contain `dbus:"-"` and unexported fields will -be skipped. - -Pointers encode as the value they're pointed to. - -Trying to encode any other type or a slice, map or struct containing an -unsupported type will result in an InvalidTypeError. - -For incoming messages, the inverse of these rules are used, with the exception -of STRUCTs. Incoming STRUCTS are represented as a slice of empty interfaces -containing the struct fields in the correct order. The Store function can be -used to convert such values to Go structs. - -Unix FD passing - -Handling Unix file descriptors deserves special mention. To use them, you should -first check that they are supported on a connection by calling SupportsUnixFDs. -If it returns true, all method of Connection will translate messages containing -UnixFD's to messages that are accompanied by the given file descriptors with the -UnixFD values being substituted by the correct indices. Similarily, the indices -of incoming messages are automatically resolved. It shouldn't be necessary to use -UnixFDIndex. - -*/ -package dbus diff --git a/vendor/github.com/godbus/dbus/encoder.go b/vendor/github.com/godbus/dbus/encoder.go deleted file mode 100644 index 9f0a9e89eabfe..0000000000000 --- a/vendor/github.com/godbus/dbus/encoder.go +++ /dev/null @@ -1,208 +0,0 @@ -package dbus - -import ( - "bytes" - "encoding/binary" - "io" - "reflect" -) - -// An encoder encodes values to the D-Bus wire format. -type encoder struct { - out io.Writer - order binary.ByteOrder - pos int -} - -// NewEncoder returns a new encoder that writes to out in the given byte order. -func newEncoder(out io.Writer, order binary.ByteOrder) *encoder { - return newEncoderAtOffset(out, 0, order) -} - -// newEncoderAtOffset returns a new encoder that writes to out in the given -// byte order. Specify the offset to initialize pos for proper alignment -// computation. -func newEncoderAtOffset(out io.Writer, offset int, order binary.ByteOrder) *encoder { - enc := new(encoder) - enc.out = out - enc.order = order - enc.pos = offset - return enc -} - -// Aligns the next output to be on a multiple of n. Panics on write errors. -func (enc *encoder) align(n int) { - pad := enc.padding(0, n) - if pad > 0 { - empty := make([]byte, pad) - if _, err := enc.out.Write(empty); err != nil { - panic(err) - } - enc.pos += pad - } -} - -// pad returns the number of bytes of padding, based on current position and additional offset. -// and alignment. -func (enc *encoder) padding(offset, algn int) int { - abs := enc.pos + offset - if abs%algn != 0 { - newabs := (abs + algn - 1) & ^(algn - 1) - return newabs - abs - } - return 0 -} - -// Calls binary.Write(enc.out, enc.order, v) and panics on write errors. -func (enc *encoder) binwrite(v interface{}) { - if err := binary.Write(enc.out, enc.order, v); err != nil { - panic(err) - } -} - -// Encode encodes the given values to the underyling reader. All written values -// are aligned properly as required by the D-Bus spec. -func (enc *encoder) Encode(vs ...interface{}) (err error) { - defer func() { - err, _ = recover().(error) - }() - for _, v := range vs { - enc.encode(reflect.ValueOf(v), 0) - } - return nil -} - -// encode encodes the given value to the writer and panics on error. depth holds -// the depth of the container nesting. -func (enc *encoder) encode(v reflect.Value, depth int) { - enc.align(alignment(v.Type())) - switch v.Kind() { - case reflect.Uint8: - var b [1]byte - b[0] = byte(v.Uint()) - if _, err := enc.out.Write(b[:]); err != nil { - panic(err) - } - enc.pos++ - case reflect.Bool: - if v.Bool() { - enc.encode(reflect.ValueOf(uint32(1)), depth) - } else { - enc.encode(reflect.ValueOf(uint32(0)), depth) - } - case reflect.Int16: - enc.binwrite(int16(v.Int())) - enc.pos += 2 - case reflect.Uint16: - enc.binwrite(uint16(v.Uint())) - enc.pos += 2 - case reflect.Int32: - enc.binwrite(int32(v.Int())) - enc.pos += 4 - case reflect.Uint32: - enc.binwrite(uint32(v.Uint())) - enc.pos += 4 - case reflect.Int64: - enc.binwrite(v.Int()) - enc.pos += 8 - case reflect.Uint64: - enc.binwrite(v.Uint()) - enc.pos += 8 - case reflect.Float64: - enc.binwrite(v.Float()) - enc.pos += 8 - case reflect.String: - enc.encode(reflect.ValueOf(uint32(len(v.String()))), depth) - b := make([]byte, v.Len()+1) - copy(b, v.String()) - b[len(b)-1] = 0 - n, err := enc.out.Write(b) - if err != nil { - panic(err) - } - enc.pos += n - case reflect.Ptr: - enc.encode(v.Elem(), depth) - case reflect.Slice, reflect.Array: - if depth >= 64 { - panic(FormatError("input exceeds container depth limit")) - } - // Lookahead offset: 4 bytes for uint32 length (with alignment), - // plus alignment for elements. - n := enc.padding(0, 4) + 4 - offset := enc.pos + n + enc.padding(n, alignment(v.Type().Elem())) - - var buf bytes.Buffer - bufenc := newEncoderAtOffset(&buf, offset, enc.order) - - for i := 0; i < v.Len(); i++ { - bufenc.encode(v.Index(i), depth+1) - } - enc.encode(reflect.ValueOf(uint32(buf.Len())), depth) - length := buf.Len() - enc.align(alignment(v.Type().Elem())) - if _, err := buf.WriteTo(enc.out); err != nil { - panic(err) - } - enc.pos += length - case reflect.Struct: - if depth >= 64 && v.Type() != signatureType { - panic(FormatError("input exceeds container depth limit")) - } - switch t := v.Type(); t { - case signatureType: - str := v.Field(0) - enc.encode(reflect.ValueOf(byte(str.Len())), depth+1) - b := make([]byte, str.Len()+1) - copy(b, str.String()) - b[len(b)-1] = 0 - n, err := enc.out.Write(b) - if err != nil { - panic(err) - } - enc.pos += n - case variantType: - variant := v.Interface().(Variant) - enc.encode(reflect.ValueOf(variant.sig), depth+1) - enc.encode(reflect.ValueOf(variant.value), depth+1) - default: - for i := 0; i < v.Type().NumField(); i++ { - field := t.Field(i) - if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { - enc.encode(v.Field(i), depth+1) - } - } - } - case reflect.Map: - // Maps are arrays of structures, so they actually increase the depth by - // 2. - if depth >= 63 { - panic(FormatError("input exceeds container depth limit")) - } - if !isKeyType(v.Type().Key()) { - panic(InvalidTypeError{v.Type()}) - } - keys := v.MapKeys() - // Lookahead offset: 4 bytes for uint32 length (with alignment), - // plus 8-byte alignment - n := enc.padding(0, 4) + 4 - offset := enc.pos + n + enc.padding(n, 8) - - var buf bytes.Buffer - bufenc := newEncoderAtOffset(&buf, offset, enc.order) - for _, k := range keys { - bufenc.align(8) - bufenc.encode(k, depth+2) - bufenc.encode(v.MapIndex(k), depth+2) - } - enc.encode(reflect.ValueOf(uint32(buf.Len())), depth) - length := buf.Len() - enc.align(8) - if _, err := buf.WriteTo(enc.out); err != nil { - panic(err) - } - enc.pos += length - default: - panic(InvalidTypeError{v.Type()}) - } -} diff --git a/vendor/github.com/godbus/dbus/export.go b/vendor/github.com/godbus/dbus/export.go deleted file mode 100644 index 6c3352202665a..0000000000000 --- a/vendor/github.com/godbus/dbus/export.go +++ /dev/null @@ -1,468 +0,0 @@ -package dbus - -import ( - "bytes" - "errors" - "fmt" - "reflect" - "strings" -) - -var ( - errmsgInvalidArg = Error{ - "org.freedesktop.DBus.Error.InvalidArgs", - []interface{}{"Invalid type / number of args"}, - } - errmsgNoObject = Error{ - "org.freedesktop.DBus.Error.NoSuchObject", - []interface{}{"No such object"}, - } - errmsgUnknownMethod = Error{ - "org.freedesktop.DBus.Error.UnknownMethod", - []interface{}{"Unknown / invalid method"}, - } -) - -// exportedObj represents an exported object. It stores a precomputed -// method table that represents the methods exported on the bus. -type exportedObj struct { - methods map[string]reflect.Value - - // Whether or not this export is for the entire subtree - includeSubtree bool -} - -func (obj exportedObj) Method(name string) (reflect.Value, bool) { - out, exists := obj.methods[name] - return out, exists -} - -// Sender is a type which can be used in exported methods to receive the message -// sender. -type Sender string - -func computeMethodName(name string, mapping map[string]string) string { - newname, ok := mapping[name] - if ok { - name = newname - } - return name -} - -func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value { - if in == nil { - return nil - } - methods := make(map[string]reflect.Value) - val := reflect.ValueOf(in) - typ := val.Type() - for i := 0; i < typ.NumMethod(); i++ { - methtype := typ.Method(i) - method := val.Method(i) - t := method.Type() - // only track valid methods must return *Error as last arg - // and must be exported - if t.NumOut() == 0 || - t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) || - methtype.PkgPath != "" { - continue - } - // map names while building table - methods[computeMethodName(methtype.Name, mapping)] = method - } - return methods -} - -// searchHandlers will look through all registered handlers looking for one -// to handle the given path. If a verbatim one isn't found, it will check for -// a subtree registration for the path as well. -func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportedObj, bool) { - conn.handlersLck.RLock() - defer conn.handlersLck.RUnlock() - - handlers, ok := conn.handlers[path] - if ok { - return handlers, ok - } - - // If handlers weren't found for this exact path, look for a matching subtree - // registration - handlers = make(map[string]exportedObj) - path = path[:strings.LastIndex(string(path), "/")] - for len(path) > 0 { - var subtreeHandlers map[string]exportedObj - subtreeHandlers, ok = conn.handlers[path] - if ok { - for iface, handler := range subtreeHandlers { - // Only include this handler if it registered for the subtree - if handler.includeSubtree { - handlers[iface] = handler - } - } - - break - } - - path = path[:strings.LastIndex(string(path), "/")] - } - - return handlers, ok -} - -// handleCall handles the given method call (i.e. looks if it's one of the -// pre-implemented ones and searches for a corresponding handler if not). -func (conn *Conn) handleCall(msg *Message) { - name := msg.Headers[FieldMember].value.(string) - path := msg.Headers[FieldPath].value.(ObjectPath) - ifaceName, hasIface := msg.Headers[FieldInterface].value.(string) - sender, hasSender := msg.Headers[FieldSender].value.(string) - serial := msg.serial - if ifaceName == "org.freedesktop.DBus.Peer" { - switch name { - case "Ping": - conn.sendReply(sender, serial) - case "GetMachineId": - conn.sendReply(sender, serial, conn.uuid) - default: - conn.sendError(errmsgUnknownMethod, sender, serial) - } - return - } else if ifaceName == "org.freedesktop.DBus.Introspectable" && name == "Introspect" { - if _, ok := conn.handlers[path]; !ok { - subpath := make(map[string]struct{}) - var xml bytes.Buffer - xml.WriteString("") - for h, _ := range conn.handlers { - p := string(path) - if p != "/" { - p += "/" - } - if strings.HasPrefix(string(h), p) { - node_name := strings.Split(string(h[len(p):]), "/")[0] - subpath[node_name] = struct{}{} - } - } - for s, _ := range subpath { - xml.WriteString("\n\t") - } - xml.WriteString("\n") - conn.sendReply(sender, serial, xml.String()) - return - } - } - if len(name) == 0 { - conn.sendError(errmsgUnknownMethod, sender, serial) - } - - // Find the exported handler (if any) for this path - handlers, ok := conn.searchHandlers(path) - if !ok { - conn.sendError(errmsgNoObject, sender, serial) - return - } - - var m reflect.Value - var exists bool - if hasIface { - iface := handlers[ifaceName] - m, exists = iface.Method(name) - } else { - for _, v := range handlers { - m, exists = v.Method(name) - if exists { - break - } - } - } - - if !exists { - conn.sendError(errmsgUnknownMethod, sender, serial) - return - } - - t := m.Type() - vs := msg.Body - pointers := make([]interface{}, t.NumIn()) - decode := make([]interface{}, 0, len(vs)) - for i := 0; i < t.NumIn(); i++ { - tp := t.In(i) - val := reflect.New(tp) - pointers[i] = val.Interface() - if tp == reflect.TypeOf((*Sender)(nil)).Elem() { - val.Elem().SetString(sender) - } else if tp == reflect.TypeOf((*Message)(nil)).Elem() { - val.Elem().Set(reflect.ValueOf(*msg)) - } else { - decode = append(decode, pointers[i]) - } - } - - if len(decode) != len(vs) { - conn.sendError(errmsgInvalidArg, sender, serial) - return - } - - if err := Store(vs, decode...); err != nil { - conn.sendError(errmsgInvalidArg, sender, serial) - return - } - - // Extract parameters - params := make([]reflect.Value, len(pointers)) - for i := 0; i < len(pointers); i++ { - params[i] = reflect.ValueOf(pointers[i]).Elem() - } - - // Call method - ret := m.Call(params) - if em := ret[t.NumOut()-1].Interface().(*Error); em != nil { - conn.sendError(*em, sender, serial) - return - } - - if msg.Flags&FlagNoReplyExpected == 0 { - reply := new(Message) - reply.Type = TypeMethodReply - reply.serial = conn.getSerial() - reply.Headers = make(map[HeaderField]Variant) - if hasSender { - reply.Headers[FieldDestination] = msg.Headers[FieldSender] - } - reply.Headers[FieldReplySerial] = MakeVariant(msg.serial) - reply.Body = make([]interface{}, len(ret)-1) - for i := 0; i < len(ret)-1; i++ { - reply.Body[i] = ret[i].Interface() - } - if len(ret) != 1 { - reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) - } - conn.outLck.RLock() - if !conn.closed { - conn.out <- reply - } - conn.outLck.RUnlock() - } -} - -// Emit emits the given signal on the message bus. The name parameter must be -// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost". -func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error { - if !path.IsValid() { - return errors.New("dbus: invalid object path") - } - i := strings.LastIndex(name, ".") - if i == -1 { - return errors.New("dbus: invalid method name") - } - iface := name[:i] - member := name[i+1:] - if !isValidMember(member) { - return errors.New("dbus: invalid method name") - } - if !isValidInterface(iface) { - return errors.New("dbus: invalid interface name") - } - msg := new(Message) - msg.Type = TypeSignal - msg.serial = conn.getSerial() - msg.Headers = make(map[HeaderField]Variant) - msg.Headers[FieldInterface] = MakeVariant(iface) - msg.Headers[FieldMember] = MakeVariant(member) - msg.Headers[FieldPath] = MakeVariant(path) - msg.Body = values - if len(values) > 0 { - msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) - } - conn.outLck.RLock() - defer conn.outLck.RUnlock() - if conn.closed { - return ErrClosed - } - conn.out <- msg - return nil -} - -// Export registers the given value to be exported as an object on the -// message bus. -// -// If a method call on the given path and interface is received, an exported -// method with the same name is called with v as the receiver if the -// parameters match and the last return value is of type *Error. If this -// *Error is not nil, it is sent back to the caller as an error. -// Otherwise, a method reply is sent with the other return values as its body. -// -// Any parameters with the special type Sender are set to the sender of the -// dbus message when the method is called. Parameters of this type do not -// contribute to the dbus signature of the method (i.e. the method is exposed -// as if the parameters of type Sender were not there). -// -// Similarly, any parameters with the type Message are set to the raw message -// received on the bus. Again, parameters of this type do not contribute to the -// dbus signature of the method. -// -// Every method call is executed in a new goroutine, so the method may be called -// in multiple goroutines at once. -// -// Method calls on the interface org.freedesktop.DBus.Peer will be automatically -// handled for every object. -// -// Passing nil as the first parameter will cause conn to cease handling calls on -// the given combination of path and interface. -// -// Export returns an error if path is not a valid path name. -func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error { - return conn.ExportWithMap(v, nil, path, iface) -} - -// ExportWithMap works exactly like Export but provides the ability to remap -// method names (e.g. export a lower-case method). -// -// The keys in the map are the real method names (exported on the struct), and -// the values are the method names to be exported on DBus. -func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error { - return conn.export(getMethods(v, mapping), path, iface, false) -} - -// ExportSubtree works exactly like Export but registers the given value for -// an entire subtree rather under the root path provided. -// -// In order to make this useful, one parameter in each of the value's exported -// methods should be a Message, in which case it will contain the raw message -// (allowing one to get access to the path that caused the method to be called). -// -// Note that more specific export paths take precedence over less specific. For -// example, a method call using the ObjectPath /foo/bar/baz will call a method -// exported on /foo/bar before a method exported on /foo. -func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error { - return conn.ExportSubtreeWithMap(v, nil, path, iface) -} - -// ExportSubtreeWithMap works exactly like ExportSubtree but provides the -// ability to remap method names (e.g. export a lower-case method). -// -// The keys in the map are the real method names (exported on the struct), and -// the values are the method names to be exported on DBus. -func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error { - return conn.export(getMethods(v, mapping), path, iface, true) -} - -// ExportMethodTable like Export registers the given methods as an object -// on the message bus. Unlike Export the it uses a method table to define -// the object instead of a native go object. -// -// The method table is a map from method name to function closure -// representing the method. This allows an object exported on the bus to not -// necessarily be a native go object. It can be useful for generating exposed -// methods on the fly. -// -// Any non-function objects in the method table are ignored. -func (conn *Conn) ExportMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error { - return conn.exportMethodTable(methods, path, iface, false) -} - -// Like ExportSubtree, but with the same caveats as ExportMethodTable. -func (conn *Conn) ExportSubtreeMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error { - return conn.exportMethodTable(methods, path, iface, true) -} - -func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectPath, iface string, includeSubtree bool) error { - out := make(map[string]reflect.Value) - for name, method := range methods { - rval := reflect.ValueOf(method) - if rval.Kind() != reflect.Func { - continue - } - t := rval.Type() - // only track valid methods must return *Error as last arg - if t.NumOut() == 0 || - t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) { - continue - } - out[name] = rval - } - return conn.export(out, path, iface, includeSubtree) -} - -// exportWithMap is the worker function for all exports/registrations. -func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error { - if !path.IsValid() { - return fmt.Errorf(`dbus: Invalid path name: "%s"`, path) - } - - conn.handlersLck.Lock() - defer conn.handlersLck.Unlock() - - // Remove a previous export if the interface is nil - if methods == nil { - if _, ok := conn.handlers[path]; ok { - delete(conn.handlers[path], iface) - if len(conn.handlers[path]) == 0 { - delete(conn.handlers, path) - } - } - - return nil - } - - // If this is the first handler for this path, make a new map to hold all - // handlers for this path. - if _, ok := conn.handlers[path]; !ok { - conn.handlers[path] = make(map[string]exportedObj) - } - - // Finally, save this handler - conn.handlers[path][iface] = exportedObj{ - methods: methods, - includeSubtree: includeSubtree, - } - - return nil -} - -// ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response. -func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) { - var r uint32 - err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r) - if err != nil { - return 0, err - } - return ReleaseNameReply(r), nil -} - -// RequestName calls org.freedesktop.DBus.RequestName and awaits a response. -func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) { - var r uint32 - err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r) - if err != nil { - return 0, err - } - return RequestNameReply(r), nil -} - -// ReleaseNameReply is the reply to a ReleaseName call. -type ReleaseNameReply uint32 - -const ( - ReleaseNameReplyReleased ReleaseNameReply = 1 + iota - ReleaseNameReplyNonExistent - ReleaseNameReplyNotOwner -) - -// RequestNameFlags represents the possible flags for a RequestName call. -type RequestNameFlags uint32 - -const ( - NameFlagAllowReplacement RequestNameFlags = 1 << iota - NameFlagReplaceExisting - NameFlagDoNotQueue -) - -// RequestNameReply is the reply to a RequestName call. -type RequestNameReply uint32 - -const ( - RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota - RequestNameReplyInQueue - RequestNameReplyExists - RequestNameReplyAlreadyOwner -) diff --git a/vendor/github.com/godbus/dbus/object.go b/vendor/github.com/godbus/dbus/object.go deleted file mode 100644 index 9573b7095ab99..0000000000000 --- a/vendor/github.com/godbus/dbus/object.go +++ /dev/null @@ -1,136 +0,0 @@ -package dbus - -import ( - "errors" - "strings" -) - -// BusObject is the interface of a remote object on which methods can be -// invoked. -type BusObject interface { - Call(method string, flags Flags, args ...interface{}) *Call - Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call - GetProperty(p string) (Variant, error) - Destination() string - Path() ObjectPath -} - -// Object represents a remote object on which methods can be invoked. -type Object struct { - conn *Conn - dest string - path ObjectPath -} - -// Call calls a method with (*Object).Go and waits for its reply. -func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call { - return <-o.Go(method, flags, make(chan *Call, 1), args...).Done -} - -// AddMatchSignal subscribes BusObject to signals from specified interface and -// method (member). -func (o *Object) AddMatchSignal(iface, member string) *Call { - return o.Call( - "org.freedesktop.DBus.AddMatch", - 0, - "type='signal',interface='"+iface+"',member='"+member+"'", - ) -} - -// Go calls a method with the given arguments asynchronously. It returns a -// Call structure representing this method call. The passed channel will -// return the same value once the call is done. If ch is nil, a new channel -// will be allocated. Otherwise, ch has to be buffered or Go will panic. -// -// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure -// is returned of which only the Err member is valid. -// -// If the method parameter contains a dot ('.'), the part before the last dot -// specifies the interface on which the method is called. -func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call { - iface := "" - i := strings.LastIndex(method, ".") - if i != -1 { - iface = method[:i] - } - method = method[i+1:] - msg := new(Message) - msg.Type = TypeMethodCall - msg.serial = o.conn.getSerial() - msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected) - msg.Headers = make(map[HeaderField]Variant) - msg.Headers[FieldPath] = MakeVariant(o.path) - msg.Headers[FieldDestination] = MakeVariant(o.dest) - msg.Headers[FieldMember] = MakeVariant(method) - if iface != "" { - msg.Headers[FieldInterface] = MakeVariant(iface) - } - msg.Body = args - if len(args) > 0 { - msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...)) - } - if msg.Flags&FlagNoReplyExpected == 0 { - if ch == nil { - ch = make(chan *Call, 10) - } else if cap(ch) == 0 { - panic("dbus: unbuffered channel passed to (*Object).Go") - } - call := &Call{ - Destination: o.dest, - Path: o.path, - Method: method, - Args: args, - Done: ch, - } - o.conn.callsLck.Lock() - o.conn.calls[msg.serial] = call - o.conn.callsLck.Unlock() - o.conn.outLck.RLock() - if o.conn.closed { - call.Err = ErrClosed - call.Done <- call - } else { - o.conn.out <- msg - } - o.conn.outLck.RUnlock() - return call - } - o.conn.outLck.RLock() - defer o.conn.outLck.RUnlock() - if o.conn.closed { - return &Call{Err: ErrClosed} - } - o.conn.out <- msg - return &Call{Err: nil} -} - -// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given -// object. The property name must be given in interface.member notation. -func (o *Object) GetProperty(p string) (Variant, error) { - idx := strings.LastIndex(p, ".") - if idx == -1 || idx+1 == len(p) { - return Variant{}, errors.New("dbus: invalid property " + p) - } - - iface := p[:idx] - prop := p[idx+1:] - - result := Variant{} - err := o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop).Store(&result) - - if err != nil { - return Variant{}, err - } - - return result, nil -} - -// Destination returns the destination that calls on o are sent to. -func (o *Object) Destination() string { - return o.dest -} - -// Path returns the path that calls on o are sent to. -func (o *Object) Path() ObjectPath { - return o.path -} diff --git a/vendor/github.com/godbus/dbus/transport_generic.go b/vendor/github.com/godbus/dbus/transport_generic.go deleted file mode 100644 index 46f8f49d699b7..0000000000000 --- a/vendor/github.com/godbus/dbus/transport_generic.go +++ /dev/null @@ -1,35 +0,0 @@ -package dbus - -import ( - "encoding/binary" - "errors" - "io" -) - -type genericTransport struct { - io.ReadWriteCloser -} - -func (t genericTransport) SendNullByte() error { - _, err := t.Write([]byte{0}) - return err -} - -func (t genericTransport) SupportsUnixFDs() bool { - return false -} - -func (t genericTransport) EnableUnixFDs() {} - -func (t genericTransport) ReadMessage() (*Message, error) { - return DecodeMessage(t) -} - -func (t genericTransport) SendMessage(msg *Message) error { - for _, v := range msg.Body { - if _, ok := v.(UnixFD); ok { - return errors.New("dbus: unix fd passing not enabled") - } - } - return msg.EncodeTo(t, binary.LittleEndian) -} diff --git a/vendor/github.com/godbus/dbus/LICENSE b/vendor/github.com/godbus/dbus/v5/LICENSE similarity index 100% rename from vendor/github.com/godbus/dbus/LICENSE rename to vendor/github.com/godbus/dbus/v5/LICENSE diff --git a/vendor/github.com/godbus/dbus/v5/README.markdown b/vendor/github.com/godbus/dbus/v5/README.markdown new file mode 100644 index 0000000000000..1fb2eacaa15a7 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/README.markdown @@ -0,0 +1,46 @@ +![Build Status](https://github.com/godbus/dbus/workflows/Go/badge.svg) + +dbus +---- + +dbus is a simple library that implements native Go client bindings for the +D-Bus message bus system. + +### Features + +* Complete native implementation of the D-Bus message protocol +* Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections) +* Subpackages that help with the introspection / property interfaces + +### Installation + +This packages requires Go 1.7. If you installed it and set up your GOPATH, just run: + +``` +go get github.com/godbus/dbus +``` + +If you want to use the subpackages, you can install them the same way. + +### Usage + +The complete package documentation and some simple examples are available at +[godoc.org](http://godoc.org/github.com/godbus/dbus). Also, the +[_examples](https://github.com/godbus/dbus/tree/master/_examples) directory +gives a short overview over the basic usage. + +#### Projects using godbus +- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library. +- [go-bluetooth](https://github.com/muka/go-bluetooth) provides a bluetooth client over bluez dbus API. +- [playerbm](https://github.com/altdesktop/playerbm) a bookmark utility for media players. +- [iwd](https://github.com/shibumi/iwd) go bindings for the internet wireless daemon "iwd". + +Please note that the API is considered unstable for now and may change without +further notice. + +### License + +go.dbus is available under the Simplified BSD License; see LICENSE for the full +text. + +Nearly all of the credit for this library goes to github.com/guelfey/go.dbus. diff --git a/vendor/github.com/godbus/dbus/v5/auth.go b/vendor/github.com/godbus/dbus/v5/auth.go new file mode 100644 index 0000000000000..283487a0e3138 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/auth.go @@ -0,0 +1,252 @@ +package dbus + +import ( + "bufio" + "bytes" + "errors" + "io" + "os" + "strconv" +) + +// AuthStatus represents the Status of an authentication mechanism. +type AuthStatus byte + +const ( + // AuthOk signals that authentication is finished; the next command + // from the server should be an OK. + AuthOk AuthStatus = iota + + // AuthContinue signals that additional data is needed; the next command + // from the server should be a DATA. + AuthContinue + + // AuthError signals an error; the server sent invalid data or some + // other unexpected thing happened and the current authentication + // process should be aborted. + AuthError +) + +type authState byte + +const ( + waitingForData authState = iota + waitingForOk + waitingForReject +) + +// Auth defines the behaviour of an authentication mechanism. +type Auth interface { + // Return the name of the mechanism, the argument to the first AUTH command + // and the next status. + FirstData() (name, resp []byte, status AuthStatus) + + // Process the given DATA command, and return the argument to the DATA + // command and the next status. If len(resp) == 0, no DATA command is sent. + HandleData(data []byte) (resp []byte, status AuthStatus) +} + +// Auth authenticates the connection, trying the given list of authentication +// mechanisms (in that order). If nil is passed, the EXTERNAL and +// DBUS_COOKIE_SHA1 mechanisms are tried for the current user. For private +// connections, this method must be called before sending any messages to the +// bus. Auth must not be called on shared connections. +func (conn *Conn) Auth(methods []Auth) error { + if methods == nil { + uid := strconv.Itoa(os.Getuid()) + methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())} + } + in := bufio.NewReader(conn.transport) + err := conn.transport.SendNullByte() + if err != nil { + return err + } + err = authWriteLine(conn.transport, []byte("AUTH")) + if err != nil { + return err + } + s, err := authReadLine(in) + if err != nil { + return err + } + if len(s) < 2 || !bytes.Equal(s[0], []byte("REJECTED")) { + return errors.New("dbus: authentication protocol error") + } + s = s[1:] + for _, v := range s { + for _, m := range methods { + if name, data, status := m.FirstData(); bytes.Equal(v, name) { + var ok bool + err = authWriteLine(conn.transport, []byte("AUTH"), v, data) + if err != nil { + return err + } + switch status { + case AuthOk: + err, ok = conn.tryAuth(m, waitingForOk, in) + case AuthContinue: + err, ok = conn.tryAuth(m, waitingForData, in) + default: + panic("dbus: invalid authentication status") + } + if err != nil { + return err + } + if ok { + if conn.transport.SupportsUnixFDs() { + err = authWriteLine(conn, []byte("NEGOTIATE_UNIX_FD")) + if err != nil { + return err + } + line, err := authReadLine(in) + if err != nil { + return err + } + switch { + case bytes.Equal(line[0], []byte("AGREE_UNIX_FD")): + conn.EnableUnixFDs() + conn.unixFD = true + case bytes.Equal(line[0], []byte("ERROR")): + default: + return errors.New("dbus: authentication protocol error") + } + } + err = authWriteLine(conn.transport, []byte("BEGIN")) + if err != nil { + return err + } + go conn.inWorker() + return nil + } + } + } + } + return errors.New("dbus: authentication failed") +} + +// tryAuth tries to authenticate with m as the mechanism, using state as the +// initial authState and in for reading input. It returns (nil, true) on +// success, (nil, false) on a REJECTED and (someErr, false) if some other +// error occurred. +func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) { + for { + s, err := authReadLine(in) + if err != nil { + return err, false + } + switch { + case state == waitingForData && string(s[0]) == "DATA": + if len(s) != 2 { + err = authWriteLine(conn.transport, []byte("ERROR")) + if err != nil { + return err, false + } + continue + } + data, status := m.HandleData(s[1]) + switch status { + case AuthOk, AuthContinue: + if len(data) != 0 { + err = authWriteLine(conn.transport, []byte("DATA"), data) + if err != nil { + return err, false + } + } + if status == AuthOk { + state = waitingForOk + } + case AuthError: + err = authWriteLine(conn.transport, []byte("ERROR")) + if err != nil { + return err, false + } + } + case state == waitingForData && string(s[0]) == "REJECTED": + return nil, false + case state == waitingForData && string(s[0]) == "ERROR": + err = authWriteLine(conn.transport, []byte("CANCEL")) + if err != nil { + return err, false + } + state = waitingForReject + case state == waitingForData && string(s[0]) == "OK": + if len(s) != 2 { + err = authWriteLine(conn.transport, []byte("CANCEL")) + if err != nil { + return err, false + } + state = waitingForReject + } + conn.uuid = string(s[1]) + return nil, true + case state == waitingForData: + err = authWriteLine(conn.transport, []byte("ERROR")) + if err != nil { + return err, false + } + case state == waitingForOk && string(s[0]) == "OK": + if len(s) != 2 { + err = authWriteLine(conn.transport, []byte("CANCEL")) + if err != nil { + return err, false + } + state = waitingForReject + } + conn.uuid = string(s[1]) + return nil, true + case state == waitingForOk && string(s[0]) == "REJECTED": + return nil, false + case state == waitingForOk && (string(s[0]) == "DATA" || + string(s[0]) == "ERROR"): + + err = authWriteLine(conn.transport, []byte("CANCEL")) + if err != nil { + return err, false + } + state = waitingForReject + case state == waitingForOk: + err = authWriteLine(conn.transport, []byte("ERROR")) + if err != nil { + return err, false + } + case state == waitingForReject && string(s[0]) == "REJECTED": + return nil, false + case state == waitingForReject: + return errors.New("dbus: authentication protocol error"), false + default: + panic("dbus: invalid auth state") + } + } +} + +// authReadLine reads a line and separates it into its fields. +func authReadLine(in *bufio.Reader) ([][]byte, error) { + data, err := in.ReadBytes('\n') + if err != nil { + return nil, err + } + data = bytes.TrimSuffix(data, []byte("\r\n")) + return bytes.Split(data, []byte{' '}), nil +} + +// authWriteLine writes the given line in the authentication protocol format +// (elements of data separated by a " " and terminated by "\r\n"). +func authWriteLine(out io.Writer, data ...[]byte) error { + buf := make([]byte, 0) + for i, v := range data { + buf = append(buf, v...) + if i != len(data)-1 { + buf = append(buf, ' ') + } + } + buf = append(buf, '\r') + buf = append(buf, '\n') + n, err := out.Write(buf) + if err != nil { + return err + } + if n != len(buf) { + return io.ErrUnexpectedEOF + } + return nil +} diff --git a/vendor/github.com/godbus/dbus/v5/auth_anonymous.go b/vendor/github.com/godbus/dbus/v5/auth_anonymous.go new file mode 100644 index 0000000000000..75f3ad34d7aa5 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/auth_anonymous.go @@ -0,0 +1,16 @@ +package dbus + +// AuthAnonymous returns an Auth that uses the ANONYMOUS mechanism. +func AuthAnonymous() Auth { + return &authAnonymous{} +} + +type authAnonymous struct{} + +func (a *authAnonymous) FirstData() (name, resp []byte, status AuthStatus) { + return []byte("ANONYMOUS"), nil, AuthOk +} + +func (a *authAnonymous) HandleData(data []byte) (resp []byte, status AuthStatus) { + return nil, AuthError +} diff --git a/vendor/github.com/godbus/dbus/auth_external.go b/vendor/github.com/godbus/dbus/v5/auth_external.go similarity index 100% rename from vendor/github.com/godbus/dbus/auth_external.go rename to vendor/github.com/godbus/dbus/v5/auth_external.go diff --git a/vendor/github.com/godbus/dbus/auth_sha1.go b/vendor/github.com/godbus/dbus/v5/auth_sha1.go similarity index 96% rename from vendor/github.com/godbus/dbus/auth_sha1.go rename to vendor/github.com/godbus/dbus/v5/auth_sha1.go index df15b46119895..80286700b6d1d 100644 --- a/vendor/github.com/godbus/dbus/auth_sha1.go +++ b/vendor/github.com/godbus/dbus/v5/auth_sha1.go @@ -60,7 +60,7 @@ func (a authCookieSha1) HandleData(data []byte) ([]byte, AuthStatus) { // getCookie searches for the cookie identified by id in context and returns // the cookie content or nil. (Since HandleData can't return a specific error, -// but only whether an error occured, this function also doesn't bother to +// but only whether an error occurred, this function also doesn't bother to // return an error.) func (a authCookieSha1) getCookie(context, id []byte) []byte { file, err := os.Open(a.home + "/.dbus-keyrings/" + string(context)) diff --git a/vendor/github.com/godbus/dbus/v5/call.go b/vendor/github.com/godbus/dbus/v5/call.go new file mode 100644 index 0000000000000..b06b063580fc5 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/call.go @@ -0,0 +1,69 @@ +package dbus + +import ( + "context" + "errors" +) + +var errSignature = errors.New("dbus: mismatched signature") + +// Call represents a pending or completed method call. +type Call struct { + Destination string + Path ObjectPath + Method string + Args []interface{} + + // Strobes when the call is complete. + Done chan *Call + + // After completion, the error status. If this is non-nil, it may be an + // error message from the peer (with Error as its type) or some other error. + Err error + + // Holds the response once the call is done. + Body []interface{} + + // ResponseSequence stores the sequence number of the DBus message containing + // the call response (or error). This can be compared to the sequence number + // of other call responses and signals on this connection to determine their + // relative ordering on the underlying DBus connection. + // For errors, ResponseSequence is populated only if the error came from a + // DBusMessage that was received or if there was an error receiving. In case of + // failure to make the call, ResponseSequence will be NoSequence. + ResponseSequence Sequence + + // tracks context and canceler + ctx context.Context + ctxCanceler context.CancelFunc +} + +func (c *Call) Context() context.Context { + if c.ctx == nil { + return context.Background() + } + + return c.ctx +} + +func (c *Call) ContextCancel() { + if c.ctxCanceler != nil { + c.ctxCanceler() + } +} + +// Store stores the body of the reply into the provided pointers. It returns +// an error if the signatures of the body and retvalues don't match, or if +// the error status is not nil. +func (c *Call) Store(retvalues ...interface{}) error { + if c.Err != nil { + return c.Err + } + + return Store(c.Body, retvalues...) +} + +func (c *Call) done() { + c.Done <- c + c.ContextCancel() +} diff --git a/vendor/github.com/godbus/dbus/v5/conn.go b/vendor/github.com/godbus/dbus/v5/conn.go new file mode 100644 index 0000000000000..29fe018ad823b --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/conn.go @@ -0,0 +1,959 @@ +package dbus + +import ( + "context" + "errors" + "io" + "os" + "strings" + "sync" +) + +var ( + systemBus *Conn + systemBusLck sync.Mutex + sessionBus *Conn + sessionBusLck sync.Mutex +) + +// ErrClosed is the error returned by calls on a closed connection. +var ErrClosed = errors.New("dbus: connection closed by user") + +// Conn represents a connection to a message bus (usually, the system or +// session bus). +// +// Connections are either shared or private. Shared connections +// are shared between calls to the functions that return them. As a result, +// the methods Close, Auth and Hello must not be called on them. +// +// Multiple goroutines may invoke methods on a connection simultaneously. +type Conn struct { + transport + + ctx context.Context + cancelCtx context.CancelFunc + + closeOnce sync.Once + closeErr error + + busObj BusObject + unixFD bool + uuid string + + handler Handler + signalHandler SignalHandler + serialGen SerialGenerator + inInt Interceptor + outInt Interceptor + auth []Auth + + names *nameTracker + calls *callTracker + outHandler *outputHandler + + eavesdropped chan<- *Message + eavesdroppedLck sync.Mutex +} + +// SessionBus returns a shared connection to the session bus, connecting to it +// if not already done. +func SessionBus() (conn *Conn, err error) { + sessionBusLck.Lock() + defer sessionBusLck.Unlock() + if sessionBus != nil && + sessionBus.Connected() { + return sessionBus, nil + } + defer func() { + if conn != nil { + sessionBus = conn + } + }() + conn, err = ConnectSessionBus() + return +} + +func getSessionBusAddress() (string, error) { + if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" { + return address, nil + + } else if address := tryDiscoverDbusSessionBusAddress(); address != "" { + os.Setenv("DBUS_SESSION_BUS_ADDRESS", address) + return address, nil + } + return getSessionBusPlatformAddress() +} + +// SessionBusPrivate returns a new private connection to the session bus. +func SessionBusPrivate(opts ...ConnOption) (*Conn, error) { + address, err := getSessionBusAddress() + if err != nil { + return nil, err + } + + return Dial(address, opts...) +} + +// SessionBusPrivate returns a new private connection to the session bus. +// +// Deprecated: use SessionBusPrivate with options instead. +func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) { + return SessionBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler)) +} + +// SystemBus returns a shared connection to the system bus, connecting to it if +// not already done. +func SystemBus() (conn *Conn, err error) { + systemBusLck.Lock() + defer systemBusLck.Unlock() + if systemBus != nil && + systemBus.Connected() { + return systemBus, nil + } + defer func() { + if conn != nil { + systemBus = conn + } + }() + conn, err = ConnectSystemBus() + return +} + +// ConnectSessionBus connects to the session bus. +func ConnectSessionBus(opts ...ConnOption) (*Conn, error) { + address, err := getSessionBusAddress() + if err != nil { + return nil, err + } + return Connect(address, opts...) +} + +// ConnectSystemBus connects to the system bus. +func ConnectSystemBus(opts ...ConnOption) (*Conn, error) { + return Connect(getSystemBusPlatformAddress(), opts...) +} + +// Connect connects to the given address. +// +// Returned connection is ready to use and doesn't require calling +// Auth and Hello methods to make it usable. +func Connect(address string, opts ...ConnOption) (*Conn, error) { + conn, err := Dial(address, opts...) + if err != nil { + return nil, err + } + if err = conn.Auth(conn.auth); err != nil { + _ = conn.Close() + return nil, err + } + if err = conn.Hello(); err != nil { + _ = conn.Close() + return nil, err + } + return conn, nil +} + +// SystemBusPrivate returns a new private connection to the system bus. +// Note: this connection is not ready to use. One must perform Auth and Hello +// on the connection before it is useable. +func SystemBusPrivate(opts ...ConnOption) (*Conn, error) { + return Dial(getSystemBusPlatformAddress(), opts...) +} + +// SystemBusPrivateHandler returns a new private connection to the system bus, using the provided handlers. +// +// Deprecated: use SystemBusPrivate with options instead. +func SystemBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) { + return SystemBusPrivate(WithHandler(handler), WithSignalHandler(signalHandler)) +} + +// Dial establishes a new private connection to the message bus specified by address. +func Dial(address string, opts ...ConnOption) (*Conn, error) { + tr, err := getTransport(address) + if err != nil { + return nil, err + } + return newConn(tr, opts...) +} + +// DialHandler establishes a new private connection to the message bus specified by address, using the supplied handlers. +// +// Deprecated: use Dial with options instead. +func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) { + return Dial(address, WithSignalHandler(signalHandler)) +} + +// ConnOption is a connection option. +type ConnOption func(conn *Conn) error + +// WithHandler overrides the default handler. +func WithHandler(handler Handler) ConnOption { + return func(conn *Conn) error { + conn.handler = handler + return nil + } +} + +// WithSignalHandler overrides the default signal handler. +func WithSignalHandler(handler SignalHandler) ConnOption { + return func(conn *Conn) error { + conn.signalHandler = handler + return nil + } +} + +// WithSerialGenerator overrides the default signals generator. +func WithSerialGenerator(gen SerialGenerator) ConnOption { + return func(conn *Conn) error { + conn.serialGen = gen + return nil + } +} + +// WithAuth sets authentication methods for the auth conversation. +func WithAuth(methods ...Auth) ConnOption { + return func(conn *Conn) error { + conn.auth = methods + return nil + } +} + +// Interceptor intercepts incoming and outgoing messages. +type Interceptor func(msg *Message) + +// WithIncomingInterceptor sets the given interceptor for incoming messages. +func WithIncomingInterceptor(interceptor Interceptor) ConnOption { + return func(conn *Conn) error { + conn.inInt = interceptor + return nil + } +} + +// WithOutgoingInterceptor sets the given interceptor for outgoing messages. +func WithOutgoingInterceptor(interceptor Interceptor) ConnOption { + return func(conn *Conn) error { + conn.outInt = interceptor + return nil + } +} + +// WithContext overrides the default context for the connection. +func WithContext(ctx context.Context) ConnOption { + return func(conn *Conn) error { + conn.ctx = ctx + return nil + } +} + +// NewConn creates a new private *Conn from an already established connection. +func NewConn(conn io.ReadWriteCloser, opts ...ConnOption) (*Conn, error) { + return newConn(genericTransport{conn}, opts...) +} + +// NewConnHandler creates a new private *Conn from an already established connection, using the supplied handlers. +// +// Deprecated: use NewConn with options instead. +func NewConnHandler(conn io.ReadWriteCloser, handler Handler, signalHandler SignalHandler) (*Conn, error) { + return NewConn(genericTransport{conn}, WithHandler(handler), WithSignalHandler(signalHandler)) +} + +// newConn creates a new *Conn from a transport. +func newConn(tr transport, opts ...ConnOption) (*Conn, error) { + conn := new(Conn) + conn.transport = tr + for _, opt := range opts { + if err := opt(conn); err != nil { + return nil, err + } + } + if conn.ctx == nil { + conn.ctx = context.Background() + } + conn.ctx, conn.cancelCtx = context.WithCancel(conn.ctx) + go func() { + <-conn.ctx.Done() + conn.Close() + }() + + conn.calls = newCallTracker() + if conn.handler == nil { + conn.handler = NewDefaultHandler() + } + if conn.signalHandler == nil { + conn.signalHandler = NewDefaultSignalHandler() + } + if conn.serialGen == nil { + conn.serialGen = newSerialGenerator() + } + conn.outHandler = &outputHandler{conn: conn} + conn.names = newNameTracker() + conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") + return conn, nil +} + +// BusObject returns the object owned by the bus daemon which handles +// administrative requests. +func (conn *Conn) BusObject() BusObject { + return conn.busObj +} + +// Close closes the connection. Any blocked operations will return with errors +// and the channels passed to Eavesdrop and Signal are closed. This method must +// not be called on shared connections. +func (conn *Conn) Close() error { + conn.closeOnce.Do(func() { + conn.outHandler.close() + if term, ok := conn.signalHandler.(Terminator); ok { + term.Terminate() + } + + if term, ok := conn.handler.(Terminator); ok { + term.Terminate() + } + + conn.eavesdroppedLck.Lock() + if conn.eavesdropped != nil { + close(conn.eavesdropped) + } + conn.eavesdroppedLck.Unlock() + + conn.cancelCtx() + + conn.closeErr = conn.transport.Close() + }) + return conn.closeErr +} + +// Context returns the context associated with the connection. The +// context will be cancelled when the connection is closed. +func (conn *Conn) Context() context.Context { + return conn.ctx +} + +// Connected returns whether conn is connected +func (conn *Conn) Connected() bool { + return conn.ctx.Err() == nil +} + +// Eavesdrop causes conn to send all incoming messages to the given channel +// without further processing. Method replies, errors and signals will not be +// sent to the appropriate channels and method calls will not be handled. If nil +// is passed, the normal behaviour is restored. +// +// The caller has to make sure that ch is sufficiently buffered; +// if a message arrives when a write to ch is not possible, the message is +// discarded. +func (conn *Conn) Eavesdrop(ch chan<- *Message) { + conn.eavesdroppedLck.Lock() + conn.eavesdropped = ch + conn.eavesdroppedLck.Unlock() +} + +// getSerial returns an unused serial. +func (conn *Conn) getSerial() uint32 { + return conn.serialGen.GetSerial() +} + +// Hello sends the initial org.freedesktop.DBus.Hello call. This method must be +// called after authentication, but before sending any other messages to the +// bus. Hello must not be called for shared connections. +func (conn *Conn) Hello() error { + var s string + err := conn.busObj.Call("org.freedesktop.DBus.Hello", 0).Store(&s) + if err != nil { + return err + } + conn.names.acquireUniqueConnectionName(s) + return nil +} + +// inWorker runs in an own goroutine, reading incoming messages from the +// transport and dispatching them appropriately. +func (conn *Conn) inWorker() { + sequenceGen := newSequenceGenerator() + for { + msg, err := conn.ReadMessage() + if err != nil { + if _, ok := err.(InvalidMessageError); !ok { + // Some read error occurred (usually EOF); we can't really do + // anything but to shut down all stuff and returns errors to all + // pending replies. + conn.Close() + conn.calls.finalizeAllWithError(sequenceGen, err) + return + } + // invalid messages are ignored + continue + } + conn.eavesdroppedLck.Lock() + if conn.eavesdropped != nil { + select { + case conn.eavesdropped <- msg: + default: + } + conn.eavesdroppedLck.Unlock() + continue + } + conn.eavesdroppedLck.Unlock() + dest, _ := msg.Headers[FieldDestination].value.(string) + found := dest == "" || + !conn.names.uniqueNameIsKnown() || + conn.names.isKnownName(dest) + if !found { + // Eavesdropped a message, but no channel for it is registered. + // Ignore it. + continue + } + + if conn.inInt != nil { + conn.inInt(msg) + } + sequence := sequenceGen.next() + switch msg.Type { + case TypeError: + conn.serialGen.RetireSerial(conn.calls.handleDBusError(sequence, msg)) + case TypeMethodReply: + conn.serialGen.RetireSerial(conn.calls.handleReply(sequence, msg)) + case TypeSignal: + conn.handleSignal(sequence, msg) + case TypeMethodCall: + go conn.handleCall(msg) + } + + } +} + +func (conn *Conn) handleSignal(sequence Sequence, msg *Message) { + iface := msg.Headers[FieldInterface].value.(string) + member := msg.Headers[FieldMember].value.(string) + // as per http://dbus.freedesktop.org/doc/dbus-specification.html , + // sender is optional for signals. + sender, _ := msg.Headers[FieldSender].value.(string) + if iface == "org.freedesktop.DBus" && sender == "org.freedesktop.DBus" { + if member == "NameLost" { + // If we lost the name on the bus, remove it from our + // tracking list. + name, ok := msg.Body[0].(string) + if !ok { + panic("Unable to read the lost name") + } + conn.names.loseName(name) + } else if member == "NameAcquired" { + // If we acquired the name on the bus, add it to our + // tracking list. + name, ok := msg.Body[0].(string) + if !ok { + panic("Unable to read the acquired name") + } + conn.names.acquireName(name) + } + } + signal := &Signal{ + Sender: sender, + Path: msg.Headers[FieldPath].value.(ObjectPath), + Name: iface + "." + member, + Body: msg.Body, + Sequence: sequence, + } + conn.signalHandler.DeliverSignal(iface, member, signal) +} + +// Names returns the list of all names that are currently owned by this +// connection. The slice is always at least one element long, the first element +// being the unique name of the connection. +func (conn *Conn) Names() []string { + return conn.names.listKnownNames() +} + +// Object returns the object identified by the given destination name and path. +func (conn *Conn) Object(dest string, path ObjectPath) BusObject { + return &Object{conn, dest, path} +} + +func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) { + if msg.serial == 0 { + msg.serial = conn.getSerial() + } + if conn.outInt != nil { + conn.outInt(msg) + } + err := conn.outHandler.sendAndIfClosed(msg, ifClosed) + conn.calls.handleSendError(msg, err) + if err != nil { + conn.serialGen.RetireSerial(msg.serial) + } else if msg.Type != TypeMethodCall { + conn.serialGen.RetireSerial(msg.serial) + } +} + +// Send sends the given message to the message bus. You usually don't need to +// use this; use the higher-level equivalents (Call / Go, Emit and Export) +// instead. If msg is a method call and NoReplyExpected is not set, a non-nil +// call is returned and the same value is sent to ch (which must be buffered) +// once the call is complete. Otherwise, ch is ignored and a Call structure is +// returned of which only the Err member is valid. +func (conn *Conn) Send(msg *Message, ch chan *Call) *Call { + return conn.send(context.Background(), msg, ch) +} + +// SendWithContext acts like Send but takes a context +func (conn *Conn) SendWithContext(ctx context.Context, msg *Message, ch chan *Call) *Call { + return conn.send(ctx, msg, ch) +} + +func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call { + if ctx == nil { + panic("nil context") + } + if ch == nil { + ch = make(chan *Call, 1) + } else if cap(ch) == 0 { + panic("dbus: unbuffered channel passed to (*Conn).Send") + } + + var call *Call + ctx, canceler := context.WithCancel(ctx) + msg.serial = conn.getSerial() + if msg.Type == TypeMethodCall && msg.Flags&FlagNoReplyExpected == 0 { + call = new(Call) + call.Destination, _ = msg.Headers[FieldDestination].value.(string) + call.Path, _ = msg.Headers[FieldPath].value.(ObjectPath) + iface, _ := msg.Headers[FieldInterface].value.(string) + member, _ := msg.Headers[FieldMember].value.(string) + call.Method = iface + "." + member + call.Args = msg.Body + call.Done = ch + call.ctx = ctx + call.ctxCanceler = canceler + conn.calls.track(msg.serial, call) + go func() { + <-ctx.Done() + conn.calls.handleSendError(msg, ctx.Err()) + }() + conn.sendMessageAndIfClosed(msg, func() { + conn.calls.handleSendError(msg, ErrClosed) + canceler() + }) + } else { + canceler() + call = &Call{Err: nil, Done: ch} + ch <- call + conn.sendMessageAndIfClosed(msg, func() { + call = &Call{Err: ErrClosed} + }) + } + return call +} + +// sendError creates an error message corresponding to the parameters and sends +// it to conn.out. +func (conn *Conn) sendError(err error, dest string, serial uint32) { + var e *Error + switch em := err.(type) { + case Error: + e = &em + case *Error: + e = em + case DBusError: + name, body := em.DBusError() + e = NewError(name, body) + default: + e = MakeFailedError(err) + } + msg := new(Message) + msg.Type = TypeError + msg.Headers = make(map[HeaderField]Variant) + if dest != "" { + msg.Headers[FieldDestination] = MakeVariant(dest) + } + msg.Headers[FieldErrorName] = MakeVariant(e.Name) + msg.Headers[FieldReplySerial] = MakeVariant(serial) + msg.Body = e.Body + if len(e.Body) > 0 { + msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...)) + } + conn.sendMessageAndIfClosed(msg, nil) +} + +// sendReply creates a method reply message corresponding to the parameters and +// sends it to conn.out. +func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) { + msg := new(Message) + msg.Type = TypeMethodReply + msg.Headers = make(map[HeaderField]Variant) + if dest != "" { + msg.Headers[FieldDestination] = MakeVariant(dest) + } + msg.Headers[FieldReplySerial] = MakeVariant(serial) + msg.Body = values + if len(values) > 0 { + msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) + } + conn.sendMessageAndIfClosed(msg, nil) +} + +// AddMatchSignal registers the given match rule to receive broadcast +// signals based on their contents. +func (conn *Conn) AddMatchSignal(options ...MatchOption) error { + return conn.AddMatchSignalContext(context.Background(), options...) +} + +// AddMatchSignalContext acts like AddMatchSignal but takes a context. +func (conn *Conn) AddMatchSignalContext(ctx context.Context, options ...MatchOption) error { + options = append([]MatchOption{withMatchType("signal")}, options...) + return conn.busObj.CallWithContext( + ctx, + "org.freedesktop.DBus.AddMatch", 0, + formatMatchOptions(options), + ).Store() +} + +// RemoveMatchSignal removes the first rule that matches previously registered with AddMatchSignal. +func (conn *Conn) RemoveMatchSignal(options ...MatchOption) error { + return conn.RemoveMatchSignalContext(context.Background(), options...) +} + +// RemoveMatchSignalContext acts like RemoveMatchSignal but takes a context. +func (conn *Conn) RemoveMatchSignalContext(ctx context.Context, options ...MatchOption) error { + options = append([]MatchOption{withMatchType("signal")}, options...) + return conn.busObj.CallWithContext( + ctx, + "org.freedesktop.DBus.RemoveMatch", 0, + formatMatchOptions(options), + ).Store() +} + +// Signal registers the given channel to be passed all received signal messages. +// +// Multiple of these channels can be registered at the same time. +// +// These channels are "overwritten" by Eavesdrop; i.e., if there currently is a +// channel for eavesdropped messages, this channel receives all signals, and +// none of the channels passed to Signal will receive any signals. +// +// Panics if the signal handler is not a `SignalRegistrar`. +func (conn *Conn) Signal(ch chan<- *Signal) { + handler, ok := conn.signalHandler.(SignalRegistrar) + if !ok { + panic("cannot use this method with a non SignalRegistrar handler") + } + handler.AddSignal(ch) +} + +// RemoveSignal removes the given channel from the list of the registered channels. +// +// Panics if the signal handler is not a `SignalRegistrar`. +func (conn *Conn) RemoveSignal(ch chan<- *Signal) { + handler, ok := conn.signalHandler.(SignalRegistrar) + if !ok { + panic("cannot use this method with a non SignalRegistrar handler") + } + handler.RemoveSignal(ch) +} + +// SupportsUnixFDs returns whether the underlying transport supports passing of +// unix file descriptors. If this is false, method calls containing unix file +// descriptors will return an error and emitted signals containing them will +// not be sent. +func (conn *Conn) SupportsUnixFDs() bool { + return conn.unixFD +} + +// Error represents a D-Bus message of type Error. +type Error struct { + Name string + Body []interface{} +} + +func NewError(name string, body []interface{}) *Error { + return &Error{name, body} +} + +func (e Error) Error() string { + if len(e.Body) >= 1 { + s, ok := e.Body[0].(string) + if ok { + return s + } + } + return e.Name +} + +// Signal represents a D-Bus message of type Signal. The name member is given in +// "interface.member" notation, e.g. org.freedesktop.D-Bus.NameLost. +type Signal struct { + Sender string + Path ObjectPath + Name string + Body []interface{} + Sequence Sequence +} + +// transport is a D-Bus transport. +type transport interface { + // Read and Write raw data (for example, for the authentication protocol). + io.ReadWriteCloser + + // Send the initial null byte used for the EXTERNAL mechanism. + SendNullByte() error + + // Returns whether this transport supports passing Unix FDs. + SupportsUnixFDs() bool + + // Signal the transport that Unix FD passing is enabled for this connection. + EnableUnixFDs() + + // Read / send a message, handling things like Unix FDs. + ReadMessage() (*Message, error) + SendMessage(*Message) error +} + +var ( + transports = make(map[string]func(string) (transport, error)) +) + +func getTransport(address string) (transport, error) { + var err error + var t transport + + addresses := strings.Split(address, ";") + for _, v := range addresses { + i := strings.IndexRune(v, ':') + if i == -1 { + err = errors.New("dbus: invalid bus address (no transport)") + continue + } + f := transports[v[:i]] + if f == nil { + err = errors.New("dbus: invalid bus address (invalid or unsupported transport)") + continue + } + t, err = f(v[i+1:]) + if err == nil { + return t, nil + } + } + return nil, err +} + +// getKey gets a key from a the list of keys. Returns "" on error / not found... +func getKey(s, key string) string { + for _, keyEqualsValue := range strings.Split(s, ",") { + keyValue := strings.SplitN(keyEqualsValue, "=", 2) + if len(keyValue) == 2 && keyValue[0] == key { + return keyValue[1] + } + } + return "" +} + +type outputHandler struct { + conn *Conn + sendLck sync.Mutex + closed struct { + isClosed bool + lck sync.RWMutex + } +} + +func (h *outputHandler) sendAndIfClosed(msg *Message, ifClosed func()) error { + h.closed.lck.RLock() + defer h.closed.lck.RUnlock() + if h.closed.isClosed { + if ifClosed != nil { + ifClosed() + } + return nil + } + h.sendLck.Lock() + defer h.sendLck.Unlock() + return h.conn.SendMessage(msg) +} + +func (h *outputHandler) close() { + h.closed.lck.Lock() + defer h.closed.lck.Unlock() + h.closed.isClosed = true +} + +type serialGenerator struct { + lck sync.Mutex + nextSerial uint32 + serialUsed map[uint32]bool +} + +func newSerialGenerator() *serialGenerator { + return &serialGenerator{ + serialUsed: map[uint32]bool{0: true}, + nextSerial: 1, + } +} + +func (gen *serialGenerator) GetSerial() uint32 { + gen.lck.Lock() + defer gen.lck.Unlock() + n := gen.nextSerial + for gen.serialUsed[n] { + n++ + } + gen.serialUsed[n] = true + gen.nextSerial = n + 1 + return n +} + +func (gen *serialGenerator) RetireSerial(serial uint32) { + gen.lck.Lock() + defer gen.lck.Unlock() + delete(gen.serialUsed, serial) +} + +type nameTracker struct { + lck sync.RWMutex + unique string + names map[string]struct{} +} + +func newNameTracker() *nameTracker { + return &nameTracker{names: map[string]struct{}{}} +} +func (tracker *nameTracker) acquireUniqueConnectionName(name string) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + tracker.unique = name +} +func (tracker *nameTracker) acquireName(name string) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + tracker.names[name] = struct{}{} +} +func (tracker *nameTracker) loseName(name string) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + delete(tracker.names, name) +} + +func (tracker *nameTracker) uniqueNameIsKnown() bool { + tracker.lck.RLock() + defer tracker.lck.RUnlock() + return tracker.unique != "" +} +func (tracker *nameTracker) isKnownName(name string) bool { + tracker.lck.RLock() + defer tracker.lck.RUnlock() + _, ok := tracker.names[name] + return ok || name == tracker.unique +} +func (tracker *nameTracker) listKnownNames() []string { + tracker.lck.RLock() + defer tracker.lck.RUnlock() + out := make([]string, 0, len(tracker.names)+1) + out = append(out, tracker.unique) + for k := range tracker.names { + out = append(out, k) + } + return out +} + +type callTracker struct { + calls map[uint32]*Call + lck sync.RWMutex +} + +func newCallTracker() *callTracker { + return &callTracker{calls: map[uint32]*Call{}} +} + +func (tracker *callTracker) track(sn uint32, call *Call) { + tracker.lck.Lock() + tracker.calls[sn] = call + tracker.lck.Unlock() +} + +func (tracker *callTracker) handleReply(sequence Sequence, msg *Message) uint32 { + serial := msg.Headers[FieldReplySerial].value.(uint32) + tracker.lck.RLock() + _, ok := tracker.calls[serial] + tracker.lck.RUnlock() + if ok { + tracker.finalizeWithBody(serial, sequence, msg.Body) + } + return serial +} + +func (tracker *callTracker) handleDBusError(sequence Sequence, msg *Message) uint32 { + serial := msg.Headers[FieldReplySerial].value.(uint32) + tracker.lck.RLock() + _, ok := tracker.calls[serial] + tracker.lck.RUnlock() + if ok { + name, _ := msg.Headers[FieldErrorName].value.(string) + tracker.finalizeWithError(serial, sequence, Error{name, msg.Body}) + } + return serial +} + +func (tracker *callTracker) handleSendError(msg *Message, err error) { + if err == nil { + return + } + tracker.lck.RLock() + _, ok := tracker.calls[msg.serial] + tracker.lck.RUnlock() + if ok { + tracker.finalizeWithError(msg.serial, NoSequence, err) + } +} + +// finalize was the only func that did not strobe Done +func (tracker *callTracker) finalize(sn uint32) { + tracker.lck.Lock() + defer tracker.lck.Unlock() + c, ok := tracker.calls[sn] + if ok { + delete(tracker.calls, sn) + c.ContextCancel() + } +} + +func (tracker *callTracker) finalizeWithBody(sn uint32, sequence Sequence, body []interface{}) { + tracker.lck.Lock() + c, ok := tracker.calls[sn] + if ok { + delete(tracker.calls, sn) + } + tracker.lck.Unlock() + if ok { + c.Body = body + c.ResponseSequence = sequence + c.done() + } +} + +func (tracker *callTracker) finalizeWithError(sn uint32, sequence Sequence, err error) { + tracker.lck.Lock() + c, ok := tracker.calls[sn] + if ok { + delete(tracker.calls, sn) + } + tracker.lck.Unlock() + if ok { + c.Err = err + c.ResponseSequence = sequence + c.done() + } +} + +func (tracker *callTracker) finalizeAllWithError(sequenceGen *sequenceGenerator, err error) { + tracker.lck.Lock() + closedCalls := make([]*Call, 0, len(tracker.calls)) + for sn := range tracker.calls { + closedCalls = append(closedCalls, tracker.calls[sn]) + } + tracker.calls = map[uint32]*Call{} + tracker.lck.Unlock() + for _, call := range closedCalls { + call.Err = err + call.ResponseSequence = sequenceGen.next() + call.done() + } +} diff --git a/vendor/github.com/godbus/dbus/v5/conn_darwin.go b/vendor/github.com/godbus/dbus/v5/conn_darwin.go new file mode 100644 index 0000000000000..6e2e402021650 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/conn_darwin.go @@ -0,0 +1,37 @@ +package dbus + +import ( + "errors" + "fmt" + "os" + "os/exec" +) + +const defaultSystemBusAddress = "unix:path=/opt/local/var/run/dbus/system_bus_socket" + +func getSessionBusPlatformAddress() (string, error) { + cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET") + b, err := cmd.CombinedOutput() + + if err != nil { + return "", err + } + + if len(b) == 0 { + return "", errors.New("dbus: couldn't determine address of session bus") + } + + return "unix:path=" + string(b[:len(b)-1]), nil +} + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET") + if address != "" { + return fmt.Sprintf("unix:path=%s", address) + } + return defaultSystemBusAddress +} + +func tryDiscoverDbusSessionBusAddress() string { + return "" +} diff --git a/vendor/github.com/godbus/dbus/v5/conn_other.go b/vendor/github.com/godbus/dbus/v5/conn_other.go new file mode 100644 index 0000000000000..616dcf66449f7 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/conn_other.go @@ -0,0 +1,93 @@ +// +build !darwin + +package dbus + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "os/user" + "path" + "strings" +) + +var execCommand = exec.Command + +func getSessionBusPlatformAddress() (string, error) { + cmd := execCommand("dbus-launch") + b, err := cmd.CombinedOutput() + + if err != nil { + return "", err + } + + i := bytes.IndexByte(b, '=') + j := bytes.IndexByte(b, '\n') + + if i == -1 || j == -1 || i > j { + return "", errors.New("dbus: couldn't determine address of session bus") + } + + env, addr := string(b[0:i]), string(b[i+1:j]) + os.Setenv(env, addr) + + return addr, nil +} + +// tryDiscoverDbusSessionBusAddress tries to discover an existing dbus session +// and return the value of its DBUS_SESSION_BUS_ADDRESS. +// It tries different techniques employed by different operating systems, +// returning the first valid address it finds, or an empty string. +// +// * /run/user//bus if this exists, it *is* the bus socket. present on +// Ubuntu 18.04 +// * /run/user//dbus-session: if this exists, it can be parsed for the bus +// address. present on Ubuntu 16.04 +// +// See https://dbus.freedesktop.org/doc/dbus-launch.1.html +func tryDiscoverDbusSessionBusAddress() string { + if runtimeDirectory, err := getRuntimeDirectory(); err == nil { + + if runUserBusFile := path.Join(runtimeDirectory, "bus"); fileExists(runUserBusFile) { + // if /run/user//bus exists, that file itself + // *is* the unix socket, so return its path + return fmt.Sprintf("unix:path=%s", runUserBusFile) + } + if runUserSessionDbusFile := path.Join(runtimeDirectory, "dbus-session"); fileExists(runUserSessionDbusFile) { + // if /run/user//dbus-session exists, it's a + // text file // containing the address of the socket, e.g.: + // DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-E1c73yNqrG + + if f, err := ioutil.ReadFile(runUserSessionDbusFile); err == nil { + fileContent := string(f) + + prefix := "DBUS_SESSION_BUS_ADDRESS=" + + if strings.HasPrefix(fileContent, prefix) { + address := strings.TrimRight(strings.TrimPrefix(fileContent, prefix), "\n\r") + return address + } + } + } + } + return "" +} + +func getRuntimeDirectory() (string, error) { + if currentUser, err := user.Current(); err != nil { + return "", err + } else { + return fmt.Sprintf("/run/user/%s", currentUser.Uid), nil + } +} + +func fileExists(filename string) bool { + if _, err := os.Stat(filename); !os.IsNotExist(err) { + return true + } else { + return false + } +} diff --git a/vendor/github.com/godbus/dbus/v5/conn_unix.go b/vendor/github.com/godbus/dbus/v5/conn_unix.go new file mode 100644 index 0000000000000..58aee7d2af5eb --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/conn_unix.go @@ -0,0 +1,17 @@ +//+build !windows,!solaris,!darwin + +package dbus + +import ( + "os" +) + +const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + if address != "" { + return address + } + return defaultSystemBusAddress +} diff --git a/vendor/github.com/godbus/dbus/v5/conn_windows.go b/vendor/github.com/godbus/dbus/v5/conn_windows.go new file mode 100644 index 0000000000000..4291e4519cc01 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/conn_windows.go @@ -0,0 +1,15 @@ +//+build windows + +package dbus + +import "os" + +const defaultSystemBusAddress = "tcp:host=127.0.0.1,port=12434" + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + if address != "" { + return address + } + return defaultSystemBusAddress +} diff --git a/vendor/github.com/godbus/dbus/v5/dbus.go b/vendor/github.com/godbus/dbus/v5/dbus.go new file mode 100644 index 0000000000000..ddf3b7afde9d3 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/dbus.go @@ -0,0 +1,432 @@ +package dbus + +import ( + "errors" + "fmt" + "reflect" + "strings" +) + +var ( + byteType = reflect.TypeOf(byte(0)) + boolType = reflect.TypeOf(false) + uint8Type = reflect.TypeOf(uint8(0)) + int16Type = reflect.TypeOf(int16(0)) + uint16Type = reflect.TypeOf(uint16(0)) + intType = reflect.TypeOf(int(0)) + uintType = reflect.TypeOf(uint(0)) + int32Type = reflect.TypeOf(int32(0)) + uint32Type = reflect.TypeOf(uint32(0)) + int64Type = reflect.TypeOf(int64(0)) + uint64Type = reflect.TypeOf(uint64(0)) + float64Type = reflect.TypeOf(float64(0)) + stringType = reflect.TypeOf("") + signatureType = reflect.TypeOf(Signature{""}) + objectPathType = reflect.TypeOf(ObjectPath("")) + variantType = reflect.TypeOf(Variant{Signature{""}, nil}) + interfacesType = reflect.TypeOf([]interface{}{}) + interfaceType = reflect.TypeOf((*interface{})(nil)).Elem() + unixFDType = reflect.TypeOf(UnixFD(0)) + unixFDIndexType = reflect.TypeOf(UnixFDIndex(0)) + errType = reflect.TypeOf((*error)(nil)).Elem() +) + +// An InvalidTypeError signals that a value which cannot be represented in the +// D-Bus wire format was passed to a function. +type InvalidTypeError struct { + Type reflect.Type +} + +func (e InvalidTypeError) Error() string { + return "dbus: invalid type " + e.Type.String() +} + +// Store copies the values contained in src to dest, which must be a slice of +// pointers. It converts slices of interfaces from src to corresponding structs +// in dest. An error is returned if the lengths of src and dest or the types of +// their elements don't match. +func Store(src []interface{}, dest ...interface{}) error { + if len(src) != len(dest) { + return errors.New("dbus.Store: length mismatch") + } + + for i := range src { + if err := storeInterfaces(src[i], dest[i]); err != nil { + return err + } + } + return nil +} + +func storeInterfaces(src, dest interface{}) error { + return store(reflect.ValueOf(dest), reflect.ValueOf(src)) +} + +func store(dest, src reflect.Value) error { + if dest.Kind() == reflect.Ptr { + if dest.IsNil() { + dest.Set(reflect.New(dest.Type().Elem())) + } + return store(dest.Elem(), src) + } + switch src.Kind() { + case reflect.Slice: + return storeSlice(dest, src) + case reflect.Map: + return storeMap(dest, src) + default: + return storeBase(dest, src) + } +} + +func storeBase(dest, src reflect.Value) error { + return setDest(dest, src) +} + +func setDest(dest, src reflect.Value) error { + if !isVariant(src.Type()) && isVariant(dest.Type()) { + //special conversion for dbus.Variant + dest.Set(reflect.ValueOf(MakeVariant(src.Interface()))) + return nil + } + if isVariant(src.Type()) && !isVariant(dest.Type()) { + src = getVariantValue(src) + return store(dest, src) + } + if !src.Type().ConvertibleTo(dest.Type()) { + return fmt.Errorf( + "dbus.Store: type mismatch: cannot convert %s to %s", + src.Type(), dest.Type()) + } + dest.Set(src.Convert(dest.Type())) + return nil +} + +func kindsAreCompatible(dest, src reflect.Type) bool { + switch { + case isVariant(dest): + return true + case dest.Kind() == reflect.Interface: + return true + default: + return dest.Kind() == src.Kind() + } +} + +func isConvertibleTo(dest, src reflect.Type) bool { + switch { + case isVariant(dest): + return true + case dest.Kind() == reflect.Interface: + return true + case dest.Kind() == reflect.Slice: + return src.Kind() == reflect.Slice && + isConvertibleTo(dest.Elem(), src.Elem()) + case dest.Kind() == reflect.Struct: + return src == interfacesType + default: + return src.ConvertibleTo(dest) + } +} + +func storeMap(dest, src reflect.Value) error { + switch { + case !kindsAreCompatible(dest.Type(), src.Type()): + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "map: cannot store a value of %s into %s", + src.Type(), dest.Type()) + case isVariant(dest.Type()): + return storeMapIntoVariant(dest, src) + case dest.Kind() == reflect.Interface: + return storeMapIntoInterface(dest, src) + case isConvertibleTo(dest.Type().Key(), src.Type().Key()) && + isConvertibleTo(dest.Type().Elem(), src.Type().Elem()): + return storeMapIntoMap(dest, src) + default: + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "map: cannot convert a value of %s into %s", + src.Type(), dest.Type()) + } +} + +func storeMapIntoVariant(dest, src reflect.Value) error { + dv := reflect.MakeMap(src.Type()) + err := store(dv, src) + if err != nil { + return err + } + return storeBase(dest, dv) +} + +func storeMapIntoInterface(dest, src reflect.Value) error { + var dv reflect.Value + if isVariant(src.Type().Elem()) { + //Convert variants to interface{} recursively when converting + //to interface{} + dv = reflect.MakeMap( + reflect.MapOf(src.Type().Key(), interfaceType)) + } else { + dv = reflect.MakeMap(src.Type()) + } + err := store(dv, src) + if err != nil { + return err + } + return storeBase(dest, dv) +} + +func storeMapIntoMap(dest, src reflect.Value) error { + if dest.IsNil() { + dest.Set(reflect.MakeMap(dest.Type())) + } + keys := src.MapKeys() + for _, key := range keys { + dkey := key.Convert(dest.Type().Key()) + dval := reflect.New(dest.Type().Elem()).Elem() + err := store(dval, getVariantValue(src.MapIndex(key))) + if err != nil { + return err + } + dest.SetMapIndex(dkey, dval) + } + return nil +} + +func storeSlice(dest, src reflect.Value) error { + switch { + case src.Type() == interfacesType && dest.Kind() == reflect.Struct: + //The decoder always decodes structs as slices of interface{} + return storeStruct(dest, src) + case !kindsAreCompatible(dest.Type(), src.Type()): + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "slice: cannot store a value of %s into %s", + src.Type(), dest.Type()) + case isVariant(dest.Type()): + return storeSliceIntoVariant(dest, src) + case dest.Kind() == reflect.Interface: + return storeSliceIntoInterface(dest, src) + case isConvertibleTo(dest.Type().Elem(), src.Type().Elem()): + return storeSliceIntoSlice(dest, src) + default: + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "slice: cannot convert a value of %s into %s", + src.Type(), dest.Type()) + } +} + +func storeStruct(dest, src reflect.Value) error { + if isVariant(dest.Type()) { + return storeBase(dest, src) + } + dval := make([]interface{}, 0, dest.NumField()) + dtype := dest.Type() + for i := 0; i < dest.NumField(); i++ { + field := dest.Field(i) + ftype := dtype.Field(i) + if ftype.PkgPath != "" { + continue + } + if ftype.Tag.Get("dbus") == "-" { + continue + } + dval = append(dval, field.Addr().Interface()) + } + if src.Len() != len(dval) { + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "destination struct does not have "+ + "enough fields need: %d have: %d", + src.Len(), len(dval)) + } + return Store(src.Interface().([]interface{}), dval...) +} + +func storeSliceIntoVariant(dest, src reflect.Value) error { + dv := reflect.MakeSlice(src.Type(), src.Len(), src.Cap()) + err := store(dv, src) + if err != nil { + return err + } + return storeBase(dest, dv) +} + +func storeSliceIntoInterface(dest, src reflect.Value) error { + var dv reflect.Value + if isVariant(src.Type().Elem()) { + //Convert variants to interface{} recursively when converting + //to interface{} + dv = reflect.MakeSlice(reflect.SliceOf(interfaceType), + src.Len(), src.Cap()) + } else { + dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap()) + } + err := store(dv, src) + if err != nil { + return err + } + return storeBase(dest, dv) +} + +func storeSliceIntoSlice(dest, src reflect.Value) error { + if dest.IsNil() || dest.Len() < src.Len() { + dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap())) + } + if dest.Len() != src.Len() { + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "slices are different lengths "+ + "need: %d have: %d", + src.Len(), dest.Len()) + } + for i := 0; i < src.Len(); i++ { + err := store(dest.Index(i), getVariantValue(src.Index(i))) + if err != nil { + return err + } + } + return nil +} + +func getVariantValue(in reflect.Value) reflect.Value { + if isVariant(in.Type()) { + return reflect.ValueOf(in.Interface().(Variant).Value()) + } + return in +} + +func isVariant(t reflect.Type) bool { + return t == variantType +} + +// An ObjectPath is an object path as defined by the D-Bus spec. +type ObjectPath string + +// IsValid returns whether the object path is valid. +func (o ObjectPath) IsValid() bool { + s := string(o) + if len(s) == 0 { + return false + } + if s[0] != '/' { + return false + } + if s[len(s)-1] == '/' && len(s) != 1 { + return false + } + // probably not used, but technically possible + if s == "/" { + return true + } + split := strings.Split(s[1:], "/") + for _, v := range split { + if len(v) == 0 { + return false + } + for _, c := range v { + if !isMemberChar(c) { + return false + } + } + } + return true +} + +// A UnixFD is a Unix file descriptor sent over the wire. See the package-level +// documentation for more information about Unix file descriptor passsing. +type UnixFD int32 + +// A UnixFDIndex is the representation of a Unix file descriptor in a message. +type UnixFDIndex uint32 + +// alignment returns the alignment of values of type t. +func alignment(t reflect.Type) int { + switch t { + case variantType: + return 1 + case objectPathType: + return 4 + case signatureType: + return 1 + case interfacesType: + return 4 + } + switch t.Kind() { + case reflect.Uint8: + return 1 + case reflect.Uint16, reflect.Int16: + return 2 + case reflect.Uint, reflect.Int, reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map: + return 4 + case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct: + return 8 + case reflect.Ptr: + return alignment(t.Elem()) + } + return 1 +} + +// isKeyType returns whether t is a valid type for a D-Bus dict. +func isKeyType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64, + reflect.String, reflect.Uint, reflect.Int: + + return true + } + return false +} + +// isValidInterface returns whether s is a valid name for an interface. +func isValidInterface(s string) bool { + if len(s) == 0 || len(s) > 255 || s[0] == '.' { + return false + } + elem := strings.Split(s, ".") + if len(elem) < 2 { + return false + } + for _, v := range elem { + if len(v) == 0 { + return false + } + if v[0] >= '0' && v[0] <= '9' { + return false + } + for _, c := range v { + if !isMemberChar(c) { + return false + } + } + } + return true +} + +// isValidMember returns whether s is a valid name for a member. +func isValidMember(s string) bool { + if len(s) == 0 || len(s) > 255 { + return false + } + i := strings.Index(s, ".") + if i != -1 { + return false + } + if s[0] >= '0' && s[0] <= '9' { + return false + } + for _, c := range s { + if !isMemberChar(c) { + return false + } + } + return true +} + +func isMemberChar(c rune) bool { + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || c == '_' +} diff --git a/vendor/github.com/godbus/dbus/v5/decoder.go b/vendor/github.com/godbus/dbus/v5/decoder.go new file mode 100644 index 0000000000000..ede91575b3e86 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/decoder.go @@ -0,0 +1,286 @@ +package dbus + +import ( + "encoding/binary" + "io" + "reflect" +) + +type decoder struct { + in io.Reader + order binary.ByteOrder + pos int +} + +// newDecoder returns a new decoder that reads values from in. The input is +// expected to be in the given byte order. +func newDecoder(in io.Reader, order binary.ByteOrder) *decoder { + dec := new(decoder) + dec.in = in + dec.order = order + return dec +} + +// align aligns the input to the given boundary and panics on error. +func (dec *decoder) align(n int) { + if dec.pos%n != 0 { + newpos := (dec.pos + n - 1) & ^(n - 1) + empty := make([]byte, newpos-dec.pos) + if _, err := io.ReadFull(dec.in, empty); err != nil { + panic(err) + } + dec.pos = newpos + } +} + +// Calls binary.Read(dec.in, dec.order, v) and panics on read errors. +func (dec *decoder) binread(v interface{}) { + if err := binary.Read(dec.in, dec.order, v); err != nil { + panic(err) + } +} + +func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) { + defer func() { + var ok bool + v := recover() + if err, ok = v.(error); ok { + if err == io.EOF || err == io.ErrUnexpectedEOF { + err = FormatError("unexpected EOF") + } + } + }() + vs = make([]interface{}, 0) + s := sig.str + for s != "" { + err, rem := validSingle(s, 0) + if err != nil { + return nil, err + } + v := dec.decode(s[:len(s)-len(rem)], 0) + vs = append(vs, v) + s = rem + } + return vs, nil +} + +func (dec *decoder) decode(s string, depth int) interface{} { + dec.align(alignment(typeFor(s))) + switch s[0] { + case 'y': + var b [1]byte + if _, err := dec.in.Read(b[:]); err != nil { + panic(err) + } + dec.pos++ + return b[0] + case 'b': + i := dec.decode("u", depth).(uint32) + switch { + case i == 0: + return false + case i == 1: + return true + default: + panic(FormatError("invalid value for boolean")) + } + case 'n': + var i int16 + dec.binread(&i) + dec.pos += 2 + return i + case 'i': + var i int32 + dec.binread(&i) + dec.pos += 4 + return i + case 'x': + var i int64 + dec.binread(&i) + dec.pos += 8 + return i + case 'q': + var i uint16 + dec.binread(&i) + dec.pos += 2 + return i + case 'u': + var i uint32 + dec.binread(&i) + dec.pos += 4 + return i + case 't': + var i uint64 + dec.binread(&i) + dec.pos += 8 + return i + case 'd': + var f float64 + dec.binread(&f) + dec.pos += 8 + return f + case 's': + length := dec.decode("u", depth).(uint32) + b := make([]byte, int(length)+1) + if _, err := io.ReadFull(dec.in, b); err != nil { + panic(err) + } + dec.pos += int(length) + 1 + return string(b[:len(b)-1]) + case 'o': + return ObjectPath(dec.decode("s", depth).(string)) + case 'g': + length := dec.decode("y", depth).(byte) + b := make([]byte, int(length)+1) + if _, err := io.ReadFull(dec.in, b); err != nil { + panic(err) + } + dec.pos += int(length) + 1 + sig, err := ParseSignature(string(b[:len(b)-1])) + if err != nil { + panic(err) + } + return sig + case 'v': + if depth >= 64 { + panic(FormatError("input exceeds container depth limit")) + } + var variant Variant + sig := dec.decode("g", depth).(Signature) + if len(sig.str) == 0 { + panic(FormatError("variant signature is empty")) + } + err, rem := validSingle(sig.str, 0) + if err != nil { + panic(err) + } + if rem != "" { + panic(FormatError("variant signature has multiple types")) + } + variant.sig = sig + variant.value = dec.decode(sig.str, depth+1) + return variant + case 'h': + return UnixFDIndex(dec.decode("u", depth).(uint32)) + case 'a': + if len(s) > 1 && s[1] == '{' { + ksig := s[2:3] + vsig := s[3 : len(s)-1] + v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig))) + if depth >= 63 { + panic(FormatError("input exceeds container depth limit")) + } + length := dec.decode("u", depth).(uint32) + // Even for empty maps, the correct padding must be included + dec.align(8) + spos := dec.pos + for dec.pos < spos+int(length) { + dec.align(8) + if !isKeyType(v.Type().Key()) { + panic(InvalidTypeError{v.Type()}) + } + kv := dec.decode(ksig, depth+2) + vv := dec.decode(vsig, depth+2) + v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv)) + } + return v.Interface() + } + if depth >= 64 { + panic(FormatError("input exceeds container depth limit")) + } + sig := s[1:] + length := dec.decode("u", depth).(uint32) + // capacity can be determined only for fixed-size element types + var capacity int + if s := sigByteSize(sig); s != 0 { + capacity = int(length) / s + } + v := reflect.MakeSlice(reflect.SliceOf(typeFor(sig)), 0, capacity) + // Even for empty arrays, the correct padding must be included + align := alignment(typeFor(s[1:])) + if len(s) > 1 && s[1] == '(' { + //Special case for arrays of structs + //structs decode as a slice of interface{} values + //but the dbus alignment does not match this + align = 8 + } + dec.align(align) + spos := dec.pos + for dec.pos < spos+int(length) { + ev := dec.decode(s[1:], depth+1) + v = reflect.Append(v, reflect.ValueOf(ev)) + } + return v.Interface() + case '(': + if depth >= 64 { + panic(FormatError("input exceeds container depth limit")) + } + dec.align(8) + v := make([]interface{}, 0) + s = s[1 : len(s)-1] + for s != "" { + err, rem := validSingle(s, 0) + if err != nil { + panic(err) + } + ev := dec.decode(s[:len(s)-len(rem)], depth+1) + v = append(v, ev) + s = rem + } + return v + default: + panic(SignatureError{Sig: s}) + } +} + +// sigByteSize tries to calculates size of the given signature in bytes. +// +// It returns zero when it can't, for example when it contains non-fixed size +// types such as strings, maps and arrays that require reading of the transmitted +// data, for that we would need to implement the unread method for Decoder first. +func sigByteSize(sig string) int { + var total int + for offset := 0; offset < len(sig); { + switch sig[offset] { + case 'y': + total += 1 + offset += 1 + case 'n', 'q': + total += 2 + offset += 1 + case 'b', 'i', 'u', 'h': + total += 4 + offset += 1 + case 'x', 't', 'd': + total += 8 + offset += 1 + case '(': + i := 1 + depth := 1 + for i < len(sig[offset:]) && depth != 0 { + if sig[offset+i] == '(' { + depth++ + } else if sig[offset+i] == ')' { + depth-- + } + i++ + } + s := sigByteSize(sig[offset+1 : offset+i-1]) + if s == 0 { + return 0 + } + total += s + offset += i + default: + return 0 + } + } + return total +} + +// A FormatError is an error in the wire format. +type FormatError string + +func (e FormatError) Error() string { + return "dbus: wire format error: " + string(e) +} diff --git a/vendor/github.com/godbus/dbus/v5/default_handler.go b/vendor/github.com/godbus/dbus/v5/default_handler.go new file mode 100644 index 0000000000000..13132c6b47970 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/default_handler.go @@ -0,0 +1,342 @@ +package dbus + +import ( + "bytes" + "reflect" + "strings" + "sync" +) + +func newIntrospectIntf(h *defaultHandler) *exportedIntf { + methods := make(map[string]Method) + methods["Introspect"] = exportedMethod{ + reflect.ValueOf(func(msg Message) (string, *Error) { + path := msg.Headers[FieldPath].value.(ObjectPath) + return h.introspectPath(path), nil + }), + } + return newExportedIntf(methods, true) +} + +//NewDefaultHandler returns an instance of the default +//call handler. This is useful if you want to implement only +//one of the two handlers but not both. +// +// Deprecated: this is the default value, don't use it, it will be unexported. +func NewDefaultHandler() *defaultHandler { + h := &defaultHandler{ + objects: make(map[ObjectPath]*exportedObj), + defaultIntf: make(map[string]*exportedIntf), + } + h.defaultIntf["org.freedesktop.DBus.Introspectable"] = newIntrospectIntf(h) + return h +} + +type defaultHandler struct { + sync.RWMutex + objects map[ObjectPath]*exportedObj + defaultIntf map[string]*exportedIntf +} + +func (h *defaultHandler) PathExists(path ObjectPath) bool { + _, ok := h.objects[path] + return ok +} + +func (h *defaultHandler) introspectPath(path ObjectPath) string { + subpath := make(map[string]struct{}) + var xml bytes.Buffer + xml.WriteString("") + for obj := range h.objects { + p := string(path) + if p != "/" { + p += "/" + } + if strings.HasPrefix(string(obj), p) { + node_name := strings.Split(string(obj[len(p):]), "/")[0] + subpath[node_name] = struct{}{} + } + } + for s := range subpath { + xml.WriteString("\n\t") + } + xml.WriteString("\n") + return xml.String() +} + +func (h *defaultHandler) LookupObject(path ObjectPath) (ServerObject, bool) { + h.RLock() + defer h.RUnlock() + object, ok := h.objects[path] + if ok { + return object, ok + } + + // If an object wasn't found for this exact path, + // look for a matching subtree registration + subtreeObject := newExportedObject() + path = path[:strings.LastIndex(string(path), "/")] + for len(path) > 0 { + object, ok = h.objects[path] + if ok { + for name, iface := range object.interfaces { + // Only include this handler if it registered for the subtree + if iface.isFallbackInterface() { + subtreeObject.interfaces[name] = iface + } + } + break + } + + path = path[:strings.LastIndex(string(path), "/")] + } + + for name, intf := range h.defaultIntf { + if _, exists := subtreeObject.interfaces[name]; exists { + continue + } + subtreeObject.interfaces[name] = intf + } + + return subtreeObject, true +} + +func (h *defaultHandler) AddObject(path ObjectPath, object *exportedObj) { + h.Lock() + h.objects[path] = object + h.Unlock() +} + +func (h *defaultHandler) DeleteObject(path ObjectPath) { + h.Lock() + delete(h.objects, path) + h.Unlock() +} + +type exportedMethod struct { + reflect.Value +} + +func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) { + t := m.Type() + + params := make([]reflect.Value, len(args)) + for i := 0; i < len(args); i++ { + params[i] = reflect.ValueOf(args[i]).Elem() + } + + ret := m.Value.Call(params) + var err error + nilErr := false // The reflection will find almost-nils, let's only pass back clean ones! + if t.NumOut() > 0 { + if e, ok := ret[t.NumOut()-1].Interface().(*Error); ok { // godbus *Error + nilErr = ret[t.NumOut()-1].IsNil() + ret = ret[:t.NumOut()-1] + err = e + } else if ret[t.NumOut()-1].Type().Implements(errType) { // Go error + i := ret[t.NumOut()-1].Interface() + if i == nil { + nilErr = ret[t.NumOut()-1].IsNil() + } else { + err = i.(error) + } + ret = ret[:t.NumOut()-1] + } + } + out := make([]interface{}, len(ret)) + for i, val := range ret { + out[i] = val.Interface() + } + if nilErr || err == nil { + //concrete type to interface nil is a special case + return out, nil + } + return out, err +} + +func (m exportedMethod) NumArguments() int { + return m.Value.Type().NumIn() +} + +func (m exportedMethod) ArgumentValue(i int) interface{} { + return reflect.Zero(m.Type().In(i)).Interface() +} + +func (m exportedMethod) NumReturns() int { + return m.Value.Type().NumOut() +} + +func (m exportedMethod) ReturnValue(i int) interface{} { + return reflect.Zero(m.Type().Out(i)).Interface() +} + +func newExportedObject() *exportedObj { + return &exportedObj{ + interfaces: make(map[string]*exportedIntf), + } +} + +type exportedObj struct { + mu sync.RWMutex + interfaces map[string]*exportedIntf +} + +func (obj *exportedObj) LookupInterface(name string) (Interface, bool) { + if name == "" { + return obj, true + } + obj.mu.RLock() + defer obj.mu.RUnlock() + intf, exists := obj.interfaces[name] + return intf, exists +} + +func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) { + obj.mu.Lock() + defer obj.mu.Unlock() + obj.interfaces[name] = iface +} + +func (obj *exportedObj) DeleteInterface(name string) { + obj.mu.Lock() + defer obj.mu.Unlock() + delete(obj.interfaces, name) +} + +func (obj *exportedObj) LookupMethod(name string) (Method, bool) { + obj.mu.RLock() + defer obj.mu.RUnlock() + for _, intf := range obj.interfaces { + method, exists := intf.LookupMethod(name) + if exists { + return method, exists + } + } + return nil, false +} + +func (obj *exportedObj) isFallbackInterface() bool { + return false +} + +func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf { + return &exportedIntf{ + methods: methods, + includeSubtree: includeSubtree, + } +} + +type exportedIntf struct { + methods map[string]Method + + // Whether or not this export is for the entire subtree + includeSubtree bool +} + +func (obj *exportedIntf) LookupMethod(name string) (Method, bool) { + out, exists := obj.methods[name] + return out, exists +} + +func (obj *exportedIntf) isFallbackInterface() bool { + return obj.includeSubtree +} + +//NewDefaultSignalHandler returns an instance of the default +//signal handler. This is useful if you want to implement only +//one of the two handlers but not both. +// +// Deprecated: this is the default value, don't use it, it will be unexported. +func NewDefaultSignalHandler() *defaultSignalHandler { + return &defaultSignalHandler{} +} + +type defaultSignalHandler struct { + mu sync.RWMutex + closed bool + signals []*signalChannelData +} + +func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) { + sh.mu.RLock() + defer sh.mu.RUnlock() + if sh.closed { + return + } + for _, scd := range sh.signals { + scd.deliver(signal) + } +} + +func (sh *defaultSignalHandler) Terminate() { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.closed { + return + } + + for _, scd := range sh.signals { + scd.close() + close(scd.ch) + } + sh.closed = true + sh.signals = nil +} + +func (sh *defaultSignalHandler) AddSignal(ch chan<- *Signal) { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.closed { + return + } + sh.signals = append(sh.signals, &signalChannelData{ + ch: ch, + done: make(chan struct{}), + }) +} + +func (sh *defaultSignalHandler) RemoveSignal(ch chan<- *Signal) { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.closed { + return + } + for i := len(sh.signals) - 1; i >= 0; i-- { + if ch == sh.signals[i].ch { + sh.signals[i].close() + copy(sh.signals[i:], sh.signals[i+1:]) + sh.signals[len(sh.signals)-1] = nil + sh.signals = sh.signals[:len(sh.signals)-1] + } + } +} + +type signalChannelData struct { + wg sync.WaitGroup + ch chan<- *Signal + done chan struct{} +} + +func (scd *signalChannelData) deliver(signal *Signal) { + select { + case scd.ch <- signal: + case <-scd.done: + return + default: + scd.wg.Add(1) + go scd.deferredDeliver(signal) + } +} + +func (scd *signalChannelData) deferredDeliver(signal *Signal) { + select { + case scd.ch <- signal: + case <-scd.done: + } + scd.wg.Done() +} + +func (scd *signalChannelData) close() { + close(scd.done) + scd.wg.Wait() // wait until all spawned goroutines return +} diff --git a/vendor/github.com/godbus/dbus/v5/doc.go b/vendor/github.com/godbus/dbus/v5/doc.go new file mode 100644 index 0000000000000..ade1df951cd22 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/doc.go @@ -0,0 +1,69 @@ +/* +Package dbus implements bindings to the D-Bus message bus system. + +To use the message bus API, you first need to connect to a bus (usually the +session or system bus). The acquired connection then can be used to call methods +on remote objects and emit or receive signals. Using the Export method, you can +arrange D-Bus methods calls to be directly translated to method calls on a Go +value. + +Conversion Rules + +For outgoing messages, Go types are automatically converted to the +corresponding D-Bus types. The following types are directly encoded as their +respective D-Bus equivalents: + + Go type | D-Bus type + ------------+----------- + byte | BYTE + bool | BOOLEAN + int16 | INT16 + uint16 | UINT16 + int | INT32 + uint | UINT32 + int32 | INT32 + uint32 | UINT32 + int64 | INT64 + uint64 | UINT64 + float64 | DOUBLE + string | STRING + ObjectPath | OBJECT_PATH + Signature | SIGNATURE + Variant | VARIANT + interface{} | VARIANT + UnixFDIndex | UNIX_FD + +Slices and arrays encode as ARRAYs of their element type. + +Maps encode as DICTs, provided that their key type can be used as a key for +a DICT. + +Structs other than Variant and Signature encode as a STRUCT containing their +exported fields. Fields whose tags contain `dbus:"-"` and unexported fields will +be skipped. + +Pointers encode as the value they're pointed to. + +Types convertible to one of the base types above will be mapped as the +base type. + +Trying to encode any other type or a slice, map or struct containing an +unsupported type will result in an InvalidTypeError. + +For incoming messages, the inverse of these rules are used, with the exception +of STRUCTs. Incoming STRUCTS are represented as a slice of empty interfaces +containing the struct fields in the correct order. The Store function can be +used to convert such values to Go structs. + +Unix FD passing + +Handling Unix file descriptors deserves special mention. To use them, you should +first check that they are supported on a connection by calling SupportsUnixFDs. +If it returns true, all method of Connection will translate messages containing +UnixFD's to messages that are accompanied by the given file descriptors with the +UnixFD values being substituted by the correct indices. Similarly, the indices +of incoming messages are automatically resolved. It shouldn't be necessary to use +UnixFDIndex. + +*/ +package dbus diff --git a/vendor/github.com/godbus/dbus/v5/encoder.go b/vendor/github.com/godbus/dbus/v5/encoder.go new file mode 100644 index 0000000000000..adfbb75c5595b --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/encoder.go @@ -0,0 +1,210 @@ +package dbus + +import ( + "bytes" + "encoding/binary" + "io" + "reflect" +) + +// An encoder encodes values to the D-Bus wire format. +type encoder struct { + out io.Writer + order binary.ByteOrder + pos int +} + +// NewEncoder returns a new encoder that writes to out in the given byte order. +func newEncoder(out io.Writer, order binary.ByteOrder) *encoder { + return newEncoderAtOffset(out, 0, order) +} + +// newEncoderAtOffset returns a new encoder that writes to out in the given +// byte order. Specify the offset to initialize pos for proper alignment +// computation. +func newEncoderAtOffset(out io.Writer, offset int, order binary.ByteOrder) *encoder { + enc := new(encoder) + enc.out = out + enc.order = order + enc.pos = offset + return enc +} + +// Aligns the next output to be on a multiple of n. Panics on write errors. +func (enc *encoder) align(n int) { + pad := enc.padding(0, n) + if pad > 0 { + empty := make([]byte, pad) + if _, err := enc.out.Write(empty); err != nil { + panic(err) + } + enc.pos += pad + } +} + +// pad returns the number of bytes of padding, based on current position and additional offset. +// and alignment. +func (enc *encoder) padding(offset, algn int) int { + abs := enc.pos + offset + if abs%algn != 0 { + newabs := (abs + algn - 1) & ^(algn - 1) + return newabs - abs + } + return 0 +} + +// Calls binary.Write(enc.out, enc.order, v) and panics on write errors. +func (enc *encoder) binwrite(v interface{}) { + if err := binary.Write(enc.out, enc.order, v); err != nil { + panic(err) + } +} + +// Encode encodes the given values to the underlying reader. All written values +// are aligned properly as required by the D-Bus spec. +func (enc *encoder) Encode(vs ...interface{}) (err error) { + defer func() { + err, _ = recover().(error) + }() + for _, v := range vs { + enc.encode(reflect.ValueOf(v), 0) + } + return nil +} + +// encode encodes the given value to the writer and panics on error. depth holds +// the depth of the container nesting. +func (enc *encoder) encode(v reflect.Value, depth int) { + enc.align(alignment(v.Type())) + switch v.Kind() { + case reflect.Uint8: + var b [1]byte + b[0] = byte(v.Uint()) + if _, err := enc.out.Write(b[:]); err != nil { + panic(err) + } + enc.pos++ + case reflect.Bool: + if v.Bool() { + enc.encode(reflect.ValueOf(uint32(1)), depth) + } else { + enc.encode(reflect.ValueOf(uint32(0)), depth) + } + case reflect.Int16: + enc.binwrite(int16(v.Int())) + enc.pos += 2 + case reflect.Uint16: + enc.binwrite(uint16(v.Uint())) + enc.pos += 2 + case reflect.Int, reflect.Int32: + enc.binwrite(int32(v.Int())) + enc.pos += 4 + case reflect.Uint, reflect.Uint32: + enc.binwrite(uint32(v.Uint())) + enc.pos += 4 + case reflect.Int64: + enc.binwrite(v.Int()) + enc.pos += 8 + case reflect.Uint64: + enc.binwrite(v.Uint()) + enc.pos += 8 + case reflect.Float64: + enc.binwrite(v.Float()) + enc.pos += 8 + case reflect.String: + enc.encode(reflect.ValueOf(uint32(len(v.String()))), depth) + b := make([]byte, v.Len()+1) + copy(b, v.String()) + b[len(b)-1] = 0 + n, err := enc.out.Write(b) + if err != nil { + panic(err) + } + enc.pos += n + case reflect.Ptr: + enc.encode(v.Elem(), depth) + case reflect.Slice, reflect.Array: + if depth >= 64 { + panic(FormatError("input exceeds container depth limit")) + } + // Lookahead offset: 4 bytes for uint32 length (with alignment), + // plus alignment for elements. + n := enc.padding(0, 4) + 4 + offset := enc.pos + n + enc.padding(n, alignment(v.Type().Elem())) + + var buf bytes.Buffer + bufenc := newEncoderAtOffset(&buf, offset, enc.order) + + for i := 0; i < v.Len(); i++ { + bufenc.encode(v.Index(i), depth+1) + } + enc.encode(reflect.ValueOf(uint32(buf.Len())), depth) + length := buf.Len() + enc.align(alignment(v.Type().Elem())) + if _, err := buf.WriteTo(enc.out); err != nil { + panic(err) + } + enc.pos += length + case reflect.Struct: + if depth >= 64 && v.Type() != signatureType { + panic(FormatError("input exceeds container depth limit")) + } + switch t := v.Type(); t { + case signatureType: + str := v.Field(0) + enc.encode(reflect.ValueOf(byte(str.Len())), depth+1) + b := make([]byte, str.Len()+1) + copy(b, str.String()) + b[len(b)-1] = 0 + n, err := enc.out.Write(b) + if err != nil { + panic(err) + } + enc.pos += n + case variantType: + variant := v.Interface().(Variant) + enc.encode(reflect.ValueOf(variant.sig), depth+1) + enc.encode(reflect.ValueOf(variant.value), depth+1) + default: + for i := 0; i < v.Type().NumField(); i++ { + field := t.Field(i) + if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { + enc.encode(v.Field(i), depth+1) + } + } + } + case reflect.Map: + // Maps are arrays of structures, so they actually increase the depth by + // 2. + if depth >= 63 { + panic(FormatError("input exceeds container depth limit")) + } + if !isKeyType(v.Type().Key()) { + panic(InvalidTypeError{v.Type()}) + } + keys := v.MapKeys() + // Lookahead offset: 4 bytes for uint32 length (with alignment), + // plus 8-byte alignment + n := enc.padding(0, 4) + 4 + offset := enc.pos + n + enc.padding(n, 8) + + var buf bytes.Buffer + bufenc := newEncoderAtOffset(&buf, offset, enc.order) + for _, k := range keys { + bufenc.align(8) + bufenc.encode(k, depth+2) + bufenc.encode(v.MapIndex(k), depth+2) + } + enc.encode(reflect.ValueOf(uint32(buf.Len())), depth) + length := buf.Len() + enc.align(8) + if _, err := buf.WriteTo(enc.out); err != nil { + panic(err) + } + enc.pos += length + case reflect.Interface: + enc.encode(reflect.ValueOf(MakeVariant(v.Interface())), depth) + default: + panic(InvalidTypeError{v.Type()}) + } +} diff --git a/vendor/github.com/godbus/dbus/v5/export.go b/vendor/github.com/godbus/dbus/v5/export.go new file mode 100644 index 0000000000000..2447b51d469d3 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/export.go @@ -0,0 +1,441 @@ +package dbus + +import ( + "errors" + "fmt" + "reflect" + "strings" +) + +var ( + ErrMsgInvalidArg = Error{ + "org.freedesktop.DBus.Error.InvalidArgs", + []interface{}{"Invalid type / number of args"}, + } + ErrMsgNoObject = Error{ + "org.freedesktop.DBus.Error.NoSuchObject", + []interface{}{"No such object"}, + } + ErrMsgUnknownMethod = Error{ + "org.freedesktop.DBus.Error.UnknownMethod", + []interface{}{"Unknown / invalid method"}, + } + ErrMsgUnknownInterface = Error{ + "org.freedesktop.DBus.Error.UnknownInterface", + []interface{}{"Object does not implement the interface"}, + } +) + +func MakeFailedError(err error) *Error { + return &Error{ + "org.freedesktop.DBus.Error.Failed", + []interface{}{err.Error()}, + } +} + +// Sender is a type which can be used in exported methods to receive the message +// sender. +type Sender string + +func computeMethodName(name string, mapping map[string]string) string { + newname, ok := mapping[name] + if ok { + name = newname + } + return name +} + +func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Value { + if in == nil { + return nil + } + methods := make(map[string]reflect.Value) + val := reflect.ValueOf(in) + typ := val.Type() + for i := 0; i < typ.NumMethod(); i++ { + methtype := typ.Method(i) + method := val.Method(i) + t := method.Type() + // only track valid methods must return *Error as last arg + // and must be exported + if t.NumOut() == 0 || + t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) || + methtype.PkgPath != "" { + continue + } + // map names while building table + methods[computeMethodName(methtype.Name, mapping)] = method + } + return methods +} + +func getAllMethods(in interface{}, mapping map[string]string) map[string]reflect.Value { + if in == nil { + return nil + } + methods := make(map[string]reflect.Value) + val := reflect.ValueOf(in) + typ := val.Type() + for i := 0; i < typ.NumMethod(); i++ { + methtype := typ.Method(i) + method := val.Method(i) + // map names while building table + methods[computeMethodName(methtype.Name, mapping)] = method + } + return methods +} + +func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) { + pointers := make([]interface{}, m.NumArguments()) + decode := make([]interface{}, 0, len(body)) + + for i := 0; i < m.NumArguments(); i++ { + tp := reflect.TypeOf(m.ArgumentValue(i)) + val := reflect.New(tp) + pointers[i] = val.Interface() + if tp == reflect.TypeOf((*Sender)(nil)).Elem() { + val.Elem().SetString(sender) + } else if tp == reflect.TypeOf((*Message)(nil)).Elem() { + val.Elem().Set(reflect.ValueOf(*msg)) + } else { + decode = append(decode, pointers[i]) + } + } + + if len(decode) != len(body) { + return nil, ErrMsgInvalidArg + } + + if err := Store(body, decode...); err != nil { + return nil, ErrMsgInvalidArg + } + + return pointers, nil +} + +func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) { + if decoder, ok := m.(ArgumentDecoder); ok { + return decoder.DecodeArguments(conn, sender, msg, msg.Body) + } + return standardMethodArgumentDecode(m, sender, msg, msg.Body) +} + +// handleCall handles the given method call (i.e. looks if it's one of the +// pre-implemented ones and searches for a corresponding handler if not). +func (conn *Conn) handleCall(msg *Message) { + name := msg.Headers[FieldMember].value.(string) + path := msg.Headers[FieldPath].value.(ObjectPath) + ifaceName, _ := msg.Headers[FieldInterface].value.(string) + sender, hasSender := msg.Headers[FieldSender].value.(string) + serial := msg.serial + if ifaceName == "org.freedesktop.DBus.Peer" { + switch name { + case "Ping": + conn.sendReply(sender, serial) + case "GetMachineId": + conn.sendReply(sender, serial, conn.uuid) + default: + conn.sendError(ErrMsgUnknownMethod, sender, serial) + } + return + } + if len(name) == 0 { + conn.sendError(ErrMsgUnknownMethod, sender, serial) + } + + object, ok := conn.handler.LookupObject(path) + if !ok { + conn.sendError(ErrMsgNoObject, sender, serial) + return + } + + iface, exists := object.LookupInterface(ifaceName) + if !exists { + conn.sendError(ErrMsgUnknownInterface, sender, serial) + return + } + + m, exists := iface.LookupMethod(name) + if !exists { + conn.sendError(ErrMsgUnknownMethod, sender, serial) + return + } + args, err := conn.decodeArguments(m, sender, msg) + if err != nil { + conn.sendError(err, sender, serial) + return + } + + ret, err := m.Call(args...) + if err != nil { + conn.sendError(err, sender, serial) + return + } + + if msg.Flags&FlagNoReplyExpected == 0 { + reply := new(Message) + reply.Type = TypeMethodReply + reply.Headers = make(map[HeaderField]Variant) + if hasSender { + reply.Headers[FieldDestination] = msg.Headers[FieldSender] + } + reply.Headers[FieldReplySerial] = MakeVariant(msg.serial) + reply.Body = make([]interface{}, len(ret)) + for i := 0; i < len(ret); i++ { + reply.Body[i] = ret[i] + } + reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) + + conn.sendMessageAndIfClosed(reply, nil) + } +} + +// Emit emits the given signal on the message bus. The name parameter must be +// formatted as "interface.member", e.g., "org.freedesktop.DBus.NameLost". +func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) error { + if !path.IsValid() { + return errors.New("dbus: invalid object path") + } + i := strings.LastIndex(name, ".") + if i == -1 { + return errors.New("dbus: invalid method name") + } + iface := name[:i] + member := name[i+1:] + if !isValidMember(member) { + return errors.New("dbus: invalid method name") + } + if !isValidInterface(iface) { + return errors.New("dbus: invalid interface name") + } + msg := new(Message) + msg.Type = TypeSignal + msg.Headers = make(map[HeaderField]Variant) + msg.Headers[FieldInterface] = MakeVariant(iface) + msg.Headers[FieldMember] = MakeVariant(member) + msg.Headers[FieldPath] = MakeVariant(path) + msg.Body = values + if len(values) > 0 { + msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) + } + + var closed bool + conn.sendMessageAndIfClosed(msg, func() { + closed = true + }) + if closed { + return ErrClosed + } + return nil +} + +// Export registers the given value to be exported as an object on the +// message bus. +// +// If a method call on the given path and interface is received, an exported +// method with the same name is called with v as the receiver if the +// parameters match and the last return value is of type *Error. If this +// *Error is not nil, it is sent back to the caller as an error. +// Otherwise, a method reply is sent with the other return values as its body. +// +// Any parameters with the special type Sender are set to the sender of the +// dbus message when the method is called. Parameters of this type do not +// contribute to the dbus signature of the method (i.e. the method is exposed +// as if the parameters of type Sender were not there). +// +// Similarly, any parameters with the type Message are set to the raw message +// received on the bus. Again, parameters of this type do not contribute to the +// dbus signature of the method. +// +// Every method call is executed in a new goroutine, so the method may be called +// in multiple goroutines at once. +// +// Method calls on the interface org.freedesktop.DBus.Peer will be automatically +// handled for every object. +// +// Passing nil as the first parameter will cause conn to cease handling calls on +// the given combination of path and interface. +// +// Export returns an error if path is not a valid path name. +func (conn *Conn) Export(v interface{}, path ObjectPath, iface string) error { + return conn.ExportWithMap(v, nil, path, iface) +} + +// ExportAll registers all exported methods defined by the given object on +// the message bus. +// +// Unlike Export there is no requirement to have the last parameter as type +// *Error. If you want to be able to return error then you can append an error +// type parameter to your method signature. If the error returned is not nil, +// it is sent back to the caller as an error. Otherwise, a method reply is +// sent with the other return values as its body. +func (conn *Conn) ExportAll(v interface{}, path ObjectPath, iface string) error { + return conn.export(getAllMethods(v, nil), path, iface, false) +} + +// ExportWithMap works exactly like Export but provides the ability to remap +// method names (e.g. export a lower-case method). +// +// The keys in the map are the real method names (exported on the struct), and +// the values are the method names to be exported on DBus. +func (conn *Conn) ExportWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error { + return conn.export(getMethods(v, mapping), path, iface, false) +} + +// ExportSubtree works exactly like Export but registers the given value for +// an entire subtree rather under the root path provided. +// +// In order to make this useful, one parameter in each of the value's exported +// methods should be a Message, in which case it will contain the raw message +// (allowing one to get access to the path that caused the method to be called). +// +// Note that more specific export paths take precedence over less specific. For +// example, a method call using the ObjectPath /foo/bar/baz will call a method +// exported on /foo/bar before a method exported on /foo. +func (conn *Conn) ExportSubtree(v interface{}, path ObjectPath, iface string) error { + return conn.ExportSubtreeWithMap(v, nil, path, iface) +} + +// ExportSubtreeWithMap works exactly like ExportSubtree but provides the +// ability to remap method names (e.g. export a lower-case method). +// +// The keys in the map are the real method names (exported on the struct), and +// the values are the method names to be exported on DBus. +func (conn *Conn) ExportSubtreeWithMap(v interface{}, mapping map[string]string, path ObjectPath, iface string) error { + return conn.export(getMethods(v, mapping), path, iface, true) +} + +// ExportMethodTable like Export registers the given methods as an object +// on the message bus. Unlike Export the it uses a method table to define +// the object instead of a native go object. +// +// The method table is a map from method name to function closure +// representing the method. This allows an object exported on the bus to not +// necessarily be a native go object. It can be useful for generating exposed +// methods on the fly. +// +// Any non-function objects in the method table are ignored. +func (conn *Conn) ExportMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error { + return conn.exportMethodTable(methods, path, iface, false) +} + +// Like ExportSubtree, but with the same caveats as ExportMethodTable. +func (conn *Conn) ExportSubtreeMethodTable(methods map[string]interface{}, path ObjectPath, iface string) error { + return conn.exportMethodTable(methods, path, iface, true) +} + +func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectPath, iface string, includeSubtree bool) error { + var out map[string]reflect.Value + if methods != nil { + out = make(map[string]reflect.Value) + for name, method := range methods { + rval := reflect.ValueOf(method) + if rval.Kind() != reflect.Func { + continue + } + t := rval.Type() + // only track valid methods must return *Error as last arg + if t.NumOut() == 0 || + t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) { + continue + } + out[name] = rval + } + } + return conn.export(out, path, iface, includeSubtree) +} + +func (conn *Conn) unexport(h *defaultHandler, path ObjectPath, iface string) error { + if h.PathExists(path) { + obj := h.objects[path] + obj.DeleteInterface(iface) + if len(obj.interfaces) == 0 { + h.DeleteObject(path) + } + } + return nil +} + +// export is the worker function for all exports/registrations. +func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error { + h, ok := conn.handler.(*defaultHandler) + if !ok { + return fmt.Errorf( + `dbus: export only allowed on the default handler. Received: %T"`, + conn.handler) + } + + if !path.IsValid() { + return fmt.Errorf(`dbus: Invalid path name: "%s"`, path) + } + + // Remove a previous export if the interface is nil + if methods == nil { + return conn.unexport(h, path, iface) + } + + // If this is the first handler for this path, make a new map to hold all + // handlers for this path. + if !h.PathExists(path) { + h.AddObject(path, newExportedObject()) + } + + exportedMethods := make(map[string]Method) + for name, method := range methods { + exportedMethods[name] = exportedMethod{method} + } + + // Finally, save this handler + obj := h.objects[path] + obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree)) + + return nil +} + +// ReleaseName calls org.freedesktop.DBus.ReleaseName and awaits a response. +func (conn *Conn) ReleaseName(name string) (ReleaseNameReply, error) { + var r uint32 + err := conn.busObj.Call("org.freedesktop.DBus.ReleaseName", 0, name).Store(&r) + if err != nil { + return 0, err + } + return ReleaseNameReply(r), nil +} + +// RequestName calls org.freedesktop.DBus.RequestName and awaits a response. +func (conn *Conn) RequestName(name string, flags RequestNameFlags) (RequestNameReply, error) { + var r uint32 + err := conn.busObj.Call("org.freedesktop.DBus.RequestName", 0, name, flags).Store(&r) + if err != nil { + return 0, err + } + return RequestNameReply(r), nil +} + +// ReleaseNameReply is the reply to a ReleaseName call. +type ReleaseNameReply uint32 + +const ( + ReleaseNameReplyReleased ReleaseNameReply = 1 + iota + ReleaseNameReplyNonExistent + ReleaseNameReplyNotOwner +) + +// RequestNameFlags represents the possible flags for a RequestName call. +type RequestNameFlags uint32 + +const ( + NameFlagAllowReplacement RequestNameFlags = 1 << iota + NameFlagReplaceExisting + NameFlagDoNotQueue +) + +// RequestNameReply is the reply to a RequestName call. +type RequestNameReply uint32 + +const ( + RequestNameReplyPrimaryOwner RequestNameReply = 1 + iota + RequestNameReplyInQueue + RequestNameReplyExists + RequestNameReplyAlreadyOwner +) diff --git a/vendor/github.com/godbus/dbus/v5/go.mod b/vendor/github.com/godbus/dbus/v5/go.mod new file mode 100644 index 0000000000000..15b920203c554 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/go.mod @@ -0,0 +1,3 @@ +module github.com/godbus/dbus/v5 + +go 1.12 diff --git a/vendor/github.com/godbus/dbus/homedir.go b/vendor/github.com/godbus/dbus/v5/homedir.go similarity index 100% rename from vendor/github.com/godbus/dbus/homedir.go rename to vendor/github.com/godbus/dbus/v5/homedir.go diff --git a/vendor/github.com/godbus/dbus/homedir_dynamic.go b/vendor/github.com/godbus/dbus/v5/homedir_dynamic.go similarity index 100% rename from vendor/github.com/godbus/dbus/homedir_dynamic.go rename to vendor/github.com/godbus/dbus/v5/homedir_dynamic.go diff --git a/vendor/github.com/godbus/dbus/homedir_static.go b/vendor/github.com/godbus/dbus/v5/homedir_static.go similarity index 100% rename from vendor/github.com/godbus/dbus/homedir_static.go rename to vendor/github.com/godbus/dbus/v5/homedir_static.go diff --git a/vendor/github.com/godbus/dbus/v5/match.go b/vendor/github.com/godbus/dbus/v5/match.go new file mode 100644 index 0000000000000..5a607e53e4107 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/match.go @@ -0,0 +1,89 @@ +package dbus + +import ( + "strconv" + "strings" +) + +// MatchOption specifies option for dbus routing match rule. Options can be constructed with WithMatch* helpers. +// For full list of available options consult +// https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules +type MatchOption struct { + key string + value string +} + +func formatMatchOptions(options []MatchOption) string { + items := make([]string, 0, len(options)) + for _, option := range options { + items = append(items, option.key+"='"+option.value+"'") + } + return strings.Join(items, ",") +} + +// WithMatchOption creates match option with given key and value +func WithMatchOption(key, value string) MatchOption { + return MatchOption{key, value} +} + +// doesn't make sense to export this option because clients can only +// subscribe to messages with signal type. +func withMatchType(typ string) MatchOption { + return WithMatchOption("type", typ) +} + +// WithMatchSender sets sender match option. +func WithMatchSender(sender string) MatchOption { + return WithMatchOption("sender", sender) +} + +// WithMatchSender sets interface match option. +func WithMatchInterface(iface string) MatchOption { + return WithMatchOption("interface", iface) +} + +// WithMatchMember sets member match option. +func WithMatchMember(member string) MatchOption { + return WithMatchOption("member", member) +} + +// WithMatchObjectPath creates match option that filters events based on given path +func WithMatchObjectPath(path ObjectPath) MatchOption { + return WithMatchOption("path", string(path)) +} + +// WithMatchPathNamespace sets path_namespace match option. +func WithMatchPathNamespace(namespace ObjectPath) MatchOption { + return WithMatchOption("path_namespace", string(namespace)) +} + +// WithMatchDestination sets destination match option. +func WithMatchDestination(destination string) MatchOption { + return WithMatchOption("destination", destination) +} + +// WithMatchArg sets argN match option, range of N is 0 to 63. +func WithMatchArg(argIdx int, value string) MatchOption { + if argIdx < 0 || argIdx > 63 { + panic("range of argument index is 0 to 63") + } + return WithMatchOption("arg"+strconv.Itoa(argIdx), value) +} + +// WithMatchArgPath sets argN path match option, range of N is 0 to 63. +func WithMatchArgPath(argIdx int, path string) MatchOption { + if argIdx < 0 || argIdx > 63 { + panic("range of argument index is 0 to 63") + } + return WithMatchOption("arg"+strconv.Itoa(argIdx)+"path", path) +} + +// WithMatchArg0Namespace sets arg0namespace match option. +func WithMatchArg0Namespace(arg0Namespace string) MatchOption { + return WithMatchOption("arg0namespace", arg0Namespace) +} + +// WithMatchEavesdrop sets eavesdrop match option. +func WithMatchEavesdrop(eavesdrop bool) MatchOption { + return WithMatchOption("eavesdrop", strconv.FormatBool(eavesdrop)) +} diff --git a/vendor/github.com/godbus/dbus/message.go b/vendor/github.com/godbus/dbus/v5/message.go similarity index 100% rename from vendor/github.com/godbus/dbus/message.go rename to vendor/github.com/godbus/dbus/v5/message.go diff --git a/vendor/github.com/godbus/dbus/v5/object.go b/vendor/github.com/godbus/dbus/v5/object.go new file mode 100644 index 0000000000000..664abb7fbabb6 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/object.go @@ -0,0 +1,174 @@ +package dbus + +import ( + "context" + "errors" + "strings" +) + +// BusObject is the interface of a remote object on which methods can be +// invoked. +type BusObject interface { + Call(method string, flags Flags, args ...interface{}) *Call + CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call + Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call + GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call + AddMatchSignal(iface, member string, options ...MatchOption) *Call + RemoveMatchSignal(iface, member string, options ...MatchOption) *Call + GetProperty(p string) (Variant, error) + StoreProperty(p string, value interface{}) error + SetProperty(p string, v interface{}) error + Destination() string + Path() ObjectPath +} + +// Object represents a remote object on which methods can be invoked. +type Object struct { + conn *Conn + dest string + path ObjectPath +} + +// Call calls a method with (*Object).Go and waits for its reply. +func (o *Object) Call(method string, flags Flags, args ...interface{}) *Call { + return <-o.createCall(context.Background(), method, flags, make(chan *Call, 1), args...).Done +} + +// CallWithContext acts like Call but takes a context +func (o *Object) CallWithContext(ctx context.Context, method string, flags Flags, args ...interface{}) *Call { + return <-o.createCall(ctx, method, flags, make(chan *Call, 1), args...).Done +} + +// AddMatchSignal subscribes BusObject to signals from specified interface, +// method (member). Additional filter rules can be added via WithMatch* option constructors. +// Note: To filter events by object path you have to specify this path via an option. +// +// Deprecated: use (*Conn) AddMatchSignal instead. +func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call { + base := []MatchOption{ + withMatchType("signal"), + WithMatchInterface(iface), + WithMatchMember(member), + } + + options = append(base, options...) + return o.conn.BusObject().Call( + "org.freedesktop.DBus.AddMatch", + 0, + formatMatchOptions(options), + ) +} + +// RemoveMatchSignal unsubscribes BusObject from signals from specified interface, +// method (member). Additional filter rules can be added via WithMatch* option constructors +// +// Deprecated: use (*Conn) RemoveMatchSignal instead. +func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call { + base := []MatchOption{ + withMatchType("signal"), + WithMatchInterface(iface), + WithMatchMember(member), + } + + options = append(base, options...) + return o.conn.BusObject().Call( + "org.freedesktop.DBus.RemoveMatch", + 0, + formatMatchOptions(options), + ) +} + +// Go calls a method with the given arguments asynchronously. It returns a +// Call structure representing this method call. The passed channel will +// return the same value once the call is done. If ch is nil, a new channel +// will be allocated. Otherwise, ch has to be buffered or Go will panic. +// +// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure +// is returned with any error in Err and a closed channel in Done containing +// the returned Call as it's one entry. +// +// If the method parameter contains a dot ('.'), the part before the last dot +// specifies the interface on which the method is called. +func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface{}) *Call { + return o.createCall(context.Background(), method, flags, ch, args...) +} + +// GoWithContext acts like Go but takes a context +func (o *Object) GoWithContext(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call { + return o.createCall(ctx, method, flags, ch, args...) +} + +func (o *Object) createCall(ctx context.Context, method string, flags Flags, ch chan *Call, args ...interface{}) *Call { + if ctx == nil { + panic("nil context") + } + iface := "" + i := strings.LastIndex(method, ".") + if i != -1 { + iface = method[:i] + } + method = method[i+1:] + msg := new(Message) + msg.Type = TypeMethodCall + msg.Flags = flags & (FlagNoAutoStart | FlagNoReplyExpected) + msg.Headers = make(map[HeaderField]Variant) + msg.Headers[FieldPath] = MakeVariant(o.path) + msg.Headers[FieldDestination] = MakeVariant(o.dest) + msg.Headers[FieldMember] = MakeVariant(method) + if iface != "" { + msg.Headers[FieldInterface] = MakeVariant(iface) + } + msg.Body = args + if len(args) > 0 { + msg.Headers[FieldSignature] = MakeVariant(SignatureOf(args...)) + } + return o.conn.SendWithContext(ctx, msg, ch) +} + +// GetProperty calls org.freedesktop.DBus.Properties.Get on the given +// object. The property name must be given in interface.member notation. +func (o *Object) GetProperty(p string) (Variant, error) { + var result Variant + err := o.StoreProperty(p, &result) + return result, err +} + +// StoreProperty calls org.freedesktop.DBus.Properties.Get on the given +// object. The property name must be given in interface.member notation. +// It stores the returned property into the provided value. +func (o *Object) StoreProperty(p string, value interface{}) error { + idx := strings.LastIndex(p, ".") + if idx == -1 || idx+1 == len(p) { + return errors.New("dbus: invalid property " + p) + } + + iface := p[:idx] + prop := p[idx+1:] + + return o.Call("org.freedesktop.DBus.Properties.Get", 0, iface, prop). + Store(value) +} + +// SetProperty calls org.freedesktop.DBus.Properties.Set on the given +// object. The property name must be given in interface.member notation. +func (o *Object) SetProperty(p string, v interface{}) error { + idx := strings.LastIndex(p, ".") + if idx == -1 || idx+1 == len(p) { + return errors.New("dbus: invalid property " + p) + } + + iface := p[:idx] + prop := p[idx+1:] + + return o.Call("org.freedesktop.DBus.Properties.Set", 0, iface, prop, v).Err +} + +// Destination returns the destination that calls on (o *Object) are sent to. +func (o *Object) Destination() string { + return o.dest +} + +// Path returns the path that calls on (o *Object") are sent to. +func (o *Object) Path() ObjectPath { + return o.path +} diff --git a/vendor/github.com/godbus/dbus/v5/sequence.go b/vendor/github.com/godbus/dbus/v5/sequence.go new file mode 100644 index 0000000000000..89435d3933bdb --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/sequence.go @@ -0,0 +1,24 @@ +package dbus + +// Sequence represents the value of a monotonically increasing counter. +type Sequence uint64 + +const ( + // NoSequence indicates the absence of a sequence value. + NoSequence Sequence = 0 +) + +// sequenceGenerator represents a monotonically increasing counter. +type sequenceGenerator struct { + nextSequence Sequence +} + +func (generator *sequenceGenerator) next() Sequence { + result := generator.nextSequence + generator.nextSequence++ + return result +} + +func newSequenceGenerator() *sequenceGenerator { + return &sequenceGenerator{nextSequence: 1} +} diff --git a/vendor/github.com/godbus/dbus/v5/sequential_handler.go b/vendor/github.com/godbus/dbus/v5/sequential_handler.go new file mode 100644 index 0000000000000..ef2fcdba179cf --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/sequential_handler.go @@ -0,0 +1,125 @@ +package dbus + +import ( + "sync" +) + +// NewSequentialSignalHandler returns an instance of a new +// signal handler that guarantees sequential processing of signals. It is a +// guarantee of this signal handler that signals will be written to +// channels in the order they are received on the DBus connection. +func NewSequentialSignalHandler() SignalHandler { + return &sequentialSignalHandler{} +} + +type sequentialSignalHandler struct { + mu sync.RWMutex + closed bool + signals []*sequentialSignalChannelData +} + +func (sh *sequentialSignalHandler) DeliverSignal(intf, name string, signal *Signal) { + sh.mu.RLock() + defer sh.mu.RUnlock() + if sh.closed { + return + } + for _, scd := range sh.signals { + scd.deliver(signal) + } +} + +func (sh *sequentialSignalHandler) Terminate() { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.closed { + return + } + + for _, scd := range sh.signals { + scd.close() + close(scd.ch) + } + sh.closed = true + sh.signals = nil +} + +func (sh *sequentialSignalHandler) AddSignal(ch chan<- *Signal) { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.closed { + return + } + sh.signals = append(sh.signals, newSequentialSignalChannelData(ch)) +} + +func (sh *sequentialSignalHandler) RemoveSignal(ch chan<- *Signal) { + sh.mu.Lock() + defer sh.mu.Unlock() + if sh.closed { + return + } + for i := len(sh.signals) - 1; i >= 0; i-- { + if ch == sh.signals[i].ch { + sh.signals[i].close() + copy(sh.signals[i:], sh.signals[i+1:]) + sh.signals[len(sh.signals)-1] = nil + sh.signals = sh.signals[:len(sh.signals)-1] + } + } +} + +type sequentialSignalChannelData struct { + ch chan<- *Signal + in chan *Signal + done chan struct{} +} + +func newSequentialSignalChannelData(ch chan<- *Signal) *sequentialSignalChannelData { + scd := &sequentialSignalChannelData{ + ch: ch, + in: make(chan *Signal), + done: make(chan struct{}), + } + go scd.bufferSignals() + return scd +} + +func (scd *sequentialSignalChannelData) bufferSignals() { + defer close(scd.done) + + // Ensure that signals are delivered to scd.ch in the same + // order they are received from scd.in. + var queue []*Signal + for { + if len(queue) == 0 { + signal, ok := <- scd.in + if !ok { + return + } + queue = append(queue, signal) + } + select { + case scd.ch <- queue[0]: + copy(queue, queue[1:]) + queue[len(queue)-1] = nil + queue = queue[:len(queue)-1] + case signal, ok := <-scd.in: + if !ok { + return + } + queue = append(queue, signal) + } + } +} + +func (scd *sequentialSignalChannelData) deliver(signal *Signal) { + scd.in <- signal +} + +func (scd *sequentialSignalChannelData) close() { + close(scd.in) + // Ensure that bufferSignals() has exited and won't attempt + // any future sends on scd.ch + <-scd.done +} diff --git a/vendor/github.com/godbus/dbus/v5/server_interfaces.go b/vendor/github.com/godbus/dbus/v5/server_interfaces.go new file mode 100644 index 0000000000000..79d97edf3ece9 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/server_interfaces.go @@ -0,0 +1,107 @@ +package dbus + +// Terminator allows a handler to implement a shutdown mechanism that +// is called when the connection terminates. +type Terminator interface { + Terminate() +} + +// Handler is the representation of a D-Bus Application. +// +// The Handler must have a way to lookup objects given +// an ObjectPath. The returned object must implement the +// ServerObject interface. +type Handler interface { + LookupObject(path ObjectPath) (ServerObject, bool) +} + +// ServerObject is the representation of an D-Bus Object. +// +// Objects are registered at a path for a given Handler. +// The Objects implement D-Bus interfaces. The semantics +// of Interface lookup is up to the implementation of +// the ServerObject. The ServerObject implementation may +// choose to implement empty string as a valid interface +// represeting all methods or not per the D-Bus specification. +type ServerObject interface { + LookupInterface(name string) (Interface, bool) +} + +// An Interface is the representation of a D-Bus Interface. +// +// Interfaces are a grouping of methods implemented by the Objects. +// Interfaces are responsible for routing method calls. +type Interface interface { + LookupMethod(name string) (Method, bool) +} + +// A Method represents the exposed methods on D-Bus. +type Method interface { + // Call requires that all arguments are decoded before being passed to it. + Call(args ...interface{}) ([]interface{}, error) + NumArguments() int + NumReturns() int + // ArgumentValue returns a representative value for the argument at position + // it should be of the proper type. reflect.Zero would be a good mechanism + // to use for this Value. + ArgumentValue(position int) interface{} + // ReturnValue returns a representative value for the return at position + // it should be of the proper type. reflect.Zero would be a good mechanism + // to use for this Value. + ReturnValue(position int) interface{} +} + +// An Argument Decoder can decode arguments using the non-standard mechanism +// +// If a method implements this interface then the non-standard +// decoder will be used. +// +// Method arguments must be decoded from the message. +// The mechanism for doing this will vary based on the +// implementation of the method. A normal approach is provided +// as part of this library, but may be replaced with +// any other decoding scheme. +type ArgumentDecoder interface { + // To decode the arguments of a method the sender and message are + // provided incase the semantics of the implementer provides access + // to these as part of the method invocation. + DecodeArguments(conn *Conn, sender string, msg *Message, args []interface{}) ([]interface{}, error) +} + +// A SignalHandler is responsible for delivering a signal. +// +// Signal delivery may be changed from the default channel +// based approach by Handlers implementing the SignalHandler +// interface. +type SignalHandler interface { + DeliverSignal(iface, name string, signal *Signal) +} + +// SignalRegistrar manages signal delivery channels. +// +// This is an optional set of methods for `SignalHandler`. +type SignalRegistrar interface { + AddSignal(ch chan<- *Signal) + RemoveSignal(ch chan<- *Signal) +} + +// A DBusError is used to convert a generic object to a D-Bus error. +// +// Any custom error mechanism may implement this interface to provide +// a custom encoding of the error on D-Bus. By default if a normal +// error is returned, it will be encoded as the generic +// "org.freedesktop.DBus.Error.Failed" error. By implementing this +// interface as well a custom encoding may be provided. +type DBusError interface { + DBusError() (string, []interface{}) +} + +// SerialGenerator is responsible for serials generation. +// +// Different approaches for the serial generation can be used, +// maintaining a map guarded with a mutex (the standard way) or +// simply increment an atomic counter. +type SerialGenerator interface { + GetSerial() uint32 + RetireSerial(serial uint32) +} diff --git a/vendor/github.com/godbus/dbus/sig.go b/vendor/github.com/godbus/dbus/v5/sig.go similarity index 96% rename from vendor/github.com/godbus/dbus/sig.go rename to vendor/github.com/godbus/dbus/v5/sig.go index f45b53ce1b275..2d326cebc0d84 100644 --- a/vendor/github.com/godbus/dbus/sig.go +++ b/vendor/github.com/godbus/dbus/v5/sig.go @@ -57,12 +57,12 @@ func getSignature(t reflect.Type) string { return "n" case reflect.Uint16: return "q" - case reflect.Int32: + case reflect.Int, reflect.Int32: if t == unixFDType { return "h" } return "i" - case reflect.Uint32: + case reflect.Uint, reflect.Uint32: if t == unixFDIndexType { return "h" } @@ -101,6 +101,8 @@ func getSignature(t reflect.Type) string { panic(InvalidTypeError{t}) } return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}" + case reflect.Interface: + return "v" } panic(InvalidTypeError{t}) } @@ -135,7 +137,7 @@ func ParseSignatureMust(s string) Signature { return sig } -// Empty retruns whether the signature is the empty signature. +// Empty returns whether the signature is the empty signature. func (s Signature) Empty() bool { return s.str == "" } @@ -162,7 +164,7 @@ func (e SignatureError) Error() string { return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason) } -// Try to read a single type from this string. If it was successfull, err is nil +// Try to read a single type from this string. If it was successful, err is nil // and rem is the remaining unparsed part. Otherwise, err is a non-nil // SignatureError and rem is "". depth is the current recursion depth which may // not be greater than 64 and should be given as 0 on the first call. diff --git a/vendor/github.com/godbus/dbus/transport_darwin.go b/vendor/github.com/godbus/dbus/v5/transport_darwin.go similarity index 100% rename from vendor/github.com/godbus/dbus/transport_darwin.go rename to vendor/github.com/godbus/dbus/v5/transport_darwin.go diff --git a/vendor/github.com/godbus/dbus/v5/transport_generic.go b/vendor/github.com/godbus/dbus/v5/transport_generic.go new file mode 100644 index 0000000000000..718a1ff0219a1 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/transport_generic.go @@ -0,0 +1,50 @@ +package dbus + +import ( + "encoding/binary" + "errors" + "io" + "unsafe" +) + +var nativeEndian binary.ByteOrder + +func detectEndianness() binary.ByteOrder { + var x uint32 = 0x01020304 + if *(*byte)(unsafe.Pointer(&x)) == 0x01 { + return binary.BigEndian + } + return binary.LittleEndian +} + +func init() { + nativeEndian = detectEndianness() +} + +type genericTransport struct { + io.ReadWriteCloser +} + +func (t genericTransport) SendNullByte() error { + _, err := t.Write([]byte{0}) + return err +} + +func (t genericTransport) SupportsUnixFDs() bool { + return false +} + +func (t genericTransport) EnableUnixFDs() {} + +func (t genericTransport) ReadMessage() (*Message, error) { + return DecodeMessage(t) +} + +func (t genericTransport) SendMessage(msg *Message) error { + for _, v := range msg.Body { + if _, ok := v.(UnixFD); ok { + return errors.New("dbus: unix fd passing not enabled") + } + } + return msg.EncodeTo(t, nativeEndian) +} diff --git a/vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go b/vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go new file mode 100644 index 0000000000000..697739efafbfb --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go @@ -0,0 +1,39 @@ +//+build !windows + +package dbus + +import ( + "errors" + "io/ioutil" + "net" +) + +func init() { + transports["nonce-tcp"] = newNonceTcpTransport +} + +func newNonceTcpTransport(keys string) (transport, error) { + host := getKey(keys, "host") + port := getKey(keys, "port") + noncefile := getKey(keys, "noncefile") + if host == "" || port == "" || noncefile == "" { + return nil, errors.New("dbus: unsupported address (must set host, port and noncefile)") + } + protocol, err := tcpFamily(keys) + if err != nil { + return nil, err + } + socket, err := net.Dial(protocol, net.JoinHostPort(host, port)) + if err != nil { + return nil, err + } + b, err := ioutil.ReadFile(noncefile) + if err != nil { + return nil, err + } + _, err = socket.Write(b) + if err != nil { + return nil, err + } + return NewConn(socket) +} diff --git a/vendor/github.com/godbus/dbus/transport_tcp.go b/vendor/github.com/godbus/dbus/v5/transport_tcp.go similarity index 97% rename from vendor/github.com/godbus/dbus/transport_tcp.go rename to vendor/github.com/godbus/dbus/v5/transport_tcp.go index dd1c8e59c5b86..f91c9b7d709cb 100644 --- a/vendor/github.com/godbus/dbus/transport_tcp.go +++ b/vendor/github.com/godbus/dbus/v5/transport_tcp.go @@ -1,5 +1,3 @@ -//+build !windows - package dbus import ( diff --git a/vendor/github.com/godbus/dbus/transport_unix.go b/vendor/github.com/godbus/dbus/v5/transport_unix.go similarity index 84% rename from vendor/github.com/godbus/dbus/transport_unix.go rename to vendor/github.com/godbus/dbus/v5/transport_unix.go index a1d00cbc1247f..c7cd02f97fdf2 100644 --- a/vendor/github.com/godbus/dbus/transport_unix.go +++ b/vendor/github.com/godbus/dbus/v5/transport_unix.go @@ -31,6 +31,7 @@ func (o *oobReader) Read(b []byte) (n int, err error) { type unixTransport struct { *net.UnixConn + rdr *oobReader hasUnixFDs bool } @@ -79,10 +80,15 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // To be sure that all bytes of out-of-band data are read, we use a special // reader that uses ReadUnix on the underlying connection instead of Read // and gathers the out-of-band data in a buffer. - rd := &oobReader{conn: t.UnixConn} + if t.rdr == nil { + t.rdr = &oobReader{conn: t.UnixConn} + } else { + t.rdr.oob = nil + } + // read the first 16 bytes (the part of the header that has a constant size), // from which we can figure out the length of the rest of the message - if _, err := io.ReadFull(rd, csheader[:]); err != nil { + if _, err := io.ReadFull(t.rdr, csheader[:]); err != nil { return nil, err } switch csheader[0] { @@ -104,7 +110,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // decode headers and look for unix fds headerdata := make([]byte, hlen+4) copy(headerdata, csheader[12:]) - if _, err := io.ReadFull(t, headerdata[4:]); err != nil { + if _, err := io.ReadFull(t.rdr, headerdata[4:]); err != nil { return nil, err } dec := newDecoder(bytes.NewBuffer(headerdata), order) @@ -122,7 +128,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { all := make([]byte, 16+hlen+blen) copy(all, csheader[:]) copy(all[16:], headerdata[4:]) - if _, err := io.ReadFull(rd, all[16+hlen:]); err != nil { + if _, err := io.ReadFull(t.rdr, all[16+hlen:]); err != nil { return nil, err } if unixfds != 0 { @@ -130,7 +136,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { return nil, errors.New("dbus: got unix fds on unsupported transport") } // read the fds from the OOB data - scms, err := syscall.ParseSocketControlMessage(rd.oob) + scms, err := syscall.ParseSocketControlMessage(t.rdr.oob) if err != nil { return nil, err } @@ -148,11 +154,23 @@ func (t *unixTransport) ReadMessage() (*Message, error) { // substitute the values in the message body (which are indices for the // array receiver via OOB) with the actual values for i, v := range msg.Body { - if j, ok := v.(UnixFDIndex); ok { + switch v.(type) { + case UnixFDIndex: + j := v.(UnixFDIndex) if uint32(j) >= unixfds { return nil, InvalidMessageError("invalid index for unix fd") } msg.Body[i] = UnixFD(fds[j]) + case []UnixFDIndex: + idxArray := v.([]UnixFDIndex) + fdArray := make([]UnixFD, len(idxArray)) + for k, j := range idxArray { + if uint32(j) >= unixfds { + return nil, InvalidMessageError("invalid index for unix fd") + } + fdArray[k] = UnixFD(fds[j]) + } + msg.Body[i] = fdArray } } return msg, nil @@ -175,7 +193,7 @@ func (t *unixTransport) SendMessage(msg *Message) error { msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds))) oob := syscall.UnixRights(fds...) buf := new(bytes.Buffer) - msg.EncodeTo(buf, binary.LittleEndian) + msg.EncodeTo(buf, nativeEndian) n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil) if err != nil { return err @@ -184,8 +202,8 @@ func (t *unixTransport) SendMessage(msg *Message) error { return io.ErrShortWrite } } else { - if err := msg.EncodeTo(t, binary.LittleEndian); err != nil { - return nil + if err := msg.EncodeTo(t, nativeEndian); err != nil { + return err } } return nil diff --git a/vendor/github.com/godbus/dbus/transport_unixcred_dragonfly.go b/vendor/github.com/godbus/dbus/v5/transport_unixcred_dragonfly.go similarity index 100% rename from vendor/github.com/godbus/dbus/transport_unixcred_dragonfly.go rename to vendor/github.com/godbus/dbus/v5/transport_unixcred_dragonfly.go diff --git a/vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go b/vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go new file mode 100644 index 0000000000000..1b5ed2089d0b9 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go @@ -0,0 +1,92 @@ +// The UnixCredentials system call is currently only implemented on Linux +// http://golang.org/src/pkg/syscall/sockcmsg_linux.go +// https://golang.org/s/go1.4-syscall +// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys + +// Local implementation of the UnixCredentials system call for FreeBSD + +package dbus + +/* +const int sizeofPtr = sizeof(void*); +#define _WANT_UCRED +#include +#include +*/ +import "C" + +import ( + "io" + "os" + "syscall" + "unsafe" +) + +// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go +// https://golang.org/src/syscall/ztypes_freebsd_amd64.go +type Ucred struct { + Pid int32 + Uid uint32 + Gid uint32 +} + +// http://golang.org/src/pkg/syscall/types_linux.go +// https://golang.org/src/syscall/types_freebsd.go +// https://github.com/freebsd/freebsd/blob/master/sys/sys/ucred.h +const ( + SizeofUcred = C.sizeof_struct_ucred +) + +// http://golang.org/src/pkg/syscall/sockcmsg_unix.go +func cmsgAlignOf(salen int) int { + salign := C.sizeofPtr + + return (salen + salign - 1) & ^(salign - 1) +} + +// http://golang.org/src/pkg/syscall/sockcmsg_unix.go +func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer { + return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr))) +} + +// http://golang.org/src/pkg/syscall/sockcmsg_linux.go +// UnixCredentials encodes credentials into a socket control message +// for sending to another process. This can be used for +// authentication. +func UnixCredentials(ucred *Ucred) []byte { + b := make([]byte, syscall.CmsgSpace(SizeofUcred)) + h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0])) + h.Level = syscall.SOL_SOCKET + h.Type = syscall.SCM_CREDS + h.SetLen(syscall.CmsgLen(SizeofUcred)) + *((*Ucred)(cmsgData(h))) = *ucred + return b +} + +// http://golang.org/src/pkg/syscall/sockcmsg_linux.go +// ParseUnixCredentials decodes a socket control message that contains +// credentials in a Ucred structure. To receive such a message, the +// SO_PASSCRED option must be enabled on the socket. +func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) { + if m.Header.Level != syscall.SOL_SOCKET { + return nil, syscall.EINVAL + } + if m.Header.Type != syscall.SCM_CREDS { + return nil, syscall.EINVAL + } + ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0])) + return &ucred, nil +} + +func (t *unixTransport) SendNullByte() error { + ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())} + b := UnixCredentials(ucred) + _, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil) + if err != nil { + return err + } + if oobn != len(b) { + return io.ErrShortWrite + } + return nil +} diff --git a/vendor/github.com/godbus/dbus/transport_unixcred_linux.go b/vendor/github.com/godbus/dbus/v5/transport_unixcred_linux.go similarity index 100% rename from vendor/github.com/godbus/dbus/transport_unixcred_linux.go rename to vendor/github.com/godbus/dbus/v5/transport_unixcred_linux.go diff --git a/vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go b/vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go new file mode 100644 index 0000000000000..af7bafdf953b8 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go @@ -0,0 +1,14 @@ +package dbus + +import "io" + +func (t *unixTransport) SendNullByte() error { + n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil) + if err != nil { + return err + } + if n != 1 { + return io.ErrShortWrite + } + return nil +} diff --git a/vendor/github.com/godbus/dbus/variant.go b/vendor/github.com/godbus/dbus/v5/variant.go similarity index 84% rename from vendor/github.com/godbus/dbus/variant.go rename to vendor/github.com/godbus/dbus/v5/variant.go index b7b13ae90ddf1..f1e81f3ede671 100644 --- a/vendor/github.com/godbus/dbus/variant.go +++ b/vendor/github.com/godbus/dbus/v5/variant.go @@ -17,11 +17,16 @@ type Variant struct { // MakeVariant converts the given value to a Variant. It panics if v cannot be // represented as a D-Bus type. func MakeVariant(v interface{}) Variant { - return Variant{SignatureOf(v), v} + return MakeVariantWithSignature(v, SignatureOf(v)) +} + +// MakeVariantWithSignature converts the given value to a Variant. +func MakeVariantWithSignature(v interface{}, s Signature) Variant { + return Variant{s, v} } // ParseVariant parses the given string as a variant as described at -// https://developer.gnome.org/glib/unstable/gvariant-text.html. If sig is not +// https://developer.gnome.org/glib/stable/gvariant-text.html. If sig is not // empty, it is taken to be the expected signature for the variant. func ParseVariant(s string, sig Signature) (Variant, error) { tokens := varLex(s) @@ -124,7 +129,7 @@ func (v Variant) Signature() Signature { } // String returns the string representation of the underlying value of v as -// described at https://developer.gnome.org/glib/unstable/gvariant-text.html. +// described at https://developer.gnome.org/glib/stable/gvariant-text.html. func (v Variant) String() string { s, unamb := v.format() if !unamb { @@ -137,3 +142,9 @@ func (v Variant) String() string { func (v Variant) Value() interface{} { return v.value } + +// Store converts the variant into a native go type using the same +// mechanism as the "Store" function. +func (v Variant) Store(value interface{}) error { + return storeInterfaces(v.value, value) +} diff --git a/vendor/github.com/godbus/dbus/variant_lexer.go b/vendor/github.com/godbus/dbus/v5/variant_lexer.go similarity index 95% rename from vendor/github.com/godbus/dbus/variant_lexer.go rename to vendor/github.com/godbus/dbus/v5/variant_lexer.go index 332007d6f1232..bf1398c8f0572 100644 --- a/vendor/github.com/godbus/dbus/variant_lexer.go +++ b/vendor/github.com/godbus/dbus/v5/variant_lexer.go @@ -51,7 +51,7 @@ func varLex(s string) []varToken { } func (l *varLexer) accept(valid string) bool { - if strings.IndexRune(valid, l.next()) >= 0 { + if strings.ContainsRune(valid, l.next()) { return true } l.backup() @@ -214,17 +214,17 @@ func varLexNumber(l *varLexer) lexState { digits = "01234567" } } - for strings.IndexRune(digits, l.next()) >= 0 { + for strings.ContainsRune(digits, l.next()) { } l.backup() if l.accept(".") { - for strings.IndexRune(digits, l.next()) >= 0 { + for strings.ContainsRune(digits, l.next()) { } l.backup() } if l.accept("eE") { l.accept("+-") - for strings.IndexRune("0123456789", l.next()) >= 0 { + for strings.ContainsRune("0123456789", l.next()) { } l.backup() } diff --git a/vendor/github.com/godbus/dbus/variant_parser.go b/vendor/github.com/godbus/dbus/v5/variant_parser.go similarity index 100% rename from vendor/github.com/godbus/dbus/variant_parser.go rename to vendor/github.com/godbus/dbus/v5/variant_parser.go diff --git a/vendor/github.com/gofrs/flock/LICENSE b/vendor/github.com/gofrs/flock/LICENSE new file mode 100644 index 0000000000000..8b8ff36fe4269 --- /dev/null +++ b/vendor/github.com/gofrs/flock/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2015-2020, Tim Heckman +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of gofrs nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gofrs/flock/README.md b/vendor/github.com/gofrs/flock/README.md new file mode 100644 index 0000000000000..71ce63692e8e3 --- /dev/null +++ b/vendor/github.com/gofrs/flock/README.md @@ -0,0 +1,41 @@ +# flock +[![TravisCI Build Status](https://img.shields.io/travis/gofrs/flock/master.svg?style=flat)](https://travis-ci.org/gofrs/flock) +[![GoDoc](https://img.shields.io/badge/godoc-flock-blue.svg?style=flat)](https://godoc.org/github.com/gofrs/flock) +[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/master/LICENSE) +[![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/flock)](https://goreportcard.com/report/github.com/gofrs/flock) + +`flock` implements a thread-safe sync.Locker interface for file locking. It also +includes a non-blocking TryLock() function to allow locking without blocking execution. + +## License +`flock` is released under the BSD 3-Clause License. See the `LICENSE` file for more details. + +## Go Compatibility +This package makes use of the `context` package that was introduced in Go 1.7. As such, this +package has an implicit dependency on Go 1.7+. + +## Installation +``` +go get -u github.com/gofrs/flock +``` + +## Usage +```Go +import "github.com/gofrs/flock" + +fileLock := flock.New("/var/lock/go-lock.lock") + +locked, err := fileLock.TryLock() + +if err != nil { + // handle locking error +} + +if locked { + // do work + fileLock.Unlock() +} +``` + +For more detailed usage information take a look at the package API docs on +[GoDoc](https://godoc.org/github.com/gofrs/flock). diff --git a/vendor/github.com/gofrs/flock/flock.go b/vendor/github.com/gofrs/flock/flock.go new file mode 100644 index 0000000000000..2fd16033763dc --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock.go @@ -0,0 +1,135 @@ +// Copyright 2015 Tim Heckman. All rights reserved. +// Use of this source code is governed by the BSD 3-Clause +// license that can be found in the LICENSE file. + +// Package flock implements a thread-safe interface for file locking. +// It also includes a non-blocking TryLock() function to allow locking +// without blocking execution. +// +// Package flock is released under the BSD 3-Clause License. See the LICENSE file +// for more details. +// +// While using this library, remember that the locking behaviors are not +// guaranteed to be the same on each platform. For example, some UNIX-like +// operating systems will transparently convert a shared lock to an exclusive +// lock. If you Unlock() the flock from a location where you believe that you +// have the shared lock, you may accidentally drop the exclusive lock. +package flock + +import ( + "context" + "os" + "sync" + "time" +) + +// Flock is the struct type to handle file locking. All fields are unexported, +// with access to some of the fields provided by getter methods (Path() and Locked()). +type Flock struct { + path string + m sync.RWMutex + fh *os.File + l bool + r bool +} + +// New returns a new instance of *Flock. The only parameter +// it takes is the path to the desired lockfile. +func New(path string) *Flock { + return &Flock{path: path} +} + +// NewFlock returns a new instance of *Flock. The only parameter +// it takes is the path to the desired lockfile. +// +// Deprecated: Use New instead. +func NewFlock(path string) *Flock { + return New(path) +} + +// Close is equivalent to calling Unlock. +// +// This will release the lock and close the underlying file descriptor. +// It will not remove the file from disk, that's up to your application. +func (f *Flock) Close() error { + return f.Unlock() +} + +// Path returns the path as provided in NewFlock(). +func (f *Flock) Path() string { + return f.path +} + +// Locked returns the lock state (locked: true, unlocked: false). +// +// Warning: by the time you use the returned value, the state may have changed. +func (f *Flock) Locked() bool { + f.m.RLock() + defer f.m.RUnlock() + return f.l +} + +// RLocked returns the read lock state (locked: true, unlocked: false). +// +// Warning: by the time you use the returned value, the state may have changed. +func (f *Flock) RLocked() bool { + f.m.RLock() + defer f.m.RUnlock() + return f.r +} + +func (f *Flock) String() string { + return f.path +} + +// TryLockContext repeatedly tries to take an exclusive lock until one of the +// conditions is met: TryLock succeeds, TryLock fails with error, or Context +// Done channel is closed. +func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) { + return tryCtx(ctx, f.TryLock, retryDelay) +} + +// TryRLockContext repeatedly tries to take a shared lock until one of the +// conditions is met: TryRLock succeeds, TryRLock fails with error, or Context +// Done channel is closed. +func (f *Flock) TryRLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) { + return tryCtx(ctx, f.TryRLock, retryDelay) +} + +func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Duration) (bool, error) { + if ctx.Err() != nil { + return false, ctx.Err() + } + for { + if ok, err := fn(); ok || err != nil { + return ok, err + } + select { + case <-ctx.Done(): + return false, ctx.Err() + case <-time.After(retryDelay): + // try again + } + } +} + +func (f *Flock) setFh() error { + // open a new os.File instance + // create it if it doesn't exist, and open the file read-only. + fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDONLY, os.FileMode(0600)) + if err != nil { + return err + } + + // set the filehandle on the struct + f.fh = fh + return nil +} + +// ensure the file handle is closed if no lock is held +func (f *Flock) ensureFhState() { + if !f.l && !f.r && f.fh != nil { + f.fh.Close() + f.fh = nil + } +} diff --git a/vendor/github.com/gofrs/flock/flock_unix.go b/vendor/github.com/gofrs/flock/flock_unix.go new file mode 100644 index 0000000000000..366a60ca6d3b3 --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_unix.go @@ -0,0 +1,197 @@ +// Copyright 2015 Tim Heckman. All rights reserved. +// Use of this source code is governed by the BSD 3-Clause +// license that can be found in the LICENSE file. + +// +build !windows + +package flock + +import ( + "os" + "syscall" +) + +// Lock is a blocking call to try and take an exclusive file lock. It will wait +// until it is able to obtain the exclusive file lock. It's recommended that +// TryLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already exclusive-locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +// +// If the *Flock has a shared lock (RLock), this may transparently replace the +// shared lock with an exclusive lock on some UNIX-like operating systems. Be +// careful when using exclusive locks in conjunction with shared locks +// (RLock()), because calling Unlock() may accidentally release the exclusive +// lock that was once a shared lock. +func (f *Flock) Lock() error { + return f.lock(&f.l, syscall.LOCK_EX) +} + +// RLock is a blocking call to try and take a shared file lock. It will wait +// until it is able to obtain the shared file lock. It's recommended that +// TryRLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already shared-locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +func (f *Flock) RLock() error { + return f.lock(&f.r, syscall.LOCK_SH) +} + +func (f *Flock) lock(locked *bool, flag int) error { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return nil + } + + if f.fh == nil { + if err := f.setFh(); err != nil { + return err + } + defer f.ensureFhState() + } + + if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil { + shouldRetry, reopenErr := f.reopenFDOnError(err) + if reopenErr != nil { + return reopenErr + } + + if !shouldRetry { + return err + } + + if err = syscall.Flock(int(f.fh.Fd()), flag); err != nil { + return err + } + } + + *locked = true + return nil +} + +// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so +// while it is running the Locked() and RLocked() functions will be blocked. +// +// This function short-circuits if we are unlocked already. If not, it calls +// syscall.LOCK_UN on the file and closes the file descriptor. It does not +// remove the file from disk. It's up to your application to do. +// +// Please note, if your shared lock became an exclusive lock this may +// unintentionally drop the exclusive lock if called by the consumer that +// believes they have a shared lock. Please see Lock() for more details. +func (f *Flock) Unlock() error { + f.m.Lock() + defer f.m.Unlock() + + // if we aren't locked or if the lockfile instance is nil + // just return a nil error because we are unlocked + if (!f.l && !f.r) || f.fh == nil { + return nil + } + + // mark the file as unlocked + if err := syscall.Flock(int(f.fh.Fd()), syscall.LOCK_UN); err != nil { + return err + } + + f.fh.Close() + + f.l = false + f.r = false + f.fh = nil + + return nil +} + +// TryLock is the preferred function for taking an exclusive file lock. This +// function takes an RW-mutex lock before it tries to lock the file, so there is +// the possibility that this function may block for a short time if another +// goroutine is trying to take any action. +// +// The actual file lock is non-blocking. If we are unable to get the exclusive +// file lock, the function will return false instead of waiting for the lock. If +// we get the lock, we also set the *Flock instance as being exclusive-locked. +func (f *Flock) TryLock() (bool, error) { + return f.try(&f.l, syscall.LOCK_EX) +} + +// TryRLock is the preferred function for taking a shared file lock. This +// function takes an RW-mutex lock before it tries to lock the file, so there is +// the possibility that this function may block for a short time if another +// goroutine is trying to take any action. +// +// The actual file lock is non-blocking. If we are unable to get the shared file +// lock, the function will return false instead of waiting for the lock. If we +// get the lock, we also set the *Flock instance as being share-locked. +func (f *Flock) TryRLock() (bool, error) { + return f.try(&f.r, syscall.LOCK_SH) +} + +func (f *Flock) try(locked *bool, flag int) (bool, error) { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return true, nil + } + + if f.fh == nil { + if err := f.setFh(); err != nil { + return false, err + } + defer f.ensureFhState() + } + + var retried bool +retry: + err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB) + + switch err { + case syscall.EWOULDBLOCK: + return false, nil + case nil: + *locked = true + return true, nil + } + if !retried { + if shouldRetry, reopenErr := f.reopenFDOnError(err); reopenErr != nil { + return false, reopenErr + } else if shouldRetry { + retried = true + goto retry + } + } + + return false, err +} + +// reopenFDOnError determines whether we should reopen the file handle +// in readwrite mode and try again. This comes from util-linux/sys-utils/flock.c: +// Since Linux 3.4 (commit 55725513) +// Probably NFSv4 where flock() is emulated by fcntl(). +func (f *Flock) reopenFDOnError(err error) (bool, error) { + if err != syscall.EIO && err != syscall.EBADF { + return false, nil + } + if st, err := f.fh.Stat(); err == nil { + // if the file is able to be read and written + if st.Mode()&0600 == 0600 { + f.fh.Close() + f.fh = nil + + // reopen in read-write mode and set the filehandle + fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDWR, os.FileMode(0600)) + if err != nil { + return false, err + } + f.fh = fh + return true, nil + } + } + + return false, nil +} diff --git a/vendor/github.com/gofrs/flock/flock_winapi.go b/vendor/github.com/gofrs/flock/flock_winapi.go new file mode 100644 index 0000000000000..fe405a255ae58 --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_winapi.go @@ -0,0 +1,76 @@ +// Copyright 2015 Tim Heckman. All rights reserved. +// Use of this source code is governed by the BSD 3-Clause +// license that can be found in the LICENSE file. + +// +build windows + +package flock + +import ( + "syscall" + "unsafe" +) + +var ( + kernel32, _ = syscall.LoadLibrary("kernel32.dll") + procLockFileEx, _ = syscall.GetProcAddress(kernel32, "LockFileEx") + procUnlockFileEx, _ = syscall.GetProcAddress(kernel32, "UnlockFileEx") +) + +const ( + winLockfileFailImmediately = 0x00000001 + winLockfileExclusiveLock = 0x00000002 + winLockfileSharedLock = 0x00000000 +) + +// Use of 0x00000000 for the shared lock is a guess based on some the MS Windows +// `LockFileEX` docs, which document the `LOCKFILE_EXCLUSIVE_LOCK` flag as: +// +// > The function requests an exclusive lock. Otherwise, it requests a shared +// > lock. +// +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx + +func lockFileEx(handle syscall.Handle, flags uint32, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) { + r1, _, errNo := syscall.Syscall6( + uintptr(procLockFileEx), + 6, + uintptr(handle), + uintptr(flags), + uintptr(reserved), + uintptr(numberOfBytesToLockLow), + uintptr(numberOfBytesToLockHigh), + uintptr(unsafe.Pointer(offset))) + + if r1 != 1 { + if errNo == 0 { + return false, syscall.EINVAL + } + + return false, errNo + } + + return true, 0 +} + +func unlockFileEx(handle syscall.Handle, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) { + r1, _, errNo := syscall.Syscall6( + uintptr(procUnlockFileEx), + 5, + uintptr(handle), + uintptr(reserved), + uintptr(numberOfBytesToLockLow), + uintptr(numberOfBytesToLockHigh), + uintptr(unsafe.Pointer(offset)), + 0) + + if r1 != 1 { + if errNo == 0 { + return false, syscall.EINVAL + } + + return false, errNo + } + + return true, 0 +} diff --git a/vendor/github.com/gofrs/flock/flock_windows.go b/vendor/github.com/gofrs/flock/flock_windows.go new file mode 100644 index 0000000000000..ddb534ccef097 --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_windows.go @@ -0,0 +1,142 @@ +// Copyright 2015 Tim Heckman. All rights reserved. +// Use of this source code is governed by the BSD 3-Clause +// license that can be found in the LICENSE file. + +package flock + +import ( + "syscall" +) + +// ErrorLockViolation is the error code returned from the Windows syscall when a +// lock would block and you ask to fail immediately. +const ErrorLockViolation syscall.Errno = 0x21 // 33 + +// Lock is a blocking call to try and take an exclusive file lock. It will wait +// until it is able to obtain the exclusive file lock. It's recommended that +// TryLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +func (f *Flock) Lock() error { + return f.lock(&f.l, winLockfileExclusiveLock) +} + +// RLock is a blocking call to try and take a shared file lock. It will wait +// until it is able to obtain the shared file lock. It's recommended that +// TryRLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +func (f *Flock) RLock() error { + return f.lock(&f.r, winLockfileSharedLock) +} + +func (f *Flock) lock(locked *bool, flag uint32) error { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return nil + } + + if f.fh == nil { + if err := f.setFh(); err != nil { + return err + } + defer f.ensureFhState() + } + + if _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}); errNo > 0 { + return errNo + } + + *locked = true + return nil +} + +// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so +// while it is running the Locked() and RLocked() functions will be blocked. +// +// This function short-circuits if we are unlocked already. If not, it calls +// UnlockFileEx() on the file and closes the file descriptor. It does not remove +// the file from disk. It's up to your application to do. +func (f *Flock) Unlock() error { + f.m.Lock() + defer f.m.Unlock() + + // if we aren't locked or if the lockfile instance is nil + // just return a nil error because we are unlocked + if (!f.l && !f.r) || f.fh == nil { + return nil + } + + // mark the file as unlocked + if _, errNo := unlockFileEx(syscall.Handle(f.fh.Fd()), 0, 1, 0, &syscall.Overlapped{}); errNo > 0 { + return errNo + } + + f.fh.Close() + + f.l = false + f.r = false + f.fh = nil + + return nil +} + +// TryLock is the preferred function for taking an exclusive file lock. This +// function does take a RW-mutex lock before it tries to lock the file, so there +// is the possibility that this function may block for a short time if another +// goroutine is trying to take any action. +// +// The actual file lock is non-blocking. If we are unable to get the exclusive +// file lock, the function will return false instead of waiting for the lock. If +// we get the lock, we also set the *Flock instance as being exclusive-locked. +func (f *Flock) TryLock() (bool, error) { + return f.try(&f.l, winLockfileExclusiveLock) +} + +// TryRLock is the preferred function for taking a shared file lock. This +// function does take a RW-mutex lock before it tries to lock the file, so there +// is the possibility that this function may block for a short time if another +// goroutine is trying to take any action. +// +// The actual file lock is non-blocking. If we are unable to get the shared file +// lock, the function will return false instead of waiting for the lock. If we +// get the lock, we also set the *Flock instance as being shared-locked. +func (f *Flock) TryRLock() (bool, error) { + return f.try(&f.r, winLockfileSharedLock) +} + +func (f *Flock) try(locked *bool, flag uint32) (bool, error) { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return true, nil + } + + if f.fh == nil { + if err := f.setFh(); err != nil { + return false, err + } + defer f.ensureFhState() + } + + _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag|winLockfileFailImmediately, 0, 1, 0, &syscall.Overlapped{}) + + if errNo > 0 { + if errNo == ErrorLockViolation || errNo == syscall.ERROR_IO_PENDING { + return false, nil + } + + return false, errNo + } + + *locked = true + + return true, nil +} diff --git a/vendor/github.com/gogo/googleapis/LICENSE b/vendor/github.com/gogo/googleapis/LICENSE new file mode 100644 index 0000000000000..d6f85b181780d --- /dev/null +++ b/vendor/github.com/gogo/googleapis/LICENSE @@ -0,0 +1,203 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015, Google Inc + Copyright 2018, GoGo Authors + + 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. + diff --git a/vendor/github.com/gogo/googleapis/go.mod b/vendor/github.com/gogo/googleapis/go.mod new file mode 100644 index 0000000000000..89be4cf8789e6 --- /dev/null +++ b/vendor/github.com/gogo/googleapis/go.mod @@ -0,0 +1,5 @@ +module github.com/gogo/googleapis + +go 1.12 + +require github.com/gogo/protobuf v1.3.1 diff --git a/vendor/github.com/gogo/googleapis/google/rpc/code.pb.go b/vendor/github.com/gogo/googleapis/google/rpc/code.pb.go index 2a77c1bfae87a..1f4d4d432217d 100644 --- a/vendor/github.com/gogo/googleapis/google/rpc/code.pb.go +++ b/vendor/github.com/gogo/googleapis/google/rpc/code.pb.go @@ -3,17 +3,24 @@ package rpc -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -import strconv "strconv" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + math "math" + strconv "strconv" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + // The canonical error codes for Google APIs. // // @@ -181,6 +188,7 @@ var Code_name = map[int32]string{ 14: "UNAVAILABLE", 15: "DATA_LOSS", } + var Code_value = map[string]int32{ "OK": 0, "CANCELLED": 1, @@ -201,22 +209,17 @@ var Code_value = map[string]int32{ "DATA_LOSS": 15, } -func (Code) EnumDescriptor() ([]byte, []int) { return fileDescriptorCode, []int{0} } +func (Code) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_fe593a732623ccf0, []int{0} +} func init() { proto.RegisterEnum("google.rpc.Code", Code_name, Code_value) } -func (x Code) String() string { - s, ok := Code_name[int32(x)] - if ok { - return s - } - return strconv.Itoa(int(x)) -} -func init() { proto.RegisterFile("google/rpc/code.proto", fileDescriptorCode) } +func init() { proto.RegisterFile("google/rpc/code.proto", fileDescriptor_fe593a732623ccf0) } -var fileDescriptorCode = []byte{ +var fileDescriptor_fe593a732623ccf0 = []byte{ // 393 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x91, 0x3d, 0x6e, 0x13, 0x41, 0x14, 0xc7, 0x3d, 0x76, 0x70, 0xe2, 0xf1, 0xd7, 0xcb, 0x84, 0x40, 0x37, 0x07, 0xa0, 0x70, 0x0a, @@ -244,3 +247,11 @@ var fileDescriptorCode = []byte{ 0xf3, 0xb5, 0x3f, 0x08, 0x65, 0xf1, 0x61, 0xf8, 0xb7, 0xaa, 0xd7, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x03, 0xd4, 0x27, 0xff, 0xc3, 0x01, 0x00, 0x00, } + +func (x Code) String() string { + s, ok := Code_name[int32(x)] + if ok { + return s + } + return strconv.Itoa(int(x)) +} diff --git a/vendor/github.com/gogo/googleapis/google/rpc/code.proto b/vendor/github.com/gogo/googleapis/google/rpc/code.proto index d832de11e9810..0540a4f6c5832 100644 --- a/vendor/github.com/gogo/googleapis/google/rpc/code.proto +++ b/vendor/github.com/gogo/googleapis/google/rpc/code.proto @@ -22,7 +22,6 @@ option java_outer_classname = "CodeProto"; option java_package = "com.google.rpc"; option objc_class_prefix = "RPC"; - // The canonical error codes for Google APIs. // // diff --git a/vendor/github.com/gogo/googleapis/google/rpc/error_details.pb.go b/vendor/github.com/gogo/googleapis/google/rpc/error_details.pb.go index 5677581f1e4d5..07d251d4b1536 100644 --- a/vendor/github.com/gogo/googleapis/google/rpc/error_details.pb.go +++ b/vendor/github.com/gogo/googleapis/google/rpc/error_details.pb.go @@ -3,21 +3,29 @@ package rpc -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf1 "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + types "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + // Describes when the clients can retry a failed request. Clients could ignore // the recommendation here or retry when this information is missing from error // responses. @@ -33,14 +41,45 @@ var _ = math.Inf // reached. type RetryInfo struct { // Clients should wait at least this long between retrying the same request. - RetryDelay *google_protobuf1.Duration `protobuf:"bytes,1,opt,name=retry_delay,json=retryDelay" json:"retry_delay,omitempty"` + RetryDelay *types.Duration `protobuf:"bytes,1,opt,name=retry_delay,json=retryDelay,proto3" json:"retry_delay,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RetryInfo) Reset() { *m = RetryInfo{} } +func (*RetryInfo) ProtoMessage() {} +func (*RetryInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_851816e4d6b6361a, []int{0} +} +func (m *RetryInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RetryInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RetryInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RetryInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_RetryInfo.Merge(m, src) +} +func (m *RetryInfo) XXX_Size() int { + return m.Size() +} +func (m *RetryInfo) XXX_DiscardUnknown() { + xxx_messageInfo_RetryInfo.DiscardUnknown(m) } -func (m *RetryInfo) Reset() { *m = RetryInfo{} } -func (*RetryInfo) ProtoMessage() {} -func (*RetryInfo) Descriptor() ([]byte, []int) { return fileDescriptorErrorDetails, []int{0} } +var xxx_messageInfo_RetryInfo proto.InternalMessageInfo -func (m *RetryInfo) GetRetryDelay() *google_protobuf1.Duration { +func (m *RetryInfo) GetRetryDelay() *types.Duration { if m != nil { return m.RetryDelay } @@ -54,14 +93,45 @@ func (*RetryInfo) XXX_MessageName() string { // Describes additional debugging info. type DebugInfo struct { // The stack trace entries indicating where the error occurred. - StackEntries []string `protobuf:"bytes,1,rep,name=stack_entries,json=stackEntries" json:"stack_entries,omitempty"` + StackEntries []string `protobuf:"bytes,1,rep,name=stack_entries,json=stackEntries,proto3" json:"stack_entries,omitempty"` // Additional debugging information provided by the server. - Detail string `protobuf:"bytes,2,opt,name=detail,proto3" json:"detail,omitempty"` + Detail string `protobuf:"bytes,2,opt,name=detail,proto3" json:"detail,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DebugInfo) Reset() { *m = DebugInfo{} } +func (*DebugInfo) ProtoMessage() {} +func (*DebugInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_851816e4d6b6361a, []int{1} +} +func (m *DebugInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DebugInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DebugInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DebugInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_DebugInfo.Merge(m, src) +} +func (m *DebugInfo) XXX_Size() int { + return m.Size() +} +func (m *DebugInfo) XXX_DiscardUnknown() { + xxx_messageInfo_DebugInfo.DiscardUnknown(m) } -func (m *DebugInfo) Reset() { *m = DebugInfo{} } -func (*DebugInfo) ProtoMessage() {} -func (*DebugInfo) Descriptor() ([]byte, []int) { return fileDescriptorErrorDetails, []int{1} } +var xxx_messageInfo_DebugInfo proto.InternalMessageInfo func (m *DebugInfo) GetStackEntries() []string { if m != nil { @@ -94,12 +164,43 @@ func (*DebugInfo) XXX_MessageName() string { // quota failure. type QuotaFailure struct { // Describes all quota violations. - Violations []*QuotaFailure_Violation `protobuf:"bytes,1,rep,name=violations" json:"violations,omitempty"` + Violations []*QuotaFailure_Violation `protobuf:"bytes,1,rep,name=violations,proto3" json:"violations,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *QuotaFailure) Reset() { *m = QuotaFailure{} } +func (*QuotaFailure) ProtoMessage() {} +func (*QuotaFailure) Descriptor() ([]byte, []int) { + return fileDescriptor_851816e4d6b6361a, []int{2} +} +func (m *QuotaFailure) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuotaFailure) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuotaFailure.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QuotaFailure) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuotaFailure.Merge(m, src) +} +func (m *QuotaFailure) XXX_Size() int { + return m.Size() +} +func (m *QuotaFailure) XXX_DiscardUnknown() { + xxx_messageInfo_QuotaFailure.DiscardUnknown(m) } -func (m *QuotaFailure) Reset() { *m = QuotaFailure{} } -func (*QuotaFailure) ProtoMessage() {} -func (*QuotaFailure) Descriptor() ([]byte, []int) { return fileDescriptorErrorDetails, []int{2} } +var xxx_messageInfo_QuotaFailure proto.InternalMessageInfo func (m *QuotaFailure) GetViolations() []*QuotaFailure_Violation { if m != nil { @@ -126,14 +227,43 @@ type QuotaFailure_Violation struct { // // For example: "Service disabled" or "Daily Limit for read operations // exceeded". - Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *QuotaFailure_Violation) Reset() { *m = QuotaFailure_Violation{} } func (*QuotaFailure_Violation) ProtoMessage() {} func (*QuotaFailure_Violation) Descriptor() ([]byte, []int) { - return fileDescriptorErrorDetails, []int{2, 0} + return fileDescriptor_851816e4d6b6361a, []int{2, 0} +} +func (m *QuotaFailure_Violation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuotaFailure_Violation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuotaFailure_Violation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QuotaFailure_Violation) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuotaFailure_Violation.Merge(m, src) } +func (m *QuotaFailure_Violation) XXX_Size() int { + return m.Size() +} +func (m *QuotaFailure_Violation) XXX_DiscardUnknown() { + xxx_messageInfo_QuotaFailure_Violation.DiscardUnknown(m) +} + +var xxx_messageInfo_QuotaFailure_Violation proto.InternalMessageInfo func (m *QuotaFailure_Violation) GetSubject() string { if m != nil { @@ -160,12 +290,43 @@ func (*QuotaFailure_Violation) XXX_MessageName() string { // PreconditionFailure message. type PreconditionFailure struct { // Describes all precondition violations. - Violations []*PreconditionFailure_Violation `protobuf:"bytes,1,rep,name=violations" json:"violations,omitempty"` + Violations []*PreconditionFailure_Violation `protobuf:"bytes,1,rep,name=violations,proto3" json:"violations,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PreconditionFailure) Reset() { *m = PreconditionFailure{} } +func (*PreconditionFailure) ProtoMessage() {} +func (*PreconditionFailure) Descriptor() ([]byte, []int) { + return fileDescriptor_851816e4d6b6361a, []int{3} +} +func (m *PreconditionFailure) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PreconditionFailure) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PreconditionFailure.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PreconditionFailure) XXX_Merge(src proto.Message) { + xxx_messageInfo_PreconditionFailure.Merge(m, src) +} +func (m *PreconditionFailure) XXX_Size() int { + return m.Size() +} +func (m *PreconditionFailure) XXX_DiscardUnknown() { + xxx_messageInfo_PreconditionFailure.DiscardUnknown(m) } -func (m *PreconditionFailure) Reset() { *m = PreconditionFailure{} } -func (*PreconditionFailure) ProtoMessage() {} -func (*PreconditionFailure) Descriptor() ([]byte, []int) { return fileDescriptorErrorDetails, []int{3} } +var xxx_messageInfo_PreconditionFailure proto.InternalMessageInfo func (m *PreconditionFailure) GetViolations() []*PreconditionFailure_Violation { if m != nil { @@ -192,15 +353,44 @@ type PreconditionFailure_Violation struct { // description to understand how to fix the failure. // // For example: "Terms of service not accepted". - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *PreconditionFailure_Violation) Reset() { *m = PreconditionFailure_Violation{} } func (*PreconditionFailure_Violation) ProtoMessage() {} func (*PreconditionFailure_Violation) Descriptor() ([]byte, []int) { - return fileDescriptorErrorDetails, []int{3, 0} + return fileDescriptor_851816e4d6b6361a, []int{3, 0} +} +func (m *PreconditionFailure_Violation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PreconditionFailure_Violation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PreconditionFailure_Violation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PreconditionFailure_Violation) XXX_Merge(src proto.Message) { + xxx_messageInfo_PreconditionFailure_Violation.Merge(m, src) +} +func (m *PreconditionFailure_Violation) XXX_Size() int { + return m.Size() +} +func (m *PreconditionFailure_Violation) XXX_DiscardUnknown() { + xxx_messageInfo_PreconditionFailure_Violation.DiscardUnknown(m) } +var xxx_messageInfo_PreconditionFailure_Violation proto.InternalMessageInfo + func (m *PreconditionFailure_Violation) GetType() string { if m != nil { return m.Type @@ -230,12 +420,43 @@ func (*PreconditionFailure_Violation) XXX_MessageName() string { // syntactic aspects of the request. type BadRequest struct { // Describes all violations in a client request. - FieldViolations []*BadRequest_FieldViolation `protobuf:"bytes,1,rep,name=field_violations,json=fieldViolations" json:"field_violations,omitempty"` + FieldViolations []*BadRequest_FieldViolation `protobuf:"bytes,1,rep,name=field_violations,json=fieldViolations,proto3" json:"field_violations,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BadRequest) Reset() { *m = BadRequest{} } +func (*BadRequest) ProtoMessage() {} +func (*BadRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_851816e4d6b6361a, []int{4} +} +func (m *BadRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BadRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BadRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BadRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_BadRequest.Merge(m, src) +} +func (m *BadRequest) XXX_Size() int { + return m.Size() +} +func (m *BadRequest) XXX_DiscardUnknown() { + xxx_messageInfo_BadRequest.DiscardUnknown(m) } -func (m *BadRequest) Reset() { *m = BadRequest{} } -func (*BadRequest) ProtoMessage() {} -func (*BadRequest) Descriptor() ([]byte, []int) { return fileDescriptorErrorDetails, []int{4} } +var xxx_messageInfo_BadRequest proto.InternalMessageInfo func (m *BadRequest) GetFieldViolations() []*BadRequest_FieldViolation { if m != nil { @@ -255,14 +476,43 @@ type BadRequest_FieldViolation struct { // field. E.g., "field_violations.field" would identify this field. Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"` // A description of why the request element is bad. - Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *BadRequest_FieldViolation) Reset() { *m = BadRequest_FieldViolation{} } func (*BadRequest_FieldViolation) ProtoMessage() {} func (*BadRequest_FieldViolation) Descriptor() ([]byte, []int) { - return fileDescriptorErrorDetails, []int{4, 0} + return fileDescriptor_851816e4d6b6361a, []int{4, 0} } +func (m *BadRequest_FieldViolation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BadRequest_FieldViolation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BadRequest_FieldViolation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BadRequest_FieldViolation) XXX_Merge(src proto.Message) { + xxx_messageInfo_BadRequest_FieldViolation.Merge(m, src) +} +func (m *BadRequest_FieldViolation) XXX_Size() int { + return m.Size() +} +func (m *BadRequest_FieldViolation) XXX_DiscardUnknown() { + xxx_messageInfo_BadRequest_FieldViolation.DiscardUnknown(m) +} + +var xxx_messageInfo_BadRequest_FieldViolation proto.InternalMessageInfo func (m *BadRequest_FieldViolation) GetField() string { if m != nil { @@ -290,12 +540,43 @@ type RequestInfo struct { RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` // Any data that was used to serve this request. For example, an encrypted // stack trace that can be sent back to the service provider for debugging. - ServingData string `protobuf:"bytes,2,opt,name=serving_data,json=servingData,proto3" json:"serving_data,omitempty"` + ServingData string `protobuf:"bytes,2,opt,name=serving_data,json=servingData,proto3" json:"serving_data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RequestInfo) Reset() { *m = RequestInfo{} } +func (*RequestInfo) ProtoMessage() {} +func (*RequestInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_851816e4d6b6361a, []int{5} +} +func (m *RequestInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RequestInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RequestInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RequestInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_RequestInfo.Merge(m, src) +} +func (m *RequestInfo) XXX_Size() int { + return m.Size() +} +func (m *RequestInfo) XXX_DiscardUnknown() { + xxx_messageInfo_RequestInfo.DiscardUnknown(m) } -func (m *RequestInfo) Reset() { *m = RequestInfo{} } -func (*RequestInfo) ProtoMessage() {} -func (*RequestInfo) Descriptor() ([]byte, []int) { return fileDescriptorErrorDetails, []int{5} } +var xxx_messageInfo_RequestInfo proto.InternalMessageInfo func (m *RequestInfo) GetRequestId() string { if m != nil { @@ -323,7 +604,8 @@ type ResourceInfo struct { ResourceType string `protobuf:"bytes,1,opt,name=resource_type,json=resourceType,proto3" json:"resource_type,omitempty"` // The name of the resource being accessed. For example, a shared calendar // name: "example.com_4fghdhgsrgh@group.calendar.google.com", if the current - // error is [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. + // error is + // [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. ResourceName string `protobuf:"bytes,2,opt,name=resource_name,json=resourceName,proto3" json:"resource_name,omitempty"` // The owner of the resource (optional). // For example, "user:" or "project: 0 { - for _, s := range m.StackEntries { - dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) - } + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Detail) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Detail) + copy(dAtA[i:], m.Detail) i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Detail))) - i += copy(dAtA[i:], m.Detail) + i-- + dAtA[i] = 0x12 + } + if len(m.StackEntries) > 0 { + for iNdEx := len(m.StackEntries) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.StackEntries[iNdEx]) + copy(dAtA[i:], m.StackEntries[iNdEx]) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.StackEntries[iNdEx]))) + i-- + dAtA[i] = 0xa + } } - return i, nil + return len(dAtA) - i, nil } func (m *QuotaFailure) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1600,29 +2184,40 @@ func (m *QuotaFailure) Marshal() (dAtA []byte, err error) { } func (m *QuotaFailure) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuotaFailure) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Violations) > 0 { - for _, msg := range m.Violations { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Violations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Violations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintErrorDetails(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *QuotaFailure_Violation) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1630,29 +2225,40 @@ func (m *QuotaFailure_Violation) Marshal() (dAtA []byte, err error) { } func (m *QuotaFailure_Violation) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuotaFailure_Violation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Subject) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Subject))) - i += copy(dAtA[i:], m.Subject) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Description) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Description) + copy(dAtA[i:], m.Description) i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Description))) - i += copy(dAtA[i:], m.Description) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.Subject) > 0 { + i -= len(m.Subject) + copy(dAtA[i:], m.Subject) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Subject))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *PreconditionFailure) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1660,29 +2266,40 @@ func (m *PreconditionFailure) Marshal() (dAtA []byte, err error) { } func (m *PreconditionFailure) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PreconditionFailure) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Violations) > 0 { - for _, msg := range m.Violations { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Violations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Violations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintErrorDetails(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *PreconditionFailure_Violation) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1690,35 +2307,47 @@ func (m *PreconditionFailure_Violation) Marshal() (dAtA []byte, err error) { } func (m *PreconditionFailure_Violation) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PreconditionFailure_Violation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Type) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Type))) - i += copy(dAtA[i:], m.Type) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x1a } if len(m.Subject) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Subject) + copy(dAtA[i:], m.Subject) i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Subject))) - i += copy(dAtA[i:], m.Subject) + i-- + dAtA[i] = 0x12 } - if len(m.Description) > 0 { - dAtA[i] = 0x1a - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Description))) - i += copy(dAtA[i:], m.Description) + if len(m.Type) > 0 { + i -= len(m.Type) + copy(dAtA[i:], m.Type) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Type))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *BadRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1726,29 +2355,40 @@ func (m *BadRequest) Marshal() (dAtA []byte, err error) { } func (m *BadRequest) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BadRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.FieldViolations) > 0 { - for _, msg := range m.FieldViolations { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.FieldViolations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.FieldViolations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintErrorDetails(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *BadRequest_FieldViolation) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1756,29 +2396,40 @@ func (m *BadRequest_FieldViolation) Marshal() (dAtA []byte, err error) { } func (m *BadRequest_FieldViolation) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BadRequest_FieldViolation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Field) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Field))) - i += copy(dAtA[i:], m.Field) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Description) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Description) + copy(dAtA[i:], m.Description) i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Description))) - i += copy(dAtA[i:], m.Description) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.Field) > 0 { + i -= len(m.Field) + copy(dAtA[i:], m.Field) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Field))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *RequestInfo) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1786,29 +2437,40 @@ func (m *RequestInfo) Marshal() (dAtA []byte, err error) { } func (m *RequestInfo) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RequestInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.RequestId) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.RequestId))) - i += copy(dAtA[i:], m.RequestId) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.ServingData) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.ServingData) + copy(dAtA[i:], m.ServingData) i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.ServingData))) - i += copy(dAtA[i:], m.ServingData) + i-- + dAtA[i] = 0x12 + } + if len(m.RequestId) > 0 { + i -= len(m.RequestId) + copy(dAtA[i:], m.RequestId) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.RequestId))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *ResourceInfo) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1816,41 +2478,54 @@ func (m *ResourceInfo) Marshal() (dAtA []byte, err error) { } func (m *ResourceInfo) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.ResourceType) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.ResourceType))) - i += copy(dAtA[i:], m.ResourceType) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.ResourceName) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.ResourceName))) - i += copy(dAtA[i:], m.ResourceName) + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x22 } if len(m.Owner) > 0 { - dAtA[i] = 0x1a - i++ + i -= len(m.Owner) + copy(dAtA[i:], m.Owner) i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Owner))) - i += copy(dAtA[i:], m.Owner) + i-- + dAtA[i] = 0x1a } - if len(m.Description) > 0 { - dAtA[i] = 0x22 - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Description))) - i += copy(dAtA[i:], m.Description) + if len(m.ResourceName) > 0 { + i -= len(m.ResourceName) + copy(dAtA[i:], m.ResourceName) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.ResourceName))) + i-- + dAtA[i] = 0x12 + } + if len(m.ResourceType) > 0 { + i -= len(m.ResourceType) + copy(dAtA[i:], m.ResourceType) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.ResourceType))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *Help) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1858,29 +2533,40 @@ func (m *Help) Marshal() (dAtA []byte, err error) { } func (m *Help) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Help) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Links) > 0 { - for _, msg := range m.Links { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Links) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Links[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintErrorDetails(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *Help_Link) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1888,29 +2574,40 @@ func (m *Help_Link) Marshal() (dAtA []byte, err error) { } func (m *Help_Link) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Help_Link) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Description) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Description))) - i += copy(dAtA[i:], m.Description) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Url) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Url) + copy(dAtA[i:], m.Url) i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Url))) - i += copy(dAtA[i:], m.Url) + i-- + dAtA[i] = 0x12 + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *LocalizedMessage) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1918,40 +2615,54 @@ func (m *LocalizedMessage) Marshal() (dAtA []byte, err error) { } func (m *LocalizedMessage) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LocalizedMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.Locale) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Locale))) - i += copy(dAtA[i:], m.Locale) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Message) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Message) + copy(dAtA[i:], m.Message) i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Message))) - i += copy(dAtA[i:], m.Message) + i-- + dAtA[i] = 0x12 + } + if len(m.Locale) > 0 { + i -= len(m.Locale) + copy(dAtA[i:], m.Locale) + i = encodeVarintErrorDetails(dAtA, i, uint64(len(m.Locale))) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintErrorDetails(dAtA []byte, offset int, v uint64) int { + offset -= sovErrorDetails(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func NewPopulatedRetryInfo(r randyErrorDetails, easy bool) *RetryInfo { this := &RetryInfo{} - if r.Intn(10) != 0 { - this.RetryDelay = google_protobuf1.NewPopulatedDuration(r, easy) + if r.Intn(5) != 0 { + this.RetryDelay = types.NewPopulatedDuration(r, easy) } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 2) } return this } @@ -1965,13 +2676,14 @@ func NewPopulatedDebugInfo(r randyErrorDetails, easy bool) *DebugInfo { } this.Detail = string(randStringErrorDetails(r)) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 3) } return this } func NewPopulatedQuotaFailure(r randyErrorDetails, easy bool) *QuotaFailure { this := &QuotaFailure{} - if r.Intn(10) != 0 { + if r.Intn(5) != 0 { v2 := r.Intn(5) this.Violations = make([]*QuotaFailure_Violation, v2) for i := 0; i < v2; i++ { @@ -1979,6 +2691,7 @@ func NewPopulatedQuotaFailure(r randyErrorDetails, easy bool) *QuotaFailure { } } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 2) } return this } @@ -1988,13 +2701,14 @@ func NewPopulatedQuotaFailure_Violation(r randyErrorDetails, easy bool) *QuotaFa this.Subject = string(randStringErrorDetails(r)) this.Description = string(randStringErrorDetails(r)) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 3) } return this } func NewPopulatedPreconditionFailure(r randyErrorDetails, easy bool) *PreconditionFailure { this := &PreconditionFailure{} - if r.Intn(10) != 0 { + if r.Intn(5) != 0 { v3 := r.Intn(5) this.Violations = make([]*PreconditionFailure_Violation, v3) for i := 0; i < v3; i++ { @@ -2002,6 +2716,7 @@ func NewPopulatedPreconditionFailure(r randyErrorDetails, easy bool) *Preconditi } } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 2) } return this } @@ -2012,13 +2727,14 @@ func NewPopulatedPreconditionFailure_Violation(r randyErrorDetails, easy bool) * this.Subject = string(randStringErrorDetails(r)) this.Description = string(randStringErrorDetails(r)) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 4) } return this } func NewPopulatedBadRequest(r randyErrorDetails, easy bool) *BadRequest { this := &BadRequest{} - if r.Intn(10) != 0 { + if r.Intn(5) != 0 { v4 := r.Intn(5) this.FieldViolations = make([]*BadRequest_FieldViolation, v4) for i := 0; i < v4; i++ { @@ -2026,6 +2742,7 @@ func NewPopulatedBadRequest(r randyErrorDetails, easy bool) *BadRequest { } } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 2) } return this } @@ -2035,6 +2752,7 @@ func NewPopulatedBadRequest_FieldViolation(r randyErrorDetails, easy bool) *BadR this.Field = string(randStringErrorDetails(r)) this.Description = string(randStringErrorDetails(r)) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 3) } return this } @@ -2044,6 +2762,7 @@ func NewPopulatedRequestInfo(r randyErrorDetails, easy bool) *RequestInfo { this.RequestId = string(randStringErrorDetails(r)) this.ServingData = string(randStringErrorDetails(r)) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 3) } return this } @@ -2055,13 +2774,14 @@ func NewPopulatedResourceInfo(r randyErrorDetails, easy bool) *ResourceInfo { this.Owner = string(randStringErrorDetails(r)) this.Description = string(randStringErrorDetails(r)) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 5) } return this } func NewPopulatedHelp(r randyErrorDetails, easy bool) *Help { this := &Help{} - if r.Intn(10) != 0 { + if r.Intn(5) != 0 { v5 := r.Intn(5) this.Links = make([]*Help_Link, v5) for i := 0; i < v5; i++ { @@ -2069,6 +2789,7 @@ func NewPopulatedHelp(r randyErrorDetails, easy bool) *Help { } } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 2) } return this } @@ -2078,6 +2799,7 @@ func NewPopulatedHelp_Link(r randyErrorDetails, easy bool) *Help_Link { this.Description = string(randStringErrorDetails(r)) this.Url = string(randStringErrorDetails(r)) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 3) } return this } @@ -2087,6 +2809,7 @@ func NewPopulatedLocalizedMessage(r randyErrorDetails, easy bool) *LocalizedMess this.Locale = string(randStringErrorDetails(r)) this.Message = string(randStringErrorDetails(r)) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedErrorDetails(r, 3) } return this } @@ -2164,16 +2887,25 @@ func encodeVarintPopulateErrorDetails(dAtA []byte, v uint64) []byte { return dAtA } func (m *RetryInfo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.RetryDelay != nil { l = m.RetryDelay.Size() n += 1 + l + sovErrorDetails(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *DebugInfo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.StackEntries) > 0 { @@ -2186,10 +2918,16 @@ func (m *DebugInfo) Size() (n int) { if l > 0 { n += 1 + l + sovErrorDetails(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *QuotaFailure) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Violations) > 0 { @@ -2198,10 +2936,16 @@ func (m *QuotaFailure) Size() (n int) { n += 1 + l + sovErrorDetails(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *QuotaFailure_Violation) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Subject) @@ -2212,10 +2956,16 @@ func (m *QuotaFailure_Violation) Size() (n int) { if l > 0 { n += 1 + l + sovErrorDetails(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *PreconditionFailure) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Violations) > 0 { @@ -2224,10 +2974,16 @@ func (m *PreconditionFailure) Size() (n int) { n += 1 + l + sovErrorDetails(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *PreconditionFailure_Violation) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Type) @@ -2242,10 +2998,16 @@ func (m *PreconditionFailure_Violation) Size() (n int) { if l > 0 { n += 1 + l + sovErrorDetails(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *BadRequest) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.FieldViolations) > 0 { @@ -2254,10 +3016,16 @@ func (m *BadRequest) Size() (n int) { n += 1 + l + sovErrorDetails(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *BadRequest_FieldViolation) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Field) @@ -2268,10 +3036,16 @@ func (m *BadRequest_FieldViolation) Size() (n int) { if l > 0 { n += 1 + l + sovErrorDetails(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *RequestInfo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.RequestId) @@ -2282,10 +3056,16 @@ func (m *RequestInfo) Size() (n int) { if l > 0 { n += 1 + l + sovErrorDetails(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *ResourceInfo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.ResourceType) @@ -2304,10 +3084,16 @@ func (m *ResourceInfo) Size() (n int) { if l > 0 { n += 1 + l + sovErrorDetails(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *Help) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Links) > 0 { @@ -2316,10 +3102,16 @@ func (m *Help) Size() (n int) { n += 1 + l + sovErrorDetails(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *Help_Link) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Description) @@ -2330,10 +3122,16 @@ func (m *Help_Link) Size() (n int) { if l > 0 { n += 1 + l + sovErrorDetails(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *LocalizedMessage) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Locale) @@ -2344,18 +3142,14 @@ func (m *LocalizedMessage) Size() (n int) { if l > 0 { n += 1 + l + sovErrorDetails(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovErrorDetails(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozErrorDetails(x uint64) (n int) { return sovErrorDetails(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -2365,7 +3159,8 @@ func (this *RetryInfo) String() string { return "nil" } s := strings.Join([]string{`&RetryInfo{`, - `RetryDelay:` + strings.Replace(fmt.Sprintf("%v", this.RetryDelay), "Duration", "google_protobuf1.Duration", 1) + `,`, + `RetryDelay:` + strings.Replace(fmt.Sprintf("%v", this.RetryDelay), "Duration", "types.Duration", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2377,6 +3172,7 @@ func (this *DebugInfo) String() string { s := strings.Join([]string{`&DebugInfo{`, `StackEntries:` + fmt.Sprintf("%v", this.StackEntries) + `,`, `Detail:` + fmt.Sprintf("%v", this.Detail) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2385,8 +3181,14 @@ func (this *QuotaFailure) String() string { if this == nil { return "nil" } + repeatedStringForViolations := "[]*QuotaFailure_Violation{" + for _, f := range this.Violations { + repeatedStringForViolations += strings.Replace(fmt.Sprintf("%v", f), "QuotaFailure_Violation", "QuotaFailure_Violation", 1) + "," + } + repeatedStringForViolations += "}" s := strings.Join([]string{`&QuotaFailure{`, - `Violations:` + strings.Replace(fmt.Sprintf("%v", this.Violations), "QuotaFailure_Violation", "QuotaFailure_Violation", 1) + `,`, + `Violations:` + repeatedStringForViolations + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2398,6 +3200,7 @@ func (this *QuotaFailure_Violation) String() string { s := strings.Join([]string{`&QuotaFailure_Violation{`, `Subject:` + fmt.Sprintf("%v", this.Subject) + `,`, `Description:` + fmt.Sprintf("%v", this.Description) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2406,8 +3209,14 @@ func (this *PreconditionFailure) String() string { if this == nil { return "nil" } + repeatedStringForViolations := "[]*PreconditionFailure_Violation{" + for _, f := range this.Violations { + repeatedStringForViolations += strings.Replace(fmt.Sprintf("%v", f), "PreconditionFailure_Violation", "PreconditionFailure_Violation", 1) + "," + } + repeatedStringForViolations += "}" s := strings.Join([]string{`&PreconditionFailure{`, - `Violations:` + strings.Replace(fmt.Sprintf("%v", this.Violations), "PreconditionFailure_Violation", "PreconditionFailure_Violation", 1) + `,`, + `Violations:` + repeatedStringForViolations + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2420,6 +3229,7 @@ func (this *PreconditionFailure_Violation) String() string { `Type:` + fmt.Sprintf("%v", this.Type) + `,`, `Subject:` + fmt.Sprintf("%v", this.Subject) + `,`, `Description:` + fmt.Sprintf("%v", this.Description) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2428,8 +3238,14 @@ func (this *BadRequest) String() string { if this == nil { return "nil" } + repeatedStringForFieldViolations := "[]*BadRequest_FieldViolation{" + for _, f := range this.FieldViolations { + repeatedStringForFieldViolations += strings.Replace(fmt.Sprintf("%v", f), "BadRequest_FieldViolation", "BadRequest_FieldViolation", 1) + "," + } + repeatedStringForFieldViolations += "}" s := strings.Join([]string{`&BadRequest{`, - `FieldViolations:` + strings.Replace(fmt.Sprintf("%v", this.FieldViolations), "BadRequest_FieldViolation", "BadRequest_FieldViolation", 1) + `,`, + `FieldViolations:` + repeatedStringForFieldViolations + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2441,6 +3257,7 @@ func (this *BadRequest_FieldViolation) String() string { s := strings.Join([]string{`&BadRequest_FieldViolation{`, `Field:` + fmt.Sprintf("%v", this.Field) + `,`, `Description:` + fmt.Sprintf("%v", this.Description) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2452,6 +3269,7 @@ func (this *RequestInfo) String() string { s := strings.Join([]string{`&RequestInfo{`, `RequestId:` + fmt.Sprintf("%v", this.RequestId) + `,`, `ServingData:` + fmt.Sprintf("%v", this.ServingData) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2465,6 +3283,7 @@ func (this *ResourceInfo) String() string { `ResourceName:` + fmt.Sprintf("%v", this.ResourceName) + `,`, `Owner:` + fmt.Sprintf("%v", this.Owner) + `,`, `Description:` + fmt.Sprintf("%v", this.Description) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2473,8 +3292,14 @@ func (this *Help) String() string { if this == nil { return "nil" } + repeatedStringForLinks := "[]*Help_Link{" + for _, f := range this.Links { + repeatedStringForLinks += strings.Replace(fmt.Sprintf("%v", f), "Help_Link", "Help_Link", 1) + "," + } + repeatedStringForLinks += "}" s := strings.Join([]string{`&Help{`, - `Links:` + strings.Replace(fmt.Sprintf("%v", this.Links), "Help_Link", "Help_Link", 1) + `,`, + `Links:` + repeatedStringForLinks + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2486,6 +3311,7 @@ func (this *Help_Link) String() string { s := strings.Join([]string{`&Help_Link{`, `Description:` + fmt.Sprintf("%v", this.Description) + `,`, `Url:` + fmt.Sprintf("%v", this.Url) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2497,6 +3323,7 @@ func (this *LocalizedMessage) String() string { s := strings.Join([]string{`&LocalizedMessage{`, `Locale:` + fmt.Sprintf("%v", this.Locale) + `,`, `Message:` + fmt.Sprintf("%v", this.Message) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -2524,7 +3351,7 @@ func (m *RetryInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2552,7 +3379,7 @@ func (m *RetryInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2561,11 +3388,14 @@ func (m *RetryInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } if m.RetryDelay == nil { - m.RetryDelay = &google_protobuf1.Duration{} + m.RetryDelay = &types.Duration{} } if err := m.RetryDelay.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -2580,9 +3410,13 @@ func (m *RetryInfo) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2607,7 +3441,7 @@ func (m *DebugInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2635,7 +3469,7 @@ func (m *DebugInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2645,6 +3479,9 @@ func (m *DebugInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2664,7 +3501,7 @@ func (m *DebugInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2674,6 +3511,9 @@ func (m *DebugInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2688,9 +3528,13 @@ func (m *DebugInfo) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2715,7 +3559,7 @@ func (m *QuotaFailure) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2743,7 +3587,7 @@ func (m *QuotaFailure) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2752,6 +3596,9 @@ func (m *QuotaFailure) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2769,9 +3616,13 @@ func (m *QuotaFailure) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2796,7 +3647,7 @@ func (m *QuotaFailure_Violation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2824,7 +3675,7 @@ func (m *QuotaFailure_Violation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2834,6 +3685,9 @@ func (m *QuotaFailure_Violation) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2853,7 +3707,7 @@ func (m *QuotaFailure_Violation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2863,6 +3717,9 @@ func (m *QuotaFailure_Violation) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2877,9 +3734,13 @@ func (m *QuotaFailure_Violation) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2904,7 +3765,7 @@ func (m *PreconditionFailure) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2932,7 +3793,7 @@ func (m *PreconditionFailure) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2941,6 +3802,9 @@ func (m *PreconditionFailure) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2958,9 +3822,13 @@ func (m *PreconditionFailure) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2985,7 +3853,7 @@ func (m *PreconditionFailure_Violation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3013,7 +3881,7 @@ func (m *PreconditionFailure_Violation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3023,6 +3891,9 @@ func (m *PreconditionFailure_Violation) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3042,7 +3913,7 @@ func (m *PreconditionFailure_Violation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3052,6 +3923,9 @@ func (m *PreconditionFailure_Violation) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3071,7 +3945,7 @@ func (m *PreconditionFailure_Violation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3081,6 +3955,9 @@ func (m *PreconditionFailure_Violation) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3095,9 +3972,13 @@ func (m *PreconditionFailure_Violation) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3122,7 +4003,7 @@ func (m *BadRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3150,7 +4031,7 @@ func (m *BadRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3159,6 +4040,9 @@ func (m *BadRequest) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3176,9 +4060,13 @@ func (m *BadRequest) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3203,7 +4091,7 @@ func (m *BadRequest_FieldViolation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3231,7 +4119,7 @@ func (m *BadRequest_FieldViolation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3241,6 +4129,9 @@ func (m *BadRequest_FieldViolation) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3260,7 +4151,7 @@ func (m *BadRequest_FieldViolation) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3270,6 +4161,9 @@ func (m *BadRequest_FieldViolation) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3284,9 +4178,13 @@ func (m *BadRequest_FieldViolation) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3311,7 +4209,7 @@ func (m *RequestInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3339,7 +4237,7 @@ func (m *RequestInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3349,6 +4247,9 @@ func (m *RequestInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3368,7 +4269,7 @@ func (m *RequestInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3378,6 +4279,9 @@ func (m *RequestInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3392,9 +4296,13 @@ func (m *RequestInfo) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3419,7 +4327,7 @@ func (m *ResourceInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3447,7 +4355,7 @@ func (m *ResourceInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3457,6 +4365,9 @@ func (m *ResourceInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3476,7 +4387,7 @@ func (m *ResourceInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3486,6 +4397,9 @@ func (m *ResourceInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3505,7 +4419,7 @@ func (m *ResourceInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3515,6 +4429,9 @@ func (m *ResourceInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3534,7 +4451,7 @@ func (m *ResourceInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3544,6 +4461,9 @@ func (m *ResourceInfo) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3558,9 +4478,13 @@ func (m *ResourceInfo) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3585,7 +4509,7 @@ func (m *Help) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3613,7 +4537,7 @@ func (m *Help) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -3622,6 +4546,9 @@ func (m *Help) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3639,9 +4566,13 @@ func (m *Help) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3666,7 +4597,7 @@ func (m *Help_Link) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3694,7 +4625,7 @@ func (m *Help_Link) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3704,6 +4635,9 @@ func (m *Help_Link) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3723,7 +4657,7 @@ func (m *Help_Link) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3733,6 +4667,9 @@ func (m *Help_Link) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3747,9 +4684,13 @@ func (m *Help_Link) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3774,7 +4715,7 @@ func (m *LocalizedMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3802,7 +4743,7 @@ func (m *LocalizedMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3812,6 +4753,9 @@ func (m *LocalizedMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3831,7 +4775,7 @@ func (m *LocalizedMessage) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -3841,6 +4785,9 @@ func (m *LocalizedMessage) Unmarshal(dAtA []byte) error { return ErrInvalidLengthErrorDetails } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthErrorDetails + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -3855,9 +4802,13 @@ func (m *LocalizedMessage) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthErrorDetails } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthErrorDetails + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -3870,6 +4821,7 @@ func (m *LocalizedMessage) Unmarshal(dAtA []byte) error { func skipErrorDetails(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -3901,10 +4853,8 @@ func skipErrorDetails(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -3921,98 +4871,34 @@ func skipErrorDetails(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthErrorDetails } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowErrorDetails - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipErrorDetails(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupErrorDetails + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthErrorDetails + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthErrorDetails = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowErrorDetails = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthErrorDetails = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowErrorDetails = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupErrorDetails = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("google/rpc/error_details.proto", fileDescriptorErrorDetails) } - -var fileDescriptorErrorDetails = []byte{ - // 624 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xbf, 0x6f, 0xd3, 0x40, - 0x18, 0xed, 0x35, 0x69, 0x91, 0xbf, 0x84, 0x52, 0xcc, 0x0f, 0x85, 0x48, 0x9c, 0x82, 0x11, 0x52, - 0x11, 0x92, 0x2b, 0x95, 0xad, 0x63, 0x48, 0x7f, 0x49, 0x05, 0x82, 0x85, 0x18, 0x60, 0xb0, 0x2e, - 0xf6, 0x97, 0xe8, 0xa8, 0xe3, 0x33, 0x67, 0xbb, 0xa8, 0x4c, 0xfc, 0x09, 0xec, 0x6c, 0x4c, 0xfd, - 0x27, 0xd8, 0x3b, 0x76, 0x64, 0x24, 0xe9, 0xc2, 0xd8, 0x91, 0x11, 0x9d, 0x7d, 0xd7, 0xba, 0x4d, - 0x41, 0x6c, 0x7e, 0xef, 0xde, 0x3d, 0xbf, 0xf7, 0xe9, 0xee, 0x80, 0x8e, 0x84, 0x18, 0x45, 0xb8, - 0x2a, 0x93, 0x60, 0x15, 0xa5, 0x14, 0xd2, 0x0f, 0x31, 0x63, 0x3c, 0x4a, 0xdd, 0x44, 0x8a, 0x4c, - 0xd8, 0x50, 0xae, 0xbb, 0x32, 0x09, 0xda, 0x46, 0x5b, 0xac, 0x0c, 0xf2, 0xe1, 0x6a, 0x98, 0x4b, - 0x96, 0x71, 0x11, 0x97, 0x5a, 0x67, 0x0b, 0x2c, 0x0f, 0x33, 0x79, 0xb0, 0x13, 0x0f, 0x85, 0xbd, - 0x0e, 0x0d, 0xa9, 0x80, 0x1f, 0x62, 0xc4, 0x0e, 0x5a, 0xa4, 0x43, 0x56, 0x1a, 0x6b, 0xf7, 0x5c, - 0x6d, 0x67, 0x2c, 0xdc, 0x9e, 0xb6, 0xf0, 0xa0, 0x50, 0xf7, 0x94, 0xd8, 0xd9, 0x06, 0xab, 0x87, - 0x83, 0x7c, 0x54, 0x18, 0x3d, 0x84, 0xeb, 0x69, 0xc6, 0x82, 0x3d, 0x1f, 0xe3, 0x4c, 0x72, 0x4c, - 0x5b, 0xa4, 0x53, 0x5b, 0xb1, 0xbc, 0x66, 0x41, 0x6e, 0x94, 0x9c, 0x7d, 0x17, 0x16, 0xcb, 0xdc, - 0xad, 0xf9, 0x0e, 0x59, 0xb1, 0x3c, 0x8d, 0x9c, 0xaf, 0x04, 0x9a, 0xaf, 0x72, 0x91, 0xb1, 0x4d, - 0xc6, 0xa3, 0x5c, 0xa2, 0xdd, 0x05, 0xd8, 0xe7, 0x22, 0x2a, 0xfe, 0x59, 0x5a, 0x35, 0xd6, 0x1c, - 0xf7, 0xbc, 0xa4, 0x5b, 0x55, 0xbb, 0x6f, 0x8c, 0xd4, 0xab, 0xec, 0x6a, 0x6f, 0x81, 0x75, 0xb6, - 0x60, 0xb7, 0xe0, 0x5a, 0x9a, 0x0f, 0xde, 0x63, 0x90, 0x15, 0x1d, 0x2d, 0xcf, 0x40, 0xbb, 0x03, - 0x8d, 0x10, 0xd3, 0x40, 0xf2, 0x44, 0x09, 0x75, 0xb0, 0x2a, 0xe5, 0x7c, 0x27, 0x70, 0xab, 0x2f, - 0x31, 0x10, 0x71, 0xc8, 0x15, 0x61, 0x42, 0xee, 0x5c, 0x11, 0xf2, 0x71, 0x35, 0xe4, 0x15, 0x9b, - 0xfe, 0x92, 0xf5, 0x5d, 0x35, 0xab, 0x0d, 0xf5, 0xec, 0x20, 0x41, 0x1d, 0xb4, 0xf8, 0xae, 0xe6, - 0x9f, 0xff, 0x67, 0xfe, 0xda, 0x6c, 0xfe, 0x43, 0x02, 0xd0, 0x65, 0xa1, 0x87, 0x1f, 0x72, 0x4c, - 0x33, 0xbb, 0x0f, 0xcb, 0x43, 0x8e, 0x51, 0xe8, 0xcf, 0x84, 0x7f, 0x54, 0x0d, 0x7f, 0xbe, 0xc3, - 0xdd, 0x54, 0xf2, 0xf3, 0xe0, 0x37, 0x86, 0x17, 0x70, 0xda, 0xde, 0x86, 0xa5, 0x8b, 0x12, 0xfb, - 0x36, 0x2c, 0x14, 0x22, 0xdd, 0xa1, 0x04, 0xff, 0x31, 0xea, 0x97, 0xd0, 0xd0, 0x3f, 0x2d, 0x0e, - 0xd5, 0x7d, 0x00, 0x59, 0x42, 0x9f, 0x1b, 0x2f, 0x4b, 0x33, 0x3b, 0xa1, 0xfd, 0x00, 0x9a, 0x29, - 0xca, 0x7d, 0x1e, 0x8f, 0xfc, 0x90, 0x65, 0xcc, 0x18, 0x6a, 0xae, 0xc7, 0x32, 0xe6, 0x7c, 0x21, - 0xd0, 0xf4, 0x30, 0x15, 0xb9, 0x0c, 0xd0, 0x9c, 0x53, 0xa9, 0xb1, 0x5f, 0x99, 0x72, 0xd3, 0x90, - 0xaf, 0xd5, 0xb4, 0xab, 0xa2, 0x98, 0x8d, 0x51, 0x3b, 0x9f, 0x89, 0x5e, 0xb0, 0x31, 0xaa, 0x8e, - 0xe2, 0x63, 0x8c, 0x52, 0x8f, 0xbc, 0x04, 0x97, 0x3b, 0xd6, 0x67, 0x3b, 0x0a, 0xa8, 0x6f, 0x63, - 0x94, 0xd8, 0x4f, 0x60, 0x21, 0xe2, 0xf1, 0x9e, 0x19, 0xfe, 0x9d, 0xea, 0xf0, 0x95, 0xc0, 0xdd, - 0xe5, 0xf1, 0x9e, 0x57, 0x6a, 0xda, 0xeb, 0x50, 0x57, 0xf0, 0xb2, 0x3d, 0x99, 0xb1, 0xb7, 0x97, - 0xa1, 0x96, 0x4b, 0x73, 0xc1, 0xd4, 0xa7, 0xd3, 0x83, 0xe5, 0x5d, 0x11, 0xb0, 0x88, 0x7f, 0xc2, - 0xf0, 0x39, 0xa6, 0x29, 0x1b, 0xa1, 0xba, 0x89, 0x91, 0xe2, 0x4c, 0x7f, 0x8d, 0xd4, 0x39, 0x1b, - 0x97, 0x12, 0x73, 0xce, 0x34, 0xec, 0x86, 0xc7, 0x13, 0x3a, 0xf7, 0x63, 0x42, 0xe7, 0x4e, 0x27, - 0x94, 0xfc, 0x9e, 0x50, 0xf2, 0x79, 0x4a, 0xc9, 0xe1, 0x94, 0x92, 0xa3, 0x29, 0x25, 0xc7, 0x53, - 0x4a, 0x7e, 0x4e, 0x29, 0xf9, 0x35, 0xa5, 0x73, 0xa7, 0x8a, 0x3f, 0xa1, 0xe4, 0xe8, 0x84, 0x12, - 0x58, 0x0a, 0xc4, 0xb8, 0x52, 0xac, 0x7b, 0x73, 0x43, 0xbd, 0x5e, 0xbd, 0xf2, 0xf1, 0xea, 0xab, - 0xe7, 0xa5, 0x4f, 0xde, 0xd6, 0x64, 0x12, 0x7c, 0x9b, 0xaf, 0x79, 0xfd, 0x67, 0x83, 0xc5, 0xe2, - 0xc9, 0x79, 0xfa, 0x27, 0x00, 0x00, 0xff, 0xff, 0x63, 0xe4, 0x76, 0x26, 0xf1, 0x04, 0x00, 0x00, -} diff --git a/vendor/github.com/gogo/googleapis/google/rpc/error_details.proto b/vendor/github.com/gogo/googleapis/google/rpc/error_details.proto index a62078ba0a0ec..0682cc97bb895 100644 --- a/vendor/github.com/gogo/googleapis/google/rpc/error_details.proto +++ b/vendor/github.com/gogo/googleapis/google/rpc/error_details.proto @@ -24,7 +24,6 @@ option java_outer_classname = "ErrorDetailsProto"; option java_package = "com.google.rpc"; option objc_class_prefix = "RPC"; - // Describes when the clients can retry a failed request. Clients could ignore // the recommendation here or retry when this information is missing from error // responses. @@ -154,7 +153,8 @@ message ResourceInfo { // The name of the resource being accessed. For example, a shared calendar // name: "example.com_4fghdhgsrgh@group.calendar.google.com", if the current - // error is [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. + // error is + // [google.rpc.Code.PERMISSION_DENIED][google.rpc.Code.PERMISSION_DENIED]. string resource_name = 2; // The owner of the resource (optional). diff --git a/vendor/github.com/gogo/googleapis/google/rpc/status.pb.go b/vendor/github.com/gogo/googleapis/google/rpc/status.pb.go index 2d123908f78af..59793ba3809c4 100644 --- a/vendor/github.com/gogo/googleapis/google/rpc/status.pb.go +++ b/vendor/github.com/gogo/googleapis/google/rpc/status.pb.go @@ -1,37 +1,19 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: google/rpc/status.proto -/* - Package rpc is a generated protocol buffer package. - - It is generated from these files: - google/rpc/status.proto - google/rpc/error_details.proto - google/rpc/code.proto - - It has these top-level messages: - Status - RetryInfo - DebugInfo - QuotaFailure - PreconditionFailure - BadRequest - RequestInfo - ResourceInfo - Help - LocalizedMessage -*/ package rpc -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/gogo/protobuf/types" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + types "github.com/gogo/protobuf/types" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -42,26 +24,27 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// The `Status` type defines a logical error model that is suitable for different -// programming environments, including REST APIs and RPC APIs. It is used by -// [gRPC](https://github.com/grpc). The error model is designed to be: +// The `Status` type defines a logical error model that is suitable for +// different programming environments, including REST APIs and RPC APIs. It is +// used by [gRPC](https://github.com/grpc). The error model is designed to be: // // - Simple to use and understand for most users // - Flexible enough to meet unexpected needs // // # Overview // -// The `Status` message contains three pieces of data: error code, error message, -// and error details. The error code should be an enum value of -// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The -// error message should be a developer-facing English message that helps -// developers *understand* and *resolve* the error. If a localized user-facing -// error message is needed, put the localized message in the error details or -// localize it in the client. The optional error details may contain arbitrary -// information about the error. There is a predefined set of error detail types -// in the package `google.rpc` that can be used for common error conditions. +// The `Status` message contains three pieces of data: error code, error +// message, and error details. The error code should be an enum value of +// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes +// if needed. The error message should be a developer-facing English message +// that helps developers *understand* and *resolve* the error. If a localized +// user-facing error message is needed, put the localized message in the error +// details or localize it in the client. The optional error details may contain +// arbitrary information about the error. There is a predefined set of error +// detail types in the package `google.rpc` that can be used for common error +// conditions. // // # Language mapping // @@ -97,20 +80,53 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package // - Logging. If some API errors are stored in logs, the message `Status` could // be used directly after any stripping needed for security/privacy reasons. type Status struct { - // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + // The status code, which should be an enum value of + // [google.rpc.Code][google.rpc.Code]. Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` // A developer-facing error message, which should be in English. Any // user-facing error message should be localized and sent in the - // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + // [google.rpc.Status.details][google.rpc.Status.details] field, or localized + // by the client. Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // A list of messages that carry the error details. There is a common set of // message types for APIs to use. - Details []*google_protobuf.Any `protobuf:"bytes,3,rep,name=details" json:"details,omitempty"` + Details []*types.Any `protobuf:"bytes,3,rep,name=details,proto3" json:"details,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *Status) Reset() { *m = Status{} } -func (*Status) ProtoMessage() {} -func (*Status) Descriptor() ([]byte, []int) { return fileDescriptorStatus, []int{0} } +func (m *Status) Reset() { *m = Status{} } +func (*Status) ProtoMessage() {} +func (*Status) Descriptor() ([]byte, []int) { + return fileDescriptor_24d244abaf643bfe, []int{0} +} +func (m *Status) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Status) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Status.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Status) XXX_Merge(src proto.Message) { + xxx_messageInfo_Status.Merge(m, src) +} +func (m *Status) XXX_Size() int { + return m.Size() +} +func (m *Status) XXX_DiscardUnknown() { + xxx_messageInfo_Status.DiscardUnknown(m) +} + +var xxx_messageInfo_Status proto.InternalMessageInfo func (m *Status) GetCode() int32 { if m != nil { @@ -126,7 +142,7 @@ func (m *Status) GetMessage() string { return "" } -func (m *Status) GetDetails() []*google_protobuf.Any { +func (m *Status) GetDetails() []*types.Any { if m != nil { return m.Details } @@ -139,6 +155,28 @@ func (*Status) XXX_MessageName() string { func init() { proto.RegisterType((*Status)(nil), "google.rpc.Status") } + +func init() { proto.RegisterFile("google/rpc/status.proto", fileDescriptor_24d244abaf643bfe) } + +var fileDescriptor_24d244abaf643bfe = []byte{ + // 235 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x2a, 0x48, 0xd6, 0x2f, 0x2e, 0x49, 0x2c, 0x29, 0x2d, 0xd6, 0x2b, 0x28, + 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x82, 0x48, 0xe8, 0x15, 0x15, 0x24, 0x4b, 0x49, 0x42, 0x15, 0x81, + 0x65, 0x92, 0x4a, 0xd3, 0xf4, 0x13, 0xf3, 0x2a, 0x21, 0xca, 0x94, 0xd2, 0xb8, 0xd8, 0x82, 0xc1, + 0xda, 0x84, 0x84, 0xb8, 0x58, 0x92, 0xf3, 0x53, 0x52, 0x25, 0x18, 0x15, 0x18, 0x35, 0x58, 0x83, + 0xc0, 0x6c, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0xf4, 0x54, 0x09, 0x26, 0x05, + 0x46, 0x0d, 0xce, 0x20, 0x18, 0x57, 0x48, 0x8f, 0x8b, 0x3d, 0x25, 0xb5, 0x24, 0x31, 0x33, 0xa7, + 0x58, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x44, 0x0f, 0x6a, 0x21, 0xcc, 0x12, 0x3d, 0xc7, + 0xbc, 0xca, 0x20, 0x98, 0x22, 0xa7, 0xb8, 0x0b, 0x0f, 0xe5, 0x18, 0x6e, 0x3c, 0x94, 0x63, 0xf8, + 0xf0, 0x50, 0x8e, 0xf1, 0xc7, 0x43, 0x39, 0xc6, 0x86, 0x47, 0x72, 0x8c, 0x2b, 0x1e, 0xc9, 0x31, + 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x2f, 0x1e, 0xc9, + 0x31, 0x7c, 0x00, 0x89, 0x3f, 0x96, 0x63, 0x3c, 0xf1, 0x58, 0x8e, 0x91, 0x8b, 0x2f, 0x39, 0x3f, + 0x57, 0x0f, 0xe1, 0x11, 0x27, 0x6e, 0x88, 0x5b, 0x03, 0x40, 0x56, 0x04, 0x30, 0x46, 0x31, 0x17, + 0x15, 0x24, 0x2f, 0x62, 0x62, 0x0e, 0x0a, 0x70, 0x4e, 0x62, 0x03, 0x5b, 0x6b, 0x0c, 0x08, 0x00, + 0x00, 0xff, 0xff, 0xaa, 0x06, 0xa1, 0xaa, 0x10, 0x01, 0x00, 0x00, +} + func (this *Status) Compare(that interface{}) int { if that == nil { if this == nil { @@ -187,6 +225,9 @@ func (this *Status) Compare(that interface{}) int { return c } } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *Status) Equal(that interface{}) bool { @@ -222,6 +263,9 @@ func (this *Status) Equal(that interface{}) bool { return false } } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *Status) GoString() string { @@ -235,6 +279,9 @@ func (this *Status) GoString() string { if this.Details != nil { s = append(s, "Details: "+fmt.Sprintf("%#v", this.Details)+",\n") } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -249,7 +296,7 @@ func valueToGoStringStatus(v interface{}, typ string) string { func (m *Status) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -257,44 +304,58 @@ func (m *Status) Marshal() (dAtA []byte, err error) { } func (m *Status) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Status) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Code != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintStatus(dAtA, i, uint64(m.Code)) - } - if len(m.Message) > 0 { - dAtA[i] = 0x12 - i++ - i = encodeVarintStatus(dAtA, i, uint64(len(m.Message))) - i += copy(dAtA[i:], m.Message) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Details) > 0 { - for _, msg := range m.Details { - dAtA[i] = 0x1a - i++ - i = encodeVarintStatus(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Details) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Details[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStatus(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0x1a } } - return i, nil + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintStatus(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x12 + } + if m.Code != 0 { + i = encodeVarintStatus(dAtA, i, uint64(m.Code)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func encodeVarintStatus(dAtA []byte, offset int, v uint64) int { + offset -= sovStatus(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func NewPopulatedStatus(r randyStatus, easy bool) *Status { this := &Status{} @@ -303,14 +364,15 @@ func NewPopulatedStatus(r randyStatus, easy bool) *Status { this.Code *= -1 } this.Message = string(randStringStatus(r)) - if r.Intn(10) != 0 { + if r.Intn(5) != 0 { v1 := r.Intn(5) - this.Details = make([]*google_protobuf.Any, v1) + this.Details = make([]*types.Any, v1) for i := 0; i < v1; i++ { - this.Details[i] = google_protobuf.NewPopulatedAny(r, easy) + this.Details[i] = types.NewPopulatedAny(r, easy) } } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedStatus(r, 4) } return this } @@ -388,6 +450,9 @@ func encodeVarintPopulateStatus(dAtA []byte, v uint64) []byte { return dAtA } func (m *Status) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Code != 0 { @@ -403,18 +468,14 @@ func (m *Status) Size() (n int) { n += 1 + l + sovStatus(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovStatus(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozStatus(x uint64) (n int) { return sovStatus(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -423,10 +484,16 @@ func (this *Status) String() string { if this == nil { return "nil" } + repeatedStringForDetails := "[]*Any{" + for _, f := range this.Details { + repeatedStringForDetails += strings.Replace(fmt.Sprintf("%v", f), "Any", "types.Any", 1) + "," + } + repeatedStringForDetails += "}" s := strings.Join([]string{`&Status{`, `Code:` + fmt.Sprintf("%v", this.Code) + `,`, `Message:` + fmt.Sprintf("%v", this.Message) + `,`, - `Details:` + strings.Replace(fmt.Sprintf("%v", this.Details), "Any", "google_protobuf.Any", 1) + `,`, + `Details:` + repeatedStringForDetails + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -454,7 +521,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -482,7 +549,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Code |= (int32(b) & 0x7F) << shift + m.Code |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -501,7 +568,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -511,6 +578,9 @@ func (m *Status) Unmarshal(dAtA []byte) error { return ErrInvalidLengthStatus } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStatus + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -530,7 +600,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -539,10 +609,13 @@ func (m *Status) Unmarshal(dAtA []byte) error { return ErrInvalidLengthStatus } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStatus + } if postIndex > l { return io.ErrUnexpectedEOF } - m.Details = append(m.Details, &google_protobuf.Any{}) + m.Details = append(m.Details, &types.Any{}) if err := m.Details[len(m.Details)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -556,9 +629,13 @@ func (m *Status) Unmarshal(dAtA []byte) error { if skippy < 0 { return ErrInvalidLengthStatus } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthStatus + } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -571,6 +648,7 @@ func (m *Status) Unmarshal(dAtA []byte) error { func skipStatus(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -602,10 +680,8 @@ func skipStatus(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -622,74 +698,34 @@ func skipStatus(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthStatus } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowStatus - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipStatus(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupStatus + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthStatus + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthStatus = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowStatus = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthStatus = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowStatus = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupStatus = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("google/rpc/status.proto", fileDescriptorStatus) } - -var fileDescriptorStatus = []byte{ - // 235 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0xcf, 0xcf, 0x4f, - 0xcf, 0x49, 0xd5, 0x2f, 0x2a, 0x48, 0xd6, 0x2f, 0x2e, 0x49, 0x2c, 0x29, 0x2d, 0xd6, 0x2b, 0x28, - 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x82, 0x48, 0xe8, 0x15, 0x15, 0x24, 0x4b, 0x49, 0x42, 0x15, 0x81, - 0x65, 0x92, 0x4a, 0xd3, 0xf4, 0x13, 0xf3, 0x2a, 0x21, 0xca, 0x94, 0xd2, 0xb8, 0xd8, 0x82, 0xc1, - 0xda, 0x84, 0x84, 0xb8, 0x58, 0x92, 0xf3, 0x53, 0x52, 0x25, 0x18, 0x15, 0x18, 0x35, 0x58, 0x83, - 0xc0, 0x6c, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0xf4, 0x54, 0x09, 0x26, 0x05, - 0x46, 0x0d, 0xce, 0x20, 0x18, 0x57, 0x48, 0x8f, 0x8b, 0x3d, 0x25, 0xb5, 0x24, 0x31, 0x33, 0xa7, - 0x58, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x44, 0x0f, 0x6a, 0x21, 0xcc, 0x12, 0x3d, 0xc7, - 0xbc, 0xca, 0x20, 0x98, 0x22, 0xa7, 0xb8, 0x0b, 0x0f, 0xe5, 0x18, 0x6e, 0x3c, 0x94, 0x63, 0xf8, - 0xf0, 0x50, 0x8e, 0xf1, 0xc7, 0x43, 0x39, 0xc6, 0x86, 0x47, 0x72, 0x8c, 0x2b, 0x1e, 0xc9, 0x31, - 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x2f, 0x1e, 0xc9, - 0x31, 0x7c, 0x00, 0x89, 0x3f, 0x96, 0x63, 0x3c, 0xf1, 0x58, 0x8e, 0x91, 0x8b, 0x2f, 0x39, 0x3f, - 0x57, 0x0f, 0xe1, 0x11, 0x27, 0x6e, 0x88, 0x5b, 0x03, 0x40, 0x56, 0x04, 0x30, 0x46, 0x31, 0x17, - 0x15, 0x24, 0x2f, 0x62, 0x62, 0x0e, 0x0a, 0x70, 0x4e, 0x62, 0x03, 0x5b, 0x6b, 0x0c, 0x08, 0x00, - 0x00, 0xff, 0xff, 0xaa, 0x06, 0xa1, 0xaa, 0x10, 0x01, 0x00, 0x00, -} diff --git a/vendor/github.com/gogo/googleapis/google/rpc/status.proto b/vendor/github.com/gogo/googleapis/google/rpc/status.proto index db3226ee06fc0..abcd4534317f9 100644 --- a/vendor/github.com/gogo/googleapis/google/rpc/status.proto +++ b/vendor/github.com/gogo/googleapis/google/rpc/status.proto @@ -24,25 +24,25 @@ option java_outer_classname = "StatusProto"; option java_package = "com.google.rpc"; option objc_class_prefix = "RPC"; - -// The `Status` type defines a logical error model that is suitable for different -// programming environments, including REST APIs and RPC APIs. It is used by -// [gRPC](https://github.com/grpc). The error model is designed to be: +// The `Status` type defines a logical error model that is suitable for +// different programming environments, including REST APIs and RPC APIs. It is +// used by [gRPC](https://github.com/grpc). The error model is designed to be: // // - Simple to use and understand for most users // - Flexible enough to meet unexpected needs // // # Overview // -// The `Status` message contains three pieces of data: error code, error message, -// and error details. The error code should be an enum value of -// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The -// error message should be a developer-facing English message that helps -// developers *understand* and *resolve* the error. If a localized user-facing -// error message is needed, put the localized message in the error details or -// localize it in the client. The optional error details may contain arbitrary -// information about the error. There is a predefined set of error detail types -// in the package `google.rpc` that can be used for common error conditions. +// The `Status` message contains three pieces of data: error code, error +// message, and error details. The error code should be an enum value of +// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes +// if needed. The error message should be a developer-facing English message +// that helps developers *understand* and *resolve* the error. If a localized +// user-facing error message is needed, put the localized message in the error +// details or localize it in the client. The optional error details may contain +// arbitrary information about the error. There is a predefined set of error +// detail types in the package `google.rpc` that can be used for common error +// conditions. // // # Language mapping // @@ -78,12 +78,14 @@ option objc_class_prefix = "RPC"; // - Logging. If some API errors are stored in logs, the message `Status` could // be used directly after any stripping needed for security/privacy reasons. message Status { - // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + // The status code, which should be an enum value of + // [google.rpc.Code][google.rpc.Code]. int32 code = 1; // A developer-facing error message, which should be in English. Any // user-facing error message should be localized and sent in the - // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + // [google.rpc.Status.details][google.rpc.Status.details] field, or localized + // by the client. string message = 2; // A list of messages that carry the error details. There is a common set of diff --git a/vendor/github.com/gogo/protobuf/LICENSE b/vendor/github.com/gogo/protobuf/LICENSE index 7be0cc7b62cf0..f57de90da8ac3 100644 --- a/vendor/github.com/gogo/protobuf/LICENSE +++ b/vendor/github.com/gogo/protobuf/LICENSE @@ -1,7 +1,6 @@ -Protocol Buffers for Go with Gadgets - Copyright (c) 2013, The GoGo Authors. All rights reserved. -http://github.com/gogo/protobuf + +Protocol Buffers for Go with Gadgets Go support for Protocol Buffers - Google's data interchange format diff --git a/vendor/github.com/gogo/protobuf/README b/vendor/github.com/gogo/protobuf/README index 035426df559af..6af26af75d843 100644 --- a/vendor/github.com/gogo/protobuf/README +++ b/vendor/github.com/gogo/protobuf/README @@ -1,13 +1,18 @@ -GoGoProtobuf http://github.com/gogo/protobuf extends +Protocol Buffers for Go with Gadgets + +GoGoProtobuf http://github.com/gogo/protobuf extends GoProtobuf http://github.com/golang/protobuf +Copyright (c) 2013, The GoGo Authors. All rights reserved. + + # Go support for Protocol Buffers Google's data interchange format. Copyright 2010 The Go Authors. https://github.com/golang/protobuf -This package and the code it generates requires at least Go 1.4. +This package and the code it generates requires at least Go 1.6. This software implements Go bindings for protocol buffers. For information about protocol buffers themselves, see @@ -24,11 +29,19 @@ To use this software, you must: https://golang.org/doc/install for details or, if you are using gccgo, follow the instructions at https://golang.org/doc/install/gccgo -- Grab the code from the repository and install the proto package. +- Grab the code from the repository and install the `proto` package. The simplest way is to run `go get -u github.com/golang/protobuf/protoc-gen-go`. - The compiler plugin, protoc-gen-go, will be installed in $GOBIN, - defaulting to $GOPATH/bin. It must be in your $PATH for the protocol - compiler, protoc, to find it. + The compiler plugin, `protoc-gen-go`, will be installed in `$GOPATH/bin` + unless `$GOBIN` is set. It must be in your `$PATH` for the protocol + compiler, `protoc`, to find it. +- If you need a particular version of `protoc-gen-go` (e.g., to match your + `proto` package version), one option is + ```shell + GIT_TAG="v1.2.0" # change as needed + go get -d -u github.com/golang/protobuf/protoc-gen-go + git -C "$(go env GOPATH)"/src/github.com/golang/protobuf checkout $GIT_TAG + go install github.com/golang/protobuf/protoc-gen-go + ``` This software has two parts: a 'protocol compiler plugin' that generates Go source files that, once compiled, can access and manage @@ -58,6 +71,45 @@ parameter set to the directory you want to output the Go code to. The generated files will be suffixed .pb.go. See the Test code below for an example using such a file. +## Packages and input paths ## + +The protocol buffer language has a concept of "packages" which does not +correspond well to the Go notion of packages. In generated Go code, +each source `.proto` file is associated with a single Go package. The +name and import path for this package is specified with the `go_package` +proto option: + + option go_package = "github.com/gogo/protobuf/types"; + +The protocol buffer compiler will attempt to derive a package name and +import path if a `go_package` option is not present, but it is +best to always specify one explicitly. + +There is a one-to-one relationship between source `.proto` files and +generated `.pb.go` files, but any number of `.pb.go` files may be +contained in the same Go package. + +The output name of a generated file is produced by replacing the +`.proto` suffix with `.pb.go` (e.g., `foo.proto` produces `foo.pb.go`). +However, the output directory is selected in one of two ways. Let +us say we have `inputs/x.proto` with a `go_package` option of +`github.com/golang/protobuf/p`. The corresponding output file may +be: + +- Relative to the import path: + + protoc --gogo_out=. inputs/x.proto + # writes ./github.com/gogo/protobuf/p/x.pb.go + + (This can work well with `--gogo_out=$GOPATH`.) + +- Relative to the input file: + + protoc --gogo_out=paths=source_relative:. inputs/x.proto + # generate ./inputs/x.pb.go + +## Generated code ## + The package comment for the proto library contains text describing the interface provided in Go for protocol buffers. Here is an edited version. @@ -125,16 +177,13 @@ Consider file test.proto, containing ```proto syntax = "proto2"; package example; - + enum FOO { X = 17; }; - + message Test { required string label = 1; optional int32 type = 2 [default=77]; repeated int64 reps = 3; - optional group OptionalGroup = 4 { - required string RequiredField = 5; - } } ``` @@ -151,13 +200,10 @@ To create and play with a Test object from the example package, ) func main() { - test := &example.Test { + test := &example.Test{ Label: proto.String("hello"), Type: proto.Int32(17), Reps: []int64{1, 2, 3}, - Optionalgroup: &example.Test_OptionalGroup { - RequiredField: proto.String("good bye"), - }, } data, err := proto.Marshal(test) if err != nil { @@ -185,19 +231,23 @@ parameter list separated from the output directory by a colon: protoc --gogo_out=plugins=grpc,import_path=mypackage:. *.proto - -- `import_prefix=xxx` - a prefix that is added onto the beginning of - all imports. Useful for things like generating protos in a - subdirectory, or regenerating vendored protobufs in-place. -- `import_path=foo/bar` - used as the package if no input files - declare `go_package`. If it contains slashes, everything up to the - rightmost slash is ignored. +- `paths=(import | source_relative)` - specifies how the paths of + generated files are structured. See the "Packages and imports paths" + section above. The default is `import`. - `plugins=plugin1+plugin2` - specifies the list of sub-plugins to load. The only plugin in this repo is `grpc`. - `Mfoo/bar.proto=quux/shme` - declares that foo/bar.proto is associated with Go package quux/shme. This is subject to the import_prefix parameter. +The following parameters are deprecated and should not be used: + +- `import_prefix=xxx` - a prefix that is added onto the beginning of + all imports. +- `import_path=foo/bar` - used as the package if no input files + declare `go_package`. If it contains slashes, everything up to the + rightmost slash is ignored. + ## gRPC Support ## If a proto file specifies RPC services, protoc-gen-go can be instructed to @@ -251,8 +301,6 @@ generated code and declare a new package-level constant whose name incorporates the latest version number. Removing a compatibility constant is considered a breaking change and would be subject to the announcement policy stated above. -## Plugins ## - The `protoc-gen-go/generator` package exposes a plugin interface, which is used by the gRPC code generation. This interface is not supported and is subject to incompatible changes without notice. diff --git a/vendor/github.com/gogo/protobuf/Readme.md b/vendor/github.com/gogo/protobuf/Readme.md index a4ad3eecd0656..e11dad70cf092 100644 --- a/vendor/github.com/gogo/protobuf/Readme.md +++ b/vendor/github.com/gogo/protobuf/Readme.md @@ -1,6 +1,9 @@ +[GoGo Protobuf looking for new ownership](https://github.com/gogo/protobuf/issues/691) + # Protocol Buffers for Go with Gadgets -[![Build Status](https://travis-ci.org/gogo/protobuf.svg?branch=master)](https://travis-ci.org/gogo/protobuf) +[![Build Status](https://github.com/gogo/protobuf/workflows/Continuous%20Integration/badge.svg)](https://github.com/gogo/protobuf/actions) +[![GoDoc](https://godoc.org/github.com/gogo/protobuf?status.svg)](http://godoc.org/github.com/gogo/protobuf) gogoprotobuf is a fork of golang/protobuf with extra code generation features. @@ -16,6 +19,18 @@ This code generation is used to achieve: Keeping track of how up to date gogoprotobuf is relative to golang/protobuf is done in this issue +## Release v1.3.0 + +The project has updated to release v1.3.0. Check out the release notes here. + +With this new release comes a new internal library version. This means any newly generated *pb.go files generated with the v1.3.0 library will not be compatible with the old library version (v1.2.1). However, current *pb.go files (generated with v1.2.1) should still work with the new library. + +Please make sure you manage your dependencies correctly when upgrading your project. If you are still using v1.2.1 and you update your dependencies, one of which could include a new *pb.go (generated with v1.3.0), you could get a compile time error. + +Our upstream repo, golang/protobuf, also had to go through this process in order to update their library version. +Here is a link explaining hermetic builds. + + ## Users These projects use gogoprotobuf: @@ -45,6 +60,11 @@ These projects use gogoprotobuf: - carbonzipper stack - sendgrid - zero-os/0-stor + - go-spacemesh + - cortex - sample proto file + - Apache SkyWalking APM - Istio telemetry receiver based on Mixer bypass protocol + - Hyperledger Burrow - a permissioned DLT framework + - IOV Weave - a blockchain framework - sample proto files Please let us know if you are using gogoprotobuf by posting on our GoogleGroup. @@ -53,6 +73,12 @@ Please let us know if you are using gogoprotobuf by posting on our Cloudflare - go serialization talk - Albert Strasheim - GopherCon 2014 Writing High Performance Databases in Go by Ben Johnson - alecthomas' go serialization benchmarks + - Go faster with gogoproto - Agniva De Sarker + - Evolution of protobuf (Gource Visualization) - Landon Wilkins + - Creating GopherJS Apps with gRPC-Web - Johan Brandhorst + - So you want to use GoGo Protobuf - Johan Brandhorst + - Advanced gRPC Error Usage - Johan Brandhorst + - gRPC Golang Course on Udemy - Stephane Maarek ## Getting Started @@ -65,10 +91,11 @@ After that you can choose: ### Installation -To install it, you must first have Go (at least version 1.6.3) installed (see [http://golang.org/doc/install](http://golang.org/doc/install)). Latest patch versions of Go 1.8, 1.9 and 1.10 are continuously tested. +To install it, you must first have Go (at least version 1.6.3 or 1.9 if you are using gRPC) installed (see [http://golang.org/doc/install](http://golang.org/doc/install)). +Latest patch versions of 1.12 and 1.15 are continuously tested. Next, install the standard protocol buffer implementation from [https://github.com/google/protobuf](https://github.com/google/protobuf). -Most versions from 2.3.1 should not give any problems, but 2.6.1, 3.0.2 and 3.5.1 are continuously tested. +Most versions from 2.3.1 should not give any problems, but 2.6.1, 3.0.2 and 3.14.0 are continuously tested. ### Speed @@ -114,7 +141,7 @@ To use proto files from "google/protobuf" you need to add additional args to pro Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types,\ Mgoogle/protobuf/wrappers.proto=github.com/gogo/protobuf/types:. \ myproto.proto - + Note that in the protoc command, {binary} does not contain the initial prefix of "protoc-gen". ### Most Speed and most customization @@ -137,3 +164,12 @@ It works the same as golang/protobuf, simply specify the plugin. Here is an example using gofast: protoc --gofast_out=plugins=grpc:. my.proto + +See [https://github.com/gogo/grpc-example](https://github.com/gogo/grpc-example) for an example of using gRPC with gogoprotobuf and the wider grpc-ecosystem. + + +## License +This software is licensed under the 3-Clause BSD License +("BSD License 2.0", "Revised BSD License", "New BSD License", or "Modified BSD License"). + + diff --git a/vendor/github.com/gogo/protobuf/go.mod b/vendor/github.com/gogo/protobuf/go.mod new file mode 100644 index 0000000000000..001b419c95dad --- /dev/null +++ b/vendor/github.com/gogo/protobuf/go.mod @@ -0,0 +1,9 @@ +module github.com/gogo/protobuf + +go 1.15 + +require ( + github.com/kisielk/errcheck v1.5.0 // indirect + github.com/kisielk/gotool v1.0.0 // indirect + golang.org/x/tools v0.0.0-20210106214847-113979e3529a // indirect +) diff --git a/vendor/github.com/gogo/protobuf/gogoproto/doc.go b/vendor/github.com/gogo/protobuf/gogoproto/doc.go index 147b5ecc62fda..081c86fa8ecba 100644 --- a/vendor/github.com/gogo/protobuf/gogoproto/doc.go +++ b/vendor/github.com/gogo/protobuf/gogoproto/doc.go @@ -162,7 +162,7 @@ The most complete way to see examples is to look at github.com/gogo/protobuf/test/thetest.proto Gogoprototest is a seperate project, -because we want to keep gogoprotobuf independant of goprotobuf, +because we want to keep gogoprotobuf independent of goprotobuf, but we still want to test it thoroughly. */ diff --git a/vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go b/vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go index 5765acb15300d..1e91766aeeae0 100644 --- a/vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go +++ b/vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go @@ -1,20 +1,14 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: gogo.proto -/* -Package gogoproto is a generated protocol buffer package. - -It is generated from these files: - gogo.proto - -It has these top-level messages: -*/ package gogoproto -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -25,46 +19,46 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package var E_GoprotoEnumPrefix = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.EnumOptions)(nil), + ExtendedType: (*descriptor.EnumOptions)(nil), ExtensionType: (*bool)(nil), Field: 62001, Name: "gogoproto.goproto_enum_prefix", - Tag: "varint,62001,opt,name=goproto_enum_prefix,json=goprotoEnumPrefix", + Tag: "varint,62001,opt,name=goproto_enum_prefix", Filename: "gogo.proto", } var E_GoprotoEnumStringer = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.EnumOptions)(nil), + ExtendedType: (*descriptor.EnumOptions)(nil), ExtensionType: (*bool)(nil), Field: 62021, Name: "gogoproto.goproto_enum_stringer", - Tag: "varint,62021,opt,name=goproto_enum_stringer,json=goprotoEnumStringer", + Tag: "varint,62021,opt,name=goproto_enum_stringer", Filename: "gogo.proto", } var E_EnumStringer = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.EnumOptions)(nil), + ExtendedType: (*descriptor.EnumOptions)(nil), ExtensionType: (*bool)(nil), Field: 62022, Name: "gogoproto.enum_stringer", - Tag: "varint,62022,opt,name=enum_stringer,json=enumStringer", + Tag: "varint,62022,opt,name=enum_stringer", Filename: "gogo.proto", } var E_EnumCustomname = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.EnumOptions)(nil), + ExtendedType: (*descriptor.EnumOptions)(nil), ExtensionType: (*string)(nil), Field: 62023, Name: "gogoproto.enum_customname", - Tag: "bytes,62023,opt,name=enum_customname,json=enumCustomname", + Tag: "bytes,62023,opt,name=enum_customname", Filename: "gogo.proto", } var E_Enumdecl = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.EnumOptions)(nil), + ExtendedType: (*descriptor.EnumOptions)(nil), ExtensionType: (*bool)(nil), Field: 62024, Name: "gogoproto.enumdecl", @@ -73,304 +67,331 @@ var E_Enumdecl = &proto.ExtensionDesc{ } var E_EnumvalueCustomname = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.EnumValueOptions)(nil), + ExtendedType: (*descriptor.EnumValueOptions)(nil), ExtensionType: (*string)(nil), Field: 66001, Name: "gogoproto.enumvalue_customname", - Tag: "bytes,66001,opt,name=enumvalue_customname,json=enumvalueCustomname", + Tag: "bytes,66001,opt,name=enumvalue_customname", Filename: "gogo.proto", } var E_GoprotoGettersAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63001, Name: "gogoproto.goproto_getters_all", - Tag: "varint,63001,opt,name=goproto_getters_all,json=goprotoGettersAll", + Tag: "varint,63001,opt,name=goproto_getters_all", Filename: "gogo.proto", } var E_GoprotoEnumPrefixAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63002, Name: "gogoproto.goproto_enum_prefix_all", - Tag: "varint,63002,opt,name=goproto_enum_prefix_all,json=goprotoEnumPrefixAll", + Tag: "varint,63002,opt,name=goproto_enum_prefix_all", Filename: "gogo.proto", } var E_GoprotoStringerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63003, Name: "gogoproto.goproto_stringer_all", - Tag: "varint,63003,opt,name=goproto_stringer_all,json=goprotoStringerAll", + Tag: "varint,63003,opt,name=goproto_stringer_all", Filename: "gogo.proto", } var E_VerboseEqualAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63004, Name: "gogoproto.verbose_equal_all", - Tag: "varint,63004,opt,name=verbose_equal_all,json=verboseEqualAll", + Tag: "varint,63004,opt,name=verbose_equal_all", Filename: "gogo.proto", } var E_FaceAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63005, Name: "gogoproto.face_all", - Tag: "varint,63005,opt,name=face_all,json=faceAll", + Tag: "varint,63005,opt,name=face_all", Filename: "gogo.proto", } var E_GostringAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63006, Name: "gogoproto.gostring_all", - Tag: "varint,63006,opt,name=gostring_all,json=gostringAll", + Tag: "varint,63006,opt,name=gostring_all", Filename: "gogo.proto", } var E_PopulateAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63007, Name: "gogoproto.populate_all", - Tag: "varint,63007,opt,name=populate_all,json=populateAll", + Tag: "varint,63007,opt,name=populate_all", Filename: "gogo.proto", } var E_StringerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63008, Name: "gogoproto.stringer_all", - Tag: "varint,63008,opt,name=stringer_all,json=stringerAll", + Tag: "varint,63008,opt,name=stringer_all", Filename: "gogo.proto", } var E_OnlyoneAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63009, Name: "gogoproto.onlyone_all", - Tag: "varint,63009,opt,name=onlyone_all,json=onlyoneAll", + Tag: "varint,63009,opt,name=onlyone_all", Filename: "gogo.proto", } var E_EqualAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63013, Name: "gogoproto.equal_all", - Tag: "varint,63013,opt,name=equal_all,json=equalAll", + Tag: "varint,63013,opt,name=equal_all", Filename: "gogo.proto", } var E_DescriptionAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63014, Name: "gogoproto.description_all", - Tag: "varint,63014,opt,name=description_all,json=descriptionAll", + Tag: "varint,63014,opt,name=description_all", Filename: "gogo.proto", } var E_TestgenAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63015, Name: "gogoproto.testgen_all", - Tag: "varint,63015,opt,name=testgen_all,json=testgenAll", + Tag: "varint,63015,opt,name=testgen_all", Filename: "gogo.proto", } var E_BenchgenAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63016, Name: "gogoproto.benchgen_all", - Tag: "varint,63016,opt,name=benchgen_all,json=benchgenAll", + Tag: "varint,63016,opt,name=benchgen_all", Filename: "gogo.proto", } var E_MarshalerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63017, Name: "gogoproto.marshaler_all", - Tag: "varint,63017,opt,name=marshaler_all,json=marshalerAll", + Tag: "varint,63017,opt,name=marshaler_all", Filename: "gogo.proto", } var E_UnmarshalerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63018, Name: "gogoproto.unmarshaler_all", - Tag: "varint,63018,opt,name=unmarshaler_all,json=unmarshalerAll", + Tag: "varint,63018,opt,name=unmarshaler_all", Filename: "gogo.proto", } var E_StableMarshalerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63019, Name: "gogoproto.stable_marshaler_all", - Tag: "varint,63019,opt,name=stable_marshaler_all,json=stableMarshalerAll", + Tag: "varint,63019,opt,name=stable_marshaler_all", Filename: "gogo.proto", } var E_SizerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63020, Name: "gogoproto.sizer_all", - Tag: "varint,63020,opt,name=sizer_all,json=sizerAll", + Tag: "varint,63020,opt,name=sizer_all", Filename: "gogo.proto", } var E_GoprotoEnumStringerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63021, Name: "gogoproto.goproto_enum_stringer_all", - Tag: "varint,63021,opt,name=goproto_enum_stringer_all,json=goprotoEnumStringerAll", + Tag: "varint,63021,opt,name=goproto_enum_stringer_all", Filename: "gogo.proto", } var E_EnumStringerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63022, Name: "gogoproto.enum_stringer_all", - Tag: "varint,63022,opt,name=enum_stringer_all,json=enumStringerAll", + Tag: "varint,63022,opt,name=enum_stringer_all", Filename: "gogo.proto", } var E_UnsafeMarshalerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63023, Name: "gogoproto.unsafe_marshaler_all", - Tag: "varint,63023,opt,name=unsafe_marshaler_all,json=unsafeMarshalerAll", + Tag: "varint,63023,opt,name=unsafe_marshaler_all", Filename: "gogo.proto", } var E_UnsafeUnmarshalerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63024, Name: "gogoproto.unsafe_unmarshaler_all", - Tag: "varint,63024,opt,name=unsafe_unmarshaler_all,json=unsafeUnmarshalerAll", + Tag: "varint,63024,opt,name=unsafe_unmarshaler_all", Filename: "gogo.proto", } var E_GoprotoExtensionsMapAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63025, Name: "gogoproto.goproto_extensions_map_all", - Tag: "varint,63025,opt,name=goproto_extensions_map_all,json=goprotoExtensionsMapAll", + Tag: "varint,63025,opt,name=goproto_extensions_map_all", Filename: "gogo.proto", } var E_GoprotoUnrecognizedAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63026, Name: "gogoproto.goproto_unrecognized_all", - Tag: "varint,63026,opt,name=goproto_unrecognized_all,json=goprotoUnrecognizedAll", + Tag: "varint,63026,opt,name=goproto_unrecognized_all", Filename: "gogo.proto", } var E_GogoprotoImport = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63027, Name: "gogoproto.gogoproto_import", - Tag: "varint,63027,opt,name=gogoproto_import,json=gogoprotoImport", + Tag: "varint,63027,opt,name=gogoproto_import", Filename: "gogo.proto", } var E_ProtosizerAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63028, Name: "gogoproto.protosizer_all", - Tag: "varint,63028,opt,name=protosizer_all,json=protosizerAll", + Tag: "varint,63028,opt,name=protosizer_all", Filename: "gogo.proto", } var E_CompareAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63029, Name: "gogoproto.compare_all", - Tag: "varint,63029,opt,name=compare_all,json=compareAll", + Tag: "varint,63029,opt,name=compare_all", Filename: "gogo.proto", } var E_TypedeclAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63030, Name: "gogoproto.typedecl_all", - Tag: "varint,63030,opt,name=typedecl_all,json=typedeclAll", + Tag: "varint,63030,opt,name=typedecl_all", Filename: "gogo.proto", } var E_EnumdeclAll = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63031, Name: "gogoproto.enumdecl_all", - Tag: "varint,63031,opt,name=enumdecl_all,json=enumdeclAll", + Tag: "varint,63031,opt,name=enumdecl_all", Filename: "gogo.proto", } var E_GoprotoRegistration = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FileOptions)(nil), + ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 63032, Name: "gogoproto.goproto_registration", - Tag: "varint,63032,opt,name=goproto_registration,json=goprotoRegistration", + Tag: "varint,63032,opt,name=goproto_registration", + Filename: "gogo.proto", +} + +var E_MessagenameAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63033, + Name: "gogoproto.messagename_all", + Tag: "varint,63033,opt,name=messagename_all", + Filename: "gogo.proto", +} + +var E_GoprotoSizecacheAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63034, + Name: "gogoproto.goproto_sizecache_all", + Tag: "varint,63034,opt,name=goproto_sizecache_all", + Filename: "gogo.proto", +} + +var E_GoprotoUnkeyedAll = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FileOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 63035, + Name: "gogoproto.goproto_unkeyed_all", + Tag: "varint,63035,opt,name=goproto_unkeyed_all", Filename: "gogo.proto", } var E_GoprotoGetters = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64001, Name: "gogoproto.goproto_getters", - Tag: "varint,64001,opt,name=goproto_getters,json=goprotoGetters", + Tag: "varint,64001,opt,name=goproto_getters", Filename: "gogo.proto", } var E_GoprotoStringer = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64003, Name: "gogoproto.goproto_stringer", - Tag: "varint,64003,opt,name=goproto_stringer,json=goprotoStringer", + Tag: "varint,64003,opt,name=goproto_stringer", Filename: "gogo.proto", } var E_VerboseEqual = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64004, Name: "gogoproto.verbose_equal", - Tag: "varint,64004,opt,name=verbose_equal,json=verboseEqual", + Tag: "varint,64004,opt,name=verbose_equal", Filename: "gogo.proto", } var E_Face = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64005, Name: "gogoproto.face", @@ -379,7 +400,7 @@ var E_Face = &proto.ExtensionDesc{ } var E_Gostring = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64006, Name: "gogoproto.gostring", @@ -388,7 +409,7 @@ var E_Gostring = &proto.ExtensionDesc{ } var E_Populate = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64007, Name: "gogoproto.populate", @@ -397,7 +418,7 @@ var E_Populate = &proto.ExtensionDesc{ } var E_Stringer = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 67008, Name: "gogoproto.stringer", @@ -406,7 +427,7 @@ var E_Stringer = &proto.ExtensionDesc{ } var E_Onlyone = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64009, Name: "gogoproto.onlyone", @@ -415,7 +436,7 @@ var E_Onlyone = &proto.ExtensionDesc{ } var E_Equal = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64013, Name: "gogoproto.equal", @@ -424,7 +445,7 @@ var E_Equal = &proto.ExtensionDesc{ } var E_Description = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64014, Name: "gogoproto.description", @@ -433,7 +454,7 @@ var E_Description = &proto.ExtensionDesc{ } var E_Testgen = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64015, Name: "gogoproto.testgen", @@ -442,7 +463,7 @@ var E_Testgen = &proto.ExtensionDesc{ } var E_Benchgen = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64016, Name: "gogoproto.benchgen", @@ -451,7 +472,7 @@ var E_Benchgen = &proto.ExtensionDesc{ } var E_Marshaler = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64017, Name: "gogoproto.marshaler", @@ -460,7 +481,7 @@ var E_Marshaler = &proto.ExtensionDesc{ } var E_Unmarshaler = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64018, Name: "gogoproto.unmarshaler", @@ -469,16 +490,16 @@ var E_Unmarshaler = &proto.ExtensionDesc{ } var E_StableMarshaler = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64019, Name: "gogoproto.stable_marshaler", - Tag: "varint,64019,opt,name=stable_marshaler,json=stableMarshaler", + Tag: "varint,64019,opt,name=stable_marshaler", Filename: "gogo.proto", } var E_Sizer = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64020, Name: "gogoproto.sizer", @@ -487,43 +508,43 @@ var E_Sizer = &proto.ExtensionDesc{ } var E_UnsafeMarshaler = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64023, Name: "gogoproto.unsafe_marshaler", - Tag: "varint,64023,opt,name=unsafe_marshaler,json=unsafeMarshaler", + Tag: "varint,64023,opt,name=unsafe_marshaler", Filename: "gogo.proto", } var E_UnsafeUnmarshaler = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64024, Name: "gogoproto.unsafe_unmarshaler", - Tag: "varint,64024,opt,name=unsafe_unmarshaler,json=unsafeUnmarshaler", + Tag: "varint,64024,opt,name=unsafe_unmarshaler", Filename: "gogo.proto", } var E_GoprotoExtensionsMap = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64025, Name: "gogoproto.goproto_extensions_map", - Tag: "varint,64025,opt,name=goproto_extensions_map,json=goprotoExtensionsMap", + Tag: "varint,64025,opt,name=goproto_extensions_map", Filename: "gogo.proto", } var E_GoprotoUnrecognized = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64026, Name: "gogoproto.goproto_unrecognized", - Tag: "varint,64026,opt,name=goproto_unrecognized,json=goprotoUnrecognized", + Tag: "varint,64026,opt,name=goproto_unrecognized", Filename: "gogo.proto", } var E_Protosizer = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64028, Name: "gogoproto.protosizer", @@ -532,7 +553,7 @@ var E_Protosizer = &proto.ExtensionDesc{ } var E_Compare = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64029, Name: "gogoproto.compare", @@ -541,7 +562,7 @@ var E_Compare = &proto.ExtensionDesc{ } var E_Typedecl = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.MessageOptions)(nil), + ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 64030, Name: "gogoproto.typedecl", @@ -549,8 +570,35 @@ var E_Typedecl = &proto.ExtensionDesc{ Filename: "gogo.proto", } +var E_Messagename = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64033, + Name: "gogoproto.messagename", + Tag: "varint,64033,opt,name=messagename", + Filename: "gogo.proto", +} + +var E_GoprotoSizecache = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64034, + Name: "gogoproto.goproto_sizecache", + Tag: "varint,64034,opt,name=goproto_sizecache", + Filename: "gogo.proto", +} + +var E_GoprotoUnkeyed = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 64035, + Name: "gogoproto.goproto_unkeyed", + Tag: "varint,64035,opt,name=goproto_unkeyed", + Filename: "gogo.proto", +} + var E_Nullable = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*bool)(nil), Field: 65001, Name: "gogoproto.nullable", @@ -559,7 +607,7 @@ var E_Nullable = &proto.ExtensionDesc{ } var E_Embed = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*bool)(nil), Field: 65002, Name: "gogoproto.embed", @@ -568,7 +616,7 @@ var E_Embed = &proto.ExtensionDesc{ } var E_Customtype = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*string)(nil), Field: 65003, Name: "gogoproto.customtype", @@ -577,7 +625,7 @@ var E_Customtype = &proto.ExtensionDesc{ } var E_Customname = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*string)(nil), Field: 65004, Name: "gogoproto.customname", @@ -586,7 +634,7 @@ var E_Customname = &proto.ExtensionDesc{ } var E_Jsontag = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*string)(nil), Field: 65005, Name: "gogoproto.jsontag", @@ -595,7 +643,7 @@ var E_Jsontag = &proto.ExtensionDesc{ } var E_Moretags = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*string)(nil), Field: 65006, Name: "gogoproto.moretags", @@ -604,7 +652,7 @@ var E_Moretags = &proto.ExtensionDesc{ } var E_Casttype = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*string)(nil), Field: 65007, Name: "gogoproto.casttype", @@ -613,7 +661,7 @@ var E_Casttype = &proto.ExtensionDesc{ } var E_Castkey = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*string)(nil), Field: 65008, Name: "gogoproto.castkey", @@ -622,7 +670,7 @@ var E_Castkey = &proto.ExtensionDesc{ } var E_Castvalue = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*string)(nil), Field: 65009, Name: "gogoproto.castvalue", @@ -631,7 +679,7 @@ var E_Castvalue = &proto.ExtensionDesc{ } var E_Stdtime = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*bool)(nil), Field: 65010, Name: "gogoproto.stdtime", @@ -640,7 +688,7 @@ var E_Stdtime = &proto.ExtensionDesc{ } var E_Stdduration = &proto.ExtensionDesc{ - ExtendedType: (*google_protobuf.FieldOptions)(nil), + ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*bool)(nil), Field: 65011, Name: "gogoproto.stdduration", @@ -648,6 +696,15 @@ var E_Stdduration = &proto.ExtensionDesc{ Filename: "gogo.proto", } +var E_Wktpointer = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 65012, + Name: "gogoproto.wktpointer", + Tag: "varint,65012,opt,name=wktpointer", + Filename: "gogo.proto", +} + func init() { proto.RegisterExtension(E_GoprotoEnumPrefix) proto.RegisterExtension(E_GoprotoEnumStringer) @@ -684,6 +741,9 @@ func init() { proto.RegisterExtension(E_TypedeclAll) proto.RegisterExtension(E_EnumdeclAll) proto.RegisterExtension(E_GoprotoRegistration) + proto.RegisterExtension(E_MessagenameAll) + proto.RegisterExtension(E_GoprotoSizecacheAll) + proto.RegisterExtension(E_GoprotoUnkeyedAll) proto.RegisterExtension(E_GoprotoGetters) proto.RegisterExtension(E_GoprotoStringer) proto.RegisterExtension(E_VerboseEqual) @@ -707,6 +767,9 @@ func init() { proto.RegisterExtension(E_Protosizer) proto.RegisterExtension(E_Compare) proto.RegisterExtension(E_Typedecl) + proto.RegisterExtension(E_Messagename) + proto.RegisterExtension(E_GoprotoSizecache) + proto.RegisterExtension(E_GoprotoUnkeyed) proto.RegisterExtension(E_Nullable) proto.RegisterExtension(E_Embed) proto.RegisterExtension(E_Customtype) @@ -718,87 +781,94 @@ func init() { proto.RegisterExtension(E_Castvalue) proto.RegisterExtension(E_Stdtime) proto.RegisterExtension(E_Stdduration) -} - -func init() { proto.RegisterFile("gogo.proto", fileDescriptorGogo) } - -var fileDescriptorGogo = []byte{ - // 1220 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x98, 0x4b, 0x6f, 0x1c, 0x45, - 0x10, 0x80, 0x85, 0x48, 0x14, 0x6f, 0xd9, 0x8e, 0xf1, 0xda, 0x98, 0x10, 0x81, 0x08, 0x9c, 0x38, - 0xd9, 0xa7, 0x08, 0xa5, 0xad, 0xc8, 0x72, 0x2c, 0xc7, 0x4a, 0x84, 0xc1, 0x98, 0x38, 0xbc, 0x0e, - 0xab, 0xd9, 0xdd, 0xf6, 0x78, 0x60, 0x66, 0x7a, 0x98, 0xe9, 0x89, 0xe2, 0xdc, 0x50, 0x78, 0x08, - 0x21, 0xde, 0x48, 0x90, 0x90, 0x04, 0x38, 0xf0, 0x7e, 0x86, 0xf7, 0x91, 0x0b, 0x8f, 0x2b, 0xff, - 0x81, 0x0b, 0x60, 0xde, 0xbe, 0xf9, 0x82, 0x6a, 0xb6, 0x6a, 0xb6, 0x67, 0xbd, 0x52, 0xf7, 0xde, - 0xc6, 0xeb, 0xfe, 0xbe, 0xad, 0xa9, 0x9a, 0xae, 0xea, 0x59, 0x00, 0x5f, 0xf9, 0x6a, 0x3a, 0x49, - 0x95, 0x56, 0xf5, 0x1a, 0x5e, 0x17, 0x97, 0x07, 0x0f, 0xf9, 0x4a, 0xf9, 0xa1, 0x9c, 0x29, 0xfe, - 0x6a, 0xe6, 0xeb, 0x33, 0x6d, 0x99, 0xb5, 0xd2, 0x20, 0xd1, 0x2a, 0xed, 0x2c, 0x16, 0x77, 0xc1, - 0x04, 0x2d, 0x6e, 0xc8, 0x38, 0x8f, 0x1a, 0x49, 0x2a, 0xd7, 0x83, 0xb3, 0xf5, 0x9b, 0xa6, 0x3b, - 0xe4, 0x34, 0x93, 0xd3, 0x8b, 0x71, 0x1e, 0xdd, 0x9d, 0xe8, 0x40, 0xc5, 0xd9, 0x81, 0xab, 0xbf, - 0x5c, 0x7b, 0xe8, 0x9a, 0xdb, 0x87, 0x56, 0xc7, 0x09, 0xc5, 0xff, 0xad, 0x14, 0xa0, 0x58, 0x85, - 0xeb, 0x2b, 0xbe, 0x4c, 0xa7, 0x41, 0xec, 0xcb, 0xd4, 0x62, 0xfc, 0x9e, 0x8c, 0x13, 0x86, 0xf1, - 0x5e, 0x42, 0xc5, 0x02, 0x8c, 0x0e, 0xe2, 0xfa, 0x81, 0x5c, 0x23, 0xd2, 0x94, 0x2c, 0xc1, 0x58, - 0x21, 0x69, 0xe5, 0x99, 0x56, 0x51, 0xec, 0x45, 0xd2, 0xa2, 0xf9, 0xb1, 0xd0, 0xd4, 0x56, 0xf7, - 0x23, 0xb6, 0x50, 0x52, 0x42, 0xc0, 0x10, 0x7e, 0xd2, 0x96, 0xad, 0xd0, 0x62, 0xf8, 0x89, 0x02, - 0x29, 0xd7, 0x8b, 0xd3, 0x30, 0x89, 0xd7, 0x67, 0xbc, 0x30, 0x97, 0x66, 0x24, 0xb7, 0xf6, 0xf5, - 0x9c, 0xc6, 0x65, 0x2c, 0xfb, 0xf9, 0xfc, 0x9e, 0x22, 0x9c, 0x89, 0x52, 0x60, 0xc4, 0x64, 0x54, - 0xd1, 0x97, 0x5a, 0xcb, 0x34, 0x6b, 0x78, 0x61, 0xbf, 0xf0, 0x8e, 0x07, 0x61, 0x69, 0xbc, 0xb0, - 0x55, 0xad, 0xe2, 0x52, 0x87, 0x9c, 0x0f, 0x43, 0xb1, 0x06, 0x37, 0xf4, 0x79, 0x2a, 0x1c, 0x9c, - 0x17, 0xc9, 0x39, 0xb9, 0xeb, 0xc9, 0x40, 0xed, 0x0a, 0xf0, 0xe7, 0x65, 0x2d, 0x1d, 0x9c, 0xaf, - 0x93, 0xb3, 0x4e, 0x2c, 0x97, 0x14, 0x8d, 0x27, 0x61, 0xfc, 0x8c, 0x4c, 0x9b, 0x2a, 0x93, 0x0d, - 0xf9, 0x68, 0xee, 0x85, 0x0e, 0xba, 0x4b, 0xa4, 0x1b, 0x23, 0x70, 0x11, 0x39, 0x74, 0x1d, 0x81, - 0xa1, 0x75, 0xaf, 0x25, 0x1d, 0x14, 0x97, 0x49, 0xb1, 0x0f, 0xd7, 0x23, 0x3a, 0x0f, 0x23, 0xbe, - 0xea, 0xdc, 0x92, 0x03, 0x7e, 0x85, 0xf0, 0x61, 0x66, 0x48, 0x91, 0xa8, 0x24, 0x0f, 0x3d, 0xed, - 0x12, 0xc1, 0x1b, 0xac, 0x60, 0x86, 0x14, 0x03, 0xa4, 0xf5, 0x4d, 0x56, 0x64, 0x46, 0x3e, 0xe7, - 0x60, 0x58, 0xc5, 0xe1, 0xa6, 0x8a, 0x5d, 0x82, 0x78, 0x8b, 0x0c, 0x40, 0x08, 0x0a, 0x66, 0xa1, - 0xe6, 0x5a, 0x88, 0xb7, 0xb7, 0x78, 0x7b, 0x70, 0x05, 0x96, 0x60, 0x8c, 0x1b, 0x54, 0xa0, 0x62, - 0x07, 0xc5, 0x3b, 0xa4, 0xd8, 0x6f, 0x60, 0x74, 0x1b, 0x5a, 0x66, 0xda, 0x97, 0x2e, 0x92, 0x77, - 0xf9, 0x36, 0x08, 0xa1, 0x54, 0x36, 0x65, 0xdc, 0xda, 0x70, 0x33, 0xbc, 0xc7, 0xa9, 0x64, 0x06, - 0x15, 0x0b, 0x30, 0x1a, 0x79, 0x69, 0xb6, 0xe1, 0x85, 0x4e, 0xe5, 0x78, 0x9f, 0x1c, 0x23, 0x25, - 0x44, 0x19, 0xc9, 0xe3, 0x41, 0x34, 0x1f, 0x70, 0x46, 0x0c, 0x8c, 0xb6, 0x5e, 0xa6, 0xbd, 0x66, - 0x28, 0x1b, 0x83, 0xd8, 0x3e, 0xe4, 0xad, 0xd7, 0x61, 0x97, 0x4d, 0xe3, 0x2c, 0xd4, 0xb2, 0xe0, - 0x9c, 0x93, 0xe6, 0x23, 0xae, 0x74, 0x01, 0x20, 0xfc, 0x00, 0xdc, 0xd8, 0x77, 0x4c, 0x38, 0xc8, - 0x3e, 0x26, 0xd9, 0x54, 0x9f, 0x51, 0x41, 0x2d, 0x61, 0x50, 0xe5, 0x27, 0xdc, 0x12, 0x64, 0x8f, - 0x6b, 0x05, 0x26, 0xf3, 0x38, 0xf3, 0xd6, 0x07, 0xcb, 0xda, 0xa7, 0x9c, 0xb5, 0x0e, 0x5b, 0xc9, - 0xda, 0x29, 0x98, 0x22, 0xe3, 0x60, 0x75, 0xfd, 0x8c, 0x1b, 0x6b, 0x87, 0x5e, 0xab, 0x56, 0xf7, - 0x21, 0x38, 0x58, 0xa6, 0xf3, 0xac, 0x96, 0x71, 0x86, 0x4c, 0x23, 0xf2, 0x12, 0x07, 0xf3, 0x55, - 0x32, 0x73, 0xc7, 0x5f, 0x2c, 0x05, 0xcb, 0x5e, 0x82, 0xf2, 0xfb, 0xe1, 0x00, 0xcb, 0xf3, 0x38, - 0x95, 0x2d, 0xe5, 0xc7, 0xc1, 0x39, 0xd9, 0x76, 0x50, 0x7f, 0xde, 0x53, 0xaa, 0x35, 0x03, 0x47, - 0xf3, 0x09, 0xb8, 0xae, 0x3c, 0xab, 0x34, 0x82, 0x28, 0x51, 0xa9, 0xb6, 0x18, 0xbf, 0xe0, 0x4a, - 0x95, 0xdc, 0x89, 0x02, 0x13, 0x8b, 0xb0, 0xbf, 0xf8, 0xd3, 0xf5, 0x91, 0xfc, 0x92, 0x44, 0xa3, - 0x5d, 0x8a, 0x1a, 0x47, 0x4b, 0x45, 0x89, 0x97, 0xba, 0xf4, 0xbf, 0xaf, 0xb8, 0x71, 0x10, 0x42, - 0x8d, 0x43, 0x6f, 0x26, 0x12, 0xa7, 0xbd, 0x83, 0xe1, 0x6b, 0x6e, 0x1c, 0xcc, 0x90, 0x82, 0x0f, - 0x0c, 0x0e, 0x8a, 0x6f, 0x58, 0xc1, 0x0c, 0x2a, 0xee, 0xe9, 0x0e, 0xda, 0x54, 0xfa, 0x41, 0xa6, - 0x53, 0x0f, 0x57, 0x5b, 0x54, 0xdf, 0x6e, 0x55, 0x0f, 0x61, 0xab, 0x06, 0x2a, 0x4e, 0xc2, 0x58, - 0xcf, 0x11, 0xa3, 0x7e, 0xcb, 0x2e, 0xdb, 0xb2, 0xcc, 0x32, 0xcf, 0x2f, 0x85, 0x8f, 0x6d, 0x53, - 0x33, 0xaa, 0x9e, 0x30, 0xc4, 0x9d, 0x58, 0xf7, 0xea, 0x39, 0xc0, 0x2e, 0x3b, 0xbf, 0x5d, 0x96, - 0xbe, 0x72, 0x0c, 0x10, 0xc7, 0x61, 0xb4, 0x72, 0x06, 0xb0, 0xab, 0x1e, 0x27, 0xd5, 0x88, 0x79, - 0x04, 0x10, 0x87, 0x61, 0x0f, 0xce, 0x73, 0x3b, 0xfe, 0x04, 0xe1, 0xc5, 0x72, 0x71, 0x14, 0x86, - 0x78, 0x8e, 0xdb, 0xd1, 0x27, 0x09, 0x2d, 0x11, 0xc4, 0x79, 0x86, 0xdb, 0xf1, 0xa7, 0x18, 0x67, - 0x04, 0x71, 0xf7, 0x14, 0x7e, 0xf7, 0xcc, 0x1e, 0xea, 0xc3, 0x9c, 0xbb, 0x59, 0xd8, 0x47, 0xc3, - 0xdb, 0x4e, 0x3f, 0x4d, 0x5f, 0xce, 0x84, 0xb8, 0x03, 0xf6, 0x3a, 0x26, 0xfc, 0x59, 0x42, 0x3b, - 0xeb, 0xc5, 0x02, 0x0c, 0x1b, 0x03, 0xdb, 0x8e, 0x3f, 0x47, 0xb8, 0x49, 0x61, 0xe8, 0x34, 0xb0, - 0xed, 0x82, 0xe7, 0x39, 0x74, 0x22, 0x30, 0x6d, 0x3c, 0xab, 0xed, 0xf4, 0x0b, 0x9c, 0x75, 0x46, - 0xc4, 0x1c, 0xd4, 0xca, 0xfe, 0x6b, 0xe7, 0x5f, 0x24, 0xbe, 0xcb, 0x60, 0x06, 0x8c, 0xfe, 0x6f, - 0x57, 0xbc, 0xc4, 0x19, 0x30, 0x28, 0xdc, 0x46, 0xbd, 0x33, 0xdd, 0x6e, 0x7a, 0x99, 0xb7, 0x51, - 0xcf, 0x48, 0xc7, 0x6a, 0x16, 0x6d, 0xd0, 0xae, 0x78, 0x85, 0xab, 0x59, 0xac, 0xc7, 0x30, 0x7a, - 0x87, 0xa4, 0xdd, 0xf1, 0x2a, 0x87, 0xd1, 0x33, 0x23, 0xc5, 0x0a, 0xd4, 0x77, 0x0f, 0x48, 0xbb, - 0xef, 0x35, 0xf2, 0x8d, 0xef, 0x9a, 0x8f, 0xe2, 0x3e, 0x98, 0xea, 0x3f, 0x1c, 0xed, 0xd6, 0x0b, - 0xdb, 0x3d, 0xaf, 0x33, 0xe6, 0x6c, 0x14, 0xa7, 0xba, 0x5d, 0xd6, 0x1c, 0x8c, 0x76, 0xed, 0xc5, - 0xed, 0x6a, 0xa3, 0x35, 0xe7, 0xa2, 0x98, 0x07, 0xe8, 0xce, 0x24, 0xbb, 0xeb, 0x12, 0xb9, 0x0c, - 0x08, 0xb7, 0x06, 0x8d, 0x24, 0x3b, 0x7f, 0x99, 0xb7, 0x06, 0x11, 0xb8, 0x35, 0x78, 0x1a, 0xd9, - 0xe9, 0x2b, 0xbc, 0x35, 0x18, 0x11, 0xb3, 0x30, 0x14, 0xe7, 0x61, 0x88, 0xcf, 0x56, 0xfd, 0xe6, - 0x3e, 0xe3, 0x46, 0x86, 0x6d, 0x86, 0x7f, 0xdd, 0x21, 0x98, 0x01, 0x71, 0x18, 0xf6, 0xca, 0xa8, - 0x29, 0xdb, 0x36, 0xf2, 0xb7, 0x1d, 0xee, 0x27, 0xb8, 0x5a, 0xcc, 0x01, 0x74, 0x5e, 0xa6, 0x31, - 0x0a, 0x1b, 0xfb, 0xfb, 0x4e, 0xe7, 0xbd, 0xde, 0x40, 0xba, 0x82, 0xe2, 0x6d, 0xdc, 0x22, 0xd8, - 0xaa, 0x0a, 0x8a, 0x17, 0xf0, 0x23, 0xb0, 0xef, 0xe1, 0x4c, 0xc5, 0xda, 0xf3, 0x6d, 0xf4, 0x1f, - 0x44, 0xf3, 0x7a, 0x4c, 0x58, 0xa4, 0x52, 0xa9, 0x3d, 0x3f, 0xb3, 0xb1, 0x7f, 0x12, 0x5b, 0x02, - 0x08, 0xb7, 0xbc, 0x4c, 0xbb, 0xdc, 0xf7, 0x5f, 0x0c, 0x33, 0x80, 0x41, 0xe3, 0xf5, 0x23, 0x72, - 0xd3, 0xc6, 0xfe, 0xcd, 0x41, 0xd3, 0x7a, 0x71, 0x14, 0x6a, 0x78, 0x59, 0xfc, 0x0e, 0x61, 0x83, - 0xff, 0x21, 0xb8, 0x4b, 0xe0, 0x37, 0x67, 0xba, 0xad, 0x03, 0x7b, 0xb2, 0xff, 0xa5, 0x4a, 0xf3, - 0x7a, 0x31, 0x0f, 0xc3, 0x99, 0x6e, 0xb7, 0x73, 0x3a, 0xd1, 0x58, 0xf0, 0xff, 0x76, 0xca, 0x97, - 0xdc, 0x92, 0x39, 0xb6, 0x08, 0x13, 0x2d, 0x15, 0xf5, 0x82, 0xc7, 0x60, 0x49, 0x2d, 0xa9, 0x95, - 0x62, 0x17, 0x3d, 0x78, 0x9b, 0x1f, 0xe8, 0x8d, 0xbc, 0x39, 0xdd, 0x52, 0xd1, 0x0c, 0x1e, 0x35, - 0xbb, 0xbf, 0xa0, 0x95, 0x07, 0xcf, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0xed, 0x5f, 0x6c, 0x20, - 0x74, 0x13, 0x00, 0x00, + proto.RegisterExtension(E_Wktpointer) +} + +func init() { proto.RegisterFile("gogo.proto", fileDescriptor_592445b5231bc2b9) } + +var fileDescriptor_592445b5231bc2b9 = []byte{ + // 1328 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x98, 0x49, 0x6f, 0x1c, 0x45, + 0x14, 0x80, 0x85, 0x48, 0x64, 0x4f, 0x79, 0x8b, 0xc7, 0xc6, 0x84, 0x08, 0x44, 0xe0, 0xc4, 0xc9, + 0x3e, 0x45, 0x28, 0x65, 0x45, 0x96, 0x63, 0x39, 0x56, 0x10, 0x0e, 0xc6, 0x89, 0xc3, 0x76, 0x18, + 0xf5, 0xf4, 0x94, 0xdb, 0x8d, 0xbb, 0xbb, 0x9a, 0xee, 0xea, 0x10, 0xe7, 0x86, 0xc2, 0x22, 0x84, + 0xd8, 0x91, 0x20, 0x21, 0x09, 0x04, 0xc4, 0xbe, 0x86, 0x7d, 0xb9, 0x70, 0x61, 0xb9, 0xf2, 0x1f, + 0xb8, 0x00, 0x66, 0xf7, 0xcd, 0x17, 0xf4, 0xba, 0xdf, 0xeb, 0xa9, 0x69, 0x8f, 0x54, 0x35, 0xb7, + 0xf6, 0xb8, 0xbe, 0x6f, 0xaa, 0xdf, 0xeb, 0x7a, 0xef, 0x4d, 0x33, 0xe6, 0x49, 0x4f, 0x4e, 0xc6, + 0x89, 0x54, 0xb2, 0x5e, 0x83, 0xeb, 0xfc, 0x72, 0xdf, 0x7e, 0x4f, 0x4a, 0x2f, 0x10, 0x53, 0xf9, + 0x5f, 0xcd, 0x6c, 0x75, 0xaa, 0x25, 0x52, 0x37, 0xf1, 0x63, 0x25, 0x93, 0x62, 0x31, 0x3f, 0xc6, + 0xc6, 0x70, 0x71, 0x43, 0x44, 0x59, 0xd8, 0x88, 0x13, 0xb1, 0xea, 0x9f, 0xae, 0x5f, 0x3f, 0x59, + 0x90, 0x93, 0x44, 0x4e, 0xce, 0x47, 0x59, 0x78, 0x47, 0xac, 0x7c, 0x19, 0xa5, 0x7b, 0xaf, 0xfc, + 0x72, 0xf5, 0xfe, 0xab, 0x6e, 0xe9, 0x5f, 0x1e, 0x45, 0x14, 0xfe, 0xb7, 0x94, 0x83, 0x7c, 0x99, + 0x5d, 0xd3, 0xe1, 0x4b, 0x55, 0xe2, 0x47, 0x9e, 0x48, 0x0c, 0xc6, 0xef, 0xd1, 0x38, 0xa6, 0x19, + 0x8f, 0x23, 0xca, 0xe7, 0xd8, 0x50, 0x2f, 0xae, 0x1f, 0xd0, 0x35, 0x28, 0x74, 0xc9, 0x02, 0x1b, + 0xc9, 0x25, 0x6e, 0x96, 0x2a, 0x19, 0x46, 0x4e, 0x28, 0x0c, 0x9a, 0x1f, 0x73, 0x4d, 0x6d, 0x79, + 0x18, 0xb0, 0xb9, 0x92, 0xe2, 0x9c, 0xf5, 0xc3, 0x27, 0x2d, 0xe1, 0x06, 0x06, 0xc3, 0x4f, 0xb8, + 0x91, 0x72, 0x3d, 0x3f, 0xc9, 0xc6, 0xe1, 0xfa, 0x94, 0x13, 0x64, 0x42, 0xdf, 0xc9, 0x4d, 0x5d, + 0x3d, 0x27, 0x61, 0x19, 0xc9, 0x7e, 0x3e, 0xbb, 0x2b, 0xdf, 0xce, 0x58, 0x29, 0xd0, 0xf6, 0xa4, + 0x65, 0xd1, 0x13, 0x4a, 0x89, 0x24, 0x6d, 0x38, 0x41, 0xb7, 0xed, 0x1d, 0xf1, 0x83, 0xd2, 0x78, + 0x6e, 0xb3, 0x33, 0x8b, 0x0b, 0x05, 0x39, 0x1b, 0x04, 0x7c, 0x85, 0x5d, 0xdb, 0xe5, 0xa9, 0xb0, + 0x70, 0x9e, 0x47, 0xe7, 0xf8, 0x8e, 0x27, 0x03, 0xb4, 0x4b, 0x8c, 0x3e, 0x2f, 0x73, 0x69, 0xe1, + 0x7c, 0x19, 0x9d, 0x75, 0x64, 0x29, 0xa5, 0x60, 0xbc, 0x8d, 0x8d, 0x9e, 0x12, 0x49, 0x53, 0xa6, + 0xa2, 0x21, 0x1e, 0xc8, 0x9c, 0xc0, 0x42, 0x77, 0x01, 0x75, 0x23, 0x08, 0xce, 0x03, 0x07, 0xae, + 0x83, 0xac, 0x7f, 0xd5, 0x71, 0x85, 0x85, 0xe2, 0x22, 0x2a, 0xfa, 0x60, 0x3d, 0xa0, 0xb3, 0x6c, + 0xd0, 0x93, 0xc5, 0x2d, 0x59, 0xe0, 0x97, 0x10, 0x1f, 0x20, 0x06, 0x15, 0xb1, 0x8c, 0xb3, 0xc0, + 0x51, 0x36, 0x3b, 0x78, 0x85, 0x14, 0xc4, 0xa0, 0xa2, 0x87, 0xb0, 0xbe, 0x4a, 0x8a, 0x54, 0x8b, + 0xe7, 0x0c, 0x1b, 0x90, 0x51, 0xb0, 0x21, 0x23, 0x9b, 0x4d, 0x5c, 0x46, 0x03, 0x43, 0x04, 0x04, + 0xd3, 0xac, 0x66, 0x9b, 0x88, 0x37, 0x36, 0xe9, 0x78, 0x50, 0x06, 0x16, 0xd8, 0x08, 0x15, 0x28, + 0x5f, 0x46, 0x16, 0x8a, 0x37, 0x51, 0x31, 0xac, 0x61, 0x78, 0x1b, 0x4a, 0xa4, 0xca, 0x13, 0x36, + 0x92, 0xb7, 0xe8, 0x36, 0x10, 0xc1, 0x50, 0x36, 0x45, 0xe4, 0xae, 0xd9, 0x19, 0xde, 0xa6, 0x50, + 0x12, 0x03, 0x8a, 0x39, 0x36, 0x14, 0x3a, 0x49, 0xba, 0xe6, 0x04, 0x56, 0xe9, 0x78, 0x07, 0x1d, + 0x83, 0x25, 0x84, 0x11, 0xc9, 0xa2, 0x5e, 0x34, 0xef, 0x52, 0x44, 0x34, 0x0c, 0x8f, 0x5e, 0xaa, + 0x9c, 0x66, 0x20, 0x1a, 0xbd, 0xd8, 0xde, 0xa3, 0xa3, 0x57, 0xb0, 0x8b, 0xba, 0x71, 0x9a, 0xd5, + 0x52, 0xff, 0x8c, 0x95, 0xe6, 0x7d, 0xca, 0x74, 0x0e, 0x00, 0x7c, 0x0f, 0xbb, 0xae, 0x6b, 0x9b, + 0xb0, 0x90, 0x7d, 0x80, 0xb2, 0x89, 0x2e, 0xad, 0x02, 0x4b, 0x42, 0xaf, 0xca, 0x0f, 0xa9, 0x24, + 0x88, 0x8a, 0x6b, 0x89, 0x8d, 0x67, 0x51, 0xea, 0xac, 0xf6, 0x16, 0xb5, 0x8f, 0x28, 0x6a, 0x05, + 0xdb, 0x11, 0xb5, 0x13, 0x6c, 0x02, 0x8d, 0xbd, 0xe5, 0xf5, 0x63, 0x2a, 0xac, 0x05, 0xbd, 0xd2, + 0x99, 0xdd, 0xfb, 0xd8, 0xbe, 0x32, 0x9c, 0xa7, 0x95, 0x88, 0x52, 0x60, 0x1a, 0xa1, 0x13, 0x5b, + 0x98, 0xaf, 0xa0, 0x99, 0x2a, 0xfe, 0x7c, 0x29, 0x58, 0x74, 0x62, 0x90, 0xdf, 0xcd, 0xf6, 0x92, + 0x3c, 0x8b, 0x12, 0xe1, 0x4a, 0x2f, 0xf2, 0xcf, 0x88, 0x96, 0x85, 0xfa, 0x93, 0x4a, 0xaa, 0x56, + 0x34, 0x1c, 0xcc, 0x47, 0xd9, 0x9e, 0x72, 0x56, 0x69, 0xf8, 0x61, 0x2c, 0x13, 0x65, 0x30, 0x7e, + 0x4a, 0x99, 0x2a, 0xb9, 0xa3, 0x39, 0xc6, 0xe7, 0xd9, 0x70, 0xfe, 0xa7, 0xed, 0x23, 0xf9, 0x19, + 0x8a, 0x86, 0xda, 0x14, 0x16, 0x0e, 0x57, 0x86, 0xb1, 0x93, 0xd8, 0xd4, 0xbf, 0xcf, 0xa9, 0x70, + 0x20, 0x82, 0x85, 0x43, 0x6d, 0xc4, 0x02, 0xba, 0xbd, 0x85, 0xe1, 0x0b, 0x2a, 0x1c, 0xc4, 0xa0, + 0x82, 0x06, 0x06, 0x0b, 0xc5, 0x97, 0xa4, 0x20, 0x06, 0x14, 0x77, 0xb6, 0x1b, 0x6d, 0x22, 0x3c, + 0x3f, 0x55, 0x89, 0x03, 0xab, 0x0d, 0xaa, 0xaf, 0x36, 0x3b, 0x87, 0xb0, 0x65, 0x0d, 0x85, 0x4a, + 0x14, 0x8a, 0x34, 0x75, 0x3c, 0x01, 0x13, 0x87, 0xc5, 0xc6, 0xbe, 0xa6, 0x4a, 0xa4, 0x61, 0xb0, + 0x37, 0x6d, 0x42, 0x84, 0xb0, 0xbb, 0x8e, 0xbb, 0x66, 0xa3, 0xfb, 0xa6, 0xb2, 0xb9, 0xe3, 0xc4, + 0x82, 0x53, 0x9b, 0x7f, 0xb2, 0x68, 0x5d, 0x6c, 0x58, 0x3d, 0x9d, 0xdf, 0x56, 0xe6, 0x9f, 0x95, + 0x82, 0x2c, 0x6a, 0xc8, 0x48, 0x65, 0x9e, 0xaa, 0xdf, 0xb8, 0xc3, 0xb5, 0x58, 0xdc, 0x17, 0xe9, + 0x1e, 0xda, 0xc2, 0xfb, 0xed, 0x1c, 0xa7, 0xf8, 0xed, 0xf0, 0x90, 0x77, 0x0e, 0x3d, 0x66, 0xd9, + 0xd9, 0xad, 0xf2, 0x39, 0xef, 0x98, 0x79, 0xf8, 0x11, 0x36, 0xd4, 0x31, 0xf0, 0x98, 0x55, 0x0f, + 0xa3, 0x6a, 0x50, 0x9f, 0x77, 0xf8, 0x01, 0xb6, 0x0b, 0x86, 0x17, 0x33, 0xfe, 0x08, 0xe2, 0xf9, + 0x72, 0x7e, 0x88, 0xf5, 0xd3, 0xd0, 0x62, 0x46, 0x1f, 0x45, 0xb4, 0x44, 0x00, 0xa7, 0x81, 0xc5, + 0x8c, 0x3f, 0x46, 0x38, 0x21, 0x80, 0xdb, 0x87, 0xf0, 0xbb, 0x27, 0x76, 0x61, 0xd3, 0xa1, 0xd8, + 0x4d, 0xb3, 0x3e, 0x9c, 0x54, 0xcc, 0xf4, 0xe3, 0xf8, 0xe5, 0x44, 0xf0, 0x5b, 0xd9, 0x6e, 0xcb, + 0x80, 0x3f, 0x89, 0x68, 0xb1, 0x9e, 0xcf, 0xb1, 0x01, 0x6d, 0x3a, 0x31, 0xe3, 0x4f, 0x21, 0xae, + 0x53, 0xb0, 0x75, 0x9c, 0x4e, 0xcc, 0x82, 0xa7, 0x69, 0xeb, 0x48, 0x40, 0xd8, 0x68, 0x30, 0x31, + 0xd3, 0xcf, 0x50, 0xd4, 0x09, 0xe1, 0x33, 0xac, 0x56, 0x36, 0x1b, 0x33, 0xff, 0x2c, 0xf2, 0x6d, + 0x06, 0x22, 0xa0, 0x35, 0x3b, 0xb3, 0xe2, 0x39, 0x8a, 0x80, 0x46, 0xc1, 0x31, 0xaa, 0x0e, 0x30, + 0x66, 0xd3, 0xf3, 0x74, 0x8c, 0x2a, 0xf3, 0x0b, 0x64, 0x33, 0xaf, 0xf9, 0x66, 0xc5, 0x0b, 0x94, + 0xcd, 0x7c, 0x3d, 0x6c, 0xa3, 0x3a, 0x11, 0x98, 0x1d, 0x2f, 0xd2, 0x36, 0x2a, 0x03, 0x01, 0x5f, + 0x62, 0xf5, 0x9d, 0xd3, 0x80, 0xd9, 0xf7, 0x12, 0xfa, 0x46, 0x77, 0x0c, 0x03, 0xfc, 0x2e, 0x36, + 0xd1, 0x7d, 0x12, 0x30, 0x5b, 0xcf, 0x6d, 0x55, 0x7e, 0xbb, 0xe9, 0x83, 0x00, 0x3f, 0xd1, 0x6e, + 0x29, 0xfa, 0x14, 0x60, 0xd6, 0x9e, 0xdf, 0xea, 0x2c, 0xdc, 0xfa, 0x10, 0xc0, 0x67, 0x19, 0x6b, + 0x37, 0x60, 0xb3, 0xeb, 0x02, 0xba, 0x34, 0x08, 0x8e, 0x06, 0xf6, 0x5f, 0x33, 0x7f, 0x91, 0x8e, + 0x06, 0x12, 0x70, 0x34, 0xa8, 0xf5, 0x9a, 0xe9, 0x4b, 0x74, 0x34, 0x08, 0x81, 0x27, 0x5b, 0xeb, + 0x6e, 0x66, 0xc3, 0x65, 0x7a, 0xb2, 0x35, 0x8a, 0x1f, 0x63, 0xa3, 0x3b, 0x1a, 0xa2, 0x59, 0xf5, + 0x1a, 0xaa, 0xf6, 0x54, 0xfb, 0xa1, 0xde, 0xbc, 0xb0, 0x19, 0x9a, 0x6d, 0xaf, 0x57, 0x9a, 0x17, + 0xf6, 0x42, 0x3e, 0xcd, 0xfa, 0xa3, 0x2c, 0x08, 0xe0, 0xf0, 0xd4, 0x6f, 0xe8, 0xd2, 0x4d, 0x45, + 0xd0, 0x22, 0xc5, 0xaf, 0xdb, 0x18, 0x1d, 0x02, 0xf8, 0x01, 0xb6, 0x5b, 0x84, 0x4d, 0xd1, 0x32, + 0x91, 0xbf, 0x6d, 0x53, 0xc1, 0x84, 0xd5, 0x7c, 0x86, 0xb1, 0xe2, 0xd5, 0x08, 0x84, 0xd9, 0xc4, + 0xfe, 0xbe, 0x5d, 0xbc, 0xa5, 0xd1, 0x90, 0xb6, 0x20, 0x4f, 0x8a, 0x41, 0xb0, 0xd9, 0x29, 0xc8, + 0x33, 0x72, 0x90, 0xf5, 0xdd, 0x9f, 0xca, 0x48, 0x39, 0x9e, 0x89, 0xfe, 0x03, 0x69, 0x5a, 0x0f, + 0x01, 0x0b, 0x65, 0x22, 0x94, 0xe3, 0xa5, 0x26, 0xf6, 0x4f, 0x64, 0x4b, 0x00, 0x60, 0xd7, 0x49, + 0x95, 0xcd, 0x7d, 0xff, 0x45, 0x30, 0x01, 0xb0, 0x69, 0xb8, 0x5e, 0x17, 0x1b, 0x26, 0xf6, 0x6f, + 0xda, 0x34, 0xae, 0xe7, 0x87, 0x58, 0x0d, 0x2e, 0xf3, 0xb7, 0x4a, 0x26, 0xf8, 0x1f, 0x84, 0xdb, + 0x04, 0x7c, 0x73, 0xaa, 0x5a, 0xca, 0x37, 0x07, 0xfb, 0x5f, 0xcc, 0x34, 0xad, 0xe7, 0xb3, 0x6c, + 0x20, 0x55, 0xad, 0x56, 0x86, 0xf3, 0xa9, 0x01, 0xff, 0x6f, 0xbb, 0x7c, 0x65, 0x51, 0x32, 0x90, + 0xed, 0x07, 0xd7, 0x55, 0x2c, 0xfd, 0x48, 0x89, 0xc4, 0x64, 0xd8, 0x42, 0x83, 0x86, 0x1c, 0x9e, + 0x67, 0x63, 0xae, 0x0c, 0xab, 0xdc, 0x61, 0xb6, 0x20, 0x17, 0xe4, 0x52, 0x5e, 0x67, 0xee, 0xbd, + 0xd9, 0xf3, 0xd5, 0x5a, 0xd6, 0x9c, 0x74, 0x65, 0x38, 0x05, 0xbf, 0x3c, 0xda, 0x2f, 0x54, 0xcb, + 0xdf, 0x21, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0x9c, 0xaf, 0x70, 0x4e, 0x83, 0x15, 0x00, 0x00, } diff --git a/vendor/github.com/gogo/protobuf/gogoproto/gogo.proto b/vendor/github.com/gogo/protobuf/gogoproto/gogo.proto index 7f09979358972..b80c85653f74a 100644 --- a/vendor/github.com/gogo/protobuf/gogoproto/gogo.proto +++ b/vendor/github.com/gogo/protobuf/gogoproto/gogo.proto @@ -83,6 +83,10 @@ extend google.protobuf.FileOptions { optional bool enumdecl_all = 63031; optional bool goproto_registration = 63032; + optional bool messagename_all = 63033; + + optional bool goproto_sizecache_all = 63034; + optional bool goproto_unkeyed_all = 63035; } extend google.protobuf.MessageOptions { @@ -115,6 +119,11 @@ extend google.protobuf.MessageOptions { optional bool compare = 64029; optional bool typedecl = 64030; + + optional bool messagename = 64033; + + optional bool goproto_sizecache = 64034; + optional bool goproto_unkeyed = 64035; } extend google.protobuf.FieldOptions { @@ -130,4 +139,6 @@ extend google.protobuf.FieldOptions { optional bool stdtime = 65010; optional bool stdduration = 65011; + optional bool wktpointer = 65012; + } diff --git a/vendor/github.com/gogo/protobuf/gogoproto/helper.go b/vendor/github.com/gogo/protobuf/gogoproto/helper.go index 6b851c562396d..390d4e4be6b83 100644 --- a/vendor/github.com/gogo/protobuf/gogoproto/helper.go +++ b/vendor/github.com/gogo/protobuf/gogoproto/helper.go @@ -47,6 +47,55 @@ func IsStdDuration(field *google_protobuf.FieldDescriptorProto) bool { return proto.GetBoolExtension(field.Options, E_Stdduration, false) } +func IsStdDouble(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.DoubleValue" +} + +func IsStdFloat(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.FloatValue" +} + +func IsStdInt64(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.Int64Value" +} + +func IsStdUInt64(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.UInt64Value" +} + +func IsStdInt32(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.Int32Value" +} + +func IsStdUInt32(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.UInt32Value" +} + +func IsStdBool(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.BoolValue" +} + +func IsStdString(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.StringValue" +} + +func IsStdBytes(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) && *field.TypeName == ".google.protobuf.BytesValue" +} + +func IsStdType(field *google_protobuf.FieldDescriptorProto) bool { + return (IsStdTime(field) || IsStdDuration(field) || + IsStdDouble(field) || IsStdFloat(field) || + IsStdInt64(field) || IsStdUInt64(field) || + IsStdInt32(field) || IsStdUInt32(field) || + IsStdBool(field) || + IsStdString(field) || IsStdBytes(field)) +} + +func IsWktPtr(field *google_protobuf.FieldDescriptorProto) bool { + return proto.GetBoolExtension(field.Options, E_Wktpointer, false) +} + func NeedsNilCheck(proto3 bool, field *google_protobuf.FieldDescriptorProto) bool { nullable := IsNullable(field) if field.IsMessage() || IsCustomType(field) { @@ -334,9 +383,6 @@ func HasExtensionsMap(file *google_protobuf.FileDescriptorProto, message *google } func HasUnrecognized(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { - if IsProto3(file) { - return false - } return proto.GetBoolExtension(message.Options, E_GoprotoUnrecognized, proto.GetBoolExtension(file.Options, E_GoprotoUnrecognizedAll, true)) } @@ -355,3 +401,15 @@ func HasCompare(file *google_protobuf.FileDescriptorProto, message *google_proto func RegistersGolangProto(file *google_protobuf.FileDescriptorProto) bool { return proto.GetBoolExtension(file.Options, E_GoprotoRegistration, false) } + +func HasMessageName(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_Messagename, proto.GetBoolExtension(file.Options, E_MessagenameAll, false)) +} + +func HasSizecache(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_GoprotoSizecache, proto.GetBoolExtension(file.Options, E_GoprotoSizecacheAll, true)) +} + +func HasUnkeyed(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool { + return proto.GetBoolExtension(message.Options, E_GoprotoUnkeyed, proto.GetBoolExtension(file.Options, E_GoprotoUnkeyedAll, true)) +} diff --git a/vendor/github.com/gogo/protobuf/io/uint32.go b/vendor/github.com/gogo/protobuf/io/uint32.go index fc43857dd77f8..233b909242960 100644 --- a/vendor/github.com/gogo/protobuf/io/uint32.go +++ b/vendor/github.com/gogo/protobuf/io/uint32.go @@ -120,7 +120,7 @@ func (this *uint32Reader) ReadMsg(msg proto.Message) error { if length < 0 || length > this.maxSize { return io.ErrShortBuffer } - if length >= len(this.buf) { + if length > len(this.buf) { this.buf = make([]byte, length) } _, err := io.ReadFull(this.r, this.buf[:length]) diff --git a/vendor/github.com/gogo/protobuf/io/varint.go b/vendor/github.com/gogo/protobuf/io/varint.go index a72e14a583e68..e81e296e4f58d 100644 --- a/vendor/github.com/gogo/protobuf/io/varint.go +++ b/vendor/github.com/gogo/protobuf/io/varint.go @@ -42,7 +42,7 @@ var ( ) func NewDelimitedWriter(w io.Writer) WriteCloser { - return &varintWriter{w, make([]byte, 10), nil} + return &varintWriter{w, make([]byte, binary.MaxVarintLen64), nil} } type varintWriter struct { @@ -55,26 +55,25 @@ func (this *varintWriter) WriteMsg(msg proto.Message) (err error) { var data []byte if m, ok := msg.(marshaler); ok { n, ok := getSize(m) - if !ok { - data, err = proto.Marshal(msg) + if ok { + if n+binary.MaxVarintLen64 >= len(this.buffer) { + this.buffer = make([]byte, n+binary.MaxVarintLen64) + } + lenOff := binary.PutUvarint(this.buffer, uint64(n)) + _, err = m.MarshalTo(this.buffer[lenOff:]) if err != nil { return err } - } - if n >= len(this.buffer) { - this.buffer = make([]byte, n) - } - _, err = m.MarshalTo(this.buffer) - if err != nil { - return err - } - data = this.buffer[:n] - } else { - data, err = proto.Marshal(msg) - if err != nil { + _, err = this.w.Write(this.buffer[:lenOff+n]) return err } } + + // fallback + data, err = proto.Marshal(msg) + if err != nil { + return err + } length := uint64(len(data)) n := binary.PutUvarint(this.lenBuf, length) _, err = this.w.Write(this.lenBuf[:n]) diff --git a/vendor/github.com/gogo/protobuf/proto/clone.go b/vendor/github.com/gogo/protobuf/proto/clone.go index 5d4cba4b51c6c..a26b046d94f1a 100644 --- a/vendor/github.com/gogo/protobuf/proto/clone.go +++ b/vendor/github.com/gogo/protobuf/proto/clone.go @@ -35,22 +35,39 @@ package proto import ( + "fmt" "log" "reflect" "strings" ) // Clone returns a deep copy of a protocol buffer. -func Clone(pb Message) Message { - in := reflect.ValueOf(pb) +func Clone(src Message) Message { + in := reflect.ValueOf(src) if in.IsNil() { - return pb + return src } - out := reflect.New(in.Type().Elem()) - // out is empty so a merge is a deep copy. - mergeStruct(out.Elem(), in.Elem()) - return out.Interface().(Message) + dst := out.Interface().(Message) + Merge(dst, src) + return dst +} + +// Merger is the interface representing objects that can merge messages of the same type. +type Merger interface { + // Merge merges src into this message. + // Required and optional fields that are set in src will be set to that value in dst. + // Elements of repeated fields will be appended. + // + // Merge may panic if called with a different argument type than the receiver. + Merge(src Message) +} + +// generatedMerger is the custom merge method that generated protos will have. +// We must add this method since a generate Merge method will conflict with +// many existing protos that have a Merge data field already defined. +type generatedMerger interface { + XXX_Merge(src Message) } // Merge merges src into dst. @@ -58,17 +75,24 @@ func Clone(pb Message) Message { // Elements of repeated fields will be appended. // Merge panics if src and dst are not the same type, or if dst is nil. func Merge(dst, src Message) { + if m, ok := dst.(Merger); ok { + m.Merge(src) + return + } + in := reflect.ValueOf(src) out := reflect.ValueOf(dst) if out.IsNil() { panic("proto: nil destination") } if in.Type() != out.Type() { - // Explicit test prior to mergeStruct so that mistyped nils will fail - panic("proto: type mismatch") + panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src)) } if in.IsNil() { - // Merging nil into non-nil is a quiet no-op + return // Merge from nil src is a noop + } + if m, ok := dst.(generatedMerger); ok { + m.XXX_Merge(src) return } mergeStruct(out.Elem(), in.Elem()) @@ -89,7 +113,7 @@ func mergeStruct(out, in reflect.Value) { bIn := emIn.GetExtensions() bOut := emOut.GetExtensions() *bOut = append(*bOut, *bIn...) - } else if emIn, ok := extendable(in.Addr().Interface()); ok { + } else if emIn, err := extendable(in.Addr().Interface()); err == nil { emOut, _ := extendable(out.Addr().Interface()) mIn, muIn := emIn.extensionsRead() if mIn != nil { diff --git a/vendor/github.com/gogo/protobuf/proto/custom_gogo.go b/vendor/github.com/gogo/protobuf/proto/custom_gogo.go new file mode 100644 index 0000000000000..24552483c6cea --- /dev/null +++ b/vendor/github.com/gogo/protobuf/proto/custom_gogo.go @@ -0,0 +1,39 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2018, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import "reflect" + +type custom interface { + Marshal() ([]byte, error) + Unmarshal(data []byte) error + Size() int +} + +var customType = reflect.TypeOf((*custom)(nil)).Elem() diff --git a/vendor/github.com/gogo/protobuf/proto/decode.go b/vendor/github.com/gogo/protobuf/proto/decode.go index 737f2731d45dc..63b0f08bef2a0 100644 --- a/vendor/github.com/gogo/protobuf/proto/decode.go +++ b/vendor/github.com/gogo/protobuf/proto/decode.go @@ -39,8 +39,6 @@ import ( "errors" "fmt" "io" - "os" - "reflect" ) // errOverflow is returned when an integer is too large to be represented. @@ -50,10 +48,6 @@ var errOverflow = errors.New("proto: integer overflow") // wire type is encountered. It does not get returned to user code. var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof") -// The fundamental decoders that interpret bytes on the wire. -// Those that take integer types all return uint64 and are -// therefore of type valueDecoder. - // DecodeVarint reads a varint-encoded integer from the slice. // It returns the integer and the number of bytes consumed, or // zero if there is not enough. @@ -192,7 +186,6 @@ func (p *Buffer) DecodeVarint() (x uint64, err error) { if b&0x80 == 0 { goto done } - // x -= 0x80 << 63 // Always zero. return 0, errOverflow @@ -267,9 +260,6 @@ func (p *Buffer) DecodeZigzag32() (x uint64, err error) { return } -// These are not ValueDecoders: they produce an array of bytes or a string. -// bytes, embedded messages - // DecodeRawBytes reads a count-delimited byte buffer from the Buffer. // This is the format used for the bytes protocol buffer // type and for embedded messages. @@ -311,81 +301,29 @@ func (p *Buffer) DecodeStringBytes() (s string, err error) { return string(buf), nil } -// Skip the next item in the buffer. Its wire type is decoded and presented as an argument. -// If the protocol buffer has extensions, and the field matches, add it as an extension. -// Otherwise, if the XXX_unrecognized field exists, append the skipped data there. -func (o *Buffer) skipAndSave(t reflect.Type, tag, wire int, base structPointer, unrecField field) error { - oi := o.index - - err := o.skip(t, tag, wire) - if err != nil { - return err - } - - if !unrecField.IsValid() { - return nil - } - - ptr := structPointer_Bytes(base, unrecField) - - // Add the skipped field to struct field - obuf := o.buf - - o.buf = *ptr - o.EncodeVarint(uint64(tag<<3 | wire)) - *ptr = append(o.buf, obuf[oi:o.index]...) - - o.buf = obuf - - return nil -} - -// Skip the next item in the buffer. Its wire type is decoded and presented as an argument. -func (o *Buffer) skip(t reflect.Type, tag, wire int) error { - - var u uint64 - var err error - - switch wire { - case WireVarint: - _, err = o.DecodeVarint() - case WireFixed64: - _, err = o.DecodeFixed64() - case WireBytes: - _, err = o.DecodeRawBytes(false) - case WireFixed32: - _, err = o.DecodeFixed32() - case WireStartGroup: - for { - u, err = o.DecodeVarint() - if err != nil { - break - } - fwire := int(u & 0x7) - if fwire == WireEndGroup { - break - } - ftag := int(u >> 3) - err = o.skip(t, ftag, fwire) - if err != nil { - break - } - } - default: - err = fmt.Errorf("proto: can't skip unknown wire type %d for %s", wire, t) - } - return err -} - // Unmarshaler is the interface representing objects that can -// unmarshal themselves. The method should reset the receiver before -// decoding starts. The argument points to data that may be +// unmarshal themselves. The argument points to data that may be // overwritten, so implementations should not keep references to the // buffer. +// Unmarshal implementations should not clear the receiver. +// Any unmarshaled data should be merged into the receiver. +// Callers of Unmarshal that do not want to retain existing data +// should Reset the receiver before calling Unmarshal. type Unmarshaler interface { Unmarshal([]byte) error } +// newUnmarshaler is the interface representing objects that can +// unmarshal themselves. The semantics are identical to Unmarshaler. +// +// This exists to support protoc-gen-go generated messages. +// The proto package will stop type-asserting to this interface in the future. +// +// DO NOT DEPEND ON THIS. +type newUnmarshaler interface { + XXX_Unmarshal([]byte) error +} + // Unmarshal parses the protocol buffer representation in buf and places the // decoded result in pb. If the struct underlying pb does not match // the data in buf, the results can be unpredictable. @@ -395,7 +333,13 @@ type Unmarshaler interface { // to preserve and append to existing data. func Unmarshal(buf []byte, pb Message) error { pb.Reset() - return UnmarshalMerge(buf, pb) + if u, ok := pb.(newUnmarshaler); ok { + return u.XXX_Unmarshal(buf) + } + if u, ok := pb.(Unmarshaler); ok { + return u.Unmarshal(buf) + } + return NewBuffer(buf).Unmarshal(pb) } // UnmarshalMerge parses the protocol buffer representation in buf and @@ -405,8 +349,16 @@ func Unmarshal(buf []byte, pb Message) error { // UnmarshalMerge merges into existing data in pb. // Most code should use Unmarshal instead. func UnmarshalMerge(buf []byte, pb Message) error { - // If the object can unmarshal itself, let it. + if u, ok := pb.(newUnmarshaler); ok { + return u.XXX_Unmarshal(buf) + } if u, ok := pb.(Unmarshaler); ok { + // NOTE: The history of proto have unfortunately been inconsistent + // whether Unmarshaler should or should not implicitly clear itself. + // Some implementations do, most do not. + // Thus, calling this here may or may not do what people want. + // + // See https://github.com/golang/protobuf/issues/424 return u.Unmarshal(buf) } return NewBuffer(buf).Unmarshal(pb) @@ -422,12 +374,17 @@ func (p *Buffer) DecodeMessage(pb Message) error { } // DecodeGroup reads a tag-delimited group from the Buffer. +// StartGroup tag is already consumed. This function consumes +// EndGroup tag. func (p *Buffer) DecodeGroup(pb Message) error { - typ, base, err := getbase(pb) - if err != nil { - return err + b := p.buf[p.index:] + x, y := findEndGroup(b) + if x < 0 { + return io.ErrUnexpectedEOF } - return p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), true, base) + err := Unmarshal(b[:x], pb) + p.index += y + return err } // Unmarshal parses the protocol buffer representation in the @@ -438,541 +395,33 @@ func (p *Buffer) DecodeGroup(pb Message) error { // Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal. func (p *Buffer) Unmarshal(pb Message) error { // If the object can unmarshal itself, let it. - if u, ok := pb.(Unmarshaler); ok { - err := u.Unmarshal(p.buf[p.index:]) + if u, ok := pb.(newUnmarshaler); ok { + err := u.XXX_Unmarshal(p.buf[p.index:]) p.index = len(p.buf) return err } - - typ, base, err := getbase(pb) - if err != nil { - return err - } - - err = p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), false, base) - - if collectStats { - stats.Decode++ - } - - return err -} - -// unmarshalType does the work of unmarshaling a structure. -func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group bool, base structPointer) error { - var state errorState - required, reqFields := prop.reqCount, uint64(0) - - var err error - for err == nil && o.index < len(o.buf) { - oi := o.index - var u uint64 - u, err = o.DecodeVarint() - if err != nil { - break - } - wire := int(u & 0x7) - if wire == WireEndGroup { - if is_group { - if required > 0 { - // Not enough information to determine the exact field. - // (See below.) - return &RequiredNotSetError{"{Unknown}"} - } - return nil // input is satisfied - } - return fmt.Errorf("proto: %s: wiretype end group for non-group", st) - } - tag := int(u >> 3) - if tag <= 0 { - return fmt.Errorf("proto: %s: illegal tag %d (wire type %d)", st, tag, wire) - } - fieldnum, ok := prop.decoderTags.get(tag) - if !ok { - // Maybe it's an extension? - if prop.extendable { - if e, eok := structPointer_Interface(base, st).(extensionsBytes); eok { - if isExtensionField(e, int32(tag)) { - if err = o.skip(st, tag, wire); err == nil { - ext := e.GetExtensions() - *ext = append(*ext, o.buf[oi:o.index]...) - } - continue - } - } else if e, _ := extendable(structPointer_Interface(base, st)); isExtensionField(e, int32(tag)) { - if err = o.skip(st, tag, wire); err == nil { - extmap := e.extensionsWrite() - ext := extmap[int32(tag)] // may be missing - ext.enc = append(ext.enc, o.buf[oi:o.index]...) - extmap[int32(tag)] = ext - } - continue - } - } - // Maybe it's a oneof? - if prop.oneofUnmarshaler != nil { - m := structPointer_Interface(base, st).(Message) - // First return value indicates whether tag is a oneof field. - ok, err = prop.oneofUnmarshaler(m, tag, wire, o) - if err == ErrInternalBadWireType { - // Map the error to something more descriptive. - // Do the formatting here to save generated code space. - err = fmt.Errorf("bad wiretype for oneof field in %T", m) - } - if ok { - continue - } - } - err = o.skipAndSave(st, tag, wire, base, prop.unrecField) - continue - } - p := prop.Prop[fieldnum] - - if p.dec == nil { - fmt.Fprintf(os.Stderr, "proto: no protobuf decoder for %s.%s\n", st, st.Field(fieldnum).Name) - continue - } - dec := p.dec - if wire != WireStartGroup && wire != p.WireType { - if wire == WireBytes && p.packedDec != nil { - // a packable field - dec = p.packedDec - } else { - err = fmt.Errorf("proto: bad wiretype for field %s.%s: got wiretype %d, want %d", st, st.Field(fieldnum).Name, wire, p.WireType) - continue - } - } - decErr := dec(o, p, base) - if decErr != nil && !state.shouldContinue(decErr, p) { - err = decErr - } - if err == nil && p.Required { - // Successfully decoded a required field. - if tag <= 64 { - // use bitmap for fields 1-64 to catch field reuse. - var mask uint64 = 1 << uint64(tag-1) - if reqFields&mask == 0 { - // new required field - reqFields |= mask - required-- - } - } else { - // This is imprecise. It can be fooled by a required field - // with a tag > 64 that is encoded twice; that's very rare. - // A fully correct implementation would require allocating - // a data structure, which we would like to avoid. - required-- - } - } - } - if err == nil { - if is_group { - return io.ErrUnexpectedEOF - } - if state.err != nil { - return state.err - } - if required > 0 { - // Not enough information to determine the exact field. If we use extra - // CPU, we could determine the field only if the missing required field - // has a tag <= 64 and we check reqFields. - return &RequiredNotSetError{"{Unknown}"} - } - } - return err -} - -// Individual type decoders -// For each, -// u is the decoded value, -// v is a pointer to the field (pointer) in the struct - -// Sizes of the pools to allocate inside the Buffer. -// The goal is modest amortization and allocation -// on at least 16-byte boundaries. -const ( - boolPoolSize = 16 - uint32PoolSize = 8 - uint64PoolSize = 4 -) - -// Decode a bool. -func (o *Buffer) dec_bool(p *Properties, base structPointer) error { - u, err := p.valDec(o) - if err != nil { - return err - } - if len(o.bools) == 0 { - o.bools = make([]bool, boolPoolSize) - } - o.bools[0] = u != 0 - *structPointer_Bool(base, p.field) = &o.bools[0] - o.bools = o.bools[1:] - return nil -} - -func (o *Buffer) dec_proto3_bool(p *Properties, base structPointer) error { - u, err := p.valDec(o) - if err != nil { - return err - } - *structPointer_BoolVal(base, p.field) = u != 0 - return nil -} - -// Decode an int32. -func (o *Buffer) dec_int32(p *Properties, base structPointer) error { - u, err := p.valDec(o) - if err != nil { - return err - } - word32_Set(structPointer_Word32(base, p.field), o, uint32(u)) - return nil -} - -func (o *Buffer) dec_proto3_int32(p *Properties, base structPointer) error { - u, err := p.valDec(o) - if err != nil { - return err - } - word32Val_Set(structPointer_Word32Val(base, p.field), uint32(u)) - return nil -} - -// Decode an int64. -func (o *Buffer) dec_int64(p *Properties, base structPointer) error { - u, err := p.valDec(o) - if err != nil { - return err - } - word64_Set(structPointer_Word64(base, p.field), o, u) - return nil -} - -func (o *Buffer) dec_proto3_int64(p *Properties, base structPointer) error { - u, err := p.valDec(o) - if err != nil { - return err - } - word64Val_Set(structPointer_Word64Val(base, p.field), o, u) - return nil -} - -// Decode a string. -func (o *Buffer) dec_string(p *Properties, base structPointer) error { - s, err := o.DecodeStringBytes() - if err != nil { - return err - } - *structPointer_String(base, p.field) = &s - return nil -} - -func (o *Buffer) dec_proto3_string(p *Properties, base structPointer) error { - s, err := o.DecodeStringBytes() - if err != nil { - return err - } - *structPointer_StringVal(base, p.field) = s - return nil -} - -// Decode a slice of bytes ([]byte). -func (o *Buffer) dec_slice_byte(p *Properties, base structPointer) error { - b, err := o.DecodeRawBytes(true) - if err != nil { - return err - } - *structPointer_Bytes(base, p.field) = b - return nil -} - -// Decode a slice of bools ([]bool). -func (o *Buffer) dec_slice_bool(p *Properties, base structPointer) error { - u, err := p.valDec(o) - if err != nil { - return err - } - v := structPointer_BoolSlice(base, p.field) - *v = append(*v, u != 0) - return nil -} - -// Decode a slice of bools ([]bool) in packed format. -func (o *Buffer) dec_slice_packed_bool(p *Properties, base structPointer) error { - v := structPointer_BoolSlice(base, p.field) - - nn, err := o.DecodeVarint() - if err != nil { - return err - } - nb := int(nn) // number of bytes of encoded bools - fin := o.index + nb - if fin < o.index { - return errOverflow - } - - y := *v - for o.index < fin { - u, err := p.valDec(o) - if err != nil { - return err - } - y = append(y, u != 0) - } - - *v = y - return nil -} - -// Decode a slice of int32s ([]int32). -func (o *Buffer) dec_slice_int32(p *Properties, base structPointer) error { - u, err := p.valDec(o) - if err != nil { - return err - } - structPointer_Word32Slice(base, p.field).Append(uint32(u)) - return nil -} - -// Decode a slice of int32s ([]int32) in packed format. -func (o *Buffer) dec_slice_packed_int32(p *Properties, base structPointer) error { - v := structPointer_Word32Slice(base, p.field) - - nn, err := o.DecodeVarint() - if err != nil { - return err - } - nb := int(nn) // number of bytes of encoded int32s - - fin := o.index + nb - if fin < o.index { - return errOverflow - } - for o.index < fin { - u, err := p.valDec(o) - if err != nil { - return err - } - v.Append(uint32(u)) - } - return nil -} - -// Decode a slice of int64s ([]int64). -func (o *Buffer) dec_slice_int64(p *Properties, base structPointer) error { - u, err := p.valDec(o) - if err != nil { - return err - } - - structPointer_Word64Slice(base, p.field).Append(u) - return nil -} - -// Decode a slice of int64s ([]int64) in packed format. -func (o *Buffer) dec_slice_packed_int64(p *Properties, base structPointer) error { - v := structPointer_Word64Slice(base, p.field) - - nn, err := o.DecodeVarint() - if err != nil { - return err - } - nb := int(nn) // number of bytes of encoded int64s - - fin := o.index + nb - if fin < o.index { - return errOverflow - } - for o.index < fin { - u, err := p.valDec(o) - if err != nil { - return err - } - v.Append(u) - } - return nil -} - -// Decode a slice of strings ([]string). -func (o *Buffer) dec_slice_string(p *Properties, base structPointer) error { - s, err := o.DecodeStringBytes() - if err != nil { - return err - } - v := structPointer_StringSlice(base, p.field) - *v = append(*v, s) - return nil -} - -// Decode a slice of slice of bytes ([][]byte). -func (o *Buffer) dec_slice_slice_byte(p *Properties, base structPointer) error { - b, err := o.DecodeRawBytes(true) - if err != nil { - return err - } - v := structPointer_BytesSlice(base, p.field) - *v = append(*v, b) - return nil -} - -// Decode a map field. -func (o *Buffer) dec_new_map(p *Properties, base structPointer) error { - raw, err := o.DecodeRawBytes(false) - if err != nil { - return err - } - oi := o.index // index at the end of this map entry - o.index -= len(raw) // move buffer back to start of map entry - - mptr := structPointer_NewAt(base, p.field, p.mtype) // *map[K]V - if mptr.Elem().IsNil() { - mptr.Elem().Set(reflect.MakeMap(mptr.Type().Elem())) - } - v := mptr.Elem() // map[K]V - - // Prepare addressable doubly-indirect placeholders for the key and value types. - // See enc_new_map for why. - keyptr := reflect.New(reflect.PtrTo(p.mtype.Key())).Elem() // addressable *K - keybase := toStructPointer(keyptr.Addr()) // **K - - var valbase structPointer - var valptr reflect.Value - switch p.mtype.Elem().Kind() { - case reflect.Slice: - // []byte - var dummy []byte - valptr = reflect.ValueOf(&dummy) // *[]byte - valbase = toStructPointer(valptr) // *[]byte - case reflect.Ptr: - // message; valptr is **Msg; need to allocate the intermediate pointer - valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V - valptr.Set(reflect.New(valptr.Type().Elem())) - valbase = toStructPointer(valptr) - default: - // everything else - valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V - valbase = toStructPointer(valptr.Addr()) // **V - } - - // Decode. - // This parses a restricted wire format, namely the encoding of a message - // with two fields. See enc_new_map for the format. - for o.index < oi { - // tagcode for key and value properties are always a single byte - // because they have tags 1 and 2. - tagcode := o.buf[o.index] - o.index++ - switch tagcode { - case p.mkeyprop.tagcode[0]: - if err := p.mkeyprop.dec(o, p.mkeyprop, keybase); err != nil { - return err - } - case p.mvalprop.tagcode[0]: - if err := p.mvalprop.dec(o, p.mvalprop, valbase); err != nil { - return err - } - default: - // TODO: Should we silently skip this instead? - return fmt.Errorf("proto: bad map data tag %d", raw[0]) - } - } - keyelem, valelem := keyptr.Elem(), valptr.Elem() - if !keyelem.IsValid() { - keyelem = reflect.Zero(p.mtype.Key()) - } - if !valelem.IsValid() { - valelem = reflect.Zero(p.mtype.Elem()) - } - - v.SetMapIndex(keyelem, valelem) - return nil -} - -// Decode a group. -func (o *Buffer) dec_struct_group(p *Properties, base structPointer) error { - bas := structPointer_GetStructPointer(base, p.field) - if structPointer_IsNil(bas) { - // allocate new nested message - bas = toStructPointer(reflect.New(p.stype)) - structPointer_SetStructPointer(base, p.field, bas) - } - return o.unmarshalType(p.stype, p.sprop, true, bas) -} - -// Decode an embedded message. -func (o *Buffer) dec_struct_message(p *Properties, base structPointer) (err error) { - raw, e := o.DecodeRawBytes(false) - if e != nil { - return e - } - - bas := structPointer_GetStructPointer(base, p.field) - if structPointer_IsNil(bas) { - // allocate new nested message - bas = toStructPointer(reflect.New(p.stype)) - structPointer_SetStructPointer(base, p.field, bas) - } - - // If the object can unmarshal itself, let it. - if p.isUnmarshaler { - iv := structPointer_Interface(bas, p.stype) - return iv.(Unmarshaler).Unmarshal(raw) - } - - obuf := o.buf - oi := o.index - o.buf = raw - o.index = 0 - - err = o.unmarshalType(p.stype, p.sprop, false, bas) - o.buf = obuf - o.index = oi - - return err -} - -// Decode a slice of embedded messages. -func (o *Buffer) dec_slice_struct_message(p *Properties, base structPointer) error { - return o.dec_slice_struct(p, false, base) -} - -// Decode a slice of embedded groups. -func (o *Buffer) dec_slice_struct_group(p *Properties, base structPointer) error { - return o.dec_slice_struct(p, true, base) -} - -// Decode a slice of structs ([]*struct). -func (o *Buffer) dec_slice_struct(p *Properties, is_group bool, base structPointer) error { - v := reflect.New(p.stype) - bas := toStructPointer(v) - structPointer_StructPointerSlice(base, p.field).Append(bas) - - if is_group { - err := o.unmarshalType(p.stype, p.sprop, is_group, bas) - return err - } - - raw, err := o.DecodeRawBytes(false) - if err != nil { + if u, ok := pb.(Unmarshaler); ok { + // NOTE: The history of proto have unfortunately been inconsistent + // whether Unmarshaler should or should not implicitly clear itself. + // Some implementations do, most do not. + // Thus, calling this here may or may not do what people want. + // + // See https://github.com/golang/protobuf/issues/424 + err := u.Unmarshal(p.buf[p.index:]) + p.index = len(p.buf) return err } - // If the object can unmarshal itself, let it. - if p.isUnmarshaler { - iv := v.Interface() - return iv.(Unmarshaler).Unmarshal(raw) - } - - obuf := o.buf - oi := o.index - o.buf = raw - o.index = 0 - - err = o.unmarshalType(p.stype, p.sprop, is_group, bas) - - o.buf = obuf - o.index = oi - + // Slow workaround for messages that aren't Unmarshalers. + // This includes some hand-coded .pb.go files and + // bootstrap protos. + // TODO: fix all of those and then add Unmarshal to + // the Message interface. Then: + // The cast above and code below can be deleted. + // The old unmarshaler can be deleted. + // Clients can call Unmarshal directly (can already do that, actually). + var info InternalMessageInfo + err := info.Unmarshal(pb, p.buf[p.index:]) + p.index = len(p.buf) return err } diff --git a/vendor/github.com/gogo/protobuf/proto/decode_gogo.go b/vendor/github.com/gogo/protobuf/proto/decode_gogo.go deleted file mode 100644 index 6fb74de4cc94c..0000000000000 --- a/vendor/github.com/gogo/protobuf/proto/decode_gogo.go +++ /dev/null @@ -1,172 +0,0 @@ -// Protocol Buffers for Go with Gadgets -// -// Copyright (c) 2013, The GoGo Authors. All rights reserved. -// http://github.com/gogo/protobuf -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package proto - -import ( - "reflect" -) - -// Decode a reference to a struct pointer. -func (o *Buffer) dec_ref_struct_message(p *Properties, base structPointer) (err error) { - raw, e := o.DecodeRawBytes(false) - if e != nil { - return e - } - - // If the object can unmarshal itself, let it. - if p.isUnmarshaler { - panic("not supported, since this is a pointer receiver") - } - - obuf := o.buf - oi := o.index - o.buf = raw - o.index = 0 - - bas := structPointer_FieldPointer(base, p.field) - - err = o.unmarshalType(p.stype, p.sprop, false, bas) - o.buf = obuf - o.index = oi - - return err -} - -// Decode a slice of references to struct pointers ([]struct). -func (o *Buffer) dec_slice_ref_struct(p *Properties, is_group bool, base structPointer) error { - newBas := appendStructPointer(base, p.field, p.sstype) - - if is_group { - panic("not supported, maybe in future, if requested.") - } - - raw, err := o.DecodeRawBytes(false) - if err != nil { - return err - } - - // If the object can unmarshal itself, let it. - if p.isUnmarshaler { - panic("not supported, since this is not a pointer receiver.") - } - - obuf := o.buf - oi := o.index - o.buf = raw - o.index = 0 - - err = o.unmarshalType(p.stype, p.sprop, is_group, newBas) - - o.buf = obuf - o.index = oi - - return err -} - -// Decode a slice of references to struct pointers. -func (o *Buffer) dec_slice_ref_struct_message(p *Properties, base structPointer) error { - return o.dec_slice_ref_struct(p, false, base) -} - -func setPtrCustomType(base structPointer, f field, v interface{}) { - if v == nil { - return - } - structPointer_SetStructPointer(base, f, toStructPointer(reflect.ValueOf(v))) -} - -func setCustomType(base structPointer, f field, value interface{}) { - if value == nil { - return - } - v := reflect.ValueOf(value).Elem() - t := reflect.TypeOf(value).Elem() - kind := t.Kind() - switch kind { - case reflect.Slice: - slice := reflect.MakeSlice(t, v.Len(), v.Cap()) - reflect.Copy(slice, v) - oldHeader := structPointer_GetSliceHeader(base, f) - oldHeader.Data = slice.Pointer() - oldHeader.Len = v.Len() - oldHeader.Cap = v.Cap() - default: - size := reflect.TypeOf(value).Elem().Size() - structPointer_Copy(toStructPointer(reflect.ValueOf(value)), structPointer_Add(base, f), int(size)) - } -} - -func (o *Buffer) dec_custom_bytes(p *Properties, base structPointer) error { - b, err := o.DecodeRawBytes(true) - if err != nil { - return err - } - i := reflect.New(p.ctype.Elem()).Interface() - custom := (i).(Unmarshaler) - if err := custom.Unmarshal(b); err != nil { - return err - } - setPtrCustomType(base, p.field, custom) - return nil -} - -func (o *Buffer) dec_custom_ref_bytes(p *Properties, base structPointer) error { - b, err := o.DecodeRawBytes(true) - if err != nil { - return err - } - i := reflect.New(p.ctype).Interface() - custom := (i).(Unmarshaler) - if err := custom.Unmarshal(b); err != nil { - return err - } - if custom != nil { - setCustomType(base, p.field, custom) - } - return nil -} - -// Decode a slice of bytes ([]byte) into a slice of custom types. -func (o *Buffer) dec_custom_slice_bytes(p *Properties, base structPointer) error { - b, err := o.DecodeRawBytes(true) - if err != nil { - return err - } - i := reflect.New(p.ctype.Elem()).Interface() - custom := (i).(Unmarshaler) - if err := custom.Unmarshal(b); err != nil { - return err - } - newBas := appendStructPointer(base, p.field, p.ctype) - - var zero field - setCustomType(newBas, zero, custom) - - return nil -} diff --git a/vendor/github.com/gogo/protobuf/proto/deprecated.go b/vendor/github.com/gogo/protobuf/proto/deprecated.go new file mode 100644 index 0000000000000..35b882c09aaf9 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/proto/deprecated.go @@ -0,0 +1,63 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2018 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import "errors" + +// Deprecated: do not use. +type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 } + +// Deprecated: do not use. +func GetStats() Stats { return Stats{} } + +// Deprecated: do not use. +func MarshalMessageSet(interface{}) ([]byte, error) { + return nil, errors.New("proto: not implemented") +} + +// Deprecated: do not use. +func UnmarshalMessageSet([]byte, interface{}) error { + return errors.New("proto: not implemented") +} + +// Deprecated: do not use. +func MarshalMessageSetJSON(interface{}) ([]byte, error) { + return nil, errors.New("proto: not implemented") +} + +// Deprecated: do not use. +func UnmarshalMessageSetJSON([]byte, interface{}) error { + return errors.New("proto: not implemented") +} + +// Deprecated: do not use. +func RegisterMessageSetType(Message, int32, string) {} diff --git a/vendor/github.com/gogo/protobuf/proto/discard.go b/vendor/github.com/gogo/protobuf/proto/discard.go index bd0e3bb4c8510..fe1bd7d904e24 100644 --- a/vendor/github.com/gogo/protobuf/proto/discard.go +++ b/vendor/github.com/gogo/protobuf/proto/discard.go @@ -35,8 +35,14 @@ import ( "fmt" "reflect" "strings" + "sync" + "sync/atomic" ) +type generatedDiscarder interface { + XXX_DiscardUnknown() +} + // DiscardUnknown recursively discards all unknown fields from this message // and all embedded messages. // @@ -49,9 +55,202 @@ import ( // For proto2 messages, the unknown fields of message extensions are only // discarded from messages that have been accessed via GetExtension. func DiscardUnknown(m Message) { + if m, ok := m.(generatedDiscarder); ok { + m.XXX_DiscardUnknown() + return + } + // TODO: Dynamically populate a InternalMessageInfo for legacy messages, + // but the master branch has no implementation for InternalMessageInfo, + // so it would be more work to replicate that approach. discardLegacy(m) } +// DiscardUnknown recursively discards all unknown fields. +func (a *InternalMessageInfo) DiscardUnknown(m Message) { + di := atomicLoadDiscardInfo(&a.discard) + if di == nil { + di = getDiscardInfo(reflect.TypeOf(m).Elem()) + atomicStoreDiscardInfo(&a.discard, di) + } + di.discard(toPointer(&m)) +} + +type discardInfo struct { + typ reflect.Type + + initialized int32 // 0: only typ is valid, 1: everything is valid + lock sync.Mutex + + fields []discardFieldInfo + unrecognized field +} + +type discardFieldInfo struct { + field field // Offset of field, guaranteed to be valid + discard func(src pointer) +} + +var ( + discardInfoMap = map[reflect.Type]*discardInfo{} + discardInfoLock sync.Mutex +) + +func getDiscardInfo(t reflect.Type) *discardInfo { + discardInfoLock.Lock() + defer discardInfoLock.Unlock() + di := discardInfoMap[t] + if di == nil { + di = &discardInfo{typ: t} + discardInfoMap[t] = di + } + return di +} + +func (di *discardInfo) discard(src pointer) { + if src.isNil() { + return // Nothing to do. + } + + if atomic.LoadInt32(&di.initialized) == 0 { + di.computeDiscardInfo() + } + + for _, fi := range di.fields { + sfp := src.offset(fi.field) + fi.discard(sfp) + } + + // For proto2 messages, only discard unknown fields in message extensions + // that have been accessed via GetExtension. + if em, err := extendable(src.asPointerTo(di.typ).Interface()); err == nil { + // Ignore lock since DiscardUnknown is not concurrency safe. + emm, _ := em.extensionsRead() + for _, mx := range emm { + if m, ok := mx.value.(Message); ok { + DiscardUnknown(m) + } + } + } + + if di.unrecognized.IsValid() { + *src.offset(di.unrecognized).toBytes() = nil + } +} + +func (di *discardInfo) computeDiscardInfo() { + di.lock.Lock() + defer di.lock.Unlock() + if di.initialized != 0 { + return + } + t := di.typ + n := t.NumField() + + for i := 0; i < n; i++ { + f := t.Field(i) + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + + dfi := discardFieldInfo{field: toField(&f)} + tf := f.Type + + // Unwrap tf to get its most basic type. + var isPointer, isSlice bool + if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { + isSlice = true + tf = tf.Elem() + } + if tf.Kind() == reflect.Ptr { + isPointer = true + tf = tf.Elem() + } + if isPointer && isSlice && tf.Kind() != reflect.Struct { + panic(fmt.Sprintf("%v.%s cannot be a slice of pointers to primitive types", t, f.Name)) + } + + switch tf.Kind() { + case reflect.Struct: + switch { + case !isPointer: + panic(fmt.Sprintf("%v.%s cannot be a direct struct value", t, f.Name)) + case isSlice: // E.g., []*pb.T + discardInfo := getDiscardInfo(tf) + dfi.discard = func(src pointer) { + sps := src.getPointerSlice() + for _, sp := range sps { + if !sp.isNil() { + discardInfo.discard(sp) + } + } + } + default: // E.g., *pb.T + discardInfo := getDiscardInfo(tf) + dfi.discard = func(src pointer) { + sp := src.getPointer() + if !sp.isNil() { + discardInfo.discard(sp) + } + } + } + case reflect.Map: + switch { + case isPointer || isSlice: + panic(fmt.Sprintf("%v.%s cannot be a pointer to a map or a slice of map values", t, f.Name)) + default: // E.g., map[K]V + if tf.Elem().Kind() == reflect.Ptr { // Proto struct (e.g., *T) + dfi.discard = func(src pointer) { + sm := src.asPointerTo(tf).Elem() + if sm.Len() == 0 { + return + } + for _, key := range sm.MapKeys() { + val := sm.MapIndex(key) + DiscardUnknown(val.Interface().(Message)) + } + } + } else { + dfi.discard = func(pointer) {} // Noop + } + } + case reflect.Interface: + // Must be oneof field. + switch { + case isPointer || isSlice: + panic(fmt.Sprintf("%v.%s cannot be a pointer to a interface or a slice of interface values", t, f.Name)) + default: // E.g., interface{} + // TODO: Make this faster? + dfi.discard = func(src pointer) { + su := src.asPointerTo(tf).Elem() + if !su.IsNil() { + sv := su.Elem().Elem().Field(0) + if sv.Kind() == reflect.Ptr && sv.IsNil() { + return + } + switch sv.Type().Kind() { + case reflect.Ptr: // Proto struct (e.g., *T) + DiscardUnknown(sv.Interface().(Message)) + } + } + } + } + default: + continue + } + di.fields = append(di.fields, dfi) + } + + di.unrecognized = invalidField + if f, ok := t.FieldByName("XXX_unrecognized"); ok { + if f.Type != reflect.TypeOf([]byte{}) { + panic("expected XXX_unrecognized to be of type []byte") + } + di.unrecognized = toField(&f) + } + + atomic.StoreInt32(&di.initialized, 1) +} + func discardLegacy(m Message) { v := reflect.ValueOf(m) if v.Kind() != reflect.Ptr || v.IsNil() { @@ -139,7 +338,7 @@ func discardLegacy(m Message) { // For proto2 messages, only discard unknown fields in message extensions // that have been accessed via GetExtension. - if em, ok := extendable(m); ok { + if em, err := extendable(m); err == nil { // Ignore lock since discardLegacy is not concurrency safe. emm, _ := em.extensionsRead() for _, mx := range emm { diff --git a/vendor/github.com/gogo/protobuf/proto/duration_gogo.go b/vendor/github.com/gogo/protobuf/proto/duration_gogo.go index 18e2a5f77654f..e748e1730e1cc 100644 --- a/vendor/github.com/gogo/protobuf/proto/duration_gogo.go +++ b/vendor/github.com/gogo/protobuf/proto/duration_gogo.go @@ -47,157 +47,3 @@ func (*duration) String() string { return "duration" } func init() { RegisterType((*duration)(nil), "gogo.protobuf.proto.duration") } - -func (o *Buffer) decDuration() (time.Duration, error) { - b, err := o.DecodeRawBytes(true) - if err != nil { - return 0, err - } - dproto := &duration{} - if err := Unmarshal(b, dproto); err != nil { - return 0, err - } - return durationFromProto(dproto) -} - -func (o *Buffer) dec_duration(p *Properties, base structPointer) error { - d, err := o.decDuration() - if err != nil { - return err - } - word64_Set(structPointer_Word64(base, p.field), o, uint64(d)) - return nil -} - -func (o *Buffer) dec_ref_duration(p *Properties, base structPointer) error { - d, err := o.decDuration() - if err != nil { - return err - } - word64Val_Set(structPointer_Word64Val(base, p.field), o, uint64(d)) - return nil -} - -func (o *Buffer) dec_slice_duration(p *Properties, base structPointer) error { - d, err := o.decDuration() - if err != nil { - return err - } - newBas := appendStructPointer(base, p.field, reflect.SliceOf(reflect.PtrTo(durationType))) - var zero field - setPtrCustomType(newBas, zero, &d) - return nil -} - -func (o *Buffer) dec_slice_ref_duration(p *Properties, base structPointer) error { - d, err := o.decDuration() - if err != nil { - return err - } - structPointer_Word64Slice(base, p.field).Append(uint64(d)) - return nil -} - -func size_duration(p *Properties, base structPointer) (n int) { - structp := structPointer_GetStructPointer(base, p.field) - if structPointer_IsNil(structp) { - return 0 - } - dur := structPointer_Interface(structp, durationType).(*time.Duration) - d := durationProto(*dur) - size := Size(d) - return size + sizeVarint(uint64(size)) + len(p.tagcode) -} - -func (o *Buffer) enc_duration(p *Properties, base structPointer) error { - structp := structPointer_GetStructPointer(base, p.field) - if structPointer_IsNil(structp) { - return ErrNil - } - dur := structPointer_Interface(structp, durationType).(*time.Duration) - d := durationProto(*dur) - data, err := Marshal(d) - if err != nil { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - return nil -} - -func size_ref_duration(p *Properties, base structPointer) (n int) { - dur := structPointer_InterfaceAt(base, p.field, durationType).(*time.Duration) - d := durationProto(*dur) - size := Size(d) - return size + sizeVarint(uint64(size)) + len(p.tagcode) -} - -func (o *Buffer) enc_ref_duration(p *Properties, base structPointer) error { - dur := structPointer_InterfaceAt(base, p.field, durationType).(*time.Duration) - d := durationProto(*dur) - data, err := Marshal(d) - if err != nil { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - return nil -} - -func size_slice_duration(p *Properties, base structPointer) (n int) { - pdurs := structPointer_InterfaceAt(base, p.field, reflect.SliceOf(reflect.PtrTo(durationType))).(*[]*time.Duration) - durs := *pdurs - for i := 0; i < len(durs); i++ { - if durs[i] == nil { - return 0 - } - dproto := durationProto(*durs[i]) - size := Size(dproto) - n += len(p.tagcode) + size + sizeVarint(uint64(size)) - } - return n -} - -func (o *Buffer) enc_slice_duration(p *Properties, base structPointer) error { - pdurs := structPointer_InterfaceAt(base, p.field, reflect.SliceOf(reflect.PtrTo(durationType))).(*[]*time.Duration) - durs := *pdurs - for i := 0; i < len(durs); i++ { - if durs[i] == nil { - return errRepeatedHasNil - } - dproto := durationProto(*durs[i]) - data, err := Marshal(dproto) - if err != nil { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - } - return nil -} - -func size_slice_ref_duration(p *Properties, base structPointer) (n int) { - pdurs := structPointer_InterfaceAt(base, p.field, reflect.SliceOf(durationType)).(*[]time.Duration) - durs := *pdurs - for i := 0; i < len(durs); i++ { - dproto := durationProto(durs[i]) - size := Size(dproto) - n += len(p.tagcode) + size + sizeVarint(uint64(size)) - } - return n -} - -func (o *Buffer) enc_slice_ref_duration(p *Properties, base structPointer) error { - pdurs := structPointer_InterfaceAt(base, p.field, reflect.SliceOf(durationType)).(*[]time.Duration) - durs := *pdurs - for i := 0; i < len(durs); i++ { - dproto := durationProto(durs[i]) - data, err := Marshal(dproto) - if err != nil { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - } - return nil -} diff --git a/vendor/github.com/gogo/protobuf/proto/encode.go b/vendor/github.com/gogo/protobuf/proto/encode.go index 8b84d1b22d4c0..9581ccd3042fc 100644 --- a/vendor/github.com/gogo/protobuf/proto/encode.go +++ b/vendor/github.com/gogo/protobuf/proto/encode.go @@ -37,28 +37,9 @@ package proto import ( "errors" - "fmt" "reflect" - "sort" ) -// RequiredNotSetError is the error returned if Marshal is called with -// a protocol buffer struct whose required fields have not -// all been initialized. It is also the error returned if Unmarshal is -// called with an encoded protocol buffer that does not include all the -// required fields. -// -// When printed, RequiredNotSetError reports the first unset required field in a -// message. If the field cannot be precisely determined, it is reported as -// "{Unknown}". -type RequiredNotSetError struct { - field string -} - -func (e *RequiredNotSetError) Error() string { - return fmt.Sprintf("proto: required field %q not set", e.field) -} - var ( // errRepeatedHasNil is the error returned if Marshal is called with // a struct with a repeated field containing a nil element. @@ -82,10 +63,6 @@ var ( const maxVarintBytes = 10 // maximum length of a varint -// maxMarshalSize is the largest allowed size of an encoded protobuf, -// since C++ and Java use signed int32s for the size. -const maxMarshalSize = 1<<31 - 1 - // EncodeVarint returns the varint encoding of x. // This is the format for the // int32, int64, uint32, uint64, bool, and enum @@ -119,18 +96,27 @@ func (p *Buffer) EncodeVarint(x uint64) error { // SizeVarint returns the varint encoding size of an integer. func SizeVarint(x uint64) int { - return sizeVarint(x) -} - -func sizeVarint(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + switch { + case x < 1<<7: + return 1 + case x < 1<<14: + return 2 + case x < 1<<21: + return 3 + case x < 1<<28: + return 4 + case x < 1<<35: + return 5 + case x < 1<<42: + return 6 + case x < 1<<49: + return 7 + case x < 1<<56: + return 8 + case x < 1<<63: + return 9 + } + return 10 } // EncodeFixed64 writes a 64-bit integer to the Buffer. @@ -149,10 +135,6 @@ func (p *Buffer) EncodeFixed64(x uint64) error { return nil } -func sizeFixed64(x uint64) int { - return 8 -} - // EncodeFixed32 writes a 32-bit integer to the Buffer. // This is the format for the // fixed32, sfixed32, and float protocol buffer types. @@ -165,20 +147,12 @@ func (p *Buffer) EncodeFixed32(x uint64) error { return nil } -func sizeFixed32(x uint64) int { - return 4 -} - // EncodeZigzag64 writes a zigzag-encoded 64-bit integer // to the Buffer. // This is the format used for the sint64 protocol buffer type. func (p *Buffer) EncodeZigzag64(x uint64) error { // use signed number to get arithmetic right shift. - return p.EncodeVarint((x << 1) ^ uint64((int64(x) >> 63))) -} - -func sizeZigzag64(x uint64) int { - return sizeVarint((x << 1) ^ uint64((int64(x) >> 63))) + return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } // EncodeZigzag32 writes a zigzag-encoded 32-bit integer @@ -189,10 +163,6 @@ func (p *Buffer) EncodeZigzag32(x uint64) error { return p.EncodeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31)))) } -func sizeZigzag32(x uint64) int { - return sizeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31)))) -} - // EncodeRawBytes writes a count-delimited byte buffer to the Buffer. // This is the format used for the bytes protocol buffer // type and for embedded messages. @@ -202,11 +172,6 @@ func (p *Buffer) EncodeRawBytes(b []byte) error { return nil } -func sizeRawBytes(b []byte) int { - return sizeVarint(uint64(len(b))) + - len(b) -} - // EncodeStringBytes writes an encoded string to the Buffer. // This is the format used for the proto2 string type. func (p *Buffer) EncodeStringBytes(s string) error { @@ -215,319 +180,19 @@ func (p *Buffer) EncodeStringBytes(s string) error { return nil } -func sizeStringBytes(s string) int { - return sizeVarint(uint64(len(s))) + - len(s) -} - // Marshaler is the interface representing objects that can marshal themselves. type Marshaler interface { Marshal() ([]byte, error) } -// Marshal takes the protocol buffer -// and encodes it into the wire format, returning the data. -func Marshal(pb Message) ([]byte, error) { - // Can the object marshal itself? - if m, ok := pb.(Marshaler); ok { - return m.Marshal() - } - p := NewBuffer(nil) - err := p.Marshal(pb) - if p.buf == nil && err == nil { - // Return a non-nil slice on success. - return []byte{}, nil - } - return p.buf, err -} - // EncodeMessage writes the protocol buffer to the Buffer, // prefixed by a varint-encoded length. func (p *Buffer) EncodeMessage(pb Message) error { - t, base, err := getbase(pb) - if structPointer_IsNil(base) { - return ErrNil - } - if err == nil { - var state errorState - err = p.enc_len_struct(GetProperties(t.Elem()), base, &state) - } - return err -} - -// Marshal takes the protocol buffer -// and encodes it into the wire format, writing the result to the -// Buffer. -func (p *Buffer) Marshal(pb Message) error { - // Can the object marshal itself? - if m, ok := pb.(Marshaler); ok { - data, err := m.Marshal() - p.buf = append(p.buf, data...) - return err - } - - t, base, err := getbase(pb) - if structPointer_IsNil(base) { - return ErrNil - } - if err == nil { - err = p.enc_struct(GetProperties(t.Elem()), base) - } - - if collectStats { - (stats).Encode++ // Parens are to work around a goimports bug. - } - - if len(p.buf) > maxMarshalSize { - return ErrTooLarge - } - return err -} - -// Size returns the encoded size of a protocol buffer. -func Size(pb Message) (n int) { - // Can the object marshal itself? If so, Size is slow. - // TODO: add Size to Marshaler, or add a Sizer interface. - if m, ok := pb.(Marshaler); ok { - b, _ := m.Marshal() - return len(b) - } - - t, base, err := getbase(pb) - if structPointer_IsNil(base) { - return 0 - } - if err == nil { - n = size_struct(GetProperties(t.Elem()), base) - } - - if collectStats { - (stats).Size++ // Parens are to work around a goimports bug. - } - - return -} - -// Individual type encoders. - -// Encode a bool. -func (o *Buffer) enc_bool(p *Properties, base structPointer) error { - v := *structPointer_Bool(base, p.field) - if v == nil { - return ErrNil - } - x := 0 - if *v { - x = 1 - } - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, uint64(x)) - return nil -} - -func (o *Buffer) enc_proto3_bool(p *Properties, base structPointer) error { - v := *structPointer_BoolVal(base, p.field) - if !v { - return ErrNil - } - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, 1) - return nil -} - -func size_bool(p *Properties, base structPointer) int { - v := *structPointer_Bool(base, p.field) - if v == nil { - return 0 - } - return len(p.tagcode) + 1 // each bool takes exactly one byte -} - -func size_proto3_bool(p *Properties, base structPointer) int { - v := *structPointer_BoolVal(base, p.field) - if !v && !p.oneof { - return 0 - } - return len(p.tagcode) + 1 // each bool takes exactly one byte -} - -// Encode an int32. -func (o *Buffer) enc_int32(p *Properties, base structPointer) error { - v := structPointer_Word32(base, p.field) - if word32_IsNil(v) { - return ErrNil - } - x := int32(word32_Get(v)) // permit sign extension to use full 64-bit range - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, uint64(x)) - return nil -} - -func (o *Buffer) enc_proto3_int32(p *Properties, base structPointer) error { - v := structPointer_Word32Val(base, p.field) - x := int32(word32Val_Get(v)) // permit sign extension to use full 64-bit range - if x == 0 { - return ErrNil - } - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, uint64(x)) - return nil -} - -func size_int32(p *Properties, base structPointer) (n int) { - v := structPointer_Word32(base, p.field) - if word32_IsNil(v) { - return 0 - } - x := int32(word32_Get(v)) // permit sign extension to use full 64-bit range - n += len(p.tagcode) - n += p.valSize(uint64(x)) - return -} - -func size_proto3_int32(p *Properties, base structPointer) (n int) { - v := structPointer_Word32Val(base, p.field) - x := int32(word32Val_Get(v)) // permit sign extension to use full 64-bit range - if x == 0 && !p.oneof { - return 0 - } - n += len(p.tagcode) - n += p.valSize(uint64(x)) - return -} - -// Encode a uint32. -// Exactly the same as int32, except for no sign extension. -func (o *Buffer) enc_uint32(p *Properties, base structPointer) error { - v := structPointer_Word32(base, p.field) - if word32_IsNil(v) { - return ErrNil - } - x := word32_Get(v) - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, uint64(x)) - return nil -} - -func (o *Buffer) enc_proto3_uint32(p *Properties, base structPointer) error { - v := structPointer_Word32Val(base, p.field) - x := word32Val_Get(v) - if x == 0 { - return ErrNil - } - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, uint64(x)) - return nil -} - -func size_uint32(p *Properties, base structPointer) (n int) { - v := structPointer_Word32(base, p.field) - if word32_IsNil(v) { - return 0 - } - x := word32_Get(v) - n += len(p.tagcode) - n += p.valSize(uint64(x)) - return -} - -func size_proto3_uint32(p *Properties, base structPointer) (n int) { - v := structPointer_Word32Val(base, p.field) - x := word32Val_Get(v) - if x == 0 && !p.oneof { - return 0 - } - n += len(p.tagcode) - n += p.valSize(uint64(x)) - return -} - -// Encode an int64. -func (o *Buffer) enc_int64(p *Properties, base structPointer) error { - v := structPointer_Word64(base, p.field) - if word64_IsNil(v) { - return ErrNil - } - x := word64_Get(v) - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, x) - return nil -} - -func (o *Buffer) enc_proto3_int64(p *Properties, base structPointer) error { - v := structPointer_Word64Val(base, p.field) - x := word64Val_Get(v) - if x == 0 { - return ErrNil - } - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, x) - return nil -} - -func size_int64(p *Properties, base structPointer) (n int) { - v := structPointer_Word64(base, p.field) - if word64_IsNil(v) { - return 0 - } - x := word64_Get(v) - n += len(p.tagcode) - n += p.valSize(x) - return -} - -func size_proto3_int64(p *Properties, base structPointer) (n int) { - v := structPointer_Word64Val(base, p.field) - x := word64Val_Get(v) - if x == 0 && !p.oneof { - return 0 - } - n += len(p.tagcode) - n += p.valSize(x) - return -} - -// Encode a string. -func (o *Buffer) enc_string(p *Properties, base structPointer) error { - v := *structPointer_String(base, p.field) - if v == nil { - return ErrNil - } - x := *v - o.buf = append(o.buf, p.tagcode...) - o.EncodeStringBytes(x) - return nil -} - -func (o *Buffer) enc_proto3_string(p *Properties, base structPointer) error { - v := *structPointer_StringVal(base, p.field) - if v == "" { - return ErrNil - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeStringBytes(v) - return nil -} - -func size_string(p *Properties, base structPointer) (n int) { - v := *structPointer_String(base, p.field) - if v == nil { - return 0 - } - x := *v - n += len(p.tagcode) - n += sizeStringBytes(x) - return -} - -func size_proto3_string(p *Properties, base structPointer) (n int) { - v := *structPointer_StringVal(base, p.field) - if v == "" && !p.oneof { - return 0 - } - n += len(p.tagcode) - n += sizeStringBytes(v) - return + siz := Size(pb) + sizVar := SizeVarint(uint64(siz)) + p.grow(siz + sizVar) + p.EncodeVarint(uint64(siz)) + return p.Marshal(pb) } // All protocol buffer fields are nillable, but be careful. @@ -538,825 +203,3 @@ func isNil(v reflect.Value) bool { } return false } - -// Encode a message struct. -func (o *Buffer) enc_struct_message(p *Properties, base structPointer) error { - var state errorState - structp := structPointer_GetStructPointer(base, p.field) - if structPointer_IsNil(structp) { - return ErrNil - } - - // Can the object marshal itself? - if p.isMarshaler { - m := structPointer_Interface(structp, p.stype).(Marshaler) - data, err := m.Marshal() - if err != nil && !state.shouldContinue(err, nil) { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - return state.err - } - - o.buf = append(o.buf, p.tagcode...) - return o.enc_len_struct(p.sprop, structp, &state) -} - -func size_struct_message(p *Properties, base structPointer) int { - structp := structPointer_GetStructPointer(base, p.field) - if structPointer_IsNil(structp) { - return 0 - } - - // Can the object marshal itself? - if p.isMarshaler { - m := structPointer_Interface(structp, p.stype).(Marshaler) - data, _ := m.Marshal() - n0 := len(p.tagcode) - n1 := sizeRawBytes(data) - return n0 + n1 - } - - n0 := len(p.tagcode) - n1 := size_struct(p.sprop, structp) - n2 := sizeVarint(uint64(n1)) // size of encoded length - return n0 + n1 + n2 -} - -// Encode a group struct. -func (o *Buffer) enc_struct_group(p *Properties, base structPointer) error { - var state errorState - b := structPointer_GetStructPointer(base, p.field) - if structPointer_IsNil(b) { - return ErrNil - } - - o.EncodeVarint(uint64((p.Tag << 3) | WireStartGroup)) - err := o.enc_struct(p.sprop, b) - if err != nil && !state.shouldContinue(err, nil) { - return err - } - o.EncodeVarint(uint64((p.Tag << 3) | WireEndGroup)) - return state.err -} - -func size_struct_group(p *Properties, base structPointer) (n int) { - b := structPointer_GetStructPointer(base, p.field) - if structPointer_IsNil(b) { - return 0 - } - - n += sizeVarint(uint64((p.Tag << 3) | WireStartGroup)) - n += size_struct(p.sprop, b) - n += sizeVarint(uint64((p.Tag << 3) | WireEndGroup)) - return -} - -// Encode a slice of bools ([]bool). -func (o *Buffer) enc_slice_bool(p *Properties, base structPointer) error { - s := *structPointer_BoolSlice(base, p.field) - l := len(s) - if l == 0 { - return ErrNil - } - for _, x := range s { - o.buf = append(o.buf, p.tagcode...) - v := uint64(0) - if x { - v = 1 - } - p.valEnc(o, v) - } - return nil -} - -func size_slice_bool(p *Properties, base structPointer) int { - s := *structPointer_BoolSlice(base, p.field) - l := len(s) - if l == 0 { - return 0 - } - return l * (len(p.tagcode) + 1) // each bool takes exactly one byte -} - -// Encode a slice of bools ([]bool) in packed format. -func (o *Buffer) enc_slice_packed_bool(p *Properties, base structPointer) error { - s := *structPointer_BoolSlice(base, p.field) - l := len(s) - if l == 0 { - return ErrNil - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeVarint(uint64(l)) // each bool takes exactly one byte - for _, x := range s { - v := uint64(0) - if x { - v = 1 - } - p.valEnc(o, v) - } - return nil -} - -func size_slice_packed_bool(p *Properties, base structPointer) (n int) { - s := *structPointer_BoolSlice(base, p.field) - l := len(s) - if l == 0 { - return 0 - } - n += len(p.tagcode) - n += sizeVarint(uint64(l)) - n += l // each bool takes exactly one byte - return -} - -// Encode a slice of bytes ([]byte). -func (o *Buffer) enc_slice_byte(p *Properties, base structPointer) error { - s := *structPointer_Bytes(base, p.field) - if s == nil { - return ErrNil - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(s) - return nil -} - -func (o *Buffer) enc_proto3_slice_byte(p *Properties, base structPointer) error { - s := *structPointer_Bytes(base, p.field) - if len(s) == 0 { - return ErrNil - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(s) - return nil -} - -func size_slice_byte(p *Properties, base structPointer) (n int) { - s := *structPointer_Bytes(base, p.field) - if s == nil && !p.oneof { - return 0 - } - n += len(p.tagcode) - n += sizeRawBytes(s) - return -} - -func size_proto3_slice_byte(p *Properties, base structPointer) (n int) { - s := *structPointer_Bytes(base, p.field) - if len(s) == 0 && !p.oneof { - return 0 - } - n += len(p.tagcode) - n += sizeRawBytes(s) - return -} - -// Encode a slice of int32s ([]int32). -func (o *Buffer) enc_slice_int32(p *Properties, base structPointer) error { - s := structPointer_Word32Slice(base, p.field) - l := s.Len() - if l == 0 { - return ErrNil - } - for i := 0; i < l; i++ { - o.buf = append(o.buf, p.tagcode...) - x := int32(s.Index(i)) // permit sign extension to use full 64-bit range - p.valEnc(o, uint64(x)) - } - return nil -} - -func size_slice_int32(p *Properties, base structPointer) (n int) { - s := structPointer_Word32Slice(base, p.field) - l := s.Len() - if l == 0 { - return 0 - } - for i := 0; i < l; i++ { - n += len(p.tagcode) - x := int32(s.Index(i)) // permit sign extension to use full 64-bit range - n += p.valSize(uint64(x)) - } - return -} - -// Encode a slice of int32s ([]int32) in packed format. -func (o *Buffer) enc_slice_packed_int32(p *Properties, base structPointer) error { - s := structPointer_Word32Slice(base, p.field) - l := s.Len() - if l == 0 { - return ErrNil - } - // TODO: Reuse a Buffer. - buf := NewBuffer(nil) - for i := 0; i < l; i++ { - x := int32(s.Index(i)) // permit sign extension to use full 64-bit range - p.valEnc(buf, uint64(x)) - } - - o.buf = append(o.buf, p.tagcode...) - o.EncodeVarint(uint64(len(buf.buf))) - o.buf = append(o.buf, buf.buf...) - return nil -} - -func size_slice_packed_int32(p *Properties, base structPointer) (n int) { - s := structPointer_Word32Slice(base, p.field) - l := s.Len() - if l == 0 { - return 0 - } - var bufSize int - for i := 0; i < l; i++ { - x := int32(s.Index(i)) // permit sign extension to use full 64-bit range - bufSize += p.valSize(uint64(x)) - } - - n += len(p.tagcode) - n += sizeVarint(uint64(bufSize)) - n += bufSize - return -} - -// Encode a slice of uint32s ([]uint32). -// Exactly the same as int32, except for no sign extension. -func (o *Buffer) enc_slice_uint32(p *Properties, base structPointer) error { - s := structPointer_Word32Slice(base, p.field) - l := s.Len() - if l == 0 { - return ErrNil - } - for i := 0; i < l; i++ { - o.buf = append(o.buf, p.tagcode...) - x := s.Index(i) - p.valEnc(o, uint64(x)) - } - return nil -} - -func size_slice_uint32(p *Properties, base structPointer) (n int) { - s := structPointer_Word32Slice(base, p.field) - l := s.Len() - if l == 0 { - return 0 - } - for i := 0; i < l; i++ { - n += len(p.tagcode) - x := s.Index(i) - n += p.valSize(uint64(x)) - } - return -} - -// Encode a slice of uint32s ([]uint32) in packed format. -// Exactly the same as int32, except for no sign extension. -func (o *Buffer) enc_slice_packed_uint32(p *Properties, base structPointer) error { - s := structPointer_Word32Slice(base, p.field) - l := s.Len() - if l == 0 { - return ErrNil - } - // TODO: Reuse a Buffer. - buf := NewBuffer(nil) - for i := 0; i < l; i++ { - p.valEnc(buf, uint64(s.Index(i))) - } - - o.buf = append(o.buf, p.tagcode...) - o.EncodeVarint(uint64(len(buf.buf))) - o.buf = append(o.buf, buf.buf...) - return nil -} - -func size_slice_packed_uint32(p *Properties, base structPointer) (n int) { - s := structPointer_Word32Slice(base, p.field) - l := s.Len() - if l == 0 { - return 0 - } - var bufSize int - for i := 0; i < l; i++ { - bufSize += p.valSize(uint64(s.Index(i))) - } - - n += len(p.tagcode) - n += sizeVarint(uint64(bufSize)) - n += bufSize - return -} - -// Encode a slice of int64s ([]int64). -func (o *Buffer) enc_slice_int64(p *Properties, base structPointer) error { - s := structPointer_Word64Slice(base, p.field) - l := s.Len() - if l == 0 { - return ErrNil - } - for i := 0; i < l; i++ { - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, s.Index(i)) - } - return nil -} - -func size_slice_int64(p *Properties, base structPointer) (n int) { - s := structPointer_Word64Slice(base, p.field) - l := s.Len() - if l == 0 { - return 0 - } - for i := 0; i < l; i++ { - n += len(p.tagcode) - n += p.valSize(s.Index(i)) - } - return -} - -// Encode a slice of int64s ([]int64) in packed format. -func (o *Buffer) enc_slice_packed_int64(p *Properties, base structPointer) error { - s := structPointer_Word64Slice(base, p.field) - l := s.Len() - if l == 0 { - return ErrNil - } - // TODO: Reuse a Buffer. - buf := NewBuffer(nil) - for i := 0; i < l; i++ { - p.valEnc(buf, s.Index(i)) - } - - o.buf = append(o.buf, p.tagcode...) - o.EncodeVarint(uint64(len(buf.buf))) - o.buf = append(o.buf, buf.buf...) - return nil -} - -func size_slice_packed_int64(p *Properties, base structPointer) (n int) { - s := structPointer_Word64Slice(base, p.field) - l := s.Len() - if l == 0 { - return 0 - } - var bufSize int - for i := 0; i < l; i++ { - bufSize += p.valSize(s.Index(i)) - } - - n += len(p.tagcode) - n += sizeVarint(uint64(bufSize)) - n += bufSize - return -} - -// Encode a slice of slice of bytes ([][]byte). -func (o *Buffer) enc_slice_slice_byte(p *Properties, base structPointer) error { - ss := *structPointer_BytesSlice(base, p.field) - l := len(ss) - if l == 0 { - return ErrNil - } - for i := 0; i < l; i++ { - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(ss[i]) - } - return nil -} - -func size_slice_slice_byte(p *Properties, base structPointer) (n int) { - ss := *structPointer_BytesSlice(base, p.field) - l := len(ss) - if l == 0 { - return 0 - } - n += l * len(p.tagcode) - for i := 0; i < l; i++ { - n += sizeRawBytes(ss[i]) - } - return -} - -// Encode a slice of strings ([]string). -func (o *Buffer) enc_slice_string(p *Properties, base structPointer) error { - ss := *structPointer_StringSlice(base, p.field) - l := len(ss) - for i := 0; i < l; i++ { - o.buf = append(o.buf, p.tagcode...) - o.EncodeStringBytes(ss[i]) - } - return nil -} - -func size_slice_string(p *Properties, base structPointer) (n int) { - ss := *structPointer_StringSlice(base, p.field) - l := len(ss) - n += l * len(p.tagcode) - for i := 0; i < l; i++ { - n += sizeStringBytes(ss[i]) - } - return -} - -// Encode a slice of message structs ([]*struct). -func (o *Buffer) enc_slice_struct_message(p *Properties, base structPointer) error { - var state errorState - s := structPointer_StructPointerSlice(base, p.field) - l := s.Len() - - for i := 0; i < l; i++ { - structp := s.Index(i) - if structPointer_IsNil(structp) { - return errRepeatedHasNil - } - - // Can the object marshal itself? - if p.isMarshaler { - m := structPointer_Interface(structp, p.stype).(Marshaler) - data, err := m.Marshal() - if err != nil && !state.shouldContinue(err, nil) { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - continue - } - - o.buf = append(o.buf, p.tagcode...) - err := o.enc_len_struct(p.sprop, structp, &state) - if err != nil && !state.shouldContinue(err, nil) { - if err == ErrNil { - return errRepeatedHasNil - } - return err - } - } - return state.err -} - -func size_slice_struct_message(p *Properties, base structPointer) (n int) { - s := structPointer_StructPointerSlice(base, p.field) - l := s.Len() - n += l * len(p.tagcode) - for i := 0; i < l; i++ { - structp := s.Index(i) - if structPointer_IsNil(structp) { - return // return the size up to this point - } - - // Can the object marshal itself? - if p.isMarshaler { - m := structPointer_Interface(structp, p.stype).(Marshaler) - data, _ := m.Marshal() - n += sizeRawBytes(data) - continue - } - - n0 := size_struct(p.sprop, structp) - n1 := sizeVarint(uint64(n0)) // size of encoded length - n += n0 + n1 - } - return -} - -// Encode a slice of group structs ([]*struct). -func (o *Buffer) enc_slice_struct_group(p *Properties, base structPointer) error { - var state errorState - s := structPointer_StructPointerSlice(base, p.field) - l := s.Len() - - for i := 0; i < l; i++ { - b := s.Index(i) - if structPointer_IsNil(b) { - return errRepeatedHasNil - } - - o.EncodeVarint(uint64((p.Tag << 3) | WireStartGroup)) - - err := o.enc_struct(p.sprop, b) - - if err != nil && !state.shouldContinue(err, nil) { - if err == ErrNil { - return errRepeatedHasNil - } - return err - } - - o.EncodeVarint(uint64((p.Tag << 3) | WireEndGroup)) - } - return state.err -} - -func size_slice_struct_group(p *Properties, base structPointer) (n int) { - s := structPointer_StructPointerSlice(base, p.field) - l := s.Len() - - n += l * sizeVarint(uint64((p.Tag<<3)|WireStartGroup)) - n += l * sizeVarint(uint64((p.Tag<<3)|WireEndGroup)) - for i := 0; i < l; i++ { - b := s.Index(i) - if structPointer_IsNil(b) { - return // return size up to this point - } - - n += size_struct(p.sprop, b) - } - return -} - -// Encode an extension map. -func (o *Buffer) enc_map(p *Properties, base structPointer) error { - exts := structPointer_ExtMap(base, p.field) - if err := encodeExtensionsMap(*exts); err != nil { - return err - } - - return o.enc_map_body(*exts) -} - -func (o *Buffer) enc_exts(p *Properties, base structPointer) error { - exts := structPointer_Extensions(base, p.field) - - v, mu := exts.extensionsRead() - if v == nil { - return nil - } - - mu.Lock() - defer mu.Unlock() - if err := encodeExtensionsMap(v); err != nil { - return err - } - - return o.enc_map_body(v) -} - -func (o *Buffer) enc_map_body(v map[int32]Extension) error { - // Fast-path for common cases: zero or one extensions. - if len(v) <= 1 { - for _, e := range v { - o.buf = append(o.buf, e.enc...) - } - return nil - } - - // Sort keys to provide a deterministic encoding. - keys := make([]int, 0, len(v)) - for k := range v { - keys = append(keys, int(k)) - } - sort.Ints(keys) - - for _, k := range keys { - o.buf = append(o.buf, v[int32(k)].enc...) - } - return nil -} - -func size_map(p *Properties, base structPointer) int { - v := structPointer_ExtMap(base, p.field) - return extensionsMapSize(*v) -} - -func size_exts(p *Properties, base structPointer) int { - v := structPointer_Extensions(base, p.field) - return extensionsSize(v) -} - -// Encode a map field. -func (o *Buffer) enc_new_map(p *Properties, base structPointer) error { - var state errorState // XXX: or do we need to plumb this through? - - /* - A map defined as - map map_field = N; - is encoded in the same way as - message MapFieldEntry { - key_type key = 1; - value_type value = 2; - } - repeated MapFieldEntry map_field = N; - */ - - v := structPointer_NewAt(base, p.field, p.mtype).Elem() // map[K]V - if v.Len() == 0 { - return nil - } - - keycopy, valcopy, keybase, valbase := mapEncodeScratch(p.mtype) - - enc := func() error { - if err := p.mkeyprop.enc(o, p.mkeyprop, keybase); err != nil { - return err - } - if err := p.mvalprop.enc(o, p.mvalprop, valbase); err != nil && err != ErrNil { - return err - } - return nil - } - - // Don't sort map keys. It is not required by the spec, and C++ doesn't do it. - for _, key := range v.MapKeys() { - val := v.MapIndex(key) - - keycopy.Set(key) - valcopy.Set(val) - - o.buf = append(o.buf, p.tagcode...) - if err := o.enc_len_thing(enc, &state); err != nil { - return err - } - } - return nil -} - -func size_new_map(p *Properties, base structPointer) int { - v := structPointer_NewAt(base, p.field, p.mtype).Elem() // map[K]V - - keycopy, valcopy, keybase, valbase := mapEncodeScratch(p.mtype) - - n := 0 - for _, key := range v.MapKeys() { - val := v.MapIndex(key) - keycopy.Set(key) - valcopy.Set(val) - - // Tag codes for key and val are the responsibility of the sub-sizer. - keysize := p.mkeyprop.size(p.mkeyprop, keybase) - valsize := p.mvalprop.size(p.mvalprop, valbase) - entry := keysize + valsize - // Add on tag code and length of map entry itself. - n += len(p.tagcode) + sizeVarint(uint64(entry)) + entry - } - return n -} - -// mapEncodeScratch returns a new reflect.Value matching the map's value type, -// and a structPointer suitable for passing to an encoder or sizer. -func mapEncodeScratch(mapType reflect.Type) (keycopy, valcopy reflect.Value, keybase, valbase structPointer) { - // Prepare addressable doubly-indirect placeholders for the key and value types. - // This is needed because the element-type encoders expect **T, but the map iteration produces T. - - keycopy = reflect.New(mapType.Key()).Elem() // addressable K - keyptr := reflect.New(reflect.PtrTo(keycopy.Type())).Elem() // addressable *K - keyptr.Set(keycopy.Addr()) // - keybase = toStructPointer(keyptr.Addr()) // **K - - // Value types are more varied and require special handling. - switch mapType.Elem().Kind() { - case reflect.Slice: - // []byte - var dummy []byte - valcopy = reflect.ValueOf(&dummy).Elem() // addressable []byte - valbase = toStructPointer(valcopy.Addr()) - case reflect.Ptr: - // message; the generated field type is map[K]*Msg (so V is *Msg), - // so we only need one level of indirection. - valcopy = reflect.New(mapType.Elem()).Elem() // addressable V - valbase = toStructPointer(valcopy.Addr()) - default: - // everything else - valcopy = reflect.New(mapType.Elem()).Elem() // addressable V - valptr := reflect.New(reflect.PtrTo(valcopy.Type())).Elem() // addressable *V - valptr.Set(valcopy.Addr()) // - valbase = toStructPointer(valptr.Addr()) // **V - } - return -} - -// Encode a struct. -func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error { - var state errorState - // Encode fields in tag order so that decoders may use optimizations - // that depend on the ordering. - // https://developers.google.com/protocol-buffers/docs/encoding#order - for _, i := range prop.order { - p := prop.Prop[i] - if p.enc != nil { - err := p.enc(o, p, base) - if err != nil { - if err == ErrNil { - if p.Required && state.err == nil { - state.err = &RequiredNotSetError{p.Name} - } - } else if err == errRepeatedHasNil { - // Give more context to nil values in repeated fields. - return errors.New("repeated field " + p.OrigName + " has nil element") - } else if !state.shouldContinue(err, p) { - return err - } - } - if len(o.buf) > maxMarshalSize { - return ErrTooLarge - } - } - } - - // Do oneof fields. - if prop.oneofMarshaler != nil { - m := structPointer_Interface(base, prop.stype).(Message) - if err := prop.oneofMarshaler(m, o); err == ErrNil { - return errOneofHasNil - } else if err != nil { - return err - } - } - - // Add unrecognized fields at the end. - if prop.unrecField.IsValid() { - v := *structPointer_Bytes(base, prop.unrecField) - if len(o.buf)+len(v) > maxMarshalSize { - return ErrTooLarge - } - if len(v) > 0 { - o.buf = append(o.buf, v...) - } - } - - return state.err -} - -func size_struct(prop *StructProperties, base structPointer) (n int) { - for _, i := range prop.order { - p := prop.Prop[i] - if p.size != nil { - n += p.size(p, base) - } - } - - // Add unrecognized fields at the end. - if prop.unrecField.IsValid() { - v := *structPointer_Bytes(base, prop.unrecField) - n += len(v) - } - - // Factor in any oneof fields. - if prop.oneofSizer != nil { - m := structPointer_Interface(base, prop.stype).(Message) - n += prop.oneofSizer(m) - } - - return -} - -var zeroes [20]byte // longer than any conceivable sizeVarint - -// Encode a struct, preceded by its encoded length (as a varint). -func (o *Buffer) enc_len_struct(prop *StructProperties, base structPointer, state *errorState) error { - return o.enc_len_thing(func() error { return o.enc_struct(prop, base) }, state) -} - -// Encode something, preceded by its encoded length (as a varint). -func (o *Buffer) enc_len_thing(enc func() error, state *errorState) error { - iLen := len(o.buf) - o.buf = append(o.buf, 0, 0, 0, 0) // reserve four bytes for length - iMsg := len(o.buf) - err := enc() - if err != nil && !state.shouldContinue(err, nil) { - return err - } - lMsg := len(o.buf) - iMsg - lLen := sizeVarint(uint64(lMsg)) - switch x := lLen - (iMsg - iLen); { - case x > 0: // actual length is x bytes larger than the space we reserved - // Move msg x bytes right. - o.buf = append(o.buf, zeroes[:x]...) - copy(o.buf[iMsg+x:], o.buf[iMsg:iMsg+lMsg]) - case x < 0: // actual length is x bytes smaller than the space we reserved - // Move msg x bytes left. - copy(o.buf[iMsg+x:], o.buf[iMsg:iMsg+lMsg]) - o.buf = o.buf[:len(o.buf)+x] // x is negative - } - // Encode the length in the reserved space. - o.buf = o.buf[:iLen] - o.EncodeVarint(uint64(lMsg)) - o.buf = o.buf[:len(o.buf)+lMsg] - return state.err -} - -// errorState maintains the first error that occurs and updates that error -// with additional context. -type errorState struct { - err error -} - -// shouldContinue reports whether encoding should continue upon encountering the -// given error. If the error is RequiredNotSetError, shouldContinue returns true -// and, if this is the first appearance of that error, remembers it for future -// reporting. -// -// If prop is not nil, it may update any error with additional context about the -// field with the error. -func (s *errorState) shouldContinue(err error, prop *Properties) bool { - // Ignore unset required fields. - reqNotSet, ok := err.(*RequiredNotSetError) - if !ok { - return false - } - if s.err == nil { - if prop != nil { - err = &RequiredNotSetError{prop.Name + "." + reqNotSet.field} - } - s.err = err - } - return true -} diff --git a/vendor/github.com/gogo/protobuf/proto/encode_gogo.go b/vendor/github.com/gogo/protobuf/proto/encode_gogo.go index 32111b7f41d79..0f5fb173e9fd0 100644 --- a/vendor/github.com/gogo/protobuf/proto/encode_gogo.go +++ b/vendor/github.com/gogo/protobuf/proto/encode_gogo.go @@ -3,11 +3,6 @@ // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // -// Go support for Protocol Buffers - Google's data interchange format -// -// Copyright 2010 The Go Authors. All rights reserved. -// http://github.com/golang/protobuf/ -// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -18,9 +13,6 @@ // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -36,315 +28,6 @@ package proto -import ( - "reflect" -) - func NewRequiredNotSetError(field string) *RequiredNotSetError { return &RequiredNotSetError{field} } - -type Sizer interface { - Size() int -} - -func (o *Buffer) enc_ext_slice_byte(p *Properties, base structPointer) error { - s := *structPointer_Bytes(base, p.field) - if s == nil { - return ErrNil - } - o.buf = append(o.buf, s...) - return nil -} - -func size_ext_slice_byte(p *Properties, base structPointer) (n int) { - s := *structPointer_Bytes(base, p.field) - if s == nil { - return 0 - } - n += len(s) - return -} - -// Encode a reference to bool pointer. -func (o *Buffer) enc_ref_bool(p *Properties, base structPointer) error { - v := *structPointer_BoolVal(base, p.field) - x := 0 - if v { - x = 1 - } - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, uint64(x)) - return nil -} - -func size_ref_bool(p *Properties, base structPointer) int { - return len(p.tagcode) + 1 // each bool takes exactly one byte -} - -// Encode a reference to int32 pointer. -func (o *Buffer) enc_ref_int32(p *Properties, base structPointer) error { - v := structPointer_Word32Val(base, p.field) - x := int32(word32Val_Get(v)) - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, uint64(x)) - return nil -} - -func size_ref_int32(p *Properties, base structPointer) (n int) { - v := structPointer_Word32Val(base, p.field) - x := int32(word32Val_Get(v)) - n += len(p.tagcode) - n += p.valSize(uint64(x)) - return -} - -func (o *Buffer) enc_ref_uint32(p *Properties, base structPointer) error { - v := structPointer_Word32Val(base, p.field) - x := word32Val_Get(v) - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, uint64(x)) - return nil -} - -func size_ref_uint32(p *Properties, base structPointer) (n int) { - v := structPointer_Word32Val(base, p.field) - x := word32Val_Get(v) - n += len(p.tagcode) - n += p.valSize(uint64(x)) - return -} - -// Encode a reference to an int64 pointer. -func (o *Buffer) enc_ref_int64(p *Properties, base structPointer) error { - v := structPointer_Word64Val(base, p.field) - x := word64Val_Get(v) - o.buf = append(o.buf, p.tagcode...) - p.valEnc(o, x) - return nil -} - -func size_ref_int64(p *Properties, base structPointer) (n int) { - v := structPointer_Word64Val(base, p.field) - x := word64Val_Get(v) - n += len(p.tagcode) - n += p.valSize(x) - return -} - -// Encode a reference to a string pointer. -func (o *Buffer) enc_ref_string(p *Properties, base structPointer) error { - v := *structPointer_StringVal(base, p.field) - o.buf = append(o.buf, p.tagcode...) - o.EncodeStringBytes(v) - return nil -} - -func size_ref_string(p *Properties, base structPointer) (n int) { - v := *structPointer_StringVal(base, p.field) - n += len(p.tagcode) - n += sizeStringBytes(v) - return -} - -// Encode a reference to a message struct. -func (o *Buffer) enc_ref_struct_message(p *Properties, base structPointer) error { - var state errorState - structp := structPointer_GetRefStructPointer(base, p.field) - if structPointer_IsNil(structp) { - return ErrNil - } - - // Can the object marshal itself? - if p.isMarshaler { - m := structPointer_Interface(structp, p.stype).(Marshaler) - data, err := m.Marshal() - if err != nil && !state.shouldContinue(err, nil) { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - return nil - } - - o.buf = append(o.buf, p.tagcode...) - return o.enc_len_struct(p.sprop, structp, &state) -} - -//TODO this is only copied, please fix this -func size_ref_struct_message(p *Properties, base structPointer) int { - structp := structPointer_GetRefStructPointer(base, p.field) - if structPointer_IsNil(structp) { - return 0 - } - - // Can the object marshal itself? - if p.isMarshaler { - m := structPointer_Interface(structp, p.stype).(Marshaler) - data, _ := m.Marshal() - n0 := len(p.tagcode) - n1 := sizeRawBytes(data) - return n0 + n1 - } - - n0 := len(p.tagcode) - n1 := size_struct(p.sprop, structp) - n2 := sizeVarint(uint64(n1)) // size of encoded length - return n0 + n1 + n2 -} - -// Encode a slice of references to message struct pointers ([]struct). -func (o *Buffer) enc_slice_ref_struct_message(p *Properties, base structPointer) error { - var state errorState - ss := structPointer_StructRefSlice(base, p.field, p.stype.Size()) - l := ss.Len() - for i := 0; i < l; i++ { - structp := ss.Index(i) - if structPointer_IsNil(structp) { - return errRepeatedHasNil - } - - // Can the object marshal itself? - if p.isMarshaler { - m := structPointer_Interface(structp, p.stype).(Marshaler) - data, err := m.Marshal() - if err != nil && !state.shouldContinue(err, nil) { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - continue - } - - o.buf = append(o.buf, p.tagcode...) - err := o.enc_len_struct(p.sprop, structp, &state) - if err != nil && !state.shouldContinue(err, nil) { - if err == ErrNil { - return errRepeatedHasNil - } - return err - } - - } - return state.err -} - -//TODO this is only copied, please fix this -func size_slice_ref_struct_message(p *Properties, base structPointer) (n int) { - ss := structPointer_StructRefSlice(base, p.field, p.stype.Size()) - l := ss.Len() - n += l * len(p.tagcode) - for i := 0; i < l; i++ { - structp := ss.Index(i) - if structPointer_IsNil(structp) { - return // return the size up to this point - } - - // Can the object marshal itself? - if p.isMarshaler { - m := structPointer_Interface(structp, p.stype).(Marshaler) - data, _ := m.Marshal() - n += len(p.tagcode) - n += sizeRawBytes(data) - continue - } - - n0 := size_struct(p.sprop, structp) - n1 := sizeVarint(uint64(n0)) // size of encoded length - n += n0 + n1 - } - return -} - -func (o *Buffer) enc_custom_bytes(p *Properties, base structPointer) error { - i := structPointer_InterfaceRef(base, p.field, p.ctype) - if i == nil { - return ErrNil - } - custom := i.(Marshaler) - data, err := custom.Marshal() - if err != nil { - return err - } - if data == nil { - return ErrNil - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - return nil -} - -func size_custom_bytes(p *Properties, base structPointer) (n int) { - n += len(p.tagcode) - i := structPointer_InterfaceRef(base, p.field, p.ctype) - if i == nil { - return 0 - } - custom := i.(Marshaler) - data, _ := custom.Marshal() - n += sizeRawBytes(data) - return -} - -func (o *Buffer) enc_custom_ref_bytes(p *Properties, base structPointer) error { - custom := structPointer_InterfaceAt(base, p.field, p.ctype).(Marshaler) - data, err := custom.Marshal() - if err != nil { - return err - } - if data == nil { - return ErrNil - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - return nil -} - -func size_custom_ref_bytes(p *Properties, base structPointer) (n int) { - n += len(p.tagcode) - i := structPointer_InterfaceAt(base, p.field, p.ctype) - if i == nil { - return 0 - } - custom := i.(Marshaler) - data, _ := custom.Marshal() - n += sizeRawBytes(data) - return -} - -func (o *Buffer) enc_custom_slice_bytes(p *Properties, base structPointer) error { - inter := structPointer_InterfaceRef(base, p.field, p.ctype) - if inter == nil { - return ErrNil - } - slice := reflect.ValueOf(inter) - l := slice.Len() - for i := 0; i < l; i++ { - v := slice.Index(i) - custom := v.Interface().(Marshaler) - data, err := custom.Marshal() - if err != nil { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - } - return nil -} - -func size_custom_slice_bytes(p *Properties, base structPointer) (n int) { - inter := structPointer_InterfaceRef(base, p.field, p.ctype) - if inter == nil { - return 0 - } - slice := reflect.ValueOf(inter) - l := slice.Len() - n += l * len(p.tagcode) - for i := 0; i < l; i++ { - v := slice.Index(i) - custom := v.Interface().(Marshaler) - data, _ := custom.Marshal() - n += sizeRawBytes(data) - } - return -} diff --git a/vendor/github.com/gogo/protobuf/proto/equal.go b/vendor/github.com/gogo/protobuf/proto/equal.go index 2ed1cf596664d..d4db5a1c14577 100644 --- a/vendor/github.com/gogo/protobuf/proto/equal.go +++ b/vendor/github.com/gogo/protobuf/proto/equal.go @@ -109,15 +109,6 @@ func equalStruct(v1, v2 reflect.Value) bool { // set/unset mismatch return false } - b1, ok := f1.Interface().(raw) - if ok { - b2 := f2.Interface().(raw) - // RawMessage - if !bytes.Equal(b1.Bytes(), b2.Bytes()) { - return false - } - continue - } f1, f2 = f1.Elem(), f2.Elem() } if !equalAny(f1, f2, sprop.Prop[i]) { @@ -146,11 +137,7 @@ func equalStruct(v1, v2 reflect.Value) bool { u1 := uf.Bytes() u2 := v2.FieldByName("XXX_unrecognized").Bytes() - if !bytes.Equal(u1, u2) { - return false - } - - return true + return bytes.Equal(u1, u2) } // v1 and v2 are known to have the same type. @@ -261,6 +248,15 @@ func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool { m1, m2 := e1.value, e2.value + if m1 == nil && m2 == nil { + // Both have only encoded form. + if bytes.Equal(e1.enc, e2.enc) { + continue + } + // The bytes are different, but the extensions might still be + // equal. We need to decode them to compare. + } + if m1 != nil && m2 != nil { // Both are unencoded. if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { @@ -276,8 +272,12 @@ func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool { desc = m[extNum] } if desc == nil { + // If both have only encoded form and the bytes are the same, + // it is handled above. We get here when the bytes are different. + // We don't know how to decode it, so just compare them as byte + // slices. log.Printf("proto: don't know how to compare extension %d of %v", extNum, base) - continue + return false } var err error if m1 == nil { diff --git a/vendor/github.com/gogo/protobuf/proto/extensions.go b/vendor/github.com/gogo/protobuf/proto/extensions.go index 0dfcb538e8f1b..341c6f57f5210 100644 --- a/vendor/github.com/gogo/protobuf/proto/extensions.go +++ b/vendor/github.com/gogo/protobuf/proto/extensions.go @@ -38,6 +38,7 @@ package proto import ( "errors" "fmt" + "io" "reflect" "strconv" "sync" @@ -69,12 +70,6 @@ type extendableProtoV1 interface { ExtensionMap() map[int32]Extension } -type extensionsBytes interface { - Message - ExtensionRangeArray() []ExtensionRange - GetExtensions() *[]byte -} - // extensionAdapter is a wrapper around extendableProtoV1 that implements extendableProto. type extensionAdapter struct { extendableProtoV1 @@ -97,14 +92,31 @@ func (n notLocker) Unlock() {} // extendable returns the extendableProto interface for the given generated proto message. // If the proto message has the old extension format, it returns a wrapper that implements // the extendableProto interface. -func extendable(p interface{}) (extendableProto, bool) { - if ep, ok := p.(extendableProto); ok { - return ep, ok - } - if ep, ok := p.(extendableProtoV1); ok { - return extensionAdapter{ep}, ok +func extendable(p interface{}) (extendableProto, error) { + switch p := p.(type) { + case extendableProto: + if isNilPtr(p) { + return nil, fmt.Errorf("proto: nil %T is not extendable", p) + } + return p, nil + case extendableProtoV1: + if isNilPtr(p) { + return nil, fmt.Errorf("proto: nil %T is not extendable", p) + } + return extensionAdapter{p}, nil + case extensionsBytes: + return slowExtensionAdapter{p}, nil } - return nil, false + // Don't allocate a specific error containing %T: + // this is the hot path for Clone and MarshalText. + return nil, errNotExtendable +} + +var errNotExtendable = errors.New("proto: not an extendable proto.Message") + +func isNilPtr(x interface{}) bool { + v := reflect.ValueOf(x) + return v.Kind() == reflect.Ptr && v.IsNil() } // XXX_InternalExtensions is an internal representation of proto extensions. @@ -149,16 +161,6 @@ func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Loc return e.p.extensionMap, &e.p.mu } -type extensionRange interface { - Message - ExtensionRangeArray() []ExtensionRange -} - -var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem() -var extendableProtoV1Type = reflect.TypeOf((*extendableProtoV1)(nil)).Elem() -var extendableBytesType = reflect.TypeOf((*extensionsBytes)(nil)).Elem() -var extensionRangeType = reflect.TypeOf((*extensionRange)(nil)).Elem() - // ExtensionDesc represents an extension specification. // Used in generated code from the protocol compiler. type ExtensionDesc struct { @@ -198,8 +200,8 @@ func SetRawExtension(base Message, id int32, b []byte) { *ext = append(*ext, b...) return } - epb, ok := extendable(base) - if !ok { + epb, err := extendable(base) + if err != nil { return } extmap := epb.extensionsWrite() @@ -207,7 +209,7 @@ func SetRawExtension(base Message, id int32, b []byte) { } // isExtensionField returns true iff the given field number is in an extension range. -func isExtensionField(pb extensionRange, field int32) bool { +func isExtensionField(pb extendableProto, field int32) bool { for _, er := range pb.ExtensionRangeArray() { if er.Start <= field && field <= er.End { return true @@ -223,8 +225,11 @@ func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error { if ea, ok := pbi.(extensionAdapter); ok { pbi = ea.extendableProtoV1 } + if ea, ok := pbi.(slowExtensionAdapter); ok { + pbi = ea.extensionsBytes + } if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b { - return errors.New("proto: bad extended type; " + b.String() + " does not extend " + a.String()) + return fmt.Errorf("proto: bad extended type; %v does not extend %v", b, a) } // Check the range. if !isExtensionField(pb, extension.Field) { @@ -269,80 +274,6 @@ func extensionProperties(ed *ExtensionDesc) *Properties { return prop } -// encode encodes any unmarshaled (unencoded) extensions in e. -func encodeExtensions(e *XXX_InternalExtensions) error { - m, mu := e.extensionsRead() - if m == nil { - return nil // fast path - } - mu.Lock() - defer mu.Unlock() - return encodeExtensionsMap(m) -} - -// encode encodes any unmarshaled (unencoded) extensions in e. -func encodeExtensionsMap(m map[int32]Extension) error { - for k, e := range m { - if e.value == nil || e.desc == nil { - // Extension is only in its encoded form. - continue - } - - // We don't skip extensions that have an encoded form set, - // because the extension value may have been mutated after - // the last time this function was called. - - et := reflect.TypeOf(e.desc.ExtensionType) - props := extensionProperties(e.desc) - - p := NewBuffer(nil) - // If e.value has type T, the encoder expects a *struct{ X T }. - // Pass a *T with a zero field and hope it all works out. - x := reflect.New(et) - x.Elem().Set(reflect.ValueOf(e.value)) - if err := props.enc(p, props, toStructPointer(x)); err != nil { - return err - } - e.enc = p.buf - m[k] = e - } - return nil -} - -func extensionsSize(e *XXX_InternalExtensions) (n int) { - m, mu := e.extensionsRead() - if m == nil { - return 0 - } - mu.Lock() - defer mu.Unlock() - return extensionsMapSize(m) -} - -func extensionsMapSize(m map[int32]Extension) (n int) { - for _, e := range m { - if e.value == nil || e.desc == nil { - // Extension is only in its encoded form. - n += len(e.enc) - continue - } - - // We don't skip extensions that have an encoded form set, - // because the extension value may have been mutated after - // the last time this function was called. - - et := reflect.TypeOf(e.desc.ExtensionType) - props := extensionProperties(e.desc) - - // If e.value has type T, the encoder expects a *struct{ X T }. - // Pass a *T with a zero field and hope it all works out. - x := reflect.New(et) - x.Elem().Set(reflect.ValueOf(e.value)) - n += props.size(props, toStructPointer(x)) - } - return -} - // HasExtension returns whether the given extension is present in pb. func HasExtension(pb Message, extension *ExtensionDesc) bool { if epb, doki := pb.(extensionsBytes); doki { @@ -366,8 +297,8 @@ func HasExtension(pb Message, extension *ExtensionDesc) bool { return false } // TODO: Check types, field numbers, etc.? - epb, ok := extendable(pb) - if !ok { + epb, err := extendable(pb) + if err != nil { return false } extmap, mu := epb.extensionsRead() @@ -375,46 +306,26 @@ func HasExtension(pb Message, extension *ExtensionDesc) bool { return false } mu.Lock() - _, ok = extmap[extension.Field] + _, ok := extmap[extension.Field] mu.Unlock() return ok } -func deleteExtension(pb extensionsBytes, theFieldNum int32, offset int) int { - ext := pb.GetExtensions() - for offset < len(*ext) { - tag, n1 := DecodeVarint((*ext)[offset:]) - fieldNum := int32(tag >> 3) - wireType := int(tag & 0x7) - n2, err := size((*ext)[offset+n1:], wireType) - if err != nil { - panic(err) - } - newOffset := offset + n1 + n2 - if fieldNum == theFieldNum { - *ext = append((*ext)[:offset], (*ext)[newOffset:]...) - return offset - } - offset = newOffset - } - return -1 -} - // ClearExtension removes the given extension from pb. func ClearExtension(pb Message, extension *ExtensionDesc) { clearExtension(pb, extension.Field) } func clearExtension(pb Message, fieldNum int32) { - if epb, doki := pb.(extensionsBytes); doki { + if epb, ok := pb.(extensionsBytes); ok { offset := 0 for offset != -1 { offset = deleteExtension(epb, fieldNum, offset) } return } - epb, ok := extendable(pb) - if !ok { + epb, err := extendable(pb) + if err != nil { return } // TODO: Check types, field numbers, etc.? @@ -422,39 +333,33 @@ func clearExtension(pb Message, fieldNum int32) { delete(extmap, fieldNum) } -// GetExtension parses and returns the given extension of pb. -// If the extension is not present and has no default value it returns ErrMissingExtension. +// GetExtension retrieves a proto2 extended field from pb. +// +// If the descriptor is type complete (i.e., ExtensionDesc.ExtensionType is non-nil), +// then GetExtension parses the encoded field and returns a Go value of the specified type. +// If the field is not present, then the default value is returned (if one is specified), +// otherwise ErrMissingExtension is reported. +// +// If the descriptor is not type complete (i.e., ExtensionDesc.ExtensionType is nil), +// then GetExtension returns the raw encoded bytes of the field extension. func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) { if epb, doki := pb.(extensionsBytes); doki { ext := epb.GetExtensions() - o := 0 - for o < len(*ext) { - tag, n := DecodeVarint((*ext)[o:]) - fieldNum := int32(tag >> 3) - wireType := int(tag & 0x7) - l, err := size((*ext)[o+n:], wireType) - if err != nil { - return nil, err - } - if int32(fieldNum) == extension.Field { - v, err := decodeExtension((*ext)[o:o+n+l], extension) - if err != nil { - return nil, err - } - return v, nil - } - o += n + l - } - return defaultExtensionValue(extension) + return decodeExtensionFromBytes(extension, *ext) } - epb, ok := extendable(pb) - if !ok { - return nil, errors.New("proto: not an extendable proto") - } - if err := checkExtensionTypes(epb, extension); err != nil { + + epb, err := extendable(pb) + if err != nil { return nil, err } + if extension.ExtendedType != nil { + // can only check type if this is a complete descriptor + if cerr := checkExtensionTypes(epb, extension); cerr != nil { + return nil, cerr + } + } + emap, mu := epb.extensionsRead() if emap == nil { return defaultExtensionValue(extension) @@ -479,6 +384,11 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) { return e.value, nil } + if extension.ExtensionType == nil { + // incomplete descriptor + return e.enc, nil + } + v, err := decodeExtension(e.enc, extension) if err != nil { return nil, err @@ -496,6 +406,11 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) { // defaultExtensionValue returns the default value for extension. // If no default for an extension is defined ErrMissingExtension is returned. func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) { + if extension.ExtensionType == nil { + // incomplete descriptor, so no default + return nil, ErrMissingExtension + } + t := reflect.TypeOf(extension.ExtensionType) props := extensionProperties(extension) @@ -530,31 +445,28 @@ func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) { // decodeExtension decodes an extension encoded in b. func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { - o := NewBuffer(b) - t := reflect.TypeOf(extension.ExtensionType) - - props := extensionProperties(extension) + unmarshal := typeUnmarshaler(t, extension.Tag) // t is a pointer to a struct, pointer to basic type or a slice. - // Allocate a "field" to store the pointer/slice itself; the - // pointer/slice will be stored here. We pass - // the address of this field to props.dec. - // This passes a zero field and a *t and lets props.dec - // interpret it as a *struct{ x t }. + // Allocate space to store the pointer/slice. value := reflect.New(t).Elem() + var err error for { - // Discard wire type and field number varint. It isn't needed. - if _, err := o.DecodeVarint(); err != nil { - return nil, err + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF } + b = b[n:] + wire := int(x) & 7 - if err := props.dec(o, props, toStructPointer(value.Addr())); err != nil { + b, err = unmarshal(b, valToPointer(value.Addr()), wire) + if err != nil { return nil, err } - if o.index >= len(o.buf) { + if len(b) == 0 { break } } @@ -564,9 +476,13 @@ func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { // GetExtensions returns a slice of the extensions present in pb that are also listed in es. // The returned slice has the same length as es; missing extensions will appear as nil elements. func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) { + epb, err := extendable(pb) + if err != nil { + return nil, err + } extensions = make([]interface{}, len(es)) for i, e := range es { - extensions[i], err = GetExtension(pb, e) + extensions[i], err = GetExtension(epb, e) if err == ErrMissingExtension { err = nil } @@ -581,9 +497,9 @@ func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, e // For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing // just the Field field, which defines the extension's field number. func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) { - epb, ok := extendable(pb) - if !ok { - return nil, fmt.Errorf("proto: %T is not an extendable proto.Message", pb) + epb, err := extendable(pb) + if err != nil { + return nil, err } registeredExtensions := RegisteredExtensions(pb) @@ -610,30 +526,26 @@ func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) { // SetExtension sets the specified extension of pb to the specified value. func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error { - if epb, doki := pb.(extensionsBytes); doki { + if epb, ok := pb.(extensionsBytes); ok { ClearExtension(pb, extension) - ext := epb.GetExtensions() - et := reflect.TypeOf(extension.ExtensionType) - props := extensionProperties(extension) - p := NewBuffer(nil) - x := reflect.New(et) - x.Elem().Set(reflect.ValueOf(value)) - if err := props.enc(p, props, toStructPointer(x)); err != nil { + newb, err := encodeExtension(extension, value) + if err != nil { return err } - *ext = append(*ext, p.buf...) + bb := epb.GetExtensions() + *bb = append(*bb, newb...) return nil } - epb, ok := extendable(pb) - if !ok { - return errors.New("proto: not an extendable proto") + epb, err := extendable(pb) + if err != nil { + return err } if err := checkExtensionTypes(epb, extension); err != nil { return err } typ := reflect.TypeOf(extension.ExtensionType) if typ != reflect.TypeOf(value) { - return errors.New("proto: bad extension value type") + return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", value, extension.ExtensionType) } // nil extension values need to be caught early, because the // encoder can't distinguish an ErrNil due to a nil extension @@ -656,8 +568,8 @@ func ClearAllExtensions(pb Message) { *ext = []byte{} return } - epb, ok := extendable(pb) - if !ok { + epb, err := extendable(pb) + if err != nil { return } m := epb.extensionsWrite() diff --git a/vendor/github.com/gogo/protobuf/proto/extensions_gogo.go b/vendor/github.com/gogo/protobuf/proto/extensions_gogo.go index ea6478f009d7f..6f1ae120ece58 100644 --- a/vendor/github.com/gogo/protobuf/proto/extensions_gogo.go +++ b/vendor/github.com/gogo/protobuf/proto/extensions_gogo.go @@ -32,12 +32,36 @@ import ( "bytes" "errors" "fmt" + "io" "reflect" "sort" "strings" "sync" ) +type extensionsBytes interface { + Message + ExtensionRangeArray() []ExtensionRange + GetExtensions() *[]byte +} + +type slowExtensionAdapter struct { + extensionsBytes +} + +func (s slowExtensionAdapter) extensionsWrite() map[int32]Extension { + panic("Please report a bug to github.com/gogo/protobuf if you see this message: Writing extensions is not supported for extensions stored in a byte slice field.") +} + +func (s slowExtensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) { + b := s.GetExtensions() + m, err := BytesToExtensionsMap(*b) + if err != nil { + panic(err) + } + return m, notLocker{} +} + func GetBoolExtension(pb Message, extension *ExtensionDesc, ifnotset bool) bool { if reflect.ValueOf(pb).IsNil() { return ifnotset @@ -56,19 +80,28 @@ func GetBoolExtension(pb Message, extension *ExtensionDesc, ifnotset bool) bool } func (this *Extension) Equal(that *Extension) bool { + if err := this.Encode(); err != nil { + return false + } + if err := that.Encode(); err != nil { + return false + } return bytes.Equal(this.enc, that.enc) } func (this *Extension) Compare(that *Extension) int { + if err := this.Encode(); err != nil { + return 1 + } + if err := that.Encode(); err != nil { + return -1 + } return bytes.Compare(this.enc, that.enc) } func SizeOfInternalExtension(m extendableProto) (n int) { - return SizeOfExtensionMap(m.extensionsWrite()) -} - -func SizeOfExtensionMap(m map[int32]Extension) (n int) { - return extensionsMapSize(m) + info := getMarshalInfo(reflect.TypeOf(m)) + return info.sizeV1Extensions(m.extensionsWrite()) } type sortableMapElem struct { @@ -121,29 +154,48 @@ func EncodeInternalExtension(m extendableProto, data []byte) (n int, err error) return EncodeExtensionMap(m.extensionsWrite(), data) } +func EncodeInternalExtensionBackwards(m extendableProto, data []byte) (n int, err error) { + return EncodeExtensionMapBackwards(m.extensionsWrite(), data) +} + func EncodeExtensionMap(m map[int32]Extension, data []byte) (n int, err error) { - if err := encodeExtensionsMap(m); err != nil { - return 0, err - } - keys := make([]int, 0, len(m)) - for k := range m { - keys = append(keys, int(k)) + o := 0 + for _, e := range m { + if err := e.Encode(); err != nil { + return 0, err + } + n := copy(data[o:], e.enc) + if n != len(e.enc) { + return 0, io.ErrShortBuffer + } + o += n } - sort.Ints(keys) - for _, k := range keys { - n += copy(data[n:], m[int32(k)].enc) + return o, nil +} + +func EncodeExtensionMapBackwards(m map[int32]Extension, data []byte) (n int, err error) { + o := 0 + end := len(data) + for _, e := range m { + if err := e.Encode(); err != nil { + return 0, err + } + n := copy(data[end-len(e.enc):], e.enc) + if n != len(e.enc) { + return 0, io.ErrShortBuffer + } + end -= n + o += n } - return n, nil + return o, nil } func GetRawExtension(m map[int32]Extension, id int32) ([]byte, error) { - if m[id].value == nil || m[id].desc == nil { - return m[id].enc, nil - } - if err := encodeExtensionsMap(m); err != nil { + e := m[id] + if err := e.Encode(); err != nil { return nil, err } - return m[id].enc, nil + return e.enc, nil } func size(buf []byte, wire int) (int, error) { @@ -218,35 +270,58 @@ func AppendExtension(e Message, tag int32, buf []byte) { } } -func encodeExtension(e *Extension) error { - if e.value == nil || e.desc == nil { - // Extension is only in its encoded form. - return nil +func encodeExtension(extension *ExtensionDesc, value interface{}) ([]byte, error) { + u := getMarshalInfo(reflect.TypeOf(extension.ExtendedType)) + ei := u.getExtElemInfo(extension) + v := value + p := toAddrPointer(&v, ei.isptr) + siz := ei.sizer(p, SizeVarint(ei.wiretag)) + buf := make([]byte, 0, siz) + return ei.marshaler(buf, p, ei.wiretag, false) +} + +func decodeExtensionFromBytes(extension *ExtensionDesc, buf []byte) (interface{}, error) { + o := 0 + for o < len(buf) { + tag, n := DecodeVarint((buf)[o:]) + fieldNum := int32(tag >> 3) + wireType := int(tag & 0x7) + if o+n > len(buf) { + return nil, fmt.Errorf("unable to decode extension") + } + l, err := size((buf)[o+n:], wireType) + if err != nil { + return nil, err + } + if int32(fieldNum) == extension.Field { + if o+n+l > len(buf) { + return nil, fmt.Errorf("unable to decode extension") + } + v, err := decodeExtension((buf)[o:o+n+l], extension) + if err != nil { + return nil, err + } + return v, nil + } + o += n + l } - // We don't skip extensions that have an encoded form set, - // because the extension value may have been mutated after - // the last time this function was called. - - et := reflect.TypeOf(e.desc.ExtensionType) - props := extensionProperties(e.desc) - - p := NewBuffer(nil) - // If e.value has type T, the encoder expects a *struct{ X T }. - // Pass a *T with a zero field and hope it all works out. - x := reflect.New(et) - x.Elem().Set(reflect.ValueOf(e.value)) - if err := props.enc(p, props, toStructPointer(x)); err != nil { - return err + return defaultExtensionValue(extension) +} + +func (this *Extension) Encode() error { + if this.enc == nil { + var err error + this.enc, err = encodeExtension(this.desc, this.value) + if err != nil { + return err + } } - e.enc = p.buf return nil } func (this Extension) GoString() string { - if this.enc == nil { - if err := encodeExtension(&this); err != nil { - panic(err) - } + if err := this.Encode(); err != nil { + return fmt.Sprintf("error encoding extension: %v", err) } return fmt.Sprintf("proto.NewExtension(%#v)", this.enc) } @@ -292,3 +367,23 @@ func GetUnsafeExtensionsMap(extendable Message) map[int32]Extension { pb := extendable.(extendableProto) return pb.extensionsWrite() } + +func deleteExtension(pb extensionsBytes, theFieldNum int32, offset int) int { + ext := pb.GetExtensions() + for offset < len(*ext) { + tag, n1 := DecodeVarint((*ext)[offset:]) + fieldNum := int32(tag >> 3) + wireType := int(tag & 0x7) + n2, err := size((*ext)[offset+n1:], wireType) + if err != nil { + panic(err) + } + newOffset := offset + n1 + n2 + if fieldNum == theFieldNum { + *ext = append((*ext)[:offset], (*ext)[newOffset:]...) + return offset + } + offset = newOffset + } + return -1 +} diff --git a/vendor/github.com/gogo/protobuf/proto/lib.go b/vendor/github.com/gogo/protobuf/proto/lib.go index c98d73da49ea2..80db1c155b597 100644 --- a/vendor/github.com/gogo/protobuf/proto/lib.go +++ b/vendor/github.com/gogo/protobuf/proto/lib.go @@ -273,32 +273,73 @@ import ( "sync" ) -// Message is implemented by generated protocol buffer messages. -type Message interface { - Reset() - String() string - ProtoMessage() +// RequiredNotSetError is an error type returned by either Marshal or Unmarshal. +// Marshal reports this when a required field is not initialized. +// Unmarshal reports this when a required field is missing from the wire data. +type RequiredNotSetError struct{ field string } + +func (e *RequiredNotSetError) Error() string { + if e.field == "" { + return fmt.Sprintf("proto: required field not set") + } + return fmt.Sprintf("proto: required field %q not set", e.field) } +func (e *RequiredNotSetError) RequiredNotSet() bool { + return true +} + +type invalidUTF8Error struct{ field string } -// Stats records allocation details about the protocol buffer encoders -// and decoders. Useful for tuning the library itself. -type Stats struct { - Emalloc uint64 // mallocs in encode - Dmalloc uint64 // mallocs in decode - Encode uint64 // number of encodes - Decode uint64 // number of decodes - Chit uint64 // number of cache hits - Cmiss uint64 // number of cache misses - Size uint64 // number of sizes +func (e *invalidUTF8Error) Error() string { + if e.field == "" { + return "proto: invalid UTF-8 detected" + } + return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field) +} +func (e *invalidUTF8Error) InvalidUTF8() bool { + return true } -// Set to true to enable stats collection. -const collectStats = false +// errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8. +// This error should not be exposed to the external API as such errors should +// be recreated with the field information. +var errInvalidUTF8 = &invalidUTF8Error{} -var stats Stats +// isNonFatal reports whether the error is either a RequiredNotSet error +// or a InvalidUTF8 error. +func isNonFatal(err error) bool { + if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() { + return true + } + if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() { + return true + } + return false +} + +type nonFatal struct{ E error } -// GetStats returns a copy of the global Stats structure. -func GetStats() Stats { return stats } +// Merge merges err into nf and reports whether it was successful. +// Otherwise it returns false for any fatal non-nil errors. +func (nf *nonFatal) Merge(err error) (ok bool) { + if err == nil { + return true // not an error + } + if !isNonFatal(err) { + return false // fatal error + } + if nf.E == nil { + nf.E = err // store first instance of non-fatal error + } + return true +} + +// Message is implemented by generated protocol buffer messages. +type Message interface { + Reset() + String() string + ProtoMessage() +} // A Buffer is a buffer manager for marshaling and unmarshaling // protocol buffers. It may be reused between invocations to @@ -309,16 +350,7 @@ type Buffer struct { buf []byte // encode/decode byte stream index int // read point - // pools of basic types to amortize allocation. - bools []bool - uint32s []uint32 - uint64s []uint64 - - // extra pools, only used with pointer_reflect.go - int32s []int32 - int64s []int64 - float32s []float32 - float64s []float64 + deterministic bool } // NewBuffer allocates a new Buffer and initializes its internal data to @@ -343,6 +375,30 @@ func (p *Buffer) SetBuf(s []byte) { // Bytes returns the contents of the Buffer. func (p *Buffer) Bytes() []byte { return p.buf } +// SetDeterministic sets whether to use deterministic serialization. +// +// Deterministic serialization guarantees that for a given binary, equal +// messages will always be serialized to the same bytes. This implies: +// +// - Repeated serialization of a message will return the same bytes. +// - Different processes of the same binary (which may be executing on +// different machines) will serialize equal messages to the same bytes. +// +// Note that the deterministic serialization is NOT canonical across +// languages. It is not guaranteed to remain stable over time. It is unstable +// across different builds with schema changes due to unknown fields. +// Users who need canonical serialization (e.g., persistent storage in a +// canonical form, fingerprinting, etc.) should define their own +// canonicalization specification and implement their own serializer rather +// than relying on this API. +// +// If deterministic serialization is requested, map entries will be sorted +// by keys in lexographical order. This is an implementation detail and +// subject to change. +func (p *Buffer) SetDeterministic(deterministic bool) { + p.deterministic = deterministic +} + /* * Helper routines for simplifying the creation of optional fields of basic type. */ @@ -552,9 +608,11 @@ func SetDefaults(pb Message) { setDefaults(reflect.ValueOf(pb), true, false) } -// v is a pointer to a struct. +// v is a struct. func setDefaults(v reflect.Value, recur, zeros bool) { - v = v.Elem() + if v.Kind() == reflect.Ptr { + v = v.Elem() + } defaultMu.RLock() dm, ok := defaults[v.Type()] @@ -656,8 +714,11 @@ func setDefaults(v reflect.Value, recur, zeros bool) { for _, ni := range dm.nested { f := v.Field(ni) - // f is *T or []*T or map[T]*T + // f is *T or T or []*T or []T switch f.Kind() { + case reflect.Struct: + setDefaults(f, recur, zeros) + case reflect.Ptr: if f.IsNil() { continue @@ -667,7 +728,7 @@ func setDefaults(v reflect.Value, recur, zeros bool) { case reflect.Slice: for i := 0; i < f.Len(); i++ { e := f.Index(i) - if e.IsNil() { + if e.Kind() == reflect.Ptr && e.IsNil() { continue } setDefaults(e, recur, zeros) @@ -739,6 +800,9 @@ func buildDefaultMessage(t reflect.Type) (dm defaultMessage) { func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) { var canHaveDefault bool switch ft.Kind() { + case reflect.Struct: + nestedMessage = true // non-nullable + case reflect.Ptr: if ft.Elem().Kind() == reflect.Struct { nestedMessage = true @@ -748,7 +812,7 @@ func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMes case reflect.Slice: switch ft.Elem().Kind() { - case reflect.Ptr: + case reflect.Ptr, reflect.Struct: nestedMessage = true // repeated message case reflect.Uint8: canHaveDefault = true // bytes field @@ -831,22 +895,12 @@ func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMes return sf, false, nil } +// mapKeys returns a sort.Interface to be used for sorting the map keys. // Map fields may have key types of non-float scalars, strings and enums. -// The easiest way to sort them in some deterministic order is to use fmt. -// If this turns out to be inefficient we can always consider other options, -// such as doing a Schwartzian transform. - func mapKeys(vs []reflect.Value) sort.Interface { - s := mapKeySorter{ - vs: vs, - // default Less function: textual comparison - less: func(a, b reflect.Value) bool { - return fmt.Sprint(a.Interface()) < fmt.Sprint(b.Interface()) - }, - } + s := mapKeySorter{vs: vs} - // Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps; - // numeric keys are sorted numerically. + // Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps. if len(vs) == 0 { return s } @@ -855,6 +909,12 @@ func mapKeys(vs []reflect.Value) sort.Interface { s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() } case reflect.Uint32, reflect.Uint64: s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() } + case reflect.Bool: + s.less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() } // false < true + case reflect.String: + s.less = func(a, b reflect.Value) bool { return a.String() < b.String() } + default: + panic(fmt.Sprintf("unsupported map key type: %v", vs[0].Kind())) } return s @@ -888,10 +948,26 @@ func isProto3Zero(v reflect.Value) bool { return false } -// ProtoPackageIsVersion2 is referenced from generated protocol buffer files -// to assert that that code is compatible with this version of the proto package. -const GoGoProtoPackageIsVersion2 = true +const ( + // ProtoPackageIsVersion3 is referenced from generated protocol buffer files + // to assert that that code is compatible with this version of the proto package. + GoGoProtoPackageIsVersion3 = true + + // ProtoPackageIsVersion2 is referenced from generated protocol buffer files + // to assert that that code is compatible with this version of the proto package. + GoGoProtoPackageIsVersion2 = true -// ProtoPackageIsVersion1 is referenced from generated protocol buffer files -// to assert that that code is compatible with this version of the proto package. -const GoGoProtoPackageIsVersion1 = true + // ProtoPackageIsVersion1 is referenced from generated protocol buffer files + // to assert that that code is compatible with this version of the proto package. + GoGoProtoPackageIsVersion1 = true +) + +// InternalMessageInfo is a type used internally by generated .pb.go files. +// This type is not intended to be used by non-generated code. +// This type is not subject to any compatibility guarantee. +type InternalMessageInfo struct { + marshal *marshalInfo + unmarshal *unmarshalInfo + merge *mergeInfo + discard *discardInfo +} diff --git a/vendor/github.com/gogo/protobuf/proto/lib_gogo.go b/vendor/github.com/gogo/protobuf/proto/lib_gogo.go index 4b4f7c909e6ba..b3aa39190a130 100644 --- a/vendor/github.com/gogo/protobuf/proto/lib_gogo.go +++ b/vendor/github.com/gogo/protobuf/proto/lib_gogo.go @@ -33,6 +33,14 @@ import ( "strconv" ) +type Sizer interface { + Size() int +} + +type ProtoSizer interface { + ProtoSize() int +} + func MarshalJSONEnum(m map[int32]string, value int32) ([]byte, error) { s, ok := m[value] if !ok { diff --git a/vendor/github.com/gogo/protobuf/proto/message_set.go b/vendor/github.com/gogo/protobuf/proto/message_set.go index fd982decd66e4..f48a756761ead 100644 --- a/vendor/github.com/gogo/protobuf/proto/message_set.go +++ b/vendor/github.com/gogo/protobuf/proto/message_set.go @@ -36,12 +36,7 @@ package proto */ import ( - "bytes" - "encoding/json" "errors" - "fmt" - "reflect" - "sort" ) // errNoMessageTypeID occurs when a protocol buffer does not have a message type ID. @@ -94,10 +89,7 @@ func (ms *messageSet) find(pb Message) *_MessageSet_Item { } func (ms *messageSet) Has(pb Message) bool { - if ms.find(pb) != nil { - return true - } - return false + return ms.find(pb) != nil } func (ms *messageSet) Unmarshal(pb Message) error { @@ -147,50 +139,9 @@ func skipVarint(buf []byte) []byte { return buf[i+1:] } -// MarshalMessageSet encodes the extension map represented by m in the message set wire format. -// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option. -func MarshalMessageSet(exts interface{}) ([]byte, error) { - var m map[int32]Extension - switch exts := exts.(type) { - case *XXX_InternalExtensions: - if err := encodeExtensions(exts); err != nil { - return nil, err - } - m, _ = exts.extensionsRead() - case map[int32]Extension: - if err := encodeExtensionsMap(exts); err != nil { - return nil, err - } - m = exts - default: - return nil, errors.New("proto: not an extension map") - } - - // Sort extension IDs to provide a deterministic encoding. - // See also enc_map in encode.go. - ids := make([]int, 0, len(m)) - for id := range m { - ids = append(ids, int(id)) - } - sort.Ints(ids) - - ms := &messageSet{Item: make([]*_MessageSet_Item, 0, len(m))} - for _, id := range ids { - e := m[int32(id)] - // Remove the wire type and field number varint, as well as the length varint. - msg := skipVarint(skipVarint(e.enc)) - - ms.Item = append(ms.Item, &_MessageSet_Item{ - TypeId: Int32(int32(id)), - Message: msg, - }) - } - return Marshal(ms) -} - -// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. -// It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option. -func UnmarshalMessageSet(buf []byte, exts interface{}) error { +// unmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. +// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option. +func unmarshalMessageSet(buf []byte, exts interface{}) error { var m map[int32]Extension switch exts := exts.(type) { case *XXX_InternalExtensions: @@ -228,84 +179,3 @@ func UnmarshalMessageSet(buf []byte, exts interface{}) error { } return nil } - -// MarshalMessageSetJSON encodes the extension map represented by m in JSON format. -// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option. -func MarshalMessageSetJSON(exts interface{}) ([]byte, error) { - var m map[int32]Extension - switch exts := exts.(type) { - case *XXX_InternalExtensions: - m, _ = exts.extensionsRead() - case map[int32]Extension: - m = exts - default: - return nil, errors.New("proto: not an extension map") - } - var b bytes.Buffer - b.WriteByte('{') - - // Process the map in key order for deterministic output. - ids := make([]int32, 0, len(m)) - for id := range m { - ids = append(ids, id) - } - sort.Sort(int32Slice(ids)) // int32Slice defined in text.go - - for i, id := range ids { - ext := m[id] - if i > 0 { - b.WriteByte(',') - } - - msd, ok := messageSetMap[id] - if !ok { - // Unknown type; we can't render it, so skip it. - continue - } - fmt.Fprintf(&b, `"[%s]":`, msd.name) - - x := ext.value - if x == nil { - x = reflect.New(msd.t.Elem()).Interface() - if err := Unmarshal(ext.enc, x.(Message)); err != nil { - return nil, err - } - } - d, err := json.Marshal(x) - if err != nil { - return nil, err - } - b.Write(d) - } - b.WriteByte('}') - return b.Bytes(), nil -} - -// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format. -// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option. -func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error { - // Common-case fast path. - if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) { - return nil - } - - // This is fairly tricky, and it's not clear that it is needed. - return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented") -} - -// A global registry of types that can be used in a MessageSet. - -var messageSetMap = make(map[int32]messageSetDesc) - -type messageSetDesc struct { - t reflect.Type // pointer to struct - name string -} - -// RegisterMessageSetType is called from the generated code. -func RegisterMessageSetType(m Message, fieldNum int32, name string) { - messageSetMap[fieldNum] = messageSetDesc{ - t: reflect.TypeOf(m), - name: name, - } -} diff --git a/vendor/github.com/gogo/protobuf/proto/pointer_reflect.go b/vendor/github.com/gogo/protobuf/proto/pointer_reflect.go index fb512e2e16dce..b6cad90834b31 100644 --- a/vendor/github.com/gogo/protobuf/proto/pointer_reflect.go +++ b/vendor/github.com/gogo/protobuf/proto/pointer_reflect.go @@ -29,7 +29,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +build appengine js +// +build purego appengine js // This file contains an implementation of proto field accesses using package reflect. // It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can @@ -38,32 +38,13 @@ package proto import ( - "math" "reflect" + "sync" ) -// A structPointer is a pointer to a struct. -type structPointer struct { - v reflect.Value -} - -// toStructPointer returns a structPointer equivalent to the given reflect value. -// The reflect value must itself be a pointer to a struct. -func toStructPointer(v reflect.Value) structPointer { - return structPointer{v} -} - -// IsNil reports whether p is nil. -func structPointer_IsNil(p structPointer) bool { - return p.v.IsNil() -} +const unsafeAllowed = false -// Interface returns the struct pointer as an interface value. -func structPointer_Interface(p structPointer, _ reflect.Type) interface{} { - return p.v.Interface() -} - -// A field identifies a field in a struct, accessible from a structPointer. +// A field identifies a field in a struct, accessible from a pointer. // In this implementation, a field is identified by the sequence of field indices // passed to reflect's FieldByIndex. type field []int @@ -76,409 +57,301 @@ func toField(f *reflect.StructField) field { // invalidField is an invalid field identifier. var invalidField = field(nil) +// zeroField is a noop when calling pointer.offset. +var zeroField = field([]int{}) + // IsValid reports whether the field identifier is valid. func (f field) IsValid() bool { return f != nil } -// field returns the given field in the struct as a reflect value. -func structPointer_field(p structPointer, f field) reflect.Value { - // Special case: an extension map entry with a value of type T - // passes a *T to the struct-handling code with a zero field, - // expecting that it will be treated as equivalent to *struct{ X T }, - // which has the same memory layout. We have to handle that case - // specially, because reflect will panic if we call FieldByIndex on a - // non-struct. - if f == nil { - return p.v.Elem() - } - - return p.v.Elem().FieldByIndex(f) +// The pointer type is for the table-driven decoder. +// The implementation here uses a reflect.Value of pointer type to +// create a generic pointer. In pointer_unsafe.go we use unsafe +// instead of reflect to implement the same (but faster) interface. +type pointer struct { + v reflect.Value } -// ifield returns the given field in the struct as an interface value. -func structPointer_ifield(p structPointer, f field) interface{} { - return structPointer_field(p, f).Addr().Interface() +// toPointer converts an interface of pointer type to a pointer +// that points to the same target. +func toPointer(i *Message) pointer { + return pointer{v: reflect.ValueOf(*i)} } -// Bytes returns the address of a []byte field in the struct. -func structPointer_Bytes(p structPointer, f field) *[]byte { - return structPointer_ifield(p, f).(*[]byte) +// toAddrPointer converts an interface to a pointer that points to +// the interface data. +func toAddrPointer(i *interface{}, isptr bool) pointer { + v := reflect.ValueOf(*i) + u := reflect.New(v.Type()) + u.Elem().Set(v) + return pointer{v: u} } -// BytesSlice returns the address of a [][]byte field in the struct. -func structPointer_BytesSlice(p structPointer, f field) *[][]byte { - return structPointer_ifield(p, f).(*[][]byte) +// valToPointer converts v to a pointer. v must be of pointer type. +func valToPointer(v reflect.Value) pointer { + return pointer{v: v} } -// Bool returns the address of a *bool field in the struct. -func structPointer_Bool(p structPointer, f field) **bool { - return structPointer_ifield(p, f).(**bool) +// offset converts from a pointer to a structure to a pointer to +// one of its fields. +func (p pointer) offset(f field) pointer { + return pointer{v: p.v.Elem().FieldByIndex(f).Addr()} } -// BoolVal returns the address of a bool field in the struct. -func structPointer_BoolVal(p structPointer, f field) *bool { - return structPointer_ifield(p, f).(*bool) +func (p pointer) isNil() bool { + return p.v.IsNil() } -// BoolSlice returns the address of a []bool field in the struct. -func structPointer_BoolSlice(p structPointer, f field) *[]bool { - return structPointer_ifield(p, f).(*[]bool) +// grow updates the slice s in place to make it one element longer. +// s must be addressable. +// Returns the (addressable) new element. +func grow(s reflect.Value) reflect.Value { + n, m := s.Len(), s.Cap() + if n < m { + s.SetLen(n + 1) + } else { + s.Set(reflect.Append(s, reflect.Zero(s.Type().Elem()))) + } + return s.Index(n) } -// String returns the address of a *string field in the struct. -func structPointer_String(p structPointer, f field) **string { - return structPointer_ifield(p, f).(**string) +func (p pointer) toInt64() *int64 { + return p.v.Interface().(*int64) } - -// StringVal returns the address of a string field in the struct. -func structPointer_StringVal(p structPointer, f field) *string { - return structPointer_ifield(p, f).(*string) +func (p pointer) toInt64Ptr() **int64 { + return p.v.Interface().(**int64) } - -// StringSlice returns the address of a []string field in the struct. -func structPointer_StringSlice(p structPointer, f field) *[]string { - return structPointer_ifield(p, f).(*[]string) +func (p pointer) toInt64Slice() *[]int64 { + return p.v.Interface().(*[]int64) } -// Extensions returns the address of an extension map field in the struct. -func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions { - return structPointer_ifield(p, f).(*XXX_InternalExtensions) -} +var int32ptr = reflect.TypeOf((*int32)(nil)) -// ExtMap returns the address of an extension map field in the struct. -func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension { - return structPointer_ifield(p, f).(*map[int32]Extension) +func (p pointer) toInt32() *int32 { + return p.v.Convert(int32ptr).Interface().(*int32) } -// NewAt returns the reflect.Value for a pointer to a field in the struct. -func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value { - return structPointer_field(p, f).Addr() +// The toInt32Ptr/Slice methods don't work because of enums. +// Instead, we must use set/get methods for the int32ptr/slice case. +/* + func (p pointer) toInt32Ptr() **int32 { + return p.v.Interface().(**int32) } - -// SetStructPointer writes a *struct field in the struct. -func structPointer_SetStructPointer(p structPointer, f field, q structPointer) { - structPointer_field(p, f).Set(q.v) + func (p pointer) toInt32Slice() *[]int32 { + return p.v.Interface().(*[]int32) } - -// GetStructPointer reads a *struct field in the struct. -func structPointer_GetStructPointer(p structPointer, f field) structPointer { - return structPointer{structPointer_field(p, f)} +*/ +func (p pointer) getInt32Ptr() *int32 { + if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { + // raw int32 type + return p.v.Elem().Interface().(*int32) + } + // an enum + return p.v.Elem().Convert(int32PtrType).Interface().(*int32) +} +func (p pointer) setInt32Ptr(v int32) { + // Allocate value in a *int32. Possibly convert that to a *enum. + // Then assign it to a **int32 or **enum. + // Note: we can convert *int32 to *enum, but we can't convert + // **int32 to **enum! + p.v.Elem().Set(reflect.ValueOf(&v).Convert(p.v.Type().Elem())) +} + +// getInt32Slice copies []int32 from p as a new slice. +// This behavior differs from the implementation in pointer_unsafe.go. +func (p pointer) getInt32Slice() []int32 { + if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { + // raw int32 type + return p.v.Elem().Interface().([]int32) + } + // an enum + // Allocate a []int32, then assign []enum's values into it. + // Note: we can't convert []enum to []int32. + slice := p.v.Elem() + s := make([]int32, slice.Len()) + for i := 0; i < slice.Len(); i++ { + s[i] = int32(slice.Index(i).Int()) + } + return s } -// StructPointerSlice the address of a []*struct field in the struct. -func structPointer_StructPointerSlice(p structPointer, f field) structPointerSlice { - return structPointerSlice{structPointer_field(p, f)} +// setInt32Slice copies []int32 into p as a new slice. +// This behavior differs from the implementation in pointer_unsafe.go. +func (p pointer) setInt32Slice(v []int32) { + if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { + // raw int32 type + p.v.Elem().Set(reflect.ValueOf(v)) + return + } + // an enum + // Allocate a []enum, then assign []int32's values into it. + // Note: we can't convert []enum to []int32. + slice := reflect.MakeSlice(p.v.Type().Elem(), len(v), cap(v)) + for i, x := range v { + slice.Index(i).SetInt(int64(x)) + } + p.v.Elem().Set(slice) } - -// A structPointerSlice represents the address of a slice of pointers to structs -// (themselves messages or groups). That is, v.Type() is *[]*struct{...}. -type structPointerSlice struct { - v reflect.Value +func (p pointer) appendInt32Slice(v int32) { + grow(p.v.Elem()).SetInt(int64(v)) } -func (p structPointerSlice) Len() int { return p.v.Len() } -func (p structPointerSlice) Index(i int) structPointer { return structPointer{p.v.Index(i)} } -func (p structPointerSlice) Append(q structPointer) { - p.v.Set(reflect.Append(p.v, q.v)) +func (p pointer) toUint64() *uint64 { + return p.v.Interface().(*uint64) } - -var ( - int32Type = reflect.TypeOf(int32(0)) - uint32Type = reflect.TypeOf(uint32(0)) - float32Type = reflect.TypeOf(float32(0)) - int64Type = reflect.TypeOf(int64(0)) - uint64Type = reflect.TypeOf(uint64(0)) - float64Type = reflect.TypeOf(float64(0)) -) - -// A word32 represents a field of type *int32, *uint32, *float32, or *enum. -// That is, v.Type() is *int32, *uint32, *float32, or *enum and v is assignable. -type word32 struct { - v reflect.Value +func (p pointer) toUint64Ptr() **uint64 { + return p.v.Interface().(**uint64) } - -// IsNil reports whether p is nil. -func word32_IsNil(p word32) bool { - return p.v.IsNil() +func (p pointer) toUint64Slice() *[]uint64 { + return p.v.Interface().(*[]uint64) } - -// Set sets p to point at a newly allocated word with bits set to x. -func word32_Set(p word32, o *Buffer, x uint32) { - t := p.v.Type().Elem() - switch t { - case int32Type: - if len(o.int32s) == 0 { - o.int32s = make([]int32, uint32PoolSize) - } - o.int32s[0] = int32(x) - p.v.Set(reflect.ValueOf(&o.int32s[0])) - o.int32s = o.int32s[1:] - return - case uint32Type: - if len(o.uint32s) == 0 { - o.uint32s = make([]uint32, uint32PoolSize) - } - o.uint32s[0] = x - p.v.Set(reflect.ValueOf(&o.uint32s[0])) - o.uint32s = o.uint32s[1:] - return - case float32Type: - if len(o.float32s) == 0 { - o.float32s = make([]float32, uint32PoolSize) - } - o.float32s[0] = math.Float32frombits(x) - p.v.Set(reflect.ValueOf(&o.float32s[0])) - o.float32s = o.float32s[1:] - return - } - - // must be enum - p.v.Set(reflect.New(t)) - p.v.Elem().SetInt(int64(int32(x))) +func (p pointer) toUint32() *uint32 { + return p.v.Interface().(*uint32) } - -// Get gets the bits pointed at by p, as a uint32. -func word32_Get(p word32) uint32 { - elem := p.v.Elem() - switch elem.Kind() { - case reflect.Int32: - return uint32(elem.Int()) - case reflect.Uint32: - return uint32(elem.Uint()) - case reflect.Float32: - return math.Float32bits(float32(elem.Float())) - } - panic("unreachable") +func (p pointer) toUint32Ptr() **uint32 { + return p.v.Interface().(**uint32) } - -// Word32 returns a reference to a *int32, *uint32, *float32, or *enum field in the struct. -func structPointer_Word32(p structPointer, f field) word32 { - return word32{structPointer_field(p, f)} +func (p pointer) toUint32Slice() *[]uint32 { + return p.v.Interface().(*[]uint32) } - -// A word32Val represents a field of type int32, uint32, float32, or enum. -// That is, v.Type() is int32, uint32, float32, or enum and v is assignable. -type word32Val struct { - v reflect.Value +func (p pointer) toBool() *bool { + return p.v.Interface().(*bool) } - -// Set sets *p to x. -func word32Val_Set(p word32Val, x uint32) { - switch p.v.Type() { - case int32Type: - p.v.SetInt(int64(x)) - return - case uint32Type: - p.v.SetUint(uint64(x)) - return - case float32Type: - p.v.SetFloat(float64(math.Float32frombits(x))) - return - } - - // must be enum - p.v.SetInt(int64(int32(x))) +func (p pointer) toBoolPtr() **bool { + return p.v.Interface().(**bool) } - -// Get gets the bits pointed at by p, as a uint32. -func word32Val_Get(p word32Val) uint32 { - elem := p.v - switch elem.Kind() { - case reflect.Int32: - return uint32(elem.Int()) - case reflect.Uint32: - return uint32(elem.Uint()) - case reflect.Float32: - return math.Float32bits(float32(elem.Float())) - } - panic("unreachable") +func (p pointer) toBoolSlice() *[]bool { + return p.v.Interface().(*[]bool) } - -// Word32Val returns a reference to a int32, uint32, float32, or enum field in the struct. -func structPointer_Word32Val(p structPointer, f field) word32Val { - return word32Val{structPointer_field(p, f)} +func (p pointer) toFloat64() *float64 { + return p.v.Interface().(*float64) } - -// A word32Slice is a slice of 32-bit values. -// That is, v.Type() is []int32, []uint32, []float32, or []enum. -type word32Slice struct { - v reflect.Value +func (p pointer) toFloat64Ptr() **float64 { + return p.v.Interface().(**float64) } - -func (p word32Slice) Append(x uint32) { - n, m := p.v.Len(), p.v.Cap() - if n < m { - p.v.SetLen(n + 1) - } else { - t := p.v.Type().Elem() - p.v.Set(reflect.Append(p.v, reflect.Zero(t))) - } - elem := p.v.Index(n) - switch elem.Kind() { - case reflect.Int32: - elem.SetInt(int64(int32(x))) - case reflect.Uint32: - elem.SetUint(uint64(x)) - case reflect.Float32: - elem.SetFloat(float64(math.Float32frombits(x))) - } +func (p pointer) toFloat64Slice() *[]float64 { + return p.v.Interface().(*[]float64) } - -func (p word32Slice) Len() int { - return p.v.Len() +func (p pointer) toFloat32() *float32 { + return p.v.Interface().(*float32) } - -func (p word32Slice) Index(i int) uint32 { - elem := p.v.Index(i) - switch elem.Kind() { - case reflect.Int32: - return uint32(elem.Int()) - case reflect.Uint32: - return uint32(elem.Uint()) - case reflect.Float32: - return math.Float32bits(float32(elem.Float())) - } - panic("unreachable") +func (p pointer) toFloat32Ptr() **float32 { + return p.v.Interface().(**float32) } - -// Word32Slice returns a reference to a []int32, []uint32, []float32, or []enum field in the struct. -func structPointer_Word32Slice(p structPointer, f field) word32Slice { - return word32Slice{structPointer_field(p, f)} +func (p pointer) toFloat32Slice() *[]float32 { + return p.v.Interface().(*[]float32) } - -// word64 is like word32 but for 64-bit values. -type word64 struct { - v reflect.Value +func (p pointer) toString() *string { + return p.v.Interface().(*string) } - -func word64_Set(p word64, o *Buffer, x uint64) { - t := p.v.Type().Elem() - switch t { - case int64Type: - if len(o.int64s) == 0 { - o.int64s = make([]int64, uint64PoolSize) - } - o.int64s[0] = int64(x) - p.v.Set(reflect.ValueOf(&o.int64s[0])) - o.int64s = o.int64s[1:] - return - case uint64Type: - if len(o.uint64s) == 0 { - o.uint64s = make([]uint64, uint64PoolSize) - } - o.uint64s[0] = x - p.v.Set(reflect.ValueOf(&o.uint64s[0])) - o.uint64s = o.uint64s[1:] - return - case float64Type: - if len(o.float64s) == 0 { - o.float64s = make([]float64, uint64PoolSize) - } - o.float64s[0] = math.Float64frombits(x) - p.v.Set(reflect.ValueOf(&o.float64s[0])) - o.float64s = o.float64s[1:] - return - } - panic("unreachable") +func (p pointer) toStringPtr() **string { + return p.v.Interface().(**string) } - -func word64_IsNil(p word64) bool { - return p.v.IsNil() +func (p pointer) toStringSlice() *[]string { + return p.v.Interface().(*[]string) } - -func word64_Get(p word64) uint64 { - elem := p.v.Elem() - switch elem.Kind() { - case reflect.Int64: - return uint64(elem.Int()) - case reflect.Uint64: - return elem.Uint() - case reflect.Float64: - return math.Float64bits(elem.Float()) - } - panic("unreachable") +func (p pointer) toBytes() *[]byte { + return p.v.Interface().(*[]byte) } - -func structPointer_Word64(p structPointer, f field) word64 { - return word64{structPointer_field(p, f)} +func (p pointer) toBytesSlice() *[][]byte { + return p.v.Interface().(*[][]byte) +} +func (p pointer) toExtensions() *XXX_InternalExtensions { + return p.v.Interface().(*XXX_InternalExtensions) +} +func (p pointer) toOldExtensions() *map[int32]Extension { + return p.v.Interface().(*map[int32]Extension) +} +func (p pointer) getPointer() pointer { + return pointer{v: p.v.Elem()} +} +func (p pointer) setPointer(q pointer) { + p.v.Elem().Set(q.v) +} +func (p pointer) appendPointer(q pointer) { + grow(p.v.Elem()).Set(q.v) } -// word64Val is like word32Val but for 64-bit values. -type word64Val struct { - v reflect.Value +// getPointerSlice copies []*T from p as a new []pointer. +// This behavior differs from the implementation in pointer_unsafe.go. +func (p pointer) getPointerSlice() []pointer { + if p.v.IsNil() { + return nil + } + n := p.v.Elem().Len() + s := make([]pointer, n) + for i := 0; i < n; i++ { + s[i] = pointer{v: p.v.Elem().Index(i)} + } + return s } -func word64Val_Set(p word64Val, o *Buffer, x uint64) { - switch p.v.Type() { - case int64Type: - p.v.SetInt(int64(x)) - return - case uint64Type: - p.v.SetUint(x) - return - case float64Type: - p.v.SetFloat(math.Float64frombits(x)) +// setPointerSlice copies []pointer into p as a new []*T. +// This behavior differs from the implementation in pointer_unsafe.go. +func (p pointer) setPointerSlice(v []pointer) { + if v == nil { + p.v.Elem().Set(reflect.New(p.v.Elem().Type()).Elem()) return } - panic("unreachable") + s := reflect.MakeSlice(p.v.Elem().Type(), 0, len(v)) + for _, p := range v { + s = reflect.Append(s, p.v) + } + p.v.Elem().Set(s) } -func word64Val_Get(p word64Val) uint64 { - elem := p.v - switch elem.Kind() { - case reflect.Int64: - return uint64(elem.Int()) - case reflect.Uint64: - return elem.Uint() - case reflect.Float64: - return math.Float64bits(elem.Float()) +// getInterfacePointer returns a pointer that points to the +// interface data of the interface pointed by p. +func (p pointer) getInterfacePointer() pointer { + if p.v.Elem().IsNil() { + return pointer{v: p.v.Elem()} } - panic("unreachable") + return pointer{v: p.v.Elem().Elem().Elem().Field(0).Addr()} // *interface -> interface -> *struct -> struct } -func structPointer_Word64Val(p structPointer, f field) word64Val { - return word64Val{structPointer_field(p, f)} +func (p pointer) asPointerTo(t reflect.Type) reflect.Value { + // TODO: check that p.v.Type().Elem() == t? + return p.v } -type word64Slice struct { - v reflect.Value +func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo { + atomicLock.Lock() + defer atomicLock.Unlock() + return *p } - -func (p word64Slice) Append(x uint64) { - n, m := p.v.Len(), p.v.Cap() - if n < m { - p.v.SetLen(n + 1) - } else { - t := p.v.Type().Elem() - p.v.Set(reflect.Append(p.v, reflect.Zero(t))) - } - elem := p.v.Index(n) - switch elem.Kind() { - case reflect.Int64: - elem.SetInt(int64(int64(x))) - case reflect.Uint64: - elem.SetUint(uint64(x)) - case reflect.Float64: - elem.SetFloat(float64(math.Float64frombits(x))) - } +func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) { + atomicLock.Lock() + defer atomicLock.Unlock() + *p = v } - -func (p word64Slice) Len() int { - return p.v.Len() +func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo { + atomicLock.Lock() + defer atomicLock.Unlock() + return *p } - -func (p word64Slice) Index(i int) uint64 { - elem := p.v.Index(i) - switch elem.Kind() { - case reflect.Int64: - return uint64(elem.Int()) - case reflect.Uint64: - return uint64(elem.Uint()) - case reflect.Float64: - return math.Float64bits(float64(elem.Float())) - } - panic("unreachable") +func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) { + atomicLock.Lock() + defer atomicLock.Unlock() + *p = v } - -func structPointer_Word64Slice(p structPointer, f field) word64Slice { - return word64Slice{structPointer_field(p, f)} +func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo { + atomicLock.Lock() + defer atomicLock.Unlock() + return *p +} +func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) { + atomicLock.Lock() + defer atomicLock.Unlock() + *p = v } +func atomicLoadDiscardInfo(p **discardInfo) *discardInfo { + atomicLock.Lock() + defer atomicLock.Unlock() + return *p +} +func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) { + atomicLock.Lock() + defer atomicLock.Unlock() + *p = v +} + +var atomicLock sync.Mutex diff --git a/vendor/github.com/gogo/protobuf/proto/pointer_reflect_gogo.go b/vendor/github.com/gogo/protobuf/proto/pointer_reflect_gogo.go index 1763a5f227a0c..7ffd3c29d90c3 100644 --- a/vendor/github.com/gogo/protobuf/proto/pointer_reflect_gogo.go +++ b/vendor/github.com/gogo/protobuf/proto/pointer_reflect_gogo.go @@ -1,6 +1,6 @@ // Protocol Buffers for Go with Gadgets // -// Copyright (c) 2016, The GoGo Authors. All rights reserved. +// Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without @@ -26,7 +26,11 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +build appengine js +// +build purego appengine js + +// This file contains an implementation of proto field accesses using package reflect. +// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can +// be used on App Engine. package proto @@ -34,52 +38,22 @@ import ( "reflect" ) -func structPointer_FieldPointer(p structPointer, f field) structPointer { - panic("not implemented") -} - -func appendStructPointer(base structPointer, f field, typ reflect.Type) structPointer { - panic("not implemented") -} - -func structPointer_InterfaceAt(p structPointer, f field, t reflect.Type) interface{} { - panic("not implemented") -} - -func structPointer_InterfaceRef(p structPointer, f field, t reflect.Type) interface{} { - panic("not implemented") -} - -func structPointer_GetRefStructPointer(p structPointer, f field) structPointer { - panic("not implemented") -} +// TODO: untested, so probably incorrect. -func structPointer_Add(p structPointer, size field) structPointer { - panic("not implemented") +func (p pointer) getRef() pointer { + return pointer{v: p.v.Addr()} } -func structPointer_Len(p structPointer, f field) int { - panic("not implemented") -} - -func structPointer_GetSliceHeader(p structPointer, f field) *reflect.SliceHeader { - panic("not implemented") -} - -func structPointer_Copy(oldptr structPointer, newptr structPointer, size int) { - panic("not implemented") -} - -func structPointer_StructRefSlice(p structPointer, f field, size uintptr) *structRefSlice { - panic("not implemented") -} - -type structRefSlice struct{} - -func (v *structRefSlice) Len() int { - panic("not implemented") +func (p pointer) appendRef(v pointer, typ reflect.Type) { + slice := p.getSlice(typ) + elem := v.asPointerTo(typ).Elem() + newSlice := reflect.Append(slice, elem) + slice.Set(newSlice) } -func (v *structRefSlice) Index(i int) structPointer { - panic("not implemented") +func (p pointer) getSlice(typ reflect.Type) reflect.Value { + sliceTyp := reflect.SliceOf(typ) + slice := p.asPointerTo(sliceTyp) + slice = slice.Elem() + return slice } diff --git a/vendor/github.com/gogo/protobuf/proto/pointer_unsafe.go b/vendor/github.com/gogo/protobuf/proto/pointer_unsafe.go index 6b5567d47cd39..d55a335d94532 100644 --- a/vendor/github.com/gogo/protobuf/proto/pointer_unsafe.go +++ b/vendor/github.com/gogo/protobuf/proto/pointer_unsafe.go @@ -29,7 +29,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +build !appengine,!js +// +build !purego,!appengine,!js // This file contains the implementation of the proto field accesses using package unsafe. @@ -37,38 +37,13 @@ package proto import ( "reflect" + "sync/atomic" "unsafe" ) -// NOTE: These type_Foo functions would more idiomatically be methods, -// but Go does not allow methods on pointer types, and we must preserve -// some pointer type for the garbage collector. We use these -// funcs with clunky names as our poor approximation to methods. -// -// An alternative would be -// type structPointer struct { p unsafe.Pointer } -// but that does not registerize as well. - -// A structPointer is a pointer to a struct. -type structPointer unsafe.Pointer - -// toStructPointer returns a structPointer equivalent to the given reflect value. -func toStructPointer(v reflect.Value) structPointer { - return structPointer(unsafe.Pointer(v.Pointer())) -} - -// IsNil reports whether p is nil. -func structPointer_IsNil(p structPointer) bool { - return p == nil -} - -// Interface returns the struct pointer, assumed to have element type t, -// as an interface value. -func structPointer_Interface(p structPointer, t reflect.Type) interface{} { - return reflect.NewAt(t, unsafe.Pointer(p)).Interface() -} +const unsafeAllowed = true -// A field identifies a field in a struct, accessible from a structPointer. +// A field identifies a field in a struct, accessible from a pointer. // In this implementation, a field is identified by its byte offset from the start of the struct. type field uintptr @@ -80,191 +55,254 @@ func toField(f *reflect.StructField) field { // invalidField is an invalid field identifier. const invalidField = ^field(0) +// zeroField is a noop when calling pointer.offset. +const zeroField = field(0) + // IsValid reports whether the field identifier is valid. func (f field) IsValid() bool { - return f != ^field(0) + return f != invalidField } -// Bytes returns the address of a []byte field in the struct. -func structPointer_Bytes(p structPointer, f field) *[]byte { - return (*[]byte)(unsafe.Pointer(uintptr(p) + uintptr(f))) +// The pointer type below is for the new table-driven encoder/decoder. +// The implementation here uses unsafe.Pointer to create a generic pointer. +// In pointer_reflect.go we use reflect instead of unsafe to implement +// the same (but slower) interface. +type pointer struct { + p unsafe.Pointer } -// BytesSlice returns the address of a [][]byte field in the struct. -func structPointer_BytesSlice(p structPointer, f field) *[][]byte { - return (*[][]byte)(unsafe.Pointer(uintptr(p) + uintptr(f))) -} +// size of pointer +var ptrSize = unsafe.Sizeof(uintptr(0)) -// Bool returns the address of a *bool field in the struct. -func structPointer_Bool(p structPointer, f field) **bool { - return (**bool)(unsafe.Pointer(uintptr(p) + uintptr(f))) +// toPointer converts an interface of pointer type to a pointer +// that points to the same target. +func toPointer(i *Message) pointer { + // Super-tricky - read pointer out of data word of interface value. + // Saves ~25ns over the equivalent: + // return valToPointer(reflect.ValueOf(*i)) + return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} } -// BoolVal returns the address of a bool field in the struct. -func structPointer_BoolVal(p structPointer, f field) *bool { - return (*bool)(unsafe.Pointer(uintptr(p) + uintptr(f))) +// toAddrPointer converts an interface to a pointer that points to +// the interface data. +func toAddrPointer(i *interface{}, isptr bool) pointer { + // Super-tricky - read or get the address of data word of interface value. + if isptr { + // The interface is of pointer type, thus it is a direct interface. + // The data word is the pointer data itself. We take its address. + return pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)} + } + // The interface is not of pointer type. The data word is the pointer + // to the data. + return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} } -// BoolSlice returns the address of a []bool field in the struct. -func structPointer_BoolSlice(p structPointer, f field) *[]bool { - return (*[]bool)(unsafe.Pointer(uintptr(p) + uintptr(f))) +// valToPointer converts v to a pointer. v must be of pointer type. +func valToPointer(v reflect.Value) pointer { + return pointer{p: unsafe.Pointer(v.Pointer())} } -// String returns the address of a *string field in the struct. -func structPointer_String(p structPointer, f field) **string { - return (**string)(unsafe.Pointer(uintptr(p) + uintptr(f))) +// offset converts from a pointer to a structure to a pointer to +// one of its fields. +func (p pointer) offset(f field) pointer { + // For safety, we should panic if !f.IsValid, however calling panic causes + // this to no longer be inlineable, which is a serious performance cost. + /* + if !f.IsValid() { + panic("invalid field") + } + */ + return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))} } -// StringVal returns the address of a string field in the struct. -func structPointer_StringVal(p structPointer, f field) *string { - return (*string)(unsafe.Pointer(uintptr(p) + uintptr(f))) +func (p pointer) isNil() bool { + return p.p == nil } -// StringSlice returns the address of a []string field in the struct. -func structPointer_StringSlice(p structPointer, f field) *[]string { - return (*[]string)(unsafe.Pointer(uintptr(p) + uintptr(f))) +func (p pointer) toInt64() *int64 { + return (*int64)(p.p) } - -// ExtMap returns the address of an extension map field in the struct. -func structPointer_Extensions(p structPointer, f field) *XXX_InternalExtensions { - return (*XXX_InternalExtensions)(unsafe.Pointer(uintptr(p) + uintptr(f))) +func (p pointer) toInt64Ptr() **int64 { + return (**int64)(p.p) } - -func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension { - return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f))) +func (p pointer) toInt64Slice() *[]int64 { + return (*[]int64)(p.p) } - -// NewAt returns the reflect.Value for a pointer to a field in the struct. -func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value { - return reflect.NewAt(typ, unsafe.Pointer(uintptr(p)+uintptr(f))) +func (p pointer) toInt32() *int32 { + return (*int32)(p.p) } -// SetStructPointer writes a *struct field in the struct. -func structPointer_SetStructPointer(p structPointer, f field, q structPointer) { - *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) = q +// See pointer_reflect.go for why toInt32Ptr/Slice doesn't exist. +/* + func (p pointer) toInt32Ptr() **int32 { + return (**int32)(p.p) + } + func (p pointer) toInt32Slice() *[]int32 { + return (*[]int32)(p.p) + } +*/ +func (p pointer) getInt32Ptr() *int32 { + return *(**int32)(p.p) } - -// GetStructPointer reads a *struct field in the struct. -func structPointer_GetStructPointer(p structPointer, f field) structPointer { - return *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) +func (p pointer) setInt32Ptr(v int32) { + *(**int32)(p.p) = &v } -// StructPointerSlice the address of a []*struct field in the struct. -func structPointer_StructPointerSlice(p structPointer, f field) *structPointerSlice { - return (*structPointerSlice)(unsafe.Pointer(uintptr(p) + uintptr(f))) +// getInt32Slice loads a []int32 from p. +// The value returned is aliased with the original slice. +// This behavior differs from the implementation in pointer_reflect.go. +func (p pointer) getInt32Slice() []int32 { + return *(*[]int32)(p.p) } -// A structPointerSlice represents a slice of pointers to structs (themselves submessages or groups). -type structPointerSlice []structPointer - -func (v *structPointerSlice) Len() int { return len(*v) } -func (v *structPointerSlice) Index(i int) structPointer { return (*v)[i] } -func (v *structPointerSlice) Append(p structPointer) { *v = append(*v, p) } - -// A word32 is the address of a "pointer to 32-bit value" field. -type word32 **uint32 - -// IsNil reports whether *v is nil. -func word32_IsNil(p word32) bool { - return *p == nil +// setInt32Slice stores a []int32 to p. +// The value set is aliased with the input slice. +// This behavior differs from the implementation in pointer_reflect.go. +func (p pointer) setInt32Slice(v []int32) { + *(*[]int32)(p.p) = v } -// Set sets *v to point at a newly allocated word set to x. -func word32_Set(p word32, o *Buffer, x uint32) { - if len(o.uint32s) == 0 { - o.uint32s = make([]uint32, uint32PoolSize) - } - o.uint32s[0] = x - *p = &o.uint32s[0] - o.uint32s = o.uint32s[1:] +// TODO: Can we get rid of appendInt32Slice and use setInt32Slice instead? +func (p pointer) appendInt32Slice(v int32) { + s := (*[]int32)(p.p) + *s = append(*s, v) } -// Get gets the value pointed at by *v. -func word32_Get(p word32) uint32 { - return **p +func (p pointer) toUint64() *uint64 { + return (*uint64)(p.p) } - -// Word32 returns the address of a *int32, *uint32, *float32, or *enum field in the struct. -func structPointer_Word32(p structPointer, f field) word32 { - return word32((**uint32)(unsafe.Pointer(uintptr(p) + uintptr(f)))) +func (p pointer) toUint64Ptr() **uint64 { + return (**uint64)(p.p) } - -// A word32Val is the address of a 32-bit value field. -type word32Val *uint32 - -// Set sets *p to x. -func word32Val_Set(p word32Val, x uint32) { - *p = x +func (p pointer) toUint64Slice() *[]uint64 { + return (*[]uint64)(p.p) } - -// Get gets the value pointed at by p. -func word32Val_Get(p word32Val) uint32 { - return *p +func (p pointer) toUint32() *uint32 { + return (*uint32)(p.p) } - -// Word32Val returns the address of a *int32, *uint32, *float32, or *enum field in the struct. -func structPointer_Word32Val(p structPointer, f field) word32Val { - return word32Val((*uint32)(unsafe.Pointer(uintptr(p) + uintptr(f)))) +func (p pointer) toUint32Ptr() **uint32 { + return (**uint32)(p.p) } - -// A word32Slice is a slice of 32-bit values. -type word32Slice []uint32 - -func (v *word32Slice) Append(x uint32) { *v = append(*v, x) } -func (v *word32Slice) Len() int { return len(*v) } -func (v *word32Slice) Index(i int) uint32 { return (*v)[i] } - -// Word32Slice returns the address of a []int32, []uint32, []float32, or []enum field in the struct. -func structPointer_Word32Slice(p structPointer, f field) *word32Slice { - return (*word32Slice)(unsafe.Pointer(uintptr(p) + uintptr(f))) +func (p pointer) toUint32Slice() *[]uint32 { + return (*[]uint32)(p.p) } - -// word64 is like word32 but for 64-bit values. -type word64 **uint64 - -func word64_Set(p word64, o *Buffer, x uint64) { - if len(o.uint64s) == 0 { - o.uint64s = make([]uint64, uint64PoolSize) - } - o.uint64s[0] = x - *p = &o.uint64s[0] - o.uint64s = o.uint64s[1:] +func (p pointer) toBool() *bool { + return (*bool)(p.p) } - -func word64_IsNil(p word64) bool { - return *p == nil +func (p pointer) toBoolPtr() **bool { + return (**bool)(p.p) } - -func word64_Get(p word64) uint64 { - return **p +func (p pointer) toBoolSlice() *[]bool { + return (*[]bool)(p.p) +} +func (p pointer) toFloat64() *float64 { + return (*float64)(p.p) +} +func (p pointer) toFloat64Ptr() **float64 { + return (**float64)(p.p) +} +func (p pointer) toFloat64Slice() *[]float64 { + return (*[]float64)(p.p) +} +func (p pointer) toFloat32() *float32 { + return (*float32)(p.p) +} +func (p pointer) toFloat32Ptr() **float32 { + return (**float32)(p.p) +} +func (p pointer) toFloat32Slice() *[]float32 { + return (*[]float32)(p.p) +} +func (p pointer) toString() *string { + return (*string)(p.p) +} +func (p pointer) toStringPtr() **string { + return (**string)(p.p) +} +func (p pointer) toStringSlice() *[]string { + return (*[]string)(p.p) +} +func (p pointer) toBytes() *[]byte { + return (*[]byte)(p.p) +} +func (p pointer) toBytesSlice() *[][]byte { + return (*[][]byte)(p.p) +} +func (p pointer) toExtensions() *XXX_InternalExtensions { + return (*XXX_InternalExtensions)(p.p) +} +func (p pointer) toOldExtensions() *map[int32]Extension { + return (*map[int32]Extension)(p.p) } -func structPointer_Word64(p structPointer, f field) word64 { - return word64((**uint64)(unsafe.Pointer(uintptr(p) + uintptr(f)))) +// getPointerSlice loads []*T from p as a []pointer. +// The value returned is aliased with the original slice. +// This behavior differs from the implementation in pointer_reflect.go. +func (p pointer) getPointerSlice() []pointer { + // Super-tricky - p should point to a []*T where T is a + // message type. We load it as []pointer. + return *(*[]pointer)(p.p) } -// word64Val is like word32Val but for 64-bit values. -type word64Val *uint64 +// setPointerSlice stores []pointer into p as a []*T. +// The value set is aliased with the input slice. +// This behavior differs from the implementation in pointer_reflect.go. +func (p pointer) setPointerSlice(v []pointer) { + // Super-tricky - p should point to a []*T where T is a + // message type. We store it as []pointer. + *(*[]pointer)(p.p) = v +} -func word64Val_Set(p word64Val, o *Buffer, x uint64) { - *p = x +// getPointer loads the pointer at p and returns it. +func (p pointer) getPointer() pointer { + return pointer{p: *(*unsafe.Pointer)(p.p)} } -func word64Val_Get(p word64Val) uint64 { - return *p +// setPointer stores the pointer q at p. +func (p pointer) setPointer(q pointer) { + *(*unsafe.Pointer)(p.p) = q.p } -func structPointer_Word64Val(p structPointer, f field) word64Val { - return word64Val((*uint64)(unsafe.Pointer(uintptr(p) + uintptr(f)))) +// append q to the slice pointed to by p. +func (p pointer) appendPointer(q pointer) { + s := (*[]unsafe.Pointer)(p.p) + *s = append(*s, q.p) } -// word64Slice is like word32Slice but for 64-bit values. -type word64Slice []uint64 +// getInterfacePointer returns a pointer that points to the +// interface data of the interface pointed by p. +func (p pointer) getInterfacePointer() pointer { + // Super-tricky - read pointer out of data word of interface value. + return pointer{p: (*(*[2]unsafe.Pointer)(p.p))[1]} +} -func (v *word64Slice) Append(x uint64) { *v = append(*v, x) } -func (v *word64Slice) Len() int { return len(*v) } -func (v *word64Slice) Index(i int) uint64 { return (*v)[i] } +// asPointerTo returns a reflect.Value that is a pointer to an +// object of type t stored at p. +func (p pointer) asPointerTo(t reflect.Type) reflect.Value { + return reflect.NewAt(t, p.p) +} -func structPointer_Word64Slice(p structPointer, f field) *word64Slice { - return (*word64Slice)(unsafe.Pointer(uintptr(p) + uintptr(f))) +func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo { + return (*unmarshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) +} +func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) { + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) +} +func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo { + return (*marshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) +} +func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) { + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) +} +func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo { + return (*mergeInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) +} +func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) { + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) +} +func atomicLoadDiscardInfo(p **discardInfo) *discardInfo { + return (*discardInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) +} +func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) { + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) } diff --git a/vendor/github.com/gogo/protobuf/proto/pointer_unsafe_gogo.go b/vendor/github.com/gogo/protobuf/proto/pointer_unsafe_gogo.go index f156a29f0e830..aca8eed02a11e 100644 --- a/vendor/github.com/gogo/protobuf/proto/pointer_unsafe_gogo.go +++ b/vendor/github.com/gogo/protobuf/proto/pointer_unsafe_gogo.go @@ -1,6 +1,6 @@ // Protocol Buffers for Go with Gadgets // -// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without @@ -26,7 +26,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +build !appengine,!js +// +build !purego,!appengine,!js // This file contains the implementation of the proto field accesses using package unsafe. @@ -37,92 +37,20 @@ import ( "unsafe" ) -func structPointer_InterfaceAt(p structPointer, f field, t reflect.Type) interface{} { - point := unsafe.Pointer(uintptr(p) + uintptr(f)) - r := reflect.NewAt(t, point) - return r.Interface() +func (p pointer) getRef() pointer { + return pointer{p: (unsafe.Pointer)(&p.p)} } -func structPointer_InterfaceRef(p structPointer, f field, t reflect.Type) interface{} { - point := unsafe.Pointer(uintptr(p) + uintptr(f)) - r := reflect.NewAt(t, point) - if r.Elem().IsNil() { - return nil - } - return r.Elem().Interface() +func (p pointer) appendRef(v pointer, typ reflect.Type) { + slice := p.getSlice(typ) + elem := v.asPointerTo(typ).Elem() + newSlice := reflect.Append(slice, elem) + slice.Set(newSlice) } -func copyUintPtr(oldptr, newptr uintptr, size int) { - oldbytes := make([]byte, 0) - oldslice := (*reflect.SliceHeader)(unsafe.Pointer(&oldbytes)) - oldslice.Data = oldptr - oldslice.Len = size - oldslice.Cap = size - newbytes := make([]byte, 0) - newslice := (*reflect.SliceHeader)(unsafe.Pointer(&newbytes)) - newslice.Data = newptr - newslice.Len = size - newslice.Cap = size - copy(newbytes, oldbytes) -} - -func structPointer_Copy(oldptr structPointer, newptr structPointer, size int) { - copyUintPtr(uintptr(oldptr), uintptr(newptr), size) -} - -func appendStructPointer(base structPointer, f field, typ reflect.Type) structPointer { - size := typ.Elem().Size() - - oldHeader := structPointer_GetSliceHeader(base, f) - oldSlice := reflect.NewAt(typ, unsafe.Pointer(oldHeader)).Elem() - newLen := oldHeader.Len + 1 - newSlice := reflect.MakeSlice(typ, newLen, newLen) - reflect.Copy(newSlice, oldSlice) - bas := toStructPointer(newSlice) - oldHeader.Data = uintptr(bas) - oldHeader.Len = newLen - oldHeader.Cap = newLen - - return structPointer(unsafe.Pointer(uintptr(unsafe.Pointer(bas)) + uintptr(uintptr(newLen-1)*size))) -} - -func structPointer_FieldPointer(p structPointer, f field) structPointer { - return structPointer(unsafe.Pointer(uintptr(p) + uintptr(f))) -} - -func structPointer_GetRefStructPointer(p structPointer, f field) structPointer { - return structPointer((*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f)))) -} - -func structPointer_GetSliceHeader(p structPointer, f field) *reflect.SliceHeader { - return (*reflect.SliceHeader)(unsafe.Pointer(uintptr(p) + uintptr(f))) -} - -func structPointer_Add(p structPointer, size field) structPointer { - return structPointer(unsafe.Pointer(uintptr(p) + uintptr(size))) -} - -func structPointer_Len(p structPointer, f field) int { - return len(*(*[]interface{})(unsafe.Pointer(structPointer_GetRefStructPointer(p, f)))) -} - -func structPointer_StructRefSlice(p structPointer, f field, size uintptr) *structRefSlice { - return &structRefSlice{p: p, f: f, size: size} -} - -// A structRefSlice represents a slice of structs (themselves submessages or groups). -type structRefSlice struct { - p structPointer - f field - size uintptr -} - -func (v *structRefSlice) Len() int { - return structPointer_Len(v.p, v.f) -} - -func (v *structRefSlice) Index(i int) structPointer { - ss := structPointer_GetStructPointer(v.p, v.f) - ss1 := structPointer_GetRefStructPointer(ss, 0) - return structPointer_Add(ss1, field(uintptr(i)*v.size)) +func (p pointer) getSlice(typ reflect.Type) reflect.Value { + sliceTyp := reflect.SliceOf(typ) + slice := p.asPointerTo(sliceTyp) + slice = slice.Elem() + return slice } diff --git a/vendor/github.com/gogo/protobuf/proto/properties.go b/vendor/github.com/gogo/protobuf/proto/properties.go index 2a69e8862d564..28da1475fb397 100644 --- a/vendor/github.com/gogo/protobuf/proto/properties.go +++ b/vendor/github.com/gogo/protobuf/proto/properties.go @@ -43,7 +43,6 @@ package proto import ( "fmt" "log" - "os" "reflect" "sort" "strconv" @@ -63,42 +62,6 @@ const ( WireFixed32 = 5 ) -const startSize = 10 // initial slice/string sizes - -// Encoders are defined in encode.go -// An encoder outputs the full representation of a field, including its -// tag and encoder type. -type encoder func(p *Buffer, prop *Properties, base structPointer) error - -// A valueEncoder encodes a single integer in a particular encoding. -type valueEncoder func(o *Buffer, x uint64) error - -// Sizers are defined in encode.go -// A sizer returns the encoded size of a field, including its tag and encoder -// type. -type sizer func(prop *Properties, base structPointer) int - -// A valueSizer returns the encoded size of a single integer in a particular -// encoding. -type valueSizer func(x uint64) int - -// Decoders are defined in decode.go -// A decoder creates a value from its wire representation. -// Unrecognized subelements are saved in unrec. -type decoder func(p *Buffer, prop *Properties, base structPointer) error - -// A valueDecoder decodes a single integer in a particular encoding. -type valueDecoder func(o *Buffer) (x uint64, err error) - -// A oneofMarshaler does the marshaling for all oneof fields in a message. -type oneofMarshaler func(Message, *Buffer) error - -// A oneofUnmarshaler does the unmarshaling for a oneof field in a message. -type oneofUnmarshaler func(Message, int, int, *Buffer) (bool, error) - -// A oneofSizer does the sizing for all oneof fields in a message. -type oneofSizer func(Message) int - // tagMap is an optimization over map[int]int for typical protocol buffer // use-cases. Encoded protocol buffers are often in tag order with small tag // numbers. @@ -145,13 +108,6 @@ type StructProperties struct { decoderTags tagMap // map from proto tag to struct field number decoderOrigNames map[string]int // map from original name to struct field number order []int // list of struct field numbers in tag order - unrecField field // field id of the XXX_unrecognized []byte field - extendable bool // is this an extendable proto - - oneofMarshaler oneofMarshaler - oneofUnmarshaler oneofUnmarshaler - oneofSizer oneofSizer - stype reflect.Type // OneofTypes contains information about the oneof fields in this message. // It is keyed by the original name of a field. @@ -187,7 +143,7 @@ type Properties struct { Repeated bool Packed bool // relevant for repeated primitives only Enum string // set for enum types only - proto3 bool // whether this is known to be a proto3 field; set for []byte only + proto3 bool // whether this is known to be a proto3 field oneof bool // whether this is a oneof field Default string // default value @@ -196,37 +152,21 @@ type Properties struct { CastType string StdTime bool StdDuration bool + WktPointer bool + + stype reflect.Type // set for struct types only + ctype reflect.Type // set for custom types only + sprop *StructProperties // set for struct types only - enc encoder - valEnc valueEncoder // set for bool and numeric types only - field field - tagcode []byte // encoding of EncodeVarint((Tag<<3)|WireType) - tagbuf [8]byte - stype reflect.Type // set for struct types only - sstype reflect.Type // set for slices of structs types only - ctype reflect.Type // set for custom types only - sprop *StructProperties // set for struct types only - isMarshaler bool - isUnmarshaler bool - - mtype reflect.Type // set for map types only - mkeyprop *Properties // set for map types only - mvalprop *Properties // set for map types only - - size sizer - valSize valueSizer // set for bool and numeric types only - - dec decoder - valDec valueDecoder // set for bool and numeric types only - - // If this is a packable field, this will be the decoder for the packed version of the field. - packedDec decoder + mtype reflect.Type // set for map types only + MapKeyProp *Properties // set for map types only + MapValProp *Properties // set for map types only } // String formats the properties in the protobuf struct field tag style. func (p *Properties) String() string { s := p.Wire - s = "," + s += "," s += strconv.Itoa(p.Tag) if p.Required { s += ",req" @@ -264,7 +204,7 @@ func (p *Properties) Parse(s string) { // "bytes,49,opt,name=foo,def=hello!" fields := strings.Split(s, ",") // breaks def=, but handled below. if len(fields) < 2 { - fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s) + log.Printf("proto: tag has too few fields: %q", s) return } @@ -272,34 +212,19 @@ func (p *Properties) Parse(s string) { switch p.Wire { case "varint": p.WireType = WireVarint - p.valEnc = (*Buffer).EncodeVarint - p.valDec = (*Buffer).DecodeVarint - p.valSize = sizeVarint case "fixed32": p.WireType = WireFixed32 - p.valEnc = (*Buffer).EncodeFixed32 - p.valDec = (*Buffer).DecodeFixed32 - p.valSize = sizeFixed32 case "fixed64": p.WireType = WireFixed64 - p.valEnc = (*Buffer).EncodeFixed64 - p.valDec = (*Buffer).DecodeFixed64 - p.valSize = sizeFixed64 case "zigzag32": p.WireType = WireVarint - p.valEnc = (*Buffer).EncodeZigzag32 - p.valDec = (*Buffer).DecodeZigzag32 - p.valSize = sizeZigzag32 case "zigzag64": p.WireType = WireVarint - p.valEnc = (*Buffer).EncodeZigzag64 - p.valDec = (*Buffer).DecodeZigzag64 - p.valSize = sizeZigzag64 case "bytes", "group": p.WireType = WireBytes // no numeric converter for non-numeric types default: - fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s) + log.Printf("proto: tag has unknown wire type: %q", s) return } @@ -309,6 +234,7 @@ func (p *Properties) Parse(s string) { return } +outer: for i := 2; i < len(fields); i++ { f := fields[i] switch { @@ -336,7 +262,7 @@ func (p *Properties) Parse(s string) { if i+1 < len(fields) { // Commas aren't escaped, and def is always last. p.Default += "," + strings.Join(fields[i+1:], ",") - break + break outer } case strings.HasPrefix(f, "embedded="): p.OrigName = strings.Split(f, "=")[1] @@ -348,301 +274,58 @@ func (p *Properties) Parse(s string) { p.StdTime = true case f == "stdduration": p.StdDuration = true + case f == "wktptr": + p.WktPointer = true } } } -func logNoSliceEnc(t1, t2 reflect.Type) { - fmt.Fprintf(os.Stderr, "proto: no slice oenc for %T = []%T\n", t1, t2) -} - var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem() -// Initialize the fields for encoding and decoding. -func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lockGetProp bool) { - p.enc = nil - p.dec = nil - p.size = nil +// setFieldProps initializes the field properties for submessages and maps. +func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, lockGetProp bool) { isMap := typ.Kind() == reflect.Map if len(p.CustomType) > 0 && !isMap { - p.setCustomEncAndDec(typ) + p.ctype = typ p.setTag(lockGetProp) return } if p.StdTime && !isMap { - p.setTimeEncAndDec(typ) p.setTag(lockGetProp) return } if p.StdDuration && !isMap { - p.setDurationEncAndDec(typ) + p.setTag(lockGetProp) + return + } + if p.WktPointer && !isMap { p.setTag(lockGetProp) return } switch t1 := typ; t1.Kind() { - default: - fmt.Fprintf(os.Stderr, "proto: no coders for %v\n", t1) - - // proto3 scalar types - - case reflect.Bool: - if p.proto3 { - p.enc = (*Buffer).enc_proto3_bool - p.dec = (*Buffer).dec_proto3_bool - p.size = size_proto3_bool - } else { - p.enc = (*Buffer).enc_ref_bool - p.dec = (*Buffer).dec_proto3_bool - p.size = size_ref_bool - } - case reflect.Int32: - if p.proto3 { - p.enc = (*Buffer).enc_proto3_int32 - p.dec = (*Buffer).dec_proto3_int32 - p.size = size_proto3_int32 - } else { - p.enc = (*Buffer).enc_ref_int32 - p.dec = (*Buffer).dec_proto3_int32 - p.size = size_ref_int32 - } - case reflect.Uint32: - if p.proto3 { - p.enc = (*Buffer).enc_proto3_uint32 - p.dec = (*Buffer).dec_proto3_int32 // can reuse - p.size = size_proto3_uint32 - } else { - p.enc = (*Buffer).enc_ref_uint32 - p.dec = (*Buffer).dec_proto3_int32 // can reuse - p.size = size_ref_uint32 - } - case reflect.Int64, reflect.Uint64: - if p.proto3 { - p.enc = (*Buffer).enc_proto3_int64 - p.dec = (*Buffer).dec_proto3_int64 - p.size = size_proto3_int64 - } else { - p.enc = (*Buffer).enc_ref_int64 - p.dec = (*Buffer).dec_proto3_int64 - p.size = size_ref_int64 - } - case reflect.Float32: - if p.proto3 { - p.enc = (*Buffer).enc_proto3_uint32 // can just treat them as bits - p.dec = (*Buffer).dec_proto3_int32 - p.size = size_proto3_uint32 - } else { - p.enc = (*Buffer).enc_ref_uint32 // can just treat them as bits - p.dec = (*Buffer).dec_proto3_int32 - p.size = size_ref_uint32 - } - case reflect.Float64: - if p.proto3 { - p.enc = (*Buffer).enc_proto3_int64 // can just treat them as bits - p.dec = (*Buffer).dec_proto3_int64 - p.size = size_proto3_int64 - } else { - p.enc = (*Buffer).enc_ref_int64 // can just treat them as bits - p.dec = (*Buffer).dec_proto3_int64 - p.size = size_ref_int64 - } - case reflect.String: - if p.proto3 { - p.enc = (*Buffer).enc_proto3_string - p.dec = (*Buffer).dec_proto3_string - p.size = size_proto3_string - } else { - p.enc = (*Buffer).enc_ref_string - p.dec = (*Buffer).dec_proto3_string - p.size = size_ref_string - } case reflect.Struct: p.stype = typ - p.isMarshaler = isMarshaler(typ) - p.isUnmarshaler = isUnmarshaler(typ) - if p.Wire == "bytes" { - p.enc = (*Buffer).enc_ref_struct_message - p.dec = (*Buffer).dec_ref_struct_message - p.size = size_ref_struct_message - } else { - fmt.Fprintf(os.Stderr, "proto: no coders for struct %T\n", typ) - } - case reflect.Ptr: - switch t2 := t1.Elem(); t2.Kind() { - default: - fmt.Fprintf(os.Stderr, "proto: no encoder function for %v -> %v\n", t1, t2) - break - case reflect.Bool: - p.enc = (*Buffer).enc_bool - p.dec = (*Buffer).dec_bool - p.size = size_bool - case reflect.Int32: - p.enc = (*Buffer).enc_int32 - p.dec = (*Buffer).dec_int32 - p.size = size_int32 - case reflect.Uint32: - p.enc = (*Buffer).enc_uint32 - p.dec = (*Buffer).dec_int32 // can reuse - p.size = size_uint32 - case reflect.Int64, reflect.Uint64: - p.enc = (*Buffer).enc_int64 - p.dec = (*Buffer).dec_int64 - p.size = size_int64 - case reflect.Float32: - p.enc = (*Buffer).enc_uint32 // can just treat them as bits - p.dec = (*Buffer).dec_int32 - p.size = size_uint32 - case reflect.Float64: - p.enc = (*Buffer).enc_int64 // can just treat them as bits - p.dec = (*Buffer).dec_int64 - p.size = size_int64 - case reflect.String: - p.enc = (*Buffer).enc_string - p.dec = (*Buffer).dec_string - p.size = size_string - case reflect.Struct: + if t1.Elem().Kind() == reflect.Struct { p.stype = t1.Elem() - p.isMarshaler = isMarshaler(t1) - p.isUnmarshaler = isUnmarshaler(t1) - if p.Wire == "bytes" { - p.enc = (*Buffer).enc_struct_message - p.dec = (*Buffer).dec_struct_message - p.size = size_struct_message - } else { - p.enc = (*Buffer).enc_struct_group - p.dec = (*Buffer).dec_struct_group - p.size = size_struct_group - } } - case reflect.Slice: switch t2 := t1.Elem(); t2.Kind() { - default: - logNoSliceEnc(t1, t2) - break - case reflect.Bool: - if p.Packed { - p.enc = (*Buffer).enc_slice_packed_bool - p.size = size_slice_packed_bool - } else { - p.enc = (*Buffer).enc_slice_bool - p.size = size_slice_bool - } - p.dec = (*Buffer).dec_slice_bool - p.packedDec = (*Buffer).dec_slice_packed_bool - case reflect.Int32: - if p.Packed { - p.enc = (*Buffer).enc_slice_packed_int32 - p.size = size_slice_packed_int32 - } else { - p.enc = (*Buffer).enc_slice_int32 - p.size = size_slice_int32 - } - p.dec = (*Buffer).dec_slice_int32 - p.packedDec = (*Buffer).dec_slice_packed_int32 - case reflect.Uint32: - if p.Packed { - p.enc = (*Buffer).enc_slice_packed_uint32 - p.size = size_slice_packed_uint32 - } else { - p.enc = (*Buffer).enc_slice_uint32 - p.size = size_slice_uint32 - } - p.dec = (*Buffer).dec_slice_int32 - p.packedDec = (*Buffer).dec_slice_packed_int32 - case reflect.Int64, reflect.Uint64: - if p.Packed { - p.enc = (*Buffer).enc_slice_packed_int64 - p.size = size_slice_packed_int64 - } else { - p.enc = (*Buffer).enc_slice_int64 - p.size = size_slice_int64 - } - p.dec = (*Buffer).dec_slice_int64 - p.packedDec = (*Buffer).dec_slice_packed_int64 - case reflect.Uint8: - p.dec = (*Buffer).dec_slice_byte - if p.proto3 { - p.enc = (*Buffer).enc_proto3_slice_byte - p.size = size_proto3_slice_byte - } else { - p.enc = (*Buffer).enc_slice_byte - p.size = size_slice_byte - } - case reflect.Float32, reflect.Float64: - switch t2.Bits() { - case 32: - // can just treat them as bits - if p.Packed { - p.enc = (*Buffer).enc_slice_packed_uint32 - p.size = size_slice_packed_uint32 - } else { - p.enc = (*Buffer).enc_slice_uint32 - p.size = size_slice_uint32 - } - p.dec = (*Buffer).dec_slice_int32 - p.packedDec = (*Buffer).dec_slice_packed_int32 - case 64: - // can just treat them as bits - if p.Packed { - p.enc = (*Buffer).enc_slice_packed_int64 - p.size = size_slice_packed_int64 - } else { - p.enc = (*Buffer).enc_slice_int64 - p.size = size_slice_int64 - } - p.dec = (*Buffer).dec_slice_int64 - p.packedDec = (*Buffer).dec_slice_packed_int64 - default: - logNoSliceEnc(t1, t2) - break - } - case reflect.String: - p.enc = (*Buffer).enc_slice_string - p.dec = (*Buffer).dec_slice_string - p.size = size_slice_string case reflect.Ptr: switch t3 := t2.Elem(); t3.Kind() { - default: - fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T -> %T\n", t1, t2, t3) - break case reflect.Struct: - p.stype = t2.Elem() - p.isMarshaler = isMarshaler(t2) - p.isUnmarshaler = isUnmarshaler(t2) - if p.Wire == "bytes" { - p.enc = (*Buffer).enc_slice_struct_message - p.dec = (*Buffer).dec_slice_struct_message - p.size = size_slice_struct_message - } else { - p.enc = (*Buffer).enc_slice_struct_group - p.dec = (*Buffer).dec_slice_struct_group - p.size = size_slice_struct_group - } - } - case reflect.Slice: - switch t2.Elem().Kind() { - default: - fmt.Fprintf(os.Stderr, "proto: no slice elem oenc for %T -> %T -> %T\n", t1, t2, t2.Elem()) - break - case reflect.Uint8: - p.enc = (*Buffer).enc_slice_slice_byte - p.dec = (*Buffer).dec_slice_slice_byte - p.size = size_slice_slice_byte + p.stype = t3 } case reflect.Struct: - p.setSliceOfNonPointerStructs(t1) + p.stype = t2 } case reflect.Map: - p.enc = (*Buffer).enc_new_map - p.dec = (*Buffer).dec_new_map - p.size = size_new_map p.mtype = t1 - p.mkeyprop = &Properties{} - p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp) - p.mvalprop = &Properties{} + p.MapKeyProp = &Properties{} + p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp) + p.MapValProp = &Properties{} vtype := p.mtype.Elem() if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice { // The value type is not a message (*T) or bytes ([]byte), @@ -650,29 +333,16 @@ func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lock vtype = reflect.PtrTo(vtype) } - p.mvalprop.CustomType = p.CustomType - p.mvalprop.StdDuration = p.StdDuration - p.mvalprop.StdTime = p.StdTime - p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp) + p.MapValProp.CustomType = p.CustomType + p.MapValProp.StdDuration = p.StdDuration + p.MapValProp.StdTime = p.StdTime + p.MapValProp.WktPointer = p.WktPointer + p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp) } p.setTag(lockGetProp) } func (p *Properties) setTag(lockGetProp bool) { - // precalculate tag code - wire := p.WireType - if p.Packed { - wire = WireBytes - } - x := uint32(p.Tag)<<3 | uint32(wire) - i := 0 - for i = 0; x > 127; i++ { - p.tagbuf[i] = 0x80 | uint8(x&0x7F) - x >>= 7 - } - p.tagbuf[i] = uint8(x) - p.tagcode = p.tagbuf[0 : i+1] - if p.stype != nil { if lockGetProp { p.sprop = GetProperties(p.stype) @@ -683,20 +353,9 @@ func (p *Properties) setTag(lockGetProp bool) { } var ( - marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() - unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem() + marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() ) -// isMarshaler reports whether type t implements Marshaler. -func isMarshaler(t reflect.Type) bool { - return t.Implements(marshalerType) -} - -// isUnmarshaler reports whether type t implements Unmarshaler. -func isUnmarshaler(t reflect.Type) bool { - return t.Implements(unmarshalerType) -} - // Init populates the properties from a protocol buffer struct tag. func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) { p.init(typ, name, tag, f, true) @@ -706,14 +365,11 @@ func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructF // "bytes,49,opt,def=hello!" p.Name = name p.OrigName = name - if f != nil { - p.field = toField(f) - } if tag == "" { return } p.Parse(tag) - p.setEncAndDec(typ, f, lockGetProp) + p.setFieldProps(typ, f, lockGetProp) } var ( @@ -734,9 +390,6 @@ func GetProperties(t reflect.Type) *StructProperties { sprop, ok := propertiesMap[t] propertiesMu.RUnlock() if ok { - if collectStats { - stats.Chit++ - } return sprop } @@ -746,27 +399,26 @@ func GetProperties(t reflect.Type) *StructProperties { return sprop } +type ( + oneofFuncsIface interface { + XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) + } + oneofWrappersIface interface { + XXX_OneofWrappers() []interface{} + } +) + // getPropertiesLocked requires that propertiesMu is held. func getPropertiesLocked(t reflect.Type) *StructProperties { if prop, ok := propertiesMap[t]; ok { - if collectStats { - stats.Chit++ - } return prop } - if collectStats { - stats.Cmiss++ - } prop := new(StructProperties) // in case of recursive protos, fill this in now. propertiesMap[t] = prop // build properties - prop.extendable = reflect.PtrTo(t).Implements(extendableProtoType) || - reflect.PtrTo(t).Implements(extendableProtoV1Type) || - reflect.PtrTo(t).Implements(extendableBytesType) - prop.unrecField = invalidField prop.Prop = make([]*Properties, t.NumField()) prop.order = make([]int, t.NumField()) @@ -777,23 +429,6 @@ func getPropertiesLocked(t reflect.Type) *StructProperties { name := f.Name p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false) - if f.Name == "XXX_InternalExtensions" { // special case - p.enc = (*Buffer).enc_exts - p.dec = nil // not needed - p.size = size_exts - } else if f.Name == "XXX_extensions" { // special case - if len(f.Tag.Get("protobuf")) > 0 { - p.enc = (*Buffer).enc_ext_slice_byte - p.dec = nil // not needed - p.size = size_ext_slice_byte - } else { - p.enc = (*Buffer).enc_map - p.dec = nil // not needed - p.size = size_map - } - } else if f.Name == "XXX_unrecognized" { // special case - prop.unrecField = toField(&f) - } oneof := f.Tag.Get("protobuf_oneof") // special case if oneof != "" { isOneofMessage = true @@ -809,46 +444,45 @@ func getPropertiesLocked(t reflect.Type) *StructProperties { } print("\n") } - if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") && oneof == "" { - fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]") - } } // Re-order prop.order. sort.Sort(prop) - type oneofMessage interface { - XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) - } - if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); isOneofMessage && ok { + if isOneofMessage { var oots []interface{} - prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofSizer, oots = om.XXX_OneofFuncs() - prop.stype = t - - // Interpret oneof metadata. - prop.OneofTypes = make(map[string]*OneofProperties) - for _, oot := range oots { - oop := &OneofProperties{ - Type: reflect.ValueOf(oot).Type(), // *T - Prop: new(Properties), - } - sft := oop.Type.Elem().Field(0) - oop.Prop.Name = sft.Name - oop.Prop.Parse(sft.Tag.Get("protobuf")) - // There will be exactly one interface field that - // this new value is assignable to. - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - if f.Type.Kind() != reflect.Interface { - continue + switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) { + case oneofFuncsIface: + _, _, _, oots = m.XXX_OneofFuncs() + case oneofWrappersIface: + oots = m.XXX_OneofWrappers() + } + if len(oots) > 0 { + // Interpret oneof metadata. + prop.OneofTypes = make(map[string]*OneofProperties) + for _, oot := range oots { + oop := &OneofProperties{ + Type: reflect.ValueOf(oot).Type(), // *T + Prop: new(Properties), } - if !oop.Type.AssignableTo(f.Type) { - continue + sft := oop.Type.Elem().Field(0) + oop.Prop.Name = sft.Name + oop.Prop.Parse(sft.Tag.Get("protobuf")) + // There will be exactly one interface field that + // this new value is assignable to. + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.Type.Kind() != reflect.Interface { + continue + } + if !oop.Type.AssignableTo(f.Type) { + continue + } + oop.Field = i + break } - oop.Field = i - break + prop.OneofTypes[oop.Prop.OrigName] = oop } - prop.OneofTypes[oop.Prop.OrigName] = oop } } @@ -873,30 +507,6 @@ func getPropertiesLocked(t reflect.Type) *StructProperties { return prop } -// Return the Properties object for the x[0]'th field of the structure. -func propByIndex(t reflect.Type, x []int) *Properties { - if len(x) != 1 { - fmt.Fprintf(os.Stderr, "proto: field index dimension %d (not 1) for type %s\n", len(x), t) - return nil - } - prop := GetProperties(t) - return prop.Prop[x[0]] -} - -// Get the address and type of a pointer to a struct from an interface. -func getbase(pb Message) (t reflect.Type, b structPointer, err error) { - if pb == nil { - err = ErrNil - return - } - // get the reflect type of the pointer to the struct. - t = reflect.TypeOf(pb) - // get the address of the struct. - value := reflect.ValueOf(pb) - b = toStructPointer(value) - return -} - // A global registry of enum types. // The generated code will register the generated maps by calling RegisterEnum. @@ -925,20 +535,42 @@ func EnumValueMap(enumType string) map[string]int32 { // A registry of all linked message types. // The string is a fully-qualified proto name ("pkg.Message"). var ( - protoTypes = make(map[string]reflect.Type) - revProtoTypes = make(map[reflect.Type]string) + protoTypedNils = make(map[string]Message) // a map from proto names to typed nil pointers + protoMapTypes = make(map[string]reflect.Type) // a map from proto names to map types + revProtoTypes = make(map[reflect.Type]string) ) // RegisterType is called from generated code and maps from the fully qualified // proto name to the type (pointer to struct) of the protocol buffer. func RegisterType(x Message, name string) { - if _, ok := protoTypes[name]; ok { + if _, ok := protoTypedNils[name]; ok { // TODO: Some day, make this a panic. log.Printf("proto: duplicate proto type registered: %s", name) return } t := reflect.TypeOf(x) - protoTypes[name] = t + if v := reflect.ValueOf(x); v.Kind() == reflect.Ptr && v.Pointer() == 0 { + // Generated code always calls RegisterType with nil x. + // This check is just for extra safety. + protoTypedNils[name] = x + } else { + protoTypedNils[name] = reflect.Zero(t).Interface().(Message) + } + revProtoTypes[t] = name +} + +// RegisterMapType is called from generated code and maps from the fully qualified +// proto name to the native map type of the proto map definition. +func RegisterMapType(x interface{}, name string) { + if reflect.TypeOf(x).Kind() != reflect.Map { + panic(fmt.Sprintf("RegisterMapType(%T, %q); want map", x, name)) + } + if _, ok := protoMapTypes[name]; ok { + log.Printf("proto: duplicate proto type registered: %s", name) + return + } + t := reflect.TypeOf(x) + protoMapTypes[name] = t revProtoTypes[t] = name } @@ -954,7 +586,14 @@ func MessageName(x Message) string { } // MessageType returns the message type (pointer to struct) for a named message. -func MessageType(name string) reflect.Type { return protoTypes[name] } +// The type is not guaranteed to implement proto.Message if the name refers to a +// map entry. +func MessageType(name string) reflect.Type { + if t, ok := protoTypedNils[name]; ok { + return reflect.TypeOf(t) + } + return protoMapTypes[name] +} // A registry of all linked proto files. var ( diff --git a/vendor/github.com/gogo/protobuf/proto/properties_gogo.go b/vendor/github.com/gogo/protobuf/proto/properties_gogo.go index b6b7176c5656f..40ea3dd935c27 100644 --- a/vendor/github.com/gogo/protobuf/proto/properties_gogo.go +++ b/vendor/github.com/gogo/protobuf/proto/properties_gogo.go @@ -1,6 +1,6 @@ // Protocol Buffers for Go with Gadgets // -// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without @@ -29,83 +29,8 @@ package proto import ( - "fmt" - "os" "reflect" ) -func (p *Properties) setCustomEncAndDec(typ reflect.Type) { - p.ctype = typ - if p.Repeated { - p.enc = (*Buffer).enc_custom_slice_bytes - p.dec = (*Buffer).dec_custom_slice_bytes - p.size = size_custom_slice_bytes - } else if typ.Kind() == reflect.Ptr { - p.enc = (*Buffer).enc_custom_bytes - p.dec = (*Buffer).dec_custom_bytes - p.size = size_custom_bytes - } else { - p.enc = (*Buffer).enc_custom_ref_bytes - p.dec = (*Buffer).dec_custom_ref_bytes - p.size = size_custom_ref_bytes - } -} - -func (p *Properties) setDurationEncAndDec(typ reflect.Type) { - if p.Repeated { - if typ.Elem().Kind() == reflect.Ptr { - p.enc = (*Buffer).enc_slice_duration - p.dec = (*Buffer).dec_slice_duration - p.size = size_slice_duration - } else { - p.enc = (*Buffer).enc_slice_ref_duration - p.dec = (*Buffer).dec_slice_ref_duration - p.size = size_slice_ref_duration - } - } else if typ.Kind() == reflect.Ptr { - p.enc = (*Buffer).enc_duration - p.dec = (*Buffer).dec_duration - p.size = size_duration - } else { - p.enc = (*Buffer).enc_ref_duration - p.dec = (*Buffer).dec_ref_duration - p.size = size_ref_duration - } -} - -func (p *Properties) setTimeEncAndDec(typ reflect.Type) { - if p.Repeated { - if typ.Elem().Kind() == reflect.Ptr { - p.enc = (*Buffer).enc_slice_time - p.dec = (*Buffer).dec_slice_time - p.size = size_slice_time - } else { - p.enc = (*Buffer).enc_slice_ref_time - p.dec = (*Buffer).dec_slice_ref_time - p.size = size_slice_ref_time - } - } else if typ.Kind() == reflect.Ptr { - p.enc = (*Buffer).enc_time - p.dec = (*Buffer).dec_time - p.size = size_time - } else { - p.enc = (*Buffer).enc_ref_time - p.dec = (*Buffer).dec_ref_time - p.size = size_ref_time - } - -} - -func (p *Properties) setSliceOfNonPointerStructs(typ reflect.Type) { - t2 := typ.Elem() - p.sstype = typ - p.stype = t2 - p.isMarshaler = isMarshaler(t2) - p.isUnmarshaler = isUnmarshaler(t2) - p.enc = (*Buffer).enc_slice_ref_struct_message - p.dec = (*Buffer).dec_slice_ref_struct_message - p.size = size_slice_ref_struct_message - if p.Wire != "bytes" { - fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T \n", typ, t2) - } -} +var sizerType = reflect.TypeOf((*Sizer)(nil)).Elem() +var protosizerType = reflect.TypeOf((*ProtoSizer)(nil)).Elem() diff --git a/vendor/github.com/gogo/protobuf/proto/table_marshal.go b/vendor/github.com/gogo/protobuf/proto/table_marshal.go new file mode 100644 index 0000000000000..f8babdefab94e --- /dev/null +++ b/vendor/github.com/gogo/protobuf/proto/table_marshal.go @@ -0,0 +1,3009 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import ( + "errors" + "fmt" + "math" + "reflect" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + "unicode/utf8" +) + +// a sizer takes a pointer to a field and the size of its tag, computes the size of +// the encoded data. +type sizer func(pointer, int) int + +// a marshaler takes a byte slice, a pointer to a field, and its tag (in wire format), +// marshals the field to the end of the slice, returns the slice and error (if any). +type marshaler func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) + +// marshalInfo is the information used for marshaling a message. +type marshalInfo struct { + typ reflect.Type + fields []*marshalFieldInfo + unrecognized field // offset of XXX_unrecognized + extensions field // offset of XXX_InternalExtensions + v1extensions field // offset of XXX_extensions + sizecache field // offset of XXX_sizecache + initialized int32 // 0 -- only typ is set, 1 -- fully initialized + messageset bool // uses message set wire format + hasmarshaler bool // has custom marshaler + sync.RWMutex // protect extElems map, also for initialization + extElems map[int32]*marshalElemInfo // info of extension elements + + hassizer bool // has custom sizer + hasprotosizer bool // has custom protosizer + + bytesExtensions field // offset of XXX_extensions where the field type is []byte +} + +// marshalFieldInfo is the information used for marshaling a field of a message. +type marshalFieldInfo struct { + field field + wiretag uint64 // tag in wire format + tagsize int // size of tag in wire format + sizer sizer + marshaler marshaler + isPointer bool + required bool // field is required + name string // name of the field, for error reporting + oneofElems map[reflect.Type]*marshalElemInfo // info of oneof elements +} + +// marshalElemInfo is the information used for marshaling an extension or oneof element. +type marshalElemInfo struct { + wiretag uint64 // tag in wire format + tagsize int // size of tag in wire format + sizer sizer + marshaler marshaler + isptr bool // elem is pointer typed, thus interface of this type is a direct interface (extension only) +} + +var ( + marshalInfoMap = map[reflect.Type]*marshalInfo{} + marshalInfoLock sync.Mutex + + uint8SliceType = reflect.TypeOf(([]uint8)(nil)).Kind() +) + +// getMarshalInfo returns the information to marshal a given type of message. +// The info it returns may not necessarily initialized. +// t is the type of the message (NOT the pointer to it). +func getMarshalInfo(t reflect.Type) *marshalInfo { + marshalInfoLock.Lock() + u, ok := marshalInfoMap[t] + if !ok { + u = &marshalInfo{typ: t} + marshalInfoMap[t] = u + } + marshalInfoLock.Unlock() + return u +} + +// Size is the entry point from generated code, +// and should be ONLY called by generated code. +// It computes the size of encoded data of msg. +// a is a pointer to a place to store cached marshal info. +func (a *InternalMessageInfo) Size(msg Message) int { + u := getMessageMarshalInfo(msg, a) + ptr := toPointer(&msg) + if ptr.isNil() { + // We get here if msg is a typed nil ((*SomeMessage)(nil)), + // so it satisfies the interface, and msg == nil wouldn't + // catch it. We don't want crash in this case. + return 0 + } + return u.size(ptr) +} + +// Marshal is the entry point from generated code, +// and should be ONLY called by generated code. +// It marshals msg to the end of b. +// a is a pointer to a place to store cached marshal info. +func (a *InternalMessageInfo) Marshal(b []byte, msg Message, deterministic bool) ([]byte, error) { + u := getMessageMarshalInfo(msg, a) + ptr := toPointer(&msg) + if ptr.isNil() { + // We get here if msg is a typed nil ((*SomeMessage)(nil)), + // so it satisfies the interface, and msg == nil wouldn't + // catch it. We don't want crash in this case. + return b, ErrNil + } + return u.marshal(b, ptr, deterministic) +} + +func getMessageMarshalInfo(msg interface{}, a *InternalMessageInfo) *marshalInfo { + // u := a.marshal, but atomically. + // We use an atomic here to ensure memory consistency. + u := atomicLoadMarshalInfo(&a.marshal) + if u == nil { + // Get marshal information from type of message. + t := reflect.ValueOf(msg).Type() + if t.Kind() != reflect.Ptr { + panic(fmt.Sprintf("cannot handle non-pointer message type %v", t)) + } + u = getMarshalInfo(t.Elem()) + // Store it in the cache for later users. + // a.marshal = u, but atomically. + atomicStoreMarshalInfo(&a.marshal, u) + } + return u +} + +// size is the main function to compute the size of the encoded data of a message. +// ptr is the pointer to the message. +func (u *marshalInfo) size(ptr pointer) int { + if atomic.LoadInt32(&u.initialized) == 0 { + u.computeMarshalInfo() + } + + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + if u.hasmarshaler { + // Uses the message's Size method if available + if u.hassizer { + s := ptr.asPointerTo(u.typ).Interface().(Sizer) + return s.Size() + } + // Uses the message's ProtoSize method if available + if u.hasprotosizer { + s := ptr.asPointerTo(u.typ).Interface().(ProtoSizer) + return s.ProtoSize() + } + + m := ptr.asPointerTo(u.typ).Interface().(Marshaler) + b, _ := m.Marshal() + return len(b) + } + + n := 0 + for _, f := range u.fields { + if f.isPointer && ptr.offset(f.field).getPointer().isNil() { + // nil pointer always marshals to nothing + continue + } + n += f.sizer(ptr.offset(f.field), f.tagsize) + } + if u.extensions.IsValid() { + e := ptr.offset(u.extensions).toExtensions() + if u.messageset { + n += u.sizeMessageSet(e) + } else { + n += u.sizeExtensions(e) + } + } + if u.v1extensions.IsValid() { + m := *ptr.offset(u.v1extensions).toOldExtensions() + n += u.sizeV1Extensions(m) + } + if u.bytesExtensions.IsValid() { + s := *ptr.offset(u.bytesExtensions).toBytes() + n += len(s) + } + if u.unrecognized.IsValid() { + s := *ptr.offset(u.unrecognized).toBytes() + n += len(s) + } + + // cache the result for use in marshal + if u.sizecache.IsValid() { + atomic.StoreInt32(ptr.offset(u.sizecache).toInt32(), int32(n)) + } + return n +} + +// cachedsize gets the size from cache. If there is no cache (i.e. message is not generated), +// fall back to compute the size. +func (u *marshalInfo) cachedsize(ptr pointer) int { + if u.sizecache.IsValid() { + return int(atomic.LoadInt32(ptr.offset(u.sizecache).toInt32())) + } + return u.size(ptr) +} + +// marshal is the main function to marshal a message. It takes a byte slice and appends +// the encoded data to the end of the slice, returns the slice and error (if any). +// ptr is the pointer to the message. +// If deterministic is true, map is marshaled in deterministic order. +func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte, error) { + if atomic.LoadInt32(&u.initialized) == 0 { + u.computeMarshalInfo() + } + + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + if u.hasmarshaler { + m := ptr.asPointerTo(u.typ).Interface().(Marshaler) + b1, err := m.Marshal() + b = append(b, b1...) + return b, err + } + + var err, errLater error + // The old marshaler encodes extensions at beginning. + if u.extensions.IsValid() { + e := ptr.offset(u.extensions).toExtensions() + if u.messageset { + b, err = u.appendMessageSet(b, e, deterministic) + } else { + b, err = u.appendExtensions(b, e, deterministic) + } + if err != nil { + return b, err + } + } + if u.v1extensions.IsValid() { + m := *ptr.offset(u.v1extensions).toOldExtensions() + b, err = u.appendV1Extensions(b, m, deterministic) + if err != nil { + return b, err + } + } + if u.bytesExtensions.IsValid() { + s := *ptr.offset(u.bytesExtensions).toBytes() + b = append(b, s...) + } + for _, f := range u.fields { + if f.required { + if f.isPointer && ptr.offset(f.field).getPointer().isNil() { + // Required field is not set. + // We record the error but keep going, to give a complete marshaling. + if errLater == nil { + errLater = &RequiredNotSetError{f.name} + } + continue + } + } + if f.isPointer && ptr.offset(f.field).getPointer().isNil() { + // nil pointer always marshals to nothing + continue + } + b, err = f.marshaler(b, ptr.offset(f.field), f.wiretag, deterministic) + if err != nil { + if err1, ok := err.(*RequiredNotSetError); ok { + // Required field in submessage is not set. + // We record the error but keep going, to give a complete marshaling. + if errLater == nil { + errLater = &RequiredNotSetError{f.name + "." + err1.field} + } + continue + } + if err == errRepeatedHasNil { + err = errors.New("proto: repeated field " + f.name + " has nil element") + } + if err == errInvalidUTF8 { + if errLater == nil { + fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name + errLater = &invalidUTF8Error{fullName} + } + continue + } + return b, err + } + } + if u.unrecognized.IsValid() { + s := *ptr.offset(u.unrecognized).toBytes() + b = append(b, s...) + } + return b, errLater +} + +// computeMarshalInfo initializes the marshal info. +func (u *marshalInfo) computeMarshalInfo() { + u.Lock() + defer u.Unlock() + if u.initialized != 0 { // non-atomic read is ok as it is protected by the lock + return + } + + t := u.typ + u.unrecognized = invalidField + u.extensions = invalidField + u.v1extensions = invalidField + u.bytesExtensions = invalidField + u.sizecache = invalidField + isOneofMessage := false + + if reflect.PtrTo(t).Implements(sizerType) { + u.hassizer = true + } + if reflect.PtrTo(t).Implements(protosizerType) { + u.hasprotosizer = true + } + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + if reflect.PtrTo(t).Implements(marshalerType) { + u.hasmarshaler = true + atomic.StoreInt32(&u.initialized, 1) + return + } + + n := t.NumField() + + // deal with XXX fields first + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.Tag.Get("protobuf_oneof") != "" { + isOneofMessage = true + } + if !strings.HasPrefix(f.Name, "XXX_") { + continue + } + switch f.Name { + case "XXX_sizecache": + u.sizecache = toField(&f) + case "XXX_unrecognized": + u.unrecognized = toField(&f) + case "XXX_InternalExtensions": + u.extensions = toField(&f) + u.messageset = f.Tag.Get("protobuf_messageset") == "1" + case "XXX_extensions": + if f.Type.Kind() == reflect.Map { + u.v1extensions = toField(&f) + } else { + u.bytesExtensions = toField(&f) + } + case "XXX_NoUnkeyedLiteral": + // nothing to do + default: + panic("unknown XXX field: " + f.Name) + } + n-- + } + + // get oneof implementers + var oneofImplementers []interface{} + // gogo: isOneofMessage is needed for embedded oneof messages, without a marshaler and unmarshaler + if isOneofMessage { + switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) { + case oneofFuncsIface: + _, _, _, oneofImplementers = m.XXX_OneofFuncs() + case oneofWrappersIface: + oneofImplementers = m.XXX_OneofWrappers() + } + } + + // normal fields + fields := make([]marshalFieldInfo, n) // batch allocation + u.fields = make([]*marshalFieldInfo, 0, n) + for i, j := 0, 0; i < t.NumField(); i++ { + f := t.Field(i) + + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + field := &fields[j] + j++ + field.name = f.Name + u.fields = append(u.fields, field) + if f.Tag.Get("protobuf_oneof") != "" { + field.computeOneofFieldInfo(&f, oneofImplementers) + continue + } + if f.Tag.Get("protobuf") == "" { + // field has no tag (not in generated message), ignore it + u.fields = u.fields[:len(u.fields)-1] + j-- + continue + } + field.computeMarshalFieldInfo(&f) + } + + // fields are marshaled in tag order on the wire. + sort.Sort(byTag(u.fields)) + + atomic.StoreInt32(&u.initialized, 1) +} + +// helper for sorting fields by tag +type byTag []*marshalFieldInfo + +func (a byTag) Len() int { return len(a) } +func (a byTag) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byTag) Less(i, j int) bool { return a[i].wiretag < a[j].wiretag } + +// getExtElemInfo returns the information to marshal an extension element. +// The info it returns is initialized. +func (u *marshalInfo) getExtElemInfo(desc *ExtensionDesc) *marshalElemInfo { + // get from cache first + u.RLock() + e, ok := u.extElems[desc.Field] + u.RUnlock() + if ok { + return e + } + + t := reflect.TypeOf(desc.ExtensionType) // pointer or slice to basic type or struct + tags := strings.Split(desc.Tag, ",") + tag, err := strconv.Atoi(tags[1]) + if err != nil { + panic("tag is not an integer") + } + wt := wiretype(tags[0]) + sizr, marshalr := typeMarshaler(t, tags, false, false) + e = &marshalElemInfo{ + wiretag: uint64(tag)<<3 | wt, + tagsize: SizeVarint(uint64(tag) << 3), + sizer: sizr, + marshaler: marshalr, + isptr: t.Kind() == reflect.Ptr, + } + + // update cache + u.Lock() + if u.extElems == nil { + u.extElems = make(map[int32]*marshalElemInfo) + } + u.extElems[desc.Field] = e + u.Unlock() + return e +} + +// computeMarshalFieldInfo fills up the information to marshal a field. +func (fi *marshalFieldInfo) computeMarshalFieldInfo(f *reflect.StructField) { + // parse protobuf tag of the field. + // tag has format of "bytes,49,opt,name=foo,def=hello!" + tags := strings.Split(f.Tag.Get("protobuf"), ",") + if tags[0] == "" { + return + } + tag, err := strconv.Atoi(tags[1]) + if err != nil { + panic("tag is not an integer") + } + wt := wiretype(tags[0]) + if tags[2] == "req" { + fi.required = true + } + fi.setTag(f, tag, wt) + fi.setMarshaler(f, tags) +} + +func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofImplementers []interface{}) { + fi.field = toField(f) + fi.wiretag = math.MaxInt32 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire. + fi.isPointer = true + fi.sizer, fi.marshaler = makeOneOfMarshaler(fi, f) + fi.oneofElems = make(map[reflect.Type]*marshalElemInfo) + + ityp := f.Type // interface type + for _, o := range oneofImplementers { + t := reflect.TypeOf(o) + if !t.Implements(ityp) { + continue + } + sf := t.Elem().Field(0) // oneof implementer is a struct with a single field + tags := strings.Split(sf.Tag.Get("protobuf"), ",") + tag, err := strconv.Atoi(tags[1]) + if err != nil { + panic("tag is not an integer") + } + wt := wiretype(tags[0]) + sizr, marshalr := typeMarshaler(sf.Type, tags, false, true) // oneof should not omit any zero value + fi.oneofElems[t.Elem()] = &marshalElemInfo{ + wiretag: uint64(tag)<<3 | wt, + tagsize: SizeVarint(uint64(tag) << 3), + sizer: sizr, + marshaler: marshalr, + } + } +} + +// wiretype returns the wire encoding of the type. +func wiretype(encoding string) uint64 { + switch encoding { + case "fixed32": + return WireFixed32 + case "fixed64": + return WireFixed64 + case "varint", "zigzag32", "zigzag64": + return WireVarint + case "bytes": + return WireBytes + case "group": + return WireStartGroup + } + panic("unknown wire type " + encoding) +} + +// setTag fills up the tag (in wire format) and its size in the info of a field. +func (fi *marshalFieldInfo) setTag(f *reflect.StructField, tag int, wt uint64) { + fi.field = toField(f) + fi.wiretag = uint64(tag)<<3 | wt + fi.tagsize = SizeVarint(uint64(tag) << 3) +} + +// setMarshaler fills up the sizer and marshaler in the info of a field. +func (fi *marshalFieldInfo) setMarshaler(f *reflect.StructField, tags []string) { + switch f.Type.Kind() { + case reflect.Map: + // map field + fi.isPointer = true + fi.sizer, fi.marshaler = makeMapMarshaler(f) + return + case reflect.Ptr, reflect.Slice: + fi.isPointer = true + } + fi.sizer, fi.marshaler = typeMarshaler(f.Type, tags, true, false) +} + +// typeMarshaler returns the sizer and marshaler of a given field. +// t is the type of the field. +// tags is the generated "protobuf" tag of the field. +// If nozero is true, zero value is not marshaled to the wire. +// If oneof is true, it is a oneof field. +func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, marshaler) { + encoding := tags[0] + + pointer := false + slice := false + if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 { + slice = true + t = t.Elem() + } + if t.Kind() == reflect.Ptr { + pointer = true + t = t.Elem() + } + + packed := false + proto3 := false + ctype := false + isTime := false + isDuration := false + isWktPointer := false + validateUTF8 := true + for i := 2; i < len(tags); i++ { + if tags[i] == "packed" { + packed = true + } + if tags[i] == "proto3" { + proto3 = true + } + if strings.HasPrefix(tags[i], "customtype=") { + ctype = true + } + if tags[i] == "stdtime" { + isTime = true + } + if tags[i] == "stdduration" { + isDuration = true + } + if tags[i] == "wktptr" { + isWktPointer = true + } + } + validateUTF8 = validateUTF8 && proto3 + if !proto3 && !pointer && !slice { + nozero = false + } + + if ctype { + if reflect.PtrTo(t).Implements(customType) { + if slice { + return makeMessageRefSliceMarshaler(getMarshalInfo(t)) + } + if pointer { + return makeCustomPtrMarshaler(getMarshalInfo(t)) + } + return makeCustomMarshaler(getMarshalInfo(t)) + } else { + panic(fmt.Sprintf("custom type: type: %v, does not implement the proto.custom interface", t)) + } + } + + if isTime { + if pointer { + if slice { + return makeTimePtrSliceMarshaler(getMarshalInfo(t)) + } + return makeTimePtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeTimeSliceMarshaler(getMarshalInfo(t)) + } + return makeTimeMarshaler(getMarshalInfo(t)) + } + + if isDuration { + if pointer { + if slice { + return makeDurationPtrSliceMarshaler(getMarshalInfo(t)) + } + return makeDurationPtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeDurationSliceMarshaler(getMarshalInfo(t)) + } + return makeDurationMarshaler(getMarshalInfo(t)) + } + + if isWktPointer { + switch t.Kind() { + case reflect.Float64: + if pointer { + if slice { + return makeStdDoubleValuePtrSliceMarshaler(getMarshalInfo(t)) + } + return makeStdDoubleValuePtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeStdDoubleValueSliceMarshaler(getMarshalInfo(t)) + } + return makeStdDoubleValueMarshaler(getMarshalInfo(t)) + case reflect.Float32: + if pointer { + if slice { + return makeStdFloatValuePtrSliceMarshaler(getMarshalInfo(t)) + } + return makeStdFloatValuePtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeStdFloatValueSliceMarshaler(getMarshalInfo(t)) + } + return makeStdFloatValueMarshaler(getMarshalInfo(t)) + case reflect.Int64: + if pointer { + if slice { + return makeStdInt64ValuePtrSliceMarshaler(getMarshalInfo(t)) + } + return makeStdInt64ValuePtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeStdInt64ValueSliceMarshaler(getMarshalInfo(t)) + } + return makeStdInt64ValueMarshaler(getMarshalInfo(t)) + case reflect.Uint64: + if pointer { + if slice { + return makeStdUInt64ValuePtrSliceMarshaler(getMarshalInfo(t)) + } + return makeStdUInt64ValuePtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeStdUInt64ValueSliceMarshaler(getMarshalInfo(t)) + } + return makeStdUInt64ValueMarshaler(getMarshalInfo(t)) + case reflect.Int32: + if pointer { + if slice { + return makeStdInt32ValuePtrSliceMarshaler(getMarshalInfo(t)) + } + return makeStdInt32ValuePtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeStdInt32ValueSliceMarshaler(getMarshalInfo(t)) + } + return makeStdInt32ValueMarshaler(getMarshalInfo(t)) + case reflect.Uint32: + if pointer { + if slice { + return makeStdUInt32ValuePtrSliceMarshaler(getMarshalInfo(t)) + } + return makeStdUInt32ValuePtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeStdUInt32ValueSliceMarshaler(getMarshalInfo(t)) + } + return makeStdUInt32ValueMarshaler(getMarshalInfo(t)) + case reflect.Bool: + if pointer { + if slice { + return makeStdBoolValuePtrSliceMarshaler(getMarshalInfo(t)) + } + return makeStdBoolValuePtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeStdBoolValueSliceMarshaler(getMarshalInfo(t)) + } + return makeStdBoolValueMarshaler(getMarshalInfo(t)) + case reflect.String: + if pointer { + if slice { + return makeStdStringValuePtrSliceMarshaler(getMarshalInfo(t)) + } + return makeStdStringValuePtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeStdStringValueSliceMarshaler(getMarshalInfo(t)) + } + return makeStdStringValueMarshaler(getMarshalInfo(t)) + case uint8SliceType: + if pointer { + if slice { + return makeStdBytesValuePtrSliceMarshaler(getMarshalInfo(t)) + } + return makeStdBytesValuePtrMarshaler(getMarshalInfo(t)) + } + if slice { + return makeStdBytesValueSliceMarshaler(getMarshalInfo(t)) + } + return makeStdBytesValueMarshaler(getMarshalInfo(t)) + default: + panic(fmt.Sprintf("unknown wktpointer type %#v", t)) + } + } + + switch t.Kind() { + case reflect.Bool: + if pointer { + return sizeBoolPtr, appendBoolPtr + } + if slice { + if packed { + return sizeBoolPackedSlice, appendBoolPackedSlice + } + return sizeBoolSlice, appendBoolSlice + } + if nozero { + return sizeBoolValueNoZero, appendBoolValueNoZero + } + return sizeBoolValue, appendBoolValue + case reflect.Uint32: + switch encoding { + case "fixed32": + if pointer { + return sizeFixed32Ptr, appendFixed32Ptr + } + if slice { + if packed { + return sizeFixed32PackedSlice, appendFixed32PackedSlice + } + return sizeFixed32Slice, appendFixed32Slice + } + if nozero { + return sizeFixed32ValueNoZero, appendFixed32ValueNoZero + } + return sizeFixed32Value, appendFixed32Value + case "varint": + if pointer { + return sizeVarint32Ptr, appendVarint32Ptr + } + if slice { + if packed { + return sizeVarint32PackedSlice, appendVarint32PackedSlice + } + return sizeVarint32Slice, appendVarint32Slice + } + if nozero { + return sizeVarint32ValueNoZero, appendVarint32ValueNoZero + } + return sizeVarint32Value, appendVarint32Value + } + case reflect.Int32: + switch encoding { + case "fixed32": + if pointer { + return sizeFixedS32Ptr, appendFixedS32Ptr + } + if slice { + if packed { + return sizeFixedS32PackedSlice, appendFixedS32PackedSlice + } + return sizeFixedS32Slice, appendFixedS32Slice + } + if nozero { + return sizeFixedS32ValueNoZero, appendFixedS32ValueNoZero + } + return sizeFixedS32Value, appendFixedS32Value + case "varint": + if pointer { + return sizeVarintS32Ptr, appendVarintS32Ptr + } + if slice { + if packed { + return sizeVarintS32PackedSlice, appendVarintS32PackedSlice + } + return sizeVarintS32Slice, appendVarintS32Slice + } + if nozero { + return sizeVarintS32ValueNoZero, appendVarintS32ValueNoZero + } + return sizeVarintS32Value, appendVarintS32Value + case "zigzag32": + if pointer { + return sizeZigzag32Ptr, appendZigzag32Ptr + } + if slice { + if packed { + return sizeZigzag32PackedSlice, appendZigzag32PackedSlice + } + return sizeZigzag32Slice, appendZigzag32Slice + } + if nozero { + return sizeZigzag32ValueNoZero, appendZigzag32ValueNoZero + } + return sizeZigzag32Value, appendZigzag32Value + } + case reflect.Uint64: + switch encoding { + case "fixed64": + if pointer { + return sizeFixed64Ptr, appendFixed64Ptr + } + if slice { + if packed { + return sizeFixed64PackedSlice, appendFixed64PackedSlice + } + return sizeFixed64Slice, appendFixed64Slice + } + if nozero { + return sizeFixed64ValueNoZero, appendFixed64ValueNoZero + } + return sizeFixed64Value, appendFixed64Value + case "varint": + if pointer { + return sizeVarint64Ptr, appendVarint64Ptr + } + if slice { + if packed { + return sizeVarint64PackedSlice, appendVarint64PackedSlice + } + return sizeVarint64Slice, appendVarint64Slice + } + if nozero { + return sizeVarint64ValueNoZero, appendVarint64ValueNoZero + } + return sizeVarint64Value, appendVarint64Value + } + case reflect.Int64: + switch encoding { + case "fixed64": + if pointer { + return sizeFixedS64Ptr, appendFixedS64Ptr + } + if slice { + if packed { + return sizeFixedS64PackedSlice, appendFixedS64PackedSlice + } + return sizeFixedS64Slice, appendFixedS64Slice + } + if nozero { + return sizeFixedS64ValueNoZero, appendFixedS64ValueNoZero + } + return sizeFixedS64Value, appendFixedS64Value + case "varint": + if pointer { + return sizeVarintS64Ptr, appendVarintS64Ptr + } + if slice { + if packed { + return sizeVarintS64PackedSlice, appendVarintS64PackedSlice + } + return sizeVarintS64Slice, appendVarintS64Slice + } + if nozero { + return sizeVarintS64ValueNoZero, appendVarintS64ValueNoZero + } + return sizeVarintS64Value, appendVarintS64Value + case "zigzag64": + if pointer { + return sizeZigzag64Ptr, appendZigzag64Ptr + } + if slice { + if packed { + return sizeZigzag64PackedSlice, appendZigzag64PackedSlice + } + return sizeZigzag64Slice, appendZigzag64Slice + } + if nozero { + return sizeZigzag64ValueNoZero, appendZigzag64ValueNoZero + } + return sizeZigzag64Value, appendZigzag64Value + } + case reflect.Float32: + if pointer { + return sizeFloat32Ptr, appendFloat32Ptr + } + if slice { + if packed { + return sizeFloat32PackedSlice, appendFloat32PackedSlice + } + return sizeFloat32Slice, appendFloat32Slice + } + if nozero { + return sizeFloat32ValueNoZero, appendFloat32ValueNoZero + } + return sizeFloat32Value, appendFloat32Value + case reflect.Float64: + if pointer { + return sizeFloat64Ptr, appendFloat64Ptr + } + if slice { + if packed { + return sizeFloat64PackedSlice, appendFloat64PackedSlice + } + return sizeFloat64Slice, appendFloat64Slice + } + if nozero { + return sizeFloat64ValueNoZero, appendFloat64ValueNoZero + } + return sizeFloat64Value, appendFloat64Value + case reflect.String: + if validateUTF8 { + if pointer { + return sizeStringPtr, appendUTF8StringPtr + } + if slice { + return sizeStringSlice, appendUTF8StringSlice + } + if nozero { + return sizeStringValueNoZero, appendUTF8StringValueNoZero + } + return sizeStringValue, appendUTF8StringValue + } + if pointer { + return sizeStringPtr, appendStringPtr + } + if slice { + return sizeStringSlice, appendStringSlice + } + if nozero { + return sizeStringValueNoZero, appendStringValueNoZero + } + return sizeStringValue, appendStringValue + case reflect.Slice: + if slice { + return sizeBytesSlice, appendBytesSlice + } + if oneof { + // Oneof bytes field may also have "proto3" tag. + // We want to marshal it as a oneof field. Do this + // check before the proto3 check. + return sizeBytesOneof, appendBytesOneof + } + if proto3 { + return sizeBytes3, appendBytes3 + } + return sizeBytes, appendBytes + case reflect.Struct: + switch encoding { + case "group": + if slice { + return makeGroupSliceMarshaler(getMarshalInfo(t)) + } + return makeGroupMarshaler(getMarshalInfo(t)) + case "bytes": + if pointer { + if slice { + return makeMessageSliceMarshaler(getMarshalInfo(t)) + } + return makeMessageMarshaler(getMarshalInfo(t)) + } else { + if slice { + return makeMessageRefSliceMarshaler(getMarshalInfo(t)) + } + return makeMessageRefMarshaler(getMarshalInfo(t)) + } + } + } + panic(fmt.Sprintf("unknown or mismatched type: type: %v, wire type: %v", t, encoding)) +} + +// Below are functions to size/marshal a specific type of a field. +// They are stored in the field's info, and called by function pointers. +// They have type sizer or marshaler. + +func sizeFixed32Value(_ pointer, tagsize int) int { + return 4 + tagsize +} +func sizeFixed32ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toUint32() + if v == 0 { + return 0 + } + return 4 + tagsize +} +func sizeFixed32Ptr(ptr pointer, tagsize int) int { + p := *ptr.toUint32Ptr() + if p == nil { + return 0 + } + return 4 + tagsize +} +func sizeFixed32Slice(ptr pointer, tagsize int) int { + s := *ptr.toUint32Slice() + return (4 + tagsize) * len(s) +} +func sizeFixed32PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toUint32Slice() + if len(s) == 0 { + return 0 + } + return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize +} +func sizeFixedS32Value(_ pointer, tagsize int) int { + return 4 + tagsize +} +func sizeFixedS32ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt32() + if v == 0 { + return 0 + } + return 4 + tagsize +} +func sizeFixedS32Ptr(ptr pointer, tagsize int) int { + p := ptr.getInt32Ptr() + if p == nil { + return 0 + } + return 4 + tagsize +} +func sizeFixedS32Slice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + return (4 + tagsize) * len(s) +} +func sizeFixedS32PackedSlice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + if len(s) == 0 { + return 0 + } + return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize +} +func sizeFloat32Value(_ pointer, tagsize int) int { + return 4 + tagsize +} +func sizeFloat32ValueNoZero(ptr pointer, tagsize int) int { + v := math.Float32bits(*ptr.toFloat32()) + if v == 0 { + return 0 + } + return 4 + tagsize +} +func sizeFloat32Ptr(ptr pointer, tagsize int) int { + p := *ptr.toFloat32Ptr() + if p == nil { + return 0 + } + return 4 + tagsize +} +func sizeFloat32Slice(ptr pointer, tagsize int) int { + s := *ptr.toFloat32Slice() + return (4 + tagsize) * len(s) +} +func sizeFloat32PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toFloat32Slice() + if len(s) == 0 { + return 0 + } + return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize +} +func sizeFixed64Value(_ pointer, tagsize int) int { + return 8 + tagsize +} +func sizeFixed64ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toUint64() + if v == 0 { + return 0 + } + return 8 + tagsize +} +func sizeFixed64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toUint64Ptr() + if p == nil { + return 0 + } + return 8 + tagsize +} +func sizeFixed64Slice(ptr pointer, tagsize int) int { + s := *ptr.toUint64Slice() + return (8 + tagsize) * len(s) +} +func sizeFixed64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toUint64Slice() + if len(s) == 0 { + return 0 + } + return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize +} +func sizeFixedS64Value(_ pointer, tagsize int) int { + return 8 + tagsize +} +func sizeFixedS64ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt64() + if v == 0 { + return 0 + } + return 8 + tagsize +} +func sizeFixedS64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toInt64Ptr() + if p == nil { + return 0 + } + return 8 + tagsize +} +func sizeFixedS64Slice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + return (8 + tagsize) * len(s) +} +func sizeFixedS64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return 0 + } + return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize +} +func sizeFloat64Value(_ pointer, tagsize int) int { + return 8 + tagsize +} +func sizeFloat64ValueNoZero(ptr pointer, tagsize int) int { + v := math.Float64bits(*ptr.toFloat64()) + if v == 0 { + return 0 + } + return 8 + tagsize +} +func sizeFloat64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toFloat64Ptr() + if p == nil { + return 0 + } + return 8 + tagsize +} +func sizeFloat64Slice(ptr pointer, tagsize int) int { + s := *ptr.toFloat64Slice() + return (8 + tagsize) * len(s) +} +func sizeFloat64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toFloat64Slice() + if len(s) == 0 { + return 0 + } + return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize +} +func sizeVarint32Value(ptr pointer, tagsize int) int { + v := *ptr.toUint32() + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarint32ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toUint32() + if v == 0 { + return 0 + } + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarint32Ptr(ptr pointer, tagsize int) int { + p := *ptr.toUint32Ptr() + if p == nil { + return 0 + } + return SizeVarint(uint64(*p)) + tagsize +} +func sizeVarint32Slice(ptr pointer, tagsize int) int { + s := *ptr.toUint32Slice() + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + tagsize + } + return n +} +func sizeVarint32PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toUint32Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeVarintS32Value(ptr pointer, tagsize int) int { + v := *ptr.toInt32() + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarintS32ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt32() + if v == 0 { + return 0 + } + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarintS32Ptr(ptr pointer, tagsize int) int { + p := ptr.getInt32Ptr() + if p == nil { + return 0 + } + return SizeVarint(uint64(*p)) + tagsize +} +func sizeVarintS32Slice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + tagsize + } + return n +} +func sizeVarintS32PackedSlice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeVarint64Value(ptr pointer, tagsize int) int { + v := *ptr.toUint64() + return SizeVarint(v) + tagsize +} +func sizeVarint64ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toUint64() + if v == 0 { + return 0 + } + return SizeVarint(v) + tagsize +} +func sizeVarint64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toUint64Ptr() + if p == nil { + return 0 + } + return SizeVarint(*p) + tagsize +} +func sizeVarint64Slice(ptr pointer, tagsize int) int { + s := *ptr.toUint64Slice() + n := 0 + for _, v := range s { + n += SizeVarint(v) + tagsize + } + return n +} +func sizeVarint64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toUint64Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(v) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeVarintS64Value(ptr pointer, tagsize int) int { + v := *ptr.toInt64() + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarintS64ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt64() + if v == 0 { + return 0 + } + return SizeVarint(uint64(v)) + tagsize +} +func sizeVarintS64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toInt64Ptr() + if p == nil { + return 0 + } + return SizeVarint(uint64(*p)) + tagsize +} +func sizeVarintS64Slice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + tagsize + } + return n +} +func sizeVarintS64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeZigzag32Value(ptr pointer, tagsize int) int { + v := *ptr.toInt32() + return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize +} +func sizeZigzag32ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt32() + if v == 0 { + return 0 + } + return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize +} +func sizeZigzag32Ptr(ptr pointer, tagsize int) int { + p := ptr.getInt32Ptr() + if p == nil { + return 0 + } + v := *p + return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize +} +func sizeZigzag32Slice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + n := 0 + for _, v := range s { + n += SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize + } + return n +} +func sizeZigzag32PackedSlice(ptr pointer, tagsize int) int { + s := ptr.getInt32Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31)))) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeZigzag64Value(ptr pointer, tagsize int) int { + v := *ptr.toInt64() + return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize +} +func sizeZigzag64ValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toInt64() + if v == 0 { + return 0 + } + return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize +} +func sizeZigzag64Ptr(ptr pointer, tagsize int) int { + p := *ptr.toInt64Ptr() + if p == nil { + return 0 + } + v := *p + return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize +} +func sizeZigzag64Slice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize + } + return n +} +func sizeZigzag64PackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return 0 + } + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v<<1) ^ uint64((int64(v) >> 63))) + } + return n + SizeVarint(uint64(n)) + tagsize +} +func sizeBoolValue(_ pointer, tagsize int) int { + return 1 + tagsize +} +func sizeBoolValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toBool() + if !v { + return 0 + } + return 1 + tagsize +} +func sizeBoolPtr(ptr pointer, tagsize int) int { + p := *ptr.toBoolPtr() + if p == nil { + return 0 + } + return 1 + tagsize +} +func sizeBoolSlice(ptr pointer, tagsize int) int { + s := *ptr.toBoolSlice() + return (1 + tagsize) * len(s) +} +func sizeBoolPackedSlice(ptr pointer, tagsize int) int { + s := *ptr.toBoolSlice() + if len(s) == 0 { + return 0 + } + return len(s) + SizeVarint(uint64(len(s))) + tagsize +} +func sizeStringValue(ptr pointer, tagsize int) int { + v := *ptr.toString() + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeStringValueNoZero(ptr pointer, tagsize int) int { + v := *ptr.toString() + if v == "" { + return 0 + } + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeStringPtr(ptr pointer, tagsize int) int { + p := *ptr.toStringPtr() + if p == nil { + return 0 + } + v := *p + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeStringSlice(ptr pointer, tagsize int) int { + s := *ptr.toStringSlice() + n := 0 + for _, v := range s { + n += len(v) + SizeVarint(uint64(len(v))) + tagsize + } + return n +} +func sizeBytes(ptr pointer, tagsize int) int { + v := *ptr.toBytes() + if v == nil { + return 0 + } + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeBytes3(ptr pointer, tagsize int) int { + v := *ptr.toBytes() + if len(v) == 0 { + return 0 + } + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeBytesOneof(ptr pointer, tagsize int) int { + v := *ptr.toBytes() + return len(v) + SizeVarint(uint64(len(v))) + tagsize +} +func sizeBytesSlice(ptr pointer, tagsize int) int { + s := *ptr.toBytesSlice() + n := 0 + for _, v := range s { + n += len(v) + SizeVarint(uint64(len(v))) + tagsize + } + return n +} + +// appendFixed32 appends an encoded fixed32 to b. +func appendFixed32(b []byte, v uint32) []byte { + b = append(b, + byte(v), + byte(v>>8), + byte(v>>16), + byte(v>>24)) + return b +} + +// appendFixed64 appends an encoded fixed64 to b. +func appendFixed64(b []byte, v uint64) []byte { + b = append(b, + byte(v), + byte(v>>8), + byte(v>>16), + byte(v>>24), + byte(v>>32), + byte(v>>40), + byte(v>>48), + byte(v>>56)) + return b +} + +// appendVarint appends an encoded varint to b. +func appendVarint(b []byte, v uint64) []byte { + // TODO: make 1-byte (maybe 2-byte) case inline-able, once we + // have non-leaf inliner. + switch { + case v < 1<<7: + b = append(b, byte(v)) + case v < 1<<14: + b = append(b, + byte(v&0x7f|0x80), + byte(v>>7)) + case v < 1<<21: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte(v>>14)) + case v < 1<<28: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte(v>>21)) + case v < 1<<35: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte(v>>28)) + case v < 1<<42: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte((v>>28)&0x7f|0x80), + byte(v>>35)) + case v < 1<<49: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte((v>>28)&0x7f|0x80), + byte((v>>35)&0x7f|0x80), + byte(v>>42)) + case v < 1<<56: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte((v>>28)&0x7f|0x80), + byte((v>>35)&0x7f|0x80), + byte((v>>42)&0x7f|0x80), + byte(v>>49)) + case v < 1<<63: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte((v>>28)&0x7f|0x80), + byte((v>>35)&0x7f|0x80), + byte((v>>42)&0x7f|0x80), + byte((v>>49)&0x7f|0x80), + byte(v>>56)) + default: + b = append(b, + byte(v&0x7f|0x80), + byte((v>>7)&0x7f|0x80), + byte((v>>14)&0x7f|0x80), + byte((v>>21)&0x7f|0x80), + byte((v>>28)&0x7f|0x80), + byte((v>>35)&0x7f|0x80), + byte((v>>42)&0x7f|0x80), + byte((v>>49)&0x7f|0x80), + byte((v>>56)&0x7f|0x80), + 1) + } + return b +} + +func appendFixed32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint32() + b = appendVarint(b, wiretag) + b = appendFixed32(b, v) + return b, nil +} +func appendFixed32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint32() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, v) + return b, nil +} +func appendFixed32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toUint32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, *p) + return b, nil +} +func appendFixed32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed32(b, v) + } + return b, nil +} +func appendFixed32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(4*len(s))) + for _, v := range s { + b = appendFixed32(b, v) + } + return b, nil +} +func appendFixedS32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + b = appendVarint(b, wiretag) + b = appendFixed32(b, uint32(v)) + return b, nil +} +func appendFixedS32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, uint32(v)) + return b, nil +} +func appendFixedS32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := ptr.getInt32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, uint32(*p)) + return b, nil +} +func appendFixedS32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed32(b, uint32(v)) + } + return b, nil +} +func appendFixedS32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(4*len(s))) + for _, v := range s { + b = appendFixed32(b, uint32(v)) + } + return b, nil +} +func appendFloat32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := math.Float32bits(*ptr.toFloat32()) + b = appendVarint(b, wiretag) + b = appendFixed32(b, v) + return b, nil +} +func appendFloat32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := math.Float32bits(*ptr.toFloat32()) + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, v) + return b, nil +} +func appendFloat32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toFloat32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed32(b, math.Float32bits(*p)) + return b, nil +} +func appendFloat32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toFloat32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed32(b, math.Float32bits(v)) + } + return b, nil +} +func appendFloat32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toFloat32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(4*len(s))) + for _, v := range s { + b = appendFixed32(b, math.Float32bits(v)) + } + return b, nil +} +func appendFixed64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint64() + b = appendVarint(b, wiretag) + b = appendFixed64(b, v) + return b, nil +} +func appendFixed64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint64() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, v) + return b, nil +} +func appendFixed64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toUint64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, *p) + return b, nil +} +func appendFixed64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed64(b, v) + } + return b, nil +} +func appendFixed64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(8*len(s))) + for _, v := range s { + b = appendFixed64(b, v) + } + return b, nil +} +func appendFixedS64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + b = appendVarint(b, wiretag) + b = appendFixed64(b, uint64(v)) + return b, nil +} +func appendFixedS64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, uint64(v)) + return b, nil +} +func appendFixedS64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toInt64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, uint64(*p)) + return b, nil +} +func appendFixedS64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed64(b, uint64(v)) + } + return b, nil +} +func appendFixedS64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(8*len(s))) + for _, v := range s { + b = appendFixed64(b, uint64(v)) + } + return b, nil +} +func appendFloat64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := math.Float64bits(*ptr.toFloat64()) + b = appendVarint(b, wiretag) + b = appendFixed64(b, v) + return b, nil +} +func appendFloat64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := math.Float64bits(*ptr.toFloat64()) + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, v) + return b, nil +} +func appendFloat64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toFloat64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendFixed64(b, math.Float64bits(*p)) + return b, nil +} +func appendFloat64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toFloat64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendFixed64(b, math.Float64bits(v)) + } + return b, nil +} +func appendFloat64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toFloat64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(8*len(s))) + for _, v := range s { + b = appendFixed64(b, math.Float64bits(v)) + } + return b, nil +} +func appendVarint32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint32() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarint32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint32() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarint32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toUint32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(*p)) + return b, nil +} +func appendVarint32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendVarint32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendVarintS32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarintS32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarintS32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := ptr.getInt32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(*p)) + return b, nil +} +func appendVarintS32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendVarintS32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendVarint64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint64() + b = appendVarint(b, wiretag) + b = appendVarint(b, v) + return b, nil +} +func appendVarint64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toUint64() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, v) + return b, nil +} +func appendVarint64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toUint64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, *p) + return b, nil +} +func appendVarint64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, v) + } + return b, nil +} +func appendVarint64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toUint64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(v) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, v) + } + return b, nil +} +func appendVarintS64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarintS64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + return b, nil +} +func appendVarintS64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toInt64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(*p)) + return b, nil +} +func appendVarintS64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendVarintS64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v)) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, uint64(v)) + } + return b, nil +} +func appendZigzag32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + return b, nil +} +func appendZigzag32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt32() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + return b, nil +} +func appendZigzag32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := ptr.getInt32Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + v := *p + b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + return b, nil +} +func appendZigzag32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + } + return b, nil +} +func appendZigzag32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := ptr.getInt32Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31)))) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + } + return b, nil +} +func appendZigzag64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) + return b, nil +} +func appendZigzag64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toInt64() + if v == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) + return b, nil +} +func appendZigzag64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toInt64Ptr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + v := *p + b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) + return b, nil +} +func appendZigzag64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) + } + return b, nil +} +func appendZigzag64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toInt64Slice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + // compute size + n := 0 + for _, v := range s { + n += SizeVarint(uint64(v<<1) ^ uint64((int64(v) >> 63))) + } + b = appendVarint(b, uint64(n)) + for _, v := range s { + b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) + } + return b, nil +} +func appendBoolValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toBool() + b = appendVarint(b, wiretag) + if v { + b = append(b, 1) + } else { + b = append(b, 0) + } + return b, nil +} +func appendBoolValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toBool() + if !v { + return b, nil + } + b = appendVarint(b, wiretag) + b = append(b, 1) + return b, nil +} + +func appendBoolPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toBoolPtr() + if p == nil { + return b, nil + } + b = appendVarint(b, wiretag) + if *p { + b = append(b, 1) + } else { + b = append(b, 0) + } + return b, nil +} +func appendBoolSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toBoolSlice() + for _, v := range s { + b = appendVarint(b, wiretag) + if v { + b = append(b, 1) + } else { + b = append(b, 0) + } + } + return b, nil +} +func appendBoolPackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toBoolSlice() + if len(s) == 0 { + return b, nil + } + b = appendVarint(b, wiretag&^7|WireBytes) + b = appendVarint(b, uint64(len(s))) + for _, v := range s { + if v { + b = append(b, 1) + } else { + b = append(b, 0) + } + } + return b, nil +} +func appendStringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toString() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendStringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toString() + if v == "" { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendStringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toStringPtr() + if p == nil { + return b, nil + } + v := *p + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendStringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toStringSlice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + } + return b, nil +} +func appendUTF8StringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool + v := *ptr.toString() + if !utf8.ValidString(v) { + invalidUTF8 = true + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + if invalidUTF8 { + return b, errInvalidUTF8 + } + return b, nil +} +func appendUTF8StringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool + v := *ptr.toString() + if v == "" { + return b, nil + } + if !utf8.ValidString(v) { + invalidUTF8 = true + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + if invalidUTF8 { + return b, errInvalidUTF8 + } + return b, nil +} +func appendUTF8StringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool + p := *ptr.toStringPtr() + if p == nil { + return b, nil + } + v := *p + if !utf8.ValidString(v) { + invalidUTF8 = true + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + if invalidUTF8 { + return b, errInvalidUTF8 + } + return b, nil +} +func appendUTF8StringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool + s := *ptr.toStringSlice() + for _, v := range s { + if !utf8.ValidString(v) { + invalidUTF8 = true + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + } + if invalidUTF8 { + return b, errInvalidUTF8 + } + return b, nil +} +func appendBytes(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toBytes() + if v == nil { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendBytes3(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toBytes() + if len(v) == 0 { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendBytesOneof(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toBytes() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendBytesSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toBytesSlice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + } + return b, nil +} + +// makeGroupMarshaler returns the sizer and marshaler for a group. +// u is the marshal info of the underlying message. +func makeGroupMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + p := ptr.getPointer() + if p.isNil() { + return 0 + } + return u.size(p) + 2*tagsize + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + p := ptr.getPointer() + if p.isNil() { + return b, nil + } + var err error + b = appendVarint(b, wiretag) // start group + b, err = u.marshal(b, p, deterministic) + b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group + return b, err + } +} + +// makeGroupSliceMarshaler returns the sizer and marshaler for a group slice. +// u is the marshal info of the underlying message. +func makeGroupSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getPointerSlice() + n := 0 + for _, v := range s { + if v.isNil() { + continue + } + n += u.size(v) + 2*tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getPointerSlice() + var err error + var nerr nonFatal + for _, v := range s { + if v.isNil() { + return b, errRepeatedHasNil + } + b = appendVarint(b, wiretag) // start group + b, err = u.marshal(b, v, deterministic) + b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group + if !nerr.Merge(err) { + if err == ErrNil { + err = errRepeatedHasNil + } + return b, err + } + } + return b, nerr.E + } +} + +// makeMessageMarshaler returns the sizer and marshaler for a message field. +// u is the marshal info of the message. +func makeMessageMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + p := ptr.getPointer() + if p.isNil() { + return 0 + } + siz := u.size(p) + return siz + SizeVarint(uint64(siz)) + tagsize + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + p := ptr.getPointer() + if p.isNil() { + return b, nil + } + b = appendVarint(b, wiretag) + siz := u.cachedsize(p) + b = appendVarint(b, uint64(siz)) + return u.marshal(b, p, deterministic) + } +} + +// makeMessageSliceMarshaler returns the sizer and marshaler for a message slice. +// u is the marshal info of the message. +func makeMessageSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getPointerSlice() + n := 0 + for _, v := range s { + if v.isNil() { + continue + } + siz := u.size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getPointerSlice() + var err error + var nerr nonFatal + for _, v := range s { + if v.isNil() { + return b, errRepeatedHasNil + } + b = appendVarint(b, wiretag) + siz := u.cachedsize(v) + b = appendVarint(b, uint64(siz)) + b, err = u.marshal(b, v, deterministic) + + if !nerr.Merge(err) { + if err == ErrNil { + err = errRepeatedHasNil + } + return b, err + } + } + return b, nerr.E + } +} + +// makeMapMarshaler returns the sizer and marshaler for a map field. +// f is the pointer to the reflect data structure of the field. +func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) { + // figure out key and value type + t := f.Type + keyType := t.Key() + valType := t.Elem() + tags := strings.Split(f.Tag.Get("protobuf"), ",") + keyTags := strings.Split(f.Tag.Get("protobuf_key"), ",") + valTags := strings.Split(f.Tag.Get("protobuf_val"), ",") + stdOptions := false + for _, t := range tags { + if strings.HasPrefix(t, "customtype=") { + valTags = append(valTags, t) + } + if t == "stdtime" { + valTags = append(valTags, t) + stdOptions = true + } + if t == "stdduration" { + valTags = append(valTags, t) + stdOptions = true + } + if t == "wktptr" { + valTags = append(valTags, t) + } + } + keySizer, keyMarshaler := typeMarshaler(keyType, keyTags, false, false) // don't omit zero value in map + valSizer, valMarshaler := typeMarshaler(valType, valTags, false, false) // don't omit zero value in map + keyWireTag := 1<<3 | wiretype(keyTags[0]) + valWireTag := 2<<3 | wiretype(valTags[0]) + + // We create an interface to get the addresses of the map key and value. + // If value is pointer-typed, the interface is a direct interface, the + // idata itself is the value. Otherwise, the idata is the pointer to the + // value. + // Key cannot be pointer-typed. + valIsPtr := valType.Kind() == reflect.Ptr + + // If value is a message with nested maps, calling + // valSizer in marshal may be quadratic. We should use + // cached version in marshal (but not in size). + // If value is not message type, we don't have size cache, + // but it cannot be nested either. Just use valSizer. + valCachedSizer := valSizer + if valIsPtr && !stdOptions && valType.Elem().Kind() == reflect.Struct { + u := getMarshalInfo(valType.Elem()) + valCachedSizer = func(ptr pointer, tagsize int) int { + // Same as message sizer, but use cache. + p := ptr.getPointer() + if p.isNil() { + return 0 + } + siz := u.cachedsize(p) + return siz + SizeVarint(uint64(siz)) + tagsize + } + } + return func(ptr pointer, tagsize int) int { + m := ptr.asPointerTo(t).Elem() // the map + n := 0 + for _, k := range m.MapKeys() { + ki := k.Interface() + vi := m.MapIndex(k).Interface() + kaddr := toAddrPointer(&ki, false) // pointer to key + vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value + siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, tag uint64, deterministic bool) ([]byte, error) { + m := ptr.asPointerTo(t).Elem() // the map + var err error + keys := m.MapKeys() + if len(keys) > 1 && deterministic { + sort.Sort(mapKeys(keys)) + } + + var nerr nonFatal + for _, k := range keys { + ki := k.Interface() + vi := m.MapIndex(k).Interface() + kaddr := toAddrPointer(&ki, false) // pointer to key + vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value + b = appendVarint(b, tag) + siz := keySizer(kaddr, 1) + valCachedSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1) + b = appendVarint(b, uint64(siz)) + b, err = keyMarshaler(b, kaddr, keyWireTag, deterministic) + if !nerr.Merge(err) { + return b, err + } + b, err = valMarshaler(b, vaddr, valWireTag, deterministic) + if err != ErrNil && !nerr.Merge(err) { // allow nil value in map + return b, err + } + } + return b, nerr.E + } +} + +// makeOneOfMarshaler returns the sizer and marshaler for a oneof field. +// fi is the marshal info of the field. +// f is the pointer to the reflect data structure of the field. +func makeOneOfMarshaler(fi *marshalFieldInfo, f *reflect.StructField) (sizer, marshaler) { + // Oneof field is an interface. We need to get the actual data type on the fly. + t := f.Type + return func(ptr pointer, _ int) int { + p := ptr.getInterfacePointer() + if p.isNil() { + return 0 + } + v := ptr.asPointerTo(t).Elem().Elem().Elem() // *interface -> interface -> *struct -> struct + telem := v.Type() + e := fi.oneofElems[telem] + return e.sizer(p, e.tagsize) + }, + func(b []byte, ptr pointer, _ uint64, deterministic bool) ([]byte, error) { + p := ptr.getInterfacePointer() + if p.isNil() { + return b, nil + } + v := ptr.asPointerTo(t).Elem().Elem().Elem() // *interface -> interface -> *struct -> struct + telem := v.Type() + if telem.Field(0).Type.Kind() == reflect.Ptr && p.getPointer().isNil() { + return b, errOneofHasNil + } + e := fi.oneofElems[telem] + return e.marshaler(b, p, e.wiretag, deterministic) + } +} + +// sizeExtensions computes the size of encoded data for a XXX_InternalExtensions field. +func (u *marshalInfo) sizeExtensions(ext *XXX_InternalExtensions) int { + m, mu := ext.extensionsRead() + if m == nil { + return 0 + } + mu.Lock() + + n := 0 + for _, e := range m { + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + n += len(e.enc) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + n += ei.sizer(p, ei.tagsize) + } + mu.Unlock() + return n +} + +// appendExtensions marshals a XXX_InternalExtensions field to the end of byte slice b. +func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, deterministic bool) ([]byte, error) { + m, mu := ext.extensionsRead() + if m == nil { + return b, nil + } + mu.Lock() + defer mu.Unlock() + + var err error + var nerr nonFatal + + // Fast-path for common cases: zero or one extensions. + // Don't bother sorting the keys. + if len(m) <= 1 { + for _, e := range m { + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + b = append(b, e.enc...) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + b, err = ei.marshaler(b, p, ei.wiretag, deterministic) + if !nerr.Merge(err) { + return b, err + } + } + return b, nerr.E + } + + // Sort the keys to provide a deterministic encoding. + // Not sure this is required, but the old code does it. + keys := make([]int, 0, len(m)) + for k := range m { + keys = append(keys, int(k)) + } + sort.Ints(keys) + + for _, k := range keys { + e := m[int32(k)] + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + b = append(b, e.enc...) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + b, err = ei.marshaler(b, p, ei.wiretag, deterministic) + if !nerr.Merge(err) { + return b, err + } + } + return b, nerr.E +} + +// message set format is: +// message MessageSet { +// repeated group Item = 1 { +// required int32 type_id = 2; +// required string message = 3; +// }; +// } + +// sizeMessageSet computes the size of encoded data for a XXX_InternalExtensions field +// in message set format (above). +func (u *marshalInfo) sizeMessageSet(ext *XXX_InternalExtensions) int { + m, mu := ext.extensionsRead() + if m == nil { + return 0 + } + mu.Lock() + + n := 0 + for id, e := range m { + n += 2 // start group, end group. tag = 1 (size=1) + n += SizeVarint(uint64(id)) + 1 // type_id, tag = 2 (size=1) + + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint + siz := len(msgWithLen) + n += siz + 1 // message, tag = 3 (size=1) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + n += ei.sizer(p, 1) // message, tag = 3 (size=1) + } + mu.Unlock() + return n +} + +// appendMessageSet marshals a XXX_InternalExtensions field in message set format (above) +// to the end of byte slice b. +func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, deterministic bool) ([]byte, error) { + m, mu := ext.extensionsRead() + if m == nil { + return b, nil + } + mu.Lock() + defer mu.Unlock() + + var err error + var nerr nonFatal + + // Fast-path for common cases: zero or one extensions. + // Don't bother sorting the keys. + if len(m) <= 1 { + for id, e := range m { + b = append(b, 1<<3|WireStartGroup) + b = append(b, 2<<3|WireVarint) + b = appendVarint(b, uint64(id)) + + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint + b = append(b, 3<<3|WireBytes) + b = append(b, msgWithLen...) + b = append(b, 1<<3|WireEndGroup) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic) + if !nerr.Merge(err) { + return b, err + } + b = append(b, 1<<3|WireEndGroup) + } + return b, nerr.E + } + + // Sort the keys to provide a deterministic encoding. + keys := make([]int, 0, len(m)) + for k := range m { + keys = append(keys, int(k)) + } + sort.Ints(keys) + + for _, id := range keys { + e := m[int32(id)] + b = append(b, 1<<3|WireStartGroup) + b = append(b, 2<<3|WireVarint) + b = appendVarint(b, uint64(id)) + + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint + b = append(b, 3<<3|WireBytes) + b = append(b, msgWithLen...) + b = append(b, 1<<3|WireEndGroup) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic) + b = append(b, 1<<3|WireEndGroup) + if !nerr.Merge(err) { + return b, err + } + } + return b, nerr.E +} + +// sizeV1Extensions computes the size of encoded data for a V1-API extension field. +func (u *marshalInfo) sizeV1Extensions(m map[int32]Extension) int { + if m == nil { + return 0 + } + + n := 0 + for _, e := range m { + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + n += len(e.enc) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + n += ei.sizer(p, ei.tagsize) + } + return n +} + +// appendV1Extensions marshals a V1-API extension field to the end of byte slice b. +func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, deterministic bool) ([]byte, error) { + if m == nil { + return b, nil + } + + // Sort the keys to provide a deterministic encoding. + keys := make([]int, 0, len(m)) + for k := range m { + keys = append(keys, int(k)) + } + sort.Ints(keys) + + var err error + var nerr nonFatal + for _, k := range keys { + e := m[int32(k)] + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + b = append(b, e.enc...) + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + ei := u.getExtElemInfo(e.desc) + v := e.value + p := toAddrPointer(&v, ei.isptr) + b, err = ei.marshaler(b, p, ei.wiretag, deterministic) + if !nerr.Merge(err) { + return b, err + } + } + return b, nerr.E +} + +// newMarshaler is the interface representing objects that can marshal themselves. +// +// This exists to support protoc-gen-go generated messages. +// The proto package will stop type-asserting to this interface in the future. +// +// DO NOT DEPEND ON THIS. +type newMarshaler interface { + XXX_Size() int + XXX_Marshal(b []byte, deterministic bool) ([]byte, error) +} + +// Size returns the encoded size of a protocol buffer message. +// This is the main entry point. +func Size(pb Message) int { + if m, ok := pb.(newMarshaler); ok { + return m.XXX_Size() + } + if m, ok := pb.(Marshaler); ok { + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + b, _ := m.Marshal() + return len(b) + } + // in case somehow we didn't generate the wrapper + if pb == nil { + return 0 + } + var info InternalMessageInfo + return info.Size(pb) +} + +// Marshal takes a protocol buffer message +// and encodes it into the wire format, returning the data. +// This is the main entry point. +func Marshal(pb Message) ([]byte, error) { + if m, ok := pb.(newMarshaler); ok { + siz := m.XXX_Size() + b := make([]byte, 0, siz) + return m.XXX_Marshal(b, false) + } + if m, ok := pb.(Marshaler); ok { + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + return m.Marshal() + } + // in case somehow we didn't generate the wrapper + if pb == nil { + return nil, ErrNil + } + var info InternalMessageInfo + siz := info.Size(pb) + b := make([]byte, 0, siz) + return info.Marshal(b, pb, false) +} + +// Marshal takes a protocol buffer message +// and encodes it into the wire format, writing the result to the +// Buffer. +// This is an alternative entry point. It is not necessary to use +// a Buffer for most applications. +func (p *Buffer) Marshal(pb Message) error { + var err error + if p.deterministic { + if _, ok := pb.(Marshaler); ok { + return fmt.Errorf("proto: deterministic not supported by the Marshal method of %T", pb) + } + } + if m, ok := pb.(newMarshaler); ok { + siz := m.XXX_Size() + p.grow(siz) // make sure buf has enough capacity + pp := p.buf[len(p.buf) : len(p.buf) : len(p.buf)+siz] + pp, err = m.XXX_Marshal(pp, p.deterministic) + p.buf = append(p.buf, pp...) + return err + } + if m, ok := pb.(Marshaler); ok { + // If the message can marshal itself, let it do it, for compatibility. + // NOTE: This is not efficient. + var b []byte + b, err = m.Marshal() + p.buf = append(p.buf, b...) + return err + } + // in case somehow we didn't generate the wrapper + if pb == nil { + return ErrNil + } + var info InternalMessageInfo + siz := info.Size(pb) + p.grow(siz) // make sure buf has enough capacity + p.buf, err = info.Marshal(p.buf, pb, p.deterministic) + return err +} + +// grow grows the buffer's capacity, if necessary, to guarantee space for +// another n bytes. After grow(n), at least n bytes can be written to the +// buffer without another allocation. +func (p *Buffer) grow(n int) { + need := len(p.buf) + n + if need <= cap(p.buf) { + return + } + newCap := len(p.buf) * 2 + if newCap < need { + newCap = need + } + p.buf = append(make([]byte, 0, newCap), p.buf...) +} diff --git a/vendor/github.com/gogo/protobuf/proto/table_marshal_gogo.go b/vendor/github.com/gogo/protobuf/proto/table_marshal_gogo.go new file mode 100644 index 0000000000000..997f57c1e1027 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/proto/table_marshal_gogo.go @@ -0,0 +1,388 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2018, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import ( + "reflect" + "time" +) + +// makeMessageRefMarshaler differs a bit from makeMessageMarshaler +// It marshal a message T instead of a *T +func makeMessageRefMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + siz := u.size(ptr) + return siz + SizeVarint(uint64(siz)) + tagsize + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + b = appendVarint(b, wiretag) + siz := u.cachedsize(ptr) + b = appendVarint(b, uint64(siz)) + return u.marshal(b, ptr, deterministic) + } +} + +// makeMessageRefSliceMarshaler differs quite a lot from makeMessageSliceMarshaler +// It marshals a slice of messages []T instead of []*T +func makeMessageRefSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + e := elem.Interface() + v := toAddrPointer(&e, false) + siz := u.size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + var err, errreq error + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + e := elem.Interface() + v := toAddrPointer(&e, false) + b = appendVarint(b, wiretag) + siz := u.size(v) + b = appendVarint(b, uint64(siz)) + b, err = u.marshal(b, v, deterministic) + + if err != nil { + if _, ok := err.(*RequiredNotSetError); ok { + // Required field in submessage is not set. + // We record the error but keep going, to give a complete marshaling. + if errreq == nil { + errreq = err + } + continue + } + if err == ErrNil { + err = errRepeatedHasNil + } + return b, err + } + } + + return b, errreq + } +} + +func makeCustomPtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + m := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(custom) + siz := m.Size() + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + m := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(custom) + siz := m.Size() + buf, err := m.Marshal() + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + return b, nil + } +} + +func makeCustomMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + m := ptr.asPointerTo(u.typ).Interface().(custom) + siz := m.Size() + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + m := ptr.asPointerTo(u.typ).Interface().(custom) + siz := m.Size() + buf, err := m.Marshal() + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + return b, nil + } +} + +func makeTimeMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + t := ptr.asPointerTo(u.typ).Interface().(*time.Time) + ts, err := timestampProto(*t) + if err != nil { + return 0 + } + siz := Size(ts) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + t := ptr.asPointerTo(u.typ).Interface().(*time.Time) + ts, err := timestampProto(*t) + if err != nil { + return nil, err + } + buf, err := Marshal(ts) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeTimePtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*time.Time) + ts, err := timestampProto(*t) + if err != nil { + return 0 + } + siz := Size(ts) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*time.Time) + ts, err := timestampProto(*t) + if err != nil { + return nil, err + } + buf, err := Marshal(ts) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeTimeSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(time.Time) + ts, err := timestampProto(t) + if err != nil { + return 0 + } + siz := Size(ts) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(time.Time) + ts, err := timestampProto(t) + if err != nil { + return nil, err + } + siz := Size(ts) + buf, err := Marshal(ts) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeTimePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*time.Time) + ts, err := timestampProto(*t) + if err != nil { + return 0 + } + siz := Size(ts) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*time.Time) + ts, err := timestampProto(*t) + if err != nil { + return nil, err + } + siz := Size(ts) + buf, err := Marshal(ts) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeDurationMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + d := ptr.asPointerTo(u.typ).Interface().(*time.Duration) + dur := durationProto(*d) + siz := Size(dur) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + d := ptr.asPointerTo(u.typ).Interface().(*time.Duration) + dur := durationProto(*d) + buf, err := Marshal(dur) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeDurationPtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + d := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*time.Duration) + dur := durationProto(*d) + siz := Size(dur) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + d := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*time.Duration) + dur := durationProto(*d) + buf, err := Marshal(dur) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeDurationSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + d := elem.Interface().(time.Duration) + dur := durationProto(d) + siz := Size(dur) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + d := elem.Interface().(time.Duration) + dur := durationProto(d) + siz := Size(dur) + buf, err := Marshal(dur) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeDurationPtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + d := elem.Interface().(*time.Duration) + dur := durationProto(*d) + siz := Size(dur) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + d := elem.Interface().(*time.Duration) + dur := durationProto(*d) + siz := Size(dur) + buf, err := Marshal(dur) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} diff --git a/vendor/github.com/gogo/protobuf/proto/table_merge.go b/vendor/github.com/gogo/protobuf/proto/table_merge.go new file mode 100644 index 0000000000000..60dcf70d1e6c6 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/proto/table_merge.go @@ -0,0 +1,676 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import ( + "fmt" + "reflect" + "strings" + "sync" + "sync/atomic" +) + +// Merge merges the src message into dst. +// This assumes that dst and src of the same type and are non-nil. +func (a *InternalMessageInfo) Merge(dst, src Message) { + mi := atomicLoadMergeInfo(&a.merge) + if mi == nil { + mi = getMergeInfo(reflect.TypeOf(dst).Elem()) + atomicStoreMergeInfo(&a.merge, mi) + } + mi.merge(toPointer(&dst), toPointer(&src)) +} + +type mergeInfo struct { + typ reflect.Type + + initialized int32 // 0: only typ is valid, 1: everything is valid + lock sync.Mutex + + fields []mergeFieldInfo + unrecognized field // Offset of XXX_unrecognized +} + +type mergeFieldInfo struct { + field field // Offset of field, guaranteed to be valid + + // isPointer reports whether the value in the field is a pointer. + // This is true for the following situations: + // * Pointer to struct + // * Pointer to basic type (proto2 only) + // * Slice (first value in slice header is a pointer) + // * String (first value in string header is a pointer) + isPointer bool + + // basicWidth reports the width of the field assuming that it is directly + // embedded in the struct (as is the case for basic types in proto3). + // The possible values are: + // 0: invalid + // 1: bool + // 4: int32, uint32, float32 + // 8: int64, uint64, float64 + basicWidth int + + // Where dst and src are pointers to the types being merged. + merge func(dst, src pointer) +} + +var ( + mergeInfoMap = map[reflect.Type]*mergeInfo{} + mergeInfoLock sync.Mutex +) + +func getMergeInfo(t reflect.Type) *mergeInfo { + mergeInfoLock.Lock() + defer mergeInfoLock.Unlock() + mi := mergeInfoMap[t] + if mi == nil { + mi = &mergeInfo{typ: t} + mergeInfoMap[t] = mi + } + return mi +} + +// merge merges src into dst assuming they are both of type *mi.typ. +func (mi *mergeInfo) merge(dst, src pointer) { + if dst.isNil() { + panic("proto: nil destination") + } + if src.isNil() { + return // Nothing to do. + } + + if atomic.LoadInt32(&mi.initialized) == 0 { + mi.computeMergeInfo() + } + + for _, fi := range mi.fields { + sfp := src.offset(fi.field) + + // As an optimization, we can avoid the merge function call cost + // if we know for sure that the source will have no effect + // by checking if it is the zero value. + if unsafeAllowed { + if fi.isPointer && sfp.getPointer().isNil() { // Could be slice or string + continue + } + if fi.basicWidth > 0 { + switch { + case fi.basicWidth == 1 && !*sfp.toBool(): + continue + case fi.basicWidth == 4 && *sfp.toUint32() == 0: + continue + case fi.basicWidth == 8 && *sfp.toUint64() == 0: + continue + } + } + } + + dfp := dst.offset(fi.field) + fi.merge(dfp, sfp) + } + + // TODO: Make this faster? + out := dst.asPointerTo(mi.typ).Elem() + in := src.asPointerTo(mi.typ).Elem() + if emIn, err := extendable(in.Addr().Interface()); err == nil { + emOut, _ := extendable(out.Addr().Interface()) + mIn, muIn := emIn.extensionsRead() + if mIn != nil { + mOut := emOut.extensionsWrite() + muIn.Lock() + mergeExtension(mOut, mIn) + muIn.Unlock() + } + } + + if mi.unrecognized.IsValid() { + if b := *src.offset(mi.unrecognized).toBytes(); len(b) > 0 { + *dst.offset(mi.unrecognized).toBytes() = append([]byte(nil), b...) + } + } +} + +func (mi *mergeInfo) computeMergeInfo() { + mi.lock.Lock() + defer mi.lock.Unlock() + if mi.initialized != 0 { + return + } + t := mi.typ + n := t.NumField() + + props := GetProperties(t) + for i := 0; i < n; i++ { + f := t.Field(i) + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + + mfi := mergeFieldInfo{field: toField(&f)} + tf := f.Type + + // As an optimization, we can avoid the merge function call cost + // if we know for sure that the source will have no effect + // by checking if it is the zero value. + if unsafeAllowed { + switch tf.Kind() { + case reflect.Ptr, reflect.Slice, reflect.String: + // As a special case, we assume slices and strings are pointers + // since we know that the first field in the SliceSlice or + // StringHeader is a data pointer. + mfi.isPointer = true + case reflect.Bool: + mfi.basicWidth = 1 + case reflect.Int32, reflect.Uint32, reflect.Float32: + mfi.basicWidth = 4 + case reflect.Int64, reflect.Uint64, reflect.Float64: + mfi.basicWidth = 8 + } + } + + // Unwrap tf to get at its most basic type. + var isPointer, isSlice bool + if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { + isSlice = true + tf = tf.Elem() + } + if tf.Kind() == reflect.Ptr { + isPointer = true + tf = tf.Elem() + } + if isPointer && isSlice && tf.Kind() != reflect.Struct { + panic("both pointer and slice for basic type in " + tf.Name()) + } + + switch tf.Kind() { + case reflect.Int32: + switch { + case isSlice: // E.g., []int32 + mfi.merge = func(dst, src pointer) { + // NOTE: toInt32Slice is not defined (see pointer_reflect.go). + /* + sfsp := src.toInt32Slice() + if *sfsp != nil { + dfsp := dst.toInt32Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []int64{} + } + } + */ + sfs := src.getInt32Slice() + if sfs != nil { + dfs := dst.getInt32Slice() + dfs = append(dfs, sfs...) + if dfs == nil { + dfs = []int32{} + } + dst.setInt32Slice(dfs) + } + } + case isPointer: // E.g., *int32 + mfi.merge = func(dst, src pointer) { + // NOTE: toInt32Ptr is not defined (see pointer_reflect.go). + /* + sfpp := src.toInt32Ptr() + if *sfpp != nil { + dfpp := dst.toInt32Ptr() + if *dfpp == nil { + *dfpp = Int32(**sfpp) + } else { + **dfpp = **sfpp + } + } + */ + sfp := src.getInt32Ptr() + if sfp != nil { + dfp := dst.getInt32Ptr() + if dfp == nil { + dst.setInt32Ptr(*sfp) + } else { + *dfp = *sfp + } + } + } + default: // E.g., int32 + mfi.merge = func(dst, src pointer) { + if v := *src.toInt32(); v != 0 { + *dst.toInt32() = v + } + } + } + case reflect.Int64: + switch { + case isSlice: // E.g., []int64 + mfi.merge = func(dst, src pointer) { + sfsp := src.toInt64Slice() + if *sfsp != nil { + dfsp := dst.toInt64Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []int64{} + } + } + } + case isPointer: // E.g., *int64 + mfi.merge = func(dst, src pointer) { + sfpp := src.toInt64Ptr() + if *sfpp != nil { + dfpp := dst.toInt64Ptr() + if *dfpp == nil { + *dfpp = Int64(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., int64 + mfi.merge = func(dst, src pointer) { + if v := *src.toInt64(); v != 0 { + *dst.toInt64() = v + } + } + } + case reflect.Uint32: + switch { + case isSlice: // E.g., []uint32 + mfi.merge = func(dst, src pointer) { + sfsp := src.toUint32Slice() + if *sfsp != nil { + dfsp := dst.toUint32Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []uint32{} + } + } + } + case isPointer: // E.g., *uint32 + mfi.merge = func(dst, src pointer) { + sfpp := src.toUint32Ptr() + if *sfpp != nil { + dfpp := dst.toUint32Ptr() + if *dfpp == nil { + *dfpp = Uint32(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., uint32 + mfi.merge = func(dst, src pointer) { + if v := *src.toUint32(); v != 0 { + *dst.toUint32() = v + } + } + } + case reflect.Uint64: + switch { + case isSlice: // E.g., []uint64 + mfi.merge = func(dst, src pointer) { + sfsp := src.toUint64Slice() + if *sfsp != nil { + dfsp := dst.toUint64Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []uint64{} + } + } + } + case isPointer: // E.g., *uint64 + mfi.merge = func(dst, src pointer) { + sfpp := src.toUint64Ptr() + if *sfpp != nil { + dfpp := dst.toUint64Ptr() + if *dfpp == nil { + *dfpp = Uint64(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., uint64 + mfi.merge = func(dst, src pointer) { + if v := *src.toUint64(); v != 0 { + *dst.toUint64() = v + } + } + } + case reflect.Float32: + switch { + case isSlice: // E.g., []float32 + mfi.merge = func(dst, src pointer) { + sfsp := src.toFloat32Slice() + if *sfsp != nil { + dfsp := dst.toFloat32Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []float32{} + } + } + } + case isPointer: // E.g., *float32 + mfi.merge = func(dst, src pointer) { + sfpp := src.toFloat32Ptr() + if *sfpp != nil { + dfpp := dst.toFloat32Ptr() + if *dfpp == nil { + *dfpp = Float32(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., float32 + mfi.merge = func(dst, src pointer) { + if v := *src.toFloat32(); v != 0 { + *dst.toFloat32() = v + } + } + } + case reflect.Float64: + switch { + case isSlice: // E.g., []float64 + mfi.merge = func(dst, src pointer) { + sfsp := src.toFloat64Slice() + if *sfsp != nil { + dfsp := dst.toFloat64Slice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []float64{} + } + } + } + case isPointer: // E.g., *float64 + mfi.merge = func(dst, src pointer) { + sfpp := src.toFloat64Ptr() + if *sfpp != nil { + dfpp := dst.toFloat64Ptr() + if *dfpp == nil { + *dfpp = Float64(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., float64 + mfi.merge = func(dst, src pointer) { + if v := *src.toFloat64(); v != 0 { + *dst.toFloat64() = v + } + } + } + case reflect.Bool: + switch { + case isSlice: // E.g., []bool + mfi.merge = func(dst, src pointer) { + sfsp := src.toBoolSlice() + if *sfsp != nil { + dfsp := dst.toBoolSlice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []bool{} + } + } + } + case isPointer: // E.g., *bool + mfi.merge = func(dst, src pointer) { + sfpp := src.toBoolPtr() + if *sfpp != nil { + dfpp := dst.toBoolPtr() + if *dfpp == nil { + *dfpp = Bool(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., bool + mfi.merge = func(dst, src pointer) { + if v := *src.toBool(); v { + *dst.toBool() = v + } + } + } + case reflect.String: + switch { + case isSlice: // E.g., []string + mfi.merge = func(dst, src pointer) { + sfsp := src.toStringSlice() + if *sfsp != nil { + dfsp := dst.toStringSlice() + *dfsp = append(*dfsp, *sfsp...) + if *dfsp == nil { + *dfsp = []string{} + } + } + } + case isPointer: // E.g., *string + mfi.merge = func(dst, src pointer) { + sfpp := src.toStringPtr() + if *sfpp != nil { + dfpp := dst.toStringPtr() + if *dfpp == nil { + *dfpp = String(**sfpp) + } else { + **dfpp = **sfpp + } + } + } + default: // E.g., string + mfi.merge = func(dst, src pointer) { + if v := *src.toString(); v != "" { + *dst.toString() = v + } + } + } + case reflect.Slice: + isProto3 := props.Prop[i].proto3 + switch { + case isPointer: + panic("bad pointer in byte slice case in " + tf.Name()) + case tf.Elem().Kind() != reflect.Uint8: + panic("bad element kind in byte slice case in " + tf.Name()) + case isSlice: // E.g., [][]byte + mfi.merge = func(dst, src pointer) { + sbsp := src.toBytesSlice() + if *sbsp != nil { + dbsp := dst.toBytesSlice() + for _, sb := range *sbsp { + if sb == nil { + *dbsp = append(*dbsp, nil) + } else { + *dbsp = append(*dbsp, append([]byte{}, sb...)) + } + } + if *dbsp == nil { + *dbsp = [][]byte{} + } + } + } + default: // E.g., []byte + mfi.merge = func(dst, src pointer) { + sbp := src.toBytes() + if *sbp != nil { + dbp := dst.toBytes() + if !isProto3 || len(*sbp) > 0 { + *dbp = append([]byte{}, *sbp...) + } + } + } + } + case reflect.Struct: + switch { + case isSlice && !isPointer: // E.g. []pb.T + mergeInfo := getMergeInfo(tf) + zero := reflect.Zero(tf) + mfi.merge = func(dst, src pointer) { + // TODO: Make this faster? + dstsp := dst.asPointerTo(f.Type) + dsts := dstsp.Elem() + srcs := src.asPointerTo(f.Type).Elem() + for i := 0; i < srcs.Len(); i++ { + dsts = reflect.Append(dsts, zero) + srcElement := srcs.Index(i).Addr() + dstElement := dsts.Index(dsts.Len() - 1).Addr() + mergeInfo.merge(valToPointer(dstElement), valToPointer(srcElement)) + } + if dsts.IsNil() { + dsts = reflect.MakeSlice(f.Type, 0, 0) + } + dstsp.Elem().Set(dsts) + } + case !isPointer: + mergeInfo := getMergeInfo(tf) + mfi.merge = func(dst, src pointer) { + mergeInfo.merge(dst, src) + } + case isSlice: // E.g., []*pb.T + mergeInfo := getMergeInfo(tf) + mfi.merge = func(dst, src pointer) { + sps := src.getPointerSlice() + if sps != nil { + dps := dst.getPointerSlice() + for _, sp := range sps { + var dp pointer + if !sp.isNil() { + dp = valToPointer(reflect.New(tf)) + mergeInfo.merge(dp, sp) + } + dps = append(dps, dp) + } + if dps == nil { + dps = []pointer{} + } + dst.setPointerSlice(dps) + } + } + default: // E.g., *pb.T + mergeInfo := getMergeInfo(tf) + mfi.merge = func(dst, src pointer) { + sp := src.getPointer() + if !sp.isNil() { + dp := dst.getPointer() + if dp.isNil() { + dp = valToPointer(reflect.New(tf)) + dst.setPointer(dp) + } + mergeInfo.merge(dp, sp) + } + } + } + case reflect.Map: + switch { + case isPointer || isSlice: + panic("bad pointer or slice in map case in " + tf.Name()) + default: // E.g., map[K]V + mfi.merge = func(dst, src pointer) { + sm := src.asPointerTo(tf).Elem() + if sm.Len() == 0 { + return + } + dm := dst.asPointerTo(tf).Elem() + if dm.IsNil() { + dm.Set(reflect.MakeMap(tf)) + } + + switch tf.Elem().Kind() { + case reflect.Ptr: // Proto struct (e.g., *T) + for _, key := range sm.MapKeys() { + val := sm.MapIndex(key) + val = reflect.ValueOf(Clone(val.Interface().(Message))) + dm.SetMapIndex(key, val) + } + case reflect.Slice: // E.g. Bytes type (e.g., []byte) + for _, key := range sm.MapKeys() { + val := sm.MapIndex(key) + val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) + dm.SetMapIndex(key, val) + } + default: // Basic type (e.g., string) + for _, key := range sm.MapKeys() { + val := sm.MapIndex(key) + dm.SetMapIndex(key, val) + } + } + } + } + case reflect.Interface: + // Must be oneof field. + switch { + case isPointer || isSlice: + panic("bad pointer or slice in interface case in " + tf.Name()) + default: // E.g., interface{} + // TODO: Make this faster? + mfi.merge = func(dst, src pointer) { + su := src.asPointerTo(tf).Elem() + if !su.IsNil() { + du := dst.asPointerTo(tf).Elem() + typ := su.Elem().Type() + if du.IsNil() || du.Elem().Type() != typ { + du.Set(reflect.New(typ.Elem())) // Initialize interface if empty + } + sv := su.Elem().Elem().Field(0) + if sv.Kind() == reflect.Ptr && sv.IsNil() { + return + } + dv := du.Elem().Elem().Field(0) + if dv.Kind() == reflect.Ptr && dv.IsNil() { + dv.Set(reflect.New(sv.Type().Elem())) // Initialize proto message if empty + } + switch sv.Type().Kind() { + case reflect.Ptr: // Proto struct (e.g., *T) + Merge(dv.Interface().(Message), sv.Interface().(Message)) + case reflect.Slice: // E.g. Bytes type (e.g., []byte) + dv.Set(reflect.ValueOf(append([]byte{}, sv.Bytes()...))) + default: // Basic type (e.g., string) + dv.Set(sv) + } + } + } + } + default: + panic(fmt.Sprintf("merger not found for type:%s", tf)) + } + mi.fields = append(mi.fields, mfi) + } + + mi.unrecognized = invalidField + if f, ok := t.FieldByName("XXX_unrecognized"); ok { + if f.Type != reflect.TypeOf([]byte{}) { + panic("expected XXX_unrecognized to be of type []byte") + } + mi.unrecognized = toField(&f) + } + + atomic.StoreInt32(&mi.initialized, 1) +} diff --git a/vendor/github.com/gogo/protobuf/proto/table_unmarshal.go b/vendor/github.com/gogo/protobuf/proto/table_unmarshal.go new file mode 100644 index 0000000000000..937229386a27c --- /dev/null +++ b/vendor/github.com/gogo/protobuf/proto/table_unmarshal.go @@ -0,0 +1,2249 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2016 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import ( + "errors" + "fmt" + "io" + "math" + "reflect" + "strconv" + "strings" + "sync" + "sync/atomic" + "unicode/utf8" +) + +// Unmarshal is the entry point from the generated .pb.go files. +// This function is not intended to be used by non-generated code. +// This function is not subject to any compatibility guarantee. +// msg contains a pointer to a protocol buffer struct. +// b is the data to be unmarshaled into the protocol buffer. +// a is a pointer to a place to store cached unmarshal information. +func (a *InternalMessageInfo) Unmarshal(msg Message, b []byte) error { + // Load the unmarshal information for this message type. + // The atomic load ensures memory consistency. + u := atomicLoadUnmarshalInfo(&a.unmarshal) + if u == nil { + // Slow path: find unmarshal info for msg, update a with it. + u = getUnmarshalInfo(reflect.TypeOf(msg).Elem()) + atomicStoreUnmarshalInfo(&a.unmarshal, u) + } + // Then do the unmarshaling. + err := u.unmarshal(toPointer(&msg), b) + return err +} + +type unmarshalInfo struct { + typ reflect.Type // type of the protobuf struct + + // 0 = only typ field is initialized + // 1 = completely initialized + initialized int32 + lock sync.Mutex // prevents double initialization + dense []unmarshalFieldInfo // fields indexed by tag # + sparse map[uint64]unmarshalFieldInfo // fields indexed by tag # + reqFields []string // names of required fields + reqMask uint64 // 1< 0 { + // Read tag and wire type. + // Special case 1 and 2 byte varints. + var x uint64 + if b[0] < 128 { + x = uint64(b[0]) + b = b[1:] + } else if len(b) >= 2 && b[1] < 128 { + x = uint64(b[0]&0x7f) + uint64(b[1])<<7 + b = b[2:] + } else { + var n int + x, n = decodeVarint(b) + if n == 0 { + return io.ErrUnexpectedEOF + } + b = b[n:] + } + tag := x >> 3 + wire := int(x) & 7 + + // Dispatch on the tag to one of the unmarshal* functions below. + var f unmarshalFieldInfo + if tag < uint64(len(u.dense)) { + f = u.dense[tag] + } else { + f = u.sparse[tag] + } + if fn := f.unmarshal; fn != nil { + var err error + b, err = fn(b, m.offset(f.field), wire) + if err == nil { + reqMask |= f.reqMask + continue + } + if r, ok := err.(*RequiredNotSetError); ok { + // Remember this error, but keep parsing. We need to produce + // a full parse even if a required field is missing. + if errLater == nil { + errLater = r + } + reqMask |= f.reqMask + continue + } + if err != errInternalBadWireType { + if err == errInvalidUTF8 { + if errLater == nil { + fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name + errLater = &invalidUTF8Error{fullName} + } + continue + } + return err + } + // Fragments with bad wire type are treated as unknown fields. + } + + // Unknown tag. + if !u.unrecognized.IsValid() { + // Don't keep unrecognized data; just skip it. + var err error + b, err = skipField(b, wire) + if err != nil { + return err + } + continue + } + // Keep unrecognized data around. + // maybe in extensions, maybe in the unrecognized field. + z := m.offset(u.unrecognized).toBytes() + var emap map[int32]Extension + var e Extension + for _, r := range u.extensionRanges { + if uint64(r.Start) <= tag && tag <= uint64(r.End) { + if u.extensions.IsValid() { + mp := m.offset(u.extensions).toExtensions() + emap = mp.extensionsWrite() + e = emap[int32(tag)] + z = &e.enc + break + } + if u.oldExtensions.IsValid() { + p := m.offset(u.oldExtensions).toOldExtensions() + emap = *p + if emap == nil { + emap = map[int32]Extension{} + *p = emap + } + e = emap[int32(tag)] + z = &e.enc + break + } + if u.bytesExtensions.IsValid() { + z = m.offset(u.bytesExtensions).toBytes() + break + } + panic("no extensions field available") + } + } + // Use wire type to skip data. + var err error + b0 := b + b, err = skipField(b, wire) + if err != nil { + return err + } + *z = encodeVarint(*z, tag<<3|uint64(wire)) + *z = append(*z, b0[:len(b0)-len(b)]...) + + if emap != nil { + emap[int32(tag)] = e + } + } + if reqMask != u.reqMask && errLater == nil { + // A required field of this message is missing. + for _, n := range u.reqFields { + if reqMask&1 == 0 { + errLater = &RequiredNotSetError{n} + } + reqMask >>= 1 + } + } + return errLater +} + +// computeUnmarshalInfo fills in u with information for use +// in unmarshaling protocol buffers of type u.typ. +func (u *unmarshalInfo) computeUnmarshalInfo() { + u.lock.Lock() + defer u.lock.Unlock() + if u.initialized != 0 { + return + } + t := u.typ + n := t.NumField() + + // Set up the "not found" value for the unrecognized byte buffer. + // This is the default for proto3. + u.unrecognized = invalidField + u.extensions = invalidField + u.oldExtensions = invalidField + u.bytesExtensions = invalidField + + // List of the generated type and offset for each oneof field. + type oneofField struct { + ityp reflect.Type // interface type of oneof field + field field // offset in containing message + } + var oneofFields []oneofField + + for i := 0; i < n; i++ { + f := t.Field(i) + if f.Name == "XXX_unrecognized" { + // The byte slice used to hold unrecognized input is special. + if f.Type != reflect.TypeOf(([]byte)(nil)) { + panic("bad type for XXX_unrecognized field: " + f.Type.Name()) + } + u.unrecognized = toField(&f) + continue + } + if f.Name == "XXX_InternalExtensions" { + // Ditto here. + if f.Type != reflect.TypeOf(XXX_InternalExtensions{}) { + panic("bad type for XXX_InternalExtensions field: " + f.Type.Name()) + } + u.extensions = toField(&f) + if f.Tag.Get("protobuf_messageset") == "1" { + u.isMessageSet = true + } + continue + } + if f.Name == "XXX_extensions" { + // An older form of the extensions field. + if f.Type == reflect.TypeOf((map[int32]Extension)(nil)) { + u.oldExtensions = toField(&f) + continue + } else if f.Type == reflect.TypeOf(([]byte)(nil)) { + u.bytesExtensions = toField(&f) + continue + } + panic("bad type for XXX_extensions field: " + f.Type.Name()) + } + if f.Name == "XXX_NoUnkeyedLiteral" || f.Name == "XXX_sizecache" { + continue + } + + oneof := f.Tag.Get("protobuf_oneof") + if oneof != "" { + oneofFields = append(oneofFields, oneofField{f.Type, toField(&f)}) + // The rest of oneof processing happens below. + continue + } + + tags := f.Tag.Get("protobuf") + tagArray := strings.Split(tags, ",") + if len(tagArray) < 2 { + panic("protobuf tag not enough fields in " + t.Name() + "." + f.Name + ": " + tags) + } + tag, err := strconv.Atoi(tagArray[1]) + if err != nil { + panic("protobuf tag field not an integer: " + tagArray[1]) + } + + name := "" + for _, tag := range tagArray[3:] { + if strings.HasPrefix(tag, "name=") { + name = tag[5:] + } + } + + // Extract unmarshaling function from the field (its type and tags). + unmarshal := fieldUnmarshaler(&f) + + // Required field? + var reqMask uint64 + if tagArray[2] == "req" { + bit := len(u.reqFields) + u.reqFields = append(u.reqFields, name) + reqMask = uint64(1) << uint(bit) + // TODO: if we have more than 64 required fields, we end up + // not verifying that all required fields are present. + // Fix this, perhaps using a count of required fields? + } + + // Store the info in the correct slot in the message. + u.setTag(tag, toField(&f), unmarshal, reqMask, name) + } + + // Find any types associated with oneof fields. + // gogo: len(oneofFields) > 0 is needed for embedded oneof messages, without a marshaler and unmarshaler + if len(oneofFields) > 0 { + var oneofImplementers []interface{} + switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) { + case oneofFuncsIface: + _, _, _, oneofImplementers = m.XXX_OneofFuncs() + case oneofWrappersIface: + oneofImplementers = m.XXX_OneofWrappers() + } + for _, v := range oneofImplementers { + tptr := reflect.TypeOf(v) // *Msg_X + typ := tptr.Elem() // Msg_X + + f := typ.Field(0) // oneof implementers have one field + baseUnmarshal := fieldUnmarshaler(&f) + tags := strings.Split(f.Tag.Get("protobuf"), ",") + fieldNum, err := strconv.Atoi(tags[1]) + if err != nil { + panic("protobuf tag field not an integer: " + tags[1]) + } + var name string + for _, tag := range tags { + if strings.HasPrefix(tag, "name=") { + name = strings.TrimPrefix(tag, "name=") + break + } + } + + // Find the oneof field that this struct implements. + // Might take O(n^2) to process all of the oneofs, but who cares. + for _, of := range oneofFields { + if tptr.Implements(of.ityp) { + // We have found the corresponding interface for this struct. + // That lets us know where this struct should be stored + // when we encounter it during unmarshaling. + unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal) + u.setTag(fieldNum, of.field, unmarshal, 0, name) + } + } + + } + } + + // Get extension ranges, if any. + fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray") + if fn.IsValid() { + if !u.extensions.IsValid() && !u.oldExtensions.IsValid() && !u.bytesExtensions.IsValid() { + panic("a message with extensions, but no extensions field in " + t.Name()) + } + u.extensionRanges = fn.Call(nil)[0].Interface().([]ExtensionRange) + } + + // Explicitly disallow tag 0. This will ensure we flag an error + // when decoding a buffer of all zeros. Without this code, we + // would decode and skip an all-zero buffer of even length. + // [0 0] is [tag=0/wiretype=varint varint-encoded-0]. + u.setTag(0, zeroField, func(b []byte, f pointer, w int) ([]byte, error) { + return nil, fmt.Errorf("proto: %s: illegal tag 0 (wire type %d)", t, w) + }, 0, "") + + // Set mask for required field check. + u.reqMask = uint64(1)<= 0 && (tag < 16 || tag < 2*n) { // TODO: what are the right numbers here? + for len(u.dense) <= tag { + u.dense = append(u.dense, unmarshalFieldInfo{}) + } + u.dense[tag] = i + return + } + if u.sparse == nil { + u.sparse = map[uint64]unmarshalFieldInfo{} + } + u.sparse[uint64(tag)] = i +} + +// fieldUnmarshaler returns an unmarshaler for the given field. +func fieldUnmarshaler(f *reflect.StructField) unmarshaler { + if f.Type.Kind() == reflect.Map { + return makeUnmarshalMap(f) + } + return typeUnmarshaler(f.Type, f.Tag.Get("protobuf")) +} + +// typeUnmarshaler returns an unmarshaler for the given field type / field tag pair. +func typeUnmarshaler(t reflect.Type, tags string) unmarshaler { + tagArray := strings.Split(tags, ",") + encoding := tagArray[0] + name := "unknown" + ctype := false + isTime := false + isDuration := false + isWktPointer := false + proto3 := false + validateUTF8 := true + for _, tag := range tagArray[3:] { + if strings.HasPrefix(tag, "name=") { + name = tag[5:] + } + if tag == "proto3" { + proto3 = true + } + if strings.HasPrefix(tag, "customtype=") { + ctype = true + } + if tag == "stdtime" { + isTime = true + } + if tag == "stdduration" { + isDuration = true + } + if tag == "wktptr" { + isWktPointer = true + } + } + validateUTF8 = validateUTF8 && proto3 + + // Figure out packaging (pointer, slice, or both) + slice := false + pointer := false + if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 { + slice = true + t = t.Elem() + } + if t.Kind() == reflect.Ptr { + pointer = true + t = t.Elem() + } + + if ctype { + if reflect.PtrTo(t).Implements(customType) { + if slice { + return makeUnmarshalCustomSlice(getUnmarshalInfo(t), name) + } + if pointer { + return makeUnmarshalCustomPtr(getUnmarshalInfo(t), name) + } + return makeUnmarshalCustom(getUnmarshalInfo(t), name) + } else { + panic(fmt.Sprintf("custom type: type: %v, does not implement the proto.custom interface", t)) + } + } + + if isTime { + if pointer { + if slice { + return makeUnmarshalTimePtrSlice(getUnmarshalInfo(t), name) + } + return makeUnmarshalTimePtr(getUnmarshalInfo(t), name) + } + if slice { + return makeUnmarshalTimeSlice(getUnmarshalInfo(t), name) + } + return makeUnmarshalTime(getUnmarshalInfo(t), name) + } + + if isDuration { + if pointer { + if slice { + return makeUnmarshalDurationPtrSlice(getUnmarshalInfo(t), name) + } + return makeUnmarshalDurationPtr(getUnmarshalInfo(t), name) + } + if slice { + return makeUnmarshalDurationSlice(getUnmarshalInfo(t), name) + } + return makeUnmarshalDuration(getUnmarshalInfo(t), name) + } + + if isWktPointer { + switch t.Kind() { + case reflect.Float64: + if pointer { + if slice { + return makeStdDoubleValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdDoubleValuePtrUnmarshaler(getUnmarshalInfo(t), name) + } + if slice { + return makeStdDoubleValueSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdDoubleValueUnmarshaler(getUnmarshalInfo(t), name) + case reflect.Float32: + if pointer { + if slice { + return makeStdFloatValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdFloatValuePtrUnmarshaler(getUnmarshalInfo(t), name) + } + if slice { + return makeStdFloatValueSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdFloatValueUnmarshaler(getUnmarshalInfo(t), name) + case reflect.Int64: + if pointer { + if slice { + return makeStdInt64ValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdInt64ValuePtrUnmarshaler(getUnmarshalInfo(t), name) + } + if slice { + return makeStdInt64ValueSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdInt64ValueUnmarshaler(getUnmarshalInfo(t), name) + case reflect.Uint64: + if pointer { + if slice { + return makeStdUInt64ValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdUInt64ValuePtrUnmarshaler(getUnmarshalInfo(t), name) + } + if slice { + return makeStdUInt64ValueSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdUInt64ValueUnmarshaler(getUnmarshalInfo(t), name) + case reflect.Int32: + if pointer { + if slice { + return makeStdInt32ValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdInt32ValuePtrUnmarshaler(getUnmarshalInfo(t), name) + } + if slice { + return makeStdInt32ValueSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdInt32ValueUnmarshaler(getUnmarshalInfo(t), name) + case reflect.Uint32: + if pointer { + if slice { + return makeStdUInt32ValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdUInt32ValuePtrUnmarshaler(getUnmarshalInfo(t), name) + } + if slice { + return makeStdUInt32ValueSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdUInt32ValueUnmarshaler(getUnmarshalInfo(t), name) + case reflect.Bool: + if pointer { + if slice { + return makeStdBoolValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdBoolValuePtrUnmarshaler(getUnmarshalInfo(t), name) + } + if slice { + return makeStdBoolValueSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdBoolValueUnmarshaler(getUnmarshalInfo(t), name) + case reflect.String: + if pointer { + if slice { + return makeStdStringValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdStringValuePtrUnmarshaler(getUnmarshalInfo(t), name) + } + if slice { + return makeStdStringValueSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdStringValueUnmarshaler(getUnmarshalInfo(t), name) + case uint8SliceType: + if pointer { + if slice { + return makeStdBytesValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdBytesValuePtrUnmarshaler(getUnmarshalInfo(t), name) + } + if slice { + return makeStdBytesValueSliceUnmarshaler(getUnmarshalInfo(t), name) + } + return makeStdBytesValueUnmarshaler(getUnmarshalInfo(t), name) + default: + panic(fmt.Sprintf("unknown wktpointer type %#v", t)) + } + } + + // We'll never have both pointer and slice for basic types. + if pointer && slice && t.Kind() != reflect.Struct { + panic("both pointer and slice for basic type in " + t.Name()) + } + + switch t.Kind() { + case reflect.Bool: + if pointer { + return unmarshalBoolPtr + } + if slice { + return unmarshalBoolSlice + } + return unmarshalBoolValue + case reflect.Int32: + switch encoding { + case "fixed32": + if pointer { + return unmarshalFixedS32Ptr + } + if slice { + return unmarshalFixedS32Slice + } + return unmarshalFixedS32Value + case "varint": + // this could be int32 or enum + if pointer { + return unmarshalInt32Ptr + } + if slice { + return unmarshalInt32Slice + } + return unmarshalInt32Value + case "zigzag32": + if pointer { + return unmarshalSint32Ptr + } + if slice { + return unmarshalSint32Slice + } + return unmarshalSint32Value + } + case reflect.Int64: + switch encoding { + case "fixed64": + if pointer { + return unmarshalFixedS64Ptr + } + if slice { + return unmarshalFixedS64Slice + } + return unmarshalFixedS64Value + case "varint": + if pointer { + return unmarshalInt64Ptr + } + if slice { + return unmarshalInt64Slice + } + return unmarshalInt64Value + case "zigzag64": + if pointer { + return unmarshalSint64Ptr + } + if slice { + return unmarshalSint64Slice + } + return unmarshalSint64Value + } + case reflect.Uint32: + switch encoding { + case "fixed32": + if pointer { + return unmarshalFixed32Ptr + } + if slice { + return unmarshalFixed32Slice + } + return unmarshalFixed32Value + case "varint": + if pointer { + return unmarshalUint32Ptr + } + if slice { + return unmarshalUint32Slice + } + return unmarshalUint32Value + } + case reflect.Uint64: + switch encoding { + case "fixed64": + if pointer { + return unmarshalFixed64Ptr + } + if slice { + return unmarshalFixed64Slice + } + return unmarshalFixed64Value + case "varint": + if pointer { + return unmarshalUint64Ptr + } + if slice { + return unmarshalUint64Slice + } + return unmarshalUint64Value + } + case reflect.Float32: + if pointer { + return unmarshalFloat32Ptr + } + if slice { + return unmarshalFloat32Slice + } + return unmarshalFloat32Value + case reflect.Float64: + if pointer { + return unmarshalFloat64Ptr + } + if slice { + return unmarshalFloat64Slice + } + return unmarshalFloat64Value + case reflect.Map: + panic("map type in typeUnmarshaler in " + t.Name()) + case reflect.Slice: + if pointer { + panic("bad pointer in slice case in " + t.Name()) + } + if slice { + return unmarshalBytesSlice + } + return unmarshalBytesValue + case reflect.String: + if validateUTF8 { + if pointer { + return unmarshalUTF8StringPtr + } + if slice { + return unmarshalUTF8StringSlice + } + return unmarshalUTF8StringValue + } + if pointer { + return unmarshalStringPtr + } + if slice { + return unmarshalStringSlice + } + return unmarshalStringValue + case reflect.Struct: + // message or group field + if !pointer { + switch encoding { + case "bytes": + if slice { + return makeUnmarshalMessageSlice(getUnmarshalInfo(t), name) + } + return makeUnmarshalMessage(getUnmarshalInfo(t), name) + } + } + switch encoding { + case "bytes": + if slice { + return makeUnmarshalMessageSlicePtr(getUnmarshalInfo(t), name) + } + return makeUnmarshalMessagePtr(getUnmarshalInfo(t), name) + case "group": + if slice { + return makeUnmarshalGroupSlicePtr(getUnmarshalInfo(t), name) + } + return makeUnmarshalGroupPtr(getUnmarshalInfo(t), name) + } + } + panic(fmt.Sprintf("unmarshaler not found type:%s encoding:%s", t, encoding)) +} + +// Below are all the unmarshalers for individual fields of various types. + +func unmarshalInt64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x) + *f.toInt64() = v + return b, nil +} + +func unmarshalInt64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x) + *f.toInt64Ptr() = &v + return b, nil +} + +func unmarshalInt64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x) + s := f.toInt64Slice() + *s = append(*s, v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x) + s := f.toInt64Slice() + *s = append(*s, v) + return b, nil +} + +func unmarshalSint64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x>>1) ^ int64(x)<<63>>63 + *f.toInt64() = v + return b, nil +} + +func unmarshalSint64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x>>1) ^ int64(x)<<63>>63 + *f.toInt64Ptr() = &v + return b, nil +} + +func unmarshalSint64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x>>1) ^ int64(x)<<63>>63 + s := f.toInt64Slice() + *s = append(*s, v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int64(x>>1) ^ int64(x)<<63>>63 + s := f.toInt64Slice() + *s = append(*s, v) + return b, nil +} + +func unmarshalUint64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint64(x) + *f.toUint64() = v + return b, nil +} + +func unmarshalUint64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint64(x) + *f.toUint64Ptr() = &v + return b, nil +} + +func unmarshalUint64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint64(x) + s := f.toUint64Slice() + *s = append(*s, v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint64(x) + s := f.toUint64Slice() + *s = append(*s, v) + return b, nil +} + +func unmarshalInt32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x) + *f.toInt32() = v + return b, nil +} + +func unmarshalInt32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x) + f.setInt32Ptr(v) + return b, nil +} + +func unmarshalInt32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x) + f.appendInt32Slice(v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x) + f.appendInt32Slice(v) + return b, nil +} + +func unmarshalSint32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x>>1) ^ int32(x)<<31>>31 + *f.toInt32() = v + return b, nil +} + +func unmarshalSint32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x>>1) ^ int32(x)<<31>>31 + f.setInt32Ptr(v) + return b, nil +} + +func unmarshalSint32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x>>1) ^ int32(x)<<31>>31 + f.appendInt32Slice(v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := int32(x>>1) ^ int32(x)<<31>>31 + f.appendInt32Slice(v) + return b, nil +} + +func unmarshalUint32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint32(x) + *f.toUint32() = v + return b, nil +} + +func unmarshalUint32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint32(x) + *f.toUint32Ptr() = &v + return b, nil +} + +func unmarshalUint32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint32(x) + s := f.toUint32Slice() + *s = append(*s, v) + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + v := uint32(x) + s := f.toUint32Slice() + *s = append(*s, v) + return b, nil +} + +func unmarshalFixed64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 + *f.toUint64() = v + return b[8:], nil +} + +func unmarshalFixed64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 + *f.toUint64Ptr() = &v + return b[8:], nil +} + +func unmarshalFixed64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 + s := f.toUint64Slice() + *s = append(*s, v) + b = b[8:] + } + return res, nil + } + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 + s := f.toUint64Slice() + *s = append(*s, v) + return b[8:], nil +} + +func unmarshalFixedS64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 + *f.toInt64() = v + return b[8:], nil +} + +func unmarshalFixedS64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 + *f.toInt64Ptr() = &v + return b[8:], nil +} + +func unmarshalFixedS64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 + s := f.toInt64Slice() + *s = append(*s, v) + b = b[8:] + } + return res, nil + } + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 + s := f.toInt64Slice() + *s = append(*s, v) + return b[8:], nil +} + +func unmarshalFixed32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + *f.toUint32() = v + return b[4:], nil +} + +func unmarshalFixed32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + *f.toUint32Ptr() = &v + return b[4:], nil +} + +func unmarshalFixed32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + s := f.toUint32Slice() + *s = append(*s, v) + b = b[4:] + } + return res, nil + } + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + s := f.toUint32Slice() + *s = append(*s, v) + return b[4:], nil +} + +func unmarshalFixedS32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 + *f.toInt32() = v + return b[4:], nil +} + +func unmarshalFixedS32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 + f.setInt32Ptr(v) + return b[4:], nil +} + +func unmarshalFixedS32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 + f.appendInt32Slice(v) + b = b[4:] + } + return res, nil + } + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 + f.appendInt32Slice(v) + return b[4:], nil +} + +func unmarshalBoolValue(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + // Note: any length varint is allowed, even though any sane + // encoder will use one byte. + // See https://github.com/golang/protobuf/issues/76 + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + // TODO: check if x>1? Tests seem to indicate no. + v := x != 0 + *f.toBool() = v + return b[n:], nil +} + +func unmarshalBoolPtr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + v := x != 0 + *f.toBoolPtr() = &v + return b[n:], nil +} + +func unmarshalBoolSlice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + x, n = decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + v := x != 0 + s := f.toBoolSlice() + *s = append(*s, v) + b = b[n:] + } + return res, nil + } + if w != WireVarint { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + v := x != 0 + s := f.toBoolSlice() + *s = append(*s, v) + return b[n:], nil +} + +func unmarshalFloat64Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) + *f.toFloat64() = v + return b[8:], nil +} + +func unmarshalFloat64Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) + *f.toFloat64Ptr() = &v + return b[8:], nil +} + +func unmarshalFloat64Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) + s := f.toFloat64Slice() + *s = append(*s, v) + b = b[8:] + } + return res, nil + } + if w != WireFixed64 { + return b, errInternalBadWireType + } + if len(b) < 8 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) + s := f.toFloat64Slice() + *s = append(*s, v) + return b[8:], nil +} + +func unmarshalFloat32Value(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) + *f.toFloat32() = v + return b[4:], nil +} + +func unmarshalFloat32Ptr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) + *f.toFloat32Ptr() = &v + return b[4:], nil +} + +func unmarshalFloat32Slice(b []byte, f pointer, w int) ([]byte, error) { + if w == WireBytes { // packed + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + res := b[x:] + b = b[:x] + for len(b) > 0 { + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) + s := f.toFloat32Slice() + *s = append(*s, v) + b = b[4:] + } + return res, nil + } + if w != WireFixed32 { + return b, errInternalBadWireType + } + if len(b) < 4 { + return nil, io.ErrUnexpectedEOF + } + v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) + s := f.toFloat32Slice() + *s = append(*s, v) + return b[4:], nil +} + +func unmarshalStringValue(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + *f.toString() = v + return b[x:], nil +} + +func unmarshalStringPtr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + *f.toStringPtr() = &v + return b[x:], nil +} + +func unmarshalStringSlice(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + s := f.toStringSlice() + *s = append(*s, v) + return b[x:], nil +} + +func unmarshalUTF8StringValue(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + *f.toString() = v + if !utf8.ValidString(v) { + return b[x:], errInvalidUTF8 + } + return b[x:], nil +} + +func unmarshalUTF8StringPtr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + *f.toStringPtr() = &v + if !utf8.ValidString(v) { + return b[x:], errInvalidUTF8 + } + return b[x:], nil +} + +func unmarshalUTF8StringSlice(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + s := f.toStringSlice() + *s = append(*s, v) + if !utf8.ValidString(v) { + return b[x:], errInvalidUTF8 + } + return b[x:], nil +} + +var emptyBuf [0]byte + +func unmarshalBytesValue(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + // The use of append here is a trick which avoids the zeroing + // that would be required if we used a make/copy pair. + // We append to emptyBuf instead of nil because we want + // a non-nil result even when the length is 0. + v := append(emptyBuf[:], b[:x]...) + *f.toBytes() = v + return b[x:], nil +} + +func unmarshalBytesSlice(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := append(emptyBuf[:], b[:x]...) + s := f.toBytesSlice() + *s = append(*s, v) + return b[x:], nil +} + +func makeUnmarshalMessagePtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + // First read the message field to see if something is there. + // The semantics of multiple submessages are weird. Instead of + // the last one winning (as it is for all other fields), multiple + // submessages are merged. + v := f.getPointer() + if v.isNil() { + v = valToPointer(reflect.New(sub.typ)) + f.setPointer(v) + } + err := sub.unmarshal(v, b[:x]) + if err != nil { + if r, ok := err.(*RequiredNotSetError); ok { + r.field = name + "." + r.field + } else { + return nil, err + } + } + return b[x:], err + } +} + +func makeUnmarshalMessageSlicePtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := valToPointer(reflect.New(sub.typ)) + err := sub.unmarshal(v, b[:x]) + if err != nil { + if r, ok := err.(*RequiredNotSetError); ok { + r.field = name + "." + r.field + } else { + return nil, err + } + } + f.appendPointer(v) + return b[x:], err + } +} + +func makeUnmarshalGroupPtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireStartGroup { + return b, errInternalBadWireType + } + x, y := findEndGroup(b) + if x < 0 { + return nil, io.ErrUnexpectedEOF + } + v := f.getPointer() + if v.isNil() { + v = valToPointer(reflect.New(sub.typ)) + f.setPointer(v) + } + err := sub.unmarshal(v, b[:x]) + if err != nil { + if r, ok := err.(*RequiredNotSetError); ok { + r.field = name + "." + r.field + } else { + return nil, err + } + } + return b[y:], err + } +} + +func makeUnmarshalGroupSlicePtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireStartGroup { + return b, errInternalBadWireType + } + x, y := findEndGroup(b) + if x < 0 { + return nil, io.ErrUnexpectedEOF + } + v := valToPointer(reflect.New(sub.typ)) + err := sub.unmarshal(v, b[:x]) + if err != nil { + if r, ok := err.(*RequiredNotSetError); ok { + r.field = name + "." + r.field + } else { + return nil, err + } + } + f.appendPointer(v) + return b[y:], err + } +} + +func makeUnmarshalMap(f *reflect.StructField) unmarshaler { + t := f.Type + kt := t.Key() + vt := t.Elem() + tagArray := strings.Split(f.Tag.Get("protobuf"), ",") + valTags := strings.Split(f.Tag.Get("protobuf_val"), ",") + for _, t := range tagArray { + if strings.HasPrefix(t, "customtype=") { + valTags = append(valTags, t) + } + if t == "stdtime" { + valTags = append(valTags, t) + } + if t == "stdduration" { + valTags = append(valTags, t) + } + if t == "wktptr" { + valTags = append(valTags, t) + } + } + unmarshalKey := typeUnmarshaler(kt, f.Tag.Get("protobuf_key")) + unmarshalVal := typeUnmarshaler(vt, strings.Join(valTags, ",")) + return func(b []byte, f pointer, w int) ([]byte, error) { + // The map entry is a submessage. Figure out how big it is. + if w != WireBytes { + return nil, fmt.Errorf("proto: bad wiretype for map field: got %d want %d", w, WireBytes) + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + r := b[x:] // unused data to return + b = b[:x] // data for map entry + + // Note: we could use #keys * #values ~= 200 functions + // to do map decoding without reflection. Probably not worth it. + // Maps will be somewhat slow. Oh well. + + // Read key and value from data. + var nerr nonFatal + k := reflect.New(kt) + v := reflect.New(vt) + for len(b) > 0 { + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + wire := int(x) & 7 + b = b[n:] + + var err error + switch x >> 3 { + case 1: + b, err = unmarshalKey(b, valToPointer(k), wire) + case 2: + b, err = unmarshalVal(b, valToPointer(v), wire) + default: + err = errInternalBadWireType // skip unknown tag + } + + if nerr.Merge(err) { + continue + } + if err != errInternalBadWireType { + return nil, err + } + + // Skip past unknown fields. + b, err = skipField(b, wire) + if err != nil { + return nil, err + } + } + + // Get map, allocate if needed. + m := f.asPointerTo(t).Elem() // an addressable map[K]T + if m.IsNil() { + m.Set(reflect.MakeMap(t)) + } + + // Insert into map. + m.SetMapIndex(k.Elem(), v.Elem()) + + return r, nerr.E + } +} + +// makeUnmarshalOneof makes an unmarshaler for oneof fields. +// for: +// message Msg { +// oneof F { +// int64 X = 1; +// float64 Y = 2; +// } +// } +// typ is the type of the concrete entry for a oneof case (e.g. Msg_X). +// ityp is the interface type of the oneof field (e.g. isMsg_F). +// unmarshal is the unmarshaler for the base type of the oneof case (e.g. int64). +// Note that this function will be called once for each case in the oneof. +func makeUnmarshalOneof(typ, ityp reflect.Type, unmarshal unmarshaler) unmarshaler { + sf := typ.Field(0) + field0 := toField(&sf) + return func(b []byte, f pointer, w int) ([]byte, error) { + // Allocate holder for value. + v := reflect.New(typ) + + // Unmarshal data into holder. + // We unmarshal into the first field of the holder object. + var err error + var nerr nonFatal + b, err = unmarshal(b, valToPointer(v).offset(field0), w) + if !nerr.Merge(err) { + return nil, err + } + + // Write pointer to holder into target field. + f.asPointerTo(ityp).Elem().Set(v) + + return b, nerr.E + } +} + +// Error used by decode internally. +var errInternalBadWireType = errors.New("proto: internal error: bad wiretype") + +// skipField skips past a field of type wire and returns the remaining bytes. +func skipField(b []byte, wire int) ([]byte, error) { + switch wire { + case WireVarint: + _, k := decodeVarint(b) + if k == 0 { + return b, io.ErrUnexpectedEOF + } + b = b[k:] + case WireFixed32: + if len(b) < 4 { + return b, io.ErrUnexpectedEOF + } + b = b[4:] + case WireFixed64: + if len(b) < 8 { + return b, io.ErrUnexpectedEOF + } + b = b[8:] + case WireBytes: + m, k := decodeVarint(b) + if k == 0 || uint64(len(b)-k) < m { + return b, io.ErrUnexpectedEOF + } + b = b[uint64(k)+m:] + case WireStartGroup: + _, i := findEndGroup(b) + if i == -1 { + return b, io.ErrUnexpectedEOF + } + b = b[i:] + default: + return b, fmt.Errorf("proto: can't skip unknown wire type %d", wire) + } + return b, nil +} + +// findEndGroup finds the index of the next EndGroup tag. +// Groups may be nested, so the "next" EndGroup tag is the first +// unpaired EndGroup. +// findEndGroup returns the indexes of the start and end of the EndGroup tag. +// Returns (-1,-1) if it can't find one. +func findEndGroup(b []byte) (int, int) { + depth := 1 + i := 0 + for { + x, n := decodeVarint(b[i:]) + if n == 0 { + return -1, -1 + } + j := i + i += n + switch x & 7 { + case WireVarint: + _, k := decodeVarint(b[i:]) + if k == 0 { + return -1, -1 + } + i += k + case WireFixed32: + if len(b)-4 < i { + return -1, -1 + } + i += 4 + case WireFixed64: + if len(b)-8 < i { + return -1, -1 + } + i += 8 + case WireBytes: + m, k := decodeVarint(b[i:]) + if k == 0 { + return -1, -1 + } + i += k + if uint64(len(b)-i) < m { + return -1, -1 + } + i += int(m) + case WireStartGroup: + depth++ + case WireEndGroup: + depth-- + if depth == 0 { + return j, i + } + default: + return -1, -1 + } + } +} + +// encodeVarint appends a varint-encoded integer to b and returns the result. +func encodeVarint(b []byte, x uint64) []byte { + for x >= 1<<7 { + b = append(b, byte(x&0x7f|0x80)) + x >>= 7 + } + return append(b, byte(x)) +} + +// decodeVarint reads a varint-encoded integer from b. +// Returns the decoded integer and the number of bytes read. +// If there is an error, it returns 0,0. +func decodeVarint(b []byte) (uint64, int) { + var x, y uint64 + if len(b) == 0 { + goto bad + } + x = uint64(b[0]) + if x < 0x80 { + return x, 1 + } + x -= 0x80 + + if len(b) <= 1 { + goto bad + } + y = uint64(b[1]) + x += y << 7 + if y < 0x80 { + return x, 2 + } + x -= 0x80 << 7 + + if len(b) <= 2 { + goto bad + } + y = uint64(b[2]) + x += y << 14 + if y < 0x80 { + return x, 3 + } + x -= 0x80 << 14 + + if len(b) <= 3 { + goto bad + } + y = uint64(b[3]) + x += y << 21 + if y < 0x80 { + return x, 4 + } + x -= 0x80 << 21 + + if len(b) <= 4 { + goto bad + } + y = uint64(b[4]) + x += y << 28 + if y < 0x80 { + return x, 5 + } + x -= 0x80 << 28 + + if len(b) <= 5 { + goto bad + } + y = uint64(b[5]) + x += y << 35 + if y < 0x80 { + return x, 6 + } + x -= 0x80 << 35 + + if len(b) <= 6 { + goto bad + } + y = uint64(b[6]) + x += y << 42 + if y < 0x80 { + return x, 7 + } + x -= 0x80 << 42 + + if len(b) <= 7 { + goto bad + } + y = uint64(b[7]) + x += y << 49 + if y < 0x80 { + return x, 8 + } + x -= 0x80 << 49 + + if len(b) <= 8 { + goto bad + } + y = uint64(b[8]) + x += y << 56 + if y < 0x80 { + return x, 9 + } + x -= 0x80 << 56 + + if len(b) <= 9 { + goto bad + } + y = uint64(b[9]) + x += y << 63 + if y < 2 { + return x, 10 + } + +bad: + return 0, 0 +} diff --git a/vendor/github.com/gogo/protobuf/proto/table_unmarshal_gogo.go b/vendor/github.com/gogo/protobuf/proto/table_unmarshal_gogo.go new file mode 100644 index 0000000000000..00d6c7ad9376a --- /dev/null +++ b/vendor/github.com/gogo/protobuf/proto/table_unmarshal_gogo.go @@ -0,0 +1,385 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2018, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import ( + "io" + "reflect" +) + +func makeUnmarshalMessage(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + // First read the message field to see if something is there. + // The semantics of multiple submessages are weird. Instead of + // the last one winning (as it is for all other fields), multiple + // submessages are merged. + v := f // gogo: changed from v := f.getPointer() + if v.isNil() { + v = valToPointer(reflect.New(sub.typ)) + f.setPointer(v) + } + err := sub.unmarshal(v, b[:x]) + if err != nil { + if r, ok := err.(*RequiredNotSetError); ok { + r.field = name + "." + r.field + } else { + return nil, err + } + } + return b[x:], err + } +} + +func makeUnmarshalMessageSlice(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := valToPointer(reflect.New(sub.typ)) + err := sub.unmarshal(v, b[:x]) + if err != nil { + if r, ok := err.(*RequiredNotSetError); ok { + r.field = name + "." + r.field + } else { + return nil, err + } + } + f.appendRef(v, sub.typ) // gogo: changed from f.appendPointer(v) + return b[x:], err + } +} + +func makeUnmarshalCustomPtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.New(sub.typ)) + m := s.Interface().(custom) + if err := m.Unmarshal(b[:x]); err != nil { + return nil, err + } + return b[x:], nil + } +} + +func makeUnmarshalCustomSlice(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := reflect.New(sub.typ) + c := m.Interface().(custom) + if err := c.Unmarshal(b[:x]); err != nil { + return nil, err + } + v := valToPointer(m) + f.appendRef(v, sub.typ) + return b[x:], nil + } +} + +func makeUnmarshalCustom(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + + m := f.asPointerTo(sub.typ).Interface().(custom) + if err := m.Unmarshal(b[:x]); err != nil { + return nil, err + } + return b[x:], nil + } +} + +func makeUnmarshalTime(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := ×tamp{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + t, err := timestampFromProto(m) + if err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(t)) + return b[x:], nil + } +} + +func makeUnmarshalTimePtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := ×tamp{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + t, err := timestampFromProto(m) + if err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&t)) + return b[x:], nil + } +} + +func makeUnmarshalTimePtrSlice(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := ×tamp{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + t, err := timestampFromProto(m) + if err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&t)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeUnmarshalTimeSlice(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := ×tamp{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + t, err := timestampFromProto(m) + if err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(t)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeUnmarshalDurationPtr(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &duration{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + d, err := durationFromProto(m) + if err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&d)) + return b[x:], nil + } +} + +func makeUnmarshalDuration(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &duration{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + d, err := durationFromProto(m) + if err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(d)) + return b[x:], nil + } +} + +func makeUnmarshalDurationPtrSlice(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &duration{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + d, err := durationFromProto(m) + if err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&d)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeUnmarshalDurationSlice(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &duration{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + d, err := durationFromProto(m) + if err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(d)) + slice.Set(newSlice) + return b[x:], nil + } +} diff --git a/vendor/github.com/gogo/protobuf/proto/text.go b/vendor/github.com/gogo/protobuf/proto/text.go index f609d1d453b04..87416afe95507 100644 --- a/vendor/github.com/gogo/protobuf/proto/text.go +++ b/vendor/github.com/gogo/protobuf/proto/text.go @@ -57,7 +57,6 @@ import ( var ( newline = []byte("\n") spaces = []byte(" ") - gtNewline = []byte(">\n") endBraceNewline = []byte("}\n") backslashN = []byte{'\\', 'n'} backslashR = []byte{'\\', 'r'} @@ -177,11 +176,6 @@ func writeName(w *textWriter, props *Properties) error { return nil } -// raw is the interface satisfied by RawMessage. -type raw interface { - Bytes() []byte -} - func requiresQuotes(u string) bool { // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. for _, ch := range u { @@ -276,6 +270,10 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { props := sprops.Prop[i] name := st.Field(i).Name + if name == "XXX_NoUnkeyedLiteral" { + continue + } + if strings.HasPrefix(name, "XXX_") { // There are two XXX_ fields: // XXX_unrecognized []byte @@ -366,7 +364,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { return err } } - if err := tm.writeAny(w, key, props.mkeyprop); err != nil { + if err := tm.writeAny(w, key, props.MapKeyProp); err != nil { return err } if err := w.WriteByte('\n'); err != nil { @@ -383,7 +381,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { return err } } - if err := tm.writeAny(w, val, props.mvalprop); err != nil { + if err := tm.writeAny(w, val, props.MapValProp); err != nil { return err } if err := w.WriteByte('\n'); err != nil { @@ -447,12 +445,6 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { return err } } - if b, ok := fv.Interface().(raw); ok { - if err := writeRaw(w, b.Bytes()); err != nil { - return err - } - continue - } if len(props.Enum) > 0 { if err := tm.writeEnum(w, fv, props); err != nil { @@ -475,7 +467,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { pv = reflect.New(sv.Type()) pv.Elem().Set(sv) } - if pv.Type().Implements(extensionRangeType) { + if _, err := extendable(pv.Interface()); err == nil { if err := tm.writeExtensions(w, pv); err != nil { return err } @@ -484,26 +476,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { return nil } -// writeRaw writes an uninterpreted raw message. -func writeRaw(w *textWriter, b []byte) error { - if err := w.WriteByte('<'); err != nil { - return err - } - if !w.compact { - if err := w.WriteByte('\n'); err != nil { - return err - } - } - w.indent() - if err := writeUnknownStruct(w, b); err != nil { - return err - } - w.unindent() - if err := w.WriteByte('>'); err != nil { - return err - } - return nil -} +var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() // writeAny writes an arbitrary field. func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error { @@ -605,16 +578,34 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert } } w.indent() - if etm, ok := v.Interface().(encoding.TextMarshaler); ok { - text, err := etm.MarshalText() + if v.CanAddr() { + // Calling v.Interface on a struct causes the reflect package to + // copy the entire struct. This is racy with the new Marshaler + // since we atomically update the XXX_sizecache. + // + // Thus, we retrieve a pointer to the struct if possible to avoid + // a race since v.Interface on the pointer doesn't copy the struct. + // + // If v is not addressable, then we are not worried about a race + // since it implies that the binary Marshaler cannot possibly be + // mutating this value. + v = v.Addr() + } + if v.Type().Implements(textMarshalerType) { + text, err := v.Interface().(encoding.TextMarshaler).MarshalText() if err != nil { return err } if _, err = w.Write(text); err != nil { return err } - } else if err := tm.writeStruct(w, v); err != nil { - return err + } else { + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if err := tm.writeStruct(w, v); err != nil { + return err + } } w.unindent() if err := w.WriteByte(ket); err != nil { diff --git a/vendor/github.com/gogo/protobuf/proto/text_parser.go b/vendor/github.com/gogo/protobuf/proto/text_parser.go index f1276729a3543..f85c0cc81a768 100644 --- a/vendor/github.com/gogo/protobuf/proto/text_parser.go +++ b/vendor/github.com/gogo/protobuf/proto/text_parser.go @@ -212,7 +212,6 @@ func (p *textParser) advance() { var ( errBadUTF8 = errors.New("proto: bad UTF-8") - errBadHex = errors.New("proto: bad hexadecimal") ) func unquoteC(s string, quote rune) (string, error) { @@ -283,60 +282,47 @@ func unescape(s string) (ch string, tail string, err error) { return "?", s, nil // trigraph workaround case '\'', '"', '\\': return string(r), s, nil - case '0', '1', '2', '3', '4', '5', '6', '7', 'x', 'X': + case '0', '1', '2', '3', '4', '5', '6', '7': if len(s) < 2 { return "", "", fmt.Errorf(`\%c requires 2 following digits`, r) } - base := 8 - ss := s[:2] + ss := string(r) + s[:2] s = s[2:] - if r == 'x' || r == 'X' { - base = 16 - } else { - ss = string(r) + ss - } - i, err := strconv.ParseUint(ss, base, 8) + i, err := strconv.ParseUint(ss, 8, 8) if err != nil { - return "", "", err + return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss) } return string([]byte{byte(i)}), s, nil - case 'u', 'U': - n := 4 - if r == 'U' { + case 'x', 'X', 'u', 'U': + var n int + switch r { + case 'x', 'X': + n = 2 + case 'u': + n = 4 + case 'U': n = 8 } if len(s) < n { - return "", "", fmt.Errorf(`\%c requires %d digits`, r, n) - } - - bs := make([]byte, n/2) - for i := 0; i < n; i += 2 { - a, ok1 := unhex(s[i]) - b, ok2 := unhex(s[i+1]) - if !ok1 || !ok2 { - return "", "", errBadHex - } - bs[i/2] = a<<4 | b + return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n) } + ss := s[:n] s = s[n:] - return string(bs), s, nil + i, err := strconv.ParseUint(ss, 16, 64) + if err != nil { + return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss) + } + if r == 'x' || r == 'X' { + return string([]byte{byte(i)}), s, nil + } + if i > utf8.MaxRune { + return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss) + } + return string(rune(i)), s, nil } return "", "", fmt.Errorf(`unknown escape \%c`, r) } -// Adapted from src/pkg/strconv/quote.go. -func unhex(b byte) (v byte, ok bool) { - switch { - case '0' <= b && b <= '9': - return b - '0', true - case 'a' <= b && b <= 'f': - return b - 'a' + 10, true - case 'A' <= b && b <= 'F': - return b - 'A' + 10, true - } - return 0, false -} - // Back off the parser by one token. Can only be done between calls to next(). // It makes the next advance() a no-op. func (p *textParser) back() { p.backed = true } @@ -650,17 +636,17 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error { if err := p.consumeToken(":"); err != nil { return err } - if err := p.readAny(key, props.mkeyprop); err != nil { + if err := p.readAny(key, props.MapKeyProp); err != nil { return err } if err := p.consumeOptionalSeparator(); err != nil { return err } case "value": - if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil { + if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil { return err } - if err := p.readAny(val, props.mvalprop); err != nil { + if err := p.readAny(val, props.MapValProp); err != nil { return err } if err := p.consumeOptionalSeparator(); err != nil { @@ -734,6 +720,9 @@ func (p *textParser) consumeExtName() (string, error) { if tok.err != nil { return "", p.errorf("unrecognized type_url or extension name: %s", tok.err) } + if p.done && tok.value != "]" { + return "", p.errorf("unclosed type_url or extension name") + } } return strings.Join(parts, ""), nil } @@ -934,6 +923,16 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error { fv.SetFloat(f) return nil } + case reflect.Int8: + if x, err := strconv.ParseInt(tok.value, 0, 8); err == nil { + fv.SetInt(x) + return nil + } + case reflect.Int16: + if x, err := strconv.ParseInt(tok.value, 0, 16); err == nil { + fv.SetInt(x) + return nil + } case reflect.Int32: if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { fv.SetInt(x) @@ -981,9 +980,19 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error { } // TODO: Handle nested messages which implement encoding.TextUnmarshaler. return p.readStruct(fv, terminator) + case reflect.Uint8: + if x, err := strconv.ParseUint(tok.value, 0, 8); err == nil { + fv.SetUint(x) + return nil + } + case reflect.Uint16: + if x, err := strconv.ParseUint(tok.value, 0, 16); err == nil { + fv.SetUint(x) + return nil + } case reflect.Uint32: if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { - fv.SetUint(x) + fv.SetUint(uint64(x)) return nil } case reflect.Uint64: @@ -1001,13 +1010,9 @@ func (p *textParser) readAny(v reflect.Value, props *Properties) error { // UnmarshalText returns *RequiredNotSetError. func UnmarshalText(s string, pb Message) error { if um, ok := pb.(encoding.TextUnmarshaler); ok { - err := um.UnmarshalText([]byte(s)) - return err + return um.UnmarshalText([]byte(s)) } pb.Reset() v := reflect.ValueOf(pb) - if pe := newTextParser(s).readStruct(v.Elem(), ""); pe != nil { - return pe - } - return nil + return newTextParser(s).readStruct(v.Elem(), "") } diff --git a/vendor/github.com/gogo/protobuf/proto/timestamp_gogo.go b/vendor/github.com/gogo/protobuf/proto/timestamp_gogo.go index d427647436066..38439fa990132 100644 --- a/vendor/github.com/gogo/protobuf/proto/timestamp_gogo.go +++ b/vendor/github.com/gogo/protobuf/proto/timestamp_gogo.go @@ -47,183 +47,3 @@ func (*timestamp) String() string { return "timestamp" } func init() { RegisterType((*timestamp)(nil), "gogo.protobuf.proto.timestamp") } - -func (o *Buffer) decTimestamp() (time.Time, error) { - b, err := o.DecodeRawBytes(true) - if err != nil { - return time.Time{}, err - } - tproto := ×tamp{} - if err := Unmarshal(b, tproto); err != nil { - return time.Time{}, err - } - return timestampFromProto(tproto) -} - -func (o *Buffer) dec_time(p *Properties, base structPointer) error { - t, err := o.decTimestamp() - if err != nil { - return err - } - setPtrCustomType(base, p.field, &t) - return nil -} - -func (o *Buffer) dec_ref_time(p *Properties, base structPointer) error { - t, err := o.decTimestamp() - if err != nil { - return err - } - setCustomType(base, p.field, &t) - return nil -} - -func (o *Buffer) dec_slice_time(p *Properties, base structPointer) error { - t, err := o.decTimestamp() - if err != nil { - return err - } - newBas := appendStructPointer(base, p.field, reflect.SliceOf(reflect.PtrTo(timeType))) - var zero field - setPtrCustomType(newBas, zero, &t) - return nil -} - -func (o *Buffer) dec_slice_ref_time(p *Properties, base structPointer) error { - t, err := o.decTimestamp() - if err != nil { - return err - } - newBas := appendStructPointer(base, p.field, reflect.SliceOf(timeType)) - var zero field - setCustomType(newBas, zero, &t) - return nil -} - -func size_time(p *Properties, base structPointer) (n int) { - structp := structPointer_GetStructPointer(base, p.field) - if structPointer_IsNil(structp) { - return 0 - } - tim := structPointer_Interface(structp, timeType).(*time.Time) - t, err := timestampProto(*tim) - if err != nil { - return 0 - } - size := Size(t) - return size + sizeVarint(uint64(size)) + len(p.tagcode) -} - -func (o *Buffer) enc_time(p *Properties, base structPointer) error { - structp := structPointer_GetStructPointer(base, p.field) - if structPointer_IsNil(structp) { - return ErrNil - } - tim := structPointer_Interface(structp, timeType).(*time.Time) - t, err := timestampProto(*tim) - if err != nil { - return err - } - data, err := Marshal(t) - if err != nil { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - return nil -} - -func size_ref_time(p *Properties, base structPointer) (n int) { - tim := structPointer_InterfaceAt(base, p.field, timeType).(*time.Time) - t, err := timestampProto(*tim) - if err != nil { - return 0 - } - size := Size(t) - return size + sizeVarint(uint64(size)) + len(p.tagcode) -} - -func (o *Buffer) enc_ref_time(p *Properties, base structPointer) error { - tim := structPointer_InterfaceAt(base, p.field, timeType).(*time.Time) - t, err := timestampProto(*tim) - if err != nil { - return err - } - data, err := Marshal(t) - if err != nil { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - return nil -} - -func size_slice_time(p *Properties, base structPointer) (n int) { - ptims := structPointer_InterfaceAt(base, p.field, reflect.SliceOf(reflect.PtrTo(timeType))).(*[]*time.Time) - tims := *ptims - for i := 0; i < len(tims); i++ { - if tims[i] == nil { - return 0 - } - tproto, err := timestampProto(*tims[i]) - if err != nil { - return 0 - } - size := Size(tproto) - n += len(p.tagcode) + size + sizeVarint(uint64(size)) - } - return n -} - -func (o *Buffer) enc_slice_time(p *Properties, base structPointer) error { - ptims := structPointer_InterfaceAt(base, p.field, reflect.SliceOf(reflect.PtrTo(timeType))).(*[]*time.Time) - tims := *ptims - for i := 0; i < len(tims); i++ { - if tims[i] == nil { - return errRepeatedHasNil - } - tproto, err := timestampProto(*tims[i]) - if err != nil { - return err - } - data, err := Marshal(tproto) - if err != nil { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - } - return nil -} - -func size_slice_ref_time(p *Properties, base structPointer) (n int) { - ptims := structPointer_InterfaceAt(base, p.field, reflect.SliceOf(timeType)).(*[]time.Time) - tims := *ptims - for i := 0; i < len(tims); i++ { - tproto, err := timestampProto(tims[i]) - if err != nil { - return 0 - } - size := Size(tproto) - n += len(p.tagcode) + size + sizeVarint(uint64(size)) - } - return n -} - -func (o *Buffer) enc_slice_ref_time(p *Properties, base structPointer) error { - ptims := structPointer_InterfaceAt(base, p.field, reflect.SliceOf(timeType)).(*[]time.Time) - tims := *ptims - for i := 0; i < len(tims); i++ { - tproto, err := timestampProto(tims[i]) - if err != nil { - return err - } - data, err := Marshal(tproto) - if err != nil { - return err - } - o.buf = append(o.buf, p.tagcode...) - o.EncodeRawBytes(data) - } - return nil -} diff --git a/vendor/github.com/gogo/protobuf/proto/wrappers.go b/vendor/github.com/gogo/protobuf/proto/wrappers.go new file mode 100644 index 0000000000000..b175d1b642346 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/proto/wrappers.go @@ -0,0 +1,1888 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2018, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import ( + "io" + "reflect" +) + +func makeStdDoubleValueMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + t := ptr.asPointerTo(u.typ).Interface().(*float64) + v := &float64Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + t := ptr.asPointerTo(u.typ).Interface().(*float64) + v := &float64Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdDoubleValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*float64) + v := &float64Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*float64) + v := &float64Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdDoubleValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(float64) + v := &float64Value{t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(float64) + v := &float64Value{t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdDoubleValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*float64) + v := &float64Value{*t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*float64) + v := &float64Value{*t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdDoubleValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &float64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(m.Value)) + return b[x:], nil + } +} + +func makeStdDoubleValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &float64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&m.Value)) + return b[x:], nil + } +} + +func makeStdDoubleValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &float64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdDoubleValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &float64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdFloatValueMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + t := ptr.asPointerTo(u.typ).Interface().(*float32) + v := &float32Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + t := ptr.asPointerTo(u.typ).Interface().(*float32) + v := &float32Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdFloatValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*float32) + v := &float32Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*float32) + v := &float32Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdFloatValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(float32) + v := &float32Value{t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(float32) + v := &float32Value{t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdFloatValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*float32) + v := &float32Value{*t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*float32) + v := &float32Value{*t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdFloatValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &float32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(m.Value)) + return b[x:], nil + } +} + +func makeStdFloatValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &float32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&m.Value)) + return b[x:], nil + } +} + +func makeStdFloatValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &float32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdFloatValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &float32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdInt64ValueMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + t := ptr.asPointerTo(u.typ).Interface().(*int64) + v := &int64Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + t := ptr.asPointerTo(u.typ).Interface().(*int64) + v := &int64Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdInt64ValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*int64) + v := &int64Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*int64) + v := &int64Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdInt64ValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(int64) + v := &int64Value{t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(int64) + v := &int64Value{t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdInt64ValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*int64) + v := &int64Value{*t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*int64) + v := &int64Value{*t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdInt64ValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &int64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(m.Value)) + return b[x:], nil + } +} + +func makeStdInt64ValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &int64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&m.Value)) + return b[x:], nil + } +} + +func makeStdInt64ValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &int64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdInt64ValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &int64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdUInt64ValueMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + t := ptr.asPointerTo(u.typ).Interface().(*uint64) + v := &uint64Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + t := ptr.asPointerTo(u.typ).Interface().(*uint64) + v := &uint64Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdUInt64ValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*uint64) + v := &uint64Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*uint64) + v := &uint64Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdUInt64ValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(uint64) + v := &uint64Value{t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(uint64) + v := &uint64Value{t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdUInt64ValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*uint64) + v := &uint64Value{*t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*uint64) + v := &uint64Value{*t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdUInt64ValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &uint64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(m.Value)) + return b[x:], nil + } +} + +func makeStdUInt64ValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &uint64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&m.Value)) + return b[x:], nil + } +} + +func makeStdUInt64ValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &uint64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdUInt64ValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &uint64Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdInt32ValueMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + t := ptr.asPointerTo(u.typ).Interface().(*int32) + v := &int32Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + t := ptr.asPointerTo(u.typ).Interface().(*int32) + v := &int32Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdInt32ValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*int32) + v := &int32Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*int32) + v := &int32Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdInt32ValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(int32) + v := &int32Value{t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(int32) + v := &int32Value{t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdInt32ValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*int32) + v := &int32Value{*t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*int32) + v := &int32Value{*t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdInt32ValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &int32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(m.Value)) + return b[x:], nil + } +} + +func makeStdInt32ValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &int32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&m.Value)) + return b[x:], nil + } +} + +func makeStdInt32ValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &int32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdInt32ValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &int32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdUInt32ValueMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + t := ptr.asPointerTo(u.typ).Interface().(*uint32) + v := &uint32Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + t := ptr.asPointerTo(u.typ).Interface().(*uint32) + v := &uint32Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdUInt32ValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*uint32) + v := &uint32Value{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*uint32) + v := &uint32Value{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdUInt32ValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(uint32) + v := &uint32Value{t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(uint32) + v := &uint32Value{t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdUInt32ValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*uint32) + v := &uint32Value{*t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*uint32) + v := &uint32Value{*t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdUInt32ValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &uint32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(m.Value)) + return b[x:], nil + } +} + +func makeStdUInt32ValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &uint32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&m.Value)) + return b[x:], nil + } +} + +func makeStdUInt32ValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &uint32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdUInt32ValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &uint32Value{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdBoolValueMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + t := ptr.asPointerTo(u.typ).Interface().(*bool) + v := &boolValue{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + t := ptr.asPointerTo(u.typ).Interface().(*bool) + v := &boolValue{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdBoolValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*bool) + v := &boolValue{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*bool) + v := &boolValue{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdBoolValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(bool) + v := &boolValue{t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(bool) + v := &boolValue{t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdBoolValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*bool) + v := &boolValue{*t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*bool) + v := &boolValue{*t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdBoolValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &boolValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(m.Value)) + return b[x:], nil + } +} + +func makeStdBoolValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &boolValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&m.Value)) + return b[x:], nil + } +} + +func makeStdBoolValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &boolValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdBoolValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &boolValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdStringValueMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + t := ptr.asPointerTo(u.typ).Interface().(*string) + v := &stringValue{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + t := ptr.asPointerTo(u.typ).Interface().(*string) + v := &stringValue{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdStringValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*string) + v := &stringValue{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*string) + v := &stringValue{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdStringValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(string) + v := &stringValue{t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(string) + v := &stringValue{t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdStringValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*string) + v := &stringValue{*t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*string) + v := &stringValue{*t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdStringValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &stringValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(m.Value)) + return b[x:], nil + } +} + +func makeStdStringValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &stringValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&m.Value)) + return b[x:], nil + } +} + +func makeStdStringValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &stringValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdStringValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &stringValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdBytesValueMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + t := ptr.asPointerTo(u.typ).Interface().(*[]byte) + v := &bytesValue{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + t := ptr.asPointerTo(u.typ).Interface().(*[]byte) + v := &bytesValue{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdBytesValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + if ptr.isNil() { + return 0 + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*[]byte) + v := &bytesValue{*t} + siz := Size(v) + return tagsize + SizeVarint(uint64(siz)) + siz + }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + if ptr.isNil() { + return b, nil + } + t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*[]byte) + v := &bytesValue{*t} + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(buf))) + b = append(b, buf...) + return b, nil + } +} + +func makeStdBytesValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(u.typ) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().([]byte) + v := &bytesValue{t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(u.typ) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().([]byte) + v := &bytesValue{t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdBytesValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { + return func(ptr pointer, tagsize int) int { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + n := 0 + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*[]byte) + v := &bytesValue{*t} + siz := Size(v) + n += siz + SizeVarint(uint64(siz)) + tagsize + } + return n + }, + func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { + s := ptr.getSlice(reflect.PtrTo(u.typ)) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + t := elem.Interface().(*[]byte) + v := &bytesValue{*t} + siz := Size(v) + buf, err := Marshal(v) + if err != nil { + return nil, err + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(siz)) + b = append(b, buf...) + } + + return b, nil + } +} + +func makeStdBytesValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &bytesValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(sub.typ).Elem() + s.Set(reflect.ValueOf(m.Value)) + return b[x:], nil + } +} + +func makeStdBytesValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &bytesValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() + s.Set(reflect.ValueOf(&m.Value)) + return b[x:], nil + } +} + +func makeStdBytesValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &bytesValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(reflect.PtrTo(sub.typ)) + newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} + +func makeStdBytesValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { + return func(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return nil, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + m := &bytesValue{} + if err := Unmarshal(b[:x], m); err != nil { + return nil, err + } + slice := f.getSlice(sub.typ) + newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) + slice.Set(newSlice) + return b[x:], nil + } +} diff --git a/vendor/github.com/gogo/protobuf/proto/wrappers_gogo.go b/vendor/github.com/gogo/protobuf/proto/wrappers_gogo.go new file mode 100644 index 0000000000000..c1cf7bf85e9ad --- /dev/null +++ b/vendor/github.com/gogo/protobuf/proto/wrappers_gogo.go @@ -0,0 +1,113 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2018, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +type float64Value struct { + Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *float64Value) Reset() { *m = float64Value{} } +func (*float64Value) ProtoMessage() {} +func (*float64Value) String() string { return "float64" } + +type float32Value struct { + Value float32 `protobuf:"fixed32,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *float32Value) Reset() { *m = float32Value{} } +func (*float32Value) ProtoMessage() {} +func (*float32Value) String() string { return "float32" } + +type int64Value struct { + Value int64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *int64Value) Reset() { *m = int64Value{} } +func (*int64Value) ProtoMessage() {} +func (*int64Value) String() string { return "int64" } + +type uint64Value struct { + Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *uint64Value) Reset() { *m = uint64Value{} } +func (*uint64Value) ProtoMessage() {} +func (*uint64Value) String() string { return "uint64" } + +type int32Value struct { + Value int32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *int32Value) Reset() { *m = int32Value{} } +func (*int32Value) ProtoMessage() {} +func (*int32Value) String() string { return "int32" } + +type uint32Value struct { + Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *uint32Value) Reset() { *m = uint32Value{} } +func (*uint32Value) ProtoMessage() {} +func (*uint32Value) String() string { return "uint32" } + +type boolValue struct { + Value bool `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *boolValue) Reset() { *m = boolValue{} } +func (*boolValue) ProtoMessage() {} +func (*boolValue) String() string { return "bool" } + +type stringValue struct { + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *stringValue) Reset() { *m = stringValue{} } +func (*stringValue) ProtoMessage() {} +func (*stringValue) String() string { return "string" } + +type bytesValue struct { + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *bytesValue) Reset() { *m = bytesValue{} } +func (*bytesValue) ProtoMessage() {} +func (*bytesValue) String() string { return "[]byte" } + +func init() { + RegisterType((*float64Value)(nil), "gogo.protobuf.proto.DoubleValue") + RegisterType((*float32Value)(nil), "gogo.protobuf.proto.FloatValue") + RegisterType((*int64Value)(nil), "gogo.protobuf.proto.Int64Value") + RegisterType((*uint64Value)(nil), "gogo.protobuf.proto.UInt64Value") + RegisterType((*int32Value)(nil), "gogo.protobuf.proto.Int32Value") + RegisterType((*uint32Value)(nil), "gogo.protobuf.proto.UInt32Value") + RegisterType((*boolValue)(nil), "gogo.protobuf.proto.BoolValue") + RegisterType((*stringValue)(nil), "gogo.protobuf.proto.StringValue") + RegisterType((*bytesValue)(nil), "gogo.protobuf.proto.BytesValue") +} diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/any.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/any.proto index d9519f5cf7a28..4cf3843bd79d6 100644 --- a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/any.proto +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/any.proto @@ -120,17 +120,19 @@ option objc_class_prefix = "GPB"; // } // message Any { - // A URL/resource name whose content describes the type of the - // serialized protocol buffer message. + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). // - // For URLs which use the scheme `http`, `https`, or no scheme, the - // following restrictions and interpretations apply: + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: // // * If no scheme is provided, `https` is assumed. - // * The last segment of the URL's path must represent the fully - // qualified name of the type (as in `path/google.protobuf.Duration`). - // The name should be in a canonical form (e.g., leading "." is - // not accepted). // * An HTTP GET on the URL must yield a [google.protobuf.Type][] // value in binary format, or produce an error. // * Applications are allowed to cache lookup results based on the @@ -139,6 +141,10 @@ message Any { // on changes to types. (Use versioned type names to manage // breaking changes.) // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // // Schemes other than `http`, `https` (or the empty scheme) might be // used with implementation specific semantics. // diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/api.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/api.proto new file mode 100644 index 0000000000000..67c1ddbd9d3c8 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/api.proto @@ -0,0 +1,210 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +import "google/protobuf/source_context.proto"; +import "google/protobuf/type.proto"; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "ApiProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option go_package = "types"; + +// Api is a light-weight descriptor for an API Interface. +// +// Interfaces are also described as "protocol buffer services" in some contexts, +// such as by the "service" keyword in a .proto file, but they are different +// from API Services, which represent a concrete implementation of an interface +// as opposed to simply a description of methods and bindings. They are also +// sometimes simply referred to as "APIs" in other contexts, such as the name of +// this message itself. See https://cloud.google.com/apis/design/glossary for +// detailed terminology. +message Api { + + // The fully qualified name of this interface, including package name + // followed by the interface's simple name. + string name = 1; + + // The methods of this interface, in unspecified order. + repeated Method methods = 2; + + // Any metadata attached to the interface. + repeated Option options = 3; + + // A version string for this interface. If specified, must have the form + // `major-version.minor-version`, as in `1.10`. If the minor version is + // omitted, it defaults to zero. If the entire version field is empty, the + // major version is derived from the package name, as outlined below. If the + // field is not empty, the version in the package name will be verified to be + // consistent with what is provided here. + // + // The versioning schema uses [semantic + // versioning](http://semver.org) where the major version number + // indicates a breaking change and the minor version an additive, + // non-breaking change. Both version numbers are signals to users + // what to expect from different versions, and should be carefully + // chosen based on the product plan. + // + // The major version is also reflected in the package name of the + // interface, which must end in `v`, as in + // `google.feature.v1`. For major versions 0 and 1, the suffix can + // be omitted. Zero major versions must only be used for + // experimental, non-GA interfaces. + // + // + string version = 4; + + // Source context for the protocol buffer service represented by this + // message. + SourceContext source_context = 5; + + // Included interfaces. See [Mixin][]. + repeated Mixin mixins = 6; + + // The source syntax of the service. + Syntax syntax = 7; +} + +// Method represents a method of an API interface. +message Method { + + // The simple name of this method. + string name = 1; + + // A URL of the input message type. + string request_type_url = 2; + + // If true, the request is streamed. + bool request_streaming = 3; + + // The URL of the output message type. + string response_type_url = 4; + + // If true, the response is streamed. + bool response_streaming = 5; + + // Any metadata attached to the method. + repeated Option options = 6; + + // The source syntax of this method. + Syntax syntax = 7; +} + +// Declares an API Interface to be included in this interface. The including +// interface must redeclare all the methods from the included interface, but +// documentation and options are inherited as follows: +// +// - If after comment and whitespace stripping, the documentation +// string of the redeclared method is empty, it will be inherited +// from the original method. +// +// - Each annotation belonging to the service config (http, +// visibility) which is not set in the redeclared method will be +// inherited. +// +// - If an http annotation is inherited, the path pattern will be +// modified as follows. Any version prefix will be replaced by the +// version of the including interface plus the [root][] path if +// specified. +// +// Example of a simple mixin: +// +// package google.acl.v1; +// service AccessControl { +// // Get the underlying ACL object. +// rpc GetAcl(GetAclRequest) returns (Acl) { +// option (google.api.http).get = "/v1/{resource=**}:getAcl"; +// } +// } +// +// package google.storage.v2; +// service Storage { +// rpc GetAcl(GetAclRequest) returns (Acl); +// +// // Get a data record. +// rpc GetData(GetDataRequest) returns (Data) { +// option (google.api.http).get = "/v2/{resource=**}"; +// } +// } +// +// Example of a mixin configuration: +// +// apis: +// - name: google.storage.v2.Storage +// mixins: +// - name: google.acl.v1.AccessControl +// +// The mixin construct implies that all methods in `AccessControl` are +// also declared with same name and request/response types in +// `Storage`. A documentation generator or annotation processor will +// see the effective `Storage.GetAcl` method after inherting +// documentation and annotations as follows: +// +// service Storage { +// // Get the underlying ACL object. +// rpc GetAcl(GetAclRequest) returns (Acl) { +// option (google.api.http).get = "/v2/{resource=**}:getAcl"; +// } +// ... +// } +// +// Note how the version in the path pattern changed from `v1` to `v2`. +// +// If the `root` field in the mixin is specified, it should be a +// relative path under which inherited HTTP paths are placed. Example: +// +// apis: +// - name: google.storage.v2.Storage +// mixins: +// - name: google.acl.v1.AccessControl +// root: acls +// +// This implies the following inherited HTTP annotation: +// +// service Storage { +// // Get the underlying ACL object. +// rpc GetAcl(GetAclRequest) returns (Acl) { +// option (google.api.http).get = "/v2/acls/{resource=**}:getAcl"; +// } +// ... +// } +message Mixin { + // The fully qualified name of the interface which is included. + string name = 1; + + // If non-empty specifies a path under which inherited HTTP paths + // are rooted. + string root = 2; +} diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/compiler/plugin.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/compiler/plugin.proto index e85c852fcf33f..4a88adf148834 100644 --- a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/compiler/plugin.proto +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/compiler/plugin.proto @@ -45,6 +45,7 @@ // flag "--${NAME}_out" is passed to protoc. syntax = "proto2"; + package google.protobuf.compiler; option java_package = "com.google.protobuf.compiler"; option java_outer_classname = "PluginProtos"; diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/descriptor.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/descriptor.proto index 411cd9de2d088..4a08905a56fbf 100644 --- a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/descriptor.proto +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/descriptor.proto @@ -40,6 +40,7 @@ syntax = "proto2"; package google.protobuf; + option go_package = "descriptor"; option java_package = "com.google.protobuf"; option java_outer_classname = "DescriptorProtos"; @@ -59,8 +60,8 @@ message FileDescriptorSet { // Describes a complete .proto file. message FileDescriptorProto { - optional string name = 1; // file name, relative to root of source tree - optional string package = 2; // e.g. "foo", "foo.bar", etc. + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. // Names of files imported by this file. repeated string dependency = 3; @@ -100,8 +101,8 @@ message DescriptorProto { repeated EnumDescriptorProto enum_type = 4; message ExtensionRange { - optional int32 start = 1; - optional int32 end = 2; + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. optional ExtensionRangeOptions options = 3; } @@ -115,8 +116,8 @@ message DescriptorProto { // fields or extension ranges in the same message. Reserved ranges may // not overlap. message ReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Exclusive. + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. } repeated ReservedRange reserved_range = 9; // Reserved field names, which may not be used by fields in the same message. @@ -137,42 +138,42 @@ message FieldDescriptorProto { enum Type { // 0 is reserved for errors. // Order is weird for historical reasons. - TYPE_DOUBLE = 1; - TYPE_FLOAT = 2; + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if // negative values are likely. - TYPE_INT64 = 3; - TYPE_UINT64 = 4; + TYPE_INT64 = 3; + TYPE_UINT64 = 4; // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if // negative values are likely. - TYPE_INT32 = 5; - TYPE_FIXED64 = 6; - TYPE_FIXED32 = 7; - TYPE_BOOL = 8; - TYPE_STRING = 9; + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; // Tag-delimited aggregate. // Group type is deprecated and not supported in proto3. However, Proto3 // implementations should still be able to parse the group wire format and // treat group fields as unknown fields. - TYPE_GROUP = 10; - TYPE_MESSAGE = 11; // Length-delimited aggregate. + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; // Length-delimited aggregate. // New in version 2. - TYPE_BYTES = 12; - TYPE_UINT32 = 13; - TYPE_ENUM = 14; - TYPE_SFIXED32 = 15; - TYPE_SFIXED64 = 16; - TYPE_SINT32 = 17; // Uses ZigZag encoding. - TYPE_SINT64 = 18; // Uses ZigZag encoding. - }; + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + } enum Label { // 0 is reserved for errors - LABEL_OPTIONAL = 1; - LABEL_REQUIRED = 2; - LABEL_REPEATED = 3; - }; + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } optional string name = 1; optional int32 number = 3; @@ -234,8 +235,8 @@ message EnumDescriptorProto { // is inclusive such that it can appropriately represent the entire int32 // domain. message EnumReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Inclusive. + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Inclusive. } // Range of reserved numeric values. Reserved numeric values may not be used @@ -276,9 +277,9 @@ message MethodDescriptorProto { optional MethodOptions options = 4; // Identifies if client streams multiple client messages - optional bool client_streaming = 5 [default=false]; + optional bool client_streaming = 5 [default = false]; // Identifies if server streams multiple server messages - optional bool server_streaming = 6 [default=false]; + optional bool server_streaming = 6 [default = false]; } @@ -314,7 +315,6 @@ message MethodDescriptorProto { // If this turns out to be popular, a web service will be set up // to automatically assign option numbers. - message FileOptions { // Sets the Java package where classes generated from this .proto will be @@ -337,7 +337,7 @@ message FileOptions { // named by java_outer_classname. However, the outer class will still be // generated to contain the file's getDescriptor() method as well as any // top-level extensions defined in the file. - optional bool java_multiple_files = 10 [default=false]; + optional bool java_multiple_files = 10 [default = false]; // This option does nothing. optional bool java_generate_equals_and_hash = 20 [deprecated=true]; @@ -348,17 +348,17 @@ message FileOptions { // Message reflection will do the same. // However, an extension field still accepts non-UTF-8 byte sequences. // This option has no effect on when used with the lite runtime. - optional bool java_string_check_utf8 = 27 [default=false]; + optional bool java_string_check_utf8 = 27 [default = false]; // Generated classes can be optimized for speed or code size. enum OptimizeMode { - SPEED = 1; // Generate complete code for parsing, serialization, - // etc. - CODE_SIZE = 2; // Use ReflectionOps to implement these methods. - LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. + SPEED = 1; // Generate complete code for parsing, serialization, + // etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. } - optional OptimizeMode optimize_for = 9 [default=SPEED]; + optional OptimizeMode optimize_for = 9 [default = SPEED]; // Sets the Go package where structs generated from this .proto will be // placed. If omitted, the Go package will be derived from the following: @@ -369,6 +369,7 @@ message FileOptions { + // Should generic services be generated in each language? "Generic" services // are not specific to any particular RPC system. They are generated by the // main code generators in each language (without additional plugins). @@ -379,20 +380,20 @@ message FileOptions { // that generate code specific to your particular RPC system. Therefore, // these default to false. Old code which depends on generic services should // explicitly set them to true. - optional bool cc_generic_services = 16 [default=false]; - optional bool java_generic_services = 17 [default=false]; - optional bool py_generic_services = 18 [default=false]; - optional bool php_generic_services = 42 [default=false]; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool php_generic_services = 42 [default = false]; // Is this file deprecated? // Depending on the target platform, this can emit Deprecated annotations // for everything in the file, or it will be completely ignored; in the very // least, this is a formalization for deprecating files. - optional bool deprecated = 23 [default=false]; + optional bool deprecated = 23 [default = false]; // Enables the use of arenas for the proto messages in this file. This applies // only to generated classes for C++. - optional bool cc_enable_arenas = 31 [default=false]; + optional bool cc_enable_arenas = 31 [default = false]; // Sets the objective c class prefix which is prepended to all objective c @@ -417,10 +418,23 @@ message FileOptions { // determining the namespace. optional string php_namespace = 41; - // The parser stores options it doesn't recognize here. See above. + // Use this option to change the namespace of php generated metadata classes. + // Default is empty. When this option is empty, the proto file name will be + // used for determining the namespace. + optional string php_metadata_namespace = 44; + + // Use this option to change the package of ruby generated classes. Default + // is empty. When this option is not set, the package name will be used for + // determining the ruby package. + optional string ruby_package = 45; + + + // The parser stores options it doesn't recognize here. + // See the documentation for the "Options" section above. repeated UninterpretedOption uninterpreted_option = 999; - // Clients can define custom options in extensions of this message. See above. + // Clients can define custom options in extensions of this message. + // See the documentation for the "Options" section above. extensions 1000 to max; //reserved 38; @@ -445,18 +459,18 @@ message MessageOptions { // // Because this is an option, the above two restrictions are not enforced by // the protocol compiler. - optional bool message_set_wire_format = 1 [default=false]; + optional bool message_set_wire_format = 1 [default = false]; // Disables the generation of the standard "descriptor()" accessor, which can // conflict with a field of the same name. This is meant to make migration // from proto1 easier; new code should avoid fields named "descriptor". - optional bool no_standard_descriptor_accessor = 2 [default=false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; // Is this message deprecated? // Depending on the target platform, this can emit Deprecated annotations // for the message, or it will be completely ignored; in the very least, // this is a formalization for deprecating messages. - optional bool deprecated = 3 [default=false]; + optional bool deprecated = 3 [default = false]; // Whether the message is an automatically generated map entry type for the // maps field. @@ -473,7 +487,7 @@ message MessageOptions { // // Implementations may choose not to generate the map_entry=true message, but // use a native map in the target language to hold the keys and values. - // The reflection APIs in such implementions still need to work as + // The reflection APIs in such implementations still need to work as // if the field is a repeated message field. // // NOTE: Do not set the option in .proto files. Always use the maps syntax @@ -484,6 +498,7 @@ message MessageOptions { //reserved 8; // javalite_serializable //reserved 9; // javanano_as_lite + // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -563,16 +578,16 @@ message FieldOptions { // implementation must either *always* check its required fields, or *never* // check its required fields, regardless of whether or not the message has // been parsed. - optional bool lazy = 5 [default=false]; + optional bool lazy = 5 [default = false]; // Is this field deprecated? // Depending on the target platform, this can emit Deprecated annotations // for accessors, or it will be completely ignored; in the very least, this // is a formalization for deprecating fields. - optional bool deprecated = 3 [default=false]; + optional bool deprecated = 3 [default = false]; // For Google-internal migration only. Do not use. - optional bool weak = 10 [default=false]; + optional bool weak = 10 [default = false]; // The parser stores options it doesn't recognize here. See above. @@ -602,7 +617,7 @@ message EnumOptions { // Depending on the target platform, this can emit Deprecated annotations // for the enum, or it will be completely ignored; in the very least, this // is a formalization for deprecating enums. - optional bool deprecated = 3 [default=false]; + optional bool deprecated = 3 [default = false]; //reserved 5; // javanano_as_lite @@ -618,7 +633,7 @@ message EnumValueOptions { // Depending on the target platform, this can emit Deprecated annotations // for the enum value, or it will be completely ignored; in the very least, // this is a formalization for deprecating enum values. - optional bool deprecated = 1 [default=false]; + optional bool deprecated = 1 [default = false]; // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -638,7 +653,7 @@ message ServiceOptions { // Depending on the target platform, this can emit Deprecated annotations // for the service, or it will be completely ignored; in the very least, // this is a formalization for deprecating services. - optional bool deprecated = 33 [default=false]; + optional bool deprecated = 33 [default = false]; // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -658,18 +673,18 @@ message MethodOptions { // Depending on the target platform, this can emit Deprecated annotations // for the method, or it will be completely ignored; in the very least, // this is a formalization for deprecating methods. - optional bool deprecated = 33 [default=false]; + optional bool deprecated = 33 [default = false]; // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, // or neither? HTTP based RPC implementation may choose GET verb for safe // methods, and PUT verb for idempotent methods instead of the default POST. enum IdempotencyLevel { IDEMPOTENCY_UNKNOWN = 0; - NO_SIDE_EFFECTS = 1; // implies idempotent - IDEMPOTENT = 2; // idempotent, but may have side effects + NO_SIDE_EFFECTS = 1; // implies idempotent + IDEMPOTENT = 2; // idempotent, but may have side effects } - optional IdempotencyLevel idempotency_level = - 34 [default=IDEMPOTENCY_UNKNOWN]; + optional IdempotencyLevel idempotency_level = 34 + [default = IDEMPOTENCY_UNKNOWN]; // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -750,7 +765,7 @@ message SourceCodeInfo { // beginning of the "extend" block and is shared by all extensions within // the block. // - Just because a location's span is a subset of some other location's span - // does not mean that it is a descendent. For example, a "group" defines + // does not mean that it is a descendant. For example, a "group" defines // both a type and a field in a single declaration. Thus, the locations // corresponding to the type and field and their components will overlap. // - Code which tries to interpret locations should probably be designed to @@ -781,14 +796,14 @@ message SourceCodeInfo { // [ 4, 3, 2, 7 ] // this path refers to the whole field declaration (from the beginning // of the label to the terminating semicolon). - repeated int32 path = 1 [packed=true]; + repeated int32 path = 1 [packed = true]; // Always has exactly three or four elements: start line, start column, // end line (optional, otherwise assumed same as start line), end column. // These are packed into a single field for efficiency. Note that line // and column numbers are zero-based -- typically you will want to add // 1 to each before displaying to a user. - repeated int32 span = 2 [packed=true]; + repeated int32 span = 2 [packed = true]; // If this SourceCodeInfo represents a complete declaration, these are any // comments appearing before and after the declaration which appear to be @@ -853,7 +868,7 @@ message GeneratedCodeInfo { message Annotation { // Identifies the element in the original source .proto file. This field // is formatted the same as SourceCodeInfo.Location.path. - repeated int32 path = 1 [packed=true]; + repeated int32 path = 1 [packed = true]; // Identifies the filesystem path to the original source .proto. optional string source_file = 2; diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/duration.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/duration.proto index 8bbaa8b6228e4..b14bea5d01226 100644 --- a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/duration.proto +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/duration.proto @@ -101,7 +101,6 @@ option objc_class_prefix = "GPB"; // // message Duration { - // Signed seconds of the span of time. Must be from -315,576,000,000 // to +315,576,000,000 inclusive. Note: these bounds are computed from: // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/field_mask.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/field_mask.proto index 994af79f03c61..7b77007b7e6bc 100644 --- a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/field_mask.proto +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/field_mask.proto @@ -38,6 +38,7 @@ option java_outer_classname = "FieldMaskProto"; option java_multiple_files = true; option objc_class_prefix = "GPB"; option go_package = "types"; +option cc_enable_arenas = true; // `FieldMask` represents a set of symbolic field paths, for example: // @@ -107,57 +108,49 @@ option go_package = "types"; // describe the updated values, the API ignores the values of all // fields not covered by the mask. // -// If a repeated field is specified for an update operation, the existing -// repeated values in the target resource will be overwritten by the new values. -// Note that a repeated field is only allowed in the last position of a `paths` -// string. +// If a repeated field is specified for an update operation, new values will +// be appended to the existing repeated field in the target resource. Note that +// a repeated field is only allowed in the last position of a `paths` string. // // If a sub-message is specified in the last position of the field mask for an -// update operation, then the existing sub-message in the target resource is -// overwritten. Given the target message: +// update operation, then new value will be merged into the existing sub-message +// in the target resource. +// +// For example, given the target message: // // f { // b { -// d : 1 -// x : 2 +// d: 1 +// x: 2 // } -// c : 1 +// c: [1] // } // // And an update message: // // f { // b { -// d : 10 +// d: 10 // } +// c: [2] // } // // then if the field mask is: // -// paths: "f.b" +// paths: ["f.b", "f.c"] // // then the result will be: // // f { // b { -// d : 10 +// d: 10 +// x: 2 // } -// c : 1 +// c: [1, 2] // } // -// However, if the update mask was: -// -// paths: "f.b.d" -// -// then the result would be: -// -// f { -// b { -// d : 10 -// x : 2 -// } -// c : 1 -// } +// An implementation may provide options to override this default behavior for +// repeated and message fields. // // In order to reset a field's value to the default, the field must // be in the mask and set to the default value in the provided resource. @@ -240,6 +233,12 @@ option go_package = "types"; // // Note that oneof type names ("test_oneof" in this case) cannot be used in // paths. +// +// ## Field Mask Verification +// +// The implementation of any API method which has a FieldMask type field in the +// request should verify the included field paths, and return an +// `INVALID_ARGUMENT` error if any path is duplicated or unmappable. message FieldMask { // The set of field mask paths. repeated string paths = 1; diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/source_context.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/source_context.proto new file mode 100644 index 0000000000000..8654578c770dc --- /dev/null +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/source_context.proto @@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "SourceContextProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option go_package = "types"; + +// `SourceContext` represents information about the source of a +// protobuf element, like the file in which it is defined. +message SourceContext { + // The path-qualified name of the .proto file that contained the associated + // protobuf element. For example: `"google/protobuf/source_context.proto"`. + string file_name = 1; +} diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/struct.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/struct.proto index 4f78641fa9182..9db0771592921 100644 --- a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/struct.proto +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/struct.proto @@ -40,7 +40,6 @@ option java_outer_classname = "StructProto"; option java_multiple_files = true; option objc_class_prefix = "GPB"; - // `Struct` represents a structured data value, consisting of fields // which map to dynamically typed values. In some languages, `Struct` // might be supported by a native representation. For example, in diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/timestamp.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/timestamp.proto index 4ba0b97b2acbf..0ebe36ea732e2 100644 --- a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/timestamp.proto +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/timestamp.proto @@ -40,17 +40,19 @@ option java_outer_classname = "TimestampProto"; option java_multiple_files = true; option objc_class_prefix = "GPB"; -// A Timestamp represents a point in time independent of any time zone -// or calendar, represented as seconds and fractions of seconds at -// nanosecond resolution in UTC Epoch time. It is encoded using the -// Proleptic Gregorian Calendar which extends the Gregorian calendar -// backwards to year one. It is encoded assuming all minutes are 60 -// seconds long, i.e. leap seconds are "smeared" so that no leap second -// table is needed for interpretation. Range is from -// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. -// By restricting to that range, we ensure that we can convert to -// and from RFC 3339 date strings. -// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. // // # Examples // @@ -103,23 +105,26 @@ option objc_class_prefix = "GPB"; // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone -// is required, though only UTC (as indicated by "Z") is presently supported. +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). // // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past // 01:30 UTC on January 15, 2017. // // In JavaScript, one can convert a Date object to this format using the -// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) // method. In Python, a standard `datetime.datetime` object can be converted -// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) -// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one -// can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( -// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()) -// to obtain a formatter capable of generating timestamps in this format. +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// ) to obtain a formatter capable of generating timestamps in this format. // // message Timestamp { - // Represents seconds of UTC time since Unix epoch // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to // 9999-12-31T23:59:59Z inclusive. diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/type.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/type.proto new file mode 100644 index 0000000000000..cc626250deae5 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/type.proto @@ -0,0 +1,187 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +import "google/protobuf/any.proto"; +import "google/protobuf/source_context.proto"; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TypeProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option go_package = "types"; + +// A protocol buffer message type. +message Type { + // The fully qualified message name. + string name = 1; + // The list of fields. + repeated Field fields = 2; + // The list of types appearing in `oneof` definitions in this type. + repeated string oneofs = 3; + // The protocol buffer options. + repeated Option options = 4; + // The source context. + SourceContext source_context = 5; + // The source syntax. + Syntax syntax = 6; +} + +// A single field of a message type. +message Field { + // Basic field types. + enum Kind { + // Field type unknown. + TYPE_UNKNOWN = 0; + // Field type double. + TYPE_DOUBLE = 1; + // Field type float. + TYPE_FLOAT = 2; + // Field type int64. + TYPE_INT64 = 3; + // Field type uint64. + TYPE_UINT64 = 4; + // Field type int32. + TYPE_INT32 = 5; + // Field type fixed64. + TYPE_FIXED64 = 6; + // Field type fixed32. + TYPE_FIXED32 = 7; + // Field type bool. + TYPE_BOOL = 8; + // Field type string. + TYPE_STRING = 9; + // Field type group. Proto2 syntax only, and deprecated. + TYPE_GROUP = 10; + // Field type message. + TYPE_MESSAGE = 11; + // Field type bytes. + TYPE_BYTES = 12; + // Field type uint32. + TYPE_UINT32 = 13; + // Field type enum. + TYPE_ENUM = 14; + // Field type sfixed32. + TYPE_SFIXED32 = 15; + // Field type sfixed64. + TYPE_SFIXED64 = 16; + // Field type sint32. + TYPE_SINT32 = 17; + // Field type sint64. + TYPE_SINT64 = 18; + } + + // Whether a field is optional, required, or repeated. + enum Cardinality { + // For fields with unknown cardinality. + CARDINALITY_UNKNOWN = 0; + // For optional fields. + CARDINALITY_OPTIONAL = 1; + // For required fields. Proto2 syntax only. + CARDINALITY_REQUIRED = 2; + // For repeated fields. + CARDINALITY_REPEATED = 3; + }; + + // The field type. + Kind kind = 1; + // The field cardinality. + Cardinality cardinality = 2; + // The field number. + int32 number = 3; + // The field name. + string name = 4; + // The field type URL, without the scheme, for message or enumeration + // types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`. + string type_url = 6; + // The index of the field type in `Type.oneofs`, for message or enumeration + // types. The first type has index 1; zero means the type is not in the list. + int32 oneof_index = 7; + // Whether to use alternative packed wire representation. + bool packed = 8; + // The protocol buffer options. + repeated Option options = 9; + // The field JSON name. + string json_name = 10; + // The string value of the default value of this field. Proto2 syntax only. + string default_value = 11; +} + +// Enum type definition. +message Enum { + // Enum type name. + string name = 1; + // Enum value definitions. + repeated EnumValue enumvalue = 2; + // Protocol buffer options. + repeated Option options = 3; + // The source context. + SourceContext source_context = 4; + // The source syntax. + Syntax syntax = 5; +} + +// Enum value definition. +message EnumValue { + // Enum value name. + string name = 1; + // Enum value number. + int32 number = 2; + // Protocol buffer options. + repeated Option options = 3; +} + +// A protocol buffer option, which can be attached to a message, field, +// enumeration, etc. +message Option { + // The option's name. For protobuf built-in options (options defined in + // descriptor.proto), this is the short name. For example, `"map_entry"`. + // For custom options, it should be the fully-qualified name. For example, + // `"google.api.http"`. + string name = 1; + // The option's value packed in an Any message. If the value is a primitive, + // the corresponding wrapper type defined in google/protobuf/wrappers.proto + // should be used. If the value is an enum, it should be stored as an int32 + // value using the google.protobuf.Int32Value type. + Any value = 2; +} + +// The syntax in which a protocol buffer element is defined. +enum Syntax { + // Syntax `proto2`. + SYNTAX_PROTO2 = 0; + // Syntax `proto3`. + SYNTAX_PROTO3 = 1; +} diff --git a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/wrappers.proto b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/wrappers.proto index c5632e5ca999a..59b76acde8c91 100644 --- a/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/wrappers.proto +++ b/vendor/github.com/gogo/protobuf/protobuf/google/protobuf/wrappers.proto @@ -32,6 +32,11 @@ // for embedding primitives in the `google.protobuf.Any` type and for places // where we need to distinguish between the absence of a primitive // typed field and its default value. +// +// These wrappers have no meaningful use within repeated fields as they lack +// the ability to detect presence on individual elements. +// These wrappers have no meaningful use within a map or a oneof since +// individual entries of a map or fields of a oneof can already detect presence. syntax = "proto3"; diff --git a/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.pb.go b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.pb.go index 4174cbd9f3d6a..18b2a3318a573 100644 --- a/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.pb.go +++ b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor.pb.go @@ -1,40 +1,13 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: descriptor.proto -/* -Package descriptor is a generated protocol buffer package. - -It is generated from these files: - descriptor.proto - -It has these top-level messages: - FileDescriptorSet - FileDescriptorProto - DescriptorProto - ExtensionRangeOptions - FieldDescriptorProto - OneofDescriptorProto - EnumDescriptorProto - EnumValueDescriptorProto - ServiceDescriptorProto - MethodDescriptorProto - FileOptions - MessageOptions - FieldOptions - OneofOptions - EnumOptions - EnumValueOptions - ServiceOptions - MethodOptions - UninterpretedOption - SourceCodeInfo - GeneratedCodeInfo -*/ package descriptor -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -45,7 +18,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type FieldDescriptorProto_Type int32 @@ -101,6 +74,7 @@ var FieldDescriptorProto_Type_name = map[int32]string{ 17: "TYPE_SINT32", 18: "TYPE_SINT64", } + var FieldDescriptorProto_Type_value = map[string]int32{ "TYPE_DOUBLE": 1, "TYPE_FLOAT": 2, @@ -127,9 +101,11 @@ func (x FieldDescriptorProto_Type) Enum() *FieldDescriptorProto_Type { *p = x return p } + func (x FieldDescriptorProto_Type) String() string { return proto.EnumName(FieldDescriptorProto_Type_name, int32(x)) } + func (x *FieldDescriptorProto_Type) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FieldDescriptorProto_Type_value, data, "FieldDescriptorProto_Type") if err != nil { @@ -138,8 +114,9 @@ func (x *FieldDescriptorProto_Type) UnmarshalJSON(data []byte) error { *x = FieldDescriptorProto_Type(value) return nil } + func (FieldDescriptorProto_Type) EnumDescriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{4, 0} + return fileDescriptor_308767df5ffe18af, []int{4, 0} } type FieldDescriptorProto_Label int32 @@ -156,6 +133,7 @@ var FieldDescriptorProto_Label_name = map[int32]string{ 2: "LABEL_REQUIRED", 3: "LABEL_REPEATED", } + var FieldDescriptorProto_Label_value = map[string]int32{ "LABEL_OPTIONAL": 1, "LABEL_REQUIRED": 2, @@ -167,9 +145,11 @@ func (x FieldDescriptorProto_Label) Enum() *FieldDescriptorProto_Label { *p = x return p } + func (x FieldDescriptorProto_Label) String() string { return proto.EnumName(FieldDescriptorProto_Label_name, int32(x)) } + func (x *FieldDescriptorProto_Label) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FieldDescriptorProto_Label_value, data, "FieldDescriptorProto_Label") if err != nil { @@ -178,8 +158,9 @@ func (x *FieldDescriptorProto_Label) UnmarshalJSON(data []byte) error { *x = FieldDescriptorProto_Label(value) return nil } + func (FieldDescriptorProto_Label) EnumDescriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{4, 1} + return fileDescriptor_308767df5ffe18af, []int{4, 1} } // Generated classes can be optimized for speed or code size. @@ -197,6 +178,7 @@ var FileOptions_OptimizeMode_name = map[int32]string{ 2: "CODE_SIZE", 3: "LITE_RUNTIME", } + var FileOptions_OptimizeMode_value = map[string]int32{ "SPEED": 1, "CODE_SIZE": 2, @@ -208,9 +190,11 @@ func (x FileOptions_OptimizeMode) Enum() *FileOptions_OptimizeMode { *p = x return p } + func (x FileOptions_OptimizeMode) String() string { return proto.EnumName(FileOptions_OptimizeMode_name, int32(x)) } + func (x *FileOptions_OptimizeMode) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FileOptions_OptimizeMode_value, data, "FileOptions_OptimizeMode") if err != nil { @@ -219,8 +203,9 @@ func (x *FileOptions_OptimizeMode) UnmarshalJSON(data []byte) error { *x = FileOptions_OptimizeMode(value) return nil } + func (FileOptions_OptimizeMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{10, 0} + return fileDescriptor_308767df5ffe18af, []int{10, 0} } type FieldOptions_CType int32 @@ -237,6 +222,7 @@ var FieldOptions_CType_name = map[int32]string{ 1: "CORD", 2: "STRING_PIECE", } + var FieldOptions_CType_value = map[string]int32{ "STRING": 0, "CORD": 1, @@ -248,9 +234,11 @@ func (x FieldOptions_CType) Enum() *FieldOptions_CType { *p = x return p } + func (x FieldOptions_CType) String() string { return proto.EnumName(FieldOptions_CType_name, int32(x)) } + func (x *FieldOptions_CType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FieldOptions_CType_value, data, "FieldOptions_CType") if err != nil { @@ -259,8 +247,9 @@ func (x *FieldOptions_CType) UnmarshalJSON(data []byte) error { *x = FieldOptions_CType(value) return nil } + func (FieldOptions_CType) EnumDescriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{12, 0} + return fileDescriptor_308767df5ffe18af, []int{12, 0} } type FieldOptions_JSType int32 @@ -279,6 +268,7 @@ var FieldOptions_JSType_name = map[int32]string{ 1: "JS_STRING", 2: "JS_NUMBER", } + var FieldOptions_JSType_value = map[string]int32{ "JS_NORMAL": 0, "JS_STRING": 1, @@ -290,9 +280,11 @@ func (x FieldOptions_JSType) Enum() *FieldOptions_JSType { *p = x return p } + func (x FieldOptions_JSType) String() string { return proto.EnumName(FieldOptions_JSType_name, int32(x)) } + func (x *FieldOptions_JSType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FieldOptions_JSType_value, data, "FieldOptions_JSType") if err != nil { @@ -301,8 +293,9 @@ func (x *FieldOptions_JSType) UnmarshalJSON(data []byte) error { *x = FieldOptions_JSType(value) return nil } + func (FieldOptions_JSType) EnumDescriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{12, 1} + return fileDescriptor_308767df5ffe18af, []int{12, 1} } // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, @@ -321,6 +314,7 @@ var MethodOptions_IdempotencyLevel_name = map[int32]string{ 1: "NO_SIDE_EFFECTS", 2: "IDEMPOTENT", } + var MethodOptions_IdempotencyLevel_value = map[string]int32{ "IDEMPOTENCY_UNKNOWN": 0, "NO_SIDE_EFFECTS": 1, @@ -332,9 +326,11 @@ func (x MethodOptions_IdempotencyLevel) Enum() *MethodOptions_IdempotencyLevel { *p = x return p } + func (x MethodOptions_IdempotencyLevel) String() string { return proto.EnumName(MethodOptions_IdempotencyLevel_name, int32(x)) } + func (x *MethodOptions_IdempotencyLevel) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(MethodOptions_IdempotencyLevel_value, data, "MethodOptions_IdempotencyLevel") if err != nil { @@ -343,21 +339,43 @@ func (x *MethodOptions_IdempotencyLevel) UnmarshalJSON(data []byte) error { *x = MethodOptions_IdempotencyLevel(value) return nil } + func (MethodOptions_IdempotencyLevel) EnumDescriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{17, 0} + return fileDescriptor_308767df5ffe18af, []int{17, 0} } // The protocol compiler can output a FileDescriptorSet containing the .proto // files it parses. type FileDescriptorSet struct { - File []*FileDescriptorProto `protobuf:"bytes,1,rep,name=file" json:"file,omitempty"` - XXX_unrecognized []byte `json:"-"` + File []*FileDescriptorProto `protobuf:"bytes,1,rep,name=file" json:"file,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FileDescriptorSet) Reset() { *m = FileDescriptorSet{} } +func (m *FileDescriptorSet) String() string { return proto.CompactTextString(m) } +func (*FileDescriptorSet) ProtoMessage() {} +func (*FileDescriptorSet) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{0} +} +func (m *FileDescriptorSet) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FileDescriptorSet.Unmarshal(m, b) +} +func (m *FileDescriptorSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FileDescriptorSet.Marshal(b, m, deterministic) +} +func (m *FileDescriptorSet) XXX_Merge(src proto.Message) { + xxx_messageInfo_FileDescriptorSet.Merge(m, src) +} +func (m *FileDescriptorSet) XXX_Size() int { + return xxx_messageInfo_FileDescriptorSet.Size(m) +} +func (m *FileDescriptorSet) XXX_DiscardUnknown() { + xxx_messageInfo_FileDescriptorSet.DiscardUnknown(m) } -func (m *FileDescriptorSet) Reset() { *m = FileDescriptorSet{} } -func (m *FileDescriptorSet) String() string { return proto.CompactTextString(m) } -func (*FileDescriptorSet) ProtoMessage() {} -func (*FileDescriptorSet) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{0} } +var xxx_messageInfo_FileDescriptorSet proto.InternalMessageInfo func (m *FileDescriptorSet) GetFile() []*FileDescriptorProto { if m != nil { @@ -390,14 +408,35 @@ type FileDescriptorProto struct { SourceCodeInfo *SourceCodeInfo `protobuf:"bytes,9,opt,name=source_code_info,json=sourceCodeInfo" json:"source_code_info,omitempty"` // The syntax of the proto file. // The supported values are "proto2" and "proto3". - Syntax *string `protobuf:"bytes,12,opt,name=syntax" json:"syntax,omitempty"` - XXX_unrecognized []byte `json:"-"` + Syntax *string `protobuf:"bytes,12,opt,name=syntax" json:"syntax,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *FileDescriptorProto) Reset() { *m = FileDescriptorProto{} } -func (m *FileDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*FileDescriptorProto) ProtoMessage() {} -func (*FileDescriptorProto) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{1} } +func (m *FileDescriptorProto) Reset() { *m = FileDescriptorProto{} } +func (m *FileDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*FileDescriptorProto) ProtoMessage() {} +func (*FileDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{1} +} +func (m *FileDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FileDescriptorProto.Unmarshal(m, b) +} +func (m *FileDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FileDescriptorProto.Marshal(b, m, deterministic) +} +func (m *FileDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_FileDescriptorProto.Merge(m, src) +} +func (m *FileDescriptorProto) XXX_Size() int { + return xxx_messageInfo_FileDescriptorProto.Size(m) +} +func (m *FileDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_FileDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_FileDescriptorProto proto.InternalMessageInfo func (m *FileDescriptorProto) GetName() string { if m != nil && m.Name != nil { @@ -496,14 +535,35 @@ type DescriptorProto struct { ReservedRange []*DescriptorProto_ReservedRange `protobuf:"bytes,9,rep,name=reserved_range,json=reservedRange" json:"reserved_range,omitempty"` // Reserved field names, which may not be used by fields in the same message. // A given name may only be reserved once. - ReservedName []string `protobuf:"bytes,10,rep,name=reserved_name,json=reservedName" json:"reserved_name,omitempty"` - XXX_unrecognized []byte `json:"-"` + ReservedName []string `protobuf:"bytes,10,rep,name=reserved_name,json=reservedName" json:"reserved_name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DescriptorProto) Reset() { *m = DescriptorProto{} } +func (m *DescriptorProto) String() string { return proto.CompactTextString(m) } +func (*DescriptorProto) ProtoMessage() {} +func (*DescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{2} +} +func (m *DescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DescriptorProto.Unmarshal(m, b) +} +func (m *DescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DescriptorProto.Marshal(b, m, deterministic) +} +func (m *DescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_DescriptorProto.Merge(m, src) +} +func (m *DescriptorProto) XXX_Size() int { + return xxx_messageInfo_DescriptorProto.Size(m) +} +func (m *DescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_DescriptorProto.DiscardUnknown(m) } -func (m *DescriptorProto) Reset() { *m = DescriptorProto{} } -func (m *DescriptorProto) String() string { return proto.CompactTextString(m) } -func (*DescriptorProto) ProtoMessage() {} -func (*DescriptorProto) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{2} } +var xxx_messageInfo_DescriptorProto proto.InternalMessageInfo func (m *DescriptorProto) GetName() string { if m != nil && m.Name != nil { @@ -576,19 +636,38 @@ func (m *DescriptorProto) GetReservedName() []string { } type DescriptorProto_ExtensionRange struct { - Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` - End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` - Options *ExtensionRangeOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` - XXX_unrecognized []byte `json:"-"` + Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` + End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` + Options *ExtensionRangeOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *DescriptorProto_ExtensionRange) Reset() { *m = DescriptorProto_ExtensionRange{} } func (m *DescriptorProto_ExtensionRange) String() string { return proto.CompactTextString(m) } func (*DescriptorProto_ExtensionRange) ProtoMessage() {} func (*DescriptorProto_ExtensionRange) Descriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{2, 0} + return fileDescriptor_308767df5ffe18af, []int{2, 0} +} +func (m *DescriptorProto_ExtensionRange) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DescriptorProto_ExtensionRange.Unmarshal(m, b) +} +func (m *DescriptorProto_ExtensionRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DescriptorProto_ExtensionRange.Marshal(b, m, deterministic) +} +func (m *DescriptorProto_ExtensionRange) XXX_Merge(src proto.Message) { + xxx_messageInfo_DescriptorProto_ExtensionRange.Merge(m, src) +} +func (m *DescriptorProto_ExtensionRange) XXX_Size() int { + return xxx_messageInfo_DescriptorProto_ExtensionRange.Size(m) +} +func (m *DescriptorProto_ExtensionRange) XXX_DiscardUnknown() { + xxx_messageInfo_DescriptorProto_ExtensionRange.DiscardUnknown(m) } +var xxx_messageInfo_DescriptorProto_ExtensionRange proto.InternalMessageInfo + func (m *DescriptorProto_ExtensionRange) GetStart() int32 { if m != nil && m.Start != nil { return *m.Start @@ -614,18 +693,37 @@ func (m *DescriptorProto_ExtensionRange) GetOptions() *ExtensionRangeOptions { // fields or extension ranges in the same message. Reserved ranges may // not overlap. type DescriptorProto_ReservedRange struct { - Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` - End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` - XXX_unrecognized []byte `json:"-"` + Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` + End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *DescriptorProto_ReservedRange) Reset() { *m = DescriptorProto_ReservedRange{} } func (m *DescriptorProto_ReservedRange) String() string { return proto.CompactTextString(m) } func (*DescriptorProto_ReservedRange) ProtoMessage() {} func (*DescriptorProto_ReservedRange) Descriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{2, 1} + return fileDescriptor_308767df5ffe18af, []int{2, 1} +} +func (m *DescriptorProto_ReservedRange) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DescriptorProto_ReservedRange.Unmarshal(m, b) +} +func (m *DescriptorProto_ReservedRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DescriptorProto_ReservedRange.Marshal(b, m, deterministic) +} +func (m *DescriptorProto_ReservedRange) XXX_Merge(src proto.Message) { + xxx_messageInfo_DescriptorProto_ReservedRange.Merge(m, src) +} +func (m *DescriptorProto_ReservedRange) XXX_Size() int { + return xxx_messageInfo_DescriptorProto_ReservedRange.Size(m) +} +func (m *DescriptorProto_ReservedRange) XXX_DiscardUnknown() { + xxx_messageInfo_DescriptorProto_ReservedRange.DiscardUnknown(m) } +var xxx_messageInfo_DescriptorProto_ReservedRange proto.InternalMessageInfo + func (m *DescriptorProto_ReservedRange) GetStart() int32 { if m != nil && m.Start != nil { return *m.Start @@ -643,14 +741,18 @@ func (m *DescriptorProto_ReservedRange) GetEnd() int32 { type ExtensionRangeOptions struct { // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` proto.XXX_InternalExtensions `json:"-"` XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ExtensionRangeOptions) Reset() { *m = ExtensionRangeOptions{} } -func (m *ExtensionRangeOptions) String() string { return proto.CompactTextString(m) } -func (*ExtensionRangeOptions) ProtoMessage() {} -func (*ExtensionRangeOptions) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{3} } +func (m *ExtensionRangeOptions) Reset() { *m = ExtensionRangeOptions{} } +func (m *ExtensionRangeOptions) String() string { return proto.CompactTextString(m) } +func (*ExtensionRangeOptions) ProtoMessage() {} +func (*ExtensionRangeOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{3} +} var extRange_ExtensionRangeOptions = []proto.ExtensionRange{ {Start: 1000, End: 536870911}, @@ -660,6 +762,24 @@ func (*ExtensionRangeOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_ExtensionRangeOptions } +func (m *ExtensionRangeOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ExtensionRangeOptions.Unmarshal(m, b) +} +func (m *ExtensionRangeOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ExtensionRangeOptions.Marshal(b, m, deterministic) +} +func (m *ExtensionRangeOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExtensionRangeOptions.Merge(m, src) +} +func (m *ExtensionRangeOptions) XXX_Size() int { + return xxx_messageInfo_ExtensionRangeOptions.Size(m) +} +func (m *ExtensionRangeOptions) XXX_DiscardUnknown() { + xxx_messageInfo_ExtensionRangeOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_ExtensionRangeOptions proto.InternalMessageInfo + func (m *ExtensionRangeOptions) GetUninterpretedOption() []*UninterpretedOption { if m != nil { return m.UninterpretedOption @@ -697,15 +817,36 @@ type FieldDescriptorProto struct { // user has set a "json_name" option on this field, that option's value // will be used. Otherwise, it's deduced from the field's name by converting // it to camelCase. - JsonName *string `protobuf:"bytes,10,opt,name=json_name,json=jsonName" json:"json_name,omitempty"` - Options *FieldOptions `protobuf:"bytes,8,opt,name=options" json:"options,omitempty"` - XXX_unrecognized []byte `json:"-"` + JsonName *string `protobuf:"bytes,10,opt,name=json_name,json=jsonName" json:"json_name,omitempty"` + Options *FieldOptions `protobuf:"bytes,8,opt,name=options" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *FieldDescriptorProto) Reset() { *m = FieldDescriptorProto{} } -func (m *FieldDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*FieldDescriptorProto) ProtoMessage() {} -func (*FieldDescriptorProto) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{4} } +func (m *FieldDescriptorProto) Reset() { *m = FieldDescriptorProto{} } +func (m *FieldDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*FieldDescriptorProto) ProtoMessage() {} +func (*FieldDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{4} +} +func (m *FieldDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FieldDescriptorProto.Unmarshal(m, b) +} +func (m *FieldDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FieldDescriptorProto.Marshal(b, m, deterministic) +} +func (m *FieldDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_FieldDescriptorProto.Merge(m, src) +} +func (m *FieldDescriptorProto) XXX_Size() int { + return xxx_messageInfo_FieldDescriptorProto.Size(m) +} +func (m *FieldDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_FieldDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_FieldDescriptorProto proto.InternalMessageInfo func (m *FieldDescriptorProto) GetName() string { if m != nil && m.Name != nil { @@ -779,15 +920,36 @@ func (m *FieldDescriptorProto) GetOptions() *FieldOptions { // Describes a oneof. type OneofDescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Options *OneofOptions `protobuf:"bytes,2,opt,name=options" json:"options,omitempty"` - XXX_unrecognized []byte `json:"-"` + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Options *OneofOptions `protobuf:"bytes,2,opt,name=options" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *OneofDescriptorProto) Reset() { *m = OneofDescriptorProto{} } +func (m *OneofDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*OneofDescriptorProto) ProtoMessage() {} +func (*OneofDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{5} +} +func (m *OneofDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OneofDescriptorProto.Unmarshal(m, b) +} +func (m *OneofDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OneofDescriptorProto.Marshal(b, m, deterministic) +} +func (m *OneofDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_OneofDescriptorProto.Merge(m, src) +} +func (m *OneofDescriptorProto) XXX_Size() int { + return xxx_messageInfo_OneofDescriptorProto.Size(m) +} +func (m *OneofDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_OneofDescriptorProto.DiscardUnknown(m) } -func (m *OneofDescriptorProto) Reset() { *m = OneofDescriptorProto{} } -func (m *OneofDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*OneofDescriptorProto) ProtoMessage() {} -func (*OneofDescriptorProto) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{5} } +var xxx_messageInfo_OneofDescriptorProto proto.InternalMessageInfo func (m *OneofDescriptorProto) GetName() string { if m != nil && m.Name != nil { @@ -814,14 +976,35 @@ type EnumDescriptorProto struct { ReservedRange []*EnumDescriptorProto_EnumReservedRange `protobuf:"bytes,4,rep,name=reserved_range,json=reservedRange" json:"reserved_range,omitempty"` // Reserved enum value names, which may not be reused. A given name may only // be reserved once. - ReservedName []string `protobuf:"bytes,5,rep,name=reserved_name,json=reservedName" json:"reserved_name,omitempty"` - XXX_unrecognized []byte `json:"-"` + ReservedName []string `protobuf:"bytes,5,rep,name=reserved_name,json=reservedName" json:"reserved_name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *EnumDescriptorProto) Reset() { *m = EnumDescriptorProto{} } -func (m *EnumDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*EnumDescriptorProto) ProtoMessage() {} -func (*EnumDescriptorProto) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{6} } +func (m *EnumDescriptorProto) Reset() { *m = EnumDescriptorProto{} } +func (m *EnumDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*EnumDescriptorProto) ProtoMessage() {} +func (*EnumDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{6} +} +func (m *EnumDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnumDescriptorProto.Unmarshal(m, b) +} +func (m *EnumDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnumDescriptorProto.Marshal(b, m, deterministic) +} +func (m *EnumDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumDescriptorProto.Merge(m, src) +} +func (m *EnumDescriptorProto) XXX_Size() int { + return xxx_messageInfo_EnumDescriptorProto.Size(m) +} +func (m *EnumDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_EnumDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_EnumDescriptorProto proto.InternalMessageInfo func (m *EnumDescriptorProto) GetName() string { if m != nil && m.Name != nil { @@ -865,18 +1048,37 @@ func (m *EnumDescriptorProto) GetReservedName() []string { // is inclusive such that it can appropriately represent the entire int32 // domain. type EnumDescriptorProto_EnumReservedRange struct { - Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` - End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` - XXX_unrecognized []byte `json:"-"` + Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` + End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *EnumDescriptorProto_EnumReservedRange) Reset() { *m = EnumDescriptorProto_EnumReservedRange{} } func (m *EnumDescriptorProto_EnumReservedRange) String() string { return proto.CompactTextString(m) } func (*EnumDescriptorProto_EnumReservedRange) ProtoMessage() {} func (*EnumDescriptorProto_EnumReservedRange) Descriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{6, 0} + return fileDescriptor_308767df5ffe18af, []int{6, 0} +} +func (m *EnumDescriptorProto_EnumReservedRange) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Unmarshal(m, b) +} +func (m *EnumDescriptorProto_EnumReservedRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Marshal(b, m, deterministic) +} +func (m *EnumDescriptorProto_EnumReservedRange) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Merge(m, src) +} +func (m *EnumDescriptorProto_EnumReservedRange) XXX_Size() int { + return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Size(m) +} +func (m *EnumDescriptorProto_EnumReservedRange) XXX_DiscardUnknown() { + xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.DiscardUnknown(m) } +var xxx_messageInfo_EnumDescriptorProto_EnumReservedRange proto.InternalMessageInfo + func (m *EnumDescriptorProto_EnumReservedRange) GetStart() int32 { if m != nil && m.Start != nil { return *m.Start @@ -893,19 +1095,38 @@ func (m *EnumDescriptorProto_EnumReservedRange) GetEnd() int32 { // Describes a value within an enum. type EnumValueDescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Number *int32 `protobuf:"varint,2,opt,name=number" json:"number,omitempty"` - Options *EnumValueOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` - XXX_unrecognized []byte `json:"-"` + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Number *int32 `protobuf:"varint,2,opt,name=number" json:"number,omitempty"` + Options *EnumValueOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *EnumValueDescriptorProto) Reset() { *m = EnumValueDescriptorProto{} } func (m *EnumValueDescriptorProto) String() string { return proto.CompactTextString(m) } func (*EnumValueDescriptorProto) ProtoMessage() {} func (*EnumValueDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{7} + return fileDescriptor_308767df5ffe18af, []int{7} +} +func (m *EnumValueDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnumValueDescriptorProto.Unmarshal(m, b) +} +func (m *EnumValueDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnumValueDescriptorProto.Marshal(b, m, deterministic) +} +func (m *EnumValueDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumValueDescriptorProto.Merge(m, src) +} +func (m *EnumValueDescriptorProto) XXX_Size() int { + return xxx_messageInfo_EnumValueDescriptorProto.Size(m) +} +func (m *EnumValueDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_EnumValueDescriptorProto.DiscardUnknown(m) } +var xxx_messageInfo_EnumValueDescriptorProto proto.InternalMessageInfo + func (m *EnumValueDescriptorProto) GetName() string { if m != nil && m.Name != nil { return *m.Name @@ -929,16 +1150,37 @@ func (m *EnumValueDescriptorProto) GetOptions() *EnumValueOptions { // Describes a service. type ServiceDescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Method []*MethodDescriptorProto `protobuf:"bytes,2,rep,name=method" json:"method,omitempty"` - Options *ServiceOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` - XXX_unrecognized []byte `json:"-"` + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Method []*MethodDescriptorProto `protobuf:"bytes,2,rep,name=method" json:"method,omitempty"` + Options *ServiceOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ServiceDescriptorProto) Reset() { *m = ServiceDescriptorProto{} } -func (m *ServiceDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*ServiceDescriptorProto) ProtoMessage() {} -func (*ServiceDescriptorProto) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{8} } +func (m *ServiceDescriptorProto) Reset() { *m = ServiceDescriptorProto{} } +func (m *ServiceDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*ServiceDescriptorProto) ProtoMessage() {} +func (*ServiceDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{8} +} +func (m *ServiceDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ServiceDescriptorProto.Unmarshal(m, b) +} +func (m *ServiceDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ServiceDescriptorProto.Marshal(b, m, deterministic) +} +func (m *ServiceDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServiceDescriptorProto.Merge(m, src) +} +func (m *ServiceDescriptorProto) XXX_Size() int { + return xxx_messageInfo_ServiceDescriptorProto.Size(m) +} +func (m *ServiceDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_ServiceDescriptorProto.DiscardUnknown(m) +} + +var xxx_messageInfo_ServiceDescriptorProto proto.InternalMessageInfo func (m *ServiceDescriptorProto) GetName() string { if m != nil && m.Name != nil { @@ -972,14 +1214,35 @@ type MethodDescriptorProto struct { // Identifies if client streams multiple client messages ClientStreaming *bool `protobuf:"varint,5,opt,name=client_streaming,json=clientStreaming,def=0" json:"client_streaming,omitempty"` // Identifies if server streams multiple server messages - ServerStreaming *bool `protobuf:"varint,6,opt,name=server_streaming,json=serverStreaming,def=0" json:"server_streaming,omitempty"` - XXX_unrecognized []byte `json:"-"` + ServerStreaming *bool `protobuf:"varint,6,opt,name=server_streaming,json=serverStreaming,def=0" json:"server_streaming,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *MethodDescriptorProto) Reset() { *m = MethodDescriptorProto{} } +func (m *MethodDescriptorProto) String() string { return proto.CompactTextString(m) } +func (*MethodDescriptorProto) ProtoMessage() {} +func (*MethodDescriptorProto) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{9} +} +func (m *MethodDescriptorProto) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MethodDescriptorProto.Unmarshal(m, b) +} +func (m *MethodDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MethodDescriptorProto.Marshal(b, m, deterministic) +} +func (m *MethodDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_MethodDescriptorProto.Merge(m, src) +} +func (m *MethodDescriptorProto) XXX_Size() int { + return xxx_messageInfo_MethodDescriptorProto.Size(m) +} +func (m *MethodDescriptorProto) XXX_DiscardUnknown() { + xxx_messageInfo_MethodDescriptorProto.DiscardUnknown(m) } -func (m *MethodDescriptorProto) Reset() { *m = MethodDescriptorProto{} } -func (m *MethodDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*MethodDescriptorProto) ProtoMessage() {} -func (*MethodDescriptorProto) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{9} } +var xxx_messageInfo_MethodDescriptorProto proto.InternalMessageInfo const Default_MethodDescriptorProto_ClientStreaming bool = false const Default_MethodDescriptorProto_ServerStreaming bool = false @@ -1046,7 +1309,7 @@ type FileOptions struct { // top-level extensions defined in the file. JavaMultipleFiles *bool `protobuf:"varint,10,opt,name=java_multiple_files,json=javaMultipleFiles,def=0" json:"java_multiple_files,omitempty"` // This option does nothing. - JavaGenerateEqualsAndHash *bool `protobuf:"varint,20,opt,name=java_generate_equals_and_hash,json=javaGenerateEqualsAndHash" json:"java_generate_equals_and_hash,omitempty"` + JavaGenerateEqualsAndHash *bool `protobuf:"varint,20,opt,name=java_generate_equals_and_hash,json=javaGenerateEqualsAndHash" json:"java_generate_equals_and_hash,omitempty"` // Deprecated: Do not use. // If set true, then the Java2 code generator will generate code that // throws an exception whenever an attempt is made to assign a non-UTF-8 // byte sequence to a string field. @@ -1100,16 +1363,29 @@ type FileOptions struct { // is empty. When this option is empty, the package name will be used for // determining the namespace. PhpNamespace *string `protobuf:"bytes,41,opt,name=php_namespace,json=phpNamespace" json:"php_namespace,omitempty"` - // The parser stores options it doesn't recognize here. See above. + // Use this option to change the namespace of php generated metadata classes. + // Default is empty. When this option is empty, the proto file name will be + // used for determining the namespace. + PhpMetadataNamespace *string `protobuf:"bytes,44,opt,name=php_metadata_namespace,json=phpMetadataNamespace" json:"php_metadata_namespace,omitempty"` + // Use this option to change the package of ruby generated classes. Default + // is empty. When this option is not set, the package name will be used for + // determining the ruby package. + RubyPackage *string `protobuf:"bytes,45,opt,name=ruby_package,json=rubyPackage" json:"ruby_package,omitempty"` + // The parser stores options it doesn't recognize here. + // See the documentation for the "Options" section above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` proto.XXX_InternalExtensions `json:"-"` XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *FileOptions) Reset() { *m = FileOptions{} } -func (m *FileOptions) String() string { return proto.CompactTextString(m) } -func (*FileOptions) ProtoMessage() {} -func (*FileOptions) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{10} } +func (m *FileOptions) Reset() { *m = FileOptions{} } +func (m *FileOptions) String() string { return proto.CompactTextString(m) } +func (*FileOptions) ProtoMessage() {} +func (*FileOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{10} +} var extRange_FileOptions = []proto.ExtensionRange{ {Start: 1000, End: 536870911}, @@ -1119,6 +1395,24 @@ func (*FileOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_FileOptions } +func (m *FileOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FileOptions.Unmarshal(m, b) +} +func (m *FileOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FileOptions.Marshal(b, m, deterministic) +} +func (m *FileOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_FileOptions.Merge(m, src) +} +func (m *FileOptions) XXX_Size() int { + return xxx_messageInfo_FileOptions.Size(m) +} +func (m *FileOptions) XXX_DiscardUnknown() { + xxx_messageInfo_FileOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_FileOptions proto.InternalMessageInfo + const Default_FileOptions_JavaMultipleFiles bool = false const Default_FileOptions_JavaStringCheckUtf8 bool = false const Default_FileOptions_OptimizeFor FileOptions_OptimizeMode = FileOptions_SPEED @@ -1150,6 +1444,7 @@ func (m *FileOptions) GetJavaMultipleFiles() bool { return Default_FileOptions_JavaMultipleFiles } +// Deprecated: Do not use. func (m *FileOptions) GetJavaGenerateEqualsAndHash() bool { if m != nil && m.JavaGenerateEqualsAndHash != nil { return *m.JavaGenerateEqualsAndHash @@ -1255,6 +1550,20 @@ func (m *FileOptions) GetPhpNamespace() string { return "" } +func (m *FileOptions) GetPhpMetadataNamespace() string { + if m != nil && m.PhpMetadataNamespace != nil { + return *m.PhpMetadataNamespace + } + return "" +} + +func (m *FileOptions) GetRubyPackage() string { + if m != nil && m.RubyPackage != nil { + return *m.RubyPackage + } + return "" +} + func (m *FileOptions) GetUninterpretedOption() []*UninterpretedOption { if m != nil { return m.UninterpretedOption @@ -1306,7 +1615,7 @@ type MessageOptions struct { // // Implementations may choose not to generate the map_entry=true message, but // use a native map in the target language to hold the keys and values. - // The reflection APIs in such implementions still need to work as + // The reflection APIs in such implementations still need to work as // if the field is a repeated message field. // // NOTE: Do not set the option in .proto files. Always use the maps syntax @@ -1315,14 +1624,18 @@ type MessageOptions struct { MapEntry *bool `protobuf:"varint,7,opt,name=map_entry,json=mapEntry" json:"map_entry,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` proto.XXX_InternalExtensions `json:"-"` XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *MessageOptions) Reset() { *m = MessageOptions{} } -func (m *MessageOptions) String() string { return proto.CompactTextString(m) } -func (*MessageOptions) ProtoMessage() {} -func (*MessageOptions) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{11} } +func (m *MessageOptions) Reset() { *m = MessageOptions{} } +func (m *MessageOptions) String() string { return proto.CompactTextString(m) } +func (*MessageOptions) ProtoMessage() {} +func (*MessageOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{11} +} var extRange_MessageOptions = []proto.ExtensionRange{ {Start: 1000, End: 536870911}, @@ -1332,6 +1645,24 @@ func (*MessageOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_MessageOptions } +func (m *MessageOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MessageOptions.Unmarshal(m, b) +} +func (m *MessageOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MessageOptions.Marshal(b, m, deterministic) +} +func (m *MessageOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_MessageOptions.Merge(m, src) +} +func (m *MessageOptions) XXX_Size() int { + return xxx_messageInfo_MessageOptions.Size(m) +} +func (m *MessageOptions) XXX_DiscardUnknown() { + xxx_messageInfo_MessageOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_MessageOptions proto.InternalMessageInfo + const Default_MessageOptions_MessageSetWireFormat bool = false const Default_MessageOptions_NoStandardDescriptorAccessor bool = false const Default_MessageOptions_Deprecated bool = false @@ -1433,14 +1764,18 @@ type FieldOptions struct { Weak *bool `protobuf:"varint,10,opt,name=weak,def=0" json:"weak,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` proto.XXX_InternalExtensions `json:"-"` XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *FieldOptions) Reset() { *m = FieldOptions{} } -func (m *FieldOptions) String() string { return proto.CompactTextString(m) } -func (*FieldOptions) ProtoMessage() {} -func (*FieldOptions) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{12} } +func (m *FieldOptions) Reset() { *m = FieldOptions{} } +func (m *FieldOptions) String() string { return proto.CompactTextString(m) } +func (*FieldOptions) ProtoMessage() {} +func (*FieldOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{12} +} var extRange_FieldOptions = []proto.ExtensionRange{ {Start: 1000, End: 536870911}, @@ -1450,6 +1785,24 @@ func (*FieldOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_FieldOptions } +func (m *FieldOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FieldOptions.Unmarshal(m, b) +} +func (m *FieldOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FieldOptions.Marshal(b, m, deterministic) +} +func (m *FieldOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_FieldOptions.Merge(m, src) +} +func (m *FieldOptions) XXX_Size() int { + return xxx_messageInfo_FieldOptions.Size(m) +} +func (m *FieldOptions) XXX_DiscardUnknown() { + xxx_messageInfo_FieldOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_FieldOptions proto.InternalMessageInfo + const Default_FieldOptions_Ctype FieldOptions_CType = FieldOptions_STRING const Default_FieldOptions_Jstype FieldOptions_JSType = FieldOptions_JS_NORMAL const Default_FieldOptions_Lazy bool = false @@ -1508,14 +1861,18 @@ func (m *FieldOptions) GetUninterpretedOption() []*UninterpretedOption { type OneofOptions struct { // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` proto.XXX_InternalExtensions `json:"-"` XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *OneofOptions) Reset() { *m = OneofOptions{} } -func (m *OneofOptions) String() string { return proto.CompactTextString(m) } -func (*OneofOptions) ProtoMessage() {} -func (*OneofOptions) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{13} } +func (m *OneofOptions) Reset() { *m = OneofOptions{} } +func (m *OneofOptions) String() string { return proto.CompactTextString(m) } +func (*OneofOptions) ProtoMessage() {} +func (*OneofOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{13} +} var extRange_OneofOptions = []proto.ExtensionRange{ {Start: 1000, End: 536870911}, @@ -1525,6 +1882,24 @@ func (*OneofOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_OneofOptions } +func (m *OneofOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OneofOptions.Unmarshal(m, b) +} +func (m *OneofOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OneofOptions.Marshal(b, m, deterministic) +} +func (m *OneofOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_OneofOptions.Merge(m, src) +} +func (m *OneofOptions) XXX_Size() int { + return xxx_messageInfo_OneofOptions.Size(m) +} +func (m *OneofOptions) XXX_DiscardUnknown() { + xxx_messageInfo_OneofOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_OneofOptions proto.InternalMessageInfo + func (m *OneofOptions) GetUninterpretedOption() []*UninterpretedOption { if m != nil { return m.UninterpretedOption @@ -1543,14 +1918,18 @@ type EnumOptions struct { Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0" json:"deprecated,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` proto.XXX_InternalExtensions `json:"-"` XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *EnumOptions) Reset() { *m = EnumOptions{} } -func (m *EnumOptions) String() string { return proto.CompactTextString(m) } -func (*EnumOptions) ProtoMessage() {} -func (*EnumOptions) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{14} } +func (m *EnumOptions) Reset() { *m = EnumOptions{} } +func (m *EnumOptions) String() string { return proto.CompactTextString(m) } +func (*EnumOptions) ProtoMessage() {} +func (*EnumOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{14} +} var extRange_EnumOptions = []proto.ExtensionRange{ {Start: 1000, End: 536870911}, @@ -1560,6 +1939,24 @@ func (*EnumOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_EnumOptions } +func (m *EnumOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnumOptions.Unmarshal(m, b) +} +func (m *EnumOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnumOptions.Marshal(b, m, deterministic) +} +func (m *EnumOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumOptions.Merge(m, src) +} +func (m *EnumOptions) XXX_Size() int { + return xxx_messageInfo_EnumOptions.Size(m) +} +func (m *EnumOptions) XXX_DiscardUnknown() { + xxx_messageInfo_EnumOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_EnumOptions proto.InternalMessageInfo + const Default_EnumOptions_Deprecated bool = false func (m *EnumOptions) GetAllowAlias() bool { @@ -1591,14 +1988,18 @@ type EnumValueOptions struct { Deprecated *bool `protobuf:"varint,1,opt,name=deprecated,def=0" json:"deprecated,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` proto.XXX_InternalExtensions `json:"-"` XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *EnumValueOptions) Reset() { *m = EnumValueOptions{} } -func (m *EnumValueOptions) String() string { return proto.CompactTextString(m) } -func (*EnumValueOptions) ProtoMessage() {} -func (*EnumValueOptions) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{15} } +func (m *EnumValueOptions) Reset() { *m = EnumValueOptions{} } +func (m *EnumValueOptions) String() string { return proto.CompactTextString(m) } +func (*EnumValueOptions) ProtoMessage() {} +func (*EnumValueOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{15} +} var extRange_EnumValueOptions = []proto.ExtensionRange{ {Start: 1000, End: 536870911}, @@ -1608,6 +2009,24 @@ func (*EnumValueOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_EnumValueOptions } +func (m *EnumValueOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EnumValueOptions.Unmarshal(m, b) +} +func (m *EnumValueOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EnumValueOptions.Marshal(b, m, deterministic) +} +func (m *EnumValueOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumValueOptions.Merge(m, src) +} +func (m *EnumValueOptions) XXX_Size() int { + return xxx_messageInfo_EnumValueOptions.Size(m) +} +func (m *EnumValueOptions) XXX_DiscardUnknown() { + xxx_messageInfo_EnumValueOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_EnumValueOptions proto.InternalMessageInfo + const Default_EnumValueOptions_Deprecated bool = false func (m *EnumValueOptions) GetDeprecated() bool { @@ -1632,14 +2051,18 @@ type ServiceOptions struct { Deprecated *bool `protobuf:"varint,33,opt,name=deprecated,def=0" json:"deprecated,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` proto.XXX_InternalExtensions `json:"-"` XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ServiceOptions) Reset() { *m = ServiceOptions{} } -func (m *ServiceOptions) String() string { return proto.CompactTextString(m) } -func (*ServiceOptions) ProtoMessage() {} -func (*ServiceOptions) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{16} } +func (m *ServiceOptions) Reset() { *m = ServiceOptions{} } +func (m *ServiceOptions) String() string { return proto.CompactTextString(m) } +func (*ServiceOptions) ProtoMessage() {} +func (*ServiceOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{16} +} var extRange_ServiceOptions = []proto.ExtensionRange{ {Start: 1000, End: 536870911}, @@ -1649,6 +2072,24 @@ func (*ServiceOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_ServiceOptions } +func (m *ServiceOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ServiceOptions.Unmarshal(m, b) +} +func (m *ServiceOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ServiceOptions.Marshal(b, m, deterministic) +} +func (m *ServiceOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServiceOptions.Merge(m, src) +} +func (m *ServiceOptions) XXX_Size() int { + return xxx_messageInfo_ServiceOptions.Size(m) +} +func (m *ServiceOptions) XXX_DiscardUnknown() { + xxx_messageInfo_ServiceOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_ServiceOptions proto.InternalMessageInfo + const Default_ServiceOptions_Deprecated bool = false func (m *ServiceOptions) GetDeprecated() bool { @@ -1674,14 +2115,18 @@ type MethodOptions struct { IdempotencyLevel *MethodOptions_IdempotencyLevel `protobuf:"varint,34,opt,name=idempotency_level,json=idempotencyLevel,enum=google.protobuf.MethodOptions_IdempotencyLevel,def=0" json:"idempotency_level,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` proto.XXX_InternalExtensions `json:"-"` XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *MethodOptions) Reset() { *m = MethodOptions{} } -func (m *MethodOptions) String() string { return proto.CompactTextString(m) } -func (*MethodOptions) ProtoMessage() {} -func (*MethodOptions) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{17} } +func (m *MethodOptions) Reset() { *m = MethodOptions{} } +func (m *MethodOptions) String() string { return proto.CompactTextString(m) } +func (*MethodOptions) ProtoMessage() {} +func (*MethodOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{17} +} var extRange_MethodOptions = []proto.ExtensionRange{ {Start: 1000, End: 536870911}, @@ -1691,6 +2136,24 @@ func (*MethodOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_MethodOptions } +func (m *MethodOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MethodOptions.Unmarshal(m, b) +} +func (m *MethodOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MethodOptions.Marshal(b, m, deterministic) +} +func (m *MethodOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_MethodOptions.Merge(m, src) +} +func (m *MethodOptions) XXX_Size() int { + return xxx_messageInfo_MethodOptions.Size(m) +} +func (m *MethodOptions) XXX_DiscardUnknown() { + xxx_messageInfo_MethodOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_MethodOptions proto.InternalMessageInfo + const Default_MethodOptions_Deprecated bool = false const Default_MethodOptions_IdempotencyLevel MethodOptions_IdempotencyLevel = MethodOptions_IDEMPOTENCY_UNKNOWN @@ -1725,19 +2188,40 @@ type UninterpretedOption struct { Name []*UninterpretedOption_NamePart `protobuf:"bytes,2,rep,name=name" json:"name,omitempty"` // The value of the uninterpreted option, in whatever type the tokenizer // identified it as during parsing. Exactly one of these should be set. - IdentifierValue *string `protobuf:"bytes,3,opt,name=identifier_value,json=identifierValue" json:"identifier_value,omitempty"` - PositiveIntValue *uint64 `protobuf:"varint,4,opt,name=positive_int_value,json=positiveIntValue" json:"positive_int_value,omitempty"` - NegativeIntValue *int64 `protobuf:"varint,5,opt,name=negative_int_value,json=negativeIntValue" json:"negative_int_value,omitempty"` - DoubleValue *float64 `protobuf:"fixed64,6,opt,name=double_value,json=doubleValue" json:"double_value,omitempty"` - StringValue []byte `protobuf:"bytes,7,opt,name=string_value,json=stringValue" json:"string_value,omitempty"` - AggregateValue *string `protobuf:"bytes,8,opt,name=aggregate_value,json=aggregateValue" json:"aggregate_value,omitempty"` - XXX_unrecognized []byte `json:"-"` + IdentifierValue *string `protobuf:"bytes,3,opt,name=identifier_value,json=identifierValue" json:"identifier_value,omitempty"` + PositiveIntValue *uint64 `protobuf:"varint,4,opt,name=positive_int_value,json=positiveIntValue" json:"positive_int_value,omitempty"` + NegativeIntValue *int64 `protobuf:"varint,5,opt,name=negative_int_value,json=negativeIntValue" json:"negative_int_value,omitempty"` + DoubleValue *float64 `protobuf:"fixed64,6,opt,name=double_value,json=doubleValue" json:"double_value,omitempty"` + StringValue []byte `protobuf:"bytes,7,opt,name=string_value,json=stringValue" json:"string_value,omitempty"` + AggregateValue *string `protobuf:"bytes,8,opt,name=aggregate_value,json=aggregateValue" json:"aggregate_value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UninterpretedOption) Reset() { *m = UninterpretedOption{} } +func (m *UninterpretedOption) String() string { return proto.CompactTextString(m) } +func (*UninterpretedOption) ProtoMessage() {} +func (*UninterpretedOption) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{18} +} +func (m *UninterpretedOption) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_UninterpretedOption.Unmarshal(m, b) +} +func (m *UninterpretedOption) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_UninterpretedOption.Marshal(b, m, deterministic) +} +func (m *UninterpretedOption) XXX_Merge(src proto.Message) { + xxx_messageInfo_UninterpretedOption.Merge(m, src) +} +func (m *UninterpretedOption) XXX_Size() int { + return xxx_messageInfo_UninterpretedOption.Size(m) +} +func (m *UninterpretedOption) XXX_DiscardUnknown() { + xxx_messageInfo_UninterpretedOption.DiscardUnknown(m) } -func (m *UninterpretedOption) Reset() { *m = UninterpretedOption{} } -func (m *UninterpretedOption) String() string { return proto.CompactTextString(m) } -func (*UninterpretedOption) ProtoMessage() {} -func (*UninterpretedOption) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{18} } +var xxx_messageInfo_UninterpretedOption proto.InternalMessageInfo func (m *UninterpretedOption) GetName() []*UninterpretedOption_NamePart { if m != nil { @@ -1794,17 +2278,36 @@ func (m *UninterpretedOption) GetAggregateValue() string { // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents // "foo.(bar.baz).qux". type UninterpretedOption_NamePart struct { - NamePart *string `protobuf:"bytes,1,req,name=name_part,json=namePart" json:"name_part,omitempty"` - IsExtension *bool `protobuf:"varint,2,req,name=is_extension,json=isExtension" json:"is_extension,omitempty"` - XXX_unrecognized []byte `json:"-"` + NamePart *string `protobuf:"bytes,1,req,name=name_part,json=namePart" json:"name_part,omitempty"` + IsExtension *bool `protobuf:"varint,2,req,name=is_extension,json=isExtension" json:"is_extension,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *UninterpretedOption_NamePart) Reset() { *m = UninterpretedOption_NamePart{} } func (m *UninterpretedOption_NamePart) String() string { return proto.CompactTextString(m) } func (*UninterpretedOption_NamePart) ProtoMessage() {} func (*UninterpretedOption_NamePart) Descriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{18, 0} + return fileDescriptor_308767df5ffe18af, []int{18, 0} } +func (m *UninterpretedOption_NamePart) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_UninterpretedOption_NamePart.Unmarshal(m, b) +} +func (m *UninterpretedOption_NamePart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_UninterpretedOption_NamePart.Marshal(b, m, deterministic) +} +func (m *UninterpretedOption_NamePart) XXX_Merge(src proto.Message) { + xxx_messageInfo_UninterpretedOption_NamePart.Merge(m, src) +} +func (m *UninterpretedOption_NamePart) XXX_Size() int { + return xxx_messageInfo_UninterpretedOption_NamePart.Size(m) +} +func (m *UninterpretedOption_NamePart) XXX_DiscardUnknown() { + xxx_messageInfo_UninterpretedOption_NamePart.DiscardUnknown(m) +} + +var xxx_messageInfo_UninterpretedOption_NamePart proto.InternalMessageInfo func (m *UninterpretedOption_NamePart) GetNamePart() string { if m != nil && m.NamePart != nil { @@ -1860,20 +2363,41 @@ type SourceCodeInfo struct { // beginning of the "extend" block and is shared by all extensions within // the block. // - Just because a location's span is a subset of some other location's span - // does not mean that it is a descendent. For example, a "group" defines + // does not mean that it is a descendant. For example, a "group" defines // both a type and a field in a single declaration. Thus, the locations // corresponding to the type and field and their components will overlap. // - Code which tries to interpret locations should probably be designed to // ignore those that it doesn't understand, as more types of locations could // be recorded in the future. - Location []*SourceCodeInfo_Location `protobuf:"bytes,1,rep,name=location" json:"location,omitempty"` - XXX_unrecognized []byte `json:"-"` + Location []*SourceCodeInfo_Location `protobuf:"bytes,1,rep,name=location" json:"location,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *SourceCodeInfo) Reset() { *m = SourceCodeInfo{} } -func (m *SourceCodeInfo) String() string { return proto.CompactTextString(m) } -func (*SourceCodeInfo) ProtoMessage() {} -func (*SourceCodeInfo) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{19} } +func (m *SourceCodeInfo) Reset() { *m = SourceCodeInfo{} } +func (m *SourceCodeInfo) String() string { return proto.CompactTextString(m) } +func (*SourceCodeInfo) ProtoMessage() {} +func (*SourceCodeInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{19} +} +func (m *SourceCodeInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SourceCodeInfo.Unmarshal(m, b) +} +func (m *SourceCodeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SourceCodeInfo.Marshal(b, m, deterministic) +} +func (m *SourceCodeInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_SourceCodeInfo.Merge(m, src) +} +func (m *SourceCodeInfo) XXX_Size() int { + return xxx_messageInfo_SourceCodeInfo.Size(m) +} +func (m *SourceCodeInfo) XXX_DiscardUnknown() { + xxx_messageInfo_SourceCodeInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_SourceCodeInfo proto.InternalMessageInfo func (m *SourceCodeInfo) GetLocation() []*SourceCodeInfo_Location { if m != nil { @@ -1963,15 +2487,34 @@ type SourceCodeInfo_Location struct { LeadingComments *string `protobuf:"bytes,3,opt,name=leading_comments,json=leadingComments" json:"leading_comments,omitempty"` TrailingComments *string `protobuf:"bytes,4,opt,name=trailing_comments,json=trailingComments" json:"trailing_comments,omitempty"` LeadingDetachedComments []string `protobuf:"bytes,6,rep,name=leading_detached_comments,json=leadingDetachedComments" json:"leading_detached_comments,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *SourceCodeInfo_Location) Reset() { *m = SourceCodeInfo_Location{} } func (m *SourceCodeInfo_Location) String() string { return proto.CompactTextString(m) } func (*SourceCodeInfo_Location) ProtoMessage() {} func (*SourceCodeInfo_Location) Descriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{19, 0} + return fileDescriptor_308767df5ffe18af, []int{19, 0} +} +func (m *SourceCodeInfo_Location) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SourceCodeInfo_Location.Unmarshal(m, b) +} +func (m *SourceCodeInfo_Location) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SourceCodeInfo_Location.Marshal(b, m, deterministic) } +func (m *SourceCodeInfo_Location) XXX_Merge(src proto.Message) { + xxx_messageInfo_SourceCodeInfo_Location.Merge(m, src) +} +func (m *SourceCodeInfo_Location) XXX_Size() int { + return xxx_messageInfo_SourceCodeInfo_Location.Size(m) +} +func (m *SourceCodeInfo_Location) XXX_DiscardUnknown() { + xxx_messageInfo_SourceCodeInfo_Location.DiscardUnknown(m) +} + +var xxx_messageInfo_SourceCodeInfo_Location proto.InternalMessageInfo func (m *SourceCodeInfo_Location) GetPath() []int32 { if m != nil { @@ -2014,14 +2557,35 @@ func (m *SourceCodeInfo_Location) GetLeadingDetachedComments() []string { type GeneratedCodeInfo struct { // An Annotation connects some span of text in generated code to an element // of its generating .proto file. - Annotation []*GeneratedCodeInfo_Annotation `protobuf:"bytes,1,rep,name=annotation" json:"annotation,omitempty"` - XXX_unrecognized []byte `json:"-"` + Annotation []*GeneratedCodeInfo_Annotation `protobuf:"bytes,1,rep,name=annotation" json:"annotation,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *GeneratedCodeInfo) Reset() { *m = GeneratedCodeInfo{} } -func (m *GeneratedCodeInfo) String() string { return proto.CompactTextString(m) } -func (*GeneratedCodeInfo) ProtoMessage() {} -func (*GeneratedCodeInfo) Descriptor() ([]byte, []int) { return fileDescriptorDescriptor, []int{20} } +func (m *GeneratedCodeInfo) Reset() { *m = GeneratedCodeInfo{} } +func (m *GeneratedCodeInfo) String() string { return proto.CompactTextString(m) } +func (*GeneratedCodeInfo) ProtoMessage() {} +func (*GeneratedCodeInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_308767df5ffe18af, []int{20} +} +func (m *GeneratedCodeInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GeneratedCodeInfo.Unmarshal(m, b) +} +func (m *GeneratedCodeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GeneratedCodeInfo.Marshal(b, m, deterministic) +} +func (m *GeneratedCodeInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_GeneratedCodeInfo.Merge(m, src) +} +func (m *GeneratedCodeInfo) XXX_Size() int { + return xxx_messageInfo_GeneratedCodeInfo.Size(m) +} +func (m *GeneratedCodeInfo) XXX_DiscardUnknown() { + xxx_messageInfo_GeneratedCodeInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_GeneratedCodeInfo proto.InternalMessageInfo func (m *GeneratedCodeInfo) GetAnnotation() []*GeneratedCodeInfo_Annotation { if m != nil { @@ -2042,16 +2606,35 @@ type GeneratedCodeInfo_Annotation struct { // Identifies the ending offset in bytes in the generated code that // relates to the identified offset. The end offset should be one past // the last relevant byte (so the length of the text = end - begin). - End *int32 `protobuf:"varint,4,opt,name=end" json:"end,omitempty"` - XXX_unrecognized []byte `json:"-"` + End *int32 `protobuf:"varint,4,opt,name=end" json:"end,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *GeneratedCodeInfo_Annotation) Reset() { *m = GeneratedCodeInfo_Annotation{} } func (m *GeneratedCodeInfo_Annotation) String() string { return proto.CompactTextString(m) } func (*GeneratedCodeInfo_Annotation) ProtoMessage() {} func (*GeneratedCodeInfo_Annotation) Descriptor() ([]byte, []int) { - return fileDescriptorDescriptor, []int{20, 0} + return fileDescriptor_308767df5ffe18af, []int{20, 0} +} +func (m *GeneratedCodeInfo_Annotation) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GeneratedCodeInfo_Annotation.Unmarshal(m, b) +} +func (m *GeneratedCodeInfo_Annotation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GeneratedCodeInfo_Annotation.Marshal(b, m, deterministic) +} +func (m *GeneratedCodeInfo_Annotation) XXX_Merge(src proto.Message) { + xxx_messageInfo_GeneratedCodeInfo_Annotation.Merge(m, src) +} +func (m *GeneratedCodeInfo_Annotation) XXX_Size() int { + return xxx_messageInfo_GeneratedCodeInfo_Annotation.Size(m) } +func (m *GeneratedCodeInfo_Annotation) XXX_DiscardUnknown() { + xxx_messageInfo_GeneratedCodeInfo_Annotation.DiscardUnknown(m) +} + +var xxx_messageInfo_GeneratedCodeInfo_Annotation proto.InternalMessageInfo func (m *GeneratedCodeInfo_Annotation) GetPath() []int32 { if m != nil { @@ -2082,6 +2665,12 @@ func (m *GeneratedCodeInfo_Annotation) GetEnd() int32 { } func init() { + proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Type", FieldDescriptorProto_Type_name, FieldDescriptorProto_Type_value) + proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Label", FieldDescriptorProto_Label_name, FieldDescriptorProto_Label_value) + proto.RegisterEnum("google.protobuf.FileOptions_OptimizeMode", FileOptions_OptimizeMode_name, FileOptions_OptimizeMode_value) + proto.RegisterEnum("google.protobuf.FieldOptions_CType", FieldOptions_CType_name, FieldOptions_CType_value) + proto.RegisterEnum("google.protobuf.FieldOptions_JSType", FieldOptions_JSType_name, FieldOptions_JSType_value) + proto.RegisterEnum("google.protobuf.MethodOptions_IdempotencyLevel", MethodOptions_IdempotencyLevel_name, MethodOptions_IdempotencyLevel_value) proto.RegisterType((*FileDescriptorSet)(nil), "google.protobuf.FileDescriptorSet") proto.RegisterType((*FileDescriptorProto)(nil), "google.protobuf.FileDescriptorProto") proto.RegisterType((*DescriptorProto)(nil), "google.protobuf.DescriptorProto") @@ -2109,172 +2698,168 @@ func init() { proto.RegisterType((*SourceCodeInfo_Location)(nil), "google.protobuf.SourceCodeInfo.Location") proto.RegisterType((*GeneratedCodeInfo)(nil), "google.protobuf.GeneratedCodeInfo") proto.RegisterType((*GeneratedCodeInfo_Annotation)(nil), "google.protobuf.GeneratedCodeInfo.Annotation") - proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Type", FieldDescriptorProto_Type_name, FieldDescriptorProto_Type_value) - proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Label", FieldDescriptorProto_Label_name, FieldDescriptorProto_Label_value) - proto.RegisterEnum("google.protobuf.FileOptions_OptimizeMode", FileOptions_OptimizeMode_name, FileOptions_OptimizeMode_value) - proto.RegisterEnum("google.protobuf.FieldOptions_CType", FieldOptions_CType_name, FieldOptions_CType_value) - proto.RegisterEnum("google.protobuf.FieldOptions_JSType", FieldOptions_JSType_name, FieldOptions_JSType_value) - proto.RegisterEnum("google.protobuf.MethodOptions_IdempotencyLevel", MethodOptions_IdempotencyLevel_name, MethodOptions_IdempotencyLevel_value) } -func init() { proto.RegisterFile("descriptor.proto", fileDescriptorDescriptor) } +func init() { proto.RegisterFile("descriptor.proto", fileDescriptor_308767df5ffe18af) } -var fileDescriptorDescriptor = []byte{ - // 2487 bytes of a gzipped FileDescriptorProto +var fileDescriptor_308767df5ffe18af = []byte{ + // 2522 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0xcd, 0x6f, 0xdb, 0xc8, - 0x15, 0x5f, 0x7d, 0x5a, 0x7a, 0x92, 0xe5, 0xf1, 0xd8, 0x9b, 0x30, 0xde, 0x8f, 0x38, 0xda, 0x8f, - 0x38, 0x49, 0xab, 0x2c, 0x9c, 0xc4, 0xc9, 0x3a, 0xc5, 0xb6, 0xb2, 0xc4, 0x78, 0x95, 0xca, 0x92, + 0x15, 0x5f, 0x7d, 0x5a, 0x7a, 0x92, 0x65, 0x7a, 0xec, 0x75, 0x18, 0xef, 0x47, 0x1c, 0xed, 0x66, + 0xe3, 0x24, 0xbb, 0xca, 0xc2, 0x49, 0x9c, 0xac, 0x53, 0x6c, 0x2b, 0x4b, 0x8c, 0x57, 0xa9, 0xbe, 0x4a, 0xc9, 0xdd, 0x64, 0x8b, 0x82, 0x18, 0x93, 0x23, 0x89, 0x09, 0x45, 0x72, 0x49, 0x2a, 0x89, - 0x83, 0x1e, 0x02, 0xf4, 0xd4, 0xff, 0xa0, 0x28, 0x8a, 0x1e, 0x7a, 0x59, 0xa0, 0xd7, 0x02, 0x05, - 0xda, 0x7b, 0xaf, 0x05, 0x7a, 0xef, 0xa1, 0x40, 0x0b, 0xb4, 0x7f, 0x42, 0x8f, 0xc5, 0xcc, 0x90, - 0x14, 0xf5, 0x95, 0x78, 0x17, 0x48, 0xf6, 0x64, 0xcf, 0xef, 0xfd, 0xde, 0xe3, 0x9b, 0x37, 0x6f, - 0xde, 0xbc, 0x19, 0x01, 0xd2, 0xa9, 0xa7, 0xb9, 0x86, 0xe3, 0xdb, 0x6e, 0xc5, 0x71, 0x6d, 0xdf, - 0xc6, 0x6b, 0x03, 0xdb, 0x1e, 0x98, 0x54, 0x8c, 0x4e, 0xc6, 0xfd, 0xf2, 0x11, 0xac, 0xdf, 0x33, - 0x4c, 0x5a, 0x8f, 0x88, 0x5d, 0xea, 0xe3, 0x3b, 0x90, 0xee, 0x1b, 0x26, 0x95, 0x12, 0xdb, 0xa9, - 0x9d, 0xc2, 0xee, 0x87, 0x95, 0x19, 0xa5, 0xca, 0xb4, 0x46, 0x87, 0xc1, 0x0a, 0xd7, 0x28, 0xff, - 0x3b, 0x0d, 0x1b, 0x0b, 0xa4, 0x18, 0x43, 0xda, 0x22, 0x23, 0x66, 0x31, 0xb1, 0x93, 0x57, 0xf8, - 0xff, 0x58, 0x82, 0x15, 0x87, 0x68, 0x8f, 0xc9, 0x80, 0x4a, 0x49, 0x0e, 0x87, 0x43, 0xfc, 0x3e, - 0x80, 0x4e, 0x1d, 0x6a, 0xe9, 0xd4, 0xd2, 0x4e, 0xa5, 0xd4, 0x76, 0x6a, 0x27, 0xaf, 0xc4, 0x10, - 0x7c, 0x0d, 0xd6, 0x9d, 0xf1, 0x89, 0x69, 0x68, 0x6a, 0x8c, 0x06, 0xdb, 0xa9, 0x9d, 0x8c, 0x82, - 0x84, 0xa0, 0x3e, 0x21, 0x5f, 0x86, 0xb5, 0xa7, 0x94, 0x3c, 0x8e, 0x53, 0x0b, 0x9c, 0x5a, 0x62, - 0x70, 0x8c, 0x58, 0x83, 0xe2, 0x88, 0x7a, 0x1e, 0x19, 0x50, 0xd5, 0x3f, 0x75, 0xa8, 0x94, 0xe6, - 0xb3, 0xdf, 0x9e, 0x9b, 0xfd, 0xec, 0xcc, 0x0b, 0x81, 0x56, 0xef, 0xd4, 0xa1, 0xb8, 0x0a, 0x79, - 0x6a, 0x8d, 0x47, 0xc2, 0x42, 0x66, 0x49, 0xfc, 0x64, 0x6b, 0x3c, 0x9a, 0xb5, 0x92, 0x63, 0x6a, - 0x81, 0x89, 0x15, 0x8f, 0xba, 0x4f, 0x0c, 0x8d, 0x4a, 0x59, 0x6e, 0xe0, 0xf2, 0x9c, 0x81, 0xae, - 0x90, 0xcf, 0xda, 0x08, 0xf5, 0x70, 0x0d, 0xf2, 0xf4, 0x99, 0x4f, 0x2d, 0xcf, 0xb0, 0x2d, 0x69, - 0x85, 0x1b, 0xf9, 0x68, 0xc1, 0x2a, 0x52, 0x53, 0x9f, 0x35, 0x31, 0xd1, 0xc3, 0x7b, 0xb0, 0x62, - 0x3b, 0xbe, 0x61, 0x5b, 0x9e, 0x94, 0xdb, 0x4e, 0xec, 0x14, 0x76, 0xdf, 0x5d, 0x98, 0x08, 0x6d, - 0xc1, 0x51, 0x42, 0x32, 0x6e, 0x00, 0xf2, 0xec, 0xb1, 0xab, 0x51, 0x55, 0xb3, 0x75, 0xaa, 0x1a, - 0x56, 0xdf, 0x96, 0xf2, 0xdc, 0xc0, 0xc5, 0xf9, 0x89, 0x70, 0x62, 0xcd, 0xd6, 0x69, 0xc3, 0xea, - 0xdb, 0x4a, 0xc9, 0x9b, 0x1a, 0xe3, 0x73, 0x90, 0xf5, 0x4e, 0x2d, 0x9f, 0x3c, 0x93, 0x8a, 0x3c, - 0x43, 0x82, 0x51, 0xf9, 0xcf, 0x59, 0x58, 0x3b, 0x4b, 0x8a, 0xdd, 0x85, 0x4c, 0x9f, 0xcd, 0x52, - 0x4a, 0x7e, 0x93, 0x18, 0x08, 0x9d, 0xe9, 0x20, 0x66, 0xbf, 0x65, 0x10, 0xab, 0x50, 0xb0, 0xa8, - 0xe7, 0x53, 0x5d, 0x64, 0x44, 0xea, 0x8c, 0x39, 0x05, 0x42, 0x69, 0x3e, 0xa5, 0xd2, 0xdf, 0x2a, - 0xa5, 0x1e, 0xc0, 0x5a, 0xe4, 0x92, 0xea, 0x12, 0x6b, 0x10, 0xe6, 0xe6, 0xf5, 0x57, 0x79, 0x52, - 0x91, 0x43, 0x3d, 0x85, 0xa9, 0x29, 0x25, 0x3a, 0x35, 0xc6, 0x75, 0x00, 0xdb, 0xa2, 0x76, 0x5f, - 0xd5, 0xa9, 0x66, 0x4a, 0xb9, 0x25, 0x51, 0x6a, 0x33, 0xca, 0x5c, 0x94, 0x6c, 0x81, 0x6a, 0x26, - 0xfe, 0x74, 0x92, 0x6a, 0x2b, 0x4b, 0x32, 0xe5, 0x48, 0x6c, 0xb2, 0xb9, 0x6c, 0x3b, 0x86, 0x92, - 0x4b, 0x59, 0xde, 0x53, 0x3d, 0x98, 0x59, 0x9e, 0x3b, 0x51, 0x79, 0xe5, 0xcc, 0x94, 0x40, 0x4d, - 0x4c, 0x6c, 0xd5, 0x8d, 0x0f, 0xf1, 0x07, 0x10, 0x01, 0x2a, 0x4f, 0x2b, 0xe0, 0x55, 0xa8, 0x18, - 0x82, 0x2d, 0x32, 0xa2, 0x5b, 0xcf, 0xa1, 0x34, 0x1d, 0x1e, 0xbc, 0x09, 0x19, 0xcf, 0x27, 0xae, - 0xcf, 0xb3, 0x30, 0xa3, 0x88, 0x01, 0x46, 0x90, 0xa2, 0x96, 0xce, 0xab, 0x5c, 0x46, 0x61, 0xff, - 0xe2, 0x1f, 0x4d, 0x26, 0x9c, 0xe2, 0x13, 0xfe, 0x78, 0x7e, 0x45, 0xa7, 0x2c, 0xcf, 0xce, 0x7b, - 0xeb, 0x36, 0xac, 0x4e, 0x4d, 0xe0, 0xac, 0x9f, 0x2e, 0xff, 0x02, 0xde, 0x5e, 0x68, 0x1a, 0x3f, - 0x80, 0xcd, 0xb1, 0x65, 0x58, 0x3e, 0x75, 0x1d, 0x97, 0xb2, 0x8c, 0x15, 0x9f, 0x92, 0xfe, 0xb3, - 0xb2, 0x24, 0xe7, 0x8e, 0xe3, 0x6c, 0x61, 0x45, 0xd9, 0x18, 0xcf, 0x83, 0x57, 0xf3, 0xb9, 0xff, - 0xae, 0xa0, 0x17, 0x2f, 0x5e, 0xbc, 0x48, 0x96, 0x7f, 0x9d, 0x85, 0xcd, 0x45, 0x7b, 0x66, 0xe1, - 0xf6, 0x3d, 0x07, 0x59, 0x6b, 0x3c, 0x3a, 0xa1, 0x2e, 0x0f, 0x52, 0x46, 0x09, 0x46, 0xb8, 0x0a, - 0x19, 0x93, 0x9c, 0x50, 0x53, 0x4a, 0x6f, 0x27, 0x76, 0x4a, 0xbb, 0xd7, 0xce, 0xb4, 0x2b, 0x2b, - 0x4d, 0xa6, 0xa2, 0x08, 0x4d, 0xfc, 0x19, 0xa4, 0x83, 0x12, 0xcd, 0x2c, 0x5c, 0x3d, 0x9b, 0x05, - 0xb6, 0x97, 0x14, 0xae, 0x87, 0xdf, 0x81, 0x3c, 0xfb, 0x2b, 0x72, 0x23, 0xcb, 0x7d, 0xce, 0x31, - 0x80, 0xe5, 0x05, 0xde, 0x82, 0x1c, 0xdf, 0x26, 0x3a, 0x0d, 0x8f, 0xb6, 0x68, 0xcc, 0x12, 0x4b, - 0xa7, 0x7d, 0x32, 0x36, 0x7d, 0xf5, 0x09, 0x31, 0xc7, 0x94, 0x27, 0x7c, 0x5e, 0x29, 0x06, 0xe0, - 0x4f, 0x19, 0x86, 0x2f, 0x42, 0x41, 0xec, 0x2a, 0xc3, 0xd2, 0xe9, 0x33, 0x5e, 0x3d, 0x33, 0x8a, - 0xd8, 0x68, 0x0d, 0x86, 0xb0, 0xcf, 0x3f, 0xf2, 0x6c, 0x2b, 0x4c, 0x4d, 0xfe, 0x09, 0x06, 0xf0, - 0xcf, 0xdf, 0x9e, 0x2d, 0xdc, 0xef, 0x2d, 0x9e, 0xde, 0x6c, 0x4e, 0x95, 0xff, 0x94, 0x84, 0x34, - 0xaf, 0x17, 0x6b, 0x50, 0xe8, 0x3d, 0xec, 0xc8, 0x6a, 0xbd, 0x7d, 0x7c, 0xd0, 0x94, 0x51, 0x02, - 0x97, 0x00, 0x38, 0x70, 0xaf, 0xd9, 0xae, 0xf6, 0x50, 0x32, 0x1a, 0x37, 0x5a, 0xbd, 0xbd, 0x9b, - 0x28, 0x15, 0x29, 0x1c, 0x0b, 0x20, 0x1d, 0x27, 0xdc, 0xd8, 0x45, 0x19, 0x8c, 0xa0, 0x28, 0x0c, - 0x34, 0x1e, 0xc8, 0xf5, 0xbd, 0x9b, 0x28, 0x3b, 0x8d, 0xdc, 0xd8, 0x45, 0x2b, 0x78, 0x15, 0xf2, - 0x1c, 0x39, 0x68, 0xb7, 0x9b, 0x28, 0x17, 0xd9, 0xec, 0xf6, 0x94, 0x46, 0xeb, 0x10, 0xe5, 0x23, - 0x9b, 0x87, 0x4a, 0xfb, 0xb8, 0x83, 0x20, 0xb2, 0x70, 0x24, 0x77, 0xbb, 0xd5, 0x43, 0x19, 0x15, - 0x22, 0xc6, 0xc1, 0xc3, 0x9e, 0xdc, 0x45, 0xc5, 0x29, 0xb7, 0x6e, 0xec, 0xa2, 0xd5, 0xe8, 0x13, - 0x72, 0xeb, 0xf8, 0x08, 0x95, 0xf0, 0x3a, 0xac, 0x8a, 0x4f, 0x84, 0x4e, 0xac, 0xcd, 0x40, 0x7b, - 0x37, 0x11, 0x9a, 0x38, 0x22, 0xac, 0xac, 0x4f, 0x01, 0x7b, 0x37, 0x11, 0x2e, 0xd7, 0x20, 0xc3, - 0xb3, 0x0b, 0x63, 0x28, 0x35, 0xab, 0x07, 0x72, 0x53, 0x6d, 0x77, 0x7a, 0x8d, 0x76, 0xab, 0xda, - 0x44, 0x89, 0x09, 0xa6, 0xc8, 0x3f, 0x39, 0x6e, 0x28, 0x72, 0x1d, 0x25, 0xe3, 0x58, 0x47, 0xae, - 0xf6, 0xe4, 0x3a, 0x4a, 0x95, 0x35, 0xd8, 0x5c, 0x54, 0x27, 0x17, 0xee, 0x8c, 0xd8, 0x12, 0x27, - 0x97, 0x2c, 0x31, 0xb7, 0x35, 0xb7, 0xc4, 0xff, 0x4a, 0xc2, 0xc6, 0x82, 0xb3, 0x62, 0xe1, 0x47, - 0x7e, 0x08, 0x19, 0x91, 0xa2, 0xe2, 0xf4, 0xbc, 0xb2, 0xf0, 0xd0, 0xe1, 0x09, 0x3b, 0x77, 0x82, - 0x72, 0xbd, 0x78, 0x07, 0x91, 0x5a, 0xd2, 0x41, 0x30, 0x13, 0x73, 0x35, 0xfd, 0xe7, 0x73, 0x35, - 0x5d, 0x1c, 0x7b, 0x7b, 0x67, 0x39, 0xf6, 0x38, 0xf6, 0xcd, 0x6a, 0x7b, 0x66, 0x41, 0x6d, 0xbf, - 0x0b, 0xeb, 0x73, 0x86, 0xce, 0x5c, 0x63, 0x7f, 0x99, 0x00, 0x69, 0x59, 0x70, 0x5e, 0x51, 0xe9, - 0x92, 0x53, 0x95, 0xee, 0xee, 0x6c, 0x04, 0x2f, 0x2d, 0x5f, 0x84, 0xb9, 0xb5, 0xfe, 0x3a, 0x01, - 0xe7, 0x16, 0x77, 0x8a, 0x0b, 0x7d, 0xf8, 0x0c, 0xb2, 0x23, 0xea, 0x0f, 0xed, 0xb0, 0x5b, 0xfa, - 0x78, 0xc1, 0x19, 0xcc, 0xc4, 0xb3, 0x8b, 0x1d, 0x68, 0xc5, 0x0f, 0xf1, 0xd4, 0xb2, 0x76, 0x4f, - 0x78, 0x33, 0xe7, 0xe9, 0xaf, 0x92, 0xf0, 0xf6, 0x42, 0xe3, 0x0b, 0x1d, 0x7d, 0x0f, 0xc0, 0xb0, - 0x9c, 0xb1, 0x2f, 0x3a, 0x22, 0x51, 0x60, 0xf3, 0x1c, 0xe1, 0xc5, 0x8b, 0x15, 0xcf, 0xb1, 0x1f, - 0xc9, 0x53, 0x5c, 0x0e, 0x02, 0xe2, 0x84, 0x3b, 0x13, 0x47, 0xd3, 0xdc, 0xd1, 0xf7, 0x97, 0xcc, - 0x74, 0x2e, 0x31, 0x3f, 0x01, 0xa4, 0x99, 0x06, 0xb5, 0x7c, 0xd5, 0xf3, 0x5d, 0x4a, 0x46, 0x86, - 0x35, 0xe0, 0x27, 0x48, 0x6e, 0x3f, 0xd3, 0x27, 0xa6, 0x47, 0x95, 0x35, 0x21, 0xee, 0x86, 0x52, - 0xa6, 0xc1, 0x13, 0xc8, 0x8d, 0x69, 0x64, 0xa7, 0x34, 0x84, 0x38, 0xd2, 0x28, 0xff, 0x31, 0x07, - 0x85, 0x58, 0x5f, 0x8d, 0x2f, 0x41, 0xf1, 0x11, 0x79, 0x42, 0xd4, 0xf0, 0xae, 0x24, 0x22, 0x51, - 0x60, 0x58, 0x27, 0xb8, 0x2f, 0x7d, 0x02, 0x9b, 0x9c, 0x62, 0x8f, 0x7d, 0xea, 0xaa, 0x9a, 0x49, - 0x3c, 0x8f, 0x07, 0x2d, 0xc7, 0xa9, 0x98, 0xc9, 0xda, 0x4c, 0x54, 0x0b, 0x25, 0xf8, 0x16, 0x6c, - 0x70, 0x8d, 0xd1, 0xd8, 0xf4, 0x0d, 0xc7, 0xa4, 0x2a, 0xbb, 0xbd, 0x79, 0xfc, 0x24, 0x89, 0x3c, - 0x5b, 0x67, 0x8c, 0xa3, 0x80, 0xc0, 0x3c, 0xf2, 0x70, 0x1d, 0xde, 0xe3, 0x6a, 0x03, 0x6a, 0x51, - 0x97, 0xf8, 0x54, 0xa5, 0x5f, 0x8d, 0x89, 0xe9, 0xa9, 0xc4, 0xd2, 0xd5, 0x21, 0xf1, 0x86, 0xd2, - 0x26, 0x33, 0x70, 0x90, 0x94, 0x12, 0xca, 0x05, 0x46, 0x3c, 0x0c, 0x78, 0x32, 0xa7, 0x55, 0x2d, - 0xfd, 0x73, 0xe2, 0x0d, 0xf1, 0x3e, 0x9c, 0xe3, 0x56, 0x3c, 0xdf, 0x35, 0xac, 0x81, 0xaa, 0x0d, - 0xa9, 0xf6, 0x58, 0x1d, 0xfb, 0xfd, 0x3b, 0xd2, 0x3b, 0xf1, 0xef, 0x73, 0x0f, 0xbb, 0x9c, 0x53, - 0x63, 0x94, 0x63, 0xbf, 0x7f, 0x07, 0x77, 0xa1, 0xc8, 0x16, 0x63, 0x64, 0x3c, 0xa7, 0x6a, 0xdf, - 0x76, 0xf9, 0xd1, 0x58, 0x5a, 0x50, 0x9a, 0x62, 0x11, 0xac, 0xb4, 0x03, 0x85, 0x23, 0x5b, 0xa7, - 0xfb, 0x99, 0x6e, 0x47, 0x96, 0xeb, 0x4a, 0x21, 0xb4, 0x72, 0xcf, 0x76, 0x59, 0x42, 0x0d, 0xec, - 0x28, 0xc0, 0x05, 0x91, 0x50, 0x03, 0x3b, 0x0c, 0xef, 0x2d, 0xd8, 0xd0, 0x34, 0x31, 0x67, 0x43, - 0x53, 0x83, 0x3b, 0x96, 0x27, 0xa1, 0xa9, 0x60, 0x69, 0xda, 0xa1, 0x20, 0x04, 0x39, 0xee, 0xe1, - 0x4f, 0xe1, 0xed, 0x49, 0xb0, 0xe2, 0x8a, 0xeb, 0x73, 0xb3, 0x9c, 0x55, 0xbd, 0x05, 0x1b, 0xce, - 0xe9, 0xbc, 0x22, 0x9e, 0xfa, 0xa2, 0x73, 0x3a, 0xab, 0x76, 0x1b, 0x36, 0x9d, 0xa1, 0x33, 0xaf, - 0x77, 0x35, 0xae, 0x87, 0x9d, 0xa1, 0x33, 0xab, 0xf8, 0x11, 0xbf, 0x70, 0xbb, 0x54, 0x23, 0x3e, - 0xd5, 0xa5, 0xf3, 0x71, 0x7a, 0x4c, 0x80, 0xaf, 0x03, 0xd2, 0x34, 0x95, 0x5a, 0xe4, 0xc4, 0xa4, - 0x2a, 0x71, 0xa9, 0x45, 0x3c, 0xe9, 0x62, 0x9c, 0x5c, 0xd2, 0x34, 0x99, 0x4b, 0xab, 0x5c, 0x88, - 0xaf, 0xc2, 0xba, 0x7d, 0xf2, 0x48, 0x13, 0x29, 0xa9, 0x3a, 0x2e, 0xed, 0x1b, 0xcf, 0xa4, 0x0f, - 0x79, 0x7c, 0xd7, 0x98, 0x80, 0x27, 0x64, 0x87, 0xc3, 0xf8, 0x0a, 0x20, 0xcd, 0x1b, 0x12, 0xd7, - 0xe1, 0x35, 0xd9, 0x73, 0x88, 0x46, 0xa5, 0x8f, 0x04, 0x55, 0xe0, 0xad, 0x10, 0x66, 0x5b, 0xc2, - 0x7b, 0x6a, 0xf4, 0xfd, 0xd0, 0xe2, 0x65, 0xb1, 0x25, 0x38, 0x16, 0x58, 0xdb, 0x01, 0xc4, 0x42, - 0x31, 0xf5, 0xe1, 0x1d, 0x4e, 0x2b, 0x39, 0x43, 0x27, 0xfe, 0xdd, 0x0f, 0x60, 0x95, 0x31, 0x27, - 0x1f, 0xbd, 0x22, 0x1a, 0x32, 0x67, 0x18, 0xfb, 0xe2, 0x6b, 0xeb, 0x8d, 0xcb, 0xfb, 0x50, 0x8c, - 0xe7, 0x27, 0xce, 0x83, 0xc8, 0x50, 0x94, 0x60, 0xcd, 0x4a, 0xad, 0x5d, 0x67, 0x6d, 0xc6, 0x97, - 0x32, 0x4a, 0xb2, 0x76, 0xa7, 0xd9, 0xe8, 0xc9, 0xaa, 0x72, 0xdc, 0xea, 0x35, 0x8e, 0x64, 0x94, - 0x8a, 0xf7, 0xd5, 0x7f, 0x4d, 0x42, 0x69, 0xfa, 0x8a, 0x84, 0x7f, 0x00, 0xe7, 0xc3, 0xf7, 0x0c, - 0x8f, 0xfa, 0xea, 0x53, 0xc3, 0xe5, 0x5b, 0x66, 0x44, 0xc4, 0xf1, 0x15, 0x2d, 0xda, 0x66, 0xc0, - 0xea, 0x52, 0xff, 0x0b, 0xc3, 0x65, 0x1b, 0x62, 0x44, 0x7c, 0xdc, 0x84, 0x8b, 0x96, 0xad, 0x7a, - 0x3e, 0xb1, 0x74, 0xe2, 0xea, 0xea, 0xe4, 0x25, 0x49, 0x25, 0x9a, 0x46, 0x3d, 0xcf, 0x16, 0x47, - 0x55, 0x64, 0xe5, 0x5d, 0xcb, 0xee, 0x06, 0xe4, 0x49, 0x0d, 0xaf, 0x06, 0xd4, 0x99, 0x04, 0x4b, - 0x2d, 0x4b, 0xb0, 0x77, 0x20, 0x3f, 0x22, 0x8e, 0x4a, 0x2d, 0xdf, 0x3d, 0xe5, 0x8d, 0x71, 0x4e, - 0xc9, 0x8d, 0x88, 0x23, 0xb3, 0xf1, 0x9b, 0xb9, 0x9f, 0xfc, 0x23, 0x05, 0xc5, 0x78, 0x73, 0xcc, - 0xee, 0x1a, 0x1a, 0x3f, 0x47, 0x12, 0xbc, 0xd2, 0x7c, 0xf0, 0xd2, 0x56, 0xba, 0x52, 0x63, 0x07, - 0xcc, 0x7e, 0x56, 0xb4, 0xac, 0x8a, 0xd0, 0x64, 0x87, 0x3b, 0xab, 0x2d, 0x54, 0xb4, 0x08, 0x39, - 0x25, 0x18, 0xe1, 0x43, 0xc8, 0x3e, 0xf2, 0xb8, 0xed, 0x2c, 0xb7, 0xfd, 0xe1, 0xcb, 0x6d, 0xdf, - 0xef, 0x72, 0xe3, 0xf9, 0xfb, 0x5d, 0xb5, 0xd5, 0x56, 0x8e, 0xaa, 0x4d, 0x25, 0x50, 0xc7, 0x17, - 0x20, 0x6d, 0x92, 0xe7, 0xa7, 0xd3, 0x47, 0x11, 0x87, 0xce, 0x1a, 0xf8, 0x0b, 0x90, 0x7e, 0x4a, - 0xc9, 0xe3, 0xe9, 0x03, 0x80, 0x43, 0xaf, 0x31, 0xf5, 0xaf, 0x43, 0x86, 0xc7, 0x0b, 0x03, 0x04, - 0x11, 0x43, 0x6f, 0xe1, 0x1c, 0xa4, 0x6b, 0x6d, 0x85, 0xa5, 0x3f, 0x82, 0xa2, 0x40, 0xd5, 0x4e, - 0x43, 0xae, 0xc9, 0x28, 0x59, 0xbe, 0x05, 0x59, 0x11, 0x04, 0xb6, 0x35, 0xa2, 0x30, 0xa0, 0xb7, - 0x82, 0x61, 0x60, 0x23, 0x11, 0x4a, 0x8f, 0x8f, 0x0e, 0x64, 0x05, 0x25, 0xe3, 0xcb, 0xeb, 0x41, - 0x31, 0xde, 0x17, 0xbf, 0x99, 0x9c, 0xfa, 0x4b, 0x02, 0x0a, 0xb1, 0x3e, 0x97, 0x35, 0x28, 0xc4, - 0x34, 0xed, 0xa7, 0x2a, 0x31, 0x0d, 0xe2, 0x05, 0x49, 0x01, 0x1c, 0xaa, 0x32, 0xe4, 0xac, 0x8b, - 0xf6, 0x46, 0x9c, 0xff, 0x5d, 0x02, 0xd0, 0x6c, 0x8b, 0x39, 0xe3, 0x60, 0xe2, 0x3b, 0x75, 0xf0, - 0xb7, 0x09, 0x28, 0x4d, 0xf7, 0x95, 0x33, 0xee, 0x5d, 0xfa, 0x4e, 0xdd, 0xfb, 0x67, 0x12, 0x56, - 0xa7, 0xba, 0xc9, 0xb3, 0x7a, 0xf7, 0x15, 0xac, 0x1b, 0x3a, 0x1d, 0x39, 0xb6, 0x4f, 0x2d, 0xed, - 0x54, 0x35, 0xe9, 0x13, 0x6a, 0x4a, 0x65, 0x5e, 0x28, 0xae, 0xbf, 0xbc, 0x5f, 0xad, 0x34, 0x26, - 0x7a, 0x4d, 0xa6, 0xb6, 0xbf, 0xd1, 0xa8, 0xcb, 0x47, 0x9d, 0x76, 0x4f, 0x6e, 0xd5, 0x1e, 0xaa, - 0xc7, 0xad, 0x1f, 0xb7, 0xda, 0x5f, 0xb4, 0x14, 0x64, 0xcc, 0xd0, 0x5e, 0xe3, 0x56, 0xef, 0x00, - 0x9a, 0x75, 0x0a, 0x9f, 0x87, 0x45, 0x6e, 0xa1, 0xb7, 0xf0, 0x06, 0xac, 0xb5, 0xda, 0x6a, 0xb7, - 0x51, 0x97, 0x55, 0xf9, 0xde, 0x3d, 0xb9, 0xd6, 0xeb, 0x8a, 0x17, 0x88, 0x88, 0xdd, 0x9b, 0xde, - 0xd4, 0xbf, 0x49, 0xc1, 0xc6, 0x02, 0x4f, 0x70, 0x35, 0xb8, 0x3b, 0x88, 0xeb, 0xcc, 0xf7, 0xcf, - 0xe2, 0x7d, 0x85, 0x1d, 0xf9, 0x1d, 0xe2, 0xfa, 0xc1, 0x55, 0xe3, 0x0a, 0xb0, 0x28, 0x59, 0xbe, - 0xd1, 0x37, 0xa8, 0x1b, 0x3c, 0xd8, 0x88, 0x0b, 0xc5, 0xda, 0x04, 0x17, 0x6f, 0x36, 0xdf, 0x03, - 0xec, 0xd8, 0x9e, 0xe1, 0x1b, 0x4f, 0xa8, 0x6a, 0x58, 0xe1, 0xeb, 0x0e, 0xbb, 0x60, 0xa4, 0x15, - 0x14, 0x4a, 0x1a, 0x96, 0x1f, 0xb1, 0x2d, 0x3a, 0x20, 0x33, 0x6c, 0x56, 0xc0, 0x53, 0x0a, 0x0a, - 0x25, 0x11, 0xfb, 0x12, 0x14, 0x75, 0x7b, 0xcc, 0xba, 0x2e, 0xc1, 0x63, 0xe7, 0x45, 0x42, 0x29, - 0x08, 0x2c, 0xa2, 0x04, 0xfd, 0xf4, 0xe4, 0x59, 0xa9, 0xa8, 0x14, 0x04, 0x26, 0x28, 0x97, 0x61, - 0x8d, 0x0c, 0x06, 0x2e, 0x33, 0x1e, 0x1a, 0x12, 0x37, 0x84, 0x52, 0x04, 0x73, 0xe2, 0xd6, 0x7d, - 0xc8, 0x85, 0x71, 0x60, 0x47, 0x32, 0x8b, 0x84, 0xea, 0x88, 0x6b, 0x6f, 0x72, 0x27, 0xaf, 0xe4, - 0xac, 0x50, 0x78, 0x09, 0x8a, 0x86, 0xa7, 0x4e, 0x5e, 0xc9, 0x93, 0xdb, 0xc9, 0x9d, 0x9c, 0x52, - 0x30, 0xbc, 0xe8, 0x85, 0xb1, 0xfc, 0x75, 0x12, 0x4a, 0xd3, 0xaf, 0xfc, 0xb8, 0x0e, 0x39, 0xd3, - 0xd6, 0x08, 0x4f, 0x2d, 0xf1, 0x13, 0xd3, 0xce, 0x2b, 0x7e, 0x18, 0xa8, 0x34, 0x03, 0xbe, 0x12, - 0x69, 0x6e, 0xfd, 0x2d, 0x01, 0xb9, 0x10, 0xc6, 0xe7, 0x20, 0xed, 0x10, 0x7f, 0xc8, 0xcd, 0x65, - 0x0e, 0x92, 0x28, 0xa1, 0xf0, 0x31, 0xc3, 0x3d, 0x87, 0x58, 0x3c, 0x05, 0x02, 0x9c, 0x8d, 0xd9, - 0xba, 0x9a, 0x94, 0xe8, 0xfc, 0xfa, 0x61, 0x8f, 0x46, 0xd4, 0xf2, 0xbd, 0x70, 0x5d, 0x03, 0xbc, - 0x16, 0xc0, 0xf8, 0x1a, 0xac, 0xfb, 0x2e, 0x31, 0xcc, 0x29, 0x6e, 0x9a, 0x73, 0x51, 0x28, 0x88, - 0xc8, 0xfb, 0x70, 0x21, 0xb4, 0xab, 0x53, 0x9f, 0x68, 0x43, 0xaa, 0x4f, 0x94, 0xb2, 0xfc, 0x99, - 0xe1, 0x7c, 0x40, 0xa8, 0x07, 0xf2, 0x50, 0xb7, 0xfc, 0xf7, 0x04, 0xac, 0x87, 0x17, 0x26, 0x3d, - 0x0a, 0xd6, 0x11, 0x00, 0xb1, 0x2c, 0xdb, 0x8f, 0x87, 0x6b, 0x3e, 0x95, 0xe7, 0xf4, 0x2a, 0xd5, - 0x48, 0x49, 0x89, 0x19, 0xd8, 0x1a, 0x01, 0x4c, 0x24, 0x4b, 0xc3, 0x76, 0x11, 0x0a, 0xc1, 0x4f, - 0x38, 0xfc, 0x77, 0x40, 0x71, 0xc5, 0x06, 0x01, 0xb1, 0x9b, 0x15, 0xde, 0x84, 0xcc, 0x09, 0x1d, - 0x18, 0x56, 0xf0, 0x30, 0x2b, 0x06, 0xe1, 0x43, 0x48, 0x3a, 0x7a, 0x08, 0x39, 0xf8, 0x19, 0x6c, - 0x68, 0xf6, 0x68, 0xd6, 0xdd, 0x03, 0x34, 0x73, 0xcd, 0xf7, 0x3e, 0x4f, 0x7c, 0x09, 0x93, 0x16, - 0xf3, 0x7f, 0x89, 0xc4, 0xef, 0x93, 0xa9, 0xc3, 0xce, 0xc1, 0x1f, 0x92, 0x5b, 0x87, 0x42, 0xb5, - 0x13, 0xce, 0x54, 0xa1, 0x7d, 0x93, 0x6a, 0xcc, 0xfb, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff, 0xa3, - 0x58, 0x22, 0x30, 0xdf, 0x1c, 0x00, 0x00, + 0x83, 0x1e, 0x02, 0xf4, 0x54, 0xa0, 0x7f, 0x40, 0x51, 0x14, 0x3d, 0xf4, 0xb2, 0x40, 0xff, 0x80, + 0x02, 0xed, 0xbd, 0xd7, 0x02, 0xbd, 0xf7, 0x50, 0xa0, 0x05, 0xda, 0x3f, 0xa1, 0xc7, 0x62, 0x66, + 0x48, 0x8a, 0xd4, 0x47, 0xe2, 0x5d, 0x20, 0xd9, 0x93, 0x3d, 0xef, 0xfd, 0xde, 0x9b, 0x37, 0x8f, + 0xbf, 0x79, 0xf3, 0x66, 0x04, 0x82, 0x46, 0x5c, 0xd5, 0xd1, 0x6d, 0xcf, 0x72, 0x2a, 0xb6, 0x63, + 0x79, 0x16, 0x5a, 0x1b, 0x5a, 0xd6, 0xd0, 0x20, 0x7c, 0x74, 0x32, 0x19, 0x94, 0x5b, 0xb0, 0x7e, + 0x4f, 0x37, 0x48, 0x3d, 0x04, 0xf6, 0x88, 0x87, 0xee, 0x40, 0x7a, 0xa0, 0x1b, 0x44, 0x4c, 0xec, + 0xa4, 0x76, 0x0b, 0x7b, 0x1f, 0x56, 0x66, 0x8c, 0x2a, 0x71, 0x8b, 0x2e, 0x15, 0xcb, 0xcc, 0xa2, + 0xfc, 0xef, 0x34, 0x6c, 0x2c, 0xd0, 0x22, 0x04, 0x69, 0x13, 0x8f, 0xa9, 0xc7, 0xc4, 0x6e, 0x5e, + 0x66, 0xff, 0x23, 0x11, 0x56, 0x6c, 0xac, 0x3e, 0xc6, 0x43, 0x22, 0x26, 0x99, 0x38, 0x18, 0xa2, + 0xf7, 0x01, 0x34, 0x62, 0x13, 0x53, 0x23, 0xa6, 0x7a, 0x2a, 0xa6, 0x76, 0x52, 0xbb, 0x79, 0x39, + 0x22, 0x41, 0xd7, 0x60, 0xdd, 0x9e, 0x9c, 0x18, 0xba, 0xaa, 0x44, 0x60, 0xb0, 0x93, 0xda, 0xcd, + 0xc8, 0x02, 0x57, 0xd4, 0xa7, 0xe0, 0xcb, 0xb0, 0xf6, 0x94, 0xe0, 0xc7, 0x51, 0x68, 0x81, 0x41, + 0x4b, 0x54, 0x1c, 0x01, 0xd6, 0xa0, 0x38, 0x26, 0xae, 0x8b, 0x87, 0x44, 0xf1, 0x4e, 0x6d, 0x22, + 0xa6, 0xd9, 0xea, 0x77, 0xe6, 0x56, 0x3f, 0xbb, 0xf2, 0x82, 0x6f, 0xd5, 0x3f, 0xb5, 0x09, 0xaa, + 0x42, 0x9e, 0x98, 0x93, 0x31, 0xf7, 0x90, 0x59, 0x92, 0x3f, 0xc9, 0x9c, 0x8c, 0x67, 0xbd, 0xe4, + 0xa8, 0x99, 0xef, 0x62, 0xc5, 0x25, 0xce, 0x13, 0x5d, 0x25, 0x62, 0x96, 0x39, 0xb8, 0x3c, 0xe7, + 0xa0, 0xc7, 0xf5, 0xb3, 0x3e, 0x02, 0x3b, 0x54, 0x83, 0x3c, 0x79, 0xe6, 0x11, 0xd3, 0xd5, 0x2d, + 0x53, 0x5c, 0x61, 0x4e, 0x2e, 0x2d, 0xf8, 0x8a, 0xc4, 0xd0, 0x66, 0x5d, 0x4c, 0xed, 0xd0, 0x3e, + 0xac, 0x58, 0xb6, 0xa7, 0x5b, 0xa6, 0x2b, 0xe6, 0x76, 0x12, 0xbb, 0x85, 0xbd, 0x77, 0x17, 0x12, + 0xa1, 0xc3, 0x31, 0x72, 0x00, 0x46, 0x0d, 0x10, 0x5c, 0x6b, 0xe2, 0xa8, 0x44, 0x51, 0x2d, 0x8d, + 0x28, 0xba, 0x39, 0xb0, 0xc4, 0x3c, 0x73, 0x70, 0x61, 0x7e, 0x21, 0x0c, 0x58, 0xb3, 0x34, 0xd2, + 0x30, 0x07, 0x96, 0x5c, 0x72, 0x63, 0x63, 0xb4, 0x05, 0x59, 0xf7, 0xd4, 0xf4, 0xf0, 0x33, 0xb1, + 0xc8, 0x18, 0xe2, 0x8f, 0xca, 0x7f, 0xce, 0xc2, 0xda, 0x59, 0x28, 0x76, 0x17, 0x32, 0x03, 0xba, + 0x4a, 0x31, 0xf9, 0x6d, 0x72, 0xc0, 0x6d, 0xe2, 0x49, 0xcc, 0x7e, 0xc7, 0x24, 0x56, 0xa1, 0x60, + 0x12, 0xd7, 0x23, 0x1a, 0x67, 0x44, 0xea, 0x8c, 0x9c, 0x02, 0x6e, 0x34, 0x4f, 0xa9, 0xf4, 0x77, + 0xa2, 0xd4, 0x03, 0x58, 0x0b, 0x43, 0x52, 0x1c, 0x6c, 0x0e, 0x03, 0x6e, 0x5e, 0x7f, 0x55, 0x24, + 0x15, 0x29, 0xb0, 0x93, 0xa9, 0x99, 0x5c, 0x22, 0xb1, 0x31, 0xaa, 0x03, 0x58, 0x26, 0xb1, 0x06, + 0x8a, 0x46, 0x54, 0x43, 0xcc, 0x2d, 0xc9, 0x52, 0x87, 0x42, 0xe6, 0xb2, 0x64, 0x71, 0xa9, 0x6a, + 0xa0, 0xcf, 0xa6, 0x54, 0x5b, 0x59, 0xc2, 0x94, 0x16, 0xdf, 0x64, 0x73, 0x6c, 0x3b, 0x86, 0x92, + 0x43, 0x28, 0xef, 0x89, 0xe6, 0xaf, 0x2c, 0xcf, 0x82, 0xa8, 0xbc, 0x72, 0x65, 0xb2, 0x6f, 0xc6, + 0x17, 0xb6, 0xea, 0x44, 0x87, 0xe8, 0x03, 0x08, 0x05, 0x0a, 0xa3, 0x15, 0xb0, 0x2a, 0x54, 0x0c, + 0x84, 0x6d, 0x3c, 0x26, 0xdb, 0xcf, 0xa1, 0x14, 0x4f, 0x0f, 0xda, 0x84, 0x8c, 0xeb, 0x61, 0xc7, + 0x63, 0x2c, 0xcc, 0xc8, 0x7c, 0x80, 0x04, 0x48, 0x11, 0x53, 0x63, 0x55, 0x2e, 0x23, 0xd3, 0x7f, + 0xd1, 0x8f, 0xa6, 0x0b, 0x4e, 0xb1, 0x05, 0x7f, 0x34, 0xff, 0x45, 0x63, 0x9e, 0x67, 0xd7, 0xbd, + 0x7d, 0x1b, 0x56, 0x63, 0x0b, 0x38, 0xeb, 0xd4, 0xe5, 0x5f, 0xc0, 0xdb, 0x0b, 0x5d, 0xa3, 0x07, + 0xb0, 0x39, 0x31, 0x75, 0xd3, 0x23, 0x8e, 0xed, 0x10, 0xca, 0x58, 0x3e, 0x95, 0xf8, 0x9f, 0x95, + 0x25, 0x9c, 0x3b, 0x8e, 0xa2, 0xb9, 0x17, 0x79, 0x63, 0x32, 0x2f, 0xbc, 0x9a, 0xcf, 0xfd, 0x77, + 0x45, 0x78, 0xf1, 0xe2, 0xc5, 0x8b, 0x64, 0xf9, 0x37, 0x59, 0xd8, 0x5c, 0xb4, 0x67, 0x16, 0x6e, + 0xdf, 0x2d, 0xc8, 0x9a, 0x93, 0xf1, 0x09, 0x71, 0x58, 0x92, 0x32, 0xb2, 0x3f, 0x42, 0x55, 0xc8, + 0x18, 0xf8, 0x84, 0x18, 0x62, 0x7a, 0x27, 0xb1, 0x5b, 0xda, 0xbb, 0x76, 0xa6, 0x5d, 0x59, 0x69, + 0x52, 0x13, 0x99, 0x5b, 0xa2, 0xcf, 0x21, 0xed, 0x97, 0x68, 0xea, 0xe1, 0xea, 0xd9, 0x3c, 0xd0, + 0xbd, 0x24, 0x33, 0x3b, 0xf4, 0x0e, 0xe4, 0xe9, 0x5f, 0xce, 0x8d, 0x2c, 0x8b, 0x39, 0x47, 0x05, + 0x94, 0x17, 0x68, 0x1b, 0x72, 0x6c, 0x9b, 0x68, 0x24, 0x38, 0xda, 0xc2, 0x31, 0x25, 0x96, 0x46, + 0x06, 0x78, 0x62, 0x78, 0xca, 0x13, 0x6c, 0x4c, 0x08, 0x23, 0x7c, 0x5e, 0x2e, 0xfa, 0xc2, 0x9f, + 0x52, 0x19, 0xba, 0x00, 0x05, 0xbe, 0xab, 0x74, 0x53, 0x23, 0xcf, 0x58, 0xf5, 0xcc, 0xc8, 0x7c, + 0xa3, 0x35, 0xa8, 0x84, 0x4e, 0xff, 0xc8, 0xb5, 0xcc, 0x80, 0x9a, 0x6c, 0x0a, 0x2a, 0x60, 0xd3, + 0xdf, 0x9e, 0x2d, 0xdc, 0xef, 0x2d, 0x5e, 0xde, 0x2c, 0xa7, 0xca, 0x7f, 0x4a, 0x42, 0x9a, 0xd5, + 0x8b, 0x35, 0x28, 0xf4, 0x1f, 0x76, 0x25, 0xa5, 0xde, 0x39, 0x3e, 0x6c, 0x4a, 0x42, 0x02, 0x95, + 0x00, 0x98, 0xe0, 0x5e, 0xb3, 0x53, 0xed, 0x0b, 0xc9, 0x70, 0xdc, 0x68, 0xf7, 0xf7, 0x6f, 0x0a, + 0xa9, 0xd0, 0xe0, 0x98, 0x0b, 0xd2, 0x51, 0xc0, 0x8d, 0x3d, 0x21, 0x83, 0x04, 0x28, 0x72, 0x07, + 0x8d, 0x07, 0x52, 0x7d, 0xff, 0xa6, 0x90, 0x8d, 0x4b, 0x6e, 0xec, 0x09, 0x2b, 0x68, 0x15, 0xf2, + 0x4c, 0x72, 0xd8, 0xe9, 0x34, 0x85, 0x5c, 0xe8, 0xb3, 0xd7, 0x97, 0x1b, 0xed, 0x23, 0x21, 0x1f, + 0xfa, 0x3c, 0x92, 0x3b, 0xc7, 0x5d, 0x01, 0x42, 0x0f, 0x2d, 0xa9, 0xd7, 0xab, 0x1e, 0x49, 0x42, + 0x21, 0x44, 0x1c, 0x3e, 0xec, 0x4b, 0x3d, 0xa1, 0x18, 0x0b, 0xeb, 0xc6, 0x9e, 0xb0, 0x1a, 0x4e, + 0x21, 0xb5, 0x8f, 0x5b, 0x42, 0x09, 0xad, 0xc3, 0x2a, 0x9f, 0x22, 0x08, 0x62, 0x6d, 0x46, 0xb4, + 0x7f, 0x53, 0x10, 0xa6, 0x81, 0x70, 0x2f, 0xeb, 0x31, 0xc1, 0xfe, 0x4d, 0x01, 0x95, 0x6b, 0x90, + 0x61, 0xec, 0x42, 0x08, 0x4a, 0xcd, 0xea, 0xa1, 0xd4, 0x54, 0x3a, 0xdd, 0x7e, 0xa3, 0xd3, 0xae, + 0x36, 0x85, 0xc4, 0x54, 0x26, 0x4b, 0x3f, 0x39, 0x6e, 0xc8, 0x52, 0x5d, 0x48, 0x46, 0x65, 0x5d, + 0xa9, 0xda, 0x97, 0xea, 0x42, 0xaa, 0xac, 0xc2, 0xe6, 0xa2, 0x3a, 0xb9, 0x70, 0x67, 0x44, 0x3e, + 0x71, 0x72, 0xc9, 0x27, 0x66, 0xbe, 0xe6, 0x3e, 0xf1, 0xbf, 0x92, 0xb0, 0xb1, 0xe0, 0xac, 0x58, + 0x38, 0xc9, 0x0f, 0x21, 0xc3, 0x29, 0xca, 0x4f, 0xcf, 0x2b, 0x0b, 0x0f, 0x1d, 0x46, 0xd8, 0xb9, + 0x13, 0x94, 0xd9, 0x45, 0x3b, 0x88, 0xd4, 0x92, 0x0e, 0x82, 0xba, 0x98, 0xab, 0xe9, 0x3f, 0x9f, + 0xab, 0xe9, 0xfc, 0xd8, 0xdb, 0x3f, 0xcb, 0xb1, 0xc7, 0x64, 0xdf, 0xae, 0xb6, 0x67, 0x16, 0xd4, + 0xf6, 0xbb, 0xb0, 0x3e, 0xe7, 0xe8, 0xcc, 0x35, 0xf6, 0x97, 0x09, 0x10, 0x97, 0x25, 0xe7, 0x15, + 0x95, 0x2e, 0x19, 0xab, 0x74, 0x77, 0x67, 0x33, 0x78, 0x71, 0xf9, 0x47, 0x98, 0xfb, 0xd6, 0xdf, + 0x24, 0x60, 0x6b, 0x71, 0xa7, 0xb8, 0x30, 0x86, 0xcf, 0x21, 0x3b, 0x26, 0xde, 0xc8, 0x0a, 0xba, + 0xa5, 0x8f, 0x16, 0x9c, 0xc1, 0x54, 0x3d, 0xfb, 0xb1, 0x7d, 0xab, 0xe8, 0x21, 0x9e, 0x5a, 0xd6, + 0xee, 0xf1, 0x68, 0xe6, 0x22, 0xfd, 0x55, 0x12, 0xde, 0x5e, 0xe8, 0x7c, 0x61, 0xa0, 0xef, 0x01, + 0xe8, 0xa6, 0x3d, 0xf1, 0x78, 0x47, 0xc4, 0x0b, 0x6c, 0x9e, 0x49, 0x58, 0xf1, 0xa2, 0xc5, 0x73, + 0xe2, 0x85, 0xfa, 0x14, 0xd3, 0x03, 0x17, 0x31, 0xc0, 0x9d, 0x69, 0xa0, 0x69, 0x16, 0xe8, 0xfb, + 0x4b, 0x56, 0x3a, 0x47, 0xcc, 0x4f, 0x41, 0x50, 0x0d, 0x9d, 0x98, 0x9e, 0xe2, 0x7a, 0x0e, 0xc1, + 0x63, 0xdd, 0x1c, 0xb2, 0x13, 0x24, 0x77, 0x90, 0x19, 0x60, 0xc3, 0x25, 0xf2, 0x1a, 0x57, 0xf7, + 0x02, 0x2d, 0xb5, 0x60, 0x04, 0x72, 0x22, 0x16, 0xd9, 0x98, 0x05, 0x57, 0x87, 0x16, 0xe5, 0x5f, + 0xe7, 0xa1, 0x10, 0xe9, 0xab, 0xd1, 0x45, 0x28, 0x3e, 0xc2, 0x4f, 0xb0, 0x12, 0xdc, 0x95, 0x78, + 0x26, 0x0a, 0x54, 0xd6, 0xf5, 0xef, 0x4b, 0x9f, 0xc2, 0x26, 0x83, 0x58, 0x13, 0x8f, 0x38, 0x8a, + 0x6a, 0x60, 0xd7, 0x65, 0x49, 0xcb, 0x31, 0x28, 0xa2, 0xba, 0x0e, 0x55, 0xd5, 0x02, 0x0d, 0xba, + 0x05, 0x1b, 0xcc, 0x62, 0x3c, 0x31, 0x3c, 0xdd, 0x36, 0x88, 0x42, 0x6f, 0x6f, 0x2e, 0x3b, 0x49, + 0xc2, 0xc8, 0xd6, 0x29, 0xa2, 0xe5, 0x03, 0x68, 0x44, 0x2e, 0xaa, 0xc3, 0x7b, 0xcc, 0x6c, 0x48, + 0x4c, 0xe2, 0x60, 0x8f, 0x28, 0xe4, 0xeb, 0x09, 0x36, 0x5c, 0x05, 0x9b, 0x9a, 0x32, 0xc2, 0xee, + 0x48, 0xdc, 0xa4, 0x0e, 0x0e, 0x93, 0x62, 0x42, 0x3e, 0x4f, 0x81, 0x47, 0x3e, 0x4e, 0x62, 0xb0, + 0xaa, 0xa9, 0x7d, 0x81, 0xdd, 0x11, 0x3a, 0x80, 0x2d, 0xe6, 0xc5, 0xf5, 0x1c, 0xdd, 0x1c, 0x2a, + 0xea, 0x88, 0xa8, 0x8f, 0x95, 0x89, 0x37, 0xb8, 0x23, 0xbe, 0x13, 0x9d, 0x9f, 0x45, 0xd8, 0x63, + 0x98, 0x1a, 0x85, 0x1c, 0x7b, 0x83, 0x3b, 0xa8, 0x07, 0x45, 0xfa, 0x31, 0xc6, 0xfa, 0x73, 0xa2, + 0x0c, 0x2c, 0x87, 0x1d, 0x8d, 0xa5, 0x05, 0xa5, 0x29, 0x92, 0xc1, 0x4a, 0xc7, 0x37, 0x68, 0x59, + 0x1a, 0x39, 0xc8, 0xf4, 0xba, 0x92, 0x54, 0x97, 0x0b, 0x81, 0x97, 0x7b, 0x96, 0x43, 0x09, 0x35, + 0xb4, 0xc2, 0x04, 0x17, 0x38, 0xa1, 0x86, 0x56, 0x90, 0xde, 0x5b, 0xb0, 0xa1, 0xaa, 0x7c, 0xcd, + 0xba, 0xaa, 0xf8, 0x77, 0x2c, 0x57, 0x14, 0x62, 0xc9, 0x52, 0xd5, 0x23, 0x0e, 0xf0, 0x39, 0xee, + 0xa2, 0xcf, 0xe0, 0xed, 0x69, 0xb2, 0xa2, 0x86, 0xeb, 0x73, 0xab, 0x9c, 0x35, 0xbd, 0x05, 0x1b, + 0xf6, 0xe9, 0xbc, 0x21, 0x8a, 0xcd, 0x68, 0x9f, 0xce, 0x9a, 0xdd, 0x86, 0x4d, 0x7b, 0x64, 0xcf, + 0xdb, 0x5d, 0x8d, 0xda, 0x21, 0x7b, 0x64, 0xcf, 0x1a, 0x5e, 0x62, 0x17, 0x6e, 0x87, 0xa8, 0xd8, + 0x23, 0x9a, 0x78, 0x2e, 0x0a, 0x8f, 0x28, 0xd0, 0x75, 0x10, 0x54, 0x55, 0x21, 0x26, 0x3e, 0x31, + 0x88, 0x82, 0x1d, 0x62, 0x62, 0x57, 0xbc, 0x10, 0x05, 0x97, 0x54, 0x55, 0x62, 0xda, 0x2a, 0x53, + 0xa2, 0xab, 0xb0, 0x6e, 0x9d, 0x3c, 0x52, 0x39, 0x25, 0x15, 0xdb, 0x21, 0x03, 0xfd, 0x99, 0xf8, + 0x21, 0xcb, 0xef, 0x1a, 0x55, 0x30, 0x42, 0x76, 0x99, 0x18, 0x5d, 0x01, 0x41, 0x75, 0x47, 0xd8, + 0xb1, 0x59, 0x4d, 0x76, 0x6d, 0xac, 0x12, 0xf1, 0x12, 0x87, 0x72, 0x79, 0x3b, 0x10, 0xd3, 0x2d, + 0xe1, 0x3e, 0xd5, 0x07, 0x5e, 0xe0, 0xf1, 0x32, 0xdf, 0x12, 0x4c, 0xe6, 0x7b, 0xdb, 0x05, 0x81, + 0xa6, 0x22, 0x36, 0xf1, 0x2e, 0x83, 0x95, 0xec, 0x91, 0x1d, 0x9d, 0xf7, 0x03, 0x58, 0xa5, 0xc8, + 0xe9, 0xa4, 0x57, 0x78, 0x43, 0x66, 0x8f, 0x22, 0x33, 0xde, 0x84, 0x2d, 0x0a, 0x1a, 0x13, 0x0f, + 0x6b, 0xd8, 0xc3, 0x11, 0xf4, 0xc7, 0x0c, 0x4d, 0xf3, 0xde, 0xf2, 0x95, 0xb1, 0x38, 0x9d, 0xc9, + 0xc9, 0x69, 0xc8, 0xac, 0x4f, 0x78, 0x9c, 0x54, 0x16, 0x70, 0xeb, 0xb5, 0x35, 0xdd, 0xe5, 0x03, + 0x28, 0x46, 0x89, 0x8f, 0xf2, 0xc0, 0xa9, 0x2f, 0x24, 0x68, 0x17, 0x54, 0xeb, 0xd4, 0x69, 0xff, + 0xf2, 0x95, 0x24, 0x24, 0x69, 0x1f, 0xd5, 0x6c, 0xf4, 0x25, 0x45, 0x3e, 0x6e, 0xf7, 0x1b, 0x2d, + 0x49, 0x48, 0x45, 0x1b, 0xf6, 0xbf, 0x26, 0xa1, 0x14, 0xbf, 0x7b, 0xa1, 0x1f, 0xc0, 0xb9, 0xe0, + 0xa1, 0xc4, 0x25, 0x9e, 0xf2, 0x54, 0x77, 0xd8, 0x5e, 0x1c, 0x63, 0x7e, 0x2e, 0x86, 0x6c, 0xd8, + 0xf4, 0x51, 0x3d, 0xe2, 0x7d, 0xa9, 0x3b, 0x74, 0xa7, 0x8d, 0xb1, 0x87, 0x9a, 0x70, 0xc1, 0xb4, + 0x14, 0xd7, 0xc3, 0xa6, 0x86, 0x1d, 0x4d, 0x99, 0x3e, 0x51, 0x29, 0x58, 0x55, 0x89, 0xeb, 0x5a, + 0xfc, 0x0c, 0x0c, 0xbd, 0xbc, 0x6b, 0x5a, 0x3d, 0x1f, 0x3c, 0x3d, 0x1c, 0xaa, 0x3e, 0x74, 0x86, + 0xb9, 0xa9, 0x65, 0xcc, 0x7d, 0x07, 0xf2, 0x63, 0x6c, 0x2b, 0xc4, 0xf4, 0x9c, 0x53, 0xd6, 0x71, + 0xe7, 0xe4, 0xdc, 0x18, 0xdb, 0x12, 0x1d, 0xbf, 0x99, 0x8b, 0xcf, 0x3f, 0x52, 0x50, 0x8c, 0x76, + 0xdd, 0xf4, 0x12, 0xa3, 0xb2, 0x03, 0x2a, 0xc1, 0x4a, 0xd8, 0x07, 0x2f, 0xed, 0xd1, 0x2b, 0x35, + 0x7a, 0x72, 0x1d, 0x64, 0x79, 0x2f, 0x2c, 0x73, 0x4b, 0xda, 0x35, 0x50, 0x6a, 0x11, 0xde, 0x7b, + 0xe4, 0x64, 0x7f, 0x84, 0x8e, 0x20, 0xfb, 0xc8, 0x65, 0xbe, 0xb3, 0xcc, 0xf7, 0x87, 0x2f, 0xf7, + 0x7d, 0xbf, 0xc7, 0x9c, 0xe7, 0xef, 0xf7, 0x94, 0x76, 0x47, 0x6e, 0x55, 0x9b, 0xb2, 0x6f, 0x8e, + 0xce, 0x43, 0xda, 0xc0, 0xcf, 0x4f, 0xe3, 0x67, 0x1c, 0x13, 0x9d, 0x35, 0xf1, 0xe7, 0x21, 0xfd, + 0x94, 0xe0, 0xc7, 0xf1, 0x93, 0x85, 0x89, 0x5e, 0x23, 0xf5, 0xaf, 0x43, 0x86, 0xe5, 0x0b, 0x01, + 0xf8, 0x19, 0x13, 0xde, 0x42, 0x39, 0x48, 0xd7, 0x3a, 0x32, 0xa5, 0xbf, 0x00, 0x45, 0x2e, 0x55, + 0xba, 0x0d, 0xa9, 0x26, 0x09, 0xc9, 0xf2, 0x2d, 0xc8, 0xf2, 0x24, 0xd0, 0xad, 0x11, 0xa6, 0x41, + 0x78, 0xcb, 0x1f, 0xfa, 0x3e, 0x12, 0x81, 0xf6, 0xb8, 0x75, 0x28, 0xc9, 0x42, 0x32, 0xfa, 0x79, + 0x5d, 0x28, 0x46, 0x1b, 0xee, 0x37, 0xc3, 0xa9, 0xbf, 0x24, 0xa0, 0x10, 0x69, 0xa0, 0x69, 0xe7, + 0x83, 0x0d, 0xc3, 0x7a, 0xaa, 0x60, 0x43, 0xc7, 0xae, 0x4f, 0x0a, 0x60, 0xa2, 0x2a, 0x95, 0x9c, + 0xf5, 0xa3, 0xbd, 0x91, 0xe0, 0x7f, 0x9f, 0x00, 0x61, 0xb6, 0x77, 0x9d, 0x09, 0x30, 0xf1, 0xbd, + 0x06, 0xf8, 0xbb, 0x04, 0x94, 0xe2, 0x0d, 0xeb, 0x4c, 0x78, 0x17, 0xbf, 0xd7, 0xf0, 0xfe, 0x99, + 0x84, 0xd5, 0x58, 0x9b, 0x7a, 0xd6, 0xe8, 0xbe, 0x86, 0x75, 0x5d, 0x23, 0x63, 0xdb, 0xf2, 0x88, + 0xa9, 0x9e, 0x2a, 0x06, 0x79, 0x42, 0x0c, 0xb1, 0xcc, 0x0a, 0xc5, 0xf5, 0x97, 0x37, 0xc2, 0x95, + 0xc6, 0xd4, 0xae, 0x49, 0xcd, 0x0e, 0x36, 0x1a, 0x75, 0xa9, 0xd5, 0xed, 0xf4, 0xa5, 0x76, 0xed, + 0xa1, 0x72, 0xdc, 0xfe, 0x71, 0xbb, 0xf3, 0x65, 0x5b, 0x16, 0xf4, 0x19, 0xd8, 0x6b, 0xdc, 0xea, + 0x5d, 0x10, 0x66, 0x83, 0x42, 0xe7, 0x60, 0x51, 0x58, 0xc2, 0x5b, 0x68, 0x03, 0xd6, 0xda, 0x1d, + 0xa5, 0xd7, 0xa8, 0x4b, 0x8a, 0x74, 0xef, 0x9e, 0x54, 0xeb, 0xf7, 0xf8, 0xd3, 0x46, 0x88, 0xee, + 0xc7, 0x37, 0xf5, 0x6f, 0x53, 0xb0, 0xb1, 0x20, 0x12, 0x54, 0xf5, 0x2f, 0x25, 0xfc, 0x9e, 0xf4, + 0xc9, 0x59, 0xa2, 0xaf, 0xd0, 0xae, 0xa0, 0x8b, 0x1d, 0xcf, 0xbf, 0xc3, 0x5c, 0x01, 0x9a, 0x25, + 0xd3, 0xd3, 0x07, 0x3a, 0x71, 0xfc, 0x97, 0x20, 0x7e, 0x53, 0x59, 0x9b, 0xca, 0xf9, 0x63, 0xd0, + 0xc7, 0x80, 0x6c, 0xcb, 0xd5, 0x3d, 0xfd, 0x09, 0x51, 0x74, 0x33, 0x78, 0x36, 0xa2, 0x37, 0x97, + 0xb4, 0x2c, 0x04, 0x9a, 0x86, 0xe9, 0x85, 0x68, 0x93, 0x0c, 0xf1, 0x0c, 0x9a, 0x16, 0xf0, 0x94, + 0x2c, 0x04, 0x9a, 0x10, 0x7d, 0x11, 0x8a, 0x9a, 0x35, 0xa1, 0xed, 0x1c, 0xc7, 0xd1, 0xf3, 0x22, + 0x21, 0x17, 0xb8, 0x2c, 0x84, 0xf8, 0x8d, 0xfa, 0xf4, 0xbd, 0xaa, 0x28, 0x17, 0xb8, 0x8c, 0x43, + 0x2e, 0xc3, 0x1a, 0x1e, 0x0e, 0x1d, 0xea, 0x3c, 0x70, 0xc4, 0xaf, 0x1e, 0xa5, 0x50, 0xcc, 0x80, + 0xdb, 0xf7, 0x21, 0x17, 0xe4, 0x81, 0x1e, 0xc9, 0x34, 0x13, 0x8a, 0xcd, 0xef, 0xd3, 0xc9, 0xdd, + 0xbc, 0x9c, 0x33, 0x03, 0xe5, 0x45, 0x28, 0xea, 0xae, 0x32, 0x7d, 0x7e, 0x4f, 0xee, 0x24, 0x77, + 0x73, 0x72, 0x41, 0x77, 0xc3, 0xa7, 0xcb, 0xf2, 0x37, 0x49, 0x28, 0xc5, 0x7f, 0x3e, 0x40, 0x75, + 0xc8, 0x19, 0x96, 0x8a, 0x19, 0xb5, 0xf8, 0x6f, 0x57, 0xbb, 0xaf, 0xf8, 0xc5, 0xa1, 0xd2, 0xf4, + 0xf1, 0x72, 0x68, 0xb9, 0xfd, 0xb7, 0x04, 0xe4, 0x02, 0x31, 0xda, 0x82, 0xb4, 0x8d, 0xbd, 0x11, + 0x73, 0x97, 0x39, 0x4c, 0x0a, 0x09, 0x99, 0x8d, 0xa9, 0xdc, 0xb5, 0xb1, 0xc9, 0x28, 0xe0, 0xcb, + 0xe9, 0x98, 0x7e, 0x57, 0x83, 0x60, 0x8d, 0xdd, 0x6b, 0xac, 0xf1, 0x98, 0x98, 0x9e, 0x1b, 0x7c, + 0x57, 0x5f, 0x5e, 0xf3, 0xc5, 0xe8, 0x1a, 0xac, 0x7b, 0x0e, 0xd6, 0x8d, 0x18, 0x36, 0xcd, 0xb0, + 0x42, 0xa0, 0x08, 0xc1, 0x07, 0x70, 0x3e, 0xf0, 0xab, 0x11, 0x0f, 0xab, 0x23, 0xa2, 0x4d, 0x8d, + 0xb2, 0xec, 0xfd, 0xe2, 0x9c, 0x0f, 0xa8, 0xfb, 0xfa, 0xc0, 0xb6, 0xfc, 0xf7, 0x04, 0xac, 0x07, + 0x37, 0x31, 0x2d, 0x4c, 0x56, 0x0b, 0x00, 0x9b, 0xa6, 0xe5, 0x45, 0xd3, 0x35, 0x4f, 0xe5, 0x39, + 0xbb, 0x4a, 0x35, 0x34, 0x92, 0x23, 0x0e, 0xb6, 0xc7, 0x00, 0x53, 0xcd, 0xd2, 0xb4, 0x5d, 0x80, + 0x82, 0xff, 0xdb, 0x10, 0xfb, 0x81, 0x91, 0xdf, 0xdd, 0x81, 0x8b, 0xe8, 0x95, 0x0d, 0x6d, 0x42, + 0xe6, 0x84, 0x0c, 0x75, 0xd3, 0x7f, 0xf1, 0xe5, 0x83, 0xe0, 0x85, 0x25, 0x1d, 0xbe, 0xb0, 0x1c, + 0xfe, 0x0c, 0x36, 0x54, 0x6b, 0x3c, 0x1b, 0xee, 0xa1, 0x30, 0xf3, 0x7e, 0xe0, 0x7e, 0x91, 0xf8, + 0x0a, 0xa6, 0x2d, 0xe6, 0xff, 0x12, 0x89, 0x3f, 0x24, 0x53, 0x47, 0xdd, 0xc3, 0x3f, 0x26, 0xb7, + 0x8f, 0xb8, 0x69, 0x37, 0x58, 0xa9, 0x4c, 0x06, 0x06, 0x51, 0x69, 0xf4, 0xff, 0x0f, 0x00, 0x00, + 0xff, 0xff, 0x88, 0x17, 0xc1, 0xbe, 0x38, 0x1d, 0x00, 0x00, } diff --git a/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor_gostring.gen.go b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor_gostring.gen.go index 3b95a775754b5..165b2110df8ba 100644 --- a/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor_gostring.gen.go +++ b/vendor/github.com/gogo/protobuf/protoc-gen-gogo/descriptor/descriptor_gostring.gen.go @@ -1,44 +1,18 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: descriptor.proto -/* -Package descriptor is a generated protocol buffer package. - -It is generated from these files: - descriptor.proto - -It has these top-level messages: - FileDescriptorSet - FileDescriptorProto - DescriptorProto - ExtensionRangeOptions - FieldDescriptorProto - OneofDescriptorProto - EnumDescriptorProto - EnumValueDescriptorProto - ServiceDescriptorProto - MethodDescriptorProto - FileOptions - MessageOptions - FieldOptions - OneofOptions - EnumOptions - EnumValueOptions - ServiceOptions - MethodOptions - UninterpretedOption - SourceCodeInfo - GeneratedCodeInfo -*/ package descriptor -import fmt "fmt" -import strings "strings" -import proto "github.com/gogo/protobuf/proto" -import sort "sort" -import strconv "strconv" -import reflect "reflect" -import math "math" +import ( + fmt "fmt" + github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" + proto "github.com/gogo/protobuf/proto" + math "math" + reflect "reflect" + sort "sort" + strconv "strconv" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -386,7 +360,7 @@ func (this *FileOptions) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 23) + s := make([]string, 0, 25) s = append(s, "&descriptor.FileOptions{") if this.JavaPackage != nil { s = append(s, "JavaPackage: "+valueToGoStringDescriptor(this.JavaPackage, "string")+",\n") @@ -442,6 +416,12 @@ func (this *FileOptions) GoString() string { if this.PhpNamespace != nil { s = append(s, "PhpNamespace: "+valueToGoStringDescriptor(this.PhpNamespace, "string")+",\n") } + if this.PhpMetadataNamespace != nil { + s = append(s, "PhpMetadataNamespace: "+valueToGoStringDescriptor(this.PhpMetadataNamespace, "string")+",\n") + } + if this.RubyPackage != nil { + s = append(s, "RubyPackage: "+valueToGoStringDescriptor(this.RubyPackage, "string")+",\n") + } if this.UninterpretedOption != nil { s = append(s, "UninterpretedOption: "+fmt.Sprintf("%#v", this.UninterpretedOption)+",\n") } @@ -752,8 +732,8 @@ func valueToGoStringDescriptor(v interface{}, typ string) string { pv := reflect.Indirect(rv).Interface() return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) } -func extensionToGoStringDescriptor(m proto.Message) string { - e := proto.GetUnsafeExtensionsMap(m) +func extensionToGoStringDescriptor(m github_com_gogo_protobuf_proto.Message) string { + e := github_com_gogo_protobuf_proto.GetUnsafeExtensionsMap(m) if e == nil { return "nil" } diff --git a/vendor/github.com/gogo/protobuf/types/any.go b/vendor/github.com/gogo/protobuf/types/any.go index d83c3ad0076a9..df4787de37c52 100644 --- a/vendor/github.com/gogo/protobuf/types/any.go +++ b/vendor/github.com/gogo/protobuf/types/any.go @@ -129,10 +129,12 @@ func UnmarshalAny(any *Any, pb proto.Message) error { // Is returns true if any value contains a given message type. func Is(any *Any, pb proto.Message) bool { - aname, err := AnyMessageName(any) - if err != nil { + // The following is equivalent to AnyMessageName(any) == proto.MessageName(pb), + // but it avoids scanning TypeUrl for the slash. + if any == nil { return false } - - return aname == proto.MessageName(pb) + name := proto.MessageName(pb) + prefix := len(any.TypeUrl) - len(name) + return prefix >= 1 && any.TypeUrl[prefix-1] == '/' && any.TypeUrl[prefix:] == name } diff --git a/vendor/github.com/gogo/protobuf/types/any.pb.go b/vendor/github.com/gogo/protobuf/types/any.pb.go index 4b5f5705a9ca6..e3d4d9490f5e3 100644 --- a/vendor/github.com/gogo/protobuf/types/any.pb.go +++ b/vendor/github.com/gogo/protobuf/types/any.pb.go @@ -1,27 +1,18 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: any.proto +// source: google/protobuf/any.proto -/* - Package types is a generated protocol buffer package. - - It is generated from these files: - any.proto - - It has these top-level messages: - Any -*/ package types -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -import bytes "bytes" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -32,7 +23,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // `Any` contains an arbitrary serialized protocol buffer message along with a // URL that describes the type of the serialized message. @@ -115,17 +106,19 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package // } // type Any struct { - // A URL/resource name whose content describes the type of the - // serialized protocol buffer message. + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). // - // For URLs which use the scheme `http`, `https`, or no scheme, the - // following restrictions and interpretations apply: + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: // // * If no scheme is provided, `https` is assumed. - // * The last segment of the URL's path must represent the fully - // qualified name of the type (as in `path/google.protobuf.Duration`). - // The name should be in a canonical form (e.g., leading "." is - // not accepted). // * An HTTP GET on the URL must yield a [google.protobuf.Type][] // value in binary format, or produce an error. // * Applications are allowed to cache lookup results based on the @@ -134,18 +127,53 @@ type Any struct { // on changes to types. (Use versioned type names to manage // breaking changes.) // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // // Schemes other than `http`, `https` (or the empty scheme) might be // used with implementation specific semantics. // TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"` // Must be a valid serialized protocol buffer of the above specified type. - Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *Any) Reset() { *m = Any{} } -func (*Any) ProtoMessage() {} -func (*Any) Descriptor() ([]byte, []int) { return fileDescriptorAny, []int{0} } -func (*Any) XXX_WellKnownType() string { return "Any" } +func (m *Any) Reset() { *m = Any{} } +func (*Any) ProtoMessage() {} +func (*Any) Descriptor() ([]byte, []int) { + return fileDescriptor_b53526c13ae22eb4, []int{0} +} +func (*Any) XXX_WellKnownType() string { return "Any" } +func (m *Any) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Any) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Any.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Any) XXX_Merge(src proto.Message) { + xxx_messageInfo_Any.Merge(m, src) +} +func (m *Any) XXX_Size() int { + return m.Size() +} +func (m *Any) XXX_DiscardUnknown() { + xxx_messageInfo_Any.DiscardUnknown(m) +} + +var xxx_messageInfo_Any proto.InternalMessageInfo func (m *Any) GetTypeUrl() string { if m != nil { @@ -161,9 +189,33 @@ func (m *Any) GetValue() []byte { return nil } +func (*Any) XXX_MessageName() string { + return "google.protobuf.Any" +} func init() { proto.RegisterType((*Any)(nil), "google.protobuf.Any") } + +func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4) } + +var fileDescriptor_b53526c13ae22eb4 = []byte{ + // 211 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4, + 0x03, 0x73, 0x84, 0xf8, 0x21, 0x52, 0x7a, 0x30, 0x29, 0x25, 0x33, 0x2e, 0x66, 0xc7, 0xbc, 0x4a, + 0x21, 0x49, 0x2e, 0x8e, 0x92, 0xca, 0x82, 0xd4, 0xf8, 0xd2, 0xa2, 0x1c, 0x09, 0x46, 0x05, 0x46, + 0x0d, 0xce, 0x20, 0x76, 0x10, 0x3f, 0xb4, 0x28, 0x47, 0x48, 0x84, 0x8b, 0xb5, 0x2c, 0x31, 0xa7, + 0x34, 0x55, 0x82, 0x49, 0x81, 0x51, 0x83, 0x27, 0x08, 0xc2, 0x71, 0xaa, 0xbf, 0xf1, 0x50, 0x8e, + 0xe1, 0xc3, 0x43, 0x39, 0xc6, 0x1f, 0x0f, 0xe5, 0x18, 0x1b, 0x1e, 0xc9, 0x31, 0xae, 0x78, 0x24, + 0xc7, 0x78, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0xbe, 0x78, + 0x24, 0xc7, 0xf0, 0x01, 0x24, 0xfe, 0x58, 0x8e, 0xf1, 0xc4, 0x63, 0x39, 0x46, 0x2e, 0xe1, 0xe4, + 0xfc, 0x5c, 0x3d, 0x34, 0xeb, 0x9d, 0x38, 0x1c, 0xf3, 0x2a, 0x03, 0x40, 0x9c, 0x00, 0xc6, 0x28, + 0x56, 0x90, 0x8d, 0xc5, 0x8b, 0x98, 0x98, 0xdd, 0x03, 0x9c, 0x56, 0x31, 0xc9, 0xb9, 0x43, 0x94, + 0x06, 0x40, 0x95, 0xea, 0x85, 0xa7, 0xe6, 0xe4, 0x78, 0xe7, 0xe5, 0x97, 0xe7, 0x85, 0x80, 0x94, + 0x25, 0xb1, 0x81, 0xcd, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb7, 0x81, 0x82, 0xd3, 0xed, + 0x00, 0x00, 0x00, +} + func (this *Any) Compare(that interface{}) int { if that == nil { if this == nil { @@ -198,6 +250,9 @@ func (this *Any) Compare(that interface{}) int { if c := bytes.Compare(this.Value, that1.Value); c != 0 { return c } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *Any) Equal(that interface{}) bool { @@ -225,6 +280,9 @@ func (this *Any) Equal(that interface{}) bool { if !bytes.Equal(this.Value, that1.Value) { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *Any) GoString() string { @@ -235,6 +293,9 @@ func (this *Any) GoString() string { s = append(s, "&types.Any{") s = append(s, "TypeUrl: "+fmt.Sprintf("%#v", this.TypeUrl)+",\n") s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -249,7 +310,7 @@ func valueToGoStringAny(v interface{}, typ string) string { func (m *Any) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -257,33 +318,46 @@ func (m *Any) Marshal() (dAtA []byte, err error) { } func (m *Any) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Any) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if len(m.TypeUrl) > 0 { - dAtA[i] = 0xa - i++ - i = encodeVarintAny(dAtA, i, uint64(len(m.TypeUrl))) - i += copy(dAtA[i:], m.TypeUrl) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if len(m.Value) > 0 { - dAtA[i] = 0x12 - i++ + i -= len(m.Value) + copy(dAtA[i:], m.Value) i = encodeVarintAny(dAtA, i, uint64(len(m.Value))) - i += copy(dAtA[i:], m.Value) + i-- + dAtA[i] = 0x12 } - return i, nil + if len(m.TypeUrl) > 0 { + i -= len(m.TypeUrl) + copy(dAtA[i:], m.TypeUrl) + i = encodeVarintAny(dAtA, i, uint64(len(m.TypeUrl))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func encodeVarintAny(dAtA []byte, offset int, v uint64) int { + offset -= sovAny(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func NewPopulatedAny(r randyAny, easy bool) *Any { this := &Any{} @@ -294,6 +368,7 @@ func NewPopulatedAny(r randyAny, easy bool) *Any { this.Value[i] = byte(r.Intn(256)) } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedAny(r, 3) } return this } @@ -371,6 +446,9 @@ func encodeVarintPopulateAny(dAtA []byte, v uint64) []byte { return dAtA } func (m *Any) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.TypeUrl) @@ -381,18 +459,14 @@ func (m *Any) Size() (n int) { if l > 0 { n += 1 + l + sovAny(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovAny(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozAny(x uint64) (n int) { return sovAny(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -404,6 +478,7 @@ func (this *Any) String() string { s := strings.Join([]string{`&Any{`, `TypeUrl:` + fmt.Sprintf("%v", this.TypeUrl) + `,`, `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -431,7 +506,7 @@ func (m *Any) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -459,7 +534,7 @@ func (m *Any) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -469,6 +544,9 @@ func (m *Any) Unmarshal(dAtA []byte) error { return ErrInvalidLengthAny } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAny + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -488,7 +566,7 @@ func (m *Any) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -497,6 +575,9 @@ func (m *Any) Unmarshal(dAtA []byte) error { return ErrInvalidLengthAny } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAny + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -511,12 +592,13 @@ func (m *Any) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthAny } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -529,6 +611,7 @@ func (m *Any) Unmarshal(dAtA []byte) error { func skipAny(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -560,10 +643,8 @@ func skipAny(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -580,72 +661,34 @@ func skipAny(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthAny } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowAny - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipAny(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAny + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthAny + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthAny = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowAny = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthAny = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAny = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAny = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("any.proto", fileDescriptorAny) } - -var fileDescriptorAny = []byte{ - // 204 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4c, 0xcc, 0xab, 0xd4, - 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4f, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0x85, 0xf0, 0x92, - 0x4a, 0xd3, 0x94, 0xcc, 0xb8, 0x98, 0x1d, 0xf3, 0x2a, 0x85, 0x24, 0xb9, 0x38, 0x4a, 0x2a, 0x0b, - 0x52, 0xe3, 0x4b, 0x8b, 0x72, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xd8, 0x41, 0xfc, 0xd0, - 0xa2, 0x1c, 0x21, 0x11, 0x2e, 0xd6, 0xb2, 0xc4, 0x9c, 0xd2, 0x54, 0x09, 0x26, 0x05, 0x46, 0x0d, - 0x9e, 0x20, 0x08, 0xc7, 0xa9, 0xfe, 0xc2, 0x43, 0x39, 0x86, 0x1b, 0x0f, 0xe5, 0x18, 0x3e, 0x3c, - 0x94, 0x63, 0xfc, 0xf1, 0x50, 0x8e, 0xb1, 0xe1, 0x91, 0x1c, 0xe3, 0x8a, 0x47, 0x72, 0x8c, 0x27, - 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x8b, 0x47, 0x72, 0x0c, - 0x1f, 0x40, 0xe2, 0x8f, 0xe5, 0x18, 0xb9, 0x84, 0x93, 0xf3, 0x73, 0xf5, 0xd0, 0xac, 0x77, 0xe2, - 0x70, 0xcc, 0xab, 0x0c, 0x00, 0x71, 0x02, 0x18, 0xa3, 0x58, 0x41, 0x36, 0x16, 0x2f, 0x62, 0x62, - 0x76, 0x0f, 0x70, 0x5a, 0xc5, 0x24, 0xe7, 0x0e, 0x51, 0x1a, 0x00, 0x55, 0xaa, 0x17, 0x9e, 0x9a, - 0x93, 0xe3, 0x9d, 0x97, 0x5f, 0x9e, 0x17, 0x02, 0x52, 0x96, 0xc4, 0x06, 0x36, 0xc3, 0x18, 0x10, - 0x00, 0x00, 0xff, 0xff, 0xb7, 0x39, 0x2f, 0x89, 0xdd, 0x00, 0x00, 0x00, -} diff --git a/vendor/github.com/gogo/protobuf/types/api.pb.go b/vendor/github.com/gogo/protobuf/types/api.pb.go new file mode 100644 index 0000000000000..83e8869206fe2 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/types/api.pb.go @@ -0,0 +1,2134 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: google/protobuf/api.proto + +package types + +import ( + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Api is a light-weight descriptor for an API Interface. +// +// Interfaces are also described as "protocol buffer services" in some contexts, +// such as by the "service" keyword in a .proto file, but they are different +// from API Services, which represent a concrete implementation of an interface +// as opposed to simply a description of methods and bindings. They are also +// sometimes simply referred to as "APIs" in other contexts, such as the name of +// this message itself. See https://cloud.google.com/apis/design/glossary for +// detailed terminology. +type Api struct { + // The fully qualified name of this interface, including package name + // followed by the interface's simple name. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The methods of this interface, in unspecified order. + Methods []*Method `protobuf:"bytes,2,rep,name=methods,proto3" json:"methods,omitempty"` + // Any metadata attached to the interface. + Options []*Option `protobuf:"bytes,3,rep,name=options,proto3" json:"options,omitempty"` + // A version string for this interface. If specified, must have the form + // `major-version.minor-version`, as in `1.10`. If the minor version is + // omitted, it defaults to zero. If the entire version field is empty, the + // major version is derived from the package name, as outlined below. If the + // field is not empty, the version in the package name will be verified to be + // consistent with what is provided here. + // + // The versioning schema uses [semantic + // versioning](http://semver.org) where the major version number + // indicates a breaking change and the minor version an additive, + // non-breaking change. Both version numbers are signals to users + // what to expect from different versions, and should be carefully + // chosen based on the product plan. + // + // The major version is also reflected in the package name of the + // interface, which must end in `v`, as in + // `google.feature.v1`. For major versions 0 and 1, the suffix can + // be omitted. Zero major versions must only be used for + // experimental, non-GA interfaces. + // + // + Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + // Source context for the protocol buffer service represented by this + // message. + SourceContext *SourceContext `protobuf:"bytes,5,opt,name=source_context,json=sourceContext,proto3" json:"source_context,omitempty"` + // Included interfaces. See [Mixin][]. + Mixins []*Mixin `protobuf:"bytes,6,rep,name=mixins,proto3" json:"mixins,omitempty"` + // The source syntax of the service. + Syntax Syntax `protobuf:"varint,7,opt,name=syntax,proto3,enum=google.protobuf.Syntax" json:"syntax,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Api) Reset() { *m = Api{} } +func (*Api) ProtoMessage() {} +func (*Api) Descriptor() ([]byte, []int) { + return fileDescriptor_a2ec32096296c143, []int{0} +} +func (m *Api) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Api) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Api.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Api) XXX_Merge(src proto.Message) { + xxx_messageInfo_Api.Merge(m, src) +} +func (m *Api) XXX_Size() int { + return m.Size() +} +func (m *Api) XXX_DiscardUnknown() { + xxx_messageInfo_Api.DiscardUnknown(m) +} + +var xxx_messageInfo_Api proto.InternalMessageInfo + +func (m *Api) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Api) GetMethods() []*Method { + if m != nil { + return m.Methods + } + return nil +} + +func (m *Api) GetOptions() []*Option { + if m != nil { + return m.Options + } + return nil +} + +func (m *Api) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *Api) GetSourceContext() *SourceContext { + if m != nil { + return m.SourceContext + } + return nil +} + +func (m *Api) GetMixins() []*Mixin { + if m != nil { + return m.Mixins + } + return nil +} + +func (m *Api) GetSyntax() Syntax { + if m != nil { + return m.Syntax + } + return Syntax_SYNTAX_PROTO2 +} + +func (*Api) XXX_MessageName() string { + return "google.protobuf.Api" +} + +// Method represents a method of an API interface. +type Method struct { + // The simple name of this method. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // A URL of the input message type. + RequestTypeUrl string `protobuf:"bytes,2,opt,name=request_type_url,json=requestTypeUrl,proto3" json:"request_type_url,omitempty"` + // If true, the request is streamed. + RequestStreaming bool `protobuf:"varint,3,opt,name=request_streaming,json=requestStreaming,proto3" json:"request_streaming,omitempty"` + // The URL of the output message type. + ResponseTypeUrl string `protobuf:"bytes,4,opt,name=response_type_url,json=responseTypeUrl,proto3" json:"response_type_url,omitempty"` + // If true, the response is streamed. + ResponseStreaming bool `protobuf:"varint,5,opt,name=response_streaming,json=responseStreaming,proto3" json:"response_streaming,omitempty"` + // Any metadata attached to the method. + Options []*Option `protobuf:"bytes,6,rep,name=options,proto3" json:"options,omitempty"` + // The source syntax of this method. + Syntax Syntax `protobuf:"varint,7,opt,name=syntax,proto3,enum=google.protobuf.Syntax" json:"syntax,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Method) Reset() { *m = Method{} } +func (*Method) ProtoMessage() {} +func (*Method) Descriptor() ([]byte, []int) { + return fileDescriptor_a2ec32096296c143, []int{1} +} +func (m *Method) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Method) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Method.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Method) XXX_Merge(src proto.Message) { + xxx_messageInfo_Method.Merge(m, src) +} +func (m *Method) XXX_Size() int { + return m.Size() +} +func (m *Method) XXX_DiscardUnknown() { + xxx_messageInfo_Method.DiscardUnknown(m) +} + +var xxx_messageInfo_Method proto.InternalMessageInfo + +func (m *Method) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Method) GetRequestTypeUrl() string { + if m != nil { + return m.RequestTypeUrl + } + return "" +} + +func (m *Method) GetRequestStreaming() bool { + if m != nil { + return m.RequestStreaming + } + return false +} + +func (m *Method) GetResponseTypeUrl() string { + if m != nil { + return m.ResponseTypeUrl + } + return "" +} + +func (m *Method) GetResponseStreaming() bool { + if m != nil { + return m.ResponseStreaming + } + return false +} + +func (m *Method) GetOptions() []*Option { + if m != nil { + return m.Options + } + return nil +} + +func (m *Method) GetSyntax() Syntax { + if m != nil { + return m.Syntax + } + return Syntax_SYNTAX_PROTO2 +} + +func (*Method) XXX_MessageName() string { + return "google.protobuf.Method" +} + +// Declares an API Interface to be included in this interface. The including +// interface must redeclare all the methods from the included interface, but +// documentation and options are inherited as follows: +// +// - If after comment and whitespace stripping, the documentation +// string of the redeclared method is empty, it will be inherited +// from the original method. +// +// - Each annotation belonging to the service config (http, +// visibility) which is not set in the redeclared method will be +// inherited. +// +// - If an http annotation is inherited, the path pattern will be +// modified as follows. Any version prefix will be replaced by the +// version of the including interface plus the [root][] path if +// specified. +// +// Example of a simple mixin: +// +// package google.acl.v1; +// service AccessControl { +// // Get the underlying ACL object. +// rpc GetAcl(GetAclRequest) returns (Acl) { +// option (google.api.http).get = "/v1/{resource=**}:getAcl"; +// } +// } +// +// package google.storage.v2; +// service Storage { +// rpc GetAcl(GetAclRequest) returns (Acl); +// +// // Get a data record. +// rpc GetData(GetDataRequest) returns (Data) { +// option (google.api.http).get = "/v2/{resource=**}"; +// } +// } +// +// Example of a mixin configuration: +// +// apis: +// - name: google.storage.v2.Storage +// mixins: +// - name: google.acl.v1.AccessControl +// +// The mixin construct implies that all methods in `AccessControl` are +// also declared with same name and request/response types in +// `Storage`. A documentation generator or annotation processor will +// see the effective `Storage.GetAcl` method after inherting +// documentation and annotations as follows: +// +// service Storage { +// // Get the underlying ACL object. +// rpc GetAcl(GetAclRequest) returns (Acl) { +// option (google.api.http).get = "/v2/{resource=**}:getAcl"; +// } +// ... +// } +// +// Note how the version in the path pattern changed from `v1` to `v2`. +// +// If the `root` field in the mixin is specified, it should be a +// relative path under which inherited HTTP paths are placed. Example: +// +// apis: +// - name: google.storage.v2.Storage +// mixins: +// - name: google.acl.v1.AccessControl +// root: acls +// +// This implies the following inherited HTTP annotation: +// +// service Storage { +// // Get the underlying ACL object. +// rpc GetAcl(GetAclRequest) returns (Acl) { +// option (google.api.http).get = "/v2/acls/{resource=**}:getAcl"; +// } +// ... +// } +type Mixin struct { + // The fully qualified name of the interface which is included. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // If non-empty specifies a path under which inherited HTTP paths + // are rooted. + Root string `protobuf:"bytes,2,opt,name=root,proto3" json:"root,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Mixin) Reset() { *m = Mixin{} } +func (*Mixin) ProtoMessage() {} +func (*Mixin) Descriptor() ([]byte, []int) { + return fileDescriptor_a2ec32096296c143, []int{2} +} +func (m *Mixin) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Mixin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Mixin.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Mixin) XXX_Merge(src proto.Message) { + xxx_messageInfo_Mixin.Merge(m, src) +} +func (m *Mixin) XXX_Size() int { + return m.Size() +} +func (m *Mixin) XXX_DiscardUnknown() { + xxx_messageInfo_Mixin.DiscardUnknown(m) +} + +var xxx_messageInfo_Mixin proto.InternalMessageInfo + +func (m *Mixin) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Mixin) GetRoot() string { + if m != nil { + return m.Root + } + return "" +} + +func (*Mixin) XXX_MessageName() string { + return "google.protobuf.Mixin" +} +func init() { + proto.RegisterType((*Api)(nil), "google.protobuf.Api") + proto.RegisterType((*Method)(nil), "google.protobuf.Method") + proto.RegisterType((*Mixin)(nil), "google.protobuf.Mixin") +} + +func init() { proto.RegisterFile("google/protobuf/api.proto", fileDescriptor_a2ec32096296c143) } + +var fileDescriptor_a2ec32096296c143 = []byte{ + // 467 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0x31, 0x6f, 0x13, 0x31, + 0x14, 0xc7, 0xeb, 0xbb, 0xe4, 0x52, 0x5c, 0x91, 0x82, 0x91, 0xc0, 0x64, 0xb0, 0x4e, 0x15, 0xc3, + 0x09, 0xc4, 0x45, 0x94, 0x4f, 0xd0, 0x20, 0xd4, 0x01, 0x21, 0xa2, 0x0b, 0x08, 0x89, 0x25, 0x4a, + 0x83, 0x09, 0x96, 0xee, 0x6c, 0x63, 0x3b, 0x90, 0x4c, 0xf0, 0x59, 0x98, 0x10, 0x23, 0xdf, 0x80, + 0xad, 0x23, 0x23, 0x23, 0xb9, 0x2e, 0x8c, 0x1d, 0x19, 0x91, 0x7d, 0xe7, 0xa6, 0x5c, 0x83, 0x04, + 0x9b, 0xdf, 0xfb, 0xff, 0xfc, 0xf7, 0x7b, 0x7f, 0xc3, 0x9b, 0x33, 0x21, 0x66, 0x39, 0xed, 0x4b, + 0x25, 0x8c, 0x38, 0x9a, 0xbf, 0xea, 0x4f, 0x24, 0x4b, 0x5d, 0x81, 0x76, 0x2b, 0x29, 0xf5, 0x52, + 0xef, 0x56, 0x93, 0xd5, 0x62, 0xae, 0xa6, 0x74, 0x3c, 0x15, 0xdc, 0xd0, 0x85, 0xa9, 0xc0, 0x5e, + 0xaf, 0x49, 0x99, 0xa5, 0xac, 0x4d, 0xf6, 0xbe, 0x06, 0x30, 0x3c, 0x90, 0x0c, 0x21, 0xd8, 0xe2, + 0x93, 0x82, 0x62, 0x10, 0x83, 0xe4, 0x52, 0xe6, 0xce, 0xe8, 0x1e, 0xec, 0x14, 0xd4, 0xbc, 0x16, + 0x2f, 0x35, 0x0e, 0xe2, 0x30, 0xd9, 0xd9, 0xbf, 0x91, 0x36, 0x06, 0x48, 0x1f, 0x3b, 0x3d, 0xf3, + 0x9c, 0xbd, 0x22, 0xa4, 0x61, 0x82, 0x6b, 0x1c, 0xfe, 0xe5, 0xca, 0x13, 0xa7, 0x67, 0x9e, 0x43, + 0x18, 0x76, 0xde, 0x52, 0xa5, 0x99, 0xe0, 0xb8, 0xe5, 0x1e, 0xf7, 0x25, 0x7a, 0x08, 0xbb, 0x7f, + 0xee, 0x83, 0xdb, 0x31, 0x48, 0x76, 0xf6, 0xc9, 0x05, 0xcf, 0x91, 0xc3, 0x1e, 0x54, 0x54, 0x76, + 0x59, 0x9f, 0x2f, 0x51, 0x0a, 0xa3, 0x82, 0x2d, 0x18, 0xd7, 0x38, 0x72, 0x23, 0x5d, 0xbf, 0xb8, + 0x85, 0x95, 0xb3, 0x9a, 0x42, 0x7d, 0x18, 0xe9, 0x25, 0x37, 0x93, 0x05, 0xee, 0xc4, 0x20, 0xe9, + 0x6e, 0x58, 0x61, 0xe4, 0xe4, 0xac, 0xc6, 0xf6, 0xbe, 0x04, 0x30, 0xaa, 0x82, 0xd8, 0x18, 0x63, + 0x02, 0xaf, 0x28, 0xfa, 0x66, 0x4e, 0xb5, 0x19, 0xdb, 0xe0, 0xc7, 0x73, 0x95, 0xe3, 0xc0, 0xe9, + 0xdd, 0xba, 0xff, 0x74, 0x29, 0xe9, 0x33, 0x95, 0xa3, 0x3b, 0xf0, 0xaa, 0x27, 0xb5, 0x51, 0x74, + 0x52, 0x30, 0x3e, 0xc3, 0x61, 0x0c, 0x92, 0xed, 0xcc, 0x5b, 0x8c, 0x7c, 0x1f, 0xdd, 0xb6, 0xb0, + 0x96, 0x82, 0x6b, 0xba, 0xf6, 0xad, 0x12, 0xdc, 0xf5, 0x82, 0x37, 0xbe, 0x0b, 0xd1, 0x19, 0xbb, + 0x76, 0x6e, 0x3b, 0xe7, 0x33, 0x97, 0xb5, 0xf5, 0xb9, 0x5f, 0x8c, 0xfe, 0xf1, 0x17, 0xff, 0x3b, + 0xb4, 0x3e, 0x6c, 0xbb, 0xd8, 0x37, 0x46, 0x86, 0x60, 0x4b, 0x09, 0x61, 0xea, 0x98, 0xdc, 0x79, + 0xf0, 0xfe, 0xfb, 0x8a, 0x6c, 0x9d, 0xae, 0x08, 0xf8, 0xb5, 0x22, 0xe0, 0x43, 0x49, 0xc0, 0xa7, + 0x92, 0x80, 0xe3, 0x92, 0x80, 0x6f, 0x25, 0x01, 0x3f, 0x4a, 0x02, 0x7e, 0x96, 0x64, 0xeb, 0xd4, + 0xf6, 0x4f, 0x08, 0x38, 0x3e, 0x21, 0x00, 0x5e, 0x9b, 0x8a, 0xa2, 0x39, 0xc6, 0x60, 0xfb, 0x40, + 0xb2, 0xa1, 0x2d, 0x86, 0xe0, 0x45, 0xdb, 0xe6, 0xa6, 0x3f, 0x06, 0xe1, 0xe1, 0x70, 0xf0, 0x39, + 0x20, 0x87, 0x15, 0x3a, 0xf4, 0x13, 0x3f, 0xa7, 0x79, 0xfe, 0x88, 0x8b, 0x77, 0xdc, 0xc6, 0xa8, + 0x8f, 0x22, 0xe7, 0x71, 0xff, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2b, 0x64, 0x40, 0x40, 0xa1, + 0x03, 0x00, 0x00, +} + +func (this *Api) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Api) + if !ok { + that2, ok := that.(Api) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.Name != that1.Name { + if this.Name < that1.Name { + return -1 + } + return 1 + } + if len(this.Methods) != len(that1.Methods) { + if len(this.Methods) < len(that1.Methods) { + return -1 + } + return 1 + } + for i := range this.Methods { + if c := this.Methods[i].Compare(that1.Methods[i]); c != 0 { + return c + } + } + if len(this.Options) != len(that1.Options) { + if len(this.Options) < len(that1.Options) { + return -1 + } + return 1 + } + for i := range this.Options { + if c := this.Options[i].Compare(that1.Options[i]); c != 0 { + return c + } + } + if this.Version != that1.Version { + if this.Version < that1.Version { + return -1 + } + return 1 + } + if c := this.SourceContext.Compare(that1.SourceContext); c != 0 { + return c + } + if len(this.Mixins) != len(that1.Mixins) { + if len(this.Mixins) < len(that1.Mixins) { + return -1 + } + return 1 + } + for i := range this.Mixins { + if c := this.Mixins[i].Compare(that1.Mixins[i]); c != 0 { + return c + } + } + if this.Syntax != that1.Syntax { + if this.Syntax < that1.Syntax { + return -1 + } + return 1 + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *Method) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Method) + if !ok { + that2, ok := that.(Method) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.Name != that1.Name { + if this.Name < that1.Name { + return -1 + } + return 1 + } + if this.RequestTypeUrl != that1.RequestTypeUrl { + if this.RequestTypeUrl < that1.RequestTypeUrl { + return -1 + } + return 1 + } + if this.RequestStreaming != that1.RequestStreaming { + if !this.RequestStreaming { + return -1 + } + return 1 + } + if this.ResponseTypeUrl != that1.ResponseTypeUrl { + if this.ResponseTypeUrl < that1.ResponseTypeUrl { + return -1 + } + return 1 + } + if this.ResponseStreaming != that1.ResponseStreaming { + if !this.ResponseStreaming { + return -1 + } + return 1 + } + if len(this.Options) != len(that1.Options) { + if len(this.Options) < len(that1.Options) { + return -1 + } + return 1 + } + for i := range this.Options { + if c := this.Options[i].Compare(that1.Options[i]); c != 0 { + return c + } + } + if this.Syntax != that1.Syntax { + if this.Syntax < that1.Syntax { + return -1 + } + return 1 + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *Mixin) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Mixin) + if !ok { + that2, ok := that.(Mixin) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.Name != that1.Name { + if this.Name < that1.Name { + return -1 + } + return 1 + } + if this.Root != that1.Root { + if this.Root < that1.Root { + return -1 + } + return 1 + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *Api) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Api) + if !ok { + that2, ok := that.(Api) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Name != that1.Name { + return false + } + if len(this.Methods) != len(that1.Methods) { + return false + } + for i := range this.Methods { + if !this.Methods[i].Equal(that1.Methods[i]) { + return false + } + } + if len(this.Options) != len(that1.Options) { + return false + } + for i := range this.Options { + if !this.Options[i].Equal(that1.Options[i]) { + return false + } + } + if this.Version != that1.Version { + return false + } + if !this.SourceContext.Equal(that1.SourceContext) { + return false + } + if len(this.Mixins) != len(that1.Mixins) { + return false + } + for i := range this.Mixins { + if !this.Mixins[i].Equal(that1.Mixins[i]) { + return false + } + } + if this.Syntax != that1.Syntax { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *Method) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Method) + if !ok { + that2, ok := that.(Method) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Name != that1.Name { + return false + } + if this.RequestTypeUrl != that1.RequestTypeUrl { + return false + } + if this.RequestStreaming != that1.RequestStreaming { + return false + } + if this.ResponseTypeUrl != that1.ResponseTypeUrl { + return false + } + if this.ResponseStreaming != that1.ResponseStreaming { + return false + } + if len(this.Options) != len(that1.Options) { + return false + } + for i := range this.Options { + if !this.Options[i].Equal(that1.Options[i]) { + return false + } + } + if this.Syntax != that1.Syntax { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *Mixin) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Mixin) + if !ok { + that2, ok := that.(Mixin) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Name != that1.Name { + return false + } + if this.Root != that1.Root { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *Api) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 11) + s = append(s, "&types.Api{") + s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n") + if this.Methods != nil { + s = append(s, "Methods: "+fmt.Sprintf("%#v", this.Methods)+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + s = append(s, "Version: "+fmt.Sprintf("%#v", this.Version)+",\n") + if this.SourceContext != nil { + s = append(s, "SourceContext: "+fmt.Sprintf("%#v", this.SourceContext)+",\n") + } + if this.Mixins != nil { + s = append(s, "Mixins: "+fmt.Sprintf("%#v", this.Mixins)+",\n") + } + s = append(s, "Syntax: "+fmt.Sprintf("%#v", this.Syntax)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *Method) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 11) + s = append(s, "&types.Method{") + s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n") + s = append(s, "RequestTypeUrl: "+fmt.Sprintf("%#v", this.RequestTypeUrl)+",\n") + s = append(s, "RequestStreaming: "+fmt.Sprintf("%#v", this.RequestStreaming)+",\n") + s = append(s, "ResponseTypeUrl: "+fmt.Sprintf("%#v", this.ResponseTypeUrl)+",\n") + s = append(s, "ResponseStreaming: "+fmt.Sprintf("%#v", this.ResponseStreaming)+",\n") + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + s = append(s, "Syntax: "+fmt.Sprintf("%#v", this.Syntax)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *Mixin) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&types.Mixin{") + s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n") + s = append(s, "Root: "+fmt.Sprintf("%#v", this.Root)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringApi(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func (m *Api) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Api) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Api) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Syntax != 0 { + i = encodeVarintApi(dAtA, i, uint64(m.Syntax)) + i-- + dAtA[i] = 0x38 + } + if len(m.Mixins) > 0 { + for iNdEx := len(m.Mixins) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Mixins[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if m.SourceContext != nil { + { + size, err := m.SourceContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.Version) > 0 { + i -= len(m.Version) + copy(dAtA[i:], m.Version) + i = encodeVarintApi(dAtA, i, uint64(len(m.Version))) + i-- + dAtA[i] = 0x22 + } + if len(m.Options) > 0 { + for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Options[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Methods) > 0 { + for iNdEx := len(m.Methods) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Methods[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintApi(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Method) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Method) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Method) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Syntax != 0 { + i = encodeVarintApi(dAtA, i, uint64(m.Syntax)) + i-- + dAtA[i] = 0x38 + } + if len(m.Options) > 0 { + for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Options[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if m.ResponseStreaming { + i-- + if m.ResponseStreaming { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } + if len(m.ResponseTypeUrl) > 0 { + i -= len(m.ResponseTypeUrl) + copy(dAtA[i:], m.ResponseTypeUrl) + i = encodeVarintApi(dAtA, i, uint64(len(m.ResponseTypeUrl))) + i-- + dAtA[i] = 0x22 + } + if m.RequestStreaming { + i-- + if m.RequestStreaming { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if len(m.RequestTypeUrl) > 0 { + i -= len(m.RequestTypeUrl) + copy(dAtA[i:], m.RequestTypeUrl) + i = encodeVarintApi(dAtA, i, uint64(len(m.RequestTypeUrl))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintApi(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Mixin) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Mixin) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Mixin) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Root) > 0 { + i -= len(m.Root) + copy(dAtA[i:], m.Root) + i = encodeVarintApi(dAtA, i, uint64(len(m.Root))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintApi(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintApi(dAtA []byte, offset int, v uint64) int { + offset -= sovApi(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func NewPopulatedApi(r randyApi, easy bool) *Api { + this := &Api{} + this.Name = string(randStringApi(r)) + if r.Intn(5) != 0 { + v1 := r.Intn(5) + this.Methods = make([]*Method, v1) + for i := 0; i < v1; i++ { + this.Methods[i] = NewPopulatedMethod(r, easy) + } + } + if r.Intn(5) != 0 { + v2 := r.Intn(5) + this.Options = make([]*Option, v2) + for i := 0; i < v2; i++ { + this.Options[i] = NewPopulatedOption(r, easy) + } + } + this.Version = string(randStringApi(r)) + if r.Intn(5) != 0 { + this.SourceContext = NewPopulatedSourceContext(r, easy) + } + if r.Intn(5) != 0 { + v3 := r.Intn(5) + this.Mixins = make([]*Mixin, v3) + for i := 0; i < v3; i++ { + this.Mixins[i] = NewPopulatedMixin(r, easy) + } + } + this.Syntax = Syntax([]int32{0, 1}[r.Intn(2)]) + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedApi(r, 8) + } + return this +} + +func NewPopulatedMethod(r randyApi, easy bool) *Method { + this := &Method{} + this.Name = string(randStringApi(r)) + this.RequestTypeUrl = string(randStringApi(r)) + this.RequestStreaming = bool(bool(r.Intn(2) == 0)) + this.ResponseTypeUrl = string(randStringApi(r)) + this.ResponseStreaming = bool(bool(r.Intn(2) == 0)) + if r.Intn(5) != 0 { + v4 := r.Intn(5) + this.Options = make([]*Option, v4) + for i := 0; i < v4; i++ { + this.Options[i] = NewPopulatedOption(r, easy) + } + } + this.Syntax = Syntax([]int32{0, 1}[r.Intn(2)]) + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedApi(r, 8) + } + return this +} + +func NewPopulatedMixin(r randyApi, easy bool) *Mixin { + this := &Mixin{} + this.Name = string(randStringApi(r)) + this.Root = string(randStringApi(r)) + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedApi(r, 3) + } + return this +} + +type randyApi interface { + Float32() float32 + Float64() float64 + Int63() int64 + Int31() int32 + Uint32() uint32 + Intn(n int) int +} + +func randUTF8RuneApi(r randyApi) rune { + ru := r.Intn(62) + if ru < 10 { + return rune(ru + 48) + } else if ru < 36 { + return rune(ru + 55) + } + return rune(ru + 61) +} +func randStringApi(r randyApi) string { + v5 := r.Intn(100) + tmps := make([]rune, v5) + for i := 0; i < v5; i++ { + tmps[i] = randUTF8RuneApi(r) + } + return string(tmps) +} +func randUnrecognizedApi(r randyApi, maxFieldNumber int) (dAtA []byte) { + l := r.Intn(5) + for i := 0; i < l; i++ { + wire := r.Intn(4) + if wire == 3 { + wire = 5 + } + fieldNumber := maxFieldNumber + r.Intn(100) + dAtA = randFieldApi(dAtA, r, fieldNumber, wire) + } + return dAtA +} +func randFieldApi(dAtA []byte, r randyApi, fieldNumber int, wire int) []byte { + key := uint32(fieldNumber)<<3 | uint32(wire) + switch wire { + case 0: + dAtA = encodeVarintPopulateApi(dAtA, uint64(key)) + v6 := r.Int63() + if r.Intn(2) == 0 { + v6 *= -1 + } + dAtA = encodeVarintPopulateApi(dAtA, uint64(v6)) + case 1: + dAtA = encodeVarintPopulateApi(dAtA, uint64(key)) + dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + case 2: + dAtA = encodeVarintPopulateApi(dAtA, uint64(key)) + ll := r.Intn(100) + dAtA = encodeVarintPopulateApi(dAtA, uint64(ll)) + for j := 0; j < ll; j++ { + dAtA = append(dAtA, byte(r.Intn(256))) + } + default: + dAtA = encodeVarintPopulateApi(dAtA, uint64(key)) + dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + } + return dAtA +} +func encodeVarintPopulateApi(dAtA []byte, v uint64) []byte { + for v >= 1<<7 { + dAtA = append(dAtA, uint8(uint64(v)&0x7f|0x80)) + v >>= 7 + } + dAtA = append(dAtA, uint8(v)) + return dAtA +} +func (m *Api) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if len(m.Methods) > 0 { + for _, e := range m.Methods { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + if len(m.Options) > 0 { + for _, e := range m.Options { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + l = len(m.Version) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if m.SourceContext != nil { + l = m.SourceContext.Size() + n += 1 + l + sovApi(uint64(l)) + } + if len(m.Mixins) > 0 { + for _, e := range m.Mixins { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + if m.Syntax != 0 { + n += 1 + sovApi(uint64(m.Syntax)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Method) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + l = len(m.RequestTypeUrl) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if m.RequestStreaming { + n += 2 + } + l = len(m.ResponseTypeUrl) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if m.ResponseStreaming { + n += 2 + } + if len(m.Options) > 0 { + for _, e := range m.Options { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + if m.Syntax != 0 { + n += 1 + sovApi(uint64(m.Syntax)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Mixin) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + l = len(m.Root) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovApi(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozApi(x uint64) (n int) { + return sovApi(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Api) String() string { + if this == nil { + return "nil" + } + repeatedStringForMethods := "[]*Method{" + for _, f := range this.Methods { + repeatedStringForMethods += strings.Replace(f.String(), "Method", "Method", 1) + "," + } + repeatedStringForMethods += "}" + repeatedStringForOptions := "[]*Option{" + for _, f := range this.Options { + repeatedStringForOptions += strings.Replace(fmt.Sprintf("%v", f), "Option", "Option", 1) + "," + } + repeatedStringForOptions += "}" + repeatedStringForMixins := "[]*Mixin{" + for _, f := range this.Mixins { + repeatedStringForMixins += strings.Replace(f.String(), "Mixin", "Mixin", 1) + "," + } + repeatedStringForMixins += "}" + s := strings.Join([]string{`&Api{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Methods:` + repeatedStringForMethods + `,`, + `Options:` + repeatedStringForOptions + `,`, + `Version:` + fmt.Sprintf("%v", this.Version) + `,`, + `SourceContext:` + strings.Replace(fmt.Sprintf("%v", this.SourceContext), "SourceContext", "SourceContext", 1) + `,`, + `Mixins:` + repeatedStringForMixins + `,`, + `Syntax:` + fmt.Sprintf("%v", this.Syntax) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *Method) String() string { + if this == nil { + return "nil" + } + repeatedStringForOptions := "[]*Option{" + for _, f := range this.Options { + repeatedStringForOptions += strings.Replace(fmt.Sprintf("%v", f), "Option", "Option", 1) + "," + } + repeatedStringForOptions += "}" + s := strings.Join([]string{`&Method{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `RequestTypeUrl:` + fmt.Sprintf("%v", this.RequestTypeUrl) + `,`, + `RequestStreaming:` + fmt.Sprintf("%v", this.RequestStreaming) + `,`, + `ResponseTypeUrl:` + fmt.Sprintf("%v", this.ResponseTypeUrl) + `,`, + `ResponseStreaming:` + fmt.Sprintf("%v", this.ResponseStreaming) + `,`, + `Options:` + repeatedStringForOptions + `,`, + `Syntax:` + fmt.Sprintf("%v", this.Syntax) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *Mixin) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Mixin{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Root:` + fmt.Sprintf("%v", this.Root) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringApi(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Api) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Api: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Api: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Methods", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Methods = append(m.Methods, &Method{}) + if err := m.Methods[len(m.Methods)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Options = append(m.Options, &Option{}) + if err := m.Options[len(m.Options)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Version = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SourceContext == nil { + m.SourceContext = &SourceContext{} + } + if err := m.SourceContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Mixins", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Mixins = append(m.Mixins, &Mixin{}) + if err := m.Mixins[len(m.Mixins)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Syntax", wireType) + } + m.Syntax = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Syntax |= Syntax(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Method) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Method: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Method: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestTypeUrl", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RequestTypeUrl = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RequestStreaming", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.RequestStreaming = bool(v != 0) + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResponseTypeUrl", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResponseTypeUrl = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ResponseStreaming", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ResponseStreaming = bool(v != 0) + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Options = append(m.Options, &Option{}) + if err := m.Options[len(m.Options)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Syntax", wireType) + } + m.Syntax = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Syntax |= Syntax(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Mixin) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Mixin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Mixin: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Root", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Root = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipApi(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthApi + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupApi + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthApi + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthApi = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowApi = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupApi = fmt.Errorf("proto: unexpected end of group") +) diff --git a/vendor/github.com/gogo/protobuf/types/duration.go b/vendor/github.com/gogo/protobuf/types/duration.go index 475d61f1db292..979b8e78a4efc 100644 --- a/vendor/github.com/gogo/protobuf/types/duration.go +++ b/vendor/github.com/gogo/protobuf/types/duration.go @@ -80,7 +80,7 @@ func DurationFromProto(p *Duration) (time.Duration, error) { return 0, fmt.Errorf("duration: %#v is out of range for time.Duration", p) } if p.Nanos != 0 { - d += time.Duration(p.Nanos) + d += time.Duration(p.Nanos) * time.Nanosecond if (d < 0) != (p.Nanos < 0) { return 0, fmt.Errorf("duration: %#v is out of range for time.Duration", p) } diff --git a/vendor/github.com/gogo/protobuf/types/duration.pb.go b/vendor/github.com/gogo/protobuf/types/duration.pb.go index ee9deacfd3225..4deafcb1ce957 100644 --- a/vendor/github.com/gogo/protobuf/types/duration.pb.go +++ b/vendor/github.com/gogo/protobuf/types/duration.pb.go @@ -1,25 +1,18 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: duration.proto +// source: google/protobuf/duration.proto -/* - Package types is a generated protocol buffer package. - - It is generated from these files: - duration.proto - - It has these top-level messages: - Duration -*/ package types -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -30,7 +23,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // A Duration represents a signed, fixed-length span of time represented // as a count of seconds and fractions of seconds at nanosecond @@ -103,13 +96,44 @@ type Duration struct { // of one second or more, a non-zero value for the `nanos` field must be // of the same sign as the `seconds` field. Must be from -999,999,999 // to +999,999,999 inclusive. - Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` + Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Duration) Reset() { *m = Duration{} } +func (*Duration) ProtoMessage() {} +func (*Duration) Descriptor() ([]byte, []int) { + return fileDescriptor_23597b2ebd7ac6c5, []int{0} +} +func (*Duration) XXX_WellKnownType() string { return "Duration" } +func (m *Duration) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Duration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Duration.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Duration) XXX_Merge(src proto.Message) { + xxx_messageInfo_Duration.Merge(m, src) +} +func (m *Duration) XXX_Size() int { + return m.Size() +} +func (m *Duration) XXX_DiscardUnknown() { + xxx_messageInfo_Duration.DiscardUnknown(m) } -func (m *Duration) Reset() { *m = Duration{} } -func (*Duration) ProtoMessage() {} -func (*Duration) Descriptor() ([]byte, []int) { return fileDescriptorDuration, []int{0} } -func (*Duration) XXX_WellKnownType() string { return "Duration" } +var xxx_messageInfo_Duration proto.InternalMessageInfo func (m *Duration) GetSeconds() int64 { if m != nil { @@ -125,9 +149,33 @@ func (m *Duration) GetNanos() int32 { return 0 } +func (*Duration) XXX_MessageName() string { + return "google.protobuf.Duration" +} func init() { proto.RegisterType((*Duration)(nil), "google.protobuf.Duration") } + +func init() { proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor_23597b2ebd7ac6c5) } + +var fileDescriptor_23597b2ebd7ac6c5 = []byte{ + // 209 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a, + 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0x56, + 0x5c, 0x1c, 0x2e, 0x50, 0x25, 0x42, 0x12, 0x5c, 0xec, 0xc5, 0xa9, 0xc9, 0xf9, 0x79, 0x29, 0xc5, + 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x30, 0xae, 0x90, 0x08, 0x17, 0x6b, 0x5e, 0x62, 0x5e, + 0x7e, 0xb1, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x84, 0xe3, 0x54, 0x7f, 0xe3, 0xa1, 0x1c, + 0xc3, 0x87, 0x87, 0x72, 0x8c, 0x2b, 0x1e, 0xc9, 0x31, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, + 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x2f, 0x1e, 0xc9, 0x31, 0x7c, 0x78, 0x24, 0xc7, 0xb8, 0xe2, + 0xb1, 0x1c, 0xe3, 0x89, 0xc7, 0x72, 0x8c, 0x5c, 0xc2, 0xc9, 0xf9, 0xb9, 0x7a, 0x68, 0x56, 0x3b, + 0xf1, 0xc2, 0x2c, 0x0e, 0x00, 0x89, 0x04, 0x30, 0x46, 0xb1, 0x96, 0x54, 0x16, 0xa4, 0x16, 0xff, + 0x60, 0x64, 0x5c, 0xc4, 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0xa2, 0x25, 0x00, + 0xaa, 0x45, 0x2f, 0x3c, 0x35, 0x27, 0xc7, 0x3b, 0x2f, 0xbf, 0x3c, 0x2f, 0x04, 0xa4, 0x32, 0x89, + 0x0d, 0x6c, 0x96, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x8a, 0x1c, 0x64, 0x4e, 0xf6, 0x00, 0x00, + 0x00, +} + func (this *Duration) Compare(that interface{}) int { if that == nil { if this == nil { @@ -165,6 +213,9 @@ func (this *Duration) Compare(that interface{}) int { } return 1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *Duration) Equal(that interface{}) bool { @@ -192,6 +243,9 @@ func (this *Duration) Equal(that interface{}) bool { if this.Nanos != that1.Nanos { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *Duration) GoString() string { @@ -202,6 +256,9 @@ func (this *Duration) GoString() string { s = append(s, "&types.Duration{") s = append(s, "Seconds: "+fmt.Sprintf("%#v", this.Seconds)+",\n") s = append(s, "Nanos: "+fmt.Sprintf("%#v", this.Nanos)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -216,7 +273,7 @@ func valueToGoStringDuration(v interface{}, typ string) string { func (m *Duration) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -224,33 +281,47 @@ func (m *Duration) Marshal() (dAtA []byte, err error) { } func (m *Duration) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Duration) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Seconds != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintDuration(dAtA, i, uint64(m.Seconds)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Nanos != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintDuration(dAtA, i, uint64(m.Nanos)) + i-- + dAtA[i] = 0x10 } - return i, nil + if m.Seconds != 0 { + i = encodeVarintDuration(dAtA, i, uint64(m.Seconds)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func encodeVarintDuration(dAtA []byte, offset int, v uint64) int { + offset -= sovDuration(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Duration) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Seconds != 0 { @@ -259,18 +330,14 @@ func (m *Duration) Size() (n int) { if m.Nanos != 0 { n += 1 + sovDuration(uint64(m.Nanos)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovDuration(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozDuration(x uint64) (n int) { return sovDuration(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -290,7 +357,7 @@ func (m *Duration) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -318,7 +385,7 @@ func (m *Duration) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Seconds |= (int64(b) & 0x7F) << shift + m.Seconds |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -337,7 +404,7 @@ func (m *Duration) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Nanos |= (int32(b) & 0x7F) << shift + m.Nanos |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -348,12 +415,13 @@ func (m *Duration) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthDuration } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -366,6 +434,7 @@ func (m *Duration) Unmarshal(dAtA []byte) error { func skipDuration(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -397,10 +466,8 @@ func skipDuration(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -417,72 +484,34 @@ func skipDuration(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthDuration } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowDuration - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipDuration(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupDuration + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthDuration + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthDuration = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowDuration = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthDuration = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDuration = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupDuration = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("duration.proto", fileDescriptorDuration) } - -var fileDescriptorDuration = []byte{ - // 203 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4b, 0x29, 0x2d, 0x4a, - 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4f, 0xcf, 0xcf, 0x4f, - 0xcf, 0x49, 0x85, 0xf0, 0x92, 0x4a, 0xd3, 0x94, 0xac, 0xb8, 0x38, 0x5c, 0xa0, 0x4a, 0x84, 0x24, - 0xb8, 0xd8, 0x8b, 0x53, 0x93, 0xf3, 0xf3, 0x52, 0x8a, 0x25, 0x18, 0x15, 0x18, 0x35, 0x98, 0x83, - 0x60, 0x5c, 0x21, 0x11, 0x2e, 0xd6, 0xbc, 0xc4, 0xbc, 0xfc, 0x62, 0x09, 0x26, 0x05, 0x46, 0x0d, - 0xd6, 0x20, 0x08, 0xc7, 0xa9, 0xfe, 0xc2, 0x43, 0x39, 0x86, 0x1b, 0x0f, 0xe5, 0x18, 0x3e, 0x3c, - 0x94, 0x63, 0x5c, 0xf1, 0x48, 0x8e, 0xf1, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, - 0x3c, 0x92, 0x63, 0x7c, 0xf1, 0x48, 0x8e, 0xe1, 0xc3, 0x23, 0x39, 0xc6, 0x15, 0x8f, 0xe5, 0x18, - 0xb9, 0x84, 0x93, 0xf3, 0x73, 0xf5, 0xd0, 0xac, 0x76, 0xe2, 0x85, 0x59, 0x1c, 0x00, 0x12, 0x09, - 0x60, 0x8c, 0x62, 0x2d, 0xa9, 0x2c, 0x48, 0x2d, 0xfe, 0xc1, 0xc8, 0xb8, 0x88, 0x89, 0xd9, 0x3d, - 0xc0, 0x69, 0x15, 0x93, 0x9c, 0x3b, 0x44, 0x4b, 0x00, 0x54, 0x8b, 0x5e, 0x78, 0x6a, 0x4e, 0x8e, - 0x77, 0x5e, 0x7e, 0x79, 0x5e, 0x08, 0x48, 0x65, 0x12, 0x1b, 0xd8, 0x2c, 0x63, 0x40, 0x00, 0x00, - 0x00, 0xff, 0xff, 0x9d, 0x5a, 0x25, 0xa5, 0xe6, 0x00, 0x00, 0x00, -} diff --git a/vendor/github.com/gogo/protobuf/types/empty.pb.go b/vendor/github.com/gogo/protobuf/types/empty.pb.go index e7018b905d45e..9e94748b3a33b 100644 --- a/vendor/github.com/gogo/protobuf/types/empty.pb.go +++ b/vendor/github.com/gogo/protobuf/types/empty.pb.go @@ -1,25 +1,18 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: empty.proto +// source: google/protobuf/empty.proto -/* -Package types is a generated protocol buffer package. - -It is generated from these files: - empty.proto - -It has these top-level messages: - Empty -*/ package types -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -30,7 +23,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // A generic empty message that you can re-use to avoid defining duplicated // empty messages in your APIs. A typical example is to use it as the request @@ -42,16 +35,68 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package // // The JSON representation for `Empty` is empty JSON object `{}`. type Empty struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Empty) Reset() { *m = Empty{} } +func (*Empty) ProtoMessage() {} +func (*Empty) Descriptor() ([]byte, []int) { + return fileDescriptor_900544acb223d5b8, []int{0} +} +func (*Empty) XXX_WellKnownType() string { return "Empty" } +func (m *Empty) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Empty.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Empty) XXX_Merge(src proto.Message) { + xxx_messageInfo_Empty.Merge(m, src) +} +func (m *Empty) XXX_Size() int { + return m.Size() +} +func (m *Empty) XXX_DiscardUnknown() { + xxx_messageInfo_Empty.DiscardUnknown(m) } -func (m *Empty) Reset() { *m = Empty{} } -func (*Empty) ProtoMessage() {} -func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptorEmpty, []int{0} } -func (*Empty) XXX_WellKnownType() string { return "Empty" } +var xxx_messageInfo_Empty proto.InternalMessageInfo +func (*Empty) XXX_MessageName() string { + return "google.protobuf.Empty" +} func init() { proto.RegisterType((*Empty)(nil), "google.protobuf.Empty") } + +func init() { proto.RegisterFile("google/protobuf/empty.proto", fileDescriptor_900544acb223d5b8) } + +var fileDescriptor_900544acb223d5b8 = []byte{ + // 176 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcd, 0x2d, 0x28, + 0xa9, 0xd4, 0x03, 0x73, 0x85, 0xf8, 0x21, 0x92, 0x7a, 0x30, 0x49, 0x25, 0x76, 0x2e, 0x56, 0x57, + 0x90, 0xbc, 0x53, 0x0b, 0xe3, 0x8d, 0x87, 0x72, 0x0c, 0x1f, 0x1e, 0xca, 0x31, 0xfe, 0x78, 0x28, + 0xc7, 0xd8, 0xf0, 0x48, 0x8e, 0x71, 0xc5, 0x23, 0x39, 0xc6, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, + 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0xf1, 0xc5, 0x23, 0x39, 0x86, 0x0f, 0x20, 0xf1, 0xc7, 0x72, + 0x8c, 0x27, 0x1e, 0xcb, 0x31, 0x72, 0x09, 0x27, 0xe7, 0xe7, 0xea, 0xa1, 0x19, 0xe8, 0xc4, 0x05, + 0x36, 0x2e, 0x00, 0xc4, 0x0d, 0x60, 0x8c, 0x62, 0x2d, 0xa9, 0x2c, 0x48, 0x2d, 0xfe, 0xc1, 0xc8, + 0xb8, 0x88, 0x89, 0xd9, 0x3d, 0xc0, 0x69, 0x15, 0x93, 0x9c, 0x3b, 0x44, 0x7d, 0x00, 0x54, 0xbd, + 0x5e, 0x78, 0x6a, 0x4e, 0x8e, 0x77, 0x5e, 0x7e, 0x79, 0x5e, 0x08, 0x48, 0x65, 0x12, 0x1b, 0xd8, + 0x20, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x21, 0xbe, 0xb6, 0x31, 0xc6, 0x00, 0x00, 0x00, +} + func (this *Empty) Compare(that interface{}) int { if that == nil { if this == nil { @@ -77,6 +122,9 @@ func (this *Empty) Compare(that interface{}) int { } else if this == nil { return -1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *Empty) Equal(that interface{}) bool { @@ -98,6 +146,9 @@ func (this *Empty) Equal(that interface{}) bool { } else if this == nil { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *Empty) GoString() string { @@ -106,6 +157,9 @@ func (this *Empty) GoString() string { } s := make([]string, 0, 4) s = append(s, "&types.Empty{") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -120,7 +174,7 @@ func valueToGoStringEmpty(v interface{}, typ string) string { func (m *Empty) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -128,25 +182,37 @@ func (m *Empty) Marshal() (dAtA []byte, err error) { } func (m *Empty) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Empty) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - return i, nil + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + return len(dAtA) - i, nil } func encodeVarintEmpty(dAtA []byte, offset int, v uint64) int { + offset -= sovEmpty(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func NewPopulatedEmpty(r randyEmpty, easy bool) *Empty { this := &Empty{} if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedEmpty(r, 1) } return this } @@ -224,20 +290,19 @@ func encodeVarintPopulateEmpty(dAtA []byte, v uint64) []byte { return dAtA } func (m *Empty) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovEmpty(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozEmpty(x uint64) (n int) { return sovEmpty(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -247,6 +312,7 @@ func (this *Empty) String() string { return "nil" } s := strings.Join([]string{`&Empty{`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -274,7 +340,7 @@ func (m *Empty) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -294,12 +360,13 @@ func (m *Empty) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthEmpty } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -312,6 +379,7 @@ func (m *Empty) Unmarshal(dAtA []byte) error { func skipEmpty(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -343,10 +411,8 @@ func skipEmpty(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -363,70 +429,34 @@ func skipEmpty(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthEmpty } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowEmpty - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipEmpty(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEmpty + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthEmpty + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthEmpty = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowEmpty = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthEmpty = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEmpty = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEmpty = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("empty.proto", fileDescriptorEmpty) } - -var fileDescriptorEmpty = []byte{ - // 169 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4e, 0xcd, 0x2d, 0x28, - 0xa9, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4f, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0x85, - 0xf0, 0x92, 0x4a, 0xd3, 0x94, 0xd8, 0xb9, 0x58, 0x5d, 0x41, 0xf2, 0x4e, 0x2d, 0x8c, 0x17, 0x1e, - 0xca, 0x31, 0xdc, 0x78, 0x28, 0xc7, 0xf0, 0xe1, 0xa1, 0x1c, 0xe3, 0x8f, 0x87, 0x72, 0x8c, 0x0d, - 0x8f, 0xe4, 0x18, 0x57, 0x3c, 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, - 0x07, 0x8f, 0xe4, 0x18, 0x5f, 0x3c, 0x92, 0x63, 0xf8, 0x00, 0x12, 0x7f, 0x2c, 0xc7, 0xc8, 0x25, - 0x9c, 0x9c, 0x9f, 0xab, 0x87, 0x66, 0xa0, 0x13, 0x17, 0xd8, 0xb8, 0x00, 0x10, 0x37, 0x80, 0x31, - 0x8a, 0xb5, 0xa4, 0xb2, 0x20, 0xb5, 0xf8, 0x07, 0x23, 0xe3, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, - 0x55, 0x4c, 0x72, 0xee, 0x10, 0xf5, 0x01, 0x50, 0xf5, 0x7a, 0xe1, 0xa9, 0x39, 0x39, 0xde, 0x79, - 0xf9, 0xe5, 0x79, 0x21, 0x20, 0x95, 0x49, 0x6c, 0x60, 0x83, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, - 0xff, 0x7c, 0xa8, 0xf0, 0xc4, 0xb6, 0x00, 0x00, 0x00, -} diff --git a/vendor/github.com/gogo/protobuf/types/field_mask.pb.go b/vendor/github.com/gogo/protobuf/types/field_mask.pb.go index 22e8b4f0db494..6ae346d92527c 100644 --- a/vendor/github.com/gogo/protobuf/types/field_mask.pb.go +++ b/vendor/github.com/gogo/protobuf/types/field_mask.pb.go @@ -1,25 +1,18 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: field_mask.proto +// source: google/protobuf/field_mask.proto -/* -Package types is a generated protocol buffer package. - -It is generated from these files: - field_mask.proto - -It has these top-level messages: - FieldMask -*/ package types -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -30,7 +23,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // `FieldMask` represents a set of symbolic field paths, for example: // @@ -100,57 +93,49 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package // describe the updated values, the API ignores the values of all // fields not covered by the mask. // -// If a repeated field is specified for an update operation, the existing -// repeated values in the target resource will be overwritten by the new values. -// Note that a repeated field is only allowed in the last position of a `paths` -// string. +// If a repeated field is specified for an update operation, new values will +// be appended to the existing repeated field in the target resource. Note that +// a repeated field is only allowed in the last position of a `paths` string. // // If a sub-message is specified in the last position of the field mask for an -// update operation, then the existing sub-message in the target resource is -// overwritten. Given the target message: +// update operation, then new value will be merged into the existing sub-message +// in the target resource. +// +// For example, given the target message: // // f { // b { -// d : 1 -// x : 2 +// d: 1 +// x: 2 // } -// c : 1 +// c: [1] // } // // And an update message: // // f { // b { -// d : 10 +// d: 10 // } +// c: [2] // } // // then if the field mask is: // -// paths: "f.b" +// paths: ["f.b", "f.c"] // // then the result will be: // // f { // b { -// d : 10 +// d: 10 +// x: 2 // } -// c : 1 +// c: [1, 2] // } // -// However, if the update mask was: -// -// paths: "f.b.d" -// -// then the result would be: -// -// f { -// b { -// d : 10 -// x : 2 -// } -// c : 1 -// } +// An implementation may provide options to override this default behavior for +// repeated and message fields. // // In order to reset a field's value to the default, the field must // be in the mask and set to the default value in the provided resource. @@ -233,14 +218,51 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package // // Note that oneof type names ("test_oneof" in this case) cannot be used in // paths. +// +// ## Field Mask Verification +// +// The implementation of any API method which has a FieldMask type field in the +// request should verify the included field paths, and return an +// `INVALID_ARGUMENT` error if any path is duplicated or unmappable. type FieldMask struct { // The set of field mask paths. - Paths []string `protobuf:"bytes,1,rep,name=paths" json:"paths,omitempty"` + Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *FieldMask) Reset() { *m = FieldMask{} } -func (*FieldMask) ProtoMessage() {} -func (*FieldMask) Descriptor() ([]byte, []int) { return fileDescriptorFieldMask, []int{0} } +func (m *FieldMask) Reset() { *m = FieldMask{} } +func (*FieldMask) ProtoMessage() {} +func (*FieldMask) Descriptor() ([]byte, []int) { + return fileDescriptor_5158202634f0da48, []int{0} +} +func (m *FieldMask) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FieldMask) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FieldMask.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *FieldMask) XXX_Merge(src proto.Message) { + xxx_messageInfo_FieldMask.Merge(m, src) +} +func (m *FieldMask) XXX_Size() int { + return m.Size() +} +func (m *FieldMask) XXX_DiscardUnknown() { + xxx_messageInfo_FieldMask.DiscardUnknown(m) +} + +var xxx_messageInfo_FieldMask proto.InternalMessageInfo func (m *FieldMask) GetPaths() []string { if m != nil { @@ -249,9 +271,32 @@ func (m *FieldMask) GetPaths() []string { return nil } +func (*FieldMask) XXX_MessageName() string { + return "google.protobuf.FieldMask" +} func init() { proto.RegisterType((*FieldMask)(nil), "google.protobuf.FieldMask") } + +func init() { proto.RegisterFile("google/protobuf/field_mask.proto", fileDescriptor_5158202634f0da48) } + +var fileDescriptor_5158202634f0da48 = []byte{ + // 203 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcb, 0x4c, 0xcd, + 0x49, 0x89, 0xcf, 0x4d, 0x2c, 0xce, 0xd6, 0x03, 0x8b, 0x09, 0xf1, 0x43, 0x54, 0xe8, 0xc1, 0x54, + 0x28, 0x29, 0x72, 0x71, 0xba, 0x81, 0x14, 0xf9, 0x26, 0x16, 0x67, 0x0b, 0x89, 0x70, 0xb1, 0x16, + 0x24, 0x96, 0x64, 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6b, 0x70, 0x06, 0x41, 0x38, 0x4e, 0x1d, 0x8c, + 0x37, 0x1e, 0xca, 0x31, 0x7c, 0x78, 0x28, 0xc7, 0xf8, 0xe3, 0xa1, 0x1c, 0x63, 0xc3, 0x23, 0x39, + 0xc6, 0x15, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, + 0x39, 0xc6, 0x17, 0x8f, 0xe4, 0x18, 0x3e, 0x80, 0xc4, 0x1f, 0xcb, 0x31, 0x9e, 0x78, 0x2c, 0xc7, + 0xc8, 0x25, 0x9c, 0x9c, 0x9f, 0xab, 0x87, 0x66, 0x95, 0x13, 0x1f, 0xdc, 0xa2, 0x00, 0x90, 0x50, + 0x00, 0x63, 0x14, 0x6b, 0x49, 0x65, 0x41, 0x6a, 0xf1, 0x0f, 0x46, 0xc6, 0x45, 0x4c, 0xcc, 0xee, + 0x01, 0x4e, 0xab, 0x98, 0xe4, 0xdc, 0x21, 0x7a, 0x02, 0xa0, 0x7a, 0xf4, 0xc2, 0x53, 0x73, 0x72, + 0xbc, 0xf3, 0xf2, 0xcb, 0xf3, 0x42, 0x40, 0x2a, 0x93, 0xd8, 0xc0, 0x86, 0x19, 0x03, 0x02, 0x00, + 0x00, 0xff, 0xff, 0x43, 0xa0, 0x83, 0xd0, 0xe9, 0x00, 0x00, 0x00, +} + func (this *FieldMask) Compare(that interface{}) int { if that == nil { if this == nil { @@ -291,6 +336,9 @@ func (this *FieldMask) Compare(that interface{}) int { return 1 } } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *FieldMask) Equal(that interface{}) bool { @@ -320,6 +368,9 @@ func (this *FieldMask) Equal(that interface{}) bool { return false } } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *FieldMask) GoString() string { @@ -329,6 +380,9 @@ func (this *FieldMask) GoString() string { s := make([]string, 0, 5) s = append(s, "&types.FieldMask{") s = append(s, "Paths: "+fmt.Sprintf("%#v", this.Paths)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -343,7 +397,7 @@ func valueToGoStringFieldMask(v interface{}, typ string) string { func (m *FieldMask) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -351,36 +405,41 @@ func (m *FieldMask) Marshal() (dAtA []byte, err error) { } func (m *FieldMask) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FieldMask) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Paths) > 0 { - for _, s := range m.Paths { + for iNdEx := len(m.Paths) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Paths[iNdEx]) + copy(dAtA[i:], m.Paths[iNdEx]) + i = encodeVarintFieldMask(dAtA, i, uint64(len(m.Paths[iNdEx]))) + i-- dAtA[i] = 0xa - i++ - l = len(s) - for l >= 1<<7 { - dAtA[i] = uint8(uint64(l)&0x7f | 0x80) - l >>= 7 - i++ - } - dAtA[i] = uint8(l) - i++ - i += copy(dAtA[i:], s) } } - return i, nil + return len(dAtA) - i, nil } func encodeVarintFieldMask(dAtA []byte, offset int, v uint64) int { + offset -= sovFieldMask(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func NewPopulatedFieldMask(r randyFieldMask, easy bool) *FieldMask { this := &FieldMask{} @@ -390,6 +449,7 @@ func NewPopulatedFieldMask(r randyFieldMask, easy bool) *FieldMask { this.Paths[i] = string(randStringFieldMask(r)) } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedFieldMask(r, 2) } return this } @@ -467,6 +527,9 @@ func encodeVarintPopulateFieldMask(dAtA []byte, v uint64) []byte { return dAtA } func (m *FieldMask) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Paths) > 0 { @@ -475,18 +538,14 @@ func (m *FieldMask) Size() (n int) { n += 1 + l + sovFieldMask(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovFieldMask(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozFieldMask(x uint64) (n int) { return sovFieldMask(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -497,6 +556,7 @@ func (this *FieldMask) String() string { } s := strings.Join([]string{`&FieldMask{`, `Paths:` + fmt.Sprintf("%v", this.Paths) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -524,7 +584,7 @@ func (m *FieldMask) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -552,7 +612,7 @@ func (m *FieldMask) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -562,6 +622,9 @@ func (m *FieldMask) Unmarshal(dAtA []byte) error { return ErrInvalidLengthFieldMask } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthFieldMask + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -573,12 +636,13 @@ func (m *FieldMask) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthFieldMask } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -591,6 +655,7 @@ func (m *FieldMask) Unmarshal(dAtA []byte) error { func skipFieldMask(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -622,10 +687,8 @@ func skipFieldMask(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -642,72 +705,34 @@ func skipFieldMask(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthFieldMask } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowFieldMask - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipFieldMask(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupFieldMask + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthFieldMask + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthFieldMask = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowFieldMask = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthFieldMask = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowFieldMask = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupFieldMask = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("field_mask.proto", fileDescriptorFieldMask) } - -var fileDescriptorFieldMask = []byte{ - // 193 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x48, 0xcb, 0x4c, 0xcd, - 0x49, 0x89, 0xcf, 0x4d, 0x2c, 0xce, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4f, 0xcf, - 0xcf, 0x4f, 0xcf, 0x49, 0x85, 0xf0, 0x92, 0x4a, 0xd3, 0x94, 0x14, 0xb9, 0x38, 0xdd, 0x40, 0x8a, - 0x7c, 0x13, 0x8b, 0xb3, 0x85, 0x44, 0xb8, 0x58, 0x0b, 0x12, 0x4b, 0x32, 0x8a, 0x25, 0x18, 0x15, - 0x98, 0x35, 0x38, 0x83, 0x20, 0x1c, 0xa7, 0x56, 0xc6, 0x0b, 0x0f, 0xe5, 0x18, 0x6e, 0x3c, 0x94, - 0x63, 0xf8, 0xf0, 0x50, 0x8e, 0xf1, 0xc7, 0x43, 0x39, 0xc6, 0x86, 0x47, 0x72, 0x8c, 0x2b, 0x1e, - 0xc9, 0x31, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x2f, - 0x1e, 0xc9, 0x31, 0x7c, 0x00, 0x89, 0x3f, 0x96, 0x63, 0xe4, 0x12, 0x4e, 0xce, 0xcf, 0xd5, 0x43, - 0xb3, 0xca, 0x89, 0x0f, 0x6e, 0x51, 0x00, 0x48, 0x28, 0x80, 0x31, 0x8a, 0xb5, 0xa4, 0xb2, 0x20, - 0xb5, 0x78, 0x11, 0x13, 0xb3, 0x7b, 0x80, 0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, 0x86, 0x00, 0xa8, - 0x06, 0xbd, 0xf0, 0xd4, 0x9c, 0x1c, 0xef, 0xbc, 0xfc, 0xf2, 0xbc, 0x10, 0x90, 0xb2, 0x24, 0x36, - 0xb0, 0x49, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x51, 0x31, 0x89, 0xb5, 0xd6, 0x00, 0x00, - 0x00, -} diff --git a/vendor/github.com/gogo/protobuf/types/protosize.go b/vendor/github.com/gogo/protobuf/types/protosize.go new file mode 100644 index 0000000000000..3a2d1b7e1118d --- /dev/null +++ b/vendor/github.com/gogo/protobuf/types/protosize.go @@ -0,0 +1,34 @@ +package types + +func (m *Any) ProtoSize() (n int) { return m.Size() } +func (m *Api) ProtoSize() (n int) { return m.Size() } +func (m *Method) ProtoSize() (n int) { return m.Size() } +func (m *Mixin) ProtoSize() (n int) { return m.Size() } +func (m *Duration) ProtoSize() (n int) { return m.Size() } +func (m *Empty) ProtoSize() (n int) { return m.Size() } +func (m *FieldMask) ProtoSize() (n int) { return m.Size() } +func (m *SourceContext) ProtoSize() (n int) { return m.Size() } +func (m *Struct) ProtoSize() (n int) { return m.Size() } +func (m *Value) ProtoSize() (n int) { return m.Size() } +func (m *Value_NullValue) ProtoSize() (n int) { return m.Size() } +func (m *Value_NumberValue) ProtoSize() (n int) { return m.Size() } +func (m *Value_StringValue) ProtoSize() (n int) { return m.Size() } +func (m *Value_BoolValue) ProtoSize() (n int) { return m.Size() } +func (m *Value_StructValue) ProtoSize() (n int) { return m.Size() } +func (m *Value_ListValue) ProtoSize() (n int) { return m.Size() } +func (m *ListValue) ProtoSize() (n int) { return m.Size() } +func (m *Timestamp) ProtoSize() (n int) { return m.Size() } +func (m *Type) ProtoSize() (n int) { return m.Size() } +func (m *Field) ProtoSize() (n int) { return m.Size() } +func (m *Enum) ProtoSize() (n int) { return m.Size() } +func (m *EnumValue) ProtoSize() (n int) { return m.Size() } +func (m *Option) ProtoSize() (n int) { return m.Size() } +func (m *DoubleValue) ProtoSize() (n int) { return m.Size() } +func (m *FloatValue) ProtoSize() (n int) { return m.Size() } +func (m *Int64Value) ProtoSize() (n int) { return m.Size() } +func (m *UInt64Value) ProtoSize() (n int) { return m.Size() } +func (m *Int32Value) ProtoSize() (n int) { return m.Size() } +func (m *UInt32Value) ProtoSize() (n int) { return m.Size() } +func (m *BoolValue) ProtoSize() (n int) { return m.Size() } +func (m *StringValue) ProtoSize() (n int) { return m.Size() } +func (m *BytesValue) ProtoSize() (n int) { return m.Size() } diff --git a/vendor/github.com/gogo/protobuf/types/source_context.pb.go b/vendor/github.com/gogo/protobuf/types/source_context.pb.go new file mode 100644 index 0000000000000..8e6ce71b275eb --- /dev/null +++ b/vendor/github.com/gogo/protobuf/types/source_context.pb.go @@ -0,0 +1,524 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: google/protobuf/source_context.proto + +package types + +import ( + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// `SourceContext` represents information about the source of a +// protobuf element, like the file in which it is defined. +type SourceContext struct { + // The path-qualified name of the .proto file that contained the associated + // protobuf element. For example: `"google/protobuf/source_context.proto"`. + FileName string `protobuf:"bytes,1,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SourceContext) Reset() { *m = SourceContext{} } +func (*SourceContext) ProtoMessage() {} +func (*SourceContext) Descriptor() ([]byte, []int) { + return fileDescriptor_b686cdb126d509db, []int{0} +} +func (m *SourceContext) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SourceContext) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SourceContext.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SourceContext) XXX_Merge(src proto.Message) { + xxx_messageInfo_SourceContext.Merge(m, src) +} +func (m *SourceContext) XXX_Size() int { + return m.Size() +} +func (m *SourceContext) XXX_DiscardUnknown() { + xxx_messageInfo_SourceContext.DiscardUnknown(m) +} + +var xxx_messageInfo_SourceContext proto.InternalMessageInfo + +func (m *SourceContext) GetFileName() string { + if m != nil { + return m.FileName + } + return "" +} + +func (*SourceContext) XXX_MessageName() string { + return "google.protobuf.SourceContext" +} +func init() { + proto.RegisterType((*SourceContext)(nil), "google.protobuf.SourceContext") +} + +func init() { + proto.RegisterFile("google/protobuf/source_context.proto", fileDescriptor_b686cdb126d509db) +} + +var fileDescriptor_b686cdb126d509db = []byte{ + // 212 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x49, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xce, 0x2f, 0x2d, + 0x4a, 0x4e, 0x8d, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xad, 0x28, 0xd1, 0x03, 0x8b, 0x0b, 0xf1, 0x43, + 0x54, 0xe9, 0xc1, 0x54, 0x29, 0xe9, 0x70, 0xf1, 0x06, 0x83, 0x15, 0x3a, 0x43, 0xd4, 0x09, 0x49, + 0x73, 0x71, 0xa6, 0x65, 0xe6, 0xa4, 0xc6, 0xe7, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, + 0x70, 0x06, 0x71, 0x80, 0x04, 0xfc, 0x12, 0x73, 0x53, 0x9d, 0x3a, 0x19, 0x6f, 0x3c, 0x94, 0x63, + 0xf8, 0xf0, 0x50, 0x8e, 0xf1, 0xc7, 0x43, 0x39, 0xc6, 0x86, 0x47, 0x72, 0x8c, 0x2b, 0x1e, 0xc9, + 0x31, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x2f, 0x1e, + 0xc9, 0x31, 0x7c, 0x00, 0x89, 0x3f, 0x96, 0x63, 0x3c, 0xf1, 0x58, 0x8e, 0x91, 0x4b, 0x38, 0x39, + 0x3f, 0x57, 0x0f, 0xcd, 0x56, 0x27, 0x21, 0x14, 0x3b, 0x03, 0x40, 0xc2, 0x01, 0x8c, 0x51, 0xac, + 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x8b, 0x98, 0x98, 0xdd, 0x03, 0x9c, 0x56, 0x31, 0xc9, 0xb9, 0x43, + 0x34, 0x05, 0x40, 0x35, 0xe9, 0x85, 0xa7, 0xe6, 0xe4, 0x78, 0xe7, 0xe5, 0x97, 0xe7, 0x85, 0x80, + 0x94, 0x25, 0xb1, 0x81, 0x4d, 0x33, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x37, 0x2a, 0xa1, + 0xf9, 0x00, 0x00, 0x00, +} + +func (this *SourceContext) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*SourceContext) + if !ok { + that2, ok := that.(SourceContext) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.FileName != that1.FileName { + if this.FileName < that1.FileName { + return -1 + } + return 1 + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *SourceContext) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*SourceContext) + if !ok { + that2, ok := that.(SourceContext) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.FileName != that1.FileName { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *SourceContext) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 5) + s = append(s, "&types.SourceContext{") + s = append(s, "FileName: "+fmt.Sprintf("%#v", this.FileName)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringSourceContext(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func (m *SourceContext) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SourceContext) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SourceContext) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.FileName) > 0 { + i -= len(m.FileName) + copy(dAtA[i:], m.FileName) + i = encodeVarintSourceContext(dAtA, i, uint64(len(m.FileName))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintSourceContext(dAtA []byte, offset int, v uint64) int { + offset -= sovSourceContext(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func NewPopulatedSourceContext(r randySourceContext, easy bool) *SourceContext { + this := &SourceContext{} + this.FileName = string(randStringSourceContext(r)) + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedSourceContext(r, 2) + } + return this +} + +type randySourceContext interface { + Float32() float32 + Float64() float64 + Int63() int64 + Int31() int32 + Uint32() uint32 + Intn(n int) int +} + +func randUTF8RuneSourceContext(r randySourceContext) rune { + ru := r.Intn(62) + if ru < 10 { + return rune(ru + 48) + } else if ru < 36 { + return rune(ru + 55) + } + return rune(ru + 61) +} +func randStringSourceContext(r randySourceContext) string { + v1 := r.Intn(100) + tmps := make([]rune, v1) + for i := 0; i < v1; i++ { + tmps[i] = randUTF8RuneSourceContext(r) + } + return string(tmps) +} +func randUnrecognizedSourceContext(r randySourceContext, maxFieldNumber int) (dAtA []byte) { + l := r.Intn(5) + for i := 0; i < l; i++ { + wire := r.Intn(4) + if wire == 3 { + wire = 5 + } + fieldNumber := maxFieldNumber + r.Intn(100) + dAtA = randFieldSourceContext(dAtA, r, fieldNumber, wire) + } + return dAtA +} +func randFieldSourceContext(dAtA []byte, r randySourceContext, fieldNumber int, wire int) []byte { + key := uint32(fieldNumber)<<3 | uint32(wire) + switch wire { + case 0: + dAtA = encodeVarintPopulateSourceContext(dAtA, uint64(key)) + v2 := r.Int63() + if r.Intn(2) == 0 { + v2 *= -1 + } + dAtA = encodeVarintPopulateSourceContext(dAtA, uint64(v2)) + case 1: + dAtA = encodeVarintPopulateSourceContext(dAtA, uint64(key)) + dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + case 2: + dAtA = encodeVarintPopulateSourceContext(dAtA, uint64(key)) + ll := r.Intn(100) + dAtA = encodeVarintPopulateSourceContext(dAtA, uint64(ll)) + for j := 0; j < ll; j++ { + dAtA = append(dAtA, byte(r.Intn(256))) + } + default: + dAtA = encodeVarintPopulateSourceContext(dAtA, uint64(key)) + dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + } + return dAtA +} +func encodeVarintPopulateSourceContext(dAtA []byte, v uint64) []byte { + for v >= 1<<7 { + dAtA = append(dAtA, uint8(uint64(v)&0x7f|0x80)) + v >>= 7 + } + dAtA = append(dAtA, uint8(v)) + return dAtA +} +func (m *SourceContext) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.FileName) + if l > 0 { + n += 1 + l + sovSourceContext(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovSourceContext(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozSourceContext(x uint64) (n int) { + return sovSourceContext(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *SourceContext) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&SourceContext{`, + `FileName:` + fmt.Sprintf("%v", this.FileName) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringSourceContext(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *SourceContext) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSourceContext + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SourceContext: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SourceContext: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FileName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowSourceContext + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthSourceContext + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthSourceContext + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FileName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipSourceContext(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthSourceContext + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipSourceContext(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSourceContext + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSourceContext + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowSourceContext + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthSourceContext + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupSourceContext + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthSourceContext + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthSourceContext = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowSourceContext = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupSourceContext = fmt.Errorf("proto: unexpected end of group") +) diff --git a/vendor/github.com/gogo/protobuf/types/struct.pb.go b/vendor/github.com/gogo/protobuf/types/struct.pb.go index 7d5372b03108c..c0457312e67f5 100644 --- a/vendor/github.com/gogo/protobuf/types/struct.pb.go +++ b/vendor/github.com/gogo/protobuf/types/struct.pb.go @@ -1,32 +1,21 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: struct.proto +// source: google/protobuf/struct.proto -/* - Package types is a generated protocol buffer package. - - It is generated from these files: - struct.proto - - It has these top-level messages: - Struct - Value - ListValue -*/ package types -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -import strconv "strconv" - -import strings "strings" -import reflect "reflect" -import sortkeys "github.com/gogo/protobuf/sortkeys" - -import binary "encoding/binary" - -import io "io" +import ( + bytes "bytes" + encoding_binary "encoding/binary" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strconv "strconv" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -37,7 +26,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // `NullValue` is a singleton enumeration to represent the null value for the // `Value` type union. @@ -47,18 +36,22 @@ type NullValue int32 const ( // Null value. - NULL_VALUE NullValue = 0 + NullValue_NULL_VALUE NullValue = 0 ) var NullValue_name = map[int32]string{ 0: "NULL_VALUE", } + var NullValue_value = map[string]int32{ "NULL_VALUE": 0, } -func (NullValue) EnumDescriptor() ([]byte, []int) { return fileDescriptorStruct, []int{0} } -func (NullValue) XXX_WellKnownType() string { return "NullValue" } +func (NullValue) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_df322afd6c9fb402, []int{0} +} + +func (NullValue) XXX_WellKnownType() string { return "NullValue" } // `Struct` represents a structured data value, consisting of fields // which map to dynamically typed values. In some languages, `Struct` @@ -70,13 +63,44 @@ func (NullValue) XXX_WellKnownType() string { return "NullValue" } // The JSON representation for `Struct` is JSON object. type Struct struct { // Unordered map of dynamically typed values. - Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` + Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Struct) Reset() { *m = Struct{} } +func (*Struct) ProtoMessage() {} +func (*Struct) Descriptor() ([]byte, []int) { + return fileDescriptor_df322afd6c9fb402, []int{0} +} +func (*Struct) XXX_WellKnownType() string { return "Struct" } +func (m *Struct) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Struct) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Struct.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Struct) XXX_Merge(src proto.Message) { + xxx_messageInfo_Struct.Merge(m, src) +} +func (m *Struct) XXX_Size() int { + return m.Size() +} +func (m *Struct) XXX_DiscardUnknown() { + xxx_messageInfo_Struct.DiscardUnknown(m) } -func (m *Struct) Reset() { *m = Struct{} } -func (*Struct) ProtoMessage() {} -func (*Struct) Descriptor() ([]byte, []int) { return fileDescriptorStruct, []int{0} } -func (*Struct) XXX_WellKnownType() string { return "Struct" } +var xxx_messageInfo_Struct proto.InternalMessageInfo func (m *Struct) GetFields() map[string]*Value { if m != nil { @@ -85,6 +109,10 @@ func (m *Struct) GetFields() map[string]*Value { return nil } +func (*Struct) XXX_MessageName() string { + return "google.protobuf.Struct" +} + // `Value` represents a dynamically typed value which can be either // null, a number, a string, a boolean, a recursive struct value, or a // list of values. A producer of value is expected to set one of that @@ -101,38 +129,70 @@ type Value struct { // *Value_BoolValue // *Value_StructValue // *Value_ListValue - Kind isValue_Kind `protobuf_oneof:"kind"` + Kind isValue_Kind `protobuf_oneof:"kind"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Value) Reset() { *m = Value{} } +func (*Value) ProtoMessage() {} +func (*Value) Descriptor() ([]byte, []int) { + return fileDescriptor_df322afd6c9fb402, []int{1} +} +func (*Value) XXX_WellKnownType() string { return "Value" } +func (m *Value) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Value.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Value) XXX_Merge(src proto.Message) { + xxx_messageInfo_Value.Merge(m, src) +} +func (m *Value) XXX_Size() int { + return m.Size() +} +func (m *Value) XXX_DiscardUnknown() { + xxx_messageInfo_Value.DiscardUnknown(m) } -func (m *Value) Reset() { *m = Value{} } -func (*Value) ProtoMessage() {} -func (*Value) Descriptor() ([]byte, []int) { return fileDescriptorStruct, []int{1} } -func (*Value) XXX_WellKnownType() string { return "Value" } +var xxx_messageInfo_Value proto.InternalMessageInfo type isValue_Kind interface { isValue_Kind() Equal(interface{}) bool MarshalTo([]byte) (int, error) Size() int + Compare(interface{}) int } type Value_NullValue struct { - NullValue NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,proto3,enum=google.protobuf.NullValue,oneof"` + NullValue NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,proto3,enum=google.protobuf.NullValue,oneof" json:"null_value,omitempty"` } type Value_NumberValue struct { - NumberValue float64 `protobuf:"fixed64,2,opt,name=number_value,json=numberValue,proto3,oneof"` + NumberValue float64 `protobuf:"fixed64,2,opt,name=number_value,json=numberValue,proto3,oneof" json:"number_value,omitempty"` } type Value_StringValue struct { - StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,proto3,oneof"` + StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,proto3,oneof" json:"string_value,omitempty"` } type Value_BoolValue struct { - BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,proto3,oneof"` + BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,proto3,oneof" json:"bool_value,omitempty"` } type Value_StructValue struct { - StructValue *Struct `protobuf:"bytes,5,opt,name=struct_value,json=structValue,oneof"` + StructValue *Struct `protobuf:"bytes,5,opt,name=struct_value,json=structValue,proto3,oneof" json:"struct_value,omitempty"` } type Value_ListValue struct { - ListValue *ListValue `protobuf:"bytes,6,opt,name=list_value,json=listValue,oneof"` + ListValue *ListValue `protobuf:"bytes,6,opt,name=list_value,json=listValue,proto3,oneof" json:"list_value,omitempty"` } func (*Value_NullValue) isValue_Kind() {} @@ -153,7 +213,7 @@ func (m *Value) GetNullValue() NullValue { if x, ok := m.GetKind().(*Value_NullValue); ok { return x.NullValue } - return NULL_VALUE + return NullValue_NULL_VALUE } func (m *Value) GetNumberValue() float64 { @@ -191,9 +251,9 @@ func (m *Value) GetListValue() *ListValue { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*Value) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _Value_OneofMarshaler, _Value_OneofUnmarshaler, _Value_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Value) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*Value_NullValue)(nil), (*Value_NumberValue)(nil), (*Value_StringValue)(nil), @@ -203,127 +263,8 @@ func (*Value) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, } } -func _Value_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*Value) - // kind - switch x := m.Kind.(type) { - case *Value_NullValue: - _ = b.EncodeVarint(1<<3 | proto.WireVarint) - _ = b.EncodeVarint(uint64(x.NullValue)) - case *Value_NumberValue: - _ = b.EncodeVarint(2<<3 | proto.WireFixed64) - _ = b.EncodeFixed64(math.Float64bits(x.NumberValue)) - case *Value_StringValue: - _ = b.EncodeVarint(3<<3 | proto.WireBytes) - _ = b.EncodeStringBytes(x.StringValue) - case *Value_BoolValue: - t := uint64(0) - if x.BoolValue { - t = 1 - } - _ = b.EncodeVarint(4<<3 | proto.WireVarint) - _ = b.EncodeVarint(t) - case *Value_StructValue: - _ = b.EncodeVarint(5<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.StructValue); err != nil { - return err - } - case *Value_ListValue: - _ = b.EncodeVarint(6<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.ListValue); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("Value.Kind has unexpected type %T", x) - } - return nil -} - -func _Value_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*Value) - switch tag { - case 1: // kind.null_value - if wire != proto.WireVarint { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeVarint() - m.Kind = &Value_NullValue{NullValue(x)} - return true, err - case 2: // kind.number_value - if wire != proto.WireFixed64 { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeFixed64() - m.Kind = &Value_NumberValue{math.Float64frombits(x)} - return true, err - case 3: // kind.string_value - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.Kind = &Value_StringValue{x} - return true, err - case 4: // kind.bool_value - if wire != proto.WireVarint { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeVarint() - m.Kind = &Value_BoolValue{x != 0} - return true, err - case 5: // kind.struct_value - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Struct) - err := b.DecodeMessage(msg) - m.Kind = &Value_StructValue{msg} - return true, err - case 6: // kind.list_value - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(ListValue) - err := b.DecodeMessage(msg) - m.Kind = &Value_ListValue{msg} - return true, err - default: - return false, nil - } -} - -func _Value_OneofSizer(msg proto.Message) (n int) { - m := msg.(*Value) - // kind - switch x := m.Kind.(type) { - case *Value_NullValue: - n += proto.SizeVarint(1<<3 | proto.WireVarint) - n += proto.SizeVarint(uint64(x.NullValue)) - case *Value_NumberValue: - n += proto.SizeVarint(2<<3 | proto.WireFixed64) - n += 8 - case *Value_StringValue: - n += proto.SizeVarint(3<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(len(x.StringValue))) - n += len(x.StringValue) - case *Value_BoolValue: - n += proto.SizeVarint(4<<3 | proto.WireVarint) - n += 1 - case *Value_StructValue: - s := proto.Size(x.StructValue) - n += proto.SizeVarint(5<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case *Value_ListValue: - s := proto.Size(x.ListValue) - n += proto.SizeVarint(6<<3 | proto.WireBytes) - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n +func (*Value) XXX_MessageName() string { + return "google.protobuf.Value" } // `ListValue` is a wrapper around a repeated field of values. @@ -331,13 +272,44 @@ func _Value_OneofSizer(msg proto.Message) (n int) { // The JSON representation for `ListValue` is JSON array. type ListValue struct { // Repeated field of dynamically typed values. - Values []*Value `protobuf:"bytes,1,rep,name=values" json:"values,omitempty"` + Values []*Value `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ListValue) Reset() { *m = ListValue{} } -func (*ListValue) ProtoMessage() {} -func (*ListValue) Descriptor() ([]byte, []int) { return fileDescriptorStruct, []int{2} } -func (*ListValue) XXX_WellKnownType() string { return "ListValue" } +func (m *ListValue) Reset() { *m = ListValue{} } +func (*ListValue) ProtoMessage() {} +func (*ListValue) Descriptor() ([]byte, []int) { + return fileDescriptor_df322afd6c9fb402, []int{2} +} +func (*ListValue) XXX_WellKnownType() string { return "ListValue" } +func (m *ListValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListValue.Merge(m, src) +} +func (m *ListValue) XXX_Size() int { + return m.Size() +} +func (m *ListValue) XXX_DiscardUnknown() { + xxx_messageInfo_ListValue.DiscardUnknown(m) +} + +var xxx_messageInfo_ListValue proto.InternalMessageInfo func (m *ListValue) GetValues() []*Value { if m != nil { @@ -346,11 +318,405 @@ func (m *ListValue) GetValues() []*Value { return nil } +func (*ListValue) XXX_MessageName() string { + return "google.protobuf.ListValue" +} func init() { + proto.RegisterEnum("google.protobuf.NullValue", NullValue_name, NullValue_value) proto.RegisterType((*Struct)(nil), "google.protobuf.Struct") + proto.RegisterMapType((map[string]*Value)(nil), "google.protobuf.Struct.FieldsEntry") proto.RegisterType((*Value)(nil), "google.protobuf.Value") proto.RegisterType((*ListValue)(nil), "google.protobuf.ListValue") - proto.RegisterEnum("google.protobuf.NullValue", NullValue_name, NullValue_value) +} + +func init() { proto.RegisterFile("google/protobuf/struct.proto", fileDescriptor_df322afd6c9fb402) } + +var fileDescriptor_df322afd6c9fb402 = []byte{ + // 443 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xb1, 0x6f, 0xd3, 0x40, + 0x14, 0xc6, 0xfd, 0x9c, 0xc6, 0x22, 0xcf, 0xa8, 0x54, 0x87, 0x04, 0x51, 0x41, 0x47, 0x94, 0x2e, + 0x11, 0x42, 0xae, 0x14, 0x16, 0x44, 0x58, 0x88, 0x54, 0x5a, 0x89, 0xa8, 0x32, 0x86, 0x16, 0x89, + 0x25, 0xc2, 0xae, 0x1b, 0x59, 0xbd, 0xde, 0x55, 0xf6, 0x1d, 0x28, 0x1b, 0x0b, 0xff, 0x03, 0x33, + 0x13, 0x62, 0xe4, 0xaf, 0xe8, 0xc8, 0xc8, 0x48, 0xdc, 0x85, 0xb1, 0x63, 0x47, 0x74, 0x77, 0xb6, + 0x41, 0x8d, 0xb2, 0xf9, 0x7d, 0xf7, 0x7b, 0xdf, 0x7b, 0xdf, 0x33, 0xde, 0x9f, 0x09, 0x31, 0x63, + 0xe9, 0xf6, 0x59, 0x2e, 0xa4, 0x88, 0xd5, 0xf1, 0x76, 0x21, 0x73, 0x95, 0xc8, 0xc0, 0xd4, 0xe4, + 0x96, 0x7d, 0x0d, 0xea, 0xd7, 0xfe, 0x17, 0x40, 0xef, 0xb5, 0x21, 0xc8, 0x08, 0xbd, 0xe3, 0x2c, + 0x65, 0x47, 0x45, 0x17, 0x7a, 0xad, 0x81, 0x3f, 0xdc, 0x0a, 0xae, 0xc1, 0x81, 0x05, 0x83, 0x17, + 0x86, 0xda, 0xe1, 0x32, 0x9f, 0x47, 0x55, 0xcb, 0xe6, 0x2b, 0xf4, 0xff, 0x93, 0xc9, 0x06, 0xb6, + 0x4e, 0xd2, 0x79, 0x17, 0x7a, 0x30, 0xe8, 0x44, 0xfa, 0x93, 0x3c, 0xc2, 0xf6, 0x87, 0xf7, 0x4c, + 0xa5, 0x5d, 0xb7, 0x07, 0x03, 0x7f, 0x78, 0x67, 0xc9, 0xfc, 0x50, 0xbf, 0x46, 0x16, 0x7a, 0xea, + 0x3e, 0x81, 0xfe, 0x0f, 0x17, 0xdb, 0x46, 0x24, 0x23, 0x44, 0xae, 0x18, 0x9b, 0x5a, 0x03, 0x6d, + 0xba, 0x3e, 0xdc, 0x5c, 0x32, 0xd8, 0x57, 0x8c, 0x19, 0x7e, 0xcf, 0x89, 0x3a, 0xbc, 0x2e, 0xc8, + 0x16, 0xde, 0xe4, 0xea, 0x34, 0x4e, 0xf3, 0xe9, 0xbf, 0xf9, 0xb0, 0xe7, 0x44, 0xbe, 0x55, 0x1b, + 0xa8, 0x90, 0x79, 0xc6, 0x67, 0x15, 0xd4, 0xd2, 0x8b, 0x6b, 0xc8, 0xaa, 0x16, 0x7a, 0x80, 0x18, + 0x0b, 0x51, 0xaf, 0xb1, 0xd6, 0x83, 0xc1, 0x0d, 0x3d, 0x4a, 0x6b, 0x16, 0x78, 0x66, 0x5c, 0x54, + 0x22, 0x2b, 0xa4, 0x6d, 0xa2, 0xde, 0x5d, 0x71, 0xc7, 0xca, 0x5e, 0x25, 0xb2, 0x49, 0xc9, 0xb2, + 0xa2, 0xee, 0xf5, 0x4c, 0xef, 0x72, 0xca, 0x49, 0x56, 0xc8, 0x26, 0x25, 0xab, 0x8b, 0xb1, 0x87, + 0x6b, 0x27, 0x19, 0x3f, 0xea, 0x8f, 0xb0, 0xd3, 0x10, 0x24, 0x40, 0xcf, 0x98, 0xd5, 0x7f, 0x74, + 0xd5, 0xd1, 0x2b, 0xea, 0xe1, 0x3d, 0xec, 0x34, 0x47, 0x24, 0xeb, 0x88, 0xfb, 0x07, 0x93, 0xc9, + 0xf4, 0xf0, 0xf9, 0xe4, 0x60, 0x67, 0xc3, 0x19, 0x7f, 0x86, 0x5f, 0x0b, 0xea, 0x5c, 0x2e, 0x28, + 0x5c, 0x2d, 0x28, 0x7c, 0x2a, 0x29, 0x7c, 0x2b, 0x29, 0x9c, 0x97, 0x14, 0x7e, 0x96, 0x14, 0x7e, + 0x97, 0x14, 0xfe, 0x94, 0xd4, 0xb9, 0xd4, 0xfa, 0x05, 0x85, 0xf3, 0x0b, 0x0a, 0x78, 0x3b, 0x11, + 0xa7, 0xd7, 0x47, 0x8e, 0x7d, 0x9b, 0x3e, 0xd4, 0x75, 0x08, 0xef, 0xda, 0x72, 0x7e, 0x96, 0x16, + 0x57, 0x00, 0x5f, 0xdd, 0xd6, 0x6e, 0x38, 0xfe, 0xee, 0xd2, 0x5d, 0xdb, 0x10, 0xd6, 0x3b, 0xbe, + 0x4d, 0x19, 0x7b, 0xc9, 0xc5, 0x47, 0xfe, 0x46, 0x93, 0xb1, 0x67, 0x9c, 0x1e, 0xff, 0x0d, 0x00, + 0x00, 0xff, 0xff, 0x26, 0x30, 0xdb, 0xbe, 0xe9, 0x02, 0x00, 0x00, +} + +func (this *Struct) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Struct) + if !ok { + that2, ok := that.(Struct) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if len(this.Fields) != len(that1.Fields) { + if len(this.Fields) < len(that1.Fields) { + return -1 + } + return 1 + } + for i := range this.Fields { + if c := this.Fields[i].Compare(that1.Fields[i]); c != 0 { + return c + } + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *Value) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Value) + if !ok { + that2, ok := that.(Value) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if that1.Kind == nil { + if this.Kind != nil { + return 1 + } + } else if this.Kind == nil { + return -1 + } else { + thisType := -1 + switch this.Kind.(type) { + case *Value_NullValue: + thisType = 0 + case *Value_NumberValue: + thisType = 1 + case *Value_StringValue: + thisType = 2 + case *Value_BoolValue: + thisType = 3 + case *Value_StructValue: + thisType = 4 + case *Value_ListValue: + thisType = 5 + default: + panic(fmt.Sprintf("compare: unexpected type %T in oneof", this.Kind)) + } + that1Type := -1 + switch that1.Kind.(type) { + case *Value_NullValue: + that1Type = 0 + case *Value_NumberValue: + that1Type = 1 + case *Value_StringValue: + that1Type = 2 + case *Value_BoolValue: + that1Type = 3 + case *Value_StructValue: + that1Type = 4 + case *Value_ListValue: + that1Type = 5 + default: + panic(fmt.Sprintf("compare: unexpected type %T in oneof", that1.Kind)) + } + if thisType == that1Type { + if c := this.Kind.Compare(that1.Kind); c != 0 { + return c + } + } else if thisType < that1Type { + return -1 + } else if thisType > that1Type { + return 1 + } + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *Value_NullValue) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Value_NullValue) + if !ok { + that2, ok := that.(Value_NullValue) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.NullValue != that1.NullValue { + if this.NullValue < that1.NullValue { + return -1 + } + return 1 + } + return 0 +} +func (this *Value_NumberValue) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Value_NumberValue) + if !ok { + that2, ok := that.(Value_NumberValue) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.NumberValue != that1.NumberValue { + if this.NumberValue < that1.NumberValue { + return -1 + } + return 1 + } + return 0 +} +func (this *Value_StringValue) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Value_StringValue) + if !ok { + that2, ok := that.(Value_StringValue) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.StringValue != that1.StringValue { + if this.StringValue < that1.StringValue { + return -1 + } + return 1 + } + return 0 +} +func (this *Value_BoolValue) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Value_BoolValue) + if !ok { + that2, ok := that.(Value_BoolValue) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.BoolValue != that1.BoolValue { + if !this.BoolValue { + return -1 + } + return 1 + } + return 0 +} +func (this *Value_StructValue) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Value_StructValue) + if !ok { + that2, ok := that.(Value_StructValue) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if c := this.StructValue.Compare(that1.StructValue); c != 0 { + return c + } + return 0 +} +func (this *Value_ListValue) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Value_ListValue) + if !ok { + that2, ok := that.(Value_ListValue) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if c := this.ListValue.Compare(that1.ListValue); c != 0 { + return c + } + return 0 +} +func (this *ListValue) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*ListValue) + if !ok { + that2, ok := that.(ListValue) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if len(this.Values) != len(that1.Values) { + if len(this.Values) < len(that1.Values) { + return -1 + } + return 1 + } + for i := range this.Values { + if c := this.Values[i].Compare(that1.Values[i]); c != 0 { + return c + } + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 } func (x NullValue) String() string { s, ok := NullValue_name[int32(x)] @@ -386,6 +752,9 @@ func (this *Struct) Equal(that interface{}) bool { return false } } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *Value) Equal(that interface{}) bool { @@ -416,6 +785,9 @@ func (this *Value) Equal(that interface{}) bool { } else if !this.Kind.Equal(that1.Kind) { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *Value_NullValue) Equal(that interface{}) bool { @@ -589,6 +961,9 @@ func (this *ListValue) Equal(that interface{}) bool { return false } } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *Struct) GoString() string { @@ -601,7 +976,7 @@ func (this *Struct) GoString() string { for k := range this.Fields { keysForFields = append(keysForFields, k) } - sortkeys.Strings(keysForFields) + github_com_gogo_protobuf_sortkeys.Strings(keysForFields) mapStringForFields := "map[string]*Value{" for _, k := range keysForFields { mapStringForFields += fmt.Sprintf("%#v: %#v,", k, this.Fields[k]) @@ -610,6 +985,9 @@ func (this *Struct) GoString() string { if this.Fields != nil { s = append(s, "Fields: "+mapStringForFields+",\n") } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -622,6 +1000,9 @@ func (this *Value) GoString() string { if this.Kind != nil { s = append(s, "Kind: "+fmt.Sprintf("%#v", this.Kind)+",\n") } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -682,6 +1063,9 @@ func (this *ListValue) GoString() string { if this.Values != nil { s = append(s, "Values: "+fmt.Sprintf("%#v", this.Values)+",\n") } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -696,7 +1080,7 @@ func valueToGoStringStruct(v interface{}, typ string) string { func (m *Struct) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -704,45 +1088,52 @@ func (m *Struct) Marshal() (dAtA []byte, err error) { } func (m *Struct) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Struct) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Fields) > 0 { for k := range m.Fields { - dAtA[i] = 0xa - i++ v := m.Fields[k] - msgSize := 0 - if v != nil { - msgSize = v.Size() - msgSize += 1 + sovStruct(uint64(msgSize)) - } - mapSize := 1 + len(k) + sovStruct(uint64(len(k))) + msgSize - i = encodeVarintStruct(dAtA, i, uint64(mapSize)) - dAtA[i] = 0xa - i++ - i = encodeVarintStruct(dAtA, i, uint64(len(k))) - i += copy(dAtA[i:], k) + baseI := i if v != nil { - dAtA[i] = 0x12 - i++ - i = encodeVarintStruct(dAtA, i, uint64(v.Size())) - n1, err := v.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := v.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStruct(dAtA, i, uint64(size)) } - i += n1 + i-- + dAtA[i] = 0x12 } + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintStruct(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintStruct(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func (m *Value) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -750,87 +1141,133 @@ func (m *Value) Marshal() (dAtA []byte, err error) { } func (m *Value) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Value) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Kind != nil { - nn2, err := m.Kind.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size := m.Kind.Size() + i -= size + if _, err := m.Kind.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } } - i += nn2 } - return i, nil + return len(dAtA) - i, nil } func (m *Value_NullValue) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x8 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Value_NullValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) i = encodeVarintStruct(dAtA, i, uint64(m.NullValue)) - return i, nil + i-- + dAtA[i] = 0x8 + return len(dAtA) - i, nil } func (m *Value_NumberValue) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Value_NumberValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= 8 + encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.NumberValue)))) + i-- dAtA[i] = 0x11 - i++ - binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.NumberValue)))) - i += 8 - return i, nil + return len(dAtA) - i, nil } func (m *Value_StringValue) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x1a - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Value_StringValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i -= len(m.StringValue) + copy(dAtA[i:], m.StringValue) i = encodeVarintStruct(dAtA, i, uint64(len(m.StringValue))) - i += copy(dAtA[i:], m.StringValue) - return i, nil + i-- + dAtA[i] = 0x1a + return len(dAtA) - i, nil } func (m *Value_BoolValue) MarshalTo(dAtA []byte) (int, error) { - i := 0 - dAtA[i] = 0x20 - i++ + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Value_BoolValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + i-- if m.BoolValue { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ - return i, nil + i-- + dAtA[i] = 0x20 + return len(dAtA) - i, nil } func (m *Value_StructValue) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Value_StructValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.StructValue != nil { - dAtA[i] = 0x2a - i++ - i = encodeVarintStruct(dAtA, i, uint64(m.StructValue.Size())) - n3, err := m.StructValue.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.StructValue.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStruct(dAtA, i, uint64(size)) } - i += n3 + i-- + dAtA[i] = 0x2a } - return i, nil + return len(dAtA) - i, nil } func (m *Value_ListValue) MarshalTo(dAtA []byte) (int, error) { - i := 0 + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Value_ListValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) if m.ListValue != nil { - dAtA[i] = 0x32 - i++ - i = encodeVarintStruct(dAtA, i, uint64(m.ListValue.Size())) - n4, err := m.ListValue.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + { + size, err := m.ListValue.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStruct(dAtA, i, uint64(size)) } - i += n4 + i-- + dAtA[i] = 0x32 } - return i, nil + return len(dAtA) - i, nil } func (m *ListValue) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -838,37 +1275,50 @@ func (m *ListValue) Marshal() (dAtA []byte, err error) { } func (m *ListValue) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Values) > 0 { - for _, msg := range m.Values { - dAtA[i] = 0xa - i++ - i = encodeVarintStruct(dAtA, i, uint64(msg.Size())) - n, err := msg.MarshalTo(dAtA[i:]) - if err != nil { - return 0, err + for iNdEx := len(m.Values) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Values[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStruct(dAtA, i, uint64(size)) } - i += n + i-- + dAtA[i] = 0xa } } - return i, nil + return len(dAtA) - i, nil } func encodeVarintStruct(dAtA []byte, offset int, v uint64) int { + offset -= sovStruct(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func NewPopulatedStruct(r randyStruct, easy bool) *Struct { this := &Struct{} - if r.Intn(10) == 0 { + if r.Intn(5) == 0 { v1 := r.Intn(10) this.Fields = make(map[string]*Value) for i := 0; i < v1; i++ { @@ -876,6 +1326,7 @@ func NewPopulatedStruct(r randyStruct, easy bool) *Struct { } } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedStruct(r, 2) } return this } @@ -898,6 +1349,7 @@ func NewPopulatedValue(r randyStruct, easy bool) *Value { this.Kind = NewPopulatedValue_ListValue(r, easy) } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedStruct(r, 7) } return this } @@ -937,7 +1389,7 @@ func NewPopulatedValue_ListValue(r randyStruct, easy bool) *Value_ListValue { } func NewPopulatedListValue(r randyStruct, easy bool) *ListValue { this := &ListValue{} - if r.Intn(10) == 0 { + if r.Intn(5) == 0 { v2 := r.Intn(5) this.Values = make([]*Value, v2) for i := 0; i < v2; i++ { @@ -945,6 +1397,7 @@ func NewPopulatedListValue(r randyStruct, easy bool) *ListValue { } } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedStruct(r, 2) } return this } @@ -1022,6 +1475,9 @@ func encodeVarintPopulateStruct(dAtA []byte, v uint64) []byte { return dAtA } func (m *Struct) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Fields) > 0 { @@ -1037,31 +1493,49 @@ func (m *Struct) Size() (n int) { n += mapEntrySize + 1 + sovStruct(uint64(mapEntrySize)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *Value) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Kind != nil { n += m.Kind.Size() } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *Value_NullValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 1 + sovStruct(uint64(m.NullValue)) return n } func (m *Value_NumberValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 9 return n } func (m *Value_StringValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.StringValue) @@ -1069,12 +1543,18 @@ func (m *Value_StringValue) Size() (n int) { return n } func (m *Value_BoolValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l n += 2 return n } func (m *Value_StructValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.StructValue != nil { @@ -1084,6 +1564,9 @@ func (m *Value_StructValue) Size() (n int) { return n } func (m *Value_ListValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.ListValue != nil { @@ -1093,6 +1576,9 @@ func (m *Value_ListValue) Size() (n int) { return n } func (m *ListValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Values) > 0 { @@ -1101,18 +1587,14 @@ func (m *ListValue) Size() (n int) { n += 1 + l + sovStruct(uint64(l)) } } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovStruct(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozStruct(x uint64) (n int) { return sovStruct(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -1125,7 +1607,7 @@ func (this *Struct) String() string { for k := range this.Fields { keysForFields = append(keysForFields, k) } - sortkeys.Strings(keysForFields) + github_com_gogo_protobuf_sortkeys.Strings(keysForFields) mapStringForFields := "map[string]*Value{" for _, k := range keysForFields { mapStringForFields += fmt.Sprintf("%v: %v,", k, this.Fields[k]) @@ -1133,6 +1615,7 @@ func (this *Struct) String() string { mapStringForFields += "}" s := strings.Join([]string{`&Struct{`, `Fields:` + mapStringForFields + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1143,6 +1626,7 @@ func (this *Value) String() string { } s := strings.Join([]string{`&Value{`, `Kind:` + fmt.Sprintf("%v", this.Kind) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1211,8 +1695,14 @@ func (this *ListValue) String() string { if this == nil { return "nil" } + repeatedStringForValues := "[]*Value{" + for _, f := range this.Values { + repeatedStringForValues += strings.Replace(f.String(), "Value", "Value", 1) + "," + } + repeatedStringForValues += "}" s := strings.Join([]string{`&ListValue{`, - `Values:` + strings.Replace(fmt.Sprintf("%v", this.Values), "Value", "Value", 1) + `,`, + `Values:` + repeatedStringForValues + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1240,7 +1730,7 @@ func (m *Struct) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1268,7 +1758,7 @@ func (m *Struct) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1277,6 +1767,9 @@ func (m *Struct) Unmarshal(dAtA []byte) error { return ErrInvalidLengthStruct } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStruct + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1297,7 +1790,7 @@ func (m *Struct) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1314,7 +1807,7 @@ func (m *Struct) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= (uint64(b) & 0x7F) << shift + stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1324,6 +1817,9 @@ func (m *Struct) Unmarshal(dAtA []byte) error { return ErrInvalidLengthStruct } postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthStruct + } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } @@ -1340,7 +1836,7 @@ func (m *Struct) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - mapmsglen |= (int(b) & 0x7F) << shift + mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1349,7 +1845,7 @@ func (m *Struct) Unmarshal(dAtA []byte) error { return ErrInvalidLengthStruct } postmsgIndex := iNdEx + mapmsglen - if mapmsglen < 0 { + if postmsgIndex < 0 { return ErrInvalidLengthStruct } if postmsgIndex > l { @@ -1366,7 +1862,7 @@ func (m *Struct) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStruct } if (iNdEx + skippy) > postIndex { @@ -1383,12 +1879,13 @@ func (m *Struct) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStruct } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1413,7 +1910,7 @@ func (m *Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1441,7 +1938,7 @@ func (m *Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (NullValue(b) & 0x7F) << shift + v |= NullValue(b&0x7F) << shift if b < 0x80 { break } @@ -1455,7 +1952,7 @@ func (m *Value) Unmarshal(dAtA []byte) error { if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } - v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) + v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Kind = &Value_NumberValue{float64(math.Float64frombits(v))} case 3: @@ -1472,7 +1969,7 @@ func (m *Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1482,6 +1979,9 @@ func (m *Value) Unmarshal(dAtA []byte) error { return ErrInvalidLengthStruct } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStruct + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1501,7 +2001,7 @@ func (m *Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1522,7 +2022,7 @@ func (m *Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1531,6 +2031,9 @@ func (m *Value) Unmarshal(dAtA []byte) error { return ErrInvalidLengthStruct } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStruct + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1554,7 +2057,7 @@ func (m *Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1563,6 +2066,9 @@ func (m *Value) Unmarshal(dAtA []byte) error { return ErrInvalidLengthStruct } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStruct + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1578,12 +2084,13 @@ func (m *Value) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStruct } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1608,7 +2115,7 @@ func (m *ListValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1636,7 +2143,7 @@ func (m *ListValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= (int(b) & 0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1645,6 +2152,9 @@ func (m *ListValue) Unmarshal(dAtA []byte) error { return ErrInvalidLengthStruct } postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStruct + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1659,12 +2169,13 @@ func (m *ListValue) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthStruct } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1677,6 +2188,7 @@ func (m *ListValue) Unmarshal(dAtA []byte) error { func skipStruct(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1708,10 +2220,8 @@ func skipStruct(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1728,86 +2238,34 @@ func skipStruct(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthStruct } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowStruct - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipStruct(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupStruct + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthStruct + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthStruct = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowStruct = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthStruct = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowStruct = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupStruct = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("struct.proto", fileDescriptorStruct) } - -var fileDescriptorStruct = []byte{ - // 432 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x91, 0xb1, 0x6f, 0xd3, 0x40, - 0x14, 0xc6, 0xfd, 0x9c, 0xc6, 0x22, 0xcf, 0x55, 0xa9, 0x0e, 0x09, 0xa2, 0x22, 0x1d, 0x51, 0xba, - 0x58, 0x08, 0x79, 0x08, 0x0b, 0x22, 0x2c, 0x58, 0x2a, 0xad, 0x84, 0x55, 0x19, 0x43, 0x8b, 0xc4, - 0x12, 0xe1, 0xd4, 0x8d, 0xac, 0x5e, 0xef, 0x2a, 0xfb, 0x0c, 0xca, 0x06, 0xff, 0x05, 0x33, 0x13, - 0x62, 0xe4, 0xaf, 0x60, 0xec, 0xc8, 0x88, 0x3d, 0x31, 0x76, 0xec, 0x88, 0xee, 0xce, 0x36, 0xa8, - 0x51, 0x36, 0xbf, 0xcf, 0xbf, 0xf7, 0xbd, 0xf7, 0xbd, 0xc3, 0xcd, 0x42, 0xe6, 0xe5, 0x5c, 0xfa, - 0x17, 0xb9, 0x90, 0x82, 0xdc, 0x5e, 0x08, 0xb1, 0x60, 0xa9, 0xa9, 0x92, 0xf2, 0x74, 0xfc, 0x05, - 0xd0, 0x79, 0xad, 0x09, 0x32, 0x45, 0xe7, 0x34, 0x4b, 0xd9, 0x49, 0x31, 0x84, 0x51, 0xcf, 0x73, - 0x27, 0xbb, 0xfe, 0x0d, 0xd8, 0x37, 0xa0, 0xff, 0x42, 0x53, 0x7b, 0x5c, 0xe6, 0xcb, 0xb8, 0x69, - 0xd9, 0x79, 0x85, 0xee, 0x7f, 0x32, 0xd9, 0xc6, 0xde, 0x59, 0xba, 0x1c, 0xc2, 0x08, 0xbc, 0x41, - 0xac, 0x3e, 0xc9, 0x23, 0xec, 0x7f, 0x78, 0xcf, 0xca, 0x74, 0x68, 0x8f, 0xc0, 0x73, 0x27, 0x77, - 0x57, 0xcc, 0x8f, 0xd5, 0xdf, 0xd8, 0x40, 0x4f, 0xed, 0x27, 0x30, 0xfe, 0x61, 0x63, 0x5f, 0x8b, - 0x64, 0x8a, 0xc8, 0x4b, 0xc6, 0x66, 0xc6, 0x40, 0x99, 0x6e, 0x4d, 0x76, 0x56, 0x0c, 0x0e, 0x4b, - 0xc6, 0x34, 0x7f, 0x60, 0xc5, 0x03, 0xde, 0x16, 0x64, 0x17, 0x37, 0x79, 0x79, 0x9e, 0xa4, 0xf9, - 0xec, 0xdf, 0x7c, 0x38, 0xb0, 0x62, 0xd7, 0xa8, 0x1d, 0x54, 0xc8, 0x3c, 0xe3, 0x8b, 0x06, 0xea, - 0xa9, 0xc5, 0x15, 0x64, 0x54, 0x03, 0x3d, 0x40, 0x4c, 0x84, 0x68, 0xd7, 0xd8, 0x18, 0x81, 0x77, - 0x4b, 0x8d, 0x52, 0x9a, 0x01, 0x9e, 0xb5, 0xd7, 0x6e, 0x90, 0xbe, 0x8e, 0x7a, 0x6f, 0xcd, 0x1d, - 0x1b, 0xfb, 0x72, 0x2e, 0xbb, 0x94, 0x2c, 0x2b, 0xda, 0x5e, 0x47, 0xf7, 0xae, 0xa6, 0x0c, 0xb3, - 0x42, 0x76, 0x29, 0x59, 0x5b, 0x04, 0x0e, 0x6e, 0x9c, 0x65, 0xfc, 0x64, 0x3c, 0xc5, 0x41, 0x47, - 0x10, 0x1f, 0x1d, 0x6d, 0xd6, 0xbe, 0xe8, 0xba, 0xa3, 0x37, 0xd4, 0xc3, 0xfb, 0x38, 0xe8, 0x8e, - 0x48, 0xb6, 0x10, 0x0f, 0x8f, 0xc2, 0x70, 0x76, 0xfc, 0x3c, 0x3c, 0xda, 0xdb, 0xb6, 0x82, 0xcf, - 0x70, 0x59, 0x51, 0xeb, 0x57, 0x45, 0xad, 0xab, 0x8a, 0xc2, 0x75, 0x45, 0xe1, 0x53, 0x4d, 0xe1, - 0x5b, 0x4d, 0xe1, 0x67, 0x4d, 0xe1, 0xb2, 0xa6, 0xf0, 0xbb, 0xa6, 0xf0, 0xa7, 0xa6, 0xd6, 0x55, - 0x4d, 0x01, 0xef, 0xcc, 0xc5, 0xf9, 0xcd, 0x71, 0x81, 0x6b, 0x92, 0x47, 0xaa, 0x8e, 0xe0, 0x5d, - 0x5f, 0x2e, 0x2f, 0xd2, 0xe2, 0x1a, 0xe0, 0xab, 0xdd, 0xdb, 0x8f, 0x82, 0xef, 0x36, 0xdd, 0x37, - 0x0d, 0x51, 0xbb, 0xdf, 0xdb, 0x94, 0xb1, 0x97, 0x5c, 0x7c, 0xe4, 0x6f, 0x14, 0x99, 0x38, 0xda, - 0xe9, 0xf1, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x75, 0xc5, 0x1c, 0x3b, 0xd5, 0x02, 0x00, 0x00, -} diff --git a/vendor/github.com/gogo/protobuf/types/timestamp.go b/vendor/github.com/gogo/protobuf/types/timestamp.go index 7ae54d8b3ff68..232ada57ce420 100644 --- a/vendor/github.com/gogo/protobuf/types/timestamp.go +++ b/vendor/github.com/gogo/protobuf/types/timestamp.go @@ -109,11 +109,9 @@ func TimestampNow() *Timestamp { // TimestampProto converts the time.Time to a google.protobuf.Timestamp proto. // It returns an error if the resulting Timestamp is invalid. func TimestampProto(t time.Time) (*Timestamp, error) { - seconds := t.Unix() - nanos := int32(t.Sub(time.Unix(seconds, 0))) ts := &Timestamp{ - Seconds: seconds, - Nanos: nanos, + Seconds: t.Unix(), + Nanos: int32(t.Nanosecond()), } if err := validateTimestamp(ts); err != nil { return nil, err diff --git a/vendor/github.com/gogo/protobuf/types/timestamp.pb.go b/vendor/github.com/gogo/protobuf/types/timestamp.pb.go index 41b18f941f4ee..45db7b3bb1c8a 100644 --- a/vendor/github.com/gogo/protobuf/types/timestamp.pb.go +++ b/vendor/github.com/gogo/protobuf/types/timestamp.pb.go @@ -1,25 +1,18 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: timestamp.proto +// source: google/protobuf/timestamp.proto -/* - Package types is a generated protocol buffer package. - - It is generated from these files: - timestamp.proto - - It has these top-level messages: - Timestamp -*/ package types -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -import strings "strings" -import reflect "reflect" - -import io "io" +import ( + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -30,19 +23,21 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// A Timestamp represents a point in time independent of any time zone -// or calendar, represented as seconds and fractions of seconds at -// nanosecond resolution in UTC Epoch time. It is encoded using the -// Proleptic Gregorian Calendar which extends the Gregorian calendar -// backwards to year one. It is encoded assuming all minutes are 60 -// seconds long, i.e. leap seconds are "smeared" so that no leap second -// table is needed for interpretation. Range is from -// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. -// By restricting to that range, we ensure that we can convert to -// and from RFC 3339 date strings. -// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. // // # Examples // @@ -95,19 +90,23 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone -// is required, though only UTC (as indicated by "Z") is presently supported. +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). // // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past // 01:30 UTC on January 15, 2017. // // In JavaScript, one can convert a Date object to this format using the -// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) // method. In Python, a standard `datetime.datetime` object can be converted -// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) -// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one -// can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( -// http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()) -// to obtain a formatter capable of generating timestamps in this format. +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// ) to obtain a formatter capable of generating timestamps in this format. // // type Timestamp struct { @@ -119,13 +118,44 @@ type Timestamp struct { // second values with fractions must still have non-negative nanos values // that count forward in time. Must be from 0 to 999,999,999 // inclusive. - Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` + Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Timestamp) Reset() { *m = Timestamp{} } +func (*Timestamp) ProtoMessage() {} +func (*Timestamp) Descriptor() ([]byte, []int) { + return fileDescriptor_292007bbfe81227e, []int{0} +} +func (*Timestamp) XXX_WellKnownType() string { return "Timestamp" } +func (m *Timestamp) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Timestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Timestamp.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Timestamp) XXX_Merge(src proto.Message) { + xxx_messageInfo_Timestamp.Merge(m, src) +} +func (m *Timestamp) XXX_Size() int { + return m.Size() +} +func (m *Timestamp) XXX_DiscardUnknown() { + xxx_messageInfo_Timestamp.DiscardUnknown(m) } -func (m *Timestamp) Reset() { *m = Timestamp{} } -func (*Timestamp) ProtoMessage() {} -func (*Timestamp) Descriptor() ([]byte, []int) { return fileDescriptorTimestamp, []int{0} } -func (*Timestamp) XXX_WellKnownType() string { return "Timestamp" } +var xxx_messageInfo_Timestamp proto.InternalMessageInfo func (m *Timestamp) GetSeconds() int64 { if m != nil { @@ -141,9 +171,33 @@ func (m *Timestamp) GetNanos() int32 { return 0 } +func (*Timestamp) XXX_MessageName() string { + return "google.protobuf.Timestamp" +} func init() { proto.RegisterType((*Timestamp)(nil), "google.protobuf.Timestamp") } + +func init() { proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor_292007bbfe81227e) } + +var fileDescriptor_292007bbfe81227e = []byte{ + // 212 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d, + 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0xd0, 0x03, 0x0b, 0x09, 0xf1, 0x43, 0x14, 0xe8, 0xc1, 0x14, 0x28, + 0x59, 0x73, 0x71, 0x86, 0xc0, 0xd4, 0x08, 0x49, 0x70, 0xb1, 0x17, 0xa7, 0x26, 0xe7, 0xe7, 0xa5, + 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x30, 0x07, 0xc1, 0xb8, 0x42, 0x22, 0x5c, 0xac, 0x79, 0x89, + 0x79, 0xf9, 0xc5, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xac, 0x41, 0x10, 0x8e, 0x53, 0x03, 0xe3, 0x8d, + 0x87, 0x72, 0x0c, 0x1f, 0x1e, 0xca, 0x31, 0xae, 0x78, 0x24, 0xc7, 0x78, 0xe2, 0x91, 0x1c, 0xe3, + 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0xbe, 0x78, 0x24, 0xc7, 0xf0, 0xe1, 0x91, 0x1c, + 0xe3, 0x8a, 0xc7, 0x72, 0x8c, 0x27, 0x1e, 0xcb, 0x31, 0x72, 0x09, 0x27, 0xe7, 0xe7, 0xea, 0xa1, + 0x59, 0xee, 0xc4, 0x07, 0xb7, 0x3a, 0x00, 0x24, 0x14, 0xc0, 0x18, 0xc5, 0x5a, 0x52, 0x59, 0x90, + 0x5a, 0xfc, 0x83, 0x91, 0x71, 0x11, 0x13, 0xb3, 0x7b, 0x80, 0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, + 0x9e, 0x00, 0xa8, 0x1e, 0xbd, 0xf0, 0xd4, 0x9c, 0x1c, 0xef, 0xbc, 0xfc, 0xf2, 0xbc, 0x10, 0x90, + 0xca, 0x24, 0x36, 0xb0, 0x61, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0b, 0x23, 0x83, 0xdd, + 0xfa, 0x00, 0x00, 0x00, +} + func (this *Timestamp) Compare(that interface{}) int { if that == nil { if this == nil { @@ -181,6 +235,9 @@ func (this *Timestamp) Compare(that interface{}) int { } return 1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *Timestamp) Equal(that interface{}) bool { @@ -208,6 +265,9 @@ func (this *Timestamp) Equal(that interface{}) bool { if this.Nanos != that1.Nanos { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *Timestamp) GoString() string { @@ -218,6 +278,9 @@ func (this *Timestamp) GoString() string { s = append(s, "&types.Timestamp{") s = append(s, "Seconds: "+fmt.Sprintf("%#v", this.Seconds)+",\n") s = append(s, "Nanos: "+fmt.Sprintf("%#v", this.Nanos)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -232,7 +295,7 @@ func valueToGoStringTimestamp(v interface{}, typ string) string { func (m *Timestamp) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -240,33 +303,47 @@ func (m *Timestamp) Marshal() (dAtA []byte, err error) { } func (m *Timestamp) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Timestamp) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l - if m.Seconds != 0 { - dAtA[i] = 0x8 - i++ - i = encodeVarintTimestamp(dAtA, i, uint64(m.Seconds)) + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) } if m.Nanos != 0 { - dAtA[i] = 0x10 - i++ i = encodeVarintTimestamp(dAtA, i, uint64(m.Nanos)) + i-- + dAtA[i] = 0x10 + } + if m.Seconds != 0 { + i = encodeVarintTimestamp(dAtA, i, uint64(m.Seconds)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func encodeVarintTimestamp(dAtA []byte, offset int, v uint64) int { + offset -= sovTimestamp(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func (m *Timestamp) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Seconds != 0 { @@ -275,18 +352,14 @@ func (m *Timestamp) Size() (n int) { if m.Nanos != 0 { n += 1 + sovTimestamp(uint64(m.Nanos)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovTimestamp(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozTimestamp(x uint64) (n int) { return sovTimestamp(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -306,7 +379,7 @@ func (m *Timestamp) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -334,7 +407,7 @@ func (m *Timestamp) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Seconds |= (int64(b) & 0x7F) << shift + m.Seconds |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -353,7 +426,7 @@ func (m *Timestamp) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Nanos |= (int32(b) & 0x7F) << shift + m.Nanos |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -364,12 +437,13 @@ func (m *Timestamp) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthTimestamp } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -382,6 +456,7 @@ func (m *Timestamp) Unmarshal(dAtA []byte) error { func skipTimestamp(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -413,10 +488,8 @@ func skipTimestamp(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -433,72 +506,34 @@ func skipTimestamp(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthTimestamp } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTimestamp - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipTimestamp(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTimestamp + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthTimestamp + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthTimestamp = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowTimestamp = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthTimestamp = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTimestamp = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTimestamp = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("timestamp.proto", fileDescriptorTimestamp) } - -var fileDescriptorTimestamp = []byte{ - // 205 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2f, 0xc9, 0xcc, 0x4d, - 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4f, 0xcf, 0xcf, - 0x4f, 0xcf, 0x49, 0x85, 0xf0, 0x92, 0x4a, 0xd3, 0x94, 0xac, 0xb9, 0x38, 0x43, 0x60, 0x6a, 0x84, - 0x24, 0xb8, 0xd8, 0x8b, 0x53, 0x93, 0xf3, 0xf3, 0x52, 0x8a, 0x25, 0x18, 0x15, 0x18, 0x35, 0x98, - 0x83, 0x60, 0x5c, 0x21, 0x11, 0x2e, 0xd6, 0xbc, 0xc4, 0xbc, 0xfc, 0x62, 0x09, 0x26, 0x05, 0x46, - 0x0d, 0xd6, 0x20, 0x08, 0xc7, 0xa9, 0x81, 0xf1, 0xc2, 0x43, 0x39, 0x86, 0x1b, 0x0f, 0xe5, 0x18, - 0x3e, 0x3c, 0x94, 0x63, 0x5c, 0xf1, 0x48, 0x8e, 0xf1, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, - 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x7c, 0xf1, 0x48, 0x8e, 0xe1, 0xc3, 0x23, 0x39, 0xc6, 0x15, 0x8f, - 0xe5, 0x18, 0xb9, 0x84, 0x93, 0xf3, 0x73, 0xf5, 0xd0, 0x2c, 0x77, 0xe2, 0x83, 0x5b, 0x1d, 0x00, - 0x12, 0x0a, 0x60, 0x8c, 0x62, 0x2d, 0xa9, 0x2c, 0x48, 0x2d, 0xfe, 0xc1, 0xc8, 0xb8, 0x88, 0x89, - 0xd9, 0x3d, 0xc0, 0x69, 0x15, 0x93, 0x9c, 0x3b, 0x44, 0x4f, 0x00, 0x54, 0x8f, 0x5e, 0x78, 0x6a, - 0x4e, 0x8e, 0x77, 0x5e, 0x7e, 0x79, 0x5e, 0x08, 0x48, 0x65, 0x12, 0x1b, 0xd8, 0x30, 0x63, 0x40, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x9b, 0xa2, 0x42, 0xda, 0xea, 0x00, 0x00, 0x00, -} diff --git a/vendor/github.com/gogo/protobuf/types/type.pb.go b/vendor/github.com/gogo/protobuf/types/type.pb.go new file mode 100644 index 0000000000000..791427bb228aa --- /dev/null +++ b/vendor/github.com/gogo/protobuf/types/type.pb.go @@ -0,0 +1,3355 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: google/protobuf/type.proto + +package types + +import ( + bytes "bytes" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strconv "strconv" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// The syntax in which a protocol buffer element is defined. +type Syntax int32 + +const ( + // Syntax `proto2`. + Syntax_SYNTAX_PROTO2 Syntax = 0 + // Syntax `proto3`. + Syntax_SYNTAX_PROTO3 Syntax = 1 +) + +var Syntax_name = map[int32]string{ + 0: "SYNTAX_PROTO2", + 1: "SYNTAX_PROTO3", +} + +var Syntax_value = map[string]int32{ + "SYNTAX_PROTO2": 0, + "SYNTAX_PROTO3": 1, +} + +func (Syntax) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_dd271cc1e348c538, []int{0} +} + +// Basic field types. +type Field_Kind int32 + +const ( + // Field type unknown. + Field_TYPE_UNKNOWN Field_Kind = 0 + // Field type double. + Field_TYPE_DOUBLE Field_Kind = 1 + // Field type float. + Field_TYPE_FLOAT Field_Kind = 2 + // Field type int64. + Field_TYPE_INT64 Field_Kind = 3 + // Field type uint64. + Field_TYPE_UINT64 Field_Kind = 4 + // Field type int32. + Field_TYPE_INT32 Field_Kind = 5 + // Field type fixed64. + Field_TYPE_FIXED64 Field_Kind = 6 + // Field type fixed32. + Field_TYPE_FIXED32 Field_Kind = 7 + // Field type bool. + Field_TYPE_BOOL Field_Kind = 8 + // Field type string. + Field_TYPE_STRING Field_Kind = 9 + // Field type group. Proto2 syntax only, and deprecated. + Field_TYPE_GROUP Field_Kind = 10 + // Field type message. + Field_TYPE_MESSAGE Field_Kind = 11 + // Field type bytes. + Field_TYPE_BYTES Field_Kind = 12 + // Field type uint32. + Field_TYPE_UINT32 Field_Kind = 13 + // Field type enum. + Field_TYPE_ENUM Field_Kind = 14 + // Field type sfixed32. + Field_TYPE_SFIXED32 Field_Kind = 15 + // Field type sfixed64. + Field_TYPE_SFIXED64 Field_Kind = 16 + // Field type sint32. + Field_TYPE_SINT32 Field_Kind = 17 + // Field type sint64. + Field_TYPE_SINT64 Field_Kind = 18 +) + +var Field_Kind_name = map[int32]string{ + 0: "TYPE_UNKNOWN", + 1: "TYPE_DOUBLE", + 2: "TYPE_FLOAT", + 3: "TYPE_INT64", + 4: "TYPE_UINT64", + 5: "TYPE_INT32", + 6: "TYPE_FIXED64", + 7: "TYPE_FIXED32", + 8: "TYPE_BOOL", + 9: "TYPE_STRING", + 10: "TYPE_GROUP", + 11: "TYPE_MESSAGE", + 12: "TYPE_BYTES", + 13: "TYPE_UINT32", + 14: "TYPE_ENUM", + 15: "TYPE_SFIXED32", + 16: "TYPE_SFIXED64", + 17: "TYPE_SINT32", + 18: "TYPE_SINT64", +} + +var Field_Kind_value = map[string]int32{ + "TYPE_UNKNOWN": 0, + "TYPE_DOUBLE": 1, + "TYPE_FLOAT": 2, + "TYPE_INT64": 3, + "TYPE_UINT64": 4, + "TYPE_INT32": 5, + "TYPE_FIXED64": 6, + "TYPE_FIXED32": 7, + "TYPE_BOOL": 8, + "TYPE_STRING": 9, + "TYPE_GROUP": 10, + "TYPE_MESSAGE": 11, + "TYPE_BYTES": 12, + "TYPE_UINT32": 13, + "TYPE_ENUM": 14, + "TYPE_SFIXED32": 15, + "TYPE_SFIXED64": 16, + "TYPE_SINT32": 17, + "TYPE_SINT64": 18, +} + +func (Field_Kind) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_dd271cc1e348c538, []int{1, 0} +} + +// Whether a field is optional, required, or repeated. +type Field_Cardinality int32 + +const ( + // For fields with unknown cardinality. + Field_CARDINALITY_UNKNOWN Field_Cardinality = 0 + // For optional fields. + Field_CARDINALITY_OPTIONAL Field_Cardinality = 1 + // For required fields. Proto2 syntax only. + Field_CARDINALITY_REQUIRED Field_Cardinality = 2 + // For repeated fields. + Field_CARDINALITY_REPEATED Field_Cardinality = 3 +) + +var Field_Cardinality_name = map[int32]string{ + 0: "CARDINALITY_UNKNOWN", + 1: "CARDINALITY_OPTIONAL", + 2: "CARDINALITY_REQUIRED", + 3: "CARDINALITY_REPEATED", +} + +var Field_Cardinality_value = map[string]int32{ + "CARDINALITY_UNKNOWN": 0, + "CARDINALITY_OPTIONAL": 1, + "CARDINALITY_REQUIRED": 2, + "CARDINALITY_REPEATED": 3, +} + +func (Field_Cardinality) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_dd271cc1e348c538, []int{1, 1} +} + +// A protocol buffer message type. +type Type struct { + // The fully qualified message name. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The list of fields. + Fields []*Field `protobuf:"bytes,2,rep,name=fields,proto3" json:"fields,omitempty"` + // The list of types appearing in `oneof` definitions in this type. + Oneofs []string `protobuf:"bytes,3,rep,name=oneofs,proto3" json:"oneofs,omitempty"` + // The protocol buffer options. + Options []*Option `protobuf:"bytes,4,rep,name=options,proto3" json:"options,omitempty"` + // The source context. + SourceContext *SourceContext `protobuf:"bytes,5,opt,name=source_context,json=sourceContext,proto3" json:"source_context,omitempty"` + // The source syntax. + Syntax Syntax `protobuf:"varint,6,opt,name=syntax,proto3,enum=google.protobuf.Syntax" json:"syntax,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Type) Reset() { *m = Type{} } +func (*Type) ProtoMessage() {} +func (*Type) Descriptor() ([]byte, []int) { + return fileDescriptor_dd271cc1e348c538, []int{0} +} +func (m *Type) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Type) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Type.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Type) XXX_Merge(src proto.Message) { + xxx_messageInfo_Type.Merge(m, src) +} +func (m *Type) XXX_Size() int { + return m.Size() +} +func (m *Type) XXX_DiscardUnknown() { + xxx_messageInfo_Type.DiscardUnknown(m) +} + +var xxx_messageInfo_Type proto.InternalMessageInfo + +func (m *Type) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Type) GetFields() []*Field { + if m != nil { + return m.Fields + } + return nil +} + +func (m *Type) GetOneofs() []string { + if m != nil { + return m.Oneofs + } + return nil +} + +func (m *Type) GetOptions() []*Option { + if m != nil { + return m.Options + } + return nil +} + +func (m *Type) GetSourceContext() *SourceContext { + if m != nil { + return m.SourceContext + } + return nil +} + +func (m *Type) GetSyntax() Syntax { + if m != nil { + return m.Syntax + } + return Syntax_SYNTAX_PROTO2 +} + +func (*Type) XXX_MessageName() string { + return "google.protobuf.Type" +} + +// A single field of a message type. +type Field struct { + // The field type. + Kind Field_Kind `protobuf:"varint,1,opt,name=kind,proto3,enum=google.protobuf.Field_Kind" json:"kind,omitempty"` + // The field cardinality. + Cardinality Field_Cardinality `protobuf:"varint,2,opt,name=cardinality,proto3,enum=google.protobuf.Field_Cardinality" json:"cardinality,omitempty"` + // The field number. + Number int32 `protobuf:"varint,3,opt,name=number,proto3" json:"number,omitempty"` + // The field name. + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + // The field type URL, without the scheme, for message or enumeration + // types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`. + TypeUrl string `protobuf:"bytes,6,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"` + // The index of the field type in `Type.oneofs`, for message or enumeration + // types. The first type has index 1; zero means the type is not in the list. + OneofIndex int32 `protobuf:"varint,7,opt,name=oneof_index,json=oneofIndex,proto3" json:"oneof_index,omitempty"` + // Whether to use alternative packed wire representation. + Packed bool `protobuf:"varint,8,opt,name=packed,proto3" json:"packed,omitempty"` + // The protocol buffer options. + Options []*Option `protobuf:"bytes,9,rep,name=options,proto3" json:"options,omitempty"` + // The field JSON name. + JsonName string `protobuf:"bytes,10,opt,name=json_name,json=jsonName,proto3" json:"json_name,omitempty"` + // The string value of the default value of this field. Proto2 syntax only. + DefaultValue string `protobuf:"bytes,11,opt,name=default_value,json=defaultValue,proto3" json:"default_value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Field) Reset() { *m = Field{} } +func (*Field) ProtoMessage() {} +func (*Field) Descriptor() ([]byte, []int) { + return fileDescriptor_dd271cc1e348c538, []int{1} +} +func (m *Field) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Field) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Field.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Field) XXX_Merge(src proto.Message) { + xxx_messageInfo_Field.Merge(m, src) +} +func (m *Field) XXX_Size() int { + return m.Size() +} +func (m *Field) XXX_DiscardUnknown() { + xxx_messageInfo_Field.DiscardUnknown(m) +} + +var xxx_messageInfo_Field proto.InternalMessageInfo + +func (m *Field) GetKind() Field_Kind { + if m != nil { + return m.Kind + } + return Field_TYPE_UNKNOWN +} + +func (m *Field) GetCardinality() Field_Cardinality { + if m != nil { + return m.Cardinality + } + return Field_CARDINALITY_UNKNOWN +} + +func (m *Field) GetNumber() int32 { + if m != nil { + return m.Number + } + return 0 +} + +func (m *Field) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Field) GetTypeUrl() string { + if m != nil { + return m.TypeUrl + } + return "" +} + +func (m *Field) GetOneofIndex() int32 { + if m != nil { + return m.OneofIndex + } + return 0 +} + +func (m *Field) GetPacked() bool { + if m != nil { + return m.Packed + } + return false +} + +func (m *Field) GetOptions() []*Option { + if m != nil { + return m.Options + } + return nil +} + +func (m *Field) GetJsonName() string { + if m != nil { + return m.JsonName + } + return "" +} + +func (m *Field) GetDefaultValue() string { + if m != nil { + return m.DefaultValue + } + return "" +} + +func (*Field) XXX_MessageName() string { + return "google.protobuf.Field" +} + +// Enum type definition. +type Enum struct { + // Enum type name. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Enum value definitions. + Enumvalue []*EnumValue `protobuf:"bytes,2,rep,name=enumvalue,proto3" json:"enumvalue,omitempty"` + // Protocol buffer options. + Options []*Option `protobuf:"bytes,3,rep,name=options,proto3" json:"options,omitempty"` + // The source context. + SourceContext *SourceContext `protobuf:"bytes,4,opt,name=source_context,json=sourceContext,proto3" json:"source_context,omitempty"` + // The source syntax. + Syntax Syntax `protobuf:"varint,5,opt,name=syntax,proto3,enum=google.protobuf.Syntax" json:"syntax,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Enum) Reset() { *m = Enum{} } +func (*Enum) ProtoMessage() {} +func (*Enum) Descriptor() ([]byte, []int) { + return fileDescriptor_dd271cc1e348c538, []int{2} +} +func (m *Enum) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Enum) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Enum.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Enum) XXX_Merge(src proto.Message) { + xxx_messageInfo_Enum.Merge(m, src) +} +func (m *Enum) XXX_Size() int { + return m.Size() +} +func (m *Enum) XXX_DiscardUnknown() { + xxx_messageInfo_Enum.DiscardUnknown(m) +} + +var xxx_messageInfo_Enum proto.InternalMessageInfo + +func (m *Enum) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Enum) GetEnumvalue() []*EnumValue { + if m != nil { + return m.Enumvalue + } + return nil +} + +func (m *Enum) GetOptions() []*Option { + if m != nil { + return m.Options + } + return nil +} + +func (m *Enum) GetSourceContext() *SourceContext { + if m != nil { + return m.SourceContext + } + return nil +} + +func (m *Enum) GetSyntax() Syntax { + if m != nil { + return m.Syntax + } + return Syntax_SYNTAX_PROTO2 +} + +func (*Enum) XXX_MessageName() string { + return "google.protobuf.Enum" +} + +// Enum value definition. +type EnumValue struct { + // Enum value name. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Enum value number. + Number int32 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"` + // Protocol buffer options. + Options []*Option `protobuf:"bytes,3,rep,name=options,proto3" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EnumValue) Reset() { *m = EnumValue{} } +func (*EnumValue) ProtoMessage() {} +func (*EnumValue) Descriptor() ([]byte, []int) { + return fileDescriptor_dd271cc1e348c538, []int{3} +} +func (m *EnumValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EnumValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EnumValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EnumValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumValue.Merge(m, src) +} +func (m *EnumValue) XXX_Size() int { + return m.Size() +} +func (m *EnumValue) XXX_DiscardUnknown() { + xxx_messageInfo_EnumValue.DiscardUnknown(m) +} + +var xxx_messageInfo_EnumValue proto.InternalMessageInfo + +func (m *EnumValue) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *EnumValue) GetNumber() int32 { + if m != nil { + return m.Number + } + return 0 +} + +func (m *EnumValue) GetOptions() []*Option { + if m != nil { + return m.Options + } + return nil +} + +func (*EnumValue) XXX_MessageName() string { + return "google.protobuf.EnumValue" +} + +// A protocol buffer option, which can be attached to a message, field, +// enumeration, etc. +type Option struct { + // The option's name. For protobuf built-in options (options defined in + // descriptor.proto), this is the short name. For example, `"map_entry"`. + // For custom options, it should be the fully-qualified name. For example, + // `"google.api.http"`. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // The option's value packed in an Any message. If the value is a primitive, + // the corresponding wrapper type defined in google/protobuf/wrappers.proto + // should be used. If the value is an enum, it should be stored as an int32 + // value using the google.protobuf.Int32Value type. + Value *Any `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Option) Reset() { *m = Option{} } +func (*Option) ProtoMessage() {} +func (*Option) Descriptor() ([]byte, []int) { + return fileDescriptor_dd271cc1e348c538, []int{4} +} +func (m *Option) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Option) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Option.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Option) XXX_Merge(src proto.Message) { + xxx_messageInfo_Option.Merge(m, src) +} +func (m *Option) XXX_Size() int { + return m.Size() +} +func (m *Option) XXX_DiscardUnknown() { + xxx_messageInfo_Option.DiscardUnknown(m) +} + +var xxx_messageInfo_Option proto.InternalMessageInfo + +func (m *Option) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Option) GetValue() *Any { + if m != nil { + return m.Value + } + return nil +} + +func (*Option) XXX_MessageName() string { + return "google.protobuf.Option" +} +func init() { + proto.RegisterEnum("google.protobuf.Syntax", Syntax_name, Syntax_value) + proto.RegisterEnum("google.protobuf.Field_Kind", Field_Kind_name, Field_Kind_value) + proto.RegisterEnum("google.protobuf.Field_Cardinality", Field_Cardinality_name, Field_Cardinality_value) + proto.RegisterType((*Type)(nil), "google.protobuf.Type") + proto.RegisterType((*Field)(nil), "google.protobuf.Field") + proto.RegisterType((*Enum)(nil), "google.protobuf.Enum") + proto.RegisterType((*EnumValue)(nil), "google.protobuf.EnumValue") + proto.RegisterType((*Option)(nil), "google.protobuf.Option") +} + +func init() { proto.RegisterFile("google/protobuf/type.proto", fileDescriptor_dd271cc1e348c538) } + +var fileDescriptor_dd271cc1e348c538 = []byte{ + // 840 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xcf, 0x73, 0xda, 0x46, + 0x14, 0xf6, 0x0a, 0x21, 0xa3, 0x87, 0xc1, 0x9b, 0x4d, 0x26, 0x51, 0x9c, 0x19, 0x95, 0xa1, 0x3d, + 0x30, 0x39, 0xe0, 0x29, 0x78, 0x3c, 0xbd, 0x82, 0x91, 0x29, 0x63, 0x22, 0xa9, 0x8b, 0x68, 0xe2, + 0x5e, 0x18, 0x0c, 0x72, 0x86, 0x44, 0xac, 0x18, 0x24, 0x5a, 0x73, 0xeb, 0x4c, 0xcf, 0xfd, 0x27, + 0x7a, 0xea, 0xf4, 0xdc, 0x3f, 0xc2, 0xc7, 0x1e, 0x7b, 0xac, 0xc9, 0xa5, 0xc7, 0x1c, 0x73, 0x6b, + 0x67, 0x57, 0x20, 0x8b, 0x1f, 0x9d, 0x49, 0xdb, 0x1b, 0xef, 0xfb, 0xbe, 0xf7, 0x73, 0x9f, 0x1e, + 0x70, 0xf4, 0xda, 0xf7, 0x5f, 0x7b, 0xee, 0xf1, 0x64, 0xea, 0x87, 0xfe, 0xd5, 0xec, 0xfa, 0x38, + 0x9c, 0x4f, 0xdc, 0xb2, 0xb0, 0xc8, 0x61, 0xc4, 0x95, 0x57, 0xdc, 0xd1, 0xd3, 0x4d, 0x71, 0x9f, + 0xcd, 0x23, 0xf6, 0xe8, 0xb3, 0x4d, 0x2a, 0xf0, 0x67, 0xd3, 0x81, 0xdb, 0x1b, 0xf8, 0x2c, 0x74, + 0x6f, 0xc2, 0x48, 0x55, 0xfc, 0x51, 0x02, 0xd9, 0x99, 0x4f, 0x5c, 0x42, 0x40, 0x66, 0xfd, 0xb1, + 0xab, 0xa1, 0x02, 0x2a, 0xa9, 0x54, 0xfc, 0x26, 0x65, 0x50, 0xae, 0x47, 0xae, 0x37, 0x0c, 0x34, + 0xa9, 0x90, 0x2a, 0x65, 0x2b, 0x8f, 0xcb, 0x1b, 0xf9, 0xcb, 0xe7, 0x9c, 0xa6, 0x4b, 0x15, 0x79, + 0x0c, 0x8a, 0xcf, 0x5c, 0xff, 0x3a, 0xd0, 0x52, 0x85, 0x54, 0x49, 0xa5, 0x4b, 0x8b, 0x7c, 0x0e, + 0xfb, 0xfe, 0x24, 0x1c, 0xf9, 0x2c, 0xd0, 0x64, 0x11, 0xe8, 0xc9, 0x56, 0x20, 0x4b, 0xf0, 0x74, + 0xa5, 0x23, 0x06, 0xe4, 0xd7, 0xeb, 0xd5, 0xd2, 0x05, 0x54, 0xca, 0x56, 0xf4, 0x2d, 0xcf, 0x8e, + 0x90, 0x9d, 0x45, 0x2a, 0x9a, 0x0b, 0x92, 0x26, 0x39, 0x06, 0x25, 0x98, 0xb3, 0xb0, 0x7f, 0xa3, + 0x29, 0x05, 0x54, 0xca, 0xef, 0x48, 0xdc, 0x11, 0x34, 0x5d, 0xca, 0x8a, 0xbf, 0x2a, 0x90, 0x16, + 0x4d, 0x91, 0x63, 0x90, 0xdf, 0x8e, 0xd8, 0x50, 0x0c, 0x24, 0x5f, 0x79, 0xb6, 0xbb, 0xf5, 0xf2, + 0xc5, 0x88, 0x0d, 0xa9, 0x10, 0x92, 0x06, 0x64, 0x07, 0xfd, 0xe9, 0x70, 0xc4, 0xfa, 0xde, 0x28, + 0x9c, 0x6b, 0x92, 0xf0, 0x2b, 0xfe, 0x83, 0xdf, 0xd9, 0xbd, 0x92, 0x26, 0xdd, 0xf8, 0x0c, 0xd9, + 0x6c, 0x7c, 0xe5, 0x4e, 0xb5, 0x54, 0x01, 0x95, 0xd2, 0x74, 0x69, 0xc5, 0xef, 0x23, 0x27, 0xde, + 0xe7, 0x29, 0x64, 0xf8, 0x72, 0xf4, 0x66, 0x53, 0x4f, 0xf4, 0xa7, 0xd2, 0x7d, 0x6e, 0x77, 0xa7, + 0x1e, 0xf9, 0x04, 0xb2, 0x62, 0xf8, 0xbd, 0x11, 0x1b, 0xba, 0x37, 0xda, 0xbe, 0x88, 0x05, 0x02, + 0x6a, 0x71, 0x84, 0xe7, 0x99, 0xf4, 0x07, 0x6f, 0xdd, 0xa1, 0x96, 0x29, 0xa0, 0x52, 0x86, 0x2e, + 0xad, 0xe4, 0x5b, 0xa9, 0x1f, 0xf9, 0x56, 0xcf, 0x40, 0x7d, 0x13, 0xf8, 0xac, 0x27, 0xea, 0x03, + 0x51, 0x47, 0x86, 0x03, 0x26, 0xaf, 0xf1, 0x53, 0xc8, 0x0d, 0xdd, 0xeb, 0xfe, 0xcc, 0x0b, 0x7b, + 0xdf, 0xf6, 0xbd, 0x99, 0xab, 0x65, 0x85, 0xe0, 0x60, 0x09, 0x7e, 0xcd, 0xb1, 0xe2, 0xad, 0x04, + 0x32, 0x9f, 0x24, 0xc1, 0x70, 0xe0, 0x5c, 0xda, 0x46, 0xaf, 0x6b, 0x5e, 0x98, 0xd6, 0x4b, 0x13, + 0xef, 0x91, 0x43, 0xc8, 0x0a, 0xa4, 0x61, 0x75, 0xeb, 0x6d, 0x03, 0x23, 0x92, 0x07, 0x10, 0xc0, + 0x79, 0xdb, 0xaa, 0x39, 0x58, 0x8a, 0xed, 0x96, 0xe9, 0x9c, 0x9e, 0xe0, 0x54, 0xec, 0xd0, 0x8d, + 0x00, 0x39, 0x29, 0xa8, 0x56, 0x70, 0x3a, 0xce, 0x71, 0xde, 0x7a, 0x65, 0x34, 0x4e, 0x4f, 0xb0, + 0xb2, 0x8e, 0x54, 0x2b, 0x78, 0x9f, 0xe4, 0x40, 0x15, 0x48, 0xdd, 0xb2, 0xda, 0x38, 0x13, 0xc7, + 0xec, 0x38, 0xb4, 0x65, 0x36, 0xb1, 0x1a, 0xc7, 0x6c, 0x52, 0xab, 0x6b, 0x63, 0x88, 0x23, 0xbc, + 0x30, 0x3a, 0x9d, 0x5a, 0xd3, 0xc0, 0xd9, 0x58, 0x51, 0xbf, 0x74, 0x8c, 0x0e, 0x3e, 0x58, 0x2b, + 0xab, 0x5a, 0xc1, 0xb9, 0x38, 0x85, 0x61, 0x76, 0x5f, 0xe0, 0x3c, 0x79, 0x00, 0xb9, 0x28, 0xc5, + 0xaa, 0x88, 0xc3, 0x0d, 0xe8, 0xf4, 0x04, 0xe3, 0xfb, 0x42, 0xa2, 0x28, 0x0f, 0xd6, 0x80, 0xd3, + 0x13, 0x4c, 0x8a, 0x21, 0x64, 0x13, 0xbb, 0x45, 0x9e, 0xc0, 0xc3, 0xb3, 0x1a, 0x6d, 0xb4, 0xcc, + 0x5a, 0xbb, 0xe5, 0x5c, 0x26, 0xe6, 0xaa, 0xc1, 0xa3, 0x24, 0x61, 0xd9, 0x4e, 0xcb, 0x32, 0x6b, + 0x6d, 0x8c, 0x36, 0x19, 0x6a, 0x7c, 0xd5, 0x6d, 0x51, 0xa3, 0x81, 0xa5, 0x6d, 0xc6, 0x36, 0x6a, + 0x8e, 0xd1, 0xc0, 0xa9, 0xe2, 0x5f, 0x08, 0x64, 0x83, 0xcd, 0xc6, 0x3b, 0xcf, 0xc8, 0x17, 0xa0, + 0xba, 0x6c, 0x36, 0x8e, 0x9e, 0x3f, 0xba, 0x24, 0x47, 0x5b, 0x4b, 0xc5, 0xbd, 0xc5, 0x32, 0xd0, + 0x7b, 0x71, 0x72, 0x19, 0x53, 0xff, 0xf9, 0x70, 0xc8, 0xff, 0xef, 0x70, 0xa4, 0x3f, 0xee, 0x70, + 0xbc, 0x01, 0x35, 0x6e, 0x61, 0xe7, 0x14, 0xee, 0x3f, 0x6c, 0x69, 0xed, 0xc3, 0xfe, 0xf7, 0x3d, + 0x16, 0xbf, 0x04, 0x25, 0x82, 0x76, 0x26, 0x7a, 0x0e, 0xe9, 0xd5, 0xa8, 0x79, 0xe3, 0x8f, 0xb6, + 0xc2, 0xd5, 0xd8, 0x9c, 0x46, 0x92, 0xe7, 0x65, 0x50, 0xa2, 0x3e, 0xf8, 0xb2, 0x75, 0x2e, 0x4d, + 0xa7, 0xf6, 0xaa, 0x67, 0x53, 0xcb, 0xb1, 0x2a, 0x78, 0x6f, 0x13, 0xaa, 0x62, 0x54, 0xff, 0x01, + 0xfd, 0x7e, 0xa7, 0xef, 0xbd, 0xbf, 0xd3, 0xd1, 0x87, 0x3b, 0x1d, 0x7d, 0xbf, 0xd0, 0xd1, 0xcf, + 0x0b, 0x1d, 0xdd, 0x2e, 0x74, 0xf4, 0xdb, 0x42, 0x47, 0x7f, 0x2c, 0x74, 0xf4, 0xe7, 0x42, 0xdf, + 0x7b, 0xcf, 0xf1, 0x77, 0x3a, 0xba, 0x7d, 0xa7, 0x23, 0x78, 0x38, 0xf0, 0xc7, 0x9b, 0x25, 0xd4, + 0x55, 0xfe, 0x9f, 0x63, 0x73, 0xcb, 0x46, 0xdf, 0xa4, 0xf9, 0xd1, 0x0a, 0x3e, 0x20, 0xf4, 0x93, + 0x94, 0x6a, 0xda, 0xf5, 0x5f, 0x24, 0xbd, 0x19, 0xc9, 0xed, 0x55, 0xc5, 0x2f, 0x5d, 0xcf, 0xbb, + 0x60, 0xfe, 0x77, 0x8c, 0xbb, 0x05, 0x57, 0x8a, 0x88, 0x53, 0xfd, 0x3b, 0x00, 0x00, 0xff, 0xff, + 0xbc, 0x2a, 0x5e, 0x82, 0x2b, 0x07, 0x00, 0x00, +} + +func (this *Type) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Type) + if !ok { + that2, ok := that.(Type) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.Name != that1.Name { + if this.Name < that1.Name { + return -1 + } + return 1 + } + if len(this.Fields) != len(that1.Fields) { + if len(this.Fields) < len(that1.Fields) { + return -1 + } + return 1 + } + for i := range this.Fields { + if c := this.Fields[i].Compare(that1.Fields[i]); c != 0 { + return c + } + } + if len(this.Oneofs) != len(that1.Oneofs) { + if len(this.Oneofs) < len(that1.Oneofs) { + return -1 + } + return 1 + } + for i := range this.Oneofs { + if this.Oneofs[i] != that1.Oneofs[i] { + if this.Oneofs[i] < that1.Oneofs[i] { + return -1 + } + return 1 + } + } + if len(this.Options) != len(that1.Options) { + if len(this.Options) < len(that1.Options) { + return -1 + } + return 1 + } + for i := range this.Options { + if c := this.Options[i].Compare(that1.Options[i]); c != 0 { + return c + } + } + if c := this.SourceContext.Compare(that1.SourceContext); c != 0 { + return c + } + if this.Syntax != that1.Syntax { + if this.Syntax < that1.Syntax { + return -1 + } + return 1 + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *Field) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Field) + if !ok { + that2, ok := that.(Field) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.Kind != that1.Kind { + if this.Kind < that1.Kind { + return -1 + } + return 1 + } + if this.Cardinality != that1.Cardinality { + if this.Cardinality < that1.Cardinality { + return -1 + } + return 1 + } + if this.Number != that1.Number { + if this.Number < that1.Number { + return -1 + } + return 1 + } + if this.Name != that1.Name { + if this.Name < that1.Name { + return -1 + } + return 1 + } + if this.TypeUrl != that1.TypeUrl { + if this.TypeUrl < that1.TypeUrl { + return -1 + } + return 1 + } + if this.OneofIndex != that1.OneofIndex { + if this.OneofIndex < that1.OneofIndex { + return -1 + } + return 1 + } + if this.Packed != that1.Packed { + if !this.Packed { + return -1 + } + return 1 + } + if len(this.Options) != len(that1.Options) { + if len(this.Options) < len(that1.Options) { + return -1 + } + return 1 + } + for i := range this.Options { + if c := this.Options[i].Compare(that1.Options[i]); c != 0 { + return c + } + } + if this.JsonName != that1.JsonName { + if this.JsonName < that1.JsonName { + return -1 + } + return 1 + } + if this.DefaultValue != that1.DefaultValue { + if this.DefaultValue < that1.DefaultValue { + return -1 + } + return 1 + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *Enum) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Enum) + if !ok { + that2, ok := that.(Enum) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.Name != that1.Name { + if this.Name < that1.Name { + return -1 + } + return 1 + } + if len(this.Enumvalue) != len(that1.Enumvalue) { + if len(this.Enumvalue) < len(that1.Enumvalue) { + return -1 + } + return 1 + } + for i := range this.Enumvalue { + if c := this.Enumvalue[i].Compare(that1.Enumvalue[i]); c != 0 { + return c + } + } + if len(this.Options) != len(that1.Options) { + if len(this.Options) < len(that1.Options) { + return -1 + } + return 1 + } + for i := range this.Options { + if c := this.Options[i].Compare(that1.Options[i]); c != 0 { + return c + } + } + if c := this.SourceContext.Compare(that1.SourceContext); c != 0 { + return c + } + if this.Syntax != that1.Syntax { + if this.Syntax < that1.Syntax { + return -1 + } + return 1 + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *EnumValue) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*EnumValue) + if !ok { + that2, ok := that.(EnumValue) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.Name != that1.Name { + if this.Name < that1.Name { + return -1 + } + return 1 + } + if this.Number != that1.Number { + if this.Number < that1.Number { + return -1 + } + return 1 + } + if len(this.Options) != len(that1.Options) { + if len(this.Options) < len(that1.Options) { + return -1 + } + return 1 + } + for i := range this.Options { + if c := this.Options[i].Compare(that1.Options[i]); c != 0 { + return c + } + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (this *Option) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*Option) + if !ok { + that2, ok := that.(Option) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if this.Name != that1.Name { + if this.Name < that1.Name { + return -1 + } + return 1 + } + if c := this.Value.Compare(that1.Value); c != 0 { + return c + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} +func (x Syntax) String() string { + s, ok := Syntax_name[int32(x)] + if ok { + return s + } + return strconv.Itoa(int(x)) +} +func (x Field_Kind) String() string { + s, ok := Field_Kind_name[int32(x)] + if ok { + return s + } + return strconv.Itoa(int(x)) +} +func (x Field_Cardinality) String() string { + s, ok := Field_Cardinality_name[int32(x)] + if ok { + return s + } + return strconv.Itoa(int(x)) +} +func (this *Type) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Type) + if !ok { + that2, ok := that.(Type) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Name != that1.Name { + return false + } + if len(this.Fields) != len(that1.Fields) { + return false + } + for i := range this.Fields { + if !this.Fields[i].Equal(that1.Fields[i]) { + return false + } + } + if len(this.Oneofs) != len(that1.Oneofs) { + return false + } + for i := range this.Oneofs { + if this.Oneofs[i] != that1.Oneofs[i] { + return false + } + } + if len(this.Options) != len(that1.Options) { + return false + } + for i := range this.Options { + if !this.Options[i].Equal(that1.Options[i]) { + return false + } + } + if !this.SourceContext.Equal(that1.SourceContext) { + return false + } + if this.Syntax != that1.Syntax { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *Field) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Field) + if !ok { + that2, ok := that.(Field) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Kind != that1.Kind { + return false + } + if this.Cardinality != that1.Cardinality { + return false + } + if this.Number != that1.Number { + return false + } + if this.Name != that1.Name { + return false + } + if this.TypeUrl != that1.TypeUrl { + return false + } + if this.OneofIndex != that1.OneofIndex { + return false + } + if this.Packed != that1.Packed { + return false + } + if len(this.Options) != len(that1.Options) { + return false + } + for i := range this.Options { + if !this.Options[i].Equal(that1.Options[i]) { + return false + } + } + if this.JsonName != that1.JsonName { + return false + } + if this.DefaultValue != that1.DefaultValue { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *Enum) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Enum) + if !ok { + that2, ok := that.(Enum) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Name != that1.Name { + return false + } + if len(this.Enumvalue) != len(that1.Enumvalue) { + return false + } + for i := range this.Enumvalue { + if !this.Enumvalue[i].Equal(that1.Enumvalue[i]) { + return false + } + } + if len(this.Options) != len(that1.Options) { + return false + } + for i := range this.Options { + if !this.Options[i].Equal(that1.Options[i]) { + return false + } + } + if !this.SourceContext.Equal(that1.SourceContext) { + return false + } + if this.Syntax != that1.Syntax { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *EnumValue) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*EnumValue) + if !ok { + that2, ok := that.(EnumValue) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Name != that1.Name { + return false + } + if this.Number != that1.Number { + return false + } + if len(this.Options) != len(that1.Options) { + return false + } + for i := range this.Options { + if !this.Options[i].Equal(that1.Options[i]) { + return false + } + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *Option) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Option) + if !ok { + that2, ok := that.(Option) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Name != that1.Name { + return false + } + if !this.Value.Equal(that1.Value) { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} +func (this *Type) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 10) + s = append(s, "&types.Type{") + s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n") + if this.Fields != nil { + s = append(s, "Fields: "+fmt.Sprintf("%#v", this.Fields)+",\n") + } + s = append(s, "Oneofs: "+fmt.Sprintf("%#v", this.Oneofs)+",\n") + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.SourceContext != nil { + s = append(s, "SourceContext: "+fmt.Sprintf("%#v", this.SourceContext)+",\n") + } + s = append(s, "Syntax: "+fmt.Sprintf("%#v", this.Syntax)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *Field) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 14) + s = append(s, "&types.Field{") + s = append(s, "Kind: "+fmt.Sprintf("%#v", this.Kind)+",\n") + s = append(s, "Cardinality: "+fmt.Sprintf("%#v", this.Cardinality)+",\n") + s = append(s, "Number: "+fmt.Sprintf("%#v", this.Number)+",\n") + s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n") + s = append(s, "TypeUrl: "+fmt.Sprintf("%#v", this.TypeUrl)+",\n") + s = append(s, "OneofIndex: "+fmt.Sprintf("%#v", this.OneofIndex)+",\n") + s = append(s, "Packed: "+fmt.Sprintf("%#v", this.Packed)+",\n") + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + s = append(s, "JsonName: "+fmt.Sprintf("%#v", this.JsonName)+",\n") + s = append(s, "DefaultValue: "+fmt.Sprintf("%#v", this.DefaultValue)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *Enum) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 9) + s = append(s, "&types.Enum{") + s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n") + if this.Enumvalue != nil { + s = append(s, "Enumvalue: "+fmt.Sprintf("%#v", this.Enumvalue)+",\n") + } + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.SourceContext != nil { + s = append(s, "SourceContext: "+fmt.Sprintf("%#v", this.SourceContext)+",\n") + } + s = append(s, "Syntax: "+fmt.Sprintf("%#v", this.Syntax)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *EnumValue) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 7) + s = append(s, "&types.EnumValue{") + s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n") + s = append(s, "Number: "+fmt.Sprintf("%#v", this.Number)+",\n") + if this.Options != nil { + s = append(s, "Options: "+fmt.Sprintf("%#v", this.Options)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func (this *Option) GoString() string { + if this == nil { + return "nil" + } + s := make([]string, 0, 6) + s = append(s, "&types.Option{") + s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n") + if this.Value != nil { + s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + } + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } + s = append(s, "}") + return strings.Join(s, "") +} +func valueToGoStringType(v interface{}, typ string) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv) +} +func (m *Type) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Type) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Type) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Syntax != 0 { + i = encodeVarintType(dAtA, i, uint64(m.Syntax)) + i-- + dAtA[i] = 0x30 + } + if m.SourceContext != nil { + { + size, err := m.SourceContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintType(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.Options) > 0 { + for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Options[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintType(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.Oneofs) > 0 { + for iNdEx := len(m.Oneofs) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Oneofs[iNdEx]) + copy(dAtA[i:], m.Oneofs[iNdEx]) + i = encodeVarintType(dAtA, i, uint64(len(m.Oneofs[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Fields) > 0 { + for iNdEx := len(m.Fields) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Fields[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintType(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintType(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Field) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Field) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Field) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.DefaultValue) > 0 { + i -= len(m.DefaultValue) + copy(dAtA[i:], m.DefaultValue) + i = encodeVarintType(dAtA, i, uint64(len(m.DefaultValue))) + i-- + dAtA[i] = 0x5a + } + if len(m.JsonName) > 0 { + i -= len(m.JsonName) + copy(dAtA[i:], m.JsonName) + i = encodeVarintType(dAtA, i, uint64(len(m.JsonName))) + i-- + dAtA[i] = 0x52 + } + if len(m.Options) > 0 { + for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Options[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintType(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } + if m.Packed { + i-- + if m.Packed { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } + if m.OneofIndex != 0 { + i = encodeVarintType(dAtA, i, uint64(m.OneofIndex)) + i-- + dAtA[i] = 0x38 + } + if len(m.TypeUrl) > 0 { + i -= len(m.TypeUrl) + copy(dAtA[i:], m.TypeUrl) + i = encodeVarintType(dAtA, i, uint64(len(m.TypeUrl))) + i-- + dAtA[i] = 0x32 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintType(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x22 + } + if m.Number != 0 { + i = encodeVarintType(dAtA, i, uint64(m.Number)) + i-- + dAtA[i] = 0x18 + } + if m.Cardinality != 0 { + i = encodeVarintType(dAtA, i, uint64(m.Cardinality)) + i-- + dAtA[i] = 0x10 + } + if m.Kind != 0 { + i = encodeVarintType(dAtA, i, uint64(m.Kind)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Enum) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Enum) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Enum) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Syntax != 0 { + i = encodeVarintType(dAtA, i, uint64(m.Syntax)) + i-- + dAtA[i] = 0x28 + } + if m.SourceContext != nil { + { + size, err := m.SourceContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintType(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.Options) > 0 { + for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Options[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintType(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Enumvalue) > 0 { + for iNdEx := len(m.Enumvalue) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Enumvalue[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintType(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintType(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EnumValue) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EnumValue) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EnumValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Options) > 0 { + for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Options[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintType(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if m.Number != 0 { + i = encodeVarintType(dAtA, i, uint64(m.Number)) + i-- + dAtA[i] = 0x10 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintType(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Option) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Option) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Option) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.Value != nil { + { + size, err := m.Value.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintType(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintType(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintType(dAtA []byte, offset int, v uint64) int { + offset -= sovType(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func NewPopulatedType(r randyType, easy bool) *Type { + this := &Type{} + this.Name = string(randStringType(r)) + if r.Intn(5) != 0 { + v1 := r.Intn(5) + this.Fields = make([]*Field, v1) + for i := 0; i < v1; i++ { + this.Fields[i] = NewPopulatedField(r, easy) + } + } + v2 := r.Intn(10) + this.Oneofs = make([]string, v2) + for i := 0; i < v2; i++ { + this.Oneofs[i] = string(randStringType(r)) + } + if r.Intn(5) != 0 { + v3 := r.Intn(5) + this.Options = make([]*Option, v3) + for i := 0; i < v3; i++ { + this.Options[i] = NewPopulatedOption(r, easy) + } + } + if r.Intn(5) != 0 { + this.SourceContext = NewPopulatedSourceContext(r, easy) + } + this.Syntax = Syntax([]int32{0, 1}[r.Intn(2)]) + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedType(r, 7) + } + return this +} + +func NewPopulatedField(r randyType, easy bool) *Field { + this := &Field{} + this.Kind = Field_Kind([]int32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}[r.Intn(19)]) + this.Cardinality = Field_Cardinality([]int32{0, 1, 2, 3}[r.Intn(4)]) + this.Number = int32(r.Int31()) + if r.Intn(2) == 0 { + this.Number *= -1 + } + this.Name = string(randStringType(r)) + this.TypeUrl = string(randStringType(r)) + this.OneofIndex = int32(r.Int31()) + if r.Intn(2) == 0 { + this.OneofIndex *= -1 + } + this.Packed = bool(bool(r.Intn(2) == 0)) + if r.Intn(5) != 0 { + v4 := r.Intn(5) + this.Options = make([]*Option, v4) + for i := 0; i < v4; i++ { + this.Options[i] = NewPopulatedOption(r, easy) + } + } + this.JsonName = string(randStringType(r)) + this.DefaultValue = string(randStringType(r)) + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedType(r, 12) + } + return this +} + +func NewPopulatedEnum(r randyType, easy bool) *Enum { + this := &Enum{} + this.Name = string(randStringType(r)) + if r.Intn(5) != 0 { + v5 := r.Intn(5) + this.Enumvalue = make([]*EnumValue, v5) + for i := 0; i < v5; i++ { + this.Enumvalue[i] = NewPopulatedEnumValue(r, easy) + } + } + if r.Intn(5) != 0 { + v6 := r.Intn(5) + this.Options = make([]*Option, v6) + for i := 0; i < v6; i++ { + this.Options[i] = NewPopulatedOption(r, easy) + } + } + if r.Intn(5) != 0 { + this.SourceContext = NewPopulatedSourceContext(r, easy) + } + this.Syntax = Syntax([]int32{0, 1}[r.Intn(2)]) + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedType(r, 6) + } + return this +} + +func NewPopulatedEnumValue(r randyType, easy bool) *EnumValue { + this := &EnumValue{} + this.Name = string(randStringType(r)) + this.Number = int32(r.Int31()) + if r.Intn(2) == 0 { + this.Number *= -1 + } + if r.Intn(5) != 0 { + v7 := r.Intn(5) + this.Options = make([]*Option, v7) + for i := 0; i < v7; i++ { + this.Options[i] = NewPopulatedOption(r, easy) + } + } + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedType(r, 4) + } + return this +} + +func NewPopulatedOption(r randyType, easy bool) *Option { + this := &Option{} + this.Name = string(randStringType(r)) + if r.Intn(5) != 0 { + this.Value = NewPopulatedAny(r, easy) + } + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedType(r, 3) + } + return this +} + +type randyType interface { + Float32() float32 + Float64() float64 + Int63() int64 + Int31() int32 + Uint32() uint32 + Intn(n int) int +} + +func randUTF8RuneType(r randyType) rune { + ru := r.Intn(62) + if ru < 10 { + return rune(ru + 48) + } else if ru < 36 { + return rune(ru + 55) + } + return rune(ru + 61) +} +func randStringType(r randyType) string { + v8 := r.Intn(100) + tmps := make([]rune, v8) + for i := 0; i < v8; i++ { + tmps[i] = randUTF8RuneType(r) + } + return string(tmps) +} +func randUnrecognizedType(r randyType, maxFieldNumber int) (dAtA []byte) { + l := r.Intn(5) + for i := 0; i < l; i++ { + wire := r.Intn(4) + if wire == 3 { + wire = 5 + } + fieldNumber := maxFieldNumber + r.Intn(100) + dAtA = randFieldType(dAtA, r, fieldNumber, wire) + } + return dAtA +} +func randFieldType(dAtA []byte, r randyType, fieldNumber int, wire int) []byte { + key := uint32(fieldNumber)<<3 | uint32(wire) + switch wire { + case 0: + dAtA = encodeVarintPopulateType(dAtA, uint64(key)) + v9 := r.Int63() + if r.Intn(2) == 0 { + v9 *= -1 + } + dAtA = encodeVarintPopulateType(dAtA, uint64(v9)) + case 1: + dAtA = encodeVarintPopulateType(dAtA, uint64(key)) + dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + case 2: + dAtA = encodeVarintPopulateType(dAtA, uint64(key)) + ll := r.Intn(100) + dAtA = encodeVarintPopulateType(dAtA, uint64(ll)) + for j := 0; j < ll; j++ { + dAtA = append(dAtA, byte(r.Intn(256))) + } + default: + dAtA = encodeVarintPopulateType(dAtA, uint64(key)) + dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) + } + return dAtA +} +func encodeVarintPopulateType(dAtA []byte, v uint64) []byte { + for v >= 1<<7 { + dAtA = append(dAtA, uint8(uint64(v)&0x7f|0x80)) + v >>= 7 + } + dAtA = append(dAtA, uint8(v)) + return dAtA +} +func (m *Type) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovType(uint64(l)) + } + if len(m.Fields) > 0 { + for _, e := range m.Fields { + l = e.Size() + n += 1 + l + sovType(uint64(l)) + } + } + if len(m.Oneofs) > 0 { + for _, s := range m.Oneofs { + l = len(s) + n += 1 + l + sovType(uint64(l)) + } + } + if len(m.Options) > 0 { + for _, e := range m.Options { + l = e.Size() + n += 1 + l + sovType(uint64(l)) + } + } + if m.SourceContext != nil { + l = m.SourceContext.Size() + n += 1 + l + sovType(uint64(l)) + } + if m.Syntax != 0 { + n += 1 + sovType(uint64(m.Syntax)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Field) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Kind != 0 { + n += 1 + sovType(uint64(m.Kind)) + } + if m.Cardinality != 0 { + n += 1 + sovType(uint64(m.Cardinality)) + } + if m.Number != 0 { + n += 1 + sovType(uint64(m.Number)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovType(uint64(l)) + } + l = len(m.TypeUrl) + if l > 0 { + n += 1 + l + sovType(uint64(l)) + } + if m.OneofIndex != 0 { + n += 1 + sovType(uint64(m.OneofIndex)) + } + if m.Packed { + n += 2 + } + if len(m.Options) > 0 { + for _, e := range m.Options { + l = e.Size() + n += 1 + l + sovType(uint64(l)) + } + } + l = len(m.JsonName) + if l > 0 { + n += 1 + l + sovType(uint64(l)) + } + l = len(m.DefaultValue) + if l > 0 { + n += 1 + l + sovType(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Enum) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovType(uint64(l)) + } + if len(m.Enumvalue) > 0 { + for _, e := range m.Enumvalue { + l = e.Size() + n += 1 + l + sovType(uint64(l)) + } + } + if len(m.Options) > 0 { + for _, e := range m.Options { + l = e.Size() + n += 1 + l + sovType(uint64(l)) + } + } + if m.SourceContext != nil { + l = m.SourceContext.Size() + n += 1 + l + sovType(uint64(l)) + } + if m.Syntax != 0 { + n += 1 + sovType(uint64(m.Syntax)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *EnumValue) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovType(uint64(l)) + } + if m.Number != 0 { + n += 1 + sovType(uint64(m.Number)) + } + if len(m.Options) > 0 { + for _, e := range m.Options { + l = e.Size() + n += 1 + l + sovType(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Option) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovType(uint64(l)) + } + if m.Value != nil { + l = m.Value.Size() + n += 1 + l + sovType(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovType(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozType(x uint64) (n int) { + return sovType(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Type) String() string { + if this == nil { + return "nil" + } + repeatedStringForFields := "[]*Field{" + for _, f := range this.Fields { + repeatedStringForFields += strings.Replace(f.String(), "Field", "Field", 1) + "," + } + repeatedStringForFields += "}" + repeatedStringForOptions := "[]*Option{" + for _, f := range this.Options { + repeatedStringForOptions += strings.Replace(f.String(), "Option", "Option", 1) + "," + } + repeatedStringForOptions += "}" + s := strings.Join([]string{`&Type{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Fields:` + repeatedStringForFields + `,`, + `Oneofs:` + fmt.Sprintf("%v", this.Oneofs) + `,`, + `Options:` + repeatedStringForOptions + `,`, + `SourceContext:` + strings.Replace(fmt.Sprintf("%v", this.SourceContext), "SourceContext", "SourceContext", 1) + `,`, + `Syntax:` + fmt.Sprintf("%v", this.Syntax) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *Field) String() string { + if this == nil { + return "nil" + } + repeatedStringForOptions := "[]*Option{" + for _, f := range this.Options { + repeatedStringForOptions += strings.Replace(f.String(), "Option", "Option", 1) + "," + } + repeatedStringForOptions += "}" + s := strings.Join([]string{`&Field{`, + `Kind:` + fmt.Sprintf("%v", this.Kind) + `,`, + `Cardinality:` + fmt.Sprintf("%v", this.Cardinality) + `,`, + `Number:` + fmt.Sprintf("%v", this.Number) + `,`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `TypeUrl:` + fmt.Sprintf("%v", this.TypeUrl) + `,`, + `OneofIndex:` + fmt.Sprintf("%v", this.OneofIndex) + `,`, + `Packed:` + fmt.Sprintf("%v", this.Packed) + `,`, + `Options:` + repeatedStringForOptions + `,`, + `JsonName:` + fmt.Sprintf("%v", this.JsonName) + `,`, + `DefaultValue:` + fmt.Sprintf("%v", this.DefaultValue) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *Enum) String() string { + if this == nil { + return "nil" + } + repeatedStringForEnumvalue := "[]*EnumValue{" + for _, f := range this.Enumvalue { + repeatedStringForEnumvalue += strings.Replace(f.String(), "EnumValue", "EnumValue", 1) + "," + } + repeatedStringForEnumvalue += "}" + repeatedStringForOptions := "[]*Option{" + for _, f := range this.Options { + repeatedStringForOptions += strings.Replace(f.String(), "Option", "Option", 1) + "," + } + repeatedStringForOptions += "}" + s := strings.Join([]string{`&Enum{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Enumvalue:` + repeatedStringForEnumvalue + `,`, + `Options:` + repeatedStringForOptions + `,`, + `SourceContext:` + strings.Replace(fmt.Sprintf("%v", this.SourceContext), "SourceContext", "SourceContext", 1) + `,`, + `Syntax:` + fmt.Sprintf("%v", this.Syntax) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *EnumValue) String() string { + if this == nil { + return "nil" + } + repeatedStringForOptions := "[]*Option{" + for _, f := range this.Options { + repeatedStringForOptions += strings.Replace(f.String(), "Option", "Option", 1) + "," + } + repeatedStringForOptions += "}" + s := strings.Join([]string{`&EnumValue{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Number:` + fmt.Sprintf("%v", this.Number) + `,`, + `Options:` + repeatedStringForOptions + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func (this *Option) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Option{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Value:` + strings.Replace(fmt.Sprintf("%v", this.Value), "Any", "Any", 1) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, + `}`, + }, "") + return s +} +func valueToStringType(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Type) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Type: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Type: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Fields", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Fields = append(m.Fields, &Field{}) + if err := m.Fields[len(m.Fields)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Oneofs", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Oneofs = append(m.Oneofs, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Options = append(m.Options, &Option{}) + if err := m.Options[len(m.Options)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SourceContext == nil { + m.SourceContext = &SourceContext{} + } + if err := m.SourceContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Syntax", wireType) + } + m.Syntax = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Syntax |= Syntax(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipType(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthType + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Field) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Field: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Field: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) + } + m.Kind = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Kind |= Field_Kind(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Cardinality", wireType) + } + m.Cardinality = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Cardinality |= Field_Cardinality(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Number", wireType) + } + m.Number = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Number |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeUrl = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field OneofIndex", wireType) + } + m.OneofIndex = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.OneofIndex |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Packed", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Packed = bool(v != 0) + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Options = append(m.Options, &Option{}) + if err := m.Options[len(m.Options)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field JsonName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.JsonName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DefaultValue", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DefaultValue = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipType(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthType + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Enum) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Enum: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Enum: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Enumvalue", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Enumvalue = append(m.Enumvalue, &EnumValue{}) + if err := m.Enumvalue[len(m.Enumvalue)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Options = append(m.Options, &Option{}) + if err := m.Options[len(m.Options)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SourceContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SourceContext == nil { + m.SourceContext = &SourceContext{} + } + if err := m.SourceContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Syntax", wireType) + } + m.Syntax = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Syntax |= Syntax(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipType(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthType + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EnumValue) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EnumValue: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EnumValue: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Number", wireType) + } + m.Number = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Number |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Options = append(m.Options, &Option{}) + if err := m.Options[len(m.Options)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipType(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthType + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Option) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Option: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Option: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowType + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthType + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthType + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Value == nil { + m.Value = &Any{} + } + if err := m.Value.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipType(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthType + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipType(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowType + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowType + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowType + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthType + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupType + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthType + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthType = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowType = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupType = fmt.Errorf("proto: unexpected end of group") +) diff --git a/vendor/github.com/gogo/protobuf/types/wrappers.pb.go b/vendor/github.com/gogo/protobuf/types/wrappers.pb.go index 18b384ea35256..8d415420a74d8 100644 --- a/vendor/github.com/gogo/protobuf/types/wrappers.pb.go +++ b/vendor/github.com/gogo/protobuf/types/wrappers.pb.go @@ -1,37 +1,19 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: wrappers.proto - -/* -Package types is a generated protocol buffer package. - -It is generated from these files: - wrappers.proto - -It has these top-level messages: - DoubleValue - FloatValue - Int64Value - UInt64Value - Int32Value - UInt32Value - BoolValue - StringValue - BytesValue -*/ -package types - -import proto "github.com/gogo/protobuf/proto" -import fmt "fmt" -import math "math" - -import bytes "bytes" - -import strings "strings" -import reflect "reflect" +// source: google/protobuf/wrappers.proto -import binary "encoding/binary" +package types -import io "io" +import ( + bytes "bytes" + encoding_binary "encoding/binary" + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -42,20 +24,51 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // Wrapper message for `double`. // // The JSON representation for `DoubleValue` is JSON number. type DoubleValue struct { // The double value. - Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` + Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DoubleValue) Reset() { *m = DoubleValue{} } +func (*DoubleValue) ProtoMessage() {} +func (*DoubleValue) Descriptor() ([]byte, []int) { + return fileDescriptor_5377b62bda767935, []int{0} +} +func (*DoubleValue) XXX_WellKnownType() string { return "DoubleValue" } +func (m *DoubleValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DoubleValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DoubleValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DoubleValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_DoubleValue.Merge(m, src) +} +func (m *DoubleValue) XXX_Size() int { + return m.Size() +} +func (m *DoubleValue) XXX_DiscardUnknown() { + xxx_messageInfo_DoubleValue.DiscardUnknown(m) } -func (m *DoubleValue) Reset() { *m = DoubleValue{} } -func (*DoubleValue) ProtoMessage() {} -func (*DoubleValue) Descriptor() ([]byte, []int) { return fileDescriptorWrappers, []int{0} } -func (*DoubleValue) XXX_WellKnownType() string { return "DoubleValue" } +var xxx_messageInfo_DoubleValue proto.InternalMessageInfo func (m *DoubleValue) GetValue() float64 { if m != nil { @@ -64,18 +77,53 @@ func (m *DoubleValue) GetValue() float64 { return 0 } +func (*DoubleValue) XXX_MessageName() string { + return "google.protobuf.DoubleValue" +} + // Wrapper message for `float`. // // The JSON representation for `FloatValue` is JSON number. type FloatValue struct { // The float value. - Value float32 `protobuf:"fixed32,1,opt,name=value,proto3" json:"value,omitempty"` + Value float32 `protobuf:"fixed32,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *FloatValue) Reset() { *m = FloatValue{} } +func (*FloatValue) ProtoMessage() {} +func (*FloatValue) Descriptor() ([]byte, []int) { + return fileDescriptor_5377b62bda767935, []int{1} +} +func (*FloatValue) XXX_WellKnownType() string { return "FloatValue" } +func (m *FloatValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FloatValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FloatValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *FloatValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_FloatValue.Merge(m, src) +} +func (m *FloatValue) XXX_Size() int { + return m.Size() +} +func (m *FloatValue) XXX_DiscardUnknown() { + xxx_messageInfo_FloatValue.DiscardUnknown(m) } -func (m *FloatValue) Reset() { *m = FloatValue{} } -func (*FloatValue) ProtoMessage() {} -func (*FloatValue) Descriptor() ([]byte, []int) { return fileDescriptorWrappers, []int{1} } -func (*FloatValue) XXX_WellKnownType() string { return "FloatValue" } +var xxx_messageInfo_FloatValue proto.InternalMessageInfo func (m *FloatValue) GetValue() float32 { if m != nil { @@ -84,18 +132,53 @@ func (m *FloatValue) GetValue() float32 { return 0 } +func (*FloatValue) XXX_MessageName() string { + return "google.protobuf.FloatValue" +} + // Wrapper message for `int64`. // // The JSON representation for `Int64Value` is JSON string. type Int64Value struct { // The int64 value. - Value int64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + Value int64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Int64Value) Reset() { *m = Int64Value{} } +func (*Int64Value) ProtoMessage() {} +func (*Int64Value) Descriptor() ([]byte, []int) { + return fileDescriptor_5377b62bda767935, []int{2} +} +func (*Int64Value) XXX_WellKnownType() string { return "Int64Value" } +func (m *Int64Value) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Int64Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Int64Value.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Int64Value) XXX_Merge(src proto.Message) { + xxx_messageInfo_Int64Value.Merge(m, src) +} +func (m *Int64Value) XXX_Size() int { + return m.Size() +} +func (m *Int64Value) XXX_DiscardUnknown() { + xxx_messageInfo_Int64Value.DiscardUnknown(m) } -func (m *Int64Value) Reset() { *m = Int64Value{} } -func (*Int64Value) ProtoMessage() {} -func (*Int64Value) Descriptor() ([]byte, []int) { return fileDescriptorWrappers, []int{2} } -func (*Int64Value) XXX_WellKnownType() string { return "Int64Value" } +var xxx_messageInfo_Int64Value proto.InternalMessageInfo func (m *Int64Value) GetValue() int64 { if m != nil { @@ -104,18 +187,53 @@ func (m *Int64Value) GetValue() int64 { return 0 } +func (*Int64Value) XXX_MessageName() string { + return "google.protobuf.Int64Value" +} + // Wrapper message for `uint64`. // // The JSON representation for `UInt64Value` is JSON string. type UInt64Value struct { // The uint64 value. - Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UInt64Value) Reset() { *m = UInt64Value{} } +func (*UInt64Value) ProtoMessage() {} +func (*UInt64Value) Descriptor() ([]byte, []int) { + return fileDescriptor_5377b62bda767935, []int{3} +} +func (*UInt64Value) XXX_WellKnownType() string { return "UInt64Value" } +func (m *UInt64Value) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UInt64Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UInt64Value.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UInt64Value) XXX_Merge(src proto.Message) { + xxx_messageInfo_UInt64Value.Merge(m, src) +} +func (m *UInt64Value) XXX_Size() int { + return m.Size() +} +func (m *UInt64Value) XXX_DiscardUnknown() { + xxx_messageInfo_UInt64Value.DiscardUnknown(m) } -func (m *UInt64Value) Reset() { *m = UInt64Value{} } -func (*UInt64Value) ProtoMessage() {} -func (*UInt64Value) Descriptor() ([]byte, []int) { return fileDescriptorWrappers, []int{3} } -func (*UInt64Value) XXX_WellKnownType() string { return "UInt64Value" } +var xxx_messageInfo_UInt64Value proto.InternalMessageInfo func (m *UInt64Value) GetValue() uint64 { if m != nil { @@ -124,18 +242,53 @@ func (m *UInt64Value) GetValue() uint64 { return 0 } +func (*UInt64Value) XXX_MessageName() string { + return "google.protobuf.UInt64Value" +} + // Wrapper message for `int32`. // // The JSON representation for `Int32Value` is JSON number. type Int32Value struct { // The int32 value. - Value int32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + Value int32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Int32Value) Reset() { *m = Int32Value{} } +func (*Int32Value) ProtoMessage() {} +func (*Int32Value) Descriptor() ([]byte, []int) { + return fileDescriptor_5377b62bda767935, []int{4} +} +func (*Int32Value) XXX_WellKnownType() string { return "Int32Value" } +func (m *Int32Value) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Int32Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Int32Value.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Int32Value) XXX_Merge(src proto.Message) { + xxx_messageInfo_Int32Value.Merge(m, src) +} +func (m *Int32Value) XXX_Size() int { + return m.Size() +} +func (m *Int32Value) XXX_DiscardUnknown() { + xxx_messageInfo_Int32Value.DiscardUnknown(m) } -func (m *Int32Value) Reset() { *m = Int32Value{} } -func (*Int32Value) ProtoMessage() {} -func (*Int32Value) Descriptor() ([]byte, []int) { return fileDescriptorWrappers, []int{4} } -func (*Int32Value) XXX_WellKnownType() string { return "Int32Value" } +var xxx_messageInfo_Int32Value proto.InternalMessageInfo func (m *Int32Value) GetValue() int32 { if m != nil { @@ -144,18 +297,53 @@ func (m *Int32Value) GetValue() int32 { return 0 } +func (*Int32Value) XXX_MessageName() string { + return "google.protobuf.Int32Value" +} + // Wrapper message for `uint32`. // // The JSON representation for `UInt32Value` is JSON number. type UInt32Value struct { // The uint32 value. - Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UInt32Value) Reset() { *m = UInt32Value{} } +func (*UInt32Value) ProtoMessage() {} +func (*UInt32Value) Descriptor() ([]byte, []int) { + return fileDescriptor_5377b62bda767935, []int{5} +} +func (*UInt32Value) XXX_WellKnownType() string { return "UInt32Value" } +func (m *UInt32Value) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UInt32Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UInt32Value.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UInt32Value) XXX_Merge(src proto.Message) { + xxx_messageInfo_UInt32Value.Merge(m, src) +} +func (m *UInt32Value) XXX_Size() int { + return m.Size() +} +func (m *UInt32Value) XXX_DiscardUnknown() { + xxx_messageInfo_UInt32Value.DiscardUnknown(m) } -func (m *UInt32Value) Reset() { *m = UInt32Value{} } -func (*UInt32Value) ProtoMessage() {} -func (*UInt32Value) Descriptor() ([]byte, []int) { return fileDescriptorWrappers, []int{5} } -func (*UInt32Value) XXX_WellKnownType() string { return "UInt32Value" } +var xxx_messageInfo_UInt32Value proto.InternalMessageInfo func (m *UInt32Value) GetValue() uint32 { if m != nil { @@ -164,18 +352,53 @@ func (m *UInt32Value) GetValue() uint32 { return 0 } +func (*UInt32Value) XXX_MessageName() string { + return "google.protobuf.UInt32Value" +} + // Wrapper message for `bool`. // // The JSON representation for `BoolValue` is JSON `true` and `false`. type BoolValue struct { // The bool value. - Value bool `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + Value bool `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BoolValue) Reset() { *m = BoolValue{} } +func (*BoolValue) ProtoMessage() {} +func (*BoolValue) Descriptor() ([]byte, []int) { + return fileDescriptor_5377b62bda767935, []int{6} +} +func (*BoolValue) XXX_WellKnownType() string { return "BoolValue" } +func (m *BoolValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BoolValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BoolValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BoolValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_BoolValue.Merge(m, src) +} +func (m *BoolValue) XXX_Size() int { + return m.Size() +} +func (m *BoolValue) XXX_DiscardUnknown() { + xxx_messageInfo_BoolValue.DiscardUnknown(m) } -func (m *BoolValue) Reset() { *m = BoolValue{} } -func (*BoolValue) ProtoMessage() {} -func (*BoolValue) Descriptor() ([]byte, []int) { return fileDescriptorWrappers, []int{6} } -func (*BoolValue) XXX_WellKnownType() string { return "BoolValue" } +var xxx_messageInfo_BoolValue proto.InternalMessageInfo func (m *BoolValue) GetValue() bool { if m != nil { @@ -184,18 +407,53 @@ func (m *BoolValue) GetValue() bool { return false } +func (*BoolValue) XXX_MessageName() string { + return "google.protobuf.BoolValue" +} + // Wrapper message for `string`. // // The JSON representation for `StringValue` is JSON string. type StringValue struct { // The string value. - Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *StringValue) Reset() { *m = StringValue{} } +func (*StringValue) ProtoMessage() {} +func (*StringValue) Descriptor() ([]byte, []int) { + return fileDescriptor_5377b62bda767935, []int{7} +} +func (*StringValue) XXX_WellKnownType() string { return "StringValue" } +func (m *StringValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *StringValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_StringValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *StringValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_StringValue.Merge(m, src) +} +func (m *StringValue) XXX_Size() int { + return m.Size() +} +func (m *StringValue) XXX_DiscardUnknown() { + xxx_messageInfo_StringValue.DiscardUnknown(m) } -func (m *StringValue) Reset() { *m = StringValue{} } -func (*StringValue) ProtoMessage() {} -func (*StringValue) Descriptor() ([]byte, []int) { return fileDescriptorWrappers, []int{7} } -func (*StringValue) XXX_WellKnownType() string { return "StringValue" } +var xxx_messageInfo_StringValue proto.InternalMessageInfo func (m *StringValue) GetValue() string { if m != nil { @@ -204,18 +462,53 @@ func (m *StringValue) GetValue() string { return "" } +func (*StringValue) XXX_MessageName() string { + return "google.protobuf.StringValue" +} + // Wrapper message for `bytes`. // // The JSON representation for `BytesValue` is JSON string. type BytesValue struct { // The bytes value. - Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *BytesValue) Reset() { *m = BytesValue{} } +func (*BytesValue) ProtoMessage() {} +func (*BytesValue) Descriptor() ([]byte, []int) { + return fileDescriptor_5377b62bda767935, []int{8} +} +func (*BytesValue) XXX_WellKnownType() string { return "BytesValue" } +func (m *BytesValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *BytesValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_BytesValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *BytesValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_BytesValue.Merge(m, src) +} +func (m *BytesValue) XXX_Size() int { + return m.Size() +} +func (m *BytesValue) XXX_DiscardUnknown() { + xxx_messageInfo_BytesValue.DiscardUnknown(m) } -func (m *BytesValue) Reset() { *m = BytesValue{} } -func (*BytesValue) ProtoMessage() {} -func (*BytesValue) Descriptor() ([]byte, []int) { return fileDescriptorWrappers, []int{8} } -func (*BytesValue) XXX_WellKnownType() string { return "BytesValue" } +var xxx_messageInfo_BytesValue proto.InternalMessageInfo func (m *BytesValue) GetValue() []byte { if m != nil { @@ -224,6 +517,9 @@ func (m *BytesValue) GetValue() []byte { return nil } +func (*BytesValue) XXX_MessageName() string { + return "google.protobuf.BytesValue" +} func init() { proto.RegisterType((*DoubleValue)(nil), "google.protobuf.DoubleValue") proto.RegisterType((*FloatValue)(nil), "google.protobuf.FloatValue") @@ -235,6 +531,31 @@ func init() { proto.RegisterType((*StringValue)(nil), "google.protobuf.StringValue") proto.RegisterType((*BytesValue)(nil), "google.protobuf.BytesValue") } + +func init() { proto.RegisterFile("google/protobuf/wrappers.proto", fileDescriptor_5377b62bda767935) } + +var fileDescriptor_5377b62bda767935 = []byte{ + // 285 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, + 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x2f, 0x4a, 0x2c, + 0x28, 0x48, 0x2d, 0x2a, 0xd6, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0xca, + 0x5c, 0xdc, 0x2e, 0xf9, 0xa5, 0x49, 0x39, 0xa9, 0x61, 0x89, 0x39, 0xa5, 0xa9, 0x42, 0x22, 0x5c, + 0xac, 0x65, 0x20, 0x86, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x63, 0x10, 0x84, 0xa3, 0xa4, 0xc4, 0xc5, + 0xe5, 0x96, 0x93, 0x9f, 0x58, 0x82, 0x45, 0x0d, 0x13, 0x92, 0x1a, 0xcf, 0xbc, 0x12, 0x33, 0x13, + 0x2c, 0x6a, 0x98, 0x61, 0x6a, 0x94, 0xb9, 0xb8, 0x43, 0x71, 0x29, 0x62, 0x41, 0x35, 0xc8, 0xd8, + 0x08, 0x8b, 0x1a, 0x56, 0x34, 0x83, 0xb0, 0x2a, 0xe2, 0x85, 0x29, 0x52, 0xe4, 0xe2, 0x74, 0xca, + 0xcf, 0xcf, 0xc1, 0xa2, 0x84, 0x03, 0xc9, 0x9c, 0xe0, 0x92, 0xa2, 0xcc, 0xbc, 0x74, 0x2c, 0x8a, + 0x38, 0x91, 0x1c, 0xe4, 0x54, 0x59, 0x92, 0x5a, 0x8c, 0x45, 0x0d, 0x0f, 0x54, 0x8d, 0x53, 0x3b, + 0xe3, 0x8d, 0x87, 0x72, 0x0c, 0x1f, 0x1e, 0xca, 0x31, 0xfe, 0x78, 0x28, 0xc7, 0xd8, 0xf0, 0x48, + 0x8e, 0x71, 0xc5, 0x23, 0x39, 0xc6, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, + 0x48, 0x8e, 0xf1, 0xc5, 0x23, 0x39, 0x86, 0x0f, 0x20, 0xf1, 0xc7, 0x72, 0x8c, 0x27, 0x1e, 0xcb, + 0x31, 0x72, 0x09, 0x27, 0xe7, 0xe7, 0xea, 0xa1, 0x45, 0x87, 0x13, 0x6f, 0x38, 0x34, 0xbe, 0x02, + 0x40, 0x22, 0x01, 0x8c, 0x51, 0xac, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x3f, 0x18, 0x19, 0x17, 0x31, + 0x31, 0xbb, 0x07, 0x38, 0xad, 0x62, 0x92, 0x73, 0x87, 0x68, 0x09, 0x80, 0x6a, 0xd1, 0x0b, 0x4f, + 0xcd, 0xc9, 0xf1, 0xce, 0xcb, 0x2f, 0xcf, 0x0b, 0x01, 0xa9, 0x4c, 0x62, 0x03, 0x9b, 0x65, 0x0c, + 0x08, 0x00, 0x00, 0xff, 0xff, 0x31, 0x55, 0x64, 0x90, 0x0a, 0x02, 0x00, 0x00, +} + func (this *DoubleValue) Compare(that interface{}) int { if that == nil { if this == nil { @@ -266,6 +587,9 @@ func (this *DoubleValue) Compare(that interface{}) int { } return 1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *FloatValue) Compare(that interface{}) int { @@ -299,6 +623,9 @@ func (this *FloatValue) Compare(that interface{}) int { } return 1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *Int64Value) Compare(that interface{}) int { @@ -332,6 +659,9 @@ func (this *Int64Value) Compare(that interface{}) int { } return 1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *UInt64Value) Compare(that interface{}) int { @@ -365,6 +695,9 @@ func (this *UInt64Value) Compare(that interface{}) int { } return 1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *Int32Value) Compare(that interface{}) int { @@ -398,6 +731,9 @@ func (this *Int32Value) Compare(that interface{}) int { } return 1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *UInt32Value) Compare(that interface{}) int { @@ -431,6 +767,9 @@ func (this *UInt32Value) Compare(that interface{}) int { } return 1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *BoolValue) Compare(that interface{}) int { @@ -464,6 +803,9 @@ func (this *BoolValue) Compare(that interface{}) int { } return 1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *StringValue) Compare(that interface{}) int { @@ -497,6 +839,9 @@ func (this *StringValue) Compare(that interface{}) int { } return 1 } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *BytesValue) Compare(that interface{}) int { @@ -527,6 +872,9 @@ func (this *BytesValue) Compare(that interface{}) int { if c := bytes.Compare(this.Value, that1.Value); c != 0 { return c } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } return 0 } func (this *DoubleValue) Equal(that interface{}) bool { @@ -551,6 +899,9 @@ func (this *DoubleValue) Equal(that interface{}) bool { if this.Value != that1.Value { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *FloatValue) Equal(that interface{}) bool { @@ -575,6 +926,9 @@ func (this *FloatValue) Equal(that interface{}) bool { if this.Value != that1.Value { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *Int64Value) Equal(that interface{}) bool { @@ -599,6 +953,9 @@ func (this *Int64Value) Equal(that interface{}) bool { if this.Value != that1.Value { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *UInt64Value) Equal(that interface{}) bool { @@ -623,6 +980,9 @@ func (this *UInt64Value) Equal(that interface{}) bool { if this.Value != that1.Value { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *Int32Value) Equal(that interface{}) bool { @@ -647,6 +1007,9 @@ func (this *Int32Value) Equal(that interface{}) bool { if this.Value != that1.Value { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *UInt32Value) Equal(that interface{}) bool { @@ -671,6 +1034,9 @@ func (this *UInt32Value) Equal(that interface{}) bool { if this.Value != that1.Value { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *BoolValue) Equal(that interface{}) bool { @@ -695,6 +1061,9 @@ func (this *BoolValue) Equal(that interface{}) bool { if this.Value != that1.Value { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *StringValue) Equal(that interface{}) bool { @@ -719,6 +1088,9 @@ func (this *StringValue) Equal(that interface{}) bool { if this.Value != that1.Value { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *BytesValue) Equal(that interface{}) bool { @@ -743,6 +1115,9 @@ func (this *BytesValue) Equal(that interface{}) bool { if !bytes.Equal(this.Value, that1.Value) { return false } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } return true } func (this *DoubleValue) GoString() string { @@ -752,6 +1127,9 @@ func (this *DoubleValue) GoString() string { s := make([]string, 0, 5) s = append(s, "&types.DoubleValue{") s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -762,6 +1140,9 @@ func (this *FloatValue) GoString() string { s := make([]string, 0, 5) s = append(s, "&types.FloatValue{") s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -772,6 +1153,9 @@ func (this *Int64Value) GoString() string { s := make([]string, 0, 5) s = append(s, "&types.Int64Value{") s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -782,6 +1166,9 @@ func (this *UInt64Value) GoString() string { s := make([]string, 0, 5) s = append(s, "&types.UInt64Value{") s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -792,6 +1179,9 @@ func (this *Int32Value) GoString() string { s := make([]string, 0, 5) s = append(s, "&types.Int32Value{") s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -802,6 +1192,9 @@ func (this *UInt32Value) GoString() string { s := make([]string, 0, 5) s = append(s, "&types.UInt32Value{") s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -812,6 +1205,9 @@ func (this *BoolValue) GoString() string { s := make([]string, 0, 5) s = append(s, "&types.BoolValue{") s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -822,6 +1218,9 @@ func (this *StringValue) GoString() string { s := make([]string, 0, 5) s = append(s, "&types.StringValue{") s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -832,6 +1231,9 @@ func (this *BytesValue) GoString() string { s := make([]string, 0, 5) s = append(s, "&types.BytesValue{") s = append(s, "Value: "+fmt.Sprintf("%#v", this.Value)+",\n") + if this.XXX_unrecognized != nil { + s = append(s, "XXX_unrecognized:"+fmt.Sprintf("%#v", this.XXX_unrecognized)+",\n") + } s = append(s, "}") return strings.Join(s, "") } @@ -846,7 +1248,7 @@ func valueToGoStringWrappers(v interface{}, typ string) string { func (m *DoubleValue) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -854,23 +1256,32 @@ func (m *DoubleValue) Marshal() (dAtA []byte, err error) { } func (m *DoubleValue) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DoubleValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Value != 0 { + i -= 8 + encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Value)))) + i-- dAtA[i] = 0x9 - i++ - binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Value)))) - i += 8 } - return i, nil + return len(dAtA) - i, nil } func (m *FloatValue) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -878,23 +1289,32 @@ func (m *FloatValue) Marshal() (dAtA []byte, err error) { } func (m *FloatValue) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FloatValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Value != 0 { + i -= 4 + encoding_binary.LittleEndian.PutUint32(dAtA[i:], uint32(math.Float32bits(float32(m.Value)))) + i-- dAtA[i] = 0xd - i++ - binary.LittleEndian.PutUint32(dAtA[i:], uint32(math.Float32bits(float32(m.Value)))) - i += 4 } - return i, nil + return len(dAtA) - i, nil } func (m *Int64Value) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -902,22 +1322,31 @@ func (m *Int64Value) Marshal() (dAtA []byte, err error) { } func (m *Int64Value) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Int64Value) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Value != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintWrappers(dAtA, i, uint64(m.Value)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *UInt64Value) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -925,22 +1354,31 @@ func (m *UInt64Value) Marshal() (dAtA []byte, err error) { } func (m *UInt64Value) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UInt64Value) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Value != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintWrappers(dAtA, i, uint64(m.Value)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *Int32Value) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -948,22 +1386,31 @@ func (m *Int32Value) Marshal() (dAtA []byte, err error) { } func (m *Int32Value) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Int32Value) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Value != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintWrappers(dAtA, i, uint64(m.Value)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *UInt32Value) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -971,22 +1418,31 @@ func (m *UInt32Value) Marshal() (dAtA []byte, err error) { } func (m *UInt32Value) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UInt32Value) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Value != 0 { - dAtA[i] = 0x8 - i++ i = encodeVarintWrappers(dAtA, i, uint64(m.Value)) + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *BoolValue) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -994,27 +1450,36 @@ func (m *BoolValue) Marshal() (dAtA []byte, err error) { } func (m *BoolValue) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BoolValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if m.Value { - dAtA[i] = 0x8 - i++ + i-- if m.Value { dAtA[i] = 1 } else { dAtA[i] = 0 } - i++ + i-- + dAtA[i] = 0x8 } - return i, nil + return len(dAtA) - i, nil } func (m *StringValue) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1022,23 +1487,33 @@ func (m *StringValue) Marshal() (dAtA []byte, err error) { } func (m *StringValue) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *StringValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Value) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Value) + copy(dAtA[i:], m.Value) i = encodeVarintWrappers(dAtA, i, uint64(len(m.Value))) - i += copy(dAtA[i:], m.Value) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func (m *BytesValue) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) - n, err := m.MarshalTo(dAtA) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) if err != nil { return nil, err } @@ -1046,27 +1521,39 @@ func (m *BytesValue) Marshal() (dAtA []byte, err error) { } func (m *BytesValue) MarshalTo(dAtA []byte) (int, error) { - var i int + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *BytesValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) _ = i var l int _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } if len(m.Value) > 0 { - dAtA[i] = 0xa - i++ + i -= len(m.Value) + copy(dAtA[i:], m.Value) i = encodeVarintWrappers(dAtA, i, uint64(len(m.Value))) - i += copy(dAtA[i:], m.Value) + i-- + dAtA[i] = 0xa } - return i, nil + return len(dAtA) - i, nil } func encodeVarintWrappers(dAtA []byte, offset int, v uint64) int { + offset -= sovWrappers(v) + base := offset for v >= 1<<7 { dAtA[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ } dAtA[offset] = uint8(v) - return offset + 1 + return base } func NewPopulatedDoubleValue(r randyWrappers, easy bool) *DoubleValue { this := &DoubleValue{} @@ -1075,6 +1562,7 @@ func NewPopulatedDoubleValue(r randyWrappers, easy bool) *DoubleValue { this.Value *= -1 } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedWrappers(r, 2) } return this } @@ -1086,6 +1574,7 @@ func NewPopulatedFloatValue(r randyWrappers, easy bool) *FloatValue { this.Value *= -1 } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedWrappers(r, 2) } return this } @@ -1097,6 +1586,7 @@ func NewPopulatedInt64Value(r randyWrappers, easy bool) *Int64Value { this.Value *= -1 } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedWrappers(r, 2) } return this } @@ -1105,6 +1595,7 @@ func NewPopulatedUInt64Value(r randyWrappers, easy bool) *UInt64Value { this := &UInt64Value{} this.Value = uint64(uint64(r.Uint32())) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedWrappers(r, 2) } return this } @@ -1116,6 +1607,7 @@ func NewPopulatedInt32Value(r randyWrappers, easy bool) *Int32Value { this.Value *= -1 } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedWrappers(r, 2) } return this } @@ -1124,6 +1616,7 @@ func NewPopulatedUInt32Value(r randyWrappers, easy bool) *UInt32Value { this := &UInt32Value{} this.Value = uint32(r.Uint32()) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedWrappers(r, 2) } return this } @@ -1132,6 +1625,7 @@ func NewPopulatedBoolValue(r randyWrappers, easy bool) *BoolValue { this := &BoolValue{} this.Value = bool(bool(r.Intn(2) == 0)) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedWrappers(r, 2) } return this } @@ -1140,6 +1634,7 @@ func NewPopulatedStringValue(r randyWrappers, easy bool) *StringValue { this := &StringValue{} this.Value = string(randStringWrappers(r)) if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedWrappers(r, 2) } return this } @@ -1152,6 +1647,7 @@ func NewPopulatedBytesValue(r randyWrappers, easy bool) *BytesValue { this.Value[i] = byte(r.Intn(256)) } if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedWrappers(r, 2) } return this } @@ -1229,97 +1725,144 @@ func encodeVarintPopulateWrappers(dAtA []byte, v uint64) []byte { return dAtA } func (m *DoubleValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Value != 0 { n += 9 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *FloatValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Value != 0 { n += 5 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *Int64Value) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Value != 0 { n += 1 + sovWrappers(uint64(m.Value)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UInt64Value) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Value != 0 { n += 1 + sovWrappers(uint64(m.Value)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *Int32Value) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Value != 0 { n += 1 + sovWrappers(uint64(m.Value)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *UInt32Value) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Value != 0 { n += 1 + sovWrappers(uint64(m.Value)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *BoolValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Value { n += 2 } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *StringValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Value) if l > 0 { n += 1 + l + sovWrappers(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func (m *BytesValue) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Value) if l > 0 { n += 1 + l + sovWrappers(uint64(l)) } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } return n } func sovWrappers(x uint64) (n int) { - for { - n++ - x >>= 7 - if x == 0 { - break - } - } - return n + return (math_bits.Len64(x|1) + 6) / 7 } func sozWrappers(x uint64) (n int) { return sovWrappers(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -1330,6 +1873,7 @@ func (this *DoubleValue) String() string { } s := strings.Join([]string{`&DoubleValue{`, `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1340,6 +1884,7 @@ func (this *FloatValue) String() string { } s := strings.Join([]string{`&FloatValue{`, `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1350,6 +1895,7 @@ func (this *Int64Value) String() string { } s := strings.Join([]string{`&Int64Value{`, `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1360,6 +1906,7 @@ func (this *UInt64Value) String() string { } s := strings.Join([]string{`&UInt64Value{`, `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1370,6 +1917,7 @@ func (this *Int32Value) String() string { } s := strings.Join([]string{`&Int32Value{`, `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1380,6 +1928,7 @@ func (this *UInt32Value) String() string { } s := strings.Join([]string{`&UInt32Value{`, `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1390,6 +1939,7 @@ func (this *BoolValue) String() string { } s := strings.Join([]string{`&BoolValue{`, `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1400,6 +1950,7 @@ func (this *StringValue) String() string { } s := strings.Join([]string{`&StringValue{`, `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1410,6 +1961,7 @@ func (this *BytesValue) String() string { } s := strings.Join([]string{`&BytesValue{`, `Value:` + fmt.Sprintf("%v", this.Value) + `,`, + `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`, `}`, }, "") return s @@ -1437,7 +1989,7 @@ func (m *DoubleValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1459,7 +2011,7 @@ func (m *DoubleValue) Unmarshal(dAtA []byte) error { if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } - v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) + v = uint64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Value = float64(math.Float64frombits(v)) default: @@ -1468,12 +2020,13 @@ func (m *DoubleValue) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWrappers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1498,7 +2051,7 @@ func (m *FloatValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1520,7 +2073,7 @@ func (m *FloatValue) Unmarshal(dAtA []byte) error { if (iNdEx + 4) > l { return io.ErrUnexpectedEOF } - v = uint32(binary.LittleEndian.Uint32(dAtA[iNdEx:])) + v = uint32(encoding_binary.LittleEndian.Uint32(dAtA[iNdEx:])) iNdEx += 4 m.Value = float32(math.Float32frombits(v)) default: @@ -1529,12 +2082,13 @@ func (m *FloatValue) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWrappers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1559,7 +2113,7 @@ func (m *Int64Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1587,7 +2141,7 @@ func (m *Int64Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Value |= (int64(b) & 0x7F) << shift + m.Value |= int64(b&0x7F) << shift if b < 0x80 { break } @@ -1598,12 +2152,13 @@ func (m *Int64Value) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWrappers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1628,7 +2183,7 @@ func (m *UInt64Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1656,7 +2211,7 @@ func (m *UInt64Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Value |= (uint64(b) & 0x7F) << shift + m.Value |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1667,12 +2222,13 @@ func (m *UInt64Value) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWrappers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1697,7 +2253,7 @@ func (m *Int32Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1725,7 +2281,7 @@ func (m *Int32Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Value |= (int32(b) & 0x7F) << shift + m.Value |= int32(b&0x7F) << shift if b < 0x80 { break } @@ -1736,12 +2292,13 @@ func (m *Int32Value) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWrappers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1766,7 +2323,7 @@ func (m *UInt32Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1794,7 +2351,7 @@ func (m *UInt32Value) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Value |= (uint32(b) & 0x7F) << shift + m.Value |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -1805,12 +2362,13 @@ func (m *UInt32Value) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWrappers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1835,7 +2393,7 @@ func (m *BoolValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1863,7 +2421,7 @@ func (m *BoolValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= (int(b) & 0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } @@ -1875,12 +2433,13 @@ func (m *BoolValue) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWrappers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1905,7 +2464,7 @@ func (m *StringValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1933,7 +2492,7 @@ func (m *StringValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= (uint64(b) & 0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -1943,6 +2502,9 @@ func (m *StringValue) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWrappers } postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthWrappers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -1954,12 +2516,13 @@ func (m *StringValue) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWrappers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -1984,7 +2547,7 @@ func (m *BytesValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - wire |= (uint64(b) & 0x7F) << shift + wire |= uint64(b&0x7F) << shift if b < 0x80 { break } @@ -2012,7 +2575,7 @@ func (m *BytesValue) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= (int(b) & 0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } @@ -2021,6 +2584,9 @@ func (m *BytesValue) Unmarshal(dAtA []byte) error { return ErrInvalidLengthWrappers } postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthWrappers + } if postIndex > l { return io.ErrUnexpectedEOF } @@ -2035,12 +2601,13 @@ func (m *BytesValue) Unmarshal(dAtA []byte) error { if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthWrappers } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -2053,6 +2620,7 @@ func (m *BytesValue) Unmarshal(dAtA []byte) error { func skipWrappers(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -2084,10 +2652,8 @@ func skipWrappers(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -2104,77 +2670,34 @@ func skipWrappers(dAtA []byte) (n int, err error) { break } } - iNdEx += length if length < 0 { return 0, ErrInvalidLengthWrappers } - return iNdEx, nil + iNdEx += length case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowWrappers - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipWrappers(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupWrappers + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthWrappers + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthWrappers = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowWrappers = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthWrappers = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowWrappers = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupWrappers = fmt.Errorf("proto: unexpected end of group") ) - -func init() { proto.RegisterFile("wrappers.proto", fileDescriptorWrappers) } - -var fileDescriptorWrappers = []byte{ - // 278 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2b, 0x2f, 0x4a, 0x2c, - 0x28, 0x48, 0x2d, 0x2a, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4f, 0xcf, 0xcf, 0x4f, - 0xcf, 0x49, 0x85, 0xf0, 0x92, 0x4a, 0xd3, 0x94, 0x94, 0xb9, 0xb8, 0x5d, 0xf2, 0x4b, 0x93, 0x72, - 0x52, 0xc3, 0x12, 0x73, 0x4a, 0x53, 0x85, 0x44, 0xb8, 0x58, 0xcb, 0x40, 0x0c, 0x09, 0x46, 0x05, - 0x46, 0x0d, 0xc6, 0x20, 0x08, 0x47, 0x49, 0x89, 0x8b, 0xcb, 0x2d, 0x27, 0x3f, 0xb1, 0x04, 0x8b, - 0x1a, 0x26, 0x24, 0x35, 0x9e, 0x79, 0x25, 0x66, 0x26, 0x58, 0xd4, 0x30, 0xc3, 0xd4, 0x28, 0x73, - 0x71, 0x87, 0xe2, 0x52, 0xc4, 0x82, 0x6a, 0x90, 0xb1, 0x11, 0x16, 0x35, 0xac, 0x68, 0x06, 0x61, - 0x55, 0xc4, 0x0b, 0x53, 0xa4, 0xc8, 0xc5, 0xe9, 0x94, 0x9f, 0x9f, 0x83, 0x45, 0x09, 0x07, 0x92, - 0x39, 0xc1, 0x25, 0x45, 0x99, 0x79, 0xe9, 0x58, 0x14, 0x71, 0x22, 0x39, 0xc8, 0xa9, 0xb2, 0x24, - 0xb5, 0x18, 0x8b, 0x1a, 0x1e, 0xa8, 0x1a, 0xa7, 0x76, 0xc6, 0x0b, 0x0f, 0xe5, 0x18, 0x6e, 0x3c, - 0x94, 0x63, 0xf8, 0xf0, 0x50, 0x8e, 0xf1, 0xc7, 0x43, 0x39, 0xc6, 0x86, 0x47, 0x72, 0x8c, 0x2b, - 0x1e, 0xc9, 0x31, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, - 0x2f, 0x1e, 0xc9, 0x31, 0x7c, 0x00, 0x89, 0x3f, 0x96, 0x63, 0xe4, 0x12, 0x4e, 0xce, 0xcf, 0xd5, - 0x43, 0x8b, 0x0e, 0x27, 0xde, 0x70, 0x68, 0x7c, 0x05, 0x80, 0x44, 0x02, 0x18, 0xa3, 0x58, 0x4b, - 0x2a, 0x0b, 0x52, 0x8b, 0x7f, 0x30, 0x32, 0x2e, 0x62, 0x62, 0x76, 0x0f, 0x70, 0x5a, 0xc5, 0x24, - 0xe7, 0x0e, 0xd1, 0x12, 0x00, 0xd5, 0xa2, 0x17, 0x9e, 0x9a, 0x93, 0xe3, 0x9d, 0x97, 0x5f, 0x9e, - 0x17, 0x02, 0x52, 0x99, 0xc4, 0x06, 0x36, 0xcb, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x23, 0x27, - 0x6c, 0x5f, 0xfa, 0x01, 0x00, 0x00, -} diff --git a/vendor/github.com/gogo/protobuf/types/wrappers_gogo.go b/vendor/github.com/gogo/protobuf/types/wrappers_gogo.go new file mode 100644 index 0000000000000..d905df36055d0 --- /dev/null +++ b/vendor/github.com/gogo/protobuf/types/wrappers_gogo.go @@ -0,0 +1,300 @@ +// Protocol Buffers for Go with Gadgets +// +// Copyright (c) 2018, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package types + +func NewPopulatedStdDouble(r randyWrappers, easy bool) *float64 { + v := NewPopulatedDoubleValue(r, easy) + return &v.Value +} + +func SizeOfStdDouble(v float64) int { + pv := &DoubleValue{Value: v} + return pv.Size() +} + +func StdDoubleMarshal(v float64) ([]byte, error) { + size := SizeOfStdDouble(v) + buf := make([]byte, size) + _, err := StdDoubleMarshalTo(v, buf) + return buf, err +} + +func StdDoubleMarshalTo(v float64, data []byte) (int, error) { + pv := &DoubleValue{Value: v} + return pv.MarshalTo(data) +} + +func StdDoubleUnmarshal(v *float64, data []byte) error { + pv := &DoubleValue{} + if err := pv.Unmarshal(data); err != nil { + return err + } + *v = pv.Value + return nil +} +func NewPopulatedStdFloat(r randyWrappers, easy bool) *float32 { + v := NewPopulatedFloatValue(r, easy) + return &v.Value +} + +func SizeOfStdFloat(v float32) int { + pv := &FloatValue{Value: v} + return pv.Size() +} + +func StdFloatMarshal(v float32) ([]byte, error) { + size := SizeOfStdFloat(v) + buf := make([]byte, size) + _, err := StdFloatMarshalTo(v, buf) + return buf, err +} + +func StdFloatMarshalTo(v float32, data []byte) (int, error) { + pv := &FloatValue{Value: v} + return pv.MarshalTo(data) +} + +func StdFloatUnmarshal(v *float32, data []byte) error { + pv := &FloatValue{} + if err := pv.Unmarshal(data); err != nil { + return err + } + *v = pv.Value + return nil +} +func NewPopulatedStdInt64(r randyWrappers, easy bool) *int64 { + v := NewPopulatedInt64Value(r, easy) + return &v.Value +} + +func SizeOfStdInt64(v int64) int { + pv := &Int64Value{Value: v} + return pv.Size() +} + +func StdInt64Marshal(v int64) ([]byte, error) { + size := SizeOfStdInt64(v) + buf := make([]byte, size) + _, err := StdInt64MarshalTo(v, buf) + return buf, err +} + +func StdInt64MarshalTo(v int64, data []byte) (int, error) { + pv := &Int64Value{Value: v} + return pv.MarshalTo(data) +} + +func StdInt64Unmarshal(v *int64, data []byte) error { + pv := &Int64Value{} + if err := pv.Unmarshal(data); err != nil { + return err + } + *v = pv.Value + return nil +} +func NewPopulatedStdUInt64(r randyWrappers, easy bool) *uint64 { + v := NewPopulatedUInt64Value(r, easy) + return &v.Value +} + +func SizeOfStdUInt64(v uint64) int { + pv := &UInt64Value{Value: v} + return pv.Size() +} + +func StdUInt64Marshal(v uint64) ([]byte, error) { + size := SizeOfStdUInt64(v) + buf := make([]byte, size) + _, err := StdUInt64MarshalTo(v, buf) + return buf, err +} + +func StdUInt64MarshalTo(v uint64, data []byte) (int, error) { + pv := &UInt64Value{Value: v} + return pv.MarshalTo(data) +} + +func StdUInt64Unmarshal(v *uint64, data []byte) error { + pv := &UInt64Value{} + if err := pv.Unmarshal(data); err != nil { + return err + } + *v = pv.Value + return nil +} +func NewPopulatedStdInt32(r randyWrappers, easy bool) *int32 { + v := NewPopulatedInt32Value(r, easy) + return &v.Value +} + +func SizeOfStdInt32(v int32) int { + pv := &Int32Value{Value: v} + return pv.Size() +} + +func StdInt32Marshal(v int32) ([]byte, error) { + size := SizeOfStdInt32(v) + buf := make([]byte, size) + _, err := StdInt32MarshalTo(v, buf) + return buf, err +} + +func StdInt32MarshalTo(v int32, data []byte) (int, error) { + pv := &Int32Value{Value: v} + return pv.MarshalTo(data) +} + +func StdInt32Unmarshal(v *int32, data []byte) error { + pv := &Int32Value{} + if err := pv.Unmarshal(data); err != nil { + return err + } + *v = pv.Value + return nil +} +func NewPopulatedStdUInt32(r randyWrappers, easy bool) *uint32 { + v := NewPopulatedUInt32Value(r, easy) + return &v.Value +} + +func SizeOfStdUInt32(v uint32) int { + pv := &UInt32Value{Value: v} + return pv.Size() +} + +func StdUInt32Marshal(v uint32) ([]byte, error) { + size := SizeOfStdUInt32(v) + buf := make([]byte, size) + _, err := StdUInt32MarshalTo(v, buf) + return buf, err +} + +func StdUInt32MarshalTo(v uint32, data []byte) (int, error) { + pv := &UInt32Value{Value: v} + return pv.MarshalTo(data) +} + +func StdUInt32Unmarshal(v *uint32, data []byte) error { + pv := &UInt32Value{} + if err := pv.Unmarshal(data); err != nil { + return err + } + *v = pv.Value + return nil +} +func NewPopulatedStdBool(r randyWrappers, easy bool) *bool { + v := NewPopulatedBoolValue(r, easy) + return &v.Value +} + +func SizeOfStdBool(v bool) int { + pv := &BoolValue{Value: v} + return pv.Size() +} + +func StdBoolMarshal(v bool) ([]byte, error) { + size := SizeOfStdBool(v) + buf := make([]byte, size) + _, err := StdBoolMarshalTo(v, buf) + return buf, err +} + +func StdBoolMarshalTo(v bool, data []byte) (int, error) { + pv := &BoolValue{Value: v} + return pv.MarshalTo(data) +} + +func StdBoolUnmarshal(v *bool, data []byte) error { + pv := &BoolValue{} + if err := pv.Unmarshal(data); err != nil { + return err + } + *v = pv.Value + return nil +} +func NewPopulatedStdString(r randyWrappers, easy bool) *string { + v := NewPopulatedStringValue(r, easy) + return &v.Value +} + +func SizeOfStdString(v string) int { + pv := &StringValue{Value: v} + return pv.Size() +} + +func StdStringMarshal(v string) ([]byte, error) { + size := SizeOfStdString(v) + buf := make([]byte, size) + _, err := StdStringMarshalTo(v, buf) + return buf, err +} + +func StdStringMarshalTo(v string, data []byte) (int, error) { + pv := &StringValue{Value: v} + return pv.MarshalTo(data) +} + +func StdStringUnmarshal(v *string, data []byte) error { + pv := &StringValue{} + if err := pv.Unmarshal(data); err != nil { + return err + } + *v = pv.Value + return nil +} +func NewPopulatedStdBytes(r randyWrappers, easy bool) *[]byte { + v := NewPopulatedBytesValue(r, easy) + return &v.Value +} + +func SizeOfStdBytes(v []byte) int { + pv := &BytesValue{Value: v} + return pv.Size() +} + +func StdBytesMarshal(v []byte) ([]byte, error) { + size := SizeOfStdBytes(v) + buf := make([]byte, size) + _, err := StdBytesMarshalTo(v, buf) + return buf, err +} + +func StdBytesMarshalTo(v []byte, data []byte) (int, error) { + pv := &BytesValue{Value: v} + return pv.MarshalTo(data) +} + +func StdBytesUnmarshal(v *[]byte, data []byte) error { + pv := &BytesValue{} + if err := pv.Unmarshal(data); err != nil { + return err + } + *v = pv.Value + return nil +} diff --git a/vendor/github.com/golang/gddo/README.markdown b/vendor/github.com/golang/gddo/README.markdown index 7194f4c08c227..f1b019afd73e1 100644 --- a/vendor/github.com/golang/gddo/README.markdown +++ b/vendor/github.com/golang/gddo/README.markdown @@ -1,6 +1,6 @@ -This project is the source for http://godoc.org/ +This project is the source for https://godoc.org/ -[![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](http://godoc.org/github.com/golang/gddo) +[![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](https://godoc.org/github.com/golang/gddo) [![Build Status](https://travis-ci.org/golang/gddo.svg?branch=master)](https://travis-ci.org/golang/gddo) @@ -19,8 +19,6 @@ Contributions to this project are welcome, though please [file an issue](https://github.com/golang/gddo/issues/new). before starting work on anything major. -**We do not accept GitHub pull requests** - Please refer to the [Contribution Guidelines](https://golang.org/doc/contribute.html) on how to submit changes. diff --git a/vendor/github.com/golang/groupcache/LICENSE b/vendor/github.com/golang/groupcache/LICENSE new file mode 100644 index 0000000000000..37ec93a14fdcd --- /dev/null +++ b/vendor/github.com/golang/groupcache/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/golang/groupcache/README.md b/vendor/github.com/golang/groupcache/README.md new file mode 100644 index 0000000000000..70c29da16026f --- /dev/null +++ b/vendor/github.com/golang/groupcache/README.md @@ -0,0 +1,73 @@ +# groupcache + +## Summary + +groupcache is a caching and cache-filling library, intended as a +replacement for memcached in many cases. + +For API docs and examples, see http://godoc.org/github.com/golang/groupcache + +## Comparison to memcached + +### **Like memcached**, groupcache: + + * shards by key to select which peer is responsible for that key + +### **Unlike memcached**, groupcache: + + * does not require running a separate set of servers, thus massively + reducing deployment/configuration pain. groupcache is a client + library as well as a server. It connects to its own peers. + + * comes with a cache filling mechanism. Whereas memcached just says + "Sorry, cache miss", often resulting in a thundering herd of + database (or whatever) loads from an unbounded number of clients + (which has resulted in several fun outages), groupcache coordinates + cache fills such that only one load in one process of an entire + replicated set of processes populates the cache, then multiplexes + the loaded value to all callers. + + * does not support versioned values. If key "foo" is value "bar", + key "foo" must always be "bar". There are neither cache expiration + times, nor explicit cache evictions. Thus there is also no CAS, + nor Increment/Decrement. This also means that groupcache.... + + * ... supports automatic mirroring of super-hot items to multiple + processes. This prevents memcached hot spotting where a machine's + CPU and/or NIC are overloaded by very popular keys/values. + + * is currently only available for Go. It's very unlikely that I + (bradfitz@) will port the code to any other language. + +## Loading process + +In a nutshell, a groupcache lookup of **Get("foo")** looks like: + +(On machine #5 of a set of N machines running the same code) + + 1. Is the value of "foo" in local memory because it's super hot? If so, use it. + + 2. Is the value of "foo" in local memory because peer #5 (the current + peer) is the owner of it? If so, use it. + + 3. Amongst all the peers in my set of N, am I the owner of the key + "foo"? (e.g. does it consistent hash to 5?) If so, load it. If + other callers come in, via the same process or via RPC requests + from peers, they block waiting for the load to finish and get the + same answer. If not, RPC to the peer that's the owner and get + the answer. If the RPC fails, just load it locally (still with + local dup suppression). + +## Users + +groupcache is in production use by dl.google.com (its original user), +parts of Blogger, parts of Google Code, parts of Google Fiber, parts +of Google production monitoring systems, etc. + +## Presentations + +See http://talks.golang.org/2013/oscon-dl.slide + +## Help + +Use the golang-nuts mailing list for any discussion or questions. diff --git a/vendor/github.com/golang/groupcache/lru/lru.go b/vendor/github.com/golang/groupcache/lru/lru.go new file mode 100644 index 0000000000000..eac1c7664f995 --- /dev/null +++ b/vendor/github.com/golang/groupcache/lru/lru.go @@ -0,0 +1,133 @@ +/* +Copyright 2013 Google Inc. + +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. +*/ + +// Package lru implements an LRU cache. +package lru + +import "container/list" + +// Cache is an LRU cache. It is not safe for concurrent access. +type Cache struct { + // MaxEntries is the maximum number of cache entries before + // an item is evicted. Zero means no limit. + MaxEntries int + + // OnEvicted optionally specifies a callback function to be + // executed when an entry is purged from the cache. + OnEvicted func(key Key, value interface{}) + + ll *list.List + cache map[interface{}]*list.Element +} + +// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators +type Key interface{} + +type entry struct { + key Key + value interface{} +} + +// New creates a new Cache. +// If maxEntries is zero, the cache has no limit and it's assumed +// that eviction is done by the caller. +func New(maxEntries int) *Cache { + return &Cache{ + MaxEntries: maxEntries, + ll: list.New(), + cache: make(map[interface{}]*list.Element), + } +} + +// Add adds a value to the cache. +func (c *Cache) Add(key Key, value interface{}) { + if c.cache == nil { + c.cache = make(map[interface{}]*list.Element) + c.ll = list.New() + } + if ee, ok := c.cache[key]; ok { + c.ll.MoveToFront(ee) + ee.Value.(*entry).value = value + return + } + ele := c.ll.PushFront(&entry{key, value}) + c.cache[key] = ele + if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { + c.RemoveOldest() + } +} + +// Get looks up a key's value from the cache. +func (c *Cache) Get(key Key) (value interface{}, ok bool) { + if c.cache == nil { + return + } + if ele, hit := c.cache[key]; hit { + c.ll.MoveToFront(ele) + return ele.Value.(*entry).value, true + } + return +} + +// Remove removes the provided key from the cache. +func (c *Cache) Remove(key Key) { + if c.cache == nil { + return + } + if ele, hit := c.cache[key]; hit { + c.removeElement(ele) + } +} + +// RemoveOldest removes the oldest item from the cache. +func (c *Cache) RemoveOldest() { + if c.cache == nil { + return + } + ele := c.ll.Back() + if ele != nil { + c.removeElement(ele) + } +} + +func (c *Cache) removeElement(e *list.Element) { + c.ll.Remove(e) + kv := e.Value.(*entry) + delete(c.cache, kv.key) + if c.OnEvicted != nil { + c.OnEvicted(kv.key, kv.value) + } +} + +// Len returns the number of items in the cache. +func (c *Cache) Len() int { + if c.cache == nil { + return 0 + } + return c.ll.Len() +} + +// Clear purges all stored items from the cache. +func (c *Cache) Clear() { + if c.OnEvicted != nil { + for _, e := range c.cache { + kv := e.Value.(*entry) + c.OnEvicted(kv.key, kv.value) + } + } + c.ll = nil + c.cache = nil +} diff --git a/vendor/github.com/golang/protobuf/LICENSE b/vendor/github.com/golang/protobuf/LICENSE index 1b1b1921efa6d..0f646931a4627 100644 --- a/vendor/github.com/golang/protobuf/LICENSE +++ b/vendor/github.com/golang/protobuf/LICENSE @@ -1,7 +1,4 @@ -Go support for Protocol Buffers - Google's data interchange format - Copyright 2010 The Go Authors. All rights reserved. -https://github.com/golang/protobuf Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/vendor/github.com/golang/protobuf/README.md b/vendor/github.com/golang/protobuf/README.md index 01b29daf2631a..2e253e6f872a4 100644 --- a/vendor/github.com/golang/protobuf/README.md +++ b/vendor/github.com/golang/protobuf/README.md @@ -1,4 +1,4 @@ -# Go support for Protocol Buffers +# Go support for Protocol Buffers - Google's data interchange format [![Build Status](https://travis-ci.org/golang/protobuf.svg?branch=master)](https://travis-ci.org/golang/protobuf) [![GoDoc](https://godoc.org/github.com/golang/protobuf?status.svg)](https://godoc.org/github.com/golang/protobuf) @@ -7,7 +7,7 @@ Google's data interchange format. Copyright 2010 The Go Authors. https://github.com/golang/protobuf -This package and the code it generates requires at least Go 1.6. +This package and the code it generates requires at least Go 1.9. This software implements Go bindings for protocol buffers. For information about protocol buffers themselves, see @@ -24,11 +24,22 @@ To use this software, you must: https://golang.org/doc/install for details or, if you are using gccgo, follow the instructions at https://golang.org/doc/install/gccgo -- Grab the code from the repository and install the proto package. - The simplest way is to run `go get -u github.com/golang/protobuf/protoc-gen-go`. - The compiler plugin, protoc-gen-go, will be installed in $GOBIN, - defaulting to $GOPATH/bin. It must be in your $PATH for the protocol - compiler, protoc, to find it. +- Grab the code from the repository and install the `proto` package. + The simplest way is to run: + ``` + go get -u github.com/golang/protobuf/protoc-gen-go + ``` + The compiler plugin, `protoc-gen-go`, will be installed in `$GOPATH/bin` + unless `$GOBIN` is set. It must be in your `$PATH` for the protocol + compiler, `protoc`, to find it. +- If you need a particular version of `protoc-gen-go` (e.g., to match your + `proto` package version), one option is + ```shell + GIT_TAG="v1.2.0" # change as needed + go get -d -u github.com/golang/protobuf/protoc-gen-go + git -C "$(go env GOPATH)"/src/github.com/golang/protobuf checkout $GIT_TAG + go install github.com/golang/protobuf/protoc-gen-go + ``` This software has two parts: a 'protocol compiler plugin' that generates Go source files that, once compiled, can access and manage @@ -83,15 +94,19 @@ be: - Relative to the import path: - protoc --go_out=. inputs/x.proto - # writes ./github.com/golang/protobuf/p/x.pb.go +```shell + protoc --go_out=. inputs/x.proto + # writes ./github.com/golang/protobuf/p/x.pb.go +``` (This can work well with `--go_out=$GOPATH`.) - Relative to the input file: - protoc --go_out=paths=source_relative:. inputs/x.proto - # generate ./inputs/x.pb.go +```shell +protoc --go_out=paths=source_relative:. inputs/x.proto +# generate ./inputs/x.pb.go +``` ## Generated code ## @@ -157,9 +172,6 @@ Consider file test.proto, containing required string label = 1; optional int32 type = 2 [default=77]; repeated int64 reps = 3; - optional group OptionalGroup = 4 { - required string RequiredField = 5; - } } ``` @@ -176,13 +188,10 @@ To create and play with a Test object from the example package, ) func main() { - test := &example.Test { + test := &example.Test{ Label: proto.String("hello"), Type: proto.Int32(17), Reps: []int64{1, 2, 3}, - Optionalgroup: &example.Test_OptionalGroup { - RequiredField: proto.String("good bye"), - }, } data, err := proto.Marshal(test) if err != nil { diff --git a/vendor/github.com/golang/protobuf/go.mod b/vendor/github.com/golang/protobuf/go.mod new file mode 100644 index 0000000000000..aa055824d89fe --- /dev/null +++ b/vendor/github.com/golang/protobuf/go.mod @@ -0,0 +1,3 @@ +module github.com/golang/protobuf + +go 1.9 diff --git a/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go b/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go new file mode 100644 index 0000000000000..f0d66befbb8ef --- /dev/null +++ b/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go @@ -0,0 +1,1290 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2015 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* +Package jsonpb provides marshaling and unmarshaling between protocol buffers and JSON. +It follows the specification at https://developers.google.com/protocol-buffers/docs/proto3#json. + +This package produces a different output than the standard "encoding/json" package, +which does not operate correctly on protocol buffers. +*/ +package jsonpb + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "reflect" + "sort" + "strconv" + "strings" + "time" + + "github.com/golang/protobuf/proto" + + stpb "github.com/golang/protobuf/ptypes/struct" +) + +const secondInNanos = int64(time.Second / time.Nanosecond) +const maxSecondsInDuration = 315576000000 + +// Marshaler is a configurable object for converting between +// protocol buffer objects and a JSON representation for them. +type Marshaler struct { + // Whether to render enum values as integers, as opposed to string values. + EnumsAsInts bool + + // Whether to render fields with zero values. + EmitDefaults bool + + // A string to indent each level by. The presence of this field will + // also cause a space to appear between the field separator and + // value, and for newlines to be appear between fields and array + // elements. + Indent string + + // Whether to use the original (.proto) name for fields. + OrigName bool + + // A custom URL resolver to use when marshaling Any messages to JSON. + // If unset, the default resolution strategy is to extract the + // fully-qualified type name from the type URL and pass that to + // proto.MessageType(string). + AnyResolver AnyResolver +} + +// AnyResolver takes a type URL, present in an Any message, and resolves it into +// an instance of the associated message. +type AnyResolver interface { + Resolve(typeUrl string) (proto.Message, error) +} + +func defaultResolveAny(typeUrl string) (proto.Message, error) { + // Only the part of typeUrl after the last slash is relevant. + mname := typeUrl + if slash := strings.LastIndex(mname, "/"); slash >= 0 { + mname = mname[slash+1:] + } + mt := proto.MessageType(mname) + if mt == nil { + return nil, fmt.Errorf("unknown message type %q", mname) + } + return reflect.New(mt.Elem()).Interface().(proto.Message), nil +} + +// JSONPBMarshaler is implemented by protobuf messages that customize the +// way they are marshaled to JSON. Messages that implement this should +// also implement JSONPBUnmarshaler so that the custom format can be +// parsed. +// +// The JSON marshaling must follow the proto to JSON specification: +// https://developers.google.com/protocol-buffers/docs/proto3#json +type JSONPBMarshaler interface { + MarshalJSONPB(*Marshaler) ([]byte, error) +} + +// JSONPBUnmarshaler is implemented by protobuf messages that customize +// the way they are unmarshaled from JSON. Messages that implement this +// should also implement JSONPBMarshaler so that the custom format can be +// produced. +// +// The JSON unmarshaling must follow the JSON to proto specification: +// https://developers.google.com/protocol-buffers/docs/proto3#json +type JSONPBUnmarshaler interface { + UnmarshalJSONPB(*Unmarshaler, []byte) error +} + +// Marshal marshals a protocol buffer into JSON. +func (m *Marshaler) Marshal(out io.Writer, pb proto.Message) error { + v := reflect.ValueOf(pb) + if pb == nil || (v.Kind() == reflect.Ptr && v.IsNil()) { + return errors.New("Marshal called with nil") + } + // Check for unset required fields first. + if err := checkRequiredFields(pb); err != nil { + return err + } + writer := &errWriter{writer: out} + return m.marshalObject(writer, pb, "", "") +} + +// MarshalToString converts a protocol buffer object to JSON string. +func (m *Marshaler) MarshalToString(pb proto.Message) (string, error) { + var buf bytes.Buffer + if err := m.Marshal(&buf, pb); err != nil { + return "", err + } + return buf.String(), nil +} + +type int32Slice []int32 + +var nonFinite = map[string]float64{ + `"NaN"`: math.NaN(), + `"Infinity"`: math.Inf(1), + `"-Infinity"`: math.Inf(-1), +} + +// For sorting extensions ids to ensure stable output. +func (s int32Slice) Len() int { return len(s) } +func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +type wkt interface { + XXX_WellKnownType() string +} + +var ( + wktType = reflect.TypeOf((*wkt)(nil)).Elem() + messageType = reflect.TypeOf((*proto.Message)(nil)).Elem() +) + +// marshalObject writes a struct to the Writer. +func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent, typeURL string) error { + if jsm, ok := v.(JSONPBMarshaler); ok { + b, err := jsm.MarshalJSONPB(m) + if err != nil { + return err + } + if typeURL != "" { + // we are marshaling this object to an Any type + var js map[string]*json.RawMessage + if err = json.Unmarshal(b, &js); err != nil { + return fmt.Errorf("type %T produced invalid JSON: %v", v, err) + } + turl, err := json.Marshal(typeURL) + if err != nil { + return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err) + } + js["@type"] = (*json.RawMessage)(&turl) + if m.Indent != "" { + b, err = json.MarshalIndent(js, indent, m.Indent) + } else { + b, err = json.Marshal(js) + } + if err != nil { + return err + } + } + + out.write(string(b)) + return out.err + } + + s := reflect.ValueOf(v).Elem() + + // Handle well-known types. + if wkt, ok := v.(wkt); ok { + switch wkt.XXX_WellKnownType() { + case "DoubleValue", "FloatValue", "Int64Value", "UInt64Value", + "Int32Value", "UInt32Value", "BoolValue", "StringValue", "BytesValue": + // "Wrappers use the same representation in JSON + // as the wrapped primitive type, ..." + sprop := proto.GetProperties(s.Type()) + return m.marshalValue(out, sprop.Prop[0], s.Field(0), indent) + case "Any": + // Any is a bit more involved. + return m.marshalAny(out, v, indent) + case "Duration": + s, ns := s.Field(0).Int(), s.Field(1).Int() + if s < -maxSecondsInDuration || s > maxSecondsInDuration { + return fmt.Errorf("seconds out of range %v", s) + } + if ns <= -secondInNanos || ns >= secondInNanos { + return fmt.Errorf("ns out of range (%v, %v)", -secondInNanos, secondInNanos) + } + if (s > 0 && ns < 0) || (s < 0 && ns > 0) { + return errors.New("signs of seconds and nanos do not match") + } + // Generated output always contains 0, 3, 6, or 9 fractional digits, + // depending on required precision, followed by the suffix "s". + f := "%d.%09d" + if ns < 0 { + ns = -ns + if s == 0 { + f = "-%d.%09d" + } + } + x := fmt.Sprintf(f, s, ns) + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, ".000") + out.write(`"`) + out.write(x) + out.write(`s"`) + return out.err + case "Struct", "ListValue": + // Let marshalValue handle the `Struct.fields` map or the `ListValue.values` slice. + // TODO: pass the correct Properties if needed. + return m.marshalValue(out, &proto.Properties{}, s.Field(0), indent) + case "Timestamp": + // "RFC 3339, where generated output will always be Z-normalized + // and uses 0, 3, 6 or 9 fractional digits." + s, ns := s.Field(0).Int(), s.Field(1).Int() + if ns < 0 || ns >= secondInNanos { + return fmt.Errorf("ns out of range [0, %v)", secondInNanos) + } + t := time.Unix(s, ns).UTC() + // time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits). + x := t.Format("2006-01-02T15:04:05.000000000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, ".000") + out.write(`"`) + out.write(x) + out.write(`Z"`) + return out.err + case "Value": + // Value has a single oneof. + kind := s.Field(0) + if kind.IsNil() { + // "absence of any variant indicates an error" + return errors.New("nil Value") + } + // oneof -> *T -> T -> T.F + x := kind.Elem().Elem().Field(0) + // TODO: pass the correct Properties if needed. + return m.marshalValue(out, &proto.Properties{}, x, indent) + } + } + + out.write("{") + if m.Indent != "" { + out.write("\n") + } + + firstField := true + + if typeURL != "" { + if err := m.marshalTypeURL(out, indent, typeURL); err != nil { + return err + } + firstField = false + } + + for i := 0; i < s.NumField(); i++ { + value := s.Field(i) + valueField := s.Type().Field(i) + if strings.HasPrefix(valueField.Name, "XXX_") { + continue + } + + // IsNil will panic on most value kinds. + switch value.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface: + if value.IsNil() { + continue + } + } + + if !m.EmitDefaults { + switch value.Kind() { + case reflect.Bool: + if !value.Bool() { + continue + } + case reflect.Int32, reflect.Int64: + if value.Int() == 0 { + continue + } + case reflect.Uint32, reflect.Uint64: + if value.Uint() == 0 { + continue + } + case reflect.Float32, reflect.Float64: + if value.Float() == 0 { + continue + } + case reflect.String: + if value.Len() == 0 { + continue + } + case reflect.Map, reflect.Ptr, reflect.Slice: + if value.IsNil() { + continue + } + } + } + + // Oneof fields need special handling. + if valueField.Tag.Get("protobuf_oneof") != "" { + // value is an interface containing &T{real_value}. + sv := value.Elem().Elem() // interface -> *T -> T + value = sv.Field(0) + valueField = sv.Type().Field(0) + } + prop := jsonProperties(valueField, m.OrigName) + if !firstField { + m.writeSep(out) + } + if err := m.marshalField(out, prop, value, indent); err != nil { + return err + } + firstField = false + } + + // Handle proto2 extensions. + if ep, ok := v.(proto.Message); ok { + extensions := proto.RegisteredExtensions(v) + // Sort extensions for stable output. + ids := make([]int32, 0, len(extensions)) + for id, desc := range extensions { + if !proto.HasExtension(ep, desc) { + continue + } + ids = append(ids, id) + } + sort.Sort(int32Slice(ids)) + for _, id := range ids { + desc := extensions[id] + if desc == nil { + // unknown extension + continue + } + ext, extErr := proto.GetExtension(ep, desc) + if extErr != nil { + return extErr + } + value := reflect.ValueOf(ext) + var prop proto.Properties + prop.Parse(desc.Tag) + prop.JSONName = fmt.Sprintf("[%s]", desc.Name) + if !firstField { + m.writeSep(out) + } + if err := m.marshalField(out, &prop, value, indent); err != nil { + return err + } + firstField = false + } + + } + + if m.Indent != "" { + out.write("\n") + out.write(indent) + } + out.write("}") + return out.err +} + +func (m *Marshaler) writeSep(out *errWriter) { + if m.Indent != "" { + out.write(",\n") + } else { + out.write(",") + } +} + +func (m *Marshaler) marshalAny(out *errWriter, any proto.Message, indent string) error { + // "If the Any contains a value that has a special JSON mapping, + // it will be converted as follows: {"@type": xxx, "value": yyy}. + // Otherwise, the value will be converted into a JSON object, + // and the "@type" field will be inserted to indicate the actual data type." + v := reflect.ValueOf(any).Elem() + turl := v.Field(0).String() + val := v.Field(1).Bytes() + + var msg proto.Message + var err error + if m.AnyResolver != nil { + msg, err = m.AnyResolver.Resolve(turl) + } else { + msg, err = defaultResolveAny(turl) + } + if err != nil { + return err + } + + if err := proto.Unmarshal(val, msg); err != nil { + return err + } + + if _, ok := msg.(wkt); ok { + out.write("{") + if m.Indent != "" { + out.write("\n") + } + if err := m.marshalTypeURL(out, indent, turl); err != nil { + return err + } + m.writeSep(out) + if m.Indent != "" { + out.write(indent) + out.write(m.Indent) + out.write(`"value": `) + } else { + out.write(`"value":`) + } + if err := m.marshalObject(out, msg, indent+m.Indent, ""); err != nil { + return err + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + } + out.write("}") + return out.err + } + + return m.marshalObject(out, msg, indent, turl) +} + +func (m *Marshaler) marshalTypeURL(out *errWriter, indent, typeURL string) error { + if m.Indent != "" { + out.write(indent) + out.write(m.Indent) + } + out.write(`"@type":`) + if m.Indent != "" { + out.write(" ") + } + b, err := json.Marshal(typeURL) + if err != nil { + return err + } + out.write(string(b)) + return out.err +} + +// marshalField writes field description and value to the Writer. +func (m *Marshaler) marshalField(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error { + if m.Indent != "" { + out.write(indent) + out.write(m.Indent) + } + out.write(`"`) + out.write(prop.JSONName) + out.write(`":`) + if m.Indent != "" { + out.write(" ") + } + if err := m.marshalValue(out, prop, v, indent); err != nil { + return err + } + return nil +} + +// marshalValue writes the value to the Writer. +func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error { + var err error + v = reflect.Indirect(v) + + // Handle nil pointer + if v.Kind() == reflect.Invalid { + out.write("null") + return out.err + } + + // Handle repeated elements. + if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 { + out.write("[") + comma := "" + for i := 0; i < v.Len(); i++ { + sliceVal := v.Index(i) + out.write(comma) + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + out.write(m.Indent) + } + if err := m.marshalValue(out, prop, sliceVal, indent+m.Indent); err != nil { + return err + } + comma = "," + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + } + out.write("]") + return out.err + } + + // Handle well-known types. + // Most are handled up in marshalObject (because 99% are messages). + if v.Type().Implements(wktType) { + wkt := v.Interface().(wkt) + switch wkt.XXX_WellKnownType() { + case "NullValue": + out.write("null") + return out.err + } + } + + // Handle enumerations. + if !m.EnumsAsInts && prop.Enum != "" { + // Unknown enum values will are stringified by the proto library as their + // value. Such values should _not_ be quoted or they will be interpreted + // as an enum string instead of their value. + enumStr := v.Interface().(fmt.Stringer).String() + var valStr string + if v.Kind() == reflect.Ptr { + valStr = strconv.Itoa(int(v.Elem().Int())) + } else { + valStr = strconv.Itoa(int(v.Int())) + } + isKnownEnum := enumStr != valStr + if isKnownEnum { + out.write(`"`) + } + out.write(enumStr) + if isKnownEnum { + out.write(`"`) + } + return out.err + } + + // Handle nested messages. + if v.Kind() == reflect.Struct { + return m.marshalObject(out, v.Addr().Interface().(proto.Message), indent+m.Indent, "") + } + + // Handle maps. + // Since Go randomizes map iteration, we sort keys for stable output. + if v.Kind() == reflect.Map { + out.write(`{`) + keys := v.MapKeys() + sort.Sort(mapKeys(keys)) + for i, k := range keys { + if i > 0 { + out.write(`,`) + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + out.write(m.Indent) + } + + // TODO handle map key prop properly + b, err := json.Marshal(k.Interface()) + if err != nil { + return err + } + s := string(b) + + // If the JSON is not a string value, encode it again to make it one. + if !strings.HasPrefix(s, `"`) { + b, err := json.Marshal(s) + if err != nil { + return err + } + s = string(b) + } + + out.write(s) + out.write(`:`) + if m.Indent != "" { + out.write(` `) + } + + vprop := prop + if prop != nil && prop.MapValProp != nil { + vprop = prop.MapValProp + } + if err := m.marshalValue(out, vprop, v.MapIndex(k), indent+m.Indent); err != nil { + return err + } + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + } + out.write(`}`) + return out.err + } + + // Handle non-finite floats, e.g. NaN, Infinity and -Infinity. + if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 { + f := v.Float() + var sval string + switch { + case math.IsInf(f, 1): + sval = `"Infinity"` + case math.IsInf(f, -1): + sval = `"-Infinity"` + case math.IsNaN(f): + sval = `"NaN"` + } + if sval != "" { + out.write(sval) + return out.err + } + } + + // Default handling defers to the encoding/json library. + b, err := json.Marshal(v.Interface()) + if err != nil { + return err + } + needToQuote := string(b[0]) != `"` && (v.Kind() == reflect.Int64 || v.Kind() == reflect.Uint64) + if needToQuote { + out.write(`"`) + } + out.write(string(b)) + if needToQuote { + out.write(`"`) + } + return out.err +} + +// Unmarshaler is a configurable object for converting from a JSON +// representation to a protocol buffer object. +type Unmarshaler struct { + // Whether to allow messages to contain unknown fields, as opposed to + // failing to unmarshal. + AllowUnknownFields bool + + // A custom URL resolver to use when unmarshaling Any messages from JSON. + // If unset, the default resolution strategy is to extract the + // fully-qualified type name from the type URL and pass that to + // proto.MessageType(string). + AnyResolver AnyResolver +} + +// UnmarshalNext unmarshals the next protocol buffer from a JSON object stream. +// This function is lenient and will decode any options permutations of the +// related Marshaler. +func (u *Unmarshaler) UnmarshalNext(dec *json.Decoder, pb proto.Message) error { + inputValue := json.RawMessage{} + if err := dec.Decode(&inputValue); err != nil { + return err + } + if err := u.unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue, nil); err != nil { + return err + } + return checkRequiredFields(pb) +} + +// Unmarshal unmarshals a JSON object stream into a protocol +// buffer. This function is lenient and will decode any options +// permutations of the related Marshaler. +func (u *Unmarshaler) Unmarshal(r io.Reader, pb proto.Message) error { + dec := json.NewDecoder(r) + return u.UnmarshalNext(dec, pb) +} + +// UnmarshalNext unmarshals the next protocol buffer from a JSON object stream. +// This function is lenient and will decode any options permutations of the +// related Marshaler. +func UnmarshalNext(dec *json.Decoder, pb proto.Message) error { + return new(Unmarshaler).UnmarshalNext(dec, pb) +} + +// Unmarshal unmarshals a JSON object stream into a protocol +// buffer. This function is lenient and will decode any options +// permutations of the related Marshaler. +func Unmarshal(r io.Reader, pb proto.Message) error { + return new(Unmarshaler).Unmarshal(r, pb) +} + +// UnmarshalString will populate the fields of a protocol buffer based +// on a JSON string. This function is lenient and will decode any options +// permutations of the related Marshaler. +func UnmarshalString(str string, pb proto.Message) error { + return new(Unmarshaler).Unmarshal(strings.NewReader(str), pb) +} + +// unmarshalValue converts/copies a value into the target. +// prop may be nil. +func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *proto.Properties) error { + targetType := target.Type() + + // Allocate memory for pointer fields. + if targetType.Kind() == reflect.Ptr { + // If input value is "null" and target is a pointer type, then the field should be treated as not set + // UNLESS the target is structpb.Value, in which case it should be set to structpb.NullValue. + _, isJSONPBUnmarshaler := target.Interface().(JSONPBUnmarshaler) + if string(inputValue) == "null" && targetType != reflect.TypeOf(&stpb.Value{}) && !isJSONPBUnmarshaler { + return nil + } + target.Set(reflect.New(targetType.Elem())) + + return u.unmarshalValue(target.Elem(), inputValue, prop) + } + + if jsu, ok := target.Addr().Interface().(JSONPBUnmarshaler); ok { + return jsu.UnmarshalJSONPB(u, []byte(inputValue)) + } + + // Handle well-known types that are not pointers. + if w, ok := target.Addr().Interface().(wkt); ok { + switch w.XXX_WellKnownType() { + case "DoubleValue", "FloatValue", "Int64Value", "UInt64Value", + "Int32Value", "UInt32Value", "BoolValue", "StringValue", "BytesValue": + return u.unmarshalValue(target.Field(0), inputValue, prop) + case "Any": + // Use json.RawMessage pointer type instead of value to support pre-1.8 version. + // 1.8 changed RawMessage.MarshalJSON from pointer type to value type, see + // https://github.com/golang/go/issues/14493 + var jsonFields map[string]*json.RawMessage + if err := json.Unmarshal(inputValue, &jsonFields); err != nil { + return err + } + + val, ok := jsonFields["@type"] + if !ok || val == nil { + return errors.New("Any JSON doesn't have '@type'") + } + + var turl string + if err := json.Unmarshal([]byte(*val), &turl); err != nil { + return fmt.Errorf("can't unmarshal Any's '@type': %q", *val) + } + target.Field(0).SetString(turl) + + var m proto.Message + var err error + if u.AnyResolver != nil { + m, err = u.AnyResolver.Resolve(turl) + } else { + m, err = defaultResolveAny(turl) + } + if err != nil { + return err + } + + if _, ok := m.(wkt); ok { + val, ok := jsonFields["value"] + if !ok { + return errors.New("Any JSON doesn't have 'value'") + } + + if err := u.unmarshalValue(reflect.ValueOf(m).Elem(), *val, nil); err != nil { + return fmt.Errorf("can't unmarshal Any nested proto %T: %v", m, err) + } + } else { + delete(jsonFields, "@type") + nestedProto, err := json.Marshal(jsonFields) + if err != nil { + return fmt.Errorf("can't generate JSON for Any's nested proto to be unmarshaled: %v", err) + } + + if err = u.unmarshalValue(reflect.ValueOf(m).Elem(), nestedProto, nil); err != nil { + return fmt.Errorf("can't unmarshal Any nested proto %T: %v", m, err) + } + } + + b, err := proto.Marshal(m) + if err != nil { + return fmt.Errorf("can't marshal proto %T into Any.Value: %v", m, err) + } + target.Field(1).SetBytes(b) + + return nil + case "Duration": + unq, err := unquote(string(inputValue)) + if err != nil { + return err + } + + d, err := time.ParseDuration(unq) + if err != nil { + return fmt.Errorf("bad Duration: %v", err) + } + + ns := d.Nanoseconds() + s := ns / 1e9 + ns %= 1e9 + target.Field(0).SetInt(s) + target.Field(1).SetInt(ns) + return nil + case "Timestamp": + unq, err := unquote(string(inputValue)) + if err != nil { + return err + } + + t, err := time.Parse(time.RFC3339Nano, unq) + if err != nil { + return fmt.Errorf("bad Timestamp: %v", err) + } + + target.Field(0).SetInt(t.Unix()) + target.Field(1).SetInt(int64(t.Nanosecond())) + return nil + case "Struct": + var m map[string]json.RawMessage + if err := json.Unmarshal(inputValue, &m); err != nil { + return fmt.Errorf("bad StructValue: %v", err) + } + + target.Field(0).Set(reflect.ValueOf(map[string]*stpb.Value{})) + for k, jv := range m { + pv := &stpb.Value{} + if err := u.unmarshalValue(reflect.ValueOf(pv).Elem(), jv, prop); err != nil { + return fmt.Errorf("bad value in StructValue for key %q: %v", k, err) + } + target.Field(0).SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(pv)) + } + return nil + case "ListValue": + var s []json.RawMessage + if err := json.Unmarshal(inputValue, &s); err != nil { + return fmt.Errorf("bad ListValue: %v", err) + } + + target.Field(0).Set(reflect.ValueOf(make([]*stpb.Value, len(s)))) + for i, sv := range s { + if err := u.unmarshalValue(target.Field(0).Index(i), sv, prop); err != nil { + return err + } + } + return nil + case "Value": + ivStr := string(inputValue) + if ivStr == "null" { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_NullValue{})) + } else if v, err := strconv.ParseFloat(ivStr, 0); err == nil { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_NumberValue{v})) + } else if v, err := unquote(ivStr); err == nil { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_StringValue{v})) + } else if v, err := strconv.ParseBool(ivStr); err == nil { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_BoolValue{v})) + } else if err := json.Unmarshal(inputValue, &[]json.RawMessage{}); err == nil { + lv := &stpb.ListValue{} + target.Field(0).Set(reflect.ValueOf(&stpb.Value_ListValue{lv})) + return u.unmarshalValue(reflect.ValueOf(lv).Elem(), inputValue, prop) + } else if err := json.Unmarshal(inputValue, &map[string]json.RawMessage{}); err == nil { + sv := &stpb.Struct{} + target.Field(0).Set(reflect.ValueOf(&stpb.Value_StructValue{sv})) + return u.unmarshalValue(reflect.ValueOf(sv).Elem(), inputValue, prop) + } else { + return fmt.Errorf("unrecognized type for Value %q", ivStr) + } + return nil + } + } + + // Handle enums, which have an underlying type of int32, + // and may appear as strings. + // The case of an enum appearing as a number is handled + // at the bottom of this function. + if inputValue[0] == '"' && prop != nil && prop.Enum != "" { + vmap := proto.EnumValueMap(prop.Enum) + // Don't need to do unquoting; valid enum names + // are from a limited character set. + s := inputValue[1 : len(inputValue)-1] + n, ok := vmap[string(s)] + if !ok { + return fmt.Errorf("unknown value %q for enum %s", s, prop.Enum) + } + if target.Kind() == reflect.Ptr { // proto2 + target.Set(reflect.New(targetType.Elem())) + target = target.Elem() + } + if targetType.Kind() != reflect.Int32 { + return fmt.Errorf("invalid target %q for enum %s", targetType.Kind(), prop.Enum) + } + target.SetInt(int64(n)) + return nil + } + + // Handle nested messages. + if targetType.Kind() == reflect.Struct { + var jsonFields map[string]json.RawMessage + if err := json.Unmarshal(inputValue, &jsonFields); err != nil { + return err + } + + consumeField := func(prop *proto.Properties) (json.RawMessage, bool) { + // Be liberal in what names we accept; both orig_name and camelName are okay. + fieldNames := acceptedJSONFieldNames(prop) + + vOrig, okOrig := jsonFields[fieldNames.orig] + vCamel, okCamel := jsonFields[fieldNames.camel] + if !okOrig && !okCamel { + return nil, false + } + // If, for some reason, both are present in the data, favour the camelName. + var raw json.RawMessage + if okOrig { + raw = vOrig + delete(jsonFields, fieldNames.orig) + } + if okCamel { + raw = vCamel + delete(jsonFields, fieldNames.camel) + } + return raw, true + } + + sprops := proto.GetProperties(targetType) + for i := 0; i < target.NumField(); i++ { + ft := target.Type().Field(i) + if strings.HasPrefix(ft.Name, "XXX_") { + continue + } + + valueForField, ok := consumeField(sprops.Prop[i]) + if !ok { + continue + } + + if err := u.unmarshalValue(target.Field(i), valueForField, sprops.Prop[i]); err != nil { + return err + } + } + // Check for any oneof fields. + if len(jsonFields) > 0 { + for _, oop := range sprops.OneofTypes { + raw, ok := consumeField(oop.Prop) + if !ok { + continue + } + nv := reflect.New(oop.Type.Elem()) + target.Field(oop.Field).Set(nv) + if err := u.unmarshalValue(nv.Elem().Field(0), raw, oop.Prop); err != nil { + return err + } + } + } + // Handle proto2 extensions. + if len(jsonFields) > 0 { + if ep, ok := target.Addr().Interface().(proto.Message); ok { + for _, ext := range proto.RegisteredExtensions(ep) { + name := fmt.Sprintf("[%s]", ext.Name) + raw, ok := jsonFields[name] + if !ok { + continue + } + delete(jsonFields, name) + nv := reflect.New(reflect.TypeOf(ext.ExtensionType).Elem()) + if err := u.unmarshalValue(nv.Elem(), raw, nil); err != nil { + return err + } + if err := proto.SetExtension(ep, ext, nv.Interface()); err != nil { + return err + } + } + } + } + if !u.AllowUnknownFields && len(jsonFields) > 0 { + // Pick any field to be the scapegoat. + var f string + for fname := range jsonFields { + f = fname + break + } + return fmt.Errorf("unknown field %q in %v", f, targetType) + } + return nil + } + + // Handle arrays (which aren't encoded bytes) + if targetType.Kind() == reflect.Slice && targetType.Elem().Kind() != reflect.Uint8 { + var slc []json.RawMessage + if err := json.Unmarshal(inputValue, &slc); err != nil { + return err + } + if slc != nil { + l := len(slc) + target.Set(reflect.MakeSlice(targetType, l, l)) + for i := 0; i < l; i++ { + if err := u.unmarshalValue(target.Index(i), slc[i], prop); err != nil { + return err + } + } + } + return nil + } + + // Handle maps (whose keys are always strings) + if targetType.Kind() == reflect.Map { + var mp map[string]json.RawMessage + if err := json.Unmarshal(inputValue, &mp); err != nil { + return err + } + if mp != nil { + target.Set(reflect.MakeMap(targetType)) + for ks, raw := range mp { + // Unmarshal map key. The core json library already decoded the key into a + // string, so we handle that specially. Other types were quoted post-serialization. + var k reflect.Value + if targetType.Key().Kind() == reflect.String { + k = reflect.ValueOf(ks) + } else { + k = reflect.New(targetType.Key()).Elem() + var kprop *proto.Properties + if prop != nil && prop.MapKeyProp != nil { + kprop = prop.MapKeyProp + } + if err := u.unmarshalValue(k, json.RawMessage(ks), kprop); err != nil { + return err + } + } + + // Unmarshal map value. + v := reflect.New(targetType.Elem()).Elem() + var vprop *proto.Properties + if prop != nil && prop.MapValProp != nil { + vprop = prop.MapValProp + } + if err := u.unmarshalValue(v, raw, vprop); err != nil { + return err + } + target.SetMapIndex(k, v) + } + } + return nil + } + + // Non-finite numbers can be encoded as strings. + isFloat := targetType.Kind() == reflect.Float32 || targetType.Kind() == reflect.Float64 + if isFloat { + if num, ok := nonFinite[string(inputValue)]; ok { + target.SetFloat(num) + return nil + } + } + + // integers & floats can be encoded as strings. In this case we drop + // the quotes and proceed as normal. + isNum := targetType.Kind() == reflect.Int64 || targetType.Kind() == reflect.Uint64 || + targetType.Kind() == reflect.Int32 || targetType.Kind() == reflect.Uint32 || + targetType.Kind() == reflect.Float32 || targetType.Kind() == reflect.Float64 + if isNum && strings.HasPrefix(string(inputValue), `"`) { + inputValue = inputValue[1 : len(inputValue)-1] + } + + // Use the encoding/json for parsing other value types. + return json.Unmarshal(inputValue, target.Addr().Interface()) +} + +func unquote(s string) (string, error) { + var ret string + err := json.Unmarshal([]byte(s), &ret) + return ret, err +} + +// jsonProperties returns parsed proto.Properties for the field and corrects JSONName attribute. +func jsonProperties(f reflect.StructField, origName bool) *proto.Properties { + var prop proto.Properties + prop.Init(f.Type, f.Name, f.Tag.Get("protobuf"), &f) + if origName || prop.JSONName == "" { + prop.JSONName = prop.OrigName + } + return &prop +} + +type fieldNames struct { + orig, camel string +} + +func acceptedJSONFieldNames(prop *proto.Properties) fieldNames { + opts := fieldNames{orig: prop.OrigName, camel: prop.OrigName} + if prop.JSONName != "" { + opts.camel = prop.JSONName + } + return opts +} + +// Writer wrapper inspired by https://blog.golang.org/errors-are-values +type errWriter struct { + writer io.Writer + err error +} + +func (w *errWriter) write(str string) { + if w.err != nil { + return + } + _, w.err = w.writer.Write([]byte(str)) +} + +// Map fields may have key types of non-float scalars, strings and enums. +// The easiest way to sort them in some deterministic order is to use fmt. +// If this turns out to be inefficient we can always consider other options, +// such as doing a Schwartzian transform. +// +// Numeric keys are sorted in numeric order per +// https://developers.google.com/protocol-buffers/docs/proto#maps. +type mapKeys []reflect.Value + +func (s mapKeys) Len() int { return len(s) } +func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s mapKeys) Less(i, j int) bool { + if k := s[i].Kind(); k == s[j].Kind() { + switch k { + case reflect.String: + return s[i].String() < s[j].String() + case reflect.Int32, reflect.Int64: + return s[i].Int() < s[j].Int() + case reflect.Uint32, reflect.Uint64: + return s[i].Uint() < s[j].Uint() + } + } + return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface()) +} + +// checkRequiredFields returns an error if any required field in the given proto message is not set. +// This function is used by both Marshal and Unmarshal. While required fields only exist in a +// proto2 message, a proto3 message can contain proto2 message(s). +func checkRequiredFields(pb proto.Message) error { + // Most well-known type messages do not contain required fields. The "Any" type may contain + // a message that has required fields. + // + // When an Any message is being marshaled, the code will invoked proto.Unmarshal on Any.Value + // field in order to transform that into JSON, and that should have returned an error if a + // required field is not set in the embedded message. + // + // When an Any message is being unmarshaled, the code will have invoked proto.Marshal on the + // embedded message to store the serialized message in Any.Value field, and that should have + // returned an error if a required field is not set. + if _, ok := pb.(wkt); ok { + return nil + } + + v := reflect.ValueOf(pb) + // Skip message if it is not a struct pointer. + if v.Kind() != reflect.Ptr { + return nil + } + v = v.Elem() + if v.Kind() != reflect.Struct { + return nil + } + + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + sfield := v.Type().Field(i) + + if sfield.PkgPath != "" { + // blank PkgPath means the field is exported; skip if not exported + continue + } + + if strings.HasPrefix(sfield.Name, "XXX_") { + continue + } + + // Oneof field is an interface implemented by wrapper structs containing the actual oneof + // field, i.e. an interface containing &T{real_value}. + if sfield.Tag.Get("protobuf_oneof") != "" { + if field.Kind() != reflect.Interface { + continue + } + v := field.Elem() + if v.Kind() != reflect.Ptr || v.IsNil() { + continue + } + v = v.Elem() + if v.Kind() != reflect.Struct || v.NumField() < 1 { + continue + } + field = v.Field(0) + sfield = v.Type().Field(0) + } + + protoTag := sfield.Tag.Get("protobuf") + if protoTag == "" { + continue + } + var prop proto.Properties + prop.Init(sfield.Type, sfield.Name, protoTag, &sfield) + + switch field.Kind() { + case reflect.Map: + if field.IsNil() { + continue + } + // Check each map value. + keys := field.MapKeys() + for _, k := range keys { + v := field.MapIndex(k) + if err := checkRequiredFieldsInValue(v); err != nil { + return err + } + } + case reflect.Slice: + // Handle non-repeated type, e.g. bytes. + if !prop.Repeated { + if prop.Required && field.IsNil() { + return fmt.Errorf("required field %q is not set", prop.Name) + } + continue + } + + // Handle repeated type. + if field.IsNil() { + continue + } + // Check each slice item. + for i := 0; i < field.Len(); i++ { + v := field.Index(i) + if err := checkRequiredFieldsInValue(v); err != nil { + return err + } + } + case reflect.Ptr: + if field.IsNil() { + if prop.Required { + return fmt.Errorf("required field %q is not set", prop.Name) + } + continue + } + if err := checkRequiredFieldsInValue(field); err != nil { + return err + } + } + } + + // Handle proto2 extensions. + for _, ext := range proto.RegisteredExtensions(pb) { + if !proto.HasExtension(pb, ext) { + continue + } + ep, err := proto.GetExtension(pb, ext) + if err != nil { + return err + } + err = checkRequiredFieldsInValue(reflect.ValueOf(ep)) + if err != nil { + return err + } + } + + return nil +} + +func checkRequiredFieldsInValue(v reflect.Value) error { + if v.Type().Implements(messageType) { + return checkRequiredFields(v.Interface().(proto.Message)) + } + return nil +} diff --git a/vendor/github.com/golang/protobuf/proto/decode.go b/vendor/github.com/golang/protobuf/proto/decode.go index d9aa3c42d666e..63b0f08bef2a0 100644 --- a/vendor/github.com/golang/protobuf/proto/decode.go +++ b/vendor/github.com/golang/protobuf/proto/decode.go @@ -186,7 +186,6 @@ func (p *Buffer) DecodeVarint() (x uint64, err error) { if b&0x80 == 0 { goto done } - // x -= 0x80 << 63 // Always zero. return 0, errOverflow diff --git a/vendor/github.com/golang/protobuf/proto/deprecated.go b/vendor/github.com/golang/protobuf/proto/deprecated.go new file mode 100644 index 0000000000000..35b882c09aaf9 --- /dev/null +++ b/vendor/github.com/golang/protobuf/proto/deprecated.go @@ -0,0 +1,63 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2018 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +import "errors" + +// Deprecated: do not use. +type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 } + +// Deprecated: do not use. +func GetStats() Stats { return Stats{} } + +// Deprecated: do not use. +func MarshalMessageSet(interface{}) ([]byte, error) { + return nil, errors.New("proto: not implemented") +} + +// Deprecated: do not use. +func UnmarshalMessageSet([]byte, interface{}) error { + return errors.New("proto: not implemented") +} + +// Deprecated: do not use. +func MarshalMessageSetJSON(interface{}) ([]byte, error) { + return nil, errors.New("proto: not implemented") +} + +// Deprecated: do not use. +func UnmarshalMessageSetJSON([]byte, interface{}) error { + return errors.New("proto: not implemented") +} + +// Deprecated: do not use. +func RegisterMessageSetType(Message, int32, string) {} diff --git a/vendor/github.com/golang/protobuf/proto/encode.go b/vendor/github.com/golang/protobuf/proto/encode.go index c27d35f866bb0..3abfed2cff04b 100644 --- a/vendor/github.com/golang/protobuf/proto/encode.go +++ b/vendor/github.com/golang/protobuf/proto/encode.go @@ -37,27 +37,9 @@ package proto import ( "errors" - "fmt" "reflect" ) -// RequiredNotSetError is the error returned if Marshal is called with -// a protocol buffer struct whose required fields have not -// all been initialized. It is also the error returned if Unmarshal is -// called with an encoded protocol buffer that does not include all the -// required fields. -// -// When printed, RequiredNotSetError reports the first unset required field in a -// message. If the field cannot be precisely determined, it is reported as -// "{Unknown}". -type RequiredNotSetError struct { - field string -} - -func (e *RequiredNotSetError) Error() string { - return fmt.Sprintf("proto: required field %q not set", e.field) -} - var ( // errRepeatedHasNil is the error returned if Marshal is called with // a struct with a repeated field containing a nil element. diff --git a/vendor/github.com/golang/protobuf/proto/equal.go b/vendor/github.com/golang/protobuf/proto/equal.go index d4db5a1c14577..f9b6e41b3c108 100644 --- a/vendor/github.com/golang/protobuf/proto/equal.go +++ b/vendor/github.com/golang/protobuf/proto/equal.go @@ -246,7 +246,8 @@ func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool { return false } - m1, m2 := e1.value, e2.value + m1 := extensionAsLegacyType(e1.value) + m2 := extensionAsLegacyType(e2.value) if m1 == nil && m2 == nil { // Both have only encoded form. diff --git a/vendor/github.com/golang/protobuf/proto/extensions.go b/vendor/github.com/golang/protobuf/proto/extensions.go index 816a3b9d6c09f..fa88add30a41f 100644 --- a/vendor/github.com/golang/protobuf/proto/extensions.go +++ b/vendor/github.com/golang/protobuf/proto/extensions.go @@ -185,9 +185,25 @@ type Extension struct { // extension will have only enc set. When such an extension is // accessed using GetExtension (or GetExtensions) desc and value // will be set. - desc *ExtensionDesc + desc *ExtensionDesc + + // value is a concrete value for the extension field. Let the type of + // desc.ExtensionType be the "API type" and the type of Extension.value + // be the "storage type". The API type and storage type are the same except: + // * For scalars (except []byte), the API type uses *T, + // while the storage type uses T. + // * For repeated fields, the API type uses []T, while the storage type + // uses *[]T. + // + // The reason for the divergence is so that the storage type more naturally + // matches what is expected of when retrieving the values through the + // protobuf reflection APIs. + // + // The value may only be populated if desc is also populated. value interface{} - enc []byte + + // enc is the raw bytes for the extension field. + enc []byte } // SetRawExtension is for testing only. @@ -334,7 +350,7 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) { // descriptors with the same field number. return nil, errors.New("proto: descriptor conflict") } - return e.value, nil + return extensionAsLegacyType(e.value), nil } if extension.ExtensionType == nil { @@ -349,11 +365,11 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) { // Remember the decoded version and drop the encoded version. // That way it is safe to mutate what we return. - e.value = v + e.value = extensionAsStorageType(v) e.desc = extension e.enc = nil emap[extension.Field] = e - return e.value, nil + return extensionAsLegacyType(e.value), nil } // defaultExtensionValue returns the default value for extension. @@ -488,7 +504,7 @@ func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error } typ := reflect.TypeOf(extension.ExtensionType) if typ != reflect.TypeOf(value) { - return errors.New("proto: bad extension value type") + return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", value, extension.ExtensionType) } // nil extension values need to be caught early, because the // encoder can't distinguish an ErrNil due to a nil extension @@ -500,7 +516,7 @@ func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error } extmap := epb.extensionsWrite() - extmap[extension.Field] = Extension{desc: extension, value: value} + extmap[extension.Field] = Extension{desc: extension, value: extensionAsStorageType(value)} return nil } @@ -541,3 +557,51 @@ func RegisterExtension(desc *ExtensionDesc) { func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc { return extensionMaps[reflect.TypeOf(pb).Elem()] } + +// extensionAsLegacyType converts an value in the storage type as the API type. +// See Extension.value. +func extensionAsLegacyType(v interface{}) interface{} { + switch rv := reflect.ValueOf(v); rv.Kind() { + case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String: + // Represent primitive types as a pointer to the value. + rv2 := reflect.New(rv.Type()) + rv2.Elem().Set(rv) + v = rv2.Interface() + case reflect.Ptr: + // Represent slice types as the value itself. + switch rv.Type().Elem().Kind() { + case reflect.Slice: + if rv.IsNil() { + v = reflect.Zero(rv.Type().Elem()).Interface() + } else { + v = rv.Elem().Interface() + } + } + } + return v +} + +// extensionAsStorageType converts an value in the API type as the storage type. +// See Extension.value. +func extensionAsStorageType(v interface{}) interface{} { + switch rv := reflect.ValueOf(v); rv.Kind() { + case reflect.Ptr: + // Represent slice types as the value itself. + switch rv.Type().Elem().Kind() { + case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String: + if rv.IsNil() { + v = reflect.Zero(rv.Type().Elem()).Interface() + } else { + v = rv.Elem().Interface() + } + } + case reflect.Slice: + // Represent slice types as a pointer to the value. + if rv.Type().Elem().Kind() != reflect.Uint8 { + rv2 := reflect.New(rv.Type()) + rv2.Elem().Set(rv) + v = rv2.Interface() + } + } + return v +} diff --git a/vendor/github.com/golang/protobuf/proto/lib.go b/vendor/github.com/golang/protobuf/proto/lib.go index 0e2191b8ada36..70fbda5329cb3 100644 --- a/vendor/github.com/golang/protobuf/proto/lib.go +++ b/vendor/github.com/golang/protobuf/proto/lib.go @@ -265,7 +265,6 @@ package proto import ( "encoding/json" - "errors" "fmt" "log" "reflect" @@ -274,34 +273,73 @@ import ( "sync" ) -var errInvalidUTF8 = errors.New("proto: invalid UTF-8 string") +// RequiredNotSetError is an error type returned by either Marshal or Unmarshal. +// Marshal reports this when a required field is not initialized. +// Unmarshal reports this when a required field is missing from the wire data. +type RequiredNotSetError struct{ field string } -// Message is implemented by generated protocol buffer messages. -type Message interface { - Reset() - String() string - ProtoMessage() +func (e *RequiredNotSetError) Error() string { + if e.field == "" { + return fmt.Sprintf("proto: required field not set") + } + return fmt.Sprintf("proto: required field %q not set", e.field) +} +func (e *RequiredNotSetError) RequiredNotSet() bool { + return true } -// Stats records allocation details about the protocol buffer encoders -// and decoders. Useful for tuning the library itself. -type Stats struct { - Emalloc uint64 // mallocs in encode - Dmalloc uint64 // mallocs in decode - Encode uint64 // number of encodes - Decode uint64 // number of decodes - Chit uint64 // number of cache hits - Cmiss uint64 // number of cache misses - Size uint64 // number of sizes +type invalidUTF8Error struct{ field string } + +func (e *invalidUTF8Error) Error() string { + if e.field == "" { + return "proto: invalid UTF-8 detected" + } + return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field) +} +func (e *invalidUTF8Error) InvalidUTF8() bool { + return true +} + +// errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8. +// This error should not be exposed to the external API as such errors should +// be recreated with the field information. +var errInvalidUTF8 = &invalidUTF8Error{} + +// isNonFatal reports whether the error is either a RequiredNotSet error +// or a InvalidUTF8 error. +func isNonFatal(err error) bool { + if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() { + return true + } + if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() { + return true + } + return false } -// Set to true to enable stats collection. -const collectStats = false +type nonFatal struct{ E error } -var stats Stats +// Merge merges err into nf and reports whether it was successful. +// Otherwise it returns false for any fatal non-nil errors. +func (nf *nonFatal) Merge(err error) (ok bool) { + if err == nil { + return true // not an error + } + if !isNonFatal(err) { + return false // fatal error + } + if nf.E == nil { + nf.E = err // store first instance of non-fatal error + } + return true +} -// GetStats returns a copy of the global Stats structure. -func GetStats() Stats { return stats } +// Message is implemented by generated protocol buffer messages. +type Message interface { + Reset() + String() string + ProtoMessage() +} // A Buffer is a buffer manager for marshaling and unmarshaling // protocol buffers. It may be reused between invocations to @@ -355,7 +393,7 @@ func (p *Buffer) Bytes() []byte { return p.buf } // than relying on this API. // // If deterministic serialization is requested, map entries will be sorted -// by keys in lexographical order. This is an implementation detail and +// by keys in lexicographical order. This is an implementation detail and // subject to change. func (p *Buffer) SetDeterministic(deterministic bool) { p.deterministic = deterministic @@ -902,13 +940,19 @@ func isProto3Zero(v reflect.Value) bool { return false } -// ProtoPackageIsVersion2 is referenced from generated protocol buffer files -// to assert that that code is compatible with this version of the proto package. -const ProtoPackageIsVersion2 = true +const ( + // ProtoPackageIsVersion3 is referenced from generated protocol buffer files + // to assert that that code is compatible with this version of the proto package. + ProtoPackageIsVersion3 = true + + // ProtoPackageIsVersion2 is referenced from generated protocol buffer files + // to assert that that code is compatible with this version of the proto package. + ProtoPackageIsVersion2 = true -// ProtoPackageIsVersion1 is referenced from generated protocol buffer files -// to assert that that code is compatible with this version of the proto package. -const ProtoPackageIsVersion1 = true + // ProtoPackageIsVersion1 is referenced from generated protocol buffer files + // to assert that that code is compatible with this version of the proto package. + ProtoPackageIsVersion1 = true +) // InternalMessageInfo is a type used internally by generated .pb.go files. // This type is not intended to be used by non-generated code. diff --git a/vendor/github.com/golang/protobuf/proto/message_set.go b/vendor/github.com/golang/protobuf/proto/message_set.go index 3b6ca41d5e554..f48a756761ead 100644 --- a/vendor/github.com/golang/protobuf/proto/message_set.go +++ b/vendor/github.com/golang/protobuf/proto/message_set.go @@ -36,13 +36,7 @@ package proto */ import ( - "bytes" - "encoding/json" "errors" - "fmt" - "reflect" - "sort" - "sync" ) // errNoMessageTypeID occurs when a protocol buffer does not have a message type ID. @@ -145,46 +139,9 @@ func skipVarint(buf []byte) []byte { return buf[i+1:] } -// MarshalMessageSet encodes the extension map represented by m in the message set wire format. -// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option. -func MarshalMessageSet(exts interface{}) ([]byte, error) { - return marshalMessageSet(exts, false) -} - -// marshaMessageSet implements above function, with the opt to turn on / off deterministic during Marshal. -func marshalMessageSet(exts interface{}, deterministic bool) ([]byte, error) { - switch exts := exts.(type) { - case *XXX_InternalExtensions: - var u marshalInfo - siz := u.sizeMessageSet(exts) - b := make([]byte, 0, siz) - return u.appendMessageSet(b, exts, deterministic) - - case map[int32]Extension: - // This is an old-style extension map. - // Wrap it in a new-style XXX_InternalExtensions. - ie := XXX_InternalExtensions{ - p: &struct { - mu sync.Mutex - extensionMap map[int32]Extension - }{ - extensionMap: exts, - }, - } - - var u marshalInfo - siz := u.sizeMessageSet(&ie) - b := make([]byte, 0, siz) - return u.appendMessageSet(b, &ie, deterministic) - - default: - return nil, errors.New("proto: not an extension map") - } -} - -// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. +// unmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. // It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option. -func UnmarshalMessageSet(buf []byte, exts interface{}) error { +func unmarshalMessageSet(buf []byte, exts interface{}) error { var m map[int32]Extension switch exts := exts.(type) { case *XXX_InternalExtensions: @@ -222,93 +179,3 @@ func UnmarshalMessageSet(buf []byte, exts interface{}) error { } return nil } - -// MarshalMessageSetJSON encodes the extension map represented by m in JSON format. -// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option. -func MarshalMessageSetJSON(exts interface{}) ([]byte, error) { - var m map[int32]Extension - switch exts := exts.(type) { - case *XXX_InternalExtensions: - var mu sync.Locker - m, mu = exts.extensionsRead() - if m != nil { - // Keep the extensions map locked until we're done marshaling to prevent - // races between marshaling and unmarshaling the lazily-{en,de}coded - // values. - mu.Lock() - defer mu.Unlock() - } - case map[int32]Extension: - m = exts - default: - return nil, errors.New("proto: not an extension map") - } - var b bytes.Buffer - b.WriteByte('{') - - // Process the map in key order for deterministic output. - ids := make([]int32, 0, len(m)) - for id := range m { - ids = append(ids, id) - } - sort.Sort(int32Slice(ids)) // int32Slice defined in text.go - - for i, id := range ids { - ext := m[id] - msd, ok := messageSetMap[id] - if !ok { - // Unknown type; we can't render it, so skip it. - continue - } - - if i > 0 && b.Len() > 1 { - b.WriteByte(',') - } - - fmt.Fprintf(&b, `"[%s]":`, msd.name) - - x := ext.value - if x == nil { - x = reflect.New(msd.t.Elem()).Interface() - if err := Unmarshal(ext.enc, x.(Message)); err != nil { - return nil, err - } - } - d, err := json.Marshal(x) - if err != nil { - return nil, err - } - b.Write(d) - } - b.WriteByte('}') - return b.Bytes(), nil -} - -// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format. -// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option. -func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error { - // Common-case fast path. - if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) { - return nil - } - - // This is fairly tricky, and it's not clear that it is needed. - return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented") -} - -// A global registry of types that can be used in a MessageSet. - -var messageSetMap = make(map[int32]messageSetDesc) - -type messageSetDesc struct { - t reflect.Type // pointer to struct - name string -} - -// RegisterMessageSetType is called from the generated code. -func RegisterMessageSetType(m Message, fieldNum int32, name string) { - messageSetMap[fieldNum] = messageSetDesc{ - t: reflect.TypeOf(m), - name: name, - } -} diff --git a/vendor/github.com/golang/protobuf/proto/pointer_reflect.go b/vendor/github.com/golang/protobuf/proto/pointer_reflect.go index b6cad90834b31..94fa9194a8823 100644 --- a/vendor/github.com/golang/protobuf/proto/pointer_reflect.go +++ b/vendor/github.com/golang/protobuf/proto/pointer_reflect.go @@ -79,10 +79,13 @@ func toPointer(i *Message) pointer { // toAddrPointer converts an interface to a pointer that points to // the interface data. -func toAddrPointer(i *interface{}, isptr bool) pointer { +func toAddrPointer(i *interface{}, isptr, deref bool) pointer { v := reflect.ValueOf(*i) u := reflect.New(v.Type()) u.Elem().Set(v) + if deref { + u = u.Elem() + } return pointer{v: u} } diff --git a/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go b/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go index d55a335d94532..dbfffe071b82a 100644 --- a/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go +++ b/vendor/github.com/golang/protobuf/proto/pointer_unsafe.go @@ -85,16 +85,21 @@ func toPointer(i *Message) pointer { // toAddrPointer converts an interface to a pointer that points to // the interface data. -func toAddrPointer(i *interface{}, isptr bool) pointer { +func toAddrPointer(i *interface{}, isptr, deref bool) (p pointer) { // Super-tricky - read or get the address of data word of interface value. if isptr { // The interface is of pointer type, thus it is a direct interface. // The data word is the pointer data itself. We take its address. - return pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)} + p = pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)} + } else { + // The interface is not of pointer type. The data word is the pointer + // to the data. + p = pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} } - // The interface is not of pointer type. The data word is the pointer - // to the data. - return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} + if deref { + p.p = *(*unsafe.Pointer)(p.p) + } + return p } // valToPointer converts v to a pointer. v must be of pointer type. diff --git a/vendor/github.com/golang/protobuf/proto/properties.go b/vendor/github.com/golang/protobuf/proto/properties.go index f710adab09246..a4b8c0cd3a861 100644 --- a/vendor/github.com/golang/protobuf/proto/properties.go +++ b/vendor/github.com/golang/protobuf/proto/properties.go @@ -38,7 +38,6 @@ package proto import ( "fmt" "log" - "os" "reflect" "sort" "strconv" @@ -139,7 +138,7 @@ type Properties struct { Repeated bool Packed bool // relevant for repeated primitives only Enum string // set for enum types only - proto3 bool // whether this is known to be a proto3 field; set for []byte only + proto3 bool // whether this is known to be a proto3 field oneof bool // whether this is a oneof field Default string // default value @@ -148,9 +147,9 @@ type Properties struct { stype reflect.Type // set for struct types only sprop *StructProperties // set for struct types only - mtype reflect.Type // set for map types only - mkeyprop *Properties // set for map types only - mvalprop *Properties // set for map types only + mtype reflect.Type // set for map types only + MapKeyProp *Properties // set for map types only + MapValProp *Properties // set for map types only } // String formats the properties in the protobuf struct field tag style. @@ -194,7 +193,7 @@ func (p *Properties) Parse(s string) { // "bytes,49,opt,name=foo,def=hello!" fields := strings.Split(s, ",") // breaks def=, but handled below. if len(fields) < 2 { - fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s) + log.Printf("proto: tag has too few fields: %q", s) return } @@ -214,7 +213,7 @@ func (p *Properties) Parse(s string) { p.WireType = WireBytes // no numeric converter for non-numeric types default: - fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s) + log.Printf("proto: tag has unknown wire type: %q", s) return } @@ -275,16 +274,16 @@ func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, loc case reflect.Map: p.mtype = t1 - p.mkeyprop = &Properties{} - p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp) - p.mvalprop = &Properties{} + p.MapKeyProp = &Properties{} + p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp) + p.MapValProp = &Properties{} vtype := p.mtype.Elem() if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice { // The value type is not a message (*T) or bytes ([]byte), // so we need encoders for the pointer to this type. vtype = reflect.PtrTo(vtype) } - p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp) + p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp) } if p.stype != nil { @@ -334,9 +333,6 @@ func GetProperties(t reflect.Type) *StructProperties { sprop, ok := propertiesMap[t] propertiesMu.RUnlock() if ok { - if collectStats { - stats.Chit++ - } return sprop } @@ -346,17 +342,20 @@ func GetProperties(t reflect.Type) *StructProperties { return sprop } +type ( + oneofFuncsIface interface { + XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) + } + oneofWrappersIface interface { + XXX_OneofWrappers() []interface{} + } +) + // getPropertiesLocked requires that propertiesMu is held. func getPropertiesLocked(t reflect.Type) *StructProperties { if prop, ok := propertiesMap[t]; ok { - if collectStats { - stats.Chit++ - } return prop } - if collectStats { - stats.Cmiss++ - } prop := new(StructProperties) // in case of recursive protos, fill this in now. @@ -391,13 +390,14 @@ func getPropertiesLocked(t reflect.Type) *StructProperties { // Re-order prop.order. sort.Sort(prop) - type oneofMessage interface { - XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) + var oots []interface{} + switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) { + case oneofFuncsIface: + _, _, _, oots = m.XXX_OneofFuncs() + case oneofWrappersIface: + oots = m.XXX_OneofWrappers() } - if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok { - var oots []interface{} - _, _, _, oots = om.XXX_OneofFuncs() - + if len(oots) > 0 { // Interpret oneof metadata. prop.OneofTypes = make(map[string]*OneofProperties) for _, oot := range oots { diff --git a/vendor/github.com/golang/protobuf/proto/table_marshal.go b/vendor/github.com/golang/protobuf/proto/table_marshal.go index 0f212b3029d2d..5cb11fa955e4d 100644 --- a/vendor/github.com/golang/protobuf/proto/table_marshal.go +++ b/vendor/github.com/golang/protobuf/proto/table_marshal.go @@ -87,6 +87,7 @@ type marshalElemInfo struct { sizer sizer marshaler marshaler isptr bool // elem is pointer typed, thus interface of this type is a direct interface (extension only) + deref bool // dereference the pointer before operating on it; implies isptr } var ( @@ -231,7 +232,7 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte return b, err } - var err, errreq error + var err, errLater error // The old marshaler encodes extensions at beginning. if u.extensions.IsValid() { e := ptr.offset(u.extensions).toExtensions() @@ -252,11 +253,13 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte } } for _, f := range u.fields { - if f.required && errreq == nil { + if f.required { if ptr.offset(f.field).getPointer().isNil() { // Required field is not set. // We record the error but keep going, to give a complete marshaling. - errreq = &RequiredNotSetError{f.name} + if errLater == nil { + errLater = &RequiredNotSetError{f.name} + } continue } } @@ -269,14 +272,21 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte if err1, ok := err.(*RequiredNotSetError); ok { // Required field in submessage is not set. // We record the error but keep going, to give a complete marshaling. - if errreq == nil { - errreq = &RequiredNotSetError{f.name + "." + err1.field} + if errLater == nil { + errLater = &RequiredNotSetError{f.name + "." + err1.field} } continue } if err == errRepeatedHasNil { err = errors.New("proto: repeated field " + f.name + " has nil element") } + if err == errInvalidUTF8 { + if errLater == nil { + fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name + errLater = &invalidUTF8Error{fullName} + } + continue + } return b, err } } @@ -284,7 +294,7 @@ func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte s := *ptr.offset(u.unrecognized).toBytes() b = append(b, s...) } - return b, errreq + return b, errLater } // computeMarshalInfo initializes the marshal info. @@ -311,8 +321,11 @@ func (u *marshalInfo) computeMarshalInfo() { // get oneof implementers var oneofImplementers []interface{} - if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok { + switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) { + case oneofFuncsIface: _, _, _, oneofImplementers = m.XXX_OneofFuncs() + case oneofWrappersIface: + oneofImplementers = m.XXX_OneofWrappers() } n := t.NumField() @@ -398,13 +411,22 @@ func (u *marshalInfo) getExtElemInfo(desc *ExtensionDesc) *marshalElemInfo { panic("tag is not an integer") } wt := wiretype(tags[0]) + if t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct { + t = t.Elem() + } sizer, marshaler := typeMarshaler(t, tags, false, false) + var deref bool + if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 { + t = reflect.PtrTo(t) + deref = true + } e = &marshalElemInfo{ wiretag: uint64(tag)<<3 | wt, tagsize: SizeVarint(uint64(tag) << 3), sizer: sizer, marshaler: marshaler, isptr: t.Kind() == reflect.Ptr, + deref: deref, } // update cache @@ -439,7 +461,7 @@ func (fi *marshalFieldInfo) computeMarshalFieldInfo(f *reflect.StructField) { func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofImplementers []interface{}) { fi.field = toField(f) - fi.wiretag = 1<<31 - 1 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire. + fi.wiretag = math.MaxInt32 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire. fi.isPointer = true fi.sizer, fi.marshaler = makeOneOfMarshaler(fi, f) fi.oneofElems = make(map[reflect.Type]*marshalElemInfo) @@ -467,10 +489,6 @@ func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofI } } -type oneofMessage interface { - XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) -} - // wiretype returns the wire encoding of the type. func wiretype(encoding string) uint64 { switch encoding { @@ -530,6 +548,7 @@ func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, ma packed := false proto3 := false + validateUTF8 := true for i := 2; i < len(tags); i++ { if tags[i] == "packed" { packed = true @@ -538,6 +557,7 @@ func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, ma proto3 = true } } + validateUTF8 = validateUTF8 && proto3 switch t.Kind() { case reflect.Bool: @@ -735,6 +755,18 @@ func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, ma } return sizeFloat64Value, appendFloat64Value case reflect.String: + if validateUTF8 { + if pointer { + return sizeStringPtr, appendUTF8StringPtr + } + if slice { + return sizeStringSlice, appendUTF8StringSlice + } + if nozero { + return sizeStringValueNoZero, appendUTF8StringValueNoZero + } + return sizeStringValue, appendUTF8StringValue + } if pointer { return sizeStringPtr, appendStringPtr } @@ -1983,52 +2015,105 @@ func appendBoolPackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byt return b, nil } func appendStringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toString() + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendStringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + v := *ptr.toString() + if v == "" { + return b, nil + } + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendStringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + p := *ptr.toStringPtr() + if p == nil { + return b, nil + } + v := *p + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + return b, nil +} +func appendStringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + s := *ptr.toStringSlice() + for _, v := range s { + b = appendVarint(b, wiretag) + b = appendVarint(b, uint64(len(v))) + b = append(b, v...) + } + return b, nil +} +func appendUTF8StringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool v := *ptr.toString() if !utf8.ValidString(v) { - return nil, errInvalidUTF8 + invalidUTF8 = true } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) + if invalidUTF8 { + return b, errInvalidUTF8 + } return b, nil } -func appendStringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { +func appendUTF8StringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool v := *ptr.toString() if v == "" { return b, nil } if !utf8.ValidString(v) { - return nil, errInvalidUTF8 + invalidUTF8 = true } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) + if invalidUTF8 { + return b, errInvalidUTF8 + } return b, nil } -func appendStringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { +func appendUTF8StringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool p := *ptr.toStringPtr() if p == nil { return b, nil } v := *p if !utf8.ValidString(v) { - return nil, errInvalidUTF8 + invalidUTF8 = true } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) + if invalidUTF8 { + return b, errInvalidUTF8 + } return b, nil } -func appendStringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { +func appendUTF8StringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { + var invalidUTF8 bool s := *ptr.toStringSlice() for _, v := range s { if !utf8.ValidString(v) { - return nil, errInvalidUTF8 + invalidUTF8 = true } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) } + if invalidUTF8 { + return b, errInvalidUTF8 + } return b, nil } func appendBytes(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { @@ -2107,7 +2192,8 @@ func makeGroupSliceMarshaler(u *marshalInfo) (sizer, marshaler) { }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getPointerSlice() - var err, errreq error + var err error + var nerr nonFatal for _, v := range s { if v.isNil() { return b, errRepeatedHasNil @@ -2115,22 +2201,14 @@ func makeGroupSliceMarshaler(u *marshalInfo) (sizer, marshaler) { b = appendVarint(b, wiretag) // start group b, err = u.marshal(b, v, deterministic) b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group - if err != nil { - if _, ok := err.(*RequiredNotSetError); ok { - // Required field in submessage is not set. - // We record the error but keep going, to give a complete marshaling. - if errreq == nil { - errreq = err - } - continue - } + if !nerr.Merge(err) { if err == ErrNil { err = errRepeatedHasNil } return b, err } } - return b, errreq + return b, nerr.E } } @@ -2174,7 +2252,8 @@ func makeMessageSliceMarshaler(u *marshalInfo) (sizer, marshaler) { }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getPointerSlice() - var err, errreq error + var err error + var nerr nonFatal for _, v := range s { if v.isNil() { return b, errRepeatedHasNil @@ -2184,22 +2263,14 @@ func makeMessageSliceMarshaler(u *marshalInfo) (sizer, marshaler) { b = appendVarint(b, uint64(siz)) b, err = u.marshal(b, v, deterministic) - if err != nil { - if _, ok := err.(*RequiredNotSetError); ok { - // Required field in submessage is not set. - // We record the error but keep going, to give a complete marshaling. - if errreq == nil { - errreq = err - } - continue - } + if !nerr.Merge(err) { if err == ErrNil { err = errRepeatedHasNil } return b, err } } - return b, errreq + return b, nerr.E } } @@ -2223,14 +2294,33 @@ func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) { // value. // Key cannot be pointer-typed. valIsPtr := valType.Kind() == reflect.Ptr + + // If value is a message with nested maps, calling + // valSizer in marshal may be quadratic. We should use + // cached version in marshal (but not in size). + // If value is not message type, we don't have size cache, + // but it cannot be nested either. Just use valSizer. + valCachedSizer := valSizer + if valIsPtr && valType.Elem().Kind() == reflect.Struct { + u := getMarshalInfo(valType.Elem()) + valCachedSizer = func(ptr pointer, tagsize int) int { + // Same as message sizer, but use cache. + p := ptr.getPointer() + if p.isNil() { + return 0 + } + siz := u.cachedsize(p) + return siz + SizeVarint(uint64(siz)) + tagsize + } + } return func(ptr pointer, tagsize int) int { m := ptr.asPointerTo(t).Elem() // the map n := 0 for _, k := range m.MapKeys() { ki := k.Interface() vi := m.MapIndex(k).Interface() - kaddr := toAddrPointer(&ki, false) // pointer to key - vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value + kaddr := toAddrPointer(&ki, false, false) // pointer to key + vaddr := toAddrPointer(&vi, valIsPtr, false) // pointer to value siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1) n += siz + SizeVarint(uint64(siz)) + tagsize } @@ -2243,24 +2333,26 @@ func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) { if len(keys) > 1 && deterministic { sort.Sort(mapKeys(keys)) } + + var nerr nonFatal for _, k := range keys { ki := k.Interface() vi := m.MapIndex(k).Interface() - kaddr := toAddrPointer(&ki, false) // pointer to key - vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value + kaddr := toAddrPointer(&ki, false, false) // pointer to key + vaddr := toAddrPointer(&vi, valIsPtr, false) // pointer to value b = appendVarint(b, tag) - siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1) + siz := keySizer(kaddr, 1) + valCachedSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1) b = appendVarint(b, uint64(siz)) b, err = keyMarshaler(b, kaddr, keyWireTag, deterministic) - if err != nil { + if !nerr.Merge(err) { return b, err } b, err = valMarshaler(b, vaddr, valWireTag, deterministic) - if err != nil && err != ErrNil { // allow nil value in map + if err != ErrNil && !nerr.Merge(err) { // allow nil value in map return b, err } } - return b, nil + return b, nerr.E } } @@ -2316,7 +2408,7 @@ func (u *marshalInfo) sizeExtensions(ext *XXX_InternalExtensions) int { // the last time this function was called. ei := u.getExtElemInfo(e.desc) v := e.value - p := toAddrPointer(&v, ei.isptr) + p := toAddrPointer(&v, ei.isptr, ei.deref) n += ei.sizer(p, ei.tagsize) } mu.Unlock() @@ -2333,6 +2425,7 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de defer mu.Unlock() var err error + var nerr nonFatal // Fast-path for common cases: zero or one extensions. // Don't bother sorting the keys. @@ -2350,13 +2443,13 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de ei := u.getExtElemInfo(e.desc) v := e.value - p := toAddrPointer(&v, ei.isptr) + p := toAddrPointer(&v, ei.isptr, ei.deref) b, err = ei.marshaler(b, p, ei.wiretag, deterministic) - if err != nil { + if !nerr.Merge(err) { return b, err } } - return b, nil + return b, nerr.E } // Sort the keys to provide a deterministic encoding. @@ -2381,13 +2474,13 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de ei := u.getExtElemInfo(e.desc) v := e.value - p := toAddrPointer(&v, ei.isptr) + p := toAddrPointer(&v, ei.isptr, ei.deref) b, err = ei.marshaler(b, p, ei.wiretag, deterministic) - if err != nil { + if !nerr.Merge(err) { return b, err } } - return b, nil + return b, nerr.E } // message set format is: @@ -2426,7 +2519,7 @@ func (u *marshalInfo) sizeMessageSet(ext *XXX_InternalExtensions) int { ei := u.getExtElemInfo(e.desc) v := e.value - p := toAddrPointer(&v, ei.isptr) + p := toAddrPointer(&v, ei.isptr, ei.deref) n += ei.sizer(p, 1) // message, tag = 3 (size=1) } mu.Unlock() @@ -2444,6 +2537,7 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de defer mu.Unlock() var err error + var nerr nonFatal // Fast-path for common cases: zero or one extensions. // Don't bother sorting the keys. @@ -2468,14 +2562,14 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de ei := u.getExtElemInfo(e.desc) v := e.value - p := toAddrPointer(&v, ei.isptr) + p := toAddrPointer(&v, ei.isptr, ei.deref) b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic) - if err != nil { + if !nerr.Merge(err) { return b, err } b = append(b, 1<<3|WireEndGroup) } - return b, nil + return b, nerr.E } // Sort the keys to provide a deterministic encoding. @@ -2506,14 +2600,14 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de ei := u.getExtElemInfo(e.desc) v := e.value - p := toAddrPointer(&v, ei.isptr) + p := toAddrPointer(&v, ei.isptr, ei.deref) b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic) b = append(b, 1<<3|WireEndGroup) - if err != nil { + if !nerr.Merge(err) { return b, err } } - return b, nil + return b, nerr.E } // sizeV1Extensions computes the size of encoded data for a V1-API extension field. @@ -2536,7 +2630,7 @@ func (u *marshalInfo) sizeV1Extensions(m map[int32]Extension) int { ei := u.getExtElemInfo(e.desc) v := e.value - p := toAddrPointer(&v, ei.isptr) + p := toAddrPointer(&v, ei.isptr, ei.deref) n += ei.sizer(p, ei.tagsize) } return n @@ -2556,6 +2650,7 @@ func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, determ sort.Ints(keys) var err error + var nerr nonFatal for _, k := range keys { e := m[int32(k)] if e.value == nil || e.desc == nil { @@ -2570,13 +2665,13 @@ func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, determ ei := u.getExtElemInfo(e.desc) v := e.value - p := toAddrPointer(&v, ei.isptr) + p := toAddrPointer(&v, ei.isptr, ei.deref) b, err = ei.marshaler(b, p, ei.wiretag, deterministic) - if err != nil { + if !nerr.Merge(err) { return b, err } } - return b, nil + return b, nerr.E } // newMarshaler is the interface representing objects that can marshal themselves. diff --git a/vendor/github.com/golang/protobuf/proto/table_unmarshal.go b/vendor/github.com/golang/protobuf/proto/table_unmarshal.go index 55f0340a3fde2..acee2fc529640 100644 --- a/vendor/github.com/golang/protobuf/proto/table_unmarshal.go +++ b/vendor/github.com/golang/protobuf/proto/table_unmarshal.go @@ -97,6 +97,8 @@ type unmarshalFieldInfo struct { // if a required field, contains a single set bit at this field's index in the required field list. reqMask uint64 + + name string // name of the field, for error reporting } var ( @@ -134,10 +136,10 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error { u.computeUnmarshalInfo() } if u.isMessageSet { - return UnmarshalMessageSet(b, m.offset(u.extensions).toExtensions()) + return unmarshalMessageSet(b, m.offset(u.extensions).toExtensions()) } - var reqMask uint64 // bitmask of required fields we've seen. - var rnse *RequiredNotSetError // an instance of a RequiredNotSetError returned by a submessage. + var reqMask uint64 // bitmask of required fields we've seen. + var errLater error for len(b) > 0 { // Read tag and wire type. // Special case 1 and 2 byte varints. @@ -176,11 +178,20 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error { if r, ok := err.(*RequiredNotSetError); ok { // Remember this error, but keep parsing. We need to produce // a full parse even if a required field is missing. - rnse = r + if errLater == nil { + errLater = r + } reqMask |= f.reqMask continue } if err != errInternalBadWireType { + if err == errInvalidUTF8 { + if errLater == nil { + fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name + errLater = &invalidUTF8Error{fullName} + } + continue + } return err } // Fragments with bad wire type are treated as unknown fields. @@ -239,20 +250,16 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error { emap[int32(tag)] = e } } - if rnse != nil { - // A required field of a submessage/group is missing. Return that error. - return rnse - } - if reqMask != u.reqMask { + if reqMask != u.reqMask && errLater == nil { // A required field of this message is missing. for _, n := range u.reqFields { if reqMask&1 == 0 { - return &RequiredNotSetError{n} + errLater = &RequiredNotSetError{n} } reqMask >>= 1 } } - return nil + return errLater } // computeUnmarshalInfo fills in u with information for use @@ -351,43 +358,52 @@ func (u *unmarshalInfo) computeUnmarshalInfo() { } // Store the info in the correct slot in the message. - u.setTag(tag, toField(&f), unmarshal, reqMask) + u.setTag(tag, toField(&f), unmarshal, reqMask, name) } // Find any types associated with oneof fields. - // TODO: XXX_OneofFuncs returns more info than we need. Get rid of some of it? - fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("XXX_OneofFuncs") - if fn.IsValid() { - res := fn.Call(nil)[3] // last return value from XXX_OneofFuncs: []interface{} - for i := res.Len() - 1; i >= 0; i-- { - v := res.Index(i) // interface{} - tptr := reflect.ValueOf(v.Interface()).Type() // *Msg_X - typ := tptr.Elem() // Msg_X - - f := typ.Field(0) // oneof implementers have one field - baseUnmarshal := fieldUnmarshaler(&f) - tagstr := strings.Split(f.Tag.Get("protobuf"), ",")[1] - tag, err := strconv.Atoi(tagstr) - if err != nil { - panic("protobuf tag field not an integer: " + tagstr) - } - - // Find the oneof field that this struct implements. - // Might take O(n^2) to process all of the oneofs, but who cares. - for _, of := range oneofFields { - if tptr.Implements(of.ityp) { - // We have found the corresponding interface for this struct. - // That lets us know where this struct should be stored - // when we encounter it during unmarshaling. - unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal) - u.setTag(tag, of.field, unmarshal, 0) - } + var oneofImplementers []interface{} + switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) { + case oneofFuncsIface: + _, _, _, oneofImplementers = m.XXX_OneofFuncs() + case oneofWrappersIface: + oneofImplementers = m.XXX_OneofWrappers() + } + for _, v := range oneofImplementers { + tptr := reflect.TypeOf(v) // *Msg_X + typ := tptr.Elem() // Msg_X + + f := typ.Field(0) // oneof implementers have one field + baseUnmarshal := fieldUnmarshaler(&f) + tags := strings.Split(f.Tag.Get("protobuf"), ",") + fieldNum, err := strconv.Atoi(tags[1]) + if err != nil { + panic("protobuf tag field not an integer: " + tags[1]) + } + var name string + for _, tag := range tags { + if strings.HasPrefix(tag, "name=") { + name = strings.TrimPrefix(tag, "name=") + break } } + + // Find the oneof field that this struct implements. + // Might take O(n^2) to process all of the oneofs, but who cares. + for _, of := range oneofFields { + if tptr.Implements(of.ityp) { + // We have found the corresponding interface for this struct. + // That lets us know where this struct should be stored + // when we encounter it during unmarshaling. + unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal) + u.setTag(fieldNum, of.field, unmarshal, 0, name) + } + } + } // Get extension ranges, if any. - fn = reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray") + fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray") if fn.IsValid() { if !u.extensions.IsValid() && !u.oldExtensions.IsValid() { panic("a message with extensions, but no extensions field in " + t.Name()) @@ -401,7 +417,7 @@ func (u *unmarshalInfo) computeUnmarshalInfo() { // [0 0] is [tag=0/wiretype=varint varint-encoded-0]. u.setTag(0, zeroField, func(b []byte, f pointer, w int) ([]byte, error) { return nil, fmt.Errorf("proto: %s: illegal tag 0 (wire type %d)", t, w) - }, 0) + }, 0, "") // Set mask for required field check. u.reqMask = uint64(1)<= 0 && (tag < 16 || tag < 2*n) { // TODO: what are the right numbers here? for len(u.dense) <= tag { @@ -442,11 +459,17 @@ func typeUnmarshaler(t reflect.Type, tags string) unmarshaler { tagArray := strings.Split(tags, ",") encoding := tagArray[0] name := "unknown" + proto3 := false + validateUTF8 := true for _, tag := range tagArray[3:] { if strings.HasPrefix(tag, "name=") { name = tag[5:] } + if tag == "proto3" { + proto3 = true + } } + validateUTF8 = validateUTF8 && proto3 // Figure out packaging (pointer, slice, or both) slice := false @@ -594,6 +617,15 @@ func typeUnmarshaler(t reflect.Type, tags string) unmarshaler { } return unmarshalBytesValue case reflect.String: + if validateUTF8 { + if pointer { + return unmarshalUTF8StringPtr + } + if slice { + return unmarshalUTF8StringSlice + } + return unmarshalUTF8StringValue + } if pointer { return unmarshalStringPtr } @@ -1448,9 +1480,6 @@ func unmarshalStringValue(b []byte, f pointer, w int) ([]byte, error) { return nil, io.ErrUnexpectedEOF } v := string(b[:x]) - if !utf8.ValidString(v) { - return nil, errInvalidUTF8 - } *f.toString() = v return b[x:], nil } @@ -1468,9 +1497,6 @@ func unmarshalStringPtr(b []byte, f pointer, w int) ([]byte, error) { return nil, io.ErrUnexpectedEOF } v := string(b[:x]) - if !utf8.ValidString(v) { - return nil, errInvalidUTF8 - } *f.toStringPtr() = &v return b[x:], nil } @@ -1488,11 +1514,69 @@ func unmarshalStringSlice(b []byte, f pointer, w int) ([]byte, error) { return nil, io.ErrUnexpectedEOF } v := string(b[:x]) + s := f.toStringSlice() + *s = append(*s, v) + return b[x:], nil +} + +func unmarshalUTF8StringValue(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + *f.toString() = v + if !utf8.ValidString(v) { + return b[x:], errInvalidUTF8 + } + return b[x:], nil +} + +func unmarshalUTF8StringPtr(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF + } + v := string(b[:x]) + *f.toStringPtr() = &v if !utf8.ValidString(v) { - return nil, errInvalidUTF8 + return b[x:], errInvalidUTF8 + } + return b[x:], nil +} + +func unmarshalUTF8StringSlice(b []byte, f pointer, w int) ([]byte, error) { + if w != WireBytes { + return b, errInternalBadWireType + } + x, n := decodeVarint(b) + if n == 0 { + return nil, io.ErrUnexpectedEOF + } + b = b[n:] + if x > uint64(len(b)) { + return nil, io.ErrUnexpectedEOF } + v := string(b[:x]) s := f.toStringSlice() *s = append(*s, v) + if !utf8.ValidString(v) { + return b[x:], errInvalidUTF8 + } return b[x:], nil } @@ -1674,6 +1758,7 @@ func makeUnmarshalMap(f *reflect.StructField) unmarshaler { // Maps will be somewhat slow. Oh well. // Read key and value from data. + var nerr nonFatal k := reflect.New(kt) v := reflect.New(vt) for len(b) > 0 { @@ -1694,7 +1779,7 @@ func makeUnmarshalMap(f *reflect.StructField) unmarshaler { err = errInternalBadWireType // skip unknown tag } - if err == nil { + if nerr.Merge(err) { continue } if err != errInternalBadWireType { @@ -1717,7 +1802,7 @@ func makeUnmarshalMap(f *reflect.StructField) unmarshaler { // Insert into map. m.SetMapIndex(k.Elem(), v.Elem()) - return r, nil + return r, nerr.E } } @@ -1743,15 +1828,16 @@ func makeUnmarshalOneof(typ, ityp reflect.Type, unmarshal unmarshaler) unmarshal // Unmarshal data into holder. // We unmarshal into the first field of the holder object. var err error + var nerr nonFatal b, err = unmarshal(b, valToPointer(v).offset(field0), w) - if err != nil { + if !nerr.Merge(err) { return nil, err } // Write pointer to holder into target field. f.asPointerTo(ityp).Elem().Set(v) - return b, nil + return b, nerr.E } } @@ -1864,7 +1950,7 @@ func encodeVarint(b []byte, x uint64) []byte { // If there is an error, it returns 0,0. func decodeVarint(b []byte) (uint64, int) { var x, y uint64 - if len(b) <= 0 { + if len(b) == 0 { goto bad } x = uint64(b[0]) diff --git a/vendor/github.com/golang/protobuf/proto/text.go b/vendor/github.com/golang/protobuf/proto/text.go index 2205fdaadf84c..d97f9b3563ea2 100644 --- a/vendor/github.com/golang/protobuf/proto/text.go +++ b/vendor/github.com/golang/protobuf/proto/text.go @@ -353,7 +353,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { return err } } - if err := tm.writeAny(w, key, props.mkeyprop); err != nil { + if err := tm.writeAny(w, key, props.MapKeyProp); err != nil { return err } if err := w.WriteByte('\n'); err != nil { @@ -370,7 +370,7 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { return err } } - if err := tm.writeAny(w, val, props.mvalprop); err != nil { + if err := tm.writeAny(w, val, props.MapValProp); err != nil { return err } if err := w.WriteByte('\n'); err != nil { @@ -456,6 +456,8 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { return nil } +var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() + // writeAny writes an arbitrary field. func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error { v = reflect.Indirect(v) @@ -519,8 +521,8 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert // mutating this value. v = v.Addr() } - if etm, ok := v.Interface().(encoding.TextMarshaler); ok { - text, err := etm.MarshalText() + if v.Type().Implements(textMarshalerType) { + text, err := v.Interface().(encoding.TextMarshaler).MarshalText() if err != nil { return err } diff --git a/vendor/github.com/golang/protobuf/proto/text_parser.go b/vendor/github.com/golang/protobuf/proto/text_parser.go index 0685bae36d507..bb55a3af27694 100644 --- a/vendor/github.com/golang/protobuf/proto/text_parser.go +++ b/vendor/github.com/golang/protobuf/proto/text_parser.go @@ -630,17 +630,17 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error { if err := p.consumeToken(":"); err != nil { return err } - if err := p.readAny(key, props.mkeyprop); err != nil { + if err := p.readAny(key, props.MapKeyProp); err != nil { return err } if err := p.consumeOptionalSeparator(); err != nil { return err } case "value": - if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil { + if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil { return err } - if err := p.readAny(val, props.mvalprop); err != nil { + if err := p.readAny(val, props.MapValProp); err != nil { return err } if err := p.consumeOptionalSeparator(); err != nil { diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go index e855b1f5c4abc..d371d56974b5f 100644 --- a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go +++ b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go @@ -1,11 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: google/protobuf/descriptor.proto -package descriptor // import "github.com/golang/protobuf/protoc-gen-go/descriptor" +package descriptor -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -16,7 +18,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type FieldDescriptorProto_Type int32 @@ -72,6 +74,7 @@ var FieldDescriptorProto_Type_name = map[int32]string{ 17: "TYPE_SINT32", 18: "TYPE_SINT64", } + var FieldDescriptorProto_Type_value = map[string]int32{ "TYPE_DOUBLE": 1, "TYPE_FLOAT": 2, @@ -98,9 +101,11 @@ func (x FieldDescriptorProto_Type) Enum() *FieldDescriptorProto_Type { *p = x return p } + func (x FieldDescriptorProto_Type) String() string { return proto.EnumName(FieldDescriptorProto_Type_name, int32(x)) } + func (x *FieldDescriptorProto_Type) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FieldDescriptorProto_Type_value, data, "FieldDescriptorProto_Type") if err != nil { @@ -109,8 +114,9 @@ func (x *FieldDescriptorProto_Type) UnmarshalJSON(data []byte) error { *x = FieldDescriptorProto_Type(value) return nil } + func (FieldDescriptorProto_Type) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{4, 0} + return fileDescriptor_e5baabe45344a177, []int{4, 0} } type FieldDescriptorProto_Label int32 @@ -127,6 +133,7 @@ var FieldDescriptorProto_Label_name = map[int32]string{ 2: "LABEL_REQUIRED", 3: "LABEL_REPEATED", } + var FieldDescriptorProto_Label_value = map[string]int32{ "LABEL_OPTIONAL": 1, "LABEL_REQUIRED": 2, @@ -138,9 +145,11 @@ func (x FieldDescriptorProto_Label) Enum() *FieldDescriptorProto_Label { *p = x return p } + func (x FieldDescriptorProto_Label) String() string { return proto.EnumName(FieldDescriptorProto_Label_name, int32(x)) } + func (x *FieldDescriptorProto_Label) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FieldDescriptorProto_Label_value, data, "FieldDescriptorProto_Label") if err != nil { @@ -149,8 +158,9 @@ func (x *FieldDescriptorProto_Label) UnmarshalJSON(data []byte) error { *x = FieldDescriptorProto_Label(value) return nil } + func (FieldDescriptorProto_Label) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{4, 1} + return fileDescriptor_e5baabe45344a177, []int{4, 1} } // Generated classes can be optimized for speed or code size. @@ -168,6 +178,7 @@ var FileOptions_OptimizeMode_name = map[int32]string{ 2: "CODE_SIZE", 3: "LITE_RUNTIME", } + var FileOptions_OptimizeMode_value = map[string]int32{ "SPEED": 1, "CODE_SIZE": 2, @@ -179,9 +190,11 @@ func (x FileOptions_OptimizeMode) Enum() *FileOptions_OptimizeMode { *p = x return p } + func (x FileOptions_OptimizeMode) String() string { return proto.EnumName(FileOptions_OptimizeMode_name, int32(x)) } + func (x *FileOptions_OptimizeMode) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FileOptions_OptimizeMode_value, data, "FileOptions_OptimizeMode") if err != nil { @@ -190,8 +203,9 @@ func (x *FileOptions_OptimizeMode) UnmarshalJSON(data []byte) error { *x = FileOptions_OptimizeMode(value) return nil } + func (FileOptions_OptimizeMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{10, 0} + return fileDescriptor_e5baabe45344a177, []int{10, 0} } type FieldOptions_CType int32 @@ -208,6 +222,7 @@ var FieldOptions_CType_name = map[int32]string{ 1: "CORD", 2: "STRING_PIECE", } + var FieldOptions_CType_value = map[string]int32{ "STRING": 0, "CORD": 1, @@ -219,9 +234,11 @@ func (x FieldOptions_CType) Enum() *FieldOptions_CType { *p = x return p } + func (x FieldOptions_CType) String() string { return proto.EnumName(FieldOptions_CType_name, int32(x)) } + func (x *FieldOptions_CType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FieldOptions_CType_value, data, "FieldOptions_CType") if err != nil { @@ -230,8 +247,9 @@ func (x *FieldOptions_CType) UnmarshalJSON(data []byte) error { *x = FieldOptions_CType(value) return nil } + func (FieldOptions_CType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{12, 0} + return fileDescriptor_e5baabe45344a177, []int{12, 0} } type FieldOptions_JSType int32 @@ -250,6 +268,7 @@ var FieldOptions_JSType_name = map[int32]string{ 1: "JS_STRING", 2: "JS_NUMBER", } + var FieldOptions_JSType_value = map[string]int32{ "JS_NORMAL": 0, "JS_STRING": 1, @@ -261,9 +280,11 @@ func (x FieldOptions_JSType) Enum() *FieldOptions_JSType { *p = x return p } + func (x FieldOptions_JSType) String() string { return proto.EnumName(FieldOptions_JSType_name, int32(x)) } + func (x *FieldOptions_JSType) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FieldOptions_JSType_value, data, "FieldOptions_JSType") if err != nil { @@ -272,8 +293,9 @@ func (x *FieldOptions_JSType) UnmarshalJSON(data []byte) error { *x = FieldOptions_JSType(value) return nil } + func (FieldOptions_JSType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{12, 1} + return fileDescriptor_e5baabe45344a177, []int{12, 1} } // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, @@ -292,6 +314,7 @@ var MethodOptions_IdempotencyLevel_name = map[int32]string{ 1: "NO_SIDE_EFFECTS", 2: "IDEMPOTENT", } + var MethodOptions_IdempotencyLevel_value = map[string]int32{ "IDEMPOTENCY_UNKNOWN": 0, "NO_SIDE_EFFECTS": 1, @@ -303,9 +326,11 @@ func (x MethodOptions_IdempotencyLevel) Enum() *MethodOptions_IdempotencyLevel { *p = x return p } + func (x MethodOptions_IdempotencyLevel) String() string { return proto.EnumName(MethodOptions_IdempotencyLevel_name, int32(x)) } + func (x *MethodOptions_IdempotencyLevel) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(MethodOptions_IdempotencyLevel_value, data, "MethodOptions_IdempotencyLevel") if err != nil { @@ -314,8 +339,9 @@ func (x *MethodOptions_IdempotencyLevel) UnmarshalJSON(data []byte) error { *x = MethodOptions_IdempotencyLevel(value) return nil } + func (MethodOptions_IdempotencyLevel) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{17, 0} + return fileDescriptor_e5baabe45344a177, []int{17, 0} } // The protocol compiler can output a FileDescriptorSet containing the .proto @@ -331,16 +357,17 @@ func (m *FileDescriptorSet) Reset() { *m = FileDescriptorSet{} } func (m *FileDescriptorSet) String() string { return proto.CompactTextString(m) } func (*FileDescriptorSet) ProtoMessage() {} func (*FileDescriptorSet) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{0} + return fileDescriptor_e5baabe45344a177, []int{0} } + func (m *FileDescriptorSet) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FileDescriptorSet.Unmarshal(m, b) } func (m *FileDescriptorSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_FileDescriptorSet.Marshal(b, m, deterministic) } -func (dst *FileDescriptorSet) XXX_Merge(src proto.Message) { - xxx_messageInfo_FileDescriptorSet.Merge(dst, src) +func (m *FileDescriptorSet) XXX_Merge(src proto.Message) { + xxx_messageInfo_FileDescriptorSet.Merge(m, src) } func (m *FileDescriptorSet) XXX_Size() int { return xxx_messageInfo_FileDescriptorSet.Size(m) @@ -392,16 +419,17 @@ func (m *FileDescriptorProto) Reset() { *m = FileDescriptorProto{} } func (m *FileDescriptorProto) String() string { return proto.CompactTextString(m) } func (*FileDescriptorProto) ProtoMessage() {} func (*FileDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{1} + return fileDescriptor_e5baabe45344a177, []int{1} } + func (m *FileDescriptorProto) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FileDescriptorProto.Unmarshal(m, b) } func (m *FileDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_FileDescriptorProto.Marshal(b, m, deterministic) } -func (dst *FileDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_FileDescriptorProto.Merge(dst, src) +func (m *FileDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_FileDescriptorProto.Merge(m, src) } func (m *FileDescriptorProto) XXX_Size() int { return xxx_messageInfo_FileDescriptorProto.Size(m) @@ -519,16 +547,17 @@ func (m *DescriptorProto) Reset() { *m = DescriptorProto{} } func (m *DescriptorProto) String() string { return proto.CompactTextString(m) } func (*DescriptorProto) ProtoMessage() {} func (*DescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{2} + return fileDescriptor_e5baabe45344a177, []int{2} } + func (m *DescriptorProto) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_DescriptorProto.Unmarshal(m, b) } func (m *DescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_DescriptorProto.Marshal(b, m, deterministic) } -func (dst *DescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_DescriptorProto.Merge(dst, src) +func (m *DescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_DescriptorProto.Merge(m, src) } func (m *DescriptorProto) XXX_Size() int { return xxx_messageInfo_DescriptorProto.Size(m) @@ -622,16 +651,17 @@ func (m *DescriptorProto_ExtensionRange) Reset() { *m = DescriptorProto_ func (m *DescriptorProto_ExtensionRange) String() string { return proto.CompactTextString(m) } func (*DescriptorProto_ExtensionRange) ProtoMessage() {} func (*DescriptorProto_ExtensionRange) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{2, 0} + return fileDescriptor_e5baabe45344a177, []int{2, 0} } + func (m *DescriptorProto_ExtensionRange) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_DescriptorProto_ExtensionRange.Unmarshal(m, b) } func (m *DescriptorProto_ExtensionRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_DescriptorProto_ExtensionRange.Marshal(b, m, deterministic) } -func (dst *DescriptorProto_ExtensionRange) XXX_Merge(src proto.Message) { - xxx_messageInfo_DescriptorProto_ExtensionRange.Merge(dst, src) +func (m *DescriptorProto_ExtensionRange) XXX_Merge(src proto.Message) { + xxx_messageInfo_DescriptorProto_ExtensionRange.Merge(m, src) } func (m *DescriptorProto_ExtensionRange) XXX_Size() int { return xxx_messageInfo_DescriptorProto_ExtensionRange.Size(m) @@ -678,16 +708,17 @@ func (m *DescriptorProto_ReservedRange) Reset() { *m = DescriptorProto_R func (m *DescriptorProto_ReservedRange) String() string { return proto.CompactTextString(m) } func (*DescriptorProto_ReservedRange) ProtoMessage() {} func (*DescriptorProto_ReservedRange) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{2, 1} + return fileDescriptor_e5baabe45344a177, []int{2, 1} } + func (m *DescriptorProto_ReservedRange) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_DescriptorProto_ReservedRange.Unmarshal(m, b) } func (m *DescriptorProto_ReservedRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_DescriptorProto_ReservedRange.Marshal(b, m, deterministic) } -func (dst *DescriptorProto_ReservedRange) XXX_Merge(src proto.Message) { - xxx_messageInfo_DescriptorProto_ReservedRange.Merge(dst, src) +func (m *DescriptorProto_ReservedRange) XXX_Merge(src proto.Message) { + xxx_messageInfo_DescriptorProto_ReservedRange.Merge(m, src) } func (m *DescriptorProto_ReservedRange) XXX_Size() int { return xxx_messageInfo_DescriptorProto_ReservedRange.Size(m) @@ -725,7 +756,7 @@ func (m *ExtensionRangeOptions) Reset() { *m = ExtensionRangeOptions{} } func (m *ExtensionRangeOptions) String() string { return proto.CompactTextString(m) } func (*ExtensionRangeOptions) ProtoMessage() {} func (*ExtensionRangeOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{3} + return fileDescriptor_e5baabe45344a177, []int{3} } var extRange_ExtensionRangeOptions = []proto.ExtensionRange{ @@ -735,14 +766,15 @@ var extRange_ExtensionRangeOptions = []proto.ExtensionRange{ func (*ExtensionRangeOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_ExtensionRangeOptions } + func (m *ExtensionRangeOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ExtensionRangeOptions.Unmarshal(m, b) } func (m *ExtensionRangeOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_ExtensionRangeOptions.Marshal(b, m, deterministic) } -func (dst *ExtensionRangeOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_ExtensionRangeOptions.Merge(dst, src) +func (m *ExtensionRangeOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExtensionRangeOptions.Merge(m, src) } func (m *ExtensionRangeOptions) XXX_Size() int { return xxx_messageInfo_ExtensionRangeOptions.Size(m) @@ -801,16 +833,17 @@ func (m *FieldDescriptorProto) Reset() { *m = FieldDescriptorProto{} } func (m *FieldDescriptorProto) String() string { return proto.CompactTextString(m) } func (*FieldDescriptorProto) ProtoMessage() {} func (*FieldDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{4} + return fileDescriptor_e5baabe45344a177, []int{4} } + func (m *FieldDescriptorProto) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FieldDescriptorProto.Unmarshal(m, b) } func (m *FieldDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_FieldDescriptorProto.Marshal(b, m, deterministic) } -func (dst *FieldDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_FieldDescriptorProto.Merge(dst, src) +func (m *FieldDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_FieldDescriptorProto.Merge(m, src) } func (m *FieldDescriptorProto) XXX_Size() int { return xxx_messageInfo_FieldDescriptorProto.Size(m) @@ -904,16 +937,17 @@ func (m *OneofDescriptorProto) Reset() { *m = OneofDescriptorProto{} } func (m *OneofDescriptorProto) String() string { return proto.CompactTextString(m) } func (*OneofDescriptorProto) ProtoMessage() {} func (*OneofDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{5} + return fileDescriptor_e5baabe45344a177, []int{5} } + func (m *OneofDescriptorProto) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_OneofDescriptorProto.Unmarshal(m, b) } func (m *OneofDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_OneofDescriptorProto.Marshal(b, m, deterministic) } -func (dst *OneofDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_OneofDescriptorProto.Merge(dst, src) +func (m *OneofDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_OneofDescriptorProto.Merge(m, src) } func (m *OneofDescriptorProto) XXX_Size() int { return xxx_messageInfo_OneofDescriptorProto.Size(m) @@ -959,16 +993,17 @@ func (m *EnumDescriptorProto) Reset() { *m = EnumDescriptorProto{} } func (m *EnumDescriptorProto) String() string { return proto.CompactTextString(m) } func (*EnumDescriptorProto) ProtoMessage() {} func (*EnumDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{6} + return fileDescriptor_e5baabe45344a177, []int{6} } + func (m *EnumDescriptorProto) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EnumDescriptorProto.Unmarshal(m, b) } func (m *EnumDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_EnumDescriptorProto.Marshal(b, m, deterministic) } -func (dst *EnumDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnumDescriptorProto.Merge(dst, src) +func (m *EnumDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumDescriptorProto.Merge(m, src) } func (m *EnumDescriptorProto) XXX_Size() int { return xxx_messageInfo_EnumDescriptorProto.Size(m) @@ -1032,16 +1067,17 @@ func (m *EnumDescriptorProto_EnumReservedRange) Reset() { *m = EnumDescr func (m *EnumDescriptorProto_EnumReservedRange) String() string { return proto.CompactTextString(m) } func (*EnumDescriptorProto_EnumReservedRange) ProtoMessage() {} func (*EnumDescriptorProto_EnumReservedRange) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{6, 0} + return fileDescriptor_e5baabe45344a177, []int{6, 0} } + func (m *EnumDescriptorProto_EnumReservedRange) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Unmarshal(m, b) } func (m *EnumDescriptorProto_EnumReservedRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Marshal(b, m, deterministic) } -func (dst *EnumDescriptorProto_EnumReservedRange) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Merge(dst, src) +func (m *EnumDescriptorProto_EnumReservedRange) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Merge(m, src) } func (m *EnumDescriptorProto_EnumReservedRange) XXX_Size() int { return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Size(m) @@ -1080,16 +1116,17 @@ func (m *EnumValueDescriptorProto) Reset() { *m = EnumValueDescriptorPro func (m *EnumValueDescriptorProto) String() string { return proto.CompactTextString(m) } func (*EnumValueDescriptorProto) ProtoMessage() {} func (*EnumValueDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{7} + return fileDescriptor_e5baabe45344a177, []int{7} } + func (m *EnumValueDescriptorProto) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EnumValueDescriptorProto.Unmarshal(m, b) } func (m *EnumValueDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_EnumValueDescriptorProto.Marshal(b, m, deterministic) } -func (dst *EnumValueDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnumValueDescriptorProto.Merge(dst, src) +func (m *EnumValueDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumValueDescriptorProto.Merge(m, src) } func (m *EnumValueDescriptorProto) XXX_Size() int { return xxx_messageInfo_EnumValueDescriptorProto.Size(m) @@ -1135,16 +1172,17 @@ func (m *ServiceDescriptorProto) Reset() { *m = ServiceDescriptorProto{} func (m *ServiceDescriptorProto) String() string { return proto.CompactTextString(m) } func (*ServiceDescriptorProto) ProtoMessage() {} func (*ServiceDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{8} + return fileDescriptor_e5baabe45344a177, []int{8} } + func (m *ServiceDescriptorProto) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ServiceDescriptorProto.Unmarshal(m, b) } func (m *ServiceDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_ServiceDescriptorProto.Marshal(b, m, deterministic) } -func (dst *ServiceDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_ServiceDescriptorProto.Merge(dst, src) +func (m *ServiceDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServiceDescriptorProto.Merge(m, src) } func (m *ServiceDescriptorProto) XXX_Size() int { return xxx_messageInfo_ServiceDescriptorProto.Size(m) @@ -1197,16 +1235,17 @@ func (m *MethodDescriptorProto) Reset() { *m = MethodDescriptorProto{} } func (m *MethodDescriptorProto) String() string { return proto.CompactTextString(m) } func (*MethodDescriptorProto) ProtoMessage() {} func (*MethodDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{9} + return fileDescriptor_e5baabe45344a177, []int{9} } + func (m *MethodDescriptorProto) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_MethodDescriptorProto.Unmarshal(m, b) } func (m *MethodDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_MethodDescriptorProto.Marshal(b, m, deterministic) } -func (dst *MethodDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_MethodDescriptorProto.Merge(dst, src) +func (m *MethodDescriptorProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_MethodDescriptorProto.Merge(m, src) } func (m *MethodDescriptorProto) XXX_Size() int { return xxx_messageInfo_MethodDescriptorProto.Size(m) @@ -1336,6 +1375,14 @@ type FileOptions struct { // is empty. When this option is empty, the package name will be used for // determining the namespace. PhpNamespace *string `protobuf:"bytes,41,opt,name=php_namespace,json=phpNamespace" json:"php_namespace,omitempty"` + // Use this option to change the namespace of php generated metadata classes. + // Default is empty. When this option is empty, the proto file name will be + // used for determining the namespace. + PhpMetadataNamespace *string `protobuf:"bytes,44,opt,name=php_metadata_namespace,json=phpMetadataNamespace" json:"php_metadata_namespace,omitempty"` + // Use this option to change the package of ruby generated classes. Default + // is empty. When this option is not set, the package name will be used for + // determining the ruby package. + RubyPackage *string `protobuf:"bytes,45,opt,name=ruby_package,json=rubyPackage" json:"ruby_package,omitempty"` // The parser stores options it doesn't recognize here. // See the documentation for the "Options" section above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` @@ -1349,7 +1396,7 @@ func (m *FileOptions) Reset() { *m = FileOptions{} } func (m *FileOptions) String() string { return proto.CompactTextString(m) } func (*FileOptions) ProtoMessage() {} func (*FileOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{10} + return fileDescriptor_e5baabe45344a177, []int{10} } var extRange_FileOptions = []proto.ExtensionRange{ @@ -1359,14 +1406,15 @@ var extRange_FileOptions = []proto.ExtensionRange{ func (*FileOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_FileOptions } + func (m *FileOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FileOptions.Unmarshal(m, b) } func (m *FileOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_FileOptions.Marshal(b, m, deterministic) } -func (dst *FileOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_FileOptions.Merge(dst, src) +func (m *FileOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_FileOptions.Merge(m, src) } func (m *FileOptions) XXX_Size() int { return xxx_messageInfo_FileOptions.Size(m) @@ -1514,6 +1562,20 @@ func (m *FileOptions) GetPhpNamespace() string { return "" } +func (m *FileOptions) GetPhpMetadataNamespace() string { + if m != nil && m.PhpMetadataNamespace != nil { + return *m.PhpMetadataNamespace + } + return "" +} + +func (m *FileOptions) GetRubyPackage() string { + if m != nil && m.RubyPackage != nil { + return *m.RubyPackage + } + return "" +} + func (m *FileOptions) GetUninterpretedOption() []*UninterpretedOption { if m != nil { return m.UninterpretedOption @@ -1565,7 +1627,7 @@ type MessageOptions struct { // // Implementations may choose not to generate the map_entry=true message, but // use a native map in the target language to hold the keys and values. - // The reflection APIs in such implementions still need to work as + // The reflection APIs in such implementations still need to work as // if the field is a repeated message field. // // NOTE: Do not set the option in .proto files. Always use the maps syntax @@ -1584,7 +1646,7 @@ func (m *MessageOptions) Reset() { *m = MessageOptions{} } func (m *MessageOptions) String() string { return proto.CompactTextString(m) } func (*MessageOptions) ProtoMessage() {} func (*MessageOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{11} + return fileDescriptor_e5baabe45344a177, []int{11} } var extRange_MessageOptions = []proto.ExtensionRange{ @@ -1594,14 +1656,15 @@ var extRange_MessageOptions = []proto.ExtensionRange{ func (*MessageOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_MessageOptions } + func (m *MessageOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_MessageOptions.Unmarshal(m, b) } func (m *MessageOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_MessageOptions.Marshal(b, m, deterministic) } -func (dst *MessageOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_MessageOptions.Merge(dst, src) +func (m *MessageOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_MessageOptions.Merge(m, src) } func (m *MessageOptions) XXX_Size() int { return xxx_messageInfo_MessageOptions.Size(m) @@ -1723,7 +1786,7 @@ func (m *FieldOptions) Reset() { *m = FieldOptions{} } func (m *FieldOptions) String() string { return proto.CompactTextString(m) } func (*FieldOptions) ProtoMessage() {} func (*FieldOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{12} + return fileDescriptor_e5baabe45344a177, []int{12} } var extRange_FieldOptions = []proto.ExtensionRange{ @@ -1733,14 +1796,15 @@ var extRange_FieldOptions = []proto.ExtensionRange{ func (*FieldOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_FieldOptions } + func (m *FieldOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_FieldOptions.Unmarshal(m, b) } func (m *FieldOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_FieldOptions.Marshal(b, m, deterministic) } -func (dst *FieldOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_FieldOptions.Merge(dst, src) +func (m *FieldOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_FieldOptions.Merge(m, src) } func (m *FieldOptions) XXX_Size() int { return xxx_messageInfo_FieldOptions.Size(m) @@ -1819,7 +1883,7 @@ func (m *OneofOptions) Reset() { *m = OneofOptions{} } func (m *OneofOptions) String() string { return proto.CompactTextString(m) } func (*OneofOptions) ProtoMessage() {} func (*OneofOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{13} + return fileDescriptor_e5baabe45344a177, []int{13} } var extRange_OneofOptions = []proto.ExtensionRange{ @@ -1829,14 +1893,15 @@ var extRange_OneofOptions = []proto.ExtensionRange{ func (*OneofOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_OneofOptions } + func (m *OneofOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_OneofOptions.Unmarshal(m, b) } func (m *OneofOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_OneofOptions.Marshal(b, m, deterministic) } -func (dst *OneofOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_OneofOptions.Merge(dst, src) +func (m *OneofOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_OneofOptions.Merge(m, src) } func (m *OneofOptions) XXX_Size() int { return xxx_messageInfo_OneofOptions.Size(m) @@ -1875,7 +1940,7 @@ func (m *EnumOptions) Reset() { *m = EnumOptions{} } func (m *EnumOptions) String() string { return proto.CompactTextString(m) } func (*EnumOptions) ProtoMessage() {} func (*EnumOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{14} + return fileDescriptor_e5baabe45344a177, []int{14} } var extRange_EnumOptions = []proto.ExtensionRange{ @@ -1885,14 +1950,15 @@ var extRange_EnumOptions = []proto.ExtensionRange{ func (*EnumOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_EnumOptions } + func (m *EnumOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EnumOptions.Unmarshal(m, b) } func (m *EnumOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_EnumOptions.Marshal(b, m, deterministic) } -func (dst *EnumOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnumOptions.Merge(dst, src) +func (m *EnumOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumOptions.Merge(m, src) } func (m *EnumOptions) XXX_Size() int { return xxx_messageInfo_EnumOptions.Size(m) @@ -1944,7 +2010,7 @@ func (m *EnumValueOptions) Reset() { *m = EnumValueOptions{} } func (m *EnumValueOptions) String() string { return proto.CompactTextString(m) } func (*EnumValueOptions) ProtoMessage() {} func (*EnumValueOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{15} + return fileDescriptor_e5baabe45344a177, []int{15} } var extRange_EnumValueOptions = []proto.ExtensionRange{ @@ -1954,14 +2020,15 @@ var extRange_EnumValueOptions = []proto.ExtensionRange{ func (*EnumValueOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_EnumValueOptions } + func (m *EnumValueOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EnumValueOptions.Unmarshal(m, b) } func (m *EnumValueOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_EnumValueOptions.Marshal(b, m, deterministic) } -func (dst *EnumValueOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnumValueOptions.Merge(dst, src) +func (m *EnumValueOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_EnumValueOptions.Merge(m, src) } func (m *EnumValueOptions) XXX_Size() int { return xxx_messageInfo_EnumValueOptions.Size(m) @@ -2006,7 +2073,7 @@ func (m *ServiceOptions) Reset() { *m = ServiceOptions{} } func (m *ServiceOptions) String() string { return proto.CompactTextString(m) } func (*ServiceOptions) ProtoMessage() {} func (*ServiceOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{16} + return fileDescriptor_e5baabe45344a177, []int{16} } var extRange_ServiceOptions = []proto.ExtensionRange{ @@ -2016,14 +2083,15 @@ var extRange_ServiceOptions = []proto.ExtensionRange{ func (*ServiceOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_ServiceOptions } + func (m *ServiceOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ServiceOptions.Unmarshal(m, b) } func (m *ServiceOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_ServiceOptions.Marshal(b, m, deterministic) } -func (dst *ServiceOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_ServiceOptions.Merge(dst, src) +func (m *ServiceOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_ServiceOptions.Merge(m, src) } func (m *ServiceOptions) XXX_Size() int { return xxx_messageInfo_ServiceOptions.Size(m) @@ -2069,7 +2137,7 @@ func (m *MethodOptions) Reset() { *m = MethodOptions{} } func (m *MethodOptions) String() string { return proto.CompactTextString(m) } func (*MethodOptions) ProtoMessage() {} func (*MethodOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{17} + return fileDescriptor_e5baabe45344a177, []int{17} } var extRange_MethodOptions = []proto.ExtensionRange{ @@ -2079,14 +2147,15 @@ var extRange_MethodOptions = []proto.ExtensionRange{ func (*MethodOptions) ExtensionRangeArray() []proto.ExtensionRange { return extRange_MethodOptions } + func (m *MethodOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_MethodOptions.Unmarshal(m, b) } func (m *MethodOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_MethodOptions.Marshal(b, m, deterministic) } -func (dst *MethodOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_MethodOptions.Merge(dst, src) +func (m *MethodOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_MethodOptions.Merge(m, src) } func (m *MethodOptions) XXX_Size() int { return xxx_messageInfo_MethodOptions.Size(m) @@ -2146,16 +2215,17 @@ func (m *UninterpretedOption) Reset() { *m = UninterpretedOption{} } func (m *UninterpretedOption) String() string { return proto.CompactTextString(m) } func (*UninterpretedOption) ProtoMessage() {} func (*UninterpretedOption) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{18} + return fileDescriptor_e5baabe45344a177, []int{18} } + func (m *UninterpretedOption) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_UninterpretedOption.Unmarshal(m, b) } func (m *UninterpretedOption) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_UninterpretedOption.Marshal(b, m, deterministic) } -func (dst *UninterpretedOption) XXX_Merge(src proto.Message) { - xxx_messageInfo_UninterpretedOption.Merge(dst, src) +func (m *UninterpretedOption) XXX_Merge(src proto.Message) { + xxx_messageInfo_UninterpretedOption.Merge(m, src) } func (m *UninterpretedOption) XXX_Size() int { return xxx_messageInfo_UninterpretedOption.Size(m) @@ -2232,16 +2302,17 @@ func (m *UninterpretedOption_NamePart) Reset() { *m = UninterpretedOptio func (m *UninterpretedOption_NamePart) String() string { return proto.CompactTextString(m) } func (*UninterpretedOption_NamePart) ProtoMessage() {} func (*UninterpretedOption_NamePart) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{18, 0} + return fileDescriptor_e5baabe45344a177, []int{18, 0} } + func (m *UninterpretedOption_NamePart) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_UninterpretedOption_NamePart.Unmarshal(m, b) } func (m *UninterpretedOption_NamePart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_UninterpretedOption_NamePart.Marshal(b, m, deterministic) } -func (dst *UninterpretedOption_NamePart) XXX_Merge(src proto.Message) { - xxx_messageInfo_UninterpretedOption_NamePart.Merge(dst, src) +func (m *UninterpretedOption_NamePart) XXX_Merge(src proto.Message) { + xxx_messageInfo_UninterpretedOption_NamePart.Merge(m, src) } func (m *UninterpretedOption_NamePart) XXX_Size() int { return xxx_messageInfo_UninterpretedOption_NamePart.Size(m) @@ -2306,7 +2377,7 @@ type SourceCodeInfo struct { // beginning of the "extend" block and is shared by all extensions within // the block. // - Just because a location's span is a subset of some other location's span - // does not mean that it is a descendent. For example, a "group" defines + // does not mean that it is a descendant. For example, a "group" defines // both a type and a field in a single declaration. Thus, the locations // corresponding to the type and field and their components will overlap. // - Code which tries to interpret locations should probably be designed to @@ -2322,16 +2393,17 @@ func (m *SourceCodeInfo) Reset() { *m = SourceCodeInfo{} } func (m *SourceCodeInfo) String() string { return proto.CompactTextString(m) } func (*SourceCodeInfo) ProtoMessage() {} func (*SourceCodeInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{19} + return fileDescriptor_e5baabe45344a177, []int{19} } + func (m *SourceCodeInfo) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SourceCodeInfo.Unmarshal(m, b) } func (m *SourceCodeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_SourceCodeInfo.Marshal(b, m, deterministic) } -func (dst *SourceCodeInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_SourceCodeInfo.Merge(dst, src) +func (m *SourceCodeInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_SourceCodeInfo.Merge(m, src) } func (m *SourceCodeInfo) XXX_Size() int { return xxx_messageInfo_SourceCodeInfo.Size(m) @@ -2439,16 +2511,17 @@ func (m *SourceCodeInfo_Location) Reset() { *m = SourceCodeInfo_Location func (m *SourceCodeInfo_Location) String() string { return proto.CompactTextString(m) } func (*SourceCodeInfo_Location) ProtoMessage() {} func (*SourceCodeInfo_Location) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{19, 0} + return fileDescriptor_e5baabe45344a177, []int{19, 0} } + func (m *SourceCodeInfo_Location) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SourceCodeInfo_Location.Unmarshal(m, b) } func (m *SourceCodeInfo_Location) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_SourceCodeInfo_Location.Marshal(b, m, deterministic) } -func (dst *SourceCodeInfo_Location) XXX_Merge(src proto.Message) { - xxx_messageInfo_SourceCodeInfo_Location.Merge(dst, src) +func (m *SourceCodeInfo_Location) XXX_Merge(src proto.Message) { + xxx_messageInfo_SourceCodeInfo_Location.Merge(m, src) } func (m *SourceCodeInfo_Location) XXX_Size() int { return xxx_messageInfo_SourceCodeInfo_Location.Size(m) @@ -2510,16 +2583,17 @@ func (m *GeneratedCodeInfo) Reset() { *m = GeneratedCodeInfo{} } func (m *GeneratedCodeInfo) String() string { return proto.CompactTextString(m) } func (*GeneratedCodeInfo) ProtoMessage() {} func (*GeneratedCodeInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{20} + return fileDescriptor_e5baabe45344a177, []int{20} } + func (m *GeneratedCodeInfo) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GeneratedCodeInfo.Unmarshal(m, b) } func (m *GeneratedCodeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_GeneratedCodeInfo.Marshal(b, m, deterministic) } -func (dst *GeneratedCodeInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_GeneratedCodeInfo.Merge(dst, src) +func (m *GeneratedCodeInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_GeneratedCodeInfo.Merge(m, src) } func (m *GeneratedCodeInfo) XXX_Size() int { return xxx_messageInfo_GeneratedCodeInfo.Size(m) @@ -2559,16 +2633,17 @@ func (m *GeneratedCodeInfo_Annotation) Reset() { *m = GeneratedCodeInfo_ func (m *GeneratedCodeInfo_Annotation) String() string { return proto.CompactTextString(m) } func (*GeneratedCodeInfo_Annotation) ProtoMessage() {} func (*GeneratedCodeInfo_Annotation) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{20, 0} + return fileDescriptor_e5baabe45344a177, []int{20, 0} } + func (m *GeneratedCodeInfo_Annotation) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GeneratedCodeInfo_Annotation.Unmarshal(m, b) } func (m *GeneratedCodeInfo_Annotation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_GeneratedCodeInfo_Annotation.Marshal(b, m, deterministic) } -func (dst *GeneratedCodeInfo_Annotation) XXX_Merge(src proto.Message) { - xxx_messageInfo_GeneratedCodeInfo_Annotation.Merge(dst, src) +func (m *GeneratedCodeInfo_Annotation) XXX_Merge(src proto.Message) { + xxx_messageInfo_GeneratedCodeInfo_Annotation.Merge(m, src) } func (m *GeneratedCodeInfo_Annotation) XXX_Size() int { return xxx_messageInfo_GeneratedCodeInfo_Annotation.Size(m) @@ -2608,6 +2683,12 @@ func (m *GeneratedCodeInfo_Annotation) GetEnd() int32 { } func init() { + proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Type", FieldDescriptorProto_Type_name, FieldDescriptorProto_Type_value) + proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Label", FieldDescriptorProto_Label_name, FieldDescriptorProto_Label_value) + proto.RegisterEnum("google.protobuf.FileOptions_OptimizeMode", FileOptions_OptimizeMode_name, FileOptions_OptimizeMode_value) + proto.RegisterEnum("google.protobuf.FieldOptions_CType", FieldOptions_CType_name, FieldOptions_CType_value) + proto.RegisterEnum("google.protobuf.FieldOptions_JSType", FieldOptions_JSType_name, FieldOptions_JSType_value) + proto.RegisterEnum("google.protobuf.MethodOptions_IdempotencyLevel", MethodOptions_IdempotencyLevel_name, MethodOptions_IdempotencyLevel_value) proto.RegisterType((*FileDescriptorSet)(nil), "google.protobuf.FileDescriptorSet") proto.RegisterType((*FileDescriptorProto)(nil), "google.protobuf.FileDescriptorProto") proto.RegisterType((*DescriptorProto)(nil), "google.protobuf.DescriptorProto") @@ -2635,178 +2716,174 @@ func init() { proto.RegisterType((*SourceCodeInfo_Location)(nil), "google.protobuf.SourceCodeInfo.Location") proto.RegisterType((*GeneratedCodeInfo)(nil), "google.protobuf.GeneratedCodeInfo") proto.RegisterType((*GeneratedCodeInfo_Annotation)(nil), "google.protobuf.GeneratedCodeInfo.Annotation") - proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Type", FieldDescriptorProto_Type_name, FieldDescriptorProto_Type_value) - proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Label", FieldDescriptorProto_Label_name, FieldDescriptorProto_Label_value) - proto.RegisterEnum("google.protobuf.FileOptions_OptimizeMode", FileOptions_OptimizeMode_name, FileOptions_OptimizeMode_value) - proto.RegisterEnum("google.protobuf.FieldOptions_CType", FieldOptions_CType_name, FieldOptions_CType_value) - proto.RegisterEnum("google.protobuf.FieldOptions_JSType", FieldOptions_JSType_name, FieldOptions_JSType_value) - proto.RegisterEnum("google.protobuf.MethodOptions_IdempotencyLevel", MethodOptions_IdempotencyLevel_name, MethodOptions_IdempotencyLevel_value) } func init() { - proto.RegisterFile("google/protobuf/descriptor.proto", fileDescriptor_descriptor_4df4cb5f42392df6) -} - -var fileDescriptor_descriptor_4df4cb5f42392df6 = []byte{ - // 2555 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0xdd, 0x6e, 0x1b, 0xc7, - 0xf5, 0xcf, 0xf2, 0x4b, 0xe4, 0x21, 0x45, 0x8d, 0x46, 0x8a, 0xbd, 0x56, 0x3e, 0x2c, 0x33, 0x1f, - 0x96, 0x9d, 0x7f, 0xa8, 0xc0, 0xb1, 0x1d, 0x47, 0xfe, 0x23, 0x2d, 0x45, 0xae, 0x15, 0xaa, 0x12, - 0xc9, 0x2e, 0xa9, 0xe6, 0x03, 0x28, 0x16, 0xa3, 0xdd, 0x21, 0xb9, 0xf6, 0x72, 0x77, 0xb3, 0xbb, - 0xb4, 0xad, 0xa0, 0x17, 0x06, 0x7a, 0xd5, 0xab, 0xde, 0x16, 0x45, 0xd1, 0x8b, 0xde, 0x04, 0xe8, - 0x03, 0x14, 0xc8, 0x5d, 0x9f, 0xa0, 0x40, 0xde, 0xa0, 0x68, 0x0b, 0xb4, 0x8f, 0xd0, 0xcb, 0x62, - 0x66, 0x76, 0x97, 0xbb, 0x24, 0x15, 0x2b, 0x01, 0xe2, 0x5c, 0x91, 0xf3, 0x9b, 0xdf, 0x39, 0x73, - 0xe6, 0xcc, 0x99, 0x33, 0x67, 0x66, 0x61, 0x7b, 0xe4, 0x38, 0x23, 0x8b, 0xee, 0xba, 0x9e, 0x13, - 0x38, 0xa7, 0xd3, 0xe1, 0xae, 0x41, 0x7d, 0xdd, 0x33, 0xdd, 0xc0, 0xf1, 0xea, 0x1c, 0xc3, 0x6b, - 0x82, 0x51, 0x8f, 0x18, 0xb5, 0x63, 0x58, 0x7f, 0x60, 0x5a, 0xb4, 0x15, 0x13, 0xfb, 0x34, 0xc0, - 0xf7, 0x20, 0x37, 0x34, 0x2d, 0x2a, 0x4b, 0xdb, 0xd9, 0x9d, 0xf2, 0xad, 0x37, 0xeb, 0x73, 0x42, - 0xf5, 0xb4, 0x44, 0x8f, 0xc1, 0x2a, 0x97, 0xa8, 0xfd, 0x2b, 0x07, 0x1b, 0x4b, 0x7a, 0x31, 0x86, - 0x9c, 0x4d, 0x26, 0x4c, 0xa3, 0xb4, 0x53, 0x52, 0xf9, 0x7f, 0x2c, 0xc3, 0x8a, 0x4b, 0xf4, 0x47, - 0x64, 0x44, 0xe5, 0x0c, 0x87, 0xa3, 0x26, 0x7e, 0x1d, 0xc0, 0xa0, 0x2e, 0xb5, 0x0d, 0x6a, 0xeb, - 0x67, 0x72, 0x76, 0x3b, 0xbb, 0x53, 0x52, 0x13, 0x08, 0x7e, 0x07, 0xd6, 0xdd, 0xe9, 0xa9, 0x65, - 0xea, 0x5a, 0x82, 0x06, 0xdb, 0xd9, 0x9d, 0xbc, 0x8a, 0x44, 0x47, 0x6b, 0x46, 0xbe, 0x0e, 0x6b, - 0x4f, 0x28, 0x79, 0x94, 0xa4, 0x96, 0x39, 0xb5, 0xca, 0xe0, 0x04, 0xb1, 0x09, 0x95, 0x09, 0xf5, - 0x7d, 0x32, 0xa2, 0x5a, 0x70, 0xe6, 0x52, 0x39, 0xc7, 0x67, 0xbf, 0xbd, 0x30, 0xfb, 0xf9, 0x99, - 0x97, 0x43, 0xa9, 0xc1, 0x99, 0x4b, 0x71, 0x03, 0x4a, 0xd4, 0x9e, 0x4e, 0x84, 0x86, 0xfc, 0x39, - 0xfe, 0x53, 0xec, 0xe9, 0x64, 0x5e, 0x4b, 0x91, 0x89, 0x85, 0x2a, 0x56, 0x7c, 0xea, 0x3d, 0x36, - 0x75, 0x2a, 0x17, 0xb8, 0x82, 0xeb, 0x0b, 0x0a, 0xfa, 0xa2, 0x7f, 0x5e, 0x47, 0x24, 0x87, 0x9b, - 0x50, 0xa2, 0x4f, 0x03, 0x6a, 0xfb, 0xa6, 0x63, 0xcb, 0x2b, 0x5c, 0xc9, 0x5b, 0x4b, 0x56, 0x91, - 0x5a, 0xc6, 0xbc, 0x8a, 0x99, 0x1c, 0xbe, 0x0b, 0x2b, 0x8e, 0x1b, 0x98, 0x8e, 0xed, 0xcb, 0xc5, - 0x6d, 0x69, 0xa7, 0x7c, 0xeb, 0xd5, 0xa5, 0x81, 0xd0, 0x15, 0x1c, 0x35, 0x22, 0xe3, 0x36, 0x20, - 0xdf, 0x99, 0x7a, 0x3a, 0xd5, 0x74, 0xc7, 0xa0, 0x9a, 0x69, 0x0f, 0x1d, 0xb9, 0xc4, 0x15, 0x5c, - 0x5d, 0x9c, 0x08, 0x27, 0x36, 0x1d, 0x83, 0xb6, 0xed, 0xa1, 0xa3, 0x56, 0xfd, 0x54, 0x1b, 0x5f, - 0x82, 0x82, 0x7f, 0x66, 0x07, 0xe4, 0xa9, 0x5c, 0xe1, 0x11, 0x12, 0xb6, 0x6a, 0x5f, 0x17, 0x60, - 0xed, 0x22, 0x21, 0x76, 0x1f, 0xf2, 0x43, 0x36, 0x4b, 0x39, 0xf3, 0x5d, 0x7c, 0x20, 0x64, 0xd2, - 0x4e, 0x2c, 0x7c, 0x4f, 0x27, 0x36, 0xa0, 0x6c, 0x53, 0x3f, 0xa0, 0x86, 0x88, 0x88, 0xec, 0x05, - 0x63, 0x0a, 0x84, 0xd0, 0x62, 0x48, 0xe5, 0xbe, 0x57, 0x48, 0x7d, 0x0a, 0x6b, 0xb1, 0x49, 0x9a, - 0x47, 0xec, 0x51, 0x14, 0x9b, 0xbb, 0xcf, 0xb3, 0xa4, 0xae, 0x44, 0x72, 0x2a, 0x13, 0x53, 0xab, - 0x34, 0xd5, 0xc6, 0x2d, 0x00, 0xc7, 0xa6, 0xce, 0x50, 0x33, 0xa8, 0x6e, 0xc9, 0xc5, 0x73, 0xbc, - 0xd4, 0x65, 0x94, 0x05, 0x2f, 0x39, 0x02, 0xd5, 0x2d, 0xfc, 0xe1, 0x2c, 0xd4, 0x56, 0xce, 0x89, - 0x94, 0x63, 0xb1, 0xc9, 0x16, 0xa2, 0xed, 0x04, 0xaa, 0x1e, 0x65, 0x71, 0x4f, 0x8d, 0x70, 0x66, - 0x25, 0x6e, 0x44, 0xfd, 0xb9, 0x33, 0x53, 0x43, 0x31, 0x31, 0xb1, 0x55, 0x2f, 0xd9, 0xc4, 0x6f, - 0x40, 0x0c, 0x68, 0x3c, 0xac, 0x80, 0x67, 0xa1, 0x4a, 0x04, 0x76, 0xc8, 0x84, 0x6e, 0x7d, 0x09, - 0xd5, 0xb4, 0x7b, 0xf0, 0x26, 0xe4, 0xfd, 0x80, 0x78, 0x01, 0x8f, 0xc2, 0xbc, 0x2a, 0x1a, 0x18, - 0x41, 0x96, 0xda, 0x06, 0xcf, 0x72, 0x79, 0x95, 0xfd, 0xc5, 0x3f, 0x9d, 0x4d, 0x38, 0xcb, 0x27, - 0xfc, 0xf6, 0xe2, 0x8a, 0xa6, 0x34, 0xcf, 0xcf, 0x7b, 0xeb, 0x03, 0x58, 0x4d, 0x4d, 0xe0, 0xa2, - 0x43, 0xd7, 0x7e, 0x05, 0x2f, 0x2f, 0x55, 0x8d, 0x3f, 0x85, 0xcd, 0xa9, 0x6d, 0xda, 0x01, 0xf5, - 0x5c, 0x8f, 0xb2, 0x88, 0x15, 0x43, 0xc9, 0xff, 0x5e, 0x39, 0x27, 0xe6, 0x4e, 0x92, 0x6c, 0xa1, - 0x45, 0xdd, 0x98, 0x2e, 0x82, 0x37, 0x4b, 0xc5, 0xff, 0xac, 0xa0, 0x67, 0xcf, 0x9e, 0x3d, 0xcb, - 0xd4, 0x7e, 0x57, 0x80, 0xcd, 0x65, 0x7b, 0x66, 0xe9, 0xf6, 0xbd, 0x04, 0x05, 0x7b, 0x3a, 0x39, - 0xa5, 0x1e, 0x77, 0x52, 0x5e, 0x0d, 0x5b, 0xb8, 0x01, 0x79, 0x8b, 0x9c, 0x52, 0x4b, 0xce, 0x6d, - 0x4b, 0x3b, 0xd5, 0x5b, 0xef, 0x5c, 0x68, 0x57, 0xd6, 0x8f, 0x98, 0x88, 0x2a, 0x24, 0xf1, 0x47, - 0x90, 0x0b, 0x53, 0x34, 0xd3, 0x70, 0xf3, 0x62, 0x1a, 0xd8, 0x5e, 0x52, 0xb9, 0x1c, 0x7e, 0x05, - 0x4a, 0xec, 0x57, 0xc4, 0x46, 0x81, 0xdb, 0x5c, 0x64, 0x00, 0x8b, 0x0b, 0xbc, 0x05, 0x45, 0xbe, - 0x4d, 0x0c, 0x1a, 0x1d, 0x6d, 0x71, 0x9b, 0x05, 0x96, 0x41, 0x87, 0x64, 0x6a, 0x05, 0xda, 0x63, - 0x62, 0x4d, 0x29, 0x0f, 0xf8, 0x92, 0x5a, 0x09, 0xc1, 0x5f, 0x30, 0x0c, 0x5f, 0x85, 0xb2, 0xd8, - 0x55, 0xa6, 0x6d, 0xd0, 0xa7, 0x3c, 0x7b, 0xe6, 0x55, 0xb1, 0xd1, 0xda, 0x0c, 0x61, 0xc3, 0x3f, - 0xf4, 0x1d, 0x3b, 0x0a, 0x4d, 0x3e, 0x04, 0x03, 0xf8, 0xf0, 0x1f, 0xcc, 0x27, 0xee, 0xd7, 0x96, - 0x4f, 0x6f, 0x3e, 0xa6, 0x6a, 0x7f, 0xc9, 0x40, 0x8e, 0xe7, 0x8b, 0x35, 0x28, 0x0f, 0x3e, 0xeb, - 0x29, 0x5a, 0xab, 0x7b, 0xb2, 0x7f, 0xa4, 0x20, 0x09, 0x57, 0x01, 0x38, 0xf0, 0xe0, 0xa8, 0xdb, - 0x18, 0xa0, 0x4c, 0xdc, 0x6e, 0x77, 0x06, 0x77, 0x6f, 0xa3, 0x6c, 0x2c, 0x70, 0x22, 0x80, 0x5c, - 0x92, 0xf0, 0xfe, 0x2d, 0x94, 0xc7, 0x08, 0x2a, 0x42, 0x41, 0xfb, 0x53, 0xa5, 0x75, 0xf7, 0x36, - 0x2a, 0xa4, 0x91, 0xf7, 0x6f, 0xa1, 0x15, 0xbc, 0x0a, 0x25, 0x8e, 0xec, 0x77, 0xbb, 0x47, 0xa8, - 0x18, 0xeb, 0xec, 0x0f, 0xd4, 0x76, 0xe7, 0x00, 0x95, 0x62, 0x9d, 0x07, 0x6a, 0xf7, 0xa4, 0x87, - 0x20, 0xd6, 0x70, 0xac, 0xf4, 0xfb, 0x8d, 0x03, 0x05, 0x95, 0x63, 0xc6, 0xfe, 0x67, 0x03, 0xa5, - 0x8f, 0x2a, 0x29, 0xb3, 0xde, 0xbf, 0x85, 0x56, 0xe3, 0x21, 0x94, 0xce, 0xc9, 0x31, 0xaa, 0xe2, - 0x75, 0x58, 0x15, 0x43, 0x44, 0x46, 0xac, 0xcd, 0x41, 0x77, 0x6f, 0x23, 0x34, 0x33, 0x44, 0x68, - 0x59, 0x4f, 0x01, 0x77, 0x6f, 0x23, 0x5c, 0x6b, 0x42, 0x9e, 0x47, 0x17, 0xc6, 0x50, 0x3d, 0x6a, - 0xec, 0x2b, 0x47, 0x5a, 0xb7, 0x37, 0x68, 0x77, 0x3b, 0x8d, 0x23, 0x24, 0xcd, 0x30, 0x55, 0xf9, - 0xf9, 0x49, 0x5b, 0x55, 0x5a, 0x28, 0x93, 0xc4, 0x7a, 0x4a, 0x63, 0xa0, 0xb4, 0x50, 0xb6, 0xa6, - 0xc3, 0xe6, 0xb2, 0x3c, 0xb9, 0x74, 0x67, 0x24, 0x96, 0x38, 0x73, 0xce, 0x12, 0x73, 0x5d, 0x0b, - 0x4b, 0xfc, 0xcf, 0x0c, 0x6c, 0x2c, 0x39, 0x2b, 0x96, 0x0e, 0xf2, 0x13, 0xc8, 0x8b, 0x10, 0x15, - 0xa7, 0xe7, 0x8d, 0xa5, 0x87, 0x0e, 0x0f, 0xd8, 0x85, 0x13, 0x94, 0xcb, 0x25, 0x2b, 0x88, 0xec, - 0x39, 0x15, 0x04, 0x53, 0xb1, 0x90, 0xd3, 0x7f, 0xb9, 0x90, 0xd3, 0xc5, 0xb1, 0x77, 0xf7, 0x22, - 0xc7, 0x1e, 0xc7, 0xbe, 0x5b, 0x6e, 0xcf, 0x2f, 0xc9, 0xed, 0xf7, 0x61, 0x7d, 0x41, 0xd1, 0x85, - 0x73, 0xec, 0xaf, 0x25, 0x90, 0xcf, 0x73, 0xce, 0x73, 0x32, 0x5d, 0x26, 0x95, 0xe9, 0xee, 0xcf, - 0x7b, 0xf0, 0xda, 0xf9, 0x8b, 0xb0, 0xb0, 0xd6, 0x5f, 0x49, 0x70, 0x69, 0x79, 0xa5, 0xb8, 0xd4, - 0x86, 0x8f, 0xa0, 0x30, 0xa1, 0xc1, 0xd8, 0x89, 0xaa, 0xa5, 0xb7, 0x97, 0x9c, 0xc1, 0xac, 0x7b, - 0x7e, 0xb1, 0x43, 0xa9, 0xe4, 0x21, 0x9e, 0x3d, 0xaf, 0xdc, 0x13, 0xd6, 0x2c, 0x58, 0xfa, 0x9b, - 0x0c, 0xbc, 0xbc, 0x54, 0xf9, 0x52, 0x43, 0x5f, 0x03, 0x30, 0x6d, 0x77, 0x1a, 0x88, 0x8a, 0x48, - 0x24, 0xd8, 0x12, 0x47, 0x78, 0xf2, 0x62, 0xc9, 0x73, 0x1a, 0xc4, 0xfd, 0x59, 0xde, 0x0f, 0x02, - 0xe2, 0x84, 0x7b, 0x33, 0x43, 0x73, 0xdc, 0xd0, 0xd7, 0xcf, 0x99, 0xe9, 0x42, 0x60, 0xbe, 0x07, - 0x48, 0xb7, 0x4c, 0x6a, 0x07, 0x9a, 0x1f, 0x78, 0x94, 0x4c, 0x4c, 0x7b, 0xc4, 0x4f, 0x90, 0xe2, - 0x5e, 0x7e, 0x48, 0x2c, 0x9f, 0xaa, 0x6b, 0xa2, 0xbb, 0x1f, 0xf5, 0x32, 0x09, 0x1e, 0x40, 0x5e, - 0x42, 0xa2, 0x90, 0x92, 0x10, 0xdd, 0xb1, 0x44, 0xed, 0xeb, 0x22, 0x94, 0x13, 0x75, 0x35, 0xbe, - 0x06, 0x95, 0x87, 0xe4, 0x31, 0xd1, 0xa2, 0xbb, 0x92, 0xf0, 0x44, 0x99, 0x61, 0xbd, 0xf0, 0xbe, - 0xf4, 0x1e, 0x6c, 0x72, 0x8a, 0x33, 0x0d, 0xa8, 0xa7, 0xe9, 0x16, 0xf1, 0x7d, 0xee, 0xb4, 0x22, - 0xa7, 0x62, 0xd6, 0xd7, 0x65, 0x5d, 0xcd, 0xa8, 0x07, 0xdf, 0x81, 0x0d, 0x2e, 0x31, 0x99, 0x5a, - 0x81, 0xe9, 0x5a, 0x54, 0x63, 0xb7, 0x37, 0x9f, 0x9f, 0x24, 0xb1, 0x65, 0xeb, 0x8c, 0x71, 0x1c, - 0x12, 0x98, 0x45, 0x3e, 0x6e, 0xc1, 0x6b, 0x5c, 0x6c, 0x44, 0x6d, 0xea, 0x91, 0x80, 0x6a, 0xf4, - 0x8b, 0x29, 0xb1, 0x7c, 0x8d, 0xd8, 0x86, 0x36, 0x26, 0xfe, 0x58, 0xde, 0x64, 0x0a, 0xf6, 0x33, - 0xb2, 0xa4, 0x5e, 0x61, 0xc4, 0x83, 0x90, 0xa7, 0x70, 0x5a, 0xc3, 0x36, 0x3e, 0x26, 0xfe, 0x18, - 0xef, 0xc1, 0x25, 0xae, 0xc5, 0x0f, 0x3c, 0xd3, 0x1e, 0x69, 0xfa, 0x98, 0xea, 0x8f, 0xb4, 0x69, - 0x30, 0xbc, 0x27, 0xbf, 0x92, 0x1c, 0x9f, 0x5b, 0xd8, 0xe7, 0x9c, 0x26, 0xa3, 0x9c, 0x04, 0xc3, - 0x7b, 0xb8, 0x0f, 0x15, 0xb6, 0x18, 0x13, 0xf3, 0x4b, 0xaa, 0x0d, 0x1d, 0x8f, 0x1f, 0x8d, 0xd5, - 0x25, 0xa9, 0x29, 0xe1, 0xc1, 0x7a, 0x37, 0x14, 0x38, 0x76, 0x0c, 0xba, 0x97, 0xef, 0xf7, 0x14, - 0xa5, 0xa5, 0x96, 0x23, 0x2d, 0x0f, 0x1c, 0x8f, 0x05, 0xd4, 0xc8, 0x89, 0x1d, 0x5c, 0x16, 0x01, - 0x35, 0x72, 0x22, 0xf7, 0xde, 0x81, 0x0d, 0x5d, 0x17, 0x73, 0x36, 0x75, 0x2d, 0xbc, 0x63, 0xf9, - 0x32, 0x4a, 0x39, 0x4b, 0xd7, 0x0f, 0x04, 0x21, 0x8c, 0x71, 0x1f, 0x7f, 0x08, 0x2f, 0xcf, 0x9c, - 0x95, 0x14, 0x5c, 0x5f, 0x98, 0xe5, 0xbc, 0xe8, 0x1d, 0xd8, 0x70, 0xcf, 0x16, 0x05, 0x71, 0x6a, - 0x44, 0xf7, 0x6c, 0x5e, 0xec, 0x03, 0xd8, 0x74, 0xc7, 0xee, 0xa2, 0xdc, 0xcd, 0xa4, 0x1c, 0x76, - 0xc7, 0xee, 0xbc, 0xe0, 0x5b, 0xfc, 0xc2, 0xed, 0x51, 0x9d, 0x04, 0xd4, 0x90, 0x2f, 0x27, 0xe9, - 0x89, 0x0e, 0xbc, 0x0b, 0x48, 0xd7, 0x35, 0x6a, 0x93, 0x53, 0x8b, 0x6a, 0xc4, 0xa3, 0x36, 0xf1, - 0xe5, 0xab, 0x49, 0x72, 0x55, 0xd7, 0x15, 0xde, 0xdb, 0xe0, 0x9d, 0xf8, 0x26, 0xac, 0x3b, 0xa7, - 0x0f, 0x75, 0x11, 0x92, 0x9a, 0xeb, 0xd1, 0xa1, 0xf9, 0x54, 0x7e, 0x93, 0xfb, 0x77, 0x8d, 0x75, - 0xf0, 0x80, 0xec, 0x71, 0x18, 0xdf, 0x00, 0xa4, 0xfb, 0x63, 0xe2, 0xb9, 0x3c, 0x27, 0xfb, 0x2e, - 0xd1, 0xa9, 0xfc, 0x96, 0xa0, 0x0a, 0xbc, 0x13, 0xc1, 0x6c, 0x4b, 0xf8, 0x4f, 0xcc, 0x61, 0x10, - 0x69, 0xbc, 0x2e, 0xb6, 0x04, 0xc7, 0x42, 0x6d, 0x3b, 0x80, 0x98, 0x2b, 0x52, 0x03, 0xef, 0x70, - 0x5a, 0xd5, 0x1d, 0xbb, 0xc9, 0x71, 0xdf, 0x80, 0x55, 0xc6, 0x9c, 0x0d, 0x7a, 0x43, 0x14, 0x64, - 0xee, 0x38, 0x31, 0xe2, 0x0f, 0x56, 0x1b, 0xd7, 0xf6, 0xa0, 0x92, 0x8c, 0x4f, 0x5c, 0x02, 0x11, - 0xa1, 0x48, 0x62, 0xc5, 0x4a, 0xb3, 0xdb, 0x62, 0x65, 0xc6, 0xe7, 0x0a, 0xca, 0xb0, 0x72, 0xe7, - 0xa8, 0x3d, 0x50, 0x34, 0xf5, 0xa4, 0x33, 0x68, 0x1f, 0x2b, 0x28, 0x9b, 0xa8, 0xab, 0x0f, 0x73, - 0xc5, 0xb7, 0xd1, 0xf5, 0xda, 0x37, 0x19, 0xa8, 0xa6, 0x2f, 0x4a, 0xf8, 0xff, 0xe1, 0x72, 0xf4, - 0xaa, 0xe1, 0xd3, 0x40, 0x7b, 0x62, 0x7a, 0x7c, 0xe3, 0x4c, 0x88, 0x38, 0xc4, 0xe2, 0xa5, 0xdb, - 0x0c, 0x59, 0x7d, 0x1a, 0x7c, 0x62, 0x7a, 0x6c, 0x5b, 0x4c, 0x48, 0x80, 0x8f, 0xe0, 0xaa, 0xed, - 0x68, 0x7e, 0x40, 0x6c, 0x83, 0x78, 0x86, 0x36, 0x7b, 0x4f, 0xd2, 0x88, 0xae, 0x53, 0xdf, 0x77, - 0xc4, 0x81, 0x15, 0x6b, 0x79, 0xd5, 0x76, 0xfa, 0x21, 0x79, 0x96, 0xc9, 0x1b, 0x21, 0x75, 0x2e, - 0xcc, 0xb2, 0xe7, 0x85, 0xd9, 0x2b, 0x50, 0x9a, 0x10, 0x57, 0xa3, 0x76, 0xe0, 0x9d, 0xf1, 0xf2, - 0xb8, 0xa8, 0x16, 0x27, 0xc4, 0x55, 0x58, 0xfb, 0x85, 0xdc, 0x52, 0x0e, 0x73, 0xc5, 0x22, 0x2a, - 0x1d, 0xe6, 0x8a, 0x25, 0x04, 0xb5, 0x7f, 0x64, 0xa1, 0x92, 0x2c, 0x97, 0xd9, 0xed, 0x43, 0xe7, - 0x27, 0x8b, 0xc4, 0x73, 0xcf, 0x1b, 0xdf, 0x5a, 0x5c, 0xd7, 0x9b, 0xec, 0xc8, 0xd9, 0x2b, 0x88, - 0x22, 0x56, 0x15, 0x92, 0xec, 0xb8, 0x67, 0xd9, 0x86, 0x8a, 0xa2, 0xa1, 0xa8, 0x86, 0x2d, 0x7c, - 0x00, 0x85, 0x87, 0x3e, 0xd7, 0x5d, 0xe0, 0xba, 0xdf, 0xfc, 0x76, 0xdd, 0x87, 0x7d, 0xae, 0xbc, - 0x74, 0xd8, 0xd7, 0x3a, 0x5d, 0xf5, 0xb8, 0x71, 0xa4, 0x86, 0xe2, 0xf8, 0x0a, 0xe4, 0x2c, 0xf2, - 0xe5, 0x59, 0xfa, 0x70, 0xe2, 0xd0, 0x45, 0x17, 0xe1, 0x0a, 0xe4, 0x9e, 0x50, 0xf2, 0x28, 0x7d, - 0x24, 0x70, 0xe8, 0x07, 0xdc, 0x0c, 0xbb, 0x90, 0xe7, 0xfe, 0xc2, 0x00, 0xa1, 0xc7, 0xd0, 0x4b, - 0xb8, 0x08, 0xb9, 0x66, 0x57, 0x65, 0x1b, 0x02, 0x41, 0x45, 0xa0, 0x5a, 0xaf, 0xad, 0x34, 0x15, - 0x94, 0xa9, 0xdd, 0x81, 0x82, 0x70, 0x02, 0xdb, 0x2c, 0xb1, 0x1b, 0xd0, 0x4b, 0x61, 0x33, 0xd4, - 0x21, 0x45, 0xbd, 0x27, 0xc7, 0xfb, 0x8a, 0x8a, 0x32, 0xe9, 0xa5, 0xce, 0xa1, 0x7c, 0xcd, 0x87, - 0x4a, 0xb2, 0x5e, 0x7e, 0x31, 0x77, 0xe1, 0xbf, 0x4a, 0x50, 0x4e, 0xd4, 0xbf, 0xac, 0x70, 0x21, - 0x96, 0xe5, 0x3c, 0xd1, 0x88, 0x65, 0x12, 0x3f, 0x0c, 0x0d, 0xe0, 0x50, 0x83, 0x21, 0x17, 0x5d, - 0xba, 0x17, 0xb4, 0x45, 0xf2, 0xa8, 0x50, 0xfb, 0xa3, 0x04, 0x68, 0xbe, 0x00, 0x9d, 0x33, 0x53, - 0xfa, 0x31, 0xcd, 0xac, 0xfd, 0x41, 0x82, 0x6a, 0xba, 0xea, 0x9c, 0x33, 0xef, 0xda, 0x8f, 0x6a, - 0xde, 0xdf, 0x33, 0xb0, 0x9a, 0xaa, 0x35, 0x2f, 0x6a, 0xdd, 0x17, 0xb0, 0x6e, 0x1a, 0x74, 0xe2, - 0x3a, 0x01, 0xb5, 0xf5, 0x33, 0xcd, 0xa2, 0x8f, 0xa9, 0x25, 0xd7, 0x78, 0xd2, 0xd8, 0xfd, 0xf6, - 0x6a, 0xb6, 0xde, 0x9e, 0xc9, 0x1d, 0x31, 0xb1, 0xbd, 0x8d, 0x76, 0x4b, 0x39, 0xee, 0x75, 0x07, - 0x4a, 0xa7, 0xf9, 0x99, 0x76, 0xd2, 0xf9, 0x59, 0xa7, 0xfb, 0x49, 0x47, 0x45, 0xe6, 0x1c, 0xed, - 0x07, 0xdc, 0xf6, 0x3d, 0x40, 0xf3, 0x46, 0xe1, 0xcb, 0xb0, 0xcc, 0x2c, 0xf4, 0x12, 0xde, 0x80, - 0xb5, 0x4e, 0x57, 0xeb, 0xb7, 0x5b, 0x8a, 0xa6, 0x3c, 0x78, 0xa0, 0x34, 0x07, 0x7d, 0xf1, 0x3e, - 0x11, 0xb3, 0x07, 0xa9, 0x0d, 0x5e, 0xfb, 0x7d, 0x16, 0x36, 0x96, 0x58, 0x82, 0x1b, 0xe1, 0xcd, - 0x42, 0x5c, 0x76, 0xde, 0xbd, 0x88, 0xf5, 0x75, 0x56, 0x10, 0xf4, 0x88, 0x17, 0x84, 0x17, 0x91, - 0x1b, 0xc0, 0xbc, 0x64, 0x07, 0xe6, 0xd0, 0xa4, 0x5e, 0xf8, 0x9c, 0x23, 0xae, 0x1b, 0x6b, 0x33, - 0x5c, 0xbc, 0xe8, 0xfc, 0x1f, 0x60, 0xd7, 0xf1, 0xcd, 0xc0, 0x7c, 0x4c, 0x35, 0xd3, 0x8e, 0xde, - 0x7e, 0xd8, 0xf5, 0x23, 0xa7, 0xa2, 0xa8, 0xa7, 0x6d, 0x07, 0x31, 0xdb, 0xa6, 0x23, 0x32, 0xc7, - 0x66, 0xc9, 0x3c, 0xab, 0xa2, 0xa8, 0x27, 0x66, 0x5f, 0x83, 0x8a, 0xe1, 0x4c, 0x59, 0x4d, 0x26, - 0x78, 0xec, 0xec, 0x90, 0xd4, 0xb2, 0xc0, 0x62, 0x4a, 0x58, 0x6d, 0xcf, 0x1e, 0x9d, 0x2a, 0x6a, - 0x59, 0x60, 0x82, 0x72, 0x1d, 0xd6, 0xc8, 0x68, 0xe4, 0x31, 0xe5, 0x91, 0x22, 0x71, 0x7f, 0xa8, - 0xc6, 0x30, 0x27, 0x6e, 0x1d, 0x42, 0x31, 0xf2, 0x03, 0x3b, 0xaa, 0x99, 0x27, 0x34, 0x57, 0x5c, - 0x8a, 0x33, 0x3b, 0x25, 0xb5, 0x68, 0x47, 0x9d, 0xd7, 0xa0, 0x62, 0xfa, 0xda, 0xec, 0x0d, 0x3d, - 0xb3, 0x9d, 0xd9, 0x29, 0xaa, 0x65, 0xd3, 0x8f, 0xdf, 0x1f, 0x6b, 0x5f, 0x65, 0xa0, 0x9a, 0xfe, - 0x06, 0x80, 0x5b, 0x50, 0xb4, 0x1c, 0x9d, 0xf0, 0xd0, 0x12, 0x1f, 0xa0, 0x76, 0x9e, 0xf3, 0xd9, - 0xa0, 0x7e, 0x14, 0xf2, 0xd5, 0x58, 0x72, 0xeb, 0x6f, 0x12, 0x14, 0x23, 0x18, 0x5f, 0x82, 0x9c, - 0x4b, 0x82, 0x31, 0x57, 0x97, 0xdf, 0xcf, 0x20, 0x49, 0xe5, 0x6d, 0x86, 0xfb, 0x2e, 0xb1, 0x79, - 0x08, 0x84, 0x38, 0x6b, 0xb3, 0x75, 0xb5, 0x28, 0x31, 0xf8, 0xe5, 0xc4, 0x99, 0x4c, 0xa8, 0x1d, - 0xf8, 0xd1, 0xba, 0x86, 0x78, 0x33, 0x84, 0xf1, 0x3b, 0xb0, 0x1e, 0x78, 0xc4, 0xb4, 0x52, 0xdc, - 0x1c, 0xe7, 0xa2, 0xa8, 0x23, 0x26, 0xef, 0xc1, 0x95, 0x48, 0xaf, 0x41, 0x03, 0xa2, 0x8f, 0xa9, - 0x31, 0x13, 0x2a, 0xf0, 0x47, 0x88, 0xcb, 0x21, 0xa1, 0x15, 0xf6, 0x47, 0xb2, 0xb5, 0x6f, 0x24, - 0x58, 0x8f, 0xae, 0x53, 0x46, 0xec, 0xac, 0x63, 0x00, 0x62, 0xdb, 0x4e, 0x90, 0x74, 0xd7, 0x62, - 0x28, 0x2f, 0xc8, 0xd5, 0x1b, 0xb1, 0x90, 0x9a, 0x50, 0xb0, 0x35, 0x01, 0x98, 0xf5, 0x9c, 0xeb, - 0xb6, 0xab, 0x50, 0x0e, 0x3f, 0xf0, 0xf0, 0xaf, 0x84, 0xe2, 0x02, 0x0e, 0x02, 0x62, 0xf7, 0x2e, - 0xbc, 0x09, 0xf9, 0x53, 0x3a, 0x32, 0xed, 0xf0, 0xd9, 0x56, 0x34, 0xa2, 0x67, 0x92, 0x5c, 0xfc, - 0x4c, 0xb2, 0xff, 0x5b, 0x09, 0x36, 0x74, 0x67, 0x32, 0x6f, 0xef, 0x3e, 0x9a, 0x7b, 0x05, 0xf0, - 0x3f, 0x96, 0x3e, 0xff, 0x68, 0x64, 0x06, 0xe3, 0xe9, 0x69, 0x5d, 0x77, 0x26, 0xbb, 0x23, 0xc7, - 0x22, 0xf6, 0x68, 0xf6, 0x99, 0x93, 0xff, 0xd1, 0xdf, 0x1d, 0x51, 0xfb, 0xdd, 0x91, 0x93, 0xf8, - 0xe8, 0x79, 0x7f, 0xf6, 0xf7, 0xbf, 0x92, 0xf4, 0xa7, 0x4c, 0xf6, 0xa0, 0xb7, 0xff, 0xe7, 0xcc, - 0xd6, 0x81, 0x18, 0xae, 0x17, 0xb9, 0x47, 0xa5, 0x43, 0x8b, 0xea, 0x6c, 0xca, 0xff, 0x0b, 0x00, - 0x00, 0xff, 0xff, 0x1a, 0x28, 0x25, 0x79, 0x42, 0x1d, 0x00, 0x00, + proto.RegisterFile("google/protobuf/descriptor.proto", fileDescriptor_e5baabe45344a177) +} + +var fileDescriptor_e5baabe45344a177 = []byte{ + // 2589 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0xdd, 0x8e, 0xdb, 0xc6, + 0x15, 0x0e, 0xf5, 0xb7, 0xd2, 0x91, 0x56, 0x3b, 0x3b, 0xbb, 0xb1, 0xe9, 0xcd, 0x8f, 0xd7, 0xca, + 0x8f, 0xd7, 0x4e, 0xac, 0x0d, 0x1c, 0xdb, 0x71, 0xd6, 0x45, 0x5a, 0xad, 0x44, 0x6f, 0xe4, 0xee, + 0x4a, 0x2a, 0xa5, 0x6d, 0x7e, 0x80, 0x82, 0x98, 0x25, 0x47, 0x12, 0x6d, 0x8a, 0x64, 0x48, 0xca, + 0xf6, 0x06, 0xbd, 0x30, 0xd0, 0xab, 0x5e, 0x15, 0xe8, 0x55, 0x51, 0x14, 0xbd, 0xe8, 0x4d, 0x80, + 0x3e, 0x40, 0x81, 0xde, 0xf5, 0x09, 0x0a, 0xe4, 0x0d, 0x8a, 0xb6, 0x40, 0xfb, 0x08, 0xbd, 0x2c, + 0x66, 0x86, 0xa4, 0x48, 0x49, 0x1b, 0x6f, 0x02, 0xc4, 0xb9, 0x92, 0xe6, 0x3b, 0xdf, 0x39, 0x73, + 0xe6, 0xcc, 0x99, 0x99, 0x33, 0x43, 0xd8, 0x1e, 0x39, 0xce, 0xc8, 0xa2, 0xbb, 0xae, 0xe7, 0x04, + 0xce, 0xc9, 0x74, 0xb8, 0x6b, 0x50, 0x5f, 0xf7, 0x4c, 0x37, 0x70, 0xbc, 0x3a, 0xc7, 0xf0, 0x9a, + 0x60, 0xd4, 0x23, 0x46, 0xed, 0x08, 0xd6, 0xef, 0x9b, 0x16, 0x6d, 0xc5, 0xc4, 0x3e, 0x0d, 0xf0, + 0x5d, 0xc8, 0x0d, 0x4d, 0x8b, 0xca, 0xd2, 0x76, 0x76, 0xa7, 0x7c, 0xf3, 0xcd, 0xfa, 0x9c, 0x52, + 0x3d, 0xad, 0xd1, 0x63, 0xb0, 0xca, 0x35, 0x6a, 0xff, 0xce, 0xc1, 0xc6, 0x12, 0x29, 0xc6, 0x90, + 0xb3, 0xc9, 0x84, 0x59, 0x94, 0x76, 0x4a, 0x2a, 0xff, 0x8f, 0x65, 0x58, 0x71, 0x89, 0xfe, 0x88, + 0x8c, 0xa8, 0x9c, 0xe1, 0x70, 0xd4, 0xc4, 0xaf, 0x03, 0x18, 0xd4, 0xa5, 0xb6, 0x41, 0x6d, 0xfd, + 0x54, 0xce, 0x6e, 0x67, 0x77, 0x4a, 0x6a, 0x02, 0xc1, 0xef, 0xc0, 0xba, 0x3b, 0x3d, 0xb1, 0x4c, + 0x5d, 0x4b, 0xd0, 0x60, 0x3b, 0xbb, 0x93, 0x57, 0x91, 0x10, 0xb4, 0x66, 0xe4, 0xab, 0xb0, 0xf6, + 0x84, 0x92, 0x47, 0x49, 0x6a, 0x99, 0x53, 0xab, 0x0c, 0x4e, 0x10, 0x9b, 0x50, 0x99, 0x50, 0xdf, + 0x27, 0x23, 0xaa, 0x05, 0xa7, 0x2e, 0x95, 0x73, 0x7c, 0xf4, 0xdb, 0x0b, 0xa3, 0x9f, 0x1f, 0x79, + 0x39, 0xd4, 0x1a, 0x9c, 0xba, 0x14, 0x37, 0xa0, 0x44, 0xed, 0xe9, 0x44, 0x58, 0xc8, 0x9f, 0x11, + 0x3f, 0xc5, 0x9e, 0x4e, 0xe6, 0xad, 0x14, 0x99, 0x5a, 0x68, 0x62, 0xc5, 0xa7, 0xde, 0x63, 0x53, + 0xa7, 0x72, 0x81, 0x1b, 0xb8, 0xba, 0x60, 0xa0, 0x2f, 0xe4, 0xf3, 0x36, 0x22, 0x3d, 0xdc, 0x84, + 0x12, 0x7d, 0x1a, 0x50, 0xdb, 0x37, 0x1d, 0x5b, 0x5e, 0xe1, 0x46, 0xde, 0x5a, 0x32, 0x8b, 0xd4, + 0x32, 0xe6, 0x4d, 0xcc, 0xf4, 0xf0, 0x1d, 0x58, 0x71, 0xdc, 0xc0, 0x74, 0x6c, 0x5f, 0x2e, 0x6e, + 0x4b, 0x3b, 0xe5, 0x9b, 0xaf, 0x2e, 0x4d, 0x84, 0xae, 0xe0, 0xa8, 0x11, 0x19, 0xb7, 0x01, 0xf9, + 0xce, 0xd4, 0xd3, 0xa9, 0xa6, 0x3b, 0x06, 0xd5, 0x4c, 0x7b, 0xe8, 0xc8, 0x25, 0x6e, 0xe0, 0xf2, + 0xe2, 0x40, 0x38, 0xb1, 0xe9, 0x18, 0xb4, 0x6d, 0x0f, 0x1d, 0xb5, 0xea, 0xa7, 0xda, 0xf8, 0x02, + 0x14, 0xfc, 0x53, 0x3b, 0x20, 0x4f, 0xe5, 0x0a, 0xcf, 0x90, 0xb0, 0x55, 0xfb, 0x6b, 0x01, 0xd6, + 0xce, 0x93, 0x62, 0xf7, 0x20, 0x3f, 0x64, 0xa3, 0x94, 0x33, 0xdf, 0x26, 0x06, 0x42, 0x27, 0x1d, + 0xc4, 0xc2, 0x77, 0x0c, 0x62, 0x03, 0xca, 0x36, 0xf5, 0x03, 0x6a, 0x88, 0x8c, 0xc8, 0x9e, 0x33, + 0xa7, 0x40, 0x28, 0x2d, 0xa6, 0x54, 0xee, 0x3b, 0xa5, 0xd4, 0xa7, 0xb0, 0x16, 0xbb, 0xa4, 0x79, + 0xc4, 0x1e, 0x45, 0xb9, 0xb9, 0xfb, 0x3c, 0x4f, 0xea, 0x4a, 0xa4, 0xa7, 0x32, 0x35, 0xb5, 0x4a, + 0x53, 0x6d, 0xdc, 0x02, 0x70, 0x6c, 0xea, 0x0c, 0x35, 0x83, 0xea, 0x96, 0x5c, 0x3c, 0x23, 0x4a, + 0x5d, 0x46, 0x59, 0x88, 0x92, 0x23, 0x50, 0xdd, 0xc2, 0x1f, 0xce, 0x52, 0x6d, 0xe5, 0x8c, 0x4c, + 0x39, 0x12, 0x8b, 0x6c, 0x21, 0xdb, 0x8e, 0xa1, 0xea, 0x51, 0x96, 0xf7, 0xd4, 0x08, 0x47, 0x56, + 0xe2, 0x4e, 0xd4, 0x9f, 0x3b, 0x32, 0x35, 0x54, 0x13, 0x03, 0x5b, 0xf5, 0x92, 0x4d, 0xfc, 0x06, + 0xc4, 0x80, 0xc6, 0xd3, 0x0a, 0xf8, 0x2e, 0x54, 0x89, 0xc0, 0x0e, 0x99, 0xd0, 0xad, 0x2f, 0xa1, + 0x9a, 0x0e, 0x0f, 0xde, 0x84, 0xbc, 0x1f, 0x10, 0x2f, 0xe0, 0x59, 0x98, 0x57, 0x45, 0x03, 0x23, + 0xc8, 0x52, 0xdb, 0xe0, 0xbb, 0x5c, 0x5e, 0x65, 0x7f, 0xf1, 0x4f, 0x66, 0x03, 0xce, 0xf2, 0x01, + 0xbf, 0xbd, 0x38, 0xa3, 0x29, 0xcb, 0xf3, 0xe3, 0xde, 0xfa, 0x00, 0x56, 0x53, 0x03, 0x38, 0x6f, + 0xd7, 0xb5, 0x5f, 0xc2, 0xcb, 0x4b, 0x4d, 0xe3, 0x4f, 0x61, 0x73, 0x6a, 0x9b, 0x76, 0x40, 0x3d, + 0xd7, 0xa3, 0x2c, 0x63, 0x45, 0x57, 0xf2, 0x7f, 0x56, 0xce, 0xc8, 0xb9, 0xe3, 0x24, 0x5b, 0x58, + 0x51, 0x37, 0xa6, 0x8b, 0xe0, 0xf5, 0x52, 0xf1, 0xbf, 0x2b, 0xe8, 0xd9, 0xb3, 0x67, 0xcf, 0x32, + 0xb5, 0xdf, 0x15, 0x60, 0x73, 0xd9, 0x9a, 0x59, 0xba, 0x7c, 0x2f, 0x40, 0xc1, 0x9e, 0x4e, 0x4e, + 0xa8, 0xc7, 0x83, 0x94, 0x57, 0xc3, 0x16, 0x6e, 0x40, 0xde, 0x22, 0x27, 0xd4, 0x92, 0x73, 0xdb, + 0xd2, 0x4e, 0xf5, 0xe6, 0x3b, 0xe7, 0x5a, 0x95, 0xf5, 0x43, 0xa6, 0xa2, 0x0a, 0x4d, 0xfc, 0x11, + 0xe4, 0xc2, 0x2d, 0x9a, 0x59, 0xb8, 0x7e, 0x3e, 0x0b, 0x6c, 0x2d, 0xa9, 0x5c, 0x0f, 0xbf, 0x02, + 0x25, 0xf6, 0x2b, 0x72, 0xa3, 0xc0, 0x7d, 0x2e, 0x32, 0x80, 0xe5, 0x05, 0xde, 0x82, 0x22, 0x5f, + 0x26, 0x06, 0x8d, 0x8e, 0xb6, 0xb8, 0xcd, 0x12, 0xcb, 0xa0, 0x43, 0x32, 0xb5, 0x02, 0xed, 0x31, + 0xb1, 0xa6, 0x94, 0x27, 0x7c, 0x49, 0xad, 0x84, 0xe0, 0xcf, 0x19, 0x86, 0x2f, 0x43, 0x59, 0xac, + 0x2a, 0xd3, 0x36, 0xe8, 0x53, 0xbe, 0x7b, 0xe6, 0x55, 0xb1, 0xd0, 0xda, 0x0c, 0x61, 0xdd, 0x3f, + 0xf4, 0x1d, 0x3b, 0x4a, 0x4d, 0xde, 0x05, 0x03, 0x78, 0xf7, 0x1f, 0xcc, 0x6f, 0xdc, 0xaf, 0x2d, + 0x1f, 0xde, 0x7c, 0x4e, 0xd5, 0xfe, 0x92, 0x81, 0x1c, 0xdf, 0x2f, 0xd6, 0xa0, 0x3c, 0xf8, 0xac, + 0xa7, 0x68, 0xad, 0xee, 0xf1, 0xfe, 0xa1, 0x82, 0x24, 0x5c, 0x05, 0xe0, 0xc0, 0xfd, 0xc3, 0x6e, + 0x63, 0x80, 0x32, 0x71, 0xbb, 0xdd, 0x19, 0xdc, 0xb9, 0x85, 0xb2, 0xb1, 0xc2, 0xb1, 0x00, 0x72, + 0x49, 0xc2, 0xfb, 0x37, 0x51, 0x1e, 0x23, 0xa8, 0x08, 0x03, 0xed, 0x4f, 0x95, 0xd6, 0x9d, 0x5b, + 0xa8, 0x90, 0x46, 0xde, 0xbf, 0x89, 0x56, 0xf0, 0x2a, 0x94, 0x38, 0xb2, 0xdf, 0xed, 0x1e, 0xa2, + 0x62, 0x6c, 0xb3, 0x3f, 0x50, 0xdb, 0x9d, 0x03, 0x54, 0x8a, 0x6d, 0x1e, 0xa8, 0xdd, 0xe3, 0x1e, + 0x82, 0xd8, 0xc2, 0x91, 0xd2, 0xef, 0x37, 0x0e, 0x14, 0x54, 0x8e, 0x19, 0xfb, 0x9f, 0x0d, 0x94, + 0x3e, 0xaa, 0xa4, 0xdc, 0x7a, 0xff, 0x26, 0x5a, 0x8d, 0xbb, 0x50, 0x3a, 0xc7, 0x47, 0xa8, 0x8a, + 0xd7, 0x61, 0x55, 0x74, 0x11, 0x39, 0xb1, 0x36, 0x07, 0xdd, 0xb9, 0x85, 0xd0, 0xcc, 0x11, 0x61, + 0x65, 0x3d, 0x05, 0xdc, 0xb9, 0x85, 0x70, 0xad, 0x09, 0x79, 0x9e, 0x5d, 0x18, 0x43, 0xf5, 0xb0, + 0xb1, 0xaf, 0x1c, 0x6a, 0xdd, 0xde, 0xa0, 0xdd, 0xed, 0x34, 0x0e, 0x91, 0x34, 0xc3, 0x54, 0xe5, + 0x67, 0xc7, 0x6d, 0x55, 0x69, 0xa1, 0x4c, 0x12, 0xeb, 0x29, 0x8d, 0x81, 0xd2, 0x42, 0xd9, 0x9a, + 0x0e, 0x9b, 0xcb, 0xf6, 0xc9, 0xa5, 0x2b, 0x23, 0x31, 0xc5, 0x99, 0x33, 0xa6, 0x98, 0xdb, 0x5a, + 0x98, 0xe2, 0x7f, 0x65, 0x60, 0x63, 0xc9, 0x59, 0xb1, 0xb4, 0x93, 0x1f, 0x43, 0x5e, 0xa4, 0xa8, + 0x38, 0x3d, 0xaf, 0x2d, 0x3d, 0x74, 0x78, 0xc2, 0x2e, 0x9c, 0xa0, 0x5c, 0x2f, 0x59, 0x41, 0x64, + 0xcf, 0xa8, 0x20, 0x98, 0x89, 0x85, 0x3d, 0xfd, 0x17, 0x0b, 0x7b, 0xba, 0x38, 0xf6, 0xee, 0x9c, + 0xe7, 0xd8, 0xe3, 0xd8, 0xb7, 0xdb, 0xdb, 0xf3, 0x4b, 0xf6, 0xf6, 0x7b, 0xb0, 0xbe, 0x60, 0xe8, + 0xdc, 0x7b, 0xec, 0xaf, 0x24, 0x90, 0xcf, 0x0a, 0xce, 0x73, 0x76, 0xba, 0x4c, 0x6a, 0xa7, 0xbb, + 0x37, 0x1f, 0xc1, 0x2b, 0x67, 0x4f, 0xc2, 0xc2, 0x5c, 0x7f, 0x25, 0xc1, 0x85, 0xe5, 0x95, 0xe2, + 0x52, 0x1f, 0x3e, 0x82, 0xc2, 0x84, 0x06, 0x63, 0x27, 0xaa, 0x96, 0xde, 0x5e, 0x72, 0x06, 0x33, + 0xf1, 0xfc, 0x64, 0x87, 0x5a, 0xc9, 0x43, 0x3c, 0x7b, 0x56, 0xb9, 0x27, 0xbc, 0x59, 0xf0, 0xf4, + 0xd7, 0x19, 0x78, 0x79, 0xa9, 0xf1, 0xa5, 0x8e, 0xbe, 0x06, 0x60, 0xda, 0xee, 0x34, 0x10, 0x15, + 0x91, 0xd8, 0x60, 0x4b, 0x1c, 0xe1, 0x9b, 0x17, 0xdb, 0x3c, 0xa7, 0x41, 0x2c, 0xcf, 0x72, 0x39, + 0x08, 0x88, 0x13, 0xee, 0xce, 0x1c, 0xcd, 0x71, 0x47, 0x5f, 0x3f, 0x63, 0xa4, 0x0b, 0x89, 0xf9, + 0x1e, 0x20, 0xdd, 0x32, 0xa9, 0x1d, 0x68, 0x7e, 0xe0, 0x51, 0x32, 0x31, 0xed, 0x11, 0x3f, 0x41, + 0x8a, 0x7b, 0xf9, 0x21, 0xb1, 0x7c, 0xaa, 0xae, 0x09, 0x71, 0x3f, 0x92, 0x32, 0x0d, 0x9e, 0x40, + 0x5e, 0x42, 0xa3, 0x90, 0xd2, 0x10, 0xe2, 0x58, 0xa3, 0xf6, 0xdb, 0x12, 0x94, 0x13, 0x75, 0x35, + 0xbe, 0x02, 0x95, 0x87, 0xe4, 0x31, 0xd1, 0xa2, 0xbb, 0x92, 0x88, 0x44, 0x99, 0x61, 0xbd, 0xf0, + 0xbe, 0xf4, 0x1e, 0x6c, 0x72, 0x8a, 0x33, 0x0d, 0xa8, 0xa7, 0xe9, 0x16, 0xf1, 0x7d, 0x1e, 0xb4, + 0x22, 0xa7, 0x62, 0x26, 0xeb, 0x32, 0x51, 0x33, 0x92, 0xe0, 0xdb, 0xb0, 0xc1, 0x35, 0x26, 0x53, + 0x2b, 0x30, 0x5d, 0x8b, 0x6a, 0xec, 0xf6, 0xe6, 0xf3, 0x93, 0x24, 0xf6, 0x6c, 0x9d, 0x31, 0x8e, + 0x42, 0x02, 0xf3, 0xc8, 0xc7, 0x2d, 0x78, 0x8d, 0xab, 0x8d, 0xa8, 0x4d, 0x3d, 0x12, 0x50, 0x8d, + 0x7e, 0x31, 0x25, 0x96, 0xaf, 0x11, 0xdb, 0xd0, 0xc6, 0xc4, 0x1f, 0xcb, 0x9b, 0xcc, 0xc0, 0x7e, + 0x46, 0x96, 0xd4, 0x4b, 0x8c, 0x78, 0x10, 0xf2, 0x14, 0x4e, 0x6b, 0xd8, 0xc6, 0xc7, 0xc4, 0x1f, + 0xe3, 0x3d, 0xb8, 0xc0, 0xad, 0xf8, 0x81, 0x67, 0xda, 0x23, 0x4d, 0x1f, 0x53, 0xfd, 0x91, 0x36, + 0x0d, 0x86, 0x77, 0xe5, 0x57, 0x92, 0xfd, 0x73, 0x0f, 0xfb, 0x9c, 0xd3, 0x64, 0x94, 0xe3, 0x60, + 0x78, 0x17, 0xf7, 0xa1, 0xc2, 0x26, 0x63, 0x62, 0x7e, 0x49, 0xb5, 0xa1, 0xe3, 0xf1, 0xa3, 0xb1, + 0xba, 0x64, 0x6b, 0x4a, 0x44, 0xb0, 0xde, 0x0d, 0x15, 0x8e, 0x1c, 0x83, 0xee, 0xe5, 0xfb, 0x3d, + 0x45, 0x69, 0xa9, 0xe5, 0xc8, 0xca, 0x7d, 0xc7, 0x63, 0x09, 0x35, 0x72, 0xe2, 0x00, 0x97, 0x45, + 0x42, 0x8d, 0x9c, 0x28, 0xbc, 0xb7, 0x61, 0x43, 0xd7, 0xc5, 0x98, 0x4d, 0x5d, 0x0b, 0xef, 0x58, + 0xbe, 0x8c, 0x52, 0xc1, 0xd2, 0xf5, 0x03, 0x41, 0x08, 0x73, 0xdc, 0xc7, 0x1f, 0xc2, 0xcb, 0xb3, + 0x60, 0x25, 0x15, 0xd7, 0x17, 0x46, 0x39, 0xaf, 0x7a, 0x1b, 0x36, 0xdc, 0xd3, 0x45, 0x45, 0x9c, + 0xea, 0xd1, 0x3d, 0x9d, 0x57, 0xfb, 0x00, 0x36, 0xdd, 0xb1, 0xbb, 0xa8, 0x77, 0x3d, 0xa9, 0x87, + 0xdd, 0xb1, 0x3b, 0xaf, 0xf8, 0x16, 0xbf, 0x70, 0x7b, 0x54, 0x27, 0x01, 0x35, 0xe4, 0x8b, 0x49, + 0x7a, 0x42, 0x80, 0x77, 0x01, 0xe9, 0xba, 0x46, 0x6d, 0x72, 0x62, 0x51, 0x8d, 0x78, 0xd4, 0x26, + 0xbe, 0x7c, 0x39, 0x49, 0xae, 0xea, 0xba, 0xc2, 0xa5, 0x0d, 0x2e, 0xc4, 0xd7, 0x61, 0xdd, 0x39, + 0x79, 0xa8, 0x8b, 0x94, 0xd4, 0x5c, 0x8f, 0x0e, 0xcd, 0xa7, 0xf2, 0x9b, 0x3c, 0xbe, 0x6b, 0x4c, + 0xc0, 0x13, 0xb2, 0xc7, 0x61, 0x7c, 0x0d, 0x90, 0xee, 0x8f, 0x89, 0xe7, 0xf2, 0x3d, 0xd9, 0x77, + 0x89, 0x4e, 0xe5, 0xb7, 0x04, 0x55, 0xe0, 0x9d, 0x08, 0x66, 0x4b, 0xc2, 0x7f, 0x62, 0x0e, 0x83, + 0xc8, 0xe2, 0x55, 0xb1, 0x24, 0x38, 0x16, 0x5a, 0xdb, 0x01, 0xc4, 0x42, 0x91, 0xea, 0x78, 0x87, + 0xd3, 0xaa, 0xee, 0xd8, 0x4d, 0xf6, 0xfb, 0x06, 0xac, 0x32, 0xe6, 0xac, 0xd3, 0x6b, 0xa2, 0x20, + 0x73, 0xc7, 0x89, 0x1e, 0x6f, 0xc1, 0x05, 0x46, 0x9a, 0xd0, 0x80, 0x18, 0x24, 0x20, 0x09, 0xf6, + 0xbb, 0x9c, 0xcd, 0xe2, 0x7e, 0x14, 0x0a, 0x53, 0x7e, 0x7a, 0xd3, 0x93, 0xd3, 0x38, 0xb3, 0x6e, + 0x08, 0x3f, 0x19, 0x16, 0xe5, 0xd6, 0xf7, 0x56, 0x74, 0xd7, 0xf6, 0xa0, 0x92, 0x4c, 0x7c, 0x5c, + 0x02, 0x91, 0xfa, 0x48, 0x62, 0x55, 0x50, 0xb3, 0xdb, 0x62, 0xf5, 0xcb, 0xe7, 0x0a, 0xca, 0xb0, + 0x3a, 0xea, 0xb0, 0x3d, 0x50, 0x34, 0xf5, 0xb8, 0x33, 0x68, 0x1f, 0x29, 0x28, 0x9b, 0x28, 0xd8, + 0x1f, 0xe4, 0x8a, 0x6f, 0xa3, 0xab, 0xb5, 0xaf, 0x33, 0x50, 0x4d, 0xdf, 0xc0, 0xf0, 0x8f, 0xe0, + 0x62, 0xf4, 0x5c, 0xe2, 0xd3, 0x40, 0x7b, 0x62, 0x7a, 0x7c, 0x45, 0x4e, 0x88, 0x38, 0x1d, 0xe3, + 0x9c, 0xd8, 0x0c, 0x59, 0x7d, 0x1a, 0x7c, 0x62, 0x7a, 0x6c, 0xbd, 0x4d, 0x48, 0x80, 0x0f, 0xe1, + 0xb2, 0xed, 0x68, 0x7e, 0x40, 0x6c, 0x83, 0x78, 0x86, 0x36, 0x7b, 0xa8, 0xd2, 0x88, 0xae, 0x53, + 0xdf, 0x77, 0xc4, 0x49, 0x18, 0x5b, 0x79, 0xd5, 0x76, 0xfa, 0x21, 0x79, 0x76, 0x44, 0x34, 0x42, + 0xea, 0x5c, 0xfe, 0x66, 0xcf, 0xca, 0xdf, 0x57, 0xa0, 0x34, 0x21, 0xae, 0x46, 0xed, 0xc0, 0x3b, + 0xe5, 0x75, 0x77, 0x51, 0x2d, 0x4e, 0x88, 0xab, 0xb0, 0xf6, 0x0b, 0xb9, 0xfe, 0x3c, 0xc8, 0x15, + 0x8b, 0xa8, 0xf4, 0x20, 0x57, 0x2c, 0x21, 0xa8, 0xfd, 0x33, 0x0b, 0x95, 0x64, 0x1d, 0xce, 0xae, + 0x35, 0x3a, 0x3f, 0xb2, 0x24, 0xbe, 0xa9, 0xbd, 0xf1, 0x8d, 0x55, 0x7b, 0xbd, 0xc9, 0xce, 0xb2, + 0xbd, 0x82, 0xa8, 0x8e, 0x55, 0xa1, 0xc9, 0xea, 0x08, 0x96, 0x6c, 0x54, 0x54, 0x23, 0x45, 0x35, + 0x6c, 0xe1, 0x03, 0x28, 0x3c, 0xf4, 0xb9, 0xed, 0x02, 0xb7, 0xfd, 0xe6, 0x37, 0xdb, 0x7e, 0xd0, + 0xe7, 0xc6, 0x4b, 0x0f, 0xfa, 0x5a, 0xa7, 0xab, 0x1e, 0x35, 0x0e, 0xd5, 0x50, 0x1d, 0x5f, 0x82, + 0x9c, 0x45, 0xbe, 0x3c, 0x4d, 0x9f, 0x7a, 0x1c, 0x3a, 0xef, 0x24, 0x5c, 0x82, 0xdc, 0x13, 0x4a, + 0x1e, 0xa5, 0xcf, 0x1a, 0x0e, 0x7d, 0x8f, 0x8b, 0x61, 0x17, 0xf2, 0x3c, 0x5e, 0x18, 0x20, 0x8c, + 0x18, 0x7a, 0x09, 0x17, 0x21, 0xd7, 0xec, 0xaa, 0x6c, 0x41, 0x20, 0xa8, 0x08, 0x54, 0xeb, 0xb5, + 0x95, 0xa6, 0x82, 0x32, 0xb5, 0xdb, 0x50, 0x10, 0x41, 0x60, 0x8b, 0x25, 0x0e, 0x03, 0x7a, 0x29, + 0x6c, 0x86, 0x36, 0xa4, 0x48, 0x7a, 0x7c, 0xb4, 0xaf, 0xa8, 0x28, 0x93, 0x9e, 0xea, 0x1c, 0xca, + 0xd7, 0x7c, 0xa8, 0x24, 0x0b, 0xf1, 0x17, 0x73, 0xc9, 0xfe, 0x9b, 0x04, 0xe5, 0x44, 0x61, 0xcd, + 0x2a, 0x22, 0x62, 0x59, 0xce, 0x13, 0x8d, 0x58, 0x26, 0xf1, 0xc3, 0xd4, 0x00, 0x0e, 0x35, 0x18, + 0x72, 0xde, 0xa9, 0x7b, 0x41, 0x4b, 0x24, 0x8f, 0x0a, 0xb5, 0x3f, 0x4a, 0x80, 0xe6, 0x2b, 0xdb, + 0x39, 0x37, 0xa5, 0x1f, 0xd2, 0xcd, 0xda, 0x1f, 0x24, 0xa8, 0xa6, 0xcb, 0xd9, 0x39, 0xf7, 0xae, + 0xfc, 0xa0, 0xee, 0xfd, 0x23, 0x03, 0xab, 0xa9, 0x22, 0xf6, 0xbc, 0xde, 0x7d, 0x01, 0xeb, 0xa6, + 0x41, 0x27, 0xae, 0x13, 0x50, 0x5b, 0x3f, 0xd5, 0x2c, 0xfa, 0x98, 0x5a, 0x72, 0x8d, 0x6f, 0x1a, + 0xbb, 0xdf, 0x5c, 0x26, 0xd7, 0xdb, 0x33, 0xbd, 0x43, 0xa6, 0xb6, 0xb7, 0xd1, 0x6e, 0x29, 0x47, + 0xbd, 0xee, 0x40, 0xe9, 0x34, 0x3f, 0xd3, 0x8e, 0x3b, 0x3f, 0xed, 0x74, 0x3f, 0xe9, 0xa8, 0xc8, + 0x9c, 0xa3, 0x7d, 0x8f, 0xcb, 0xbe, 0x07, 0x68, 0xde, 0x29, 0x7c, 0x11, 0x96, 0xb9, 0x85, 0x5e, + 0xc2, 0x1b, 0xb0, 0xd6, 0xe9, 0x6a, 0xfd, 0x76, 0x4b, 0xd1, 0x94, 0xfb, 0xf7, 0x95, 0xe6, 0xa0, + 0x2f, 0x1e, 0x3e, 0x62, 0xf6, 0x20, 0xb5, 0xc0, 0x6b, 0xbf, 0xcf, 0xc2, 0xc6, 0x12, 0x4f, 0x70, + 0x23, 0xbc, 0xb2, 0x88, 0x5b, 0xd4, 0x8d, 0xf3, 0x78, 0x5f, 0x67, 0x35, 0x43, 0x8f, 0x78, 0x41, + 0x78, 0xc3, 0xb9, 0x06, 0x2c, 0x4a, 0x76, 0x60, 0x0e, 0x4d, 0xea, 0x85, 0xef, 0x44, 0xe2, 0x1e, + 0xb3, 0x36, 0xc3, 0xc5, 0x53, 0xd1, 0xbb, 0x80, 0x5d, 0xc7, 0x37, 0x03, 0xf3, 0x31, 0xd5, 0x4c, + 0x3b, 0x7a, 0x54, 0x62, 0xf7, 0x9a, 0x9c, 0x8a, 0x22, 0x49, 0xdb, 0x0e, 0x62, 0xb6, 0x4d, 0x47, + 0x64, 0x8e, 0xcd, 0x36, 0xf3, 0xac, 0x8a, 0x22, 0x49, 0xcc, 0xbe, 0x02, 0x15, 0xc3, 0x99, 0xb2, + 0x62, 0x4f, 0xf0, 0xd8, 0xd9, 0x21, 0xa9, 0x65, 0x81, 0xc5, 0x94, 0xb0, 0x8c, 0x9f, 0xbd, 0x66, + 0x55, 0xd4, 0xb2, 0xc0, 0x04, 0xe5, 0x2a, 0xac, 0x91, 0xd1, 0xc8, 0x63, 0xc6, 0x23, 0x43, 0xe2, + 0x62, 0x52, 0x8d, 0x61, 0x4e, 0xdc, 0x7a, 0x00, 0xc5, 0x28, 0x0e, 0xec, 0xa8, 0x66, 0x91, 0xd0, + 0x5c, 0x71, 0xdb, 0xce, 0xec, 0x94, 0xd4, 0xa2, 0x1d, 0x09, 0xaf, 0x40, 0xc5, 0xf4, 0xb5, 0xd9, + 0xe3, 0x7c, 0x66, 0x3b, 0xb3, 0x53, 0x54, 0xcb, 0xa6, 0x1f, 0x3f, 0x6c, 0xd6, 0xbe, 0xca, 0x40, + 0x35, 0xfd, 0x71, 0x01, 0xb7, 0xa0, 0x68, 0x39, 0x3a, 0xe1, 0xa9, 0x25, 0xbe, 0x6c, 0xed, 0x3c, + 0xe7, 0x7b, 0x44, 0xfd, 0x30, 0xe4, 0xab, 0xb1, 0xe6, 0xd6, 0xdf, 0x25, 0x28, 0x46, 0x30, 0xbe, + 0x00, 0x39, 0x97, 0x04, 0x63, 0x6e, 0x2e, 0xbf, 0x9f, 0x41, 0x92, 0xca, 0xdb, 0x0c, 0xf7, 0x5d, + 0x62, 0xf3, 0x14, 0x08, 0x71, 0xd6, 0x66, 0xf3, 0x6a, 0x51, 0x62, 0xf0, 0x5b, 0x8f, 0x33, 0x99, + 0x50, 0x3b, 0xf0, 0xa3, 0x79, 0x0d, 0xf1, 0x66, 0x08, 0xe3, 0x77, 0x60, 0x3d, 0xf0, 0x88, 0x69, + 0xa5, 0xb8, 0x39, 0xce, 0x45, 0x91, 0x20, 0x26, 0xef, 0xc1, 0xa5, 0xc8, 0xae, 0x41, 0x03, 0xa2, + 0x8f, 0xa9, 0x31, 0x53, 0x2a, 0xf0, 0xd7, 0x8d, 0x8b, 0x21, 0xa1, 0x15, 0xca, 0x23, 0xdd, 0xda, + 0xd7, 0x12, 0xac, 0x47, 0xf7, 0x34, 0x23, 0x0e, 0xd6, 0x11, 0x00, 0xb1, 0x6d, 0x27, 0x48, 0x86, + 0x6b, 0x31, 0x95, 0x17, 0xf4, 0xea, 0x8d, 0x58, 0x49, 0x4d, 0x18, 0xd8, 0x9a, 0x00, 0xcc, 0x24, + 0x67, 0x86, 0xed, 0x32, 0x94, 0xc3, 0x2f, 0x47, 0xfc, 0xf3, 0xa3, 0xb8, 0xd9, 0x83, 0x80, 0xd8, + 0x85, 0x0e, 0x6f, 0x42, 0xfe, 0x84, 0x8e, 0x4c, 0x3b, 0x7c, 0x0f, 0x16, 0x8d, 0xe8, 0xfd, 0x25, + 0x17, 0xbf, 0xbf, 0xec, 0xff, 0x46, 0x82, 0x0d, 0xdd, 0x99, 0xcc, 0xfb, 0xbb, 0x8f, 0xe6, 0x9e, + 0x17, 0xfc, 0x8f, 0xa5, 0xcf, 0x3f, 0x1a, 0x99, 0xc1, 0x78, 0x7a, 0x52, 0xd7, 0x9d, 0xc9, 0xee, + 0xc8, 0xb1, 0x88, 0x3d, 0x9a, 0x7d, 0x3f, 0xe5, 0x7f, 0xf4, 0x1b, 0x23, 0x6a, 0xdf, 0x18, 0x39, + 0x89, 0xaf, 0xa9, 0xf7, 0x66, 0x7f, 0xff, 0x27, 0x49, 0x7f, 0xca, 0x64, 0x0f, 0x7a, 0xfb, 0x7f, + 0xce, 0x6c, 0x1d, 0x88, 0xee, 0x7a, 0x51, 0x78, 0x54, 0x3a, 0xb4, 0xa8, 0xce, 0x86, 0xfc, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x3e, 0xe8, 0xef, 0xc4, 0x9b, 0x1d, 0x00, 0x00, } diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto index 8697a50de4a9c..a2102d7aa9960 100644 --- a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto +++ b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto @@ -40,6 +40,7 @@ syntax = "proto2"; package google.protobuf; + option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor"; option java_package = "com.google.protobuf"; option java_outer_classname = "DescriptorProtos"; @@ -59,8 +60,8 @@ message FileDescriptorSet { // Describes a complete .proto file. message FileDescriptorProto { - optional string name = 1; // file name, relative to root of source tree - optional string package = 2; // e.g. "foo", "foo.bar", etc. + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. // Names of files imported by this file. repeated string dependency = 3; @@ -100,8 +101,8 @@ message DescriptorProto { repeated EnumDescriptorProto enum_type = 4; message ExtensionRange { - optional int32 start = 1; - optional int32 end = 2; + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. optional ExtensionRangeOptions options = 3; } @@ -115,8 +116,8 @@ message DescriptorProto { // fields or extension ranges in the same message. Reserved ranges may // not overlap. message ReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Exclusive. + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. } repeated ReservedRange reserved_range = 9; // Reserved field names, which may not be used by fields in the same message. @@ -137,42 +138,42 @@ message FieldDescriptorProto { enum Type { // 0 is reserved for errors. // Order is weird for historical reasons. - TYPE_DOUBLE = 1; - TYPE_FLOAT = 2; + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if // negative values are likely. - TYPE_INT64 = 3; - TYPE_UINT64 = 4; + TYPE_INT64 = 3; + TYPE_UINT64 = 4; // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if // negative values are likely. - TYPE_INT32 = 5; - TYPE_FIXED64 = 6; - TYPE_FIXED32 = 7; - TYPE_BOOL = 8; - TYPE_STRING = 9; + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; // Tag-delimited aggregate. // Group type is deprecated and not supported in proto3. However, Proto3 // implementations should still be able to parse the group wire format and // treat group fields as unknown fields. - TYPE_GROUP = 10; - TYPE_MESSAGE = 11; // Length-delimited aggregate. + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; // Length-delimited aggregate. // New in version 2. - TYPE_BYTES = 12; - TYPE_UINT32 = 13; - TYPE_ENUM = 14; - TYPE_SFIXED32 = 15; - TYPE_SFIXED64 = 16; - TYPE_SINT32 = 17; // Uses ZigZag encoding. - TYPE_SINT64 = 18; // Uses ZigZag encoding. - }; + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + } enum Label { // 0 is reserved for errors - LABEL_OPTIONAL = 1; - LABEL_REQUIRED = 2; - LABEL_REPEATED = 3; - }; + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } optional string name = 1; optional int32 number = 3; @@ -234,8 +235,8 @@ message EnumDescriptorProto { // is inclusive such that it can appropriately represent the entire int32 // domain. message EnumReservedRange { - optional int32 start = 1; // Inclusive. - optional int32 end = 2; // Inclusive. + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Inclusive. } // Range of reserved numeric values. Reserved numeric values may not be used @@ -276,9 +277,9 @@ message MethodDescriptorProto { optional MethodOptions options = 4; // Identifies if client streams multiple client messages - optional bool client_streaming = 5 [default=false]; + optional bool client_streaming = 5 [default = false]; // Identifies if server streams multiple server messages - optional bool server_streaming = 6 [default=false]; + optional bool server_streaming = 6 [default = false]; } @@ -314,7 +315,6 @@ message MethodDescriptorProto { // If this turns out to be popular, a web service will be set up // to automatically assign option numbers. - message FileOptions { // Sets the Java package where classes generated from this .proto will be @@ -337,7 +337,7 @@ message FileOptions { // named by java_outer_classname. However, the outer class will still be // generated to contain the file's getDescriptor() method as well as any // top-level extensions defined in the file. - optional bool java_multiple_files = 10 [default=false]; + optional bool java_multiple_files = 10 [default = false]; // This option does nothing. optional bool java_generate_equals_and_hash = 20 [deprecated=true]; @@ -348,17 +348,17 @@ message FileOptions { // Message reflection will do the same. // However, an extension field still accepts non-UTF-8 byte sequences. // This option has no effect on when used with the lite runtime. - optional bool java_string_check_utf8 = 27 [default=false]; + optional bool java_string_check_utf8 = 27 [default = false]; // Generated classes can be optimized for speed or code size. enum OptimizeMode { - SPEED = 1; // Generate complete code for parsing, serialization, - // etc. - CODE_SIZE = 2; // Use ReflectionOps to implement these methods. - LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. + SPEED = 1; // Generate complete code for parsing, serialization, + // etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. } - optional OptimizeMode optimize_for = 9 [default=SPEED]; + optional OptimizeMode optimize_for = 9 [default = SPEED]; // Sets the Go package where structs generated from this .proto will be // placed. If omitted, the Go package will be derived from the following: @@ -369,6 +369,7 @@ message FileOptions { + // Should generic services be generated in each language? "Generic" services // are not specific to any particular RPC system. They are generated by the // main code generators in each language (without additional plugins). @@ -379,20 +380,20 @@ message FileOptions { // that generate code specific to your particular RPC system. Therefore, // these default to false. Old code which depends on generic services should // explicitly set them to true. - optional bool cc_generic_services = 16 [default=false]; - optional bool java_generic_services = 17 [default=false]; - optional bool py_generic_services = 18 [default=false]; - optional bool php_generic_services = 42 [default=false]; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool php_generic_services = 42 [default = false]; // Is this file deprecated? // Depending on the target platform, this can emit Deprecated annotations // for everything in the file, or it will be completely ignored; in the very // least, this is a formalization for deprecating files. - optional bool deprecated = 23 [default=false]; + optional bool deprecated = 23 [default = false]; // Enables the use of arenas for the proto messages in this file. This applies // only to generated classes for C++. - optional bool cc_enable_arenas = 31 [default=false]; + optional bool cc_enable_arenas = 31 [default = false]; // Sets the objective c class prefix which is prepended to all objective c @@ -417,6 +418,17 @@ message FileOptions { // determining the namespace. optional string php_namespace = 41; + // Use this option to change the namespace of php generated metadata classes. + // Default is empty. When this option is empty, the proto file name will be + // used for determining the namespace. + optional string php_metadata_namespace = 44; + + // Use this option to change the package of ruby generated classes. Default + // is empty. When this option is not set, the package name will be used for + // determining the ruby package. + optional string ruby_package = 45; + + // The parser stores options it doesn't recognize here. // See the documentation for the "Options" section above. repeated UninterpretedOption uninterpreted_option = 999; @@ -447,18 +459,18 @@ message MessageOptions { // // Because this is an option, the above two restrictions are not enforced by // the protocol compiler. - optional bool message_set_wire_format = 1 [default=false]; + optional bool message_set_wire_format = 1 [default = false]; // Disables the generation of the standard "descriptor()" accessor, which can // conflict with a field of the same name. This is meant to make migration // from proto1 easier; new code should avoid fields named "descriptor". - optional bool no_standard_descriptor_accessor = 2 [default=false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; // Is this message deprecated? // Depending on the target platform, this can emit Deprecated annotations // for the message, or it will be completely ignored; in the very least, // this is a formalization for deprecating messages. - optional bool deprecated = 3 [default=false]; + optional bool deprecated = 3 [default = false]; // Whether the message is an automatically generated map entry type for the // maps field. @@ -475,7 +487,7 @@ message MessageOptions { // // Implementations may choose not to generate the map_entry=true message, but // use a native map in the target language to hold the keys and values. - // The reflection APIs in such implementions still need to work as + // The reflection APIs in such implementations still need to work as // if the field is a repeated message field. // // NOTE: Do not set the option in .proto files. Always use the maps syntax @@ -486,6 +498,7 @@ message MessageOptions { reserved 8; // javalite_serializable reserved 9; // javanano_as_lite + // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -565,16 +578,16 @@ message FieldOptions { // implementation must either *always* check its required fields, or *never* // check its required fields, regardless of whether or not the message has // been parsed. - optional bool lazy = 5 [default=false]; + optional bool lazy = 5 [default = false]; // Is this field deprecated? // Depending on the target platform, this can emit Deprecated annotations // for accessors, or it will be completely ignored; in the very least, this // is a formalization for deprecating fields. - optional bool deprecated = 3 [default=false]; + optional bool deprecated = 3 [default = false]; // For Google-internal migration only. Do not use. - optional bool weak = 10 [default=false]; + optional bool weak = 10 [default = false]; // The parser stores options it doesn't recognize here. See above. @@ -604,7 +617,7 @@ message EnumOptions { // Depending on the target platform, this can emit Deprecated annotations // for the enum, or it will be completely ignored; in the very least, this // is a formalization for deprecating enums. - optional bool deprecated = 3 [default=false]; + optional bool deprecated = 3 [default = false]; reserved 5; // javanano_as_lite @@ -620,7 +633,7 @@ message EnumValueOptions { // Depending on the target platform, this can emit Deprecated annotations // for the enum value, or it will be completely ignored; in the very least, // this is a formalization for deprecating enum values. - optional bool deprecated = 1 [default=false]; + optional bool deprecated = 1 [default = false]; // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -640,7 +653,7 @@ message ServiceOptions { // Depending on the target platform, this can emit Deprecated annotations // for the service, or it will be completely ignored; in the very least, // this is a formalization for deprecating services. - optional bool deprecated = 33 [default=false]; + optional bool deprecated = 33 [default = false]; // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -660,18 +673,18 @@ message MethodOptions { // Depending on the target platform, this can emit Deprecated annotations // for the method, or it will be completely ignored; in the very least, // this is a formalization for deprecating methods. - optional bool deprecated = 33 [default=false]; + optional bool deprecated = 33 [default = false]; // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, // or neither? HTTP based RPC implementation may choose GET verb for safe // methods, and PUT verb for idempotent methods instead of the default POST. enum IdempotencyLevel { IDEMPOTENCY_UNKNOWN = 0; - NO_SIDE_EFFECTS = 1; // implies idempotent - IDEMPOTENT = 2; // idempotent, but may have side effects + NO_SIDE_EFFECTS = 1; // implies idempotent + IDEMPOTENT = 2; // idempotent, but may have side effects } - optional IdempotencyLevel idempotency_level = - 34 [default=IDEMPOTENCY_UNKNOWN]; + optional IdempotencyLevel idempotency_level = 34 + [default = IDEMPOTENCY_UNKNOWN]; // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; @@ -752,7 +765,7 @@ message SourceCodeInfo { // beginning of the "extend" block and is shared by all extensions within // the block. // - Just because a location's span is a subset of some other location's span - // does not mean that it is a descendent. For example, a "group" defines + // does not mean that it is a descendant. For example, a "group" defines // both a type and a field in a single declaration. Thus, the locations // corresponding to the type and field and their components will overlap. // - Code which tries to interpret locations should probably be designed to @@ -783,14 +796,14 @@ message SourceCodeInfo { // [ 4, 3, 2, 7 ] // this path refers to the whole field declaration (from the beginning // of the label to the terminating semicolon). - repeated int32 path = 1 [packed=true]; + repeated int32 path = 1 [packed = true]; // Always has exactly three or four elements: start line, start column, // end line (optional, otherwise assumed same as start line), end column. // These are packed into a single field for efficiency. Note that line // and column numbers are zero-based -- typically you will want to add // 1 to each before displaying to a user. - repeated int32 span = 2 [packed=true]; + repeated int32 span = 2 [packed = true]; // If this SourceCodeInfo represents a complete declaration, these are any // comments appearing before and after the declaration which appear to be @@ -855,7 +868,7 @@ message GeneratedCodeInfo { message Annotation { // Identifies the element in the original source .proto file. This field // is formatted the same as SourceCodeInfo.Location.path. - repeated int32 path = 1 [packed=true]; + repeated int32 path = 1 [packed = true]; // Identifies the filesystem path to the original source .proto. optional string source_file = 2; diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/generator/generator.go b/vendor/github.com/golang/protobuf/protoc-gen-go/generator/generator.go new file mode 100644 index 0000000000000..63b8ca0eed470 --- /dev/null +++ b/vendor/github.com/golang/protobuf/protoc-gen-go/generator/generator.go @@ -0,0 +1,2808 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* + The code generator for the plugin for the Google protocol buffer compiler. + It generates Go code from the protocol buffer description files read by the + main routine. +*/ +package generator + +import ( + "bufio" + "bytes" + "compress/gzip" + "crypto/sha256" + "encoding/hex" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/printer" + "go/token" + "log" + "os" + "path" + "sort" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/protoc-gen-go/generator/internal/remap" + + "github.com/golang/protobuf/protoc-gen-go/descriptor" + plugin "github.com/golang/protobuf/protoc-gen-go/plugin" +) + +// generatedCodeVersion indicates a version of the generated code. +// It is incremented whenever an incompatibility between the generated code and +// proto package is introduced; the generated code references +// a constant, proto.ProtoPackageIsVersionN (where N is generatedCodeVersion). +const generatedCodeVersion = 3 + +// A Plugin provides functionality to add to the output during Go code generation, +// such as to produce RPC stubs. +type Plugin interface { + // Name identifies the plugin. + Name() string + // Init is called once after data structures are built but before + // code generation begins. + Init(g *Generator) + // Generate produces the code generated by the plugin for this file, + // except for the imports, by calling the generator's methods P, In, and Out. + Generate(file *FileDescriptor) + // GenerateImports produces the import declarations for this file. + // It is called after Generate. + GenerateImports(file *FileDescriptor) +} + +var plugins []Plugin + +// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated. +// It is typically called during initialization. +func RegisterPlugin(p Plugin) { + plugins = append(plugins, p) +} + +// A GoImportPath is the import path of a Go package. e.g., "google.golang.org/genproto/protobuf". +type GoImportPath string + +func (p GoImportPath) String() string { return strconv.Quote(string(p)) } + +// A GoPackageName is the name of a Go package. e.g., "protobuf". +type GoPackageName string + +// Each type we import as a protocol buffer (other than FileDescriptorProto) needs +// a pointer to the FileDescriptorProto that represents it. These types achieve that +// wrapping by placing each Proto inside a struct with the pointer to its File. The +// structs have the same names as their contents, with "Proto" removed. +// FileDescriptor is used to store the things that it points to. + +// The file and package name method are common to messages and enums. +type common struct { + file *FileDescriptor // File this object comes from. +} + +// GoImportPath is the import path of the Go package containing the type. +func (c *common) GoImportPath() GoImportPath { + return c.file.importPath +} + +func (c *common) File() *FileDescriptor { return c.file } + +func fileIsProto3(file *descriptor.FileDescriptorProto) bool { + return file.GetSyntax() == "proto3" +} + +func (c *common) proto3() bool { return fileIsProto3(c.file.FileDescriptorProto) } + +// Descriptor represents a protocol buffer message. +type Descriptor struct { + common + *descriptor.DescriptorProto + parent *Descriptor // The containing message, if any. + nested []*Descriptor // Inner messages, if any. + enums []*EnumDescriptor // Inner enums, if any. + ext []*ExtensionDescriptor // Extensions, if any. + typename []string // Cached typename vector. + index int // The index into the container, whether the file or another message. + path string // The SourceCodeInfo path as comma-separated integers. + group bool +} + +// TypeName returns the elements of the dotted type name. +// The package name is not part of this name. +func (d *Descriptor) TypeName() []string { + if d.typename != nil { + return d.typename + } + n := 0 + for parent := d; parent != nil; parent = parent.parent { + n++ + } + s := make([]string, n) + for parent := d; parent != nil; parent = parent.parent { + n-- + s[n] = parent.GetName() + } + d.typename = s + return s +} + +// EnumDescriptor describes an enum. If it's at top level, its parent will be nil. +// Otherwise it will be the descriptor of the message in which it is defined. +type EnumDescriptor struct { + common + *descriptor.EnumDescriptorProto + parent *Descriptor // The containing message, if any. + typename []string // Cached typename vector. + index int // The index into the container, whether the file or a message. + path string // The SourceCodeInfo path as comma-separated integers. +} + +// TypeName returns the elements of the dotted type name. +// The package name is not part of this name. +func (e *EnumDescriptor) TypeName() (s []string) { + if e.typename != nil { + return e.typename + } + name := e.GetName() + if e.parent == nil { + s = make([]string, 1) + } else { + pname := e.parent.TypeName() + s = make([]string, len(pname)+1) + copy(s, pname) + } + s[len(s)-1] = name + e.typename = s + return s +} + +// Everything but the last element of the full type name, CamelCased. +// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... . +func (e *EnumDescriptor) prefix() string { + if e.parent == nil { + // If the enum is not part of a message, the prefix is just the type name. + return CamelCase(*e.Name) + "_" + } + typeName := e.TypeName() + return CamelCaseSlice(typeName[0:len(typeName)-1]) + "_" +} + +// The integer value of the named constant in this enumerated type. +func (e *EnumDescriptor) integerValueAsString(name string) string { + for _, c := range e.Value { + if c.GetName() == name { + return fmt.Sprint(c.GetNumber()) + } + } + log.Fatal("cannot find value for enum constant") + return "" +} + +// ExtensionDescriptor describes an extension. If it's at top level, its parent will be nil. +// Otherwise it will be the descriptor of the message in which it is defined. +type ExtensionDescriptor struct { + common + *descriptor.FieldDescriptorProto + parent *Descriptor // The containing message, if any. +} + +// TypeName returns the elements of the dotted type name. +// The package name is not part of this name. +func (e *ExtensionDescriptor) TypeName() (s []string) { + name := e.GetName() + if e.parent == nil { + // top-level extension + s = make([]string, 1) + } else { + pname := e.parent.TypeName() + s = make([]string, len(pname)+1) + copy(s, pname) + } + s[len(s)-1] = name + return s +} + +// DescName returns the variable name used for the generated descriptor. +func (e *ExtensionDescriptor) DescName() string { + // The full type name. + typeName := e.TypeName() + // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix. + for i, s := range typeName { + typeName[i] = CamelCase(s) + } + return "E_" + strings.Join(typeName, "_") +} + +// ImportedDescriptor describes a type that has been publicly imported from another file. +type ImportedDescriptor struct { + common + o Object +} + +func (id *ImportedDescriptor) TypeName() []string { return id.o.TypeName() } + +// FileDescriptor describes an protocol buffer descriptor file (.proto). +// It includes slices of all the messages and enums defined within it. +// Those slices are constructed by WrapTypes. +type FileDescriptor struct { + *descriptor.FileDescriptorProto + desc []*Descriptor // All the messages defined in this file. + enum []*EnumDescriptor // All the enums defined in this file. + ext []*ExtensionDescriptor // All the top-level extensions defined in this file. + imp []*ImportedDescriptor // All types defined in files publicly imported by this file. + + // Comments, stored as a map of path (comma-separated integers) to the comment. + comments map[string]*descriptor.SourceCodeInfo_Location + + // The full list of symbols that are exported, + // as a map from the exported object to its symbols. + // This is used for supporting public imports. + exported map[Object][]symbol + + importPath GoImportPath // Import path of this file's package. + packageName GoPackageName // Name of this file's Go package. + + proto3 bool // whether to generate proto3 code for this file +} + +// VarName is the variable name we'll use in the generated code to refer +// to the compressed bytes of this descriptor. It is not exported, so +// it is only valid inside the generated package. +func (d *FileDescriptor) VarName() string { + h := sha256.Sum256([]byte(d.GetName())) + return fmt.Sprintf("fileDescriptor_%s", hex.EncodeToString(h[:8])) +} + +// goPackageOption interprets the file's go_package option. +// If there is no go_package, it returns ("", "", false). +// If there's a simple name, it returns ("", pkg, true). +// If the option implies an import path, it returns (impPath, pkg, true). +func (d *FileDescriptor) goPackageOption() (impPath GoImportPath, pkg GoPackageName, ok bool) { + opt := d.GetOptions().GetGoPackage() + if opt == "" { + return "", "", false + } + // A semicolon-delimited suffix delimits the import path and package name. + sc := strings.Index(opt, ";") + if sc >= 0 { + return GoImportPath(opt[:sc]), cleanPackageName(opt[sc+1:]), true + } + // The presence of a slash implies there's an import path. + slash := strings.LastIndex(opt, "/") + if slash >= 0 { + return GoImportPath(opt), cleanPackageName(opt[slash+1:]), true + } + return "", cleanPackageName(opt), true +} + +// goFileName returns the output name for the generated Go file. +func (d *FileDescriptor) goFileName(pathType pathType) string { + name := *d.Name + if ext := path.Ext(name); ext == ".proto" || ext == ".protodevel" { + name = name[:len(name)-len(ext)] + } + name += ".pb.go" + + if pathType == pathTypeSourceRelative { + return name + } + + // Does the file have a "go_package" option? + // If it does, it may override the filename. + if impPath, _, ok := d.goPackageOption(); ok && impPath != "" { + // Replace the existing dirname with the declared import path. + _, name = path.Split(name) + name = path.Join(string(impPath), name) + return name + } + + return name +} + +func (d *FileDescriptor) addExport(obj Object, sym symbol) { + d.exported[obj] = append(d.exported[obj], sym) +} + +// symbol is an interface representing an exported Go symbol. +type symbol interface { + // GenerateAlias should generate an appropriate alias + // for the symbol from the named package. + GenerateAlias(g *Generator, filename string, pkg GoPackageName) +} + +type messageSymbol struct { + sym string + hasExtensions, isMessageSet bool + oneofTypes []string +} + +type getterSymbol struct { + name string + typ string + typeName string // canonical name in proto world; empty for proto.Message and similar + genType bool // whether typ contains a generated type (message/group/enum) +} + +func (ms *messageSymbol) GenerateAlias(g *Generator, filename string, pkg GoPackageName) { + g.P("// ", ms.sym, " from public import ", filename) + g.P("type ", ms.sym, " = ", pkg, ".", ms.sym) + for _, name := range ms.oneofTypes { + g.P("type ", name, " = ", pkg, ".", name) + } +} + +type enumSymbol struct { + name string + proto3 bool // Whether this came from a proto3 file. +} + +func (es enumSymbol) GenerateAlias(g *Generator, filename string, pkg GoPackageName) { + s := es.name + g.P("// ", s, " from public import ", filename) + g.P("type ", s, " = ", pkg, ".", s) + g.P("var ", s, "_name = ", pkg, ".", s, "_name") + g.P("var ", s, "_value = ", pkg, ".", s, "_value") +} + +type constOrVarSymbol struct { + sym string + typ string // either "const" or "var" + cast string // if non-empty, a type cast is required (used for enums) +} + +func (cs constOrVarSymbol) GenerateAlias(g *Generator, filename string, pkg GoPackageName) { + v := string(pkg) + "." + cs.sym + if cs.cast != "" { + v = cs.cast + "(" + v + ")" + } + g.P(cs.typ, " ", cs.sym, " = ", v) +} + +// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects. +type Object interface { + GoImportPath() GoImportPath + TypeName() []string + File() *FileDescriptor +} + +// Generator is the type whose methods generate the output, stored in the associated response structure. +type Generator struct { + *bytes.Buffer + + Request *plugin.CodeGeneratorRequest // The input. + Response *plugin.CodeGeneratorResponse // The output. + + Param map[string]string // Command-line parameters. + PackageImportPath string // Go import path of the package we're generating code for + ImportPrefix string // String to prefix to imported package file names. + ImportMap map[string]string // Mapping from .proto file name to import path + + Pkg map[string]string // The names under which we import support packages + + outputImportPath GoImportPath // Package we're generating code for. + allFiles []*FileDescriptor // All files in the tree + allFilesByName map[string]*FileDescriptor // All files by filename. + genFiles []*FileDescriptor // Those files we will generate output for. + file *FileDescriptor // The file we are compiling now. + packageNames map[GoImportPath]GoPackageName // Imported package names in the current file. + usedPackages map[GoImportPath]bool // Packages used in current file. + usedPackageNames map[GoPackageName]bool // Package names used in the current file. + addedImports map[GoImportPath]bool // Additional imports to emit. + typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax. + init []string // Lines to emit in the init function. + indent string + pathType pathType // How to generate output filenames. + writeOutput bool + annotateCode bool // whether to store annotations + annotations []*descriptor.GeneratedCodeInfo_Annotation // annotations to store +} + +type pathType int + +const ( + pathTypeImport pathType = iota + pathTypeSourceRelative +) + +// New creates a new generator and allocates the request and response protobufs. +func New() *Generator { + g := new(Generator) + g.Buffer = new(bytes.Buffer) + g.Request = new(plugin.CodeGeneratorRequest) + g.Response = new(plugin.CodeGeneratorResponse) + return g +} + +// Error reports a problem, including an error, and exits the program. +func (g *Generator) Error(err error, msgs ...string) { + s := strings.Join(msgs, " ") + ":" + err.Error() + log.Print("protoc-gen-go: error:", s) + os.Exit(1) +} + +// Fail reports a problem and exits the program. +func (g *Generator) Fail(msgs ...string) { + s := strings.Join(msgs, " ") + log.Print("protoc-gen-go: error:", s) + os.Exit(1) +} + +// CommandLineParameters breaks the comma-separated list of key=value pairs +// in the parameter (a member of the request protobuf) into a key/value map. +// It then sets file name mappings defined by those entries. +func (g *Generator) CommandLineParameters(parameter string) { + g.Param = make(map[string]string) + for _, p := range strings.Split(parameter, ",") { + if i := strings.Index(p, "="); i < 0 { + g.Param[p] = "" + } else { + g.Param[p[0:i]] = p[i+1:] + } + } + + g.ImportMap = make(map[string]string) + pluginList := "none" // Default list of plugin names to enable (empty means all). + for k, v := range g.Param { + switch k { + case "import_prefix": + g.ImportPrefix = v + case "import_path": + g.PackageImportPath = v + case "paths": + switch v { + case "import": + g.pathType = pathTypeImport + case "source_relative": + g.pathType = pathTypeSourceRelative + default: + g.Fail(fmt.Sprintf(`Unknown path type %q: want "import" or "source_relative".`, v)) + } + case "plugins": + pluginList = v + case "annotate_code": + if v == "true" { + g.annotateCode = true + } + default: + if len(k) > 0 && k[0] == 'M' { + g.ImportMap[k[1:]] = v + } + } + } + if pluginList != "" { + // Amend the set of plugins. + enabled := make(map[string]bool) + for _, name := range strings.Split(pluginList, "+") { + enabled[name] = true + } + var nplugins []Plugin + for _, p := range plugins { + if enabled[p.Name()] { + nplugins = append(nplugins, p) + } + } + plugins = nplugins + } +} + +// DefaultPackageName returns the package name printed for the object. +// If its file is in a different package, it returns the package name we're using for this file, plus ".". +// Otherwise it returns the empty string. +func (g *Generator) DefaultPackageName(obj Object) string { + importPath := obj.GoImportPath() + if importPath == g.outputImportPath { + return "" + } + return string(g.GoPackageName(importPath)) + "." +} + +// GoPackageName returns the name used for a package. +func (g *Generator) GoPackageName(importPath GoImportPath) GoPackageName { + if name, ok := g.packageNames[importPath]; ok { + return name + } + name := cleanPackageName(baseName(string(importPath))) + for i, orig := 1, name; g.usedPackageNames[name] || isGoPredeclaredIdentifier[string(name)]; i++ { + name = orig + GoPackageName(strconv.Itoa(i)) + } + g.packageNames[importPath] = name + g.usedPackageNames[name] = true + return name +} + +// AddImport adds a package to the generated file's import section. +// It returns the name used for the package. +func (g *Generator) AddImport(importPath GoImportPath) GoPackageName { + g.addedImports[importPath] = true + return g.GoPackageName(importPath) +} + +var globalPackageNames = map[GoPackageName]bool{ + "fmt": true, + "math": true, + "proto": true, +} + +// Create and remember a guaranteed unique package name. Pkg is the candidate name. +// The FileDescriptor parameter is unused. +func RegisterUniquePackageName(pkg string, f *FileDescriptor) string { + name := cleanPackageName(pkg) + for i, orig := 1, name; globalPackageNames[name]; i++ { + name = orig + GoPackageName(strconv.Itoa(i)) + } + globalPackageNames[name] = true + return string(name) +} + +var isGoKeyword = map[string]bool{ + "break": true, + "case": true, + "chan": true, + "const": true, + "continue": true, + "default": true, + "else": true, + "defer": true, + "fallthrough": true, + "for": true, + "func": true, + "go": true, + "goto": true, + "if": true, + "import": true, + "interface": true, + "map": true, + "package": true, + "range": true, + "return": true, + "select": true, + "struct": true, + "switch": true, + "type": true, + "var": true, +} + +var isGoPredeclaredIdentifier = map[string]bool{ + "append": true, + "bool": true, + "byte": true, + "cap": true, + "close": true, + "complex": true, + "complex128": true, + "complex64": true, + "copy": true, + "delete": true, + "error": true, + "false": true, + "float32": true, + "float64": true, + "imag": true, + "int": true, + "int16": true, + "int32": true, + "int64": true, + "int8": true, + "iota": true, + "len": true, + "make": true, + "new": true, + "nil": true, + "panic": true, + "print": true, + "println": true, + "real": true, + "recover": true, + "rune": true, + "string": true, + "true": true, + "uint": true, + "uint16": true, + "uint32": true, + "uint64": true, + "uint8": true, + "uintptr": true, +} + +func cleanPackageName(name string) GoPackageName { + name = strings.Map(badToUnderscore, name) + // Identifier must not be keyword or predeclared identifier: insert _. + if isGoKeyword[name] { + name = "_" + name + } + // Identifier must not begin with digit: insert _. + if r, _ := utf8.DecodeRuneInString(name); unicode.IsDigit(r) { + name = "_" + name + } + return GoPackageName(name) +} + +// defaultGoPackage returns the package name to use, +// derived from the import path of the package we're building code for. +func (g *Generator) defaultGoPackage() GoPackageName { + p := g.PackageImportPath + if i := strings.LastIndex(p, "/"); i >= 0 { + p = p[i+1:] + } + return cleanPackageName(p) +} + +// SetPackageNames sets the package name for this run. +// The package name must agree across all files being generated. +// It also defines unique package names for all imported files. +func (g *Generator) SetPackageNames() { + g.outputImportPath = g.genFiles[0].importPath + + defaultPackageNames := make(map[GoImportPath]GoPackageName) + for _, f := range g.genFiles { + if _, p, ok := f.goPackageOption(); ok { + defaultPackageNames[f.importPath] = p + } + } + for _, f := range g.genFiles { + if _, p, ok := f.goPackageOption(); ok { + // Source file: option go_package = "quux/bar"; + f.packageName = p + } else if p, ok := defaultPackageNames[f.importPath]; ok { + // A go_package option in another file in the same package. + // + // This is a poor choice in general, since every source file should + // contain a go_package option. Supported mainly for historical + // compatibility. + f.packageName = p + } else if p := g.defaultGoPackage(); p != "" { + // Command-line: import_path=quux/bar. + // + // The import_path flag sets a package name for files which don't + // contain a go_package option. + f.packageName = p + } else if p := f.GetPackage(); p != "" { + // Source file: package quux.bar; + f.packageName = cleanPackageName(p) + } else { + // Source filename. + f.packageName = cleanPackageName(baseName(f.GetName())) + } + } + + // Check that all files have a consistent package name and import path. + for _, f := range g.genFiles[1:] { + if a, b := g.genFiles[0].importPath, f.importPath; a != b { + g.Fail(fmt.Sprintf("inconsistent package import paths: %v, %v", a, b)) + } + if a, b := g.genFiles[0].packageName, f.packageName; a != b { + g.Fail(fmt.Sprintf("inconsistent package names: %v, %v", a, b)) + } + } + + // Names of support packages. These never vary (if there are conflicts, + // we rename the conflicting package), so this could be removed someday. + g.Pkg = map[string]string{ + "fmt": "fmt", + "math": "math", + "proto": "proto", + } +} + +// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos +// and FileDescriptorProtos into file-referenced objects within the Generator. +// It also creates the list of files to generate and so should be called before GenerateAllFiles. +func (g *Generator) WrapTypes() { + g.allFiles = make([]*FileDescriptor, 0, len(g.Request.ProtoFile)) + g.allFilesByName = make(map[string]*FileDescriptor, len(g.allFiles)) + genFileNames := make(map[string]bool) + for _, n := range g.Request.FileToGenerate { + genFileNames[n] = true + } + for _, f := range g.Request.ProtoFile { + fd := &FileDescriptor{ + FileDescriptorProto: f, + exported: make(map[Object][]symbol), + proto3: fileIsProto3(f), + } + // The import path may be set in a number of ways. + if substitution, ok := g.ImportMap[f.GetName()]; ok { + // Command-line: M=foo.proto=quux/bar. + // + // Explicit mapping of source file to import path. + fd.importPath = GoImportPath(substitution) + } else if genFileNames[f.GetName()] && g.PackageImportPath != "" { + // Command-line: import_path=quux/bar. + // + // The import_path flag sets the import path for every file that + // we generate code for. + fd.importPath = GoImportPath(g.PackageImportPath) + } else if p, _, _ := fd.goPackageOption(); p != "" { + // Source file: option go_package = "quux/bar"; + // + // The go_package option sets the import path. Most users should use this. + fd.importPath = p + } else { + // Source filename. + // + // Last resort when nothing else is available. + fd.importPath = GoImportPath(path.Dir(f.GetName())) + } + // We must wrap the descriptors before we wrap the enums + fd.desc = wrapDescriptors(fd) + g.buildNestedDescriptors(fd.desc) + fd.enum = wrapEnumDescriptors(fd, fd.desc) + g.buildNestedEnums(fd.desc, fd.enum) + fd.ext = wrapExtensions(fd) + extractComments(fd) + g.allFiles = append(g.allFiles, fd) + g.allFilesByName[f.GetName()] = fd + } + for _, fd := range g.allFiles { + fd.imp = wrapImported(fd, g) + } + + g.genFiles = make([]*FileDescriptor, 0, len(g.Request.FileToGenerate)) + for _, fileName := range g.Request.FileToGenerate { + fd := g.allFilesByName[fileName] + if fd == nil { + g.Fail("could not find file named", fileName) + } + g.genFiles = append(g.genFiles, fd) + } +} + +// Scan the descriptors in this file. For each one, build the slice of nested descriptors +func (g *Generator) buildNestedDescriptors(descs []*Descriptor) { + for _, desc := range descs { + if len(desc.NestedType) != 0 { + for _, nest := range descs { + if nest.parent == desc { + desc.nested = append(desc.nested, nest) + } + } + if len(desc.nested) != len(desc.NestedType) { + g.Fail("internal error: nesting failure for", desc.GetName()) + } + } + } +} + +func (g *Generator) buildNestedEnums(descs []*Descriptor, enums []*EnumDescriptor) { + for _, desc := range descs { + if len(desc.EnumType) != 0 { + for _, enum := range enums { + if enum.parent == desc { + desc.enums = append(desc.enums, enum) + } + } + if len(desc.enums) != len(desc.EnumType) { + g.Fail("internal error: enum nesting failure for", desc.GetName()) + } + } + } +} + +// Construct the Descriptor +func newDescriptor(desc *descriptor.DescriptorProto, parent *Descriptor, file *FileDescriptor, index int) *Descriptor { + d := &Descriptor{ + common: common{file}, + DescriptorProto: desc, + parent: parent, + index: index, + } + if parent == nil { + d.path = fmt.Sprintf("%d,%d", messagePath, index) + } else { + d.path = fmt.Sprintf("%s,%d,%d", parent.path, messageMessagePath, index) + } + + // The only way to distinguish a group from a message is whether + // the containing message has a TYPE_GROUP field that matches. + if parent != nil { + parts := d.TypeName() + if file.Package != nil { + parts = append([]string{*file.Package}, parts...) + } + exp := "." + strings.Join(parts, ".") + for _, field := range parent.Field { + if field.GetType() == descriptor.FieldDescriptorProto_TYPE_GROUP && field.GetTypeName() == exp { + d.group = true + break + } + } + } + + for _, field := range desc.Extension { + d.ext = append(d.ext, &ExtensionDescriptor{common{file}, field, d}) + } + + return d +} + +// Return a slice of all the Descriptors defined within this file +func wrapDescriptors(file *FileDescriptor) []*Descriptor { + sl := make([]*Descriptor, 0, len(file.MessageType)+10) + for i, desc := range file.MessageType { + sl = wrapThisDescriptor(sl, desc, nil, file, i) + } + return sl +} + +// Wrap this Descriptor, recursively +func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *FileDescriptor, index int) []*Descriptor { + sl = append(sl, newDescriptor(desc, parent, file, index)) + me := sl[len(sl)-1] + for i, nested := range desc.NestedType { + sl = wrapThisDescriptor(sl, nested, me, file, i) + } + return sl +} + +// Construct the EnumDescriptor +func newEnumDescriptor(desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *FileDescriptor, index int) *EnumDescriptor { + ed := &EnumDescriptor{ + common: common{file}, + EnumDescriptorProto: desc, + parent: parent, + index: index, + } + if parent == nil { + ed.path = fmt.Sprintf("%d,%d", enumPath, index) + } else { + ed.path = fmt.Sprintf("%s,%d,%d", parent.path, messageEnumPath, index) + } + return ed +} + +// Return a slice of all the EnumDescriptors defined within this file +func wrapEnumDescriptors(file *FileDescriptor, descs []*Descriptor) []*EnumDescriptor { + sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10) + // Top-level enums. + for i, enum := range file.EnumType { + sl = append(sl, newEnumDescriptor(enum, nil, file, i)) + } + // Enums within messages. Enums within embedded messages appear in the outer-most message. + for _, nested := range descs { + for i, enum := range nested.EnumType { + sl = append(sl, newEnumDescriptor(enum, nested, file, i)) + } + } + return sl +} + +// Return a slice of all the top-level ExtensionDescriptors defined within this file. +func wrapExtensions(file *FileDescriptor) []*ExtensionDescriptor { + var sl []*ExtensionDescriptor + for _, field := range file.Extension { + sl = append(sl, &ExtensionDescriptor{common{file}, field, nil}) + } + return sl +} + +// Return a slice of all the types that are publicly imported into this file. +func wrapImported(file *FileDescriptor, g *Generator) (sl []*ImportedDescriptor) { + for _, index := range file.PublicDependency { + df := g.fileByName(file.Dependency[index]) + for _, d := range df.desc { + if d.GetOptions().GetMapEntry() { + continue + } + sl = append(sl, &ImportedDescriptor{common{file}, d}) + } + for _, e := range df.enum { + sl = append(sl, &ImportedDescriptor{common{file}, e}) + } + for _, ext := range df.ext { + sl = append(sl, &ImportedDescriptor{common{file}, ext}) + } + } + return +} + +func extractComments(file *FileDescriptor) { + file.comments = make(map[string]*descriptor.SourceCodeInfo_Location) + for _, loc := range file.GetSourceCodeInfo().GetLocation() { + if loc.LeadingComments == nil { + continue + } + var p []string + for _, n := range loc.Path { + p = append(p, strconv.Itoa(int(n))) + } + file.comments[strings.Join(p, ",")] = loc + } +} + +// BuildTypeNameMap builds the map from fully qualified type names to objects. +// The key names for the map come from the input data, which puts a period at the beginning. +// It should be called after SetPackageNames and before GenerateAllFiles. +func (g *Generator) BuildTypeNameMap() { + g.typeNameToObject = make(map[string]Object) + for _, f := range g.allFiles { + // The names in this loop are defined by the proto world, not us, so the + // package name may be empty. If so, the dotted package name of X will + // be ".X"; otherwise it will be ".pkg.X". + dottedPkg := "." + f.GetPackage() + if dottedPkg != "." { + dottedPkg += "." + } + for _, enum := range f.enum { + name := dottedPkg + dottedSlice(enum.TypeName()) + g.typeNameToObject[name] = enum + } + for _, desc := range f.desc { + name := dottedPkg + dottedSlice(desc.TypeName()) + g.typeNameToObject[name] = desc + } + } +} + +// ObjectNamed, given a fully-qualified input type name as it appears in the input data, +// returns the descriptor for the message or enum with that name. +func (g *Generator) ObjectNamed(typeName string) Object { + o, ok := g.typeNameToObject[typeName] + if !ok { + g.Fail("can't find object with type", typeName) + } + return o +} + +// AnnotatedAtoms is a list of atoms (as consumed by P) that records the file name and proto AST path from which they originated. +type AnnotatedAtoms struct { + source string + path string + atoms []interface{} +} + +// Annotate records the file name and proto AST path of a list of atoms +// so that a later call to P can emit a link from each atom to its origin. +func Annotate(file *FileDescriptor, path string, atoms ...interface{}) *AnnotatedAtoms { + return &AnnotatedAtoms{source: *file.Name, path: path, atoms: atoms} +} + +// printAtom prints the (atomic, non-annotation) argument to the generated output. +func (g *Generator) printAtom(v interface{}) { + switch v := v.(type) { + case string: + g.WriteString(v) + case *string: + g.WriteString(*v) + case bool: + fmt.Fprint(g, v) + case *bool: + fmt.Fprint(g, *v) + case int: + fmt.Fprint(g, v) + case *int32: + fmt.Fprint(g, *v) + case *int64: + fmt.Fprint(g, *v) + case float64: + fmt.Fprint(g, v) + case *float64: + fmt.Fprint(g, *v) + case GoPackageName: + g.WriteString(string(v)) + case GoImportPath: + g.WriteString(strconv.Quote(string(v))) + default: + g.Fail(fmt.Sprintf("unknown type in printer: %T", v)) + } +} + +// P prints the arguments to the generated output. It handles strings and int32s, plus +// handling indirections because they may be *string, etc. Any inputs of type AnnotatedAtoms may emit +// annotations in a .meta file in addition to outputting the atoms themselves (if g.annotateCode +// is true). +func (g *Generator) P(str ...interface{}) { + if !g.writeOutput { + return + } + g.WriteString(g.indent) + for _, v := range str { + switch v := v.(type) { + case *AnnotatedAtoms: + begin := int32(g.Len()) + for _, v := range v.atoms { + g.printAtom(v) + } + if g.annotateCode { + end := int32(g.Len()) + var path []int32 + for _, token := range strings.Split(v.path, ",") { + val, err := strconv.ParseInt(token, 10, 32) + if err != nil { + g.Fail("could not parse proto AST path: ", err.Error()) + } + path = append(path, int32(val)) + } + g.annotations = append(g.annotations, &descriptor.GeneratedCodeInfo_Annotation{ + Path: path, + SourceFile: &v.source, + Begin: &begin, + End: &end, + }) + } + default: + g.printAtom(v) + } + } + g.WriteByte('\n') +} + +// addInitf stores the given statement to be printed inside the file's init function. +// The statement is given as a format specifier and arguments. +func (g *Generator) addInitf(stmt string, a ...interface{}) { + g.init = append(g.init, fmt.Sprintf(stmt, a...)) +} + +// In Indents the output one tab stop. +func (g *Generator) In() { g.indent += "\t" } + +// Out unindents the output one tab stop. +func (g *Generator) Out() { + if len(g.indent) > 0 { + g.indent = g.indent[1:] + } +} + +// GenerateAllFiles generates the output for all the files we're outputting. +func (g *Generator) GenerateAllFiles() { + // Initialize the plugins + for _, p := range plugins { + p.Init(g) + } + // Generate the output. The generator runs for every file, even the files + // that we don't generate output for, so that we can collate the full list + // of exported symbols to support public imports. + genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles)) + for _, file := range g.genFiles { + genFileMap[file] = true + } + for _, file := range g.allFiles { + g.Reset() + g.annotations = nil + g.writeOutput = genFileMap[file] + g.generate(file) + if !g.writeOutput { + continue + } + fname := file.goFileName(g.pathType) + g.Response.File = append(g.Response.File, &plugin.CodeGeneratorResponse_File{ + Name: proto.String(fname), + Content: proto.String(g.String()), + }) + if g.annotateCode { + // Store the generated code annotations in text, as the protoc plugin protocol requires that + // strings contain valid UTF-8. + g.Response.File = append(g.Response.File, &plugin.CodeGeneratorResponse_File{ + Name: proto.String(file.goFileName(g.pathType) + ".meta"), + Content: proto.String(proto.CompactTextString(&descriptor.GeneratedCodeInfo{Annotation: g.annotations})), + }) + } + } +} + +// Run all the plugins associated with the file. +func (g *Generator) runPlugins(file *FileDescriptor) { + for _, p := range plugins { + p.Generate(file) + } +} + +// Fill the response protocol buffer with the generated output for all the files we're +// supposed to generate. +func (g *Generator) generate(file *FileDescriptor) { + g.file = file + g.usedPackages = make(map[GoImportPath]bool) + g.packageNames = make(map[GoImportPath]GoPackageName) + g.usedPackageNames = make(map[GoPackageName]bool) + g.addedImports = make(map[GoImportPath]bool) + for name := range globalPackageNames { + g.usedPackageNames[name] = true + } + + g.P("// This is a compile-time assertion to ensure that this generated file") + g.P("// is compatible with the proto package it is being compiled against.") + g.P("// A compilation error at this line likely means your copy of the") + g.P("// proto package needs to be updated.") + g.P("const _ = ", g.Pkg["proto"], ".ProtoPackageIsVersion", generatedCodeVersion, " // please upgrade the proto package") + g.P() + + for _, td := range g.file.imp { + g.generateImported(td) + } + for _, enum := range g.file.enum { + g.generateEnum(enum) + } + for _, desc := range g.file.desc { + // Don't generate virtual messages for maps. + if desc.GetOptions().GetMapEntry() { + continue + } + g.generateMessage(desc) + } + for _, ext := range g.file.ext { + g.generateExtension(ext) + } + g.generateInitFunction() + g.generateFileDescriptor(file) + + // Run the plugins before the imports so we know which imports are necessary. + g.runPlugins(file) + + // Generate header and imports last, though they appear first in the output. + rem := g.Buffer + remAnno := g.annotations + g.Buffer = new(bytes.Buffer) + g.annotations = nil + g.generateHeader() + g.generateImports() + if !g.writeOutput { + return + } + // Adjust the offsets for annotations displaced by the header and imports. + for _, anno := range remAnno { + *anno.Begin += int32(g.Len()) + *anno.End += int32(g.Len()) + g.annotations = append(g.annotations, anno) + } + g.Write(rem.Bytes()) + + // Reformat generated code and patch annotation locations. + fset := token.NewFileSet() + original := g.Bytes() + if g.annotateCode { + // make a copy independent of g; we'll need it after Reset. + original = append([]byte(nil), original...) + } + fileAST, err := parser.ParseFile(fset, "", original, parser.ParseComments) + if err != nil { + // Print out the bad code with line numbers. + // This should never happen in practice, but it can while changing generated code, + // so consider this a debugging aid. + var src bytes.Buffer + s := bufio.NewScanner(bytes.NewReader(original)) + for line := 1; s.Scan(); line++ { + fmt.Fprintf(&src, "%5d\t%s\n", line, s.Bytes()) + } + g.Fail("bad Go source code was generated:", err.Error(), "\n"+src.String()) + } + ast.SortImports(fset, fileAST) + g.Reset() + err = (&printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}).Fprint(g, fset, fileAST) + if err != nil { + g.Fail("generated Go source code could not be reformatted:", err.Error()) + } + if g.annotateCode { + m, err := remap.Compute(original, g.Bytes()) + if err != nil { + g.Fail("formatted generated Go source code could not be mapped back to the original code:", err.Error()) + } + for _, anno := range g.annotations { + new, ok := m.Find(int(*anno.Begin), int(*anno.End)) + if !ok { + g.Fail("span in formatted generated Go source code could not be mapped back to the original code") + } + *anno.Begin = int32(new.Pos) + *anno.End = int32(new.End) + } + } +} + +// Generate the header, including package definition +func (g *Generator) generateHeader() { + g.P("// Code generated by protoc-gen-go. DO NOT EDIT.") + if g.file.GetOptions().GetDeprecated() { + g.P("// ", g.file.Name, " is a deprecated file.") + } else { + g.P("// source: ", g.file.Name) + } + g.P() + g.PrintComments(strconv.Itoa(packagePath)) + g.P() + g.P("package ", g.file.packageName) + g.P() +} + +// deprecationComment is the standard comment added to deprecated +// messages, fields, enums, and enum values. +var deprecationComment = "// Deprecated: Do not use." + +// PrintComments prints any comments from the source .proto file. +// The path is a comma-separated list of integers. +// It returns an indication of whether any comments were printed. +// See descriptor.proto for its format. +func (g *Generator) PrintComments(path string) bool { + if !g.writeOutput { + return false + } + if c, ok := g.makeComments(path); ok { + g.P(c) + return true + } + return false +} + +// makeComments generates the comment string for the field, no "\n" at the end +func (g *Generator) makeComments(path string) (string, bool) { + loc, ok := g.file.comments[path] + if !ok { + return "", false + } + w := new(bytes.Buffer) + nl := "" + for _, line := range strings.Split(strings.TrimSuffix(loc.GetLeadingComments(), "\n"), "\n") { + fmt.Fprintf(w, "%s//%s", nl, line) + nl = "\n" + } + return w.String(), true +} + +func (g *Generator) fileByName(filename string) *FileDescriptor { + return g.allFilesByName[filename] +} + +// weak returns whether the ith import of the current file is a weak import. +func (g *Generator) weak(i int32) bool { + for _, j := range g.file.WeakDependency { + if j == i { + return true + } + } + return false +} + +// Generate the imports +func (g *Generator) generateImports() { + imports := make(map[GoImportPath]GoPackageName) + for i, s := range g.file.Dependency { + fd := g.fileByName(s) + importPath := fd.importPath + // Do not import our own package. + if importPath == g.file.importPath { + continue + } + // Do not import weak imports. + if g.weak(int32(i)) { + continue + } + // Do not import a package twice. + if _, ok := imports[importPath]; ok { + continue + } + // We need to import all the dependencies, even if we don't reference them, + // because other code and tools depend on having the full transitive closure + // of protocol buffer types in the binary. + packageName := g.GoPackageName(importPath) + if _, ok := g.usedPackages[importPath]; !ok { + packageName = "_" + } + imports[importPath] = packageName + } + for importPath := range g.addedImports { + imports[importPath] = g.GoPackageName(importPath) + } + // We almost always need a proto import. Rather than computing when we + // do, which is tricky when there's a plugin, just import it and + // reference it later. The same argument applies to the fmt and math packages. + g.P("import (") + g.P(g.Pkg["fmt"] + ` "fmt"`) + g.P(g.Pkg["math"] + ` "math"`) + g.P(g.Pkg["proto"]+" ", GoImportPath(g.ImportPrefix)+"github.com/golang/protobuf/proto") + for importPath, packageName := range imports { + g.P(packageName, " ", GoImportPath(g.ImportPrefix)+importPath) + } + g.P(")") + g.P() + // TODO: may need to worry about uniqueness across plugins + for _, p := range plugins { + p.GenerateImports(g.file) + g.P() + } + g.P("// Reference imports to suppress errors if they are not otherwise used.") + g.P("var _ = ", g.Pkg["proto"], ".Marshal") + g.P("var _ = ", g.Pkg["fmt"], ".Errorf") + g.P("var _ = ", g.Pkg["math"], ".Inf") + g.P() +} + +func (g *Generator) generateImported(id *ImportedDescriptor) { + df := id.o.File() + filename := *df.Name + if df.importPath == g.file.importPath { + // Don't generate type aliases for files in the same Go package as this one. + return + } + if !supportTypeAliases { + g.Fail(fmt.Sprintf("%s: public imports require at least go1.9", filename)) + } + g.usedPackages[df.importPath] = true + + for _, sym := range df.exported[id.o] { + sym.GenerateAlias(g, filename, g.GoPackageName(df.importPath)) + } + + g.P() +} + +// Generate the enum definitions for this EnumDescriptor. +func (g *Generator) generateEnum(enum *EnumDescriptor) { + // The full type name + typeName := enum.TypeName() + // The full type name, CamelCased. + ccTypeName := CamelCaseSlice(typeName) + ccPrefix := enum.prefix() + + deprecatedEnum := "" + if enum.GetOptions().GetDeprecated() { + deprecatedEnum = deprecationComment + } + g.PrintComments(enum.path) + g.P("type ", Annotate(enum.file, enum.path, ccTypeName), " int32", deprecatedEnum) + g.file.addExport(enum, enumSymbol{ccTypeName, enum.proto3()}) + g.P("const (") + for i, e := range enum.Value { + etorPath := fmt.Sprintf("%s,%d,%d", enum.path, enumValuePath, i) + g.PrintComments(etorPath) + + deprecatedValue := "" + if e.GetOptions().GetDeprecated() { + deprecatedValue = deprecationComment + } + + name := ccPrefix + *e.Name + g.P(Annotate(enum.file, etorPath, name), " ", ccTypeName, " = ", e.Number, " ", deprecatedValue) + g.file.addExport(enum, constOrVarSymbol{name, "const", ccTypeName}) + } + g.P(")") + g.P() + g.P("var ", ccTypeName, "_name = map[int32]string{") + generated := make(map[int32]bool) // avoid duplicate values + for _, e := range enum.Value { + duplicate := "" + if _, present := generated[*e.Number]; present { + duplicate = "// Duplicate value: " + } + g.P(duplicate, e.Number, ": ", strconv.Quote(*e.Name), ",") + generated[*e.Number] = true + } + g.P("}") + g.P() + g.P("var ", ccTypeName, "_value = map[string]int32{") + for _, e := range enum.Value { + g.P(strconv.Quote(*e.Name), ": ", e.Number, ",") + } + g.P("}") + g.P() + + if !enum.proto3() { + g.P("func (x ", ccTypeName, ") Enum() *", ccTypeName, " {") + g.P("p := new(", ccTypeName, ")") + g.P("*p = x") + g.P("return p") + g.P("}") + g.P() + } + + g.P("func (x ", ccTypeName, ") String() string {") + g.P("return ", g.Pkg["proto"], ".EnumName(", ccTypeName, "_name, int32(x))") + g.P("}") + g.P() + + if !enum.proto3() { + g.P("func (x *", ccTypeName, ") UnmarshalJSON(data []byte) error {") + g.P("value, err := ", g.Pkg["proto"], ".UnmarshalJSONEnum(", ccTypeName, `_value, data, "`, ccTypeName, `")`) + g.P("if err != nil {") + g.P("return err") + g.P("}") + g.P("*x = ", ccTypeName, "(value)") + g.P("return nil") + g.P("}") + g.P() + } + + var indexes []string + for m := enum.parent; m != nil; m = m.parent { + // XXX: skip groups? + indexes = append([]string{strconv.Itoa(m.index)}, indexes...) + } + indexes = append(indexes, strconv.Itoa(enum.index)) + g.P("func (", ccTypeName, ") EnumDescriptor() ([]byte, []int) {") + g.P("return ", g.file.VarName(), ", []int{", strings.Join(indexes, ", "), "}") + g.P("}") + g.P() + if enum.file.GetPackage() == "google.protobuf" && enum.GetName() == "NullValue" { + g.P("func (", ccTypeName, `) XXX_WellKnownType() string { return "`, enum.GetName(), `" }`) + g.P() + } + + g.generateEnumRegistration(enum) +} + +// The tag is a string like "varint,2,opt,name=fieldname,def=7" that +// identifies details of the field for the protocol buffer marshaling and unmarshaling +// code. The fields are: +// wire encoding +// protocol tag number +// opt,req,rep for optional, required, or repeated +// packed whether the encoding is "packed" (optional; repeated primitives only) +// name= the original declared name +// enum= the name of the enum type if it is an enum-typed field. +// proto3 if this field is in a proto3 message +// def= string representation of the default value, if any. +// The default value must be in a representation that can be used at run-time +// to generate the default value. Thus bools become 0 and 1, for instance. +func (g *Generator) goTag(message *Descriptor, field *descriptor.FieldDescriptorProto, wiretype string) string { + optrepreq := "" + switch { + case isOptional(field): + optrepreq = "opt" + case isRequired(field): + optrepreq = "req" + case isRepeated(field): + optrepreq = "rep" + } + var defaultValue string + if dv := field.DefaultValue; dv != nil { // set means an explicit default + defaultValue = *dv + // Some types need tweaking. + switch *field.Type { + case descriptor.FieldDescriptorProto_TYPE_BOOL: + if defaultValue == "true" { + defaultValue = "1" + } else { + defaultValue = "0" + } + case descriptor.FieldDescriptorProto_TYPE_STRING, + descriptor.FieldDescriptorProto_TYPE_BYTES: + // Nothing to do. Quoting is done for the whole tag. + case descriptor.FieldDescriptorProto_TYPE_ENUM: + // For enums we need to provide the integer constant. + obj := g.ObjectNamed(field.GetTypeName()) + if id, ok := obj.(*ImportedDescriptor); ok { + // It is an enum that was publicly imported. + // We need the underlying type. + obj = id.o + } + enum, ok := obj.(*EnumDescriptor) + if !ok { + log.Printf("obj is a %T", obj) + if id, ok := obj.(*ImportedDescriptor); ok { + log.Printf("id.o is a %T", id.o) + } + g.Fail("unknown enum type", CamelCaseSlice(obj.TypeName())) + } + defaultValue = enum.integerValueAsString(defaultValue) + case descriptor.FieldDescriptorProto_TYPE_FLOAT: + if def := defaultValue; def != "inf" && def != "-inf" && def != "nan" { + if f, err := strconv.ParseFloat(defaultValue, 32); err == nil { + defaultValue = fmt.Sprint(float32(f)) + } + } + case descriptor.FieldDescriptorProto_TYPE_DOUBLE: + if def := defaultValue; def != "inf" && def != "-inf" && def != "nan" { + if f, err := strconv.ParseFloat(defaultValue, 64); err == nil { + defaultValue = fmt.Sprint(f) + } + } + } + defaultValue = ",def=" + defaultValue + } + enum := "" + if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM { + // We avoid using obj.GoPackageName(), because we want to use the + // original (proto-world) package name. + obj := g.ObjectNamed(field.GetTypeName()) + if id, ok := obj.(*ImportedDescriptor); ok { + obj = id.o + } + enum = ",enum=" + if pkg := obj.File().GetPackage(); pkg != "" { + enum += pkg + "." + } + enum += CamelCaseSlice(obj.TypeName()) + } + packed := "" + if (field.Options != nil && field.Options.GetPacked()) || + // Per https://developers.google.com/protocol-buffers/docs/proto3#simple: + // "In proto3, repeated fields of scalar numeric types use packed encoding by default." + (message.proto3() && (field.Options == nil || field.Options.Packed == nil) && + isRepeated(field) && isScalar(field)) { + packed = ",packed" + } + fieldName := field.GetName() + name := fieldName + if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP { + // We must use the type name for groups instead of + // the field name to preserve capitalization. + // type_name in FieldDescriptorProto is fully-qualified, + // but we only want the local part. + name = *field.TypeName + if i := strings.LastIndex(name, "."); i >= 0 { + name = name[i+1:] + } + } + if json := field.GetJsonName(); field.Extendee == nil && json != "" && json != name { + // TODO: escaping might be needed, in which case + // perhaps this should be in its own "json" tag. + name += ",json=" + json + } + name = ",name=" + name + if message.proto3() { + name += ",proto3" + } + oneof := "" + if field.OneofIndex != nil { + oneof = ",oneof" + } + return strconv.Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s%s", + wiretype, + field.GetNumber(), + optrepreq, + packed, + name, + enum, + oneof, + defaultValue)) +} + +func needsStar(typ descriptor.FieldDescriptorProto_Type) bool { + switch typ { + case descriptor.FieldDescriptorProto_TYPE_GROUP: + return false + case descriptor.FieldDescriptorProto_TYPE_MESSAGE: + return false + case descriptor.FieldDescriptorProto_TYPE_BYTES: + return false + } + return true +} + +// TypeName is the printed name appropriate for an item. If the object is in the current file, +// TypeName drops the package name and underscores the rest. +// Otherwise the object is from another package; and the result is the underscored +// package name followed by the item name. +// The result always has an initial capital. +func (g *Generator) TypeName(obj Object) string { + return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName()) +} + +// GoType returns a string representing the type name, and the wire type +func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) { + // TODO: Options. + switch *field.Type { + case descriptor.FieldDescriptorProto_TYPE_DOUBLE: + typ, wire = "float64", "fixed64" + case descriptor.FieldDescriptorProto_TYPE_FLOAT: + typ, wire = "float32", "fixed32" + case descriptor.FieldDescriptorProto_TYPE_INT64: + typ, wire = "int64", "varint" + case descriptor.FieldDescriptorProto_TYPE_UINT64: + typ, wire = "uint64", "varint" + case descriptor.FieldDescriptorProto_TYPE_INT32: + typ, wire = "int32", "varint" + case descriptor.FieldDescriptorProto_TYPE_UINT32: + typ, wire = "uint32", "varint" + case descriptor.FieldDescriptorProto_TYPE_FIXED64: + typ, wire = "uint64", "fixed64" + case descriptor.FieldDescriptorProto_TYPE_FIXED32: + typ, wire = "uint32", "fixed32" + case descriptor.FieldDescriptorProto_TYPE_BOOL: + typ, wire = "bool", "varint" + case descriptor.FieldDescriptorProto_TYPE_STRING: + typ, wire = "string", "bytes" + case descriptor.FieldDescriptorProto_TYPE_GROUP: + desc := g.ObjectNamed(field.GetTypeName()) + typ, wire = "*"+g.TypeName(desc), "group" + case descriptor.FieldDescriptorProto_TYPE_MESSAGE: + desc := g.ObjectNamed(field.GetTypeName()) + typ, wire = "*"+g.TypeName(desc), "bytes" + case descriptor.FieldDescriptorProto_TYPE_BYTES: + typ, wire = "[]byte", "bytes" + case descriptor.FieldDescriptorProto_TYPE_ENUM: + desc := g.ObjectNamed(field.GetTypeName()) + typ, wire = g.TypeName(desc), "varint" + case descriptor.FieldDescriptorProto_TYPE_SFIXED32: + typ, wire = "int32", "fixed32" + case descriptor.FieldDescriptorProto_TYPE_SFIXED64: + typ, wire = "int64", "fixed64" + case descriptor.FieldDescriptorProto_TYPE_SINT32: + typ, wire = "int32", "zigzag32" + case descriptor.FieldDescriptorProto_TYPE_SINT64: + typ, wire = "int64", "zigzag64" + default: + g.Fail("unknown type for", field.GetName()) + } + if isRepeated(field) { + typ = "[]" + typ + } else if message != nil && message.proto3() { + return + } else if field.OneofIndex != nil && message != nil { + return + } else if needsStar(*field.Type) { + typ = "*" + typ + } + return +} + +func (g *Generator) RecordTypeUse(t string) { + if _, ok := g.typeNameToObject[t]; !ok { + return + } + importPath := g.ObjectNamed(t).GoImportPath() + if importPath == g.outputImportPath { + // Don't record use of objects in our package. + return + } + g.AddImport(importPath) + g.usedPackages[importPath] = true +} + +// Method names that may be generated. Fields with these names get an +// underscore appended. Any change to this set is a potential incompatible +// API change because it changes generated field names. +var methodNames = [...]string{ + "Reset", + "String", + "ProtoMessage", + "Marshal", + "Unmarshal", + "ExtensionRangeArray", + "ExtensionMap", + "Descriptor", +} + +// Names of messages in the `google.protobuf` package for which +// we will generate XXX_WellKnownType methods. +var wellKnownTypes = map[string]bool{ + "Any": true, + "Duration": true, + "Empty": true, + "Struct": true, + "Timestamp": true, + + "Value": true, + "ListValue": true, + "DoubleValue": true, + "FloatValue": true, + "Int64Value": true, + "UInt64Value": true, + "Int32Value": true, + "UInt32Value": true, + "BoolValue": true, + "StringValue": true, + "BytesValue": true, +} + +// getterDefault finds the default value for the field to return from a getter, +// regardless of if it's a built in default or explicit from the source. Returns e.g. "nil", `""`, "Default_MessageType_FieldName" +func (g *Generator) getterDefault(field *descriptor.FieldDescriptorProto, goMessageType string) string { + if isRepeated(field) { + return "nil" + } + if def := field.GetDefaultValue(); def != "" { + defaultConstant := g.defaultConstantName(goMessageType, field.GetName()) + if *field.Type != descriptor.FieldDescriptorProto_TYPE_BYTES { + return defaultConstant + } + return "append([]byte(nil), " + defaultConstant + "...)" + } + switch *field.Type { + case descriptor.FieldDescriptorProto_TYPE_BOOL: + return "false" + case descriptor.FieldDescriptorProto_TYPE_STRING: + return `""` + case descriptor.FieldDescriptorProto_TYPE_GROUP, descriptor.FieldDescriptorProto_TYPE_MESSAGE, descriptor.FieldDescriptorProto_TYPE_BYTES: + return "nil" + case descriptor.FieldDescriptorProto_TYPE_ENUM: + obj := g.ObjectNamed(field.GetTypeName()) + var enum *EnumDescriptor + if id, ok := obj.(*ImportedDescriptor); ok { + // The enum type has been publicly imported. + enum, _ = id.o.(*EnumDescriptor) + } else { + enum, _ = obj.(*EnumDescriptor) + } + if enum == nil { + log.Printf("don't know how to generate getter for %s", field.GetName()) + return "nil" + } + if len(enum.Value) == 0 { + return "0 // empty enum" + } + first := enum.Value[0].GetName() + return g.DefaultPackageName(obj) + enum.prefix() + first + default: + return "0" + } +} + +// defaultConstantName builds the name of the default constant from the message +// type name and the untouched field name, e.g. "Default_MessageType_FieldName" +func (g *Generator) defaultConstantName(goMessageType, protoFieldName string) string { + return "Default_" + goMessageType + "_" + CamelCase(protoFieldName) +} + +// The different types of fields in a message and how to actually print them +// Most of the logic for generateMessage is in the methods of these types. +// +// Note that the content of the field is irrelevant, a simpleField can contain +// anything from a scalar to a group (which is just a message). +// +// Extension fields (and message sets) are however handled separately. +// +// simpleField - a field that is neiter weak nor oneof, possibly repeated +// oneofField - field containing list of subfields: +// - oneofSubField - a field within the oneof + +// msgCtx contains the context for the generator functions. +type msgCtx struct { + goName string // Go struct name of the message, e.g. MessageName + message *Descriptor // The descriptor for the message +} + +// fieldCommon contains data common to all types of fields. +type fieldCommon struct { + goName string // Go name of field, e.g. "FieldName" or "Descriptor_" + protoName string // Name of field in proto language, e.g. "field_name" or "descriptor" + getterName string // Name of the getter, e.g. "GetFieldName" or "GetDescriptor_" + goType string // The Go type as a string, e.g. "*int32" or "*OtherMessage" + tags string // The tag string/annotation for the type, e.g. `protobuf:"varint,8,opt,name=region_id,json=regionId"` + fullPath string // The full path of the field as used by Annotate etc, e.g. "4,0,2,0" +} + +// getProtoName gets the proto name of a field, e.g. "field_name" or "descriptor". +func (f *fieldCommon) getProtoName() string { + return f.protoName +} + +// getGoType returns the go type of the field as a string, e.g. "*int32". +func (f *fieldCommon) getGoType() string { + return f.goType +} + +// simpleField is not weak, not a oneof, not an extension. Can be required, optional or repeated. +type simpleField struct { + fieldCommon + protoTypeName string // Proto type name, empty if primitive, e.g. ".google.protobuf.Duration" + protoType descriptor.FieldDescriptorProto_Type // Actual type enum value, e.g. descriptor.FieldDescriptorProto_TYPE_FIXED64 + deprecated string // Deprecation comment, if any, e.g. "// Deprecated: Do not use." + getterDef string // Default for getters, e.g. "nil", `""` or "Default_MessageType_FieldName" + protoDef string // Default value as defined in the proto file, e.g "yoshi" or "5" + comment string // The full comment for the field, e.g. "// Useful information" +} + +// decl prints the declaration of the field in the struct (if any). +func (f *simpleField) decl(g *Generator, mc *msgCtx) { + g.P(f.comment, Annotate(mc.message.file, f.fullPath, f.goName), "\t", f.goType, "\t`", f.tags, "`", f.deprecated) +} + +// getter prints the getter for the field. +func (f *simpleField) getter(g *Generator, mc *msgCtx) { + star := "" + tname := f.goType + if needsStar(f.protoType) && tname[0] == '*' { + tname = tname[1:] + star = "*" + } + if f.deprecated != "" { + g.P(f.deprecated) + } + g.P("func (m *", mc.goName, ") ", Annotate(mc.message.file, f.fullPath, f.getterName), "() "+tname+" {") + if f.getterDef == "nil" { // Simpler getter + g.P("if m != nil {") + g.P("return m." + f.goName) + g.P("}") + g.P("return nil") + g.P("}") + g.P() + return + } + if mc.message.proto3() { + g.P("if m != nil {") + } else { + g.P("if m != nil && m." + f.goName + " != nil {") + } + g.P("return " + star + "m." + f.goName) + g.P("}") + g.P("return ", f.getterDef) + g.P("}") + g.P() +} + +// setter prints the setter method of the field. +func (f *simpleField) setter(g *Generator, mc *msgCtx) { + // No setter for regular fields yet +} + +// getProtoDef returns the default value explicitly stated in the proto file, e.g "yoshi" or "5". +func (f *simpleField) getProtoDef() string { + return f.protoDef +} + +// getProtoTypeName returns the protobuf type name for the field as returned by field.GetTypeName(), e.g. ".google.protobuf.Duration". +func (f *simpleField) getProtoTypeName() string { + return f.protoTypeName +} + +// getProtoType returns the *field.Type value, e.g. descriptor.FieldDescriptorProto_TYPE_FIXED64. +func (f *simpleField) getProtoType() descriptor.FieldDescriptorProto_Type { + return f.protoType +} + +// oneofSubFields are kept slize held by each oneofField. They do not appear in the top level slize of fields for the message. +type oneofSubField struct { + fieldCommon + protoTypeName string // Proto type name, empty if primitive, e.g. ".google.protobuf.Duration" + protoType descriptor.FieldDescriptorProto_Type // Actual type enum value, e.g. descriptor.FieldDescriptorProto_TYPE_FIXED64 + oneofTypeName string // Type name of the enclosing struct, e.g. "MessageName_FieldName" + fieldNumber int // Actual field number, as defined in proto, e.g. 12 + getterDef string // Default for getters, e.g. "nil", `""` or "Default_MessageType_FieldName" + protoDef string // Default value as defined in the proto file, e.g "yoshi" or "5" + deprecated string // Deprecation comment, if any. +} + +// typedNil prints a nil casted to the pointer to this field. +// - for XXX_OneofWrappers +func (f *oneofSubField) typedNil(g *Generator) { + g.P("(*", f.oneofTypeName, ")(nil),") +} + +// getProtoDef returns the default value explicitly stated in the proto file, e.g "yoshi" or "5". +func (f *oneofSubField) getProtoDef() string { + return f.protoDef +} + +// getProtoTypeName returns the protobuf type name for the field as returned by field.GetTypeName(), e.g. ".google.protobuf.Duration". +func (f *oneofSubField) getProtoTypeName() string { + return f.protoTypeName +} + +// getProtoType returns the *field.Type value, e.g. descriptor.FieldDescriptorProto_TYPE_FIXED64. +func (f *oneofSubField) getProtoType() descriptor.FieldDescriptorProto_Type { + return f.protoType +} + +// oneofField represents the oneof on top level. +// The alternative fields within the oneof are represented by oneofSubField. +type oneofField struct { + fieldCommon + subFields []*oneofSubField // All the possible oneof fields + comment string // The full comment for the field, e.g. "// Types that are valid to be assigned to MyOneof:\n\\" +} + +// decl prints the declaration of the field in the struct (if any). +func (f *oneofField) decl(g *Generator, mc *msgCtx) { + comment := f.comment + for _, sf := range f.subFields { + comment += "//\t*" + sf.oneofTypeName + "\n" + } + g.P(comment, Annotate(mc.message.file, f.fullPath, f.goName), " ", f.goType, " `", f.tags, "`") +} + +// getter for a oneof field will print additional discriminators and interfaces for the oneof, +// also it prints all the getters for the sub fields. +func (f *oneofField) getter(g *Generator, mc *msgCtx) { + // The discriminator type + g.P("type ", f.goType, " interface {") + g.P(f.goType, "()") + g.P("}") + g.P() + // The subField types, fulfilling the discriminator type contract + for _, sf := range f.subFields { + g.P("type ", Annotate(mc.message.file, sf.fullPath, sf.oneofTypeName), " struct {") + g.P(Annotate(mc.message.file, sf.fullPath, sf.goName), " ", sf.goType, " `", sf.tags, "`") + g.P("}") + g.P() + } + for _, sf := range f.subFields { + g.P("func (*", sf.oneofTypeName, ") ", f.goType, "() {}") + g.P() + } + // Getter for the oneof field + g.P("func (m *", mc.goName, ") ", Annotate(mc.message.file, f.fullPath, f.getterName), "() ", f.goType, " {") + g.P("if m != nil { return m.", f.goName, " }") + g.P("return nil") + g.P("}") + g.P() + // Getters for each oneof + for _, sf := range f.subFields { + if sf.deprecated != "" { + g.P(sf.deprecated) + } + g.P("func (m *", mc.goName, ") ", Annotate(mc.message.file, sf.fullPath, sf.getterName), "() "+sf.goType+" {") + g.P("if x, ok := m.", f.getterName, "().(*", sf.oneofTypeName, "); ok {") + g.P("return x.", sf.goName) + g.P("}") + g.P("return ", sf.getterDef) + g.P("}") + g.P() + } +} + +// setter prints the setter method of the field. +func (f *oneofField) setter(g *Generator, mc *msgCtx) { + // No setters for oneof yet +} + +// topLevelField interface implemented by all types of fields on the top level (not oneofSubField). +type topLevelField interface { + decl(g *Generator, mc *msgCtx) // print declaration within the struct + getter(g *Generator, mc *msgCtx) // print getter + setter(g *Generator, mc *msgCtx) // print setter if applicable +} + +// defField interface implemented by all types of fields that can have defaults (not oneofField, but instead oneofSubField). +type defField interface { + getProtoDef() string // default value explicitly stated in the proto file, e.g "yoshi" or "5" + getProtoName() string // proto name of a field, e.g. "field_name" or "descriptor" + getGoType() string // go type of the field as a string, e.g. "*int32" + getProtoTypeName() string // protobuf type name for the field, e.g. ".google.protobuf.Duration" + getProtoType() descriptor.FieldDescriptorProto_Type // *field.Type value, e.g. descriptor.FieldDescriptorProto_TYPE_FIXED64 +} + +// generateDefaultConstants adds constants for default values if needed, which is only if the default value is. +// explicit in the proto. +func (g *Generator) generateDefaultConstants(mc *msgCtx, topLevelFields []topLevelField) { + // Collect fields that can have defaults + dFields := []defField{} + for _, pf := range topLevelFields { + if f, ok := pf.(*oneofField); ok { + for _, osf := range f.subFields { + dFields = append(dFields, osf) + } + continue + } + dFields = append(dFields, pf.(defField)) + } + for _, df := range dFields { + def := df.getProtoDef() + if def == "" { + continue + } + fieldname := g.defaultConstantName(mc.goName, df.getProtoName()) + typename := df.getGoType() + if typename[0] == '*' { + typename = typename[1:] + } + kind := "const " + switch { + case typename == "bool": + case typename == "string": + def = strconv.Quote(def) + case typename == "[]byte": + def = "[]byte(" + strconv.Quote(unescape(def)) + ")" + kind = "var " + case def == "inf", def == "-inf", def == "nan": + // These names are known to, and defined by, the protocol language. + switch def { + case "inf": + def = "math.Inf(1)" + case "-inf": + def = "math.Inf(-1)" + case "nan": + def = "math.NaN()" + } + if df.getProtoType() == descriptor.FieldDescriptorProto_TYPE_FLOAT { + def = "float32(" + def + ")" + } + kind = "var " + case df.getProtoType() == descriptor.FieldDescriptorProto_TYPE_FLOAT: + if f, err := strconv.ParseFloat(def, 32); err == nil { + def = fmt.Sprint(float32(f)) + } + case df.getProtoType() == descriptor.FieldDescriptorProto_TYPE_DOUBLE: + if f, err := strconv.ParseFloat(def, 64); err == nil { + def = fmt.Sprint(f) + } + case df.getProtoType() == descriptor.FieldDescriptorProto_TYPE_ENUM: + // Must be an enum. Need to construct the prefixed name. + obj := g.ObjectNamed(df.getProtoTypeName()) + var enum *EnumDescriptor + if id, ok := obj.(*ImportedDescriptor); ok { + // The enum type has been publicly imported. + enum, _ = id.o.(*EnumDescriptor) + } else { + enum, _ = obj.(*EnumDescriptor) + } + if enum == nil { + log.Printf("don't know how to generate constant for %s", fieldname) + continue + } + def = g.DefaultPackageName(obj) + enum.prefix() + def + } + g.P(kind, fieldname, " ", typename, " = ", def) + g.file.addExport(mc.message, constOrVarSymbol{fieldname, kind, ""}) + } + g.P() +} + +// generateInternalStructFields just adds the XXX_ fields to the message struct. +func (g *Generator) generateInternalStructFields(mc *msgCtx, topLevelFields []topLevelField) { + g.P("XXX_NoUnkeyedLiteral\tstruct{} `json:\"-\"`") // prevent unkeyed struct literals + if len(mc.message.ExtensionRange) > 0 { + messageset := "" + if opts := mc.message.Options; opts != nil && opts.GetMessageSetWireFormat() { + messageset = "protobuf_messageset:\"1\" " + } + g.P(g.Pkg["proto"], ".XXX_InternalExtensions `", messageset, "json:\"-\"`") + } + g.P("XXX_unrecognized\t[]byte `json:\"-\"`") + g.P("XXX_sizecache\tint32 `json:\"-\"`") + +} + +// generateOneofFuncs adds all the utility functions for oneof, including marshaling, unmarshaling and sizer. +func (g *Generator) generateOneofFuncs(mc *msgCtx, topLevelFields []topLevelField) { + ofields := []*oneofField{} + for _, f := range topLevelFields { + if o, ok := f.(*oneofField); ok { + ofields = append(ofields, o) + } + } + if len(ofields) == 0 { + return + } + + // OneofFuncs + g.P("// XXX_OneofWrappers is for the internal use of the proto package.") + g.P("func (*", mc.goName, ") XXX_OneofWrappers() []interface{} {") + g.P("return []interface{}{") + for _, of := range ofields { + for _, sf := range of.subFields { + sf.typedNil(g) + } + } + g.P("}") + g.P("}") + g.P() +} + +// generateMessageStruct adds the actual struct with it's members (but not methods) to the output. +func (g *Generator) generateMessageStruct(mc *msgCtx, topLevelFields []topLevelField) { + comments := g.PrintComments(mc.message.path) + + // Guarantee deprecation comments appear after user-provided comments. + if mc.message.GetOptions().GetDeprecated() { + if comments { + // Convention: Separate deprecation comments from original + // comments with an empty line. + g.P("//") + } + g.P(deprecationComment) + } + + g.P("type ", Annotate(mc.message.file, mc.message.path, mc.goName), " struct {") + for _, pf := range topLevelFields { + pf.decl(g, mc) + } + g.generateInternalStructFields(mc, topLevelFields) + g.P("}") +} + +// generateGetters adds getters for all fields, including oneofs and weak fields when applicable. +func (g *Generator) generateGetters(mc *msgCtx, topLevelFields []topLevelField) { + for _, pf := range topLevelFields { + pf.getter(g, mc) + } +} + +// generateSetters add setters for all fields, including oneofs and weak fields when applicable. +func (g *Generator) generateSetters(mc *msgCtx, topLevelFields []topLevelField) { + for _, pf := range topLevelFields { + pf.setter(g, mc) + } +} + +// generateCommonMethods adds methods to the message that are not on a per field basis. +func (g *Generator) generateCommonMethods(mc *msgCtx) { + // Reset, String and ProtoMessage methods. + g.P("func (m *", mc.goName, ") Reset() { *m = ", mc.goName, "{} }") + g.P("func (m *", mc.goName, ") String() string { return ", g.Pkg["proto"], ".CompactTextString(m) }") + g.P("func (*", mc.goName, ") ProtoMessage() {}") + var indexes []string + for m := mc.message; m != nil; m = m.parent { + indexes = append([]string{strconv.Itoa(m.index)}, indexes...) + } + g.P("func (*", mc.goName, ") Descriptor() ([]byte, []int) {") + g.P("return ", g.file.VarName(), ", []int{", strings.Join(indexes, ", "), "}") + g.P("}") + g.P() + // TODO: Revisit the decision to use a XXX_WellKnownType method + // if we change proto.MessageName to work with multiple equivalents. + if mc.message.file.GetPackage() == "google.protobuf" && wellKnownTypes[mc.message.GetName()] { + g.P("func (*", mc.goName, `) XXX_WellKnownType() string { return "`, mc.message.GetName(), `" }`) + g.P() + } + + // Extension support methods + if len(mc.message.ExtensionRange) > 0 { + g.P() + g.P("var extRange_", mc.goName, " = []", g.Pkg["proto"], ".ExtensionRange{") + for _, r := range mc.message.ExtensionRange { + end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends + g.P("{Start: ", r.Start, ", End: ", end, "},") + } + g.P("}") + g.P("func (*", mc.goName, ") ExtensionRangeArray() []", g.Pkg["proto"], ".ExtensionRange {") + g.P("return extRange_", mc.goName) + g.P("}") + g.P() + } + + // TODO: It does not scale to keep adding another method for every + // operation on protos that we want to switch over to using the + // table-driven approach. Instead, we should only add a single method + // that allows getting access to the *InternalMessageInfo struct and then + // calling Unmarshal, Marshal, Merge, Size, and Discard directly on that. + + // Wrapper for table-driven marshaling and unmarshaling. + g.P("func (m *", mc.goName, ") XXX_Unmarshal(b []byte) error {") + g.P("return xxx_messageInfo_", mc.goName, ".Unmarshal(m, b)") + g.P("}") + + g.P("func (m *", mc.goName, ") XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {") + g.P("return xxx_messageInfo_", mc.goName, ".Marshal(b, m, deterministic)") + g.P("}") + + g.P("func (m *", mc.goName, ") XXX_Merge(src ", g.Pkg["proto"], ".Message) {") + g.P("xxx_messageInfo_", mc.goName, ".Merge(m, src)") + g.P("}") + + g.P("func (m *", mc.goName, ") XXX_Size() int {") // avoid name clash with "Size" field in some message + g.P("return xxx_messageInfo_", mc.goName, ".Size(m)") + g.P("}") + + g.P("func (m *", mc.goName, ") XXX_DiscardUnknown() {") + g.P("xxx_messageInfo_", mc.goName, ".DiscardUnknown(m)") + g.P("}") + + g.P("var xxx_messageInfo_", mc.goName, " ", g.Pkg["proto"], ".InternalMessageInfo") + g.P() +} + +// Generate the type, methods and default constant definitions for this Descriptor. +func (g *Generator) generateMessage(message *Descriptor) { + topLevelFields := []topLevelField{} + oFields := make(map[int32]*oneofField) + // The full type name + typeName := message.TypeName() + // The full type name, CamelCased. + goTypeName := CamelCaseSlice(typeName) + + usedNames := make(map[string]bool) + for _, n := range methodNames { + usedNames[n] = true + } + + // allocNames finds a conflict-free variation of the given strings, + // consistently mutating their suffixes. + // It returns the same number of strings. + allocNames := func(ns ...string) []string { + Loop: + for { + for _, n := range ns { + if usedNames[n] { + for i := range ns { + ns[i] += "_" + } + continue Loop + } + } + for _, n := range ns { + usedNames[n] = true + } + return ns + } + } + + mapFieldTypes := make(map[*descriptor.FieldDescriptorProto]string) // keep track of the map fields to be added later + + // Build a structure more suitable for generating the text in one pass + for i, field := range message.Field { + // Allocate the getter and the field at the same time so name + // collisions create field/method consistent names. + // TODO: This allocation occurs based on the order of the fields + // in the proto file, meaning that a change in the field + // ordering can change generated Method/Field names. + base := CamelCase(*field.Name) + ns := allocNames(base, "Get"+base) + fieldName, fieldGetterName := ns[0], ns[1] + typename, wiretype := g.GoType(message, field) + jsonName := *field.Name + tag := fmt.Sprintf("protobuf:%s json:%q", g.goTag(message, field, wiretype), jsonName+",omitempty") + + oneof := field.OneofIndex != nil + if oneof && oFields[*field.OneofIndex] == nil { + odp := message.OneofDecl[int(*field.OneofIndex)] + base := CamelCase(odp.GetName()) + fname := allocNames(base)[0] + + // This is the first field of a oneof we haven't seen before. + // Generate the union field. + oneofFullPath := fmt.Sprintf("%s,%d,%d", message.path, messageOneofPath, *field.OneofIndex) + c, ok := g.makeComments(oneofFullPath) + if ok { + c += "\n//\n" + } + c += "// Types that are valid to be assigned to " + fname + ":\n" + // Generate the rest of this comment later, + // when we've computed any disambiguation. + + dname := "is" + goTypeName + "_" + fname + tag := `protobuf_oneof:"` + odp.GetName() + `"` + of := oneofField{ + fieldCommon: fieldCommon{ + goName: fname, + getterName: "Get" + fname, + goType: dname, + tags: tag, + protoName: odp.GetName(), + fullPath: oneofFullPath, + }, + comment: c, + } + topLevelFields = append(topLevelFields, &of) + oFields[*field.OneofIndex] = &of + } + + if *field.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE { + desc := g.ObjectNamed(field.GetTypeName()) + if d, ok := desc.(*Descriptor); ok && d.GetOptions().GetMapEntry() { + // Figure out the Go types and tags for the key and value types. + keyField, valField := d.Field[0], d.Field[1] + keyType, keyWire := g.GoType(d, keyField) + valType, valWire := g.GoType(d, valField) + keyTag, valTag := g.goTag(d, keyField, keyWire), g.goTag(d, valField, valWire) + + // We don't use stars, except for message-typed values. + // Message and enum types are the only two possibly foreign types used in maps, + // so record their use. They are not permitted as map keys. + keyType = strings.TrimPrefix(keyType, "*") + switch *valField.Type { + case descriptor.FieldDescriptorProto_TYPE_ENUM: + valType = strings.TrimPrefix(valType, "*") + g.RecordTypeUse(valField.GetTypeName()) + case descriptor.FieldDescriptorProto_TYPE_MESSAGE: + g.RecordTypeUse(valField.GetTypeName()) + default: + valType = strings.TrimPrefix(valType, "*") + } + + typename = fmt.Sprintf("map[%s]%s", keyType, valType) + mapFieldTypes[field] = typename // record for the getter generation + + tag += fmt.Sprintf(" protobuf_key:%s protobuf_val:%s", keyTag, valTag) + } + } + + fieldDeprecated := "" + if field.GetOptions().GetDeprecated() { + fieldDeprecated = deprecationComment + } + + dvalue := g.getterDefault(field, goTypeName) + if oneof { + tname := goTypeName + "_" + fieldName + // It is possible for this to collide with a message or enum + // nested in this message. Check for collisions. + for { + ok := true + for _, desc := range message.nested { + if CamelCaseSlice(desc.TypeName()) == tname { + ok = false + break + } + } + for _, enum := range message.enums { + if CamelCaseSlice(enum.TypeName()) == tname { + ok = false + break + } + } + if !ok { + tname += "_" + continue + } + break + } + + oneofField := oFields[*field.OneofIndex] + tag := "protobuf:" + g.goTag(message, field, wiretype) + sf := oneofSubField{ + fieldCommon: fieldCommon{ + goName: fieldName, + getterName: fieldGetterName, + goType: typename, + tags: tag, + protoName: field.GetName(), + fullPath: fmt.Sprintf("%s,%d,%d", message.path, messageFieldPath, i), + }, + protoTypeName: field.GetTypeName(), + fieldNumber: int(*field.Number), + protoType: *field.Type, + getterDef: dvalue, + protoDef: field.GetDefaultValue(), + oneofTypeName: tname, + deprecated: fieldDeprecated, + } + oneofField.subFields = append(oneofField.subFields, &sf) + g.RecordTypeUse(field.GetTypeName()) + continue + } + + fieldFullPath := fmt.Sprintf("%s,%d,%d", message.path, messageFieldPath, i) + c, ok := g.makeComments(fieldFullPath) + if ok { + c += "\n" + } + rf := simpleField{ + fieldCommon: fieldCommon{ + goName: fieldName, + getterName: fieldGetterName, + goType: typename, + tags: tag, + protoName: field.GetName(), + fullPath: fieldFullPath, + }, + protoTypeName: field.GetTypeName(), + protoType: *field.Type, + deprecated: fieldDeprecated, + getterDef: dvalue, + protoDef: field.GetDefaultValue(), + comment: c, + } + var pf topLevelField = &rf + + topLevelFields = append(topLevelFields, pf) + g.RecordTypeUse(field.GetTypeName()) + } + + mc := &msgCtx{ + goName: goTypeName, + message: message, + } + + g.generateMessageStruct(mc, topLevelFields) + g.P() + g.generateCommonMethods(mc) + g.P() + g.generateDefaultConstants(mc, topLevelFields) + g.P() + g.generateGetters(mc, topLevelFields) + g.P() + g.generateSetters(mc, topLevelFields) + g.P() + g.generateOneofFuncs(mc, topLevelFields) + g.P() + + var oneofTypes []string + for _, f := range topLevelFields { + if of, ok := f.(*oneofField); ok { + for _, osf := range of.subFields { + oneofTypes = append(oneofTypes, osf.oneofTypeName) + } + } + } + + opts := message.Options + ms := &messageSymbol{ + sym: goTypeName, + hasExtensions: len(message.ExtensionRange) > 0, + isMessageSet: opts != nil && opts.GetMessageSetWireFormat(), + oneofTypes: oneofTypes, + } + g.file.addExport(message, ms) + + for _, ext := range message.ext { + g.generateExtension(ext) + } + + fullName := strings.Join(message.TypeName(), ".") + if g.file.Package != nil { + fullName = *g.file.Package + "." + fullName + } + + g.addInitf("%s.RegisterType((*%s)(nil), %q)", g.Pkg["proto"], goTypeName, fullName) + // Register types for native map types. + for _, k := range mapFieldKeys(mapFieldTypes) { + fullName := strings.TrimPrefix(*k.TypeName, ".") + g.addInitf("%s.RegisterMapType((%s)(nil), %q)", g.Pkg["proto"], mapFieldTypes[k], fullName) + } + +} + +type byTypeName []*descriptor.FieldDescriptorProto + +func (a byTypeName) Len() int { return len(a) } +func (a byTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byTypeName) Less(i, j int) bool { return *a[i].TypeName < *a[j].TypeName } + +// mapFieldKeys returns the keys of m in a consistent order. +func mapFieldKeys(m map[*descriptor.FieldDescriptorProto]string) []*descriptor.FieldDescriptorProto { + keys := make([]*descriptor.FieldDescriptorProto, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Sort(byTypeName(keys)) + return keys +} + +var escapeChars = [256]byte{ + 'a': '\a', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t', 'v': '\v', '\\': '\\', '"': '"', '\'': '\'', '?': '?', +} + +// unescape reverses the "C" escaping that protoc does for default values of bytes fields. +// It is best effort in that it effectively ignores malformed input. Seemingly invalid escape +// sequences are conveyed, unmodified, into the decoded result. +func unescape(s string) string { + // NB: Sadly, we can't use strconv.Unquote because protoc will escape both + // single and double quotes, but strconv.Unquote only allows one or the + // other (based on actual surrounding quotes of its input argument). + + var out []byte + for len(s) > 0 { + // regular character, or too short to be valid escape + if s[0] != '\\' || len(s) < 2 { + out = append(out, s[0]) + s = s[1:] + } else if c := escapeChars[s[1]]; c != 0 { + // escape sequence + out = append(out, c) + s = s[2:] + } else if s[1] == 'x' || s[1] == 'X' { + // hex escape, e.g. "\x80 + if len(s) < 4 { + // too short to be valid + out = append(out, s[:2]...) + s = s[2:] + continue + } + v, err := strconv.ParseUint(s[2:4], 16, 8) + if err != nil { + out = append(out, s[:4]...) + } else { + out = append(out, byte(v)) + } + s = s[4:] + } else if '0' <= s[1] && s[1] <= '7' { + // octal escape, can vary from 1 to 3 octal digits; e.g., "\0" "\40" or "\164" + // so consume up to 2 more bytes or up to end-of-string + n := len(s[1:]) - len(strings.TrimLeft(s[1:], "01234567")) + if n > 3 { + n = 3 + } + v, err := strconv.ParseUint(s[1:1+n], 8, 8) + if err != nil { + out = append(out, s[:1+n]...) + } else { + out = append(out, byte(v)) + } + s = s[1+n:] + } else { + // bad escape, just propagate the slash as-is + out = append(out, s[0]) + s = s[1:] + } + } + + return string(out) +} + +func (g *Generator) generateExtension(ext *ExtensionDescriptor) { + ccTypeName := ext.DescName() + + extObj := g.ObjectNamed(*ext.Extendee) + var extDesc *Descriptor + if id, ok := extObj.(*ImportedDescriptor); ok { + // This is extending a publicly imported message. + // We need the underlying type for goTag. + extDesc = id.o.(*Descriptor) + } else { + extDesc = extObj.(*Descriptor) + } + extendedType := "*" + g.TypeName(extObj) // always use the original + field := ext.FieldDescriptorProto + fieldType, wireType := g.GoType(ext.parent, field) + tag := g.goTag(extDesc, field, wireType) + g.RecordTypeUse(*ext.Extendee) + if n := ext.FieldDescriptorProto.TypeName; n != nil { + // foreign extension type + g.RecordTypeUse(*n) + } + + typeName := ext.TypeName() + + // Special case for proto2 message sets: If this extension is extending + // proto2.bridge.MessageSet, and its final name component is "message_set_extension", + // then drop that last component. + // + // TODO: This should be implemented in the text formatter rather than the generator. + // In addition, the situation for when to apply this special case is implemented + // differently in other languages: + // https://github.com/google/protobuf/blob/aff10976/src/google/protobuf/text_format.cc#L1560 + if extDesc.GetOptions().GetMessageSetWireFormat() && typeName[len(typeName)-1] == "message_set_extension" { + typeName = typeName[:len(typeName)-1] + } + + // For text formatting, the package must be exactly what the .proto file declares, + // ignoring overrides such as the go_package option, and with no dot/underscore mapping. + extName := strings.Join(typeName, ".") + if g.file.Package != nil { + extName = *g.file.Package + "." + extName + } + + g.P("var ", ccTypeName, " = &", g.Pkg["proto"], ".ExtensionDesc{") + g.P("ExtendedType: (", extendedType, ")(nil),") + g.P("ExtensionType: (", fieldType, ")(nil),") + g.P("Field: ", field.Number, ",") + g.P(`Name: "`, extName, `",`) + g.P("Tag: ", tag, ",") + g.P(`Filename: "`, g.file.GetName(), `",`) + + g.P("}") + g.P() + + g.addInitf("%s.RegisterExtension(%s)", g.Pkg["proto"], ext.DescName()) + + g.file.addExport(ext, constOrVarSymbol{ccTypeName, "var", ""}) +} + +func (g *Generator) generateInitFunction() { + if len(g.init) == 0 { + return + } + g.P("func init() {") + for _, l := range g.init { + g.P(l) + } + g.P("}") + g.init = nil +} + +func (g *Generator) generateFileDescriptor(file *FileDescriptor) { + // Make a copy and trim source_code_info data. + // TODO: Trim this more when we know exactly what we need. + pb := proto.Clone(file.FileDescriptorProto).(*descriptor.FileDescriptorProto) + pb.SourceCodeInfo = nil + + b, err := proto.Marshal(pb) + if err != nil { + g.Fail(err.Error()) + } + + var buf bytes.Buffer + w, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression) + w.Write(b) + w.Close() + b = buf.Bytes() + + v := file.VarName() + g.P() + g.P("func init() {") + g.P(g.Pkg["proto"], ".RegisterFile(", strconv.Quote(*file.Name), ", ", v, ")") + g.P("}") + g.P("var ", v, " = []byte{") + g.P("// ", len(b), " bytes of a gzipped FileDescriptorProto") + for len(b) > 0 { + n := 16 + if n > len(b) { + n = len(b) + } + + s := "" + for _, c := range b[:n] { + s += fmt.Sprintf("0x%02x,", c) + } + g.P(s) + + b = b[n:] + } + g.P("}") +} + +func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) { + // // We always print the full (proto-world) package name here. + pkg := enum.File().GetPackage() + if pkg != "" { + pkg += "." + } + // The full type name + typeName := enum.TypeName() + // The full type name, CamelCased. + ccTypeName := CamelCaseSlice(typeName) + g.addInitf("%s.RegisterEnum(%q, %[3]s_name, %[3]s_value)", g.Pkg["proto"], pkg+ccTypeName, ccTypeName) +} + +// And now lots of helper functions. + +// Is c an ASCII lower-case letter? +func isASCIILower(c byte) bool { + return 'a' <= c && c <= 'z' +} + +// Is c an ASCII digit? +func isASCIIDigit(c byte) bool { + return '0' <= c && c <= '9' +} + +// CamelCase returns the CamelCased name. +// If there is an interior underscore followed by a lower case letter, +// drop the underscore and convert the letter to upper case. +// There is a remote possibility of this rewrite causing a name collision, +// but it's so remote we're prepared to pretend it's nonexistent - since the +// C++ generator lowercases names, it's extremely unlikely to have two fields +// with different capitalizations. +// In short, _my_field_name_2 becomes XMyFieldName_2. +func CamelCase(s string) string { + if s == "" { + return "" + } + t := make([]byte, 0, 32) + i := 0 + if s[0] == '_' { + // Need a capital letter; drop the '_'. + t = append(t, 'X') + i++ + } + // Invariant: if the next letter is lower case, it must be converted + // to upper case. + // That is, we process a word at a time, where words are marked by _ or + // upper case letter. Digits are treated as words. + for ; i < len(s); i++ { + c := s[i] + if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) { + continue // Skip the underscore in s. + } + if isASCIIDigit(c) { + t = append(t, c) + continue + } + // Assume we have a letter now - if not, it's a bogus identifier. + // The next word is a sequence of characters that must start upper case. + if isASCIILower(c) { + c ^= ' ' // Make it a capital letter. + } + t = append(t, c) // Guaranteed not lower case. + // Accept lower case sequence that follows. + for i+1 < len(s) && isASCIILower(s[i+1]) { + i++ + t = append(t, s[i]) + } + } + return string(t) +} + +// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to +// be joined with "_". +func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) } + +// dottedSlice turns a sliced name into a dotted name. +func dottedSlice(elem []string) string { return strings.Join(elem, ".") } + +// Is this field optional? +func isOptional(field *descriptor.FieldDescriptorProto) bool { + return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL +} + +// Is this field required? +func isRequired(field *descriptor.FieldDescriptorProto) bool { + return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED +} + +// Is this field repeated? +func isRepeated(field *descriptor.FieldDescriptorProto) bool { + return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED +} + +// Is this field a scalar numeric type? +func isScalar(field *descriptor.FieldDescriptorProto) bool { + if field.Type == nil { + return false + } + switch *field.Type { + case descriptor.FieldDescriptorProto_TYPE_DOUBLE, + descriptor.FieldDescriptorProto_TYPE_FLOAT, + descriptor.FieldDescriptorProto_TYPE_INT64, + descriptor.FieldDescriptorProto_TYPE_UINT64, + descriptor.FieldDescriptorProto_TYPE_INT32, + descriptor.FieldDescriptorProto_TYPE_FIXED64, + descriptor.FieldDescriptorProto_TYPE_FIXED32, + descriptor.FieldDescriptorProto_TYPE_BOOL, + descriptor.FieldDescriptorProto_TYPE_UINT32, + descriptor.FieldDescriptorProto_TYPE_ENUM, + descriptor.FieldDescriptorProto_TYPE_SFIXED32, + descriptor.FieldDescriptorProto_TYPE_SFIXED64, + descriptor.FieldDescriptorProto_TYPE_SINT32, + descriptor.FieldDescriptorProto_TYPE_SINT64: + return true + default: + return false + } +} + +// badToUnderscore is the mapping function used to generate Go names from package names, +// which can be dotted in the input .proto file. It replaces non-identifier characters such as +// dot or dash with underscore. +func badToUnderscore(r rune) rune { + if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' { + return r + } + return '_' +} + +// baseName returns the last path element of the name, with the last dotted suffix removed. +func baseName(name string) string { + // First, find the last element + if i := strings.LastIndex(name, "/"); i >= 0 { + name = name[i+1:] + } + // Now drop the suffix + if i := strings.LastIndex(name, "."); i >= 0 { + name = name[0:i] + } + return name +} + +// The SourceCodeInfo message describes the location of elements of a parsed +// .proto file by way of a "path", which is a sequence of integers that +// describe the route from a FileDescriptorProto to the relevant submessage. +// The path alternates between a field number of a repeated field, and an index +// into that repeated field. The constants below define the field numbers that +// are used. +// +// See descriptor.proto for more information about this. +const ( + // tag numbers in FileDescriptorProto + packagePath = 2 // package + messagePath = 4 // message_type + enumPath = 5 // enum_type + // tag numbers in DescriptorProto + messageFieldPath = 2 // field + messageMessagePath = 3 // nested_type + messageEnumPath = 4 // enum_type + messageOneofPath = 8 // oneof_decl + // tag numbers in EnumDescriptorProto + enumValuePath = 2 // value +) + +var supportTypeAliases bool + +func init() { + for _, tag := range build.Default.ReleaseTags { + if tag == "go1.9" { + supportTypeAliases = true + return + } + } +} diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/generator/internal/remap/remap.go b/vendor/github.com/golang/protobuf/protoc-gen-go/generator/internal/remap/remap.go new file mode 100644 index 0000000000000..39968eb9f92fb --- /dev/null +++ b/vendor/github.com/golang/protobuf/protoc-gen-go/generator/internal/remap/remap.go @@ -0,0 +1,117 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2017 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* +Package remap handles tracking the locations of Go tokens in a source text +across a rewrite by the Go formatter. +*/ +package remap + +import ( + "fmt" + "go/scanner" + "go/token" +) + +// A Location represents a span of byte offsets in the source text. +type Location struct { + Pos, End int // End is exclusive +} + +// A Map represents a mapping between token locations in an input source text +// and locations in the corresponding output text. +type Map map[Location]Location + +// Find reports whether the specified span is recorded by m, and if so returns +// the new location it was mapped to. If the input span was not found, the +// returned location is the same as the input. +func (m Map) Find(pos, end int) (Location, bool) { + key := Location{ + Pos: pos, + End: end, + } + if loc, ok := m[key]; ok { + return loc, true + } + return key, false +} + +func (m Map) add(opos, oend, npos, nend int) { + m[Location{Pos: opos, End: oend}] = Location{Pos: npos, End: nend} +} + +// Compute constructs a location mapping from input to output. An error is +// reported if any of the tokens of output cannot be mapped. +func Compute(input, output []byte) (Map, error) { + itok := tokenize(input) + otok := tokenize(output) + if len(itok) != len(otok) { + return nil, fmt.Errorf("wrong number of tokens, %d ≠ %d", len(itok), len(otok)) + } + m := make(Map) + for i, ti := range itok { + to := otok[i] + if ti.Token != to.Token { + return nil, fmt.Errorf("token %d type mismatch: %s ≠ %s", i+1, ti, to) + } + m.add(ti.pos, ti.end, to.pos, to.end) + } + return m, nil +} + +// tokinfo records the span and type of a source token. +type tokinfo struct { + pos, end int + token.Token +} + +func tokenize(src []byte) []tokinfo { + fs := token.NewFileSet() + var s scanner.Scanner + s.Init(fs.AddFile("src", fs.Base(), len(src)), src, nil, scanner.ScanComments) + var info []tokinfo + for { + pos, next, lit := s.Scan() + switch next { + case token.SEMICOLON: + continue + } + info = append(info, tokinfo{ + pos: int(pos - 1), + end: int(pos + token.Pos(len(lit)) - 1), + Token: next, + }) + if next == token.EOF { + break + } + } + return info +} diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/grpc/grpc.go b/vendor/github.com/golang/protobuf/protoc-gen-go/grpc/grpc.go new file mode 100644 index 0000000000000..957c3f2a63fb3 --- /dev/null +++ b/vendor/github.com/golang/protobuf/protoc-gen-go/grpc/grpc.go @@ -0,0 +1,545 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2015 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Package grpc outputs gRPC service descriptions in Go code. +// It runs as a plugin for the Go protocol buffer compiler plugin. +// It is linked in to protoc-gen-go. +package grpc + +import ( + "fmt" + "strconv" + "strings" + + pb "github.com/golang/protobuf/protoc-gen-go/descriptor" + "github.com/golang/protobuf/protoc-gen-go/generator" +) + +// generatedCodeVersion indicates a version of the generated code. +// It is incremented whenever an incompatibility between the generated code and +// the grpc package is introduced; the generated code references +// a constant, grpc.SupportPackageIsVersionN (where N is generatedCodeVersion). +const generatedCodeVersion = 6 + +// Paths for packages used by code generated in this file, +// relative to the import_prefix of the generator.Generator. +const ( + contextPkgPath = "context" + grpcPkgPath = "google.golang.org/grpc" + codePkgPath = "google.golang.org/grpc/codes" + statusPkgPath = "google.golang.org/grpc/status" +) + +func init() { + generator.RegisterPlugin(new(grpc)) +} + +// grpc is an implementation of the Go protocol buffer compiler's +// plugin architecture. It generates bindings for gRPC support. +type grpc struct { + gen *generator.Generator +} + +// Name returns the name of this plugin, "grpc". +func (g *grpc) Name() string { + return "grpc" +} + +// The names for packages imported in the generated code. +// They may vary from the final path component of the import path +// if the name is used by other packages. +var ( + contextPkg string + grpcPkg string +) + +// Init initializes the plugin. +func (g *grpc) Init(gen *generator.Generator) { + g.gen = gen +} + +// Given a type name defined in a .proto, return its object. +// Also record that we're using it, to guarantee the associated import. +func (g *grpc) objectNamed(name string) generator.Object { + g.gen.RecordTypeUse(name) + return g.gen.ObjectNamed(name) +} + +// Given a type name defined in a .proto, return its name as we will print it. +func (g *grpc) typeName(str string) string { + return g.gen.TypeName(g.objectNamed(str)) +} + +// P forwards to g.gen.P. +func (g *grpc) P(args ...interface{}) { g.gen.P(args...) } + +// Generate generates code for the services in the given file. +func (g *grpc) Generate(file *generator.FileDescriptor) { + if len(file.FileDescriptorProto.Service) == 0 { + return + } + + contextPkg = string(g.gen.AddImport(contextPkgPath)) + grpcPkg = string(g.gen.AddImport(grpcPkgPath)) + + g.P("// Reference imports to suppress errors if they are not otherwise used.") + g.P("var _ ", contextPkg, ".Context") + g.P("var _ ", grpcPkg, ".ClientConnInterface") + g.P() + + // Assert version compatibility. + g.P("// This is a compile-time assertion to ensure that this generated file") + g.P("// is compatible with the grpc package it is being compiled against.") + g.P("const _ = ", grpcPkg, ".SupportPackageIsVersion", generatedCodeVersion) + g.P() + + for i, service := range file.FileDescriptorProto.Service { + g.generateService(file, service, i) + } +} + +// GenerateImports generates the import declaration for this file. +func (g *grpc) GenerateImports(file *generator.FileDescriptor) { +} + +// reservedClientName records whether a client name is reserved on the client side. +var reservedClientName = map[string]bool{ + // TODO: do we need any in gRPC? +} + +func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] } + +// deprecationComment is the standard comment added to deprecated +// messages, fields, enums, and enum values. +var deprecationComment = "// Deprecated: Do not use." + +// generateService generates all the code for the named service. +func (g *grpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) { + path := fmt.Sprintf("6,%d", index) // 6 means service. + + origServName := service.GetName() + fullServName := origServName + if pkg := file.GetPackage(); pkg != "" { + fullServName = pkg + "." + fullServName + } + servName := generator.CamelCase(origServName) + deprecated := service.GetOptions().GetDeprecated() + + g.P() + g.P(fmt.Sprintf(`// %sClient is the client API for %s service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.`, servName, servName)) + + // Client interface. + if deprecated { + g.P("//") + g.P(deprecationComment) + } + g.P("type ", servName, "Client interface {") + for i, method := range service.Method { + g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service. + if method.GetOptions().GetDeprecated() { + g.P("//") + g.P(deprecationComment) + } + g.P(g.generateClientSignature(servName, method)) + } + g.P("}") + g.P() + + // Client structure. + g.P("type ", unexport(servName), "Client struct {") + g.P("cc ", grpcPkg, ".ClientConnInterface") + g.P("}") + g.P() + + // NewClient factory. + if deprecated { + g.P(deprecationComment) + } + g.P("func New", servName, "Client (cc ", grpcPkg, ".ClientConnInterface) ", servName, "Client {") + g.P("return &", unexport(servName), "Client{cc}") + g.P("}") + g.P() + + var methodIndex, streamIndex int + serviceDescVar := "_" + servName + "_serviceDesc" + // Client method implementations. + for _, method := range service.Method { + var descExpr string + if !method.GetServerStreaming() && !method.GetClientStreaming() { + // Unary RPC method + descExpr = fmt.Sprintf("&%s.Methods[%d]", serviceDescVar, methodIndex) + methodIndex++ + } else { + // Streaming RPC method + descExpr = fmt.Sprintf("&%s.Streams[%d]", serviceDescVar, streamIndex) + streamIndex++ + } + g.generateClientMethod(servName, fullServName, serviceDescVar, method, descExpr) + } + + // Server interface. + serverType := servName + "Server" + g.P("// ", serverType, " is the server API for ", servName, " service.") + if deprecated { + g.P("//") + g.P(deprecationComment) + } + g.P("type ", serverType, " interface {") + for i, method := range service.Method { + g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service. + if method.GetOptions().GetDeprecated() { + g.P("//") + g.P(deprecationComment) + } + g.P(g.generateServerSignature(servName, method)) + } + g.P("}") + g.P() + + // Server Unimplemented struct for forward compatibility. + if deprecated { + g.P(deprecationComment) + } + g.generateUnimplementedServer(servName, service) + + // Server registration. + if deprecated { + g.P(deprecationComment) + } + g.P("func Register", servName, "Server(s *", grpcPkg, ".Server, srv ", serverType, ") {") + g.P("s.RegisterService(&", serviceDescVar, `, srv)`) + g.P("}") + g.P() + + // Server handler implementations. + var handlerNames []string + for _, method := range service.Method { + hname := g.generateServerMethod(servName, fullServName, method) + handlerNames = append(handlerNames, hname) + } + + // Service descriptor. + g.P("var ", serviceDescVar, " = ", grpcPkg, ".ServiceDesc {") + g.P("ServiceName: ", strconv.Quote(fullServName), ",") + g.P("HandlerType: (*", serverType, ")(nil),") + g.P("Methods: []", grpcPkg, ".MethodDesc{") + for i, method := range service.Method { + if method.GetServerStreaming() || method.GetClientStreaming() { + continue + } + g.P("{") + g.P("MethodName: ", strconv.Quote(method.GetName()), ",") + g.P("Handler: ", handlerNames[i], ",") + g.P("},") + } + g.P("},") + g.P("Streams: []", grpcPkg, ".StreamDesc{") + for i, method := range service.Method { + if !method.GetServerStreaming() && !method.GetClientStreaming() { + continue + } + g.P("{") + g.P("StreamName: ", strconv.Quote(method.GetName()), ",") + g.P("Handler: ", handlerNames[i], ",") + if method.GetServerStreaming() { + g.P("ServerStreams: true,") + } + if method.GetClientStreaming() { + g.P("ClientStreams: true,") + } + g.P("},") + } + g.P("},") + g.P("Metadata: \"", file.GetName(), "\",") + g.P("}") + g.P() +} + +// generateUnimplementedServer creates the unimplemented server struct +func (g *grpc) generateUnimplementedServer(servName string, service *pb.ServiceDescriptorProto) { + serverType := servName + "Server" + g.P("// Unimplemented", serverType, " can be embedded to have forward compatible implementations.") + g.P("type Unimplemented", serverType, " struct {") + g.P("}") + g.P() + // UnimplementedServer's concrete methods + for _, method := range service.Method { + g.generateServerMethodConcrete(servName, method) + } + g.P() +} + +// generateServerMethodConcrete returns unimplemented methods which ensure forward compatibility +func (g *grpc) generateServerMethodConcrete(servName string, method *pb.MethodDescriptorProto) { + header := g.generateServerSignatureWithParamNames(servName, method) + g.P("func (*Unimplemented", servName, "Server) ", header, " {") + var nilArg string + if !method.GetServerStreaming() && !method.GetClientStreaming() { + nilArg = "nil, " + } + methName := generator.CamelCase(method.GetName()) + statusPkg := string(g.gen.AddImport(statusPkgPath)) + codePkg := string(g.gen.AddImport(codePkgPath)) + g.P("return ", nilArg, statusPkg, `.Errorf(`, codePkg, `.Unimplemented, "method `, methName, ` not implemented")`) + g.P("}") +} + +// generateClientSignature returns the client-side signature for a method. +func (g *grpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string { + origMethName := method.GetName() + methName := generator.CamelCase(origMethName) + if reservedClientName[methName] { + methName += "_" + } + reqArg := ", in *" + g.typeName(method.GetInputType()) + if method.GetClientStreaming() { + reqArg = "" + } + respName := "*" + g.typeName(method.GetOutputType()) + if method.GetServerStreaming() || method.GetClientStreaming() { + respName = servName + "_" + generator.CamelCase(origMethName) + "Client" + } + return fmt.Sprintf("%s(ctx %s.Context%s, opts ...%s.CallOption) (%s, error)", methName, contextPkg, reqArg, grpcPkg, respName) +} + +func (g *grpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) { + sname := fmt.Sprintf("/%s/%s", fullServName, method.GetName()) + methName := generator.CamelCase(method.GetName()) + inType := g.typeName(method.GetInputType()) + outType := g.typeName(method.GetOutputType()) + + if method.GetOptions().GetDeprecated() { + g.P(deprecationComment) + } + g.P("func (c *", unexport(servName), "Client) ", g.generateClientSignature(servName, method), "{") + if !method.GetServerStreaming() && !method.GetClientStreaming() { + g.P("out := new(", outType, ")") + // TODO: Pass descExpr to Invoke. + g.P(`err := c.cc.Invoke(ctx, "`, sname, `", in, out, opts...)`) + g.P("if err != nil { return nil, err }") + g.P("return out, nil") + g.P("}") + g.P() + return + } + streamType := unexport(servName) + methName + "Client" + g.P("stream, err := c.cc.NewStream(ctx, ", descExpr, `, "`, sname, `", opts...)`) + g.P("if err != nil { return nil, err }") + g.P("x := &", streamType, "{stream}") + if !method.GetClientStreaming() { + g.P("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }") + g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }") + } + g.P("return x, nil") + g.P("}") + g.P() + + genSend := method.GetClientStreaming() + genRecv := method.GetServerStreaming() + genCloseAndRecv := !method.GetServerStreaming() + + // Stream auxiliary types and methods. + g.P("type ", servName, "_", methName, "Client interface {") + if genSend { + g.P("Send(*", inType, ") error") + } + if genRecv { + g.P("Recv() (*", outType, ", error)") + } + if genCloseAndRecv { + g.P("CloseAndRecv() (*", outType, ", error)") + } + g.P(grpcPkg, ".ClientStream") + g.P("}") + g.P() + + g.P("type ", streamType, " struct {") + g.P(grpcPkg, ".ClientStream") + g.P("}") + g.P() + + if genSend { + g.P("func (x *", streamType, ") Send(m *", inType, ") error {") + g.P("return x.ClientStream.SendMsg(m)") + g.P("}") + g.P() + } + if genRecv { + g.P("func (x *", streamType, ") Recv() (*", outType, ", error) {") + g.P("m := new(", outType, ")") + g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }") + g.P("return m, nil") + g.P("}") + g.P() + } + if genCloseAndRecv { + g.P("func (x *", streamType, ") CloseAndRecv() (*", outType, ", error) {") + g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }") + g.P("m := new(", outType, ")") + g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }") + g.P("return m, nil") + g.P("}") + g.P() + } +} + +// generateServerSignatureWithParamNames returns the server-side signature for a method with parameter names. +func (g *grpc) generateServerSignatureWithParamNames(servName string, method *pb.MethodDescriptorProto) string { + origMethName := method.GetName() + methName := generator.CamelCase(origMethName) + if reservedClientName[methName] { + methName += "_" + } + + var reqArgs []string + ret := "error" + if !method.GetServerStreaming() && !method.GetClientStreaming() { + reqArgs = append(reqArgs, "ctx "+contextPkg+".Context") + ret = "(*" + g.typeName(method.GetOutputType()) + ", error)" + } + if !method.GetClientStreaming() { + reqArgs = append(reqArgs, "req *"+g.typeName(method.GetInputType())) + } + if method.GetServerStreaming() || method.GetClientStreaming() { + reqArgs = append(reqArgs, "srv "+servName+"_"+generator.CamelCase(origMethName)+"Server") + } + + return methName + "(" + strings.Join(reqArgs, ", ") + ") " + ret +} + +// generateServerSignature returns the server-side signature for a method. +func (g *grpc) generateServerSignature(servName string, method *pb.MethodDescriptorProto) string { + origMethName := method.GetName() + methName := generator.CamelCase(origMethName) + if reservedClientName[methName] { + methName += "_" + } + + var reqArgs []string + ret := "error" + if !method.GetServerStreaming() && !method.GetClientStreaming() { + reqArgs = append(reqArgs, contextPkg+".Context") + ret = "(*" + g.typeName(method.GetOutputType()) + ", error)" + } + if !method.GetClientStreaming() { + reqArgs = append(reqArgs, "*"+g.typeName(method.GetInputType())) + } + if method.GetServerStreaming() || method.GetClientStreaming() { + reqArgs = append(reqArgs, servName+"_"+generator.CamelCase(origMethName)+"Server") + } + + return methName + "(" + strings.Join(reqArgs, ", ") + ") " + ret +} + +func (g *grpc) generateServerMethod(servName, fullServName string, method *pb.MethodDescriptorProto) string { + methName := generator.CamelCase(method.GetName()) + hname := fmt.Sprintf("_%s_%s_Handler", servName, methName) + inType := g.typeName(method.GetInputType()) + outType := g.typeName(method.GetOutputType()) + + if !method.GetServerStreaming() && !method.GetClientStreaming() { + g.P("func ", hname, "(srv interface{}, ctx ", contextPkg, ".Context, dec func(interface{}) error, interceptor ", grpcPkg, ".UnaryServerInterceptor) (interface{}, error) {") + g.P("in := new(", inType, ")") + g.P("if err := dec(in); err != nil { return nil, err }") + g.P("if interceptor == nil { return srv.(", servName, "Server).", methName, "(ctx, in) }") + g.P("info := &", grpcPkg, ".UnaryServerInfo{") + g.P("Server: srv,") + g.P("FullMethod: ", strconv.Quote(fmt.Sprintf("/%s/%s", fullServName, methName)), ",") + g.P("}") + g.P("handler := func(ctx ", contextPkg, ".Context, req interface{}) (interface{}, error) {") + g.P("return srv.(", servName, "Server).", methName, "(ctx, req.(*", inType, "))") + g.P("}") + g.P("return interceptor(ctx, in, info, handler)") + g.P("}") + g.P() + return hname + } + streamType := unexport(servName) + methName + "Server" + g.P("func ", hname, "(srv interface{}, stream ", grpcPkg, ".ServerStream) error {") + if !method.GetClientStreaming() { + g.P("m := new(", inType, ")") + g.P("if err := stream.RecvMsg(m); err != nil { return err }") + g.P("return srv.(", servName, "Server).", methName, "(m, &", streamType, "{stream})") + } else { + g.P("return srv.(", servName, "Server).", methName, "(&", streamType, "{stream})") + } + g.P("}") + g.P() + + genSend := method.GetServerStreaming() + genSendAndClose := !method.GetServerStreaming() + genRecv := method.GetClientStreaming() + + // Stream auxiliary types and methods. + g.P("type ", servName, "_", methName, "Server interface {") + if genSend { + g.P("Send(*", outType, ") error") + } + if genSendAndClose { + g.P("SendAndClose(*", outType, ") error") + } + if genRecv { + g.P("Recv() (*", inType, ", error)") + } + g.P(grpcPkg, ".ServerStream") + g.P("}") + g.P() + + g.P("type ", streamType, " struct {") + g.P(grpcPkg, ".ServerStream") + g.P("}") + g.P() + + if genSend { + g.P("func (x *", streamType, ") Send(m *", outType, ") error {") + g.P("return x.ServerStream.SendMsg(m)") + g.P("}") + g.P() + } + if genSendAndClose { + g.P("func (x *", streamType, ") SendAndClose(m *", outType, ") error {") + g.P("return x.ServerStream.SendMsg(m)") + g.P("}") + g.P() + } + if genRecv { + g.P("func (x *", streamType, ") Recv() (*", inType, ", error) {") + g.P("m := new(", inType, ")") + g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }") + g.P("return m, nil") + g.P("}") + g.P() + } + + return hname +} diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/link_grpc.go b/vendor/github.com/golang/protobuf/protoc-gen-go/link_grpc.go new file mode 100644 index 0000000000000..532a550050ee3 --- /dev/null +++ b/vendor/github.com/golang/protobuf/protoc-gen-go/link_grpc.go @@ -0,0 +1,34 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2015 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package main + +import _ "github.com/golang/protobuf/protoc-gen-go/grpc" diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/main.go b/vendor/github.com/golang/protobuf/protoc-gen-go/main.go new file mode 100644 index 0000000000000..8e2486de0b2e2 --- /dev/null +++ b/vendor/github.com/golang/protobuf/protoc-gen-go/main.go @@ -0,0 +1,98 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// protoc-gen-go is a plugin for the Google protocol buffer compiler to generate +// Go code. Run it by building this program and putting it in your path with +// the name +// protoc-gen-go +// That word 'go' at the end becomes part of the option string set for the +// protocol compiler, so once the protocol compiler (protoc) is installed +// you can run +// protoc --go_out=output_directory input_directory/file.proto +// to generate Go bindings for the protocol defined by file.proto. +// With that input, the output will be written to +// output_directory/file.pb.go +// +// The generated code is documented in the package comment for +// the library. +// +// See the README and documentation for protocol buffers to learn more: +// https://developers.google.com/protocol-buffers/ +package main + +import ( + "io/ioutil" + "os" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/protoc-gen-go/generator" +) + +func main() { + // Begin by allocating a generator. The request and response structures are stored there + // so we can do error handling easily - the response structure contains the field to + // report failure. + g := generator.New() + + data, err := ioutil.ReadAll(os.Stdin) + if err != nil { + g.Error(err, "reading input") + } + + if err := proto.Unmarshal(data, g.Request); err != nil { + g.Error(err, "parsing input proto") + } + + if len(g.Request.FileToGenerate) == 0 { + g.Fail("no files to generate") + } + + g.CommandLineParameters(g.Request.GetParameter()) + + // Create a wrapped version of the Descriptors and EnumDescriptors that + // point to the file that defines them. + g.WrapTypes() + + g.SetPackageNames() + g.BuildTypeNameMap() + + g.GenerateAllFiles() + + // Send back the results. + data, err = proto.Marshal(g.Response) + if err != nil { + g.Error(err, "failed to marshal output proto") + } + _, err = os.Stdout.Write(data) + if err != nil { + g.Error(err, "failed to write output proto") + } +} diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/plugin.pb.go b/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/plugin.pb.go new file mode 100644 index 0000000000000..61bfc10e02e7c --- /dev/null +++ b/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/plugin.pb.go @@ -0,0 +1,369 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/compiler/plugin.proto + +/* +Package plugin_go is a generated protocol buffer package. + +It is generated from these files: + google/protobuf/compiler/plugin.proto + +It has these top-level messages: + Version + CodeGeneratorRequest + CodeGeneratorResponse +*/ +package plugin_go + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/golang/protobuf/protoc-gen-go/descriptor" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// The version number of protocol compiler. +type Version struct { + Major *int32 `protobuf:"varint,1,opt,name=major" json:"major,omitempty"` + Minor *int32 `protobuf:"varint,2,opt,name=minor" json:"minor,omitempty"` + Patch *int32 `protobuf:"varint,3,opt,name=patch" json:"patch,omitempty"` + // A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should + // be empty for mainline stable releases. + Suffix *string `protobuf:"bytes,4,opt,name=suffix" json:"suffix,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Version) Reset() { *m = Version{} } +func (m *Version) String() string { return proto.CompactTextString(m) } +func (*Version) ProtoMessage() {} +func (*Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (m *Version) Unmarshal(b []byte) error { + return xxx_messageInfo_Version.Unmarshal(m, b) +} +func (m *Version) Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Version.Marshal(b, m, deterministic) +} +func (dst *Version) XXX_Merge(src proto.Message) { + xxx_messageInfo_Version.Merge(dst, src) +} +func (m *Version) XXX_Size() int { + return xxx_messageInfo_Version.Size(m) +} +func (m *Version) XXX_DiscardUnknown() { + xxx_messageInfo_Version.DiscardUnknown(m) +} + +var xxx_messageInfo_Version proto.InternalMessageInfo + +func (m *Version) GetMajor() int32 { + if m != nil && m.Major != nil { + return *m.Major + } + return 0 +} + +func (m *Version) GetMinor() int32 { + if m != nil && m.Minor != nil { + return *m.Minor + } + return 0 +} + +func (m *Version) GetPatch() int32 { + if m != nil && m.Patch != nil { + return *m.Patch + } + return 0 +} + +func (m *Version) GetSuffix() string { + if m != nil && m.Suffix != nil { + return *m.Suffix + } + return "" +} + +// An encoded CodeGeneratorRequest is written to the plugin's stdin. +type CodeGeneratorRequest struct { + // The .proto files that were explicitly listed on the command-line. The + // code generator should generate code only for these files. Each file's + // descriptor will be included in proto_file, below. + FileToGenerate []string `protobuf:"bytes,1,rep,name=file_to_generate,json=fileToGenerate" json:"file_to_generate,omitempty"` + // The generator parameter passed on the command-line. + Parameter *string `protobuf:"bytes,2,opt,name=parameter" json:"parameter,omitempty"` + // FileDescriptorProtos for all files in files_to_generate and everything + // they import. The files will appear in topological order, so each file + // appears before any file that imports it. + // + // protoc guarantees that all proto_files will be written after + // the fields above, even though this is not technically guaranteed by the + // protobuf wire format. This theoretically could allow a plugin to stream + // in the FileDescriptorProtos and handle them one by one rather than read + // the entire set into memory at once. However, as of this writing, this + // is not similarly optimized on protoc's end -- it will store all fields in + // memory at once before sending them to the plugin. + // + // Type names of fields and extensions in the FileDescriptorProto are always + // fully qualified. + ProtoFile []*google_protobuf.FileDescriptorProto `protobuf:"bytes,15,rep,name=proto_file,json=protoFile" json:"proto_file,omitempty"` + // The version number of protocol compiler. + CompilerVersion *Version `protobuf:"bytes,3,opt,name=compiler_version,json=compilerVersion" json:"compiler_version,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CodeGeneratorRequest) Reset() { *m = CodeGeneratorRequest{} } +func (m *CodeGeneratorRequest) String() string { return proto.CompactTextString(m) } +func (*CodeGeneratorRequest) ProtoMessage() {} +func (*CodeGeneratorRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +func (m *CodeGeneratorRequest) Unmarshal(b []byte) error { + return xxx_messageInfo_CodeGeneratorRequest.Unmarshal(m, b) +} +func (m *CodeGeneratorRequest) Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CodeGeneratorRequest.Marshal(b, m, deterministic) +} +func (dst *CodeGeneratorRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CodeGeneratorRequest.Merge(dst, src) +} +func (m *CodeGeneratorRequest) XXX_Size() int { + return xxx_messageInfo_CodeGeneratorRequest.Size(m) +} +func (m *CodeGeneratorRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CodeGeneratorRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_CodeGeneratorRequest proto.InternalMessageInfo + +func (m *CodeGeneratorRequest) GetFileToGenerate() []string { + if m != nil { + return m.FileToGenerate + } + return nil +} + +func (m *CodeGeneratorRequest) GetParameter() string { + if m != nil && m.Parameter != nil { + return *m.Parameter + } + return "" +} + +func (m *CodeGeneratorRequest) GetProtoFile() []*google_protobuf.FileDescriptorProto { + if m != nil { + return m.ProtoFile + } + return nil +} + +func (m *CodeGeneratorRequest) GetCompilerVersion() *Version { + if m != nil { + return m.CompilerVersion + } + return nil +} + +// The plugin writes an encoded CodeGeneratorResponse to stdout. +type CodeGeneratorResponse struct { + // Error message. If non-empty, code generation failed. The plugin process + // should exit with status code zero even if it reports an error in this way. + // + // This should be used to indicate errors in .proto files which prevent the + // code generator from generating correct code. Errors which indicate a + // problem in protoc itself -- such as the input CodeGeneratorRequest being + // unparseable -- should be reported by writing a message to stderr and + // exiting with a non-zero status code. + Error *string `protobuf:"bytes,1,opt,name=error" json:"error,omitempty"` + File []*CodeGeneratorResponse_File `protobuf:"bytes,15,rep,name=file" json:"file,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CodeGeneratorResponse) Reset() { *m = CodeGeneratorResponse{} } +func (m *CodeGeneratorResponse) String() string { return proto.CompactTextString(m) } +func (*CodeGeneratorResponse) ProtoMessage() {} +func (*CodeGeneratorResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (m *CodeGeneratorResponse) Unmarshal(b []byte) error { + return xxx_messageInfo_CodeGeneratorResponse.Unmarshal(m, b) +} +func (m *CodeGeneratorResponse) Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CodeGeneratorResponse.Marshal(b, m, deterministic) +} +func (dst *CodeGeneratorResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CodeGeneratorResponse.Merge(dst, src) +} +func (m *CodeGeneratorResponse) XXX_Size() int { + return xxx_messageInfo_CodeGeneratorResponse.Size(m) +} +func (m *CodeGeneratorResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CodeGeneratorResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_CodeGeneratorResponse proto.InternalMessageInfo + +func (m *CodeGeneratorResponse) GetError() string { + if m != nil && m.Error != nil { + return *m.Error + } + return "" +} + +func (m *CodeGeneratorResponse) GetFile() []*CodeGeneratorResponse_File { + if m != nil { + return m.File + } + return nil +} + +// Represents a single generated file. +type CodeGeneratorResponse_File struct { + // The file name, relative to the output directory. The name must not + // contain "." or ".." components and must be relative, not be absolute (so, + // the file cannot lie outside the output directory). "/" must be used as + // the path separator, not "\". + // + // If the name is omitted, the content will be appended to the previous + // file. This allows the generator to break large files into small chunks, + // and allows the generated text to be streamed back to protoc so that large + // files need not reside completely in memory at one time. Note that as of + // this writing protoc does not optimize for this -- it will read the entire + // CodeGeneratorResponse before writing files to disk. + Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + // If non-empty, indicates that the named file should already exist, and the + // content here is to be inserted into that file at a defined insertion + // point. This feature allows a code generator to extend the output + // produced by another code generator. The original generator may provide + // insertion points by placing special annotations in the file that look + // like: + // @@protoc_insertion_point(NAME) + // The annotation can have arbitrary text before and after it on the line, + // which allows it to be placed in a comment. NAME should be replaced with + // an identifier naming the point -- this is what other generators will use + // as the insertion_point. Code inserted at this point will be placed + // immediately above the line containing the insertion point (thus multiple + // insertions to the same point will come out in the order they were added). + // The double-@ is intended to make it unlikely that the generated code + // could contain things that look like insertion points by accident. + // + // For example, the C++ code generator places the following line in the + // .pb.h files that it generates: + // // @@protoc_insertion_point(namespace_scope) + // This line appears within the scope of the file's package namespace, but + // outside of any particular class. Another plugin can then specify the + // insertion_point "namespace_scope" to generate additional classes or + // other declarations that should be placed in this scope. + // + // Note that if the line containing the insertion point begins with + // whitespace, the same whitespace will be added to every line of the + // inserted text. This is useful for languages like Python, where + // indentation matters. In these languages, the insertion point comment + // should be indented the same amount as any inserted code will need to be + // in order to work correctly in that context. + // + // The code generator that generates the initial file and the one which + // inserts into it must both run as part of a single invocation of protoc. + // Code generators are executed in the order in which they appear on the + // command line. + // + // If |insertion_point| is present, |name| must also be present. + InsertionPoint *string `protobuf:"bytes,2,opt,name=insertion_point,json=insertionPoint" json:"insertion_point,omitempty"` + // The file contents. + Content *string `protobuf:"bytes,15,opt,name=content" json:"content,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CodeGeneratorResponse_File) Reset() { *m = CodeGeneratorResponse_File{} } +func (m *CodeGeneratorResponse_File) String() string { return proto.CompactTextString(m) } +func (*CodeGeneratorResponse_File) ProtoMessage() {} +func (*CodeGeneratorResponse_File) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2, 0} } +func (m *CodeGeneratorResponse_File) Unmarshal(b []byte) error { + return xxx_messageInfo_CodeGeneratorResponse_File.Unmarshal(m, b) +} +func (m *CodeGeneratorResponse_File) Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CodeGeneratorResponse_File.Marshal(b, m, deterministic) +} +func (dst *CodeGeneratorResponse_File) XXX_Merge(src proto.Message) { + xxx_messageInfo_CodeGeneratorResponse_File.Merge(dst, src) +} +func (m *CodeGeneratorResponse_File) XXX_Size() int { + return xxx_messageInfo_CodeGeneratorResponse_File.Size(m) +} +func (m *CodeGeneratorResponse_File) XXX_DiscardUnknown() { + xxx_messageInfo_CodeGeneratorResponse_File.DiscardUnknown(m) +} + +var xxx_messageInfo_CodeGeneratorResponse_File proto.InternalMessageInfo + +func (m *CodeGeneratorResponse_File) GetName() string { + if m != nil && m.Name != nil { + return *m.Name + } + return "" +} + +func (m *CodeGeneratorResponse_File) GetInsertionPoint() string { + if m != nil && m.InsertionPoint != nil { + return *m.InsertionPoint + } + return "" +} + +func (m *CodeGeneratorResponse_File) GetContent() string { + if m != nil && m.Content != nil { + return *m.Content + } + return "" +} + +func init() { + proto.RegisterType((*Version)(nil), "google.protobuf.compiler.Version") + proto.RegisterType((*CodeGeneratorRequest)(nil), "google.protobuf.compiler.CodeGeneratorRequest") + proto.RegisterType((*CodeGeneratorResponse)(nil), "google.protobuf.compiler.CodeGeneratorResponse") + proto.RegisterType((*CodeGeneratorResponse_File)(nil), "google.protobuf.compiler.CodeGeneratorResponse.File") +} + +func init() { proto.RegisterFile("google/protobuf/compiler/plugin.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 417 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xcf, 0x6a, 0x14, 0x41, + 0x10, 0xc6, 0x19, 0x77, 0x63, 0x98, 0x8a, 0x64, 0x43, 0x13, 0xa5, 0x09, 0x39, 0x8c, 0x8b, 0xe2, + 0x5c, 0x32, 0x0b, 0xc1, 0x8b, 0x78, 0x4b, 0x44, 0x3d, 0x78, 0x58, 0x1a, 0xf1, 0x20, 0xc8, 0x30, + 0x99, 0xd4, 0x74, 0x5a, 0x66, 0xba, 0xc6, 0xee, 0x1e, 0xf1, 0x49, 0x7d, 0x0f, 0xdf, 0x40, 0xfa, + 0xcf, 0x24, 0xb2, 0xb8, 0xa7, 0xee, 0xef, 0x57, 0xd5, 0xd5, 0x55, 0x1f, 0x05, 0x2f, 0x25, 0x91, + 0xec, 0x71, 0x33, 0x1a, 0x72, 0x74, 0x33, 0x75, 0x9b, 0x96, 0x86, 0x51, 0xf5, 0x68, 0x36, 0x63, + 0x3f, 0x49, 0xa5, 0xab, 0x10, 0x60, 0x3c, 0xa6, 0x55, 0x73, 0x5a, 0x35, 0xa7, 0x9d, 0x15, 0xbb, + 0x05, 0x6e, 0xd1, 0xb6, 0x46, 0x8d, 0x8e, 0x4c, 0xcc, 0x5e, 0xb7, 0x70, 0xf8, 0x05, 0x8d, 0x55, + 0xa4, 0xd9, 0x29, 0x1c, 0x0c, 0xcd, 0x77, 0x32, 0x3c, 0x2b, 0xb2, 0xf2, 0x40, 0x44, 0x11, 0xa8, + 0xd2, 0x64, 0xf8, 0xa3, 0x44, 0xbd, 0xf0, 0x74, 0x6c, 0x5c, 0x7b, 0xc7, 0x17, 0x91, 0x06, 0xc1, + 0x9e, 0xc1, 0x63, 0x3b, 0x75, 0x9d, 0xfa, 0xc5, 0x97, 0x45, 0x56, 0xe6, 0x22, 0xa9, 0xf5, 0x9f, + 0x0c, 0x4e, 0xaf, 0xe9, 0x16, 0x3f, 0xa0, 0x46, 0xd3, 0x38, 0x32, 0x02, 0x7f, 0x4c, 0x68, 0x1d, + 0x2b, 0xe1, 0xa4, 0x53, 0x3d, 0xd6, 0x8e, 0x6a, 0x19, 0x63, 0xc8, 0xb3, 0x62, 0x51, 0xe6, 0xe2, + 0xd8, 0xf3, 0xcf, 0x94, 0x5e, 0x20, 0x3b, 0x87, 0x7c, 0x6c, 0x4c, 0x33, 0xa0, 0xc3, 0xd8, 0x4a, + 0x2e, 0x1e, 0x00, 0xbb, 0x06, 0x08, 0xe3, 0xd4, 0xfe, 0x15, 0x5f, 0x15, 0x8b, 0xf2, 0xe8, 0xf2, + 0x45, 0xb5, 0x6b, 0xcb, 0x7b, 0xd5, 0xe3, 0xbb, 0x7b, 0x03, 0xb6, 0x1e, 0x8b, 0x3c, 0x44, 0x7d, + 0x84, 0x7d, 0x82, 0x93, 0xd9, 0xb8, 0xfa, 0x67, 0xf4, 0x24, 0x8c, 0x77, 0x74, 0xf9, 0xbc, 0xda, + 0xe7, 0x70, 0x95, 0xcc, 0x13, 0xab, 0x99, 0x24, 0xb0, 0xfe, 0x9d, 0xc1, 0xd3, 0x9d, 0x99, 0xed, + 0x48, 0xda, 0xa2, 0xf7, 0x0e, 0x8d, 0x49, 0x3e, 0xe7, 0x22, 0x0a, 0xf6, 0x11, 0x96, 0xff, 0x34, + 0xff, 0x7a, 0xff, 0x8f, 0xff, 0x2d, 0x1a, 0x66, 0x13, 0xa1, 0xc2, 0xd9, 0x37, 0x58, 0x86, 0x79, + 0x18, 0x2c, 0x75, 0x33, 0x60, 0xfa, 0x26, 0xdc, 0xd9, 0x2b, 0x58, 0x29, 0x6d, 0xd1, 0x38, 0x45, + 0xba, 0x1e, 0x49, 0x69, 0x97, 0xcc, 0x3c, 0xbe, 0xc7, 0x5b, 0x4f, 0x19, 0x87, 0xc3, 0x96, 0xb4, + 0x43, 0xed, 0xf8, 0x2a, 0x24, 0xcc, 0xf2, 0x4a, 0xc2, 0x79, 0x4b, 0xc3, 0xde, 0xfe, 0xae, 0x9e, + 0x6c, 0xc3, 0x6e, 0x06, 0x7b, 0xed, 0xd7, 0x37, 0x52, 0xb9, 0xbb, 0xe9, 0xc6, 0x87, 0x37, 0x92, + 0xfa, 0x46, 0xcb, 0x87, 0x65, 0x0c, 0x97, 0xf6, 0x42, 0xa2, 0xbe, 0x90, 0x94, 0x56, 0xfa, 0x6d, + 0x3c, 0x6a, 0x49, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xf7, 0x15, 0x40, 0xc5, 0xfe, 0x02, 0x00, + 0x00, +} diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/plugin.proto b/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/plugin.proto new file mode 100644 index 0000000000000..5b5574529ed48 --- /dev/null +++ b/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/plugin.proto @@ -0,0 +1,167 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// +// WARNING: The plugin interface is currently EXPERIMENTAL and is subject to +// change. +// +// protoc (aka the Protocol Compiler) can be extended via plugins. A plugin is +// just a program that reads a CodeGeneratorRequest from stdin and writes a +// CodeGeneratorResponse to stdout. +// +// Plugins written using C++ can use google/protobuf/compiler/plugin.h instead +// of dealing with the raw protocol defined here. +// +// A plugin executable needs only to be placed somewhere in the path. The +// plugin should be named "protoc-gen-$NAME", and will then be used when the +// flag "--${NAME}_out" is passed to protoc. + +syntax = "proto2"; +package google.protobuf.compiler; +option java_package = "com.google.protobuf.compiler"; +option java_outer_classname = "PluginProtos"; + +option go_package = "github.com/golang/protobuf/protoc-gen-go/plugin;plugin_go"; + +import "google/protobuf/descriptor.proto"; + +// The version number of protocol compiler. +message Version { + optional int32 major = 1; + optional int32 minor = 2; + optional int32 patch = 3; + // A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should + // be empty for mainline stable releases. + optional string suffix = 4; +} + +// An encoded CodeGeneratorRequest is written to the plugin's stdin. +message CodeGeneratorRequest { + // The .proto files that were explicitly listed on the command-line. The + // code generator should generate code only for these files. Each file's + // descriptor will be included in proto_file, below. + repeated string file_to_generate = 1; + + // The generator parameter passed on the command-line. + optional string parameter = 2; + + // FileDescriptorProtos for all files in files_to_generate and everything + // they import. The files will appear in topological order, so each file + // appears before any file that imports it. + // + // protoc guarantees that all proto_files will be written after + // the fields above, even though this is not technically guaranteed by the + // protobuf wire format. This theoretically could allow a plugin to stream + // in the FileDescriptorProtos and handle them one by one rather than read + // the entire set into memory at once. However, as of this writing, this + // is not similarly optimized on protoc's end -- it will store all fields in + // memory at once before sending them to the plugin. + // + // Type names of fields and extensions in the FileDescriptorProto are always + // fully qualified. + repeated FileDescriptorProto proto_file = 15; + + // The version number of protocol compiler. + optional Version compiler_version = 3; + +} + +// The plugin writes an encoded CodeGeneratorResponse to stdout. +message CodeGeneratorResponse { + // Error message. If non-empty, code generation failed. The plugin process + // should exit with status code zero even if it reports an error in this way. + // + // This should be used to indicate errors in .proto files which prevent the + // code generator from generating correct code. Errors which indicate a + // problem in protoc itself -- such as the input CodeGeneratorRequest being + // unparseable -- should be reported by writing a message to stderr and + // exiting with a non-zero status code. + optional string error = 1; + + // Represents a single generated file. + message File { + // The file name, relative to the output directory. The name must not + // contain "." or ".." components and must be relative, not be absolute (so, + // the file cannot lie outside the output directory). "/" must be used as + // the path separator, not "\". + // + // If the name is omitted, the content will be appended to the previous + // file. This allows the generator to break large files into small chunks, + // and allows the generated text to be streamed back to protoc so that large + // files need not reside completely in memory at one time. Note that as of + // this writing protoc does not optimize for this -- it will read the entire + // CodeGeneratorResponse before writing files to disk. + optional string name = 1; + + // If non-empty, indicates that the named file should already exist, and the + // content here is to be inserted into that file at a defined insertion + // point. This feature allows a code generator to extend the output + // produced by another code generator. The original generator may provide + // insertion points by placing special annotations in the file that look + // like: + // @@protoc_insertion_point(NAME) + // The annotation can have arbitrary text before and after it on the line, + // which allows it to be placed in a comment. NAME should be replaced with + // an identifier naming the point -- this is what other generators will use + // as the insertion_point. Code inserted at this point will be placed + // immediately above the line containing the insertion point (thus multiple + // insertions to the same point will come out in the order they were added). + // The double-@ is intended to make it unlikely that the generated code + // could contain things that look like insertion points by accident. + // + // For example, the C++ code generator places the following line in the + // .pb.h files that it generates: + // // @@protoc_insertion_point(namespace_scope) + // This line appears within the scope of the file's package namespace, but + // outside of any particular class. Another plugin can then specify the + // insertion_point "namespace_scope" to generate additional classes or + // other declarations that should be placed in this scope. + // + // Note that if the line containing the insertion point begins with + // whitespace, the same whitespace will be added to every line of the + // inserted text. This is useful for languages like Python, where + // indentation matters. In these languages, the insertion point comment + // should be indented the same amount as any inserted code will need to be + // in order to work correctly in that context. + // + // The code generator that generates the initial file and the one which + // inserts into it must both run as part of a single invocation of protoc. + // Code generators are executed in the order in which they appear on the + // command line. + // + // If |insertion_point| is present, |name| must also be present. + optional string insertion_point = 2; + + // The file contents. + optional string content = 15; + } + repeated File file = 15; +} diff --git a/vendor/github.com/golang/protobuf/ptypes/any.go b/vendor/github.com/golang/protobuf/ptypes/any.go index b2af97f4a9823..70276e8f5c946 100644 --- a/vendor/github.com/golang/protobuf/ptypes/any.go +++ b/vendor/github.com/golang/protobuf/ptypes/any.go @@ -130,10 +130,12 @@ func UnmarshalAny(any *any.Any, pb proto.Message) error { // Is returns true if any value contains a given message type. func Is(any *any.Any, pb proto.Message) bool { - aname, err := AnyMessageName(any) - if err != nil { + // The following is equivalent to AnyMessageName(any) == proto.MessageName(pb), + // but it avoids scanning TypeUrl for the slash. + if any == nil { return false } - - return aname == proto.MessageName(pb) + name := proto.MessageName(pb) + prefix := len(any.TypeUrl) - len(name) + return prefix >= 1 && any.TypeUrl[prefix-1] == '/' && any.TypeUrl[prefix:] == name } diff --git a/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go b/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go index f67edc7dc2bfc..7b0ad1ad86cd0 100644 --- a/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go +++ b/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go @@ -1,11 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: google/protobuf/any.proto -package any // import "github.com/golang/protobuf/ptypes/any" +package any -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -16,7 +18,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package // `Any` contains an arbitrary serialized protocol buffer message along with a // URL that describes the type of the serialized message. @@ -99,17 +101,19 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // } // type Any struct { - // A URL/resource name whose content describes the type of the - // serialized protocol buffer message. + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). // - // For URLs which use the scheme `http`, `https`, or no scheme, the - // following restrictions and interpretations apply: + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: // // * If no scheme is provided, `https` is assumed. - // * The last segment of the URL's path must represent the fully - // qualified name of the type (as in `path/google.protobuf.Duration`). - // The name should be in a canonical form (e.g., leading "." is - // not accepted). // * An HTTP GET on the URL must yield a [google.protobuf.Type][] // value in binary format, or produce an error. // * Applications are allowed to cache lookup results based on the @@ -118,10 +122,14 @@ type Any struct { // on changes to types. (Use versioned type names to manage // breaking changes.) // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // // Schemes other than `http`, `https` (or the empty scheme) might be // used with implementation specific semantics. // - TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl" json:"type_url,omitempty"` + TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"` // Must be a valid serialized protocol buffer of the above specified type. Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -133,17 +141,19 @@ func (m *Any) Reset() { *m = Any{} } func (m *Any) String() string { return proto.CompactTextString(m) } func (*Any) ProtoMessage() {} func (*Any) Descriptor() ([]byte, []int) { - return fileDescriptor_any_744b9ca530f228db, []int{0} + return fileDescriptor_b53526c13ae22eb4, []int{0} } + func (*Any) XXX_WellKnownType() string { return "Any" } + func (m *Any) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Any.Unmarshal(m, b) } func (m *Any) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Any.Marshal(b, m, deterministic) } -func (dst *Any) XXX_Merge(src proto.Message) { - xxx_messageInfo_Any.Merge(dst, src) +func (m *Any) XXX_Merge(src proto.Message) { + xxx_messageInfo_Any.Merge(m, src) } func (m *Any) XXX_Size() int { return xxx_messageInfo_Any.Size(m) @@ -172,9 +182,11 @@ func init() { proto.RegisterType((*Any)(nil), "google.protobuf.Any") } -func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_any_744b9ca530f228db) } +func init() { + proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4) +} -var fileDescriptor_any_744b9ca530f228db = []byte{ +var fileDescriptor_b53526c13ae22eb4 = []byte{ // 185 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4, diff --git a/vendor/github.com/golang/protobuf/ptypes/any/any.proto b/vendor/github.com/golang/protobuf/ptypes/any/any.proto index c74866762315f..c9be85416736c 100644 --- a/vendor/github.com/golang/protobuf/ptypes/any/any.proto +++ b/vendor/github.com/golang/protobuf/ptypes/any/any.proto @@ -120,17 +120,19 @@ option objc_class_prefix = "GPB"; // } // message Any { - // A URL/resource name whose content describes the type of the - // serialized protocol buffer message. + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). // - // For URLs which use the scheme `http`, `https`, or no scheme, the - // following restrictions and interpretations apply: + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: // // * If no scheme is provided, `https` is assumed. - // * The last segment of the URL's path must represent the fully - // qualified name of the type (as in `path/google.protobuf.Duration`). - // The name should be in a canonical form (e.g., leading "." is - // not accepted). // * An HTTP GET on the URL must yield a [google.protobuf.Type][] // value in binary format, or produce an error. // * Applications are allowed to cache lookup results based on the @@ -139,6 +141,10 @@ message Any { // on changes to types. (Use versioned type names to manage // breaking changes.) // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // // Schemes other than `http`, `https` (or the empty scheme) might be // used with implementation specific semantics. // diff --git a/vendor/github.com/golang/protobuf/ptypes/duration.go b/vendor/github.com/golang/protobuf/ptypes/duration.go index 65cb0f8eb5f33..26d1ca2fb589c 100644 --- a/vendor/github.com/golang/protobuf/ptypes/duration.go +++ b/vendor/github.com/golang/protobuf/ptypes/duration.go @@ -82,7 +82,7 @@ func Duration(p *durpb.Duration) (time.Duration, error) { return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p) } if p.Nanos != 0 { - d += time.Duration(p.Nanos) + d += time.Duration(p.Nanos) * time.Nanosecond if (d < 0) != (p.Nanos < 0) { return 0, fmt.Errorf("duration: %v is out of range for time.Duration", p) } diff --git a/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go b/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go index 4d75473b8b2bf..58b0786990d17 100644 --- a/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go +++ b/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go @@ -1,11 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: google/protobuf/duration.proto -package duration // import "github.com/golang/protobuf/ptypes/duration" +package duration -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -16,7 +18,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package // A Duration represents a signed, fixed-length span of time represented // as a count of seconds and fractions of seconds at nanosecond @@ -39,7 +41,7 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // if (duration.seconds < 0 && duration.nanos > 0) { // duration.seconds += 1; // duration.nanos -= 1000000000; -// } else if (durations.seconds > 0 && duration.nanos < 0) { +// } else if (duration.seconds > 0 && duration.nanos < 0) { // duration.seconds -= 1; // duration.nanos += 1000000000; // } @@ -82,14 +84,14 @@ type Duration struct { // Signed seconds of the span of time. Must be from -315,576,000,000 // to +315,576,000,000 inclusive. Note: these bounds are computed from: // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years - Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"` + Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` // Signed fractions of a second at nanosecond resolution of the span // of time. Durations less than one second are represented with a 0 // `seconds` field and a positive or negative `nanos` field. For durations // of one second or more, a non-zero value for the `nanos` field must be // of the same sign as the `seconds` field. Must be from -999,999,999 // to +999,999,999 inclusive. - Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"` + Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -99,17 +101,19 @@ func (m *Duration) Reset() { *m = Duration{} } func (m *Duration) String() string { return proto.CompactTextString(m) } func (*Duration) ProtoMessage() {} func (*Duration) Descriptor() ([]byte, []int) { - return fileDescriptor_duration_e7d612259e3f0613, []int{0} + return fileDescriptor_23597b2ebd7ac6c5, []int{0} } + func (*Duration) XXX_WellKnownType() string { return "Duration" } + func (m *Duration) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Duration.Unmarshal(m, b) } func (m *Duration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Duration.Marshal(b, m, deterministic) } -func (dst *Duration) XXX_Merge(src proto.Message) { - xxx_messageInfo_Duration.Merge(dst, src) +func (m *Duration) XXX_Merge(src proto.Message) { + xxx_messageInfo_Duration.Merge(m, src) } func (m *Duration) XXX_Size() int { return xxx_messageInfo_Duration.Size(m) @@ -139,10 +143,10 @@ func init() { } func init() { - proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor_duration_e7d612259e3f0613) + proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor_23597b2ebd7ac6c5) } -var fileDescriptor_duration_e7d612259e3f0613 = []byte{ +var fileDescriptor_23597b2ebd7ac6c5 = []byte{ // 190 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a, diff --git a/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto b/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto index 975fce41aae05..99cb102c353fa 100644 --- a/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto +++ b/vendor/github.com/golang/protobuf/ptypes/duration/duration.proto @@ -61,7 +61,7 @@ option objc_class_prefix = "GPB"; // if (duration.seconds < 0 && duration.nanos > 0) { // duration.seconds += 1; // duration.nanos -= 1000000000; -// } else if (durations.seconds > 0 && duration.nanos < 0) { +// } else if (duration.seconds > 0 && duration.nanos < 0) { // duration.seconds -= 1; // duration.nanos += 1000000000; // } @@ -101,7 +101,6 @@ option objc_class_prefix = "GPB"; // // message Duration { - // Signed seconds of the span of time. Must be from -315,576,000,000 // to +315,576,000,000 inclusive. Note: these bounds are computed from: // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years diff --git a/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go b/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go index a69b403ce1562..6bd9f67480a4d 100644 --- a/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go +++ b/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go @@ -1,11 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: google/protobuf/empty.proto -package empty // import "github.com/golang/protobuf/ptypes/empty" +package empty -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -16,7 +18,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package // A generic empty message that you can re-use to avoid defining duplicated // empty messages in your APIs. A typical example is to use it as the request @@ -37,17 +39,19 @@ func (m *Empty) Reset() { *m = Empty{} } func (m *Empty) String() string { return proto.CompactTextString(m) } func (*Empty) ProtoMessage() {} func (*Empty) Descriptor() ([]byte, []int) { - return fileDescriptor_empty_39e6d6db0632e5b2, []int{0} + return fileDescriptor_900544acb223d5b8, []int{0} } + func (*Empty) XXX_WellKnownType() string { return "Empty" } + func (m *Empty) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Empty.Unmarshal(m, b) } func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Empty.Marshal(b, m, deterministic) } -func (dst *Empty) XXX_Merge(src proto.Message) { - xxx_messageInfo_Empty.Merge(dst, src) +func (m *Empty) XXX_Merge(src proto.Message) { + xxx_messageInfo_Empty.Merge(m, src) } func (m *Empty) XXX_Size() int { return xxx_messageInfo_Empty.Size(m) @@ -62,9 +66,11 @@ func init() { proto.RegisterType((*Empty)(nil), "google.protobuf.Empty") } -func init() { proto.RegisterFile("google/protobuf/empty.proto", fileDescriptor_empty_39e6d6db0632e5b2) } +func init() { + proto.RegisterFile("google/protobuf/empty.proto", fileDescriptor_900544acb223d5b8) +} -var fileDescriptor_empty_39e6d6db0632e5b2 = []byte{ +var fileDescriptor_900544acb223d5b8 = []byte{ // 148 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcd, 0x2d, 0x28, diff --git a/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go b/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go index 442c0e0999ca7..d82d6176b514b 100644 --- a/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go +++ b/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go @@ -1,11 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: google/protobuf/struct.proto -package structpb // import "github.com/golang/protobuf/ptypes/struct" +package structpb -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -16,7 +18,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package // `NullValue` is a singleton enumeration to represent the null value for the // `Value` type union. @@ -32,6 +34,7 @@ const ( var NullValue_name = map[int32]string{ 0: "NULL_VALUE", } + var NullValue_value = map[string]int32{ "NULL_VALUE": 0, } @@ -39,9 +42,11 @@ var NullValue_value = map[string]int32{ func (x NullValue) String() string { return proto.EnumName(NullValue_name, int32(x)) } + func (NullValue) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_struct_3a5a94e0c7801b27, []int{0} + return fileDescriptor_df322afd6c9fb402, []int{0} } + func (NullValue) XXX_WellKnownType() string { return "NullValue" } // `Struct` represents a structured data value, consisting of fields @@ -54,7 +59,7 @@ func (NullValue) XXX_WellKnownType() string { return "NullValue" } // The JSON representation for `Struct` is JSON object. type Struct struct { // Unordered map of dynamically typed values. - Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -64,17 +69,19 @@ func (m *Struct) Reset() { *m = Struct{} } func (m *Struct) String() string { return proto.CompactTextString(m) } func (*Struct) ProtoMessage() {} func (*Struct) Descriptor() ([]byte, []int) { - return fileDescriptor_struct_3a5a94e0c7801b27, []int{0} + return fileDescriptor_df322afd6c9fb402, []int{0} } + func (*Struct) XXX_WellKnownType() string { return "Struct" } + func (m *Struct) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Struct.Unmarshal(m, b) } func (m *Struct) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Struct.Marshal(b, m, deterministic) } -func (dst *Struct) XXX_Merge(src proto.Message) { - xxx_messageInfo_Struct.Merge(dst, src) +func (m *Struct) XXX_Merge(src proto.Message) { + xxx_messageInfo_Struct.Merge(m, src) } func (m *Struct) XXX_Size() int { return xxx_messageInfo_Struct.Size(m) @@ -118,17 +125,19 @@ func (m *Value) Reset() { *m = Value{} } func (m *Value) String() string { return proto.CompactTextString(m) } func (*Value) ProtoMessage() {} func (*Value) Descriptor() ([]byte, []int) { - return fileDescriptor_struct_3a5a94e0c7801b27, []int{1} + return fileDescriptor_df322afd6c9fb402, []int{1} } + func (*Value) XXX_WellKnownType() string { return "Value" } + func (m *Value) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Value.Unmarshal(m, b) } func (m *Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Value.Marshal(b, m, deterministic) } -func (dst *Value) XXX_Merge(src proto.Message) { - xxx_messageInfo_Value.Merge(dst, src) +func (m *Value) XXX_Merge(src proto.Message) { + xxx_messageInfo_Value.Merge(m, src) } func (m *Value) XXX_Size() int { return xxx_messageInfo_Value.Size(m) @@ -144,30 +153,40 @@ type isValue_Kind interface { } type Value_NullValue struct { - NullValue NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,enum=google.protobuf.NullValue,oneof"` + NullValue NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,proto3,enum=google.protobuf.NullValue,oneof"` } + type Value_NumberValue struct { - NumberValue float64 `protobuf:"fixed64,2,opt,name=number_value,json=numberValue,oneof"` + NumberValue float64 `protobuf:"fixed64,2,opt,name=number_value,json=numberValue,proto3,oneof"` } + type Value_StringValue struct { - StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,oneof"` + StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,proto3,oneof"` } + type Value_BoolValue struct { - BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,oneof"` + BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,proto3,oneof"` } + type Value_StructValue struct { - StructValue *Struct `protobuf:"bytes,5,opt,name=struct_value,json=structValue,oneof"` + StructValue *Struct `protobuf:"bytes,5,opt,name=struct_value,json=structValue,proto3,oneof"` } + type Value_ListValue struct { - ListValue *ListValue `protobuf:"bytes,6,opt,name=list_value,json=listValue,oneof"` + ListValue *ListValue `protobuf:"bytes,6,opt,name=list_value,json=listValue,proto3,oneof"` } -func (*Value_NullValue) isValue_Kind() {} +func (*Value_NullValue) isValue_Kind() {} + func (*Value_NumberValue) isValue_Kind() {} + func (*Value_StringValue) isValue_Kind() {} -func (*Value_BoolValue) isValue_Kind() {} + +func (*Value_BoolValue) isValue_Kind() {} + func (*Value_StructValue) isValue_Kind() {} -func (*Value_ListValue) isValue_Kind() {} + +func (*Value_ListValue) isValue_Kind() {} func (m *Value) GetKind() isValue_Kind { if m != nil { @@ -218,9 +237,9 @@ func (m *Value) GetListValue() *ListValue { return nil } -// XXX_OneofFuncs is for the internal use of the proto package. -func (*Value) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _Value_OneofMarshaler, _Value_OneofUnmarshaler, _Value_OneofSizer, []interface{}{ +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Value) XXX_OneofWrappers() []interface{} { + return []interface{}{ (*Value_NullValue)(nil), (*Value_NumberValue)(nil), (*Value_StringValue)(nil), @@ -230,135 +249,12 @@ func (*Value) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, } } -func _Value_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*Value) - // kind - switch x := m.Kind.(type) { - case *Value_NullValue: - b.EncodeVarint(1<<3 | proto.WireVarint) - b.EncodeVarint(uint64(x.NullValue)) - case *Value_NumberValue: - b.EncodeVarint(2<<3 | proto.WireFixed64) - b.EncodeFixed64(math.Float64bits(x.NumberValue)) - case *Value_StringValue: - b.EncodeVarint(3<<3 | proto.WireBytes) - b.EncodeStringBytes(x.StringValue) - case *Value_BoolValue: - t := uint64(0) - if x.BoolValue { - t = 1 - } - b.EncodeVarint(4<<3 | proto.WireVarint) - b.EncodeVarint(t) - case *Value_StructValue: - b.EncodeVarint(5<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.StructValue); err != nil { - return err - } - case *Value_ListValue: - b.EncodeVarint(6<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.ListValue); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("Value.Kind has unexpected type %T", x) - } - return nil -} - -func _Value_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*Value) - switch tag { - case 1: // kind.null_value - if wire != proto.WireVarint { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeVarint() - m.Kind = &Value_NullValue{NullValue(x)} - return true, err - case 2: // kind.number_value - if wire != proto.WireFixed64 { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeFixed64() - m.Kind = &Value_NumberValue{math.Float64frombits(x)} - return true, err - case 3: // kind.string_value - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.Kind = &Value_StringValue{x} - return true, err - case 4: // kind.bool_value - if wire != proto.WireVarint { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeVarint() - m.Kind = &Value_BoolValue{x != 0} - return true, err - case 5: // kind.struct_value - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(Struct) - err := b.DecodeMessage(msg) - m.Kind = &Value_StructValue{msg} - return true, err - case 6: // kind.list_value - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(ListValue) - err := b.DecodeMessage(msg) - m.Kind = &Value_ListValue{msg} - return true, err - default: - return false, nil - } -} - -func _Value_OneofSizer(msg proto.Message) (n int) { - m := msg.(*Value) - // kind - switch x := m.Kind.(type) { - case *Value_NullValue: - n += 1 // tag and wire - n += proto.SizeVarint(uint64(x.NullValue)) - case *Value_NumberValue: - n += 1 // tag and wire - n += 8 - case *Value_StringValue: - n += 1 // tag and wire - n += proto.SizeVarint(uint64(len(x.StringValue))) - n += len(x.StringValue) - case *Value_BoolValue: - n += 1 // tag and wire - n += 1 - case *Value_StructValue: - s := proto.Size(x.StructValue) - n += 1 // tag and wire - n += proto.SizeVarint(uint64(s)) - n += s - case *Value_ListValue: - s := proto.Size(x.ListValue) - n += 1 // tag and wire - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - // `ListValue` is a wrapper around a repeated field of values. // // The JSON representation for `ListValue` is JSON array. type ListValue struct { // Repeated field of dynamically typed values. - Values []*Value `protobuf:"bytes,1,rep,name=values" json:"values,omitempty"` + Values []*Value `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -368,17 +264,19 @@ func (m *ListValue) Reset() { *m = ListValue{} } func (m *ListValue) String() string { return proto.CompactTextString(m) } func (*ListValue) ProtoMessage() {} func (*ListValue) Descriptor() ([]byte, []int) { - return fileDescriptor_struct_3a5a94e0c7801b27, []int{2} + return fileDescriptor_df322afd6c9fb402, []int{2} } + func (*ListValue) XXX_WellKnownType() string { return "ListValue" } + func (m *ListValue) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ListValue.Unmarshal(m, b) } func (m *ListValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_ListValue.Marshal(b, m, deterministic) } -func (dst *ListValue) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListValue.Merge(dst, src) +func (m *ListValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListValue.Merge(m, src) } func (m *ListValue) XXX_Size() int { return xxx_messageInfo_ListValue.Size(m) @@ -397,18 +295,18 @@ func (m *ListValue) GetValues() []*Value { } func init() { + proto.RegisterEnum("google.protobuf.NullValue", NullValue_name, NullValue_value) proto.RegisterType((*Struct)(nil), "google.protobuf.Struct") proto.RegisterMapType((map[string]*Value)(nil), "google.protobuf.Struct.FieldsEntry") proto.RegisterType((*Value)(nil), "google.protobuf.Value") proto.RegisterType((*ListValue)(nil), "google.protobuf.ListValue") - proto.RegisterEnum("google.protobuf.NullValue", NullValue_name, NullValue_value) } func init() { - proto.RegisterFile("google/protobuf/struct.proto", fileDescriptor_struct_3a5a94e0c7801b27) + proto.RegisterFile("google/protobuf/struct.proto", fileDescriptor_df322afd6c9fb402) } -var fileDescriptor_struct_3a5a94e0c7801b27 = []byte{ +var fileDescriptor_df322afd6c9fb402 = []byte{ // 417 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x8b, 0xd3, 0x40, 0x14, 0xc7, 0x3b, 0xc9, 0x36, 0x98, 0x17, 0x59, 0x97, 0x11, 0xb4, 0xac, 0xa2, 0xa1, 0x7b, 0x09, diff --git a/vendor/github.com/golang/protobuf/ptypes/struct/struct.proto b/vendor/github.com/golang/protobuf/ptypes/struct/struct.proto index 7d7808e7fbb69..ed990e31d9590 100644 --- a/vendor/github.com/golang/protobuf/ptypes/struct/struct.proto +++ b/vendor/github.com/golang/protobuf/ptypes/struct/struct.proto @@ -40,7 +40,6 @@ option java_outer_classname = "StructProto"; option java_multiple_files = true; option objc_class_prefix = "GPB"; - // `Struct` represents a structured data value, consisting of fields // which map to dynamically typed values. In some languages, `Struct` // might be supported by a native representation. For example, in diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp.go b/vendor/github.com/golang/protobuf/ptypes/timestamp.go index 47f10dbc2ccce..8da0df01acd17 100644 --- a/vendor/github.com/golang/protobuf/ptypes/timestamp.go +++ b/vendor/github.com/golang/protobuf/ptypes/timestamp.go @@ -111,11 +111,9 @@ func TimestampNow() *tspb.Timestamp { // TimestampProto converts the time.Time to a google.protobuf.Timestamp proto. // It returns an error if the resulting Timestamp is invalid. func TimestampProto(t time.Time) (*tspb.Timestamp, error) { - seconds := t.Unix() - nanos := int32(t.Sub(time.Unix(seconds, 0))) ts := &tspb.Timestamp{ - Seconds: seconds, - Nanos: nanos, + Seconds: t.Unix(), + Nanos: int32(t.Nanosecond()), } if err := validateTimestamp(ts); err != nil { return nil, err diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go index e9c2222821664..7a3b1e40e291c 100644 --- a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go +++ b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go @@ -1,11 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: google/protobuf/timestamp.proto -package timestamp // import "github.com/golang/protobuf/ptypes/timestamp" +package timestamp -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -16,19 +18,21 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package -// A Timestamp represents a point in time independent of any time zone -// or calendar, represented as seconds and fractions of seconds at -// nanosecond resolution in UTC Epoch time. It is encoded using the -// Proleptic Gregorian Calendar which extends the Gregorian calendar -// backwards to year one. It is encoded assuming all minutes are 60 -// seconds long, i.e. leap seconds are "smeared" so that no leap second -// table is needed for interpretation. Range is from -// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. -// By restricting to that range, we ensure that we can convert to -// and from RFC 3339 date strings. -// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. // // # Examples // @@ -81,31 +85,35 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone -// is required, though only UTC (as indicated by "Z") is presently supported. +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). // // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past // 01:30 UTC on January 15, 2017. // // In JavaScript, one can convert a Date object to this format using the -// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) // method. In Python, a standard `datetime.datetime` object can be converted -// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) -// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one -// can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( -// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--) -// to obtain a formatter capable of generating timestamps in this format. +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// ) to obtain a formatter capable of generating timestamps in this format. // // type Timestamp struct { // Represents seconds of UTC time since Unix epoch // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to // 9999-12-31T23:59:59Z inclusive. - Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"` + Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` // Non-negative fractions of a second at nanosecond resolution. Negative // second values with fractions must still have non-negative nanos values // that count forward in time. Must be from 0 to 999,999,999 // inclusive. - Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"` + Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -115,17 +123,19 @@ func (m *Timestamp) Reset() { *m = Timestamp{} } func (m *Timestamp) String() string { return proto.CompactTextString(m) } func (*Timestamp) ProtoMessage() {} func (*Timestamp) Descriptor() ([]byte, []int) { - return fileDescriptor_timestamp_b826e8e5fba671a8, []int{0} + return fileDescriptor_292007bbfe81227e, []int{0} } + func (*Timestamp) XXX_WellKnownType() string { return "Timestamp" } + func (m *Timestamp) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Timestamp.Unmarshal(m, b) } func (m *Timestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return xxx_messageInfo_Timestamp.Marshal(b, m, deterministic) } -func (dst *Timestamp) XXX_Merge(src proto.Message) { - xxx_messageInfo_Timestamp.Merge(dst, src) +func (m *Timestamp) XXX_Merge(src proto.Message) { + xxx_messageInfo_Timestamp.Merge(m, src) } func (m *Timestamp) XXX_Size() int { return xxx_messageInfo_Timestamp.Size(m) @@ -155,10 +165,10 @@ func init() { } func init() { - proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor_timestamp_b826e8e5fba671a8) + proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor_292007bbfe81227e) } -var fileDescriptor_timestamp_b826e8e5fba671a8 = []byte{ +var fileDescriptor_292007bbfe81227e = []byte{ // 191 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d, diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto index 06750ab1f127d..cd357864a9e47 100644 --- a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto +++ b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.proto @@ -40,17 +40,19 @@ option java_outer_classname = "TimestampProto"; option java_multiple_files = true; option objc_class_prefix = "GPB"; -// A Timestamp represents a point in time independent of any time zone -// or calendar, represented as seconds and fractions of seconds at -// nanosecond resolution in UTC Epoch time. It is encoded using the -// Proleptic Gregorian Calendar which extends the Gregorian calendar -// backwards to year one. It is encoded assuming all minutes are 60 -// seconds long, i.e. leap seconds are "smeared" so that no leap second -// table is needed for interpretation. Range is from -// 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. -// By restricting to that range, we ensure that we can convert to -// and from RFC 3339 date strings. -// See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. // // # Examples // @@ -103,23 +105,26 @@ option objc_class_prefix = "GPB"; // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone -// is required, though only UTC (as indicated by "Z") is presently supported. +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). // // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past // 01:30 UTC on January 15, 2017. // // In JavaScript, one can convert a Date object to this format using the -// standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) // method. In Python, a standard `datetime.datetime` object can be converted -// to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) -// with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one -// can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( -// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime--) -// to obtain a formatter capable of generating timestamps in this format. +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// ) to obtain a formatter capable of generating timestamps in this format. // // message Timestamp { - // Represents seconds of UTC time since Unix epoch // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to // 9999-12-31T23:59:59Z inclusive. diff --git a/vendor/gotest.tools/LICENSE b/vendor/github.com/google/btree/LICENSE similarity index 100% rename from vendor/gotest.tools/LICENSE rename to vendor/github.com/google/btree/LICENSE diff --git a/vendor/github.com/google/btree/README.md b/vendor/github.com/google/btree/README.md new file mode 100644 index 0000000000000..6062a4dacd493 --- /dev/null +++ b/vendor/github.com/google/btree/README.md @@ -0,0 +1,12 @@ +# BTree implementation for Go + +![Travis CI Build Status](https://api.travis-ci.org/google/btree.svg?branch=master) + +This package provides an in-memory B-Tree implementation for Go, useful as +an ordered, mutable data structure. + +The API is based off of the wonderful +http://godoc.org/github.com/petar/GoLLRB/llrb, and is meant to allow btree to +act as a drop-in replacement for gollrb trees. + +See http://godoc.org/github.com/google/btree for documentation. diff --git a/vendor/github.com/google/btree/btree.go b/vendor/github.com/google/btree/btree.go new file mode 100644 index 0000000000000..b83acdbc6d3ab --- /dev/null +++ b/vendor/github.com/google/btree/btree.go @@ -0,0 +1,890 @@ +// Copyright 2014 Google Inc. +// +// 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. + +// Package btree implements in-memory B-Trees of arbitrary degree. +// +// btree implements an in-memory B-Tree for use as an ordered data structure. +// It is not meant for persistent storage solutions. +// +// It has a flatter structure than an equivalent red-black or other binary tree, +// which in some cases yields better memory usage and/or performance. +// See some discussion on the matter here: +// http://google-opensource.blogspot.com/2013/01/c-containers-that-save-memory-and-time.html +// Note, though, that this project is in no way related to the C++ B-Tree +// implementation written about there. +// +// Within this tree, each node contains a slice of items and a (possibly nil) +// slice of children. For basic numeric values or raw structs, this can cause +// efficiency differences when compared to equivalent C++ template code that +// stores values in arrays within the node: +// * Due to the overhead of storing values as interfaces (each +// value needs to be stored as the value itself, then 2 words for the +// interface pointing to that value and its type), resulting in higher +// memory use. +// * Since interfaces can point to values anywhere in memory, values are +// most likely not stored in contiguous blocks, resulting in a higher +// number of cache misses. +// These issues don't tend to matter, though, when working with strings or other +// heap-allocated structures, since C++-equivalent structures also must store +// pointers and also distribute their values across the heap. +// +// This implementation is designed to be a drop-in replacement to gollrb.LLRB +// trees, (http://github.com/petar/gollrb), an excellent and probably the most +// widely used ordered tree implementation in the Go ecosystem currently. +// Its functions, therefore, exactly mirror those of +// llrb.LLRB where possible. Unlike gollrb, though, we currently don't +// support storing multiple equivalent values. +package btree + +import ( + "fmt" + "io" + "sort" + "strings" + "sync" +) + +// Item represents a single object in the tree. +type Item interface { + // Less tests whether the current item is less than the given argument. + // + // This must provide a strict weak ordering. + // If !a.Less(b) && !b.Less(a), we treat this to mean a == b (i.e. we can only + // hold one of either a or b in the tree). + Less(than Item) bool +} + +const ( + DefaultFreeListSize = 32 +) + +var ( + nilItems = make(items, 16) + nilChildren = make(children, 16) +) + +// FreeList represents a free list of btree nodes. By default each +// BTree has its own FreeList, but multiple BTrees can share the same +// FreeList. +// Two Btrees using the same freelist are safe for concurrent write access. +type FreeList struct { + mu sync.Mutex + freelist []*node +} + +// NewFreeList creates a new free list. +// size is the maximum size of the returned free list. +func NewFreeList(size int) *FreeList { + return &FreeList{freelist: make([]*node, 0, size)} +} + +func (f *FreeList) newNode() (n *node) { + f.mu.Lock() + index := len(f.freelist) - 1 + if index < 0 { + f.mu.Unlock() + return new(node) + } + n = f.freelist[index] + f.freelist[index] = nil + f.freelist = f.freelist[:index] + f.mu.Unlock() + return +} + +// freeNode adds the given node to the list, returning true if it was added +// and false if it was discarded. +func (f *FreeList) freeNode(n *node) (out bool) { + f.mu.Lock() + if len(f.freelist) < cap(f.freelist) { + f.freelist = append(f.freelist, n) + out = true + } + f.mu.Unlock() + return +} + +// ItemIterator allows callers of Ascend* to iterate in-order over portions of +// the tree. When this function returns false, iteration will stop and the +// associated Ascend* function will immediately return. +type ItemIterator func(i Item) bool + +// New creates a new B-Tree with the given degree. +// +// New(2), for example, will create a 2-3-4 tree (each node contains 1-3 items +// and 2-4 children). +func New(degree int) *BTree { + return NewWithFreeList(degree, NewFreeList(DefaultFreeListSize)) +} + +// NewWithFreeList creates a new B-Tree that uses the given node free list. +func NewWithFreeList(degree int, f *FreeList) *BTree { + if degree <= 1 { + panic("bad degree") + } + return &BTree{ + degree: degree, + cow: ©OnWriteContext{freelist: f}, + } +} + +// items stores items in a node. +type items []Item + +// insertAt inserts a value into the given index, pushing all subsequent values +// forward. +func (s *items) insertAt(index int, item Item) { + *s = append(*s, nil) + if index < len(*s) { + copy((*s)[index+1:], (*s)[index:]) + } + (*s)[index] = item +} + +// removeAt removes a value at a given index, pulling all subsequent values +// back. +func (s *items) removeAt(index int) Item { + item := (*s)[index] + copy((*s)[index:], (*s)[index+1:]) + (*s)[len(*s)-1] = nil + *s = (*s)[:len(*s)-1] + return item +} + +// pop removes and returns the last element in the list. +func (s *items) pop() (out Item) { + index := len(*s) - 1 + out = (*s)[index] + (*s)[index] = nil + *s = (*s)[:index] + return +} + +// truncate truncates this instance at index so that it contains only the +// first index items. index must be less than or equal to length. +func (s *items) truncate(index int) { + var toClear items + *s, toClear = (*s)[:index], (*s)[index:] + for len(toClear) > 0 { + toClear = toClear[copy(toClear, nilItems):] + } +} + +// find returns the index where the given item should be inserted into this +// list. 'found' is true if the item already exists in the list at the given +// index. +func (s items) find(item Item) (index int, found bool) { + i := sort.Search(len(s), func(i int) bool { + return item.Less(s[i]) + }) + if i > 0 && !s[i-1].Less(item) { + return i - 1, true + } + return i, false +} + +// children stores child nodes in a node. +type children []*node + +// insertAt inserts a value into the given index, pushing all subsequent values +// forward. +func (s *children) insertAt(index int, n *node) { + *s = append(*s, nil) + if index < len(*s) { + copy((*s)[index+1:], (*s)[index:]) + } + (*s)[index] = n +} + +// removeAt removes a value at a given index, pulling all subsequent values +// back. +func (s *children) removeAt(index int) *node { + n := (*s)[index] + copy((*s)[index:], (*s)[index+1:]) + (*s)[len(*s)-1] = nil + *s = (*s)[:len(*s)-1] + return n +} + +// pop removes and returns the last element in the list. +func (s *children) pop() (out *node) { + index := len(*s) - 1 + out = (*s)[index] + (*s)[index] = nil + *s = (*s)[:index] + return +} + +// truncate truncates this instance at index so that it contains only the +// first index children. index must be less than or equal to length. +func (s *children) truncate(index int) { + var toClear children + *s, toClear = (*s)[:index], (*s)[index:] + for len(toClear) > 0 { + toClear = toClear[copy(toClear, nilChildren):] + } +} + +// node is an internal node in a tree. +// +// It must at all times maintain the invariant that either +// * len(children) == 0, len(items) unconstrained +// * len(children) == len(items) + 1 +type node struct { + items items + children children + cow *copyOnWriteContext +} + +func (n *node) mutableFor(cow *copyOnWriteContext) *node { + if n.cow == cow { + return n + } + out := cow.newNode() + if cap(out.items) >= len(n.items) { + out.items = out.items[:len(n.items)] + } else { + out.items = make(items, len(n.items), cap(n.items)) + } + copy(out.items, n.items) + // Copy children + if cap(out.children) >= len(n.children) { + out.children = out.children[:len(n.children)] + } else { + out.children = make(children, len(n.children), cap(n.children)) + } + copy(out.children, n.children) + return out +} + +func (n *node) mutableChild(i int) *node { + c := n.children[i].mutableFor(n.cow) + n.children[i] = c + return c +} + +// split splits the given node at the given index. The current node shrinks, +// and this function returns the item that existed at that index and a new node +// containing all items/children after it. +func (n *node) split(i int) (Item, *node) { + item := n.items[i] + next := n.cow.newNode() + next.items = append(next.items, n.items[i+1:]...) + n.items.truncate(i) + if len(n.children) > 0 { + next.children = append(next.children, n.children[i+1:]...) + n.children.truncate(i + 1) + } + return item, next +} + +// maybeSplitChild checks if a child should be split, and if so splits it. +// Returns whether or not a split occurred. +func (n *node) maybeSplitChild(i, maxItems int) bool { + if len(n.children[i].items) < maxItems { + return false + } + first := n.mutableChild(i) + item, second := first.split(maxItems / 2) + n.items.insertAt(i, item) + n.children.insertAt(i+1, second) + return true +} + +// insert inserts an item into the subtree rooted at this node, making sure +// no nodes in the subtree exceed maxItems items. Should an equivalent item be +// be found/replaced by insert, it will be returned. +func (n *node) insert(item Item, maxItems int) Item { + i, found := n.items.find(item) + if found { + out := n.items[i] + n.items[i] = item + return out + } + if len(n.children) == 0 { + n.items.insertAt(i, item) + return nil + } + if n.maybeSplitChild(i, maxItems) { + inTree := n.items[i] + switch { + case item.Less(inTree): + // no change, we want first split node + case inTree.Less(item): + i++ // we want second split node + default: + out := n.items[i] + n.items[i] = item + return out + } + } + return n.mutableChild(i).insert(item, maxItems) +} + +// get finds the given key in the subtree and returns it. +func (n *node) get(key Item) Item { + i, found := n.items.find(key) + if found { + return n.items[i] + } else if len(n.children) > 0 { + return n.children[i].get(key) + } + return nil +} + +// min returns the first item in the subtree. +func min(n *node) Item { + if n == nil { + return nil + } + for len(n.children) > 0 { + n = n.children[0] + } + if len(n.items) == 0 { + return nil + } + return n.items[0] +} + +// max returns the last item in the subtree. +func max(n *node) Item { + if n == nil { + return nil + } + for len(n.children) > 0 { + n = n.children[len(n.children)-1] + } + if len(n.items) == 0 { + return nil + } + return n.items[len(n.items)-1] +} + +// toRemove details what item to remove in a node.remove call. +type toRemove int + +const ( + removeItem toRemove = iota // removes the given item + removeMin // removes smallest item in the subtree + removeMax // removes largest item in the subtree +) + +// remove removes an item from the subtree rooted at this node. +func (n *node) remove(item Item, minItems int, typ toRemove) Item { + var i int + var found bool + switch typ { + case removeMax: + if len(n.children) == 0 { + return n.items.pop() + } + i = len(n.items) + case removeMin: + if len(n.children) == 0 { + return n.items.removeAt(0) + } + i = 0 + case removeItem: + i, found = n.items.find(item) + if len(n.children) == 0 { + if found { + return n.items.removeAt(i) + } + return nil + } + default: + panic("invalid type") + } + // If we get to here, we have children. + if len(n.children[i].items) <= minItems { + return n.growChildAndRemove(i, item, minItems, typ) + } + child := n.mutableChild(i) + // Either we had enough items to begin with, or we've done some + // merging/stealing, because we've got enough now and we're ready to return + // stuff. + if found { + // The item exists at index 'i', and the child we've selected can give us a + // predecessor, since if we've gotten here it's got > minItems items in it. + out := n.items[i] + // We use our special-case 'remove' call with typ=maxItem to pull the + // predecessor of item i (the rightmost leaf of our immediate left child) + // and set it into where we pulled the item from. + n.items[i] = child.remove(nil, minItems, removeMax) + return out + } + // Final recursive call. Once we're here, we know that the item isn't in this + // node and that the child is big enough to remove from. + return child.remove(item, minItems, typ) +} + +// growChildAndRemove grows child 'i' to make sure it's possible to remove an +// item from it while keeping it at minItems, then calls remove to actually +// remove it. +// +// Most documentation says we have to do two sets of special casing: +// 1) item is in this node +// 2) item is in child +// In both cases, we need to handle the two subcases: +// A) node has enough values that it can spare one +// B) node doesn't have enough values +// For the latter, we have to check: +// a) left sibling has node to spare +// b) right sibling has node to spare +// c) we must merge +// To simplify our code here, we handle cases #1 and #2 the same: +// If a node doesn't have enough items, we make sure it does (using a,b,c). +// We then simply redo our remove call, and the second time (regardless of +// whether we're in case 1 or 2), we'll have enough items and can guarantee +// that we hit case A. +func (n *node) growChildAndRemove(i int, item Item, minItems int, typ toRemove) Item { + if i > 0 && len(n.children[i-1].items) > minItems { + // Steal from left child + child := n.mutableChild(i) + stealFrom := n.mutableChild(i - 1) + stolenItem := stealFrom.items.pop() + child.items.insertAt(0, n.items[i-1]) + n.items[i-1] = stolenItem + if len(stealFrom.children) > 0 { + child.children.insertAt(0, stealFrom.children.pop()) + } + } else if i < len(n.items) && len(n.children[i+1].items) > minItems { + // steal from right child + child := n.mutableChild(i) + stealFrom := n.mutableChild(i + 1) + stolenItem := stealFrom.items.removeAt(0) + child.items = append(child.items, n.items[i]) + n.items[i] = stolenItem + if len(stealFrom.children) > 0 { + child.children = append(child.children, stealFrom.children.removeAt(0)) + } + } else { + if i >= len(n.items) { + i-- + } + child := n.mutableChild(i) + // merge with right child + mergeItem := n.items.removeAt(i) + mergeChild := n.children.removeAt(i + 1) + child.items = append(child.items, mergeItem) + child.items = append(child.items, mergeChild.items...) + child.children = append(child.children, mergeChild.children...) + n.cow.freeNode(mergeChild) + } + return n.remove(item, minItems, typ) +} + +type direction int + +const ( + descend = direction(-1) + ascend = direction(+1) +) + +// iterate provides a simple method for iterating over elements in the tree. +// +// When ascending, the 'start' should be less than 'stop' and when descending, +// the 'start' should be greater than 'stop'. Setting 'includeStart' to true +// will force the iterator to include the first item when it equals 'start', +// thus creating a "greaterOrEqual" or "lessThanEqual" rather than just a +// "greaterThan" or "lessThan" queries. +func (n *node) iterate(dir direction, start, stop Item, includeStart bool, hit bool, iter ItemIterator) (bool, bool) { + var ok, found bool + var index int + switch dir { + case ascend: + if start != nil { + index, _ = n.items.find(start) + } + for i := index; i < len(n.items); i++ { + if len(n.children) > 0 { + if hit, ok = n.children[i].iterate(dir, start, stop, includeStart, hit, iter); !ok { + return hit, false + } + } + if !includeStart && !hit && start != nil && !start.Less(n.items[i]) { + hit = true + continue + } + hit = true + if stop != nil && !n.items[i].Less(stop) { + return hit, false + } + if !iter(n.items[i]) { + return hit, false + } + } + if len(n.children) > 0 { + if hit, ok = n.children[len(n.children)-1].iterate(dir, start, stop, includeStart, hit, iter); !ok { + return hit, false + } + } + case descend: + if start != nil { + index, found = n.items.find(start) + if !found { + index = index - 1 + } + } else { + index = len(n.items) - 1 + } + for i := index; i >= 0; i-- { + if start != nil && !n.items[i].Less(start) { + if !includeStart || hit || start.Less(n.items[i]) { + continue + } + } + if len(n.children) > 0 { + if hit, ok = n.children[i+1].iterate(dir, start, stop, includeStart, hit, iter); !ok { + return hit, false + } + } + if stop != nil && !stop.Less(n.items[i]) { + return hit, false // continue + } + hit = true + if !iter(n.items[i]) { + return hit, false + } + } + if len(n.children) > 0 { + if hit, ok = n.children[0].iterate(dir, start, stop, includeStart, hit, iter); !ok { + return hit, false + } + } + } + return hit, true +} + +// Used for testing/debugging purposes. +func (n *node) print(w io.Writer, level int) { + fmt.Fprintf(w, "%sNODE:%v\n", strings.Repeat(" ", level), n.items) + for _, c := range n.children { + c.print(w, level+1) + } +} + +// BTree is an implementation of a B-Tree. +// +// BTree stores Item instances in an ordered structure, allowing easy insertion, +// removal, and iteration. +// +// Write operations are not safe for concurrent mutation by multiple +// goroutines, but Read operations are. +type BTree struct { + degree int + length int + root *node + cow *copyOnWriteContext +} + +// copyOnWriteContext pointers determine node ownership... a tree with a write +// context equivalent to a node's write context is allowed to modify that node. +// A tree whose write context does not match a node's is not allowed to modify +// it, and must create a new, writable copy (IE: it's a Clone). +// +// When doing any write operation, we maintain the invariant that the current +// node's context is equal to the context of the tree that requested the write. +// We do this by, before we descend into any node, creating a copy with the +// correct context if the contexts don't match. +// +// Since the node we're currently visiting on any write has the requesting +// tree's context, that node is modifiable in place. Children of that node may +// not share context, but before we descend into them, we'll make a mutable +// copy. +type copyOnWriteContext struct { + freelist *FreeList +} + +// Clone clones the btree, lazily. Clone should not be called concurrently, +// but the original tree (t) and the new tree (t2) can be used concurrently +// once the Clone call completes. +// +// The internal tree structure of b is marked read-only and shared between t and +// t2. Writes to both t and t2 use copy-on-write logic, creating new nodes +// whenever one of b's original nodes would have been modified. Read operations +// should have no performance degredation. Write operations for both t and t2 +// will initially experience minor slow-downs caused by additional allocs and +// copies due to the aforementioned copy-on-write logic, but should converge to +// the original performance characteristics of the original tree. +func (t *BTree) Clone() (t2 *BTree) { + // Create two entirely new copy-on-write contexts. + // This operation effectively creates three trees: + // the original, shared nodes (old b.cow) + // the new b.cow nodes + // the new out.cow nodes + cow1, cow2 := *t.cow, *t.cow + out := *t + t.cow = &cow1 + out.cow = &cow2 + return &out +} + +// maxItems returns the max number of items to allow per node. +func (t *BTree) maxItems() int { + return t.degree*2 - 1 +} + +// minItems returns the min number of items to allow per node (ignored for the +// root node). +func (t *BTree) minItems() int { + return t.degree - 1 +} + +func (c *copyOnWriteContext) newNode() (n *node) { + n = c.freelist.newNode() + n.cow = c + return +} + +type freeType int + +const ( + ftFreelistFull freeType = iota // node was freed (available for GC, not stored in freelist) + ftStored // node was stored in the freelist for later use + ftNotOwned // node was ignored by COW, since it's owned by another one +) + +// freeNode frees a node within a given COW context, if it's owned by that +// context. It returns what happened to the node (see freeType const +// documentation). +func (c *copyOnWriteContext) freeNode(n *node) freeType { + if n.cow == c { + // clear to allow GC + n.items.truncate(0) + n.children.truncate(0) + n.cow = nil + if c.freelist.freeNode(n) { + return ftStored + } else { + return ftFreelistFull + } + } else { + return ftNotOwned + } +} + +// ReplaceOrInsert adds the given item to the tree. If an item in the tree +// already equals the given one, it is removed from the tree and returned. +// Otherwise, nil is returned. +// +// nil cannot be added to the tree (will panic). +func (t *BTree) ReplaceOrInsert(item Item) Item { + if item == nil { + panic("nil item being added to BTree") + } + if t.root == nil { + t.root = t.cow.newNode() + t.root.items = append(t.root.items, item) + t.length++ + return nil + } else { + t.root = t.root.mutableFor(t.cow) + if len(t.root.items) >= t.maxItems() { + item2, second := t.root.split(t.maxItems() / 2) + oldroot := t.root + t.root = t.cow.newNode() + t.root.items = append(t.root.items, item2) + t.root.children = append(t.root.children, oldroot, second) + } + } + out := t.root.insert(item, t.maxItems()) + if out == nil { + t.length++ + } + return out +} + +// Delete removes an item equal to the passed in item from the tree, returning +// it. If no such item exists, returns nil. +func (t *BTree) Delete(item Item) Item { + return t.deleteItem(item, removeItem) +} + +// DeleteMin removes the smallest item in the tree and returns it. +// If no such item exists, returns nil. +func (t *BTree) DeleteMin() Item { + return t.deleteItem(nil, removeMin) +} + +// DeleteMax removes the largest item in the tree and returns it. +// If no such item exists, returns nil. +func (t *BTree) DeleteMax() Item { + return t.deleteItem(nil, removeMax) +} + +func (t *BTree) deleteItem(item Item, typ toRemove) Item { + if t.root == nil || len(t.root.items) == 0 { + return nil + } + t.root = t.root.mutableFor(t.cow) + out := t.root.remove(item, t.minItems(), typ) + if len(t.root.items) == 0 && len(t.root.children) > 0 { + oldroot := t.root + t.root = t.root.children[0] + t.cow.freeNode(oldroot) + } + if out != nil { + t.length-- + } + return out +} + +// AscendRange calls the iterator for every value in the tree within the range +// [greaterOrEqual, lessThan), until iterator returns false. +func (t *BTree) AscendRange(greaterOrEqual, lessThan Item, iterator ItemIterator) { + if t.root == nil { + return + } + t.root.iterate(ascend, greaterOrEqual, lessThan, true, false, iterator) +} + +// AscendLessThan calls the iterator for every value in the tree within the range +// [first, pivot), until iterator returns false. +func (t *BTree) AscendLessThan(pivot Item, iterator ItemIterator) { + if t.root == nil { + return + } + t.root.iterate(ascend, nil, pivot, false, false, iterator) +} + +// AscendGreaterOrEqual calls the iterator for every value in the tree within +// the range [pivot, last], until iterator returns false. +func (t *BTree) AscendGreaterOrEqual(pivot Item, iterator ItemIterator) { + if t.root == nil { + return + } + t.root.iterate(ascend, pivot, nil, true, false, iterator) +} + +// Ascend calls the iterator for every value in the tree within the range +// [first, last], until iterator returns false. +func (t *BTree) Ascend(iterator ItemIterator) { + if t.root == nil { + return + } + t.root.iterate(ascend, nil, nil, false, false, iterator) +} + +// DescendRange calls the iterator for every value in the tree within the range +// [lessOrEqual, greaterThan), until iterator returns false. +func (t *BTree) DescendRange(lessOrEqual, greaterThan Item, iterator ItemIterator) { + if t.root == nil { + return + } + t.root.iterate(descend, lessOrEqual, greaterThan, true, false, iterator) +} + +// DescendLessOrEqual calls the iterator for every value in the tree within the range +// [pivot, first], until iterator returns false. +func (t *BTree) DescendLessOrEqual(pivot Item, iterator ItemIterator) { + if t.root == nil { + return + } + t.root.iterate(descend, pivot, nil, true, false, iterator) +} + +// DescendGreaterThan calls the iterator for every value in the tree within +// the range [last, pivot), until iterator returns false. +func (t *BTree) DescendGreaterThan(pivot Item, iterator ItemIterator) { + if t.root == nil { + return + } + t.root.iterate(descend, nil, pivot, false, false, iterator) +} + +// Descend calls the iterator for every value in the tree within the range +// [last, first], until iterator returns false. +func (t *BTree) Descend(iterator ItemIterator) { + if t.root == nil { + return + } + t.root.iterate(descend, nil, nil, false, false, iterator) +} + +// Get looks for the key item in the tree, returning it. It returns nil if +// unable to find that item. +func (t *BTree) Get(key Item) Item { + if t.root == nil { + return nil + } + return t.root.get(key) +} + +// Min returns the smallest item in the tree, or nil if the tree is empty. +func (t *BTree) Min() Item { + return min(t.root) +} + +// Max returns the largest item in the tree, or nil if the tree is empty. +func (t *BTree) Max() Item { + return max(t.root) +} + +// Has returns true if the given key is in the tree. +func (t *BTree) Has(key Item) bool { + return t.Get(key) != nil +} + +// Len returns the number of items currently in the tree. +func (t *BTree) Len() int { + return t.length +} + +// Clear removes all items from the btree. If addNodesToFreelist is true, +// t's nodes are added to its freelist as part of this call, until the freelist +// is full. Otherwise, the root node is simply dereferenced and the subtree +// left to Go's normal GC processes. +// +// This can be much faster +// than calling Delete on all elements, because that requires finding/removing +// each element in the tree and updating the tree accordingly. It also is +// somewhat faster than creating a new tree to replace the old one, because +// nodes from the old tree are reclaimed into the freelist for use by the new +// one, instead of being lost to the garbage collector. +// +// This call takes: +// O(1): when addNodesToFreelist is false, this is a single operation. +// O(1): when the freelist is already full, it breaks out immediately +// O(freelist size): when the freelist is empty and the nodes are all owned +// by this tree, nodes are added to the freelist until full. +// O(tree size): when all nodes are owned by another tree, all nodes are +// iterated over looking for nodes to add to the freelist, and due to +// ownership, none are. +func (t *BTree) Clear(addNodesToFreelist bool) { + if t.root != nil && addNodesToFreelist { + t.root.reset(t.cow) + } + t.root, t.length = nil, 0 +} + +// reset returns a subtree to the freelist. It breaks out immediately if the +// freelist is full, since the only benefit of iterating is to fill that +// freelist up. Returns true if parent reset call should continue. +func (n *node) reset(c *copyOnWriteContext) bool { + for _, child := range n.children { + if !child.reset(c) { + return false + } + } + return c.freeNode(n) != ftFreelistFull +} + +// Int implements the Item interface for integers. +type Int int + +// Less returns true if int(a) < int(b). +func (a Int) Less(b Item) bool { + return a < b.(Int) +} diff --git a/vendor/github.com/google/btree/go.mod b/vendor/github.com/google/btree/go.mod new file mode 100644 index 0000000000000..fe4d5ca17b377 --- /dev/null +++ b/vendor/github.com/google/btree/go.mod @@ -0,0 +1,17 @@ +// Copyright 2014 Google Inc. +// +// 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. + +module github.com/google/btree + +go 1.12 diff --git a/vendor/github.com/google/shlex/go.mod b/vendor/github.com/google/shlex/go.mod new file mode 100644 index 0000000000000..0ab3bce7f12a2 --- /dev/null +++ b/vendor/github.com/google/shlex/go.mod @@ -0,0 +1,3 @@ +module github.com/google/shlex + +go 1.13 diff --git a/vendor/github.com/google/shlex/shlex.go b/vendor/github.com/google/shlex/shlex.go index 3cb37b7e481e9..d98308bce3802 100644 --- a/vendor/github.com/google/shlex/shlex.go +++ b/vendor/github.com/google/shlex/shlex.go @@ -253,7 +253,6 @@ func (t *Tokenizer) scanStream() (*Token, error) { } case spaceRuneClass: { - t.input.UnreadRune() token := &Token{ tokenType: tokenType, value: string(value)} diff --git a/vendor/github.com/pborman/uuid/LICENSE b/vendor/github.com/google/uuid/LICENSE similarity index 100% rename from vendor/github.com/pborman/uuid/LICENSE rename to vendor/github.com/google/uuid/LICENSE diff --git a/vendor/github.com/google/uuid/README.md b/vendor/github.com/google/uuid/README.md new file mode 100644 index 0000000000000..9d92c11f16f59 --- /dev/null +++ b/vendor/github.com/google/uuid/README.md @@ -0,0 +1,19 @@ +# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master) +The uuid package generates and inspects UUIDs based on +[RFC 4122](http://tools.ietf.org/html/rfc4122) +and DCE 1.1: Authentication and Security Services. + +This package is based on the github.com/pborman/uuid package (previously named +code.google.com/p/go-uuid). It differs from these earlier packages in that +a UUID is a 16 byte array rather than a byte slice. One loss due to this +change is the ability to represent an invalid UUID (vs a NIL UUID). + +###### Install +`go get github.com/google/uuid` + +###### Documentation +[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid) + +Full `go doc` style documentation for the package can be viewed online without +installing this package by using the GoDoc site here: +http://godoc.org/github.com/google/uuid diff --git a/vendor/github.com/google/uuid/dce.go b/vendor/github.com/google/uuid/dce.go new file mode 100644 index 0000000000000..fa820b9d3092b --- /dev/null +++ b/vendor/github.com/google/uuid/dce.go @@ -0,0 +1,80 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "fmt" + "os" +) + +// A Domain represents a Version 2 domain +type Domain byte + +// Domain constants for DCE Security (Version 2) UUIDs. +const ( + Person = Domain(0) + Group = Domain(1) + Org = Domain(2) +) + +// NewDCESecurity returns a DCE Security (Version 2) UUID. +// +// The domain should be one of Person, Group or Org. +// On a POSIX system the id should be the users UID for the Person +// domain and the users GID for the Group. The meaning of id for +// the domain Org or on non-POSIX systems is site defined. +// +// For a given domain/id pair the same token may be returned for up to +// 7 minutes and 10 seconds. +func NewDCESecurity(domain Domain, id uint32) (UUID, error) { + uuid, err := NewUUID() + if err == nil { + uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 + uuid[9] = byte(domain) + binary.BigEndian.PutUint32(uuid[0:], id) + } + return uuid, err +} + +// NewDCEPerson returns a DCE Security (Version 2) UUID in the person +// domain with the id returned by os.Getuid. +// +// NewDCESecurity(Person, uint32(os.Getuid())) +func NewDCEPerson() (UUID, error) { + return NewDCESecurity(Person, uint32(os.Getuid())) +} + +// NewDCEGroup returns a DCE Security (Version 2) UUID in the group +// domain with the id returned by os.Getgid. +// +// NewDCESecurity(Group, uint32(os.Getgid())) +func NewDCEGroup() (UUID, error) { + return NewDCESecurity(Group, uint32(os.Getgid())) +} + +// Domain returns the domain for a Version 2 UUID. Domains are only defined +// for Version 2 UUIDs. +func (uuid UUID) Domain() Domain { + return Domain(uuid[9]) +} + +// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2 +// UUIDs. +func (uuid UUID) ID() uint32 { + return binary.BigEndian.Uint32(uuid[0:4]) +} + +func (d Domain) String() string { + switch d { + case Person: + return "Person" + case Group: + return "Group" + case Org: + return "Org" + } + return fmt.Sprintf("Domain%d", int(d)) +} diff --git a/vendor/github.com/google/uuid/doc.go b/vendor/github.com/google/uuid/doc.go new file mode 100644 index 0000000000000..5b8a4b9af8ce3 --- /dev/null +++ b/vendor/github.com/google/uuid/doc.go @@ -0,0 +1,12 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package uuid generates and inspects UUIDs. +// +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security +// Services. +// +// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to +// maps or compared directly. +package uuid diff --git a/vendor/github.com/google/uuid/go.mod b/vendor/github.com/google/uuid/go.mod new file mode 100644 index 0000000000000..fc84cd79d4c79 --- /dev/null +++ b/vendor/github.com/google/uuid/go.mod @@ -0,0 +1 @@ +module github.com/google/uuid diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go new file mode 100644 index 0000000000000..b174616315118 --- /dev/null +++ b/vendor/github.com/google/uuid/hash.go @@ -0,0 +1,53 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "crypto/md5" + "crypto/sha1" + "hash" +) + +// Well known namespace IDs and UUIDs +var ( + NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) + Nil UUID // empty UUID, all zeros +) + +// NewHash returns a new UUID derived from the hash of space concatenated with +// data generated by h. The hash should be at least 16 byte in length. The +// first 16 bytes of the hash are used to form the UUID. The version of the +// UUID will be the lower 4 bits of version. NewHash is used to implement +// NewMD5 and NewSHA1. +func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { + h.Reset() + h.Write(space[:]) + h.Write(data) + s := h.Sum(nil) + var uuid UUID + copy(uuid[:], s) + uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant + return uuid +} + +// NewMD5 returns a new MD5 (Version 3) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(md5.New(), space, data, 3) +func NewMD5(space UUID, data []byte) UUID { + return NewHash(md5.New(), space, data, 3) +} + +// NewSHA1 returns a new SHA1 (Version 5) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(sha1.New(), space, data, 5) +func NewSHA1(space UUID, data []byte) UUID { + return NewHash(sha1.New(), space, data, 5) +} diff --git a/vendor/github.com/google/uuid/marshal.go b/vendor/github.com/google/uuid/marshal.go new file mode 100644 index 0000000000000..7f9e0c6c0e385 --- /dev/null +++ b/vendor/github.com/google/uuid/marshal.go @@ -0,0 +1,37 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "fmt" + +// MarshalText implements encoding.TextMarshaler. +func (uuid UUID) MarshalText() ([]byte, error) { + var js [36]byte + encodeHex(js[:], uuid) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (uuid *UUID) UnmarshalText(data []byte) error { + id, err := ParseBytes(data) + if err == nil { + *uuid = id + } + return err +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (uuid UUID) MarshalBinary() ([]byte, error) { + return uuid[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (uuid *UUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(uuid[:], data) + return nil +} diff --git a/vendor/github.com/google/uuid/node.go b/vendor/github.com/google/uuid/node.go new file mode 100644 index 0000000000000..d651a2b0619fa --- /dev/null +++ b/vendor/github.com/google/uuid/node.go @@ -0,0 +1,90 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "sync" +) + +var ( + nodeMu sync.Mutex + ifname string // name of interface being used + nodeID [6]byte // hardware for version 1 UUIDs + zeroID [6]byte // nodeID with only 0's +) + +// NodeInterface returns the name of the interface from which the NodeID was +// derived. The interface "user" is returned if the NodeID was set by +// SetNodeID. +func NodeInterface() string { + defer nodeMu.Unlock() + nodeMu.Lock() + return ifname +} + +// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. +// If name is "" then the first usable interface found will be used or a random +// Node ID will be generated. If a named interface cannot be found then false +// is returned. +// +// SetNodeInterface never fails when name is "". +func SetNodeInterface(name string) bool { + defer nodeMu.Unlock() + nodeMu.Lock() + return setNodeInterface(name) +} + +func setNodeInterface(name string) bool { + iname, addr := getHardwareInterface(name) // null implementation for js + if iname != "" && addr != nil { + ifname = iname + copy(nodeID[:], addr) + return true + } + + // We found no interfaces with a valid hardware address. If name + // does not specify a specific interface generate a random Node ID + // (section 4.1.6) + if name == "" { + ifname = "random" + randomBits(nodeID[:]) + return true + } + return false +} + +// NodeID returns a slice of a copy of the current Node ID, setting the Node ID +// if not already set. +func NodeID() []byte { + defer nodeMu.Unlock() + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + nid := nodeID + return nid[:] +} + +// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes +// of id are used. If id is less than 6 bytes then false is returned and the +// Node ID is not set. +func SetNodeID(id []byte) bool { + if len(id) < 6 { + return false + } + defer nodeMu.Unlock() + nodeMu.Lock() + copy(nodeID[:], id) + ifname = "user" + return true +} + +// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is +// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) NodeID() []byte { + var node [6]byte + copy(node[:], uuid[10:]) + return node[:] +} diff --git a/vendor/github.com/google/uuid/node_js.go b/vendor/github.com/google/uuid/node_js.go new file mode 100644 index 0000000000000..24b78edc90710 --- /dev/null +++ b/vendor/github.com/google/uuid/node_js.go @@ -0,0 +1,12 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build js + +package uuid + +// getHardwareInterface returns nil values for the JS version of the code. +// This remvoves the "net" dependency, because it is not used in the browser. +// Using the "net" library inflates the size of the transpiled JS code by 673k bytes. +func getHardwareInterface(name string) (string, []byte) { return "", nil } diff --git a/vendor/github.com/google/uuid/node_net.go b/vendor/github.com/google/uuid/node_net.go new file mode 100644 index 0000000000000..0cbbcddbd6e81 --- /dev/null +++ b/vendor/github.com/google/uuid/node_net.go @@ -0,0 +1,33 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !js + +package uuid + +import "net" + +var interfaces []net.Interface // cached list of interfaces + +// getHardwareInterface returns the name and hardware address of interface name. +// If name is "" then the name and hardware address of one of the system's +// interfaces is returned. If no interfaces are found (name does not exist or +// there are no interfaces) then "", nil is returned. +// +// Only addresses of at least 6 bytes are returned. +func getHardwareInterface(name string) (string, []byte) { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil { + return "", nil + } + } + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + return ifs.Name, ifs.HardwareAddr + } + } + return "", nil +} diff --git a/vendor/github.com/google/uuid/sql.go b/vendor/github.com/google/uuid/sql.go new file mode 100644 index 0000000000000..f326b54db37a6 --- /dev/null +++ b/vendor/github.com/google/uuid/sql.go @@ -0,0 +1,59 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "database/sql/driver" + "fmt" +) + +// Scan implements sql.Scanner so UUIDs can be read from databases transparently +// Currently, database types that map to string and []byte are supported. Please +// consult database-specific driver documentation for matching types. +func (uuid *UUID) Scan(src interface{}) error { + switch src := src.(type) { + case nil: + return nil + + case string: + // if an empty UUID comes from a table, we return a null UUID + if src == "" { + return nil + } + + // see Parse for required string format + u, err := Parse(src) + if err != nil { + return fmt.Errorf("Scan: %v", err) + } + + *uuid = u + + case []byte: + // if an empty UUID comes from a table, we return a null UUID + if len(src) == 0 { + return nil + } + + // assumes a simple slice of bytes if 16 bytes + // otherwise attempts to parse + if len(src) != 16 { + return uuid.Scan(string(src)) + } + copy((*uuid)[:], src) + + default: + return fmt.Errorf("Scan: unable to scan type %T into UUID", src) + } + + return nil +} + +// Value implements sql.Valuer so that UUIDs can be written to databases +// transparently. Currently, UUIDs map to strings. Please consult +// database-specific driver documentation for matching types. +func (uuid UUID) Value() (driver.Value, error) { + return uuid.String(), nil +} diff --git a/vendor/github.com/google/uuid/time.go b/vendor/github.com/google/uuid/time.go new file mode 100644 index 0000000000000..e6ef06cdc87aa --- /dev/null +++ b/vendor/github.com/google/uuid/time.go @@ -0,0 +1,123 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "sync" + "time" +) + +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct +// 1582. +type Time int64 + +const ( + lillian = 2299160 // Julian day of 15 Oct 1582 + unix = 2440587 // Julian day of 1 Jan 1970 + epoch = unix - lillian // Days between epochs + g1582 = epoch * 86400 // seconds between epochs + g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs +) + +var ( + timeMu sync.Mutex + lasttime uint64 // last time we returned + clockSeq uint16 // clock sequence for this run + + timeNow = time.Now // for testing +) + +// UnixTime converts t the number of seconds and nanoseconds using the Unix +// epoch of 1 Jan 1970. +func (t Time) UnixTime() (sec, nsec int64) { + sec = int64(t - g1582ns100) + nsec = (sec % 10000000) * 100 + sec /= 10000000 + return sec, nsec +} + +// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and +// clock sequence as well as adjusting the clock sequence as needed. An error +// is returned if the current time cannot be determined. +func GetTime() (Time, uint16, error) { + defer timeMu.Unlock() + timeMu.Lock() + return getTime() +} + +func getTime() (Time, uint16, error) { + t := timeNow() + + // If we don't have a clock sequence already, set one. + if clockSeq == 0 { + setClockSequence(-1) + } + now := uint64(t.UnixNano()/100) + g1582ns100 + + // If time has gone backwards with this clock sequence then we + // increment the clock sequence + if now <= lasttime { + clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000 + } + lasttime = now + return Time(now), clockSeq, nil +} + +// ClockSequence returns the current clock sequence, generating one if not +// already set. The clock sequence is only used for Version 1 UUIDs. +// +// The uuid package does not use global static storage for the clock sequence or +// the last time a UUID was generated. Unless SetClockSequence is used, a new +// random clock sequence is generated the first time a clock sequence is +// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) +func ClockSequence() int { + defer timeMu.Unlock() + timeMu.Lock() + return clockSequence() +} + +func clockSequence() int { + if clockSeq == 0 { + setClockSequence(-1) + } + return int(clockSeq & 0x3fff) +} + +// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to +// -1 causes a new sequence to be generated. +func SetClockSequence(seq int) { + defer timeMu.Unlock() + timeMu.Lock() + setClockSequence(seq) +} + +func setClockSequence(seq int) { + if seq == -1 { + var b [2]byte + randomBits(b[:]) // clock sequence + seq = int(b[0])<<8 | int(b[1]) + } + oldSeq := clockSeq + clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant + if oldSeq != clockSeq { + lasttime = 0 + } +} + +// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in +// uuid. The time is only defined for version 1 and 2 UUIDs. +func (uuid UUID) Time() Time { + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + return Time(time) +} + +// ClockSequence returns the clock sequence encoded in uuid. +// The clock sequence is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) ClockSequence() int { + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff +} diff --git a/vendor/github.com/google/uuid/util.go b/vendor/github.com/google/uuid/util.go new file mode 100644 index 0000000000000..5ea6c737806e6 --- /dev/null +++ b/vendor/github.com/google/uuid/util.go @@ -0,0 +1,43 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// randomBits completely fills slice b with random data. +func randomBits(b []byte) { + if _, err := io.ReadFull(rander, b); err != nil { + panic(err.Error()) // rand should never fail + } +} + +// xvalues returns the value of a byte as a hexadecimal digit or 255. +var xvalues = [256]byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +} + +// xtob converts hex characters x1 and x2 into a byte. +func xtob(x1, x2 byte) (byte, bool) { + b1 := xvalues[x1] + b2 := xvalues[x2] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go new file mode 100644 index 0000000000000..524404cc5227b --- /dev/null +++ b/vendor/github.com/google/uuid/uuid.go @@ -0,0 +1,245 @@ +// Copyright 2018 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "errors" + "fmt" + "io" + "strings" +) + +// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC +// 4122. +type UUID [16]byte + +// A Version represents a UUID's version. +type Version byte + +// A Variant represents a UUID's variant. +type Variant byte + +// Constants returned by Variant. +const ( + Invalid = Variant(iota) // Invalid UUID + RFC4122 // The variant specified in RFC4122 + Reserved // Reserved, NCS backward compatibility. + Microsoft // Reserved, Microsoft Corporation backward compatibility. + Future // Reserved for future definition. +) + +var rander = rand.Reader // random function + +// Parse decodes s into a UUID or returns an error. Both the standard UUID +// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the +// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex +// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. +func Parse(s string) (UUID, error) { + var uuid UUID + switch len(s) { + // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36: + + // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: + if strings.ToLower(s[:9]) != "urn:uuid:" { + return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) + } + s = s[9:] + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + case 36 + 2: + s = s[1:] + + // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + case 32: + var ok bool + for i := range uuid { + uuid[i], ok = xtob(s[i*2], s[i*2+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) + } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + v, ok := xtob(s[x], s[x+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + uuid[i] = v + } + return uuid, nil +} + +// ParseBytes is like Parse, except it parses a byte slice instead of a string. +func ParseBytes(b []byte) (UUID, error) { + var uuid UUID + switch len(b) { + case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { + return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) + } + b = b[9:] + case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + b = b[1:] + case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + var ok bool + for i := 0; i < 32; i += 2 { + uuid[i/2], ok = xtob(b[i], b[i+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, fmt.Errorf("invalid UUID length: %d", len(b)) + } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + v, ok := xtob(b[x], b[x+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + uuid[i] = v + } + return uuid, nil +} + +// MustParse is like Parse but panics if the string cannot be parsed. +// It simplifies safe initialization of global variables holding compiled UUIDs. +func MustParse(s string) UUID { + uuid, err := Parse(s) + if err != nil { + panic(`uuid: Parse(` + s + `): ` + err.Error()) + } + return uuid +} + +// FromBytes creates a new UUID from a byte slice. Returns an error if the slice +// does not have a length of 16. The bytes are copied from the slice. +func FromBytes(b []byte) (uuid UUID, err error) { + err = uuid.UnmarshalBinary(b) + return uuid, err +} + +// Must returns uuid if err is nil and panics otherwise. +func Must(uuid UUID, err error) UUID { + if err != nil { + panic(err) + } + return uuid +} + +// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// , or "" if uuid is invalid. +func (uuid UUID) String() string { + var buf [36]byte + encodeHex(buf[:], uuid) + return string(buf[:]) +} + +// URN returns the RFC 2141 URN form of uuid, +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. +func (uuid UUID) URN() string { + var buf [36 + 9]byte + copy(buf[:], "urn:uuid:") + encodeHex(buf[9:], uuid) + return string(buf[:]) +} + +func encodeHex(dst []byte, uuid UUID) { + hex.Encode(dst, uuid[:4]) + dst[8] = '-' + hex.Encode(dst[9:13], uuid[4:6]) + dst[13] = '-' + hex.Encode(dst[14:18], uuid[6:8]) + dst[18] = '-' + hex.Encode(dst[19:23], uuid[8:10]) + dst[23] = '-' + hex.Encode(dst[24:], uuid[10:]) +} + +// Variant returns the variant encoded in uuid. +func (uuid UUID) Variant() Variant { + switch { + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + default: + return Reserved + } +} + +// Version returns the version of uuid. +func (uuid UUID) Version() Version { + return Version(uuid[6] >> 4) +} + +func (v Version) String() string { + if v > 15 { + return fmt.Sprintf("BAD_VERSION_%d", v) + } + return fmt.Sprintf("VERSION_%d", v) +} + +func (v Variant) String() string { + switch v { + case RFC4122: + return "RFC4122" + case Reserved: + return "Reserved" + case Microsoft: + return "Microsoft" + case Future: + return "Future" + case Invalid: + return "Invalid" + } + return fmt.Sprintf("BadVariant%d", int(v)) +} + +// SetRand sets the random number generator to r, which implements io.Reader. +// If r.Read returns an error when the package requests random data then +// a panic will be issued. +// +// Calling SetRand with nil sets the random number generator to the default +// generator. +func SetRand(r io.Reader) { + if r == nil { + rander = rand.Reader + return + } + rander = r +} diff --git a/vendor/github.com/google/uuid/version1.go b/vendor/github.com/google/uuid/version1.go new file mode 100644 index 0000000000000..199a1ac65403e --- /dev/null +++ b/vendor/github.com/google/uuid/version1.go @@ -0,0 +1,44 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" +) + +// NewUUID returns a Version 1 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewUUID returns nil. If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewUUID returns nil and an error. +// +// In most cases, New should be used. +func NewUUID() (UUID, error) { + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + nodeMu.Unlock() + + var uuid UUID + now, seq, err := GetTime() + if err != nil { + return uuid, err + } + + timeLow := uint32(now & 0xffffffff) + timeMid := uint16((now >> 32) & 0xffff) + timeHi := uint16((now >> 48) & 0x0fff) + timeHi |= 0x1000 // Version 1 + + binary.BigEndian.PutUint32(uuid[0:], timeLow) + binary.BigEndian.PutUint16(uuid[4:], timeMid) + binary.BigEndian.PutUint16(uuid[6:], timeHi) + binary.BigEndian.PutUint16(uuid[8:], seq) + copy(uuid[10:], nodeID[:]) + + return uuid, nil +} diff --git a/vendor/github.com/google/uuid/version4.go b/vendor/github.com/google/uuid/version4.go new file mode 100644 index 0000000000000..84af91c9f54f0 --- /dev/null +++ b/vendor/github.com/google/uuid/version4.go @@ -0,0 +1,38 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "io" + +// New creates a new random UUID or panics. New is equivalent to +// the expression +// +// uuid.Must(uuid.NewRandom()) +func New() UUID { + return Must(NewRandom()) +} + +// NewRandom returns a Random (Version 4) UUID. +// +// The strength of the UUIDs is based on the strength of the crypto/rand +// package. +// +// A note about uniqueness derived from the UUID Wikipedia entry: +// +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. +func NewRandom() (UUID, error) { + var uuid UUID + _, err := io.ReadFull(rander, uuid[:]) + if err != nil { + return Nil, err + } + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} diff --git a/vendor/github.com/googleapis/gax-go/README.md b/vendor/github.com/googleapis/gax-go/README.md index 3cedd5be96c5d..c3bb2e18779e2 100644 --- a/vendor/github.com/googleapis/gax-go/README.md +++ b/vendor/github.com/googleapis/gax-go/README.md @@ -1,19 +1,22 @@ Google API Extensions for Go ============================ -[![Build Status](https://travis-ci.org/googleapis/gax-go.svg?branch=master)](https://travis-ci.org/googleapis/gax-go) -[![Code Coverage](https://img.shields.io/codecov/c/github/googleapis/gax-go.svg)](https://codecov.io/github/googleapis/gax-go) +[![GoDoc](https://godoc.org/github.com/googleapis/gax-go?status.svg)](https://godoc.org/github.com/googleapis/gax-go) Google API Extensions for Go (gax-go) is a set of modules which aids the development of APIs for clients and servers based on `gRPC` and Google API conventions. -Application code will rarely need to use this library directly, +To install the API extensions, use: + +``` +go get -u github.com/googleapis/gax-go +``` + +**Note:** Application code will rarely need to use this library directly, but the code generated automatically from API definition files can use it to simplify code generation and to provide more convenient and idiomatic API surface. -**This project is currently experimental and not supported.** - Go Versions =========== This library requires Go 1.6 or above. diff --git a/vendor/github.com/googleapis/gax-go/go.mod b/vendor/github.com/googleapis/gax-go/go.mod new file mode 100644 index 0000000000000..acfd6c9c61d2c --- /dev/null +++ b/vendor/github.com/googleapis/gax-go/go.mod @@ -0,0 +1,11 @@ +module github.com/googleapis/gax-go + +require ( + github.com/golang/protobuf v1.3.1 + github.com/googleapis/gax-go/v2 v2.0.2 + golang.org/x/exp v0.0.0-20190221220918-438050ddec5e + golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 + golang.org/x/tools v0.0.0-20190114222345-bf090417da8b + google.golang.org/grpc v1.19.0 + honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 +) diff --git a/vendor/github.com/googleapis/gax-go/header.go b/vendor/github.com/googleapis/gax-go/header.go deleted file mode 100644 index d81455eccd977..0000000000000 --- a/vendor/github.com/googleapis/gax-go/header.go +++ /dev/null @@ -1,24 +0,0 @@ -package gax - -import "bytes" - -// XGoogHeader is for use by the Google Cloud Libraries only. -// -// XGoogHeader formats key-value pairs. -// The resulting string is suitable for x-goog-api-client header. -func XGoogHeader(keyval ...string) string { - if len(keyval) == 0 { - return "" - } - if len(keyval)%2 != 0 { - panic("gax.Header: odd argument count") - } - var buf bytes.Buffer - for i := 0; i < len(keyval); i += 2 { - buf.WriteByte(' ') - buf.WriteString(keyval[i]) - buf.WriteByte('/') - buf.WriteString(keyval[i+1]) - } - return buf.String()[1:] -} diff --git a/vendor/github.com/googleapis/gax-go/call_option.go b/vendor/github.com/googleapis/gax-go/v2/call_option.go similarity index 90% rename from vendor/github.com/googleapis/gax-go/call_option.go rename to vendor/github.com/googleapis/gax-go/v2/call_option.go index 7b621643e9456..b1d53dd19cb25 100644 --- a/vendor/github.com/googleapis/gax-go/call_option.go +++ b/vendor/github.com/googleapis/gax-go/v2/call_option.go @@ -113,6 +113,7 @@ type Backoff struct { cur time.Duration } +// Pause returns the next time.Duration that the caller should use to backoff. func (bo *Backoff) Pause() time.Duration { if bo.Initial == 0 { bo.Initial = time.Second @@ -126,10 +127,11 @@ func (bo *Backoff) Pause() time.Duration { if bo.Multiplier < 1 { bo.Multiplier = 2 } - // Select a duration between zero and the current max. It might seem counterintuitive to - // have so much jitter, but https://www.awsarchitectureblog.com/2015/03/backoff.html - // argues that that is the best strategy. - d := time.Duration(rand.Int63n(int64(bo.cur))) + // Select a duration between 1ns and the current max. It might seem + // counterintuitive to have so much jitter, but + // https://www.awsarchitectureblog.com/2015/03/backoff.html argues that + // that is the best strategy. + d := time.Duration(1 + rand.Int63n(int64(bo.cur))) bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier) if bo.cur > bo.Max { bo.cur = bo.Max @@ -143,10 +145,12 @@ func (o grpcOpt) Resolve(s *CallSettings) { s.GRPC = o } +// WithGRPCOptions allows passing gRPC call options during client creation. func WithGRPCOptions(opt ...grpc.CallOption) CallOption { return grpcOpt(append([]grpc.CallOption(nil), opt...)) } +// CallSettings allow fine-grained control over how calls are made. type CallSettings struct { // Retry returns a Retryer to be used to control retry logic of a method call. // If Retry is nil or the returned Retryer is nil, the call will not be retried. diff --git a/vendor/github.com/googleapis/gax-go/gax.go b/vendor/github.com/googleapis/gax-go/v2/gax.go similarity index 95% rename from vendor/github.com/googleapis/gax-go/gax.go rename to vendor/github.com/googleapis/gax-go/v2/gax.go index 5ebedff0d02de..3fd1b0b84b0af 100644 --- a/vendor/github.com/googleapis/gax-go/gax.go +++ b/vendor/github.com/googleapis/gax-go/v2/gax.go @@ -33,8 +33,7 @@ // Application code will rarely need to use this library directly. // However, code generated automatically from API definition files can use it // to simplify code generation and to provide more convenient and idiomatic API surfaces. -// -// This project is currently experimental and not supported. package gax -const Version = "0.1.0" +// Version specifies the gax-go version being used. +const Version = "2.0.4" diff --git a/vendor/github.com/googleapis/gax-go/v2/go.mod b/vendor/github.com/googleapis/gax-go/v2/go.mod new file mode 100644 index 0000000000000..9cdfaf4475267 --- /dev/null +++ b/vendor/github.com/googleapis/gax-go/v2/go.mod @@ -0,0 +1,3 @@ +module github.com/googleapis/gax-go/v2 + +require google.golang.org/grpc v1.19.0 diff --git a/vendor/github.com/googleapis/gax-go/v2/header.go b/vendor/github.com/googleapis/gax-go/v2/header.go new file mode 100644 index 0000000000000..139371a0bf111 --- /dev/null +++ b/vendor/github.com/googleapis/gax-go/v2/header.go @@ -0,0 +1,53 @@ +// Copyright 2018, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package gax + +import "bytes" + +// XGoogHeader is for use by the Google Cloud Libraries only. +// +// XGoogHeader formats key-value pairs. +// The resulting string is suitable for x-goog-api-client header. +func XGoogHeader(keyval ...string) string { + if len(keyval) == 0 { + return "" + } + if len(keyval)%2 != 0 { + panic("gax.Header: odd argument count") + } + var buf bytes.Buffer + for i := 0; i < len(keyval); i += 2 { + buf.WriteByte(' ') + buf.WriteString(keyval[i]) + buf.WriteByte('/') + buf.WriteString(keyval[i+1]) + } + return buf.String()[1:] +} diff --git a/vendor/github.com/googleapis/gax-go/invoke.go b/vendor/github.com/googleapis/gax-go/v2/invoke.go similarity index 83% rename from vendor/github.com/googleapis/gax-go/invoke.go rename to vendor/github.com/googleapis/gax-go/v2/invoke.go index 86049d826f803..fe31dd004e961 100644 --- a/vendor/github.com/googleapis/gax-go/invoke.go +++ b/vendor/github.com/googleapis/gax-go/v2/invoke.go @@ -30,12 +30,12 @@ package gax import ( + "context" + "strings" "time" - - "golang.org/x/net/context" ) -// A user defined call stub. +// APICall is a user defined call stub. type APICall func(context.Context, CallSettings) error // Invoke calls the given APICall, @@ -74,6 +74,15 @@ func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper if settings.Retry == nil { return err } + // Never retry permanent certificate errors. (e.x. if ca-certificates + // are not installed). We should only make very few, targeted + // exceptions: many (other) status=Unavailable should be retried, such + // as if there's a network hiccup, or the internet goes out for a + // minute. This is also why here we are doing string parsing instead of + // simply making Unavailable a non-retried code elsewhere. + if strings.Contains(err.Error(), "x509: certificate signed by unknown authority") { + return err + } if retryer == nil { if r := settings.Retry(); r != nil { retryer = r diff --git a/vendor/github.com/gorilla/context/LICENSE b/vendor/github.com/gorilla/context/LICENSE deleted file mode 100644 index 0e5fb872800da..0000000000000 --- a/vendor/github.com/gorilla/context/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2012 Rodrigo Moraes. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/context/README.md b/vendor/github.com/gorilla/context/README.md deleted file mode 100644 index c60a31b053bc4..0000000000000 --- a/vendor/github.com/gorilla/context/README.md +++ /dev/null @@ -1,7 +0,0 @@ -context -======= -[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context) - -gorilla/context is a general purpose registry for global request variables. - -Read the full documentation here: http://www.gorillatoolkit.org/pkg/context diff --git a/vendor/github.com/gorilla/context/context.go b/vendor/github.com/gorilla/context/context.go deleted file mode 100644 index 81cb128b19cad..0000000000000 --- a/vendor/github.com/gorilla/context/context.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package context - -import ( - "net/http" - "sync" - "time" -) - -var ( - mutex sync.RWMutex - data = make(map[*http.Request]map[interface{}]interface{}) - datat = make(map[*http.Request]int64) -) - -// Set stores a value for a given key in a given request. -func Set(r *http.Request, key, val interface{}) { - mutex.Lock() - if data[r] == nil { - data[r] = make(map[interface{}]interface{}) - datat[r] = time.Now().Unix() - } - data[r][key] = val - mutex.Unlock() -} - -// Get returns a value stored for a given key in a given request. -func Get(r *http.Request, key interface{}) interface{} { - mutex.RLock() - if ctx := data[r]; ctx != nil { - value := ctx[key] - mutex.RUnlock() - return value - } - mutex.RUnlock() - return nil -} - -// GetOk returns stored value and presence state like multi-value return of map access. -func GetOk(r *http.Request, key interface{}) (interface{}, bool) { - mutex.RLock() - if _, ok := data[r]; ok { - value, ok := data[r][key] - mutex.RUnlock() - return value, ok - } - mutex.RUnlock() - return nil, false -} - -// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests. -func GetAll(r *http.Request) map[interface{}]interface{} { - mutex.RLock() - if context, ok := data[r]; ok { - result := make(map[interface{}]interface{}, len(context)) - for k, v := range context { - result[k] = v - } - mutex.RUnlock() - return result - } - mutex.RUnlock() - return nil -} - -// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if -// the request was registered. -func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) { - mutex.RLock() - context, ok := data[r] - result := make(map[interface{}]interface{}, len(context)) - for k, v := range context { - result[k] = v - } - mutex.RUnlock() - return result, ok -} - -// Delete removes a value stored for a given key in a given request. -func Delete(r *http.Request, key interface{}) { - mutex.Lock() - if data[r] != nil { - delete(data[r], key) - } - mutex.Unlock() -} - -// Clear removes all values stored for a given request. -// -// This is usually called by a handler wrapper to clean up request -// variables at the end of a request lifetime. See ClearHandler(). -func Clear(r *http.Request) { - mutex.Lock() - clear(r) - mutex.Unlock() -} - -// clear is Clear without the lock. -func clear(r *http.Request) { - delete(data, r) - delete(datat, r) -} - -// Purge removes request data stored for longer than maxAge, in seconds. -// It returns the amount of requests removed. -// -// If maxAge <= 0, all request data is removed. -// -// This is only used for sanity check: in case context cleaning was not -// properly set some request data can be kept forever, consuming an increasing -// amount of memory. In case this is detected, Purge() must be called -// periodically until the problem is fixed. -func Purge(maxAge int) int { - mutex.Lock() - count := 0 - if maxAge <= 0 { - count = len(data) - data = make(map[*http.Request]map[interface{}]interface{}) - datat = make(map[*http.Request]int64) - } else { - min := time.Now().Unix() - int64(maxAge) - for r := range data { - if datat[r] < min { - clear(r) - count++ - } - } - } - mutex.Unlock() - return count -} - -// ClearHandler wraps an http.Handler and clears request values at the end -// of a request lifetime. -func ClearHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defer Clear(r) - h.ServeHTTP(w, r) - }) -} diff --git a/vendor/github.com/gorilla/context/doc.go b/vendor/github.com/gorilla/context/doc.go deleted file mode 100644 index 73c7400311e12..0000000000000 --- a/vendor/github.com/gorilla/context/doc.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package context stores values shared during a request lifetime. - -For example, a router can set variables extracted from the URL and later -application handlers can access those values, or it can be used to store -sessions values to be saved at the end of a request. There are several -others common uses. - -The idea was posted by Brad Fitzpatrick to the go-nuts mailing list: - - http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53 - -Here's the basic usage: first define the keys that you will need. The key -type is interface{} so a key can be of any type that supports equality. -Here we define a key using a custom int type to avoid name collisions: - - package foo - - import ( - "github.com/gorilla/context" - ) - - type key int - - const MyKey key = 0 - -Then set a variable. Variables are bound to an http.Request object, so you -need a request instance to set a value: - - context.Set(r, MyKey, "bar") - -The application can later access the variable using the same key you provided: - - func MyHandler(w http.ResponseWriter, r *http.Request) { - // val is "bar". - val := context.Get(r, foo.MyKey) - - // returns ("bar", true) - val, ok := context.GetOk(r, foo.MyKey) - // ... - } - -And that's all about the basic usage. We discuss some other ideas below. - -Any type can be stored in the context. To enforce a given type, make the key -private and wrap Get() and Set() to accept and return values of a specific -type: - - type key int - - const mykey key = 0 - - // GetMyKey returns a value for this package from the request values. - func GetMyKey(r *http.Request) SomeType { - if rv := context.Get(r, mykey); rv != nil { - return rv.(SomeType) - } - return nil - } - - // SetMyKey sets a value for this package in the request values. - func SetMyKey(r *http.Request, val SomeType) { - context.Set(r, mykey, val) - } - -Variables must be cleared at the end of a request, to remove all values -that were stored. This can be done in an http.Handler, after a request was -served. Just call Clear() passing the request: - - context.Clear(r) - -...or use ClearHandler(), which conveniently wraps an http.Handler to clear -variables at the end of a request lifetime. - -The Routers from the packages gorilla/mux and gorilla/pat call Clear() -so if you are using either of them you don't need to clear the context manually. -*/ -package context diff --git a/vendor/github.com/gorilla/mux/LICENSE b/vendor/github.com/gorilla/mux/LICENSE index 0e5fb872800da..6903df6386e98 100644 --- a/vendor/github.com/gorilla/mux/LICENSE +++ b/vendor/github.com/gorilla/mux/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012 Rodrigo Moraes. All rights reserved. +Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/vendor/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md index 9516c51916308..35eea9f106354 100644 --- a/vendor/github.com/gorilla/mux/README.md +++ b/vendor/github.com/gorilla/mux/README.md @@ -1,29 +1,60 @@ -mux -=== +# gorilla/mux + [![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux) -[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux) +[![CircleCI](https://circleci.com/gh/gorilla/mux.svg?style=svg)](https://circleci.com/gh/gorilla/mux) +[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/mux/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/mux?badge) + +![Gorilla Logo](https://cloud-cdn.questionable.services/gorilla-icon-64.png) -http://www.gorillatoolkit.org/pkg/mux +https://www.gorillatoolkit.org/pkg/mux -Package `gorilla/mux` implements a request router and dispatcher. +Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to +their respective handler. The name mux stands for "HTTP request multiplexer". Like the standard `http.ServeMux`, `mux.Router` matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are: +* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`. * Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers. -* URL hosts and paths can have variables with an optional regular expression. +* URL hosts, paths and query values can have variables with an optional regular expression. * Registered URLs can be built, or "reversed", which helps maintaining references to resources. * Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching. -* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`. + +--- + +* [Install](#install) +* [Examples](#examples) +* [Matching Routes](#matching-routes) +* [Static Files](#static-files) +* [Serving Single Page Applications](#serving-single-page-applications) (e.g. React, Vue, Ember.js, etc.) +* [Registered URLs](#registered-urls) +* [Walking Routes](#walking-routes) +* [Graceful Shutdown](#graceful-shutdown) +* [Middleware](#middleware) +* [Handling CORS Requests](#handling-cors-requests) +* [Testing Handlers](#testing-handlers) +* [Full Example](#full-example) + +--- + +## Install + +With a [correctly configured](https://golang.org/doc/install#testing) Go toolchain: + +```sh +go get -u github.com/gorilla/mux +``` + +## Examples Let's start registering a couple of URL paths and handlers: ```go func main() { - r := mux.NewRouter() - r.HandleFunc("/", HomeHandler) - r.HandleFunc("/products", ProductsHandler) - r.HandleFunc("/articles", ArticlesHandler) - http.Handle("/", r) + r := mux.NewRouter() + r.HandleFunc("/", HomeHandler) + r.HandleFunc("/products", ProductsHandler) + r.HandleFunc("/articles", ArticlesHandler) + http.Handle("/", r) } ``` @@ -41,12 +72,17 @@ r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`: ```go -vars := mux.Vars(request) -category := vars["category"] +func ArticlesCategoryHandler(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "Category: %v\n", vars["category"]) +} ``` And this is all you need to know about the basic usage. More advanced options are explained below. +### Matching Routes + Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables: ```go @@ -54,7 +90,7 @@ r := mux.NewRouter() // Only matches if domain is "www.example.com". r.Host("www.example.com") // Matches a dynamic subdomain. -r.Host("{subdomain:[a-z]+}.domain.com") +r.Host("{subdomain:[a-z]+}.example.com") ``` There are several other matchers that can be added. To match path prefixes: @@ -91,7 +127,7 @@ r.Queries("key", "value") ```go r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool { - return r.ProtoMajor == 0 + return r.ProtoMajor == 0 }) ``` @@ -104,6 +140,14 @@ r.HandleFunc("/products", ProductsHandler). Schemes("http") ``` +Routes are tested in the order they were added to the router. If two routes match, the first one wins: + +```go +r := mux.NewRouter() +r.HandleFunc("/specific", specificHandler) +r.PathPrefix("/").Handler(catchAllHandler) +``` + Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting". For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it: @@ -118,7 +162,7 @@ Then register routes in the subrouter: ```go s.HandleFunc("/products/", ProductsHandler) s.HandleFunc("/products/{key}", ProductHandler) -s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler) +s.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) ``` The three URL paths we registered above will only be tested if the domain is `www.example.com`, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route. @@ -138,6 +182,125 @@ s.HandleFunc("/{key}/", ProductHandler) s.HandleFunc("/{key}/details", ProductDetailsHandler) ``` + +### Static Files + +Note that the path provided to `PathPrefix()` represents a "wildcard": calling +`PathPrefix("/static/").Handler(...)` means that the handler will be passed any +request that matches "/static/\*". This makes it easy to serve static files with mux: + +```go +func main() { + var dir string + + flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir") + flag.Parse() + r := mux.NewRouter() + + // This will serve files under http://localhost:8000/static/ + r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir)))) + + srv := &http.Server{ + Handler: r, + Addr: "127.0.0.1:8000", + // Good practice: enforce timeouts for servers you create! + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + + log.Fatal(srv.ListenAndServe()) +} +``` + +### Serving Single Page Applications + +Most of the time it makes sense to serve your SPA on a separate web server from your API, +but sometimes it's desirable to serve them both from one place. It's possible to write a simple +handler for serving your SPA (for use with React Router's [BrowserRouter](https://reacttraining.com/react-router/web/api/BrowserRouter) for example), and leverage +mux's powerful routing for your API endpoints. + +```go +package main + +import ( + "encoding/json" + "log" + "net/http" + "os" + "path/filepath" + "time" + + "github.com/gorilla/mux" +) + +// spaHandler implements the http.Handler interface, so we can use it +// to respond to HTTP requests. The path to the static directory and +// path to the index file within that static directory are used to +// serve the SPA in the given static directory. +type spaHandler struct { + staticPath string + indexPath string +} + +// ServeHTTP inspects the URL path to locate a file within the static dir +// on the SPA handler. If a file is found, it will be served. If not, the +// file located at the index path on the SPA handler will be served. This +// is suitable behavior for serving an SPA (single page application). +func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // get the absolute path to prevent directory traversal + path, err := filepath.Abs(r.URL.Path) + if err != nil { + // if we failed to get the absolute path respond with a 400 bad request + // and stop + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // prepend the path with the path to the static directory + path = filepath.Join(h.staticPath, path) + + // check whether a file exists at the given path + _, err = os.Stat(path) + if os.IsNotExist(err) { + // file does not exist, serve index.html + http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath)) + return + } else if err != nil { + // if we got an error (that wasn't that the file doesn't exist) stating the + // file, return a 500 internal server error and stop + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // otherwise, use http.FileServer to serve the static dir + http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r) +} + +func main() { + router := mux.NewRouter() + + router.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { + // an example API handler + json.NewEncoder(w).Encode(map[string]bool{"ok": true}) + }) + + spa := spaHandler{staticPath: "build", indexPath: "index.html"} + router.PathPrefix("/").Handler(spa) + + srv := &http.Server{ + Handler: router, + Addr: "127.0.0.1:8000", + // Good practice: enforce timeouts for servers you create! + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + + log.Fatal(srv.ListenAndServe()) +} +``` + +### Registered URLs + Now let's see how to build registered URLs. Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling `Name()` on a route. For example: @@ -160,19 +323,21 @@ url, err := r.Get("article").URL("category", "technology", "id", "42") "/articles/technology/42" ``` -This also works for host variables: +This also works for host and query value variables: ```go r := mux.NewRouter() -r.Host("{subdomain}.domain.com"). +r.Host("{subdomain}.example.com"). Path("/articles/{category}/{id:[0-9]+}"). + Queries("filter", "{filter}"). HandlerFunc(ArticleHandler). Name("article") -// url.String() will be "http://news.domain.com/articles/technology/42" +// url.String() will be "http://news.example.com/articles/technology/42?filter=gorilla" url, err := r.Get("article").URL("subdomain", "news", "category", "technology", - "id", "42") + "id", "42", + "filter", "gorilla") ``` All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match. @@ -188,7 +353,7 @@ r.HeadersRegexp("Content-Type", "application/(text|json)") There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do: ```go -// "http://news.domain.com/" +// "http://news.example.com/" host, err := r.Get("article").URLHost("subdomain", "news") // "/articles/technology/42" @@ -199,41 +364,439 @@ And if you use subrouters, host and path defined separately can be built as well ```go r := mux.NewRouter() -s := r.Host("{subdomain}.domain.com").Subrouter() +s := r.Host("{subdomain}.example.com").Subrouter() s.Path("/articles/{category}/{id:[0-9]+}"). HandlerFunc(ArticleHandler). Name("article") -// "http://news.domain.com/articles/technology/42" +// "http://news.example.com/articles/technology/42" url, err := r.Get("article").URL("subdomain", "news", "category", "technology", "id", "42") ``` -## Full Example +### Walking Routes -Here's a complete, runnable example of a small `mux` based server: +The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example, +the following prints all of the registered routes: ```go package main import ( + "fmt" "net/http" + "strings" "github.com/gorilla/mux" ) -func YourHandler(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Gorilla!\n")) +func handler(w http.ResponseWriter, r *http.Request) { + return } func main() { r := mux.NewRouter() - // Routes consist of a path and a handler function. - r.HandleFunc("/", YourHandler) + r.HandleFunc("/", handler) + r.HandleFunc("/products", handler).Methods("POST") + r.HandleFunc("/articles", handler).Methods("GET") + r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT") + r.HandleFunc("/authors", handler).Queries("surname", "{surname}") + err := r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { + pathTemplate, err := route.GetPathTemplate() + if err == nil { + fmt.Println("ROUTE:", pathTemplate) + } + pathRegexp, err := route.GetPathRegexp() + if err == nil { + fmt.Println("Path regexp:", pathRegexp) + } + queriesTemplates, err := route.GetQueriesTemplates() + if err == nil { + fmt.Println("Queries templates:", strings.Join(queriesTemplates, ",")) + } + queriesRegexps, err := route.GetQueriesRegexp() + if err == nil { + fmt.Println("Queries regexps:", strings.Join(queriesRegexps, ",")) + } + methods, err := route.GetMethods() + if err == nil { + fmt.Println("Methods:", strings.Join(methods, ",")) + } + fmt.Println() + return nil + }) + + if err != nil { + fmt.Println(err) + } + + http.Handle("/", r) +} +``` + +### Graceful Shutdown + +Go 1.8 introduced the ability to [gracefully shutdown](https://golang.org/doc/go1.8#http_shutdown) a `*http.Server`. Here's how to do that alongside `mux`: + +```go +package main + +import ( + "context" + "flag" + "log" + "net/http" + "os" + "os/signal" + "time" + + "github.com/gorilla/mux" +) + +func main() { + var wait time.Duration + flag.DurationVar(&wait, "graceful-timeout", time.Second * 15, "the duration for which the server gracefully wait for existing connections to finish - e.g. 15s or 1m") + flag.Parse() + + r := mux.NewRouter() + // Add your routes as needed + + srv := &http.Server{ + Addr: "0.0.0.0:8080", + // Good practice to set timeouts to avoid Slowloris attacks. + WriteTimeout: time.Second * 15, + ReadTimeout: time.Second * 15, + IdleTimeout: time.Second * 60, + Handler: r, // Pass our instance of gorilla/mux in. + } + + // Run our server in a goroutine so that it doesn't block. + go func() { + if err := srv.ListenAndServe(); err != nil { + log.Println(err) + } + }() + + c := make(chan os.Signal, 1) + // We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C) + // SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught. + signal.Notify(c, os.Interrupt) + + // Block until we receive our signal. + <-c + + // Create a deadline to wait for. + ctx, cancel := context.WithTimeout(context.Background(), wait) + defer cancel() + // Doesn't block if no connections, but will otherwise wait + // until the timeout deadline. + srv.Shutdown(ctx) + // Optionally, you could run srv.Shutdown in a goroutine and block on + // <-ctx.Done() if your application should wait for other services + // to finalize based on context cancellation. + log.Println("shutting down") + os.Exit(0) +} +``` + +### Middleware + +Mux supports the addition of middlewares to a [Router](https://godoc.org/github.com/gorilla/mux#Router), which are executed in the order they are added if a match is found, including its subrouters. +Middlewares are (typically) small pieces of code which take one request, do something with it, and pass it down to another middleware or the final handler. Some common use cases for middleware are request logging, header manipulation, or `ResponseWriter` hijacking. + +Mux middlewares are defined using the de facto standard type: + +```go +type MiddlewareFunc func(http.Handler) http.Handler +``` + +Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed to it, and then calls the handler passed as parameter to the MiddlewareFunc. This takes advantage of closures being able access variables from the context where they are created, while retaining the signature enforced by the receivers. + +A very basic middleware which logs the URI of the request being handled could be written as: + +```go +func loggingMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Do stuff here + log.Println(r.RequestURI) + // Call the next handler, which can be another middleware in the chain, or the final handler. + next.ServeHTTP(w, r) + }) +} +``` + +Middlewares can be added to a router using `Router.Use()`: + +```go +r := mux.NewRouter() +r.HandleFunc("/", handler) +r.Use(loggingMiddleware) +``` + +A more complex authentication middleware, which maps session token to users, could be written as: + +```go +// Define our struct +type authenticationMiddleware struct { + tokenUsers map[string]string +} + +// Initialize it somewhere +func (amw *authenticationMiddleware) Populate() { + amw.tokenUsers["00000000"] = "user0" + amw.tokenUsers["aaaaaaaa"] = "userA" + amw.tokenUsers["05f717e5"] = "randomUser" + amw.tokenUsers["deadbeef"] = "user0" +} + +// Middleware function, which will be called for each request +func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + token := r.Header.Get("X-Session-Token") + + if user, found := amw.tokenUsers[token]; found { + // We found the token in our map + log.Printf("Authenticated user %s\n", user) + // Pass down the request to the next middleware (or final handler) + next.ServeHTTP(w, r) + } else { + // Write an error and stop the handler chain + http.Error(w, "Forbidden", http.StatusForbidden) + } + }) +} +``` + +```go +r := mux.NewRouter() +r.HandleFunc("/", handler) + +amw := authenticationMiddleware{} +amw.Populate() + +r.Use(amw.Middleware) +``` + +Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. Middlewares _should_ write to `ResponseWriter` if they _are_ going to terminate the request, and they _should not_ write to `ResponseWriter` if they _are not_ going to terminate it. + +### Handling CORS Requests + +[CORSMethodMiddleware](https://godoc.org/github.com/gorilla/mux#CORSMethodMiddleware) intends to make it easier to strictly set the `Access-Control-Allow-Methods` response header. + +* You will still need to use your own CORS handler to set the other CORS headers such as `Access-Control-Allow-Origin` +* The middleware will set the `Access-Control-Allow-Methods` header to all the method matchers (e.g. `r.Methods(http.MethodGet, http.MethodPut, http.MethodOptions)` -> `Access-Control-Allow-Methods: GET,PUT,OPTIONS`) on a route +* If you do not specify any methods, then: +> _Important_: there must be an `OPTIONS` method matcher for the middleware to set the headers. + +Here is an example of using `CORSMethodMiddleware` along with a custom `OPTIONS` handler to set all the required CORS headers: + +```go +package main + +import ( + "net/http" + "github.com/gorilla/mux" +) + +func main() { + r := mux.NewRouter() + + // IMPORTANT: you must specify an OPTIONS method matcher for the middleware to set CORS headers + r.HandleFunc("/foo", fooHandler).Methods(http.MethodGet, http.MethodPut, http.MethodPatch, http.MethodOptions) + r.Use(mux.CORSMethodMiddleware(r)) + + http.ListenAndServe(":8080", r) +} + +func fooHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + if r.Method == http.MethodOptions { + return + } + + w.Write([]byte("foo")) +} +``` + +And an request to `/foo` using something like: + +```bash +curl localhost:8080/foo -v +``` + +Would look like: + +```bash +* Trying ::1... +* TCP_NODELAY set +* Connected to localhost (::1) port 8080 (#0) +> GET /foo HTTP/1.1 +> Host: localhost:8080 +> User-Agent: curl/7.59.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< Access-Control-Allow-Methods: GET,PUT,PATCH,OPTIONS +< Access-Control-Allow-Origin: * +< Date: Fri, 28 Jun 2019 20:13:30 GMT +< Content-Length: 3 +< Content-Type: text/plain; charset=utf-8 +< +* Connection #0 to host localhost left intact +foo +``` + +### Testing Handlers + +Testing handlers in a Go web application is straightforward, and _mux_ doesn't complicate this any further. Given two files: `endpoints.go` and `endpoints_test.go`, here's how we'd test an application using _mux_. + +First, our simple HTTP handler: + +```go +// endpoints.go +package main + +func HealthCheckHandler(w http.ResponseWriter, r *http.Request) { + // A very simple health check. + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + // In the future we could report back on the status of our DB, or our cache + // (e.g. Redis) by performing a simple PING, and include them in the response. + io.WriteString(w, `{"alive": true}`) +} + +func main() { + r := mux.NewRouter() + r.HandleFunc("/health", HealthCheckHandler) + + log.Fatal(http.ListenAndServe("localhost:8080", r)) +} +``` + +Our test code: + +```go +// endpoints_test.go +package main + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestHealthCheckHandler(t *testing.T) { + // Create a request to pass to our handler. We don't have any query parameters for now, so we'll + // pass 'nil' as the third parameter. + req, err := http.NewRequest("GET", "/health", nil) + if err != nil { + t.Fatal(err) + } + + // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + rr := httptest.NewRecorder() + handler := http.HandlerFunc(HealthCheckHandler) + + // Our handlers satisfy http.Handler, so we can call their ServeHTTP method + // directly and pass in our Request and ResponseRecorder. + handler.ServeHTTP(rr, req) + + // Check the status code is what we expect. + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Check the response body is what we expect. + expected := `{"alive": true}` + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: got %v want %v", + rr.Body.String(), expected) + } +} +``` + +In the case that our routes have [variables](#examples), we can pass those in the request. We could write +[table-driven tests](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go) to test multiple +possible route variables as needed. + +```go +// endpoints.go +func main() { + r := mux.NewRouter() + // A route with a route variable: + r.HandleFunc("/metrics/{type}", MetricsHandler) + + log.Fatal(http.ListenAndServe("localhost:8080", r)) +} +``` + +Our test file, with a table-driven test of `routeVariables`: + +```go +// endpoints_test.go +func TestMetricsHandler(t *testing.T) { + tt := []struct{ + routeVariable string + shouldPass bool + }{ + {"goroutines", true}, + {"heap", true}, + {"counters", true}, + {"queries", true}, + {"adhadaeqm3k", false}, + } + + for _, tc := range tt { + path := fmt.Sprintf("/metrics/%s", tc.routeVariable) + req, err := http.NewRequest("GET", path, nil) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + + // Need to create a router that we can pass the request through so that the vars will be added to the context + router := mux.NewRouter() + router.HandleFunc("/metrics/{type}", MetricsHandler) + router.ServeHTTP(rr, req) + + // In this case, our MetricsHandler returns a non-200 response + // for a route variable it doesn't know about. + if rr.Code == http.StatusOK && !tc.shouldPass { + t.Errorf("handler should have failed on routeVariable %s: got %v want %v", + tc.routeVariable, rr.Code, http.StatusOK) + } + } +} +``` + +## Full Example + +Here's a complete, runnable example of a small `mux` based server: + +```go +package main + +import ( + "net/http" + "log" + "github.com/gorilla/mux" +) + +func YourHandler(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Gorilla!\n")) +} + +func main() { + r := mux.NewRouter() + // Routes consist of a path and a handler function. + r.HandleFunc("/", YourHandler) - // Bind to a port and pass our router in - http.ListenAndServe(":8000", r) + // Bind to a port and pass our router in + log.Fatal(http.ListenAndServe(":8000", r)) } ``` diff --git a/vendor/github.com/gorilla/mux/doc.go b/vendor/github.com/gorilla/mux/doc.go index 835f5342eb97d..bd5a38b55d82f 100644 --- a/vendor/github.com/gorilla/mux/doc.go +++ b/vendor/github.com/gorilla/mux/doc.go @@ -12,8 +12,8 @@ or other conditions. The main features are: * Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers. - * URL hosts and paths can have variables with an optional regular - expression. + * URL hosts, paths and query values can have variables with an optional + regular expression. * Registered URLs can be built, or "reversed", which helps maintaining references to resources. * Routes can be used as subrouters: nested routes are only tested if the @@ -47,12 +47,21 @@ variable will be anything until the next slash. For example: r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) +Groups can be used inside patterns, as long as they are non-capturing (?:re). For example: + + r.HandleFunc("/articles/{category}/{sort:(?:asc|desc|new)}", ArticlesCategoryHandler) + The names are used to create a map of route variables which can be retrieved calling mux.Vars(): vars := mux.Vars(request) category := vars["category"] +Note that if any capturing groups are present, mux will panic() during parsing. To prevent +this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to +"/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably +when capturing groups were present. + And this is all you need to know about the basic usage. More advanced options are explained below. @@ -136,6 +145,31 @@ the inner routes use it as base for their paths: // "/products/{key}/details" s.HandleFunc("/{key}/details", ProductDetailsHandler) +Note that the path provided to PathPrefix() represents a "wildcard": calling +PathPrefix("/static/").Handler(...) means that the handler will be passed any +request that matches "/static/*". This makes it easy to serve static files with mux: + + func main() { + var dir string + + flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir") + flag.Parse() + r := mux.NewRouter() + + // This will serve files under http://localhost:8000/static/ + r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir)))) + + srv := &http.Server{ + Handler: r, + Addr: "127.0.0.1:8000", + // Good practice: enforce timeouts for servers you create! + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + + log.Fatal(srv.ListenAndServe()) + } + Now let's see how to build registered URLs. Routes can be named. All routes that define a name can have their URLs built, @@ -154,18 +188,20 @@ key/value pairs for the route variables. For the previous route, we would do: "/articles/technology/42" -This also works for host variables: +This also works for host and query value variables: r := mux.NewRouter() r.Host("{subdomain}.domain.com"). Path("/articles/{category}/{id:[0-9]+}"). + Queries("filter", "{filter}"). HandlerFunc(ArticleHandler). Name("article") - // url.String() will be "http://news.domain.com/articles/technology/42" + // url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla" url, err := r.Get("article").URL("subdomain", "news", "category", "technology", - "id", "42") + "id", "42", + "filter", "gorilla") All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a @@ -202,5 +238,69 @@ as well: url, err := r.Get("article").URL("subdomain", "news", "category", "technology", "id", "42") + +Mux supports the addition of middlewares to a Router, which are executed in the order they are added if a match is found, including its subrouters. Middlewares are (typically) small pieces of code which take one request, do something with it, and pass it down to another middleware or the final handler. Some common use cases for middleware are request logging, header manipulation, or ResponseWriter hijacking. + + type MiddlewareFunc func(http.Handler) http.Handler + +Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed to it, and then calls the handler passed as parameter to the MiddlewareFunc (closures can access variables from the context where they are created). + +A very basic middleware which logs the URI of the request being handled could be written as: + + func simpleMw(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Do stuff here + log.Println(r.RequestURI) + // Call the next handler, which can be another middleware in the chain, or the final handler. + next.ServeHTTP(w, r) + }) + } + +Middlewares can be added to a router using `Router.Use()`: + + r := mux.NewRouter() + r.HandleFunc("/", handler) + r.Use(simpleMw) + +A more complex authentication middleware, which maps session token to users, could be written as: + + // Define our struct + type authenticationMiddleware struct { + tokenUsers map[string]string + } + + // Initialize it somewhere + func (amw *authenticationMiddleware) Populate() { + amw.tokenUsers["00000000"] = "user0" + amw.tokenUsers["aaaaaaaa"] = "userA" + amw.tokenUsers["05f717e5"] = "randomUser" + amw.tokenUsers["deadbeef"] = "user0" + } + + // Middleware function, which will be called for each request + func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + token := r.Header.Get("X-Session-Token") + + if user, found := amw.tokenUsers[token]; found { + // We found the token in our map + log.Printf("Authenticated user %s\n", user) + next.ServeHTTP(w, r) + } else { + http.Error(w, "Forbidden", http.StatusForbidden) + } + }) + } + + r := mux.NewRouter() + r.HandleFunc("/", handler) + + amw := authenticationMiddleware{tokenUsers: make(map[string]string)} + amw.Populate() + + r.Use(amw.Middleware) + +Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to. + */ package mux diff --git a/vendor/github.com/gorilla/mux/go.mod b/vendor/github.com/gorilla/mux/go.mod new file mode 100644 index 0000000000000..df170a3994039 --- /dev/null +++ b/vendor/github.com/gorilla/mux/go.mod @@ -0,0 +1,3 @@ +module github.com/gorilla/mux + +go 1.12 diff --git a/vendor/github.com/gorilla/mux/middleware.go b/vendor/github.com/gorilla/mux/middleware.go new file mode 100644 index 0000000000000..cb51c565ebd3a --- /dev/null +++ b/vendor/github.com/gorilla/mux/middleware.go @@ -0,0 +1,74 @@ +package mux + +import ( + "net/http" + "strings" +) + +// MiddlewareFunc is a function which receives an http.Handler and returns another http.Handler. +// Typically, the returned handler is a closure which does something with the http.ResponseWriter and http.Request passed +// to it, and then calls the handler passed as parameter to the MiddlewareFunc. +type MiddlewareFunc func(http.Handler) http.Handler + +// middleware interface is anything which implements a MiddlewareFunc named Middleware. +type middleware interface { + Middleware(handler http.Handler) http.Handler +} + +// Middleware allows MiddlewareFunc to implement the middleware interface. +func (mw MiddlewareFunc) Middleware(handler http.Handler) http.Handler { + return mw(handler) +} + +// Use appends a MiddlewareFunc to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. +func (r *Router) Use(mwf ...MiddlewareFunc) { + for _, fn := range mwf { + r.middlewares = append(r.middlewares, fn) + } +} + +// useInterface appends a middleware to the chain. Middleware can be used to intercept or otherwise modify requests and/or responses, and are executed in the order that they are applied to the Router. +func (r *Router) useInterface(mw middleware) { + r.middlewares = append(r.middlewares, mw) +} + +// CORSMethodMiddleware automatically sets the Access-Control-Allow-Methods response header +// on requests for routes that have an OPTIONS method matcher to all the method matchers on +// the route. Routes that do not explicitly handle OPTIONS requests will not be processed +// by the middleware. See examples for usage. +func CORSMethodMiddleware(r *Router) MiddlewareFunc { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + allMethods, err := getAllMethodsForRoute(r, req) + if err == nil { + for _, v := range allMethods { + if v == http.MethodOptions { + w.Header().Set("Access-Control-Allow-Methods", strings.Join(allMethods, ",")) + } + } + } + + next.ServeHTTP(w, req) + }) + } +} + +// getAllMethodsForRoute returns all the methods from method matchers matching a given +// request. +func getAllMethodsForRoute(r *Router, req *http.Request) ([]string, error) { + var allMethods []string + + for _, route := range r.routes { + var match RouteMatch + if route.Match(req, &match) || match.MatchErr == ErrMethodMismatch { + methods, err := route.GetMethods() + if err != nil { + return nil, err + } + + allMethods = append(allMethods, methods...) + } + } + + return allMethods, nil +} diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go index fbb7f19adfc1e..782a34b22a608 100644 --- a/vendor/github.com/gorilla/mux/mux.go +++ b/vendor/github.com/gorilla/mux/mux.go @@ -5,18 +5,25 @@ package mux import ( + "context" "errors" "fmt" "net/http" "path" "regexp" +) - "github.com/gorilla/context" +var ( + // ErrMethodMismatch is returned when the method in the request does not match + // the method defined against the route. + ErrMethodMismatch = errors.New("method is not allowed") + // ErrNotFound is returned when no route match is found. + ErrNotFound = errors.New("no matching route was found") ) // NewRouter returns a new router instance. func NewRouter() *Router { - return &Router{namedRoutes: make(map[string]*Route), KeepContext: false} + return &Router{namedRoutes: make(map[string]*Route)} } // Router registers routes to be matched and dispatches a handler. @@ -40,31 +47,122 @@ func NewRouter() *Router { type Router struct { // Configurable Handler to be used when no route matches. NotFoundHandler http.Handler - // Parent route, if this is a subrouter. - parent parentRoute + + // Configurable Handler to be used when the request method does not match the route. + MethodNotAllowedHandler http.Handler + // Routes to be matched, in order. routes []*Route + // Routes by name for URL building. namedRoutes map[string]*Route - // See Router.StrictSlash(). This defines the flag for new routes. - strictSlash bool - // If true, do not clear the request context after handling the request + + // If true, do not clear the request context after handling the request. + // + // Deprecated: No effect, since the context is stored on the request itself. KeepContext bool + + // Slice of middlewares to be called after a match is found + middlewares []middleware + + // configuration shared with `Route` + routeConf +} + +// common route configuration shared between `Router` and `Route` +type routeConf struct { + // If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to" + useEncodedPath bool + + // If true, when the path pattern is "/path/", accessing "/path" will + // redirect to the former and vice versa. + strictSlash bool + + // If true, when the path pattern is "/path//to", accessing "/path//to" + // will not redirect + skipClean bool + + // Manager for the variables from host and path. + regexp routeRegexpGroup + + // List of matchers. + matchers []matcher + + // The scheme used when building URLs. + buildScheme string + + buildVarsFunc BuildVarsFunc +} + +// returns an effective deep copy of `routeConf` +func copyRouteConf(r routeConf) routeConf { + c := r + + if r.regexp.path != nil { + c.regexp.path = copyRouteRegexp(r.regexp.path) + } + + if r.regexp.host != nil { + c.regexp.host = copyRouteRegexp(r.regexp.host) + } + + c.regexp.queries = make([]*routeRegexp, 0, len(r.regexp.queries)) + for _, q := range r.regexp.queries { + c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q)) + } + + c.matchers = make([]matcher, len(r.matchers)) + copy(c.matchers, r.matchers) + + return c +} + +func copyRouteRegexp(r *routeRegexp) *routeRegexp { + c := *r + return &c } -// Match matches registered routes against the request. +// Match attempts to match the given request against the router's registered routes. +// +// If the request matches a route of this router or one of its subrouters the Route, +// Handler, and Vars fields of the the match argument are filled and this function +// returns true. +// +// If the request does not match any of this router's or its subrouters' routes +// then this function returns false. If available, a reason for the match failure +// will be filled in the match argument's MatchErr field. If the match failure type +// (eg: not found) has a registered handler, the handler is assigned to the Handler +// field of the match argument. func (r *Router) Match(req *http.Request, match *RouteMatch) bool { for _, route := range r.routes { if route.Match(req, match) { + // Build middleware chain if no error was found + if match.MatchErr == nil { + for i := len(r.middlewares) - 1; i >= 0; i-- { + match.Handler = r.middlewares[i].Middleware(match.Handler) + } + } return true } } + if match.MatchErr == ErrMethodMismatch { + if r.MethodNotAllowedHandler != nil { + match.Handler = r.MethodNotAllowedHandler + return true + } + + return false + } + // Closest match for a router (includes sub-routers) if r.NotFoundHandler != nil { match.Handler = r.NotFoundHandler + match.MatchErr = ErrNotFound return true } + + match.MatchErr = ErrNotFound return false } @@ -73,57 +171,71 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool { // When there is a match, the route variables can be retrieved calling // mux.Vars(request). func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // Clean path to canonical form and redirect. - if p := cleanPath(req.URL.Path); p != req.URL.Path { - - // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. - // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: - // http://code.google.com/p/go/issues/detail?id=5252 - url := *req.URL - url.Path = p - p = url.String() - - w.Header().Set("Location", p) - w.WriteHeader(http.StatusMovedPermanently) - return + if !r.skipClean { + path := req.URL.Path + if r.useEncodedPath { + path = req.URL.EscapedPath() + } + // Clean path to canonical form and redirect. + if p := cleanPath(path); p != path { + + // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. + // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: + // http://code.google.com/p/go/issues/detail?id=5252 + url := *req.URL + url.Path = p + p = url.String() + + w.Header().Set("Location", p) + w.WriteHeader(http.StatusMovedPermanently) + return + } } var match RouteMatch var handler http.Handler if r.Match(req, &match) { handler = match.Handler - setVars(req, match.Vars) - setCurrentRoute(req, match.Route) + req = requestWithVars(req, match.Vars) + req = requestWithRoute(req, match.Route) + } + + if handler == nil && match.MatchErr == ErrMethodMismatch { + handler = methodNotAllowedHandler() } + if handler == nil { handler = http.NotFoundHandler() } - if !r.KeepContext { - defer context.Clear(req) - } + handler.ServeHTTP(w, req) } // Get returns a route registered with the given name. func (r *Router) Get(name string) *Route { - return r.getNamedRoutes()[name] + return r.namedRoutes[name] } // GetRoute returns a route registered with the given name. This method // was renamed to Get() and remains here for backwards compatibility. func (r *Router) GetRoute(name string) *Route { - return r.getNamedRoutes()[name] + return r.namedRoutes[name] } // StrictSlash defines the trailing slash behavior for new routes. The initial // value is false. // -// When true, if the route path is "/path/", accessing "/path" will redirect +// When true, if the route path is "/path/", accessing "/path" will perform a redirect // to the former and vice versa. In other words, your application will always // see the path as specified in the route. // // When false, if the route path is "/path", accessing "/path/" will not match // this route and vice versa. // +// The re-direct is a HTTP 301 (Moved Permanently). Note that when this is set for +// routes with a non-idempotent method (e.g. POST, PUT), the subsequent re-directed +// request will be made as a GET by most clients. Use middleware or client settings +// to modify this behaviour as needed. +// // Special case: when a route sets a path prefix using the PathPrefix() method, // strict slash is ignored for that route because the redirect behavior can't // be determined from a prefix alone. However, any subrouters created from that @@ -133,35 +245,28 @@ func (r *Router) StrictSlash(value bool) *Router { return r } -// ---------------------------------------------------------------------------- -// parentRoute -// ---------------------------------------------------------------------------- - -// getNamedRoutes returns the map where named routes are registered. -func (r *Router) getNamedRoutes() map[string]*Route { - if r.namedRoutes == nil { - if r.parent != nil { - r.namedRoutes = r.parent.getNamedRoutes() - } else { - r.namedRoutes = make(map[string]*Route) - } - } - return r.namedRoutes -} - -// getRegexpGroup returns regexp definitions from the parent route, if any. -func (r *Router) getRegexpGroup() *routeRegexpGroup { - if r.parent != nil { - return r.parent.getRegexpGroup() - } - return nil +// SkipClean defines the path cleaning behaviour for new routes. The initial +// value is false. Users should be careful about which routes are not cleaned +// +// When true, if the route path is "/path//to", it will remain with the double +// slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/ +// +// When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will +// become /fetch/http/xkcd.com/534 +func (r *Router) SkipClean(value bool) *Router { + r.skipClean = value + return r } -func (r *Router) buildVars(m map[string]string) map[string]string { - if r.parent != nil { - m = r.parent.buildVars(m) - } - return m +// UseEncodedPath tells the router to match the encoded original path +// to the routes. +// For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to". +// +// If not called, the router will match the unencoded path to the routes. +// For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to" +func (r *Router) UseEncodedPath() *Router { + r.useEncodedPath = true + return r } // ---------------------------------------------------------------------------- @@ -170,11 +275,18 @@ func (r *Router) buildVars(m map[string]string) map[string]string { // NewRoute registers an empty route. func (r *Router) NewRoute() *Route { - route := &Route{parent: r, strictSlash: r.strictSlash} + // initialize a route with a copy of the parent router's configuration + route := &Route{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes} r.routes = append(r.routes, route) return route } +// Name registers a new route with a name. +// See Route.Name(). +func (r *Router) Name(name string) *Route { + return r.NewRoute().Name(name) +} + // Handle registers a new route with a matcher for the URL path. // See Route.Path() and Route.Handler(). func (r *Router) Handle(path string, handler http.Handler) *Route { @@ -260,20 +372,21 @@ type WalkFunc func(route *Route, router *Router, ancestors []*Route) error func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { for _, t := range r.routes { - if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" { - continue - } - err := walkFn(t, r, ancestors) if err == SkipRouter { continue } + if err != nil { + return err + } for _, sr := range t.matchers { if h, ok := sr.(*Router); ok { + ancestors = append(ancestors, t) err := h.walk(walkFn, ancestors) if err != nil { return err } + ancestors = ancestors[:len(ancestors)-1] } } if h, ok := t.handler.(*Router); ok { @@ -297,6 +410,11 @@ type RouteMatch struct { Route *Route Handler http.Handler Vars map[string]string + + // MatchErr is set to appropriate matching error + // It is set to ErrMethodMismatch if there is a mismatch in + // the request method and route method + MatchErr error } type contextKey int @@ -308,7 +426,7 @@ const ( // Vars returns the route variables for the current request, if any. func Vars(r *http.Request) map[string]string { - if rv := context.Get(r, varsKey); rv != nil { + if rv := r.Context().Value(varsKey); rv != nil { return rv.(map[string]string) } return nil @@ -317,25 +435,22 @@ func Vars(r *http.Request) map[string]string { // CurrentRoute returns the matched route for the current request, if any. // This only works when called inside the handler of the matched route // because the matched route is stored in the request context which is cleared -// after the handler returns, unless the KeepContext option is set on the -// Router. +// after the handler returns. func CurrentRoute(r *http.Request) *Route { - if rv := context.Get(r, routeKey); rv != nil { + if rv := r.Context().Value(routeKey); rv != nil { return rv.(*Route) } return nil } -func setVars(r *http.Request, val interface{}) { - if val != nil { - context.Set(r, varsKey, val) - } +func requestWithVars(r *http.Request, vars map[string]string) *http.Request { + ctx := context.WithValue(r.Context(), varsKey, vars) + return r.WithContext(ctx) } -func setCurrentRoute(r *http.Request, val interface{}) { - if val != nil { - context.Set(r, routeKey, val) - } +func requestWithRoute(r *http.Request, route *Route) *http.Request { + ctx := context.WithValue(r.Context(), routeKey, route) + return r.WithContext(ctx) } // ---------------------------------------------------------------------------- @@ -357,6 +472,7 @@ func cleanPath(p string) string { if p[len(p)-1] == '/' && np != "/" { np += "/" } + return np } @@ -397,7 +513,7 @@ func mapFromPairsToString(pairs ...string) (map[string]string, error) { return m, nil } -// mapFromPairsToRegex converts variadic string paramers to a +// mapFromPairsToRegex converts variadic string parameters to a // string to regex map. func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { length, err := checkPairs(pairs...) @@ -479,3 +595,12 @@ func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]s } return true } + +// methodNotAllowed replies to the request with an HTTP status code 405. +func methodNotAllowed(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusMethodNotAllowed) +} + +// methodNotAllowedHandler returns a simple request handler +// that replies to each request with a status code 405. +func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) } diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go index 08710bc984712..0144842bb23ee 100644 --- a/vendor/github.com/gorilla/mux/regexp.go +++ b/vendor/github.com/gorilla/mux/regexp.go @@ -14,6 +14,20 @@ import ( "strings" ) +type routeRegexpOptions struct { + strictSlash bool + useEncodedPath bool +} + +type regexpType int + +const ( + regexpTypePath regexpType = 0 + regexpTypeHost regexpType = 1 + regexpTypePrefix regexpType = 2 + regexpTypeQuery regexpType = 3 +) + // newRouteRegexp parses a route template and returns a routeRegexp, // used to match a host, a path or a query string. // @@ -24,7 +38,7 @@ import ( // Previously we accepted only Python-like identifiers for variable // names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that // name and pattern can't be empty, and names can't contain a colon. -func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) { +func newRouteRegexp(tpl string, typ regexpType, options routeRegexpOptions) (*routeRegexp, error) { // Check if it is well-formed. idxs, errBraces := braceIndices(tpl) if errBraces != nil { @@ -34,19 +48,18 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash template := tpl // Now let's parse it. defaultPattern := "[^/]+" - if matchQuery { - defaultPattern = "[^?&]*" - } else if matchHost { + if typ == regexpTypeQuery { + defaultPattern = ".*" + } else if typ == regexpTypeHost { defaultPattern = "[^.]+" - matchPrefix = false } // Only match strict slash if not matching - if matchPrefix || matchHost || matchQuery { - strictSlash = false + if typ != regexpTypePath { + options.strictSlash = false } // Set a flag for strictSlash. endSlash := false - if strictSlash && strings.HasSuffix(tpl, "/") { + if options.strictSlash && strings.HasSuffix(tpl, "/") { tpl = tpl[:len(tpl)-1] endSlash = true } @@ -88,18 +101,25 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash // Add the remaining. raw := tpl[end:] pattern.WriteString(regexp.QuoteMeta(raw)) - if strictSlash { + if options.strictSlash { pattern.WriteString("[/]?") } - if matchQuery { + if typ == regexpTypeQuery { // Add the default pattern if the query value is empty if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" { pattern.WriteString(defaultPattern) } } - if !matchPrefix { + if typ != regexpTypePrefix { pattern.WriteByte('$') } + + var wildcardHostPort bool + if typ == regexpTypeHost { + if !strings.Contains(pattern.String(), ":") { + wildcardHostPort = true + } + } reverse.WriteString(raw) if endSlash { reverse.WriteByte('/') @@ -109,16 +129,23 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash if errCompile != nil { return nil, errCompile } + + // Check for capturing groups which used to work in older versions + if reg.NumSubexp() != len(idxs)/2 { + panic(fmt.Sprintf("route %s contains capture groups in its regexp. ", template) + + "Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)") + } + // Done! return &routeRegexp{ - template: template, - matchHost: matchHost, - matchQuery: matchQuery, - strictSlash: strictSlash, - regexp: reg, - reverse: reverse.String(), - varsN: varsN, - varsR: varsR, + template: template, + regexpType: typ, + options: options, + regexp: reg, + reverse: reverse.String(), + varsN: varsN, + varsR: varsR, + wildcardHostPort: wildcardHostPort, }, nil } @@ -127,12 +154,10 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash type routeRegexp struct { // The unmodified template. template string - // True for host match, false for path or query string match. - matchHost bool - // True for query string match, false for path and host match. - matchQuery bool - // The strictSlash value defined on the route, but disabled if PathPrefix was used. - strictSlash bool + // The type of match + regexpType regexpType + // Options for matching + options routeRegexpOptions // Expanded regexp. regexp *regexp.Regexp // Reverse template. @@ -141,29 +166,44 @@ type routeRegexp struct { varsN []string // Variable regexps (validators). varsR []*regexp.Regexp + // Wildcard host-port (no strict port match in hostname) + wildcardHostPort bool } // Match matches the regexp against the URL host or path. func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool { - if !r.matchHost { - if r.matchQuery { - return r.matchQueryString(req) + if r.regexpType == regexpTypeHost { + host := getHost(req) + if r.wildcardHostPort { + // Don't be strict on the port match + if i := strings.Index(host, ":"); i != -1 { + host = host[:i] + } } - - return r.regexp.MatchString(req.URL.Path) + return r.regexp.MatchString(host) } - return r.regexp.MatchString(getHost(req)) + if r.regexpType == regexpTypeQuery { + return r.matchQueryString(req) + } + path := req.URL.Path + if r.options.useEncodedPath { + path = req.URL.EscapedPath() + } + return r.regexp.MatchString(path) } // url builds a URL part using the given values. func (r *routeRegexp) url(values map[string]string) (string, error) { - urlValues := make([]interface{}, len(r.varsN)) + urlValues := make([]interface{}, len(r.varsN), len(r.varsN)) for k, v := range r.varsN { value, ok := values[v] if !ok { return "", fmt.Errorf("mux: missing route variable %q", v) } + if r.regexpType == regexpTypeQuery { + value = url.QueryEscape(value) + } urlValues[k] = value } rv := fmt.Sprintf(r.reverse, urlValues...) @@ -186,18 +226,55 @@ func (r *routeRegexp) url(values map[string]string) (string, error) { // For a URL with foo=bar&baz=ding, we return only the relevant key // value pair for the routeRegexp. func (r *routeRegexp) getURLQuery(req *http.Request) string { - if !r.matchQuery { + if r.regexpType != regexpTypeQuery { return "" } templateKey := strings.SplitN(r.template, "=", 2)[0] - for key, vals := range req.URL.Query() { - if key == templateKey && len(vals) > 0 { - return key + "=" + vals[0] - } + val, ok := findFirstQueryKey(req.URL.RawQuery, templateKey) + if ok { + return templateKey + "=" + val } return "" } +// findFirstQueryKey returns the same result as (*url.URL).Query()[key][0]. +// If key was not found, empty string and false is returned. +func findFirstQueryKey(rawQuery, key string) (value string, ok bool) { + query := []byte(rawQuery) + for len(query) > 0 { + foundKey := query + if i := bytes.IndexAny(foundKey, "&;"); i >= 0 { + foundKey, query = foundKey[:i], foundKey[i+1:] + } else { + query = query[:0] + } + if len(foundKey) == 0 { + continue + } + var value []byte + if i := bytes.IndexByte(foundKey, '='); i >= 0 { + foundKey, value = foundKey[:i], foundKey[i+1:] + } + if len(foundKey) < len(key) { + // Cannot possibly be key. + continue + } + keyString, err := url.QueryUnescape(string(foundKey)) + if err != nil { + continue + } + if keyString != key { + continue + } + valueString, err := url.QueryUnescape(string(value)) + if err != nil { + continue + } + return valueString, true + } + return "", false +} + func (r *routeRegexp) matchQueryString(req *http.Request) bool { return r.regexp.MatchString(r.getURLQuery(req)) } @@ -244,23 +321,33 @@ type routeRegexpGroup struct { } // setMatch extracts the variables from the URL once a route matches. -func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) { +func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) { // Store host variables. if v.host != nil { host := getHost(req) + if v.host.wildcardHostPort { + // Don't be strict on the port match + if i := strings.Index(host, ":"); i != -1 { + host = host[:i] + } + } matches := v.host.regexp.FindStringSubmatchIndex(host) if len(matches) > 0 { extractVars(host, matches, v.host.varsN, m.Vars) } } + path := req.URL.Path + if r.useEncodedPath { + path = req.URL.EscapedPath() + } // Store path variables. if v.path != nil { - matches := v.path.regexp.FindStringSubmatchIndex(req.URL.Path) + matches := v.path.regexp.FindStringSubmatchIndex(path) if len(matches) > 0 { - extractVars(req.URL.Path, matches, v.path.varsN, m.Vars) + extractVars(path, matches, v.path.varsN, m.Vars) // Check if we should redirect. - if v.path.strictSlash { - p1 := strings.HasSuffix(req.URL.Path, "/") + if v.path.options.strictSlash { + p1 := strings.HasSuffix(path, "/") p2 := strings.HasSuffix(v.path.template, "/") if p1 != p2 { u, _ := url.Parse(req.URL.String()) @@ -269,7 +356,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) } else { u.Path += "/" } - m.Handler = http.RedirectHandler(u.String(), 301) + m.Handler = http.RedirectHandler(u.String(), http.StatusMovedPermanently) } } } @@ -285,28 +372,17 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) } // getHost tries its best to return the request host. +// According to section 14.23 of RFC 2616 the Host header +// can include the port number if the default value of 80 is not used. func getHost(r *http.Request) string { if r.URL.IsAbs() { return r.URL.Host } - host := r.Host - // Slice off any port information. - if i := strings.Index(host, ":"); i != -1 { - host = host[:i] - } - return host - + return r.Host } func extractVars(input string, matches []int, names []string, output map[string]string) { - matchesCount := 0 - prevEnd := -1 - for i := 2; i < len(matches) && matchesCount < len(names); i += 2 { - if prevEnd < matches[i+1] { - value := input[matches[i]:matches[i+1]] - output[names[matchesCount]] = value - prevEnd = matches[i+1] - matchesCount++ - } + for i, name := range names { + output[name] = input[matches[2*i+2]:matches[2*i+3]] } } diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go index bf92af2610526..750afe570d053 100644 --- a/vendor/github.com/gorilla/mux/route.go +++ b/vendor/github.com/gorilla/mux/route.go @@ -15,17 +15,8 @@ import ( // Route stores information to match a request and build URLs. type Route struct { - // Parent where the route was registered (a Router). - parent parentRoute // Request handler for the route. handler http.Handler - // List of matchers. - matchers []matcher - // Manager for the variables from host and path. - regexp *routeRegexpGroup - // If true, when the path pattern is "/path/", accessing "/path" will - // redirect to the former and vice versa. - strictSlash bool // If true, this route never matches: it is only used to build URLs. buildOnly bool // The name used to build URLs. @@ -33,7 +24,17 @@ type Route struct { // Error resulted from building a route. err error - buildVarsFunc BuildVarsFunc + // "global" reference to all named routes + namedRoutes map[string]*Route + + // config possibly passed in from `Router` + routeConf +} + +// SkipClean reports whether path cleaning is enabled for this route via +// Router.SkipClean. +func (r *Route) SkipClean() bool { + return r.skipClean } // Match matches the route against the request. @@ -41,12 +42,45 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { if r.buildOnly || r.err != nil { return false } + + var matchErr error + // Match everything. for _, m := range r.matchers { if matched := m.Match(req, match); !matched { + if _, ok := m.(methodMatcher); ok { + matchErr = ErrMethodMismatch + continue + } + + // Ignore ErrNotFound errors. These errors arise from match call + // to Subrouters. + // + // This prevents subsequent matching subrouters from failing to + // run middleware. If not ignored, the middleware would see a + // non-nil MatchErr and be skipped, even when there was a + // matching route. + if match.MatchErr == ErrNotFound { + match.MatchErr = nil + } + + matchErr = nil return false } } + + if matchErr != nil { + match.MatchErr = matchErr + return false + } + + if match.MatchErr == ErrMethodMismatch && r.handler != nil { + // We found a route which matches request method, clear MatchErr + match.MatchErr = nil + // Then override the mis-matched handler + match.Handler = r.handler + } + // Yay, we have a match. Let's collect some info about it. if match.Route == nil { match.Route = r @@ -57,10 +91,9 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { if match.Vars == nil { match.Vars = make(map[string]string) } + // Set variables. - if r.regexp != nil { - r.regexp.setMatch(req, match, r) - } + r.regexp.setMatch(req, match, r) return true } @@ -102,7 +135,7 @@ func (r *Route) GetHandler() http.Handler { // Name ----------------------------------------------------------------------- // Name sets the name for the route, used to build URLs. -// If the name was registered already it will be overwritten. +// It is an error to call Name more than once on a route. func (r *Route) Name(name string) *Route { if r.name != "" { r.err = fmt.Errorf("mux: route already has name %q, can't set %q", @@ -110,7 +143,7 @@ func (r *Route) Name(name string) *Route { } if r.err == nil { r.name = name - r.getNamedRoutes()[name] = r + r.namedRoutes[name] = r } return r } @@ -138,20 +171,22 @@ func (r *Route) addMatcher(m matcher) *Route { } // addRegexpMatcher adds a host or path matcher and builder to a route. -func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error { +func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error { if r.err != nil { return r.err } - r.regexp = r.getRegexpGroup() - if !matchHost && !matchQuery { - if len(tpl) == 0 || tpl[0] != '/' { + if typ == regexpTypePath || typ == regexpTypePrefix { + if len(tpl) > 0 && tpl[0] != '/' { return fmt.Errorf("mux: path must start with a slash, got %q", tpl) } if r.regexp.path != nil { tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl } } - rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash) + rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{ + strictSlash: r.strictSlash, + useEncodedPath: r.useEncodedPath, + }) if err != nil { return err } @@ -160,7 +195,7 @@ func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery return err } } - if matchHost { + if typ == regexpTypeHost { if r.regexp.path != nil { if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil { return err @@ -173,7 +208,7 @@ func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery return err } } - if matchQuery { + if typ == regexpTypeQuery { r.regexp.queries = append(r.regexp.queries, rr) } else { r.regexp.path = rr @@ -225,7 +260,8 @@ func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool { // "X-Requested-With", "XMLHttpRequest") // // The above route will only match if both the request header matches both regular expressions. -// It the value is an empty string, it will match any value if the key is set. +// If the value is an empty string, it will match any value if the key is set. +// Use the start and end of string anchors (^ and $) to match an exact value. func (r *Route) HeadersRegexp(pairs ...string) *Route { if r.err == nil { var headers map[string]*regexp.Regexp @@ -255,7 +291,7 @@ func (r *Route) HeadersRegexp(pairs ...string) *Route { // Variable names must be unique in a given route. They can be retrieved // calling mux.Vars(request). func (r *Route) Host(tpl string) *Route { - r.err = r.addRegexpMatcher(tpl, true, false, false) + r.err = r.addRegexpMatcher(tpl, regexpTypeHost) return r } @@ -315,7 +351,7 @@ func (r *Route) Methods(methods ...string) *Route { // Variable names must be unique in a given route. They can be retrieved // calling mux.Vars(request). func (r *Route) Path(tpl string) *Route { - r.err = r.addRegexpMatcher(tpl, false, false, false) + r.err = r.addRegexpMatcher(tpl, regexpTypePath) return r } @@ -331,7 +367,7 @@ func (r *Route) Path(tpl string) *Route { // Also note that the setting of Router.StrictSlash() has no effect on routes // with a PathPrefix matcher. func (r *Route) PathPrefix(tpl string) *Route { - r.err = r.addRegexpMatcher(tpl, false, true, false) + r.err = r.addRegexpMatcher(tpl, regexpTypePrefix) return r } @@ -347,7 +383,7 @@ func (r *Route) PathPrefix(tpl string) *Route { // The above route will only match if the URL contains the defined queries // values, e.g.: ?foo=bar&id=42. // -// It the value is an empty string, it will match any value if the key is set. +// If the value is an empty string, it will match any value if the key is set. // // Variables can define an optional regexp pattern to be matched: // @@ -362,7 +398,7 @@ func (r *Route) Queries(pairs ...string) *Route { return nil } for i := 0; i < length; i += 2 { - if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil { + if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], regexpTypeQuery); r.err != nil { return r } } @@ -376,15 +412,37 @@ func (r *Route) Queries(pairs ...string) *Route { type schemeMatcher []string func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool { - return matchInArray(m, r.URL.Scheme) + scheme := r.URL.Scheme + // https://golang.org/pkg/net/http/#Request + // "For [most] server requests, fields other than Path and RawQuery will be + // empty." + // Since we're an http muxer, the scheme is either going to be http or https + // though, so we can just set it based on the tls termination state. + if scheme == "" { + if r.TLS == nil { + scheme = "http" + } else { + scheme = "https" + } + } + return matchInArray(m, scheme) } // Schemes adds a matcher for URL schemes. // It accepts a sequence of schemes to be matched, e.g.: "http", "https". +// If the request's URL has a scheme set, it will be matched against. +// Generally, the URL scheme will only be set if a previous handler set it, +// such as the ProxyHeaders handler from gorilla/handlers. +// If unset, the scheme will be determined based on the request's TLS +// termination state. +// The first argument to Schemes will be used when constructing a route URL. func (r *Route) Schemes(schemes ...string) *Route { for k, v := range schemes { schemes[k] = strings.ToLower(v) } + if len(schemes) > 0 { + r.buildScheme = schemes[0] + } return r.addMatcher(schemeMatcher(schemes)) } @@ -397,7 +455,15 @@ type BuildVarsFunc func(map[string]string) map[string]string // BuildVarsFunc adds a custom function to be used to modify build variables // before a route's URL is built. func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { - r.buildVarsFunc = f + if r.buildVarsFunc != nil { + // compose the old and new functions + old := r.buildVarsFunc + r.buildVarsFunc = func(m map[string]string) map[string]string { + return f(old(m)) + } + } else { + r.buildVarsFunc = f + } return r } @@ -416,7 +482,8 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route { // Here, the routes registered in the subrouter won't be tested if the host // doesn't match. func (r *Route) Subrouter() *Router { - router := &Router{parent: r, strictSlash: r.strictSlash} + // initialize a subrouter with a copy of the parent route's configuration + router := &Router{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes} r.addMatcher(router) return router } @@ -445,8 +512,8 @@ func (r *Route) Subrouter() *Router { // This also works for host variables: // // r := mux.NewRouter() -// r.Host("{subdomain}.domain.com"). -// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). +// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler). +// Host("{subdomain}.domain.com"). // Name("article") // // // url.String() will be "http://news.domain.com/articles/technology/42" @@ -454,36 +521,51 @@ func (r *Route) Subrouter() *Router { // "category", "technology", // "id", "42") // +// The scheme of the resulting url will be the first argument that was passed to Schemes: +// +// // url.String() will be "https://example.com" +// r := mux.NewRouter() +// url, err := r.Host("example.com") +// .Schemes("https", "http").URL() +// // All variables defined in the route are required, and their values must // conform to the corresponding patterns. func (r *Route) URL(pairs ...string) (*url.URL, error) { if r.err != nil { return nil, r.err } - if r.regexp == nil { - return nil, errors.New("mux: route doesn't have a host or path") - } values, err := r.prepareVars(pairs...) if err != nil { return nil, err } var scheme, host, path string + queries := make([]string, 0, len(r.regexp.queries)) if r.regexp.host != nil { - // Set a default scheme. - scheme = "http" if host, err = r.regexp.host.url(values); err != nil { return nil, err } + scheme = "http" + if r.buildScheme != "" { + scheme = r.buildScheme + } } if r.regexp.path != nil { if path, err = r.regexp.path.url(values); err != nil { return nil, err } } + for _, q := range r.regexp.queries { + var query string + if query, err = q.url(values); err != nil { + return nil, err + } + queries = append(queries, query) + } return &url.URL{ - Scheme: scheme, - Host: host, - Path: path, + Scheme: scheme, + Host: host, + Path: path, + RawQuery: strings.Join(queries, "&"), }, nil } @@ -494,7 +576,7 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) { if r.err != nil { return nil, r.err } - if r.regexp == nil || r.regexp.host == nil { + if r.regexp.host == nil { return nil, errors.New("mux: route doesn't have a host") } values, err := r.prepareVars(pairs...) @@ -505,10 +587,14 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) { if err != nil { return nil, err } - return &url.URL{ + u := &url.URL{ Scheme: "http", Host: host, - }, nil + } + if r.buildScheme != "" { + u.Scheme = r.buildScheme + } + return u, nil } // URLPath builds the path part of the URL for a route. See Route.URL(). @@ -518,7 +604,7 @@ func (r *Route) URLPath(pairs ...string) (*url.URL, error) { if r.err != nil { return nil, r.err } - if r.regexp == nil || r.regexp.path == nil { + if r.regexp.path == nil { return nil, errors.New("mux: route doesn't have a path") } values, err := r.prepareVars(pairs...) @@ -543,12 +629,80 @@ func (r *Route) GetPathTemplate() (string, error) { if r.err != nil { return "", r.err } - if r.regexp == nil || r.regexp.path == nil { + if r.regexp.path == nil { return "", errors.New("mux: route doesn't have a path") } return r.regexp.path.template, nil } +// GetPathRegexp returns the expanded regular expression used to match route path. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An error will be returned if the route does not define a path. +func (r *Route) GetPathRegexp() (string, error) { + if r.err != nil { + return "", r.err + } + if r.regexp.path == nil { + return "", errors.New("mux: route does not have a path") + } + return r.regexp.path.regexp.String(), nil +} + +// GetQueriesRegexp returns the expanded regular expressions used to match the +// route queries. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An error will be returned if the route does not have queries. +func (r *Route) GetQueriesRegexp() ([]string, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp.queries == nil { + return nil, errors.New("mux: route doesn't have queries") + } + queries := make([]string, 0, len(r.regexp.queries)) + for _, query := range r.regexp.queries { + queries = append(queries, query.regexp.String()) + } + return queries, nil +} + +// GetQueriesTemplates returns the templates used to build the +// query matching. +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An error will be returned if the route does not define queries. +func (r *Route) GetQueriesTemplates() ([]string, error) { + if r.err != nil { + return nil, r.err + } + if r.regexp.queries == nil { + return nil, errors.New("mux: route doesn't have queries") + } + queries := make([]string, 0, len(r.regexp.queries)) + for _, query := range r.regexp.queries { + queries = append(queries, query.template) + } + return queries, nil +} + +// GetMethods returns the methods the route matches against +// This is useful for building simple REST API documentation and for instrumentation +// against third-party services. +// An error will be returned if route does not have methods. +func (r *Route) GetMethods() ([]string, error) { + if r.err != nil { + return nil, r.err + } + for _, m := range r.matchers { + if methods, ok := m.(methodMatcher); ok { + return []string(methods), nil + } + } + return nil, errors.New("mux: route doesn't have methods") +} + // GetHostTemplate returns the template used to build the // route match. // This is useful for building simple REST API documentation and for instrumentation @@ -558,7 +712,7 @@ func (r *Route) GetHostTemplate() (string, error) { if r.err != nil { return "", r.err } - if r.regexp == nil || r.regexp.host == nil { + if r.regexp.host == nil { return "", errors.New("mux: route doesn't have a host") } return r.regexp.host.template, nil @@ -575,53 +729,8 @@ func (r *Route) prepareVars(pairs ...string) (map[string]string, error) { } func (r *Route) buildVars(m map[string]string) map[string]string { - if r.parent != nil { - m = r.parent.buildVars(m) - } if r.buildVarsFunc != nil { m = r.buildVarsFunc(m) } return m } - -// ---------------------------------------------------------------------------- -// parentRoute -// ---------------------------------------------------------------------------- - -// parentRoute allows routes to know about parent host and path definitions. -type parentRoute interface { - getNamedRoutes() map[string]*Route - getRegexpGroup() *routeRegexpGroup - buildVars(map[string]string) map[string]string -} - -// getNamedRoutes returns the map where named routes are registered. -func (r *Route) getNamedRoutes() map[string]*Route { - if r.parent == nil { - // During tests router is not always set. - r.parent = NewRouter() - } - return r.parent.getNamedRoutes() -} - -// getRegexpGroup returns regexp definitions from this route. -func (r *Route) getRegexpGroup() *routeRegexpGroup { - if r.regexp == nil { - if r.parent == nil { - // During tests router is not always set. - r.parent = NewRouter() - } - regexp := r.parent.getRegexpGroup() - if regexp == nil { - r.regexp = new(routeRegexpGroup) - } else { - // Copy. - r.regexp = &routeRegexpGroup{ - host: regexp.host, - path: regexp.path, - queries: regexp.queries, - } - } - } - return r.regexp -} diff --git a/vendor/github.com/gorilla/mux/test_helpers.go b/vendor/github.com/gorilla/mux/test_helpers.go new file mode 100644 index 0000000000000..5f5c496de0129 --- /dev/null +++ b/vendor/github.com/gorilla/mux/test_helpers.go @@ -0,0 +1,19 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mux + +import "net/http" + +// SetURLVars sets the URL variables for the given request, to be accessed via +// mux.Vars for testing route behaviour. Arguments are not modified, a shallow +// copy is returned. +// +// This API should only be used for testing purposes; it provides a way to +// inject variables into the request context. Alternatively, URL variables +// can be set by making a route that captures the required variables, +// starting a server and sending the request to that server. +func SetURLVars(r *http.Request, val map[string]string) *http.Request { + return requestWithVars(r, val) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE new file mode 100644 index 0000000000000..b2b065037fc4d --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. \ No newline at end of file diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md new file mode 100644 index 0000000000000..3a4cc2175e52c --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/README.md @@ -0,0 +1,85 @@ +# Go gRPC Middleware + +[![Travis Build](https://travis-ci.org/grpc-ecosystem/go-grpc-middleware.svg?branch=master)](https://travis-ci.org/grpc-ecosystem/go-grpc-middleware) +[![Go Report Card](https://goreportcard.com/badge/github.com/grpc-ecosystem/go-grpc-middleware)](https://goreportcard.com/report/github.com/grpc-ecosystem/go-grpc-middleware) +[![GoDoc](http://img.shields.io/badge/GoDoc-Reference-blue.svg)](https://godoc.org/github.com/grpc-ecosystem/go-grpc-middleware) +[![SourceGraph](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-middleware/-/badge.svg)](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-middleware/?badge) +[![codecov](https://codecov.io/gh/grpc-ecosystem/go-grpc-middleware/branch/master/graph/badge.svg)](https://codecov.io/gh/grpc-ecosystem/go-grpc-middleware) +[![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) +[![quality: production](https://img.shields.io/badge/quality-production-orange.svg)](#status) +[![Slack](https://img.shields.io/badge/slack-%23grpc--middleware-brightgreen)](https://slack.com/share/IRUQCFC23/9Tm7hxRFVKKNoajQfMOcUiIk/enQtODc4ODI4NTIyMDcxLWM5NDA0ZTE4Njg5YjRjYWZkMTI5MzQwNDY3YzBjMzE1YzdjOGM5ZjI1NDNiM2JmNzI2YjM5ODE5OTRiNTEyOWE) + +[gRPC Go](https://github.com/grpc/grpc-go) Middleware: interceptors, helpers, utilities. + +## Middleware + +[gRPC Go](https://github.com/grpc/grpc-go) recently acquired support for +Interceptors, i.e. [middleware](https://medium.com/@matryer/writing-middleware-in-golang-and-how-go-makes-it-so-much-fun-4375c1246e81#.gv7tdlghs) +that is executed either on the gRPC Server before the request is passed onto the user's application logic, or on the gRPC client either around the user call. It is a perfect way to implement +common patterns: auth, logging, message, validation, retries or monitoring. + +These are generic building blocks that make it easy to build multiple microservices easily. +The purpose of this repository is to act as a go-to point for such reusable functionality. It contains +some of them itself, but also will link to useful external repos. + +`grpc_middleware` itself provides support for chaining interceptors, here's an example: + +```go +import "github.com/grpc-ecosystem/go-grpc-middleware" + +myServer := grpc.NewServer( + grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( + grpc_ctxtags.StreamServerInterceptor(), + grpc_opentracing.StreamServerInterceptor(), + grpc_prometheus.StreamServerInterceptor, + grpc_zap.StreamServerInterceptor(zapLogger), + grpc_auth.StreamServerInterceptor(myAuthFunction), + grpc_recovery.StreamServerInterceptor(), + )), + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( + grpc_ctxtags.UnaryServerInterceptor(), + grpc_opentracing.UnaryServerInterceptor(), + grpc_prometheus.UnaryServerInterceptor, + grpc_zap.UnaryServerInterceptor(zapLogger), + grpc_auth.UnaryServerInterceptor(myAuthFunction), + grpc_recovery.UnaryServerInterceptor(), + )), +) +``` + +## Interceptors + +*Please send a PR to add new interceptors or middleware to this list* + +#### Auth + * [`grpc_auth`](auth) - a customizable (via `AuthFunc`) piece of auth middleware + +#### Logging + * [`grpc_ctxtags`](tags/) - a library that adds a `Tag` map to context, with data populated from request body + * [`grpc_zap`](logging/zap/) - integration of [zap](https://github.com/uber-go/zap) logging library into gRPC handlers. + * [`grpc_logrus`](logging/logrus/) - integration of [logrus](https://github.com/sirupsen/logrus) logging library into gRPC handlers. + * [`grpc_kit`](logging/kit/) - integration of [go-kit](https://github.com/go-kit/kit/tree/master/log) logging library into gRPC handlers. + +#### Monitoring + * [`grpc_prometheus`⚡](https://github.com/grpc-ecosystem/go-grpc-prometheus) - Prometheus client-side and server-side monitoring middleware + * [`otgrpc`⚡](https://github.com/grpc-ecosystem/grpc-opentracing/tree/master/go/otgrpc) - [OpenTracing](http://opentracing.io/) client-side and server-side interceptors + * [`grpc_opentracing`](tracing/opentracing) - [OpenTracing](http://opentracing.io/) client-side and server-side interceptors with support for streaming and handler-returned tags + +#### Client + * [`grpc_retry`](retry/) - a generic gRPC response code retry mechanism, client-side middleware + +#### Server + * [`grpc_validator`](validator/) - codegen inbound message validation from `.proto` options + * [`grpc_recovery`](recovery/) - turn panics into gRPC errors + * [`ratelimit`](ratelimit/) - grpc rate limiting by your own limiter + + +## Status + +This code has been running in *production* since May 2016 as the basis of the gRPC micro services stack at [Improbable](https://improbable.io). + +Additional tooling will be added, and contributions are welcome. + +## License + +`go-grpc-middleware` is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details. diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go new file mode 100644 index 0000000000000..ea3738b896cfd --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/chain.go @@ -0,0 +1,120 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +// gRPC Server Interceptor chaining middleware. + +package grpc_middleware + +import ( + "context" + + "google.golang.org/grpc" +) + +// ChainUnaryServer creates a single interceptor out of a chain of many interceptors. +// +// Execution is done in left-to-right order, including passing of context. +// For example ChainUnaryServer(one, two, three) will execute one before two before three, and three +// will see context changes of one and two. +func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor { + n := len(interceptors) + + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + chainer := func(currentInter grpc.UnaryServerInterceptor, currentHandler grpc.UnaryHandler) grpc.UnaryHandler { + return func(currentCtx context.Context, currentReq interface{}) (interface{}, error) { + return currentInter(currentCtx, currentReq, info, currentHandler) + } + } + + chainedHandler := handler + for i := n - 1; i >= 0; i-- { + chainedHandler = chainer(interceptors[i], chainedHandler) + } + + return chainedHandler(ctx, req) + } +} + +// ChainStreamServer creates a single interceptor out of a chain of many interceptors. +// +// Execution is done in left-to-right order, including passing of context. +// For example ChainUnaryServer(one, two, three) will execute one before two before three. +// If you want to pass context between interceptors, use WrapServerStream. +func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor { + n := len(interceptors) + + return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + chainer := func(currentInter grpc.StreamServerInterceptor, currentHandler grpc.StreamHandler) grpc.StreamHandler { + return func(currentSrv interface{}, currentStream grpc.ServerStream) error { + return currentInter(currentSrv, currentStream, info, currentHandler) + } + } + + chainedHandler := handler + for i := n - 1; i >= 0; i-- { + chainedHandler = chainer(interceptors[i], chainedHandler) + } + + return chainedHandler(srv, ss) + } +} + +// ChainUnaryClient creates a single interceptor out of a chain of many interceptors. +// +// Execution is done in left-to-right order, including passing of context. +// For example ChainUnaryClient(one, two, three) will execute one before two before three. +func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor { + n := len(interceptors) + + return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + chainer := func(currentInter grpc.UnaryClientInterceptor, currentInvoker grpc.UnaryInvoker) grpc.UnaryInvoker { + return func(currentCtx context.Context, currentMethod string, currentReq, currentRepl interface{}, currentConn *grpc.ClientConn, currentOpts ...grpc.CallOption) error { + return currentInter(currentCtx, currentMethod, currentReq, currentRepl, currentConn, currentInvoker, currentOpts...) + } + } + + chainedInvoker := invoker + for i := n - 1; i >= 0; i-- { + chainedInvoker = chainer(interceptors[i], chainedInvoker) + } + + return chainedInvoker(ctx, method, req, reply, cc, opts...) + } +} + +// ChainStreamClient creates a single interceptor out of a chain of many interceptors. +// +// Execution is done in left-to-right order, including passing of context. +// For example ChainStreamClient(one, two, three) will execute one before two before three. +func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor { + n := len(interceptors) + + return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { + chainer := func(currentInter grpc.StreamClientInterceptor, currentStreamer grpc.Streamer) grpc.Streamer { + return func(currentCtx context.Context, currentDesc *grpc.StreamDesc, currentConn *grpc.ClientConn, currentMethod string, currentOpts ...grpc.CallOption) (grpc.ClientStream, error) { + return currentInter(currentCtx, currentDesc, currentConn, currentMethod, currentStreamer, currentOpts...) + } + } + + chainedStreamer := streamer + for i := n - 1; i >= 0; i-- { + chainedStreamer = chainer(interceptors[i], chainedStreamer) + } + + return chainedStreamer(ctx, desc, cc, method, opts...) + } +} + +// Chain creates a single interceptor out of a chain of many interceptors. +// +// WithUnaryServerChain is a grpc.Server config option that accepts multiple unary interceptors. +// Basically syntactic sugar. +func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption { + return grpc.UnaryInterceptor(ChainUnaryServer(interceptors...)) +} + +// WithStreamServerChain is a grpc.Server config option that accepts multiple stream interceptors. +// Basically syntactic sugar. +func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption { + return grpc.StreamInterceptor(ChainStreamServer(interceptors...)) +} diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go new file mode 100644 index 0000000000000..716895036423b --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/doc.go @@ -0,0 +1,69 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +/* +`grpc_middleware` is a collection of gRPC middleware packages: interceptors, helpers and tools. + +Middleware + +gRPC is a fantastic RPC middleware, which sees a lot of adoption in the Golang world. However, the +upstream gRPC codebase is relatively bare bones. + +This package, and most of its child packages provides commonly needed middleware for gRPC: +client-side interceptors for retires, server-side interceptors for input validation and auth, +functions for chaining said interceptors, metadata convenience methods and more. + +Chaining + +By default, gRPC doesn't allow one to have more than one interceptor either on the client nor on +the server side. `grpc_middleware` provides convenient chaining methods + +Simple way of turning a multiple interceptors into a single interceptor. Here's an example for +server chaining: + + myServer := grpc.NewServer( + grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(loggingStream, monitoringStream, authStream)), + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(loggingUnary, monitoringUnary, authUnary), + ) + +These interceptors will be executed from left to right: logging, monitoring and auth. + +Here's an example for client side chaining: + + clientConn, err = grpc.Dial( + address, + grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(monitoringClientUnary, retryUnary)), + grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(monitoringClientStream, retryStream)), + ) + client = pb_testproto.NewTestServiceClient(clientConn) + resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"}) + +These interceptors will be executed from left to right: monitoring and then retry logic. + +The retry interceptor will call every interceptor that follows it whenever when a retry happens. + +Writing Your Own + +Implementing your own interceptor is pretty trivial: there are interfaces for that. But the interesting +bit exposing common data to handlers (and other middleware), similarly to HTTP Middleware design. +For example, you may want to pass the identity of the caller from the auth interceptor all the way +to the handling function. + +For example, a client side interceptor example for auth looks like: + + func FakeAuthUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + newCtx := context.WithValue(ctx, "user_id", "john@example.com") + return handler(newCtx, req) + } + +Unfortunately, it's not as easy for streaming RPCs. These have the `context.Context` embedded within +the `grpc.ServerStream` object. To pass values through context, a wrapper (`WrappedServerStream`) is +needed. For example: + + func FakeAuthStreamingInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + newStream := grpc_middleware.WrapServerStream(stream) + newStream.WrappedContext = context.WithValue(ctx, "user_id", "john@example.com") + return handler(srv, stream) + } +*/ +package grpc_middleware diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/go.mod b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/go.mod new file mode 100644 index 0000000000000..6f8eeac43d2b1 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/go.mod @@ -0,0 +1,22 @@ +module github.com/grpc-ecosystem/go-grpc-middleware + +require ( + github.com/go-kit/kit v0.9.0 + github.com/go-logfmt/logfmt v0.4.0 // indirect + github.com/go-stack/stack v1.8.0 // indirect + github.com/gogo/protobuf v1.2.1 + github.com/golang/protobuf v1.3.2 + github.com/opentracing/opentracing-go v1.1.0 + github.com/pkg/errors v0.8.1 // indirect + github.com/sirupsen/logrus v1.4.2 + github.com/stretchr/testify v1.4.0 + go.uber.org/atomic v1.4.0 // indirect + go.uber.org/multierr v1.1.0 // indirect + go.uber.org/zap v1.10.0 + golang.org/x/net v0.0.0-20190311183353-d8887717615a + golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be + google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect + google.golang.org/grpc v1.19.0 +) + +go 1.13 diff --git a/vendor/github.com/grpc-ecosystem/go-grpc-middleware/wrappers.go b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/wrappers.go new file mode 100644 index 0000000000000..05ccfb3f24a79 --- /dev/null +++ b/vendor/github.com/grpc-ecosystem/go-grpc-middleware/wrappers.go @@ -0,0 +1,30 @@ +// Copyright 2016 Michal Witkowski. All Rights Reserved. +// See LICENSE for licensing terms. + +package grpc_middleware + +import ( + "context" + + "google.golang.org/grpc" +) + +// WrappedServerStream is a thin wrapper around grpc.ServerStream that allows modifying context. +type WrappedServerStream struct { + grpc.ServerStream + // WrappedContext is the wrapper's own Context. You can assign it. + WrappedContext context.Context +} + +// Context returns the wrapper's WrappedContext, overwriting the nested grpc.ServerStream.Context() +func (w *WrappedServerStream) Context() context.Context { + return w.WrappedContext +} + +// WrapServerStream returns a ServerStream that has the ability to overwrite context. +func WrapServerStream(stream grpc.ServerStream) *WrappedServerStream { + if existing, ok := stream.(*WrappedServerStream); ok { + return existing + } + return &WrappedServerStream{ServerStream: stream, WrappedContext: stream.Context()} +} diff --git a/vendor/github.com/hashicorp/errwrap/LICENSE b/vendor/github.com/hashicorp/errwrap/LICENSE new file mode 100644 index 0000000000000..c33dcc7c928c6 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/LICENSE @@ -0,0 +1,354 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. + diff --git a/vendor/github.com/hashicorp/errwrap/README.md b/vendor/github.com/hashicorp/errwrap/README.md new file mode 100644 index 0000000000000..444df08f8e775 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/README.md @@ -0,0 +1,89 @@ +# errwrap + +`errwrap` is a package for Go that formalizes the pattern of wrapping errors +and checking if an error contains another error. + +There is a common pattern in Go of taking a returned `error` value and +then wrapping it (such as with `fmt.Errorf`) before returning it. The problem +with this pattern is that you completely lose the original `error` structure. + +Arguably the _correct_ approach is that you should make a custom structure +implementing the `error` interface, and have the original error as a field +on that structure, such [as this example](http://golang.org/pkg/os/#PathError). +This is a good approach, but you have to know the entire chain of possible +rewrapping that happens, when you might just care about one. + +`errwrap` formalizes this pattern (it doesn't matter what approach you use +above) by giving a single interface for wrapping errors, checking if a specific +error is wrapped, and extracting that error. + +## Installation and Docs + +Install using `go get github.com/hashicorp/errwrap`. + +Full documentation is available at +http://godoc.org/github.com/hashicorp/errwrap + +## Usage + +#### Basic Usage + +Below is a very basic example of its usage: + +```go +// A function that always returns an error, but wraps it, like a real +// function might. +func tryOpen() error { + _, err := os.Open("/i/dont/exist") + if err != nil { + return errwrap.Wrapf("Doesn't exist: {{err}}", err) + } + + return nil +} + +func main() { + err := tryOpen() + + // We can use the Contains helpers to check if an error contains + // another error. It is safe to do this with a nil error, or with + // an error that doesn't even use the errwrap package. + if errwrap.Contains(err, "does not exist") { + // Do something + } + if errwrap.ContainsType(err, new(os.PathError)) { + // Do something + } + + // Or we can use the associated `Get` functions to just extract + // a specific error. This would return nil if that specific error doesn't + // exist. + perr := errwrap.GetType(err, new(os.PathError)) +} +``` + +#### Custom Types + +If you're already making custom types that properly wrap errors, then +you can get all the functionality of `errwraps.Contains` and such by +implementing the `Wrapper` interface with just one function. Example: + +```go +type AppError { + Code ErrorCode + Err error +} + +func (e *AppError) WrappedErrors() []error { + return []error{e.Err} +} +``` + +Now this works: + +```go +err := &AppError{Err: fmt.Errorf("an error")} +if errwrap.ContainsType(err, fmt.Errorf("")) { + // This will work! +} +``` diff --git a/vendor/github.com/hashicorp/errwrap/errwrap.go b/vendor/github.com/hashicorp/errwrap/errwrap.go new file mode 100644 index 0000000000000..a733bef18c059 --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/errwrap.go @@ -0,0 +1,169 @@ +// Package errwrap implements methods to formalize error wrapping in Go. +// +// All of the top-level functions that take an `error` are built to be able +// to take any error, not just wrapped errors. This allows you to use errwrap +// without having to type-check and type-cast everywhere. +package errwrap + +import ( + "errors" + "reflect" + "strings" +) + +// WalkFunc is the callback called for Walk. +type WalkFunc func(error) + +// Wrapper is an interface that can be implemented by custom types to +// have all the Contains, Get, etc. functions in errwrap work. +// +// When Walk reaches a Wrapper, it will call the callback for every +// wrapped error in addition to the wrapper itself. Since all the top-level +// functions in errwrap use Walk, this means that all those functions work +// with your custom type. +type Wrapper interface { + WrappedErrors() []error +} + +// Wrap defines that outer wraps inner, returning an error type that +// can be cleanly used with the other methods in this package, such as +// Contains, GetAll, etc. +// +// This function won't modify the error message at all (the outer message +// will be used). +func Wrap(outer, inner error) error { + return &wrappedError{ + Outer: outer, + Inner: inner, + } +} + +// Wrapf wraps an error with a formatting message. This is similar to using +// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap +// errors, you should replace it with this. +// +// format is the format of the error message. The string '{{err}}' will +// be replaced with the original error message. +func Wrapf(format string, err error) error { + outerMsg := "" + if err != nil { + outerMsg = err.Error() + } + + outer := errors.New(strings.Replace( + format, "{{err}}", outerMsg, -1)) + + return Wrap(outer, err) +} + +// Contains checks if the given error contains an error with the +// message msg. If err is not a wrapped error, this will always return +// false unless the error itself happens to match this msg. +func Contains(err error, msg string) bool { + return len(GetAll(err, msg)) > 0 +} + +// ContainsType checks if the given error contains an error with +// the same concrete type as v. If err is not a wrapped error, this will +// check the err itself. +func ContainsType(err error, v interface{}) bool { + return len(GetAllType(err, v)) > 0 +} + +// Get is the same as GetAll but returns the deepest matching error. +func Get(err error, msg string) error { + es := GetAll(err, msg) + if len(es) > 0 { + return es[len(es)-1] + } + + return nil +} + +// GetType is the same as GetAllType but returns the deepest matching error. +func GetType(err error, v interface{}) error { + es := GetAllType(err, v) + if len(es) > 0 { + return es[len(es)-1] + } + + return nil +} + +// GetAll gets all the errors that might be wrapped in err with the +// given message. The order of the errors is such that the outermost +// matching error (the most recent wrap) is index zero, and so on. +func GetAll(err error, msg string) []error { + var result []error + + Walk(err, func(err error) { + if err.Error() == msg { + result = append(result, err) + } + }) + + return result +} + +// GetAllType gets all the errors that are the same type as v. +// +// The order of the return value is the same as described in GetAll. +func GetAllType(err error, v interface{}) []error { + var result []error + + var search string + if v != nil { + search = reflect.TypeOf(v).String() + } + Walk(err, func(err error) { + var needle string + if err != nil { + needle = reflect.TypeOf(err).String() + } + + if needle == search { + result = append(result, err) + } + }) + + return result +} + +// Walk walks all the wrapped errors in err and calls the callback. If +// err isn't a wrapped error, this will be called once for err. If err +// is a wrapped error, the callback will be called for both the wrapper +// that implements error as well as the wrapped error itself. +func Walk(err error, cb WalkFunc) { + if err == nil { + return + } + + switch e := err.(type) { + case *wrappedError: + cb(e.Outer) + Walk(e.Inner, cb) + case Wrapper: + cb(err) + + for _, err := range e.WrappedErrors() { + Walk(err, cb) + } + default: + cb(err) + } +} + +// wrappedError is an implementation of error that has both the +// outer and inner errors. +type wrappedError struct { + Outer error + Inner error +} + +func (w *wrappedError) Error() string { + return w.Outer.Error() +} + +func (w *wrappedError) WrappedErrors() []error { + return []error{w.Outer, w.Inner} +} diff --git a/vendor/github.com/hashicorp/errwrap/go.mod b/vendor/github.com/hashicorp/errwrap/go.mod new file mode 100644 index 0000000000000..c9b84022cf7ae --- /dev/null +++ b/vendor/github.com/hashicorp/errwrap/go.mod @@ -0,0 +1 @@ +module github.com/hashicorp/errwrap diff --git a/vendor/github.com/hashicorp/go-multierror/README.md b/vendor/github.com/hashicorp/go-multierror/README.md index e81be50e0d31c..ead5830f7b764 100644 --- a/vendor/github.com/hashicorp/go-multierror/README.md +++ b/vendor/github.com/hashicorp/go-multierror/README.md @@ -1,5 +1,11 @@ # go-multierror +[![Build Status](http://img.shields.io/travis/hashicorp/go-multierror.svg?style=flat-square)][travis] +[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs] + +[travis]: https://travis-ci.org/hashicorp/go-multierror +[godocs]: https://godoc.org/github.com/hashicorp/go-multierror + `go-multierror` is a package for Go that provides a mechanism for representing a list of `error` values as a single `error`. diff --git a/vendor/github.com/hashicorp/go-multierror/append.go b/vendor/github.com/hashicorp/go-multierror/append.go index 8d22ee7a0ea8d..775b6e753e77b 100644 --- a/vendor/github.com/hashicorp/go-multierror/append.go +++ b/vendor/github.com/hashicorp/go-multierror/append.go @@ -14,7 +14,20 @@ func Append(err error, errs ...error) *Error { err = new(Error) } - err.Errors = append(err.Errors, errs...) + // Go through each error and flatten + for _, e := range errs { + switch e := e.(type) { + case *Error: + if e != nil { + err.Errors = append(err.Errors, e.Errors...) + } + default: + if e != nil { + err.Errors = append(err.Errors, e) + } + } + } + return err default: newErrs := make([]error, 0, len(errs)+1) @@ -23,8 +36,6 @@ func Append(err error, errs ...error) *Error { } newErrs = append(newErrs, errs...) - return &Error{ - Errors: newErrs, - } + return Append(&Error{}, newErrs...) } } diff --git a/vendor/github.com/hashicorp/go-multierror/flatten.go b/vendor/github.com/hashicorp/go-multierror/flatten.go new file mode 100644 index 0000000000000..aab8e9abec9d8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/flatten.go @@ -0,0 +1,26 @@ +package multierror + +// Flatten flattens the given error, merging any *Errors together into +// a single *Error. +func Flatten(err error) error { + // If it isn't an *Error, just return the error as-is + if _, ok := err.(*Error); !ok { + return err + } + + // Otherwise, make the result and flatten away! + flatErr := new(Error) + flatten(err, flatErr) + return flatErr +} + +func flatten(err error, flatErr *Error) { + switch err := err.(type) { + case *Error: + for _, e := range err.Errors { + flatten(e, flatErr) + } + default: + flatErr.Errors = append(flatErr.Errors, err) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/format.go b/vendor/github.com/hashicorp/go-multierror/format.go index bb65a12e743af..47f13c49a673e 100644 --- a/vendor/github.com/hashicorp/go-multierror/format.go +++ b/vendor/github.com/hashicorp/go-multierror/format.go @@ -12,12 +12,16 @@ type ErrorFormatFunc func([]error) string // ListFormatFunc is a basic formatter that outputs the number of errors // that occurred along with a bullet point list of the errors. func ListFormatFunc(es []error) string { + if len(es) == 1 { + return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", es[0]) + } + points := make([]string, len(es)) for i, err := range es { points[i] = fmt.Sprintf("* %s", err) } return fmt.Sprintf( - "%d error(s) occurred:\n\n%s", - len(es), strings.Join(points, "\n")) + "%d errors occurred:\n\t%s\n\n", + len(es), strings.Join(points, "\n\t")) } diff --git a/vendor/github.com/hashicorp/go-multierror/go.mod b/vendor/github.com/hashicorp/go-multierror/go.mod new file mode 100644 index 0000000000000..2534331d5f9cb --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/go.mod @@ -0,0 +1,3 @@ +module github.com/hashicorp/go-multierror + +require github.com/hashicorp/errwrap v1.0.0 diff --git a/vendor/github.com/hashicorp/go-multierror/multierror.go b/vendor/github.com/hashicorp/go-multierror/multierror.go index 2ea0827329036..89b1422d1d17a 100644 --- a/vendor/github.com/hashicorp/go-multierror/multierror.go +++ b/vendor/github.com/hashicorp/go-multierror/multierror.go @@ -40,11 +40,11 @@ func (e *Error) GoString() string { } // WrappedErrors returns the list of errors that this Error is wrapping. -// It is an implementatin of the errwrap.Wrapper interface so that +// It is an implementation of the errwrap.Wrapper interface so that // multierror.Error can be used with that library. // // This method is not safe to be called concurrently and is no different -// than accessing the Errors field directly. It is implementd only to +// than accessing the Errors field directly. It is implemented only to // satisfy the errwrap.Wrapper interface. func (e *Error) WrappedErrors() []error { return e.Errors diff --git a/vendor/github.com/hashicorp/go-multierror/prefix.go b/vendor/github.com/hashicorp/go-multierror/prefix.go new file mode 100644 index 0000000000000..5c477abe44f80 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/prefix.go @@ -0,0 +1,37 @@ +package multierror + +import ( + "fmt" + + "github.com/hashicorp/errwrap" +) + +// Prefix is a helper function that will prefix some text +// to the given error. If the error is a multierror.Error, then +// it will be prefixed to each wrapped error. +// +// This is useful to use when appending multiple multierrors +// together in order to give better scoping. +func Prefix(err error, prefix string) error { + if err == nil { + return nil + } + + format := fmt.Sprintf("%s {{err}}", prefix) + switch err := err.(type) { + case *Error: + // Typed nils can reach here, so initialize if we are nil + if err == nil { + err = new(Error) + } + + // Wrap each of the errors + for i, e := range err.Errors { + err.Errors[i] = errwrap.Wrapf(format, e) + } + + return err + default: + return errwrap.Wrapf(format, err) + } +} diff --git a/vendor/github.com/hashicorp/go-multierror/sort.go b/vendor/github.com/hashicorp/go-multierror/sort.go new file mode 100644 index 0000000000000..fecb14e81c542 --- /dev/null +++ b/vendor/github.com/hashicorp/go-multierror/sort.go @@ -0,0 +1,16 @@ +package multierror + +// Len implements sort.Interface function for length +func (err Error) Len() int { + return len(err.Errors) +} + +// Swap implements sort.Interface function for swapping elements +func (err Error) Swap(i, j int) { + err.Errors[i], err.Errors[j] = err.Errors[j], err.Errors[i] +} + +// Less implements sort.Interface function for determining order +func (err Error) Less(i, j int) bool { + return err.Errors[i].Error() < err.Errors[j].Error() +} diff --git a/vendor/github.com/hashicorp/go-sockaddr/go.mod b/vendor/github.com/hashicorp/go-sockaddr/go.mod new file mode 100644 index 0000000000000..21f8d8e8e758e --- /dev/null +++ b/vendor/github.com/hashicorp/go-sockaddr/go.mod @@ -0,0 +1,8 @@ +module github.com/hashicorp/go-sockaddr + +require ( + github.com/hashicorp/errwrap v1.0.0 + github.com/mitchellh/cli v1.0.0 + github.com/mitchellh/go-wordwrap v1.0.0 + github.com/ryanuber/columnize v2.1.0+incompatible +) diff --git a/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go b/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go index 2a706c34e92b8..80f61bef6808b 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go +++ b/vendor/github.com/hashicorp/go-sockaddr/ifaddrs.go @@ -1197,23 +1197,46 @@ func parseDefaultIfNameFromRoute(routeOut string) (string, error) { // parseDefaultIfNameFromIPCmd parses the default interface from ip(8) for // Linux. func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) { + parsedLines := parseIfNameFromIPCmd(routeOut) + for _, parsedLine := range parsedLines { + if parsedLine[0] == "default" && + parsedLine[1] == "via" && + parsedLine[3] == "dev" { + ifName := strings.TrimSpace(parsedLine[4]) + return ifName, nil + } + } + + return "", errors.New("No default interface found") +} + +// parseDefaultIfNameFromIPCmdAndroid parses the default interface from ip(8) for +// Android. +func parseDefaultIfNameFromIPCmdAndroid(routeOut string) (string, error) { + parsedLines := parseIfNameFromIPCmd(routeOut) + if (len(parsedLines) > 0) { + ifName := strings.TrimSpace(parsedLines[0][4]) + return ifName, nil + } + + return "", errors.New("No default interface found") +} + + +// parseIfNameFromIPCmd parses interfaces from ip(8) for +// Linux. +func parseIfNameFromIPCmd(routeOut string) [][]string { lines := strings.Split(routeOut, "\n") re := whitespaceRE.Copy() + parsedLines := make([][]string, 0, len(lines)) for _, line := range lines { kvs := re.Split(line, -1) if len(kvs) < 5 { continue } - - if kvs[0] == "default" && - kvs[1] == "via" && - kvs[3] == "dev" { - ifName := strings.TrimSpace(kvs[4]) - return ifName, nil - } + parsedLines = append(parsedLines, kvs) } - - return "", errors.New("No default interface found") + return parsedLines } // parseDefaultIfNameWindows parses the default interface from `netstat -rn` and diff --git a/vendor/github.com/hashicorp/go-sockaddr/route_info_android.go b/vendor/github.com/hashicorp/go-sockaddr/route_info_android.go new file mode 100644 index 0000000000000..9885915a6bada --- /dev/null +++ b/vendor/github.com/hashicorp/go-sockaddr/route_info_android.go @@ -0,0 +1,34 @@ +package sockaddr + +import ( + "errors" + "os/exec" +) + +type routeInfo struct { + cmds map[string][]string +} + +// NewRouteInfo returns a Android-specific implementation of the RouteInfo +// interface. +func NewRouteInfo() (routeInfo, error) { + return routeInfo{ + cmds: map[string][]string{"ip": {"/system/bin/ip", "route", "get", "8.8.8.8"}}, + }, nil +} + +// GetDefaultInterfaceName returns the interface name attached to the default +// route on the default interface. +func (ri routeInfo) GetDefaultInterfaceName() (string, error) { + out, err := exec.Command(ri.cmds["ip"][0], ri.cmds["ip"][1:]...).Output() + if err != nil { + return "", err + } + + + var ifName string + if ifName, err = parseDefaultIfNameFromIPCmdAndroid(string(out)); err != nil { + return "", errors.New("No default interface found") + } + return ifName, nil +} diff --git a/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go b/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go index c2ec91eaf456e..b62ce6ecb2178 100644 --- a/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go +++ b/vendor/github.com/hashicorp/go-sockaddr/route_info_linux.go @@ -1,3 +1,5 @@ +// +build !android + package sockaddr import ( diff --git a/vendor/github.com/hashicorp/golang-lru/go.mod b/vendor/github.com/hashicorp/golang-lru/go.mod new file mode 100644 index 0000000000000..8ad8826b368df --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/go.mod @@ -0,0 +1,3 @@ +module github.com/hashicorp/golang-lru + +go 1.12 diff --git a/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go b/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go index 5673773b22beb..a86c8539e0663 100644 --- a/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go +++ b/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go @@ -73,6 +73,9 @@ func (c *LRU) Add(key, value interface{}) (evicted bool) { func (c *LRU) Get(key interface{}) (value interface{}, ok bool) { if ent, ok := c.items[key]; ok { c.evictList.MoveToFront(ent) + if ent.Value.(*entry) == nil { + return nil, false + } return ent.Value.(*entry).value, true } return @@ -142,6 +145,19 @@ func (c *LRU) Len() int { return c.evictList.Len() } +// Resize changes the cache size. +func (c *LRU) Resize(size int) (evicted int) { + diff := c.Len() - size + if diff < 0 { + diff = 0 + } + for i := 0; i < diff; i++ { + c.removeOldest() + } + c.size = size + return diff +} + // removeOldest removes the oldest item from the cache. func (c *LRU) removeOldest() { ent := c.evictList.Back() diff --git a/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go b/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go index 744cac01c63c3..92d70934d632f 100644 --- a/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go +++ b/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go @@ -1,37 +1,39 @@ package simplelru - // LRUCache is the interface for simple LRU cache. type LRUCache interface { - // Adds a value to the cache, returns true if an eviction occurred and - // updates the "recently used"-ness of the key. - Add(key, value interface{}) bool + // Adds a value to the cache, returns true if an eviction occurred and + // updates the "recently used"-ness of the key. + Add(key, value interface{}) bool + + // Returns key's value from the cache and + // updates the "recently used"-ness of the key. #value, isFound + Get(key interface{}) (value interface{}, ok bool) - // Returns key's value from the cache and - // updates the "recently used"-ness of the key. #value, isFound - Get(key interface{}) (value interface{}, ok bool) + // Checks if a key exists in cache without updating the recent-ness. + Contains(key interface{}) (ok bool) - // Check if a key exsists in cache without updating the recent-ness. - Contains(key interface{}) (ok bool) + // Returns key's value without updating the "recently used"-ness of the key. + Peek(key interface{}) (value interface{}, ok bool) - // Returns key's value without updating the "recently used"-ness of the key. - Peek(key interface{}) (value interface{}, ok bool) + // Removes a key from the cache. + Remove(key interface{}) bool - // Removes a key from the cache. - Remove(key interface{}) bool + // Removes the oldest entry from cache. + RemoveOldest() (interface{}, interface{}, bool) - // Removes the oldest entry from cache. - RemoveOldest() (interface{}, interface{}, bool) + // Returns the oldest entry from the cache. #key, value, isFound + GetOldest() (interface{}, interface{}, bool) - // Returns the oldest entry from the cache. #key, value, isFound - GetOldest() (interface{}, interface{}, bool) + // Returns a slice of the keys in the cache, from oldest to newest. + Keys() []interface{} - // Returns a slice of the keys in the cache, from oldest to newest. - Keys() []interface{} + // Returns the number of items in the cache. + Len() int - // Returns the number of items in the cache. - Len() int + // Clears all cache entries. + Purge() - // Clear all cache entries - Purge() + // Resizes cache, returning number evicted + Resize(int) int } diff --git a/vendor/github.com/hashicorp/memberlist/README.md b/vendor/github.com/hashicorp/memberlist/README.md index 0adc075e815ea..6a2caa30e06f5 100644 --- a/vendor/github.com/hashicorp/memberlist/README.md +++ b/vendor/github.com/hashicorp/memberlist/README.md @@ -1,4 +1,4 @@ -# memberlist [![GoDoc](https://godoc.org/github.com/hashicorp/memberlist?status.png)](https://godoc.org/github.com/hashicorp/memberlist) +# memberlist [![GoDoc](https://godoc.org/github.com/hashicorp/memberlist?status.png)](https://godoc.org/github.com/hashicorp/memberlist) [![CircleCI](https://circleci.com/gh/hashicorp/memberlist.svg?style=svg)](https://circleci.com/gh/hashicorp/memberlist) memberlist is a [Go](http://www.golang.org) library that manages cluster membership and member failure detection using a gossip based protocol. @@ -23,8 +23,6 @@ Please check your installation with: go version ``` -Run `make deps` to fetch dependencies before building - ## Usage Memberlist is surprisingly simple to use. An example is shown below: @@ -65,7 +63,7 @@ For complete documentation, see the associated [Godoc](http://godoc.org/github.c ## Protocol -memberlist is based on ["SWIM: Scalable Weakly-consistent Infection-style Process Group Membership Protocol"](http://www.cs.cornell.edu/~asdas/research/dsn02-swim.pdf). However, we extend the protocol in a number of ways: +memberlist is based on ["SWIM: Scalable Weakly-consistent Infection-style Process Group Membership Protocol"](http://ieeexplore.ieee.org/document/1028914/). However, we extend the protocol in a number of ways: * Several extensions are made to increase propagation speed and convergence rate. diff --git a/vendor/github.com/hashicorp/memberlist/alive_delegate.go b/vendor/github.com/hashicorp/memberlist/alive_delegate.go index 51a0ba9054ada..615f4a90a5909 100644 --- a/vendor/github.com/hashicorp/memberlist/alive_delegate.go +++ b/vendor/github.com/hashicorp/memberlist/alive_delegate.go @@ -7,8 +7,8 @@ package memberlist // a node out and prevent it from being considered a peer // using application specific logic. type AliveDelegate interface { - // NotifyMerge is invoked when a merge could take place. - // Provides a list of the nodes known by the peer. If - // the return value is non-nil, the merge is canceled. + // NotifyAlive is invoked when a message about a live + // node is received from the network. Returning a non-nil + // error prevents the node from being considered a peer. NotifyAlive(peer *Node) error } diff --git a/vendor/github.com/hashicorp/memberlist/broadcast.go b/vendor/github.com/hashicorp/memberlist/broadcast.go index f7e85a119cf09..d07d41bb69d91 100644 --- a/vendor/github.com/hashicorp/memberlist/broadcast.go +++ b/vendor/github.com/hashicorp/memberlist/broadcast.go @@ -29,6 +29,11 @@ func (b *memberlistBroadcast) Invalidates(other Broadcast) bool { return b.node == mb.node } +// memberlist.NamedBroadcast optional interface +func (b *memberlistBroadcast) Name() string { + return b.node +} + func (b *memberlistBroadcast) Message() []byte { return b.msg } diff --git a/vendor/github.com/hashicorp/memberlist/config.go b/vendor/github.com/hashicorp/memberlist/config.go index c85b1657a2cbf..31099e75f4423 100644 --- a/vendor/github.com/hashicorp/memberlist/config.go +++ b/vendor/github.com/hashicorp/memberlist/config.go @@ -1,10 +1,15 @@ package memberlist import ( + "fmt" "io" "log" + "net" "os" + "strings" "time" + + multierror "github.com/hashicorp/go-multierror" ) type Config struct { @@ -116,6 +121,10 @@ type Config struct { // indirect UDP pings. DisableTcpPings bool + // DisableTcpPingsForNode is like DisableTcpPings, but lets you control + // whether to perform TCP pings on a node-by-node basis. + DisableTcpPingsForNode func(nodeName string) bool + // AwarenessMaxMultiplier will increase the probe interval if the node // becomes aware that it might be degraded and not meeting the soft real // time requirements to reliably probe other nodes. @@ -215,6 +224,44 @@ type Config struct { // This is a legacy name for backward compatibility but should really be // called PacketBufferSize now that we have generalized the transport. UDPBufferSize int + + // DeadNodeReclaimTime controls the time before a dead node's name can be + // reclaimed by one with a different address or port. By default, this is 0, + // meaning nodes cannot be reclaimed this way. + DeadNodeReclaimTime time.Duration + + // RequireNodeNames controls if the name of a node is required when sending + // a message to that node. + RequireNodeNames bool + // CIDRsAllowed If nil, allow any connection (default), otherwise specify all networks + // allowed to connect (you must specify IPv6/IPv4 separately) + // Using [] will block all connections. + CIDRsAllowed []net.IPNet +} + +// ParseCIDRs return a possible empty list of all Network that have been parsed +// In case of error, it returns succesfully parsed CIDRs and the last error found +func ParseCIDRs(v []string) ([]net.IPNet, error) { + nets := make([]net.IPNet, 0) + if v == nil { + return nets, nil + } + var errs error + hasErrors := false + for _, p := range v { + _, net, err := net.ParseCIDR(strings.TrimSpace(p)) + if err != nil { + err = fmt.Errorf("invalid cidr: %s", p) + errs = multierror.Append(errs, err) + hasErrors = true + } else { + nets = append(nets, *net) + } + } + if !hasErrors { + errs = nil + } + return nets, errs } // DefaultLANConfig returns a sane set of configurations for Memberlist. @@ -258,6 +305,7 @@ func DefaultLANConfig() *Config { HandoffQueueDepth: 1024, UDPBufferSize: 1400, + CIDRsAllowed: nil, // same as allow all } } @@ -277,6 +325,24 @@ func DefaultWANConfig() *Config { return conf } +// IPMustBeChecked return true if IPAllowed must be called +func (c *Config) IPMustBeChecked() bool { + return len(c.CIDRsAllowed) > 0 +} + +// IPAllowed return an error if access to memberlist is denied +func (c *Config) IPAllowed(ip net.IP) error { + if !c.IPMustBeChecked() { + return nil + } + for _, n := range c.CIDRsAllowed { + if n.Contains(ip) { + return nil + } + } + return fmt.Errorf("%s is not allowed", ip) +} + // DefaultLocalConfig works like DefaultConfig, however it returns a configuration // that is optimized for a local loopback environments. The default configuration is // still very conservative and errs on the side of caution. diff --git a/vendor/github.com/hashicorp/memberlist/event_delegate.go b/vendor/github.com/hashicorp/memberlist/event_delegate.go index 35e2a56fdd06a..352f98b43e700 100644 --- a/vendor/github.com/hashicorp/memberlist/event_delegate.go +++ b/vendor/github.com/hashicorp/memberlist/event_delegate.go @@ -49,13 +49,16 @@ type NodeEvent struct { } func (c *ChannelEventDelegate) NotifyJoin(n *Node) { - c.Ch <- NodeEvent{NodeJoin, n} + node := *n + c.Ch <- NodeEvent{NodeJoin, &node} } func (c *ChannelEventDelegate) NotifyLeave(n *Node) { - c.Ch <- NodeEvent{NodeLeave, n} + node := *n + c.Ch <- NodeEvent{NodeLeave, &node} } func (c *ChannelEventDelegate) NotifyUpdate(n *Node) { - c.Ch <- NodeEvent{NodeUpdate, n} + node := *n + c.Ch <- NodeEvent{NodeUpdate, &node} } diff --git a/vendor/github.com/hashicorp/memberlist/go.mod b/vendor/github.com/hashicorp/memberlist/go.mod new file mode 100644 index 0000000000000..1b83a4f285104 --- /dev/null +++ b/vendor/github.com/hashicorp/memberlist/go.mod @@ -0,0 +1,18 @@ +module github.com/hashicorp/memberlist + +go 1.12 + +require ( + github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c + github.com/hashicorp/go-immutable-radix v1.0.0 // indirect + github.com/hashicorp/go-msgpack v0.5.3 + github.com/hashicorp/go-multierror v1.0.0 + github.com/hashicorp/go-sockaddr v1.0.0 + github.com/miekg/dns v1.1.26 + github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 + github.com/stretchr/testify v1.2.2 +) diff --git a/vendor/github.com/hashicorp/memberlist/logging.go b/vendor/github.com/hashicorp/memberlist/logging.go index f31acfb2fa347..2ca2bab4e3402 100644 --- a/vendor/github.com/hashicorp/memberlist/logging.go +++ b/vendor/github.com/hashicorp/memberlist/logging.go @@ -13,6 +13,14 @@ func LogAddress(addr net.Addr) string { return fmt.Sprintf("from=%s", addr.String()) } +func LogStringAddress(addr string) string { + if addr == "" { + return "from=" + } + + return fmt.Sprintf("from=%s", addr) +} + func LogConn(conn net.Conn) string { if conn == nil { return LogAddress(nil) diff --git a/vendor/github.com/hashicorp/memberlist/memberlist.go b/vendor/github.com/hashicorp/memberlist/memberlist.go index e9084f9fd43ee..7ee04009191e8 100644 --- a/vendor/github.com/hashicorp/memberlist/memberlist.go +++ b/vendor/github.com/hashicorp/memberlist/memberlist.go @@ -15,6 +15,8 @@ multiple routes. package memberlist import ( + "container/list" + "errors" "fmt" "log" "net" @@ -30,10 +32,17 @@ import ( "github.com/miekg/dns" ) +var errNodeNamesAreRequired = errors.New("memberlist: node names are required by configuration but one was not provided") + type Memberlist struct { sequenceNum uint32 // Local sequence number incarnation uint32 // Local incarnation number numNodes uint32 // Number of known nodes (estimate) + pushPullReq uint32 // Number of push/pull requests + + advertiseLock sync.RWMutex + advertiseAddr net.IP + advertisePort uint16 config *Config shutdown int32 // Used as an atomic boolean value @@ -44,13 +53,17 @@ type Memberlist struct { shutdownLock sync.Mutex // Serializes calls to Shutdown leaveLock sync.Mutex // Serializes calls to Leave - transport Transport - handoff chan msgHandoff + transport NodeAwareTransport + + handoffCh chan struct{} + highPriorityMsgQueue *list.List + lowPriorityMsgQueue *list.List + msgQueueLock sync.Mutex nodeLock sync.RWMutex nodes []*nodeState // Known nodes - nodeMap map[string]*nodeState // Maps Addr.String() -> NodeState - nodeTimers map[string]*suspicion // Maps Addr.String() -> suspicion timer + nodeMap map[string]*nodeState // Maps Node.Name -> NodeState + nodeTimers map[string]*suspicion // Maps Node.Name -> suspicion timer awareness *awareness tickerLock sync.Mutex @@ -66,6 +79,15 @@ type Memberlist struct { logger *log.Logger } +// BuildVsnArray creates the array of Vsn +func (conf *Config) BuildVsnArray() []uint8 { + return []uint8{ + ProtocolVersionMin, ProtocolVersionMax, conf.ProtocolVersion, + conf.DelegateProtocolMin, conf.DelegateProtocolMax, + conf.DelegateProtocolVersion, + } +} + // newMemberlist creates the network listeners. // Does not schedule execution of background maintenance. func newMemberlist(conf *Config) (*Memberlist, error) { @@ -159,22 +181,38 @@ func newMemberlist(conf *Config) (*Memberlist, error) { transport = nt } + nodeAwareTransport, ok := transport.(NodeAwareTransport) + if !ok { + logger.Printf("[DEBUG] memberlist: configured Transport is not a NodeAwareTransport and some features may not work as desired") + nodeAwareTransport = &shimNodeAwareTransport{transport} + } + m := &Memberlist{ - config: conf, - shutdownCh: make(chan struct{}), - leaveBroadcast: make(chan struct{}, 1), - transport: transport, - handoff: make(chan msgHandoff, conf.HandoffQueueDepth), - nodeMap: make(map[string]*nodeState), - nodeTimers: make(map[string]*suspicion), - awareness: newAwareness(conf.AwarenessMaxMultiplier), - ackHandlers: make(map[uint32]*ackHandler), - broadcasts: &TransmitLimitedQueue{RetransmitMult: conf.RetransmitMult}, - logger: logger, + config: conf, + shutdownCh: make(chan struct{}), + leaveBroadcast: make(chan struct{}, 1), + transport: nodeAwareTransport, + handoffCh: make(chan struct{}, 1), + highPriorityMsgQueue: list.New(), + lowPriorityMsgQueue: list.New(), + nodeMap: make(map[string]*nodeState), + nodeTimers: make(map[string]*suspicion), + awareness: newAwareness(conf.AwarenessMaxMultiplier), + ackHandlers: make(map[uint32]*ackHandler), + broadcasts: &TransmitLimitedQueue{RetransmitMult: conf.RetransmitMult}, + logger: logger, } m.broadcasts.NumNodes = func() int { return m.estNumNodes() } + + // Get the final advertise address from the transport, which may need + // to see which address we bound to. We'll refresh this each time we + // send out an alive message. + if _, _, err := m.refreshAdvertise(); err != nil { + return nil, err + } + go m.streamListen() go m.packetListen() go m.packetHandler() @@ -222,7 +260,8 @@ func (m *Memberlist) Join(existing []string) (int, error) { for _, addr := range addrs { hp := joinHostPort(addr.ip.String(), addr.port) - if err := m.pushPullNode(hp, true); err != nil { + a := Address{Addr: hp, Name: addr.nodeName} + if err := m.pushPullNode(a, true); err != nil { err = fmt.Errorf("Failed to join %s: %v", addr.ip, err) errs = multierror.Append(errs, err) m.logger.Printf("[DEBUG] memberlist: %v", err) @@ -240,8 +279,9 @@ func (m *Memberlist) Join(existing []string) (int, error) { // ipPort holds information about a node we want to try to join. type ipPort struct { - ip net.IP - port uint16 + ip net.IP + port uint16 + nodeName string // optional } // tcpLookupIP is a helper to initiate a TCP-based DNS lookup for the given host. @@ -250,7 +290,7 @@ type ipPort struct { // Consul's. By doing the TCP lookup directly, we get the best chance for the // largest list of hosts to join. Since joins are relatively rare events, it's ok // to do this rather expensive operation. -func (m *Memberlist) tcpLookupIP(host string, defaultPort uint16) ([]ipPort, error) { +func (m *Memberlist) tcpLookupIP(host string, defaultPort uint16, nodeName string) ([]ipPort, error) { // Don't attempt any TCP lookups against non-fully qualified domain // names, since those will likely come from the resolv.conf file. if !strings.Contains(host, ".") { @@ -292,9 +332,9 @@ func (m *Memberlist) tcpLookupIP(host string, defaultPort uint16) ([]ipPort, err for _, r := range in.Answer { switch rr := r.(type) { case (*dns.A): - ips = append(ips, ipPort{rr.A, defaultPort}) + ips = append(ips, ipPort{ip: rr.A, port: defaultPort, nodeName: nodeName}) case (*dns.AAAA): - ips = append(ips, ipPort{rr.AAAA, defaultPort}) + ips = append(ips, ipPort{ip: rr.AAAA, port: defaultPort, nodeName: nodeName}) case (*dns.CNAME): m.logger.Printf("[DEBUG] memberlist: Ignoring CNAME RR in TCP-first answer for '%s'", host) } @@ -308,6 +348,16 @@ func (m *Memberlist) tcpLookupIP(host string, defaultPort uint16) ([]ipPort, err // resolveAddr is used to resolve the address into an address, // port, and error. If no port is given, use the default func (m *Memberlist) resolveAddr(hostStr string) ([]ipPort, error) { + // First peel off any leading node name. This is optional. + nodeName := "" + if slashIdx := strings.Index(hostStr, "/"); slashIdx >= 0 { + if slashIdx == 0 { + return nil, fmt.Errorf("empty node name provided") + } + nodeName = hostStr[0:slashIdx] + hostStr = hostStr[slashIdx+1:] + } + // This captures the supplied port, or the default one. hostStr = ensurePort(hostStr, m.config.BindPort) host, sport, err := net.SplitHostPort(hostStr) @@ -324,13 +374,15 @@ func (m *Memberlist) resolveAddr(hostStr string) ([]ipPort, error) { // will make sure the host part is in good shape for parsing, even for // IPv6 addresses. if ip := net.ParseIP(host); ip != nil { - return []ipPort{ipPort{ip, port}}, nil + return []ipPort{ + ipPort{ip: ip, port: port, nodeName: nodeName}, + }, nil } // First try TCP so we have the best chance for the largest list of // hosts to join. If this fails it's not fatal since this isn't a standard // way to query DNS, and we have a fallback below. - ips, err := m.tcpLookupIP(host, port) + ips, err := m.tcpLookupIP(host, port, nodeName) if err != nil { m.logger.Printf("[DEBUG] memberlist: TCP-first lookup failed for '%s', falling back to UDP: %s", hostStr, err) } @@ -347,7 +399,7 @@ func (m *Memberlist) resolveAddr(hostStr string) ([]ipPort, error) { } ips = make([]ipPort, 0, len(ans)) for _, ip := range ans { - ips = append(ips, ipPort{ip, port}) + ips = append(ips, ipPort{ip: ip, port: port, nodeName: nodeName}) } return ips, nil } @@ -358,10 +410,9 @@ func (m *Memberlist) resolveAddr(hostStr string) ([]ipPort, error) { func (m *Memberlist) setAlive() error { // Get the final advertise address from the transport, which may need // to see which address we bound to. - addr, port, err := m.transport.FinalAdvertiseAddr( - m.config.AdvertiseAddr, m.config.AdvertisePort) + addr, port, err := m.refreshAdvertise() if err != nil { - return fmt.Errorf("Failed to get final advertise address: %v", err) + return err } // Check if this is a public address without encryption @@ -394,16 +445,36 @@ func (m *Memberlist) setAlive() error { Addr: addr, Port: uint16(port), Meta: meta, - Vsn: []uint8{ - ProtocolVersionMin, ProtocolVersionMax, m.config.ProtocolVersion, - m.config.DelegateProtocolMin, m.config.DelegateProtocolMax, - m.config.DelegateProtocolVersion, - }, + Vsn: m.config.BuildVsnArray(), } m.aliveNode(&a, nil, true) + return nil } +func (m *Memberlist) getAdvertise() (net.IP, uint16) { + m.advertiseLock.RLock() + defer m.advertiseLock.RUnlock() + return m.advertiseAddr, m.advertisePort +} + +func (m *Memberlist) setAdvertise(addr net.IP, port int) { + m.advertiseLock.Lock() + defer m.advertiseLock.Unlock() + m.advertiseAddr = addr + m.advertisePort = uint16(port) +} + +func (m *Memberlist) refreshAdvertise() (net.IP, int, error) { + addr, port, err := m.transport.FinalAdvertiseAddr( + m.config.AdvertiseAddr, m.config.AdvertisePort) + if err != nil { + return nil, 0, fmt.Errorf("Failed to get final advertise address: %v", err) + } + m.setAdvertise(addr, port) + return addr, port, nil +} + // LocalNode is used to return the local Node func (m *Memberlist) LocalNode() *Node { m.nodeLock.RLock() @@ -439,11 +510,7 @@ func (m *Memberlist) UpdateNode(timeout time.Duration) error { Addr: state.Addr, Port: state.Port, Meta: meta, - Vsn: []uint8{ - ProtocolVersionMin, ProtocolVersionMax, m.config.ProtocolVersion, - m.config.DelegateProtocolMin, m.config.DelegateProtocolMax, - m.config.DelegateProtocolVersion, - }, + Vsn: m.config.BuildVsnArray(), } notifyCh := make(chan struct{}) m.aliveNode(&a, notifyCh, true) @@ -463,24 +530,29 @@ func (m *Memberlist) UpdateNode(timeout time.Duration) error { return nil } -// SendTo is deprecated in favor of SendBestEffort, which requires a node to -// target. +// Deprecated: SendTo is deprecated in favor of SendBestEffort, which requires a node to +// target. If you don't have a node then use SendToAddress. func (m *Memberlist) SendTo(to net.Addr, msg []byte) error { + a := Address{Addr: to.String(), Name: ""} + return m.SendToAddress(a, msg) +} + +func (m *Memberlist) SendToAddress(a Address, msg []byte) error { // Encode as a user message buf := make([]byte, 1, len(msg)+1) buf[0] = byte(userMsg) buf = append(buf, msg...) // Send the message - return m.rawSendMsgPacket(to.String(), nil, buf) + return m.rawSendMsgPacket(a, nil, buf) } -// SendToUDP is deprecated in favor of SendBestEffort. +// Deprecated: SendToUDP is deprecated in favor of SendBestEffort. func (m *Memberlist) SendToUDP(to *Node, msg []byte) error { return m.SendBestEffort(to, msg) } -// SendToTCP is deprecated in favor of SendReliable. +// Deprecated: SendToTCP is deprecated in favor of SendReliable. func (m *Memberlist) SendToTCP(to *Node, msg []byte) error { return m.SendReliable(to, msg) } @@ -496,7 +568,8 @@ func (m *Memberlist) SendBestEffort(to *Node, msg []byte) error { buf = append(buf, msg...) // Send the message - return m.rawSendMsgPacket(to.Address(), to, buf) + a := Address{Addr: to.Address(), Name: to.Name} + return m.rawSendMsgPacket(a, to, buf) } // SendReliable uses the reliable stream-oriented interface of the transport to @@ -504,7 +577,7 @@ func (m *Memberlist) SendBestEffort(to *Node, msg []byte) error { // mechanism). Delivery is guaranteed if no error is returned, and there is no // limit on the size of the message. func (m *Memberlist) SendReliable(to *Node, msg []byte) error { - return m.sendUserMsg(to.Address(), msg) + return m.sendUserMsg(to.FullAddress(), msg) } // Members returns a list of all known live nodes. The node structures @@ -516,7 +589,7 @@ func (m *Memberlist) Members() []*Node { nodes := make([]*Node, 0, len(m.nodes)) for _, n := range m.nodes { - if n.State != stateDead { + if !n.DeadOrLeft() { nodes = append(nodes, &n.Node) } } @@ -533,7 +606,7 @@ func (m *Memberlist) NumMembers() (alive int) { defer m.nodeLock.RUnlock() for _, n := range m.nodes { - if n.State != stateDead { + if !n.DeadOrLeft() { alive++ } } @@ -570,9 +643,14 @@ func (m *Memberlist) Leave(timeout time.Duration) error { return nil } + // This dead message is special, because Node and From are the + // same. This helps other nodes figure out that a node left + // intentionally. When Node equals From, other nodes know for + // sure this node is gone. d := dead{ Incarnation: state.Incarnation, Node: state.Name, + From: state.Name, } m.deadNode(&d) @@ -598,7 +676,7 @@ func (m *Memberlist) anyAlive() bool { m.nodeLock.RLock() defer m.nodeLock.RUnlock() for _, n := range m.nodes { - if n.State != stateDead && n.Name != m.config.Name { + if !n.DeadOrLeft() && n.Name != m.config.Name { return true } } @@ -621,7 +699,7 @@ func (m *Memberlist) ProtocolVersion() uint8 { return m.config.ProtocolVersion } -// Shutdown will stop any background maintanence of network activity +// Shutdown will stop any background maintenance of network activity // for this memberlist, causing it to appear "dead". A leave message // will not be broadcasted prior, so the cluster being left will have // to detect this node's shutdown using probing. If you wish to more @@ -657,3 +735,27 @@ func (m *Memberlist) hasShutdown() bool { func (m *Memberlist) hasLeft() bool { return atomic.LoadInt32(&m.leave) == 1 } + +func (m *Memberlist) getNodeState(addr string) NodeStateType { + m.nodeLock.RLock() + defer m.nodeLock.RUnlock() + + n := m.nodeMap[addr] + return n.State +} + +func (m *Memberlist) getNodeStateChange(addr string) time.Time { + m.nodeLock.RLock() + defer m.nodeLock.RUnlock() + + n := m.nodeMap[addr] + return n.StateChange +} + +func (m *Memberlist) changeNode(addr string, f func(*nodeState)) { + m.nodeLock.Lock() + defer m.nodeLock.Unlock() + + n := m.nodeMap[addr] + f(n) +} diff --git a/vendor/github.com/hashicorp/memberlist/mock_transport.go b/vendor/github.com/hashicorp/memberlist/mock_transport.go index b8bafa80260bc..0a7d30a277d40 100644 --- a/vendor/github.com/hashicorp/memberlist/mock_transport.go +++ b/vendor/github.com/hashicorp/memberlist/mock_transport.go @@ -1,7 +1,9 @@ package memberlist import ( + "bytes" "fmt" + "io" "net" "strconv" "time" @@ -10,26 +12,33 @@ import ( // MockNetwork is used as a factory that produces MockTransport instances which // are uniquely addressed and wired up to talk to each other. type MockNetwork struct { - transports map[string]*MockTransport - port int + transportsByAddr map[string]*MockTransport + transportsByName map[string]*MockTransport + port int } // NewTransport returns a new MockTransport with a unique address, wired up to // talk to the other transports in the MockNetwork. -func (n *MockNetwork) NewTransport() *MockTransport { +func (n *MockNetwork) NewTransport(name string) *MockTransport { n.port += 1 addr := fmt.Sprintf("127.0.0.1:%d", n.port) transport := &MockTransport{ net: n, - addr: &MockAddress{addr}, + addr: &MockAddress{addr, name}, packetCh: make(chan *Packet), streamCh: make(chan net.Conn), } - if n.transports == nil { - n.transports = make(map[string]*MockTransport) + if n.transportsByAddr == nil { + n.transportsByAddr = make(map[string]*MockTransport) } - n.transports[addr] = transport + n.transportsByAddr[addr] = transport + + if n.transportsByName == nil { + n.transportsByName = make(map[string]*MockTransport) + } + n.transportsByName[name] = transport + return transport } @@ -37,6 +46,7 @@ func (n *MockNetwork) NewTransport() *MockTransport { // address scheme. type MockAddress struct { addr string + name string } // See net.Addr. @@ -57,6 +67,8 @@ type MockTransport struct { streamCh chan net.Conn } +var _ NodeAwareTransport = (*MockTransport)(nil) + // See Transport. func (t *MockTransport) FinalAdvertiseAddr(string, int) (net.IP, int, error) { host, portStr, err := net.SplitHostPort(t.addr.String()) @@ -79,9 +91,15 @@ func (t *MockTransport) FinalAdvertiseAddr(string, int) (net.IP, int, error) { // See Transport. func (t *MockTransport) WriteTo(b []byte, addr string) (time.Time, error) { - dest, ok := t.net.transports[addr] - if !ok { - return time.Time{}, fmt.Errorf("No route to %q", addr) + a := Address{Addr: addr, Name: ""} + return t.WriteToAddress(b, a) +} + +// See NodeAwareTransport. +func (t *MockTransport) WriteToAddress(b []byte, a Address) (time.Time, error) { + dest, err := t.getPeer(a) + if err != nil { + return time.Time{}, err } now := time.Now() @@ -98,11 +116,45 @@ func (t *MockTransport) PacketCh() <-chan *Packet { return t.packetCh } +// See NodeAwareTransport. +func (t *MockTransport) IngestPacket(conn net.Conn, addr net.Addr, now time.Time, shouldClose bool) error { + if shouldClose { + defer conn.Close() + } + + // Copy everything from the stream into packet buffer. + var buf bytes.Buffer + if _, err := io.Copy(&buf, conn); err != nil { + return fmt.Errorf("failed to read packet: %v", err) + } + + // Check the length - it needs to have at least one byte to be a proper + // message. This is checked elsewhere for writes coming in directly from + // the UDP socket. + if n := buf.Len(); n < 1 { + return fmt.Errorf("packet too short (%d bytes) %s", n, LogAddress(addr)) + } + + // Inject the packet. + t.packetCh <- &Packet{ + Buf: buf.Bytes(), + From: addr, + Timestamp: now, + } + return nil +} + // See Transport. func (t *MockTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) { - dest, ok := t.net.transports[addr] - if !ok { - return nil, fmt.Errorf("No route to %q", addr) + a := Address{Addr: addr, Name: ""} + return t.DialAddressTimeout(a, timeout) +} + +// See NodeAwareTransport. +func (t *MockTransport) DialAddressTimeout(a Address, timeout time.Duration) (net.Conn, error) { + dest, err := t.getPeer(a) + if err != nil { + return nil, err } p1, p2 := net.Pipe() @@ -115,7 +167,29 @@ func (t *MockTransport) StreamCh() <-chan net.Conn { return t.streamCh } +// See NodeAwareTransport. +func (t *MockTransport) IngestStream(conn net.Conn) error { + t.streamCh <- conn + return nil +} + // See Transport. func (t *MockTransport) Shutdown() error { return nil } + +func (t *MockTransport) getPeer(a Address) (*MockTransport, error) { + var ( + dest *MockTransport + ok bool + ) + if a.Name != "" { + dest, ok = t.net.transportsByName[a.Name] + } else { + dest, ok = t.net.transportsByAddr[a.Addr] + } + if !ok { + return nil, fmt.Errorf("No route to %s", a) + } + return dest, nil +} diff --git a/vendor/github.com/hashicorp/memberlist/net.go b/vendor/github.com/hashicorp/memberlist/net.go index a4330c4d20474..bac73bd89f870 100644 --- a/vendor/github.com/hashicorp/memberlist/net.go +++ b/vendor/github.com/hashicorp/memberlist/net.go @@ -8,9 +8,10 @@ import ( "hash/crc32" "io" "net" + "sync/atomic" "time" - "github.com/armon/go-metrics" + metrics "github.com/armon/go-metrics" "github.com/hashicorp/go-msgpack/codec" ) @@ -71,7 +72,8 @@ const ( compoundOverhead = 2 // Assumed overhead per entry in compoundHeader userMsgOverhead = 1 blockingWarning = 10 * time.Millisecond // Warn if a UDP packet takes this long to process - maxPushStateBytes = 10 * 1024 * 1024 + maxPushStateBytes = 20 * 1024 * 1024 + maxPushPullRequests = 128 // Maximum number of concurrent push/pull requests ) // ping request sent directly to node @@ -82,15 +84,28 @@ type ping struct { // the intended recipient. This is to protect again an agent // restart with a new name. Node string + + SourceAddr []byte `codec:",omitempty"` // Source address, used for a direct reply + SourcePort uint16 `codec:",omitempty"` // Source port, used for a direct reply + SourceNode string `codec:",omitempty"` // Source name, used for a direct reply } -// indirect ping sent to an indirect ndoe +// indirect ping sent to an indirect node type indirectPingReq struct { SeqNo uint32 Target []byte Port uint16 - Node string - Nack bool // true if we'd like a nack back + + // Node is sent so the target can verify they are + // the intended recipient. This is to protect against an agent + // restart with a new name. + Node string + + Nack bool // true if we'd like a nack back + + SourceAddr []byte `codec:",omitempty"` // Source address, used for a direct reply + SourcePort uint16 `codec:",omitempty"` // Source port, used for a direct reply + SourceNode string `codec:",omitempty"` // Source name, used for a direct reply } // ack response is sent for a ping @@ -161,7 +176,7 @@ type pushNodeState struct { Port uint16 Meta []byte Incarnation uint32 - State nodeStateType + State NodeStateType Vsn []uint8 // Protocol versions } @@ -205,9 +220,9 @@ func (m *Memberlist) streamListen() { // handleConn handles a single incoming stream connection from the transport. func (m *Memberlist) handleConn(conn net.Conn) { + defer conn.Close() m.logger.Printf("[DEBUG] memberlist: Stream connection %s", LogConn(conn)) - defer conn.Close() metrics.IncrCounter([]string{"memberlist", "tcp", "accept"}, 1) conn.SetDeadline(time.Now().Add(m.config.TCPTimeout)) @@ -238,6 +253,16 @@ func (m *Memberlist) handleConn(conn net.Conn) { m.logger.Printf("[ERR] memberlist: Failed to receive user message: %s %s", err, LogConn(conn)) } case pushPullMsg: + // Increment counter of pending push/pulls + numConcurrent := atomic.AddUint32(&m.pushPullReq, 1) + defer atomic.AddUint32(&m.pushPullReq, ^uint32(0)) + + // Check if we have too many open push/pull requests + if numConcurrent >= maxPushPullRequests { + m.logger.Printf("[ERR] memberlist: Too many pending push/pull requests") + return + } + join, remoteNodes, userState, err := m.readRemoteState(bufConn, dec) if err != nil { m.logger.Printf("[ERR] memberlist: Failed to read remote state: %s %s", err, LogConn(conn)) @@ -330,6 +355,10 @@ func (m *Memberlist) ingestPacket(buf []byte, from net.Addr, timestamp time.Time } func (m *Memberlist) handleCommand(buf []byte, from net.Addr, timestamp time.Time) { + if len(buf) < 1 { + m.logger.Printf("[ERR] memberlist: missing message type byte %s", LogAddress(from)) + return + } // Decode the message type msgType := messageType(buf[0]) buf = buf[1:] @@ -357,10 +386,25 @@ func (m *Memberlist) handleCommand(buf []byte, from net.Addr, timestamp time.Tim case deadMsg: fallthrough case userMsg: + // Determine the message queue, prioritize alive + queue := m.lowPriorityMsgQueue + if msgType == aliveMsg { + queue = m.highPriorityMsgQueue + } + + // Check for overflow and append if not full + m.msgQueueLock.Lock() + if queue.Len() >= m.config.HandoffQueueDepth { + m.logger.Printf("[WARN] memberlist: handler queue full, dropping message (%d) %s", msgType, LogAddress(from)) + } else { + queue.PushBack(msgHandoff{msgType, buf, from}) + } + m.msgQueueLock.Unlock() + + // Notify of pending message select { - case m.handoff <- msgHandoff{msgType, buf, from}: + case m.handoffCh <- struct{}{}: default: - m.logger.Printf("[WARN] memberlist: handler queue full, dropping message (%d) %s", msgType, LogAddress(from)) } default: @@ -368,28 +412,51 @@ func (m *Memberlist) handleCommand(buf []byte, from net.Addr, timestamp time.Tim } } +// getNextMessage returns the next message to process in priority order, using LIFO +func (m *Memberlist) getNextMessage() (msgHandoff, bool) { + m.msgQueueLock.Lock() + defer m.msgQueueLock.Unlock() + + if el := m.highPriorityMsgQueue.Back(); el != nil { + m.highPriorityMsgQueue.Remove(el) + msg := el.Value.(msgHandoff) + return msg, true + } else if el := m.lowPriorityMsgQueue.Back(); el != nil { + m.lowPriorityMsgQueue.Remove(el) + msg := el.Value.(msgHandoff) + return msg, true + } + return msgHandoff{}, false +} + // packetHandler is a long running goroutine that processes messages received // over the packet interface, but is decoupled from the listener to avoid // blocking the listener which may cause ping/ack messages to be delayed. func (m *Memberlist) packetHandler() { for { select { - case msg := <-m.handoff: - msgType := msg.msgType - buf := msg.buf - from := msg.from - - switch msgType { - case suspectMsg: - m.handleSuspect(buf, from) - case aliveMsg: - m.handleAlive(buf, from) - case deadMsg: - m.handleDead(buf, from) - case userMsg: - m.handleUser(buf, from) - default: - m.logger.Printf("[ERR] memberlist: Message type (%d) not supported %s (packet handler)", msgType, LogAddress(from)) + case <-m.handoffCh: + for { + msg, ok := m.getNextMessage() + if !ok { + break + } + msgType := msg.msgType + buf := msg.buf + from := msg.from + + switch msgType { + case suspectMsg: + m.handleSuspect(buf, from) + case aliveMsg: + m.handleAlive(buf, from) + case deadMsg: + m.handleDead(buf, from) + case userMsg: + m.handleUser(buf, from) + default: + m.logger.Printf("[ERR] memberlist: Message type (%d) not supported %s (packet handler)", msgType, LogAddress(from)) + } } case <-m.shutdownCh: @@ -433,7 +500,19 @@ func (m *Memberlist) handlePing(buf []byte, from net.Addr) { if m.config.Ping != nil { ack.Payload = m.config.Ping.AckPayload() } - if err := m.encodeAndSendMsg(from.String(), ackRespMsg, &ack); err != nil { + + addr := "" + if len(p.SourceAddr) > 0 && p.SourcePort > 0 { + addr = joinHostPort(net.IP(p.SourceAddr).String(), p.SourcePort) + } else { + addr = from.String() + } + + a := Address{ + Addr: addr, + Name: p.SourceNode, + } + if err := m.encodeAndSendMsg(a, ackRespMsg, &ack); err != nil { m.logger.Printf("[ERR] memberlist: Failed to send ack: %s %s", err, LogAddress(from)) } } @@ -453,7 +532,25 @@ func (m *Memberlist) handleIndirectPing(buf []byte, from net.Addr) { // Send a ping to the correct host. localSeqNo := m.nextSeqNo() - ping := ping{SeqNo: localSeqNo, Node: ind.Node} + selfAddr, selfPort := m.getAdvertise() + ping := ping{ + SeqNo: localSeqNo, + Node: ind.Node, + // The outbound message is addressed FROM us. + SourceAddr: selfAddr, + SourcePort: selfPort, + SourceNode: m.config.Name, + } + + // Forward the ack back to the requestor. If the request encodes an origin + // use that otherwise assume that the other end of the UDP socket is + // usable. + indAddr := "" + if len(ind.SourceAddr) > 0 && ind.SourcePort > 0 { + indAddr = joinHostPort(net.IP(ind.SourceAddr).String(), ind.SourcePort) + } else { + indAddr = from.String() + } // Setup a response handler to relay the ack cancelCh := make(chan struct{}) @@ -461,18 +558,25 @@ func (m *Memberlist) handleIndirectPing(buf []byte, from net.Addr) { // Try to prevent the nack if we've caught it in time. close(cancelCh) - // Forward the ack back to the requestor. ack := ackResp{ind.SeqNo, nil} - if err := m.encodeAndSendMsg(from.String(), ackRespMsg, &ack); err != nil { - m.logger.Printf("[ERR] memberlist: Failed to forward ack: %s %s", err, LogAddress(from)) + a := Address{ + Addr: indAddr, + Name: ind.SourceNode, + } + if err := m.encodeAndSendMsg(a, ackRespMsg, &ack); err != nil { + m.logger.Printf("[ERR] memberlist: Failed to forward ack: %s %s", err, LogStringAddress(indAddr)) } } m.setAckHandler(localSeqNo, respHandler, m.config.ProbeTimeout) // Send the ping. addr := joinHostPort(net.IP(ind.Target).String(), ind.Port) - if err := m.encodeAndSendMsg(addr, pingMsg, &ping); err != nil { - m.logger.Printf("[ERR] memberlist: Failed to send ping: %s %s", err, LogAddress(from)) + a := Address{ + Addr: addr, + Name: ind.Node, + } + if err := m.encodeAndSendMsg(a, pingMsg, &ping); err != nil { + m.logger.Printf("[ERR] memberlist: Failed to send indirect ping: %s %s", err, LogStringAddress(indAddr)) } // Setup a timer to fire off a nack if no ack is seen in time. @@ -483,8 +587,12 @@ func (m *Memberlist) handleIndirectPing(buf []byte, from net.Addr) { return case <-time.After(m.config.ProbeTimeout): nack := nackResp{ind.SeqNo} - if err := m.encodeAndSendMsg(from.String(), nackRespMsg, &nack); err != nil { - m.logger.Printf("[ERR] memberlist: Failed to send nack: %s %s", err, LogAddress(from)) + a := Address{ + Addr: indAddr, + Name: ind.SourceNode, + } + if err := m.encodeAndSendMsg(a, nackRespMsg, &nack); err != nil { + m.logger.Printf("[ERR] memberlist: Failed to send nack: %s %s", err, LogStringAddress(indAddr)) } } }() @@ -518,12 +626,47 @@ func (m *Memberlist) handleSuspect(buf []byte, from net.Addr) { m.suspectNode(&sus) } +// ensureCanConnect return the IP from a RemoteAddress +// return error if this client must not connect +func (m *Memberlist) ensureCanConnect(from net.Addr) error { + if !m.config.IPMustBeChecked() { + return nil + } + source := from.String() + if source == "pipe" { + return nil + } + host, _, err := net.SplitHostPort(source) + if err != nil { + return err + } + + ip := net.ParseIP(host) + if ip == nil { + return fmt.Errorf("Cannot parse IP from %s", host) + } + return m.config.IPAllowed(ip) +} + func (m *Memberlist) handleAlive(buf []byte, from net.Addr) { + if err := m.ensureCanConnect(from); err != nil { + m.logger.Printf("[DEBUG] memberlist: Blocked alive message: %s %s", err, LogAddress(from)) + return + } var live alive if err := decode(buf, &live); err != nil { m.logger.Printf("[ERR] memberlist: Failed to decode alive message: %s %s", err, LogAddress(from)) return } + if m.config.IPMustBeChecked() { + innerIP := net.IP(live.Addr) + if innerIP != nil { + if err := m.config.IPAllowed(innerIP); err != nil { + m.logger.Printf("[DEBUG] memberlist: Blocked alive.Addr=%s message from: %s %s", innerIP.String(), err, LogAddress(from)) + return + } + } + } // For proto versions < 2, there is no port provided. Mask old // behavior by using the configured port @@ -565,12 +708,12 @@ func (m *Memberlist) handleCompressed(buf []byte, from net.Addr, timestamp time. } // encodeAndSendMsg is used to combine the encoding and sending steps -func (m *Memberlist) encodeAndSendMsg(addr string, msgType messageType, msg interface{}) error { +func (m *Memberlist) encodeAndSendMsg(a Address, msgType messageType, msg interface{}) error { out, err := encode(msgType, msg) if err != nil { return err } - if err := m.sendMsg(addr, out.Bytes()); err != nil { + if err := m.sendMsg(a, out.Bytes()); err != nil { return err } return nil @@ -578,7 +721,7 @@ func (m *Memberlist) encodeAndSendMsg(addr string, msgType messageType, msg inte // sendMsg is used to send a message via packet to another host. It will // opportunistically create a compoundMsg and piggy back other broadcasts. -func (m *Memberlist) sendMsg(addr string, msg []byte) error { +func (m *Memberlist) sendMsg(a Address, msg []byte) error { // Check if we can piggy back any messages bytesAvail := m.config.UDPBufferSize - len(msg) - compoundHeaderOverhead if m.config.EncryptionEnabled() && m.config.GossipVerifyOutgoing { @@ -588,7 +731,7 @@ func (m *Memberlist) sendMsg(addr string, msg []byte) error { // Fast path if nothing to piggypack if len(extra) == 0 { - return m.rawSendMsgPacket(addr, nil, msg) + return m.rawSendMsgPacket(a, nil, msg) } // Join all the messages @@ -600,12 +743,16 @@ func (m *Memberlist) sendMsg(addr string, msg []byte) error { compound := makeCompoundMessage(msgs) // Send the message - return m.rawSendMsgPacket(addr, nil, compound.Bytes()) + return m.rawSendMsgPacket(a, nil, compound.Bytes()) } // rawSendMsgPacket is used to send message via packet to another host without // modification, other than compression or encryption if enabled. -func (m *Memberlist) rawSendMsgPacket(addr string, node *Node, msg []byte) error { +func (m *Memberlist) rawSendMsgPacket(a Address, node *Node, msg []byte) error { + if a.Name == "" && m.config.RequireNodeNames { + return errNodeNamesAreRequired + } + // Check if we have compression enabled if m.config.EnableCompression { buf, err := compressPayload(msg) @@ -619,11 +766,12 @@ func (m *Memberlist) rawSendMsgPacket(addr string, node *Node, msg []byte) error } } - // Try to look up the destination node + // Try to look up the destination node. Note this will only work if the + // bare ip address is used as the node name, which is not guaranteed. if node == nil { - toAddr, _, err := net.SplitHostPort(addr) + toAddr, _, err := net.SplitHostPort(a.Addr) if err != nil { - m.logger.Printf("[ERR] memberlist: Failed to parse address %q: %v", addr, err) + m.logger.Printf("[ERR] memberlist: Failed to parse address %q: %v", a.Addr, err) return err } m.nodeLock.RLock() @@ -658,14 +806,14 @@ func (m *Memberlist) rawSendMsgPacket(addr string, node *Node, msg []byte) error } metrics.IncrCounter([]string{"memberlist", "udp", "sent"}, float32(len(msg))) - _, err := m.transport.WriteTo(msg, addr) + _, err := m.transport.WriteToAddress(msg, a) return err } // rawSendMsgStream is used to stream a message to another host without // modification, other than applying compression and encryption if enabled. func (m *Memberlist) rawSendMsgStream(conn net.Conn, sendBuf []byte) error { - // Check if compresion is enabled + // Check if compression is enabled if m.config.EnableCompression { compBuf, err := compressPayload(sendBuf) if err != nil { @@ -698,8 +846,12 @@ func (m *Memberlist) rawSendMsgStream(conn net.Conn, sendBuf []byte) error { } // sendUserMsg is used to stream a user message to another host. -func (m *Memberlist) sendUserMsg(addr string, sendBuf []byte) error { - conn, err := m.transport.DialTimeout(addr, m.config.TCPTimeout) +func (m *Memberlist) sendUserMsg(a Address, sendBuf []byte) error { + if a.Name == "" && m.config.RequireNodeNames { + return errNodeNamesAreRequired + } + + conn, err := m.transport.DialAddressTimeout(a, m.config.TCPTimeout) if err != nil { return err } @@ -724,14 +876,18 @@ func (m *Memberlist) sendUserMsg(addr string, sendBuf []byte) error { // sendAndReceiveState is used to initiate a push/pull over a stream with a // remote host. -func (m *Memberlist) sendAndReceiveState(addr string, join bool) ([]pushNodeState, []byte, error) { +func (m *Memberlist) sendAndReceiveState(a Address, join bool) ([]pushNodeState, []byte, error) { + if a.Name == "" && m.config.RequireNodeNames { + return nil, nil, errNodeNamesAreRequired + } + // Attempt to connect - conn, err := m.transport.DialTimeout(addr, m.config.TCPTimeout) + conn, err := m.transport.DialAddressTimeout(a, m.config.TCPTimeout) if err != nil { return nil, nil, err } defer conn.Close() - m.logger.Printf("[DEBUG] memberlist: Initiating push/pull sync with: %s", conn.RemoteAddr()) + m.logger.Printf("[DEBUG] memberlist: Initiating push/pull sync with: %s %s", a.Name, conn.RemoteAddr()) metrics.IncrCounter([]string{"memberlist", "tcp", "connect"}, 1) // Send our state @@ -996,16 +1152,17 @@ func (m *Memberlist) mergeRemoteState(join bool, remoteNodes []pushNodeState, us nodes := make([]*Node, len(remoteNodes)) for idx, n := range remoteNodes { nodes[idx] = &Node{ - Name: n.Name, - Addr: n.Addr, - Port: n.Port, - Meta: n.Meta, - PMin: n.Vsn[0], - PMax: n.Vsn[1], - PCur: n.Vsn[2], - DMin: n.Vsn[3], - DMax: n.Vsn[4], - DCur: n.Vsn[5], + Name: n.Name, + Addr: n.Addr, + Port: n.Port, + Meta: n.Meta, + State: n.State, + PMin: n.Vsn[0], + PMax: n.Vsn[1], + PCur: n.Vsn[2], + DMin: n.Vsn[3], + DMax: n.Vsn[4], + DCur: n.Vsn[5], } } if err := m.config.Merge.NotifyMerge(nodes); err != nil { @@ -1058,8 +1215,12 @@ func (m *Memberlist) readUserMsg(bufConn io.Reader, dec *codec.Decoder) error { // a ping, and waits for an ack. All of this is done as a series of blocking // operations, given the deadline. The bool return parameter is true if we // we able to round trip a ping to the other node. -func (m *Memberlist) sendPingAndWaitForAck(addr string, ping ping, deadline time.Time) (bool, error) { - conn, err := m.transport.DialTimeout(addr, deadline.Sub(time.Now())) +func (m *Memberlist) sendPingAndWaitForAck(a Address, ping ping, deadline time.Time) (bool, error) { + if a.Name == "" && m.config.RequireNodeNames { + return false, errNodeNamesAreRequired + } + + conn, err := m.transport.DialAddressTimeout(a, deadline.Sub(time.Now())) if err != nil { // If the node is actually dead we expect this to fail, so we // shouldn't spam the logs with it. After this point, errors @@ -1094,7 +1255,7 @@ func (m *Memberlist) sendPingAndWaitForAck(addr string, ping ping, deadline time } if ack.SeqNo != ping.SeqNo { - return false, fmt.Errorf("Sequence number from ack (%d) doesn't match ping (%d)", ack.SeqNo, ping.SeqNo, LogConn(conn)) + return false, fmt.Errorf("Sequence number from ack (%d) doesn't match ping (%d)", ack.SeqNo, ping.SeqNo) } return true, nil diff --git a/vendor/github.com/hashicorp/memberlist/net_transport.go b/vendor/github.com/hashicorp/memberlist/net_transport.go index e7b88b01f6b14..05830117297c2 100644 --- a/vendor/github.com/hashicorp/memberlist/net_transport.go +++ b/vendor/github.com/hashicorp/memberlist/net_transport.go @@ -1,7 +1,9 @@ package memberlist import ( + "bytes" "fmt" + "io" "log" "net" "sync" @@ -48,6 +50,8 @@ type NetTransport struct { shutdown int32 } +var _ NodeAwareTransport = (*NetTransport)(nil) + // NewNetTransport returns a net transport with the given configuration. On // success all the network listeners will be created and listening. func NewNetTransport(config *NetTransportConfig) (*NetTransport, error) { @@ -170,6 +174,14 @@ func (t *NetTransport) FinalAdvertiseAddr(ip string, port int) (net.IP, int, err // See Transport. func (t *NetTransport) WriteTo(b []byte, addr string) (time.Time, error) { + a := Address{Addr: addr, Name: ""} + return t.WriteToAddress(b, a) +} + +// See NodeAwareTransport. +func (t *NetTransport) WriteToAddress(b []byte, a Address) (time.Time, error) { + addr := a.Addr + udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { return time.Time{}, err @@ -188,8 +200,44 @@ func (t *NetTransport) PacketCh() <-chan *Packet { return t.packetCh } +// See IngestionAwareTransport. +func (t *NetTransport) IngestPacket(conn net.Conn, addr net.Addr, now time.Time, shouldClose bool) error { + if shouldClose { + defer conn.Close() + } + + // Copy everything from the stream into packet buffer. + var buf bytes.Buffer + if _, err := io.Copy(&buf, conn); err != nil { + return fmt.Errorf("failed to read packet: %v", err) + } + + // Check the length - it needs to have at least one byte to be a proper + // message. This is checked elsewhere for writes coming in directly from + // the UDP socket. + if n := buf.Len(); n < 1 { + return fmt.Errorf("packet too short (%d bytes) %s", n, LogAddress(addr)) + } + + // Inject the packet. + t.packetCh <- &Packet{ + Buf: buf.Bytes(), + From: addr, + Timestamp: now, + } + return nil +} + // See Transport. func (t *NetTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) { + a := Address{Addr: addr, Name: ""} + return t.DialAddressTimeout(a, timeout) +} + +// See NodeAwareTransport. +func (t *NetTransport) DialAddressTimeout(a Address, timeout time.Duration) (net.Conn, error) { + addr := a.Addr + dialer := net.Dialer{Timeout: timeout} return dialer.Dial("tcp", addr) } @@ -199,6 +247,12 @@ func (t *NetTransport) StreamCh() <-chan net.Conn { return t.streamCh } +// See IngestionAwareTransport. +func (t *NetTransport) IngestStream(conn net.Conn) error { + t.streamCh <- conn + return nil +} + // See Transport. func (t *NetTransport) Shutdown() error { // This will avoid log spam about errors when we shut down. @@ -221,6 +275,16 @@ func (t *NetTransport) Shutdown() error { // and hands them off to the stream channel. func (t *NetTransport) tcpListen(tcpLn *net.TCPListener) { defer t.wg.Done() + + // baseDelay is the initial delay after an AcceptTCP() error before attempting again + const baseDelay = 5 * time.Millisecond + + // maxDelay is the maximum delay after an AcceptTCP() error before attempting again. + // In the case that tcpListen() is error-looping, it will delay the shutdown check. + // Therefore, changes to maxDelay may have an effect on the latency of shutdown. + const maxDelay = 1 * time.Second + + var loopDelay time.Duration for { conn, err := tcpLn.AcceptTCP() if err != nil { @@ -228,9 +292,22 @@ func (t *NetTransport) tcpListen(tcpLn *net.TCPListener) { break } + if loopDelay == 0 { + loopDelay = baseDelay + } else { + loopDelay *= 2 + } + + if loopDelay > maxDelay { + loopDelay = maxDelay + } + t.logger.Printf("[ERR] memberlist: Error accepting TCP connection: %v", err) + time.Sleep(loopDelay) continue } + // No error, reset loop delay + loopDelay = 0 t.streamCh <- conn } diff --git a/vendor/github.com/hashicorp/memberlist/queue.go b/vendor/github.com/hashicorp/memberlist/queue.go index 994b90ff1040a..c970176e18fda 100644 --- a/vendor/github.com/hashicorp/memberlist/queue.go +++ b/vendor/github.com/hashicorp/memberlist/queue.go @@ -1,8 +1,10 @@ package memberlist import ( - "sort" + "math" "sync" + + "github.com/google/btree" ) // TransmitLimitedQueue is used to queue messages to broadcast to @@ -19,15 +21,93 @@ type TransmitLimitedQueue struct { // number of retransmissions attempted. RetransmitMult int - sync.Mutex - bcQueue limitedBroadcasts + mu sync.Mutex + tq *btree.BTree // stores *limitedBroadcast as btree.Item + tm map[string]*limitedBroadcast + idGen int64 } type limitedBroadcast struct { - transmits int // Number of transmissions attempted. + transmits int // btree-key[0]: Number of transmissions attempted. + msgLen int64 // btree-key[1]: copied from len(b.Message()) + id int64 // btree-key[2]: unique incrementing id stamped at submission time b Broadcast + + name string // set if Broadcast is a NamedBroadcast +} + +// Less tests whether the current item is less than the given argument. +// +// This must provide a strict weak ordering. +// If !a.Less(b) && !b.Less(a), we treat this to mean a == b (i.e. we can only +// hold one of either a or b in the tree). +// +// default ordering is +// - [transmits=0, ..., transmits=inf] +// - [transmits=0:len=999, ..., transmits=0:len=2, ...] +// - [transmits=0:len=999,id=999, ..., transmits=0:len=999:id=1, ...] +func (b *limitedBroadcast) Less(than btree.Item) bool { + o := than.(*limitedBroadcast) + if b.transmits < o.transmits { + return true + } else if b.transmits > o.transmits { + return false + } + if b.msgLen > o.msgLen { + return true + } else if b.msgLen < o.msgLen { + return false + } + return b.id > o.id +} + +// for testing; emits in transmit order if reverse=false +func (q *TransmitLimitedQueue) orderedView(reverse bool) []*limitedBroadcast { + q.mu.Lock() + defer q.mu.Unlock() + + out := make([]*limitedBroadcast, 0, q.lenLocked()) + q.walkReadOnlyLocked(reverse, func(cur *limitedBroadcast) bool { + out = append(out, cur) + return true + }) + + return out +} + +// walkReadOnlyLocked calls f for each item in the queue traversing it in +// natural order (by Less) when reverse=false and the opposite when true. You +// must hold the mutex. +// +// This method panics if you attempt to mutate the item during traversal. The +// underlying btree should also not be mutated during traversal. +func (q *TransmitLimitedQueue) walkReadOnlyLocked(reverse bool, f func(*limitedBroadcast) bool) { + if q.lenLocked() == 0 { + return + } + + iter := func(item btree.Item) bool { + cur := item.(*limitedBroadcast) + + prevTransmits := cur.transmits + prevMsgLen := cur.msgLen + prevID := cur.id + + keepGoing := f(cur) + + if prevTransmits != cur.transmits || prevMsgLen != cur.msgLen || prevID != cur.id { + panic("edited queue while walking read only") + } + + return keepGoing + } + + if reverse { + q.tq.Descend(iter) // end with transmit 0 + } else { + q.tq.Ascend(iter) // start with transmit 0 + } } -type limitedBroadcasts []*limitedBroadcast // Broadcast is something that can be broadcasted via gossip to // the memberlist cluster. @@ -45,123 +125,298 @@ type Broadcast interface { Finished() } +// NamedBroadcast is an optional extension of the Broadcast interface that +// gives each message a unique string name, and that is used to optimize +// +// You shoud ensure that Invalidates() checks the same uniqueness as the +// example below: +// +// func (b *foo) Invalidates(other Broadcast) bool { +// nb, ok := other.(NamedBroadcast) +// if !ok { +// return false +// } +// return b.Name() == nb.Name() +// } +// +// Invalidates() isn't currently used for NamedBroadcasts, but that may change +// in the future. +type NamedBroadcast interface { + Broadcast + // The unique identity of this broadcast message. + Name() string +} + +// UniqueBroadcast is an optional interface that indicates that each message is +// intrinsically unique and there is no need to scan the broadcast queue for +// duplicates. +// +// You should ensure that Invalidates() always returns false if implementing +// this interface. Invalidates() isn't currently used for UniqueBroadcasts, but +// that may change in the future. +type UniqueBroadcast interface { + Broadcast + // UniqueBroadcast is just a marker method for this interface. + UniqueBroadcast() +} + // QueueBroadcast is used to enqueue a broadcast func (q *TransmitLimitedQueue) QueueBroadcast(b Broadcast) { - q.Lock() - defer q.Unlock() - - // Check if this message invalidates another - n := len(q.bcQueue) - for i := 0; i < n; i++ { - if b.Invalidates(q.bcQueue[i].b) { - q.bcQueue[i].b.Finished() - copy(q.bcQueue[i:], q.bcQueue[i+1:]) - q.bcQueue[n-1] = nil - q.bcQueue = q.bcQueue[:n-1] - n-- + q.queueBroadcast(b, 0) +} + +// lazyInit initializes internal data structures the first time they are +// needed. You must already hold the mutex. +func (q *TransmitLimitedQueue) lazyInit() { + if q.tq == nil { + q.tq = btree.New(32) + } + if q.tm == nil { + q.tm = make(map[string]*limitedBroadcast) + } +} + +// queueBroadcast is like QueueBroadcast but you can use a nonzero value for +// the initial transmit tier assigned to the message. This is meant to be used +// for unit testing. +func (q *TransmitLimitedQueue) queueBroadcast(b Broadcast, initialTransmits int) { + q.mu.Lock() + defer q.mu.Unlock() + + q.lazyInit() + + if q.idGen == math.MaxInt64 { + // it's super duper unlikely to wrap around within the retransmit limit + q.idGen = 1 + } else { + q.idGen++ + } + id := q.idGen + + lb := &limitedBroadcast{ + transmits: initialTransmits, + msgLen: int64(len(b.Message())), + id: id, + b: b, + } + unique := false + if nb, ok := b.(NamedBroadcast); ok { + lb.name = nb.Name() + } else if _, ok := b.(UniqueBroadcast); ok { + unique = true + } + + // Check if this message invalidates another. + if lb.name != "" { + if old, ok := q.tm[lb.name]; ok { + old.b.Finished() + q.deleteItem(old) + } + } else if !unique { + // Slow path, hopefully nothing hot hits this. + var remove []*limitedBroadcast + q.tq.Ascend(func(item btree.Item) bool { + cur := item.(*limitedBroadcast) + + // Special Broadcasts can only invalidate each other. + switch cur.b.(type) { + case NamedBroadcast: + // noop + case UniqueBroadcast: + // noop + default: + if b.Invalidates(cur.b) { + cur.b.Finished() + remove = append(remove, cur) + } + } + return true + }) + for _, cur := range remove { + q.deleteItem(cur) } } - // Append to the queue - q.bcQueue = append(q.bcQueue, &limitedBroadcast{0, b}) + // Append to the relevant queue. + q.addItem(lb) +} + +// deleteItem removes the given item from the overall datastructure. You +// must already hold the mutex. +func (q *TransmitLimitedQueue) deleteItem(cur *limitedBroadcast) { + _ = q.tq.Delete(cur) + if cur.name != "" { + delete(q.tm, cur.name) + } + + if q.tq.Len() == 0 { + // At idle there's no reason to let the id generator keep going + // indefinitely. + q.idGen = 0 + } +} + +// addItem adds the given item into the overall datastructure. You must already +// hold the mutex. +func (q *TransmitLimitedQueue) addItem(cur *limitedBroadcast) { + _ = q.tq.ReplaceOrInsert(cur) + if cur.name != "" { + q.tm[cur.name] = cur + } +} + +// getTransmitRange returns a pair of min/max values for transmit values +// represented by the current queue contents. Both values represent actual +// transmit values on the interval [0, len). You must already hold the mutex. +func (q *TransmitLimitedQueue) getTransmitRange() (minTransmit, maxTransmit int) { + if q.lenLocked() == 0 { + return 0, 0 + } + minItem, maxItem := q.tq.Min(), q.tq.Max() + if minItem == nil || maxItem == nil { + return 0, 0 + } + + min := minItem.(*limitedBroadcast).transmits + max := maxItem.(*limitedBroadcast).transmits + + return min, max } // GetBroadcasts is used to get a number of broadcasts, up to a byte limit // and applying a per-message overhead as provided. func (q *TransmitLimitedQueue) GetBroadcasts(overhead, limit int) [][]byte { - q.Lock() - defer q.Unlock() + q.mu.Lock() + defer q.mu.Unlock() // Fast path the default case - if len(q.bcQueue) == 0 { + if q.lenLocked() == 0 { return nil } transmitLimit := retransmitLimit(q.RetransmitMult, q.NumNodes()) - bytesUsed := 0 - var toSend [][]byte - - for i := len(q.bcQueue) - 1; i >= 0; i-- { - // Check if this is within our limits - b := q.bcQueue[i] - msg := b.b.Message() - if bytesUsed+overhead+len(msg) > limit { + + var ( + bytesUsed int + toSend [][]byte + reinsert []*limitedBroadcast + ) + + // Visit fresher items first, but only look at stuff that will fit. + // We'll go tier by tier, grabbing the largest items first. + minTr, maxTr := q.getTransmitRange() + for transmits := minTr; transmits <= maxTr; /*do not advance automatically*/ { + free := int64(limit - bytesUsed - overhead) + if free <= 0 { + break // bail out early + } + + // Search for the least element on a given tier (by transmit count) as + // defined in the limitedBroadcast.Less function that will fit into our + // remaining space. + greaterOrEqual := &limitedBroadcast{ + transmits: transmits, + msgLen: free, + id: math.MaxInt64, + } + lessThan := &limitedBroadcast{ + transmits: transmits + 1, + msgLen: math.MaxInt64, + id: math.MaxInt64, + } + var keep *limitedBroadcast + q.tq.AscendRange(greaterOrEqual, lessThan, func(item btree.Item) bool { + cur := item.(*limitedBroadcast) + // Check if this is within our limits + if int64(len(cur.b.Message())) > free { + // If this happens it's a bug in the datastructure or + // surrounding use doing something like having len(Message()) + // change over time. There's enough going on here that it's + // probably sane to just skip it and move on for now. + return true + } + keep = cur + return false + }) + if keep == nil { + // No more items of an appropriate size in the tier. + transmits++ continue } + msg := keep.b.Message() + // Add to slice to send bytesUsed += overhead + len(msg) toSend = append(toSend, msg) // Check if we should stop transmission - b.transmits++ - if b.transmits >= transmitLimit { - b.b.Finished() - n := len(q.bcQueue) - q.bcQueue[i], q.bcQueue[n-1] = q.bcQueue[n-1], nil - q.bcQueue = q.bcQueue[:n-1] + q.deleteItem(keep) + if keep.transmits+1 >= transmitLimit { + keep.b.Finished() + } else { + // We need to bump this item down to another transmit tier, but + // because it would be in the same direction that we're walking the + // tiers, we will have to delay the reinsertion until we are + // finished our search. Otherwise we'll possibly re-add the message + // when we ascend to the next tier. + keep.transmits++ + reinsert = append(reinsert, keep) } } - // If we are sending anything, we need to re-sort to deal - // with adjusted transmit counts - if len(toSend) > 0 { - q.bcQueue.Sort() + for _, cur := range reinsert { + q.addItem(cur) } + return toSend } // NumQueued returns the number of queued messages func (q *TransmitLimitedQueue) NumQueued() int { - q.Lock() - defer q.Unlock() - return len(q.bcQueue) + q.mu.Lock() + defer q.mu.Unlock() + return q.lenLocked() } -// Reset clears all the queued messages -func (q *TransmitLimitedQueue) Reset() { - q.Lock() - defer q.Unlock() - for _, b := range q.bcQueue { - b.b.Finished() +// lenLocked returns the length of the overall queue datastructure. You must +// hold the mutex. +func (q *TransmitLimitedQueue) lenLocked() int { + if q.tq == nil { + return 0 } - q.bcQueue = nil + return q.tq.Len() +} + +// Reset clears all the queued messages. Should only be used for tests. +func (q *TransmitLimitedQueue) Reset() { + q.mu.Lock() + defer q.mu.Unlock() + + q.walkReadOnlyLocked(false, func(cur *limitedBroadcast) bool { + cur.b.Finished() + return true + }) + + q.tq = nil + q.tm = nil + q.idGen = 0 } // Prune will retain the maxRetain latest messages, and the rest // will be discarded. This can be used to prevent unbounded queue sizes func (q *TransmitLimitedQueue) Prune(maxRetain int) { - q.Lock() - defer q.Unlock() + q.mu.Lock() + defer q.mu.Unlock() // Do nothing if queue size is less than the limit - n := len(q.bcQueue) - if n < maxRetain { - return - } - - // Invalidate the messages we will be removing - for i := 0; i < n-maxRetain; i++ { - q.bcQueue[i].b.Finished() + for q.tq.Len() > maxRetain { + item := q.tq.Max() + if item == nil { + break + } + cur := item.(*limitedBroadcast) + cur.b.Finished() + q.deleteItem(cur) } - - // Move the messages, and retain only the last maxRetain - copy(q.bcQueue[0:], q.bcQueue[n-maxRetain:]) - q.bcQueue = q.bcQueue[:maxRetain] -} - -func (b limitedBroadcasts) Len() int { - return len(b) -} - -func (b limitedBroadcasts) Less(i, j int) bool { - return b[i].transmits < b[j].transmits -} - -func (b limitedBroadcasts) Swap(i, j int) { - b[i], b[j] = b[j], b[i] -} - -func (b limitedBroadcasts) Sort() { - sort.Sort(sort.Reverse(b)) } diff --git a/vendor/github.com/hashicorp/memberlist/security.go b/vendor/github.com/hashicorp/memberlist/security.go index d90114eb0c4f3..4cb4da36f05ba 100644 --- a/vendor/github.com/hashicorp/memberlist/security.go +++ b/vendor/github.com/hashicorp/memberlist/security.go @@ -106,7 +106,10 @@ func encryptPayload(vsn encryptionVersion, key []byte, msg []byte, data []byte, dst.WriteByte(byte(vsn)) // Add a random nonce - io.CopyN(dst, rand.Reader, nonceSize) + _, err = io.CopyN(dst, rand.Reader, nonceSize) + if err != nil { + return err + } afterNonce := dst.Len() // Ensure we are correctly padded (only version 0) diff --git a/vendor/github.com/hashicorp/memberlist/state.go b/vendor/github.com/hashicorp/memberlist/state.go index f51692de0ac70..5e4f7fdd704cc 100644 --- a/vendor/github.com/hashicorp/memberlist/state.go +++ b/vendor/github.com/hashicorp/memberlist/state.go @@ -6,32 +6,35 @@ import ( "math" "math/rand" "net" + "strings" "sync/atomic" "time" - "github.com/armon/go-metrics" + metrics "github.com/armon/go-metrics" ) -type nodeStateType int +type NodeStateType int const ( - stateAlive nodeStateType = iota - stateSuspect - stateDead + StateAlive NodeStateType = iota + StateSuspect + StateDead + StateLeft ) // Node represents a node in the cluster. type Node struct { - Name string - Addr net.IP - Port uint16 - Meta []byte // Metadata from the delegate for this node. - PMin uint8 // Minimum protocol version this understands - PMax uint8 // Maximum protocol version this understands - PCur uint8 // Current version node is speaking - DMin uint8 // Min protocol version for the delegate to understand - DMax uint8 // Max protocol version for the delegate to understand - DCur uint8 // Current version delegate is speaking + Name string + Addr net.IP + Port uint16 + Meta []byte // Metadata from the delegate for this node. + State NodeStateType // State of the node. + PMin uint8 // Minimum protocol version this understands + PMax uint8 // Maximum protocol version this understands + PCur uint8 // Current version node is speaking + DMin uint8 // Min protocol version for the delegate to understand + DMax uint8 // Max protocol version for the delegate to understand + DCur uint8 // Current version delegate is speaking } // Address returns the host:port form of a node's address, suitable for use @@ -40,6 +43,15 @@ func (n *Node) Address() string { return joinHostPort(n.Addr.String(), n.Port) } +// FullAddress returns the node name and host:port form of a node's address, +// suitable for use with a transport. +func (n *Node) FullAddress() Address { + return Address{ + Addr: joinHostPort(n.Addr.String(), n.Port), + Name: n.Name, + } +} + // String returns the node name func (n *Node) String() string { return n.Name @@ -49,7 +61,7 @@ func (n *Node) String() string { type nodeState struct { Node Incarnation uint32 // Last known incarnation number - State nodeStateType // Current state + State NodeStateType // Current state StateChange time.Time // Time last state change happened } @@ -59,6 +71,16 @@ func (n *nodeState) Address() string { return n.Node.Address() } +// FullAddress returns the node name and host:port form of a node's address, +// suitable for use with a transport. +func (n *nodeState) FullAddress() Address { + return n.Node.FullAddress() +} + +func (n *nodeState) DeadOrLeft() bool { + return n.State == StateDead || n.State == StateLeft +} + // ackHandler is used to register handlers for incoming acks and nacks. type ackHandler struct { ackFn func([]byte, time.Time) @@ -217,7 +239,7 @@ START: node = *m.nodes[m.probeIndex] if node.Name == m.config.Name { skip = true - } else if node.State == stateDead { + } else if node.DeadOrLeft() { skip = true } @@ -233,6 +255,30 @@ START: m.probeNode(&node) } +// probeNodeByAddr just safely calls probeNode given only the address of the node (for tests) +func (m *Memberlist) probeNodeByAddr(addr string) { + m.nodeLock.RLock() + n := m.nodeMap[addr] + m.nodeLock.RUnlock() + + m.probeNode(n) +} + +// failedRemote checks the error and decides if it indicates a failure on the +// other end. +func failedRemote(err error) bool { + switch t := err.(type) { + case *net.OpError: + if strings.HasPrefix(t.Net, "tcp") { + switch t.Op { + case "dial", "read", "write": + return true + } + } + } + return false +} + // probeNode handles a single round of failure checking on a node. func (m *Memberlist) probeNode(node *nodeState) { defer metrics.MeasureSince([]string{"memberlist", "probeNode"}, time.Now()) @@ -246,7 +292,14 @@ func (m *Memberlist) probeNode(node *nodeState) { } // Prepare a ping message and setup an ack handler. - ping := ping{SeqNo: m.nextSeqNo(), Node: node.Name} + selfAddr, selfPort := m.getAdvertise() + ping := ping{ + SeqNo: m.nextSeqNo(), + Node: node.Name, + SourceAddr: selfAddr, + SourcePort: selfPort, + SourceNode: m.config.Name, + } ackCh := make(chan ackMessage, m.config.IndirectChecks+1) nackCh := make(chan struct{}, m.config.IndirectChecks+1) m.setProbeChannels(ping.SeqNo, ackCh, nackCh, probeInterval) @@ -263,10 +316,20 @@ func (m *Memberlist) probeNode(node *nodeState) { // soon as possible. deadline := sent.Add(probeInterval) addr := node.Address() - if node.State == stateAlive { - if err := m.encodeAndSendMsg(addr, pingMsg, &ping); err != nil { + + // Arrange for our self-awareness to get updated. + var awarenessDelta int + defer func() { + m.awareness.ApplyDelta(awarenessDelta) + }() + if node.State == StateAlive { + if err := m.encodeAndSendMsg(node.FullAddress(), pingMsg, &ping); err != nil { m.logger.Printf("[ERR] memberlist: Failed to send ping: %s", err) - return + if failedRemote(err) { + goto HANDLE_REMOTE_FAILURE + } else { + return + } } } else { var msgs [][]byte @@ -285,9 +348,13 @@ func (m *Memberlist) probeNode(node *nodeState) { } compound := makeCompoundMessage(msgs) - if err := m.rawSendMsgPacket(addr, &node.Node, compound.Bytes()); err != nil { + if err := m.rawSendMsgPacket(node.FullAddress(), &node.Node, compound.Bytes()); err != nil { m.logger.Printf("[ERR] memberlist: Failed to send compound ping and suspect message to %s: %s", addr, err) - return + if failedRemote(err) { + goto HANDLE_REMOTE_FAILURE + } else { + return + } } } @@ -296,10 +363,7 @@ func (m *Memberlist) probeNode(node *nodeState) { // which will improve our health until we get to the failure scenarios // at the end of this function, which will alter this delta variable // accordingly. - awarenessDelta := -1 - defer func() { - m.awareness.ApplyDelta(awarenessDelta) - }() + awarenessDelta = -1 // Wait for response or round-trip-time. select { @@ -324,21 +388,31 @@ func (m *Memberlist) probeNode(node *nodeState) { // probe interval it will give the TCP fallback more time, which // is more active in dealing with lost packets, and it gives more // time to wait for indirect acks/nacks. - m.logger.Printf("[DEBUG] memberlist: Failed ping: %v (timeout reached)", node.Name) + m.logger.Printf("[DEBUG] memberlist: Failed ping: %s (timeout reached)", node.Name) } +HANDLE_REMOTE_FAILURE: // Get some random live nodes. m.nodeLock.RLock() kNodes := kRandomNodes(m.config.IndirectChecks, m.nodes, func(n *nodeState) bool { return n.Name == m.config.Name || n.Name == node.Name || - n.State != stateAlive + n.State != StateAlive }) m.nodeLock.RUnlock() // Attempt an indirect ping. expectedNacks := 0 - ind := indirectPingReq{SeqNo: ping.SeqNo, Target: node.Addr, Port: node.Port, Node: node.Name} + selfAddr, selfPort = m.getAdvertise() + ind := indirectPingReq{ + SeqNo: ping.SeqNo, + Target: node.Addr, + Port: node.Port, + Node: node.Name, + SourceAddr: selfAddr, + SourcePort: selfPort, + SourceNode: m.config.Name, + } for _, peer := range kNodes { // We only expect nack to be sent from peers who understand // version 4 of the protocol. @@ -346,7 +420,7 @@ func (m *Memberlist) probeNode(node *nodeState) { expectedNacks++ } - if err := m.encodeAndSendMsg(peer.Address(), indirectPingMsg, &ind); err != nil { + if err := m.encodeAndSendMsg(peer.FullAddress(), indirectPingMsg, &ind); err != nil { m.logger.Printf("[ERR] memberlist: Failed to send indirect ping: %s", err) } } @@ -362,10 +436,13 @@ func (m *Memberlist) probeNode(node *nodeState) { // which protocol version we are speaking. That's why we've included a // config option to turn this off if desired. fallbackCh := make(chan bool, 1) - if (!m.config.DisableTcpPings) && (node.PMax >= 3) { + + disableTcpPings := m.config.DisableTcpPings || + (m.config.DisableTcpPingsForNode != nil && m.config.DisableTcpPingsForNode(node.Name)) + if (!disableTcpPings) && (node.PMax >= 3) { go func() { defer close(fallbackCh) - didContact, err := m.sendPingAndWaitForAck(node.Address(), ping, deadline) + didContact, err := m.sendPingAndWaitForAck(node.FullAddress(), ping, deadline) if err != nil { m.logger.Printf("[ERR] memberlist: Failed fallback ping: %s", err) } else { @@ -422,12 +499,21 @@ func (m *Memberlist) probeNode(node *nodeState) { // Ping initiates a ping to the node with the specified name. func (m *Memberlist) Ping(node string, addr net.Addr) (time.Duration, error) { // Prepare a ping message and setup an ack handler. - ping := ping{SeqNo: m.nextSeqNo(), Node: node} + selfAddr, selfPort := m.getAdvertise() + ping := ping{ + SeqNo: m.nextSeqNo(), + Node: node, + SourceAddr: selfAddr, + SourcePort: selfPort, + SourceNode: m.config.Name, + } ackCh := make(chan ackMessage, m.config.IndirectChecks+1) m.setProbeChannels(ping.SeqNo, ackCh, nil, m.config.ProbeInterval) + a := Address{Addr: addr.String(), Name: node} + // Send a ping to the node. - if err := m.encodeAndSendMsg(addr.String(), pingMsg, &ping); err != nil { + if err := m.encodeAndSendMsg(a, pingMsg, &ping); err != nil { return 0, err } @@ -488,10 +574,10 @@ func (m *Memberlist) gossip() { } switch n.State { - case stateAlive, stateSuspect: + case StateAlive, StateSuspect: return false - case stateDead: + case StateDead: return time.Since(n.StateChange) > m.config.GossipToTheDeadTime default: @@ -516,13 +602,13 @@ func (m *Memberlist) gossip() { addr := node.Address() if len(msgs) == 1 { // Send single message as is - if err := m.rawSendMsgPacket(addr, &node.Node, msgs[0]); err != nil { + if err := m.rawSendMsgPacket(node.FullAddress(), &node, msgs[0]); err != nil { m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", addr, err) } } else { // Otherwise create and send a compound message compound := makeCompoundMessage(msgs) - if err := m.rawSendMsgPacket(addr, &node.Node, compound.Bytes()); err != nil { + if err := m.rawSendMsgPacket(node.FullAddress(), &node, compound.Bytes()); err != nil { m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", addr, err) } } @@ -538,7 +624,7 @@ func (m *Memberlist) pushPull() { m.nodeLock.RLock() nodes := kRandomNodes(1, m.nodes, func(n *nodeState) bool { return n.Name == m.config.Name || - n.State != stateAlive + n.State != StateAlive }) m.nodeLock.RUnlock() @@ -549,17 +635,17 @@ func (m *Memberlist) pushPull() { node := nodes[0] // Attempt a push pull - if err := m.pushPullNode(node.Address(), false); err != nil { + if err := m.pushPullNode(node.FullAddress(), false); err != nil { m.logger.Printf("[ERR] memberlist: Push/Pull with %s failed: %s", node.Name, err) } } // pushPullNode does a complete state exchange with a specific node. -func (m *Memberlist) pushPullNode(addr string, join bool) error { +func (m *Memberlist) pushPullNode(a Address, join bool) error { defer metrics.MeasureSince([]string{"memberlist", "pushPullNode"}, time.Now()) // Attempt to send and receive with the node - remote, userState, err := m.sendAndReceiveState(addr, join) + remote, userState, err := m.sendAndReceiveState(a, join) if err != nil { return err } @@ -596,7 +682,7 @@ func (m *Memberlist) verifyProtocol(remote []pushNodeState) error { for _, rn := range remote { // If the node isn't alive, then skip it - if rn.State != stateAlive { + if rn.State != StateAlive { continue } @@ -625,7 +711,7 @@ func (m *Memberlist) verifyProtocol(remote []pushNodeState) error { for _, n := range m.nodes { // Ignore non-alive nodes - if n.State != stateAlive { + if n.State != StateAlive { continue } @@ -841,11 +927,26 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) { return } + if len(a.Vsn) >= 3 { + pMin := a.Vsn[0] + pMax := a.Vsn[1] + pCur := a.Vsn[2] + if pMin == 0 || pMax == 0 || pMin > pMax { + m.logger.Printf("[WARN] memberlist: Ignoring an alive message for '%s' (%v:%d) because protocol version(s) are wrong: %d <= %d <= %d should be >0", a.Node, net.IP(a.Addr), a.Port, pMin, pCur, pMax) + return + } + } + // Invoke the Alive delegate if any. This can be used to filter out // alive messages based on custom logic. For example, using a cluster name. // Using a merge delegate is not enough, as it is possible for passive // cluster merging to still occur. if m.config.Alive != nil { + if len(a.Vsn) < 6 { + m.logger.Printf("[WARN] memberlist: ignoring alive message for '%s' (%v:%d) because Vsn is not present", + a.Node, net.IP(a.Addr), a.Port) + return + } node := &Node{ Name: a.Node, Addr: a.Addr, @@ -867,7 +968,13 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) { // Check if we've never seen this node before, and if not, then // store this node in our node map. + var updatesNode bool if !ok { + errCon := m.config.IPAllowed(a.Addr) + if errCon != nil { + m.logger.Printf("[WARN] memberlist: Rejected node %s (%v): %s", a.Node, net.IP(a.Addr), errCon) + return + } state = &nodeState{ Node: Node{ Name: a.Node, @@ -875,7 +982,15 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) { Port: a.Port, Meta: a.Meta, }, - State: stateDead, + State: StateDead, + } + if len(a.Vsn) > 5 { + state.PMin = a.Vsn[0] + state.PMax = a.Vsn[1] + state.PCur = a.Vsn[2] + state.DMin = a.Vsn[3] + state.DMax = a.Vsn[4] + state.DCur = a.Vsn[5] } // Add to map @@ -894,29 +1009,45 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) { // Update numNodes after we've added a new node atomic.AddUint32(&m.numNodes, 1) - } - - // Check if this address is different than the existing node - if !bytes.Equal([]byte(state.Addr), a.Addr) || state.Port != a.Port { - m.logger.Printf("[ERR] memberlist: Conflicting address for %s. Mine: %v:%d Theirs: %v:%d", - state.Name, state.Addr, state.Port, net.IP(a.Addr), a.Port) - - // Inform the conflict delegate if provided - if m.config.Conflict != nil { - other := Node{ - Name: a.Node, - Addr: a.Addr, - Port: a.Port, - Meta: a.Meta, + } else { + // Check if this address is different than the existing node unless the old node is dead. + if !bytes.Equal([]byte(state.Addr), a.Addr) || state.Port != a.Port { + errCon := m.config.IPAllowed(a.Addr) + if errCon != nil { + m.logger.Printf("[WARN] memberlist: Rejected IP update from %v to %v for node %s: %s", a.Node, state.Addr, net.IP(a.Addr), errCon) + return + } + // If DeadNodeReclaimTime is configured, check if enough time has elapsed since the node died. + canReclaim := (m.config.DeadNodeReclaimTime > 0 && + time.Since(state.StateChange) > m.config.DeadNodeReclaimTime) + + // Allow the address to be updated if a dead node is being replaced. + if state.State == StateLeft || (state.State == StateDead && canReclaim) { + m.logger.Printf("[INFO] memberlist: Updating address for left or failed node %s from %v:%d to %v:%d", + state.Name, state.Addr, state.Port, net.IP(a.Addr), a.Port) + updatesNode = true + } else { + m.logger.Printf("[ERR] memberlist: Conflicting address for %s. Mine: %v:%d Theirs: %v:%d Old state: %v", + state.Name, state.Addr, state.Port, net.IP(a.Addr), a.Port, state.State) + + // Inform the conflict delegate if provided + if m.config.Conflict != nil { + other := Node{ + Name: a.Node, + Addr: a.Addr, + Port: a.Port, + Meta: a.Meta, + } + m.config.Conflict.NotifyConflict(&state.Node, &other) + } + return } - m.config.Conflict.NotifyConflict(&state.Node, &other) } - return } // Bail if the incarnation number is older, and this is not about us isLocalNode := state.Name == m.config.Name - if a.Incarnation <= state.Incarnation && !isLocalNode { + if a.Incarnation <= state.Incarnation && !isLocalNode && !updatesNode { return } @@ -956,9 +1087,8 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) { bytes.Equal(a.Vsn, versions) { return } - m.refute(state, a.Incarnation) - m.logger.Printf("[WARN] memberlist: Refuting an alive message") + m.logger.Printf("[WARN] memberlist: Refuting an alive message for '%s' (%v:%d) meta:(%v VS %v), vsn:(%v VS %v)", a.Node, net.IP(a.Addr), a.Port, a.Meta, state.Meta, a.Vsn, versions) } else { m.encodeBroadcastNotify(a.Node, aliveMsg, a, notify) @@ -975,8 +1105,10 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) { // Update the state and incarnation number state.Incarnation = a.Incarnation state.Meta = a.Meta - if state.State != stateAlive { - state.State = stateAlive + state.Addr = a.Addr + state.Port = a.Port + if state.State != StateAlive { + state.State = StateAlive state.StateChange = time.Now() } } @@ -986,8 +1118,8 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) { // Notify the delegate of any relevant updates if m.config.Events != nil { - if oldState == stateDead { - // if Dead -> Alive, notify of join + if oldState == StateDead || oldState == StateLeft { + // if Dead/Left -> Alive, notify of join m.config.Events.NotifyJoin(&state.Node) } else if !bytes.Equal(oldMeta, state.Meta) { @@ -1026,7 +1158,7 @@ func (m *Memberlist) suspectNode(s *suspect) { } // Ignore non-alive nodes - if state.State != stateAlive { + if state.State != StateAlive { return } @@ -1044,7 +1176,7 @@ func (m *Memberlist) suspectNode(s *suspect) { // Update the state state.Incarnation = s.Incarnation - state.State = stateSuspect + state.State = StateSuspect changeTime := time.Now() state.StateChange = changeTime @@ -1066,9 +1198,14 @@ func (m *Memberlist) suspectNode(s *suspect) { min := suspicionTimeout(m.config.SuspicionMult, n, m.config.ProbeInterval) max := time.Duration(m.config.SuspicionMaxTimeoutMult) * min fn := func(numConfirmations int) { + var d *dead + m.nodeLock.Lock() state, ok := m.nodeMap[s.Node] - timeout := ok && state.State == stateSuspect && state.StateChange == changeTime + timeout := ok && state.State == StateSuspect && state.StateChange == changeTime + if timeout { + d = &dead{Incarnation: state.Incarnation, Node: state.Name, From: m.config.Name} + } m.nodeLock.Unlock() if timeout { @@ -1078,8 +1215,8 @@ func (m *Memberlist) suspectNode(s *suspect) { m.logger.Printf("[INFO] memberlist: Marking %s as failed, suspect timeout reached (%d peer confirmations)", state.Name, numConfirmations) - d := dead{Incarnation: state.Incarnation, Node: state.Name, From: m.config.Name} - m.deadNode(&d) + + m.deadNode(d) } } m.nodeTimers[s.Node] = newSuspicion(s.From, k, min, max, fn) @@ -1106,7 +1243,7 @@ func (m *Memberlist) deadNode(d *dead) { delete(m.nodeTimers, d.Node) // Ignore if node is already dead - if state.State == stateDead { + if state.DeadOrLeft() { return } @@ -1130,7 +1267,14 @@ func (m *Memberlist) deadNode(d *dead) { // Update the state state.Incarnation = d.Incarnation - state.State = stateDead + + // If the dead message was send by the node itself, mark it is left + // instead of dead. + if d.Node == d.From { + state.State = StateLeft + } else { + state.State = StateDead + } state.StateChange = time.Now() // Notify of death @@ -1144,7 +1288,7 @@ func (m *Memberlist) deadNode(d *dead) { func (m *Memberlist) mergeState(remote []pushNodeState) { for _, r := range remote { switch r.State { - case stateAlive: + case StateAlive: a := alive{ Incarnation: r.Incarnation, Node: r.Name, @@ -1155,11 +1299,14 @@ func (m *Memberlist) mergeState(remote []pushNodeState) { } m.aliveNode(&a, nil, false) - case stateDead: + case StateLeft: + d := dead{Incarnation: r.Incarnation, Node: r.Name, From: r.Name} + m.deadNode(&d) + case StateDead: // If the remote node believes a node is dead, we prefer to // suspect that node instead of declaring it dead instantly fallthrough - case stateSuspect: + case StateSuspect: s := suspect{Incarnation: r.Incarnation, Node: r.Name, From: m.config.Name} m.suspectNode(&s) } diff --git a/vendor/github.com/hashicorp/memberlist/transport.go b/vendor/github.com/hashicorp/memberlist/transport.go index 6ce55ea47f88c..b23b83914b98e 100644 --- a/vendor/github.com/hashicorp/memberlist/transport.go +++ b/vendor/github.com/hashicorp/memberlist/transport.go @@ -1,6 +1,7 @@ package memberlist import ( + "fmt" "net" "time" ) @@ -63,3 +64,50 @@ type Transport interface { // transport a chance to clean up any listeners. Shutdown() error } + +type Address struct { + // Addr is a network address as a string, similar to Dial. This usually is + // in the form of "host:port". This is required. + Addr string + + // Name is the name of the node being addressed. This is optional but + // transports may require it. + Name string +} + +func (a *Address) String() string { + if a.Name != "" { + return fmt.Sprintf("%s (%s)", a.Name, a.Addr) + } + return a.Addr +} + +// IngestionAwareTransport is not used. +// +// Deprecated: IngestionAwareTransport is not used and may be removed in a future +// version. Define the interface locally instead of referencing this exported +// interface. +type IngestionAwareTransport interface { + IngestPacket(conn net.Conn, addr net.Addr, now time.Time, shouldClose bool) error + IngestStream(conn net.Conn) error +} + +type NodeAwareTransport interface { + Transport + WriteToAddress(b []byte, addr Address) (time.Time, error) + DialAddressTimeout(addr Address, timeout time.Duration) (net.Conn, error) +} + +type shimNodeAwareTransport struct { + Transport +} + +var _ NodeAwareTransport = (*shimNodeAwareTransport)(nil) + +func (t *shimNodeAwareTransport) WriteToAddress(b []byte, addr Address) (time.Time, error) { + return t.WriteTo(b, addr.Addr) +} + +func (t *shimNodeAwareTransport) DialAddressTimeout(addr Address, timeout time.Duration) (net.Conn, error) { + return t.DialTimeout(addr.Addr, timeout) +} diff --git a/vendor/github.com/hashicorp/memberlist/util.go b/vendor/github.com/hashicorp/memberlist/util.go index e2381a69866ef..16a7d36d0b65d 100644 --- a/vendor/github.com/hashicorp/memberlist/util.go +++ b/vendor/github.com/hashicorp/memberlist/util.go @@ -78,10 +78,9 @@ func retransmitLimit(retransmitMult, n int) int { // shuffleNodes randomly shuffles the input nodes using the Fisher-Yates shuffle func shuffleNodes(nodes []*nodeState) { n := len(nodes) - for i := n - 1; i > 0; i-- { - j := rand.Intn(i + 1) + rand.Shuffle(n, func(i, j int) { nodes[i], nodes[j] = nodes[j], nodes[i] - } + }) } // pushPushScale is used to scale the time interval at which push/pull @@ -103,7 +102,7 @@ func moveDeadNodes(nodes []*nodeState, gossipToTheDeadTime time.Duration) int { numDead := 0 n := len(nodes) for i := 0; i < n-numDead; i++ { - if nodes[i].State != stateDead { + if nodes[i].State != StateDead { continue } @@ -120,35 +119,35 @@ func moveDeadNodes(nodes []*nodeState, gossipToTheDeadTime time.Duration) int { return n - numDead } -// kRandomNodes is used to select up to k random nodes, excluding any nodes where -// the filter function returns true. It is possible that less than k nodes are +// kRandomNodes is used to select up to k random Nodes, excluding any nodes where +// the exclude function returns true. It is possible that less than k nodes are // returned. -func kRandomNodes(k int, nodes []*nodeState, filterFn func(*nodeState) bool) []*nodeState { +func kRandomNodes(k int, nodes []*nodeState, exclude func(*nodeState) bool) []Node { n := len(nodes) - kNodes := make([]*nodeState, 0, k) + kNodes := make([]Node, 0, k) OUTER: // Probe up to 3*n times, with large n this is not necessary // since k << n, but with small n we want search to be // exhaustive for i := 0; i < 3*n && len(kNodes) < k; i++ { - // Get random node + // Get random nodeState idx := randomOffset(n) - node := nodes[idx] + state := nodes[idx] // Give the filter a shot at it. - if filterFn != nil && filterFn(node) { + if exclude != nil && exclude(state) { continue OUTER } // Check if we have this node already for j := 0; j < len(kNodes); j++ { - if node == kNodes[j] { + if state.Node.Name == kNodes[j].Name { continue OUTER } } // Append the node - kNodes = append(kNodes, node) + kNodes = append(kNodes, state.Node) } return kNodes } @@ -186,18 +185,18 @@ func decodeCompoundMessage(buf []byte) (trunc int, parts [][]byte, err error) { err = fmt.Errorf("missing compound length byte") return } - numParts := uint8(buf[0]) + numParts := int(buf[0]) buf = buf[1:] // Check we have enough bytes - if len(buf) < int(numParts*2) { + if len(buf) < numParts*2 { err = fmt.Errorf("truncated len slice") return } // Decode the lengths lengths := make([]uint16, numParts) - for i := 0; i < int(numParts); i++ { + for i := 0; i < numParts; i++ { lengths[i] = binary.BigEndian.Uint16(buf[i*2 : i*2+2]) } buf = buf[numParts*2:] @@ -205,7 +204,7 @@ func decodeCompoundMessage(buf []byte) (trunc int, parts [][]byte, err error) { // Split each message for idx, msgLen := range lengths { if len(buf) < int(msgLen) { - trunc = int(numParts) - idx + trunc = numParts - idx return } diff --git a/vendor/github.com/imdario/mergo/README.md b/vendor/github.com/imdario/mergo/README.md index 8b76f1fbf33a6..aa8cbd7ce6d73 100644 --- a/vendor/github.com/imdario/mergo/README.md +++ b/vendor/github.com/imdario/mergo/README.md @@ -1,43 +1,54 @@ # Mergo -A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. - -Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche. - -## Status - -It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc](https://github.com/imdario/mergo#mergo-in-the-wild). [![GoDoc][3]][4] -[![GoCard][5]][6] +[![GitHub release][5]][6] +[![GoCard][7]][8] [![Build Status][1]][2] -[![Coverage Status][7]][8] -[![Sourcegraph][9]][10] +[![Coverage Status][9]][10] +[![Sourcegraph][11]][12] +[![FOSSA Status][13]][14] + +[![GoCenter Kudos][15]][16] [1]: https://travis-ci.org/imdario/mergo.png [2]: https://travis-ci.org/imdario/mergo [3]: https://godoc.org/github.com/imdario/mergo?status.svg [4]: https://godoc.org/github.com/imdario/mergo -[5]: https://goreportcard.com/badge/imdario/mergo -[6]: https://goreportcard.com/report/github.com/imdario/mergo -[7]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master -[8]: https://coveralls.io/github/imdario/mergo?branch=master -[9]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg -[10]: https://sourcegraph.com/github.com/imdario/mergo?badge +[5]: https://img.shields.io/github/release/imdario/mergo.svg +[6]: https://github.com/imdario/mergo/releases +[7]: https://goreportcard.com/badge/imdario/mergo +[8]: https://goreportcard.com/report/github.com/imdario/mergo +[9]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master +[10]: https://coveralls.io/github/imdario/mergo?branch=master +[11]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg +[12]: https://sourcegraph.com/github.com/imdario/mergo?badge +[13]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=shield +[14]: https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield +[15]: https://search.gocenter.io/api/ui/badge/github.com%2Fimdario%2Fmergo +[16]: https://search.gocenter.io/github.com/imdario/mergo + +A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. + +Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). -### Latest release +Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche. + +## Status -[Release v0.3.6](https://github.com/imdario/mergo/releases/tag/v0.3.6). +It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc](https://github.com/imdario/mergo#mergo-in-the-wild). ### Important note -Please keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2) Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). An optional/variadic argument has been added, so it won't break existing code. +Please keep in mind that a problematic PR broke [0.3.9](//github.com/imdario/mergo/releases/tag/0.3.9). I reverted it in [0.3.10](//github.com/imdario/mergo/releases/tag/0.3.10), and I consider it stable but not bug-free. Also, this version adds suppot for go modules. + +Keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2), Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). I added an optional/variadic argument so that it won't break the existing code. -If you were using Mergo **before** April 6th 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause (I hope it won't!) in existing projects after the change (release 0.2.0). +If you were using Mergo before April 6th, 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0). ### Donations -If Mergo is useful to you, consider buying me a coffee, a beer or making a monthly donation so I can keep building great free software. :heart_eyes: +If Mergo is useful to you, consider buying me a coffee, a beer, or making a monthly donation to allow me to keep building great free software. :heart_eyes: Buy Me a Coffee at ko-fi.com [![Beerpay](https://beerpay.io/imdario/mergo/badge.svg)](https://beerpay.io/imdario/mergo) @@ -86,8 +97,9 @@ If Mergo is useful to you, consider buying me a coffee, a beer or making a month - [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server) - [jnuthong/item_search](https://github.com/jnuthong/item_search) - [bukalapak/snowboard](https://github.com/bukalapak/snowboard) +- [containerssh/containerssh](https://github.com/containerssh/containerssh) -## Installation +## Install go get github.com/imdario/mergo @@ -98,7 +110,7 @@ If Mergo is useful to you, consider buying me a coffee, a beer or making a month ## Usage -You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are not considered zero values](https://golang.org/ref/spec#The_zero_value) either. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). +You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are zero values](https://golang.org/ref/spec#The_zero_value) too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). ```go if err := mergo.Merge(&dst, src); err != nil { @@ -124,9 +136,7 @@ if err := mergo.Map(&dst, srcMap); err != nil { Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as `map[string]interface{}`. They will be just assigned as values. -More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo). - -### Nice example +Here is a nice example: ```go package main @@ -174,10 +184,10 @@ import ( "time" ) -type timeTransfomer struct { +type timeTransformer struct { } -func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { +func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { if typ == reflect.TypeOf(time.Time{}) { return func(dst, src reflect.Value) error { if dst.CanSet() { @@ -201,7 +211,7 @@ type Snapshot struct { func main() { src := Snapshot{time.Now()} dest := Snapshot{} - mergo.Merge(&dest, src, mergo.WithTransformers(timeTransfomer{})) + mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{})) fmt.Println(dest) // Will print // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 } @@ -217,6 +227,21 @@ If I can help you, you have an idea or you are using Mergo in your projects, don Written by [Dario Castañé](http://dario.im). +## Top Contributors + +[![0](https://sourcerer.io/fame/imdario/imdario/mergo/images/0)](https://sourcerer.io/fame/imdario/imdario/mergo/links/0) +[![1](https://sourcerer.io/fame/imdario/imdario/mergo/images/1)](https://sourcerer.io/fame/imdario/imdario/mergo/links/1) +[![2](https://sourcerer.io/fame/imdario/imdario/mergo/images/2)](https://sourcerer.io/fame/imdario/imdario/mergo/links/2) +[![3](https://sourcerer.io/fame/imdario/imdario/mergo/images/3)](https://sourcerer.io/fame/imdario/imdario/mergo/links/3) +[![4](https://sourcerer.io/fame/imdario/imdario/mergo/images/4)](https://sourcerer.io/fame/imdario/imdario/mergo/links/4) +[![5](https://sourcerer.io/fame/imdario/imdario/mergo/images/5)](https://sourcerer.io/fame/imdario/imdario/mergo/links/5) +[![6](https://sourcerer.io/fame/imdario/imdario/mergo/images/6)](https://sourcerer.io/fame/imdario/imdario/mergo/links/6) +[![7](https://sourcerer.io/fame/imdario/imdario/mergo/images/7)](https://sourcerer.io/fame/imdario/imdario/mergo/links/7) + + ## License [BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE). + + +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_large) diff --git a/vendor/github.com/imdario/mergo/doc.go b/vendor/github.com/imdario/mergo/doc.go index 6e9aa7baf3540..fcd985f995dc2 100644 --- a/vendor/github.com/imdario/mergo/doc.go +++ b/vendor/github.com/imdario/mergo/doc.go @@ -4,41 +4,140 @@ // license that can be found in the LICENSE file. /* -Package mergo merges same-type structs and maps by setting default values in zero-value fields. +A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. -Mergo won't merge unexported (private) fields but will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). +Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). + +Status + +It is ready for production use. It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc. + +Important note + +Please keep in mind that a problematic PR broke 0.3.9. We reverted it in 0.3.10. We consider 0.3.10 as stable but not bug-free. . Also, this version adds suppot for go modules. + +Keep in mind that in 0.3.2, Mergo changed Merge() and Map() signatures to support transformers. We added an optional/variadic argument so that it won't break the existing code. + +If you were using Mergo before April 6th, 2015, please check your project works as intended after updating your local copy with go get -u github.com/imdario/mergo. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0). + +Install + +Do your usual installation procedure: + + go get github.com/imdario/mergo + + // use in your .go code + import ( + "github.com/imdario/mergo" + ) Usage -From my own work-in-progress project: +You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as they are zero values too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). + + if err := mergo.Merge(&dst, src); err != nil { + // ... + } + +Also, you can merge overwriting values using the transformer WithOverride. + + if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { + // ... + } + +Additionally, you can map a map[string]interface{} to a struct (and otherwise, from struct to map), following the same restrictions as in Merge(). Keys are capitalized to find each corresponding exported field. + + if err := mergo.Map(&dst, srcMap); err != nil { + // ... + } + +Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as map[string]interface{}. They will be just assigned as values. + +Here is a nice example: + + package main + + import ( + "fmt" + "github.com/imdario/mergo" + ) - type networkConfig struct { - Protocol string - Address string - ServerType string `json: "server_type"` - Port uint16 + type Foo struct { + A string + B int64 } - type FssnConfig struct { - Network networkConfig + func main() { + src := Foo{ + A: "one", + B: 2, + } + dest := Foo{ + A: "two", + } + mergo.Merge(&dest, src) + fmt.Println(dest) + // Will print + // {two 2} } - var fssnDefault = FssnConfig { - networkConfig { - "tcp", - "127.0.0.1", - "http", - 31560, - }, +Transformers + +Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, time.Time is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero time.Time? + + package main + + import ( + "fmt" + "github.com/imdario/mergo" + "reflect" + "time" + ) + + type timeTransformer struct { } - // Inside a function [...] + func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { + if typ == reflect.TypeOf(time.Time{}) { + return func(dst, src reflect.Value) error { + if dst.CanSet() { + isZero := dst.MethodByName("IsZero") + result := isZero.Call([]reflect.Value{}) + if result[0].Bool() { + dst.Set(src) + } + } + return nil + } + } + return nil + } + + type Snapshot struct { + Time time.Time + // ... + } - if err := mergo.Merge(&config, fssnDefault); err != nil { - log.Fatal(err) + func main() { + src := Snapshot{time.Now()} + dest := Snapshot{} + mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{})) + fmt.Println(dest) + // Will print + // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 } } - // More code [...] +Contact me + +If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): https://twitter.com/im_dario + +About + +Written by Dario Castañé: https://da.rio.hn + +License + +BSD 3-Clause license, as Go language. */ package mergo diff --git a/vendor/github.com/imdario/mergo/go.mod b/vendor/github.com/imdario/mergo/go.mod new file mode 100644 index 0000000000000..3d689d93eb3b0 --- /dev/null +++ b/vendor/github.com/imdario/mergo/go.mod @@ -0,0 +1,5 @@ +module github.com/imdario/mergo + +go 1.13 + +require gopkg.in/yaml.v2 v2.3.0 diff --git a/vendor/github.com/imdario/mergo/map.go b/vendor/github.com/imdario/mergo/map.go index 6ea38e636b64f..a13a7ee46c777 100644 --- a/vendor/github.com/imdario/mergo/map.go +++ b/vendor/github.com/imdario/mergo/map.go @@ -72,6 +72,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, conf case reflect.Struct: srcMap := src.Interface().(map[string]interface{}) for key := range srcMap { + config.overwriteWithEmptyValue = true srcValue := srcMap[key] fieldName := changeInitialCase(key, unicode.ToUpper) dstElement := dst.FieldByName(fieldName) @@ -140,6 +141,9 @@ func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { } func _map(dst, src interface{}, opts ...func(*Config)) error { + if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr { + return ErrNonPointerAgument + } var ( vDst, vSrc reflect.Value err error diff --git a/vendor/github.com/imdario/mergo/merge.go b/vendor/github.com/imdario/mergo/merge.go index 44f70a89d919e..8c2a8fcd9019e 100644 --- a/vendor/github.com/imdario/mergo/merge.go +++ b/vendor/github.com/imdario/mergo/merge.go @@ -13,22 +13,39 @@ import ( "reflect" ) -func hasExportedField(dst reflect.Value) (exported bool) { +func hasMergeableFields(dst reflect.Value) (exported bool) { for i, n := 0, dst.NumField(); i < n; i++ { field := dst.Type().Field(i) if field.Anonymous && dst.Field(i).Kind() == reflect.Struct { - exported = exported || hasExportedField(dst.Field(i)) - } else { + exported = exported || hasMergeableFields(dst.Field(i)) + } else if isExportedComponent(&field) { exported = exported || len(field.PkgPath) == 0 } } return } +func isExportedComponent(field *reflect.StructField) bool { + pkgPath := field.PkgPath + if len(pkgPath) > 0 { + return false + } + c := field.Name[0] + if 'a' <= c && c <= 'z' || c == '_' { + return false + } + return true +} + type Config struct { - Overwrite bool - AppendSlice bool - Transformers Transformers + Overwrite bool + AppendSlice bool + TypeCheck bool + Transformers Transformers + overwriteWithEmptyValue bool + overwriteSliceWithEmptyValue bool + sliceDeepCopy bool + debug bool } type Transformers interface { @@ -40,6 +57,10 @@ type Transformers interface { // short circuiting on recursive types. func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { overwrite := config.Overwrite + typeCheck := config.TypeCheck + overwriteWithEmptySrc := config.overwriteWithEmptyValue + overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue + sliceDeepCopy := config.sliceDeepCopy if !src.IsValid() { return @@ -67,21 +88,34 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co switch dst.Kind() { case reflect.Struct: - if hasExportedField(dst) { + if hasMergeableFields(dst) { for i, n := 0, dst.NumField(); i < n; i++ { if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil { return } } } else { - if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) { + if dst.CanSet() && (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) { dst.Set(src) } } case reflect.Map: if dst.IsNil() && !src.IsNil() { - dst.Set(reflect.MakeMap(dst.Type())) + if dst.CanSet() { + dst.Set(reflect.MakeMap(dst.Type())) + } else { + dst = src + return + } } + + if src.Kind() != reflect.Map { + if overwrite { + dst.Set(src) + } + return + } + for _, key := range src.MapKeys() { srcElement := src.MapIndex(key) if !srcElement.IsValid() { @@ -91,6 +125,9 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co switch srcElement.Kind() { case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice: if srcElement.IsNil() { + if overwrite { + dst.SetMapIndex(key, srcElement) + } continue } fallthrough @@ -125,22 +162,43 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co dstSlice = reflect.ValueOf(dstElement.Interface()) } - if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy { + if typeCheck && srcSlice.Type() != dstSlice.Type() { + return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) + } dstSlice = srcSlice } else if config.AppendSlice { if srcSlice.Type() != dstSlice.Type() { - return fmt.Errorf("cannot append two slice with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) + return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) } dstSlice = reflect.AppendSlice(dstSlice, srcSlice) + } else if sliceDeepCopy { + i := 0 + for ; i < srcSlice.Len() && i < dstSlice.Len(); i++ { + srcElement := srcSlice.Index(i) + dstElement := dstSlice.Index(i) + + if srcElement.CanInterface() { + srcElement = reflect.ValueOf(srcElement.Interface()) + } + if dstElement.CanInterface() { + dstElement = reflect.ValueOf(dstElement.Interface()) + } + + if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { + return + } + } + } dst.SetMapIndex(key, dstSlice) } } - if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map { + if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) { continue } - if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dstElement))) { + if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) { if dst.IsNil() { dst.Set(reflect.MakeMap(dst.Type())) } @@ -151,22 +209,41 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co if !dst.CanSet() { break } - if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice { + if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy { dst.Set(src) } else if config.AppendSlice { if src.Type() != dst.Type() { return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type()) } dst.Set(reflect.AppendSlice(dst, src)) + } else if sliceDeepCopy { + for i := 0; i < src.Len() && i < dst.Len(); i++ { + srcElement := src.Index(i) + dstElement := dst.Index(i) + if srcElement.CanInterface() { + srcElement = reflect.ValueOf(srcElement.Interface()) + } + if dstElement.CanInterface() { + dstElement = reflect.ValueOf(dstElement.Interface()) + } + + if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { + return + } + } } case reflect.Ptr: fallthrough case reflect.Interface: - if src.IsNil() { + if isReflectNil(src) { + if overwriteWithEmptySrc && dst.CanSet() && src.Type().AssignableTo(dst.Type()) { + dst.Set(src) + } break } + if src.Kind() != reflect.Interface { - if dst.IsNil() || overwrite { + if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) { if dst.CanSet() && (overwrite || isEmptyValue(dst)) { dst.Set(src) } @@ -183,18 +260,31 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co } break } + if dst.IsNil() || overwrite { if dst.CanSet() && (overwrite || isEmptyValue(dst)) { dst.Set(src) } - } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { - return + break + } + + if dst.Elem().Kind() == src.Elem().Kind() { + if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { + return + } + break } default: - if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) { - dst.Set(src) + mustSet := (isEmptyValue(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) + if mustSet { + if dst.CanSet() { + dst.Set(src) + } else { + dst = src + } } } + return } @@ -206,7 +296,7 @@ func Merge(dst, src interface{}, opts ...func(*Config)) error { return merge(dst, src, opts...) } -// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by +// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by // non-empty src attribute values. // Deprecated: use Merge(…) with WithOverride func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { @@ -225,12 +315,37 @@ func WithOverride(config *Config) { config.Overwrite = true } -// WithAppendSlice will make merge append slices instead of overwriting it +// WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values. +func WithOverwriteWithEmptyValue(config *Config) { + config.Overwrite = true + config.overwriteWithEmptyValue = true +} + +// WithOverrideEmptySlice will make merge override empty dst slice with empty src slice. +func WithOverrideEmptySlice(config *Config) { + config.overwriteSliceWithEmptyValue = true +} + +// WithAppendSlice will make merge append slices instead of overwriting it. func WithAppendSlice(config *Config) { config.AppendSlice = true } +// WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride). +func WithTypeCheck(config *Config) { + config.TypeCheck = true +} + +// WithSliceDeepCopy will merge slice element one by one with Overwrite flag. +func WithSliceDeepCopy(config *Config) { + config.sliceDeepCopy = true + config.Overwrite = true +} + func merge(dst, src interface{}, opts ...func(*Config)) error { + if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr { + return ErrNonPointerAgument + } var ( vDst, vSrc reflect.Value err error @@ -250,3 +365,16 @@ func merge(dst, src interface{}, opts ...func(*Config)) error { } return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) } + +// IsReflectNil is the reflect value provided nil +func isReflectNil(v reflect.Value) bool { + k := v.Kind() + switch k { + case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr: + // Both interface and slice are nil if first word is 0. + // Both are always bigger than a word; assume flagIndir. + return v.IsNil() + default: + return false + } +} diff --git a/vendor/github.com/imdario/mergo/mergo.go b/vendor/github.com/imdario/mergo/mergo.go index a82fea2fdccc3..3cc926c7f6245 100644 --- a/vendor/github.com/imdario/mergo/mergo.go +++ b/vendor/github.com/imdario/mergo/mergo.go @@ -20,6 +20,7 @@ var ( ErrNotSupported = errors.New("only structs and maps are supported") ErrExpectedMapAsDestination = errors.New("dst was expected to be a map") ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct") + ErrNonPointerAgument = errors.New("dst must be a pointer") ) // During deepMerge, must keep track of checks that are @@ -75,23 +76,3 @@ func resolveValues(dst, src interface{}) (vDst, vSrc reflect.Value, err error) { } return } - -// Traverses recursively both values, assigning src's fields values to dst. -// The map argument tracks comparisons that have already been seen, which allows -// short circuiting on recursive types. -func deeper(dst, src reflect.Value, visited map[uintptr]*visit, depth int) (err error) { - if dst.CanAddr() { - addr := dst.UnsafeAddr() - h := 17 * addr - seen := visited[h] - typ := dst.Type() - for p := seen; p != nil; p = p.next { - if p.ptr == addr && p.typ == typ { - return nil - } - } - // Remember, remember... - visited[h] = &visit{addr, typ, seen} - } - return // TODO refactor -} diff --git a/vendor/github.com/ishidawataru/sctp/GO_LICENSE b/vendor/github.com/ishidawataru/sctp/GO_LICENSE new file mode 100644 index 0000000000000..6a66aea5eafe0 --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/GO_LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/ishidawataru/sctp/NOTICE b/vendor/github.com/ishidawataru/sctp/NOTICE new file mode 100644 index 0000000000000..cfb675fd4ba42 --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/NOTICE @@ -0,0 +1,3 @@ +This source code includes following third party code + +- ipsock_linux.go : licensed by the Go authors, see GO_LICENSE file for the license which applies to the code diff --git a/vendor/github.com/ishidawataru/sctp/go.mod b/vendor/github.com/ishidawataru/sctp/go.mod new file mode 100644 index 0000000000000..5adf982b086ca --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/go.mod @@ -0,0 +1,3 @@ +module github.com/ishidawataru/sctp + +go 1.12 diff --git a/vendor/github.com/ishidawataru/sctp/ipsock_linux.go b/vendor/github.com/ishidawataru/sctp/ipsock_linux.go new file mode 100644 index 0000000000000..3df30fa4601a0 --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/ipsock_linux.go @@ -0,0 +1,222 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the GO_LICENSE file. + +package sctp + +import ( + "net" + "os" + "sync" + "syscall" +) + +//from https://github.com/golang/go +// Boolean to int. +func boolint(b bool) int { + if b { + return 1 + } + return 0 +} + +//from https://github.com/golang/go +func ipToSockaddr(family int, ip net.IP, port int, zone string) (syscall.Sockaddr, error) { + switch family { + case syscall.AF_INET: + if len(ip) == 0 { + ip = net.IPv4zero + } + ip4 := ip.To4() + if ip4 == nil { + return nil, &net.AddrError{Err: "non-IPv4 address", Addr: ip.String()} + } + sa := &syscall.SockaddrInet4{Port: port} + copy(sa.Addr[:], ip4) + return sa, nil + case syscall.AF_INET6: + // In general, an IP wildcard address, which is either + // "0.0.0.0" or "::", means the entire IP addressing + // space. For some historical reason, it is used to + // specify "any available address" on some operations + // of IP node. + // + // When the IP node supports IPv4-mapped IPv6 address, + // we allow an listener to listen to the wildcard + // address of both IP addressing spaces by specifying + // IPv6 wildcard address. + if len(ip) == 0 || ip.Equal(net.IPv4zero) { + ip = net.IPv6zero + } + // We accept any IPv6 address including IPv4-mapped + // IPv6 address. + ip6 := ip.To16() + if ip6 == nil { + return nil, &net.AddrError{Err: "non-IPv6 address", Addr: ip.String()} + } + //we set ZoneId to 0, as currently we use this functon only to probe the IP capabilities of the host + //if real Zone handling is required, the zone cache implementation in golang/net should be pulled here + sa := &syscall.SockaddrInet6{Port: port, ZoneId: 0} + copy(sa.Addr[:], ip6) + return sa, nil + } + return nil, &net.AddrError{Err: "invalid address family", Addr: ip.String()} +} + +//from https://github.com/golang/go +func sockaddr(a *net.TCPAddr, family int) (syscall.Sockaddr, error) { + if a == nil { + return nil, nil + } + return ipToSockaddr(family, a.IP, a.Port, a.Zone) +} + +//from https://github.com/golang/go +type ipStackCapabilities struct { + sync.Once // guards following + ipv4Enabled bool + ipv6Enabled bool + ipv4MappedIPv6Enabled bool +} + +//from https://github.com/golang/go +var ipStackCaps ipStackCapabilities + +//from https://github.com/golang/go +// supportsIPv4 reports whether the platform supports IPv4 networking +// functionality. +func supportsIPv4() bool { + ipStackCaps.Once.Do(ipStackCaps.probe) + return ipStackCaps.ipv4Enabled +} + +//from https://github.com/golang/go +// supportsIPv6 reports whether the platform supports IPv6 networking +// functionality. +func supportsIPv6() bool { + ipStackCaps.Once.Do(ipStackCaps.probe) + return ipStackCaps.ipv6Enabled +} + +//from https://github.com/golang/go +// supportsIPv4map reports whether the platform supports mapping an +// IPv4 address inside an IPv6 address at transport layer +// protocols. See RFC 4291, RFC 4038 and RFC 3493. +func supportsIPv4map() bool { + ipStackCaps.Once.Do(ipStackCaps.probe) + return ipStackCaps.ipv4MappedIPv6Enabled +} + +//from https://github.com/golang/go +// Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication +// capabilities which are controlled by the IPV6_V6ONLY socket option +// and kernel configuration. +// +// Should we try to use the IPv4 socket interface if we're only +// dealing with IPv4 sockets? As long as the host system understands +// IPv4-mapped IPv6, it's okay to pass IPv4-mapeed IPv6 addresses to +// the IPv6 interface. That simplifies our code and is most +// general. Unfortunately, we need to run on kernels built without +// IPv6 support too. So probe the kernel to figure it out. +func (p *ipStackCapabilities) probe() { + s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + switch err { + case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT: + case nil: + syscall.Close(s) + p.ipv4Enabled = true + } + var probes = []struct { + laddr net.TCPAddr + value int + }{ + // IPv6 communication capability + {laddr: net.TCPAddr{IP: net.IPv6loopback}, value: 1}, + // IPv4-mapped IPv6 address communication capability + {laddr: net.TCPAddr{IP: net.IPv4(127, 0, 0, 1)}, value: 0}, + } + + for i := range probes { + s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + if err != nil { + continue + } + defer syscall.Close(s) + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value) + sa, err := sockaddr(&(probes[i].laddr), syscall.AF_INET6) + if err != nil { + continue + } + if err := syscall.Bind(s, sa); err != nil { + continue + } + if i == 0 { + p.ipv6Enabled = true + } else { + p.ipv4MappedIPv6Enabled = true + } + } +} + +//from https://github.com/golang/go +//Change: we check the first IP address in the list of candidate SCTP IP addresses +func (a *SCTPAddr) isWildcard() bool { + if a == nil { + return true + } + if 0 == len(a.IPAddrs) { + return true + } + + return a.IPAddrs[0].IP.IsUnspecified() +} + +func (a *SCTPAddr) family() int { + if a != nil { + for _, ip := range a.IPAddrs { + if ip.IP.To4() == nil { + return syscall.AF_INET6 + } + } + } + return syscall.AF_INET +} + +//from https://github.com/golang/go +func favoriteAddrFamily(network string, laddr *SCTPAddr, raddr *SCTPAddr, mode string) (family int, ipv6only bool) { + switch network[len(network)-1] { + case '4': + return syscall.AF_INET, false + case '6': + return syscall.AF_INET6, true + } + + if mode == "listen" && (laddr == nil || laddr.isWildcard()) { + if supportsIPv4map() || !supportsIPv4() { + return syscall.AF_INET6, false + } + if laddr == nil { + return syscall.AF_INET, false + } + return laddr.family(), false + } + + if (laddr == nil || laddr.family() == syscall.AF_INET) && + (raddr == nil || raddr.family() == syscall.AF_INET) { + return syscall.AF_INET, false + } + return syscall.AF_INET6, false +} + +//from https://github.com/golang/go +//Changes: it is for SCTP only +func setDefaultSockopts(s int, family int, ipv6only bool) error { + if family == syscall.AF_INET6 { + // Allow both IP versions even if the OS default + // is otherwise. Note that some operating systems + // never admit this option. + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only)) + } + // Allow broadcast. + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)) +} diff --git a/vendor/github.com/ishidawataru/sctp/sctp.go b/vendor/github.com/ishidawataru/sctp/sctp.go index cac1a889ca305..94842f42702fa 100644 --- a/vendor/github.com/ishidawataru/sctp/sctp.go +++ b/vendor/github.com/ishidawataru/sctp/sctp.go @@ -1,3 +1,18 @@ +// Copyright 2019 Wataru Ishida. All rights reserved. +// +// 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. + package sctp import ( @@ -197,37 +212,58 @@ func htons(h uint16) uint16 { var ntohs = htons -func setNumOstreams(fd, num int) error { - param := InitMsg{ - NumOstreams: uint16(num), - } - optlen := unsafe.Sizeof(param) - _, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(¶m)), uintptr(optlen)) +// setInitOpts sets options for an SCTP association initialization +// see https://tools.ietf.org/html/rfc4960#page-25 +func setInitOpts(fd int, options InitMsg) error { + optlen := unsafe.Sizeof(options) + _, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(&options)), uintptr(optlen)) return err } +func setNumOstreams(fd, num int) error { + return setInitOpts(fd, InitMsg{NumOstreams: uint16(num)}) +} + type SCTPAddr struct { - IP []net.IP - Port int + IPAddrs []net.IPAddr + Port int } func (a *SCTPAddr) ToRawSockAddrBuf() []byte { - buf := []byte{} p := htons(uint16(a.Port)) - for _, ip := range a.IP { - if ip.To4() != nil { + if len(a.IPAddrs) == 0 { // if a.IPAddrs list is empty - fall back to IPv4 zero addr + s := syscall.RawSockaddrInet4{ + Family: syscall.AF_INET, + Port: p, + } + copy(s.Addr[:], net.IPv4zero) + return toBuf(s) + } + buf := []byte{} + for _, ip := range a.IPAddrs { + ipBytes := ip.IP + if len(ipBytes) == 0 { + ipBytes = net.IPv4zero + } + if ip4 := ipBytes.To4(); ip4 != nil { s := syscall.RawSockaddrInet4{ Family: syscall.AF_INET, Port: p, } - copy(s.Addr[:], ip.To4()) + copy(s.Addr[:], ip4) buf = append(buf, toBuf(s)...) } else { + var scopeid uint32 + ifi, err := net.InterfaceByName(ip.Zone) + if err == nil { + scopeid = uint32(ifi.Index) + } s := syscall.RawSockaddrInet6{ - Family: syscall.AF_INET6, - Port: p, + Family: syscall.AF_INET6, + Port: p, + Scope_id: scopeid, } - copy(s.Addr[:], ip) + copy(s.Addr[:], ipBytes) buf = append(buf, toBuf(s)...) } } @@ -237,15 +273,15 @@ func (a *SCTPAddr) ToRawSockAddrBuf() []byte { func (a *SCTPAddr) String() string { var b bytes.Buffer - for n, i := range a.IP { - if a.IP[n].To4() != nil { + for n, i := range a.IPAddrs { + if i.IP.To4() != nil { b.WriteString(i.String()) - } else if a.IP[n].To16() != nil { + } else if i.IP.To16() != nil { b.WriteRune('[') b.WriteString(i.String()) b.WriteRune(']') } - if n < len(a.IP)-1 { + if n < len(a.IPAddrs)-1 { b.WriteRune('/') } } @@ -260,6 +296,7 @@ func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) { tcpnet := "" switch network { case "", "sctp": + tcpnet = "tcp" case "sctp4": tcpnet = "tcp4" case "sctp6": @@ -271,26 +308,26 @@ func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) { if len(elems) == 0 { return nil, fmt.Errorf("invalid input: %s", addrs) } - ipaddrs := make([]net.IP, 0, len(elems)) + ipaddrs := make([]net.IPAddr, 0, len(elems)) for _, e := range elems[:len(elems)-1] { tcpa, err := net.ResolveTCPAddr(tcpnet, e+":") if err != nil { return nil, err } - ipaddrs = append(ipaddrs, tcpa.IP) + ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone}) } tcpa, err := net.ResolveTCPAddr(tcpnet, elems[len(elems)-1]) if err != nil { return nil, err } if tcpa.IP != nil { - ipaddrs = append(ipaddrs, tcpa.IP) + ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone}) } else { ipaddrs = nil } return &SCTPAddr{ - IP: ipaddrs, - Port: tcpa.Port, + IPAddrs: ipaddrs, + Port: tcpa.Port, }, nil } @@ -357,15 +394,12 @@ func (c *SCTPConn) Read(b []byte) (int, error) { } func (c *SCTPConn) SetInitMsg(numOstreams, maxInstreams, maxAttempts, maxInitTimeout int) error { - param := InitMsg{ + return setInitOpts(c.fd(), InitMsg{ NumOstreams: uint16(numOstreams), MaxInstreams: uint16(maxInstreams), MaxAttempts: uint16(maxAttempts), MaxInitTimeout: uint16(maxInitTimeout), - } - optlen := unsafe.Sizeof(param) - _, _, err := setsockopt(c.fd(), SCTP_INITMSG, uintptr(unsafe.Pointer(¶m)), uintptr(optlen)) - return err + }) } func (c *SCTPConn) SubscribeEvents(flags int) error { @@ -473,7 +507,7 @@ func (c *SCTPConn) GetDefaultSentParam() (*SndRcvInfo, error) { func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) { addr := &SCTPAddr{ - IP: make([]net.IP, n), + IPAddrs: make([]net.IPAddr, n), } switch family := (*(*syscall.RawSockaddrAny)(ptr)).Addr.Family; family { @@ -484,7 +518,7 @@ func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) { for i := 0; i < n; i++ { a := *(*syscall.RawSockaddrInet4)(unsafe.Pointer( uintptr(ptr) + size*uintptr(i))) - addr.IP[i] = a.Addr[:] + addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:]} } case syscall.AF_INET6: addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port))) @@ -493,7 +527,12 @@ func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) { for i := 0; i < n; i++ { a := *(*syscall.RawSockaddrInet6)(unsafe.Pointer( uintptr(ptr) + size*uintptr(i))) - addr.IP[i] = a.Addr[:] + var zone string + ifi, err := net.InterfaceByIndex(int(a.Scope_id)) + if err == nil { + zone = ifi.Name + } + addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:], Zone: zone} } default: return nil, fmt.Errorf("unknown address family: %d", family) @@ -654,3 +693,37 @@ func (c *SCTPSndRcvInfoWrappedConn) SetReadDeadline(t time.Time) error { func (c *SCTPSndRcvInfoWrappedConn) SetWriteDeadline(t time.Time) error { return c.conn.SetWriteDeadline(t) } + +func (c *SCTPSndRcvInfoWrappedConn) SetWriteBuffer(bytes int) error { + return c.conn.SetWriteBuffer(bytes) +} + +func (c *SCTPSndRcvInfoWrappedConn) GetWriteBuffer() (int, error) { + return c.conn.GetWriteBuffer() +} + +func (c *SCTPSndRcvInfoWrappedConn) SetReadBuffer(bytes int) error { + return c.conn.SetReadBuffer(bytes) +} + +func (c *SCTPSndRcvInfoWrappedConn) GetReadBuffer() (int, error) { + return c.conn.GetReadBuffer() +} + +// SocketConfig contains options for the SCTP socket. +type SocketConfig struct { + // If Control is not nil it is called after the socket is created but before + // it is bound or connected. + Control func(network, address string, c syscall.RawConn) error + + // InitMsg is the options to send in the initial SCTP message + InitMsg InitMsg +} + +func (cfg *SocketConfig) Listen(net string, laddr *SCTPAddr) (*SCTPListener, error) { + return listenSCTPExtConfig(net, laddr, cfg.InitMsg, cfg.Control) +} + +func (cfg *SocketConfig) Dial(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) { + return dialSCTPExtConfig(net, laddr, raddr, cfg.InitMsg, cfg.Control) +} diff --git a/vendor/github.com/ishidawataru/sctp/sctp_linux.go b/vendor/github.com/ishidawataru/sctp/sctp_linux.go index f93ab8622ac90..d96d09e5ca925 100644 --- a/vendor/github.com/ishidawataru/sctp/sctp_linux.go +++ b/vendor/github.com/ishidawataru/sctp/sctp_linux.go @@ -1,9 +1,22 @@ // +build linux,!386 +// Copyright 2019 Wataru Ishida. All rights reserved. +// +// 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. package sctp import ( - "fmt" "io" "net" "sync/atomic" @@ -41,6 +54,23 @@ func getsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, erro return r0, r1, nil } +type rawConn struct { + sockfd int +} + +func (r rawConn) Control(f func(fd uintptr)) error { + f(uintptr(r.sockfd)) + return nil +} + +func (r rawConn) Read(f func(fd uintptr) (done bool)) error { + panic("not implemented") +} + +func (r rawConn) Write(f func(fd uintptr) (done bool)) error { + panic("not implemented") +} + func (c *SCTPConn) SCTPWrite(b []byte, info *SndRcvInfo) (int, error) { var cbuf []byte if info != nil { @@ -115,31 +145,35 @@ func (c *SCTPConn) Close() error { return syscall.EBADF } +func (c *SCTPConn) SetWriteBuffer(bytes int) error { + return syscall.SetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes) +} + +func (c *SCTPConn) GetWriteBuffer() (int, error) { + return syscall.GetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_SNDBUF) +} + +func (c *SCTPConn) SetReadBuffer(bytes int) error { + return syscall.SetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes) +} + +func (c *SCTPConn) GetReadBuffer() (int, error) { + return syscall.GetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_RCVBUF) +} + +// ListenSCTP - start listener on specified address/port func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) { - af := syscall.AF_INET - switch net { - case "sctp": - hasv6 := func(addr *SCTPAddr) bool { - if addr == nil { - return false - } - for _, ip := range addr.IP { - if ip.To4() == nil { - return true - } - } - return false - } - if hasv6(laddr) { - af = syscall.AF_INET6 - } - case "sctp4": - case "sctp6": - af = syscall.AF_INET6 - default: - return nil, fmt.Errorf("invalid net: %s", net) - } + return ListenSCTPExt(net, laddr, InitMsg{NumOstreams: SCTP_MAX_STREAM}) +} +// ListenSCTPExt - start listener on specified address/port with given SCTP options +func ListenSCTPExt(network string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) { + return listenSCTPExtConfig(network, laddr, options, nil) +} + +// listenSCTPExtConfig - start listener on specified address/port with given SCTP options and socket configuration +func listenSCTPExtConfig(network string, laddr *SCTPAddr, options InitMsg, control func(network, address string, c syscall.RawConn) error) (*SCTPListener, error) { + af, ipv6only := favoriteAddrFamily(network, laddr, nil, "listen") sock, err := syscall.Socket( af, syscall.SOCK_STREAM, @@ -148,12 +182,37 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) { if err != nil { return nil, err } - err = setNumOstreams(sock, SCTP_MAX_STREAM) + + // close socket on error + defer func() { + if err != nil { + syscall.Close(sock) + } + }() + if err = setDefaultSockopts(sock, af, ipv6only); err != nil { + return nil, err + } + if control != nil { + rc := rawConn{sockfd: sock} + if err = control(network, laddr.String(), rc); err != nil { + return nil, err + } + } + err = setInitOpts(sock, options) if err != nil { return nil, err } - if laddr != nil && len(laddr.IP) != 0 { - err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR) + + if laddr != nil { + // If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address + if len(laddr.IPAddrs) == 0 { + if af == syscall.AF_INET { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero}) + } else if af == syscall.AF_INET6 { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero}) + } + } + err = SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR) if err != nil { return nil, err } @@ -167,40 +226,35 @@ func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) { }, nil } -func (ln *SCTPListener) Accept() (net.Conn, error) { +// AcceptSCTP waits for and returns the next SCTP connection to the listener. +func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) { fd, _, err := syscall.Accept4(ln.fd, 0) return NewSCTPConn(fd, nil), err } +// Accept waits for and returns the next connection connection to the listener. +func (ln *SCTPListener) Accept() (net.Conn, error) { + return ln.AcceptSCTP() +} + func (ln *SCTPListener) Close() error { syscall.Shutdown(ln.fd, syscall.SHUT_RDWR) return syscall.Close(ln.fd) } +// DialSCTP - bind socket to laddr (if given) and connect to raddr func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) { - af := syscall.AF_INET - switch net { - case "sctp": - hasv6 := func(addr *SCTPAddr) bool { - if addr == nil { - return false - } - for _, ip := range addr.IP { - if ip.To4() == nil { - return true - } - } - return false - } - if hasv6(laddr) || hasv6(raddr) { - af = syscall.AF_INET6 - } - case "sctp4": - case "sctp6": - af = syscall.AF_INET6 - default: - return nil, fmt.Errorf("invalid net: %s", net) - } + return DialSCTPExt(net, laddr, raddr, InitMsg{NumOstreams: SCTP_MAX_STREAM}) +} + +// DialSCTPExt - same as DialSCTP but with given SCTP options +func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) { + return dialSCTPExtConfig(network, laddr, raddr, options, nil) +} + +// dialSCTPExtConfig - same as DialSCTP but with given SCTP options and socket configuration +func dialSCTPExtConfig(network string, laddr, raddr *SCTPAddr, options InitMsg, control func(network, address string, c syscall.RawConn) error) (*SCTPConn, error) { + af, ipv6only := favoriteAddrFamily(network, laddr, raddr, "dial") sock, err := syscall.Socket( af, syscall.SOCK_STREAM, @@ -209,11 +263,35 @@ func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) { if err != nil { return nil, err } - err = setNumOstreams(sock, SCTP_MAX_STREAM) + + // close socket on error + defer func() { + if err != nil { + syscall.Close(sock) + } + }() + if err = setDefaultSockopts(sock, af, ipv6only); err != nil { + return nil, err + } + if control != nil { + rc := rawConn{sockfd: sock} + if err = control(network, laddr.String(), rc); err != nil { + return nil, err + } + } + err = setInitOpts(sock, options) if err != nil { return nil, err } if laddr != nil { + // If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address + if len(laddr.IPAddrs) == 0 { + if af == syscall.AF_INET { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero}) + } else if af == syscall.AF_INET6 { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero}) + } + } err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR) if err != nil { return nil, err diff --git a/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go b/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go index adcbf78b46fb5..118fe159e92d9 100644 --- a/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go +++ b/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go @@ -1,4 +1,18 @@ // +build !linux linux,386 +// Copyright 2019 Wataru Ishida. All rights reserved. +// +// 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. package sctp @@ -6,6 +20,7 @@ import ( "errors" "net" "runtime" + "syscall" ) var ErrUnsupported = errors.New("SCTP is unsupported on " + runtime.GOOS + "/" + runtime.GOARCH) @@ -30,14 +45,42 @@ func (c *SCTPConn) Close() error { return ErrUnsupported } +func (c *SCTPConn) SetWriteBuffer(bytes int) error { + return ErrUnsupported +} + +func (c *SCTPConn) GetWriteBuffer() (int, error) { + return 0, ErrUnsupported +} + +func (c *SCTPConn) SetReadBuffer(bytes int) error { + return ErrUnsupported +} + +func (c *SCTPConn) GetReadBuffer() (int, error) { + return 0, ErrUnsupported +} + func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) { return nil, ErrUnsupported } +func ListenSCTPExt(net string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) { + return nil, ErrUnsupported +} + +func listenSCTPExtConfig(network string, laddr *SCTPAddr, options InitMsg, control func(network, address string, c syscall.RawConn) error) (*SCTPListener, error) { + return nil, ErrUnsupported +} + func (ln *SCTPListener) Accept() (net.Conn, error) { return nil, ErrUnsupported } +func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) { + return nil, ErrUnsupported +} + func (ln *SCTPListener) Close() error { return ErrUnsupported } @@ -45,3 +88,11 @@ func (ln *SCTPListener) Close() error { func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) { return nil, ErrUnsupported } + +func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) { + return nil, ErrUnsupported +} + +func dialSCTPExtConfig(network string, laddr, raddr *SCTPAddr, options InitMsg, control func(network, address string, c syscall.RawConn) error) (*SCTPConn, error) { + return nil, ErrUnsupported +} diff --git a/vendor/github.com/jmespath/go-jmespath/README.md b/vendor/github.com/jmespath/go-jmespath/README.md index 187ef676dc9c7..110ad799976dc 100644 --- a/vendor/github.com/jmespath/go-jmespath/README.md +++ b/vendor/github.com/jmespath/go-jmespath/README.md @@ -4,4 +4,84 @@ -See http://jmespath.org for more info. +go-jmespath is a GO implementation of JMESPath, +which is a query language for JSON. It will take a JSON +document and transform it into another JSON document +through a JMESPath expression. + +Using go-jmespath is really easy. There's a single function +you use, `jmespath.search`: + + +```go +> import "github.com/jmespath/go-jmespath" +> +> var jsondata = []byte(`{"foo": {"bar": {"baz": [0, 1, 2, 3, 4]}}}`) // your data +> var data interface{} +> err := json.Unmarshal(jsondata, &data) +> result, err := jmespath.Search("foo.bar.baz[2]", data) +result = 2 +``` + +In the example we gave the ``search`` function input data of +`{"foo": {"bar": {"baz": [0, 1, 2, 3, 4]}}}` as well as the JMESPath +expression `foo.bar.baz[2]`, and the `search` function evaluated +the expression against the input data to produce the result ``2``. + +The JMESPath language can do a lot more than select an element +from a list. Here are a few more examples: + +```go +> var jsondata = []byte(`{"foo": {"bar": {"baz": [0, 1, 2, 3, 4]}}}`) // your data +> var data interface{} +> err := json.Unmarshal(jsondata, &data) +> result, err := jmespath.search("foo.bar", data) +result = { "baz": [ 0, 1, 2, 3, 4 ] } + + +> var jsondata = []byte(`{"foo": [{"first": "a", "last": "b"}, + {"first": "c", "last": "d"}]}`) // your data +> var data interface{} +> err := json.Unmarshal(jsondata, &data) +> result, err := jmespath.search({"foo[*].first", data) +result [ 'a', 'c' ] + + +> var jsondata = []byte(`{"foo": [{"age": 20}, {"age": 25}, + {"age": 30}, {"age": 35}, + {"age": 40}]}`) // your data +> var data interface{} +> err := json.Unmarshal(jsondata, &data) +> result, err := jmespath.search("foo[?age > `30`]") +result = [ { age: 35 }, { age: 40 } ] +``` + +You can also pre-compile your query. This is usefull if +you are going to run multiple searches with it: + +```go + > var jsondata = []byte(`{"foo": "bar"}`) + > var data interface{} + > err := json.Unmarshal(jsondata, &data) + > precompiled, err := Compile("foo") + > if err != nil{ + > // ... handle the error + > } + > result, err := precompiled.Search(data) + result = "bar" +``` + +## More Resources + +The example above only show a small amount of what +a JMESPath expression can do. If you want to take a +tour of the language, the *best* place to go is the +[JMESPath Tutorial](http://jmespath.org/tutorial.html). + +One of the best things about JMESPath is that it is +implemented in many different programming languages including +python, ruby, php, lua, etc. To see a complete list of libraries, +check out the [JMESPath libraries page](http://jmespath.org/libraries.html). + +And finally, the full JMESPath specification can be found +on the [JMESPath site](http://jmespath.org/specification.html). diff --git a/vendor/github.com/jmespath/go-jmespath/api.go b/vendor/github.com/jmespath/go-jmespath/api.go index 9cfa988bc5b8a..010efe9bfba36 100644 --- a/vendor/github.com/jmespath/go-jmespath/api.go +++ b/vendor/github.com/jmespath/go-jmespath/api.go @@ -2,7 +2,7 @@ package jmespath import "strconv" -// JmesPath is the epresentation of a compiled JMES path query. A JmesPath is +// JMESPath is the representation of a compiled JMES path query. A JMESPath is // safe for concurrent use by multiple goroutines. type JMESPath struct { ast ASTNode diff --git a/vendor/github.com/jmespath/go-jmespath/go.mod b/vendor/github.com/jmespath/go-jmespath/go.mod new file mode 100644 index 0000000000000..aa1e3f1c9f7ce --- /dev/null +++ b/vendor/github.com/jmespath/go-jmespath/go.mod @@ -0,0 +1,5 @@ +module github.com/jmespath/go-jmespath + +go 1.14 + +require github.com/stretchr/testify v1.5.1 diff --git a/vendor/github.com/jmespath/go-jmespath/parser.go b/vendor/github.com/jmespath/go-jmespath/parser.go index 1240a17552157..4abc303ab4a99 100644 --- a/vendor/github.com/jmespath/go-jmespath/parser.go +++ b/vendor/github.com/jmespath/go-jmespath/parser.go @@ -137,7 +137,7 @@ func (p *Parser) Parse(expression string) (ASTNode, error) { } if p.current() != tEOF { return ASTNode{}, p.syntaxError(fmt.Sprintf( - "Unexpected token at the end of the expresssion: %s", p.current())) + "Unexpected token at the end of the expression: %s", p.current())) } return parsed, nil } diff --git a/vendor/github.com/json-iterator/go/LICENSE b/vendor/github.com/json-iterator/go/LICENSE new file mode 100644 index 0000000000000..2cf4f5ab28e9c --- /dev/null +++ b/vendor/github.com/json-iterator/go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 json-iterator + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/json-iterator/go/README.md b/vendor/github.com/json-iterator/go/README.md new file mode 100644 index 0000000000000..52b111d5f36ef --- /dev/null +++ b/vendor/github.com/json-iterator/go/README.md @@ -0,0 +1,87 @@ +[![Sourcegraph](https://sourcegraph.com/github.com/json-iterator/go/-/badge.svg)](https://sourcegraph.com/github.com/json-iterator/go?badge) +[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/json-iterator/go) +[![Build Status](https://travis-ci.org/json-iterator/go.svg?branch=master)](https://travis-ci.org/json-iterator/go) +[![codecov](https://codecov.io/gh/json-iterator/go/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/go) +[![rcard](https://goreportcard.com/badge/github.com/json-iterator/go)](https://goreportcard.com/report/github.com/json-iterator/go) +[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/json-iterator/go/master/LICENSE) +[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) + +A high-performance 100% compatible drop-in replacement of "encoding/json" + +You can also use thrift like JSON using [thrift-iterator](https://github.com/thrift-iterator/go) + +# Benchmark + +![benchmark](http://jsoniter.com/benchmarks/go-benchmark.png) + +Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/github.com/json-iterator/go-benchmark/benchmark_medium_payload_test.go + +Raw Result (easyjson requires static code generation) + +| | ns/op | allocation bytes | allocation times | +| --------------- | ----------- | ---------------- | ---------------- | +| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op | +| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op | +| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op | +| std encode | 2213 ns/op | 712 B/op | 5 allocs/op | +| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op | +| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op | + +Always benchmark with your own workload. +The result depends heavily on the data input. + +# Usage + +100% compatibility with standard lib + +Replace + +```go +import "encoding/json" +json.Marshal(&data) +``` + +with + +```go +import jsoniter "github.com/json-iterator/go" + +var json = jsoniter.ConfigCompatibleWithStandardLibrary +json.Marshal(&data) +``` + +Replace + +```go +import "encoding/json" +json.Unmarshal(input, &data) +``` + +with + +```go +import jsoniter "github.com/json-iterator/go" + +var json = jsoniter.ConfigCompatibleWithStandardLibrary +json.Unmarshal(input, &data) +``` + +[More documentation](http://jsoniter.com/migrate-from-go-std.html) + +# How to get + +``` +go get github.com/json-iterator/go +``` + +# Contribution Welcomed ! + +Contributors + +- [thockin](https://github.com/thockin) +- [mattn](https://github.com/mattn) +- [cch123](https://github.com/cch123) +- [Oleg Shaldybin](https://github.com/olegshaldybin) +- [Jason Toffaletti](https://github.com/toffaletti) + +Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) diff --git a/vendor/github.com/json-iterator/go/adapter.go b/vendor/github.com/json-iterator/go/adapter.go new file mode 100644 index 0000000000000..92d2cc4a3dd5c --- /dev/null +++ b/vendor/github.com/json-iterator/go/adapter.go @@ -0,0 +1,150 @@ +package jsoniter + +import ( + "bytes" + "io" +) + +// RawMessage to make replace json with jsoniter +type RawMessage []byte + +// Unmarshal adapts to json/encoding Unmarshal API +// +// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v. +// Refer to https://godoc.org/encoding/json#Unmarshal for more information +func Unmarshal(data []byte, v interface{}) error { + return ConfigDefault.Unmarshal(data, v) +} + +// UnmarshalFromString is a convenient method to read from string instead of []byte +func UnmarshalFromString(str string, v interface{}) error { + return ConfigDefault.UnmarshalFromString(str, v) +} + +// Get quick method to get value from deeply nested JSON structure +func Get(data []byte, path ...interface{}) Any { + return ConfigDefault.Get(data, path...) +} + +// Marshal adapts to json/encoding Marshal API +// +// Marshal returns the JSON encoding of v, adapts to json/encoding Marshal API +// Refer to https://godoc.org/encoding/json#Marshal for more information +func Marshal(v interface{}) ([]byte, error) { + return ConfigDefault.Marshal(v) +} + +// MarshalIndent same as json.MarshalIndent. Prefix is not supported. +func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { + return ConfigDefault.MarshalIndent(v, prefix, indent) +} + +// MarshalToString convenient method to write as string instead of []byte +func MarshalToString(v interface{}) (string, error) { + return ConfigDefault.MarshalToString(v) +} + +// NewDecoder adapts to json/stream NewDecoder API. +// +// NewDecoder returns a new decoder that reads from r. +// +// Instead of a json/encoding Decoder, an Decoder is returned +// Refer to https://godoc.org/encoding/json#NewDecoder for more information +func NewDecoder(reader io.Reader) *Decoder { + return ConfigDefault.NewDecoder(reader) +} + +// Decoder reads and decodes JSON values from an input stream. +// Decoder provides identical APIs with json/stream Decoder (Token() and UseNumber() are in progress) +type Decoder struct { + iter *Iterator +} + +// Decode decode JSON into interface{} +func (adapter *Decoder) Decode(obj interface{}) error { + if adapter.iter.head == adapter.iter.tail && adapter.iter.reader != nil { + if !adapter.iter.loadMore() { + return io.EOF + } + } + adapter.iter.ReadVal(obj) + err := adapter.iter.Error + if err == io.EOF { + return nil + } + return adapter.iter.Error +} + +// More is there more? +func (adapter *Decoder) More() bool { + iter := adapter.iter + if iter.Error != nil { + return false + } + c := iter.nextToken() + if c == 0 { + return false + } + iter.unreadByte() + return c != ']' && c != '}' +} + +// Buffered remaining buffer +func (adapter *Decoder) Buffered() io.Reader { + remaining := adapter.iter.buf[adapter.iter.head:adapter.iter.tail] + return bytes.NewReader(remaining) +} + +// UseNumber causes the Decoder to unmarshal a number into an interface{} as a +// Number instead of as a float64. +func (adapter *Decoder) UseNumber() { + cfg := adapter.iter.cfg.configBeforeFrozen + cfg.UseNumber = true + adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions) +} + +// DisallowUnknownFields causes the Decoder to return an error when the destination +// is a struct and the input contains object keys which do not match any +// non-ignored, exported fields in the destination. +func (adapter *Decoder) DisallowUnknownFields() { + cfg := adapter.iter.cfg.configBeforeFrozen + cfg.DisallowUnknownFields = true + adapter.iter.cfg = cfg.frozeWithCacheReuse(adapter.iter.cfg.extraExtensions) +} + +// NewEncoder same as json.NewEncoder +func NewEncoder(writer io.Writer) *Encoder { + return ConfigDefault.NewEncoder(writer) +} + +// Encoder same as json.Encoder +type Encoder struct { + stream *Stream +} + +// Encode encode interface{} as JSON to io.Writer +func (adapter *Encoder) Encode(val interface{}) error { + adapter.stream.WriteVal(val) + adapter.stream.WriteRaw("\n") + adapter.stream.Flush() + return adapter.stream.Error +} + +// SetIndent set the indention. Prefix is not supported +func (adapter *Encoder) SetIndent(prefix, indent string) { + config := adapter.stream.cfg.configBeforeFrozen + config.IndentionStep = len(indent) + adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions) +} + +// SetEscapeHTML escape html by default, set to false to disable +func (adapter *Encoder) SetEscapeHTML(escapeHTML bool) { + config := adapter.stream.cfg.configBeforeFrozen + config.EscapeHTML = escapeHTML + adapter.stream.cfg = config.frozeWithCacheReuse(adapter.stream.cfg.extraExtensions) +} + +// Valid reports whether data is a valid JSON encoding. +func Valid(data []byte) bool { + return ConfigDefault.Valid(data) +} diff --git a/vendor/github.com/json-iterator/go/any.go b/vendor/github.com/json-iterator/go/any.go new file mode 100644 index 0000000000000..f6b8aeab0a12d --- /dev/null +++ b/vendor/github.com/json-iterator/go/any.go @@ -0,0 +1,325 @@ +package jsoniter + +import ( + "errors" + "fmt" + "github.com/modern-go/reflect2" + "io" + "reflect" + "strconv" + "unsafe" +) + +// Any generic object representation. +// The lazy json implementation holds []byte and parse lazily. +type Any interface { + LastError() error + ValueType() ValueType + MustBeValid() Any + ToBool() bool + ToInt() int + ToInt32() int32 + ToInt64() int64 + ToUint() uint + ToUint32() uint32 + ToUint64() uint64 + ToFloat32() float32 + ToFloat64() float64 + ToString() string + ToVal(val interface{}) + Get(path ...interface{}) Any + Size() int + Keys() []string + GetInterface() interface{} + WriteTo(stream *Stream) +} + +type baseAny struct{} + +func (any *baseAny) Get(path ...interface{}) Any { + return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)} +} + +func (any *baseAny) Size() int { + return 0 +} + +func (any *baseAny) Keys() []string { + return []string{} +} + +func (any *baseAny) ToVal(obj interface{}) { + panic("not implemented") +} + +// WrapInt32 turn int32 into Any interface +func WrapInt32(val int32) Any { + return &int32Any{baseAny{}, val} +} + +// WrapInt64 turn int64 into Any interface +func WrapInt64(val int64) Any { + return &int64Any{baseAny{}, val} +} + +// WrapUint32 turn uint32 into Any interface +func WrapUint32(val uint32) Any { + return &uint32Any{baseAny{}, val} +} + +// WrapUint64 turn uint64 into Any interface +func WrapUint64(val uint64) Any { + return &uint64Any{baseAny{}, val} +} + +// WrapFloat64 turn float64 into Any interface +func WrapFloat64(val float64) Any { + return &floatAny{baseAny{}, val} +} + +// WrapString turn string into Any interface +func WrapString(val string) Any { + return &stringAny{baseAny{}, val} +} + +// Wrap turn a go object into Any interface +func Wrap(val interface{}) Any { + if val == nil { + return &nilAny{} + } + asAny, isAny := val.(Any) + if isAny { + return asAny + } + typ := reflect2.TypeOf(val) + switch typ.Kind() { + case reflect.Slice: + return wrapArray(val) + case reflect.Struct: + return wrapStruct(val) + case reflect.Map: + return wrapMap(val) + case reflect.String: + return WrapString(val.(string)) + case reflect.Int: + if strconv.IntSize == 32 { + return WrapInt32(int32(val.(int))) + } + return WrapInt64(int64(val.(int))) + case reflect.Int8: + return WrapInt32(int32(val.(int8))) + case reflect.Int16: + return WrapInt32(int32(val.(int16))) + case reflect.Int32: + return WrapInt32(val.(int32)) + case reflect.Int64: + return WrapInt64(val.(int64)) + case reflect.Uint: + if strconv.IntSize == 32 { + return WrapUint32(uint32(val.(uint))) + } + return WrapUint64(uint64(val.(uint))) + case reflect.Uintptr: + if ptrSize == 32 { + return WrapUint32(uint32(val.(uintptr))) + } + return WrapUint64(uint64(val.(uintptr))) + case reflect.Uint8: + return WrapUint32(uint32(val.(uint8))) + case reflect.Uint16: + return WrapUint32(uint32(val.(uint16))) + case reflect.Uint32: + return WrapUint32(uint32(val.(uint32))) + case reflect.Uint64: + return WrapUint64(val.(uint64)) + case reflect.Float32: + return WrapFloat64(float64(val.(float32))) + case reflect.Float64: + return WrapFloat64(val.(float64)) + case reflect.Bool: + if val.(bool) == true { + return &trueAny{} + } + return &falseAny{} + } + return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", typ)} +} + +// ReadAny read next JSON element as an Any object. It is a better json.RawMessage. +func (iter *Iterator) ReadAny() Any { + return iter.readAny() +} + +func (iter *Iterator) readAny() Any { + c := iter.nextToken() + switch c { + case '"': + iter.unreadByte() + return &stringAny{baseAny{}, iter.ReadString()} + case 'n': + iter.skipThreeBytes('u', 'l', 'l') // null + return &nilAny{} + case 't': + iter.skipThreeBytes('r', 'u', 'e') // true + return &trueAny{} + case 'f': + iter.skipFourBytes('a', 'l', 's', 'e') // false + return &falseAny{} + case '{': + return iter.readObjectAny() + case '[': + return iter.readArrayAny() + case '-': + return iter.readNumberAny(false) + case 0: + return &invalidAny{baseAny{}, errors.New("input is empty")} + default: + return iter.readNumberAny(true) + } +} + +func (iter *Iterator) readNumberAny(positive bool) Any { + iter.startCapture(iter.head - 1) + iter.skipNumber() + lazyBuf := iter.stopCapture() + return &numberLazyAny{baseAny{}, iter.cfg, lazyBuf, nil} +} + +func (iter *Iterator) readObjectAny() Any { + iter.startCapture(iter.head - 1) + iter.skipObject() + lazyBuf := iter.stopCapture() + return &objectLazyAny{baseAny{}, iter.cfg, lazyBuf, nil} +} + +func (iter *Iterator) readArrayAny() Any { + iter.startCapture(iter.head - 1) + iter.skipArray() + lazyBuf := iter.stopCapture() + return &arrayLazyAny{baseAny{}, iter.cfg, lazyBuf, nil} +} + +func locateObjectField(iter *Iterator, target string) []byte { + var found []byte + iter.ReadObjectCB(func(iter *Iterator, field string) bool { + if field == target { + found = iter.SkipAndReturnBytes() + return false + } + iter.Skip() + return true + }) + return found +} + +func locateArrayElement(iter *Iterator, target int) []byte { + var found []byte + n := 0 + iter.ReadArrayCB(func(iter *Iterator) bool { + if n == target { + found = iter.SkipAndReturnBytes() + return false + } + iter.Skip() + n++ + return true + }) + return found +} + +func locatePath(iter *Iterator, path []interface{}) Any { + for i, pathKeyObj := range path { + switch pathKey := pathKeyObj.(type) { + case string: + valueBytes := locateObjectField(iter, pathKey) + if valueBytes == nil { + return newInvalidAny(path[i:]) + } + iter.ResetBytes(valueBytes) + case int: + valueBytes := locateArrayElement(iter, pathKey) + if valueBytes == nil { + return newInvalidAny(path[i:]) + } + iter.ResetBytes(valueBytes) + case int32: + if '*' == pathKey { + return iter.readAny().Get(path[i:]...) + } + return newInvalidAny(path[i:]) + default: + return newInvalidAny(path[i:]) + } + } + if iter.Error != nil && iter.Error != io.EOF { + return &invalidAny{baseAny{}, iter.Error} + } + return iter.readAny() +} + +var anyType = reflect2.TypeOfPtr((*Any)(nil)).Elem() + +func createDecoderOfAny(ctx *ctx, typ reflect2.Type) ValDecoder { + if typ == anyType { + return &directAnyCodec{} + } + if typ.Implements(anyType) { + return &anyCodec{ + valType: typ, + } + } + return nil +} + +func createEncoderOfAny(ctx *ctx, typ reflect2.Type) ValEncoder { + if typ == anyType { + return &directAnyCodec{} + } + if typ.Implements(anyType) { + return &anyCodec{ + valType: typ, + } + } + return nil +} + +type anyCodec struct { + valType reflect2.Type +} + +func (codec *anyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { + panic("not implemented") +} + +func (codec *anyCodec) Encode(ptr unsafe.Pointer, stream *Stream) { + obj := codec.valType.UnsafeIndirect(ptr) + any := obj.(Any) + any.WriteTo(stream) +} + +func (codec *anyCodec) IsEmpty(ptr unsafe.Pointer) bool { + obj := codec.valType.UnsafeIndirect(ptr) + any := obj.(Any) + return any.Size() == 0 +} + +type directAnyCodec struct { +} + +func (codec *directAnyCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { + *(*Any)(ptr) = iter.readAny() +} + +func (codec *directAnyCodec) Encode(ptr unsafe.Pointer, stream *Stream) { + any := *(*Any)(ptr) + if any == nil { + stream.WriteNil() + return + } + any.WriteTo(stream) +} + +func (codec *directAnyCodec) IsEmpty(ptr unsafe.Pointer) bool { + any := *(*Any)(ptr) + return any.Size() == 0 +} diff --git a/vendor/github.com/json-iterator/go/any_array.go b/vendor/github.com/json-iterator/go/any_array.go new file mode 100644 index 0000000000000..0449e9aa428ae --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_array.go @@ -0,0 +1,278 @@ +package jsoniter + +import ( + "reflect" + "unsafe" +) + +type arrayLazyAny struct { + baseAny + cfg *frozenConfig + buf []byte + err error +} + +func (any *arrayLazyAny) ValueType() ValueType { + return ArrayValue +} + +func (any *arrayLazyAny) MustBeValid() Any { + return any +} + +func (any *arrayLazyAny) LastError() error { + return any.err +} + +func (any *arrayLazyAny) ToBool() bool { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + return iter.ReadArray() +} + +func (any *arrayLazyAny) ToInt() int { + if any.ToBool() { + return 1 + } + return 0 +} + +func (any *arrayLazyAny) ToInt32() int32 { + if any.ToBool() { + return 1 + } + return 0 +} + +func (any *arrayLazyAny) ToInt64() int64 { + if any.ToBool() { + return 1 + } + return 0 +} + +func (any *arrayLazyAny) ToUint() uint { + if any.ToBool() { + return 1 + } + return 0 +} + +func (any *arrayLazyAny) ToUint32() uint32 { + if any.ToBool() { + return 1 + } + return 0 +} + +func (any *arrayLazyAny) ToUint64() uint64 { + if any.ToBool() { + return 1 + } + return 0 +} + +func (any *arrayLazyAny) ToFloat32() float32 { + if any.ToBool() { + return 1 + } + return 0 +} + +func (any *arrayLazyAny) ToFloat64() float64 { + if any.ToBool() { + return 1 + } + return 0 +} + +func (any *arrayLazyAny) ToString() string { + return *(*string)(unsafe.Pointer(&any.buf)) +} + +func (any *arrayLazyAny) ToVal(val interface{}) { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + iter.ReadVal(val) +} + +func (any *arrayLazyAny) Get(path ...interface{}) Any { + if len(path) == 0 { + return any + } + switch firstPath := path[0].(type) { + case int: + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + valueBytes := locateArrayElement(iter, firstPath) + if valueBytes == nil { + return newInvalidAny(path) + } + iter.ResetBytes(valueBytes) + return locatePath(iter, path[1:]) + case int32: + if '*' == firstPath { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + arr := make([]Any, 0) + iter.ReadArrayCB(func(iter *Iterator) bool { + found := iter.readAny().Get(path[1:]...) + if found.ValueType() != InvalidValue { + arr = append(arr, found) + } + return true + }) + return wrapArray(arr) + } + return newInvalidAny(path) + default: + return newInvalidAny(path) + } +} + +func (any *arrayLazyAny) Size() int { + size := 0 + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + iter.ReadArrayCB(func(iter *Iterator) bool { + size++ + iter.Skip() + return true + }) + return size +} + +func (any *arrayLazyAny) WriteTo(stream *Stream) { + stream.Write(any.buf) +} + +func (any *arrayLazyAny) GetInterface() interface{} { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + return iter.Read() +} + +type arrayAny struct { + baseAny + val reflect.Value +} + +func wrapArray(val interface{}) *arrayAny { + return &arrayAny{baseAny{}, reflect.ValueOf(val)} +} + +func (any *arrayAny) ValueType() ValueType { + return ArrayValue +} + +func (any *arrayAny) MustBeValid() Any { + return any +} + +func (any *arrayAny) LastError() error { + return nil +} + +func (any *arrayAny) ToBool() bool { + return any.val.Len() != 0 +} + +func (any *arrayAny) ToInt() int { + if any.val.Len() == 0 { + return 0 + } + return 1 +} + +func (any *arrayAny) ToInt32() int32 { + if any.val.Len() == 0 { + return 0 + } + return 1 +} + +func (any *arrayAny) ToInt64() int64 { + if any.val.Len() == 0 { + return 0 + } + return 1 +} + +func (any *arrayAny) ToUint() uint { + if any.val.Len() == 0 { + return 0 + } + return 1 +} + +func (any *arrayAny) ToUint32() uint32 { + if any.val.Len() == 0 { + return 0 + } + return 1 +} + +func (any *arrayAny) ToUint64() uint64 { + if any.val.Len() == 0 { + return 0 + } + return 1 +} + +func (any *arrayAny) ToFloat32() float32 { + if any.val.Len() == 0 { + return 0 + } + return 1 +} + +func (any *arrayAny) ToFloat64() float64 { + if any.val.Len() == 0 { + return 0 + } + return 1 +} + +func (any *arrayAny) ToString() string { + str, _ := MarshalToString(any.val.Interface()) + return str +} + +func (any *arrayAny) Get(path ...interface{}) Any { + if len(path) == 0 { + return any + } + switch firstPath := path[0].(type) { + case int: + if firstPath < 0 || firstPath >= any.val.Len() { + return newInvalidAny(path) + } + return Wrap(any.val.Index(firstPath).Interface()) + case int32: + if '*' == firstPath { + mappedAll := make([]Any, 0) + for i := 0; i < any.val.Len(); i++ { + mapped := Wrap(any.val.Index(i).Interface()).Get(path[1:]...) + if mapped.ValueType() != InvalidValue { + mappedAll = append(mappedAll, mapped) + } + } + return wrapArray(mappedAll) + } + return newInvalidAny(path) + default: + return newInvalidAny(path) + } +} + +func (any *arrayAny) Size() int { + return any.val.Len() +} + +func (any *arrayAny) WriteTo(stream *Stream) { + stream.WriteVal(any.val) +} + +func (any *arrayAny) GetInterface() interface{} { + return any.val.Interface() +} diff --git a/vendor/github.com/json-iterator/go/any_bool.go b/vendor/github.com/json-iterator/go/any_bool.go new file mode 100644 index 0000000000000..9452324af5b17 --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_bool.go @@ -0,0 +1,137 @@ +package jsoniter + +type trueAny struct { + baseAny +} + +func (any *trueAny) LastError() error { + return nil +} + +func (any *trueAny) ToBool() bool { + return true +} + +func (any *trueAny) ToInt() int { + return 1 +} + +func (any *trueAny) ToInt32() int32 { + return 1 +} + +func (any *trueAny) ToInt64() int64 { + return 1 +} + +func (any *trueAny) ToUint() uint { + return 1 +} + +func (any *trueAny) ToUint32() uint32 { + return 1 +} + +func (any *trueAny) ToUint64() uint64 { + return 1 +} + +func (any *trueAny) ToFloat32() float32 { + return 1 +} + +func (any *trueAny) ToFloat64() float64 { + return 1 +} + +func (any *trueAny) ToString() string { + return "true" +} + +func (any *trueAny) WriteTo(stream *Stream) { + stream.WriteTrue() +} + +func (any *trueAny) Parse() *Iterator { + return nil +} + +func (any *trueAny) GetInterface() interface{} { + return true +} + +func (any *trueAny) ValueType() ValueType { + return BoolValue +} + +func (any *trueAny) MustBeValid() Any { + return any +} + +type falseAny struct { + baseAny +} + +func (any *falseAny) LastError() error { + return nil +} + +func (any *falseAny) ToBool() bool { + return false +} + +func (any *falseAny) ToInt() int { + return 0 +} + +func (any *falseAny) ToInt32() int32 { + return 0 +} + +func (any *falseAny) ToInt64() int64 { + return 0 +} + +func (any *falseAny) ToUint() uint { + return 0 +} + +func (any *falseAny) ToUint32() uint32 { + return 0 +} + +func (any *falseAny) ToUint64() uint64 { + return 0 +} + +func (any *falseAny) ToFloat32() float32 { + return 0 +} + +func (any *falseAny) ToFloat64() float64 { + return 0 +} + +func (any *falseAny) ToString() string { + return "false" +} + +func (any *falseAny) WriteTo(stream *Stream) { + stream.WriteFalse() +} + +func (any *falseAny) Parse() *Iterator { + return nil +} + +func (any *falseAny) GetInterface() interface{} { + return false +} + +func (any *falseAny) ValueType() ValueType { + return BoolValue +} + +func (any *falseAny) MustBeValid() Any { + return any +} diff --git a/vendor/github.com/json-iterator/go/any_float.go b/vendor/github.com/json-iterator/go/any_float.go new file mode 100644 index 0000000000000..35fdb09497fa8 --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_float.go @@ -0,0 +1,83 @@ +package jsoniter + +import ( + "strconv" +) + +type floatAny struct { + baseAny + val float64 +} + +func (any *floatAny) Parse() *Iterator { + return nil +} + +func (any *floatAny) ValueType() ValueType { + return NumberValue +} + +func (any *floatAny) MustBeValid() Any { + return any +} + +func (any *floatAny) LastError() error { + return nil +} + +func (any *floatAny) ToBool() bool { + return any.ToFloat64() != 0 +} + +func (any *floatAny) ToInt() int { + return int(any.val) +} + +func (any *floatAny) ToInt32() int32 { + return int32(any.val) +} + +func (any *floatAny) ToInt64() int64 { + return int64(any.val) +} + +func (any *floatAny) ToUint() uint { + if any.val > 0 { + return uint(any.val) + } + return 0 +} + +func (any *floatAny) ToUint32() uint32 { + if any.val > 0 { + return uint32(any.val) + } + return 0 +} + +func (any *floatAny) ToUint64() uint64 { + if any.val > 0 { + return uint64(any.val) + } + return 0 +} + +func (any *floatAny) ToFloat32() float32 { + return float32(any.val) +} + +func (any *floatAny) ToFloat64() float64 { + return any.val +} + +func (any *floatAny) ToString() string { + return strconv.FormatFloat(any.val, 'E', -1, 64) +} + +func (any *floatAny) WriteTo(stream *Stream) { + stream.WriteFloat64(any.val) +} + +func (any *floatAny) GetInterface() interface{} { + return any.val +} diff --git a/vendor/github.com/json-iterator/go/any_int32.go b/vendor/github.com/json-iterator/go/any_int32.go new file mode 100644 index 0000000000000..1b56f399150d9 --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_int32.go @@ -0,0 +1,74 @@ +package jsoniter + +import ( + "strconv" +) + +type int32Any struct { + baseAny + val int32 +} + +func (any *int32Any) LastError() error { + return nil +} + +func (any *int32Any) ValueType() ValueType { + return NumberValue +} + +func (any *int32Any) MustBeValid() Any { + return any +} + +func (any *int32Any) ToBool() bool { + return any.val != 0 +} + +func (any *int32Any) ToInt() int { + return int(any.val) +} + +func (any *int32Any) ToInt32() int32 { + return any.val +} + +func (any *int32Any) ToInt64() int64 { + return int64(any.val) +} + +func (any *int32Any) ToUint() uint { + return uint(any.val) +} + +func (any *int32Any) ToUint32() uint32 { + return uint32(any.val) +} + +func (any *int32Any) ToUint64() uint64 { + return uint64(any.val) +} + +func (any *int32Any) ToFloat32() float32 { + return float32(any.val) +} + +func (any *int32Any) ToFloat64() float64 { + return float64(any.val) +} + +func (any *int32Any) ToString() string { + return strconv.FormatInt(int64(any.val), 10) +} + +func (any *int32Any) WriteTo(stream *Stream) { + stream.WriteInt32(any.val) +} + +func (any *int32Any) Parse() *Iterator { + return nil +} + +func (any *int32Any) GetInterface() interface{} { + return any.val +} diff --git a/vendor/github.com/json-iterator/go/any_int64.go b/vendor/github.com/json-iterator/go/any_int64.go new file mode 100644 index 0000000000000..c440d72b6d3ae --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_int64.go @@ -0,0 +1,74 @@ +package jsoniter + +import ( + "strconv" +) + +type int64Any struct { + baseAny + val int64 +} + +func (any *int64Any) LastError() error { + return nil +} + +func (any *int64Any) ValueType() ValueType { + return NumberValue +} + +func (any *int64Any) MustBeValid() Any { + return any +} + +func (any *int64Any) ToBool() bool { + return any.val != 0 +} + +func (any *int64Any) ToInt() int { + return int(any.val) +} + +func (any *int64Any) ToInt32() int32 { + return int32(any.val) +} + +func (any *int64Any) ToInt64() int64 { + return any.val +} + +func (any *int64Any) ToUint() uint { + return uint(any.val) +} + +func (any *int64Any) ToUint32() uint32 { + return uint32(any.val) +} + +func (any *int64Any) ToUint64() uint64 { + return uint64(any.val) +} + +func (any *int64Any) ToFloat32() float32 { + return float32(any.val) +} + +func (any *int64Any) ToFloat64() float64 { + return float64(any.val) +} + +func (any *int64Any) ToString() string { + return strconv.FormatInt(any.val, 10) +} + +func (any *int64Any) WriteTo(stream *Stream) { + stream.WriteInt64(any.val) +} + +func (any *int64Any) Parse() *Iterator { + return nil +} + +func (any *int64Any) GetInterface() interface{} { + return any.val +} diff --git a/vendor/github.com/json-iterator/go/any_invalid.go b/vendor/github.com/json-iterator/go/any_invalid.go new file mode 100644 index 0000000000000..1d859eac3274a --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_invalid.go @@ -0,0 +1,82 @@ +package jsoniter + +import "fmt" + +type invalidAny struct { + baseAny + err error +} + +func newInvalidAny(path []interface{}) *invalidAny { + return &invalidAny{baseAny{}, fmt.Errorf("%v not found", path)} +} + +func (any *invalidAny) LastError() error { + return any.err +} + +func (any *invalidAny) ValueType() ValueType { + return InvalidValue +} + +func (any *invalidAny) MustBeValid() Any { + panic(any.err) +} + +func (any *invalidAny) ToBool() bool { + return false +} + +func (any *invalidAny) ToInt() int { + return 0 +} + +func (any *invalidAny) ToInt32() int32 { + return 0 +} + +func (any *invalidAny) ToInt64() int64 { + return 0 +} + +func (any *invalidAny) ToUint() uint { + return 0 +} + +func (any *invalidAny) ToUint32() uint32 { + return 0 +} + +func (any *invalidAny) ToUint64() uint64 { + return 0 +} + +func (any *invalidAny) ToFloat32() float32 { + return 0 +} + +func (any *invalidAny) ToFloat64() float64 { + return 0 +} + +func (any *invalidAny) ToString() string { + return "" +} + +func (any *invalidAny) WriteTo(stream *Stream) { +} + +func (any *invalidAny) Get(path ...interface{}) Any { + if any.err == nil { + return &invalidAny{baseAny{}, fmt.Errorf("get %v from invalid", path)} + } + return &invalidAny{baseAny{}, fmt.Errorf("%v, get %v from invalid", any.err, path)} +} + +func (any *invalidAny) Parse() *Iterator { + return nil +} + +func (any *invalidAny) GetInterface() interface{} { + return nil +} diff --git a/vendor/github.com/json-iterator/go/any_nil.go b/vendor/github.com/json-iterator/go/any_nil.go new file mode 100644 index 0000000000000..d04cb54c11c1e --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_nil.go @@ -0,0 +1,69 @@ +package jsoniter + +type nilAny struct { + baseAny +} + +func (any *nilAny) LastError() error { + return nil +} + +func (any *nilAny) ValueType() ValueType { + return NilValue +} + +func (any *nilAny) MustBeValid() Any { + return any +} + +func (any *nilAny) ToBool() bool { + return false +} + +func (any *nilAny) ToInt() int { + return 0 +} + +func (any *nilAny) ToInt32() int32 { + return 0 +} + +func (any *nilAny) ToInt64() int64 { + return 0 +} + +func (any *nilAny) ToUint() uint { + return 0 +} + +func (any *nilAny) ToUint32() uint32 { + return 0 +} + +func (any *nilAny) ToUint64() uint64 { + return 0 +} + +func (any *nilAny) ToFloat32() float32 { + return 0 +} + +func (any *nilAny) ToFloat64() float64 { + return 0 +} + +func (any *nilAny) ToString() string { + return "" +} + +func (any *nilAny) WriteTo(stream *Stream) { + stream.WriteNil() +} + +func (any *nilAny) Parse() *Iterator { + return nil +} + +func (any *nilAny) GetInterface() interface{} { + return nil +} diff --git a/vendor/github.com/json-iterator/go/any_number.go b/vendor/github.com/json-iterator/go/any_number.go new file mode 100644 index 0000000000000..9d1e901a66ad3 --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_number.go @@ -0,0 +1,123 @@ +package jsoniter + +import ( + "io" + "unsafe" +) + +type numberLazyAny struct { + baseAny + cfg *frozenConfig + buf []byte + err error +} + +func (any *numberLazyAny) ValueType() ValueType { + return NumberValue +} + +func (any *numberLazyAny) MustBeValid() Any { + return any +} + +func (any *numberLazyAny) LastError() error { + return any.err +} + +func (any *numberLazyAny) ToBool() bool { + return any.ToFloat64() != 0 +} + +func (any *numberLazyAny) ToInt() int { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + val := iter.ReadInt() + if iter.Error != nil && iter.Error != io.EOF { + any.err = iter.Error + } + return val +} + +func (any *numberLazyAny) ToInt32() int32 { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + val := iter.ReadInt32() + if iter.Error != nil && iter.Error != io.EOF { + any.err = iter.Error + } + return val +} + +func (any *numberLazyAny) ToInt64() int64 { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + val := iter.ReadInt64() + if iter.Error != nil && iter.Error != io.EOF { + any.err = iter.Error + } + return val +} + +func (any *numberLazyAny) ToUint() uint { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + val := iter.ReadUint() + if iter.Error != nil && iter.Error != io.EOF { + any.err = iter.Error + } + return val +} + +func (any *numberLazyAny) ToUint32() uint32 { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + val := iter.ReadUint32() + if iter.Error != nil && iter.Error != io.EOF { + any.err = iter.Error + } + return val +} + +func (any *numberLazyAny) ToUint64() uint64 { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + val := iter.ReadUint64() + if iter.Error != nil && iter.Error != io.EOF { + any.err = iter.Error + } + return val +} + +func (any *numberLazyAny) ToFloat32() float32 { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + val := iter.ReadFloat32() + if iter.Error != nil && iter.Error != io.EOF { + any.err = iter.Error + } + return val +} + +func (any *numberLazyAny) ToFloat64() float64 { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + val := iter.ReadFloat64() + if iter.Error != nil && iter.Error != io.EOF { + any.err = iter.Error + } + return val +} + +func (any *numberLazyAny) ToString() string { + return *(*string)(unsafe.Pointer(&any.buf)) +} + +func (any *numberLazyAny) WriteTo(stream *Stream) { + stream.Write(any.buf) +} + +func (any *numberLazyAny) GetInterface() interface{} { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + return iter.Read() +} diff --git a/vendor/github.com/json-iterator/go/any_object.go b/vendor/github.com/json-iterator/go/any_object.go new file mode 100644 index 0000000000000..c44ef5c989a46 --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_object.go @@ -0,0 +1,374 @@ +package jsoniter + +import ( + "reflect" + "unsafe" +) + +type objectLazyAny struct { + baseAny + cfg *frozenConfig + buf []byte + err error +} + +func (any *objectLazyAny) ValueType() ValueType { + return ObjectValue +} + +func (any *objectLazyAny) MustBeValid() Any { + return any +} + +func (any *objectLazyAny) LastError() error { + return any.err +} + +func (any *objectLazyAny) ToBool() bool { + return true +} + +func (any *objectLazyAny) ToInt() int { + return 0 +} + +func (any *objectLazyAny) ToInt32() int32 { + return 0 +} + +func (any *objectLazyAny) ToInt64() int64 { + return 0 +} + +func (any *objectLazyAny) ToUint() uint { + return 0 +} + +func (any *objectLazyAny) ToUint32() uint32 { + return 0 +} + +func (any *objectLazyAny) ToUint64() uint64 { + return 0 +} + +func (any *objectLazyAny) ToFloat32() float32 { + return 0 +} + +func (any *objectLazyAny) ToFloat64() float64 { + return 0 +} + +func (any *objectLazyAny) ToString() string { + return *(*string)(unsafe.Pointer(&any.buf)) +} + +func (any *objectLazyAny) ToVal(obj interface{}) { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + iter.ReadVal(obj) +} + +func (any *objectLazyAny) Get(path ...interface{}) Any { + if len(path) == 0 { + return any + } + switch firstPath := path[0].(type) { + case string: + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + valueBytes := locateObjectField(iter, firstPath) + if valueBytes == nil { + return newInvalidAny(path) + } + iter.ResetBytes(valueBytes) + return locatePath(iter, path[1:]) + case int32: + if '*' == firstPath { + mappedAll := map[string]Any{} + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + iter.ReadMapCB(func(iter *Iterator, field string) bool { + mapped := locatePath(iter, path[1:]) + if mapped.ValueType() != InvalidValue { + mappedAll[field] = mapped + } + return true + }) + return wrapMap(mappedAll) + } + return newInvalidAny(path) + default: + return newInvalidAny(path) + } +} + +func (any *objectLazyAny) Keys() []string { + keys := []string{} + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + iter.ReadMapCB(func(iter *Iterator, field string) bool { + iter.Skip() + keys = append(keys, field) + return true + }) + return keys +} + +func (any *objectLazyAny) Size() int { + size := 0 + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + iter.ReadObjectCB(func(iter *Iterator, field string) bool { + iter.Skip() + size++ + return true + }) + return size +} + +func (any *objectLazyAny) WriteTo(stream *Stream) { + stream.Write(any.buf) +} + +func (any *objectLazyAny) GetInterface() interface{} { + iter := any.cfg.BorrowIterator(any.buf) + defer any.cfg.ReturnIterator(iter) + return iter.Read() +} + +type objectAny struct { + baseAny + err error + val reflect.Value +} + +func wrapStruct(val interface{}) *objectAny { + return &objectAny{baseAny{}, nil, reflect.ValueOf(val)} +} + +func (any *objectAny) ValueType() ValueType { + return ObjectValue +} + +func (any *objectAny) MustBeValid() Any { + return any +} + +func (any *objectAny) Parse() *Iterator { + return nil +} + +func (any *objectAny) LastError() error { + return any.err +} + +func (any *objectAny) ToBool() bool { + return any.val.NumField() != 0 +} + +func (any *objectAny) ToInt() int { + return 0 +} + +func (any *objectAny) ToInt32() int32 { + return 0 +} + +func (any *objectAny) ToInt64() int64 { + return 0 +} + +func (any *objectAny) ToUint() uint { + return 0 +} + +func (any *objectAny) ToUint32() uint32 { + return 0 +} + +func (any *objectAny) ToUint64() uint64 { + return 0 +} + +func (any *objectAny) ToFloat32() float32 { + return 0 +} + +func (any *objectAny) ToFloat64() float64 { + return 0 +} + +func (any *objectAny) ToString() string { + str, err := MarshalToString(any.val.Interface()) + any.err = err + return str +} + +func (any *objectAny) Get(path ...interface{}) Any { + if len(path) == 0 { + return any + } + switch firstPath := path[0].(type) { + case string: + field := any.val.FieldByName(firstPath) + if !field.IsValid() { + return newInvalidAny(path) + } + return Wrap(field.Interface()) + case int32: + if '*' == firstPath { + mappedAll := map[string]Any{} + for i := 0; i < any.val.NumField(); i++ { + field := any.val.Field(i) + if field.CanInterface() { + mapped := Wrap(field.Interface()).Get(path[1:]...) + if mapped.ValueType() != InvalidValue { + mappedAll[any.val.Type().Field(i).Name] = mapped + } + } + } + return wrapMap(mappedAll) + } + return newInvalidAny(path) + default: + return newInvalidAny(path) + } +} + +func (any *objectAny) Keys() []string { + keys := make([]string, 0, any.val.NumField()) + for i := 0; i < any.val.NumField(); i++ { + keys = append(keys, any.val.Type().Field(i).Name) + } + return keys +} + +func (any *objectAny) Size() int { + return any.val.NumField() +} + +func (any *objectAny) WriteTo(stream *Stream) { + stream.WriteVal(any.val) +} + +func (any *objectAny) GetInterface() interface{} { + return any.val.Interface() +} + +type mapAny struct { + baseAny + err error + val reflect.Value +} + +func wrapMap(val interface{}) *mapAny { + return &mapAny{baseAny{}, nil, reflect.ValueOf(val)} +} + +func (any *mapAny) ValueType() ValueType { + return ObjectValue +} + +func (any *mapAny) MustBeValid() Any { + return any +} + +func (any *mapAny) Parse() *Iterator { + return nil +} + +func (any *mapAny) LastError() error { + return any.err +} + +func (any *mapAny) ToBool() bool { + return true +} + +func (any *mapAny) ToInt() int { + return 0 +} + +func (any *mapAny) ToInt32() int32 { + return 0 +} + +func (any *mapAny) ToInt64() int64 { + return 0 +} + +func (any *mapAny) ToUint() uint { + return 0 +} + +func (any *mapAny) ToUint32() uint32 { + return 0 +} + +func (any *mapAny) ToUint64() uint64 { + return 0 +} + +func (any *mapAny) ToFloat32() float32 { + return 0 +} + +func (any *mapAny) ToFloat64() float64 { + return 0 +} + +func (any *mapAny) ToString() string { + str, err := MarshalToString(any.val.Interface()) + any.err = err + return str +} + +func (any *mapAny) Get(path ...interface{}) Any { + if len(path) == 0 { + return any + } + switch firstPath := path[0].(type) { + case int32: + if '*' == firstPath { + mappedAll := map[string]Any{} + for _, key := range any.val.MapKeys() { + keyAsStr := key.String() + element := Wrap(any.val.MapIndex(key).Interface()) + mapped := element.Get(path[1:]...) + if mapped.ValueType() != InvalidValue { + mappedAll[keyAsStr] = mapped + } + } + return wrapMap(mappedAll) + } + return newInvalidAny(path) + default: + value := any.val.MapIndex(reflect.ValueOf(firstPath)) + if !value.IsValid() { + return newInvalidAny(path) + } + return Wrap(value.Interface()) + } +} + +func (any *mapAny) Keys() []string { + keys := make([]string, 0, any.val.Len()) + for _, key := range any.val.MapKeys() { + keys = append(keys, key.String()) + } + return keys +} + +func (any *mapAny) Size() int { + return any.val.Len() +} + +func (any *mapAny) WriteTo(stream *Stream) { + stream.WriteVal(any.val) +} + +func (any *mapAny) GetInterface() interface{} { + return any.val.Interface() +} diff --git a/vendor/github.com/json-iterator/go/any_str.go b/vendor/github.com/json-iterator/go/any_str.go new file mode 100644 index 0000000000000..1f12f6612de98 --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_str.go @@ -0,0 +1,166 @@ +package jsoniter + +import ( + "fmt" + "strconv" +) + +type stringAny struct { + baseAny + val string +} + +func (any *stringAny) Get(path ...interface{}) Any { + if len(path) == 0 { + return any + } + return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)} +} + +func (any *stringAny) Parse() *Iterator { + return nil +} + +func (any *stringAny) ValueType() ValueType { + return StringValue +} + +func (any *stringAny) MustBeValid() Any { + return any +} + +func (any *stringAny) LastError() error { + return nil +} + +func (any *stringAny) ToBool() bool { + str := any.ToString() + if str == "0" { + return false + } + for _, c := range str { + switch c { + case ' ', '\n', '\r', '\t': + default: + return true + } + } + return false +} + +func (any *stringAny) ToInt() int { + return int(any.ToInt64()) + +} + +func (any *stringAny) ToInt32() int32 { + return int32(any.ToInt64()) +} + +func (any *stringAny) ToInt64() int64 { + if any.val == "" { + return 0 + } + + flag := 1 + startPos := 0 + if any.val[0] == '+' || any.val[0] == '-' { + startPos = 1 + } + + if any.val[0] == '-' { + flag = -1 + } + + endPos := startPos + for i := startPos; i < len(any.val); i++ { + if any.val[i] >= '0' && any.val[i] <= '9' { + endPos = i + 1 + } else { + break + } + } + parsed, _ := strconv.ParseInt(any.val[startPos:endPos], 10, 64) + return int64(flag) * parsed +} + +func (any *stringAny) ToUint() uint { + return uint(any.ToUint64()) +} + +func (any *stringAny) ToUint32() uint32 { + return uint32(any.ToUint64()) +} + +func (any *stringAny) ToUint64() uint64 { + if any.val == "" { + return 0 + } + + startPos := 0 + + if any.val[0] == '-' { + return 0 + } + if any.val[0] == '+' { + startPos = 1 + } + + endPos := startPos + for i := startPos; i < len(any.val); i++ { + if any.val[i] >= '0' && any.val[i] <= '9' { + endPos = i + 1 + } else { + break + } + } + parsed, _ := strconv.ParseUint(any.val[startPos:endPos], 10, 64) + return parsed +} + +func (any *stringAny) ToFloat32() float32 { + return float32(any.ToFloat64()) +} + +func (any *stringAny) ToFloat64() float64 { + if len(any.val) == 0 { + return 0 + } + + // first char invalid + if any.val[0] != '+' && any.val[0] != '-' && (any.val[0] > '9' || any.val[0] < '0') { + return 0 + } + + // extract valid num expression from string + // eg 123true => 123, -12.12xxa => -12.12 + endPos := 1 + for i := 1; i < len(any.val); i++ { + if any.val[i] == '.' || any.val[i] == 'e' || any.val[i] == 'E' || any.val[i] == '+' || any.val[i] == '-' { + endPos = i + 1 + continue + } + + // end position is the first char which is not digit + if any.val[i] >= '0' && any.val[i] <= '9' { + endPos = i + 1 + } else { + endPos = i + break + } + } + parsed, _ := strconv.ParseFloat(any.val[:endPos], 64) + return parsed +} + +func (any *stringAny) ToString() string { + return any.val +} + +func (any *stringAny) WriteTo(stream *Stream) { + stream.WriteString(any.val) +} + +func (any *stringAny) GetInterface() interface{} { + return any.val +} diff --git a/vendor/github.com/json-iterator/go/any_uint32.go b/vendor/github.com/json-iterator/go/any_uint32.go new file mode 100644 index 0000000000000..656bbd33d7ee9 --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_uint32.go @@ -0,0 +1,74 @@ +package jsoniter + +import ( + "strconv" +) + +type uint32Any struct { + baseAny + val uint32 +} + +func (any *uint32Any) LastError() error { + return nil +} + +func (any *uint32Any) ValueType() ValueType { + return NumberValue +} + +func (any *uint32Any) MustBeValid() Any { + return any +} + +func (any *uint32Any) ToBool() bool { + return any.val != 0 +} + +func (any *uint32Any) ToInt() int { + return int(any.val) +} + +func (any *uint32Any) ToInt32() int32 { + return int32(any.val) +} + +func (any *uint32Any) ToInt64() int64 { + return int64(any.val) +} + +func (any *uint32Any) ToUint() uint { + return uint(any.val) +} + +func (any *uint32Any) ToUint32() uint32 { + return any.val +} + +func (any *uint32Any) ToUint64() uint64 { + return uint64(any.val) +} + +func (any *uint32Any) ToFloat32() float32 { + return float32(any.val) +} + +func (any *uint32Any) ToFloat64() float64 { + return float64(any.val) +} + +func (any *uint32Any) ToString() string { + return strconv.FormatInt(int64(any.val), 10) +} + +func (any *uint32Any) WriteTo(stream *Stream) { + stream.WriteUint32(any.val) +} + +func (any *uint32Any) Parse() *Iterator { + return nil +} + +func (any *uint32Any) GetInterface() interface{} { + return any.val +} diff --git a/vendor/github.com/json-iterator/go/any_uint64.go b/vendor/github.com/json-iterator/go/any_uint64.go new file mode 100644 index 0000000000000..7df2fce33ba97 --- /dev/null +++ b/vendor/github.com/json-iterator/go/any_uint64.go @@ -0,0 +1,74 @@ +package jsoniter + +import ( + "strconv" +) + +type uint64Any struct { + baseAny + val uint64 +} + +func (any *uint64Any) LastError() error { + return nil +} + +func (any *uint64Any) ValueType() ValueType { + return NumberValue +} + +func (any *uint64Any) MustBeValid() Any { + return any +} + +func (any *uint64Any) ToBool() bool { + return any.val != 0 +} + +func (any *uint64Any) ToInt() int { + return int(any.val) +} + +func (any *uint64Any) ToInt32() int32 { + return int32(any.val) +} + +func (any *uint64Any) ToInt64() int64 { + return int64(any.val) +} + +func (any *uint64Any) ToUint() uint { + return uint(any.val) +} + +func (any *uint64Any) ToUint32() uint32 { + return uint32(any.val) +} + +func (any *uint64Any) ToUint64() uint64 { + return any.val +} + +func (any *uint64Any) ToFloat32() float32 { + return float32(any.val) +} + +func (any *uint64Any) ToFloat64() float64 { + return float64(any.val) +} + +func (any *uint64Any) ToString() string { + return strconv.FormatUint(any.val, 10) +} + +func (any *uint64Any) WriteTo(stream *Stream) { + stream.WriteUint64(any.val) +} + +func (any *uint64Any) Parse() *Iterator { + return nil +} + +func (any *uint64Any) GetInterface() interface{} { + return any.val +} diff --git a/vendor/github.com/json-iterator/go/config.go b/vendor/github.com/json-iterator/go/config.go new file mode 100644 index 0000000000000..2adcdc3b790e5 --- /dev/null +++ b/vendor/github.com/json-iterator/go/config.go @@ -0,0 +1,375 @@ +package jsoniter + +import ( + "encoding/json" + "io" + "reflect" + "sync" + "unsafe" + + "github.com/modern-go/concurrent" + "github.com/modern-go/reflect2" +) + +// Config customize how the API should behave. +// The API is created from Config by Froze. +type Config struct { + IndentionStep int + MarshalFloatWith6Digits bool + EscapeHTML bool + SortMapKeys bool + UseNumber bool + DisallowUnknownFields bool + TagKey string + OnlyTaggedField bool + ValidateJsonRawMessage bool + ObjectFieldMustBeSimpleString bool + CaseSensitive bool +} + +// API the public interface of this package. +// Primary Marshal and Unmarshal. +type API interface { + IteratorPool + StreamPool + MarshalToString(v interface{}) (string, error) + Marshal(v interface{}) ([]byte, error) + MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) + UnmarshalFromString(str string, v interface{}) error + Unmarshal(data []byte, v interface{}) error + Get(data []byte, path ...interface{}) Any + NewEncoder(writer io.Writer) *Encoder + NewDecoder(reader io.Reader) *Decoder + Valid(data []byte) bool + RegisterExtension(extension Extension) + DecoderOf(typ reflect2.Type) ValDecoder + EncoderOf(typ reflect2.Type) ValEncoder +} + +// ConfigDefault the default API +var ConfigDefault = Config{ + EscapeHTML: true, +}.Froze() + +// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior +var ConfigCompatibleWithStandardLibrary = Config{ + EscapeHTML: true, + SortMapKeys: true, + ValidateJsonRawMessage: true, +}.Froze() + +// ConfigFastest marshals float with only 6 digits precision +var ConfigFastest = Config{ + EscapeHTML: false, + MarshalFloatWith6Digits: true, // will lose precession + ObjectFieldMustBeSimpleString: true, // do not unescape object field +}.Froze() + +type frozenConfig struct { + configBeforeFrozen Config + sortMapKeys bool + indentionStep int + objectFieldMustBeSimpleString bool + onlyTaggedField bool + disallowUnknownFields bool + decoderCache *concurrent.Map + encoderCache *concurrent.Map + encoderExtension Extension + decoderExtension Extension + extraExtensions []Extension + streamPool *sync.Pool + iteratorPool *sync.Pool + caseSensitive bool +} + +func (cfg *frozenConfig) initCache() { + cfg.decoderCache = concurrent.NewMap() + cfg.encoderCache = concurrent.NewMap() +} + +func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) { + cfg.decoderCache.Store(cacheKey, decoder) +} + +func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) { + cfg.encoderCache.Store(cacheKey, encoder) +} + +func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder { + decoder, found := cfg.decoderCache.Load(cacheKey) + if found { + return decoder.(ValDecoder) + } + return nil +} + +func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder { + encoder, found := cfg.encoderCache.Load(cacheKey) + if found { + return encoder.(ValEncoder) + } + return nil +} + +var cfgCache = concurrent.NewMap() + +func getFrozenConfigFromCache(cfg Config) *frozenConfig { + obj, found := cfgCache.Load(cfg) + if found { + return obj.(*frozenConfig) + } + return nil +} + +func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) { + cfgCache.Store(cfg, frozenConfig) +} + +// Froze forge API from config +func (cfg Config) Froze() API { + api := &frozenConfig{ + sortMapKeys: cfg.SortMapKeys, + indentionStep: cfg.IndentionStep, + objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString, + onlyTaggedField: cfg.OnlyTaggedField, + disallowUnknownFields: cfg.DisallowUnknownFields, + caseSensitive: cfg.CaseSensitive, + } + api.streamPool = &sync.Pool{ + New: func() interface{} { + return NewStream(api, nil, 512) + }, + } + api.iteratorPool = &sync.Pool{ + New: func() interface{} { + return NewIterator(api) + }, + } + api.initCache() + encoderExtension := EncoderExtension{} + decoderExtension := DecoderExtension{} + if cfg.MarshalFloatWith6Digits { + api.marshalFloatWith6Digits(encoderExtension) + } + if cfg.EscapeHTML { + api.escapeHTML(encoderExtension) + } + if cfg.UseNumber { + api.useNumber(decoderExtension) + } + if cfg.ValidateJsonRawMessage { + api.validateJsonRawMessage(encoderExtension) + } + api.encoderExtension = encoderExtension + api.decoderExtension = decoderExtension + api.configBeforeFrozen = cfg + return api +} + +func (cfg Config) frozeWithCacheReuse(extraExtensions []Extension) *frozenConfig { + api := getFrozenConfigFromCache(cfg) + if api != nil { + return api + } + api = cfg.Froze().(*frozenConfig) + for _, extension := range extraExtensions { + api.RegisterExtension(extension) + } + addFrozenConfigToCache(cfg, api) + return api +} + +func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) { + encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) { + rawMessage := *(*json.RawMessage)(ptr) + iter := cfg.BorrowIterator([]byte(rawMessage)) + defer cfg.ReturnIterator(iter) + iter.Read() + if iter.Error != nil && iter.Error != io.EOF { + stream.WriteRaw("null") + } else { + stream.WriteRaw(string(rawMessage)) + } + }, func(ptr unsafe.Pointer) bool { + return len(*((*json.RawMessage)(ptr))) == 0 + }} + extension[reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()] = encoder + extension[reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()] = encoder +} + +func (cfg *frozenConfig) useNumber(extension DecoderExtension) { + extension[reflect2.TypeOfPtr((*interface{})(nil)).Elem()] = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) { + exitingValue := *((*interface{})(ptr)) + if exitingValue != nil && reflect.TypeOf(exitingValue).Kind() == reflect.Ptr { + iter.ReadVal(exitingValue) + return + } + if iter.WhatIsNext() == NumberValue { + *((*interface{})(ptr)) = json.Number(iter.readNumberAsString()) + } else { + *((*interface{})(ptr)) = iter.Read() + } + }} +} +func (cfg *frozenConfig) getTagKey() string { + tagKey := cfg.configBeforeFrozen.TagKey + if tagKey == "" { + return "json" + } + return tagKey +} + +func (cfg *frozenConfig) RegisterExtension(extension Extension) { + cfg.extraExtensions = append(cfg.extraExtensions, extension) + copied := cfg.configBeforeFrozen + cfg.configBeforeFrozen = copied +} + +type lossyFloat32Encoder struct { +} + +func (encoder *lossyFloat32Encoder) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteFloat32Lossy(*((*float32)(ptr))) +} + +func (encoder *lossyFloat32Encoder) IsEmpty(ptr unsafe.Pointer) bool { + return *((*float32)(ptr)) == 0 +} + +type lossyFloat64Encoder struct { +} + +func (encoder *lossyFloat64Encoder) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteFloat64Lossy(*((*float64)(ptr))) +} + +func (encoder *lossyFloat64Encoder) IsEmpty(ptr unsafe.Pointer) bool { + return *((*float64)(ptr)) == 0 +} + +// EnableLossyFloatMarshalling keeps 10**(-6) precision +// for float variables for better performance. +func (cfg *frozenConfig) marshalFloatWith6Digits(extension EncoderExtension) { + // for better performance + extension[reflect2.TypeOfPtr((*float32)(nil)).Elem()] = &lossyFloat32Encoder{} + extension[reflect2.TypeOfPtr((*float64)(nil)).Elem()] = &lossyFloat64Encoder{} +} + +type htmlEscapedStringEncoder struct { +} + +func (encoder *htmlEscapedStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + str := *((*string)(ptr)) + stream.WriteStringWithHTMLEscaped(str) +} + +func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return *((*string)(ptr)) == "" +} + +func (cfg *frozenConfig) escapeHTML(encoderExtension EncoderExtension) { + encoderExtension[reflect2.TypeOfPtr((*string)(nil)).Elem()] = &htmlEscapedStringEncoder{} +} + +func (cfg *frozenConfig) cleanDecoders() { + typeDecoders = map[string]ValDecoder{} + fieldDecoders = map[string]ValDecoder{} + *cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig)) +} + +func (cfg *frozenConfig) cleanEncoders() { + typeEncoders = map[string]ValEncoder{} + fieldEncoders = map[string]ValEncoder{} + *cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig)) +} + +func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) { + stream := cfg.BorrowStream(nil) + defer cfg.ReturnStream(stream) + stream.WriteVal(v) + if stream.Error != nil { + return "", stream.Error + } + return string(stream.Buffer()), nil +} + +func (cfg *frozenConfig) Marshal(v interface{}) ([]byte, error) { + stream := cfg.BorrowStream(nil) + defer cfg.ReturnStream(stream) + stream.WriteVal(v) + if stream.Error != nil { + return nil, stream.Error + } + result := stream.Buffer() + copied := make([]byte, len(result)) + copy(copied, result) + return copied, nil +} + +func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { + if prefix != "" { + panic("prefix is not supported") + } + for _, r := range indent { + if r != ' ' { + panic("indent can only be space") + } + } + newCfg := cfg.configBeforeFrozen + newCfg.IndentionStep = len(indent) + return newCfg.frozeWithCacheReuse(cfg.extraExtensions).Marshal(v) +} + +func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error { + data := []byte(str) + iter := cfg.BorrowIterator(data) + defer cfg.ReturnIterator(iter) + iter.ReadVal(v) + c := iter.nextToken() + if c == 0 { + if iter.Error == io.EOF { + return nil + } + return iter.Error + } + iter.ReportError("Unmarshal", "there are bytes left after unmarshal") + return iter.Error +} + +func (cfg *frozenConfig) Get(data []byte, path ...interface{}) Any { + iter := cfg.BorrowIterator(data) + defer cfg.ReturnIterator(iter) + return locatePath(iter, path) +} + +func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error { + iter := cfg.BorrowIterator(data) + defer cfg.ReturnIterator(iter) + iter.ReadVal(v) + c := iter.nextToken() + if c == 0 { + if iter.Error == io.EOF { + return nil + } + return iter.Error + } + iter.ReportError("Unmarshal", "there are bytes left after unmarshal") + return iter.Error +} + +func (cfg *frozenConfig) NewEncoder(writer io.Writer) *Encoder { + stream := NewStream(cfg, writer, 512) + return &Encoder{stream} +} + +func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder { + iter := Parse(cfg, reader, 512) + return &Decoder{iter} +} + +func (cfg *frozenConfig) Valid(data []byte) bool { + iter := cfg.BorrowIterator(data) + defer cfg.ReturnIterator(iter) + iter.Skip() + return iter.Error == nil +} diff --git a/vendor/github.com/json-iterator/go/go.mod b/vendor/github.com/json-iterator/go/go.mod new file mode 100644 index 0000000000000..e05c42ff58b81 --- /dev/null +++ b/vendor/github.com/json-iterator/go/go.mod @@ -0,0 +1,11 @@ +module github.com/json-iterator/go + +go 1.12 + +require ( + github.com/davecgh/go-spew v1.1.1 + github.com/google/gofuzz v1.0.0 + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 + github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 + github.com/stretchr/testify v1.3.0 +) diff --git a/vendor/github.com/json-iterator/go/iter.go b/vendor/github.com/json-iterator/go/iter.go new file mode 100644 index 0000000000000..29b31cf789506 --- /dev/null +++ b/vendor/github.com/json-iterator/go/iter.go @@ -0,0 +1,349 @@ +package jsoniter + +import ( + "encoding/json" + "fmt" + "io" +) + +// ValueType the type for JSON element +type ValueType int + +const ( + // InvalidValue invalid JSON element + InvalidValue ValueType = iota + // StringValue JSON element "string" + StringValue + // NumberValue JSON element 100 or 0.10 + NumberValue + // NilValue JSON element null + NilValue + // BoolValue JSON element true or false + BoolValue + // ArrayValue JSON element [] + ArrayValue + // ObjectValue JSON element {} + ObjectValue +) + +var hexDigits []byte +var valueTypes []ValueType + +func init() { + hexDigits = make([]byte, 256) + for i := 0; i < len(hexDigits); i++ { + hexDigits[i] = 255 + } + for i := '0'; i <= '9'; i++ { + hexDigits[i] = byte(i - '0') + } + for i := 'a'; i <= 'f'; i++ { + hexDigits[i] = byte((i - 'a') + 10) + } + for i := 'A'; i <= 'F'; i++ { + hexDigits[i] = byte((i - 'A') + 10) + } + valueTypes = make([]ValueType, 256) + for i := 0; i < len(valueTypes); i++ { + valueTypes[i] = InvalidValue + } + valueTypes['"'] = StringValue + valueTypes['-'] = NumberValue + valueTypes['0'] = NumberValue + valueTypes['1'] = NumberValue + valueTypes['2'] = NumberValue + valueTypes['3'] = NumberValue + valueTypes['4'] = NumberValue + valueTypes['5'] = NumberValue + valueTypes['6'] = NumberValue + valueTypes['7'] = NumberValue + valueTypes['8'] = NumberValue + valueTypes['9'] = NumberValue + valueTypes['t'] = BoolValue + valueTypes['f'] = BoolValue + valueTypes['n'] = NilValue + valueTypes['['] = ArrayValue + valueTypes['{'] = ObjectValue +} + +// Iterator is a io.Reader like object, with JSON specific read functions. +// Error is not returned as return value, but stored as Error member on this iterator instance. +type Iterator struct { + cfg *frozenConfig + reader io.Reader + buf []byte + head int + tail int + depth int + captureStartedAt int + captured []byte + Error error + Attachment interface{} // open for customized decoder +} + +// NewIterator creates an empty Iterator instance +func NewIterator(cfg API) *Iterator { + return &Iterator{ + cfg: cfg.(*frozenConfig), + reader: nil, + buf: nil, + head: 0, + tail: 0, + depth: 0, + } +} + +// Parse creates an Iterator instance from io.Reader +func Parse(cfg API, reader io.Reader, bufSize int) *Iterator { + return &Iterator{ + cfg: cfg.(*frozenConfig), + reader: reader, + buf: make([]byte, bufSize), + head: 0, + tail: 0, + depth: 0, + } +} + +// ParseBytes creates an Iterator instance from byte array +func ParseBytes(cfg API, input []byte) *Iterator { + return &Iterator{ + cfg: cfg.(*frozenConfig), + reader: nil, + buf: input, + head: 0, + tail: len(input), + depth: 0, + } +} + +// ParseString creates an Iterator instance from string +func ParseString(cfg API, input string) *Iterator { + return ParseBytes(cfg, []byte(input)) +} + +// Pool returns a pool can provide more iterator with same configuration +func (iter *Iterator) Pool() IteratorPool { + return iter.cfg +} + +// Reset reuse iterator instance by specifying another reader +func (iter *Iterator) Reset(reader io.Reader) *Iterator { + iter.reader = reader + iter.head = 0 + iter.tail = 0 + iter.depth = 0 + return iter +} + +// ResetBytes reuse iterator instance by specifying another byte array as input +func (iter *Iterator) ResetBytes(input []byte) *Iterator { + iter.reader = nil + iter.buf = input + iter.head = 0 + iter.tail = len(input) + iter.depth = 0 + return iter +} + +// WhatIsNext gets ValueType of relatively next json element +func (iter *Iterator) WhatIsNext() ValueType { + valueType := valueTypes[iter.nextToken()] + iter.unreadByte() + return valueType +} + +func (iter *Iterator) skipWhitespacesWithoutLoadMore() bool { + for i := iter.head; i < iter.tail; i++ { + c := iter.buf[i] + switch c { + case ' ', '\n', '\t', '\r': + continue + } + iter.head = i + return false + } + return true +} + +func (iter *Iterator) isObjectEnd() bool { + c := iter.nextToken() + if c == ',' { + return false + } + if c == '}' { + return true + } + iter.ReportError("isObjectEnd", "object ended prematurely, unexpected char "+string([]byte{c})) + return true +} + +func (iter *Iterator) nextToken() byte { + // a variation of skip whitespaces, returning the next non-whitespace token + for { + for i := iter.head; i < iter.tail; i++ { + c := iter.buf[i] + switch c { + case ' ', '\n', '\t', '\r': + continue + } + iter.head = i + 1 + return c + } + if !iter.loadMore() { + return 0 + } + } +} + +// ReportError record a error in iterator instance with current position. +func (iter *Iterator) ReportError(operation string, msg string) { + if iter.Error != nil { + if iter.Error != io.EOF { + return + } + } + peekStart := iter.head - 10 + if peekStart < 0 { + peekStart = 0 + } + peekEnd := iter.head + 10 + if peekEnd > iter.tail { + peekEnd = iter.tail + } + parsing := string(iter.buf[peekStart:peekEnd]) + contextStart := iter.head - 50 + if contextStart < 0 { + contextStart = 0 + } + contextEnd := iter.head + 50 + if contextEnd > iter.tail { + contextEnd = iter.tail + } + context := string(iter.buf[contextStart:contextEnd]) + iter.Error = fmt.Errorf("%s: %s, error found in #%v byte of ...|%s|..., bigger context ...|%s|...", + operation, msg, iter.head-peekStart, parsing, context) +} + +// CurrentBuffer gets current buffer as string for debugging purpose +func (iter *Iterator) CurrentBuffer() string { + peekStart := iter.head - 10 + if peekStart < 0 { + peekStart = 0 + } + return fmt.Sprintf("parsing #%v byte, around ...|%s|..., whole buffer ...|%s|...", iter.head, + string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail])) +} + +func (iter *Iterator) readByte() (ret byte) { + if iter.head == iter.tail { + if iter.loadMore() { + ret = iter.buf[iter.head] + iter.head++ + return ret + } + return 0 + } + ret = iter.buf[iter.head] + iter.head++ + return ret +} + +func (iter *Iterator) loadMore() bool { + if iter.reader == nil { + if iter.Error == nil { + iter.head = iter.tail + iter.Error = io.EOF + } + return false + } + if iter.captured != nil { + iter.captured = append(iter.captured, + iter.buf[iter.captureStartedAt:iter.tail]...) + iter.captureStartedAt = 0 + } + for { + n, err := iter.reader.Read(iter.buf) + if n == 0 { + if err != nil { + if iter.Error == nil { + iter.Error = err + } + return false + } + } else { + iter.head = 0 + iter.tail = n + return true + } + } +} + +func (iter *Iterator) unreadByte() { + if iter.Error != nil { + return + } + iter.head-- + return +} + +// Read read the next JSON element as generic interface{}. +func (iter *Iterator) Read() interface{} { + valueType := iter.WhatIsNext() + switch valueType { + case StringValue: + return iter.ReadString() + case NumberValue: + if iter.cfg.configBeforeFrozen.UseNumber { + return json.Number(iter.readNumberAsString()) + } + return iter.ReadFloat64() + case NilValue: + iter.skipFourBytes('n', 'u', 'l', 'l') + return nil + case BoolValue: + return iter.ReadBool() + case ArrayValue: + arr := []interface{}{} + iter.ReadArrayCB(func(iter *Iterator) bool { + var elem interface{} + iter.ReadVal(&elem) + arr = append(arr, elem) + return true + }) + return arr + case ObjectValue: + obj := map[string]interface{}{} + iter.ReadMapCB(func(Iter *Iterator, field string) bool { + var elem interface{} + iter.ReadVal(&elem) + obj[field] = elem + return true + }) + return obj + default: + iter.ReportError("Read", fmt.Sprintf("unexpected value type: %v", valueType)) + return nil + } +} + +// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9 +const maxDepth = 10000 + +func (iter *Iterator) incrementDepth() (success bool) { + iter.depth++ + if iter.depth <= maxDepth { + return true + } + iter.ReportError("incrementDepth", "exceeded max depth") + return false +} + +func (iter *Iterator) decrementDepth() (success bool) { + iter.depth-- + if iter.depth >= 0 { + return true + } + iter.ReportError("decrementDepth", "unexpected negative nesting") + return false +} diff --git a/vendor/github.com/json-iterator/go/iter_array.go b/vendor/github.com/json-iterator/go/iter_array.go new file mode 100644 index 0000000000000..204fe0e0922aa --- /dev/null +++ b/vendor/github.com/json-iterator/go/iter_array.go @@ -0,0 +1,64 @@ +package jsoniter + +// ReadArray read array element, tells if the array has more element to read. +func (iter *Iterator) ReadArray() (ret bool) { + c := iter.nextToken() + switch c { + case 'n': + iter.skipThreeBytes('u', 'l', 'l') + return false // null + case '[': + c = iter.nextToken() + if c != ']' { + iter.unreadByte() + return true + } + return false + case ']': + return false + case ',': + return true + default: + iter.ReportError("ReadArray", "expect [ or , or ] or n, but found "+string([]byte{c})) + return + } +} + +// ReadArrayCB read array with callback +func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) { + c := iter.nextToken() + if c == '[' { + if !iter.incrementDepth() { + return false + } + c = iter.nextToken() + if c != ']' { + iter.unreadByte() + if !callback(iter) { + iter.decrementDepth() + return false + } + c = iter.nextToken() + for c == ',' { + if !callback(iter) { + iter.decrementDepth() + return false + } + c = iter.nextToken() + } + if c != ']' { + iter.ReportError("ReadArrayCB", "expect ] in the end, but found "+string([]byte{c})) + iter.decrementDepth() + return false + } + return iter.decrementDepth() + } + return iter.decrementDepth() + } + if c == 'n' { + iter.skipThreeBytes('u', 'l', 'l') + return true // null + } + iter.ReportError("ReadArrayCB", "expect [ or n, but found "+string([]byte{c})) + return false +} diff --git a/vendor/github.com/json-iterator/go/iter_float.go b/vendor/github.com/json-iterator/go/iter_float.go new file mode 100644 index 0000000000000..b9754638e8886 --- /dev/null +++ b/vendor/github.com/json-iterator/go/iter_float.go @@ -0,0 +1,339 @@ +package jsoniter + +import ( + "encoding/json" + "io" + "math/big" + "strconv" + "strings" + "unsafe" +) + +var floatDigits []int8 + +const invalidCharForNumber = int8(-1) +const endOfNumber = int8(-2) +const dotInNumber = int8(-3) + +func init() { + floatDigits = make([]int8, 256) + for i := 0; i < len(floatDigits); i++ { + floatDigits[i] = invalidCharForNumber + } + for i := int8('0'); i <= int8('9'); i++ { + floatDigits[i] = i - int8('0') + } + floatDigits[','] = endOfNumber + floatDigits[']'] = endOfNumber + floatDigits['}'] = endOfNumber + floatDigits[' '] = endOfNumber + floatDigits['\t'] = endOfNumber + floatDigits['\n'] = endOfNumber + floatDigits['.'] = dotInNumber +} + +// ReadBigFloat read big.Float +func (iter *Iterator) ReadBigFloat() (ret *big.Float) { + str := iter.readNumberAsString() + if iter.Error != nil && iter.Error != io.EOF { + return nil + } + prec := 64 + if len(str) > prec { + prec = len(str) + } + val, _, err := big.ParseFloat(str, 10, uint(prec), big.ToZero) + if err != nil { + iter.Error = err + return nil + } + return val +} + +// ReadBigInt read big.Int +func (iter *Iterator) ReadBigInt() (ret *big.Int) { + str := iter.readNumberAsString() + if iter.Error != nil && iter.Error != io.EOF { + return nil + } + ret = big.NewInt(0) + var success bool + ret, success = ret.SetString(str, 10) + if !success { + iter.ReportError("ReadBigInt", "invalid big int") + return nil + } + return ret +} + +//ReadFloat32 read float32 +func (iter *Iterator) ReadFloat32() (ret float32) { + c := iter.nextToken() + if c == '-' { + return -iter.readPositiveFloat32() + } + iter.unreadByte() + return iter.readPositiveFloat32() +} + +func (iter *Iterator) readPositiveFloat32() (ret float32) { + i := iter.head + // first char + if i == iter.tail { + return iter.readFloat32SlowPath() + } + c := iter.buf[i] + i++ + ind := floatDigits[c] + switch ind { + case invalidCharForNumber: + return iter.readFloat32SlowPath() + case endOfNumber: + iter.ReportError("readFloat32", "empty number") + return + case dotInNumber: + iter.ReportError("readFloat32", "leading dot is invalid") + return + case 0: + if i == iter.tail { + return iter.readFloat32SlowPath() + } + c = iter.buf[i] + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + iter.ReportError("readFloat32", "leading zero is invalid") + return + } + } + value := uint64(ind) + // chars before dot +non_decimal_loop: + for ; i < iter.tail; i++ { + c = iter.buf[i] + ind := floatDigits[c] + switch ind { + case invalidCharForNumber: + return iter.readFloat32SlowPath() + case endOfNumber: + iter.head = i + return float32(value) + case dotInNumber: + break non_decimal_loop + } + if value > uint64SafeToMultiple10 { + return iter.readFloat32SlowPath() + } + value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind; + } + // chars after dot + if c == '.' { + i++ + decimalPlaces := 0 + if i == iter.tail { + return iter.readFloat32SlowPath() + } + for ; i < iter.tail; i++ { + c = iter.buf[i] + ind := floatDigits[c] + switch ind { + case endOfNumber: + if decimalPlaces > 0 && decimalPlaces < len(pow10) { + iter.head = i + return float32(float64(value) / float64(pow10[decimalPlaces])) + } + // too many decimal places + return iter.readFloat32SlowPath() + case invalidCharForNumber, dotInNumber: + return iter.readFloat32SlowPath() + } + decimalPlaces++ + if value > uint64SafeToMultiple10 { + return iter.readFloat32SlowPath() + } + value = (value << 3) + (value << 1) + uint64(ind) + } + } + return iter.readFloat32SlowPath() +} + +func (iter *Iterator) readNumberAsString() (ret string) { + strBuf := [16]byte{} + str := strBuf[0:0] +load_loop: + for { + for i := iter.head; i < iter.tail; i++ { + c := iter.buf[i] + switch c { + case '+', '-', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + str = append(str, c) + continue + default: + iter.head = i + break load_loop + } + } + if !iter.loadMore() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF { + return + } + if len(str) == 0 { + iter.ReportError("readNumberAsString", "invalid number") + } + return *(*string)(unsafe.Pointer(&str)) +} + +func (iter *Iterator) readFloat32SlowPath() (ret float32) { + str := iter.readNumberAsString() + if iter.Error != nil && iter.Error != io.EOF { + return + } + errMsg := validateFloat(str) + if errMsg != "" { + iter.ReportError("readFloat32SlowPath", errMsg) + return + } + val, err := strconv.ParseFloat(str, 32) + if err != nil { + iter.Error = err + return + } + return float32(val) +} + +// ReadFloat64 read float64 +func (iter *Iterator) ReadFloat64() (ret float64) { + c := iter.nextToken() + if c == '-' { + return -iter.readPositiveFloat64() + } + iter.unreadByte() + return iter.readPositiveFloat64() +} + +func (iter *Iterator) readPositiveFloat64() (ret float64) { + i := iter.head + // first char + if i == iter.tail { + return iter.readFloat64SlowPath() + } + c := iter.buf[i] + i++ + ind := floatDigits[c] + switch ind { + case invalidCharForNumber: + return iter.readFloat64SlowPath() + case endOfNumber: + iter.ReportError("readFloat64", "empty number") + return + case dotInNumber: + iter.ReportError("readFloat64", "leading dot is invalid") + return + case 0: + if i == iter.tail { + return iter.readFloat64SlowPath() + } + c = iter.buf[i] + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + iter.ReportError("readFloat64", "leading zero is invalid") + return + } + } + value := uint64(ind) + // chars before dot +non_decimal_loop: + for ; i < iter.tail; i++ { + c = iter.buf[i] + ind := floatDigits[c] + switch ind { + case invalidCharForNumber: + return iter.readFloat64SlowPath() + case endOfNumber: + iter.head = i + return float64(value) + case dotInNumber: + break non_decimal_loop + } + if value > uint64SafeToMultiple10 { + return iter.readFloat64SlowPath() + } + value = (value << 3) + (value << 1) + uint64(ind) // value = value * 10 + ind; + } + // chars after dot + if c == '.' { + i++ + decimalPlaces := 0 + if i == iter.tail { + return iter.readFloat64SlowPath() + } + for ; i < iter.tail; i++ { + c = iter.buf[i] + ind := floatDigits[c] + switch ind { + case endOfNumber: + if decimalPlaces > 0 && decimalPlaces < len(pow10) { + iter.head = i + return float64(value) / float64(pow10[decimalPlaces]) + } + // too many decimal places + return iter.readFloat64SlowPath() + case invalidCharForNumber, dotInNumber: + return iter.readFloat64SlowPath() + } + decimalPlaces++ + if value > uint64SafeToMultiple10 { + return iter.readFloat64SlowPath() + } + value = (value << 3) + (value << 1) + uint64(ind) + } + } + return iter.readFloat64SlowPath() +} + +func (iter *Iterator) readFloat64SlowPath() (ret float64) { + str := iter.readNumberAsString() + if iter.Error != nil && iter.Error != io.EOF { + return + } + errMsg := validateFloat(str) + if errMsg != "" { + iter.ReportError("readFloat64SlowPath", errMsg) + return + } + val, err := strconv.ParseFloat(str, 64) + if err != nil { + iter.Error = err + return + } + return val +} + +func validateFloat(str string) string { + // strconv.ParseFloat is not validating `1.` or `1.e1` + if len(str) == 0 { + return "empty number" + } + if str[0] == '-' { + return "-- is not valid" + } + dotPos := strings.IndexByte(str, '.') + if dotPos != -1 { + if dotPos == len(str)-1 { + return "dot can not be last character" + } + switch str[dotPos+1] { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + default: + return "missing digit after dot" + } + } + return "" +} + +// ReadNumber read json.Number +func (iter *Iterator) ReadNumber() (ret json.Number) { + return json.Number(iter.readNumberAsString()) +} diff --git a/vendor/github.com/json-iterator/go/iter_int.go b/vendor/github.com/json-iterator/go/iter_int.go new file mode 100644 index 0000000000000..2142320355e87 --- /dev/null +++ b/vendor/github.com/json-iterator/go/iter_int.go @@ -0,0 +1,345 @@ +package jsoniter + +import ( + "math" + "strconv" +) + +var intDigits []int8 + +const uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1 +const uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 1 + +func init() { + intDigits = make([]int8, 256) + for i := 0; i < len(intDigits); i++ { + intDigits[i] = invalidCharForNumber + } + for i := int8('0'); i <= int8('9'); i++ { + intDigits[i] = i - int8('0') + } +} + +// ReadUint read uint +func (iter *Iterator) ReadUint() uint { + if strconv.IntSize == 32 { + return uint(iter.ReadUint32()) + } + return uint(iter.ReadUint64()) +} + +// ReadInt read int +func (iter *Iterator) ReadInt() int { + if strconv.IntSize == 32 { + return int(iter.ReadInt32()) + } + return int(iter.ReadInt64()) +} + +// ReadInt8 read int8 +func (iter *Iterator) ReadInt8() (ret int8) { + c := iter.nextToken() + if c == '-' { + val := iter.readUint32(iter.readByte()) + if val > math.MaxInt8+1 { + iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10)) + return + } + return -int8(val) + } + val := iter.readUint32(c) + if val > math.MaxInt8 { + iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10)) + return + } + return int8(val) +} + +// ReadUint8 read uint8 +func (iter *Iterator) ReadUint8() (ret uint8) { + val := iter.readUint32(iter.nextToken()) + if val > math.MaxUint8 { + iter.ReportError("ReadUint8", "overflow: "+strconv.FormatInt(int64(val), 10)) + return + } + return uint8(val) +} + +// ReadInt16 read int16 +func (iter *Iterator) ReadInt16() (ret int16) { + c := iter.nextToken() + if c == '-' { + val := iter.readUint32(iter.readByte()) + if val > math.MaxInt16+1 { + iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10)) + return + } + return -int16(val) + } + val := iter.readUint32(c) + if val > math.MaxInt16 { + iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10)) + return + } + return int16(val) +} + +// ReadUint16 read uint16 +func (iter *Iterator) ReadUint16() (ret uint16) { + val := iter.readUint32(iter.nextToken()) + if val > math.MaxUint16 { + iter.ReportError("ReadUint16", "overflow: "+strconv.FormatInt(int64(val), 10)) + return + } + return uint16(val) +} + +// ReadInt32 read int32 +func (iter *Iterator) ReadInt32() (ret int32) { + c := iter.nextToken() + if c == '-' { + val := iter.readUint32(iter.readByte()) + if val > math.MaxInt32+1 { + iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10)) + return + } + return -int32(val) + } + val := iter.readUint32(c) + if val > math.MaxInt32 { + iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10)) + return + } + return int32(val) +} + +// ReadUint32 read uint32 +func (iter *Iterator) ReadUint32() (ret uint32) { + return iter.readUint32(iter.nextToken()) +} + +func (iter *Iterator) readUint32(c byte) (ret uint32) { + ind := intDigits[c] + if ind == 0 { + iter.assertInteger() + return 0 // single zero + } + if ind == invalidCharForNumber { + iter.ReportError("readUint32", "unexpected character: "+string([]byte{byte(ind)})) + return + } + value := uint32(ind) + if iter.tail-iter.head > 10 { + i := iter.head + ind2 := intDigits[iter.buf[i]] + if ind2 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value + } + i++ + ind3 := intDigits[iter.buf[i]] + if ind3 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*10 + uint32(ind2) + } + //iter.head = i + 1 + //value = value * 100 + uint32(ind2) * 10 + uint32(ind3) + i++ + ind4 := intDigits[iter.buf[i]] + if ind4 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*100 + uint32(ind2)*10 + uint32(ind3) + } + i++ + ind5 := intDigits[iter.buf[i]] + if ind5 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*1000 + uint32(ind2)*100 + uint32(ind3)*10 + uint32(ind4) + } + i++ + ind6 := intDigits[iter.buf[i]] + if ind6 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*10000 + uint32(ind2)*1000 + uint32(ind3)*100 + uint32(ind4)*10 + uint32(ind5) + } + i++ + ind7 := intDigits[iter.buf[i]] + if ind7 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*100000 + uint32(ind2)*10000 + uint32(ind3)*1000 + uint32(ind4)*100 + uint32(ind5)*10 + uint32(ind6) + } + i++ + ind8 := intDigits[iter.buf[i]] + if ind8 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*1000000 + uint32(ind2)*100000 + uint32(ind3)*10000 + uint32(ind4)*1000 + uint32(ind5)*100 + uint32(ind6)*10 + uint32(ind7) + } + i++ + ind9 := intDigits[iter.buf[i]] + value = value*10000000 + uint32(ind2)*1000000 + uint32(ind3)*100000 + uint32(ind4)*10000 + uint32(ind5)*1000 + uint32(ind6)*100 + uint32(ind7)*10 + uint32(ind8) + iter.head = i + if ind9 == invalidCharForNumber { + iter.assertInteger() + return value + } + } + for { + for i := iter.head; i < iter.tail; i++ { + ind = intDigits[iter.buf[i]] + if ind == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value + } + if value > uint32SafeToMultiply10 { + value2 := (value << 3) + (value << 1) + uint32(ind) + if value2 < value { + iter.ReportError("readUint32", "overflow") + return + } + value = value2 + continue + } + value = (value << 3) + (value << 1) + uint32(ind) + } + if !iter.loadMore() { + iter.assertInteger() + return value + } + } +} + +// ReadInt64 read int64 +func (iter *Iterator) ReadInt64() (ret int64) { + c := iter.nextToken() + if c == '-' { + val := iter.readUint64(iter.readByte()) + if val > math.MaxInt64+1 { + iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10)) + return + } + return -int64(val) + } + val := iter.readUint64(c) + if val > math.MaxInt64 { + iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10)) + return + } + return int64(val) +} + +// ReadUint64 read uint64 +func (iter *Iterator) ReadUint64() uint64 { + return iter.readUint64(iter.nextToken()) +} + +func (iter *Iterator) readUint64(c byte) (ret uint64) { + ind := intDigits[c] + if ind == 0 { + iter.assertInteger() + return 0 // single zero + } + if ind == invalidCharForNumber { + iter.ReportError("readUint64", "unexpected character: "+string([]byte{byte(ind)})) + return + } + value := uint64(ind) + if iter.tail-iter.head > 10 { + i := iter.head + ind2 := intDigits[iter.buf[i]] + if ind2 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value + } + i++ + ind3 := intDigits[iter.buf[i]] + if ind3 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*10 + uint64(ind2) + } + //iter.head = i + 1 + //value = value * 100 + uint32(ind2) * 10 + uint32(ind3) + i++ + ind4 := intDigits[iter.buf[i]] + if ind4 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*100 + uint64(ind2)*10 + uint64(ind3) + } + i++ + ind5 := intDigits[iter.buf[i]] + if ind5 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*1000 + uint64(ind2)*100 + uint64(ind3)*10 + uint64(ind4) + } + i++ + ind6 := intDigits[iter.buf[i]] + if ind6 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*10000 + uint64(ind2)*1000 + uint64(ind3)*100 + uint64(ind4)*10 + uint64(ind5) + } + i++ + ind7 := intDigits[iter.buf[i]] + if ind7 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*100000 + uint64(ind2)*10000 + uint64(ind3)*1000 + uint64(ind4)*100 + uint64(ind5)*10 + uint64(ind6) + } + i++ + ind8 := intDigits[iter.buf[i]] + if ind8 == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value*1000000 + uint64(ind2)*100000 + uint64(ind3)*10000 + uint64(ind4)*1000 + uint64(ind5)*100 + uint64(ind6)*10 + uint64(ind7) + } + i++ + ind9 := intDigits[iter.buf[i]] + value = value*10000000 + uint64(ind2)*1000000 + uint64(ind3)*100000 + uint64(ind4)*10000 + uint64(ind5)*1000 + uint64(ind6)*100 + uint64(ind7)*10 + uint64(ind8) + iter.head = i + if ind9 == invalidCharForNumber { + iter.assertInteger() + return value + } + } + for { + for i := iter.head; i < iter.tail; i++ { + ind = intDigits[iter.buf[i]] + if ind == invalidCharForNumber { + iter.head = i + iter.assertInteger() + return value + } + if value > uint64SafeToMultiple10 { + value2 := (value << 3) + (value << 1) + uint64(ind) + if value2 < value { + iter.ReportError("readUint64", "overflow") + return + } + value = value2 + continue + } + value = (value << 3) + (value << 1) + uint64(ind) + } + if !iter.loadMore() { + iter.assertInteger() + return value + } + } +} + +func (iter *Iterator) assertInteger() { + if iter.head < len(iter.buf) && iter.buf[iter.head] == '.' { + iter.ReportError("assertInteger", "can not decode float as int") + } +} diff --git a/vendor/github.com/json-iterator/go/iter_object.go b/vendor/github.com/json-iterator/go/iter_object.go new file mode 100644 index 0000000000000..58ee89c849e7b --- /dev/null +++ b/vendor/github.com/json-iterator/go/iter_object.go @@ -0,0 +1,267 @@ +package jsoniter + +import ( + "fmt" + "strings" +) + +// ReadObject read one field from object. +// If object ended, returns empty string. +// Otherwise, returns the field name. +func (iter *Iterator) ReadObject() (ret string) { + c := iter.nextToken() + switch c { + case 'n': + iter.skipThreeBytes('u', 'l', 'l') + return "" // null + case '{': + c = iter.nextToken() + if c == '"' { + iter.unreadByte() + field := iter.ReadString() + c = iter.nextToken() + if c != ':' { + iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) + } + return field + } + if c == '}' { + return "" // end of object + } + iter.ReportError("ReadObject", `expect " after {, but found `+string([]byte{c})) + return + case ',': + field := iter.ReadString() + c = iter.nextToken() + if c != ':' { + iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) + } + return field + case '}': + return "" // end of object + default: + iter.ReportError("ReadObject", fmt.Sprintf(`expect { or , or } or n, but found %s`, string([]byte{c}))) + return + } +} + +// CaseInsensitive +func (iter *Iterator) readFieldHash() int64 { + hash := int64(0x811c9dc5) + c := iter.nextToken() + if c != '"' { + iter.ReportError("readFieldHash", `expect ", but found `+string([]byte{c})) + return 0 + } + for { + for i := iter.head; i < iter.tail; i++ { + // require ascii string and no escape + b := iter.buf[i] + if b == '\\' { + iter.head = i + for _, b := range iter.readStringSlowPath() { + if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive { + b += 'a' - 'A' + } + hash ^= int64(b) + hash *= 0x1000193 + } + c = iter.nextToken() + if c != ':' { + iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c})) + return 0 + } + return hash + } + if b == '"' { + iter.head = i + 1 + c = iter.nextToken() + if c != ':' { + iter.ReportError("readFieldHash", `expect :, but found `+string([]byte{c})) + return 0 + } + return hash + } + if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive { + b += 'a' - 'A' + } + hash ^= int64(b) + hash *= 0x1000193 + } + if !iter.loadMore() { + iter.ReportError("readFieldHash", `incomplete field name`) + return 0 + } + } +} + +func calcHash(str string, caseSensitive bool) int64 { + if !caseSensitive { + str = strings.ToLower(str) + } + hash := int64(0x811c9dc5) + for _, b := range []byte(str) { + hash ^= int64(b) + hash *= 0x1000193 + } + return int64(hash) +} + +// ReadObjectCB read object with callback, the key is ascii only and field name not copied +func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool { + c := iter.nextToken() + var field string + if c == '{' { + if !iter.incrementDepth() { + return false + } + c = iter.nextToken() + if c == '"' { + iter.unreadByte() + field = iter.ReadString() + c = iter.nextToken() + if c != ':' { + iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) + } + if !callback(iter, field) { + iter.decrementDepth() + return false + } + c = iter.nextToken() + for c == ',' { + field = iter.ReadString() + c = iter.nextToken() + if c != ':' { + iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) + } + if !callback(iter, field) { + iter.decrementDepth() + return false + } + c = iter.nextToken() + } + if c != '}' { + iter.ReportError("ReadObjectCB", `object not ended with }`) + iter.decrementDepth() + return false + } + return iter.decrementDepth() + } + if c == '}' { + return iter.decrementDepth() + } + iter.ReportError("ReadObjectCB", `expect " after {, but found `+string([]byte{c})) + iter.decrementDepth() + return false + } + if c == 'n' { + iter.skipThreeBytes('u', 'l', 'l') + return true // null + } + iter.ReportError("ReadObjectCB", `expect { or n, but found `+string([]byte{c})) + return false +} + +// ReadMapCB read map with callback, the key can be any string +func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool { + c := iter.nextToken() + if c == '{' { + if !iter.incrementDepth() { + return false + } + c = iter.nextToken() + if c == '"' { + iter.unreadByte() + field := iter.ReadString() + if iter.nextToken() != ':' { + iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c})) + iter.decrementDepth() + return false + } + if !callback(iter, field) { + iter.decrementDepth() + return false + } + c = iter.nextToken() + for c == ',' { + field = iter.ReadString() + if iter.nextToken() != ':' { + iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c})) + iter.decrementDepth() + return false + } + if !callback(iter, field) { + iter.decrementDepth() + return false + } + c = iter.nextToken() + } + if c != '}' { + iter.ReportError("ReadMapCB", `object not ended with }`) + iter.decrementDepth() + return false + } + return iter.decrementDepth() + } + if c == '}' { + return iter.decrementDepth() + } + iter.ReportError("ReadMapCB", `expect " after {, but found `+string([]byte{c})) + iter.decrementDepth() + return false + } + if c == 'n' { + iter.skipThreeBytes('u', 'l', 'l') + return true // null + } + iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c})) + return false +} + +func (iter *Iterator) readObjectStart() bool { + c := iter.nextToken() + if c == '{' { + c = iter.nextToken() + if c == '}' { + return false + } + iter.unreadByte() + return true + } else if c == 'n' { + iter.skipThreeBytes('u', 'l', 'l') + return false + } + iter.ReportError("readObjectStart", "expect { or n, but found "+string([]byte{c})) + return false +} + +func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) { + str := iter.ReadStringAsSlice() + if iter.skipWhitespacesWithoutLoadMore() { + if ret == nil { + ret = make([]byte, len(str)) + copy(ret, str) + } + if !iter.loadMore() { + return + } + } + if iter.buf[iter.head] != ':' { + iter.ReportError("readObjectFieldAsBytes", "expect : after object field, but found "+string([]byte{iter.buf[iter.head]})) + return + } + iter.head++ + if iter.skipWhitespacesWithoutLoadMore() { + if ret == nil { + ret = make([]byte, len(str)) + copy(ret, str) + } + if !iter.loadMore() { + return + } + } + if ret == nil { + return str + } + return ret +} diff --git a/vendor/github.com/json-iterator/go/iter_skip.go b/vendor/github.com/json-iterator/go/iter_skip.go new file mode 100644 index 0000000000000..e91eefb15becf --- /dev/null +++ b/vendor/github.com/json-iterator/go/iter_skip.go @@ -0,0 +1,130 @@ +package jsoniter + +import "fmt" + +// ReadNil reads a json object as nil and +// returns whether it's a nil or not +func (iter *Iterator) ReadNil() (ret bool) { + c := iter.nextToken() + if c == 'n' { + iter.skipThreeBytes('u', 'l', 'l') // null + return true + } + iter.unreadByte() + return false +} + +// ReadBool reads a json object as BoolValue +func (iter *Iterator) ReadBool() (ret bool) { + c := iter.nextToken() + if c == 't' { + iter.skipThreeBytes('r', 'u', 'e') + return true + } + if c == 'f' { + iter.skipFourBytes('a', 'l', 's', 'e') + return false + } + iter.ReportError("ReadBool", "expect t or f, but found "+string([]byte{c})) + return +} + +// SkipAndReturnBytes skip next JSON element, and return its content as []byte. +// The []byte can be kept, it is a copy of data. +func (iter *Iterator) SkipAndReturnBytes() []byte { + iter.startCapture(iter.head) + iter.Skip() + return iter.stopCapture() +} + +// SkipAndAppendBytes skips next JSON element and appends its content to +// buffer, returning the result. +func (iter *Iterator) SkipAndAppendBytes(buf []byte) []byte { + iter.startCaptureTo(buf, iter.head) + iter.Skip() + return iter.stopCapture() +} + +func (iter *Iterator) startCaptureTo(buf []byte, captureStartedAt int) { + if iter.captured != nil { + panic("already in capture mode") + } + iter.captureStartedAt = captureStartedAt + iter.captured = buf +} + +func (iter *Iterator) startCapture(captureStartedAt int) { + iter.startCaptureTo(make([]byte, 0, 32), captureStartedAt) +} + +func (iter *Iterator) stopCapture() []byte { + if iter.captured == nil { + panic("not in capture mode") + } + captured := iter.captured + remaining := iter.buf[iter.captureStartedAt:iter.head] + iter.captureStartedAt = -1 + iter.captured = nil + return append(captured, remaining...) +} + +// Skip skips a json object and positions to relatively the next json object +func (iter *Iterator) Skip() { + c := iter.nextToken() + switch c { + case '"': + iter.skipString() + case 'n': + iter.skipThreeBytes('u', 'l', 'l') // null + case 't': + iter.skipThreeBytes('r', 'u', 'e') // true + case 'f': + iter.skipFourBytes('a', 'l', 's', 'e') // false + case '0': + iter.unreadByte() + iter.ReadFloat32() + case '-', '1', '2', '3', '4', '5', '6', '7', '8', '9': + iter.skipNumber() + case '[': + iter.skipArray() + case '{': + iter.skipObject() + default: + iter.ReportError("Skip", fmt.Sprintf("do not know how to skip: %v", c)) + return + } +} + +func (iter *Iterator) skipFourBytes(b1, b2, b3, b4 byte) { + if iter.readByte() != b1 { + iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) + return + } + if iter.readByte() != b2 { + iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) + return + } + if iter.readByte() != b3 { + iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) + return + } + if iter.readByte() != b4 { + iter.ReportError("skipFourBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3, b4}))) + return + } +} + +func (iter *Iterator) skipThreeBytes(b1, b2, b3 byte) { + if iter.readByte() != b1 { + iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3}))) + return + } + if iter.readByte() != b2 { + iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3}))) + return + } + if iter.readByte() != b3 { + iter.ReportError("skipThreeBytes", fmt.Sprintf("expect %s", string([]byte{b1, b2, b3}))) + return + } +} diff --git a/vendor/github.com/json-iterator/go/iter_skip_sloppy.go b/vendor/github.com/json-iterator/go/iter_skip_sloppy.go new file mode 100644 index 0000000000000..9303de41e4005 --- /dev/null +++ b/vendor/github.com/json-iterator/go/iter_skip_sloppy.go @@ -0,0 +1,163 @@ +//+build jsoniter_sloppy + +package jsoniter + +// sloppy but faster implementation, do not validate the input json + +func (iter *Iterator) skipNumber() { + for { + for i := iter.head; i < iter.tail; i++ { + c := iter.buf[i] + switch c { + case ' ', '\n', '\r', '\t', ',', '}', ']': + iter.head = i + return + } + } + if !iter.loadMore() { + return + } + } +} + +func (iter *Iterator) skipArray() { + level := 1 + if !iter.incrementDepth() { + return + } + for { + for i := iter.head; i < iter.tail; i++ { + switch iter.buf[i] { + case '"': // If inside string, skip it + iter.head = i + 1 + iter.skipString() + i = iter.head - 1 // it will be i++ soon + case '[': // If open symbol, increase level + level++ + if !iter.incrementDepth() { + return + } + case ']': // If close symbol, increase level + level-- + if !iter.decrementDepth() { + return + } + + // If we have returned to the original level, we're done + if level == 0 { + iter.head = i + 1 + return + } + } + } + if !iter.loadMore() { + iter.ReportError("skipObject", "incomplete array") + return + } + } +} + +func (iter *Iterator) skipObject() { + level := 1 + if !iter.incrementDepth() { + return + } + + for { + for i := iter.head; i < iter.tail; i++ { + switch iter.buf[i] { + case '"': // If inside string, skip it + iter.head = i + 1 + iter.skipString() + i = iter.head - 1 // it will be i++ soon + case '{': // If open symbol, increase level + level++ + if !iter.incrementDepth() { + return + } + case '}': // If close symbol, increase level + level-- + if !iter.decrementDepth() { + return + } + + // If we have returned to the original level, we're done + if level == 0 { + iter.head = i + 1 + return + } + } + } + if !iter.loadMore() { + iter.ReportError("skipObject", "incomplete object") + return + } + } +} + +func (iter *Iterator) skipString() { + for { + end, escaped := iter.findStringEnd() + if end == -1 { + if !iter.loadMore() { + iter.ReportError("skipString", "incomplete string") + return + } + if escaped { + iter.head = 1 // skip the first char as last char read is \ + } + } else { + iter.head = end + return + } + } +} + +// adapted from: https://github.com/buger/jsonparser/blob/master/parser.go +// Tries to find the end of string +// Support if string contains escaped quote symbols. +func (iter *Iterator) findStringEnd() (int, bool) { + escaped := false + for i := iter.head; i < iter.tail; i++ { + c := iter.buf[i] + if c == '"' { + if !escaped { + return i + 1, false + } + j := i - 1 + for { + if j < iter.head || iter.buf[j] != '\\' { + // even number of backslashes + // either end of buffer, or " found + return i + 1, true + } + j-- + if j < iter.head || iter.buf[j] != '\\' { + // odd number of backslashes + // it is \" or \\\" + break + } + j-- + } + } else if c == '\\' { + escaped = true + } + } + j := iter.tail - 1 + for { + if j < iter.head || iter.buf[j] != '\\' { + // even number of backslashes + // either end of buffer, or " found + return -1, false // do not end with \ + } + j-- + if j < iter.head || iter.buf[j] != '\\' { + // odd number of backslashes + // it is \" or \\\" + break + } + j-- + + } + return -1, true // end with \ +} diff --git a/vendor/github.com/json-iterator/go/iter_skip_strict.go b/vendor/github.com/json-iterator/go/iter_skip_strict.go new file mode 100644 index 0000000000000..6cf66d0438dbe --- /dev/null +++ b/vendor/github.com/json-iterator/go/iter_skip_strict.go @@ -0,0 +1,99 @@ +//+build !jsoniter_sloppy + +package jsoniter + +import ( + "fmt" + "io" +) + +func (iter *Iterator) skipNumber() { + if !iter.trySkipNumber() { + iter.unreadByte() + if iter.Error != nil && iter.Error != io.EOF { + return + } + iter.ReadFloat64() + if iter.Error != nil && iter.Error != io.EOF { + iter.Error = nil + iter.ReadBigFloat() + } + } +} + +func (iter *Iterator) trySkipNumber() bool { + dotFound := false + for i := iter.head; i < iter.tail; i++ { + c := iter.buf[i] + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + case '.': + if dotFound { + iter.ReportError("validateNumber", `more than one dot found in number`) + return true // already failed + } + if i+1 == iter.tail { + return false + } + c = iter.buf[i+1] + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + default: + iter.ReportError("validateNumber", `missing digit after dot`) + return true // already failed + } + dotFound = true + default: + switch c { + case ',', ']', '}', ' ', '\t', '\n', '\r': + if iter.head == i { + return false // if - without following digits + } + iter.head = i + return true // must be valid + } + return false // may be invalid + } + } + return false +} + +func (iter *Iterator) skipString() { + if !iter.trySkipString() { + iter.unreadByte() + iter.ReadString() + } +} + +func (iter *Iterator) trySkipString() bool { + for i := iter.head; i < iter.tail; i++ { + c := iter.buf[i] + if c == '"' { + iter.head = i + 1 + return true // valid + } else if c == '\\' { + return false + } else if c < ' ' { + iter.ReportError("trySkipString", + fmt.Sprintf(`invalid control character found: %d`, c)) + return true // already failed + } + } + return false +} + +func (iter *Iterator) skipObject() { + iter.unreadByte() + iter.ReadObjectCB(func(iter *Iterator, field string) bool { + iter.Skip() + return true + }) +} + +func (iter *Iterator) skipArray() { + iter.unreadByte() + iter.ReadArrayCB(func(iter *Iterator) bool { + iter.Skip() + return true + }) +} diff --git a/vendor/github.com/json-iterator/go/iter_str.go b/vendor/github.com/json-iterator/go/iter_str.go new file mode 100644 index 0000000000000..adc487ea80483 --- /dev/null +++ b/vendor/github.com/json-iterator/go/iter_str.go @@ -0,0 +1,215 @@ +package jsoniter + +import ( + "fmt" + "unicode/utf16" +) + +// ReadString read string from iterator +func (iter *Iterator) ReadString() (ret string) { + c := iter.nextToken() + if c == '"' { + for i := iter.head; i < iter.tail; i++ { + c := iter.buf[i] + if c == '"' { + ret = string(iter.buf[iter.head:i]) + iter.head = i + 1 + return ret + } else if c == '\\' { + break + } else if c < ' ' { + iter.ReportError("ReadString", + fmt.Sprintf(`invalid control character found: %d`, c)) + return + } + } + return iter.readStringSlowPath() + } else if c == 'n' { + iter.skipThreeBytes('u', 'l', 'l') + return "" + } + iter.ReportError("ReadString", `expects " or n, but found `+string([]byte{c})) + return +} + +func (iter *Iterator) readStringSlowPath() (ret string) { + var str []byte + var c byte + for iter.Error == nil { + c = iter.readByte() + if c == '"' { + return string(str) + } + if c == '\\' { + c = iter.readByte() + str = iter.readEscapedChar(c, str) + } else { + str = append(str, c) + } + } + iter.ReportError("readStringSlowPath", "unexpected end of input") + return +} + +func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte { + switch c { + case 'u': + r := iter.readU4() + if utf16.IsSurrogate(r) { + c = iter.readByte() + if iter.Error != nil { + return nil + } + if c != '\\' { + iter.unreadByte() + str = appendRune(str, r) + return str + } + c = iter.readByte() + if iter.Error != nil { + return nil + } + if c != 'u' { + str = appendRune(str, r) + return iter.readEscapedChar(c, str) + } + r2 := iter.readU4() + if iter.Error != nil { + return nil + } + combined := utf16.DecodeRune(r, r2) + if combined == '\uFFFD' { + str = appendRune(str, r) + str = appendRune(str, r2) + } else { + str = appendRune(str, combined) + } + } else { + str = appendRune(str, r) + } + case '"': + str = append(str, '"') + case '\\': + str = append(str, '\\') + case '/': + str = append(str, '/') + case 'b': + str = append(str, '\b') + case 'f': + str = append(str, '\f') + case 'n': + str = append(str, '\n') + case 'r': + str = append(str, '\r') + case 't': + str = append(str, '\t') + default: + iter.ReportError("readEscapedChar", + `invalid escape char after \`) + return nil + } + return str +} + +// ReadStringAsSlice read string from iterator without copying into string form. +// The []byte can not be kept, as it will change after next iterator call. +func (iter *Iterator) ReadStringAsSlice() (ret []byte) { + c := iter.nextToken() + if c == '"' { + for i := iter.head; i < iter.tail; i++ { + // require ascii string and no escape + // for: field name, base64, number + if iter.buf[i] == '"' { + // fast path: reuse the underlying buffer + ret = iter.buf[iter.head:i] + iter.head = i + 1 + return ret + } + } + readLen := iter.tail - iter.head + copied := make([]byte, readLen, readLen*2) + copy(copied, iter.buf[iter.head:iter.tail]) + iter.head = iter.tail + for iter.Error == nil { + c := iter.readByte() + if c == '"' { + return copied + } + copied = append(copied, c) + } + return copied + } + iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c})) + return +} + +func (iter *Iterator) readU4() (ret rune) { + for i := 0; i < 4; i++ { + c := iter.readByte() + if iter.Error != nil { + return + } + if c >= '0' && c <= '9' { + ret = ret*16 + rune(c-'0') + } else if c >= 'a' && c <= 'f' { + ret = ret*16 + rune(c-'a'+10) + } else if c >= 'A' && c <= 'F' { + ret = ret*16 + rune(c-'A'+10) + } else { + iter.ReportError("readU4", "expects 0~9 or a~f, but found "+string([]byte{c})) + return + } + } + return ret +} + +const ( + t1 = 0x00 // 0000 0000 + tx = 0x80 // 1000 0000 + t2 = 0xC0 // 1100 0000 + t3 = 0xE0 // 1110 0000 + t4 = 0xF0 // 1111 0000 + t5 = 0xF8 // 1111 1000 + + maskx = 0x3F // 0011 1111 + mask2 = 0x1F // 0001 1111 + mask3 = 0x0F // 0000 1111 + mask4 = 0x07 // 0000 0111 + + rune1Max = 1<<7 - 1 + rune2Max = 1<<11 - 1 + rune3Max = 1<<16 - 1 + + surrogateMin = 0xD800 + surrogateMax = 0xDFFF + + maxRune = '\U0010FFFF' // Maximum valid Unicode code point. + runeError = '\uFFFD' // the "error" Rune or "Unicode replacement character" +) + +func appendRune(p []byte, r rune) []byte { + // Negative values are erroneous. Making it unsigned addresses the problem. + switch i := uint32(r); { + case i <= rune1Max: + p = append(p, byte(r)) + return p + case i <= rune2Max: + p = append(p, t2|byte(r>>6)) + p = append(p, tx|byte(r)&maskx) + return p + case i > maxRune, surrogateMin <= i && i <= surrogateMax: + r = runeError + fallthrough + case i <= rune3Max: + p = append(p, t3|byte(r>>12)) + p = append(p, tx|byte(r>>6)&maskx) + p = append(p, tx|byte(r)&maskx) + return p + default: + p = append(p, t4|byte(r>>18)) + p = append(p, tx|byte(r>>12)&maskx) + p = append(p, tx|byte(r>>6)&maskx) + p = append(p, tx|byte(r)&maskx) + return p + } +} diff --git a/vendor/github.com/json-iterator/go/jsoniter.go b/vendor/github.com/json-iterator/go/jsoniter.go new file mode 100644 index 0000000000000..c2934f916eb30 --- /dev/null +++ b/vendor/github.com/json-iterator/go/jsoniter.go @@ -0,0 +1,18 @@ +// Package jsoniter implements encoding and decoding of JSON as defined in +// RFC 4627 and provides interfaces with identical syntax of standard lib encoding/json. +// Converting from encoding/json to jsoniter is no more than replacing the package with jsoniter +// and variable type declarations (if any). +// jsoniter interfaces gives 100% compatibility with code using standard lib. +// +// "JSON and Go" +// (https://golang.org/doc/articles/json_and_go.html) +// gives a description of how Marshal/Unmarshal operate +// between arbitrary or predefined json objects and bytes, +// and it applies to jsoniter.Marshal/Unmarshal as well. +// +// Besides, jsoniter.Iterator provides a different set of interfaces +// iterating given bytes/string/reader +// and yielding parsed elements one by one. +// This set of interfaces reads input as required and gives +// better performance. +package jsoniter diff --git a/vendor/github.com/json-iterator/go/pool.go b/vendor/github.com/json-iterator/go/pool.go new file mode 100644 index 0000000000000..e2389b56cfff3 --- /dev/null +++ b/vendor/github.com/json-iterator/go/pool.go @@ -0,0 +1,42 @@ +package jsoniter + +import ( + "io" +) + +// IteratorPool a thread safe pool of iterators with same configuration +type IteratorPool interface { + BorrowIterator(data []byte) *Iterator + ReturnIterator(iter *Iterator) +} + +// StreamPool a thread safe pool of streams with same configuration +type StreamPool interface { + BorrowStream(writer io.Writer) *Stream + ReturnStream(stream *Stream) +} + +func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream { + stream := cfg.streamPool.Get().(*Stream) + stream.Reset(writer) + return stream +} + +func (cfg *frozenConfig) ReturnStream(stream *Stream) { + stream.out = nil + stream.Error = nil + stream.Attachment = nil + cfg.streamPool.Put(stream) +} + +func (cfg *frozenConfig) BorrowIterator(data []byte) *Iterator { + iter := cfg.iteratorPool.Get().(*Iterator) + iter.ResetBytes(data) + return iter +} + +func (cfg *frozenConfig) ReturnIterator(iter *Iterator) { + iter.Error = nil + iter.Attachment = nil + cfg.iteratorPool.Put(iter) +} diff --git a/vendor/github.com/json-iterator/go/reflect.go b/vendor/github.com/json-iterator/go/reflect.go new file mode 100644 index 0000000000000..74974ba74b067 --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect.go @@ -0,0 +1,337 @@ +package jsoniter + +import ( + "fmt" + "reflect" + "unsafe" + + "github.com/modern-go/reflect2" +) + +// ValDecoder is an internal type registered to cache as needed. +// Don't confuse jsoniter.ValDecoder with json.Decoder. +// For json.Decoder's adapter, refer to jsoniter.AdapterDecoder(todo link). +// +// Reflection on type to create decoders, which is then cached +// Reflection on value is avoided as we can, as the reflect.Value itself will allocate, with following exceptions +// 1. create instance of new value, for example *int will need a int to be allocated +// 2. append to slice, if the existing cap is not enough, allocate will be done using Reflect.New +// 3. assignment to map, both key and value will be reflect.Value +// For a simple struct binding, it will be reflect.Value free and allocation free +type ValDecoder interface { + Decode(ptr unsafe.Pointer, iter *Iterator) +} + +// ValEncoder is an internal type registered to cache as needed. +// Don't confuse jsoniter.ValEncoder with json.Encoder. +// For json.Encoder's adapter, refer to jsoniter.AdapterEncoder(todo godoc link). +type ValEncoder interface { + IsEmpty(ptr unsafe.Pointer) bool + Encode(ptr unsafe.Pointer, stream *Stream) +} + +type checkIsEmpty interface { + IsEmpty(ptr unsafe.Pointer) bool +} + +type ctx struct { + *frozenConfig + prefix string + encoders map[reflect2.Type]ValEncoder + decoders map[reflect2.Type]ValDecoder +} + +func (b *ctx) caseSensitive() bool { + if b.frozenConfig == nil { + // default is case-insensitive + return false + } + return b.frozenConfig.caseSensitive +} + +func (b *ctx) append(prefix string) *ctx { + return &ctx{ + frozenConfig: b.frozenConfig, + prefix: b.prefix + " " + prefix, + encoders: b.encoders, + decoders: b.decoders, + } +} + +// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal +func (iter *Iterator) ReadVal(obj interface{}) { + depth := iter.depth + cacheKey := reflect2.RTypeOf(obj) + decoder := iter.cfg.getDecoderFromCache(cacheKey) + if decoder == nil { + typ := reflect2.TypeOf(obj) + if typ.Kind() != reflect.Ptr { + iter.ReportError("ReadVal", "can only unmarshal into pointer") + return + } + decoder = iter.cfg.DecoderOf(typ) + } + ptr := reflect2.PtrOf(obj) + if ptr == nil { + iter.ReportError("ReadVal", "can not read into nil pointer") + return + } + decoder.Decode(ptr, iter) + if iter.depth != depth { + iter.ReportError("ReadVal", "unexpected mismatched nesting") + return + } +} + +// WriteVal copy the go interface into underlying JSON, same as json.Marshal +func (stream *Stream) WriteVal(val interface{}) { + if nil == val { + stream.WriteNil() + return + } + cacheKey := reflect2.RTypeOf(val) + encoder := stream.cfg.getEncoderFromCache(cacheKey) + if encoder == nil { + typ := reflect2.TypeOf(val) + encoder = stream.cfg.EncoderOf(typ) + } + encoder.Encode(reflect2.PtrOf(val), stream) +} + +func (cfg *frozenConfig) DecoderOf(typ reflect2.Type) ValDecoder { + cacheKey := typ.RType() + decoder := cfg.getDecoderFromCache(cacheKey) + if decoder != nil { + return decoder + } + ctx := &ctx{ + frozenConfig: cfg, + prefix: "", + decoders: map[reflect2.Type]ValDecoder{}, + encoders: map[reflect2.Type]ValEncoder{}, + } + ptrType := typ.(*reflect2.UnsafePtrType) + decoder = decoderOfType(ctx, ptrType.Elem()) + cfg.addDecoderToCache(cacheKey, decoder) + return decoder +} + +func decoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder { + decoder := getTypeDecoderFromExtension(ctx, typ) + if decoder != nil { + return decoder + } + decoder = createDecoderOfType(ctx, typ) + for _, extension := range extensions { + decoder = extension.DecorateDecoder(typ, decoder) + } + decoder = ctx.decoderExtension.DecorateDecoder(typ, decoder) + for _, extension := range ctx.extraExtensions { + decoder = extension.DecorateDecoder(typ, decoder) + } + return decoder +} + +func createDecoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder { + decoder := ctx.decoders[typ] + if decoder != nil { + return decoder + } + placeholder := &placeholderDecoder{} + ctx.decoders[typ] = placeholder + decoder = _createDecoderOfType(ctx, typ) + placeholder.decoder = decoder + return decoder +} + +func _createDecoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder { + decoder := createDecoderOfJsonRawMessage(ctx, typ) + if decoder != nil { + return decoder + } + decoder = createDecoderOfJsonNumber(ctx, typ) + if decoder != nil { + return decoder + } + decoder = createDecoderOfMarshaler(ctx, typ) + if decoder != nil { + return decoder + } + decoder = createDecoderOfAny(ctx, typ) + if decoder != nil { + return decoder + } + decoder = createDecoderOfNative(ctx, typ) + if decoder != nil { + return decoder + } + switch typ.Kind() { + case reflect.Interface: + ifaceType, isIFace := typ.(*reflect2.UnsafeIFaceType) + if isIFace { + return &ifaceDecoder{valType: ifaceType} + } + return &efaceDecoder{} + case reflect.Struct: + return decoderOfStruct(ctx, typ) + case reflect.Array: + return decoderOfArray(ctx, typ) + case reflect.Slice: + return decoderOfSlice(ctx, typ) + case reflect.Map: + return decoderOfMap(ctx, typ) + case reflect.Ptr: + return decoderOfOptional(ctx, typ) + default: + return &lazyErrorDecoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())} + } +} + +func (cfg *frozenConfig) EncoderOf(typ reflect2.Type) ValEncoder { + cacheKey := typ.RType() + encoder := cfg.getEncoderFromCache(cacheKey) + if encoder != nil { + return encoder + } + ctx := &ctx{ + frozenConfig: cfg, + prefix: "", + decoders: map[reflect2.Type]ValDecoder{}, + encoders: map[reflect2.Type]ValEncoder{}, + } + encoder = encoderOfType(ctx, typ) + if typ.LikePtr() { + encoder = &onePtrEncoder{encoder} + } + cfg.addEncoderToCache(cacheKey, encoder) + return encoder +} + +type onePtrEncoder struct { + encoder ValEncoder +} + +func (encoder *onePtrEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.encoder.IsEmpty(unsafe.Pointer(&ptr)) +} + +func (encoder *onePtrEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + encoder.encoder.Encode(unsafe.Pointer(&ptr), stream) +} + +func encoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder { + encoder := getTypeEncoderFromExtension(ctx, typ) + if encoder != nil { + return encoder + } + encoder = createEncoderOfType(ctx, typ) + for _, extension := range extensions { + encoder = extension.DecorateEncoder(typ, encoder) + } + encoder = ctx.encoderExtension.DecorateEncoder(typ, encoder) + for _, extension := range ctx.extraExtensions { + encoder = extension.DecorateEncoder(typ, encoder) + } + return encoder +} + +func createEncoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder { + encoder := ctx.encoders[typ] + if encoder != nil { + return encoder + } + placeholder := &placeholderEncoder{} + ctx.encoders[typ] = placeholder + encoder = _createEncoderOfType(ctx, typ) + placeholder.encoder = encoder + return encoder +} +func _createEncoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder { + encoder := createEncoderOfJsonRawMessage(ctx, typ) + if encoder != nil { + return encoder + } + encoder = createEncoderOfJsonNumber(ctx, typ) + if encoder != nil { + return encoder + } + encoder = createEncoderOfMarshaler(ctx, typ) + if encoder != nil { + return encoder + } + encoder = createEncoderOfAny(ctx, typ) + if encoder != nil { + return encoder + } + encoder = createEncoderOfNative(ctx, typ) + if encoder != nil { + return encoder + } + kind := typ.Kind() + switch kind { + case reflect.Interface: + return &dynamicEncoder{typ} + case reflect.Struct: + return encoderOfStruct(ctx, typ) + case reflect.Array: + return encoderOfArray(ctx, typ) + case reflect.Slice: + return encoderOfSlice(ctx, typ) + case reflect.Map: + return encoderOfMap(ctx, typ) + case reflect.Ptr: + return encoderOfOptional(ctx, typ) + default: + return &lazyErrorEncoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())} + } +} + +type lazyErrorDecoder struct { + err error +} + +func (decoder *lazyErrorDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if iter.WhatIsNext() != NilValue { + if iter.Error == nil { + iter.Error = decoder.err + } + } else { + iter.Skip() + } +} + +type lazyErrorEncoder struct { + err error +} + +func (encoder *lazyErrorEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + if ptr == nil { + stream.WriteNil() + } else if stream.Error == nil { + stream.Error = encoder.err + } +} + +func (encoder *lazyErrorEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return false +} + +type placeholderDecoder struct { + decoder ValDecoder +} + +func (decoder *placeholderDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + decoder.decoder.Decode(ptr, iter) +} + +type placeholderEncoder struct { + encoder ValEncoder +} + +func (encoder *placeholderEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + encoder.encoder.Encode(ptr, stream) +} + +func (encoder *placeholderEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.encoder.IsEmpty(ptr) +} diff --git a/vendor/github.com/json-iterator/go/reflect_array.go b/vendor/github.com/json-iterator/go/reflect_array.go new file mode 100644 index 0000000000000..13a0b7b0878cb --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_array.go @@ -0,0 +1,104 @@ +package jsoniter + +import ( + "fmt" + "github.com/modern-go/reflect2" + "io" + "unsafe" +) + +func decoderOfArray(ctx *ctx, typ reflect2.Type) ValDecoder { + arrayType := typ.(*reflect2.UnsafeArrayType) + decoder := decoderOfType(ctx.append("[arrayElem]"), arrayType.Elem()) + return &arrayDecoder{arrayType, decoder} +} + +func encoderOfArray(ctx *ctx, typ reflect2.Type) ValEncoder { + arrayType := typ.(*reflect2.UnsafeArrayType) + if arrayType.Len() == 0 { + return emptyArrayEncoder{} + } + encoder := encoderOfType(ctx.append("[arrayElem]"), arrayType.Elem()) + return &arrayEncoder{arrayType, encoder} +} + +type emptyArrayEncoder struct{} + +func (encoder emptyArrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteEmptyArray() +} + +func (encoder emptyArrayEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return true +} + +type arrayEncoder struct { + arrayType *reflect2.UnsafeArrayType + elemEncoder ValEncoder +} + +func (encoder *arrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteArrayStart() + elemPtr := unsafe.Pointer(ptr) + encoder.elemEncoder.Encode(elemPtr, stream) + for i := 1; i < encoder.arrayType.Len(); i++ { + stream.WriteMore() + elemPtr = encoder.arrayType.UnsafeGetIndex(ptr, i) + encoder.elemEncoder.Encode(elemPtr, stream) + } + stream.WriteArrayEnd() + if stream.Error != nil && stream.Error != io.EOF { + stream.Error = fmt.Errorf("%v: %s", encoder.arrayType, stream.Error.Error()) + } +} + +func (encoder *arrayEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return false +} + +type arrayDecoder struct { + arrayType *reflect2.UnsafeArrayType + elemDecoder ValDecoder +} + +func (decoder *arrayDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + decoder.doDecode(ptr, iter) + if iter.Error != nil && iter.Error != io.EOF { + iter.Error = fmt.Errorf("%v: %s", decoder.arrayType, iter.Error.Error()) + } +} + +func (decoder *arrayDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) { + c := iter.nextToken() + arrayType := decoder.arrayType + if c == 'n' { + iter.skipThreeBytes('u', 'l', 'l') + return + } + if c != '[' { + iter.ReportError("decode array", "expect [ or n, but found "+string([]byte{c})) + return + } + c = iter.nextToken() + if c == ']' { + return + } + iter.unreadByte() + elemPtr := arrayType.UnsafeGetIndex(ptr, 0) + decoder.elemDecoder.Decode(elemPtr, iter) + length := 1 + for c = iter.nextToken(); c == ','; c = iter.nextToken() { + if length >= arrayType.Len() { + iter.Skip() + continue + } + idx := length + length += 1 + elemPtr = arrayType.UnsafeGetIndex(ptr, idx) + decoder.elemDecoder.Decode(elemPtr, iter) + } + if c != ']' { + iter.ReportError("decode array", "expect ], but found "+string([]byte{c})) + return + } +} diff --git a/vendor/github.com/json-iterator/go/reflect_dynamic.go b/vendor/github.com/json-iterator/go/reflect_dynamic.go new file mode 100644 index 0000000000000..8b6bc8b433286 --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_dynamic.go @@ -0,0 +1,70 @@ +package jsoniter + +import ( + "github.com/modern-go/reflect2" + "reflect" + "unsafe" +) + +type dynamicEncoder struct { + valType reflect2.Type +} + +func (encoder *dynamicEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + obj := encoder.valType.UnsafeIndirect(ptr) + stream.WriteVal(obj) +} + +func (encoder *dynamicEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.valType.UnsafeIndirect(ptr) == nil +} + +type efaceDecoder struct { +} + +func (decoder *efaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + pObj := (*interface{})(ptr) + obj := *pObj + if obj == nil { + *pObj = iter.Read() + return + } + typ := reflect2.TypeOf(obj) + if typ.Kind() != reflect.Ptr { + *pObj = iter.Read() + return + } + ptrType := typ.(*reflect2.UnsafePtrType) + ptrElemType := ptrType.Elem() + if iter.WhatIsNext() == NilValue { + if ptrElemType.Kind() != reflect.Ptr { + iter.skipFourBytes('n', 'u', 'l', 'l') + *pObj = nil + return + } + } + if reflect2.IsNil(obj) { + obj := ptrElemType.New() + iter.ReadVal(obj) + *pObj = obj + return + } + iter.ReadVal(obj) +} + +type ifaceDecoder struct { + valType *reflect2.UnsafeIFaceType +} + +func (decoder *ifaceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if iter.ReadNil() { + decoder.valType.UnsafeSet(ptr, decoder.valType.UnsafeNew()) + return + } + obj := decoder.valType.UnsafeIndirect(ptr) + if reflect2.IsNil(obj) { + iter.ReportError("decode non empty interface", "can not unmarshal into nil") + return + } + iter.ReadVal(obj) +} diff --git a/vendor/github.com/json-iterator/go/reflect_extension.go b/vendor/github.com/json-iterator/go/reflect_extension.go new file mode 100644 index 0000000000000..74a97bfe5abfb --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_extension.go @@ -0,0 +1,483 @@ +package jsoniter + +import ( + "fmt" + "github.com/modern-go/reflect2" + "reflect" + "sort" + "strings" + "unicode" + "unsafe" +) + +var typeDecoders = map[string]ValDecoder{} +var fieldDecoders = map[string]ValDecoder{} +var typeEncoders = map[string]ValEncoder{} +var fieldEncoders = map[string]ValEncoder{} +var extensions = []Extension{} + +// StructDescriptor describe how should we encode/decode the struct +type StructDescriptor struct { + Type reflect2.Type + Fields []*Binding +} + +// GetField get one field from the descriptor by its name. +// Can not use map here to keep field orders. +func (structDescriptor *StructDescriptor) GetField(fieldName string) *Binding { + for _, binding := range structDescriptor.Fields { + if binding.Field.Name() == fieldName { + return binding + } + } + return nil +} + +// Binding describe how should we encode/decode the struct field +type Binding struct { + levels []int + Field reflect2.StructField + FromNames []string + ToNames []string + Encoder ValEncoder + Decoder ValDecoder +} + +// Extension the one for all SPI. Customize encoding/decoding by specifying alternate encoder/decoder. +// Can also rename fields by UpdateStructDescriptor. +type Extension interface { + UpdateStructDescriptor(structDescriptor *StructDescriptor) + CreateMapKeyDecoder(typ reflect2.Type) ValDecoder + CreateMapKeyEncoder(typ reflect2.Type) ValEncoder + CreateDecoder(typ reflect2.Type) ValDecoder + CreateEncoder(typ reflect2.Type) ValEncoder + DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder + DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder +} + +// DummyExtension embed this type get dummy implementation for all methods of Extension +type DummyExtension struct { +} + +// UpdateStructDescriptor No-op +func (extension *DummyExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) { +} + +// CreateMapKeyDecoder No-op +func (extension *DummyExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder { + return nil +} + +// CreateMapKeyEncoder No-op +func (extension *DummyExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder { + return nil +} + +// CreateDecoder No-op +func (extension *DummyExtension) CreateDecoder(typ reflect2.Type) ValDecoder { + return nil +} + +// CreateEncoder No-op +func (extension *DummyExtension) CreateEncoder(typ reflect2.Type) ValEncoder { + return nil +} + +// DecorateDecoder No-op +func (extension *DummyExtension) DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder { + return decoder +} + +// DecorateEncoder No-op +func (extension *DummyExtension) DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder { + return encoder +} + +type EncoderExtension map[reflect2.Type]ValEncoder + +// UpdateStructDescriptor No-op +func (extension EncoderExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) { +} + +// CreateDecoder No-op +func (extension EncoderExtension) CreateDecoder(typ reflect2.Type) ValDecoder { + return nil +} + +// CreateEncoder get encoder from map +func (extension EncoderExtension) CreateEncoder(typ reflect2.Type) ValEncoder { + return extension[typ] +} + +// CreateMapKeyDecoder No-op +func (extension EncoderExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder { + return nil +} + +// CreateMapKeyEncoder No-op +func (extension EncoderExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder { + return nil +} + +// DecorateDecoder No-op +func (extension EncoderExtension) DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder { + return decoder +} + +// DecorateEncoder No-op +func (extension EncoderExtension) DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder { + return encoder +} + +type DecoderExtension map[reflect2.Type]ValDecoder + +// UpdateStructDescriptor No-op +func (extension DecoderExtension) UpdateStructDescriptor(structDescriptor *StructDescriptor) { +} + +// CreateMapKeyDecoder No-op +func (extension DecoderExtension) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder { + return nil +} + +// CreateMapKeyEncoder No-op +func (extension DecoderExtension) CreateMapKeyEncoder(typ reflect2.Type) ValEncoder { + return nil +} + +// CreateDecoder get decoder from map +func (extension DecoderExtension) CreateDecoder(typ reflect2.Type) ValDecoder { + return extension[typ] +} + +// CreateEncoder No-op +func (extension DecoderExtension) CreateEncoder(typ reflect2.Type) ValEncoder { + return nil +} + +// DecorateDecoder No-op +func (extension DecoderExtension) DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder { + return decoder +} + +// DecorateEncoder No-op +func (extension DecoderExtension) DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder { + return encoder +} + +type funcDecoder struct { + fun DecoderFunc +} + +func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + decoder.fun(ptr, iter) +} + +type funcEncoder struct { + fun EncoderFunc + isEmptyFunc func(ptr unsafe.Pointer) bool +} + +func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + encoder.fun(ptr, stream) +} + +func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool { + if encoder.isEmptyFunc == nil { + return false + } + return encoder.isEmptyFunc(ptr) +} + +// DecoderFunc the function form of TypeDecoder +type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator) + +// EncoderFunc the function form of TypeEncoder +type EncoderFunc func(ptr unsafe.Pointer, stream *Stream) + +// RegisterTypeDecoderFunc register TypeDecoder for a type with function +func RegisterTypeDecoderFunc(typ string, fun DecoderFunc) { + typeDecoders[typ] = &funcDecoder{fun} +} + +// RegisterTypeDecoder register TypeDecoder for a typ +func RegisterTypeDecoder(typ string, decoder ValDecoder) { + typeDecoders[typ] = decoder +} + +// RegisterFieldDecoderFunc register TypeDecoder for a struct field with function +func RegisterFieldDecoderFunc(typ string, field string, fun DecoderFunc) { + RegisterFieldDecoder(typ, field, &funcDecoder{fun}) +} + +// RegisterFieldDecoder register TypeDecoder for a struct field +func RegisterFieldDecoder(typ string, field string, decoder ValDecoder) { + fieldDecoders[fmt.Sprintf("%s/%s", typ, field)] = decoder +} + +// RegisterTypeEncoderFunc register TypeEncoder for a type with encode/isEmpty function +func RegisterTypeEncoderFunc(typ string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) { + typeEncoders[typ] = &funcEncoder{fun, isEmptyFunc} +} + +// RegisterTypeEncoder register TypeEncoder for a type +func RegisterTypeEncoder(typ string, encoder ValEncoder) { + typeEncoders[typ] = encoder +} + +// RegisterFieldEncoderFunc register TypeEncoder for a struct field with encode/isEmpty function +func RegisterFieldEncoderFunc(typ string, field string, fun EncoderFunc, isEmptyFunc func(unsafe.Pointer) bool) { + RegisterFieldEncoder(typ, field, &funcEncoder{fun, isEmptyFunc}) +} + +// RegisterFieldEncoder register TypeEncoder for a struct field +func RegisterFieldEncoder(typ string, field string, encoder ValEncoder) { + fieldEncoders[fmt.Sprintf("%s/%s", typ, field)] = encoder +} + +// RegisterExtension register extension +func RegisterExtension(extension Extension) { + extensions = append(extensions, extension) +} + +func getTypeDecoderFromExtension(ctx *ctx, typ reflect2.Type) ValDecoder { + decoder := _getTypeDecoderFromExtension(ctx, typ) + if decoder != nil { + for _, extension := range extensions { + decoder = extension.DecorateDecoder(typ, decoder) + } + decoder = ctx.decoderExtension.DecorateDecoder(typ, decoder) + for _, extension := range ctx.extraExtensions { + decoder = extension.DecorateDecoder(typ, decoder) + } + } + return decoder +} +func _getTypeDecoderFromExtension(ctx *ctx, typ reflect2.Type) ValDecoder { + for _, extension := range extensions { + decoder := extension.CreateDecoder(typ) + if decoder != nil { + return decoder + } + } + decoder := ctx.decoderExtension.CreateDecoder(typ) + if decoder != nil { + return decoder + } + for _, extension := range ctx.extraExtensions { + decoder := extension.CreateDecoder(typ) + if decoder != nil { + return decoder + } + } + typeName := typ.String() + decoder = typeDecoders[typeName] + if decoder != nil { + return decoder + } + if typ.Kind() == reflect.Ptr { + ptrType := typ.(*reflect2.UnsafePtrType) + decoder := typeDecoders[ptrType.Elem().String()] + if decoder != nil { + return &OptionalDecoder{ptrType.Elem(), decoder} + } + } + return nil +} + +func getTypeEncoderFromExtension(ctx *ctx, typ reflect2.Type) ValEncoder { + encoder := _getTypeEncoderFromExtension(ctx, typ) + if encoder != nil { + for _, extension := range extensions { + encoder = extension.DecorateEncoder(typ, encoder) + } + encoder = ctx.encoderExtension.DecorateEncoder(typ, encoder) + for _, extension := range ctx.extraExtensions { + encoder = extension.DecorateEncoder(typ, encoder) + } + } + return encoder +} + +func _getTypeEncoderFromExtension(ctx *ctx, typ reflect2.Type) ValEncoder { + for _, extension := range extensions { + encoder := extension.CreateEncoder(typ) + if encoder != nil { + return encoder + } + } + encoder := ctx.encoderExtension.CreateEncoder(typ) + if encoder != nil { + return encoder + } + for _, extension := range ctx.extraExtensions { + encoder := extension.CreateEncoder(typ) + if encoder != nil { + return encoder + } + } + typeName := typ.String() + encoder = typeEncoders[typeName] + if encoder != nil { + return encoder + } + if typ.Kind() == reflect.Ptr { + typePtr := typ.(*reflect2.UnsafePtrType) + encoder := typeEncoders[typePtr.Elem().String()] + if encoder != nil { + return &OptionalEncoder{encoder} + } + } + return nil +} + +func describeStruct(ctx *ctx, typ reflect2.Type) *StructDescriptor { + structType := typ.(*reflect2.UnsafeStructType) + embeddedBindings := []*Binding{} + bindings := []*Binding{} + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + tag, hastag := field.Tag().Lookup(ctx.getTagKey()) + if ctx.onlyTaggedField && !hastag && !field.Anonymous() { + continue + } + if tag == "-" || field.Name() == "_" { + continue + } + tagParts := strings.Split(tag, ",") + if field.Anonymous() && (tag == "" || tagParts[0] == "") { + if field.Type().Kind() == reflect.Struct { + structDescriptor := describeStruct(ctx, field.Type()) + for _, binding := range structDescriptor.Fields { + binding.levels = append([]int{i}, binding.levels...) + omitempty := binding.Encoder.(*structFieldEncoder).omitempty + binding.Encoder = &structFieldEncoder{field, binding.Encoder, omitempty} + binding.Decoder = &structFieldDecoder{field, binding.Decoder} + embeddedBindings = append(embeddedBindings, binding) + } + continue + } else if field.Type().Kind() == reflect.Ptr { + ptrType := field.Type().(*reflect2.UnsafePtrType) + if ptrType.Elem().Kind() == reflect.Struct { + structDescriptor := describeStruct(ctx, ptrType.Elem()) + for _, binding := range structDescriptor.Fields { + binding.levels = append([]int{i}, binding.levels...) + omitempty := binding.Encoder.(*structFieldEncoder).omitempty + binding.Encoder = &dereferenceEncoder{binding.Encoder} + binding.Encoder = &structFieldEncoder{field, binding.Encoder, omitempty} + binding.Decoder = &dereferenceDecoder{ptrType.Elem(), binding.Decoder} + binding.Decoder = &structFieldDecoder{field, binding.Decoder} + embeddedBindings = append(embeddedBindings, binding) + } + continue + } + } + } + fieldNames := calcFieldNames(field.Name(), tagParts[0], tag) + fieldCacheKey := fmt.Sprintf("%s/%s", typ.String(), field.Name()) + decoder := fieldDecoders[fieldCacheKey] + if decoder == nil { + decoder = decoderOfType(ctx.append(field.Name()), field.Type()) + } + encoder := fieldEncoders[fieldCacheKey] + if encoder == nil { + encoder = encoderOfType(ctx.append(field.Name()), field.Type()) + } + binding := &Binding{ + Field: field, + FromNames: fieldNames, + ToNames: fieldNames, + Decoder: decoder, + Encoder: encoder, + } + binding.levels = []int{i} + bindings = append(bindings, binding) + } + return createStructDescriptor(ctx, typ, bindings, embeddedBindings) +} +func createStructDescriptor(ctx *ctx, typ reflect2.Type, bindings []*Binding, embeddedBindings []*Binding) *StructDescriptor { + structDescriptor := &StructDescriptor{ + Type: typ, + Fields: bindings, + } + for _, extension := range extensions { + extension.UpdateStructDescriptor(structDescriptor) + } + ctx.encoderExtension.UpdateStructDescriptor(structDescriptor) + ctx.decoderExtension.UpdateStructDescriptor(structDescriptor) + for _, extension := range ctx.extraExtensions { + extension.UpdateStructDescriptor(structDescriptor) + } + processTags(structDescriptor, ctx.frozenConfig) + // merge normal & embedded bindings & sort with original order + allBindings := sortableBindings(append(embeddedBindings, structDescriptor.Fields...)) + sort.Sort(allBindings) + structDescriptor.Fields = allBindings + return structDescriptor +} + +type sortableBindings []*Binding + +func (bindings sortableBindings) Len() int { + return len(bindings) +} + +func (bindings sortableBindings) Less(i, j int) bool { + left := bindings[i].levels + right := bindings[j].levels + k := 0 + for { + if left[k] < right[k] { + return true + } else if left[k] > right[k] { + return false + } + k++ + } +} + +func (bindings sortableBindings) Swap(i, j int) { + bindings[i], bindings[j] = bindings[j], bindings[i] +} + +func processTags(structDescriptor *StructDescriptor, cfg *frozenConfig) { + for _, binding := range structDescriptor.Fields { + shouldOmitEmpty := false + tagParts := strings.Split(binding.Field.Tag().Get(cfg.getTagKey()), ",") + for _, tagPart := range tagParts[1:] { + if tagPart == "omitempty" { + shouldOmitEmpty = true + } else if tagPart == "string" { + if binding.Field.Type().Kind() == reflect.String { + binding.Decoder = &stringModeStringDecoder{binding.Decoder, cfg} + binding.Encoder = &stringModeStringEncoder{binding.Encoder, cfg} + } else { + binding.Decoder = &stringModeNumberDecoder{binding.Decoder} + binding.Encoder = &stringModeNumberEncoder{binding.Encoder} + } + } + } + binding.Decoder = &structFieldDecoder{binding.Field, binding.Decoder} + binding.Encoder = &structFieldEncoder{binding.Field, binding.Encoder, shouldOmitEmpty} + } +} + +func calcFieldNames(originalFieldName string, tagProvidedFieldName string, wholeTag string) []string { + // ignore? + if wholeTag == "-" { + return []string{} + } + // rename? + var fieldNames []string + if tagProvidedFieldName == "" { + fieldNames = []string{originalFieldName} + } else { + fieldNames = []string{tagProvidedFieldName} + } + // private? + isNotExported := unicode.IsLower(rune(originalFieldName[0])) || originalFieldName[0] == '_' + if isNotExported { + fieldNames = []string{} + } + return fieldNames +} diff --git a/vendor/github.com/json-iterator/go/reflect_json_number.go b/vendor/github.com/json-iterator/go/reflect_json_number.go new file mode 100644 index 0000000000000..98d45c1ec2550 --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_json_number.go @@ -0,0 +1,112 @@ +package jsoniter + +import ( + "encoding/json" + "github.com/modern-go/reflect2" + "strconv" + "unsafe" +) + +type Number string + +// String returns the literal text of the number. +func (n Number) String() string { return string(n) } + +// Float64 returns the number as a float64. +func (n Number) Float64() (float64, error) { + return strconv.ParseFloat(string(n), 64) +} + +// Int64 returns the number as an int64. +func (n Number) Int64() (int64, error) { + return strconv.ParseInt(string(n), 10, 64) +} + +func CastJsonNumber(val interface{}) (string, bool) { + switch typedVal := val.(type) { + case json.Number: + return string(typedVal), true + case Number: + return string(typedVal), true + } + return "", false +} + +var jsonNumberType = reflect2.TypeOfPtr((*json.Number)(nil)).Elem() +var jsoniterNumberType = reflect2.TypeOfPtr((*Number)(nil)).Elem() + +func createDecoderOfJsonNumber(ctx *ctx, typ reflect2.Type) ValDecoder { + if typ.AssignableTo(jsonNumberType) { + return &jsonNumberCodec{} + } + if typ.AssignableTo(jsoniterNumberType) { + return &jsoniterNumberCodec{} + } + return nil +} + +func createEncoderOfJsonNumber(ctx *ctx, typ reflect2.Type) ValEncoder { + if typ.AssignableTo(jsonNumberType) { + return &jsonNumberCodec{} + } + if typ.AssignableTo(jsoniterNumberType) { + return &jsoniterNumberCodec{} + } + return nil +} + +type jsonNumberCodec struct { +} + +func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { + switch iter.WhatIsNext() { + case StringValue: + *((*json.Number)(ptr)) = json.Number(iter.ReadString()) + case NilValue: + iter.skipFourBytes('n', 'u', 'l', 'l') + *((*json.Number)(ptr)) = "" + default: + *((*json.Number)(ptr)) = json.Number([]byte(iter.readNumberAsString())) + } +} + +func (codec *jsonNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) { + number := *((*json.Number)(ptr)) + if len(number) == 0 { + stream.writeByte('0') + } else { + stream.WriteRaw(string(number)) + } +} + +func (codec *jsonNumberCodec) IsEmpty(ptr unsafe.Pointer) bool { + return len(*((*json.Number)(ptr))) == 0 +} + +type jsoniterNumberCodec struct { +} + +func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { + switch iter.WhatIsNext() { + case StringValue: + *((*Number)(ptr)) = Number(iter.ReadString()) + case NilValue: + iter.skipFourBytes('n', 'u', 'l', 'l') + *((*Number)(ptr)) = "" + default: + *((*Number)(ptr)) = Number([]byte(iter.readNumberAsString())) + } +} + +func (codec *jsoniterNumberCodec) Encode(ptr unsafe.Pointer, stream *Stream) { + number := *((*Number)(ptr)) + if len(number) == 0 { + stream.writeByte('0') + } else { + stream.WriteRaw(string(number)) + } +} + +func (codec *jsoniterNumberCodec) IsEmpty(ptr unsafe.Pointer) bool { + return len(*((*Number)(ptr))) == 0 +} diff --git a/vendor/github.com/json-iterator/go/reflect_json_raw_message.go b/vendor/github.com/json-iterator/go/reflect_json_raw_message.go new file mode 100644 index 0000000000000..f2619936c88fd --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_json_raw_message.go @@ -0,0 +1,60 @@ +package jsoniter + +import ( + "encoding/json" + "github.com/modern-go/reflect2" + "unsafe" +) + +var jsonRawMessageType = reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem() +var jsoniterRawMessageType = reflect2.TypeOfPtr((*RawMessage)(nil)).Elem() + +func createEncoderOfJsonRawMessage(ctx *ctx, typ reflect2.Type) ValEncoder { + if typ == jsonRawMessageType { + return &jsonRawMessageCodec{} + } + if typ == jsoniterRawMessageType { + return &jsoniterRawMessageCodec{} + } + return nil +} + +func createDecoderOfJsonRawMessage(ctx *ctx, typ reflect2.Type) ValDecoder { + if typ == jsonRawMessageType { + return &jsonRawMessageCodec{} + } + if typ == jsoniterRawMessageType { + return &jsoniterRawMessageCodec{} + } + return nil +} + +type jsonRawMessageCodec struct { +} + +func (codec *jsonRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { + *((*json.RawMessage)(ptr)) = json.RawMessage(iter.SkipAndReturnBytes()) +} + +func (codec *jsonRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteRaw(string(*((*json.RawMessage)(ptr)))) +} + +func (codec *jsonRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool { + return len(*((*json.RawMessage)(ptr))) == 0 +} + +type jsoniterRawMessageCodec struct { +} + +func (codec *jsoniterRawMessageCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { + *((*RawMessage)(ptr)) = RawMessage(iter.SkipAndReturnBytes()) +} + +func (codec *jsoniterRawMessageCodec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteRaw(string(*((*RawMessage)(ptr)))) +} + +func (codec *jsoniterRawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool { + return len(*((*RawMessage)(ptr))) == 0 +} diff --git a/vendor/github.com/json-iterator/go/reflect_map.go b/vendor/github.com/json-iterator/go/reflect_map.go new file mode 100644 index 0000000000000..5829671301353 --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_map.go @@ -0,0 +1,346 @@ +package jsoniter + +import ( + "fmt" + "github.com/modern-go/reflect2" + "io" + "reflect" + "sort" + "unsafe" +) + +func decoderOfMap(ctx *ctx, typ reflect2.Type) ValDecoder { + mapType := typ.(*reflect2.UnsafeMapType) + keyDecoder := decoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()) + elemDecoder := decoderOfType(ctx.append("[mapElem]"), mapType.Elem()) + return &mapDecoder{ + mapType: mapType, + keyType: mapType.Key(), + elemType: mapType.Elem(), + keyDecoder: keyDecoder, + elemDecoder: elemDecoder, + } +} + +func encoderOfMap(ctx *ctx, typ reflect2.Type) ValEncoder { + mapType := typ.(*reflect2.UnsafeMapType) + if ctx.sortMapKeys { + return &sortKeysMapEncoder{ + mapType: mapType, + keyEncoder: encoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()), + elemEncoder: encoderOfType(ctx.append("[mapElem]"), mapType.Elem()), + } + } + return &mapEncoder{ + mapType: mapType, + keyEncoder: encoderOfMapKey(ctx.append("[mapKey]"), mapType.Key()), + elemEncoder: encoderOfType(ctx.append("[mapElem]"), mapType.Elem()), + } +} + +func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder { + decoder := ctx.decoderExtension.CreateMapKeyDecoder(typ) + if decoder != nil { + return decoder + } + for _, extension := range ctx.extraExtensions { + decoder := extension.CreateMapKeyDecoder(typ) + if decoder != nil { + return decoder + } + } + + ptrType := reflect2.PtrTo(typ) + if ptrType.Implements(unmarshalerType) { + return &referenceDecoder{ + &unmarshalerDecoder{ + valType: ptrType, + }, + } + } + if typ.Implements(unmarshalerType) { + return &unmarshalerDecoder{ + valType: typ, + } + } + if ptrType.Implements(textUnmarshalerType) { + return &referenceDecoder{ + &textUnmarshalerDecoder{ + valType: ptrType, + }, + } + } + if typ.Implements(textUnmarshalerType) { + return &textUnmarshalerDecoder{ + valType: typ, + } + } + + switch typ.Kind() { + case reflect.String: + return decoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String)) + case reflect.Bool, + reflect.Uint8, reflect.Int8, + reflect.Uint16, reflect.Int16, + reflect.Uint32, reflect.Int32, + reflect.Uint64, reflect.Int64, + reflect.Uint, reflect.Int, + reflect.Float32, reflect.Float64, + reflect.Uintptr: + typ = reflect2.DefaultTypeOfKind(typ.Kind()) + return &numericMapKeyDecoder{decoderOfType(ctx, typ)} + default: + return &lazyErrorDecoder{err: fmt.Errorf("unsupported map key type: %v", typ)} + } +} + +func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder { + encoder := ctx.encoderExtension.CreateMapKeyEncoder(typ) + if encoder != nil { + return encoder + } + for _, extension := range ctx.extraExtensions { + encoder := extension.CreateMapKeyEncoder(typ) + if encoder != nil { + return encoder + } + } + + if typ == textMarshalerType { + return &directTextMarshalerEncoder{ + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + } + } + if typ.Implements(textMarshalerType) { + return &textMarshalerEncoder{ + valType: typ, + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + } + } + + switch typ.Kind() { + case reflect.String: + return encoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String)) + case reflect.Bool, + reflect.Uint8, reflect.Int8, + reflect.Uint16, reflect.Int16, + reflect.Uint32, reflect.Int32, + reflect.Uint64, reflect.Int64, + reflect.Uint, reflect.Int, + reflect.Float32, reflect.Float64, + reflect.Uintptr: + typ = reflect2.DefaultTypeOfKind(typ.Kind()) + return &numericMapKeyEncoder{encoderOfType(ctx, typ)} + default: + if typ.Kind() == reflect.Interface { + return &dynamicMapKeyEncoder{ctx, typ} + } + return &lazyErrorEncoder{err: fmt.Errorf("unsupported map key type: %v", typ)} + } +} + +type mapDecoder struct { + mapType *reflect2.UnsafeMapType + keyType reflect2.Type + elemType reflect2.Type + keyDecoder ValDecoder + elemDecoder ValDecoder +} + +func (decoder *mapDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + mapType := decoder.mapType + c := iter.nextToken() + if c == 'n' { + iter.skipThreeBytes('u', 'l', 'l') + *(*unsafe.Pointer)(ptr) = nil + mapType.UnsafeSet(ptr, mapType.UnsafeNew()) + return + } + if mapType.UnsafeIsNil(ptr) { + mapType.UnsafeSet(ptr, mapType.UnsafeMakeMap(0)) + } + if c != '{' { + iter.ReportError("ReadMapCB", `expect { or n, but found `+string([]byte{c})) + return + } + c = iter.nextToken() + if c == '}' { + return + } + iter.unreadByte() + key := decoder.keyType.UnsafeNew() + decoder.keyDecoder.Decode(key, iter) + c = iter.nextToken() + if c != ':' { + iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c})) + return + } + elem := decoder.elemType.UnsafeNew() + decoder.elemDecoder.Decode(elem, iter) + decoder.mapType.UnsafeSetIndex(ptr, key, elem) + for c = iter.nextToken(); c == ','; c = iter.nextToken() { + key := decoder.keyType.UnsafeNew() + decoder.keyDecoder.Decode(key, iter) + c = iter.nextToken() + if c != ':' { + iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c})) + return + } + elem := decoder.elemType.UnsafeNew() + decoder.elemDecoder.Decode(elem, iter) + decoder.mapType.UnsafeSetIndex(ptr, key, elem) + } + if c != '}' { + iter.ReportError("ReadMapCB", `expect }, but found `+string([]byte{c})) + } +} + +type numericMapKeyDecoder struct { + decoder ValDecoder +} + +func (decoder *numericMapKeyDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + c := iter.nextToken() + if c != '"' { + iter.ReportError("ReadMapCB", `expect ", but found `+string([]byte{c})) + return + } + decoder.decoder.Decode(ptr, iter) + c = iter.nextToken() + if c != '"' { + iter.ReportError("ReadMapCB", `expect ", but found `+string([]byte{c})) + return + } +} + +type numericMapKeyEncoder struct { + encoder ValEncoder +} + +func (encoder *numericMapKeyEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.writeByte('"') + encoder.encoder.Encode(ptr, stream) + stream.writeByte('"') +} + +func (encoder *numericMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return false +} + +type dynamicMapKeyEncoder struct { + ctx *ctx + valType reflect2.Type +} + +func (encoder *dynamicMapKeyEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + obj := encoder.valType.UnsafeIndirect(ptr) + encoderOfMapKey(encoder.ctx, reflect2.TypeOf(obj)).Encode(reflect2.PtrOf(obj), stream) +} + +func (encoder *dynamicMapKeyEncoder) IsEmpty(ptr unsafe.Pointer) bool { + obj := encoder.valType.UnsafeIndirect(ptr) + return encoderOfMapKey(encoder.ctx, reflect2.TypeOf(obj)).IsEmpty(reflect2.PtrOf(obj)) +} + +type mapEncoder struct { + mapType *reflect2.UnsafeMapType + keyEncoder ValEncoder + elemEncoder ValEncoder +} + +func (encoder *mapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + if *(*unsafe.Pointer)(ptr) == nil { + stream.WriteNil() + return + } + stream.WriteObjectStart() + iter := encoder.mapType.UnsafeIterate(ptr) + for i := 0; iter.HasNext(); i++ { + if i != 0 { + stream.WriteMore() + } + key, elem := iter.UnsafeNext() + encoder.keyEncoder.Encode(key, stream) + if stream.indention > 0 { + stream.writeTwoBytes(byte(':'), byte(' ')) + } else { + stream.writeByte(':') + } + encoder.elemEncoder.Encode(elem, stream) + } + stream.WriteObjectEnd() +} + +func (encoder *mapEncoder) IsEmpty(ptr unsafe.Pointer) bool { + iter := encoder.mapType.UnsafeIterate(ptr) + return !iter.HasNext() +} + +type sortKeysMapEncoder struct { + mapType *reflect2.UnsafeMapType + keyEncoder ValEncoder + elemEncoder ValEncoder +} + +func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + if *(*unsafe.Pointer)(ptr) == nil { + stream.WriteNil() + return + } + stream.WriteObjectStart() + mapIter := encoder.mapType.UnsafeIterate(ptr) + subStream := stream.cfg.BorrowStream(nil) + subStream.Attachment = stream.Attachment + subIter := stream.cfg.BorrowIterator(nil) + keyValues := encodedKeyValues{} + for mapIter.HasNext() { + key, elem := mapIter.UnsafeNext() + subStreamIndex := subStream.Buffered() + encoder.keyEncoder.Encode(key, subStream) + if subStream.Error != nil && subStream.Error != io.EOF && stream.Error == nil { + stream.Error = subStream.Error + } + encodedKey := subStream.Buffer()[subStreamIndex:] + subIter.ResetBytes(encodedKey) + decodedKey := subIter.ReadString() + if stream.indention > 0 { + subStream.writeTwoBytes(byte(':'), byte(' ')) + } else { + subStream.writeByte(':') + } + encoder.elemEncoder.Encode(elem, subStream) + keyValues = append(keyValues, encodedKV{ + key: decodedKey, + keyValue: subStream.Buffer()[subStreamIndex:], + }) + } + sort.Sort(keyValues) + for i, keyValue := range keyValues { + if i != 0 { + stream.WriteMore() + } + stream.Write(keyValue.keyValue) + } + if subStream.Error != nil && stream.Error == nil { + stream.Error = subStream.Error + } + stream.WriteObjectEnd() + stream.cfg.ReturnStream(subStream) + stream.cfg.ReturnIterator(subIter) +} + +func (encoder *sortKeysMapEncoder) IsEmpty(ptr unsafe.Pointer) bool { + iter := encoder.mapType.UnsafeIterate(ptr) + return !iter.HasNext() +} + +type encodedKeyValues []encodedKV + +type encodedKV struct { + key string + keyValue []byte +} + +func (sv encodedKeyValues) Len() int { return len(sv) } +func (sv encodedKeyValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } +func (sv encodedKeyValues) Less(i, j int) bool { return sv[i].key < sv[j].key } diff --git a/vendor/github.com/json-iterator/go/reflect_marshaler.go b/vendor/github.com/json-iterator/go/reflect_marshaler.go new file mode 100644 index 0000000000000..3e21f3756717a --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_marshaler.go @@ -0,0 +1,225 @@ +package jsoniter + +import ( + "encoding" + "encoding/json" + "unsafe" + + "github.com/modern-go/reflect2" +) + +var marshalerType = reflect2.TypeOfPtr((*json.Marshaler)(nil)).Elem() +var unmarshalerType = reflect2.TypeOfPtr((*json.Unmarshaler)(nil)).Elem() +var textMarshalerType = reflect2.TypeOfPtr((*encoding.TextMarshaler)(nil)).Elem() +var textUnmarshalerType = reflect2.TypeOfPtr((*encoding.TextUnmarshaler)(nil)).Elem() + +func createDecoderOfMarshaler(ctx *ctx, typ reflect2.Type) ValDecoder { + ptrType := reflect2.PtrTo(typ) + if ptrType.Implements(unmarshalerType) { + return &referenceDecoder{ + &unmarshalerDecoder{ptrType}, + } + } + if ptrType.Implements(textUnmarshalerType) { + return &referenceDecoder{ + &textUnmarshalerDecoder{ptrType}, + } + } + return nil +} + +func createEncoderOfMarshaler(ctx *ctx, typ reflect2.Type) ValEncoder { + if typ == marshalerType { + checkIsEmpty := createCheckIsEmpty(ctx, typ) + var encoder ValEncoder = &directMarshalerEncoder{ + checkIsEmpty: checkIsEmpty, + } + return encoder + } + if typ.Implements(marshalerType) { + checkIsEmpty := createCheckIsEmpty(ctx, typ) + var encoder ValEncoder = &marshalerEncoder{ + valType: typ, + checkIsEmpty: checkIsEmpty, + } + return encoder + } + ptrType := reflect2.PtrTo(typ) + if ctx.prefix != "" && ptrType.Implements(marshalerType) { + checkIsEmpty := createCheckIsEmpty(ctx, ptrType) + var encoder ValEncoder = &marshalerEncoder{ + valType: ptrType, + checkIsEmpty: checkIsEmpty, + } + return &referenceEncoder{encoder} + } + if typ == textMarshalerType { + checkIsEmpty := createCheckIsEmpty(ctx, typ) + var encoder ValEncoder = &directTextMarshalerEncoder{ + checkIsEmpty: checkIsEmpty, + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + } + return encoder + } + if typ.Implements(textMarshalerType) { + checkIsEmpty := createCheckIsEmpty(ctx, typ) + var encoder ValEncoder = &textMarshalerEncoder{ + valType: typ, + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + checkIsEmpty: checkIsEmpty, + } + return encoder + } + // if prefix is empty, the type is the root type + if ctx.prefix != "" && ptrType.Implements(textMarshalerType) { + checkIsEmpty := createCheckIsEmpty(ctx, ptrType) + var encoder ValEncoder = &textMarshalerEncoder{ + valType: ptrType, + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + checkIsEmpty: checkIsEmpty, + } + return &referenceEncoder{encoder} + } + return nil +} + +type marshalerEncoder struct { + checkIsEmpty checkIsEmpty + valType reflect2.Type +} + +func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + obj := encoder.valType.UnsafeIndirect(ptr) + if encoder.valType.IsNullable() && reflect2.IsNil(obj) { + stream.WriteNil() + return + } + marshaler := obj.(json.Marshaler) + bytes, err := marshaler.MarshalJSON() + if err != nil { + stream.Error = err + } else { + // html escape was already done by jsoniter + // but the extra '\n' should be trimed + l := len(bytes) + if l > 0 && bytes[l-1] == '\n' { + bytes = bytes[:l-1] + } + stream.Write(bytes) + } +} + +func (encoder *marshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.checkIsEmpty.IsEmpty(ptr) +} + +type directMarshalerEncoder struct { + checkIsEmpty checkIsEmpty +} + +func (encoder *directMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + marshaler := *(*json.Marshaler)(ptr) + if marshaler == nil { + stream.WriteNil() + return + } + bytes, err := marshaler.MarshalJSON() + if err != nil { + stream.Error = err + } else { + stream.Write(bytes) + } +} + +func (encoder *directMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.checkIsEmpty.IsEmpty(ptr) +} + +type textMarshalerEncoder struct { + valType reflect2.Type + stringEncoder ValEncoder + checkIsEmpty checkIsEmpty +} + +func (encoder *textMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + obj := encoder.valType.UnsafeIndirect(ptr) + if encoder.valType.IsNullable() && reflect2.IsNil(obj) { + stream.WriteNil() + return + } + marshaler := (obj).(encoding.TextMarshaler) + bytes, err := marshaler.MarshalText() + if err != nil { + stream.Error = err + } else { + str := string(bytes) + encoder.stringEncoder.Encode(unsafe.Pointer(&str), stream) + } +} + +func (encoder *textMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.checkIsEmpty.IsEmpty(ptr) +} + +type directTextMarshalerEncoder struct { + stringEncoder ValEncoder + checkIsEmpty checkIsEmpty +} + +func (encoder *directTextMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + marshaler := *(*encoding.TextMarshaler)(ptr) + if marshaler == nil { + stream.WriteNil() + return + } + bytes, err := marshaler.MarshalText() + if err != nil { + stream.Error = err + } else { + str := string(bytes) + encoder.stringEncoder.Encode(unsafe.Pointer(&str), stream) + } +} + +func (encoder *directTextMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.checkIsEmpty.IsEmpty(ptr) +} + +type unmarshalerDecoder struct { + valType reflect2.Type +} + +func (decoder *unmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + valType := decoder.valType + obj := valType.UnsafeIndirect(ptr) + unmarshaler := obj.(json.Unmarshaler) + iter.nextToken() + iter.unreadByte() // skip spaces + bytes := iter.SkipAndReturnBytes() + err := unmarshaler.UnmarshalJSON(bytes) + if err != nil { + iter.ReportError("unmarshalerDecoder", err.Error()) + } +} + +type textUnmarshalerDecoder struct { + valType reflect2.Type +} + +func (decoder *textUnmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + valType := decoder.valType + obj := valType.UnsafeIndirect(ptr) + if reflect2.IsNil(obj) { + ptrType := valType.(*reflect2.UnsafePtrType) + elemType := ptrType.Elem() + elem := elemType.UnsafeNew() + ptrType.UnsafeSet(ptr, unsafe.Pointer(&elem)) + obj = valType.UnsafeIndirect(ptr) + } + unmarshaler := (obj).(encoding.TextUnmarshaler) + str := iter.ReadString() + err := unmarshaler.UnmarshalText([]byte(str)) + if err != nil { + iter.ReportError("textUnmarshalerDecoder", err.Error()) + } +} diff --git a/vendor/github.com/json-iterator/go/reflect_native.go b/vendor/github.com/json-iterator/go/reflect_native.go new file mode 100644 index 0000000000000..f88722d14d198 --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_native.go @@ -0,0 +1,453 @@ +package jsoniter + +import ( + "encoding/base64" + "reflect" + "strconv" + "unsafe" + + "github.com/modern-go/reflect2" +) + +const ptrSize = 32 << uintptr(^uintptr(0)>>63) + +func createEncoderOfNative(ctx *ctx, typ reflect2.Type) ValEncoder { + if typ.Kind() == reflect.Slice && typ.(reflect2.SliceType).Elem().Kind() == reflect.Uint8 { + sliceDecoder := decoderOfSlice(ctx, typ) + return &base64Codec{sliceDecoder: sliceDecoder} + } + typeName := typ.String() + kind := typ.Kind() + switch kind { + case reflect.String: + if typeName != "string" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*string)(nil)).Elem()) + } + return &stringCodec{} + case reflect.Int: + if typeName != "int" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*int)(nil)).Elem()) + } + if strconv.IntSize == 32 { + return &int32Codec{} + } + return &int64Codec{} + case reflect.Int8: + if typeName != "int8" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*int8)(nil)).Elem()) + } + return &int8Codec{} + case reflect.Int16: + if typeName != "int16" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*int16)(nil)).Elem()) + } + return &int16Codec{} + case reflect.Int32: + if typeName != "int32" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*int32)(nil)).Elem()) + } + return &int32Codec{} + case reflect.Int64: + if typeName != "int64" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*int64)(nil)).Elem()) + } + return &int64Codec{} + case reflect.Uint: + if typeName != "uint" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*uint)(nil)).Elem()) + } + if strconv.IntSize == 32 { + return &uint32Codec{} + } + return &uint64Codec{} + case reflect.Uint8: + if typeName != "uint8" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*uint8)(nil)).Elem()) + } + return &uint8Codec{} + case reflect.Uint16: + if typeName != "uint16" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*uint16)(nil)).Elem()) + } + return &uint16Codec{} + case reflect.Uint32: + if typeName != "uint32" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*uint32)(nil)).Elem()) + } + return &uint32Codec{} + case reflect.Uintptr: + if typeName != "uintptr" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*uintptr)(nil)).Elem()) + } + if ptrSize == 32 { + return &uint32Codec{} + } + return &uint64Codec{} + case reflect.Uint64: + if typeName != "uint64" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*uint64)(nil)).Elem()) + } + return &uint64Codec{} + case reflect.Float32: + if typeName != "float32" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*float32)(nil)).Elem()) + } + return &float32Codec{} + case reflect.Float64: + if typeName != "float64" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*float64)(nil)).Elem()) + } + return &float64Codec{} + case reflect.Bool: + if typeName != "bool" { + return encoderOfType(ctx, reflect2.TypeOfPtr((*bool)(nil)).Elem()) + } + return &boolCodec{} + } + return nil +} + +func createDecoderOfNative(ctx *ctx, typ reflect2.Type) ValDecoder { + if typ.Kind() == reflect.Slice && typ.(reflect2.SliceType).Elem().Kind() == reflect.Uint8 { + sliceDecoder := decoderOfSlice(ctx, typ) + return &base64Codec{sliceDecoder: sliceDecoder} + } + typeName := typ.String() + switch typ.Kind() { + case reflect.String: + if typeName != "string" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*string)(nil)).Elem()) + } + return &stringCodec{} + case reflect.Int: + if typeName != "int" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*int)(nil)).Elem()) + } + if strconv.IntSize == 32 { + return &int32Codec{} + } + return &int64Codec{} + case reflect.Int8: + if typeName != "int8" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*int8)(nil)).Elem()) + } + return &int8Codec{} + case reflect.Int16: + if typeName != "int16" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*int16)(nil)).Elem()) + } + return &int16Codec{} + case reflect.Int32: + if typeName != "int32" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*int32)(nil)).Elem()) + } + return &int32Codec{} + case reflect.Int64: + if typeName != "int64" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*int64)(nil)).Elem()) + } + return &int64Codec{} + case reflect.Uint: + if typeName != "uint" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*uint)(nil)).Elem()) + } + if strconv.IntSize == 32 { + return &uint32Codec{} + } + return &uint64Codec{} + case reflect.Uint8: + if typeName != "uint8" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*uint8)(nil)).Elem()) + } + return &uint8Codec{} + case reflect.Uint16: + if typeName != "uint16" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*uint16)(nil)).Elem()) + } + return &uint16Codec{} + case reflect.Uint32: + if typeName != "uint32" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*uint32)(nil)).Elem()) + } + return &uint32Codec{} + case reflect.Uintptr: + if typeName != "uintptr" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*uintptr)(nil)).Elem()) + } + if ptrSize == 32 { + return &uint32Codec{} + } + return &uint64Codec{} + case reflect.Uint64: + if typeName != "uint64" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*uint64)(nil)).Elem()) + } + return &uint64Codec{} + case reflect.Float32: + if typeName != "float32" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*float32)(nil)).Elem()) + } + return &float32Codec{} + case reflect.Float64: + if typeName != "float64" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*float64)(nil)).Elem()) + } + return &float64Codec{} + case reflect.Bool: + if typeName != "bool" { + return decoderOfType(ctx, reflect2.TypeOfPtr((*bool)(nil)).Elem()) + } + return &boolCodec{} + } + return nil +} + +type stringCodec struct { +} + +func (codec *stringCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { + *((*string)(ptr)) = iter.ReadString() +} + +func (codec *stringCodec) Encode(ptr unsafe.Pointer, stream *Stream) { + str := *((*string)(ptr)) + stream.WriteString(str) +} + +func (codec *stringCodec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*string)(ptr)) == "" +} + +type int8Codec struct { +} + +func (codec *int8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*int8)(ptr)) = iter.ReadInt8() + } +} + +func (codec *int8Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteInt8(*((*int8)(ptr))) +} + +func (codec *int8Codec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*int8)(ptr)) == 0 +} + +type int16Codec struct { +} + +func (codec *int16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*int16)(ptr)) = iter.ReadInt16() + } +} + +func (codec *int16Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteInt16(*((*int16)(ptr))) +} + +func (codec *int16Codec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*int16)(ptr)) == 0 +} + +type int32Codec struct { +} + +func (codec *int32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*int32)(ptr)) = iter.ReadInt32() + } +} + +func (codec *int32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteInt32(*((*int32)(ptr))) +} + +func (codec *int32Codec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*int32)(ptr)) == 0 +} + +type int64Codec struct { +} + +func (codec *int64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*int64)(ptr)) = iter.ReadInt64() + } +} + +func (codec *int64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteInt64(*((*int64)(ptr))) +} + +func (codec *int64Codec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*int64)(ptr)) == 0 +} + +type uint8Codec struct { +} + +func (codec *uint8Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*uint8)(ptr)) = iter.ReadUint8() + } +} + +func (codec *uint8Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteUint8(*((*uint8)(ptr))) +} + +func (codec *uint8Codec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*uint8)(ptr)) == 0 +} + +type uint16Codec struct { +} + +func (codec *uint16Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*uint16)(ptr)) = iter.ReadUint16() + } +} + +func (codec *uint16Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteUint16(*((*uint16)(ptr))) +} + +func (codec *uint16Codec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*uint16)(ptr)) == 0 +} + +type uint32Codec struct { +} + +func (codec *uint32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*uint32)(ptr)) = iter.ReadUint32() + } +} + +func (codec *uint32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteUint32(*((*uint32)(ptr))) +} + +func (codec *uint32Codec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*uint32)(ptr)) == 0 +} + +type uint64Codec struct { +} + +func (codec *uint64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*uint64)(ptr)) = iter.ReadUint64() + } +} + +func (codec *uint64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteUint64(*((*uint64)(ptr))) +} + +func (codec *uint64Codec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*uint64)(ptr)) == 0 +} + +type float32Codec struct { +} + +func (codec *float32Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*float32)(ptr)) = iter.ReadFloat32() + } +} + +func (codec *float32Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteFloat32(*((*float32)(ptr))) +} + +func (codec *float32Codec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*float32)(ptr)) == 0 +} + +type float64Codec struct { +} + +func (codec *float64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*float64)(ptr)) = iter.ReadFloat64() + } +} + +func (codec *float64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteFloat64(*((*float64)(ptr))) +} + +func (codec *float64Codec) IsEmpty(ptr unsafe.Pointer) bool { + return *((*float64)(ptr)) == 0 +} + +type boolCodec struct { +} + +func (codec *boolCodec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.ReadNil() { + *((*bool)(ptr)) = iter.ReadBool() + } +} + +func (codec *boolCodec) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteBool(*((*bool)(ptr))) +} + +func (codec *boolCodec) IsEmpty(ptr unsafe.Pointer) bool { + return !(*((*bool)(ptr))) +} + +type base64Codec struct { + sliceType *reflect2.UnsafeSliceType + sliceDecoder ValDecoder +} + +func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) { + if iter.ReadNil() { + codec.sliceType.UnsafeSetNil(ptr) + return + } + switch iter.WhatIsNext() { + case StringValue: + src := iter.ReadString() + dst, err := base64.StdEncoding.DecodeString(src) + if err != nil { + iter.ReportError("decode base64", err.Error()) + } else { + codec.sliceType.UnsafeSet(ptr, unsafe.Pointer(&dst)) + } + case ArrayValue: + codec.sliceDecoder.Decode(ptr, iter) + default: + iter.ReportError("base64Codec", "invalid input") + } +} + +func (codec *base64Codec) Encode(ptr unsafe.Pointer, stream *Stream) { + if codec.sliceType.UnsafeIsNil(ptr) { + stream.WriteNil() + return + } + src := *((*[]byte)(ptr)) + encoding := base64.StdEncoding + stream.writeByte('"') + if len(src) != 0 { + size := encoding.EncodedLen(len(src)) + buf := make([]byte, size) + encoding.Encode(buf, src) + stream.buf = append(stream.buf, buf...) + } + stream.writeByte('"') +} + +func (codec *base64Codec) IsEmpty(ptr unsafe.Pointer) bool { + return len(*((*[]byte)(ptr))) == 0 +} diff --git a/vendor/github.com/json-iterator/go/reflect_optional.go b/vendor/github.com/json-iterator/go/reflect_optional.go new file mode 100644 index 0000000000000..fa71f47489121 --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_optional.go @@ -0,0 +1,129 @@ +package jsoniter + +import ( + "github.com/modern-go/reflect2" + "unsafe" +) + +func decoderOfOptional(ctx *ctx, typ reflect2.Type) ValDecoder { + ptrType := typ.(*reflect2.UnsafePtrType) + elemType := ptrType.Elem() + decoder := decoderOfType(ctx, elemType) + return &OptionalDecoder{elemType, decoder} +} + +func encoderOfOptional(ctx *ctx, typ reflect2.Type) ValEncoder { + ptrType := typ.(*reflect2.UnsafePtrType) + elemType := ptrType.Elem() + elemEncoder := encoderOfType(ctx, elemType) + encoder := &OptionalEncoder{elemEncoder} + return encoder +} + +type OptionalDecoder struct { + ValueType reflect2.Type + ValueDecoder ValDecoder +} + +func (decoder *OptionalDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if iter.ReadNil() { + *((*unsafe.Pointer)(ptr)) = nil + } else { + if *((*unsafe.Pointer)(ptr)) == nil { + //pointer to null, we have to allocate memory to hold the value + newPtr := decoder.ValueType.UnsafeNew() + decoder.ValueDecoder.Decode(newPtr, iter) + *((*unsafe.Pointer)(ptr)) = newPtr + } else { + //reuse existing instance + decoder.ValueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter) + } + } +} + +type dereferenceDecoder struct { + // only to deference a pointer + valueType reflect2.Type + valueDecoder ValDecoder +} + +func (decoder *dereferenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if *((*unsafe.Pointer)(ptr)) == nil { + //pointer to null, we have to allocate memory to hold the value + newPtr := decoder.valueType.UnsafeNew() + decoder.valueDecoder.Decode(newPtr, iter) + *((*unsafe.Pointer)(ptr)) = newPtr + } else { + //reuse existing instance + decoder.valueDecoder.Decode(*((*unsafe.Pointer)(ptr)), iter) + } +} + +type OptionalEncoder struct { + ValueEncoder ValEncoder +} + +func (encoder *OptionalEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + if *((*unsafe.Pointer)(ptr)) == nil { + stream.WriteNil() + } else { + encoder.ValueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream) + } +} + +func (encoder *OptionalEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return *((*unsafe.Pointer)(ptr)) == nil +} + +type dereferenceEncoder struct { + ValueEncoder ValEncoder +} + +func (encoder *dereferenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + if *((*unsafe.Pointer)(ptr)) == nil { + stream.WriteNil() + } else { + encoder.ValueEncoder.Encode(*((*unsafe.Pointer)(ptr)), stream) + } +} + +func (encoder *dereferenceEncoder) IsEmpty(ptr unsafe.Pointer) bool { + dePtr := *((*unsafe.Pointer)(ptr)) + if dePtr == nil { + return true + } + return encoder.ValueEncoder.IsEmpty(dePtr) +} + +func (encoder *dereferenceEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool { + deReferenced := *((*unsafe.Pointer)(ptr)) + if deReferenced == nil { + return true + } + isEmbeddedPtrNil, converted := encoder.ValueEncoder.(IsEmbeddedPtrNil) + if !converted { + return false + } + fieldPtr := unsafe.Pointer(deReferenced) + return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr) +} + +type referenceEncoder struct { + encoder ValEncoder +} + +func (encoder *referenceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + encoder.encoder.Encode(unsafe.Pointer(&ptr), stream) +} + +func (encoder *referenceEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.encoder.IsEmpty(unsafe.Pointer(&ptr)) +} + +type referenceDecoder struct { + decoder ValDecoder +} + +func (decoder *referenceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + decoder.decoder.Decode(unsafe.Pointer(&ptr), iter) +} diff --git a/vendor/github.com/json-iterator/go/reflect_slice.go b/vendor/github.com/json-iterator/go/reflect_slice.go new file mode 100644 index 0000000000000..9441d79df33b4 --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_slice.go @@ -0,0 +1,99 @@ +package jsoniter + +import ( + "fmt" + "github.com/modern-go/reflect2" + "io" + "unsafe" +) + +func decoderOfSlice(ctx *ctx, typ reflect2.Type) ValDecoder { + sliceType := typ.(*reflect2.UnsafeSliceType) + decoder := decoderOfType(ctx.append("[sliceElem]"), sliceType.Elem()) + return &sliceDecoder{sliceType, decoder} +} + +func encoderOfSlice(ctx *ctx, typ reflect2.Type) ValEncoder { + sliceType := typ.(*reflect2.UnsafeSliceType) + encoder := encoderOfType(ctx.append("[sliceElem]"), sliceType.Elem()) + return &sliceEncoder{sliceType, encoder} +} + +type sliceEncoder struct { + sliceType *reflect2.UnsafeSliceType + elemEncoder ValEncoder +} + +func (encoder *sliceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + if encoder.sliceType.UnsafeIsNil(ptr) { + stream.WriteNil() + return + } + length := encoder.sliceType.UnsafeLengthOf(ptr) + if length == 0 { + stream.WriteEmptyArray() + return + } + stream.WriteArrayStart() + encoder.elemEncoder.Encode(encoder.sliceType.UnsafeGetIndex(ptr, 0), stream) + for i := 1; i < length; i++ { + stream.WriteMore() + elemPtr := encoder.sliceType.UnsafeGetIndex(ptr, i) + encoder.elemEncoder.Encode(elemPtr, stream) + } + stream.WriteArrayEnd() + if stream.Error != nil && stream.Error != io.EOF { + stream.Error = fmt.Errorf("%v: %s", encoder.sliceType, stream.Error.Error()) + } +} + +func (encoder *sliceEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.sliceType.UnsafeLengthOf(ptr) == 0 +} + +type sliceDecoder struct { + sliceType *reflect2.UnsafeSliceType + elemDecoder ValDecoder +} + +func (decoder *sliceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + decoder.doDecode(ptr, iter) + if iter.Error != nil && iter.Error != io.EOF { + iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error()) + } +} + +func (decoder *sliceDecoder) doDecode(ptr unsafe.Pointer, iter *Iterator) { + c := iter.nextToken() + sliceType := decoder.sliceType + if c == 'n' { + iter.skipThreeBytes('u', 'l', 'l') + sliceType.UnsafeSetNil(ptr) + return + } + if c != '[' { + iter.ReportError("decode slice", "expect [ or n, but found "+string([]byte{c})) + return + } + c = iter.nextToken() + if c == ']' { + sliceType.UnsafeSet(ptr, sliceType.UnsafeMakeSlice(0, 0)) + return + } + iter.unreadByte() + sliceType.UnsafeGrow(ptr, 1) + elemPtr := sliceType.UnsafeGetIndex(ptr, 0) + decoder.elemDecoder.Decode(elemPtr, iter) + length := 1 + for c = iter.nextToken(); c == ','; c = iter.nextToken() { + idx := length + length += 1 + sliceType.UnsafeGrow(ptr, length) + elemPtr = sliceType.UnsafeGetIndex(ptr, idx) + decoder.elemDecoder.Decode(elemPtr, iter) + } + if c != ']' { + iter.ReportError("decode slice", "expect ], but found "+string([]byte{c})) + return + } +} diff --git a/vendor/github.com/json-iterator/go/reflect_struct_decoder.go b/vendor/github.com/json-iterator/go/reflect_struct_decoder.go new file mode 100644 index 0000000000000..d7eb0eb5caa85 --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_struct_decoder.go @@ -0,0 +1,1092 @@ +package jsoniter + +import ( + "fmt" + "io" + "strings" + "unsafe" + + "github.com/modern-go/reflect2" +) + +func decoderOfStruct(ctx *ctx, typ reflect2.Type) ValDecoder { + bindings := map[string]*Binding{} + structDescriptor := describeStruct(ctx, typ) + for _, binding := range structDescriptor.Fields { + for _, fromName := range binding.FromNames { + old := bindings[fromName] + if old == nil { + bindings[fromName] = binding + continue + } + ignoreOld, ignoreNew := resolveConflictBinding(ctx.frozenConfig, old, binding) + if ignoreOld { + delete(bindings, fromName) + } + if !ignoreNew { + bindings[fromName] = binding + } + } + } + fields := map[string]*structFieldDecoder{} + for k, binding := range bindings { + fields[k] = binding.Decoder.(*structFieldDecoder) + } + + if !ctx.caseSensitive() { + for k, binding := range bindings { + if _, found := fields[strings.ToLower(k)]; !found { + fields[strings.ToLower(k)] = binding.Decoder.(*structFieldDecoder) + } + } + } + + return createStructDecoder(ctx, typ, fields) +} + +func createStructDecoder(ctx *ctx, typ reflect2.Type, fields map[string]*structFieldDecoder) ValDecoder { + if ctx.disallowUnknownFields { + return &generalStructDecoder{typ: typ, fields: fields, disallowUnknownFields: true} + } + knownHash := map[int64]struct{}{ + 0: {}, + } + + switch len(fields) { + case 0: + return &skipObjectDecoder{typ} + case 1: + for fieldName, fieldDecoder := range fields { + fieldHash := calcHash(fieldName, ctx.caseSensitive()) + _, known := knownHash[fieldHash] + if known { + return &generalStructDecoder{typ, fields, false} + } + knownHash[fieldHash] = struct{}{} + return &oneFieldStructDecoder{typ, fieldHash, fieldDecoder} + } + case 2: + var fieldHash1 int64 + var fieldHash2 int64 + var fieldDecoder1 *structFieldDecoder + var fieldDecoder2 *structFieldDecoder + for fieldName, fieldDecoder := range fields { + fieldHash := calcHash(fieldName, ctx.caseSensitive()) + _, known := knownHash[fieldHash] + if known { + return &generalStructDecoder{typ, fields, false} + } + knownHash[fieldHash] = struct{}{} + if fieldHash1 == 0 { + fieldHash1 = fieldHash + fieldDecoder1 = fieldDecoder + } else { + fieldHash2 = fieldHash + fieldDecoder2 = fieldDecoder + } + } + return &twoFieldsStructDecoder{typ, fieldHash1, fieldDecoder1, fieldHash2, fieldDecoder2} + case 3: + var fieldName1 int64 + var fieldName2 int64 + var fieldName3 int64 + var fieldDecoder1 *structFieldDecoder + var fieldDecoder2 *structFieldDecoder + var fieldDecoder3 *structFieldDecoder + for fieldName, fieldDecoder := range fields { + fieldHash := calcHash(fieldName, ctx.caseSensitive()) + _, known := knownHash[fieldHash] + if known { + return &generalStructDecoder{typ, fields, false} + } + knownHash[fieldHash] = struct{}{} + if fieldName1 == 0 { + fieldName1 = fieldHash + fieldDecoder1 = fieldDecoder + } else if fieldName2 == 0 { + fieldName2 = fieldHash + fieldDecoder2 = fieldDecoder + } else { + fieldName3 = fieldHash + fieldDecoder3 = fieldDecoder + } + } + return &threeFieldsStructDecoder{typ, + fieldName1, fieldDecoder1, + fieldName2, fieldDecoder2, + fieldName3, fieldDecoder3} + case 4: + var fieldName1 int64 + var fieldName2 int64 + var fieldName3 int64 + var fieldName4 int64 + var fieldDecoder1 *structFieldDecoder + var fieldDecoder2 *structFieldDecoder + var fieldDecoder3 *structFieldDecoder + var fieldDecoder4 *structFieldDecoder + for fieldName, fieldDecoder := range fields { + fieldHash := calcHash(fieldName, ctx.caseSensitive()) + _, known := knownHash[fieldHash] + if known { + return &generalStructDecoder{typ, fields, false} + } + knownHash[fieldHash] = struct{}{} + if fieldName1 == 0 { + fieldName1 = fieldHash + fieldDecoder1 = fieldDecoder + } else if fieldName2 == 0 { + fieldName2 = fieldHash + fieldDecoder2 = fieldDecoder + } else if fieldName3 == 0 { + fieldName3 = fieldHash + fieldDecoder3 = fieldDecoder + } else { + fieldName4 = fieldHash + fieldDecoder4 = fieldDecoder + } + } + return &fourFieldsStructDecoder{typ, + fieldName1, fieldDecoder1, + fieldName2, fieldDecoder2, + fieldName3, fieldDecoder3, + fieldName4, fieldDecoder4} + case 5: + var fieldName1 int64 + var fieldName2 int64 + var fieldName3 int64 + var fieldName4 int64 + var fieldName5 int64 + var fieldDecoder1 *structFieldDecoder + var fieldDecoder2 *structFieldDecoder + var fieldDecoder3 *structFieldDecoder + var fieldDecoder4 *structFieldDecoder + var fieldDecoder5 *structFieldDecoder + for fieldName, fieldDecoder := range fields { + fieldHash := calcHash(fieldName, ctx.caseSensitive()) + _, known := knownHash[fieldHash] + if known { + return &generalStructDecoder{typ, fields, false} + } + knownHash[fieldHash] = struct{}{} + if fieldName1 == 0 { + fieldName1 = fieldHash + fieldDecoder1 = fieldDecoder + } else if fieldName2 == 0 { + fieldName2 = fieldHash + fieldDecoder2 = fieldDecoder + } else if fieldName3 == 0 { + fieldName3 = fieldHash + fieldDecoder3 = fieldDecoder + } else if fieldName4 == 0 { + fieldName4 = fieldHash + fieldDecoder4 = fieldDecoder + } else { + fieldName5 = fieldHash + fieldDecoder5 = fieldDecoder + } + } + return &fiveFieldsStructDecoder{typ, + fieldName1, fieldDecoder1, + fieldName2, fieldDecoder2, + fieldName3, fieldDecoder3, + fieldName4, fieldDecoder4, + fieldName5, fieldDecoder5} + case 6: + var fieldName1 int64 + var fieldName2 int64 + var fieldName3 int64 + var fieldName4 int64 + var fieldName5 int64 + var fieldName6 int64 + var fieldDecoder1 *structFieldDecoder + var fieldDecoder2 *structFieldDecoder + var fieldDecoder3 *structFieldDecoder + var fieldDecoder4 *structFieldDecoder + var fieldDecoder5 *structFieldDecoder + var fieldDecoder6 *structFieldDecoder + for fieldName, fieldDecoder := range fields { + fieldHash := calcHash(fieldName, ctx.caseSensitive()) + _, known := knownHash[fieldHash] + if known { + return &generalStructDecoder{typ, fields, false} + } + knownHash[fieldHash] = struct{}{} + if fieldName1 == 0 { + fieldName1 = fieldHash + fieldDecoder1 = fieldDecoder + } else if fieldName2 == 0 { + fieldName2 = fieldHash + fieldDecoder2 = fieldDecoder + } else if fieldName3 == 0 { + fieldName3 = fieldHash + fieldDecoder3 = fieldDecoder + } else if fieldName4 == 0 { + fieldName4 = fieldHash + fieldDecoder4 = fieldDecoder + } else if fieldName5 == 0 { + fieldName5 = fieldHash + fieldDecoder5 = fieldDecoder + } else { + fieldName6 = fieldHash + fieldDecoder6 = fieldDecoder + } + } + return &sixFieldsStructDecoder{typ, + fieldName1, fieldDecoder1, + fieldName2, fieldDecoder2, + fieldName3, fieldDecoder3, + fieldName4, fieldDecoder4, + fieldName5, fieldDecoder5, + fieldName6, fieldDecoder6} + case 7: + var fieldName1 int64 + var fieldName2 int64 + var fieldName3 int64 + var fieldName4 int64 + var fieldName5 int64 + var fieldName6 int64 + var fieldName7 int64 + var fieldDecoder1 *structFieldDecoder + var fieldDecoder2 *structFieldDecoder + var fieldDecoder3 *structFieldDecoder + var fieldDecoder4 *structFieldDecoder + var fieldDecoder5 *structFieldDecoder + var fieldDecoder6 *structFieldDecoder + var fieldDecoder7 *structFieldDecoder + for fieldName, fieldDecoder := range fields { + fieldHash := calcHash(fieldName, ctx.caseSensitive()) + _, known := knownHash[fieldHash] + if known { + return &generalStructDecoder{typ, fields, false} + } + knownHash[fieldHash] = struct{}{} + if fieldName1 == 0 { + fieldName1 = fieldHash + fieldDecoder1 = fieldDecoder + } else if fieldName2 == 0 { + fieldName2 = fieldHash + fieldDecoder2 = fieldDecoder + } else if fieldName3 == 0 { + fieldName3 = fieldHash + fieldDecoder3 = fieldDecoder + } else if fieldName4 == 0 { + fieldName4 = fieldHash + fieldDecoder4 = fieldDecoder + } else if fieldName5 == 0 { + fieldName5 = fieldHash + fieldDecoder5 = fieldDecoder + } else if fieldName6 == 0 { + fieldName6 = fieldHash + fieldDecoder6 = fieldDecoder + } else { + fieldName7 = fieldHash + fieldDecoder7 = fieldDecoder + } + } + return &sevenFieldsStructDecoder{typ, + fieldName1, fieldDecoder1, + fieldName2, fieldDecoder2, + fieldName3, fieldDecoder3, + fieldName4, fieldDecoder4, + fieldName5, fieldDecoder5, + fieldName6, fieldDecoder6, + fieldName7, fieldDecoder7} + case 8: + var fieldName1 int64 + var fieldName2 int64 + var fieldName3 int64 + var fieldName4 int64 + var fieldName5 int64 + var fieldName6 int64 + var fieldName7 int64 + var fieldName8 int64 + var fieldDecoder1 *structFieldDecoder + var fieldDecoder2 *structFieldDecoder + var fieldDecoder3 *structFieldDecoder + var fieldDecoder4 *structFieldDecoder + var fieldDecoder5 *structFieldDecoder + var fieldDecoder6 *structFieldDecoder + var fieldDecoder7 *structFieldDecoder + var fieldDecoder8 *structFieldDecoder + for fieldName, fieldDecoder := range fields { + fieldHash := calcHash(fieldName, ctx.caseSensitive()) + _, known := knownHash[fieldHash] + if known { + return &generalStructDecoder{typ, fields, false} + } + knownHash[fieldHash] = struct{}{} + if fieldName1 == 0 { + fieldName1 = fieldHash + fieldDecoder1 = fieldDecoder + } else if fieldName2 == 0 { + fieldName2 = fieldHash + fieldDecoder2 = fieldDecoder + } else if fieldName3 == 0 { + fieldName3 = fieldHash + fieldDecoder3 = fieldDecoder + } else if fieldName4 == 0 { + fieldName4 = fieldHash + fieldDecoder4 = fieldDecoder + } else if fieldName5 == 0 { + fieldName5 = fieldHash + fieldDecoder5 = fieldDecoder + } else if fieldName6 == 0 { + fieldName6 = fieldHash + fieldDecoder6 = fieldDecoder + } else if fieldName7 == 0 { + fieldName7 = fieldHash + fieldDecoder7 = fieldDecoder + } else { + fieldName8 = fieldHash + fieldDecoder8 = fieldDecoder + } + } + return &eightFieldsStructDecoder{typ, + fieldName1, fieldDecoder1, + fieldName2, fieldDecoder2, + fieldName3, fieldDecoder3, + fieldName4, fieldDecoder4, + fieldName5, fieldDecoder5, + fieldName6, fieldDecoder6, + fieldName7, fieldDecoder7, + fieldName8, fieldDecoder8} + case 9: + var fieldName1 int64 + var fieldName2 int64 + var fieldName3 int64 + var fieldName4 int64 + var fieldName5 int64 + var fieldName6 int64 + var fieldName7 int64 + var fieldName8 int64 + var fieldName9 int64 + var fieldDecoder1 *structFieldDecoder + var fieldDecoder2 *structFieldDecoder + var fieldDecoder3 *structFieldDecoder + var fieldDecoder4 *structFieldDecoder + var fieldDecoder5 *structFieldDecoder + var fieldDecoder6 *structFieldDecoder + var fieldDecoder7 *structFieldDecoder + var fieldDecoder8 *structFieldDecoder + var fieldDecoder9 *structFieldDecoder + for fieldName, fieldDecoder := range fields { + fieldHash := calcHash(fieldName, ctx.caseSensitive()) + _, known := knownHash[fieldHash] + if known { + return &generalStructDecoder{typ, fields, false} + } + knownHash[fieldHash] = struct{}{} + if fieldName1 == 0 { + fieldName1 = fieldHash + fieldDecoder1 = fieldDecoder + } else if fieldName2 == 0 { + fieldName2 = fieldHash + fieldDecoder2 = fieldDecoder + } else if fieldName3 == 0 { + fieldName3 = fieldHash + fieldDecoder3 = fieldDecoder + } else if fieldName4 == 0 { + fieldName4 = fieldHash + fieldDecoder4 = fieldDecoder + } else if fieldName5 == 0 { + fieldName5 = fieldHash + fieldDecoder5 = fieldDecoder + } else if fieldName6 == 0 { + fieldName6 = fieldHash + fieldDecoder6 = fieldDecoder + } else if fieldName7 == 0 { + fieldName7 = fieldHash + fieldDecoder7 = fieldDecoder + } else if fieldName8 == 0 { + fieldName8 = fieldHash + fieldDecoder8 = fieldDecoder + } else { + fieldName9 = fieldHash + fieldDecoder9 = fieldDecoder + } + } + return &nineFieldsStructDecoder{typ, + fieldName1, fieldDecoder1, + fieldName2, fieldDecoder2, + fieldName3, fieldDecoder3, + fieldName4, fieldDecoder4, + fieldName5, fieldDecoder5, + fieldName6, fieldDecoder6, + fieldName7, fieldDecoder7, + fieldName8, fieldDecoder8, + fieldName9, fieldDecoder9} + case 10: + var fieldName1 int64 + var fieldName2 int64 + var fieldName3 int64 + var fieldName4 int64 + var fieldName5 int64 + var fieldName6 int64 + var fieldName7 int64 + var fieldName8 int64 + var fieldName9 int64 + var fieldName10 int64 + var fieldDecoder1 *structFieldDecoder + var fieldDecoder2 *structFieldDecoder + var fieldDecoder3 *structFieldDecoder + var fieldDecoder4 *structFieldDecoder + var fieldDecoder5 *structFieldDecoder + var fieldDecoder6 *structFieldDecoder + var fieldDecoder7 *structFieldDecoder + var fieldDecoder8 *structFieldDecoder + var fieldDecoder9 *structFieldDecoder + var fieldDecoder10 *structFieldDecoder + for fieldName, fieldDecoder := range fields { + fieldHash := calcHash(fieldName, ctx.caseSensitive()) + _, known := knownHash[fieldHash] + if known { + return &generalStructDecoder{typ, fields, false} + } + knownHash[fieldHash] = struct{}{} + if fieldName1 == 0 { + fieldName1 = fieldHash + fieldDecoder1 = fieldDecoder + } else if fieldName2 == 0 { + fieldName2 = fieldHash + fieldDecoder2 = fieldDecoder + } else if fieldName3 == 0 { + fieldName3 = fieldHash + fieldDecoder3 = fieldDecoder + } else if fieldName4 == 0 { + fieldName4 = fieldHash + fieldDecoder4 = fieldDecoder + } else if fieldName5 == 0 { + fieldName5 = fieldHash + fieldDecoder5 = fieldDecoder + } else if fieldName6 == 0 { + fieldName6 = fieldHash + fieldDecoder6 = fieldDecoder + } else if fieldName7 == 0 { + fieldName7 = fieldHash + fieldDecoder7 = fieldDecoder + } else if fieldName8 == 0 { + fieldName8 = fieldHash + fieldDecoder8 = fieldDecoder + } else if fieldName9 == 0 { + fieldName9 = fieldHash + fieldDecoder9 = fieldDecoder + } else { + fieldName10 = fieldHash + fieldDecoder10 = fieldDecoder + } + } + return &tenFieldsStructDecoder{typ, + fieldName1, fieldDecoder1, + fieldName2, fieldDecoder2, + fieldName3, fieldDecoder3, + fieldName4, fieldDecoder4, + fieldName5, fieldDecoder5, + fieldName6, fieldDecoder6, + fieldName7, fieldDecoder7, + fieldName8, fieldDecoder8, + fieldName9, fieldDecoder9, + fieldName10, fieldDecoder10} + } + return &generalStructDecoder{typ, fields, false} +} + +type generalStructDecoder struct { + typ reflect2.Type + fields map[string]*structFieldDecoder + disallowUnknownFields bool +} + +func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + var c byte + for c = ','; c == ','; c = iter.nextToken() { + decoder.decodeOneField(ptr, iter) + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + if c != '}' { + iter.ReportError("struct Decode", `expect }, but found `+string([]byte{c})) + } + iter.decrementDepth() +} + +func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator) { + var field string + var fieldDecoder *structFieldDecoder + if iter.cfg.objectFieldMustBeSimpleString { + fieldBytes := iter.ReadStringAsSlice() + field = *(*string)(unsafe.Pointer(&fieldBytes)) + fieldDecoder = decoder.fields[field] + if fieldDecoder == nil && !iter.cfg.caseSensitive { + fieldDecoder = decoder.fields[strings.ToLower(field)] + } + } else { + field = iter.ReadString() + fieldDecoder = decoder.fields[field] + if fieldDecoder == nil && !iter.cfg.caseSensitive { + fieldDecoder = decoder.fields[strings.ToLower(field)] + } + } + if fieldDecoder == nil { + if decoder.disallowUnknownFields { + msg := "found unknown field: " + field + iter.ReportError("ReadObject", msg) + } + c := iter.nextToken() + if c != ':' { + iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) + } + iter.Skip() + return + } + c := iter.nextToken() + if c != ':' { + iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c})) + } + fieldDecoder.Decode(ptr, iter) +} + +type skipObjectDecoder struct { + typ reflect2.Type +} + +func (decoder *skipObjectDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + valueType := iter.WhatIsNext() + if valueType != ObjectValue && valueType != NilValue { + iter.ReportError("skipObjectDecoder", "expect object or null") + return + } + iter.Skip() +} + +type oneFieldStructDecoder struct { + typ reflect2.Type + fieldHash int64 + fieldDecoder *structFieldDecoder +} + +func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + for { + if iter.readFieldHash() == decoder.fieldHash { + decoder.fieldDecoder.Decode(ptr, iter) + } else { + iter.Skip() + } + if iter.isObjectEnd() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + iter.decrementDepth() +} + +type twoFieldsStructDecoder struct { + typ reflect2.Type + fieldHash1 int64 + fieldDecoder1 *structFieldDecoder + fieldHash2 int64 + fieldDecoder2 *structFieldDecoder +} + +func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + for { + switch iter.readFieldHash() { + case decoder.fieldHash1: + decoder.fieldDecoder1.Decode(ptr, iter) + case decoder.fieldHash2: + decoder.fieldDecoder2.Decode(ptr, iter) + default: + iter.Skip() + } + if iter.isObjectEnd() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + iter.decrementDepth() +} + +type threeFieldsStructDecoder struct { + typ reflect2.Type + fieldHash1 int64 + fieldDecoder1 *structFieldDecoder + fieldHash2 int64 + fieldDecoder2 *structFieldDecoder + fieldHash3 int64 + fieldDecoder3 *structFieldDecoder +} + +func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + for { + switch iter.readFieldHash() { + case decoder.fieldHash1: + decoder.fieldDecoder1.Decode(ptr, iter) + case decoder.fieldHash2: + decoder.fieldDecoder2.Decode(ptr, iter) + case decoder.fieldHash3: + decoder.fieldDecoder3.Decode(ptr, iter) + default: + iter.Skip() + } + if iter.isObjectEnd() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + iter.decrementDepth() +} + +type fourFieldsStructDecoder struct { + typ reflect2.Type + fieldHash1 int64 + fieldDecoder1 *structFieldDecoder + fieldHash2 int64 + fieldDecoder2 *structFieldDecoder + fieldHash3 int64 + fieldDecoder3 *structFieldDecoder + fieldHash4 int64 + fieldDecoder4 *structFieldDecoder +} + +func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + for { + switch iter.readFieldHash() { + case decoder.fieldHash1: + decoder.fieldDecoder1.Decode(ptr, iter) + case decoder.fieldHash2: + decoder.fieldDecoder2.Decode(ptr, iter) + case decoder.fieldHash3: + decoder.fieldDecoder3.Decode(ptr, iter) + case decoder.fieldHash4: + decoder.fieldDecoder4.Decode(ptr, iter) + default: + iter.Skip() + } + if iter.isObjectEnd() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + iter.decrementDepth() +} + +type fiveFieldsStructDecoder struct { + typ reflect2.Type + fieldHash1 int64 + fieldDecoder1 *structFieldDecoder + fieldHash2 int64 + fieldDecoder2 *structFieldDecoder + fieldHash3 int64 + fieldDecoder3 *structFieldDecoder + fieldHash4 int64 + fieldDecoder4 *structFieldDecoder + fieldHash5 int64 + fieldDecoder5 *structFieldDecoder +} + +func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + for { + switch iter.readFieldHash() { + case decoder.fieldHash1: + decoder.fieldDecoder1.Decode(ptr, iter) + case decoder.fieldHash2: + decoder.fieldDecoder2.Decode(ptr, iter) + case decoder.fieldHash3: + decoder.fieldDecoder3.Decode(ptr, iter) + case decoder.fieldHash4: + decoder.fieldDecoder4.Decode(ptr, iter) + case decoder.fieldHash5: + decoder.fieldDecoder5.Decode(ptr, iter) + default: + iter.Skip() + } + if iter.isObjectEnd() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + iter.decrementDepth() +} + +type sixFieldsStructDecoder struct { + typ reflect2.Type + fieldHash1 int64 + fieldDecoder1 *structFieldDecoder + fieldHash2 int64 + fieldDecoder2 *structFieldDecoder + fieldHash3 int64 + fieldDecoder3 *structFieldDecoder + fieldHash4 int64 + fieldDecoder4 *structFieldDecoder + fieldHash5 int64 + fieldDecoder5 *structFieldDecoder + fieldHash6 int64 + fieldDecoder6 *structFieldDecoder +} + +func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + for { + switch iter.readFieldHash() { + case decoder.fieldHash1: + decoder.fieldDecoder1.Decode(ptr, iter) + case decoder.fieldHash2: + decoder.fieldDecoder2.Decode(ptr, iter) + case decoder.fieldHash3: + decoder.fieldDecoder3.Decode(ptr, iter) + case decoder.fieldHash4: + decoder.fieldDecoder4.Decode(ptr, iter) + case decoder.fieldHash5: + decoder.fieldDecoder5.Decode(ptr, iter) + case decoder.fieldHash6: + decoder.fieldDecoder6.Decode(ptr, iter) + default: + iter.Skip() + } + if iter.isObjectEnd() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + iter.decrementDepth() +} + +type sevenFieldsStructDecoder struct { + typ reflect2.Type + fieldHash1 int64 + fieldDecoder1 *structFieldDecoder + fieldHash2 int64 + fieldDecoder2 *structFieldDecoder + fieldHash3 int64 + fieldDecoder3 *structFieldDecoder + fieldHash4 int64 + fieldDecoder4 *structFieldDecoder + fieldHash5 int64 + fieldDecoder5 *structFieldDecoder + fieldHash6 int64 + fieldDecoder6 *structFieldDecoder + fieldHash7 int64 + fieldDecoder7 *structFieldDecoder +} + +func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + for { + switch iter.readFieldHash() { + case decoder.fieldHash1: + decoder.fieldDecoder1.Decode(ptr, iter) + case decoder.fieldHash2: + decoder.fieldDecoder2.Decode(ptr, iter) + case decoder.fieldHash3: + decoder.fieldDecoder3.Decode(ptr, iter) + case decoder.fieldHash4: + decoder.fieldDecoder4.Decode(ptr, iter) + case decoder.fieldHash5: + decoder.fieldDecoder5.Decode(ptr, iter) + case decoder.fieldHash6: + decoder.fieldDecoder6.Decode(ptr, iter) + case decoder.fieldHash7: + decoder.fieldDecoder7.Decode(ptr, iter) + default: + iter.Skip() + } + if iter.isObjectEnd() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + iter.decrementDepth() +} + +type eightFieldsStructDecoder struct { + typ reflect2.Type + fieldHash1 int64 + fieldDecoder1 *structFieldDecoder + fieldHash2 int64 + fieldDecoder2 *structFieldDecoder + fieldHash3 int64 + fieldDecoder3 *structFieldDecoder + fieldHash4 int64 + fieldDecoder4 *structFieldDecoder + fieldHash5 int64 + fieldDecoder5 *structFieldDecoder + fieldHash6 int64 + fieldDecoder6 *structFieldDecoder + fieldHash7 int64 + fieldDecoder7 *structFieldDecoder + fieldHash8 int64 + fieldDecoder8 *structFieldDecoder +} + +func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + for { + switch iter.readFieldHash() { + case decoder.fieldHash1: + decoder.fieldDecoder1.Decode(ptr, iter) + case decoder.fieldHash2: + decoder.fieldDecoder2.Decode(ptr, iter) + case decoder.fieldHash3: + decoder.fieldDecoder3.Decode(ptr, iter) + case decoder.fieldHash4: + decoder.fieldDecoder4.Decode(ptr, iter) + case decoder.fieldHash5: + decoder.fieldDecoder5.Decode(ptr, iter) + case decoder.fieldHash6: + decoder.fieldDecoder6.Decode(ptr, iter) + case decoder.fieldHash7: + decoder.fieldDecoder7.Decode(ptr, iter) + case decoder.fieldHash8: + decoder.fieldDecoder8.Decode(ptr, iter) + default: + iter.Skip() + } + if iter.isObjectEnd() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + iter.decrementDepth() +} + +type nineFieldsStructDecoder struct { + typ reflect2.Type + fieldHash1 int64 + fieldDecoder1 *structFieldDecoder + fieldHash2 int64 + fieldDecoder2 *structFieldDecoder + fieldHash3 int64 + fieldDecoder3 *structFieldDecoder + fieldHash4 int64 + fieldDecoder4 *structFieldDecoder + fieldHash5 int64 + fieldDecoder5 *structFieldDecoder + fieldHash6 int64 + fieldDecoder6 *structFieldDecoder + fieldHash7 int64 + fieldDecoder7 *structFieldDecoder + fieldHash8 int64 + fieldDecoder8 *structFieldDecoder + fieldHash9 int64 + fieldDecoder9 *structFieldDecoder +} + +func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + for { + switch iter.readFieldHash() { + case decoder.fieldHash1: + decoder.fieldDecoder1.Decode(ptr, iter) + case decoder.fieldHash2: + decoder.fieldDecoder2.Decode(ptr, iter) + case decoder.fieldHash3: + decoder.fieldDecoder3.Decode(ptr, iter) + case decoder.fieldHash4: + decoder.fieldDecoder4.Decode(ptr, iter) + case decoder.fieldHash5: + decoder.fieldDecoder5.Decode(ptr, iter) + case decoder.fieldHash6: + decoder.fieldDecoder6.Decode(ptr, iter) + case decoder.fieldHash7: + decoder.fieldDecoder7.Decode(ptr, iter) + case decoder.fieldHash8: + decoder.fieldDecoder8.Decode(ptr, iter) + case decoder.fieldHash9: + decoder.fieldDecoder9.Decode(ptr, iter) + default: + iter.Skip() + } + if iter.isObjectEnd() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + iter.decrementDepth() +} + +type tenFieldsStructDecoder struct { + typ reflect2.Type + fieldHash1 int64 + fieldDecoder1 *structFieldDecoder + fieldHash2 int64 + fieldDecoder2 *structFieldDecoder + fieldHash3 int64 + fieldDecoder3 *structFieldDecoder + fieldHash4 int64 + fieldDecoder4 *structFieldDecoder + fieldHash5 int64 + fieldDecoder5 *structFieldDecoder + fieldHash6 int64 + fieldDecoder6 *structFieldDecoder + fieldHash7 int64 + fieldDecoder7 *structFieldDecoder + fieldHash8 int64 + fieldDecoder8 *structFieldDecoder + fieldHash9 int64 + fieldDecoder9 *structFieldDecoder + fieldHash10 int64 + fieldDecoder10 *structFieldDecoder +} + +func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + if !iter.readObjectStart() { + return + } + if !iter.incrementDepth() { + return + } + for { + switch iter.readFieldHash() { + case decoder.fieldHash1: + decoder.fieldDecoder1.Decode(ptr, iter) + case decoder.fieldHash2: + decoder.fieldDecoder2.Decode(ptr, iter) + case decoder.fieldHash3: + decoder.fieldDecoder3.Decode(ptr, iter) + case decoder.fieldHash4: + decoder.fieldDecoder4.Decode(ptr, iter) + case decoder.fieldHash5: + decoder.fieldDecoder5.Decode(ptr, iter) + case decoder.fieldHash6: + decoder.fieldDecoder6.Decode(ptr, iter) + case decoder.fieldHash7: + decoder.fieldDecoder7.Decode(ptr, iter) + case decoder.fieldHash8: + decoder.fieldDecoder8.Decode(ptr, iter) + case decoder.fieldHash9: + decoder.fieldDecoder9.Decode(ptr, iter) + case decoder.fieldHash10: + decoder.fieldDecoder10.Decode(ptr, iter) + default: + iter.Skip() + } + if iter.isObjectEnd() { + break + } + } + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { + iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + } + iter.decrementDepth() +} + +type structFieldDecoder struct { + field reflect2.StructField + fieldDecoder ValDecoder +} + +func (decoder *structFieldDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + fieldPtr := decoder.field.UnsafeGet(ptr) + decoder.fieldDecoder.Decode(fieldPtr, iter) + if iter.Error != nil && iter.Error != io.EOF { + iter.Error = fmt.Errorf("%s: %s", decoder.field.Name(), iter.Error.Error()) + } +} + +type stringModeStringDecoder struct { + elemDecoder ValDecoder + cfg *frozenConfig +} + +func (decoder *stringModeStringDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + decoder.elemDecoder.Decode(ptr, iter) + str := *((*string)(ptr)) + tempIter := decoder.cfg.BorrowIterator([]byte(str)) + defer decoder.cfg.ReturnIterator(tempIter) + *((*string)(ptr)) = tempIter.ReadString() +} + +type stringModeNumberDecoder struct { + elemDecoder ValDecoder +} + +func (decoder *stringModeNumberDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { + c := iter.nextToken() + if c != '"' { + iter.ReportError("stringModeNumberDecoder", `expect ", but found `+string([]byte{c})) + return + } + decoder.elemDecoder.Decode(ptr, iter) + if iter.Error != nil { + return + } + c = iter.readByte() + if c != '"' { + iter.ReportError("stringModeNumberDecoder", `expect ", but found `+string([]byte{c})) + return + } +} diff --git a/vendor/github.com/json-iterator/go/reflect_struct_encoder.go b/vendor/github.com/json-iterator/go/reflect_struct_encoder.go new file mode 100644 index 0000000000000..152e3ef5a93c6 --- /dev/null +++ b/vendor/github.com/json-iterator/go/reflect_struct_encoder.go @@ -0,0 +1,211 @@ +package jsoniter + +import ( + "fmt" + "github.com/modern-go/reflect2" + "io" + "reflect" + "unsafe" +) + +func encoderOfStruct(ctx *ctx, typ reflect2.Type) ValEncoder { + type bindingTo struct { + binding *Binding + toName string + ignored bool + } + orderedBindings := []*bindingTo{} + structDescriptor := describeStruct(ctx, typ) + for _, binding := range structDescriptor.Fields { + for _, toName := range binding.ToNames { + new := &bindingTo{ + binding: binding, + toName: toName, + } + for _, old := range orderedBindings { + if old.toName != toName { + continue + } + old.ignored, new.ignored = resolveConflictBinding(ctx.frozenConfig, old.binding, new.binding) + } + orderedBindings = append(orderedBindings, new) + } + } + if len(orderedBindings) == 0 { + return &emptyStructEncoder{} + } + finalOrderedFields := []structFieldTo{} + for _, bindingTo := range orderedBindings { + if !bindingTo.ignored { + finalOrderedFields = append(finalOrderedFields, structFieldTo{ + encoder: bindingTo.binding.Encoder.(*structFieldEncoder), + toName: bindingTo.toName, + }) + } + } + return &structEncoder{typ, finalOrderedFields} +} + +func createCheckIsEmpty(ctx *ctx, typ reflect2.Type) checkIsEmpty { + encoder := createEncoderOfNative(ctx, typ) + if encoder != nil { + return encoder + } + kind := typ.Kind() + switch kind { + case reflect.Interface: + return &dynamicEncoder{typ} + case reflect.Struct: + return &structEncoder{typ: typ} + case reflect.Array: + return &arrayEncoder{} + case reflect.Slice: + return &sliceEncoder{} + case reflect.Map: + return encoderOfMap(ctx, typ) + case reflect.Ptr: + return &OptionalEncoder{} + default: + return &lazyErrorEncoder{err: fmt.Errorf("unsupported type: %v", typ)} + } +} + +func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) { + newTagged := new.Field.Tag().Get(cfg.getTagKey()) != "" + oldTagged := old.Field.Tag().Get(cfg.getTagKey()) != "" + if newTagged { + if oldTagged { + if len(old.levels) > len(new.levels) { + return true, false + } else if len(new.levels) > len(old.levels) { + return false, true + } else { + return true, true + } + } else { + return true, false + } + } else { + if oldTagged { + return true, false + } + if len(old.levels) > len(new.levels) { + return true, false + } else if len(new.levels) > len(old.levels) { + return false, true + } else { + return true, true + } + } +} + +type structFieldEncoder struct { + field reflect2.StructField + fieldEncoder ValEncoder + omitempty bool +} + +func (encoder *structFieldEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + fieldPtr := encoder.field.UnsafeGet(ptr) + encoder.fieldEncoder.Encode(fieldPtr, stream) + if stream.Error != nil && stream.Error != io.EOF { + stream.Error = fmt.Errorf("%s: %s", encoder.field.Name(), stream.Error.Error()) + } +} + +func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool { + fieldPtr := encoder.field.UnsafeGet(ptr) + return encoder.fieldEncoder.IsEmpty(fieldPtr) +} + +func (encoder *structFieldEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool { + isEmbeddedPtrNil, converted := encoder.fieldEncoder.(IsEmbeddedPtrNil) + if !converted { + return false + } + fieldPtr := encoder.field.UnsafeGet(ptr) + return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr) +} + +type IsEmbeddedPtrNil interface { + IsEmbeddedPtrNil(ptr unsafe.Pointer) bool +} + +type structEncoder struct { + typ reflect2.Type + fields []structFieldTo +} + +type structFieldTo struct { + encoder *structFieldEncoder + toName string +} + +func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteObjectStart() + isNotFirst := false + for _, field := range encoder.fields { + if field.encoder.omitempty && field.encoder.IsEmpty(ptr) { + continue + } + if field.encoder.IsEmbeddedPtrNil(ptr) { + continue + } + if isNotFirst { + stream.WriteMore() + } + stream.WriteObjectField(field.toName) + field.encoder.Encode(ptr, stream) + isNotFirst = true + } + stream.WriteObjectEnd() + if stream.Error != nil && stream.Error != io.EOF { + stream.Error = fmt.Errorf("%v.%s", encoder.typ, stream.Error.Error()) + } +} + +func (encoder *structEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return false +} + +type emptyStructEncoder struct { +} + +func (encoder *emptyStructEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.WriteEmptyObject() +} + +func (encoder *emptyStructEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return false +} + +type stringModeNumberEncoder struct { + elemEncoder ValEncoder +} + +func (encoder *stringModeNumberEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + stream.writeByte('"') + encoder.elemEncoder.Encode(ptr, stream) + stream.writeByte('"') +} + +func (encoder *stringModeNumberEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.elemEncoder.IsEmpty(ptr) +} + +type stringModeStringEncoder struct { + elemEncoder ValEncoder + cfg *frozenConfig +} + +func (encoder *stringModeStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { + tempStream := encoder.cfg.BorrowStream(nil) + tempStream.Attachment = stream.Attachment + defer encoder.cfg.ReturnStream(tempStream) + encoder.elemEncoder.Encode(ptr, tempStream) + stream.WriteString(string(tempStream.Buffer())) +} + +func (encoder *stringModeStringEncoder) IsEmpty(ptr unsafe.Pointer) bool { + return encoder.elemEncoder.IsEmpty(ptr) +} diff --git a/vendor/github.com/json-iterator/go/stream.go b/vendor/github.com/json-iterator/go/stream.go new file mode 100644 index 0000000000000..23d8a3ad6b126 --- /dev/null +++ b/vendor/github.com/json-iterator/go/stream.go @@ -0,0 +1,210 @@ +package jsoniter + +import ( + "io" +) + +// stream is a io.Writer like object, with JSON specific write functions. +// Error is not returned as return value, but stored as Error member on this stream instance. +type Stream struct { + cfg *frozenConfig + out io.Writer + buf []byte + Error error + indention int + Attachment interface{} // open for customized encoder +} + +// NewStream create new stream instance. +// cfg can be jsoniter.ConfigDefault. +// out can be nil if write to internal buffer. +// bufSize is the initial size for the internal buffer in bytes. +func NewStream(cfg API, out io.Writer, bufSize int) *Stream { + return &Stream{ + cfg: cfg.(*frozenConfig), + out: out, + buf: make([]byte, 0, bufSize), + Error: nil, + indention: 0, + } +} + +// Pool returns a pool can provide more stream with same configuration +func (stream *Stream) Pool() StreamPool { + return stream.cfg +} + +// Reset reuse this stream instance by assign a new writer +func (stream *Stream) Reset(out io.Writer) { + stream.out = out + stream.buf = stream.buf[:0] +} + +// Available returns how many bytes are unused in the buffer. +func (stream *Stream) Available() int { + return cap(stream.buf) - len(stream.buf) +} + +// Buffered returns the number of bytes that have been written into the current buffer. +func (stream *Stream) Buffered() int { + return len(stream.buf) +} + +// Buffer if writer is nil, use this method to take the result +func (stream *Stream) Buffer() []byte { + return stream.buf +} + +// SetBuffer allows to append to the internal buffer directly +func (stream *Stream) SetBuffer(buf []byte) { + stream.buf = buf +} + +// Write writes the contents of p into the buffer. +// It returns the number of bytes written. +// If nn < len(p), it also returns an error explaining +// why the write is short. +func (stream *Stream) Write(p []byte) (nn int, err error) { + stream.buf = append(stream.buf, p...) + if stream.out != nil { + nn, err = stream.out.Write(stream.buf) + stream.buf = stream.buf[nn:] + return + } + return len(p), nil +} + +// WriteByte writes a single byte. +func (stream *Stream) writeByte(c byte) { + stream.buf = append(stream.buf, c) +} + +func (stream *Stream) writeTwoBytes(c1 byte, c2 byte) { + stream.buf = append(stream.buf, c1, c2) +} + +func (stream *Stream) writeThreeBytes(c1 byte, c2 byte, c3 byte) { + stream.buf = append(stream.buf, c1, c2, c3) +} + +func (stream *Stream) writeFourBytes(c1 byte, c2 byte, c3 byte, c4 byte) { + stream.buf = append(stream.buf, c1, c2, c3, c4) +} + +func (stream *Stream) writeFiveBytes(c1 byte, c2 byte, c3 byte, c4 byte, c5 byte) { + stream.buf = append(stream.buf, c1, c2, c3, c4, c5) +} + +// Flush writes any buffered data to the underlying io.Writer. +func (stream *Stream) Flush() error { + if stream.out == nil { + return nil + } + if stream.Error != nil { + return stream.Error + } + _, err := stream.out.Write(stream.buf) + if err != nil { + if stream.Error == nil { + stream.Error = err + } + return err + } + stream.buf = stream.buf[:0] + return nil +} + +// WriteRaw write string out without quotes, just like []byte +func (stream *Stream) WriteRaw(s string) { + stream.buf = append(stream.buf, s...) +} + +// WriteNil write null to stream +func (stream *Stream) WriteNil() { + stream.writeFourBytes('n', 'u', 'l', 'l') +} + +// WriteTrue write true to stream +func (stream *Stream) WriteTrue() { + stream.writeFourBytes('t', 'r', 'u', 'e') +} + +// WriteFalse write false to stream +func (stream *Stream) WriteFalse() { + stream.writeFiveBytes('f', 'a', 'l', 's', 'e') +} + +// WriteBool write true or false into stream +func (stream *Stream) WriteBool(val bool) { + if val { + stream.WriteTrue() + } else { + stream.WriteFalse() + } +} + +// WriteObjectStart write { with possible indention +func (stream *Stream) WriteObjectStart() { + stream.indention += stream.cfg.indentionStep + stream.writeByte('{') + stream.writeIndention(0) +} + +// WriteObjectField write "field": with possible indention +func (stream *Stream) WriteObjectField(field string) { + stream.WriteString(field) + if stream.indention > 0 { + stream.writeTwoBytes(':', ' ') + } else { + stream.writeByte(':') + } +} + +// WriteObjectEnd write } with possible indention +func (stream *Stream) WriteObjectEnd() { + stream.writeIndention(stream.cfg.indentionStep) + stream.indention -= stream.cfg.indentionStep + stream.writeByte('}') +} + +// WriteEmptyObject write {} +func (stream *Stream) WriteEmptyObject() { + stream.writeByte('{') + stream.writeByte('}') +} + +// WriteMore write , with possible indention +func (stream *Stream) WriteMore() { + stream.writeByte(',') + stream.writeIndention(0) +} + +// WriteArrayStart write [ with possible indention +func (stream *Stream) WriteArrayStart() { + stream.indention += stream.cfg.indentionStep + stream.writeByte('[') + stream.writeIndention(0) +} + +// WriteEmptyArray write [] +func (stream *Stream) WriteEmptyArray() { + stream.writeTwoBytes('[', ']') +} + +// WriteArrayEnd write ] with possible indention +func (stream *Stream) WriteArrayEnd() { + stream.writeIndention(stream.cfg.indentionStep) + stream.indention -= stream.cfg.indentionStep + stream.writeByte(']') +} + +func (stream *Stream) writeIndention(delta int) { + if stream.indention == 0 { + return + } + stream.writeByte('\n') + toWrite := stream.indention - delta + for i := 0; i < toWrite; i++ { + stream.buf = append(stream.buf, ' ') + } +} diff --git a/vendor/github.com/json-iterator/go/stream_float.go b/vendor/github.com/json-iterator/go/stream_float.go new file mode 100644 index 0000000000000..826aa594ac6f3 --- /dev/null +++ b/vendor/github.com/json-iterator/go/stream_float.go @@ -0,0 +1,111 @@ +package jsoniter + +import ( + "fmt" + "math" + "strconv" +) + +var pow10 []uint64 + +func init() { + pow10 = []uint64{1, 10, 100, 1000, 10000, 100000, 1000000} +} + +// WriteFloat32 write float32 to stream +func (stream *Stream) WriteFloat32(val float32) { + if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { + stream.Error = fmt.Errorf("unsupported value: %f", val) + return + } + abs := math.Abs(float64(val)) + fmt := byte('f') + // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. + if abs != 0 { + if float32(abs) < 1e-6 || float32(abs) >= 1e21 { + fmt = 'e' + } + } + stream.buf = strconv.AppendFloat(stream.buf, float64(val), fmt, -1, 32) +} + +// WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster +func (stream *Stream) WriteFloat32Lossy(val float32) { + if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { + stream.Error = fmt.Errorf("unsupported value: %f", val) + return + } + if val < 0 { + stream.writeByte('-') + val = -val + } + if val > 0x4ffffff { + stream.WriteFloat32(val) + return + } + precision := 6 + exp := uint64(1000000) // 6 + lval := uint64(float64(val)*float64(exp) + 0.5) + stream.WriteUint64(lval / exp) + fval := lval % exp + if fval == 0 { + return + } + stream.writeByte('.') + for p := precision - 1; p > 0 && fval < pow10[p]; p-- { + stream.writeByte('0') + } + stream.WriteUint64(fval) + for stream.buf[len(stream.buf)-1] == '0' { + stream.buf = stream.buf[:len(stream.buf)-1] + } +} + +// WriteFloat64 write float64 to stream +func (stream *Stream) WriteFloat64(val float64) { + if math.IsInf(val, 0) || math.IsNaN(val) { + stream.Error = fmt.Errorf("unsupported value: %f", val) + return + } + abs := math.Abs(val) + fmt := byte('f') + // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right. + if abs != 0 { + if abs < 1e-6 || abs >= 1e21 { + fmt = 'e' + } + } + stream.buf = strconv.AppendFloat(stream.buf, float64(val), fmt, -1, 64) +} + +// WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster +func (stream *Stream) WriteFloat64Lossy(val float64) { + if math.IsInf(val, 0) || math.IsNaN(val) { + stream.Error = fmt.Errorf("unsupported value: %f", val) + return + } + if val < 0 { + stream.writeByte('-') + val = -val + } + if val > 0x4ffffff { + stream.WriteFloat64(val) + return + } + precision := 6 + exp := uint64(1000000) // 6 + lval := uint64(val*float64(exp) + 0.5) + stream.WriteUint64(lval / exp) + fval := lval % exp + if fval == 0 { + return + } + stream.writeByte('.') + for p := precision - 1; p > 0 && fval < pow10[p]; p-- { + stream.writeByte('0') + } + stream.WriteUint64(fval) + for stream.buf[len(stream.buf)-1] == '0' { + stream.buf = stream.buf[:len(stream.buf)-1] + } +} diff --git a/vendor/github.com/json-iterator/go/stream_int.go b/vendor/github.com/json-iterator/go/stream_int.go new file mode 100644 index 0000000000000..d1059ee4c20e3 --- /dev/null +++ b/vendor/github.com/json-iterator/go/stream_int.go @@ -0,0 +1,190 @@ +package jsoniter + +var digits []uint32 + +func init() { + digits = make([]uint32, 1000) + for i := uint32(0); i < 1000; i++ { + digits[i] = (((i / 100) + '0') << 16) + ((((i / 10) % 10) + '0') << 8) + i%10 + '0' + if i < 10 { + digits[i] += 2 << 24 + } else if i < 100 { + digits[i] += 1 << 24 + } + } +} + +func writeFirstBuf(space []byte, v uint32) []byte { + start := v >> 24 + if start == 0 { + space = append(space, byte(v>>16), byte(v>>8)) + } else if start == 1 { + space = append(space, byte(v>>8)) + } + space = append(space, byte(v)) + return space +} + +func writeBuf(buf []byte, v uint32) []byte { + return append(buf, byte(v>>16), byte(v>>8), byte(v)) +} + +// WriteUint8 write uint8 to stream +func (stream *Stream) WriteUint8(val uint8) { + stream.buf = writeFirstBuf(stream.buf, digits[val]) +} + +// WriteInt8 write int8 to stream +func (stream *Stream) WriteInt8(nval int8) { + var val uint8 + if nval < 0 { + val = uint8(-nval) + stream.buf = append(stream.buf, '-') + } else { + val = uint8(nval) + } + stream.buf = writeFirstBuf(stream.buf, digits[val]) +} + +// WriteUint16 write uint16 to stream +func (stream *Stream) WriteUint16(val uint16) { + q1 := val / 1000 + if q1 == 0 { + stream.buf = writeFirstBuf(stream.buf, digits[val]) + return + } + r1 := val - q1*1000 + stream.buf = writeFirstBuf(stream.buf, digits[q1]) + stream.buf = writeBuf(stream.buf, digits[r1]) + return +} + +// WriteInt16 write int16 to stream +func (stream *Stream) WriteInt16(nval int16) { + var val uint16 + if nval < 0 { + val = uint16(-nval) + stream.buf = append(stream.buf, '-') + } else { + val = uint16(nval) + } + stream.WriteUint16(val) +} + +// WriteUint32 write uint32 to stream +func (stream *Stream) WriteUint32(val uint32) { + q1 := val / 1000 + if q1 == 0 { + stream.buf = writeFirstBuf(stream.buf, digits[val]) + return + } + r1 := val - q1*1000 + q2 := q1 / 1000 + if q2 == 0 { + stream.buf = writeFirstBuf(stream.buf, digits[q1]) + stream.buf = writeBuf(stream.buf, digits[r1]) + return + } + r2 := q1 - q2*1000 + q3 := q2 / 1000 + if q3 == 0 { + stream.buf = writeFirstBuf(stream.buf, digits[q2]) + } else { + r3 := q2 - q3*1000 + stream.buf = append(stream.buf, byte(q3+'0')) + stream.buf = writeBuf(stream.buf, digits[r3]) + } + stream.buf = writeBuf(stream.buf, digits[r2]) + stream.buf = writeBuf(stream.buf, digits[r1]) +} + +// WriteInt32 write int32 to stream +func (stream *Stream) WriteInt32(nval int32) { + var val uint32 + if nval < 0 { + val = uint32(-nval) + stream.buf = append(stream.buf, '-') + } else { + val = uint32(nval) + } + stream.WriteUint32(val) +} + +// WriteUint64 write uint64 to stream +func (stream *Stream) WriteUint64(val uint64) { + q1 := val / 1000 + if q1 == 0 { + stream.buf = writeFirstBuf(stream.buf, digits[val]) + return + } + r1 := val - q1*1000 + q2 := q1 / 1000 + if q2 == 0 { + stream.buf = writeFirstBuf(stream.buf, digits[q1]) + stream.buf = writeBuf(stream.buf, digits[r1]) + return + } + r2 := q1 - q2*1000 + q3 := q2 / 1000 + if q3 == 0 { + stream.buf = writeFirstBuf(stream.buf, digits[q2]) + stream.buf = writeBuf(stream.buf, digits[r2]) + stream.buf = writeBuf(stream.buf, digits[r1]) + return + } + r3 := q2 - q3*1000 + q4 := q3 / 1000 + if q4 == 0 { + stream.buf = writeFirstBuf(stream.buf, digits[q3]) + stream.buf = writeBuf(stream.buf, digits[r3]) + stream.buf = writeBuf(stream.buf, digits[r2]) + stream.buf = writeBuf(stream.buf, digits[r1]) + return + } + r4 := q3 - q4*1000 + q5 := q4 / 1000 + if q5 == 0 { + stream.buf = writeFirstBuf(stream.buf, digits[q4]) + stream.buf = writeBuf(stream.buf, digits[r4]) + stream.buf = writeBuf(stream.buf, digits[r3]) + stream.buf = writeBuf(stream.buf, digits[r2]) + stream.buf = writeBuf(stream.buf, digits[r1]) + return + } + r5 := q4 - q5*1000 + q6 := q5 / 1000 + if q6 == 0 { + stream.buf = writeFirstBuf(stream.buf, digits[q5]) + } else { + stream.buf = writeFirstBuf(stream.buf, digits[q6]) + r6 := q5 - q6*1000 + stream.buf = writeBuf(stream.buf, digits[r6]) + } + stream.buf = writeBuf(stream.buf, digits[r5]) + stream.buf = writeBuf(stream.buf, digits[r4]) + stream.buf = writeBuf(stream.buf, digits[r3]) + stream.buf = writeBuf(stream.buf, digits[r2]) + stream.buf = writeBuf(stream.buf, digits[r1]) +} + +// WriteInt64 write int64 to stream +func (stream *Stream) WriteInt64(nval int64) { + var val uint64 + if nval < 0 { + val = uint64(-nval) + stream.buf = append(stream.buf, '-') + } else { + val = uint64(nval) + } + stream.WriteUint64(val) +} + +// WriteInt write int to stream +func (stream *Stream) WriteInt(val int) { + stream.WriteInt64(int64(val)) +} + +// WriteUint write uint to stream +func (stream *Stream) WriteUint(val uint) { + stream.WriteUint64(uint64(val)) +} diff --git a/vendor/github.com/json-iterator/go/stream_str.go b/vendor/github.com/json-iterator/go/stream_str.go new file mode 100644 index 0000000000000..54c2ba0b3a2d9 --- /dev/null +++ b/vendor/github.com/json-iterator/go/stream_str.go @@ -0,0 +1,372 @@ +package jsoniter + +import ( + "unicode/utf8" +) + +// htmlSafeSet holds the value true if the ASCII character with the given +// array position can be safely represented inside a JSON string, embedded +// inside of HTML